Browse Source

OpenAL-soft for windows

RexTimmy 8 years ago
parent
commit
3a0a720115
100 changed files with 47984 additions and 0 deletions
  1. 7 0
      Engine/lib/openal-soft/.gitignore
  2. 5 0
      Engine/lib/openal-soft/.travis.yml
  3. 4139 0
      Engine/lib/openal-soft/Alc/ALc.c
  4. 1657 0
      Engine/lib/openal-soft/Alc/ALu.c
  5. 598 0
      Engine/lib/openal-soft/Alc/alcConfig.c
  6. 301 0
      Engine/lib/openal-soft/Alc/alcRing.c
  7. 49 0
      Engine/lib/openal-soft/Alc/alstring.h
  8. 557 0
      Engine/lib/openal-soft/Alc/ambdec.c
  9. 46 0
      Engine/lib/openal-soft/Alc/ambdec.h
  10. 1394 0
      Engine/lib/openal-soft/Alc/backends/alsa.c
  11. 225 0
      Engine/lib/openal-soft/Alc/backends/base.c
  12. 153 0
      Engine/lib/openal-soft/Alc/backends/base.h
  13. 724 0
      Engine/lib/openal-soft/Alc/backends/coreaudio.c
  14. 1064 0
      Engine/lib/openal-soft/Alc/backends/dsound.c
  15. 627 0
      Engine/lib/openal-soft/Alc/backends/jack.c
  16. 133 0
      Engine/lib/openal-soft/Alc/backends/loopback.c
  17. 1911 0
      Engine/lib/openal-soft/Alc/backends/mmdevapi.c
  18. 223 0
      Engine/lib/openal-soft/Alc/backends/null.c
  19. 436 0
      Engine/lib/openal-soft/Alc/backends/opensl.c
  20. 821 0
      Engine/lib/openal-soft/Alc/backends/oss.c
  21. 573 0
      Engine/lib/openal-soft/Alc/backends/portaudio.c
  22. 1847 0
      Engine/lib/openal-soft/Alc/backends/pulseaudio.c
  23. 916 0
      Engine/lib/openal-soft/Alc/backends/qsa.c
  24. 294 0
      Engine/lib/openal-soft/Alc/backends/sndio.c
  25. 339 0
      Engine/lib/openal-soft/Alc/backends/solaris.c
  26. 446 0
      Engine/lib/openal-soft/Alc/backends/wave.c
  27. 803 0
      Engine/lib/openal-soft/Alc/backends/winmm.c
  28. 670 0
      Engine/lib/openal-soft/Alc/bformatdec.c
  29. 49 0
      Engine/lib/openal-soft/Alc/bformatdec.h
  30. 187 0
      Engine/lib/openal-soft/Alc/bs2b.c
  31. 981 0
      Engine/lib/openal-soft/Alc/bsinc.c
  32. 49 0
      Engine/lib/openal-soft/Alc/compat.h
  33. 412 0
      Engine/lib/openal-soft/Alc/effects/chorus.c
  34. 254 0
      Engine/lib/openal-soft/Alc/effects/compressor.c
  35. 201 0
      Engine/lib/openal-soft/Alc/effects/dedicated.c
  36. 298 0
      Engine/lib/openal-soft/Alc/effects/distortion.c
  37. 326 0
      Engine/lib/openal-soft/Alc/effects/echo.c
  38. 404 0
      Engine/lib/openal-soft/Alc/effects/equalizer.c
  39. 411 0
      Engine/lib/openal-soft/Alc/effects/flanger.c
  40. 311 0
      Engine/lib/openal-soft/Alc/effects/modulator.c
  41. 179 0
      Engine/lib/openal-soft/Alc/effects/null.c
  42. 1994 0
      Engine/lib/openal-soft/Alc/effects/reverb.c
  43. 1127 0
      Engine/lib/openal-soft/Alc/helpers.c
  44. 1054 0
      Engine/lib/openal-soft/Alc/hrtf.c
  45. 52 0
      Engine/lib/openal-soft/Alc/hrtf.h
  46. 5 0
      Engine/lib/openal-soft/Alc/hrtf_res.h
  47. 4 0
      Engine/lib/openal-soft/Alc/hrtf_res.rc
  48. 702 0
      Engine/lib/openal-soft/Alc/mixer.c
  49. 228 0
      Engine/lib/openal-soft/Alc/mixer_c.c
  50. 110 0
      Engine/lib/openal-soft/Alc/mixer_defs.h
  51. 149 0
      Engine/lib/openal-soft/Alc/mixer_inc.c
  52. 173 0
      Engine/lib/openal-soft/Alc/mixer_neon.c
  53. 292 0
      Engine/lib/openal-soft/Alc/mixer_sse.c
  54. 82 0
      Engine/lib/openal-soft/Alc/mixer_sse2.c
  55. 164 0
      Engine/lib/openal-soft/Alc/mixer_sse3.c
  56. 227 0
      Engine/lib/openal-soft/Alc/mixer_sse41.c
  57. 1037 0
      Engine/lib/openal-soft/Alc/panning.c
  58. 134 0
      Engine/lib/openal-soft/Alc/uhjfilter.c
  59. 48 0
      Engine/lib/openal-soft/Alc/uhjfilter.h
  60. 110 0
      Engine/lib/openal-soft/Alc/vector.h
  61. 1468 0
      Engine/lib/openal-soft/CMakeLists.txt
  62. 481 0
      Engine/lib/openal-soft/COPYING
  63. 188 0
      Engine/lib/openal-soft/ChangeLog
  64. 183 0
      Engine/lib/openal-soft/OpenAL32/Include/alAuxEffectSlot.h
  65. 130 0
      Engine/lib/openal-soft/OpenAL32/Include/alBuffer.h
  66. 196 0
      Engine/lib/openal-soft/OpenAL32/Include/alEffect.h
  67. 33 0
      Engine/lib/openal-soft/OpenAL32/Include/alError.h
  68. 155 0
      Engine/lib/openal-soft/OpenAL32/Include/alFilter.h
  69. 66 0
      Engine/lib/openal-soft/OpenAL32/Include/alListener.h
  70. 967 0
      Engine/lib/openal-soft/OpenAL32/Include/alMain.h
  71. 231 0
      Engine/lib/openal-soft/OpenAL32/Include/alSource.h
  72. 20 0
      Engine/lib/openal-soft/OpenAL32/Include/alThunk.h
  73. 363 0
      Engine/lib/openal-soft/OpenAL32/Include/alu.h
  74. 94 0
      Engine/lib/openal-soft/OpenAL32/Include/bs2b.h
  75. 9 0
      Engine/lib/openal-soft/OpenAL32/Include/sample_cvt.h
  76. 715 0
      Engine/lib/openal-soft/OpenAL32/alAuxEffectSlot.c
  77. 1415 0
      Engine/lib/openal-soft/OpenAL32/alBuffer.c
  78. 716 0
      Engine/lib/openal-soft/OpenAL32/alEffect.c
  79. 77 0
      Engine/lib/openal-soft/OpenAL32/alError.c
  80. 103 0
      Engine/lib/openal-soft/OpenAL32/alExtension.c
  81. 718 0
      Engine/lib/openal-soft/OpenAL32/alFilter.c
  82. 515 0
      Engine/lib/openal-soft/OpenAL32/alListener.c
  83. 3401 0
      Engine/lib/openal-soft/OpenAL32/alSource.c
  84. 687 0
      Engine/lib/openal-soft/OpenAL32/alState.c
  85. 105 0
      Engine/lib/openal-soft/OpenAL32/alThunk.c
  86. 1112 0
      Engine/lib/openal-soft/OpenAL32/sample_cvt.c
  87. 55 0
      Engine/lib/openal-soft/README
  88. 39 0
      Engine/lib/openal-soft/XCompile-Android.txt
  89. 37 0
      Engine/lib/openal-soft/XCompile.txt
  90. 456 0
      Engine/lib/openal-soft/alsoftrc.sample
  91. 14 0
      Engine/lib/openal-soft/appveyor.yml
  92. 9 0
      Engine/lib/openal-soft/cmake/CheckFileOffsetBits.c
  93. 39 0
      Engine/lib/openal-soft/cmake/CheckFileOffsetBits.cmake
  94. 92 0
      Engine/lib/openal-soft/cmake/CheckSharedFunctionExists.cmake
  95. 73 0
      Engine/lib/openal-soft/cmake/FindALSA.cmake
  96. 21 0
      Engine/lib/openal-soft/cmake/FindAudioIO.cmake
  97. 35 0
      Engine/lib/openal-soft/cmake/FindDSound.cmake
  98. 173 0
      Engine/lib/openal-soft/cmake/FindFFmpeg.cmake
  99. 60 0
      Engine/lib/openal-soft/cmake/FindJACK.cmake
  100. 21 0
      Engine/lib/openal-soft/cmake/FindOSS.cmake

+ 7 - 0
Engine/lib/openal-soft/.gitignore

@@ -0,0 +1,7 @@
+build
+winbuild
+win64build
+include/SLES
+include/sndio.h
+include/sys
+openal-soft.kdev4

+ 5 - 0
Engine/lib/openal-soft/.travis.yml

@@ -0,0 +1,5 @@
+os:
+  - linux
+  - osx
+language: c
+script: cmake . && make -j2

+ 4139 - 0
Engine/lib/openal-soft/Alc/ALc.c

@@ -0,0 +1,4139 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 1999-2007 by authors.
+ * This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the
+ *  Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#include "config.h"
+
+#include <math.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <memory.h>
+#include <ctype.h>
+#include <signal.h>
+
+#include "alMain.h"
+#include "alSource.h"
+#include "alListener.h"
+#include "alThunk.h"
+#include "alSource.h"
+#include "alBuffer.h"
+#include "alAuxEffectSlot.h"
+#include "alError.h"
+#include "bformatdec.h"
+#include "alu.h"
+
+#include "compat.h"
+#include "threads.h"
+#include "alstring.h"
+#include "almalloc.h"
+
+#include "backends/base.h"
+
+
+/************************************************
+ * Backends
+ ************************************************/
+struct BackendInfo {
+    const char *name;
+    ALCbackendFactory* (*getFactory)(void);
+    ALCboolean (*Init)(BackendFuncs*);
+    void (*Deinit)(void);
+    void (*Probe)(enum DevProbe);
+    BackendFuncs Funcs;
+};
+
+#define EmptyFuncs { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
+static struct BackendInfo BackendList[] = {
+#ifdef HAVE_JACK
+    { "jack", ALCjackBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs },
+#endif
+#ifdef HAVE_PULSEAUDIO
+    { "pulse", ALCpulseBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs },
+#endif
+#ifdef HAVE_ALSA
+    { "alsa", ALCalsaBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs },
+#endif
+#ifdef HAVE_COREAUDIO
+    { "core", NULL, alc_ca_init, alc_ca_deinit, alc_ca_probe, EmptyFuncs },
+#endif
+#ifdef HAVE_OSS
+    { "oss", ALCossBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs },
+#endif
+#ifdef HAVE_SOLARIS
+    { "solaris", ALCsolarisBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs },
+#endif
+#ifdef HAVE_SNDIO
+    { "sndio", NULL, alc_sndio_init, alc_sndio_deinit, alc_sndio_probe, EmptyFuncs },
+#endif
+#ifdef HAVE_QSA
+    { "qsa", NULL, alc_qsa_init, alc_qsa_deinit, alc_qsa_probe, EmptyFuncs },
+#endif
+#ifdef HAVE_MMDEVAPI
+    { "mmdevapi", ALCmmdevBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs },
+#endif
+#ifdef HAVE_DSOUND
+    { "dsound", ALCdsoundBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs },
+#endif
+#ifdef HAVE_WINMM
+    { "winmm", ALCwinmmBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs },
+#endif
+#ifdef HAVE_PORTAUDIO
+    { "port", ALCportBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs },
+#endif
+#ifdef HAVE_OPENSL
+    { "opensl", NULL, alc_opensl_init, alc_opensl_deinit, alc_opensl_probe, EmptyFuncs },
+#endif
+
+    { "null", ALCnullBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs },
+#ifdef HAVE_WAVE
+    { "wave", ALCwaveBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs },
+#endif
+
+    { NULL, NULL, NULL, NULL, NULL, EmptyFuncs }
+};
+#undef EmptyFuncs
+
+static struct BackendInfo PlaybackBackend;
+static struct BackendInfo CaptureBackend;
+
+
+/************************************************
+ * Functions, enums, and errors
+ ************************************************/
+typedef struct ALCfunction {
+    const ALCchar *funcName;
+    ALCvoid *address;
+} ALCfunction;
+
+typedef struct ALCenums {
+    const ALCchar *enumName;
+    ALCenum value;
+} ALCenums;
+
+#define DECL(x) { #x, (ALCvoid*)(x) }
+static const ALCfunction alcFunctions[] = {
+    DECL(alcCreateContext),
+    DECL(alcMakeContextCurrent),
+    DECL(alcProcessContext),
+    DECL(alcSuspendContext),
+    DECL(alcDestroyContext),
+    DECL(alcGetCurrentContext),
+    DECL(alcGetContextsDevice),
+    DECL(alcOpenDevice),
+    DECL(alcCloseDevice),
+    DECL(alcGetError),
+    DECL(alcIsExtensionPresent),
+    DECL(alcGetProcAddress),
+    DECL(alcGetEnumValue),
+    DECL(alcGetString),
+    DECL(alcGetIntegerv),
+    DECL(alcCaptureOpenDevice),
+    DECL(alcCaptureCloseDevice),
+    DECL(alcCaptureStart),
+    DECL(alcCaptureStop),
+    DECL(alcCaptureSamples),
+
+    DECL(alcSetThreadContext),
+    DECL(alcGetThreadContext),
+
+    DECL(alcLoopbackOpenDeviceSOFT),
+    DECL(alcIsRenderFormatSupportedSOFT),
+    DECL(alcRenderSamplesSOFT),
+
+    DECL(alcDevicePauseSOFT),
+    DECL(alcDeviceResumeSOFT),
+
+    DECL(alcGetStringiSOFT),
+    DECL(alcResetDeviceSOFT),
+
+    DECL(alcGetInteger64vSOFT),
+
+    DECL(alEnable),
+    DECL(alDisable),
+    DECL(alIsEnabled),
+    DECL(alGetString),
+    DECL(alGetBooleanv),
+    DECL(alGetIntegerv),
+    DECL(alGetFloatv),
+    DECL(alGetDoublev),
+    DECL(alGetBoolean),
+    DECL(alGetInteger),
+    DECL(alGetFloat),
+    DECL(alGetDouble),
+    DECL(alGetError),
+    DECL(alIsExtensionPresent),
+    DECL(alGetProcAddress),
+    DECL(alGetEnumValue),
+    DECL(alListenerf),
+    DECL(alListener3f),
+    DECL(alListenerfv),
+    DECL(alListeneri),
+    DECL(alListener3i),
+    DECL(alListeneriv),
+    DECL(alGetListenerf),
+    DECL(alGetListener3f),
+    DECL(alGetListenerfv),
+    DECL(alGetListeneri),
+    DECL(alGetListener3i),
+    DECL(alGetListeneriv),
+    DECL(alGenSources),
+    DECL(alDeleteSources),
+    DECL(alIsSource),
+    DECL(alSourcef),
+    DECL(alSource3f),
+    DECL(alSourcefv),
+    DECL(alSourcei),
+    DECL(alSource3i),
+    DECL(alSourceiv),
+    DECL(alGetSourcef),
+    DECL(alGetSource3f),
+    DECL(alGetSourcefv),
+    DECL(alGetSourcei),
+    DECL(alGetSource3i),
+    DECL(alGetSourceiv),
+    DECL(alSourcePlayv),
+    DECL(alSourceStopv),
+    DECL(alSourceRewindv),
+    DECL(alSourcePausev),
+    DECL(alSourcePlay),
+    DECL(alSourceStop),
+    DECL(alSourceRewind),
+    DECL(alSourcePause),
+    DECL(alSourceQueueBuffers),
+    DECL(alSourceUnqueueBuffers),
+    DECL(alGenBuffers),
+    DECL(alDeleteBuffers),
+    DECL(alIsBuffer),
+    DECL(alBufferData),
+    DECL(alBufferf),
+    DECL(alBuffer3f),
+    DECL(alBufferfv),
+    DECL(alBufferi),
+    DECL(alBuffer3i),
+    DECL(alBufferiv),
+    DECL(alGetBufferf),
+    DECL(alGetBuffer3f),
+    DECL(alGetBufferfv),
+    DECL(alGetBufferi),
+    DECL(alGetBuffer3i),
+    DECL(alGetBufferiv),
+    DECL(alDopplerFactor),
+    DECL(alDopplerVelocity),
+    DECL(alSpeedOfSound),
+    DECL(alDistanceModel),
+
+    DECL(alGenFilters),
+    DECL(alDeleteFilters),
+    DECL(alIsFilter),
+    DECL(alFilteri),
+    DECL(alFilteriv),
+    DECL(alFilterf),
+    DECL(alFilterfv),
+    DECL(alGetFilteri),
+    DECL(alGetFilteriv),
+    DECL(alGetFilterf),
+    DECL(alGetFilterfv),
+    DECL(alGenEffects),
+    DECL(alDeleteEffects),
+    DECL(alIsEffect),
+    DECL(alEffecti),
+    DECL(alEffectiv),
+    DECL(alEffectf),
+    DECL(alEffectfv),
+    DECL(alGetEffecti),
+    DECL(alGetEffectiv),
+    DECL(alGetEffectf),
+    DECL(alGetEffectfv),
+    DECL(alGenAuxiliaryEffectSlots),
+    DECL(alDeleteAuxiliaryEffectSlots),
+    DECL(alIsAuxiliaryEffectSlot),
+    DECL(alAuxiliaryEffectSloti),
+    DECL(alAuxiliaryEffectSlotiv),
+    DECL(alAuxiliaryEffectSlotf),
+    DECL(alAuxiliaryEffectSlotfv),
+    DECL(alGetAuxiliaryEffectSloti),
+    DECL(alGetAuxiliaryEffectSlotiv),
+    DECL(alGetAuxiliaryEffectSlotf),
+    DECL(alGetAuxiliaryEffectSlotfv),
+
+    DECL(alDeferUpdatesSOFT),
+    DECL(alProcessUpdatesSOFT),
+
+    DECL(alSourcedSOFT),
+    DECL(alSource3dSOFT),
+    DECL(alSourcedvSOFT),
+    DECL(alGetSourcedSOFT),
+    DECL(alGetSource3dSOFT),
+    DECL(alGetSourcedvSOFT),
+    DECL(alSourcei64SOFT),
+    DECL(alSource3i64SOFT),
+    DECL(alSourcei64vSOFT),
+    DECL(alGetSourcei64SOFT),
+    DECL(alGetSource3i64SOFT),
+    DECL(alGetSourcei64vSOFT),
+
+    DECL(alBufferSamplesSOFT),
+    DECL(alGetBufferSamplesSOFT),
+    DECL(alIsBufferFormatSupportedSOFT),
+
+    { NULL, NULL }
+};
+#undef DECL
+
+#define DECL(x) { #x, (x) }
+static const ALCenums enumeration[] = {
+    DECL(ALC_INVALID),
+    DECL(ALC_FALSE),
+    DECL(ALC_TRUE),
+
+    DECL(ALC_MAJOR_VERSION),
+    DECL(ALC_MINOR_VERSION),
+    DECL(ALC_ATTRIBUTES_SIZE),
+    DECL(ALC_ALL_ATTRIBUTES),
+    DECL(ALC_DEFAULT_DEVICE_SPECIFIER),
+    DECL(ALC_DEVICE_SPECIFIER),
+    DECL(ALC_ALL_DEVICES_SPECIFIER),
+    DECL(ALC_DEFAULT_ALL_DEVICES_SPECIFIER),
+    DECL(ALC_EXTENSIONS),
+    DECL(ALC_FREQUENCY),
+    DECL(ALC_REFRESH),
+    DECL(ALC_SYNC),
+    DECL(ALC_MONO_SOURCES),
+    DECL(ALC_STEREO_SOURCES),
+    DECL(ALC_CAPTURE_DEVICE_SPECIFIER),
+    DECL(ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER),
+    DECL(ALC_CAPTURE_SAMPLES),
+    DECL(ALC_CONNECTED),
+
+    DECL(ALC_EFX_MAJOR_VERSION),
+    DECL(ALC_EFX_MINOR_VERSION),
+    DECL(ALC_MAX_AUXILIARY_SENDS),
+
+    DECL(ALC_FORMAT_CHANNELS_SOFT),
+    DECL(ALC_FORMAT_TYPE_SOFT),
+
+    DECL(ALC_MONO_SOFT),
+    DECL(ALC_STEREO_SOFT),
+    DECL(ALC_QUAD_SOFT),
+    DECL(ALC_5POINT1_SOFT),
+    DECL(ALC_6POINT1_SOFT),
+    DECL(ALC_7POINT1_SOFT),
+
+    DECL(ALC_BYTE_SOFT),
+    DECL(ALC_UNSIGNED_BYTE_SOFT),
+    DECL(ALC_SHORT_SOFT),
+    DECL(ALC_UNSIGNED_SHORT_SOFT),
+    DECL(ALC_INT_SOFT),
+    DECL(ALC_UNSIGNED_INT_SOFT),
+    DECL(ALC_FLOAT_SOFT),
+
+    DECL(ALC_HRTF_SOFT),
+    DECL(ALC_DONT_CARE_SOFT),
+    DECL(ALC_HRTF_STATUS_SOFT),
+    DECL(ALC_HRTF_DISABLED_SOFT),
+    DECL(ALC_HRTF_ENABLED_SOFT),
+    DECL(ALC_HRTF_DENIED_SOFT),
+    DECL(ALC_HRTF_REQUIRED_SOFT),
+    DECL(ALC_HRTF_HEADPHONES_DETECTED_SOFT),
+    DECL(ALC_HRTF_UNSUPPORTED_FORMAT_SOFT),
+    DECL(ALC_NUM_HRTF_SPECIFIERS_SOFT),
+    DECL(ALC_HRTF_SPECIFIER_SOFT),
+    DECL(ALC_HRTF_ID_SOFT),
+
+    DECL(ALC_NO_ERROR),
+    DECL(ALC_INVALID_DEVICE),
+    DECL(ALC_INVALID_CONTEXT),
+    DECL(ALC_INVALID_ENUM),
+    DECL(ALC_INVALID_VALUE),
+    DECL(ALC_OUT_OF_MEMORY),
+
+
+    DECL(AL_INVALID),
+    DECL(AL_NONE),
+    DECL(AL_FALSE),
+    DECL(AL_TRUE),
+
+    DECL(AL_SOURCE_RELATIVE),
+    DECL(AL_CONE_INNER_ANGLE),
+    DECL(AL_CONE_OUTER_ANGLE),
+    DECL(AL_PITCH),
+    DECL(AL_POSITION),
+    DECL(AL_DIRECTION),
+    DECL(AL_VELOCITY),
+    DECL(AL_LOOPING),
+    DECL(AL_BUFFER),
+    DECL(AL_GAIN),
+    DECL(AL_MIN_GAIN),
+    DECL(AL_MAX_GAIN),
+    DECL(AL_ORIENTATION),
+    DECL(AL_REFERENCE_DISTANCE),
+    DECL(AL_ROLLOFF_FACTOR),
+    DECL(AL_CONE_OUTER_GAIN),
+    DECL(AL_MAX_DISTANCE),
+    DECL(AL_SEC_OFFSET),
+    DECL(AL_SAMPLE_OFFSET),
+    DECL(AL_BYTE_OFFSET),
+    DECL(AL_SOURCE_TYPE),
+    DECL(AL_STATIC),
+    DECL(AL_STREAMING),
+    DECL(AL_UNDETERMINED),
+    DECL(AL_METERS_PER_UNIT),
+    DECL(AL_LOOP_POINTS_SOFT),
+    DECL(AL_DIRECT_CHANNELS_SOFT),
+
+    DECL(AL_DIRECT_FILTER),
+    DECL(AL_AUXILIARY_SEND_FILTER),
+    DECL(AL_AIR_ABSORPTION_FACTOR),
+    DECL(AL_ROOM_ROLLOFF_FACTOR),
+    DECL(AL_CONE_OUTER_GAINHF),
+    DECL(AL_DIRECT_FILTER_GAINHF_AUTO),
+    DECL(AL_AUXILIARY_SEND_FILTER_GAIN_AUTO),
+    DECL(AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO),
+
+    DECL(AL_SOURCE_STATE),
+    DECL(AL_INITIAL),
+    DECL(AL_PLAYING),
+    DECL(AL_PAUSED),
+    DECL(AL_STOPPED),
+
+    DECL(AL_BUFFERS_QUEUED),
+    DECL(AL_BUFFERS_PROCESSED),
+
+    DECL(AL_FORMAT_MONO8),
+    DECL(AL_FORMAT_MONO16),
+    DECL(AL_FORMAT_MONO_FLOAT32),
+    DECL(AL_FORMAT_MONO_DOUBLE_EXT),
+    DECL(AL_FORMAT_STEREO8),
+    DECL(AL_FORMAT_STEREO16),
+    DECL(AL_FORMAT_STEREO_FLOAT32),
+    DECL(AL_FORMAT_STEREO_DOUBLE_EXT),
+    DECL(AL_FORMAT_MONO_IMA4),
+    DECL(AL_FORMAT_STEREO_IMA4),
+    DECL(AL_FORMAT_MONO_MSADPCM_SOFT),
+    DECL(AL_FORMAT_STEREO_MSADPCM_SOFT),
+    DECL(AL_FORMAT_QUAD8_LOKI),
+    DECL(AL_FORMAT_QUAD16_LOKI),
+    DECL(AL_FORMAT_QUAD8),
+    DECL(AL_FORMAT_QUAD16),
+    DECL(AL_FORMAT_QUAD32),
+    DECL(AL_FORMAT_51CHN8),
+    DECL(AL_FORMAT_51CHN16),
+    DECL(AL_FORMAT_51CHN32),
+    DECL(AL_FORMAT_61CHN8),
+    DECL(AL_FORMAT_61CHN16),
+    DECL(AL_FORMAT_61CHN32),
+    DECL(AL_FORMAT_71CHN8),
+    DECL(AL_FORMAT_71CHN16),
+    DECL(AL_FORMAT_71CHN32),
+    DECL(AL_FORMAT_REAR8),
+    DECL(AL_FORMAT_REAR16),
+    DECL(AL_FORMAT_REAR32),
+    DECL(AL_FORMAT_MONO_MULAW),
+    DECL(AL_FORMAT_MONO_MULAW_EXT),
+    DECL(AL_FORMAT_STEREO_MULAW),
+    DECL(AL_FORMAT_STEREO_MULAW_EXT),
+    DECL(AL_FORMAT_QUAD_MULAW),
+    DECL(AL_FORMAT_51CHN_MULAW),
+    DECL(AL_FORMAT_61CHN_MULAW),
+    DECL(AL_FORMAT_71CHN_MULAW),
+    DECL(AL_FORMAT_REAR_MULAW),
+    DECL(AL_FORMAT_MONO_ALAW_EXT),
+    DECL(AL_FORMAT_STEREO_ALAW_EXT),
+
+    DECL(AL_FORMAT_BFORMAT2D_8),
+    DECL(AL_FORMAT_BFORMAT2D_16),
+    DECL(AL_FORMAT_BFORMAT2D_FLOAT32),
+    DECL(AL_FORMAT_BFORMAT2D_MULAW),
+    DECL(AL_FORMAT_BFORMAT3D_8),
+    DECL(AL_FORMAT_BFORMAT3D_16),
+    DECL(AL_FORMAT_BFORMAT3D_FLOAT32),
+    DECL(AL_FORMAT_BFORMAT3D_MULAW),
+
+    DECL(AL_MONO8_SOFT),
+    DECL(AL_MONO16_SOFT),
+    DECL(AL_MONO32F_SOFT),
+    DECL(AL_STEREO8_SOFT),
+    DECL(AL_STEREO16_SOFT),
+    DECL(AL_STEREO32F_SOFT),
+    DECL(AL_QUAD8_SOFT),
+    DECL(AL_QUAD16_SOFT),
+    DECL(AL_QUAD32F_SOFT),
+    DECL(AL_REAR8_SOFT),
+    DECL(AL_REAR16_SOFT),
+    DECL(AL_REAR32F_SOFT),
+    DECL(AL_5POINT1_8_SOFT),
+    DECL(AL_5POINT1_16_SOFT),
+    DECL(AL_5POINT1_32F_SOFT),
+    DECL(AL_6POINT1_8_SOFT),
+    DECL(AL_6POINT1_16_SOFT),
+    DECL(AL_6POINT1_32F_SOFT),
+    DECL(AL_7POINT1_8_SOFT),
+    DECL(AL_7POINT1_16_SOFT),
+    DECL(AL_7POINT1_32F_SOFT),
+    DECL(AL_BFORMAT2D_8_SOFT),
+    DECL(AL_BFORMAT2D_16_SOFT),
+    DECL(AL_BFORMAT2D_32F_SOFT),
+    DECL(AL_BFORMAT3D_8_SOFT),
+    DECL(AL_BFORMAT3D_16_SOFT),
+    DECL(AL_BFORMAT3D_32F_SOFT),
+
+    DECL(AL_MONO_SOFT),
+    DECL(AL_STEREO_SOFT),
+    DECL(AL_QUAD_SOFT),
+    DECL(AL_REAR_SOFT),
+    DECL(AL_5POINT1_SOFT),
+    DECL(AL_6POINT1_SOFT),
+    DECL(AL_7POINT1_SOFT),
+    DECL(AL_BFORMAT2D_SOFT),
+    DECL(AL_BFORMAT3D_SOFT),
+
+    DECL(AL_BYTE_SOFT),
+    DECL(AL_UNSIGNED_BYTE_SOFT),
+    DECL(AL_SHORT_SOFT),
+    DECL(AL_UNSIGNED_SHORT_SOFT),
+    DECL(AL_INT_SOFT),
+    DECL(AL_UNSIGNED_INT_SOFT),
+    DECL(AL_FLOAT_SOFT),
+    DECL(AL_DOUBLE_SOFT),
+    DECL(AL_BYTE3_SOFT),
+    DECL(AL_UNSIGNED_BYTE3_SOFT),
+    DECL(AL_MULAW_SOFT),
+
+    DECL(AL_FREQUENCY),
+    DECL(AL_BITS),
+    DECL(AL_CHANNELS),
+    DECL(AL_SIZE),
+    DECL(AL_INTERNAL_FORMAT_SOFT),
+    DECL(AL_BYTE_LENGTH_SOFT),
+    DECL(AL_SAMPLE_LENGTH_SOFT),
+    DECL(AL_SEC_LENGTH_SOFT),
+    DECL(AL_UNPACK_BLOCK_ALIGNMENT_SOFT),
+    DECL(AL_PACK_BLOCK_ALIGNMENT_SOFT),
+
+    DECL(AL_SOURCE_RADIUS),
+
+    DECL(AL_STEREO_ANGLES),
+
+    DECL(AL_UNUSED),
+    DECL(AL_PENDING),
+    DECL(AL_PROCESSED),
+
+    DECL(AL_NO_ERROR),
+    DECL(AL_INVALID_NAME),
+    DECL(AL_INVALID_ENUM),
+    DECL(AL_INVALID_VALUE),
+    DECL(AL_INVALID_OPERATION),
+    DECL(AL_OUT_OF_MEMORY),
+
+    DECL(AL_VENDOR),
+    DECL(AL_VERSION),
+    DECL(AL_RENDERER),
+    DECL(AL_EXTENSIONS),
+
+    DECL(AL_DOPPLER_FACTOR),
+    DECL(AL_DOPPLER_VELOCITY),
+    DECL(AL_DISTANCE_MODEL),
+    DECL(AL_SPEED_OF_SOUND),
+    DECL(AL_SOURCE_DISTANCE_MODEL),
+    DECL(AL_DEFERRED_UPDATES_SOFT),
+    DECL(AL_GAIN_LIMIT_SOFT),
+
+    DECL(AL_INVERSE_DISTANCE),
+    DECL(AL_INVERSE_DISTANCE_CLAMPED),
+    DECL(AL_LINEAR_DISTANCE),
+    DECL(AL_LINEAR_DISTANCE_CLAMPED),
+    DECL(AL_EXPONENT_DISTANCE),
+    DECL(AL_EXPONENT_DISTANCE_CLAMPED),
+
+    DECL(AL_FILTER_TYPE),
+    DECL(AL_FILTER_NULL),
+    DECL(AL_FILTER_LOWPASS),
+    DECL(AL_FILTER_HIGHPASS),
+    DECL(AL_FILTER_BANDPASS),
+
+    DECL(AL_LOWPASS_GAIN),
+    DECL(AL_LOWPASS_GAINHF),
+
+    DECL(AL_HIGHPASS_GAIN),
+    DECL(AL_HIGHPASS_GAINLF),
+
+    DECL(AL_BANDPASS_GAIN),
+    DECL(AL_BANDPASS_GAINHF),
+    DECL(AL_BANDPASS_GAINLF),
+
+    DECL(AL_EFFECT_TYPE),
+    DECL(AL_EFFECT_NULL),
+    DECL(AL_EFFECT_REVERB),
+    DECL(AL_EFFECT_EAXREVERB),
+    DECL(AL_EFFECT_CHORUS),
+    DECL(AL_EFFECT_DISTORTION),
+    DECL(AL_EFFECT_ECHO),
+    DECL(AL_EFFECT_FLANGER),
+#if 0
+    DECL(AL_EFFECT_FREQUENCY_SHIFTER),
+    DECL(AL_EFFECT_VOCAL_MORPHER),
+    DECL(AL_EFFECT_PITCH_SHIFTER),
+#endif
+    DECL(AL_EFFECT_RING_MODULATOR),
+#if 0
+    DECL(AL_EFFECT_AUTOWAH),
+#endif
+    DECL(AL_EFFECT_COMPRESSOR),
+    DECL(AL_EFFECT_EQUALIZER),
+    DECL(AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT),
+    DECL(AL_EFFECT_DEDICATED_DIALOGUE),
+
+    DECL(AL_EAXREVERB_DENSITY),
+    DECL(AL_EAXREVERB_DIFFUSION),
+    DECL(AL_EAXREVERB_GAIN),
+    DECL(AL_EAXREVERB_GAINHF),
+    DECL(AL_EAXREVERB_GAINLF),
+    DECL(AL_EAXREVERB_DECAY_TIME),
+    DECL(AL_EAXREVERB_DECAY_HFRATIO),
+    DECL(AL_EAXREVERB_DECAY_LFRATIO),
+    DECL(AL_EAXREVERB_REFLECTIONS_GAIN),
+    DECL(AL_EAXREVERB_REFLECTIONS_DELAY),
+    DECL(AL_EAXREVERB_REFLECTIONS_PAN),
+    DECL(AL_EAXREVERB_LATE_REVERB_GAIN),
+    DECL(AL_EAXREVERB_LATE_REVERB_DELAY),
+    DECL(AL_EAXREVERB_LATE_REVERB_PAN),
+    DECL(AL_EAXREVERB_ECHO_TIME),
+    DECL(AL_EAXREVERB_ECHO_DEPTH),
+    DECL(AL_EAXREVERB_MODULATION_TIME),
+    DECL(AL_EAXREVERB_MODULATION_DEPTH),
+    DECL(AL_EAXREVERB_AIR_ABSORPTION_GAINHF),
+    DECL(AL_EAXREVERB_HFREFERENCE),
+    DECL(AL_EAXREVERB_LFREFERENCE),
+    DECL(AL_EAXREVERB_ROOM_ROLLOFF_FACTOR),
+    DECL(AL_EAXREVERB_DECAY_HFLIMIT),
+
+    DECL(AL_REVERB_DENSITY),
+    DECL(AL_REVERB_DIFFUSION),
+    DECL(AL_REVERB_GAIN),
+    DECL(AL_REVERB_GAINHF),
+    DECL(AL_REVERB_DECAY_TIME),
+    DECL(AL_REVERB_DECAY_HFRATIO),
+    DECL(AL_REVERB_REFLECTIONS_GAIN),
+    DECL(AL_REVERB_REFLECTIONS_DELAY),
+    DECL(AL_REVERB_LATE_REVERB_GAIN),
+    DECL(AL_REVERB_LATE_REVERB_DELAY),
+    DECL(AL_REVERB_AIR_ABSORPTION_GAINHF),
+    DECL(AL_REVERB_ROOM_ROLLOFF_FACTOR),
+    DECL(AL_REVERB_DECAY_HFLIMIT),
+
+    DECL(AL_CHORUS_WAVEFORM),
+    DECL(AL_CHORUS_PHASE),
+    DECL(AL_CHORUS_RATE),
+    DECL(AL_CHORUS_DEPTH),
+    DECL(AL_CHORUS_FEEDBACK),
+    DECL(AL_CHORUS_DELAY),
+
+    DECL(AL_DISTORTION_EDGE),
+    DECL(AL_DISTORTION_GAIN),
+    DECL(AL_DISTORTION_LOWPASS_CUTOFF),
+    DECL(AL_DISTORTION_EQCENTER),
+    DECL(AL_DISTORTION_EQBANDWIDTH),
+
+    DECL(AL_ECHO_DELAY),
+    DECL(AL_ECHO_LRDELAY),
+    DECL(AL_ECHO_DAMPING),
+    DECL(AL_ECHO_FEEDBACK),
+    DECL(AL_ECHO_SPREAD),
+
+    DECL(AL_FLANGER_WAVEFORM),
+    DECL(AL_FLANGER_PHASE),
+    DECL(AL_FLANGER_RATE),
+    DECL(AL_FLANGER_DEPTH),
+    DECL(AL_FLANGER_FEEDBACK),
+    DECL(AL_FLANGER_DELAY),
+
+    DECL(AL_RING_MODULATOR_FREQUENCY),
+    DECL(AL_RING_MODULATOR_HIGHPASS_CUTOFF),
+    DECL(AL_RING_MODULATOR_WAVEFORM),
+
+    DECL(AL_COMPRESSOR_ONOFF),
+
+    DECL(AL_EQUALIZER_LOW_GAIN),
+    DECL(AL_EQUALIZER_LOW_CUTOFF),
+    DECL(AL_EQUALIZER_MID1_GAIN),
+    DECL(AL_EQUALIZER_MID1_CENTER),
+    DECL(AL_EQUALIZER_MID1_WIDTH),
+    DECL(AL_EQUALIZER_MID2_GAIN),
+    DECL(AL_EQUALIZER_MID2_CENTER),
+    DECL(AL_EQUALIZER_MID2_WIDTH),
+    DECL(AL_EQUALIZER_HIGH_GAIN),
+    DECL(AL_EQUALIZER_HIGH_CUTOFF),
+
+    DECL(AL_DEDICATED_GAIN),
+
+    { NULL, (ALCenum)0 }
+};
+#undef DECL
+
+static const ALCchar alcNoError[] = "No Error";
+static const ALCchar alcErrInvalidDevice[] = "Invalid Device";
+static const ALCchar alcErrInvalidContext[] = "Invalid Context";
+static const ALCchar alcErrInvalidEnum[] = "Invalid Enum";
+static const ALCchar alcErrInvalidValue[] = "Invalid Value";
+static const ALCchar alcErrOutOfMemory[] = "Out of Memory";
+
+
+/************************************************
+ * Global variables
+ ************************************************/
+
+/* Enumerated device names */
+static const ALCchar alcDefaultName[] = "OpenAL Soft\0";
+
+static al_string alcAllDevicesList;
+static al_string alcCaptureDeviceList;
+
+/* Default is always the first in the list */
+static ALCchar *alcDefaultAllDevicesSpecifier;
+static ALCchar *alcCaptureDefaultDeviceSpecifier;
+
+/* Default context extensions */
+static const ALchar alExtList[] =
+    "AL_EXT_ALAW AL_EXT_BFORMAT AL_EXT_DOUBLE AL_EXT_EXPONENT_DISTANCE "
+    "AL_EXT_FLOAT32 AL_EXT_IMA4 AL_EXT_LINEAR_DISTANCE AL_EXT_MCFORMATS "
+    "AL_EXT_MULAW AL_EXT_MULAW_BFORMAT AL_EXT_MULAW_MCFORMATS AL_EXT_OFFSET "
+    "AL_EXT_source_distance_model AL_EXT_SOURCE_RADIUS AL_EXT_STEREO_ANGLES "
+    "AL_LOKI_quadriphonic AL_SOFT_block_alignment AL_SOFT_deferred_updates "
+    "AL_SOFT_direct_channels AL_SOFT_gain_clamp_ex AL_SOFT_loop_points "
+    "AL_SOFT_MSADPCM AL_SOFT_source_latency AL_SOFT_source_length";
+
+static ATOMIC(ALCenum) LastNullDeviceError = ATOMIC_INIT_STATIC(ALC_NO_ERROR);
+
+/* Thread-local current context */
+static altss_t LocalContext;
+/* Process-wide current context */
+static ATOMIC(ALCcontext*) GlobalContext = ATOMIC_INIT_STATIC(NULL);
+
+/* Mixing thread piority level */
+ALint RTPrioLevel;
+
+FILE *LogFile;
+#ifdef _DEBUG
+enum LogLevel LogLevel = LogWarning;
+#else
+enum LogLevel LogLevel = LogError;
+#endif
+
+/* Flag to trap ALC device errors */
+static ALCboolean TrapALCError = ALC_FALSE;
+
+/* One-time configuration init control */
+static alonce_flag alc_config_once = AL_ONCE_FLAG_INIT;
+
+/* Default effect that applies to sources that don't have an effect on send 0 */
+static ALeffect DefaultEffect;
+
+/* Flag to specify if alcSuspendContext/alcProcessContext should defer/process
+ * updates.
+ */
+static ALCboolean SuspendDefers = ALC_TRUE;
+
+
+/************************************************
+ * ALC information
+ ************************************************/
+static const ALCchar alcNoDeviceExtList[] =
+    "ALC_ENUMERATE_ALL_EXT ALC_ENUMERATION_EXT ALC_EXT_CAPTURE "
+    "ALC_EXT_thread_local_context ALC_SOFT_loopback";
+static const ALCchar alcExtensionList[] =
+    "ALC_ENUMERATE_ALL_EXT ALC_ENUMERATION_EXT ALC_EXT_CAPTURE "
+    "ALC_EXT_DEDICATED ALC_EXT_disconnect ALC_EXT_EFX "
+    "ALC_EXT_thread_local_context ALC_SOFTX_device_clock ALC_SOFT_HRTF "
+    "ALC_SOFT_loopback ALC_SOFT_pause_device";
+static const ALCint alcMajorVersion = 1;
+static const ALCint alcMinorVersion = 1;
+
+static const ALCint alcEFXMajorVersion = 1;
+static const ALCint alcEFXMinorVersion = 0;
+
+
+/************************************************
+ * Device lists
+ ************************************************/
+static ATOMIC(ALCdevice*) DeviceList = ATOMIC_INIT_STATIC(NULL);
+
+static almtx_t ListLock;
+static inline void LockLists(void)
+{
+    int ret = almtx_lock(&ListLock);
+    assert(ret == althrd_success);
+}
+static inline void UnlockLists(void)
+{
+    int ret = almtx_unlock(&ListLock);
+    assert(ret == althrd_success);
+}
+
+/************************************************
+ * Library initialization
+ ************************************************/
+#if defined(_WIN32)
+static void alc_init(void);
+static void alc_deinit(void);
+static void alc_deinit_safe(void);
+
+#ifndef AL_LIBTYPE_STATIC
+BOOL APIENTRY DllMain(HINSTANCE hModule, DWORD reason, LPVOID lpReserved)
+{
+    switch(reason)
+    {
+        case DLL_PROCESS_ATTACH:
+            /* Pin the DLL so we won't get unloaded until the process terminates */
+            GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN | GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
+                               (WCHAR*)hModule, &hModule);
+            alc_init();
+            break;
+
+        case DLL_THREAD_DETACH:
+            break;
+
+        case DLL_PROCESS_DETACH:
+            if(!lpReserved)
+                alc_deinit();
+            else
+                alc_deinit_safe();
+            break;
+    }
+    return TRUE;
+}
+#elif defined(_MSC_VER)
+#pragma section(".CRT$XCU",read)
+static void alc_constructor(void);
+static void alc_destructor(void);
+__declspec(allocate(".CRT$XCU")) void (__cdecl* alc_constructor_)(void) = alc_constructor;
+
+static void alc_constructor(void)
+{
+    atexit(alc_destructor);
+    alc_init();
+}
+
+static void alc_destructor(void)
+{
+    alc_deinit();
+}
+#elif defined(HAVE_GCC_DESTRUCTOR)
+static void alc_init(void) __attribute__((constructor));
+static void alc_deinit(void) __attribute__((destructor));
+#else
+#error "No static initialization available on this platform!"
+#endif
+
+#elif defined(HAVE_GCC_DESTRUCTOR)
+
+static void alc_init(void) __attribute__((constructor));
+static void alc_deinit(void) __attribute__((destructor));
+
+#else
+#error "No global initialization available on this platform!"
+#endif
+
+static void ReleaseThreadCtx(void *ptr);
+static void alc_init(void)
+{
+    const char *str;
+    int ret;
+
+    LogFile = stderr;
+
+    AL_STRING_INIT(alcAllDevicesList);
+    AL_STRING_INIT(alcCaptureDeviceList);
+
+    str = getenv("__ALSOFT_HALF_ANGLE_CONES");
+    if(str && (strcasecmp(str, "true") == 0 || strtol(str, NULL, 0) == 1))
+        ConeScale *= 0.5f;
+
+    str = getenv("__ALSOFT_REVERSE_Z");
+    if(str && (strcasecmp(str, "true") == 0 || strtol(str, NULL, 0) == 1))
+        ZScale *= -1.0f;
+
+    ret = altss_create(&LocalContext, ReleaseThreadCtx);
+    assert(ret == althrd_success);
+
+    ret = almtx_init(&ListLock, almtx_recursive);
+    assert(ret == althrd_success);
+
+    ThunkInit();
+}
+
+static void alc_initconfig(void)
+{
+    const char *devs, *str;
+    ALuint capfilter;
+    float valf;
+    int i, n;
+
+    str = getenv("ALSOFT_LOGLEVEL");
+    if(str)
+    {
+        long lvl = strtol(str, NULL, 0);
+        if(lvl >= NoLog && lvl <= LogRef)
+            LogLevel = lvl;
+    }
+
+    str = getenv("ALSOFT_LOGFILE");
+    if(str && str[0])
+    {
+        FILE *logfile = al_fopen(str, "wt");
+        if(logfile) LogFile = logfile;
+        else ERR("Failed to open log file '%s'\n", str);
+    }
+
+    {
+        char buf[1024] = "";
+        int len = snprintf(buf, sizeof(buf), "%s", BackendList[0].name);
+        for(i = 1;BackendList[i].name;i++)
+            len += snprintf(buf+len, sizeof(buf)-len, ", %s", BackendList[i].name);
+        TRACE("Supported backends: %s\n", buf);
+    }
+    ReadALConfig();
+
+    str = getenv("__ALSOFT_SUSPEND_CONTEXT");
+    if(str && *str)
+    {
+        if(strcasecmp(str, "ignore") == 0)
+        {
+            SuspendDefers = ALC_FALSE;
+            TRACE("Selected context suspend behavior, \"ignore\"\n");
+        }
+        else
+            ERR("Unhandled context suspend behavior setting: \"%s\"\n", str);
+    }
+
+    capfilter = 0;
+#if defined(HAVE_SSE4_1)
+    capfilter |= CPU_CAP_SSE | CPU_CAP_SSE2 | CPU_CAP_SSE3 | CPU_CAP_SSE4_1;
+#elif defined(HAVE_SSE3)
+    capfilter |= CPU_CAP_SSE | CPU_CAP_SSE2 | CPU_CAP_SSE3;
+#elif defined(HAVE_SSE2)
+    capfilter |= CPU_CAP_SSE | CPU_CAP_SSE2;
+#elif defined(HAVE_SSE)
+    capfilter |= CPU_CAP_SSE;
+#endif
+#ifdef HAVE_NEON
+    capfilter |= CPU_CAP_NEON;
+#endif
+    if(ConfigValueStr(NULL, NULL, "disable-cpu-exts", &str))
+    {
+        if(strcasecmp(str, "all") == 0)
+            capfilter = 0;
+        else
+        {
+            size_t len;
+            const char *next = str;
+
+            do {
+                str = next;
+                while(isspace(str[0]))
+                    str++;
+                next = strchr(str, ',');
+
+                if(!str[0] || str[0] == ',')
+                    continue;
+
+                len = (next ? ((size_t)(next-str)) : strlen(str));
+                while(len > 0 && isspace(str[len-1]))
+                    len--;
+                if(len == 3 && strncasecmp(str, "sse", len) == 0)
+                    capfilter &= ~CPU_CAP_SSE;
+                else if(len == 4 && strncasecmp(str, "sse2", len) == 0)
+                    capfilter &= ~CPU_CAP_SSE2;
+                else if(len == 4 && strncasecmp(str, "sse3", len) == 0)
+                    capfilter &= ~CPU_CAP_SSE3;
+                else if(len == 6 && strncasecmp(str, "sse4.1", len) == 0)
+                    capfilter &= ~CPU_CAP_SSE4_1;
+                else if(len == 4 && strncasecmp(str, "neon", len) == 0)
+                    capfilter &= ~CPU_CAP_NEON;
+                else
+                    WARN("Invalid CPU extension \"%s\"\n", str);
+            } while(next++);
+        }
+    }
+    FillCPUCaps(capfilter);
+
+#ifdef _WIN32
+    RTPrioLevel = 1;
+#else
+    RTPrioLevel = 0;
+#endif
+    ConfigValueInt(NULL, NULL, "rt-prio", &RTPrioLevel);
+
+    aluInitMixer();
+
+    str = getenv("ALSOFT_TRAP_ERROR");
+    if(str && (strcasecmp(str, "true") == 0 || strtol(str, NULL, 0) == 1))
+    {
+        TrapALError  = AL_TRUE;
+        TrapALCError = AL_TRUE;
+    }
+    else
+    {
+        str = getenv("ALSOFT_TRAP_AL_ERROR");
+        if(str && (strcasecmp(str, "true") == 0 || strtol(str, NULL, 0) == 1))
+            TrapALError = AL_TRUE;
+        TrapALError = GetConfigValueBool(NULL, NULL, "trap-al-error", TrapALError);
+
+        str = getenv("ALSOFT_TRAP_ALC_ERROR");
+        if(str && (strcasecmp(str, "true") == 0 || strtol(str, NULL, 0) == 1))
+            TrapALCError = ALC_TRUE;
+        TrapALCError = GetConfigValueBool(NULL, NULL, "trap-alc-error", TrapALCError);
+    }
+
+    if(ConfigValueFloat(NULL, "reverb", "boost", &valf))
+        ReverbBoost *= powf(10.0f, valf / 20.0f);
+
+    EmulateEAXReverb = GetConfigValueBool(NULL, "reverb", "emulate-eax", AL_FALSE);
+
+    if(((devs=getenv("ALSOFT_DRIVERS")) && devs[0]) ||
+       ConfigValueStr(NULL, NULL, "drivers", &devs))
+    {
+        int n;
+        size_t len;
+        const char *next = devs;
+        int endlist, delitem;
+
+        i = 0;
+        do {
+            devs = next;
+            while(isspace(devs[0]))
+                devs++;
+            next = strchr(devs, ',');
+
+            delitem = (devs[0] == '-');
+            if(devs[0] == '-') devs++;
+
+            if(!devs[0] || devs[0] == ',')
+            {
+                endlist = 0;
+                continue;
+            }
+            endlist = 1;
+
+            len = (next ? ((size_t)(next-devs)) : strlen(devs));
+            while(len > 0 && isspace(devs[len-1]))
+                len--;
+            for(n = i;BackendList[n].name;n++)
+            {
+                if(len == strlen(BackendList[n].name) &&
+                   strncmp(BackendList[n].name, devs, len) == 0)
+                {
+                    if(delitem)
+                    {
+                        do {
+                            BackendList[n] = BackendList[n+1];
+                            ++n;
+                        } while(BackendList[n].name);
+                    }
+                    else
+                    {
+                        struct BackendInfo Bkp = BackendList[n];
+                        while(n > i)
+                        {
+                            BackendList[n] = BackendList[n-1];
+                            --n;
+                        }
+                        BackendList[n] = Bkp;
+
+                        i++;
+                    }
+                    break;
+                }
+            }
+        } while(next++);
+
+        if(endlist)
+        {
+            BackendList[i].name = NULL;
+            BackendList[i].getFactory = NULL;
+            BackendList[i].Init = NULL;
+            BackendList[i].Deinit = NULL;
+            BackendList[i].Probe = NULL;
+        }
+    }
+
+    for(i = 0;(BackendList[i].Init || BackendList[i].getFactory) && (!PlaybackBackend.name || !CaptureBackend.name);i++)
+    {
+        if(BackendList[i].getFactory)
+        {
+            ALCbackendFactory *factory = BackendList[i].getFactory();
+            if(!V0(factory,init)())
+            {
+                WARN("Failed to initialize backend \"%s\"\n", BackendList[i].name);
+                continue;
+            }
+
+            TRACE("Initialized backend \"%s\"\n", BackendList[i].name);
+            if(!PlaybackBackend.name && V(factory,querySupport)(ALCbackend_Playback))
+            {
+                PlaybackBackend = BackendList[i];
+                TRACE("Added \"%s\" for playback\n", PlaybackBackend.name);
+            }
+            if(!CaptureBackend.name && V(factory,querySupport)(ALCbackend_Capture))
+            {
+                CaptureBackend = BackendList[i];
+                TRACE("Added \"%s\" for capture\n", CaptureBackend.name);
+            }
+
+            continue;
+        }
+
+        if(!BackendList[i].Init(&BackendList[i].Funcs))
+        {
+            WARN("Failed to initialize backend \"%s\"\n", BackendList[i].name);
+            continue;
+        }
+
+        TRACE("Initialized backend \"%s\"\n", BackendList[i].name);
+        if(BackendList[i].Funcs.OpenPlayback && !PlaybackBackend.name)
+        {
+            PlaybackBackend = BackendList[i];
+            TRACE("Added \"%s\" for playback\n", PlaybackBackend.name);
+        }
+        if(BackendList[i].Funcs.OpenCapture && !CaptureBackend.name)
+        {
+            CaptureBackend = BackendList[i];
+            TRACE("Added \"%s\" for capture\n", CaptureBackend.name);
+        }
+    }
+    {
+        ALCbackendFactory *factory = ALCloopbackFactory_getFactory();
+        V0(factory,init)();
+    }
+
+    if(!PlaybackBackend.name)
+        WARN("No playback backend available!\n");
+    if(!CaptureBackend.name)
+        WARN("No capture backend available!\n");
+
+    if(ConfigValueStr(NULL, NULL, "excludefx", &str))
+    {
+        size_t len;
+        const char *next = str;
+
+        do {
+            str = next;
+            next = strchr(str, ',');
+
+            if(!str[0] || next == str)
+                continue;
+
+            len = (next ? ((size_t)(next-str)) : strlen(str));
+            for(n = 0;EffectList[n].name;n++)
+            {
+                if(len == strlen(EffectList[n].name) &&
+                   strncmp(EffectList[n].name, str, len) == 0)
+                    DisabledEffects[EffectList[n].type] = AL_TRUE;
+            }
+        } while(next++);
+    }
+
+    InitEffectFactoryMap();
+
+    InitEffect(&DefaultEffect);
+    str = getenv("ALSOFT_DEFAULT_REVERB");
+    if((str && str[0]) || ConfigValueStr(NULL, NULL, "default-reverb", &str))
+        LoadReverbPreset(str, &DefaultEffect);
+}
+#define DO_INITCONFIG() alcall_once(&alc_config_once, alc_initconfig)
+
+
+/************************************************
+ * Library deinitialization
+ ************************************************/
+static void alc_cleanup(void)
+{
+    ALCdevice *dev;
+
+    AL_STRING_DEINIT(alcAllDevicesList);
+    AL_STRING_DEINIT(alcCaptureDeviceList);
+
+    free(alcDefaultAllDevicesSpecifier);
+    alcDefaultAllDevicesSpecifier = NULL;
+    free(alcCaptureDefaultDeviceSpecifier);
+    alcCaptureDefaultDeviceSpecifier = NULL;
+
+    if((dev=ATOMIC_EXCHANGE(ALCdevice*, &DeviceList, NULL)) != NULL)
+    {
+        ALCuint num = 0;
+        do {
+            num++;
+        } while((dev=dev->next) != NULL);
+        ERR("%u device%s not closed\n", num, (num>1)?"s":"");
+    }
+
+    DeinitEffectFactoryMap();
+}
+
+static void alc_deinit_safe(void)
+{
+    alc_cleanup();
+
+    FreeHrtfs();
+    FreeALConfig();
+
+    ThunkExit();
+    almtx_destroy(&ListLock);
+    altss_delete(LocalContext);
+
+    if(LogFile != stderr)
+        fclose(LogFile);
+    LogFile = NULL;
+}
+
+static void alc_deinit(void)
+{
+    int i;
+
+    alc_cleanup();
+
+    memset(&PlaybackBackend, 0, sizeof(PlaybackBackend));
+    memset(&CaptureBackend, 0, sizeof(CaptureBackend));
+
+    for(i = 0;BackendList[i].Deinit || BackendList[i].getFactory;i++)
+    {
+        if(!BackendList[i].getFactory)
+            BackendList[i].Deinit();
+        else
+        {
+            ALCbackendFactory *factory = BackendList[i].getFactory();
+            V0(factory,deinit)();
+        }
+    }
+    {
+        ALCbackendFactory *factory = ALCloopbackFactory_getFactory();
+        V0(factory,deinit)();
+    }
+
+    alc_deinit_safe();
+}
+
+
+/************************************************
+ * Device enumeration
+ ************************************************/
+static void ProbeDevices(al_string *list, struct BackendInfo *backendinfo, enum DevProbe type)
+{
+    DO_INITCONFIG();
+
+    LockLists();
+    al_string_clear(list);
+
+    if(backendinfo->Probe)
+        backendinfo->Probe(type);
+    else if(backendinfo->getFactory)
+    {
+        ALCbackendFactory *factory = backendinfo->getFactory();
+        V(factory,probe)(type);
+    }
+
+    UnlockLists();
+}
+static void ProbeAllDevicesList(void)
+{ ProbeDevices(&alcAllDevicesList, &PlaybackBackend, ALL_DEVICE_PROBE); }
+static void ProbeCaptureDeviceList(void)
+{ ProbeDevices(&alcCaptureDeviceList, &CaptureBackend, CAPTURE_DEVICE_PROBE); }
+
+static void AppendDevice(const ALCchar *name, al_string *devnames)
+{
+    size_t len = strlen(name);
+    if(len > 0)
+        al_string_append_range(devnames, name, name+len+1);
+}
+void AppendAllDevicesList(const ALCchar *name)
+{ AppendDevice(name, &alcAllDevicesList); }
+void AppendCaptureDeviceList(const ALCchar *name)
+{ AppendDevice(name, &alcCaptureDeviceList); }
+
+
+/************************************************
+ * Device format information
+ ************************************************/
+const ALCchar *DevFmtTypeString(enum DevFmtType type)
+{
+    switch(type)
+    {
+    case DevFmtByte: return "Signed Byte";
+    case DevFmtUByte: return "Unsigned Byte";
+    case DevFmtShort: return "Signed Short";
+    case DevFmtUShort: return "Unsigned Short";
+    case DevFmtInt: return "Signed Int";
+    case DevFmtUInt: return "Unsigned Int";
+    case DevFmtFloat: return "Float";
+    }
+    return "(unknown type)";
+}
+const ALCchar *DevFmtChannelsString(enum DevFmtChannels chans)
+{
+    switch(chans)
+    {
+    case DevFmtMono: return "Mono";
+    case DevFmtStereo: return "Stereo";
+    case DevFmtQuad: return "Quadraphonic";
+    case DevFmtX51: return "5.1 Surround";
+    case DevFmtX51Rear: return "5.1 Surround (Rear)";
+    case DevFmtX61: return "6.1 Surround";
+    case DevFmtX71: return "7.1 Surround";
+    case DevFmtAmbi1: return "Ambisonic (1st Order)";
+    case DevFmtAmbi2: return "Ambisonic (2nd Order)";
+    case DevFmtAmbi3: return "Ambisonic (3rd Order)";
+    }
+    return "(unknown channels)";
+}
+
+extern inline ALuint FrameSizeFromDevFmt(enum DevFmtChannels chans, enum DevFmtType type);
+ALuint BytesFromDevFmt(enum DevFmtType type)
+{
+    switch(type)
+    {
+    case DevFmtByte: return sizeof(ALbyte);
+    case DevFmtUByte: return sizeof(ALubyte);
+    case DevFmtShort: return sizeof(ALshort);
+    case DevFmtUShort: return sizeof(ALushort);
+    case DevFmtInt: return sizeof(ALint);
+    case DevFmtUInt: return sizeof(ALuint);
+    case DevFmtFloat: return sizeof(ALfloat);
+    }
+    return 0;
+}
+ALuint ChannelsFromDevFmt(enum DevFmtChannels chans)
+{
+    switch(chans)
+    {
+    case DevFmtMono: return 1;
+    case DevFmtStereo: return 2;
+    case DevFmtQuad: return 4;
+    case DevFmtX51: return 6;
+    case DevFmtX51Rear: return 6;
+    case DevFmtX61: return 7;
+    case DevFmtX71: return 8;
+    case DevFmtAmbi1: return 4;
+    case DevFmtAmbi2: return 9;
+    case DevFmtAmbi3: return 16;
+    }
+    return 0;
+}
+
+static ALboolean DecomposeDevFormat(ALenum format, enum DevFmtChannels *chans,
+                                    enum DevFmtType *type)
+{
+    static const struct {
+        ALenum format;
+        enum DevFmtChannels channels;
+        enum DevFmtType type;
+    } list[] = {
+        { AL_FORMAT_MONO8,        DevFmtMono, DevFmtUByte },
+        { AL_FORMAT_MONO16,       DevFmtMono, DevFmtShort },
+        { AL_FORMAT_MONO_FLOAT32, DevFmtMono, DevFmtFloat },
+
+        { AL_FORMAT_STEREO8,        DevFmtStereo, DevFmtUByte },
+        { AL_FORMAT_STEREO16,       DevFmtStereo, DevFmtShort },
+        { AL_FORMAT_STEREO_FLOAT32, DevFmtStereo, DevFmtFloat },
+
+        { AL_FORMAT_QUAD8,  DevFmtQuad, DevFmtUByte },
+        { AL_FORMAT_QUAD16, DevFmtQuad, DevFmtShort },
+        { AL_FORMAT_QUAD32, DevFmtQuad, DevFmtFloat },
+
+        { AL_FORMAT_51CHN8,  DevFmtX51, DevFmtUByte },
+        { AL_FORMAT_51CHN16, DevFmtX51, DevFmtShort },
+        { AL_FORMAT_51CHN32, DevFmtX51, DevFmtFloat },
+
+        { AL_FORMAT_61CHN8,  DevFmtX61, DevFmtUByte },
+        { AL_FORMAT_61CHN16, DevFmtX61, DevFmtShort },
+        { AL_FORMAT_61CHN32, DevFmtX61, DevFmtFloat },
+
+        { AL_FORMAT_71CHN8,  DevFmtX71, DevFmtUByte },
+        { AL_FORMAT_71CHN16, DevFmtX71, DevFmtShort },
+        { AL_FORMAT_71CHN32, DevFmtX71, DevFmtFloat },
+    };
+    ALuint i;
+
+    for(i = 0;i < COUNTOF(list);i++)
+    {
+        if(list[i].format == format)
+        {
+            *chans = list[i].channels;
+            *type  = list[i].type;
+            return AL_TRUE;
+        }
+    }
+
+    return AL_FALSE;
+}
+
+static ALCboolean IsValidALCType(ALCenum type)
+{
+    switch(type)
+    {
+        case ALC_BYTE_SOFT:
+        case ALC_UNSIGNED_BYTE_SOFT:
+        case ALC_SHORT_SOFT:
+        case ALC_UNSIGNED_SHORT_SOFT:
+        case ALC_INT_SOFT:
+        case ALC_UNSIGNED_INT_SOFT:
+        case ALC_FLOAT_SOFT:
+            return ALC_TRUE;
+    }
+    return ALC_FALSE;
+}
+
+static ALCboolean IsValidALCChannels(ALCenum channels)
+{
+    switch(channels)
+    {
+        case ALC_MONO_SOFT:
+        case ALC_STEREO_SOFT:
+        case ALC_QUAD_SOFT:
+        case ALC_5POINT1_SOFT:
+        case ALC_6POINT1_SOFT:
+        case ALC_7POINT1_SOFT:
+            return ALC_TRUE;
+    }
+    return ALC_FALSE;
+}
+
+
+/************************************************
+ * Miscellaneous ALC helpers
+ ************************************************/
+
+extern inline void LockContext(ALCcontext *context);
+extern inline void UnlockContext(ALCcontext *context);
+
+void ALCdevice_Lock(ALCdevice *device)
+{
+    V0(device->Backend,lock)();
+}
+
+void ALCdevice_Unlock(ALCdevice *device)
+{
+    V0(device->Backend,unlock)();
+}
+
+
+/* SetDefaultWFXChannelOrder
+ *
+ * Sets the default channel order used by WaveFormatEx.
+ */
+void SetDefaultWFXChannelOrder(ALCdevice *device)
+{
+    ALuint i;
+
+    for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
+        device->RealOut.ChannelName[i] = InvalidChannel;
+
+    switch(device->FmtChans)
+    {
+    case DevFmtMono:
+        device->RealOut.ChannelName[0] = FrontCenter;
+        break;
+    case DevFmtStereo:
+        device->RealOut.ChannelName[0] = FrontLeft;
+        device->RealOut.ChannelName[1] = FrontRight;
+        break;
+    case DevFmtQuad:
+        device->RealOut.ChannelName[0] = FrontLeft;
+        device->RealOut.ChannelName[1] = FrontRight;
+        device->RealOut.ChannelName[2] = BackLeft;
+        device->RealOut.ChannelName[3] = BackRight;
+        break;
+    case DevFmtX51:
+        device->RealOut.ChannelName[0] = FrontLeft;
+        device->RealOut.ChannelName[1] = FrontRight;
+        device->RealOut.ChannelName[2] = FrontCenter;
+        device->RealOut.ChannelName[3] = LFE;
+        device->RealOut.ChannelName[4] = SideLeft;
+        device->RealOut.ChannelName[5] = SideRight;
+        break;
+    case DevFmtX51Rear:
+        device->RealOut.ChannelName[0] = FrontLeft;
+        device->RealOut.ChannelName[1] = FrontRight;
+        device->RealOut.ChannelName[2] = FrontCenter;
+        device->RealOut.ChannelName[3] = LFE;
+        device->RealOut.ChannelName[4] = BackLeft;
+        device->RealOut.ChannelName[5] = BackRight;
+        break;
+    case DevFmtX61:
+        device->RealOut.ChannelName[0] = FrontLeft;
+        device->RealOut.ChannelName[1] = FrontRight;
+        device->RealOut.ChannelName[2] = FrontCenter;
+        device->RealOut.ChannelName[3] = LFE;
+        device->RealOut.ChannelName[4] = BackCenter;
+        device->RealOut.ChannelName[5] = SideLeft;
+        device->RealOut.ChannelName[6] = SideRight;
+        break;
+    case DevFmtX71:
+        device->RealOut.ChannelName[0] = FrontLeft;
+        device->RealOut.ChannelName[1] = FrontRight;
+        device->RealOut.ChannelName[2] = FrontCenter;
+        device->RealOut.ChannelName[3] = LFE;
+        device->RealOut.ChannelName[4] = BackLeft;
+        device->RealOut.ChannelName[5] = BackRight;
+        device->RealOut.ChannelName[6] = SideLeft;
+        device->RealOut.ChannelName[7] = SideRight;
+        break;
+    case DevFmtAmbi1:
+        device->RealOut.ChannelName[0] = Aux0;
+        device->RealOut.ChannelName[1] = Aux1;
+        device->RealOut.ChannelName[2] = Aux2;
+        device->RealOut.ChannelName[3] = Aux3;
+        break;
+    case DevFmtAmbi2:
+        device->RealOut.ChannelName[0] = Aux0;
+        device->RealOut.ChannelName[1] = Aux1;
+        device->RealOut.ChannelName[2] = Aux2;
+        device->RealOut.ChannelName[3] = Aux3;
+        device->RealOut.ChannelName[4] = Aux4;
+        device->RealOut.ChannelName[5] = Aux5;
+        device->RealOut.ChannelName[6] = Aux6;
+        device->RealOut.ChannelName[7] = Aux7;
+        device->RealOut.ChannelName[8] = Aux8;
+        break;
+    case DevFmtAmbi3:
+        device->RealOut.ChannelName[0] = Aux0;
+        device->RealOut.ChannelName[1] = Aux1;
+        device->RealOut.ChannelName[2] = Aux2;
+        device->RealOut.ChannelName[3] = Aux3;
+        device->RealOut.ChannelName[4] = Aux4;
+        device->RealOut.ChannelName[5] = Aux5;
+        device->RealOut.ChannelName[6] = Aux6;
+        device->RealOut.ChannelName[7] = Aux7;
+        device->RealOut.ChannelName[8] = Aux8;
+        device->RealOut.ChannelName[9] = Aux9;
+        device->RealOut.ChannelName[10] = Aux10;
+        device->RealOut.ChannelName[11] = Aux11;
+        device->RealOut.ChannelName[12] = Aux12;
+        device->RealOut.ChannelName[13] = Aux13;
+        device->RealOut.ChannelName[14] = Aux14;
+        device->RealOut.ChannelName[15] = Aux15;
+        break;
+    }
+}
+
+/* SetDefaultChannelOrder
+ *
+ * Sets the default channel order used by most non-WaveFormatEx-based APIs.
+ */
+void SetDefaultChannelOrder(ALCdevice *device)
+{
+    ALuint i;
+
+    for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
+        device->RealOut.ChannelName[i] = InvalidChannel;
+
+    switch(device->FmtChans)
+    {
+    case DevFmtX51Rear:
+        device->RealOut.ChannelName[0] = FrontLeft;
+        device->RealOut.ChannelName[1] = FrontRight;
+        device->RealOut.ChannelName[2] = BackLeft;
+        device->RealOut.ChannelName[3] = BackRight;
+        device->RealOut.ChannelName[4] = FrontCenter;
+        device->RealOut.ChannelName[5] = LFE;
+        return;
+    case DevFmtX71:
+        device->RealOut.ChannelName[0] = FrontLeft;
+        device->RealOut.ChannelName[1] = FrontRight;
+        device->RealOut.ChannelName[2] = BackLeft;
+        device->RealOut.ChannelName[3] = BackRight;
+        device->RealOut.ChannelName[4] = FrontCenter;
+        device->RealOut.ChannelName[5] = LFE;
+        device->RealOut.ChannelName[6] = SideLeft;
+        device->RealOut.ChannelName[7] = SideRight;
+        return;
+
+    /* Same as WFX order */
+    case DevFmtMono:
+    case DevFmtStereo:
+    case DevFmtQuad:
+    case DevFmtX51:
+    case DevFmtX61:
+    case DevFmtAmbi1:
+    case DevFmtAmbi2:
+    case DevFmtAmbi3:
+        SetDefaultWFXChannelOrder(device);
+        break;
+    }
+}
+
+extern inline ALint GetChannelIndex(const enum Channel names[MAX_OUTPUT_CHANNELS], enum Channel chan);
+
+
+/* ALCcontext_DeferUpdates
+ *
+ * Defers/suspends updates for the given context's listener and sources. This
+ * does *NOT* stop mixing, but rather prevents certain property changes from
+ * taking effect.
+ */
+void ALCcontext_DeferUpdates(ALCcontext *context, ALenum type)
+{
+    ATOMIC_STORE(&context->DeferUpdates, type);
+}
+
+/* ALCcontext_ProcessUpdates
+ *
+ * Resumes update processing after being deferred.
+ */
+void ALCcontext_ProcessUpdates(ALCcontext *context)
+{
+    ALCdevice *device = context->Device;
+
+    ReadLock(&context->PropLock);
+    if(ATOMIC_EXCHANGE(ALenum, &context->DeferUpdates, AL_FALSE))
+    {
+        ALsizei pos;
+        uint updates;
+
+        /* Tell the mixer to stop applying updates, then wait for any active
+         * updating to finish, before providing updates.
+         */
+        ATOMIC_STORE(&context->HoldUpdates, AL_TRUE);
+        while(((updates=ReadRef(&context->UpdateCount))&1) != 0)
+            althrd_yield();
+
+        UpdateListenerProps(context);
+        UpdateAllEffectSlotProps(context);
+
+        LockUIntMapRead(&context->SourceMap);
+        V0(device->Backend,lock)();
+        for(pos = 0;pos < context->SourceMap.size;pos++)
+        {
+            ALsource *Source = context->SourceMap.values[pos];
+            ALenum new_state;
+
+            if((Source->state == AL_PLAYING || Source->state == AL_PAUSED) &&
+               Source->OffsetType != AL_NONE)
+            {
+                WriteLock(&Source->queue_lock);
+                ApplyOffset(Source);
+                WriteUnlock(&Source->queue_lock);
+            }
+
+            new_state = Source->new_state;
+            Source->new_state = AL_NONE;
+            if(new_state)
+                SetSourceState(Source, context, new_state);
+        }
+        V0(device->Backend,unlock)();
+        UnlockUIntMapRead(&context->SourceMap);
+
+        UpdateAllSourceProps(context);
+
+        /* Now with all updates declared, let the mixer continue applying them
+         * so they all happen at once.
+         */
+        ATOMIC_STORE(&context->HoldUpdates, AL_FALSE);
+    }
+    ReadUnlock(&context->PropLock);
+}
+
+
+/* alcSetError
+ *
+ * Stores the latest ALC device error
+ */
+static void alcSetError(ALCdevice *device, ALCenum errorCode)
+{
+    if(TrapALCError)
+    {
+#ifdef _WIN32
+        /* DebugBreak() will cause an exception if there is no debugger */
+        if(IsDebuggerPresent())
+            DebugBreak();
+#elif defined(SIGTRAP)
+        raise(SIGTRAP);
+#endif
+    }
+
+    if(device)
+        ATOMIC_STORE(&device->LastError, errorCode);
+    else
+        ATOMIC_STORE(&LastNullDeviceError, errorCode);
+}
+
+
+/* UpdateClockBase
+ *
+ * Updates the device's base clock time with however many samples have been
+ * done. This is used so frequency changes on the device don't cause the time
+ * to jump forward or back.
+ */
+static inline void UpdateClockBase(ALCdevice *device)
+{
+    device->ClockBase += device->SamplesDone * DEVICE_CLOCK_RES / device->Frequency;
+    device->SamplesDone = 0;
+}
+
+/* UpdateDeviceParams
+ *
+ * Updates device parameters according to the attribute list (caller is
+ * responsible for holding the list lock).
+ */
+static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
+{
+    ALCcontext *context;
+    enum HrtfRequestMode hrtf_appreq = Hrtf_Default;
+    enum HrtfRequestMode hrtf_userreq = Hrtf_Default;
+    enum DevFmtChannels oldChans;
+    enum DevFmtType oldType;
+    ALCuint oldFreq;
+    FPUCtl oldMode;
+    ALCsizei hrtf_id = -1;
+    size_t size;
+
+    // Check for attributes
+    if(device->Type == Loopback)
+    {
+        enum {
+            GotFreq  = 1<<0,
+            GotChans = 1<<1,
+            GotType  = 1<<2,
+            GotAll   = GotFreq|GotChans|GotType
+        };
+        ALCuint freq, numMono, numStereo, numSends;
+        enum DevFmtChannels schans;
+        enum DevFmtType stype;
+        ALCuint attrIdx = 0;
+        ALCint gotFmt = 0;
+
+        if(!attrList)
+        {
+            WARN("Missing attributes for loopback device\n");
+            return ALC_INVALID_VALUE;
+        }
+
+        numMono = device->NumMonoSources;
+        numStereo = device->NumStereoSources;
+        numSends = device->NumAuxSends;
+        schans = device->FmtChans;
+        stype = device->FmtType;
+        freq = device->Frequency;
+
+#define TRACE_ATTR(a, v) TRACE("Loopback %s = %d\n", #a, v)
+        while(attrList[attrIdx])
+        {
+            if(attrList[attrIdx] == ALC_FORMAT_CHANNELS_SOFT)
+            {
+                ALCint val = attrList[attrIdx + 1];
+                TRACE_ATTR(ALC_FORMAT_CHANNELS_SOFT, val);
+                if(!IsValidALCChannels(val) || !ChannelsFromDevFmt(val))
+                    return ALC_INVALID_VALUE;
+                schans = val;
+                gotFmt |= GotChans;
+            }
+
+            if(attrList[attrIdx] == ALC_FORMAT_TYPE_SOFT)
+            {
+                ALCint val = attrList[attrIdx + 1];
+                TRACE_ATTR(ALC_FORMAT_TYPE_SOFT, val);
+                if(!IsValidALCType(val) || !BytesFromDevFmt(val))
+                    return ALC_INVALID_VALUE;
+                stype = val;
+                gotFmt |= GotType;
+            }
+
+            if(attrList[attrIdx] == ALC_FREQUENCY)
+            {
+                freq = attrList[attrIdx + 1];
+                TRACE_ATTR(ALC_FREQUENCY, freq);
+                if(freq < MIN_OUTPUT_RATE)
+                    return ALC_INVALID_VALUE;
+                gotFmt |= GotFreq;
+            }
+
+            if(attrList[attrIdx] == ALC_STEREO_SOURCES)
+            {
+                numStereo = attrList[attrIdx + 1];
+                TRACE_ATTR(ALC_STEREO_SOURCES, numStereo);
+                if(numStereo > device->SourcesMax)
+                    numStereo = device->SourcesMax;
+
+                numMono = device->SourcesMax - numStereo;
+            }
+
+            if(attrList[attrIdx] == ALC_MAX_AUXILIARY_SENDS)
+            {
+                numSends = attrList[attrIdx + 1];
+                TRACE_ATTR(ALC_MAX_AUXILIARY_SENDS, numSends);
+            }
+
+            if(attrList[attrIdx] == ALC_HRTF_SOFT)
+            {
+                TRACE_ATTR(ALC_HRTF_SOFT, attrList[attrIdx + 1]);
+                if(attrList[attrIdx + 1] == ALC_FALSE)
+                    hrtf_appreq = Hrtf_Disable;
+                else if(attrList[attrIdx + 1] == ALC_TRUE)
+                    hrtf_appreq = Hrtf_Enable;
+                else
+                    hrtf_appreq = Hrtf_Default;
+            }
+
+            if(attrList[attrIdx] == ALC_HRTF_ID_SOFT)
+            {
+                hrtf_id = attrList[attrIdx + 1];
+                TRACE_ATTR(ALC_HRTF_ID_SOFT, hrtf_id);
+            }
+
+            attrIdx += 2;
+        }
+#undef TRACE_ATTR
+
+        if(gotFmt != GotAll)
+        {
+            WARN("Missing format for loopback device\n");
+            return ALC_INVALID_VALUE;
+        }
+
+        ConfigValueUInt(NULL, NULL, "sends", &numSends);
+        numSends = minu(MAX_SENDS, numSends);
+
+        if((device->Flags&DEVICE_RUNNING))
+            V0(device->Backend,stop)();
+        device->Flags &= ~DEVICE_RUNNING;
+
+        UpdateClockBase(device);
+
+        device->Frequency = freq;
+        device->FmtChans = schans;
+        device->FmtType = stype;
+        device->NumMonoSources = numMono;
+        device->NumStereoSources = numStereo;
+        device->NumAuxSends = numSends;
+    }
+    else if(attrList && attrList[0])
+    {
+        ALCuint freq, numMono, numStereo, numSends;
+        ALCuint attrIdx = 0;
+
+        /* If a context is already running on the device, stop playback so the
+         * device attributes can be updated. */
+        if((device->Flags&DEVICE_RUNNING))
+            V0(device->Backend,stop)();
+        device->Flags &= ~DEVICE_RUNNING;
+
+        freq = device->Frequency;
+        numMono = device->NumMonoSources;
+        numStereo = device->NumStereoSources;
+        numSends = device->NumAuxSends;
+
+#define TRACE_ATTR(a, v) TRACE("%s = %d\n", #a, v)
+        while(attrList[attrIdx])
+        {
+            if(attrList[attrIdx] == ALC_FREQUENCY)
+            {
+                freq = attrList[attrIdx + 1];
+                device->Flags |= DEVICE_FREQUENCY_REQUEST;
+                TRACE_ATTR(ALC_FREQUENCY, freq);
+            }
+
+            if(attrList[attrIdx] == ALC_STEREO_SOURCES)
+            {
+                numStereo = attrList[attrIdx + 1];
+                TRACE_ATTR(ALC_STEREO_SOURCES, numStereo);
+                if(numStereo > device->SourcesMax)
+                    numStereo = device->SourcesMax;
+
+                numMono = device->SourcesMax - numStereo;
+            }
+
+            if(attrList[attrIdx] == ALC_MAX_AUXILIARY_SENDS)
+            {
+                numSends = attrList[attrIdx + 1];
+                TRACE_ATTR(ALC_MAX_AUXILIARY_SENDS, numSends);
+            }
+
+            if(attrList[attrIdx] == ALC_HRTF_SOFT)
+            {
+                TRACE_ATTR(ALC_HRTF_SOFT, attrList[attrIdx + 1]);
+                if(attrList[attrIdx + 1] == ALC_FALSE)
+                    hrtf_appreq = Hrtf_Disable;
+                else if(attrList[attrIdx + 1] == ALC_TRUE)
+                    hrtf_appreq = Hrtf_Enable;
+                else
+                    hrtf_appreq = Hrtf_Default;
+            }
+
+            if(attrList[attrIdx] == ALC_HRTF_ID_SOFT)
+            {
+                hrtf_id = attrList[attrIdx + 1];
+                TRACE_ATTR(ALC_HRTF_ID_SOFT, hrtf_id);
+            }
+
+            attrIdx += 2;
+        }
+#undef TRACE_ATTR
+
+        ConfigValueUInt(al_string_get_cstr(device->DeviceName), NULL, "frequency", &freq);
+        freq = maxu(freq, MIN_OUTPUT_RATE);
+
+        ConfigValueUInt(al_string_get_cstr(device->DeviceName), NULL, "sends", &numSends);
+        numSends = minu(MAX_SENDS, numSends);
+
+        UpdateClockBase(device);
+
+        device->UpdateSize = (ALuint64)device->UpdateSize * freq /
+                             device->Frequency;
+        /* SSE and Neon do best with the update size being a multiple of 4 */
+        if((CPUCapFlags&(CPU_CAP_SSE|CPU_CAP_NEON)) != 0)
+            device->UpdateSize = (device->UpdateSize+3)&~3;
+
+        device->Frequency = freq;
+        device->NumMonoSources = numMono;
+        device->NumStereoSources = numStereo;
+        device->NumAuxSends = numSends;
+    }
+
+    if((device->Flags&DEVICE_RUNNING))
+        return ALC_NO_ERROR;
+
+    al_free(device->Uhj_Encoder);
+    device->Uhj_Encoder = NULL;
+
+    al_free(device->Bs2b);
+    device->Bs2b = NULL;
+
+    al_free(device->Dry.Buffer);
+    device->Dry.Buffer = NULL;
+    device->Dry.NumChannels = 0;
+    device->FOAOut.Buffer = NULL;
+    device->FOAOut.NumChannels = 0;
+    device->RealOut.Buffer = NULL;
+    device->RealOut.NumChannels = 0;
+
+    UpdateClockBase(device);
+
+    /*************************************************************************
+     * Update device format request if HRTF is requested
+     */
+    device->Hrtf.Status = ALC_HRTF_DISABLED_SOFT;
+    if(device->Type != Loopback)
+    {
+        const char *hrtf;
+        if(ConfigValueStr(al_string_get_cstr(device->DeviceName), NULL, "hrtf", &hrtf))
+        {
+            if(strcasecmp(hrtf, "true") == 0)
+                hrtf_userreq = Hrtf_Enable;
+            else if(strcasecmp(hrtf, "false") == 0)
+                hrtf_userreq = Hrtf_Disable;
+            else if(strcasecmp(hrtf, "auto") != 0)
+                ERR("Unexpected hrtf value: %s\n", hrtf);
+        }
+
+        if(hrtf_userreq == Hrtf_Enable || (hrtf_userreq != Hrtf_Disable && hrtf_appreq == Hrtf_Enable))
+        {
+            if(VECTOR_SIZE(device->Hrtf.List) == 0)
+            {
+                VECTOR_DEINIT(device->Hrtf.List);
+                device->Hrtf.List = EnumerateHrtf(device->DeviceName);
+            }
+            if(VECTOR_SIZE(device->Hrtf.List) > 0)
+            {
+                device->FmtChans = DevFmtStereo;
+                if(hrtf_id >= 0 && (size_t)hrtf_id < VECTOR_SIZE(device->Hrtf.List))
+                    device->Frequency = VECTOR_ELEM(device->Hrtf.List, hrtf_id).hrtf->sampleRate;
+                else
+                    device->Frequency = VECTOR_ELEM(device->Hrtf.List, 0).hrtf->sampleRate;
+                device->Flags |= DEVICE_CHANNELS_REQUEST | DEVICE_FREQUENCY_REQUEST;
+            }
+            else
+            {
+                hrtf_userreq = Hrtf_Default;
+                hrtf_appreq = Hrtf_Disable;
+                device->Hrtf.Status = ALC_HRTF_UNSUPPORTED_FORMAT_SOFT;
+            }
+        }
+    }
+    else if(hrtf_appreq == Hrtf_Enable)
+    {
+        size_t i = VECTOR_SIZE(device->Hrtf.List);
+        /* Loopback device. We don't need to match to a specific HRTF entry
+         * here. If the requested ID matches, we'll pick that later, if not,
+         * we'll try to auto-select one anyway. Just make sure one exists
+         * that'll work.
+         */
+        if(device->FmtChans == DevFmtStereo)
+        {
+            if(VECTOR_SIZE(device->Hrtf.List) == 0)
+            {
+                VECTOR_DEINIT(device->Hrtf.List);
+                device->Hrtf.List = EnumerateHrtf(device->DeviceName);
+            }
+            for(i = 0;i < VECTOR_SIZE(device->Hrtf.List);i++)
+            {
+                const struct Hrtf *hrtf = VECTOR_ELEM(device->Hrtf.List, i).hrtf;
+                if(hrtf->sampleRate == device->Frequency)
+                    break;
+            }
+        }
+        if(i == VECTOR_SIZE(device->Hrtf.List))
+        {
+            ERR("Requested format not HRTF compatible: %s, %uhz\n",
+                DevFmtChannelsString(device->FmtChans), device->Frequency);
+            hrtf_appreq = Hrtf_Disable;
+            device->Hrtf.Status = ALC_HRTF_UNSUPPORTED_FORMAT_SOFT;
+        }
+    }
+
+    oldFreq  = device->Frequency;
+    oldChans = device->FmtChans;
+    oldType  = device->FmtType;
+
+    TRACE("Pre-reset: %s%s, %s%s, %s%uhz, %u update size x%d\n",
+        (device->Flags&DEVICE_CHANNELS_REQUEST)?"*":"", DevFmtChannelsString(device->FmtChans),
+        (device->Flags&DEVICE_SAMPLE_TYPE_REQUEST)?"*":"", DevFmtTypeString(device->FmtType),
+        (device->Flags&DEVICE_FREQUENCY_REQUEST)?"*":"", device->Frequency,
+        device->UpdateSize, device->NumUpdates
+    );
+
+    if(V0(device->Backend,reset)() == ALC_FALSE)
+        return ALC_INVALID_DEVICE;
+
+    if(device->FmtChans != oldChans && (device->Flags&DEVICE_CHANNELS_REQUEST))
+    {
+        ERR("Failed to set %s, got %s instead\n", DevFmtChannelsString(oldChans),
+            DevFmtChannelsString(device->FmtChans));
+        device->Flags &= ~DEVICE_CHANNELS_REQUEST;
+    }
+    if(device->FmtType != oldType && (device->Flags&DEVICE_SAMPLE_TYPE_REQUEST))
+    {
+        ERR("Failed to set %s, got %s instead\n", DevFmtTypeString(oldType),
+            DevFmtTypeString(device->FmtType));
+        device->Flags &= ~DEVICE_SAMPLE_TYPE_REQUEST;
+    }
+    if(device->Frequency != oldFreq && (device->Flags&DEVICE_FREQUENCY_REQUEST))
+    {
+        ERR("Failed to set %uhz, got %uhz instead\n", oldFreq, device->Frequency);
+        device->Flags &= ~DEVICE_FREQUENCY_REQUEST;
+    }
+
+    if((device->UpdateSize&3) != 0)
+    {
+        if((CPUCapFlags&CPU_CAP_SSE))
+            WARN("SSE performs best with multiple of 4 update sizes (%u)\n", device->UpdateSize);
+        if((CPUCapFlags&CPU_CAP_NEON))
+            WARN("NEON performs best with multiple of 4 update sizes (%u)\n", device->UpdateSize);
+    }
+
+    TRACE("Post-reset: %s, %s, %uhz, %u update size x%d\n",
+        DevFmtChannelsString(device->FmtChans), DevFmtTypeString(device->FmtType),
+        device->Frequency, device->UpdateSize, device->NumUpdates
+    );
+
+    aluInitRenderer(device, hrtf_id, hrtf_appreq, hrtf_userreq);
+
+    /* Allocate extra channels for any post-filter output. */
+    size = device->Dry.NumChannels * sizeof(device->Dry.Buffer[0]);
+    if((device->AmbiDecoder && bformatdec_getOrder(device->AmbiDecoder) >= 2))
+        size += (ChannelsFromDevFmt(device->FmtChans)+4) * sizeof(device->Dry.Buffer[0]);
+    else if(device->Hrtf.Handle || device->Uhj_Encoder || device->AmbiDecoder)
+        size += ChannelsFromDevFmt(device->FmtChans) * sizeof(device->Dry.Buffer[0]);
+    else if(device->FmtChans > DevFmtAmbi1 && device->FmtChans <= DevFmtAmbi3)
+        size += 4 * sizeof(device->Dry.Buffer[0]);
+    device->Dry.Buffer = al_calloc(16, size);
+    if(!device->Dry.Buffer)
+    {
+        ERR("Failed to allocate "SZFMT" bytes for mix buffer\n", size);
+        return ALC_INVALID_DEVICE;
+    }
+
+    if(device->Hrtf.Handle || device->Uhj_Encoder || device->AmbiDecoder)
+    {
+        device->RealOut.Buffer = device->Dry.Buffer + device->Dry.NumChannels;
+        device->RealOut.NumChannels = ChannelsFromDevFmt(device->FmtChans);
+    }
+    else
+    {
+        device->RealOut.Buffer = device->Dry.Buffer;
+        device->RealOut.NumChannels = device->Dry.NumChannels;
+    }
+
+    if((device->AmbiDecoder && bformatdec_getOrder(device->AmbiDecoder) >= 2) ||
+       (device->FmtChans > DevFmtAmbi1 && device->FmtChans <= DevFmtAmbi3))
+    {
+        /* Higher-order rendering requires upsampling first-order content, so
+         * make sure to mix it separately.
+         */
+        device->FOAOut.Buffer = device->RealOut.Buffer + device->RealOut.NumChannels;
+        device->FOAOut.NumChannels = 4;
+    }
+    else
+    {
+        device->FOAOut.Buffer = device->Dry.Buffer;
+        device->FOAOut.NumChannels = device->Dry.NumChannels;
+    }
+
+    SetMixerFPUMode(&oldMode);
+    if(device->DefaultSlot)
+    {
+        ALeffectslot *slot = device->DefaultSlot;
+        ALeffectState *state = slot->Effect.State;
+
+        state->OutBuffer = device->Dry.Buffer;
+        state->OutChannels = device->Dry.NumChannels;
+        if(V(state,deviceUpdate)(device) == AL_FALSE)
+        {
+            RestoreFPUMode(&oldMode);
+            return ALC_INVALID_DEVICE;
+        }
+        UpdateEffectSlotProps(slot);
+    }
+
+    context = ATOMIC_LOAD(&device->ContextList);
+    while(context)
+    {
+        ALsizei pos;
+
+        ReadLock(&context->PropLock);
+        LockUIntMapRead(&context->EffectSlotMap);
+        for(pos = 0;pos < context->EffectSlotMap.size;pos++)
+        {
+            ALeffectslot *slot = context->EffectSlotMap.values[pos];
+            ALeffectState *state = slot->Effect.State;
+
+            state->OutBuffer = device->Dry.Buffer;
+            state->OutChannels = device->Dry.NumChannels;
+            if(V(state,deviceUpdate)(device) == AL_FALSE)
+            {
+                UnlockUIntMapRead(&context->EffectSlotMap);
+                ReadUnlock(&context->PropLock);
+                RestoreFPUMode(&oldMode);
+                return ALC_INVALID_DEVICE;
+            }
+
+            UpdateEffectSlotProps(slot);
+        }
+        UnlockUIntMapRead(&context->EffectSlotMap);
+
+        LockUIntMapRead(&context->SourceMap);
+        for(pos = 0;pos < context->SourceMap.size;pos++)
+        {
+            ALsource *source = context->SourceMap.values[pos];
+            ALuint s = device->NumAuxSends;
+            while(s < MAX_SENDS)
+            {
+                if(source->Send[s].Slot)
+                    DecrementRef(&source->Send[s].Slot->ref);
+                source->Send[s].Slot = NULL;
+                source->Send[s].Gain = 1.0f;
+                source->Send[s].GainHF = 1.0f;
+                source->Send[s].HFReference = LOWPASSFREQREF;
+                source->Send[s].GainLF = 1.0f;
+                source->Send[s].LFReference = HIGHPASSFREQREF;
+                s++;
+            }
+        }
+        UnlockUIntMapRead(&context->SourceMap);
+
+        UpdateAllSourceProps(context);
+        ReadUnlock(&context->PropLock);
+
+        context = context->next;
+    }
+    RestoreFPUMode(&oldMode);
+
+    if(!(device->Flags&DEVICE_PAUSED))
+    {
+        if(V0(device->Backend,start)() == ALC_FALSE)
+            return ALC_INVALID_DEVICE;
+        device->Flags |= DEVICE_RUNNING;
+    }
+
+    return ALC_NO_ERROR;
+}
+
+/* FreeDevice
+ *
+ * Frees the device structure, and destroys any objects the app failed to
+ * delete. Called once there's no more references on the device.
+ */
+static ALCvoid FreeDevice(ALCdevice *device)
+{
+    TRACE("%p\n", device);
+
+    V0(device->Backend,close)();
+    DELETE_OBJ(device->Backend);
+    device->Backend = NULL;
+
+    almtx_destroy(&device->BackendLock);
+
+    if(device->DefaultSlot)
+    {
+        DeinitEffectSlot(device->DefaultSlot);
+        device->DefaultSlot = NULL;
+    }
+
+    if(device->BufferMap.size > 0)
+    {
+        WARN("(%p) Deleting %d Buffer%s\n", device, device->BufferMap.size,
+             (device->BufferMap.size==1)?"":"s");
+        ReleaseALBuffers(device);
+    }
+    ResetUIntMap(&device->BufferMap);
+
+    if(device->EffectMap.size > 0)
+    {
+        WARN("(%p) Deleting %d Effect%s\n", device, device->EffectMap.size,
+             (device->EffectMap.size==1)?"":"s");
+        ReleaseALEffects(device);
+    }
+    ResetUIntMap(&device->EffectMap);
+
+    if(device->FilterMap.size > 0)
+    {
+        WARN("(%p) Deleting %d Filter%s\n", device, device->FilterMap.size,
+             (device->FilterMap.size==1)?"":"s");
+        ReleaseALFilters(device);
+    }
+    ResetUIntMap(&device->FilterMap);
+
+    AL_STRING_DEINIT(device->Hrtf.Name);
+    FreeHrtfList(&device->Hrtf.List);
+
+    al_free(device->Bs2b);
+    device->Bs2b = NULL;
+
+    al_free(device->Uhj_Encoder);
+    device->Uhj_Encoder = NULL;
+
+    bformatdec_free(device->AmbiDecoder);
+    device->AmbiDecoder = NULL;
+
+    ambiup_free(device->AmbiUp);
+    device->AmbiUp = NULL;
+
+    AL_STRING_DEINIT(device->DeviceName);
+
+    al_free(device->Dry.Buffer);
+    device->Dry.Buffer = NULL;
+    device->Dry.NumChannels = 0;
+    device->FOAOut.Buffer = NULL;
+    device->FOAOut.NumChannels = 0;
+    device->RealOut.Buffer = NULL;
+    device->RealOut.NumChannels = 0;
+
+    al_free(device);
+}
+
+
+void ALCdevice_IncRef(ALCdevice *device)
+{
+    uint ref;
+    ref = IncrementRef(&device->ref);
+    TRACEREF("%p increasing refcount to %u\n", device, ref);
+}
+
+void ALCdevice_DecRef(ALCdevice *device)
+{
+    uint ref;
+    ref = DecrementRef(&device->ref);
+    TRACEREF("%p decreasing refcount to %u\n", device, ref);
+    if(ref == 0) FreeDevice(device);
+}
+
+/* VerifyDevice
+ *
+ * Checks if the device handle is valid, and increments its ref count if so.
+ */
+static ALCboolean VerifyDevice(ALCdevice **device)
+{
+    ALCdevice *tmpDevice;
+
+    LockLists();
+    tmpDevice = ATOMIC_LOAD(&DeviceList);
+    while(tmpDevice)
+    {
+        if(tmpDevice == *device)
+        {
+            ALCdevice_IncRef(tmpDevice);
+            UnlockLists();
+            return ALC_TRUE;
+        }
+        tmpDevice = tmpDevice->next;
+    }
+    UnlockLists();
+
+    *device = NULL;
+    return ALC_FALSE;
+}
+
+
+/* InitContext
+ *
+ * Initializes context fields
+ */
+static ALvoid InitContext(ALCcontext *Context)
+{
+    ALlistener *listener = Context->Listener;
+
+    //Initialise listener
+    listener->Gain = 1.0f;
+    listener->MetersPerUnit = 1.0f;
+    listener->Position[0] = 0.0f;
+    listener->Position[1] = 0.0f;
+    listener->Position[2] = 0.0f;
+    listener->Velocity[0] = 0.0f;
+    listener->Velocity[1] = 0.0f;
+    listener->Velocity[2] = 0.0f;
+    listener->Forward[0] = 0.0f;
+    listener->Forward[1] = 0.0f;
+    listener->Forward[2] = -1.0f;
+    listener->Up[0] = 0.0f;
+    listener->Up[1] = 1.0f;
+    listener->Up[2] = 0.0f;
+
+    aluMatrixfSet(&listener->Params.Matrix,
+        1.0f, 0.0f, 0.0f, 0.0f,
+        0.0f, 1.0f, 0.0f, 0.0f,
+        0.0f, 0.0f, 1.0f, 0.0f,
+        0.0f, 0.0f, 0.0f, 1.0f
+    );
+    aluVectorSet(&listener->Params.Velocity, 0.0f, 0.0f, 0.0f, 0.0f);
+    listener->Params.Gain = 1.0f;
+    listener->Params.MetersPerUnit = 1.0f;
+    listener->Params.DopplerFactor = 1.0f;
+    listener->Params.SpeedOfSound = SPEEDOFSOUNDMETRESPERSEC;
+
+    ATOMIC_INIT(&listener->Update, NULL);
+    ATOMIC_INIT(&listener->FreeList, NULL);
+
+    //Validate Context
+    InitRef(&Context->UpdateCount, 0);
+    ATOMIC_INIT(&Context->HoldUpdates, AL_FALSE);
+    Context->GainBoost = 1.0f;
+    RWLockInit(&Context->PropLock);
+    ATOMIC_INIT(&Context->LastError, AL_NO_ERROR);
+    InitUIntMap(&Context->SourceMap, Context->Device->SourcesMax);
+    InitUIntMap(&Context->EffectSlotMap, Context->Device->AuxiliaryEffectSlotMax);
+
+    //Set globals
+    Context->DistanceModel = DefaultDistanceModel;
+    Context->SourceDistanceModel = AL_FALSE;
+    Context->DopplerFactor = 1.0f;
+    Context->DopplerVelocity = 1.0f;
+    Context->SpeedOfSound = SPEEDOFSOUNDMETRESPERSEC;
+    ATOMIC_INIT(&Context->DeferUpdates, AL_FALSE);
+
+    Context->ExtensionList = alExtList;
+}
+
+
+/* FreeContext
+ *
+ * Cleans up the context, and destroys any remaining objects the app failed to
+ * delete. Called once there's no more references on the context.
+ */
+static void FreeContext(ALCcontext *context)
+{
+    ALlistener *listener = context->Listener;
+    struct ALlistenerProps *lprops;
+    size_t count;
+
+    TRACE("%p\n", context);
+
+    if(context->SourceMap.size > 0)
+    {
+        WARN("(%p) Deleting %d Source%s\n", context, context->SourceMap.size,
+             (context->SourceMap.size==1)?"":"s");
+        ReleaseALSources(context);
+    }
+    ResetUIntMap(&context->SourceMap);
+
+    if(context->EffectSlotMap.size > 0)
+    {
+        WARN("(%p) Deleting %d AuxiliaryEffectSlot%s\n", context, context->EffectSlotMap.size,
+             (context->EffectSlotMap.size==1)?"":"s");
+        ReleaseALAuxiliaryEffectSlots(context);
+    }
+    ResetUIntMap(&context->EffectSlotMap);
+
+    al_free(context->Voices);
+    context->Voices = NULL;
+    context->VoiceCount = 0;
+    context->MaxVoices = 0;
+
+    if((lprops=ATOMIC_LOAD(&listener->Update, almemory_order_acquire)) != NULL)
+    {
+        TRACE("Freed unapplied listener update %p\n", lprops);
+        al_free(lprops);
+    }
+    count = 0;
+    lprops = ATOMIC_LOAD(&listener->FreeList, almemory_order_consume);
+    while(lprops)
+    {
+        struct ALlistenerProps *next = ATOMIC_LOAD(&lprops->next, almemory_order_consume);
+        al_free(lprops);
+        lprops = next;
+        ++count;
+    }
+    TRACE("Freed "SZFMT" listener property object%s\n", count, (count==1)?"":"s");
+
+    ALCdevice_DecRef(context->Device);
+    context->Device = NULL;
+
+    //Invalidate context
+    memset(context, 0, sizeof(ALCcontext));
+    al_free(context);
+}
+
+/* ReleaseContext
+ *
+ * Removes the context reference from the given device and removes it from
+ * being current on the running thread or globally.
+ */
+static void ReleaseContext(ALCcontext *context, ALCdevice *device)
+{
+    ALCcontext *nextctx;
+    ALCcontext *origctx;
+
+    if(altss_get(LocalContext) == context)
+    {
+        WARN("%p released while current on thread\n", context);
+        altss_set(LocalContext, NULL);
+        ALCcontext_DecRef(context);
+    }
+
+    origctx = context;
+    if(ATOMIC_COMPARE_EXCHANGE_STRONG(ALCcontext*, &GlobalContext, &origctx, NULL))
+        ALCcontext_DecRef(context);
+
+    ALCdevice_Lock(device);
+    origctx = context;
+    nextctx = context->next;
+    if(!ATOMIC_COMPARE_EXCHANGE_STRONG(ALCcontext*, &device->ContextList, &origctx, nextctx))
+    {
+        ALCcontext *list;
+        do {
+            list = origctx;
+            origctx = context;
+        } while(!COMPARE_EXCHANGE(&list->next, &origctx, nextctx));
+    }
+    ALCdevice_Unlock(device);
+
+    ALCcontext_DecRef(context);
+}
+
+void ALCcontext_IncRef(ALCcontext *context)
+{
+    uint ref = IncrementRef(&context->ref);
+    TRACEREF("%p increasing refcount to %u\n", context, ref);
+}
+
+void ALCcontext_DecRef(ALCcontext *context)
+{
+    uint ref = DecrementRef(&context->ref);
+    TRACEREF("%p decreasing refcount to %u\n", context, ref);
+    if(ref == 0) FreeContext(context);
+}
+
+static void ReleaseThreadCtx(void *ptr)
+{
+    ALCcontext *context = ptr;
+    uint ref = DecrementRef(&context->ref);
+    TRACEREF("%p decreasing refcount to %u\n", context, ref);
+    ERR("Context %p current for thread being destroyed, possible leak!\n", context);
+}
+
+/* VerifyContext
+ *
+ * Checks that the given context is valid, and increments its reference count.
+ */
+static ALCboolean VerifyContext(ALCcontext **context)
+{
+    ALCdevice *dev;
+
+    LockLists();
+    dev = ATOMIC_LOAD(&DeviceList);
+    while(dev)
+    {
+        ALCcontext *ctx = ATOMIC_LOAD(&dev->ContextList);
+        while(ctx)
+        {
+            if(ctx == *context)
+            {
+                ALCcontext_IncRef(ctx);
+                UnlockLists();
+                return ALC_TRUE;
+            }
+            ctx = ctx->next;
+        }
+        dev = dev->next;
+    }
+    UnlockLists();
+
+    *context = NULL;
+    return ALC_FALSE;
+}
+
+
+/* GetContextRef
+ *
+ * Returns the currently active context for this thread, and adds a reference
+ * without locking it.
+ */
+ALCcontext *GetContextRef(void)
+{
+    ALCcontext *context;
+
+    context = altss_get(LocalContext);
+    if(context)
+        ALCcontext_IncRef(context);
+    else
+    {
+        LockLists();
+        context = ATOMIC_LOAD(&GlobalContext);
+        if(context)
+            ALCcontext_IncRef(context);
+        UnlockLists();
+    }
+
+    return context;
+}
+
+
+/************************************************
+ * Standard ALC functions
+ ************************************************/
+
+/* alcGetError
+ *
+ * Return last ALC generated error code for the given device
+*/
+ALC_API ALCenum ALC_APIENTRY alcGetError(ALCdevice *device)
+{
+    ALCenum errorCode;
+
+    if(VerifyDevice(&device))
+    {
+        errorCode = ATOMIC_EXCHANGE(ALCenum, &device->LastError, ALC_NO_ERROR);
+        ALCdevice_DecRef(device);
+    }
+    else
+        errorCode = ATOMIC_EXCHANGE(ALCenum, &LastNullDeviceError, ALC_NO_ERROR);
+
+    return errorCode;
+}
+
+
+/* alcSuspendContext
+ *
+ * Suspends updates for the given context
+ */
+ALC_API ALCvoid ALC_APIENTRY alcSuspendContext(ALCcontext *context)
+{
+    if(!SuspendDefers)
+        return;
+
+    if(!VerifyContext(&context))
+        alcSetError(NULL, ALC_INVALID_CONTEXT);
+    else
+    {
+        ALCcontext_DeferUpdates(context, DeferAllowPlay);
+        ALCcontext_DecRef(context);
+    }
+}
+
+/* alcProcessContext
+ *
+ * Resumes processing updates for the given context
+ */
+ALC_API ALCvoid ALC_APIENTRY alcProcessContext(ALCcontext *context)
+{
+    if(!SuspendDefers)
+        return;
+
+    if(!VerifyContext(&context))
+        alcSetError(NULL, ALC_INVALID_CONTEXT);
+    else
+    {
+        ALCcontext_ProcessUpdates(context);
+        ALCcontext_DecRef(context);
+    }
+}
+
+
+/* alcGetString
+ *
+ * Returns information about the device, and error strings
+ */
+ALC_API const ALCchar* ALC_APIENTRY alcGetString(ALCdevice *Device, ALCenum param)
+{
+    const ALCchar *value = NULL;
+
+    switch(param)
+    {
+    case ALC_NO_ERROR:
+        value = alcNoError;
+        break;
+
+    case ALC_INVALID_ENUM:
+        value = alcErrInvalidEnum;
+        break;
+
+    case ALC_INVALID_VALUE:
+        value = alcErrInvalidValue;
+        break;
+
+    case ALC_INVALID_DEVICE:
+        value = alcErrInvalidDevice;
+        break;
+
+    case ALC_INVALID_CONTEXT:
+        value = alcErrInvalidContext;
+        break;
+
+    case ALC_OUT_OF_MEMORY:
+        value = alcErrOutOfMemory;
+        break;
+
+    case ALC_DEVICE_SPECIFIER:
+        value = alcDefaultName;
+        break;
+
+    case ALC_ALL_DEVICES_SPECIFIER:
+        if(VerifyDevice(&Device))
+        {
+            value = al_string_get_cstr(Device->DeviceName);
+            ALCdevice_DecRef(Device);
+        }
+        else
+        {
+            ProbeAllDevicesList();
+            value = al_string_get_cstr(alcAllDevicesList);
+        }
+        break;
+
+    case ALC_CAPTURE_DEVICE_SPECIFIER:
+        if(VerifyDevice(&Device))
+        {
+            value = al_string_get_cstr(Device->DeviceName);
+            ALCdevice_DecRef(Device);
+        }
+        else
+        {
+            ProbeCaptureDeviceList();
+            value = al_string_get_cstr(alcCaptureDeviceList);
+        }
+        break;
+
+    /* Default devices are always first in the list */
+    case ALC_DEFAULT_DEVICE_SPECIFIER:
+        value = alcDefaultName;
+        break;
+
+    case ALC_DEFAULT_ALL_DEVICES_SPECIFIER:
+        if(al_string_empty(alcAllDevicesList))
+            ProbeAllDevicesList();
+
+        VerifyDevice(&Device);
+
+        free(alcDefaultAllDevicesSpecifier);
+        alcDefaultAllDevicesSpecifier = strdup(al_string_get_cstr(alcAllDevicesList));
+        if(!alcDefaultAllDevicesSpecifier)
+            alcSetError(Device, ALC_OUT_OF_MEMORY);
+
+        value = alcDefaultAllDevicesSpecifier;
+        if(Device) ALCdevice_DecRef(Device);
+        break;
+
+    case ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER:
+        if(al_string_empty(alcCaptureDeviceList))
+            ProbeCaptureDeviceList();
+
+        VerifyDevice(&Device);
+
+        free(alcCaptureDefaultDeviceSpecifier);
+        alcCaptureDefaultDeviceSpecifier = strdup(al_string_get_cstr(alcCaptureDeviceList));
+        if(!alcCaptureDefaultDeviceSpecifier)
+            alcSetError(Device, ALC_OUT_OF_MEMORY);
+
+        value = alcCaptureDefaultDeviceSpecifier;
+        if(Device) ALCdevice_DecRef(Device);
+        break;
+
+    case ALC_EXTENSIONS:
+        if(!VerifyDevice(&Device))
+            value = alcNoDeviceExtList;
+        else
+        {
+            value = alcExtensionList;
+            ALCdevice_DecRef(Device);
+        }
+        break;
+
+    case ALC_HRTF_SPECIFIER_SOFT:
+        if(!VerifyDevice(&Device))
+            alcSetError(NULL, ALC_INVALID_DEVICE);
+        else
+        {
+            almtx_lock(&Device->BackendLock);
+            value = (Device->Hrtf.Handle ? al_string_get_cstr(Device->Hrtf.Name) : "");
+            almtx_unlock(&Device->BackendLock);
+            ALCdevice_DecRef(Device);
+        }
+        break;
+
+    default:
+        VerifyDevice(&Device);
+        alcSetError(Device, ALC_INVALID_ENUM);
+        if(Device) ALCdevice_DecRef(Device);
+        break;
+    }
+
+    return value;
+}
+
+
+static ALCsizei GetIntegerv(ALCdevice *device, ALCenum param, ALCsizei size, ALCint *values)
+{
+    ALCsizei i;
+
+    if(size <= 0 || values == NULL)
+    {
+        alcSetError(device, ALC_INVALID_VALUE);
+        return 0;
+    }
+
+    if(!device)
+    {
+        switch(param)
+        {
+            case ALC_MAJOR_VERSION:
+                values[0] = alcMajorVersion;
+                return 1;
+            case ALC_MINOR_VERSION:
+                values[0] = alcMinorVersion;
+                return 1;
+
+            case ALC_ATTRIBUTES_SIZE:
+            case ALC_ALL_ATTRIBUTES:
+            case ALC_FREQUENCY:
+            case ALC_REFRESH:
+            case ALC_SYNC:
+            case ALC_MONO_SOURCES:
+            case ALC_STEREO_SOURCES:
+            case ALC_CAPTURE_SAMPLES:
+            case ALC_FORMAT_CHANNELS_SOFT:
+            case ALC_FORMAT_TYPE_SOFT:
+                alcSetError(NULL, ALC_INVALID_DEVICE);
+                return 0;
+
+            default:
+                alcSetError(NULL, ALC_INVALID_ENUM);
+                return 0;
+        }
+        return 0;
+    }
+
+    if(device->Type == Capture)
+    {
+        switch(param)
+        {
+            case ALC_CAPTURE_SAMPLES:
+                almtx_lock(&device->BackendLock);
+                values[0] = V0(device->Backend,availableSamples)();
+                almtx_unlock(&device->BackendLock);
+                return 1;
+
+            case ALC_CONNECTED:
+                values[0] = device->Connected;
+                return 1;
+
+            default:
+                alcSetError(device, ALC_INVALID_ENUM);
+                return 0;
+        }
+        return 0;
+    }
+
+    /* render device */
+    switch(param)
+    {
+        case ALC_MAJOR_VERSION:
+            values[0] = alcMajorVersion;
+            return 1;
+
+        case ALC_MINOR_VERSION:
+            values[0] = alcMinorVersion;
+            return 1;
+
+        case ALC_EFX_MAJOR_VERSION:
+            values[0] = alcEFXMajorVersion;
+            return 1;
+
+        case ALC_EFX_MINOR_VERSION:
+            values[0] = alcEFXMinorVersion;
+            return 1;
+
+        case ALC_ATTRIBUTES_SIZE:
+            values[0] = 17;
+            return 1;
+
+        case ALC_ALL_ATTRIBUTES:
+            if(size < 17)
+            {
+                alcSetError(device, ALC_INVALID_VALUE);
+                return 0;
+            }
+
+            i = 0;
+            almtx_lock(&device->BackendLock);
+            values[i++] = ALC_FREQUENCY;
+            values[i++] = device->Frequency;
+
+            if(device->Type != Loopback)
+            {
+                values[i++] = ALC_REFRESH;
+                values[i++] = device->Frequency / device->UpdateSize;
+
+                values[i++] = ALC_SYNC;
+                values[i++] = ALC_FALSE;
+            }
+            else
+            {
+                values[i++] = ALC_FORMAT_CHANNELS_SOFT;
+                values[i++] = device->FmtChans;
+
+                values[i++] = ALC_FORMAT_TYPE_SOFT;
+                values[i++] = device->FmtType;
+            }
+
+            values[i++] = ALC_MONO_SOURCES;
+            values[i++] = device->NumMonoSources;
+
+            values[i++] = ALC_STEREO_SOURCES;
+            values[i++] = device->NumStereoSources;
+
+            values[i++] = ALC_MAX_AUXILIARY_SENDS;
+            values[i++] = device->NumAuxSends;
+
+            values[i++] = ALC_HRTF_SOFT;
+            values[i++] = (device->Hrtf.Handle ? ALC_TRUE : ALC_FALSE);
+
+            values[i++] = ALC_HRTF_STATUS_SOFT;
+            values[i++] = device->Hrtf.Status;
+            almtx_unlock(&device->BackendLock);
+
+            values[i++] = 0;
+            return i;
+
+        case ALC_FREQUENCY:
+            values[0] = device->Frequency;
+            return 1;
+
+        case ALC_REFRESH:
+            if(device->Type == Loopback)
+            {
+                alcSetError(device, ALC_INVALID_DEVICE);
+                return 0;
+            }
+            almtx_lock(&device->BackendLock);
+            values[0] = device->Frequency / device->UpdateSize;
+            almtx_unlock(&device->BackendLock);
+            return 1;
+
+        case ALC_SYNC:
+            if(device->Type == Loopback)
+            {
+                alcSetError(device, ALC_INVALID_DEVICE);
+                return 0;
+            }
+            values[0] = ALC_FALSE;
+            return 1;
+
+        case ALC_FORMAT_CHANNELS_SOFT:
+            if(device->Type != Loopback)
+            {
+                alcSetError(device, ALC_INVALID_DEVICE);
+                return 0;
+            }
+            values[0] = device->FmtChans;
+            return 1;
+
+        case ALC_FORMAT_TYPE_SOFT:
+            if(device->Type != Loopback)
+            {
+                alcSetError(device, ALC_INVALID_DEVICE);
+                return 0;
+            }
+            values[0] = device->FmtType;
+            return 1;
+
+        case ALC_MONO_SOURCES:
+            values[0] = device->NumMonoSources;
+            return 1;
+
+        case ALC_STEREO_SOURCES:
+            values[0] = device->NumStereoSources;
+            return 1;
+
+        case ALC_MAX_AUXILIARY_SENDS:
+            values[0] = device->NumAuxSends;
+            return 1;
+
+        case ALC_CONNECTED:
+            values[0] = device->Connected;
+            return 1;
+
+        case ALC_HRTF_SOFT:
+            values[0] = (device->Hrtf.Handle ? ALC_TRUE : ALC_FALSE);
+            return 1;
+
+        case ALC_HRTF_STATUS_SOFT:
+            values[0] = device->Hrtf.Status;
+            return 1;
+
+        case ALC_NUM_HRTF_SPECIFIERS_SOFT:
+            almtx_lock(&device->BackendLock);
+            FreeHrtfList(&device->Hrtf.List);
+            device->Hrtf.List = EnumerateHrtf(device->DeviceName);
+            values[0] = (ALCint)VECTOR_SIZE(device->Hrtf.List);
+            almtx_unlock(&device->BackendLock);
+            return 1;
+
+        default:
+            alcSetError(device, ALC_INVALID_ENUM);
+            return 0;
+    }
+    return 0;
+}
+
+/* alcGetIntegerv
+ *
+ * Returns information about the device and the version of OpenAL
+ */
+ALC_API void ALC_APIENTRY alcGetIntegerv(ALCdevice *device, ALCenum param, ALCsizei size, ALCint *values)
+{
+    VerifyDevice(&device);
+    if(size <= 0 || values == NULL)
+        alcSetError(device, ALC_INVALID_VALUE);
+    else
+        GetIntegerv(device, param, size, values);
+    if(device) ALCdevice_DecRef(device);
+}
+
+ALC_API void ALC_APIENTRY alcGetInteger64vSOFT(ALCdevice *device, ALCenum pname, ALCsizei size, ALCint64SOFT *values)
+{
+    ALCint *ivals;
+    ALsizei i;
+
+    VerifyDevice(&device);
+    if(size <= 0 || values == NULL)
+        alcSetError(device, ALC_INVALID_VALUE);
+    else if(!device || device->Type == Capture)
+    {
+        ivals = malloc(size * sizeof(ALCint));
+        size = GetIntegerv(device, pname, size, ivals);
+        for(i = 0;i < size;i++)
+            values[i] = ivals[i];
+        free(ivals);
+    }
+    else /* render device */
+    {
+        ClockLatency clock;
+        ALuint64 basecount;
+        ALuint samplecount;
+        ALuint refcount;
+
+        switch(pname)
+        {
+            case ALC_ATTRIBUTES_SIZE:
+                *values = 21;
+                break;
+
+            case ALC_ALL_ATTRIBUTES:
+                if(size < 21)
+                    alcSetError(device, ALC_INVALID_VALUE);
+                else
+                {
+                    i = 0;
+                    almtx_lock(&device->BackendLock);
+                    values[i++] = ALC_FREQUENCY;
+                    values[i++] = device->Frequency;
+
+                    if(device->Type != Loopback)
+                    {
+                        values[i++] = ALC_REFRESH;
+                        values[i++] = device->Frequency / device->UpdateSize;
+
+                        values[i++] = ALC_SYNC;
+                        values[i++] = ALC_FALSE;
+                    }
+                    else
+                    {
+                        values[i++] = ALC_FORMAT_CHANNELS_SOFT;
+                        values[i++] = device->FmtChans;
+
+                        values[i++] = ALC_FORMAT_TYPE_SOFT;
+                        values[i++] = device->FmtType;
+                    }
+
+                    values[i++] = ALC_MONO_SOURCES;
+                    values[i++] = device->NumMonoSources;
+
+                    values[i++] = ALC_STEREO_SOURCES;
+                    values[i++] = device->NumStereoSources;
+
+                    values[i++] = ALC_MAX_AUXILIARY_SENDS;
+                    values[i++] = device->NumAuxSends;
+
+                    values[i++] = ALC_HRTF_SOFT;
+                    values[i++] = (device->Hrtf.Handle ? ALC_TRUE : ALC_FALSE);
+
+                    values[i++] = ALC_HRTF_STATUS_SOFT;
+                    values[i++] = device->Hrtf.Status;
+
+                    clock = V0(device->Backend,getClockLatency)();
+                    values[i++] = ALC_DEVICE_CLOCK_SOFT;
+                    values[i++] = clock.ClockTime;
+
+                    values[i++] = ALC_DEVICE_LATENCY_SOFT;
+                    values[i++] = clock.Latency;
+                    almtx_unlock(&device->BackendLock);
+
+                    values[i++] = 0;
+                }
+                break;
+
+            case ALC_DEVICE_CLOCK_SOFT:
+                almtx_lock(&device->BackendLock);
+                do {
+                    while(((refcount=ReadRef(&device->MixCount))&1) != 0)
+                        althrd_yield();
+                    basecount = device->ClockBase;
+                    samplecount = device->SamplesDone;
+                } while(refcount != ReadRef(&device->MixCount));
+                *values = basecount + (samplecount*DEVICE_CLOCK_RES/device->Frequency);
+                almtx_unlock(&device->BackendLock);
+                break;
+
+            case ALC_DEVICE_LATENCY_SOFT:
+                {
+                    almtx_lock(&device->BackendLock);
+                    clock = V0(device->Backend,getClockLatency)();
+                    almtx_unlock(&device->BackendLock);
+                    *values = clock.Latency;
+                }
+                break;
+
+            case ALC_DEVICE_CLOCK_LATENCY_SOFT:
+                if(size < 2)
+                    alcSetError(device, ALC_INVALID_VALUE);
+                else
+                {
+                    ClockLatency clock;
+                    almtx_lock(&device->BackendLock);
+                    clock = V0(device->Backend,getClockLatency)();
+                    almtx_unlock(&device->BackendLock);
+                    values[0] = clock.ClockTime;
+                    values[1] = clock.Latency;
+                }
+                break;
+
+            default:
+                ivals = malloc(size * sizeof(ALCint));
+                size = GetIntegerv(device, pname, size, ivals);
+                for(i = 0;i < size;i++)
+                    values[i] = ivals[i];
+                free(ivals);
+                break;
+        }
+    }
+    if(device)
+        ALCdevice_DecRef(device);
+}
+
+
+/* alcIsExtensionPresent
+ *
+ * Determines if there is support for a particular extension
+ */
+ALC_API ALCboolean ALC_APIENTRY alcIsExtensionPresent(ALCdevice *device, const ALCchar *extName)
+{
+    ALCboolean bResult = ALC_FALSE;
+
+    VerifyDevice(&device);
+
+    if(!extName)
+        alcSetError(device, ALC_INVALID_VALUE);
+    else
+    {
+        size_t len = strlen(extName);
+        const char *ptr = (device ? alcExtensionList : alcNoDeviceExtList);
+        while(ptr && *ptr)
+        {
+            if(strncasecmp(ptr, extName, len) == 0 &&
+               (ptr[len] == '\0' || isspace(ptr[len])))
+            {
+                bResult = ALC_TRUE;
+                break;
+            }
+            if((ptr=strchr(ptr, ' ')) != NULL)
+            {
+                do {
+                    ++ptr;
+                } while(isspace(*ptr));
+            }
+        }
+    }
+    if(device)
+        ALCdevice_DecRef(device);
+    return bResult;
+}
+
+
+/* alcGetProcAddress
+ *
+ * Retrieves the function address for a particular extension function
+ */
+ALC_API ALCvoid* ALC_APIENTRY alcGetProcAddress(ALCdevice *device, const ALCchar *funcName)
+{
+    ALCvoid *ptr = NULL;
+
+    if(!funcName)
+    {
+        VerifyDevice(&device);
+        alcSetError(device, ALC_INVALID_VALUE);
+        if(device) ALCdevice_DecRef(device);
+    }
+    else
+    {
+        ALsizei i = 0;
+        while(alcFunctions[i].funcName && strcmp(alcFunctions[i].funcName, funcName) != 0)
+            i++;
+        ptr = alcFunctions[i].address;
+    }
+
+    return ptr;
+}
+
+
+/* alcGetEnumValue
+ *
+ * Get the value for a particular ALC enumeration name
+ */
+ALC_API ALCenum ALC_APIENTRY alcGetEnumValue(ALCdevice *device, const ALCchar *enumName)
+{
+    ALCenum val = 0;
+
+    if(!enumName)
+    {
+        VerifyDevice(&device);
+        alcSetError(device, ALC_INVALID_VALUE);
+        if(device) ALCdevice_DecRef(device);
+    }
+    else
+    {
+        ALsizei i = 0;
+        while(enumeration[i].enumName && strcmp(enumeration[i].enumName, enumName) != 0)
+            i++;
+        val = enumeration[i].value;
+    }
+
+    return val;
+}
+
+
+/* alcCreateContext
+ *
+ * Create and attach a context to the given device.
+ */
+ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCint *attrList)
+{
+    ALCcontext *ALContext;
+    ALfloat valf;
+    ALCenum err;
+
+    LockLists();
+    if(!VerifyDevice(&device) || device->Type == Capture || !device->Connected)
+    {
+        UnlockLists();
+        alcSetError(device, ALC_INVALID_DEVICE);
+        if(device) ALCdevice_DecRef(device);
+        return NULL;
+    }
+    almtx_lock(&device->BackendLock);
+    UnlockLists();
+
+    ATOMIC_STORE(&device->LastError, ALC_NO_ERROR);
+
+    ALContext = al_calloc(16, sizeof(ALCcontext)+sizeof(ALlistener));
+    if(ALContext)
+    {
+        InitRef(&ALContext->ref, 1);
+        ALContext->Listener = (ALlistener*)ALContext->_listener_mem;
+
+        ATOMIC_INIT(&ALContext->ActiveAuxSlotList, NULL);
+
+        ALContext->VoiceCount = 0;
+        ALContext->MaxVoices = 256;
+        ALContext->Voices = al_calloc(16, ALContext->MaxVoices * sizeof(ALContext->Voices[0]));
+    }
+    if(!ALContext || !ALContext->Voices)
+    {
+        almtx_unlock(&device->BackendLock);
+
+        if(ALContext)
+        {
+            al_free(ALContext->Voices);
+            ALContext->Voices = NULL;
+
+            al_free(ALContext);
+            ALContext = NULL;
+        }
+
+        alcSetError(device, ALC_OUT_OF_MEMORY);
+        ALCdevice_DecRef(device);
+        return NULL;
+    }
+
+    if((err=UpdateDeviceParams(device, attrList)) != ALC_NO_ERROR)
+    {
+        almtx_unlock(&device->BackendLock);
+
+        al_free(ALContext->Voices);
+        ALContext->Voices = NULL;
+
+        al_free(ALContext);
+        ALContext = NULL;
+
+        alcSetError(device, err);
+        if(err == ALC_INVALID_DEVICE)
+        {
+            V0(device->Backend,lock)();
+            aluHandleDisconnect(device);
+            V0(device->Backend,unlock)();
+        }
+        ALCdevice_DecRef(device);
+        return NULL;
+    }
+
+    ALContext->Device = device;
+    ALCdevice_IncRef(device);
+    InitContext(ALContext);
+
+    if(ConfigValueFloat(al_string_get_cstr(device->DeviceName), NULL, "volume-adjust", &valf))
+    {
+        if(!isfinite(valf))
+            ERR("volume-adjust must be finite: %f\n", valf);
+        else
+        {
+            ALfloat db = clampf(valf, -24.0f, 24.0f);
+            if(db != valf)
+                WARN("volume-adjust clamped: %f, range: +/-%f\n", valf, 24.0f);
+            ALContext->GainBoost = powf(10.0f, db/20.0f);
+            TRACE("volume-adjust gain: %f\n", ALContext->GainBoost);
+        }
+    }
+    UpdateListenerProps(ALContext);
+
+    {
+        ALCcontext *head = ATOMIC_LOAD(&device->ContextList);
+        do {
+            ALContext->next = head;
+        } while(!ATOMIC_COMPARE_EXCHANGE_WEAK(ALCcontext*, &device->ContextList, &head, ALContext));
+    }
+    almtx_unlock(&device->BackendLock);
+
+    ALCdevice_DecRef(device);
+
+    TRACE("Created context %p\n", ALContext);
+    return ALContext;
+}
+
+/* alcDestroyContext
+ *
+ * Remove a context from its device
+ */
+ALC_API ALCvoid ALC_APIENTRY alcDestroyContext(ALCcontext *context)
+{
+    ALCdevice *Device;
+
+    LockLists();
+    /* alcGetContextsDevice sets an error for invalid contexts */
+    Device = alcGetContextsDevice(context);
+    if(Device)
+    {
+        almtx_lock(&Device->BackendLock);
+        ReleaseContext(context, Device);
+        if(!ATOMIC_LOAD(&Device->ContextList))
+        {
+            V0(Device->Backend,stop)();
+            Device->Flags &= ~DEVICE_RUNNING;
+        }
+        almtx_unlock(&Device->BackendLock);
+    }
+    UnlockLists();
+}
+
+
+/* alcGetCurrentContext
+ *
+ * Returns the currently active context on the calling thread
+ */
+ALC_API ALCcontext* ALC_APIENTRY alcGetCurrentContext(void)
+{
+    ALCcontext *Context = altss_get(LocalContext);
+    if(!Context) Context = ATOMIC_LOAD(&GlobalContext);
+    return Context;
+}
+
+/* alcGetThreadContext
+ *
+ * Returns the currently active thread-local context
+ */
+ALC_API ALCcontext* ALC_APIENTRY alcGetThreadContext(void)
+{
+    return altss_get(LocalContext);
+}
+
+
+/* alcMakeContextCurrent
+ *
+ * Makes the given context the active process-wide context, and removes the
+ * thread-local context for the calling thread.
+ */
+ALC_API ALCboolean ALC_APIENTRY alcMakeContextCurrent(ALCcontext *context)
+{
+    /* context must be valid or NULL */
+    if(context && !VerifyContext(&context))
+    {
+        alcSetError(NULL, ALC_INVALID_CONTEXT);
+        return ALC_FALSE;
+    }
+    /* context's reference count is already incremented */
+    context = ATOMIC_EXCHANGE(ALCcontext*, &GlobalContext, context);
+    if(context) ALCcontext_DecRef(context);
+
+    if((context=altss_get(LocalContext)) != NULL)
+    {
+        altss_set(LocalContext, NULL);
+        ALCcontext_DecRef(context);
+    }
+
+    return ALC_TRUE;
+}
+
+/* alcSetThreadContext
+ *
+ * Makes the given context the active context for the current thread
+ */
+ALC_API ALCboolean ALC_APIENTRY alcSetThreadContext(ALCcontext *context)
+{
+    ALCcontext *old;
+
+    /* context must be valid or NULL */
+    if(context && !VerifyContext(&context))
+    {
+        alcSetError(NULL, ALC_INVALID_CONTEXT);
+        return ALC_FALSE;
+    }
+    /* context's reference count is already incremented */
+    old = altss_get(LocalContext);
+    altss_set(LocalContext, context);
+    if(old) ALCcontext_DecRef(old);
+
+    return ALC_TRUE;
+}
+
+
+/* alcGetContextsDevice
+ *
+ * Returns the device that a particular context is attached to
+ */
+ALC_API ALCdevice* ALC_APIENTRY alcGetContextsDevice(ALCcontext *Context)
+{
+    ALCdevice *Device;
+
+    if(!VerifyContext(&Context))
+    {
+        alcSetError(NULL, ALC_INVALID_CONTEXT);
+        return NULL;
+    }
+    Device = Context->Device;
+    ALCcontext_DecRef(Context);
+
+    return Device;
+}
+
+
+/* alcOpenDevice
+ *
+ * Opens the named device.
+ */
+ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *deviceName)
+{
+    const ALCchar *fmt;
+    ALCdevice *device;
+    ALCenum err;
+
+    DO_INITCONFIG();
+
+    if(!PlaybackBackend.name)
+    {
+        alcSetError(NULL, ALC_INVALID_VALUE);
+        return NULL;
+    }
+
+    if(deviceName && (!deviceName[0] || strcasecmp(deviceName, alcDefaultName) == 0 || strcasecmp(deviceName, "openal-soft") == 0
+#ifdef _WIN32
+        /* Some old Windows apps hardcode these expecting OpenAL to use a
+         * specific audio API, even when they're not enumerated. Creative's
+         * router effectively ignores them too.
+         */
+        || strcasecmp(deviceName, "DirectSound3D") == 0 || strcasecmp(deviceName, "DirectSound") == 0
+        || strcasecmp(deviceName, "MMSYSTEM") == 0
+#endif
+    ))
+        deviceName = NULL;
+
+    device = al_calloc(16, sizeof(ALCdevice)+sizeof(ALeffectslot));
+    if(!device)
+    {
+        alcSetError(NULL, ALC_OUT_OF_MEMORY);
+        return NULL;
+    }
+
+    //Validate device
+    InitRef(&device->ref, 1);
+    device->Connected = ALC_TRUE;
+    device->Type = Playback;
+    ATOMIC_INIT(&device->LastError, ALC_NO_ERROR);
+
+    device->Flags = 0;
+    device->Bs2b = NULL;
+    device->Uhj_Encoder = NULL;
+    VECTOR_INIT(device->Hrtf.List);
+    AL_STRING_INIT(device->Hrtf.Name);
+    device->Render_Mode = NormalRender;
+    AL_STRING_INIT(device->DeviceName);
+    device->Dry.Buffer = NULL;
+    device->Dry.NumChannels = 0;
+    device->FOAOut.Buffer = NULL;
+    device->FOAOut.NumChannels = 0;
+    device->RealOut.Buffer = NULL;
+    device->RealOut.NumChannels = 0;
+
+    ATOMIC_INIT(&device->ContextList, NULL);
+
+    device->ClockBase = 0;
+    device->SamplesDone = 0;
+
+    device->SourcesMax = 256;
+    device->AuxiliaryEffectSlotMax = 4;
+    device->NumAuxSends = MAX_SENDS;
+
+    InitUIntMap(&device->BufferMap, ~0);
+    InitUIntMap(&device->EffectMap, ~0);
+    InitUIntMap(&device->FilterMap, ~0);
+
+    //Set output format
+    device->FmtChans = DevFmtChannelsDefault;
+    device->FmtType = DevFmtTypeDefault;
+    device->Frequency = DEFAULT_OUTPUT_RATE;
+    device->IsHeadphones = AL_FALSE;
+    device->AmbiFmt = AmbiFormat_Default;
+    device->NumUpdates = 4;
+    device->UpdateSize = 1024;
+
+    if(!PlaybackBackend.getFactory)
+        device->Backend = create_backend_wrapper(device, &PlaybackBackend.Funcs,
+                                                 ALCbackend_Playback);
+    else
+    {
+        ALCbackendFactory *factory = PlaybackBackend.getFactory();
+        device->Backend = V(factory,createBackend)(device, ALCbackend_Playback);
+    }
+    if(!device->Backend)
+    {
+        al_free(device);
+        alcSetError(NULL, ALC_OUT_OF_MEMORY);
+        return NULL;
+    }
+
+
+    if(ConfigValueStr(deviceName, NULL, "channels", &fmt))
+    {
+        static const struct {
+            const char name[16];
+            enum DevFmtChannels chans;
+        } chanlist[] = {
+            { "mono",       DevFmtMono   },
+            { "stereo",     DevFmtStereo },
+            { "quad",       DevFmtQuad   },
+            { "surround51", DevFmtX51    },
+            { "surround61", DevFmtX61    },
+            { "surround71", DevFmtX71    },
+            { "surround51rear", DevFmtX51Rear },
+            { "ambi1", DevFmtAmbi1 },
+            { "ambi2", DevFmtAmbi2 },
+            { "ambi3", DevFmtAmbi3 },
+        };
+        size_t i;
+
+        for(i = 0;i < COUNTOF(chanlist);i++)
+        {
+            if(strcasecmp(chanlist[i].name, fmt) == 0)
+            {
+                device->FmtChans = chanlist[i].chans;
+                device->Flags |= DEVICE_CHANNELS_REQUEST;
+                break;
+            }
+        }
+        if(i == COUNTOF(chanlist))
+            ERR("Unsupported channels: %s\n", fmt);
+    }
+    if(ConfigValueStr(deviceName, NULL, "sample-type", &fmt))
+    {
+        static const struct {
+            const char name[16];
+            enum DevFmtType type;
+        } typelist[] = {
+            { "int8",    DevFmtByte   },
+            { "uint8",   DevFmtUByte  },
+            { "int16",   DevFmtShort  },
+            { "uint16",  DevFmtUShort },
+            { "int32",   DevFmtInt    },
+            { "uint32",  DevFmtUInt   },
+            { "float32", DevFmtFloat  },
+        };
+        size_t i;
+
+        for(i = 0;i < COUNTOF(typelist);i++)
+        {
+            if(strcasecmp(typelist[i].name, fmt) == 0)
+            {
+                device->FmtType = typelist[i].type;
+                device->Flags |= DEVICE_SAMPLE_TYPE_REQUEST;
+                break;
+            }
+        }
+        if(i == COUNTOF(typelist))
+            ERR("Unsupported sample-type: %s\n", fmt);
+    }
+
+    if(ConfigValueUInt(deviceName, NULL, "frequency", &device->Frequency))
+    {
+        device->Flags |= DEVICE_FREQUENCY_REQUEST;
+        if(device->Frequency < MIN_OUTPUT_RATE)
+            ERR("%uhz request clamped to %uhz minimum\n", device->Frequency, MIN_OUTPUT_RATE);
+        device->Frequency = maxu(device->Frequency, MIN_OUTPUT_RATE);
+    }
+
+    ConfigValueUInt(deviceName, NULL, "periods", &device->NumUpdates);
+    device->NumUpdates = clampu(device->NumUpdates, 2, 16);
+
+    ConfigValueUInt(deviceName, NULL, "period_size", &device->UpdateSize);
+    device->UpdateSize = clampu(device->UpdateSize, 64, 8192);
+    if((CPUCapFlags&(CPU_CAP_SSE|CPU_CAP_NEON)) != 0)
+        device->UpdateSize = (device->UpdateSize+3)&~3;
+
+    ConfigValueUInt(deviceName, NULL, "sources", &device->SourcesMax);
+    if(device->SourcesMax == 0) device->SourcesMax = 256;
+
+    ConfigValueUInt(deviceName, NULL, "slots", &device->AuxiliaryEffectSlotMax);
+    if(device->AuxiliaryEffectSlotMax == 0) device->AuxiliaryEffectSlotMax = 4;
+
+    ConfigValueUInt(deviceName, NULL, "sends", &device->NumAuxSends);
+    if(device->NumAuxSends > MAX_SENDS) device->NumAuxSends = MAX_SENDS;
+
+    device->NumStereoSources = 1;
+    device->NumMonoSources = device->SourcesMax - device->NumStereoSources;
+
+    // Find a playback device to open
+    if((err=V(device->Backend,open)(deviceName)) != ALC_NO_ERROR)
+    {
+        DELETE_OBJ(device->Backend);
+        al_free(device);
+        alcSetError(NULL, err);
+        return NULL;
+    }
+    almtx_init(&device->BackendLock, almtx_plain);
+
+    if(ConfigValueStr(al_string_get_cstr(device->DeviceName), NULL, "ambi-format", &fmt))
+    {
+        if(strcasecmp(fmt, "fuma") == 0)
+            device->AmbiFmt = AmbiFormat_FuMa;
+        else if(strcasecmp(fmt, "acn+sn3d") == 0)
+            device->AmbiFmt = AmbiFormat_ACN_SN3D;
+        else if(strcasecmp(fmt, "acn+n3d") == 0)
+            device->AmbiFmt = AmbiFormat_ACN_N3D;
+        else
+            ERR("Unsupported ambi-format: %s\n", fmt);
+    }
+
+    if(DefaultEffect.type != AL_EFFECT_NULL)
+    {
+        device->DefaultSlot = (ALeffectslot*)device->_slot_mem;
+        if(InitEffectSlot(device->DefaultSlot) != AL_NO_ERROR)
+        {
+            device->DefaultSlot = NULL;
+            ERR("Failed to initialize the default effect slot\n");
+        }
+        else
+        {
+            aluInitEffectPanning(device->DefaultSlot);
+            if(InitializeEffect(device, device->DefaultSlot, &DefaultEffect) != AL_NO_ERROR)
+            {
+                DeinitEffectSlot(device->DefaultSlot);
+                device->DefaultSlot = NULL;
+                ERR("Failed to initialize the default effect\n");
+            }
+        }
+    }
+
+    {
+        ALCdevice *head = ATOMIC_LOAD(&DeviceList);
+        do {
+            device->next = head;
+        } while(!ATOMIC_COMPARE_EXCHANGE_WEAK(ALCdevice*, &DeviceList, &head, device));
+    }
+
+    TRACE("Created device %p, \"%s\"\n", device, al_string_get_cstr(device->DeviceName));
+    return device;
+}
+
+/* alcCloseDevice
+ *
+ * Closes the given device.
+ */
+ALC_API ALCboolean ALC_APIENTRY alcCloseDevice(ALCdevice *device)
+{
+    ALCdevice *list, *origdev, *nextdev;
+    ALCcontext *ctx;
+
+    LockLists();
+    list = ATOMIC_LOAD(&DeviceList);
+    do {
+        if(list == device)
+            break;
+    } while((list=list->next) != NULL);
+    if(!list || list->Type == Capture)
+    {
+        alcSetError(list, ALC_INVALID_DEVICE);
+        UnlockLists();
+        return ALC_FALSE;
+    }
+    almtx_lock(&device->BackendLock);
+
+    origdev = device;
+    nextdev = device->next;
+    if(!ATOMIC_COMPARE_EXCHANGE_STRONG(ALCdevice*, &DeviceList, &origdev, nextdev))
+    {
+        do {
+            list = origdev;
+            origdev = device;
+        } while(!COMPARE_EXCHANGE(&list->next, &origdev, nextdev));
+    }
+    UnlockLists();
+
+    ctx = ATOMIC_LOAD(&device->ContextList);
+    while(ctx != NULL)
+    {
+        ALCcontext *next = ctx->next;
+        WARN("Releasing context %p\n", ctx);
+        ReleaseContext(ctx, device);
+        ctx = next;
+    }
+    if((device->Flags&DEVICE_RUNNING))
+        V0(device->Backend,stop)();
+    device->Flags &= ~DEVICE_RUNNING;
+    almtx_unlock(&device->BackendLock);
+
+    ALCdevice_DecRef(device);
+
+    return ALC_TRUE;
+}
+
+
+/************************************************
+ * ALC capture functions
+ ************************************************/
+ALC_API ALCdevice* ALC_APIENTRY alcCaptureOpenDevice(const ALCchar *deviceName, ALCuint frequency, ALCenum format, ALCsizei samples)
+{
+    ALCdevice *device = NULL;
+    ALCenum err;
+
+    DO_INITCONFIG();
+
+    if(!CaptureBackend.name)
+    {
+        alcSetError(NULL, ALC_INVALID_VALUE);
+        return NULL;
+    }
+
+    if(samples <= 0)
+    {
+        alcSetError(NULL, ALC_INVALID_VALUE);
+        return NULL;
+    }
+
+    if(deviceName && (!deviceName[0] || strcasecmp(deviceName, alcDefaultName) == 0 || strcasecmp(deviceName, "openal-soft") == 0))
+        deviceName = NULL;
+
+    device = al_calloc(16, sizeof(ALCdevice));
+    if(!device)
+    {
+        alcSetError(NULL, ALC_OUT_OF_MEMORY);
+        return NULL;
+    }
+
+    //Validate device
+    InitRef(&device->ref, 1);
+    device->Connected = ALC_TRUE;
+    device->Type = Capture;
+
+    VECTOR_INIT(device->Hrtf.List);
+    AL_STRING_INIT(device->Hrtf.Name);
+
+    AL_STRING_INIT(device->DeviceName);
+    device->Dry.Buffer = NULL;
+    device->Dry.NumChannels = 0;
+    device->FOAOut.Buffer = NULL;
+    device->FOAOut.NumChannels = 0;
+    device->RealOut.Buffer = NULL;
+    device->RealOut.NumChannels = 0;
+
+    InitUIntMap(&device->BufferMap, ~0);
+    InitUIntMap(&device->EffectMap, ~0);
+    InitUIntMap(&device->FilterMap, ~0);
+
+    if(!CaptureBackend.getFactory)
+        device->Backend = create_backend_wrapper(device, &CaptureBackend.Funcs,
+                                                 ALCbackend_Capture);
+    else
+    {
+        ALCbackendFactory *factory = CaptureBackend.getFactory();
+        device->Backend = V(factory,createBackend)(device, ALCbackend_Capture);
+    }
+    if(!device->Backend)
+    {
+        al_free(device);
+        alcSetError(NULL, ALC_OUT_OF_MEMORY);
+        return NULL;
+    }
+
+    device->Flags |= DEVICE_FREQUENCY_REQUEST;
+    device->Frequency = frequency;
+
+    device->Flags |= DEVICE_CHANNELS_REQUEST | DEVICE_SAMPLE_TYPE_REQUEST;
+    if(DecomposeDevFormat(format, &device->FmtChans, &device->FmtType) == AL_FALSE)
+    {
+        al_free(device);
+        alcSetError(NULL, ALC_INVALID_ENUM);
+        return NULL;
+    }
+    device->IsHeadphones = AL_FALSE;
+    device->AmbiFmt = AmbiFormat_Default;
+
+    device->UpdateSize = samples;
+    device->NumUpdates = 1;
+
+    if((err=V(device->Backend,open)(deviceName)) != ALC_NO_ERROR)
+    {
+        al_free(device);
+        alcSetError(NULL, err);
+        return NULL;
+    }
+    almtx_init(&device->BackendLock, almtx_plain);
+
+    {
+        ALCdevice *head = ATOMIC_LOAD(&DeviceList);
+        do {
+            device->next = head;
+        } while(!ATOMIC_COMPARE_EXCHANGE_WEAK(ALCdevice*, &DeviceList, &head, device));
+    }
+
+    TRACE("Created device %p, \"%s\"\n", device, al_string_get_cstr(device->DeviceName));
+    return device;
+}
+
+ALC_API ALCboolean ALC_APIENTRY alcCaptureCloseDevice(ALCdevice *device)
+{
+    ALCdevice *list, *next, *nextdev;
+
+    LockLists();
+    list = ATOMIC_LOAD(&DeviceList);
+    do {
+        if(list == device)
+            break;
+    } while((list=list->next) != NULL);
+    if(!list || list->Type != Capture)
+    {
+        alcSetError(list, ALC_INVALID_DEVICE);
+        UnlockLists();
+        return ALC_FALSE;
+    }
+
+    next = device;
+    nextdev = device->next;
+    if(!ATOMIC_COMPARE_EXCHANGE_STRONG(ALCdevice*, &DeviceList, &next, nextdev))
+    {
+        do {
+            list = next;
+            next = device;
+        } while(!COMPARE_EXCHANGE(&list->next, &next, nextdev));
+    }
+    UnlockLists();
+
+    ALCdevice_DecRef(device);
+
+    return ALC_TRUE;
+}
+
+ALC_API void ALC_APIENTRY alcCaptureStart(ALCdevice *device)
+{
+    if(!VerifyDevice(&device) || device->Type != Capture)
+        alcSetError(device, ALC_INVALID_DEVICE);
+    else
+    {
+        almtx_lock(&device->BackendLock);
+        if(!device->Connected)
+            alcSetError(device, ALC_INVALID_DEVICE);
+        else if(!(device->Flags&DEVICE_RUNNING))
+        {
+            if(V0(device->Backend,start)())
+                device->Flags |= DEVICE_RUNNING;
+            else
+            {
+                aluHandleDisconnect(device);
+                alcSetError(device, ALC_INVALID_DEVICE);
+            }
+        }
+        almtx_unlock(&device->BackendLock);
+    }
+
+    if(device) ALCdevice_DecRef(device);
+}
+
+ALC_API void ALC_APIENTRY alcCaptureStop(ALCdevice *device)
+{
+    if(!VerifyDevice(&device) || device->Type != Capture)
+        alcSetError(device, ALC_INVALID_DEVICE);
+    else
+    {
+        almtx_lock(&device->BackendLock);
+        if((device->Flags&DEVICE_RUNNING))
+            V0(device->Backend,stop)();
+        device->Flags &= ~DEVICE_RUNNING;
+        almtx_unlock(&device->BackendLock);
+    }
+
+    if(device) ALCdevice_DecRef(device);
+}
+
+ALC_API void ALC_APIENTRY alcCaptureSamples(ALCdevice *device, ALCvoid *buffer, ALCsizei samples)
+{
+    if(!VerifyDevice(&device) || device->Type != Capture)
+        alcSetError(device, ALC_INVALID_DEVICE);
+    else
+    {
+        ALCenum err = ALC_INVALID_VALUE;
+
+        almtx_lock(&device->BackendLock);
+        if(samples >= 0 && V0(device->Backend,availableSamples)() >= (ALCuint)samples)
+            err = V(device->Backend,captureSamples)(buffer, samples);
+        almtx_unlock(&device->BackendLock);
+
+        if(err != ALC_NO_ERROR)
+            alcSetError(device, err);
+    }
+    if(device) ALCdevice_DecRef(device);
+}
+
+
+/************************************************
+ * ALC loopback functions
+ ************************************************/
+
+/* alcLoopbackOpenDeviceSOFT
+ *
+ * Open a loopback device, for manual rendering.
+ */
+ALC_API ALCdevice* ALC_APIENTRY alcLoopbackOpenDeviceSOFT(const ALCchar *deviceName)
+{
+    ALCbackendFactory *factory;
+    ALCdevice *device;
+
+    DO_INITCONFIG();
+
+    /* Make sure the device name, if specified, is us. */
+    if(deviceName && strcmp(deviceName, alcDefaultName) != 0)
+    {
+        alcSetError(NULL, ALC_INVALID_VALUE);
+        return NULL;
+    }
+
+    device = al_calloc(16, sizeof(ALCdevice));
+    if(!device)
+    {
+        alcSetError(NULL, ALC_OUT_OF_MEMORY);
+        return NULL;
+    }
+
+    //Validate device
+    InitRef(&device->ref, 1);
+    device->Connected = ALC_TRUE;
+    device->Type = Loopback;
+    ATOMIC_INIT(&device->LastError, ALC_NO_ERROR);
+
+    device->Flags = 0;
+    VECTOR_INIT(device->Hrtf.List);
+    AL_STRING_INIT(device->Hrtf.Name);
+    device->Bs2b = NULL;
+    device->Uhj_Encoder = NULL;
+    device->Render_Mode = NormalRender;
+    AL_STRING_INIT(device->DeviceName);
+    device->Dry.Buffer = NULL;
+    device->Dry.NumChannels = 0;
+    device->FOAOut.Buffer = NULL;
+    device->FOAOut.NumChannels = 0;
+    device->RealOut.Buffer = NULL;
+    device->RealOut.NumChannels = 0;
+
+    ATOMIC_INIT(&device->ContextList, NULL);
+
+    device->ClockBase = 0;
+    device->SamplesDone = 0;
+
+    device->SourcesMax = 256;
+    device->AuxiliaryEffectSlotMax = 4;
+    device->NumAuxSends = MAX_SENDS;
+
+    InitUIntMap(&device->BufferMap, ~0);
+    InitUIntMap(&device->EffectMap, ~0);
+    InitUIntMap(&device->FilterMap, ~0);
+
+    factory = ALCloopbackFactory_getFactory();
+    device->Backend = V(factory,createBackend)(device, ALCbackend_Loopback);
+    if(!device->Backend)
+    {
+        al_free(device);
+        alcSetError(NULL, ALC_OUT_OF_MEMORY);
+        return NULL;
+    }
+    almtx_init(&device->BackendLock, almtx_plain);
+
+    //Set output format
+    device->NumUpdates = 0;
+    device->UpdateSize = 0;
+
+    device->Frequency = DEFAULT_OUTPUT_RATE;
+    device->FmtChans = DevFmtChannelsDefault;
+    device->FmtType = DevFmtTypeDefault;
+    device->IsHeadphones = AL_FALSE;
+    device->AmbiFmt = AmbiFormat_Default;
+
+    ConfigValueUInt(NULL, NULL, "sources", &device->SourcesMax);
+    if(device->SourcesMax == 0) device->SourcesMax = 256;
+
+    ConfigValueUInt(NULL, NULL, "slots", &device->AuxiliaryEffectSlotMax);
+    if(device->AuxiliaryEffectSlotMax == 0) device->AuxiliaryEffectSlotMax = 4;
+
+    ConfigValueUInt(NULL, NULL, "sends", &device->NumAuxSends);
+    if(device->NumAuxSends > MAX_SENDS) device->NumAuxSends = MAX_SENDS;
+
+    device->NumStereoSources = 1;
+    device->NumMonoSources = device->SourcesMax - device->NumStereoSources;
+
+    // Open the "backend"
+    V(device->Backend,open)("Loopback");
+
+    {
+        ALCdevice *head = ATOMIC_LOAD(&DeviceList);
+        do {
+            device->next = head;
+        } while(!ATOMIC_COMPARE_EXCHANGE_WEAK(ALCdevice*, &DeviceList, &head, device));
+    }
+
+    TRACE("Created device %p\n", device);
+    return device;
+}
+
+/* alcIsRenderFormatSupportedSOFT
+ *
+ * Determines if the loopback device supports the given format for rendering.
+ */
+ALC_API ALCboolean ALC_APIENTRY alcIsRenderFormatSupportedSOFT(ALCdevice *device, ALCsizei freq, ALCenum channels, ALCenum type)
+{
+    ALCboolean ret = ALC_FALSE;
+
+    if(!VerifyDevice(&device) || device->Type != Loopback)
+        alcSetError(device, ALC_INVALID_DEVICE);
+    else if(freq <= 0)
+        alcSetError(device, ALC_INVALID_VALUE);
+    else
+    {
+        if(IsValidALCType(type) && BytesFromDevFmt(type) > 0 &&
+           IsValidALCChannels(channels) && ChannelsFromDevFmt(channels) > 0 &&
+           freq >= MIN_OUTPUT_RATE)
+            ret = ALC_TRUE;
+    }
+    if(device) ALCdevice_DecRef(device);
+
+    return ret;
+}
+
+/* alcRenderSamplesSOFT
+ *
+ * Renders some samples into a buffer, using the format last set by the
+ * attributes given to alcCreateContext.
+ */
+FORCE_ALIGN ALC_API void ALC_APIENTRY alcRenderSamplesSOFT(ALCdevice *device, ALCvoid *buffer, ALCsizei samples)
+{
+    if(!VerifyDevice(&device) || device->Type != Loopback)
+        alcSetError(device, ALC_INVALID_DEVICE);
+    else if(samples < 0 || (samples > 0 && buffer == NULL))
+        alcSetError(device, ALC_INVALID_VALUE);
+    else
+        aluMixData(device, buffer, samples);
+    if(device) ALCdevice_DecRef(device);
+}
+
+
+/************************************************
+ * ALC DSP pause/resume functions
+ ************************************************/
+
+/* alcDevicePauseSOFT
+ *
+ * Pause the DSP to stop audio processing.
+ */
+ALC_API void ALC_APIENTRY alcDevicePauseSOFT(ALCdevice *device)
+{
+    if(!VerifyDevice(&device) || device->Type != Playback)
+        alcSetError(device, ALC_INVALID_DEVICE);
+    else
+    {
+        almtx_lock(&device->BackendLock);
+        if((device->Flags&DEVICE_RUNNING))
+            V0(device->Backend,stop)();
+        device->Flags &= ~DEVICE_RUNNING;
+        device->Flags |= DEVICE_PAUSED;
+        almtx_unlock(&device->BackendLock);
+    }
+    if(device) ALCdevice_DecRef(device);
+}
+
+/* alcDeviceResumeSOFT
+ *
+ * Resume the DSP to restart audio processing.
+ */
+ALC_API void ALC_APIENTRY alcDeviceResumeSOFT(ALCdevice *device)
+{
+    if(!VerifyDevice(&device) || device->Type != Playback)
+        alcSetError(device, ALC_INVALID_DEVICE);
+    else
+    {
+        almtx_lock(&device->BackendLock);
+        if((device->Flags&DEVICE_PAUSED))
+        {
+            device->Flags &= ~DEVICE_PAUSED;
+            if(ATOMIC_LOAD(&device->ContextList) != NULL)
+            {
+                if(V0(device->Backend,start)() != ALC_FALSE)
+                    device->Flags |= DEVICE_RUNNING;
+                else
+                {
+                    alcSetError(device, ALC_INVALID_DEVICE);
+                    V0(device->Backend,lock)();
+                    aluHandleDisconnect(device);
+                    V0(device->Backend,unlock)();
+                }
+            }
+        }
+        almtx_unlock(&device->BackendLock);
+    }
+    if(device) ALCdevice_DecRef(device);
+}
+
+
+/************************************************
+ * ALC HRTF functions
+ ************************************************/
+
+/* alcGetStringiSOFT
+ *
+ * Gets a string parameter at the given index.
+ */
+ALC_API const ALCchar* ALC_APIENTRY alcGetStringiSOFT(ALCdevice *device, ALCenum paramName, ALCsizei index)
+{
+    const ALCchar *str = NULL;
+
+    if(!VerifyDevice(&device) || device->Type == Capture)
+        alcSetError(device, ALC_INVALID_DEVICE);
+    else switch(paramName)
+    {
+        case ALC_HRTF_SPECIFIER_SOFT:
+            if(index >= 0 && (size_t)index < VECTOR_SIZE(device->Hrtf.List))
+                str = al_string_get_cstr(VECTOR_ELEM(device->Hrtf.List, index).name);
+            else
+                alcSetError(device, ALC_INVALID_VALUE);
+            break;
+
+        default:
+            alcSetError(device, ALC_INVALID_ENUM);
+            break;
+    }
+    if(device) ALCdevice_DecRef(device);
+
+    return str;
+}
+
+/* alcResetDeviceSOFT
+ *
+ * Resets the given device output, using the specified attribute list.
+ */
+ALC_API ALCboolean ALC_APIENTRY alcResetDeviceSOFT(ALCdevice *device, const ALCint *attribs)
+{
+    ALCenum err;
+
+    LockLists();
+    if(!VerifyDevice(&device) || device->Type == Capture || !device->Connected)
+    {
+        UnlockLists();
+        alcSetError(device, ALC_INVALID_DEVICE);
+        if(device) ALCdevice_DecRef(device);
+        return ALC_FALSE;
+    }
+    almtx_lock(&device->BackendLock);
+    UnlockLists();
+
+    err = UpdateDeviceParams(device, attribs);
+    almtx_unlock(&device->BackendLock);
+
+    if(err != ALC_NO_ERROR)
+    {
+        alcSetError(device, err);
+        if(err == ALC_INVALID_DEVICE)
+        {
+            V0(device->Backend,lock)();
+            aluHandleDisconnect(device);
+            V0(device->Backend,unlock)();
+        }
+        ALCdevice_DecRef(device);
+        return ALC_FALSE;
+    }
+    ALCdevice_DecRef(device);
+
+    return ALC_TRUE;
+}

+ 1657 - 0
Engine/lib/openal-soft/Alc/ALu.c

@@ -0,0 +1,1657 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 1999-2007 by authors.
+ * This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the
+ *  Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#include "config.h"
+
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+
+#include "alMain.h"
+#include "alSource.h"
+#include "alBuffer.h"
+#include "alListener.h"
+#include "alAuxEffectSlot.h"
+#include "alu.h"
+#include "bs2b.h"
+#include "hrtf.h"
+#include "uhjfilter.h"
+#include "bformatdec.h"
+#include "static_assert.h"
+
+#include "mixer_defs.h"
+
+#include "backends/base.h"
+
+
+struct ChanMap {
+    enum Channel channel;
+    ALfloat angle;
+    ALfloat elevation;
+};
+
+/* Cone scalar */
+ALfloat ConeScale = 1.0f;
+
+/* Localized Z scalar for mono sources */
+ALfloat ZScale = 1.0f;
+
+extern inline ALfloat minf(ALfloat a, ALfloat b);
+extern inline ALfloat maxf(ALfloat a, ALfloat b);
+extern inline ALfloat clampf(ALfloat val, ALfloat min, ALfloat max);
+
+extern inline ALdouble mind(ALdouble a, ALdouble b);
+extern inline ALdouble maxd(ALdouble a, ALdouble b);
+extern inline ALdouble clampd(ALdouble val, ALdouble min, ALdouble max);
+
+extern inline ALuint minu(ALuint a, ALuint b);
+extern inline ALuint maxu(ALuint a, ALuint b);
+extern inline ALuint clampu(ALuint val, ALuint min, ALuint max);
+
+extern inline ALint mini(ALint a, ALint b);
+extern inline ALint maxi(ALint a, ALint b);
+extern inline ALint clampi(ALint val, ALint min, ALint max);
+
+extern inline ALint64 mini64(ALint64 a, ALint64 b);
+extern inline ALint64 maxi64(ALint64 a, ALint64 b);
+extern inline ALint64 clampi64(ALint64 val, ALint64 min, ALint64 max);
+
+extern inline ALuint64 minu64(ALuint64 a, ALuint64 b);
+extern inline ALuint64 maxu64(ALuint64 a, ALuint64 b);
+extern inline ALuint64 clampu64(ALuint64 val, ALuint64 min, ALuint64 max);
+
+extern inline ALfloat lerp(ALfloat val1, ALfloat val2, ALfloat mu);
+extern inline ALfloat resample_fir4(ALfloat val0, ALfloat val1, ALfloat val2, ALfloat val3, ALuint frac);
+extern inline ALfloat resample_fir8(ALfloat val0, ALfloat val1, ALfloat val2, ALfloat val3, ALfloat val4, ALfloat val5, ALfloat val6, ALfloat val7, ALuint frac);
+
+extern inline void aluVectorSet(aluVector *restrict vector, ALfloat x, ALfloat y, ALfloat z, ALfloat w);
+
+extern inline void aluMatrixfSetRow(aluMatrixf *matrix, ALuint row,
+                                    ALfloat m0, ALfloat m1, ALfloat m2, ALfloat m3);
+extern inline void aluMatrixfSet(aluMatrixf *matrix,
+                                 ALfloat m00, ALfloat m01, ALfloat m02, ALfloat m03,
+                                 ALfloat m10, ALfloat m11, ALfloat m12, ALfloat m13,
+                                 ALfloat m20, ALfloat m21, ALfloat m22, ALfloat m23,
+                                 ALfloat m30, ALfloat m31, ALfloat m32, ALfloat m33);
+
+const aluMatrixf IdentityMatrixf = {{
+    { 1.0f, 0.0f, 0.0f, 0.0f },
+    { 0.0f, 1.0f, 0.0f, 0.0f },
+    { 0.0f, 0.0f, 1.0f, 0.0f },
+    { 0.0f, 0.0f, 0.0f, 1.0f },
+}};
+
+
+static inline HrtfDirectMixerFunc SelectHrtfMixer(void)
+{
+#ifdef HAVE_SSE
+    if((CPUCapFlags&CPU_CAP_SSE))
+        return MixDirectHrtf_SSE;
+#endif
+#ifdef HAVE_NEON
+    if((CPUCapFlags&CPU_CAP_NEON))
+        return MixDirectHrtf_Neon;
+#endif
+
+    return MixDirectHrtf_C;
+}
+
+
+static inline void aluCrossproduct(const ALfloat *inVector1, const ALfloat *inVector2, ALfloat *outVector)
+{
+    outVector[0] = inVector1[1]*inVector2[2] - inVector1[2]*inVector2[1];
+    outVector[1] = inVector1[2]*inVector2[0] - inVector1[0]*inVector2[2];
+    outVector[2] = inVector1[0]*inVector2[1] - inVector1[1]*inVector2[0];
+}
+
+static inline ALfloat aluDotproduct(const aluVector *vec1, const aluVector *vec2)
+{
+    return vec1->v[0]*vec2->v[0] + vec1->v[1]*vec2->v[1] + vec1->v[2]*vec2->v[2];
+}
+
+static ALfloat aluNormalize(ALfloat *vec)
+{
+    ALfloat length = sqrtf(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]);
+    if(length > 0.0f)
+    {
+        ALfloat inv_length = 1.0f/length;
+        vec[0] *= inv_length;
+        vec[1] *= inv_length;
+        vec[2] *= inv_length;
+    }
+    return length;
+}
+
+static void aluMatrixfFloat3(ALfloat *vec, ALfloat w, const aluMatrixf *mtx)
+{
+    ALfloat v[4] = { vec[0], vec[1], vec[2], w };
+
+    vec[0] = v[0]*mtx->m[0][0] + v[1]*mtx->m[1][0] + v[2]*mtx->m[2][0] + v[3]*mtx->m[3][0];
+    vec[1] = v[0]*mtx->m[0][1] + v[1]*mtx->m[1][1] + v[2]*mtx->m[2][1] + v[3]*mtx->m[3][1];
+    vec[2] = v[0]*mtx->m[0][2] + v[1]*mtx->m[1][2] + v[2]*mtx->m[2][2] + v[3]*mtx->m[3][2];
+}
+
+static aluVector aluMatrixfVector(const aluMatrixf *mtx, const aluVector *vec)
+{
+    aluVector v;
+    v.v[0] = vec->v[0]*mtx->m[0][0] + vec->v[1]*mtx->m[1][0] + vec->v[2]*mtx->m[2][0] + vec->v[3]*mtx->m[3][0];
+    v.v[1] = vec->v[0]*mtx->m[0][1] + vec->v[1]*mtx->m[1][1] + vec->v[2]*mtx->m[2][1] + vec->v[3]*mtx->m[3][1];
+    v.v[2] = vec->v[0]*mtx->m[0][2] + vec->v[1]*mtx->m[1][2] + vec->v[2]*mtx->m[2][2] + vec->v[3]*mtx->m[3][2];
+    v.v[3] = vec->v[0]*mtx->m[0][3] + vec->v[1]*mtx->m[1][3] + vec->v[2]*mtx->m[2][3] + vec->v[3]*mtx->m[3][3];
+    return v;
+}
+
+
+/* Prepares the interpolator for a given rate (determined by increment).  A
+ * result of AL_FALSE indicates that the filter output will completely cut
+ * the input signal.
+ *
+ * With a bit of work, and a trade of memory for CPU cost, this could be
+ * modified for use with an interpolated increment for buttery-smooth pitch
+ * changes.
+ */
+static ALboolean BsincPrepare(const ALuint increment, BsincState *state)
+{
+    static const ALfloat scaleBase = 1.510578918e-01f, scaleRange = 1.177936623e+00f;
+    static const ALuint m[BSINC_SCALE_COUNT] = { 24, 24, 24, 24, 24, 24, 24, 20, 20, 20, 16, 16, 16, 12, 12, 12 };
+    static const ALuint to[4][BSINC_SCALE_COUNT] =
+    {
+        { 0, 24, 408, 792, 1176, 1560, 1944, 2328, 2648, 2968, 3288, 3544, 3800, 4056, 4248, 4440 },
+        { 4632, 5016, 5400, 5784, 6168, 6552, 6936, 7320, 7640, 7960, 8280, 8536, 8792, 9048, 9240, 0 },
+        { 0, 9432, 9816, 10200, 10584, 10968, 11352, 11736, 12056, 12376, 12696, 12952, 13208, 13464, 13656, 13848 },
+        { 14040, 14424, 14808, 15192, 15576, 15960, 16344, 16728, 17048, 17368, 17688, 17944, 18200, 18456, 18648, 0 }
+    };
+    static const ALuint tm[2][BSINC_SCALE_COUNT] =
+    {
+        { 0, 24, 24, 24, 24, 24, 24, 20, 20, 20, 16, 16, 16, 12, 12, 12 },
+        { 24, 24, 24, 24, 24, 24, 24, 20, 20, 20, 16, 16, 16, 12, 12, 0 }
+    };
+    ALfloat sf;
+    ALuint si, pi;
+    ALboolean uncut = AL_TRUE;
+
+    if(increment > FRACTIONONE)
+    {
+        sf = (ALfloat)FRACTIONONE / increment;
+        if(sf < scaleBase)
+        {
+            /* Signal has been completely cut.  The return result can be used
+             * to skip the filter (and output zeros) as an optimization.
+             */
+            sf = 0.0f;
+            si = 0;
+            uncut = AL_FALSE;
+        }
+        else
+        {
+            sf = (BSINC_SCALE_COUNT - 1) * (sf - scaleBase) * scaleRange;
+            si = fastf2u(sf);
+            /* The interpolation factor is fit to this diagonally-symmetric
+             * curve to reduce the transition ripple caused by interpolating
+             * different scales of the sinc function.
+             */
+            sf = 1.0f - cosf(asinf(sf - si));
+        }
+    }
+    else
+    {
+        sf = 0.0f;
+        si = BSINC_SCALE_COUNT - 1;
+    }
+
+    state->sf = sf;
+    state->m = m[si];
+    state->l = -(ALint)((m[si] / 2) - 1);
+    /* The CPU cost of this table re-mapping could be traded for the memory
+     * cost of a complete table map (1024 elements large).
+     */
+    for(pi = 0;pi < BSINC_PHASE_COUNT;pi++)
+    {
+        state->coeffs[pi].filter  = &bsincTab[to[0][si] + tm[0][si]*pi];
+        state->coeffs[pi].scDelta = &bsincTab[to[1][si] + tm[1][si]*pi];
+        state->coeffs[pi].phDelta = &bsincTab[to[2][si] + tm[0][si]*pi];
+        state->coeffs[pi].spDelta = &bsincTab[to[3][si] + tm[1][si]*pi];
+    }
+    return uncut;
+}
+
+
+static ALboolean CalcListenerParams(ALCcontext *Context)
+{
+    ALlistener *Listener = Context->Listener;
+    ALfloat N[3], V[3], U[3], P[3];
+    struct ALlistenerProps *first;
+    struct ALlistenerProps *props;
+    aluVector vel;
+
+    props = ATOMIC_EXCHANGE(struct ALlistenerProps*, &Listener->Update, NULL, almemory_order_acq_rel);
+    if(!props) return AL_FALSE;
+
+    /* AT then UP */
+    N[0] = ATOMIC_LOAD(&props->Forward[0], almemory_order_relaxed);
+    N[1] = ATOMIC_LOAD(&props->Forward[1], almemory_order_relaxed);
+    N[2] = ATOMIC_LOAD(&props->Forward[2], almemory_order_relaxed);
+    aluNormalize(N);
+    V[0] = ATOMIC_LOAD(&props->Up[0], almemory_order_relaxed);
+    V[1] = ATOMIC_LOAD(&props->Up[1], almemory_order_relaxed);
+    V[2] = ATOMIC_LOAD(&props->Up[2], almemory_order_relaxed);
+    aluNormalize(V);
+    /* Build and normalize right-vector */
+    aluCrossproduct(N, V, U);
+    aluNormalize(U);
+
+    aluMatrixfSet(&Listener->Params.Matrix,
+        U[0], V[0], -N[0], 0.0,
+        U[1], V[1], -N[1], 0.0,
+        U[2], V[2], -N[2], 0.0,
+         0.0,  0.0,   0.0, 1.0
+    );
+
+    P[0] = ATOMIC_LOAD(&props->Position[0], almemory_order_relaxed);
+    P[1] = ATOMIC_LOAD(&props->Position[1], almemory_order_relaxed);
+    P[2] = ATOMIC_LOAD(&props->Position[2], almemory_order_relaxed);
+    aluMatrixfFloat3(P, 1.0, &Listener->Params.Matrix);
+    aluMatrixfSetRow(&Listener->Params.Matrix, 3, -P[0], -P[1], -P[2], 1.0f);
+
+    aluVectorSet(&vel, ATOMIC_LOAD(&props->Velocity[0], almemory_order_relaxed),
+                       ATOMIC_LOAD(&props->Velocity[1], almemory_order_relaxed),
+                       ATOMIC_LOAD(&props->Velocity[2], almemory_order_relaxed),
+                       0.0f);
+    Listener->Params.Velocity = aluMatrixfVector(&Listener->Params.Matrix, &vel);
+
+    Listener->Params.Gain = ATOMIC_LOAD(&props->Gain, almemory_order_relaxed) * Context->GainBoost;
+    Listener->Params.MetersPerUnit = ATOMIC_LOAD(&props->MetersPerUnit, almemory_order_relaxed);
+
+    Listener->Params.DopplerFactor = ATOMIC_LOAD(&props->DopplerFactor, almemory_order_relaxed);
+    Listener->Params.SpeedOfSound = ATOMIC_LOAD(&props->SpeedOfSound, almemory_order_relaxed) *
+                                    ATOMIC_LOAD(&props->DopplerVelocity, almemory_order_relaxed);
+
+    Listener->Params.SourceDistanceModel = ATOMIC_LOAD(&props->SourceDistanceModel, almemory_order_relaxed);
+    Listener->Params.DistanceModel = ATOMIC_LOAD(&props->DistanceModel, almemory_order_relaxed);
+
+    /* WARNING: A livelock is theoretically possible if another thread keeps
+     * changing the freelist head without giving this a chance to actually swap
+     * in the old container (practically impossible with this little code,
+     * but...).
+     */
+    first = ATOMIC_LOAD(&Listener->FreeList);
+    do {
+        ATOMIC_STORE(&props->next, first, almemory_order_relaxed);
+    } while(ATOMIC_COMPARE_EXCHANGE_WEAK(struct ALlistenerProps*,
+            &Listener->FreeList, &first, props) == 0);
+
+    return AL_TRUE;
+}
+
+static ALboolean CalcEffectSlotParams(ALeffectslot *slot, ALCdevice *device)
+{
+    struct ALeffectslotProps *first;
+    struct ALeffectslotProps *props;
+    ALeffectState *state;
+
+    props = ATOMIC_EXCHANGE(struct ALeffectslotProps*, &slot->Update, NULL, almemory_order_acq_rel);
+    if(!props) return AL_FALSE;
+
+    slot->Params.Gain = ATOMIC_LOAD(&props->Gain, almemory_order_relaxed);
+    slot->Params.AuxSendAuto = ATOMIC_LOAD(&props->AuxSendAuto, almemory_order_relaxed);
+    slot->Params.EffectType = ATOMIC_LOAD(&props->Type, almemory_order_relaxed);
+    if(IsReverbEffect(slot->Params.EffectType))
+    {
+        slot->Params.RoomRolloff = props->Props.Reverb.RoomRolloffFactor;
+        slot->Params.DecayTime = props->Props.Reverb.DecayTime;
+        slot->Params.AirAbsorptionGainHF = props->Props.Reverb.AirAbsorptionGainHF;
+    }
+    else
+    {
+        slot->Params.RoomRolloff = 0.0f;
+        slot->Params.DecayTime = 0.0f;
+        slot->Params.AirAbsorptionGainHF = 1.0f;
+    }
+
+    /* Swap effect states. No need to play with the ref counts since they keep
+     * the same number of refs.
+     */
+    state = ATOMIC_EXCHANGE(ALeffectState*, &props->State, slot->Params.EffectState,
+                            almemory_order_relaxed);
+    slot->Params.EffectState = state;
+
+    V(state,update)(device, slot, &props->Props);
+
+    /* WARNING: A livelock is theoretically possible if another thread keeps
+     * changing the freelist head without giving this a chance to actually swap
+     * in the old container (practically impossible with this little code,
+     * but...).
+     */
+    first = ATOMIC_LOAD(&slot->FreeList);
+    do {
+        ATOMIC_STORE(&props->next, first, almemory_order_relaxed);
+    } while(ATOMIC_COMPARE_EXCHANGE_WEAK(struct ALeffectslotProps*,
+            &slot->FreeList, &first, props) == 0);
+
+    return AL_TRUE;
+}
+
+
+static void CalcNonAttnSourceParams(ALvoice *voice, const struct ALsourceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
+{
+    static const struct ChanMap MonoMap[1] = {
+        { FrontCenter, 0.0f, 0.0f }
+    }, RearMap[2] = {
+        { BackLeft,  DEG2RAD(-150.0f), DEG2RAD(0.0f) },
+        { BackRight, DEG2RAD( 150.0f), DEG2RAD(0.0f) }
+    }, QuadMap[4] = {
+        { FrontLeft,  DEG2RAD( -45.0f), DEG2RAD(0.0f) },
+        { FrontRight, DEG2RAD(  45.0f), DEG2RAD(0.0f) },
+        { BackLeft,   DEG2RAD(-135.0f), DEG2RAD(0.0f) },
+        { BackRight,  DEG2RAD( 135.0f), DEG2RAD(0.0f) }
+    }, X51Map[6] = {
+        { FrontLeft,   DEG2RAD( -30.0f), DEG2RAD(0.0f) },
+        { FrontRight,  DEG2RAD(  30.0f), DEG2RAD(0.0f) },
+        { FrontCenter, DEG2RAD(   0.0f), DEG2RAD(0.0f) },
+        { LFE, 0.0f, 0.0f },
+        { SideLeft,    DEG2RAD(-110.0f), DEG2RAD(0.0f) },
+        { SideRight,   DEG2RAD( 110.0f), DEG2RAD(0.0f) }
+    }, X61Map[7] = {
+        { FrontLeft,    DEG2RAD(-30.0f), DEG2RAD(0.0f) },
+        { FrontRight,   DEG2RAD( 30.0f), DEG2RAD(0.0f) },
+        { FrontCenter,  DEG2RAD(  0.0f), DEG2RAD(0.0f) },
+        { LFE, 0.0f, 0.0f },
+        { BackCenter,   DEG2RAD(180.0f), DEG2RAD(0.0f) },
+        { SideLeft,     DEG2RAD(-90.0f), DEG2RAD(0.0f) },
+        { SideRight,    DEG2RAD( 90.0f), DEG2RAD(0.0f) }
+    }, X71Map[8] = {
+        { FrontLeft,   DEG2RAD( -30.0f), DEG2RAD(0.0f) },
+        { FrontRight,  DEG2RAD(  30.0f), DEG2RAD(0.0f) },
+        { FrontCenter, DEG2RAD(   0.0f), DEG2RAD(0.0f) },
+        { LFE, 0.0f, 0.0f },
+        { BackLeft,    DEG2RAD(-150.0f), DEG2RAD(0.0f) },
+        { BackRight,   DEG2RAD( 150.0f), DEG2RAD(0.0f) },
+        { SideLeft,    DEG2RAD( -90.0f), DEG2RAD(0.0f) },
+        { SideRight,   DEG2RAD(  90.0f), DEG2RAD(0.0f) }
+    };
+
+    const ALCdevice *Device = ALContext->Device;
+    const ALlistener *Listener = ALContext->Listener;
+    ALfloat SourceVolume,ListenerGain,MinVolume,MaxVolume;
+    ALfloat DryGain, DryGainHF, DryGainLF;
+    ALfloat WetGain[MAX_SENDS];
+    ALfloat WetGainHF[MAX_SENDS];
+    ALfloat WetGainLF[MAX_SENDS];
+    ALeffectslot *SendSlots[MAX_SENDS];
+    ALuint NumSends, Frequency;
+    ALboolean Relative;
+    const struct ChanMap *chans = NULL;
+    struct ChanMap StereoMap[2] = {
+        { FrontLeft,  DEG2RAD(-30.0f), DEG2RAD(0.0f) },
+        { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) }
+    };
+    ALuint num_channels = 0;
+    ALboolean DirectChannels;
+    ALboolean isbformat = AL_FALSE;
+    ALfloat Pitch;
+    ALuint i, j, c;
+
+    /* Get device properties */
+    NumSends  = Device->NumAuxSends;
+    Frequency = Device->Frequency;
+
+    /* Get listener properties */
+    ListenerGain = Listener->Params.Gain;
+
+    /* Get source properties */
+    SourceVolume   = ATOMIC_LOAD(&props->Gain, almemory_order_relaxed);
+    MinVolume      = ATOMIC_LOAD(&props->MinGain, almemory_order_relaxed);
+    MaxVolume      = ATOMIC_LOAD(&props->MaxGain, almemory_order_relaxed);
+    Pitch          = ATOMIC_LOAD(&props->Pitch, almemory_order_relaxed);
+    Relative       = ATOMIC_LOAD(&props->HeadRelative, almemory_order_relaxed);
+    DirectChannels = ATOMIC_LOAD(&props->DirectChannels, almemory_order_relaxed);
+
+    /* Convert counter-clockwise to clockwise. */
+    StereoMap[0].angle = -ATOMIC_LOAD(&props->StereoPan[0], almemory_order_relaxed);
+    StereoMap[1].angle = -ATOMIC_LOAD(&props->StereoPan[1], almemory_order_relaxed);
+
+    voice->DirectOut.Buffer = Device->Dry.Buffer;
+    voice->DirectOut.Channels = Device->Dry.NumChannels;
+    for(i = 0;i < NumSends;i++)
+    {
+        SendSlots[i] = ATOMIC_LOAD(&props->Send[i].Slot, almemory_order_relaxed);
+        if(!SendSlots[i] && i == 0)
+            SendSlots[i] = Device->DefaultSlot;
+        if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
+        {
+            SendSlots[i] = NULL;
+            voice->SendOut[i].Buffer = NULL;
+            voice->SendOut[i].Channels = 0;
+        }
+        else
+        {
+            voice->SendOut[i].Buffer = SendSlots[i]->WetBuffer;
+            voice->SendOut[i].Channels = SendSlots[i]->NumChannels;
+        }
+    }
+
+    /* Calculate the stepping value */
+    Pitch *= (ALfloat)ALBuffer->Frequency / Frequency;
+    if(Pitch > (ALfloat)MAX_PITCH)
+        voice->Step = MAX_PITCH<<FRACTIONBITS;
+    else
+        voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
+    BsincPrepare(voice->Step, &voice->SincState);
+
+    /* Calculate gains */
+    DryGain  = clampf(SourceVolume, MinVolume, MaxVolume);
+    DryGain *= ATOMIC_LOAD(&props->Direct.Gain, almemory_order_relaxed) * ListenerGain;
+    DryGain  = minf(DryGain, GAIN_MIX_MAX);
+    DryGainHF = ATOMIC_LOAD(&props->Direct.GainHF, almemory_order_relaxed);
+    DryGainLF = ATOMIC_LOAD(&props->Direct.GainLF, almemory_order_relaxed);
+    for(i = 0;i < NumSends;i++)
+    {
+        WetGain[i]  = clampf(SourceVolume, MinVolume, MaxVolume);
+        WetGain[i] *= ATOMIC_LOAD(&props->Send[i].Gain, almemory_order_relaxed) * ListenerGain;
+        WetGain[i]  = minf(WetGain[i], GAIN_MIX_MAX);
+        WetGainHF[i] = ATOMIC_LOAD(&props->Send[i].GainHF, almemory_order_relaxed);
+        WetGainLF[i] = ATOMIC_LOAD(&props->Send[i].GainLF, almemory_order_relaxed);
+    }
+
+    switch(ALBuffer->FmtChannels)
+    {
+    case FmtMono:
+        chans = MonoMap;
+        num_channels = 1;
+        break;
+
+    case FmtStereo:
+        chans = StereoMap;
+        num_channels = 2;
+        break;
+
+    case FmtRear:
+        chans = RearMap;
+        num_channels = 2;
+        break;
+
+    case FmtQuad:
+        chans = QuadMap;
+        num_channels = 4;
+        break;
+
+    case FmtX51:
+        chans = X51Map;
+        num_channels = 6;
+        break;
+
+    case FmtX61:
+        chans = X61Map;
+        num_channels = 7;
+        break;
+
+    case FmtX71:
+        chans = X71Map;
+        num_channels = 8;
+        break;
+
+    case FmtBFormat2D:
+        num_channels = 3;
+        isbformat = AL_TRUE;
+        DirectChannels = AL_FALSE;
+        break;
+
+    case FmtBFormat3D:
+        num_channels = 4;
+        isbformat = AL_TRUE;
+        DirectChannels = AL_FALSE;
+        break;
+    }
+
+    if(isbformat)
+    {
+        ALfloat N[3], V[3], U[3];
+        aluMatrixf matrix;
+        ALfloat scale;
+
+        /* AT then UP */
+        N[0] = ATOMIC_LOAD(&props->Orientation[0][0], almemory_order_relaxed);
+        N[1] = ATOMIC_LOAD(&props->Orientation[0][1], almemory_order_relaxed);
+        N[2] = ATOMIC_LOAD(&props->Orientation[0][2], almemory_order_relaxed);
+        aluNormalize(N);
+        V[0] = ATOMIC_LOAD(&props->Orientation[1][0], almemory_order_relaxed);
+        V[1] = ATOMIC_LOAD(&props->Orientation[1][1], almemory_order_relaxed);
+        V[2] = ATOMIC_LOAD(&props->Orientation[1][2], almemory_order_relaxed);
+        aluNormalize(V);
+        if(!Relative)
+        {
+            const aluMatrixf *lmatrix = &Listener->Params.Matrix;
+            aluMatrixfFloat3(N, 0.0f, lmatrix);
+            aluMatrixfFloat3(V, 0.0f, lmatrix);
+        }
+        /* Build and normalize right-vector */
+        aluCrossproduct(N, V, U);
+        aluNormalize(U);
+
+        /* Build a rotate + conversion matrix (FuMa -> ACN+N3D). */
+        scale = 1.732050808f;
+        aluMatrixfSet(&matrix,
+            1.414213562f,        0.0f,        0.0f,        0.0f,
+                    0.0f, -N[0]*scale,  N[1]*scale, -N[2]*scale,
+                    0.0f,  U[0]*scale, -U[1]*scale,  U[2]*scale,
+                    0.0f, -V[0]*scale,  V[1]*scale, -V[2]*scale
+        );
+
+        voice->DirectOut.Buffer = Device->FOAOut.Buffer;
+        voice->DirectOut.Channels = Device->FOAOut.NumChannels;
+        for(c = 0;c < num_channels;c++)
+            ComputeFirstOrderGains(Device->FOAOut, matrix.m[c], DryGain,
+                                   voice->Chan[c].Direct.Gains.Target);
+
+        for(i = 0;i < NumSends;i++)
+        {
+            if(!SendSlots[i])
+            {
+                for(c = 0;c < num_channels;c++)
+                {
+                    for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
+                        voice->Chan[c].Send[i].Gains.Target[j] = 0.0f;
+                }
+            }
+            else
+            {
+                for(c = 0;c < num_channels;c++)
+                {
+                    const ALeffectslot *Slot = SendSlots[i];
+                    ComputeFirstOrderGainsBF(Slot->ChanMap, Slot->NumChannels, matrix.m[c],
+                                             WetGain[i], voice->Chan[c].Send[i].Gains.Target);
+                }
+            }
+        }
+
+        voice->IsHrtf = AL_FALSE;
+    }
+    else
+    {
+        ALfloat coeffs[MAX_AMBI_COEFFS];
+
+        if(DirectChannels)
+        {
+            /* Skip the virtual channels and write inputs to the real output. */
+            voice->DirectOut.Buffer = Device->RealOut.Buffer;
+            voice->DirectOut.Channels = Device->RealOut.NumChannels;
+            for(c = 0;c < num_channels;c++)
+            {
+                int idx;
+                for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
+                    voice->Chan[c].Direct.Gains.Target[j] = 0.0f;
+                if((idx=GetChannelIdxByName(Device->RealOut, chans[c].channel)) != -1)
+                    voice->Chan[c].Direct.Gains.Target[idx] = DryGain;
+            }
+
+            /* Auxiliary sends still use normal panning since they mix to B-Format, which can't
+             * channel-match. */
+            for(c = 0;c < num_channels;c++)
+            {
+                CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
+
+                for(i = 0;i < NumSends;i++)
+                {
+                    if(!SendSlots[i])
+                    {
+                        for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
+                            voice->Chan[c].Send[i].Gains.Target[j] = 0.0f;
+                    }
+                    else
+                    {
+                        const ALeffectslot *Slot = SendSlots[i];
+                        ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs,
+                                              WetGain[i], voice->Chan[c].Send[i].Gains.Target);
+                    }
+                }
+            }
+
+            voice->IsHrtf = AL_FALSE;
+        }
+        else if(Device->Render_Mode == HrtfRender)
+        {
+            /* Full HRTF rendering. Skip the virtual channels and render each
+             * input channel to the real outputs.
+             */
+            voice->DirectOut.Buffer = Device->RealOut.Buffer;
+            voice->DirectOut.Channels = Device->RealOut.NumChannels;
+            for(c = 0;c < num_channels;c++)
+            {
+                if(chans[c].channel == LFE)
+                {
+                    /* Skip LFE */
+                    voice->Chan[c].Direct.Hrtf.Target.Delay[0] = 0;
+                    voice->Chan[c].Direct.Hrtf.Target.Delay[1] = 0;
+                    for(i = 0;i < HRIR_LENGTH;i++)
+                    {
+                        voice->Chan[c].Direct.Hrtf.Target.Coeffs[i][0] = 0.0f;
+                        voice->Chan[c].Direct.Hrtf.Target.Coeffs[i][1] = 0.0f;
+                    }
+
+                    for(i = 0;i < NumSends;i++)
+                    {
+                        for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
+                            voice->Chan[c].Send[i].Gains.Target[j] = 0.0f;
+                    }
+
+                    continue;
+                }
+
+                /* Get the static HRIR coefficients and delays for this channel. */
+                GetHrtfCoeffs(Device->Hrtf.Handle,
+                    chans[c].elevation, chans[c].angle, 0.0f, DryGain,
+                    voice->Chan[c].Direct.Hrtf.Target.Coeffs,
+                    voice->Chan[c].Direct.Hrtf.Target.Delay
+                );
+
+                /* Normal panning for auxiliary sends. */
+                CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
+
+                for(i = 0;i < NumSends;i++)
+                {
+                    if(!SendSlots[i])
+                    {
+                        for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
+                            voice->Chan[c].Send[i].Gains.Target[j] = 0.0f;
+                    }
+                    else
+                    {
+                        const ALeffectslot *Slot = SendSlots[i];
+                        ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs,
+                                              WetGain[i], voice->Chan[c].Send[i].Gains.Target);
+                    }
+                }
+            }
+
+            voice->IsHrtf = AL_TRUE;
+        }
+        else
+        {
+            /* Non-HRTF rendering. Use normal panning to the output. */
+            for(c = 0;c < num_channels;c++)
+            {
+                /* Special-case LFE */
+                if(chans[c].channel == LFE)
+                {
+                    for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
+                        voice->Chan[c].Direct.Gains.Target[j] = 0.0f;
+                    if(Device->Dry.Buffer == Device->RealOut.Buffer)
+                    {
+                        int idx;
+                        if((idx=GetChannelIdxByName(Device->RealOut, chans[c].channel)) != -1)
+                            voice->Chan[c].Direct.Gains.Target[idx] = DryGain;
+                    }
+
+                    for(i = 0;i < NumSends;i++)
+                    {
+                        ALuint j;
+                        for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
+                            voice->Chan[c].Send[i].Gains.Target[j] = 0.0f;
+                    }
+                    continue;
+                }
+
+                if(Device->Render_Mode == StereoPair)
+                {
+                    /* Clamp X so it remains within 30 degrees of 0 or 180 degree azimuth. */
+                    ALfloat x = sinf(chans[c].angle) * cosf(chans[c].elevation);
+                    coeffs[0] = clampf(-x, -0.5f, 0.5f) + 0.5f;
+                    voice->Chan[c].Direct.Gains.Target[0] = coeffs[0] * DryGain;
+                    voice->Chan[c].Direct.Gains.Target[1] = (1.0f-coeffs[0]) * DryGain;
+                    for(j = 2;j < MAX_OUTPUT_CHANNELS;j++)
+                        voice->Chan[c].Direct.Gains.Target[j] = 0.0f;
+
+                    CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
+                }
+                else
+                {
+                    CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
+                    ComputePanningGains(Device->Dry, coeffs, DryGain,
+                                        voice->Chan[c].Direct.Gains.Target);
+                }
+
+                for(i = 0;i < NumSends;i++)
+                {
+                    if(!SendSlots[i])
+                    {
+                        ALuint j;
+                        for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
+                            voice->Chan[c].Send[i].Gains.Target[j] = 0.0f;
+                    }
+                    else
+                    {
+                        const ALeffectslot *Slot = SendSlots[i];
+                        ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs,
+                                              WetGain[i], voice->Chan[c].Send[i].Gains.Target);
+                    }
+                }
+            }
+
+            voice->IsHrtf = AL_FALSE;
+        }
+    }
+
+    {
+        ALfloat hfscale = ATOMIC_LOAD(&props->Direct.HFReference, almemory_order_relaxed) /
+                          Frequency;
+        ALfloat lfscale = ATOMIC_LOAD(&props->Direct.LFReference, almemory_order_relaxed) /
+                          Frequency;
+        DryGainHF = maxf(DryGainHF, 0.0001f);
+        DryGainLF = maxf(DryGainLF, 0.0001f);
+        for(c = 0;c < num_channels;c++)
+        {
+            voice->Chan[c].Direct.FilterType = AF_None;
+            if(DryGainHF != 1.0f) voice->Chan[c].Direct.FilterType |= AF_LowPass;
+            if(DryGainLF != 1.0f) voice->Chan[c].Direct.FilterType |= AF_HighPass;
+            ALfilterState_setParams(
+                &voice->Chan[c].Direct.LowPass, ALfilterType_HighShelf,
+                DryGainHF, hfscale, calc_rcpQ_from_slope(DryGainHF, 0.75f)
+            );
+            ALfilterState_setParams(
+                &voice->Chan[c].Direct.HighPass, ALfilterType_LowShelf,
+                DryGainLF, lfscale, calc_rcpQ_from_slope(DryGainLF, 0.75f)
+            );
+        }
+    }
+    for(i = 0;i < NumSends;i++)
+    {
+        ALfloat hfscale = ATOMIC_LOAD(&props->Send[i].HFReference, almemory_order_relaxed) /
+                          Frequency;
+        ALfloat lfscale = ATOMIC_LOAD(&props->Send[i].LFReference, almemory_order_relaxed) /
+                          Frequency;
+        WetGainHF[i] = maxf(WetGainHF[i], 0.0001f);
+        WetGainLF[i] = maxf(WetGainLF[i], 0.0001f);
+        for(c = 0;c < num_channels;c++)
+        {
+            voice->Chan[c].Send[i].FilterType = AF_None;
+            if(WetGainHF[i] != 1.0f) voice->Chan[c].Send[i].FilterType |= AF_LowPass;
+            if(WetGainLF[i] != 1.0f) voice->Chan[c].Send[i].FilterType |= AF_HighPass;
+            ALfilterState_setParams(
+                &voice->Chan[c].Send[i].LowPass, ALfilterType_HighShelf,
+                WetGainHF[i], hfscale, calc_rcpQ_from_slope(WetGainHF[i], 0.75f)
+            );
+            ALfilterState_setParams(
+                &voice->Chan[c].Send[i].HighPass, ALfilterType_LowShelf,
+                WetGainLF[i], lfscale, calc_rcpQ_from_slope(WetGainLF[i], 0.75f)
+            );
+        }
+    }
+}
+
+static void CalcAttnSourceParams(ALvoice *voice, const struct ALsourceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
+{
+    const ALCdevice *Device = ALContext->Device;
+    const ALlistener *Listener = ALContext->Listener;
+    aluVector Position, Velocity, Direction, SourceToListener;
+    ALfloat InnerAngle,OuterAngle,Distance,ClampedDist;
+    ALfloat MinVolume,MaxVolume,MinDist,MaxDist,Rolloff;
+    ALfloat SourceVolume,ListenerGain;
+    ALfloat DopplerFactor, SpeedOfSound;
+    ALfloat AirAbsorptionFactor;
+    ALfloat RoomAirAbsorption[MAX_SENDS];
+    ALeffectslot *SendSlots[MAX_SENDS];
+    ALfloat Attenuation;
+    ALfloat RoomAttenuation[MAX_SENDS];
+    ALfloat MetersPerUnit;
+    ALfloat RoomRolloffBase;
+    ALfloat RoomRolloff[MAX_SENDS];
+    ALfloat DecayDistance[MAX_SENDS];
+    ALfloat DryGain;
+    ALfloat DryGainHF;
+    ALfloat DryGainLF;
+    ALboolean DryGainHFAuto;
+    ALfloat WetGain[MAX_SENDS];
+    ALfloat WetGainHF[MAX_SENDS];
+    ALfloat WetGainLF[MAX_SENDS];
+    ALboolean WetGainAuto;
+    ALboolean WetGainHFAuto;
+    ALfloat Pitch;
+    ALuint Frequency;
+    ALint NumSends;
+    ALint i;
+
+    DryGainHF = 1.0f;
+    DryGainLF = 1.0f;
+    for(i = 0;i < MAX_SENDS;i++)
+    {
+        WetGainHF[i] = 1.0f;
+        WetGainLF[i] = 1.0f;
+    }
+
+    /* Get context/device properties */
+    DopplerFactor = Listener->Params.DopplerFactor;
+    SpeedOfSound  = Listener->Params.SpeedOfSound;
+    NumSends      = Device->NumAuxSends;
+    Frequency     = Device->Frequency;
+
+    /* Get listener properties */
+    ListenerGain  = Listener->Params.Gain;
+    MetersPerUnit = Listener->Params.MetersPerUnit;
+
+    /* Get source properties */
+    SourceVolume   = ATOMIC_LOAD(&props->Gain, almemory_order_relaxed);
+    MinVolume      = ATOMIC_LOAD(&props->MinGain, almemory_order_relaxed);
+    MaxVolume      = ATOMIC_LOAD(&props->MaxGain, almemory_order_relaxed);
+    Pitch          = ATOMIC_LOAD(&props->Pitch, almemory_order_relaxed);
+    aluVectorSet(&Position, ATOMIC_LOAD(&props->Position[0], almemory_order_relaxed),
+                            ATOMIC_LOAD(&props->Position[1], almemory_order_relaxed),
+                            ATOMIC_LOAD(&props->Position[2], almemory_order_relaxed),
+                            1.0f);
+    aluVectorSet(&Direction, ATOMIC_LOAD(&props->Direction[0], almemory_order_relaxed),
+                             ATOMIC_LOAD(&props->Direction[1], almemory_order_relaxed),
+                             ATOMIC_LOAD(&props->Direction[2], almemory_order_relaxed),
+                             0.0f);
+    aluVectorSet(&Velocity, ATOMIC_LOAD(&props->Velocity[0], almemory_order_relaxed),
+                            ATOMIC_LOAD(&props->Velocity[1], almemory_order_relaxed),
+                            ATOMIC_LOAD(&props->Velocity[2], almemory_order_relaxed),
+                            0.0f);
+    MinDist        = ATOMIC_LOAD(&props->RefDistance, almemory_order_relaxed);
+    MaxDist        = ATOMIC_LOAD(&props->MaxDistance, almemory_order_relaxed);
+    Rolloff        = ATOMIC_LOAD(&props->RollOffFactor, almemory_order_relaxed);
+    DopplerFactor *= ATOMIC_LOAD(&props->DopplerFactor, almemory_order_relaxed);
+    InnerAngle     = ATOMIC_LOAD(&props->InnerAngle, almemory_order_relaxed);
+    OuterAngle     = ATOMIC_LOAD(&props->OuterAngle, almemory_order_relaxed);
+    AirAbsorptionFactor = ATOMIC_LOAD(&props->AirAbsorptionFactor, almemory_order_relaxed);
+    DryGainHFAuto   = ATOMIC_LOAD(&props->DryGainHFAuto, almemory_order_relaxed);
+    WetGainAuto     = ATOMIC_LOAD(&props->WetGainAuto, almemory_order_relaxed);
+    WetGainHFAuto   = ATOMIC_LOAD(&props->WetGainHFAuto, almemory_order_relaxed);
+    RoomRolloffBase = ATOMIC_LOAD(&props->RoomRolloffFactor, almemory_order_relaxed);
+
+    voice->DirectOut.Buffer = Device->Dry.Buffer;
+    voice->DirectOut.Channels = Device->Dry.NumChannels;
+    for(i = 0;i < NumSends;i++)
+    {
+        SendSlots[i] = ATOMIC_LOAD(&props->Send[i].Slot, almemory_order_relaxed);
+
+        if(!SendSlots[i] && i == 0)
+            SendSlots[i] = Device->DefaultSlot;
+        if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
+        {
+            SendSlots[i] = NULL;
+            RoomRolloff[i] = 0.0f;
+            DecayDistance[i] = 0.0f;
+            RoomAirAbsorption[i] = 1.0f;
+        }
+        else if(SendSlots[i]->Params.AuxSendAuto)
+        {
+            RoomRolloff[i] = SendSlots[i]->Params.RoomRolloff + RoomRolloffBase;
+            DecayDistance[i] = SendSlots[i]->Params.DecayTime *
+                               SPEEDOFSOUNDMETRESPERSEC;
+            RoomAirAbsorption[i] = SendSlots[i]->Params.AirAbsorptionGainHF;
+        }
+        else
+        {
+            /* If the slot's auxiliary send auto is off, the data sent to the
+             * effect slot is the same as the dry path, sans filter effects */
+            RoomRolloff[i] = Rolloff;
+            DecayDistance[i] = 0.0f;
+            RoomAirAbsorption[i] = AIRABSORBGAINHF;
+        }
+
+        if(!SendSlots[i])
+        {
+            voice->SendOut[i].Buffer = NULL;
+            voice->SendOut[i].Channels = 0;
+        }
+        else
+        {
+            voice->SendOut[i].Buffer = SendSlots[i]->WetBuffer;
+            voice->SendOut[i].Channels = SendSlots[i]->NumChannels;
+        }
+    }
+
+    /* Transform source to listener space (convert to head relative) */
+    if(ATOMIC_LOAD(&props->HeadRelative, almemory_order_relaxed) == AL_FALSE)
+    {
+        const aluMatrixf *Matrix = &Listener->Params.Matrix;
+        /* Transform source vectors */
+        Position = aluMatrixfVector(Matrix, &Position);
+        Velocity = aluMatrixfVector(Matrix, &Velocity);
+        Direction = aluMatrixfVector(Matrix, &Direction);
+    }
+    else
+    {
+        const aluVector *lvelocity = &Listener->Params.Velocity;
+        /* Offset the source velocity to be relative of the listener velocity */
+        Velocity.v[0] += lvelocity->v[0];
+        Velocity.v[1] += lvelocity->v[1];
+        Velocity.v[2] += lvelocity->v[2];
+    }
+
+    aluNormalize(Direction.v);
+    SourceToListener.v[0] = -Position.v[0];
+    SourceToListener.v[1] = -Position.v[1];
+    SourceToListener.v[2] = -Position.v[2];
+    SourceToListener.v[3] = 0.0f;
+    Distance = aluNormalize(SourceToListener.v);
+
+    /* Calculate distance attenuation */
+    ClampedDist = Distance;
+
+    Attenuation = 1.0f;
+    for(i = 0;i < NumSends;i++)
+        RoomAttenuation[i] = 1.0f;
+    switch(Listener->Params.SourceDistanceModel ?
+           ATOMIC_LOAD(&props->DistanceModel, almemory_order_relaxed) :
+           Listener->Params.DistanceModel)
+    {
+        case InverseDistanceClamped:
+            ClampedDist = clampf(ClampedDist, MinDist, MaxDist);
+            if(MaxDist < MinDist)
+                break;
+            /*fall-through*/
+        case InverseDistance:
+            if(MinDist > 0.0f)
+            {
+                ALfloat dist = lerp(MinDist, ClampedDist, Rolloff);
+                if(dist > 0.0f) Attenuation = MinDist / dist;
+                for(i = 0;i < NumSends;i++)
+                {
+                    dist = lerp(MinDist, ClampedDist, RoomRolloff[i]);
+                    if(dist > 0.0f) RoomAttenuation[i] = MinDist / dist;
+                }
+            }
+            break;
+
+        case LinearDistanceClamped:
+            ClampedDist = clampf(ClampedDist, MinDist, MaxDist);
+            if(MaxDist < MinDist)
+                break;
+            /*fall-through*/
+        case LinearDistance:
+            if(MaxDist != MinDist)
+            {
+                Attenuation = 1.0f - (Rolloff*(ClampedDist-MinDist)/(MaxDist - MinDist));
+                Attenuation = maxf(Attenuation, 0.0f);
+                for(i = 0;i < NumSends;i++)
+                {
+                    RoomAttenuation[i] = 1.0f - (RoomRolloff[i]*(ClampedDist-MinDist)/(MaxDist - MinDist));
+                    RoomAttenuation[i] = maxf(RoomAttenuation[i], 0.0f);
+                }
+            }
+            break;
+
+        case ExponentDistanceClamped:
+            ClampedDist = clampf(ClampedDist, MinDist, MaxDist);
+            if(MaxDist < MinDist)
+                break;
+            /*fall-through*/
+        case ExponentDistance:
+            if(ClampedDist > 0.0f && MinDist > 0.0f)
+            {
+                Attenuation = powf(ClampedDist/MinDist, -Rolloff);
+                for(i = 0;i < NumSends;i++)
+                    RoomAttenuation[i] = powf(ClampedDist/MinDist, -RoomRolloff[i]);
+            }
+            break;
+
+        case DisableDistance:
+            ClampedDist = MinDist;
+            break;
+    }
+
+    /* Source Gain + Attenuation */
+    DryGain = SourceVolume * Attenuation;
+    for(i = 0;i < NumSends;i++)
+        WetGain[i] = SourceVolume * RoomAttenuation[i];
+
+    /* Distance-based air absorption */
+    if(AirAbsorptionFactor > 0.0f && ClampedDist > MinDist)
+    {
+        ALfloat meters = (ClampedDist-MinDist) * MetersPerUnit;
+        DryGainHF *= powf(AIRABSORBGAINHF, AirAbsorptionFactor*meters);
+        for(i = 0;i < NumSends;i++)
+            WetGainHF[i] *= powf(RoomAirAbsorption[i], AirAbsorptionFactor*meters);
+    }
+
+    if(WetGainAuto)
+    {
+        ALfloat ApparentDist = 1.0f/maxf(Attenuation, 0.00001f) - 1.0f;
+
+        /* Apply a decay-time transformation to the wet path, based on the
+         * attenuation of the dry path.
+         *
+         * Using the apparent distance, based on the distance attenuation, the
+         * initial decay of the reverb effect is calculated and applied to the
+         * wet path.
+         */
+        for(i = 0;i < NumSends;i++)
+        {
+            if(DecayDistance[i] > 0.0f)
+                WetGain[i] *= powf(0.001f/*-60dB*/, ApparentDist/DecayDistance[i]);
+        }
+    }
+
+    /* Calculate directional soundcones */
+    if(InnerAngle < 360.0f)
+    {
+        ALfloat ConeVolume;
+        ALfloat ConeHF;
+        ALfloat Angle;
+        ALfloat scale;
+
+        Angle = RAD2DEG(acosf(aluDotproduct(&Direction, &SourceToListener)) * ConeScale) * 2.0f;
+        if(Angle > InnerAngle)
+        {
+            if(Angle < OuterAngle)
+            {
+                scale = (Angle-InnerAngle) / (OuterAngle-InnerAngle);
+                ConeVolume = lerp(
+                    1.0f, ATOMIC_LOAD(&props->OuterGain, almemory_order_relaxed), scale
+                );
+                ConeHF = lerp(
+                    1.0f, ATOMIC_LOAD(&props->OuterGainHF, almemory_order_relaxed), scale
+                );
+            }
+            else
+            {
+                ConeVolume = ATOMIC_LOAD(&props->OuterGain, almemory_order_relaxed);
+                ConeHF = ATOMIC_LOAD(&props->OuterGainHF, almemory_order_relaxed);
+            }
+            DryGain *= ConeVolume;
+            if(DryGainHFAuto)
+                DryGainHF *= ConeHF;
+        }
+
+        /* Wet path uses the total area of the cone emitter (the room will
+         * receive the same amount of sound regardless of its direction).
+         */
+        scale = (asinf(maxf((OuterAngle-InnerAngle)/360.0f, 0.0f)) / F_PI) +
+                (InnerAngle/360.0f);
+        if(WetGainAuto)
+        {
+            ConeVolume = lerp(
+                1.0f, ATOMIC_LOAD(&props->OuterGain, almemory_order_relaxed), scale
+            );
+            for(i = 0;i < NumSends;i++)
+                WetGain[i] *= ConeVolume;
+        }
+        if(WetGainHFAuto)
+        {
+            ConeHF = lerp(
+                1.0f, ATOMIC_LOAD(&props->OuterGainHF, almemory_order_relaxed), scale
+            );
+            for(i = 0;i < NumSends;i++)
+                WetGainHF[i] *= ConeHF;
+        }
+    }
+
+    /* Apply gain and frequency filters */
+    DryGain  = clampf(DryGain, MinVolume, MaxVolume);
+    DryGain *= ATOMIC_LOAD(&props->Direct.Gain, almemory_order_relaxed) * ListenerGain;
+    DryGain  = minf(DryGain, GAIN_MIX_MAX);
+    DryGainHF *= ATOMIC_LOAD(&props->Direct.GainHF, almemory_order_relaxed);
+    DryGainLF *= ATOMIC_LOAD(&props->Direct.GainLF, almemory_order_relaxed);
+    for(i = 0;i < NumSends;i++)
+    {
+        WetGain[i]  = clampf(WetGain[i], MinVolume, MaxVolume);
+        WetGain[i] *= ATOMIC_LOAD(&props->Send[i].Gain, almemory_order_relaxed) * ListenerGain;
+        WetGain[i]  = minf(WetGain[i], GAIN_MIX_MAX);
+        WetGainHF[i] *= ATOMIC_LOAD(&props->Send[i].GainHF, almemory_order_relaxed);
+        WetGainLF[i] *= ATOMIC_LOAD(&props->Send[i].GainLF, almemory_order_relaxed);
+    }
+
+    /* Calculate velocity-based doppler effect */
+    if(DopplerFactor > 0.0f)
+    {
+        const aluVector *lvelocity = &Listener->Params.Velocity;
+        ALfloat VSS, VLS;
+
+        if(SpeedOfSound < 1.0f)
+        {
+            DopplerFactor *= 1.0f/SpeedOfSound;
+            SpeedOfSound   = 1.0f;
+        }
+
+        VSS = aluDotproduct(&Velocity, &SourceToListener) * DopplerFactor;
+        VLS = aluDotproduct(lvelocity, &SourceToListener) * DopplerFactor;
+
+        Pitch *= clampf(SpeedOfSound-VLS, 1.0f, SpeedOfSound*2.0f - 1.0f) /
+                 clampf(SpeedOfSound-VSS, 1.0f, SpeedOfSound*2.0f - 1.0f);
+    }
+
+    /* Calculate fixed-point stepping value, based on the pitch, buffer
+     * frequency, and output frequency.
+     */
+    Pitch *= (ALfloat)ALBuffer->Frequency / Frequency;
+    if(Pitch > (ALfloat)MAX_PITCH)
+        voice->Step = MAX_PITCH<<FRACTIONBITS;
+    else
+        voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
+    BsincPrepare(voice->Step, &voice->SincState);
+
+    if(Device->Render_Mode == HrtfRender)
+    {
+        /* Full HRTF rendering. Skip the virtual channels and render to the
+         * real outputs.
+         */
+        ALfloat dir[3] = { 0.0f, 0.0f, -1.0f };
+        ALfloat ev = 0.0f, az = 0.0f;
+        ALfloat radius = ATOMIC_LOAD(&props->Radius, almemory_order_relaxed);
+        ALfloat coeffs[MAX_AMBI_COEFFS];
+        ALfloat spread = 0.0f;
+
+        voice->DirectOut.Buffer = Device->RealOut.Buffer;
+        voice->DirectOut.Channels = Device->RealOut.NumChannels;
+
+        if(Distance > FLT_EPSILON)
+        {
+            dir[0] = -SourceToListener.v[0];
+            dir[1] = -SourceToListener.v[1];
+            dir[2] = -SourceToListener.v[2] * ZScale;
+
+            /* Calculate elevation and azimuth only when the source is not at
+             * the listener. This prevents +0 and -0 Z from producing
+             * inconsistent panning. Also, clamp Y in case FP precision errors
+             * cause it to land outside of -1..+1. */
+            ev = asinf(clampf(dir[1], -1.0f, 1.0f));
+            az = atan2f(dir[0], -dir[2]);
+        }
+        if(radius > Distance)
+            spread = F_TAU - Distance/radius*F_PI;
+        else if(Distance > FLT_EPSILON)
+            spread = asinf(radius / Distance) * 2.0f;
+
+        /* Get the HRIR coefficients and delays. */
+        GetHrtfCoeffs(Device->Hrtf.Handle, ev, az, spread, DryGain,
+                      voice->Chan[0].Direct.Hrtf.Target.Coeffs,
+                      voice->Chan[0].Direct.Hrtf.Target.Delay);
+
+        CalcDirectionCoeffs(dir, spread, coeffs);
+
+        for(i = 0;i < NumSends;i++)
+        {
+            if(!SendSlots[i])
+            {
+                ALuint j;
+                for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
+                    voice->Chan[0].Send[i].Gains.Target[j] = 0.0f;
+            }
+            else
+            {
+                const ALeffectslot *Slot = SendSlots[i];
+                ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs,
+                                      WetGain[i], voice->Chan[0].Send[i].Gains.Target);
+            }
+        }
+
+        voice->IsHrtf = AL_TRUE;
+    }
+    else
+    {
+        /* Non-HRTF rendering. */
+        ALfloat dir[3] = { 0.0f, 0.0f, -1.0f };
+        ALfloat radius = ATOMIC_LOAD(&props->Radius, almemory_order_relaxed);
+        ALfloat coeffs[MAX_AMBI_COEFFS];
+        ALfloat spread = 0.0f;
+
+        /* Get the localized direction, and compute panned gains. */
+        if(Distance > FLT_EPSILON)
+        {
+            dir[0] = -SourceToListener.v[0];
+            dir[1] = -SourceToListener.v[1];
+            dir[2] = -SourceToListener.v[2] * ZScale;
+        }
+        if(radius > Distance)
+            spread = F_TAU - Distance/radius*F_PI;
+        else if(Distance > FLT_EPSILON)
+            spread = asinf(radius / Distance) * 2.0f;
+
+        if(Device->Render_Mode == StereoPair)
+        {
+            /* Clamp X so it remains within 30 degrees of 0 or 180 degree azimuth. */
+            ALfloat x = -dir[0] * (0.5f * (cosf(spread*0.5f) + 1.0f));
+            x = clampf(x, -0.5f, 0.5f) + 0.5f;
+            voice->Chan[0].Direct.Gains.Target[0] = x * DryGain;
+            voice->Chan[0].Direct.Gains.Target[1] = (1.0f-x) * DryGain;
+            for(i = 2;i < MAX_OUTPUT_CHANNELS;i++)
+                voice->Chan[0].Direct.Gains.Target[i] = 0.0f;
+
+            CalcDirectionCoeffs(dir, spread, coeffs);
+        }
+        else
+        {
+            CalcDirectionCoeffs(dir, spread, coeffs);
+            ComputePanningGains(Device->Dry, coeffs, DryGain,
+                                voice->Chan[0].Direct.Gains.Target);
+        }
+
+        for(i = 0;i < NumSends;i++)
+        {
+            if(!SendSlots[i])
+            {
+                ALuint j;
+                for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
+                    voice->Chan[0].Send[i].Gains.Target[j] = 0.0f;
+            }
+            else
+            {
+                const ALeffectslot *Slot = SendSlots[i];
+                ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs,
+                                      WetGain[i], voice->Chan[0].Send[i].Gains.Target);
+            }
+        }
+
+        voice->IsHrtf = AL_FALSE;
+    }
+
+    {
+        ALfloat hfscale = ATOMIC_LOAD(&props->Direct.HFReference, almemory_order_relaxed) /
+                          Frequency;
+        ALfloat lfscale = ATOMIC_LOAD(&props->Direct.LFReference, almemory_order_relaxed) /
+                          Frequency;
+        DryGainHF = maxf(DryGainHF, 0.0001f);
+        DryGainLF = maxf(DryGainLF, 0.0001f);
+        voice->Chan[0].Direct.FilterType = AF_None;
+        if(DryGainHF != 1.0f) voice->Chan[0].Direct.FilterType |= AF_LowPass;
+        if(DryGainLF != 1.0f) voice->Chan[0].Direct.FilterType |= AF_HighPass;
+        ALfilterState_setParams(
+            &voice->Chan[0].Direct.LowPass, ALfilterType_HighShelf,
+            DryGainHF, hfscale, calc_rcpQ_from_slope(DryGainHF, 0.75f)
+        );
+        ALfilterState_setParams(
+            &voice->Chan[0].Direct.HighPass, ALfilterType_LowShelf,
+            DryGainLF, lfscale, calc_rcpQ_from_slope(DryGainLF, 0.75f)
+        );
+    }
+    for(i = 0;i < NumSends;i++)
+    {
+        ALfloat hfscale = ATOMIC_LOAD(&props->Send[i].HFReference, almemory_order_relaxed) /
+                          Frequency;
+        ALfloat lfscale = ATOMIC_LOAD(&props->Send[i].LFReference, almemory_order_relaxed) /
+                          Frequency;
+        WetGainHF[i] = maxf(WetGainHF[i], 0.0001f);
+        WetGainLF[i] = maxf(WetGainLF[i], 0.0001f);
+        voice->Chan[0].Send[i].FilterType = AF_None;
+        if(WetGainHF[i] != 1.0f) voice->Chan[0].Send[i].FilterType |= AF_LowPass;
+        if(WetGainLF[i] != 1.0f) voice->Chan[0].Send[i].FilterType |= AF_HighPass;
+        ALfilterState_setParams(
+            &voice->Chan[0].Send[i].LowPass, ALfilterType_HighShelf,
+            WetGainHF[i], hfscale, calc_rcpQ_from_slope(WetGainHF[i], 0.75f)
+        );
+        ALfilterState_setParams(
+            &voice->Chan[0].Send[i].HighPass, ALfilterType_LowShelf,
+            WetGainLF[i], lfscale, calc_rcpQ_from_slope(WetGainLF[i], 0.75f)
+        );
+    }
+}
+
+static void CalcSourceParams(ALvoice *voice, ALCcontext *context, ALboolean force)
+{
+    ALsource *source = voice->Source;
+    const ALbufferlistitem *BufferListItem;
+    struct ALsourceProps *first;
+    struct ALsourceProps *props;
+
+    props = ATOMIC_EXCHANGE(struct ALsourceProps*, &source->Update, NULL, almemory_order_acq_rel);
+    if(!props && !force) return;
+
+    if(props)
+    {
+        voice->Props = *props;
+
+        /* WARNING: A livelock is theoretically possible if another thread
+         * keeps changing the freelist head without giving this a chance to
+         * actually swap in the old container (practically impossible with this
+         * little code, but...).
+         */
+        first = ATOMIC_LOAD(&source->FreeList);
+        do {
+            ATOMIC_STORE(&props->next, first, almemory_order_relaxed);
+        } while(ATOMIC_COMPARE_EXCHANGE_WEAK(struct ALsourceProps*,
+                &source->FreeList, &first, props) == 0);
+    }
+
+    BufferListItem = ATOMIC_LOAD(&source->queue, almemory_order_relaxed);
+    while(BufferListItem != NULL)
+    {
+        const ALbuffer *buffer;
+        if((buffer=BufferListItem->buffer) != NULL)
+        {
+            if(buffer->FmtChannels == FmtMono)
+                CalcAttnSourceParams(voice, &voice->Props, buffer, context);
+            else
+                CalcNonAttnSourceParams(voice, &voice->Props, buffer, context);
+            break;
+        }
+        BufferListItem = BufferListItem->next;
+    }
+}
+
+
+static void UpdateContextSources(ALCcontext *ctx, ALeffectslot *slot)
+{
+    ALvoice *voice, *voice_end;
+    ALsource *source;
+
+    IncrementRef(&ctx->UpdateCount);
+    if(!ATOMIC_LOAD(&ctx->HoldUpdates))
+    {
+        ALboolean force = CalcListenerParams(ctx);
+        while(slot)
+        {
+            force |= CalcEffectSlotParams(slot, ctx->Device);
+            slot = ATOMIC_LOAD(&slot->next, almemory_order_relaxed);
+        }
+
+        voice = ctx->Voices;
+        voice_end = voice + ctx->VoiceCount;
+        for(;voice != voice_end;++voice)
+        {
+            if(!(source=voice->Source)) continue;
+            if(source->state != AL_PLAYING && source->state != AL_PAUSED)
+                voice->Source = NULL;
+            else
+                CalcSourceParams(voice, ctx, force);
+        }
+    }
+    IncrementRef(&ctx->UpdateCount);
+}
+
+
+/* Specialized function to clamp to [-1, +1] with only one branch. This also
+ * converts NaN to 0. */
+static inline ALfloat aluClampf(ALfloat val)
+{
+    if(fabsf(val) <= 1.0f) return val;
+    return (ALfloat)((0.0f < val) - (val < 0.0f));
+}
+
+static inline ALfloat aluF2F(ALfloat val)
+{ return val; }
+
+static inline ALint aluF2I(ALfloat val)
+{
+    /* Floats only have a 24-bit mantissa, so [-16777215, +16777215] is the max
+     * integer range normalized floats can be safely converted to.
+     */
+    return fastf2i(aluClampf(val)*16777215.0f)<<7;
+}
+static inline ALuint aluF2UI(ALfloat val)
+{ return aluF2I(val)+2147483648u; }
+
+static inline ALshort aluF2S(ALfloat val)
+{ return fastf2i(aluClampf(val)*32767.0f); }
+static inline ALushort aluF2US(ALfloat val)
+{ return aluF2S(val)+32768; }
+
+static inline ALbyte aluF2B(ALfloat val)
+{ return fastf2i(aluClampf(val)*127.0f); }
+static inline ALubyte aluF2UB(ALfloat val)
+{ return aluF2B(val)+128; }
+
+#define DECL_TEMPLATE(T, func)                                                \
+static void Write_##T(ALfloatBUFFERSIZE *InBuffer, ALvoid *OutBuffer,         \
+                      ALuint SamplesToDo, ALuint numchans)                    \
+{                                                                             \
+    ALuint i, j;                                                              \
+    for(j = 0;j < numchans;j++)                                               \
+    {                                                                         \
+        const ALfloat *in = InBuffer[j];                                      \
+        T *restrict out = (T*)OutBuffer + j;                                  \
+        for(i = 0;i < SamplesToDo;i++)                                        \
+            out[i*numchans] = func(in[i]);                                    \
+    }                                                                         \
+}
+
+DECL_TEMPLATE(ALfloat, aluF2F)
+DECL_TEMPLATE(ALuint, aluF2UI)
+DECL_TEMPLATE(ALint, aluF2I)
+DECL_TEMPLATE(ALushort, aluF2US)
+DECL_TEMPLATE(ALshort, aluF2S)
+DECL_TEMPLATE(ALubyte, aluF2UB)
+DECL_TEMPLATE(ALbyte, aluF2B)
+
+#undef DECL_TEMPLATE
+
+
+ALvoid aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size)
+{
+    ALuint SamplesToDo;
+    ALvoice *voice, *voice_end;
+    ALeffectslot *slot;
+    ALsource *source;
+    ALCcontext *ctx;
+    FPUCtl oldMode;
+    ALuint i, c;
+
+    SetMixerFPUMode(&oldMode);
+
+    while(size > 0)
+    {
+        SamplesToDo = minu(size, BUFFERSIZE);
+        for(c = 0;c < device->Dry.NumChannels;c++)
+            memset(device->Dry.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
+        if(device->Dry.Buffer != device->RealOut.Buffer)
+            for(c = 0;c < device->RealOut.NumChannels;c++)
+                memset(device->RealOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
+        if(device->Dry.Buffer != device->FOAOut.Buffer)
+            for(c = 0;c < device->FOAOut.NumChannels;c++)
+                memset(device->FOAOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
+
+        IncrementRef(&device->MixCount);
+        V0(device->Backend,lock)();
+
+        if((slot=device->DefaultSlot) != NULL)
+        {
+            CalcEffectSlotParams(device->DefaultSlot, device);
+            for(i = 0;i < slot->NumChannels;i++)
+                memset(slot->WetBuffer[i], 0, SamplesToDo*sizeof(ALfloat));
+        }
+
+        ctx = ATOMIC_LOAD(&device->ContextList);
+        while(ctx)
+        {
+            ALeffectslot *slotroot;
+
+            slotroot = ATOMIC_LOAD(&ctx->ActiveAuxSlotList);
+            UpdateContextSources(ctx, slotroot);
+
+            slot = slotroot;
+            while(slot)
+            {
+                for(i = 0;i < slot->NumChannels;i++)
+                    memset(slot->WetBuffer[i], 0, SamplesToDo*sizeof(ALfloat));
+                slot = ATOMIC_LOAD(&slot->next, almemory_order_relaxed);
+            }
+
+            /* source processing */
+            voice = ctx->Voices;
+            voice_end = voice + ctx->VoiceCount;
+            for(;voice != voice_end;++voice)
+            {
+                ALboolean IsVoiceInit = (voice->Step > 0);
+                source = voice->Source;
+                if(source && source->state == AL_PLAYING && IsVoiceInit)
+                    MixSource(voice, source, device, SamplesToDo);
+            }
+
+            /* effect slot processing */
+            slot = slotroot;
+            while(slot)
+            {
+                ALeffectState *state = slot->Params.EffectState;
+                V(state,process)(SamplesToDo, SAFE_CONST(ALfloatBUFFERSIZE*,slot->WetBuffer),
+                                 state->OutBuffer, state->OutChannels);
+                slot = ATOMIC_LOAD(&slot->next, almemory_order_relaxed);
+            }
+
+            ctx = ctx->next;
+        }
+
+        if(device->DefaultSlot != NULL)
+        {
+            const ALeffectslot *slot = device->DefaultSlot;
+            ALeffectState *state = slot->Params.EffectState;
+            V(state,process)(SamplesToDo, slot->WetBuffer, state->OutBuffer,
+                             state->OutChannels);
+        }
+
+        /* Increment the clock time. Every second's worth of samples is
+         * converted and added to clock base so that large sample counts don't
+         * overflow during conversion. This also guarantees an exact, stable
+         * conversion. */
+        device->SamplesDone += SamplesToDo;
+        device->ClockBase += (device->SamplesDone/device->Frequency) * DEVICE_CLOCK_RES;
+        device->SamplesDone %= device->Frequency;
+        V0(device->Backend,unlock)();
+        IncrementRef(&device->MixCount);
+
+        if(device->Hrtf.Handle)
+        {
+            int lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
+            int ridx = GetChannelIdxByName(device->RealOut, FrontRight);
+            if(lidx != -1 && ridx != -1)
+            {
+                HrtfDirectMixerFunc HrtfMix = SelectHrtfMixer();
+                ALuint irsize = device->Hrtf.IrSize;
+                for(c = 0;c < device->Dry.NumChannels;c++)
+                {
+                    HrtfMix(device->RealOut.Buffer, lidx, ridx,
+                        device->Dry.Buffer[c], device->Hrtf.Offset, irsize,
+                        device->Hrtf.Coeffs[c], device->Hrtf.Values[c],
+                        SamplesToDo
+                    );
+                }
+                device->Hrtf.Offset += SamplesToDo;
+            }
+        }
+        else if(device->AmbiDecoder)
+        {
+            if(device->Dry.Buffer != device->FOAOut.Buffer)
+                bformatdec_upSample(device->AmbiDecoder,
+                    device->Dry.Buffer, SAFE_CONST(ALfloatBUFFERSIZE*,device->FOAOut.Buffer),
+                    device->FOAOut.NumChannels, SamplesToDo
+                );
+            bformatdec_process(device->AmbiDecoder,
+                device->RealOut.Buffer, device->RealOut.NumChannels,
+                SAFE_CONST(ALfloatBUFFERSIZE*,device->Dry.Buffer), SamplesToDo
+            );
+        }
+        else if(device->AmbiUp)
+        {
+            ambiup_process(device->AmbiUp,
+                device->RealOut.Buffer, device->RealOut.NumChannels,
+                SAFE_CONST(ALfloatBUFFERSIZE*,device->FOAOut.Buffer), SamplesToDo
+            );
+        }
+        else if(device->Uhj_Encoder)
+        {
+            int lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
+            int ridx = GetChannelIdxByName(device->RealOut, FrontRight);
+            if(lidx != -1 && ridx != -1)
+            {
+                /* Encode to stereo-compatible 2-channel UHJ output. */
+                EncodeUhj2(device->Uhj_Encoder,
+                    device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
+                    device->Dry.Buffer, SamplesToDo
+                );
+            }
+        }
+        else if(device->Bs2b)
+        {
+            int lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
+            int ridx = GetChannelIdxByName(device->RealOut, FrontRight);
+            if(lidx != -1 && ridx != -1)
+            {
+                /* Apply binaural/crossfeed filter */
+                bs2b_cross_feed(device->Bs2b, device->RealOut.Buffer[lidx],
+                                device->RealOut.Buffer[ridx], SamplesToDo);
+            }
+        }
+
+        if(buffer)
+        {
+            ALfloat (*OutBuffer)[BUFFERSIZE] = device->RealOut.Buffer;
+            ALuint OutChannels = device->RealOut.NumChannels;
+
+#define WRITE(T, a, b, c, d) do {               \
+    Write_##T((a), (b), (c), (d));              \
+    buffer = (T*)buffer + (c)*(d);              \
+} while(0)
+            switch(device->FmtType)
+            {
+                case DevFmtByte:
+                    WRITE(ALbyte, OutBuffer, buffer, SamplesToDo, OutChannels);
+                    break;
+                case DevFmtUByte:
+                    WRITE(ALubyte, OutBuffer, buffer, SamplesToDo, OutChannels);
+                    break;
+                case DevFmtShort:
+                    WRITE(ALshort, OutBuffer, buffer, SamplesToDo, OutChannels);
+                    break;
+                case DevFmtUShort:
+                    WRITE(ALushort, OutBuffer, buffer, SamplesToDo, OutChannels);
+                    break;
+                case DevFmtInt:
+                    WRITE(ALint, OutBuffer, buffer, SamplesToDo, OutChannels);
+                    break;
+                case DevFmtUInt:
+                    WRITE(ALuint, OutBuffer, buffer, SamplesToDo, OutChannels);
+                    break;
+                case DevFmtFloat:
+                    WRITE(ALfloat, OutBuffer, buffer, SamplesToDo, OutChannels);
+                    break;
+            }
+#undef WRITE
+        }
+
+        size -= SamplesToDo;
+    }
+
+    RestoreFPUMode(&oldMode);
+}
+
+
+ALvoid aluHandleDisconnect(ALCdevice *device)
+{
+    ALCcontext *Context;
+
+    device->Connected = ALC_FALSE;
+
+    Context = ATOMIC_LOAD(&device->ContextList);
+    while(Context)
+    {
+        ALvoice *voice, *voice_end;
+
+        voice = Context->Voices;
+        voice_end = voice + Context->VoiceCount;
+        while(voice != voice_end)
+        {
+            ALsource *source = voice->Source;
+            voice->Source = NULL;
+
+            if(source && source->state == AL_PLAYING)
+            {
+                source->state = AL_STOPPED;
+                ATOMIC_STORE(&source->current_buffer, NULL);
+                ATOMIC_STORE(&source->position, 0);
+                ATOMIC_STORE(&source->position_fraction, 0);
+            }
+
+            voice++;
+        }
+        Context->VoiceCount = 0;
+
+        Context = Context->next;
+    }
+}

+ 598 - 0
Engine/lib/openal-soft/Alc/alcConfig.c

@@ -0,0 +1,598 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 1999-2007 by authors.
+ * This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the
+ *  Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#ifdef _WIN32
+#ifdef __MINGW32__
+#define _WIN32_IE 0x501
+#else
+#define _WIN32_IE 0x400
+#endif
+#endif
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#ifdef _WIN32_IE
+#include <windows.h>
+#include <shlobj.h>
+#endif
+
+#include "alMain.h"
+#include "compat.h"
+#include "bool.h"
+
+
+typedef struct ConfigEntry {
+    char *key;
+    char *value;
+} ConfigEntry;
+
+typedef struct ConfigBlock {
+    ConfigEntry *entries;
+    unsigned int entryCount;
+} ConfigBlock;
+static ConfigBlock cfgBlock;
+
+
+static char *lstrip(char *line)
+{
+    while(isspace(line[0]))
+        line++;
+    return line;
+}
+
+static char *rstrip(char *line)
+{
+    size_t len = strlen(line);
+    while(len > 0 && isspace(line[len-1]))
+        len--;
+    line[len] = 0;
+    return line;
+}
+
+static int readline(FILE *f, char **output, size_t *maxlen)
+{
+    size_t len = 0;
+    int c;
+
+    while((c=fgetc(f)) != EOF && (c == '\r' || c == '\n'))
+        ;
+    if(c == EOF)
+        return 0;
+
+    do {
+        if(len+1 >= *maxlen)
+        {
+            void *temp = NULL;
+            size_t newmax;
+
+            newmax = (*maxlen ? (*maxlen)<<1 : 32);
+            if(newmax > *maxlen)
+                temp = realloc(*output, newmax);
+            if(!temp)
+            {
+                ERR("Failed to realloc "SZFMT" bytes from "SZFMT"!\n", newmax, *maxlen);
+                return 0;
+            }
+
+            *output = temp;
+            *maxlen = newmax;
+        }
+        (*output)[len++] = c;
+        (*output)[len] = '\0';
+    } while((c=fgetc(f)) != EOF && c != '\r' && c != '\n');
+
+    return 1;
+}
+
+
+static char *expdup(const char *str)
+{
+    char *output = NULL;
+    size_t maxlen = 0;
+    size_t len = 0;
+
+    while(*str != '\0')
+    {
+        const char *addstr;
+        size_t addstrlen;
+        size_t i;
+
+        if(str[0] != '$')
+        {
+            const char *next = strchr(str, '$');
+            addstr = str;
+            addstrlen = next ? (size_t)(next-str) : strlen(str);
+
+            str += addstrlen;
+        }
+        else
+        {
+            str++;
+            if(*str == '$')
+            {
+                const char *next = strchr(str+1, '$');
+                addstr = str;
+                addstrlen = next ? (size_t)(next-str) : strlen(str);
+
+                str += addstrlen;
+            }
+            else
+            {
+                bool hasbraces;
+                char envname[1024];
+                size_t k = 0;
+
+                hasbraces = (*str == '{');
+                if(hasbraces) str++;
+
+                while((isalnum(*str) || *str == '_') && k < sizeof(envname)-1)
+                    envname[k++] = *(str++);
+                envname[k++] = '\0';
+
+                if(hasbraces && *str != '}')
+                    continue;
+
+                if(hasbraces) str++;
+                if((addstr=getenv(envname)) == NULL)
+                    continue;
+                addstrlen = strlen(addstr);
+            }
+        }
+        if(addstrlen == 0)
+            continue;
+
+        if(addstrlen >= maxlen-len)
+        {
+            void *temp = NULL;
+            size_t newmax;
+
+            newmax = len+addstrlen+1;
+            if(newmax > maxlen)
+                temp = realloc(output, newmax);
+            if(!temp)
+            {
+                ERR("Failed to realloc "SZFMT" bytes from "SZFMT"!\n", newmax, maxlen);
+                return output;
+            }
+
+            output = temp;
+            maxlen = newmax;
+        }
+
+        for(i = 0;i < addstrlen;i++)
+            output[len++] = addstr[i];
+        output[len] = '\0';
+    }
+
+    return output ? output : calloc(1, 1);
+}
+
+
+static void LoadConfigFromFile(FILE *f)
+{
+    char curSection[128] = "";
+    char *buffer = NULL;
+    size_t maxlen = 0;
+    ConfigEntry *ent;
+
+    while(readline(f, &buffer, &maxlen))
+    {
+        char *line, *comment;
+        char key[256] = "";
+        char value[256] = "";
+
+        line = rstrip(lstrip(buffer));
+        if(!line[0]) continue;
+
+        if(line[0] == '[')
+        {
+            char *section = line+1;
+            char *endsection;
+
+            endsection = strchr(section, ']');
+            if(!endsection || section == endsection)
+            {
+                ERR("config parse error: bad line \"%s\"\n", line);
+                continue;
+            }
+            if(endsection[1] != 0)
+            {
+                char *end = endsection+1;
+                while(isspace(*end))
+                    ++end;
+                if(*end != 0 && *end != '#')
+                {
+                    ERR("config parse error: bad line \"%s\"\n", line);
+                    continue;
+                }
+            }
+            *endsection = 0;
+
+            if(strcasecmp(section, "general") == 0)
+                curSection[0] = 0;
+            else
+            {
+                strncpy(curSection, section, sizeof(curSection)-1);
+                curSection[sizeof(curSection)-1] = 0;
+            }
+
+            continue;
+        }
+
+        comment = strchr(line, '#');
+        if(comment) *(comment++) = 0;
+        if(!line[0]) continue;
+
+        if(sscanf(line, "%255[^=] = \"%255[^\"]\"", key, value) == 2 ||
+           sscanf(line, "%255[^=] = '%255[^\']'", key, value) == 2 ||
+           sscanf(line, "%255[^=] = %255[^\n]", key, value) == 2)
+        {
+            /* sscanf doesn't handle '' or "" as empty values, so clip it
+             * manually. */
+            if(strcmp(value, "\"\"") == 0 || strcmp(value, "''") == 0)
+                value[0] = 0;
+        }
+        else if(sscanf(line, "%255[^=] %255[=]", key, value) == 2)
+        {
+            /* Special case for 'key =' */
+            value[0] = 0;
+        }
+        else
+        {
+            ERR("config parse error: malformed option line: \"%s\"\n\n", line);
+            continue;
+        }
+        rstrip(key);
+
+        if(curSection[0] != 0)
+        {
+            size_t len = strlen(curSection);
+            memmove(&key[len+1], key, sizeof(key)-1-len);
+            key[len] = '/';
+            memcpy(key, curSection, len);
+        }
+
+        /* Check if we already have this option set */
+        ent = cfgBlock.entries;
+        while((unsigned int)(ent-cfgBlock.entries) < cfgBlock.entryCount)
+        {
+            if(strcasecmp(ent->key, key) == 0)
+                break;
+            ent++;
+        }
+
+        if((unsigned int)(ent-cfgBlock.entries) >= cfgBlock.entryCount)
+        {
+            /* Allocate a new option entry */
+            ent = realloc(cfgBlock.entries, (cfgBlock.entryCount+1)*sizeof(ConfigEntry));
+            if(!ent)
+            {
+                 ERR("config parse error: error reallocating config entries\n");
+                 continue;
+            }
+            cfgBlock.entries = ent;
+            ent = cfgBlock.entries + cfgBlock.entryCount;
+            cfgBlock.entryCount++;
+
+            ent->key = strdup(key);
+            ent->value = NULL;
+        }
+
+        free(ent->value);
+        ent->value = expdup(value);
+
+        TRACE("found '%s' = '%s'\n", ent->key, ent->value);
+    }
+
+    free(buffer);
+}
+
+#ifdef _WIN32
+void ReadALConfig(void)
+{
+    WCHAR buffer[PATH_MAX];
+    const WCHAR *str;
+    al_string ppath;
+    FILE *f;
+
+    if(SHGetSpecialFolderPathW(NULL, buffer, CSIDL_APPDATA, FALSE) != FALSE)
+    {
+        al_string filepath = AL_STRING_INIT_STATIC();
+        al_string_copy_wcstr(&filepath, buffer);
+        al_string_append_cstr(&filepath, "\\alsoft.ini");
+
+        TRACE("Loading config %s...\n", al_string_get_cstr(filepath));
+        f = al_fopen(al_string_get_cstr(filepath), "rt");
+        if(f)
+        {
+            LoadConfigFromFile(f);
+            fclose(f);
+        }
+        al_string_deinit(&filepath);
+    }
+
+    ppath = GetProcPath();
+    if(!al_string_empty(ppath))
+    {
+        al_string_append_cstr(&ppath, "\\alsoft.ini");
+        TRACE("Loading config %s...\n", al_string_get_cstr(ppath));
+        f = al_fopen(al_string_get_cstr(ppath), "r");
+        if(f)
+        {
+            LoadConfigFromFile(f);
+            fclose(f);
+        }
+    }
+
+    if((str=_wgetenv(L"ALSOFT_CONF")) != NULL && *str)
+    {
+        al_string filepath = AL_STRING_INIT_STATIC();
+        al_string_copy_wcstr(&filepath, str);
+
+        TRACE("Loading config %s...\n", al_string_get_cstr(filepath));
+        f = al_fopen(al_string_get_cstr(filepath), "rt");
+        if(f)
+        {
+            LoadConfigFromFile(f);
+            fclose(f);
+        }
+        al_string_deinit(&filepath);
+    }
+
+    al_string_deinit(&ppath);
+}
+#else
+void ReadALConfig(void)
+{
+    char buffer[PATH_MAX];
+    const char *str;
+    al_string ppath;
+    FILE *f;
+
+    str = "/etc/openal/alsoft.conf";
+
+    TRACE("Loading config %s...\n", str);
+    f = al_fopen(str, "r");
+    if(f)
+    {
+        LoadConfigFromFile(f);
+        fclose(f);
+    }
+
+    if(!(str=getenv("XDG_CONFIG_DIRS")) || str[0] == 0)
+        str = "/etc/xdg";
+    strncpy(buffer, str, sizeof(buffer)-1);
+    buffer[sizeof(buffer)-1] = 0;
+    /* Go through the list in reverse, since "the order of base directories
+     * denotes their importance; the first directory listed is the most
+     * important". Ergo, we need to load the settings from the later dirs
+     * first so that the settings in the earlier dirs override them.
+     */
+    while(1)
+    {
+        char *next = strrchr(buffer, ':');
+        if(next) *(next++) = 0;
+        else next = buffer;
+
+        if(next[0] != '/')
+            WARN("Ignoring XDG config dir: %s\n", next);
+        else
+        {
+            size_t len = strlen(next);
+            strncpy(next+len, "/alsoft.conf", buffer+sizeof(buffer)-next-len);
+            buffer[sizeof(buffer)-1] = 0;
+
+            TRACE("Loading config %s...\n", next);
+            f = al_fopen(next, "r");
+            if(f)
+            {
+                LoadConfigFromFile(f);
+                fclose(f);
+            }
+        }
+        if(next == buffer)
+            break;
+    }
+
+    if((str=getenv("HOME")) != NULL && *str)
+    {
+        snprintf(buffer, sizeof(buffer), "%s/.alsoftrc", str);
+
+        TRACE("Loading config %s...\n", buffer);
+        f = al_fopen(buffer, "r");
+        if(f)
+        {
+            LoadConfigFromFile(f);
+            fclose(f);
+        }
+    }
+
+    if((str=getenv("XDG_CONFIG_HOME")) != NULL && str[0] != 0)
+        snprintf(buffer, sizeof(buffer), "%s/%s", str, "alsoft.conf");
+    else
+    {
+        buffer[0] = 0;
+        if((str=getenv("HOME")) != NULL && str[0] != 0)
+            snprintf(buffer, sizeof(buffer), "%s/.config/%s", str, "alsoft.conf");
+    }
+    if(buffer[0] != 0)
+    {
+        TRACE("Loading config %s...\n", buffer);
+        f = al_fopen(buffer, "r");
+        if(f)
+        {
+            LoadConfigFromFile(f);
+            fclose(f);
+        }
+    }
+
+    ppath = GetProcPath();
+    if(!al_string_empty(ppath))
+    {
+        al_string_append_cstr(&ppath, "/alsoft.conf");
+        TRACE("Loading config %s...\n", al_string_get_cstr(ppath));
+        f = al_fopen(al_string_get_cstr(ppath), "r");
+        if(f)
+        {
+            LoadConfigFromFile(f);
+            fclose(f);
+        }
+    }
+
+    if((str=getenv("ALSOFT_CONF")) != NULL && *str)
+    {
+        TRACE("Loading config %s...\n", str);
+        f = al_fopen(str, "r");
+        if(f)
+        {
+            LoadConfigFromFile(f);
+            fclose(f);
+        }
+    }
+
+    al_string_deinit(&ppath);
+}
+#endif
+
+void FreeALConfig(void)
+{
+    unsigned int i;
+
+    for(i = 0;i < cfgBlock.entryCount;i++)
+    {
+        free(cfgBlock.entries[i].key);
+        free(cfgBlock.entries[i].value);
+    }
+    free(cfgBlock.entries);
+}
+
+const char *GetConfigValue(const char *devName, const char *blockName, const char *keyName, const char *def)
+{
+    unsigned int i;
+    char key[256];
+
+    if(!keyName)
+        return def;
+
+    if(blockName && strcasecmp(blockName, "general") != 0)
+    {
+        if(devName)
+            snprintf(key, sizeof(key), "%s/%s/%s", blockName, devName, keyName);
+        else
+            snprintf(key, sizeof(key), "%s/%s", blockName, keyName);
+    }
+    else
+    {
+        if(devName)
+            snprintf(key, sizeof(key), "%s/%s", devName, keyName);
+        else
+        {
+            strncpy(key, keyName, sizeof(key)-1);
+            key[sizeof(key)-1] = 0;
+        }
+    }
+
+    for(i = 0;i < cfgBlock.entryCount;i++)
+    {
+        if(strcmp(cfgBlock.entries[i].key, key) == 0)
+        {
+            TRACE("Found %s = \"%s\"\n", key, cfgBlock.entries[i].value);
+            if(cfgBlock.entries[i].value[0])
+                return cfgBlock.entries[i].value;
+            return def;
+        }
+    }
+
+    if(!devName)
+    {
+        TRACE("Key %s not found\n", key);
+        return def;
+    }
+    return GetConfigValue(NULL, blockName, keyName, def);
+}
+
+int ConfigValueExists(const char *devName, const char *blockName, const char *keyName)
+{
+    const char *val = GetConfigValue(devName, blockName, keyName, "");
+    return !!val[0];
+}
+
+int ConfigValueStr(const char *devName, const char *blockName, const char *keyName, const char **ret)
+{
+    const char *val = GetConfigValue(devName, blockName, keyName, "");
+    if(!val[0]) return 0;
+
+    *ret = val;
+    return 1;
+}
+
+int ConfigValueInt(const char *devName, const char *blockName, const char *keyName, int *ret)
+{
+    const char *val = GetConfigValue(devName, blockName, keyName, "");
+    if(!val[0]) return 0;
+
+    *ret = strtol(val, NULL, 0);
+    return 1;
+}
+
+int ConfigValueUInt(const char *devName, const char *blockName, const char *keyName, unsigned int *ret)
+{
+    const char *val = GetConfigValue(devName, blockName, keyName, "");
+    if(!val[0]) return 0;
+
+    *ret = strtoul(val, NULL, 0);
+    return 1;
+}
+
+int ConfigValueFloat(const char *devName, const char *blockName, const char *keyName, float *ret)
+{
+    const char *val = GetConfigValue(devName, blockName, keyName, "");
+    if(!val[0]) return 0;
+
+#ifdef HAVE_STRTOF
+    *ret = strtof(val, NULL);
+#else
+    *ret = (float)strtod(val, NULL);
+#endif
+    return 1;
+}
+
+int ConfigValueBool(const char *devName, const char *blockName, const char *keyName, int *ret)
+{
+    const char *val = GetConfigValue(devName, blockName, keyName, "");
+    if(!val[0]) return 0;
+
+    *ret = (strcasecmp(val, "true") == 0 || strcasecmp(val, "yes") == 0 ||
+            strcasecmp(val, "on") == 0 || atoi(val) != 0);
+    return 1;
+}
+
+int GetConfigValueBool(const char *devName, const char *blockName, const char *keyName, int def)
+{
+    const char *val = GetConfigValue(devName, blockName, keyName, "");
+
+    if(!val[0]) return !!def;
+    return (strcasecmp(val, "true") == 0 || strcasecmp(val, "yes") == 0 ||
+            strcasecmp(val, "on") == 0 || atoi(val) != 0);
+}

+ 301 - 0
Engine/lib/openal-soft/Alc/alcRing.c

@@ -0,0 +1,301 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 1999-2007 by authors.
+ * This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the
+ *  Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "alMain.h"
+#include "threads.h"
+#include "almalloc.h"
+#include "compat.h"
+
+
+/* NOTE: This lockless ringbuffer implementation is copied from JACK, extended
+ * to include an element size. Consequently, parameters and return values for a
+ * size or count is in 'elements', not bytes. Additionally, it only supports
+ * single-consumer/single-provider operation. */
+struct ll_ringbuffer {
+    volatile size_t write_ptr;
+    volatile size_t read_ptr;
+    size_t size;
+    size_t size_mask;
+    size_t elem_size;
+    int mlocked;
+
+    alignas(16) char buf[];
+};
+
+/* Create a new ringbuffer to hold at least `sz' elements of `elem_sz' bytes.
+ * The number of elements is rounded up to the next power of two. */
+ll_ringbuffer_t *ll_ringbuffer_create(size_t sz, size_t elem_sz)
+{
+    ll_ringbuffer_t *rb;
+    ALuint power_of_two;
+
+    power_of_two = NextPowerOf2(sz);
+    if(power_of_two < sz)
+        return NULL;
+
+    rb = al_malloc(16, sizeof(*rb) + power_of_two*elem_sz);
+    if(!rb) return NULL;
+
+    rb->size = power_of_two;
+    rb->size_mask = rb->size - 1;
+    rb->elem_size = elem_sz;
+    rb->write_ptr = 0;
+    rb->read_ptr = 0;
+    rb->mlocked = 0;
+    return rb;
+}
+
+/* Free all data associated with the ringbuffer `rb'. */
+void ll_ringbuffer_free(ll_ringbuffer_t *rb)
+{
+    if(rb)
+    {
+#ifdef USE_MLOCK
+        if(rb->mlocked)
+            munlock(rb, sizeof(*rb) + rb->size*rb->elem_size);
+#endif /* USE_MLOCK */
+        al_free(rb);
+    }
+}
+
+/* Lock the data block of `rb' using the system call 'mlock'. */
+int ll_ringbuffer_mlock(ll_ringbuffer_t *rb)
+{
+#ifdef USE_MLOCK
+    if(!rb->locked && mlock(rb, sizeof(*rb) + rb->size*rb->elem_size))
+        return -1;
+#endif /* USE_MLOCK */
+    rb->mlocked = 1;
+    return 0;
+}
+
+/* Reset the read and write pointers to zero. This is not thread safe. */
+void ll_ringbuffer_reset(ll_ringbuffer_t *rb)
+{
+    rb->read_ptr = 0;
+    rb->write_ptr = 0;
+    memset(rb->buf, 0, rb->size*rb->elem_size);
+}
+
+/* Return the number of elements available for reading. This is the number of
+ * elements in front of the read pointer and behind the write pointer. */
+size_t ll_ringbuffer_read_space(const ll_ringbuffer_t *rb)
+{
+    size_t w = rb->write_ptr;
+    size_t r = rb->read_ptr;
+    return (rb->size+w-r) & rb->size_mask;
+}
+/* Return the number of elements available for writing. This is the number of
+ * elements in front of the write pointer and behind the read pointer. */
+size_t ll_ringbuffer_write_space(const ll_ringbuffer_t *rb)
+{
+    size_t w = rb->write_ptr;
+    size_t r = rb->read_ptr;
+    return (rb->size+r-w-1) & rb->size_mask;
+}
+
+/* The copying data reader. Copy at most `cnt' elements from `rb' to `dest'.
+ * Returns the actual number of elements copied. */
+size_t ll_ringbuffer_read(ll_ringbuffer_t *rb, char *dest, size_t cnt)
+{
+    size_t free_cnt;
+    size_t cnt2;
+    size_t to_read;
+    size_t n1, n2;
+
+    free_cnt = ll_ringbuffer_read_space(rb);
+    if(free_cnt == 0) return 0;
+
+    to_read = (cnt > free_cnt) ? free_cnt : cnt;
+    cnt2 = rb->read_ptr + to_read;
+    if(cnt2 > rb->size)
+    {
+        n1 = rb->size - rb->read_ptr;
+        n2 = cnt2 & rb->size_mask;
+    }
+    else
+    {
+        n1 = to_read;
+        n2 = 0;
+    }
+
+    memcpy(dest, &(rb->buf[rb->read_ptr*rb->elem_size]), n1*rb->elem_size);
+    rb->read_ptr = (rb->read_ptr + n1) & rb->size_mask;
+    if(n2)
+    {
+        memcpy(dest + n1*rb->elem_size, &(rb->buf[rb->read_ptr*rb->elem_size]), n2*rb->elem_size);
+        rb->read_ptr = (rb->read_ptr + n2) & rb->size_mask;
+    }
+    return to_read;
+}
+
+/* The copying data reader w/o read pointer advance. Copy at most `cnt'
+ * elements from `rb' to `dest'. Returns the actual number of elements copied.
+ */
+size_t ll_ringbuffer_peek(ll_ringbuffer_t *rb, char *dest, size_t cnt)
+{
+    size_t free_cnt;
+    size_t cnt2;
+    size_t to_read;
+    size_t n1, n2;
+    size_t tmp_read_ptr;
+
+    tmp_read_ptr = rb->read_ptr;
+    free_cnt = ll_ringbuffer_read_space(rb);
+    if(free_cnt == 0) return 0;
+
+    to_read = (cnt > free_cnt) ? free_cnt : cnt;
+    cnt2 = tmp_read_ptr + to_read;
+    if(cnt2 > rb->size)
+    {
+        n1 = rb->size - tmp_read_ptr;
+        n2 = cnt2 & rb->size_mask;
+    }
+    else
+    {
+        n1 = to_read;
+        n2 = 0;
+    }
+
+    memcpy(dest, &(rb->buf[tmp_read_ptr*rb->elem_size]), n1*rb->elem_size);
+    tmp_read_ptr = (tmp_read_ptr + n1) & rb->size_mask;
+    if(n2)
+        memcpy(dest + n1*rb->elem_size, &(rb->buf[tmp_read_ptr*rb->elem_size]), n2*rb->elem_size);
+    return to_read;
+}
+
+/* The copying data writer. Copy at most `cnt' elements to `rb' from `src'.
+ * Returns the actual number of elements copied. */
+size_t ll_ringbuffer_write(ll_ringbuffer_t *rb, const char *src, size_t cnt)
+{
+    size_t free_cnt;
+    size_t cnt2;
+    size_t to_write;
+    size_t n1, n2;
+
+    free_cnt = ll_ringbuffer_write_space(rb);
+    if(free_cnt == 0) return 0;
+
+    to_write = (cnt > free_cnt) ? free_cnt : cnt;
+    cnt2 = rb->write_ptr + to_write;
+    if(cnt2 > rb->size)
+    {
+        n1 = rb->size - rb->write_ptr;
+        n2 = cnt2 & rb->size_mask;
+    }
+    else
+    {
+        n1 = to_write;
+        n2 = 0;
+    }
+
+    memcpy(&(rb->buf[rb->write_ptr*rb->elem_size]), src, n1*rb->elem_size);
+    rb->write_ptr = (rb->write_ptr + n1) & rb->size_mask;
+    if(n2)
+    {
+        memcpy(&(rb->buf[rb->write_ptr*rb->elem_size]), src + n1*rb->elem_size, n2*rb->elem_size);
+        rb->write_ptr = (rb->write_ptr + n2) & rb->size_mask;
+    }
+    return to_write;
+}
+
+/* Advance the read pointer `cnt' places. */
+void ll_ringbuffer_read_advance(ll_ringbuffer_t *rb, size_t cnt)
+{
+    size_t tmp = (rb->read_ptr + cnt) & rb->size_mask;
+    rb->read_ptr = tmp;
+}
+
+/* Advance the write pointer `cnt' places. */
+void ll_ringbuffer_write_advance(ll_ringbuffer_t *rb, size_t cnt)
+{
+    size_t tmp = (rb->write_ptr + cnt) & rb->size_mask;
+    rb->write_ptr = tmp;
+}
+
+/* The non-copying data reader. `vec' is an array of two places. Set the values
+ * at `vec' to hold the current readable data at `rb'. If the readable data is
+ * in one segment the second segment has zero length. */
+void ll_ringbuffer_get_read_vector(const ll_ringbuffer_t *rb, ll_ringbuffer_data_t * vec)
+{
+    size_t free_cnt;
+    size_t cnt2;
+    size_t w, r;
+
+    w = rb->write_ptr;
+    r = rb->read_ptr;
+    free_cnt = (rb->size+w-r) & rb->size_mask;
+
+    cnt2 = r + free_cnt;
+    if(cnt2 > rb->size)
+    {
+        /* Two part vector: the rest of the buffer after the current write ptr,
+         * plus some from the start of the buffer. */
+        vec[0].buf = (char*)&(rb->buf[r*rb->elem_size]);
+        vec[0].len = rb->size - r;
+        vec[1].buf = (char*)rb->buf;
+        vec[1].len = cnt2 & rb->size_mask;
+    }
+    else
+    {
+        /* Single part vector: just the rest of the buffer */
+        vec[0].buf = (char*)&(rb->buf[r*rb->elem_size]);
+        vec[0].len = free_cnt;
+        vec[1].buf = NULL;
+        vec[1].len = 0;
+    }
+}
+
+/* The non-copying data writer. `vec' is an array of two places. Set the values
+ * at `vec' to hold the current writeable data at `rb'. If the writeable data
+ * is in one segment the second segment has zero length. */
+void ll_ringbuffer_get_write_vector(const ll_ringbuffer_t *rb, ll_ringbuffer_data_t *vec)
+{
+    size_t free_cnt;
+    size_t cnt2;
+    size_t w, r;
+
+    w = rb->write_ptr;
+    r = rb->read_ptr;
+    free_cnt = (rb->size+r-w-1) & rb->size_mask;
+
+    cnt2 = w + free_cnt;
+    if(cnt2 > rb->size)
+    {
+        /* Two part vector: the rest of the buffer after the current write ptr,
+         * plus some from the start of the buffer. */
+        vec[0].buf = (char*)&(rb->buf[w*rb->elem_size]);
+        vec[0].len = rb->size - w;
+        vec[1].buf = (char*)rb->buf;
+        vec[1].len = cnt2 & rb->size_mask;
+    }
+    else
+    {
+        vec[0].buf = (char*)&(rb->buf[w*rb->elem_size]);
+        vec[0].len = free_cnt;
+        vec[1].buf = NULL;
+        vec[1].len = 0;
+    }
+}

+ 49 - 0
Engine/lib/openal-soft/Alc/alstring.h

@@ -0,0 +1,49 @@
+#ifndef ALSTRING_H
+#define ALSTRING_H
+
+#include <string.h>
+
+#include "vector.h"
+
+
+typedef char al_string_char_type;
+TYPEDEF_VECTOR(al_string_char_type, al_string)
+TYPEDEF_VECTOR(al_string, vector_al_string)
+
+inline void al_string_deinit(al_string *str)
+{ VECTOR_DEINIT(*str); }
+#define AL_STRING_INIT(_x)       do { (_x) = (al_string)NULL; } while(0)
+#define AL_STRING_INIT_STATIC()  ((al_string)NULL)
+#define AL_STRING_DEINIT(_x)     al_string_deinit(&(_x))
+
+inline size_t al_string_length(const_al_string str)
+{ return VECTOR_SIZE(str); }
+
+inline ALboolean al_string_empty(const_al_string str)
+{ return al_string_length(str) == 0; }
+
+inline const al_string_char_type *al_string_get_cstr(const_al_string str)
+{ return str ? &VECTOR_FRONT(str) : ""; }
+
+void al_string_clear(al_string *str);
+
+int al_string_cmp(const_al_string str1, const_al_string str2);
+int al_string_cmp_cstr(const_al_string str1, const al_string_char_type *str2);
+
+void al_string_copy(al_string *str, const_al_string from);
+void al_string_copy_cstr(al_string *str, const al_string_char_type *from);
+void al_string_copy_range(al_string *str, const al_string_char_type *from, const al_string_char_type *to);
+
+void al_string_append_char(al_string *str, const al_string_char_type c);
+void al_string_append_cstr(al_string *str, const al_string_char_type *from);
+void al_string_append_range(al_string *str, const al_string_char_type *from, const al_string_char_type *to);
+
+#ifdef _WIN32
+#include <wchar.h>
+/* Windows-only methods to deal with WideChar strings. */
+void al_string_copy_wcstr(al_string *str, const wchar_t *from);
+void al_string_append_wcstr(al_string *str, const wchar_t *from);
+void al_string_append_wrange(al_string *str, const wchar_t *from, const wchar_t *to);
+#endif
+
+#endif /* ALSTRING_H */

+ 557 - 0
Engine/lib/openal-soft/Alc/ambdec.c

@@ -0,0 +1,557 @@
+
+#include "config.h"
+
+#include "ambdec.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "compat.h"
+
+
+static char *lstrip(char *line)
+{
+    while(isspace(line[0]))
+        line++;
+    return line;
+}
+
+static char *rstrip(char *line)
+{
+    size_t len = strlen(line);
+    while(len > 0 && isspace(line[len-1]))
+        len--;
+    line[len] = 0;
+    return line;
+}
+
+static int readline(FILE *f, char **output, size_t *maxlen)
+{
+    size_t len = 0;
+    int c;
+
+    while((c=fgetc(f)) != EOF && (c == '\r' || c == '\n'))
+        ;
+    if(c == EOF)
+        return 0;
+
+    do {
+        if(len+1 >= *maxlen)
+        {
+            void *temp = NULL;
+            size_t newmax;
+
+            newmax = (*maxlen ? (*maxlen)<<1 : 32);
+            if(newmax > *maxlen)
+                temp = realloc(*output, newmax);
+            if(!temp)
+            {
+                ERR("Failed to realloc "SZFMT" bytes from "SZFMT"!\n", newmax, *maxlen);
+                return 0;
+            }
+
+            *output = temp;
+            *maxlen = newmax;
+        }
+        (*output)[len++] = c;
+        (*output)[len] = '\0';
+    } while((c=fgetc(f)) != EOF && c != '\r' && c != '\n');
+
+    return 1;
+}
+
+
+/* Custom strtok_r, since we can't rely on it existing. */
+static char *my_strtok_r(char *str, const char *delim, char **saveptr)
+{
+    /* Sanity check and update internal pointer. */
+    if(!saveptr || !delim) return NULL;
+    if(str) *saveptr = str;
+    str = *saveptr;
+
+    /* Nothing more to do with this string. */
+    if(!str) return NULL;
+
+    /* Find the first non-delimiter character. */
+    while(*str != '\0' && strchr(delim, *str) != NULL)
+        str++;
+    if(*str == '\0')
+    {
+        /* End of string. */
+        *saveptr = NULL;
+        return NULL;
+    }
+
+    /* Find the next delimiter character. */
+    *saveptr = strpbrk(str, delim);
+    if(*saveptr) *((*saveptr)++) = '\0';
+
+    return str;
+}
+
+static char *read_uint(ALuint *num, const char *line, int base)
+{
+    char *end;
+    *num = strtoul(line, &end, base);
+    if(end && *end != '\0')
+        end = lstrip(end);
+    return end;
+}
+
+static char *read_float(ALfloat *num, const char *line)
+{
+    char *end;
+#ifdef HAVE_STRTOF
+    *num = strtof(line, &end);
+#else
+    *num = (ALfloat)strtod(line, &end);
+#endif
+    if(end && *end != '\0')
+        end = lstrip(end);
+    return end;
+}
+
+
+char *read_clipped_line(FILE *f, char **buffer, size_t *maxlen)
+{
+    while(readline(f, buffer, maxlen))
+    {
+        char *line, *comment;
+
+        line = lstrip(*buffer);
+        comment = strchr(line, '#');
+        if(comment) *(comment++) = 0;
+
+        line = rstrip(line);
+        if(line[0]) return line;
+    }
+    return NULL;
+}
+
+static int load_ambdec_speakers(AmbDecConf *conf, FILE *f, char **buffer, size_t *maxlen, char **saveptr)
+{
+    ALuint cur = 0;
+    while(cur < conf->NumSpeakers)
+    {
+        const char *cmd = my_strtok_r(NULL, " \t", saveptr);
+        if(!cmd)
+        {
+            char *line = read_clipped_line(f, buffer, maxlen);
+            if(!line)
+            {
+                ERR("Unexpected end of file\n");
+                return 0;
+            }
+            cmd = my_strtok_r(line, " \t", saveptr);
+        }
+
+        if(strcmp(cmd, "add_spkr") == 0)
+        {
+            const char *name = my_strtok_r(NULL, " \t", saveptr);
+            const char *dist = my_strtok_r(NULL, " \t", saveptr);
+            const char *az = my_strtok_r(NULL, " \t", saveptr);
+            const char *elev = my_strtok_r(NULL, " \t", saveptr);
+            const char *conn = my_strtok_r(NULL, " \t", saveptr);
+
+            if(!name) WARN("Name not specified for speaker %u\n", cur+1);
+            else al_string_copy_cstr(&conf->Speakers[cur].Name, name);
+            if(!dist) WARN("Distance not specified for speaker %u\n", cur+1);
+            else read_float(&conf->Speakers[cur].Distance, dist);
+            if(!az) WARN("Azimuth not specified for speaker %u\n", cur+1);
+            else read_float(&conf->Speakers[cur].Azimuth, az);
+            if(!elev) WARN("Elevation not specified for speaker %u\n", cur+1);
+            else read_float(&conf->Speakers[cur].Elevation, elev);
+            if(!conn) TRACE("Connection not specified for speaker %u\n", cur+1);
+            else al_string_copy_cstr(&conf->Speakers[cur].Connection, conn);
+
+            cur++;
+        }
+        else
+        {
+            ERR("Unexpected speakers command: %s\n", cmd);
+            return 0;
+        }
+
+        cmd = my_strtok_r(NULL, " \t", saveptr);
+        if(cmd)
+        {
+            ERR("Unexpected junk on line: %s\n", cmd);
+            return 0;
+        }
+    }
+
+    return 1;
+}
+
+static int load_ambdec_matrix(ALfloat *gains, ALfloat (*matrix)[MAX_AMBI_COEFFS], ALuint maxrow, FILE *f, char **buffer, size_t *maxlen, char **saveptr)
+{
+    int gotgains = 0;
+    ALuint cur = 0;
+    while(cur < maxrow)
+    {
+        const char *cmd = my_strtok_r(NULL, " \t", saveptr);
+        if(!cmd)
+        {
+            char *line = read_clipped_line(f, buffer, maxlen);
+            if(!line)
+            {
+                ERR("Unexpected end of file\n");
+                return 0;
+            }
+            cmd = my_strtok_r(line, " \t", saveptr);
+        }
+
+        if(strcmp(cmd, "order_gain") == 0)
+        {
+            ALuint curgain = 0;
+            char *line;
+            while((line=my_strtok_r(NULL, " \t", saveptr)) != NULL)
+            {
+                ALfloat value;
+                line = read_float(&value, line);
+                if(line && *line != '\0')
+                {
+                    ERR("Extra junk on gain %u: %s\n", curgain+1, line);
+                    return 0;
+                }
+                if(curgain < MAX_AMBI_ORDER+1)
+                    gains[curgain] = value;
+                curgain++;
+            }
+            while(curgain < MAX_AMBI_ORDER+1)
+                gains[curgain++] = 0.0f;
+            gotgains = 1;
+        }
+        else if(strcmp(cmd, "add_row") == 0)
+        {
+            ALuint curidx = 0;
+            char *line;
+            while((line=my_strtok_r(NULL, " \t", saveptr)) != NULL)
+            {
+                ALfloat value;
+                line = read_float(&value, line);
+                if(line && *line != '\0')
+                {
+                    ERR("Extra junk on matrix element %ux%u: %s\n", cur, curidx, line);
+                    return 0;
+                }
+                if(curidx < MAX_AMBI_COEFFS)
+                    matrix[cur][curidx] = value;
+                curidx++;
+            }
+            while(curidx < MAX_AMBI_COEFFS)
+                matrix[cur][curidx++] = 0.0f;
+            cur++;
+        }
+        else
+        {
+            ERR("Unexpected speakers command: %s\n", cmd);
+            return 0;
+        }
+
+        cmd = my_strtok_r(NULL, " \t", saveptr);
+        if(cmd)
+        {
+            ERR("Unexpected junk on line: %s\n", cmd);
+            return 0;
+        }
+    }
+
+    if(!gotgains)
+    {
+        ERR("Matrix order_gain not specified\n");
+        return 0;
+    }
+
+    return 1;
+}
+
+void ambdec_init(AmbDecConf *conf)
+{
+    ALuint i;
+
+    memset(conf, 0, sizeof(*conf));
+    AL_STRING_INIT(conf->Description);
+    for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
+    {
+        AL_STRING_INIT(conf->Speakers[i].Name);
+        AL_STRING_INIT(conf->Speakers[i].Connection);
+    }
+}
+
+void ambdec_deinit(AmbDecConf *conf)
+{
+    ALuint i;
+
+    al_string_deinit(&conf->Description);
+    for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
+    {
+        al_string_deinit(&conf->Speakers[i].Name);
+        al_string_deinit(&conf->Speakers[i].Connection);
+    }
+    memset(conf, 0, sizeof(*conf));
+}
+
+int ambdec_load(AmbDecConf *conf, const char *fname)
+{
+    char *buffer = NULL;
+    size_t maxlen = 0;
+    char *line;
+    FILE *f;
+
+    f = al_fopen(fname, "r");
+    if(!f)
+    {
+        ERR("Failed to open: %s\n", fname);
+        return 0;
+    }
+
+    while((line=read_clipped_line(f, &buffer, &maxlen)) != NULL)
+    {
+        char *saveptr;
+        char *command;
+
+        command = my_strtok_r(line, "/ \t", &saveptr);
+        if(!command)
+        {
+            ERR("Malformed line: %s\n", line);
+            goto fail;
+        }
+
+        if(strcmp(command, "description") == 0)
+        {
+            char *value = my_strtok_r(NULL, "", &saveptr);
+            al_string_copy_cstr(&conf->Description, lstrip(value));
+        }
+        else if(strcmp(command, "version") == 0)
+        {
+            line = my_strtok_r(NULL, "", &saveptr);
+            line = read_uint(&conf->Version, line, 10);
+            if(line && *line != '\0')
+            {
+                ERR("Extra junk after version: %s\n", line);
+                goto fail;
+            }
+            if(conf->Version != 3)
+            {
+                ERR("Unsupported version: %u\n", conf->Version);
+                goto fail;
+            }
+        }
+        else if(strcmp(command, "dec") == 0)
+        {
+            const char *dec = my_strtok_r(NULL, "/ \t", &saveptr);
+            if(strcmp(dec, "chan_mask") == 0)
+            {
+                line = my_strtok_r(NULL, "", &saveptr);
+                line = read_uint(&conf->ChanMask, line, 16);
+                if(line && *line != '\0')
+                {
+                    ERR("Extra junk after mask: %s\n", line);
+                    goto fail;
+                }
+            }
+            else if(strcmp(dec, "freq_bands") == 0)
+            {
+                line = my_strtok_r(NULL, "", &saveptr);
+                line = read_uint(&conf->FreqBands, line, 10);
+                if(line && *line != '\0')
+                {
+                    ERR("Extra junk after freq_bands: %s\n", line);
+                    goto fail;
+                }
+                if(conf->FreqBands != 1 && conf->FreqBands != 2)
+                {
+                    ERR("Invalid freq_bands value: %u\n", conf->FreqBands);
+                    goto fail;
+                }
+            }
+            else if(strcmp(dec, "speakers") == 0)
+            {
+                line = my_strtok_r(NULL, "", &saveptr);
+                line = read_uint(&conf->NumSpeakers, line, 10);
+                if(line && *line != '\0')
+                {
+                    ERR("Extra junk after speakers: %s\n", line);
+                    goto fail;
+                }
+                if(conf->NumSpeakers > MAX_OUTPUT_CHANNELS)
+                {
+                    ERR("Unsupported speaker count: %u\n", conf->NumSpeakers);
+                    goto fail;
+                }
+            }
+            else if(strcmp(dec, "coeff_scale") == 0)
+            {
+                line = my_strtok_r(NULL, " \t", &saveptr);
+                if(strcmp(line, "n3d") == 0)
+                    conf->CoeffScale = ADS_N3D;
+                else if(strcmp(line, "sn3d") == 0)
+                    conf->CoeffScale = ADS_SN3D;
+                else if(strcmp(line, "fuma") == 0)
+                    conf->CoeffScale = ADS_FuMa;
+                else
+                {
+                    ERR("Unsupported coeff scale: %s\n", line);
+                    goto fail;
+                }
+            }
+            else
+            {
+                ERR("Unexpected /dec option: %s\n", dec);
+                goto fail;
+            }
+        }
+        else if(strcmp(command, "opt") == 0)
+        {
+            const char *opt = my_strtok_r(NULL, "/ \t", &saveptr);
+            if(strcmp(opt, "xover_freq") == 0)
+            {
+                line = my_strtok_r(NULL, "", &saveptr);
+                line = read_float(&conf->XOverFreq, line);
+                if(line && *line != '\0')
+                {
+                    ERR("Extra junk after xover_freq: %s\n", line);
+                    goto fail;
+                }
+            }
+            else if(strcmp(opt, "xover_ratio") == 0)
+            {
+                line = my_strtok_r(NULL, "", &saveptr);
+                line = read_float(&conf->XOverRatio, line);
+                if(line && *line != '\0')
+                {
+                    ERR("Extra junk after xover_ratio: %s\n", line);
+                    goto fail;
+                }
+            }
+            else if(strcmp(opt, "input_scale") == 0 || strcmp(opt, "nfeff_comp") == 0 ||
+                    strcmp(opt, "delay_comp") == 0 || strcmp(opt, "level_comp") == 0)
+            {
+                /* Unused */
+                my_strtok_r(NULL, " \t", &saveptr);
+            }
+            else
+            {
+                ERR("Unexpected /opt option: %s\n", opt);
+                goto fail;
+            }
+        }
+        else if(strcmp(command, "speakers") == 0)
+        {
+            const char *value = my_strtok_r(NULL, "/ \t", &saveptr);
+            if(strcmp(value, "{") != 0)
+            {
+                ERR("Expected { after %s command, got %s\n", command, value);
+                goto fail;
+            }
+            if(!load_ambdec_speakers(conf, f, &buffer, &maxlen, &saveptr))
+                goto fail;
+            value = my_strtok_r(NULL, "/ \t", &saveptr);
+            if(!value)
+            {
+                line = read_clipped_line(f, &buffer, &maxlen);
+                if(!line)
+                {
+                    ERR("Unexpected end of file\n");
+                    goto fail;
+                }
+                value = my_strtok_r(line, "/ \t", &saveptr);
+            }
+            if(strcmp(value, "}") != 0)
+            {
+                ERR("Expected } after speaker definitions, got %s\n", value);
+                goto fail;
+            }
+        }
+        else if(strcmp(command, "lfmatrix") == 0 || strcmp(command, "hfmatrix") == 0 ||
+                strcmp(command, "matrix") == 0)
+        {
+            const char *value = my_strtok_r(NULL, "/ \t", &saveptr);
+            if(strcmp(value, "{") != 0)
+            {
+                ERR("Expected { after %s command, got %s\n", command, value);
+                goto fail;
+            }
+            if(conf->FreqBands == 1)
+            {
+                if(strcmp(command, "matrix") != 0)
+                {
+                    ERR("Unexpected \"%s\" type for a single-band decoder\n", command);
+                    goto fail;
+                }
+                if(!load_ambdec_matrix(conf->HFOrderGain, conf->HFMatrix, conf->NumSpeakers,
+                                       f, &buffer, &maxlen, &saveptr))
+                    goto fail;
+            }
+            else
+            {
+                if(strcmp(command, "lfmatrix") == 0)
+                {
+                    if(!load_ambdec_matrix(conf->LFOrderGain, conf->LFMatrix, conf->NumSpeakers,
+                                           f, &buffer, &maxlen, &saveptr))
+                        goto fail;
+                }
+                else if(strcmp(command, "hfmatrix") == 0)
+                {
+                    if(!load_ambdec_matrix(conf->HFOrderGain, conf->HFMatrix, conf->NumSpeakers,
+                                           f, &buffer, &maxlen, &saveptr))
+                        goto fail;
+                }
+                else
+                {
+                    ERR("Unexpected \"%s\" type for a dual-band decoder\n", command);
+                    goto fail;
+                }
+            }
+            value = my_strtok_r(NULL, "/ \t", &saveptr);
+            if(!value)
+            {
+                line = read_clipped_line(f, &buffer, &maxlen);
+                if(!line)
+                {
+                    ERR("Unexpected end of file\n");
+                    goto fail;
+                }
+                value = my_strtok_r(line, "/ \t", &saveptr);
+            }
+            if(strcmp(value, "}") != 0)
+            {
+                ERR("Expected } after matrix definitions, got %s\n", value);
+                goto fail;
+            }
+        }
+        else if(strcmp(command, "end") == 0)
+        {
+            line = my_strtok_r(NULL, "/ \t", &saveptr);
+            if(line)
+            {
+                ERR("Unexpected junk on end: %s\n", line);
+                goto fail;
+            }
+
+            fclose(f);
+            free(buffer);
+            return 1;
+        }
+        else
+        {
+            ERR("Unexpected command: %s\n", command);
+            goto fail;
+        }
+
+        line = my_strtok_r(NULL, "/ \t", &saveptr);
+        if(line)
+        {
+            ERR("Unexpected junk on line: %s\n", line);
+            goto fail;
+        }
+    }
+    ERR("Unexpected end of file\n");
+
+fail:
+    fclose(f);
+    free(buffer);
+    return 0;
+}

+ 46 - 0
Engine/lib/openal-soft/Alc/ambdec.h

@@ -0,0 +1,46 @@
+#ifndef AMBDEC_H
+#define AMBDEC_H
+
+#include "alstring.h"
+#include "alMain.h"
+
+/* Helpers to read .ambdec configuration files. */
+
+enum AmbDecScaleType {
+    ADS_N3D,
+    ADS_SN3D,
+    ADS_FuMa,
+};
+typedef struct AmbDecConf {
+    al_string Description;
+    ALuint Version; /* Must be 3 */
+
+    ALuint ChanMask;
+    ALuint FreqBands; /* Must be 1 or 2 */
+    ALuint NumSpeakers;
+    enum AmbDecScaleType CoeffScale;
+
+    ALfloat XOverFreq;
+    ALfloat XOverRatio;
+
+    struct {
+        al_string Name;
+        ALfloat Distance;
+        ALfloat Azimuth;
+        ALfloat Elevation;
+        al_string Connection;
+    } Speakers[MAX_OUTPUT_CHANNELS];
+
+    /* Unused when FreqBands == 1 */
+    ALfloat LFOrderGain[MAX_AMBI_ORDER+1];
+    ALfloat LFMatrix[MAX_OUTPUT_CHANNELS][MAX_AMBI_COEFFS];
+
+    ALfloat HFOrderGain[MAX_AMBI_ORDER+1];
+    ALfloat HFMatrix[MAX_OUTPUT_CHANNELS][MAX_AMBI_COEFFS];
+} AmbDecConf;
+
+void ambdec_init(AmbDecConf *conf);
+void ambdec_deinit(AmbDecConf *conf);
+int ambdec_load(AmbDecConf *conf, const char *fname);
+
+#endif /* AMBDEC_H */

+ 1394 - 0
Engine/lib/openal-soft/Alc/backends/alsa.c

@@ -0,0 +1,1394 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 1999-2007 by authors.
+ * This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the
+ *  Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <memory.h>
+
+#include "alMain.h"
+#include "alu.h"
+#include "threads.h"
+#include "compat.h"
+
+#include "backends/base.h"
+
+#include <alsa/asoundlib.h>
+
+
+static const ALCchar alsaDevice[] = "ALSA Default";
+
+
+#ifdef HAVE_DYNLOAD
+#define ALSA_FUNCS(MAGIC)                                                     \
+    MAGIC(snd_strerror);                                                      \
+    MAGIC(snd_pcm_open);                                                      \
+    MAGIC(snd_pcm_close);                                                     \
+    MAGIC(snd_pcm_nonblock);                                                  \
+    MAGIC(snd_pcm_frames_to_bytes);                                           \
+    MAGIC(snd_pcm_bytes_to_frames);                                           \
+    MAGIC(snd_pcm_hw_params_malloc);                                          \
+    MAGIC(snd_pcm_hw_params_free);                                            \
+    MAGIC(snd_pcm_hw_params_any);                                             \
+    MAGIC(snd_pcm_hw_params_current);                                         \
+    MAGIC(snd_pcm_hw_params_set_access);                                      \
+    MAGIC(snd_pcm_hw_params_set_format);                                      \
+    MAGIC(snd_pcm_hw_params_set_channels);                                    \
+    MAGIC(snd_pcm_hw_params_set_periods_near);                                \
+    MAGIC(snd_pcm_hw_params_set_rate_near);                                   \
+    MAGIC(snd_pcm_hw_params_set_rate);                                        \
+    MAGIC(snd_pcm_hw_params_set_rate_resample);                               \
+    MAGIC(snd_pcm_hw_params_set_buffer_time_near);                            \
+    MAGIC(snd_pcm_hw_params_set_period_time_near);                            \
+    MAGIC(snd_pcm_hw_params_set_buffer_size_near);                            \
+    MAGIC(snd_pcm_hw_params_set_period_size_near);                            \
+    MAGIC(snd_pcm_hw_params_set_buffer_size_min);                             \
+    MAGIC(snd_pcm_hw_params_get_buffer_time_min);                             \
+    MAGIC(snd_pcm_hw_params_get_buffer_time_max);                             \
+    MAGIC(snd_pcm_hw_params_get_period_time_min);                             \
+    MAGIC(snd_pcm_hw_params_get_period_time_max);                             \
+    MAGIC(snd_pcm_hw_params_get_buffer_size);                                 \
+    MAGIC(snd_pcm_hw_params_get_period_size);                                 \
+    MAGIC(snd_pcm_hw_params_get_access);                                      \
+    MAGIC(snd_pcm_hw_params_get_periods);                                     \
+    MAGIC(snd_pcm_hw_params_test_format);                                     \
+    MAGIC(snd_pcm_hw_params_test_channels);                                   \
+    MAGIC(snd_pcm_hw_params);                                                 \
+    MAGIC(snd_pcm_sw_params_malloc);                                          \
+    MAGIC(snd_pcm_sw_params_current);                                         \
+    MAGIC(snd_pcm_sw_params_set_avail_min);                                   \
+    MAGIC(snd_pcm_sw_params_set_stop_threshold);                              \
+    MAGIC(snd_pcm_sw_params);                                                 \
+    MAGIC(snd_pcm_sw_params_free);                                            \
+    MAGIC(snd_pcm_prepare);                                                   \
+    MAGIC(snd_pcm_start);                                                     \
+    MAGIC(snd_pcm_resume);                                                    \
+    MAGIC(snd_pcm_reset);                                                     \
+    MAGIC(snd_pcm_wait);                                                      \
+    MAGIC(snd_pcm_delay);                                                     \
+    MAGIC(snd_pcm_state);                                                     \
+    MAGIC(snd_pcm_avail_update);                                              \
+    MAGIC(snd_pcm_areas_silence);                                             \
+    MAGIC(snd_pcm_mmap_begin);                                                \
+    MAGIC(snd_pcm_mmap_commit);                                               \
+    MAGIC(snd_pcm_readi);                                                     \
+    MAGIC(snd_pcm_writei);                                                    \
+    MAGIC(snd_pcm_drain);                                                     \
+    MAGIC(snd_pcm_drop);                                                      \
+    MAGIC(snd_pcm_recover);                                                   \
+    MAGIC(snd_pcm_info_malloc);                                               \
+    MAGIC(snd_pcm_info_free);                                                 \
+    MAGIC(snd_pcm_info_set_device);                                           \
+    MAGIC(snd_pcm_info_set_subdevice);                                        \
+    MAGIC(snd_pcm_info_set_stream);                                           \
+    MAGIC(snd_pcm_info_get_name);                                             \
+    MAGIC(snd_ctl_pcm_next_device);                                           \
+    MAGIC(snd_ctl_pcm_info);                                                  \
+    MAGIC(snd_ctl_open);                                                      \
+    MAGIC(snd_ctl_close);                                                     \
+    MAGIC(snd_ctl_card_info_malloc);                                          \
+    MAGIC(snd_ctl_card_info_free);                                            \
+    MAGIC(snd_ctl_card_info);                                                 \
+    MAGIC(snd_ctl_card_info_get_name);                                        \
+    MAGIC(snd_ctl_card_info_get_id);                                          \
+    MAGIC(snd_card_next);                                                     \
+    MAGIC(snd_config_update_free_global)
+
+static void *alsa_handle;
+#define MAKE_FUNC(f) static __typeof(f) * p##f
+ALSA_FUNCS(MAKE_FUNC);
+#undef MAKE_FUNC
+
+#define snd_strerror psnd_strerror
+#define snd_pcm_open psnd_pcm_open
+#define snd_pcm_close psnd_pcm_close
+#define snd_pcm_nonblock psnd_pcm_nonblock
+#define snd_pcm_frames_to_bytes psnd_pcm_frames_to_bytes
+#define snd_pcm_bytes_to_frames psnd_pcm_bytes_to_frames
+#define snd_pcm_hw_params_malloc psnd_pcm_hw_params_malloc
+#define snd_pcm_hw_params_free psnd_pcm_hw_params_free
+#define snd_pcm_hw_params_any psnd_pcm_hw_params_any
+#define snd_pcm_hw_params_current psnd_pcm_hw_params_current
+#define snd_pcm_hw_params_set_access psnd_pcm_hw_params_set_access
+#define snd_pcm_hw_params_set_format psnd_pcm_hw_params_set_format
+#define snd_pcm_hw_params_set_channels psnd_pcm_hw_params_set_channels
+#define snd_pcm_hw_params_set_periods_near psnd_pcm_hw_params_set_periods_near
+#define snd_pcm_hw_params_set_rate_near psnd_pcm_hw_params_set_rate_near
+#define snd_pcm_hw_params_set_rate psnd_pcm_hw_params_set_rate
+#define snd_pcm_hw_params_set_rate_resample psnd_pcm_hw_params_set_rate_resample
+#define snd_pcm_hw_params_set_buffer_time_near psnd_pcm_hw_params_set_buffer_time_near
+#define snd_pcm_hw_params_set_period_time_near psnd_pcm_hw_params_set_period_time_near
+#define snd_pcm_hw_params_set_buffer_size_near psnd_pcm_hw_params_set_buffer_size_near
+#define snd_pcm_hw_params_set_period_size_near psnd_pcm_hw_params_set_period_size_near
+#define snd_pcm_hw_params_set_buffer_size_min psnd_pcm_hw_params_set_buffer_size_min
+#define snd_pcm_hw_params_get_buffer_time_min psnd_pcm_hw_params_get_buffer_time_min
+#define snd_pcm_hw_params_get_buffer_time_max psnd_pcm_hw_params_get_buffer_time_max
+#define snd_pcm_hw_params_get_period_time_min psnd_pcm_hw_params_get_period_time_min
+#define snd_pcm_hw_params_get_period_time_max psnd_pcm_hw_params_get_period_time_max
+#define snd_pcm_hw_params_get_buffer_size psnd_pcm_hw_params_get_buffer_size
+#define snd_pcm_hw_params_get_period_size psnd_pcm_hw_params_get_period_size
+#define snd_pcm_hw_params_get_access psnd_pcm_hw_params_get_access
+#define snd_pcm_hw_params_get_periods psnd_pcm_hw_params_get_periods
+#define snd_pcm_hw_params_test_format psnd_pcm_hw_params_test_format
+#define snd_pcm_hw_params_test_channels psnd_pcm_hw_params_test_channels
+#define snd_pcm_hw_params psnd_pcm_hw_params
+#define snd_pcm_sw_params_malloc psnd_pcm_sw_params_malloc
+#define snd_pcm_sw_params_current psnd_pcm_sw_params_current
+#define snd_pcm_sw_params_set_avail_min psnd_pcm_sw_params_set_avail_min
+#define snd_pcm_sw_params_set_stop_threshold psnd_pcm_sw_params_set_stop_threshold
+#define snd_pcm_sw_params psnd_pcm_sw_params
+#define snd_pcm_sw_params_free psnd_pcm_sw_params_free
+#define snd_pcm_prepare psnd_pcm_prepare
+#define snd_pcm_start psnd_pcm_start
+#define snd_pcm_resume psnd_pcm_resume
+#define snd_pcm_reset psnd_pcm_reset
+#define snd_pcm_wait psnd_pcm_wait
+#define snd_pcm_delay psnd_pcm_delay
+#define snd_pcm_state psnd_pcm_state
+#define snd_pcm_avail_update psnd_pcm_avail_update
+#define snd_pcm_areas_silence psnd_pcm_areas_silence
+#define snd_pcm_mmap_begin psnd_pcm_mmap_begin
+#define snd_pcm_mmap_commit psnd_pcm_mmap_commit
+#define snd_pcm_readi psnd_pcm_readi
+#define snd_pcm_writei psnd_pcm_writei
+#define snd_pcm_drain psnd_pcm_drain
+#define snd_pcm_drop psnd_pcm_drop
+#define snd_pcm_recover psnd_pcm_recover
+#define snd_pcm_info_malloc psnd_pcm_info_malloc
+#define snd_pcm_info_free psnd_pcm_info_free
+#define snd_pcm_info_set_device psnd_pcm_info_set_device
+#define snd_pcm_info_set_subdevice psnd_pcm_info_set_subdevice
+#define snd_pcm_info_set_stream psnd_pcm_info_set_stream
+#define snd_pcm_info_get_name psnd_pcm_info_get_name
+#define snd_ctl_pcm_next_device psnd_ctl_pcm_next_device
+#define snd_ctl_pcm_info psnd_ctl_pcm_info
+#define snd_ctl_open psnd_ctl_open
+#define snd_ctl_close psnd_ctl_close
+#define snd_ctl_card_info_malloc psnd_ctl_card_info_malloc
+#define snd_ctl_card_info_free psnd_ctl_card_info_free
+#define snd_ctl_card_info psnd_ctl_card_info
+#define snd_ctl_card_info_get_name psnd_ctl_card_info_get_name
+#define snd_ctl_card_info_get_id psnd_ctl_card_info_get_id
+#define snd_card_next psnd_card_next
+#define snd_config_update_free_global psnd_config_update_free_global
+#endif
+
+
+static ALCboolean alsa_load(void)
+{
+    ALCboolean error = ALC_FALSE;
+
+#ifdef HAVE_DYNLOAD
+    if(!alsa_handle)
+    {
+        alsa_handle = LoadLib("libasound.so.2");
+        if(!alsa_handle)
+            return ALC_FALSE;
+
+        error = ALC_FALSE;
+#define LOAD_FUNC(f) do {                                                     \
+    p##f = GetSymbol(alsa_handle, #f);                                        \
+    if(p##f == NULL) {                                                        \
+        error = ALC_TRUE;                                                     \
+    }                                                                         \
+} while(0)
+        ALSA_FUNCS(LOAD_FUNC);
+#undef LOAD_FUNC
+
+        if(error)
+        {
+            CloseLib(alsa_handle);
+            alsa_handle = NULL;
+            return ALC_FALSE;
+        }
+    }
+#endif
+
+    return !error;
+}
+
+
+typedef struct {
+    al_string name;
+    al_string device_name;
+} DevMap;
+TYPEDEF_VECTOR(DevMap, vector_DevMap)
+
+static vector_DevMap PlaybackDevices;
+static vector_DevMap CaptureDevices;
+
+static void clear_devlist(vector_DevMap *devlist)
+{
+#define FREE_DEV(i) do {                                                      \
+    AL_STRING_DEINIT((i)->name);                                              \
+    AL_STRING_DEINIT((i)->device_name);                                       \
+} while(0)
+    VECTOR_FOR_EACH(DevMap, *devlist, FREE_DEV);
+    VECTOR_RESIZE(*devlist, 0, 0);
+#undef FREE_DEV
+}
+
+
+static const char *prefix_name(snd_pcm_stream_t stream)
+{
+    assert(stream == SND_PCM_STREAM_PLAYBACK || stream == SND_PCM_STREAM_CAPTURE);
+    return (stream==SND_PCM_STREAM_PLAYBACK) ? "device-prefix" : "capture-prefix";
+}
+
+static void probe_devices(snd_pcm_stream_t stream, vector_DevMap *DeviceList)
+{
+    const char *main_prefix = "plughw:";
+    snd_ctl_t *handle;
+    snd_ctl_card_info_t *info;
+    snd_pcm_info_t *pcminfo;
+    int card, err, dev;
+    DevMap entry;
+
+    clear_devlist(DeviceList);
+
+    snd_ctl_card_info_malloc(&info);
+    snd_pcm_info_malloc(&pcminfo);
+
+    AL_STRING_INIT(entry.name);
+    AL_STRING_INIT(entry.device_name);
+    al_string_copy_cstr(&entry.name, alsaDevice);
+    al_string_copy_cstr(&entry.device_name, GetConfigValue(NULL, "alsa", (stream==SND_PCM_STREAM_PLAYBACK) ?
+                                                           "device" : "capture", "default"));
+    VECTOR_PUSH_BACK(*DeviceList, entry);
+
+    card = -1;
+    if((err=snd_card_next(&card)) < 0)
+        ERR("Failed to find a card: %s\n", snd_strerror(err));
+    ConfigValueStr(NULL, "alsa", prefix_name(stream), &main_prefix);
+    while(card >= 0)
+    {
+        const char *card_prefix = main_prefix;
+        const char *cardname, *cardid;
+        char name[256];
+
+        snprintf(name, sizeof(name), "hw:%d", card);
+        if((err = snd_ctl_open(&handle, name, 0)) < 0)
+        {
+            ERR("control open (hw:%d): %s\n", card, snd_strerror(err));
+            goto next_card;
+        }
+        if((err = snd_ctl_card_info(handle, info)) < 0)
+        {
+            ERR("control hardware info (hw:%d): %s\n", card, snd_strerror(err));
+            snd_ctl_close(handle);
+            goto next_card;
+        }
+
+        cardname = snd_ctl_card_info_get_name(info);
+        cardid = snd_ctl_card_info_get_id(info);
+
+        snprintf(name, sizeof(name), "%s-%s", prefix_name(stream), cardid);
+        ConfigValueStr(NULL, "alsa", name, &card_prefix);
+
+        dev = -1;
+        while(1)
+        {
+            const char *device_prefix = card_prefix;
+            const char *devname;
+            char device[128];
+
+            if(snd_ctl_pcm_next_device(handle, &dev) < 0)
+                ERR("snd_ctl_pcm_next_device failed\n");
+            if(dev < 0)
+                break;
+
+            snd_pcm_info_set_device(pcminfo, dev);
+            snd_pcm_info_set_subdevice(pcminfo, 0);
+            snd_pcm_info_set_stream(pcminfo, stream);
+            if((err = snd_ctl_pcm_info(handle, pcminfo)) < 0) {
+                if(err != -ENOENT)
+                    ERR("control digital audio info (hw:%d): %s\n", card, snd_strerror(err));
+                continue;
+            }
+
+            devname = snd_pcm_info_get_name(pcminfo);
+
+            snprintf(name, sizeof(name), "%s-%s-%d", prefix_name(stream), cardid, dev);
+            ConfigValueStr(NULL, "alsa", name, &device_prefix);
+
+            snprintf(name, sizeof(name), "%s, %s (CARD=%s,DEV=%d)",
+                        cardname, devname, cardid, dev);
+            snprintf(device, sizeof(device), "%sCARD=%s,DEV=%d",
+                        device_prefix, cardid, dev);
+
+            TRACE("Got device \"%s\", \"%s\"\n", name, device);
+            AL_STRING_INIT(entry.name);
+            AL_STRING_INIT(entry.device_name);
+            al_string_copy_cstr(&entry.name, name);
+            al_string_copy_cstr(&entry.device_name, device);
+            VECTOR_PUSH_BACK(*DeviceList, entry);
+        }
+        snd_ctl_close(handle);
+    next_card:
+        if(snd_card_next(&card) < 0) {
+            ERR("snd_card_next failed\n");
+            break;
+        }
+    }
+
+    snd_pcm_info_free(pcminfo);
+    snd_ctl_card_info_free(info);
+}
+
+
+static int verify_state(snd_pcm_t *handle)
+{
+    snd_pcm_state_t state = snd_pcm_state(handle);
+    int err;
+
+    switch(state)
+    {
+        case SND_PCM_STATE_OPEN:
+        case SND_PCM_STATE_SETUP:
+        case SND_PCM_STATE_PREPARED:
+        case SND_PCM_STATE_RUNNING:
+        case SND_PCM_STATE_DRAINING:
+        case SND_PCM_STATE_PAUSED:
+            /* All Okay */
+            break;
+
+        case SND_PCM_STATE_XRUN:
+            if((err=snd_pcm_recover(handle, -EPIPE, 1)) < 0)
+                return err;
+            break;
+        case SND_PCM_STATE_SUSPENDED:
+            if((err=snd_pcm_recover(handle, -ESTRPIPE, 1)) < 0)
+                return err;
+            break;
+        case SND_PCM_STATE_DISCONNECTED:
+            return -ENODEV;
+    }
+
+    return state;
+}
+
+
+typedef struct ALCplaybackAlsa {
+    DERIVE_FROM_TYPE(ALCbackend);
+
+    snd_pcm_t *pcmHandle;
+
+    ALvoid *buffer;
+    ALsizei size;
+
+    volatile int killNow;
+    althrd_t thread;
+} ALCplaybackAlsa;
+
+static int ALCplaybackAlsa_mixerProc(void *ptr);
+static int ALCplaybackAlsa_mixerNoMMapProc(void *ptr);
+
+static void ALCplaybackAlsa_Construct(ALCplaybackAlsa *self, ALCdevice *device);
+static DECLARE_FORWARD(ALCplaybackAlsa, ALCbackend, void, Destruct)
+static ALCenum ALCplaybackAlsa_open(ALCplaybackAlsa *self, const ALCchar *name);
+static void ALCplaybackAlsa_close(ALCplaybackAlsa *self);
+static ALCboolean ALCplaybackAlsa_reset(ALCplaybackAlsa *self);
+static ALCboolean ALCplaybackAlsa_start(ALCplaybackAlsa *self);
+static void ALCplaybackAlsa_stop(ALCplaybackAlsa *self);
+static DECLARE_FORWARD2(ALCplaybackAlsa, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
+static DECLARE_FORWARD(ALCplaybackAlsa, ALCbackend, ALCuint, availableSamples)
+static ClockLatency ALCplaybackAlsa_getClockLatency(ALCplaybackAlsa *self);
+static DECLARE_FORWARD(ALCplaybackAlsa, ALCbackend, void, lock)
+static DECLARE_FORWARD(ALCplaybackAlsa, ALCbackend, void, unlock)
+DECLARE_DEFAULT_ALLOCATORS(ALCplaybackAlsa)
+
+DEFINE_ALCBACKEND_VTABLE(ALCplaybackAlsa);
+
+
+static void ALCplaybackAlsa_Construct(ALCplaybackAlsa *self, ALCdevice *device)
+{
+    ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
+    SET_VTABLE2(ALCplaybackAlsa, ALCbackend, self);
+}
+
+
+static int ALCplaybackAlsa_mixerProc(void *ptr)
+{
+    ALCplaybackAlsa *self = (ALCplaybackAlsa*)ptr;
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+    const snd_pcm_channel_area_t *areas = NULL;
+    snd_pcm_uframes_t update_size, num_updates;
+    snd_pcm_sframes_t avail, commitres;
+    snd_pcm_uframes_t offset, frames;
+    char *WritePtr;
+    int err;
+
+    SetRTPriority();
+    althrd_setname(althrd_current(), MIXER_THREAD_NAME);
+
+    update_size = device->UpdateSize;
+    num_updates = device->NumUpdates;
+    while(!self->killNow)
+    {
+        int state = verify_state(self->pcmHandle);
+        if(state < 0)
+        {
+            ERR("Invalid state detected: %s\n", snd_strerror(state));
+            ALCplaybackAlsa_lock(self);
+            aluHandleDisconnect(device);
+            ALCplaybackAlsa_unlock(self);
+            break;
+        }
+
+        avail = snd_pcm_avail_update(self->pcmHandle);
+        if(avail < 0)
+        {
+            ERR("available update failed: %s\n", snd_strerror(avail));
+            continue;
+        }
+
+        if((snd_pcm_uframes_t)avail > update_size*(num_updates+1))
+        {
+            WARN("available samples exceeds the buffer size\n");
+            snd_pcm_reset(self->pcmHandle);
+            continue;
+        }
+
+        // make sure there's frames to process
+        if((snd_pcm_uframes_t)avail < update_size)
+        {
+            if(state != SND_PCM_STATE_RUNNING)
+            {
+                err = snd_pcm_start(self->pcmHandle);
+                if(err < 0)
+                {
+                    ERR("start failed: %s\n", snd_strerror(err));
+                    continue;
+                }
+            }
+            if(snd_pcm_wait(self->pcmHandle, 1000) == 0)
+                ERR("Wait timeout... buffer size too low?\n");
+            continue;
+        }
+        avail -= avail%update_size;
+
+        // it is possible that contiguous areas are smaller, thus we use a loop
+        ALCplaybackAlsa_lock(self);
+        while(avail > 0)
+        {
+            frames = avail;
+
+            err = snd_pcm_mmap_begin(self->pcmHandle, &areas, &offset, &frames);
+            if(err < 0)
+            {
+                ERR("mmap begin error: %s\n", snd_strerror(err));
+                break;
+            }
+
+            WritePtr = (char*)areas->addr + (offset * areas->step / 8);
+            aluMixData(device, WritePtr, frames);
+
+            commitres = snd_pcm_mmap_commit(self->pcmHandle, offset, frames);
+            if(commitres < 0 || (commitres-frames) != 0)
+            {
+                ERR("mmap commit error: %s\n",
+                    snd_strerror(commitres >= 0 ? -EPIPE : commitres));
+                break;
+            }
+
+            avail -= frames;
+        }
+        ALCplaybackAlsa_unlock(self);
+    }
+
+    return 0;
+}
+
+static int ALCplaybackAlsa_mixerNoMMapProc(void *ptr)
+{
+    ALCplaybackAlsa *self = (ALCplaybackAlsa*)ptr;
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+    snd_pcm_uframes_t update_size, num_updates;
+    snd_pcm_sframes_t avail;
+    char *WritePtr;
+    int err;
+
+    SetRTPriority();
+    althrd_setname(althrd_current(), MIXER_THREAD_NAME);
+
+    update_size = device->UpdateSize;
+    num_updates = device->NumUpdates;
+    while(!self->killNow)
+    {
+        int state = verify_state(self->pcmHandle);
+        if(state < 0)
+        {
+            ERR("Invalid state detected: %s\n", snd_strerror(state));
+            ALCplaybackAlsa_lock(self);
+            aluHandleDisconnect(device);
+            ALCplaybackAlsa_unlock(self);
+            break;
+        }
+
+        avail = snd_pcm_avail_update(self->pcmHandle);
+        if(avail < 0)
+        {
+            ERR("available update failed: %s\n", snd_strerror(avail));
+            continue;
+        }
+
+        if((snd_pcm_uframes_t)avail > update_size*num_updates)
+        {
+            WARN("available samples exceeds the buffer size\n");
+            snd_pcm_reset(self->pcmHandle);
+            continue;
+        }
+
+        if((snd_pcm_uframes_t)avail < update_size)
+        {
+            if(state != SND_PCM_STATE_RUNNING)
+            {
+                err = snd_pcm_start(self->pcmHandle);
+                if(err < 0)
+                {
+                    ERR("start failed: %s\n", snd_strerror(err));
+                    continue;
+                }
+            }
+            if(snd_pcm_wait(self->pcmHandle, 1000) == 0)
+                ERR("Wait timeout... buffer size too low?\n");
+            continue;
+        }
+
+        ALCplaybackAlsa_lock(self);
+        WritePtr = self->buffer;
+        avail = snd_pcm_bytes_to_frames(self->pcmHandle, self->size);
+        aluMixData(device, WritePtr, avail);
+
+        while(avail > 0)
+        {
+            int ret = snd_pcm_writei(self->pcmHandle, WritePtr, avail);
+            switch (ret)
+            {
+            case -EAGAIN:
+                continue;
+#if ESTRPIPE != EPIPE
+            case -ESTRPIPE:
+#endif
+            case -EPIPE:
+            case -EINTR:
+                ret = snd_pcm_recover(self->pcmHandle, ret, 1);
+                if(ret < 0)
+                    avail = 0;
+                break;
+            default:
+                if (ret >= 0)
+                {
+                    WritePtr += snd_pcm_frames_to_bytes(self->pcmHandle, ret);
+                    avail -= ret;
+                }
+                break;
+            }
+            if (ret < 0)
+            {
+                ret = snd_pcm_prepare(self->pcmHandle);
+                if(ret < 0)
+                    break;
+            }
+        }
+        ALCplaybackAlsa_unlock(self);
+    }
+
+    return 0;
+}
+
+
+static ALCenum ALCplaybackAlsa_open(ALCplaybackAlsa *self, const ALCchar *name)
+{
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+    const char *driver = NULL;
+    int err;
+
+    if(name)
+    {
+        const DevMap *iter;
+
+        if(VECTOR_SIZE(PlaybackDevices) == 0)
+            probe_devices(SND_PCM_STREAM_PLAYBACK, &PlaybackDevices);
+
+#define MATCH_NAME(i)  (al_string_cmp_cstr((i)->name, name) == 0)
+        VECTOR_FIND_IF(iter, const DevMap, PlaybackDevices, MATCH_NAME);
+#undef MATCH_NAME
+        if(iter == VECTOR_END(PlaybackDevices))
+            return ALC_INVALID_VALUE;
+        driver = al_string_get_cstr(iter->device_name);
+    }
+    else
+    {
+        name = alsaDevice;
+        driver = GetConfigValue(NULL, "alsa", "device", "default");
+    }
+
+    TRACE("Opening device \"%s\"\n", driver);
+    err = snd_pcm_open(&self->pcmHandle, driver, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
+    if(err < 0)
+    {
+        ERR("Could not open playback device '%s': %s\n", driver, snd_strerror(err));
+        return ALC_OUT_OF_MEMORY;
+    }
+
+    /* Free alsa's global config tree. Otherwise valgrind reports a ton of leaks. */
+    snd_config_update_free_global();
+
+    al_string_copy_cstr(&device->DeviceName, name);
+
+    return ALC_NO_ERROR;
+}
+
+static void ALCplaybackAlsa_close(ALCplaybackAlsa *self)
+{
+    snd_pcm_close(self->pcmHandle);
+}
+
+static ALCboolean ALCplaybackAlsa_reset(ALCplaybackAlsa *self)
+{
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+    snd_pcm_uframes_t periodSizeInFrames;
+    unsigned int periodLen, bufferLen;
+    snd_pcm_sw_params_t *sp = NULL;
+    snd_pcm_hw_params_t *hp = NULL;
+    snd_pcm_format_t format = -1;
+    snd_pcm_access_t access;
+    unsigned int periods;
+    unsigned int rate;
+    const char *funcerr;
+    int allowmmap;
+    int dir;
+    int err;
+
+    switch(device->FmtType)
+    {
+        case DevFmtByte:
+            format = SND_PCM_FORMAT_S8;
+            break;
+        case DevFmtUByte:
+            format = SND_PCM_FORMAT_U8;
+            break;
+        case DevFmtShort:
+            format = SND_PCM_FORMAT_S16;
+            break;
+        case DevFmtUShort:
+            format = SND_PCM_FORMAT_U16;
+            break;
+        case DevFmtInt:
+            format = SND_PCM_FORMAT_S32;
+            break;
+        case DevFmtUInt:
+            format = SND_PCM_FORMAT_U32;
+            break;
+        case DevFmtFloat:
+            format = SND_PCM_FORMAT_FLOAT;
+            break;
+    }
+
+    allowmmap = GetConfigValueBool(al_string_get_cstr(device->DeviceName), "alsa", "mmap", 1);
+    periods = device->NumUpdates;
+    periodLen = (ALuint64)device->UpdateSize * 1000000 / device->Frequency;
+    bufferLen = periodLen * periods;
+    rate = device->Frequency;
+
+    snd_pcm_hw_params_malloc(&hp);
+#define CHECK(x) if((funcerr=#x),(err=(x)) < 0) goto error
+    CHECK(snd_pcm_hw_params_any(self->pcmHandle, hp));
+    /* set interleaved access */
+    if(!allowmmap || snd_pcm_hw_params_set_access(self->pcmHandle, hp, SND_PCM_ACCESS_MMAP_INTERLEAVED) < 0)
+    {
+        /* No mmap */
+        CHECK(snd_pcm_hw_params_set_access(self->pcmHandle, hp, SND_PCM_ACCESS_RW_INTERLEAVED));
+    }
+    /* test and set format (implicitly sets sample bits) */
+    if(snd_pcm_hw_params_test_format(self->pcmHandle, hp, format) < 0)
+    {
+        static const struct {
+            snd_pcm_format_t format;
+            enum DevFmtType fmttype;
+        } formatlist[] = {
+            { SND_PCM_FORMAT_FLOAT, DevFmtFloat  },
+            { SND_PCM_FORMAT_S32,   DevFmtInt    },
+            { SND_PCM_FORMAT_U32,   DevFmtUInt   },
+            { SND_PCM_FORMAT_S16,   DevFmtShort  },
+            { SND_PCM_FORMAT_U16,   DevFmtUShort },
+            { SND_PCM_FORMAT_S8,    DevFmtByte   },
+            { SND_PCM_FORMAT_U8,    DevFmtUByte  },
+        };
+        size_t k;
+
+        for(k = 0;k < COUNTOF(formatlist);k++)
+        {
+            format = formatlist[k].format;
+            if(snd_pcm_hw_params_test_format(self->pcmHandle, hp, format) >= 0)
+            {
+                device->FmtType = formatlist[k].fmttype;
+                break;
+            }
+        }
+    }
+    CHECK(snd_pcm_hw_params_set_format(self->pcmHandle, hp, format));
+    /* test and set channels (implicitly sets frame bits) */
+    if(snd_pcm_hw_params_test_channels(self->pcmHandle, hp, ChannelsFromDevFmt(device->FmtChans)) < 0)
+    {
+        static const enum DevFmtChannels channellist[] = {
+            DevFmtStereo,
+            DevFmtQuad,
+            DevFmtX51,
+            DevFmtX71,
+            DevFmtMono,
+        };
+        size_t k;
+
+        for(k = 0;k < COUNTOF(channellist);k++)
+        {
+            if(snd_pcm_hw_params_test_channels(self->pcmHandle, hp, ChannelsFromDevFmt(channellist[k])) >= 0)
+            {
+                device->FmtChans = channellist[k];
+                break;
+            }
+        }
+    }
+    CHECK(snd_pcm_hw_params_set_channels(self->pcmHandle, hp, ChannelsFromDevFmt(device->FmtChans)));
+    /* set rate (implicitly constrains period/buffer parameters) */
+    if(!GetConfigValueBool(al_string_get_cstr(device->DeviceName), "alsa", "allow-resampler", 0) ||
+       !(device->Flags&DEVICE_FREQUENCY_REQUEST))
+    {
+        if(snd_pcm_hw_params_set_rate_resample(self->pcmHandle, hp, 0) < 0)
+            ERR("Failed to disable ALSA resampler\n");
+    }
+    else if(snd_pcm_hw_params_set_rate_resample(self->pcmHandle, hp, 1) < 0)
+        ERR("Failed to enable ALSA resampler\n");
+    CHECK(snd_pcm_hw_params_set_rate_near(self->pcmHandle, hp, &rate, NULL));
+    /* set buffer time (implicitly constrains period/buffer parameters) */
+    if((err=snd_pcm_hw_params_set_buffer_time_near(self->pcmHandle, hp, &bufferLen, NULL)) < 0)
+        ERR("snd_pcm_hw_params_set_buffer_time_near failed: %s\n", snd_strerror(err));
+    /* set period time (implicitly sets buffer size/bytes/time and period size/bytes) */
+    if((err=snd_pcm_hw_params_set_period_time_near(self->pcmHandle, hp, &periodLen, NULL)) < 0)
+        ERR("snd_pcm_hw_params_set_period_time_near failed: %s\n", snd_strerror(err));
+    /* install and prepare hardware configuration */
+    CHECK(snd_pcm_hw_params(self->pcmHandle, hp));
+    /* retrieve configuration info */
+    CHECK(snd_pcm_hw_params_get_access(hp, &access));
+    CHECK(snd_pcm_hw_params_get_period_size(hp, &periodSizeInFrames, NULL));
+    CHECK(snd_pcm_hw_params_get_periods(hp, &periods, &dir));
+    if(dir != 0)
+        WARN("Inexact period count: %u (%d)\n", periods, dir);
+
+    snd_pcm_hw_params_free(hp);
+    hp = NULL;
+    snd_pcm_sw_params_malloc(&sp);
+
+    CHECK(snd_pcm_sw_params_current(self->pcmHandle, sp));
+    CHECK(snd_pcm_sw_params_set_avail_min(self->pcmHandle, sp, periodSizeInFrames));
+    CHECK(snd_pcm_sw_params_set_stop_threshold(self->pcmHandle, sp, periodSizeInFrames*periods));
+    CHECK(snd_pcm_sw_params(self->pcmHandle, sp));
+#undef CHECK
+    snd_pcm_sw_params_free(sp);
+    sp = NULL;
+
+    device->NumUpdates = periods;
+    device->UpdateSize = periodSizeInFrames;
+    device->Frequency = rate;
+
+    SetDefaultChannelOrder(device);
+
+    return ALC_TRUE;
+
+error:
+    ERR("%s failed: %s\n", funcerr, snd_strerror(err));
+    if(hp) snd_pcm_hw_params_free(hp);
+    if(sp) snd_pcm_sw_params_free(sp);
+    return ALC_FALSE;
+}
+
+static ALCboolean ALCplaybackAlsa_start(ALCplaybackAlsa *self)
+{
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+    int (*thread_func)(void*) = NULL;
+    snd_pcm_hw_params_t *hp = NULL;
+    snd_pcm_access_t access;
+    const char *funcerr;
+    int err;
+
+    snd_pcm_hw_params_malloc(&hp);
+#define CHECK(x) if((funcerr=#x),(err=(x)) < 0) goto error
+    CHECK(snd_pcm_hw_params_current(self->pcmHandle, hp));
+    /* retrieve configuration info */
+    CHECK(snd_pcm_hw_params_get_access(hp, &access));
+#undef CHECK
+    snd_pcm_hw_params_free(hp);
+    hp = NULL;
+
+    self->size = snd_pcm_frames_to_bytes(self->pcmHandle, device->UpdateSize);
+    if(access == SND_PCM_ACCESS_RW_INTERLEAVED)
+    {
+        self->buffer = al_malloc(16, self->size);
+        if(!self->buffer)
+        {
+            ERR("buffer malloc failed\n");
+            return ALC_FALSE;
+        }
+        thread_func = ALCplaybackAlsa_mixerNoMMapProc;
+    }
+    else
+    {
+        err = snd_pcm_prepare(self->pcmHandle);
+        if(err < 0)
+        {
+            ERR("snd_pcm_prepare(data->pcmHandle) failed: %s\n", snd_strerror(err));
+            return ALC_FALSE;
+        }
+        thread_func = ALCplaybackAlsa_mixerProc;
+    }
+    self->killNow = 0;
+    if(althrd_create(&self->thread, thread_func, self) != althrd_success)
+    {
+        ERR("Could not create playback thread\n");
+        al_free(self->buffer);
+        self->buffer = NULL;
+        return ALC_FALSE;
+    }
+
+    return ALC_TRUE;
+
+error:
+    ERR("%s failed: %s\n", funcerr, snd_strerror(err));
+    if(hp) snd_pcm_hw_params_free(hp);
+    return ALC_FALSE;
+}
+
+static void ALCplaybackAlsa_stop(ALCplaybackAlsa *self)
+{
+    int res;
+
+    if(self->killNow)
+        return;
+
+    self->killNow = 1;
+    althrd_join(self->thread, &res);
+
+    al_free(self->buffer);
+    self->buffer = NULL;
+}
+
+static ClockLatency ALCplaybackAlsa_getClockLatency(ALCplaybackAlsa *self)
+{
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+    snd_pcm_sframes_t delay = 0;
+    ClockLatency ret;
+    int err;
+
+    ALCplaybackAlsa_lock(self);
+    ret.ClockTime = GetDeviceClockTime(device);
+    if((err=snd_pcm_delay(self->pcmHandle, &delay)) < 0)
+    {
+        ERR("Failed to get pcm delay: %s\n", snd_strerror(err));
+        delay = 0;
+    }
+    if(delay < 0) delay = 0;
+    ret.Latency = delay * DEVICE_CLOCK_RES / device->Frequency;
+    ALCplaybackAlsa_unlock(self);
+
+    return ret;
+}
+
+
+typedef struct ALCcaptureAlsa {
+    DERIVE_FROM_TYPE(ALCbackend);
+
+    snd_pcm_t *pcmHandle;
+
+    ALvoid *buffer;
+    ALsizei size;
+
+    ALboolean doCapture;
+    ll_ringbuffer_t *ring;
+
+    snd_pcm_sframes_t last_avail;
+} ALCcaptureAlsa;
+
+static void ALCcaptureAlsa_Construct(ALCcaptureAlsa *self, ALCdevice *device);
+static DECLARE_FORWARD(ALCcaptureAlsa, ALCbackend, void, Destruct)
+static ALCenum ALCcaptureAlsa_open(ALCcaptureAlsa *self, const ALCchar *name);
+static void ALCcaptureAlsa_close(ALCcaptureAlsa *self);
+static DECLARE_FORWARD(ALCcaptureAlsa, ALCbackend, ALCboolean, reset)
+static ALCboolean ALCcaptureAlsa_start(ALCcaptureAlsa *self);
+static void ALCcaptureAlsa_stop(ALCcaptureAlsa *self);
+static ALCenum ALCcaptureAlsa_captureSamples(ALCcaptureAlsa *self, ALCvoid *buffer, ALCuint samples);
+static ALCuint ALCcaptureAlsa_availableSamples(ALCcaptureAlsa *self);
+static ClockLatency ALCcaptureAlsa_getClockLatency(ALCcaptureAlsa *self);
+static DECLARE_FORWARD(ALCcaptureAlsa, ALCbackend, void, lock)
+static DECLARE_FORWARD(ALCcaptureAlsa, ALCbackend, void, unlock)
+DECLARE_DEFAULT_ALLOCATORS(ALCcaptureAlsa)
+
+DEFINE_ALCBACKEND_VTABLE(ALCcaptureAlsa);
+
+
+static void ALCcaptureAlsa_Construct(ALCcaptureAlsa *self, ALCdevice *device)
+{
+    ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
+    SET_VTABLE2(ALCcaptureAlsa, ALCbackend, self);
+}
+
+
+static ALCenum ALCcaptureAlsa_open(ALCcaptureAlsa *self, const ALCchar *name)
+{
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+    const char *driver = NULL;
+    snd_pcm_hw_params_t *hp;
+    snd_pcm_uframes_t bufferSizeInFrames;
+    snd_pcm_uframes_t periodSizeInFrames;
+    ALboolean needring = AL_FALSE;
+    snd_pcm_format_t format = -1;
+    const char *funcerr;
+    int err;
+
+    if(name)
+    {
+        const DevMap *iter;
+
+        if(VECTOR_SIZE(CaptureDevices) == 0)
+            probe_devices(SND_PCM_STREAM_CAPTURE, &CaptureDevices);
+
+#define MATCH_NAME(i)  (al_string_cmp_cstr((i)->name, name) == 0)
+        VECTOR_FIND_IF(iter, const DevMap, CaptureDevices, MATCH_NAME);
+#undef MATCH_NAME
+        if(iter == VECTOR_END(CaptureDevices))
+            return ALC_INVALID_VALUE;
+        driver = al_string_get_cstr(iter->device_name);
+    }
+    else
+    {
+        name = alsaDevice;
+        driver = GetConfigValue(NULL, "alsa", "capture", "default");
+    }
+
+    TRACE("Opening device \"%s\"\n", driver);
+    err = snd_pcm_open(&self->pcmHandle, driver, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK);
+    if(err < 0)
+    {
+        ERR("Could not open capture device '%s': %s\n", driver, snd_strerror(err));
+        return ALC_INVALID_VALUE;
+    }
+
+    /* Free alsa's global config tree. Otherwise valgrind reports a ton of leaks. */
+    snd_config_update_free_global();
+
+    switch(device->FmtType)
+    {
+        case DevFmtByte:
+            format = SND_PCM_FORMAT_S8;
+            break;
+        case DevFmtUByte:
+            format = SND_PCM_FORMAT_U8;
+            break;
+        case DevFmtShort:
+            format = SND_PCM_FORMAT_S16;
+            break;
+        case DevFmtUShort:
+            format = SND_PCM_FORMAT_U16;
+            break;
+        case DevFmtInt:
+            format = SND_PCM_FORMAT_S32;
+            break;
+        case DevFmtUInt:
+            format = SND_PCM_FORMAT_U32;
+            break;
+        case DevFmtFloat:
+            format = SND_PCM_FORMAT_FLOAT;
+            break;
+    }
+
+    funcerr = NULL;
+    bufferSizeInFrames = maxu(device->UpdateSize*device->NumUpdates,
+                              100*device->Frequency/1000);
+    periodSizeInFrames = minu(bufferSizeInFrames, 25*device->Frequency/1000);
+
+    snd_pcm_hw_params_malloc(&hp);
+#define CHECK(x) if((funcerr=#x),(err=(x)) < 0) goto error
+    CHECK(snd_pcm_hw_params_any(self->pcmHandle, hp));
+    /* set interleaved access */
+    CHECK(snd_pcm_hw_params_set_access(self->pcmHandle, hp, SND_PCM_ACCESS_RW_INTERLEAVED));
+    /* set format (implicitly sets sample bits) */
+    CHECK(snd_pcm_hw_params_set_format(self->pcmHandle, hp, format));
+    /* set channels (implicitly sets frame bits) */
+    CHECK(snd_pcm_hw_params_set_channels(self->pcmHandle, hp, ChannelsFromDevFmt(device->FmtChans)));
+    /* set rate (implicitly constrains period/buffer parameters) */
+    CHECK(snd_pcm_hw_params_set_rate(self->pcmHandle, hp, device->Frequency, 0));
+    /* set buffer size in frame units (implicitly sets period size/bytes/time and buffer time/bytes) */
+    if(snd_pcm_hw_params_set_buffer_size_min(self->pcmHandle, hp, &bufferSizeInFrames) < 0)
+    {
+        TRACE("Buffer too large, using intermediate ring buffer\n");
+        needring = AL_TRUE;
+        CHECK(snd_pcm_hw_params_set_buffer_size_near(self->pcmHandle, hp, &bufferSizeInFrames));
+    }
+    /* set buffer size in frame units (implicitly sets period size/bytes/time and buffer time/bytes) */
+    CHECK(snd_pcm_hw_params_set_period_size_near(self->pcmHandle, hp, &periodSizeInFrames, NULL));
+    /* install and prepare hardware configuration */
+    CHECK(snd_pcm_hw_params(self->pcmHandle, hp));
+    /* retrieve configuration info */
+    CHECK(snd_pcm_hw_params_get_period_size(hp, &periodSizeInFrames, NULL));
+#undef CHECK
+    snd_pcm_hw_params_free(hp);
+    hp = NULL;
+
+    if(needring)
+    {
+        self->ring = ll_ringbuffer_create(
+            device->UpdateSize*device->NumUpdates + 1,
+            FrameSizeFromDevFmt(device->FmtChans, device->FmtType)
+        );
+        if(!self->ring)
+        {
+            ERR("ring buffer create failed\n");
+            goto error2;
+        }
+    }
+
+    al_string_copy_cstr(&device->DeviceName, name);
+
+    return ALC_NO_ERROR;
+
+error:
+    ERR("%s failed: %s\n", funcerr, snd_strerror(err));
+    if(hp) snd_pcm_hw_params_free(hp);
+
+error2:
+    ll_ringbuffer_free(self->ring);
+    self->ring = NULL;
+    snd_pcm_close(self->pcmHandle);
+
+    return ALC_INVALID_VALUE;
+}
+
+static void ALCcaptureAlsa_close(ALCcaptureAlsa *self)
+{
+    snd_pcm_close(self->pcmHandle);
+    ll_ringbuffer_free(self->ring);
+
+    al_free(self->buffer);
+    self->buffer = NULL;
+}
+
+static ALCboolean ALCcaptureAlsa_start(ALCcaptureAlsa *self)
+{
+    int err = snd_pcm_start(self->pcmHandle);
+    if(err < 0)
+    {
+        ERR("start failed: %s\n", snd_strerror(err));
+        aluHandleDisconnect(STATIC_CAST(ALCbackend, self)->mDevice);
+        return ALC_FALSE;
+    }
+
+    self->doCapture = AL_TRUE;
+    return ALC_TRUE;
+}
+
+static void ALCcaptureAlsa_stop(ALCcaptureAlsa *self)
+{
+    ALCuint avail;
+    int err;
+
+    /* OpenAL requires access to unread audio after stopping, but ALSA's
+     * snd_pcm_drain is unreliable and snd_pcm_drop drops it. Capture what's
+     * available now so it'll be available later after the drop. */
+    avail = ALCcaptureAlsa_availableSamples(self);
+    if(!self->ring && avail > 0)
+    {
+        /* The ring buffer implicitly captures when checking availability.
+         * Direct access needs to explicitly capture it into temp storage. */
+        ALsizei size;
+        void *ptr;
+
+        size = snd_pcm_frames_to_bytes(self->pcmHandle, avail);
+        ptr = al_malloc(16, size);
+        if(ptr)
+        {
+            ALCcaptureAlsa_captureSamples(self, ptr, avail);
+            al_free(self->buffer);
+            self->buffer = ptr;
+            self->size = size;
+        }
+    }
+    err = snd_pcm_drop(self->pcmHandle);
+    if(err < 0)
+        ERR("drop failed: %s\n", snd_strerror(err));
+    self->doCapture = AL_FALSE;
+}
+
+static ALCenum ALCcaptureAlsa_captureSamples(ALCcaptureAlsa *self, ALCvoid *buffer, ALCuint samples)
+{
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+
+    if(self->ring)
+    {
+        ll_ringbuffer_read(self->ring, buffer, samples);
+        return ALC_NO_ERROR;
+    }
+
+    self->last_avail -= samples;
+    while(device->Connected && samples > 0)
+    {
+        snd_pcm_sframes_t amt = 0;
+
+        if(self->size > 0)
+        {
+            /* First get any data stored from the last stop */
+            amt = snd_pcm_bytes_to_frames(self->pcmHandle, self->size);
+            if((snd_pcm_uframes_t)amt > samples) amt = samples;
+
+            amt = snd_pcm_frames_to_bytes(self->pcmHandle, amt);
+            memcpy(buffer, self->buffer, amt);
+
+            if(self->size > amt)
+            {
+                memmove(self->buffer, self->buffer+amt, self->size - amt);
+                self->size -= amt;
+            }
+            else
+            {
+                al_free(self->buffer);
+                self->buffer = NULL;
+                self->size = 0;
+            }
+            amt = snd_pcm_bytes_to_frames(self->pcmHandle, amt);
+        }
+        else if(self->doCapture)
+            amt = snd_pcm_readi(self->pcmHandle, buffer, samples);
+        if(amt < 0)
+        {
+            ERR("read error: %s\n", snd_strerror(amt));
+
+            if(amt == -EAGAIN)
+                continue;
+            if((amt=snd_pcm_recover(self->pcmHandle, amt, 1)) >= 0)
+            {
+                amt = snd_pcm_start(self->pcmHandle);
+                if(amt >= 0)
+                    amt = snd_pcm_avail_update(self->pcmHandle);
+            }
+            if(amt < 0)
+            {
+                ERR("restore error: %s\n", snd_strerror(amt));
+                aluHandleDisconnect(device);
+                break;
+            }
+            /* If the amount available is less than what's asked, we lost it
+             * during recovery. So just give silence instead. */
+            if((snd_pcm_uframes_t)amt < samples)
+                break;
+            continue;
+        }
+
+        buffer = (ALbyte*)buffer + amt;
+        samples -= amt;
+    }
+    if(samples > 0)
+        memset(buffer, ((device->FmtType == DevFmtUByte) ? 0x80 : 0),
+               snd_pcm_frames_to_bytes(self->pcmHandle, samples));
+
+    return ALC_NO_ERROR;
+}
+
+static ALCuint ALCcaptureAlsa_availableSamples(ALCcaptureAlsa *self)
+{
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+    snd_pcm_sframes_t avail = 0;
+
+    if(device->Connected && self->doCapture)
+        avail = snd_pcm_avail_update(self->pcmHandle);
+    if(avail < 0)
+    {
+        ERR("avail update failed: %s\n", snd_strerror(avail));
+
+        if((avail=snd_pcm_recover(self->pcmHandle, avail, 1)) >= 0)
+        {
+            if(self->doCapture)
+                avail = snd_pcm_start(self->pcmHandle);
+            if(avail >= 0)
+                avail = snd_pcm_avail_update(self->pcmHandle);
+        }
+        if(avail < 0)
+        {
+            ERR("restore error: %s\n", snd_strerror(avail));
+            aluHandleDisconnect(device);
+        }
+    }
+
+    if(!self->ring)
+    {
+        if(avail < 0) avail = 0;
+        avail += snd_pcm_bytes_to_frames(self->pcmHandle, self->size);
+        if(avail > self->last_avail) self->last_avail = avail;
+        return self->last_avail;
+    }
+
+    while(avail > 0)
+    {
+        ll_ringbuffer_data_t vec[2];
+        snd_pcm_sframes_t amt;
+
+        ll_ringbuffer_get_write_vector(self->ring, vec);
+        if(vec[0].len == 0) break;
+
+        amt = (vec[0].len < (snd_pcm_uframes_t)avail) ?
+              vec[0].len : (snd_pcm_uframes_t)avail;
+        amt = snd_pcm_readi(self->pcmHandle, vec[0].buf, amt);
+        if(amt < 0)
+        {
+            ERR("read error: %s\n", snd_strerror(amt));
+
+            if(amt == -EAGAIN)
+                continue;
+            if((amt=snd_pcm_recover(self->pcmHandle, amt, 1)) >= 0)
+            {
+                if(self->doCapture)
+                    amt = snd_pcm_start(self->pcmHandle);
+                if(amt >= 0)
+                    amt = snd_pcm_avail_update(self->pcmHandle);
+            }
+            if(amt < 0)
+            {
+                ERR("restore error: %s\n", snd_strerror(amt));
+                aluHandleDisconnect(device);
+                break;
+            }
+            avail = amt;
+            continue;
+        }
+
+        ll_ringbuffer_write_advance(self->ring, amt);
+        avail -= amt;
+    }
+
+    return ll_ringbuffer_read_space(self->ring);
+}
+
+static ClockLatency ALCcaptureAlsa_getClockLatency(ALCcaptureAlsa *self)
+{
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+    snd_pcm_sframes_t delay = 0;
+    ClockLatency ret;
+    int err;
+
+    ALCcaptureAlsa_lock(self);
+    ret.ClockTime = GetDeviceClockTime(device);
+    if((err=snd_pcm_delay(self->pcmHandle, &delay)) < 0)
+    {
+        ERR("Failed to get pcm delay: %s\n", snd_strerror(err));
+        delay = 0;
+    }
+    if(delay < 0) delay = 0;
+    ret.Latency = delay * DEVICE_CLOCK_RES / device->Frequency;
+    ALCcaptureAlsa_unlock(self);
+
+    return ret;
+}
+
+
+static inline void AppendAllDevicesList2(const DevMap *entry)
+{ AppendAllDevicesList(al_string_get_cstr(entry->name)); }
+static inline void AppendCaptureDeviceList2(const DevMap *entry)
+{ AppendCaptureDeviceList(al_string_get_cstr(entry->name)); }
+
+typedef struct ALCalsaBackendFactory {
+    DERIVE_FROM_TYPE(ALCbackendFactory);
+} ALCalsaBackendFactory;
+#define ALCALSABACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCalsaBackendFactory, ALCbackendFactory) } }
+
+static ALCboolean ALCalsaBackendFactory_init(ALCalsaBackendFactory* UNUSED(self))
+{
+    VECTOR_INIT(PlaybackDevices);
+    VECTOR_INIT(CaptureDevices);
+
+    if(!alsa_load())
+        return ALC_FALSE;
+    return ALC_TRUE;
+}
+
+static void ALCalsaBackendFactory_deinit(ALCalsaBackendFactory* UNUSED(self))
+{
+    clear_devlist(&PlaybackDevices);
+    VECTOR_DEINIT(PlaybackDevices);
+
+    clear_devlist(&CaptureDevices);
+    VECTOR_DEINIT(CaptureDevices);
+
+#ifdef HAVE_DYNLOAD
+    if(alsa_handle)
+        CloseLib(alsa_handle);
+    alsa_handle = NULL;
+#endif
+}
+
+static ALCboolean ALCalsaBackendFactory_querySupport(ALCalsaBackendFactory* UNUSED(self), ALCbackend_Type type)
+{
+    if(type == ALCbackend_Playback || type == ALCbackend_Capture)
+        return ALC_TRUE;
+    return ALC_FALSE;
+}
+
+static void ALCalsaBackendFactory_probe(ALCalsaBackendFactory* UNUSED(self), enum DevProbe type)
+{
+    switch(type)
+    {
+        case ALL_DEVICE_PROBE:
+            probe_devices(SND_PCM_STREAM_PLAYBACK, &PlaybackDevices);
+            VECTOR_FOR_EACH(const DevMap, PlaybackDevices, AppendAllDevicesList2);
+            break;
+
+        case CAPTURE_DEVICE_PROBE:
+            probe_devices(SND_PCM_STREAM_CAPTURE, &CaptureDevices);
+            VECTOR_FOR_EACH(const DevMap, CaptureDevices, AppendCaptureDeviceList2);
+            break;
+    }
+}
+
+static ALCbackend* ALCalsaBackendFactory_createBackend(ALCalsaBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
+{
+    if(type == ALCbackend_Playback)
+    {
+        ALCplaybackAlsa *backend;
+        NEW_OBJ(backend, ALCplaybackAlsa)(device);
+        if(!backend) return NULL;
+        return STATIC_CAST(ALCbackend, backend);
+    }
+    if(type == ALCbackend_Capture)
+    {
+        ALCcaptureAlsa *backend;
+        NEW_OBJ(backend, ALCcaptureAlsa)(device);
+        if(!backend) return NULL;
+        return STATIC_CAST(ALCbackend, backend);
+    }
+
+    return NULL;
+}
+
+DEFINE_ALCBACKENDFACTORY_VTABLE(ALCalsaBackendFactory);
+
+
+ALCbackendFactory *ALCalsaBackendFactory_getFactory(void)
+{
+    static ALCalsaBackendFactory factory = ALCALSABACKENDFACTORY_INITIALIZER;
+    return STATIC_CAST(ALCbackendFactory, &factory);
+}

+ 225 - 0
Engine/lib/openal-soft/Alc/backends/base.c

@@ -0,0 +1,225 @@
+
+#include "config.h"
+
+#include <stdlib.h>
+
+#include "alMain.h"
+
+#include "backends/base.h"
+
+
+extern inline ALuint64 GetDeviceClockTime(ALCdevice *device);
+
+/* Base ALCbackend method implementations. */
+void ALCbackend_Construct(ALCbackend *self, ALCdevice *device)
+{
+    int ret = almtx_init(&self->mMutex, almtx_recursive);
+    assert(ret == althrd_success);
+    self->mDevice = device;
+}
+
+void ALCbackend_Destruct(ALCbackend *self)
+{
+    almtx_destroy(&self->mMutex);
+}
+
+ALCboolean ALCbackend_reset(ALCbackend* UNUSED(self))
+{
+    return ALC_FALSE;
+}
+
+ALCenum ALCbackend_captureSamples(ALCbackend* UNUSED(self), void* UNUSED(buffer), ALCuint UNUSED(samples))
+{
+    return ALC_INVALID_DEVICE;
+}
+
+ALCuint ALCbackend_availableSamples(ALCbackend* UNUSED(self))
+{
+    return 0;
+}
+
+ClockLatency ALCbackend_getClockLatency(ALCbackend *self)
+{
+    ALCdevice *device = self->mDevice;
+    ClockLatency ret;
+
+    almtx_lock(&self->mMutex);
+    ret.ClockTime = GetDeviceClockTime(device);
+    // TODO: Perhaps should be NumUpdates-1 worth of UpdateSize?
+    ret.Latency = 0;
+    almtx_unlock(&self->mMutex);
+
+    return ret;
+}
+
+void ALCbackend_lock(ALCbackend *self)
+{
+    int ret = almtx_lock(&self->mMutex);
+    assert(ret == althrd_success);
+}
+
+void ALCbackend_unlock(ALCbackend *self)
+{
+    int ret = almtx_unlock(&self->mMutex);
+    assert(ret == althrd_success);
+}
+
+
+/* Base ALCbackendFactory method implementations. */
+void ALCbackendFactory_deinit(ALCbackendFactory* UNUSED(self))
+{
+}
+
+
+/* Wrappers to use an old-style backend with the new interface. */
+typedef struct PlaybackWrapper {
+    DERIVE_FROM_TYPE(ALCbackend);
+
+    const BackendFuncs *Funcs;
+} PlaybackWrapper;
+
+static void PlaybackWrapper_Construct(PlaybackWrapper *self, ALCdevice *device, const BackendFuncs *funcs);
+static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, void, Destruct)
+static ALCenum PlaybackWrapper_open(PlaybackWrapper *self, const ALCchar *name);
+static void PlaybackWrapper_close(PlaybackWrapper *self);
+static ALCboolean PlaybackWrapper_reset(PlaybackWrapper *self);
+static ALCboolean PlaybackWrapper_start(PlaybackWrapper *self);
+static void PlaybackWrapper_stop(PlaybackWrapper *self);
+static DECLARE_FORWARD2(PlaybackWrapper, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
+static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, ALCuint, availableSamples)
+static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, ClockLatency, getClockLatency)
+static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, void, lock)
+static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, void, unlock)
+DECLARE_DEFAULT_ALLOCATORS(PlaybackWrapper)
+DEFINE_ALCBACKEND_VTABLE(PlaybackWrapper);
+
+static void PlaybackWrapper_Construct(PlaybackWrapper *self, ALCdevice *device, const BackendFuncs *funcs)
+{
+    ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
+    SET_VTABLE2(PlaybackWrapper, ALCbackend, self);
+
+    self->Funcs = funcs;
+}
+
+static ALCenum PlaybackWrapper_open(PlaybackWrapper *self, const ALCchar *name)
+{
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+    return self->Funcs->OpenPlayback(device, name);
+}
+
+static void PlaybackWrapper_close(PlaybackWrapper *self)
+{
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+    self->Funcs->ClosePlayback(device);
+}
+
+static ALCboolean PlaybackWrapper_reset(PlaybackWrapper *self)
+{
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+    return self->Funcs->ResetPlayback(device);
+}
+
+static ALCboolean PlaybackWrapper_start(PlaybackWrapper *self)
+{
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+    return self->Funcs->StartPlayback(device);
+}
+
+static void PlaybackWrapper_stop(PlaybackWrapper *self)
+{
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+    self->Funcs->StopPlayback(device);
+}
+
+
+typedef struct CaptureWrapper {
+    DERIVE_FROM_TYPE(ALCbackend);
+
+    const BackendFuncs *Funcs;
+} CaptureWrapper;
+
+static void CaptureWrapper_Construct(CaptureWrapper *self, ALCdevice *device, const BackendFuncs *funcs);
+static DECLARE_FORWARD(CaptureWrapper, ALCbackend, void, Destruct)
+static ALCenum CaptureWrapper_open(CaptureWrapper *self, const ALCchar *name);
+static void CaptureWrapper_close(CaptureWrapper *self);
+static DECLARE_FORWARD(CaptureWrapper, ALCbackend, ALCboolean, reset)
+static ALCboolean CaptureWrapper_start(CaptureWrapper *self);
+static void CaptureWrapper_stop(CaptureWrapper *self);
+static ALCenum CaptureWrapper_captureSamples(CaptureWrapper *self, void *buffer, ALCuint samples);
+static ALCuint CaptureWrapper_availableSamples(CaptureWrapper *self);
+static DECLARE_FORWARD(CaptureWrapper, ALCbackend, ClockLatency, getClockLatency)
+static DECLARE_FORWARD(CaptureWrapper, ALCbackend, void, lock)
+static DECLARE_FORWARD(CaptureWrapper, ALCbackend, void, unlock)
+DECLARE_DEFAULT_ALLOCATORS(CaptureWrapper)
+DEFINE_ALCBACKEND_VTABLE(CaptureWrapper);
+
+static void CaptureWrapper_Construct(CaptureWrapper *self, ALCdevice *device, const BackendFuncs *funcs)
+{
+    ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
+    SET_VTABLE2(CaptureWrapper, ALCbackend, self);
+
+    self->Funcs = funcs;
+}
+
+static ALCenum CaptureWrapper_open(CaptureWrapper *self, const ALCchar *name)
+{
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+    return self->Funcs->OpenCapture(device, name);
+}
+
+static void CaptureWrapper_close(CaptureWrapper *self)
+{
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+    self->Funcs->CloseCapture(device);
+}
+
+static ALCboolean CaptureWrapper_start(CaptureWrapper *self)
+{
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+    self->Funcs->StartCapture(device);
+    return ALC_TRUE;
+}
+
+static void CaptureWrapper_stop(CaptureWrapper *self)
+{
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+    self->Funcs->StopCapture(device);
+}
+
+static ALCenum CaptureWrapper_captureSamples(CaptureWrapper *self, void *buffer, ALCuint samples)
+{
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+    return self->Funcs->CaptureSamples(device, buffer, samples);
+}
+
+static ALCuint CaptureWrapper_availableSamples(CaptureWrapper *self)
+{
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+    return self->Funcs->AvailableSamples(device);
+}
+
+
+ALCbackend *create_backend_wrapper(ALCdevice *device, const BackendFuncs *funcs, ALCbackend_Type type)
+{
+    if(type == ALCbackend_Playback)
+    {
+        PlaybackWrapper *backend;
+
+        NEW_OBJ(backend, PlaybackWrapper)(device, funcs);
+        if(!backend) return NULL;
+
+        return STATIC_CAST(ALCbackend, backend);
+    }
+
+    if(type == ALCbackend_Capture)
+    {
+        CaptureWrapper *backend;
+
+        NEW_OBJ(backend, CaptureWrapper)(device, funcs);
+        if(!backend) return NULL;
+
+        return STATIC_CAST(ALCbackend, backend);
+    }
+
+    return NULL;
+}

+ 153 - 0
Engine/lib/openal-soft/Alc/backends/base.h

@@ -0,0 +1,153 @@
+#ifndef AL_BACKENDS_BASE_H
+#define AL_BACKENDS_BASE_H
+
+#include "alMain.h"
+#include "threads.h"
+
+
+typedef struct ClockLatency {
+    ALint64 ClockTime;
+    ALint64 Latency;
+} ClockLatency;
+
+/* Helper to get the current clock time from the device's ClockBase, and
+ * SamplesDone converted from the sample rate.
+ */
+inline ALuint64 GetDeviceClockTime(ALCdevice *device)
+{
+    return device->ClockBase + (device->SamplesDone * DEVICE_CLOCK_RES /
+                                device->Frequency);
+}
+
+
+struct ALCbackendVtable;
+
+typedef struct ALCbackend {
+    const struct ALCbackendVtable *vtbl;
+
+    ALCdevice *mDevice;
+
+    almtx_t mMutex;
+} ALCbackend;
+
+void ALCbackend_Construct(ALCbackend *self, ALCdevice *device);
+void ALCbackend_Destruct(ALCbackend *self);
+ALCboolean ALCbackend_reset(ALCbackend *self);
+ALCenum ALCbackend_captureSamples(ALCbackend *self, void *buffer, ALCuint samples);
+ALCuint ALCbackend_availableSamples(ALCbackend *self);
+ClockLatency ALCbackend_getClockLatency(ALCbackend *self);
+void ALCbackend_lock(ALCbackend *self);
+void ALCbackend_unlock(ALCbackend *self);
+
+struct ALCbackendVtable {
+    void (*const Destruct)(ALCbackend*);
+
+    ALCenum (*const open)(ALCbackend*, const ALCchar*);
+    void (*const close)(ALCbackend*);
+
+    ALCboolean (*const reset)(ALCbackend*);
+    ALCboolean (*const start)(ALCbackend*);
+    void (*const stop)(ALCbackend*);
+
+    ALCenum (*const captureSamples)(ALCbackend*, void*, ALCuint);
+    ALCuint (*const availableSamples)(ALCbackend*);
+
+    ClockLatency (*const getClockLatency)(ALCbackend*);
+
+    void (*const lock)(ALCbackend*);
+    void (*const unlock)(ALCbackend*);
+
+    void (*const Delete)(void*);
+};
+
+#define DEFINE_ALCBACKEND_VTABLE(T)                                           \
+DECLARE_THUNK(T, ALCbackend, void, Destruct)                                  \
+DECLARE_THUNK1(T, ALCbackend, ALCenum, open, const ALCchar*)                  \
+DECLARE_THUNK(T, ALCbackend, void, close)                                     \
+DECLARE_THUNK(T, ALCbackend, ALCboolean, reset)                               \
+DECLARE_THUNK(T, ALCbackend, ALCboolean, start)                               \
+DECLARE_THUNK(T, ALCbackend, void, stop)                                      \
+DECLARE_THUNK2(T, ALCbackend, ALCenum, captureSamples, void*, ALCuint)        \
+DECLARE_THUNK(T, ALCbackend, ALCuint, availableSamples)                       \
+DECLARE_THUNK(T, ALCbackend, ClockLatency, getClockLatency)                   \
+DECLARE_THUNK(T, ALCbackend, void, lock)                                      \
+DECLARE_THUNK(T, ALCbackend, void, unlock)                                    \
+static void T##_ALCbackend_Delete(void *ptr)                                  \
+{ T##_Delete(STATIC_UPCAST(T, ALCbackend, (ALCbackend*)ptr)); }               \
+                                                                              \
+static const struct ALCbackendVtable T##_ALCbackend_vtable = {                \
+    T##_ALCbackend_Destruct,                                                  \
+                                                                              \
+    T##_ALCbackend_open,                                                      \
+    T##_ALCbackend_close,                                                     \
+    T##_ALCbackend_reset,                                                     \
+    T##_ALCbackend_start,                                                     \
+    T##_ALCbackend_stop,                                                      \
+    T##_ALCbackend_captureSamples,                                            \
+    T##_ALCbackend_availableSamples,                                          \
+    T##_ALCbackend_getClockLatency,                                           \
+    T##_ALCbackend_lock,                                                      \
+    T##_ALCbackend_unlock,                                                    \
+                                                                              \
+    T##_ALCbackend_Delete,                                                    \
+}
+
+
+typedef enum ALCbackend_Type {
+    ALCbackend_Playback,
+    ALCbackend_Capture,
+    ALCbackend_Loopback
+} ALCbackend_Type;
+
+
+struct ALCbackendFactoryVtable;
+
+typedef struct ALCbackendFactory {
+    const struct ALCbackendFactoryVtable *vtbl;
+} ALCbackendFactory;
+
+void ALCbackendFactory_deinit(ALCbackendFactory *self);
+
+struct ALCbackendFactoryVtable {
+    ALCboolean (*const init)(ALCbackendFactory *self);
+    void (*const deinit)(ALCbackendFactory *self);
+
+    ALCboolean (*const querySupport)(ALCbackendFactory *self, ALCbackend_Type type);
+
+    void (*const probe)(ALCbackendFactory *self, enum DevProbe type);
+
+    ALCbackend* (*const createBackend)(ALCbackendFactory *self, ALCdevice *device, ALCbackend_Type type);
+};
+
+#define DEFINE_ALCBACKENDFACTORY_VTABLE(T)                                    \
+DECLARE_THUNK(T, ALCbackendFactory, ALCboolean, init)                         \
+DECLARE_THUNK(T, ALCbackendFactory, void, deinit)                             \
+DECLARE_THUNK1(T, ALCbackendFactory, ALCboolean, querySupport, ALCbackend_Type) \
+DECLARE_THUNK1(T, ALCbackendFactory, void, probe, enum DevProbe)              \
+DECLARE_THUNK2(T, ALCbackendFactory, ALCbackend*, createBackend, ALCdevice*, ALCbackend_Type) \
+                                                                              \
+static const struct ALCbackendFactoryVtable T##_ALCbackendFactory_vtable = {  \
+    T##_ALCbackendFactory_init,                                               \
+    T##_ALCbackendFactory_deinit,                                             \
+    T##_ALCbackendFactory_querySupport,                                       \
+    T##_ALCbackendFactory_probe,                                              \
+    T##_ALCbackendFactory_createBackend,                                      \
+}
+
+
+ALCbackendFactory *ALCpulseBackendFactory_getFactory(void);
+ALCbackendFactory *ALCalsaBackendFactory_getFactory(void);
+ALCbackendFactory *ALCossBackendFactory_getFactory(void);
+ALCbackendFactory *ALCjackBackendFactory_getFactory(void);
+ALCbackendFactory *ALCsolarisBackendFactory_getFactory(void);
+ALCbackendFactory *ALCmmdevBackendFactory_getFactory(void);
+ALCbackendFactory *ALCdsoundBackendFactory_getFactory(void);
+ALCbackendFactory *ALCwinmmBackendFactory_getFactory(void);
+ALCbackendFactory *ALCportBackendFactory_getFactory(void);
+ALCbackendFactory *ALCnullBackendFactory_getFactory(void);
+ALCbackendFactory *ALCwaveBackendFactory_getFactory(void);
+ALCbackendFactory *ALCloopbackFactory_getFactory(void);
+
+ALCbackend *create_backend_wrapper(ALCdevice *device, const BackendFuncs *funcs, ALCbackend_Type type);
+
+#endif /* AL_BACKENDS_BASE_H */

+ 724 - 0
Engine/lib/openal-soft/Alc/backends/coreaudio.c

@@ -0,0 +1,724 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 1999-2007 by authors.
+ * This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the
+ *  Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <alloca.h>
+
+#include "alMain.h"
+#include "alu.h"
+
+#include <CoreServices/CoreServices.h>
+#include <unistd.h>
+#include <AudioUnit/AudioUnit.h>
+#include <AudioToolbox/AudioToolbox.h>
+
+
+typedef struct {
+    AudioUnit audioUnit;
+
+    ALuint frameSize;
+    ALdouble sampleRateRatio;              // Ratio of hardware sample rate / requested sample rate
+    AudioStreamBasicDescription format;    // This is the OpenAL format as a CoreAudio ASBD
+
+    AudioConverterRef audioConverter;      // Sample rate converter if needed
+    AudioBufferList *bufferList;           // Buffer for data coming from the input device
+    ALCvoid *resampleBuffer;               // Buffer for returned RingBuffer data when resampling
+
+    ll_ringbuffer_t *ring;
+} ca_data;
+
+static const ALCchar ca_device[] = "CoreAudio Default";
+
+
+static void destroy_buffer_list(AudioBufferList* list)
+{
+    if(list)
+    {
+        UInt32 i;
+        for(i = 0;i < list->mNumberBuffers;i++)
+            free(list->mBuffers[i].mData);
+        free(list);
+    }
+}
+
+static AudioBufferList* allocate_buffer_list(UInt32 channelCount, UInt32 byteSize)
+{
+    AudioBufferList *list;
+
+    list = calloc(1, sizeof(AudioBufferList) + sizeof(AudioBuffer));
+    if(list)
+    {
+        list->mNumberBuffers = 1;
+
+        list->mBuffers[0].mNumberChannels = channelCount;
+        list->mBuffers[0].mDataByteSize = byteSize;
+        list->mBuffers[0].mData = malloc(byteSize);
+        if(list->mBuffers[0].mData == NULL)
+        {
+            free(list);
+            list = NULL;
+        }
+    }
+    return list;
+}
+
+static OSStatus ca_callback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp,
+                            UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData)
+{
+    ALCdevice *device = (ALCdevice*)inRefCon;
+    ca_data *data = (ca_data*)device->ExtraData;
+
+    aluMixData(device, ioData->mBuffers[0].mData,
+               ioData->mBuffers[0].mDataByteSize / data->frameSize);
+
+    return noErr;
+}
+
+static OSStatus ca_capture_conversion_callback(AudioConverterRef inAudioConverter, UInt32 *ioNumberDataPackets,
+        AudioBufferList *ioData, AudioStreamPacketDescription **outDataPacketDescription, void* inUserData)
+{
+    ALCdevice *device = (ALCdevice*)inUserData;
+    ca_data *data = (ca_data*)device->ExtraData;
+
+    // Read from the ring buffer and store temporarily in a large buffer
+    ll_ringbuffer_read(data->ring, data->resampleBuffer, *ioNumberDataPackets);
+
+    // Set the input data
+    ioData->mNumberBuffers = 1;
+    ioData->mBuffers[0].mNumberChannels = data->format.mChannelsPerFrame;
+    ioData->mBuffers[0].mData = data->resampleBuffer;
+    ioData->mBuffers[0].mDataByteSize = (*ioNumberDataPackets) * data->format.mBytesPerFrame;
+
+    return noErr;
+}
+
+static OSStatus ca_capture_callback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags,
+                                    const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber,
+                                    UInt32 inNumberFrames, AudioBufferList *ioData)
+{
+    ALCdevice *device = (ALCdevice*)inRefCon;
+    ca_data *data = (ca_data*)device->ExtraData;
+    AudioUnitRenderActionFlags flags = 0;
+    OSStatus err;
+
+    // fill the bufferList with data from the input device
+    err = AudioUnitRender(data->audioUnit, &flags, inTimeStamp, 1, inNumberFrames, data->bufferList);
+    if(err != noErr)
+    {
+        ERR("AudioUnitRender error: %d\n", err);
+        return err;
+    }
+
+    ll_ringbuffer_write(data->ring, data->bufferList->mBuffers[0].mData, inNumberFrames);
+
+    return noErr;
+}
+
+static ALCenum ca_open_playback(ALCdevice *device, const ALCchar *deviceName)
+{
+    AudioComponentDescription desc;
+    AudioComponent comp;
+    ca_data *data;
+    OSStatus err;
+
+    if(!deviceName)
+        deviceName = ca_device;
+    else if(strcmp(deviceName, ca_device) != 0)
+        return ALC_INVALID_VALUE;
+
+    /* open the default output unit */
+    desc.componentType = kAudioUnitType_Output;
+    desc.componentSubType = kAudioUnitSubType_DefaultOutput;
+    desc.componentManufacturer = kAudioUnitManufacturer_Apple;
+    desc.componentFlags = 0;
+    desc.componentFlagsMask = 0;
+
+    comp = AudioComponentFindNext(NULL, &desc);
+    if(comp == NULL)
+    {
+        ERR("AudioComponentFindNext failed\n");
+        return ALC_INVALID_VALUE;
+    }
+
+    data = calloc(1, sizeof(*data));
+
+    err = AudioComponentInstanceNew(comp, &data->audioUnit);
+    if(err != noErr)
+    {
+        ERR("AudioComponentInstanceNew failed\n");
+        free(data);
+        return ALC_INVALID_VALUE;
+    }
+
+    /* init and start the default audio unit... */
+    err = AudioUnitInitialize(data->audioUnit);
+    if(err != noErr)
+    {
+        ERR("AudioUnitInitialize failed\n");
+        AudioComponentInstanceDispose(data->audioUnit);
+        free(data);
+        return ALC_INVALID_VALUE;
+    }
+
+    al_string_copy_cstr(&device->DeviceName, deviceName);
+    device->ExtraData = data;
+    return ALC_NO_ERROR;
+}
+
+static void ca_close_playback(ALCdevice *device)
+{
+    ca_data *data = (ca_data*)device->ExtraData;
+
+    AudioUnitUninitialize(data->audioUnit);
+    AudioComponentInstanceDispose(data->audioUnit);
+
+    free(data);
+    device->ExtraData = NULL;
+}
+
+static ALCboolean ca_reset_playback(ALCdevice *device)
+{
+    ca_data *data = (ca_data*)device->ExtraData;
+    AudioStreamBasicDescription streamFormat;
+    AURenderCallbackStruct input;
+    OSStatus err;
+    UInt32 size;
+
+    err = AudioUnitUninitialize(data->audioUnit);
+    if(err != noErr)
+        ERR("-- AudioUnitUninitialize failed.\n");
+
+    /* retrieve default output unit's properties (output side) */
+    size = sizeof(AudioStreamBasicDescription);
+    err = AudioUnitGetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &streamFormat, &size);
+    if(err != noErr || size != sizeof(AudioStreamBasicDescription))
+    {
+        ERR("AudioUnitGetProperty failed\n");
+        return ALC_FALSE;
+    }
+
+#if 0
+    TRACE("Output streamFormat of default output unit -\n");
+    TRACE("  streamFormat.mFramesPerPacket = %d\n", streamFormat.mFramesPerPacket);
+    TRACE("  streamFormat.mChannelsPerFrame = %d\n", streamFormat.mChannelsPerFrame);
+    TRACE("  streamFormat.mBitsPerChannel = %d\n", streamFormat.mBitsPerChannel);
+    TRACE("  streamFormat.mBytesPerPacket = %d\n", streamFormat.mBytesPerPacket);
+    TRACE("  streamFormat.mBytesPerFrame = %d\n", streamFormat.mBytesPerFrame);
+    TRACE("  streamFormat.mSampleRate = %5.0f\n", streamFormat.mSampleRate);
+#endif
+
+    /* set default output unit's input side to match output side */
+    err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &streamFormat, size);
+    if(err != noErr)
+    {
+        ERR("AudioUnitSetProperty failed\n");
+        return ALC_FALSE;
+    }
+
+    if(device->Frequency != streamFormat.mSampleRate)
+    {
+        device->NumUpdates = (ALuint)((ALuint64)device->NumUpdates *
+                                      streamFormat.mSampleRate /
+                                      device->Frequency);
+        device->Frequency = streamFormat.mSampleRate;
+    }
+
+    /* FIXME: How to tell what channels are what in the output device, and how
+     * to specify what we're giving?  eg, 6.0 vs 5.1 */
+    switch(streamFormat.mChannelsPerFrame)
+    {
+        case 1:
+            device->FmtChans = DevFmtMono;
+            break;
+        case 2:
+            device->FmtChans = DevFmtStereo;
+            break;
+        case 4:
+            device->FmtChans = DevFmtQuad;
+            break;
+        case 6:
+            device->FmtChans = DevFmtX51;
+            break;
+        case 7:
+            device->FmtChans = DevFmtX61;
+            break;
+        case 8:
+            device->FmtChans = DevFmtX71;
+            break;
+        default:
+            ERR("Unhandled channel count (%d), using Stereo\n", streamFormat.mChannelsPerFrame);
+            device->FmtChans = DevFmtStereo;
+            streamFormat.mChannelsPerFrame = 2;
+            break;
+    }
+    SetDefaultWFXChannelOrder(device);
+
+    /* use channel count and sample rate from the default output unit's current
+     * parameters, but reset everything else */
+    streamFormat.mFramesPerPacket = 1;
+    streamFormat.mFormatFlags = 0;
+    switch(device->FmtType)
+    {
+        case DevFmtUByte:
+            device->FmtType = DevFmtByte;
+            /* fall-through */
+        case DevFmtByte:
+            streamFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
+            streamFormat.mBitsPerChannel = 8;
+            break;
+        case DevFmtUShort:
+            device->FmtType = DevFmtShort;
+            /* fall-through */
+        case DevFmtShort:
+            streamFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
+            streamFormat.mBitsPerChannel = 16;
+            break;
+        case DevFmtUInt:
+            device->FmtType = DevFmtInt;
+            /* fall-through */
+        case DevFmtInt:
+            streamFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
+            streamFormat.mBitsPerChannel = 32;
+            break;
+        case DevFmtFloat:
+            streamFormat.mFormatFlags = kLinearPCMFormatFlagIsFloat;
+            streamFormat.mBitsPerChannel = 32;
+            break;
+    }
+    streamFormat.mBytesPerFrame = streamFormat.mChannelsPerFrame *
+                                  streamFormat.mBitsPerChannel / 8;
+    streamFormat.mBytesPerPacket = streamFormat.mBytesPerFrame;
+    streamFormat.mFormatID = kAudioFormatLinearPCM;
+    streamFormat.mFormatFlags |= kAudioFormatFlagsNativeEndian |
+                                 kLinearPCMFormatFlagIsPacked;
+
+    err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &streamFormat, sizeof(AudioStreamBasicDescription));
+    if(err != noErr)
+    {
+        ERR("AudioUnitSetProperty failed\n");
+        return ALC_FALSE;
+    }
+
+    /* setup callback */
+    data->frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
+    input.inputProc = ca_callback;
+    input.inputProcRefCon = device;
+
+    err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &input, sizeof(AURenderCallbackStruct));
+    if(err != noErr)
+    {
+        ERR("AudioUnitSetProperty failed\n");
+        return ALC_FALSE;
+    }
+
+    /* init the default audio unit... */
+    err = AudioUnitInitialize(data->audioUnit);
+    if(err != noErr)
+    {
+        ERR("AudioUnitInitialize failed\n");
+        return ALC_FALSE;
+    }
+
+    return ALC_TRUE;
+}
+
+static ALCboolean ca_start_playback(ALCdevice *device)
+{
+    ca_data *data = (ca_data*)device->ExtraData;
+    OSStatus err;
+
+    err = AudioOutputUnitStart(data->audioUnit);
+    if(err != noErr)
+    {
+        ERR("AudioOutputUnitStart failed\n");
+        return ALC_FALSE;
+    }
+
+    return ALC_TRUE;
+}
+
+static void ca_stop_playback(ALCdevice *device)
+{
+    ca_data *data = (ca_data*)device->ExtraData;
+    OSStatus err;
+
+    err = AudioOutputUnitStop(data->audioUnit);
+    if(err != noErr)
+        ERR("AudioOutputUnitStop failed\n");
+}
+
+static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
+{
+    AudioStreamBasicDescription requestedFormat;  // The application requested format
+    AudioStreamBasicDescription hardwareFormat;   // The hardware format
+    AudioStreamBasicDescription outputFormat;     // The AudioUnit output format
+    AURenderCallbackStruct input;
+    AudioComponentDescription desc;
+    AudioDeviceID inputDevice;
+    UInt32 outputFrameCount;
+    UInt32 propertySize;
+    AudioObjectPropertyAddress propertyAddress;
+    UInt32 enableIO;
+    AudioComponent comp;
+    ca_data *data;
+    OSStatus err;
+
+    if(!deviceName)
+        deviceName = ca_device;
+    else if(strcmp(deviceName, ca_device) != 0)
+        return ALC_INVALID_VALUE;
+
+    desc.componentType = kAudioUnitType_Output;
+    desc.componentSubType = kAudioUnitSubType_HALOutput;
+    desc.componentManufacturer = kAudioUnitManufacturer_Apple;
+    desc.componentFlags = 0;
+    desc.componentFlagsMask = 0;
+
+    // Search for component with given description
+    comp = AudioComponentFindNext(NULL, &desc);
+    if(comp == NULL)
+    {
+        ERR("AudioComponentFindNext failed\n");
+        return ALC_INVALID_VALUE;
+    }
+
+    data = calloc(1, sizeof(*data));
+    device->ExtraData = data;
+
+    // Open the component
+    err = AudioComponentInstanceNew(comp, &data->audioUnit);
+    if(err != noErr)
+    {
+        ERR("AudioComponentInstanceNew failed\n");
+        goto error;
+    }
+
+    // Turn off AudioUnit output
+    enableIO = 0;
+    err = AudioUnitSetProperty(data->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, &enableIO, sizeof(ALuint));
+    if(err != noErr)
+    {
+        ERR("AudioUnitSetProperty failed\n");
+        goto error;
+    }
+
+    // Turn on AudioUnit input
+    enableIO = 1;
+    err = AudioUnitSetProperty(data->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &enableIO, sizeof(ALuint));
+    if(err != noErr)
+    {
+        ERR("AudioUnitSetProperty failed\n");
+        goto error;
+    }
+
+    // Get the default input device
+
+    propertySize = sizeof(AudioDeviceID);
+    propertyAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice;
+    propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
+    propertyAddress.mElement = kAudioObjectPropertyElementMaster;
+
+    err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &propertySize, &inputDevice);
+    if(err != noErr)
+    {
+        ERR("AudioObjectGetPropertyData failed\n");
+        goto error;
+    }
+
+    if(inputDevice == kAudioDeviceUnknown)
+    {
+        ERR("No input device found\n");
+        goto error;
+    }
+
+    // Track the input device
+    err = AudioUnitSetProperty(data->audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &inputDevice, sizeof(AudioDeviceID));
+    if(err != noErr)
+    {
+        ERR("AudioUnitSetProperty failed\n");
+        goto error;
+    }
+
+    // set capture callback
+    input.inputProc = ca_capture_callback;
+    input.inputProcRefCon = device;
+
+    err = AudioUnitSetProperty(data->audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &input, sizeof(AURenderCallbackStruct));
+    if(err != noErr)
+    {
+        ERR("AudioUnitSetProperty failed\n");
+        goto error;
+    }
+
+    // Initialize the device
+    err = AudioUnitInitialize(data->audioUnit);
+    if(err != noErr)
+    {
+        ERR("AudioUnitInitialize failed\n");
+        goto error;
+    }
+
+    // Get the hardware format
+    propertySize = sizeof(AudioStreamBasicDescription);
+    err = AudioUnitGetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 1, &hardwareFormat, &propertySize);
+    if(err != noErr || propertySize != sizeof(AudioStreamBasicDescription))
+    {
+        ERR("AudioUnitGetProperty failed\n");
+        goto error;
+    }
+
+    // Set up the requested format description
+    switch(device->FmtType)
+    {
+        case DevFmtUByte:
+            requestedFormat.mBitsPerChannel = 8;
+            requestedFormat.mFormatFlags = kAudioFormatFlagIsPacked;
+            break;
+        case DevFmtShort:
+            requestedFormat.mBitsPerChannel = 16;
+            requestedFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked;
+            break;
+        case DevFmtInt:
+            requestedFormat.mBitsPerChannel = 32;
+            requestedFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked;
+            break;
+        case DevFmtFloat:
+            requestedFormat.mBitsPerChannel = 32;
+            requestedFormat.mFormatFlags = kAudioFormatFlagIsPacked;
+            break;
+        case DevFmtByte:
+        case DevFmtUShort:
+        case DevFmtUInt:
+            ERR("%s samples not supported\n", DevFmtTypeString(device->FmtType));
+            goto error;
+    }
+
+    switch(device->FmtChans)
+    {
+        case DevFmtMono:
+            requestedFormat.mChannelsPerFrame = 1;
+            break;
+        case DevFmtStereo:
+            requestedFormat.mChannelsPerFrame = 2;
+            break;
+
+        case DevFmtQuad:
+        case DevFmtX51:
+        case DevFmtX51Rear:
+        case DevFmtX61:
+        case DevFmtX71:
+        case DevFmtAmbi1:
+        case DevFmtAmbi2:
+        case DevFmtAmbi3:
+            ERR("%s not supported\n", DevFmtChannelsString(device->FmtChans));
+            goto error;
+    }
+
+    requestedFormat.mBytesPerFrame = requestedFormat.mChannelsPerFrame * requestedFormat.mBitsPerChannel / 8;
+    requestedFormat.mBytesPerPacket = requestedFormat.mBytesPerFrame;
+    requestedFormat.mSampleRate = device->Frequency;
+    requestedFormat.mFormatID = kAudioFormatLinearPCM;
+    requestedFormat.mReserved = 0;
+    requestedFormat.mFramesPerPacket = 1;
+
+    // save requested format description for later use
+    data->format = requestedFormat;
+    data->frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
+
+    // Use intermediate format for sample rate conversion (outputFormat)
+    // Set sample rate to the same as hardware for resampling later
+    outputFormat = requestedFormat;
+    outputFormat.mSampleRate = hardwareFormat.mSampleRate;
+
+    // Determine sample rate ratio for resampling
+    data->sampleRateRatio = outputFormat.mSampleRate / device->Frequency;
+
+    // The output format should be the requested format, but using the hardware sample rate
+    // This is because the AudioUnit will automatically scale other properties, except for sample rate
+    err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, (void *)&outputFormat, sizeof(outputFormat));
+    if(err != noErr)
+    {
+        ERR("AudioUnitSetProperty failed\n");
+        goto error;
+    }
+
+    // Set the AudioUnit output format frame count
+    outputFrameCount = device->UpdateSize * data->sampleRateRatio;
+    err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Output, 0, &outputFrameCount, sizeof(outputFrameCount));
+    if(err != noErr)
+    {
+        ERR("AudioUnitSetProperty failed: %d\n", err);
+        goto error;
+    }
+
+    // Set up sample converter
+    err = AudioConverterNew(&outputFormat, &requestedFormat, &data->audioConverter);
+    if(err != noErr)
+    {
+        ERR("AudioConverterNew failed: %d\n", err);
+        goto error;
+    }
+
+    // Create a buffer for use in the resample callback
+    data->resampleBuffer = malloc(device->UpdateSize * data->frameSize * data->sampleRateRatio);
+
+    // Allocate buffer for the AudioUnit output
+    data->bufferList = allocate_buffer_list(outputFormat.mChannelsPerFrame, device->UpdateSize * data->frameSize * data->sampleRateRatio);
+    if(data->bufferList == NULL)
+        goto error;
+
+    data->ring = ll_ringbuffer_create(
+        device->UpdateSize*data->sampleRateRatio*device->NumUpdates + 1,
+        data->frameSize
+    );
+    if(!data->ring) goto error;
+
+    al_string_copy_cstr(&device->DeviceName, deviceName);
+
+    return ALC_NO_ERROR;
+
+error:
+    ll_ringbuffer_free(data->ring);
+    data->ring = NULL;
+    free(data->resampleBuffer);
+    destroy_buffer_list(data->bufferList);
+
+    if(data->audioConverter)
+        AudioConverterDispose(data->audioConverter);
+    if(data->audioUnit)
+        AudioComponentInstanceDispose(data->audioUnit);
+
+    free(data);
+    device->ExtraData = NULL;
+
+    return ALC_INVALID_VALUE;
+}
+
+static void ca_close_capture(ALCdevice *device)
+{
+    ca_data *data = (ca_data*)device->ExtraData;
+
+    ll_ringbuffer_free(data->ring);
+    data->ring = NULL;
+    free(data->resampleBuffer);
+    destroy_buffer_list(data->bufferList);
+
+    AudioConverterDispose(data->audioConverter);
+    AudioComponentInstanceDispose(data->audioUnit);
+
+    free(data);
+    device->ExtraData = NULL;
+}
+
+static void ca_start_capture(ALCdevice *device)
+{
+    ca_data *data = (ca_data*)device->ExtraData;
+    OSStatus err = AudioOutputUnitStart(data->audioUnit);
+    if(err != noErr)
+        ERR("AudioOutputUnitStart failed\n");
+}
+
+static void ca_stop_capture(ALCdevice *device)
+{
+    ca_data *data = (ca_data*)device->ExtraData;
+    OSStatus err = AudioOutputUnitStop(data->audioUnit);
+    if(err != noErr)
+        ERR("AudioOutputUnitStop failed\n");
+}
+
+static ALCenum ca_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint samples)
+{
+    ca_data *data = (ca_data*)device->ExtraData;
+    AudioBufferList *list;
+    UInt32 frameCount;
+    OSStatus err;
+
+    // If no samples are requested, just return
+    if(samples == 0)
+        return ALC_NO_ERROR;
+
+    // Allocate a temporary AudioBufferList to use as the return resamples data
+    list = alloca(sizeof(AudioBufferList) + sizeof(AudioBuffer));
+
+    // Point the resampling buffer to the capture buffer
+    list->mNumberBuffers = 1;
+    list->mBuffers[0].mNumberChannels = data->format.mChannelsPerFrame;
+    list->mBuffers[0].mDataByteSize = samples * data->frameSize;
+    list->mBuffers[0].mData = buffer;
+
+    // Resample into another AudioBufferList
+    frameCount = samples;
+    err = AudioConverterFillComplexBuffer(data->audioConverter, ca_capture_conversion_callback,
+                                          device, &frameCount, list, NULL);
+    if(err != noErr)
+    {
+        ERR("AudioConverterFillComplexBuffer error: %d\n", err);
+        return ALC_INVALID_VALUE;
+    }
+    return ALC_NO_ERROR;
+}
+
+static ALCuint ca_available_samples(ALCdevice *device)
+{
+    ca_data *data = device->ExtraData;
+    return ll_ringbuffer_read_space(data->ring) / data->sampleRateRatio;
+}
+
+
+static const BackendFuncs ca_funcs = {
+    ca_open_playback,
+    ca_close_playback,
+    ca_reset_playback,
+    ca_start_playback,
+    ca_stop_playback,
+    ca_open_capture,
+    ca_close_capture,
+    ca_start_capture,
+    ca_stop_capture,
+    ca_capture_samples,
+    ca_available_samples
+};
+
+ALCboolean alc_ca_init(BackendFuncs *func_list)
+{
+    *func_list = ca_funcs;
+    return ALC_TRUE;
+}
+
+void alc_ca_deinit(void)
+{
+}
+
+void alc_ca_probe(enum DevProbe type)
+{
+    switch(type)
+    {
+        case ALL_DEVICE_PROBE:
+            AppendAllDevicesList(ca_device);
+            break;
+        case CAPTURE_DEVICE_PROBE:
+            AppendCaptureDeviceList(ca_device);
+            break;
+    }
+}

+ 1064 - 0
Engine/lib/openal-soft/Alc/backends/dsound.c

@@ -0,0 +1,1064 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 1999-2007 by authors.
+ * This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the
+ *  Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <memory.h>
+
+#include <dsound.h>
+#include <cguid.h>
+#include <mmreg.h>
+#ifndef _WAVEFORMATEXTENSIBLE_
+#include <ks.h>
+#include <ksmedia.h>
+#endif
+
+#include "alMain.h"
+#include "alu.h"
+#include "threads.h"
+#include "compat.h"
+#include "alstring.h"
+
+#include "backends/base.h"
+
+#ifndef DSSPEAKER_5POINT1
+#   define DSSPEAKER_5POINT1          0x00000006
+#endif
+#ifndef DSSPEAKER_5POINT1_BACK
+#   define DSSPEAKER_5POINT1_BACK     0x00000006
+#endif
+#ifndef DSSPEAKER_7POINT1
+#   define DSSPEAKER_7POINT1          0x00000007
+#endif
+#ifndef DSSPEAKER_7POINT1_SURROUND
+#   define DSSPEAKER_7POINT1_SURROUND 0x00000008
+#endif
+#ifndef DSSPEAKER_5POINT1_SURROUND
+#   define DSSPEAKER_5POINT1_SURROUND 0x00000009
+#endif
+
+
+DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
+DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
+
+#define DEVNAME_HEAD "OpenAL Soft on "
+
+
+#ifdef HAVE_DYNLOAD
+static void *ds_handle;
+static HRESULT (WINAPI *pDirectSoundCreate)(const GUID *pcGuidDevice, IDirectSound **ppDS, IUnknown *pUnkOuter);
+static HRESULT (WINAPI *pDirectSoundEnumerateW)(LPDSENUMCALLBACKW pDSEnumCallback, void *pContext);
+static HRESULT (WINAPI *pDirectSoundCaptureCreate)(const GUID *pcGuidDevice, IDirectSoundCapture **ppDSC, IUnknown *pUnkOuter);
+static HRESULT (WINAPI *pDirectSoundCaptureEnumerateW)(LPDSENUMCALLBACKW pDSEnumCallback, void *pContext);
+
+#define DirectSoundCreate            pDirectSoundCreate
+#define DirectSoundEnumerateW        pDirectSoundEnumerateW
+#define DirectSoundCaptureCreate     pDirectSoundCaptureCreate
+#define DirectSoundCaptureEnumerateW pDirectSoundCaptureEnumerateW
+#endif
+
+
+static ALCboolean DSoundLoad(void)
+{
+#ifdef HAVE_DYNLOAD
+    if(!ds_handle)
+    {
+        ds_handle = LoadLib("dsound.dll");
+        if(ds_handle == NULL)
+        {
+            ERR("Failed to load dsound.dll\n");
+            return ALC_FALSE;
+        }
+
+#define LOAD_FUNC(f) do {                                                     \
+    p##f = GetSymbol(ds_handle, #f);                                          \
+    if(p##f == NULL) {                                                        \
+        CloseLib(ds_handle);                                                  \
+        ds_handle = NULL;                                                     \
+        return ALC_FALSE;                                                     \
+    }                                                                         \
+} while(0)
+        LOAD_FUNC(DirectSoundCreate);
+        LOAD_FUNC(DirectSoundEnumerateW);
+        LOAD_FUNC(DirectSoundCaptureCreate);
+        LOAD_FUNC(DirectSoundCaptureEnumerateW);
+#undef LOAD_FUNC
+    }
+#endif
+    return ALC_TRUE;
+}
+
+
+#define MAX_UPDATES 128
+
+typedef struct {
+    al_string name;
+    GUID guid;
+} DevMap;
+TYPEDEF_VECTOR(DevMap, vector_DevMap)
+
+static vector_DevMap PlaybackDevices;
+static vector_DevMap CaptureDevices;
+
+static void clear_devlist(vector_DevMap *list)
+{
+#define DEINIT_STR(i) AL_STRING_DEINIT((i)->name)
+    VECTOR_FOR_EACH(DevMap, *list, DEINIT_STR);
+    VECTOR_RESIZE(*list, 0, 0);
+#undef DEINIT_STR
+}
+
+static BOOL CALLBACK DSoundEnumDevices(GUID *guid, const WCHAR *desc, const WCHAR* UNUSED(drvname), void *data)
+{
+    vector_DevMap *devices = data;
+    OLECHAR *guidstr = NULL;
+    DevMap entry;
+    HRESULT hr;
+    int count;
+
+    if(!guid)
+        return TRUE;
+
+    AL_STRING_INIT(entry.name);
+
+    count = 0;
+    while(1)
+    {
+        const DevMap *iter;
+
+        al_string_copy_cstr(&entry.name, DEVNAME_HEAD);
+        al_string_append_wcstr(&entry.name, desc);
+        if(count != 0)
+        {
+            char str[64];
+            snprintf(str, sizeof(str), " #%d", count+1);
+            al_string_append_cstr(&entry.name, str);
+        }
+
+#define MATCH_ENTRY(i) (al_string_cmp(entry.name, (i)->name) == 0)
+        VECTOR_FIND_IF(iter, const DevMap, *devices, MATCH_ENTRY);
+        if(iter == VECTOR_END(*devices)) break;
+#undef MATCH_ENTRY
+        count++;
+    }
+    entry.guid = *guid;
+
+    hr = StringFromCLSID(guid, &guidstr);
+    if(SUCCEEDED(hr))
+    {
+        TRACE("Got device \"%s\", GUID \"%ls\"\n", al_string_get_cstr(entry.name), guidstr);
+        CoTaskMemFree(guidstr);
+    }
+
+    VECTOR_PUSH_BACK(*devices, entry);
+
+    return TRUE;
+}
+
+
+typedef struct ALCdsoundPlayback {
+    DERIVE_FROM_TYPE(ALCbackend);
+
+    IDirectSound       *DS;
+    IDirectSoundBuffer *PrimaryBuffer;
+    IDirectSoundBuffer *Buffer;
+    IDirectSoundNotify *Notifies;
+    HANDLE             NotifyEvent;
+
+    volatile int killNow;
+    althrd_t thread;
+} ALCdsoundPlayback;
+
+static int ALCdsoundPlayback_mixerProc(void *ptr);
+
+static void ALCdsoundPlayback_Construct(ALCdsoundPlayback *self, ALCdevice *device);
+static DECLARE_FORWARD(ALCdsoundPlayback, ALCbackend, void, Destruct)
+static ALCenum ALCdsoundPlayback_open(ALCdsoundPlayback *self, const ALCchar *name);
+static void ALCdsoundPlayback_close(ALCdsoundPlayback *self);
+static ALCboolean ALCdsoundPlayback_reset(ALCdsoundPlayback *self);
+static ALCboolean ALCdsoundPlayback_start(ALCdsoundPlayback *self);
+static void ALCdsoundPlayback_stop(ALCdsoundPlayback *self);
+static DECLARE_FORWARD2(ALCdsoundPlayback, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
+static DECLARE_FORWARD(ALCdsoundPlayback, ALCbackend, ALCuint, availableSamples)
+static DECLARE_FORWARD(ALCdsoundPlayback, ALCbackend, ClockLatency, getClockLatency)
+static DECLARE_FORWARD(ALCdsoundPlayback, ALCbackend, void, lock)
+static DECLARE_FORWARD(ALCdsoundPlayback, ALCbackend, void, unlock)
+DECLARE_DEFAULT_ALLOCATORS(ALCdsoundPlayback)
+
+DEFINE_ALCBACKEND_VTABLE(ALCdsoundPlayback);
+
+
+static void ALCdsoundPlayback_Construct(ALCdsoundPlayback *self, ALCdevice *device)
+{
+    ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
+    SET_VTABLE2(ALCdsoundPlayback, ALCbackend, self);
+}
+
+
+FORCE_ALIGN static int ALCdsoundPlayback_mixerProc(void *ptr)
+{
+    ALCdsoundPlayback *self = ptr;
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+    DSBCAPS DSBCaps;
+    DWORD LastCursor = 0;
+    DWORD PlayCursor;
+    void *WritePtr1, *WritePtr2;
+    DWORD WriteCnt1,  WriteCnt2;
+    BOOL Playing = FALSE;
+    DWORD FrameSize;
+    DWORD FragSize;
+    DWORD avail;
+    HRESULT err;
+
+    SetRTPriority();
+    althrd_setname(althrd_current(), MIXER_THREAD_NAME);
+
+    memset(&DSBCaps, 0, sizeof(DSBCaps));
+    DSBCaps.dwSize = sizeof(DSBCaps);
+    err = IDirectSoundBuffer_GetCaps(self->Buffer, &DSBCaps);
+    if(FAILED(err))
+    {
+        ERR("Failed to get buffer caps: 0x%lx\n", err);
+        ALCdevice_Lock(device);
+        aluHandleDisconnect(device);
+        ALCdevice_Unlock(device);
+        return 1;
+    }
+
+    FrameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
+    FragSize = device->UpdateSize * FrameSize;
+
+    IDirectSoundBuffer_GetCurrentPosition(self->Buffer, &LastCursor, NULL);
+    while(!self->killNow)
+    {
+        // Get current play cursor
+        IDirectSoundBuffer_GetCurrentPosition(self->Buffer, &PlayCursor, NULL);
+        avail = (PlayCursor-LastCursor+DSBCaps.dwBufferBytes) % DSBCaps.dwBufferBytes;
+
+        if(avail < FragSize)
+        {
+            if(!Playing)
+            {
+                err = IDirectSoundBuffer_Play(self->Buffer, 0, 0, DSBPLAY_LOOPING);
+                if(FAILED(err))
+                {
+                    ERR("Failed to play buffer: 0x%lx\n", err);
+                    ALCdevice_Lock(device);
+                    aluHandleDisconnect(device);
+                    ALCdevice_Unlock(device);
+                    return 1;
+                }
+                Playing = TRUE;
+            }
+
+            avail = WaitForSingleObjectEx(self->NotifyEvent, 2000, FALSE);
+            if(avail != WAIT_OBJECT_0)
+                ERR("WaitForSingleObjectEx error: 0x%lx\n", avail);
+            continue;
+        }
+        avail -= avail%FragSize;
+
+        // Lock output buffer
+        WriteCnt1 = 0;
+        WriteCnt2 = 0;
+        err = IDirectSoundBuffer_Lock(self->Buffer, LastCursor, avail, &WritePtr1, &WriteCnt1, &WritePtr2, &WriteCnt2, 0);
+
+        // If the buffer is lost, restore it and lock
+        if(err == DSERR_BUFFERLOST)
+        {
+            WARN("Buffer lost, restoring...\n");
+            err = IDirectSoundBuffer_Restore(self->Buffer);
+            if(SUCCEEDED(err))
+            {
+                Playing = FALSE;
+                LastCursor = 0;
+                err = IDirectSoundBuffer_Lock(self->Buffer, 0, DSBCaps.dwBufferBytes, &WritePtr1, &WriteCnt1, &WritePtr2, &WriteCnt2, 0);
+            }
+        }
+
+        // Successfully locked the output buffer
+        if(SUCCEEDED(err))
+        {
+            // If we have an active context, mix data directly into output buffer otherwise fill with silence
+            aluMixData(device, WritePtr1, WriteCnt1/FrameSize);
+            aluMixData(device, WritePtr2, WriteCnt2/FrameSize);
+
+            // Unlock output buffer only when successfully locked
+            IDirectSoundBuffer_Unlock(self->Buffer, WritePtr1, WriteCnt1, WritePtr2, WriteCnt2);
+        }
+        else
+        {
+            ERR("Buffer lock error: %#lx\n", err);
+            ALCdevice_Lock(device);
+            aluHandleDisconnect(device);
+            ALCdevice_Unlock(device);
+            return 1;
+        }
+
+        // Update old write cursor location
+        LastCursor += WriteCnt1+WriteCnt2;
+        LastCursor %= DSBCaps.dwBufferBytes;
+    }
+
+    return 0;
+}
+
+static ALCenum ALCdsoundPlayback_open(ALCdsoundPlayback *self, const ALCchar *deviceName)
+{
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+    const GUID *guid = NULL;
+    HRESULT hr, hrcom;
+
+    if(VECTOR_SIZE(PlaybackDevices) == 0)
+    {
+        /* Initialize COM to prevent name truncation */
+        hrcom = CoInitialize(NULL);
+        hr = DirectSoundEnumerateW(DSoundEnumDevices, &PlaybackDevices);
+        if(FAILED(hr))
+            ERR("Error enumerating DirectSound devices (0x%lx)!\n", hr);
+        if(SUCCEEDED(hrcom))
+            CoUninitialize();
+    }
+
+    if(!deviceName && VECTOR_SIZE(PlaybackDevices) > 0)
+    {
+        deviceName = al_string_get_cstr(VECTOR_FRONT(PlaybackDevices).name);
+        guid = &VECTOR_FRONT(PlaybackDevices).guid;
+    }
+    else
+    {
+        const DevMap *iter;
+
+#define MATCH_NAME(i)  (al_string_cmp_cstr((i)->name, deviceName) == 0)
+        VECTOR_FIND_IF(iter, const DevMap, PlaybackDevices, MATCH_NAME);
+#undef MATCH_NAME
+        if(iter == VECTOR_END(PlaybackDevices))
+            return ALC_INVALID_VALUE;
+        guid = &iter->guid;
+    }
+
+    hr = DS_OK;
+    self->NotifyEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
+    if(self->NotifyEvent == NULL)
+        hr = E_FAIL;
+
+    //DirectSound Init code
+    if(SUCCEEDED(hr))
+        hr = DirectSoundCreate(guid, &self->DS, NULL);
+    if(SUCCEEDED(hr))
+        hr = IDirectSound_SetCooperativeLevel(self->DS, GetForegroundWindow(), DSSCL_PRIORITY);
+    if(FAILED(hr))
+    {
+        if(self->DS)
+            IDirectSound_Release(self->DS);
+        self->DS = NULL;
+        if(self->NotifyEvent)
+            CloseHandle(self->NotifyEvent);
+        self->NotifyEvent = NULL;
+
+        ERR("Device init failed: 0x%08lx\n", hr);
+        return ALC_INVALID_VALUE;
+    }
+
+    al_string_copy_cstr(&device->DeviceName, deviceName);
+
+    return ALC_NO_ERROR;
+}
+
+static void ALCdsoundPlayback_close(ALCdsoundPlayback *self)
+{
+    if(self->Notifies)
+        IDirectSoundNotify_Release(self->Notifies);
+    self->Notifies = NULL;
+    if(self->Buffer)
+        IDirectSoundBuffer_Release(self->Buffer);
+    self->Buffer = NULL;
+    if(self->PrimaryBuffer != NULL)
+        IDirectSoundBuffer_Release(self->PrimaryBuffer);
+    self->PrimaryBuffer = NULL;
+
+    IDirectSound_Release(self->DS);
+    self->DS = NULL;
+    CloseHandle(self->NotifyEvent);
+    self->NotifyEvent = NULL;
+}
+
+static ALCboolean ALCdsoundPlayback_reset(ALCdsoundPlayback *self)
+{
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+    DSBUFFERDESC DSBDescription;
+    WAVEFORMATEXTENSIBLE OutputType;
+    DWORD speakers;
+    HRESULT hr;
+
+    memset(&OutputType, 0, sizeof(OutputType));
+
+    if(self->Notifies)
+        IDirectSoundNotify_Release(self->Notifies);
+    self->Notifies = NULL;
+    if(self->Buffer)
+        IDirectSoundBuffer_Release(self->Buffer);
+    self->Buffer = NULL;
+    if(self->PrimaryBuffer != NULL)
+        IDirectSoundBuffer_Release(self->PrimaryBuffer);
+    self->PrimaryBuffer = NULL;
+
+    switch(device->FmtType)
+    {
+        case DevFmtByte:
+            device->FmtType = DevFmtUByte;
+            break;
+        case DevFmtFloat:
+            if((device->Flags&DEVICE_SAMPLE_TYPE_REQUEST))
+                break;
+            /* fall-through */
+        case DevFmtUShort:
+            device->FmtType = DevFmtShort;
+            break;
+        case DevFmtUInt:
+            device->FmtType = DevFmtInt;
+            break;
+        case DevFmtUByte:
+        case DevFmtShort:
+        case DevFmtInt:
+            break;
+    }
+
+    hr = IDirectSound_GetSpeakerConfig(self->DS, &speakers);
+    if(SUCCEEDED(hr))
+    {
+        speakers = DSSPEAKER_CONFIG(speakers);
+        if(!(device->Flags&DEVICE_CHANNELS_REQUEST))
+        {
+            if(speakers == DSSPEAKER_MONO)
+                device->FmtChans = DevFmtMono;
+            else if(speakers == DSSPEAKER_STEREO || speakers == DSSPEAKER_HEADPHONE)
+                device->FmtChans = DevFmtStereo;
+            else if(speakers == DSSPEAKER_QUAD)
+                device->FmtChans = DevFmtQuad;
+            else if(speakers == DSSPEAKER_5POINT1_SURROUND)
+                device->FmtChans = DevFmtX51;
+            else if(speakers == DSSPEAKER_5POINT1_BACK)
+                device->FmtChans = DevFmtX51Rear;
+            else if(speakers == DSSPEAKER_7POINT1 || speakers == DSSPEAKER_7POINT1_SURROUND)
+                device->FmtChans = DevFmtX71;
+            else
+                ERR("Unknown system speaker config: 0x%lx\n", speakers);
+        }
+        device->IsHeadphones = (device->FmtChans == DevFmtStereo &&
+                                speakers == DSSPEAKER_HEADPHONE);
+
+        switch(device->FmtChans)
+        {
+            case DevFmtMono:
+                OutputType.dwChannelMask = SPEAKER_FRONT_CENTER;
+                break;
+            case DevFmtAmbi1:
+            case DevFmtAmbi2:
+            case DevFmtAmbi3:
+                device->FmtChans = DevFmtStereo;
+                /*fall-through*/
+            case DevFmtStereo:
+                OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
+                                           SPEAKER_FRONT_RIGHT;
+                break;
+            case DevFmtQuad:
+                OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
+                                           SPEAKER_FRONT_RIGHT |
+                                           SPEAKER_BACK_LEFT |
+                                           SPEAKER_BACK_RIGHT;
+                break;
+            case DevFmtX51:
+                OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
+                                           SPEAKER_FRONT_RIGHT |
+                                           SPEAKER_FRONT_CENTER |
+                                           SPEAKER_LOW_FREQUENCY |
+                                           SPEAKER_SIDE_LEFT |
+                                           SPEAKER_SIDE_RIGHT;
+                break;
+            case DevFmtX51Rear:
+                OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
+                                           SPEAKER_FRONT_RIGHT |
+                                           SPEAKER_FRONT_CENTER |
+                                           SPEAKER_LOW_FREQUENCY |
+                                           SPEAKER_BACK_LEFT |
+                                           SPEAKER_BACK_RIGHT;
+                break;
+            case DevFmtX61:
+                OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
+                                           SPEAKER_FRONT_RIGHT |
+                                           SPEAKER_FRONT_CENTER |
+                                           SPEAKER_LOW_FREQUENCY |
+                                           SPEAKER_BACK_CENTER |
+                                           SPEAKER_SIDE_LEFT |
+                                           SPEAKER_SIDE_RIGHT;
+                break;
+            case DevFmtX71:
+                OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
+                                           SPEAKER_FRONT_RIGHT |
+                                           SPEAKER_FRONT_CENTER |
+                                           SPEAKER_LOW_FREQUENCY |
+                                           SPEAKER_BACK_LEFT |
+                                           SPEAKER_BACK_RIGHT |
+                                           SPEAKER_SIDE_LEFT |
+                                           SPEAKER_SIDE_RIGHT;
+                break;
+        }
+
+retry_open:
+        hr = S_OK;
+        OutputType.Format.wFormatTag = WAVE_FORMAT_PCM;
+        OutputType.Format.nChannels = ChannelsFromDevFmt(device->FmtChans);
+        OutputType.Format.wBitsPerSample = BytesFromDevFmt(device->FmtType) * 8;
+        OutputType.Format.nBlockAlign = OutputType.Format.nChannels*OutputType.Format.wBitsPerSample/8;
+        OutputType.Format.nSamplesPerSec = device->Frequency;
+        OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec*OutputType.Format.nBlockAlign;
+        OutputType.Format.cbSize = 0;
+    }
+
+    if(OutputType.Format.nChannels > 2 || device->FmtType == DevFmtFloat)
+    {
+        OutputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
+        OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
+        OutputType.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
+        if(device->FmtType == DevFmtFloat)
+            OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
+        else
+            OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
+
+        if(self->PrimaryBuffer)
+            IDirectSoundBuffer_Release(self->PrimaryBuffer);
+        self->PrimaryBuffer = NULL;
+    }
+    else
+    {
+        if(SUCCEEDED(hr) && !self->PrimaryBuffer)
+        {
+            memset(&DSBDescription,0,sizeof(DSBUFFERDESC));
+            DSBDescription.dwSize=sizeof(DSBUFFERDESC);
+            DSBDescription.dwFlags=DSBCAPS_PRIMARYBUFFER;
+            hr = IDirectSound_CreateSoundBuffer(self->DS, &DSBDescription, &self->PrimaryBuffer, NULL);
+        }
+        if(SUCCEEDED(hr))
+            hr = IDirectSoundBuffer_SetFormat(self->PrimaryBuffer,&OutputType.Format);
+    }
+
+    if(SUCCEEDED(hr))
+    {
+        if(device->NumUpdates > MAX_UPDATES)
+        {
+            device->UpdateSize = (device->UpdateSize*device->NumUpdates +
+                                  MAX_UPDATES-1) / MAX_UPDATES;
+            device->NumUpdates = MAX_UPDATES;
+        }
+
+        memset(&DSBDescription,0,sizeof(DSBUFFERDESC));
+        DSBDescription.dwSize=sizeof(DSBUFFERDESC);
+        DSBDescription.dwFlags=DSBCAPS_CTRLPOSITIONNOTIFY|DSBCAPS_GETCURRENTPOSITION2|DSBCAPS_GLOBALFOCUS;
+        DSBDescription.dwBufferBytes=device->UpdateSize * device->NumUpdates *
+                                     OutputType.Format.nBlockAlign;
+        DSBDescription.lpwfxFormat=&OutputType.Format;
+        hr = IDirectSound_CreateSoundBuffer(self->DS, &DSBDescription, &self->Buffer, NULL);
+        if(FAILED(hr) && device->FmtType == DevFmtFloat)
+        {
+            device->FmtType = DevFmtShort;
+            goto retry_open;
+        }
+    }
+
+    if(SUCCEEDED(hr))
+    {
+        hr = IDirectSoundBuffer_QueryInterface(self->Buffer, &IID_IDirectSoundNotify, (void**)&self->Notifies);
+        if(SUCCEEDED(hr))
+        {
+            DSBPOSITIONNOTIFY notifies[MAX_UPDATES];
+            ALuint i;
+
+            for(i = 0;i < device->NumUpdates;++i)
+            {
+                notifies[i].dwOffset = i * device->UpdateSize *
+                                       OutputType.Format.nBlockAlign;
+                notifies[i].hEventNotify = self->NotifyEvent;
+            }
+            if(IDirectSoundNotify_SetNotificationPositions(self->Notifies, device->NumUpdates, notifies) != DS_OK)
+                hr = E_FAIL;
+        }
+    }
+
+    if(FAILED(hr))
+    {
+        if(self->Notifies != NULL)
+            IDirectSoundNotify_Release(self->Notifies);
+        self->Notifies = NULL;
+        if(self->Buffer != NULL)
+            IDirectSoundBuffer_Release(self->Buffer);
+        self->Buffer = NULL;
+        if(self->PrimaryBuffer != NULL)
+            IDirectSoundBuffer_Release(self->PrimaryBuffer);
+        self->PrimaryBuffer = NULL;
+        return ALC_FALSE;
+    }
+
+    ResetEvent(self->NotifyEvent);
+    SetDefaultWFXChannelOrder(device);
+
+    return ALC_TRUE;
+}
+
+static ALCboolean ALCdsoundPlayback_start(ALCdsoundPlayback *self)
+{
+    self->killNow = 0;
+    if(althrd_create(&self->thread, ALCdsoundPlayback_mixerProc, self) != althrd_success)
+        return ALC_FALSE;
+
+    return ALC_TRUE;
+}
+
+static void ALCdsoundPlayback_stop(ALCdsoundPlayback *self)
+{
+    int res;
+
+    if(self->killNow)
+        return;
+
+    self->killNow = 1;
+    althrd_join(self->thread, &res);
+
+    IDirectSoundBuffer_Stop(self->Buffer);
+}
+
+
+
+typedef struct ALCdsoundCapture {
+    DERIVE_FROM_TYPE(ALCbackend);
+
+    IDirectSoundCapture *DSC;
+    IDirectSoundCaptureBuffer *DSCbuffer;
+    DWORD BufferBytes;
+    DWORD Cursor;
+
+    ll_ringbuffer_t *Ring;
+} ALCdsoundCapture;
+
+static void ALCdsoundCapture_Construct(ALCdsoundCapture *self, ALCdevice *device);
+static DECLARE_FORWARD(ALCdsoundCapture, ALCbackend, void, Destruct)
+static ALCenum ALCdsoundCapture_open(ALCdsoundCapture *self, const ALCchar *name);
+static void ALCdsoundCapture_close(ALCdsoundCapture *self);
+static DECLARE_FORWARD(ALCdsoundCapture, ALCbackend, ALCboolean, reset)
+static ALCboolean ALCdsoundCapture_start(ALCdsoundCapture *self);
+static void ALCdsoundCapture_stop(ALCdsoundCapture *self);
+static ALCenum ALCdsoundCapture_captureSamples(ALCdsoundCapture *self, ALCvoid *buffer, ALCuint samples);
+static ALCuint ALCdsoundCapture_availableSamples(ALCdsoundCapture *self);
+static DECLARE_FORWARD(ALCdsoundCapture, ALCbackend, ClockLatency, getClockLatency)
+static DECLARE_FORWARD(ALCdsoundCapture, ALCbackend, void, lock)
+static DECLARE_FORWARD(ALCdsoundCapture, ALCbackend, void, unlock)
+DECLARE_DEFAULT_ALLOCATORS(ALCdsoundCapture)
+
+DEFINE_ALCBACKEND_VTABLE(ALCdsoundCapture);
+
+static void ALCdsoundCapture_Construct(ALCdsoundCapture *self, ALCdevice *device)
+{
+    ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
+    SET_VTABLE2(ALCdsoundCapture, ALCbackend, self);
+}
+
+
+static ALCenum ALCdsoundCapture_open(ALCdsoundCapture *self, const ALCchar *deviceName)
+{
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+    WAVEFORMATEXTENSIBLE InputType;
+    DSCBUFFERDESC DSCBDescription;
+    const GUID *guid = NULL;
+    HRESULT hr, hrcom;
+    ALuint samples;
+
+    if(VECTOR_SIZE(CaptureDevices) == 0)
+    {
+        /* Initialize COM to prevent name truncation */
+        hrcom = CoInitialize(NULL);
+        hr = DirectSoundCaptureEnumerateW(DSoundEnumDevices, &CaptureDevices);
+        if(FAILED(hr))
+            ERR("Error enumerating DirectSound devices (0x%lx)!\n", hr);
+        if(SUCCEEDED(hrcom))
+            CoUninitialize();
+    }
+
+    if(!deviceName && VECTOR_SIZE(CaptureDevices) > 0)
+    {
+        deviceName = al_string_get_cstr(VECTOR_FRONT(CaptureDevices).name);
+        guid = &VECTOR_FRONT(CaptureDevices).guid;
+    }
+    else
+    {
+        const DevMap *iter;
+
+#define MATCH_NAME(i)  (al_string_cmp_cstr((i)->name, deviceName) == 0)
+        VECTOR_FIND_IF(iter, const DevMap, CaptureDevices, MATCH_NAME);
+#undef MATCH_NAME
+        if(iter == VECTOR_END(CaptureDevices))
+            return ALC_INVALID_VALUE;
+        guid = &iter->guid;
+    }
+
+    switch(device->FmtType)
+    {
+        case DevFmtByte:
+        case DevFmtUShort:
+        case DevFmtUInt:
+            WARN("%s capture samples not supported\n", DevFmtTypeString(device->FmtType));
+            return ALC_INVALID_ENUM;
+
+        case DevFmtUByte:
+        case DevFmtShort:
+        case DevFmtInt:
+        case DevFmtFloat:
+            break;
+    }
+
+    //DirectSoundCapture Init code
+    hr = DirectSoundCaptureCreate(guid, &self->DSC, NULL);
+    if(SUCCEEDED(hr))
+    {
+        memset(&InputType, 0, sizeof(InputType));
+
+        switch(device->FmtChans)
+        {
+            case DevFmtMono:
+                InputType.dwChannelMask = SPEAKER_FRONT_CENTER;
+                break;
+            case DevFmtStereo:
+                InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
+                                          SPEAKER_FRONT_RIGHT;
+                break;
+            case DevFmtQuad:
+                InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
+                                          SPEAKER_FRONT_RIGHT |
+                                          SPEAKER_BACK_LEFT |
+                                          SPEAKER_BACK_RIGHT;
+                break;
+            case DevFmtX51:
+                InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
+                                          SPEAKER_FRONT_RIGHT |
+                                          SPEAKER_FRONT_CENTER |
+                                          SPEAKER_LOW_FREQUENCY |
+                                          SPEAKER_SIDE_LEFT |
+                                          SPEAKER_SIDE_RIGHT;
+                break;
+            case DevFmtX51Rear:
+                InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
+                                          SPEAKER_FRONT_RIGHT |
+                                          SPEAKER_FRONT_CENTER |
+                                          SPEAKER_LOW_FREQUENCY |
+                                          SPEAKER_BACK_LEFT |
+                                          SPEAKER_BACK_RIGHT;
+                break;
+            case DevFmtX61:
+                InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
+                                          SPEAKER_FRONT_RIGHT |
+                                          SPEAKER_FRONT_CENTER |
+                                          SPEAKER_LOW_FREQUENCY |
+                                          SPEAKER_BACK_CENTER |
+                                          SPEAKER_SIDE_LEFT |
+                                          SPEAKER_SIDE_RIGHT;
+                break;
+            case DevFmtX71:
+                InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
+                                          SPEAKER_FRONT_RIGHT |
+                                          SPEAKER_FRONT_CENTER |
+                                          SPEAKER_LOW_FREQUENCY |
+                                          SPEAKER_BACK_LEFT |
+                                          SPEAKER_BACK_RIGHT |
+                                          SPEAKER_SIDE_LEFT |
+                                          SPEAKER_SIDE_RIGHT;
+                break;
+            case DevFmtAmbi1:
+            case DevFmtAmbi2:
+            case DevFmtAmbi3:
+                break;
+        }
+
+        InputType.Format.wFormatTag = WAVE_FORMAT_PCM;
+        InputType.Format.nChannels = ChannelsFromDevFmt(device->FmtChans);
+        InputType.Format.wBitsPerSample = BytesFromDevFmt(device->FmtType) * 8;
+        InputType.Format.nBlockAlign = InputType.Format.nChannels*InputType.Format.wBitsPerSample/8;
+        InputType.Format.nSamplesPerSec = device->Frequency;
+        InputType.Format.nAvgBytesPerSec = InputType.Format.nSamplesPerSec*InputType.Format.nBlockAlign;
+        InputType.Format.cbSize = 0;
+
+        if(InputType.Format.nChannels > 2 || device->FmtType == DevFmtFloat)
+        {
+            InputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
+            InputType.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
+            InputType.Samples.wValidBitsPerSample = InputType.Format.wBitsPerSample;
+            if(device->FmtType == DevFmtFloat)
+                InputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
+            else
+                InputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
+        }
+
+        samples = device->UpdateSize * device->NumUpdates;
+        samples = maxu(samples, 100 * device->Frequency / 1000);
+
+        memset(&DSCBDescription, 0, sizeof(DSCBUFFERDESC));
+        DSCBDescription.dwSize = sizeof(DSCBUFFERDESC);
+        DSCBDescription.dwFlags = 0;
+        DSCBDescription.dwBufferBytes = samples * InputType.Format.nBlockAlign;
+        DSCBDescription.lpwfxFormat = &InputType.Format;
+
+        hr = IDirectSoundCapture_CreateCaptureBuffer(self->DSC, &DSCBDescription, &self->DSCbuffer, NULL);
+    }
+    if(SUCCEEDED(hr))
+    {
+         self->Ring = ll_ringbuffer_create(device->UpdateSize*device->NumUpdates + 1,
+                                           InputType.Format.nBlockAlign);
+         if(self->Ring == NULL)
+             hr = DSERR_OUTOFMEMORY;
+    }
+
+    if(FAILED(hr))
+    {
+        ERR("Device init failed: 0x%08lx\n", hr);
+
+        ll_ringbuffer_free(self->Ring);
+        self->Ring = NULL;
+        if(self->DSCbuffer != NULL)
+            IDirectSoundCaptureBuffer_Release(self->DSCbuffer);
+        self->DSCbuffer = NULL;
+        if(self->DSC)
+            IDirectSoundCapture_Release(self->DSC);
+        self->DSC = NULL;
+
+        return ALC_INVALID_VALUE;
+    }
+
+    self->BufferBytes = DSCBDescription.dwBufferBytes;
+    SetDefaultWFXChannelOrder(device);
+
+    al_string_copy_cstr(&device->DeviceName, deviceName);
+
+    return ALC_NO_ERROR;
+}
+
+static void ALCdsoundCapture_close(ALCdsoundCapture *self)
+{
+    ll_ringbuffer_free(self->Ring);
+    self->Ring = NULL;
+
+    if(self->DSCbuffer != NULL)
+    {
+        IDirectSoundCaptureBuffer_Stop(self->DSCbuffer);
+        IDirectSoundCaptureBuffer_Release(self->DSCbuffer);
+        self->DSCbuffer = NULL;
+    }
+
+    IDirectSoundCapture_Release(self->DSC);
+    self->DSC = NULL;
+}
+
+static ALCboolean ALCdsoundCapture_start(ALCdsoundCapture *self)
+{
+    HRESULT hr;
+
+    hr = IDirectSoundCaptureBuffer_Start(self->DSCbuffer, DSCBSTART_LOOPING);
+    if(FAILED(hr))
+    {
+        ERR("start failed: 0x%08lx\n", hr);
+        aluHandleDisconnect(STATIC_CAST(ALCbackend, self)->mDevice);
+        return ALC_FALSE;
+    }
+
+    return ALC_TRUE;
+}
+
+static void ALCdsoundCapture_stop(ALCdsoundCapture *self)
+{
+    HRESULT hr;
+
+    hr = IDirectSoundCaptureBuffer_Stop(self->DSCbuffer);
+    if(FAILED(hr))
+    {
+        ERR("stop failed: 0x%08lx\n", hr);
+        aluHandleDisconnect(STATIC_CAST(ALCbackend, self)->mDevice);
+    }
+}
+
+static ALCenum ALCdsoundCapture_captureSamples(ALCdsoundCapture *self, ALCvoid *buffer, ALCuint samples)
+{
+    ll_ringbuffer_read(self->Ring, buffer, samples);
+    return ALC_NO_ERROR;
+}
+
+static ALCuint ALCdsoundCapture_availableSamples(ALCdsoundCapture *self)
+{
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+    DWORD ReadCursor, LastCursor, BufferBytes, NumBytes;
+    void *ReadPtr1, *ReadPtr2;
+    DWORD ReadCnt1,  ReadCnt2;
+    DWORD FrameSize;
+    HRESULT hr;
+
+    if(!device->Connected)
+        goto done;
+
+    FrameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
+    BufferBytes = self->BufferBytes;
+    LastCursor = self->Cursor;
+
+    hr = IDirectSoundCaptureBuffer_GetCurrentPosition(self->DSCbuffer, NULL, &ReadCursor);
+    if(SUCCEEDED(hr))
+    {
+        NumBytes = (ReadCursor-LastCursor + BufferBytes) % BufferBytes;
+        if(NumBytes == 0)
+            goto done;
+        hr = IDirectSoundCaptureBuffer_Lock(self->DSCbuffer, LastCursor, NumBytes,
+                                            &ReadPtr1, &ReadCnt1,
+                                            &ReadPtr2, &ReadCnt2, 0);
+    }
+    if(SUCCEEDED(hr))
+    {
+        ll_ringbuffer_write(self->Ring, ReadPtr1, ReadCnt1/FrameSize);
+        if(ReadPtr2 != NULL)
+            ll_ringbuffer_write(self->Ring, ReadPtr2, ReadCnt2/FrameSize);
+        hr = IDirectSoundCaptureBuffer_Unlock(self->DSCbuffer,
+                                              ReadPtr1, ReadCnt1,
+                                              ReadPtr2, ReadCnt2);
+        self->Cursor = (LastCursor+ReadCnt1+ReadCnt2) % BufferBytes;
+    }
+
+    if(FAILED(hr))
+    {
+        ERR("update failed: 0x%08lx\n", hr);
+        aluHandleDisconnect(device);
+    }
+
+done:
+    return ll_ringbuffer_read_space(self->Ring);
+}
+
+
+static inline void AppendAllDevicesList2(const DevMap *entry)
+{ AppendAllDevicesList(al_string_get_cstr(entry->name)); }
+static inline void AppendCaptureDeviceList2(const DevMap *entry)
+{ AppendCaptureDeviceList(al_string_get_cstr(entry->name)); }
+
+typedef struct ALCdsoundBackendFactory {
+    DERIVE_FROM_TYPE(ALCbackendFactory);
+} ALCdsoundBackendFactory;
+#define ALCDSOUNDBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCdsoundBackendFactory, ALCbackendFactory) } }
+
+ALCbackendFactory *ALCdsoundBackendFactory_getFactory(void);
+
+static ALCboolean ALCdsoundBackendFactory_init(ALCdsoundBackendFactory *self);
+static void ALCdsoundBackendFactory_deinit(ALCdsoundBackendFactory *self);
+static ALCboolean ALCdsoundBackendFactory_querySupport(ALCdsoundBackendFactory *self, ALCbackend_Type type);
+static void ALCdsoundBackendFactory_probe(ALCdsoundBackendFactory *self, enum DevProbe type);
+static ALCbackend* ALCdsoundBackendFactory_createBackend(ALCdsoundBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
+DEFINE_ALCBACKENDFACTORY_VTABLE(ALCdsoundBackendFactory);
+
+
+ALCbackendFactory *ALCdsoundBackendFactory_getFactory(void)
+{
+    static ALCdsoundBackendFactory factory = ALCDSOUNDBACKENDFACTORY_INITIALIZER;
+    return STATIC_CAST(ALCbackendFactory, &factory);
+}
+
+
+static ALCboolean ALCdsoundBackendFactory_init(ALCdsoundBackendFactory* UNUSED(self))
+{
+    VECTOR_INIT(PlaybackDevices);
+    VECTOR_INIT(CaptureDevices);
+
+    if(!DSoundLoad())
+        return ALC_FALSE;
+    return ALC_TRUE;
+}
+
+static void ALCdsoundBackendFactory_deinit(ALCdsoundBackendFactory* UNUSED(self))
+{
+    clear_devlist(&PlaybackDevices);
+    VECTOR_DEINIT(PlaybackDevices);
+
+    clear_devlist(&CaptureDevices);
+    VECTOR_DEINIT(CaptureDevices);
+
+#ifdef HAVE_DYNLOAD
+    if(ds_handle)
+        CloseLib(ds_handle);
+    ds_handle = NULL;
+#endif
+}
+
+static ALCboolean ALCdsoundBackendFactory_querySupport(ALCdsoundBackendFactory* UNUSED(self), ALCbackend_Type type)
+{
+    if(type == ALCbackend_Playback || type == ALCbackend_Capture)
+        return ALC_TRUE;
+    return ALC_FALSE;
+}
+
+static void ALCdsoundBackendFactory_probe(ALCdsoundBackendFactory* UNUSED(self), enum DevProbe type)
+{
+    HRESULT hr, hrcom;
+
+    /* Initialize COM to prevent name truncation */
+    hrcom = CoInitialize(NULL);
+    switch(type)
+    {
+        case ALL_DEVICE_PROBE:
+            clear_devlist(&PlaybackDevices);
+            hr = DirectSoundEnumerateW(DSoundEnumDevices, &PlaybackDevices);
+            if(FAILED(hr))
+                ERR("Error enumerating DirectSound playback devices (0x%lx)!\n", hr);
+            VECTOR_FOR_EACH(const DevMap, PlaybackDevices, AppendAllDevicesList2);
+            break;
+
+        case CAPTURE_DEVICE_PROBE:
+            clear_devlist(&CaptureDevices);
+            hr = DirectSoundCaptureEnumerateW(DSoundEnumDevices, &CaptureDevices);
+            if(FAILED(hr))
+                ERR("Error enumerating DirectSound capture devices (0x%lx)!\n", hr);
+            VECTOR_FOR_EACH(const DevMap, CaptureDevices, AppendCaptureDeviceList2);
+            break;
+    }
+    if(SUCCEEDED(hrcom))
+        CoUninitialize();
+}
+
+static ALCbackend* ALCdsoundBackendFactory_createBackend(ALCdsoundBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
+{
+    if(type == ALCbackend_Playback)
+    {
+        ALCdsoundPlayback *backend;
+        NEW_OBJ(backend, ALCdsoundPlayback)(device);
+        if(!backend) return NULL;
+        return STATIC_CAST(ALCbackend, backend);
+    }
+
+    if(type == ALCbackend_Capture)
+    {
+        ALCdsoundCapture *backend;
+        NEW_OBJ(backend, ALCdsoundCapture)(device);
+        if(!backend) return NULL;
+        return STATIC_CAST(ALCbackend, backend);
+    }
+
+    return NULL;
+}

+ 627 - 0
Engine/lib/openal-soft/Alc/backends/jack.c

@@ -0,0 +1,627 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 1999-2007 by authors.
+ * This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the
+ *  Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <memory.h>
+
+#include "alMain.h"
+#include "alu.h"
+#include "threads.h"
+#include "compat.h"
+
+#include "backends/base.h"
+
+#include <jack/jack.h>
+#include <jack/ringbuffer.h>
+
+
+static const ALCchar jackDevice[] = "JACK Default";
+
+
+#ifdef HAVE_DYNLOAD
+#define JACK_FUNCS(MAGIC)          \
+    MAGIC(jack_client_open);       \
+    MAGIC(jack_client_close);      \
+    MAGIC(jack_client_name_size);  \
+    MAGIC(jack_get_client_name);   \
+    MAGIC(jack_connect);           \
+    MAGIC(jack_activate);          \
+    MAGIC(jack_deactivate);        \
+    MAGIC(jack_port_register);     \
+    MAGIC(jack_port_unregister);   \
+    MAGIC(jack_port_get_buffer);   \
+    MAGIC(jack_port_name);         \
+    MAGIC(jack_get_ports);         \
+    MAGIC(jack_free);              \
+    MAGIC(jack_get_sample_rate);   \
+    MAGIC(jack_set_error_function); \
+    MAGIC(jack_set_process_callback); \
+    MAGIC(jack_set_buffer_size_callback); \
+    MAGIC(jack_set_buffer_size);   \
+    MAGIC(jack_get_buffer_size);
+
+static void *jack_handle;
+#define MAKE_FUNC(f) static __typeof(f) * p##f
+JACK_FUNCS(MAKE_FUNC);
+#undef MAKE_FUNC
+
+#define jack_client_open pjack_client_open
+#define jack_client_close pjack_client_close
+#define jack_client_name_size pjack_client_name_size
+#define jack_get_client_name pjack_get_client_name
+#define jack_connect pjack_connect
+#define jack_activate pjack_activate
+#define jack_deactivate pjack_deactivate
+#define jack_port_register pjack_port_register
+#define jack_port_unregister pjack_port_unregister
+#define jack_port_get_buffer pjack_port_get_buffer
+#define jack_port_name pjack_port_name
+#define jack_get_ports pjack_get_ports
+#define jack_free pjack_free
+#define jack_get_sample_rate pjack_get_sample_rate
+#define jack_set_error_function pjack_set_error_function
+#define jack_set_process_callback pjack_set_process_callback
+#define jack_set_buffer_size_callback pjack_set_buffer_size_callback
+#define jack_set_buffer_size pjack_set_buffer_size
+#define jack_get_buffer_size pjack_get_buffer_size
+#endif
+
+
+static jack_options_t ClientOptions = JackNullOption;
+
+static ALCboolean jack_load(void)
+{
+    ALCboolean error = ALC_FALSE;
+
+#ifdef HAVE_DYNLOAD
+    if(!jack_handle)
+    {
+#ifdef _WIN32
+#define JACKLIB "libjack.dll"
+#else
+#define JACKLIB "libjack.so.0"
+#endif
+        jack_handle = LoadLib(JACKLIB);
+        if(!jack_handle)
+            return ALC_FALSE;
+
+        error = ALC_FALSE;
+#define LOAD_FUNC(f) do {                                                     \
+    p##f = GetSymbol(jack_handle, #f);                                        \
+    if(p##f == NULL) {                                                        \
+        error = ALC_TRUE;                                                     \
+    }                                                                         \
+} while(0)
+        JACK_FUNCS(LOAD_FUNC);
+#undef LOAD_FUNC
+
+        if(error)
+        {
+            CloseLib(jack_handle);
+            jack_handle = NULL;
+            return ALC_FALSE;
+        }
+    }
+#endif
+
+    return !error;
+}
+
+
+typedef struct ALCjackPlayback {
+    DERIVE_FROM_TYPE(ALCbackend);
+
+    jack_client_t *Client;
+    jack_port_t *Port[MAX_OUTPUT_CHANNELS];
+
+    ll_ringbuffer_t *Ring;
+    alcnd_t Cond;
+
+    volatile int killNow;
+    althrd_t thread;
+} ALCjackPlayback;
+
+static int ALCjackPlayback_bufferSizeNotify(jack_nframes_t numframes, void *arg);
+
+static int ALCjackPlayback_process(jack_nframes_t numframes, void *arg);
+static int ALCjackPlayback_mixerProc(void *arg);
+
+static void ALCjackPlayback_Construct(ALCjackPlayback *self, ALCdevice *device);
+static void ALCjackPlayback_Destruct(ALCjackPlayback *self);
+static ALCenum ALCjackPlayback_open(ALCjackPlayback *self, const ALCchar *name);
+static void ALCjackPlayback_close(ALCjackPlayback *self);
+static ALCboolean ALCjackPlayback_reset(ALCjackPlayback *self);
+static ALCboolean ALCjackPlayback_start(ALCjackPlayback *self);
+static void ALCjackPlayback_stop(ALCjackPlayback *self);
+static DECLARE_FORWARD2(ALCjackPlayback, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
+static DECLARE_FORWARD(ALCjackPlayback, ALCbackend, ALCuint, availableSamples)
+static ClockLatency ALCjackPlayback_getClockLatency(ALCjackPlayback *self);
+static void ALCjackPlayback_lock(ALCjackPlayback *self);
+static void ALCjackPlayback_unlock(ALCjackPlayback *self);
+DECLARE_DEFAULT_ALLOCATORS(ALCjackPlayback)
+
+DEFINE_ALCBACKEND_VTABLE(ALCjackPlayback);
+
+
+static void ALCjackPlayback_Construct(ALCjackPlayback *self, ALCdevice *device)
+{
+    ALuint i;
+
+    ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
+    SET_VTABLE2(ALCjackPlayback, ALCbackend, self);
+
+    alcnd_init(&self->Cond);
+
+    self->Client = NULL;
+    for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
+        self->Port[i] = NULL;
+    self->Ring = NULL;
+
+    self->killNow = 1;
+}
+
+static void ALCjackPlayback_Destruct(ALCjackPlayback *self)
+{
+    ALuint i;
+
+    if(self->Client)
+    {
+        for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
+        {
+            if(self->Port[i])
+                jack_port_unregister(self->Client, self->Port[i]);
+            self->Port[i] = NULL;
+        }
+        jack_client_close(self->Client);
+        self->Client = NULL;
+    }
+
+    alcnd_destroy(&self->Cond);
+
+    ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
+}
+
+
+static int ALCjackPlayback_bufferSizeNotify(jack_nframes_t numframes, void *arg)
+{
+    ALCjackPlayback *self = arg;
+    ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
+    ALuint bufsize;
+
+    ALCjackPlayback_lock(self);
+    device->UpdateSize = numframes;
+    device->NumUpdates = 2;
+    TRACE("%u update size x%u\n", device->UpdateSize, device->NumUpdates);
+
+    bufsize = device->UpdateSize;
+    if(ConfigValueUInt(al_string_get_cstr(device->DeviceName), "jack", "buffer-size", &bufsize))
+        bufsize = maxu(NextPowerOf2(bufsize), device->UpdateSize);
+    bufsize += device->UpdateSize;
+
+    ll_ringbuffer_free(self->Ring);
+    self->Ring = ll_ringbuffer_create(bufsize, FrameSizeFromDevFmt(device->FmtChans, device->FmtType));
+    if(!self->Ring)
+    {
+        ERR("Failed to reallocate ringbuffer\n");
+        aluHandleDisconnect(device);
+    }
+    ALCjackPlayback_unlock(self);
+    return 0;
+}
+
+
+static int ALCjackPlayback_process(jack_nframes_t numframes, void *arg)
+{
+    ALCjackPlayback *self = arg;
+    jack_default_audio_sample_t *out[MAX_OUTPUT_CHANNELS];
+    ll_ringbuffer_data_t data[2];
+    jack_nframes_t total = 0;
+    jack_nframes_t todo;
+    ALuint i, c, numchans;
+
+    ll_ringbuffer_get_read_vector(self->Ring, data);
+
+    for(c = 0;c < MAX_OUTPUT_CHANNELS && self->Port[c];c++)
+        out[c] = jack_port_get_buffer(self->Port[c], numframes);
+    numchans = c;
+
+    todo = minu(numframes, data[0].len);
+    for(c = 0;c < numchans;c++)
+    {
+        for(i = 0;i < todo;i++)
+            out[c][i] = ((ALfloat*)data[0].buf)[i*numchans + c];
+        out[c] += todo;
+    }
+    total += todo;
+
+    todo = minu(numframes-total, data[1].len);
+    if(todo > 0)
+    {
+        for(c = 0;c < numchans;c++)
+        {
+            for(i = 0;i < todo;i++)
+                out[c][i] = ((ALfloat*)data[1].buf)[i*numchans + c];
+            out[c] += todo;
+        }
+        total += todo;
+    }
+
+    ll_ringbuffer_read_advance(self->Ring, total);
+    alcnd_signal(&self->Cond);
+
+    if(numframes > total)
+    {
+        todo = numframes-total;
+        for(c = 0;c < numchans;c++)
+        {
+            for(i = 0;i < todo;i++)
+                out[c][i] = 0.0f;
+        }
+    }
+
+    return 0;
+}
+
+static int ALCjackPlayback_mixerProc(void *arg)
+{
+    ALCjackPlayback *self = arg;
+    ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
+    ll_ringbuffer_data_t data[2];
+
+    SetRTPriority();
+    althrd_setname(althrd_current(), MIXER_THREAD_NAME);
+
+    ALCjackPlayback_lock(self);
+    while(!self->killNow && device->Connected)
+    {
+        ALuint todo, len1, len2;
+
+        /* NOTE: Unfortunately, there is an unavoidable race condition here.
+         * It's possible for the process() method to run, updating the read
+         * pointer and signaling the condition variable, in between the mixer
+         * loop checking the write size and waiting for the condition variable.
+         * This will cause the mixer loop to wait until the *next* process()
+         * invocation, most likely writing silence for it.
+         *
+         * However, this should only happen if the mixer is running behind
+         * anyway (as ideally we'll be asleep in alcnd_wait by the time the
+         * process() method is invoked), so this behavior is not unwarranted.
+         * It's unfortunate since it'll be wasting time sleeping that could be
+         * used to catch up, but there's no way around it without blocking in
+         * the process() method.
+         */
+        if(ll_ringbuffer_write_space(self->Ring) < device->UpdateSize)
+        {
+            alcnd_wait(&self->Cond, &STATIC_CAST(ALCbackend,self)->mMutex);
+            continue;
+        }
+
+        ll_ringbuffer_get_write_vector(self->Ring, data);
+        todo  = data[0].len + data[1].len;
+        todo -= todo%device->UpdateSize;
+
+        len1 = minu(data[0].len, todo);
+        len2 = minu(data[1].len, todo-len1);
+
+        aluMixData(device, data[0].buf, len1);
+        if(len2 > 0)
+            aluMixData(device, data[1].buf, len2);
+        ll_ringbuffer_write_advance(self->Ring, todo);
+    }
+    ALCjackPlayback_unlock(self);
+
+    return 0;
+}
+
+
+static ALCenum ALCjackPlayback_open(ALCjackPlayback *self, const ALCchar *name)
+{
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+    const char *client_name = "alsoft";
+    jack_status_t status;
+
+    if(!name)
+        name = jackDevice;
+    else if(strcmp(name, jackDevice) != 0)
+        return ALC_INVALID_VALUE;
+
+    self->Client = jack_client_open(client_name, ClientOptions, &status, NULL);
+    if(self->Client == NULL)
+    {
+        ERR("jack_client_open() failed, status = 0x%02x\n", status);
+        return ALC_INVALID_VALUE;
+    }
+    if((status&JackServerStarted))
+        TRACE("JACK server started\n");
+    if((status&JackNameNotUnique))
+    {
+        client_name = jack_get_client_name(self->Client);
+        TRACE("Client name not unique, got `%s' instead\n", client_name);
+    }
+
+    jack_set_process_callback(self->Client, ALCjackPlayback_process, self);
+    jack_set_buffer_size_callback(self->Client, ALCjackPlayback_bufferSizeNotify, self);
+
+    al_string_copy_cstr(&device->DeviceName, name);
+
+    return ALC_NO_ERROR;
+}
+
+static void ALCjackPlayback_close(ALCjackPlayback *self)
+{
+    ALuint i;
+
+    for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
+    {
+        if(self->Port[i])
+            jack_port_unregister(self->Client, self->Port[i]);
+        self->Port[i] = NULL;
+    }
+    jack_client_close(self->Client);
+    self->Client = NULL;
+}
+
+static ALCboolean ALCjackPlayback_reset(ALCjackPlayback *self)
+{
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+    ALuint numchans, i;
+    ALuint bufsize;
+
+    for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
+    {
+        if(self->Port[i])
+            jack_port_unregister(self->Client, self->Port[i]);
+        self->Port[i] = NULL;
+    }
+
+    /* Ignore the requested buffer metrics and just keep one JACK-sized buffer
+     * ready for when requested. Note that one period's worth of audio in the
+     * ring buffer will always be left unfilled because one element of the ring
+     * buffer will not be writeable, and we only write in period-sized chunks.
+     */
+    device->Frequency = jack_get_sample_rate(self->Client);
+    device->UpdateSize = jack_get_buffer_size(self->Client);
+    device->NumUpdates = 2;
+
+    bufsize = device->UpdateSize;
+    if(ConfigValueUInt(al_string_get_cstr(device->DeviceName), "jack", "buffer-size", &bufsize))
+        bufsize = maxu(NextPowerOf2(bufsize), device->UpdateSize);
+    bufsize += device->UpdateSize;
+
+    /* Force 32-bit float output. */
+    device->FmtType = DevFmtFloat;
+
+    numchans = ChannelsFromDevFmt(device->FmtChans);
+    for(i = 0;i < numchans;i++)
+    {
+        char name[64];
+        snprintf(name, sizeof(name), "channel_%d", i+1);
+        self->Port[i] = jack_port_register(self->Client, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
+        if(self->Port[i] == NULL)
+        {
+            ERR("Not enough JACK ports available for %s output\n", DevFmtChannelsString(device->FmtChans));
+            if(i == 0) return ALC_FALSE;
+            break;
+        }
+    }
+    if(i < numchans)
+    {
+        if(i == 1)
+            device->FmtChans = DevFmtMono;
+        else
+        {
+            for(--i;i >= 2;i--)
+            {
+                jack_port_unregister(self->Client, self->Port[i]);
+                self->Port[i] = NULL;
+            }
+            device->FmtChans = DevFmtStereo;
+        }
+    }
+
+    ll_ringbuffer_free(self->Ring);
+    self->Ring = ll_ringbuffer_create(bufsize, FrameSizeFromDevFmt(device->FmtChans, device->FmtType));
+    if(!self->Ring)
+    {
+        ERR("Failed to allocate ringbuffer\n");
+        return ALC_FALSE;
+    }
+
+    SetDefaultChannelOrder(device);
+
+    return ALC_TRUE;
+}
+
+static ALCboolean ALCjackPlayback_start(ALCjackPlayback *self)
+{
+    const char **ports;
+    ALuint i;
+
+    if(jack_activate(self->Client))
+    {
+        ERR("Failed to activate client\n");
+        return ALC_FALSE;
+    }
+
+    ports = jack_get_ports(self->Client, NULL, NULL, JackPortIsPhysical|JackPortIsInput);
+    if(ports == NULL)
+    {
+        ERR("No physical playback ports found\n");
+        jack_deactivate(self->Client);
+        return ALC_FALSE;
+    }
+    for(i = 0;i < MAX_OUTPUT_CHANNELS && self->Port[i];i++)
+    {
+        if(!ports[i])
+        {
+            ERR("No physical playback port for \"%s\"\n", jack_port_name(self->Port[i]));
+            break;
+        }
+        if(jack_connect(self->Client, jack_port_name(self->Port[i]), ports[i]))
+            ERR("Failed to connect output port \"%s\" to \"%s\"\n", jack_port_name(self->Port[i]), ports[i]);
+    }
+    jack_free(ports);
+
+    self->killNow = 0;
+    if(althrd_create(&self->thread, ALCjackPlayback_mixerProc, self) != althrd_success)
+    {
+        jack_deactivate(self->Client);
+        return ALC_FALSE;
+    }
+
+    return ALC_TRUE;
+}
+
+static void ALCjackPlayback_stop(ALCjackPlayback *self)
+{
+    int res;
+
+    if(self->killNow)
+        return;
+
+    self->killNow = 1;
+    /* Lock the backend to ensure we don't flag the mixer to die and signal the
+     * mixer to wake up in between it checking the flag and going to sleep and
+     * wait for a wakeup (potentially leading to it never waking back up to see
+     * the flag). */
+    ALCjackPlayback_lock(self);
+    ALCjackPlayback_unlock(self);
+    alcnd_signal(&self->Cond);
+    althrd_join(self->thread, &res);
+
+    jack_deactivate(self->Client);
+}
+
+
+static ClockLatency ALCjackPlayback_getClockLatency(ALCjackPlayback *self)
+{
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+    ClockLatency ret;
+
+    ALCjackPlayback_lock(self);
+    ret.ClockTime = GetDeviceClockTime(device);
+    ret.Latency = ll_ringbuffer_read_space(self->Ring) * DEVICE_CLOCK_RES /
+                  device->Frequency;
+    ALCjackPlayback_unlock(self);
+
+    return ret;
+}
+
+
+static void ALCjackPlayback_lock(ALCjackPlayback *self)
+{
+    almtx_lock(&STATIC_CAST(ALCbackend,self)->mMutex);
+}
+
+static void ALCjackPlayback_unlock(ALCjackPlayback *self)
+{
+    almtx_unlock(&STATIC_CAST(ALCbackend,self)->mMutex);
+}
+
+
+static void jack_msg_handler(const char *message)
+{
+    WARN("%s\n", message);
+}
+
+typedef struct ALCjackBackendFactory {
+    DERIVE_FROM_TYPE(ALCbackendFactory);
+} ALCjackBackendFactory;
+#define ALCJACKBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCjackBackendFactory, ALCbackendFactory) } }
+
+static ALCboolean ALCjackBackendFactory_init(ALCjackBackendFactory* UNUSED(self))
+{
+    jack_client_t *client;
+    jack_status_t status;
+
+    if(!jack_load())
+        return ALC_FALSE;
+
+    if(!GetConfigValueBool(NULL, "jack", "spawn-server", 0))
+        ClientOptions |= JackNoStartServer;
+
+    jack_set_error_function(jack_msg_handler);
+    client = jack_client_open("alsoft", ClientOptions, &status, NULL);
+    jack_set_error_function(NULL);
+    if(client == NULL)
+    {
+        WARN("jack_client_open() failed, 0x%02x\n", status);
+        if((status&JackServerFailed) && !(ClientOptions&JackNoStartServer))
+            ERR("Unable to connect to JACK server\n");
+        return ALC_FALSE;
+    }
+
+    jack_client_close(client);
+    return ALC_TRUE;
+}
+
+static void ALCjackBackendFactory_deinit(ALCjackBackendFactory* UNUSED(self))
+{
+#ifdef HAVE_DYNLOAD
+    if(jack_handle)
+        CloseLib(jack_handle);
+    jack_handle = NULL;
+#endif
+}
+
+static ALCboolean ALCjackBackendFactory_querySupport(ALCjackBackendFactory* UNUSED(self), ALCbackend_Type type)
+{
+    if(type == ALCbackend_Playback)
+        return ALC_TRUE;
+    return ALC_FALSE;
+}
+
+static void ALCjackBackendFactory_probe(ALCjackBackendFactory* UNUSED(self), enum DevProbe type)
+{
+    switch(type)
+    {
+        case ALL_DEVICE_PROBE:
+            AppendAllDevicesList(jackDevice);
+            break;
+
+        case CAPTURE_DEVICE_PROBE:
+            break;
+    }
+}
+
+static ALCbackend* ALCjackBackendFactory_createBackend(ALCjackBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
+{
+    if(type == ALCbackend_Playback)
+    {
+        ALCjackPlayback *backend;
+        NEW_OBJ(backend, ALCjackPlayback)(device);
+        if(!backend) return NULL;
+        return STATIC_CAST(ALCbackend, backend);
+    }
+
+    return NULL;
+}
+
+DEFINE_ALCBACKENDFACTORY_VTABLE(ALCjackBackendFactory);
+
+
+ALCbackendFactory *ALCjackBackendFactory_getFactory(void)
+{
+    static ALCjackBackendFactory factory = ALCJACKBACKENDFACTORY_INITIALIZER;
+    return STATIC_CAST(ALCbackendFactory, &factory);
+}

+ 133 - 0
Engine/lib/openal-soft/Alc/backends/loopback.c

@@ -0,0 +1,133 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 2011 by Chris Robinson
+ * This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the
+ *  Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+
+#include "alMain.h"
+#include "alu.h"
+
+#include "backends/base.h"
+
+
+typedef struct ALCloopback {
+    DERIVE_FROM_TYPE(ALCbackend);
+} ALCloopback;
+
+static void ALCloopback_Construct(ALCloopback *self, ALCdevice *device);
+static DECLARE_FORWARD(ALCloopback, ALCbackend, void, Destruct)
+static ALCenum ALCloopback_open(ALCloopback *self, const ALCchar *name);
+static void ALCloopback_close(ALCloopback *self);
+static ALCboolean ALCloopback_reset(ALCloopback *self);
+static ALCboolean ALCloopback_start(ALCloopback *self);
+static void ALCloopback_stop(ALCloopback *self);
+static DECLARE_FORWARD2(ALCloopback, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
+static DECLARE_FORWARD(ALCloopback, ALCbackend, ALCuint, availableSamples)
+static DECLARE_FORWARD(ALCloopback, ALCbackend, ClockLatency, getClockLatency)
+static DECLARE_FORWARD(ALCloopback, ALCbackend, void, lock)
+static DECLARE_FORWARD(ALCloopback, ALCbackend, void, unlock)
+DECLARE_DEFAULT_ALLOCATORS(ALCloopback)
+DEFINE_ALCBACKEND_VTABLE(ALCloopback);
+
+
+static void ALCloopback_Construct(ALCloopback *self, ALCdevice *device)
+{
+    ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
+    SET_VTABLE2(ALCloopback, ALCbackend, self);
+}
+
+
+static ALCenum ALCloopback_open(ALCloopback *self, const ALCchar *name)
+{
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+
+    al_string_copy_cstr(&device->DeviceName, name);
+    return ALC_NO_ERROR;
+}
+
+static void ALCloopback_close(ALCloopback* UNUSED(self))
+{
+}
+
+static ALCboolean ALCloopback_reset(ALCloopback *self)
+{
+    SetDefaultWFXChannelOrder(STATIC_CAST(ALCbackend, self)->mDevice);
+    return ALC_TRUE;
+}
+
+static ALCboolean ALCloopback_start(ALCloopback* UNUSED(self))
+{
+    return ALC_TRUE;
+}
+
+static void ALCloopback_stop(ALCloopback* UNUSED(self))
+{
+}
+
+
+typedef struct ALCloopbackFactory {
+    DERIVE_FROM_TYPE(ALCbackendFactory);
+} ALCloopbackFactory;
+#define ALCNULLBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCloopbackFactory, ALCbackendFactory) } }
+
+ALCbackendFactory *ALCloopbackFactory_getFactory(void);
+static ALCboolean ALCloopbackFactory_init(ALCloopbackFactory *self);
+static DECLARE_FORWARD(ALCloopbackFactory, ALCbackendFactory, void, deinit)
+static ALCboolean ALCloopbackFactory_querySupport(ALCloopbackFactory *self, ALCbackend_Type type);
+static void ALCloopbackFactory_probe(ALCloopbackFactory *self, enum DevProbe type);
+static ALCbackend* ALCloopbackFactory_createBackend(ALCloopbackFactory *self, ALCdevice *device, ALCbackend_Type type);
+DEFINE_ALCBACKENDFACTORY_VTABLE(ALCloopbackFactory);
+
+
+ALCbackendFactory *ALCloopbackFactory_getFactory(void)
+{
+    static ALCloopbackFactory factory = ALCNULLBACKENDFACTORY_INITIALIZER;
+    return STATIC_CAST(ALCbackendFactory, &factory);
+}
+
+static ALCboolean ALCloopbackFactory_init(ALCloopbackFactory* UNUSED(self))
+{
+    return ALC_TRUE;
+}
+
+static ALCboolean ALCloopbackFactory_querySupport(ALCloopbackFactory* UNUSED(self), ALCbackend_Type type)
+{
+    if(type == ALCbackend_Loopback)
+        return ALC_TRUE;
+    return ALC_FALSE;
+}
+
+static void ALCloopbackFactory_probe(ALCloopbackFactory* UNUSED(self), enum DevProbe UNUSED(type))
+{
+}
+
+static ALCbackend* ALCloopbackFactory_createBackend(ALCloopbackFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
+{
+    if(type == ALCbackend_Loopback)
+    {
+        ALCloopback *backend;
+        NEW_OBJ(backend, ALCloopback)(device);
+        if(!backend) return NULL;
+        return STATIC_CAST(ALCbackend, backend);
+    }
+
+    return NULL;
+}

+ 1911 - 0
Engine/lib/openal-soft/Alc/backends/mmdevapi.c

@@ -0,0 +1,1911 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 2011 by authors.
+ * This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the
+ *  Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#include "config.h"
+
+#define COBJMACROS
+#include <stdlib.h>
+#include <stdio.h>
+#include <memory.h>
+
+#include <wtypes.h>
+#include <mmdeviceapi.h>
+#include <audioclient.h>
+#include <cguid.h>
+#include <devpropdef.h>
+#include <mmreg.h>
+#include <propsys.h>
+#include <propkey.h>
+#include <devpkey.h>
+#ifndef _WAVEFORMATEXTENSIBLE_
+#include <ks.h>
+#include <ksmedia.h>
+#endif
+
+#include "alMain.h"
+#include "alu.h"
+#include "threads.h"
+#include "compat.h"
+#include "alstring.h"
+
+#include "backends/base.h"
+
+
+DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
+DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
+
+DEFINE_DEVPROPKEY(DEVPKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80,0x20, 0x67,0xd1,0x46,0xa8,0x50,0xe0, 14);
+DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_FormFactor, 0x1da5d803, 0xd492, 0x4edd, 0x8c,0x23, 0xe0,0xc0,0xff,0xee,0x7f,0x0e, 0);
+DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_GUID, 0x1da5d803, 0xd492, 0x4edd, 0x8c, 0x23,0xe0, 0xc0,0xff,0xee,0x7f,0x0e, 4 );
+
+#define MONO SPEAKER_FRONT_CENTER
+#define STEREO (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT)
+#define QUAD (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT)
+#define X5DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
+#define X5DOT1REAR (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT)
+#define X6DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_CENTER|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
+#define X7DOT1 (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 X7DOT1_WIDE (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 DEVNAME_HEAD "OpenAL Soft on "
+
+
+typedef struct {
+    al_string name;
+    al_string endpoint_guid; // obtained from PKEY_AudioEndpoint_GUID , set to "Unknown device GUID" if absent.
+    WCHAR *devid;
+} DevMap;
+TYPEDEF_VECTOR(DevMap, vector_DevMap)
+
+static void clear_devlist(vector_DevMap *list)
+{
+#define CLEAR_DEVMAP(i) do {     \
+    AL_STRING_DEINIT((i)->name); \
+    AL_STRING_DEINIT((i)->endpoint_guid); \
+    free((i)->devid);            \
+    (i)->devid = NULL;           \
+} while(0)
+    VECTOR_FOR_EACH(DevMap, *list, CLEAR_DEVMAP);
+    VECTOR_RESIZE(*list, 0, 0);
+#undef CLEAR_DEVMAP
+}
+
+static vector_DevMap PlaybackDevices;
+static vector_DevMap CaptureDevices;
+
+
+static HANDLE ThreadHdl;
+static DWORD ThreadID;
+
+typedef struct {
+    HANDLE FinishedEvt;
+    HRESULT result;
+} ThreadRequest;
+
+#define WM_USER_First       (WM_USER+0)
+#define WM_USER_OpenDevice  (WM_USER+0)
+#define WM_USER_ResetDevice (WM_USER+1)
+#define WM_USER_StartDevice (WM_USER+2)
+#define WM_USER_StopDevice  (WM_USER+3)
+#define WM_USER_CloseDevice (WM_USER+4)
+#define WM_USER_Enumerate   (WM_USER+5)
+#define WM_USER_Last        (WM_USER+5)
+
+static inline void ReturnMsgResponse(ThreadRequest *req, HRESULT res)
+{
+    req->result = res;
+    SetEvent(req->FinishedEvt);
+}
+
+static HRESULT WaitForResponse(ThreadRequest *req)
+{
+    if(WaitForSingleObject(req->FinishedEvt, INFINITE) == WAIT_OBJECT_0)
+        return req->result;
+    ERR("Message response error: %lu\n", GetLastError());
+    return E_FAIL;
+}
+
+
+static void get_device_name_and_guid(IMMDevice *device, al_string *name, al_string *guid)
+{
+    IPropertyStore *ps;
+    PROPVARIANT pvname;
+    PROPVARIANT pvguid;
+    HRESULT hr;
+
+    al_string_copy_cstr(name, DEVNAME_HEAD);
+
+    hr = IMMDevice_OpenPropertyStore(device, STGM_READ, &ps);
+    if(FAILED(hr))
+    {
+        WARN("OpenPropertyStore failed: 0x%08lx\n", hr);
+        al_string_append_cstr(name, "Unknown Device Name");
+        if(guid!=NULL)al_string_copy_cstr(guid, "Unknown Device GUID");
+        return;
+    }
+
+    PropVariantInit(&pvname);
+
+    hr = IPropertyStore_GetValue(ps, (const PROPERTYKEY*)&DEVPKEY_Device_FriendlyName, &pvname);
+    if(FAILED(hr))
+    {
+        WARN("GetValue Device_FriendlyName failed: 0x%08lx\n", hr);
+        al_string_append_cstr(name, "Unknown Device Name");
+    }
+    else if(pvname.vt == VT_LPWSTR)
+        al_string_append_wcstr(name, pvname.pwszVal);
+    else
+    {
+        WARN("Unexpected PROPVARIANT type: 0x%04x\n", pvname.vt);
+        al_string_append_cstr(name, "Unknown Device Name");
+    }
+    PropVariantClear(&pvname);
+
+    if(guid!=NULL){
+        PropVariantInit(&pvguid);
+
+        hr = IPropertyStore_GetValue(ps, (const PROPERTYKEY*)&PKEY_AudioEndpoint_GUID, &pvguid);
+        if(FAILED(hr))
+        {
+            WARN("GetValue AudioEndpoint_GUID failed: 0x%08lx\n", hr);
+            al_string_copy_cstr(guid, "Unknown Device GUID");
+        }
+        else if(pvguid.vt == VT_LPWSTR)
+            al_string_copy_wcstr(guid, pvguid.pwszVal);
+        else
+        {
+            WARN("Unexpected PROPVARIANT type: 0x%04x\n", pvguid.vt);
+            al_string_copy_cstr(guid, "Unknown Device GUID");
+        }
+
+        PropVariantClear(&pvguid);
+    }
+
+    IPropertyStore_Release(ps);
+}
+
+static void get_device_formfactor(IMMDevice *device, EndpointFormFactor *formfactor)
+{
+    IPropertyStore *ps;
+    PROPVARIANT pvform;
+    HRESULT hr;
+
+    hr = IMMDevice_OpenPropertyStore(device, STGM_READ, &ps);
+    if(FAILED(hr))
+    {
+        WARN("OpenPropertyStore failed: 0x%08lx\n", hr);
+        return;
+    }
+
+    PropVariantInit(&pvform);
+
+    hr = IPropertyStore_GetValue(ps, &PKEY_AudioEndpoint_FormFactor, &pvform);
+    if(FAILED(hr))
+        WARN("GetValue AudioEndpoint_FormFactor failed: 0x%08lx\n", hr);
+    else if(pvform.vt == VT_UI4)
+        *formfactor = pvform.ulVal;
+    else if(pvform.vt == VT_EMPTY)
+        *formfactor = UnknownFormFactor;
+    else
+        WARN("Unexpected PROPVARIANT type: 0x%04x\n", pvform.vt);
+
+    PropVariantClear(&pvform);
+    IPropertyStore_Release(ps);
+}
+
+
+static void add_device(IMMDevice *device, LPCWSTR devid, vector_DevMap *list)
+{
+    int count = 0;
+    al_string tmpname;
+    DevMap entry;
+
+    AL_STRING_INIT(tmpname);
+    AL_STRING_INIT(entry.name);
+    AL_STRING_INIT(entry.endpoint_guid);
+
+    entry.devid = strdupW(devid);
+    get_device_name_and_guid(device, &tmpname, &entry.endpoint_guid);
+
+    while(1)
+    {
+        const DevMap *iter;
+
+        al_string_copy(&entry.name, tmpname);
+        if(count != 0)
+        {
+            char str[64];
+            snprintf(str, sizeof(str), " #%d", count+1);
+            al_string_append_cstr(&entry.name, str);
+        }
+
+#define MATCH_ENTRY(i) (al_string_cmp(entry.name, (i)->name) == 0)
+        VECTOR_FIND_IF(iter, const DevMap, *list, MATCH_ENTRY);
+        if(iter == VECTOR_END(*list)) break;
+#undef MATCH_ENTRY
+        count++;
+    }
+
+    TRACE("Got device \"%s\", \"%s\", \"%ls\"\n", al_string_get_cstr(entry.name), al_string_get_cstr(entry.endpoint_guid), entry.devid);
+    VECTOR_PUSH_BACK(*list, entry);
+
+    AL_STRING_DEINIT(tmpname);
+}
+
+static LPWSTR get_device_id(IMMDevice *device)
+{
+    LPWSTR devid;
+    HRESULT hr;
+
+    hr = IMMDevice_GetId(device, &devid);
+    if(FAILED(hr))
+    {
+        ERR("Failed to get device id: %lx\n", hr);
+        return NULL;
+    }
+
+    return devid;
+}
+
+static HRESULT probe_devices(IMMDeviceEnumerator *devenum, EDataFlow flowdir, vector_DevMap *list)
+{
+    IMMDeviceCollection *coll;
+    IMMDevice *defdev = NULL;
+    LPWSTR defdevid = NULL;
+    HRESULT hr;
+    UINT count;
+    UINT i;
+
+    hr = IMMDeviceEnumerator_EnumAudioEndpoints(devenum, flowdir, DEVICE_STATE_ACTIVE, &coll);
+    if(FAILED(hr))
+    {
+        ERR("Failed to enumerate audio endpoints: 0x%08lx\n", hr);
+        return hr;
+    }
+
+    count = 0;
+    hr = IMMDeviceCollection_GetCount(coll, &count);
+    if(SUCCEEDED(hr) && count > 0)
+    {
+        clear_devlist(list);
+        VECTOR_RESIZE(*list, 0, count);
+
+        hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(devenum, flowdir,
+                                                         eMultimedia, &defdev);
+    }
+    if(SUCCEEDED(hr) && defdev != NULL)
+    {
+        defdevid = get_device_id(defdev);
+        if(defdevid)
+            add_device(defdev, defdevid, list);
+    }
+
+    for(i = 0;i < count;++i)
+    {
+        IMMDevice *device;
+        LPWSTR devid;
+
+        hr = IMMDeviceCollection_Item(coll, i, &device);
+        if(FAILED(hr)) continue;
+
+        devid = get_device_id(device);
+        if(devid)
+        {
+            if(wcscmp(devid, defdevid) != 0)
+                add_device(device, devid, list);
+            CoTaskMemFree(devid);
+        }
+        IMMDevice_Release(device);
+    }
+
+    if(defdev) IMMDevice_Release(defdev);
+    if(defdevid) CoTaskMemFree(defdevid);
+    IMMDeviceCollection_Release(coll);
+
+    return S_OK;
+}
+
+
+/* Proxy interface used by the message handler. */
+struct ALCmmdevProxyVtable;
+
+typedef struct ALCmmdevProxy {
+    const struct ALCmmdevProxyVtable *vtbl;
+} ALCmmdevProxy;
+
+struct ALCmmdevProxyVtable {
+    HRESULT (*const openProxy)(ALCmmdevProxy*);
+    void (*const closeProxy)(ALCmmdevProxy*);
+
+    HRESULT (*const resetProxy)(ALCmmdevProxy*);
+    HRESULT (*const startProxy)(ALCmmdevProxy*);
+    void  (*const stopProxy)(ALCmmdevProxy*);
+};
+
+#define DEFINE_ALCMMDEVPROXY_VTABLE(T)                                        \
+DECLARE_THUNK(T, ALCmmdevProxy, HRESULT, openProxy)                           \
+DECLARE_THUNK(T, ALCmmdevProxy, void, closeProxy)                             \
+DECLARE_THUNK(T, ALCmmdevProxy, HRESULT, resetProxy)                          \
+DECLARE_THUNK(T, ALCmmdevProxy, HRESULT, startProxy)                          \
+DECLARE_THUNK(T, ALCmmdevProxy, void, stopProxy)                              \
+                                                                              \
+static const struct ALCmmdevProxyVtable T##_ALCmmdevProxy_vtable = {          \
+    T##_ALCmmdevProxy_openProxy,                                              \
+    T##_ALCmmdevProxy_closeProxy,                                             \
+    T##_ALCmmdevProxy_resetProxy,                                             \
+    T##_ALCmmdevProxy_startProxy,                                             \
+    T##_ALCmmdevProxy_stopProxy,                                              \
+}
+
+static void ALCmmdevProxy_Construct(ALCmmdevProxy* UNUSED(self)) { }
+static void ALCmmdevProxy_Destruct(ALCmmdevProxy* UNUSED(self)) { }
+
+static DWORD CALLBACK ALCmmdevProxy_messageHandler(void *ptr)
+{
+    ThreadRequest *req = ptr;
+    IMMDeviceEnumerator *Enumerator;
+    ALuint deviceCount = 0;
+    ALCmmdevProxy *proxy;
+    HRESULT hr, cohr;
+    MSG msg;
+
+    TRACE("Starting message thread\n");
+
+    cohr = CoInitialize(NULL);
+    if(FAILED(cohr))
+    {
+        WARN("Failed to initialize COM: 0x%08lx\n", cohr);
+        ReturnMsgResponse(req, cohr);
+        return 0;
+    }
+
+    hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
+    if(FAILED(hr))
+    {
+        WARN("Failed to create IMMDeviceEnumerator instance: 0x%08lx\n", hr);
+        CoUninitialize();
+        ReturnMsgResponse(req, hr);
+        return 0;
+    }
+    Enumerator = ptr;
+    IMMDeviceEnumerator_Release(Enumerator);
+    Enumerator = NULL;
+
+    CoUninitialize();
+
+    /* HACK: Force Windows to create a message queue for this thread before
+     * returning success, otherwise PostThreadMessage may fail if it gets
+     * called before GetMessage.
+     */
+    PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
+
+    TRACE("Message thread initialization complete\n");
+    ReturnMsgResponse(req, S_OK);
+
+    TRACE("Starting message loop\n");
+    while(GetMessage(&msg, NULL, WM_USER_First, WM_USER_Last))
+    {
+        TRACE("Got message %u (lparam=%p, wparam=%p)\n", msg.message, (void*)msg.lParam, (void*)msg.wParam);
+        switch(msg.message)
+        {
+        case WM_USER_OpenDevice:
+            req = (ThreadRequest*)msg.wParam;
+            proxy = (ALCmmdevProxy*)msg.lParam;
+
+            hr = cohr = S_OK;
+            if(++deviceCount == 1)
+                hr = cohr = CoInitialize(NULL);
+            if(SUCCEEDED(hr))
+                hr = V0(proxy,openProxy)();
+            if(FAILED(hr))
+            {
+                if(--deviceCount == 0 && SUCCEEDED(cohr))
+                    CoUninitialize();
+            }
+
+            ReturnMsgResponse(req, hr);
+            continue;
+
+        case WM_USER_ResetDevice:
+            req = (ThreadRequest*)msg.wParam;
+            proxy = (ALCmmdevProxy*)msg.lParam;
+
+            hr = V0(proxy,resetProxy)();
+            ReturnMsgResponse(req, hr);
+            continue;
+
+        case WM_USER_StartDevice:
+            req = (ThreadRequest*)msg.wParam;
+            proxy = (ALCmmdevProxy*)msg.lParam;
+
+            hr = V0(proxy,startProxy)();
+            ReturnMsgResponse(req, hr);
+            continue;
+
+        case WM_USER_StopDevice:
+            req = (ThreadRequest*)msg.wParam;
+            proxy = (ALCmmdevProxy*)msg.lParam;
+
+            V0(proxy,stopProxy)();
+            ReturnMsgResponse(req, S_OK);
+            continue;
+
+        case WM_USER_CloseDevice:
+            req = (ThreadRequest*)msg.wParam;
+            proxy = (ALCmmdevProxy*)msg.lParam;
+
+            V0(proxy,closeProxy)();
+            if(--deviceCount == 0)
+                CoUninitialize();
+
+            ReturnMsgResponse(req, S_OK);
+            continue;
+
+        case WM_USER_Enumerate:
+            req = (ThreadRequest*)msg.wParam;
+
+            hr = cohr = S_OK;
+            if(++deviceCount == 1)
+                hr = cohr = CoInitialize(NULL);
+            if(SUCCEEDED(hr))
+                hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
+            if(SUCCEEDED(hr))
+            {
+                Enumerator = ptr;
+
+                if(msg.lParam == ALL_DEVICE_PROBE)
+                    hr = probe_devices(Enumerator, eRender, &PlaybackDevices);
+                else if(msg.lParam == CAPTURE_DEVICE_PROBE)
+                    hr = probe_devices(Enumerator, eCapture, &CaptureDevices);
+
+                IMMDeviceEnumerator_Release(Enumerator);
+                Enumerator = NULL;
+            }
+
+            if(--deviceCount == 0 && SUCCEEDED(cohr))
+                CoUninitialize();
+
+            ReturnMsgResponse(req, hr);
+            continue;
+
+        default:
+            ERR("Unexpected message: %u\n", msg.message);
+            continue;
+        }
+    }
+    TRACE("Message loop finished\n");
+
+    return 0;
+}
+
+
+typedef struct ALCmmdevPlayback {
+    DERIVE_FROM_TYPE(ALCbackend);
+    DERIVE_FROM_TYPE(ALCmmdevProxy);
+
+    WCHAR *devid;
+
+    IMMDevice *mmdev;
+    IAudioClient *client;
+    IAudioRenderClient *render;
+    HANDLE NotifyEvent;
+
+    HANDLE MsgEvent;
+
+    volatile UINT32 Padding;
+
+    volatile int killNow;
+    althrd_t thread;
+} ALCmmdevPlayback;
+
+static int ALCmmdevPlayback_mixerProc(void *arg);
+
+static void ALCmmdevPlayback_Construct(ALCmmdevPlayback *self, ALCdevice *device);
+static void ALCmmdevPlayback_Destruct(ALCmmdevPlayback *self);
+static ALCenum ALCmmdevPlayback_open(ALCmmdevPlayback *self, const ALCchar *name);
+static HRESULT ALCmmdevPlayback_openProxy(ALCmmdevPlayback *self);
+static void ALCmmdevPlayback_close(ALCmmdevPlayback *self);
+static void ALCmmdevPlayback_closeProxy(ALCmmdevPlayback *self);
+static ALCboolean ALCmmdevPlayback_reset(ALCmmdevPlayback *self);
+static HRESULT ALCmmdevPlayback_resetProxy(ALCmmdevPlayback *self);
+static ALCboolean ALCmmdevPlayback_start(ALCmmdevPlayback *self);
+static HRESULT ALCmmdevPlayback_startProxy(ALCmmdevPlayback *self);
+static void ALCmmdevPlayback_stop(ALCmmdevPlayback *self);
+static void ALCmmdevPlayback_stopProxy(ALCmmdevPlayback *self);
+static DECLARE_FORWARD2(ALCmmdevPlayback, ALCbackend, ALCenum, captureSamples, ALCvoid*, ALCuint)
+static DECLARE_FORWARD(ALCmmdevPlayback, ALCbackend, ALCuint, availableSamples)
+static ClockLatency ALCmmdevPlayback_getClockLatency(ALCmmdevPlayback *self);
+static DECLARE_FORWARD(ALCmmdevPlayback, ALCbackend, void, lock)
+static DECLARE_FORWARD(ALCmmdevPlayback, ALCbackend, void, unlock)
+DECLARE_DEFAULT_ALLOCATORS(ALCmmdevPlayback)
+
+DEFINE_ALCMMDEVPROXY_VTABLE(ALCmmdevPlayback);
+DEFINE_ALCBACKEND_VTABLE(ALCmmdevPlayback);
+
+
+static void ALCmmdevPlayback_Construct(ALCmmdevPlayback *self, ALCdevice *device)
+{
+    SET_VTABLE2(ALCmmdevPlayback, ALCbackend, self);
+    SET_VTABLE2(ALCmmdevPlayback, ALCmmdevProxy, self);
+    ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
+    ALCmmdevProxy_Construct(STATIC_CAST(ALCmmdevProxy, self));
+
+    self->devid = NULL;
+
+    self->mmdev = NULL;
+    self->client = NULL;
+    self->render = NULL;
+    self->NotifyEvent = NULL;
+
+    self->MsgEvent = NULL;
+
+    self->Padding = 0;
+
+    self->killNow = 0;
+}
+
+static void ALCmmdevPlayback_Destruct(ALCmmdevPlayback *self)
+{
+    if(self->NotifyEvent != NULL)
+        CloseHandle(self->NotifyEvent);
+    self->NotifyEvent = NULL;
+    if(self->MsgEvent != NULL)
+        CloseHandle(self->MsgEvent);
+    self->MsgEvent = NULL;
+
+    free(self->devid);
+    self->devid = NULL;
+
+    ALCmmdevProxy_Destruct(STATIC_CAST(ALCmmdevProxy, self));
+    ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
+}
+
+
+FORCE_ALIGN static int ALCmmdevPlayback_mixerProc(void *arg)
+{
+    ALCmmdevPlayback *self = arg;
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+    UINT32 buffer_len, written;
+    ALuint update_size, len;
+    BYTE *buffer;
+    HRESULT hr;
+
+    hr = CoInitialize(NULL);
+    if(FAILED(hr))
+    {
+        ERR("CoInitialize(NULL) failed: 0x%08lx\n", hr);
+        V0(device->Backend,lock)();
+        aluHandleDisconnect(device);
+        V0(device->Backend,unlock)();
+        return 1;
+    }
+
+    SetRTPriority();
+    althrd_setname(althrd_current(), MIXER_THREAD_NAME);
+
+    update_size = device->UpdateSize;
+    buffer_len = update_size * device->NumUpdates;
+    while(!self->killNow)
+    {
+        hr = IAudioClient_GetCurrentPadding(self->client, &written);
+        if(FAILED(hr))
+        {
+            ERR("Failed to get padding: 0x%08lx\n", hr);
+            V0(device->Backend,lock)();
+            aluHandleDisconnect(device);
+            V0(device->Backend,unlock)();
+            break;
+        }
+        self->Padding = written;
+
+        len = buffer_len - written;
+        if(len < update_size)
+        {
+            DWORD res;
+            res = WaitForSingleObjectEx(self->NotifyEvent, 2000, FALSE);
+            if(res != WAIT_OBJECT_0)
+                ERR("WaitForSingleObjectEx error: 0x%lx\n", res);
+            continue;
+        }
+        len -= len%update_size;
+
+        hr = IAudioRenderClient_GetBuffer(self->render, len, &buffer);
+        if(SUCCEEDED(hr))
+        {
+            V0(device->Backend,lock)();
+            aluMixData(device, buffer, len);
+            self->Padding = written + len;
+            V0(device->Backend,unlock)();
+            hr = IAudioRenderClient_ReleaseBuffer(self->render, len, 0);
+        }
+        if(FAILED(hr))
+        {
+            ERR("Failed to buffer data: 0x%08lx\n", hr);
+            V0(device->Backend,lock)();
+            aluHandleDisconnect(device);
+            V0(device->Backend,unlock)();
+            break;
+        }
+    }
+    self->Padding = 0;
+
+    CoUninitialize();
+    return 0;
+}
+
+
+static ALCboolean MakeExtensible(WAVEFORMATEXTENSIBLE *out, const WAVEFORMATEX *in)
+{
+    memset(out, 0, sizeof(*out));
+    if(in->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
+        *out = *(const WAVEFORMATEXTENSIBLE*)in;
+    else if(in->wFormatTag == WAVE_FORMAT_PCM)
+    {
+        out->Format = *in;
+        out->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
+        out->Format.cbSize = sizeof(*out) - sizeof(*in);
+        if(out->Format.nChannels == 1)
+            out->dwChannelMask = MONO;
+        else if(out->Format.nChannels == 2)
+            out->dwChannelMask = STEREO;
+        else
+            ERR("Unhandled PCM channel count: %d\n", out->Format.nChannels);
+        out->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
+    }
+    else if(in->wFormatTag == WAVE_FORMAT_IEEE_FLOAT)
+    {
+        out->Format = *in;
+        out->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
+        out->Format.cbSize = sizeof(*out) - sizeof(*in);
+        if(out->Format.nChannels == 1)
+            out->dwChannelMask = MONO;
+        else if(out->Format.nChannels == 2)
+            out->dwChannelMask = STEREO;
+        else
+            ERR("Unhandled IEEE float channel count: %d\n", out->Format.nChannels);
+        out->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
+    }
+    else
+    {
+        ERR("Unhandled format tag: 0x%04x\n", in->wFormatTag);
+        return ALC_FALSE;
+    }
+    return ALC_TRUE;
+}
+
+static ALCenum ALCmmdevPlayback_open(ALCmmdevPlayback *self, const ALCchar *deviceName)
+{
+    HRESULT hr = S_OK;
+
+    self->NotifyEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
+    self->MsgEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
+    if(self->NotifyEvent == NULL || self->MsgEvent == NULL)
+    {
+        ERR("Failed to create message events: %lu\n", GetLastError());
+        hr = E_FAIL;
+    }
+
+    if(SUCCEEDED(hr))
+    {
+        if(deviceName)
+        {
+            const DevMap *iter;
+
+            if(VECTOR_SIZE(PlaybackDevices) == 0)
+            {
+                ThreadRequest req = { self->MsgEvent, 0 };
+                if(PostThreadMessage(ThreadID, WM_USER_Enumerate, (WPARAM)&req, ALL_DEVICE_PROBE))
+                    (void)WaitForResponse(&req);
+            }
+
+            hr = E_FAIL;
+#define MATCH_NAME(i) (al_string_cmp_cstr((i)->name, deviceName) == 0 ||        \
+                       al_string_cmp_cstr((i)->endpoint_guid, deviceName) == 0)
+            VECTOR_FIND_IF(iter, const DevMap, PlaybackDevices, MATCH_NAME);
+#undef MATCH_NAME
+            if(iter == VECTOR_END(PlaybackDevices))
+            {
+                int len;
+                if((len=MultiByteToWideChar(CP_UTF8, 0, deviceName, -1, NULL, 0)) > 0)
+                {
+                    WCHAR *wname = calloc(sizeof(WCHAR), len);
+                    MultiByteToWideChar(CP_UTF8, 0, deviceName, -1, wname, len);
+#define MATCH_NAME(i) (wcscmp((i)->devid, wname) == 0)
+                    VECTOR_FIND_IF(iter, const DevMap, PlaybackDevices, MATCH_NAME);
+#undef MATCH_NAME
+                    free(wname);
+                }
+            }
+            if(iter == VECTOR_END(PlaybackDevices))
+                WARN("Failed to find device name matching \"%s\"\n", deviceName);
+            else
+            {
+                ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
+                self->devid = strdupW(iter->devid);
+                al_string_copy(&device->DeviceName, iter->name);
+                hr = S_OK;
+            }
+        }
+    }
+
+    if(SUCCEEDED(hr))
+    {
+        ThreadRequest req = { self->MsgEvent, 0 };
+
+        hr = E_FAIL;
+        if(PostThreadMessage(ThreadID, WM_USER_OpenDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
+            hr = WaitForResponse(&req);
+        else
+            ERR("Failed to post thread message: %lu\n", GetLastError());
+    }
+
+    if(FAILED(hr))
+    {
+        if(self->NotifyEvent != NULL)
+            CloseHandle(self->NotifyEvent);
+        self->NotifyEvent = NULL;
+        if(self->MsgEvent != NULL)
+            CloseHandle(self->MsgEvent);
+        self->MsgEvent = NULL;
+
+        free(self->devid);
+        self->devid = NULL;
+
+        ERR("Device init failed: 0x%08lx\n", hr);
+        return ALC_INVALID_VALUE;
+    }
+
+    return ALC_NO_ERROR;
+}
+
+static HRESULT ALCmmdevPlayback_openProxy(ALCmmdevPlayback *self)
+{
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+    void *ptr;
+    HRESULT hr;
+
+    hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
+    if(SUCCEEDED(hr))
+    {
+        IMMDeviceEnumerator *Enumerator = ptr;
+        if(!self->devid)
+            hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(Enumerator, eRender, eMultimedia, &self->mmdev);
+        else
+            hr = IMMDeviceEnumerator_GetDevice(Enumerator, self->devid, &self->mmdev);
+        IMMDeviceEnumerator_Release(Enumerator);
+        Enumerator = NULL;
+    }
+    if(SUCCEEDED(hr))
+        hr = IMMDevice_Activate(self->mmdev, &IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, &ptr);
+    if(SUCCEEDED(hr))
+    {
+        self->client = ptr;
+        if(al_string_empty(device->DeviceName))
+            get_device_name_and_guid(self->mmdev, &device->DeviceName, NULL);
+    }
+
+    if(FAILED(hr))
+    {
+        if(self->mmdev)
+            IMMDevice_Release(self->mmdev);
+        self->mmdev = NULL;
+    }
+
+    return hr;
+}
+
+
+static void ALCmmdevPlayback_close(ALCmmdevPlayback *self)
+{
+    ThreadRequest req = { self->MsgEvent, 0 };
+
+    if(PostThreadMessage(ThreadID, WM_USER_CloseDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
+        (void)WaitForResponse(&req);
+
+    CloseHandle(self->MsgEvent);
+    self->MsgEvent = NULL;
+
+    CloseHandle(self->NotifyEvent);
+    self->NotifyEvent = NULL;
+
+    free(self->devid);
+    self->devid = NULL;
+}
+
+static void ALCmmdevPlayback_closeProxy(ALCmmdevPlayback *self)
+{
+    if(self->client)
+        IAudioClient_Release(self->client);
+    self->client = NULL;
+
+    if(self->mmdev)
+        IMMDevice_Release(self->mmdev);
+    self->mmdev = NULL;
+}
+
+
+static ALCboolean ALCmmdevPlayback_reset(ALCmmdevPlayback *self)
+{
+    ThreadRequest req = { self->MsgEvent, 0 };
+    HRESULT hr = E_FAIL;
+
+    if(PostThreadMessage(ThreadID, WM_USER_ResetDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
+        hr = WaitForResponse(&req);
+
+    return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE;
+}
+
+static HRESULT ALCmmdevPlayback_resetProxy(ALCmmdevPlayback *self)
+{
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+    EndpointFormFactor formfactor = UnknownFormFactor;
+    WAVEFORMATEXTENSIBLE OutputType;
+    WAVEFORMATEX *wfx = NULL;
+    REFERENCE_TIME min_per, buf_time;
+    UINT32 buffer_len, min_len;
+    void *ptr = NULL;
+    HRESULT hr;
+
+    if(self->client)
+        IAudioClient_Release(self->client);
+    self->client = NULL;
+
+    hr = IMMDevice_Activate(self->mmdev, &IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, &ptr);
+    if(FAILED(hr))
+    {
+        ERR("Failed to reactivate audio client: 0x%08lx\n", hr);
+        return hr;
+    }
+    self->client = ptr;
+
+    hr = IAudioClient_GetMixFormat(self->client, &wfx);
+    if(FAILED(hr))
+    {
+        ERR("Failed to get mix format: 0x%08lx\n", hr);
+        return hr;
+    }
+
+    if(!MakeExtensible(&OutputType, wfx))
+    {
+        CoTaskMemFree(wfx);
+        return E_FAIL;
+    }
+    CoTaskMemFree(wfx);
+    wfx = NULL;
+
+    buf_time = ((REFERENCE_TIME)device->UpdateSize*device->NumUpdates*10000000 +
+                                device->Frequency-1) / device->Frequency;
+
+    if(!(device->Flags&DEVICE_FREQUENCY_REQUEST))
+        device->Frequency = OutputType.Format.nSamplesPerSec;
+    if(!(device->Flags&DEVICE_CHANNELS_REQUEST))
+    {
+        if(OutputType.Format.nChannels == 1 && OutputType.dwChannelMask == MONO)
+            device->FmtChans = DevFmtMono;
+        else if(OutputType.Format.nChannels == 2 && OutputType.dwChannelMask == STEREO)
+            device->FmtChans = DevFmtStereo;
+        else if(OutputType.Format.nChannels == 4 && OutputType.dwChannelMask == QUAD)
+            device->FmtChans = DevFmtQuad;
+        else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1)
+            device->FmtChans = DevFmtX51;
+        else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1REAR)
+            device->FmtChans = DevFmtX51Rear;
+        else if(OutputType.Format.nChannels == 7 && OutputType.dwChannelMask == X6DOT1)
+            device->FmtChans = DevFmtX61;
+        else if(OutputType.Format.nChannels == 8 && (OutputType.dwChannelMask == X7DOT1 || OutputType.dwChannelMask == X7DOT1_WIDE))
+            device->FmtChans = DevFmtX71;
+        else
+            ERR("Unhandled channel config: %d -- 0x%08lx\n", OutputType.Format.nChannels, OutputType.dwChannelMask);
+    }
+
+    switch(device->FmtChans)
+    {
+        case DevFmtMono:
+            OutputType.Format.nChannels = 1;
+            OutputType.dwChannelMask = MONO;
+            break;
+        case DevFmtAmbi1:
+        case DevFmtAmbi2:
+        case DevFmtAmbi3:
+            device->FmtChans = DevFmtStereo;
+            /*fall-through*/
+        case DevFmtStereo:
+            OutputType.Format.nChannels = 2;
+            OutputType.dwChannelMask = STEREO;
+            break;
+        case DevFmtQuad:
+            OutputType.Format.nChannels = 4;
+            OutputType.dwChannelMask = QUAD;
+            break;
+        case DevFmtX51:
+            OutputType.Format.nChannels = 6;
+            OutputType.dwChannelMask = X5DOT1;
+            break;
+        case DevFmtX51Rear:
+            OutputType.Format.nChannels = 6;
+            OutputType.dwChannelMask = X5DOT1REAR;
+            break;
+        case DevFmtX61:
+            OutputType.Format.nChannels = 7;
+            OutputType.dwChannelMask = X6DOT1;
+            break;
+        case DevFmtX71:
+            OutputType.Format.nChannels = 8;
+            OutputType.dwChannelMask = X7DOT1;
+            break;
+    }
+    switch(device->FmtType)
+    {
+        case DevFmtByte:
+            device->FmtType = DevFmtUByte;
+            /* fall-through */
+        case DevFmtUByte:
+            OutputType.Format.wBitsPerSample = 8;
+            OutputType.Samples.wValidBitsPerSample = 8;
+            OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
+            break;
+        case DevFmtUShort:
+            device->FmtType = DevFmtShort;
+            /* fall-through */
+        case DevFmtShort:
+            OutputType.Format.wBitsPerSample = 16;
+            OutputType.Samples.wValidBitsPerSample = 16;
+            OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
+            break;
+        case DevFmtUInt:
+            device->FmtType = DevFmtInt;
+            /* fall-through */
+        case DevFmtInt:
+            OutputType.Format.wBitsPerSample = 32;
+            OutputType.Samples.wValidBitsPerSample = 32;
+            OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
+            break;
+        case DevFmtFloat:
+            OutputType.Format.wBitsPerSample = 32;
+            OutputType.Samples.wValidBitsPerSample = 32;
+            OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
+            break;
+    }
+    OutputType.Format.nSamplesPerSec = device->Frequency;
+
+    OutputType.Format.nBlockAlign = OutputType.Format.nChannels *
+                                    OutputType.Format.wBitsPerSample / 8;
+    OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec *
+                                        OutputType.Format.nBlockAlign;
+
+    hr = IAudioClient_IsFormatSupported(self->client, AUDCLNT_SHAREMODE_SHARED, &OutputType.Format, &wfx);
+    if(FAILED(hr))
+    {
+        ERR("Failed to check format support: 0x%08lx\n", hr);
+        hr = IAudioClient_GetMixFormat(self->client, &wfx);
+    }
+    if(FAILED(hr))
+    {
+        ERR("Failed to find a supported format: 0x%08lx\n", hr);
+        return hr;
+    }
+
+    if(wfx != NULL)
+    {
+        if(!MakeExtensible(&OutputType, wfx))
+        {
+            CoTaskMemFree(wfx);
+            return E_FAIL;
+        }
+        CoTaskMemFree(wfx);
+        wfx = NULL;
+
+        device->Frequency = OutputType.Format.nSamplesPerSec;
+        if(OutputType.Format.nChannels == 1 && OutputType.dwChannelMask == MONO)
+            device->FmtChans = DevFmtMono;
+        else if(OutputType.Format.nChannels == 2 && OutputType.dwChannelMask == STEREO)
+            device->FmtChans = DevFmtStereo;
+        else if(OutputType.Format.nChannels == 4 && OutputType.dwChannelMask == QUAD)
+            device->FmtChans = DevFmtQuad;
+        else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1)
+            device->FmtChans = DevFmtX51;
+        else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1REAR)
+            device->FmtChans = DevFmtX51Rear;
+        else if(OutputType.Format.nChannels == 7 && OutputType.dwChannelMask == X6DOT1)
+            device->FmtChans = DevFmtX61;
+        else if(OutputType.Format.nChannels == 8 && (OutputType.dwChannelMask == X7DOT1 || OutputType.dwChannelMask == X7DOT1_WIDE))
+            device->FmtChans = DevFmtX71;
+        else
+        {
+            ERR("Unhandled extensible channels: %d -- 0x%08lx\n", OutputType.Format.nChannels, OutputType.dwChannelMask);
+            device->FmtChans = DevFmtStereo;
+            OutputType.Format.nChannels = 2;
+            OutputType.dwChannelMask = STEREO;
+        }
+
+        if(IsEqualGUID(&OutputType.SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))
+        {
+            if(OutputType.Format.wBitsPerSample == 8)
+                device->FmtType = DevFmtUByte;
+            else if(OutputType.Format.wBitsPerSample == 16)
+                device->FmtType = DevFmtShort;
+            else if(OutputType.Format.wBitsPerSample == 32)
+                device->FmtType = DevFmtInt;
+            else
+            {
+                device->FmtType = DevFmtShort;
+                OutputType.Format.wBitsPerSample = 16;
+            }
+        }
+        else if(IsEqualGUID(&OutputType.SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))
+        {
+            device->FmtType = DevFmtFloat;
+            OutputType.Format.wBitsPerSample = 32;
+        }
+        else
+        {
+            ERR("Unhandled format sub-type\n");
+            device->FmtType = DevFmtShort;
+            OutputType.Format.wBitsPerSample = 16;
+            OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
+        }
+        OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
+    }
+    get_device_formfactor(self->mmdev, &formfactor);
+    device->IsHeadphones = (device->FmtChans == DevFmtStereo &&
+                            (formfactor == Headphones || formfactor == Headset)
+                           );
+
+    SetDefaultWFXChannelOrder(device);
+
+    hr = IAudioClient_Initialize(self->client, AUDCLNT_SHAREMODE_SHARED,
+                                 AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
+                                 buf_time, 0, &OutputType.Format, NULL);
+    if(FAILED(hr))
+    {
+        ERR("Failed to initialize audio client: 0x%08lx\n", hr);
+        return hr;
+    }
+
+    hr = IAudioClient_GetDevicePeriod(self->client, &min_per, NULL);
+    if(SUCCEEDED(hr))
+    {
+        min_len = (UINT32)((min_per*device->Frequency + 10000000-1) / 10000000);
+        /* Find the nearest multiple of the period size to the update size */
+        if(min_len < device->UpdateSize)
+            min_len *= (device->UpdateSize + min_len/2)/min_len;
+        hr = IAudioClient_GetBufferSize(self->client, &buffer_len);
+    }
+    if(FAILED(hr))
+    {
+        ERR("Failed to get audio buffer info: 0x%08lx\n", hr);
+        return hr;
+    }
+
+    device->UpdateSize = min_len;
+    device->NumUpdates = buffer_len / device->UpdateSize;
+    if(device->NumUpdates <= 1)
+    {
+        ERR("Audio client returned buffer_len < period*2; expect break up\n");
+        device->NumUpdates = 2;
+        device->UpdateSize = buffer_len / device->NumUpdates;
+    }
+
+    hr = IAudioClient_SetEventHandle(self->client, self->NotifyEvent);
+    if(FAILED(hr))
+    {
+        ERR("Failed to set event handle: 0x%08lx\n", hr);
+        return hr;
+    }
+
+    return hr;
+}
+
+
+static ALCboolean ALCmmdevPlayback_start(ALCmmdevPlayback *self)
+{
+    ThreadRequest req = { self->MsgEvent, 0 };
+    HRESULT hr = E_FAIL;
+
+    if(PostThreadMessage(ThreadID, WM_USER_StartDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
+        hr = WaitForResponse(&req);
+
+    return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE;
+}
+
+static HRESULT ALCmmdevPlayback_startProxy(ALCmmdevPlayback *self)
+{
+    HRESULT hr;
+    void *ptr;
+
+    ResetEvent(self->NotifyEvent);
+    hr = IAudioClient_Start(self->client);
+    if(FAILED(hr))
+        ERR("Failed to start audio client: 0x%08lx\n", hr);
+
+    if(SUCCEEDED(hr))
+        hr = IAudioClient_GetService(self->client, &IID_IAudioRenderClient, &ptr);
+    if(SUCCEEDED(hr))
+    {
+        self->render = ptr;
+        self->killNow = 0;
+        if(althrd_create(&self->thread, ALCmmdevPlayback_mixerProc, self) != althrd_success)
+        {
+            if(self->render)
+                IAudioRenderClient_Release(self->render);
+            self->render = NULL;
+            IAudioClient_Stop(self->client);
+            ERR("Failed to start thread\n");
+            hr = E_FAIL;
+        }
+    }
+
+    return hr;
+}
+
+
+static void ALCmmdevPlayback_stop(ALCmmdevPlayback *self)
+{
+    ThreadRequest req = { self->MsgEvent, 0 };
+    if(PostThreadMessage(ThreadID, WM_USER_StopDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
+        (void)WaitForResponse(&req);
+}
+
+static void ALCmmdevPlayback_stopProxy(ALCmmdevPlayback *self)
+{
+    int res;
+
+    if(!self->render)
+        return;
+
+    self->killNow = 1;
+    althrd_join(self->thread, &res);
+
+    IAudioRenderClient_Release(self->render);
+    self->render = NULL;
+    IAudioClient_Stop(self->client);
+}
+
+
+static ClockLatency ALCmmdevPlayback_getClockLatency(ALCmmdevPlayback *self)
+{
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+    ClockLatency ret;
+
+    ALCmmdevPlayback_lock(self);
+    ret.ClockTime = GetDeviceClockTime(device);
+    ret.Latency = self->Padding * DEVICE_CLOCK_RES / device->Frequency;
+    ALCmmdevPlayback_unlock(self);
+
+    return ret;
+}
+
+
+typedef struct ALCmmdevCapture {
+    DERIVE_FROM_TYPE(ALCbackend);
+    DERIVE_FROM_TYPE(ALCmmdevProxy);
+
+    WCHAR *devid;
+
+    IMMDevice *mmdev;
+    IAudioClient *client;
+    IAudioCaptureClient *capture;
+    HANDLE NotifyEvent;
+
+    HANDLE MsgEvent;
+
+    ll_ringbuffer_t *Ring;
+
+    volatile int killNow;
+    althrd_t thread;
+} ALCmmdevCapture;
+
+static int ALCmmdevCapture_recordProc(void *arg);
+
+static void ALCmmdevCapture_Construct(ALCmmdevCapture *self, ALCdevice *device);
+static void ALCmmdevCapture_Destruct(ALCmmdevCapture *self);
+static ALCenum ALCmmdevCapture_open(ALCmmdevCapture *self, const ALCchar *name);
+static HRESULT ALCmmdevCapture_openProxy(ALCmmdevCapture *self);
+static void ALCmmdevCapture_close(ALCmmdevCapture *self);
+static void ALCmmdevCapture_closeProxy(ALCmmdevCapture *self);
+static DECLARE_FORWARD(ALCmmdevCapture, ALCbackend, ALCboolean, reset)
+static HRESULT ALCmmdevCapture_resetProxy(ALCmmdevCapture *self);
+static ALCboolean ALCmmdevCapture_start(ALCmmdevCapture *self);
+static HRESULT ALCmmdevCapture_startProxy(ALCmmdevCapture *self);
+static void ALCmmdevCapture_stop(ALCmmdevCapture *self);
+static void ALCmmdevCapture_stopProxy(ALCmmdevCapture *self);
+static ALCenum ALCmmdevCapture_captureSamples(ALCmmdevCapture *self, ALCvoid *buffer, ALCuint samples);
+static ALuint ALCmmdevCapture_availableSamples(ALCmmdevCapture *self);
+static DECLARE_FORWARD(ALCmmdevCapture, ALCbackend, ClockLatency, getClockLatency)
+static DECLARE_FORWARD(ALCmmdevCapture, ALCbackend, void, lock)
+static DECLARE_FORWARD(ALCmmdevCapture, ALCbackend, void, unlock)
+DECLARE_DEFAULT_ALLOCATORS(ALCmmdevCapture)
+
+DEFINE_ALCMMDEVPROXY_VTABLE(ALCmmdevCapture);
+DEFINE_ALCBACKEND_VTABLE(ALCmmdevCapture);
+
+
+static void ALCmmdevCapture_Construct(ALCmmdevCapture *self, ALCdevice *device)
+{
+    SET_VTABLE2(ALCmmdevCapture, ALCbackend, self);
+    SET_VTABLE2(ALCmmdevCapture, ALCmmdevProxy, self);
+    ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
+    ALCmmdevProxy_Construct(STATIC_CAST(ALCmmdevProxy, self));
+
+    self->devid = NULL;
+
+    self->mmdev = NULL;
+    self->client = NULL;
+    self->capture = NULL;
+    self->NotifyEvent = NULL;
+
+    self->MsgEvent = NULL;
+
+    self->Ring = NULL;
+
+    self->killNow = 0;
+}
+
+static void ALCmmdevCapture_Destruct(ALCmmdevCapture *self)
+{
+    ll_ringbuffer_free(self->Ring);
+    self->Ring = NULL;
+
+    if(self->NotifyEvent != NULL)
+        CloseHandle(self->NotifyEvent);
+    self->NotifyEvent = NULL;
+    if(self->MsgEvent != NULL)
+        CloseHandle(self->MsgEvent);
+    self->MsgEvent = NULL;
+
+    free(self->devid);
+    self->devid = NULL;
+
+    ALCmmdevProxy_Destruct(STATIC_CAST(ALCmmdevProxy, self));
+    ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
+}
+
+
+FORCE_ALIGN int ALCmmdevCapture_recordProc(void *arg)
+{
+    ALCmmdevCapture *self = arg;
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+    HRESULT hr;
+
+    hr = CoInitialize(NULL);
+    if(FAILED(hr))
+    {
+        ERR("CoInitialize(NULL) failed: 0x%08lx\n", hr);
+        V0(device->Backend,lock)();
+        aluHandleDisconnect(device);
+        V0(device->Backend,unlock)();
+        return 1;
+    }
+
+    althrd_setname(althrd_current(), RECORD_THREAD_NAME);
+
+    while(!self->killNow)
+    {
+        UINT32 avail;
+        DWORD res;
+
+        hr = IAudioCaptureClient_GetNextPacketSize(self->capture, &avail);
+        if(FAILED(hr))
+            ERR("Failed to get next packet size: 0x%08lx\n", hr);
+        else while(avail > 0 && SUCCEEDED(hr))
+        {
+            UINT32 numsamples;
+            DWORD flags;
+            BYTE *data;
+
+            hr = IAudioCaptureClient_GetBuffer(self->capture,
+                &data, &numsamples, &flags, NULL, NULL
+            );
+            if(FAILED(hr))
+            {
+                ERR("Failed to get capture buffer: 0x%08lx\n", hr);
+                break;
+            }
+
+            ll_ringbuffer_write(self->Ring, (char*)data, numsamples);
+
+            hr = IAudioCaptureClient_ReleaseBuffer(self->capture, numsamples);
+            if(FAILED(hr))
+            {
+                ERR("Failed to release capture buffer: 0x%08lx\n", hr);
+                break;
+            }
+
+            hr = IAudioCaptureClient_GetNextPacketSize(self->capture, &avail);
+            if(FAILED(hr))
+                ERR("Failed to get next packet size: 0x%08lx\n", hr);
+        }
+
+        if(FAILED(hr))
+        {
+            V0(device->Backend,lock)();
+            aluHandleDisconnect(device);
+            V0(device->Backend,unlock)();
+            break;
+        }
+
+        res = WaitForSingleObjectEx(self->NotifyEvent, 2000, FALSE);
+        if(res != WAIT_OBJECT_0)
+            ERR("WaitForSingleObjectEx error: 0x%lx\n", res);
+    }
+
+    CoUninitialize();
+    return 0;
+}
+
+
+static ALCenum ALCmmdevCapture_open(ALCmmdevCapture *self, const ALCchar *deviceName)
+{
+    HRESULT hr = S_OK;
+
+    self->NotifyEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
+    self->MsgEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
+    if(self->NotifyEvent == NULL || self->MsgEvent == NULL)
+    {
+        ERR("Failed to create message events: %lu\n", GetLastError());
+        hr = E_FAIL;
+    }
+
+    if(SUCCEEDED(hr))
+    {
+        if(deviceName)
+        {
+            const DevMap *iter;
+
+            if(VECTOR_SIZE(CaptureDevices) == 0)
+            {
+                ThreadRequest req = { self->MsgEvent, 0 };
+                if(PostThreadMessage(ThreadID, WM_USER_Enumerate, (WPARAM)&req, CAPTURE_DEVICE_PROBE))
+                    (void)WaitForResponse(&req);
+            }
+
+            hr = E_FAIL;
+#define MATCH_NAME(i) (al_string_cmp_cstr((i)->name, deviceName) == 0 ||        \
+                       al_string_cmp_cstr((i)->endpoint_guid, deviceName) == 0)
+            VECTOR_FIND_IF(iter, const DevMap, CaptureDevices, MATCH_NAME);
+#undef MATCH_NAME
+            if(iter == VECTOR_END(CaptureDevices))
+            {
+                int len;
+                if((len=MultiByteToWideChar(CP_UTF8, 0, deviceName, -1, NULL, 0)) > 0)
+                {
+                    WCHAR *wname = calloc(sizeof(WCHAR), len);
+                    MultiByteToWideChar(CP_UTF8, 0, deviceName, -1, wname, len);
+#define MATCH_NAME(i) (wcscmp((i)->devid, wname) == 0)
+                    VECTOR_FIND_IF(iter, const DevMap, CaptureDevices, MATCH_NAME);
+#undef MATCH_NAME
+                    free(wname);
+                }
+            }
+            if(iter == VECTOR_END(CaptureDevices))
+                WARN("Failed to find device name matching \"%s\"\n", deviceName);
+            else
+            {
+                ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
+                self->devid = strdupW(iter->devid);
+                al_string_copy(&device->DeviceName, iter->name);
+                hr = S_OK;
+            }
+        }
+    }
+
+    if(SUCCEEDED(hr))
+    {
+        ThreadRequest req = { self->MsgEvent, 0 };
+
+        hr = E_FAIL;
+        if(PostThreadMessage(ThreadID, WM_USER_OpenDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
+            hr = WaitForResponse(&req);
+        else
+            ERR("Failed to post thread message: %lu\n", GetLastError());
+    }
+
+    if(FAILED(hr))
+    {
+        if(self->NotifyEvent != NULL)
+            CloseHandle(self->NotifyEvent);
+        self->NotifyEvent = NULL;
+        if(self->MsgEvent != NULL)
+            CloseHandle(self->MsgEvent);
+        self->MsgEvent = NULL;
+
+        free(self->devid);
+        self->devid = NULL;
+
+        ERR("Device init failed: 0x%08lx\n", hr);
+        return ALC_INVALID_VALUE;
+    }
+    else
+    {
+        ThreadRequest req = { self->MsgEvent, 0 };
+
+        hr = E_FAIL;
+        if(PostThreadMessage(ThreadID, WM_USER_ResetDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
+            hr = WaitForResponse(&req);
+        else
+            ERR("Failed to post thread message: %lu\n", GetLastError());
+
+        if(FAILED(hr))
+        {
+            ALCmmdevCapture_close(self);
+            if(hr == E_OUTOFMEMORY)
+               return ALC_OUT_OF_MEMORY;
+            return ALC_INVALID_VALUE;
+        }
+    }
+
+    return ALC_NO_ERROR;
+}
+
+static HRESULT ALCmmdevCapture_openProxy(ALCmmdevCapture *self)
+{
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+    void *ptr;
+    HRESULT hr;
+
+    hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
+    if(SUCCEEDED(hr))
+    {
+        IMMDeviceEnumerator *Enumerator = ptr;
+        if(!self->devid)
+            hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(Enumerator, eCapture, eMultimedia, &self->mmdev);
+        else
+            hr = IMMDeviceEnumerator_GetDevice(Enumerator, self->devid, &self->mmdev);
+        IMMDeviceEnumerator_Release(Enumerator);
+        Enumerator = NULL;
+    }
+    if(SUCCEEDED(hr))
+        hr = IMMDevice_Activate(self->mmdev, &IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, &ptr);
+    if(SUCCEEDED(hr))
+    {
+        self->client = ptr;
+        if(al_string_empty(device->DeviceName))
+            get_device_name_and_guid(self->mmdev, &device->DeviceName, NULL);
+    }
+
+    if(FAILED(hr))
+    {
+        if(self->mmdev)
+            IMMDevice_Release(self->mmdev);
+        self->mmdev = NULL;
+    }
+
+    return hr;
+}
+
+
+static void ALCmmdevCapture_close(ALCmmdevCapture *self)
+{
+    ThreadRequest req = { self->MsgEvent, 0 };
+
+    if(PostThreadMessage(ThreadID, WM_USER_CloseDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
+        (void)WaitForResponse(&req);
+
+    ll_ringbuffer_free(self->Ring);
+    self->Ring = NULL;
+
+    CloseHandle(self->MsgEvent);
+    self->MsgEvent = NULL;
+
+    CloseHandle(self->NotifyEvent);
+    self->NotifyEvent = NULL;
+
+    free(self->devid);
+    self->devid = NULL;
+}
+
+static void ALCmmdevCapture_closeProxy(ALCmmdevCapture *self)
+{
+    if(self->client)
+        IAudioClient_Release(self->client);
+    self->client = NULL;
+
+    if(self->mmdev)
+        IMMDevice_Release(self->mmdev);
+    self->mmdev = NULL;
+}
+
+
+static HRESULT ALCmmdevCapture_resetProxy(ALCmmdevCapture *self)
+{
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+    WAVEFORMATEXTENSIBLE OutputType;
+    WAVEFORMATEX *wfx = NULL;
+    REFERENCE_TIME buf_time;
+    UINT32 buffer_len;
+    void *ptr = NULL;
+    HRESULT hr;
+
+    if(self->client)
+        IAudioClient_Release(self->client);
+    self->client = NULL;
+
+    hr = IMMDevice_Activate(self->mmdev, &IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, &ptr);
+    if(FAILED(hr))
+    {
+        ERR("Failed to reactivate audio client: 0x%08lx\n", hr);
+        return hr;
+    }
+    self->client = ptr;
+
+    buf_time = ((REFERENCE_TIME)device->UpdateSize*device->NumUpdates*10000000 +
+                                device->Frequency-1) / device->Frequency;
+
+    OutputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
+    switch(device->FmtChans)
+    {
+        case DevFmtMono:
+            OutputType.Format.nChannels = 1;
+            OutputType.dwChannelMask = MONO;
+            break;
+        case DevFmtStereo:
+            OutputType.Format.nChannels = 2;
+            OutputType.dwChannelMask = STEREO;
+            break;
+        case DevFmtQuad:
+            OutputType.Format.nChannels = 4;
+            OutputType.dwChannelMask = QUAD;
+            break;
+        case DevFmtX51:
+            OutputType.Format.nChannels = 6;
+            OutputType.dwChannelMask = X5DOT1;
+            break;
+        case DevFmtX51Rear:
+            OutputType.Format.nChannels = 6;
+            OutputType.dwChannelMask = X5DOT1REAR;
+            break;
+        case DevFmtX61:
+            OutputType.Format.nChannels = 7;
+            OutputType.dwChannelMask = X6DOT1;
+            break;
+        case DevFmtX71:
+            OutputType.Format.nChannels = 8;
+            OutputType.dwChannelMask = X7DOT1;
+            break;
+
+        case DevFmtAmbi1:
+        case DevFmtAmbi2:
+        case DevFmtAmbi3:
+            return E_FAIL;
+    }
+    switch(device->FmtType)
+    {
+        case DevFmtUByte:
+            OutputType.Format.wBitsPerSample = 8;
+            OutputType.Samples.wValidBitsPerSample = 8;
+            OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
+            break;
+        case DevFmtShort:
+            OutputType.Format.wBitsPerSample = 16;
+            OutputType.Samples.wValidBitsPerSample = 16;
+            OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
+            break;
+        case DevFmtInt:
+            OutputType.Format.wBitsPerSample = 32;
+            OutputType.Samples.wValidBitsPerSample = 32;
+            OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
+            break;
+        case DevFmtFloat:
+            OutputType.Format.wBitsPerSample = 32;
+            OutputType.Samples.wValidBitsPerSample = 32;
+            OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
+            break;
+
+        case DevFmtByte:
+        case DevFmtUShort:
+        case DevFmtUInt:
+            WARN("%s capture samples not supported\n", DevFmtTypeString(device->FmtType));
+            return E_FAIL;
+    }
+    OutputType.Format.nSamplesPerSec = device->Frequency;
+
+    OutputType.Format.nBlockAlign = OutputType.Format.nChannels *
+                                    OutputType.Format.wBitsPerSample / 8;
+    OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec *
+                                        OutputType.Format.nBlockAlign;
+    OutputType.Format.cbSize = sizeof(OutputType) - sizeof(OutputType.Format);
+
+    hr = IAudioClient_IsFormatSupported(self->client,
+        AUDCLNT_SHAREMODE_SHARED, &OutputType.Format, &wfx
+    );
+    if(FAILED(hr))
+    {
+        ERR("Failed to check format support: 0x%08lx\n", hr);
+        return hr;
+    }
+
+    /* FIXME: We should do conversion/resampling if we didn't get a matching format. */
+    if(wfx->nSamplesPerSec != OutputType.Format.nSamplesPerSec ||
+       wfx->wBitsPerSample != OutputType.Format.wBitsPerSample ||
+       wfx->nChannels != OutputType.Format.nChannels ||
+       wfx->nBlockAlign != OutputType.Format.nBlockAlign)
+    {
+        ERR("Failed to get matching format, wanted: %s %s %uhz, got: %d channel%s %d-bit %luhz\n",
+            DevFmtChannelsString(device->FmtChans), DevFmtTypeString(device->FmtType),
+            device->Frequency, wfx->nChannels, (wfx->nChannels==1)?"":"s", wfx->wBitsPerSample,
+            wfx->nSamplesPerSec);
+        CoTaskMemFree(wfx);
+        return E_FAIL;
+    }
+
+    if(!MakeExtensible(&OutputType, wfx))
+    {
+        CoTaskMemFree(wfx);
+        return E_FAIL;
+    }
+    CoTaskMemFree(wfx);
+    wfx = NULL;
+
+    hr = IAudioClient_Initialize(self->client,
+        AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
+        buf_time, 0, &OutputType.Format, NULL
+    );
+    if(FAILED(hr))
+    {
+        ERR("Failed to initialize audio client: 0x%08lx\n", hr);
+        return hr;
+    }
+
+    hr = IAudioClient_GetBufferSize(self->client, &buffer_len);
+    if(FAILED(hr))
+    {
+        ERR("Failed to get buffer size: 0x%08lx\n", hr);
+        return hr;
+    }
+
+    buffer_len = maxu(device->UpdateSize*device->NumUpdates + 1, buffer_len);
+    ll_ringbuffer_free(self->Ring);
+    self->Ring = ll_ringbuffer_create(buffer_len, OutputType.Format.nBlockAlign);
+    if(!self->Ring)
+    {
+        ERR("Failed to allocate capture ring buffer\n");
+        return E_OUTOFMEMORY;
+    }
+
+    hr = IAudioClient_SetEventHandle(self->client, self->NotifyEvent);
+    if(FAILED(hr))
+    {
+        ERR("Failed to set event handle: 0x%08lx\n", hr);
+        return hr;
+    }
+
+    return hr;
+}
+
+
+static ALCboolean ALCmmdevCapture_start(ALCmmdevCapture *self)
+{
+    ThreadRequest req = { self->MsgEvent, 0 };
+    HRESULT hr = E_FAIL;
+
+    if(PostThreadMessage(ThreadID, WM_USER_StartDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
+        hr = WaitForResponse(&req);
+
+    return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE;
+}
+
+static HRESULT ALCmmdevCapture_startProxy(ALCmmdevCapture *self)
+{
+    HRESULT hr;
+    void *ptr;
+
+    ResetEvent(self->NotifyEvent);
+    hr = IAudioClient_Start(self->client);
+    if(FAILED(hr))
+    {
+        ERR("Failed to start audio client: 0x%08lx\n", hr);
+        return hr;
+    }
+
+    hr = IAudioClient_GetService(self->client, &IID_IAudioCaptureClient, &ptr);
+    if(SUCCEEDED(hr))
+    {
+        self->capture = ptr;
+        self->killNow = 0;
+        if(althrd_create(&self->thread, ALCmmdevCapture_recordProc, self) != althrd_success)
+        {
+            ERR("Failed to start thread\n");
+            IAudioCaptureClient_Release(self->capture);
+            self->capture = NULL;
+            hr = E_FAIL;
+        }
+    }
+
+    if(FAILED(hr))
+    {
+        IAudioClient_Stop(self->client);
+        IAudioClient_Reset(self->client);
+    }
+
+    return hr;
+}
+
+
+static void ALCmmdevCapture_stop(ALCmmdevCapture *self)
+{
+    ThreadRequest req = { self->MsgEvent, 0 };
+    if(PostThreadMessage(ThreadID, WM_USER_StopDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
+        (void)WaitForResponse(&req);
+}
+
+static void ALCmmdevCapture_stopProxy(ALCmmdevCapture *self)
+{
+    int res;
+
+    if(!self->capture)
+        return;
+
+    self->killNow = 1;
+    althrd_join(self->thread, &res);
+
+    IAudioCaptureClient_Release(self->capture);
+    self->capture = NULL;
+    IAudioClient_Stop(self->client);
+    IAudioClient_Reset(self->client);
+}
+
+
+ALuint ALCmmdevCapture_availableSamples(ALCmmdevCapture *self)
+{
+    return (ALuint)ll_ringbuffer_read_space(self->Ring);
+}
+
+ALCenum ALCmmdevCapture_captureSamples(ALCmmdevCapture *self, ALCvoid *buffer, ALCuint samples)
+{
+    if(ALCmmdevCapture_availableSamples(self) < samples)
+        return ALC_INVALID_VALUE;
+    ll_ringbuffer_read(self->Ring, buffer, samples);
+    return ALC_NO_ERROR;
+}
+
+
+static inline void AppendAllDevicesList2(const DevMap *entry)
+{ AppendAllDevicesList(al_string_get_cstr(entry->name)); }
+static inline void AppendCaptureDeviceList2(const DevMap *entry)
+{ AppendCaptureDeviceList(al_string_get_cstr(entry->name)); }
+
+typedef struct ALCmmdevBackendFactory {
+    DERIVE_FROM_TYPE(ALCbackendFactory);
+} ALCmmdevBackendFactory;
+#define ALCMMDEVBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCmmdevBackendFactory, ALCbackendFactory) } }
+
+static ALCboolean ALCmmdevBackendFactory_init(ALCmmdevBackendFactory *self);
+static void ALCmmdevBackendFactory_deinit(ALCmmdevBackendFactory *self);
+static ALCboolean ALCmmdevBackendFactory_querySupport(ALCmmdevBackendFactory *self, ALCbackend_Type type);
+static void ALCmmdevBackendFactory_probe(ALCmmdevBackendFactory *self, enum DevProbe type);
+static ALCbackend* ALCmmdevBackendFactory_createBackend(ALCmmdevBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
+
+DEFINE_ALCBACKENDFACTORY_VTABLE(ALCmmdevBackendFactory);
+
+
+static BOOL MMDevApiLoad(void)
+{
+    static HRESULT InitResult;
+    if(!ThreadHdl)
+    {
+        ThreadRequest req;
+        InitResult = E_FAIL;
+
+        req.FinishedEvt = CreateEventW(NULL, FALSE, FALSE, NULL);
+        if(req.FinishedEvt == NULL)
+            ERR("Failed to create event: %lu\n", GetLastError());
+        else
+        {
+            ThreadHdl = CreateThread(NULL, 0, ALCmmdevProxy_messageHandler, &req, 0, &ThreadID);
+            if(ThreadHdl != NULL)
+                InitResult = WaitForResponse(&req);
+            CloseHandle(req.FinishedEvt);
+        }
+    }
+    return SUCCEEDED(InitResult);
+}
+
+static ALCboolean ALCmmdevBackendFactory_init(ALCmmdevBackendFactory* UNUSED(self))
+{
+    VECTOR_INIT(PlaybackDevices);
+    VECTOR_INIT(CaptureDevices);
+
+    if(!MMDevApiLoad())
+        return ALC_FALSE;
+    return ALC_TRUE;
+}
+
+static void ALCmmdevBackendFactory_deinit(ALCmmdevBackendFactory* UNUSED(self))
+{
+    clear_devlist(&PlaybackDevices);
+    VECTOR_DEINIT(PlaybackDevices);
+
+    clear_devlist(&CaptureDevices);
+    VECTOR_DEINIT(CaptureDevices);
+
+    if(ThreadHdl)
+    {
+        TRACE("Sending WM_QUIT to Thread %04lx\n", ThreadID);
+        PostThreadMessage(ThreadID, WM_QUIT, 0, 0);
+        CloseHandle(ThreadHdl);
+        ThreadHdl = NULL;
+    }
+}
+
+static ALCboolean ALCmmdevBackendFactory_querySupport(ALCmmdevBackendFactory* UNUSED(self), ALCbackend_Type type)
+{
+    /* TODO: Disable capture with mmdevapi for now, since it doesn't do any
+     * rechanneling or resampling; if the device is configured for 48000hz
+     * stereo input, for example, and the app asks for 22050hz mono,
+     * initialization will fail.
+     */
+    if(type == ALCbackend_Playback /*|| type == ALCbackend_Capture*/)
+        return ALC_TRUE;
+    return ALC_FALSE;
+}
+
+static void ALCmmdevBackendFactory_probe(ALCmmdevBackendFactory* UNUSED(self), enum DevProbe type)
+{
+    ThreadRequest req = { NULL, 0 };
+
+    req.FinishedEvt = CreateEventW(NULL, FALSE, FALSE, NULL);
+    if(req.FinishedEvt == NULL)
+        ERR("Failed to create event: %lu\n", GetLastError());
+    else
+    {
+        HRESULT hr = E_FAIL;
+        if(PostThreadMessage(ThreadID, WM_USER_Enumerate, (WPARAM)&req, type))
+            hr = WaitForResponse(&req);
+        if(SUCCEEDED(hr)) switch(type)
+        {
+        case ALL_DEVICE_PROBE:
+            VECTOR_FOR_EACH(const DevMap, PlaybackDevices, AppendAllDevicesList2);
+            break;
+
+        case CAPTURE_DEVICE_PROBE:
+            VECTOR_FOR_EACH(const DevMap, CaptureDevices, AppendCaptureDeviceList2);
+            break;
+        }
+        CloseHandle(req.FinishedEvt);
+        req.FinishedEvt = NULL;
+    }
+}
+
+static ALCbackend* ALCmmdevBackendFactory_createBackend(ALCmmdevBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
+{
+    if(type == ALCbackend_Playback)
+    {
+        ALCmmdevPlayback *backend;
+        NEW_OBJ(backend, ALCmmdevPlayback)(device);
+        if(!backend) return NULL;
+        return STATIC_CAST(ALCbackend, backend);
+    }
+    if(type == ALCbackend_Capture)
+    {
+        ALCmmdevCapture *backend;
+        NEW_OBJ(backend, ALCmmdevCapture)(device);
+        if(!backend) return NULL;
+        return STATIC_CAST(ALCbackend, backend);
+    }
+
+    return NULL;
+}
+
+
+ALCbackendFactory *ALCmmdevBackendFactory_getFactory(void)
+{
+    static ALCmmdevBackendFactory factory = ALCMMDEVBACKENDFACTORY_INITIALIZER;
+    return STATIC_CAST(ALCbackendFactory, &factory);
+}

+ 223 - 0
Engine/lib/openal-soft/Alc/backends/null.c

@@ -0,0 +1,223 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 2010 by Chris Robinson
+ * This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the
+ *  Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#ifdef HAVE_WINDOWS_H
+#include <windows.h>
+#endif
+
+#include "alMain.h"
+#include "alu.h"
+#include "threads.h"
+#include "compat.h"
+
+#include "backends/base.h"
+
+
+typedef struct ALCnullBackend {
+    DERIVE_FROM_TYPE(ALCbackend);
+
+    volatile int killNow;
+    althrd_t thread;
+} ALCnullBackend;
+
+static int ALCnullBackend_mixerProc(void *ptr);
+
+static void ALCnullBackend_Construct(ALCnullBackend *self, ALCdevice *device);
+static DECLARE_FORWARD(ALCnullBackend, ALCbackend, void, Destruct)
+static ALCenum ALCnullBackend_open(ALCnullBackend *self, const ALCchar *name);
+static void ALCnullBackend_close(ALCnullBackend *self);
+static ALCboolean ALCnullBackend_reset(ALCnullBackend *self);
+static ALCboolean ALCnullBackend_start(ALCnullBackend *self);
+static void ALCnullBackend_stop(ALCnullBackend *self);
+static DECLARE_FORWARD2(ALCnullBackend, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
+static DECLARE_FORWARD(ALCnullBackend, ALCbackend, ALCuint, availableSamples)
+static DECLARE_FORWARD(ALCnullBackend, ALCbackend, ClockLatency, getClockLatency)
+static DECLARE_FORWARD(ALCnullBackend, ALCbackend, void, lock)
+static DECLARE_FORWARD(ALCnullBackend, ALCbackend, void, unlock)
+DECLARE_DEFAULT_ALLOCATORS(ALCnullBackend)
+
+DEFINE_ALCBACKEND_VTABLE(ALCnullBackend);
+
+
+static const ALCchar nullDevice[] = "No Output";
+
+
+static void ALCnullBackend_Construct(ALCnullBackend *self, ALCdevice *device)
+{
+    ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
+    SET_VTABLE2(ALCnullBackend, ALCbackend, self);
+}
+
+
+static int ALCnullBackend_mixerProc(void *ptr)
+{
+    ALCnullBackend *self = (ALCnullBackend*)ptr;
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+    struct timespec now, start;
+    ALuint64 avail, done;
+    const long restTime = (long)((ALuint64)device->UpdateSize * 1000000000 /
+                                 device->Frequency / 2);
+
+    SetRTPriority();
+    althrd_setname(althrd_current(), MIXER_THREAD_NAME);
+
+    done = 0;
+    if(altimespec_get(&start, AL_TIME_UTC) != AL_TIME_UTC)
+    {
+        ERR("Failed to get starting time\n");
+        return 1;
+    }
+    while(!self->killNow && device->Connected)
+    {
+        if(altimespec_get(&now, AL_TIME_UTC) != AL_TIME_UTC)
+        {
+            ERR("Failed to get current time\n");
+            return 1;
+        }
+
+        avail  = (now.tv_sec - start.tv_sec) * device->Frequency;
+        avail += (ALint64)(now.tv_nsec - start.tv_nsec) * device->Frequency / 1000000000;
+        if(avail < done)
+        {
+            /* Oops, time skipped backwards. Reset the number of samples done
+             * with one update available since we (likely) just came back from
+             * sleeping. */
+            done = avail - device->UpdateSize;
+        }
+
+        if(avail-done < device->UpdateSize)
+            al_nssleep(restTime);
+        else while(avail-done >= device->UpdateSize)
+        {
+            aluMixData(device, NULL, device->UpdateSize);
+            done += device->UpdateSize;
+        }
+    }
+
+    return 0;
+}
+
+
+static ALCenum ALCnullBackend_open(ALCnullBackend *self, const ALCchar *name)
+{
+    ALCdevice *device;
+
+    if(!name)
+        name = nullDevice;
+    else if(strcmp(name, nullDevice) != 0)
+        return ALC_INVALID_VALUE;
+
+    device = STATIC_CAST(ALCbackend, self)->mDevice;
+    al_string_copy_cstr(&device->DeviceName, name);
+
+    return ALC_NO_ERROR;
+}
+
+static void ALCnullBackend_close(ALCnullBackend* UNUSED(self))
+{
+}
+
+static ALCboolean ALCnullBackend_reset(ALCnullBackend *self)
+{
+    SetDefaultWFXChannelOrder(STATIC_CAST(ALCbackend, self)->mDevice);
+    return ALC_TRUE;
+}
+
+static ALCboolean ALCnullBackend_start(ALCnullBackend *self)
+{
+    self->killNow = 0;
+    if(althrd_create(&self->thread, ALCnullBackend_mixerProc, self) != althrd_success)
+        return ALC_FALSE;
+    return ALC_TRUE;
+}
+
+static void ALCnullBackend_stop(ALCnullBackend *self)
+{
+    int res;
+
+    if(self->killNow)
+        return;
+
+    self->killNow = 1;
+    althrd_join(self->thread, &res);
+}
+
+
+typedef struct ALCnullBackendFactory {
+    DERIVE_FROM_TYPE(ALCbackendFactory);
+} ALCnullBackendFactory;
+#define ALCNULLBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCnullBackendFactory, ALCbackendFactory) } }
+
+ALCbackendFactory *ALCnullBackendFactory_getFactory(void);
+
+static ALCboolean ALCnullBackendFactory_init(ALCnullBackendFactory *self);
+static DECLARE_FORWARD(ALCnullBackendFactory, ALCbackendFactory, void, deinit)
+static ALCboolean ALCnullBackendFactory_querySupport(ALCnullBackendFactory *self, ALCbackend_Type type);
+static void ALCnullBackendFactory_probe(ALCnullBackendFactory *self, enum DevProbe type);
+static ALCbackend* ALCnullBackendFactory_createBackend(ALCnullBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
+DEFINE_ALCBACKENDFACTORY_VTABLE(ALCnullBackendFactory);
+
+
+ALCbackendFactory *ALCnullBackendFactory_getFactory(void)
+{
+    static ALCnullBackendFactory factory = ALCNULLBACKENDFACTORY_INITIALIZER;
+    return STATIC_CAST(ALCbackendFactory, &factory);
+}
+
+
+static ALCboolean ALCnullBackendFactory_init(ALCnullBackendFactory* UNUSED(self))
+{
+    return ALC_TRUE;
+}
+
+static ALCboolean ALCnullBackendFactory_querySupport(ALCnullBackendFactory* UNUSED(self), ALCbackend_Type type)
+{
+    if(type == ALCbackend_Playback)
+        return ALC_TRUE;
+    return ALC_FALSE;
+}
+
+static void ALCnullBackendFactory_probe(ALCnullBackendFactory* UNUSED(self), enum DevProbe type)
+{
+    switch(type)
+    {
+        case ALL_DEVICE_PROBE:
+            AppendAllDevicesList(nullDevice);
+            break;
+        case CAPTURE_DEVICE_PROBE:
+            break;
+    }
+}
+
+static ALCbackend* ALCnullBackendFactory_createBackend(ALCnullBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
+{
+    if(type == ALCbackend_Playback)
+    {
+        ALCnullBackend *backend;
+        NEW_OBJ(backend, ALCnullBackend)(device);
+        if(!backend) return NULL;
+        return STATIC_CAST(ALCbackend, backend);
+    }
+
+    return NULL;
+}

+ 436 - 0
Engine/lib/openal-soft/Alc/backends/opensl.c

@@ -0,0 +1,436 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* This is an OpenAL backend for Android using the native audio APIs based on
+ * OpenSL ES 1.0.1. It is based on source code for the native-audio sample app
+ * bundled with NDK.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+
+#include "alMain.h"
+#include "alu.h"
+#include "threads.h"
+
+#include <SLES/OpenSLES.h>
+#include <SLES/OpenSLES_Android.h>
+
+/* Helper macros */
+#define VCALL(obj, func)  ((*(obj))->func((obj), EXTRACT_VCALL_ARGS
+#define VCALL0(obj, func)  ((*(obj))->func((obj) EXTRACT_VCALL_ARGS
+
+
+typedef struct {
+    /* engine interfaces */
+    SLObjectItf engineObject;
+    SLEngineItf engine;
+
+    /* output mix interfaces */
+    SLObjectItf outputMix;
+
+    /* buffer queue player interfaces */
+    SLObjectItf bufferQueueObject;
+
+    void *buffer;
+    ALuint bufferSize;
+    ALuint curBuffer;
+
+    ALuint frameSize;
+} osl_data;
+
+
+static const ALCchar opensl_device[] = "OpenSL";
+
+
+static SLuint32 GetChannelMask(enum DevFmtChannels chans)
+{
+    switch(chans)
+    {
+        case DevFmtMono: return SL_SPEAKER_FRONT_CENTER;
+        case DevFmtStereo: return SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT;
+        case DevFmtQuad: return SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT|
+                                SL_SPEAKER_BACK_LEFT|SL_SPEAKER_BACK_RIGHT;
+        case DevFmtX51: return SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT|
+                               SL_SPEAKER_FRONT_CENTER|SL_SPEAKER_LOW_FREQUENCY|
+                               SL_SPEAKER_SIDE_LEFT|SL_SPEAKER_SIDE_RIGHT;
+        case DevFmtX51Rear: return SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT|
+                                   SL_SPEAKER_FRONT_CENTER|SL_SPEAKER_LOW_FREQUENCY|
+                                   SL_SPEAKER_BACK_LEFT|SL_SPEAKER_BACK_RIGHT;
+        case DevFmtX61: return SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT|
+                               SL_SPEAKER_FRONT_CENTER|SL_SPEAKER_LOW_FREQUENCY|
+                               SL_SPEAKER_BACK_CENTER|
+                               SL_SPEAKER_SIDE_LEFT|SL_SPEAKER_SIDE_RIGHT;
+        case DevFmtX71: return SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT|
+                               SL_SPEAKER_FRONT_CENTER|SL_SPEAKER_LOW_FREQUENCY|
+                               SL_SPEAKER_BACK_LEFT|SL_SPEAKER_BACK_RIGHT|
+                               SL_SPEAKER_SIDE_LEFT|SL_SPEAKER_SIDE_RIGHT;
+        case DevFmtAmbi1:
+        case DevFmtAmbi2:
+        case DevFmtAmbi3:
+            break;
+    }
+    return 0;
+}
+
+static const char *res_str(SLresult result)
+{
+    switch(result)
+    {
+        case SL_RESULT_SUCCESS: return "Success";
+        case SL_RESULT_PRECONDITIONS_VIOLATED: return "Preconditions violated";
+        case SL_RESULT_PARAMETER_INVALID: return "Parameter invalid";
+        case SL_RESULT_MEMORY_FAILURE: return "Memory failure";
+        case SL_RESULT_RESOURCE_ERROR: return "Resource error";
+        case SL_RESULT_RESOURCE_LOST: return "Resource lost";
+        case SL_RESULT_IO_ERROR: return "I/O error";
+        case SL_RESULT_BUFFER_INSUFFICIENT: return "Buffer insufficient";
+        case SL_RESULT_CONTENT_CORRUPTED: return "Content corrupted";
+        case SL_RESULT_CONTENT_UNSUPPORTED: return "Content unsupported";
+        case SL_RESULT_CONTENT_NOT_FOUND: return "Content not found";
+        case SL_RESULT_PERMISSION_DENIED: return "Permission denied";
+        case SL_RESULT_FEATURE_UNSUPPORTED: return "Feature unsupported";
+        case SL_RESULT_INTERNAL_ERROR: return "Internal error";
+        case SL_RESULT_UNKNOWN_ERROR: return "Unknown error";
+        case SL_RESULT_OPERATION_ABORTED: return "Operation aborted";
+        case SL_RESULT_CONTROL_LOST: return "Control lost";
+#ifdef SL_RESULT_READONLY
+        case SL_RESULT_READONLY: return "ReadOnly";
+#endif
+#ifdef SL_RESULT_ENGINEOPTION_UNSUPPORTED
+        case SL_RESULT_ENGINEOPTION_UNSUPPORTED: return "Engine option unsupported";
+#endif
+#ifdef SL_RESULT_SOURCE_SINK_INCOMPATIBLE
+        case SL_RESULT_SOURCE_SINK_INCOMPATIBLE: return "Source/Sink incompatible";
+#endif
+    }
+    return "Unknown error code";
+}
+
+#define PRINTERR(x, s) do {                                                      \
+    if((x) != SL_RESULT_SUCCESS)                                                 \
+        ERR("%s: %s\n", (s), res_str((x)));                                      \
+} while(0)
+
+/* this callback handler is called every time a buffer finishes playing */
+static void opensl_callback(SLAndroidSimpleBufferQueueItf bq, void *context)
+{
+    ALCdevice *Device = context;
+    osl_data *data = Device->ExtraData;
+    ALvoid *buf;
+    SLresult result;
+
+    buf = (ALbyte*)data->buffer + data->curBuffer*data->bufferSize;
+    aluMixData(Device, buf, data->bufferSize/data->frameSize);
+
+    result = VCALL(bq,Enqueue)(buf, data->bufferSize);
+    PRINTERR(result, "bq->Enqueue");
+
+    data->curBuffer = (data->curBuffer+1) % Device->NumUpdates;
+}
+
+
+static ALCenum opensl_open_playback(ALCdevice *Device, const ALCchar *deviceName)
+{
+    osl_data *data = NULL;
+    SLresult result;
+
+    if(!deviceName)
+        deviceName = opensl_device;
+    else if(strcmp(deviceName, opensl_device) != 0)
+        return ALC_INVALID_VALUE;
+
+    data = calloc(1, sizeof(*data));
+    if(!data)
+        return ALC_OUT_OF_MEMORY;
+
+    // create engine
+    result = slCreateEngine(&data->engineObject, 0, NULL, 0, NULL, NULL);
+    PRINTERR(result, "slCreateEngine");
+    if(SL_RESULT_SUCCESS == result)
+    {
+        result = VCALL(data->engineObject,Realize)(SL_BOOLEAN_FALSE);
+        PRINTERR(result, "engine->Realize");
+    }
+    if(SL_RESULT_SUCCESS == result)
+    {
+        result = VCALL(data->engineObject,GetInterface)(SL_IID_ENGINE, &data->engine);
+        PRINTERR(result, "engine->GetInterface");
+    }
+    if(SL_RESULT_SUCCESS == result)
+    {
+        result = VCALL(data->engine,CreateOutputMix)(&data->outputMix, 0, NULL, NULL);
+        PRINTERR(result, "engine->CreateOutputMix");
+    }
+    if(SL_RESULT_SUCCESS == result)
+    {
+        result = VCALL(data->outputMix,Realize)(SL_BOOLEAN_FALSE);
+        PRINTERR(result, "outputMix->Realize");
+    }
+
+    if(SL_RESULT_SUCCESS != result)
+    {
+        if(data->outputMix != NULL)
+            VCALL0(data->outputMix,Destroy)();
+        data->outputMix = NULL;
+
+        if(data->engineObject != NULL)
+            VCALL0(data->engineObject,Destroy)();
+        data->engineObject = NULL;
+        data->engine = NULL;
+
+        free(data);
+        return ALC_INVALID_VALUE;
+    }
+
+    al_string_copy_cstr(&Device->DeviceName, deviceName);
+    Device->ExtraData = data;
+
+    return ALC_NO_ERROR;
+}
+
+
+static void opensl_close_playback(ALCdevice *Device)
+{
+    osl_data *data = Device->ExtraData;
+
+    if(data->bufferQueueObject != NULL)
+        VCALL0(data->bufferQueueObject,Destroy)();
+    data->bufferQueueObject = NULL;
+
+    VCALL0(data->outputMix,Destroy)();
+    data->outputMix = NULL;
+
+    VCALL0(data->engineObject,Destroy)();
+    data->engineObject = NULL;
+    data->engine = NULL;
+
+    free(data);
+    Device->ExtraData = NULL;
+}
+
+static ALCboolean opensl_reset_playback(ALCdevice *Device)
+{
+    osl_data *data = Device->ExtraData;
+    SLDataLocator_AndroidSimpleBufferQueue loc_bufq;
+    SLDataLocator_OutputMix loc_outmix;
+    SLDataFormat_PCM format_pcm;
+    SLDataSource audioSrc;
+    SLDataSink audioSnk;
+    SLInterfaceID id;
+    SLboolean req;
+    SLresult result;
+
+
+    Device->UpdateSize = (ALuint64)Device->UpdateSize * 44100 / Device->Frequency;
+    Device->UpdateSize = Device->UpdateSize * Device->NumUpdates / 2;
+    Device->NumUpdates = 2;
+
+    Device->Frequency = 44100;
+    Device->FmtChans = DevFmtStereo;
+    Device->FmtType = DevFmtShort;
+
+    SetDefaultWFXChannelOrder(Device);
+
+
+    id  = SL_IID_ANDROIDSIMPLEBUFFERQUEUE;
+    req = SL_BOOLEAN_TRUE;
+
+    loc_bufq.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
+    loc_bufq.numBuffers = Device->NumUpdates;
+
+    format_pcm.formatType = SL_DATAFORMAT_PCM;
+    format_pcm.numChannels = ChannelsFromDevFmt(Device->FmtChans);
+    format_pcm.samplesPerSec = Device->Frequency * 1000;
+    format_pcm.bitsPerSample = BytesFromDevFmt(Device->FmtType) * 8;
+    format_pcm.containerSize = format_pcm.bitsPerSample;
+    format_pcm.channelMask = GetChannelMask(Device->FmtChans);
+    format_pcm.endianness = IS_LITTLE_ENDIAN ? SL_BYTEORDER_LITTLEENDIAN :
+                                               SL_BYTEORDER_BIGENDIAN;
+
+    audioSrc.pLocator = &loc_bufq;
+    audioSrc.pFormat = &format_pcm;
+
+    loc_outmix.locatorType = SL_DATALOCATOR_OUTPUTMIX;
+    loc_outmix.outputMix = data->outputMix;
+    audioSnk.pLocator = &loc_outmix;
+    audioSnk.pFormat = NULL;
+
+
+    if(data->bufferQueueObject != NULL)
+        VCALL0(data->bufferQueueObject,Destroy)();
+    data->bufferQueueObject = NULL;
+
+    result = VCALL(data->engine,CreateAudioPlayer)(&data->bufferQueueObject, &audioSrc, &audioSnk, 1, &id, &req);
+    PRINTERR(result, "engine->CreateAudioPlayer");
+    if(SL_RESULT_SUCCESS == result)
+    {
+        result = VCALL(data->bufferQueueObject,Realize)(SL_BOOLEAN_FALSE);
+        PRINTERR(result, "bufferQueue->Realize");
+    }
+
+    if(SL_RESULT_SUCCESS != result)
+    {
+        if(data->bufferQueueObject != NULL)
+            VCALL0(data->bufferQueueObject,Destroy)();
+        data->bufferQueueObject = NULL;
+
+        return ALC_FALSE;
+    }
+
+    return ALC_TRUE;
+}
+
+static ALCboolean opensl_start_playback(ALCdevice *Device)
+{
+    osl_data *data = Device->ExtraData;
+    SLAndroidSimpleBufferQueueItf bufferQueue;
+    SLPlayItf player;
+    SLresult result;
+    ALuint i;
+
+    result = VCALL(data->bufferQueueObject,GetInterface)(SL_IID_BUFFERQUEUE, &bufferQueue);
+    PRINTERR(result, "bufferQueue->GetInterface");
+    if(SL_RESULT_SUCCESS == result)
+    {
+        result = VCALL(bufferQueue,RegisterCallback)(opensl_callback, Device);
+        PRINTERR(result, "bufferQueue->RegisterCallback");
+    }
+    if(SL_RESULT_SUCCESS == result)
+    {
+        data->frameSize = FrameSizeFromDevFmt(Device->FmtChans, Device->FmtType);
+        data->bufferSize = Device->UpdateSize * data->frameSize;
+        data->buffer = calloc(Device->NumUpdates, data->bufferSize);
+        if(!data->buffer)
+        {
+            result = SL_RESULT_MEMORY_FAILURE;
+            PRINTERR(result, "calloc");
+        }
+    }
+    /* enqueue the first buffer to kick off the callbacks */
+    for(i = 0;i < Device->NumUpdates;i++)
+    {
+        if(SL_RESULT_SUCCESS == result)
+        {
+            ALvoid *buf = (ALbyte*)data->buffer + i*data->bufferSize;
+            result = VCALL(bufferQueue,Enqueue)(buf, data->bufferSize);
+            PRINTERR(result, "bufferQueue->Enqueue");
+        }
+    }
+    data->curBuffer = 0;
+    if(SL_RESULT_SUCCESS == result)
+    {
+        result = VCALL(data->bufferQueueObject,GetInterface)(SL_IID_PLAY, &player);
+        PRINTERR(result, "bufferQueue->GetInterface");
+    }
+    if(SL_RESULT_SUCCESS == result)
+    {
+        result = VCALL(player,SetPlayState)(SL_PLAYSTATE_PLAYING);
+        PRINTERR(result, "player->SetPlayState");
+    }
+
+    if(SL_RESULT_SUCCESS != result)
+    {
+        if(data->bufferQueueObject != NULL)
+            VCALL0(data->bufferQueueObject,Destroy)();
+        data->bufferQueueObject = NULL;
+
+        free(data->buffer);
+        data->buffer = NULL;
+        data->bufferSize = 0;
+
+        return ALC_FALSE;
+    }
+
+    return ALC_TRUE;
+}
+
+
+static void opensl_stop_playback(ALCdevice *Device)
+{
+    osl_data *data = Device->ExtraData;
+    SLPlayItf player;
+    SLAndroidSimpleBufferQueueItf bufferQueue;
+    SLresult result;
+
+    result = VCALL(data->bufferQueueObject,GetInterface)(SL_IID_PLAY, &player);
+    PRINTERR(result, "bufferQueue->GetInterface");
+    if(SL_RESULT_SUCCESS == result)
+    {
+        result = VCALL(player,SetPlayState)(SL_PLAYSTATE_STOPPED);
+        PRINTERR(result, "player->SetPlayState");
+    }
+
+    result = VCALL(data->bufferQueueObject,GetInterface)(SL_IID_BUFFERQUEUE, &bufferQueue);
+    PRINTERR(result, "bufferQueue->GetInterface");
+    if(SL_RESULT_SUCCESS == result)
+    {
+        result = VCALL0(bufferQueue,Clear)();
+        PRINTERR(result, "bufferQueue->Clear");
+    }
+    if(SL_RESULT_SUCCESS == result)
+    {
+        SLAndroidSimpleBufferQueueState state;
+        do {
+            althrd_yield();
+            result = VCALL(bufferQueue,GetState)(&state);
+        } while(SL_RESULT_SUCCESS == result && state.count > 0);
+        PRINTERR(result, "bufferQueue->GetState");
+    }
+
+    free(data->buffer);
+    data->buffer = NULL;
+    data->bufferSize = 0;
+}
+
+
+static const BackendFuncs opensl_funcs = {
+    opensl_open_playback,
+    opensl_close_playback,
+    opensl_reset_playback,
+    opensl_start_playback,
+    opensl_stop_playback,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL
+};
+
+
+ALCboolean alc_opensl_init(BackendFuncs *func_list)
+{
+    *func_list = opensl_funcs;
+    return ALC_TRUE;
+}
+
+void alc_opensl_deinit(void)
+{
+}
+
+void alc_opensl_probe(enum DevProbe type)
+{
+    switch(type)
+    {
+        case ALL_DEVICE_PROBE:
+            AppendAllDevicesList(opensl_device);
+            break;
+        case CAPTURE_DEVICE_PROBE:
+            break;
+    }
+}

+ 821 - 0
Engine/lib/openal-soft/Alc/backends/oss.c

@@ -0,0 +1,821 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 1999-2007 by authors.
+ * This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the
+ *  Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#include "config.h"
+
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <memory.h>
+#include <unistd.h>
+#include <errno.h>
+#include <math.h>
+
+#include "alMain.h"
+#include "alu.h"
+#include "threads.h"
+#include "compat.h"
+
+#include "backends/base.h"
+
+#include <sys/soundcard.h>
+
+/*
+ * The OSS documentation talks about SOUND_MIXER_READ, but the header
+ * only contains MIXER_READ. Play safe. Same for WRITE.
+ */
+#ifndef SOUND_MIXER_READ
+#define SOUND_MIXER_READ MIXER_READ
+#endif
+#ifndef SOUND_MIXER_WRITE
+#define SOUND_MIXER_WRITE MIXER_WRITE
+#endif
+
+#if defined(SOUND_VERSION) && (SOUND_VERSION < 0x040000)
+#define ALC_OSS_COMPAT
+#endif
+#ifndef SNDCTL_AUDIOINFO
+#define ALC_OSS_COMPAT
+#endif
+
+/*
+ * FreeBSD strongly discourages the use of specific devices,
+ * such as those returned in oss_audioinfo.devnode
+ */
+#ifdef __FreeBSD__
+#define ALC_OSS_DEVNODE_TRUC
+#endif
+
+struct oss_device {
+    const ALCchar *handle;
+    const char *path;
+    struct oss_device *next;
+};
+
+static struct oss_device oss_playback = {
+    "OSS Default",
+    "/dev/dsp",
+    NULL
+};
+
+static struct oss_device oss_capture = {
+    "OSS Default",
+    "/dev/dsp",
+    NULL
+};
+
+#ifdef ALC_OSS_COMPAT
+
+static void ALCossListPopulate(struct oss_device *UNUSED(playback), struct oss_device *UNUSED(capture))
+{
+}
+
+#else
+
+#ifndef HAVE_STRNLEN
+static size_t strnlen(const char *str, size_t maxlen)
+{
+    const char *end = memchr(str, 0, maxlen);
+    if(!end) return maxlen;
+    return end - str;
+}
+#endif
+
+static void ALCossListAppend(struct oss_device *list, const char *handle, size_t hlen, const char *path, size_t plen)
+{
+    struct oss_device *next;
+    struct oss_device *last;
+    size_t i;
+
+    /* skip the first item "OSS Default" */
+    last = list;
+    next = list->next;
+#ifdef ALC_OSS_DEVNODE_TRUC
+    for(i = 0;i < plen;i++)
+    {
+        if(path[i] == '.')
+        {
+            if(strncmp(path + i, handle + hlen + i - plen, plen - i) == 0)
+                hlen = hlen + i - plen;
+            plen = i;
+        }
+    }
+#else
+    (void)i;
+#endif
+    if(handle[0] == '\0')
+    {
+        handle = path;
+        hlen = plen;
+    }
+
+    while(next != NULL)
+    {
+        if(strncmp(next->path, path, plen) == 0)
+            return;
+        last = next;
+        next = next->next;
+    }
+
+    next = (struct oss_device*)malloc(sizeof(struct oss_device) + hlen + plen + 2);
+    next->handle = (char*)(next + 1);
+    next->path = next->handle + hlen + 1;
+    next->next = NULL;
+    last->next = next;
+
+    strncpy((char*)next->handle, handle, hlen);
+    ((char*)next->handle)[hlen] = '\0';
+    strncpy((char*)next->path, path, plen);
+    ((char*)next->path)[plen] = '\0';
+
+    TRACE("Got device \"%s\", \"%s\"\n", next->handle, next->path);
+}
+
+static void ALCossListPopulate(struct oss_device *playback, struct oss_device *capture)
+{
+    struct oss_sysinfo si;
+    struct oss_audioinfo ai;
+    int fd, i;
+
+    if((fd=open("/dev/mixer", O_RDONLY)) < 0)
+    {
+        ERR("Could not open /dev/mixer\n");
+        return;
+    }
+    if(ioctl(fd, SNDCTL_SYSINFO, &si) == -1)
+    {
+        ERR("SNDCTL_SYSINFO failed: %s\n", strerror(errno));
+        goto done;
+    }
+    for(i = 0;i < si.numaudios;i++)
+    {
+        const char *handle;
+        size_t len;
+
+        ai.dev = i;
+        if(ioctl(fd, SNDCTL_AUDIOINFO, &ai) == -1)
+        {
+            ERR("SNDCTL_AUDIOINFO (%d) failed: %s\n", i, strerror(errno));
+            continue;
+        }
+        if(ai.devnode[0] == '\0')
+            continue;
+
+        if(ai.handle[0] != '\0')
+        {
+            len = strnlen(ai.handle, sizeof(ai.handle));
+            handle = ai.handle;
+        }
+        else
+        {
+            len = strnlen(ai.name, sizeof(ai.name));
+            handle = ai.name;
+        }
+        if((ai.caps&DSP_CAP_INPUT) && capture != NULL)
+            ALCossListAppend(capture, handle, len, ai.devnode, strnlen(ai.devnode, sizeof(ai.devnode)));
+        if((ai.caps&DSP_CAP_OUTPUT) && playback != NULL)
+            ALCossListAppend(playback, handle, len, ai.devnode, strnlen(ai.devnode, sizeof(ai.devnode)));
+    }
+
+done:
+    close(fd);
+}
+
+#endif
+
+static void ALCossListFree(struct oss_device *list)
+{
+    struct oss_device *cur;
+    if(list == NULL)
+        return;
+
+    /* skip the first item "OSS Default" */
+    cur = list->next;
+    list->next = NULL;
+
+    while(cur != NULL)
+    {
+        struct oss_device *next = cur->next;
+        free(cur);
+        cur = next;
+    }
+}
+
+static int log2i(ALCuint x)
+{
+    int y = 0;
+    while (x > 1)
+    {
+        x >>= 1;
+        y++;
+    }
+    return y;
+}
+
+typedef struct ALCplaybackOSS {
+    DERIVE_FROM_TYPE(ALCbackend);
+
+    int fd;
+
+    ALubyte *mix_data;
+    int data_size;
+
+    volatile int killNow;
+    althrd_t thread;
+} ALCplaybackOSS;
+
+static int ALCplaybackOSS_mixerProc(void *ptr);
+
+static void ALCplaybackOSS_Construct(ALCplaybackOSS *self, ALCdevice *device);
+static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, void, Destruct)
+static ALCenum ALCplaybackOSS_open(ALCplaybackOSS *self, const ALCchar *name);
+static void ALCplaybackOSS_close(ALCplaybackOSS *self);
+static ALCboolean ALCplaybackOSS_reset(ALCplaybackOSS *self);
+static ALCboolean ALCplaybackOSS_start(ALCplaybackOSS *self);
+static void ALCplaybackOSS_stop(ALCplaybackOSS *self);
+static DECLARE_FORWARD2(ALCplaybackOSS, ALCbackend, ALCenum, captureSamples, ALCvoid*, ALCuint)
+static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, ALCuint, availableSamples)
+static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, ClockLatency, getClockLatency)
+static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, void, lock)
+static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, void, unlock)
+DECLARE_DEFAULT_ALLOCATORS(ALCplaybackOSS)
+DEFINE_ALCBACKEND_VTABLE(ALCplaybackOSS);
+
+
+static int ALCplaybackOSS_mixerProc(void *ptr)
+{
+    ALCplaybackOSS *self = (ALCplaybackOSS*)ptr;
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+    ALint frameSize;
+    ssize_t wrote;
+
+    SetRTPriority();
+    althrd_setname(althrd_current(), MIXER_THREAD_NAME);
+
+    frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
+
+    while(!self->killNow && device->Connected)
+    {
+        ALint len = self->data_size;
+        ALubyte *WritePtr = self->mix_data;
+
+        aluMixData(device, WritePtr, len/frameSize);
+        while(len > 0 && !self->killNow)
+        {
+            wrote = write(self->fd, WritePtr, len);
+            if(wrote < 0)
+            {
+                if(errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR)
+                {
+                    ERR("write failed: %s\n", strerror(errno));
+                    ALCplaybackOSS_lock(self);
+                    aluHandleDisconnect(device);
+                    ALCplaybackOSS_unlock(self);
+                    break;
+                }
+
+                al_nssleep(1000000);
+                continue;
+            }
+
+            len -= wrote;
+            WritePtr += wrote;
+        }
+    }
+
+    return 0;
+}
+
+
+static void ALCplaybackOSS_Construct(ALCplaybackOSS *self, ALCdevice *device)
+{
+    ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
+    SET_VTABLE2(ALCplaybackOSS, ALCbackend, self);
+}
+
+static ALCenum ALCplaybackOSS_open(ALCplaybackOSS *self, const ALCchar *name)
+{
+    struct oss_device *dev = &oss_playback;
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+
+    if(!name)
+        name = dev->handle;
+    else
+    {
+        while (dev != NULL)
+        {
+            if (strcmp(dev->handle, name) == 0)
+                break;
+            dev = dev->next;
+        }
+        if (dev == NULL)
+            return ALC_INVALID_VALUE;
+    }
+
+    self->killNow = 0;
+
+    self->fd = open(dev->path, O_WRONLY);
+    if(self->fd == -1)
+    {
+        ERR("Could not open %s: %s\n", dev->path, strerror(errno));
+        return ALC_INVALID_VALUE;
+    }
+
+    al_string_copy_cstr(&device->DeviceName, name);
+
+    return ALC_NO_ERROR;
+}
+
+static void ALCplaybackOSS_close(ALCplaybackOSS *self)
+{
+    close(self->fd);
+    self->fd = -1;
+}
+
+static ALCboolean ALCplaybackOSS_reset(ALCplaybackOSS *self)
+{
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+    int numFragmentsLogSize;
+    int log2FragmentSize;
+    unsigned int periods;
+    audio_buf_info info;
+    ALuint frameSize;
+    int numChannels;
+    int ossFormat;
+    int ossSpeed;
+    char *err;
+
+    switch(device->FmtType)
+    {
+        case DevFmtByte:
+            ossFormat = AFMT_S8;
+            break;
+        case DevFmtUByte:
+            ossFormat = AFMT_U8;
+            break;
+        case DevFmtUShort:
+        case DevFmtInt:
+        case DevFmtUInt:
+        case DevFmtFloat:
+            device->FmtType = DevFmtShort;
+            /* fall-through */
+        case DevFmtShort:
+            ossFormat = AFMT_S16_NE;
+            break;
+    }
+
+    periods = device->NumUpdates;
+    numChannels = ChannelsFromDevFmt(device->FmtChans);
+    frameSize = numChannels * BytesFromDevFmt(device->FmtType);
+
+    ossSpeed = device->Frequency;
+    log2FragmentSize = log2i(device->UpdateSize * frameSize);
+
+    /* according to the OSS spec, 16 bytes are the minimum */
+    if (log2FragmentSize < 4)
+        log2FragmentSize = 4;
+    /* Subtract one period since the temp mixing buffer counts as one. Still
+     * need at least two on the card, though. */
+    if(periods > 2) periods--;
+    numFragmentsLogSize = (periods << 16) | log2FragmentSize;
+
+#define CHECKERR(func) if((func) < 0) {                                       \
+    err = #func;                                                              \
+    goto err;                                                                 \
+}
+    /* Don't fail if SETFRAGMENT fails. We can handle just about anything
+     * that's reported back via GETOSPACE */
+    ioctl(self->fd, SNDCTL_DSP_SETFRAGMENT, &numFragmentsLogSize);
+    CHECKERR(ioctl(self->fd, SNDCTL_DSP_SETFMT, &ossFormat));
+    CHECKERR(ioctl(self->fd, SNDCTL_DSP_CHANNELS, &numChannels));
+    CHECKERR(ioctl(self->fd, SNDCTL_DSP_SPEED, &ossSpeed));
+    CHECKERR(ioctl(self->fd, SNDCTL_DSP_GETOSPACE, &info));
+    if(0)
+    {
+    err:
+        ERR("%s failed: %s\n", err, strerror(errno));
+        return ALC_FALSE;
+    }
+#undef CHECKERR
+
+    if((int)ChannelsFromDevFmt(device->FmtChans) != numChannels)
+    {
+        ERR("Failed to set %s, got %d channels instead\n", DevFmtChannelsString(device->FmtChans), numChannels);
+        return ALC_FALSE;
+    }
+
+    if(!((ossFormat == AFMT_S8 && device->FmtType == DevFmtByte) ||
+         (ossFormat == AFMT_U8 && device->FmtType == DevFmtUByte) ||
+         (ossFormat == AFMT_S16_NE && device->FmtType == DevFmtShort)))
+    {
+        ERR("Failed to set %s samples, got OSS format %#x\n", DevFmtTypeString(device->FmtType), ossFormat);
+        return ALC_FALSE;
+    }
+
+    device->Frequency = ossSpeed;
+    device->UpdateSize = info.fragsize / frameSize;
+    device->NumUpdates = info.fragments + 1;
+
+    SetDefaultChannelOrder(device);
+
+    return ALC_TRUE;
+}
+
+static ALCboolean ALCplaybackOSS_start(ALCplaybackOSS *self)
+{
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+
+    self->data_size = device->UpdateSize * FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
+    self->mix_data = calloc(1, self->data_size);
+
+    self->killNow = 0;
+    if(althrd_create(&self->thread, ALCplaybackOSS_mixerProc, self) != althrd_success)
+    {
+        free(self->mix_data);
+        self->mix_data = NULL;
+        return ALC_FALSE;
+    }
+
+    return ALC_TRUE;
+}
+
+static void ALCplaybackOSS_stop(ALCplaybackOSS *self)
+{
+    int res;
+
+    if(self->killNow)
+        return;
+
+    self->killNow = 1;
+    althrd_join(self->thread, &res);
+
+    if(ioctl(self->fd, SNDCTL_DSP_RESET) != 0)
+        ERR("Error resetting device: %s\n", strerror(errno));
+
+    free(self->mix_data);
+    self->mix_data = NULL;
+}
+
+
+typedef struct ALCcaptureOSS {
+    DERIVE_FROM_TYPE(ALCbackend);
+
+    int fd;
+
+    ll_ringbuffer_t *ring;
+    int doCapture;
+
+    volatile int killNow;
+    althrd_t thread;
+} ALCcaptureOSS;
+
+static int ALCcaptureOSS_recordProc(void *ptr);
+
+static void ALCcaptureOSS_Construct(ALCcaptureOSS *self, ALCdevice *device);
+static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, void, Destruct)
+static ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name);
+static void ALCcaptureOSS_close(ALCcaptureOSS *self);
+static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, ALCboolean, reset)
+static ALCboolean ALCcaptureOSS_start(ALCcaptureOSS *self);
+static void ALCcaptureOSS_stop(ALCcaptureOSS *self);
+static ALCenum ALCcaptureOSS_captureSamples(ALCcaptureOSS *self, ALCvoid *buffer, ALCuint samples);
+static ALCuint ALCcaptureOSS_availableSamples(ALCcaptureOSS *self);
+static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, ClockLatency, getClockLatency)
+static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, void, lock)
+static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, void, unlock)
+DECLARE_DEFAULT_ALLOCATORS(ALCcaptureOSS)
+DEFINE_ALCBACKEND_VTABLE(ALCcaptureOSS);
+
+
+static int ALCcaptureOSS_recordProc(void *ptr)
+{
+    ALCcaptureOSS *self = (ALCcaptureOSS*)ptr;
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+    int frameSize;
+    ssize_t amt;
+
+    SetRTPriority();
+    althrd_setname(althrd_current(), RECORD_THREAD_NAME);
+
+    frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
+
+    while(!self->killNow)
+    {
+        ll_ringbuffer_data_t vec[2];
+
+        amt = 0;
+        if(self->doCapture)
+        {
+            ll_ringbuffer_get_write_vector(self->ring, vec);
+            if(vec[0].len > 0)
+            {
+                amt = read(self->fd, vec[0].buf, vec[0].len*frameSize);
+                if(amt < 0)
+                {
+                    ERR("read failed: %s\n", strerror(errno));
+                    ALCcaptureOSS_lock(self);
+                    aluHandleDisconnect(device);
+                    ALCcaptureOSS_unlock(self);
+                    break;
+                }
+                ll_ringbuffer_write_advance(self->ring, amt/frameSize);
+            }
+        }
+        if(amt == 0)
+        {
+            al_nssleep(1000000);
+            continue;
+        }
+    }
+
+    return 0;
+}
+
+
+static void ALCcaptureOSS_Construct(ALCcaptureOSS *self, ALCdevice *device)
+{
+    ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
+    SET_VTABLE2(ALCcaptureOSS, ALCbackend, self);
+}
+
+static ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name)
+{
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+    struct oss_device *dev = &oss_capture;
+    int numFragmentsLogSize;
+    int log2FragmentSize;
+    unsigned int periods;
+    audio_buf_info info;
+    ALuint frameSize;
+    int numChannels;
+    int ossFormat;
+    int ossSpeed;
+    char *err;
+
+    if(!name)
+        name = dev->handle;
+    else
+    {
+        while (dev != NULL)
+        {
+            if (strcmp(dev->handle, name) == 0)
+                break;
+            dev = dev->next;
+        }
+        if (dev == NULL)
+            return ALC_INVALID_VALUE;
+    }
+
+    self->fd = open(dev->path, O_RDONLY);
+    if(self->fd == -1)
+    {
+        ERR("Could not open %s: %s\n", dev->path, strerror(errno));
+        return ALC_INVALID_VALUE;
+    }
+
+    switch(device->FmtType)
+    {
+        case DevFmtByte:
+            ossFormat = AFMT_S8;
+            break;
+        case DevFmtUByte:
+            ossFormat = AFMT_U8;
+            break;
+        case DevFmtShort:
+            ossFormat = AFMT_S16_NE;
+            break;
+        case DevFmtUShort:
+        case DevFmtInt:
+        case DevFmtUInt:
+        case DevFmtFloat:
+            ERR("%s capture samples not supported\n", DevFmtTypeString(device->FmtType));
+            return ALC_INVALID_VALUE;
+    }
+
+    periods = 4;
+    numChannels = ChannelsFromDevFmt(device->FmtChans);
+    frameSize = numChannels * BytesFromDevFmt(device->FmtType);
+    ossSpeed = device->Frequency;
+    log2FragmentSize = log2i(device->UpdateSize * device->NumUpdates *
+                             frameSize / periods);
+
+    /* according to the OSS spec, 16 bytes are the minimum */
+    if (log2FragmentSize < 4)
+        log2FragmentSize = 4;
+    numFragmentsLogSize = (periods << 16) | log2FragmentSize;
+
+#define CHECKERR(func) if((func) < 0) {                                       \
+    err = #func;                                                              \
+    goto err;                                                                 \
+}
+    CHECKERR(ioctl(self->fd, SNDCTL_DSP_SETFRAGMENT, &numFragmentsLogSize));
+    CHECKERR(ioctl(self->fd, SNDCTL_DSP_SETFMT, &ossFormat));
+    CHECKERR(ioctl(self->fd, SNDCTL_DSP_CHANNELS, &numChannels));
+    CHECKERR(ioctl(self->fd, SNDCTL_DSP_SPEED, &ossSpeed));
+    CHECKERR(ioctl(self->fd, SNDCTL_DSP_GETISPACE, &info));
+    if(0)
+    {
+    err:
+        ERR("%s failed: %s\n", err, strerror(errno));
+        close(self->fd);
+        self->fd = -1;
+        return ALC_INVALID_VALUE;
+    }
+#undef CHECKERR
+
+    if((int)ChannelsFromDevFmt(device->FmtChans) != numChannels)
+    {
+        ERR("Failed to set %s, got %d channels instead\n", DevFmtChannelsString(device->FmtChans), numChannels);
+        close(self->fd);
+        self->fd = -1;
+        return ALC_INVALID_VALUE;
+    }
+
+    if(!((ossFormat == AFMT_S8 && device->FmtType == DevFmtByte) ||
+         (ossFormat == AFMT_U8 && device->FmtType == DevFmtUByte) ||
+         (ossFormat == AFMT_S16_NE && device->FmtType == DevFmtShort)))
+    {
+        ERR("Failed to set %s samples, got OSS format %#x\n", DevFmtTypeString(device->FmtType), ossFormat);
+        close(self->fd);
+        self->fd = -1;
+        return ALC_INVALID_VALUE;
+    }
+
+    self->ring = ll_ringbuffer_create(device->UpdateSize*device->NumUpdates + 1, frameSize);
+    if(!self->ring)
+    {
+        ERR("Ring buffer create failed\n");
+        close(self->fd);
+        self->fd = -1;
+        return ALC_OUT_OF_MEMORY;
+    }
+
+    self->killNow = 0;
+    if(althrd_create(&self->thread, ALCcaptureOSS_recordProc, self) != althrd_success)
+    {
+        ll_ringbuffer_free(self->ring);
+        self->ring = NULL;
+        close(self->fd);
+        self->fd = -1;
+        return ALC_OUT_OF_MEMORY;
+    }
+
+    al_string_copy_cstr(&device->DeviceName, name);
+
+    return ALC_NO_ERROR;
+}
+
+static void ALCcaptureOSS_close(ALCcaptureOSS *self)
+{
+    int res;
+
+    self->killNow = 1;
+    althrd_join(self->thread, &res);
+
+    close(self->fd);
+    self->fd = -1;
+
+    ll_ringbuffer_free(self->ring);
+    self->ring = NULL;
+}
+
+static ALCboolean ALCcaptureOSS_start(ALCcaptureOSS *self)
+{
+    self->doCapture = 1;
+    return ALC_TRUE;
+}
+
+static void ALCcaptureOSS_stop(ALCcaptureOSS *self)
+{
+    self->doCapture = 0;
+}
+
+static ALCenum ALCcaptureOSS_captureSamples(ALCcaptureOSS *self, ALCvoid *buffer, ALCuint samples)
+{
+    ll_ringbuffer_read(self->ring, buffer, samples);
+    return ALC_NO_ERROR;
+}
+
+static ALCuint ALCcaptureOSS_availableSamples(ALCcaptureOSS *self)
+{
+    return ll_ringbuffer_read_space(self->ring);
+}
+
+
+typedef struct ALCossBackendFactory {
+    DERIVE_FROM_TYPE(ALCbackendFactory);
+} ALCossBackendFactory;
+#define ALCOSSBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCossBackendFactory, ALCbackendFactory) } }
+
+ALCbackendFactory *ALCossBackendFactory_getFactory(void);
+
+static ALCboolean ALCossBackendFactory_init(ALCossBackendFactory *self);
+static void ALCossBackendFactory_deinit(ALCossBackendFactory *self);
+static ALCboolean ALCossBackendFactory_querySupport(ALCossBackendFactory *self, ALCbackend_Type type);
+static void ALCossBackendFactory_probe(ALCossBackendFactory *self, enum DevProbe type);
+static ALCbackend* ALCossBackendFactory_createBackend(ALCossBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
+DEFINE_ALCBACKENDFACTORY_VTABLE(ALCossBackendFactory);
+
+
+ALCbackendFactory *ALCossBackendFactory_getFactory(void)
+{
+    static ALCossBackendFactory factory = ALCOSSBACKENDFACTORY_INITIALIZER;
+    return STATIC_CAST(ALCbackendFactory, &factory);
+}
+
+
+ALCboolean ALCossBackendFactory_init(ALCossBackendFactory* UNUSED(self))
+{
+    ConfigValueStr(NULL, "oss", "device", &oss_playback.path);
+    ConfigValueStr(NULL, "oss", "capture", &oss_capture.path);
+
+    return ALC_TRUE;
+}
+
+void  ALCossBackendFactory_deinit(ALCossBackendFactory* UNUSED(self))
+{
+    ALCossListFree(&oss_playback);
+    ALCossListFree(&oss_capture);
+}
+
+
+ALCboolean ALCossBackendFactory_querySupport(ALCossBackendFactory* UNUSED(self), ALCbackend_Type type)
+{
+    if(type == ALCbackend_Playback || type == ALCbackend_Capture)
+        return ALC_TRUE;
+    return ALC_FALSE;
+}
+
+void ALCossBackendFactory_probe(ALCossBackendFactory* UNUSED(self), enum DevProbe type)
+{
+    switch(type)
+    {
+        case ALL_DEVICE_PROBE:
+        {
+            struct oss_device *cur = &oss_playback;
+            ALCossListFree(cur);
+            ALCossListPopulate(cur, NULL);
+            while (cur != NULL)
+            {
+                AppendAllDevicesList(cur->handle);
+                cur = cur->next;
+            }
+        }
+        break;
+
+        case CAPTURE_DEVICE_PROBE:
+        {
+            struct oss_device *cur = &oss_capture;
+            ALCossListFree(cur);
+            ALCossListPopulate(NULL, cur);
+            while (cur != NULL)
+            {
+                AppendCaptureDeviceList(cur->handle);
+                cur = cur->next;
+            }
+        }
+        break;
+    }
+}
+
+ALCbackend* ALCossBackendFactory_createBackend(ALCossBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
+{
+    if(type == ALCbackend_Playback)
+    {
+        ALCplaybackOSS *backend;
+        NEW_OBJ(backend, ALCplaybackOSS)(device);
+        if(!backend) return NULL;
+        return STATIC_CAST(ALCbackend, backend);
+    }
+    if(type == ALCbackend_Capture)
+    {
+        ALCcaptureOSS *backend;
+        NEW_OBJ(backend, ALCcaptureOSS)(device);
+        if(!backend) return NULL;
+        return STATIC_CAST(ALCbackend, backend);
+    }
+
+    return NULL;
+}

+ 573 - 0
Engine/lib/openal-soft/Alc/backends/portaudio.c

@@ -0,0 +1,573 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 1999-2007 by authors.
+ * This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the
+ *  Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "alMain.h"
+#include "alu.h"
+#include "compat.h"
+
+#include "backends/base.h"
+
+#include <portaudio.h>
+
+
+static const ALCchar pa_device[] = "PortAudio Default";
+
+
+#ifdef HAVE_DYNLOAD
+static void *pa_handle;
+#define MAKE_FUNC(x) static __typeof(x) * p##x
+MAKE_FUNC(Pa_Initialize);
+MAKE_FUNC(Pa_Terminate);
+MAKE_FUNC(Pa_GetErrorText);
+MAKE_FUNC(Pa_StartStream);
+MAKE_FUNC(Pa_StopStream);
+MAKE_FUNC(Pa_OpenStream);
+MAKE_FUNC(Pa_CloseStream);
+MAKE_FUNC(Pa_GetDefaultOutputDevice);
+MAKE_FUNC(Pa_GetDefaultInputDevice);
+MAKE_FUNC(Pa_GetStreamInfo);
+#undef MAKE_FUNC
+
+#define Pa_Initialize                  pPa_Initialize
+#define Pa_Terminate                   pPa_Terminate
+#define Pa_GetErrorText                pPa_GetErrorText
+#define Pa_StartStream                 pPa_StartStream
+#define Pa_StopStream                  pPa_StopStream
+#define Pa_OpenStream                  pPa_OpenStream
+#define Pa_CloseStream                 pPa_CloseStream
+#define Pa_GetDefaultOutputDevice      pPa_GetDefaultOutputDevice
+#define Pa_GetDefaultInputDevice       pPa_GetDefaultInputDevice
+#define Pa_GetStreamInfo               pPa_GetStreamInfo
+#endif
+
+static ALCboolean pa_load(void)
+{
+    PaError err;
+
+#ifdef HAVE_DYNLOAD
+    if(!pa_handle)
+    {
+#ifdef _WIN32
+# define PALIB "portaudio.dll"
+#elif defined(__APPLE__) && defined(__MACH__)
+# define PALIB "libportaudio.2.dylib"
+#elif defined(__OpenBSD__)
+# define PALIB "libportaudio.so"
+#else
+# define PALIB "libportaudio.so.2"
+#endif
+
+        pa_handle = LoadLib(PALIB);
+        if(!pa_handle)
+            return ALC_FALSE;
+
+#define LOAD_FUNC(f) do {                                                     \
+    p##f = GetSymbol(pa_handle, #f);                                          \
+    if(p##f == NULL)                                                          \
+    {                                                                         \
+        CloseLib(pa_handle);                                                  \
+        pa_handle = NULL;                                                     \
+        return ALC_FALSE;                                                     \
+    }                                                                         \
+} while(0)
+        LOAD_FUNC(Pa_Initialize);
+        LOAD_FUNC(Pa_Terminate);
+        LOAD_FUNC(Pa_GetErrorText);
+        LOAD_FUNC(Pa_StartStream);
+        LOAD_FUNC(Pa_StopStream);
+        LOAD_FUNC(Pa_OpenStream);
+        LOAD_FUNC(Pa_CloseStream);
+        LOAD_FUNC(Pa_GetDefaultOutputDevice);
+        LOAD_FUNC(Pa_GetDefaultInputDevice);
+        LOAD_FUNC(Pa_GetStreamInfo);
+#undef LOAD_FUNC
+
+        if((err=Pa_Initialize()) != paNoError)
+        {
+            ERR("Pa_Initialize() returned an error: %s\n", Pa_GetErrorText(err));
+            CloseLib(pa_handle);
+            pa_handle = NULL;
+            return ALC_FALSE;
+        }
+    }
+#else
+    if((err=Pa_Initialize()) != paNoError)
+    {
+        ERR("Pa_Initialize() returned an error: %s\n", Pa_GetErrorText(err));
+        return ALC_FALSE;
+    }
+#endif
+    return ALC_TRUE;
+}
+
+
+typedef struct ALCportPlayback {
+    DERIVE_FROM_TYPE(ALCbackend);
+
+    PaStream *stream;
+    PaStreamParameters params;
+    ALuint update_size;
+} ALCportPlayback;
+
+static int ALCportPlayback_WriteCallback(const void *inputBuffer, void *outputBuffer,
+    unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo,
+    const PaStreamCallbackFlags statusFlags, void *userData);
+
+static void ALCportPlayback_Construct(ALCportPlayback *self, ALCdevice *device);
+static void ALCportPlayback_Destruct(ALCportPlayback *self);
+static ALCenum ALCportPlayback_open(ALCportPlayback *self, const ALCchar *name);
+static void ALCportPlayback_close(ALCportPlayback *self);
+static ALCboolean ALCportPlayback_reset(ALCportPlayback *self);
+static ALCboolean ALCportPlayback_start(ALCportPlayback *self);
+static void ALCportPlayback_stop(ALCportPlayback *self);
+static DECLARE_FORWARD2(ALCportPlayback, ALCbackend, ALCenum, captureSamples, ALCvoid*, ALCuint)
+static DECLARE_FORWARD(ALCportPlayback, ALCbackend, ALCuint, availableSamples)
+static DECLARE_FORWARD(ALCportPlayback, ALCbackend, ClockLatency, getClockLatency)
+static DECLARE_FORWARD(ALCportPlayback, ALCbackend, void, lock)
+static DECLARE_FORWARD(ALCportPlayback, ALCbackend, void, unlock)
+DECLARE_DEFAULT_ALLOCATORS(ALCportPlayback)
+
+DEFINE_ALCBACKEND_VTABLE(ALCportPlayback);
+
+
+static void ALCportPlayback_Construct(ALCportPlayback *self, ALCdevice *device)
+{
+    ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
+    SET_VTABLE2(ALCportPlayback, ALCbackend, self);
+
+    self->stream = NULL;
+}
+
+static void ALCportPlayback_Destruct(ALCportPlayback *self)
+{
+    if(self->stream)
+        Pa_CloseStream(self->stream);
+    self->stream = NULL;
+
+    ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
+}
+
+
+static int ALCportPlayback_WriteCallback(const void *UNUSED(inputBuffer), void *outputBuffer,
+    unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *UNUSED(timeInfo),
+    const PaStreamCallbackFlags UNUSED(statusFlags), void *userData)
+{
+    ALCportPlayback *self = userData;
+
+    aluMixData(STATIC_CAST(ALCbackend, self)->mDevice, outputBuffer, framesPerBuffer);
+    return 0;
+}
+
+
+static ALCenum ALCportPlayback_open(ALCportPlayback *self, const ALCchar *name)
+{
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+    PaError err;
+
+    if(!name)
+        name = pa_device;
+    else if(strcmp(name, pa_device) != 0)
+        return ALC_INVALID_VALUE;
+
+    self->update_size = device->UpdateSize;
+
+    self->params.device = -1;
+    if(!ConfigValueInt(NULL, "port", "device", &self->params.device) ||
+       self->params.device < 0)
+        self->params.device = Pa_GetDefaultOutputDevice();
+    self->params.suggestedLatency = (device->UpdateSize*device->NumUpdates) /
+                                    (float)device->Frequency;
+    self->params.hostApiSpecificStreamInfo = NULL;
+
+    self->params.channelCount = ((device->FmtChans == DevFmtMono) ? 1 : 2);
+
+    switch(device->FmtType)
+    {
+        case DevFmtByte:
+            self->params.sampleFormat = paInt8;
+            break;
+        case DevFmtUByte:
+            self->params.sampleFormat = paUInt8;
+            break;
+        case DevFmtUShort:
+            /* fall-through */
+        case DevFmtShort:
+            self->params.sampleFormat = paInt16;
+            break;
+        case DevFmtUInt:
+            /* fall-through */
+        case DevFmtInt:
+            self->params.sampleFormat = paInt32;
+            break;
+        case DevFmtFloat:
+            self->params.sampleFormat = paFloat32;
+            break;
+    }
+
+retry_open:
+    err = Pa_OpenStream(&self->stream, NULL, &self->params,
+        device->Frequency, device->UpdateSize, paNoFlag,
+        ALCportPlayback_WriteCallback, self
+    );
+    if(err != paNoError)
+    {
+        if(self->params.sampleFormat == paFloat32)
+        {
+            self->params.sampleFormat = paInt16;
+            goto retry_open;
+        }
+        ERR("Pa_OpenStream() returned an error: %s\n", Pa_GetErrorText(err));
+        return ALC_INVALID_VALUE;
+    }
+
+    al_string_copy_cstr(&device->DeviceName, name);
+
+    return ALC_NO_ERROR;
+
+}
+
+static void ALCportPlayback_close(ALCportPlayback *self)
+{
+    PaError err = Pa_CloseStream(self->stream);
+    if(err != paNoError)
+        ERR("Error closing stream: %s\n", Pa_GetErrorText(err));
+    self->stream = NULL;
+}
+
+static ALCboolean ALCportPlayback_reset(ALCportPlayback *self)
+{
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+    const PaStreamInfo *streamInfo;
+
+    streamInfo = Pa_GetStreamInfo(self->stream);
+    device->Frequency = streamInfo->sampleRate;
+    device->UpdateSize = self->update_size;
+
+    if(self->params.sampleFormat == paInt8)
+        device->FmtType = DevFmtByte;
+    else if(self->params.sampleFormat == paUInt8)
+        device->FmtType = DevFmtUByte;
+    else if(self->params.sampleFormat == paInt16)
+        device->FmtType = DevFmtShort;
+    else if(self->params.sampleFormat == paInt32)
+        device->FmtType = DevFmtInt;
+    else if(self->params.sampleFormat == paFloat32)
+        device->FmtType = DevFmtFloat;
+    else
+    {
+        ERR("Unexpected sample format: 0x%lx\n", self->params.sampleFormat);
+        return ALC_FALSE;
+    }
+
+    if(self->params.channelCount == 2)
+        device->FmtChans = DevFmtStereo;
+    else if(self->params.channelCount == 1)
+        device->FmtChans = DevFmtMono;
+    else
+    {
+        ERR("Unexpected channel count: %u\n", self->params.channelCount);
+        return ALC_FALSE;
+    }
+    SetDefaultChannelOrder(device);
+
+    return ALC_TRUE;
+}
+
+static ALCboolean ALCportPlayback_start(ALCportPlayback *self)
+{
+    PaError err;
+
+    err = Pa_StartStream(self->stream);
+    if(err != paNoError)
+    {
+        ERR("Pa_StartStream() returned an error: %s\n", Pa_GetErrorText(err));
+        return ALC_FALSE;
+    }
+
+    return ALC_TRUE;
+}
+
+static void ALCportPlayback_stop(ALCportPlayback *self)
+{
+    PaError err = Pa_StopStream(self->stream);
+    if(err != paNoError)
+        ERR("Error stopping stream: %s\n", Pa_GetErrorText(err));
+}
+
+
+typedef struct ALCportCapture {
+    DERIVE_FROM_TYPE(ALCbackend);
+
+    PaStream *stream;
+    PaStreamParameters params;
+
+    ll_ringbuffer_t *ring;
+} ALCportCapture;
+
+static int ALCportCapture_ReadCallback(const void *inputBuffer, void *outputBuffer,
+    unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo,
+    const PaStreamCallbackFlags statusFlags, void *userData);
+
+static void ALCportCapture_Construct(ALCportCapture *self, ALCdevice *device);
+static void ALCportCapture_Destruct(ALCportCapture *self);
+static ALCenum ALCportCapture_open(ALCportCapture *self, const ALCchar *name);
+static void ALCportCapture_close(ALCportCapture *self);
+static DECLARE_FORWARD(ALCportCapture, ALCbackend, ALCboolean, reset)
+static ALCboolean ALCportCapture_start(ALCportCapture *self);
+static void ALCportCapture_stop(ALCportCapture *self);
+static ALCenum ALCportCapture_captureSamples(ALCportCapture *self, ALCvoid *buffer, ALCuint samples);
+static ALCuint ALCportCapture_availableSamples(ALCportCapture *self);
+static DECLARE_FORWARD(ALCportCapture, ALCbackend, ClockLatency, getClockLatency)
+static DECLARE_FORWARD(ALCportCapture, ALCbackend, void, lock)
+static DECLARE_FORWARD(ALCportCapture, ALCbackend, void, unlock)
+DECLARE_DEFAULT_ALLOCATORS(ALCportCapture)
+
+DEFINE_ALCBACKEND_VTABLE(ALCportCapture);
+
+
+static void ALCportCapture_Construct(ALCportCapture *self, ALCdevice *device)
+{
+    ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
+    SET_VTABLE2(ALCportCapture, ALCbackend, self);
+
+    self->stream = NULL;
+}
+
+static void ALCportCapture_Destruct(ALCportCapture *self)
+{
+    if(self->stream)
+        Pa_CloseStream(self->stream);
+    self->stream = NULL;
+
+    if(self->ring)
+        ll_ringbuffer_free(self->ring);
+    self->ring = NULL;
+
+    ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
+}
+
+
+static int ALCportCapture_ReadCallback(const void *inputBuffer, void *UNUSED(outputBuffer),
+    unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *UNUSED(timeInfo),
+    const PaStreamCallbackFlags UNUSED(statusFlags), void *userData)
+{
+    ALCportCapture *self = userData;
+    size_t writable = ll_ringbuffer_write_space(self->ring);
+
+    if(framesPerBuffer > writable)
+        framesPerBuffer = writable;
+    ll_ringbuffer_write(self->ring, inputBuffer, framesPerBuffer);
+    return 0;
+}
+
+
+static ALCenum ALCportCapture_open(ALCportCapture *self, const ALCchar *name)
+{
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+    ALuint samples, frame_size;
+    PaError err;
+
+    if(!name)
+        name = pa_device;
+    else if(strcmp(name, pa_device) != 0)
+        return ALC_INVALID_VALUE;
+
+    samples = device->UpdateSize * device->NumUpdates;
+    samples = maxu(samples, 100 * device->Frequency / 1000);
+    frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
+
+    self->ring = ll_ringbuffer_create(samples, frame_size);
+    if(self->ring == NULL) return ALC_INVALID_VALUE;
+
+    self->params.device = -1;
+    if(!ConfigValueInt(NULL, "port", "capture", &self->params.device) ||
+       self->params.device < 0)
+        self->params.device = Pa_GetDefaultInputDevice();
+    self->params.suggestedLatency = 0.0f;
+    self->params.hostApiSpecificStreamInfo = NULL;
+
+    switch(device->FmtType)
+    {
+        case DevFmtByte:
+            self->params.sampleFormat = paInt8;
+            break;
+        case DevFmtUByte:
+            self->params.sampleFormat = paUInt8;
+            break;
+        case DevFmtShort:
+            self->params.sampleFormat = paInt16;
+            break;
+        case DevFmtInt:
+            self->params.sampleFormat = paInt32;
+            break;
+        case DevFmtFloat:
+            self->params.sampleFormat = paFloat32;
+            break;
+        case DevFmtUInt:
+        case DevFmtUShort:
+            ERR("%s samples not supported\n", DevFmtTypeString(device->FmtType));
+            return ALC_INVALID_VALUE;
+    }
+    self->params.channelCount = ChannelsFromDevFmt(device->FmtChans);
+
+    err = Pa_OpenStream(&self->stream, &self->params, NULL,
+        device->Frequency, paFramesPerBufferUnspecified, paNoFlag,
+        ALCportCapture_ReadCallback, self
+    );
+    if(err != paNoError)
+    {
+        ERR("Pa_OpenStream() returned an error: %s\n", Pa_GetErrorText(err));
+        return ALC_INVALID_VALUE;
+    }
+
+    al_string_copy_cstr(&device->DeviceName, name);
+
+    return ALC_NO_ERROR;
+}
+
+static void ALCportCapture_close(ALCportCapture *self)
+{
+    PaError err = Pa_CloseStream(self->stream);
+    if(err != paNoError)
+        ERR("Error closing stream: %s\n", Pa_GetErrorText(err));
+    self->stream = NULL;
+
+    ll_ringbuffer_free(self->ring);
+    self->ring = NULL;
+}
+
+
+static ALCboolean ALCportCapture_start(ALCportCapture *self)
+{
+    PaError err = Pa_StartStream(self->stream);
+    if(err != paNoError)
+    {
+        ERR("Error starting stream: %s\n", Pa_GetErrorText(err));
+        return ALC_FALSE;
+    }
+    return ALC_TRUE;
+}
+
+static void ALCportCapture_stop(ALCportCapture *self)
+{
+    PaError err = Pa_StopStream(self->stream);
+    if(err != paNoError)
+        ERR("Error stopping stream: %s\n", Pa_GetErrorText(err));
+}
+
+
+static ALCuint ALCportCapture_availableSamples(ALCportCapture *self)
+{
+    return ll_ringbuffer_read_space(self->ring);
+}
+
+static ALCenum ALCportCapture_captureSamples(ALCportCapture *self, ALCvoid *buffer, ALCuint samples)
+{
+    ll_ringbuffer_read(self->ring, buffer, samples);
+    return ALC_NO_ERROR;
+}
+
+
+typedef struct ALCportBackendFactory {
+    DERIVE_FROM_TYPE(ALCbackendFactory);
+} ALCportBackendFactory;
+#define ALCPORTBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCportBackendFactory, ALCbackendFactory) } }
+
+static ALCboolean ALCportBackendFactory_init(ALCportBackendFactory *self);
+static void ALCportBackendFactory_deinit(ALCportBackendFactory *self);
+static ALCboolean ALCportBackendFactory_querySupport(ALCportBackendFactory *self, ALCbackend_Type type);
+static void ALCportBackendFactory_probe(ALCportBackendFactory *self, enum DevProbe type);
+static ALCbackend* ALCportBackendFactory_createBackend(ALCportBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
+
+DEFINE_ALCBACKENDFACTORY_VTABLE(ALCportBackendFactory);
+
+
+static ALCboolean ALCportBackendFactory_init(ALCportBackendFactory* UNUSED(self))
+{
+    if(!pa_load())
+        return ALC_FALSE;
+    return ALC_TRUE;
+}
+
+static void ALCportBackendFactory_deinit(ALCportBackendFactory* UNUSED(self))
+{
+#ifdef HAVE_DYNLOAD
+    if(pa_handle)
+    {
+        Pa_Terminate();
+        CloseLib(pa_handle);
+        pa_handle = NULL;
+    }
+#else
+    Pa_Terminate();
+#endif
+}
+
+static ALCboolean ALCportBackendFactory_querySupport(ALCportBackendFactory* UNUSED(self), ALCbackend_Type type)
+{
+    if(type == ALCbackend_Playback || type == ALCbackend_Capture)
+        return ALC_TRUE;
+    return ALC_FALSE;
+}
+
+static void ALCportBackendFactory_probe(ALCportBackendFactory* UNUSED(self), enum DevProbe type)
+{
+    switch(type)
+    {
+        case ALL_DEVICE_PROBE:
+            AppendAllDevicesList(pa_device);
+            break;
+        case CAPTURE_DEVICE_PROBE:
+            AppendCaptureDeviceList(pa_device);
+            break;
+    }
+}
+
+static ALCbackend* ALCportBackendFactory_createBackend(ALCportBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
+{
+    if(type == ALCbackend_Playback)
+    {
+        ALCportPlayback *backend;
+        NEW_OBJ(backend, ALCportPlayback)(device);
+        if(!backend) return NULL;
+        return STATIC_CAST(ALCbackend, backend);
+    }
+    if(type == ALCbackend_Capture)
+    {
+        ALCportCapture *backend;
+        NEW_OBJ(backend, ALCportCapture)(device);
+        if(!backend) return NULL;
+        return STATIC_CAST(ALCbackend, backend);
+    }
+
+    return NULL;
+}
+
+ALCbackendFactory *ALCportBackendFactory_getFactory(void)
+{
+    static ALCportBackendFactory factory = ALCPORTBACKENDFACTORY_INITIALIZER;
+    return STATIC_CAST(ALCbackendFactory, &factory);
+}

+ 1847 - 0
Engine/lib/openal-soft/Alc/backends/pulseaudio.c

@@ -0,0 +1,1847 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 2009 by Konstantinos Natsakis <[email protected]>
+ * Copyright (C) 2010 by Chris Robinson <[email protected]>
+ * This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the
+ *  Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include "alMain.h"
+#include "alu.h"
+#include "threads.h"
+#include "compat.h"
+
+#include "backends/base.h"
+
+#include <pulse/pulseaudio.h>
+
+#if PA_API_VERSION == 12
+
+#ifdef HAVE_DYNLOAD
+static void *pa_handle;
+#define MAKE_FUNC(x) static __typeof(x) * p##x
+MAKE_FUNC(pa_context_unref);
+MAKE_FUNC(pa_sample_spec_valid);
+MAKE_FUNC(pa_frame_size);
+MAKE_FUNC(pa_stream_drop);
+MAKE_FUNC(pa_strerror);
+MAKE_FUNC(pa_context_get_state);
+MAKE_FUNC(pa_stream_get_state);
+MAKE_FUNC(pa_threaded_mainloop_signal);
+MAKE_FUNC(pa_stream_peek);
+MAKE_FUNC(pa_threaded_mainloop_wait);
+MAKE_FUNC(pa_threaded_mainloop_unlock);
+MAKE_FUNC(pa_threaded_mainloop_in_thread);
+MAKE_FUNC(pa_context_new);
+MAKE_FUNC(pa_threaded_mainloop_stop);
+MAKE_FUNC(pa_context_disconnect);
+MAKE_FUNC(pa_threaded_mainloop_start);
+MAKE_FUNC(pa_threaded_mainloop_get_api);
+MAKE_FUNC(pa_context_set_state_callback);
+MAKE_FUNC(pa_stream_write);
+MAKE_FUNC(pa_xfree);
+MAKE_FUNC(pa_stream_connect_record);
+MAKE_FUNC(pa_stream_connect_playback);
+MAKE_FUNC(pa_stream_readable_size);
+MAKE_FUNC(pa_stream_writable_size);
+MAKE_FUNC(pa_stream_is_corked);
+MAKE_FUNC(pa_stream_cork);
+MAKE_FUNC(pa_stream_is_suspended);
+MAKE_FUNC(pa_stream_get_device_name);
+MAKE_FUNC(pa_stream_get_latency);
+MAKE_FUNC(pa_path_get_filename);
+MAKE_FUNC(pa_get_binary_name);
+MAKE_FUNC(pa_threaded_mainloop_free);
+MAKE_FUNC(pa_context_errno);
+MAKE_FUNC(pa_xmalloc);
+MAKE_FUNC(pa_stream_unref);
+MAKE_FUNC(pa_threaded_mainloop_accept);
+MAKE_FUNC(pa_stream_set_write_callback);
+MAKE_FUNC(pa_threaded_mainloop_new);
+MAKE_FUNC(pa_context_connect);
+MAKE_FUNC(pa_stream_set_buffer_attr);
+MAKE_FUNC(pa_stream_get_buffer_attr);
+MAKE_FUNC(pa_stream_get_sample_spec);
+MAKE_FUNC(pa_stream_get_time);
+MAKE_FUNC(pa_stream_set_read_callback);
+MAKE_FUNC(pa_stream_set_state_callback);
+MAKE_FUNC(pa_stream_set_moved_callback);
+MAKE_FUNC(pa_stream_set_underflow_callback);
+MAKE_FUNC(pa_stream_new_with_proplist);
+MAKE_FUNC(pa_stream_disconnect);
+MAKE_FUNC(pa_threaded_mainloop_lock);
+MAKE_FUNC(pa_channel_map_init_auto);
+MAKE_FUNC(pa_channel_map_parse);
+MAKE_FUNC(pa_channel_map_snprint);
+MAKE_FUNC(pa_channel_map_equal);
+MAKE_FUNC(pa_context_get_server_info);
+MAKE_FUNC(pa_context_get_sink_info_by_name);
+MAKE_FUNC(pa_context_get_sink_info_list);
+MAKE_FUNC(pa_context_get_source_info_by_name);
+MAKE_FUNC(pa_context_get_source_info_list);
+MAKE_FUNC(pa_operation_get_state);
+MAKE_FUNC(pa_operation_unref);
+MAKE_FUNC(pa_proplist_new);
+MAKE_FUNC(pa_proplist_free);
+MAKE_FUNC(pa_proplist_set);
+MAKE_FUNC(pa_channel_map_superset);
+MAKE_FUNC(pa_stream_set_buffer_attr_callback);
+MAKE_FUNC(pa_stream_begin_write);
+#undef MAKE_FUNC
+
+#define pa_context_unref ppa_context_unref
+#define pa_sample_spec_valid ppa_sample_spec_valid
+#define pa_frame_size ppa_frame_size
+#define pa_stream_drop ppa_stream_drop
+#define pa_strerror ppa_strerror
+#define pa_context_get_state ppa_context_get_state
+#define pa_stream_get_state ppa_stream_get_state
+#define pa_threaded_mainloop_signal ppa_threaded_mainloop_signal
+#define pa_stream_peek ppa_stream_peek
+#define pa_threaded_mainloop_wait ppa_threaded_mainloop_wait
+#define pa_threaded_mainloop_unlock ppa_threaded_mainloop_unlock
+#define pa_threaded_mainloop_in_thread ppa_threaded_mainloop_in_thread
+#define pa_context_new ppa_context_new
+#define pa_threaded_mainloop_stop ppa_threaded_mainloop_stop
+#define pa_context_disconnect ppa_context_disconnect
+#define pa_threaded_mainloop_start ppa_threaded_mainloop_start
+#define pa_threaded_mainloop_get_api ppa_threaded_mainloop_get_api
+#define pa_context_set_state_callback ppa_context_set_state_callback
+#define pa_stream_write ppa_stream_write
+#define pa_xfree ppa_xfree
+#define pa_stream_connect_record ppa_stream_connect_record
+#define pa_stream_connect_playback ppa_stream_connect_playback
+#define pa_stream_readable_size ppa_stream_readable_size
+#define pa_stream_writable_size ppa_stream_writable_size
+#define pa_stream_is_corked ppa_stream_is_corked
+#define pa_stream_cork ppa_stream_cork
+#define pa_stream_is_suspended ppa_stream_is_suspended
+#define pa_stream_get_device_name ppa_stream_get_device_name
+#define pa_stream_get_latency ppa_stream_get_latency
+#define pa_path_get_filename ppa_path_get_filename
+#define pa_get_binary_name ppa_get_binary_name
+#define pa_threaded_mainloop_free ppa_threaded_mainloop_free
+#define pa_context_errno ppa_context_errno
+#define pa_xmalloc ppa_xmalloc
+#define pa_stream_unref ppa_stream_unref
+#define pa_threaded_mainloop_accept ppa_threaded_mainloop_accept
+#define pa_stream_set_write_callback ppa_stream_set_write_callback
+#define pa_threaded_mainloop_new ppa_threaded_mainloop_new
+#define pa_context_connect ppa_context_connect
+#define pa_stream_set_buffer_attr ppa_stream_set_buffer_attr
+#define pa_stream_get_buffer_attr ppa_stream_get_buffer_attr
+#define pa_stream_get_sample_spec ppa_stream_get_sample_spec
+#define pa_stream_get_time ppa_stream_get_time
+#define pa_stream_set_read_callback ppa_stream_set_read_callback
+#define pa_stream_set_state_callback ppa_stream_set_state_callback
+#define pa_stream_set_moved_callback ppa_stream_set_moved_callback
+#define pa_stream_set_underflow_callback ppa_stream_set_underflow_callback
+#define pa_stream_new_with_proplist ppa_stream_new_with_proplist
+#define pa_stream_disconnect ppa_stream_disconnect
+#define pa_threaded_mainloop_lock ppa_threaded_mainloop_lock
+#define pa_channel_map_init_auto ppa_channel_map_init_auto
+#define pa_channel_map_parse ppa_channel_map_parse
+#define pa_channel_map_snprint ppa_channel_map_snprint
+#define pa_channel_map_equal ppa_channel_map_equal
+#define pa_context_get_server_info ppa_context_get_server_info
+#define pa_context_get_sink_info_by_name ppa_context_get_sink_info_by_name
+#define pa_context_get_sink_info_list ppa_context_get_sink_info_list
+#define pa_context_get_source_info_by_name ppa_context_get_source_info_by_name
+#define pa_context_get_source_info_list ppa_context_get_source_info_list
+#define pa_operation_get_state ppa_operation_get_state
+#define pa_operation_unref ppa_operation_unref
+#define pa_proplist_new ppa_proplist_new
+#define pa_proplist_free ppa_proplist_free
+#define pa_proplist_set ppa_proplist_set
+#define pa_channel_map_superset ppa_channel_map_superset
+#define pa_stream_set_buffer_attr_callback ppa_stream_set_buffer_attr_callback
+#define pa_stream_begin_write ppa_stream_begin_write
+
+#endif
+
+static ALCboolean pulse_load(void)
+{
+    ALCboolean ret = ALC_TRUE;
+#ifdef HAVE_DYNLOAD
+    if(!pa_handle)
+    {
+#ifdef _WIN32
+#define PALIB "libpulse-0.dll"
+#elif defined(__APPLE__) && defined(__MACH__)
+#define PALIB "libpulse.0.dylib"
+#else
+#define PALIB "libpulse.so.0"
+#endif
+        pa_handle = LoadLib(PALIB);
+        if(!pa_handle)
+            return ALC_FALSE;
+
+#define LOAD_FUNC(x) do {                                                     \
+    p##x = GetSymbol(pa_handle, #x);                                          \
+    if(!(p##x)) {                                                             \
+        ret = ALC_FALSE;                                                      \
+    }                                                                         \
+} while(0)
+        LOAD_FUNC(pa_context_unref);
+        LOAD_FUNC(pa_sample_spec_valid);
+        LOAD_FUNC(pa_stream_drop);
+        LOAD_FUNC(pa_frame_size);
+        LOAD_FUNC(pa_strerror);
+        LOAD_FUNC(pa_context_get_state);
+        LOAD_FUNC(pa_stream_get_state);
+        LOAD_FUNC(pa_threaded_mainloop_signal);
+        LOAD_FUNC(pa_stream_peek);
+        LOAD_FUNC(pa_threaded_mainloop_wait);
+        LOAD_FUNC(pa_threaded_mainloop_unlock);
+        LOAD_FUNC(pa_threaded_mainloop_in_thread);
+        LOAD_FUNC(pa_context_new);
+        LOAD_FUNC(pa_threaded_mainloop_stop);
+        LOAD_FUNC(pa_context_disconnect);
+        LOAD_FUNC(pa_threaded_mainloop_start);
+        LOAD_FUNC(pa_threaded_mainloop_get_api);
+        LOAD_FUNC(pa_context_set_state_callback);
+        LOAD_FUNC(pa_stream_write);
+        LOAD_FUNC(pa_xfree);
+        LOAD_FUNC(pa_stream_connect_record);
+        LOAD_FUNC(pa_stream_connect_playback);
+        LOAD_FUNC(pa_stream_readable_size);
+        LOAD_FUNC(pa_stream_writable_size);
+        LOAD_FUNC(pa_stream_is_corked);
+        LOAD_FUNC(pa_stream_cork);
+        LOAD_FUNC(pa_stream_is_suspended);
+        LOAD_FUNC(pa_stream_get_device_name);
+        LOAD_FUNC(pa_stream_get_latency);
+        LOAD_FUNC(pa_path_get_filename);
+        LOAD_FUNC(pa_get_binary_name);
+        LOAD_FUNC(pa_threaded_mainloop_free);
+        LOAD_FUNC(pa_context_errno);
+        LOAD_FUNC(pa_xmalloc);
+        LOAD_FUNC(pa_stream_unref);
+        LOAD_FUNC(pa_threaded_mainloop_accept);
+        LOAD_FUNC(pa_stream_set_write_callback);
+        LOAD_FUNC(pa_threaded_mainloop_new);
+        LOAD_FUNC(pa_context_connect);
+        LOAD_FUNC(pa_stream_set_buffer_attr);
+        LOAD_FUNC(pa_stream_get_buffer_attr);
+        LOAD_FUNC(pa_stream_get_sample_spec);
+        LOAD_FUNC(pa_stream_get_time);
+        LOAD_FUNC(pa_stream_set_read_callback);
+        LOAD_FUNC(pa_stream_set_state_callback);
+        LOAD_FUNC(pa_stream_set_moved_callback);
+        LOAD_FUNC(pa_stream_set_underflow_callback);
+        LOAD_FUNC(pa_stream_new_with_proplist);
+        LOAD_FUNC(pa_stream_disconnect);
+        LOAD_FUNC(pa_threaded_mainloop_lock);
+        LOAD_FUNC(pa_channel_map_init_auto);
+        LOAD_FUNC(pa_channel_map_parse);
+        LOAD_FUNC(pa_channel_map_snprint);
+        LOAD_FUNC(pa_channel_map_equal);
+        LOAD_FUNC(pa_context_get_server_info);
+        LOAD_FUNC(pa_context_get_sink_info_by_name);
+        LOAD_FUNC(pa_context_get_sink_info_list);
+        LOAD_FUNC(pa_context_get_source_info_by_name);
+        LOAD_FUNC(pa_context_get_source_info_list);
+        LOAD_FUNC(pa_operation_get_state);
+        LOAD_FUNC(pa_operation_unref);
+        LOAD_FUNC(pa_proplist_new);
+        LOAD_FUNC(pa_proplist_free);
+        LOAD_FUNC(pa_proplist_set);
+        LOAD_FUNC(pa_channel_map_superset);
+        LOAD_FUNC(pa_stream_set_buffer_attr_callback);
+        LOAD_FUNC(pa_stream_begin_write);
+#undef LOAD_FUNC
+
+        if(ret == ALC_FALSE)
+        {
+            CloseLib(pa_handle);
+            pa_handle = NULL;
+        }
+    }
+#endif /* HAVE_DYNLOAD */
+    return ret;
+}
+
+
+/* Global flags and properties */
+static pa_context_flags_t pulse_ctx_flags;
+static pa_proplist *prop_filter;
+
+
+/* PulseAudio Event Callbacks */
+static void context_state_callback(pa_context *context, void *pdata)
+{
+    pa_threaded_mainloop *loop = pdata;
+    pa_context_state_t state;
+
+    state = pa_context_get_state(context);
+    if(state == PA_CONTEXT_READY || !PA_CONTEXT_IS_GOOD(state))
+        pa_threaded_mainloop_signal(loop, 0);
+}
+
+static void stream_state_callback(pa_stream *stream, void *pdata)
+{
+    pa_threaded_mainloop *loop = pdata;
+    pa_stream_state_t state;
+
+    state = pa_stream_get_state(stream);
+    if(state == PA_STREAM_READY || !PA_STREAM_IS_GOOD(state))
+        pa_threaded_mainloop_signal(loop, 0);
+}
+
+static void stream_success_callback(pa_stream *UNUSED(stream), int UNUSED(success), void *pdata)
+{
+    pa_threaded_mainloop *loop = pdata;
+    pa_threaded_mainloop_signal(loop, 0);
+}
+
+static void wait_for_operation(pa_operation *op, pa_threaded_mainloop *loop)
+{
+    if(op)
+    {
+        while(pa_operation_get_state(op) == PA_OPERATION_RUNNING)
+            pa_threaded_mainloop_wait(loop);
+        pa_operation_unref(op);
+    }
+}
+
+
+static pa_context *connect_context(pa_threaded_mainloop *loop, ALboolean silent)
+{
+    const char *name = "OpenAL Soft";
+    char path_name[PATH_MAX];
+    pa_context_state_t state;
+    pa_context *context;
+    int err;
+
+    if(pa_get_binary_name(path_name, sizeof(path_name)))
+        name = pa_path_get_filename(path_name);
+
+    context = pa_context_new(pa_threaded_mainloop_get_api(loop), name);
+    if(!context)
+    {
+        ERR("pa_context_new() failed\n");
+        return NULL;
+    }
+
+    pa_context_set_state_callback(context, context_state_callback, loop);
+
+    if((err=pa_context_connect(context, NULL, pulse_ctx_flags, NULL)) >= 0)
+    {
+        while((state=pa_context_get_state(context)) != PA_CONTEXT_READY)
+        {
+            if(!PA_CONTEXT_IS_GOOD(state))
+            {
+                err = pa_context_errno(context);
+                if(err > 0)  err = -err;
+                break;
+            }
+
+            pa_threaded_mainloop_wait(loop);
+        }
+    }
+    pa_context_set_state_callback(context, NULL, NULL);
+
+    if(err < 0)
+    {
+        if(!silent)
+            ERR("Context did not connect: %s\n", pa_strerror(err));
+        pa_context_unref(context);
+        return NULL;
+    }
+
+    return context;
+}
+
+
+static ALCboolean pulse_open(pa_threaded_mainloop **loop, pa_context **context,
+                             void(*state_cb)(pa_context*,void*), void *ptr)
+{
+    if(!(*loop = pa_threaded_mainloop_new()))
+    {
+        ERR("pa_threaded_mainloop_new() failed!\n");
+        return ALC_FALSE;
+    }
+    if(pa_threaded_mainloop_start(*loop) < 0)
+    {
+        ERR("pa_threaded_mainloop_start() failed\n");
+        goto error;
+    }
+
+    pa_threaded_mainloop_lock(*loop);
+
+    *context = connect_context(*loop, AL_FALSE);
+    if(!*context)
+    {
+        pa_threaded_mainloop_unlock(*loop);
+        pa_threaded_mainloop_stop(*loop);
+        goto error;
+    }
+    pa_context_set_state_callback(*context, state_cb, ptr);
+
+    pa_threaded_mainloop_unlock(*loop);
+    return ALC_TRUE;
+
+error:
+    pa_threaded_mainloop_free(*loop);
+    *loop = NULL;
+
+    return ALC_FALSE;
+}
+
+static void pulse_close(pa_threaded_mainloop *loop, pa_context *context, pa_stream *stream)
+{
+    pa_threaded_mainloop_lock(loop);
+
+    if(stream)
+    {
+        pa_stream_set_state_callback(stream, NULL, NULL);
+        pa_stream_set_moved_callback(stream, NULL, NULL);
+        pa_stream_set_write_callback(stream, NULL, NULL);
+        pa_stream_set_buffer_attr_callback(stream, NULL, NULL);
+        pa_stream_disconnect(stream);
+        pa_stream_unref(stream);
+    }
+
+    pa_context_disconnect(context);
+    pa_context_unref(context);
+
+    pa_threaded_mainloop_unlock(loop);
+
+    pa_threaded_mainloop_stop(loop);
+    pa_threaded_mainloop_free(loop);
+}
+
+
+typedef struct {
+    al_string name;
+    al_string device_name;
+} DevMap;
+TYPEDEF_VECTOR(DevMap, vector_DevMap)
+
+static vector_DevMap PlaybackDevices;
+static vector_DevMap CaptureDevices;
+
+static void clear_devlist(vector_DevMap *list)
+{
+#define DEINIT_STRS(i)  (AL_STRING_DEINIT((i)->name),AL_STRING_DEINIT((i)->device_name))
+    VECTOR_FOR_EACH(DevMap, *list, DEINIT_STRS);
+#undef DEINIT_STRS
+    VECTOR_RESIZE(*list, 0, 0);
+}
+
+
+typedef struct ALCpulsePlayback {
+    DERIVE_FROM_TYPE(ALCbackend);
+
+    al_string device_name;
+
+    pa_buffer_attr attr;
+    pa_sample_spec spec;
+
+    pa_threaded_mainloop *loop;
+
+    pa_stream *stream;
+    pa_context *context;
+
+    volatile ALboolean killNow;
+    althrd_t thread;
+} ALCpulsePlayback;
+
+static void ALCpulsePlayback_deviceCallback(pa_context *context, const pa_sink_info *info, int eol, void *pdata);
+static void ALCpulsePlayback_probeDevices(void);
+
+static void ALCpulsePlayback_bufferAttrCallback(pa_stream *stream, void *pdata);
+static void ALCpulsePlayback_contextStateCallback(pa_context *context, void *pdata);
+static void ALCpulsePlayback_streamStateCallback(pa_stream *stream, void *pdata);
+static void ALCpulsePlayback_streamWriteCallback(pa_stream *p, size_t nbytes, void *userdata);
+static void ALCpulsePlayback_sinkInfoCallback(pa_context *context, const pa_sink_info *info, int eol, void *pdata);
+static void ALCpulsePlayback_sinkNameCallback(pa_context *context, const pa_sink_info *info, int eol, void *pdata);
+static void ALCpulsePlayback_streamMovedCallback(pa_stream *stream, void *pdata);
+static pa_stream *ALCpulsePlayback_connectStream(const char *device_name, pa_threaded_mainloop *loop,
+                                                 pa_context *context, pa_stream_flags_t flags,
+                                                 pa_buffer_attr *attr, pa_sample_spec *spec,
+                                                 pa_channel_map *chanmap);
+static int ALCpulsePlayback_mixerProc(void *ptr);
+
+static void ALCpulsePlayback_Construct(ALCpulsePlayback *self, ALCdevice *device);
+static void ALCpulsePlayback_Destruct(ALCpulsePlayback *self);
+static ALCenum ALCpulsePlayback_open(ALCpulsePlayback *self, const ALCchar *name);
+static void ALCpulsePlayback_close(ALCpulsePlayback *self);
+static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self);
+static ALCboolean ALCpulsePlayback_start(ALCpulsePlayback *self);
+static void ALCpulsePlayback_stop(ALCpulsePlayback *self);
+static DECLARE_FORWARD2(ALCpulsePlayback, ALCbackend, ALCenum, captureSamples, ALCvoid*, ALCuint)
+static DECLARE_FORWARD(ALCpulsePlayback, ALCbackend, ALCuint, availableSamples)
+static ClockLatency ALCpulsePlayback_getClockLatency(ALCpulsePlayback *self);
+static void ALCpulsePlayback_lock(ALCpulsePlayback *self);
+static void ALCpulsePlayback_unlock(ALCpulsePlayback *self);
+DECLARE_DEFAULT_ALLOCATORS(ALCpulsePlayback)
+
+DEFINE_ALCBACKEND_VTABLE(ALCpulsePlayback);
+
+
+static void ALCpulsePlayback_Construct(ALCpulsePlayback *self, ALCdevice *device)
+{
+    ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
+    SET_VTABLE2(ALCpulsePlayback, ALCbackend, self);
+
+    AL_STRING_INIT(self->device_name);
+}
+
+static void ALCpulsePlayback_Destruct(ALCpulsePlayback *self)
+{
+    AL_STRING_DEINIT(self->device_name);
+    ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
+}
+
+
+static void ALCpulsePlayback_deviceCallback(pa_context *UNUSED(context), const pa_sink_info *info, int eol, void *pdata)
+{
+    pa_threaded_mainloop *loop = pdata;
+    const DevMap *iter;
+    DevMap entry;
+    int count;
+
+    if(eol)
+    {
+        pa_threaded_mainloop_signal(loop, 0);
+        return;
+    }
+
+#define MATCH_INFO_NAME(iter) (al_string_cmp_cstr((iter)->device_name, info->name) == 0)
+    VECTOR_FIND_IF(iter, const DevMap, PlaybackDevices, MATCH_INFO_NAME);
+    if(iter != VECTOR_END(PlaybackDevices)) return;
+#undef MATCH_INFO_NAME
+
+    AL_STRING_INIT(entry.name);
+    AL_STRING_INIT(entry.device_name);
+
+    al_string_copy_cstr(&entry.device_name, info->name);
+
+    count = 0;
+    while(1)
+    {
+        al_string_copy_cstr(&entry.name, info->description);
+        if(count != 0)
+        {
+            char str[64];
+            snprintf(str, sizeof(str), " #%d", count+1);
+            al_string_append_cstr(&entry.name, str);
+        }
+
+#define MATCH_ENTRY(i) (al_string_cmp(entry.name, (i)->name) == 0)
+        VECTOR_FIND_IF(iter, const DevMap, PlaybackDevices, MATCH_ENTRY);
+        if(iter == VECTOR_END(PlaybackDevices)) break;
+#undef MATCH_ENTRY
+        count++;
+    }
+
+    TRACE("Got device \"%s\", \"%s\"\n", al_string_get_cstr(entry.name), al_string_get_cstr(entry.device_name));
+
+    VECTOR_PUSH_BACK(PlaybackDevices, entry);
+}
+
+static void ALCpulsePlayback_probeDevices(void)
+{
+    pa_threaded_mainloop *loop;
+
+    clear_devlist(&PlaybackDevices);
+
+    if((loop=pa_threaded_mainloop_new()) &&
+       pa_threaded_mainloop_start(loop) >= 0)
+    {
+        pa_context *context;
+
+        pa_threaded_mainloop_lock(loop);
+        context = connect_context(loop, AL_FALSE);
+        if(context)
+        {
+            pa_operation *o;
+            pa_stream_flags_t flags;
+            pa_sample_spec spec;
+            pa_stream *stream;
+
+            flags = PA_STREAM_FIX_FORMAT | PA_STREAM_FIX_RATE |
+                    PA_STREAM_FIX_CHANNELS | PA_STREAM_DONT_MOVE;
+
+            spec.format = PA_SAMPLE_S16NE;
+            spec.rate = 44100;
+            spec.channels = 2;
+
+            stream = ALCpulsePlayback_connectStream(NULL, loop, context, flags,
+                                                    NULL, &spec, NULL);
+            if(stream)
+            {
+                o = pa_context_get_sink_info_by_name(context, pa_stream_get_device_name(stream),
+                                                     ALCpulsePlayback_deviceCallback, loop);
+                wait_for_operation(o, loop);
+
+                pa_stream_disconnect(stream);
+                pa_stream_unref(stream);
+                stream = NULL;
+            }
+
+            o = pa_context_get_sink_info_list(context, ALCpulsePlayback_deviceCallback, loop);
+            wait_for_operation(o, loop);
+
+            pa_context_disconnect(context);
+            pa_context_unref(context);
+        }
+        pa_threaded_mainloop_unlock(loop);
+        pa_threaded_mainloop_stop(loop);
+    }
+    if(loop)
+        pa_threaded_mainloop_free(loop);
+}
+
+
+static void ALCpulsePlayback_bufferAttrCallback(pa_stream *stream, void *pdata)
+{
+    ALCpulsePlayback *self = pdata;
+
+    self->attr = *pa_stream_get_buffer_attr(stream);
+    TRACE("minreq=%d, tlength=%d, prebuf=%d\n", self->attr.minreq, self->attr.tlength, self->attr.prebuf);
+}
+
+static void ALCpulsePlayback_contextStateCallback(pa_context *context, void *pdata)
+{
+    ALCpulsePlayback *self = pdata;
+    if(pa_context_get_state(context) == PA_CONTEXT_FAILED)
+    {
+        ERR("Received context failure!\n");
+        aluHandleDisconnect(STATIC_CAST(ALCbackend,self)->mDevice);
+    }
+    pa_threaded_mainloop_signal(self->loop, 0);
+}
+
+static void ALCpulsePlayback_streamStateCallback(pa_stream *stream, void *pdata)
+{
+    ALCpulsePlayback *self = pdata;
+    if(pa_stream_get_state(stream) == PA_STREAM_FAILED)
+    {
+        ERR("Received stream failure!\n");
+        aluHandleDisconnect(STATIC_CAST(ALCbackend,self)->mDevice);
+    }
+    pa_threaded_mainloop_signal(self->loop, 0);
+}
+
+static void ALCpulsePlayback_streamWriteCallback(pa_stream* UNUSED(p), size_t UNUSED(nbytes), void *pdata)
+{
+    ALCpulsePlayback *self = pdata;
+    pa_threaded_mainloop_signal(self->loop, 0);
+}
+
+static void ALCpulsePlayback_sinkInfoCallback(pa_context *UNUSED(context), const pa_sink_info *info, int eol, void *pdata)
+{
+    static const struct {
+        enum DevFmtChannels chans;
+        pa_channel_map map;
+    } chanmaps[] = {
+        { DevFmtX71, { 8, {
+            PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
+            PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE,
+            PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
+            PA_CHANNEL_POSITION_SIDE_LEFT, PA_CHANNEL_POSITION_SIDE_RIGHT
+        } } },
+        { DevFmtX61, { 7, {
+            PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
+            PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE,
+            PA_CHANNEL_POSITION_REAR_CENTER,
+            PA_CHANNEL_POSITION_SIDE_LEFT, PA_CHANNEL_POSITION_SIDE_RIGHT
+        } } },
+        { DevFmtX51, { 6, {
+            PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
+            PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE,
+            PA_CHANNEL_POSITION_SIDE_LEFT, PA_CHANNEL_POSITION_SIDE_RIGHT
+        } } },
+        { DevFmtX51Rear, { 6, {
+            PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
+            PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE,
+            PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT
+        } } },
+        { DevFmtQuad, { 4, {
+            PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
+            PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT
+        } } },
+        { DevFmtStereo, { 2, {
+            PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT
+        } } },
+        { DevFmtMono, { 1, {PA_CHANNEL_POSITION_MONO} } }
+    };
+    ALCpulsePlayback *self = pdata;
+    ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
+    size_t i;
+
+    if(eol)
+    {
+        pa_threaded_mainloop_signal(self->loop, 0);
+        return;
+    }
+
+    for(i = 0;i < COUNTOF(chanmaps);i++)
+    {
+        if(pa_channel_map_superset(&info->channel_map, &chanmaps[i].map))
+        {
+            if(!(device->Flags&DEVICE_CHANNELS_REQUEST))
+                device->FmtChans = chanmaps[i].chans;
+            break;
+        }
+    }
+    if(i == COUNTOF(chanmaps))
+    {
+        char chanmap_str[PA_CHANNEL_MAP_SNPRINT_MAX] = "";
+        pa_channel_map_snprint(chanmap_str, sizeof(chanmap_str), &info->channel_map);
+        WARN("Failed to find format for channel map:\n    %s\n", chanmap_str);
+    }
+
+    if(info->active_port)
+        TRACE("Active port: %s (%s)\n", info->active_port->name, info->active_port->description);
+    device->IsHeadphones = (info->active_port &&
+                            strcmp(info->active_port->name, "analog-output-headphones") == 0 &&
+                            device->FmtChans == DevFmtStereo);
+}
+
+static void ALCpulsePlayback_sinkNameCallback(pa_context *UNUSED(context), const pa_sink_info *info, int eol, void *pdata)
+{
+    ALCpulsePlayback *self = pdata;
+    ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
+
+    if(eol)
+    {
+        pa_threaded_mainloop_signal(self->loop, 0);
+        return;
+    }
+
+    al_string_copy_cstr(&device->DeviceName, info->description);
+}
+
+
+static void ALCpulsePlayback_streamMovedCallback(pa_stream *stream, void *pdata)
+{
+    ALCpulsePlayback *self = pdata;
+
+    al_string_copy_cstr(&self->device_name, pa_stream_get_device_name(stream));
+
+    TRACE("Stream moved to %s\n", al_string_get_cstr(self->device_name));
+}
+
+
+static pa_stream *ALCpulsePlayback_connectStream(const char *device_name,
+    pa_threaded_mainloop *loop, pa_context *context,
+    pa_stream_flags_t flags, pa_buffer_attr *attr, pa_sample_spec *spec,
+    pa_channel_map *chanmap)
+{
+    pa_stream_state_t state;
+    pa_stream *stream;
+
+    stream = pa_stream_new_with_proplist(context, "Playback Stream", spec, chanmap, prop_filter);
+    if(!stream)
+    {
+        ERR("pa_stream_new_with_proplist() failed: %s\n", pa_strerror(pa_context_errno(context)));
+        return NULL;
+    }
+
+    pa_stream_set_state_callback(stream, stream_state_callback, loop);
+
+    if(pa_stream_connect_playback(stream, device_name, attr, flags, NULL, NULL) < 0)
+    {
+        ERR("Stream did not connect: %s\n", pa_strerror(pa_context_errno(context)));
+        pa_stream_unref(stream);
+        return NULL;
+    }
+
+    while((state=pa_stream_get_state(stream)) != PA_STREAM_READY)
+    {
+        if(!PA_STREAM_IS_GOOD(state))
+        {
+            ERR("Stream did not get ready: %s\n", pa_strerror(pa_context_errno(context)));
+            pa_stream_unref(stream);
+            return NULL;
+        }
+
+        pa_threaded_mainloop_wait(loop);
+    }
+    pa_stream_set_state_callback(stream, NULL, NULL);
+
+    return stream;
+}
+
+
+static int ALCpulsePlayback_mixerProc(void *ptr)
+{
+    ALCpulsePlayback *self = ptr;
+    ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
+    ALuint buffer_size;
+    ALint update_size;
+    size_t frame_size;
+    ssize_t len;
+
+    SetRTPriority();
+    althrd_setname(althrd_current(), MIXER_THREAD_NAME);
+
+    pa_threaded_mainloop_lock(self->loop);
+    frame_size = pa_frame_size(&self->spec);
+    update_size = device->UpdateSize * frame_size;
+
+    /* Sanitize buffer metrics, in case we actually have less than what we
+     * asked for. */
+    buffer_size = minu(update_size*device->NumUpdates, self->attr.tlength);
+    update_size = minu(update_size, buffer_size/2);
+    do {
+        len = pa_stream_writable_size(self->stream) - self->attr.tlength +
+              buffer_size;
+        if(len < update_size)
+        {
+            if(pa_stream_is_corked(self->stream) == 1)
+            {
+                pa_operation *o;
+                o = pa_stream_cork(self->stream, 0, NULL, NULL);
+                if(o) pa_operation_unref(o);
+            }
+            pa_threaded_mainloop_wait(self->loop);
+            continue;
+        }
+        len -= len%update_size;
+
+        while(len > 0)
+        {
+            size_t newlen = len;
+            void *buf;
+            pa_free_cb_t free_func = NULL;
+
+            if(pa_stream_begin_write(self->stream, &buf, &newlen) < 0)
+            {
+                buf = pa_xmalloc(newlen);
+                free_func = pa_xfree;
+            }
+
+            aluMixData(device, buf, newlen/frame_size);
+
+            pa_stream_write(self->stream, buf, newlen, free_func, 0, PA_SEEK_RELATIVE);
+            len -= newlen;
+        }
+    } while(!self->killNow && device->Connected);
+    pa_threaded_mainloop_unlock(self->loop);
+
+    return 0;
+}
+
+
+static ALCenum ALCpulsePlayback_open(ALCpulsePlayback *self, const ALCchar *name)
+{
+    const_al_string dev_name = AL_STRING_INIT_STATIC();
+    const char *pulse_name = NULL;
+    pa_stream_flags_t flags;
+    pa_sample_spec spec;
+
+    if(name)
+    {
+        const DevMap *iter;
+
+        if(VECTOR_SIZE(PlaybackDevices) == 0)
+            ALCpulsePlayback_probeDevices();
+
+#define MATCH_NAME(iter) (al_string_cmp_cstr((iter)->name, name) == 0)
+        VECTOR_FIND_IF(iter, const DevMap, PlaybackDevices, MATCH_NAME);
+#undef MATCH_NAME
+        if(iter == VECTOR_END(PlaybackDevices))
+            return ALC_INVALID_VALUE;
+        pulse_name = al_string_get_cstr(iter->device_name);
+        dev_name = iter->name;
+    }
+
+    if(!pulse_open(&self->loop, &self->context, ALCpulsePlayback_contextStateCallback, self))
+        return ALC_INVALID_VALUE;
+
+    pa_threaded_mainloop_lock(self->loop);
+
+    flags = PA_STREAM_FIX_FORMAT | PA_STREAM_FIX_RATE |
+            PA_STREAM_FIX_CHANNELS;
+    if(!GetConfigValueBool(NULL, "pulse", "allow-moves", 0))
+        flags |= PA_STREAM_DONT_MOVE;
+
+    spec.format = PA_SAMPLE_S16NE;
+    spec.rate = 44100;
+    spec.channels = 2;
+
+    TRACE("Connecting to \"%s\"\n", pulse_name ? pulse_name : "(default)");
+    self->stream = ALCpulsePlayback_connectStream(pulse_name, self->loop, self->context,
+                                                  flags, NULL, &spec, NULL);
+    if(!self->stream)
+    {
+        pa_threaded_mainloop_unlock(self->loop);
+        pulse_close(self->loop, self->context, self->stream);
+        self->loop = NULL;
+        self->context = NULL;
+        return ALC_INVALID_VALUE;
+    }
+    pa_stream_set_moved_callback(self->stream, ALCpulsePlayback_streamMovedCallback, self);
+
+    al_string_copy_cstr(&self->device_name, pa_stream_get_device_name(self->stream));
+    if(al_string_empty(dev_name))
+    {
+        pa_operation *o = pa_context_get_sink_info_by_name(
+            self->context, al_string_get_cstr(self->device_name),
+            ALCpulsePlayback_sinkNameCallback, self
+        );
+        wait_for_operation(o, self->loop);
+    }
+    else
+    {
+        ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
+        al_string_copy(&device->DeviceName, dev_name);
+    }
+
+    pa_threaded_mainloop_unlock(self->loop);
+
+    return ALC_NO_ERROR;
+}
+
+static void ALCpulsePlayback_close(ALCpulsePlayback *self)
+{
+    pulse_close(self->loop, self->context, self->stream);
+    self->loop = NULL;
+    self->context = NULL;
+    self->stream = NULL;
+
+    al_string_clear(&self->device_name);
+}
+
+static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self)
+{
+    ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
+    pa_stream_flags_t flags = 0;
+    const char *mapname = NULL;
+    pa_channel_map chanmap;
+    pa_operation *o;
+    ALuint len;
+
+    pa_threaded_mainloop_lock(self->loop);
+
+    if(self->stream)
+    {
+        pa_stream_set_state_callback(self->stream, NULL, NULL);
+        pa_stream_set_moved_callback(self->stream, NULL, NULL);
+        pa_stream_set_write_callback(self->stream, NULL, NULL);
+        pa_stream_set_buffer_attr_callback(self->stream, NULL, NULL);
+        pa_stream_disconnect(self->stream);
+        pa_stream_unref(self->stream);
+        self->stream = NULL;
+    }
+
+    o = pa_context_get_sink_info_by_name(self->context, al_string_get_cstr(self->device_name),
+                                         ALCpulsePlayback_sinkInfoCallback, self);
+    wait_for_operation(o, self->loop);
+
+    if(GetConfigValueBool(al_string_get_cstr(device->DeviceName), "pulse", "fix-rate", 0) ||
+       !(device->Flags&DEVICE_FREQUENCY_REQUEST))
+        flags |= PA_STREAM_FIX_RATE;
+    flags |= PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE;
+    flags |= PA_STREAM_ADJUST_LATENCY;
+    flags |= PA_STREAM_START_CORKED;
+    if(!GetConfigValueBool(NULL, "pulse", "allow-moves", 0))
+        flags |= PA_STREAM_DONT_MOVE;
+
+    switch(device->FmtType)
+    {
+        case DevFmtByte:
+            device->FmtType = DevFmtUByte;
+            /* fall-through */
+        case DevFmtUByte:
+            self->spec.format = PA_SAMPLE_U8;
+            break;
+        case DevFmtUShort:
+            device->FmtType = DevFmtShort;
+            /* fall-through */
+        case DevFmtShort:
+            self->spec.format = PA_SAMPLE_S16NE;
+            break;
+        case DevFmtUInt:
+            device->FmtType = DevFmtInt;
+            /* fall-through */
+        case DevFmtInt:
+            self->spec.format = PA_SAMPLE_S32NE;
+            break;
+        case DevFmtFloat:
+            self->spec.format = PA_SAMPLE_FLOAT32NE;
+            break;
+    }
+    self->spec.rate = device->Frequency;
+    self->spec.channels = ChannelsFromDevFmt(device->FmtChans);
+
+    if(pa_sample_spec_valid(&self->spec) == 0)
+    {
+        ERR("Invalid sample format\n");
+        pa_threaded_mainloop_unlock(self->loop);
+        return ALC_FALSE;
+    }
+
+    switch(device->FmtChans)
+    {
+        case DevFmtMono:
+            mapname = "mono";
+            break;
+        case DevFmtAmbi1:
+        case DevFmtAmbi2:
+        case DevFmtAmbi3:
+            device->FmtChans = DevFmtStereo;
+            /*fall-through*/
+        case DevFmtStereo:
+            mapname = "front-left,front-right";
+            break;
+        case DevFmtQuad:
+            mapname = "front-left,front-right,rear-left,rear-right";
+            break;
+        case DevFmtX51:
+            mapname = "front-left,front-right,front-center,lfe,side-left,side-right";
+            break;
+        case DevFmtX51Rear:
+            mapname = "front-left,front-right,front-center,lfe,rear-left,rear-right";
+            break;
+        case DevFmtX61:
+            mapname = "front-left,front-right,front-center,lfe,rear-center,side-left,side-right";
+            break;
+        case DevFmtX71:
+            mapname = "front-left,front-right,front-center,lfe,rear-left,rear-right,side-left,side-right";
+            break;
+    }
+    if(!pa_channel_map_parse(&chanmap, mapname))
+    {
+        ERR("Failed to build channel map for %s\n", DevFmtChannelsString(device->FmtChans));
+        pa_threaded_mainloop_unlock(self->loop);
+        return ALC_FALSE;
+    }
+    SetDefaultWFXChannelOrder(device);
+
+    self->attr.fragsize = -1;
+    self->attr.prebuf = 0;
+    self->attr.minreq = device->UpdateSize * pa_frame_size(&self->spec);
+    self->attr.tlength = self->attr.minreq * maxu(device->NumUpdates, 2);
+    self->attr.maxlength = -1;
+
+    self->stream = ALCpulsePlayback_connectStream(al_string_get_cstr(self->device_name),
+                                                  self->loop, self->context, flags,
+                                                  &self->attr, &self->spec, &chanmap);
+    if(!self->stream)
+    {
+        pa_threaded_mainloop_unlock(self->loop);
+        return ALC_FALSE;
+    }
+    pa_stream_set_state_callback(self->stream, ALCpulsePlayback_streamStateCallback, self);
+    pa_stream_set_moved_callback(self->stream, ALCpulsePlayback_streamMovedCallback, self);
+    pa_stream_set_write_callback(self->stream, ALCpulsePlayback_streamWriteCallback, self);
+
+    self->spec = *(pa_stream_get_sample_spec(self->stream));
+    if(device->Frequency != self->spec.rate)
+    {
+        /* Server updated our playback rate, so modify the buffer attribs
+         * accordingly. */
+        device->NumUpdates = (ALuint)clampd(
+            (ALdouble)device->NumUpdates/device->Frequency*self->spec.rate + 0.5, 2.0, 16.0
+        );
+
+        self->attr.minreq  = device->UpdateSize * pa_frame_size(&self->spec);
+        self->attr.tlength = self->attr.minreq * device->NumUpdates;
+        self->attr.maxlength = -1;
+        self->attr.prebuf  = 0;
+
+        o = pa_stream_set_buffer_attr(self->stream, &self->attr,
+                                      stream_success_callback, self->loop);
+        wait_for_operation(o, self->loop);
+
+        device->Frequency = self->spec.rate;
+    }
+
+    pa_stream_set_buffer_attr_callback(self->stream, ALCpulsePlayback_bufferAttrCallback, self);
+    ALCpulsePlayback_bufferAttrCallback(self->stream, self);
+
+    len = self->attr.minreq / pa_frame_size(&self->spec);
+    device->NumUpdates = (ALuint)clampd(
+        (ALdouble)device->NumUpdates/len*device->UpdateSize + 0.5, 2.0, 16.0
+    );
+    device->UpdateSize = len;
+
+    /* HACK: prebuf should be 0 as that's what we set it to. However on some
+     * systems it comes back as non-0, so we have to make sure the device will
+     * write enough audio to start playback. The lack of manual start control
+     * may have unintended consequences, but it's better than not starting at
+     * all.
+     */
+    if(self->attr.prebuf != 0)
+    {
+        len = self->attr.prebuf / pa_frame_size(&self->spec);
+        if(len <= device->UpdateSize*device->NumUpdates)
+            ERR("Non-0 prebuf, %u samples (%u bytes), device has %u samples\n",
+                len, self->attr.prebuf, device->UpdateSize*device->NumUpdates);
+        else
+        {
+            ERR("Large prebuf, %u samples (%u bytes), increasing device from %u samples",
+                len, self->attr.prebuf, device->UpdateSize*device->NumUpdates);
+            device->NumUpdates = (len+device->UpdateSize-1) / device->UpdateSize;
+        }
+    }
+
+    pa_threaded_mainloop_unlock(self->loop);
+    return ALC_TRUE;
+}
+
+static ALCboolean ALCpulsePlayback_start(ALCpulsePlayback *self)
+{
+    self->killNow = AL_FALSE;
+    if(althrd_create(&self->thread, ALCpulsePlayback_mixerProc, self) != althrd_success)
+        return ALC_FALSE;
+    return ALC_TRUE;
+}
+
+static void ALCpulsePlayback_stop(ALCpulsePlayback *self)
+{
+    pa_operation *o;
+    int res;
+
+    if(!self->stream || self->killNow)
+        return;
+
+    self->killNow = AL_TRUE;
+    /* Signal the main loop in case PulseAudio isn't sending us audio requests
+     * (e.g. if the device is suspended). We need to lock the mainloop in case
+     * the mixer is between checking the killNow flag but before waiting for
+     * the signal.
+     */
+    pa_threaded_mainloop_lock(self->loop);
+    pa_threaded_mainloop_unlock(self->loop);
+    pa_threaded_mainloop_signal(self->loop, 0);
+    althrd_join(self->thread, &res);
+
+    pa_threaded_mainloop_lock(self->loop);
+
+    o = pa_stream_cork(self->stream, 1, stream_success_callback, self->loop);
+    wait_for_operation(o, self->loop);
+
+    pa_threaded_mainloop_unlock(self->loop);
+}
+
+
+static ClockLatency ALCpulsePlayback_getClockLatency(ALCpulsePlayback *self)
+{
+    pa_usec_t latency = 0;
+    ClockLatency ret;
+    int neg, err;
+
+    pa_threaded_mainloop_lock(self->loop);
+    ret.ClockTime = GetDeviceClockTime(STATIC_CAST(ALCbackend,self)->mDevice);
+    if((err=pa_stream_get_latency(self->stream, &latency, &neg)) != 0)
+    {
+        /* FIXME: if err = -PA_ERR_NODATA, it means we were called too soon
+         * after starting the stream and no timing info has been received from
+         * the server yet. Should we wait, possibly stalling the app, or give a
+         * dummy value? Either way, it shouldn't be 0. */
+        if(err != -PA_ERR_NODATA)
+            ERR("Failed to get stream latency: 0x%x\n", err);
+        latency = 0;
+        neg = 0;
+    }
+    if(neg) latency = 0;
+    ret.Latency = minu64(latency, U64(0xffffffffffffffff)/1000) * 1000;
+    pa_threaded_mainloop_unlock(self->loop);
+
+    return ret;
+}
+
+
+static void ALCpulsePlayback_lock(ALCpulsePlayback *self)
+{
+    pa_threaded_mainloop_lock(self->loop);
+}
+
+static void ALCpulsePlayback_unlock(ALCpulsePlayback *self)
+{
+    pa_threaded_mainloop_unlock(self->loop);
+}
+
+
+typedef struct ALCpulseCapture {
+    DERIVE_FROM_TYPE(ALCbackend);
+
+    al_string device_name;
+
+    const void *cap_store;
+    size_t cap_len;
+    size_t cap_remain;
+
+    ALCuint last_readable;
+
+    pa_buffer_attr attr;
+    pa_sample_spec spec;
+
+    pa_threaded_mainloop *loop;
+
+    pa_stream *stream;
+    pa_context *context;
+} ALCpulseCapture;
+
+static void ALCpulseCapture_deviceCallback(pa_context *context, const pa_source_info *info, int eol, void *pdata);
+static void ALCpulseCapture_probeDevices(void);
+
+static void ALCpulseCapture_contextStateCallback(pa_context *context, void *pdata);
+static void ALCpulseCapture_streamStateCallback(pa_stream *stream, void *pdata);
+static void ALCpulseCapture_sourceNameCallback(pa_context *context, const pa_source_info *info, int eol, void *pdata);
+static void ALCpulseCapture_streamMovedCallback(pa_stream *stream, void *pdata);
+static pa_stream *ALCpulseCapture_connectStream(const char *device_name,
+                                                pa_threaded_mainloop *loop, pa_context *context,
+                                                pa_stream_flags_t flags, pa_buffer_attr *attr,
+                                                pa_sample_spec *spec, pa_channel_map *chanmap);
+
+static void ALCpulseCapture_Construct(ALCpulseCapture *self, ALCdevice *device);
+static void ALCpulseCapture_Destruct(ALCpulseCapture *self);
+static ALCenum ALCpulseCapture_open(ALCpulseCapture *self, const ALCchar *name);
+static void ALCpulseCapture_close(ALCpulseCapture *self);
+static DECLARE_FORWARD(ALCpulseCapture, ALCbackend, ALCboolean, reset)
+static ALCboolean ALCpulseCapture_start(ALCpulseCapture *self);
+static void ALCpulseCapture_stop(ALCpulseCapture *self);
+static ALCenum ALCpulseCapture_captureSamples(ALCpulseCapture *self, ALCvoid *buffer, ALCuint samples);
+static ALCuint ALCpulseCapture_availableSamples(ALCpulseCapture *self);
+static ClockLatency ALCpulseCapture_getClockLatency(ALCpulseCapture *self);
+static void ALCpulseCapture_lock(ALCpulseCapture *self);
+static void ALCpulseCapture_unlock(ALCpulseCapture *self);
+DECLARE_DEFAULT_ALLOCATORS(ALCpulseCapture)
+
+DEFINE_ALCBACKEND_VTABLE(ALCpulseCapture);
+
+
+static void ALCpulseCapture_Construct(ALCpulseCapture *self, ALCdevice *device)
+{
+    ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
+    SET_VTABLE2(ALCpulseCapture, ALCbackend, self);
+
+    AL_STRING_INIT(self->device_name);
+}
+
+static void ALCpulseCapture_Destruct(ALCpulseCapture *self)
+{
+    AL_STRING_DEINIT(self->device_name);
+    ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
+}
+
+
+static void ALCpulseCapture_deviceCallback(pa_context *UNUSED(context), const pa_source_info *info, int eol, void *pdata)
+{
+    pa_threaded_mainloop *loop = pdata;
+    const DevMap *iter;
+    DevMap entry;
+    int count;
+
+    if(eol)
+    {
+        pa_threaded_mainloop_signal(loop, 0);
+        return;
+    }
+
+#define MATCH_INFO_NAME(iter) (al_string_cmp_cstr((iter)->device_name, info->name) == 0)
+    VECTOR_FIND_IF(iter, const DevMap, CaptureDevices, MATCH_INFO_NAME);
+    if(iter != VECTOR_END(CaptureDevices)) return;
+#undef MATCH_INFO_NAME
+
+    AL_STRING_INIT(entry.name);
+    AL_STRING_INIT(entry.device_name);
+
+    al_string_copy_cstr(&entry.device_name, info->name);
+
+    count = 0;
+    while(1)
+    {
+        al_string_copy_cstr(&entry.name, info->description);
+        if(count != 0)
+        {
+            char str[64];
+            snprintf(str, sizeof(str), " #%d", count+1);
+            al_string_append_cstr(&entry.name, str);
+        }
+
+#define MATCH_ENTRY(i) (al_string_cmp(entry.name, (i)->name) == 0)
+        VECTOR_FIND_IF(iter, const DevMap, CaptureDevices, MATCH_ENTRY);
+        if(iter == VECTOR_END(CaptureDevices)) break;
+#undef MATCH_ENTRY
+        count++;
+    }
+
+    TRACE("Got device \"%s\", \"%s\"\n", al_string_get_cstr(entry.name), al_string_get_cstr(entry.device_name));
+
+    VECTOR_PUSH_BACK(CaptureDevices, entry);
+}
+
+static void ALCpulseCapture_probeDevices(void)
+{
+    pa_threaded_mainloop *loop;
+
+    clear_devlist(&CaptureDevices);
+
+    if((loop=pa_threaded_mainloop_new()) &&
+       pa_threaded_mainloop_start(loop) >= 0)
+    {
+        pa_context *context;
+
+        pa_threaded_mainloop_lock(loop);
+        context = connect_context(loop, AL_FALSE);
+        if(context)
+        {
+            pa_operation *o;
+            pa_stream_flags_t flags;
+            pa_sample_spec spec;
+            pa_stream *stream;
+
+            flags = PA_STREAM_FIX_FORMAT | PA_STREAM_FIX_RATE |
+                    PA_STREAM_FIX_CHANNELS | PA_STREAM_DONT_MOVE;
+
+            spec.format = PA_SAMPLE_S16NE;
+            spec.rate = 44100;
+            spec.channels = 1;
+
+            stream = ALCpulseCapture_connectStream(NULL, loop, context, flags,
+                                                   NULL, &spec, NULL);
+            if(stream)
+            {
+                o = pa_context_get_source_info_by_name(context, pa_stream_get_device_name(stream),
+                                                       ALCpulseCapture_deviceCallback, loop);
+                wait_for_operation(o, loop);
+
+                pa_stream_disconnect(stream);
+                pa_stream_unref(stream);
+                stream = NULL;
+            }
+
+            o = pa_context_get_source_info_list(context, ALCpulseCapture_deviceCallback, loop);
+            wait_for_operation(o, loop);
+
+            pa_context_disconnect(context);
+            pa_context_unref(context);
+        }
+        pa_threaded_mainloop_unlock(loop);
+        pa_threaded_mainloop_stop(loop);
+    }
+    if(loop)
+        pa_threaded_mainloop_free(loop);
+}
+
+
+static void ALCpulseCapture_contextStateCallback(pa_context *context, void *pdata)
+{
+    ALCpulseCapture *self = pdata;
+    if(pa_context_get_state(context) == PA_CONTEXT_FAILED)
+    {
+        ERR("Received context failure!\n");
+        aluHandleDisconnect(STATIC_CAST(ALCbackend,self)->mDevice);
+    }
+    pa_threaded_mainloop_signal(self->loop, 0);
+}
+
+static void ALCpulseCapture_streamStateCallback(pa_stream *stream, void *pdata)
+{
+    ALCpulseCapture *self = pdata;
+    if(pa_stream_get_state(stream) == PA_STREAM_FAILED)
+    {
+        ERR("Received stream failure!\n");
+        aluHandleDisconnect(STATIC_CAST(ALCbackend,self)->mDevice);
+    }
+    pa_threaded_mainloop_signal(self->loop, 0);
+}
+
+
+static void ALCpulseCapture_sourceNameCallback(pa_context *UNUSED(context), const pa_source_info *info, int eol, void *pdata)
+{
+    ALCpulseCapture *self = pdata;
+    ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
+
+    if(eol)
+    {
+        pa_threaded_mainloop_signal(self->loop, 0);
+        return;
+    }
+
+    al_string_copy_cstr(&device->DeviceName, info->description);
+}
+
+
+static void ALCpulseCapture_streamMovedCallback(pa_stream *stream, void *pdata)
+{
+    ALCpulseCapture *self = pdata;
+
+    al_string_copy_cstr(&self->device_name, pa_stream_get_device_name(stream));
+
+    TRACE("Stream moved to %s\n", al_string_get_cstr(self->device_name));
+}
+
+
+static pa_stream *ALCpulseCapture_connectStream(const char *device_name,
+    pa_threaded_mainloop *loop, pa_context *context,
+    pa_stream_flags_t flags, pa_buffer_attr *attr, pa_sample_spec *spec,
+    pa_channel_map *chanmap)
+{
+    pa_stream_state_t state;
+    pa_stream *stream;
+
+    stream = pa_stream_new_with_proplist(context, "Capture Stream", spec, chanmap, prop_filter);
+    if(!stream)
+    {
+        ERR("pa_stream_new_with_proplist() failed: %s\n", pa_strerror(pa_context_errno(context)));
+        return NULL;
+    }
+
+    pa_stream_set_state_callback(stream, stream_state_callback, loop);
+
+    if(pa_stream_connect_record(stream, device_name, attr, flags) < 0)
+    {
+        ERR("Stream did not connect: %s\n", pa_strerror(pa_context_errno(context)));
+        pa_stream_unref(stream);
+        return NULL;
+    }
+
+    while((state=pa_stream_get_state(stream)) != PA_STREAM_READY)
+    {
+        if(!PA_STREAM_IS_GOOD(state))
+        {
+            ERR("Stream did not get ready: %s\n", pa_strerror(pa_context_errno(context)));
+            pa_stream_unref(stream);
+            return NULL;
+        }
+
+        pa_threaded_mainloop_wait(loop);
+    }
+    pa_stream_set_state_callback(stream, NULL, NULL);
+
+    return stream;
+}
+
+
+static ALCenum ALCpulseCapture_open(ALCpulseCapture *self, const ALCchar *name)
+{
+    ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
+    const char *pulse_name = NULL;
+    pa_stream_flags_t flags = 0;
+    pa_channel_map chanmap;
+    ALuint samples;
+
+    if(name)
+    {
+        const DevMap *iter;
+
+        if(VECTOR_SIZE(CaptureDevices) == 0)
+            ALCpulseCapture_probeDevices();
+
+#define MATCH_NAME(iter) (al_string_cmp_cstr((iter)->name, name) == 0)
+        VECTOR_FIND_IF(iter, const DevMap, CaptureDevices, MATCH_NAME);
+#undef MATCH_NAME
+        if(iter == VECTOR_END(CaptureDevices))
+            return ALC_INVALID_VALUE;
+        pulse_name = al_string_get_cstr(iter->device_name);
+        al_string_copy(&device->DeviceName, iter->name);
+    }
+
+    if(!pulse_open(&self->loop, &self->context, ALCpulseCapture_contextStateCallback, self))
+        return ALC_INVALID_VALUE;
+
+    pa_threaded_mainloop_lock(self->loop);
+
+    self->spec.rate = device->Frequency;
+    self->spec.channels = ChannelsFromDevFmt(device->FmtChans);
+
+    switch(device->FmtType)
+    {
+        case DevFmtUByte:
+            self->spec.format = PA_SAMPLE_U8;
+            break;
+        case DevFmtShort:
+            self->spec.format = PA_SAMPLE_S16NE;
+            break;
+        case DevFmtInt:
+            self->spec.format = PA_SAMPLE_S32NE;
+            break;
+        case DevFmtFloat:
+            self->spec.format = PA_SAMPLE_FLOAT32NE;
+            break;
+        case DevFmtByte:
+        case DevFmtUShort:
+        case DevFmtUInt:
+            ERR("%s capture samples not supported\n", DevFmtTypeString(device->FmtType));
+            pa_threaded_mainloop_unlock(self->loop);
+            goto fail;
+    }
+
+    if(pa_sample_spec_valid(&self->spec) == 0)
+    {
+        ERR("Invalid sample format\n");
+        pa_threaded_mainloop_unlock(self->loop);
+        goto fail;
+    }
+
+    if(!pa_channel_map_init_auto(&chanmap, self->spec.channels, PA_CHANNEL_MAP_WAVEEX))
+    {
+        ERR("Couldn't build map for channel count (%d)!\n", self->spec.channels);
+        pa_threaded_mainloop_unlock(self->loop);
+        goto fail;
+    }
+
+    samples = device->UpdateSize * device->NumUpdates;
+    samples = maxu(samples, 100 * device->Frequency / 1000);
+
+    self->attr.minreq = -1;
+    self->attr.prebuf = -1;
+    self->attr.maxlength = samples * pa_frame_size(&self->spec);
+    self->attr.tlength = -1;
+    self->attr.fragsize = minu(samples, 50*device->Frequency/1000) *
+                          pa_frame_size(&self->spec);
+
+    flags |= PA_STREAM_START_CORKED|PA_STREAM_ADJUST_LATENCY;
+    if(!GetConfigValueBool(NULL, "pulse", "allow-moves", 0))
+        flags |= PA_STREAM_DONT_MOVE;
+
+    TRACE("Connecting to \"%s\"\n", pulse_name ? pulse_name : "(default)");
+    self->stream = ALCpulseCapture_connectStream(pulse_name, self->loop, self->context,
+                                                 flags, &self->attr, &self->spec,
+                                                 &chanmap);
+    if(!self->stream)
+    {
+        pa_threaded_mainloop_unlock(self->loop);
+        goto fail;
+    }
+    pa_stream_set_moved_callback(self->stream, ALCpulseCapture_streamMovedCallback, self);
+    pa_stream_set_state_callback(self->stream, ALCpulseCapture_streamStateCallback, self);
+
+    al_string_copy_cstr(&self->device_name, pa_stream_get_device_name(self->stream));
+    if(al_string_empty(device->DeviceName))
+    {
+        pa_operation *o = pa_context_get_source_info_by_name(
+            self->context, al_string_get_cstr(self->device_name),
+            ALCpulseCapture_sourceNameCallback, self
+        );
+        wait_for_operation(o, self->loop);
+    }
+
+    pa_threaded_mainloop_unlock(self->loop);
+    return ALC_NO_ERROR;
+
+fail:
+    pulse_close(self->loop, self->context, self->stream);
+    self->loop = NULL;
+    self->context = NULL;
+    self->stream = NULL;
+
+    return ALC_INVALID_VALUE;
+}
+
+static void ALCpulseCapture_close(ALCpulseCapture *self)
+{
+    pulse_close(self->loop, self->context, self->stream);
+    self->loop = NULL;
+    self->context = NULL;
+    self->stream = NULL;
+
+    al_string_clear(&self->device_name);
+}
+
+static ALCboolean ALCpulseCapture_start(ALCpulseCapture *self)
+{
+    pa_operation *o;
+    o = pa_stream_cork(self->stream, 0, stream_success_callback, self->loop);
+    wait_for_operation(o, self->loop);
+
+    return ALC_TRUE;
+}
+
+static void ALCpulseCapture_stop(ALCpulseCapture *self)
+{
+    pa_operation *o;
+    o = pa_stream_cork(self->stream, 1, stream_success_callback, self->loop);
+    wait_for_operation(o, self->loop);
+}
+
+static ALCenum ALCpulseCapture_captureSamples(ALCpulseCapture *self, ALCvoid *buffer, ALCuint samples)
+{
+    ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
+    ALCuint todo = samples * pa_frame_size(&self->spec);
+
+    /* Capture is done in fragment-sized chunks, so we loop until we get all
+     * that's available */
+    self->last_readable -= todo;
+    while(todo > 0)
+    {
+        size_t rem = todo;
+
+        if(self->cap_len == 0)
+        {
+            pa_stream_state_t state;
+
+            state = pa_stream_get_state(self->stream);
+            if(!PA_STREAM_IS_GOOD(state))
+            {
+                aluHandleDisconnect(device);
+                break;
+            }
+            if(pa_stream_peek(self->stream, &self->cap_store, &self->cap_len) < 0)
+            {
+                ERR("pa_stream_peek() failed: %s\n",
+                    pa_strerror(pa_context_errno(self->context)));
+                aluHandleDisconnect(device);
+                break;
+            }
+            self->cap_remain = self->cap_len;
+        }
+        if(rem > self->cap_remain)
+            rem = self->cap_remain;
+
+        memcpy(buffer, self->cap_store, rem);
+
+        buffer = (ALbyte*)buffer + rem;
+        todo -= rem;
+
+        self->cap_store = (ALbyte*)self->cap_store + rem;
+        self->cap_remain -= rem;
+        if(self->cap_remain == 0)
+        {
+            pa_stream_drop(self->stream);
+            self->cap_len = 0;
+        }
+    }
+    if(todo > 0)
+        memset(buffer, ((device->FmtType==DevFmtUByte) ? 0x80 : 0), todo);
+
+    return ALC_NO_ERROR;
+}
+
+static ALCuint ALCpulseCapture_availableSamples(ALCpulseCapture *self)
+{
+    ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
+    size_t readable = self->cap_remain;
+
+    if(device->Connected)
+    {
+        ssize_t got = pa_stream_readable_size(self->stream);
+        if(got < 0)
+        {
+            ERR("pa_stream_readable_size() failed: %s\n", pa_strerror(got));
+            aluHandleDisconnect(device);
+        }
+        else if((size_t)got > self->cap_len)
+            readable += got - self->cap_len;
+    }
+
+    if(self->last_readable < readable)
+        self->last_readable = readable;
+    return self->last_readable / pa_frame_size(&self->spec);
+}
+
+
+static ClockLatency ALCpulseCapture_getClockLatency(ALCpulseCapture *self)
+{
+    pa_usec_t latency = 0;
+    ClockLatency ret;
+    int neg, err;
+
+    pa_threaded_mainloop_lock(self->loop);
+    ret.ClockTime = GetDeviceClockTime(STATIC_CAST(ALCbackend,self)->mDevice);
+    if((err=pa_stream_get_latency(self->stream, &latency, &neg)) != 0)
+    {
+        ERR("Failed to get stream latency: 0x%x\n", err);
+        latency = 0;
+        neg = 0;
+    }
+    if(neg) latency = 0;
+    ret.Latency = minu64(latency, U64(0xffffffffffffffff)/1000) * 1000;
+    pa_threaded_mainloop_unlock(self->loop);
+
+    return ret;
+}
+
+
+static void ALCpulseCapture_lock(ALCpulseCapture *self)
+{
+    pa_threaded_mainloop_lock(self->loop);
+}
+
+static void ALCpulseCapture_unlock(ALCpulseCapture *self)
+{
+    pa_threaded_mainloop_unlock(self->loop);
+}
+
+
+typedef struct ALCpulseBackendFactory {
+    DERIVE_FROM_TYPE(ALCbackendFactory);
+} ALCpulseBackendFactory;
+#define ALCPULSEBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCpulseBackendFactory, ALCbackendFactory) } }
+
+static ALCboolean ALCpulseBackendFactory_init(ALCpulseBackendFactory *self);
+static void ALCpulseBackendFactory_deinit(ALCpulseBackendFactory *self);
+static ALCboolean ALCpulseBackendFactory_querySupport(ALCpulseBackendFactory *self, ALCbackend_Type type);
+static void ALCpulseBackendFactory_probe(ALCpulseBackendFactory *self, enum DevProbe type);
+static ALCbackend* ALCpulseBackendFactory_createBackend(ALCpulseBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
+
+DEFINE_ALCBACKENDFACTORY_VTABLE(ALCpulseBackendFactory);
+
+
+static ALCboolean ALCpulseBackendFactory_init(ALCpulseBackendFactory* UNUSED(self))
+{
+    ALCboolean ret = ALC_FALSE;
+
+    VECTOR_INIT(PlaybackDevices);
+    VECTOR_INIT(CaptureDevices);
+
+    if(pulse_load())
+    {
+        pa_threaded_mainloop *loop;
+
+        pulse_ctx_flags = 0;
+        if(!GetConfigValueBool(NULL, "pulse", "spawn-server", 1))
+            pulse_ctx_flags |= PA_CONTEXT_NOAUTOSPAWN;
+
+        if((loop=pa_threaded_mainloop_new()) &&
+           pa_threaded_mainloop_start(loop) >= 0)
+        {
+            pa_context *context;
+
+            pa_threaded_mainloop_lock(loop);
+            context = connect_context(loop, AL_TRUE);
+            if(context)
+            {
+                ret = ALC_TRUE;
+
+                /* Some libraries (Phonon, Qt) set some pulseaudio properties
+                 * through environment variables, which causes all streams in
+                 * the process to inherit them. This attempts to filter those
+                 * properties out by setting them to 0-length data. */
+                prop_filter = pa_proplist_new();
+                pa_proplist_set(prop_filter, PA_PROP_MEDIA_ROLE, NULL, 0);
+                pa_proplist_set(prop_filter, "phonon.streamid", NULL, 0);
+
+                pa_context_disconnect(context);
+                pa_context_unref(context);
+            }
+            pa_threaded_mainloop_unlock(loop);
+            pa_threaded_mainloop_stop(loop);
+        }
+        if(loop)
+            pa_threaded_mainloop_free(loop);
+    }
+
+    return ret;
+}
+
+static void ALCpulseBackendFactory_deinit(ALCpulseBackendFactory* UNUSED(self))
+{
+    clear_devlist(&PlaybackDevices);
+    VECTOR_DEINIT(PlaybackDevices);
+
+    clear_devlist(&CaptureDevices);
+    VECTOR_DEINIT(CaptureDevices);
+
+    if(prop_filter)
+        pa_proplist_free(prop_filter);
+    prop_filter = NULL;
+
+    /* PulseAudio doesn't like being CloseLib'd sometimes */
+}
+
+static ALCboolean ALCpulseBackendFactory_querySupport(ALCpulseBackendFactory* UNUSED(self), ALCbackend_Type type)
+{
+    if(type == ALCbackend_Playback || type == ALCbackend_Capture)
+        return ALC_TRUE;
+    return ALC_FALSE;
+}
+
+static void ALCpulseBackendFactory_probe(ALCpulseBackendFactory* UNUSED(self), enum DevProbe type)
+{
+    switch(type)
+    {
+        case ALL_DEVICE_PROBE:
+            ALCpulsePlayback_probeDevices();
+#define APPEND_ALL_DEVICES_LIST(e)  AppendAllDevicesList(al_string_get_cstr((e)->name))
+            VECTOR_FOR_EACH(const DevMap, PlaybackDevices, APPEND_ALL_DEVICES_LIST);
+#undef APPEND_ALL_DEVICES_LIST
+            break;
+
+        case CAPTURE_DEVICE_PROBE:
+            ALCpulseCapture_probeDevices();
+#define APPEND_CAPTURE_DEVICE_LIST(e) AppendCaptureDeviceList(al_string_get_cstr((e)->name))
+            VECTOR_FOR_EACH(const DevMap, CaptureDevices, APPEND_CAPTURE_DEVICE_LIST);
+#undef APPEND_CAPTURE_DEVICE_LIST
+            break;
+    }
+}
+
+static ALCbackend* ALCpulseBackendFactory_createBackend(ALCpulseBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
+{
+    if(type == ALCbackend_Playback)
+    {
+        ALCpulsePlayback *backend;
+        NEW_OBJ(backend, ALCpulsePlayback)(device);
+        if(!backend) return NULL;
+        return STATIC_CAST(ALCbackend, backend);
+    }
+    if(type == ALCbackend_Capture)
+    {
+        ALCpulseCapture *backend;
+        NEW_OBJ(backend, ALCpulseCapture)(device);
+        if(!backend) return NULL;
+        return STATIC_CAST(ALCbackend, backend);
+    }
+
+    return NULL;
+}
+
+
+#else /* PA_API_VERSION == 12 */
+
+#warning "Unsupported API version, backend will be unavailable!"
+
+typedef struct ALCpulseBackendFactory {
+    DERIVE_FROM_TYPE(ALCbackendFactory);
+} ALCpulseBackendFactory;
+#define ALCPULSEBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCpulseBackendFactory, ALCbackendFactory) } }
+
+static ALCboolean ALCpulseBackendFactory_init(ALCpulseBackendFactory* UNUSED(self))
+{
+    return ALC_FALSE;
+}
+
+static void ALCpulseBackendFactory_deinit(ALCpulseBackendFactory* UNUSED(self))
+{
+}
+
+static ALCboolean ALCpulseBackendFactory_querySupport(ALCpulseBackendFactory* UNUSED(self), ALCbackend_Type UNUSED(type))
+{
+    return ALC_FALSE;
+}
+
+static void ALCpulseBackendFactory_probe(ALCpulseBackendFactory* UNUSED(self), enum DevProbe UNUSED(type))
+{
+}
+
+static ALCbackend* ALCpulseBackendFactory_createBackend(ALCpulseBackendFactory* UNUSED(self), ALCdevice* UNUSED(device), ALCbackend_Type UNUSED(type))
+{
+    return NULL;
+}
+
+DEFINE_ALCBACKENDFACTORY_VTABLE(ALCpulseBackendFactory);
+
+#endif /* PA_API_VERSION == 12 */
+
+ALCbackendFactory *ALCpulseBackendFactory_getFactory(void)
+{
+    static ALCpulseBackendFactory factory = ALCPULSEBACKENDFACTORY_INITIALIZER;
+    return STATIC_CAST(ALCbackendFactory, &factory);
+}

+ 916 - 0
Engine/lib/openal-soft/Alc/backends/qsa.c

@@ -0,0 +1,916 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 2011-2013 by authors.
+ * This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the
+ *  Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <sched.h>
+#include <errno.h>
+#include <memory.h>
+#include <sys/select.h>
+#include <sys/asoundlib.h>
+#include <sys/neutrino.h>
+
+#include "alMain.h"
+#include "alu.h"
+#include "threads.h"
+
+
+typedef struct {
+    snd_pcm_t* pcmHandle;
+    int audio_fd;
+
+    snd_pcm_channel_setup_t  csetup;
+    snd_pcm_channel_params_t cparams;
+
+    ALvoid* buffer;
+    ALsizei size;
+
+    volatile int killNow;
+    althrd_t thread;
+} qsa_data;
+
+typedef struct {
+    ALCchar* name;
+    int card;
+    int dev;
+} DevMap;
+TYPEDEF_VECTOR(DevMap, vector_DevMap)
+
+static vector_DevMap DeviceNameMap;
+static vector_DevMap CaptureNameMap;
+
+static const ALCchar qsaDevice[] = "QSA Default";
+
+static const struct {
+    int32_t format;
+} formatlist[] = {
+    {SND_PCM_SFMT_FLOAT_LE},
+    {SND_PCM_SFMT_S32_LE},
+    {SND_PCM_SFMT_U32_LE},
+    {SND_PCM_SFMT_S16_LE},
+    {SND_PCM_SFMT_U16_LE},
+    {SND_PCM_SFMT_S8},
+    {SND_PCM_SFMT_U8},
+    {0},
+};
+
+static const struct {
+    int32_t rate;
+} ratelist[] = {
+    {192000},
+    {176400},
+    {96000},
+    {88200},
+    {48000},
+    {44100},
+    {32000},
+    {24000},
+    {22050},
+    {16000},
+    {12000},
+    {11025},
+    {8000},
+    {0},
+};
+
+static const struct {
+    int32_t channels;
+} channellist[] = {
+    {8},
+    {7},
+    {6},
+    {4},
+    {2},
+    {1},
+    {0},
+};
+
+static void deviceList(int type, vector_DevMap *devmap)
+{
+    snd_ctl_t* handle;
+    snd_pcm_info_t pcminfo;
+    int max_cards, card, err, dev;
+    DevMap entry;
+    char name[1024];
+    struct snd_ctl_hw_info info;
+
+    max_cards = snd_cards();
+    if(max_cards < 0)
+        return;
+
+    VECTOR_RESIZE(*devmap, 0, max_cards+1);
+
+    entry.name = strdup(qsaDevice);
+    entry.card = 0;
+    entry.dev = 0;
+    VECTOR_PUSH_BACK(*devmap, entry);
+
+    for(card = 0;card < max_cards;card++)
+    {
+        if((err=snd_ctl_open(&handle, card)) < 0)
+            continue;
+
+        if((err=snd_ctl_hw_info(handle, &info)) < 0)
+        {
+            snd_ctl_close(handle);
+            continue;
+        }
+
+        for(dev = 0;dev < (int)info.pcmdevs;dev++)
+        {
+            if((err=snd_ctl_pcm_info(handle, dev, &pcminfo)) < 0)
+                continue;
+
+            if((type==SND_PCM_CHANNEL_PLAYBACK && (pcminfo.flags&SND_PCM_INFO_PLAYBACK)) ||
+               (type==SND_PCM_CHANNEL_CAPTURE && (pcminfo.flags&SND_PCM_INFO_CAPTURE)))
+            {
+                snprintf(name, sizeof(name), "%s [%s] (hw:%d,%d)", info.name, pcminfo.name, card, dev);
+                entry.name = strdup(name);
+                entry.card = card;
+                entry.dev = dev;
+
+                VECTOR_PUSH_BACK(*devmap, entry);
+                TRACE("Got device \"%s\", card %d, dev %d\n", name, card, dev);
+            }
+        }
+        snd_ctl_close(handle);
+    }
+}
+
+
+FORCE_ALIGN static int qsa_proc_playback(void* ptr)
+{
+    ALCdevice* device=(ALCdevice*)ptr;
+    qsa_data* data=(qsa_data*)device->ExtraData;
+    char* write_ptr;
+    int avail;
+    snd_pcm_channel_status_t status;
+    struct sched_param param;
+    fd_set wfds;
+    int selectret;
+    struct timeval timeout;
+
+    SetRTPriority();
+    althrd_setname(althrd_current(), MIXER_THREAD_NAME);
+
+    /* Increase default 10 priority to 11 to avoid jerky sound */
+    SchedGet(0, 0, &param);
+    param.sched_priority=param.sched_curpriority+1;
+    SchedSet(0, 0, SCHED_NOCHANGE, &param);
+
+    ALint frame_size=FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
+
+    while (!data->killNow)
+    {
+        ALint len=data->size;
+        write_ptr=data->buffer;
+
+        avail=len/frame_size;
+        aluMixData(device, write_ptr, avail);
+
+        while (len>0 && !data->killNow)
+        {
+            FD_ZERO(&wfds);
+            FD_SET(data->audio_fd, &wfds);
+            timeout.tv_sec=2;
+            timeout.tv_usec=0;
+
+            /* Select also works like time slice to OS */
+            selectret=select(data->audio_fd+1, NULL, &wfds, NULL, &timeout);
+            switch (selectret)
+            {
+                case -1:
+                     aluHandleDisconnect(device);
+                     return 1;
+                case 0:
+                     break;
+                default:
+                     if (FD_ISSET(data->audio_fd, &wfds))
+                     {
+                         break;
+                     }
+                     break;
+            }
+
+            int wrote=snd_pcm_plugin_write(data->pcmHandle, write_ptr, len);
+
+            if (wrote<=0)
+            {
+                if ((errno==EAGAIN) || (errno==EWOULDBLOCK))
+                {
+                    continue;
+                }
+
+                memset(&status, 0, sizeof (status));
+                status.channel=SND_PCM_CHANNEL_PLAYBACK;
+
+                snd_pcm_plugin_status(data->pcmHandle, &status);
+
+                /* we need to reinitialize the sound channel if we've underrun the buffer */
+                if ((status.status==SND_PCM_STATUS_UNDERRUN) ||
+                    (status.status==SND_PCM_STATUS_READY))
+                {
+                    if ((snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_PLAYBACK))<0)
+                    {
+                        aluHandleDisconnect(device);
+                        break;
+                    }
+                }
+            }
+            else
+            {
+                write_ptr+=wrote;
+                len-=wrote;
+            }
+        }
+    }
+
+    return 0;
+}
+
+/************/
+/* Playback */
+/************/
+
+static ALCenum qsa_open_playback(ALCdevice* device, const ALCchar* deviceName)
+{
+    qsa_data *data;
+    int card, dev;
+    int status;
+
+    data = (qsa_data*)calloc(1, sizeof(qsa_data));
+    if(data == NULL)
+        return ALC_OUT_OF_MEMORY;
+
+    if(!deviceName)
+        deviceName = qsaDevice;
+
+    if(strcmp(deviceName, qsaDevice) == 0)
+        status = snd_pcm_open_preferred(&data->pcmHandle, &card, &dev, SND_PCM_OPEN_PLAYBACK);
+    else
+    {
+        const DevMap *iter;
+
+        if(VECTOR_SIZE(DeviceNameMap) == 0)
+            deviceList(SND_PCM_CHANNEL_PLAYBACK, &DeviceNameMap);
+
+#define MATCH_DEVNAME(iter) ((iter)->name && strcmp(deviceName, (iter)->name)==0)
+        VECTOR_FIND_IF(iter, const DevMap, DeviceNameMap, MATCH_DEVNAME);
+#undef MATCH_DEVNAME
+        if(iter == VECTOR_END(DeviceNameMap))
+        {
+            free(data);
+            return ALC_INVALID_DEVICE;
+        }
+
+        status = snd_pcm_open(&data->pcmHandle, iter->card, iter->dev, SND_PCM_OPEN_PLAYBACK);
+    }
+
+    if(status < 0)
+    {
+        free(data);
+        return ALC_INVALID_DEVICE;
+    }
+
+    data->audio_fd = snd_pcm_file_descriptor(data->pcmHandle, SND_PCM_CHANNEL_PLAYBACK);
+    if(data->audio_fd < 0)
+    {
+        snd_pcm_close(data->pcmHandle);
+        free(data);
+        return ALC_INVALID_DEVICE;
+    }
+
+    al_string_copy_cstr(&device->DeviceName, deviceName);
+    device->ExtraData = data;
+
+    return ALC_NO_ERROR;
+}
+
+static void qsa_close_playback(ALCdevice* device)
+{
+    qsa_data* data=(qsa_data*)device->ExtraData;
+
+    if (data->buffer!=NULL)
+    {
+        free(data->buffer);
+        data->buffer=NULL;
+    }
+
+    snd_pcm_close(data->pcmHandle);
+    free(data);
+
+    device->ExtraData=NULL;
+}
+
+static ALCboolean qsa_reset_playback(ALCdevice* device)
+{
+    qsa_data* data=(qsa_data*)device->ExtraData;
+    int32_t format=-1;
+
+    switch(device->FmtType)
+    {
+        case DevFmtByte:
+             format=SND_PCM_SFMT_S8;
+             break;
+        case DevFmtUByte:
+             format=SND_PCM_SFMT_U8;
+             break;
+        case DevFmtShort:
+             format=SND_PCM_SFMT_S16_LE;
+             break;
+        case DevFmtUShort:
+             format=SND_PCM_SFMT_U16_LE;
+             break;
+        case DevFmtInt:
+             format=SND_PCM_SFMT_S32_LE;
+             break;
+        case DevFmtUInt:
+             format=SND_PCM_SFMT_U32_LE;
+             break;
+        case DevFmtFloat:
+             format=SND_PCM_SFMT_FLOAT_LE;
+             break;
+    }
+
+    /* we actually don't want to block on writes */
+    snd_pcm_nonblock_mode(data->pcmHandle, 1);
+    /* Disable mmap to control data transfer to the audio device */
+    snd_pcm_plugin_set_disable(data->pcmHandle, PLUGIN_DISABLE_MMAP);
+    snd_pcm_plugin_set_disable(data->pcmHandle, PLUGIN_DISABLE_BUFFER_PARTIAL_BLOCKS);
+
+    // configure a sound channel
+    memset(&data->cparams, 0, sizeof(data->cparams));
+    data->cparams.channel=SND_PCM_CHANNEL_PLAYBACK;
+    data->cparams.mode=SND_PCM_MODE_BLOCK;
+    data->cparams.start_mode=SND_PCM_START_FULL;
+    data->cparams.stop_mode=SND_PCM_STOP_STOP;
+
+    data->cparams.buf.block.frag_size=device->UpdateSize*
+        ChannelsFromDevFmt(device->FmtChans)*BytesFromDevFmt(device->FmtType);
+    data->cparams.buf.block.frags_max=device->NumUpdates;
+    data->cparams.buf.block.frags_min=device->NumUpdates;
+
+    data->cparams.format.interleave=1;
+    data->cparams.format.rate=device->Frequency;
+    data->cparams.format.voices=ChannelsFromDevFmt(device->FmtChans);
+    data->cparams.format.format=format;
+
+    if ((snd_pcm_plugin_params(data->pcmHandle, &data->cparams))<0)
+    {
+        int original_rate=data->cparams.format.rate;
+        int original_voices=data->cparams.format.voices;
+        int original_format=data->cparams.format.format;
+        int it;
+        int jt;
+
+        for (it=0; it<1; it++)
+        {
+            /* Check for second pass */
+            if (it==1)
+            {
+                original_rate=ratelist[0].rate;
+                original_voices=channellist[0].channels;
+                original_format=formatlist[0].format;
+            }
+
+            do {
+                /* At first downgrade sample format */
+                jt=0;
+                do {
+                    if (formatlist[jt].format==data->cparams.format.format)
+                    {
+                        data->cparams.format.format=formatlist[jt+1].format;
+                        break;
+                    }
+                    if (formatlist[jt].format==0)
+                    {
+                        data->cparams.format.format=0;
+                        break;
+                    }
+                    jt++;
+                } while(1);
+
+                if (data->cparams.format.format==0)
+                {
+                    data->cparams.format.format=original_format;
+
+                    /* At secod downgrade sample rate */
+                    jt=0;
+                    do {
+                        if (ratelist[jt].rate==data->cparams.format.rate)
+                        {
+                            data->cparams.format.rate=ratelist[jt+1].rate;
+                            break;
+                        }
+                        if (ratelist[jt].rate==0)
+                        {
+                            data->cparams.format.rate=0;
+                            break;
+                        }
+                        jt++;
+                    } while(1);
+
+                    if (data->cparams.format.rate==0)
+                    {
+                        data->cparams.format.rate=original_rate;
+                        data->cparams.format.format=original_format;
+
+                        /* At third downgrade channels number */
+                        jt=0;
+                        do {
+                            if(channellist[jt].channels==data->cparams.format.voices)
+                            {
+                                data->cparams.format.voices=channellist[jt+1].channels;
+                                break;
+                            }
+                            if (channellist[jt].channels==0)
+                            {
+                                data->cparams.format.voices=0;
+                                break;
+                            }
+                           jt++;
+                        } while(1);
+                    }
+
+                    if (data->cparams.format.voices==0)
+                    {
+                        break;
+                    }
+                }
+
+                data->cparams.buf.block.frag_size=device->UpdateSize*
+                    data->cparams.format.voices*
+                    snd_pcm_format_width(data->cparams.format.format)/8;
+                data->cparams.buf.block.frags_max=device->NumUpdates;
+                data->cparams.buf.block.frags_min=device->NumUpdates;
+                if ((snd_pcm_plugin_params(data->pcmHandle, &data->cparams))<0)
+                {
+                    continue;
+                }
+                else
+                {
+                    break;
+                }
+            } while(1);
+
+            if (data->cparams.format.voices!=0)
+            {
+                break;
+            }
+        }
+
+        if (data->cparams.format.voices==0)
+        {
+            return ALC_FALSE;
+        }
+    }
+
+    if ((snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_PLAYBACK))<0)
+    {
+        return ALC_FALSE;
+    }
+
+    memset(&data->csetup, 0, sizeof(data->csetup));
+    data->csetup.channel=SND_PCM_CHANNEL_PLAYBACK;
+    if (snd_pcm_plugin_setup(data->pcmHandle, &data->csetup)<0)
+    {
+        return ALC_FALSE;
+    }
+
+    /* now fill back to the our AL device */
+    device->Frequency=data->cparams.format.rate;
+
+    switch (data->cparams.format.voices)
+    {
+        case 1:
+             device->FmtChans=DevFmtMono;
+             break;
+        case 2:
+             device->FmtChans=DevFmtStereo;
+             break;
+        case 4:
+             device->FmtChans=DevFmtQuad;
+             break;
+        case 6:
+             device->FmtChans=DevFmtX51;
+             break;
+        case 7:
+             device->FmtChans=DevFmtX61;
+             break;
+        case 8:
+             device->FmtChans=DevFmtX71;
+             break;
+        default:
+             device->FmtChans=DevFmtMono;
+             break;
+    }
+
+    switch (data->cparams.format.format)
+    {
+        case SND_PCM_SFMT_S8:
+             device->FmtType=DevFmtByte;
+             break;
+        case SND_PCM_SFMT_U8:
+             device->FmtType=DevFmtUByte;
+             break;
+        case SND_PCM_SFMT_S16_LE:
+             device->FmtType=DevFmtShort;
+             break;
+        case SND_PCM_SFMT_U16_LE:
+             device->FmtType=DevFmtUShort;
+             break;
+        case SND_PCM_SFMT_S32_LE:
+             device->FmtType=DevFmtInt;
+             break;
+        case SND_PCM_SFMT_U32_LE:
+             device->FmtType=DevFmtUInt;
+             break;
+        case SND_PCM_SFMT_FLOAT_LE:
+             device->FmtType=DevFmtFloat;
+             break;
+        default:
+             device->FmtType=DevFmtShort;
+             break;
+    }
+
+    SetDefaultChannelOrder(device);
+
+    device->UpdateSize=data->csetup.buf.block.frag_size/
+        (ChannelsFromDevFmt(device->FmtChans)*BytesFromDevFmt(device->FmtType));
+    device->NumUpdates=data->csetup.buf.block.frags;
+
+    data->size=data->csetup.buf.block.frag_size;
+    data->buffer=malloc(data->size);
+    if (!data->buffer)
+    {
+        return ALC_FALSE;
+    }
+
+    return ALC_TRUE;
+}
+
+static ALCboolean qsa_start_playback(ALCdevice* device)
+{
+    qsa_data *data = (qsa_data*)device->ExtraData;
+
+    data->killNow = 0;
+    if(althrd_create(&data->thread, qsa_proc_playback, device) != althrd_success)
+        return ALC_FALSE;
+
+    return ALC_TRUE;
+}
+
+static void qsa_stop_playback(ALCdevice* device)
+{
+    qsa_data *data = (qsa_data*)device->ExtraData;
+    int res;
+
+    if(data->killNow)
+        return;
+
+    data->killNow = 1;
+    althrd_join(data->thread, &res);
+}
+
+/***********/
+/* Capture */
+/***********/
+
+static ALCenum qsa_open_capture(ALCdevice* device, const ALCchar* deviceName)
+{
+    qsa_data *data;
+    int card, dev;
+    int format=-1;
+    int status;
+
+    data=(qsa_data*)calloc(1, sizeof(qsa_data));
+    if (data==NULL)
+    {
+        return ALC_OUT_OF_MEMORY;
+    }
+
+    if(!deviceName)
+        deviceName = qsaDevice;
+
+    if(strcmp(deviceName, qsaDevice) == 0)
+        status = snd_pcm_open_preferred(&data->pcmHandle, &card, &dev, SND_PCM_OPEN_CAPTURE);
+    else
+    {
+        const DevMap *iter;
+
+        if(VECTOR_SIZE(CaptureNameMap) == 0)
+            deviceList(SND_PCM_CHANNEL_CAPTURE, &CaptureNameMap);
+
+#define MATCH_DEVNAME(iter) ((iter)->name && strcmp(deviceName, (iter)->name)==0)
+        VECTOR_FIND_IF(iter, const DevMap, CaptureNameMap, MATCH_DEVNAME);
+#undef MATCH_DEVNAME
+        if(iter == VECTOR_END(CaptureNameMap))
+        {
+            free(data);
+            return ALC_INVALID_DEVICE;
+        }
+
+        status = snd_pcm_open(&data->pcmHandle, iter->card, iter->dev, SND_PCM_OPEN_CAPTURE);
+    }
+
+    if(status < 0)
+    {
+        free(data);
+        return ALC_INVALID_DEVICE;
+    }
+
+    data->audio_fd = snd_pcm_file_descriptor(data->pcmHandle, SND_PCM_CHANNEL_CAPTURE);
+    if(data->audio_fd < 0)
+    {
+        snd_pcm_close(data->pcmHandle);
+        free(data);
+        return ALC_INVALID_DEVICE;
+    }
+
+    al_string_copy_cstr(&device->DeviceName, deviceName);
+    device->ExtraData = data;
+
+    switch (device->FmtType)
+    {
+        case DevFmtByte:
+             format=SND_PCM_SFMT_S8;
+             break;
+        case DevFmtUByte:
+             format=SND_PCM_SFMT_U8;
+             break;
+        case DevFmtShort:
+             format=SND_PCM_SFMT_S16_LE;
+             break;
+        case DevFmtUShort:
+             format=SND_PCM_SFMT_U16_LE;
+             break;
+        case DevFmtInt:
+             format=SND_PCM_SFMT_S32_LE;
+             break;
+        case DevFmtUInt:
+             format=SND_PCM_SFMT_U32_LE;
+             break;
+        case DevFmtFloat:
+             format=SND_PCM_SFMT_FLOAT_LE;
+             break;
+    }
+
+    /* we actually don't want to block on reads */
+    snd_pcm_nonblock_mode(data->pcmHandle, 1);
+    /* Disable mmap to control data transfer to the audio device */
+    snd_pcm_plugin_set_disable(data->pcmHandle, PLUGIN_DISABLE_MMAP);
+
+    /* configure a sound channel */
+    memset(&data->cparams, 0, sizeof(data->cparams));
+    data->cparams.mode=SND_PCM_MODE_BLOCK;
+    data->cparams.channel=SND_PCM_CHANNEL_CAPTURE;
+    data->cparams.start_mode=SND_PCM_START_GO;
+    data->cparams.stop_mode=SND_PCM_STOP_STOP;
+
+    data->cparams.buf.block.frag_size=device->UpdateSize*
+        ChannelsFromDevFmt(device->FmtChans)*BytesFromDevFmt(device->FmtType);
+    data->cparams.buf.block.frags_max=device->NumUpdates;
+    data->cparams.buf.block.frags_min=device->NumUpdates;
+
+    data->cparams.format.interleave=1;
+    data->cparams.format.rate=device->Frequency;
+    data->cparams.format.voices=ChannelsFromDevFmt(device->FmtChans);
+    data->cparams.format.format=format;
+
+    if(snd_pcm_plugin_params(data->pcmHandle, &data->cparams) < 0)
+    {
+        snd_pcm_close(data->pcmHandle);
+        free(data);
+        device->ExtraData=NULL;
+
+        return ALC_INVALID_VALUE;
+    }
+
+    return ALC_NO_ERROR;
+}
+
+static void qsa_close_capture(ALCdevice* device)
+{
+    qsa_data* data=(qsa_data*)device->ExtraData;
+
+    if (data->pcmHandle!=NULL)
+        snd_pcm_close(data->pcmHandle);
+
+    free(data);
+    device->ExtraData=NULL;
+}
+
+static void qsa_start_capture(ALCdevice* device)
+{
+    qsa_data* data=(qsa_data*)device->ExtraData;
+    int rstatus;
+
+    if ((rstatus=snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_CAPTURE))<0)
+    {
+        ERR("capture prepare failed: %s\n", snd_strerror(rstatus));
+        return;
+    }
+
+    memset(&data->csetup, 0, sizeof(data->csetup));
+    data->csetup.channel=SND_PCM_CHANNEL_CAPTURE;
+    if ((rstatus=snd_pcm_plugin_setup(data->pcmHandle, &data->csetup))<0)
+    {
+        ERR("capture setup failed: %s\n", snd_strerror(rstatus));
+        return;
+    }
+
+    snd_pcm_capture_go(data->pcmHandle);
+}
+
+static void qsa_stop_capture(ALCdevice* device)
+{
+    qsa_data* data=(qsa_data*)device->ExtraData;
+
+    snd_pcm_capture_flush(data->pcmHandle);
+}
+
+static ALCuint qsa_available_samples(ALCdevice* device)
+{
+    qsa_data* data=(qsa_data*)device->ExtraData;
+    snd_pcm_channel_status_t status;
+    ALint frame_size=FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
+    ALint free_size;
+    int rstatus;
+
+    memset(&status, 0, sizeof (status));
+    status.channel=SND_PCM_CHANNEL_CAPTURE;
+    snd_pcm_plugin_status(data->pcmHandle, &status);
+    if ((status.status==SND_PCM_STATUS_OVERRUN) ||
+        (status.status==SND_PCM_STATUS_READY))
+    {
+        if ((rstatus=snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_CAPTURE))<0)
+        {
+            ERR("capture prepare failed: %s\n", snd_strerror(rstatus));
+            aluHandleDisconnect(device);
+            return 0;
+        }
+
+        snd_pcm_capture_go(data->pcmHandle);
+        return 0;
+    }
+
+    free_size=data->csetup.buf.block.frag_size*data->csetup.buf.block.frags;
+    free_size-=status.free;
+
+    return free_size/frame_size;
+}
+
+static ALCenum qsa_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint samples)
+{
+    qsa_data* data=(qsa_data*)device->ExtraData;
+    char* read_ptr;
+    snd_pcm_channel_status_t status;
+    fd_set rfds;
+    int selectret;
+    struct timeval timeout;
+    int bytes_read;
+    ALint frame_size=FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
+    ALint len=samples*frame_size;
+    int rstatus;
+
+    read_ptr=buffer;
+
+    while (len>0)
+    {
+        FD_ZERO(&rfds);
+        FD_SET(data->audio_fd, &rfds);
+        timeout.tv_sec=2;
+        timeout.tv_usec=0;
+
+        /* Select also works like time slice to OS */
+        bytes_read=0;
+        selectret=select(data->audio_fd+1, &rfds, NULL, NULL, &timeout);
+        switch (selectret)
+        {
+            case -1:
+                 aluHandleDisconnect(device);
+                 return ALC_INVALID_DEVICE;
+            case 0:
+                 break;
+            default:
+                 if (FD_ISSET(data->audio_fd, &rfds))
+                 {
+                     bytes_read=snd_pcm_plugin_read(data->pcmHandle, read_ptr, len);
+                     break;
+                 }
+                 break;
+        }
+
+        if (bytes_read<=0)
+        {
+            if ((errno==EAGAIN) || (errno==EWOULDBLOCK))
+            {
+                continue;
+            }
+
+            memset(&status, 0, sizeof (status));
+            status.channel=SND_PCM_CHANNEL_CAPTURE;
+            snd_pcm_plugin_status(data->pcmHandle, &status);
+
+            /* we need to reinitialize the sound channel if we've overrun the buffer */
+            if ((status.status==SND_PCM_STATUS_OVERRUN) ||
+                (status.status==SND_PCM_STATUS_READY))
+            {
+                if ((rstatus=snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_CAPTURE))<0)
+                {
+                    ERR("capture prepare failed: %s\n", snd_strerror(rstatus));
+                    aluHandleDisconnect(device);
+                    return ALC_INVALID_DEVICE;
+                }
+                snd_pcm_capture_go(data->pcmHandle);
+            }
+        }
+        else
+        {
+            read_ptr+=bytes_read;
+            len-=bytes_read;
+        }
+    }
+
+    return ALC_NO_ERROR;
+}
+
+static const BackendFuncs qsa_funcs= {
+    qsa_open_playback,
+    qsa_close_playback,
+    qsa_reset_playback,
+    qsa_start_playback,
+    qsa_stop_playback,
+    qsa_open_capture,
+    qsa_close_capture,
+    qsa_start_capture,
+    qsa_stop_capture,
+    qsa_capture_samples,
+    qsa_available_samples
+};
+
+ALCboolean alc_qsa_init(BackendFuncs* func_list)
+{
+    *func_list = qsa_funcs;
+    return ALC_TRUE;
+}
+
+void alc_qsa_deinit(void)
+{
+#define FREE_NAME(iter) free((iter)->name)
+    VECTOR_FOR_EACH(DevMap, DeviceNameMap, FREE_NAME);
+    VECTOR_DEINIT(DeviceNameMap);
+
+    VECTOR_FOR_EACH(DevMap, CaptureNameMap, FREE_NAME);
+    VECTOR_DEINIT(CaptureNameMap);
+#undef FREE_NAME
+}
+
+void alc_qsa_probe(enum DevProbe type)
+{
+    switch (type)
+    {
+        case ALL_DEVICE_PROBE:
+#define FREE_NAME(iter) free((iter)->name)
+            VECTOR_FOR_EACH(DevMap, DeviceNameMap, FREE_NAME);
+            VECTOR_RESIZE(DeviceNameMap, 0, 0);
+#undef FREE_NAME
+
+            deviceList(SND_PCM_CHANNEL_PLAYBACK, &DeviceNameMap);
+#define APPEND_DEVICE(iter) AppendAllDevicesList((iter)->name)
+            VECTOR_FOR_EACH(const DevMap, DeviceNameMap, APPEND_DEVICE);
+#undef APPEND_DEVICE
+            break;
+
+        case CAPTURE_DEVICE_PROBE:
+#define FREE_NAME(iter) free((iter)->name)
+            VECTOR_FOR_EACH(DevMap, CaptureNameMap, FREE_NAME);
+            VECTOR_RESIZE(CaptureNameMap, 0, 0);
+#undef FREE_NAME
+
+            deviceList(SND_PCM_CHANNEL_CAPTURE, &CaptureNameMap);
+#define APPEND_DEVICE(iter) AppendCaptureDeviceList((iter)->name)
+            VECTOR_FOR_EACH(const DevMap, CaptureNameMap, APPEND_DEVICE);
+#undef APPEND_DEVICE
+            break;
+    }
+}

+ 294 - 0
Engine/lib/openal-soft/Alc/backends/sndio.c

@@ -0,0 +1,294 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 1999-2007 by authors.
+ * This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the
+ *  Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "alMain.h"
+#include "alu.h"
+#include "threads.h"
+
+#include <sndio.h>
+
+
+static const ALCchar sndio_device[] = "SndIO Default";
+
+
+static ALCboolean sndio_load(void)
+{
+    return ALC_TRUE;
+}
+
+
+typedef struct {
+    struct sio_hdl *sndHandle;
+
+    ALvoid *mix_data;
+    ALsizei data_size;
+
+    volatile int killNow;
+    althrd_t thread;
+} sndio_data;
+
+
+static int sndio_proc(void *ptr)
+{
+    ALCdevice *device = ptr;
+    sndio_data *data = device->ExtraData;
+    ALsizei frameSize;
+    size_t wrote;
+
+    SetRTPriority();
+    althrd_setname(althrd_current(), MIXER_THREAD_NAME);
+
+    frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
+
+    while(!data->killNow && device->Connected)
+    {
+        ALsizei len = data->data_size;
+        ALubyte *WritePtr = data->mix_data;
+
+        aluMixData(device, WritePtr, len/frameSize);
+        while(len > 0 && !data->killNow)
+        {
+            wrote = sio_write(data->sndHandle, WritePtr, len);
+            if(wrote == 0)
+            {
+                ERR("sio_write failed\n");
+                ALCdevice_Lock(device);
+                aluHandleDisconnect(device);
+                ALCdevice_Unlock(device);
+                break;
+            }
+
+            len -= wrote;
+            WritePtr += wrote;
+        }
+    }
+
+    return 0;
+}
+
+
+
+static ALCenum sndio_open_playback(ALCdevice *device, const ALCchar *deviceName)
+{
+    sndio_data *data;
+
+    if(!deviceName)
+        deviceName = sndio_device;
+    else if(strcmp(deviceName, sndio_device) != 0)
+        return ALC_INVALID_VALUE;
+
+    data = calloc(1, sizeof(*data));
+    data->killNow = 0;
+
+    data->sndHandle = sio_open(NULL, SIO_PLAY, 0);
+    if(data->sndHandle == NULL)
+    {
+        free(data);
+        ERR("Could not open device\n");
+        return ALC_INVALID_VALUE;
+    }
+
+    al_string_copy_cstr(&device->DeviceName, deviceName);
+    device->ExtraData = data;
+
+    return ALC_NO_ERROR;
+}
+
+static void sndio_close_playback(ALCdevice *device)
+{
+    sndio_data *data = device->ExtraData;
+
+    sio_close(data->sndHandle);
+    free(data);
+    device->ExtraData = NULL;
+}
+
+static ALCboolean sndio_reset_playback(ALCdevice *device)
+{
+    sndio_data *data = device->ExtraData;
+    struct sio_par par;
+
+    sio_initpar(&par);
+
+    par.rate = device->Frequency;
+    par.pchan = ((device->FmtChans != DevFmtMono) ? 2 : 1);
+
+    switch(device->FmtType)
+    {
+        case DevFmtByte:
+            par.bits = 8;
+            par.sig = 1;
+            break;
+        case DevFmtUByte:
+            par.bits = 8;
+            par.sig = 0;
+            break;
+        case DevFmtFloat:
+        case DevFmtShort:
+            par.bits = 16;
+            par.sig = 1;
+            break;
+        case DevFmtUShort:
+            par.bits = 16;
+            par.sig = 0;
+            break;
+        case DevFmtInt:
+            par.bits = 32;
+            par.sig = 1;
+            break;
+        case DevFmtUInt:
+            par.bits = 32;
+            par.sig = 0;
+            break;
+    }
+    par.le = SIO_LE_NATIVE;
+
+    par.round = device->UpdateSize;
+    par.appbufsz = device->UpdateSize * (device->NumUpdates-1);
+    if(!par.appbufsz) par.appbufsz = device->UpdateSize;
+
+    if(!sio_setpar(data->sndHandle, &par) || !sio_getpar(data->sndHandle, &par))
+    {
+        ERR("Failed to set device parameters\n");
+        return ALC_FALSE;
+    }
+
+    if(par.bits != par.bps*8)
+    {
+        ERR("Padded samples not supported (%u of %u bits)\n", par.bits, par.bps*8);
+        return ALC_FALSE;
+    }
+
+    device->Frequency = par.rate;
+    device->FmtChans = ((par.pchan==1) ? DevFmtMono : DevFmtStereo);
+
+    if(par.bits == 8 && par.sig == 1)
+        device->FmtType = DevFmtByte;
+    else if(par.bits == 8 && par.sig == 0)
+        device->FmtType = DevFmtUByte;
+    else if(par.bits == 16 && par.sig == 1)
+        device->FmtType = DevFmtShort;
+    else if(par.bits == 16 && par.sig == 0)
+        device->FmtType = DevFmtUShort;
+    else if(par.bits == 32 && par.sig == 1)
+        device->FmtType = DevFmtInt;
+    else if(par.bits == 32 && par.sig == 0)
+        device->FmtType = DevFmtUInt;
+    else
+    {
+        ERR("Unhandled sample format: %s %u-bit\n", (par.sig?"signed":"unsigned"), par.bits);
+        return ALC_FALSE;
+    }
+
+    device->UpdateSize = par.round;
+    device->NumUpdates = (par.bufsz/par.round) + 1;
+
+    SetDefaultChannelOrder(device);
+
+    return ALC_TRUE;
+}
+
+static ALCboolean sndio_start_playback(ALCdevice *device)
+{
+    sndio_data *data = device->ExtraData;
+
+    if(!sio_start(data->sndHandle))
+    {
+        ERR("Error starting playback\n");
+        return ALC_FALSE;
+    }
+
+    data->data_size = device->UpdateSize * FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
+    data->mix_data = calloc(1, data->data_size);
+
+    data->killNow = 0;
+    if(althrd_create(&data->thread, sndio_proc, device) != althrd_success)
+    {
+        sio_stop(data->sndHandle);
+        free(data->mix_data);
+        data->mix_data = NULL;
+        return ALC_FALSE;
+    }
+
+    return ALC_TRUE;
+}
+
+static void sndio_stop_playback(ALCdevice *device)
+{
+    sndio_data *data = device->ExtraData;
+    int res;
+
+    if(data->killNow)
+        return;
+
+    data->killNow = 1;
+    althrd_join(data->thread, &res);
+
+    if(!sio_stop(data->sndHandle))
+        ERR("Error stopping device\n");
+
+    free(data->mix_data);
+    data->mix_data = NULL;
+}
+
+
+static const BackendFuncs sndio_funcs = {
+    sndio_open_playback,
+    sndio_close_playback,
+    sndio_reset_playback,
+    sndio_start_playback,
+    sndio_stop_playback,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL
+};
+
+ALCboolean alc_sndio_init(BackendFuncs *func_list)
+{
+    if(!sndio_load())
+        return ALC_FALSE;
+    *func_list = sndio_funcs;
+    return ALC_TRUE;
+}
+
+void alc_sndio_deinit(void)
+{
+}
+
+void alc_sndio_probe(enum DevProbe type)
+{
+    switch(type)
+    {
+        case ALL_DEVICE_PROBE:
+            AppendAllDevicesList(sndio_device);
+            break;
+        case CAPTURE_DEVICE_PROBE:
+            break;
+    }
+}

+ 339 - 0
Engine/lib/openal-soft/Alc/backends/solaris.c

@@ -0,0 +1,339 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 1999-2007 by authors.
+ * This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the
+ *  Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#include "config.h"
+
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <memory.h>
+#include <unistd.h>
+#include <errno.h>
+#include <math.h>
+
+#include "alMain.h"
+#include "alu.h"
+#include "threads.h"
+#include "compat.h"
+
+#include "backends/base.h"
+
+#include <sys/audioio.h>
+
+
+typedef struct ALCsolarisBackend {
+    DERIVE_FROM_TYPE(ALCbackend);
+
+    int fd;
+
+    ALubyte *mix_data;
+    int data_size;
+
+    volatile int killNow;
+    althrd_t thread;
+} ALCsolarisBackend;
+
+static int ALCsolarisBackend_mixerProc(void *ptr);
+
+static void ALCsolarisBackend_Construct(ALCsolarisBackend *self, ALCdevice *device);
+static void ALCsolarisBackend_Destruct(ALCsolarisBackend *self);
+static ALCenum ALCsolarisBackend_open(ALCsolarisBackend *self, const ALCchar *name);
+static void ALCsolarisBackend_close(ALCsolarisBackend *self);
+static ALCboolean ALCsolarisBackend_reset(ALCsolarisBackend *self);
+static ALCboolean ALCsolarisBackend_start(ALCsolarisBackend *self);
+static void ALCsolarisBackend_stop(ALCsolarisBackend *self);
+static DECLARE_FORWARD2(ALCsolarisBackend, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
+static DECLARE_FORWARD(ALCsolarisBackend, ALCbackend, ALCuint, availableSamples)
+static DECLARE_FORWARD(ALCsolarisBackend, ALCbackend, ClockLatency, getClockLatency)
+static DECLARE_FORWARD(ALCsolarisBackend, ALCbackend, void, lock)
+static DECLARE_FORWARD(ALCsolarisBackend, ALCbackend, void, unlock)
+DECLARE_DEFAULT_ALLOCATORS(ALCsolarisBackend)
+
+DEFINE_ALCBACKEND_VTABLE(ALCsolarisBackend);
+
+
+static const ALCchar solaris_device[] = "Solaris Default";
+
+static const char *solaris_driver = "/dev/audio";
+
+
+static void ALCsolarisBackend_Construct(ALCsolarisBackend *self, ALCdevice *device)
+{
+    ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
+    SET_VTABLE2(ALCsolarisBackend, ALCbackend, self);
+
+    self->fd = -1;
+}
+
+static void ALCsolarisBackend_Destruct(ALCsolarisBackend *self)
+{
+    if(self->fd != -1)
+        close(self->fd);
+    self->fd = -1;
+
+    free(self->mix_data);
+    self->mix_data = NULL;
+    self->data_size = 0;
+
+    ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
+}
+
+
+static int ALCsolarisBackend_mixerProc(void *ptr)
+{
+    ALCsolarisBackend *self = ptr;
+    ALCdevice *Device = STATIC_CAST(ALCbackend,self)->mDevice;
+    ALint frameSize;
+    int wrote;
+
+    SetRTPriority();
+    althrd_setname(althrd_current(), MIXER_THREAD_NAME);
+
+    frameSize = FrameSizeFromDevFmt(Device->FmtChans, Device->FmtType);
+
+    while(!self->killNow && Device->Connected)
+    {
+        ALint len = self->data_size;
+        ALubyte *WritePtr = self->mix_data;
+
+        aluMixData(Device, WritePtr, len/frameSize);
+        while(len > 0 && !self->killNow)
+        {
+            wrote = write(self->fd, WritePtr, len);
+            if(wrote < 0)
+            {
+                if(errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR)
+                {
+                    ERR("write failed: %s\n", strerror(errno));
+                    ALCsolarisBackend_lock(self);
+                    aluHandleDisconnect(Device);
+                    ALCsolarisBackend_unlock(self);
+                    break;
+                }
+
+                al_nssleep(1000000);
+                continue;
+            }
+
+            len -= wrote;
+            WritePtr += wrote;
+        }
+    }
+
+    return 0;
+}
+
+
+static ALCenum ALCsolarisBackend_open(ALCsolarisBackend *self, const ALCchar *name)
+{
+    ALCdevice *device;
+
+    if(!name)
+        name = solaris_device;
+    else if(strcmp(name, solaris_device) != 0)
+        return ALC_INVALID_VALUE;
+
+    self->fd = open(solaris_driver, O_WRONLY);
+    if(self->fd == -1)
+    {
+        ERR("Could not open %s: %s\n", solaris_driver, strerror(errno));
+        return ALC_INVALID_VALUE;
+    }
+
+    device = STATIC_CAST(ALCbackend,self)->mDevice;
+    al_string_copy_cstr(&device->DeviceName, name);
+
+    return ALC_NO_ERROR;
+}
+
+static void ALCsolarisBackend_close(ALCsolarisBackend *self)
+{
+    close(self->fd);
+    self->fd = -1;
+}
+
+static ALCboolean ALCsolarisBackend_reset(ALCsolarisBackend *self)
+{
+    ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
+    audio_info_t info;
+    ALuint frameSize;
+    int numChannels;
+
+    AUDIO_INITINFO(&info);
+
+    info.play.sample_rate = device->Frequency;
+
+    if(device->FmtChans != DevFmtMono)
+        device->FmtChans = DevFmtStereo;
+    numChannels = ChannelsFromDevFmt(device->FmtChans);
+    info.play.channels = numChannels;
+
+    switch(device->FmtType)
+    {
+        case DevFmtByte:
+            info.play.precision = 8;
+            info.play.encoding = AUDIO_ENCODING_LINEAR;
+            break;
+        case DevFmtUByte:
+            info.play.precision = 8;
+            info.play.encoding = AUDIO_ENCODING_LINEAR8;
+            break;
+        case DevFmtUShort:
+        case DevFmtInt:
+        case DevFmtUInt:
+        case DevFmtFloat:
+            device->FmtType = DevFmtShort;
+            /* fall-through */
+        case DevFmtShort:
+            info.play.precision = 16;
+            info.play.encoding = AUDIO_ENCODING_LINEAR;
+            break;
+    }
+
+    frameSize = numChannels * BytesFromDevFmt(device->FmtType);
+    info.play.buffer_size = device->UpdateSize*device->NumUpdates * frameSize;
+
+    if(ioctl(self->fd, AUDIO_SETINFO, &info) < 0)
+    {
+        ERR("ioctl failed: %s\n", strerror(errno));
+        return ALC_FALSE;
+    }
+
+    if(ChannelsFromDevFmt(device->FmtChans) != info.play.channels)
+    {
+        ERR("Could not set %d channels, got %d instead\n", ChannelsFromDevFmt(device->FmtChans), info.play.channels);
+        return ALC_FALSE;
+    }
+
+    if(!((info.play.precision == 8 && info.play.encoding == AUDIO_ENCODING_LINEAR8 && device->FmtType == DevFmtUByte) ||
+         (info.play.precision == 8 && info.play.encoding == AUDIO_ENCODING_LINEAR && device->FmtType == DevFmtByte) ||
+         (info.play.precision == 16 && info.play.encoding == AUDIO_ENCODING_LINEAR && device->FmtType == DevFmtShort) ||
+         (info.play.precision == 32 && info.play.encoding == AUDIO_ENCODING_LINEAR && device->FmtType == DevFmtInt)))
+    {
+        ERR("Could not set %s samples, got %d (0x%x)\n", DevFmtTypeString(device->FmtType),
+            info.play.precision, info.play.encoding);
+        return ALC_FALSE;
+    }
+
+    device->Frequency = info.play.sample_rate;
+    device->UpdateSize = (info.play.buffer_size/device->NumUpdates) + 1;
+
+    SetDefaultChannelOrder(device);
+
+    free(self->mix_data);
+    self->data_size = device->UpdateSize * FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
+    self->mix_data = calloc(1, self->data_size);
+
+    return ALC_TRUE;
+}
+
+static ALCboolean ALCsolarisBackend_start(ALCsolarisBackend *self)
+{
+    self->killNow = 0;
+    if(althrd_create(&self->thread, ALCsolarisBackend_mixerProc, self) != althrd_success)
+        return ALC_FALSE;
+    return ALC_TRUE;
+}
+
+static void ALCsolarisBackend_stop(ALCsolarisBackend *self)
+{
+    int res;
+
+    if(self->killNow)
+        return;
+
+    self->killNow = 1;
+    althrd_join(self->thread, &res);
+
+    if(ioctl(self->fd, AUDIO_DRAIN) < 0)
+        ERR("Error draining device: %s\n", strerror(errno));
+}
+
+
+typedef struct ALCsolarisBackendFactory {
+    DERIVE_FROM_TYPE(ALCbackendFactory);
+} ALCsolarisBackendFactory;
+#define ALCSOLARISBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCsolarisBackendFactory, ALCbackendFactory) } }
+
+ALCbackendFactory *ALCsolarisBackendFactory_getFactory(void);
+
+static ALCboolean ALCsolarisBackendFactory_init(ALCsolarisBackendFactory *self);
+static DECLARE_FORWARD(ALCsolarisBackendFactory, ALCbackendFactory, void, deinit)
+static ALCboolean ALCsolarisBackendFactory_querySupport(ALCsolarisBackendFactory *self, ALCbackend_Type type);
+static void ALCsolarisBackendFactory_probe(ALCsolarisBackendFactory *self, enum DevProbe type);
+static ALCbackend* ALCsolarisBackendFactory_createBackend(ALCsolarisBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
+DEFINE_ALCBACKENDFACTORY_VTABLE(ALCsolarisBackendFactory);
+
+
+ALCbackendFactory *ALCsolarisBackendFactory_getFactory(void)
+{
+    static ALCsolarisBackendFactory factory = ALCSOLARISBACKENDFACTORY_INITIALIZER;
+    return STATIC_CAST(ALCbackendFactory, &factory);
+}
+
+
+static ALCboolean ALCsolarisBackendFactory_init(ALCsolarisBackendFactory* UNUSED(self))
+{
+    ConfigValueStr(NULL, "solaris", "device", &solaris_driver);
+    return ALC_TRUE;
+}
+
+static ALCboolean ALCsolarisBackendFactory_querySupport(ALCsolarisBackendFactory* UNUSED(self), ALCbackend_Type type)
+{
+    if(type == ALCbackend_Playback)
+        return ALC_TRUE;
+    return ALC_FALSE;
+}
+
+static void ALCsolarisBackendFactory_probe(ALCsolarisBackendFactory* UNUSED(self), enum DevProbe type)
+{
+    switch(type)
+    {
+        case ALL_DEVICE_PROBE:
+        {
+#ifdef HAVE_STAT
+            struct stat buf;
+            if(stat(solaris_driver, &buf) == 0)
+#endif
+                AppendAllDevicesList(solaris_device);
+        }
+        break;
+
+        case CAPTURE_DEVICE_PROBE:
+            break;
+    }
+}
+
+ALCbackend* ALCsolarisBackendFactory_createBackend(ALCsolarisBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
+{
+    if(type == ALCbackend_Playback)
+    {
+        ALCsolarisBackend *backend;
+        NEW_OBJ(backend, ALCsolarisBackend)(device);
+        if(!backend) return NULL;
+        return STATIC_CAST(ALCbackend, backend);
+    }
+
+    return NULL;
+}

+ 446 - 0
Engine/lib/openal-soft/Alc/backends/wave.c

@@ -0,0 +1,446 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 1999-2007 by authors.
+ * This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the
+ *  Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <memory.h>
+#include <errno.h>
+
+#include "alMain.h"
+#include "alu.h"
+#include "threads.h"
+#include "compat.h"
+
+#include "backends/base.h"
+
+
+static const ALCchar waveDevice[] = "Wave File Writer";
+
+static const ALubyte SUBTYPE_PCM[] = {
+    0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa,
+    0x00, 0x38, 0x9b, 0x71
+};
+static const ALubyte SUBTYPE_FLOAT[] = {
+    0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa,
+    0x00, 0x38, 0x9b, 0x71
+};
+
+static const ALubyte SUBTYPE_BFORMAT_PCM[] = {
+    0x01, 0x00, 0x00, 0x00, 0x21, 0x07, 0xd3, 0x11, 0x86, 0x44, 0xc8, 0xc1,
+    0xca, 0x00, 0x00, 0x00
+};
+
+static const ALubyte SUBTYPE_BFORMAT_FLOAT[] = {
+    0x03, 0x00, 0x00, 0x00, 0x21, 0x07, 0xd3, 0x11, 0x86, 0x44, 0xc8, 0xc1,
+    0xca, 0x00, 0x00, 0x00
+};
+
+static void fwrite16le(ALushort val, FILE *f)
+{
+    ALubyte data[2] = { val&0xff, (val>>8)&0xff };
+    fwrite(data, 1, 2, f);
+}
+
+static void fwrite32le(ALuint val, FILE *f)
+{
+    ALubyte data[4] = { val&0xff, (val>>8)&0xff, (val>>16)&0xff, (val>>24)&0xff };
+    fwrite(data, 1, 4, f);
+}
+
+
+typedef struct ALCwaveBackend {
+    DERIVE_FROM_TYPE(ALCbackend);
+
+    FILE *mFile;
+    long mDataStart;
+
+    ALvoid *mBuffer;
+    ALuint mSize;
+
+    volatile int killNow;
+    althrd_t thread;
+} ALCwaveBackend;
+
+static int ALCwaveBackend_mixerProc(void *ptr);
+
+static void ALCwaveBackend_Construct(ALCwaveBackend *self, ALCdevice *device);
+static DECLARE_FORWARD(ALCwaveBackend, ALCbackend, void, Destruct)
+static ALCenum ALCwaveBackend_open(ALCwaveBackend *self, const ALCchar *name);
+static void ALCwaveBackend_close(ALCwaveBackend *self);
+static ALCboolean ALCwaveBackend_reset(ALCwaveBackend *self);
+static ALCboolean ALCwaveBackend_start(ALCwaveBackend *self);
+static void ALCwaveBackend_stop(ALCwaveBackend *self);
+static DECLARE_FORWARD2(ALCwaveBackend, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
+static DECLARE_FORWARD(ALCwaveBackend, ALCbackend, ALCuint, availableSamples)
+static DECLARE_FORWARD(ALCwaveBackend, ALCbackend, ClockLatency, getClockLatency)
+static DECLARE_FORWARD(ALCwaveBackend, ALCbackend, void, lock)
+static DECLARE_FORWARD(ALCwaveBackend, ALCbackend, void, unlock)
+DECLARE_DEFAULT_ALLOCATORS(ALCwaveBackend)
+
+DEFINE_ALCBACKEND_VTABLE(ALCwaveBackend);
+
+
+static void ALCwaveBackend_Construct(ALCwaveBackend *self, ALCdevice *device)
+{
+    ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
+    SET_VTABLE2(ALCwaveBackend, ALCbackend, self);
+
+    self->mFile = NULL;
+    self->mDataStart = -1;
+
+    self->mBuffer = NULL;
+    self->mSize = 0;
+
+    self->killNow = 1;
+}
+
+
+static int ALCwaveBackend_mixerProc(void *ptr)
+{
+    ALCwaveBackend *self = (ALCwaveBackend*)ptr;
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+    struct timespec now, start;
+    ALint64 avail, done;
+    ALuint frameSize;
+    size_t fs;
+    const long restTime = (long)((ALuint64)device->UpdateSize * 1000000000 /
+                                 device->Frequency / 2);
+
+    althrd_setname(althrd_current(), MIXER_THREAD_NAME);
+
+    frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
+
+    done = 0;
+    if(altimespec_get(&start, AL_TIME_UTC) != AL_TIME_UTC)
+    {
+        ERR("Failed to get starting time\n");
+        return 1;
+    }
+    while(!self->killNow && device->Connected)
+    {
+        if(altimespec_get(&now, AL_TIME_UTC) != AL_TIME_UTC)
+        {
+            ERR("Failed to get current time\n");
+            return 1;
+        }
+
+        avail  = (now.tv_sec - start.tv_sec) * device->Frequency;
+        avail += (ALint64)(now.tv_nsec - start.tv_nsec) * device->Frequency / 1000000000;
+        if(avail < done)
+        {
+            /* Oops, time skipped backwards. Reset the number of samples done
+             * with one update available since we (likely) just came back from
+             * sleeping. */
+            done = avail - device->UpdateSize;
+        }
+
+        if(avail-done < device->UpdateSize)
+            al_nssleep(restTime);
+        else while(avail-done >= device->UpdateSize)
+        {
+            aluMixData(device, self->mBuffer, device->UpdateSize);
+            done += device->UpdateSize;
+
+            if(!IS_LITTLE_ENDIAN)
+            {
+                ALuint bytesize = BytesFromDevFmt(device->FmtType);
+                ALuint i;
+
+                if(bytesize == 2)
+                {
+                    ALushort *samples = self->mBuffer;
+                    ALuint len = self->mSize / 2;
+                    for(i = 0;i < len;i++)
+                    {
+                        ALushort samp = samples[i];
+                        samples[i] = (samp>>8) | (samp<<8);
+                    }
+                }
+                else if(bytesize == 4)
+                {
+                    ALuint *samples = self->mBuffer;
+                    ALuint len = self->mSize / 4;
+                    for(i = 0;i < len;i++)
+                    {
+                        ALuint samp = samples[i];
+                        samples[i] = (samp>>24) | ((samp>>8)&0x0000ff00) |
+                                     ((samp<<8)&0x00ff0000) | (samp<<24);
+                    }
+                }
+            }
+
+            fs = fwrite(self->mBuffer, frameSize, device->UpdateSize, self->mFile);
+            (void)fs;
+            if(ferror(self->mFile))
+            {
+                ERR("Error writing to file\n");
+                ALCdevice_Lock(device);
+                aluHandleDisconnect(device);
+                ALCdevice_Unlock(device);
+                break;
+            }
+        }
+    }
+
+    return 0;
+}
+
+
+static ALCenum ALCwaveBackend_open(ALCwaveBackend *self, const ALCchar *name)
+{
+    ALCdevice *device;
+    const char *fname;
+
+    fname = GetConfigValue(NULL, "wave", "file", "");
+    if(!fname[0]) return ALC_INVALID_VALUE;
+
+    if(!name)
+        name = waveDevice;
+    else if(strcmp(name, waveDevice) != 0)
+        return ALC_INVALID_VALUE;
+
+    self->mFile = al_fopen(fname, "wb");
+    if(!self->mFile)
+    {
+        ERR("Could not open file '%s': %s\n", fname, strerror(errno));
+        return ALC_INVALID_VALUE;
+    }
+
+    device = STATIC_CAST(ALCbackend, self)->mDevice;
+    al_string_copy_cstr(&device->DeviceName, name);
+
+    return ALC_NO_ERROR;
+}
+
+static void ALCwaveBackend_close(ALCwaveBackend *self)
+{
+    if(self->mFile)
+        fclose(self->mFile);
+    self->mFile = NULL;
+}
+
+static ALCboolean ALCwaveBackend_reset(ALCwaveBackend *self)
+{
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+    ALuint channels=0, bits=0, chanmask=0;
+    int isbformat = 0;
+    size_t val;
+
+    fseek(self->mFile, 0, SEEK_SET);
+    clearerr(self->mFile);
+
+    if(GetConfigValueBool(NULL, "wave", "bformat", 0))
+        device->FmtChans = DevFmtAmbi1;
+
+    switch(device->FmtType)
+    {
+        case DevFmtByte:
+            device->FmtType = DevFmtUByte;
+            break;
+        case DevFmtUShort:
+            device->FmtType = DevFmtShort;
+            break;
+        case DevFmtUInt:
+            device->FmtType = DevFmtInt;
+            break;
+        case DevFmtUByte:
+        case DevFmtShort:
+        case DevFmtInt:
+        case DevFmtFloat:
+            break;
+    }
+    switch(device->FmtChans)
+    {
+        case DevFmtMono:   chanmask = 0x04; break;
+        case DevFmtStereo: chanmask = 0x01 | 0x02; break;
+        case DevFmtQuad:   chanmask = 0x01 | 0x02 | 0x10 | 0x20; break;
+        case DevFmtX51: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x200 | 0x400; break;
+        case DevFmtX51Rear: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x010 | 0x020; break;
+        case DevFmtX61: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x100 | 0x200 | 0x400; break;
+        case DevFmtX71: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x010 | 0x020 | 0x200 | 0x400; break;
+        case DevFmtAmbi1:
+        case DevFmtAmbi2:
+        case DevFmtAmbi3:
+            /* .amb output requires FuMa */
+            device->AmbiFmt = AmbiFormat_FuMa;
+            isbformat = 1;
+            chanmask = 0;
+            break;
+    }
+    bits = BytesFromDevFmt(device->FmtType) * 8;
+    channels = ChannelsFromDevFmt(device->FmtChans);
+
+    fprintf(self->mFile, "RIFF");
+    fwrite32le(0xFFFFFFFF, self->mFile); // 'RIFF' header len; filled in at close
+
+    fprintf(self->mFile, "WAVE");
+
+    fprintf(self->mFile, "fmt ");
+    fwrite32le(40, self->mFile); // 'fmt ' header len; 40 bytes for EXTENSIBLE
+
+    // 16-bit val, format type id (extensible: 0xFFFE)
+    fwrite16le(0xFFFE, self->mFile);
+    // 16-bit val, channel count
+    fwrite16le(channels, self->mFile);
+    // 32-bit val, frequency
+    fwrite32le(device->Frequency, self->mFile);
+    // 32-bit val, bytes per second
+    fwrite32le(device->Frequency * channels * bits / 8, self->mFile);
+    // 16-bit val, frame size
+    fwrite16le(channels * bits / 8, self->mFile);
+    // 16-bit val, bits per sample
+    fwrite16le(bits, self->mFile);
+    // 16-bit val, extra byte count
+    fwrite16le(22, self->mFile);
+    // 16-bit val, valid bits per sample
+    fwrite16le(bits, self->mFile);
+    // 32-bit val, channel mask
+    fwrite32le(chanmask, self->mFile);
+    // 16 byte GUID, sub-type format
+    val = fwrite(((bits==32) ? (isbformat ? SUBTYPE_BFORMAT_FLOAT : SUBTYPE_FLOAT) :
+                               (isbformat ? SUBTYPE_BFORMAT_PCM : SUBTYPE_PCM)), 1, 16, self->mFile);
+    (void)val;
+
+    fprintf(self->mFile, "data");
+    fwrite32le(0xFFFFFFFF, self->mFile); // 'data' header len; filled in at close
+
+    if(ferror(self->mFile))
+    {
+        ERR("Error writing header: %s\n", strerror(errno));
+        return ALC_FALSE;
+    }
+    self->mDataStart = ftell(self->mFile);
+
+    SetDefaultWFXChannelOrder(device);
+
+    return ALC_TRUE;
+}
+
+static ALCboolean ALCwaveBackend_start(ALCwaveBackend *self)
+{
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+
+    self->mSize = device->UpdateSize * FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
+    self->mBuffer = malloc(self->mSize);
+    if(!self->mBuffer)
+    {
+        ERR("Buffer malloc failed\n");
+        return ALC_FALSE;
+    }
+
+    self->killNow = 0;
+    if(althrd_create(&self->thread, ALCwaveBackend_mixerProc, self) != althrd_success)
+    {
+        free(self->mBuffer);
+        self->mBuffer = NULL;
+        self->mSize = 0;
+        return ALC_FALSE;
+    }
+
+    return ALC_TRUE;
+}
+
+static void ALCwaveBackend_stop(ALCwaveBackend *self)
+{
+    ALuint dataLen;
+    long size;
+    int res;
+
+    if(self->killNow)
+        return;
+
+    self->killNow = 1;
+    althrd_join(self->thread, &res);
+
+    free(self->mBuffer);
+    self->mBuffer = NULL;
+
+    size = ftell(self->mFile);
+    if(size > 0)
+    {
+        dataLen = size - self->mDataStart;
+        if(fseek(self->mFile, self->mDataStart-4, SEEK_SET) == 0)
+            fwrite32le(dataLen, self->mFile); // 'data' header len
+        if(fseek(self->mFile, 4, SEEK_SET) == 0)
+            fwrite32le(size-8, self->mFile); // 'WAVE' header len
+    }
+}
+
+
+typedef struct ALCwaveBackendFactory {
+    DERIVE_FROM_TYPE(ALCbackendFactory);
+} ALCwaveBackendFactory;
+#define ALCWAVEBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCwaveBackendFactory, ALCbackendFactory) } }
+
+ALCbackendFactory *ALCwaveBackendFactory_getFactory(void);
+
+static ALCboolean ALCwaveBackendFactory_init(ALCwaveBackendFactory *self);
+static DECLARE_FORWARD(ALCwaveBackendFactory, ALCbackendFactory, void, deinit)
+static ALCboolean ALCwaveBackendFactory_querySupport(ALCwaveBackendFactory *self, ALCbackend_Type type);
+static void ALCwaveBackendFactory_probe(ALCwaveBackendFactory *self, enum DevProbe type);
+static ALCbackend* ALCwaveBackendFactory_createBackend(ALCwaveBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
+DEFINE_ALCBACKENDFACTORY_VTABLE(ALCwaveBackendFactory);
+
+
+ALCbackendFactory *ALCwaveBackendFactory_getFactory(void)
+{
+    static ALCwaveBackendFactory factory = ALCWAVEBACKENDFACTORY_INITIALIZER;
+    return STATIC_CAST(ALCbackendFactory, &factory);
+}
+
+
+static ALCboolean ALCwaveBackendFactory_init(ALCwaveBackendFactory* UNUSED(self))
+{
+    return ALC_TRUE;
+}
+
+static ALCboolean ALCwaveBackendFactory_querySupport(ALCwaveBackendFactory* UNUSED(self), ALCbackend_Type type)
+{
+    if(type == ALCbackend_Playback)
+        return !!ConfigValueExists(NULL, "wave", "file");
+    return ALC_FALSE;
+}
+
+static void ALCwaveBackendFactory_probe(ALCwaveBackendFactory* UNUSED(self), enum DevProbe type)
+{
+    switch(type)
+    {
+        case ALL_DEVICE_PROBE:
+            AppendAllDevicesList(waveDevice);
+            break;
+        case CAPTURE_DEVICE_PROBE:
+            break;
+    }
+}
+
+static ALCbackend* ALCwaveBackendFactory_createBackend(ALCwaveBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
+{
+    if(type == ALCbackend_Playback)
+    {
+        ALCwaveBackend *backend;
+        NEW_OBJ(backend, ALCwaveBackend)(device);
+        if(!backend) return NULL;
+        return STATIC_CAST(ALCbackend, backend);
+    }
+
+    return NULL;
+}

+ 803 - 0
Engine/lib/openal-soft/Alc/backends/winmm.c

@@ -0,0 +1,803 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 1999-2007 by authors.
+ * This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the
+ *  Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <memory.h>
+
+#include <windows.h>
+#include <mmsystem.h>
+
+#include "alMain.h"
+#include "alu.h"
+#include "threads.h"
+
+#include "backends/base.h"
+
+#ifndef WAVE_FORMAT_IEEE_FLOAT
+#define WAVE_FORMAT_IEEE_FLOAT  0x0003
+#endif
+
+#define DEVNAME_HEAD "OpenAL Soft on "
+
+
+static vector_al_string PlaybackDevices;
+static vector_al_string CaptureDevices;
+
+static void clear_devlist(vector_al_string *list)
+{
+    VECTOR_FOR_EACH(al_string, *list, al_string_deinit);
+    VECTOR_RESIZE(*list, 0, 0);
+}
+
+
+static void ProbePlaybackDevices(void)
+{
+    ALuint numdevs;
+    ALuint i;
+
+    clear_devlist(&PlaybackDevices);
+
+    numdevs = waveOutGetNumDevs();
+    VECTOR_RESIZE(PlaybackDevices, 0, numdevs);
+    for(i = 0;i < numdevs;i++)
+    {
+        WAVEOUTCAPSW WaveCaps;
+        const al_string *iter;
+        al_string dname;
+
+        AL_STRING_INIT(dname);
+        if(waveOutGetDevCapsW(i, &WaveCaps, sizeof(WaveCaps)) == MMSYSERR_NOERROR)
+        {
+            ALuint count = 0;
+            while(1)
+            {
+                al_string_copy_cstr(&dname, DEVNAME_HEAD);
+                al_string_append_wcstr(&dname, WaveCaps.szPname);
+                if(count != 0)
+                {
+                    char str[64];
+                    snprintf(str, sizeof(str), " #%d", count+1);
+                    al_string_append_cstr(&dname, str);
+                }
+                count++;
+
+#define MATCH_ENTRY(i) (al_string_cmp(dname, *(i)) == 0)
+                VECTOR_FIND_IF(iter, const al_string, PlaybackDevices, MATCH_ENTRY);
+                if(iter == VECTOR_END(PlaybackDevices)) break;
+#undef MATCH_ENTRY
+            }
+
+            TRACE("Got device \"%s\", ID %u\n", al_string_get_cstr(dname), i);
+        }
+        VECTOR_PUSH_BACK(PlaybackDevices, dname);
+    }
+}
+
+static void ProbeCaptureDevices(void)
+{
+    ALuint numdevs;
+    ALuint i;
+
+    clear_devlist(&CaptureDevices);
+
+    numdevs = waveInGetNumDevs();
+    VECTOR_RESIZE(CaptureDevices, 0, numdevs);
+    for(i = 0;i < numdevs;i++)
+    {
+        WAVEINCAPSW WaveCaps;
+        const al_string *iter;
+        al_string dname;
+
+        AL_STRING_INIT(dname);
+        if(waveInGetDevCapsW(i, &WaveCaps, sizeof(WaveCaps)) == MMSYSERR_NOERROR)
+        {
+            ALuint count = 0;
+            while(1)
+            {
+                al_string_copy_cstr(&dname, DEVNAME_HEAD);
+                al_string_append_wcstr(&dname, WaveCaps.szPname);
+                if(count != 0)
+                {
+                    char str[64];
+                    snprintf(str, sizeof(str), " #%d", count+1);
+                    al_string_append_cstr(&dname, str);
+                }
+                count++;
+
+#define MATCH_ENTRY(i) (al_string_cmp(dname, *(i)) == 0)
+                VECTOR_FIND_IF(iter, const al_string, CaptureDevices, MATCH_ENTRY);
+                if(iter == VECTOR_END(CaptureDevices)) break;
+#undef MATCH_ENTRY
+            }
+
+            TRACE("Got device \"%s\", ID %u\n", al_string_get_cstr(dname), i);
+        }
+        VECTOR_PUSH_BACK(CaptureDevices, dname);
+    }
+}
+
+
+typedef struct ALCwinmmPlayback {
+    DERIVE_FROM_TYPE(ALCbackend);
+
+    RefCount WaveBuffersCommitted;
+    WAVEHDR WaveBuffer[4];
+
+    HWAVEOUT OutHdl;
+
+    WAVEFORMATEX Format;
+
+    volatile ALboolean killNow;
+    althrd_t thread;
+} ALCwinmmPlayback;
+
+static void ALCwinmmPlayback_Construct(ALCwinmmPlayback *self, ALCdevice *device);
+static void ALCwinmmPlayback_Destruct(ALCwinmmPlayback *self);
+
+static void CALLBACK ALCwinmmPlayback_waveOutProc(HWAVEOUT device, UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR param2);
+static int ALCwinmmPlayback_mixerProc(void *arg);
+
+static ALCenum ALCwinmmPlayback_open(ALCwinmmPlayback *self, const ALCchar *name);
+static void ALCwinmmPlayback_close(ALCwinmmPlayback *self);
+static ALCboolean ALCwinmmPlayback_reset(ALCwinmmPlayback *self);
+static ALCboolean ALCwinmmPlayback_start(ALCwinmmPlayback *self);
+static void ALCwinmmPlayback_stop(ALCwinmmPlayback *self);
+static DECLARE_FORWARD2(ALCwinmmPlayback, ALCbackend, ALCenum, captureSamples, ALCvoid*, ALCuint)
+static DECLARE_FORWARD(ALCwinmmPlayback, ALCbackend, ALCuint, availableSamples)
+static DECLARE_FORWARD(ALCwinmmPlayback, ALCbackend, ClockLatency, getClockLatency)
+static DECLARE_FORWARD(ALCwinmmPlayback, ALCbackend, void, lock)
+static DECLARE_FORWARD(ALCwinmmPlayback, ALCbackend, void, unlock)
+DECLARE_DEFAULT_ALLOCATORS(ALCwinmmPlayback)
+
+DEFINE_ALCBACKEND_VTABLE(ALCwinmmPlayback);
+
+
+static void ALCwinmmPlayback_Construct(ALCwinmmPlayback *self, ALCdevice *device)
+{
+    ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
+    SET_VTABLE2(ALCwinmmPlayback, ALCbackend, self);
+
+    InitRef(&self->WaveBuffersCommitted, 0);
+    self->OutHdl = NULL;
+
+    self->killNow = AL_TRUE;
+}
+
+static void ALCwinmmPlayback_Destruct(ALCwinmmPlayback *self)
+{
+    if(self->OutHdl)
+        waveOutClose(self->OutHdl);
+    self->OutHdl = 0;
+
+    ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
+}
+
+
+/* ALCwinmmPlayback_waveOutProc
+ *
+ * Posts a message to 'ALCwinmmPlayback_mixerProc' everytime a WaveOut Buffer
+ * is completed and returns to the application (for more data)
+ */
+static void CALLBACK ALCwinmmPlayback_waveOutProc(HWAVEOUT UNUSED(device), UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR UNUSED(param2))
+{
+    ALCwinmmPlayback *self = (ALCwinmmPlayback*)instance;
+
+    if(msg != WOM_DONE)
+        return;
+
+    DecrementRef(&self->WaveBuffersCommitted);
+    PostThreadMessage(self->thread, msg, 0, param1);
+}
+
+FORCE_ALIGN static int ALCwinmmPlayback_mixerProc(void *arg)
+{
+    ALCwinmmPlayback *self = arg;
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+    WAVEHDR *WaveHdr;
+    MSG msg;
+
+    SetRTPriority();
+    althrd_setname(althrd_current(), MIXER_THREAD_NAME);
+
+    while(GetMessage(&msg, NULL, 0, 0))
+    {
+        if(msg.message != WOM_DONE)
+            continue;
+
+        if(self->killNow)
+        {
+            if(ReadRef(&self->WaveBuffersCommitted) == 0)
+                break;
+            continue;
+        }
+
+        WaveHdr = ((WAVEHDR*)msg.lParam);
+        aluMixData(device, WaveHdr->lpData, WaveHdr->dwBufferLength /
+                                            self->Format.nBlockAlign);
+
+        // Send buffer back to play more data
+        waveOutWrite(self->OutHdl, WaveHdr, sizeof(WAVEHDR));
+        IncrementRef(&self->WaveBuffersCommitted);
+    }
+
+    return 0;
+}
+
+
+static ALCenum ALCwinmmPlayback_open(ALCwinmmPlayback *self, const ALCchar *deviceName)
+{
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+    const al_string *iter;
+    UINT DeviceID;
+    MMRESULT res;
+
+    if(VECTOR_SIZE(PlaybackDevices) == 0)
+        ProbePlaybackDevices();
+
+    // Find the Device ID matching the deviceName if valid
+#define MATCH_DEVNAME(iter) (!al_string_empty(*(iter)) && \
+                             (!deviceName || al_string_cmp_cstr(*(iter), deviceName) == 0))
+    VECTOR_FIND_IF(iter, const al_string, PlaybackDevices, MATCH_DEVNAME);
+    if(iter == VECTOR_END(PlaybackDevices))
+        return ALC_INVALID_VALUE;
+#undef MATCH_DEVNAME
+
+    DeviceID = (UINT)(iter - VECTOR_BEGIN(PlaybackDevices));
+
+retry_open:
+    memset(&self->Format, 0, sizeof(WAVEFORMATEX));
+    if(device->FmtType == DevFmtFloat)
+    {
+        self->Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
+        self->Format.wBitsPerSample = 32;
+    }
+    else
+    {
+        self->Format.wFormatTag = WAVE_FORMAT_PCM;
+        if(device->FmtType == DevFmtUByte || device->FmtType == DevFmtByte)
+            self->Format.wBitsPerSample = 8;
+        else
+            self->Format.wBitsPerSample = 16;
+    }
+    self->Format.nChannels = ((device->FmtChans == DevFmtMono) ? 1 : 2);
+    self->Format.nBlockAlign = self->Format.wBitsPerSample *
+                               self->Format.nChannels / 8;
+    self->Format.nSamplesPerSec = device->Frequency;
+    self->Format.nAvgBytesPerSec = self->Format.nSamplesPerSec *
+                                   self->Format.nBlockAlign;
+    self->Format.cbSize = 0;
+
+    if((res=waveOutOpen(&self->OutHdl, DeviceID, &self->Format, (DWORD_PTR)&ALCwinmmPlayback_waveOutProc, (DWORD_PTR)self, CALLBACK_FUNCTION)) != MMSYSERR_NOERROR)
+    {
+        if(device->FmtType == DevFmtFloat)
+        {
+            device->FmtType = DevFmtShort;
+            goto retry_open;
+        }
+        ERR("waveOutOpen failed: %u\n", res);
+        goto failure;
+    }
+
+    al_string_copy(&device->DeviceName, VECTOR_ELEM(PlaybackDevices, DeviceID));
+    return ALC_NO_ERROR;
+
+failure:
+    if(self->OutHdl)
+        waveOutClose(self->OutHdl);
+    self->OutHdl = NULL;
+
+    return ALC_INVALID_VALUE;
+}
+
+static void ALCwinmmPlayback_close(ALCwinmmPlayback* UNUSED(self))
+{ }
+
+static ALCboolean ALCwinmmPlayback_reset(ALCwinmmPlayback *self)
+{
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+
+    device->UpdateSize = (ALuint)((ALuint64)device->UpdateSize *
+                                  self->Format.nSamplesPerSec /
+                                  device->Frequency);
+    device->UpdateSize = (device->UpdateSize*device->NumUpdates + 3) / 4;
+    device->NumUpdates = 4;
+    device->Frequency = self->Format.nSamplesPerSec;
+
+    if(self->Format.wFormatTag == WAVE_FORMAT_IEEE_FLOAT)
+    {
+        if(self->Format.wBitsPerSample == 32)
+            device->FmtType = DevFmtFloat;
+        else
+        {
+            ERR("Unhandled IEEE float sample depth: %d\n", self->Format.wBitsPerSample);
+            return ALC_FALSE;
+        }
+    }
+    else if(self->Format.wFormatTag == WAVE_FORMAT_PCM)
+    {
+        if(self->Format.wBitsPerSample == 16)
+            device->FmtType = DevFmtShort;
+        else if(self->Format.wBitsPerSample == 8)
+            device->FmtType = DevFmtUByte;
+        else
+        {
+            ERR("Unhandled PCM sample depth: %d\n", self->Format.wBitsPerSample);
+            return ALC_FALSE;
+        }
+    }
+    else
+    {
+        ERR("Unhandled format tag: 0x%04x\n", self->Format.wFormatTag);
+        return ALC_FALSE;
+    }
+
+    if(self->Format.nChannels == 2)
+        device->FmtChans = DevFmtStereo;
+    else if(self->Format.nChannels == 1)
+        device->FmtChans = DevFmtMono;
+    else
+    {
+        ERR("Unhandled channel count: %d\n", self->Format.nChannels);
+        return ALC_FALSE;
+    }
+    SetDefaultWFXChannelOrder(device);
+
+    return ALC_TRUE;
+}
+
+static ALCboolean ALCwinmmPlayback_start(ALCwinmmPlayback *self)
+{
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+    ALbyte *BufferData;
+    ALint BufferSize;
+    ALuint i;
+
+    self->killNow = AL_FALSE;
+    if(althrd_create(&self->thread, ALCwinmmPlayback_mixerProc, self) != althrd_success)
+        return ALC_FALSE;
+
+    InitRef(&self->WaveBuffersCommitted, 0);
+
+    // Create 4 Buffers
+    BufferSize  = device->UpdateSize*device->NumUpdates / 4;
+    BufferSize *= FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
+
+    BufferData = calloc(4, BufferSize);
+    for(i = 0;i < 4;i++)
+    {
+        memset(&self->WaveBuffer[i], 0, sizeof(WAVEHDR));
+        self->WaveBuffer[i].dwBufferLength = BufferSize;
+        self->WaveBuffer[i].lpData = ((i==0) ? (CHAR*)BufferData :
+                                      (self->WaveBuffer[i-1].lpData +
+                                       self->WaveBuffer[i-1].dwBufferLength));
+        waveOutPrepareHeader(self->OutHdl, &self->WaveBuffer[i], sizeof(WAVEHDR));
+        waveOutWrite(self->OutHdl, &self->WaveBuffer[i], sizeof(WAVEHDR));
+        IncrementRef(&self->WaveBuffersCommitted);
+    }
+
+    return ALC_TRUE;
+}
+
+static void ALCwinmmPlayback_stop(ALCwinmmPlayback *self)
+{
+    void *buffer = NULL;
+    int i;
+
+    if(self->killNow)
+        return;
+
+    // Set flag to stop processing headers
+    self->killNow = AL_TRUE;
+    althrd_join(self->thread, &i);
+
+    // Release the wave buffers
+    for(i = 0;i < 4;i++)
+    {
+        waveOutUnprepareHeader(self->OutHdl, &self->WaveBuffer[i], sizeof(WAVEHDR));
+        if(i == 0) buffer = self->WaveBuffer[i].lpData;
+        self->WaveBuffer[i].lpData = NULL;
+    }
+    free(buffer);
+}
+
+
+
+typedef struct ALCwinmmCapture {
+    DERIVE_FROM_TYPE(ALCbackend);
+
+    RefCount WaveBuffersCommitted;
+    WAVEHDR WaveBuffer[4];
+
+    HWAVEIN InHdl;
+
+    ll_ringbuffer_t *Ring;
+
+    WAVEFORMATEX Format;
+
+    volatile ALboolean killNow;
+    althrd_t thread;
+} ALCwinmmCapture;
+
+static void ALCwinmmCapture_Construct(ALCwinmmCapture *self, ALCdevice *device);
+static void ALCwinmmCapture_Destruct(ALCwinmmCapture *self);
+
+static void CALLBACK ALCwinmmCapture_waveInProc(HWAVEIN device, UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR param2);
+static int ALCwinmmCapture_captureProc(void *arg);
+
+static ALCenum ALCwinmmCapture_open(ALCwinmmCapture *self, const ALCchar *name);
+static void ALCwinmmCapture_close(ALCwinmmCapture *self);
+static DECLARE_FORWARD(ALCwinmmCapture, ALCbackend, ALCboolean, reset)
+static ALCboolean ALCwinmmCapture_start(ALCwinmmCapture *self);
+static void ALCwinmmCapture_stop(ALCwinmmCapture *self);
+static ALCenum ALCwinmmCapture_captureSamples(ALCwinmmCapture *self, ALCvoid *buffer, ALCuint samples);
+static ALCuint ALCwinmmCapture_availableSamples(ALCwinmmCapture *self);
+static DECLARE_FORWARD(ALCwinmmCapture, ALCbackend, ClockLatency, getClockLatency)
+static DECLARE_FORWARD(ALCwinmmCapture, ALCbackend, void, lock)
+static DECLARE_FORWARD(ALCwinmmCapture, ALCbackend, void, unlock)
+DECLARE_DEFAULT_ALLOCATORS(ALCwinmmCapture)
+
+DEFINE_ALCBACKEND_VTABLE(ALCwinmmCapture);
+
+
+static void ALCwinmmCapture_Construct(ALCwinmmCapture *self, ALCdevice *device)
+{
+    ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
+    SET_VTABLE2(ALCwinmmCapture, ALCbackend, self);
+
+    InitRef(&self->WaveBuffersCommitted, 0);
+    self->InHdl = NULL;
+
+    self->killNow = AL_TRUE;
+}
+
+static void ALCwinmmCapture_Destruct(ALCwinmmCapture *self)
+{
+    if(self->InHdl)
+        waveInClose(self->InHdl);
+    self->InHdl = 0;
+
+    ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
+}
+
+
+/* ALCwinmmCapture_waveInProc
+ *
+ * Posts a message to 'ALCwinmmCapture_captureProc' everytime a WaveIn Buffer
+ * is completed and returns to the application (with more data).
+ */
+static void CALLBACK ALCwinmmCapture_waveInProc(HWAVEIN UNUSED(device), UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR UNUSED(param2))
+{
+    ALCwinmmCapture *self = (ALCwinmmCapture*)instance;
+
+    if(msg != WIM_DATA)
+        return;
+
+    DecrementRef(&self->WaveBuffersCommitted);
+    PostThreadMessage(self->thread, msg, 0, param1);
+}
+
+static int ALCwinmmCapture_captureProc(void *arg)
+{
+    ALCwinmmCapture *self = arg;
+    WAVEHDR *WaveHdr;
+    MSG msg;
+
+    althrd_setname(althrd_current(), RECORD_THREAD_NAME);
+
+    while(GetMessage(&msg, NULL, 0, 0))
+    {
+        if(msg.message != WIM_DATA)
+            continue;
+        /* Don't wait for other buffers to finish before quitting. We're
+         * closing so we don't need them. */
+        if(self->killNow)
+            break;
+
+        WaveHdr = ((WAVEHDR*)msg.lParam);
+        ll_ringbuffer_write(self->Ring, WaveHdr->lpData,
+            WaveHdr->dwBytesRecorded / self->Format.nBlockAlign
+        );
+
+        // Send buffer back to capture more data
+        waveInAddBuffer(self->InHdl, WaveHdr, sizeof(WAVEHDR));
+        IncrementRef(&self->WaveBuffersCommitted);
+    }
+
+    return 0;
+}
+
+
+static ALCenum ALCwinmmCapture_open(ALCwinmmCapture *self, const ALCchar *name)
+{
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+    const al_string *iter;
+    ALbyte *BufferData = NULL;
+    DWORD CapturedDataSize;
+    ALint BufferSize;
+    UINT DeviceID;
+    MMRESULT res;
+    ALuint i;
+
+    if(VECTOR_SIZE(CaptureDevices) == 0)
+        ProbeCaptureDevices();
+
+    // Find the Device ID matching the deviceName if valid
+#define MATCH_DEVNAME(iter) (!al_string_empty(*(iter)) &&  (!name || al_string_cmp_cstr(*iter, name) == 0))
+    VECTOR_FIND_IF(iter, const al_string, CaptureDevices, MATCH_DEVNAME);
+    if(iter == VECTOR_END(CaptureDevices))
+        return ALC_INVALID_VALUE;
+#undef MATCH_DEVNAME
+
+    DeviceID = (UINT)(iter - VECTOR_BEGIN(CaptureDevices));
+
+    switch(device->FmtChans)
+    {
+        case DevFmtMono:
+        case DevFmtStereo:
+            break;
+
+        case DevFmtQuad:
+        case DevFmtX51:
+        case DevFmtX51Rear:
+        case DevFmtX61:
+        case DevFmtX71:
+        case DevFmtAmbi1:
+        case DevFmtAmbi2:
+        case DevFmtAmbi3:
+            return ALC_INVALID_ENUM;
+    }
+
+    switch(device->FmtType)
+    {
+        case DevFmtUByte:
+        case DevFmtShort:
+        case DevFmtInt:
+        case DevFmtFloat:
+            break;
+
+        case DevFmtByte:
+        case DevFmtUShort:
+        case DevFmtUInt:
+            return ALC_INVALID_ENUM;
+    }
+
+    memset(&self->Format, 0, sizeof(WAVEFORMATEX));
+    self->Format.wFormatTag = ((device->FmtType == DevFmtFloat) ?
+                               WAVE_FORMAT_IEEE_FLOAT : WAVE_FORMAT_PCM);
+    self->Format.nChannels = ChannelsFromDevFmt(device->FmtChans);
+    self->Format.wBitsPerSample = BytesFromDevFmt(device->FmtType) * 8;
+    self->Format.nBlockAlign = self->Format.wBitsPerSample *
+                               self->Format.nChannels / 8;
+    self->Format.nSamplesPerSec = device->Frequency;
+    self->Format.nAvgBytesPerSec = self->Format.nSamplesPerSec *
+                                   self->Format.nBlockAlign;
+    self->Format.cbSize = 0;
+
+    if((res=waveInOpen(&self->InHdl, DeviceID, &self->Format, (DWORD_PTR)&ALCwinmmCapture_waveInProc, (DWORD_PTR)self, CALLBACK_FUNCTION)) != MMSYSERR_NOERROR)
+    {
+        ERR("waveInOpen failed: %u\n", res);
+        goto failure;
+    }
+
+    // Allocate circular memory buffer for the captured audio
+    CapturedDataSize = device->UpdateSize*device->NumUpdates;
+
+    // Make sure circular buffer is at least 100ms in size
+    if(CapturedDataSize < (self->Format.nSamplesPerSec / 10))
+        CapturedDataSize = self->Format.nSamplesPerSec / 10;
+
+    self->Ring = ll_ringbuffer_create(CapturedDataSize+1, self->Format.nBlockAlign);
+    if(!self->Ring) goto failure;
+
+    InitRef(&self->WaveBuffersCommitted, 0);
+
+    // Create 4 Buffers of 50ms each
+    BufferSize = self->Format.nAvgBytesPerSec / 20;
+    BufferSize -= (BufferSize % self->Format.nBlockAlign);
+
+    BufferData = calloc(4, BufferSize);
+    if(!BufferData) goto failure;
+
+    for(i = 0;i < 4;i++)
+    {
+        memset(&self->WaveBuffer[i], 0, sizeof(WAVEHDR));
+        self->WaveBuffer[i].dwBufferLength = BufferSize;
+        self->WaveBuffer[i].lpData = ((i==0) ? (CHAR*)BufferData :
+                                      (self->WaveBuffer[i-1].lpData +
+                                       self->WaveBuffer[i-1].dwBufferLength));
+        self->WaveBuffer[i].dwFlags = 0;
+        self->WaveBuffer[i].dwLoops = 0;
+        waveInPrepareHeader(self->InHdl, &self->WaveBuffer[i], sizeof(WAVEHDR));
+        waveInAddBuffer(self->InHdl, &self->WaveBuffer[i], sizeof(WAVEHDR));
+        IncrementRef(&self->WaveBuffersCommitted);
+    }
+
+    self->killNow = AL_FALSE;
+    if(althrd_create(&self->thread, ALCwinmmCapture_captureProc, self) != althrd_success)
+        goto failure;
+
+    al_string_copy(&device->DeviceName, VECTOR_ELEM(CaptureDevices, DeviceID));
+    return ALC_NO_ERROR;
+
+failure:
+    if(BufferData)
+    {
+        for(i = 0;i < 4;i++)
+            waveInUnprepareHeader(self->InHdl, &self->WaveBuffer[i], sizeof(WAVEHDR));
+        free(BufferData);
+    }
+
+    ll_ringbuffer_free(self->Ring);
+    self->Ring = NULL;
+
+    if(self->InHdl)
+        waveInClose(self->InHdl);
+    self->InHdl = NULL;
+
+    return ALC_INVALID_VALUE;
+}
+
+static void ALCwinmmCapture_close(ALCwinmmCapture *self)
+{
+    void *buffer = NULL;
+    int i;
+
+    /* Tell the processing thread to quit and wait for it to do so. */
+    self->killNow = AL_TRUE;
+    PostThreadMessage(self->thread, WM_QUIT, 0, 0);
+
+    althrd_join(self->thread, &i);
+
+    /* Make sure capture is stopped and all pending buffers are flushed. */
+    waveInReset(self->InHdl);
+
+    // Release the wave buffers
+    for(i = 0;i < 4;i++)
+    {
+        waveInUnprepareHeader(self->InHdl, &self->WaveBuffer[i], sizeof(WAVEHDR));
+        if(i == 0) buffer = self->WaveBuffer[i].lpData;
+        self->WaveBuffer[i].lpData = NULL;
+    }
+    free(buffer);
+
+    ll_ringbuffer_free(self->Ring);
+    self->Ring = NULL;
+
+    // Close the Wave device
+    waveInClose(self->InHdl);
+    self->InHdl = NULL;
+}
+
+static ALCboolean ALCwinmmCapture_start(ALCwinmmCapture *self)
+{
+    waveInStart(self->InHdl);
+    return ALC_TRUE;
+}
+
+static void ALCwinmmCapture_stop(ALCwinmmCapture *self)
+{
+    waveInStop(self->InHdl);
+}
+
+static ALCenum ALCwinmmCapture_captureSamples(ALCwinmmCapture *self, ALCvoid *buffer, ALCuint samples)
+{
+    ll_ringbuffer_read(self->Ring, buffer, samples);
+    return ALC_NO_ERROR;
+}
+
+static ALCuint ALCwinmmCapture_availableSamples(ALCwinmmCapture *self)
+{
+    return ll_ringbuffer_read_space(self->Ring);
+}
+
+
+static inline void AppendAllDevicesList2(const al_string *name)
+{
+    if(!al_string_empty(*name))
+        AppendAllDevicesList(al_string_get_cstr(*name));
+}
+static inline void AppendCaptureDeviceList2(const al_string *name)
+{
+    if(!al_string_empty(*name))
+        AppendCaptureDeviceList(al_string_get_cstr(*name));
+}
+
+typedef struct ALCwinmmBackendFactory {
+    DERIVE_FROM_TYPE(ALCbackendFactory);
+} ALCwinmmBackendFactory;
+#define ALCWINMMBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCwinmmBackendFactory, ALCbackendFactory) } }
+
+static ALCboolean ALCwinmmBackendFactory_init(ALCwinmmBackendFactory *self);
+static void ALCwinmmBackendFactory_deinit(ALCwinmmBackendFactory *self);
+static ALCboolean ALCwinmmBackendFactory_querySupport(ALCwinmmBackendFactory *self, ALCbackend_Type type);
+static void ALCwinmmBackendFactory_probe(ALCwinmmBackendFactory *self, enum DevProbe type);
+static ALCbackend* ALCwinmmBackendFactory_createBackend(ALCwinmmBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
+
+DEFINE_ALCBACKENDFACTORY_VTABLE(ALCwinmmBackendFactory);
+
+
+static ALCboolean ALCwinmmBackendFactory_init(ALCwinmmBackendFactory* UNUSED(self))
+{
+    VECTOR_INIT(PlaybackDevices);
+    VECTOR_INIT(CaptureDevices);
+
+    return ALC_TRUE;
+}
+
+static void ALCwinmmBackendFactory_deinit(ALCwinmmBackendFactory* UNUSED(self))
+{
+    clear_devlist(&PlaybackDevices);
+    VECTOR_DEINIT(PlaybackDevices);
+
+    clear_devlist(&CaptureDevices);
+    VECTOR_DEINIT(CaptureDevices);
+}
+
+static ALCboolean ALCwinmmBackendFactory_querySupport(ALCwinmmBackendFactory* UNUSED(self), ALCbackend_Type type)
+{
+    if(type == ALCbackend_Playback || type == ALCbackend_Capture)
+        return ALC_TRUE;
+    return ALC_FALSE;
+}
+
+static void ALCwinmmBackendFactory_probe(ALCwinmmBackendFactory* UNUSED(self), enum DevProbe type)
+{
+    switch(type)
+    {
+        case ALL_DEVICE_PROBE:
+            ProbePlaybackDevices();
+            VECTOR_FOR_EACH(const al_string, PlaybackDevices, AppendAllDevicesList2);
+            break;
+
+        case CAPTURE_DEVICE_PROBE:
+            ProbeCaptureDevices();
+            VECTOR_FOR_EACH(const al_string, CaptureDevices, AppendCaptureDeviceList2);
+            break;
+    }
+}
+
+static ALCbackend* ALCwinmmBackendFactory_createBackend(ALCwinmmBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
+{
+    if(type == ALCbackend_Playback)
+    {
+        ALCwinmmPlayback *backend;
+        NEW_OBJ(backend, ALCwinmmPlayback)(device);
+        if(!backend) return NULL;
+        return STATIC_CAST(ALCbackend, backend);
+    }
+    if(type == ALCbackend_Capture)
+    {
+        ALCwinmmCapture *backend;
+        NEW_OBJ(backend, ALCwinmmCapture)(device);
+        if(!backend) return NULL;
+        return STATIC_CAST(ALCbackend, backend);
+    }
+
+    return NULL;
+}
+
+ALCbackendFactory *ALCwinmmBackendFactory_getFactory(void)
+{
+    static ALCwinmmBackendFactory factory = ALCWINMMBACKENDFACTORY_INITIALIZER;
+    return STATIC_CAST(ALCbackendFactory, &factory);
+}

+ 670 - 0
Engine/lib/openal-soft/Alc/bformatdec.c

@@ -0,0 +1,670 @@
+
+#include "config.h"
+
+#include "bformatdec.h"
+#include "ambdec.h"
+#include "mixer_defs.h"
+#include "alu.h"
+
+#include "threads.h"
+#include "almalloc.h"
+
+
+void bandsplit_init(BandSplitter *splitter, ALfloat freq_mult)
+{
+    ALfloat w = freq_mult * F_TAU;
+    ALfloat cw = cosf(w);
+    if(cw > FLT_EPSILON)
+        splitter->coeff = (sinf(w) - 1.0f) / cw;
+    else
+        splitter->coeff = cw * -0.5f;
+
+    splitter->lp_z1 = 0.0f;
+    splitter->lp_z2 = 0.0f;
+    splitter->hp_z1 = 0.0f;
+}
+
+void bandsplit_clear(BandSplitter *splitter)
+{
+    splitter->lp_z1 = 0.0f;
+    splitter->lp_z2 = 0.0f;
+    splitter->hp_z1 = 0.0f;
+}
+
+void bandsplit_process(BandSplitter *splitter, ALfloat *restrict hpout, ALfloat *restrict lpout,
+                       const ALfloat *input, ALuint count)
+{
+    ALfloat coeff, d, x;
+    ALfloat z1, z2;
+    ALuint i;
+
+    coeff = splitter->coeff*0.5f + 0.5f;
+    z1 = splitter->lp_z1;
+    z2 = splitter->lp_z2;
+    for(i = 0;i < count;i++)
+    {
+        x = input[i];
+
+        d = (x - z1) * coeff;
+        x = z1 + d;
+        z1 = x + d;
+
+        d = (x - z2) * coeff;
+        x = z2 + d;
+        z2 = x + d;
+
+        lpout[i] = x;
+    }
+    splitter->lp_z1 = z1;
+    splitter->lp_z2 = z2;
+
+    coeff = splitter->coeff;
+    z1 = splitter->hp_z1;
+    for(i = 0;i < count;i++)
+    {
+        x = input[i];
+
+        d = x - coeff*z1;
+        x = z1 + coeff*d;
+        z1 = d;
+
+        hpout[i] = x - lpout[i];
+    }
+    splitter->hp_z1 = z1;
+}
+
+
+static const ALfloat UnitScale[MAX_AMBI_COEFFS] = {
+    1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
+    1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f
+};
+static const ALfloat SN3D2N3DScale[MAX_AMBI_COEFFS] = {
+    1.000000000f, /* ACN  0 (W), sqrt(1) */
+    1.732050808f, /* ACN  1 (Y), sqrt(3) */
+    1.732050808f, /* ACN  2 (Z), sqrt(3) */
+    1.732050808f, /* ACN  3 (X), sqrt(3) */
+    2.236067978f, /* ACN  4 (V), sqrt(5) */
+    2.236067978f, /* ACN  5 (T), sqrt(5) */
+    2.236067978f, /* ACN  6 (R), sqrt(5) */
+    2.236067978f, /* ACN  7 (S), sqrt(5) */
+    2.236067978f, /* ACN  8 (U), sqrt(5) */
+    2.645751311f, /* ACN  9 (Q), sqrt(7) */
+    2.645751311f, /* ACN 10 (O), sqrt(7) */
+    2.645751311f, /* ACN 11 (M), sqrt(7) */
+    2.645751311f, /* ACN 12 (K), sqrt(7) */
+    2.645751311f, /* ACN 13 (L), sqrt(7) */
+    2.645751311f, /* ACN 14 (N), sqrt(7) */
+    2.645751311f, /* ACN 15 (P), sqrt(7) */
+};
+static const ALfloat FuMa2N3DScale[MAX_AMBI_COEFFS] = {
+    1.414213562f, /* ACN  0 (W), sqrt(2) */
+    1.732050808f, /* ACN  1 (Y), sqrt(3) */
+    1.732050808f, /* ACN  2 (Z), sqrt(3) */
+    1.732050808f, /* ACN  3 (X), sqrt(3) */
+    1.936491673f, /* ACN  4 (V), sqrt(15)/2 */
+    1.936491673f, /* ACN  5 (T), sqrt(15)/2 */
+    2.236067978f, /* ACN  6 (R), sqrt(5) */
+    1.936491673f, /* ACN  7 (S), sqrt(15)/2 */
+    1.936491673f, /* ACN  8 (U), sqrt(15)/2 */
+    2.091650066f, /* ACN  9 (Q), sqrt(35/8) */
+    1.972026594f, /* ACN 10 (O), sqrt(35)/3 */
+    2.231093404f, /* ACN 11 (M), sqrt(224/45) */
+    2.645751311f, /* ACN 12 (K), sqrt(7) */
+    2.231093404f, /* ACN 13 (L), sqrt(224/45) */
+    1.972026594f, /* ACN 14 (N), sqrt(35)/3 */
+    2.091650066f, /* ACN 15 (P), sqrt(35/8) */
+};
+
+
+enum FreqBand {
+    FB_HighFreq,
+    FB_LowFreq,
+    FB_Max
+};
+
+/* These points are in AL coordinates! */
+static const ALfloat Ambi2DPoints[4][3] = {
+    { -0.707106781f,  0.0f, -0.707106781f },
+    {  0.707106781f,  0.0f, -0.707106781f },
+    { -0.707106781f,  0.0f,  0.707106781f },
+    {  0.707106781f,  0.0f,  0.707106781f },
+};
+static const ALfloat Ambi2DDecoder[4][FB_Max][MAX_AMBI_COEFFS] = {
+    { { 0.353553f,  0.204094f, 0.0f,  0.204094f }, { 0.25f,  0.204094f, 0.0f,  0.204094f } },
+    { { 0.353553f, -0.204094f, 0.0f,  0.204094f }, { 0.25f, -0.204094f, 0.0f,  0.204094f } },
+    { { 0.353553f,  0.204094f, 0.0f, -0.204094f }, { 0.25f,  0.204094f, 0.0f, -0.204094f } },
+    { { 0.353553f, -0.204094f, 0.0f, -0.204094f }, { 0.25f, -0.204094f, 0.0f, -0.204094f } },
+};
+static ALfloat Ambi2DEncoder[4][MAX_AMBI_COEFFS];
+
+/* These points are in AL coordinates! */
+static const ALfloat Ambi3DPoints[8][3] = {
+    { -0.577350269f,  0.577350269f, -0.577350269f },
+    {  0.577350269f,  0.577350269f, -0.577350269f },
+    { -0.577350269f,  0.577350269f,  0.577350269f },
+    {  0.577350269f,  0.577350269f,  0.577350269f },
+    { -0.577350269f, -0.577350269f, -0.577350269f },
+    {  0.577350269f, -0.577350269f, -0.577350269f },
+    { -0.577350269f, -0.577350269f,  0.577350269f },
+    {  0.577350269f, -0.577350269f,  0.577350269f },
+};
+static const ALfloat Ambi3DDecoder[8][FB_Max][MAX_AMBI_COEFFS] = {
+    { { 0.25f,  0.1443375672f,  0.1443375672f,  0.1443375672f }, { 0.125f,  0.125f,  0.125f,  0.125f } },
+    { { 0.25f, -0.1443375672f,  0.1443375672f,  0.1443375672f }, { 0.125f, -0.125f,  0.125f,  0.125f } },
+    { { 0.25f,  0.1443375672f,  0.1443375672f, -0.1443375672f }, { 0.125f,  0.125f,  0.125f, -0.125f } },
+    { { 0.25f, -0.1443375672f,  0.1443375672f, -0.1443375672f }, { 0.125f, -0.125f,  0.125f, -0.125f } },
+    { { 0.25f,  0.1443375672f, -0.1443375672f,  0.1443375672f }, { 0.125f,  0.125f, -0.125f,  0.125f } },
+    { { 0.25f, -0.1443375672f, -0.1443375672f,  0.1443375672f }, { 0.125f, -0.125f, -0.125f,  0.125f } },
+    { { 0.25f,  0.1443375672f, -0.1443375672f, -0.1443375672f }, { 0.125f,  0.125f, -0.125f, -0.125f } },
+    { { 0.25f, -0.1443375672f, -0.1443375672f, -0.1443375672f }, { 0.125f, -0.125f, -0.125f, -0.125f } },
+};
+static ALfloat Ambi3DEncoder[8][MAX_AMBI_COEFFS];
+
+
+static RowMixerFunc MixMatrixRow = MixRow_C;
+
+
+static alonce_flag bformatdec_inited = AL_ONCE_FLAG_INIT;
+
+static void init_bformatdec(void)
+{
+    ALuint i, j;
+
+    MixMatrixRow = SelectRowMixer();
+
+    for(i = 0;i < COUNTOF(Ambi3DPoints);i++)
+        CalcDirectionCoeffs(Ambi3DPoints[i], 0.0f, Ambi3DEncoder[i]);
+
+    for(i = 0;i < COUNTOF(Ambi2DPoints);i++)
+    {
+        CalcDirectionCoeffs(Ambi2DPoints[i], 0.0f, Ambi2DEncoder[i]);
+
+        /* Remove the skipped height-related coefficients for 2D rendering. */
+        Ambi2DEncoder[i][2] = Ambi2DEncoder[i][3];
+        Ambi2DEncoder[i][3] = Ambi2DEncoder[i][4];
+        Ambi2DEncoder[i][4] = Ambi2DEncoder[i][8];
+        Ambi2DEncoder[i][5] = Ambi2DEncoder[i][9];
+        Ambi2DEncoder[i][6] = Ambi2DEncoder[i][15];
+        for(j = 7;j < MAX_AMBI_COEFFS;j++)
+            Ambi2DEncoder[i][j] = 0.0f;
+    }
+}
+
+
+#define MAX_DELAY_LENGTH 128
+
+/* NOTE: BandSplitter filters are unused with single-band decoding */
+typedef struct BFormatDec {
+    ALboolean Enabled[MAX_OUTPUT_CHANNELS];
+
+    union {
+        alignas(16) ALfloat Dual[MAX_OUTPUT_CHANNELS][FB_Max][MAX_AMBI_COEFFS];
+        alignas(16) ALfloat Single[MAX_OUTPUT_CHANNELS][MAX_AMBI_COEFFS];
+    } Matrix;
+
+    BandSplitter XOver[MAX_AMBI_COEFFS];
+
+    ALfloat (*Samples)[BUFFERSIZE];
+    /* These two alias into Samples */
+    ALfloat (*SamplesHF)[BUFFERSIZE];
+    ALfloat (*SamplesLF)[BUFFERSIZE];
+
+    alignas(16) ALfloat ChannelMix[BUFFERSIZE];
+
+    struct {
+        alignas(16) ALfloat Buffer[MAX_DELAY_LENGTH];
+        ALuint Length; /* Valid range is [0...MAX_DELAY_LENGTH). */
+    } Delay[MAX_OUTPUT_CHANNELS];
+
+    struct {
+        BandSplitter XOver[4];
+
+        ALfloat Gains[4][MAX_OUTPUT_CHANNELS][FB_Max];
+    } UpSampler;
+
+    ALuint NumChannels;
+    ALboolean DualBand;
+    ALboolean Periphonic;
+} BFormatDec;
+
+BFormatDec *bformatdec_alloc()
+{
+    alcall_once(&bformatdec_inited, init_bformatdec);
+    return al_calloc(16, sizeof(BFormatDec));
+}
+
+void bformatdec_free(BFormatDec *dec)
+{
+    if(dec)
+    {
+        al_free(dec->Samples);
+        dec->Samples = NULL;
+        dec->SamplesHF = NULL;
+        dec->SamplesLF = NULL;
+
+        memset(dec, 0, sizeof(*dec));
+        al_free(dec);
+    }
+}
+
+int bformatdec_getOrder(const struct BFormatDec *dec)
+{
+    if(dec->Periphonic)
+    {
+        if(dec->NumChannels > 9) return 3;
+        if(dec->NumChannels > 4) return 2;
+        if(dec->NumChannels > 1) return 1;
+    }
+    else
+    {
+        if(dec->NumChannels > 5) return 3;
+        if(dec->NumChannels > 3) return 2;
+        if(dec->NumChannels > 1) return 1;
+    }
+    return 0;
+}
+
+void bformatdec_reset(BFormatDec *dec, const AmbDecConf *conf, ALuint chancount, ALuint srate, const ALuint chanmap[MAX_OUTPUT_CHANNELS], int flags)
+{
+    static const ALuint map2DTo3D[MAX_AMBI2D_COEFFS] = {
+        0,  1, 3,  4, 8,  9, 15
+    };
+    const ALfloat *coeff_scale = UnitScale;
+    ALfloat distgain[MAX_OUTPUT_CHANNELS];
+    ALfloat maxdist, ratio;
+    ALuint i, j, k;
+
+    al_free(dec->Samples);
+    dec->Samples = NULL;
+    dec->SamplesHF = NULL;
+    dec->SamplesLF = NULL;
+
+    dec->NumChannels = chancount;
+    dec->Samples = al_calloc(16, dec->NumChannels*2 * sizeof(dec->Samples[0]));
+    dec->SamplesHF = dec->Samples;
+    dec->SamplesLF = dec->SamplesHF + dec->NumChannels;
+
+    for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
+        dec->Enabled[i] = AL_FALSE;
+    for(i = 0;i < conf->NumSpeakers;i++)
+        dec->Enabled[chanmap[i]] = AL_TRUE;
+
+    if(conf->CoeffScale == ADS_SN3D)
+        coeff_scale = SN3D2N3DScale;
+    else if(conf->CoeffScale == ADS_FuMa)
+        coeff_scale = FuMa2N3DScale;
+
+    ratio = 400.0f / (ALfloat)srate;
+    for(i = 0;i < 4;i++)
+        bandsplit_init(&dec->UpSampler.XOver[i], ratio);
+    memset(dec->UpSampler.Gains, 0, sizeof(dec->UpSampler.Gains));
+    if((conf->ChanMask&AMBI_PERIPHONIC_MASK))
+    {
+        /* Combine the matrices that do the in->virt and virt->out conversions
+         * so we get a single in->out conversion.
+         */
+        for(i = 0;i < 4;i++)
+        {
+            for(j = 0;j < dec->NumChannels;j++)
+            {
+                ALfloat *gains = dec->UpSampler.Gains[i][j];
+                for(k = 0;k < COUNTOF(Ambi3DDecoder);k++)
+                {
+                    gains[FB_HighFreq] += Ambi3DDecoder[k][FB_HighFreq][i]*Ambi3DEncoder[k][j];
+                    gains[FB_LowFreq] += Ambi3DDecoder[k][FB_LowFreq][i]*Ambi3DEncoder[k][j];
+                }
+            }
+        }
+
+        dec->Periphonic = AL_TRUE;
+    }
+    else
+    {
+        for(i = 0;i < 4;i++)
+        {
+            for(j = 0;j < dec->NumChannels;j++)
+            {
+                ALfloat *gains = dec->UpSampler.Gains[i][j];
+                for(k = 0;k < COUNTOF(Ambi2DDecoder);k++)
+                {
+                    gains[FB_HighFreq] += Ambi2DDecoder[k][FB_HighFreq][i]*Ambi2DEncoder[k][j];
+                    gains[FB_LowFreq] += Ambi2DDecoder[k][FB_LowFreq][i]*Ambi2DEncoder[k][j];
+                }
+            }
+        }
+
+        dec->Periphonic = AL_FALSE;
+    }
+
+    maxdist = 0.0f;
+    for(i = 0;i < conf->NumSpeakers;i++)
+    {
+        maxdist = maxf(maxdist, conf->Speakers[i].Distance);
+        distgain[i] = 1.0f;
+    }
+
+    memset(dec->Delay, 0, sizeof(dec->Delay));
+    if((flags&BFDF_DistanceComp) && maxdist > 0.0f)
+    {
+        for(i = 0;i < conf->NumSpeakers;i++)
+        {
+            ALuint chan = chanmap[i];
+            ALfloat delay;
+
+            /* Distance compensation only delays in steps of the sample rate.
+             * This is a bit less accurate since the delay time falls to the
+             * nearest sample time, but it's far simpler as it doesn't have to
+             * deal with phase offsets. This means at 48khz, for instance, the
+             * distance delay will be in steps of about 7 millimeters.
+             */
+            delay = floorf((maxdist-conf->Speakers[i].Distance) / SPEEDOFSOUNDMETRESPERSEC *
+                           (ALfloat)srate + 0.5f);
+            if(delay >= (ALfloat)MAX_DELAY_LENGTH)
+                ERR("Delay for speaker \"%s\" exceeds buffer length (%f >= %u)\n",
+                    al_string_get_cstr(conf->Speakers[i].Name), delay, MAX_DELAY_LENGTH);
+
+            dec->Delay[chan].Length = (ALuint)clampf(delay, 0.0f, (ALfloat)(MAX_DELAY_LENGTH-1));
+            distgain[i] = conf->Speakers[i].Distance / maxdist;
+            TRACE("Channel %u \"%s\" distance compensation: %u samples, %f gain\n", chan,
+                al_string_get_cstr(conf->Speakers[i].Name), dec->Delay[chan].Length, distgain[i]
+            );
+        }
+    }
+
+    memset(&dec->Matrix, 0, sizeof(dec->Matrix));
+    if(conf->FreqBands == 1)
+    {
+        dec->DualBand = AL_FALSE;
+        for(i = 0;i < conf->NumSpeakers;i++)
+        {
+            ALuint chan = chanmap[i];
+            ALfloat gain;
+            ALuint j, k;
+
+            if(!dec->Periphonic)
+            {
+                for(j = 0,k = 0;j < MAX_AMBI2D_COEFFS;j++)
+                {
+                    ALuint l = map2DTo3D[j];
+                    if(j == 0) gain = conf->HFOrderGain[0];
+                    else if(j == 1) gain = conf->HFOrderGain[1];
+                    else if(j == 3) gain = conf->HFOrderGain[2];
+                    else if(j == 5) gain = conf->HFOrderGain[3];
+                    if((conf->ChanMask&(1<<l)))
+                        dec->Matrix.Single[chan][j] = conf->HFMatrix[i][k++] / coeff_scale[l] *
+                                                      gain * distgain[i];
+                }
+            }
+            else
+            {
+                for(j = 0,k = 0;j < MAX_AMBI_COEFFS;j++)
+                {
+                    if(j == 0) gain = conf->HFOrderGain[0];
+                    else if(j == 1) gain = conf->HFOrderGain[1];
+                    else if(j == 4) gain = conf->HFOrderGain[2];
+                    else if(j == 9) gain = conf->HFOrderGain[3];
+                    if((conf->ChanMask&(1<<j)))
+                        dec->Matrix.Single[chan][j] = conf->HFMatrix[i][k++] / coeff_scale[j] *
+                                                      gain * distgain[i];
+                }
+            }
+        }
+    }
+    else
+    {
+        dec->DualBand = AL_TRUE;
+
+        ratio = conf->XOverFreq / (ALfloat)srate;
+        for(i = 0;i < MAX_AMBI_COEFFS;i++)
+            bandsplit_init(&dec->XOver[i], ratio);
+
+        ratio = powf(10.0f, conf->XOverRatio / 40.0f);
+        for(i = 0;i < conf->NumSpeakers;i++)
+        {
+            ALuint chan = chanmap[i];
+            ALfloat gain;
+            ALuint j, k;
+
+            if(!dec->Periphonic)
+            {
+                for(j = 0,k = 0;j < MAX_AMBI2D_COEFFS;j++)
+                {
+                    ALuint l = map2DTo3D[j];
+                    if(j == 0) gain = conf->HFOrderGain[0] * ratio;
+                    else if(j == 1) gain = conf->HFOrderGain[1] * ratio;
+                    else if(j == 3) gain = conf->HFOrderGain[2] * ratio;
+                    else if(j == 5) gain = conf->HFOrderGain[3] * ratio;
+                    if((conf->ChanMask&(1<<l)))
+                        dec->Matrix.Dual[chan][FB_HighFreq][j] = conf->HFMatrix[i][k++] /
+                                                                 coeff_scale[l] * gain *
+                                                                 distgain[i];
+                }
+                for(j = 0,k = 0;j < MAX_AMBI2D_COEFFS;j++)
+                {
+                    ALuint l = map2DTo3D[j];
+                    if(j == 0) gain = conf->LFOrderGain[0] / ratio;
+                    else if(j == 1) gain = conf->LFOrderGain[1] / ratio;
+                    else if(j == 3) gain = conf->LFOrderGain[2] / ratio;
+                    else if(j == 5) gain = conf->LFOrderGain[3] / ratio;
+                    if((conf->ChanMask&(1<<l)))
+                        dec->Matrix.Dual[chan][FB_LowFreq][j] = conf->LFMatrix[i][k++] /
+                                                                coeff_scale[l] * gain *
+                                                                distgain[i];
+                }
+            }
+            else
+            {
+                for(j = 0,k = 0;j < MAX_AMBI_COEFFS;j++)
+                {
+                    if(j == 0) gain = conf->HFOrderGain[0] * ratio;
+                    else if(j == 1) gain = conf->HFOrderGain[1] * ratio;
+                    else if(j == 4) gain = conf->HFOrderGain[2] * ratio;
+                    else if(j == 9) gain = conf->HFOrderGain[3] * ratio;
+                    if((conf->ChanMask&(1<<j)))
+                        dec->Matrix.Dual[chan][FB_HighFreq][j] = conf->HFMatrix[i][k++] /
+                                                                 coeff_scale[j] * gain *
+                                                                 distgain[i];
+                }
+                for(j = 0,k = 0;j < MAX_AMBI_COEFFS;j++)
+                {
+                    if(j == 0) gain = conf->LFOrderGain[0] / ratio;
+                    else if(j == 1) gain = conf->LFOrderGain[1] / ratio;
+                    else if(j == 4) gain = conf->LFOrderGain[2] / ratio;
+                    else if(j == 9) gain = conf->LFOrderGain[3] / ratio;
+                    if((conf->ChanMask&(1<<j)))
+                        dec->Matrix.Dual[chan][FB_LowFreq][j] = conf->LFMatrix[i][k++] /
+                                                                coeff_scale[j] * gain *
+                                                                distgain[i];
+                }
+            }
+        }
+    }
+}
+
+
+void bformatdec_process(struct BFormatDec *dec, ALfloat (*restrict OutBuffer)[BUFFERSIZE], ALuint OutChannels, const ALfloat (*restrict InSamples)[BUFFERSIZE], ALuint SamplesToDo)
+{
+    ALuint chan, i;
+
+    if(dec->DualBand)
+    {
+        for(i = 0;i < dec->NumChannels;i++)
+            bandsplit_process(&dec->XOver[i], dec->SamplesHF[i], dec->SamplesLF[i],
+                              InSamples[i], SamplesToDo);
+
+        for(chan = 0;chan < OutChannels;chan++)
+        {
+            if(!dec->Enabled[chan])
+                continue;
+
+            memset(dec->ChannelMix, 0, SamplesToDo*sizeof(ALfloat));
+            MixMatrixRow(dec->ChannelMix, dec->Matrix.Dual[chan][FB_HighFreq],
+                SAFE_CONST(ALfloatBUFFERSIZE*,dec->SamplesHF), dec->NumChannels, 0,
+                SamplesToDo
+            );
+            MixMatrixRow(dec->ChannelMix, dec->Matrix.Dual[chan][FB_LowFreq],
+                SAFE_CONST(ALfloatBUFFERSIZE*,dec->SamplesLF), dec->NumChannels, 0,
+                SamplesToDo
+            );
+
+            if(dec->Delay[chan].Length > 0)
+            {
+                const ALuint base = dec->Delay[chan].Length;
+                if(SamplesToDo >= base)
+                {
+                    for(i = 0;i < base;i++)
+                        OutBuffer[chan][i] += dec->Delay[chan].Buffer[i];
+                    for(;i < SamplesToDo;i++)
+                        OutBuffer[chan][i] += dec->ChannelMix[i-base];
+                    memcpy(dec->Delay[chan].Buffer, &dec->ChannelMix[SamplesToDo-base],
+                           base*sizeof(ALfloat));
+                }
+                else
+                {
+                    for(i = 0;i < SamplesToDo;i++)
+                        OutBuffer[chan][i] += dec->Delay[chan].Buffer[i];
+                    memmove(dec->Delay[chan].Buffer, dec->Delay[chan].Buffer+SamplesToDo,
+                            base - SamplesToDo);
+                    memcpy(dec->Delay[chan].Buffer+base-SamplesToDo, dec->ChannelMix,
+                           SamplesToDo*sizeof(ALfloat));
+                }
+            }
+            else for(i = 0;i < SamplesToDo;i++)
+                OutBuffer[chan][i] += dec->ChannelMix[i];
+        }
+    }
+    else
+    {
+        for(chan = 0;chan < OutChannels;chan++)
+        {
+            if(!dec->Enabled[chan])
+                continue;
+
+            memset(dec->ChannelMix, 0, SamplesToDo*sizeof(ALfloat));
+            MixMatrixRow(dec->ChannelMix, dec->Matrix.Single[chan], InSamples,
+                         dec->NumChannels, 0, SamplesToDo);
+
+            if(dec->Delay[chan].Length > 0)
+            {
+                const ALuint base = dec->Delay[chan].Length;
+                if(SamplesToDo >= base)
+                {
+                    for(i = 0;i < base;i++)
+                        OutBuffer[chan][i] += dec->Delay[chan].Buffer[i];
+                    for(;i < SamplesToDo;i++)
+                        OutBuffer[chan][i] += dec->ChannelMix[i-base];
+                    memcpy(dec->Delay[chan].Buffer, &dec->ChannelMix[SamplesToDo-base],
+                           base*sizeof(ALfloat));
+                }
+                else
+                {
+                    for(i = 0;i < SamplesToDo;i++)
+                        OutBuffer[chan][i] += dec->Delay[chan].Buffer[i];
+                    memmove(dec->Delay[chan].Buffer, dec->Delay[chan].Buffer+SamplesToDo,
+                            base - SamplesToDo);
+                    memcpy(dec->Delay[chan].Buffer+base-SamplesToDo, dec->ChannelMix,
+                           SamplesToDo*sizeof(ALfloat));
+                }
+            }
+            else for(i = 0;i < SamplesToDo;i++)
+                OutBuffer[chan][i] += dec->ChannelMix[i];
+        }
+    }
+}
+
+
+void bformatdec_upSample(struct BFormatDec *dec, ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat (*restrict InSamples)[BUFFERSIZE], ALuint InChannels, ALuint SamplesToDo)
+{
+    ALuint i, j;
+
+    /* This up-sampler is very simplistic. It essentially decodes the first-
+     * order content to a square channel array (or cube if height is desired),
+     * then encodes those points onto the higher order soundfield. The decoder
+     * and encoder matrices have been combined to directly convert each input
+     * channel to the output, without the need for storing the virtual channel
+     * array.
+     */
+    for(i = 0;i < InChannels;i++)
+    {
+        /* First, split the first-order components into low and high frequency
+         * bands.
+         */
+        bandsplit_process(&dec->UpSampler.XOver[i],
+            dec->Samples[FB_HighFreq], dec->Samples[FB_LowFreq],
+            InSamples[i], SamplesToDo
+        );
+
+        /* Now write each band to the output. */
+        for(j = 0;j < dec->NumChannels;j++)
+            MixMatrixRow(OutBuffer[j], dec->UpSampler.Gains[i][j],
+                SAFE_CONST(ALfloatBUFFERSIZE*,dec->Samples), FB_Max, 0,
+                SamplesToDo
+            );
+    }
+}
+
+
+typedef struct AmbiUpsampler {
+    alignas(16) ALfloat Samples[FB_Max][BUFFERSIZE];
+
+    BandSplitter XOver[4];
+
+    ALfloat Gains[4][MAX_OUTPUT_CHANNELS][FB_Max];
+} AmbiUpsampler;
+
+AmbiUpsampler *ambiup_alloc()
+{
+    alcall_once(&bformatdec_inited, init_bformatdec);
+    return al_calloc(16, sizeof(AmbiUpsampler));
+}
+
+void ambiup_free(struct AmbiUpsampler *ambiup)
+{
+    al_free(ambiup);
+}
+
+void ambiup_reset(struct AmbiUpsampler *ambiup, const ALCdevice *device)
+{
+    ALfloat gains[8][MAX_OUTPUT_CHANNELS];
+    ALfloat ratio;
+    ALuint i, j, k;
+
+    ratio = 400.0f / (ALfloat)device->Frequency;
+    for(i = 0;i < 4;i++)
+        bandsplit_init(&ambiup->XOver[i], ratio);
+
+    for(i = 0;i < COUNTOF(Ambi3DEncoder);i++)
+        ComputePanningGains(device->Dry, Ambi3DEncoder[i], 1.0f, gains[i]);
+
+    memset(ambiup->Gains, 0, sizeof(ambiup->Gains));
+    for(i = 0;i < 4;i++)
+    {
+        for(j = 0;j < device->Dry.NumChannels;j++)
+        {
+            for(k = 0;k < COUNTOF(Ambi3DDecoder);k++)
+            {
+                ambiup->Gains[i][j][FB_HighFreq] += Ambi3DDecoder[k][FB_HighFreq][i]*gains[k][j];
+                ambiup->Gains[i][j][FB_LowFreq] += Ambi3DDecoder[k][FB_LowFreq][i]*gains[k][j];
+            }
+        }
+    }
+}
+
+void ambiup_process(struct AmbiUpsampler *ambiup, ALfloat (*restrict OutBuffer)[BUFFERSIZE], ALuint OutChannels, const ALfloat (*restrict InSamples)[BUFFERSIZE], ALuint SamplesToDo)
+{
+    ALuint i, j;
+
+    for(i = 0;i < 4;i++)
+    {
+        bandsplit_process(&ambiup->XOver[i],
+            ambiup->Samples[FB_HighFreq], ambiup->Samples[FB_LowFreq],
+            InSamples[i], SamplesToDo
+        );
+
+        for(j = 0;j < OutChannels;j++)
+            MixMatrixRow(OutBuffer[j], ambiup->Gains[i][j],
+                SAFE_CONST(ALfloatBUFFERSIZE*,ambiup->Samples), FB_Max, 0,
+                SamplesToDo
+            );
+    }
+}

+ 49 - 0
Engine/lib/openal-soft/Alc/bformatdec.h

@@ -0,0 +1,49 @@
+#ifndef BFORMATDEC_H
+#define BFORMATDEC_H
+
+#include "alMain.h"
+
+struct AmbDecConf;
+struct BFormatDec;
+struct AmbiUpsampler;
+
+enum BFormatDecFlags {
+    BFDF_DistanceComp = 1<<0
+};
+
+struct BFormatDec *bformatdec_alloc();
+void bformatdec_free(struct BFormatDec *dec);
+int bformatdec_getOrder(const struct BFormatDec *dec);
+void bformatdec_reset(struct BFormatDec *dec, const struct AmbDecConf *conf, ALuint chancount, ALuint srate, const ALuint chanmap[MAX_OUTPUT_CHANNELS], int flags);
+
+/* Decodes the ambisonic input to the given output channels. */
+void bformatdec_process(struct BFormatDec *dec, ALfloat (*restrict OutBuffer)[BUFFERSIZE], ALuint OutChannels, const ALfloat (*restrict InSamples)[BUFFERSIZE], ALuint SamplesToDo);
+
+/* Up-samples a first-order input to the decoder's configuration. */
+void bformatdec_upSample(struct BFormatDec *dec, ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat (*restrict InSamples)[BUFFERSIZE], ALuint InChannels, ALuint SamplesToDo);
+
+
+/* Stand-alone first-order upsampler. Kept here because it shares some stuff
+ * with bformatdec.
+ */
+struct AmbiUpsampler *ambiup_alloc();
+void ambiup_free(struct AmbiUpsampler *ambiup);
+void ambiup_reset(struct AmbiUpsampler *ambiup, const ALCdevice *device);
+
+void ambiup_process(struct AmbiUpsampler *ambiup, ALfloat (*restrict OutBuffer)[BUFFERSIZE], ALuint OutChannels, const ALfloat (*restrict InSamples)[BUFFERSIZE], ALuint SamplesToDo);
+
+
+/* Band splitter. Splits a signal into two phase-matching frequency bands. */
+typedef struct BandSplitter {
+    ALfloat coeff;
+    ALfloat lp_z1;
+    ALfloat lp_z2;
+    ALfloat hp_z1;
+} BandSplitter;
+
+void bandsplit_init(BandSplitter *splitter, ALfloat freq_mult);
+void bandsplit_clear(BandSplitter *splitter);
+void bandsplit_process(BandSplitter *splitter, ALfloat *restrict hpout, ALfloat *restrict lpout,
+                       const ALfloat *input, ALuint count);
+
+#endif /* BFORMATDEC_H */

+ 187 - 0
Engine/lib/openal-soft/Alc/bs2b.c

@@ -0,0 +1,187 @@
+/*-
+ * Copyright (c) 2005 Boris Mikhaylov
+ *
+ * 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.
+ */
+
+#include "config.h"
+
+#include <math.h>
+#include <string.h>
+
+#include "bs2b.h"
+#include "alu.h"
+
+
+/* Set up all data. */
+static void init(struct bs2b *bs2b)
+{
+    float Fc_lo, Fc_hi;
+    float G_lo,  G_hi;
+    float x, g;
+
+    switch(bs2b->level)
+    {
+    case BS2B_LOW_CLEVEL: /* Low crossfeed level */
+        Fc_lo = 360.0f;
+        Fc_hi = 501.0f;
+        G_lo  = 0.398107170553497f;
+        G_hi  = 0.205671765275719f;
+        break;
+
+    case BS2B_MIDDLE_CLEVEL: /* Middle crossfeed level */
+        Fc_lo = 500.0f;
+        Fc_hi = 711.0f;
+        G_lo  = 0.459726988530872f;
+        G_hi  = 0.228208484414988f;
+        break;
+
+    case BS2B_HIGH_CLEVEL: /* High crossfeed level (virtual speakers are closer to itself) */
+        Fc_lo = 700.0f;
+        Fc_hi = 1021.0f;
+        G_lo  = 0.530884444230988f;
+        G_hi  = 0.250105790667544f;
+        break;
+
+    case BS2B_LOW_ECLEVEL: /* Low easy crossfeed level */
+        Fc_lo = 360.0f;
+        Fc_hi = 494.0f;
+        G_lo  = 0.316227766016838f;
+        G_hi  = 0.168236228897329f;
+        break;
+
+    case BS2B_MIDDLE_ECLEVEL: /* Middle easy crossfeed level */
+        Fc_lo = 500.0f;
+        Fc_hi = 689.0f;
+        G_lo  = 0.354813389233575f;
+        G_hi  = 0.187169483835901f;
+        break;
+
+    default: /* High easy crossfeed level */
+        bs2b->level = BS2B_HIGH_ECLEVEL;
+
+        Fc_lo = 700.0f;
+        Fc_hi = 975.0f;
+        G_lo  = 0.398107170553497f;
+        G_hi  = 0.205671765275719f;
+        break;
+    } /* switch */
+
+    g = 1.0f / (1.0f - G_hi + G_lo);
+
+    /* $fc = $Fc / $s;
+     * $d  = 1 / 2 / pi / $fc;
+     * $x  = exp(-1 / $d);
+     */
+    x           = expf(-2.0f * F_PI * Fc_lo / bs2b->srate);
+    bs2b->b1_lo = x;
+    bs2b->a0_lo = G_lo * (1.0f - x) * g;
+
+    x           = expf(-2.0f * F_PI * Fc_hi / bs2b->srate);
+    bs2b->b1_hi = x;
+    bs2b->a0_hi = (1.0f - G_hi * (1.0f - x)) * g;
+    bs2b->a1_hi = -x * g;
+} /* init */
+
+
+/* Exported functions.
+ * See descriptions in "bs2b.h"
+ */
+
+void bs2b_set_params(struct bs2b *bs2b, int level, int srate)
+{
+    if(srate <= 0) srate = 1;
+
+    bs2b->level = level;
+    bs2b->srate = srate;
+    init(bs2b);
+} /* bs2b_set_params */
+
+int bs2b_get_level(struct bs2b *bs2b)
+{
+    return bs2b->level;
+} /* bs2b_get_level */
+
+int bs2b_get_srate(struct bs2b *bs2b)
+{
+    return bs2b->srate;
+} /* bs2b_get_srate */
+
+void bs2b_clear(struct bs2b *bs2b)
+{
+    memset(&bs2b->last_sample, 0, sizeof(bs2b->last_sample));
+} /* bs2b_clear */
+
+void bs2b_cross_feed(struct bs2b *bs2b, float *restrict Left, float *restrict Right, unsigned int SamplesToDo)
+{
+    float lsamples[128][2];
+    float rsamples[128][2];
+    unsigned int base;
+
+    for(base = 0;base < SamplesToDo;)
+    {
+        unsigned int todo = minu(128, SamplesToDo-base);
+        unsigned int i;
+
+        /* Process left input */
+        lsamples[0][0] = bs2b->a0_lo*Left[0] +
+                         bs2b->b1_lo*bs2b->last_sample[0].lo;
+        lsamples[0][1] = bs2b->a0_hi*Left[0] +
+                         bs2b->a1_hi*bs2b->last_sample[0].asis +
+                         bs2b->b1_hi*bs2b->last_sample[0].hi;
+        for(i = 1;i < todo;i++)
+        {
+            lsamples[i][0] = bs2b->a0_lo*Left[i] +
+                             bs2b->b1_lo*lsamples[i-1][0];
+            lsamples[i][1] = bs2b->a0_hi*Left[i] +
+                             bs2b->a1_hi*Left[i-1] +
+                             bs2b->b1_hi*lsamples[i-1][1];
+        }
+        bs2b->last_sample[0].asis = Left[i-1];
+        bs2b->last_sample[0].lo = lsamples[i-1][0];
+        bs2b->last_sample[0].hi = lsamples[i-1][1];
+
+        /* Process right input */
+        rsamples[0][0] = bs2b->a0_lo*Right[0] +
+                         bs2b->b1_lo*bs2b->last_sample[1].lo;
+        rsamples[0][1] = bs2b->a0_hi*Right[0] +
+                         bs2b->a1_hi*bs2b->last_sample[1].asis +
+                         bs2b->b1_hi*bs2b->last_sample[1].hi;
+        for(i = 1;i < todo;i++)
+        {
+            rsamples[i][0] = bs2b->a0_lo*Right[i] +
+                             bs2b->b1_lo*rsamples[i-1][0];
+            rsamples[i][1] = bs2b->a0_hi*Right[i] +
+                             bs2b->a1_hi*Right[i-1] +
+                             bs2b->b1_hi*rsamples[i-1][1];
+        }
+        bs2b->last_sample[1].asis = Right[i-1];
+        bs2b->last_sample[1].lo = rsamples[i-1][0];
+        bs2b->last_sample[1].hi = rsamples[i-1][1];
+
+        /* Crossfeed */
+        for(i = 0;i < todo;i++)
+            *(Left++) = lsamples[i][1] + rsamples[i][0];
+        for(i = 0;i < todo;i++)
+            *(Right++) = rsamples[i][1] + lsamples[i][0];
+
+        base += todo;
+    }
+} /* bs2b_cross_feed */

+ 981 - 0
Engine/lib/openal-soft/Alc/bsinc.c

@@ -0,0 +1,981 @@
+
+#include "config.h"
+
+#include "AL/al.h"
+#include "align.h"
+
+/* Table of windowed sinc coefficients and deltas.  This 11th order filter
+ * has a rejection of -60 dB, yielding a transition width of ~0.302
+ * (normalized frequency).  Order increases when downsampling to a limit of
+ * one octave, after which the quality of the filter (transition width)
+ * suffers to reduce the CPU cost.  The bandlimiting will cut all sound after
+ * downsampling by ~2.73 octaves.
+ */
+alignas(16) const ALfloat bsincTab[18840] =
+{
+    /* 24, 0 */ +0.000000000e+00f, +0.000000000e+00f, +0.000000000e+00f, +0.000000000e+00f, +0.000000000e+00f, +0.000000000e+00f, +0.000000000e+00f, +0.000000000e+00f, +0.000000000e+00f, +0.000000000e+00f, +0.000000000e+00f, +0.000000000e+00f, +0.000000000e+00f, +0.000000000e+00f, +0.000000000e+00f, +0.000000000e+00f, +0.000000000e+00f, +0.000000000e+00f, +0.000000000e+00f, +0.000000000e+00f, +0.000000000e+00f, +0.000000000e+00f, +0.000000000e+00f, +0.000000000e+00f,
+
+    /* 24, 0 */ +1.501390780e-03f, +3.431804419e-03f, +6.512803185e-03f, +1.091425387e-02f, +1.664594540e-02f, +2.351091132e-02f, +3.109255671e-02f, +3.878419288e-02f, +4.586050701e-02f, +5.158058002e-02f, +5.530384985e-02f, +5.659614054e-02f, +5.530384985e-02f, +5.158058002e-02f, +4.586050701e-02f, +3.878419288e-02f, +3.109255671e-02f, +2.351091132e-02f, +1.664594540e-02f, +1.091425387e-02f, +6.512803185e-03f, +3.431804419e-03f, +1.501390780e-03f, +4.573885647e-04f,
+    /* 24, 1 */ +1.413186400e-03f, +3.279858311e-03f, +6.282638036e-03f, +1.059932179e-02f, +1.625135142e-02f, +2.305547031e-02f, +3.060840342e-02f, +3.831365198e-02f, +4.545054680e-02f, +5.127577001e-02f, +5.513916011e-02f, +5.659104154e-02f, +5.545895049e-02f, +5.187752167e-02f, +4.626513642e-02f, +3.925233583e-02f, +3.157717954e-02f, +2.396921539e-02f, +1.704503934e-02f, +1.123445076e-02f, +6.748179094e-03f, +3.588275667e-03f, +1.593065611e-03f, +5.022154476e-04f,
+    /* 24, 2 */ +1.328380648e-03f, +3.132379333e-03f, +6.057656813e-03f, +1.028967374e-02f, +1.586133102e-02f, +2.260301890e-02f, +3.012488684e-02f, +3.784089895e-02f, +4.503543229e-02f, +5.096323022e-02f, +5.496495842e-02f, +5.657574693e-02f, +5.560438923e-02f, +5.216645963e-02f, +4.666426010e-02f, +3.971789474e-02f, +3.206210284e-02f, +2.443025293e-02f, +1.744855617e-02f, +1.155988996e-02f, +6.988790100e-03f, +3.749328623e-03f, +1.688282347e-03f, +5.494305796e-04f,
+    /* 24, 3 */ +1.246901403e-03f, +2.989308098e-03f, +5.837830254e-03f, +9.985325752e-03f, +1.547595434e-02f, +2.215368059e-02f, +2.964217216e-02f, +3.736611920e-02f, +4.461534144e-02f, +5.064310236e-02f, +5.478132634e-02f, +5.655026396e-02f, +5.574009777e-02f, +5.244726189e-02f, +4.705770477e-02f, +4.018068337e-02f, +3.254715574e-02f, +2.489389144e-02f, +1.785641537e-02f, +1.189054572e-02f, +7.234657995e-03f, +3.915018340e-03f, +1.787112015e-03f, +5.991047395e-04f,
+    /* 24, 4 */ +1.168676301e-03f, +2.850583915e-03f, +5.623126723e-03f, +9.686290690e-03f, +1.509528803e-02f, +2.170757578e-02f, +2.916042250e-02f, +3.688949768e-02f, +4.419045351e-02f, +5.031553118e-02f, +5.458834968e-02f, +5.651460469e-02f, +5.586601230e-02f, +5.271979985e-02f, +4.744529894e-02f, +4.064051541e-02f, +3.303216567e-02f, +2.535999546e-02f, +1.826853297e-02f, +1.222638897e-02f, +7.485801959e-03f, +4.085398290e-03f, +1.889625146e-03f, +6.513091287e-04f,
+    /* 24, 5 */ +1.093632798e-03f, +2.716144855e-03f, +5.413512274e-03f, +9.392578266e-03f, +1.471939531e-02f, +2.126482169e-02f, +2.867979883e-02f, +3.641121873e-02f, +4.376094899e-02f, +4.998066438e-02f, +5.438611851e-02f, +5.646878599e-02f, +5.598207354e-02f, +5.298394839e-02f, +4.782687301e-02f, +4.109720465e-02f, +3.351695842e-02f, +2.582842673e-02f, +1.868482156e-02f, +1.256738733e-02f, +7.742238512e-03f, +4.260520294e-03f, +1.995891717e-03f, +7.061153220e-04f,
+    /* 24, 6 */ +1.021698233e-03f, +2.585927824e-03f, +5.208950715e-03f, +9.104195104e-03f, +1.434833590e-02f, +2.082553239e-02f, +2.820045990e-02f, +3.593146595e-02f, +4.332700946e-02f, +4.963865252e-02f, +5.417472708e-02f, +5.641282954e-02f, +5.608822683e-02f, +5.323958602e-02f, +4.820225940e-02f, +4.155056502e-02f, +3.400135826e-02f, +2.629904416e-02f, +1.910519032e-02f, +1.291350505e-02f, +8.003981455e-03f, +4.440434453e-03f, +2.105981077e-03f, +7.635952183e-04f,
+    /* 24, 7 */ +9.527998831e-04f, +2.459868628e-03f, +5.009403670e-03f, +8.821144768e-03f, +1.398216608e-02f, +2.038981869e-02f, +2.772256216e-02f, +3.545042216e-02f, +4.288881749e-02f, +4.928964888e-02f, +5.395427373e-02f, +5.634676181e-02f, +5.618442211e-02f, +5.348659488e-02f, +4.857129262e-02f, +4.200041076e-02f, +3.448518802e-02f, +2.677170395e-02f, +1.952954505e-02f, +1.326470299e-02f, +8.271041819e-03f, +4.625189083e-03f, +2.219961884e-03f, +8.238209888e-04f,
+    /* 24, 8 */ +8.868650246e-04f, +2.337902042e-03f, +4.814830642e-03f, +8.543427812e-03f, +1.362093865e-02f, +1.995778816e-02f, +2.724625964e-02f, +3.496826923e-02f, +4.244655653e-02f, +4.893380942e-02f, +5.372486088e-02f, +5.627061400e-02f, +5.627061400e-02f, +5.372486088e-02f, +4.893380942e-02f, +4.244655653e-02f, +3.496826923e-02f, +2.724625964e-02f, +1.995778816e-02f, +1.362093865e-02f, +8.543427812e-03f, +4.814830642e-03f, +2.337902042e-03f, +8.868650246e-04f,
+    /* 24, 9 */ +8.238209888e-04f, +2.219961884e-03f, +4.625189083e-03f, +8.271041819e-03f, +1.326470299e-02f, +1.952954505e-02f, +2.677170395e-02f, +3.448518802e-02f, +4.200041076e-02f, +4.857129262e-02f, +5.348659488e-02f, +5.618442211e-02f, +5.634676181e-02f, +5.395427373e-02f, +4.928964888e-02f, +4.288881749e-02f, +3.545042216e-02f, +2.772256216e-02f, +2.038981869e-02f, +1.398216608e-02f, +8.821144768e-03f, +5.009403670e-03f, +2.459868628e-03f, +9.527998831e-04f,
+    /* 24,10 */ +7.635952183e-04f, +2.105981077e-03f, +4.440434453e-03f, +8.003981455e-03f, +1.291350505e-02f, +1.910519032e-02f, +2.629904416e-02f, +3.400135826e-02f, +4.155056502e-02f, +4.820225940e-02f, +5.323958602e-02f, +5.608822683e-02f, +5.641282954e-02f, +5.417472708e-02f, +4.963865252e-02f, +4.332700946e-02f, +3.593146595e-02f, +2.820045990e-02f, +2.082553239e-02f, +1.434833590e-02f, +9.104195104e-03f, +5.208950715e-03f, +2.585927824e-03f, +1.021698233e-03f,
+    /* 24,11 */ +7.061153220e-04f, +1.995891717e-03f, +4.260520294e-03f, +7.742238512e-03f, +1.256738733e-02f, +1.868482156e-02f, +2.582842673e-02f, +3.351695842e-02f, +4.109720465e-02f, +4.782687301e-02f, +5.298394839e-02f, +5.598207354e-02f, +5.646878599e-02f, +5.438611851e-02f, +4.998066438e-02f, +4.376094899e-02f, +3.641121873e-02f, +2.867979883e-02f, +2.126482169e-02f, +1.471939531e-02f, +9.392578266e-03f, +5.413512274e-03f, +2.716144855e-03f, +1.093632798e-03f,
+    /* 24,12 */ +6.513091287e-04f, +1.889625146e-03f, +4.085398290e-03f, +7.485801959e-03f, +1.222638897e-02f, +1.826853297e-02f, +2.535999546e-02f, +3.303216567e-02f, +4.064051541e-02f, +4.744529894e-02f, +5.271979985e-02f, +5.586601230e-02f, +5.651460469e-02f, +5.458834968e-02f, +5.031553118e-02f, +4.419045351e-02f, +3.688949768e-02f, +2.916042250e-02f, +2.170757578e-02f, +1.509528803e-02f, +9.686290690e-03f, +5.623126723e-03f, +2.850583915e-03f, +1.168676301e-03f,
+    /* 24,13 */ +5.991047395e-04f, +1.787112015e-03f, +3.915018340e-03f, +7.234657995e-03f, +1.189054572e-02f, +1.785641537e-02f, +2.489389144e-02f, +3.254715574e-02f, +4.018068337e-02f, +4.705770477e-02f, +5.244726189e-02f, +5.574009777e-02f, +5.655026396e-02f, +5.478132634e-02f, +5.064310236e-02f, +4.461534144e-02f, +3.736611920e-02f, +2.964217216e-02f, +2.215368059e-02f, +1.547595434e-02f, +9.985325752e-03f, +5.837830254e-03f, +2.989308098e-03f, +1.246901403e-03f,
+    /* 24,14 */ +5.494305796e-04f, +1.688282347e-03f, +3.749328623e-03f, +6.988790100e-03f, +1.155988996e-02f, +1.744855617e-02f, +2.443025293e-02f, +3.206210284e-02f, +3.971789474e-02f, +4.666426010e-02f, +5.216645963e-02f, +5.560438923e-02f, +5.657574693e-02f, +5.496495842e-02f, +5.096323022e-02f, +4.503543229e-02f, +3.784089895e-02f, +3.012488684e-02f, +2.260301890e-02f, +1.586133102e-02f, +1.028967374e-02f, +6.057656813e-03f, +3.132379333e-03f, +1.328380648e-03f,
+    /* 24,15 */ +5.022154476e-04f, +1.593065611e-03f, +3.588275667e-03f, +6.748179094e-03f, +1.123445076e-02f, +1.704503934e-02f, +2.396921539e-02f, +3.157717954e-02f, +3.925233583e-02f, +4.626513642e-02f, +5.187752167e-02f, +5.545895049e-02f, +5.659104154e-02f, +5.513916011e-02f, +5.127577001e-02f, +4.545054680e-02f, +3.831365198e-02f, +3.060840342e-02f, +2.305547031e-02f, +1.625135142e-02f, +1.059932179e-02f, +6.282638036e-03f, +3.279858311e-03f, +1.413186400e-03f,
+    /* 24, 0 */ -1.127794091e-03f, -1.412146034e-03f, -3.831821143e-04f, +3.227045776e-03f, +1.066768284e-02f, +2.270769386e-02f, +3.918787347e-02f, +5.876378120e-02f, +7.897914846e-02f, +9.670702233e-02f, +1.088639494e-01f, +1.131922811e-01f, +1.088639494e-01f, +9.670702233e-02f, +7.897914846e-02f, +5.876378120e-02f, +3.918787347e-02f, +2.270769386e-02f, +1.066768284e-02f, +3.227045776e-03f, -3.831821143e-04f, -1.412146034e-03f, -1.127794091e-03f, -4.881068065e-04f,
+    /* 24, 1 */ -1.090580766e-03f, -1.420873386e-03f, -5.091873886e-04f, +2.900756187e-03f, +1.007202248e-02f, +2.181774373e-02f, +3.804709217e-02f, +5.749143322e-02f, +7.775467878e-02f, +9.573284944e-02f, +1.083163184e-01f, +1.131750947e-01f, +1.093805191e-01f, +9.765915788e-02f, +8.019389052e-02f, +6.003885715e-02f, +4.034112484e-02f, +2.361538773e-02f, +1.128161899e-02f, +3.568453927e-03f, -2.470940015e-04f, -1.398398357e-03f, -1.163769773e-03f, -5.264712252e-04f,
+    /* 24, 2 */ -1.052308046e-03f, -1.424863695e-03f, -6.254426725e-04f, +2.589304991e-03f, +9.494532203e-03f, +2.094570441e-02f, +3.691925193e-02f, +5.622252667e-02f, +7.652128881e-02f, +9.473734332e-02f, +1.077380418e-01f, +1.131235487e-01f, +1.098656350e-01f, +9.858856505e-02f, +8.139809812e-02f, +6.131593665e-02f, +4.150635732e-02f, +2.454063933e-02f, +1.191392194e-02f, +3.925253463e-03f, -1.005901154e-04f, -1.379341793e-03f, -1.198322390e-03f, -5.655319713e-04f,
+    /* 24, 3 */ -1.013146991e-03f, -1.424394748e-03f, -7.322803183e-04f, +2.292405169e-03f, +8.935092066e-03f, +2.009172411e-02f, +3.580480556e-02f, +5.495776346e-02f, +7.527978553e-02f, +9.372122042e-02f, +1.071295572e-01f, +1.130376830e-01f, +1.103189277e-01f, +9.949456662e-02f, +8.259096568e-02f, +6.259428489e-02f, +4.268306423e-02f, +2.548324354e-02f, +1.256466766e-02f, +4.297709369e-03f, +5.666214823e-05f, -1.354682733e-03f, -1.231259367e-03f, -6.052075404e-04f,
+    /* 24, 4 */ -9.732614753e-04f, -1.419738627e-03f, -8.300317840e-04f, +2.009763334e-03f, +8.393568260e-03f, +1.925593236e-02f, +3.470418745e-02f, +5.369783330e-02f, +7.403097485e-02f, +9.268520876e-02f, +1.064913245e-01f, +1.129175635e-01f, +1.107400515e-01f, +1.003764999e-01f, +8.377168968e-02f, +6.387315732e-02f, +4.387072153e-02f, +2.644297610e-02f, +1.323391671e-02f, +4.686078310e-03f, +2.249946366e-04f, -1.324122762e-03f, -1.262381011e-03f, -6.454100415e-04f,
+    /* 24, 5 */ -9.328082015e-04f, -1.411161525e-03f, -9.190272443e-04f, +1.741080225e-03f, +7.869813543e-03f, +1.843844016e-02f, +3.361781336e-02f, +5.244341325e-02f, +7.277566076e-02f, +9.163004717e-02f, +1.058238246e-01f, +1.127632829e-01f, +1.111286847e-01f, +1.012337173e-01f, +8.493946948e-02f, +6.515180031e-02f, +4.506878807e-02f, +2.741959349e-02f, +1.392171386e-02f, +5.090608136e-03f, +4.047380047e-04f, -1.287358924e-03f, -1.291480556e-03f, -6.860450823e-04f,
+    /* 24, 6 */ -8.919367204e-04f, -1.398923562e-03f, -9.995952120e-04f, +1.486051192e-03f, +7.363667669e-03f, +1.763934022e-02f, +3.254608027e-02f, +5.119516710e-02f, +7.151464464e-02f, +9.055648452e-02f, +1.051275597e-01f, +1.125749599e-01f, +1.114845301e-01f, +1.020655875e-01f, +8.609350809e-02f, +6.642945179e-02f, +4.627670593e-02f, +2.841283293e-02f, +1.462808772e-02f, +5.511537402e-03f, +5.962212734e-04f, -1.244083985e-03f, -1.318344226e-03f, -7.270116618e-04f,
+    /* 24, 7 */ -8.507894667e-04f, -1.383278624e-03f, -1.072062171e-03f, +1.244366682e-03f, +6.874957829e-03f, +1.685870710e-02f, +3.148936626e-02f, +4.995374490e-02f, +7.024872443e-02f, +8.946527894e-02f, +1.044030520e-01f, +1.123527394e-01f, +1.118073151e-01f, +1.028714955e-01f, +8.723301301e-02f, +6.770534195e-02f, +4.749390083e-02f, +2.942241233e-02f, +1.535305047e-02f, +5.949094883e-03f, +7.997713791e-04f, -1.193986722e-03f, -1.342751302e-03f, -7.682020711e-04f,
+    /* 24, 8 */ -8.095018024e-04f, -1.364474212e-03f, -1.136752219e-03f, +1.015712718e-03f, +6.403499096e-03f, +1.609659749e-02f, +3.044803032e-02f, +4.871978245e-02f, +6.897869391e-02f, +8.835719701e-02f, +1.036508436e-01f, +1.120967923e-01f, +1.120967923e-01f, +1.036508436e-01f, +8.835719701e-02f, +6.897869391e-02f, +4.871978245e-02f, +3.044803032e-02f, +1.609659749e-02f, +6.403499096e-03f, +1.015712718e-03f, -1.136752219e-03f, -1.364474212e-03f, -8.095018024e-04f,
+    /* 24, 9 */ -7.682020711e-04f, -1.342751302e-03f, -1.193986722e-03f, +7.997713791e-04f, +5.949094883e-03f, +1.535305047e-02f, +2.942241233e-02f, +4.749390083e-02f, +6.770534195e-02f, +8.723301301e-02f, +1.028714955e-01f, +1.118073151e-01f, +1.123527394e-01f, +1.044030520e-01f, +8.946527894e-02f, +7.024872443e-02f, +4.995374490e-02f, +3.148936626e-02f, +1.685870710e-02f, +6.874957829e-03f, +1.244366682e-03f, -1.072062171e-03f, -1.383278624e-03f, -8.507894667e-04f,
+    /* 24,10 */ -7.270116618e-04f, -1.318344226e-03f, -1.244083985e-03f, +5.962212734e-04f, +5.511537402e-03f, +1.462808772e-02f, +2.841283293e-02f, +4.627670593e-02f, +6.642945179e-02f, +8.609350809e-02f, +1.020655875e-01f, +1.114845301e-01f, +1.125749599e-01f, +1.051275597e-01f, +9.055648452e-02f, +7.151464464e-02f, +5.119516710e-02f, +3.254608027e-02f, +1.763934022e-02f, +7.363667669e-03f, +1.486051192e-03f, -9.995952120e-04f, -1.398923562e-03f, -8.919367204e-04f,
+    /* 24,11 */ -6.860450823e-04f, -1.291480556e-03f, -1.287358924e-03f, +4.047380047e-04f, +5.090608136e-03f, +1.392171386e-02f, +2.741959349e-02f, +4.506878807e-02f, +6.515180031e-02f, +8.493946948e-02f, +1.012337173e-01f, +1.111286847e-01f, +1.127632829e-01f, +1.058238246e-01f, +9.163004717e-02f, +7.277566076e-02f, +5.244341325e-02f, +3.361781336e-02f, +1.843844016e-02f, +7.869813543e-03f, +1.741080225e-03f, -9.190272443e-04f, -1.411161525e-03f, -9.328082015e-04f,
+    /* 24,12 */ -6.454100415e-04f, -1.262381011e-03f, -1.324122762e-03f, +2.249946366e-04f, +4.686078310e-03f, +1.323391671e-02f, +2.644297610e-02f, +4.387072153e-02f, +6.387315732e-02f, +8.377168968e-02f, +1.003764999e-01f, +1.107400515e-01f, +1.129175635e-01f, +1.064913245e-01f, +9.268520876e-02f, +7.403097485e-02f, +5.369783330e-02f, +3.470418745e-02f, +1.925593236e-02f, +8.393568260e-03f, +2.009763334e-03f, -8.300317840e-04f, -1.419738627e-03f, -9.732614753e-04f,
+    /* 24,13 */ -6.052075404e-04f, -1.231259367e-03f, -1.354682733e-03f, +5.666214823e-05f, +4.297709369e-03f, +1.256466766e-02f, +2.548324354e-02f, +4.268306423e-02f, +6.259428489e-02f, +8.259096568e-02f, +9.949456662e-02f, +1.103189277e-01f, +1.130376830e-01f, +1.071295572e-01f, +9.372122042e-02f, +7.527978553e-02f, +5.495776346e-02f, +3.580480556e-02f, +2.009172411e-02f, +8.935092066e-03f, +2.292405169e-03f, -7.322803183e-04f, -1.424394748e-03f, -1.013146991e-03f,
+    /* 24,14 */ -5.655319713e-04f, -1.198322390e-03f, -1.379341793e-03f, -1.005901154e-04f, +3.925253463e-03f, +1.191392194e-02f, +2.454063933e-02f, +4.150635732e-02f, +6.131593665e-02f, +8.139809812e-02f, +9.858856505e-02f, +1.098656350e-01f, +1.131235487e-01f, +1.077380418e-01f, +9.473734332e-02f, +7.652128881e-02f, +5.622252667e-02f, +3.691925193e-02f, +2.094570441e-02f, +9.494532203e-03f, +2.589304991e-03f, -6.254426725e-04f, -1.424863695e-03f, -1.052308046e-03f,
+    /* 24,15 */ -5.264712252e-04f, -1.163769773e-03f, -1.398398357e-03f, -2.470940015e-04f, +3.568453927e-03f, +1.128161899e-02f, +2.361538773e-02f, +4.034112484e-02f, +6.003885715e-02f, +8.019389052e-02f, +9.765915788e-02f, +1.093805191e-01f, +1.131750947e-01f, +1.083163184e-01f, +9.573284944e-02f, +7.775467878e-02f, +5.749143322e-02f, +3.804709217e-02f, +2.181774373e-02f, +1.007202248e-02f, +2.900756187e-03f, -5.091873886e-04f, -1.420873386e-03f, -1.090580766e-03f,
+    /* 24, 0 */ -6.542299160e-04f, -2.850723396e-03f, -6.490258587e-03f, -9.960104872e-03f, -9.809478345e-03f, -1.578994128e-03f, +1.829834548e-02f, +5.025161588e-02f, +9.015425381e-02f, +1.297327779e-01f, +1.589915292e-01f, +1.697884216e-01f, +1.589915292e-01f, +1.297327779e-01f, +9.015425381e-02f, +5.025161588e-02f, +1.829834548e-02f, -1.578994128e-03f, -9.809478345e-03f, -9.960104872e-03f, -6.490258587e-03f, -2.850723396e-03f, -6.542299160e-04f, +6.349952235e-05f,
+    /* 24, 1 */ -5.715660670e-04f, -2.664319167e-03f, -6.241370053e-03f, -9.805460951e-03f, -1.000906214e-02f, -2.409006146e-03f, +1.668518463e-02f, +4.795494216e-02f, +8.756853655e-02f, +1.274593023e-01f, +1.576392865e-01f, +1.697451719e-01f, +1.602699418e-01f, +1.319653222e-01f, +9.273931864e-02f, +5.258078166e-02f, +1.996024013e-02f, -7.024321916e-04f, -9.578061730e-03f, -1.010098516e-02f, -6.739131402e-03f, -3.043301383e-03f, -7.429059724e-04f, +4.968305000e-05f,
+    /* 24, 2 */ -4.947700224e-04f, -2.484234158e-03f, -5.993080931e-03f, -9.638098137e-03f, -1.017794029e-02f, -3.193110201e-03f, +1.512113085e-02f, +4.569233080e-02f, +8.498458400e-02f, +1.251473534e-01f, +1.562147818e-01f, +1.696154741e-01f, +1.614730379e-01f, +1.341545065e-01f, +9.532128436e-02f, +5.494080034e-02f, +2.167042077e-02f, +2.212715669e-04f, -9.313697641e-03f, -1.022703863e-02f, -6.987342300e-03f, -3.241882098e-03f, -8.377276082e-04f, +3.267464436e-05f,
+    /* 24, 3 */ -4.236872966e-04f, -2.310589033e-03f, -5.745975157e-03f, -9.459041324e-03f, -1.031725016e-02f, -3.931996122e-03f, +1.360648366e-02f, +4.346528177e-02f, +8.240478048e-02f, +1.227994149e-01f, +1.547196623e-01f, +1.693994819e-01f, +1.625994153e-01f, +1.362979353e-01f, +9.789767810e-02f, +5.732996534e-02f, +2.342836441e-02f, +1.192656872e-03f, -9.015286272e-03f, -1.033718507e-02f, -7.234214214e-03f, -3.446268223e-03f, -9.388162067e-04f, +1.226776811e-05f,
+    /* 24, 4 */ -3.581542611e-04f, -2.143480447e-03f, -5.500605429e-03f, -9.269294257e-03f, -1.042813706e-02f, -4.626399385e-03f, +1.214146970e-02f, +4.127522351e-02f, +7.983147433e-02f, +1.204179923e-01f, +1.531556516e-01f, +1.690974512e-01f, +1.636477581e-01f, +1.383932498e-01f, +1.004660042e-01f, +5.974650439e-02f, +2.523347234e-02f, +2.212209196e-03f, -8.681745020e-03f, -1.043032883e-02f, -7.479039479e-03f, -3.656235461e-03f, -1.046280200e-03f, -1.174474466e-05f,
+    /* 24, 5 */ -2.979990698e-04f, -1.982981877e-03f, -5.257493218e-03f, -9.069838305e-03f, -1.051175200e-02f, -5.277098842e-03f, +1.072624378e-02f, +3.912351177e-02f, +7.726697481e-02f, +1.180056089e-01f, +1.515245471e-01f, +1.687097397e-01f, +1.646168392e-01f, +1.404381320e-01f, +1.030237476e-01f, +6.218858132e-02f, +2.708506973e-02f, +3.280357753e-03f, -8.312010872e-03f, -1.050536039e-02f, -7.721080180e-03f, -3.871531888e-03f, -1.160214103e-03f, -3.957001380e-05f,
+    /* 24, 6 */ -2.430425717e-04f, -1.829144469e-03f, -5.017128860e-03f, -8.861631306e-03f, -1.056924947e-02f, -5.884914422e-03f, +9.360889972e-03f, +3.701142868e-02f, +7.471354913e-02f, +1.155648023e-01f, +1.498282170e-01f, +1.682368061e-01f, +1.655055219e-01f, +1.424303080e-01f, +1.055683777e-01f, +6.465429798e-02f, +2.898240538e-02f, +4.397473555e-03f, -7.905042791e-03f, -1.056115807e-02f, -7.959568583e-03f, -4.091877352e-03f, -1.280697546e-03f, -7.141440876e-05f,
+    /* 24, 7 */ -1.930992056e-04f, -1.681997919e-03f, -4.779971710e-03f, -8.645606504e-03f, -1.060178532e-02f, -6.450704795e-03f, +8.045422842e-03f, +3.494018190e-02f, +7.217341954e-02f, +1.130981205e-01f, +1.480685972e-01f, +1.676792097e-01f, +1.663127619e-01f, +1.443675516e-01f, +1.080973515e-01f, +6.714169632e-02f, +3.092465153e-02f, +5.563867546e-03f, -7.459824124e-03f, -1.059658974e-02f, -8.193707637e-03f, -4.316962918e-03f, -1.407794310e-03f, -1.074828157e-04f,
+    /* 24, 8 */ -1.479778772e-04f, -1.541551365e-03f, -4.546450360e-03f, -8.422671559e-03f, -1.061051465e-02f, -6.975365011e-03f, +6.779788805e-03f, +3.291090392e-02f, +6.964876056e-02f, +1.106081178e-01f, +1.462476880e-01f, +1.670376092e-01f, +1.670376092e-01f, +1.462476880e-01f, +1.106081178e-01f, +6.964876056e-02f, +3.291090392e-02f, +6.779788805e-03f, -6.975365011e-03f, -1.061051465e-02f, -8.422671559e-03f, -4.546450360e-03f, -1.541551365e-03f, -1.479778772e-04f,
+    /* 24, 9 */ -1.074828157e-04f, -1.407794310e-03f, -4.316962918e-03f, -8.193707637e-03f, -1.059658974e-02f, -7.459824124e-03f, +5.563867546e-03f, +3.092465153e-02f, +6.714169632e-02f, +1.080973515e-01f, +1.443675516e-01f, +1.663127619e-01f, +1.676792097e-01f, +1.480685972e-01f, +1.130981205e-01f, +7.217341954e-02f, +3.494018190e-02f, +8.045422842e-03f, -6.450704795e-03f, -1.060178532e-02f, -8.645606504e-03f, -4.779971710e-03f, -1.681997919e-03f, -1.930992056e-04f,
+    /* 24,10 */ -7.141440876e-05f, -1.280697546e-03f, -4.091877352e-03f, -7.959568583e-03f, -1.056115807e-02f, -7.905042791e-03f, +4.397473555e-03f, +2.898240538e-02f, +6.465429798e-02f, +1.055683777e-01f, +1.424303080e-01f, +1.655055219e-01f, +1.682368061e-01f, +1.498282170e-01f, +1.155648023e-01f, +7.471354913e-02f, +3.701142868e-02f, +9.360889972e-03f, -5.884914422e-03f, -1.056924947e-02f, -8.861631306e-03f, -5.017128860e-03f, -1.829144469e-03f, -2.430425717e-04f,
+    /* 24,11 */ -3.957001380e-05f, -1.160214103e-03f, -3.871531888e-03f, -7.721080180e-03f, -1.050536039e-02f, -8.312010872e-03f, +3.280357753e-03f, +2.708506973e-02f, +6.218858132e-02f, +1.030237476e-01f, +1.404381320e-01f, +1.646168392e-01f, +1.687097397e-01f, +1.515245471e-01f, +1.180056089e-01f, +7.726697481e-02f, +3.912351177e-02f, +1.072624378e-02f, -5.277098842e-03f, -1.051175200e-02f, -9.069838305e-03f, -5.257493218e-03f, -1.982981877e-03f, -2.979990698e-04f,
+    /* 24,12 */ -1.174474466e-05f, -1.046280200e-03f, -3.656235461e-03f, -7.479039479e-03f, -1.043032883e-02f, -8.681745020e-03f, +2.212209196e-03f, +2.523347234e-02f, +5.974650439e-02f, +1.004660042e-01f, +1.383932498e-01f, +1.636477581e-01f, +1.690974512e-01f, +1.531556516e-01f, +1.204179923e-01f, +7.983147433e-02f, +4.127522351e-02f, +1.214146970e-02f, -4.626399385e-03f, -1.042813706e-02f, -9.269294257e-03f, -5.500605429e-03f, -2.143480447e-03f, -3.581542611e-04f,
+    /* 24,13 */ +1.226776811e-05f, -9.388162067e-04f, -3.446268223e-03f, -7.234214214e-03f, -1.033718507e-02f, -9.015286272e-03f, +1.192656872e-03f, +2.342836441e-02f, +5.732996534e-02f, +9.789767810e-02f, +1.362979353e-01f, +1.625994153e-01f, +1.693994819e-01f, +1.547196623e-01f, +1.227994149e-01f, +8.240478048e-02f, +4.346528177e-02f, +1.360648366e-02f, -3.931996122e-03f, -1.031725016e-02f, -9.459041324e-03f, -5.745975157e-03f, -2.310589033e-03f, -4.236872966e-04f,
+    /* 24,14 */ +3.267464436e-05f, -8.377276082e-04f, -3.241882098e-03f, -6.987342300e-03f, -1.022703863e-02f, -9.313697641e-03f, +2.212715669e-04f, +2.167042077e-02f, +5.494080034e-02f, +9.532128436e-02f, +1.341545065e-01f, +1.614730379e-01f, +1.696154741e-01f, +1.562147818e-01f, +1.251473534e-01f, +8.498458400e-02f, +4.569233080e-02f, +1.512113085e-02f, -3.193110201e-03f, -1.017794029e-02f, -9.638098137e-03f, -5.993080931e-03f, -2.484234158e-03f, -4.947700224e-04f,
+    /* 24,15 */ +4.968305000e-05f, -7.429059724e-04f, -3.043301383e-03f, -6.739131402e-03f, -1.010098516e-02f, -9.578061730e-03f, -7.024321916e-04f, +1.996024013e-02f, +5.258078166e-02f, +9.273931864e-02f, +1.319653222e-01f, +1.602699418e-01f, +1.697451719e-01f, +1.576392865e-01f, +1.274593023e-01f, +8.756853655e-02f, +4.795494216e-02f, +1.668518463e-02f, -2.409006146e-03f, -1.000906214e-02f, -9.805460951e-03f, -6.241370053e-03f, -2.664319167e-03f, -5.715660670e-04f,
+    /* 24, 0 */ +1.619229527e-03f, +2.585184252e-03f, +7.650378125e-04f, -6.171975840e-03f, -1.695416291e-02f, -2.423274385e-02f, -1.612533623e-02f, +1.737483974e-02f, +7.628093610e-02f, +1.465254238e-01f, +2.041060488e-01f, +2.263845622e-01f, +2.041060488e-01f, +1.465254238e-01f, +7.628093610e-02f, +1.737483974e-02f, -1.612533623e-02f, -2.423274385e-02f, -1.695416291e-02f, -6.171975840e-03f, +7.650378125e-04f, +2.585184252e-03f, +1.619229527e-03f, +4.203426526e-04f,
+    /* 24, 1 */ +1.531668339e-03f, +2.575087939e-03f, +1.015030141e-03f, -5.584253498e-03f, -1.627529113e-02f, -2.409742304e-02f, -1.730694612e-02f, +1.446720847e-02f, +7.205349539e-02f, +1.422361210e-01f, +2.013530187e-01f, +2.262942875e-01f, +2.067165090e-01f, +1.507648574e-01f, +8.055624103e-02f, +2.038667606e-02f, -1.484111026e-02f, -2.430745079e-02f, -1.762106127e-02f, -6.776879595e-03f, +4.938567092e-04f, +2.584413048e-03f, +1.706479068e-03f, +4.743886054e-04f,
+    /* 24, 2 */ +1.444251783e-03f, +2.554897668e-03f, +1.244217996e-03f, -5.014646771e-03f, -1.558700841e-02f, -2.390468713e-02f, -1.838770218e-02f, +1.166535016e-02f, +6.787901036e-02f, +1.379034790e-01f, +1.984620386e-01f, +2.260236194e-01f, +2.091800031e-01f, +1.549479100e-01f, +8.487414623e-02f, +2.350091114e-02f, -1.345266938e-02f, -2.431836796e-02f, -1.827334019e-02f, -7.397926552e-03f, +2.011593926e-04f, +2.571998910e-03f, +1.792931314e-03f, +5.318997770e-04f,
+    /* 24, 3 */ +1.357406375e-03f, +2.525382259e-03f, +1.453038602e-03f, -4.463987325e-03f, -1.489178967e-02f, -2.365774922e-02f, -1.936952211e-02f, +8.970595311e-03f, +6.376239146e-02f, +1.335340325e-01f, +1.954379419e-01f, +2.255730254e-01f, +2.114923689e-01f, +1.590681020e-01f, +8.922922426e-02f, +2.671549986e-02f, -1.195858585e-02f, -2.426235104e-02f, -1.890827435e-02f, -8.033973302e-03f, -1.133208208e-04f, +2.547167581e-03f, +1.878071789e-03f, +5.928148062e-04f,
+    /* 24, 4 */ +1.271528621e-03f, +2.487303056e-03f, +1.641978155e-03f, -3.933006021e-03f, -1.419201875e-02f, -2.335982837e-02f, -2.025447087e-02f, +6.384038515e-03f, +5.970836021e-02f, +1.291343068e-01f, +1.922857641e-01f, +2.249432832e-01f, +2.136496877e-01f, +1.631190001e-01f, +9.361589375e-02f, +3.002815853e-02f, -1.035761042e-02f, -2.413629608e-02f, -1.952306378e-02f, -8.683770335e-03f, -4.497860188e-04f, +2.509149105e-03f, +1.961357874e-03f, +6.570484107e-04f,
+    /* 24, 5 */ +1.186984902e-03f, +2.441411339e-03f, +1.811567846e-03f, -3.422334900e-03f, -1.348998519e-02f, -2.301414142e-02f, -2.104475231e-02f, +3.906540614e-03f, +5.572144173e-02f, +1.247108046e-01f, +1.890107314e-01f, +2.241354793e-01f, +2.156482934e-01f, +1.670942309e-01f, +9.802842938e-02f, +3.343636564e-02f, -8.648679404e-03f, -2.393714847e-02f, -2.011483893e-02f, -9.345961431e-03f, -8.083699235e-04f, +2.457181100e-03f, +2.042219659e-03f, +7.244903794e-04f,
+    /* 24, 6 */ +1.104111497e-03f, +2.388445882e-03f, +1.962379900e-03f, -2.932509404e-03f, -1.278788140e-02f, -2.262389503e-02f, -2.174270056e-02f, +1.538731332e-03f, +5.180595798e-02f, +1.202699924e-01f, +1.856182490e-01f, +2.231510062e-01f, +2.174847808e-01f, +1.709874949e-01f, +1.024609723e-01f, +3.693736330e-02f, -6.830921421e-03f, -2.366191192e-02f, -2.068066597e-02f, -1.001908338e-02f, -1.189134206e-03f, +2.390512141e-03f, +2.120060933e-03f, +7.950046334e-04f,
+    /* 24, 7 */ +1.023214729e-03f, +2.329130668e-03f, +2.095023622e-03f, -2.463970822e-03f, -1.208780014e-02f, -2.219227795e-02f, -2.235077131e-02f, -7.189875350e-04f, +4.796602143e-02f, +1.158182872e-01f, +1.821138888e-01f, +2.219915591e-01f, +2.191560137e-01f, +1.747925803e-01f, +1.069075413e-01f, +4.052815924e-02f, -4.903663772e-03f, -2.330765754e-02f, -2.121755248e-02f, -1.070156599e-02f, -1.592064902e-03f, +2.308405249e-03f, +2.194260355e-03f, +8.684283615e-04f,
+    /* 24, 8 */ +9.445712380e-04f, +2.264172764e-03f, +2.210141486e-03f, -2.017068943e-03f, -1.139173248e-02f, -2.172245353e-02f, -2.287153293e-02f, -2.866438416e-03f, +4.420552946e-02f, +1.113620436e-01f, +1.785033768e-01f, +2.206591322e-01f, +2.206591322e-01f, +1.785033768e-01f, +1.113620436e-01f, +4.420552946e-02f, -2.866438416e-03f, -2.287153293e-02f, -2.172245353e-02f, -1.139173248e-02f, -2.017068943e-03f, +2.210141486e-03f, +2.264172764e-03f, +9.445712380e-04f,
+    /* 24, 9 */ +8.684283615e-04f, +2.194260355e-03f, +2.308405249e-03f, -1.592064902e-03f, -1.070156599e-02f, -2.121755248e-02f, -2.330765754e-02f, -4.903663772e-03f, +4.052815924e-02f, +1.069075413e-01f, +1.747925803e-01f, +2.191560137e-01f, +2.219915591e-01f, +1.821138888e-01f, +1.158182872e-01f, +4.796602143e-02f, -7.189875350e-04f, -2.235077131e-02f, -2.219227795e-02f, -1.208780014e-02f, -2.463970822e-03f, +2.095023622e-03f, +2.329130668e-03f, +1.023214729e-03f,
+    /* 24,10 */ +7.950046334e-04f, +2.120060933e-03f, +2.390512141e-03f, -1.189134206e-03f, -1.001908338e-02f, -2.068066597e-02f, -2.366191192e-02f, -6.830921421e-03f, +3.693736330e-02f, +1.024609723e-01f, +1.709874949e-01f, +2.174847808e-01f, +2.231510062e-01f, +1.856182490e-01f, +1.202699924e-01f, +5.180595798e-02f, +1.538731332e-03f, -2.174270056e-02f, -2.262389503e-02f, -1.278788140e-02f, -2.932509404e-03f, +1.962379900e-03f, +2.388445882e-03f, +1.104111497e-03f,
+    /* 24,11 */ +7.244903794e-04f, +2.042219659e-03f, +2.457181100e-03f, -8.083699235e-04f, -9.345961431e-03f, -2.011483893e-02f, -2.393714847e-02f, -8.648679404e-03f, +3.343636564e-02f, +9.802842938e-02f, +1.670942309e-01f, +2.156482934e-01f, +2.241354793e-01f, +1.890107314e-01f, +1.247108046e-01f, +5.572144173e-02f, +3.906540614e-03f, -2.104475231e-02f, -2.301414142e-02f, -1.348998519e-02f, -3.422334900e-03f, +1.811567846e-03f, +2.441411339e-03f, +1.186984902e-03f,
+    /* 24,12 */ +6.570484107e-04f, +1.961357874e-03f, +2.509149105e-03f, -4.497860188e-04f, -8.683770335e-03f, -1.952306378e-02f, -2.413629608e-02f, -1.035761042e-02f, +3.002815853e-02f, +9.361589375e-02f, +1.631190001e-01f, +2.136496877e-01f, +2.249432832e-01f, +1.922857641e-01f, +1.291343068e-01f, +5.970836021e-02f, +6.384038515e-03f, -2.025447087e-02f, -2.335982837e-02f, -1.419201875e-02f, -3.933006021e-03f, +1.641978155e-03f, +2.487303056e-03f, +1.271528621e-03f,
+    /* 24,13 */ +5.928148062e-04f, +1.878071789e-03f, +2.547167581e-03f, -1.133208208e-04f, -8.033973302e-03f, -1.890827435e-02f, -2.426235104e-02f, -1.195858585e-02f, +2.671549986e-02f, +8.922922426e-02f, +1.590681020e-01f, +2.114923689e-01f, +2.255730254e-01f, +1.954379419e-01f, +1.335340325e-01f, +6.376239146e-02f, +8.970595311e-03f, -1.936952211e-02f, -2.365774922e-02f, -1.489178967e-02f, -4.463987325e-03f, +1.453038602e-03f, +2.525382259e-03f, +1.357406375e-03f,
+    /* 24,14 */ +5.318997770e-04f, +1.792931314e-03f, +2.571998910e-03f, +2.011593926e-04f, -7.397926552e-03f, -1.827334019e-02f, -2.431836796e-02f, -1.345266938e-02f, +2.350091114e-02f, +8.487414623e-02f, +1.549479100e-01f, +2.091800031e-01f, +2.260236194e-01f, +1.984620386e-01f, +1.379034790e-01f, +6.787901036e-02f, +1.166535016e-02f, -1.838770218e-02f, -2.390468713e-02f, -1.558700841e-02f, -5.014646771e-03f, +1.244217996e-03f, +2.554897668e-03f, +1.444251783e-03f,
+    /* 24,15 */ +4.743886054e-04f, +1.706479068e-03f, +2.584413048e-03f, +4.938567092e-04f, -6.776879595e-03f, -1.762106127e-02f, -2.430745079e-02f, -1.484111026e-02f, +2.038667606e-02f, +8.055624103e-02f, +1.507648574e-01f, +2.067165090e-01f, +2.262942875e-01f, +2.013530187e-01f, +1.422361210e-01f, +7.205349539e-02f, +1.446720847e-02f, -1.730694612e-02f, -2.409742304e-02f, -1.627529113e-02f, -5.584253498e-03f, +1.015030141e-03f, +2.575087939e-03f, +1.531668339e-03f,
+    /* 24, 0 */ -5.620806651e-04f, +1.786951327e-03f, +6.445247430e-03f, +8.135220753e-03f, -1.055728075e-03f, -2.182587186e-02f, -3.862210468e-02f, -2.392616717e-02f, +4.121375257e-02f, +1.449837419e-01f, +2.427850309e-01f, +2.829807027e-01f, +2.427850309e-01f, +1.449837419e-01f, +4.121375257e-02f, -2.392616717e-02f, -3.862210468e-02f, -2.182587186e-02f, -1.055728075e-03f, +8.135220753e-03f, +6.445247430e-03f, +1.786951327e-03f, -5.620806651e-04f, -5.120724112e-04f,
+    /* 24, 1 */ -6.104492932e-04f, +1.548760636e-03f, +6.159105160e-03f, +8.277197332e-03f, -7.779733613e-05f, -2.039475337e-02f, -3.819819741e-02f, -2.624621678e-02f, +3.569722776e-02f, +1.380982730e-01f, +2.379020609e-01f, +2.828154582e-01f, +2.474326717e-01f, +1.518487179e-01f, +4.689321837e-02f, -2.139810919e-02f, -3.892035913e-02f, -2.324619800e-02f, -2.084809535e-03f, +7.948411519e-03f, +6.721048149e-03f, +2.036121529e-03f, -5.037148469e-04f, -5.469834647e-04f,
+    /* 24, 2 */ -6.493280747e-04f, +1.322056610e-03f, +5.864617557e-03f, +8.376206823e-03f, +8.476165560e-04f, -1.895882060e-02f, -3.765599422e-02f, -2.836041007e-02f, +3.035103267e-02f, +1.312062799e-01f, +2.327950895e-01f, +2.823201223e-01f, +2.518341522e-01f, +1.586790922e-01f, +5.272765217e-02f, -1.866041870e-02f, -3.908572584e-02f, -2.464952038e-02f, -3.163389088e-03f, +7.715013258e-03f, +6.984447000e-03f, +2.295668536e-03f, -4.348733531e-04f, -5.801620624e-04f,
+    /* 24, 3 */ -6.792484933e-04f, +1.107253309e-03f, +5.563710253e-03f, +8.434210700e-03f, +1.719427448e-03f, -1.752380524e-02f, -3.700294628e-02f, -3.027140813e-02f, +2.518195985e-02f, +1.243215533e-01f, +2.274759065e-01f, +2.814958870e-01f, +2.559791715e-01f, +1.654606562e-01f, +5.870851072e-02f, -1.571201688e-02f, -3.911111998e-02f, -2.602940857e-02f, -4.289522020e-03f, +7.433392157e-03f, +7.233326681e-03f, +2.564892035e-03f, -3.551113499e-04f, -6.111213026e-04f,
+    /* 24, 4 */ -7.007615573e-04f, +9.046745282e-04f, +5.258232435e-03f, +8.453253159e-03f, +2.536821717e-03f, -1.609518093e-02f, -3.624657153e-02f, -3.198236083e-02f, +2.019619593e-02f, +1.174576676e-01f, +2.219567270e-01f, +2.803447347e-01f, +2.598579915e-01f, +1.721791413e-01f, +6.482669661e-02f, -1.255238605e-02f, -3.898963496e-02f, -2.737922870e-02f, -5.460966964e-03f, +7.102050305e-03f, +7.465520628e-03f, +2.842992490e-03f, -2.640225027e-04f, -6.393525967e-04f,
+    /* 24, 5 */ -7.144333056e-04f, +7.145569834e-04f, +4.949951622e-03f, +8.435448103e-03f, +3.299249997e-03f, -1.467815287e-02f, -3.539442781e-02f, -3.349688539e-02f, +1.539931407e-02f, +1.106279448e-01f, +2.162501543e-01f, +2.788694321e-01f, +2.634614666e-01f, +1.788202595e-01f, +7.107257652e-02f, -9.181583996e-03f, -3.871457066e-02f, -2.869216031e-02f, -6.675182397e-03f, +6.719638981e-03f, +7.678821339e-03f, +3.129069982e-03f, -1.612438490e-04f, -6.643278432e-04f,
+    /* 24, 6 */ -7.208404573e-04f, +5.370537968e-04f, +4.640549073e-03f, +8.382966356e-03f, +4.006418111e-03f, -1.327764882e-02f, -3.445408633e-02f, -3.481904366e-02f, +1.079626845e-02f, +1.038454185e-01f, +2.103691410e-01f, +2.770735210e-01f, +2.667810728e-01f, +1.853697450e-01f, +7.743600141e-02f, -5.600256400e-03f, -3.827946167e-02f, -2.996121443e-02f, -7.929324241e-03f, +6.284971816e-03f, +7.870989279e-03f, +3.422123547e-03f, -4.646067064e-05f, -6.855018549e-04f,
+    /* 24, 7 */ -7.205662235e-04f, +3.722382714e-04f, +4.331615834e-03f, +8.298023138e-03f, +4.658277300e-03f, -1.189831143e-02f, -3.343310598e-02f, -3.595331849e-02f, +6.391391026e-03f, +9.712280024e-02f, +2.043269499e-01f, +2.749613076e-01f, +2.698089343e-01f, +1.918133956e-01f, +8.390632879e-02f, -1.809647645e-03f, -3.767810524e-02f, -3.117925279e-02f, -9.220244622e-03f, +5.797037759e-03f, +8.039762345e-03f, +3.721051017e-03f, +8.058866307e-05f, -7.023150347e-04f,
+    /* 24, 8 */ -7.141962964e-04f, +2.201079121e-04f, +4.024649403e-03f, +8.182865872e-03f, +5.255013788e-03f, -1.054449181e-02f, -3.233900821e-02f, -3.690458903e-02f, +2.188390323e-03f, +9.047244679e-02f, +1.981371140e-01f, +2.725378483e-01f, +2.725378483e-01f, +1.981371140e-01f, +9.047244679e-02f, +2.188390323e-03f, -3.690458903e-02f, -3.233900821e-02f, -1.054449181e-02f, +5.255013788e-03f, +8.182865872e-03f, +4.024649403e-03f, +2.201079121e-04f, -7.141962964e-04f,
+    /* 24, 9 */ -7.023150347e-04f, +8.058866307e-05f, +3.721051017e-03f, +8.039762345e-03f, +5.797037759e-03f, -9.220244622e-03f, -3.117925279e-02f, -3.767810524e-02f, -1.809647645e-03f, +8.390632879e-02f, +1.918133956e-01f, +2.698089343e-01f, +2.749613076e-01f, +2.043269499e-01f, +9.712280024e-02f, +6.391391026e-03f, -3.595331849e-02f, -3.343310598e-02f, -1.189831143e-02f, +4.658277300e-03f, +8.298023138e-03f, +4.331615834e-03f, +3.722382714e-04f, -7.205662235e-04f,
+    /* 24,10 */ -6.855018549e-04f, -4.646067064e-05f, +3.422123547e-03f, +7.870989279e-03f, +6.284971816e-03f, -7.929324241e-03f, -2.996121443e-02f, -3.827946167e-02f, -5.600256400e-03f, +7.743600141e-02f, +1.853697450e-01f, +2.667810728e-01f, +2.770735210e-01f, +2.103691410e-01f, +1.038454185e-01f, +1.079626845e-02f, -3.481904366e-02f, -3.445408633e-02f, -1.327764882e-02f, +4.006418111e-03f, +8.382966356e-03f, +4.640549073e-03f, +5.370537968e-04f, -7.208404573e-04f,
+    /* 24,11 */ -6.643278432e-04f, -1.612438490e-04f, +3.129069982e-03f, +7.678821339e-03f, +6.719638981e-03f, -6.675182397e-03f, -2.869216031e-02f, -3.871457066e-02f, -9.181583996e-03f, +7.107257652e-02f, +1.788202595e-01f, +2.634614666e-01f, +2.788694321e-01f, +2.162501543e-01f, +1.106279448e-01f, +1.539931407e-02f, -3.349688539e-02f, -3.539442781e-02f, -1.467815287e-02f, +3.299249997e-03f, +8.435448103e-03f, +4.949951622e-03f, +7.145569834e-04f, -7.144333056e-04f,
+    /* 24,12 */ -6.393525967e-04f, -2.640225027e-04f, +2.842992490e-03f, +7.465520628e-03f, +7.102050305e-03f, -5.460966964e-03f, -2.737922870e-02f, -3.898963496e-02f, -1.255238605e-02f, +6.482669661e-02f, +1.721791413e-01f, +2.598579915e-01f, +2.803447347e-01f, +2.219567270e-01f, +1.174576676e-01f, +2.019619593e-02f, -3.198236083e-02f, -3.624657153e-02f, -1.609518093e-02f, +2.536821717e-03f, +8.453253159e-03f, +5.258232435e-03f, +9.046745282e-04f, -7.007615573e-04f,
+    /* 24,13 */ -6.111213026e-04f, -3.551113499e-04f, +2.564892035e-03f, +7.233326681e-03f, +7.433392157e-03f, -4.289522020e-03f, -2.602940857e-02f, -3.911111998e-02f, -1.571201688e-02f, +5.870851072e-02f, +1.654606562e-01f, +2.559791715e-01f, +2.814958870e-01f, +2.274759065e-01f, +1.243215533e-01f, +2.518195985e-02f, -3.027140813e-02f, -3.700294628e-02f, -1.752380524e-02f, +1.719427448e-03f, +8.434210700e-03f, +5.563710253e-03f, +1.107253309e-03f, -6.792484933e-04f,
+    /* 24,14 */ -5.801620624e-04f, -4.348733531e-04f, +2.295668536e-03f, +6.984447000e-03f, +7.715013258e-03f, -3.163389088e-03f, -2.464952038e-02f, -3.908572584e-02f, -1.866041870e-02f, +5.272765217e-02f, +1.586790922e-01f, +2.518341522e-01f, +2.823201223e-01f, +2.327950895e-01f, +1.312062799e-01f, +3.035103267e-02f, -2.836041007e-02f, -3.765599422e-02f, -1.895882060e-02f, +8.476165560e-04f, +8.376206823e-03f, +5.864617557e-03f, +1.322056610e-03f, -6.493280747e-04f,
+    /* 24,15 */ -5.469834647e-04f, -5.037148469e-04f, +2.036121529e-03f, +6.721048149e-03f, +7.948411519e-03f, -2.084809535e-03f, -2.324619800e-02f, -3.892035913e-02f, -2.139810919e-02f, +4.689321837e-02f, +1.518487179e-01f, +2.474326717e-01f, +2.828154582e-01f, +2.379020609e-01f, +1.380982730e-01f, +3.569722776e-02f, -2.624621678e-02f, -3.819819741e-02f, -2.039475337e-02f, -7.779733613e-05f, +8.277197332e-03f, +6.159105160e-03f, +1.548760636e-03f, -6.104492932e-04f,
+    /* 24, 0 */ -1.197013499e-03f, -3.320493122e-03f, -1.144245270e-03f, +8.577337679e-03f, +1.627759141e-02f, +3.152522439e-03f, -3.255249254e-02f, -5.362651723e-02f, -5.304244056e-03f, +1.253006387e-01f, +2.738089134e-01f, +3.395768433e-01f, +2.738089134e-01f, +1.253006387e-01f, -5.304244056e-03f, -5.362651723e-02f, -3.255249254e-02f, +3.152522439e-03f, +1.627759141e-02f, +8.577337679e-03f, -1.144245270e-03f, -3.320493122e-03f, -1.197013499e-03f, +1.261205705e-04f,
+    /* 24, 1 */ -1.060573898e-03f, -3.246029352e-03f, -1.514205592e-03f, +7.849505167e-03f, +1.622707505e-02f, +4.797556391e-03f, -3.017446993e-02f, -5.385088872e-02f, -1.098434059e-02f, +1.155960124e-01f, +2.659858985e-01f, +3.393017042e-01f, +2.812897341e-01f, +1.350895441e-01f, +7.263382766e-04f, -5.311639759e-02f, -3.488122370e-02f, +1.404407443e-03f, +1.624118609e-02f, +9.301572693e-03f, -7.399572733e-04f, -3.377916464e-03f, -1.338504197e-03f, +9.901282259e-05f,
+    /* 24, 2 */ -9.298712414e-04f, -3.156277727e-03f, -1.849729696e-03f, +7.122444811e-03f, +1.609438844e-02f, +6.335978542e-03f, -2.776122262e-02f, -5.380213751e-02f, -1.630850202e-02f, +1.060004951e-01f, +2.578448120e-01f, +3.384771755e-01f, +2.884051592e-01f, +1.449371908e-01f, +7.100538384e-03f, -5.230860749e-02f, -3.714619841e-02f, -4.425295608e-04f, +1.611336946e-02f, +1.001762126e-02f, -3.016869975e-04f, -3.416553196e-03f, -1.484263468e-03f, +6.526428163e-05f,
+    /* 24, 3 */ -8.054954021e-04f, -3.052984548e-03f, -2.150934111e-03f, +6.400291527e-03f, +1.588450664e-02f, +7.764974256e-03f, -2.532636892e-02f, -5.349351937e-02f, -2.127269005e-02f, +9.653812261e-02f, +2.494105998e-01f, +3.371059190e-01f, +2.951330020e-01f, +1.548174220e-01f, +1.381006797e-02f, -5.119199897e-02f, -3.933260713e-02f, -2.383292518e-03f, +1.588995194e-02f, +1.072069264e-02f, +1.699725421e-04f, -3.434676821e-03f, -1.633412152e-03f, +2.453170435e-05f,
+    /* 24, 4 */ -6.879416803e-04f, -2.937877889e-03f, -2.418147761e-03f, +5.686932142e-03f, +1.560259046e-02f, +9.082429619e-03f, -2.288303213e-02f, -5.293884648e-02f, -2.587426360e-02f, +8.723205545e-02f, +2.407089312e-01f, +3.351923590e-01f, +3.014521813e-01f, +1.647035561e-01f, +2.084522321e-02f, -4.975626781e-02f, -4.142535273e-02f, -4.412143180e-03f, +1.556708209e-02f, +1.140581394e-02f, +6.741710759e-04f, -3.430594409e-03f, -1.784975276e-03f, -2.348660763e-05f,
+    /* 24, 5 */ -5.776128643e-04f, -2.812656385e-03f, -2.651898517e-03f, +4.985994150e-03f, +1.525394912e-02f, +1.028691298e-02f, -2.044379769e-02f, -5.215241275e-02f, -3.011195925e-02f, +7.810450253e-02f, +2.317660961e-01f, +3.327426635e-01f, +3.073428069e-01f, +1.745684830e-01f, +2.819489579e-02f, -4.799202051e-02f, -4.340911052e-02f, -6.522599631e-03f, +1.514128438e-02f, +1.206785167e-02f, +1.209792693e-03f, -3.402660968e-03f, -1.937883690e-03f, -7.904503123e-05f,
+    /* 24, 6 */ -4.748218959e-04f, -2.678978820e-03f, -2.852899102e-03f, +4.300836533e-03f, +1.484400357e-02f, +1.137765361e-02f, -1.802067434e-02f, -5.114891871e-02f, -3.398586582e-02f, +6.917664952e-02f, +2.226089010e-01f, +3.297647184e-01f, +3.127862620e-01f, +1.843847637e-01f, +3.584659036e-02f, -4.589083871e-02f, -4.526839113e-02f, -8.707438641e-03f, +1.460949637e-02f, +1.270153536e-02f, +1.775448814e-03f, -3.349294247e-03f, -2.090976552e-03f, -1.423449116e-04f,
+    /* 24, 7 */ -3.797950945e-04f, -2.538454547e-03f, -3.022032460e-03f, +3.634542645e-03f, +1.437825069e-02f, +1.235451773e-02f, -1.562505912e-02f, -4.994339647e-02f, -3.749739364e-02f, +6.046859273e-02f, +2.132645624e-01f, +3.262680950e-01f, +3.177652787e-01f, +1.941247325e-01f, +4.378644843e-02f, -4.344534054e-02f, -4.698760595e-02f, -1.095870195e-02f, +1.396910503e-02f, +1.330148306e-02f, +2.369472628e-03f, -3.268989872e-03f, -2.243004675e-03f, -2.135289700e-04f,
+    /* 24, 8 */ -2.926758839e-04f, -2.392634763e-03f, -3.160336722e-03f, +2.989915102e-03f, +1.386222860e-02f, +1.321798203e-02f, -1.326770657e-02f, -4.855113496e-02f, -4.064923848e-02f, +5.199927852e-02f, +2.037606007e-01f, +3.222640100e-01f, +3.222640100e-01f, +2.037606007e-01f, +5.199927852e-02f, -4.064923848e-02f, -4.855113496e-02f, -1.326770657e-02f, +1.321798203e-02f, +1.386222860e-02f, +2.989915102e-03f, -3.160336722e-03f, -2.392634763e-03f, -2.926758839e-04f,
+    /* 24, 9 */ -2.135289700e-04f, -2.243004675e-03f, -3.268989872e-03f, +2.369472628e-03f, +1.330148306e-02f, +1.396910503e-02f, -1.095870195e-02f, -4.698760595e-02f, -4.344534054e-02f, +4.378644843e-02f, +1.941247325e-01f, +3.177652787e-01f, +3.262680950e-01f, +2.132645624e-01f, +6.046859273e-02f, -3.749739364e-02f, -4.994339647e-02f, -1.562505912e-02f, +1.235451773e-02f, +1.437825069e-02f, +3.634542645e-03f, -3.022032460e-03f, -2.538454547e-03f, -3.797950945e-04f,
+    /* 24,10 */ -1.423449116e-04f, -2.090976552e-03f, -3.349294247e-03f, +1.775448814e-03f, +1.270153536e-02f, +1.460949637e-02f, -8.707438641e-03f, -4.526839113e-02f, -4.589083871e-02f, +3.584659036e-02f, +1.843847637e-01f, +3.127862620e-01f, +3.297647184e-01f, +2.226089010e-01f, +6.917664952e-02f, -3.398586582e-02f, -5.114891871e-02f, -1.802067434e-02f, +1.137765361e-02f, +1.484400357e-02f, +4.300836533e-03f, -2.852899102e-03f, -2.678978820e-03f, -4.748218959e-04f,
+    /* 24,11 */ -7.904503123e-05f, -1.937883690e-03f, -3.402660968e-03f, +1.209792693e-03f, +1.206785167e-02f, +1.514128438e-02f, -6.522599631e-03f, -4.340911052e-02f, -4.799202051e-02f, +2.819489579e-02f, +1.745684830e-01f, +3.073428069e-01f, +3.327426635e-01f, +2.317660961e-01f, +7.810450253e-02f, -3.011195925e-02f, -5.215241275e-02f, -2.044379769e-02f, +1.028691298e-02f, +1.525394912e-02f, +4.985994150e-03f, -2.651898517e-03f, -2.812656385e-03f, -5.776128643e-04f,
+    /* 24,12 */ -2.348660763e-05f, -1.784975276e-03f, -3.430594409e-03f, +6.741710759e-04f, +1.140581394e-02f, +1.556708209e-02f, -4.412143180e-03f, -4.142535273e-02f, -4.975626781e-02f, +2.084522321e-02f, +1.647035561e-01f, +3.014521813e-01f, +3.351923590e-01f, +2.407089312e-01f, +8.723205545e-02f, -2.587426360e-02f, -5.293884648e-02f, -2.288303213e-02f, +9.082429619e-03f, +1.560259046e-02f, +5.686932142e-03f, -2.418147761e-03f, -2.937877889e-03f, -6.879416803e-04f,
+    /* 24,13 */ +2.453170435e-05f, -1.633412152e-03f, -3.434676821e-03f, +1.699725421e-04f, +1.072069264e-02f, +1.588995194e-02f, -2.383292518e-03f, -3.933260713e-02f, -5.119199897e-02f, +1.381006797e-02f, +1.548174220e-01f, +2.951330020e-01f, +3.371059190e-01f, +2.494105998e-01f, +9.653812261e-02f, -2.127269005e-02f, -5.349351937e-02f, -2.532636892e-02f, +7.764974256e-03f, +1.588450664e-02f, +6.400291527e-03f, -2.150934111e-03f, -3.052984548e-03f, -8.054954021e-04f,
+    /* 24,14 */ +6.526428163e-05f, -1.484263468e-03f, -3.416553196e-03f, -3.016869975e-04f, +1.001762126e-02f, +1.611336946e-02f, -4.425295608e-04f, -3.714619841e-02f, -5.230860749e-02f, +7.100538384e-03f, +1.449371908e-01f, +2.884051592e-01f, +3.384771755e-01f, +2.578448120e-01f, +1.060004951e-01f, -1.630850202e-02f, -5.380213751e-02f, -2.776122262e-02f, +6.335978542e-03f, +1.609438844e-02f, +7.122444811e-03f, -1.849729696e-03f, -3.156277727e-03f, -9.298712414e-04f,
+    /* 24,15 */ +9.901282259e-05f, -1.338504197e-03f, -3.377916464e-03f, -7.399572733e-04f, +9.301572693e-03f, +1.624118609e-02f, +1.404407443e-03f, -3.488122370e-02f, -5.311639759e-02f, +7.263382766e-04f, +1.350895441e-01f, +2.812897341e-01f, +3.393017042e-01f, +2.659858985e-01f, +1.155960124e-01f, -1.098434059e-02f, -5.385088872e-02f, -3.017446993e-02f, +4.797556391e-03f, +1.622707505e-02f, +7.849505167e-03f, -1.514205592e-03f, -3.246029352e-03f, -1.060573898e-03f,
+    /* 20, 0 */ -4.161478318e-03f, -1.410215661e-03f, +1.216462436e-02f, +1.839753508e-02f, -1.019572218e-02f, -5.576407638e-02f, -3.857794503e-02f, +9.869941459e-02f, +2.903842315e-01f, +3.819037908e-01f, +2.903842315e-01f, +9.869941459e-02f, -3.857794503e-02f, -5.576407638e-02f, -1.019572218e-02f, +1.839753508e-02f, +1.216462436e-02f, -1.410215661e-03f, -4.161478318e-03f, -1.002136091e-03f,
+    /* 20, 1 */ -4.024812873e-03f, -1.935046598e-03f, +1.120868183e-02f, +1.884704309e-02f, -7.349314558e-03f, -5.377462232e-02f, -4.306909662e-02f, +8.713841011e-02f, +2.797272456e-01f, +3.815142140e-01f, +3.006231905e-01f, +1.105090175e-01f, -3.357053763e-02f, -5.750258783e-02f, -1.313424017e-02f, +1.779561475e-02f, +1.309754391e-02f, -8.338854378e-04f, -4.274968522e-03f, -1.184613130e-03f,
+    /* 20, 2 */ -3.867923854e-03f, -2.407896178e-03f, +1.023778220e-02f, +1.914947854e-02f, -4.608279480e-03f, -5.155911253e-02f, -4.704785126e-02f, +7.585987841e-02f, +2.686929243e-01f, +3.803470604e-01f, +3.104047352e-01f, +1.225315522e-01f, -2.804532708e-02f, -5.896552129e-02f, -1.615031726e-02f, +1.703673197e-02f, +1.399907244e-02f, -2.069933478e-04f, -4.362323551e-03f, -1.376799150e-03f,
+    /* 20, 3 */ -3.693729756e-03f, -2.828715617e-03f, +9.259646102e-03f, +1.931089692e-02f, -1.984565051e-03f, -4.914254142e-02f, -5.052030051e-02f, +6.489576800e-02f, +2.573230589e-01f, +3.784070525e-01f, +3.196909791e-01f, +1.347297046e-01f, -2.200314505e-02f, -6.012863981e-02f, -1.922814732e-02f, +1.611719780e-02f, +1.486058724e-02f, +4.690483654e-04f, -4.420601658e-03f, -1.577606548e-03f,
+    /* 20, 4 */ -3.505092707e-03f, -3.197865053e-03f, +8.281610454e-03f, +1.933799402e-02f, +5.112106557e-04f, -4.654987033e-02f, -5.349467921e-02f, +5.427598051e-02f, +2.456603330e-01f, +3.757020336e-01f, +3.284457292e-01f, +1.470646693e-01f, -1.544725782e-02f, -6.096825350e-02f, -2.235071271e-02f, +1.503425320e-02f, +1.567326271e-02f, +1.192336051e-03f, -4.446907145e-03f, -1.785766437e-03f,
+    /* 20, 5 */ -3.304797071e-03f, -3.516087186e-03f, +7.310594668e-03f, +1.923802927e-02f, +2.869766685e-03f, -4.380589142e-02f, -5.598126618e-02f, +4.402826232e-02f, +2.337481127e-01f, +3.722429273e-01f, +3.366346688e-01f, +1.594963158e-01f, -8.383409345e-03f, -6.146136934e-02f, -2.549983702e-02f, +1.378613431e-02f, +1.642812632e-02f, +1.960461229e-03f, -4.438419451e-03f, -1.999828319e-03f,
+    /* 20, 6 */ -3.095529963e-03f, -3.784479230e-03f, +6.353071566e-03f, +1.901874863e-02f, +5.083157654e-03f, -4.093509653e-02f, -5.799227582e-02f, +3.417810958e-02f, +2.216302330e-01f, +3.680436800e-01f, +3.442255330e-01f, +1.719833640e-01f, -8.198514564e-04f, -6.158584092e-02f, -2.865624686e-02f, +1.237213363e-02f, +1.711611824e-02f, +2.770500592e-03f, -4.392423280e-03f, -2.218161540e-03f,
+    /* 20, 7 */ -2.879863731e-03f, -4.004463491e-03f, +5.415043050e-03f, +1.868830751e-02f, +7.144763866e-03f, -3.796155187e-02f, -5.954174144e-02f, +2.474868675e-02f, +2.093507858e-01f, +3.631211887e-01f, +3.511882735e-01f, +1.844835679e-01f, +7.232639407e-03f, -6.132051750e-02f, -3.179964276e-02f, +1.079265669e-02f, +1.772815465e-02f, +3.619009684e-03f, -4.306339536e-03f, -2.438958613e-03f,
+    /* 20, 8 */ -2.660240473e-03f, -4.177756859e-03f, +4.502020476e-03f, +1.825519424e-02f, +9.049273418e-03f, -3.490877898e-02f, -6.064539119e-02f, +1.576075912e-02f, +1.969539074e-01f, +3.574952133e-01f, +3.574952133e-01f, +1.969539074e-01f, +1.576075912e-02f, -6.064539119e-02f, -3.490877898e-02f, +9.049273418e-03f, +1.825519424e-02f, +4.502020476e-03f, -4.177756859e-03f, -2.660240473e-03f,
+    /* 20, 9 */ -2.438958613e-03f, -4.306339536e-03f, +3.619009684e-03f, +1.772815465e-02f, +1.079265669e-02f, -3.179964276e-02f, -6.132051750e-02f, +7.232639407e-03f, +1.844835679e-01f, +3.511882735e-01f, +3.631211887e-01f, +2.093507858e-01f, +2.474868675e-02f, -5.954174144e-02f, -3.796155187e-02f, +7.144763866e-03f, +1.868830751e-02f, +5.415043050e-03f, -4.004463491e-03f, -2.879863731e-03f,
+    /* 20,10 */ -2.218161540e-03f, -4.392423280e-03f, +2.770500592e-03f, +1.711611824e-02f, +1.237213363e-02f, -2.865624686e-02f, -6.158584092e-02f, -8.198514564e-04f, +1.719833640e-01f, +3.442255330e-01f, +3.680436800e-01f, +2.216302330e-01f, +3.417810958e-02f, -5.799227582e-02f, -4.093509653e-02f, +5.083157654e-03f, +1.901874863e-02f, +6.353071566e-03f, -3.784479230e-03f, -3.095529963e-03f,
+    /* 20,11 */ -1.999828319e-03f, -4.438419451e-03f, +1.960461229e-03f, +1.642812632e-02f, +1.378613431e-02f, -2.549983702e-02f, -6.146136934e-02f, -8.383409345e-03f, +1.594963158e-01f, +3.366346688e-01f, +3.722429273e-01f, +2.337481127e-01f, +4.402826232e-02f, -5.598126618e-02f, -4.380589142e-02f, +2.869766685e-03f, +1.923802927e-02f, +7.310594668e-03f, -3.516087186e-03f, -3.304797071e-03f,
+    /* 20,12 */ -1.785766437e-03f, -4.446907145e-03f, +1.192336051e-03f, +1.567326271e-02f, +1.503425320e-02f, -2.235071271e-02f, -6.096825350e-02f, -1.544725782e-02f, +1.470646693e-01f, +3.284457292e-01f, +3.757020336e-01f, +2.456603330e-01f, +5.427598051e-02f, -5.349467921e-02f, -4.654987033e-02f, +5.112106557e-04f, +1.933799402e-02f, +8.281610454e-03f, -3.197865053e-03f, -3.505092707e-03f,
+    /* 20,13 */ -1.577606548e-03f, -4.420601658e-03f, +4.690483654e-04f, +1.486058724e-02f, +1.611719780e-02f, -1.922814732e-02f, -6.012863981e-02f, -2.200314505e-02f, +1.347297046e-01f, +3.196909791e-01f, +3.784070525e-01f, +2.573230589e-01f, +6.489576800e-02f, -5.052030051e-02f, -4.914254142e-02f, -1.984565051e-03f, +1.931089692e-02f, +9.259646102e-03f, -2.828715617e-03f, -3.693729756e-03f,
+    /* 20,14 */ -1.376799150e-03f, -4.362323551e-03f, -2.069933478e-04f, +1.399907244e-02f, +1.703673197e-02f, -1.615031726e-02f, -5.896552129e-02f, -2.804532708e-02f, +1.225315522e-01f, +3.104047352e-01f, +3.803470604e-01f, +2.686929243e-01f, +7.585987841e-02f, -4.704785126e-02f, -5.155911253e-02f, -4.608279480e-03f, +1.914947854e-02f, +1.023778220e-02f, -2.407896178e-03f, -3.867923854e-03f,
+    /* 20,15 */ -1.184613130e-03f, -4.274968522e-03f, -8.338854378e-04f, +1.309754391e-02f, +1.779561475e-02f, -1.313424017e-02f, -5.750258783e-02f, -3.357053763e-02f, +1.105090175e-01f, +3.006231905e-01f, +3.815142140e-01f, +2.797272456e-01f, +8.713841011e-02f, -4.306909662e-02f, -5.377462232e-02f, -7.349314558e-03f, +1.884704309e-02f, +1.120868183e-02f, -1.935046598e-03f, -4.024812873e-03f,
+    /* 20, 0 */ -1.329352252e-03f, -4.865562069e-03f, +1.662947600e-03f, +1.893743982e-02f, +1.052975469e-02f, -4.314924294e-02f, -6.168215525e-02f, +6.793829558e-02f, +3.007295231e-01f, +4.214013440e-01f, +3.007295231e-01f, +6.793829558e-02f, -6.168215525e-02f, -4.314924294e-02f, +1.052975469e-02f, +1.893743982e-02f, +1.662947600e-03f, -4.865562069e-03f, -1.329352252e-03f, +0.000000000e+00f,
+    /* 20, 1 */ -1.106503038e-03f, -4.784011640e-03f, +7.620481209e-04f, +1.810159639e-02f, +1.258770510e-02f, -3.946126222e-02f, -6.412166469e-02f, +5.516844650e-02f, +2.870012762e-01f, +4.208780002e-01f, +3.139874692e-01f, +8.118253044e-02f, -5.860314053e-02f, -4.671882663e-02f, +8.254179692e-03f, +1.967037055e-02f, +2.622520317e-03f, -4.905078775e-03f, -1.565095816e-03f, +0.000000000e+00f,
+    /* 20, 2 */ -8.979094201e-04f, -4.664743082e-03f, -7.633034189e-05f, +1.717630323e-02f, +1.442449893e-02f, -3.568911340e-02f, -6.594250862e-02f, +4.291292121e-02f, +2.728656387e-01f, +4.193105477e-01f, +3.267137817e-01f, +9.485763802e-02f, -5.486676259e-02f, -5.013477905e-02f, +5.766539049e-03f, +2.028700325e-02f, +3.636071544e-03f, -4.898357639e-03f, -1.812078842e-03f, +3.513221827e-04f,
+    /* 20, 3 */ -7.046452899e-04f, -4.512131585e-03f, -8.491713087e-04f, +1.617498084e-02f, +1.603855621e-02f, -3.186582692e-02f, -6.716828458e-02f, +3.120792005e-02f, +2.583867198e-01f, +4.167067067e-01f, +3.388490774e-01f, +1.089167196e-01f, -5.045835457e-02f, -5.336106841e-02f, +3.074480429e-03f, +2.077415592e-02f, +4.698051453e-03f, -4.841360048e-03f, -2.068349661e-03f, +3.288882594e-04f,
+    /* 20, 4 */ -5.275063595e-04f, -4.330562617e-03f, -1.554266965e-03f, +1.511089362e-02f, +1.743016199e-02f, -2.802305698e-02f, -6.782511100e-02f, +2.008577030e-02f, +2.436294448e-01f, +4.130792899e-01f, +3.503362849e-01f, +1.233097059e-01f, -4.536663323e-02f, -5.636108634e-02f, +1.877878266e-04f, +2.111898038e-02f, +5.802054958e-03f, -4.730268616e-03f, -2.331660076e-03f, +2.941732022e-04f,
+    /* 20, 5 */ -3.670219514e-04f, -4.124385552e-03f, -2.190189120e-03f, +1.399704337e-02f, +1.860136846e-02f, -2.419092016e-02f, -6.794137953e-02f, +9.574818877e-03f, +2.286591711e-01f, +4.084461208e-01f, +3.611209950e-01f, +1.379835966e-01f, -3.958387309e-02f, -5.909788086e-02f, -2.881585959e-03f, +2.130909659e-02f, +6.940829951e-03f, -4.561544087e-03f, -2.599469210e-03f, +2.461206998e-04f,
+    /* 20, 6 */ -2.234691091e-04f, -3.897870552e-03f, -2.756254356e-03f, +1.284607053e-02f, +1.955588746e-02f, -2.039785121e-02f, -6.754749865e-02f, -3.006469464e-04f, +2.135413047e-01f, +4.028299211e-01f, +3.711517965e-01f, +1.528827232e-01f, -3.310606021e-02f, -6.153439984e-02f, -6.119502817e-03f, +2.133272965e-02f, +8.106294302e-03f, -4.331982887e-03f, -2.868951124e-03f, +1.837671888e-04f,
+    /* 20, 7 */ -9.688872179e-05f, -3.655168976e-03f, -3.252484018e-03f, +1.167016373e-02f, +2.029897446e-02f, -1.667047641e-02f, -6.667563061e-02f, -9.520450398e-03f, +1.983409219e-01f, +3.962581664e-01f, +3.803805952e-01f, +1.679490334e-01f, -2.593302438e-02f, -6.363374348e-02f, -9.509639499e-03f, +2.117884859e-02f, +9.289561904e-03f, -4.038774728e-03f, -3.137006363e-03f, +1.062635081e-04f,
+    /* 20, 8 */ +1.289664191e-05f, -3.400277535e-03f, -3.679559671e-03f, +1.048097797e-02f, +2.083730557e-02f, -1.303350512e-02f, -6.535942396e-02f, -1.806854797e-02f, +1.831223958e-01f, +3.887629129e-01f, +3.887629129e-01f, +1.831223958e-01f, -1.806854797e-02f, -6.535942396e-02f, -1.303350512e-02f, +2.083730557e-02f, +1.048097797e-02f, -3.679559671e-03f, -3.400277535e-03f, +1.289664191e-05f,
+    /* 20, 9 */ +1.062635081e-04f, -3.137006363e-03f, -4.038774728e-03f, +9.289561904e-03f, +2.117884859e-02f, -9.509639499e-03f, -6.363374348e-02f, -2.593302438e-02f, +1.679490334e-01f, +3.803805952e-01f, +3.962581664e-01f, +1.983409219e-01f, -9.520450398e-03f, -6.667563061e-02f, -1.667047641e-02f, +2.029897446e-02f, +1.167016373e-02f, -3.252484018e-03f, -3.655168976e-03f, -9.688872179e-05f,
+    /* 20,10 */ +1.837671888e-04f, -2.868951124e-03f, -4.331982887e-03f, +8.106294302e-03f, +2.133272965e-02f, -6.119502817e-03f, -6.153439984e-02f, -3.310606021e-02f, +1.528827232e-01f, +3.711517965e-01f, +4.028299211e-01f, +2.135413047e-01f, -3.006469464e-04f, -6.754749865e-02f, -2.039785121e-02f, +1.955588746e-02f, +1.284607053e-02f, -2.756254356e-03f, -3.897870552e-03f, -2.234691091e-04f,
+    /* 20,11 */ +2.461206998e-04f, -2.599469210e-03f, -4.561544087e-03f, +6.940829951e-03f, +2.130909659e-02f, -2.881585959e-03f, -5.909788086e-02f, -3.958387309e-02f, +1.379835966e-01f, +3.611209950e-01f, +4.084461208e-01f, +2.286591711e-01f, +9.574818877e-03f, -6.794137953e-02f, -2.419092016e-02f, +1.860136846e-02f, +1.399704337e-02f, -2.190189120e-03f, -4.124385552e-03f, -3.670219514e-04f,
+    /* 20,12 */ +2.941732022e-04f, -2.331660076e-03f, -4.730268616e-03f, +5.802054958e-03f, +2.111898038e-02f, +1.877878266e-04f, -5.636108634e-02f, -4.536663323e-02f, +1.233097059e-01f, +3.503362849e-01f, +4.130792899e-01f, +2.436294448e-01f, +2.008577030e-02f, -6.782511100e-02f, -2.802305698e-02f, +1.743016199e-02f, +1.511089362e-02f, -1.554266965e-03f, -4.330562617e-03f, -5.275063595e-04f,
+    /* 20,13 */ +3.288882594e-04f, -2.068349661e-03f, -4.841360048e-03f, +4.698051453e-03f, +2.077415592e-02f, +3.074480429e-03f, -5.336106841e-02f, -5.045835457e-02f, +1.089167196e-01f, +3.388490774e-01f, +4.167067067e-01f, +2.583867198e-01f, +3.120792005e-02f, -6.716828458e-02f, -3.186582692e-02f, +1.603855621e-02f, +1.617498084e-02f, -8.491713087e-04f, -4.512131585e-03f, -7.046452899e-04f,
+    /* 20,14 */ +3.513221827e-04f, -1.812078842e-03f, -4.898357639e-03f, +3.636071544e-03f, +2.028700325e-02f, +5.766539049e-03f, -5.013477905e-02f, -5.486676259e-02f, +9.485763802e-02f, +3.267137817e-01f, +4.193105477e-01f, +2.728656387e-01f, +4.291292121e-02f, -6.594250862e-02f, -3.568911340e-02f, +1.442449893e-02f, +1.717630323e-02f, -7.633034189e-05f, -4.664743082e-03f, -8.979094201e-04f,
+    /* 20,15 */ +0.000000000e+00f, -1.565095816e-03f, -4.905078775e-03f, +2.622520317e-03f, +1.967037055e-02f, +8.254179692e-03f, -4.671882663e-02f, -5.860314053e-02f, +8.118253044e-02f, +3.139874692e-01f, +4.208780002e-01f, +2.870012762e-01f, +5.516844650e-02f, -6.412166469e-02f, -3.946126222e-02f, +1.258770510e-02f, +1.810159639e-02f, +7.620481209e-04f, -4.784011640e-03f, -1.106503038e-03f,
+    /* 20, 0 */ +3.735125865e-04f, -2.550984103e-03f, -4.871486096e-03f, +1.016287769e-02f, +2.252246682e-02f, -2.231523982e-02f, -7.431762424e-02f, +3.414137659e-02f, +3.062278786e-01f, +4.608988972e-01f, +3.062278786e-01f, +3.414137659e-02f, -7.431762424e-02f, -2.231523982e-02f, +2.252246682e-02f, +1.016287769e-02f, -4.871486096e-03f, -2.550984103e-03f, +3.735125865e-04f, +0.000000000e+00f,
+    /* 20, 1 */ +3.929324583e-04f, -2.236335973e-03f, -5.106050653e-03f, +8.748210493e-03f, +2.303691111e-02f, -1.786093260e-02f, -7.411924916e-02f, +2.086992015e-02f, +2.890848421e-01f, +4.602142272e-01f, +3.228796668e-01f, +4.817530421e-02f, -7.382833631e-02f, -2.685541297e-02f, +2.174514756e-02f, +1.158816420e-02f, -4.555417854e-03f, -2.871502680e-03f, +3.387491377e-04f, +0.000000000e+00f,
+    /* 20, 2 */ +0.000000000e+00f, -1.931175707e-03f, -5.263447672e-03f, +7.357928950e-03f, +2.330080531e-02f, -1.352771902e-02f, -7.327813327e-02f, +8.401230311e-03f, +2.715429904e-01f, +4.581642525e-01f, +3.389493512e-01f, +6.292517925e-02f, -7.260941515e-02f, -3.144322519e-02f, +2.069454672e-02f, +1.300917115e-02f, -4.154170312e-03f, -3.193828376e-03f, +2.870815261e-04f, +0.000000000e+00f,
+    /* 20, 3 */ +0.000000000e+00f, -1.638662038e-03f, -5.348575554e-03f, +6.004591146e-03f, +2.332816092e-02f, -9.347674729e-03f, -7.184169597e-02f, -3.230759097e-03f, +2.536956771e-01f, +4.547610488e-01f, +3.543483057e-01f, +7.833843581e-02f, -7.062228683e-02f, -3.603763775e-02f, +1.936240016e-02f, +1.440996064e-02f, -3.664825055e-03f, -3.513472060e-03f, +2.170808154e-04f, +0.000000000e+00f,
+    /* 20, 4 */ +0.000000000e+00f, -1.361489293e-03f, -5.366796329e-03f, +4.699488665e-03f, +2.313444000e-02f, -5.349604768e-03f, -6.985939290e-02f, -1.399855627e-02f, +2.356365195e-01f, +4.500246402e-01f, +3.689907742e-01f, +9.435670405e-02f, -6.783220328e-02f, -4.059502103e-02f, +1.774280068e-02f, +1.577366666e-02f, -3.085314600e-03f, -3.825546457e-03f, +1.274866066e-04f, +0.000000000e+00f,
+    /* 20, 5 */ +0.000000000e+00f, -1.101888322e-03f, -5.323837897e-03f, +3.452605627e-03f, +2.273631696e-02f, -1.558959414e-03f, -6.738225311e-02f, -2.388113922e-02f, +2.174587480e-01f, +4.439828472e-01f, +3.827944964e-01f, +1.109160995e-01f, -6.420865295e-02f, -4.506940842e-02f, +1.583239979e-02f, +1.708262332e-02f, -2.414511840e-03f, -4.124801502e-03f, +1.724424543e-05f, +0.000000000e+00f,
+    /* 20, 6 */ +0.000000000e+00f, -8.616336525e-04f, -5.225698259e-03f, +2.272594520e-03f, +2.215144089e-02f, +2.002216238e-03f, -6.446241843e-02f, -3.286393171e-02f, +1.992545658e-01f, +4.366710759e-01f, +3.956813102e-01f, +1.279475618e-01f, -5.972574809e-02f, -4.941278189e-02f, +1.363059437e-02f, +1.831850983e-02f, -1.652313816e-03f, -4.405667401e-03f, -1.144582079e-04f, +0.000000000e+00f,
+    /* 20, 7 */ +0.000000000e+00f, -6.420563626e-04f, -5.078552799e-03f, +1.166768183e-03f, +2.139820068e-02f, +5.315299677e-03f, -6.115268907e-02f, -4.093872873e-02f, +1.811145256e-01f, +4.281320500e-01f, +4.075777294e-01f, +1.453772407e-01f, -5.436258502e-02f, -5.357538755e-02f, +1.113969540e-02f, +1.946251142e-02f, -7.997184460e-04f, -4.662305323e-03f, -2.681538305e-04f, +0.000000000e+00f,
+    /* 20, 8 */ +0.000000000e+00f, -4.440621234e-04f, -4.888665552e-03f, +1.411071672e-04f, +2.049549532e-02f, +8.365076494e-03f, -5.750607943e-02f, -4.810357305e-02f, +1.631269271e-01f, +4.184154881e-01f, +4.184154881e-01f, +1.631269271e-01f, -4.810357305e-02f, -5.750607943e-02f, +8.365076494e-03f, +2.049549532e-02f, +1.411071672e-04f, -4.888665552e-03f, -4.440621234e-04f, +0.000000000e+00f,
+    /* 20, 9 */ +0.000000000e+00f, -2.681538305e-04f, -4.662305323e-03f, -7.997184460e-04f, +1.946251142e-02f, +1.113969540e-02f, -5.357538755e-02f, -5.436258502e-02f, +1.453772407e-01f, +4.075777294e-01f, +4.281320500e-01f, +1.811145256e-01f, -4.093872873e-02f, -6.115268907e-02f, +5.315299677e-03f, +2.139820068e-02f, +1.166768183e-03f, -5.078552799e-03f, -6.420563626e-04f, +0.000000000e+00f,
+    /* 20,10 */ +0.000000000e+00f, -1.144582079e-04f, -4.405667401e-03f, -1.652313816e-03f, +1.831850983e-02f, +1.363059437e-02f, -4.941278189e-02f, -5.972574809e-02f, +1.279475618e-01f, +3.956813102e-01f, +4.366710759e-01f, +1.992545658e-01f, -3.286393171e-02f, -6.446241843e-02f, +2.002216238e-03f, +2.215144089e-02f, +2.272594520e-03f, -5.225698259e-03f, -8.616336525e-04f, +0.000000000e+00f,
+    /* 20,11 */ +0.000000000e+00f, +1.724424543e-05f, -4.124801502e-03f, -2.414511840e-03f, +1.708262332e-02f, +1.583239979e-02f, -4.506940842e-02f, -6.420865295e-02f, +1.109160995e-01f, +3.827944964e-01f, +4.439828472e-01f, +2.174587480e-01f, -2.388113922e-02f, -6.738225311e-02f, -1.558959414e-03f, +2.273631696e-02f, +3.452605627e-03f, -5.323837897e-03f, -1.101888322e-03f, +0.000000000e+00f,
+    /* 20,12 */ +0.000000000e+00f, +1.274866066e-04f, -3.825546457e-03f, -3.085314600e-03f, +1.577366666e-02f, +1.774280068e-02f, -4.059502103e-02f, -6.783220328e-02f, +9.435670405e-02f, +3.689907742e-01f, +4.500246402e-01f, +2.356365195e-01f, -1.399855627e-02f, -6.985939290e-02f, -5.349604768e-03f, +2.313444000e-02f, +4.699488665e-03f, -5.366796329e-03f, -1.361489293e-03f, +0.000000000e+00f,
+    /* 20,13 */ +0.000000000e+00f, +2.170808154e-04f, -3.513472060e-03f, -3.664825055e-03f, +1.440996064e-02f, +1.936240016e-02f, -3.603763775e-02f, -7.062228683e-02f, +7.833843581e-02f, +3.543483057e-01f, +4.547610488e-01f, +2.536956771e-01f, -3.230759097e-03f, -7.184169597e-02f, -9.347674729e-03f, +2.332816092e-02f, +6.004591146e-03f, -5.348575554e-03f, -1.638662038e-03f, +0.000000000e+00f,
+    /* 20,14 */ +0.000000000e+00f, +2.870815261e-04f, -3.193828376e-03f, -4.154170312e-03f, +1.300917115e-02f, +2.069454672e-02f, -3.144322519e-02f, -7.260941515e-02f, +6.292517925e-02f, +3.389493512e-01f, +4.581642525e-01f, +2.715429904e-01f, +8.401230311e-03f, -7.327813327e-02f, -1.352771902e-02f, +2.330080531e-02f, +7.357928950e-03f, -5.263447672e-03f, -1.931175707e-03f, +0.000000000e+00f,
+    /* 20,15 */ +0.000000000e+00f, +3.387491377e-04f, -2.871502680e-03f, -4.555417854e-03f, +1.158816420e-02f, +2.174514756e-02f, -2.685541297e-02f, -7.382833631e-02f, +4.817530421e-02f, +3.228796668e-01f, +4.602142272e-01f, +2.890848421e-01f, +2.086992015e-02f, -7.411924916e-02f, -1.786093260e-02f, +2.303691111e-02f, +8.748210493e-03f, -5.106050653e-03f, -2.236335973e-03f, +3.929324583e-04f,
+    /* 16, 0 */ -4.898743621e-03f, -8.679086087e-05f, +2.336043359e-02f, +2.135055302e-04f, -7.556698393e-02f, -3.418085064e-04f, +3.068350485e-01f, +5.003964504e-01f, +3.068350485e-01f, -3.418085064e-04f, -7.556698393e-02f, +2.135055302e-04f, +2.336043359e-02f, -8.679086087e-05f, -4.898743621e-03f, +1.466795211e-05f,
+    /* 16, 1 */ -4.577177643e-03f, -1.168030162e-03f, +2.231338881e-02f, +4.259286102e-03f, -7.256190983e-02f, -1.325523148e-02f, +2.859961851e-01f, +4.995203198e-01f, +3.272077887e-01f, +1.366916740e-02f, -7.794631959e-02f, -4.137969722e-03f, +2.421268789e-02f, +1.104119466e-03f, -5.186216174e-03f, -1.424716020e-04f,
+    /* 16, 2 */ -4.229744004e-03f, -2.135347302e-03f, +2.109817837e-02f, +7.975999347e-03f, -6.900371451e-02f, -2.503996167e-02f, +2.648211478e-01f, +4.968980143e-01f, +3.469854770e-01f, +2.873680235e-02f, -7.962890015e-02f, -8.766610174e-03f, +2.484400873e-02f, +2.398541233e-03f, -5.431090074e-03f, -3.278051009e-04f,
+    /* 16, 3 */ -3.864289504e-03f, -2.986316790e-03f, +1.974155805e-02f, +1.134537917e-02f, -6.496587406e-02f, -3.567459207e-02f, +2.434398342e-01f, +4.925477372e-01f, +3.660412816e-01f, +4.481051979e-02f, -8.054606315e-02f, -1.363873477e-02f, +2.522910176e-02f, +3.788344934e-03f, -5.624692566e-03f, -5.415628140e-04f,
+    /* 16, 4 */ -3.488195595e-03f, -3.720237037e-03f, +1.827008292e-02f, +1.435416313e-02f, -6.052200094e-02f, -4.514733704e-02f, +2.219810322e-01f, +4.864996469e-01f, +3.842515379e-01f, +6.183022559e-02f, -8.063225815e-02f, -1.871557408e-02f, +2.534390000e-02f, +5.263393235e-03f, -5.758306879e-03f, -7.834433658e-04f,
+    /* 16, 5 */ -3.108305495e-03f, -4.338012209e-03f, +1.670979794e-02f, +1.699394035e-02f, -5.574514781e-02f, -5.345583367e-02f, +2.005713869e-01f, +4.787955863e-01f, +4.014968013e-01f, +7.972656727e-02f, -7.982580067e-02f, -2.395339311e-02f, +2.516595041e-02f, +6.811528665e-03f, -5.823306183e-03f, -1.052565974e-03f,
+    /* 16, 6 */ -2.730865011e-03f, -4.842020290e-03f, +1.508595356e-02f, +1.926095418e-02f, -5.070714412e-02f, -6.060686010e-02f, +1.793344024e-01f, +4.694887096e-01f, +4.176628724e-01f, +9.842128660e-02f, -7.806961406e-02f, -2.930367505e-02f, +2.467480385e-02f, +8.418588685e-03f, -5.811296621e-03f, -1.347428958e-03f,
+    /* 16, 7 */ -2.361477017e-03f, -5.235970004e-03f, +1.342274837e-02f, +2.115586371e-02f, -4.547797122e-02f, -6.661597542e-02f, +1.583894867e-01f, +4.586430086e-01f, +4.326417845e-01f, +1.178276637e-01f, -7.531195111e-02f, -3.471336612e-02f, +2.385240383e-02f, +1.006844961e-02f, -5.714267850e-03f, -1.665875716e-03f,
+    /* 16, 8 */ -2.005069351e-03f, -5.524749278e-03f, +1.174310057e-02f, +2.268346885e-02f, -4.012518151e-02f, -7.150708699e-02f, +1.378510501e-01f, +4.463327434e-01f, +4.463327434e-01f, +1.378510501e-01f, -7.150708699e-02f, -4.012518151e-02f, +2.268346885e-02f, +1.174310057e-02f, -5.524749278e-03f, -2.005069351e-03f,
+    /* 16, 9 */ -1.665875716e-03f, -5.714267850e-03f, +1.006844961e-02f, +2.385240383e-02f, -3.471336612e-02f, -7.531195111e-02f, +1.178276637e-01f, +4.326417845e-01f, +4.586430086e-01f, +1.583894867e-01f, -6.661597542e-02f, -4.547797122e-02f, +2.115586371e-02f, +1.342274837e-02f, -5.235970004e-03f, -2.361477017e-03f,
+    /* 16,10 */ -1.347428958e-03f, -5.811296621e-03f, +8.418588685e-03f, +2.467480385e-02f, -2.930367505e-02f, -7.806961406e-02f, +9.842128660e-02f, +4.176628724e-01f, +4.694887096e-01f, +1.793344024e-01f, -6.060686010e-02f, -5.070714412e-02f, +1.926095418e-02f, +1.508595356e-02f, -4.842020290e-03f, -2.730865011e-03f,
+    /* 16,11 */ -1.052565974e-03f, -5.823306183e-03f, +6.811528665e-03f, +2.516595041e-02f, -2.395339311e-02f, -7.982580067e-02f, +7.972656727e-02f, +4.014968013e-01f, +4.787955863e-01f, +2.005713869e-01f, -5.345583367e-02f, -5.574514781e-02f, +1.699394035e-02f, +1.670979794e-02f, -4.338012209e-03f, -3.108305495e-03f,
+    /* 16,12 */ -7.834433658e-04f, -5.758306879e-03f, +5.263393235e-03f, +2.534390000e-02f, -1.871557408e-02f, -8.063225815e-02f, +6.183022559e-02f, +3.842515379e-01f, +4.864996469e-01f, +2.219810322e-01f, -4.514733704e-02f, -6.052200094e-02f, +1.435416313e-02f, +1.827008292e-02f, -3.720237037e-03f, -3.488195595e-03f,
+    /* 16,13 */ -5.415628140e-04f, -5.624692566e-03f, +3.788344934e-03f, +2.522910176e-02f, -1.363873477e-02f, -8.054606315e-02f, +4.481051979e-02f, +3.660412816e-01f, +4.925477372e-01f, +2.434398342e-01f, -3.567459207e-02f, -6.496587406e-02f, +1.134537917e-02f, +1.974155805e-02f, -2.986316790e-03f, -3.864289504e-03f,
+    /* 16,14 */ -3.278051009e-04f, -5.431090074e-03f, +2.398541233e-03f, +2.484400873e-02f, -8.766610174e-03f, -7.962890015e-02f, +2.873680235e-02f, +3.469854770e-01f, +4.968980143e-01f, +2.648211478e-01f, -2.503996167e-02f, -6.900371451e-02f, +7.975999347e-03f, +2.109817837e-02f, -2.135347302e-03f, -4.229744004e-03f,
+    /* 16,15 */ -1.424716020e-04f, -5.186216174e-03f, +1.104119466e-03f, +2.421268789e-02f, -4.137969722e-03f, -7.794631959e-02f, +1.366916740e-02f, +3.272077887e-01f, +4.995203198e-01f, +2.859961851e-01f, -1.325523148e-02f, -7.256190983e-02f, +4.259286102e-03f, +2.231338881e-02f, -1.168030162e-03f, -4.577177643e-03f,
+    /* 16, 0 */ -1.854349243e-03f, -5.842655877e-03f, +1.571555836e-02f, +1.847159410e-02f, -6.634453543e-02f, -3.320569278e-02f, +3.025932104e-01f, +5.398940036e-01f, +3.025932104e-01f, -3.320569278e-02f, -6.634453543e-02f, +1.847159410e-02f, +1.571555836e-02f, -5.842655877e-03f, -1.854349243e-03f, +0.000000000e+00f,
+    /* 16, 1 */ -1.480579358e-03f, -6.106700866e-03f, +1.376986381e-02f, +2.107103425e-02f, -6.084498265e-02f, -4.482173865e-02f, +2.778559558e-01f, +5.387937054e-01f, +3.269511876e-01f, -2.013603096e-02f, -7.140657205e-02f, +1.540395578e-02f, +1.762103499e-02f, -5.450677830e-03f, -2.253515747e-03f, +0.000000000e+00f,
+    /* 16, 2 */ -1.136163241e-03f, -6.251562574e-03f, +1.181432209e-02f, +2.320368920e-02f, -5.500558000e-02f, -5.497544958e-02f, +2.529151163e-01f, +5.355017071e-01f, +3.507537104e-01f, -5.635398845e-03f, -7.593183970e-02f, +1.187314151e-02f, +1.945395611e-02f, -4.923375547e-03f, -2.673102395e-03f, +0.000000000e+00f,
+    /* 16, 3 */ -8.240613879e-04f, -6.287094834e-03f, +9.877041313e-03f, +2.487696888e-02f, -4.892141083e-02f, -6.367164518e-02f, +2.279442418e-01f, +5.300446041e-01f, +3.738258052e-01f, +1.025938806e-02f, -7.982055919e-02f, +7.890985370e-03f, +2.118036474e-02f, -4.254967852e-03f, -3.107109230e-03f, +0.000000000e+00f,
+    /* 16, 4 */ -5.462770218e-04f, -6.224002243e-03f, +7.983624178e-03f, +2.610378910e-02f, -4.268405269e-02f, -7.092815895e-02f, +2.031131300e-01f, +5.224664143e-01f, +3.959953988e-01f, +2.749725254e-02f, -8.297353764e-02f, +3.476439880e-03f, +2.276508169e-02f, -3.441527233e-03f, -3.548528769e-03f, +4.634120047e-04f,
+    /* 16, 5 */ -3.039101756e-04f, -6.073592210e-03f, +6.156978545e-03f, +2.690203687e-02f, -3.638073666e-02f, -7.677518685e-02f, +1.785862812e-01f, +5.128281199e-01f, +4.170950072e-01f, +4.601292009e-02f, -8.529332152e-02f, -1.344195268e-03f, +2.417214928e-02f, -2.481210963e-03f, -3.989383394e-03f, +4.400286560e-04f,
+    /* 16, 6 */ -9.722433303e-05f, -5.847536081e-03f, +4.417180930e-03f, +2.729400173e-02f, -3.009359622e-02f, -8.125451993e-02f, +1.545214364e-01f, +5.012070337e-01f, +4.369633957e-01f, +6.572714234e-02f, -8.668537697e-02f, -6.537129252e-03f, +2.536531778e-02f, -1.374474827e-03f, -4.420785166e-03f, +3.921992729e-04f,
+    /* 16, 7 */ +7.427658644e-05f, -5.557642708e-03f, +2.781391675e-03f, +2.730578215e-02f, -2.389901141e-02f, -8.441867356e-02f, +1.310682124e-01f, +4.876959980e-01f, +4.554471907e-01f, +8.654708074e-02f, -8.705928349e-02f, -1.206102709e-02f, +2.630856978e-02f, -1.242645535e-04f, -4.833018707e-03f, +3.169896142e-04f,
+    /* 16, 8 */ +2.117631665e-04f, -5.215647395e-03f, +1.263819875e-03f, +2.696667686e-02f, -1.786705263e-02f, -8.632992647e-02f, +1.083668491e-01f, +4.724024249e-01f, +4.724024249e-01f, +1.083668491e-01f, -8.632992647e-02f, -1.786705263e-02f, +2.696667686e-02f, +1.263819875e-03f, -5.215647395e-03f, +2.117631665e-04f,
+    /* 16, 9 */ +3.169896142e-04f, -4.833018707e-03f, -1.242645535e-04f, +2.630856978e-02f, -1.206102709e-02f, -8.705928349e-02f, +8.654708074e-02f, +4.554471907e-01f, +4.876959980e-01f, +1.310682124e-01f, -8.441867356e-02f, -2.389901141e-02f, +2.730578215e-02f, +2.781391675e-03f, -5.557642708e-03f, +7.427658644e-05f,
+    /* 16,10 */ +3.921992729e-04f, -4.420785166e-03f, -1.374474827e-03f, +2.536531778e-02f, -6.537129252e-03f, -8.668537697e-02f, +6.572714234e-02f, +4.369633957e-01f, +5.012070337e-01f, +1.545214364e-01f, -8.125451993e-02f, -3.009359622e-02f, +2.729400173e-02f, +4.417180930e-03f, -5.847536081e-03f, -9.722433303e-05f,
+    /* 16,11 */ +4.400286560e-04f, -3.989383394e-03f, -2.481210963e-03f, +2.417214928e-02f, -1.344195268e-03f, -8.529332152e-02f, +4.601292009e-02f, +4.170950072e-01f, +5.128281199e-01f, +1.785862812e-01f, -7.677518685e-02f, -3.638073666e-02f, +2.690203687e-02f, +6.156978545e-03f, -6.073592210e-03f, -3.039101756e-04f,
+    /* 16,12 */ +4.634120047e-04f, -3.548528769e-03f, -3.441527233e-03f, +2.276508169e-02f, +3.476439880e-03f, -8.297353764e-02f, +2.749725254e-02f, +3.959953988e-01f, +5.224664143e-01f, +2.031131300e-01f, -7.092815895e-02f, -4.268405269e-02f, +2.610378910e-02f, +7.983624178e-03f, -6.224002243e-03f, -5.462770218e-04f,
+    /* 16,13 */ +0.000000000e+00f, -3.107109230e-03f, -4.254967852e-03f, +2.118036474e-02f, +7.890985370e-03f, -7.982055919e-02f, +1.025938806e-02f, +3.738258052e-01f, +5.300446041e-01f, +2.279442418e-01f, -6.367164518e-02f, -4.892141083e-02f, +2.487696888e-02f, +9.877041313e-03f, -6.287094834e-03f, -8.240613879e-04f,
+    /* 16,14 */ +0.000000000e+00f, -2.673102395e-03f, -4.923375547e-03f, +1.945395611e-02f, +1.187314151e-02f, -7.593183970e-02f, -5.635398845e-03f, +3.507537104e-01f, +5.355017071e-01f, +2.529151163e-01f, -5.497544958e-02f, -5.500558000e-02f, +2.320368920e-02f, +1.181432209e-02f, -6.251562574e-03f, -1.136163241e-03f,
+    /* 16,15 */ +0.000000000e+00f, -2.253515747e-03f, -5.450677830e-03f, +1.762103499e-02f, +1.540395578e-02f, -7.140657205e-02f, -2.013603096e-02f, +3.269511876e-01f, +5.387937054e-01f, +2.778559558e-01f, -4.482173865e-02f, -6.084498265e-02f, +2.107103425e-02f, +1.376986381e-02f, -6.106700866e-03f, -1.480579358e-03f,
+    /* 16, 0 */ +2.517634455e-04f, -5.956310854e-03f, +5.008864062e-03f, +2.864631470e-02f, -4.909056125e-02f, -6.235528720e-02f, +2.936293584e-01f, +5.793915568e-01f, +2.936293584e-01f, -6.235528720e-02f, -4.909056125e-02f, +2.864631470e-02f, +5.008864062e-03f, -5.956310854e-03f, +2.517634455e-04f, +0.000000000e+00f,
+    /* 16, 1 */ +3.647589216e-04f, -5.559366521e-03f, +3.110945653e-03f, +2.922667528e-02f, -4.185408685e-02f, -7.174125192e-02f, +2.648835910e-01f, +5.780318135e-01f, +3.221602930e-01f, -5.117389488e-02f, -5.619351824e-02f, +2.755457966e-02f, +7.032385926e-03f, -6.289189967e-03f, +9.939782505e-05f, +0.000000000e+00f,
+    /* 16, 2 */ +4.414886472e-04f, -5.113535158e-03f, +1.357910925e-03f, +2.932892142e-02f, -3.459618474e-02f, -7.936172697e-02f, +2.361522790e-01f, +5.739652427e-01f, +3.502436629e-01f, -3.818535953e-02f, -6.304412145e-02f, +2.592390265e-02f, +9.158035052e-03f, -6.542440674e-03f, -9.477253367e-05f, +0.000000000e+00f,
+    /* 16, 3 */ +4.855802427e-04f, -4.633347213e-03f, -2.351453324e-04f, +2.899108127e-02f, -2.742112952e-02f, -8.526380919e-02f, +2.076588264e-01f, +5.672296697e-01f, +3.776458156e-01f, -2.339704980e-02f, -6.951800781e-02f, +2.373330296e-02f, +1.135820669e-02f, -6.700410184e-03f, -3.323676319e-04f, +0.000000000e+00f,
+    /* 16, 4 */ +0.000000000e+00f, -4.132457962e-03f, -1.657230762e-03f, +2.825501306e-02f, -2.042447313e-02f, -8.951050933e-02f, +1.796184274e-01f, +5.578876325e-01f, +4.041347532e-01f, -6.836060229e-03f, -7.548664793e-02f, +2.096923455e-02f, +1.360131724e-02f, -6.747680557e-03f, -6.140535745e-04f, +0.000000000e+00f,
+    /* 16, 5 */ +0.000000000e+00f, -3.623457200e-03f, -2.901306411e-03f, +2.716546883e-02f, -1.369230379e-02f, -9.217934767e-02f, +1.522358833e-01f, +5.460256322e-01f, +4.294827240e-01f, +1.145038182e-02f, -8.081883229e-02f, +1.762637069e-02f, +1.585203938e-02f, -6.669417793e-03f, -9.394126333e-04f, +0.000000000e+00f,
+    /* 16, 6 */ +0.000000000e+00f, -3.117716971e-03f, -3.964078879e-03f, +2.576917487e-02f, -7.300675124e-03f, -9.336081470e-02f, +1.257035893e-01f, +5.317530991e-01f, +4.534687988e-01f, +3.139475616e-02f, -8.538226676e-02f, +1.370830904e-02f, +1.807162423e-02f, -6.451740247e-03f, -1.306826648e-03f, +0.000000000e+00f,
+    /* 16, 7 */ +0.000000000e+00f, -2.625277441e-03f, -4.845741482e-03f, +2.411394259e-02f, -1.315205805e-03f, -9.315672206e-02f, +1.001997139e-01f, +5.152010878e-01f, +4.758813956e-01f, +5.290934542e-02f, -8.904525833e-02f, +9.228181275e-03f, +2.021831098e-02f, -6.082100328e-03f, -1.713377737e-03f, +0.000000000e+00f,
+    /* 16, 8 */ +0.000000000e+00f, -2.154770219e-03f, -5.549672684e-03f, +2.224782226e-02f, +4.209152176e-03f, -9.167847004e-02f, +7.588659143e-02f, +4.965207199e-01f, +4.965207199e-01f, +7.588659143e-02f, -9.167847004e-02f, +4.209152176e-03f, +2.224782226e-02f, -5.549672684e-03f, -2.154770219e-03f, +0.000000000e+00f,
+    /* 16, 9 */ +0.000000000e+00f, -1.713377737e-03f, -6.082100328e-03f, +2.021831098e-02f, +9.228181275e-03f, -8.904525833e-02f, +5.290934542e-02f, +4.758813956e-01f, +5.152010878e-01f, +1.001997139e-01f, -9.315672206e-02f, -1.315205805e-03f, +2.411394259e-02f, -4.845741482e-03f, -2.625277441e-03f, +0.000000000e+00f,
+    /* 16,10 */ +0.000000000e+00f, -1.306826648e-03f, -6.451740247e-03f, +1.807162423e-02f, +1.370830904e-02f, -8.538226676e-02f, +3.139475616e-02f, +4.534687988e-01f, +5.317530991e-01f, +1.257035893e-01f, -9.336081470e-02f, -7.300675124e-03f, +2.576917487e-02f, -3.964078879e-03f, -3.117716971e-03f, +0.000000000e+00f,
+    /* 16,11 */ +0.000000000e+00f, -9.394126333e-04f, -6.669417793e-03f, +1.585203938e-02f, +1.762637069e-02f, -8.081883229e-02f, +1.145038182e-02f, +4.294827240e-01f, +5.460256322e-01f, +1.522358833e-01f, -9.217934767e-02f, -1.369230379e-02f, +2.716546883e-02f, -2.901306411e-03f, -3.623457200e-03f, +0.000000000e+00f,
+    /* 16,12 */ +0.000000000e+00f, -6.140535745e-04f, -6.747680557e-03f, +1.360131724e-02f, +2.096923455e-02f, -7.548664793e-02f, -6.836060229e-03f, +4.041347532e-01f, +5.578876325e-01f, +1.796184274e-01f, -8.951050933e-02f, -2.042447313e-02f, +2.825501306e-02f, -1.657230762e-03f, -4.132457962e-03f, +0.000000000e+00f,
+    /* 16,13 */ +0.000000000e+00f, -3.323676319e-04f, -6.700410184e-03f, +1.135820669e-02f, +2.373330296e-02f, -6.951800781e-02f, -2.339704980e-02f, +3.776458156e-01f, +5.672296697e-01f, +2.076588264e-01f, -8.526380919e-02f, -2.742112952e-02f, +2.899108127e-02f, -2.351453324e-04f, -4.633347213e-03f, +4.855802427e-04f,
+    /* 16,14 */ +0.000000000e+00f, -9.477253367e-05f, -6.542440674e-03f, +9.158035052e-03f, +2.592390265e-02f, -6.304412145e-02f, -3.818535953e-02f, +3.502436629e-01f, +5.739652427e-01f, +2.361522790e-01f, -7.936172697e-02f, -3.459618474e-02f, +2.932892142e-02f, +1.357910925e-03f, -5.113535158e-03f, +4.414886472e-04f,
+    /* 16,15 */ +0.000000000e+00f, +9.939782505e-05f, -6.289189967e-03f, +7.032385926e-03f, +2.755457966e-02f, -5.619351824e-02f, -5.117389488e-02f, +3.221602930e-01f, +5.780318135e-01f, +2.648835910e-01f, -7.174125192e-02f, -4.185408685e-02f, +2.922667528e-02f, +3.110945653e-03f, -5.559366521e-03f, +3.647589216e-04f,
+    /* 12, 0 */ -3.638165547e-03f, +2.979985982e-02f, -2.723323293e-02f, -8.605047059e-02f, +2.801520768e-01f, +6.188891100e-01f, +2.801520768e-01f, -8.605047059e-02f, -2.723323293e-02f, +2.979985982e-02f, -3.638165547e-03f, -3.041512814e-03f,
+    /* 12, 1 */ -4.749738186e-03f, +2.841159300e-02f, -1.933319589e-02f, -9.237133076e-02f, +2.473915856e-01f, +6.172320760e-01f, +3.129551421e-01f, -7.764805088e-02f, -3.538029377e-02f, +3.077779188e-02f, -2.305275216e-03f, -3.612081594e-03f,
+    /* 12, 2 */ -5.642312008e-03f, +2.667449885e-02f, -1.178831271e-02f, -9.669686191e-02f, +2.149632830e-01f, +6.122785731e-01f, +3.455026876e-01f, -6.709962705e-02f, -4.365285408e-02f, +3.128617370e-02f, -7.534120996e-04f, -4.192641091e-03f,
+    /* 12, 3 */ -6.322395719e-03f, +2.465107974e-02f, -4.692548813e-03f, -9.913294928e-02f, +1.831448749e-01f, +6.040811565e-01f, +3.774917553e-01f, -5.436442589e-02f, -5.191703591e-02f, +3.126943445e-02f, +1.010002855e-03f, -4.769140004e-03f,
+    /* 12, 4 */ -6.800166749e-03f, +2.240361196e-02f, +1.874795505e-03f, -9.980279721e-02f, +1.521989634e-01f, +5.927266195e-01f, +4.086182709e-01f, -3.942694703e-02f, -6.002791415e-02f, +3.067703358e-02f, +2.972136287e-03f, -5.325763732e-03f,
+    /* 12, 5 */ -7.088917510e-03f, +1.999301835e-02f, +7.849320649e-03f, -9.884443272e-02f, +1.223701278e-01f, +5.783348068e-01f, +4.385808709e-01f, -2.229824534e-02f, -6.783107929e-02f, +2.946485483e-02f, +5.114495497e-03f, -5.845139328e-03f,
+    /* 12, 6 */ -7.204483224e-03f, +1.747784955e-02f, +1.318150805e-02f, -9.640809295e-02f, +9.388232321e-02f, +5.610569814e-01f, +4.670847512e-01f, -3.016864363e-03f, -7.516444191e-02f, +2.759658236e-02f, +7.412783505e-03f, -6.308600966e-03f,
+    /* 12, 7 */ -7.164664930e-03f, +1.491338589e-02f, +1.783645872e-02f, -9.265354184e-02f, +6.693662660e-02f, +5.410737692e-01f, +4.938454794e-01f, +1.835060492e-02f, -8.186025948e-02f, +2.504503167e-02f, +9.836867291e-03f, -6.696514785e-03f,
+    /* 12, 8 */ -6.988660420e-03f, +1.235086875e-02f, +2.179340724e-02f, -8.774736114e-02f, +4.170935934e-02f, +5.185927134e-01f, +5.185927134e-01f, +4.170935934e-02f, -8.774736114e-02f, +2.179340724e-02f, +1.235086875e-02f, -6.988660420e-03f,
+    /* 12, 9 */ -6.696514785e-03f, +9.836867291e-03f, +2.504503167e-02f, -8.186025948e-02f, +1.835060492e-02f, +4.938454794e-01f, +5.410737692e-01f, +6.693662660e-02f, -9.265354184e-02f, +1.783645872e-02f, +1.491338589e-02f, -7.164664930e-03f,
+    /* 12,10 */ -6.308600966e-03f, +7.412783505e-03f, +2.759658236e-02f, -7.516444191e-02f, -3.016864363e-03f, +4.670847512e-01f, +5.610569814e-01f, +9.388232321e-02f, -9.640809295e-02f, +1.318150805e-02f, +1.747784955e-02f, -7.204483224e-03f,
+    /* 12,11 */ -5.845139328e-03f, +5.114495497e-03f, +2.946485483e-02f, -6.783107929e-02f, -2.229824534e-02f, +4.385808709e-01f, +5.783348068e-01f, +1.223701278e-01f, -9.884443272e-02f, +7.849320649e-03f, +1.999301835e-02f, -7.088917510e-03f,
+    /* 12,12 */ -5.325763732e-03f, +2.972136287e-03f, +3.067703358e-02f, -6.002791415e-02f, -3.942694703e-02f, +4.086182709e-01f, +5.927266195e-01f, +1.521989634e-01f, -9.980279721e-02f, +1.874795505e-03f, +2.240361196e-02f, -6.800166749e-03f,
+    /* 12,13 */ -4.769140004e-03f, +1.010002855e-03f, +3.126943445e-02f, -5.191703591e-02f, -5.436442589e-02f, +3.774917553e-01f, +6.040811565e-01f, +1.831448749e-01f, -9.913294928e-02f, -4.692548813e-03f, +2.465107974e-02f, -6.322395719e-03f,
+    /* 12,14 */ -4.192641091e-03f, -7.534120996e-04f, +3.128617370e-02f, -4.365285408e-02f, -6.709962705e-02f, +3.455026876e-01f, +6.122785731e-01f, +2.149632830e-01f, -9.669686191e-02f, -1.178831271e-02f, +2.667449885e-02f, -5.642312008e-03f,
+    /* 12,15 */ -3.612081594e-03f, -2.305275216e-03f, +3.077779188e-02f, -3.538029377e-02f, -7.764805088e-02f, +3.129551421e-01f, +6.172320760e-01f, +2.473915856e-01f, -9.237133076e-02f, -1.933319589e-02f, +2.841159300e-02f, -4.749738186e-03f,
+    /* 12, 0 */ -7.562702671e-03f, +2.362257603e-02f, -4.531854693e-03f, -1.030173373e-01f, +2.624467795e-01f, +6.583866631e-01f, +2.624467795e-01f, -1.030173373e-01f, -4.531854693e-03f, +2.362257603e-02f, -7.562702671e-03f, -3.516889901e-04f,
+    /* 12, 1 */ -7.668183010e-03f, +2.087771707e-02f, +2.839059860e-03f, -1.056218320e-01f, +2.257778124e-01f, +6.563919279e-01f, +2.995227467e-01f, -9.814415944e-02f, -1.254420342e-02f, +2.617709089e-02f, -7.250906804e-03f, -7.142430143e-04f,
+    /* 12, 2 */ -7.590423774e-03f, +1.801705410e-02f, +9.487879886e-03f, -1.061169074e-01f, +1.898705313e-01f, +6.504316937e-01f, +3.366347146e-01f, -9.086648783e-02f, -2.109702270e-02f, +2.846338313e-02f, -6.712315500e-03f, -1.140764971e-03f,
+    /* 12, 3 */ -7.354275530e-03f, +1.511050856e-02f, +1.535418598e-02f, -1.046816488e-01f, +1.550586973e-01f, +6.405775033e-01f, +3.734003416e-01f, -8.107535131e-02f, -3.006937567e-02f, +3.040170317e-02f, -5.929880498e-03f, -1.628307909e-03f,
+    /* 12, 4 */ -6.985575046e-03f, +1.222230420e-02f, +2.039736787e-02f, -1.015111601e-01f, +1.216509697e-01f, +6.269473635e-01f, +4.094311989e-01f, -6.869168809e-02f, -3.932109040e-02f, +3.191206547e-02f, -4.890814148e-03f, -2.171433859e-03f,
+    /* 12, 5 */ -6.510406524e-03f, +9.410098002e-03f, +2.459585213e-02f, -9.681266365e-02f, +8.992722338e-02f, +6.097039216e-01f, +4.443382217e-01f, -5.366903171e-02f, -4.869391429e-02f, +3.291602689e-02f, -3.587389454e-03f, -2.762058981e-03f,
+    /* 12, 6 */ -5.954426605e-03f, +6.724324555e-03f, +2.794602403e-02f, -9.080157573e-02f, +6.013541337e-02f, +5.890519583e-01f, +4.777372670e-01f, -3.599575069e-02f, -5.801308453e-02f, +3.333857894e-02f, -2.017687876e-03f, -3.389362400e-03f,
+    /* 12, 7 */ -5.342264546e-03f, +4.207752570e-03f, +3.046088231e-02f, -8.369763050e-02f, +3.248902822e-02f, +5.652352421e-01f, +5.092546840e-01f, -1.569678608e-02f, -6.708930595e-02f, +3.311011989e-02f, -1.862710466e-04f, -4.039767889e-03f,
+    /* 12, 8 */ -4.697006242e-03f, +1.895247343e-03f, +3.216846911e-02f, -7.572111885e-02f, +7.165160645e-03f, +5.385328020e-01f, +5.385328020e-01f, +7.165160645e-03f, -7.572111885e-02f, +3.216846911e-02f, +1.895247343e-03f, -4.697006242e-03f,
+    /* 12, 9 */ -4.039767889e-03f, -1.862710466e-04f, +3.311011989e-02f, -6.708930595e-02f, -1.569678608e-02f, +5.092546840e-01f, +5.652352421e-01f, +3.248902822e-02f, -8.369763050e-02f, +3.046088231e-02f, +4.207752570e-03f, -5.342264546e-03f,
+    /* 12,10 */ -3.389362400e-03f, -2.017687876e-03f, +3.333857894e-02f, -5.801308453e-02f, -3.599575069e-02f, +4.777372670e-01f, +5.890519583e-01f, +6.013541337e-02f, -9.080157573e-02f, +2.794602403e-02f, +6.724324555e-03f, -5.954426605e-03f,
+    /* 12,11 */ -2.762058981e-03f, -3.587389454e-03f, +3.291602689e-02f, -4.869391429e-02f, -5.366903171e-02f, +4.443382217e-01f, +6.097039216e-01f, +8.992722338e-02f, -9.681266365e-02f, +2.459585213e-02f, +9.410098002e-03f, -6.510406524e-03f,
+    /* 12,12 */ -2.171433859e-03f, -4.890814148e-03f, +3.191206547e-02f, -3.932109040e-02f, -6.869168809e-02f, +4.094311989e-01f, +6.269473635e-01f, +1.216509697e-01f, -1.015111601e-01f, +2.039736787e-02f, +1.222230420e-02f, -6.985575046e-03f,
+    /* 12,13 */ -1.628307909e-03f, -5.929880498e-03f, +3.040170317e-02f, -3.006937567e-02f, -8.107535131e-02f, +3.734003416e-01f, +6.405775033e-01f, +1.550586973e-01f, -1.046816488e-01f, +1.535418598e-02f, +1.511050856e-02f, -7.354275530e-03f,
+    /* 12,14 */ -1.140764971e-03f, -6.712315500e-03f, +2.846338313e-02f, -2.109702270e-02f, -9.086648783e-02f, +3.366347146e-01f, +6.504316937e-01f, +1.898705313e-01f, -1.061169074e-01f, +9.487879886e-03f, +1.801705410e-02f, -7.590423774e-03f,
+    /* 12,15 */ -7.142430143e-04f, -7.250906804e-03f, +2.617709089e-02f, -1.254420342e-02f, -9.814415944e-02f, +2.995227467e-01f, +6.563919279e-01f, +2.257778124e-01f, -1.056218320e-01f, +2.839059860e-03f, +2.087771707e-02f, -7.668183010e-03f,
+    /* 12, 0 */ -7.009786996e-03f, +1.344312953e-02f, +1.557210222e-02f, -1.125190619e-01f, +2.408695221e-01f, +6.978842163e-01f, +2.408695221e-01f, -1.125190619e-01f, +1.557210222e-02f, +1.344312953e-02f, -7.009786996e-03f, +6.003640016e-04f,
+    /* 12, 1 */ -6.398742119e-03f, +1.026913982e-02f, +2.132332546e-02f, -1.110115061e-01f, +2.005160832e-01f, +6.955088069e-01f, +2.821133540e-01f, -1.117099281e-01f, +8.845385329e-03f, +1.669708199e-02f, -7.518519523e-03f, +5.590854556e-04f,
+    /* 12, 2 */ -5.716737920e-03f, +7.240001966e-03f, +2.606967700e-02f, -1.074325911e-01f, +1.614746033e-01f, +6.884146462e-01f, +3.237982615e-01f, -1.083606220e-01f, +1.198165974e-03f, +1.995657080e-02f, -7.892366537e-03f, +4.637249154e-04f,
+    /* 12, 3 */ -4.993154633e-03f, +4.410399512e-03f, +2.980590100e-02f, -1.020439692e-01f, +1.241329667e-01f, +6.766973789e-01f, +3.654537522e-01f, -1.022748781e-01f, -7.287927063e-03f, +2.313861998e-02f, -8.098411048e-03f, +3.063703263e-04f,
+    /* 12, 4 */ -4.254780730e-03f, +1.824453005e-03f, +3.254896791e-02f, -9.511805181e-02f, +8.884019172e-02f, +6.605145641e-01f, +4.065952670e-01f, -9.328874484e-02f, -1.650412301e-02f, +2.615301189e-02f, -8.104377377e-03f, +8.035370630e-05f,
+    /* 12, 5 */ -3.525333045e-03f, -4.843426812e-04f, +3.433584064e-02f, -8.693252112e-02f, +5.590207404e-02f, +6.400829406e-01f, +4.467316931e-01f, -8.127529114e-02f, -2.631456917e-02f, +2.890390773e-02f, -7.879705669e-03f, -2.193022854e-04f,
+    /* 12, 6 */ -2.825116890e-03f, -2.492908449e-03f, +3.522097401e-02f, -7.776502604e-02f, +2.557772128e-02f, +6.156746770e-01f, +4.853731430e-01f, -6.614880956e-02f, -3.655698883e-02f, +3.129176395e-02f, -7.396691856e-03f, -5.954441701e-04f,
+    /* 12, 7 */ -2.170822930e-03f, -4.188126176e-03f, +3.527362061e-02f, -6.788815999e-02f, -1.922977207e-03f, +5.876126808e-01f, +5.220388545e-01f, -4.786841211e-02f, -4.704395826e-02f, +3.321551909e-02f, -6.631665064e-03f, -1.048370326e-03f,
+    /* 12, 8 */ -1.575453811e-03f, -5.566170902e-03f, +3.457501660e-02f, -5.756481055e-02f, -2.644092188e-02f, +5.562650609e-01f, +5.562650609e-01f, -2.644092188e-02f, -5.756481055e-02f, +3.457501660e-02f, -5.566170902e-03f, -1.575453811e-03f,
+    /* 12, 9 */ -1.048370326e-03f, -6.631665064e-03f, +3.321551909e-02f, -4.704395826e-02f, -4.786841211e-02f, +5.220388545e-01f, +5.876126808e-01f, -1.922977207e-03f, -6.788815999e-02f, +3.527362061e-02f, -4.188126176e-03f, -2.170822930e-03f,
+    /* 12,10 */ -5.954441701e-04f, -7.396691856e-03f, +3.129176395e-02f, -3.655698883e-02f, -6.614880956e-02f, +4.853731430e-01f, +6.156746770e-01f, +2.557772128e-02f, -7.776502604e-02f, +3.522097401e-02f, -2.492908449e-03f, -2.825116890e-03f,
+    /* 12,11 */ -2.193022854e-04f, -7.879705669e-03f, +2.890390773e-02f, -2.631456917e-02f, -8.127529114e-02f, +4.467316931e-01f, +6.400829406e-01f, +5.590207404e-02f, -8.693252112e-02f, +3.433584064e-02f, -4.843426812e-04f, -3.525333045e-03f,
+    /* 12,12 */ +8.035370630e-05f, -8.104377377e-03f, +2.615301189e-02f, -1.650412301e-02f, -9.328874484e-02f, +4.065952670e-01f, +6.605145641e-01f, +8.884019172e-02f, -9.511805181e-02f, +3.254896791e-02f, +1.824453005e-03f, -4.254780730e-03f,
+    /* 12,13 */ +3.063703263e-04f, -8.098411048e-03f, +2.313861998e-02f, -7.287927063e-03f, -1.022748781e-01f, +3.654537522e-01f, +6.766973789e-01f, +1.241329667e-01f, -1.020439692e-01f, +2.980590100e-02f, +4.410399512e-03f, -4.993154633e-03f,
+    /* 12,14 */ +4.637249154e-04f, -7.892366537e-03f, +1.995657080e-02f, +1.198165974e-03f, -1.083606220e-01f, +3.237982615e-01f, +6.884146462e-01f, +1.614746033e-01f, -1.074325911e-01f, +2.606967700e-02f, +7.240001966e-03f, -5.716737920e-03f,
+    /* 12,15 */ +5.590854556e-04f, -7.518519523e-03f, +1.669708199e-02f, +8.845385329e-03f, -1.117099281e-01f, +2.821133540e-01f, +6.955088069e-01f, +2.005160832e-01f, -1.110115061e-01f, +2.132332546e-02f, +1.026913982e-02f, -6.398742119e-03f,
+
+    /* 24, 0 */ +1.501390780e-03f, +3.431804419e-03f, +6.512803185e-03f, +1.091425387e-02f, +1.664594540e-02f, +2.351091132e-02f, +3.109255671e-02f, +3.878419288e-02f, +4.586050701e-02f, +5.158058002e-02f, +5.530384985e-02f, +5.659614054e-02f, +5.530384985e-02f, +5.158058002e-02f, +4.586050701e-02f, +3.878419288e-02f, +3.109255671e-02f, +2.351091132e-02f, +1.664594540e-02f, +1.091425387e-02f, +6.512803185e-03f, +3.431804419e-03f, +1.501390780e-03f, +4.573885647e-04f,
+    /* 24, 1 */ +1.413186400e-03f, +3.279858311e-03f, +6.282638036e-03f, +1.059932179e-02f, +1.625135142e-02f, +2.305547031e-02f, +3.060840342e-02f, +3.831365198e-02f, +4.545054680e-02f, +5.127577001e-02f, +5.513916011e-02f, +5.659104154e-02f, +5.545895049e-02f, +5.187752167e-02f, +4.626513642e-02f, +3.925233583e-02f, +3.157717954e-02f, +2.396921539e-02f, +1.704503934e-02f, +1.123445076e-02f, +6.748179094e-03f, +3.588275667e-03f, +1.593065611e-03f, +5.022154476e-04f,
+    /* 24, 2 */ +1.328380648e-03f, +3.132379333e-03f, +6.057656813e-03f, +1.028967374e-02f, +1.586133102e-02f, +2.260301890e-02f, +3.012488684e-02f, +3.784089895e-02f, +4.503543229e-02f, +5.096323022e-02f, +5.496495842e-02f, +5.657574693e-02f, +5.560438923e-02f, +5.216645963e-02f, +4.666426010e-02f, +3.971789474e-02f, +3.206210284e-02f, +2.443025293e-02f, +1.744855617e-02f, +1.155988996e-02f, +6.988790100e-03f, +3.749328623e-03f, +1.688282347e-03f, +5.494305796e-04f,
+    /* 24, 3 */ +1.246901403e-03f, +2.989308098e-03f, +5.837830254e-03f, +9.985325752e-03f, +1.547595434e-02f, +2.215368059e-02f, +2.964217216e-02f, +3.736611920e-02f, +4.461534144e-02f, +5.064310236e-02f, +5.478132634e-02f, +5.655026396e-02f, +5.574009777e-02f, +5.244726189e-02f, +4.705770477e-02f, +4.018068337e-02f, +3.254715574e-02f, +2.489389144e-02f, +1.785641537e-02f, +1.189054572e-02f, +7.234657995e-03f, +3.915018340e-03f, +1.787112015e-03f, +5.991047395e-04f,
+    /* 24, 4 */ +1.168676301e-03f, +2.850583915e-03f, +5.623126723e-03f, +9.686290690e-03f, +1.509528803e-02f, +2.170757578e-02f, +2.916042250e-02f, +3.688949768e-02f, +4.419045351e-02f, +5.031553118e-02f, +5.458834968e-02f, +5.651460469e-02f, +5.586601230e-02f, +5.271979985e-02f, +4.744529894e-02f, +4.064051541e-02f, +3.303216567e-02f, +2.535999546e-02f, +1.826853297e-02f, +1.222638897e-02f, +7.485801959e-03f, +4.085398290e-03f, +1.889625146e-03f, +6.513091287e-04f,
+    /* 24, 5 */ +1.093632798e-03f, +2.716144855e-03f, +5.413512274e-03f, +9.392578266e-03f, +1.471939531e-02f, +2.126482169e-02f, +2.867979883e-02f, +3.641121873e-02f, +4.376094899e-02f, +4.998066438e-02f, +5.438611851e-02f, +5.646878599e-02f, +5.598207354e-02f, +5.298394839e-02f, +4.782687301e-02f, +4.109720465e-02f, +3.351695842e-02f, +2.582842673e-02f, +1.868482156e-02f, +1.256738733e-02f, +7.742238512e-03f, +4.260520294e-03f, +1.995891717e-03f, +7.061153220e-04f,
+    /* 24, 6 */ +1.021698233e-03f, +2.585927824e-03f, +5.208950715e-03f, +9.104195104e-03f, +1.434833590e-02f, +2.082553239e-02f, +2.820045990e-02f, +3.593146595e-02f, +4.332700946e-02f, +4.963865252e-02f, +5.417472708e-02f, +5.641282954e-02f, +5.608822683e-02f, +5.323958602e-02f, +4.820225940e-02f, +4.155056502e-02f, +3.400135826e-02f, +2.629904416e-02f, +1.910519032e-02f, +1.291350505e-02f, +8.003981455e-03f, +4.440434453e-03f, +2.105981077e-03f, +7.635952183e-04f,
+    /* 24, 7 */ +9.527998831e-04f, +2.459868628e-03f, +5.009403670e-03f, +8.821144768e-03f, +1.398216608e-02f, +2.038981869e-02f, +2.772256216e-02f, +3.545042216e-02f, +4.288881749e-02f, +4.928964888e-02f, +5.395427373e-02f, +5.634676181e-02f, +5.618442211e-02f, +5.348659488e-02f, +4.857129262e-02f, +4.200041076e-02f, +3.448518802e-02f, +2.677170395e-02f, +1.952954505e-02f, +1.326470299e-02f, +8.271041819e-03f, +4.625189083e-03f, +2.219961884e-03f, +8.238209888e-04f,
+    /* 24, 8 */ +8.868650246e-04f, +2.337902042e-03f, +4.814830642e-03f, +8.543427812e-03f, +1.362093865e-02f, +1.995778816e-02f, +2.724625964e-02f, +3.496826923e-02f, +4.244655653e-02f, +4.893380942e-02f, +5.372486088e-02f, +5.627061400e-02f, +5.627061400e-02f, +5.372486088e-02f, +4.893380942e-02f, +4.244655653e-02f, +3.496826923e-02f, +2.724625964e-02f, +1.995778816e-02f, +1.362093865e-02f, +8.543427812e-03f, +4.814830642e-03f, +2.337902042e-03f, +8.868650246e-04f,
+    /* 24, 9 */ +8.238209888e-04f, +2.219961884e-03f, +4.625189083e-03f, +8.271041819e-03f, +1.326470299e-02f, +1.952954505e-02f, +2.677170395e-02f, +3.448518802e-02f, +4.200041076e-02f, +4.857129262e-02f, +5.348659488e-02f, +5.618442211e-02f, +5.634676181e-02f, +5.395427373e-02f, +4.928964888e-02f, +4.288881749e-02f, +3.545042216e-02f, +2.772256216e-02f, +2.038981869e-02f, +1.398216608e-02f, +8.821144768e-03f, +5.009403670e-03f, +2.459868628e-03f, +9.527998831e-04f,
+    /* 24,10 */ +7.635952183e-04f, +2.105981077e-03f, +4.440434453e-03f, +8.003981455e-03f, +1.291350505e-02f, +1.910519032e-02f, +2.629904416e-02f, +3.400135826e-02f, +4.155056502e-02f, +4.820225940e-02f, +5.323958602e-02f, +5.608822683e-02f, +5.641282954e-02f, +5.417472708e-02f, +4.963865252e-02f, +4.332700946e-02f, +3.593146595e-02f, +2.820045990e-02f, +2.082553239e-02f, +1.434833590e-02f, +9.104195104e-03f, +5.208950715e-03f, +2.585927824e-03f, +1.021698233e-03f,
+    /* 24,11 */ +7.061153220e-04f, +1.995891717e-03f, +4.260520294e-03f, +7.742238512e-03f, +1.256738733e-02f, +1.868482156e-02f, +2.582842673e-02f, +3.351695842e-02f, +4.109720465e-02f, +4.782687301e-02f, +5.298394839e-02f, +5.598207354e-02f, +5.646878599e-02f, +5.438611851e-02f, +4.998066438e-02f, +4.376094899e-02f, +3.641121873e-02f, +2.867979883e-02f, +2.126482169e-02f, +1.471939531e-02f, +9.392578266e-03f, +5.413512274e-03f, +2.716144855e-03f, +1.093632798e-03f,
+    /* 24,12 */ +6.513091287e-04f, +1.889625146e-03f, +4.085398290e-03f, +7.485801959e-03f, +1.222638897e-02f, +1.826853297e-02f, +2.535999546e-02f, +3.303216567e-02f, +4.064051541e-02f, +4.744529894e-02f, +5.271979985e-02f, +5.586601230e-02f, +5.651460469e-02f, +5.458834968e-02f, +5.031553118e-02f, +4.419045351e-02f, +3.688949768e-02f, +2.916042250e-02f, +2.170757578e-02f, +1.509528803e-02f, +9.686290690e-03f, +5.623126723e-03f, +2.850583915e-03f, +1.168676301e-03f,
+    /* 24,13 */ +5.991047395e-04f, +1.787112015e-03f, +3.915018340e-03f, +7.234657995e-03f, +1.189054572e-02f, +1.785641537e-02f, +2.489389144e-02f, +3.254715574e-02f, +4.018068337e-02f, +4.705770477e-02f, +5.244726189e-02f, +5.574009777e-02f, +5.655026396e-02f, +5.478132634e-02f, +5.064310236e-02f, +4.461534144e-02f, +3.736611920e-02f, +2.964217216e-02f, +2.215368059e-02f, +1.547595434e-02f, +9.985325752e-03f, +5.837830254e-03f, +2.989308098e-03f, +1.246901403e-03f,
+    /* 24,14 */ +5.494305796e-04f, +1.688282347e-03f, +3.749328623e-03f, +6.988790100e-03f, +1.155988996e-02f, +1.744855617e-02f, +2.443025293e-02f, +3.206210284e-02f, +3.971789474e-02f, +4.666426010e-02f, +5.216645963e-02f, +5.560438923e-02f, +5.657574693e-02f, +5.496495842e-02f, +5.096323022e-02f, +4.503543229e-02f, +3.784089895e-02f, +3.012488684e-02f, +2.260301890e-02f, +1.586133102e-02f, +1.028967374e-02f, +6.057656813e-03f, +3.132379333e-03f, +1.328380648e-03f,
+    /* 24,15 */ +5.022154476e-04f, +1.593065611e-03f, +3.588275667e-03f, +6.748179094e-03f, +1.123445076e-02f, +1.704503934e-02f, +2.396921539e-02f, +3.157717954e-02f, +3.925233583e-02f, +4.626513642e-02f, +5.187752167e-02f, +5.545895049e-02f, +5.659104154e-02f, +5.513916011e-02f, +5.127577001e-02f, +4.545054680e-02f, +3.831365198e-02f, +3.060840342e-02f, +2.305547031e-02f, +1.625135142e-02f, +1.059932179e-02f, +6.282638036e-03f, +3.279858311e-03f, +1.413186400e-03f,
+    /* 24, 0 */ -2.629184871e-03f, -4.843950453e-03f, -6.895985300e-03f, -7.687208098e-03f, -5.978262553e-03f, -8.032174656e-04f, +8.095316761e-03f, +1.997958831e-02f, +3.311864145e-02f, +4.512644231e-02f, +5.356009950e-02f, +5.659614054e-02f, +5.356009950e-02f, +4.512644231e-02f, +3.311864145e-02f, +1.997958831e-02f, +8.095316761e-03f, -8.032174656e-04f, -5.978262553e-03f, -7.687208098e-03f, -6.895985300e-03f, -4.843950453e-03f, -2.629184871e-03f, -9.454953712e-04f,
+    /* 24, 1 */ -2.503767166e-03f, -4.700731697e-03f, -6.791825424e-03f, -7.698565601e-03f, -6.179328945e-03f, -1.237726578e-03f, +7.438688744e-03f, +1.917778123e-02f, +3.230413198e-02f, +4.445707943e-02f, +5.317715832e-02f, +5.658405316e-02f, +5.392156860e-02f, +4.578163621e-02f, +3.392875410e-02f, +2.078652132e-02f, +8.763945305e-03f, -3.538276542e-04f, -5.763420347e-03f, -7.665996832e-03f, -6.995273095e-03f, -4.986674025e-03f, -2.756835384e-03f, -1.028686673e-03f,
+    /* 24, 2 */ -2.380688695e-03f, -4.557243028e-03f, -6.683099486e-03f, -7.700368745e-03f, -6.366798820e-03f, -1.657314491e-03f, +6.794365087e-03f, +1.838162773e-02f, +3.148585651e-02f, +4.377411309e-02f, +5.277308334e-02f, +5.654780182e-02f, +5.426124576e-02f, +4.642210542e-02f, +3.473383802e-02f, +2.159804191e-02f, +9.444254477e-03f, +1.103863968e-04f, -5.534634231e-03f, -7.634636496e-03f, -7.089380216e-03f, -5.128670417e-03f, -2.886604737e-03f, -1.114962551e-03f,
+    /* 24, 3 */ -2.260048394e-03f, -4.413702845e-03f, -6.570110572e-03f, -7.692920583e-03f, -6.540862270e-03f, -2.061956485e-03f, +6.162633403e-03f, +1.759164425e-02f, +3.066444409e-02f, +4.307811806e-02f, +5.234823086e-02f, +5.648741902e-02f, +5.457882991e-02f, +4.704730472e-02f, +3.553326091e-02f, +2.241360152e-02f, +1.013590849e-02f, +5.893521078e-04f, -5.291747706e-03f, -7.592836347e-03f, -7.177995846e-03f, -5.269701073e-03f, -3.018371382e-03f, -1.204312280e-03f,
+    /* 24, 4 */ -2.141937776e-03f, -4.270322542e-03f, -6.453158507e-03f, -7.676527355e-03f, -6.701719772e-03f, -2.451643421e-03f, +5.543764951e-03f, +1.680833562e-02f, +2.984052134e-02f, +4.236967758e-02f, +5.190297478e-02f, +5.640295884e-02f, +5.487403917e-02f, +4.765670002e-02f, +3.632639074e-02f, +2.323264190e-02f, +1.083855586e-02f, +1.082980638e-03f, -5.034616251e-03f, -7.540310660e-03f, -7.260807322e-03f, -5.409521052e-03f, -3.152006158e-03f, -1.296719170e-03f,
+    /* 24, 5 */ -2.026441000e-03f, -4.127306381e-03f, -6.332539518e-03f, -7.651498041e-03f, -6.849581767e-03f, -2.826381528e-03f, +4.938014526e-03f, +1.603219452e-02f, +2.901471178e-02f, +4.164938279e-02f, +5.143770614e-02f, +5.629449693e-02f, +5.514661113e-02f, +4.824976895e-02f, +3.711259647e-02f, +2.405459566e-02f, +1.155182965e-02f, +1.591166761e-03f, -4.763107701e-03f, -7.476779193e-03f, -7.337500507e-03f, -5.547879217e-03f, -3.287372274e-03f, -1.392160404e-03f,
+    /* 24, 6 */ -1.913634953e-03f, -3.984851387e-03f, -6.208545927e-03f, -7.618143912e-03f, -6.984668233e-03f, -3.186192169e-03f, +4.345620369e-03f, +1.526370115e-02f, +2.818763519e-02f, +4.091783200e-02f, +5.095283267e-02f, +5.616213037e-02f, +5.539630322e-02f, +4.882600150e-02f, +3.789124869e-02f, +2.487888677e-02f, +1.227534767e-02f, +2.113788767e-03f, -4.477102606e-03f, -7.401967644e-03f, -7.407760182e-03f, -5.684518438e-03f, -3.424325302e-03f, -1.490606880e-03f,
+    /* 24, 7 */ -1.803589350e-03f, -3.843147252e-03f, -6.081465840e-03f, -7.576778087e-03f, -7.107208249e-03f, -3.531111592e-03f, +3.766804102e-03f, +1.450332275e-02f, +2.735990694e-02f, +4.017563005e-02f, +5.044877831e-02f, +5.600597761e-02f, +5.562289296e-02f, +4.938490059e-02f, +3.866172039e-02f, +2.570493119e-02f, +1.300871280e-02f, +2.650708377e-03f, -4.176494585e-03f, -7.315608112e-03f, -7.471270440e-03f, -5.819175805e-03f, -3.562713186e-03f, -1.592023060e-03f,
+    /* 24, 8 */ -1.696366827e-03f, -3.702376254e-03f, -5.951582861e-03f, -7.527715094e-03f, -7.217439556e-03f, -3.861190662e-03f, +3.201770681e-03f, +1.375151322e-02f, +2.653213738e-02f, +3.942338759e-02f, +4.992598268e-02f, +5.582617825e-02f, +5.582617825e-02f, +4.992598268e-02f, +3.942338759e-02f, +2.653213738e-02f, +1.375151322e-02f, +3.201770681e-03f, -3.861190662e-03f, -7.217439556e-03f, -7.527715094e-03f, -5.951582861e-03f, -3.702376254e-03f, -1.696366827e-03f,
+    /* 24, 9 */ -1.592023060e-03f, -3.562713186e-03f, -5.819175805e-03f, -7.471270440e-03f, -7.315608112e-03f, -4.176494585e-03f, +2.650708377e-03f, +1.300871280e-02f, +2.570493119e-02f, +3.866172039e-02f, +4.938490059e-02f, +5.562289296e-02f, +5.600597761e-02f, +5.044877831e-02f, +4.017563005e-02f, +2.735990694e-02f, +1.450332275e-02f, +3.766804102e-03f, -3.531111592e-03f, -7.107208249e-03f, -7.576778087e-03f, -6.081465840e-03f, -3.843147252e-03f, -1.803589350e-03f,
+    /* 24,10 */ -1.490606880e-03f, -3.424325302e-03f, -5.684518438e-03f, -7.407760182e-03f, -7.401967644e-03f, -4.477102606e-03f, +2.113788767e-03f, +1.227534767e-02f, +2.487888677e-02f, +3.789124869e-02f, +4.882600150e-02f, +5.539630322e-02f, +5.616213037e-02f, +5.095283267e-02f, +4.091783200e-02f, +2.818763519e-02f, +1.526370115e-02f, +4.345620369e-03f, -3.186192169e-03f, -6.984668233e-03f, -7.618143912e-03f, -6.208545927e-03f, -3.984851387e-03f, -1.913634953e-03f,
+    /* 24,11 */ -1.392160404e-03f, -3.287372274e-03f, -5.547879217e-03f, -7.337500507e-03f, -7.476779193e-03f, -4.763107701e-03f, +1.591166761e-03f, +1.155182965e-02f, +2.405459566e-02f, +3.711259647e-02f, +4.824976895e-02f, +5.514661113e-02f, +5.629449693e-02f, +5.143770614e-02f, +4.164938279e-02f, +2.901471178e-02f, +1.603219452e-02f, +4.938014526e-03f, -2.826381528e-03f, -6.849581767e-03f, -7.651498041e-03f, -6.332539518e-03f, -4.127306381e-03f, -2.026441000e-03f,
+    /* 24,12 */ -1.296719170e-03f, -3.152006158e-03f, -5.409521052e-03f, -7.260807322e-03f, -7.540310660e-03f, -5.034616251e-03f, +1.082980638e-03f, +1.083855586e-02f, +2.323264190e-02f, +3.632639074e-02f, +4.765670002e-02f, +5.487403917e-02f, +5.640295884e-02f, +5.190297478e-02f, +4.236967758e-02f, +2.984052134e-02f, +1.680833562e-02f, +5.543764951e-03f, -2.451643421e-03f, -6.701719772e-03f, -7.676527355e-03f, -6.453158507e-03f, -4.270322542e-03f, -2.141937776e-03f,
+    /* 24,13 */ -1.204312280e-03f, -3.018371382e-03f, -5.269701073e-03f, -7.177995846e-03f, -7.592836347e-03f, -5.291747706e-03f, +5.893521078e-04f, +1.013590849e-02f, +2.241360152e-02f, +3.553326091e-02f, +4.704730472e-02f, +5.457882991e-02f, +5.648741902e-02f, +5.234823086e-02f, +4.307811806e-02f, +3.066444409e-02f, +1.759164425e-02f, +6.162633403e-03f, -2.061956485e-03f, -6.540862270e-03f, -7.692920583e-03f, -6.570110572e-03f, -4.413702845e-03f, -2.260048394e-03f,
+    /* 24,14 */ -1.114962551e-03f, -2.886604737e-03f, -5.128670417e-03f, -7.089380216e-03f, -7.634636496e-03f, -5.534634231e-03f, +1.103863968e-04f, +9.444254477e-03f, +2.159804191e-02f, +3.473383802e-02f, +4.642210542e-02f, +5.426124576e-02f, +5.654780182e-02f, +5.277308334e-02f, +4.377411309e-02f, +3.148585651e-02f, +1.838162773e-02f, +6.794365087e-03f, -1.657314491e-03f, -6.366798820e-03f, -7.700368745e-03f, -6.683099486e-03f, -4.557243028e-03f, -2.380688695e-03f,
+    /* 24,15 */ -1.028686673e-03f, -2.756835384e-03f, -4.986674025e-03f, -6.995273095e-03f, -7.665996832e-03f, -5.763420347e-03f, -3.538276542e-04f, +8.763945305e-03f, +2.078652132e-02f, +3.392875410e-02f, +4.578163621e-02f, +5.392156860e-02f, +5.658405316e-02f, +5.317715832e-02f, +4.445707943e-02f, +3.230413198e-02f, +1.917778123e-02f, +7.438688744e-03f, -1.237726578e-03f, -6.179328945e-03f, -7.698565601e-03f, -6.791825424e-03f, -4.700731697e-03f, -2.503767166e-03f,
+    /* 24, 0 */ +4.735641749e-04f, -1.438577362e-03f, -6.107076473e-03f, -1.318715065e-02f, -2.047716119e-02f, -2.428668798e-02f, -2.088952800e-02f, -8.512165320e-03f, +1.117510535e-02f, +3.302575560e-02f, +5.012757987e-02f, +5.659614054e-02f, +5.012757987e-02f, +3.302575560e-02f, +1.117510535e-02f, -8.512165320e-03f, -2.088952800e-02f, -2.428668798e-02f, -2.047716119e-02f, -1.318715065e-02f, -6.107076473e-03f, -1.438577362e-03f, +4.735641749e-04f, +5.516063288e-04f,
+    /* 24, 1 */ +5.190146993e-04f, -1.243445781e-03f, -5.732182665e-03f, -1.270621714e-02f, -2.008108462e-02f, -2.422674988e-02f, -2.136190754e-02f, -9.536491055e-03f, +9.813857768e-03f, +3.172645287e-02f, +4.932296812e-02f, +5.657007725e-02f, +5.088942272e-02f, +3.430616434e-02f, +1.254542812e-02f, -7.458075491e-03f, -2.038088472e-02f, -2.431781992e-02f, -2.085968072e-02f, -1.366943909e-02f, -6.492037400e-03f, -1.644903025e-03f, +4.208638005e-04f, +5.761542752e-04f,
+    /* 24, 2 */ +5.575380238e-04f, -1.059370463e-03f, -5.367638258e-03f, -1.222740313e-02f, -1.967247249e-02f, -2.413881461e-02f, -2.179812108e-02f, -1.053019587e-02f, +8.463295193e-03f, +3.041001010e-02f, +4.847674006e-02f, +5.649192540e-02f, +5.160740292e-02f, +3.556594146e-02f, +1.392318625e-02f, -6.375136316e-03f, -1.983593655e-02f, -2.431936776e-02f, -2.122761958e-02f, -1.415229209e-02f, -6.886752185e-03f, -1.862540304e-03f, +3.605947820e-04f, +5.982066157e-04f,
+    /* 24, 3 */ +5.894596941e-04f, -8.861942853e-04f, -5.013694839e-03f, -1.175144649e-02f, -1.925234223e-02f, -2.402372023e-02f, -2.219832191e-02f, -1.149248169e-02f, +7.124994951e-03f, +2.907819451e-02f, +4.759010507e-02f, +5.636179897e-02f, +5.228048761e-02f, +3.680336864e-02f, +1.530671242e-02f, -5.264319552e-03f, -1.925469982e-02f, -2.429058667e-02f, -2.157995394e-02f, -1.463489443e-02f, -7.290876362e-03f, -2.091585491e-03f, +2.924431607e-04f, +6.174753085e-04f,
+    /* 24, 4 */ +6.151072142e-04f, -7.237418200e-04f, -4.670573645e-03f, -1.127905759e-02f, -1.882170532e-02f, -2.388233174e-02f, -2.256271775e-02f, -1.242260980e-02f, +5.800499486e-03f, +2.773278351e-02f, +4.666432713e-02f, +5.617988770e-02f, +5.290770668e-02f, +3.801674993e-02f, +1.669431448e-02f, -4.126652928e-03f, -1.863724919e-02f, -2.423076690e-02f, -2.191566174e-02f, -1.511640714e-02f, -7.704034115e-03f, -2.332112699e-03f, +2.161008111e-04f, +6.336652968e-04f,
+    /* 24, 5 */ +6.348091316e-04f, -5.718203517e-04f, -4.338465973e-03f, -1.081091853e-02f, -1.838156554e-02f, -2.371553901e-02f, -2.289156957e-02f, -1.331990148e-02f, +4.491314051e-03f, +2.637556170e-02f, +4.570072248e-02f, +5.594645674e-02f, +5.348815454e-02f, +3.920441468e-02f, +1.808427811e-02f, -2.963218986e-03f, -1.798371833e-02f, -2.413923574e-02f, -2.223372473e-02f, -1.559596853e-02f, -8.125818185e-03f, -2.584172964e-03f, +1.312664533e-04f, +6.464750685e-04f,
+    /* 24, 6 */ +6.488941487e-04f, -4.302209069e-04f, -4.017533648e-03f, -1.034768250e-02f, -1.793291714e-02f, -2.352425464e-02f, -2.318519030e-02f, -1.418373842e-02f, +3.198904487e-03f, +2.500831779e-02f, +4.470065727e-02f, +5.566184614e-02f, +5.402099181e-02f, +4.036472049e-02f, +1.947486957e-02f, -1.775153809e-03f, -1.729430055e-02f, -2.401535937e-02f, -2.253313051e-02f, -1.607269547e-02f, -8.555789856e-03f, -2.847793367e-03f, +3.764667972e-05f, +6.555972530e-04f,
+    /* 24, 7 */ +6.576902611e-04f, -2.987192943e-04f, -3.707909539e-03f, -9.889973186e-03f, -1.747674315e-02f, -2.330941189e-02f, -2.344394342e-02f, -1.501356300e-02f, +1.924695104e-03f, +2.363284155e-02f, +4.366554511e-02f, +5.532647026e-02f, +5.450544680e-02f, +4.149605616e-02f, +2.086433852e-02f, -5.636456344e-04f, -1.656924930e-02f, -2.385854478e-02f, -2.281287459e-02f, -1.654568462e-02f, -8.993479016e-03f, -3.122976195e-03f, -6.504300803e-05f, +6.607192554e-04f,
+    /* 24, 8 */ +6.615239252e-04f, -1.770771536e-04f, -3.409698141e-03f, -9.438384277e-03f, -1.701401374e-02f, -2.307196251e-02f, -2.366824152e-02f, -1.580887853e-02f, +6.700666468e-04f, +2.225092083e-02f, +4.259684448e-02f, +5.494081698e-02f, +5.494081698e-02f, +4.259684448e-02f, +2.225092083e-02f, +6.700666468e-04f, -1.580887853e-02f, -2.366824152e-02f, -2.307196251e-02f, -1.701401374e-02f, -9.438384277e-03f, -3.409698141e-03f, -1.770771536e-04f, +6.615239252e-04f,
+    /* 24, 9 */ +6.607192554e-04f, -6.504300803e-05f, -3.122976195e-03f, -8.993479016e-03f, -1.654568462e-02f, -2.281287459e-02f, -2.385854478e-02f, -1.656924930e-02f, -5.636456344e-04f, +2.086433852e-02f, +4.149605616e-02f, +5.450544680e-02f, +5.532647026e-02f, +4.366554511e-02f, +2.363284155e-02f, +1.924695104e-03f, -1.501356300e-02f, -2.344394342e-02f, -2.330941189e-02f, -1.747674315e-02f, -9.889973186e-03f, -3.707909539e-03f, -2.987192943e-04f, +6.576902611e-04f,
+    /* 24,10 */ +6.555972530e-04f, +3.764667972e-05f, -2.847793367e-03f, -8.555789856e-03f, -1.607269547e-02f, -2.253313051e-02f, -2.401535937e-02f, -1.729430055e-02f, -1.775153809e-03f, +1.947486957e-02f, +4.036472049e-02f, +5.402099181e-02f, +5.566184614e-02f, +4.470065727e-02f, +2.500831779e-02f, +3.198904487e-03f, -1.418373842e-02f, -2.318519030e-02f, -2.352425464e-02f, -1.793291714e-02f, -1.034768250e-02f, -4.017533648e-03f, -4.302209069e-04f, +6.488941487e-04f,
+    /* 24,11 */ +6.464750685e-04f, +1.312664533e-04f, -2.584172964e-03f, -8.125818185e-03f, -1.559596853e-02f, -2.223372473e-02f, -2.413923574e-02f, -1.798371833e-02f, -2.963218986e-03f, +1.808427811e-02f, +3.920441468e-02f, +5.348815454e-02f, +5.594645674e-02f, +4.570072248e-02f, +2.637556170e-02f, +4.491314051e-03f, -1.331990148e-02f, -2.289156957e-02f, -2.371553901e-02f, -1.838156554e-02f, -1.081091853e-02f, -4.338465973e-03f, -5.718203517e-04f, +6.348091316e-04f,
+    /* 24,12 */ +6.336652968e-04f, +2.161008111e-04f, -2.332112699e-03f, -7.704034115e-03f, -1.511640714e-02f, -2.191566174e-02f, -2.423076690e-02f, -1.863724919e-02f, -4.126652928e-03f, +1.669431448e-02f, +3.801674993e-02f, +5.290770668e-02f, +5.617988770e-02f, +4.666432713e-02f, +2.773278351e-02f, +5.800499486e-03f, -1.242260980e-02f, -2.256271775e-02f, -2.388233174e-02f, -1.882170532e-02f, -1.127905759e-02f, -4.670573645e-03f, -7.237418200e-04f, +6.151072142e-04f,
+    /* 24,13 */ +6.174753085e-04f, +2.924431607e-04f, -2.091585491e-03f, -7.290876362e-03f, -1.463489443e-02f, -2.157995394e-02f, -2.429058667e-02f, -1.925469982e-02f, -5.264319552e-03f, +1.530671242e-02f, +3.680336864e-02f, +5.228048761e-02f, +5.636179897e-02f, +4.759010507e-02f, +2.907819451e-02f, +7.124994951e-03f, -1.149248169e-02f, -2.219832191e-02f, -2.402372023e-02f, -1.925234223e-02f, -1.175144649e-02f, -5.013694839e-03f, -8.861942853e-04f, +5.894596941e-04f,
+    /* 24,14 */ +5.982066157e-04f, +3.605947820e-04f, -1.862540304e-03f, -6.886752185e-03f, -1.415229209e-02f, -2.122761958e-02f, -2.431936776e-02f, -1.983593655e-02f, -6.375136316e-03f, +1.392318625e-02f, +3.556594146e-02f, +5.160740292e-02f, +5.649192540e-02f, +4.847674006e-02f, +3.041001010e-02f, +8.463295193e-03f, -1.053019587e-02f, -2.179812108e-02f, -2.413881461e-02f, -1.967247249e-02f, -1.222740313e-02f, -5.367638258e-03f, -1.059370463e-03f, +5.575380238e-04f,
+    /* 24,15 */ +5.761542752e-04f, +4.208638005e-04f, -1.644903025e-03f, -6.492037400e-03f, -1.366943909e-02f, -2.085968072e-02f, -2.431781992e-02f, -2.038088472e-02f, -7.458075491e-03f, +1.254542812e-02f, +3.430616434e-02f, +5.088942272e-02f, +5.657007725e-02f, +4.932296812e-02f, +3.172645287e-02f, +9.813857768e-03f, -9.536491055e-03f, -2.136190754e-02f, -2.422674988e-02f, -2.008108462e-02f, -1.270621714e-02f, -5.732182665e-03f, -1.243445781e-03f, +5.190146993e-04f,
+    /* 24, 0 */ +2.273459443e-03f, +5.435907648e-03f, +7.255296399e-03f, +3.788129032e-03f, -7.144684562e-03f, -2.265374973e-02f, -3.442368170e-02f, -3.287677614e-02f, -1.387331771e-02f, +1.679264590e-02f, +4.511451955e-02f, +5.659614054e-02f, +4.511451955e-02f, +1.679264590e-02f, -1.387331771e-02f, -3.287677614e-02f, -3.442368170e-02f, -2.265374973e-02f, -7.144684562e-03f, +3.788129032e-03f, +7.255296399e-03f, +5.435907648e-03f, +2.273459443e-03f, +3.568431303e-04f,
+    /* 24, 1 */ +2.103234406e-03f, +5.239407106e-03f, +7.256400195e-03f, +4.221207454e-03f, -6.266228991e-03f, -2.168841690e-02f, -3.399213075e-02f, -3.348773370e-02f, -1.551504116e-02f, +1.477681868e-02f, +4.371373211e-02f, +5.654911556e-02f, +4.644656718e-02f, +1.879953521e-02f, -1.218307761e-02f, -3.219410560e-02f, -3.480135038e-02f, -2.360501860e-02f, -8.042999544e-03f, +3.324105568e-03f, +7.232988111e-03f, +5.627714430e-03f, +2.449385040e-03f, +4.247055554e-04f,
+    /* 24, 2 */ +1.939021806e-03f, +5.039131826e-03f, +7.237298926e-03f, +4.623451366e-03f, -5.409068119e-03f, -2.071157693e-02f, -3.350883303e-02f, -3.402698064e-02f, -1.710557364e-02f, +1.275612557e-02f, +4.224725679e-02f, +5.640814528e-02f, +4.770696520e-02f, +2.079340350e-02f, -1.044713814e-02f, -3.143988919e-02f, -3.512309015e-02f, -2.453963953e-02f, -8.959642554e-03f, +2.829112073e-03f, +7.188501693e-03f, +5.813881008e-03f, +2.630658922e-03f, +4.992251326e-04f,
+    /* 24, 3 */ +1.781093672e-03f, +4.835971292e-03f, +7.199013760e-03f, +4.995053998e-03f, -4.574539506e-03f, -1.972575310e-02f, -3.297600576e-02f, -3.449468646e-02f, -1.864238902e-02f, +1.073461753e-02f, +4.071827965e-02f, +5.617354343e-02f, +4.889295364e-02f, +2.277016679e-02f, -8.668453837e-03f, -3.061446547e-02f, -3.538695026e-02f, -2.545500791e-02f, -9.892988076e-03f, +2.303211764e-03f, +7.120893393e-03f, +5.993435804e-03f, +2.816887995e-03f, +5.805470381e-04f,
+    /* 24, 4 */ +1.629682882e-03f, +4.630783503e-03f, +7.142583584e-03f, +5.336288236e-03f, -3.763881685e-03f, -1.873342898e-02f, -3.239594057e-02f, -3.489118499e-02f, -2.012311412e-02f, +8.716314532e-03f, +3.913011251e-02f, +5.584583195e-02f, +5.000192959e-02f, +2.472575033e-02f, -6.850110412e-03f, -2.971834586e-02f, -3.559108276e-02f, -2.634850527e-02f, -1.084131876e-02f, +1.746558492e-03f, +7.029253460e-03f, +6.165384566e-03f, +3.007638074e-03f, +6.687931554e-04f,
+    /* 24, 5 */ +1.484983972e-03f, +4.424393216e-03f, +7.069061064e-03f, +5.647503405e-03f, -2.978233194e-03f, -1.773704257e-02f, -3.177099609e-02f, -3.521697115e-02f, -2.154553308e-02f, +6.705195776e-03f, +3.748618427e-02f, +5.542573963e-02f, +5.103145416e-02f, +2.665609886e-02f, -4.995318215e-03f, -2.875221569e-02f, -3.573374914e-02f, -2.721750622e-02f, -1.180282806e-02f, +1.159398961e-03f, +6.912710257e-03f, +6.328712988e-03f, +3.202433762e-03f, +7.640603932e-04f,
+    /* 24, 6 */ +1.347154069e-03f, +4.217590351e-03f, +6.979508760e-03f, +5.929121902e-03f, -2.218631929e-03f, -1.673898061e-02f, -3.110359053e-02f, -3.547269735e-02f, -2.290759116e-02f, +4.705190128e-03f, +3.579003198e-02f, +5.491420012e-02f, +5.197925890e-02f, +2.855718684e-02f, -3.107405323e-03f, -2.771693469e-02f, -3.581332680e-02f, -2.805938547e-02f, -1.277562317e-02f, +5.420746921e-04f, +6.770434377e-03f, +6.482389493e-03f, +3.400758480e-03f, +8.664190421e-04f,
+    /* 24, 7 */ +1.216313935e-03f, +4.011128586e-03f, +6.874995333e-03f, +6.181635683e-03f, -1.486014823e-03f, -1.574157316e-02f, -3.039619415e-02f, -3.565916944e-02f, -2.420739811e-02f, +2.720166717e-03f, +3.404529163e-02f, +5.431234943e-02f, +5.284325182e-02f, +3.042502866e-02f, -1.189810237e-03f, -2.661353708e-02f, -3.582831530e-02f, -2.887152508e-02f, -1.375772836e-02f, -1.049762569e-04f, +6.601642735e-03f, +6.625368167e-03f, +3.602054665e-03f, +9.759111772e-04f,
+    /* 24, 8 */ +1.092549115e-03f, +3.805724130e-03f, +6.756591845e-03f, +6.405602617e-03f, -7.812178358e-04f, -1.474708852e-02f, -2.965132174e-02f, -3.577734234e-02f, -2.544323110e-02f, +7.539257812e-04f, +3.225568873e-02f, +5.362152294e-02f, +5.362152294e-02f, +3.225568873e-02f, +7.539257812e-04f, -2.544323110e-02f, -3.577734234e-02f, -2.965132174e-02f, -1.474708852e-02f, -7.812178358e-04f, +6.405602617e-03f, +6.756591845e-03f, +3.805724130e-03f, +1.092549115e-03f,
+    /* 24, 9 */ +9.759111772e-04f, +3.602054665e-03f, +6.625368167e-03f, +6.601642735e-03f, -1.049762569e-04f, -1.375772836e-02f, -2.887152508e-02f, -3.582831530e-02f, -2.661353708e-02f, -1.189810237e-03f, +3.042502866e-02f, +5.284325182e-02f, +5.431234943e-02f, +3.404529163e-02f, +2.720166717e-03f, -2.420739811e-02f, -3.565916944e-02f, -3.039619415e-02f, -1.574157316e-02f, -1.486014823e-03f, +6.181635683e-03f, +6.874995333e-03f, +4.011128586e-03f, +1.216313935e-03f,
+    /* 24,10 */ +8.664190421e-04f, +3.400758480e-03f, +6.482389493e-03f, +6.770434377e-03f, +5.420746921e-04f, -1.277562317e-02f, -2.805938547e-02f, -3.581332680e-02f, -2.771693469e-02f, -3.107405323e-03f, +2.855718684e-02f, +5.197925890e-02f, +5.491420012e-02f, +3.579003198e-02f, +4.705190128e-03f, -2.290759116e-02f, -3.547269735e-02f, -3.110359053e-02f, -1.673898061e-02f, -2.218631929e-03f, +5.929121902e-03f, +6.979508760e-03f, +4.217590351e-03f, +1.347154069e-03f,
+    /* 24,11 */ +7.640603932e-04f, +3.202433762e-03f, +6.328712988e-03f, +6.912710257e-03f, +1.159398961e-03f, -1.180282806e-02f, -2.721750622e-02f, -3.573374914e-02f, -2.875221569e-02f, -4.995318215e-03f, +2.665609886e-02f, +5.103145416e-02f, +5.542573963e-02f, +3.748618427e-02f, +6.705195776e-03f, -2.154553308e-02f, -3.521697115e-02f, -3.177099609e-02f, -1.773704257e-02f, -2.978233194e-03f, +5.647503405e-03f, +7.069061064e-03f, +4.424393216e-03f, +1.484983972e-03f,
+    /* 24,12 */ +6.687931554e-04f, +3.007638074e-03f, +6.165384566e-03f, +7.029253460e-03f, +1.746558492e-03f, -1.084131876e-02f, -2.634850527e-02f, -3.559108276e-02f, -2.971834586e-02f, -6.850110412e-03f, +2.472575033e-02f, +5.000192959e-02f, +5.584583195e-02f, +3.913011251e-02f, +8.716314532e-03f, -2.012311412e-02f, -3.489118499e-02f, -3.239594057e-02f, -1.873342898e-02f, -3.763881685e-03f, +5.336288236e-03f, +7.142583584e-03f, +4.630783503e-03f, +1.629682882e-03f,
+    /* 24,13 */ +5.805470381e-04f, +2.816887995e-03f, +5.993435804e-03f, +7.120893393e-03f, +2.303211764e-03f, -9.892988076e-03f, -2.545500791e-02f, -3.538695026e-02f, -3.061446547e-02f, -8.668453837e-03f, +2.277016679e-02f, +4.889295364e-02f, +5.617354343e-02f, +4.071827965e-02f, +1.073461753e-02f, -1.864238902e-02f, -3.449468646e-02f, -3.297600576e-02f, -1.972575310e-02f, -4.574539506e-03f, +4.995053998e-03f, +7.199013760e-03f, +4.835971292e-03f, +1.781093672e-03f,
+    /* 24,14 */ +4.992251326e-04f, +2.630658922e-03f, +5.813881008e-03f, +7.188501693e-03f, +2.829112073e-03f, -8.959642554e-03f, -2.453963953e-02f, -3.512309015e-02f, -3.143988919e-02f, -1.044713814e-02f, +2.079340350e-02f, +4.770696520e-02f, +5.640814528e-02f, +4.224725679e-02f, +1.275612557e-02f, -1.710557364e-02f, -3.402698064e-02f, -3.350883303e-02f, -2.071157693e-02f, -5.409068119e-03f, +4.623451366e-03f, +7.237298926e-03f, +5.039131826e-03f, +1.939021806e-03f,
+    /* 24,15 */ +4.247055554e-04f, +2.449385040e-03f, +5.627714430e-03f, +7.232988111e-03f, +3.324105568e-03f, -8.042999544e-03f, -2.360501860e-02f, -3.480135038e-02f, -3.219410560e-02f, -1.218307761e-02f, +1.879953521e-02f, +4.644656718e-02f, +5.654911556e-02f, +4.371373211e-02f, +1.477681868e-02f, -1.551504116e-02f, -3.348773370e-02f, -3.399213075e-02f, -2.168841690e-02f, -6.266228991e-03f, +4.221207454e-03f, +7.256400195e-03f, +5.239407106e-03f, +2.103234406e-03f,
+    /* 24, 0 */ -2.181310192e-03f, -7.982329251e-04f, +5.680209618e-03f, +1.430719659e-02f, +1.589843483e-02f, +2.406871994e-03f, -2.249676846e-02f, -4.130100690e-02f, -3.506718353e-02f, -1.541681908e-03f, +3.867898214e-02f, +5.659614054e-02f, +3.867898214e-02f, -1.541681908e-03f, -3.506718353e-02f, -4.130100690e-02f, -2.249676846e-02f, +2.406871994e-03f, +1.589843483e-02f, +1.430719659e-02f, +5.680209618e-03f, -7.982329251e-04f, -2.181310192e-03f, -9.324150638e-04f,
+    /* 24, 1 */ -2.142117633e-03f, -1.026327303e-03f, +5.144075019e-03f, +1.386145083e-02f, +1.619749380e-02f, +3.702669669e-03f, -2.089125129e-02f, -4.071342524e-02f, -3.635626763e-02f, -4.137848013e-03f, +3.654904222e-02f, +5.652117066e-02f, +4.071616274e-02f, +1.083860427e-03f, -3.366302266e-02f, -4.178478525e-02f, -2.407924887e-02f, +1.061252794e-03f, +1.553625174e-02f, +1.472529111e-02f, +6.227191439e-03f, -5.482915187e-04f, -2.210193915e-03f, -1.021372070e-03f,
+    /* 24, 2 */ -2.093579858e-03f, -1.232841058e-03f, +4.620399561e-03f, +1.339085359e-02f, +1.643462496e-02f, +4.945866528e-03f, -1.926829204e-02f, -4.002576024e-02f, -3.752797769e-02f, -6.697199080e-03f, +3.433305089e-02f, +5.629650283e-02f, +4.265414906e-02f, +3.731182183e-03f, -3.214649405e-02f, -4.216132985e-02f, -2.563305646e-02f, -3.311524193e-04f, +1.510995111e-02f, +1.511293981e-02f, +6.783287607e-03f, -2.763303743e-04f, -2.227804667e-03f, -1.112061839e-03f,
+    /* 24, 3 */ -2.036654869e-03f, -1.418128950e-03f, +4.110671651e-03f, +1.289819803e-02f, +1.661121711e-02f, +6.133943976e-03f, -1.763342417e-02f, -3.924200344e-02f, -3.858043162e-02f, -9.212479159e-03f, +3.203796457e-02f, +5.592286159e-02f, +4.448680259e-02f, +6.392554173e-03f, -3.052071354e-02f, -4.242751674e-02f, -2.715253413e-02f, -1.767057534e-03f, +1.461875233e-02f, +1.546736546e-02f, +7.346647501e-03f, +1.772445414e-05f, -2.233183138e-03f, -1.203936109e-03f,
+    /* 24, 4 */ -1.972290178e-03f, -1.582628528e-03f, +3.616254280e-03f, +1.238625918e-02f, +1.672884047e-02f, +7.264647438e-03f, -1.599210066e-02f, -3.836639935e-02f, -3.951216427e-02f, -1.167663915e-02f, +2.967096294e-02f, +5.540145153e-02f, +4.620830373e-02f, +9.060141131e-03f, -2.878919714e-02f, -4.258054458e-02f, -2.863202454e-02f, -3.242932618e-03f, +1.406209682e-02f, +1.578582064e-02f, +7.915306646e-03f, +3.338433846e-04f, -2.225380377e-03f, -1.296401007e-03f,
+    /* 24, 5 */ -1.901418208e-03f, -1.726854356e-03f, +3.138383775e-03f, +1.185778300e-02f, +1.678923519e-02f, +8.335988548e-03f, -1.434967550e-02f, -3.740342600e-02f, -4.032212766e-02f, -1.408285985e-02f, +2.723942290e-02f, +5.473395280e-02f, +4.781317317e-02f, +1.172602857e-02f, -2.695585286e-02f, -4.261794963e-02f, -3.006589126e-02f, -4.755011838e-03f, +1.343965653e-02f, +1.606560041e-02f, +8.487191262e-03f, +6.718888821e-04f, -2.203463508e-03f, -1.388818223e-03f,
+    /* 24, 6 */ -1.824951954e-03f, -1.851392085e-03f, +2.678169173e-03f, +1.131547576e-02f, +1.679429951e-02f, +9.346246206e-03f, -1.271138577e-02f, -3.635777499e-02f, -4.100968953e-02f, -1.642457396e-02f, +2.475089197e-02f, +5.392251484e-02f, +4.929629202e-02f, +1.438225015e-02f, -2.502497093e-02f, -4.253761970e-02f, -3.144854025e-02f, -6.299302508e-03f, +1.275134172e-02f, +1.630405519e-02f, +9.060123484e-03f, +1.031611407e-03f, -2.166521604e-03f, -1.480506488e-03f,
+    /* 24, 7 */ -1.743780953e-03f, -1.956892396e-03f, +2.236592212e-03f, +1.076199396e-02f, +1.674607744e-02f, +1.029396653e-02f, -1.108233467e-02f, -3.523433096e-02f, -4.157463041e-02f, -1.869548696e-02f, +2.221306113e-02f, +5.296974848e-02f, +5.065292065e-02f, +1.702081530e-02f, -2.300121251e-02f, -4.233780689e-02f, -3.277444146e-02f, -7.871595256e-03f, +1.199730786e-02f, +1.649860375e-02f, +9.631827247e-03f, +1.412645767e-03f, -2.113671692e-03f, -1.570743396e-03f,
+    /* 24, 8 */ -1.658767534e-03f, -2.044064852e-03f, +1.814507917e-03f, +1.019993481e-02f, +1.664674627e-02f, +1.117796172e-02f, -9.467475281e-03f, -3.403815061e-02f, -4.201713914e-02f, -2.088959684e-02f, +1.963373727e-02f, +5.187871616e-02f, +5.187871616e-02f, +1.963373727e-02f, -2.088959684e-02f, -4.201713914e-02f, -3.403815061e-02f, -9.467475281e-03f, +1.117796172e-02f, +1.664674627e-02f, +1.019993481e-02f, +1.814507917e-03f, -2.044064852e-03f, -1.658767534e-03f,
+    /* 24, 9 */ -1.570743396e-03f, -2.113671692e-03f, +1.412645767e-03f, +9.631827247e-03f, +1.649860375e-02f, +1.199730786e-02f, -7.871595256e-03f, -3.277444146e-02f, -4.233780689e-02f, -2.300121251e-02f, +1.702081530e-02f, +5.065292065e-02f, +5.296974848e-02f, +2.221306113e-02f, -1.869548696e-02f, -4.157463041e-02f, -3.523433096e-02f, -1.108233467e-02f, +1.029396653e-02f, +1.674607744e-02f, +1.076199396e-02f, +2.236592212e-03f, -1.956892396e-03f, -1.743780953e-03f,
+    /* 24,10 */ -1.480506488e-03f, -2.166521604e-03f, +1.031611407e-03f, +9.060123484e-03f, +1.630405519e-02f, +1.275134172e-02f, -6.299302508e-03f, -3.144854025e-02f, -4.253761970e-02f, -2.502497093e-02f, +1.438225015e-02f, +4.929629202e-02f, +5.392251484e-02f, +2.475089197e-02f, -1.642457396e-02f, -4.100968953e-02f, -3.635777499e-02f, -1.271138577e-02f, +9.346246206e-03f, +1.679429951e-02f, +1.131547576e-02f, +2.678169173e-03f, -1.851392085e-03f, -1.824951954e-03f,
+    /* 24,11 */ -1.388818223e-03f, -2.203463508e-03f, +6.718888821e-04f, +8.487191262e-03f, +1.606560041e-02f, +1.343965653e-02f, -4.755011838e-03f, -3.006589126e-02f, -4.261794963e-02f, -2.695585286e-02f, +1.172602857e-02f, +4.781317317e-02f, +5.473395280e-02f, +2.723942290e-02f, -1.408285985e-02f, -4.032212766e-02f, -3.740342600e-02f, -1.434967550e-02f, +8.335988548e-03f, +1.678923519e-02f, +1.185778300e-02f, +3.138383775e-03f, -1.726854356e-03f, -1.901418208e-03f,
+    /* 24,12 */ -1.296401007e-03f, -2.225380377e-03f, +3.338433846e-04f, +7.915306646e-03f, +1.578582064e-02f, +1.406209682e-02f, -3.242932618e-03f, -2.863202454e-02f, -4.258054458e-02f, -2.878919714e-02f, +9.060141131e-03f, +4.620830373e-02f, +5.540145153e-02f, +2.967096294e-02f, -1.167663915e-02f, -3.951216427e-02f, -3.836639935e-02f, -1.599210066e-02f, +7.264647438e-03f, +1.672884047e-02f, +1.238625918e-02f, +3.616254280e-03f, -1.582628528e-03f, -1.972290178e-03f,
+    /* 24,13 */ -1.203936109e-03f, -2.233183138e-03f, +1.772445414e-05f, +7.346647501e-03f, +1.546736546e-02f, +1.461875233e-02f, -1.767057534e-03f, -2.715253413e-02f, -4.242751674e-02f, -3.052071354e-02f, +6.392554173e-03f, +4.448680259e-02f, +5.592286159e-02f, +3.203796457e-02f, -9.212479159e-03f, -3.858043162e-02f, -3.924200344e-02f, -1.763342417e-02f, +6.133943976e-03f, +1.661121711e-02f, +1.289819803e-02f, +4.110671651e-03f, -1.418128950e-03f, -2.036654869e-03f,
+    /* 24,14 */ -1.112061839e-03f, -2.227804667e-03f, -2.763303743e-04f, +6.783287607e-03f, +1.511293981e-02f, +1.510995111e-02f, -3.311524193e-04f, -2.563305646e-02f, -4.216132985e-02f, -3.214649405e-02f, +3.731182183e-03f, +4.265414906e-02f, +5.629650283e-02f, +3.433305089e-02f, -6.697199080e-03f, -3.752797769e-02f, -4.002576024e-02f, -1.926829204e-02f, +4.945866528e-03f, +1.643462496e-02f, +1.339085359e-02f, +4.620399561e-03f, -1.232841058e-03f, -2.093579858e-03f,
+    /* 24,15 */ -1.021372070e-03f, -2.210193915e-03f, -5.482915187e-04f, +6.227191439e-03f, +1.472529111e-02f, +1.553625174e-02f, +1.061252794e-03f, -2.407924887e-02f, -4.178478525e-02f, -3.366302266e-02f, +1.083860427e-03f, +4.071616274e-02f, +5.652117066e-02f, +3.654904222e-02f, -4.137848013e-03f, -3.635626763e-02f, -4.071342524e-02f, -2.089125129e-02f, +3.702669669e-03f, +1.619749380e-02f, +1.386145083e-02f, +5.144075019e-03f, -1.026327303e-03f, -2.142117633e-03f,
+    /* 24, 0 */ -6.349328336e-04f, -5.107444449e-03f, -7.589492700e-03f, +4.421169254e-04f, +1.733331948e-02f, +2.497839430e-02f, +6.069612140e-03f, -2.970035006e-02f, -4.651799663e-02f, -1.968310326e-02f, +3.102388246e-02f, +5.659614054e-02f, +3.102388246e-02f, -1.968310326e-02f, -4.651799663e-02f, -2.970035006e-02f, +6.069612140e-03f, +2.497839430e-02f, +1.733331948e-02f, +4.421169254e-04f, -7.589492700e-03f, -5.107444449e-03f, -6.349328336e-04f, +6.381929817e-04f,
+    /* 24, 1 */ -4.501246045e-04f, -4.794789988e-03f, -7.673310752e-03f, -4.276921651e-04f, +1.630487239e-02f, +2.519230977e-02f, +8.023727481e-03f, -2.760467194e-02f, -4.668156835e-02f, -2.250226055e-02f, +2.808383767e-02f, +5.648624601e-02f, +3.385706239e-02f, -1.675917373e-02f, -4.616688010e-02f, -3.171828840e-02f, +4.039135426e-03f, +2.465060544e-02f, +1.832599562e-02f, +1.353161175e-03f, -7.461005422e-03f, -5.414037993e-03f, -8.347893499e-04f, +6.459962873e-04f,
+    /* 24, 2 */ -2.805431667e-04f, -4.478334337e-03f, -7.714347254e-03f, -1.253762011e-03f, +1.524677189e-02f, +2.529479915e-02f, +9.894771606e-03f, -2.544172743e-02f, -4.665953469e-02f, -2.520578478e-02f, +2.504972253e-02f, +5.615705320e-02f, +3.657100703e-02f, -1.374190145e-02f, -4.562711379e-02f, -3.364818878e-02f, +1.939527429e-03f, +2.420699082e-02f, +1.927675855e-02f, +2.302607998e-03f, -7.286133997e-03f, -5.712221732e-03f, -1.049390115e-03f, +6.454263440e-04f,
+    /* 24, 3 */ -1.262469087e-04f, -4.160237857e-03f, -7.714644364e-03f, -2.033919173e-03f, +1.416507919e-02f, +2.528877950e-02f, +1.167657735e-02f, -2.322211124e-02f, -4.645464989e-02f, -2.778343069e-02f, +2.193469332e-02f, +5.561003206e-02f, +3.915383052e-02f, -1.064323425e-02f, -4.489844274e-02f, -3.547998209e-02f, -2.214871506e-04f, +2.364611605e-02f, +2.017947396e-02f, +3.287300483e-03f, -7.063354139e-03f, -5.999568857e-03f, -1.278300802e-03f, +6.356530069e-04f,
+    /* 24, 4 */ +1.281987696e-05f, -3.842552418e-03f, -7.676380196e-03f, -2.766321017e-03f, +1.306576874e-02f, +2.517761055e-02f, +1.336353941e-02f, -2.095648565e-02f, -4.607045954e-02f, -3.022561220e-02f, +1.875220414e-02f, +5.484762432e-02f, +4.159418981e-02f, -7.475585158e-03f, -4.398147340e-02f, -3.720388175e-02f, -2.435717775e-03f, +2.296708552e-02f, +2.102804905e-02f, +4.303763633e-03f, -6.791349552e-03f, -6.273586899e-03f, -1.520952773e-03f, +6.158659890e-04f,
+    /* 24, 5 */ +1.368204413e-04f, -3.527213369e-03f, -7.601850138e-03f, -3.449453954e-03f, +1.195469912e-02f, +2.496506585e-02f, +1.495063011e-02f, -1.865552736e-02f, -4.551127331e-02f, -3.252344226e-02f, +1.551594186e-02f, +5.387323140e-02f, +4.388134039e-02f, -4.251776447e-03f, -4.287768073e-02f, -3.881043651e-02f, -4.694539858e-03f, +2.216956067e-02f, +2.181646678e-02f, +5.348212687e-03f, -6.469028645e-03f, -6.531730950e-03f, -1.776639841e-03f, +5.852828120e-04f,
+    /* 24, 6 */ +2.460185614e-04f, -3.216032617e-03f, -7.493448175e-03f, -4.082129823e-03f, +1.083758546e-02f, +2.465530244e-02f, +1.643341199e-02f, -1.632987505e-02f, -4.478213426e-02f, -3.466876896e-02f, +1.223976005e-02f, +5.269119738e-02f, +4.600518917e-02f, -9.849812576e-04f, -4.158941105e-02f, -4.029058231e-02f, -6.988929457e-03f, +2.125377578e-02f, +2.253882061e-02f, +6.416563547e-03f, -6.095540465e-03f, -6.771417794e-03f, -2.044515881e-03f, +5.431569434e-04f,
+    /* 24, 7 */ +3.407711290e-04f, -2.910692818e-03f, -7.353648294e-03f, -4.663480492e-03f, +9.719973395e-03f, +2.425282916e-02f, +1.780804686e-02f, -1.399007798e-02f, -4.388878466e-02f, -3.665420751e-02f, +8.937612534e-03f, +5.130678745e-02f, +4.795634432e-02f, +2.311336934e-03f, -4.011988036e-02f, -4.163569289e-02f, -9.309500719e-03f, +2.022055084e-02f, +2.318934965e-02f, +7.504445299e-03f, -5.670289717e-03f, -6.990040888e-03f, -2.323593338e-03f, +4.887860648e-04f,
+    /* 24, 8 */ +4.215204125e-04f, -2.612742676e-03f, -7.184986124e-03f, -5.192950770e-03f, +8.607214812e-03f, +2.376247385e-02f, +1.907130164e-02f, -1.164654593e-02f, -4.283762880e-02f, -3.847316827e-02f, +5.623486691e-03f, +4.972616165e-02f, +4.972616165e-02f, +5.623486691e-03f, -3.847316827e-02f, -4.283762880e-02f, -1.164654593e-02f, +1.907130164e-02f, +2.376247385e-02f, +8.607214812e-03f, -5.192950770e-03f, -7.184986124e-03f, -2.612742676e-03f, +4.215204125e-04f,
+    /* 24, 9 */ +4.887860648e-04f, -2.323593338e-03f, -6.990040888e-03f, -5.670289717e-03f, +7.504445299e-03f, +2.318934965e-02f, +2.022055084e-02f, -9.309500719e-03f, -4.163569289e-02f, -4.011988036e-02f, +2.311336934e-03f, +4.795634432e-02f, +5.130678745e-02f, +8.937612534e-03f, -3.665420751e-02f, -4.388878466e-02f, -1.399007798e-02f, +1.780804686e-02f, +2.425282916e-02f, +9.719973395e-03f, -4.663480492e-03f, -7.353648294e-03f, -2.910692818e-03f, +3.407711290e-04f,
+    /* 24,10 */ +5.431569434e-04f, -2.044515881e-03f, -6.771417794e-03f, -6.095540465e-03f, +6.416563547e-03f, +2.253882061e-02f, +2.125377578e-02f, -6.988929457e-03f, -4.029058231e-02f, -4.158941105e-02f, -9.849812576e-04f, +4.600518917e-02f, +5.269119738e-02f, +1.223976005e-02f, -3.466876896e-02f, -4.478213426e-02f, -1.632987505e-02f, +1.643341199e-02f, +2.465530244e-02f, +1.083758546e-02f, -4.082129823e-03f, -7.493448175e-03f, -3.216032617e-03f, +2.460185614e-04f,
+    /* 24,11 */ +5.852828120e-04f, -1.776639841e-03f, -6.531730950e-03f, -6.469028645e-03f, +5.348212687e-03f, +2.181646678e-02f, +2.216956067e-02f, -4.694539858e-03f, -3.881043651e-02f, -4.287768073e-02f, -4.251776447e-03f, +4.388134039e-02f, +5.387323140e-02f, +1.551594186e-02f, -3.252344226e-02f, -4.551127331e-02f, -1.865552736e-02f, +1.495063011e-02f, +2.496506585e-02f, +1.195469912e-02f, -3.449453954e-03f, -7.601850138e-03f, -3.527213369e-03f, +1.368204413e-04f,
+    /* 24,12 */ +6.158659890e-04f, -1.520952773e-03f, -6.273586899e-03f, -6.791349552e-03f, +4.303763633e-03f, +2.102804905e-02f, +2.296708552e-02f, -2.435717775e-03f, -3.720388175e-02f, -4.398147340e-02f, -7.475585158e-03f, +4.159418981e-02f, +5.484762432e-02f, +1.875220414e-02f, -3.022561220e-02f, -4.607045954e-02f, -2.095648565e-02f, +1.336353941e-02f, +2.517761055e-02f, +1.306576874e-02f, -2.766321017e-03f, -7.676380196e-03f, -3.842552418e-03f, +1.281987696e-05f,
+    /* 24,13 */ +6.356530069e-04f, -1.278300802e-03f, -5.999568857e-03f, -7.063354139e-03f, +3.287300483e-03f, +2.017947396e-02f, +2.364611605e-02f, -2.214871506e-04f, -3.547998209e-02f, -4.489844274e-02f, -1.064323425e-02f, +3.915383052e-02f, +5.561003206e-02f, +2.193469332e-02f, -2.778343069e-02f, -4.645464989e-02f, -2.322211124e-02f, +1.167657735e-02f, +2.528877950e-02f, +1.416507919e-02f, -2.033919173e-03f, -7.714644364e-03f, -4.160237857e-03f, -1.262469087e-04f,
+    /* 24,14 */ +6.454263440e-04f, -1.049390115e-03f, -5.712221732e-03f, -7.286133997e-03f, +2.302607998e-03f, +1.927675855e-02f, +2.420699082e-02f, +1.939527429e-03f, -3.364818878e-02f, -4.562711379e-02f, -1.374190145e-02f, +3.657100703e-02f, +5.615705320e-02f, +2.504972253e-02f, -2.520578478e-02f, -4.665953469e-02f, -2.544172743e-02f, +9.894771606e-03f, +2.529479915e-02f, +1.524677189e-02f, -1.253762011e-03f, -7.714347254e-03f, -4.478334337e-03f, -2.805431667e-04f,
+    /* 24,15 */ +6.459962873e-04f, -8.347893499e-04f, -5.414037993e-03f, -7.461005422e-03f, +1.353161175e-03f, +1.832599562e-02f, +2.465060544e-02f, +4.039135426e-03f, -3.171828840e-02f, -4.616688010e-02f, -1.675917373e-02f, +3.385706239e-02f, +5.648624601e-02f, +2.808383767e-02f, -2.250226055e-02f, -4.668156835e-02f, -2.760467194e-02f, +8.023727481e-03f, +2.519230977e-02f, +1.630487239e-02f, -4.276921651e-04f, -7.673310752e-03f, -4.794789988e-03f, -4.501246045e-04f,
+    /* 24, 0 */ +1.197013499e-03f, +3.320493122e-03f, -3.017233048e-03f, -9.987553340e-03f, -4.112967049e-03f, +1.524501264e-02f, +2.235677036e-02f, -2.137559158e-03f, -3.327370097e-02f, -2.660122408e-02f, +1.657531807e-02f, +4.232694754e-02f, +1.657531807e-02f, -2.660122408e-02f, -3.327370097e-02f, -2.137559158e-03f, +2.235677036e-02f, +1.524501264e-02f, -4.112967049e-03f, -9.987553340e-03f, -3.017233048e-03f, +2.318357031e-03f, +1.197013499e-03f, -1.261205705e-04f,
+    /* 24, 1 */ +1.060573898e-03f, +3.246029352e-03f, -2.510607281e-03f, -9.784551764e-03f, -5.018393221e-03f, +1.404948670e-02f, +2.282515537e-02f, +7.626640355e-05f, -3.208475603e-02f, -2.845760231e-02f, +1.374134711e-02f, +4.221250979e-02f, +1.933345641e-02f, -2.458052660e-02f, -3.429687591e-02f, -4.386190237e-03f, +2.174698353e-02f, +1.639120730e-02f, -3.143642175e-03f, -1.013545813e-02f, -3.535011248e-03f, +2.193303334e-03f, +1.338504197e-03f, -9.901282259e-05f,
+    /* 24, 2 */ +9.298712414e-04f, +3.156277727e-03f, -2.018194158e-03f, -9.530340989e-03f, -5.856606243e-03f, +1.281349999e-02f, +2.315294314e-02f, +2.243024978e-03f, -3.073934924e-02f, -3.014061672e-02f, +1.084811230e-02f, +4.186988491e-02f, +2.199957595e-02f, -2.240563854e-02f, -3.514586546e-02f, -6.656913804e-03f, +2.099588115e-02f, +1.747926153e-02f, -2.114297018e-03f, -1.022461460e-02f, -4.060636553e-03f, +2.039754046e-03f, +1.484263468e-03f, -6.526428163e-05f,
+    /* 24, 3 */ +8.054954021e-04f, +3.052984548e-03f, -1.542795645e-03f, -9.229007144e-03f, -6.624860539e-03f, +1.154592266e-02f, +2.334180387e-02f, +4.350977952e-03f, -2.924761047e-02f, -3.164235460e-02f, +7.912459038e-03f, +4.130113344e-02f, +2.455797710e-02f, -2.008771737e-02f, -3.581321303e-02f, -8.936640836e-03f, +2.010445982e-02f, +1.850049032e-02f, -1.029364704e-03f, -1.025164428e-02f, -4.590574200e-03f, +1.857070273e-03f, +1.633412152e-03f, -2.453170435e-05f,
+    /* 24, 4 */ +6.879416803e-04f, +2.937877889e-03f, -1.086944946e-03f, -8.884797195e-03f, -7.320980005e-03f, +1.025556440e-02f, +2.339424278e-02f, +6.388976156e-03f, -2.762041561e-02f, -3.295607494e-02f, +4.951401864e-03f, +4.050967459e-02f, +2.699354793e-02f, -1.763888677e-02f, -3.629248103e-02f, -1.121198570e-02f, +1.907464002e-02f, +1.944639638e-02f, +1.061806254e-04f, -1.021347789e-02f, -5.121078221e-03f, +1.644827972e-03f, +1.784975276e-03f, +2.348660763e-05f,
+    /* 24, 5 */ +5.776128643e-04f, +2.812656385e-03f, -6.528985547e-04f, -8.502081335e-03f, -7.943354450e-03f, +8.951116293e-03f, +2.331356438e-02f, +8.346521334e-03f, -2.586930694e-02f, -3.407624020e-02f, +1.982016505e-03f, +3.950026382e-02f, +2.929186185e-02f, -1.507216716e-02f, -3.657830513e-02f, -1.346934883e-02f, +1.790927350e-02f, +2.030873394e-02f, +1.286841938e-03f, -1.010739044e-02f, -5.648212144e-03f, +1.402832649e-03f, +1.937883690e-03f, +7.904503123e-05f,
+    /* 24, 6 */ +4.748218959e-04f, +2.678978820e-03f, -2.426308611e-04f, -8.085315763e-03f, -8.490932000e-03f, +7.641095022e-03f, +2.310383199e-02f, +1.021382218e-02f, -2.400641001e-02f, -3.499853994e-02f, -9.786680537e-04f, +3.827896159e-02f, +3.143927100e-02f, -1.240139974e-02f, -3.666644182e-02f, -1.569500220e-02f, +1.661214427e-02f, +2.107957227e-02f, +2.506621864e-03f, -9.931034771e-03f, -6.167872094e-03f, +1.131132707e-03f, +2.090976552e-03f, +1.423449116e-04f,
+    /* 24, 7 */ +3.797950945e-04f, +2.538454547e-03f, +1.421687298e-04f, -7.639006136e-03f, -8.963207645e-03f, +6.333789781e-03f, +2.276982298e-02f, +1.198184460e-02f, -2.204434780e-02f, -3.571990598e-02f, -3.913776625e-03f, +3.685309369e-02f, +3.342299483e-02f, -9.641164594e-03f, -3.655380902e-02f, -1.787517696e-02f, +1.518796320e-02f, +2.175135864e-02f, +3.759049618e-03f, -9.682473374e-03f, -6.675812165e-03f, +8.300312585e-04f, +2.243004675e-03f, +2.135289700e-04f,
+    /* 24, 8 */ +2.926758839e-04f, +2.392634763e-03f, +5.000962484e-04f, -7.167671961e-03f, -9.360208124e-03f, +5.037212205e-03f, +2.231697999e-02f, +1.364235598e-02f, -1.999615271e-02f, -3.623851940e-02f, -6.806693291e-03f, +3.523120326e-02f, +3.523120326e-02f, -6.806693291e-03f, -3.623851940e-02f, -1.999615271e-02f, +1.364235598e-02f, +2.231697999e-02f, +5.037212205e-03f, -9.360208124e-03f, -7.167671961e-03f, +5.000962484e-04f, +2.392634763e-03f, +2.926758839e-04f,
+    /* 24, 9 */ +2.135289700e-04f, +2.243004675e-03f, +8.300312585e-04f, -6.675812165e-03f, -9.682473374e-03f, +3.759049618e-03f, +2.175135864e-02f, +1.518796320e-02f, -1.787517696e-02f, -3.655380902e-02f, -9.641164594e-03f, +3.342299483e-02f, +3.685309369e-02f, -3.913776625e-03f, -3.571990598e-02f, -2.204434780e-02f, +1.198184460e-02f, +2.276982298e-02f, +6.333789781e-03f, -8.963207645e-03f, -7.639006136e-03f, +1.421687298e-04f, +2.538454547e-03f, +3.797950945e-04f,
+    /* 24,10 */ +1.423449116e-04f, +2.090976552e-03f, +1.131132707e-03f, -6.167872094e-03f, -9.931034771e-03f, +2.506621864e-03f, +2.107957227e-02f, +1.661214427e-02f, -1.569500220e-02f, -3.666644182e-02f, -1.240139974e-02f, +3.143927100e-02f, +3.827896159e-02f, -9.786680537e-04f, -3.499853994e-02f, -2.400641001e-02f, +1.021382218e-02f, +2.310383199e-02f, +7.641095022e-03f, -8.490932000e-03f, -8.085315763e-03f, -2.426308611e-04f, +2.678978820e-03f, +4.748218959e-04f,
+    /* 24,11 */ +7.904503123e-05f, +1.937883690e-03f, +1.402832649e-03f, -5.648212144e-03f, -1.010739044e-02f, +1.286841938e-03f, +2.030873394e-02f, +1.790927350e-02f, -1.346934883e-02f, -3.657830513e-02f, -1.507216716e-02f, +2.929186185e-02f, +3.950026382e-02f, +1.982016505e-03f, -3.407624020e-02f, -2.586930694e-02f, +8.346521334e-03f, +2.331356438e-02f, +8.951116293e-03f, -7.943354450e-03f, -8.502081335e-03f, -6.528985547e-04f, +2.812656385e-03f, +5.776128643e-04f,
+    /* 24,12 */ +2.348660763e-05f, +1.784975276e-03f, +1.644827972e-03f, -5.121078221e-03f, -1.021347789e-02f, +1.061806254e-04f, +1.944639638e-02f, +1.907464002e-02f, -1.121198570e-02f, -3.629248103e-02f, -1.763888677e-02f, +2.699354793e-02f, +4.050967459e-02f, +4.951401864e-03f, -3.295607494e-02f, -2.762041561e-02f, +6.388976156e-03f, +2.339424278e-02f, +1.025556440e-02f, -7.320980005e-03f, -8.884797195e-03f, -1.086944946e-03f, +2.937877889e-03f, +6.879416803e-04f,
+    /* 24,13 */ -2.453170435e-05f, +1.633412152e-03f, +1.857070273e-03f, -4.590574200e-03f, -1.025164428e-02f, -1.029364704e-03f, +1.850049032e-02f, +2.010445982e-02f, -8.936640836e-03f, -3.581321303e-02f, -2.008771737e-02f, +2.455797710e-02f, +4.130113344e-02f, +7.912459038e-03f, -3.164235460e-02f, -2.924761047e-02f, +4.350977952e-03f, +2.334180387e-02f, +1.154592266e-02f, -6.624860539e-03f, -9.229007144e-03f, -1.542795645e-03f, +3.052984548e-03f, +8.054954021e-04f,
+    /* 24,14 */ -6.526428163e-05f, +1.484263468e-03f, +2.039754046e-03f, -4.060636553e-03f, -1.022461460e-02f, -2.114297018e-03f, +1.747926153e-02f, +2.099588115e-02f, -6.656913804e-03f, -3.514586546e-02f, -2.240563854e-02f, +2.199957595e-02f, +4.186988491e-02f, +1.084811230e-02f, -3.014061672e-02f, -3.073934924e-02f, +2.243024978e-03f, +2.315294314e-02f, +1.281349999e-02f, -5.856606243e-03f, -9.530340989e-03f, -2.018194158e-03f, +3.156277727e-03f, +9.298712414e-04f,
+    /* 24,15 */ -9.901282259e-05f, +1.338504197e-03f, +2.193303334e-03f, -3.535011248e-03f, -1.013545813e-02f, -3.143642175e-03f, +1.639120730e-02f, +2.174698353e-02f, -4.386190237e-03f, -3.429687591e-02f, -2.458052660e-02f, +1.933345641e-02f, +4.221250979e-02f, +1.374134711e-02f, -2.845760231e-02f, -3.208475603e-02f, +7.626640355e-05f, +2.282515537e-02f, +1.404948670e-02f, -5.018393221e-03f, -9.784551764e-03f, -2.510607281e-03f, +3.246029352e-03f, +1.060573898e-03f,
+    /* 20, 0 */ +2.832126065e-03f, -3.455346407e-03f, -1.050167676e-02f, +5.399047405e-04f, +2.072547687e-02f, +1.261483344e-02f, -2.310421022e-02f, -3.076111900e-02f, +1.034529168e-02f, +3.949755319e-02f, +1.034529168e-02f, -3.076111900e-02f, -2.310421022e-02f, +1.261483344e-02f, +2.072547687e-02f, +5.399047405e-04f, -1.050167676e-02f, -3.455346407e-03f, +2.832126065e-03f, +1.002136091e-03f,
+    /* 20, 1 */ +2.918309836e-03f, -2.848965042e-03f, -1.044663371e-02f, -7.454467031e-04f, +1.993701966e-02f, +1.431336010e-02f, -2.105256806e-02f, -3.196996361e-02f, +7.274030562e-03f, +3.936378623e-02f, +1.336427867e-02f, -2.932648708e-02f, -2.503260290e-02f, +1.078376120e-02f, +2.138841986e-02f, +1.874755806e-03f, -1.047502359e-02f, -4.071193337e-03f, +2.709872705e-03f, +1.184613130e-03f,
+    /* 20, 2 */ +2.970014434e-03f, -2.256846905e-03f, -1.031411254e-02f, -1.973175306e-03f, +1.903277841e-02f, +1.586999913e-02f, -1.889465735e-02f, -3.294695719e-02f, +4.172714360e-03f, +3.896348732e-02f, +1.630904655e-02f, -2.767391419e-02f, -2.682143551e-02f, +8.830742245e-03f, +2.191685631e-02f, +3.250271284e-03f, -1.036300090e-02f, -4.691364292e-03f, +2.550244709e-03f, +1.728121332e-03f,
+    /* 20, 3 */ +2.989084466e-03f, -1.683415968e-03f, -1.010881741e-02f, -3.135916081e-03f, +1.802312126e-02f, +1.727671449e-02f, -1.664798407e-02f, -3.368784795e-02f, +1.063660935e-03f, +3.829965427e-02f, +1.915809828e-02f, -2.581298498e-02f, -2.845520951e-02f, +6.767571398e-03f, +2.230262774e-02f, +4.656958119e-03f, -1.016253578e-02f, -5.310408414e-03f, +2.352251997e-03f, +1.906494808e-03f,
+    /* 20, 4 */ +2.977586348e-03f, -1.132697564e-03f, -9.835877420e-03f, -4.227100403e-03f, +1.691895133e-02f, +1.852681335e-02f, -1.433043179e-02f, -3.419021020e-02f, -2.030888217e-03f, +3.737725626e-02f, +2.189055571e-02f, -2.375496340e-02f, -2.991937541e-02f, +4.607167163e-03f, +2.253850054e-02f, +6.084727180e-03f, -9.871207756e-03f, -5.922604667e-03f, +2.115247068e-03f, +2.079939639e-03f,
+    /* 20, 5 */ +2.937775120e-03f, -6.082983663e-04f, -9.500783787e-03f, -5.240985904e-03f, +1.573160178e-02f, +1.961497125e-02f, -1.196011335e-02f, -3.445344345e-02f, -5.088941581e-03f, +3.620319350e-02f, +2.448632622e-02f, -2.151271924e-02f, -3.120046374e-02f, +2.363488482e-03f, +2.261825107e-02f, +7.522962281e-03f, -9.487296369e-03f, -6.522005316e-03f, +1.838950241e-03f, +2.245949018e-03f,
+    /* 20, 6 */ +2.872060854e-03f, -1.133913219e-04f, -9.109325922e-03f, -6.172678101e-03f, +1.447272980e-02f, +2.053724533e-02f, -9.555222824e-03f, -3.447875653e-02f, -8.088928223e-03f, +3.478624106e-02f, +2.692626358e-02f, -1.910064083e-02f, -3.228620876e-02f, +5.144107936e-05f, +2.253674404e-02f, +8.960596019e-03f, -9.009823935e-03f, -7.102483479e-03f, +1.523472156e-03f, +2.401928729e-03f,
+    /* 20, 7 */ +2.782975009e-03f, +3.492945151e-04f, -8.667527067e-03f, -7.018143782e-03f, +1.315421060e-02f, +2.129107545e-02f, -7.133889173e-03f, -3.426913715e-02f, -1.100986395e-02f, +3.313697764e-02f, +2.919232167e-02f, -1.653453452e-02f, -3.316566379e-02f, -2.313225982e-03f, +2.229000326e-02f, +1.038619190e-02f, -8.438592747e-03f, -7.657784411e-03f, +1.169333173e-03f, +2.545222121e-03f,
+    /* 20, 8 */ +2.673137115e-03f, +7.774793244e-04f, -8.181580147e-03f, -7.774216267e-03f, +1.178803215e-02f, +2.187527386e-02f, -4.714032773e-03f, -3.382930709e-02f, -1.383151166e-02f, +3.126769968e-02f, +3.126769968e-02f, -1.383151166e-02f, -3.382930709e-02f, -4.714032773e-03f, +2.187527386e-02f, +1.178803215e-02f, -7.774216267e-03f, -8.181580147e-03f, +7.774793244e-04f, +2.673137115e-03f,
+    /* 20, 9 */ +2.545222121e-03f, +1.169333173e-03f, -7.657784411e-03f, -8.438592747e-03f, +1.038619190e-02f, +2.229000326e-02f, -2.313225982e-03f, -3.316566379e-02f, -1.653453452e-02f, +2.919232167e-02f, +3.313697764e-02f, -1.100986395e-02f, -3.426913715e-02f, -7.133889173e-03f, +2.129107545e-02f, +1.315421060e-02f, -7.018143782e-03f, -8.667527067e-03f, +3.492945151e-04f, +2.782975009e-03f,
+    /* 20,10 */ +2.401928729e-03f, +1.523472156e-03f, -7.102483479e-03f, -9.009823935e-03f, +8.960596019e-03f, +2.253674404e-02f, +5.144107936e-05f, -3.228620876e-02f, -1.910064083e-02f, +2.692626358e-02f, +3.478624106e-02f, -8.088928223e-03f, -3.447875653e-02f, -9.555222824e-03f, +2.053724533e-02f, +1.447272980e-02f, -6.172678101e-03f, -9.109325922e-03f, -1.133913219e-04f, +2.872060854e-03f,
+    /* 20,11 */ +2.245949018e-03f, +1.838950241e-03f, -6.522005316e-03f, -9.487296369e-03f, +7.522962281e-03f, +2.261825107e-02f, +2.363488482e-03f, -3.120046374e-02f, -2.151271924e-02f, +2.448632622e-02f, +3.620319350e-02f, -5.088941581e-03f, -3.445344345e-02f, -1.196011335e-02f, +1.961497125e-02f, +1.573160178e-02f, -5.240985904e-03f, -9.500783787e-03f, -6.082983663e-04f, +2.937775120e-03f,
+    /* 20,12 */ +2.079939639e-03f, +2.115247068e-03f, -5.922604667e-03f, -9.871207756e-03f, +6.084727180e-03f, +2.253850054e-02f, +4.607167163e-03f, -2.991937541e-02f, -2.375496340e-02f, +2.189055571e-02f, +3.737725626e-02f, -2.030888217e-03f, -3.419021020e-02f, -1.433043179e-02f, +1.852681335e-02f, +1.691895133e-02f, -4.227100403e-03f, -9.835877420e-03f, -1.132697564e-03f, +2.977586348e-03f,
+    /* 20,13 */ +1.906494808e-03f, +2.352251997e-03f, -5.310408414e-03f, -1.016253578e-02f, +4.656958119e-03f, +2.230262774e-02f, +6.767571398e-03f, -2.845520951e-02f, -2.581298498e-02f, +1.915809828e-02f, +3.829965427e-02f, +1.063660935e-03f, -3.368784795e-02f, -1.664798407e-02f, +1.727671449e-02f, +1.802312126e-02f, -3.135916081e-03f, -1.010881741e-02f, -1.683415968e-03f, +2.989084466e-03f,
+    /* 20,14 */ +1.728121332e-03f, +2.550244709e-03f, -4.691364292e-03f, -1.036300090e-02f, +3.250271284e-03f, +2.191685631e-02f, +8.830742245e-03f, -2.682143551e-02f, -2.767391419e-02f, +1.630904655e-02f, +3.896348732e-02f, +4.172714360e-03f, -3.294695719e-02f, -1.889465735e-02f, +1.586999913e-02f, +1.903277841e-02f, -1.973175306e-03f, -1.031411254e-02f, -2.256846905e-03f, +2.970014434e-03f,
+    /* 20,15 */ +1.184613130e-03f, +2.709872705e-03f, -4.071193337e-03f, -1.047502359e-02f, +1.874755806e-03f, +2.138841986e-02f, +1.078376120e-02f, -2.503260290e-02f, -2.932648708e-02f, +1.336427867e-02f, +3.936378623e-02f, +7.274030562e-03f, -3.196996361e-02f, -2.105256806e-02f, +1.431336010e-02f, +1.993701966e-02f, -7.454467031e-04f, -1.044663371e-02f, -2.848965042e-03f, +2.918309836e-03f,
+    /* 20, 0 */ +1.702864838e-03f, +2.314577966e-03f, -6.534433696e-03f, -8.774562126e-03f, +1.199271213e-02f, +2.083400312e-02f, -1.263546899e-02f, -3.379691899e-02f, +5.498355442e-03f, +3.949755319e-02f, +5.498355442e-03f, -3.379691899e-02f, -1.263546899e-02f, +2.083400312e-02f, +1.199271213e-02f, -8.774562126e-03f, -6.534433696e-03f, +2.314577966e-03f, +1.702864838e-03f, +0.000000000e+00f,
+    /* 20, 1 */ +1.499435496e-03f, +2.547675666e-03f, -5.868098774e-03f, -9.353385898e-03f, +1.044920601e-02f, +2.160032962e-02f, -9.997584470e-03f, -3.429852636e-02f, +2.083565903e-03f, +3.933622696e-02f, +8.892197588e-03f, -3.300722623e-02f, -1.522519578e-02f, +1.986341365e-02f, +1.349096787e-02f, -8.082206357e-03f, -7.177938171e-03f, +2.033576095e-03f, +1.903844954e-03f, +0.000000000e+00f,
+    /* 20, 2 */ +8.979094201e-04f, +2.733567376e-03f, -5.187117330e-03f, -9.818374279e-03f, +8.876306372e-03f, +2.216139438e-02f, -7.335624654e-03f, -3.451169090e-02f, -1.322648265e-03f, +3.885370480e-02f, +1.223556951e-02f, -3.193245877e-02f, -1.774265256e-02f, +1.869155385e-02f, +1.492800767e-02f, -7.277832099e-03f, -7.790241855e-03f, +1.704529263e-03f, +2.099160368e-03f, -3.513221827e-04f,
+    /* 20, 3 */ +7.046452899e-04f, +2.873469547e-03f, -4.499404245e-03f, -1.017038969e-02f, +7.289604703e-03f, +2.251815219e-02f, -4.673411391e-03f, -3.443867914e-02f, -4.691042688e-03f, +3.805434209e-02f, +1.549922824e-02f, -3.057828381e-02f, -2.016393226e-02f, +1.732343066e-02f, +1.628791973e-02f, -6.364195274e-03f, -8.362876507e-03f, +1.327887989e-03f, +2.285430476e-03f, -3.288882594e-04f,
+    /* 20, 4 */ +5.275063595e-04f, +2.969073324e-03f, -3.812529364e-03f, -1.041140495e-02f, +5.704278009e-03f, +2.267345221e-02f, -2.034281900e-03f, -3.408432658e-02f, -7.992925296e-03f, +3.694535030e-02f, +1.865448932e-02f, -2.895300188e-02f, -2.246557005e-02f, +1.576606531e-02f, +1.755501285e-02f, -5.345313722e-03f, -8.887369558e-03f, +9.047221591e-04f, +2.459146683e-03f, -2.941732022e-04f,
+    /* 20, 5 */ +3.670219514e-04f, +3.022497230e-03f, -3.133648777e-03f, -1.054443774e-02f, +4.134948499e-03f, +2.263196075e-02f, +5.591264205e-04f, -3.345595810e-02f, -1.120042307e-02f, +3.553672638e-02f, +2.167350137e-02f, -2.706749712e-02f, -2.462477986e-02f, +1.402847243e-02f, +1.871398575e-02f, -4.226473266e-03f, -9.355341791e-03f, +4.367425848e-04f, +2.616713456e-03f, -2.461206998e-04f,
+    /* 20, 6 */ +2.234691091e-04f, +3.036236900e-03f, -2.469443903e-03f, -1.057347601e-02f, +2.595553432e-03f, +2.240006745e-02f, +3.085080219e-03f, -3.256328476e-02f, -1.428673893e-02f, +3.384115481e-02f, +2.452951370e-02f, -2.493516141e-02f, -2.661968788e-02f, +1.212161795e-02f, +1.975009719e-02f, -3.014219819e-03f, -9.758608118e-03f, -7.368451392e-05f, +2.754492916e-03f, -1.837671888e-04f,
+    /* 20, 7 */ +9.688872179e-05f, +3.013112613e-03f, -1.826068782e-03f, -1.050339555e-02f, +1.099226216e-03f, +2.198577609e-02f, +5.522941542e-03f, -3.141827834e-02f, -1.722639627e-02f, +3.187388359e-02f, +2.719713425e-02f, -2.257179274e-02f, -2.842956063e-02f, +1.005835593e-02f, +2.064933490e-02f, -1.716337171e-03f, -1.008928035e-02f, -6.235305950e-04f, +2.868852533e-03f, -1.062635081e-04f,
+    /* 20, 8 */ -1.289664191e-05f, +2.956215411e-03f, -1.209105881e-03f, -1.033987080e-02f, -3.418102460e-04f, +2.139858161e-02f, +7.853344535e-03f, -3.003502508e-02f, -1.999546871e-02f, +2.965257519e-02f, +2.965257519e-02f, -1.999546871e-02f, -3.003502508e-02f, +7.853344535e-03f, +2.139858161e-02f, -3.418102460e-04f, -1.033987080e-02f, -1.209105881e-03f, +2.956215411e-03f, -1.289664191e-05f,
+    /* 20, 9 */ -1.062635081e-04f, +2.868852533e-03f, -6.235305950e-04f, -1.008928035e-02f, -1.716337171e-03f, +2.064933490e-02f, +1.005835593e-02f, -2.842956063e-02f, -2.257179274e-02f, +2.719713425e-02f, +3.187388359e-02f, -1.722639627e-02f, -3.141827834e-02f, +5.522941542e-03f, +2.198577609e-02f, +1.099226216e-03f, -1.050339555e-02f, -1.826068782e-03f, +3.013112613e-03f, +9.688872179e-05f,
+    /* 20,10 */ -1.837671888e-04f, +2.754492916e-03f, -7.368451392e-05f, -9.758608118e-03f, -3.014219819e-03f, +1.975009719e-02f, +1.212161795e-02f, -2.661968788e-02f, -2.493516141e-02f, +2.452951370e-02f, +3.384115481e-02f, -1.428673893e-02f, -3.256328476e-02f, +3.085080219e-03f, +2.240006745e-02f, +2.595553432e-03f, -1.057347601e-02f, -2.469443903e-03f, +3.036236900e-03f, +2.234691091e-04f,
+    /* 20,11 */ -2.461206998e-04f, +2.616713456e-03f, +4.367425848e-04f, -9.355341791e-03f, -4.226473266e-03f, +1.871398575e-02f, +1.402847243e-02f, -2.462477986e-02f, -2.706749712e-02f, +2.167350137e-02f, +3.553672638e-02f, -1.120042307e-02f, -3.345595810e-02f, +5.591264205e-04f, +2.263196075e-02f, +4.134948499e-03f, -1.054443774e-02f, -3.133648777e-03f, +3.022497230e-03f, +3.670219514e-04f,
+    /* 20,12 */ -2.941732022e-04f, +2.459146683e-03f, +9.047221591e-04f, -8.887369558e-03f, -5.345313722e-03f, +1.755501285e-02f, +1.576606531e-02f, -2.246557005e-02f, -2.895300188e-02f, +1.865448932e-02f, +3.694535030e-02f, -7.992925296e-03f, -3.408432658e-02f, -2.034281900e-03f, +2.267345221e-02f, +5.704278009e-03f, -1.041140495e-02f, -3.812529364e-03f, +2.969073324e-03f, +5.275063595e-04f,
+    /* 20,13 */ -3.288882594e-04f, +2.285430476e-03f, +1.327887989e-03f, -8.362876507e-03f, -6.364195274e-03f, +1.628791973e-02f, +1.732343066e-02f, -2.016393226e-02f, -3.057828381e-02f, +1.549922824e-02f, +3.805434209e-02f, -4.691042688e-03f, -3.443867914e-02f, -4.673411391e-03f, +2.251815219e-02f, +7.289604703e-03f, -1.017038969e-02f, -4.499404245e-03f, +2.873469547e-03f, +7.046452899e-04f,
+    /* 20,14 */ -3.513221827e-04f, +2.099160368e-03f, +1.704529263e-03f, -7.790241855e-03f, -7.277832099e-03f, +1.492800767e-02f, +1.869155385e-02f, -1.774265256e-02f, -3.193245877e-02f, +1.223556951e-02f, +3.885370480e-02f, -1.322648265e-03f, -3.451169090e-02f, -7.335624654e-03f, +2.216139438e-02f, +8.876306372e-03f, -9.818374279e-03f, -5.187117330e-03f, +2.733567376e-03f, +8.979094201e-04f,
+    /* 20,15 */ +0.000000000e+00f, +1.903844954e-03f, +2.033576095e-03f, -7.177938171e-03f, -8.082206357e-03f, +1.349096787e-02f, +1.986341365e-02f, -1.522519578e-02f, -3.300722623e-02f, +8.892197588e-03f, +3.933622696e-02f, +2.083565903e-03f, -3.429852636e-02f, -9.997584470e-03f, +2.160032962e-02f, +1.044920601e-02f, -9.353385898e-03f, -5.868098774e-03f, +2.547675666e-03f, +1.499435496e-03f,
+    /* 20, 0 */ -3.735125865e-04f, +2.550984103e-03f, -2.725752467e-05f, -1.024966855e-02f, +8.379667777e-04f, +2.252874535e-02f, -1.249359695e-03f, -3.448318510e-02f, +6.071698875e-04f, +3.949755319e-02f, +6.071698875e-04f, -3.448318510e-02f, -1.249359695e-03f, +2.252874535e-02f, +8.379667777e-04f, -1.024966855e-02f, -2.725752467e-05f, +2.565652055e-03f, -3.735125865e-04f, +0.000000000e+00f,
+    /* 20, 1 */ -3.929324583e-04f, +2.236335973e-03f, +5.288730096e-04f, -9.916240655e-03f, -7.235223044e-04f, +2.212021870e-02f, +1.557339330e-03f, -3.412515163e-02f, -3.088657053e-03f, +3.930609269e-02f, +4.328121901e-03f, -3.450613681e-02f, -4.117983282e-03f, +2.271744325e-02f, +2.467540325e-03f, -1.048404473e-02f, -6.307983207e-04f, +2.729031078e-03f, -3.387491377e-04f, +0.000000000e+00f,
+    /* 20, 2 */ +0.000000000e+00f, +1.931175707e-03f, +1.033703668e-03f, -9.493276252e-03f, -2.202626937e-03f, +2.150371837e-02f, +4.274418757e-03f, -3.344119198e-02f, -6.721842627e-03f, +3.873376177e-02f, +8.036125742e-03f, -3.418837690e-02f, -7.019484999e-03f, +2.267661502e-02f, +4.149462009e-03f, -1.061062992e-02f, -1.276919763e-03f, +2.866023275e-03f, -2.870815261e-04f, +0.000000000e+00f,
+    /* 20, 3 */ +0.000000000e+00f, +1.638662038e-03f, +1.484286050e-03f, -8.990907936e-03f, -3.586602863e-03f, +2.069305390e-02f, +6.875821908e-03f, -3.244383298e-02f, -1.025584288e-02f, +3.778668841e-02f, +1.169297592e-02f, -3.352791602e-02f, -9.923776326e-03f, +2.239890298e-02f, +5.866701604e-03f, -1.062161571e-02f, -1.959867511e-03f, +2.971909246e-03f, -2.170808154e-04f, +0.000000000e+00f,
+    /* 20, 4 */ +0.000000000e+00f, +1.361489293e-03f, +1.878600735e-03f, -8.419725702e-03f, -4.864357078e-03f, +1.970376789e-02f, +9.337391966e-03f, -3.114878076e-02f, -1.365548733e-02f, +3.647500669e-02f, +1.526076366e-02f, -3.252647846e-02f, -1.280005487e-02f, +2.187944695e-02f, +7.601099328e-03f, -1.051027343e-02f, -2.672992279e-03f, +3.042103091e-03f, -1.274866066e-04f, +0.000000000e+00f,
+    /* 20, 5 */ +0.000000000e+00f, +1.101888322e-03f, +2.215532402e-03f, -7.790617836e-03f, -6.026519023e-03f, +1.855289977e-02f, +1.163710530e-02f, -2.957469445e-02f, -1.688736106e-02f, +3.481273909e-02f, +1.870230489e-02f, -3.118953221e-02f, -1.561714772e-02f, +2.111601531e-02f, +9.333550613e-03f, -1.027109466e-02f, -3.408794343e-03f, +3.072235528e-03f, -1.724424543e-05f, +0.000000000e+00f,
+    /* 20, 6 */ +0.000000000e+00f, +8.616336525e-04f, +2.494833248e-03f, -7.114614810e-03f, -7.065487327e-03f, +1.725873795e-02f, +1.375527431e-02f, -2.774292839e-02f, -1.992016339e-02f, +3.281763375e-02f, +2.198156213e-02f, -2.952627516e-02f, -1.834386597e-02f, +2.010910684e-02f, +1.104420948e-02f, -9.899921148e-03f, -4.158982805e-03f, +3.058238443e-03f, +1.144582079e-04f, +0.000000000e+00f,
+    /* 20, 7 */ +0.000000000e+00f, +6.420563626e-04f, +2.717075783e-03f, -6.402738187e-03f, -7.975452307e-03f, +1.584056403e-02f, +1.567471786e-02f, -2.567724669e-02f, -2.272503888e-02f, +3.051095862e-02f, +2.506405514e-02f, -2.754957697e-02f, -2.094936609e-02f, +1.886202143e-02f, +1.271270843e-02f, -9.394061811e-03f, -4.914549404e-03f, +2.996429606e-03f, +2.681538305e-04f, +0.000000000e+00f,
+    /* 20, 8 */ +0.000000000e+00f, +4.440621234e-04f, +2.883596201e-03f, -5.665856445e-03f, -8.752394750e-03f, +1.431839236e-02f, +1.738089791e-02f, -2.340351394e-02f, -2.527587699e-02f, +2.791725525e-02f, +2.791725525e-02f, -2.527587699e-02f, -2.340351394e-02f, +1.738089791e-02f, +1.431839236e-02f, -8.752394750e-03f, -5.665856445e-03f, +2.883596201e-03f, +4.440621234e-04f, +0.000000000e+00f,
+    /* 20, 9 */ +0.000000000e+00f, +2.681538305e-04f, +2.996429606e-03f, -4.914549404e-03f, -9.394061811e-03f, +1.271270843e-02f, +1.886202143e-02f, -2.094936609e-02f, -2.754957697e-02f, +2.506405514e-02f, +3.051095862e-02f, -2.272503888e-02f, -2.567724669e-02f, +1.567471786e-02f, +1.584056403e-02f, -7.975452307e-03f, -6.402738187e-03f, +2.717075783e-03f, +6.420563626e-04f, +0.000000000e+00f,
+    /* 20,10 */ +0.000000000e+00f, +1.144582079e-04f, +3.058238443e-03f, -4.158982805e-03f, -9.899921148e-03f, +1.104420948e-02f, +2.010910684e-02f, -1.834386597e-02f, -2.952627516e-02f, +2.198156213e-02f, +3.281763375e-02f, -1.992016339e-02f, -2.774292839e-02f, +1.375527431e-02f, +1.725873795e-02f, -7.065487327e-03f, -7.114614810e-03f, +2.494833248e-03f, +8.616336525e-04f, +0.000000000e+00f,
+    /* 20,11 */ +0.000000000e+00f, -1.724424543e-05f, +3.072235528e-03f, -3.408794343e-03f, -1.027109466e-02f, +9.333550613e-03f, +2.111601531e-02f, -1.561714772e-02f, -3.118953221e-02f, +1.870230489e-02f, +3.481273909e-02f, -1.688736106e-02f, -2.957469445e-02f, +1.163710530e-02f, +1.855289977e-02f, -6.026519023e-03f, -7.790617836e-03f, +2.215532402e-03f, +1.101888322e-03f, +0.000000000e+00f,
+    /* 20,12 */ +0.000000000e+00f, -1.274866066e-04f, +3.042103091e-03f, -2.672992279e-03f, -1.051027343e-02f, +7.601099328e-03f, +2.187944695e-02f, -1.280005487e-02f, -3.252647846e-02f, +1.526076366e-02f, +3.647500669e-02f, -1.365548733e-02f, -3.114878076e-02f, +9.337391966e-03f, +1.970376789e-02f, -4.864357078e-03f, -8.419725702e-03f, +1.878600735e-03f, +1.361489293e-03f, +0.000000000e+00f,
+    /* 20,13 */ +0.000000000e+00f, -2.170808154e-04f, +2.971909246e-03f, -1.959867511e-03f, -1.062161571e-02f, +5.866701604e-03f, +2.239890298e-02f, -9.923776326e-03f, -3.352791602e-02f, +1.169297592e-02f, +3.778668841e-02f, -1.025584288e-02f, -3.244383298e-02f, +6.875821908e-03f, +2.069305390e-02f, -3.586602863e-03f, -8.990907936e-03f, +1.484286050e-03f, +1.638662038e-03f, +0.000000000e+00f,
+    /* 20,14 */ +0.000000000e+00f, -2.870815261e-04f, +2.866023275e-03f, -1.276919763e-03f, -1.061062992e-02f, +4.149462009e-03f, +2.267661502e-02f, -7.019484999e-03f, -3.418837690e-02f, +8.036125742e-03f, +3.873376177e-02f, -6.721842627e-03f, -3.344119198e-02f, +4.274418757e-03f, +2.150371837e-02f, -2.202626937e-03f, -9.493276252e-03f, +1.033703668e-03f, +1.931175707e-03f, +0.000000000e+00f,
+    /* 20,15 */ +0.000000000e+00f, -3.387491377e-04f, +2.729031078e-03f, -6.307983207e-04f, -1.048404473e-02f, +2.467540325e-03f, +2.271744325e-02f, -4.117983282e-03f, -3.450613681e-02f, +4.328121901e-03f, +3.930609269e-02f, -3.088657053e-03f, -3.412515163e-02f, +1.557339330e-03f, +2.212021870e-02f, -7.235223044e-04f, -9.916240655e-03f, +5.288730096e-04f, +2.236335973e-03f, -3.929324583e-04f,
+    /* 16, 0 */ +3.044394378e-03f, -5.755865016e-03f, -7.644875237e-03f, +1.825808857e-02f, +9.222448502e-03f, -3.286388427e-02f, -4.241838036e-03f, +3.949755319e-02f, -4.241838036e-03f, -3.286388427e-02f, +9.222448502e-03f, +1.825808857e-02f, -7.644875237e-03f, -5.755865016e-03f, +3.044394378e-03f, -1.466795211e-05f,
+    /* 16, 1 */ +3.096598285e-03f, -4.938670705e-03f, -8.543525001e-03f, +1.681174815e-02f, +1.171692718e-02f, -3.156650717e-02f, -8.140229219e-03f, +3.927338559e-02f, -2.566011090e-04f, -3.380519836e-02f, +6.539747546e-03f, +1.954192550e-02f, -6.591652894e-03f, -6.554797296e-03f, +2.932700428e-03f, +1.424716020e-04f,
+    /* 16, 2 */ +3.093580763e-03f, -4.116215273e-03f, -9.283856280e-03f, +1.522768985e-02f, +1.399813452e-02f, -2.993548791e-02f, -1.190603152e-02f, +3.860369285e-02f, +3.768233493e-03f, -3.437220120e-02f, +3.697060454e-03f, +2.063975168e-02f, -5.390052618e-03f, -7.321916780e-03f, +2.757987679e-03f, +3.278051009e-04f,
+    /* 16, 3 */ +3.040228116e-03f, -3.300778044e-03f, -9.864516741e-03f, +1.353158972e-02f, +1.604446323e-02f, -2.799705310e-02f, -1.549559239e-02f, +3.749686691e-02f, +7.784523662e-03f, -3.455113173e-02f, +7.255039591e-04f, +2.152972014e-02f, -4.048737024e-03f, -8.043312786e-03f, +2.517583336e-03f, +5.415628140e-04f,
+    /* 16, 4 */ +2.941918573e-03f, -2.503765206e-03f, -1.028645874e-02f, +1.174962597e-02f, +1.783794825e-02f, -2.578082191e-02f, -1.886790220e-02f, +3.596676747e-02f, +1.174386090e-02f, -3.433297304e-02f, -2.341279491e-03f, +2.219201396e-02f, -2.578818314e-03f, -8.704920467e-03f, +2.209778110e-03f, +1.246855370e-03f,
+    /* 16, 5 */ +2.804395319e-03f, -1.735580001e-03f, -1.055281940e-02f, +9.908096520e-03f, +1.936441115e-02f, -2.331935318e-02f, -2.198510572e-02f, +3.403253365e-02f, +1.559820593e-02f, -3.371364718e-02f, -5.467520855e-03f, +2.260919785e-02f, -9.938011251e-04f, -9.292739628e-03f, +1.833922789e-03f, +1.492594630e-03f,
+    /* 16, 6 */ +2.633640678e-03f, -1.005515791e-03f, -1.066877263e-02f, +8.033047542e-03f, +2.061354789e-02f, -2.064765983e-02f, -2.481296600e-02f, +3.171832409e-02f, +1.930052332e-02f, -3.269414425e-02f, -8.615762914e-03f, +2.276654580e-02f, +6.905139302e-04f, -9.793063512e-03f, +1.390511455e-03f, +1.739628231e-03f,
+    /* 16, 7 */ +2.435753603e-03f, -3.216727033e-04f, -1.064135670e-02f, +6.149918447e-03f, +2.157895980e-02f, -1.780269814e-02f, -2.732127429e-02f, +2.905298940e-02f, +2.280540611e-02f, -3.128058296e-02f, -1.174733238e-02f, +2.265233903e-02f, +2.456165958e-03f, -1.019271416e-02f, +8.812491435e-04f, +1.982865330e-03f,
+    /* 16, 8 */ +2.216832517e-03f, +3.091018830e-04f, -1.047928070e-02f, +4.283208005e-03f, +2.225812888e-02f, -1.482283947e-02f, -2.948420097e-02f, +2.606968157e-02f, +2.606968157e-02f, -2.948420097e-02f, -1.482283947e-02f, +2.225812888e-02f, +4.283208005e-03f, -1.047928070e-02f, +3.091018830e-04f, +2.216832517e-03f,
+    /* 16, 9 */ +1.982865330e-03f, +8.812491435e-04f, -1.019271416e-02f, +2.456165958e-03f, +2.265233903e-02f, -1.174733238e-02f, -3.128058296e-02f, +2.280540611e-02f, +2.905298940e-02f, -2.732127429e-02f, -1.780269814e-02f, +2.157895980e-02f, +6.149918447e-03f, -1.064135670e-02f, -3.216727033e-04f, +2.435753603e-03f,
+    /* 16,10 */ +1.739628231e-03f, +1.390511455e-03f, -9.793063512e-03f, +6.905139302e-04f, +2.276654580e-02f, -8.615762914e-03f, -3.269414425e-02f, +1.930052332e-02f, +3.171832409e-02f, -2.481296600e-02f, -2.064765983e-02f, +2.061354789e-02f, +8.033047542e-03f, -1.066877263e-02f, -1.005515791e-03f, +2.633640678e-03f,
+    /* 16,11 */ +1.492594630e-03f, +1.833922789e-03f, -9.292739628e-03f, -9.938011251e-04f, +2.260919785e-02f, -5.467520855e-03f, -3.371364718e-02f, +1.559820593e-02f, +3.403253365e-02f, -2.198510572e-02f, -2.331935318e-02f, +1.936441115e-02f, +9.908096520e-03f, -1.055281940e-02f, -1.735580001e-03f, +2.804395319e-03f,
+    /* 16,12 */ +1.246855370e-03f, +2.209778110e-03f, -8.704920467e-03f, -2.578818314e-03f, +2.219201396e-02f, -2.341279491e-03f, -3.433297304e-02f, +1.174386090e-02f, +3.596676747e-02f, -1.886790220e-02f, -2.578082191e-02f, +1.783794825e-02f, +1.174962597e-02f, -1.028645874e-02f, -2.503765206e-03f, +2.941918573e-03f,
+    /* 16,13 */ +5.415628140e-04f, +2.517583336e-03f, -8.043312786e-03f, -4.048737024e-03f, +2.152972014e-02f, +7.255039591e-04f, -3.455113173e-02f, +7.784523662e-03f, +3.749686691e-02f, -1.549559239e-02f, -2.799705310e-02f, +1.604446323e-02f, +1.353158972e-02f, -9.864516741e-03f, -3.300778044e-03f, +3.040228116e-03f,
+    /* 16,14 */ +3.278051009e-04f, +2.757987679e-03f, -7.321916780e-03f, -5.390052618e-03f, +2.063975168e-02f, +3.697060454e-03f, -3.437220120e-02f, +3.768233493e-03f, +3.860369285e-02f, -1.190603152e-02f, -2.993548791e-02f, +1.399813452e-02f, +1.522768985e-02f, -9.283856280e-03f, -4.116215273e-03f, +3.093580763e-03f,
+    /* 16,15 */ +1.424716020e-04f, +2.932700428e-03f, -6.554797296e-03f, -6.591652894e-03f, +1.954192550e-02f, +6.539747546e-03f, -3.380519836e-02f, -2.566011090e-04f, +3.927338559e-02f, -8.140229219e-03f, -3.156650717e-02f, +1.171692718e-02f, +1.681174815e-02f, -8.543525001e-03f, -4.938670705e-03f, +3.096598285e-03f,
+    /* 16, 0 */ +2.106112688e-03f, -1.136549770e-04f, -1.070669430e-02f, +1.017472059e-02f, +1.725397418e-02f, -2.914959442e-02f, -8.963852042e-03f, +3.949755319e-02f, -8.963852042e-03f, -2.914959442e-02f, +1.725397418e-02f, +1.017472059e-02f, -1.070669430e-02f, -1.136549770e-04f, +2.106112688e-03f, +0.000000000e+00f,
+    /* 16, 1 */ +1.845338280e-03f, +5.473343456e-04f, -1.065891816e-02f, +8.155641030e-03f, +1.899089579e-02f, -2.691951328e-02f, -1.297236480e-02f, +3.923810803e-02f, -4.790894575e-03f, -3.103786392e-02f, +1.521305380e-02f, +1.215062389e-02f, -1.058864907e-02f, -8.385121370e-04f, +2.352913572e-03f, +0.000000000e+00f,
+    /* 16, 2 */ +1.577651889e-03f, +1.138027416e-03f, -1.045641117e-02f, +6.125232216e-03f, +2.040939526e-02f, -2.438627738e-02f, -1.676283724e-02f, +3.846353557e-02f, -5.100475811e-04f, -3.254996068e-02f, +1.288771825e-02f, +1.405076114e-02f, -1.029592106e-02f, -1.619065127e-03f, +2.578329862e-03f, +0.000000000e+00f,
+    /* 16, 3 */ +1.309641631e-03f, +1.653747621e-03f, -1.011218665e-02f, +4.114112388e-03f, +2.150028131e-02f, -2.159216402e-02f, -2.028541539e-02f, +3.718506562e-02f, +3.820010384e-03f, -3.365643786e-02f, +1.030255138e-02f, +1.584231759e-02f, -9.822158049e-03f, -2.445442331e-03f, +2.774741598e-03f, +0.000000000e+00f,
+    /* 16, 4 */ +5.462770218e-04f, +2.091544281e-03f, -9.640854940e-03f, +2.151223968e-03f, +2.225957955e-02f, -1.858235038e-02f, -2.349470263e-02f, +3.542121816e-02f, +8.139354376e-03f, -3.433331277e-02f, +7.486889709e-03f, +1.749279467e-02f, -9.163764446e-03f, -3.306153325e-03f, +2.934475195e-03f, -4.634120047e-04f,
+    /* 16, 5 */ +3.039101756e-04f, +2.450135010e-03f, -9.058284956e-03f, +2.634319572e-04f, +2.268843286e-02f, -1.540416082e-02f, -2.635039795e-02f, +3.319751225e-02f, +1.238771678e-02f, -3.456253827e-02f, +4.474489227e-03f, +1.897056596e-02f, -8.320109905e-03f, -4.188206830e-03f, +3.049970761e-03f, -4.400286560e-04f,
+    /* 16, 6 */ +9.722433303e-05f, +2.729819110e-03f, -8.381259809e-03f, -1.524826854e-03f, +2.279292110e-02f, -1.210629477e-02f, -2.881784707e-02f, +3.054606534e-02f, +1.650540309e-02f, -3.433238619e-02f, +1.303110212e-03f, +2.024543829e-02f, -7.293693549e-03f, -5.077265419e-03f, +3.113958519e-03f, -3.921992729e-04f,
+    /* 16, 7 */ -7.427658644e-05f, +2.932365266e-03f, -7.627133157e-03f, -3.191839567e-03f, +2.258380561e-02f, -8.738048497e-03f, -3.086849848e-02f, +2.750508980e-02f, +2.043420490e-02f, -3.363773532e-02f, -1.985974837e-03f, +2.128920837e-02f, -6.090258802e-03f, -5.957835775e-03f, +3.119640969e-03f, -3.169896142e-04f,
+    /* 16, 8 */ -2.117631665e-04f, +3.060877176e-03f, -6.813492559e-03f, -4.718854594e-03f, +2.207620481e-02f, -5.348543574e-03f, -3.248025769e-02f, +2.411829496e-02f, +2.411829496e-02f, -3.248025769e-02f, -5.348543574e-03f, +2.207620481e-02f, -4.718854594e-03f, -6.813492559e-03f, +3.060877176e-03f, -2.117631665e-04f,
+    /* 16, 9 */ -3.169896142e-04f, +3.119640969e-03f, -5.957835775e-03f, -6.090258802e-03f, +2.128920837e-02f, -1.985974837e-03f, -3.363773532e-02f, +2.043420490e-02f, +2.750508980e-02f, -3.086849848e-02f, -8.738048497e-03f, +2.258380561e-02f, -3.191839567e-03f, -7.627133157e-03f, +2.932365266e-03f, -7.427658644e-05f,
+    /* 16,10 */ -3.921992729e-04f, +3.113958519e-03f, -5.077265419e-03f, -7.293693549e-03f, +2.024543829e-02f, +1.303110212e-03f, -3.433238619e-02f, +1.650540309e-02f, +3.054606534e-02f, -2.881784707e-02f, -1.210629477e-02f, +2.279292110e-02f, -1.524826854e-03f, -8.381259809e-03f, +2.729819110e-03f, +9.722433303e-05f,
+    /* 16,11 */ -4.400286560e-04f, +3.049970761e-03f, -4.188206830e-03f, -8.320109905e-03f, +1.897056596e-02f, +4.474489227e-03f, -3.456253827e-02f, +1.238771678e-02f, +3.319751225e-02f, -2.635039795e-02f, -1.540416082e-02f, +2.268843286e-02f, +2.634319572e-04f, -9.058284956e-03f, +2.450135010e-03f, +3.039101756e-04f,
+    /* 16,12 */ -4.634120047e-04f, +2.934475195e-03f, -3.306153325e-03f, -9.163764446e-03f, +1.749279467e-02f, +7.486889709e-03f, -3.433331277e-02f, +8.139354376e-03f, +3.542121816e-02f, -2.349470263e-02f, -1.858235038e-02f, +2.225957955e-02f, +2.151223968e-03f, -9.640854940e-03f, +2.091544281e-03f, +5.462770218e-04f,
+    /* 16,13 */ +0.000000000e+00f, +2.774741598e-03f, -2.445442331e-03f, -9.822158049e-03f, +1.584231759e-02f, +1.030255138e-02f, -3.365643786e-02f, +3.820010384e-03f, +3.718506562e-02f, -2.028541539e-02f, -2.159216402e-02f, +2.150028131e-02f, +4.114112388e-03f, -1.011218665e-02f, +1.653747621e-03f, +1.309641631e-03f,
+    /* 16,14 */ +0.000000000e+00f, +2.578329862e-03f, -1.619065127e-03f, -1.029592106e-02f, +1.405076114e-02f, +1.288771825e-02f, -3.254996068e-02f, -5.100475811e-04f, +3.846353557e-02f, -1.676283724e-02f, -2.438627738e-02f, +2.040939526e-02f, +6.125232216e-03f, -1.045641117e-02f, +1.138027416e-03f, +1.577651889e-03f,
+    /* 16,15 */ +0.000000000e+00f, +2.352913572e-03f, -8.385121370e-04f, -1.058864907e-02f, +1.215062389e-02f, +1.521305380e-02f, -3.103786392e-02f, -4.790894575e-03f, +3.923810803e-02f, -1.297236480e-02f, -2.691951328e-02f, +1.899089579e-02f, +8.155641030e-03f, -1.065891816e-02f, +5.473343456e-04f, +1.845338280e-03f,
+    /* 16, 0 */ -2.517634455e-04f, +5.956310854e-03f, -8.647029609e-03f, +1.153545125e-03f, +2.185732832e-02f, -2.369518339e-02f, -1.347728153e-02f, +3.949755319e-02f, -1.347728153e-02f, -2.369518339e-02f, +2.185732832e-02f, +1.153545125e-03f, -8.647029609e-03f, +2.914798040e-03f, -2.517634455e-04f, +0.000000000e+00f,
+    /* 16, 1 */ -3.647589216e-04f, +5.559366521e-03f, -7.860683839e-03f, -8.150822761e-04f, +2.252089097e-02f, -2.063007883e-02f, -1.749200540e-02f, +3.920026256e-02f, -9.205150933e-03f, -2.647415600e-02f, +2.081322447e-02f, +3.223212212e-03f, -9.337661142e-03f, +2.677108373e-03f, -9.939782505e-05f, +0.000000000e+00f,
+    /* 16, 2 */ -4.414886472e-04f, +5.113535158e-03f, -7.000222932e-03f, -2.654422569e-03f, +2.280787202e-02f, -1.733513495e-02f, -2.118899605e-02f, +3.831333038e-02f, -4.740975303e-03f, -2.891426752e-02f, +1.939126736e-02f, +5.362271050e-03f, -9.911447152e-03f, +2.349799583e-03f, +9.477253367e-05f, +0.000000000e+00f,
+    /* 16, 3 */ -4.855802427e-04f, +4.633347213e-03f, -6.087250386e-03f, -4.340001532e-03f, +2.272858071e-02f, -1.386914009e-02f, -2.451395150e-02f, +3.685148678e-02f, -1.540603716e-04f, -3.096737610e-02f, +1.760097190e-02f, +7.536131493e-03f, -1.034820384e-02f, +1.931270179e-03f, +3.323676319e-04f, +0.000000000e+00f,
+    /* 16, 4 */ +0.000000000e+00f, +4.132457962e-03f, -5.142935987e-03f, -5.851401101e-03f, +2.229926864e-02f, -1.029228788e-02f, -2.741946400e-02f, +3.483898696e-02f, +4.483517704e-03f, -3.259088680e-02f, +1.545873378e-02f, +9.707799030e-03f, -1.062918096e-02f, +1.421916825e-03f, +6.140535745e-04f, +0.000000000e+00f,
+    /* 16, 5 */ +0.000000000e+00f, +3.623457200e-03f, -4.187611099e-03f, -7.172450485e-03f, +2.154162444e-02f, -6.665085054e-03f, -2.986575546e-02f, +3.230917458e-02f, +9.098146940e-03f, -3.374862716e-02f, +1.298775300e-02f, +1.183848413e-02f, -1.073754388e-02f, +8.242784646e-04f, +9.394126333e-04f, +0.000000000e+00f,
+    /* 16, 6 */ +0.000000000e+00f, +3.117716971e-03f, -3.240404345e-03f, -8.291325326e-03f, +2.048218318e-02f, -3.047278250e-03f, -3.182126614e-02f, +2.930388237e-02f, +1.361595241e-02f, -3.441162052e-02f, +1.021782484e-02f, +1.388827332e-02f, -1.065884073e-02f, +1.431392807e-04f, +1.306826648e-03f, +0.000000000e+00f,
+    /* 16, 7 */ +0.000000000e+00f, +2.625277441e-03f, -2.318923448e-03f, -9.200556695e-03f, +1.915166452e-02f, +5.031802154e-04f, -3.326308735e-02f, +2.587268140e-02f, +1.796408380e-02f, -3.455874049e-02f, +7.184998851e-03f, +1.581685040e-02f, -1.038144369e-02f, -6.144144569e-04f, +1.713377737e-03f, +0.000000000e+00f,
+    /* 16, 8 */ +0.000000000e+00f, +2.154770219e-03f, -1.438987736e-03f, -9.896953513e-03f, +1.758425506e-02f, +3.931108902e-03f, -3.417723209e-02f, +2.207199352e-02f, +2.207199352e-02f, -3.417723209e-02f, +3.931108902e-03f, +1.758425506e-02f, -9.896953513e-03f, -1.438987736e-03f, +2.154770219e-03f, +0.000000000e+00f,
+    /* 16, 9 */ +0.000000000e+00f, +1.713377737e-03f, -6.144144569e-04f, -1.038144369e-02f, +1.581685040e-02f, +7.184998851e-03f, -3.455874049e-02f, +1.796408380e-02f, +2.587268140e-02f, -3.326308735e-02f, +5.031802154e-04f, +1.915166452e-02f, -9.200556695e-03f, -2.318923448e-03f, +2.625277441e-03f, +0.000000000e+00f,
+    /* 16,10 */ +0.000000000e+00f, +1.306826648e-03f, +1.431392807e-04f, -1.065884073e-02f, +1.388827332e-02f, +1.021782484e-02f, -3.441162052e-02f, +1.361595241e-02f, +2.930388237e-02f, -3.182126614e-02f, -3.047278250e-03f, +2.048218318e-02f, -8.291325326e-03f, -3.240404345e-03f, +3.117716971e-03f, +0.000000000e+00f,
+    /* 16,11 */ +0.000000000e+00f, +9.394126333e-04f, +8.242784646e-04f, -1.073754388e-02f, +1.183848413e-02f, +1.298775300e-02f, -3.374862716e-02f, +9.098146940e-03f, +3.230917458e-02f, -2.986575546e-02f, -6.665085054e-03f, +2.154162444e-02f, -7.172450485e-03f, -4.187611099e-03f, +3.623457200e-03f, +0.000000000e+00f,
+    /* 16,12 */ +0.000000000e+00f, +6.140535745e-04f, +1.421916825e-03f, -1.062918096e-02f, +9.707799030e-03f, +1.545873378e-02f, -3.259088680e-02f, +4.483517704e-03f, +3.483898696e-02f, -2.741946400e-02f, -1.029228788e-02f, +2.229926864e-02f, -5.851401101e-03f, -5.142935987e-03f, +4.132457962e-03f, +0.000000000e+00f,
+    /* 16,13 */ +0.000000000e+00f, +3.323676319e-04f, +1.931270179e-03f, -1.034820384e-02f, +7.536131493e-03f, +1.760097190e-02f, -3.096737610e-02f, -1.540603716e-04f, +3.685148678e-02f, -2.451395150e-02f, -1.386914009e-02f, +2.272858071e-02f, -4.340001532e-03f, -6.087250386e-03f, +4.633347213e-03f, -4.855802427e-04f,
+    /* 16,14 */ +0.000000000e+00f, +9.477253367e-05f, +2.349799583e-03f, -9.911447152e-03f, +5.362271050e-03f, +1.939126736e-02f, -2.891426752e-02f, -4.740975303e-03f, +3.831333038e-02f, -2.118899605e-02f, -1.733513495e-02f, +2.280787202e-02f, -2.654422569e-03f, -7.000222932e-03f, +5.113535158e-03f, -4.414886472e-04f,
+    /* 16,15 */ +0.000000000e+00f, -9.939782505e-05f, +2.677108373e-03f, -9.337661142e-03f, +3.223212212e-03f, +2.081322447e-02f, -2.647415600e-02f, -9.205150933e-03f, +3.920026256e-02f, -1.749200540e-02f, -2.063007883e-02f, +2.252089097e-02f, -8.150822761e-04f, -7.860683839e-03f, +5.559366521e-03f, -3.647589216e-04f,
+    /* 12, 0 */ -3.924537125e-03f, -6.177283790e-03f, +2.270137823e-02f, -1.696686670e-02f, -1.770529738e-02f, +3.949755319e-02f, -1.770529738e-02f, -1.696686670e-02f, +2.270137823e-02f, -6.177283790e-03f, -3.924537125e-03f, +2.689823824e-03f,
+    /* 12, 1 */ -2.918444824e-03f, -7.533875928e-03f, +2.217225575e-02f, -1.325050127e-02f, -2.161377319e-02f, +3.915985190e-02f, -1.343239533e-02f, -2.049610855e-02f, +2.283609035e-02f, -4.600700986e-03f, -4.945631587e-03f, +2.897838580e-03f,
+    /* 12, 2 */ -1.948111766e-03f, -8.657444743e-03f, +2.127619260e-02f, -9.420045488e-03f, -2.509275167e-02f, +3.815312062e-02f, -8.867972963e-03f, -2.376686078e-02f, +2.255583139e-02f, -2.822790564e-03f, -5.958903400e-03f, +3.051876120e-03f,
+    /* 12, 3 */ -1.031879811e-03f, -9.540571177e-03f, +2.004673480e-02f, -5.548699503e-03f, -2.808617760e-02f, +3.649634673e-02f, -4.091413649e-03f, -2.671092541e-02f, +2.184766025e-02f, -8.677312765e-04f, -6.939883353e-03f, +3.140832095e-03f,
+    /* 12, 4 */ -1.854082964e-04f, -1.018130776e-02f, +1.852257236e-02f, -1.708362919e-03f, -3.054799363e-02f, +3.422074403e-02f, +8.129280323e-04f, -2.926474106e-02f, +2.070682375e-02f, +1.235031889e-03f, -7.862950435e-03f, +3.154329873e-03f,
+    /* 12, 5 */ +5.785109863e-04f, -1.058292035e-02f, +1.674653148e-02f, +2.031769072e-03f, -3.244290444e-02f, +3.136911490e-02f, +5.757350815e-03f, -3.137078637e-02f, +1.913716500e-02f, +3.451172065e-03f, -8.701884951e-03f, +3.083080347e-03f,
+    /* 12, 6 */ +1.250056620e-03f, -1.075352499e-02f, +1.476451598e-02f, +5.606517218e-03f, -3.374690984e-02f, +2.799497691e-02f, +1.065251585e-02f, -3.297888633e-02f, +1.715135739e-02f, +5.741996578e-03f, -9.430471381e-03f, +2.919238566e-03f,
+    /* 12, 7 */ +1.822400383e-03f, -1.070563332e-02f, +1.262442359e-02f, +8.955911342e-03f, -3.444759838e-02f, +2.416147295e-02f, +1.540920462e-02f, -3.404739100e-02f, +1.477095353e-02f, +8.065088216e-03f, -1.002313834e-02f, +2.656746896e-03f,
+    /* 12, 8 */ +2.291654178e-03f, -1.045562141e-02f, +1.037506188e-02f, +1.202624229e-02f, -3.454419870e-02f, +1.994008861e-02f, +1.994008861e-02f, -3.454419870e-02f, +1.202624229e-02f, +1.037506188e-02f, -1.045562141e-02f, +2.291654178e-03f,
+    /* 12, 9 */ +2.656746896e-03f, -1.002313834e-02f, +8.065088216e-03f, +1.477095353e-02f, -3.404739100e-02f, +1.540920462e-02f, +2.416147295e-02f, -3.444759838e-02f, +8.955911342e-03f, +1.262442359e-02f, -1.070563332e-02f, +1.822400383e-03f,
+    /* 12,10 */ +2.919238566e-03f, -9.430471381e-03f, +5.741996578e-03f, +1.715135739e-02f, -3.297888633e-02f, +1.065251585e-02f, +2.799497691e-02f, -3.374690984e-02f, +5.606517218e-03f, +1.476451598e-02f, -1.075352499e-02f, +1.250056620e-03f,
+    /* 12,11 */ +3.083080347e-03f, -8.701884951e-03f, +3.451172065e-03f, +1.913716500e-02f, -3.137078637e-02f, +5.757350815e-03f, +3.136911490e-02f, -3.244290444e-02f, +2.031769072e-03f, +1.674653148e-02f, -1.058292035e-02f, +5.785109863e-04f,
+    /* 12,12 */ +3.154329873e-03f, -7.862950435e-03f, +1.235031889e-03f, +2.070682375e-02f, -2.926474106e-02f, +8.129280323e-04f, +3.422074403e-02f, -3.054799363e-02f, -1.708362919e-03f, +1.852257236e-02f, -1.018130776e-02f, -1.854082964e-04f,
+    /* 12,13 */ +3.140832095e-03f, -6.939883353e-03f, -8.677312765e-04f, +2.184766025e-02f, -2.671092541e-02f, -4.091413649e-03f, +3.649634673e-02f, -2.808617760e-02f, -5.548699503e-03f, +2.004673480e-02f, -9.540571177e-03f, -1.031879811e-03f,
+    /* 12,14 */ +3.051876120e-03f, -5.958903400e-03f, -2.822790564e-03f, +2.255583139e-02f, -2.376686078e-02f, -8.867972963e-03f, +3.815312062e-02f, -2.509275167e-02f, -9.420045488e-03f, +2.127619260e-02f, -8.657444743e-03f, -1.948111766e-03f,
+    /* 12,15 */ +2.897838580e-03f, -4.945631587e-03f, -4.600700986e-03f, +2.283609035e-02f, -2.049610855e-02f, -1.343239533e-02f, +3.915985190e-02f, -2.161377319e-02f, -1.325050127e-02f, +2.217225575e-02f, -7.533875928e-03f, -2.918444824e-03f,
+    /* 12, 0 */ +5.529156756e-04f, -1.017944650e-02f, +2.010395691e-02f, -9.501724583e-03f, -2.157725737e-02f, +3.949755319e-02f, -2.157725737e-02f, -9.501724583e-03f, +2.010395691e-02f, -1.017944650e-02f, +5.529156756e-04f, +9.520529918e-04f,
+    /* 12, 1 */ +1.269440891e-03f, -1.060857725e-02f, +1.848426560e-02f, -5.389674050e-03f, -2.526172923e-02f, +3.911687897e-02f, -1.740939271e-02f, -1.356576871e-02f, +2.138958875e-02f, -9.480008902e-03f, -2.676127190e-04f, +1.273328470e-03f,
+    /* 12, 2 */ +1.873685854e-03f, -1.077705214e-02f, +1.658179711e-02f, -1.315683663e-03f, -2.839592801e-02f, +3.798295250e-02f, -1.283645305e-02f, -1.749413418e-02f, +2.229518867e-02f, -8.506812328e-03f, -1.180051037e-03f, +1.604489886e-03f,
+    /* 12, 3 */ +2.361120897e-03f, -1.070010905e-02f, +1.445171501e-02f, +2.637679549e-03f, -3.092573063e-02f, +3.611987567e-02f, -7.946589383e-03f, -2.119952675e-02f, +2.278144860e-02f, -7.263083188e-03f, -2.168530550e-03f, +1.934678236e-03f,
+    /* 12, 4 */ +2.730794315e-03f, -1.039785120e-02f, +1.215160004e-02f, +6.393108320e-03f, -3.281077803e-02f, +3.356720057e-02f, -2.835931904e-03f, -2.459705675e-02f, +2.281696739e-02f, -5.759053577e-03f, -3.213563229e-03f, +2.251787566e-03f,
+    /* 12, 5 */ +2.985073479e-03f, -9.894440683e-03f, +9.739988515e-03f, +9.880142532e-03f, -3.402514934e-02f, +3.037901891e-02f, +2.393471366e-03f, -2.760625942e-02f, +2.237934513e-02f, -4.012119167e-03f, -4.292316215e-03f, +2.542756696e-03f,
+    /* 12, 6 */ +3.129309714e-03f, -9.217233004e-03f, +7.274949975e-03f, +1.303654969e-02f, -3.455769209e-02f, +2.662271867e-02f, +7.635875993e-03f, -3.015305887e-02f, +2.145609570e-02f, -2.046814992e-03f, -5.379003980e-03f, +2.793918230e-03f,
+    /* 12, 7 */ +3.171441616e-03f, -8.395878746e-03f, +4.812738303e-03f, +1.580947051e-02f, -3.441200543e-02f, +2.237743869e-02f, +1.278417054e-02f, -3.217162603e-02f, +2.004534769e-02f, +1.053992035e-04f, -6.445394017e-03f, +2.991397563e-03f,
+    /* 12, 8 */ +3.121552430e-03f, -7.461418245e-03f, +2.406547485e-03f, +1.815630830e-02f, -3.360608252e-02f, +1.773225889e-02f, +1.773225889e-02f, -3.360608252e-02f, +1.815630830e-02f, +2.406547485e-03f, -7.461418245e-03f, +3.121552430e-03f,
+    /* 12, 9 */ +2.991397563e-03f, -6.445394017e-03f, +1.053992035e-04f, +2.004534769e-02f, -3.217162603e-02f, +1.278417054e-02f, +2.237743869e-02f, -3.441200543e-02f, +1.580947051e-02f, +4.812738303e-03f, -8.395878746e-03f, +3.171441616e-03f,
+    /* 12,10 */ +2.793918230e-03f, -5.379003980e-03f, -2.046814992e-03f, +2.145609570e-02f, -3.015305887e-02f, +7.635875993e-03f, +2.662271867e-02f, -3.455769209e-02f, +1.303654969e-02f, +7.274949975e-03f, -9.217233004e-03f, +3.129309714e-03f,
+    /* 12,11 */ +2.542756696e-03f, -4.292316215e-03f, -4.012119167e-03f, +2.237934513e-02f, -2.760625942e-02f, +2.393471366e-03f, +3.037901891e-02f, -3.402514934e-02f, +9.880142532e-03f, +9.739988515e-03f, -9.894440683e-03f, +2.985073479e-03f,
+    /* 12,12 */ +2.251787566e-03f, -3.213563229e-03f, -5.759053577e-03f, +2.281696739e-02f, -2.459705675e-02f, -2.835931904e-03f, +3.356720057e-02f, -3.281077803e-02f, +6.393108320e-03f, +1.215160004e-02f, -1.039785120e-02f, +2.730794315e-03f,
+    /* 12,13 */ +1.934678236e-03f, -2.168530550e-03f, -7.263083188e-03f, +2.278144860e-02f, -2.119952675e-02f, -7.946589383e-03f, +3.611987567e-02f, -3.092573063e-02f, +2.637679549e-03f, +1.445171501e-02f, -1.070010905e-02f, +2.361120897e-03f,
+    /* 12,14 */ +1.604489886e-03f, -1.180051037e-03f, -8.506812328e-03f, +2.229518867e-02f, -1.749413418e-02f, -1.283645305e-02f, +3.798295250e-02f, -2.839592801e-02f, -1.315683663e-03f, +1.658179711e-02f, -1.077705214e-02f, +1.873685854e-03f,
+    /* 12,15 */ +1.273328470e-03f, -2.676127190e-04f, -9.480008902e-03f, +2.138958875e-02f, -1.356576871e-02f, -1.740939271e-02f, +3.911687897e-02f, -2.526172923e-02f, -5.389674050e-03f, +1.848426560e-02f, -1.060857725e-02f, +1.269440891e-03f,
+
+    /* 24, 0 */ -8.820438069e-05f, -1.519461079e-04f, -2.301651496e-04f, -3.149320871e-04f, -3.945939739e-04f, -4.554410135e-04f, -4.841532882e-04f, -4.705408991e-04f, -4.099602091e-04f, -3.048100066e-04f, -1.646897470e-04f, -5.099007530e-06f, +1.551006323e-04f, +2.969416536e-04f, +4.046294158e-04f, +4.681429482e-04f, +4.846228261e-04f, +4.583040637e-04f, +3.990939388e-04f, +3.201968846e-04f, +2.353759082e-04f, +1.564712483e-04f, +9.167483068e-05f, +4.482688286e-05f,
+    /* 24, 1 */ -8.480575132e-05f, -1.474789784e-04f, -2.249812225e-04f, -3.096480504e-04f, -3.900204007e-04f, -4.524514078e-04f, -4.835165803e-04f, -4.727530367e-04f, -4.151145025e-04f, -3.125397891e-04f, -1.742016828e-04f, -1.529460870e-05f, +1.454387449e-04f, +2.889379628e-04f, +3.991236794e-04f, +4.655589110e-04f, +4.849233000e-04f, +4.610375470e-04f, +4.035168325e-04f, +3.254391996e-04f, +2.406110065e-04f, +1.610529558e-04f, +9.521673594e-05f, +4.721513201e-05f,
+    /* 24, 2 */ -8.147924507e-05f, -1.430712350e-04f, -2.198265592e-04f, -3.043479843e-04f, -3.853766873e-04f, -4.493383067e-04f, -4.827146831e-04f, -4.747797448e-04f, -4.200908527e-04f, -3.201278616e-04f, -1.836320864e-04f, -2.548296987e-05f, +1.357085413e-04f, +2.808022583e-04f, +3.934446700e-04f, +4.627886263e-04f, +4.850529052e-04f, +4.636385032e-04f, +4.078592000e-04f, +3.306557574e-04f, +2.458678944e-04f, +1.656897170e-04f, +9.882966748e-05f, +4.967415993e-05f,
+    /* 24, 3 */ -7.822510242e-05f, -1.387241832e-04f, -2.147035314e-04f, -2.990350629e-04f, -3.806663042e-04f, -4.461048161e-04f, -4.817496625e-04f, -4.766215175e-04f, -4.248879309e-04f, -3.275711800e-04f, -1.929766610e-04f, -3.565926997e-05f, +1.259145254e-04f, +2.725379532e-04f, +3.875941701e-04f, +4.598320457e-04f, +4.850099279e-04f, +4.661040260e-04f, +4.121175966e-04f, +3.358432542e-04f, +2.511439643e-04f, +1.703799499e-04f, +1.025131319e-04f, +5.220438912e-05f,
+    /* 24, 4 */ -7.504350274e-05f, -1.344390595e-04f, -2.096144489e-04f, -2.937124231e-04f, -3.758927218e-04f, -4.427540852e-04f, -4.806236671e-04f, -4.782789577e-04f, -4.295045226e-04f, -3.348667971e-04f, -2.022311686e-04f, -4.581869630e-05f, +1.160612449e-04f, +2.641485480e-04f, +3.815740737e-04f, +4.566892339e-04f, +4.847927474e-04f, +4.684312656e-04f, +4.162885910e-04f, +3.409983591e-04f, +2.564365532e-04f, +1.751220037e-04f, +1.062665706e-04f, +5.480619333e-05f,
+    /* 24, 5 */ -7.193456522e-05f, -1.302170312e-04f, -2.045615590e-04f, -2.883831622e-04f, -3.710594077e-04f, -4.392893036e-04f, -4.793389263e-04f, -4.797527765e-04f, -4.339395286e-04f, -3.420118645e-04f, -2.113914331e-04f, -5.595644787e-05f, +1.061532886e-04f, +2.556376279e-04f, +3.753863858e-04f, +4.533603695e-04f, +4.843998374e-04f, +4.706174312e-04f, +4.203687678e-04f, +3.461177167e-04f, +2.617429433e-04f, +1.799141593e-04f, +1.100893595e-04f, +5.747989630e-05f,
+    /* 24, 6 */ -6.889834987e-05f, -1.260591965e-04f, -1.995470454e-04f, -2.830503358e-04f, -3.661698243e-04f, -4.357136989e-04f, -4.778977479e-04f, -4.810437916e-04f, -4.381919648e-04f, -3.490036345e-04f, -2.204533432e-04f, -6.606773875e-05f, +9.619528314e-05f, +2.470088608e-04f, +3.690332209e-04f, +4.498457452e-04f, +4.838297683e-04f, +4.726597937e-04f, +4.243547301e-04f, +3.511979487e-04f, +2.670603639e-04f, +1.847546294e-04f, +1.139808078e-04f, +6.022577049e-05f,
+    /* 24, 7 */ -6.593485851e-05f, -1.219665852e-04f, -1.945730275e-04f, -2.777169567e-04f, -3.612274261e-04f, -4.320305335e-04f, -4.763025159e-04f, -4.821529264e-04f, -4.422609626e-04f, -3.558394612e-04f, -2.294128549e-04f, -7.614780136e-05f, +8.619188981e-05f, +2.382659945e-04f, +3.625168024e-04f, +4.461457687e-04f, +4.830812085e-04f, +4.745556880e-04f, +4.282431024e-04f, +3.562356572e-04f, +2.723859925e-04f, +1.896415594e-04f, +1.179401580e-04f, +6.304403582e-05f,
+    /* 24, 8 */ -6.304403582e-05f, -1.179401580e-04f, -1.896415594e-04f, -2.723859925e-04f, -3.562356572e-04f, -4.282431024e-04f, -4.745556880e-04f, -4.830812085e-04f, -4.461457687e-04f, -3.625168024e-04f, -2.382659945e-04f, -8.619188981e-05f, +7.614780136e-05f, +2.294128549e-04f, +3.558394612e-04f, +4.422609626e-04f, +4.821529264e-04f, +4.763025159e-04f, +4.320305335e-04f, +3.612274261e-04f, +2.777169567e-04f, +1.945730275e-04f, +1.219665852e-04f, +6.593485851e-05f,
+    /* 24, 9 */ -6.022577049e-05f, -1.139808078e-04f, -1.847546294e-04f, -2.670603639e-04f, -3.511979487e-04f, -4.243547301e-04f, -4.726597937e-04f, -4.838297683e-04f, -4.498457452e-04f, -3.690332209e-04f, -2.470088608e-04f, -9.619528314e-05f, +6.606773875e-05f, +2.204533432e-04f, +3.490036345e-04f, +4.381919648e-04f, +4.810437916e-04f, +4.778977479e-04f, +4.357136989e-04f, +3.661698243e-04f, +2.830503358e-04f, +1.995470454e-04f, +1.260591965e-04f, +6.889834987e-05f,
+    /* 24,10 */ -5.747989630e-05f, -1.100893595e-04f, -1.799141593e-04f, -2.617429433e-04f, -3.461177167e-04f, -4.203687678e-04f, -4.706174312e-04f, -4.843998374e-04f, -4.533603695e-04f, -3.753863858e-04f, -2.556376279e-04f, -1.061532886e-04f, +5.595644787e-05f, +2.113914331e-04f, +3.420118645e-04f, +4.339395286e-04f, +4.797527765e-04f, +4.793389263e-04f, +4.392893036e-04f, +3.710594077e-04f, +2.883831622e-04f, +2.045615590e-04f, +1.302170312e-04f, +7.193456522e-05f,
+    /* 24,11 */ -5.480619333e-05f, -1.062665706e-04f, -1.751220037e-04f, -2.564365532e-04f, -3.409983591e-04f, -4.162885910e-04f, -4.684312656e-04f, -4.847927474e-04f, -4.566892339e-04f, -3.815740737e-04f, -2.641485480e-04f, -1.160612449e-04f, +4.581869630e-05f, +2.022311686e-04f, +3.348667971e-04f, +4.295045226e-04f, +4.782789577e-04f, +4.806236671e-04f, +4.427540852e-04f, +3.758927218e-04f, +2.937124231e-04f, +2.096144489e-04f, +1.344390595e-04f, +7.504350274e-05f,
+    /* 24,12 */ -5.220438912e-05f, -1.025131319e-04f, -1.703799499e-04f, -2.511439643e-04f, -3.358432542e-04f, -4.121175966e-04f, -4.661040260e-04f, -4.850099279e-04f, -4.598320457e-04f, -3.875941701e-04f, -2.725379532e-04f, -1.259145254e-04f, +3.565926997e-05f, +1.929766610e-04f, +3.275711800e-04f, +4.248879309e-04f, +4.766215175e-04f, +4.817496625e-04f, +4.461048161e-04f, +3.806663042e-04f, +2.990350629e-04f, +2.147035314e-04f, +1.387241832e-04f, +7.822510242e-05f,
+    /* 24,13 */ -4.967415993e-05f, -9.882966748e-05f, -1.656897170e-04f, -2.458678944e-04f, -3.306557574e-04f, -4.078592000e-04f, -4.636385032e-04f, -4.850529052e-04f, -4.627886263e-04f, -3.934446700e-04f, -2.808022583e-04f, -1.357085413e-04f, +2.548296987e-05f, +1.836320864e-04f, +3.201278616e-04f, +4.200908527e-04f, +4.747797448e-04f, +4.827146831e-04f, +4.493383067e-04f, +3.853766873e-04f, +3.043479843e-04f, +2.198265592e-04f, +1.430712350e-04f, +8.147924507e-05f,
+    /* 24,14 */ -4.721513201e-05f, -9.521673594e-05f, -1.610529558e-04f, -2.406110065e-04f, -3.254391996e-04f, -4.035168325e-04f, -4.610375470e-04f, -4.849233000e-04f, -4.655589110e-04f, -3.991236794e-04f, -2.889379628e-04f, -1.454387449e-04f, +1.529460870e-05f, +1.742016828e-04f, +3.125397891e-04f, +4.151145025e-04f, +4.727530367e-04f, +4.835165803e-04f, +4.524514078e-04f, +3.900204007e-04f, +3.096480504e-04f, +2.249812225e-04f, +1.474789784e-04f, +8.480575132e-05f,
+    /* 24,15 */ -4.482688286e-05f, -9.167483068e-05f, -1.564712483e-04f, -2.353759082e-04f, -3.201968846e-04f, -3.990939388e-04f, -4.583040637e-04f, -4.846228261e-04f, -4.681429482e-04f, -4.046294158e-04f, -2.969416536e-04f, -1.551006323e-04f, +5.099007530e-06f, +1.646897470e-04f, +3.048100066e-04f, +4.099602091e-04f, +4.705408991e-04f, +4.841532882e-04f, +4.554410135e-04f, +3.945939739e-04f, +3.149320871e-04f, +2.301651496e-04f, +1.519461079e-04f, +8.820438069e-05f,
+    /* 24, 0 */ +3.721332452e-05f, -8.727351622e-06f, -1.260052743e-04f, -3.262895896e-04f, -5.956603662e-04f, -8.899501259e-04f, -1.140781305e-03f, -1.272347980e-03f, -1.224469676e-03f, -9.741728935e-04f, -5.476309302e-04f, -1.718639697e-05f, +5.165697336e-04f, +9.521355524e-04f, +1.214742061e-03f, +1.275075958e-03f, +1.153251370e-03f, +9.076938752e-04f, +6.139361451e-04f, +3.414081512e-04f, +1.360881127e-04f, +1.374767685e-05f, -3.597568203e-05f, -3.836441874e-05f,
+    /* 24, 1 */ +3.827272022e-05f, -3.990309212e-06f, -1.162552839e-04f, -3.114511951e-04f, -5.774902753e-04f, -8.720393210e-04f, -1.127840237e-03f, -1.268906542e-03f, -1.233389975e-03f, -9.955061195e-04f, -5.782766642e-04f, -5.154594549e-05f, +4.851159022e-04f, +9.294071718e-04f, +1.204207601e-03f, +1.277079499e-03f, +1.165232472e-03f, +9.252515980e-04f, +6.323029482e-04f, +3.567995352e-04f, +1.465038861e-04f, +1.905656402e-05f, -3.455261727e-05f, -3.906074609e-05f,
+    /* 24, 2 */ +3.916105537e-05f, +4.689475139e-07f, -1.068376458e-04f, -2.968998221e-04f, -5.594401371e-04f, -8.539803004e-04f, -1.114446367e-03f, -1.264763218e-03f, -1.241503276e-03f, -1.016122900e-03f, -6.084845647e-04f, -8.586577249e-05f, +4.532926958e-04f, +9.060015608e-04f, +1.192867560e-03f, +1.278348235e-03f, +1.176706916e-03f, +9.426042142e-04f, +6.507457252e-04f, +3.724559064e-04f, +1.572522637e-04f, +2.465906089e-05f, -3.293697730e-05f, -3.967556907e-05f,
+    /* 24, 3 */ +3.988551544e-05f, +4.656120273e-06f, -9.775146571e-05f, -2.826418349e-04f, -5.415238064e-04f, -8.357917522e-04f, -1.100618114e-03f, -1.259930153e-03f, -1.248810685e-03f, -1.036011659e-03f, -6.382327409e-04f, -1.201194461e-04f, +4.211237814e-04f, +8.819332498e-04f, +1.180724004e-03f, +1.278872430e-03f, +1.187657300e-03f, +9.597325558e-04f, +6.692490513e-04f, +3.883689407e-04f, +1.683324883e-04f, +3.055997058e-05f, -3.112164378e-05f, -4.020250110e-05f,
+    /* 24, 4 */ +4.045327387e-05f, +8.577101915e-06f, -8.899546026e-05f, -2.686831091e-04f, -5.237547173e-04f, -8.174921923e-04f, -1.086374092e-03f, -1.254420050e-03f, -1.255314082e-03f, -1.055161588e-03f, -6.674998060e-04f, -1.542806079e-04f, +3.886332072e-04f, +8.572174771e-04f, +1.167779798e-03f, +1.278642989e-03f, +1.198066533e-03f, +9.766173891e-04f, +6.877971412e-04f, +4.045298263e-04f, +1.797433681e-04f, +3.676383839e-05f, -2.909954521e-05f, -4.063504078e-05f,
+    /* 24, 5 */ +4.087148106e-05f, +1.223796294e-05f, -8.056796775e-05f, -2.550290329e-04f, -5.061458738e-04f, -7.990999447e-04f, -1.071733083e-03f, -1.248246148e-03f, -1.261016119e-03f, -1.073562648e-03f, -6.962648992e-04f, -1.883230024e-04f, +3.558453770e-04f, +8.318701747e-04f, +1.154038613e-03f, +1.277651484e-03f, +1.207917863e-03f, +9.932394374e-04f, +7.063738625e-04f, +4.209292660e-04f, +1.914832687e-04f, +4.327493877e-05f, -2.686366939e-05f, -4.096657950e-05f,
+    /* 24, 6 */ +4.114725367e-05f, +1.564493806e-05f, -7.246695886e-05f, -2.416845105e-04f, -4.887098400e-04f, -7.806331214e-04f, -1.056714015e-03f, -1.241422199e-03f, -1.265920211e-03f, -1.091205584e-03f, -7.245077077e-04f, -2.222205061e-04f, +3.227850234e-04f, +8.059079530e-04f, +1.139504920e-03f, +1.275890161e-03f, +1.217194897e-03f, +1.009579403e-03f, +7.249627509e-04f, +4.375574807e-04f, +2.035501057e-04f, +5.009726244e-05f, -2.440707607e-05f, -4.119040933e-05f,
+    /* 24, 7 */ +4.128766430e-05f, +1.880441272e-05f, -6.469004778e-05f, -2.286539641e-04f, -4.714587328e-04f, -7.621096041e-04f, -1.041335936e-03f, -1.233962452e-03f, -1.270030522e-03f, -1.108081926e-03f, -7.522084876e-04f, -2.559471563e-04f, +2.894771803e-04f, +7.793480846e-04f, +1.124183998e-03f, +1.273351959e-03f, +1.225881627e-03f, +1.025617992e-03f, +7.435470253e-04f, +4.544042133e-04f, +2.159413387e-04f, +5.723450367e-05f, -2.172290976e-05f, -4.129973134e-05f,
+    /* 24, 8 */ +4.129973134e-05f, +2.172290976e-05f, -5.723450367e-05f, -2.159413387e-04f, -4.544042133e-04f, -7.435470253e-04f, -1.025617992e-03f, -1.225881627e-03f, -1.273351959e-03f, -1.124183998e-03f, -7.793480846e-04f, -2.894771803e-04f, +2.559471563e-04f, +7.522084876e-04f, +1.108081926e-03f, +1.270030522e-03f, +1.233962452e-03f, +1.041335936e-03f, +7.621096041e-04f, +4.714587328e-04f, +2.286539641e-04f, +6.469004778e-05f, -1.880441272e-05f, -4.128766430e-05f,
+    /* 24, 9 */ +4.119040933e-05f, +2.440707607e-05f, -5.009726244e-05f, -2.035501057e-04f, -4.375574807e-04f, -7.249627509e-04f, -1.009579403e-03f, -1.217194897e-03f, -1.275890161e-03f, -1.139504920e-03f, -8.059079530e-04f, -3.227850234e-04f, +2.222205061e-04f, +7.245077077e-04f, +1.091205584e-03f, +1.265920211e-03f, +1.241422199e-03f, +1.056714015e-03f, +7.806331214e-04f, +4.887098400e-04f, +2.416845105e-04f, +7.246695886e-05f, -1.564493806e-05f, -4.114725367e-05f,
+    /* 24,10 */ +4.096657950e-05f, +2.686366939e-05f, -4.327493877e-05f, -1.914832687e-04f, -4.209292660e-04f, -7.063738625e-04f, -9.932394374e-04f, -1.207917863e-03f, -1.277651484e-03f, -1.154038613e-03f, -8.318701747e-04f, -3.558453770e-04f, +1.883230024e-04f, +6.962648992e-04f, +1.073562648e-03f, +1.261016119e-03f, +1.248246148e-03f, +1.071733083e-03f, +7.990999447e-04f, +5.061458738e-04f, +2.550290329e-04f, +8.056796775e-05f, -1.223796294e-05f, -4.087148106e-05f,
+    /* 24,11 */ +4.063504078e-05f, +2.909954521e-05f, -3.676383839e-05f, -1.797433681e-04f, -4.045298263e-04f, -6.877971412e-04f, -9.766173891e-04f, -1.198066533e-03f, -1.278642989e-03f, -1.167779798e-03f, -8.572174771e-04f, -3.886332072e-04f, +1.542806079e-04f, +6.674998060e-04f, +1.055161588e-03f, +1.255314082e-03f, +1.254420050e-03f, +1.086374092e-03f, +8.174921923e-04f, +5.237547173e-04f, +2.686831091e-04f, +8.899546026e-05f, -8.577101915e-06f, -4.045327387e-05f,
+    /* 24,12 */ +4.020250110e-05f, +3.112164378e-05f, -3.055997058e-05f, -1.683324883e-04f, -3.883689407e-04f, -6.692490513e-04f, -9.597325558e-04f, -1.187657300e-03f, -1.278872430e-03f, -1.180724004e-03f, -8.819332498e-04f, -4.211237814e-04f, +1.201194461e-04f, +6.382327409e-04f, +1.036011659e-03f, +1.248810685e-03f, +1.259930153e-03f, +1.100618114e-03f, +8.357917522e-04f, +5.415238064e-04f, +2.826418349e-04f, +9.775146571e-05f, -4.656120273e-06f, -3.988551544e-05f,
+    /* 24,13 */ +3.967556907e-05f, +3.293697730e-05f, -2.465906089e-05f, -1.572522637e-04f, -3.724559064e-04f, -6.507457252e-04f, -9.426042142e-04f, -1.176706916e-03f, -1.278348235e-03f, -1.192867560e-03f, -9.060015608e-04f, -4.532926958e-04f, +8.586577249e-05f, +6.084845647e-04f, +1.016122900e-03f, +1.241503276e-03f, +1.264763218e-03f, +1.114446367e-03f, +8.539803004e-04f, +5.594401371e-04f, +2.968998221e-04f, +1.068376458e-04f, -4.689475139e-07f, -3.916105537e-05f,
+    /* 24,14 */ +3.906074609e-05f, +3.455261727e-05f, -1.905656402e-05f, -1.465038861e-04f, -3.567995352e-04f, -6.323029482e-04f, -9.252515980e-04f, -1.165232472e-03f, -1.277079499e-03f, -1.204207601e-03f, -9.294071718e-04f, -4.851159022e-04f, +5.154594549e-05f, +5.782766642e-04f, +9.955061195e-04f, +1.233389975e-03f, +1.268906542e-03f, +1.127840237e-03f, +8.720393210e-04f, +5.774902753e-04f, +3.114511951e-04f, +1.162552839e-04f, +3.990309212e-06f, -3.827272022e-05f,
+    /* 24,15 */ +3.836441874e-05f, +3.597568203e-05f, -1.374767685e-05f, -1.360881127e-04f, -3.414081512e-04f, -6.139361451e-04f, -9.076938752e-04f, -1.153251370e-03f, -1.275075958e-03f, -1.214742061e-03f, -9.521355524e-04f, -5.165697336e-04f, +1.718639697e-05f, +5.476309302e-04f, +9.741728935e-04f, +1.224469676e-03f, +1.272347980e-03f, +1.140781305e-03f, +8.899501259e-04f, +5.956603662e-04f, +3.262895896e-04f, +1.260052743e-04f, +8.727351622e-06f, -3.721332452e-05f,
+    /* 24, 0 */ +8.266384897e-05f, +1.864042294e-04f, +2.488885336e-04f, +1.546439211e-04f, -1.995837972e-04f, -8.300120177e-04f, -1.613160849e-03f, -2.296673715e-03f, -2.585717258e-03f, -2.273475621e-03f, -1.352242686e-03f, -4.324968723e-05f, +1.278412578e-03f, +2.232544293e-03f, +2.585064833e-03f, +2.329165788e-03f, +1.661894649e-03f, +8.765619362e-04f, +2.314166150e-04f, -1.408802900e-04f, -2.488728147e-04f, -1.925779863e-04f, -8.867605644e-05f, -1.381647235e-05f,
+    /* 24, 1 */ +7.679604466e-05f, +1.800850086e-04f, +2.482891228e-04f, +1.673628145e-04f, -1.688781476e-04f, -7.841040553e-04f, -1.564053778e-03f, -2.262611362e-03f, -2.583952550e-03f, -2.311948888e-03f, -1.424504725e-03f, -1.296977982e-04f, +1.203096102e-03f, +2.189184288e-03f, +2.581965725e-03f, +2.360018674e-03f, +1.710180642e-03f, +9.237037585e-04f, +2.643640884e-04f, -1.260534627e-04f, -2.482108983e-04f, -1.985807152e-04f, -9.482163577e-05f, -1.700840565e-05f,
+    /* 24, 2 */ +7.108272573e-05f, +1.736451253e-04f, +2.471057735e-04f, +1.790568131e-04f, -1.393098721e-04f, -7.388859215e-04f, -1.514647192e-03f, -2.227049028e-03f, -2.579803518e-03f, -2.347938498e-03f, -1.495119547e-03f, -2.159922036e-04f, +1.126377386e-03f, +2.143428746e-03f, +2.576393731e-03f, +2.389164999e-03f, +1.757943642e-03f, +9.713853048e-04f, +2.984113695e-04f, -1.101464409e-04f, -2.468719141e-04f, -2.043861254e-04f, -1.010885985e-04f, -2.040687624e-05f,
+    /* 24, 3 */ +6.553303557e-05f, +1.671085856e-04f, +2.453697283e-04f, +1.897470664e-04f, -1.108869031e-04f, -6.944032625e-04f, -1.465013955e-03f, -2.190058265e-03f, -2.573306150e-03f, -2.381422658e-03f, -1.564010684e-03f, -3.020307159e-04f, +1.048342851e-03f, +2.095314540e-03f, +2.568326065e-03f, +2.416539054e-03f, +1.805107932e-03f, +1.019552324e-03f, +3.335412514e-04f, -9.314376077e-05f, -2.448252646e-04f, -2.099672379e-04f, -1.074639934e-04f, -2.401251277e-05f,
+    /* 24, 4 */ +6.015519126e-05f, +1.604985702e-04f, +2.431122111e-04f, +1.994559526e-04f, -8.361493575e-05f, -6.506994570e-04f, -1.415225919e-03f, -2.151711738e-03f, -2.564499518e-03f, -2.412383397e-03f, -1.631104459e-03f, -3.877115714e-04f, +9.690810727e-04f, +2.044882222e-03f, +2.557743429e-03f, +2.442076932e-03f, +1.851597394e-03f, +1.068148557e-03f, +3.697341485e-04f, -7.503156587e-05f, -2.420407013e-04f, -2.152964269e-04f, -1.139339030e-04f, -2.782526914e-05f,
+    /* 24, 5 */ +5.495649810e-05f, +1.538374078e-04f, +2.403643576e-04f, +2.082069988e-04f, -5.749746926e-05f, -6.078155802e-04f, -1.365353811e-03f, -2.112083086e-03f, -2.553425683e-03f, -2.440806561e-03f, -1.696330106e-03f, -4.729335981e-04f, +8.886826408e-04f, +1.992175989e-03f, +2.544630075e-03f, +2.465716661e-03f, +1.897335642e-03f, +1.117115802e-03f, +4.069680813e-04f, -5.579767803e-05f, -2.384884029e-04f, -2.203454641e-04f, -1.204834430e-04f, -3.184439496e-05f,
+    /* 24, 6 */ +4.994336610e-05f, +1.471465506e-04f, +2.371571498e-04f, +2.160248014e-04f, -3.253585139e-05f, -5.657903730e-04f, -1.315467130e-03f, -2.071246779e-03f, -2.540129593e-03f, -2.466681818e-03f, -1.759619871e-03f, -5.575963834e-04f, +8.072400133e-04f, +1.937243618e-03f, +2.528973864e-03f, +2.487398336e-03f, +1.942246152e-03f, +1.166393990e-03f, +4.452186667e-04f, -3.543166589e-05f, -2.341390536e-04f, -2.250855657e-04f, -1.270967638e-04f, -3.606840695e-05f,
+    /* 24, 7 */ +4.512132841e-05f, +1.404465535e-04f, +2.335213506e-04f, +2.229349451e-04f, -8.729326764e-06f, -5.246602166e-04f, -1.265634037e-03f, -2.029277978e-03f, -2.524658979e-03f, -2.490002646e-03f, -1.820909115e-03f, -6.416004392e-04f, +7.248473657e-04f, +1.880136408e-03f, +2.510766314e-03f, +2.507064240e-03f, +1.986252395e-03f, +1.215921259e-03f, +4.844591124e-04f, -1.392491133e-05f, -2.289639228e-04f, -2.294874421e-04f, -1.337570553e-04f, -4.049506149e-05f,
+    /* 24, 8 */ +4.049506149e-05f, +1.337570553e-04f, +2.294874421e-04f, +2.289639228e-04f, +1.392491133e-05f, -4.844591124e-04f, -1.215921259e-03f, -1.986252395e-03f, -2.507064240e-03f, -2.510766314e-03f, -1.880136408e-03f, -7.248473657e-04f, +6.416004392e-04f, +1.820909115e-03f, +2.490002646e-03f, +2.524658979e-03f, +2.029277978e-03f, +1.265634037e-03f, +5.246602166e-04f, +8.729326764e-06f, -2.229349451e-04f, -2.335213506e-04f, -1.404465535e-04f, -4.512132841e-05f,
+    /* 24, 9 */ +3.606840695e-05f, +1.270967638e-04f, +2.250855657e-04f, +2.341390536e-04f, +3.543166589e-05f, -4.452186667e-04f, -1.166393990e-03f, -1.942246152e-03f, -2.487398336e-03f, -2.528973864e-03f, -1.937243618e-03f, -8.072400133e-04f, +5.575963834e-04f, +1.759619871e-03f, +2.466681818e-03f, +2.540129593e-03f, +2.071246779e-03f, +1.315467130e-03f, +5.657903730e-04f, +3.253585139e-05f, -2.160248014e-04f, -2.371571498e-04f, -1.471465506e-04f, -4.994336610e-05f,
+    /* 24,10 */ +3.184439496e-05f, +1.204834430e-04f, +2.203454641e-04f, +2.384884029e-04f, +5.579767803e-05f, -4.069680813e-04f, -1.117115802e-03f, -1.897335642e-03f, -2.465716661e-03f, -2.544630075e-03f, -1.992175989e-03f, -8.886826408e-04f, +4.729335981e-04f, +1.696330106e-03f, +2.440806561e-03f, +2.553425683e-03f, +2.112083086e-03f, +1.365353811e-03f, +6.078155802e-04f, +5.749746926e-05f, -2.082069988e-04f, -2.403643576e-04f, -1.538374078e-04f, -5.495649810e-05f,
+    /* 24,11 */ +2.782526914e-05f, +1.139339030e-04f, +2.152964269e-04f, +2.420407013e-04f, +7.503156587e-05f, -3.697341485e-04f, -1.068148557e-03f, -1.851597394e-03f, -2.442076932e-03f, -2.557743429e-03f, -2.044882222e-03f, -9.690810727e-04f, +3.877115714e-04f, +1.631104459e-03f, +2.412383397e-03f, +2.564499518e-03f, +2.151711738e-03f, +1.415225919e-03f, +6.506994570e-04f, +8.361493575e-05f, -1.994559526e-04f, -2.431122111e-04f, -1.604985702e-04f, -6.015519126e-05f,
+    /* 24,12 */ +2.401251277e-05f, +1.074639934e-04f, +2.099672379e-04f, +2.448252646e-04f, +9.314376077e-05f, -3.335412514e-04f, -1.019552324e-03f, -1.805107932e-03f, -2.416539054e-03f, -2.568326065e-03f, -2.095314540e-03f, -1.048342851e-03f, +3.020307159e-04f, +1.564010684e-03f, +2.381422658e-03f, +2.573306150e-03f, +2.190058265e-03f, +1.465013955e-03f, +6.944032625e-04f, +1.108869031e-04f, -1.897470664e-04f, -2.453697283e-04f, -1.671085856e-04f, -6.553303557e-05f,
+    /* 24,13 */ +2.040687624e-05f, +1.010885985e-04f, +2.043861254e-04f, +2.468719141e-04f, +1.101464409e-04f, -2.984113695e-04f, -9.713853048e-04f, -1.757943642e-03f, -2.389164999e-03f, -2.576393731e-03f, -2.143428746e-03f, -1.126377386e-03f, +2.159922036e-04f, +1.495119547e-03f, +2.347938498e-03f, +2.579803518e-03f, +2.227049028e-03f, +1.514647192e-03f, +7.388859215e-04f, +1.393098721e-04f, -1.790568131e-04f, -2.471057735e-04f, -1.736451253e-04f, -7.108272573e-05f,
+    /* 24,14 */ +1.700840565e-05f, +9.482163577e-05f, +1.985807152e-04f, +2.482108983e-04f, +1.260534627e-04f, -2.643640884e-04f, -9.237037585e-04f, -1.710180642e-03f, -2.360018674e-03f, -2.581965725e-03f, -2.189184288e-03f, -1.203096102e-03f, +1.296977982e-04f, +1.424504725e-03f, +2.311948888e-03f, +2.583952550e-03f, +2.262611362e-03f, +1.564053778e-03f, +7.841040553e-04f, +1.688781476e-04f, -1.673628145e-04f, -2.482891228e-04f, -1.800850086e-04f, -7.679604466e-05f,
+    /* 24,15 */ +1.381647235e-05f, +8.867605644e-05f, +1.925779863e-04f, +2.488728147e-04f, +1.408802900e-04f, -2.314166150e-04f, -8.765619362e-04f, -1.661894649e-03f, -2.329165788e-03f, -2.585064833e-03f, -2.232544293e-03f, -1.278412578e-03f, +4.324968723e-05f, +1.352242686e-03f, +2.273475621e-03f, +2.585717258e-03f, +2.296673715e-03f, +1.613160849e-03f, +8.300120177e-04f, +1.995837972e-04f, -1.546439211e-04f, -2.488885336e-04f, -1.864042294e-04f, -8.266384897e-05f,
+    /* 24, 0 */ -8.756118778e-05f, -1.009631262e-05f, +2.499923290e-04f, +5.877223422e-04f, +6.788717735e-04f, +1.353208099e-04f, -1.181609893e-03f, -2.907631270e-03f, -4.227440709e-03f, -4.289302846e-03f, -2.753030129e-03f, -9.027467135e-05f, +2.610460208e-03f, +4.239433597e-03f, +4.275304929e-03f, +3.011836329e-03f, +1.284225967e-03f, -7.470693818e-05f, -6.668983668e-04f, -6.049037547e-04f, -2.711811033e-04f, -7.712041122e-07f, +8.724954076e-05f, +5.404595280e-05f,
+    /* 24, 1 */ -8.741655630e-05f, -2.019027119e-05f, +2.291878545e-04f, +5.696067266e-04f, +6.882827247e-04f, +1.927359117e-04f, -1.080756061e-03f, -2.801858302e-03f, -4.174485032e-03f, -4.332641998e-03f, -2.890980043e-03f, -2.706680808e-04f, +2.463494124e-03f, +4.183052585e-03f, +4.317905196e-03f, +3.114235078e-03f, +1.388440877e-03f, -1.091717027e-05f, -6.522789212e-04f, -6.210469572e-04f, -2.926973166e-04f, -1.241413728e-05f, +8.645224618e-05f, +5.751117153e-05f,
+    /* 24, 2 */ -8.684540791e-05f, -2.951540942e-05f, +2.088206066e-04f, +5.506594457e-04f, +6.952187414e-04f, +2.469379129e-04f, -9.818199274e-04f, -2.694754852e-03f, -4.116618896e-03f, -4.369446535e-03f, -3.024096686e-03f, -4.505940556e-04f, +2.312365817e-03f, +4.120192033e-03f, +4.355078033e-03f, +3.214588722e-03f, +1.494083528e-03f, +5.601692583e-05f, -6.349341530e-04f, -6.360467505e-04f, -3.144802134e-04f, -2.483132916e-05f, +8.514047439e-05f, +6.091502927e-05f,
+    /* 24, 3 */ -8.587775422e-05f, -3.807920270e-05f, +1.889395528e-04f, +5.309813040e-04f, +6.997709178e-04f, +2.979208532e-04f, -8.849487633e-04f, -2.586556795e-03f, -4.054031257e-03f, -4.399725659e-03f, -3.152177828e-03f, -6.297421881e-04f, +2.157318805e-03f, +4.050898074e-03f, +4.386669491e-03f, +3.312658663e-03f, +1.600975431e-03f, +1.260549593e-04f, -6.147894349e-04f, -6.497970322e-04f, -3.364651980e-04f, -3.801847602e-05f, +8.328608571e-05f, +6.423360450e-05f,
+    /* 24, 4 */ -8.454371894e-05f, -4.589171696e-05f, +1.695896910e-04f, +5.106711213e-04f, +7.020335552e-04f, +3.456869501e-04f, -7.902814402e-04f, -2.477497901e-03f, -3.986918477e-03f, -4.423502152e-03f, -3.275032702e-03f, -8.078038906e-04f, +1.998605647e-03f, +3.975230752e-03f, +4.412535626e-03f, +3.408207107e-03f, +1.708931018e-03f, +1.991476106e-04f, -5.917751493e-04f, -6.621910968e-04f, -3.585839047e-04f, -5.196800512e-05f, +8.086178430e-05f, +6.744196867e-05f,
+    /* 24, 5 */ -8.287340509e-05f, -5.296545744e-05f, +1.508120534e-04f, +4.898254962e-04f, +7.021037953e-04f, +3.902463858e-04f, -6.979482496e-04f, -2.367809282e-03f, -3.915483754e-03f, -4.440812209e-03f, -3.392482395e-03f, -9.844731162e-04f, +1.836487379e-03f, +3.893263974e-03f, +4.432542967e-03f, +3.500997660e-03f, +1.817757983e-03f, +2.752365501e-04f, -5.658270331e-04f, -6.731219472e-04f, -3.807642824e-04f, -6.666895953e-05f, +7.784127492e-05f, +7.051425394e-05f,
+    /* 24, 6 */ -8.089676755e-05f, -5.931521393e-05f, +1.326437227e-04f, +4.685385820e-04f, +7.000812546e-04f, +4.316170765e-04f, -6.080707472e-04f, -2.257718867e-03f, -3.839936544e-03f, -4.451705229e-03f, -3.504360214e-03f, -1.159447072e-03f, +1.671232927e-03f, +3.805085432e-03f, +4.446568950e-03f, +3.590795947e-03f, +1.927257648e-03f, +3.542543807e-04f, -5.368865161e-04f, -6.824826149e-04f, -4.029306956e-04f, -8.210689127e-05f, +7.419942176e-05f, +7.342372810e-05f,
+    /* 24, 7 */ -7.864349144e-05f, -6.495790319e-05f, +1.151178633e-04f, +4.469018792e-04f, +6.960676605e-04f, +4.698244236e-04f, -5.207616239e-04f, -2.147450881e-03f, -3.760491970e-03f, -4.456243581e-03f, -3.610512013e-03f, -1.332426925e-03f, +1.503118492e-03f, +3.710796487e-03f, +4.454502333e-03f, +3.677370219e-03f, +2.037225356e-03f, +4.361246060e-04f, -5.049010493e-04f, -6.901664903e-04f, -4.250040411e-04f, -9.826376366e-05f, +6.991240915e-05f, +7.614287656e-05f,
+    /* 24, 8 */ -7.614287656e-05f, -6.991240915e-05f, +9.826376366e-05f, +4.250040411e-04f, +6.901664903e-04f, +5.049010493e-04f, -4.361246060e-04f, -2.037225356e-03f, -3.677370219e-03f, -4.454502333e-03f, -3.710796487e-03f, -1.503118492e-03f, +1.332426925e-03f, +3.610512013e-03f, +4.456243581e-03f, +3.760491970e-03f, +2.147450881e-03f, +5.207616239e-04f, -4.698244236e-04f, -6.960676605e-04f, -4.469018792e-04f, -1.151178633e-04f, +6.495790319e-05f, +7.864349144e-05f,
+    /* 24, 9 */ -7.342372810e-05f, -7.419942176e-05f, +8.210689127e-05f, +4.029306956e-04f, +6.824826149e-04f, +5.368865161e-04f, -3.542543807e-04f, -1.927257648e-03f, -3.590795947e-03f, -4.446568950e-03f, -3.805085432e-03f, -1.671232927e-03f, +1.159447072e-03f, +3.504360214e-03f, +4.451705229e-03f, +3.839936544e-03f, +2.257718867e-03f, +6.080707472e-04f, -4.316170765e-04f, -7.000812546e-04f, -4.685385820e-04f, -1.326437227e-04f, +5.931521393e-05f, +8.089676755e-05f,
+    /* 24,10 */ -7.051425394e-05f, -7.784127492e-05f, +6.666895953e-05f, +3.807642824e-04f, +6.731219472e-04f, +5.658270331e-04f, -2.752365501e-04f, -1.817757983e-03f, -3.500997660e-03f, -4.432542967e-03f, -3.893263974e-03f, -1.836487379e-03f, +9.844731162e-04f, +3.392482395e-03f, +4.440812209e-03f, +3.915483754e-03f, +2.367809282e-03f, +6.979482496e-04f, -3.902463858e-04f, -7.021037953e-04f, -4.898254962e-04f, -1.508120534e-04f, +5.296545744e-05f, +8.287340509e-05f,
+    /* 24,11 */ -6.744196867e-05f, -8.086178430e-05f, +5.196800512e-05f, +3.585839047e-04f, +6.621910968e-04f, +5.917751493e-04f, -1.991476106e-04f, -1.708931018e-03f, -3.408207107e-03f, -4.412535626e-03f, -3.975230752e-03f, -1.998605647e-03f, +8.078038906e-04f, +3.275032702e-03f, +4.423502152e-03f, +3.986918477e-03f, +2.477497901e-03f, +7.902814402e-04f, -3.456869501e-04f, -7.020335552e-04f, -5.106711213e-04f, -1.695896910e-04f, +4.589171696e-05f, +8.454371894e-05f,
+    /* 24,12 */ -6.423360450e-05f, -8.328608571e-05f, +3.801847602e-05f, +3.364651980e-04f, +6.497970322e-04f, +6.147894349e-04f, -1.260549593e-04f, -1.600975431e-03f, -3.312658663e-03f, -4.386669491e-03f, -4.050898074e-03f, -2.157318805e-03f, +6.297421881e-04f, +3.152177828e-03f, +4.399725659e-03f, +4.054031257e-03f, +2.586556795e-03f, +8.849487633e-04f, -2.979208532e-04f, -6.997709178e-04f, -5.309813040e-04f, -1.889395528e-04f, +3.807920270e-05f, +8.587775422e-05f,
+    /* 24,13 */ -6.091502927e-05f, -8.514047439e-05f, +2.483132916e-05f, +3.144802134e-04f, +6.360467505e-04f, +6.349341530e-04f, -5.601692583e-05f, -1.494083528e-03f, -3.214588722e-03f, -4.355078033e-03f, -4.120192033e-03f, -2.312365817e-03f, +4.505940556e-04f, +3.024096686e-03f, +4.369446535e-03f, +4.116618896e-03f, +2.694754852e-03f, +9.818199274e-04f, -2.469379129e-04f, -6.952187414e-04f, -5.506594457e-04f, -2.088206066e-04f, +2.951540942e-05f, +8.684540791e-05f,
+    /* 24,14 */ -5.751117153e-05f, -8.645224618e-05f, +1.241413728e-05f, +2.926973166e-04f, +6.210469572e-04f, +6.522789212e-04f, +1.091717027e-05f, -1.388440877e-03f, -3.114235078e-03f, -4.317905196e-03f, -4.183052585e-03f, -2.463494124e-03f, +2.706680808e-04f, +2.890980043e-03f, +4.332641998e-03f, +4.174485032e-03f, +2.801858302e-03f, +1.080756061e-03f, -1.927359117e-04f, -6.882827247e-04f, -5.696067266e-04f, -2.291878545e-04f, +2.019027119e-05f, +8.741655630e-05f,
+    /* 24,15 */ -5.404595280e-05f, -8.724954076e-05f, +7.712041122e-07f, +2.711811033e-04f, +6.049037547e-04f, +6.668983668e-04f, +7.470693818e-05f, -1.284225967e-03f, -3.011836329e-03f, -4.275304929e-03f, -4.239433597e-03f, -2.610460208e-03f, +9.027467135e-05f, +2.753030129e-03f, +4.289302846e-03f, +4.227440709e-03f, +2.907631270e-03f, +1.181609893e-03f, -1.353208099e-04f, -6.788717735e-04f, -5.877223422e-04f, -2.499923290e-04f, +1.009631262e-05f, +8.756118778e-05f,
+    /* 24, 0 */ -4.836862817e-05f, -2.381906908e-04f, -2.861422699e-04f, +1.419765781e-04f, +9.779307384e-04f, +1.431118485e-03f, +4.239072727e-04f, -2.320049614e-03f, -5.516524807e-03f, -6.885468951e-03f, -4.882970050e-03f, -1.652445539e-04f, +4.647640808e-03f, +6.864975932e-03f, +5.679465803e-03f, +2.528057977e-03f, -2.982544427e-04f, -1.420326139e-03f, -1.029081461e-03f, -1.868092348e-04f, +2.758007186e-04f, +2.491702023e-04f, +5.836581816e-05f, -3.491105347e-05f,
+    /* 24, 1 */ -3.887878147e-05f, -2.267040256e-04f, -2.944876029e-04f, +9.900949096e-05f, +9.254138922e-04f, +1.435932770e-03f, +5.422031866e-04f, -2.114193296e-03f, -5.346195092e-03f, -6.891993065e-03f, -5.106971374e-03f, -4.953359135e-04f, +4.401480442e-03f, +6.830374341e-03f, +5.834433801e-03f, +2.737690485e-03f, -1.653667139e-04f, -1.403322383e-03f, -1.078579553e-03f, -2.333982604e-04f, +2.633988510e-04f, +2.595470071e-04f, +6.884149383e-05f, -3.317859772e-05f,
+    /* 24, 2 */ -2.992041863e-05f, -2.148033017e-04f, -3.009073041e-04f, +5.800387728e-05f, +8.718108917e-04f, +1.435015360e-03f, +6.530479478e-04f, -1.910998057e-03f, -5.169072824e-03f, -6.884726614e-03f, -5.319183002e-03f, -8.242352929e-04f, +4.145019341e-03f, +6.781564023e-03f, +5.980858542e-03f, +2.948401826e-03f, -2.539414214e-05f, -1.379888189e-03f, -1.126132932e-03f, -2.816211011e-04f, +2.488796810e-04f, +2.692234993e-04f, +7.976200316e-05f, -3.095924021e-05f,
+    /* 24, 3 */ -2.151306397e-05f, -2.025787804e-04f, -3.054778181e-04f, +1.904245883e-05f, +8.173942695e-04f, +1.428624316e-03f, +7.563747429e-04f, -1.710952703e-03f, -4.985763915e-03f, -6.863885651e-03f, -5.519179462e-03f, -1.151152247e-03f, +3.878819947e-03f, +6.718485032e-03f, +6.118185890e-03f, +3.159630822e-03f, +1.214850236e-04f, -1.349820125e-03f, -1.171444944e-03f, -3.313418525e-04f, +2.321939470e-04f, +2.781004545e-04f, +9.108884721e-05f, -2.823129406e-05f,
+    /* 24, 4 */ -1.367174826e-05f, -1.901175448e-04f, -3.082808136e-04f, -1.780505549e-05f, +7.624282793e-04f, +1.417028061e-03f, +8.521437270e-04f, -1.514524553e-03f, -4.796881865e-03f, -6.829722854e-03f, -5.706572744e-03f, -1.475302628e-03f, +3.603475092e-03f, +6.641118197e-03f, +6.245879909e-03f, +3.370802059e-03f, +2.750642929e-04f, -1.312931609e-03f, -1.214215433e-03f, -3.824113238e-04f, +2.133007109e-04f, +2.860774924e-04f, +1.027786538e-04f, -2.497524656e-05f,
+    /* 24, 5 */ -6.407151783e-06f, -1.775031866e-04f, -3.094025487e-04f, -5.248174754e-05f, +7.071681142e-04f, +1.400504044e-03f, +9.403414758e-04f, -1.322158275e-03f, -4.603045619e-03f, -6.782526316e-03f, -5.881013326e-03f, -1.795911068e-03f, +3.319606230e-03f, +6.549485546e-03f, +6.363424898e-03f, +3.581327596e-03f, +4.351089927e-04f, -1.269054120e-03f, -1.254141844e-03f, -4.346671648e-04f, +1.921679399e-04f, +2.930535649e-04f, +1.147831783e-04f, -2.117401174e-05f,
+    /* 24, 6 */ +2.742338831e-07f, -1.648155254e-04f, -3.089332390e-04f, -8.494321776e-05f, +6.518591895e-04f, +1.379337399e-03f, +1.020980349e-03f, -1.134274832e-03f, -4.404877423e-03f, -6.722618231e-03f, -6.042191052e-03f, -2.112213435e-03f, +3.027861551e-03f, +6.443650588e-03f, +6.470327373e-03f, +3.790608755e-03f, +6.013564365e-04f, -1.218038367e-03f, -1.290920380e-03f, -4.879340571e-04f, +1.687730668e-04f, +2.989274696e-04f, +1.270493337e-04f, -1.681317980e-05f,
+    /* 24, 7 */ +6.369927035e-06f, -1.521303593e-04f, -3.069664313e-04f, -1.151572658e-04f, +5.967364879e-04f, +1.353819611e-03f, +1.094097769e-03f, -9.512705360e-04f, -4.203000702e-03f, -6.650353458e-03f, -6.189835876e-03f, -2.423459243e-03f, +2.728914008e-03f, +6.323718452e-03f, +6.566118000e-03f, +3.998037969e-03f, +7.735162047e-04f, -1.159755419e-03f, -1.324247193e-03f, -5.420239710e-04f, +1.431035268e-04f, +3.035983857e-04f, +1.395192491e-04f, -1.188126168e-05f,
+    /* 24, 8 */ +1.188126168e-05f, -1.395192491e-04f, -3.035983857e-04f, -1.431035268e-04f, +5.420239710e-04f, +1.324247193e-03f, +1.159755419e-03f, -7.735162047e-04f, -3.998037969e-03f, -6.566118000e-03f, -6.323718452e-03f, -2.728914008e-03f, +2.423459243e-03f, +6.189835876e-03f, +6.650353458e-03f, +4.203000702e-03f, +9.512705360e-04f, -1.094097769e-03f, -1.353819611e-03f, -5.967364879e-04f, +1.151572658e-04f, +3.069664313e-04f, +1.521303593e-04f, -6.369927035e-06f,
+    /* 24, 9 */ +1.681317980e-05f, -1.270493337e-04f, -2.989274696e-04f, -1.687730668e-04f, +4.879340571e-04f, +1.290920380e-03f, +1.218038367e-03f, -6.013564365e-04f, -3.790608755e-03f, -6.470327373e-03f, -6.443650588e-03f, -3.027861551e-03f, +2.112213435e-03f, +6.042191052e-03f, +6.722618231e-03f, +4.404877423e-03f, +1.134274832e-03f, -1.020980349e-03f, -1.379337399e-03f, -6.518591895e-04f, +8.494321776e-05f, +3.089332390e-04f, +1.648155254e-04f, -2.742338831e-07f,
+    /* 24,10 */ +2.117401174e-05f, -1.147831783e-04f, -2.930535649e-04f, -1.921679399e-04f, +4.346671648e-04f, +1.254141844e-03f, +1.269054120e-03f, -4.351089927e-04f, -3.581327596e-03f, -6.363424898e-03f, -6.549485546e-03f, -3.319606230e-03f, +1.795911068e-03f, +5.881013326e-03f, +6.782526316e-03f, +4.603045619e-03f, +1.322158275e-03f, -9.403414758e-04f, -1.400504044e-03f, -7.071681142e-04f, +5.248174754e-05f, +3.094025487e-04f, +1.775031866e-04f, +6.407151783e-06f,
+    /* 24,11 */ +2.497524656e-05f, -1.027786538e-04f, -2.860774924e-04f, -2.133007109e-04f, +3.824113238e-04f, +1.214215433e-03f, +1.312931609e-03f, -2.750642929e-04f, -3.370802059e-03f, -6.245879909e-03f, -6.641118197e-03f, -3.603475092e-03f, +1.475302628e-03f, +5.706572744e-03f, +6.829722854e-03f, +4.796881865e-03f, +1.514524553e-03f, -8.521437270e-04f, -1.417028061e-03f, -7.624282793e-04f, +1.780505549e-05f, +3.082808136e-04f, +1.901175448e-04f, +1.367174826e-05f,
+    /* 24,12 */ +2.823129406e-05f, -9.108884721e-05f, -2.781004545e-04f, -2.321939470e-04f, +3.313418525e-04f, +1.171444944e-03f, +1.349820125e-03f, -1.214850236e-04f, -3.159630822e-03f, -6.118185890e-03f, -6.718485032e-03f, -3.878819947e-03f, +1.151152247e-03f, +5.519179462e-03f, +6.863885651e-03f, +4.985763915e-03f, +1.710952703e-03f, -7.563747429e-04f, -1.428624316e-03f, -8.173942695e-04f, -1.904245883e-05f, +3.054778181e-04f, +2.025787804e-04f, +2.151306397e-05f,
+    /* 24,13 */ +3.095924021e-05f, -7.976200316e-05f, -2.692234993e-04f, -2.488796810e-04f, +2.816211011e-04f, +1.126132932e-03f, +1.379888189e-03f, +2.539414214e-05f, -2.948401826e-03f, -5.980858542e-03f, -6.781564023e-03f, -4.145019341e-03f, +8.242352929e-04f, +5.319183002e-03f, +6.884726614e-03f, +5.169072824e-03f, +1.910998057e-03f, -6.530479478e-04f, -1.435015360e-03f, -8.718108917e-04f, -5.800387728e-05f, +3.009073041e-04f, +2.148033017e-04f, +2.992041863e-05f,
+    /* 24,14 */ +3.317859772e-05f, -6.884149383e-05f, -2.595470071e-04f, -2.633988510e-04f, +2.333982604e-04f, +1.078579553e-03f, +1.403322383e-03f, +1.653667139e-04f, -2.737690485e-03f, -5.834433801e-03f, -6.830374341e-03f, -4.401480442e-03f, +4.953359135e-04f, +5.106971374e-03f, +6.891993065e-03f, +5.346195092e-03f, +2.114193296e-03f, -5.422031866e-04f, -1.435932770e-03f, -9.254138922e-04f, -9.900949096e-05f, +2.944876029e-04f, +2.267040256e-04f, +3.887878147e-05f,
+    /* 24,15 */ +3.491105347e-05f, -5.836581816e-05f, -2.491702023e-04f, -2.758007186e-04f, +1.868092348e-04f, +1.029081461e-03f, +1.420326139e-03f, +2.982544427e-04f, -2.528057977e-03f, -5.679465803e-03f, -6.864975932e-03f, -4.647640808e-03f, +1.652445539e-04f, +4.882970050e-03f, +6.885468951e-03f, +5.516524807e-03f, +2.320049614e-03f, -4.239072727e-04f, -1.431118485e-03f, -9.779307384e-04f, -1.419765781e-04f, +2.861422699e-04f, +2.381906908e-04f, +4.836862817e-05f,
+    /* 24, 0 */ +1.364396009e-04f, +7.446376994e-05f, -3.699603221e-04f, -7.278325124e-04f, -5.051635567e-05f, +1.645033952e-03f, +2.378022613e-03f, -2.243714932e-04f, -5.680096534e-03f, -9.704626250e-03f, -7.823014841e-03f, -2.751390883e-04f, +7.480820734e-03f, +9.788905453e-03f, +6.030582333e-03f, +5.101196376e-04f, -2.328731157e-03f, -1.748114996e-03f, -3.640531891e-05f, +7.242350145e-04f, +4.042879967e-04f, -5.742334247e-05f, -1.414906982e-04f, -2.710774794e-05f,
+    /* 24, 1 */ +1.307026563e-04f, +8.975162518e-05f, -3.355241044e-04f, -7.270603554e-04f, -1.326866063e-04f, +1.538422151e-03f, +2.413247311e-03f, +4.875121157e-05f, -5.324161431e-03f, -9.595517291e-03f, -8.141086512e-03f, -8.245287223e-04f, +7.115425083e-03f, +9.847646625e-03f, +6.374200107e-03f, +8.077901021e-04f, -2.264974711e-03f, -1.846937004e-03f, -1.278166248e-04f, +7.160485626e-04f, +4.382702758e-04f, -3.863673145e-05f, -1.457592711e-04f, -3.374854096e-05f,
+    /* 24, 2 */ +1.243758393e-04f, +1.032931784e-04f, -3.012044148e-04f, -7.221532843e-04f, -2.098818030e-04f, +1.428995714e-03f, +2.434853697e-03f, +3.086181402e-04f, -4.964188024e-03f, -9.462372525e-03f, -8.434212217e-03f, -1.371256439e-03f, +6.727842835e-03f, +9.880231223e-03f, +6.709529590e-03f, +1.116608515e-03f, -2.186408722e-03f, -1.940762958e-03f, -2.234175210e-04f, +7.030713845e-04f, +4.716595396e-04f, -1.812362539e-05f, -1.491486840e-04f, -4.073257728e-05f,
+    /* 24, 3 */ +1.175537217e-04f, +1.151066589e-04f, -2.672136493e-04f, -7.133593846e-04f, -2.819161814e-04f, +1.317455363e-03f, +2.443336794e-03f, +5.546728855e-04f, -4.601573558e-03f, -9.306067159e-03f, -8.701668636e-03f, -1.913559980e-03f, +6.319179241e-03f, +9.886134123e-03f, +7.035155238e-03f, +1.435731166e-03f, -2.092745601e-03f, -2.028850662e-03f, -3.228698520e-04f, +6.851212972e-04f, +5.041985339e-04f, +4.082412249e-06f, -1.515631236e-04f, -4.801831197e-05f,
+    /* 24, 4 */ +1.103288160e-04f, +1.252215041e-04f, -2.337507561e-04f, -7.009379926e-04f, -3.486413414e-04f, +1.204483360e-03f, +2.439234434e-03f, +7.864337317e-04f, -4.237695644e-03f, -9.127552920e-03f, -8.942835031e-03f, -2.449695551e-03f, +5.890625670e-03f, +9.864926907e-03f, +7.349672574e-03f, +1.764247300e-03f, -1.983757790e-03f, -2.110456450e-03f, -4.257977068e-04f, +6.620377298e-04f, +5.356216172e-04f, +2.793344097e-05f, -1.529084143e-04f, -5.555842361e-05f,
+    /* 24, 5 */ +1.027909684e-04f, +1.336775649e-04f, -2.010005851e-04f, -6.851576168e-04f, -4.099455518e-04f, +1.090740633e-03f, +2.423123355e-03f, +1.003494037e-03f, -3.873906570e-03f, -8.927853008e-03f, -9.157195135e-03f, -2.977945083e-03f, +5.443455012e-03f, +9.816280735e-03f, +7.651694576e-03f, +2.101181791e-03f, -1.859280606e-03f, -2.184839011e-03f, -5.317880090e-04f, +6.336836952e-04f, +5.656561208e-04f, +5.336672075e-05f, -1.530928622e-04f, -6.329988035e-05f,
+    /* 24, 6 */ +9.502680145e-05f, +1.405242734e-04f, -1.691333585e-04f, -6.662938873e-04f, -4.657528710e-04f, +9.768641187e-04f, +2.395615219e-03f, +1.205522243e-03f, -3.511527821e-03f, -8.708056786e-03f, -9.344338564e-03f, -3.496623369e-03f, +4.979016698e-03f, +9.739968779e-03f, +7.939858067e-03f, +2.445498177e-03f, -1.719214825e-03f, -2.251263312e-03f, -6.403913399e-04f, +5.999476948e-04f, +5.940238143e-04f, +8.030437567e-05f, -1.520281231e-04f, -7.118405837e-05f,
+    /* 24, 7 */ +8.711921055e-05f, +1.458197836e-04f, -1.383042613e-04f, -6.446275435e-04f, -5.160220948e-04f, +8.634643034e-04f, +2.357352548e-03f, +1.392261511e-03f, -3.151844842e-03f, -8.469314215e-03f, -9.503961719e-03f, -4.004085039e-03f, +4.498731341e-03f, +9.635868210e-03f, +8.212830089e-03f, +2.796102059e-03f, -1.563529005e-03f, -2.309004616e-03f, -7.511229995e-04f, +5.607455425e-04f, +6.204424737e-04f, +1.086531499e-04f, -1.496300883e-04f, -7.914691395e-05f,
+    /* 24, 8 */ +7.914691395e-05f, +1.496300883e-04f, -1.086531499e-04f, -6.204424737e-04f, -5.607455425e-04f, +7.511229995e-04f, +2.309004616e-03f, +1.563529005e-03f, -2.796102059e-03f, -8.212830089e-03f, -9.635868210e-03f, -4.498731341e-03f, +4.004085039e-03f, +9.503961719e-03f, +8.469314215e-03f, +3.151844842e-03f, -1.392261511e-03f, -2.357352548e-03f, -8.634643034e-04f, +5.160220948e-04f, +6.446275435e-04f, +1.383042613e-04f, -1.458197836e-04f, -8.711921055e-05f,
+    /* 24, 9 */ +7.118405837e-05f, +1.520281231e-04f, -8.030437567e-05f, -5.940238143e-04f, -5.999476948e-04f, +6.403913399e-04f, +2.251263312e-03f, +1.719214825e-03f, -2.445498177e-03f, -7.939858067e-03f, -9.739968779e-03f, -4.979016698e-03f, +3.496623369e-03f, +9.344338564e-03f, +8.708056786e-03f, +3.511527821e-03f, -1.205522243e-03f, -2.395615219e-03f, -9.768641187e-04f, +4.657528710e-04f, +6.662938873e-04f, +1.691333585e-04f, -1.405242734e-04f, -9.502680145e-05f,
+    /* 24,10 */ +6.329988035e-05f, +1.530928622e-04f, -5.336672075e-05f, -5.656561208e-04f, -6.336836952e-04f, +5.317880090e-04f, +2.184839011e-03f, +1.859280606e-03f, -2.101181791e-03f, -7.651694576e-03f, -9.816280735e-03f, -5.443455012e-03f, +2.977945083e-03f, +9.157195135e-03f, +8.927853008e-03f, +3.873906570e-03f, -1.003494037e-03f, -2.423123355e-03f, -1.090740633e-03f, +4.099455518e-04f, +6.851576168e-04f, +2.010005851e-04f, -1.336775649e-04f, -1.027909684e-04f,
+    /* 24,11 */ +5.555842361e-05f, +1.529084143e-04f, -2.793344097e-05f, -5.356216172e-04f, -6.620377298e-04f, +4.257977068e-04f, +2.110456450e-03f, +1.983757790e-03f, -1.764247300e-03f, -7.349672574e-03f, -9.864926907e-03f, -5.890625670e-03f, +2.449695551e-03f, +8.942835031e-03f, +9.127552920e-03f, +4.237695644e-03f, -7.864337317e-04f, -2.439234434e-03f, -1.204483360e-03f, +3.486413414e-04f, +7.009379926e-04f, +2.337507561e-04f, -1.252215041e-04f, -1.103288160e-04f,
+    /* 24,12 */ +4.801831197e-05f, +1.515631236e-04f, -4.082412249e-06f, -5.041985339e-04f, -6.851212972e-04f, +3.228698520e-04f, +2.028850662e-03f, +2.092745601e-03f, -1.435731166e-03f, -7.035155238e-03f, -9.886134123e-03f, -6.319179241e-03f, +1.913559980e-03f, +8.701668636e-03f, +9.306067159e-03f, +4.601573558e-03f, -5.546728855e-04f, -2.443336794e-03f, -1.317455363e-03f, +2.819161814e-04f, +7.133593846e-04f, +2.672136493e-04f, -1.151066589e-04f, -1.175537217e-04f,
+    /* 24,13 */ +4.073257728e-05f, +1.491486840e-04f, +1.812362539e-05f, -4.716595396e-04f, -7.030713845e-04f, +2.234175210e-04f, +1.940762958e-03f, +2.186408722e-03f, -1.116608515e-03f, -6.709529590e-03f, -9.880231223e-03f, -6.727842835e-03f, +1.371256439e-03f, +8.434212217e-03f, +9.462372525e-03f, +4.964188024e-03f, -3.086181402e-04f, -2.434853697e-03f, -1.428995714e-03f, +2.098818030e-04f, +7.221532843e-04f, +3.012044148e-04f, -1.032931784e-04f, -1.243758393e-04f,
+    /* 24,14 */ +3.374854096e-05f, +1.457592711e-04f, +3.863673145e-05f, -4.382702758e-04f, -7.160485626e-04f, +1.278166248e-04f, +1.846937004e-03f, +2.264974711e-03f, -8.077901021e-04f, -6.374200107e-03f, -9.847646625e-03f, -7.115425083e-03f, +8.245287223e-04f, +8.141086512e-03f, +9.595517291e-03f, +5.324161431e-03f, -4.875121157e-05f, -2.413247311e-03f, -1.538422151e-03f, +1.326866063e-04f, +7.270603554e-04f, +3.355241044e-04f, -8.975162518e-05f, -1.307026563e-04f,
+    /* 24,15 */ +2.710774794e-05f, +1.414906982e-04f, +5.742334247e-05f, -4.042879967e-04f, -7.242350145e-04f, +3.640531891e-05f, +1.748114996e-03f, +2.328731157e-03f, -5.101196376e-04f, -6.030582333e-03f, -9.788905453e-03f, -7.480820734e-03f, +2.751390883e-04f, +7.823014841e-03f, +9.704626250e-03f, +5.680096534e-03f, +2.243714932e-04f, -2.378022613e-03f, -1.645033952e-03f, +5.051635567e-05f, +7.278325124e-04f, +3.699603221e-04f, -7.446376994e-05f, -1.364396009e-04f,
+    /* 20, 0 */ +1.366654441e-04f, -5.248309364e-04f, -9.559425272e-04f, +4.495080153e-04f, +2.846407623e-03f, +1.989454068e-03f, -4.491151594e-03f, -1.156100448e-02f, -1.065698581e-02f, -3.895768346e-04f, +1.023895907e-02f, +1.180960294e-02f, +5.007407400e-03f, -1.738511442e-03f, -2.938517986e-03f, -6.019203323e-04f, +9.329195550e-04f, +5.763302237e-04f, -1.134902041e-04f, -1.824770389e-04f,
+    /* 20, 1 */ +1.568890194e-04f, -4.728495798e-04f, -9.708996289e-04f, +3.024354413e-04f, +2.741035078e-03f, +2.215509786e-03f, -3.978754642e-03f, -1.127853170e-02f, -1.103432131e-02f, -1.167153605e-03f, +9.781544627e-03f, +1.202253469e-02f, +5.525210549e-03f, -1.462933465e-03f, -3.016077094e-03f, -7.588827792e-04f, +9.015285321e-04f, +6.268920900e-04f, -8.735502929e-05f, -1.921860197e-04f,
+    /* 20, 2 */ +1.741940978e-04f, -4.208194393e-04f, -9.781360984e-04f, +1.614183836e-04f, +2.623714429e-03f, +2.416571115e-03f, -3.472449249e-03f, -1.096411041e-02f, -1.136986548e-02f, -1.940007910e-03f, +9.286243980e-03f, +1.219815240e-02f, +6.042182027e-03f, -1.163118517e-03f, -3.077830056e-03f, -9.195341683e-04f, +8.615147935e-04f, +6.760417132e-04f, -5.827810709e-05f, -2.008073985e-04f,
+    /* 20, 3 */ +1.886370492e-04f, -3.691494362e-04f, -9.780356474e-04f, +2.709710175e-05f, +2.495775707e-03f, +2.592671089e-03f, -2.974378699e-03f, -1.061978749e-02f, -1.166272581e-02f, -2.705018824e-03f, +8.754750074e-03f, +1.233496472e-02f, +6.555887236e-03f, -8.396136973e-04f, -3.122565398e-03f, -1.082944596e-03f, +8.126754773e-04f, +7.232876858e-04f, -2.630548656e-05f, -2.081598890e-04f,
+    /* 20, 4 */ +2.002956356e-04f, -3.182221327e-04f, -9.710157868e-04f, -9.996474913e-05f, +2.358556029e-03f, +2.743978910e-03f, -2.486586970e-03f, -1.024771819e-02f, -1.191222039e-02f, -3.459106327e-03f, +8.188939591e-03f, +1.243164652e-02f, +7.063848473e-03f, -4.931158341e-04f, -3.149124311e-03f, -1.248118895e-03f, +7.548636055e-04f, +7.681251779e-04f, +8.487693398e-06f, -2.140618814e-04f,
+    /* 20, 5 */ +2.092671085e-04f, -2.683920446e-04f, -9.575231021e-04f, -2.192806382e-04f, +2.213390969e-03f, +2.870794883e-03f, -2.011009640e-03f, -9.850152742e-03f, -1.211787969e-02f, -4.199247312e-03f, +7.590864160e-03f, +1.248704815e-02f, +7.563557889e-03f, -1.244715801e-04f, -3.156409832e-03f, -1.414000676e-03f, +6.879919170e-04f, +8.100393630e-04f, +4.599617124e-05f, -2.183332212e-04f,
+    /* 20, 6 */ +2.156662323e-04f, -2.199842607e-04f, -9.380285157e-04f, -3.304411223e-04f, +2.061606212e-03f, +2.973544666e-03f, -1.549465619e-03f, -9.429422833e-03f, -1.227944713e-02f, -4.922491262e-03f, +6.962740532e-03f, +1.250020393e-02f, +8.052490864e-03f, +2.653234199e-04f, -3.143395899e-03f, -1.579476941e-03f, +6.120364145e-04f, +8.485090918e-04f, +8.608374363e-05f, -2.207970734e-04f,
+    /* 20, 7 */ +2.196232573e-04f, -1.732933680e-04f, -9.130225739e-04f, -4.331132727e-04f, +1.904509553e-03f, +3.052772891e-03f, -1.103649750e-03f, -8.987927630e-03f, -1.239687839e-02f, -5.625975475e-03f, +6.306939763e-03f, +1.247033951e-02f, +8.528119711e-03f, +6.751263085e-04f, -3.109136222e-03f, -1.743383273e-03f, +5.270395872e-04f, +8.830107920e-04f, +1.285826773e-04f, -2.212818602e-04f,
+    /* 20, 8 */ +2.212818602e-04f, -1.285826773e-04f, -8.830107920e-04f, -5.270395872e-04f, +1.743383273e-03f, +3.109136222e-03f, -6.751263085e-04f, -8.528119711e-03f, -1.247033951e-02f, -6.306939763e-03f, +5.625975475e-03f, +1.239687839e-02f, +8.987927630e-03f, +1.103649750e-03f, -3.052772891e-03f, -1.904509553e-03f, +4.331132727e-04f, +9.130225739e-04f, +1.732933680e-04f, -2.196232573e-04f,
+    /* 20, 9 */ +2.207970734e-04f, -8.608374363e-05f, -8.485090918e-04f, -6.120364145e-04f, +1.579476941e-03f, +3.143395899e-03f, -2.653234199e-04f, -8.052490864e-03f, -1.250020393e-02f, -6.962740532e-03f, +4.922491262e-03f, +1.227944713e-02f, +9.429422833e-03f, +1.549465619e-03f, -2.973544666e-03f, -2.061606212e-03f, +3.304411223e-04f, +9.380285157e-04f, +2.199842607e-04f, -2.156662323e-04f,
+    /* 20,10 */ +2.183332212e-04f, -4.599617124e-05f, -8.100393630e-04f, -6.879919170e-04f, +1.414000676e-03f, +3.156409832e-03f, +1.244715801e-04f, -7.563557889e-03f, -1.248704815e-02f, -7.590864160e-03f, +4.199247312e-03f, +1.211787969e-02f, +9.850152742e-03f, +2.011009640e-03f, -2.870794883e-03f, -2.213390969e-03f, +2.192806382e-04f, +9.575231021e-04f, +2.683920446e-04f, -2.092671085e-04f,
+    /* 20,11 */ +2.140618814e-04f, -8.487693398e-06f, -7.681251779e-04f, -7.548636055e-04f, +1.248118895e-03f, +3.149124311e-03f, +4.931158341e-04f, -7.063848473e-03f, -1.243164652e-02f, -8.188939591e-03f, +3.459106327e-03f, +1.191222039e-02f, +1.024771819e-02f, +2.486586970e-03f, -2.743978910e-03f, -2.358556029e-03f, +9.996474913e-05f, +9.710157868e-04f, +3.182221327e-04f, -2.002956356e-04f,
+    /* 20,12 */ +2.081598890e-04f, +2.630548656e-05f, -7.232876858e-04f, -8.126754773e-04f, +1.082944596e-03f, +3.122565398e-03f, +8.396136973e-04f, -6.555887236e-03f, -1.233496472e-02f, -8.754750074e-03f, +2.705018824e-03f, +1.166272581e-02f, +1.061978749e-02f, +2.974378699e-03f, -2.592671089e-03f, -2.495775707e-03f, -2.709710175e-05f, +9.780356474e-04f, +3.691494362e-04f, -1.886370492e-04f,
+    /* 20,13 */ +2.008073985e-04f, +5.827810709e-05f, -6.760417132e-04f, -8.615147935e-04f, +9.195341683e-04f, +3.077830056e-03f, +1.163118517e-03f, -6.042182027e-03f, -1.219815240e-02f, -9.286243980e-03f, +1.940007910e-03f, +1.136986548e-02f, +1.096411041e-02f, +3.472449249e-03f, -2.416571115e-03f, -2.623714429e-03f, -1.614183836e-04f, +9.781360984e-04f, +4.208194393e-04f, -1.741940978e-04f,
+    /* 20,14 */ +1.921860197e-04f, +8.735502929e-05f, -6.268920900e-04f, -9.015285321e-04f, +7.588827792e-04f, +3.016077094e-03f, +1.462933465e-03f, -5.525210549e-03f, -1.202253469e-02f, -9.781544627e-03f, +1.167153605e-03f, +1.103432131e-02f, +1.127853170e-02f, +3.978754642e-03f, -2.215509786e-03f, -2.741035078e-03f, -3.024354413e-04f, +9.708996289e-04f, +4.728495798e-04f, -1.568890194e-04f,
+    /* 20,15 */ +1.824770389e-04f, +1.134902041e-04f, -5.763302237e-04f, -9.329195550e-04f, +6.019203323e-04f, +2.938517986e-03f, +1.738511442e-03f, -5.007407400e-03f, -1.180960294e-02f, -1.023895907e-02f, +3.895768346e-04f, +1.065698581e-02f, +1.156100448e-02f, +4.491151594e-03f, -1.989454068e-03f, -2.846407623e-03f, -4.495080153e-04f, +9.559425272e-04f, +5.248309364e-04f, -1.366654441e-04f,
+    /* 20, 0 */ +2.228492143e-04f, +8.155042897e-05f, -9.008994790e-04f, -8.358434283e-04f, +2.057950411e-03f, +3.687980724e-03f, -2.439509438e-03f, -1.276984908e-02f, -1.372824692e-02f, -5.233437973e-04f, +1.325794606e-02f, +1.324423486e-02f, +3.079014715e-03f, -3.569583683e-03f, -2.275574997e-03f, +7.329307333e-04f, +9.595727172e-04f, -3.951670647e-05f, -2.357435643e-04f, +0.000000000e+00f,
+    /* 20, 1 */ +2.085936177e-04f, +1.192685572e-04f, -8.383784628e-04f, -9.252931617e-04f, +1.836793834e-03f, +3.772148819e-03f, -1.820843931e-03f, -1.225552529e-02f, -1.413563751e-02f, -1.567452511e-03f, +1.272631251e-02f, +1.367510758e-02f, +3.736377939e-03f, -3.415952420e-03f, -2.487640644e-03f, +6.166326985e-04f, +1.013551226e-03f, +6.721135679e-06f, -2.469830254e-04f, +3.513221827e-04f,
+    /* 20, 2 */ +1.932641301e-04f, +1.526114972e-04f, -7.728409668e-04f, -1.001322392e-03f, +1.614057279e-03f, +3.823286477e-03f, -1.225775962e-03f, -1.170500117e-02f, -1.447891891e-02f, -2.603840968e-03f, +1.213529571e-02f, +1.405908160e-02f, +4.408408028e-03f, -3.226289363e-03f, -2.692058620e-03f, +4.871526668e-04f, +1.061979909e-03f, +5.699759108e-05f, -2.562708188e-04f, -2.243392330e-05f,
+    /* 20, 3 */ +1.771389305e-04f, +1.815689683e-04f, -7.050956564e-04f, -1.064087220e-03f, +1.391605775e-03f, +3.842769943e-03f, -6.568264230e-04f, -1.112214974e-02f, -1.475727496e-02f, -3.627416831e-03f, +1.148720750e-02f, +1.439298630e-02f, +5.091721334e-03f, -3.000017933e-03f, -2.886692602e-03f, +3.448244648e-04f, +1.104003505e-03f, +1.110914327e-04f, -2.633104157e-04f, -3.471505729e-05f,
+    /* 20, 4 */ +1.604844080e-04f, +2.061770649e-04f, -6.359221544e-04f, -1.113850250e-03f, +1.171206475e-03f, +3.832136817e-03f, -1.162685315e-04f, -1.051095143e-02f, -1.497027375e-02f, -4.633169088e-03f, +1.078471010e-02f, +1.467389068e-02f, +5.782760145e-03f, -2.736794515e-03f, -3.069373786e-03f, +1.901162062e-04f, +1.138774993e-03f, +1.687245284e-04f, -2.678091338e-04f, -4.805250238e-05f,
+    /* 20, 5 */ +1.435528424e-04f, +2.265149998e-04f, -5.660652366e-04f, -1.150972836e-03f, +9.545189950e-04f, +3.793068954e-03f, +3.938808876e-04f, -9.875465824e-03f, -1.511786634e-02f, -5.616199746e-03f, +1.003080152e-02f, +1.489912656e-02f, +6.477812874e-03f, -2.436518983e-03f, -3.237916857e-03f, +2.363306300e-05f, +1.165464351e-03f, +2.295611999e-04f, -2.694819135e-04f, -6.235351094e-05f,
+    /* 20, 6 */ +1.265803873e-04f, +2.427015763e-04f, -4.962296614e-04f, -1.175906803e-03f, +7.430870045e-04f, +3.727374795e-03f, +8.718680321e-04f, -9.219803451e-03f, -1.520038286e-02f, -6.571754682e-03f, +9.228798617e-03f, +1.506631023e-02f, +7.173035830e-03f, -2.099343642e-03f, -3.390136682e-03f, -1.538810619e-04f, +1.183267602e-03f, +2.932081598e-04f, -2.680552395e-04f, -7.750368078e-05f,
+    /* 20, 7 */ +1.097853637e-04f, +2.548914413e-04f, -4.270756535e-04f, -1.189185758e-03f, +5.383311068e-04f, +3.636971298e-03f, +1.316206650e-03f, -8.548097577e-03f, -1.521852609e-02f, -7.495253444e-03f, +8.382317770e-03f, +1.517336238e-02f, +7.864476409e-03f, -1.725680482e-03f, -3.523865616e-03f, -3.415430197e-04f, +1.191416067e-03f, +3.592150564e-04f, -2.632711715e-04f, -9.336686615e-05f,
+    /* 20, 8 */ +9.336686615e-05f, +2.632711715e-04f, -3.592150564e-04f, -1.191416067e-03f, +3.415430197e-04f, +3.523865616e-03f, +1.725680482e-03f, -7.864476409e-03f, -1.517336238e-02f, -8.382317770e-03f, +7.495253444e-03f, +1.521852609e-02f, +8.548097577e-03f, -1.316206650e-03f, -3.636971298e-03f, -5.383311068e-04f, +1.189185758e-03f, +4.270756535e-04f, -2.548914413e-04f, -1.097853637e-04f,
+    /* 20, 9 */ +7.750368078e-05f, +2.680552395e-04f, -2.932081598e-04f, -1.183267602e-03f, +1.538810619e-04f, +3.390136682e-03f, +2.099343642e-03f, -7.173035830e-03f, -1.506631023e-02f, -9.228798617e-03f, +6.571754682e-03f, +1.520038286e-02f, +9.219803451e-03f, -8.718680321e-04f, -3.727374795e-03f, -7.430870045e-04f, +1.175906803e-03f, +4.962296614e-04f, -2.427015763e-04f, -1.265803873e-04f,
+    /* 20,10 */ +6.235351094e-05f, +2.694819135e-04f, -2.295611999e-04f, -1.165464351e-03f, -2.363306300e-05f, +3.237916857e-03f, +2.436518983e-03f, -6.477812874e-03f, -1.489912656e-02f, -1.003080152e-02f, +5.616199746e-03f, +1.511786634e-02f, +9.875465824e-03f, -3.938808876e-04f, -3.793068954e-03f, -9.545189950e-04f, +1.150972836e-03f, +5.660652366e-04f, -2.265149998e-04f, -1.435528424e-04f,
+    /* 20,11 */ +4.805250238e-05f, +2.678091338e-04f, -1.687245284e-04f, -1.138774993e-03f, -1.901162062e-04f, +3.069373786e-03f, +2.736794515e-03f, -5.782760145e-03f, -1.467389068e-02f, -1.078471010e-02f, +4.633169088e-03f, +1.497027375e-02f, +1.051095143e-02f, +1.162685315e-04f, -3.832136817e-03f, -1.171206475e-03f, +1.113850250e-03f, +6.359221544e-04f, -2.061770649e-04f, -1.604844080e-04f,
+    /* 20,12 */ +3.471505729e-05f, +2.633104157e-04f, -1.110914327e-04f, -1.104003505e-03f, -3.448244648e-04f, +2.886692602e-03f, +3.000017933e-03f, -5.091721334e-03f, -1.439298630e-02f, -1.148720750e-02f, +3.627416831e-03f, +1.475727496e-02f, +1.112214974e-02f, +6.568264230e-04f, -3.842769943e-03f, -1.391605775e-03f, +1.064087220e-03f, +7.050956564e-04f, -1.815689683e-04f, -1.771389305e-04f,
+    /* 20,13 */ +2.243392330e-05f, +2.562708188e-04f, -5.699759108e-05f, -1.061979909e-03f, -4.871526668e-04f, +2.692058620e-03f, +3.226289363e-03f, -4.408408028e-03f, -1.405908160e-02f, -1.213529571e-02f, +2.603840968e-03f, +1.447891891e-02f, +1.170500117e-02f, +1.225775962e-03f, -3.823286477e-03f, -1.614057279e-03f, +1.001322392e-03f, +7.728409668e-04f, -1.526114972e-04f, -1.932641301e-04f,
+    /* 20,14 */ -3.513221827e-04f, +2.469830254e-04f, -6.721135679e-06f, -1.013551226e-03f, -6.166326985e-04f, +2.487640644e-03f, +3.415952420e-03f, -3.736377939e-03f, -1.367510758e-02f, -1.272631251e-02f, +1.567452511e-03f, +1.413563751e-02f, +1.225552529e-02f, +1.820843931e-03f, -3.772148819e-03f, -1.836793834e-03f, +9.252931617e-04f, +8.383784628e-04f, -1.192685572e-04f, -2.085936177e-04f,
+    /* 20,15 */ +0.000000000e+00f, +2.357435643e-04f, +3.951670647e-05f, -9.595727172e-04f, -7.329307333e-04f, +2.275574997e-03f, +3.569583683e-03f, -3.079014715e-03f, -1.324423486e-02f, -1.325794606e-02f, +5.233437973e-04f, +1.372824692e-02f, +1.276984908e-02f, +2.439509438e-03f, -3.687980724e-03f, -2.057950411e-03f, +8.358434283e-04f, +9.008994790e-04f, -8.155042897e-05f, -2.228492143e-04f,
+    /* 20, 0 */ +1.941987182e-05f, +3.146481294e-04f, -2.345645569e-04f, -1.414667200e-03f, +5.144442975e-04f, +4.454307224e-03f, +1.983750799e-04f, -1.327145644e-02f, -1.714303646e-02f, -6.846700315e-04f, +1.665178821e-02f, +1.403392762e-02f, +4.892879248e-04f, -4.540173148e-03f, -7.773192529e-04f, +1.425286503e-03f, +3.160682424e-04f, -3.205185770e-04f, -3.476344875e-05f, +0.000000000e+00f,
+    /* 20, 1 */ -3.929324583e-04f, +3.051602666e-04f, -1.573970191e-04f, -1.390281543e-03f, +2.638941923e-04f, +4.333213577e-03f, +8.411158857e-04f, -1.246868983e-02f, -1.754185168e-02f, -2.049974665e-03f, +1.606968443e-02f, +1.474987505e-02f, +1.218921159e-03f, -4.587812221e-03f, -1.050600845e-03f, +1.421006956e-03f, +4.012475422e-04f, -3.223256966e-04f, -5.166761157e-05f, +0.000000000e+00f,
+    /* 20, 2 */ +0.000000000e+00f, +2.925136688e-04f, -8.512788201e-05f, -1.353337804e-03f, +2.735561011e-05f, +4.180044295e-03f, +1.436437300e-03f, -1.163198941e-02f, -1.784731333e-02f, -3.403203680e-03f, +1.539895444e-02f, +1.541325656e-02f, +1.987128326e-03f, -4.594412557e-03f, -1.332146559e-03f, +1.400789491e-03f, +4.893452569e-04f, -3.196436833e-04f, -7.000071077e-05f, +0.000000000e+00f,
+    /* 20, 3 */ +0.000000000e+00f, +2.771727451e-04f, -1.822077520e-05f, -1.305102482e-03f, -1.937209193e-04f, +3.998069961e-03f, +1.982303068e-03f, -1.076779718e-02f, -1.805915757e-02f, -4.736408619e-03f, +1.464246859e-02f, +1.601826823e-02f, +2.790083541e-03f, -4.557383277e-03f, -1.619599483e-03f, +1.363706016e-03f, +5.795104543e-04f, -3.120743969e-04f, -8.959420873e-05f, +0.000000000e+00f,
+    /* 20, 4 */ +0.000000000e+00f, +2.596009711e-04f, +4.295843246e-05f, -1.246883037e-03f, -3.981230344e-04f, +3.790645354e-03f, +2.477139789e-03f, -9.882582946e-03f, -1.817777152e-02f, -6.041793017e-03f, +1.380372215e-02f, +1.655939544e-02f, +3.623550339e-03f, -4.474387395e-03f, -1.910400883e-03f, +1.308956662e-03f, +6.708027600e-04f, -2.992550459e-04f, -1.102423612e-04f, +0.000000000e+00f,
+    /* 20, 5 */ +0.000000000e+00f, +2.402546692e-04f, +9.813963752e-05f, -1.180011107e-03f, -5.848760724e-04f, +3.561175652e-03f, +2.919834686e-03f, -8.982792490e-03f, -1.820418220e-02f, -7.311771311e-03f, +1.288681385e-02f, +1.703146227e-02f, +4.482904855e-03f, -4.343373467e-03f, -2.201805423e-03f, +1.235886510e-03f, +7.621980241e-04f, -2.808658988e-04f, -1.317024534e-04f, +0.000000000e+00f,
+    /* 20, 6 */ +0.000000000e+00f, +2.195772899e-04f, +1.471454599e-04f, -1.105826337e-03f, -7.532402115e-04f, +3.313083439e-03f, +3.309729355e-03f, -8.074797022e-03f, -1.814004021e-02f, -8.539025902e-03f, +1.189641917e-02f, +1.742967891e-02f, +5.363163073e-03f, -4.162605659e-03f, -2.490898970e-03f, +1.144001586e-03f, +8.525953702e-04f, -2.566379213e-04f, -1.536956226e-04f, +0.000000000e+00f,
+    /* 20, 7 */ +0.000000000e+00f, +1.979942392e-04f, +1.898872476e-04f, -1.025661016e-03f, -9.027053553e-04f, +3.049776817e-03f, +3.646609643e-03f, -7.164844319e-03f, -1.798759853e-02f, -9.716561844e-03f, +1.083775871e-02f, +1.774968640e-02f, +6.259011965e-03f, -3.930691879e-03f, -2.774618906e-03f, +1.032983905e-03f, +9.408256132e-04f, -2.263602294e-04f, -1.759082929e-04f, +0.000000000e+00f,
+    /* 20, 8 */ +0.000000000e+00f, +1.759082929e-04f, +2.263602294e-04f, -9.408256132e-04f, -1.032983905e-03f, +2.774618906e-03f, +3.930691879e-03f, -6.259011965e-03f, -1.774968640e-02f, -1.083775871e-02f, +9.716561844e-03f, +1.798759853e-02f, +7.164844319e-03f, -3.646609643e-03f, -3.049776817e-03f, +9.027053553e-04f, +1.025661016e-03f, -1.898872476e-04f, -1.979942392e-04f, +0.000000000e+00f,
+    /* 20, 9 */ +0.000000000e+00f, +1.536956226e-04f, +2.566379213e-04f, -8.525953702e-04f, -1.144001586e-03f, +2.490898970e-03f, +4.162605659e-03f, -5.363163073e-03f, -1.742967891e-02f, -1.189641917e-02f, +8.539025902e-03f, +1.814004021e-02f, +8.074797022e-03f, -3.309729355e-03f, -3.313083439e-03f, +7.532402115e-04f, +1.105826337e-03f, -1.471454599e-04f, -2.195772899e-04f, +0.000000000e+00f,
+    /* 20,10 */ +0.000000000e+00f, +1.317024534e-04f, +2.808658988e-04f, -7.621980241e-04f, -1.235886510e-03f, +2.201805423e-03f, +4.343373467e-03f, -4.482904855e-03f, -1.703146227e-02f, -1.288681385e-02f, +7.311771311e-03f, +1.820418220e-02f, +8.982792490e-03f, -2.919834686e-03f, -3.561175652e-03f, +5.848760724e-04f, +1.180011107e-03f, -9.813963752e-05f, -2.402546692e-04f, +0.000000000e+00f,
+    /* 20,11 */ +0.000000000e+00f, +1.102423612e-04f, +2.992550459e-04f, -6.708027600e-04f, -1.308956662e-03f, +1.910400883e-03f, +4.474387395e-03f, -3.623550339e-03f, -1.655939544e-02f, -1.380372215e-02f, +6.041793017e-03f, +1.817777152e-02f, +9.882582946e-03f, -2.477139789e-03f, -3.790645354e-03f, +3.981230344e-04f, +1.246883037e-03f, -4.295843246e-05f, -2.596009711e-04f, +0.000000000e+00f,
+    /* 20,12 */ +0.000000000e+00f, +8.959420873e-05f, +3.120743969e-04f, -5.795104543e-04f, -1.363706016e-03f, +1.619599483e-03f, +4.557383277e-03f, -2.790083541e-03f, -1.601826823e-02f, -1.464246859e-02f, +4.736408619e-03f, +1.805915757e-02f, +1.076779718e-02f, -1.982303068e-03f, -3.998069961e-03f, +1.937209193e-04f, +1.305102482e-03f, +1.822077520e-05f, -2.771727451e-04f, +0.000000000e+00f,
+    /* 20,13 */ +0.000000000e+00f, +7.000071077e-05f, +3.196436833e-04f, -4.893452569e-04f, -1.400789491e-03f, +1.332146559e-03f, +4.594412557e-03f, -1.987128326e-03f, -1.541325656e-02f, -1.539895444e-02f, +3.403203680e-03f, +1.784731333e-02f, +1.163198941e-02f, -1.436437300e-03f, -4.180044295e-03f, -2.735561011e-05f, +1.353337804e-03f, +8.512788201e-05f, -2.925136688e-04f, +0.000000000e+00f,
+    /* 20,14 */ +0.000000000e+00f, +5.166761157e-05f, +3.223256966e-04f, -4.012475422e-04f, -1.421006956e-03f, +1.050600845e-03f, +4.587812221e-03f, -1.218921159e-03f, -1.474987505e-02f, -1.606968443e-02f, +2.049974665e-03f, +1.754185168e-02f, +1.246868983e-02f, -8.411158857e-04f, -4.333213577e-03f, -2.638941923e-04f, +1.390281543e-03f, +1.573970191e-04f, -3.051602666e-04f, +3.929324583e-04f,
+    /* 20,15 */ +0.000000000e+00f, +3.476344875e-05f, +3.205185770e-04f, -3.160682424e-04f, -1.425286503e-03f, +7.773192529e-04f, +4.540173148e-03f, -4.892879248e-04f, -1.403392762e-02f, -1.665178821e-02f, +6.846700315e-04f, +1.714303646e-02f, +1.327145644e-02f, -1.983750799e-04f, -4.454307224e-03f, -5.144442975e-04f, +1.414667200e-03f, +2.345645569e-04f, -3.146481294e-04f, -1.941987182e-05f,
+    /* 16, 0 */ +3.215659774e-04f, -1.081239301e-03f, -1.047044785e-03f, +4.045780572e-03f, +3.005074105e-03f, -1.291342297e-02f, -2.083886340e-02f, -8.761305366e-04f, +2.037274022e-02f, +1.401097590e-02f, -2.379335663e-03f, -4.351475252e-03f, +8.522542940e-04f, +1.190910327e-03f, -2.874725537e-04f, -1.571395541e-04f,
+    /* 16, 1 */ +3.474336395e-04f, -9.673171402e-04f, -1.215210440e-03f, +3.716713245e-03f, +3.558195313e-03f, -1.178473019e-02f, -2.117503726e-02f, -2.622305580e-03f, +1.977768827e-02f, +1.506763496e-02f, -1.682580557e-03f, -4.628640452e-03f, +6.313208395e-04f, +1.294421768e-03f, -2.448738999e-04f, -1.853334990e-04f,
+    /* 16, 2 */ +3.654544998e-04f, -8.509694882e-04f, -1.356620316e-03f, +3.369379821e-03f, +4.037840451e-03f, -1.063463040e-02f, -2.138131359e-02f, -4.350277043e-03f, +1.905580462e-02f, +1.607371744e-02f, -9.171630004e-04f, -4.872124601e-03f, +3.850930357e-04f, +1.389803701e-03f, -1.936024914e-04f, -2.137577131e-04f,
+    /* 16, 3 */ +3.760939096e-04f, -7.339202470e-04f, -1.471475134e-03f, +3.008783957e-03f, +4.443873126e-03f, -9.472744965e-03f, -2.145880202e-02f, -6.048090342e-03f, +1.821025632e-02f, +1.701970579e-02f, -8.619500088e-05f, -5.076839304e-03f, +1.147982409e-04f, +1.475048300e-03f, -1.336143134e-04f, -2.418805517e-04f,
+    /* 16, 4 */ +3.798900997e-04f, -6.177751723e-04f, -1.560284979e-03f, +2.639777229e-03f, +4.776853126e-03f, -8.308496634e-03f, -2.140964525e-02f, -7.704060609e-03f, +1.724526338e-02f, +1.789634168e-02f, +8.064574864e-04f, -5.237819035e-03f, -1.779495980e-04f, +1.548135431e-03f, -6.499930382e-05f, -2.691226085e-04f,
+    /* 16, 5 */ +3.774404835e-04f, -5.040080805e-04f, -1.623844377e-03f, +2.267013831e-03f, +5.038003695e-03f, -7.151026424e-03f, -2.123698452e-02f, -9.306876654e-03f, +1.616607109e-02f, +1.869471933e-02f, +1.756186609e-03f, -5.350281937e-03f, -4.911465534e-04f, +1.607060019e-03f, +1.200956190e-05f, -2.948629835e-04f,
+    /* 16, 6 */ +3.693879948e-04f, -3.939497146e-04f, -1.663205192e-03f, +1.894909522e-03f, +5.229172900e-03f, -6.009115326e-03f, -2.094491570e-02f, -1.084570103e-02f, +1.497891218e-02f, +1.940637710e-02f, +2.757662946e-03f, -5.409691072e-03f, -8.224000281e-04f, +1.649860923e-03f, +9.702877105e-05f, -3.184467584e-04f,
+    /* 16, 7 */ +3.564076658e-04f, -2.887792732e-04f, -1.679647798e-03f, +1.527605146e-03f, +5.352789702e-03f, -4.891111570e-03f, -2.053843663e-02f, -1.231026521e-02f, +1.369095882e-02f, +2.002338639e-02f, +3.804864118e-03f, -5.411815390e-03f, -1.168934972e-03f, +1.674650966e-03f, +1.895185724e-04f, -3.391936346e-04f,
+    /* 16, 8 */ +3.391936346e-04f, -1.895185724e-04f, -1.674650966e-03f, +1.168934972e-03f, +5.411815390e-03f, -3.804864118e-03f, -2.002338639e-02f, -1.369095882e-02f, +1.231026521e-02f, +2.053843663e-02f, +4.891111570e-03f, -5.352789702e-03f, -1.527605146e-03f, +1.679647798e-03f, +2.887792732e-04f, -3.564076658e-04f,
+    /* 16, 9 */ +3.184467584e-04f, -9.702877105e-05f, -1.649860923e-03f, +8.224000281e-04f, +5.409691072e-03f, -2.757662946e-03f, -1.940637710e-02f, -1.497891218e-02f, +1.084570103e-02f, +2.094491570e-02f, +6.009115326e-03f, -5.229172900e-03f, -1.894909522e-03f, +1.663205192e-03f, +3.939497146e-04f, -3.693879948e-04f,
+    /* 16,10 */ +2.948629835e-04f, -1.200956190e-05f, -1.607060019e-03f, +4.911465534e-04f, +5.350281937e-03f, -1.756186609e-03f, -1.869471933e-02f, -1.616607109e-02f, +9.306876654e-03f, +2.123698452e-02f, +7.151026424e-03f, -5.038003695e-03f, -2.267013831e-03f, +1.623844377e-03f, +5.040080805e-04f, -3.774404835e-04f,
+    /* 16,11 */ +2.691226085e-04f, +6.499930382e-05f, -1.548135431e-03f, +1.779495980e-04f, +5.237819035e-03f, -8.064574864e-04f, -1.789634168e-02f, -1.724526338e-02f, +7.704060609e-03f, +2.140964525e-02f, +8.308496634e-03f, -4.776853126e-03f, -2.639777229e-03f, +1.560284979e-03f, +6.177751723e-04f, -3.798900997e-04f,
+    /* 16,12 */ +2.418805517e-04f, +1.336143134e-04f, -1.475048300e-03f, -1.147982409e-04f, +5.076839304e-03f, +8.619500088e-05f, -1.701970579e-02f, -1.821025632e-02f, +6.048090342e-03f, +2.145880202e-02f, +9.472744965e-03f, -4.443873126e-03f, -3.008783957e-03f, +1.471475134e-03f, +7.339202470e-04f, -3.760939096e-04f,
+    /* 16,13 */ +2.137577131e-04f, +1.936024914e-04f, -1.389803701e-03f, -3.850930357e-04f, +4.872124601e-03f, +9.171630004e-04f, -1.607371744e-02f, -1.905580462e-02f, +4.350277043e-03f, +2.138131359e-02f, +1.063463040e-02f, -4.037840451e-03f, -3.369379821e-03f, +1.356620316e-03f, +8.509694882e-04f, -3.654544998e-04f,
+    /* 16,14 */ +1.853334990e-04f, +2.448738999e-04f, -1.294421768e-03f, -6.313208395e-04f, +4.628640452e-03f, +1.682580557e-03f, -1.506763496e-02f, -1.977768827e-02f, +2.622305580e-03f, +2.117503726e-02f, +1.178473019e-02f, -3.558195313e-03f, -3.716713245e-03f, +1.215210440e-03f, +9.673171402e-04f, -3.474336395e-04f,
+    /* 16,15 */ +1.571395541e-04f, +2.874725537e-04f, -1.190910327e-03f, -8.522542940e-04f, +4.351475252e-03f, +2.379335663e-03f, -1.401097590e-02f, -2.037274022e-02f, +8.761305366e-04f, +2.083886340e-02f, +1.291342297e-02f, -3.005074105e-03f, -4.045780572e-03f, +1.047044785e-03f, +1.081239301e-03f, -3.215659774e-04f,
+    /* 16, 0 */ +3.737698842e-04f, -2.640449894e-04f, -1.945694549e-03f, +2.599440145e-03f, +5.499552783e-03f, -1.161604587e-02f, -2.473725459e-02f, -1.100298137e-03f, +2.435797715e-02f, +1.306966182e-02f, -5.062036618e-03f, -3.067638325e-03f, +1.905476637e-03f, +3.919780470e-04f, -3.991665042e-04f, +0.000000000e+00f,
+    /* 16, 1 */ +3.444161169e-04f, -1.448617079e-04f, -1.955541719e-03f, +2.132654952e-03f, +5.839402648e-03f, -1.015371093e-02f, -2.494083956e-02f, -3.291998323e-03f, +2.380252288e-02f, +1.450063212e-02f, -4.525267650e-03f, -3.530814272e-03f, +1.832921116e-03f, +5.273022833e-04f, -4.195866486e-04f, +0.000000000e+00f,
+    /* 16, 2 */ +3.121018536e-04f, -3.553225965e-05f, -1.937280777e-03f, +1.673279684e-03f, +6.084169165e-03f, -8.696195593e-03f, -2.497087446e-02f, -5.457102987e-03f, +2.307209479e-02f, +1.589478690e-02f, -3.888719495e-03f, -3.982156136e-03f, +1.726408630e-03f, +6.684076945e-04f, -4.340068349e-04f, +0.000000000e+00f,
+    /* 16, 3 */ +2.777843660e-04f, +6.309259068e-05f, -1.893417136e-03f, +1.226820211e-03f, +6.237358144e-03f, -7.256513778e-03f, -2.483111182e-02f, -7.578189778e-03f, +2.216959356e-02f, +1.723786448e-02f, -3.152978451e-03f, -4.414545490e-03f, +1.584716951e-03f, +8.134406195e-04f, -4.414195390e-04f, +4.634120047e-04f,
+    /* 16, 4 */ +2.423668462e-04f, +1.504100330e-04f, -1.826645633e-03f, +7.982477790e-04f, +6.303316031e-03f, -5.847027899e-03f, -2.452684878e-02f, -9.638294429e-03f, +2.109960841e-02f, +1.851566754e-02f, -2.319783877e-03f, -4.820635148e-03f, +1.407067591e-03f, +9.603162700e-04f, -4.408546251e-04f, -2.338334874e-05f,
+    /* 16, 5 */ +2.066858426e-04f, +2.260561294e-04f, -1.739797615e-03f, +3.919648528e-04f, +6.287140435e-03f, -4.479333074e-03f, -2.406484480e-02f, -1.162108621e-02f, +1.986838848e-02f, +1.971422226e-02f, -1.392055451e-03f, -5.192933984e-03f, +1.193168502e-03f, +1.106736135e-03f, -4.314017719e-04f, -4.782938304e-05f,
+    /* 16, 6 */ +1.715009195e-04f, +2.898933732e-04f, -1.635789255e-03f, +1.178042715e-05f, +6.194584811e-03f, -3.164153635e-03f, -2.345322399e-02f, -1.351103572e-02f, +1.848379497e-02f, +2.081993840e-02f, -3.739065234e-04f, -5.523897843e-03f, +9.432519997e-04f, +1.250210274e-03f, -4.122335402e-04f, -7.520965874e-05f,
+    /* 16, 7 */ +1.374865801e-04f, +3.419953130e-04f, -1.517571800e-03f, -3.391052954e-04f, +6.031958779e-03f, -1.911252903e-03f, -2.270136331e-02f, -1.529357304e-02f, +1.695523429e-02f, +2.181976838e-02f, +7.293570292e-04f, -5.806025538e-03f, +6.581070752e-04f, +1.388084428e-03f, -3.826286881e-04f, -1.052264476e-04f,
+    /* 16, 8 */ +1.052264476e-04f, +3.826286881e-04f, -1.388084428e-03f, -6.581070752e-04f, +5.806025538e-03f, -7.293570292e-04f, -2.181976838e-02f, -1.695523429e-02f, +1.529357304e-02f, +2.270136331e-02f, +1.911252903e-03f, -6.031958779e-03f, +3.391052954e-04f, +1.517571800e-03f, -3.419953130e-04f, -1.374865801e-04f,
+    /* 16, 9 */ +7.520965874e-05f, +4.122335402e-04f, -1.250210274e-03f, -9.432519997e-04f, +5.523897843e-03f, +3.739065234e-04f, -2.081993840e-02f, -1.848379497e-02f, +1.351103572e-02f, +2.345322399e-02f, +3.164153635e-03f, -6.194584811e-03f, -1.178042715e-05f, +1.635789255e-03f, -2.898933732e-04f, -1.715009195e-04f,
+    /* 16,10 */ +4.782938304e-05f, +4.314017719e-04f, -1.106736135e-03f, -1.193168502e-03f, +5.192933984e-03f, +1.392055451e-03f, -1.971422226e-02f, -1.986838848e-02f, +1.162108621e-02f, +2.406484480e-02f, +4.479333074e-03f, -6.287140435e-03f, -3.919648528e-04f, +1.739797615e-03f, -2.260561294e-04f, -2.066858426e-04f,
+    /* 16,11 */ +2.338334874e-05f, +4.408546251e-04f, -9.603162700e-04f, -1.407067591e-03f, +4.820635148e-03f, +2.319783877e-03f, -1.851566754e-02f, -2.109960841e-02f, +9.638294429e-03f, +2.452684878e-02f, +5.847027899e-03f, -6.303316031e-03f, -7.982477790e-04f, +1.826645633e-03f, -1.504100330e-04f, -2.423668462e-04f,
+    /* 16,12 */ -4.634120047e-04f, +4.414195390e-04f, -8.134406195e-04f, -1.584716951e-03f, +4.414545490e-03f, +3.152978451e-03f, -1.723786448e-02f, -2.216959356e-02f, +7.578189778e-03f, +2.483111182e-02f, +7.256513778e-03f, -6.237358144e-03f, -1.226820211e-03f, +1.893417136e-03f, -6.309259068e-05f, -2.777843660e-04f,
+    /* 16,13 */ +0.000000000e+00f, +4.340068349e-04f, -6.684076945e-04f, -1.726408630e-03f, +3.982156136e-03f, +3.888719495e-03f, -1.589478690e-02f, -2.307209479e-02f, +5.457102987e-03f, +2.497087446e-02f, +8.696195593e-03f, -6.084169165e-03f, -1.673279684e-03f, +1.937280777e-03f, +3.553225965e-05f, -3.121018536e-04f,
+    /* 16,14 */ +0.000000000e+00f, +4.195866486e-04f, -5.273022833e-04f, -1.832921116e-03f, +3.530814272e-03f, +4.525267650e-03f, -1.450063212e-02f, -2.380252288e-02f, +3.291998323e-03f, +2.494083956e-02f, +1.015371093e-02f, -5.839402648e-03f, -2.132654952e-03f, +1.955541719e-03f, +1.448617079e-04f, -3.444161169e-04f,
+    /* 16,15 */ +0.000000000e+00f, +3.991665042e-04f, -3.919780470e-04f, -1.905476637e-03f, +3.067638325e-03f, +5.062036618e-03f, -1.306966182e-02f, -2.435797715e-02f, +1.100298137e-03f, +2.473725459e-02f, +1.161604587e-02f, -5.499552783e-03f, -2.599440145e-03f, +1.945694549e-03f, +2.640449894e-04f, -3.737698842e-04f,
+    /* 16, 0 */ +1.129954761e-04f, +3.969443331e-04f, -1.897918409e-03f, +5.803605804e-04f, +7.236474393e-03f, -9.385964725e-03f, -2.874576735e-02f, -1.359743295e-03f, +2.853093461e-02f, +1.118139232e-02f, -7.102956997e-03f, -1.091735034e-03f, +2.023521864e-03f, -3.328791130e-04f, -1.523656204e-04f, +0.000000000e+00f,
+    /* 16, 1 */ +7.672972562e-05f, +4.458313625e-04f, -1.753034729e-03f, +1.022461377e-04f, +7.257902118e-03f, -7.620475041e-03f, -2.873131200e-02f, -4.066570788e-03f, +2.808336987e-02f, +1.298853535e-02f, -6.850603202e-03f, -1.630677017e-03f, +2.125649126e-03f, -2.532507071e-04f, -1.941703587e-04f, +0.000000000e+00f,
+    /* 16, 2 */ +4.409159553e-05f, +4.801879450e-04f, -1.593056257e-03f, -3.378401438e-04f, +7.175055211e-03f, -5.902082228e-03f, -2.849345261e-02f, -6.735572940e-03f, +2.740215275e-02f, +1.478830973e-02f, -6.473886365e-03f, -2.190599691e-03f, +2.200171639e-03f, -1.579695096e-04f, -2.375950982e-04f, +0.000000000e+00f,
+    /* 16, 3 */ -4.855802427e-04f, +5.008892512e-04f, -1.422085430e-03f, -7.360682089e-04f, +6.996656391e-03f, -4.246700138e-03f, -2.804039906e-02f, -9.342037235e-03f, +2.648893755e-02f, +1.656098957e-02f, -5.968640123e-03f, -2.764068406e-03f, +2.243110553e-03f, -4.727037370e-05f, -2.816859426e-04f, +0.000000000e+00f,
+    /* 16, 4 */ +0.000000000e+00f, +5.090007623e-04f, -1.244075649e-03f, -1.089544232e-03f, +6.732169339e-03f, -2.668838337e-03f, -2.738254409e-02f, -1.186200034e-02f, +2.534797081e-02f, +1.828644204e-02f, -5.332184360e-03f, -3.342863857e-03f, +2.250722132e-03f, +7.826276448e-05f, -3.253590588e-04f, +0.000000000e+00f,
+    /* 16, 5 */ +0.000000000e+00f, +5.057402285e-04f, -1.062772468e-03f, -1.396293958e-03f, +6.391628670e-03f, -1.181467029e-03f, -2.653229393e-02f, -1.427253312e-02f, +2.398607480e-02f, +1.994437434e-02f, -4.563434465e-03f, -3.918061651e-03f, +2.219584858e-03f, +2.176775461e-04f, -3.674140144e-04f, +0.000000000e+00f,
+    /* 16, 6 */ +0.000000000e+00f, +4.924395299e-04f, -8.816626027e-04f, -1.655232286e-03f, +5.985469320e-03f, +2.040926394e-04f, -2.550387540e-02f, -1.655201127e-02f, +2.241259678e-02f, +2.151458926e-02f, -3.662991573e-03f, -4.480127768e-03f, +2.146686747e-03f, +3.696399185e-04f, -4.065510896e-04f, +0.000000000e+00f,
+    /* 16, 7 */ +0.000000000e+00f, +4.705072223e-04f, -7.039312026e-04f, -1.866120323e-03f, +5.524357980e-03f, +1.478252020e-03f, -2.431312252e-02f, -1.868036788e-02f, +2.063932435e-02f, +2.297724601e-02f, -2.633211707e-03f, -5.019029099e-03f, +2.029511283e-03f, +5.324276437e-04f, -4.413924818e-04f, +0.000000000e+00f,
+    /* 16, 8 */ +0.000000000e+00f, +4.413924818e-04f, -5.324276437e-04f, -2.029511283e-03f, +5.019029099e-03f, +2.633211707e-03f, -2.297724601e-02f, -2.063932435e-02f, +1.868036788e-02f, +2.431312252e-02f, -1.478252020e-03f, -5.524357980e-03f, +1.866120323e-03f, +7.039312026e-04f, -4.705072223e-04f, +0.000000000e+00f,
+    /* 16, 9 */ +0.000000000e+00f, +4.065510896e-04f, -3.696399185e-04f, -2.146686747e-03f, +4.480127768e-03f, +3.662991573e-03f, -2.151458926e-02f, -2.241259678e-02f, +1.655201127e-02f, +2.550387540e-02f, -2.040926394e-04f, -5.985469320e-03f, +1.655232286e-03f, +8.816626027e-04f, -4.924395299e-04f, +0.000000000e+00f,
+    /* 16,10 */ +0.000000000e+00f, +3.674140144e-04f, -2.176775461e-04f, -2.219584858e-03f, +3.918061651e-03f, +4.563434465e-03f, -1.994437434e-02f, -2.398607480e-02f, +1.427253312e-02f, +2.653229393e-02f, +1.181467029e-03f, -6.391628670e-03f, +1.396293958e-03f, +1.062772468e-03f, -5.057402285e-04f, +0.000000000e+00f,
+    /* 16,11 */ +0.000000000e+00f, +3.253590588e-04f, -7.826276448e-05f, -2.250722132e-03f, +3.342863857e-03f, +5.332184360e-03f, -1.828644204e-02f, -2.534797081e-02f, +1.186200034e-02f, +2.738254409e-02f, +2.668838337e-03f, -6.732169339e-03f, +1.089544232e-03f, +1.244075649e-03f, -5.090007623e-04f, +0.000000000e+00f,
+    /* 16,12 */ +0.000000000e+00f, +2.816859426e-04f, +4.727037370e-05f, -2.243110553e-03f, +2.764068406e-03f, +5.968640123e-03f, -1.656098957e-02f, -2.648893755e-02f, +9.342037235e-03f, +2.804039906e-02f, +4.246700138e-03f, -6.996656391e-03f, +7.360682089e-04f, +1.422085430e-03f, -5.008892512e-04f, +4.855802427e-04f,
+    /* 16,13 */ +0.000000000e+00f, +2.375950982e-04f, +1.579695096e-04f, -2.200171639e-03f, +2.190599691e-03f, +6.473886365e-03f, -1.478830973e-02f, -2.740215275e-02f, +6.735572940e-03f, +2.849345261e-02f, +5.902082228e-03f, -7.175055211e-03f, +3.378401438e-04f, +1.593056257e-03f, -4.801879450e-04f, -4.409159553e-05f,
+    /* 16,14 */ +0.000000000e+00f, +1.941703587e-04f, +2.532507071e-04f, -2.125649126e-03f, +1.630677017e-03f, +6.850603202e-03f, -1.298853535e-02f, -2.808336987e-02f, +4.066570788e-03f, +2.873131200e-02f, +7.620475041e-03f, -7.257902118e-03f, -1.022461377e-04f, +1.753034729e-03f, -4.458313625e-04f, -7.672972562e-05f,
+    /* 16,15 */ +0.000000000e+00f, +1.523656204e-04f, +3.328791130e-04f, -2.023521864e-03f, +1.091735034e-03f, +7.102956997e-03f, -1.118139232e-02f, -2.853093461e-02f, +1.359743295e-03f, +2.874576735e-02f, +9.385964725e-03f, -7.236474393e-03f, -5.803605804e-04f, +1.897918409e-03f, -3.969443331e-04f, -1.129954761e-04f,
+    /* 12, 0 */ -1.111572639e-03f, -1.388266820e-03f, +7.900037037e-03f, -6.320860170e-03f, -3.276049121e-02f, -1.657033928e-03f, +3.280306521e-02f, +8.402419704e-03f, -8.147060845e-03f, +9.779320530e-04f, +1.332890330e-03f, -5.705687800e-04f,
+    /* 12, 1 */ -8.925738220e-04f, -1.737094155e-03f, +7.544883174e-03f, -4.325531156e-03f, -3.242830266e-02f, -4.953502968e-03f, +3.254754550e-02f, +1.054842384e-02f, -8.272560314e-03f, +5.083818205e-04f, +1.551863117e-03f, -5.805594968e-04f,
+    /* 12, 2 */ -6.800837112e-04f, -2.023419107e-03f, +7.095763901e-03f, -2.436087367e-03f, -3.181840805e-02f, -8.197416535e-03f, +3.198906769e-02f, +1.273520115e-02f, -8.264181830e-03f, -1.673924732e-05f, +1.763414955e-03f, -5.764989135e-04f,
+    /* 12, 3 */ -4.777710304e-04f, -2.247467778e-03f, +6.567344318e-03f, -6.698479312e-04f, -3.094591156e-02f, -1.135453705e-02f, +3.112651563e-02f, +1.493747887e-02f, -8.110878239e-03f, -5.924008684e-04f, +1.962133432e-03f, -5.566237283e-04f,
+    /* 12, 4 */ -2.887507612e-04f, -2.410593616e-03f, +5.974525144e-03f, +9.583644900e-04f, -2.982883555e-02f, -1.439181272e-02f, +2.996260004e-02f, +1.712870168e-02f, -7.803165138e-03f, -1.212178752e-03f, +2.142359210e-03f, -5.193755958e-04f,
+    /* 12, 5 */ -1.155657138e-04f, -2.515168800e-03f, +5.332187403e-03f, +2.436339775e-03f, -2.848780461e-02f, -1.727782533e-02f, +2.850388027e-02f, +1.928138098e-02f, -7.333362623e-03f, -1.868272468e-03f, +2.298288008e-03f, -4.634616378e-04f,
+    /* 12, 6 */ +3.981829456e-05f, -2.564463655e-03f, +4.654950666e-03f, +3.754551105e-03f, -2.694569661e-02f, -1.998321224e-02f, +2.676072816e-02f, +2.136746929e-02f, -6.695817566e-03f, -2.551550686e-03f, +2.424083786e-03f, -3.879138191e-04f,
+    /* 12, 7 */ +1.760045094e-04f, -2.562517141e-03f, +3.956948521e-03f, +4.906180706e-03f, -2.522726725e-02f, -2.248105576e-02f, +2.474723407e-02f, +2.335875442e-02f, -5.887101657e-03f, -3.251624436e-03f, +2.514001460e-03f, -2.921456350e-04f,
+    /* 12, 8 */ +2.921456350e-04f, -2.514001460e-03f, +3.251624436e-03f, +5.887101657e-03f, -2.335875442e-02f, -2.474723407e-02f, +2.248105576e-02f, +2.522726725e-02f, -4.906180706e-03f, -3.956948521e-03f, +2.562517141e-03f, -1.760045094e-04f,
+    /* 12, 9 */ +3.879138191e-04f, -2.424083786e-03f, +2.551550686e-03f, +6.695817566e-03f, -2.136746929e-02f, -2.676072816e-02f, +1.998321224e-02f, +2.694569661e-02f, -3.754551105e-03f, -4.654950666e-03f, +2.564463655e-03f, -3.981829456e-05f,
+    /* 12,10 */ +4.634616378e-04f, -2.298288008e-03f, +1.868272468e-03f, +7.333362623e-03f, -1.928138098e-02f, -2.850388027e-02f, +1.727782533e-02f, +2.848780461e-02f, -2.436339775e-03f, -5.332187403e-03f, +2.515168800e-03f, +1.155657138e-04f,
+    /* 12,11 */ +5.193755958e-04f, -2.142359210e-03f, +1.212178752e-03f, +7.803165138e-03f, -1.712870168e-02f, -2.996260004e-02f, +1.439181272e-02f, +2.982883555e-02f, -9.583644900e-04f, -5.974525144e-03f, +2.410593616e-03f, +2.887507612e-04f,
+    /* 12,12 */ +5.566237283e-04f, -1.962133432e-03f, +5.924008684e-04f, +8.110878239e-03f, -1.493747887e-02f, -3.112651563e-02f, +1.135453705e-02f, +3.094591156e-02f, +6.698479312e-04f, -6.567344318e-03f, +2.247467778e-03f, +4.777710304e-04f,
+    /* 12,13 */ +5.764989135e-04f, -1.763414955e-03f, +1.673924732e-05f, +8.264181830e-03f, -1.273520115e-02f, -3.198906769e-02f, +8.197416535e-03f, +3.181840805e-02f, +2.436087367e-03f, -7.095763901e-03f, +2.023419107e-03f, +6.800837112e-04f,
+    /* 12,14 */ +5.805594968e-04f, -1.551863117e-03f, -5.083818205e-04f, +8.272560314e-03f, -1.054842384e-02f, -3.254754550e-02f, +4.953502968e-03f, +3.242830266e-02f, +4.325531156e-03f, -7.544883174e-03f, +1.737094155e-03f, +8.925738220e-04f,
+    /* 12,15 */ +5.705687800e-04f, -1.332890330e-03f, -9.779320530e-04f, +8.147060845e-03f, -8.402419704e-03f, -3.280306521e-02f, +1.657033928e-03f, +3.276049121e-02f, +6.320860170e-03f, -7.900037037e-03f, +1.388266820e-03f, +1.111572639e-03f,
+    /* 12, 0 */ -1.054803383e-04f, -2.744858958e-03f, +7.370914553e-03f, -2.604494739e-03f, -3.666896703e-02f, -1.994735221e-03f, +3.707596726e-02f, +4.873177855e-03f, -8.012348726e-03f, +2.554514858e-03f, +3.117958677e-04f, -3.625540242e-04f,
+    /* 12, 1 */ +7.775923585e-05f, -2.860662969e-03f, +6.648820026e-03f, -4.950753709e-04f, -3.590728113e-02f, -5.960234251e-03f, +3.711196787e-02f, +7.277671607e-03f, -8.552819277e-03f, +2.286292242e-03f, +5.385913036e-04f, -4.265219564e-04f,
+    /* 12, 2 */ +2.361482435e-04f, -2.906545541e-03f, +5.866306097e-03f, +1.435258618e-03f, -3.481183398e-02f, -9.854190425e-03f, +3.676562700e-02f, +9.791136525e-03f, -8.972352970e-03f, +1.938320040e-03f, +7.824350015e-04f, -4.875429386e-04f,
+    /* 12, 3 */ +3.687004846e-04f, -2.888204359e-03f, +5.043181884e-03f, +3.170488652e-03f, -3.340772759e-02f, -1.363013975e-02f, +3.603085731e-02f, +1.238366322e-02f, -9.251714734e-03f, +1.510362297e-03f, +1.039066351e-03f, -5.431259501e-04f,
+    /* 12, 4 */ +4.751685216e-04f, -2.812206203e-03f, +4.198484259e-03f, +4.698496481e-03f, -3.172374637e-02f, -1.724344185e-02f, +3.490702283e-02f, +1.502265637e-02f, -9.372823891e-03f, +1.003961424e-03f, +1.303424694e-03f, -5.906251216e-04f,
+    /* 12, 5 */ +5.559799195e-04f, -2.685773447e-03f, +3.350171906e-03f, +6.011087921e-03f, -2.979181001e-02f, -2.065196332e-02f, +3.339904530e-02f, +1.767328103e-02f, -9.319170237e-03f, +4.225520445e-04f, +1.569701578e-03f, -6.273034189e-04f,
+    /* 12, 6 */ +6.121620582e-04f, -2.516571985e-03f, +2.514858275e-03f, +7.103945228e-03f, -2.764638515e-02f, -2.381671619e-02f, +3.151741694e-02f, +2.029896461e-02f, -9.076221424e-03f, -2.284590483e-04f, +1.831416829e-03f, -6.504054889e-04f,
+    /* 12, 7 */ +6.452583046e-04f, -2.312505227e-03f, +1.707586806e-03f, +7.976511649e-03f, -2.532386758e-02f, -2.670244010e-02f, +2.927811806e-02f, +2.286194672e-02f, -8.631812899e-03f, -9.416507761e-04f, +2.081518390e-03f, -6.572383529e-04f,
+    /* 12, 8 */ +6.572383529e-04f, -2.081518390e-03f, +9.416507761e-04f, +8.631812899e-03f, -2.286194672e-02f, -2.927811806e-02f, +2.670244010e-02f, +2.532386758e-02f, -7.976511649e-03f, -1.707586806e-03f, +2.312505227e-03f, -6.452583046e-04f,
+    /* 12, 9 */ +6.504054889e-04f, -1.831416829e-03f, +2.284590483e-04f, +9.076221424e-03f, -2.029896461e-02f, -3.151741694e-02f, +2.381671619e-02f, +2.764638515e-02f, -7.103945228e-03f, -2.514858275e-03f, +2.516571985e-03f, -6.121620582e-04f,
+    /* 12,10 */ +6.273034189e-04f, -1.569701578e-03f, -4.225520445e-04f, +9.319170237e-03f, -1.767328103e-02f, -3.339904530e-02f, +2.065196332e-02f, +2.979181001e-02f, -6.011087921e-03f, -3.350171906e-03f, +2.685773447e-03f, -5.559799195e-04f,
+    /* 12,11 */ +5.906251216e-04f, -1.303424694e-03f, -1.003961424e-03f, +9.372823891e-03f, -1.502265637e-02f, -3.490702283e-02f, +1.724344185e-02f, +3.172374637e-02f, -4.698496481e-03f, -4.198484259e-03f, +2.812206203e-03f, -4.751685216e-04f,
+    /* 12,12 */ +5.431259501e-04f, -1.039066351e-03f, -1.510362297e-03f, +9.251714734e-03f, -1.238366322e-02f, -3.603085731e-02f, +1.363013975e-02f, +3.340772759e-02f, -3.170488652e-03f, -5.043181884e-03f, +2.888204359e-03f, -3.687004846e-04f,
+    /* 12,13 */ +4.875429386e-04f, -7.824350015e-04f, -1.938320040e-03f, +8.972352970e-03f, -9.791136525e-03f, -3.676562700e-02f, +9.854190425e-03f, +3.481183398e-02f, -1.435258618e-03f, -5.866306097e-03f, +2.906545541e-03f, -2.361482435e-04f,
+    /* 12,14 */ +4.265219564e-04f, -5.385913036e-04f, -2.286292242e-03f, +8.552819277e-03f, -7.277671607e-03f, -3.711196787e-02f, +5.960234251e-03f, +3.590728113e-02f, +4.950753709e-04f, -6.648820026e-03f, +2.860662969e-03f, -7.775923585e-05f,
+    /* 12,15 */ +3.625540242e-04f, -3.117958677e-04f, -2.554514858e-03f, +8.012348726e-03f, -4.873177855e-03f, -3.707596726e-02f, +1.994735221e-03f, +3.666896703e-02f, +2.604494739e-03f, -7.370914553e-03f, +2.744858958e-03f, +1.054803383e-04f,
+    /* 12, 0 */ +6.110448771e-04f, -3.173989705e-03f, +5.751223243e-03f, +1.507555794e-03f, -4.035343888e-02f, -2.375409442e-03f, +4.124383193e-02f, +8.091337269e-04f, -6.726716888e-03f, +3.253952459e-03f, -5.087325269e-04f, -4.127854608e-05f,
+    /* 12, 1 */ +6.820041984e-04f, -3.029137857e-03f, +4.746351538e-03f, +3.578915017e-03f, -3.904147991e-02f, -7.094160720e-03f, +4.168490752e-02f, +3.349306133e-03f, -7.647219355e-03f, +3.259488816e-03f, -3.738470149e-04f, -9.536054020e-05f,
+    /* 12, 2 */ +7.235832870e-04f, -2.829602454e-03f, +3.736224000e-03f, +5.388621830e-03f, -3.734163661e-02f, -1.171726725e-02f, +4.165549067e-02f, +6.085743956e-03f, -8.486093037e-03f, +3.182049180e-03f, -2.060445111e-04f, -1.573545891e-04f,
+    /* 12, 3 */ +7.383739029e-04f, -2.585946508e-03f, +2.743066911e-03f, +6.925917423e-03f, -3.529277498e-02f, -1.618281485e-02f, +4.114151479e-02f, +8.986133221e-03f, -9.216195947e-03f, +3.014391908e-03f, -5.966328360e-06f, -2.260166200e-04f,
+    /* 12, 4 */ +7.294476853e-04f, -2.308795686e-03f, +1.786872733e-03f, +8.185530694e-03f, -3.293811768e-02f, -2.043162352e-02f, +4.013642610e-02f, +1.201345370e-02f, -9.810446156e-03f, +2.750895834e-03f, +2.246717082e-04f, -2.996559917e-04f,
+    /* 12, 5 */ +7.002161546e-04f, -2.008565767e-03f, +8.851333656e-04f, +9.167495079e-03f, -3.032435275e-02f, -2.440826356e-02f, +3.864144993e-02f, +1.512648157e-02f, -1.024241966e-02f, +2.387856219e-03f, +4.830138128e-04f, -3.761418846e-04f,
+    /* 12, 6 */ +6.542939604e-04f, -1.695217727e-03f, +5.264660367e-05f, +9.876866054e-03f, -2.750069849e-02f, -2.806199617e-02f, +3.666571148e-02f, +1.828039745e-02f, -1.048696943e-02f, +1.923755148e-03f, +7.650267923e-04f, -4.529261561e-04f,
+    /* 12, 7 */ +5.953691186e-04f, -1.378044726e-03f, -6.986040124e-04f, +1.032334944e-02f, -2.451794467e-02f, -3.134761990e-02f, +3.422620641e-02f, +2.142749023e-02f, -1.052085229e-02f, +1.359497506e-03f, +1.065494162e-03f, -5.270834853e-04f,
+    /* 12, 8 */ +5.270834853e-04f, -1.065494162e-03f, -1.359497506e-03f, +1.052085229e-02f, -2.142749023e-02f, -3.422620641e-02f, +3.134761990e-02f, +2.451794467e-02f, -1.032334944e-02f, +6.986040124e-04f, +1.378044726e-03f, -5.953691186e-04f,
+    /* 12, 9 */ +4.529261561e-04f, -7.650267923e-04f, -1.923755148e-03f, +1.048696943e-02f, -1.828039745e-02f, -3.666571148e-02f, +2.806199617e-02f, +2.750069849e-02f, -9.876866054e-03f, -5.264660367e-05f, +1.695217727e-03f, -6.542939604e-04f,
+    /* 12,10 */ +3.761418846e-04f, -4.830138128e-04f, -2.387856219e-03f, +1.024241966e-02f, -1.512648157e-02f, -3.864144993e-02f, +2.440826356e-02f, +3.032435275e-02f, -9.167495079e-03f, -8.851333656e-04f, +2.008565767e-03f, -7.002161546e-04f,
+    /* 12,11 */ +2.996559917e-04f, -2.246717082e-04f, -2.750895834e-03f, +9.810446156e-03f, -1.201345370e-02f, -4.013642610e-02f, +2.043162352e-02f, +3.293811768e-02f, -8.185530694e-03f, -1.786872733e-03f, +2.308795686e-03f, -7.294476853e-04f,
+    /* 12,12 */ +2.260166200e-04f, +5.966328360e-06f, -3.014391908e-03f, +9.216195947e-03f, -8.986133221e-03f, -4.114151479e-02f, +1.618281485e-02f, +3.529277498e-02f, -6.925917423e-03f, -2.743066911e-03f, +2.585946508e-03f, -7.383739029e-04f,
+    /* 12,13 */ +1.573545891e-04f, +2.060445111e-04f, -3.182049180e-03f, +8.486093037e-03f, -6.085743956e-03f, -4.165549067e-02f, +1.171726725e-02f, +3.734163661e-02f, -5.388621830e-03f, -3.736224000e-03f, +2.829602454e-03f, -7.235832870e-04f,
+    /* 12,14 */ +9.536054020e-05f, +3.738470149e-04f, -3.259488816e-03f, +7.647219355e-03f, -3.349306133e-03f, -4.168490752e-02f, +7.094160720e-03f, +3.904147991e-02f, -3.578915017e-03f, -4.746351538e-03f, +3.029137857e-03f, -6.820041984e-04f,
+    /* 12,15 */ +4.127854608e-05f, +5.087325269e-04f, -3.253952459e-03f, +6.726716888e-03f, -8.091337269e-04f, -4.124383193e-02f, +2.375409442e-03f, +4.035343888e-02f, -1.507555794e-03f, -5.751223243e-03f, +3.173989705e-03f, -6.110448771e-04f,
+
+    /* 24, 0 */ -8.820438069e-05f, -1.519461079e-04f, -2.301651496e-04f, -3.149320871e-04f, -3.945939739e-04f, -4.554410135e-04f, -4.841532882e-04f, -4.705408991e-04f, -4.099602091e-04f, -3.048100066e-04f, -1.646897470e-04f, -5.099007530e-06f, +1.551006323e-04f, +2.969416536e-04f, +4.046294158e-04f, +4.681429482e-04f, +4.846228261e-04f, +4.583040637e-04f, +3.990939388e-04f, +3.201968846e-04f, +2.353759082e-04f, +1.564712483e-04f, +9.167483068e-05f, +4.482688286e-05f,
+    /* 24, 1 */ -8.480575132e-05f, -1.474789784e-04f, -2.249812225e-04f, -3.096480504e-04f, -3.900204007e-04f, -4.524514078e-04f, -4.835165803e-04f, -4.727530367e-04f, -4.151145025e-04f, -3.125397891e-04f, -1.742016828e-04f, -1.529460870e-05f, +1.454387449e-04f, +2.889379628e-04f, +3.991236794e-04f, +4.655589110e-04f, +4.849233000e-04f, +4.610375470e-04f, +4.035168325e-04f, +3.254391996e-04f, +2.406110065e-04f, +1.610529558e-04f, +9.521673594e-05f, +4.721513201e-05f,
+    /* 24, 2 */ -8.147924507e-05f, -1.430712350e-04f, -2.198265592e-04f, -3.043479843e-04f, -3.853766873e-04f, -4.493383067e-04f, -4.827146831e-04f, -4.747797448e-04f, -4.200908527e-04f, -3.201278616e-04f, -1.836320864e-04f, -2.548296987e-05f, +1.357085413e-04f, +2.808022583e-04f, +3.934446700e-04f, +4.627886263e-04f, +4.850529052e-04f, +4.636385032e-04f, +4.078592000e-04f, +3.306557574e-04f, +2.458678944e-04f, +1.656897170e-04f, +9.882966748e-05f, +4.967415993e-05f,
+    /* 24, 3 */ -7.822510242e-05f, -1.387241832e-04f, -2.147035314e-04f, -2.990350629e-04f, -3.806663042e-04f, -4.461048161e-04f, -4.817496625e-04f, -4.766215175e-04f, -4.248879309e-04f, -3.275711800e-04f, -1.929766610e-04f, -3.565926997e-05f, +1.259145254e-04f, +2.725379532e-04f, +3.875941701e-04f, +4.598320457e-04f, +4.850099279e-04f, +4.661040260e-04f, +4.121175966e-04f, +3.358432542e-04f, +2.511439643e-04f, +1.703799499e-04f, +1.025131319e-04f, +5.220438912e-05f,
+    /* 24, 4 */ -7.504350274e-05f, -1.344390595e-04f, -2.096144489e-04f, -2.937124231e-04f, -3.758927218e-04f, -4.427540852e-04f, -4.806236671e-04f, -4.782789577e-04f, -4.295045226e-04f, -3.348667971e-04f, -2.022311686e-04f, -4.581869630e-05f, +1.160612449e-04f, +2.641485480e-04f, +3.815740737e-04f, +4.566892339e-04f, +4.847927474e-04f, +4.684312656e-04f, +4.162885910e-04f, +3.409983591e-04f, +2.564365532e-04f, +1.751220037e-04f, +1.062665706e-04f, +5.480619333e-05f,
+    /* 24, 5 */ -7.193456522e-05f, -1.302170312e-04f, -2.045615590e-04f, -2.883831622e-04f, -3.710594077e-04f, -4.392893036e-04f, -4.793389263e-04f, -4.797527765e-04f, -4.339395286e-04f, -3.420118645e-04f, -2.113914331e-04f, -5.595644787e-05f, +1.061532886e-04f, +2.556376279e-04f, +3.753863858e-04f, +4.533603695e-04f, +4.843998374e-04f, +4.706174312e-04f, +4.203687678e-04f, +3.461177167e-04f, +2.617429433e-04f, +1.799141593e-04f, +1.100893595e-04f, +5.747989630e-05f,
+    /* 24, 6 */ -6.889834987e-05f, -1.260591965e-04f, -1.995470454e-04f, -2.830503358e-04f, -3.661698243e-04f, -4.357136989e-04f, -4.778977479e-04f, -4.810437916e-04f, -4.381919648e-04f, -3.490036345e-04f, -2.204533432e-04f, -6.606773875e-05f, +9.619528314e-05f, +2.470088608e-04f, +3.690332209e-04f, +4.498457452e-04f, +4.838297683e-04f, +4.726597937e-04f, +4.243547301e-04f, +3.511979487e-04f, +2.670603639e-04f, +1.847546294e-04f, +1.139808078e-04f, +6.022577049e-05f,
+    /* 24, 7 */ -6.593485851e-05f, -1.219665852e-04f, -1.945730275e-04f, -2.777169567e-04f, -3.612274261e-04f, -4.320305335e-04f, -4.763025159e-04f, -4.821529264e-04f, -4.422609626e-04f, -3.558394612e-04f, -2.294128549e-04f, -7.614780136e-05f, +8.619188981e-05f, +2.382659945e-04f, +3.625168024e-04f, +4.461457687e-04f, +4.830812085e-04f, +4.745556880e-04f, +4.282431024e-04f, +3.562356572e-04f, +2.723859925e-04f, +1.896415594e-04f, +1.179401580e-04f, +6.304403582e-05f,
+    /* 24, 8 */ -6.304403582e-05f, -1.179401580e-04f, -1.896415594e-04f, -2.723859925e-04f, -3.562356572e-04f, -4.282431024e-04f, -4.745556880e-04f, -4.830812085e-04f, -4.461457687e-04f, -3.625168024e-04f, -2.382659945e-04f, -8.619188981e-05f, +7.614780136e-05f, +2.294128549e-04f, +3.558394612e-04f, +4.422609626e-04f, +4.821529264e-04f, +4.763025159e-04f, +4.320305335e-04f, +3.612274261e-04f, +2.777169567e-04f, +1.945730275e-04f, +1.219665852e-04f, +6.593485851e-05f,
+    /* 24, 9 */ -6.022577049e-05f, -1.139808078e-04f, -1.847546294e-04f, -2.670603639e-04f, -3.511979487e-04f, -4.243547301e-04f, -4.726597937e-04f, -4.838297683e-04f, -4.498457452e-04f, -3.690332209e-04f, -2.470088608e-04f, -9.619528314e-05f, +6.606773875e-05f, +2.204533432e-04f, +3.490036345e-04f, +4.381919648e-04f, +4.810437916e-04f, +4.778977479e-04f, +4.357136989e-04f, +3.661698243e-04f, +2.830503358e-04f, +1.995470454e-04f, +1.260591965e-04f, +6.889834987e-05f,
+    /* 24,10 */ -5.747989630e-05f, -1.100893595e-04f, -1.799141593e-04f, -2.617429433e-04f, -3.461177167e-04f, -4.203687678e-04f, -4.706174312e-04f, -4.843998374e-04f, -4.533603695e-04f, -3.753863858e-04f, -2.556376279e-04f, -1.061532886e-04f, +5.595644787e-05f, +2.113914331e-04f, +3.420118645e-04f, +4.339395286e-04f, +4.797527765e-04f, +4.793389263e-04f, +4.392893036e-04f, +3.710594077e-04f, +2.883831622e-04f, +2.045615590e-04f, +1.302170312e-04f, +7.193456522e-05f,
+    /* 24,11 */ -5.480619333e-05f, -1.062665706e-04f, -1.751220037e-04f, -2.564365532e-04f, -3.409983591e-04f, -4.162885910e-04f, -4.684312656e-04f, -4.847927474e-04f, -4.566892339e-04f, -3.815740737e-04f, -2.641485480e-04f, -1.160612449e-04f, +4.581869630e-05f, +2.022311686e-04f, +3.348667971e-04f, +4.295045226e-04f, +4.782789577e-04f, +4.806236671e-04f, +4.427540852e-04f, +3.758927218e-04f, +2.937124231e-04f, +2.096144489e-04f, +1.344390595e-04f, +7.504350274e-05f,
+    /* 24,12 */ -5.220438912e-05f, -1.025131319e-04f, -1.703799499e-04f, -2.511439643e-04f, -3.358432542e-04f, -4.121175966e-04f, -4.661040260e-04f, -4.850099279e-04f, -4.598320457e-04f, -3.875941701e-04f, -2.725379532e-04f, -1.259145254e-04f, +3.565926997e-05f, +1.929766610e-04f, +3.275711800e-04f, +4.248879309e-04f, +4.766215175e-04f, +4.817496625e-04f, +4.461048161e-04f, +3.806663042e-04f, +2.990350629e-04f, +2.147035314e-04f, +1.387241832e-04f, +7.822510242e-05f,
+    /* 24,13 */ -4.967415993e-05f, -9.882966748e-05f, -1.656897170e-04f, -2.458678944e-04f, -3.306557574e-04f, -4.078592000e-04f, -4.636385032e-04f, -4.850529052e-04f, -4.627886263e-04f, -3.934446700e-04f, -2.808022583e-04f, -1.357085413e-04f, +2.548296987e-05f, +1.836320864e-04f, +3.201278616e-04f, +4.200908527e-04f, +4.747797448e-04f, +4.827146831e-04f, +4.493383067e-04f, +3.853766873e-04f, +3.043479843e-04f, +2.198265592e-04f, +1.430712350e-04f, +8.147924507e-05f,
+    /* 24,14 */ -4.721513201e-05f, -9.521673594e-05f, -1.610529558e-04f, -2.406110065e-04f, -3.254391996e-04f, -4.035168325e-04f, -4.610375470e-04f, -4.849233000e-04f, -4.655589110e-04f, -3.991236794e-04f, -2.889379628e-04f, -1.454387449e-04f, +1.529460870e-05f, +1.742016828e-04f, +3.125397891e-04f, +4.151145025e-04f, +4.727530367e-04f, +4.835165803e-04f, +4.524514078e-04f, +3.900204007e-04f, +3.096480504e-04f, +2.249812225e-04f, +1.474789784e-04f, +8.480575132e-05f,
+    /* 24,15 */ -4.482688286e-05f, -9.167483068e-05f, -1.564712483e-04f, -2.353759082e-04f, -3.201968846e-04f, -3.990939388e-04f, -4.583040637e-04f, -4.846228261e-04f, -4.681429482e-04f, -4.046294158e-04f, -2.969416536e-04f, -1.551006323e-04f, +5.099007530e-06f, +1.646897470e-04f, +3.048100066e-04f, +4.099602091e-04f, +4.705408991e-04f, +4.841532882e-04f, +4.554410135e-04f, +3.945939739e-04f, +3.149320871e-04f, +2.301651496e-04f, +1.519461079e-04f, +8.820438069e-05f,
+    /* 24, 0 */ +1.254177052e-04f, +1.432187562e-04f, +1.041598752e-04f, -1.135750248e-05f, -2.010663923e-04f, -4.345091125e-04f, -6.566280172e-04f, -8.018070806e-04f, -8.145094672e-04f, -6.693628869e-04f, -3.829411831e-04f, -1.208738944e-05f, +3.614691013e-04f, +6.551938988e-04f, +8.101126455e-04f, +8.069330100e-04f, +6.686285441e-04f, +4.493898115e-04f, +2.148422063e-04f, +2.121126661e-05f, -9.928779545e-05f, -1.427235715e-04f, -1.276505127e-04f, -8.319130160e-05f,
+    /* 24, 1 */ +1.230784715e-04f, +1.434886692e-04f, +1.087259386e-04f, -1.803144714e-06f, -1.874698746e-04f, -4.195879132e-04f, -6.443236569e-04f, -7.961535056e-04f, -8.182754723e-04f, -6.829663304e-04f, -4.040749814e-04f, -3.625133679e-05f, +3.396771574e-04f, +6.404692089e-04f, +8.050839212e-04f, +8.115205881e-04f, +6.803091718e-04f, +4.642140510e-04f, +2.287861157e-04f, +3.136033560e-05f, -9.410712043e-05f, -1.419963918e-04f, -1.297693532e-04f, -8.627587811e-05f,
+    /* 24, 2 */ +1.206403004e-04f, +1.435401825e-04f, +1.129889134e-04f, +7.448162127e-06f, -1.740634498e-04f, -4.046419937e-04f, -6.317316839e-04f, -7.899834729e-04f, -8.214124236e-04f, -6.959950382e-04f, -4.248524782e-04f, -6.038280262e-05f, +3.175841545e-04f, +6.251993025e-04f, +7.994228899e-04f, +8.155596091e-04f, +6.916540113e-04f, +4.789657110e-04f, +2.428865252e-04f, +4.180014906e-05f, -8.861563067e-05f, -1.410306561e-04f, -1.317666448e-04f, -8.934972901e-05f,
+    /* 24, 3 */ +1.181106179e-04f, +1.433803035e-04f, +1.169520657e-04f, +1.639322797e-05f, -1.608575022e-04f, -3.896869361e-04f, -6.188684520e-04f, -7.833086355e-04f, -8.239227539e-04f, -7.084404793e-04f, -4.452560799e-04f, -8.446017611e-05f, +2.952092559e-04f, +6.093952965e-04f, +7.931298338e-04f, +8.190403838e-04f, +7.026473724e-04f, +4.936285297e-04f, +2.571314547e-04f, +5.252568657e-05f, -8.281147599e-05f, -1.398199793e-04f, -1.336347757e-04f, -9.240689021e-05f,
+    /* 24, 4 */ +1.154967766e-04f, +1.430161614e-04f, +1.206189886e-04f, +2.502931407e-05f, -1.478619956e-04f, -3.747381071e-04f, -6.057504254e-04f, -7.761410928e-04f, -8.258095592e-04f, -7.202947906e-04f, -4.652686374e-04f, -1.084619116e-04f, +2.725719623e-04f, +5.930689291e-04f, +7.862057246e-04f, +8.219537553e-04f, +7.132737861e-04f, +5.081861235e-04f, +2.715085502e-04f, +6.353146713e-05f, -7.669318508e-05f, -1.383581653e-04f, -1.353661159e-04f, -9.544123411e-05f,
+    /* 24, 5 */ +1.128060463e-04f, +1.424549941e-04f, +1.239935912e-04f, +3.335412922e-05f, -1.350864661e-04f, -3.598106411e-04f, -5.923941571e-04f, -7.684933716e-04f, -8.270765907e-04f, -7.315507834e-04f, -4.848734661e-04f, -1.323665545e-04f, +2.496920884e-04f, +5.762325468e-04f, +7.786522269e-04f, +8.242911148e-04f, +7.235180256e-04f, +5.226220062e-04f, +2.860050947e-04f, +7.481154929e-05f, -7.025967465e-05f, -1.366392205e-04f, -1.369530289e-04f, -9.844647580e-05f,
+    /* 24, 6 */ +1.100456035e-04f, +1.417041346e-04f, +1.270800866e-04f, +4.136582536e-05f, -1.225400157e-04f, -3.449194225e-04f, -5.788162671e-04f, -7.603784078e-04f, -8.277282458e-04f, -7.422019493e-04f, -5.040543645e-04f, -1.561527673e-04f, +2.265897402e-04f, +5.588990922e-04f, +7.704716994e-04f, +8.260444163e-04f, +7.333651287e-04f, +5.369196097e-04f, +3.006080208e-04f, +8.635953200e-05f, -6.351025813e-05f, -1.346573670e-04f, -1.383878839e-04f, -1.014161798e-04f,
+    /* 24, 7 */ +1.072225228e-04f, +1.407709979e-04f, +1.298829797e-04f, +4.906299265e-05f, -1.102313067e-04f, -3.300790706e-04f, -5.650334202e-04f, -7.518095256e-04f, -8.277695590e-04f, -7.522424649e-04f, -5.227956327e-04f, -1.797993550e-04f, +2.032852905e-04f, +5.410820901e-04f, +7.616671958e-04f, +8.272061905e-04f, +7.428004186e-04f, +5.510623045e-04f, +3.153039229e-04f, +9.816855611e-05f, -5.644465382e-05f, -1.324070557e-04f, -1.396630677e-04f, -1.043437672e-04f,
+    /* 24, 8 */ +1.043437672e-04f, +1.396630677e-04f, +1.324070557e-04f, +5.644465382e-05f, -9.816855611e-05f, -3.153039229e-04f, -5.510623045e-04f, -7.428004186e-04f, -8.272061905e-04f, -7.616671958e-04f, -5.410820901e-04f, -2.032852905e-04f, +1.797993550e-04f, +5.227956327e-04f, +7.522424649e-04f, +8.277695590e-04f, +7.518095256e-04f, +5.650334202e-04f, +3.300790706e-04f, +1.102313067e-04f, -4.906299265e-05f, -1.298829797e-04f, -1.407709979e-04f, -1.072225228e-04f,
+    /* 24, 9 */ +1.014161798e-04f, +1.383878839e-04f, +1.346573670e-04f, +6.351025813e-05f, -8.635953200e-05f, -3.006080208e-04f, -5.369196097e-04f, -7.333651287e-04f, -8.260444163e-04f, -7.704716994e-04f, -5.588990922e-04f, -2.265897402e-04f, +1.561527673e-04f, +5.040543645e-04f, +7.422019493e-04f, +8.277282458e-04f, +7.603784078e-04f, +5.788162671e-04f, +3.449194225e-04f, +1.225400157e-04f, -4.136582536e-05f, -1.270800866e-04f, -1.417041346e-04f, -1.100456035e-04f,
+    /* 24,10 */ +9.844647580e-05f, +1.369530289e-04f, +1.366392205e-04f, +7.025967465e-05f, -7.481154929e-05f, -2.860050947e-04f, -5.226220062e-04f, -7.235180256e-04f, -8.242911148e-04f, -7.786522269e-04f, -5.762325468e-04f, -2.496920884e-04f, +1.323665545e-04f, +4.848734661e-04f, +7.315507834e-04f, +8.270765907e-04f, +7.684933716e-04f, +5.923941571e-04f, +3.598106411e-04f, +1.350864661e-04f, -3.335412922e-05f, -1.239935912e-04f, -1.424549941e-04f, -1.128060463e-04f,
+    /* 24,11 */ +9.544123411e-05f, +1.353661159e-04f, +1.383581653e-04f, +7.669318508e-05f, -6.353146713e-05f, -2.715085502e-04f, -5.081861235e-04f, -7.132737861e-04f, -8.219537553e-04f, -7.862057246e-04f, -5.930689291e-04f, -2.725719623e-04f, +1.084619116e-04f, +4.652686374e-04f, +7.202947906e-04f, +8.258095592e-04f, +7.761410928e-04f, +6.057504254e-04f, +3.747381071e-04f, +1.478619956e-04f, -2.502931407e-05f, -1.206189886e-04f, -1.430161614e-04f, -1.154967766e-04f,
+    /* 24,12 */ +9.240689021e-05f, +1.336347757e-04f, +1.398199793e-04f, +8.281147599e-05f, -5.252568657e-05f, -2.571314547e-04f, -4.936285297e-04f, -7.026473724e-04f, -8.190403838e-04f, -7.931298338e-04f, -6.093952965e-04f, -2.952092559e-04f, +8.446017611e-05f, +4.452560799e-04f, +7.084404793e-04f, +8.239227539e-04f, +7.833086355e-04f, +6.188684520e-04f, +3.896869361e-04f, +1.608575022e-04f, -1.639322797e-05f, -1.169520657e-04f, -1.433803035e-04f, -1.181106179e-04f,
+    /* 24,13 */ +8.934972901e-05f, +1.317666448e-04f, +1.410306561e-04f, +8.861563067e-05f, -4.180014906e-05f, -2.428865252e-04f, -4.789657110e-04f, -6.916540113e-04f, -8.155596091e-04f, -7.994228899e-04f, -6.251993025e-04f, -3.175841545e-04f, +6.038280262e-05f, +4.248524782e-04f, +6.959950382e-04f, +8.214124236e-04f, +7.899834729e-04f, +6.317316839e-04f, +4.046419937e-04f, +1.740634498e-04f, -7.448162127e-06f, -1.129889134e-04f, -1.435401825e-04f, -1.206403004e-04f,
+    /* 24,14 */ +8.627587811e-05f, +1.297693532e-04f, +1.419963918e-04f, +9.410712043e-05f, -3.136033560e-05f, -2.287861157e-04f, -4.642140510e-04f, -6.803091718e-04f, -8.115205881e-04f, -8.050839212e-04f, -6.404692089e-04f, -3.396771574e-04f, +3.625133679e-05f, +4.040749814e-04f, +6.829663304e-04f, +8.182754723e-04f, +7.961535056e-04f, +6.443236569e-04f, +4.195879132e-04f, +1.874698746e-04f, +1.803144714e-06f, -1.087259386e-04f, -1.434886692e-04f, -1.230784715e-04f,
+    /* 24,15 */ +8.319130160e-05f, +1.276505127e-04f, +1.427235715e-04f, +9.928779545e-05f, -2.121126661e-05f, -2.148422063e-04f, -4.493898115e-04f, -6.686285441e-04f, -8.069330100e-04f, -8.101126455e-04f, -6.551938988e-04f, -3.614691013e-04f, +1.208738944e-05f, +3.829411831e-04f, +6.693628869e-04f, +8.145094672e-04f, +8.018070806e-04f, +6.566280172e-04f, +4.345091125e-04f, +2.010663923e-04f, +1.135750248e-05f, -1.041598752e-04f, -1.432187562e-04f, -1.254177052e-04f,
+    /* 24, 0 */ +4.545052445e-05f, +1.951315810e-04f, +3.748938080e-04f, +4.809335107e-04f, +3.960765690e-04f, +5.993810822e-05f, -4.723795438e-04f, -1.024325735e-03f, -1.361247582e-03f, -1.299302728e-03f, -8.046117557e-04f, -2.606329026e-05f, +7.618428442e-04f, +1.280408741e-03f, +1.370322771e-03f, +1.054089829e-03f, +5.086432784e-04f, -3.113193898e-05f, -3.825195300e-04f, -4.822884412e-04f, -3.849609275e-04f, -2.063256631e-04f, -5.270037440e-05f, +2.454794639e-05f,
+    /* 24, 1 */ +3.852332445e-05f, +1.840753178e-04f, +3.645444067e-04f, +4.788140096e-04f, +4.086121277e-04f, +8.793526572e-05f, -4.362135407e-04f, -9.937048198e-04f, -1.350562575e-03f, -1.316442769e-03f, -8.462280606e-04f, -7.815185270e-05f, +7.179801999e-04f, +1.259777116e-03f, +1.377758125e-03f, +1.082939175e-03f, +5.449481703e-04f, -1.547839432e-06f, -3.679388598e-04f, -4.828529979e-04f, -3.947147844e-04f, -2.176372792e-04f, -6.026901850e-05f, +2.205234045e-05f,
+    /* 24, 2 */ +3.192167036e-05f, +1.731761778e-04f, +3.539434193e-04f, +4.759566352e-04f, +4.201302650e-04f, +1.150943789e-04f, -4.002008253e-04f, -9.622858103e-04f, -1.338300241e-03f, -1.331815598e-03f, -8.866349827e-04f, -1.301264311e-04f, +6.730846905e-04f, +1.237427186e-03f, +1.383526171e-03f, +1.110816763e-03f, +5.812367254e-04f, +2.878109064e-05f, -3.523343557e-04f, -4.826023474e-04f, -4.041241778e-04f, -2.290451863e-04f, -6.815162122e-05f, +1.926869283e-05f,
+    /* 24, 3 */ +2.564752013e-05f, +1.624524653e-04f, +3.431211940e-04f, +4.723889014e-04f, +4.306369033e-04f, +1.413884897e-04f, -3.643958409e-04f, -9.301281119e-04f, -1.324495465e-03f, -1.345410999e-03f, -9.257779427e-04f, -1.819112698e-04f, +6.272190697e-04f, +1.213381290e-03f, +1.387602061e-03f, +1.137666624e-03f, +6.174506312e-04f, +5.981976836e-05f, -3.357077999e-04f, -4.815127015e-04f, -4.131577529e-04f, -2.405272085e-04f, -7.634234963e-05f, +1.618998833e-05f,
+    /* 24, 4 */ +1.970191739e-05f, +1.519214683e-04f, +3.321076713e-04f, +4.681390617e-04f, +4.401397816e-04f, +1.667927354e-04f, -3.288518263e-04f, -8.972916878e-04f, -1.309185436e-03f, -1.357221809e-03f, -9.636046528e-04f, -2.334309635e-04f, +5.804478655e-04f, +1.187664745e-03f, +1.389963631e-03f, +1.163433942e-03f, +6.535308606e-04f, +9.153116784e-05f, -3.180629927e-04f, -4.795613921e-04f, -4.217840695e-04f, -2.520602653e-04f, -8.483435780e-05f, +1.280977164e-05f,
+    /* 24, 5 */ +1.408501704e-05f, +1.415994448e-04f, +3.209323254e-04f, +4.632360317e-04f, +4.486484045e-04f, +1.912843645e-04f, -2.936207276e-04f, -8.638369381e-04f, -1.292409564e-03f, -1.367243913e-03f, -1.000065207e-03f, -2.846105957e-04f, +5.328372638e-04f, +1.160305814e-03f, +1.390591463e-03f, +1.188065177e-03f, +6.894177793e-04f, +1.238763650e-04f, -2.994057812e-04f, -4.767269440e-04f, -4.299716716e-04f, -2.636204028e-04f, -9.361977359e-05f, +9.122184539e-06f,
+    /* 24, 6 */ +8.796112429e-06f, +1.315016126e-04f, +3.096241086e-04f, +4.577093118e-04f, +4.561739887e-04f, +2.148427485e-04f, -2.587531149e-04f, -8.298245798e-04f, -1.274209383e-03f, -1.375476234e-03f, -1.035112163e-03f, -3.353758773e-04f, +4.844549899e-04f, +1.131335665e-03f, +1.389468944e-03f, +1.211508174e-03f, +7.250512548e-04f, +1.568145870e-04f, -2.797440842e-04f, -4.729891466e-04f, -4.376891593e-04f, -2.751828281e-04f, -1.026896878e-04f, +5.122002376e-06f,
+    /* 24, 7 */ +3.833664119e-06f, +1.216421408e-04f, +2.982113984e-04f, +4.515889092e-04f, +4.627294060e-04f, +2.374493875e-04f, -2.242981012e-04f, -7.953155261e-04f, -1.254628457e-03f, -1.381920720e-03f, -1.068700627e-03f, -3.856532828e-04f, +4.353701854e-04f, +1.100788324e-03f, +1.386582316e-03f, +1.233712281e-03f, +7.603707678e-04f, +1.903032668e-04f, -2.590879129e-04f, -4.683291247e-04f, -4.449052614e-04f, -2.867219458e-04f, -1.120341455e-04f, +8.046698450e-07f,
+    /* 24, 8 */ -8.046698450e-07f, +1.120341455e-04f, +2.867219458e-04f, +4.449052614e-04f, +4.683291247e-04f, +2.590879129e-04f, -1.903032668e-04f, -7.603707678e-04f, -1.233712281e-03f, -1.386582316e-03f, -1.100788324e-03f, -4.353701854e-04f, +3.856532828e-04f, +1.068700627e-03f, +1.381920720e-03f, +1.254628457e-03f, +7.953155261e-04f, +2.242981012e-04f, -2.374493875e-04f, -4.627294060e-04f, -4.515889092e-04f, -2.982113984e-04f, -1.216421408e-04f, -3.833664119e-06f,
+    /* 24, 9 */ -5.122002376e-06f, +1.026896878e-04f, +2.751828281e-04f, +4.376891593e-04f, +4.729891466e-04f, +2.797440842e-04f, -1.568145870e-04f, -7.250512548e-04f, -1.211508174e-03f, -1.389468944e-03f, -1.131335665e-03f, -4.844549899e-04f, +3.353758773e-04f, +1.035112163e-03f, +1.375476234e-03f, +1.274209383e-03f, +8.298245798e-04f, +2.587531149e-04f, -2.148427485e-04f, -4.561739887e-04f, -4.577093118e-04f, -3.096241086e-04f, -1.315016126e-04f, -8.796112429e-06f,
+    /* 24,10 */ -9.122184539e-06f, +9.361977359e-05f, +2.636204028e-04f, +4.299716716e-04f, +4.767269440e-04f, +2.994057812e-04f, -1.238763650e-04f, -6.894177793e-04f, -1.188065177e-03f, -1.390591463e-03f, -1.160305814e-03f, -5.328372638e-04f, +2.846105957e-04f, +1.000065207e-03f, +1.367243913e-03f, +1.292409564e-03f, +8.638369381e-04f, +2.936207276e-04f, -1.912843645e-04f, -4.486484045e-04f, -4.632360317e-04f, -3.209323254e-04f, -1.415994448e-04f, -1.408501704e-05f,
+    /* 24,11 */ -1.280977164e-05f, +8.483435780e-05f, +2.520602653e-04f, +4.217840695e-04f, +4.795613921e-04f, +3.180629927e-04f, -9.153116784e-05f, -6.535308606e-04f, -1.163433942e-03f, -1.389963631e-03f, -1.187664745e-03f, -5.804478655e-04f, +2.334309635e-04f, +9.636046528e-04f, +1.357221809e-03f, +1.309185436e-03f, +8.972916878e-04f, +3.288518263e-04f, -1.667927354e-04f, -4.401397816e-04f, -4.681390617e-04f, -3.321076713e-04f, -1.519214683e-04f, -1.970191739e-05f,
+    /* 24,12 */ -1.618998833e-05f, +7.634234963e-05f, +2.405272085e-04f, +4.131577529e-04f, +4.815127015e-04f, +3.357077999e-04f, -5.981976836e-05f, -6.174506312e-04f, -1.137666624e-03f, -1.387602061e-03f, -1.213381290e-03f, -6.272190697e-04f, +1.819112698e-04f, +9.257779427e-04f, +1.345410999e-03f, +1.324495465e-03f, +9.301281119e-04f, +3.643958409e-04f, -1.413884897e-04f, -4.306369033e-04f, -4.723889014e-04f, -3.431211940e-04f, -1.624524653e-04f, -2.564752013e-05f,
+    /* 24,13 */ -1.926869283e-05f, +6.815162122e-05f, +2.290451863e-04f, +4.041241778e-04f, +4.826023474e-04f, +3.523343557e-04f, -2.878109064e-05f, -5.812367254e-04f, -1.110816763e-03f, -1.383526171e-03f, -1.237427186e-03f, -6.730846905e-04f, +1.301264311e-04f, +8.866349827e-04f, +1.331815598e-03f, +1.338300241e-03f, +9.622858103e-04f, +4.002008253e-04f, -1.150943789e-04f, -4.201302650e-04f, -4.759566352e-04f, -3.539434193e-04f, -1.731761778e-04f, -3.192167036e-05f,
+    /* 24,14 */ -2.205234045e-05f, +6.026901850e-05f, +2.176372792e-04f, +3.947147844e-04f, +4.828529979e-04f, +3.679388598e-04f, +1.547839432e-06f, -5.449481703e-04f, -1.082939175e-03f, -1.377758125e-03f, -1.259777116e-03f, -7.179801999e-04f, +7.815185270e-05f, +8.462280606e-04f, +1.316442769e-03f, +1.350562575e-03f, +9.937048198e-04f, +4.362135407e-04f, -8.793526572e-05f, -4.086121277e-04f, -4.788140096e-04f, -3.645444067e-04f, -1.840753178e-04f, -3.852332445e-05f,
+    /* 24,15 */ -2.454794639e-05f, +5.270037440e-05f, +2.063256631e-04f, +3.849609275e-04f, +4.822884412e-04f, +3.825195300e-04f, +3.113193898e-05f, -5.086432784e-04f, -1.054089829e-03f, -1.370322771e-03f, -1.280408741e-03f, -7.618428442e-04f, +2.606329026e-05f, +8.046117557e-04f, +1.299302728e-03f, +1.361247582e-03f, +1.024325735e-03f, +4.723795438e-04f, -5.993810822e-05f, -3.960765690e-04f, -4.809335107e-04f, -3.748938080e-04f, -1.951315810e-04f, -4.545052445e-05f,
+    /* 24, 0 */ -1.702250368e-04f, -1.965005420e-04f, +1.103795304e-06f, +4.330784212e-04f, +8.784555707e-04f, +9.653328276e-04f, +4.315509563e-04f, -6.109575553e-04f, -1.641723450e-03f, -2.015827225e-03f, -1.400787443e-03f, -4.702498413e-05f, +1.332047630e-03f, +2.006889303e-03f, +1.690240096e-03f, +6.826705419e-04f, -3.776686811e-04f, -9.512688743e-04f, -8.983149818e-04f, -4.640234647e-04f, -2.230828855e-05f, +1.918067822e-04f, +1.759255972e-04f, +6.786242515e-05f,
+    /* 24, 1 */ -1.642126010e-04f, -2.002752798e-04f, -1.910126829e-05f, +4.022439121e-04f, +8.571608722e-04f, +9.768399670e-04f, +4.832977173e-04f, -5.392469404e-04f, -1.590532482e-03f, -2.020693110e-03f, -1.466475318e-03f, -1.409702826e-04f, +1.260398022e-03f, +1.993868298e-03f, +1.735939471e-03f, +7.542164043e-04f, -3.217397652e-04f, -9.346209288e-04f, -9.166430096e-04f, -4.949934945e-04f, -4.448641826e-05f, +1.861665779e-04f, +1.812738819e-04f, +7.451957718e-05f,
+    /* 24, 2 */ -1.579281336e-04f, -2.031605347e-04f, -3.828516688e-05f, +3.716026326e-04f, +8.345286135e-04f, +9.858238344e-04f, +5.328272649e-04f, -4.677058239e-04f, -1.536815378e-03f, -2.021508037e-03f, -1.528977139e-03f, -2.346018520e-04f, +1.185988431e-03f, +1.976763286e-03f, +1.778684302e-03f, +8.254237228e-04f, -2.638601140e-04f, -9.153683790e-04f, -9.333455225e-04f, -5.259003096e-04f, -6.760829922e-05f, +1.795547962e-04f, +1.862290729e-04f, +8.132190552e-05f,
+    /* 24, 3 */ -1.514107898e-04f, -2.051877883e-04f, -5.643017558e-05f, +3.412342375e-04f, +8.106578209e-04f, +9.923241157e-04f, +5.800651921e-04f, -3.964985305e-04f, -1.480725107e-03f, -2.018303000e-03f, -1.588167145e-03f, -3.277114722e-04f, +1.108975953e-03f, +1.955583535e-03f, +1.818343426e-03f, +8.961196099e-04f, -2.041325004e-04f, -8.934973649e-04f, -9.483306864e-04f, -5.566532714e-04f, -9.163993343e-05f, +1.719487619e-04f, +1.907500791e-04f, +8.824611727e-05f,
+    /* 24, 4 */ -1.446989102e-04f, -2.063902871e-04f, -7.352252002e-05f, +3.112151687e-04f, +7.856484910e-04f, +9.963864071e-04f, +6.249444785e-04f, -3.257861630e-04f, -1.422418959e-03f, -2.011118755e-03f, -1.643928243e-03f, -4.200923193e-04f, +1.029524574e-03f, +1.930348530e-03f, +1.854792197e-03f, +9.661301752e-04f, -1.426663759e-04f, -8.690009463e-04f, -9.615092978e-04f, -5.871595310e-04f, -1.165432034e-04f, +1.633284218e-04f, +1.947956873e-04f, +9.526723781e-05f,
+    /* 24, 5 */ -1.378299032e-04f, -2.068028652e-04f, -8.955230425e-05f, +2.816184974e-04f, +7.596012646e-04f, +9.980619660e-04f, +6.674055614e-04f, -2.557261963e-04f, -1.362058071e-03f, -2.000005648e-03f, -1.696152289e-03f, -5.115395181e-04f, +9.478047386e-04f, +1.901087985e-03f, +1.887912892e-03f, +1.035280999e-03f, -7.957765930e-05f, -8.418792522e-04f, -9.727951144e-04f, -6.173242692e-04f, -1.422758795e-04f, +1.536765045e-04f, +1.983247179e-04f, +1.023586489e-04f,
+    /* 24, 6 */ -1.308401336e-04f, -2.064617646e-04f, -1.045134271e-04f, +2.525137807e-04f, +7.326171060e-04f, +9.974074494e-04f, +7.073963828e-04f, -1.864720875e-04f, -1.299806951e-03f, -1.985023411e-03f, -1.744740344e-03f, -6.018506887e-04f, +8.639929140e-04f, +1.867841815e-03f, +1.917595086e-03f, +1.103397612e-03f, -1.498850339e-05f, -8.121396097e-04f, -9.821051828e-04f, -6.470509490e-04f, -1.687916421e-04f, +1.429786744e-04f, +2.012961856e-04f, +1.094921351e-04f,
+    /* 24, 7 */ -1.237648199e-04f, -2.054044567e-04f, -1.184034872e-04f, +2.239669341e-04f, +7.047969872e-04f, +9.944846402e-04f, +7.448724134e-04f, -1.181729029e-04f, -1.235832990e-03f, -1.966240936e-03f, -1.789602898e-03f, -6.908264855e-04f, +7.782711267e-04f, +1.830660078e-03f, +1.943736019e-03f, +1.170305979e-03f, +5.097296116e-05f, -7.797966533e-04f, -9.893601617e-04f, -6.762415789e-04f, -1.960401183e-04f, +1.312236785e-04f, +2.036694644e-04f, +1.166379381e-04f,
+    /* 24, 8 */ -1.166379381e-04f, -2.036694644e-04f, -1.312236785e-04f, +1.960401183e-04f, +6.762415789e-04f, +9.893601617e-04f, +7.797966533e-04f, -5.097296116e-05f, -1.170305979e-03f, -1.943736019e-03f, -1.830660078e-03f, -7.782711267e-04f, +6.908264855e-04f, +1.789602898e-03f, +1.966240936e-03f, +1.235832990e-03f, +1.181729029e-04f, -7.448724134e-04f, -9.944846402e-04f, -7.047969872e-04f, -2.239669341e-04f, +1.184034872e-04f, +2.054044567e-04f, +1.237648199e-04f,
+    /* 24, 9 */ -1.094921351e-04f, -2.012961856e-04f, -1.429786744e-04f, +1.687916421e-04f, +6.470509490e-04f, +9.821051828e-04f, +8.121396097e-04f, +1.498850339e-05f, -1.103397612e-03f, -1.917595086e-03f, -1.867841815e-03f, -8.639929140e-04f, +6.018506887e-04f, +1.744740344e-03f, +1.985023411e-03f, +1.299806951e-03f, +1.864720875e-04f, -7.073963828e-04f, -9.974074494e-04f, -7.326171060e-04f, -2.525137807e-04f, +1.045134271e-04f, +2.064617646e-04f, +1.308401336e-04f,
+    /* 24,10 */ -1.023586489e-04f, -1.983247179e-04f, -1.536765045e-04f, +1.422758795e-04f, +6.173242692e-04f, +9.727951144e-04f, +8.418792522e-04f, +7.957765930e-05f, -1.035280999e-03f, -1.887912892e-03f, -1.901087985e-03f, -9.478047386e-04f, +5.115395181e-04f, +1.696152289e-03f, +2.000005648e-03f, +1.362058071e-03f, +2.557261963e-04f, -6.674055614e-04f, -9.980619660e-04f, -7.596012646e-04f, -2.816184974e-04f, +8.955230425e-05f, +2.068028652e-04f, +1.378299032e-04f,
+    /* 24,11 */ -9.526723781e-05f, -1.947956873e-04f, -1.633284218e-04f, +1.165432034e-04f, +5.871595310e-04f, +9.615092978e-04f, +8.690009463e-04f, +1.426663759e-04f, -9.661301752e-04f, -1.854792197e-03f, -1.930348530e-03f, -1.029524574e-03f, +4.200923193e-04f, +1.643928243e-03f, +2.011118755e-03f, +1.422418959e-03f, +3.257861630e-04f, -6.249444785e-04f, -9.963864071e-04f, -7.856484910e-04f, -3.112151687e-04f, +7.352252002e-05f, +2.063902871e-04f, +1.446989102e-04f,
+    /* 24,12 */ -8.824611727e-05f, -1.907500791e-04f, -1.719487619e-04f, +9.163993343e-05f, +5.566532714e-04f, +9.483306864e-04f, +8.934973649e-04f, +2.041325004e-04f, -8.961196099e-04f, -1.818343426e-03f, -1.955583535e-03f, -1.108975953e-03f, +3.277114722e-04f, +1.588167145e-03f, +2.018303000e-03f, +1.480725107e-03f, +3.964985305e-04f, -5.800651921e-04f, -9.923241157e-04f, -8.106578209e-04f, -3.412342375e-04f, +5.643017558e-05f, +2.051877883e-04f, +1.514107898e-04f,
+    /* 24,13 */ -8.132190552e-05f, -1.862290729e-04f, -1.795547962e-04f, +6.760829922e-05f, +5.259003096e-04f, +9.333455225e-04f, +9.153683790e-04f, +2.638601140e-04f, -8.254237228e-04f, -1.778684302e-03f, -1.976763286e-03f, -1.185988431e-03f, +2.346018520e-04f, +1.528977139e-03f, +2.021508037e-03f, +1.536815378e-03f, +4.677058239e-04f, -5.328272649e-04f, -9.858238344e-04f, -8.345286135e-04f, -3.716026326e-04f, +3.828516688e-05f, +2.031605347e-04f, +1.579281336e-04f,
+    /* 24,14 */ -7.451957718e-05f, -1.812738819e-04f, -1.861665779e-04f, +4.448641826e-05f, +4.949934945e-04f, +9.166430096e-04f, +9.346209288e-04f, +3.217397652e-04f, -7.542164043e-04f, -1.735939471e-03f, -1.993868298e-03f, -1.260398022e-03f, +1.409702826e-04f, +1.466475318e-03f, +2.020693110e-03f, +1.590532482e-03f, +5.392469404e-04f, -4.832977173e-04f, -9.768399670e-04f, -8.571608722e-04f, -4.022439121e-04f, +1.910126829e-05f, +2.002752798e-04f, +1.642126010e-04f,
+    /* 24,15 */ -6.786242515e-05f, -1.759255972e-04f, -1.918067822e-04f, +2.230828855e-05f, +4.640234647e-04f, +8.983149818e-04f, +9.512688743e-04f, +3.776686811e-04f, -6.826705419e-04f, -1.690240096e-03f, -2.006889303e-03f, -1.332047630e-03f, +4.702498413e-05f, +1.400787443e-03f, +2.015827225e-03f, +1.641723450e-03f, +6.109575553e-04f, -4.315509563e-04f, -9.653328276e-04f, -8.784555707e-04f, -4.330784212e-04f, -1.103795304e-06f, +1.965005420e-04f, +1.702250368e-04f,
+    /* 24, 0 */ +3.919255962e-05f, -2.280943782e-04f, -5.361345988e-04f, -4.457457641e-04f, +2.990589649e-04f, +1.295797675e-03f, +1.605517166e-03f, +5.875816565e-04f, -1.289084098e-03f, -2.596166105e-03f, -2.129939921e-03f, -7.496988250e-05f, +2.037180601e-03f, +2.625542335e-03f, +1.404160874e-03f, -4.837783526e-04f, -1.582480410e-03f, -1.345619201e-03f, -3.621830939e-04f, +4.180945199e-04f, +5.469818219e-04f, +2.499414065e-04f, -2.888372260e-05f, -8.895700627e-05f,
+    /* 24, 1 */ +4.853777483e-05f, -2.065137544e-04f, -5.236754574e-04f, -4.705972357e-04f, +2.371311675e-04f, +1.243196859e-03f, +1.622959247e-03f, +6.876650067e-04f, -1.171710060e-03f, -2.559351067e-03f, -2.215991331e-03f, -2.246678327e-04f, +1.937986318e-03f, +2.647321756e-03f, +1.516528605e-03f, -3.765445934e-04f, -1.553807591e-03f, -1.392405213e-03f, -4.263006320e-04f, +3.876486968e-04f, +5.560961676e-04f, +2.719611444e-04f, -1.761075235e-05f, -9.068976925e-05f,
+    /* 24, 2 */ +5.692498928e-05f, -1.852878923e-04f, -5.097279107e-04f, -4.926555685e-04f, +1.765921503e-04f, +1.188077447e-03f, +1.634867875e-03f, +7.837567953e-04f, -1.052453928e-03f, -2.515280079e-03f, -2.295086316e-03f, -3.736412373e-04f, +1.832653524e-03f, +2.661371990e-03f, +1.625780509e-03f, -2.661868955e-04f, -1.519477670e-03f, -1.435905115e-03f, -4.911987788e-04f, +3.544256494e-04f, +5.633598944e-04f, +2.940548284e-04f, -5.378471232e-06f, -9.187426948e-05f,
+    /* 24, 3 */ +6.436469025e-05f, -1.644995777e-04f, -4.944173708e-04f, -5.119388451e-04f, +1.176233517e-04f, +1.130703462e-03f, +1.641323506e-03f, +8.756040923e-04f, -9.317326579e-04f, -2.464159993e-03f, -2.367001633e-03f, -5.214100591e-04f, +1.721501142e-03f, +2.667586958e-03f, +1.731516399e-03f, -1.530278412e-04f, -1.479490407e-03f, -1.475875084e-03f, -5.566555092e-04f, +3.184551797e-04f, +5.686591450e-04f, +3.161189305e-04f, +7.802761507e-06f, -9.246489856e-05f,
+    /* 24, 4 */ +7.087197068e-05f, -1.442258278e-04f, -4.778705046e-04f, -5.284761768e-04f, +6.039472401e-05f, +1.071341110e-03f, +1.642425167e-03f, +9.629733481e-04f, -8.099633882e-04f, -2.406220702e-03f, -2.431540042e-03f, -6.674987371e-04f, +1.604869446e-03f, +2.665887444e-03f, +1.833344283e-03f, -3.740504807e-05f, -1.433866725e-03f, -1.512079220e-03f, -6.224402836e-04f, +2.797797731e-04f, +5.718846156e-04f, +3.380454975e-04f, +2.191686946e-05f, -9.241721523e-05f,
+    /* 24, 5 */ +7.646625331e-05f, -1.245377292e-04f, -4.602146020e-04f, -5.423072437e-04f, +5.064318866e-06f, +1.010257658e-03f, +1.638289725e-03f, +1.045651008e-03f, -6.875618648e-04f, -2.341714107e-03f, -2.488530931e-03f, -8.114379517e-04f, +1.483118851e-03f, +2.656221571e-03f, +1.930881931e-03f, +8.032993590e-05f, -1.382648990e-03f, -1.544290670e-03f, -6.883148110e-04f, +2.384547825e-04f, +5.729322223e-04f, +3.597225244e-04f, +3.694190340e-05f, -9.168826568e-05f,
+    /* 24, 6 */ +8.117100143e-05f, -1.055003114e-04f, -4.415769617e-04f, -5.534817998e-04f, -4.822206513e-05f, +9.477203224e-04f, +1.629051096e-03f, +1.123444034e-03f, -5.649408783e-04f, -2.270913001e-03f, -2.537830838e-03f, -9.527663633e-04f, +1.356628624e-03f, +2.638565156e-03f, +2.023758423e-03f, +1.998128074e-04f, -1.325901212e-03f, -1.572292748e-03f, -7.540338642e-04f, +1.945485578e-04f, +5.717037624e-04f, +3.810343609e-04f, +5.284991195e-05f, -9.023690790e-05f,
+    /* 24, 7 */ +8.501341847e-05f, -8.717245610e-05f, -4.220842946e-04f, -5.620591450e-04f, -9.933117260e-05f, +8.839951872e-04f, +1.614859393e-03f, +1.196180345e-03f, -4.425087329e-04f, -2.194109877e-03f, -2.579323863e-03f, -1.091032318e-03f, +1.225795515e-03f, +2.612921966e-03f, +2.111615667e-03f, +3.206677492e-04f, -1.263709151e-03f, -1.595880025e-03f, -8.193461436e-04f, +1.481425192e-04f, +5.681075679e-04f, +4.018621494e-04f, +6.960683992e-05f, -8.802413824e-05f,
+    /* 24, 8 */ +8.802413824e-05f, -6.960683992e-05f, -4.018621494e-04f, -5.681075679e-04f, -1.481425192e-04f, +8.193461436e-04f, +1.595880025e-03f, +1.263709151e-03f, -3.206677492e-04f, -2.111615667e-03f, -2.612921966e-03f, -1.225795515e-03f, +1.091032318e-03f, +2.579323863e-03f, +2.194109877e-03f, +4.425087329e-04f, -1.196180345e-03f, -1.614859393e-03f, -8.839951872e-04f, +9.933117260e-05f, +5.620591450e-04f, +4.220842946e-04f, +8.717245610e-05f, -8.501341847e-05f,
+    /* 24, 9 */ +9.023690790e-05f, -5.284991195e-05f, -3.810343609e-04f, -5.717037624e-04f, -1.945485578e-04f, +7.540338642e-04f, +1.572292748e-03f, +1.325901212e-03f, -1.998128074e-04f, -2.023758423e-03f, -2.638565156e-03f, -1.356628624e-03f, +9.527663633e-04f, +2.537830838e-03f, +2.270913001e-03f, +5.649408783e-04f, -1.123444034e-03f, -1.629051096e-03f, -9.477203224e-04f, +4.822206513e-05f, +5.534817998e-04f, +4.415769617e-04f, +1.055003114e-04f, -8.117100143e-05f,
+    /* 24,10 */ +9.168826568e-05f, -3.694190340e-05f, -3.597225244e-04f, -5.729322223e-04f, -2.384547825e-04f, +6.883148110e-04f, +1.544290670e-03f, +1.382648990e-03f, -8.032993590e-05f, -1.930881931e-03f, -2.656221571e-03f, -1.483118851e-03f, +8.114379517e-04f, +2.488530931e-03f, +2.341714107e-03f, +6.875618648e-04f, -1.045651008e-03f, -1.638289725e-03f, -1.010257658e-03f, -5.064318866e-06f, +5.423072437e-04f, +4.602146020e-04f, +1.245377292e-04f, -7.646625331e-05f,
+    /* 24,11 */ +9.241721523e-05f, -2.191686946e-05f, -3.380454975e-04f, -5.718846156e-04f, -2.797797731e-04f, +6.224402836e-04f, +1.512079220e-03f, +1.433866725e-03f, +3.740504807e-05f, -1.833344283e-03f, -2.665887444e-03f, -1.604869446e-03f, +6.674987371e-04f, +2.431540042e-03f, +2.406220702e-03f, +8.099633882e-04f, -9.629733481e-04f, -1.642425167e-03f, -1.071341110e-03f, -6.039472401e-05f, +5.284761768e-04f, +4.778705046e-04f, +1.442258278e-04f, -7.087197068e-05f,
+    /* 24,12 */ +9.246489856e-05f, -7.802761507e-06f, -3.161189305e-04f, -5.686591450e-04f, -3.184551797e-04f, +5.566555092e-04f, +1.475875084e-03f, +1.479490407e-03f, +1.530278412e-04f, -1.731516399e-03f, -2.667586958e-03f, -1.721501142e-03f, +5.214100591e-04f, +2.367001633e-03f, +2.464159993e-03f, +9.317326579e-04f, -8.756040923e-04f, -1.641323506e-03f, -1.130703462e-03f, -1.176233517e-04f, +5.119388451e-04f, +4.944173708e-04f, +1.644995777e-04f, -6.436469025e-05f,
+    /* 24,13 */ +9.187426948e-05f, +5.378471232e-06f, -2.940548284e-04f, -5.633598944e-04f, -3.544256494e-04f, +4.911987788e-04f, +1.435905115e-03f, +1.519477670e-03f, +2.661868955e-04f, -1.625780509e-03f, -2.661371990e-03f, -1.832653524e-03f, +3.736412373e-04f, +2.295086316e-03f, +2.515280079e-03f, +1.052453928e-03f, -7.837567953e-04f, -1.634867875e-03f, -1.188077447e-03f, -1.765921503e-04f, +4.926555685e-04f, +5.097279107e-04f, +1.852878923e-04f, -5.692498928e-05f,
+    /* 24,14 */ +9.068976925e-05f, +1.761075235e-05f, -2.719611444e-04f, -5.560961676e-04f, -3.876486968e-04f, +4.263006320e-04f, +1.392405213e-03f, +1.553807591e-03f, +3.765445934e-04f, -1.516528605e-03f, -2.647321756e-03f, -1.937986318e-03f, +2.246678327e-04f, +2.215991331e-03f, +2.559351067e-03f, +1.171710060e-03f, -6.876650067e-04f, -1.622959247e-03f, -1.243196859e-03f, -2.371311675e-04f, +4.705972357e-04f, +5.236754574e-04f, +2.065137544e-04f, -4.853777483e-05f,
+    /* 24,15 */ +8.895700627e-05f, +2.888372260e-05f, -2.499414065e-04f, -5.469818219e-04f, -4.180945199e-04f, +3.621830939e-04f, +1.345619201e-03f, +1.582480410e-03f, +4.837783526e-04f, -1.404160874e-03f, -2.625542335e-03f, -2.037180601e-03f, +7.496988250e-05f, +2.129939921e-03f, +2.596166105e-03f, +1.289084098e-03f, -5.875816565e-04f, -1.605517166e-03f, -1.295797675e-03f, -2.990589649e-04f, +4.457457641e-04f, +5.361345988e-04f, +2.280943782e-04f, -3.919255962e-05f,
+    /* 24, 0 */ +1.848082291e-04f, +3.126544607e-04f, -8.381805218e-05f, -8.698090905e-04f, -1.028447094e-03f, +2.139154673e-04f, +1.954115341e-03f, +2.095678120e-03f, -1.635717275e-04f, -2.819157299e-03f, -2.940044791e-03f, -1.098945345e-04f, +2.833179926e-03f, +2.923929522e-03f, +3.511165299e-04f, -2.017938339e-03f, -2.030476715e-03f, -3.277888569e-04f, +9.926761418e-04f, +9.110442493e-04f, +1.284872781e-04f, -3.065935448e-04f, -1.998565163e-04f, +7.803305534e-06f,
+    /* 24, 1 */ +1.695814378e-04f, +3.164556508e-04f, -4.103650150e-05f, -8.260698464e-04f, -1.058100498e-03f, +1.024893805e-04f, +1.871044125e-03f, +2.162944507e-03f, +2.203366063e-05f, -2.703524226e-03f, -3.034115138e-03f, -3.291928088e-04f, +2.713944640e-03f, +3.017272284e-03f, +5.397663057e-04f, -1.929900383e-03f, -2.099607997e-03f, -4.436146208e-04f, +9.507629285e-04f, +9.494468230e-04f, +1.748714247e-04f, -2.981837386e-04f, -2.146007649e-04f, -5.699432396e-07f,
+    /* 24, 2 */ +1.542962580e-04f, +3.180964801e-04f, -2.971107404e-07f, -7.801571616e-04f, -1.081692695e-03f, -6.019646704e-06f, +1.781805749e-03f, +2.219616197e-03f, +2.048848004e-04f, -2.577645910e-03f, -3.115029215e-03f, -5.470211463e-04f, +2.582823494e-03f, +3.098667200e-03f, +7.286710477e-04f, -1.831793311e-03f, -2.161014579e-03f, -5.608747689e-04f, +9.027154107e-04f, +9.846924856e-04f, +2.227798586e-04f, -2.873471247e-04f, -2.289106872e-04f, -9.773337074e-06f,
+    /* 24, 3 */ +1.390667857e-04f, +3.176854393e-04f, +3.826416878e-05f, -7.324018434e-04f, -1.099310451e-03f, -1.111689527e-04f, +1.686962051e-03f, +2.265625589e-03f, +3.841903571e-04f, -2.442181508e-03f, -3.182489175e-03f, -7.624077328e-04f, +2.440359294e-03f, +3.167649091e-03f, +9.169693475e-04f, -1.723899657e-03f, -2.214230624e-03f, -6.790305367e-04f, +8.485750922e-04f, +1.016463150e-03f, +2.720045869e-04f, -2.740180422e-04f, -2.426519708e-04f, -1.978701792e-05f,
+    /* 24, 4 */ +1.240005643e-04f, +3.153390489e-04f, +7.453005747e-05f, -6.831329371e-04f, -1.111069621e-03f, -2.125447010e-04f, +1.587090707e-03f, +2.300958285e-03f, +5.591862212e-04f, -2.297830066e-03f, -3.236262287e-03f, -9.743929228e-04f, +2.287150577e-03f, +3.223808711e-03f, +1.103792665e-03f, -1.606554759e-03f, -2.258822083e-03f, -7.975248409e-04f, +7.884177261e-04f, +1.044449054e-03f, +3.223209063e-04f, -2.581440514e-04f, -2.556870681e-04f, -3.058317705e-05f,
+    /* 24, 5 */ +1.091981202e-04f, +3.111807515e-04f, +1.084019636e-04f, -6.326758693e-04f, -1.117113666e-03f, -3.097634105e-04f, +1.482781879e-03f, +2.325652312e-03f, +7.291390495e-04f, -2.145326692e-03f, -3.276181809e-03f, -1.182034015e-03f, +2.123848782e-03f, +3.266795190e-03f, +1.288269679e-03f, -1.480145805e-03f, -2.294389599e-03f, -9.157848908e-04f, +7.223538352e-04f, +1.068350860e-03f, +3.734881809e-04f, -2.396868442e-04f, -2.678760406e-04f, -4.212586861e-05f,
+    /* 24, 6 */ +9.475256757e-05f, +3.053397988e-04f, +1.397998805e-04f, -5.813506695e-04f, -1.117612060e-03f, -4.024732802e-04f, +1.374634870e-03f, +2.339797075e-03f, +8.933496022e-04f, -1.985438555e-03f, -3.302147511e-03f, -1.384409934e-03f, +1.951155148e-03f, +3.296318191e-03f, +1.469530695e-03f, -1.345110577e-03f, -2.320571262e-03f, -1.033224945e-03f, +6.505290403e-04f, +1.087881752e-03f, +4.252507475e-04f, -2.186230940e-04f, -2.790774568e-04f, -5.437087857e-05f,
+    /* 24, 7 */ +8.074928352e-05f, +2.979501429e-04f, +1.686621699e-04f, -5.294702777e-04f, -1.112758583e-03f, -4.903553074e-04f, +1.263254779e-03f, +2.343532047e-03f, +1.051155860e-03f, -1.818960757e-03f, -3.314125843e-03f, -1.580625796e-03f, +1.769817333e-03f, +3.312149758e-03f, +1.646712089e-03f, -1.201935910e-03f, -2.337045209e-03f, -1.149249197e-03f, +5.731241934e-04f, +1.102769514e-03f, +4.773389469e-04f, -1.949452358e-04f, -2.891493374e-04f, -6.726565227e-05f,
+    /* 24, 8 */ +6.726565227e-05f, +2.891493374e-04f, +1.949452358e-04f, -4.773389469e-04f, -1.102769514e-03f, -5.731241934e-04f, +1.149249197e-03f, +2.337045209e-03f, +1.201935910e-03f, -1.646712089e-03f, -3.312149758e-03f, -1.769817333e-03f, +1.580625796e-03f, +3.314125843e-03f, +1.818960757e-03f, -1.051155860e-03f, -2.343532047e-03f, -1.263254779e-03f, +4.903553074e-04f, +1.112758583e-03f, +5.294702777e-04f, -1.686621699e-04f, -2.979501429e-04f, -8.074928352e-05f,
+    /* 24, 9 */ +5.437087857e-05f, +2.790774568e-04f, +2.186230940e-04f, -4.252507475e-04f, -1.087881752e-03f, -6.505290403e-04f, +1.033224945e-03f, +2.320571262e-03f, +1.345110577e-03f, -1.469530695e-03f, -3.296318191e-03f, -1.951155148e-03f, +1.384409934e-03f, +3.302147511e-03f, +1.985438555e-03f, -8.933496022e-04f, -2.339797075e-03f, -1.374634870e-03f, +4.024732802e-04f, +1.117612060e-03f, +5.813506695e-04f, -1.397998805e-04f, -3.053397988e-04f, -9.475256757e-05f,
+    /* 24,10 */ +4.212586861e-05f, +2.678760406e-04f, +2.396868442e-04f, -3.734881809e-04f, -1.068350860e-03f, -7.223538352e-04f, +9.157848908e-04f, +2.294389599e-03f, +1.480145805e-03f, -1.288269679e-03f, -3.266795190e-03f, -2.123848782e-03f, +1.182034015e-03f, +3.276181809e-03f, +2.145326692e-03f, -7.291390495e-04f, -2.325652312e-03f, -1.482781879e-03f, +3.097634105e-04f, +1.117113666e-03f, +6.326758693e-04f, -1.084019636e-04f, -3.111807515e-04f, -1.091981202e-04f,
+    /* 24,11 */ +3.058317705e-05f, +2.556870681e-04f, +2.581440514e-04f, -3.223209063e-04f, -1.044449054e-03f, -7.884177261e-04f, +7.975248409e-04f, +2.258822083e-03f, +1.606554759e-03f, -1.103792665e-03f, -3.223808711e-03f, -2.287150577e-03f, +9.743929228e-04f, +3.236262287e-03f, +2.297830066e-03f, -5.591862212e-04f, -2.300958285e-03f, -1.587090707e-03f, +2.125447010e-04f, +1.111069621e-03f, +6.831329371e-04f, -7.453005747e-05f, -3.153390489e-04f, -1.240005643e-04f,
+    /* 24,12 */ +1.978701792e-05f, +2.426519708e-04f, +2.740180422e-04f, -2.720045869e-04f, -1.016463150e-03f, -8.485750922e-04f, +6.790305367e-04f, +2.214230624e-03f, +1.723899657e-03f, -9.169693475e-04f, -3.167649091e-03f, -2.440359294e-03f, +7.624077328e-04f, +3.182489175e-03f, +2.442181508e-03f, -3.841903571e-04f, -2.265625589e-03f, -1.686962051e-03f, +1.111689527e-04f, +1.099310451e-03f, +7.324018434e-04f, -3.826416878e-05f, -3.176854393e-04f, -1.390667857e-04f,
+    /* 24,13 */ +9.773337074e-06f, +2.289106872e-04f, +2.873471247e-04f, -2.227798586e-04f, -9.846924856e-04f, -9.027154107e-04f, +5.608747689e-04f, +2.161014579e-03f, +1.831793311e-03f, -7.286710477e-04f, -3.098667200e-03f, -2.582823494e-03f, +5.470211463e-04f, +3.115029215e-03f, +2.577645910e-03f, -2.048848004e-04f, -2.219616197e-03f, -1.781805749e-03f, +6.019646704e-06f, +1.081692695e-03f, +7.801571616e-04f, +2.971107404e-07f, -3.180964801e-04f, -1.542962580e-04f,
+    /* 24,14 */ +5.699432396e-07f, +2.146007649e-04f, +2.981837386e-04f, -1.748714247e-04f, -9.494468230e-04f, -9.507629285e-04f, +4.436146208e-04f, +2.099607997e-03f, +1.929900383e-03f, -5.397663057e-04f, -3.017272284e-03f, -2.713944640e-03f, +3.291928088e-04f, +3.034115138e-03f, +2.703524226e-03f, -2.203366063e-05f, -2.162944507e-03f, -1.871044125e-03f, -1.024893805e-04f, +1.058100498e-03f, +8.260698464e-04f, +4.103650150e-05f, -3.164556508e-04f, -1.695814378e-04f,
+    /* 24,15 */ -7.803305534e-06f, +1.998565163e-04f, +3.065935448e-04f, -1.284872781e-04f, -9.110442493e-04f, -9.926761418e-04f, +3.277888569e-04f, +2.030476715e-03f, +2.017938339e-03f, -3.511165299e-04f, -2.923929522e-03f, -2.833179926e-03f, +1.098945345e-04f, +2.940044791e-03f, +2.819157299e-03f, +1.635717275e-04f, -2.095678120e-03f, -1.954115341e-03f, -2.139154673e-04f, +1.028447094e-03f, +8.698090905e-04f, +8.381805218e-05f, -3.126544607e-04f, -1.848082291e-04f,
+    /* 24, 0 */ -1.364396009e-04f, -7.446376994e-05f, +5.066257662e-04f, +2.030015760e-04f, -9.054261715e-04f, -1.195525937e-03f, +4.683850093e-04f, +2.213825561e-03f, +1.188944940e-03f, -1.856378227e-03f, -2.833970964e-03f, -1.144377463e-04f, +2.758138339e-03f, +2.020697482e-03f, -1.023174933e-03f, -2.248631080e-03f, -6.097868283e-04f, +1.146194663e-03f, +9.693248739e-04f, -1.479047908e-04f, -5.177782008e-04f, -1.250536964e-04f, +1.414906982e-04f, +2.710774794e-05f,
+    /* 24, 1 */ -1.307026563e-04f, -8.975162518e-05f, +4.924131238e-04f, +2.542107757e-04f, -8.382130226e-04f, -1.235986710e-03f, +3.277877666e-04f, +2.166758574e-03f, +1.345406789e-03f, -1.683014413e-03f, -2.893234801e-03f, -3.426248829e-04f, +2.666119545e-03f, +2.174888063e-03f, -8.489895579e-04f, -2.270723567e-03f, -7.511023835e-04f, +1.088054225e-03f, +1.029345157e-03f, -8.915647260e-05f, -5.256253050e-04f, -1.535492882e-04f, +1.457592711e-04f, +3.374854096e-05f,
+    /* 24, 2 */ -1.243758393e-04f, -1.032931784e-04f, +4.753985127e-04f, +3.013338450e-04f, -7.682542954e-04f, -1.267577330e-03f, +1.888607322e-04f, +2.107952975e-03f, +1.491738775e-03f, -1.501737881e-03f, -2.935653267e-03f, -5.687514710e-04f, +2.558401145e-03f, +2.317921172e-03f, -6.673475630e-04f, -2.279727032e-03f, -8.914213340e-04f, +1.021228789e-03f, +1.084932315e-03f, -2.702967135e-05f, -5.299376467e-04f, -1.826837732e-04f, +1.491486840e-04f, +4.073257728e-05f,
+    /* 24, 3 */ -1.175537217e-04f, -1.151066589e-04f, +4.558506985e-04f, +3.442099484e-04f, -6.961194660e-04f, -1.290358261e-03f, +5.243891240e-05f, +2.037998203e-03f, +1.627194859e-03f, -1.313720334e-03f, -2.961057174e-03f, -7.914588446e-04f, +2.435570833e-03f, +2.448830599e-03f, -4.792680017e-04f, -2.275344863e-03f, -1.029819797e-03f, +9.459060659e-04f, +1.135545329e-03f, +3.816638860e-05f, -5.305040204e-04f, -2.122423012e-04f, +1.515631236e-04f, +4.801831197e-05f,
+    /* 24, 4 */ -1.103288160e-04f, -1.252215041e-04f, +4.340463917e-04f, +3.827158599e-04f, -6.223744454e-04f, -1.304448109e-03f, -8.067840470e-05f, +1.957545178e-03f, +1.751108674e-03f, -1.120165265e-03f, -2.969385358e-03f, -1.009410776e-03f, +2.298313921e-03f, +2.566719612e-03f, -2.858241016e-04f, -2.257363134e-03f, -1.165366520e-03f, +8.623375551e-04f, +1.180661312e-03f, +1.060874480e-04f, -5.271339238e-04f, -2.419953224e-04f, +1.529084143e-04f, +5.555842361e-05f,
+    /* 24, 5 */ -1.027909684e-04f, -1.336775649e-04f, +4.102676936e-04f, +4.167655723e-04f, -5.475775503e-04f, -1.310021272e-03f, -2.097323863e-04f, +1.867300846e-03f, +1.862896930e-03f, -9.222997333e-04f, -2.960684559e-03f, -1.221302229e-03f, +2.147409148e-03f, +2.670767418e-03f, -8.813668768e-05f, -2.225653372e-03f, -1.297129225e-03f, +7.708383352e-04f, +1.219779926e-03f, +1.763556677e-04f, -5.196599496e-04f, -2.716999419e-04f, +1.530928622e-04f, +6.329988035e-05f,
+    /* 24, 6 */ -9.502680145e-05f, -1.405242734e-04f, +3.847995909e-04f, +4.463096266e-04f, -4.722756447e-04f, -1.307305241e-03f, -3.340090076e-04f, +1.768022423e-03f, +1.962062202e-03f, -7.213660470e-04f, -2.935108571e-03f, -1.425867893e-03f, +1.983723834e-03f, +2.760235146e-03f, +1.126327965e-04f, -2.180174758e-03f, -1.424181074e-03f, +6.717863708e-04f, +1.252427754e-03f, +2.485613970e-04f, -5.079400707e-04f, -3.011014490e-04f, +1.520281231e-04f, +7.118405837e-05f,
+    /* 24, 7 */ -8.711921055e-05f, -1.458197836e-04f, +3.579275186e-04f, +4.713341756e-04f, -3.970004792e-04f, -1.296577576e-03f, -4.528429958e-04f, +1.660511380e-03f, +2.048195092e-03f, -5.186134147e-04f, -2.892916666e-03f, -1.621890436e-03f, +1.808208423e-03f, +2.834471303e-03f, +3.152896222e-04f, -2.120975750e-03f, -1.545607217e-03f, +5.656213428e-04f, +1.278162587e-03f, +3.222652495e-04f, -4.918597964e-04f, -3.299350101e-04f, +1.496300883e-04f, +7.914691395e-05f,
+    /* 24, 8 */ -7.914691395e-05f, -1.496300883e-04f, +3.299350101e-04f, +4.918597964e-04f, -3.222652495e-04f, -1.278162587e-03f, -5.656213428e-04f, +1.545607217e-03f, +2.120975750e-03f, -3.152896222e-04f, -2.834471303e-03f, -1.808208423e-03f, +1.621890436e-03f, +2.892916666e-03f, +5.186134147e-04f, -2.048195092e-03f, -1.660511380e-03f, +4.528429958e-04f, +1.296577576e-03f, +3.970004792e-04f, -4.713341756e-04f, -3.579275186e-04f, +1.458197836e-04f, +8.711921055e-05f,
+    /* 24, 9 */ -7.118405837e-05f, -1.520281231e-04f, +3.011014490e-04f, +5.079400707e-04f, -2.485613970e-04f, -1.252427754e-03f, -6.717863708e-04f, +1.424181074e-03f, +2.180174758e-03f, -1.126327965e-04f, -2.760235146e-03f, -1.983723834e-03f, +1.425867893e-03f, +2.935108571e-03f, +7.213660470e-04f, -1.962062202e-03f, -1.768022423e-03f, +3.340090076e-04f, +1.307305241e-03f, +4.722756447e-04f, -4.463096266e-04f, -3.847995909e-04f, +1.405242734e-04f, +9.502680145e-05f,
+    /* 24,10 */ -6.329988035e-05f, -1.530928622e-04f, +2.716999419e-04f, +5.196599496e-04f, -1.763556677e-04f, -1.219779926e-03f, -7.708383352e-04f, +1.297129225e-03f, +2.225653372e-03f, +8.813668768e-05f, -2.670767418e-03f, -2.147409148e-03f, +1.221302229e-03f, +2.960684559e-03f, +9.222997333e-04f, -1.862896930e-03f, -1.867300846e-03f, +2.097323863e-04f, +1.310021272e-03f, +5.475775503e-04f, -4.167655723e-04f, -4.102676936e-04f, +1.336775649e-04f, +1.027909684e-04f,
+    /* 24,11 */ -5.555842361e-05f, -1.529084143e-04f, +2.419953224e-04f, +5.271339238e-04f, -1.060874480e-04f, -1.180661312e-03f, -8.623375551e-04f, +1.165366520e-03f, +2.257363134e-03f, +2.858241016e-04f, -2.566719612e-03f, -2.298313921e-03f, +1.009410776e-03f, +2.969385358e-03f, +1.120165265e-03f, -1.751108674e-03f, -1.957545178e-03f, +8.067840470e-05f, +1.304448109e-03f, +6.223744454e-04f, -3.827158599e-04f, -4.340463917e-04f, +1.252215041e-04f, +1.103288160e-04f,
+    /* 24,12 */ -4.801831197e-05f, -1.515631236e-04f, +2.122423012e-04f, +5.305040204e-04f, -3.816638860e-05f, -1.135545329e-03f, -9.459060659e-04f, +1.029819797e-03f, +2.275344863e-03f, +4.792680017e-04f, -2.448830599e-03f, -2.435570833e-03f, +7.914588446e-04f, +2.961057174e-03f, +1.313720334e-03f, -1.627194859e-03f, -2.037998203e-03f, -5.243891240e-05f, +1.290358261e-03f, +6.961194660e-04f, -3.442099484e-04f, -4.558506985e-04f, +1.151066589e-04f, +1.175537217e-04f,
+    /* 24,13 */ -4.073257728e-05f, -1.491486840e-04f, +1.826837732e-04f, +5.299376467e-04f, +2.702967135e-05f, -1.084932315e-03f, -1.021228789e-03f, +8.914213340e-04f, +2.279727032e-03f, +6.673475630e-04f, -2.317921172e-03f, -2.558401145e-03f, +5.687514710e-04f, +2.935653267e-03f, +1.501737881e-03f, -1.491738775e-03f, -2.107952975e-03f, -1.888607322e-04f, +1.267577330e-03f, +7.682542954e-04f, -3.013338450e-04f, -4.753985127e-04f, +1.032931784e-04f, +1.243758393e-04f,
+    /* 24,14 */ -3.374854096e-05f, -1.457592711e-04f, +1.535492882e-04f, +5.256253050e-04f, +8.915647260e-05f, -1.029345157e-03f, -1.088054225e-03f, +7.511023835e-04f, +2.270723567e-03f, +8.489895579e-04f, -2.174888063e-03f, -2.666119545e-03f, +3.426248829e-04f, +2.893234801e-03f, +1.683014413e-03f, -1.345406789e-03f, -2.166758574e-03f, -3.277877666e-04f, +1.235986710e-03f, +8.382130226e-04f, -2.542107757e-04f, -4.924131238e-04f, +8.975162518e-05f, +1.307026563e-04f,
+    /* 24,15 */ -2.710774794e-05f, -1.414906982e-04f, +1.250536964e-04f, +5.177782008e-04f, +1.479047908e-04f, -9.693248739e-04f, -1.146194663e-03f, +6.097868283e-04f, +2.248631080e-03f, +1.023174933e-03f, -2.020697482e-03f, -2.758138339e-03f, +1.144377463e-04f, +2.833970964e-03f, +1.856378227e-03f, -1.188944940e-03f, -2.213825561e-03f, -4.683850093e-04f, +1.195525937e-03f, +9.054261715e-04f, -2.030015760e-04f, -5.066257662e-04f, +7.446376994e-05f, +1.364396009e-04f,
+    /* 20, 0 */ +8.618377023e-05f, +6.063813654e-04f, +5.504304823e-05f, -1.285351444e-03f, -7.884572117e-04f, +1.698526656e-03f, +2.051642156e-03f, -1.208844608e-03f, -3.071261118e-03f, -1.337669627e-04f, +3.018986987e-03f, +1.434631920e-03f, -1.928392685e-03f, -1.831072241e-03f, +6.629429882e-04f, +1.334851066e-03f, +2.665316224e-05f, -6.158469302e-04f, -1.222533602e-04f, +1.824770389e-04f,
+    /* 20, 1 */ +5.170459821e-05f, +5.921181369e-04f, +1.325211661e-04f, -1.227728603e-03f, -9.042412445e-04f, +1.556639033e-03f, +2.157910711e-03f, -9.769935819e-04f, -3.101316201e-03f, -4.002989059e-04f, +2.944767882e-03f, +1.652572896e-03f, -1.788832610e-03f, -1.953018956e-03f, +5.284364506e-04f, +1.375515478e-03f, +1.120226944e-04f, -6.201709543e-04f, -1.596279961e-04f, +5.435082024e-04f,
+    /* 20, 2 */ +1.907003227e-05f, +5.734309365e-04f, +2.052951315e-04f, -1.162740775e-03f, -1.009657150e-03f, +1.406715362e-03f, +2.246673287e-03f, -7.408907618e-04f, -3.109053425e-03f, -6.638330580e-04f, +2.849051730e-03f, +1.860929207e-03f, -1.633773999e-03f, -2.063170847e-03f, +3.857714352e-04f, +1.406686835e-03f, +2.004651158e-04f, -6.190441221e-04f, -1.979927117e-04f, +1.783734753e-04f,
+    /* 20, 3 */ -1.149811868e-05f, +5.507184044e-04f, +2.729399910e-04f, -1.091184321e-03f, -1.104169932e-03f, +1.250098855e-03f, +2.317552276e-03f, -5.023622502e-04f, -3.094549152e-03f, -9.223980063e-04f, +2.732457430e-03f, +2.058021578e-03f, -1.464165902e-03f, -2.160404235e-03f, +2.358727957e-04f, +1.427769061e-03f, +2.913280278e-04f, -6.121962531e-04f, -2.370049292e-04f, +1.734448317e-04f,
+    /* 20, 4 */ -3.981122763e-05f, +5.243991976e-04f, +3.350936324e-04f, -1.013885501e-03f, -1.187349554e-03f, +1.088157908e-03f, +2.370318438e-03f, -2.632332411e-04f, -3.058053364e-03f, -1.174062761e-03f, +2.595770512e-03f, +2.242244162e-03f, -1.281088328e-03f, -2.243678681e-03f, +7.975052491e-05f, +1.438235101e-03f, +3.839113875e-04f, -5.994006494e-04f, -2.762968272e-04f, +1.660093790e-04f,
+    /* 20, 5 */ -6.571426612e-05f, +4.949070444e-04f, +3.914578654e-04f, -9.316921975e-04f, -1.258871974e-03f, +9.222740706e-04f, +2.404890527e-03f, -2.531308203e-05f, -2.999986642e-03f, -1.416952434e-03f, +2.439937364e-03f, +2.412078410e-03f, -1.085745014e-03f, -2.312047403e-03f, -8.150702581e-05f, +1.437633739e-03f, +4.774724341e-04f, -5.804781630e-04f, -3.154780847e-04f, +1.559797103e-04f,
+    /* 20, 6 */ -8.908584503e-05f, +4.626858369e-04f, +4.417988543e-04f, -8.454656803e-04f, -1.318519207e-03f, +7.538301290e-04f, +2.421333651e-03f, +2.096193816e-04f, -2.920935727e-03f, -1.649263420e-03f, +2.266058084e-03f, +2.566106310e-03f, -8.794550343e-04f, -2.364667062e-03f, -2.467407834e-04f, +1.425595879e-03f, +5.712311871e-04f, -5.553009320e-04f, -3.541389831e-04f, +1.432933926e-04f,
+    /* 20, 7 */ -1.098378936e-04f, +4.281848093e-04f, +4.859469205e-04f, -7.560724855e-04f, -1.366178446e-03f, +5.841984075e-04f, +2.419856400e-03f, +4.398300532e-04f, -2.821647705e-03f, -1.869277969e-03f, +2.075378006e-03f, +2.703022864e-03f, -6.636433018e-04f, -2.400806791e-03f, -4.147293943e-04f, +1.401840253e-03f, +6.643764801e-04f, -5.237957356e-04f, -3.918538488e-04f, +1.279149940e-04f,
+    /* 20, 8 */ -1.279149940e-04f, +3.918538488e-04f, +5.237957356e-04f, -6.643764801e-04f, -1.401840253e-03f, +4.147293943e-04f, +2.400806791e-03f, +6.636433018e-04f, -2.703022864e-03f, -2.075378006e-03f, +1.869277969e-03f, +2.821647705e-03f, -4.398300532e-04f, -2.419856400e-03f, -5.841984075e-04f, +1.366178446e-03f, +7.560724855e-04f, -4.859469205e-04f, -4.281848093e-04f, +1.098378936e-04f,
+    /* 20, 9 */ -1.432933926e-04f, +3.541389831e-04f, +5.553009320e-04f, -5.712311871e-04f, -1.425595879e-03f, +2.467407834e-04f, +2.364667062e-03f, +8.794550343e-04f, -2.566106310e-03f, -2.266058084e-03f, +1.649263420e-03f, +2.920935727e-03f, -2.096193816e-04f, -2.421333651e-03f, -7.538301290e-04f, +1.318519207e-03f, +8.454656803e-04f, -4.417988543e-04f, -4.626858369e-04f, +8.908584503e-05f,
+    /* 20,10 */ -1.559797103e-04f, +3.154780847e-04f, +5.804781630e-04f, -4.774724341e-04f, -1.437633739e-03f, +8.150702581e-05f, +2.312047403e-03f, +1.085745014e-03f, -2.412078410e-03f, -2.439937364e-03f, +1.416952434e-03f, +2.999986642e-03f, +2.531308203e-05f, -2.404890527e-03f, -9.222740706e-04f, +1.258871974e-03f, +9.316921975e-04f, -3.914578654e-04f, -4.949070444e-04f, +6.571426612e-05f,
+    /* 20,11 */ -1.660093790e-04f, +2.762968272e-04f, +5.994006494e-04f, -3.839113875e-04f, -1.438235101e-03f, -7.975052491e-05f, +2.243678681e-03f, +1.281088328e-03f, -2.242244162e-03f, -2.595770512e-03f, +1.174062761e-03f, +3.058053364e-03f, +2.632332411e-04f, -2.370318438e-03f, -1.088157908e-03f, +1.187349554e-03f, +1.013885501e-03f, -3.350936324e-04f, -5.243991976e-04f, +3.981122763e-05f,
+    /* 20,12 */ -1.734448317e-04f, +2.370049292e-04f, +6.121962531e-04f, -2.913280278e-04f, -1.427769061e-03f, -2.358727957e-04f, +2.160404235e-03f, +1.464165902e-03f, -2.058021578e-03f, -2.732457430e-03f, +9.223980063e-04f, +3.094549152e-03f, +5.023622502e-04f, -2.317552276e-03f, -1.250098855e-03f, +1.104169932e-03f, +1.091184321e-03f, -2.729399910e-04f, -5.507184044e-04f, +1.149811868e-05f,
+    /* 20,13 */ -1.783734753e-04f, +1.979927117e-04f, +6.190441221e-04f, -2.004651158e-04f, -1.406686835e-03f, -3.857714352e-04f, +2.063170847e-03f, +1.633773999e-03f, -1.860929207e-03f, -2.849051730e-03f, +6.638330580e-04f, +3.109053425e-03f, +7.408907618e-04f, -2.246673287e-03f, -1.406715362e-03f, +1.009657150e-03f, +1.162740775e-03f, -2.052951315e-04f, -5.734309365e-04f, -1.907003227e-05f,
+    /* 20,14 */ -5.435082024e-04f, +1.596279961e-04f, +6.201709543e-04f, -1.120226944e-04f, -1.375515478e-03f, -5.284364506e-04f, +1.953018956e-03f, +1.788832610e-03f, -1.652572896e-03f, -2.944767882e-03f, +4.002989059e-04f, +3.101316201e-03f, +9.769935819e-04f, -2.157910711e-03f, -1.556639033e-03f, +9.042412445e-04f, +1.227728603e-03f, -1.325211661e-04f, -5.921181369e-04f, -5.170459821e-05f,
+    /* 20,15 */ -1.824770389e-04f, +1.222533602e-04f, +6.158469302e-04f, -2.665316224e-05f, -1.334851066e-03f, -6.629429882e-04f, +1.831072241e-03f, +1.928392685e-03f, -1.434631920e-03f, -3.018986987e-03f, +1.337669627e-04f, +3.071261118e-03f, +1.208844608e-03f, -2.051642156e-03f, -1.698526656e-03f, +7.884572117e-04f, +1.285351444e-03f, -5.504304823e-05f, -6.063813654e-04f, -8.618377023e-05f,
+    /* 20, 0 */ -2.034293425e-04f, +2.330977005e-04f, +6.663349221e-04f, -5.788237715e-04f, -1.543506114e-03f, +7.663264997e-04f, +2.637884518e-03f, -5.016073607e-04f, -3.414789539e-03f, -1.613262342e-04f, +3.393842146e-03f, +7.896927597e-04f, -2.589726790e-03f, -9.705894653e-04f, +1.498255744e-03f, +6.923557695e-04f, -6.435044748e-04f, -2.810018705e-04f, +2.009801156e-04f, +0.000000000e+00f,
+    /* 20, 1 */ -6.015260759e-04f, +1.858917095e-04f, +6.809814437e-04f, -4.649883812e-04f, -1.572899641e-03f, +5.610647582e-04f, +2.661959816e-03f, -2.131645492e-04f, -3.406214168e-03f, -4.825221542e-04f, +3.343371924e-03f, +1.074767465e-03f, -2.517456780e-03f, -1.171859801e-03f, +1.437039798e-03f, +8.043742580e-04f, -6.123036842e-04f, -3.290468323e-04f, +1.953154138e-04f, -3.513221827e-04f,
+    /* 20, 2 */ -1.932641301e-04f, +1.399021716e-04f, +6.877130848e-04f, -3.520154127e-04f, -1.586701669e-03f, +3.567578182e-04f, +2.662213263e-03f, +7.301175991e-05f, -3.368394423e-03f, -7.993627115e-04f, +3.263658730e-03f, +1.354174959e-03f, -2.421279702e-03f, -1.368123194e-03f, +1.359912061e-03f, +9.136368247e-04f, -5.726346524e-04f, -3.766412744e-04f, +1.862701081e-04f, +2.243392330e-05f,
+    /* 20, 3 */ -1.771389305e-04f, +9.560377684e-05f, +6.868748812e-04f, -2.410152618e-04f, -1.585326694e-03f, +1.553000173e-04f, +2.639129491e-03f, +3.543525656e-04f, -3.301882608e-03f, -1.108991788e-03f, +3.155261081e-03f, +1.625281932e-03f, -2.301637793e-03f, -1.557365345e-03f, +1.267093119e-03f, +1.018881552e-03f, -5.244930508e-04f, -4.231658296e-04f, +1.737162070e-04f, +3.471505729e-05f,
+    /* 20, 4 */ -1.604844080e-04f, +5.342390613e-05f, +6.788805869e-04f, -1.330327870e-04f, -1.569329509e-03f, -4.149146373e-05f, +2.593408320e-03f, +6.283684809e-04f, -3.207497769e-03f, -1.408623929e-03f, +3.019012048e-03f, +1.885504757e-03f, -2.159209806e-03f, -1.737592880e-03f, +1.158972903e-03f, +1.118840456e-03f, -4.679722330e-04f, -4.679795743e-04f, +1.575667726e-04f, +4.805250238e-05f,
+    /* 20, 5 */ -1.435528424e-04f, +1.373966937e-05f, +6.642048742e-04f, -2.903827106e-05f, -1.539395067e-03f, -2.318933016e-04f, +2.525953799e-03f, +8.926733333e-04f, -3.086315860e-03f, -1.695571566e-03f, +2.856012327e-03f, +2.132335710e-03f, -1.994908019e-03f, -1.906854484e-03f, +1.036111434e-03f, +1.212253447e-03f, -4.032663270e-04f, -5.104270987e-04f, +1.377794601e-04f, +6.235351094e-05f,
+    /* 20, 6 */ -1.265803873e-04f, -2.312428639e-05f, +6.433751213e-04f, +7.008046528e-05f, -1.496327216e-03f, -4.142913555e-04f, +2.437861323e-03f, +1.145006429e-03f, -2.939657349e-03f, -1.967271220e-03f, +2.667620552e-03f, +2.363368676e-03f, -1.809872756e-03f, -2.063262017e-03f, +8.992377119e-04f, +1.297882648e-03f, -3.306722314e-04f, -5.498460811e-04f, +1.143596169e-04f, +7.750368078e-05f,
+    /* 20, 7 */ -1.097853637e-04f, -5.689720211e-05f, +6.169629011e-04f, +1.635247423e-04f, -1.441036462e-03f, -5.871944806e-04f, +2.330402993e-03f, +1.383253258e-03f, -2.769072436e-03f, -2.221308400e-03f, +2.455440941e-03f, +2.576324026e-03f, -1.605464444e-03f, -2.205011397e-03f, +7.492467105e-04f, +1.374526925e-03f, -2.505904541e-04f, -5.855752858e-04f, +8.736287854e-05f, +9.336686615e-05f,
+    /* 20, 8 */ -9.336686615e-05f, -8.736287854e-05f, +5.855752858e-04f, +2.505904541e-04f, -1.374526925e-03f, -7.492467105e-04f, +2.205011397e-03f, +1.605464444e-03f, -2.576324026e-03f, -2.455440941e-03f, +2.221308400e-03f, +2.769072436e-03f, -1.383253258e-03f, -2.330402993e-03f, +5.871944806e-04f, +1.441036462e-03f, -1.635247423e-04f, -6.169629011e-04f, +5.689720211e-05f, +1.097853637e-04f,
+    /* 20, 9 */ -7.750368078e-05f, -1.143596169e-04f, +5.498460811e-04f, +3.306722314e-04f, -1.297882648e-03f, -8.992377119e-04f, +2.063262017e-03f, +1.809872756e-03f, -2.363368676e-03f, -2.667620552e-03f, +1.967271220e-03f, +2.939657349e-03f, -1.145006429e-03f, -2.437861323e-03f, +4.142913555e-04f, +1.496327216e-03f, -7.008046528e-05f, -6.433751213e-04f, +2.312428639e-05f, +1.265803873e-04f,
+    /* 20,10 */ -6.235351094e-05f, -1.377794601e-04f, +5.104270987e-04f, +4.032663270e-04f, -1.212253447e-03f, -1.036111434e-03f, +1.906854484e-03f, +1.994908019e-03f, -2.132335710e-03f, -2.856012327e-03f, +1.695571566e-03f, +3.086315860e-03f, -8.926733333e-04f, -2.525953799e-03f, +2.318933016e-04f, +1.539395067e-03f, +2.903827106e-05f, -6.642048742e-04f, -1.373966937e-05f, +1.435528424e-04f,
+    /* 20,11 */ -4.805250238e-05f, -1.575667726e-04f, +4.679795743e-04f, +4.679722330e-04f, -1.118840456e-03f, -1.158972903e-03f, +1.737592880e-03f, +2.159209806e-03f, -1.885504757e-03f, -3.019012048e-03f, +1.408623929e-03f, +3.207497769e-03f, -6.283684809e-04f, -2.593408320e-03f, +4.149146373e-05f, +1.569329509e-03f, +1.330327870e-04f, -6.788805869e-04f, -5.342390613e-05f, +1.604844080e-04f,
+    /* 20,12 */ -3.471505729e-05f, -1.737162070e-04f, +4.231658296e-04f, +5.244930508e-04f, -1.018881552e-03f, -1.267093119e-03f, +1.557365345e-03f, +2.301637793e-03f, -1.625281932e-03f, -3.155261081e-03f, +1.108991788e-03f, +3.301882608e-03f, -3.543525656e-04f, -2.639129491e-03f, -1.553000173e-04f, +1.585326694e-03f, +2.410152618e-04f, -6.868748812e-04f, -9.560377684e-05f, +1.771389305e-04f,
+    /* 20,13 */ -2.243392330e-05f, -1.862701081e-04f, +3.766412744e-04f, +5.726346524e-04f, -9.136368247e-04f, -1.359912061e-03f, +1.368123194e-03f, +2.421279702e-03f, -1.354174959e-03f, -3.263658730e-03f, +7.993627115e-04f, +3.368394423e-03f, -7.301175991e-05f, -2.662213263e-03f, -3.567578182e-04f, +1.586701669e-03f, +3.520154127e-04f, -6.877130848e-04f, -1.399021716e-04f, +1.932641301e-04f,
+    /* 20,14 */ +3.513221827e-04f, -1.953154138e-04f, +3.290468323e-04f, +6.123036842e-04f, -8.043742580e-04f, -1.437039798e-03f, +1.171859801e-03f, +2.517456780e-03f, -1.074767465e-03f, -3.343371924e-03f, +4.825221542e-04f, +3.406214168e-03f, +2.131645492e-04f, -2.661959816e-03f, -5.610647582e-04f, +1.572899641e-03f, +4.649883812e-04f, -6.809814437e-04f, -1.858917095e-04f, +6.015260759e-04f,
+    /* 20,15 */ +0.000000000e+00f, -2.009801156e-04f, +2.810018705e-04f, +6.435044748e-04f, -6.923557695e-04f, -1.498255744e-03f, +9.705894653e-04f, +2.589726790e-03f, -7.896927597e-04f, -3.393842146e-03f, +1.613262342e-04f, +3.414789539e-03f, +5.016073607e-04f, -2.637884518e-03f, -7.663264997e-04f, +1.543506114e-03f, +5.788237715e-04f, -6.663349221e-04f, -2.330977005e-04f, +2.034293425e-04f,
+    /* 20, 0 */ -1.941987182e-05f, -3.146481294e-04f, +5.561305343e-04f, +3.334278991e-04f, -1.561489082e-03f, -4.085266513e-04f, +2.806699025e-03f, +3.580334706e-04f, -3.695826941e-03f, -1.914605051e-04f, +3.720952014e-03f, -2.295171057e-05f, -2.868623587e-03f, +1.886978960e-04f, +1.629573547e-03f, -2.343761763e-04f, -6.035407960e-04f, +1.633790229e-04f, +3.476344875e-05f, +0.000000000e+00f,
+    /* 20, 1 */ +3.929324583e-04f, -3.051602666e-04f, +5.048306585e-04f, +4.229644027e-04f, -1.479104632e-03f, -6.165003319e-04f, +2.717079427e-03f, +6.839596419e-04f, -3.633185574e-03f, -5.723309145e-04f, +3.708003841e-03f, +3.177599071e-04f, -2.901501717e-03f, -4.082823094e-05f, +1.681921685e-03f, -1.265851889e-04f, -6.461214422e-04f, +1.369921977e-04f, +5.166761157e-05f, +0.000000000e+00f,
+    /* 20, 2 */ +0.000000000e+00f, -2.925136688e-04f, +4.505823818e-04f, +5.023683161e-04f, -1.383975926e-03f, -8.106644739e-04f, +2.601403151e-03f, +9.973590062e-04f, -3.534000256e-03f, -9.470733637e-04f, +3.656850180e-03f, +6.604608766e-04f, -2.904291326e-03f, -2.777120434e-04f, +1.717239595e-03f, -1.098579054e-05f, -6.829477483e-04f, +1.058859702e-04f, +7.000071077e-05f, +0.000000000e+00f,
+    /* 20, 3 */ +0.000000000e+00f, -2.771727451e-04f, +3.943146848e-04f, +5.711822345e-04f, -1.277754215e-03f, -9.892860040e-04f, +2.461570058e-03f, +1.295052213e-03f, -3.399644449e-03f, -1.311681723e-03f, +3.567787737e-03f, +1.001437562e-03f, -2.876278542e-03f, -5.194560271e-04f, +1.734397724e-03f, +1.113422841e-04f, -7.131247677e-04f, +7.019384523e-05f, +8.959420873e-05f, +0.000000000e+00f,
+    /* 20, 4 */ +0.000000000e+00f, -2.596009711e-04f, +3.369316672e-04f, +6.291078651e-04f, -1.162161945e-03f, -1.150868125e-03f, +2.299713337e-03f, +1.574086312e-03f, -3.231873732e-03f, -1.662267592e-03f, +3.441541225e-03f, +1.336946247e-03f, -2.817092852e-03f, -7.634316403e-04f, +1.732451285e-03f, +2.391787684e-04f, -7.358020638e-04f, +3.013243736e-05f, +1.102423612e-04f, +0.000000000e+00f,
+    /* 20, 5 */ +0.000000000e+00f, -2.402546692e-04f, +2.793008459e-04f, +6.760030262e-04f, -1.038968304e-03f, -1.294161821e-03f, +2.118169008e-03f, +1.831766066e-03f, -3.032802328e-03f, -1.995105343e-03f, +3.279257242e-03f, +1.663257052e-03f, -2.726718246e-03f, -1.006908470e-03f, +1.710658870e-03f, +3.711735089e-04f, -7.501884622e-04f, -1.399708473e-05f, +1.317024534e-04f, +0.000000000e+00f,
+    /* 20, 6 */ +0.000000000e+00f, -2.195772899e-04f, +2.222425350e-04f, +7.118766228e-04f, -9.099649801e-04f, -1.418173917e-03f, +1.919443545e-03f, +2.065681697e-03f, -2.804875489e-03f, -2.306675131e-03f, +3.082493013e-03f, +1.976698190e-03f, -2.605500127e-03f, -1.247085413e-03f, +1.668498942e-03f, +5.058593370e-04f, -7.555665992e-04f, -6.180883712e-05f, +1.536956226e-04f, +0.000000000e+00f,
+    /* 20, 7 */ +0.000000000e+00f, -1.979942392e-04f, +1.665204182e-04f, +7.368817426e-04f, -7.769424426e-04f, -1.522171671e-03f, +1.706180058e-03f, +2.273732749e-03f, -2.550838108e-03f, -2.593703365e-03f, +2.853200114e-03f, +2.273699984e-03f, -2.454147846e-03f, -1.481123511e-03f, +1.605683934e-03f, +6.416670612e-04f, -7.513070408e-04f, -1.128334053e-04f, +1.759082929e-04f, +0.000000000e+00f,
+    /* 20, 8 */ +0.000000000e+00f, -1.759082929e-04f, +1.128334053e-04f, +7.513070408e-04f, -6.416670612e-04f, -1.605683934e-03f, +1.481123511e-03f, +2.454147846e-03f, -2.273699984e-03f, -2.853200114e-03f, +2.593703365e-03f, +2.550838108e-03f, -2.273732749e-03f, -1.706180058e-03f, +1.522171671e-03f, +7.769424426e-04f, -7.368817426e-04f, -1.665204182e-04f, +1.979942392e-04f, +0.000000000e+00f,
+    /* 20, 9 */ +0.000000000e+00f, -1.536956226e-04f, +6.180883712e-05f, +7.555665992e-04f, -5.058593370e-04f, -1.668498942e-03f, +1.247085413e-03f, +2.605500127e-03f, -1.976698190e-03f, -3.082493013e-03f, +2.306675131e-03f, +2.804875489e-03f, -2.065681697e-03f, -1.919443545e-03f, +1.418173917e-03f, +9.099649801e-04f, -7.118766228e-04f, -2.222425350e-04f, +2.195772899e-04f, +0.000000000e+00f,
+    /* 20,10 */ +0.000000000e+00f, -1.317024534e-04f, +1.399708473e-05f, +7.501884622e-04f, -3.711735089e-04f, -1.710658870e-03f, +1.006908470e-03f, +2.726718246e-03f, -1.663257052e-03f, -3.279257242e-03f, +1.995105343e-03f, +3.032802328e-03f, -1.831766066e-03f, -2.118169008e-03f, +1.294161821e-03f, +1.038968304e-03f, -6.760030262e-04f, -2.793008459e-04f, +2.402546692e-04f, +0.000000000e+00f,
+    /* 20,11 */ +0.000000000e+00f, -1.102423612e-04f, -3.013243736e-05f, +7.358020638e-04f, -2.391787684e-04f, -1.732451285e-03f, +7.634316403e-04f, +2.817092852e-03f, -1.336946247e-03f, -3.441541225e-03f, +1.662267592e-03f, +3.231873732e-03f, -1.574086312e-03f, -2.299713337e-03f, +1.150868125e-03f, +1.162161945e-03f, -6.291078651e-04f, -3.369316672e-04f, +2.596009711e-04f, +0.000000000e+00f,
+    /* 20,12 */ +0.000000000e+00f, -8.959420873e-05f, -7.019384523e-05f, +7.131247677e-04f, -1.113422841e-04f, -1.734397724e-03f, +5.194560271e-04f, +2.876278542e-03f, -1.001437562e-03f, -3.567787737e-03f, +1.311681723e-03f, +3.399644449e-03f, -1.295052213e-03f, -2.461570058e-03f, +9.892860040e-04f, +1.277754215e-03f, -5.711822345e-04f, -3.943146848e-04f, +2.771727451e-04f, +0.000000000e+00f,
+    /* 20,13 */ +0.000000000e+00f, -7.000071077e-05f, -1.058859702e-04f, +6.829477483e-04f, +1.098579054e-05f, -1.717239595e-03f, +2.777120434e-04f, +2.904291326e-03f, -6.604608766e-04f, -3.656850180e-03f, +9.470733637e-04f, +3.534000256e-03f, -9.973590062e-04f, -2.601403151e-03f, +8.106644739e-04f, +1.383975926e-03f, -5.023683161e-04f, -4.505823818e-04f, +2.925136688e-04f, +0.000000000e+00f,
+    /* 20,14 */ +0.000000000e+00f, -5.166761157e-05f, -1.369921977e-04f, +6.461214422e-04f, +1.265851889e-04f, -1.681921685e-03f, +4.082823094e-05f, +2.901501717e-03f, -3.177599071e-04f, -3.708003841e-03f, +5.723309145e-04f, +3.633185574e-03f, -6.839596419e-04f, -2.717079427e-03f, +6.165003319e-04f, +1.479104632e-03f, -4.229644027e-04f, -5.048306585e-04f, +3.051602666e-04f, -3.929324583e-04f,
+    /* 20,15 */ +0.000000000e+00f, -3.476344875e-05f, -1.633790229e-04f, +6.035407960e-04f, +2.343761763e-04f, -1.629573547e-03f, -1.886978960e-04f, +2.868623587e-03f, +2.295171057e-05f, -3.720952014e-03f, +1.914605051e-04f, +3.695826941e-03f, -3.580334706e-04f, -2.806699025e-03f, +4.085266513e-04f, +1.561489082e-03f, -3.334278991e-04f, -5.561305343e-04f, +3.146481294e-04f, +1.941987182e-05f,
+    /* 16, 0 */ +5.220390682e-05f, +8.171943113e-04f, -8.986497643e-04f, -1.446340428e-03f, +2.494478678e-03f, +1.297377101e-03f, -3.898391184e-03f, -2.241676001e-04f, +3.985236927e-03f, -9.413140896e-04f, -2.682700956e-03f, +1.283836927e-03f, +1.053222343e-03f, -7.989322796e-04f, -1.116939505e-04f, +1.571395541e-04f,
+    /* 16, 1 */ -3.017522584e-06f, +8.224554322e-04f, -7.403312787e-04f, -1.584058293e-03f, +2.281207335e-03f, +1.631019258e-03f, -3.765802305e-03f, -6.696927435e-04f, +4.024834602e-03f, -5.670028406e-04f, -2.842687093e-03f, +1.097826180e-03f, +1.201600277e-03f, -7.671194843e-04f, -1.747127487e-04f, +1.853334990e-04f,
+    /* 16, 2 */ -5.335264618e-05f, +8.154372286e-04f, -5.806604605e-04f, -1.696100138e-03f, +2.046328714e-03f, +1.938434809e-03f, -3.589560871e-03f, -1.106825943e-03f, +4.016290169e-03f, -1.789305351e-04f, -2.971556495e-03f, +8.899684644e-04f, +1.341315594e-03f, -7.213960065e-04f, -2.404043435e-04f, +2.137577131e-04f,
+    /* 16, 3 */ -9.830954357e-05f, +7.970128377e-04f, -4.219420013e-04f, -1.781963746e-03f, +1.793485018e-03f, +2.216231187e-03f, -3.372309802e-03f, -1.530099436e-03f, +3.959337237e-03f, +2.181586899e-04f, -3.066783450e-03f, +6.622938144e-04f, +1.469918710e-03f, -6.616076810e-04f, -3.078052257e-04f, +7.052925564e-04f,
+    /* 16, 4 */ -1.375232535e-04f, +7.681852053e-04f, -2.663606532e-04f, -1.841529450e-03f, +1.526462906e-03f, +2.461468734e-03f, -3.117203525e-03f, -1.934233820e-03f, +3.854345030e-03f, +6.193258599e-04f, -3.126241364e-03f, +4.171838871e-04f, +1.585017189e-03f, -5.878191605e-04f, -3.758553213e-04f, +2.457392598e-04f,
+    /* 16, 5 */ -1.707546409e-04f, +7.300642099e-04f, -1.159532388e-04f, -1.875048978e-03f, +1.249136740e-03f, +2.671693350e-03f, -2.827860277e-03f, -2.314209556e-03f, +3.702317390e-03f, +1.019502933e-03f, -3.148242059e-03f, +1.573479538e-04f, +1.684315055e-03f, -5.003238841e-04f, -4.434113338e-04f, +2.470336005e-04f,
+    /* 16, 6 */ -1.978870754e-04f, +6.838430878e-04f, +2.741593655e-05f, -1.883129095e-03f, +9.654119112e-04f, +2.844961691e-03f, -2.508308289e-03f, -2.665334690e-03f, +3.504882792e-03f, +1.413561295e-03f, -3.131569469e-03f, -1.142067707e-04f, +1.765652028e-03f, -3.996506493e-04f, -5.092623113e-04f, +2.432370997e-04f,
+    /* 16, 7 */ -2.189210857e-04f, +6.307745862e-04f, +1.620759979e-04f, -1.866710442e-03f, +6.791690772e-04f, +2.979858667e-03f, -2.162926680e-03f, -2.983307830e-03f, +3.264275462e-03f, +1.796381989e-03f, -3.075507089e-03f, -3.942101476e-04f, +1.827042047e-03f, -2.865665382e-04f, -5.721472605e-04f, +2.339671870e-04f,
+    /* 16, 8 */ -2.339671870e-04f, +5.721472605e-04f, +2.865665382e-04f, -1.827042047e-03f, +3.942101476e-04f, +3.075507089e-03f, -1.796381989e-03f, -3.264275462e-03f, +2.983307830e-03f, +2.162926680e-03f, -2.979858667e-03f, -6.791690772e-04f, +1.866710442e-03f, -1.620759979e-04f, -6.307745862e-04f, +2.189210857e-04f,
+    /* 16, 9 */ -2.432370997e-04f, +5.092623113e-04f, +3.996506493e-04f, -1.765652028e-03f, +1.142067707e-04f, +3.131569469e-03f, -1.413561295e-03f, -3.504882792e-03f, +2.665334690e-03f, +2.508308289e-03f, -2.844961691e-03f, -9.654119112e-04f, +1.883129095e-03f, -2.741593655e-05f, -6.838430878e-04f, +1.978870754e-04f,
+    /* 16,10 */ -2.470336005e-04f, +4.434113338e-04f, +5.003238841e-04f, -1.684315055e-03f, -1.573479538e-04f, +3.148242059e-03f, -1.019502933e-03f, -3.702317390e-03f, +2.314209556e-03f, +2.827860277e-03f, -2.671693350e-03f, -1.249136740e-03f, +1.875048978e-03f, +1.159532388e-04f, -7.300642099e-04f, +1.707546409e-04f,
+    /* 16,11 */ -2.457392598e-04f, +3.758553213e-04f, +5.878191605e-04f, -1.585017189e-03f, -4.171838871e-04f, +3.126241364e-03f, -6.193258599e-04f, -3.854345030e-03f, +1.934233820e-03f, +3.117203525e-03f, -2.461468734e-03f, -1.526462906e-03f, +1.841529450e-03f, +2.663606532e-04f, -7.681852053e-04f, +1.375232535e-04f,
+    /* 16,12 */ -7.052925564e-04f, +3.078052257e-04f, +6.616076810e-04f, -1.469918710e-03f, -6.622938144e-04f, +3.066783450e-03f, -2.181586899e-04f, -3.959337237e-03f, +1.530099436e-03f, +3.372309802e-03f, -2.216231187e-03f, -1.793485018e-03f, +1.781963746e-03f, +4.219420013e-04f, -7.970128377e-04f, +9.830954357e-05f,
+    /* 16,13 */ -2.137577131e-04f, +2.404043435e-04f, +7.213960065e-04f, -1.341315594e-03f, -8.899684644e-04f, +2.971556495e-03f, +1.789305351e-04f, -4.016290169e-03f, +1.106825943e-03f, +3.589560871e-03f, -1.938434809e-03f, -2.046328714e-03f, +1.696100138e-03f, +5.806604605e-04f, -8.154372286e-04f, +5.335264618e-05f,
+    /* 16,14 */ -1.853334990e-04f, +1.747127487e-04f, +7.671194843e-04f, -1.201600277e-03f, -1.097826180e-03f, +2.842687093e-03f, +5.670028406e-04f, -4.024834602e-03f, +6.696927435e-04f, +3.765802305e-03f, -1.631019258e-03f, -2.281207335e-03f, +1.584058293e-03f, +7.403312787e-04f, -8.224554322e-04f, +3.017522584e-06f,
+    /* 16,15 */ -1.571395541e-04f, +1.116939505e-04f, +7.989322796e-04f, -1.053222343e-03f, -1.283836927e-03f, +2.682700956e-03f, +9.413140896e-04f, -3.985236927e-03f, +2.241676001e-04f, +3.898391184e-03f, -1.297377101e-03f, -2.494478678e-03f, +1.446340428e-03f, +8.986497643e-04f, -8.171943113e-04f, -5.220390682e-05f,
+    /* 16, 0 */ -2.607744081e-04f, +6.609893225e-04f, +4.777614003e-05f, -2.019079564e-03f, +1.736921610e-03f, +2.230081148e-03f, -4.008512761e-03f, -2.594451583e-04f, +4.172957467e-03f, -1.888269495e-03f, -2.040920379e-03f, +1.975903291e-03f, +1.180452271e-04f, -7.248571600e-04f, +2.468008838e-04f, +0.000000000e+00f,
+    /* 16, 1 */ -2.676863913e-04f, +5.906930705e-04f, +2.025069901e-04f, -2.030408814e-03f, +1.418499470e-03f, +2.533235894e-03f, -3.790472440e-03f, -7.745724648e-04f, +4.280846994e-03f, -1.512096765e-03f, -2.325335551e-03f, +1.900137256e-03f, +2.927280105e-04f, -7.805529904e-04f, +2.254162899e-04f, +0.000000000e+00f,
+    /* 16, 2 */ -2.680102581e-04f, +5.157202047e-04f, +3.442245196e-04f, -2.011119828e-03f, +1.090886046e-03f, +2.794113365e-03f, -3.522578151e-03f, -1.278469954e-03f, +4.330057965e-03f, -1.106477171e-03f, -2.585166870e-03f, +1.791556445e-03f, +4.737630093e-04f, -8.263772041e-04f, +1.964117366e-04f, +0.000000000e+00f,
+    /* 16, 3 */ -7.633646088e-04f, +4.377966605e-04f, +4.713317060e-04f, -1.962888420e-03f, +7.592982470e-04f, +3.009813640e-03f, -3.209287239e-03f, -1.763847458e-03f, +4.319343992e-03f, -6.768749158e-04f, -2.815661672e-03f, +1.650477084e-03f, +6.583936022e-04f, -8.607109932e-04f, +1.597335965e-04f, -4.634120047e-04f,
+    /* 16, 4 */ -2.423668462e-04f, +3.585907293e-04f, +5.825699836e-04f, -1.887792011e-03f, +4.288533077e-04f, +3.178189562e-03f, -2.855695312e-03f, -2.223705909e-03f, +4.248362401e-03f, -2.292254995e-04f, -3.012400483e-03f, +1.477771292e-03f, +8.436545409e-04f, -8.820535056e-04f, +1.154955663e-04f, +2.338334874e-05f,
+    /* 16, 5 */ -2.066858426e-04f, +2.796840991e-04f, +6.770251471e-04f, -1.788258811e-03f, +1.044882353e-04f, +3.297866045e-03f, -2.467449129e-03f, -2.651446905e-03f, +4.117686315e-03f, +2.301520823e-04f, -3.171379014e-03f, +1.274872332e-03f, +1.026416356e-03f, -8.890585891e-04f, +6.398775754e-05f, +4.782938304e-05f,
+    /* 16, 6 */ -1.715009195e-04f, +2.025461567e-04f, +7.541266524e-04f, -1.667012713e-03f, -2.091154912e-04f, +3.368246274e-03f, -2.050651409e-03f, -3.040975547e-03f, +3.928801804e-03f, +6.946508648e-04f, -3.289085050e-03f, +1.043770075e-03f, +1.203434747e-03f, -8.805703554e-04f, +5.682450650e-06f, +7.520965874e-05f,
+    /* 16, 7 */ -1.374865801e-04f, +1.285119093e-04f, +8.136405975e-04f, -1.527015027e-03f, -5.076007987e-04f, +3.389504923e-03f, -1.611759203e-03f, -3.386794836e-03f, +3.684090065e-03f, +1.157477637e-03f, -3.362568736e-03f, +7.869964389e-04f, +1.371404208e-03f, -8.556567843e-04f, -5.876379361e-05f, +1.052264476e-04f,
+    /* 16, 8 */ -1.052264476e-04f, +5.876379361e-05f, +8.556567843e-04f, -1.371404208e-03f, -7.869964389e-04f, +3.362568736e-03f, -1.157477637e-03f, -3.684090065e-03f, +3.386794836e-03f, +1.611759203e-03f, -3.389504923e-03f, +5.076007987e-04f, +1.527015027e-03f, -8.136405975e-04f, -1.285119093e-04f, +1.374865801e-04f,
+    /* 16, 9 */ -7.520965874e-05f, -5.682450650e-06f, +8.805703554e-04f, -1.203434747e-03f, -1.043770075e-03f, +3.289085050e-03f, -6.946508648e-04f, -3.928801804e-03f, +3.040975547e-03f, +2.050651409e-03f, -3.368246274e-03f, +2.091154912e-04f, +1.667012713e-03f, -7.541266524e-04f, -2.025461567e-04f, +1.715009195e-04f,
+    /* 16,10 */ -4.782938304e-05f, -6.398775754e-05f, +8.890585891e-04f, -1.026416356e-03f, -1.274872332e-03f, +3.171379014e-03f, -2.301520823e-04f, -4.117686315e-03f, +2.651446905e-03f, +2.467449129e-03f, -3.297866045e-03f, -1.044882353e-04f, +1.788258811e-03f, -6.770251471e-04f, -2.796840991e-04f, +2.066858426e-04f,
+    /* 16,11 */ -2.338334874e-05f, -1.154955663e-04f, +8.820535056e-04f, -8.436545409e-04f, -1.477771292e-03f, +3.012400483e-03f, +2.292254995e-04f, -4.248362401e-03f, +2.223705909e-03f, +2.855695312e-03f, -3.178189562e-03f, -4.288533077e-04f, +1.887792011e-03f, -5.825699836e-04f, -3.585907293e-04f, +2.423668462e-04f,
+    /* 16,12 */ +4.634120047e-04f, -1.597335965e-04f, +8.607109932e-04f, -6.583936022e-04f, -1.650477084e-03f, +2.815661672e-03f, +6.768749158e-04f, -4.319343992e-03f, +1.763847458e-03f, +3.209287239e-03f, -3.009813640e-03f, -7.592982470e-04f, +1.962888420e-03f, -4.713317060e-04f, -4.377966605e-04f, +7.633646088e-04f,
+    /* 16,13 */ +0.000000000e+00f, -1.964117366e-04f, +8.263772041e-04f, -4.737630093e-04f, -1.791556445e-03f, +2.585166870e-03f, +1.106477171e-03f, -4.330057965e-03f, +1.278469954e-03f, +3.522578151e-03f, -2.794113365e-03f, -1.090886046e-03f, +2.011119828e-03f, -3.442245196e-04f, -5.157202047e-04f, +2.680102581e-04f,
+    /* 16,14 */ +0.000000000e+00f, -2.254162899e-04f, +7.805529904e-04f, -2.927280105e-04f, -1.900137256e-03f, +2.325335551e-03f, +1.512096765e-03f, -4.280846994e-03f, +7.745724648e-04f, +3.790472440e-03f, -2.533235894e-03f, -1.418499470e-03f, +2.030408814e-03f, -2.025069901e-04f, -5.906930705e-04f, +2.676863913e-04f,
+    /* 16,15 */ +0.000000000e+00f, -2.468008838e-04f, +7.248571600e-04f, -1.180452271e-04f, -1.975903291e-03f, +2.040920379e-03f, +1.888269495e-03f, -4.172957467e-03f, +2.594451583e-04f, +4.008512761e-03f, -2.230081148e-03f, -1.736921610e-03f, +2.019079564e-03f, -4.777614003e-05f, -6.609893225e-04f, +2.607744081e-04f,
+    /* 16, 0 */ -1.129954761e-04f, -3.969443331e-04f, +7.863457700e-04f, -1.968627401e-03f, +6.635626436e-04f, +3.065104554e-03f, -4.014723865e-03f, -2.972906332e-04f, +4.272130602e-03f, -2.778972616e-03f, -1.044103847e-03f, +2.069667087e-03f, -6.906315332e-04f, -2.376896670e-04f, +1.523656204e-04f, +0.000000000e+00f,
+    /* 16, 1 */ -7.672972562e-05f, -4.458313625e-04f, +8.604609066e-04f, -1.839340292e-03f, +2.869810557e-04f, +3.294943885e-03f, -3.696990655e-03f, -8.869321804e-04f, +4.464175630e-03f, -2.440111513e-03f, -1.421957113e-03f, +2.139058837e-03f, -5.737860098e-04f, -3.273087897e-04f, +1.941703587e-04f, +0.000000000e+00f,
+    /* 16, 2 */ -4.409159553e-05f, -4.801879450e-04f, +9.129725459e-04f, -1.685578963e-03f, -7.929130936e-05f, +3.465994861e-03f, -3.324955442e-03f, -1.461843594e-03f, +4.586914931e-03f, -2.053108579e-03f, -1.790295465e-03f, +2.173860444e-03f, -4.367566844e-04f, -4.185294039e-04f, +2.375950982e-04f, +0.000000000e+00f,
+    /* 16, 3 */ +4.855802427e-04f, -5.008892512e-04f, +9.443143993e-04f, -1.511399569e-03f, -4.293120730e-04f, +3.576852207e-03f, -2.905512498e-03f, -2.012499819e-03f, +4.637578076e-03f, -1.623510702e-03f, -2.142238116e-03f, +2.171667537e-03f, -2.809771210e-04f, -5.093533546e-04f, +2.816859426e-04f, +0.000000000e+00f,
+    /* 16, 4 */ +0.000000000e+00f, -5.090007623e-04f, +9.553248878e-04f, -1.321049384e-03f, -7.576441950e-04f, +3.627202827e-03f, -2.446291464e-03f, -2.529812382e-03f, +4.614629236e-03f, -1.157740361e-03f, -2.470980778e-03f, +2.130685105e-03f, -1.083629217e-04f, -5.976383603e-04f, +3.253590588e-04f, +0.000000000e+00f,
+    /* 16, 5 */ +0.000000000e+00f, -5.057402285e-04f, +9.472067544e-04f, -1.118874842e-03f, -1.059441268e-03f, +3.617806804e-03f, -1.955510679e-03f, -3.005292216e-03f, +4.517805471e-03f, -6.629933593e-04f, -2.769928158e-03f, +2.049789183e-03f, +7.870314989e-05f, -6.811391839e-04f, +3.674140144e-04f, +0.000000000e+00f,
+    /* 16, 6 */ +0.000000000e+00f, -4.924395299e-04f, +9.214808972e-04f, -9.092313688e-04f, -1.330518654e-03f, +3.550458465e-03f, -1.441821213e-03f, -3.431200966e-03f, +4.348131386e-03f, -1.471199733e-04f, -3.032825993e-03f, +1.928577081e-03f, +2.773970394e-04f, -7.575537377e-04f, +4.065510896e-04f, +0.000000000e+00f,
+    /* 16, 7 */ +0.000000000e+00f, -4.705072223e-04f, +8.799357120e-04f, -6.963968181e-04f, -1.567409460e-03f, +3.427928686e-03f, -9.141447359e-04f, -3.800687882e-03f, +4.107909720e-03f, +3.815084058e-04f, -3.253889950e-03f, +1.767404663e-03f, +4.844901765e-04f, -8.245732787e-04f, +4.413924818e-04f, +0.000000000e+00f,
+    /* 16, 8 */ +0.000000000e+00f, -4.413924818e-04f, +8.245732787e-04f, -4.844901765e-04f, -1.767404663e-03f, +3.253889950e-03f, -3.815084058e-04f, -4.107909720e-03f, +3.800687882e-03f, +9.141447359e-04f, -3.427928686e-03f, +1.567409460e-03f, +6.963968181e-04f, -8.799357120e-04f, +4.705072223e-04f, +0.000000000e+00f,
+    /* 16, 9 */ +0.000000000e+00f, -4.065510896e-04f, +7.575537377e-04f, -2.773970394e-04f, -1.928577081e-03f, +3.032825993e-03f, +1.471199733e-04f, -4.348131386e-03f, +3.431200966e-03f, +1.441821213e-03f, -3.550458465e-03f, +1.330518654e-03f, +9.092313688e-04f, -9.214808972e-04f, +4.924395299e-04f, +0.000000000e+00f,
+    /* 16,10 */ +0.000000000e+00f, -3.674140144e-04f, +6.811391839e-04f, -7.870314989e-05f, -2.049789183e-03f, +2.769928158e-03f, +6.629933593e-04f, -4.517805471e-03f, +3.005292216e-03f, +1.955510679e-03f, -3.617806804e-03f, +1.059441268e-03f, +1.118874842e-03f, -9.472067544e-04f, +5.057402285e-04f, +0.000000000e+00f,
+    /* 16,11 */ +0.000000000e+00f, -3.253590588e-04f, +5.976383603e-04f, +1.083629217e-04f, -2.130685105e-03f, +2.470980778e-03f, +1.157740361e-03f, -4.614629236e-03f, +2.529812382e-03f, +2.446291464e-03f, -3.627202827e-03f, +7.576441950e-04f, +1.321049384e-03f, -9.553248878e-04f, +5.090007623e-04f, +0.000000000e+00f,
+    /* 16,12 */ +0.000000000e+00f, -2.816859426e-04f, +5.093533546e-04f, +2.809771210e-04f, -2.171667537e-03f, +2.142238116e-03f, +1.623510702e-03f, -4.637578076e-03f, +2.012499819e-03f, +2.905512498e-03f, -3.576852207e-03f, +4.293120730e-04f, +1.511399569e-03f, -9.443143993e-04f, +5.008892512e-04f, -4.855802427e-04f,
+    /* 16,13 */ +0.000000000e+00f, -2.375950982e-04f, +4.185294039e-04f, +4.367566844e-04f, -2.173860444e-03f, +1.790295465e-03f, +2.053108579e-03f, -4.586914931e-03f, +1.461843594e-03f, +3.324955442e-03f, -3.465994861e-03f, +7.929130936e-05f, +1.685578963e-03f, -9.129725459e-04f, +4.801879450e-04f, +4.409159553e-05f,
+    /* 16,14 */ +0.000000000e+00f, -1.941703587e-04f, +3.273087897e-04f, +5.737860098e-04f, -2.139058837e-03f, +1.421957113e-03f, +2.440111513e-03f, -4.464175630e-03f, +8.869321804e-04f, +3.696990655e-03f, -3.294943885e-03f, -2.869810557e-04f, +1.839340292e-03f, -8.604609066e-04f, +4.458313625e-04f, +7.672972562e-05f,
+    /* 16,15 */ +0.000000000e+00f, -1.523656204e-04f, +2.376896670e-04f, +6.906315332e-04f, -2.069667087e-03f, +1.044103847e-03f, +2.778972616e-03f, -4.272130602e-03f, +2.972906332e-04f, +4.014723865e-03f, -3.065104554e-03f, -6.635626436e-04f, +1.968627401e-03f, -7.863457700e-04f, +3.969443331e-04f, +1.129954761e-04f,
+    /* 12, 0 */ +1.006092301e-03f, -1.356592138e-03f, -5.291224839e-04f, +3.716365432e-03f, -3.908475818e-03f, -3.377012930e-04f, +4.272902045e-03f, -3.529241849e-03f, +1.347121185e-04f, +1.576582805e-03f, -1.021094463e-03f, +2.080147558e-04f,
+    /* 12, 1 */ +9.703330579e-04f, -1.123568815e-03f, -8.960631472e-04f, +3.830455785e-03f, -3.478978472e-03f, -1.006731283e-03f, +4.564422369e-03f, -3.270752231e-03f, -2.802589631e-04f, +1.777910422e-03f, -1.013271813e-03f, +1.540375404e-04f,
+    /* 12, 2 */ +9.162319547e-04f, -8.831264337e-04f, -1.229457804e-03f, +3.871345986e-03f, -2.993425932e-03f, -1.656773890e-03f, +4.776559314e-03f, -2.944064628e-03f, -7.081711396e-04f, +1.955059288e-03f, -9.809799530e-04f, +8.895597490e-05f,
+    /* 12, 3 */ +8.464715149e-04f, -6.407365814e-04f, -1.524162434e-03f, +3.840336583e-03f, -2.461816027e-03f, -2.275602698e-03f, +4.904341682e-03f, -2.553815646e-03f, -1.140836495e-03f, +2.102763165e-03f, -9.230670815e-04f, +1.349777819e-05f,
+    /* 12, 4 */ +7.639192828e-04f, -4.016125871e-04f, -1.776040886e-03f, +3.740131991e-03f, -1.894910812e-03f, -2.851629129e-03f, +4.944422783e-03f, -2.106045312e-03f, -1.569658753e-03f, +2.216140176e-03f, -8.389345160e-04f, -7.124952580e-05f,
+    /* 12, 5 */ +6.715456333e-04f, -1.706046467e-04f, -1.982015497e-03f, +3.574748146e-03f, -1.304005398e-03f, -3.374137989e-03f, +4.895165032e-03f, -1.608099956e-03f, -1.985807614e-03f, +2.290824513e-03f, -7.285864297e-04f, -1.638417811e-04f,
+    /* 12, 6 */ +5.723437636e-04f, +4.789167018e-05f, -2.140092391e-03f, +3.349394124e-03f, -7.006885404e-04f, -3.833503957e-03f, +4.756688774e-03f, -1.068504673e-03f, -2.380403858e-03f, +2.323091638e-03f, -5.926669574e-04f, -2.624916697e-04f,
+    /* 12, 7 */ +4.692537952e-04f, +2.500119139e-04f, -2.249361715e-03f, +3.070330944e-03f, -9.660032268e-05f, -4.221384345e-03f, +4.530883988e-03f, -4.968076992e-04f, -2.744711242e-03f, +2.309973659e-03f, -4.324830696e-04f, -3.650927179e-04f,
+    /* 12, 8 */ +3.650927179e-04f, +4.324830696e-04f, -2.309973659e-03f, +2.744711242e-03f, +4.968076992e-04f, -4.530883988e-03f, +4.221384345e-03f, +9.660032268e-05f, -3.070330944e-03f, +2.249361715e-03f, -2.500119139e-04f, -4.692537952e-04f,
+    /* 12, 9 */ +2.624916697e-04f, +5.926669574e-04f, -2.323091638e-03f, +2.380403858e-03f, +1.068504673e-03f, -4.756688774e-03f, +3.833503957e-03f, +7.006885404e-04f, -3.349394124e-03f, +2.140092391e-03f, -4.789167018e-05f, -5.723437636e-04f,
+    /* 12,10 */ +1.638417811e-04f, +7.285864297e-04f, -2.290824513e-03f, +1.985807614e-03f, +1.608099956e-03f, -4.895165032e-03f, +3.374137989e-03f, +1.304005398e-03f, -3.574748146e-03f, +1.982015497e-03f, +1.706046467e-04f, -6.715456333e-04f,
+    /* 12,11 */ +7.124952580e-05f, +8.389345160e-04f, -2.216140176e-03f, +1.569658753e-03f, +2.106045312e-03f, -4.944422783e-03f, +2.851629129e-03f, +1.894910812e-03f, -3.740131991e-03f, +1.776040886e-03f, +4.016125871e-04f, -7.639192828e-04f,
+    /* 12,12 */ -1.349777819e-05f, +9.230670815e-04f, -2.102763165e-03f, +1.140836495e-03f, +2.553815646e-03f, -4.904341682e-03f, +2.275602698e-03f, +2.461816027e-03f, -3.840336583e-03f, +1.524162434e-03f, +6.407365814e-04f, -8.464715149e-04f,
+    /* 12,13 */ -8.895597490e-05f, +9.809799530e-04f, -1.955059288e-03f, +7.081711396e-04f, +2.944064628e-03f, -4.776559314e-03f, +1.656773890e-03f, +2.993425932e-03f, -3.871345986e-03f, +1.229457804e-03f, +8.831264337e-04f, -9.162319547e-04f,
+    /* 12,14 */ -1.540375404e-04f, +1.013271813e-03f, -1.777910422e-03f, +2.802589631e-04f, +3.270752231e-03f, -4.564422369e-03f, +1.006731283e-03f, +3.478978472e-03f, -3.830455785e-03f, +8.960631472e-04f, +1.123568815e-03f, -9.703330579e-04f,
+    /* 12,15 */ -2.080147558e-04f, +1.021094463e-03f, -1.576582805e-03f, -1.347121185e-04f, +3.529241849e-03f, -4.272902045e-03f, +3.377012930e-04f, +3.908475818e-03f, -3.716365432e-03f, +5.291224839e-04f, +1.356592138e-03f, -1.006092301e-03f,
+    /* 12, 0 */ +7.165252154e-04f, -4.291307465e-04f, -1.619691310e-03f, +4.112050532e-03f, -3.684471854e-03f, -3.806742210e-04f, +4.167864669e-03f, -4.064044128e-03f, +1.285631838e-03f, +6.994376016e-04f, -8.205283947e-04f, +3.212754781e-04f,
+    /* 12, 1 */ +6.042449626e-04f, -1.684748882e-04f, -1.902468489e-03f, +4.073990388e-03f, -3.134198780e-03f, -1.133926469e-03f, +4.572939653e-03f, -3.928365474e-03f, +9.055999228e-04f, +9.731965741e-04f, -9.124383184e-04f, +3.311614162e-04f,
+    /* 12, 2 */ +4.874350434e-04f, +7.694308689e-05f, -2.130082097e-03f, +3.953363211e-03f, -2.529802622e-03f, -1.863076830e-03f, +4.889863669e-03f, -3.705392569e-03f, +4.862599326e-04f, +1.243729140e-03f, -9.884795125e-04f, +3.301883495e-04f,
+    /* 12, 3 */ +3.696734183e-04f, +3.022578517e-04f, -2.300114973e-03f, +3.755428771e-03f, -1.885047396e-03f, -2.552675098e-03f, +5.110657479e-03f, -3.397530000e-03f, +3.551878686e-05f, +1.504029611e-03f, -1.045032679e-03f, +3.171093301e-04f,
+    /* 12, 4 */ +2.542791637e-04f, +5.034105172e-04f, -2.411611526e-03f, +3.487034212e-03f, -1.214371318e-03f, -3.188181667e-03f, +5.229403271e-03f, -3.009202669e-03f, -4.376222644e-04f, +1.746934410e-03f, -1.078752986e-03f, +2.909691299e-04f,
+    /* 12, 5 */ +1.442362351e-04f, +6.772076791e-04f, -2.465038541e-03f, +3.156407157e-03f, -5.325427440e-04f, -3.756300237e-03f, +5.242404627e-03f, -2.546799451e-03f, -9.232494237e-04f, +1.965304174e-03f, -1.086687765e-03f, +2.511615343e-04f,
+    /* 12, 6 */ +4.213190220e-05f, +8.213542577e-04f, -2.462211671e-03f, +2.772920825e-03f, +1.456866600e-04f, -4.245279979e-03f, +5.148294544e-03f, -2.018567160e-03f, -1.410748009e-03f, +2.152214196e-03f, -1.066390037e-03f, +1.974793328e-04f,
+    /* 12, 7 */ -4.988918599e-05f, +9.344605010e-04f, -2.406190818e-03f, +2.346837790e-03f, +8.059229054e-04f, -4.645179801e-03f, +4.948088353e-03f, -1.434456490e-03f, -1.889039390e-03f, +2.301148282e-03f, -1.016024228e-03f, +1.301548676e-04f,
+    /* 12, 8 */ -1.301548676e-04f, +1.016024228e-03f, -2.301148282e-03f, +1.889039390e-03f, +1.434456490e-03f, -4.948088353e-03f, +4.645179801e-03f, -8.059229054e-04f, -2.346837790e-03f, +2.406190818e-03f, -9.344605010e-04f, +4.988918599e-05f,
+    /* 12, 9 */ -1.974793328e-04f, +1.066390037e-03f, -2.152214196e-03f, +1.410748009e-03f, +2.018567160e-03f, -5.148294544e-03f, +4.245279979e-03f, -1.456866600e-04f, -2.772920825e-03f, +2.462211671e-03f, -8.213542577e-04f, -4.213190220e-05f,
+    /* 12,10 */ -2.511615343e-04f, +1.086687765e-03f, -1.965304174e-03f, +9.232494237e-04f, +2.546799451e-03f, -5.242404627e-03f, +3.756300237e-03f, +5.325427440e-04f, -3.156407157e-03f, +2.465038541e-03f, -6.772076791e-04f, -1.442362351e-04f,
+    /* 12,11 */ -2.909691299e-04f, +1.078752986e-03f, -1.746934410e-03f, +4.376222644e-04f, +3.009202669e-03f, -5.229403271e-03f, +3.188181667e-03f, +1.214371318e-03f, -3.487034212e-03f, +2.411611526e-03f, -5.034105172e-04f, -2.542791637e-04f,
+    /* 12,12 */ -3.171093301e-04f, +1.045032679e-03f, -1.504029611e-03f, -3.551878686e-05f, +3.397530000e-03f, -5.110657479e-03f, +2.552675098e-03f, +1.885047396e-03f, -3.755428771e-03f, +2.300114973e-03f, -3.022578517e-04f, -3.696734183e-04f,
+    /* 12,13 */ -3.301883495e-04f, +9.884795125e-04f, -1.243729140e-03f, -4.862599326e-04f, +3.705392569e-03f, -4.889863669e-03f, +1.863076830e-03f, +2.529802622e-03f, -3.953363211e-03f, +2.130082097e-03f, -7.694308689e-05f, -4.874350434e-04f,
+    /* 12,14 */ -3.311614162e-04f, +9.124383184e-04f, -9.731965741e-04f, -9.055999228e-04f, +3.928365474e-03f, -4.572939653e-03f, +1.133926469e-03f, +3.134198780e-03f, -4.073990388e-03f, +1.902468489e-03f, +1.684748882e-04f, -6.042449626e-04f,
+    /* 12,15 */ -3.212754781e-04f, +8.205283947e-04f, -6.994376016e-04f, -1.285631838e-03f, +4.064044128e-03f, -4.167864669e-03f, +3.806742210e-04f, +3.684471854e-03f, -4.112050532e-03f, +1.619691310e-03f, +4.291307465e-04f, -7.165252154e-04f
+};

+ 49 - 0
Engine/lib/openal-soft/Alc/compat.h

@@ -0,0 +1,49 @@
+#ifndef AL_COMPAT_H
+#define AL_COMPAT_H
+
+#include "alstring.h"
+
+#ifdef _WIN32
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+WCHAR *strdupW(const WCHAR *str);
+
+/* Opens a file with standard I/O. The filename is expected to be UTF-8. */
+FILE *al_fopen(const char *fname, const char *mode);
+
+#define HAVE_DYNLOAD 1
+
+#else
+
+#define al_fopen fopen
+
+#if defined(HAVE_DLFCN_H) && !defined(IN_IDE_PARSER)
+#define HAVE_DYNLOAD 1
+#endif
+
+#endif
+
+struct FileMapping {
+#ifdef _WIN32
+    HANDLE file;
+    HANDLE fmap;
+#else
+    int fd;
+#endif
+    void *ptr;
+    size_t len;
+};
+struct FileMapping MapFileToMem(const char *fname);
+void UnmapFileMem(const struct FileMapping *mapping);
+
+al_string GetProcPath(void);
+
+#ifdef HAVE_DYNLOAD
+void *LoadLib(const char *name);
+void CloseLib(void *handle);
+void *GetSymbol(void *handle, const char *name);
+#endif
+
+#endif /* AL_COMPAT_H */

+ 412 - 0
Engine/lib/openal-soft/Alc/effects/chorus.c

@@ -0,0 +1,412 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 2013 by Mike Gorchak
+ * This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the
+ *  Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#include "config.h"
+
+#include <math.h>
+#include <stdlib.h>
+
+#include "alMain.h"
+#include "alFilter.h"
+#include "alAuxEffectSlot.h"
+#include "alError.h"
+#include "alu.h"
+
+
+enum ChorusWaveForm {
+    CWF_Triangle = AL_CHORUS_WAVEFORM_TRIANGLE,
+    CWF_Sinusoid = AL_CHORUS_WAVEFORM_SINUSOID
+};
+
+typedef struct ALchorusState {
+    DERIVE_FROM_TYPE(ALeffectState);
+
+    ALfloat *SampleBuffer[2];
+    ALuint BufferLength;
+    ALuint offset;
+    ALuint lfo_range;
+    ALfloat lfo_scale;
+    ALint lfo_disp;
+
+    /* Gains for left and right sides */
+    ALfloat Gain[2][MAX_OUTPUT_CHANNELS];
+
+    /* effect parameters */
+    enum ChorusWaveForm waveform;
+    ALint delay;
+    ALfloat depth;
+    ALfloat feedback;
+} ALchorusState;
+
+static ALvoid ALchorusState_Destruct(ALchorusState *state);
+static ALboolean ALchorusState_deviceUpdate(ALchorusState *state, ALCdevice *Device);
+static ALvoid ALchorusState_update(ALchorusState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props);
+static ALvoid ALchorusState_process(ALchorusState *state, ALuint SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels);
+DECLARE_DEFAULT_ALLOCATORS(ALchorusState)
+
+DEFINE_ALEFFECTSTATE_VTABLE(ALchorusState);
+
+
+static void ALchorusState_Construct(ALchorusState *state)
+{
+    ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
+    SET_VTABLE2(ALchorusState, ALeffectState, state);
+
+    state->BufferLength = 0;
+    state->SampleBuffer[0] = NULL;
+    state->SampleBuffer[1] = NULL;
+    state->offset = 0;
+    state->lfo_range = 1;
+    state->waveform = CWF_Triangle;
+}
+
+static ALvoid ALchorusState_Destruct(ALchorusState *state)
+{
+    al_free(state->SampleBuffer[0]);
+    state->SampleBuffer[0] = NULL;
+    state->SampleBuffer[1] = NULL;
+
+    ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
+}
+
+static ALboolean ALchorusState_deviceUpdate(ALchorusState *state, ALCdevice *Device)
+{
+    ALuint maxlen;
+    ALuint it;
+
+    maxlen = fastf2u(AL_CHORUS_MAX_DELAY * 3.0f * Device->Frequency) + 1;
+    maxlen = NextPowerOf2(maxlen);
+
+    if(maxlen != state->BufferLength)
+    {
+        void *temp = al_calloc(16, maxlen * sizeof(ALfloat) * 2);
+        if(!temp) return AL_FALSE;
+
+        al_free(state->SampleBuffer[0]);
+        state->SampleBuffer[0] = temp;
+        state->SampleBuffer[1] = state->SampleBuffer[0] + maxlen;
+
+        state->BufferLength = maxlen;
+    }
+
+    for(it = 0;it < state->BufferLength;it++)
+    {
+        state->SampleBuffer[0][it] = 0.0f;
+        state->SampleBuffer[1][it] = 0.0f;
+    }
+
+    return AL_TRUE;
+}
+
+static ALvoid ALchorusState_update(ALchorusState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props)
+{
+    ALfloat frequency = (ALfloat)Device->Frequency;
+    ALfloat coeffs[MAX_AMBI_COEFFS];
+    ALfloat rate;
+    ALint phase;
+
+    switch(props->Chorus.Waveform)
+    {
+        case AL_CHORUS_WAVEFORM_TRIANGLE:
+            state->waveform = CWF_Triangle;
+            break;
+        case AL_CHORUS_WAVEFORM_SINUSOID:
+            state->waveform = CWF_Sinusoid;
+            break;
+    }
+    state->depth = props->Chorus.Depth;
+    state->feedback = props->Chorus.Feedback;
+    state->delay = fastf2i(props->Chorus.Delay * frequency);
+
+    /* Gains for left and right sides */
+    CalcXYZCoeffs(-1.0f, 0.0f, 0.0f, 0.0f, coeffs);
+    ComputePanningGains(Device->Dry, coeffs, Slot->Params.Gain, state->Gain[0]);
+    CalcXYZCoeffs( 1.0f, 0.0f, 0.0f, 0.0f, coeffs);
+    ComputePanningGains(Device->Dry, coeffs, Slot->Params.Gain, state->Gain[1]);
+
+    phase = props->Chorus.Phase;
+    rate = props->Chorus.Rate;
+    if(!(rate > 0.0f))
+    {
+        state->lfo_scale = 0.0f;
+        state->lfo_range = 1;
+        state->lfo_disp = 0;
+    }
+    else
+    {
+        /* Calculate LFO coefficient */
+        state->lfo_range = fastf2u(frequency/rate + 0.5f);
+        switch(state->waveform)
+        {
+            case CWF_Triangle:
+                state->lfo_scale = 4.0f / state->lfo_range;
+                break;
+            case CWF_Sinusoid:
+                state->lfo_scale = F_TAU / state->lfo_range;
+                break;
+        }
+
+        /* Calculate lfo phase displacement */
+        state->lfo_disp = fastf2i(state->lfo_range * (phase/360.0f));
+    }
+}
+
+static inline void Triangle(ALint *delay_left, ALint *delay_right, ALuint offset, const ALchorusState *state)
+{
+    ALfloat lfo_value;
+
+    lfo_value = 2.0f - fabsf(2.0f - state->lfo_scale*(offset%state->lfo_range));
+    lfo_value *= state->depth * state->delay;
+    *delay_left = fastf2i(lfo_value) + state->delay;
+
+    offset += state->lfo_disp;
+    lfo_value = 2.0f - fabsf(2.0f - state->lfo_scale*(offset%state->lfo_range));
+    lfo_value *= state->depth * state->delay;
+    *delay_right = fastf2i(lfo_value) + state->delay;
+}
+
+static inline void Sinusoid(ALint *delay_left, ALint *delay_right, ALuint offset, const ALchorusState *state)
+{
+    ALfloat lfo_value;
+
+    lfo_value = 1.0f + sinf(state->lfo_scale*(offset%state->lfo_range));
+    lfo_value *= state->depth * state->delay;
+    *delay_left = fastf2i(lfo_value) + state->delay;
+
+    offset += state->lfo_disp;
+    lfo_value = 1.0f + sinf(state->lfo_scale*(offset%state->lfo_range));
+    lfo_value *= state->depth * state->delay;
+    *delay_right = fastf2i(lfo_value) + state->delay;
+}
+
+#define DECL_TEMPLATE(Func)                                                   \
+static void Process##Func(ALchorusState *state, const ALuint SamplesToDo,     \
+  const ALfloat *restrict SamplesIn, ALfloat (*restrict out)[2])              \
+{                                                                             \
+    const ALuint bufmask = state->BufferLength-1;                             \
+    ALfloat *restrict leftbuf = state->SampleBuffer[0];                       \
+    ALfloat *restrict rightbuf = state->SampleBuffer[1];                      \
+    ALuint offset = state->offset;                                            \
+    const ALfloat feedback = state->feedback;                                 \
+    ALuint it;                                                                \
+                                                                              \
+    for(it = 0;it < SamplesToDo;it++)                                         \
+    {                                                                         \
+        ALint delay_left, delay_right;                                        \
+        Func(&delay_left, &delay_right, offset, state);                       \
+                                                                              \
+        out[it][0] = leftbuf[(offset-delay_left)&bufmask];                    \
+        leftbuf[offset&bufmask] = (out[it][0]+SamplesIn[it]) * feedback;      \
+                                                                              \
+        out[it][1] = rightbuf[(offset-delay_right)&bufmask];                  \
+        rightbuf[offset&bufmask] = (out[it][1]+SamplesIn[it]) * feedback;     \
+                                                                              \
+        offset++;                                                             \
+    }                                                                         \
+    state->offset = offset;                                                   \
+}
+
+DECL_TEMPLATE(Triangle)
+DECL_TEMPLATE(Sinusoid)
+
+#undef DECL_TEMPLATE
+
+static ALvoid ALchorusState_process(ALchorusState *state, ALuint SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels)
+{
+    ALuint it, kt;
+    ALuint base;
+
+    for(base = 0;base < SamplesToDo;)
+    {
+        ALfloat temps[128][2];
+        ALuint td = minu(128, SamplesToDo-base);
+
+        switch(state->waveform)
+        {
+            case CWF_Triangle:
+                ProcessTriangle(state, td, SamplesIn[0]+base, temps);
+                break;
+            case CWF_Sinusoid:
+                ProcessSinusoid(state, td, SamplesIn[0]+base, temps);
+                break;
+        }
+
+        for(kt = 0;kt < NumChannels;kt++)
+        {
+            ALfloat gain = state->Gain[0][kt];
+            if(fabsf(gain) > GAIN_SILENCE_THRESHOLD)
+            {
+                for(it = 0;it < td;it++)
+                    SamplesOut[kt][it+base] += temps[it][0] * gain;
+            }
+
+            gain = state->Gain[1][kt];
+            if(fabsf(gain) > GAIN_SILENCE_THRESHOLD)
+            {
+                for(it = 0;it < td;it++)
+                    SamplesOut[kt][it+base] += temps[it][1] * gain;
+            }
+        }
+
+        base += td;
+    }
+}
+
+
+typedef struct ALchorusStateFactory {
+    DERIVE_FROM_TYPE(ALeffectStateFactory);
+} ALchorusStateFactory;
+
+static ALeffectState *ALchorusStateFactory_create(ALchorusStateFactory *UNUSED(factory))
+{
+    ALchorusState *state;
+
+    NEW_OBJ0(state, ALchorusState)();
+    if(!state) return NULL;
+
+    return STATIC_CAST(ALeffectState, state);
+}
+
+DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALchorusStateFactory);
+
+
+ALeffectStateFactory *ALchorusStateFactory_getFactory(void)
+{
+    static ALchorusStateFactory ChorusFactory = { { GET_VTABLE2(ALchorusStateFactory, ALeffectStateFactory) } };
+
+    return STATIC_CAST(ALeffectStateFactory, &ChorusFactory);
+}
+
+
+void ALchorus_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
+{
+    ALeffectProps *props = &effect->Props;
+    switch(param)
+    {
+        case AL_CHORUS_WAVEFORM:
+            if(!(val >= AL_CHORUS_MIN_WAVEFORM && val <= AL_CHORUS_MAX_WAVEFORM))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Chorus.Waveform = val;
+            break;
+
+        case AL_CHORUS_PHASE:
+            if(!(val >= AL_CHORUS_MIN_PHASE && val <= AL_CHORUS_MAX_PHASE))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Chorus.Phase = val;
+            break;
+
+        default:
+            SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+    }
+}
+void ALchorus_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
+{
+    ALchorus_setParami(effect, context, param, vals[0]);
+}
+void ALchorus_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
+{
+    ALeffectProps *props = &effect->Props;
+    switch(param)
+    {
+        case AL_CHORUS_RATE:
+            if(!(val >= AL_CHORUS_MIN_RATE && val <= AL_CHORUS_MAX_RATE))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Chorus.Rate = val;
+            break;
+
+        case AL_CHORUS_DEPTH:
+            if(!(val >= AL_CHORUS_MIN_DEPTH && val <= AL_CHORUS_MAX_DEPTH))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Chorus.Depth = val;
+            break;
+
+        case AL_CHORUS_FEEDBACK:
+            if(!(val >= AL_CHORUS_MIN_FEEDBACK && val <= AL_CHORUS_MAX_FEEDBACK))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Chorus.Feedback = val;
+            break;
+
+        case AL_CHORUS_DELAY:
+            if(!(val >= AL_CHORUS_MIN_DELAY && val <= AL_CHORUS_MAX_DELAY))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Chorus.Delay = val;
+            break;
+
+        default:
+            SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+    }
+}
+void ALchorus_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
+{
+    ALchorus_setParamf(effect, context, param, vals[0]);
+}
+
+void ALchorus_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
+{
+    const ALeffectProps *props = &effect->Props;
+    switch(param)
+    {
+        case AL_CHORUS_WAVEFORM:
+            *val = props->Chorus.Waveform;
+            break;
+
+        case AL_CHORUS_PHASE:
+            *val = props->Chorus.Phase;
+            break;
+
+        default:
+            SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+    }
+}
+void ALchorus_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
+{
+    ALchorus_getParami(effect, context, param, vals);
+}
+void ALchorus_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
+{
+    const ALeffectProps *props = &effect->Props;
+    switch(param)
+    {
+        case AL_CHORUS_RATE:
+            *val = props->Chorus.Rate;
+            break;
+
+        case AL_CHORUS_DEPTH:
+            *val = props->Chorus.Depth;
+            break;
+
+        case AL_CHORUS_FEEDBACK:
+            *val = props->Chorus.Feedback;
+            break;
+
+        case AL_CHORUS_DELAY:
+            *val = props->Chorus.Delay;
+            break;
+
+        default:
+            SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+    }
+}
+void ALchorus_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
+{
+    ALchorus_getParamf(effect, context, param, vals);
+}
+
+DEFINE_ALEFFECT_VTABLE(ALchorus);

+ 254 - 0
Engine/lib/openal-soft/Alc/effects/compressor.c

@@ -0,0 +1,254 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 2013 by Anis A. Hireche
+ * This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the
+ *  Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#include <stdlib.h>
+
+#include "config.h"
+#include "alError.h"
+#include "alMain.h"
+#include "alAuxEffectSlot.h"
+#include "alu.h"
+
+
+typedef struct ALcompressorState {
+    DERIVE_FROM_TYPE(ALeffectState);
+
+    /* Effect gains for each channel */
+    ALfloat Gain[MAX_EFFECT_CHANNELS][MAX_OUTPUT_CHANNELS];
+
+    /* Effect parameters */
+    ALboolean Enabled;
+    ALfloat AttackRate;
+    ALfloat ReleaseRate;
+    ALfloat GainCtrl;
+} ALcompressorState;
+
+static ALvoid ALcompressorState_Destruct(ALcompressorState *state);
+static ALboolean ALcompressorState_deviceUpdate(ALcompressorState *state, ALCdevice *device);
+static ALvoid ALcompressorState_update(ALcompressorState *state, const ALCdevice *device, const ALeffectslot *slot, const ALeffectProps *props);
+static ALvoid ALcompressorState_process(ALcompressorState *state, ALuint SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels);
+DECLARE_DEFAULT_ALLOCATORS(ALcompressorState)
+
+DEFINE_ALEFFECTSTATE_VTABLE(ALcompressorState);
+
+
+static void ALcompressorState_Construct(ALcompressorState *state)
+{
+    ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
+    SET_VTABLE2(ALcompressorState, ALeffectState, state);
+
+    state->Enabled = AL_TRUE;
+    state->AttackRate = 0.0f;
+    state->ReleaseRate = 0.0f;
+    state->GainCtrl = 1.0f;
+}
+
+static ALvoid ALcompressorState_Destruct(ALcompressorState *state)
+{
+    ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
+}
+
+static ALboolean ALcompressorState_deviceUpdate(ALcompressorState *state, ALCdevice *device)
+{
+    const ALfloat attackTime = device->Frequency * 0.2f; /* 200ms Attack */
+    const ALfloat releaseTime = device->Frequency * 0.4f; /* 400ms Release */
+
+    state->AttackRate = 1.0f / attackTime;
+    state->ReleaseRate = 1.0f / releaseTime;
+
+    return AL_TRUE;
+}
+
+static ALvoid ALcompressorState_update(ALcompressorState *state, const ALCdevice *device, const ALeffectslot *slot, const ALeffectProps *props)
+{
+    ALuint i;
+
+    state->Enabled = props->Compressor.OnOff;
+
+    STATIC_CAST(ALeffectState,state)->OutBuffer = device->FOAOut.Buffer;
+    STATIC_CAST(ALeffectState,state)->OutChannels = device->FOAOut.NumChannels;
+    for(i = 0;i < 4;i++)
+        ComputeFirstOrderGains(device->FOAOut, IdentityMatrixf.m[i],
+                               slot->Params.Gain, state->Gain[i]);
+}
+
+static ALvoid ALcompressorState_process(ALcompressorState *state, ALuint SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels)
+{
+    ALuint i, j, k;
+    ALuint base;
+
+    for(base = 0;base < SamplesToDo;)
+    {
+        ALfloat temps[64][4];
+        ALuint td = minu(64, SamplesToDo-base);
+
+        /* Load samples into the temp buffer first. */
+        for(j = 0;j < 4;j++)
+        {
+            for(i = 0;i < td;i++)
+                temps[i][j] = SamplesIn[j][i+base];
+        }
+
+        if(state->Enabled)
+        {
+            ALfloat gain = state->GainCtrl;
+            ALfloat output, amplitude;
+
+            for(i = 0;i < td;i++)
+            {
+                /* Roughly calculate the maximum amplitude from the 4-channel
+                 * signal, and attack or release the gain control to reach it.
+                 */
+                amplitude = fabsf(temps[i][0]);
+                amplitude = maxf(amplitude + fabsf(temps[i][1]),
+                                 maxf(amplitude + fabsf(temps[i][2]),
+                                      amplitude + fabsf(temps[i][3])));
+                if(amplitude > gain)
+                    gain = minf(gain+state->AttackRate, amplitude);
+                else if(amplitude < gain)
+                    gain = maxf(gain-state->ReleaseRate, amplitude);
+
+                /* Apply the inverse of the gain control to normalize/compress
+                 * the volume. */
+                output = 1.0f / clampf(gain, 0.5f, 2.0f);
+                for(j = 0;j < 4;j++)
+                    temps[i][j] *= output;
+            }
+
+            state->GainCtrl = gain;
+        }
+        else
+        {
+            ALfloat gain = state->GainCtrl;
+            ALfloat output, amplitude;
+
+            for(i = 0;i < td;i++)
+            {
+                /* Same as above, except the amplitude is forced to 1. This
+                 * helps ensure smooth gain changes when the compressor is
+                 * turned on and off.
+                 */
+                amplitude = 1.0f;
+                if(amplitude > gain)
+                    gain = minf(gain+state->AttackRate, amplitude);
+                else if(amplitude < gain)
+                    gain = maxf(gain-state->ReleaseRate, amplitude);
+
+                output = 1.0f / clampf(gain, 0.5f, 2.0f);
+                for(j = 0;j < 4;j++)
+                    temps[i][j] *= output;
+            }
+
+            state->GainCtrl = gain;
+        }
+
+        /* Now mix to the output. */
+        for(j = 0;j < 4;j++)
+        {
+            for(k = 0;k < NumChannels;k++)
+            {
+                ALfloat gain = state->Gain[j][k];
+                if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
+                    continue;
+
+                for(i = 0;i < td;i++)
+                    SamplesOut[k][base+i] += gain * temps[i][j];
+            }
+        }
+
+        base += td;
+    }
+}
+
+
+typedef struct ALcompressorStateFactory {
+    DERIVE_FROM_TYPE(ALeffectStateFactory);
+} ALcompressorStateFactory;
+
+static ALeffectState *ALcompressorStateFactory_create(ALcompressorStateFactory *UNUSED(factory))
+{
+    ALcompressorState *state;
+
+    NEW_OBJ0(state, ALcompressorState)();
+    if(!state) return NULL;
+
+    return STATIC_CAST(ALeffectState, state);
+}
+
+DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALcompressorStateFactory);
+
+ALeffectStateFactory *ALcompressorStateFactory_getFactory(void)
+{
+    static ALcompressorStateFactory CompressorFactory = { { GET_VTABLE2(ALcompressorStateFactory, ALeffectStateFactory) } };
+
+    return STATIC_CAST(ALeffectStateFactory, &CompressorFactory);
+}
+
+
+void ALcompressor_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
+{
+    ALeffectProps *props = &effect->Props;
+    switch(param)
+    {
+        case AL_COMPRESSOR_ONOFF:
+            if(!(val >= AL_COMPRESSOR_MIN_ONOFF && val <= AL_COMPRESSOR_MAX_ONOFF))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Compressor.OnOff = val;
+            break;
+
+    default:
+        SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+    }
+}
+void ALcompressor_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
+{
+    ALcompressor_setParami(effect, context, param, vals[0]);
+}
+void ALcompressor_setParamf(ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALfloat UNUSED(val))
+{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
+void ALcompressor_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
+{
+    ALcompressor_setParamf(effect, context, param, vals[0]);
+}
+
+void ALcompressor_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
+{ 
+    const ALeffectProps *props = &effect->Props;
+    switch(param)
+    {
+        case AL_COMPRESSOR_ONOFF:
+            *val = props->Compressor.OnOff;
+            break;
+        default:
+            SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+    }
+}
+void ALcompressor_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
+{
+    ALcompressor_getParami(effect, context, param, vals);
+}
+void ALcompressor_getParamf(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALfloat *UNUSED(val))
+{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
+void ALcompressor_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
+{
+    ALcompressor_getParamf(effect, context, param, vals);
+}
+
+DEFINE_ALEFFECT_VTABLE(ALcompressor);

+ 201 - 0
Engine/lib/openal-soft/Alc/effects/dedicated.c

@@ -0,0 +1,201 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 2011 by Chris Robinson.
+ * This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the
+ *  Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+
+#include "alMain.h"
+#include "alFilter.h"
+#include "alAuxEffectSlot.h"
+#include "alError.h"
+#include "alu.h"
+
+
+typedef struct ALdedicatedState {
+    DERIVE_FROM_TYPE(ALeffectState);
+
+    ALfloat gains[MAX_OUTPUT_CHANNELS];
+} ALdedicatedState;
+
+static ALvoid ALdedicatedState_Destruct(ALdedicatedState *state);
+static ALboolean ALdedicatedState_deviceUpdate(ALdedicatedState *state, ALCdevice *device);
+static ALvoid ALdedicatedState_update(ALdedicatedState *state, const ALCdevice *device, const ALeffectslot *Slot, const ALeffectProps *props);
+static ALvoid ALdedicatedState_process(ALdedicatedState *state, ALuint SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels);
+DECLARE_DEFAULT_ALLOCATORS(ALdedicatedState)
+
+DEFINE_ALEFFECTSTATE_VTABLE(ALdedicatedState);
+
+
+static void ALdedicatedState_Construct(ALdedicatedState *state)
+{
+    ALsizei s;
+
+    ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
+    SET_VTABLE2(ALdedicatedState, ALeffectState, state);
+
+    for(s = 0;s < MAX_OUTPUT_CHANNELS;s++)
+        state->gains[s] = 0.0f;
+}
+
+static ALvoid ALdedicatedState_Destruct(ALdedicatedState *state)
+{
+    ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
+}
+
+static ALboolean ALdedicatedState_deviceUpdate(ALdedicatedState *UNUSED(state), ALCdevice *UNUSED(device))
+{
+    return AL_TRUE;
+}
+
+static ALvoid ALdedicatedState_update(ALdedicatedState *state, const ALCdevice *device, const ALeffectslot *Slot, const ALeffectProps *props)
+{
+    ALfloat Gain;
+    ALuint i;
+
+    for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
+        state->gains[i] = 0.0f;
+
+    Gain = Slot->Params.Gain * props->Dedicated.Gain;
+    if(Slot->Params.EffectType == AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT)
+    {
+        int idx;
+        if((idx=GetChannelIdxByName(device->RealOut, LFE)) != -1)
+        {
+            STATIC_CAST(ALeffectState,state)->OutBuffer = device->RealOut.Buffer;
+            STATIC_CAST(ALeffectState,state)->OutChannels = device->RealOut.NumChannels;
+            state->gains[idx] = Gain;
+        }
+    }
+    else if(Slot->Params.EffectType == AL_EFFECT_DEDICATED_DIALOGUE)
+    {
+        int idx;
+        /* Dialog goes to the front-center speaker if it exists, otherwise it
+         * plays from the front-center location. */
+        if((idx=GetChannelIdxByName(device->RealOut, FrontCenter)) != -1)
+        {
+            STATIC_CAST(ALeffectState,state)->OutBuffer = device->RealOut.Buffer;
+            STATIC_CAST(ALeffectState,state)->OutChannels = device->RealOut.NumChannels;
+            state->gains[idx] = Gain;
+        }
+        else
+        {
+            ALfloat coeffs[MAX_AMBI_COEFFS];
+            CalcXYZCoeffs(0.0f, 0.0f, -1.0f, 0.0f, coeffs);
+
+            STATIC_CAST(ALeffectState,state)->OutBuffer = device->Dry.Buffer;
+            STATIC_CAST(ALeffectState,state)->OutChannels = device->Dry.NumChannels;
+            ComputePanningGains(device->Dry, coeffs, Gain, state->gains);
+        }
+    }
+}
+
+static ALvoid ALdedicatedState_process(ALdedicatedState *state, ALuint SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels)
+{
+    const ALfloat *gains = state->gains;
+    ALuint i, c;
+
+    for(c = 0;c < NumChannels;c++)
+    {
+        if(!(fabsf(gains[c]) > GAIN_SILENCE_THRESHOLD))
+            continue;
+
+        for(i = 0;i < SamplesToDo;i++)
+            SamplesOut[c][i] += SamplesIn[0][i] * gains[c];
+    }
+}
+
+
+typedef struct ALdedicatedStateFactory {
+    DERIVE_FROM_TYPE(ALeffectStateFactory);
+} ALdedicatedStateFactory;
+
+ALeffectState *ALdedicatedStateFactory_create(ALdedicatedStateFactory *UNUSED(factory))
+{
+    ALdedicatedState *state;
+
+    NEW_OBJ0(state, ALdedicatedState)();
+    if(!state) return NULL;
+
+    return STATIC_CAST(ALeffectState, state);
+}
+
+DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALdedicatedStateFactory);
+
+
+ALeffectStateFactory *ALdedicatedStateFactory_getFactory(void)
+{
+    static ALdedicatedStateFactory DedicatedFactory = { { GET_VTABLE2(ALdedicatedStateFactory, ALeffectStateFactory) } };
+
+    return STATIC_CAST(ALeffectStateFactory, &DedicatedFactory);
+}
+
+
+void ALdedicated_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val))
+{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
+void ALdedicated_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
+{
+    ALdedicated_setParami(effect, context, param, vals[0]);
+}
+void ALdedicated_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
+{
+    ALeffectProps *props = &effect->Props;
+    switch(param)
+    {
+        case AL_DEDICATED_GAIN:
+            if(!(val >= 0.0f && isfinite(val)))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Dedicated.Gain = val;
+            break;
+
+        default:
+            SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+    }
+}
+void ALdedicated_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
+{
+    ALdedicated_setParamf(effect, context, param, vals[0]);
+}
+
+void ALdedicated_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val))
+{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
+void ALdedicated_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
+{
+    ALdedicated_getParami(effect, context, param, vals);
+}
+void ALdedicated_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
+{
+    const ALeffectProps *props = &effect->Props;
+    switch(param)
+    {
+        case AL_DEDICATED_GAIN:
+            *val = props->Dedicated.Gain;
+            break;
+
+        default:
+            SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+    }
+}
+void ALdedicated_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
+{
+    ALdedicated_getParamf(effect, context, param, vals);
+}
+
+DEFINE_ALEFFECT_VTABLE(ALdedicated);

+ 298 - 0
Engine/lib/openal-soft/Alc/effects/distortion.c

@@ -0,0 +1,298 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 2013 by Mike Gorchak
+ * This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the
+ *  Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#include "config.h"
+
+#include <math.h>
+#include <stdlib.h>
+
+#include "alMain.h"
+#include "alFilter.h"
+#include "alAuxEffectSlot.h"
+#include "alError.h"
+#include "alu.h"
+
+
+typedef struct ALdistortionState {
+    DERIVE_FROM_TYPE(ALeffectState);
+
+    /* Effect gains for each channel */
+    ALfloat Gain[MAX_OUTPUT_CHANNELS];
+
+    /* Effect parameters */
+    ALfilterState lowpass;
+    ALfilterState bandpass;
+    ALfloat attenuation;
+    ALfloat edge_coeff;
+} ALdistortionState;
+
+static ALvoid ALdistortionState_Destruct(ALdistortionState *state);
+static ALboolean ALdistortionState_deviceUpdate(ALdistortionState *state, ALCdevice *device);
+static ALvoid ALdistortionState_update(ALdistortionState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props);
+static ALvoid ALdistortionState_process(ALdistortionState *state, ALuint SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels);
+DECLARE_DEFAULT_ALLOCATORS(ALdistortionState)
+
+DEFINE_ALEFFECTSTATE_VTABLE(ALdistortionState);
+
+
+static void ALdistortionState_Construct(ALdistortionState *state)
+{
+    ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
+    SET_VTABLE2(ALdistortionState, ALeffectState, state);
+
+    ALfilterState_clear(&state->lowpass);
+    ALfilterState_clear(&state->bandpass);
+}
+
+static ALvoid ALdistortionState_Destruct(ALdistortionState *state)
+{
+    ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
+}
+
+static ALboolean ALdistortionState_deviceUpdate(ALdistortionState *UNUSED(state), ALCdevice *UNUSED(device))
+{
+    return AL_TRUE;
+}
+
+static ALvoid ALdistortionState_update(ALdistortionState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props)
+{
+    ALfloat frequency = (ALfloat)Device->Frequency;
+    ALfloat bandwidth;
+    ALfloat cutoff;
+    ALfloat edge;
+
+    /* Store distorted signal attenuation settings */
+    state->attenuation = props->Distortion.Gain;
+
+    /* Store waveshaper edge settings */
+    edge = sinf(props->Distortion.Edge * (F_PI_2));
+    edge = minf(edge, 0.99f);
+    state->edge_coeff = 2.0f * edge / (1.0f-edge);
+
+    /* Lowpass filter */
+    cutoff = props->Distortion.LowpassCutoff;
+    /* Bandwidth value is constant in octaves */
+    bandwidth = (cutoff / 2.0f) / (cutoff * 0.67f);
+    ALfilterState_setParams(&state->lowpass, ALfilterType_LowPass, 1.0f,
+        cutoff / (frequency*4.0f), calc_rcpQ_from_bandwidth(cutoff / (frequency*4.0f), bandwidth)
+    );
+
+    /* Bandpass filter */
+    cutoff = props->Distortion.EQCenter;
+    /* Convert bandwidth in Hz to octaves */
+    bandwidth = props->Distortion.EQBandwidth / (cutoff * 0.67f);
+    ALfilterState_setParams(&state->bandpass, ALfilterType_BandPass, 1.0f,
+        cutoff / (frequency*4.0f), calc_rcpQ_from_bandwidth(cutoff / (frequency*4.0f), bandwidth)
+    );
+
+    ComputeAmbientGains(Device->Dry, Slot->Params.Gain, state->Gain);
+}
+
+static ALvoid ALdistortionState_process(ALdistortionState *state, ALuint SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels)
+{
+    const ALfloat fc = state->edge_coeff;
+    ALuint base;
+    ALuint it;
+    ALuint ot;
+    ALuint kt;
+
+    for(base = 0;base < SamplesToDo;)
+    {
+        float buffer[2][64 * 4];
+        ALuint td = minu(64, SamplesToDo-base);
+
+        /* Perform 4x oversampling to avoid aliasing.   */
+        /* Oversampling greatly improves distortion     */
+        /* quality and allows to implement lowpass and  */
+        /* bandpass filters using high frequencies, at  */
+        /* which classic IIR filters became unstable.   */
+
+        /* Fill oversample buffer using zero stuffing */
+        for(it = 0;it < td;it++)
+        {
+            buffer[0][it*4 + 0] = SamplesIn[0][it+base];
+            buffer[0][it*4 + 1] = 0.0f;
+            buffer[0][it*4 + 2] = 0.0f;
+            buffer[0][it*4 + 3] = 0.0f;
+        }
+
+        /* First step, do lowpass filtering of original signal,  */
+        /* additionally perform buffer interpolation and lowpass */
+        /* cutoff for oversampling (which is fortunately first   */
+        /* step of distortion). So combine three operations into */
+        /* the one.                                              */
+        ALfilterState_process(&state->lowpass, buffer[1], buffer[0], td*4);
+
+        /* Second step, do distortion using waveshaper function  */
+        /* to emulate signal processing during tube overdriving. */
+        /* Three steps of waveshaping are intended to modify     */
+        /* waveform without boost/clipping/attenuation process.  */
+        for(it = 0;it < td;it++)
+        {
+            for(ot = 0;ot < 4;ot++)
+            {
+                /* Restore signal power by multiplying sample by amount of oversampling */
+                ALfloat smp = buffer[1][it*4 + ot] * 4.0f;
+
+                smp = (1.0f + fc) * smp/(1.0f + fc*fabsf(smp));
+                smp = (1.0f + fc) * smp/(1.0f + fc*fabsf(smp)) * -1.0f;
+                smp = (1.0f + fc) * smp/(1.0f + fc*fabsf(smp));
+
+                buffer[1][it*4 + ot] = smp;
+            }
+        }
+
+        /* Third step, do bandpass filtering of distorted signal */
+        ALfilterState_process(&state->bandpass, buffer[0], buffer[1], td*4);
+
+        for(kt = 0;kt < NumChannels;kt++)
+        {
+            /* Fourth step, final, do attenuation and perform decimation,
+             * store only one sample out of 4.
+             */
+            ALfloat gain = state->Gain[kt] * state->attenuation;
+            if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
+                continue;
+
+            for(it = 0;it < td;it++)
+                SamplesOut[kt][base+it] += gain * buffer[0][it*4];
+        }
+
+        base += td;
+    }
+}
+
+
+typedef struct ALdistortionStateFactory {
+    DERIVE_FROM_TYPE(ALeffectStateFactory);
+} ALdistortionStateFactory;
+
+static ALeffectState *ALdistortionStateFactory_create(ALdistortionStateFactory *UNUSED(factory))
+{
+    ALdistortionState *state;
+
+    NEW_OBJ0(state, ALdistortionState)();
+    if(!state) return NULL;
+
+    return STATIC_CAST(ALeffectState, state);
+}
+
+DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALdistortionStateFactory);
+
+
+ALeffectStateFactory *ALdistortionStateFactory_getFactory(void)
+{
+    static ALdistortionStateFactory DistortionFactory = { { GET_VTABLE2(ALdistortionStateFactory, ALeffectStateFactory) } };
+
+    return STATIC_CAST(ALeffectStateFactory, &DistortionFactory);
+}
+
+
+void ALdistortion_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val))
+{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
+void ALdistortion_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
+{
+    ALdistortion_setParami(effect, context, param, vals[0]);
+}
+void ALdistortion_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
+{
+    ALeffectProps *props = &effect->Props;
+    switch(param)
+    {
+        case AL_DISTORTION_EDGE:
+            if(!(val >= AL_DISTORTION_MIN_EDGE && val <= AL_DISTORTION_MAX_EDGE))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Distortion.Edge = val;
+            break;
+
+        case AL_DISTORTION_GAIN:
+            if(!(val >= AL_DISTORTION_MIN_GAIN && val <= AL_DISTORTION_MAX_GAIN))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Distortion.Gain = val;
+            break;
+
+        case AL_DISTORTION_LOWPASS_CUTOFF:
+            if(!(val >= AL_DISTORTION_MIN_LOWPASS_CUTOFF && val <= AL_DISTORTION_MAX_LOWPASS_CUTOFF))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Distortion.LowpassCutoff = val;
+            break;
+
+        case AL_DISTORTION_EQCENTER:
+            if(!(val >= AL_DISTORTION_MIN_EQCENTER && val <= AL_DISTORTION_MAX_EQCENTER))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Distortion.EQCenter = val;
+            break;
+
+        case AL_DISTORTION_EQBANDWIDTH:
+            if(!(val >= AL_DISTORTION_MIN_EQBANDWIDTH && val <= AL_DISTORTION_MAX_EQBANDWIDTH))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Distortion.EQBandwidth = val;
+            break;
+
+        default:
+            SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+    }
+}
+void ALdistortion_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
+{
+    ALdistortion_setParamf(effect, context, param, vals[0]);
+}
+
+void ALdistortion_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val))
+{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
+void ALdistortion_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
+{
+    ALdistortion_getParami(effect, context, param, vals);
+}
+void ALdistortion_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
+{
+    const ALeffectProps *props = &effect->Props;
+    switch(param)
+    {
+        case AL_DISTORTION_EDGE:
+            *val = props->Distortion.Edge;
+            break;
+
+        case AL_DISTORTION_GAIN:
+            *val = props->Distortion.Gain;
+            break;
+
+        case AL_DISTORTION_LOWPASS_CUTOFF:
+            *val = props->Distortion.LowpassCutoff;
+            break;
+
+        case AL_DISTORTION_EQCENTER:
+            *val = props->Distortion.EQCenter;
+            break;
+
+        case AL_DISTORTION_EQBANDWIDTH:
+            *val = props->Distortion.EQBandwidth;
+            break;
+
+        default:
+            SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+    }
+}
+void ALdistortion_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
+{
+    ALdistortion_getParamf(effect, context, param, vals);
+}
+
+DEFINE_ALEFFECT_VTABLE(ALdistortion);

+ 326 - 0
Engine/lib/openal-soft/Alc/effects/echo.c

@@ -0,0 +1,326 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 2009 by Chris Robinson.
+ * This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the
+ *  Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#include "config.h"
+
+#include <math.h>
+#include <stdlib.h>
+
+#include "alMain.h"
+#include "alFilter.h"
+#include "alAuxEffectSlot.h"
+#include "alError.h"
+#include "alu.h"
+
+
+typedef struct ALechoState {
+    DERIVE_FROM_TYPE(ALeffectState);
+
+    ALfloat *SampleBuffer;
+    ALuint BufferLength;
+
+    // The echo is two tap. The delay is the number of samples from before the
+    // current offset
+    struct {
+        ALuint delay;
+    } Tap[2];
+    ALuint Offset;
+    /* The panning gains for the two taps */
+    ALfloat Gain[2][MAX_OUTPUT_CHANNELS];
+
+    ALfloat FeedGain;
+
+    ALfilterState Filter;
+} ALechoState;
+
+static ALvoid ALechoState_Destruct(ALechoState *state);
+static ALboolean ALechoState_deviceUpdate(ALechoState *state, ALCdevice *Device);
+static ALvoid ALechoState_update(ALechoState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props);
+static ALvoid ALechoState_process(ALechoState *state, ALuint SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels);
+DECLARE_DEFAULT_ALLOCATORS(ALechoState)
+
+DEFINE_ALEFFECTSTATE_VTABLE(ALechoState);
+
+
+static void ALechoState_Construct(ALechoState *state)
+{
+    ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
+    SET_VTABLE2(ALechoState, ALeffectState, state);
+
+    state->BufferLength = 0;
+    state->SampleBuffer = NULL;
+
+    state->Tap[0].delay = 0;
+    state->Tap[1].delay = 0;
+    state->Offset = 0;
+
+    ALfilterState_clear(&state->Filter);
+}
+
+static ALvoid ALechoState_Destruct(ALechoState *state)
+{
+    al_free(state->SampleBuffer);
+    state->SampleBuffer = NULL;
+    ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
+}
+
+static ALboolean ALechoState_deviceUpdate(ALechoState *state, ALCdevice *Device)
+{
+    ALuint maxlen, i;
+
+    // Use the next power of 2 for the buffer length, so the tap offsets can be
+    // wrapped using a mask instead of a modulo
+    maxlen  = fastf2u(AL_ECHO_MAX_DELAY * Device->Frequency) + 1;
+    maxlen += fastf2u(AL_ECHO_MAX_LRDELAY * Device->Frequency) + 1;
+    maxlen  = NextPowerOf2(maxlen);
+
+    if(maxlen != state->BufferLength)
+    {
+        void *temp = al_calloc(16, maxlen * sizeof(ALfloat));
+        if(!temp) return AL_FALSE;
+
+        al_free(state->SampleBuffer);
+        state->SampleBuffer = temp;
+        state->BufferLength = maxlen;
+    }
+    for(i = 0;i < state->BufferLength;i++)
+        state->SampleBuffer[i] = 0.0f;
+
+    return AL_TRUE;
+}
+
+static ALvoid ALechoState_update(ALechoState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props)
+{
+    ALuint frequency = Device->Frequency;
+    ALfloat coeffs[MAX_AMBI_COEFFS];
+    ALfloat gain, lrpan, spread;
+
+    state->Tap[0].delay = fastf2u(props->Echo.Delay * frequency) + 1;
+    state->Tap[1].delay = fastf2u(props->Echo.LRDelay * frequency);
+    state->Tap[1].delay += state->Tap[0].delay;
+
+    spread = props->Echo.Spread;
+    if(spread < 0.0f) lrpan = -1.0f;
+    else lrpan = 1.0f;
+    /* Convert echo spread (where 0 = omni, +/-1 = directional) to coverage
+     * spread (where 0 = point, tau = omni).
+     */
+    spread = asinf(1.0f - fabsf(spread))*4.0f;
+
+    state->FeedGain = props->Echo.Feedback;
+
+    gain = minf(1.0f - props->Echo.Damping, 0.01f);
+    ALfilterState_setParams(&state->Filter, ALfilterType_HighShelf,
+                            gain, LOWPASSFREQREF/frequency,
+                            calc_rcpQ_from_slope(gain, 0.75f));
+
+    gain = Slot->Params.Gain;
+
+    /* First tap panning */
+    CalcXYZCoeffs(-lrpan, 0.0f, 0.0f, spread, coeffs);
+    ComputePanningGains(Device->Dry, coeffs, gain, state->Gain[0]);
+
+    /* Second tap panning */
+    CalcXYZCoeffs( lrpan, 0.0f, 0.0f, spread, coeffs);
+    ComputePanningGains(Device->Dry, coeffs, gain, state->Gain[1]);
+}
+
+static ALvoid ALechoState_process(ALechoState *state, ALuint SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels)
+{
+    const ALuint mask = state->BufferLength-1;
+    const ALuint tap1 = state->Tap[0].delay;
+    const ALuint tap2 = state->Tap[1].delay;
+    ALuint offset = state->Offset;
+    ALfloat x[2], y[2], in, out;
+    ALuint base;
+    ALuint i, k;
+
+    x[0] = state->Filter.x[0];
+    x[1] = state->Filter.x[1];
+    y[0] = state->Filter.y[0];
+    y[1] = state->Filter.y[1];
+    for(base = 0;base < SamplesToDo;)
+    {
+        ALfloat temps[128][2];
+        ALuint td = minu(128, SamplesToDo-base);
+
+        for(i = 0;i < td;i++)
+        {
+            /* First tap */
+            temps[i][0] = state->SampleBuffer[(offset-tap1) & mask];
+            /* Second tap */
+            temps[i][1] = state->SampleBuffer[(offset-tap2) & mask];
+
+            // Apply damping and feedback gain to the second tap, and mix in the
+            // new sample
+            in = temps[i][1] + SamplesIn[0][i+base];
+            out = in*state->Filter.b0 +
+                  x[0]*state->Filter.b1 + x[1]*state->Filter.b2 -
+                  y[0]*state->Filter.a1 - y[1]*state->Filter.a2;
+            x[1] = x[0]; x[0] = in;
+            y[1] = y[0]; y[0] = out;
+
+            state->SampleBuffer[offset&mask] = out * state->FeedGain;
+            offset++;
+        }
+
+        for(k = 0;k < NumChannels;k++)
+        {
+            ALfloat gain = state->Gain[0][k];
+            if(fabsf(gain) > GAIN_SILENCE_THRESHOLD)
+            {
+                for(i = 0;i < td;i++)
+                    SamplesOut[k][i+base] += temps[i][0] * gain;
+            }
+
+            gain = state->Gain[1][k];
+            if(fabsf(gain) > GAIN_SILENCE_THRESHOLD)
+            {
+                for(i = 0;i < td;i++)
+                    SamplesOut[k][i+base] += temps[i][1] * gain;
+            }
+        }
+
+        base += td;
+    }
+    state->Filter.x[0] = x[0];
+    state->Filter.x[1] = x[1];
+    state->Filter.y[0] = y[0];
+    state->Filter.y[1] = y[1];
+
+    state->Offset = offset;
+}
+
+
+typedef struct ALechoStateFactory {
+    DERIVE_FROM_TYPE(ALeffectStateFactory);
+} ALechoStateFactory;
+
+ALeffectState *ALechoStateFactory_create(ALechoStateFactory *UNUSED(factory))
+{
+    ALechoState *state;
+
+    NEW_OBJ0(state, ALechoState)();
+    if(!state) return NULL;
+
+    return STATIC_CAST(ALeffectState, state);
+}
+
+DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALechoStateFactory);
+
+ALeffectStateFactory *ALechoStateFactory_getFactory(void)
+{
+    static ALechoStateFactory EchoFactory = { { GET_VTABLE2(ALechoStateFactory, ALeffectStateFactory) } };
+
+    return STATIC_CAST(ALeffectStateFactory, &EchoFactory);
+}
+
+
+void ALecho_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val))
+{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
+void ALecho_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
+{
+    ALecho_setParami(effect, context, param, vals[0]);
+}
+void ALecho_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
+{
+    ALeffectProps *props = &effect->Props;
+    switch(param)
+    {
+        case AL_ECHO_DELAY:
+            if(!(val >= AL_ECHO_MIN_DELAY && val <= AL_ECHO_MAX_DELAY))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Echo.Delay = val;
+            break;
+
+        case AL_ECHO_LRDELAY:
+            if(!(val >= AL_ECHO_MIN_LRDELAY && val <= AL_ECHO_MAX_LRDELAY))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Echo.LRDelay = val;
+            break;
+
+        case AL_ECHO_DAMPING:
+            if(!(val >= AL_ECHO_MIN_DAMPING && val <= AL_ECHO_MAX_DAMPING))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Echo.Damping = val;
+            break;
+
+        case AL_ECHO_FEEDBACK:
+            if(!(val >= AL_ECHO_MIN_FEEDBACK && val <= AL_ECHO_MAX_FEEDBACK))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Echo.Feedback = val;
+            break;
+
+        case AL_ECHO_SPREAD:
+            if(!(val >= AL_ECHO_MIN_SPREAD && val <= AL_ECHO_MAX_SPREAD))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Echo.Spread = val;
+            break;
+
+        default:
+            SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+    }
+}
+void ALecho_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
+{
+    ALecho_setParamf(effect, context, param, vals[0]);
+}
+
+void ALecho_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val))
+{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
+void ALecho_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
+{
+    ALecho_getParami(effect, context, param, vals);
+}
+void ALecho_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
+{
+    const ALeffectProps *props = &effect->Props;
+    switch(param)
+    {
+        case AL_ECHO_DELAY:
+            *val = props->Echo.Delay;
+            break;
+
+        case AL_ECHO_LRDELAY:
+            *val = props->Echo.LRDelay;
+            break;
+
+        case AL_ECHO_DAMPING:
+            *val = props->Echo.Damping;
+            break;
+
+        case AL_ECHO_FEEDBACK:
+            *val = props->Echo.Feedback;
+            break;
+
+        case AL_ECHO_SPREAD:
+            *val = props->Echo.Spread;
+            break;
+
+        default:
+            SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+    }
+}
+void ALecho_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
+{
+    ALecho_getParamf(effect, context, param, vals);
+}
+
+DEFINE_ALEFFECT_VTABLE(ALecho);

+ 404 - 0
Engine/lib/openal-soft/Alc/effects/equalizer.c

@@ -0,0 +1,404 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 2013 by Mike Gorchak
+ * This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the
+ *  Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#include "config.h"
+
+#include <math.h>
+#include <stdlib.h>
+
+#include "alMain.h"
+#include "alFilter.h"
+#include "alAuxEffectSlot.h"
+#include "alError.h"
+#include "alu.h"
+
+
+/*  The document  "Effects Extension Guide.pdf"  says that low and high  *
+ *  frequencies are cutoff frequencies. This is not fully correct, they  *
+ *  are corner frequencies for low and high shelf filters. If they were  *
+ *  just cutoff frequencies, there would be no need in cutoff frequency  *
+ *  gains, which are present.  Documentation for  "Creative Proteus X2"  *
+ *  software describes  4-band equalizer functionality in a much better  *
+ *  way.  This equalizer seems  to be a predecessor  of  OpenAL  4-band  *
+ *  equalizer.  With low and high  shelf filters  we are able to cutoff  *
+ *  frequencies below and/or above corner frequencies using attenuation  *
+ *  gains (below 1.0) and amplify all low and/or high frequencies using  *
+ *  gains above 1.0.                                                     *
+ *                                                                       *
+ *     Low-shelf       Low Mid Band      High Mid Band     High-shelf    *
+ *      corner            center             center          corner      *
+ *     frequency        frequency          frequency       frequency     *
+ *    50Hz..800Hz     200Hz..3000Hz      1000Hz..8000Hz  4000Hz..16000Hz *
+ *                                                                       *
+ *          |               |                  |               |         *
+ *          |               |                  |               |         *
+ *   B -----+            /--+--\            /--+--\            +-----    *
+ *   O      |\          |   |   |          |   |   |          /|         *
+ *   O      | \        -    |    -        -    |    -        / |         *
+ *   S +    |  \      |     |     |      |     |     |      /  |         *
+ *   T      |   |    |      |      |    |      |      |    |   |         *
+ * ---------+---------------+------------------+---------------+-------- *
+ *   C      |   |    |      |      |    |      |      |    |   |         *
+ *   U -    |  /      |     |     |      |     |     |      \  |         *
+ *   T      | /        -    |    -        -    |    -        \ |         *
+ *   O      |/          |   |   |          |   |   |          \|         *
+ *   F -----+            \--+--/            \--+--/            +-----    *
+ *   F      |               |                  |               |         *
+ *          |               |                  |               |         *
+ *                                                                       *
+ * Gains vary from 0.126 up to 7.943, which means from -18dB attenuation *
+ * up to +18dB amplification. Band width varies from 0.01 up to 1.0 in   *
+ * octaves for two mid bands.                                            *
+ *                                                                       *
+ * Implementation is based on the "Cookbook formulae for audio EQ biquad *
+ * filter coefficients" by Robert Bristow-Johnson                        *
+ * http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt                   */
+
+
+/* The maximum number of sample frames per update. */
+#define MAX_UPDATE_SAMPLES 256
+
+typedef struct ALequalizerState {
+    DERIVE_FROM_TYPE(ALeffectState);
+
+    /* Effect gains for each channel */
+    ALfloat Gain[MAX_EFFECT_CHANNELS][MAX_OUTPUT_CHANNELS];
+
+    /* Effect parameters */
+    ALfilterState filter[4][MAX_EFFECT_CHANNELS];
+
+    ALfloat SampleBuffer[4][MAX_EFFECT_CHANNELS][MAX_UPDATE_SAMPLES];
+} ALequalizerState;
+
+static ALvoid ALequalizerState_Destruct(ALequalizerState *state);
+static ALboolean ALequalizerState_deviceUpdate(ALequalizerState *state, ALCdevice *device);
+static ALvoid ALequalizerState_update(ALequalizerState *state, const ALCdevice *device, const ALeffectslot *slot, const ALeffectProps *props);
+static ALvoid ALequalizerState_process(ALequalizerState *state, ALuint SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels);
+DECLARE_DEFAULT_ALLOCATORS(ALequalizerState)
+
+DEFINE_ALEFFECTSTATE_VTABLE(ALequalizerState);
+
+
+static void ALequalizerState_Construct(ALequalizerState *state)
+{
+    int it, ft;
+
+    ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
+    SET_VTABLE2(ALequalizerState, ALeffectState, state);
+
+    /* Initialize sample history only on filter creation to avoid */
+    /* sound clicks if filter settings were changed in runtime.   */
+    for(it = 0; it < 4; it++)
+    {
+        for(ft = 0;ft < MAX_EFFECT_CHANNELS;ft++)
+            ALfilterState_clear(&state->filter[it][ft]);
+    }
+}
+
+static ALvoid ALequalizerState_Destruct(ALequalizerState *state)
+{
+    ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
+}
+
+static ALboolean ALequalizerState_deviceUpdate(ALequalizerState *UNUSED(state), ALCdevice *UNUSED(device))
+{
+    return AL_TRUE;
+}
+
+static ALvoid ALequalizerState_update(ALequalizerState *state, const ALCdevice *device, const ALeffectslot *slot, const ALeffectProps *props)
+{
+    ALfloat frequency = (ALfloat)device->Frequency;
+    ALfloat gain, freq_mult;
+    ALuint i;
+
+    STATIC_CAST(ALeffectState,state)->OutBuffer = device->FOAOut.Buffer;
+    STATIC_CAST(ALeffectState,state)->OutChannels = device->FOAOut.NumChannels;
+    for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
+        ComputeFirstOrderGains(device->FOAOut, IdentityMatrixf.m[i],
+                               slot->Params.Gain, state->Gain[i]);
+
+    /* Calculate coefficients for the each type of filter. Note that the shelf
+     * filters' gain is for the reference frequency, which is the centerpoint
+     * of the transition band.
+     */
+    gain = sqrtf(props->Equalizer.LowGain);
+    freq_mult = props->Equalizer.LowCutoff/frequency;
+    ALfilterState_setParams(&state->filter[0][0], ALfilterType_LowShelf,
+        gain, freq_mult, calc_rcpQ_from_slope(gain, 0.75f)
+    );
+    /* Copy the filter coefficients for the other input channels. */
+    for(i = 1;i < MAX_EFFECT_CHANNELS;i++)
+    {
+        state->filter[0][i].a1 = state->filter[0][0].a1;
+        state->filter[0][i].a2 = state->filter[0][0].a2;
+        state->filter[0][i].b0 = state->filter[0][0].b0;
+        state->filter[0][i].b1 = state->filter[0][0].b1;
+        state->filter[0][i].b2 = state->filter[0][0].b2;
+    }
+
+    gain = props->Equalizer.Mid1Gain;
+    freq_mult = props->Equalizer.Mid1Center/frequency;
+    ALfilterState_setParams(&state->filter[1][0], ALfilterType_Peaking,
+        gain, freq_mult, calc_rcpQ_from_bandwidth(
+            freq_mult, props->Equalizer.Mid1Width
+        )
+    );
+    for(i = 1;i < MAX_EFFECT_CHANNELS;i++)
+    {
+        state->filter[1][i].a1 = state->filter[1][0].a1;
+        state->filter[1][i].a2 = state->filter[1][0].a2;
+        state->filter[1][i].b0 = state->filter[1][0].b0;
+        state->filter[1][i].b1 = state->filter[1][0].b1;
+        state->filter[1][i].b2 = state->filter[1][0].b2;
+    }
+
+    gain = props->Equalizer.Mid2Gain;
+    freq_mult = props->Equalizer.Mid2Center/frequency;
+    ALfilterState_setParams(&state->filter[2][0], ALfilterType_Peaking,
+        gain, freq_mult, calc_rcpQ_from_bandwidth(
+            freq_mult, props->Equalizer.Mid2Width
+        )
+    );
+    for(i = 1;i < MAX_EFFECT_CHANNELS;i++)
+    {
+        state->filter[2][i].a1 = state->filter[2][0].a1;
+        state->filter[2][i].a2 = state->filter[2][0].a2;
+        state->filter[2][i].b0 = state->filter[2][0].b0;
+        state->filter[2][i].b1 = state->filter[2][0].b1;
+        state->filter[2][i].b2 = state->filter[2][0].b2;
+    }
+
+    gain = sqrtf(props->Equalizer.HighGain);
+    freq_mult = props->Equalizer.HighCutoff/frequency;
+    ALfilterState_setParams(&state->filter[3][0], ALfilterType_HighShelf,
+        gain, freq_mult, calc_rcpQ_from_slope(gain, 0.75f)
+    );
+    for(i = 1;i < MAX_EFFECT_CHANNELS;i++)
+    {
+        state->filter[3][i].a1 = state->filter[3][0].a1;
+        state->filter[3][i].a2 = state->filter[3][0].a2;
+        state->filter[3][i].b0 = state->filter[3][0].b0;
+        state->filter[3][i].b1 = state->filter[3][0].b1;
+        state->filter[3][i].b2 = state->filter[3][0].b2;
+    }
+}
+
+static ALvoid ALequalizerState_process(ALequalizerState *state, ALuint SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels)
+{
+    ALfloat (*Samples)[MAX_EFFECT_CHANNELS][MAX_UPDATE_SAMPLES] = state->SampleBuffer;
+    ALuint it, kt, ft;
+    ALuint base;
+
+    for(base = 0;base < SamplesToDo;)
+    {
+        ALuint td = minu(MAX_UPDATE_SAMPLES, SamplesToDo-base);
+
+        for(ft = 0;ft < MAX_EFFECT_CHANNELS;ft++)
+            ALfilterState_process(&state->filter[0][ft], Samples[0][ft], &SamplesIn[ft][base], td);
+        for(ft = 0;ft < MAX_EFFECT_CHANNELS;ft++)
+            ALfilterState_process(&state->filter[1][ft], Samples[1][ft], Samples[0][ft], td);
+        for(ft = 0;ft < MAX_EFFECT_CHANNELS;ft++)
+            ALfilterState_process(&state->filter[2][ft], Samples[2][ft], Samples[1][ft], td);
+        for(ft = 0;ft < MAX_EFFECT_CHANNELS;ft++)
+            ALfilterState_process(&state->filter[3][ft], Samples[3][ft], Samples[2][ft], td);
+
+        for(ft = 0;ft < MAX_EFFECT_CHANNELS;ft++)
+        {
+            for(kt = 0;kt < NumChannels;kt++)
+            {
+                ALfloat gain = state->Gain[ft][kt];
+                if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
+                    continue;
+
+                for(it = 0;it < td;it++)
+                    SamplesOut[kt][base+it] += gain * Samples[3][ft][it];
+            }
+        }
+
+        base += td;
+    }
+}
+
+
+typedef struct ALequalizerStateFactory {
+    DERIVE_FROM_TYPE(ALeffectStateFactory);
+} ALequalizerStateFactory;
+
+ALeffectState *ALequalizerStateFactory_create(ALequalizerStateFactory *UNUSED(factory))
+{
+    ALequalizerState *state;
+
+    NEW_OBJ0(state, ALequalizerState)();
+    if(!state) return NULL;
+
+    return STATIC_CAST(ALeffectState, state);
+}
+
+DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALequalizerStateFactory);
+
+ALeffectStateFactory *ALequalizerStateFactory_getFactory(void)
+{
+    static ALequalizerStateFactory EqualizerFactory = { { GET_VTABLE2(ALequalizerStateFactory, ALeffectStateFactory) } };
+
+    return STATIC_CAST(ALeffectStateFactory, &EqualizerFactory);
+}
+
+
+void ALequalizer_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val))
+{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
+void ALequalizer_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
+{
+    ALequalizer_setParami(effect, context, param, vals[0]);
+}
+void ALequalizer_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
+{
+    ALeffectProps *props = &effect->Props;
+    switch(param)
+    {
+        case AL_EQUALIZER_LOW_GAIN:
+            if(!(val >= AL_EQUALIZER_MIN_LOW_GAIN && val <= AL_EQUALIZER_MAX_LOW_GAIN))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Equalizer.LowGain = val;
+            break;
+
+        case AL_EQUALIZER_LOW_CUTOFF:
+            if(!(val >= AL_EQUALIZER_MIN_LOW_CUTOFF && val <= AL_EQUALIZER_MAX_LOW_CUTOFF))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Equalizer.LowCutoff = val;
+            break;
+
+        case AL_EQUALIZER_MID1_GAIN:
+            if(!(val >= AL_EQUALIZER_MIN_MID1_GAIN && val <= AL_EQUALIZER_MAX_MID1_GAIN))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Equalizer.Mid1Gain = val;
+            break;
+
+        case AL_EQUALIZER_MID1_CENTER:
+            if(!(val >= AL_EQUALIZER_MIN_MID1_CENTER && val <= AL_EQUALIZER_MAX_MID1_CENTER))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Equalizer.Mid1Center = val;
+            break;
+
+        case AL_EQUALIZER_MID1_WIDTH:
+            if(!(val >= AL_EQUALIZER_MIN_MID1_WIDTH && val <= AL_EQUALIZER_MAX_MID1_WIDTH))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Equalizer.Mid1Width = val;
+            break;
+
+        case AL_EQUALIZER_MID2_GAIN:
+            if(!(val >= AL_EQUALIZER_MIN_MID2_GAIN && val <= AL_EQUALIZER_MAX_MID2_GAIN))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Equalizer.Mid2Gain = val;
+            break;
+
+        case AL_EQUALIZER_MID2_CENTER:
+            if(!(val >= AL_EQUALIZER_MIN_MID2_CENTER && val <= AL_EQUALIZER_MAX_MID2_CENTER))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Equalizer.Mid2Center = val;
+            break;
+
+        case AL_EQUALIZER_MID2_WIDTH:
+            if(!(val >= AL_EQUALIZER_MIN_MID2_WIDTH && val <= AL_EQUALIZER_MAX_MID2_WIDTH))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Equalizer.Mid2Width = val;
+            break;
+
+        case AL_EQUALIZER_HIGH_GAIN:
+            if(!(val >= AL_EQUALIZER_MIN_HIGH_GAIN && val <= AL_EQUALIZER_MAX_HIGH_GAIN))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Equalizer.HighGain = val;
+            break;
+
+        case AL_EQUALIZER_HIGH_CUTOFF:
+            if(!(val >= AL_EQUALIZER_MIN_HIGH_CUTOFF && val <= AL_EQUALIZER_MAX_HIGH_CUTOFF))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Equalizer.HighCutoff = val;
+            break;
+
+        default:
+            SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+    }
+}
+void ALequalizer_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
+{
+    ALequalizer_setParamf(effect, context, param, vals[0]);
+}
+
+void ALequalizer_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val))
+{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
+void ALequalizer_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
+{
+    ALequalizer_getParami(effect, context, param, vals);
+}
+void ALequalizer_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
+{
+    const ALeffectProps *props = &effect->Props;
+    switch(param)
+    {
+        case AL_EQUALIZER_LOW_GAIN:
+            *val = props->Equalizer.LowGain;
+            break;
+
+        case AL_EQUALIZER_LOW_CUTOFF:
+            *val = props->Equalizer.LowCutoff;
+            break;
+
+        case AL_EQUALIZER_MID1_GAIN:
+            *val = props->Equalizer.Mid1Gain;
+            break;
+
+        case AL_EQUALIZER_MID1_CENTER:
+            *val = props->Equalizer.Mid1Center;
+            break;
+
+        case AL_EQUALIZER_MID1_WIDTH:
+            *val = props->Equalizer.Mid1Width;
+            break;
+
+        case AL_EQUALIZER_MID2_GAIN:
+            *val = props->Equalizer.Mid2Gain;
+            break;
+
+        case AL_EQUALIZER_MID2_CENTER:
+            *val = props->Equalizer.Mid2Center;
+            break;
+
+        case AL_EQUALIZER_MID2_WIDTH:
+            *val = props->Equalizer.Mid2Width;
+            break;
+
+        case AL_EQUALIZER_HIGH_GAIN:
+            *val = props->Equalizer.HighGain;
+            break;
+
+        case AL_EQUALIZER_HIGH_CUTOFF:
+            *val = props->Equalizer.HighCutoff;
+            break;
+
+        default:
+            SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+    }
+}
+void ALequalizer_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
+{
+    ALequalizer_getParamf(effect, context, param, vals);
+}
+
+DEFINE_ALEFFECT_VTABLE(ALequalizer);

+ 411 - 0
Engine/lib/openal-soft/Alc/effects/flanger.c

@@ -0,0 +1,411 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 2013 by Mike Gorchak
+ * This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the
+ *  Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#include "config.h"
+
+#include <math.h>
+#include <stdlib.h>
+
+#include "alMain.h"
+#include "alFilter.h"
+#include "alAuxEffectSlot.h"
+#include "alError.h"
+#include "alu.h"
+
+
+enum FlangerWaveForm {
+    FWF_Triangle = AL_FLANGER_WAVEFORM_TRIANGLE,
+    FWF_Sinusoid = AL_FLANGER_WAVEFORM_SINUSOID
+};
+
+typedef struct ALflangerState {
+    DERIVE_FROM_TYPE(ALeffectState);
+
+    ALfloat *SampleBuffer[2];
+    ALuint BufferLength;
+    ALuint offset;
+    ALuint lfo_range;
+    ALfloat lfo_scale;
+    ALint lfo_disp;
+
+    /* Gains for left and right sides */
+    ALfloat Gain[2][MAX_OUTPUT_CHANNELS];
+
+    /* effect parameters */
+    enum FlangerWaveForm waveform;
+    ALint delay;
+    ALfloat depth;
+    ALfloat feedback;
+} ALflangerState;
+
+static ALvoid ALflangerState_Destruct(ALflangerState *state);
+static ALboolean ALflangerState_deviceUpdate(ALflangerState *state, ALCdevice *Device);
+static ALvoid ALflangerState_update(ALflangerState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props);
+static ALvoid ALflangerState_process(ALflangerState *state, ALuint SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels);
+DECLARE_DEFAULT_ALLOCATORS(ALflangerState)
+
+DEFINE_ALEFFECTSTATE_VTABLE(ALflangerState);
+
+
+static void ALflangerState_Construct(ALflangerState *state)
+{
+    ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
+    SET_VTABLE2(ALflangerState, ALeffectState, state);
+
+    state->BufferLength = 0;
+    state->SampleBuffer[0] = NULL;
+    state->SampleBuffer[1] = NULL;
+    state->offset = 0;
+    state->lfo_range = 1;
+    state->waveform = FWF_Triangle;
+}
+
+static ALvoid ALflangerState_Destruct(ALflangerState *state)
+{
+    al_free(state->SampleBuffer[0]);
+    state->SampleBuffer[0] = NULL;
+    state->SampleBuffer[1] = NULL;
+
+    ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
+}
+
+static ALboolean ALflangerState_deviceUpdate(ALflangerState *state, ALCdevice *Device)
+{
+    ALuint maxlen;
+    ALuint it;
+
+    maxlen = fastf2u(AL_FLANGER_MAX_DELAY * 3.0f * Device->Frequency) + 1;
+    maxlen = NextPowerOf2(maxlen);
+
+    if(maxlen != state->BufferLength)
+    {
+        void *temp = al_calloc(16, maxlen * sizeof(ALfloat) * 2);
+        if(!temp) return AL_FALSE;
+
+        al_free(state->SampleBuffer[0]);
+        state->SampleBuffer[0] = temp;
+        state->SampleBuffer[1] = state->SampleBuffer[0] + maxlen;
+
+        state->BufferLength = maxlen;
+    }
+
+    for(it = 0;it < state->BufferLength;it++)
+    {
+        state->SampleBuffer[0][it] = 0.0f;
+        state->SampleBuffer[1][it] = 0.0f;
+    }
+
+    return AL_TRUE;
+}
+
+static ALvoid ALflangerState_update(ALflangerState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props)
+{
+    ALfloat frequency = (ALfloat)Device->Frequency;
+    ALfloat coeffs[MAX_AMBI_COEFFS];
+    ALfloat rate;
+    ALint phase;
+
+    switch(props->Flanger.Waveform)
+    {
+        case AL_FLANGER_WAVEFORM_TRIANGLE:
+            state->waveform = FWF_Triangle;
+            break;
+        case AL_FLANGER_WAVEFORM_SINUSOID:
+            state->waveform = FWF_Sinusoid;
+            break;
+    }
+    state->depth = props->Flanger.Depth;
+    state->feedback = props->Flanger.Feedback;
+    state->delay = fastf2i(props->Flanger.Delay * frequency);
+
+    /* Gains for left and right sides */
+    CalcXYZCoeffs(-1.0f, 0.0f, 0.0f, 0.0f, coeffs);
+    ComputePanningGains(Device->Dry, coeffs, Slot->Params.Gain, state->Gain[0]);
+    CalcXYZCoeffs( 1.0f, 0.0f, 0.0f, 0.0f, coeffs);
+    ComputePanningGains(Device->Dry, coeffs, Slot->Params.Gain, state->Gain[1]);
+
+    phase = props->Flanger.Phase;
+    rate = props->Flanger.Rate;
+    if(!(rate > 0.0f))
+    {
+        state->lfo_scale = 0.0f;
+        state->lfo_range = 1;
+        state->lfo_disp = 0;
+    }
+    else
+    {
+        /* Calculate LFO coefficient */
+        state->lfo_range = fastf2u(frequency/rate + 0.5f);
+        switch(state->waveform)
+        {
+            case FWF_Triangle:
+                state->lfo_scale = 4.0f / state->lfo_range;
+                break;
+            case FWF_Sinusoid:
+                state->lfo_scale = F_TAU / state->lfo_range;
+                break;
+        }
+
+        /* Calculate lfo phase displacement */
+        state->lfo_disp = fastf2i(state->lfo_range * (phase/360.0f));
+    }
+}
+
+static inline void Triangle(ALint *delay_left, ALint *delay_right, ALuint offset, const ALflangerState *state)
+{
+    ALfloat lfo_value;
+
+    lfo_value = 2.0f - fabsf(2.0f - state->lfo_scale*(offset%state->lfo_range));
+    lfo_value *= state->depth * state->delay;
+    *delay_left = fastf2i(lfo_value) + state->delay;
+
+    offset += state->lfo_disp;
+    lfo_value = 2.0f - fabsf(2.0f - state->lfo_scale*(offset%state->lfo_range));
+    lfo_value *= state->depth * state->delay;
+    *delay_right = fastf2i(lfo_value) + state->delay;
+}
+
+static inline void Sinusoid(ALint *delay_left, ALint *delay_right, ALuint offset, const ALflangerState *state)
+{
+    ALfloat lfo_value;
+
+    lfo_value = 1.0f + sinf(state->lfo_scale*(offset%state->lfo_range));
+    lfo_value *= state->depth * state->delay;
+    *delay_left = fastf2i(lfo_value) + state->delay;
+
+    offset += state->lfo_disp;
+    lfo_value = 1.0f + sinf(state->lfo_scale*(offset%state->lfo_range));
+    lfo_value *= state->depth * state->delay;
+    *delay_right = fastf2i(lfo_value) + state->delay;
+}
+
+#define DECL_TEMPLATE(Func)                                                   \
+static void Process##Func(ALflangerState *state, const ALuint SamplesToDo,    \
+  const ALfloat *restrict SamplesIn, ALfloat (*restrict out)[2])              \
+{                                                                             \
+    const ALuint bufmask = state->BufferLength-1;                             \
+    ALfloat *restrict leftbuf = state->SampleBuffer[0];                       \
+    ALfloat *restrict rightbuf = state->SampleBuffer[1];                      \
+    ALuint offset = state->offset;                                            \
+    const ALfloat feedback = state->feedback;                                 \
+    ALuint it;                                                                \
+                                                                              \
+    for(it = 0;it < SamplesToDo;it++)                                         \
+    {                                                                         \
+        ALint delay_left, delay_right;                                        \
+        Func(&delay_left, &delay_right, offset, state);                       \
+                                                                              \
+        out[it][0] = leftbuf[(offset-delay_left)&bufmask];                    \
+        leftbuf[offset&bufmask] = (out[it][0]+SamplesIn[it]) * feedback;      \
+                                                                              \
+        out[it][1] = rightbuf[(offset-delay_right)&bufmask];                  \
+        rightbuf[offset&bufmask] = (out[it][1]+SamplesIn[it]) * feedback;     \
+                                                                              \
+        offset++;                                                             \
+    }                                                                         \
+    state->offset = offset;                                                   \
+}
+
+DECL_TEMPLATE(Triangle)
+DECL_TEMPLATE(Sinusoid)
+
+#undef DECL_TEMPLATE
+
+static ALvoid ALflangerState_process(ALflangerState *state, ALuint SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels)
+{
+    ALuint it, kt;
+    ALuint base;
+
+    for(base = 0;base < SamplesToDo;)
+    {
+        ALfloat temps[128][2];
+        ALuint td = minu(128, SamplesToDo-base);
+
+        switch(state->waveform)
+        {
+            case FWF_Triangle:
+                ProcessTriangle(state, td, SamplesIn[0]+base, temps);
+                break;
+            case FWF_Sinusoid:
+                ProcessSinusoid(state, td, SamplesIn[0]+base, temps);
+                break;
+        }
+
+        for(kt = 0;kt < NumChannels;kt++)
+        {
+            ALfloat gain = state->Gain[0][kt];
+            if(fabsf(gain) > GAIN_SILENCE_THRESHOLD)
+            {
+                for(it = 0;it < td;it++)
+                    SamplesOut[kt][it+base] += temps[it][0] * gain;
+            }
+
+            gain = state->Gain[1][kt];
+            if(fabsf(gain) > GAIN_SILENCE_THRESHOLD)
+            {
+                for(it = 0;it < td;it++)
+                    SamplesOut[kt][it+base] += temps[it][1] * gain;
+            }
+        }
+
+        base += td;
+    }
+}
+
+
+typedef struct ALflangerStateFactory {
+    DERIVE_FROM_TYPE(ALeffectStateFactory);
+} ALflangerStateFactory;
+
+ALeffectState *ALflangerStateFactory_create(ALflangerStateFactory *UNUSED(factory))
+{
+    ALflangerState *state;
+
+    NEW_OBJ0(state, ALflangerState)();
+    if(!state) return NULL;
+
+    return STATIC_CAST(ALeffectState, state);
+}
+
+DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALflangerStateFactory);
+
+ALeffectStateFactory *ALflangerStateFactory_getFactory(void)
+{
+    static ALflangerStateFactory FlangerFactory = { { GET_VTABLE2(ALflangerStateFactory, ALeffectStateFactory) } };
+
+    return STATIC_CAST(ALeffectStateFactory, &FlangerFactory);
+}
+
+
+void ALflanger_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
+{
+    ALeffectProps *props = &effect->Props;
+    switch(param)
+    {
+        case AL_FLANGER_WAVEFORM:
+            if(!(val >= AL_FLANGER_MIN_WAVEFORM && val <= AL_FLANGER_MAX_WAVEFORM))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Flanger.Waveform = val;
+            break;
+
+        case AL_FLANGER_PHASE:
+            if(!(val >= AL_FLANGER_MIN_PHASE && val <= AL_FLANGER_MAX_PHASE))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Flanger.Phase = val;
+            break;
+
+        default:
+            SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+    }
+}
+void ALflanger_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
+{
+    ALflanger_setParami(effect, context, param, vals[0]);
+}
+void ALflanger_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
+{
+    ALeffectProps *props = &effect->Props;
+    switch(param)
+    {
+        case AL_FLANGER_RATE:
+            if(!(val >= AL_FLANGER_MIN_RATE && val <= AL_FLANGER_MAX_RATE))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Flanger.Rate = val;
+            break;
+
+        case AL_FLANGER_DEPTH:
+            if(!(val >= AL_FLANGER_MIN_DEPTH && val <= AL_FLANGER_MAX_DEPTH))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Flanger.Depth = val;
+            break;
+
+        case AL_FLANGER_FEEDBACK:
+            if(!(val >= AL_FLANGER_MIN_FEEDBACK && val <= AL_FLANGER_MAX_FEEDBACK))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Flanger.Feedback = val;
+            break;
+
+        case AL_FLANGER_DELAY:
+            if(!(val >= AL_FLANGER_MIN_DELAY && val <= AL_FLANGER_MAX_DELAY))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Flanger.Delay = val;
+            break;
+
+        default:
+            SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+    }
+}
+void ALflanger_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
+{
+    ALflanger_setParamf(effect, context, param, vals[0]);
+}
+
+void ALflanger_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
+{
+    const ALeffectProps *props = &effect->Props;
+    switch(param)
+    {
+        case AL_FLANGER_WAVEFORM:
+            *val = props->Flanger.Waveform;
+            break;
+
+        case AL_FLANGER_PHASE:
+            *val = props->Flanger.Phase;
+            break;
+
+        default:
+            SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+    }
+}
+void ALflanger_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
+{
+    ALflanger_getParami(effect, context, param, vals);
+}
+void ALflanger_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
+{
+    const ALeffectProps *props = &effect->Props;
+    switch(param)
+    {
+        case AL_FLANGER_RATE:
+            *val = props->Flanger.Rate;
+            break;
+
+        case AL_FLANGER_DEPTH:
+            *val = props->Flanger.Depth;
+            break;
+
+        case AL_FLANGER_FEEDBACK:
+            *val = props->Flanger.Feedback;
+            break;
+
+        case AL_FLANGER_DELAY:
+            *val = props->Flanger.Delay;
+            break;
+
+        default:
+            SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+    }
+}
+void ALflanger_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
+{
+    ALflanger_getParamf(effect, context, param, vals);
+}
+
+DEFINE_ALEFFECT_VTABLE(ALflanger);

+ 311 - 0
Engine/lib/openal-soft/Alc/effects/modulator.c

@@ -0,0 +1,311 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 2009 by Chris Robinson.
+ * This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the
+ *  Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#include "config.h"
+
+#include <math.h>
+#include <stdlib.h>
+
+#include "alMain.h"
+#include "alFilter.h"
+#include "alAuxEffectSlot.h"
+#include "alError.h"
+#include "alu.h"
+
+
+typedef struct ALmodulatorState {
+    DERIVE_FROM_TYPE(ALeffectState);
+
+    void (*Process)(ALfloat*, const ALfloat*, ALuint, const ALuint, ALuint);
+
+    ALuint index;
+    ALuint step;
+
+    ALfloat Gain[MAX_EFFECT_CHANNELS][MAX_OUTPUT_CHANNELS];
+
+    ALfilterState Filter[MAX_EFFECT_CHANNELS];
+} ALmodulatorState;
+
+static ALvoid ALmodulatorState_Destruct(ALmodulatorState *state);
+static ALboolean ALmodulatorState_deviceUpdate(ALmodulatorState *state, ALCdevice *device);
+static ALvoid ALmodulatorState_update(ALmodulatorState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props);
+static ALvoid ALmodulatorState_process(ALmodulatorState *state, ALuint SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels);
+DECLARE_DEFAULT_ALLOCATORS(ALmodulatorState)
+
+DEFINE_ALEFFECTSTATE_VTABLE(ALmodulatorState);
+
+
+#define WAVEFORM_FRACBITS  24
+#define WAVEFORM_FRACONE   (1<<WAVEFORM_FRACBITS)
+#define WAVEFORM_FRACMASK  (WAVEFORM_FRACONE-1)
+
+static inline ALfloat Sin(ALuint index)
+{
+    return sinf(index*(F_TAU/WAVEFORM_FRACONE) - F_PI)*0.5f + 0.5f;
+}
+
+static inline ALfloat Saw(ALuint index)
+{
+    return (ALfloat)index / WAVEFORM_FRACONE;
+}
+
+static inline ALfloat Square(ALuint index)
+{
+    return (ALfloat)((index >> (WAVEFORM_FRACBITS - 1)) & 1);
+}
+
+#define DECL_TEMPLATE(func)                                                   \
+static void Modulate##func(ALfloat *restrict dst, const ALfloat *restrict src,\
+                           ALuint index, const ALuint step, ALuint todo)      \
+{                                                                             \
+    ALuint i;                                                                 \
+    for(i = 0;i < todo;i++)                                                   \
+    {                                                                         \
+        index += step;                                                        \
+        index &= WAVEFORM_FRACMASK;                                           \
+        dst[i] = src[i] * func(index);                                        \
+    }                                                                         \
+}
+
+DECL_TEMPLATE(Sin)
+DECL_TEMPLATE(Saw)
+DECL_TEMPLATE(Square)
+
+#undef DECL_TEMPLATE
+
+
+static void ALmodulatorState_Construct(ALmodulatorState *state)
+{
+    ALuint i;
+
+    ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
+    SET_VTABLE2(ALmodulatorState, ALeffectState, state);
+
+    state->index = 0;
+    state->step = 1;
+
+    for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
+        ALfilterState_clear(&state->Filter[i]);
+}
+
+static ALvoid ALmodulatorState_Destruct(ALmodulatorState *state)
+{
+    ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
+}
+
+static ALboolean ALmodulatorState_deviceUpdate(ALmodulatorState *UNUSED(state), ALCdevice *UNUSED(device))
+{
+    return AL_TRUE;
+}
+
+static ALvoid ALmodulatorState_update(ALmodulatorState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props)
+{
+    ALfloat cw, a;
+    ALuint i;
+
+    if(props->Modulator.Waveform == AL_RING_MODULATOR_SINUSOID)
+        state->Process = ModulateSin;
+    else if(props->Modulator.Waveform == AL_RING_MODULATOR_SAWTOOTH)
+        state->Process = ModulateSaw;
+    else /*if(Slot->Params.EffectProps.Modulator.Waveform == AL_RING_MODULATOR_SQUARE)*/
+        state->Process = ModulateSquare;
+
+    state->step = fastf2u(props->Modulator.Frequency*WAVEFORM_FRACONE /
+                          Device->Frequency);
+    if(state->step == 0) state->step = 1;
+
+    /* Custom filter coeffs, which match the old version instead of a low-shelf. */
+    cw = cosf(F_TAU * props->Modulator.HighPassCutoff / Device->Frequency);
+    a = (2.0f-cw) - sqrtf(powf(2.0f-cw, 2.0f) - 1.0f);
+
+    for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
+    {
+        state->Filter[i].a1 = -a;
+        state->Filter[i].a2 = 0.0f;
+        state->Filter[i].b0 = a;
+        state->Filter[i].b1 = -a;
+        state->Filter[i].b2 = 0.0f;
+    }
+
+    STATIC_CAST(ALeffectState,state)->OutBuffer = Device->FOAOut.Buffer;
+    STATIC_CAST(ALeffectState,state)->OutChannels = Device->FOAOut.NumChannels;
+    for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
+        ComputeFirstOrderGains(Device->FOAOut, IdentityMatrixf.m[i],
+                               Slot->Params.Gain, state->Gain[i]);
+}
+
+static ALvoid ALmodulatorState_process(ALmodulatorState *state, ALuint SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels)
+{
+    const ALuint step = state->step;
+    ALuint index = state->index;
+    ALuint base;
+
+    for(base = 0;base < SamplesToDo;)
+    {
+        ALfloat temps[2][128];
+        ALuint td = minu(128, SamplesToDo-base);
+        ALuint i, j, k;
+
+        for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
+        {
+            ALfilterState_process(&state->Filter[j], temps[0], &SamplesIn[j][base], td);
+            state->Process(temps[1], temps[0], index, step, td);
+
+            for(k = 0;k < NumChannels;k++)
+            {
+                ALfloat gain = state->Gain[j][k];
+                if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
+                    continue;
+
+                for(i = 0;i < td;i++)
+                    SamplesOut[k][base+i] += gain * temps[1][i];
+            }
+        }
+
+        for(i = 0;i < td;i++)
+        {
+            index += step;
+            index &= WAVEFORM_FRACMASK;
+        }
+        base += td;
+    }
+    state->index = index;
+}
+
+
+typedef struct ALmodulatorStateFactory {
+    DERIVE_FROM_TYPE(ALeffectStateFactory);
+} ALmodulatorStateFactory;
+
+static ALeffectState *ALmodulatorStateFactory_create(ALmodulatorStateFactory *UNUSED(factory))
+{
+    ALmodulatorState *state;
+
+    NEW_OBJ0(state, ALmodulatorState)();
+    if(!state) return NULL;
+
+    return STATIC_CAST(ALeffectState, state);
+}
+
+DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALmodulatorStateFactory);
+
+ALeffectStateFactory *ALmodulatorStateFactory_getFactory(void)
+{
+    static ALmodulatorStateFactory ModulatorFactory = { { GET_VTABLE2(ALmodulatorStateFactory, ALeffectStateFactory) } };
+
+    return STATIC_CAST(ALeffectStateFactory, &ModulatorFactory);
+}
+
+
+void ALmodulator_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
+{
+    ALeffectProps *props = &effect->Props;
+    switch(param)
+    {
+        case AL_RING_MODULATOR_FREQUENCY:
+            if(!(val >= AL_RING_MODULATOR_MIN_FREQUENCY && val <= AL_RING_MODULATOR_MAX_FREQUENCY))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Modulator.Frequency = val;
+            break;
+
+        case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
+            if(!(val >= AL_RING_MODULATOR_MIN_HIGHPASS_CUTOFF && val <= AL_RING_MODULATOR_MAX_HIGHPASS_CUTOFF))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Modulator.HighPassCutoff = val;
+            break;
+
+        default:
+            SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+    }
+}
+void ALmodulator_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
+{
+    ALmodulator_setParamf(effect, context, param, vals[0]);
+}
+void ALmodulator_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
+{
+    ALeffectProps *props = &effect->Props;
+    switch(param)
+    {
+        case AL_RING_MODULATOR_FREQUENCY:
+        case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
+            ALmodulator_setParamf(effect, context, param, (ALfloat)val);
+            break;
+
+        case AL_RING_MODULATOR_WAVEFORM:
+            if(!(val >= AL_RING_MODULATOR_MIN_WAVEFORM && val <= AL_RING_MODULATOR_MAX_WAVEFORM))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Modulator.Waveform = val;
+            break;
+
+        default:
+            SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+    }
+}
+void ALmodulator_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
+{
+    ALmodulator_setParami(effect, context, param, vals[0]);
+}
+
+void ALmodulator_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
+{
+    const ALeffectProps *props = &effect->Props;
+    switch(param)
+    {
+        case AL_RING_MODULATOR_FREQUENCY:
+            *val = (ALint)props->Modulator.Frequency;
+            break;
+        case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
+            *val = (ALint)props->Modulator.HighPassCutoff;
+            break;
+        case AL_RING_MODULATOR_WAVEFORM:
+            *val = props->Modulator.Waveform;
+            break;
+
+        default:
+            SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+    }
+}
+void ALmodulator_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
+{
+    ALmodulator_getParami(effect, context, param, vals);
+}
+void ALmodulator_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
+{
+    const ALeffectProps *props = &effect->Props;
+    switch(param)
+    {
+        case AL_RING_MODULATOR_FREQUENCY:
+            *val = props->Modulator.Frequency;
+            break;
+        case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
+            *val = props->Modulator.HighPassCutoff;
+            break;
+
+        default:
+            SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+    }
+}
+void ALmodulator_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
+{
+    ALmodulator_getParamf(effect, context, param, vals);
+}
+
+DEFINE_ALEFFECT_VTABLE(ALmodulator);

+ 179 - 0
Engine/lib/openal-soft/Alc/effects/null.c

@@ -0,0 +1,179 @@
+#include "config.h"
+
+#include <stdlib.h>
+
+#include "AL/al.h"
+#include "AL/alc.h"
+#include "alMain.h"
+#include "alAuxEffectSlot.h"
+#include "alError.h"
+
+
+typedef struct ALnullState {
+    DERIVE_FROM_TYPE(ALeffectState);
+} ALnullState;
+
+/* Forward-declare "virtual" functions to define the vtable with. */
+static ALvoid ALnullState_Destruct(ALnullState *state);
+static ALboolean ALnullState_deviceUpdate(ALnullState *state, ALCdevice *device);
+static ALvoid ALnullState_update(ALnullState *state, const ALCdevice *device, const ALeffectslot *slot, const ALeffectProps *props);
+static ALvoid ALnullState_process(ALnullState *state, ALuint samplesToDo, const ALfloatBUFFERSIZE*restrict samplesIn, ALfloatBUFFERSIZE*restrict samplesOut, ALuint NumChannels);
+static void *ALnullState_New(size_t size);
+static void ALnullState_Delete(void *ptr);
+
+/* Define the ALeffectState vtable for this type. */
+DEFINE_ALEFFECTSTATE_VTABLE(ALnullState);
+
+
+/* This constructs the effect state. It's called when the object is first
+ * created. Make sure to call the parent Construct function first, and set the
+ * vtable!
+ */
+static void ALnullState_Construct(ALnullState *state)
+{
+    ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
+    SET_VTABLE2(ALnullState, ALeffectState, state);
+}
+
+/* This destructs (not free!) the effect state. It's called only when the
+ * effect slot is no longer used. Make sure to call the parent Destruct
+ * function before returning!
+ */
+static ALvoid ALnullState_Destruct(ALnullState *state)
+{
+    ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
+}
+
+/* This updates the device-dependant effect state. This is called on
+ * initialization and any time the device parameters (eg. playback frequency,
+ * format) have been changed.
+ */
+static ALboolean ALnullState_deviceUpdate(ALnullState* UNUSED(state), ALCdevice* UNUSED(device))
+{
+    return AL_TRUE;
+}
+
+/* This updates the effect state. This is called any time the effect is
+ * (re)loaded into a slot.
+ */
+static ALvoid ALnullState_update(ALnullState* UNUSED(state), const ALCdevice* UNUSED(device), const ALeffectslot* UNUSED(slot), const ALeffectProps* UNUSED(props))
+{
+}
+
+/* This processes the effect state, for the given number of samples from the
+ * input to the output buffer. The result should be added to the output buffer,
+ * not replace it.
+ */
+static ALvoid ALnullState_process(ALnullState* UNUSED(state), ALuint UNUSED(samplesToDo), const ALfloatBUFFERSIZE*restrict UNUSED(samplesIn), ALfloatBUFFERSIZE*restrict UNUSED(samplesOut), ALuint UNUSED(NumChannels))
+{
+}
+
+/* This allocates memory to store the object, before it gets constructed.
+ * DECLARE_DEFAULT_ALLOCATORS can be used to declare a default method.
+ */
+static void *ALnullState_New(size_t size)
+{
+    return al_malloc(16, size);
+}
+
+/* This frees the memory used by the object, after it has been destructed.
+ * DECLARE_DEFAULT_ALLOCATORS can be used to declare a default method.
+ */
+static void ALnullState_Delete(void *ptr)
+{
+    al_free(ptr);
+}
+
+
+typedef struct ALnullStateFactory {
+    DERIVE_FROM_TYPE(ALeffectStateFactory);
+} ALnullStateFactory;
+
+/* Creates ALeffectState objects of the appropriate type. */
+ALeffectState *ALnullStateFactory_create(ALnullStateFactory *UNUSED(factory))
+{
+    ALnullState *state;
+
+    NEW_OBJ0(state, ALnullState)();
+    if(!state) return NULL;
+
+    return STATIC_CAST(ALeffectState, state);
+}
+
+/* Define the ALeffectStateFactory vtable for this type. */
+DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALnullStateFactory);
+
+ALeffectStateFactory *ALnullStateFactory_getFactory(void)
+{
+    static ALnullStateFactory NullFactory = { { GET_VTABLE2(ALnullStateFactory, ALeffectStateFactory) } };
+    return STATIC_CAST(ALeffectStateFactory, &NullFactory);
+}
+
+
+void ALnull_setParami(ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, ALint UNUSED(val))
+{
+    switch(param)
+    {
+        default:
+            SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+    }
+}
+void ALnull_setParamiv(ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, const ALint* UNUSED(vals))
+{
+    switch(param)
+    {
+        default:
+            SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+    }
+}
+void ALnull_setParamf(ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, ALfloat UNUSED(val))
+{
+    switch(param)
+    {
+        default:
+            SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+    }
+}
+void ALnull_setParamfv(ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, const ALfloat* UNUSED(vals))
+{
+    switch(param)
+    {
+        default:
+            SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+    }
+}
+
+void ALnull_getParami(const ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, ALint* UNUSED(val))
+{
+    switch(param)
+    {
+        default:
+            SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+    }
+}
+void ALnull_getParamiv(const ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, ALint* UNUSED(vals))
+{
+    switch(param)
+    {
+        default:
+            SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+    }
+}
+void ALnull_getParamf(const ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, ALfloat* UNUSED(val))
+{
+    switch(param)
+    {
+        default:
+            SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+    }
+}
+void ALnull_getParamfv(const ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, ALfloat* UNUSED(vals))
+{
+    switch(param)
+    {
+        default:
+            SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+    }
+}
+
+DEFINE_ALEFFECT_VTABLE(ALnull);

+ 1994 - 0
Engine/lib/openal-soft/Alc/effects/reverb.c

@@ -0,0 +1,1994 @@
+/**
+ * Reverb for the OpenAL cross platform audio library
+ * Copyright (C) 2008-2009 by Christopher Fitzgerald.
+ * This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the
+ *  Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+#include "alMain.h"
+#include "alu.h"
+#include "alAuxEffectSlot.h"
+#include "alEffect.h"
+#include "alFilter.h"
+#include "alError.h"
+#include "mixer_defs.h"
+
+
+/* This is the maximum number of samples processed for each inner loop
+ * iteration. */
+#define MAX_UPDATE_SAMPLES  256
+
+
+static MixerFunc MixSamples = Mix_C;
+static RowMixerFunc MixRowSamples = MixRow_C;
+
+static alonce_flag mixfunc_inited = AL_ONCE_FLAG_INIT;
+static void init_mixfunc(void)
+{
+    MixSamples = SelectMixer();
+    MixRowSamples = SelectRowMixer();
+}
+
+
+typedef struct DelayLine
+{
+    // The delay lines use sample lengths that are powers of 2 to allow the
+    // use of bit-masking instead of a modulus for wrapping.
+    ALuint   Mask;
+    ALfloat *Line;
+} DelayLine;
+
+typedef struct ALreverbState {
+    DERIVE_FROM_TYPE(ALeffectState);
+
+    ALboolean IsEax;
+
+    // All delay lines are allocated as a single buffer to reduce memory
+    // fragmentation and management code.
+    ALfloat  *SampleBuffer;
+    ALuint    TotalSamples;
+
+    // Master effect filters
+    struct {
+        ALfilterState Lp;
+        ALfilterState Hp; // EAX only
+    } Filter[4];
+
+    struct {
+        // Modulator delay line.
+        DelayLine Delay[4];
+
+        // The vibrato time is tracked with an index over a modulus-wrapped
+        // range (in samples).
+        ALuint    Index;
+        ALuint    Range;
+
+        // The depth of frequency change (also in samples) and its filter.
+        ALfloat   Depth;
+        ALfloat   Coeff;
+        ALfloat   Filter;
+    } Mod; // EAX only
+
+    /* Core delay line (early reflections and late reverb tap from this). */
+    DelayLine Delay;
+    /* The tap points for the initial delay. First set go to early
+     * reflections, second to late reverb.
+     */
+    ALuint    EarlyDelayTap[4];
+    ALuint    LateDelayTap[4];
+
+    struct {
+        // Early reflections are done with 4 delay lines.
+        ALfloat   Coeff[4];
+        DelayLine Delay[4];
+        ALuint    Offset[4];
+
+        // The gain for each output channel based on 3D panning.
+        ALfloat CurrentGain[4][MAX_OUTPUT_CHANNELS];
+        ALfloat PanGain[4][MAX_OUTPUT_CHANNELS];
+    } Early;
+
+    struct {
+        // Output gain for late reverb.
+        ALfloat   Gain;
+
+        // Attenuation to compensate for the modal density and decay rate of
+        // the late lines.
+        ALfloat   DensityGain;
+
+        // The feed-back and feed-forward all-pass coefficient.
+        ALfloat   ApFeedCoeff;
+
+        // Mixing matrix coefficient.
+        ALfloat   MixCoeff;
+
+        // Late reverb has 4 parallel all-pass filters.
+        struct {
+            ALfloat   Coeff;
+            DelayLine Delay;
+            ALuint    Offset;
+        } Ap[4];
+
+        // In addition to 4 cyclical delay lines.
+        ALfloat   Coeff[4];
+        DelayLine Delay[4];
+        ALuint    Offset[4];
+
+        // The cyclical delay lines are 1-pole low-pass filtered.
+        struct {
+            ALfloat Sample;
+            ALfloat Coeff;
+        } Lp[4];
+
+        // The gain for each output channel based on 3D panning.
+        ALfloat CurrentGain[4][MAX_OUTPUT_CHANNELS];
+        ALfloat PanGain[4][MAX_OUTPUT_CHANNELS];
+    } Late;
+
+    struct {
+        // Attenuation to compensate for the modal density and decay rate of
+        // the echo line.
+        ALfloat   DensityGain;
+
+        // Echo delay and all-pass lines.
+        struct {
+            DelayLine Feedback;
+            DelayLine Ap;
+        } Delay[4];
+
+        ALfloat   Coeff;
+        ALfloat   ApFeedCoeff;
+        ALfloat   ApCoeff;
+
+        ALuint    Offset;
+        ALuint    ApOffset;
+
+        // The echo line is 1-pole low-pass filtered.
+        ALfloat   LpCoeff;
+        ALfloat   LpSample[4];
+
+        // Echo mixing coefficient.
+        ALfloat   MixCoeff;
+    } Echo; // EAX only
+
+    // The current read offset for all delay lines.
+    ALuint Offset;
+
+    /* Temporary storage used when processing. */
+    alignas(16) ALfloat AFormatSamples[4][MAX_UPDATE_SAMPLES];
+    alignas(16) ALfloat ReverbSamples[4][MAX_UPDATE_SAMPLES];
+    alignas(16) ALfloat EarlySamples[4][MAX_UPDATE_SAMPLES];
+} ALreverbState;
+
+static ALvoid ALreverbState_Destruct(ALreverbState *State);
+static ALboolean ALreverbState_deviceUpdate(ALreverbState *State, ALCdevice *Device);
+static ALvoid ALreverbState_update(ALreverbState *State, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props);
+static ALvoid ALreverbState_process(ALreverbState *State, ALuint SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels);
+DECLARE_DEFAULT_ALLOCATORS(ALreverbState)
+
+DEFINE_ALEFFECTSTATE_VTABLE(ALreverbState);
+
+
+static void ALreverbState_Construct(ALreverbState *state)
+{
+    ALuint index, l;
+
+    ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
+    SET_VTABLE2(ALreverbState, ALeffectState, state);
+
+    state->IsEax = AL_FALSE;
+
+    state->TotalSamples = 0;
+    state->SampleBuffer = NULL;
+
+    for(index = 0;index < 4;index++)
+    {
+        ALfilterState_clear(&state->Filter[index].Lp);
+        ALfilterState_clear(&state->Filter[index].Hp);
+
+        state->Mod.Delay[index].Mask = 0;
+        state->Mod.Delay[index].Line = NULL;
+    }
+
+    state->Mod.Index = 0;
+    state->Mod.Range = 1;
+    state->Mod.Depth = 0.0f;
+    state->Mod.Coeff = 0.0f;
+    state->Mod.Filter = 0.0f;
+
+    state->Delay.Mask = 0;
+    state->Delay.Line = NULL;
+    for(index = 0;index < 4;index++)
+        state->EarlyDelayTap[index] = 0;
+    for(index = 0;index < 4;index++)
+        state->LateDelayTap[index] = 0;
+
+    for(index = 0;index < 4;index++)
+    {
+        state->Early.Coeff[index] = 0.0f;
+        state->Early.Delay[index].Mask = 0;
+        state->Early.Delay[index].Line = NULL;
+        state->Early.Offset[index] = 0;
+    }
+
+    state->Late.Gain = 0.0f;
+    state->Late.DensityGain = 0.0f;
+    state->Late.ApFeedCoeff = 0.0f;
+    state->Late.MixCoeff = 0.0f;
+    for(index = 0;index < 4;index++)
+    {
+        state->Late.Ap[index].Coeff = 0.0f;
+        state->Late.Ap[index].Delay.Mask = 0;
+        state->Late.Ap[index].Delay.Line = NULL;
+        state->Late.Ap[index].Offset = 0;
+
+        state->Late.Coeff[index] = 0.0f;
+        state->Late.Delay[index].Mask = 0;
+        state->Late.Delay[index].Line = NULL;
+        state->Late.Offset[index] = 0;
+
+        state->Late.Lp[index].Sample = 0.0f;
+        state->Late.Lp[index].Coeff = 0.0f;
+    }
+
+    for(l = 0;l < 4;l++)
+    {
+        for(index = 0;index < MAX_OUTPUT_CHANNELS;index++)
+        {
+            state->Early.CurrentGain[l][index] = 0.0f;
+            state->Early.PanGain[l][index] = 0.0f;
+            state->Late.CurrentGain[l][index] = 0.0f;
+            state->Late.PanGain[l][index] = 0.0f;
+        }
+    }
+
+    state->Echo.DensityGain = 0.0f;
+    for(l = 0;l < 4;l++)
+    {
+        state->Echo.Delay[l].Feedback.Mask = 0;
+        state->Echo.Delay[l].Feedback.Line = NULL;
+        state->Echo.Delay[l].Ap.Mask = 0;
+        state->Echo.Delay[l].Ap.Line = NULL;
+    }
+    state->Echo.Coeff = 0.0f;
+    state->Echo.ApFeedCoeff = 0.0f;
+    state->Echo.ApCoeff = 0.0f;
+    state->Echo.Offset = 0;
+    state->Echo.ApOffset = 0;
+    state->Echo.LpCoeff = 0.0f;
+    for(l = 0;l < 4;l++)
+        state->Echo.LpSample[l] = 0.0f;
+    state->Echo.MixCoeff = 0.0f;
+
+    state->Offset = 0;
+}
+
+static ALvoid ALreverbState_Destruct(ALreverbState *State)
+{
+    al_free(State->SampleBuffer);
+    State->SampleBuffer = NULL;
+
+    ALeffectState_Destruct(STATIC_CAST(ALeffectState,State));
+}
+
+/* This is a user config option for modifying the overall output of the reverb
+ * effect.
+ */
+ALfloat ReverbBoost = 1.0f;
+
+/* Specifies whether to use a standard reverb effect in place of EAX reverb (no
+ * high-pass, modulation, or echo).
+ */
+ALboolean EmulateEAXReverb = AL_FALSE;
+
+/* This coefficient is used to define the maximum frequency range controlled
+ * by the modulation depth.  The current value of 0.1 will allow it to swing
+ * from 0.9x to 1.1x.  This value must be below 1.  At 1 it will cause the
+ * sampler to stall on the downswing, and above 1 it will cause it to sample
+ * backwards.
+ */
+static const ALfloat MODULATION_DEPTH_COEFF = 0.1f;
+
+/* A filter is used to avoid the terrible distortion caused by changing
+ * modulation time and/or depth.  To be consistent across different sample
+ * rates, the coefficient must be raised to a constant divided by the sample
+ * rate:  coeff^(constant / rate).
+ */
+static const ALfloat MODULATION_FILTER_COEFF = 0.048f;
+static const ALfloat MODULATION_FILTER_CONST = 100000.0f;
+
+// When diffusion is above 0, an all-pass filter is used to take the edge off
+// the echo effect.  It uses the following line length (in seconds).
+static const ALfloat ECHO_ALLPASS_LENGTH = 0.0133f;
+
+/* Input into the early reflections and late reverb are decorrelated between
+ * four channels. Their timings are dependent on a fraction and multiplier. See
+ * the UpdateDelayLine() routine for the calculations involved.
+ */
+static const ALfloat DECO_FRACTION = 0.15f;
+static const ALfloat DECO_MULTIPLIER = 2.0f;
+
+// All delay line lengths are specified in seconds.
+
+// The lengths of the early delay lines.
+static const ALfloat EARLY_LINE_LENGTH[4] =
+{
+    0.0015f, 0.0045f, 0.0135f, 0.0405f
+};
+
+// The lengths of the late cyclical delay lines.
+static const ALfloat LATE_LINE_LENGTH[4] =
+{
+    0.0211f, 0.0311f, 0.0461f, 0.0680f
+};
+
+// The lengths of the late all-pass delay lines.
+static const ALfloat ALLPASS_LINE_LENGTH[4] =
+{
+    0.0151f, 0.0167f, 0.0183f, 0.0200f,
+};
+
+// The late cyclical delay lines have a variable length dependent on the
+// effect's density parameter (inverted for some reason) and this multiplier.
+static const ALfloat LATE_LINE_MULTIPLIER = 4.0f;
+
+
+#if defined(_WIN32) && !defined (_M_X64) && !defined(_M_ARM)
+/* HACK: Workaround for a modff bug in 32-bit Windows, which attempts to write
+ * a 64-bit double to the 32-bit float parameter.
+ */
+static inline float hack_modff(float x, float *y)
+{
+    double di;
+    double df = modf((double)x, &di);
+    *y = (float)di;
+    return (float)df;
+}
+#define modff hack_modff
+#endif
+
+
+/**************************************
+ *  Device Update                     *
+ **************************************/
+
+// Given the allocated sample buffer, this function updates each delay line
+// offset.
+static inline ALvoid RealizeLineOffset(ALfloat *sampleBuffer, DelayLine *Delay)
+{
+    Delay->Line = &sampleBuffer[(ptrdiff_t)Delay->Line];
+}
+
+// Calculate the length of a delay line and store its mask and offset.
+static ALuint CalcLineLength(ALfloat length, ptrdiff_t offset, ALuint frequency, ALuint extra, DelayLine *Delay)
+{
+    ALuint samples;
+
+    // All line lengths are powers of 2, calculated from their lengths, with
+    // an additional sample in case of rounding errors.
+    samples = fastf2u(length*frequency) + extra;
+    samples = NextPowerOf2(samples + 1);
+    // All lines share a single sample buffer.
+    Delay->Mask = samples - 1;
+    Delay->Line = (ALfloat*)offset;
+    // Return the sample count for accumulation.
+    return samples;
+}
+
+/* Calculates the delay line metrics and allocates the shared sample buffer
+ * for all lines given the sample rate (frequency).  If an allocation failure
+ * occurs, it returns AL_FALSE.
+ */
+static ALboolean AllocLines(ALuint frequency, ALreverbState *State)
+{
+    ALuint totalSamples, index;
+    ALfloat length;
+
+    // All delay line lengths are calculated to accomodate the full range of
+    // lengths given their respective paramters.
+    totalSamples = 0;
+
+    /* The modulator's line length is calculated from the maximum modulation
+     * time and depth coefficient, and halfed for the low-to-high frequency
+     * swing.  An additional sample is added to keep it stable when there is no
+     * modulation.
+     */
+    length = (AL_EAXREVERB_MAX_MODULATION_TIME*MODULATION_DEPTH_COEFF/2.0f);
+    for(index = 0;index < 4;index++)
+        totalSamples += CalcLineLength(length, totalSamples, frequency, 1,
+                                       &State->Mod.Delay[index]);
+
+    /* The initial delay is the sum of the reflections and late reverb delays.
+     * The decorrelator length is calculated from the lowest reverb density (a
+     * parameter value of 1). This must include space for storing a loop
+     * update.
+     */
+    length = AL_EAXREVERB_MAX_REFLECTIONS_DELAY +
+             AL_EAXREVERB_MAX_LATE_REVERB_DELAY;
+    length += (DECO_FRACTION * DECO_MULTIPLIER * DECO_MULTIPLIER) *
+              LATE_LINE_LENGTH[0] * (1.0f + LATE_LINE_MULTIPLIER);
+    /* Multiply length by 4, since we're storing 4 interleaved channels in the
+     * main delay line.
+     */
+    totalSamples += CalcLineLength(length*4, totalSamples, frequency,
+                                   MAX_UPDATE_SAMPLES*4, &State->Delay);
+
+    // The early reflection lines.
+    for(index = 0;index < 4;index++)
+        totalSamples += CalcLineLength(EARLY_LINE_LENGTH[index], totalSamples,
+                                       frequency, 0, &State->Early.Delay[index]);
+
+    // The late delay lines are calculated from the lowest reverb density.
+    for(index = 0;index < 4;index++)
+    {
+        length = LATE_LINE_LENGTH[index] * (1.0f + LATE_LINE_MULTIPLIER);
+        totalSamples += CalcLineLength(length, totalSamples, frequency, 0,
+                                       &State->Late.Delay[index]);
+    }
+
+    // The late all-pass lines.
+    for(index = 0;index < 4;index++)
+        totalSamples += CalcLineLength(ALLPASS_LINE_LENGTH[index], totalSamples,
+                                       frequency, 0, &State->Late.Ap[index].Delay);
+
+    // The echo all-pass and delay lines.
+    for(index = 0;index < 4;index++)
+    {
+        totalSamples += CalcLineLength(ECHO_ALLPASS_LENGTH, totalSamples,
+                                       frequency, 0, &State->Echo.Delay[index].Ap);
+        totalSamples += CalcLineLength(AL_EAXREVERB_MAX_ECHO_TIME, totalSamples,
+                                       frequency, 0, &State->Echo.Delay[index].Feedback);
+    }
+
+    if(totalSamples != State->TotalSamples)
+    {
+        ALfloat *newBuffer;
+
+        TRACE("New reverb buffer length: %u samples\n", totalSamples);
+        newBuffer = al_calloc(16, sizeof(ALfloat) * totalSamples);
+        if(!newBuffer) return AL_FALSE;
+
+        al_free(State->SampleBuffer);
+        State->SampleBuffer = newBuffer;
+        State->TotalSamples = totalSamples;
+    }
+
+    // Update all delays to reflect the new sample buffer.
+    RealizeLineOffset(State->SampleBuffer, &State->Delay);
+    for(index = 0;index < 4;index++)
+    {
+        RealizeLineOffset(State->SampleBuffer, &State->Mod.Delay[index]);
+
+        RealizeLineOffset(State->SampleBuffer, &State->Early.Delay[index]);
+
+        RealizeLineOffset(State->SampleBuffer, &State->Late.Ap[index].Delay);
+        RealizeLineOffset(State->SampleBuffer, &State->Late.Delay[index]);
+
+        RealizeLineOffset(State->SampleBuffer, &State->Echo.Delay[index].Ap);
+        RealizeLineOffset(State->SampleBuffer, &State->Echo.Delay[index].Feedback);
+    }
+
+    // Clear the sample buffer.
+    for(index = 0;index < State->TotalSamples;index++)
+        State->SampleBuffer[index] = 0.0f;
+
+    return AL_TRUE;
+}
+
+static ALboolean ALreverbState_deviceUpdate(ALreverbState *State, ALCdevice *Device)
+{
+    ALuint frequency = Device->Frequency, index;
+
+    // Allocate the delay lines.
+    if(!AllocLines(frequency, State))
+        return AL_FALSE;
+
+    // Calculate the modulation filter coefficient.  Notice that the exponent
+    // is calculated given the current sample rate.  This ensures that the
+    // resulting filter response over time is consistent across all sample
+    // rates.
+    State->Mod.Coeff = powf(MODULATION_FILTER_COEFF,
+                            MODULATION_FILTER_CONST / frequency);
+
+    // The early reflection and late all-pass filter line lengths are static,
+    // so their offsets only need to be calculated once.
+    for(index = 0;index < 4;index++)
+    {
+        State->Early.Offset[index] = fastf2u(EARLY_LINE_LENGTH[index] * frequency);
+        State->Late.Ap[index].Offset = fastf2u(ALLPASS_LINE_LENGTH[index] * frequency);
+    }
+
+    // The echo all-pass filter line length is static, so its offset only
+    // needs to be calculated once.
+    State->Echo.ApOffset = fastf2u(ECHO_ALLPASS_LENGTH * frequency);
+
+    return AL_TRUE;
+}
+
+/**************************************
+ *  Effect Update                     *
+ **************************************/
+
+// Calculate a decay coefficient given the length of each cycle and the time
+// until the decay reaches -60 dB.
+static inline ALfloat CalcDecayCoeff(ALfloat length, ALfloat decayTime)
+{
+    return powf(0.001f/*-60 dB*/, length/decayTime);
+}
+
+// Calculate a decay length from a coefficient and the time until the decay
+// reaches -60 dB.
+static inline ALfloat CalcDecayLength(ALfloat coeff, ALfloat decayTime)
+{
+    return log10f(coeff) * decayTime / log10f(0.001f)/*-60 dB*/;
+}
+
+// Calculate an attenuation to be applied to the input of any echo models to
+// compensate for modal density and decay time.
+static inline ALfloat CalcDensityGain(ALfloat a)
+{
+    /* The energy of a signal can be obtained by finding the area under the
+     * squared signal.  This takes the form of Sum(x_n^2), where x is the
+     * amplitude for the sample n.
+     *
+     * Decaying feedback matches exponential decay of the form Sum(a^n),
+     * where a is the attenuation coefficient, and n is the sample.  The area
+     * under this decay curve can be calculated as:  1 / (1 - a).
+     *
+     * Modifying the above equation to find the squared area under the curve
+     * (for energy) yields:  1 / (1 - a^2).  Input attenuation can then be
+     * calculated by inverting the square root of this approximation,
+     * yielding:  1 / sqrt(1 / (1 - a^2)), simplified to: sqrt(1 - a^2).
+     */
+    return sqrtf(1.0f - (a * a));
+}
+
+// Calculate the mixing matrix coefficients given a diffusion factor.
+static inline ALvoid CalcMatrixCoeffs(ALfloat diffusion, ALfloat *x, ALfloat *y)
+{
+    ALfloat n, t;
+
+    // The matrix is of order 4, so n is sqrt (4 - 1).
+    n = sqrtf(3.0f);
+    t = diffusion * atanf(n);
+
+    // Calculate the first mixing matrix coefficient.
+    *x = cosf(t);
+    // Calculate the second mixing matrix coefficient.
+    *y = sinf(t) / n;
+}
+
+// Calculate the limited HF ratio for use with the late reverb low-pass
+// filters.
+static ALfloat CalcLimitedHfRatio(ALfloat hfRatio, ALfloat airAbsorptionGainHF, ALfloat decayTime)
+{
+    ALfloat limitRatio;
+
+    /* Find the attenuation due to air absorption in dB (converting delay
+     * time to meters using the speed of sound).  Then reversing the decay
+     * equation, solve for HF ratio.  The delay length is cancelled out of
+     * the equation, so it can be calculated once for all lines.
+     */
+    limitRatio = 1.0f / (CalcDecayLength(airAbsorptionGainHF, decayTime) *
+                         SPEEDOFSOUNDMETRESPERSEC);
+    /* Using the limit calculated above, apply the upper bound to the HF
+     * ratio. Also need to limit the result to a minimum of 0.1, just like the
+     * HF ratio parameter. */
+    return clampf(limitRatio, 0.1f, hfRatio);
+}
+
+// Calculate the coefficient for a HF (and eventually LF) decay damping
+// filter.
+static inline ALfloat CalcDampingCoeff(ALfloat hfRatio, ALfloat length, ALfloat decayTime, ALfloat decayCoeff, ALfloat cw)
+{
+    ALfloat coeff, g;
+
+    // Eventually this should boost the high frequencies when the ratio
+    // exceeds 1.
+    coeff = 0.0f;
+    if (hfRatio < 1.0f)
+    {
+        // Calculate the low-pass coefficient by dividing the HF decay
+        // coefficient by the full decay coefficient.
+        g = CalcDecayCoeff(length, decayTime * hfRatio) / decayCoeff;
+
+        // Damping is done with a 1-pole filter, so g needs to be squared.
+        g *= g;
+        if(g < 0.9999f) /* 1-epsilon */
+        {
+            /* Be careful with gains < 0.001, as that causes the coefficient
+             * head towards 1, which will flatten the signal. */
+            g = maxf(g, 0.001f);
+            coeff = (1 - g*cw - sqrtf(2*g*(1-cw) - g*g*(1 - cw*cw))) /
+                    (1 - g);
+        }
+
+        // Very low decay times will produce minimal output, so apply an
+        // upper bound to the coefficient.
+        coeff = minf(coeff, 0.98f);
+    }
+    return coeff;
+}
+
+// Update the EAX modulation index, range, and depth.  Keep in mind that this
+// kind of vibrato is additive and not multiplicative as one may expect.  The
+// downswing will sound stronger than the upswing.
+static ALvoid UpdateModulator(ALfloat modTime, ALfloat modDepth, ALuint frequency, ALreverbState *State)
+{
+    ALuint range;
+
+    /* Modulation is calculated in two parts.
+     *
+     * The modulation time effects the sinus applied to the change in
+     * frequency.  An index out of the current time range (both in samples)
+     * is incremented each sample.  The range is bound to a reasonable
+     * minimum (1 sample) and when the timing changes, the index is rescaled
+     * to the new range (to keep the sinus consistent).
+     */
+    range = maxu(fastf2u(modTime*frequency), 1);
+    State->Mod.Index = (ALuint)(State->Mod.Index * (ALuint64)range /
+                                State->Mod.Range);
+    State->Mod.Range = range;
+
+    /* The modulation depth effects the amount of frequency change over the
+     * range of the sinus.  It needs to be scaled by the modulation time so
+     * that a given depth produces a consistent change in frequency over all
+     * ranges of time.  Since the depth is applied to a sinus value, it needs
+     * to be halfed once for the sinus range and again for the sinus swing
+     * in time (half of it is spent decreasing the frequency, half is spent
+     * increasing it).
+     */
+    State->Mod.Depth = modDepth * MODULATION_DEPTH_COEFF * modTime / 2.0f /
+                       2.0f * frequency;
+}
+
+// Update the offsets for the main effect delay line.
+static ALvoid UpdateDelayLine(ALfloat earlyDelay, ALfloat lateDelay, ALfloat density, ALuint frequency, ALreverbState *State)
+{
+    ALfloat length;
+    ALuint i;
+
+    /* The early reflections and late reverb inputs are decorrelated to provide
+     * time-varying reflections, smooth out the reverb tail, and reduce harsh
+     * echoes. The first tap occurs immediately, while the remaining taps are
+     * delayed by multiples of a fraction of the smallest cyclical delay time.
+     *
+     * offset[index] = (FRACTION (MULTIPLIER^(index-1))) smallest_delay
+     *
+     * for index = 1...max_lines
+     */
+    State->EarlyDelayTap[0] = fastf2u(earlyDelay * frequency);
+    for(i = 1;i < 4;i++)
+    {
+        length = (DECO_FRACTION * powf(DECO_MULTIPLIER, (ALfloat)i-1.0f)) *
+                 EARLY_LINE_LENGTH[0];
+        State->EarlyDelayTap[i] = fastf2u(length * frequency) + State->EarlyDelayTap[0];
+    }
+
+    State->LateDelayTap[0] = fastf2u((earlyDelay + lateDelay) * frequency);
+    for(i = 1;i < 4;i++)
+    {
+        length = (DECO_FRACTION * powf(DECO_MULTIPLIER, (ALfloat)i-1.0f)) *
+                 LATE_LINE_LENGTH[0] * (1.0f + (density * LATE_LINE_MULTIPLIER));
+        State->LateDelayTap[i] = fastf2u(length * frequency) + State->LateDelayTap[0];
+    }
+}
+
+// Update the early reflections mix and line coefficients.
+static ALvoid UpdateEarlyLines(ALfloat lateDelay, ALreverbState *State)
+{
+    ALuint index;
+
+    // Calculate the gain (coefficient) for each early delay line using the
+    // late delay time.  This expands the early reflections to the start of
+    // the late reverb.
+    for(index = 0;index < 4;index++)
+        State->Early.Coeff[index] = CalcDecayCoeff(EARLY_LINE_LENGTH[index],
+                                                   lateDelay);
+}
+
+// Update the late reverb mix, line lengths, and line coefficients.
+static ALvoid UpdateLateLines(ALfloat xMix, ALfloat density, ALfloat decayTime, ALfloat diffusion, ALfloat echoDepth, ALfloat hfRatio, ALfloat cw, ALuint frequency, ALreverbState *State)
+{
+    ALfloat length;
+    ALuint index;
+
+    /* Calculate the late reverb gain. Since the output is tapped prior to the
+     * application of the next delay line coefficients, this gain needs to be
+     * attenuated by the 'x' mixing matrix coefficient as well.  Also attenuate
+     * the late reverb when echo depth is high and diffusion is low, so the
+     * echo is slightly stronger than the decorrelated echos in the reverb
+     * tail.
+     */
+    State->Late.Gain = xMix * (1.0f - (echoDepth*0.5f*(1.0f - diffusion)));
+
+    /* To compensate for changes in modal density and decay time of the late
+     * reverb signal, the input is attenuated based on the maximal energy of
+     * the outgoing signal.  This approximation is used to keep the apparent
+     * energy of the signal equal for all ranges of density and decay time.
+     *
+     * The average length of the cyclcical delay lines is used to calculate
+     * the attenuation coefficient.
+     */
+    length = (LATE_LINE_LENGTH[0] + LATE_LINE_LENGTH[1] +
+              LATE_LINE_LENGTH[2] + LATE_LINE_LENGTH[3]) / 4.0f;
+    length *= 1.0f + (density * LATE_LINE_MULTIPLIER);
+    /* To account for each channel being a discrete input, also multiply by
+     * sqrt(num_channels).
+     */
+    State->Late.DensityGain = 2.0f * CalcDensityGain(
+        CalcDecayCoeff(length, decayTime)
+    );
+
+    // Calculate the all-pass feed-back and feed-forward coefficient.
+    State->Late.ApFeedCoeff = 0.5f * powf(diffusion, 2.0f);
+
+    for(index = 0;index < 4;index++)
+    {
+        // Calculate the gain (coefficient) for each all-pass line.
+        State->Late.Ap[index].Coeff = CalcDecayCoeff(
+            ALLPASS_LINE_LENGTH[index], decayTime
+        );
+
+        // Calculate the length (in seconds) of each cyclical delay line.
+        length = LATE_LINE_LENGTH[index] *
+                 (1.0f + (density * LATE_LINE_MULTIPLIER));
+
+        // Calculate the delay offset for each cyclical delay line.
+        State->Late.Offset[index] = fastf2u(length * frequency);
+
+        // Calculate the gain (coefficient) for each cyclical line.
+        State->Late.Coeff[index] = CalcDecayCoeff(length, decayTime);
+
+        // Calculate the damping coefficient for each low-pass filter.
+        State->Late.Lp[index].Coeff = CalcDampingCoeff(
+            hfRatio, length, decayTime, State->Late.Coeff[index], cw
+        );
+
+        // Attenuate the cyclical line coefficients by the mixing coefficient
+        // (x).
+        State->Late.Coeff[index] *= xMix;
+    }
+}
+
+// Update the echo gain, line offset, line coefficients, and mixing
+// coefficients.
+static ALvoid UpdateEchoLine(ALfloat echoTime, ALfloat decayTime, ALfloat diffusion, ALfloat echoDepth, ALfloat hfRatio, ALfloat cw, ALuint frequency, ALreverbState *State)
+{
+    // Update the offset and coefficient for the echo delay line.
+    State->Echo.Offset = fastf2u(echoTime * frequency);
+
+    // Calculate the decay coefficient for the echo line.
+    State->Echo.Coeff = CalcDecayCoeff(echoTime, decayTime);
+
+    // Calculate the energy-based attenuation coefficient for the echo delay
+    // line.
+    State->Echo.DensityGain = CalcDensityGain(State->Echo.Coeff);
+
+    // Calculate the echo all-pass feed coefficient.
+    State->Echo.ApFeedCoeff = 0.5f * powf(diffusion, 2.0f);
+
+    // Calculate the echo all-pass attenuation coefficient.
+    State->Echo.ApCoeff = CalcDecayCoeff(ECHO_ALLPASS_LENGTH, decayTime);
+
+    // Calculate the damping coefficient for each low-pass filter.
+    State->Echo.LpCoeff = CalcDampingCoeff(hfRatio, echoTime, decayTime,
+                                           State->Echo.Coeff, cw);
+
+    /* Calculate the echo mixing coefficient. This is applied to the output mix
+     * only, not the feedback.
+     */
+    State->Echo.MixCoeff = echoDepth;
+}
+
+/* Creates a transform matrix given a reverb vector. This works by creating a
+ * Z-focus transform, then a rotate transform around X, then Y, to place the
+ * focal point in the direction of the vector, using the vector length as a
+ * focus strength.
+ *
+ * This isn't technically correct since the vector is supposed to define the
+ * aperture and not rotate the perceived soundfield, but in practice it's
+ * probably good enough.
+ */
+static aluMatrixf GetTransformFromVector(const ALfloat *vec)
+{
+    aluMatrixf zfocus, xrot, yrot;
+    aluMatrixf tmp1, tmp2;
+    ALfloat length;
+    ALfloat sa, a;
+
+    length = sqrtf(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]);
+
+    /* Define a Z-focus (X in Ambisonics) transform, given the panning vector
+     * length.
+     */
+    sa = sinf(minf(length, 1.0f) * (F_PI/4.0f));
+    aluMatrixfSet(&zfocus,
+                     1.0f/(1.0f+sa),                       0.0f,                       0.0f, (sa/(1.0f+sa))/1.732050808f,
+                               0.0f, sqrtf((1.0f-sa)/(1.0f+sa)),                       0.0f,                        0.0f,
+                               0.0f,                       0.0f, sqrtf((1.0f-sa)/(1.0f+sa)),                        0.0f,
+        (sa/(1.0f+sa))*1.732050808f,                       0.0f,                       0.0f,              1.0f/(1.0f+sa)
+    );
+
+    /* Define rotation around X (Y in Ambisonics) */
+    a = atan2f(vec[1], sqrtf(vec[0]*vec[0] + vec[2]*vec[2]));
+    aluMatrixfSet(&xrot,
+        1.0f, 0.0f,     0.0f,    0.0f,
+        0.0f, 1.0f,     0.0f,    0.0f,
+        0.0f, 0.0f,  cosf(a), sinf(a),
+        0.0f, 0.0f, -sinf(a), cosf(a)
+    );
+
+    /* Define rotation around Y (Z in Ambisonics). NOTE: EFX's reverb vectors
+     * use a right-handled coordinate system, compared to the rest of OpenAL
+     * which uses left-handed. This is fixed by negating Z, however it would
+     * need to also be negated to get a proper Ambisonics angle, thus
+     * cancelling it out.
+     */
+    a = atan2f(-vec[0], vec[2]);
+    aluMatrixfSet(&yrot,
+        1.0f,     0.0f, 0.0f,    0.0f,
+        0.0f,  cosf(a), 0.0f, sinf(a),
+        0.0f,     0.0f, 1.0f,    0.0f,
+        0.0f, -sinf(a), 0.0f, cosf(a)
+    );
+
+#define MATRIX_MULT(_res, _m1, _m2) do {                                      \
+    int row, col;                                                             \
+    for(col = 0;col < 4;col++)                                                \
+    {                                                                         \
+        for(row = 0;row < 4;row++)                                            \
+            _res.m[row][col] = _m1.m[row][0]*_m2.m[0][col] + _m1.m[row][1]*_m2.m[1][col] + \
+                               _m1.m[row][2]*_m2.m[2][col] + _m1.m[row][3]*_m2.m[3][col];  \
+    }                                                                         \
+} while(0)
+    /* Define a matrix that first focuses on Z, then rotates around X then Y to
+     * focus the output in the direction of the vector.
+     */
+    MATRIX_MULT(tmp1, xrot, zfocus);
+    MATRIX_MULT(tmp2, yrot, tmp1);
+#undef MATRIX_MULT
+
+    return tmp2;
+}
+
+// Update the early and late 3D panning gains.
+static ALvoid Update3DPanning(const ALCdevice *Device, const ALfloat *ReflectionsPan, const ALfloat *LateReverbPan, ALfloat Gain, ALfloat EarlyGain, ALfloat LateGain, ALreverbState *State)
+{
+    /* Converts early reflections A-Format to B-Format (transposed). */
+    static const aluMatrixf EarlyA2B = {{
+        { 0.8660254038f,  0.8660254038f,  0.8660254038f,  0.8660254038f },
+        { 0.8660254038f,  0.8660254038f, -0.8660254038f, -0.8660254038f },
+        { 0.8660254038f, -0.8660254038f,  0.8660254038f, -0.8660254038f },
+        { 0.8660254038f, -0.8660254038f, -0.8660254038f,  0.8660254038f }
+    }};
+    /* Converts late reverb A-Format to B-Format (transposed). */
+    static const aluMatrixf LateA2B = {{
+        { 0.8660254038f, -0.8660254038f,  0.8660254038f,  0.8660254038f },
+        { 0.8660254038f, -0.8660254038f, -0.8660254038f, -0.8660254038f },
+        { 0.8660254038f,  0.8660254038f,  0.8660254038f, -0.8660254038f },
+        { 0.8660254038f,  0.8660254038f, -0.8660254038f,  0.8660254038f }
+/*        { 0.8660254038f,  1.2247448714f,           0.0f,  0.8660254038f },
+        { 0.8660254038f,           0.0f, -1.2247448714f, -0.8660254038f },
+        { 0.8660254038f,           0.0f,  1.2247448714f, -0.8660254038f },
+        { 0.8660254038f, -1.2247448714f,           0.0f,  0.8660254038f }*/
+    }};
+    aluMatrixf transform, rot;
+    ALuint i;
+
+    STATIC_CAST(ALeffectState,State)->OutBuffer = Device->FOAOut.Buffer;
+    STATIC_CAST(ALeffectState,State)->OutChannels = Device->FOAOut.NumChannels;
+
+    /* Note: Both _m2 and _res are transposed. */
+#define MATRIX_MULT(_res, _m1, _m2) do {                                      \
+    int row, col;                                                             \
+    for(col = 0;col < 4;col++)                                                \
+    {                                                                         \
+        for(row = 0;row < 4;row++)                                            \
+            _res.m[col][row] = _m1.m[row][0]*_m2.m[col][0] + _m1.m[row][1]*_m2.m[col][1] + \
+                               _m1.m[row][2]*_m2.m[col][2] + _m1.m[row][3]*_m2.m[col][3];  \
+    }                                                                         \
+} while(0)
+    /* Create a matrix that first converts A-Format to B-Format, then rotates
+     * the B-Format soundfield according to the panning vector.
+     */
+    rot = GetTransformFromVector(ReflectionsPan);
+    MATRIX_MULT(transform, rot, EarlyA2B);
+    memset(&State->Early.PanGain, 0, sizeof(State->Early.PanGain));
+    for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
+        ComputeFirstOrderGains(Device->FOAOut, transform.m[i], Gain*EarlyGain, State->Early.PanGain[i]);
+
+    rot = GetTransformFromVector(LateReverbPan);
+    MATRIX_MULT(transform, rot, LateA2B);
+    memset(&State->Late.PanGain, 0, sizeof(State->Late.PanGain));
+    for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
+        ComputeFirstOrderGains(Device->FOAOut, transform.m[i], Gain*LateGain, State->Late.PanGain[i]);
+#undef MATRIX_MULT
+}
+
+static ALvoid ALreverbState_update(ALreverbState *State, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props)
+{
+    ALuint frequency = Device->Frequency;
+    ALfloat lfscale, hfscale, hfRatio;
+    ALfloat gain, gainlf, gainhf;
+    ALfloat cw, x, y;
+    ALuint i;
+
+    if(Slot->Params.EffectType == AL_EFFECT_EAXREVERB && !EmulateEAXReverb)
+        State->IsEax = AL_TRUE;
+    else if(Slot->Params.EffectType == AL_EFFECT_REVERB || EmulateEAXReverb)
+        State->IsEax = AL_FALSE;
+
+    // Calculate the master filters
+    hfscale = props->Reverb.HFReference / frequency;
+    gainhf = maxf(props->Reverb.GainHF, 0.0001f);
+    ALfilterState_setParams(&State->Filter[0].Lp, ALfilterType_HighShelf,
+                            gainhf, hfscale, calc_rcpQ_from_slope(gainhf, 0.75f));
+    lfscale = props->Reverb.LFReference / frequency;
+    gainlf = maxf(props->Reverb.GainLF, 0.0001f);
+    ALfilterState_setParams(&State->Filter[0].Hp, ALfilterType_LowShelf,
+                            gainlf, lfscale, calc_rcpQ_from_slope(gainlf, 0.75f));
+    for(i = 1;i < 4;i++)
+    {
+        State->Filter[i].Lp.a1 = State->Filter[0].Lp.a1;
+        State->Filter[i].Lp.a2 = State->Filter[0].Lp.a2;
+        State->Filter[i].Lp.b0 = State->Filter[0].Lp.b0;
+        State->Filter[i].Lp.b1 = State->Filter[0].Lp.b1;
+        State->Filter[i].Lp.b2 = State->Filter[0].Lp.b2;
+
+        State->Filter[i].Hp.a1 = State->Filter[0].Hp.a1;
+        State->Filter[i].Hp.a2 = State->Filter[0].Hp.a2;
+        State->Filter[i].Hp.b0 = State->Filter[0].Hp.b0;
+        State->Filter[i].Hp.b1 = State->Filter[0].Hp.b1;
+        State->Filter[i].Hp.b2 = State->Filter[0].Hp.b2;
+    }
+
+    // Update the modulator line.
+    UpdateModulator(props->Reverb.ModulationTime, props->Reverb.ModulationDepth,
+                    frequency, State);
+
+    // Update the main effect delay.
+    UpdateDelayLine(props->Reverb.ReflectionsDelay, props->Reverb.LateReverbDelay,
+                    props->Reverb.Density, frequency, State);
+
+    // Update the early lines.
+    UpdateEarlyLines(props->Reverb.LateReverbDelay, State);
+
+    // Get the mixing matrix coefficients (x and y).
+    CalcMatrixCoeffs(props->Reverb.Diffusion, &x, &y);
+    // Then divide x into y to simplify the matrix calculation.
+    State->Late.MixCoeff = y / x;
+
+    // If the HF limit parameter is flagged, calculate an appropriate limit
+    // based on the air absorption parameter.
+    hfRatio = props->Reverb.DecayHFRatio;
+    if(props->Reverb.DecayHFLimit && props->Reverb.AirAbsorptionGainHF < 1.0f)
+        hfRatio = CalcLimitedHfRatio(hfRatio, props->Reverb.AirAbsorptionGainHF,
+                                     props->Reverb.DecayTime);
+
+    cw = cosf(F_TAU * hfscale);
+    // Update the late lines.
+    UpdateLateLines(x, props->Reverb.Density, props->Reverb.DecayTime,
+                    props->Reverb.Diffusion, props->Reverb.EchoDepth,
+                    hfRatio, cw, frequency, State);
+
+    // Update the echo line.
+    UpdateEchoLine(props->Reverb.EchoTime, props->Reverb.DecayTime,
+                   props->Reverb.Diffusion, props->Reverb.EchoDepth,
+                   hfRatio, cw, frequency, State);
+
+    gain = props->Reverb.Gain * Slot->Params.Gain * ReverbBoost;
+    // Update early and late 3D panning.
+    Update3DPanning(Device, props->Reverb.ReflectionsPan,
+                    props->Reverb.LateReverbPan, gain,
+                    props->Reverb.ReflectionsGain,
+                    props->Reverb.LateReverbGain, State);
+}
+
+
+/**************************************
+ *  Effect Processing                 *
+ **************************************/
+
+// Basic delay line input/output routines.
+static inline ALfloat DelayLineOut(DelayLine *Delay, ALuint offset)
+{
+    return Delay->Line[offset&Delay->Mask];
+}
+
+static inline ALvoid DelayLineIn(DelayLine *Delay, ALuint offset, ALfloat in)
+{
+    Delay->Line[offset&Delay->Mask] = in;
+}
+
+static inline ALfloat DelayLineInOut(DelayLine *Delay, ALuint offset, ALuint outoffset, ALfloat in)
+{
+    Delay->Line[offset&Delay->Mask] = in;
+    return Delay->Line[(offset-outoffset)&Delay->Mask];
+}
+
+static void CalcModulationDelays(ALreverbState *State, ALfloat *restrict delays, ALuint todo)
+{
+    ALfloat sinus, range;
+    ALuint index, i;
+
+    index = State->Mod.Index;
+    range = State->Mod.Filter;
+    for(i = 0;i < todo;i++)
+    {
+        /* Calculate the sinus rythm (dependent on modulation time and the
+         * sampling rate).  The center of the sinus is moved to reduce the
+         * delay of the effect when the time or depth are low.
+         */
+        sinus = 1.0f - cosf(F_TAU * index / State->Mod.Range);
+
+        /* Step the modulation index forward, keeping it bound to its range. */
+        index = (index+1) % State->Mod.Range;
+
+        /* The depth determines the range over which to read the input samples
+         * from, so it must be filtered to reduce the distortion caused by even
+         * small parameter changes.
+         */
+        range = lerp(range, State->Mod.Depth, State->Mod.Coeff);
+
+        /* Calculate the read offset with fraction. */
+        delays[i] = range*sinus;
+    }
+    State->Mod.Index = index;
+    State->Mod.Filter = range;
+}
+
+// Given some input samples, this function produces modulation for the late
+// reverb.
+static void EAXModulation(DelayLine *ModDelay, ALuint offset, const ALfloat *restrict delays, ALfloat*restrict dst, const ALfloat*restrict src, ALuint todo)
+{
+    ALfloat frac, fdelay;
+    ALfloat out0, out1;
+    ALuint delay, i;
+
+    for(i = 0;i < todo;i++)
+    {
+        /* Separate the integer offset and fraction between it and the next
+         * sample.
+         */
+        frac = modff(delays[i], &fdelay);
+        delay = fastf2u(fdelay);
+
+        /* Add the incoming sample to the delay line, and get the two samples
+         * crossed by the offset delay.
+         */
+        out0 = DelayLineInOut(ModDelay, offset, delay, src[i]);
+        out1 = DelayLineOut(ModDelay, offset - delay - 1);
+        offset++;
+
+        /* The output is obtained by linearly interpolating the two samples
+         * that were acquired above.
+         */
+        dst[i] = lerp(out0, out1, frac);
+    }
+}
+
+/* Given some input samples from the main delay line, this function produces
+ * four-channel outputs for the early reflections.
+ */
+static ALvoid EarlyReflection(ALreverbState *State, ALuint todo, ALfloat (*restrict out)[MAX_UPDATE_SAMPLES])
+{
+    ALfloat d[4], v, f[4];
+    ALuint i;
+
+    for(i = 0;i < todo;i++)
+    {
+        ALuint offset = State->Offset+i;
+
+        /* Obtain the first reflection samples from the main delay line. */
+        f[0] = DelayLineOut(&State->Delay, (offset-State->EarlyDelayTap[0])*4 + 0);
+        f[1] = DelayLineOut(&State->Delay, (offset-State->EarlyDelayTap[1])*4 + 1);
+        f[2] = DelayLineOut(&State->Delay, (offset-State->EarlyDelayTap[2])*4 + 2);
+        f[3] = DelayLineOut(&State->Delay, (offset-State->EarlyDelayTap[3])*4 + 3);
+
+        /* The following uses a lossless scattering junction from waveguide
+         * theory.  It actually amounts to a householder mixing matrix, which
+         * will produce a maximally diffuse response, and means this can
+         * probably be considered a simple feed-back delay network (FDN).
+         *          N
+         *         ---
+         *         \
+         * v = 2/N /   d_i
+         *         ---
+         *         i=1
+         */
+        v = (f[0] + f[1] + f[2] + f[3]) * 0.5f;
+
+        /* Calculate the feed values for the early delay lines. */
+        d[0] = v - f[0];
+        d[1] = v - f[1];
+        d[2] = v - f[2];
+        d[3] = v - f[3];
+
+        /* Feed the early delay lines, and load the delayed results. */
+        d[0] = DelayLineInOut(&State->Early.Delay[0], offset, State->Early.Offset[0], d[0]);
+        d[1] = DelayLineInOut(&State->Early.Delay[1], offset, State->Early.Offset[1], d[1]);
+        d[2] = DelayLineInOut(&State->Early.Delay[2], offset, State->Early.Offset[2], d[2]);
+        d[3] = DelayLineInOut(&State->Early.Delay[3], offset, State->Early.Offset[3], d[3]);
+
+        /* Output the initial reflection taps and the results of the delayed
+         * and decayed junction for all four channels.
+         */
+        out[0][i] = f[0] + d[0]*State->Early.Coeff[0];
+        out[1][i] = f[1] + d[1]*State->Early.Coeff[1];
+        out[2][i] = f[2] + d[2]*State->Early.Coeff[2];
+        out[3][i] = f[3] + d[3]*State->Early.Coeff[3];
+    }
+}
+
+// Basic attenuated all-pass input/output routine.
+static inline ALfloat AllpassInOut(DelayLine *Delay, ALuint outOffset, ALuint inOffset, ALfloat in, ALfloat feedCoeff, ALfloat coeff)
+{
+    ALfloat out, feed;
+
+    out = DelayLineOut(Delay, outOffset);
+    feed = feedCoeff * in;
+    DelayLineIn(Delay, inOffset, (feedCoeff * (out - feed)) + in);
+
+    // The time-based attenuation is only applied to the delay output to
+    // keep it from affecting the feed-back path (which is already controlled
+    // by the all-pass feed coefficient).
+    return (coeff * out) - feed;
+}
+
+// All-pass input/output routine for late reverb.
+static inline ALfloat LateAllPassInOut(ALreverbState *State, ALuint offset, ALuint index, ALfloat in)
+{
+    return AllpassInOut(&State->Late.Ap[index].Delay,
+                        offset - State->Late.Ap[index].Offset,
+                        offset, in, State->Late.ApFeedCoeff,
+                        State->Late.Ap[index].Coeff);
+}
+
+// Low-pass filter input/output routine for late reverb.
+static inline ALfloat LateLowPassInOut(ALreverbState *State, ALuint index, ALfloat in)
+{
+    in = lerp(in, State->Late.Lp[index].Sample, State->Late.Lp[index].Coeff);
+    State->Late.Lp[index].Sample = in;
+    return in;
+}
+
+// Given four decorrelated input samples, this function produces four-channel
+// output for the late reverb.
+static ALvoid LateReverb(ALreverbState *State, ALuint todo, ALfloat (*restrict out)[MAX_UPDATE_SAMPLES])
+{
+    ALfloat d[4], f[4];
+    ALuint offset;
+    ALuint base, i;
+
+    offset = State->Offset;
+    for(base = 0;base < todo;)
+    {
+        ALfloat tmp[MAX_UPDATE_SAMPLES/4][4];
+        ALuint tmp_todo = minu(todo, MAX_UPDATE_SAMPLES/4);
+
+        for(i = 0;i < tmp_todo;i++)
+        {
+            /* Obtain four decorrelated input samples. */
+            f[0] = DelayLineOut(&State->Delay, (offset-State->LateDelayTap[0])*4 + 0) * State->Late.DensityGain;
+            f[1] = DelayLineOut(&State->Delay, (offset-State->LateDelayTap[1])*4 + 1) * State->Late.DensityGain;
+            f[2] = DelayLineOut(&State->Delay, (offset-State->LateDelayTap[2])*4 + 2) * State->Late.DensityGain;
+            f[3] = DelayLineOut(&State->Delay, (offset-State->LateDelayTap[3])*4 + 3) * State->Late.DensityGain;
+
+            /* Add the decayed results of the cyclical delay lines, then pass
+             * the results through the low-pass filters.
+             */
+            f[0] += DelayLineOut(&State->Late.Delay[0], offset-State->Late.Offset[0]) * State->Late.Coeff[0];
+            f[1] += DelayLineOut(&State->Late.Delay[1], offset-State->Late.Offset[1]) * State->Late.Coeff[1];
+            f[2] += DelayLineOut(&State->Late.Delay[2], offset-State->Late.Offset[2]) * State->Late.Coeff[2];
+            f[3] += DelayLineOut(&State->Late.Delay[3], offset-State->Late.Offset[3]) * State->Late.Coeff[3];
+
+            /* This is where the feed-back cycles from line 0 to 3 to 1 to 2
+             * and back to 0.
+             */
+            d[0] = LateLowPassInOut(State, 2, f[2]);
+            d[1] = LateLowPassInOut(State, 3, f[3]);
+            d[2] = LateLowPassInOut(State, 1, f[1]);
+            d[3] = LateLowPassInOut(State, 0, f[0]);
+
+            /* To help increase diffusion, run each line through an all-pass
+             * filter. When there is no diffusion, the shortest all-pass filter
+             * will feed the shortest delay line.
+             */
+            d[0] = LateAllPassInOut(State, offset, 0, d[0]);
+            d[1] = LateAllPassInOut(State, offset, 1, d[1]);
+            d[2] = LateAllPassInOut(State, offset, 2, d[2]);
+            d[3] = LateAllPassInOut(State, offset, 3, d[3]);
+
+            /* Late reverb is done with a modified feed-back delay network (FDN)
+             * topology.  Four input lines are each fed through their own all-pass
+             * filter and then into the mixing matrix.  The four outputs of the
+             * mixing matrix are then cycled back to the inputs.  Each output feeds
+             * a different input to form a circlular feed cycle.
+             *
+             * The mixing matrix used is a 4D skew-symmetric rotation matrix
+             * derived using a single unitary rotational parameter:
+             *
+             *  [  d,  a,  b,  c ]          1 = a^2 + b^2 + c^2 + d^2
+             *  [ -a,  d,  c, -b ]
+             *  [ -b, -c,  d,  a ]
+             *  [ -c,  b, -a,  d ]
+             *
+             * The rotation is constructed from the effect's diffusion parameter,
+             * yielding: 1 = x^2 + 3 y^2; where a, b, and c are the coefficient y
+             * with differing signs, and d is the coefficient x.  The matrix is
+             * thus:
+             *
+             *  [  x,  y, -y,  y ]          n = sqrt(matrix_order - 1)
+             *  [ -y,  x,  y,  y ]          t = diffusion_parameter * atan(n)
+             *  [  y, -y,  x,  y ]          x = cos(t)
+             *  [ -y, -y, -y,  x ]          y = sin(t) / n
+             *
+             * To reduce the number of multiplies, the x coefficient is applied
+             * with the cyclical delay line coefficients. Thus only the y
+             * coefficient is applied when mixing, and is modified to be: y / x.
+             */
+            f[0] = d[0] + (State->Late.MixCoeff * (         d[1] + -d[2] + d[3]));
+            f[1] = d[1] + (State->Late.MixCoeff * (-d[0]         +  d[2] + d[3]));
+            f[2] = d[2] + (State->Late.MixCoeff * ( d[0] + -d[1]         + d[3]));
+            f[3] = d[3] + (State->Late.MixCoeff * (-d[0] + -d[1] + -d[2]       ));
+
+            /* Re-feed the cyclical delay lines. */
+            DelayLineIn(&State->Late.Delay[0], offset, f[0]);
+            DelayLineIn(&State->Late.Delay[1], offset, f[1]);
+            DelayLineIn(&State->Late.Delay[2], offset, f[2]);
+            DelayLineIn(&State->Late.Delay[3], offset, f[3]);
+            offset++;
+
+            /* Output the results of the matrix for all four channels,
+             * attenuated by the late reverb gain (which is attenuated by the
+             * 'x' mix coefficient).
+             */
+            tmp[i][0] = State->Late.Gain * f[0];
+            tmp[i][1] = State->Late.Gain * f[1];
+            tmp[i][2] = State->Late.Gain * f[2];
+            tmp[i][3] = State->Late.Gain * f[3];
+        }
+
+        /* Deinterlace to output */
+        for(i = 0;i < tmp_todo;i++) out[0][base+i] = tmp[i][0];
+        for(i = 0;i < tmp_todo;i++) out[1][base+i] = tmp[i][1];
+        for(i = 0;i < tmp_todo;i++) out[2][base+i] = tmp[i][2];
+        for(i = 0;i < tmp_todo;i++) out[3][base+i] = tmp[i][3];
+
+        base += tmp_todo;
+    }
+}
+
+// Given an input sample, this function mixes echo into the four-channel late
+// reverb.
+static ALvoid EAXEcho(ALreverbState *State, ALuint todo, ALfloat (*restrict late)[MAX_UPDATE_SAMPLES])
+{
+    ALfloat feed;
+    ALuint offset;
+    ALuint c, i;
+
+    for(c = 0;c < 4;c++)
+    {
+        offset = State->Offset;
+        for(i = 0;i < todo;i++)
+        {
+            // Get the latest attenuated echo sample for output.
+            feed = DelayLineOut(&State->Echo.Delay[c].Feedback, offset-State->Echo.Offset) *
+                   State->Echo.Coeff;
+
+            // Write the output.
+            late[c][i] += State->Echo.MixCoeff * feed;
+
+            // Mix the energy-attenuated input with the output and pass it through
+            // the echo low-pass filter.
+            feed += DelayLineOut(&State->Delay, (offset-State->LateDelayTap[0])*4 + c) *
+                    State->Echo.DensityGain;
+            feed = lerp(feed, State->Echo.LpSample[c], State->Echo.LpCoeff);
+            State->Echo.LpSample[c] = feed;
+
+            // Then the echo all-pass filter.
+            feed = AllpassInOut(&State->Echo.Delay[c].Ap, offset-State->Echo.ApOffset,
+                                offset, feed, State->Echo.ApFeedCoeff,
+                                State->Echo.ApCoeff);
+
+            // Feed the delay with the mixed and filtered sample.
+            DelayLineIn(&State->Echo.Delay[c].Feedback, offset, feed);
+            offset++;
+        }
+    }
+}
+
+// Perform the non-EAX reverb pass on a given input sample, resulting in
+// four-channel output.
+static ALvoid VerbPass(ALreverbState *State, ALuint todo, ALfloat (*restrict input)[MAX_UPDATE_SAMPLES], ALfloat (*restrict early)[MAX_UPDATE_SAMPLES], ALfloat (*restrict late)[MAX_UPDATE_SAMPLES])
+{
+    ALuint i, c;
+
+    for(c = 0;c < 4;c++)
+    {
+        /* Low-pass filter the incoming samples (use the early buffer as temp
+         * storage).
+         */
+        ALfilterState_process(&State->Filter[c].Lp, &early[0][0], input[c], todo);
+        for(i = 0;i < todo;i++)
+            DelayLineIn(&State->Delay, (State->Offset+i)*4 + c, early[0][i]);
+    }
+
+    // Calculate the early reflection from the first delay tap.
+    EarlyReflection(State, todo, early);
+
+    // Calculate the late reverb from the decorrelator taps.
+    LateReverb(State, todo, late);
+
+    // Step all delays forward one sample.
+    State->Offset += todo;
+}
+
+// Perform the EAX reverb pass on a given input sample, resulting in four-
+// channel output.
+static ALvoid EAXVerbPass(ALreverbState *State, ALuint todo, ALfloat (*restrict input)[MAX_UPDATE_SAMPLES], ALfloat (*restrict early)[MAX_UPDATE_SAMPLES], ALfloat (*restrict late)[MAX_UPDATE_SAMPLES])
+{
+    ALuint i, c;
+
+    /* Perform any modulation on the input (use the early and late buffers as
+     * temp storage).
+     */
+    CalcModulationDelays(State, &late[0][0], todo);
+    for(c = 0;c < 4;c++)
+    {
+        EAXModulation(&State->Mod.Delay[c], State->Offset, &late[0][0],
+                      &early[0][0], input[c], todo);
+
+        /* Band-pass the incoming samples */
+        ALfilterState_process(&State->Filter[c].Lp, &early[1][0], &early[0][0], todo);
+        ALfilterState_process(&State->Filter[c].Hp, &early[2][0], &early[1][0], todo);
+
+        /* Feed the initial delay line. */
+        for(i = 0;i < todo;i++)
+            DelayLineIn(&State->Delay, (State->Offset+i)*4 + c, early[2][i]);
+    }
+
+    // Calculate the early reflection from the first delay tap.
+    EarlyReflection(State, todo, early);
+
+    // Calculate the late reverb from the decorrelator taps.
+    LateReverb(State, todo, late);
+
+    // Calculate and mix in any echo.
+    EAXEcho(State, todo, late);
+
+    // Step all delays forward.
+    State->Offset += todo;
+}
+
+
+static ALvoid ALreverbState_processStandard(ALreverbState *State, ALuint SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels)
+{
+    static const aluMatrixf B2A = {{
+        { 0.288675134595f,  0.288675134595f,  0.288675134595f,  0.288675134595f },
+        { 0.288675134595f,  0.288675134595f, -0.288675134595f, -0.288675134595f },
+        { 0.288675134595f, -0.288675134595f,  0.288675134595f, -0.288675134595f },
+        { 0.288675134595f, -0.288675134595f, -0.288675134595f,  0.288675134595f }
+    }};
+    ALfloat (*restrict afmt)[MAX_UPDATE_SAMPLES] = State->AFormatSamples;
+    ALfloat (*restrict early)[MAX_UPDATE_SAMPLES] = State->EarlySamples;
+    ALfloat (*restrict late)[MAX_UPDATE_SAMPLES] = State->ReverbSamples;
+    ALuint base, c;
+
+    /* Process reverb for these samples. */
+    for(base = 0;base < SamplesToDo;)
+    {
+        ALuint todo = minu(SamplesToDo-base, MAX_UPDATE_SAMPLES);
+
+        /* Convert B-Foramt to A-Format for processing. */
+        memset(afmt, 0, sizeof(*afmt)*4);
+        for(c = 0;c < 4;c++)
+            MixRowSamples(afmt[c], B2A.m[c],
+                SamplesIn, MAX_EFFECT_CHANNELS, base, todo
+            );
+
+        VerbPass(State, todo, afmt, early, late);
+
+        /* Mix the A-Format results to output, implicitly converting back to
+         * B-Format.
+         */
+        for(c = 0;c < 4;c++)
+            MixSamples(early[c], NumChannels, SamplesOut,
+                State->Early.CurrentGain[c], State->Early.PanGain[c],
+                SamplesToDo-base, base, todo
+            );
+        for(c = 0;c < 4;c++)
+            MixSamples(late[c], NumChannels, SamplesOut,
+                State->Late.CurrentGain[c], State->Late.PanGain[c],
+                SamplesToDo-base, base, todo
+            );
+
+        base += todo;
+    }
+}
+
+static ALvoid ALreverbState_processEax(ALreverbState *State, ALuint SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels)
+{
+    static const aluMatrixf B2A = {{
+        { 0.288675134595f,  0.288675134595f,  0.288675134595f,  0.288675134595f },
+        { 0.288675134595f,  0.288675134595f, -0.288675134595f, -0.288675134595f },
+        { 0.288675134595f, -0.288675134595f,  0.288675134595f, -0.288675134595f },
+        { 0.288675134595f, -0.288675134595f, -0.288675134595f,  0.288675134595f }
+    }};
+    ALfloat (*restrict afmt)[MAX_UPDATE_SAMPLES] = State->AFormatSamples;
+    ALfloat (*restrict early)[MAX_UPDATE_SAMPLES] = State->EarlySamples;
+    ALfloat (*restrict late)[MAX_UPDATE_SAMPLES] = State->ReverbSamples;
+    ALuint base, c;
+
+    /* Process reverb for these samples. */
+    for(base = 0;base < SamplesToDo;)
+    {
+        ALuint todo = minu(SamplesToDo-base, MAX_UPDATE_SAMPLES);
+
+        memset(afmt, 0, 4*MAX_UPDATE_SAMPLES*sizeof(float));
+        for(c = 0;c < 4;c++)
+            MixRowSamples(afmt[c], B2A.m[c],
+                SamplesIn, MAX_EFFECT_CHANNELS, base, todo
+            );
+
+        EAXVerbPass(State, todo, afmt, early, late);
+
+        for(c = 0;c < 4;c++)
+            MixSamples(early[c], NumChannels, SamplesOut,
+                State->Early.CurrentGain[c], State->Early.PanGain[c],
+                SamplesToDo-base, base, todo
+            );
+        for(c = 0;c < 4;c++)
+            MixSamples(late[c], NumChannels, SamplesOut,
+                State->Late.CurrentGain[c], State->Late.PanGain[c],
+                SamplesToDo-base, base, todo
+            );
+
+        base += todo;
+    }
+}
+
+static ALvoid ALreverbState_process(ALreverbState *State, ALuint SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels)
+{
+    if(State->IsEax)
+        ALreverbState_processEax(State, SamplesToDo, SamplesIn, SamplesOut, NumChannels);
+    else
+        ALreverbState_processStandard(State, SamplesToDo, SamplesIn, SamplesOut, NumChannels);
+}
+
+
+typedef struct ALreverbStateFactory {
+    DERIVE_FROM_TYPE(ALeffectStateFactory);
+} ALreverbStateFactory;
+
+static ALeffectState *ALreverbStateFactory_create(ALreverbStateFactory* UNUSED(factory))
+{
+    ALreverbState *state;
+
+    alcall_once(&mixfunc_inited, init_mixfunc);
+
+    NEW_OBJ0(state, ALreverbState)();
+    if(!state) return NULL;
+
+    return STATIC_CAST(ALeffectState, state);
+}
+
+DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALreverbStateFactory);
+
+ALeffectStateFactory *ALreverbStateFactory_getFactory(void)
+{
+    static ALreverbStateFactory ReverbFactory = { { GET_VTABLE2(ALreverbStateFactory, ALeffectStateFactory) } };
+
+    return STATIC_CAST(ALeffectStateFactory, &ReverbFactory);
+}
+
+
+void ALeaxreverb_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
+{
+    ALeffectProps *props = &effect->Props;
+    switch(param)
+    {
+        case AL_EAXREVERB_DECAY_HFLIMIT:
+            if(!(val >= AL_EAXREVERB_MIN_DECAY_HFLIMIT && val <= AL_EAXREVERB_MAX_DECAY_HFLIMIT))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Reverb.DecayHFLimit = val;
+            break;
+
+        default:
+            SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+    }
+}
+void ALeaxreverb_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
+{
+    ALeaxreverb_setParami(effect, context, param, vals[0]);
+}
+void ALeaxreverb_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
+{
+    ALeffectProps *props = &effect->Props;
+    switch(param)
+    {
+        case AL_EAXREVERB_DENSITY:
+            if(!(val >= AL_EAXREVERB_MIN_DENSITY && val <= AL_EAXREVERB_MAX_DENSITY))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Reverb.Density = val;
+            break;
+
+        case AL_EAXREVERB_DIFFUSION:
+            if(!(val >= AL_EAXREVERB_MIN_DIFFUSION && val <= AL_EAXREVERB_MAX_DIFFUSION))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Reverb.Diffusion = val;
+            break;
+
+        case AL_EAXREVERB_GAIN:
+            if(!(val >= AL_EAXREVERB_MIN_GAIN && val <= AL_EAXREVERB_MAX_GAIN))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Reverb.Gain = val;
+            break;
+
+        case AL_EAXREVERB_GAINHF:
+            if(!(val >= AL_EAXREVERB_MIN_GAINHF && val <= AL_EAXREVERB_MAX_GAINHF))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Reverb.GainHF = val;
+            break;
+
+        case AL_EAXREVERB_GAINLF:
+            if(!(val >= AL_EAXREVERB_MIN_GAINLF && val <= AL_EAXREVERB_MAX_GAINLF))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Reverb.GainLF = val;
+            break;
+
+        case AL_EAXREVERB_DECAY_TIME:
+            if(!(val >= AL_EAXREVERB_MIN_DECAY_TIME && val <= AL_EAXREVERB_MAX_DECAY_TIME))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Reverb.DecayTime = val;
+            break;
+
+        case AL_EAXREVERB_DECAY_HFRATIO:
+            if(!(val >= AL_EAXREVERB_MIN_DECAY_HFRATIO && val <= AL_EAXREVERB_MAX_DECAY_HFRATIO))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Reverb.DecayHFRatio = val;
+            break;
+
+        case AL_EAXREVERB_DECAY_LFRATIO:
+            if(!(val >= AL_EAXREVERB_MIN_DECAY_LFRATIO && val <= AL_EAXREVERB_MAX_DECAY_LFRATIO))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Reverb.DecayLFRatio = val;
+            break;
+
+        case AL_EAXREVERB_REFLECTIONS_GAIN:
+            if(!(val >= AL_EAXREVERB_MIN_REFLECTIONS_GAIN && val <= AL_EAXREVERB_MAX_REFLECTIONS_GAIN))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Reverb.ReflectionsGain = val;
+            break;
+
+        case AL_EAXREVERB_REFLECTIONS_DELAY:
+            if(!(val >= AL_EAXREVERB_MIN_REFLECTIONS_DELAY && val <= AL_EAXREVERB_MAX_REFLECTIONS_DELAY))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Reverb.ReflectionsDelay = val;
+            break;
+
+        case AL_EAXREVERB_LATE_REVERB_GAIN:
+            if(!(val >= AL_EAXREVERB_MIN_LATE_REVERB_GAIN && val <= AL_EAXREVERB_MAX_LATE_REVERB_GAIN))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Reverb.LateReverbGain = val;
+            break;
+
+        case AL_EAXREVERB_LATE_REVERB_DELAY:
+            if(!(val >= AL_EAXREVERB_MIN_LATE_REVERB_DELAY && val <= AL_EAXREVERB_MAX_LATE_REVERB_DELAY))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Reverb.LateReverbDelay = val;
+            break;
+
+        case AL_EAXREVERB_AIR_ABSORPTION_GAINHF:
+            if(!(val >= AL_EAXREVERB_MIN_AIR_ABSORPTION_GAINHF && val <= AL_EAXREVERB_MAX_AIR_ABSORPTION_GAINHF))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Reverb.AirAbsorptionGainHF = val;
+            break;
+
+        case AL_EAXREVERB_ECHO_TIME:
+            if(!(val >= AL_EAXREVERB_MIN_ECHO_TIME && val <= AL_EAXREVERB_MAX_ECHO_TIME))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Reverb.EchoTime = val;
+            break;
+
+        case AL_EAXREVERB_ECHO_DEPTH:
+            if(!(val >= AL_EAXREVERB_MIN_ECHO_DEPTH && val <= AL_EAXREVERB_MAX_ECHO_DEPTH))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Reverb.EchoDepth = val;
+            break;
+
+        case AL_EAXREVERB_MODULATION_TIME:
+            if(!(val >= AL_EAXREVERB_MIN_MODULATION_TIME && val <= AL_EAXREVERB_MAX_MODULATION_TIME))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Reverb.ModulationTime = val;
+            break;
+
+        case AL_EAXREVERB_MODULATION_DEPTH:
+            if(!(val >= AL_EAXREVERB_MIN_MODULATION_DEPTH && val <= AL_EAXREVERB_MAX_MODULATION_DEPTH))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Reverb.ModulationDepth = val;
+            break;
+
+        case AL_EAXREVERB_HFREFERENCE:
+            if(!(val >= AL_EAXREVERB_MIN_HFREFERENCE && val <= AL_EAXREVERB_MAX_HFREFERENCE))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Reverb.HFReference = val;
+            break;
+
+        case AL_EAXREVERB_LFREFERENCE:
+            if(!(val >= AL_EAXREVERB_MIN_LFREFERENCE && val <= AL_EAXREVERB_MAX_LFREFERENCE))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Reverb.LFReference = val;
+            break;
+
+        case AL_EAXREVERB_ROOM_ROLLOFF_FACTOR:
+            if(!(val >= AL_EAXREVERB_MIN_ROOM_ROLLOFF_FACTOR && val <= AL_EAXREVERB_MAX_ROOM_ROLLOFF_FACTOR))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Reverb.RoomRolloffFactor = val;
+            break;
+
+        default:
+            SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+    }
+}
+void ALeaxreverb_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
+{
+    ALeffectProps *props = &effect->Props;
+    switch(param)
+    {
+        case AL_EAXREVERB_REFLECTIONS_PAN:
+            if(!(isfinite(vals[0]) && isfinite(vals[1]) && isfinite(vals[2])))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Reverb.ReflectionsPan[0] = vals[0];
+            props->Reverb.ReflectionsPan[1] = vals[1];
+            props->Reverb.ReflectionsPan[2] = vals[2];
+            break;
+        case AL_EAXREVERB_LATE_REVERB_PAN:
+            if(!(isfinite(vals[0]) && isfinite(vals[1]) && isfinite(vals[2])))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Reverb.LateReverbPan[0] = vals[0];
+            props->Reverb.LateReverbPan[1] = vals[1];
+            props->Reverb.LateReverbPan[2] = vals[2];
+            break;
+
+        default:
+            ALeaxreverb_setParamf(effect, context, param, vals[0]);
+            break;
+    }
+}
+
+void ALeaxreverb_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
+{
+    const ALeffectProps *props = &effect->Props;
+    switch(param)
+    {
+        case AL_EAXREVERB_DECAY_HFLIMIT:
+            *val = props->Reverb.DecayHFLimit;
+            break;
+
+        default:
+            SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+    }
+}
+void ALeaxreverb_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
+{
+    ALeaxreverb_getParami(effect, context, param, vals);
+}
+void ALeaxreverb_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
+{
+    const ALeffectProps *props = &effect->Props;
+    switch(param)
+    {
+        case AL_EAXREVERB_DENSITY:
+            *val = props->Reverb.Density;
+            break;
+
+        case AL_EAXREVERB_DIFFUSION:
+            *val = props->Reverb.Diffusion;
+            break;
+
+        case AL_EAXREVERB_GAIN:
+            *val = props->Reverb.Gain;
+            break;
+
+        case AL_EAXREVERB_GAINHF:
+            *val = props->Reverb.GainHF;
+            break;
+
+        case AL_EAXREVERB_GAINLF:
+            *val = props->Reverb.GainLF;
+            break;
+
+        case AL_EAXREVERB_DECAY_TIME:
+            *val = props->Reverb.DecayTime;
+            break;
+
+        case AL_EAXREVERB_DECAY_HFRATIO:
+            *val = props->Reverb.DecayHFRatio;
+            break;
+
+        case AL_EAXREVERB_DECAY_LFRATIO:
+            *val = props->Reverb.DecayLFRatio;
+            break;
+
+        case AL_EAXREVERB_REFLECTIONS_GAIN:
+            *val = props->Reverb.ReflectionsGain;
+            break;
+
+        case AL_EAXREVERB_REFLECTIONS_DELAY:
+            *val = props->Reverb.ReflectionsDelay;
+            break;
+
+        case AL_EAXREVERB_LATE_REVERB_GAIN:
+            *val = props->Reverb.LateReverbGain;
+            break;
+
+        case AL_EAXREVERB_LATE_REVERB_DELAY:
+            *val = props->Reverb.LateReverbDelay;
+            break;
+
+        case AL_EAXREVERB_AIR_ABSORPTION_GAINHF:
+            *val = props->Reverb.AirAbsorptionGainHF;
+            break;
+
+        case AL_EAXREVERB_ECHO_TIME:
+            *val = props->Reverb.EchoTime;
+            break;
+
+        case AL_EAXREVERB_ECHO_DEPTH:
+            *val = props->Reverb.EchoDepth;
+            break;
+
+        case AL_EAXREVERB_MODULATION_TIME:
+            *val = props->Reverb.ModulationTime;
+            break;
+
+        case AL_EAXREVERB_MODULATION_DEPTH:
+            *val = props->Reverb.ModulationDepth;
+            break;
+
+        case AL_EAXREVERB_HFREFERENCE:
+            *val = props->Reverb.HFReference;
+            break;
+
+        case AL_EAXREVERB_LFREFERENCE:
+            *val = props->Reverb.LFReference;
+            break;
+
+        case AL_EAXREVERB_ROOM_ROLLOFF_FACTOR:
+            *val = props->Reverb.RoomRolloffFactor;
+            break;
+
+        default:
+            SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+    }
+}
+void ALeaxreverb_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
+{
+    const ALeffectProps *props = &effect->Props;
+    switch(param)
+    {
+        case AL_EAXREVERB_REFLECTIONS_PAN:
+            vals[0] = props->Reverb.ReflectionsPan[0];
+            vals[1] = props->Reverb.ReflectionsPan[1];
+            vals[2] = props->Reverb.ReflectionsPan[2];
+            break;
+        case AL_EAXREVERB_LATE_REVERB_PAN:
+            vals[0] = props->Reverb.LateReverbPan[0];
+            vals[1] = props->Reverb.LateReverbPan[1];
+            vals[2] = props->Reverb.LateReverbPan[2];
+            break;
+
+        default:
+            ALeaxreverb_getParamf(effect, context, param, vals);
+            break;
+    }
+}
+
+DEFINE_ALEFFECT_VTABLE(ALeaxreverb);
+
+void ALreverb_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
+{
+    ALeffectProps *props = &effect->Props;
+    switch(param)
+    {
+        case AL_REVERB_DECAY_HFLIMIT:
+            if(!(val >= AL_REVERB_MIN_DECAY_HFLIMIT && val <= AL_REVERB_MAX_DECAY_HFLIMIT))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Reverb.DecayHFLimit = val;
+            break;
+
+        default:
+            SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+    }
+}
+void ALreverb_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
+{
+    ALreverb_setParami(effect, context, param, vals[0]);
+}
+void ALreverb_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
+{
+    ALeffectProps *props = &effect->Props;
+    switch(param)
+    {
+        case AL_REVERB_DENSITY:
+            if(!(val >= AL_REVERB_MIN_DENSITY && val <= AL_REVERB_MAX_DENSITY))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Reverb.Density = val;
+            break;
+
+        case AL_REVERB_DIFFUSION:
+            if(!(val >= AL_REVERB_MIN_DIFFUSION && val <= AL_REVERB_MAX_DIFFUSION))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Reverb.Diffusion = val;
+            break;
+
+        case AL_REVERB_GAIN:
+            if(!(val >= AL_REVERB_MIN_GAIN && val <= AL_REVERB_MAX_GAIN))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Reverb.Gain = val;
+            break;
+
+        case AL_REVERB_GAINHF:
+            if(!(val >= AL_REVERB_MIN_GAINHF && val <= AL_REVERB_MAX_GAINHF))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Reverb.GainHF = val;
+            break;
+
+        case AL_REVERB_DECAY_TIME:
+            if(!(val >= AL_REVERB_MIN_DECAY_TIME && val <= AL_REVERB_MAX_DECAY_TIME))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Reverb.DecayTime = val;
+            break;
+
+        case AL_REVERB_DECAY_HFRATIO:
+            if(!(val >= AL_REVERB_MIN_DECAY_HFRATIO && val <= AL_REVERB_MAX_DECAY_HFRATIO))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Reverb.DecayHFRatio = val;
+            break;
+
+        case AL_REVERB_REFLECTIONS_GAIN:
+            if(!(val >= AL_REVERB_MIN_REFLECTIONS_GAIN && val <= AL_REVERB_MAX_REFLECTIONS_GAIN))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Reverb.ReflectionsGain = val;
+            break;
+
+        case AL_REVERB_REFLECTIONS_DELAY:
+            if(!(val >= AL_REVERB_MIN_REFLECTIONS_DELAY && val <= AL_REVERB_MAX_REFLECTIONS_DELAY))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Reverb.ReflectionsDelay = val;
+            break;
+
+        case AL_REVERB_LATE_REVERB_GAIN:
+            if(!(val >= AL_REVERB_MIN_LATE_REVERB_GAIN && val <= AL_REVERB_MAX_LATE_REVERB_GAIN))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Reverb.LateReverbGain = val;
+            break;
+
+        case AL_REVERB_LATE_REVERB_DELAY:
+            if(!(val >= AL_REVERB_MIN_LATE_REVERB_DELAY && val <= AL_REVERB_MAX_LATE_REVERB_DELAY))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Reverb.LateReverbDelay = val;
+            break;
+
+        case AL_REVERB_AIR_ABSORPTION_GAINHF:
+            if(!(val >= AL_REVERB_MIN_AIR_ABSORPTION_GAINHF && val <= AL_REVERB_MAX_AIR_ABSORPTION_GAINHF))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Reverb.AirAbsorptionGainHF = val;
+            break;
+
+        case AL_REVERB_ROOM_ROLLOFF_FACTOR:
+            if(!(val >= AL_REVERB_MIN_ROOM_ROLLOFF_FACTOR && val <= AL_REVERB_MAX_ROOM_ROLLOFF_FACTOR))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            props->Reverb.RoomRolloffFactor = val;
+            break;
+
+        default:
+            SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+    }
+}
+void ALreverb_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
+{
+    ALreverb_setParamf(effect, context, param, vals[0]);
+}
+
+void ALreverb_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
+{
+    const ALeffectProps *props = &effect->Props;
+    switch(param)
+    {
+        case AL_REVERB_DECAY_HFLIMIT:
+            *val = props->Reverb.DecayHFLimit;
+            break;
+
+        default:
+            SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+    }
+}
+void ALreverb_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
+{
+    ALreverb_getParami(effect, context, param, vals);
+}
+void ALreverb_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
+{
+    const ALeffectProps *props = &effect->Props;
+    switch(param)
+    {
+        case AL_REVERB_DENSITY:
+            *val = props->Reverb.Density;
+            break;
+
+        case AL_REVERB_DIFFUSION:
+            *val = props->Reverb.Diffusion;
+            break;
+
+        case AL_REVERB_GAIN:
+            *val = props->Reverb.Gain;
+            break;
+
+        case AL_REVERB_GAINHF:
+            *val = props->Reverb.GainHF;
+            break;
+
+        case AL_REVERB_DECAY_TIME:
+            *val = props->Reverb.DecayTime;
+            break;
+
+        case AL_REVERB_DECAY_HFRATIO:
+            *val = props->Reverb.DecayHFRatio;
+            break;
+
+        case AL_REVERB_REFLECTIONS_GAIN:
+            *val = props->Reverb.ReflectionsGain;
+            break;
+
+        case AL_REVERB_REFLECTIONS_DELAY:
+            *val = props->Reverb.ReflectionsDelay;
+            break;
+
+        case AL_REVERB_LATE_REVERB_GAIN:
+            *val = props->Reverb.LateReverbGain;
+            break;
+
+        case AL_REVERB_LATE_REVERB_DELAY:
+            *val = props->Reverb.LateReverbDelay;
+            break;
+
+        case AL_REVERB_AIR_ABSORPTION_GAINHF:
+            *val = props->Reverb.AirAbsorptionGainHF;
+            break;
+
+        case AL_REVERB_ROOM_ROLLOFF_FACTOR:
+            *val = props->Reverb.RoomRolloffFactor;
+            break;
+
+        default:
+            SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+    }
+}
+void ALreverb_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
+{
+    ALreverb_getParamf(effect, context, param, vals);
+}
+
+DEFINE_ALEFFECT_VTABLE(ALreverb);

+ 1127 - 0
Engine/lib/openal-soft/Alc/helpers.c

@@ -0,0 +1,1127 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 2011 by authors.
+ * This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the
+ *  Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#ifdef _WIN32
+#ifdef __MINGW32__
+#define _WIN32_IE 0x501
+#else
+#define _WIN32_IE 0x400
+#endif
+#endif
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <time.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <ctype.h>
+#ifdef HAVE_MALLOC_H
+#include <malloc.h>
+#endif
+#ifdef HAVE_DIRENT_H
+#include <dirent.h>
+#endif
+
+#ifndef AL_NO_UID_DEFS
+#if defined(HAVE_GUIDDEF_H) || defined(HAVE_INITGUID_H)
+#define INITGUID
+#include <windows.h>
+#ifdef HAVE_GUIDDEF_H
+#include <guiddef.h>
+#else
+#include <initguid.h>
+#endif
+
+DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM,        0x00000001, 0x0000, 0x0010, 0x80,0x00, 0x00,0xaa,0x00,0x38,0x9b,0x71);
+DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80,0x00, 0x00,0xaa,0x00,0x38,0x9b,0x71);
+
+DEFINE_GUID(IID_IDirectSoundNotify,   0xb0210783, 0x89cd, 0x11d0, 0xaf,0x08, 0x00,0xa0,0xc9,0x25,0xcd,0x16);
+
+DEFINE_GUID(CLSID_MMDeviceEnumerator, 0xbcde0395, 0xe52f, 0x467c, 0x8e,0x3d, 0xc4,0x57,0x92,0x91,0x69,0x2e);
+DEFINE_GUID(IID_IMMDeviceEnumerator,  0xa95664d2, 0x9614, 0x4f35, 0xa7,0x46, 0xde,0x8d,0xb6,0x36,0x17,0xe6);
+DEFINE_GUID(IID_IAudioClient,         0x1cb9ad4c, 0xdbfa, 0x4c32, 0xb1,0x78, 0xc2,0xf5,0x68,0xa7,0x03,0xb2);
+DEFINE_GUID(IID_IAudioRenderClient,   0xf294acfc, 0x3146, 0x4483, 0xa7,0xbf, 0xad,0xdc,0xa7,0xc2,0x60,0xe2);
+DEFINE_GUID(IID_IAudioCaptureClient,  0xc8adbd64, 0xe71e, 0x48a0, 0xa4,0xde, 0x18,0x5c,0x39,0x5c,0xd3,0x17);
+
+#ifdef HAVE_MMDEVAPI
+#include <wtypes.h>
+#include <devpropdef.h>
+#include <propkeydef.h>
+DEFINE_DEVPROPKEY(DEVPKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80,0x20, 0x67,0xd1,0x46,0xa8,0x50,0xe0, 14);
+DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_FormFactor, 0x1da5d803, 0xd492, 0x4edd, 0x8c,0x23, 0xe0,0xc0,0xff,0xee,0x7f,0x0e, 0);
+DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_GUID, 0x1da5d803, 0xd492, 0x4edd, 0x8c, 0x23,0xe0, 0xc0,0xff,0xee,0x7f,0x0e, 4 );
+#endif
+#endif
+#endif /* AL_NO_UID_DEFS */
+
+#ifdef HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+#ifdef HAVE_INTRIN_H
+#include <intrin.h>
+#endif
+#ifdef HAVE_CPUID_H
+#include <cpuid.h>
+#endif
+#ifdef HAVE_SYS_SYSCONF_H
+#include <sys/sysconf.h>
+#endif
+#ifdef HAVE_FLOAT_H
+#include <float.h>
+#endif
+#ifdef HAVE_IEEEFP_H
+#include <ieeefp.h>
+#endif
+
+#ifndef _WIN32
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <unistd.h>
+#elif defined(_WIN32_IE)
+#include <shlobj.h>
+#endif
+
+#include "alMain.h"
+#include "alu.h"
+#include "atomic.h"
+#include "uintmap.h"
+#include "vector.h"
+#include "alstring.h"
+#include "compat.h"
+#include "threads.h"
+
+
+extern inline ALuint NextPowerOf2(ALuint value);
+extern inline ALint fastf2i(ALfloat f);
+extern inline ALuint fastf2u(ALfloat f);
+
+
+ALuint CPUCapFlags = 0;
+
+
+void FillCPUCaps(ALuint capfilter)
+{
+    ALuint caps = 0;
+
+/* FIXME: We really should get this for all available CPUs in case different
+ * CPUs have different caps (is that possible on one machine?). */
+#if defined(HAVE_GCC_GET_CPUID) && (defined(__i386__) || defined(__x86_64__) || \
+                                    defined(_M_IX86) || defined(_M_X64))
+    union {
+        unsigned int regs[4];
+        char str[sizeof(unsigned int[4])];
+    } cpuinf[3];
+
+    if(!__get_cpuid(0, &cpuinf[0].regs[0], &cpuinf[0].regs[1], &cpuinf[0].regs[2], &cpuinf[0].regs[3]))
+        ERR("Failed to get CPUID\n");
+    else
+    {
+        unsigned int maxfunc = cpuinf[0].regs[0];
+        unsigned int maxextfunc = 0;
+
+        if(__get_cpuid(0x80000000, &cpuinf[0].regs[0], &cpuinf[0].regs[1], &cpuinf[0].regs[2], &cpuinf[0].regs[3]))
+            maxextfunc = cpuinf[0].regs[0];
+        TRACE("Detected max CPUID function: 0x%x (ext. 0x%x)\n", maxfunc, maxextfunc);
+
+        TRACE("Vendor ID: \"%.4s%.4s%.4s\"\n", cpuinf[0].str+4, cpuinf[0].str+12, cpuinf[0].str+8);
+        if(maxextfunc >= 0x80000004 &&
+           __get_cpuid(0x80000002, &cpuinf[0].regs[0], &cpuinf[0].regs[1], &cpuinf[0].regs[2], &cpuinf[0].regs[3]) &&
+           __get_cpuid(0x80000003, &cpuinf[1].regs[0], &cpuinf[1].regs[1], &cpuinf[1].regs[2], &cpuinf[1].regs[3]) &&
+           __get_cpuid(0x80000004, &cpuinf[2].regs[0], &cpuinf[2].regs[1], &cpuinf[2].regs[2], &cpuinf[2].regs[3]))
+            TRACE("Name: \"%.16s%.16s%.16s\"\n", cpuinf[0].str, cpuinf[1].str, cpuinf[2].str);
+
+        if(maxfunc >= 1 &&
+           __get_cpuid(1, &cpuinf[0].regs[0], &cpuinf[0].regs[1], &cpuinf[0].regs[2], &cpuinf[0].regs[3]))
+        {
+            if((cpuinf[0].regs[3]&(1<<25)))
+            {
+                caps |= CPU_CAP_SSE;
+                if((cpuinf[0].regs[3]&(1<<26)))
+                {
+                    caps |= CPU_CAP_SSE2;
+                    if((cpuinf[0].regs[2]&(1<<0)))
+                    {
+                        caps |= CPU_CAP_SSE3;
+                        if((cpuinf[0].regs[2]&(1<<19)))
+                            caps |= CPU_CAP_SSE4_1;
+                    }
+                }
+            }
+        }
+    }
+#elif defined(HAVE_CPUID_INTRINSIC) && (defined(__i386__) || defined(__x86_64__) || \
+                                        defined(_M_IX86) || defined(_M_X64))
+    union {
+        int regs[4];
+        char str[sizeof(int[4])];
+    } cpuinf[3];
+
+    (__cpuid)(cpuinf[0].regs, 0);
+    if(cpuinf[0].regs[0] == 0)
+        ERR("Failed to get CPUID\n");
+    else
+    {
+        unsigned int maxfunc = cpuinf[0].regs[0];
+        unsigned int maxextfunc;
+
+        (__cpuid)(cpuinf[0].regs, 0x80000000);
+        maxextfunc = cpuinf[0].regs[0];
+
+        TRACE("Detected max CPUID function: 0x%x (ext. 0x%x)\n", maxfunc, maxextfunc);
+
+        TRACE("Vendor ID: \"%.4s%.4s%.4s\"\n", cpuinf[0].str+4, cpuinf[0].str+12, cpuinf[0].str+8);
+        if(maxextfunc >= 0x80000004)
+        {
+            (__cpuid)(cpuinf[0].regs, 0x80000002);
+            (__cpuid)(cpuinf[1].regs, 0x80000003);
+            (__cpuid)(cpuinf[2].regs, 0x80000004);
+            TRACE("Name: \"%.16s%.16s%.16s\"\n", cpuinf[0].str, cpuinf[1].str, cpuinf[2].str);
+        }
+
+        if(maxfunc >= 1)
+        {
+            (__cpuid)(cpuinf[0].regs, 1);
+            if((cpuinf[0].regs[3]&(1<<25)))
+            {
+                caps |= CPU_CAP_SSE;
+                if((cpuinf[0].regs[3]&(1<<26)))
+                {
+                    caps |= CPU_CAP_SSE2;
+                    if((cpuinf[0].regs[2]&(1<<0)))
+                    {
+                        caps |= CPU_CAP_SSE3;
+                        if((cpuinf[0].regs[2]&(1<<19)))
+                            caps |= CPU_CAP_SSE4_1;
+                    }
+                }
+            }
+        }
+    }
+#else
+    /* Assume support for whatever's supported if we can't check for it */
+#if defined(HAVE_SSE4_1)
+#warning "Assuming SSE 4.1 run-time support!"
+    caps |= CPU_CAP_SSE | CPU_CAP_SSE2 | CPU_CAP_SSE3 | CPU_CAP_SSE4_1;
+#elif defined(HAVE_SSE3)
+#warning "Assuming SSE 3 run-time support!"
+    caps |= CPU_CAP_SSE | CPU_CAP_SSE2 | CPU_CAP_SSE3;
+#elif defined(HAVE_SSE2)
+#warning "Assuming SSE 2 run-time support!"
+    caps |= CPU_CAP_SSE | CPU_CAP_SSE2;
+#elif defined(HAVE_SSE)
+#warning "Assuming SSE run-time support!"
+    caps |= CPU_CAP_SSE;
+#endif
+#endif
+#ifdef HAVE_NEON
+    FILE *file = fopen("/proc/cpuinfo", "rt");
+    if(!file)
+        ERR("Failed to open /proc/cpuinfo, cannot check for NEON support\n");
+    else
+    {
+        char buf[256];
+        while(fgets(buf, sizeof(buf), file) != NULL)
+        {
+            char *str;
+
+            if(strncmp(buf, "Features\t:", 10) != 0)
+                continue;
+
+            TRACE("Got features string:%s\n", buf+10);
+
+            str = buf;
+            while((str=strstr(str, "neon")) != NULL)
+            {
+                if(isspace(*(str-1)) && (str[4] == 0 || isspace(str[4])))
+                {
+                    caps |= CPU_CAP_NEON;
+                    break;
+                }
+                str++;
+            }
+            break;
+        }
+
+        fclose(file);
+        file = NULL;
+    }
+#endif
+
+    TRACE("Extensions:%s%s%s%s%s%s\n",
+        ((capfilter&CPU_CAP_SSE)    ? ((caps&CPU_CAP_SSE)    ? " +SSE"    : " -SSE")    : ""),
+        ((capfilter&CPU_CAP_SSE2)   ? ((caps&CPU_CAP_SSE2)   ? " +SSE2"   : " -SSE2")   : ""),
+        ((capfilter&CPU_CAP_SSE3)   ? ((caps&CPU_CAP_SSE3)   ? " +SSE3"   : " -SSE3")   : ""),
+        ((capfilter&CPU_CAP_SSE4_1) ? ((caps&CPU_CAP_SSE4_1) ? " +SSE4.1" : " -SSE4.1") : ""),
+        ((capfilter&CPU_CAP_NEON)   ? ((caps&CPU_CAP_NEON)   ? " +Neon"   : " -Neon")   : ""),
+        ((!capfilter) ? " -none-" : "")
+    );
+    CPUCapFlags = caps & capfilter;
+}
+
+
+void SetMixerFPUMode(FPUCtl *ctl)
+{
+#ifdef HAVE_FENV_H
+    fegetenv(STATIC_CAST(fenv_t, ctl));
+#if defined(__GNUC__) && defined(HAVE_SSE)
+    /* FIXME: Some fegetenv implementations can get the SSE environment too?
+     * How to tell when it does? */
+    if((CPUCapFlags&CPU_CAP_SSE))
+        __asm__ __volatile__("stmxcsr %0" : "=m" (*&ctl->sse_state));
+#endif
+
+#ifdef FE_TOWARDZERO
+    fesetround(FE_TOWARDZERO);
+#endif
+#if defined(__GNUC__) && defined(HAVE_SSE)
+    if((CPUCapFlags&CPU_CAP_SSE))
+    {
+        int sseState = ctl->sse_state;
+        sseState |= 0x6000; /* set round-to-zero */
+        sseState |= 0x8000; /* set flush-to-zero */
+        if((CPUCapFlags&CPU_CAP_SSE2))
+            sseState |= 0x0040; /* set denormals-are-zero */
+        __asm__ __volatile__("ldmxcsr %0" : : "m" (*&sseState));
+    }
+#endif
+
+#elif defined(HAVE___CONTROL87_2)
+
+    int mode;
+    __control87_2(0, 0, &ctl->state, NULL);
+    __control87_2(_RC_CHOP, _MCW_RC, &mode, NULL);
+#ifdef HAVE_SSE
+    if((CPUCapFlags&CPU_CAP_SSE))
+    {
+        __control87_2(0, 0, NULL, &ctl->sse_state);
+        __control87_2(_RC_CHOP|_DN_FLUSH, _MCW_RC|_MCW_DN, NULL, &mode);
+    }
+#endif
+
+#elif defined(HAVE__CONTROLFP)
+
+    ctl->state = _controlfp(0, 0);
+    (void)_controlfp(_RC_CHOP, _MCW_RC);
+#endif
+}
+
+void RestoreFPUMode(const FPUCtl *ctl)
+{
+#ifdef HAVE_FENV_H
+    fesetenv(STATIC_CAST(fenv_t, ctl));
+#if defined(__GNUC__) && defined(HAVE_SSE)
+    if((CPUCapFlags&CPU_CAP_SSE))
+        __asm__ __volatile__("ldmxcsr %0" : : "m" (*&ctl->sse_state));
+#endif
+
+#elif defined(HAVE___CONTROL87_2)
+
+    int mode;
+    __control87_2(ctl->state, _MCW_RC, &mode, NULL);
+#ifdef HAVE_SSE
+    if((CPUCapFlags&CPU_CAP_SSE))
+        __control87_2(ctl->sse_state, _MCW_RC|_MCW_DN, NULL, &mode);
+#endif
+
+#elif defined(HAVE__CONTROLFP)
+
+    _controlfp(ctl->state, _MCW_RC);
+#endif
+}
+
+
+static int StringSortCompare(const void *str1, const void *str2)
+{
+    return al_string_cmp(*(const_al_string*)str1, *(const_al_string*)str2);
+}
+
+#ifdef _WIN32
+
+static WCHAR *strrchrW(WCHAR *str, WCHAR ch)
+{
+    WCHAR *ret = NULL;
+    while(*str)
+    {
+        if(*str == ch)
+            ret = str;
+        ++str;
+    }
+    return ret;
+}
+
+al_string GetProcPath(void)
+{
+    al_string ret = AL_STRING_INIT_STATIC();
+    WCHAR *pathname, *sep;
+    DWORD pathlen;
+    DWORD len;
+
+    pathlen = 256;
+    pathname = malloc(pathlen * sizeof(pathname[0]));
+    while(pathlen > 0 && (len=GetModuleFileNameW(NULL, pathname, pathlen)) == pathlen)
+    {
+        free(pathname);
+        pathlen <<= 1;
+        pathname = malloc(pathlen * sizeof(pathname[0]));
+    }
+    if(len == 0)
+    {
+        free(pathname);
+        ERR("Failed to get process name: error %lu\n", GetLastError());
+        return ret;
+    }
+
+    pathname[len] = 0;
+    if((sep = strrchrW(pathname, '\\')))
+    {
+        WCHAR *sep2 = strrchrW(pathname, '/');
+        if(sep2) *sep2 = 0;
+        else *sep = 0;
+    }
+    else if((sep = strrchrW(pathname, '/')))
+        *sep = 0;
+    al_string_copy_wcstr(&ret, pathname);
+    free(pathname);
+
+    TRACE("Got: %s\n", al_string_get_cstr(ret));
+    return ret;
+}
+
+
+static WCHAR *FromUTF8(const char *str)
+{
+    WCHAR *out = NULL;
+    int len;
+
+    if((len=MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0)) > 0)
+    {
+        out = calloc(sizeof(WCHAR), len);
+        MultiByteToWideChar(CP_UTF8, 0, str, -1, out, len);
+    }
+    return out;
+}
+
+
+void *LoadLib(const char *name)
+{
+    HANDLE hdl = NULL;
+    WCHAR *wname;
+
+    wname = FromUTF8(name);
+    if(!wname)
+        ERR("Failed to convert UTF-8 filename: \"%s\"\n", name);
+    else
+    {
+        hdl = LoadLibraryW(wname);
+        free(wname);
+    }
+    return hdl;
+}
+void CloseLib(void *handle)
+{ FreeLibrary((HANDLE)handle); }
+void *GetSymbol(void *handle, const char *name)
+{
+    void *ret;
+
+    ret = (void*)GetProcAddress((HANDLE)handle, name);
+    if(ret == NULL)
+        ERR("Failed to load %s\n", name);
+    return ret;
+}
+
+WCHAR *strdupW(const WCHAR *str)
+{
+    const WCHAR *n;
+    WCHAR *ret;
+    size_t len;
+
+    n = str;
+    while(*n) n++;
+    len = n - str;
+
+    ret = calloc(sizeof(WCHAR), len+1);
+    if(ret != NULL)
+        memcpy(ret, str, sizeof(WCHAR)*len);
+    return ret;
+}
+
+FILE *al_fopen(const char *fname, const char *mode)
+{
+    WCHAR *wname=NULL, *wmode=NULL;
+    FILE *file = NULL;
+
+    wname = FromUTF8(fname);
+    wmode = FromUTF8(mode);
+    if(!wname)
+        ERR("Failed to convert UTF-8 filename: \"%s\"\n", fname);
+    else if(!wmode)
+        ERR("Failed to convert UTF-8 mode: \"%s\"\n", mode);
+    else
+        file = _wfopen(wname, wmode);
+
+    free(wname);
+    free(wmode);
+
+    return file;
+}
+
+
+void al_print(const char *type, const char *func, const char *fmt, ...)
+{
+    char str[1024];
+    WCHAR *wstr;
+    va_list ap;
+
+    va_start(ap, fmt);
+    vsnprintf(str, sizeof(str), fmt, ap);
+    va_end(ap);
+
+    str[sizeof(str)-1] = 0;
+    wstr = FromUTF8(str);
+    if(!wstr)
+        fprintf(LogFile, "AL lib: %s %s: <UTF-8 error> %s", type, func, str);
+    else
+    {
+        fprintf(LogFile, "AL lib: %s %s: %ls", type, func, wstr);
+        free(wstr);
+        wstr = NULL;
+    }
+    fflush(LogFile);
+}
+
+
+static inline int is_slash(int c)
+{ return (c == '\\' || c == '/'); }
+
+static void DirectorySearch(const char *path, const char *ext, vector_al_string *results)
+{
+    al_string pathstr = AL_STRING_INIT_STATIC();
+    WIN32_FIND_DATAW fdata;
+    WCHAR *wpath;
+    HANDLE hdl;
+
+    al_string_copy_cstr(&pathstr, path);
+    al_string_append_cstr(&pathstr, "\\*");
+    al_string_append_cstr(&pathstr, ext);
+
+    TRACE("Searching %s\n", al_string_get_cstr(pathstr));
+
+    wpath = FromUTF8(al_string_get_cstr(pathstr));
+
+    hdl = FindFirstFileW(wpath, &fdata);
+    if(hdl != INVALID_HANDLE_VALUE)
+    {
+        size_t base = VECTOR_SIZE(*results);
+        do {
+            al_string str = AL_STRING_INIT_STATIC();
+            al_string_copy_cstr(&str, path);
+            al_string_append_char(&str, '\\');
+            al_string_append_wcstr(&str, fdata.cFileName);
+            TRACE("Got result %s\n", al_string_get_cstr(str));
+            VECTOR_PUSH_BACK(*results, str);
+        } while(FindNextFileW(hdl, &fdata));
+        FindClose(hdl);
+
+        if(VECTOR_SIZE(*results) > base)
+            qsort(VECTOR_BEGIN(*results)+base, VECTOR_SIZE(*results)-base,
+                    sizeof(VECTOR_FRONT(*results)), StringSortCompare);
+    }
+
+    free(wpath);
+    al_string_deinit(&pathstr);
+}
+
+vector_al_string SearchDataFiles(const char *ext, const char *subdir)
+{
+    static const int ids[2] = { CSIDL_APPDATA, CSIDL_COMMON_APPDATA };
+    static RefCount search_lock;
+    vector_al_string results = VECTOR_INIT_STATIC();
+    size_t i;
+
+    while(ATOMIC_EXCHANGE(uint, &search_lock, 1) == 1)
+        althrd_yield();
+
+    /* If the path is absolute, use it directly. */
+    if(isalpha(subdir[0]) && subdir[1] == ':' && is_slash(subdir[2]))
+    {
+        al_string path = AL_STRING_INIT_STATIC();
+        al_string_copy_cstr(&path, subdir);
+#define FIX_SLASH(i) do { if(*(i) == '/') *(i) = '\\'; } while(0)
+        VECTOR_FOR_EACH(char, path, FIX_SLASH);
+#undef FIX_SLASH
+
+        DirectorySearch(al_string_get_cstr(path), ext, &results);
+
+        al_string_deinit(&path);
+    }
+    else if(subdir[0] == '\\' && subdir[1] == '\\' && subdir[2] == '?' && subdir[3] == '\\')
+        DirectorySearch(subdir, ext, &results);
+    else
+    {
+        al_string path = AL_STRING_INIT_STATIC();
+        WCHAR *cwdbuf;
+
+        /* Search the app-local directory. */
+        if((cwdbuf=_wgetenv(L"ALSOFT_LOCAL_PATH")) && *cwdbuf != '\0')
+        {
+            al_string_copy_wcstr(&path, cwdbuf);
+            if(is_slash(VECTOR_BACK(path)))
+            {
+                VECTOR_POP_BACK(path);
+                *VECTOR_END(path) = 0;
+            }
+        }
+        else if(!(cwdbuf=_wgetcwd(NULL, 0)))
+            al_string_copy_cstr(&path, ".");
+        else
+        {
+            al_string_copy_wcstr(&path, cwdbuf);
+            if(is_slash(VECTOR_BACK(path)))
+            {
+                VECTOR_POP_BACK(path);
+                *VECTOR_END(path) = 0;
+            }
+            free(cwdbuf);
+        }
+#define FIX_SLASH(i) do { if(*(i) == '/') *(i) = '\\'; } while(0)
+        VECTOR_FOR_EACH(char, path, FIX_SLASH);
+#undef FIX_SLASH
+        DirectorySearch(al_string_get_cstr(path), ext, &results);
+
+        /* Search the local and global data dirs. */
+        for(i = 0;i < COUNTOF(ids);i++)
+        {
+            WCHAR buffer[PATH_MAX];
+            if(SHGetSpecialFolderPathW(NULL, buffer, ids[i], FALSE) != FALSE)
+            {
+                al_string_copy_wcstr(&path, buffer);
+                if(!is_slash(VECTOR_BACK(path)))
+                    al_string_append_char(&path, '\\');
+                al_string_append_cstr(&path, subdir);
+#define FIX_SLASH(i) do { if(*(i) == '/') *(i) = '\\'; } while(0)
+                VECTOR_FOR_EACH(char, path, FIX_SLASH);
+#undef FIX_SLASH
+
+                DirectorySearch(al_string_get_cstr(path), ext, &results);
+            }
+        }
+
+        al_string_deinit(&path);
+    }
+
+    ATOMIC_STORE(&search_lock, 0);
+
+    return results;
+}
+
+
+struct FileMapping MapFileToMem(const char *fname)
+{
+    struct FileMapping ret = { NULL, NULL, NULL, 0 };
+    MEMORY_BASIC_INFORMATION meminfo;
+    HANDLE file, fmap;
+    WCHAR *wname;
+    void *ptr;
+
+    wname = FromUTF8(fname);
+
+    file = CreateFileW(wname, GENERIC_READ, FILE_SHARE_READ, NULL,
+                       OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+    if(file == INVALID_HANDLE_VALUE)
+    {
+        ERR("Failed to open %s: %lu\n", fname, GetLastError());
+        free(wname);
+        return ret;
+    }
+    free(wname);
+    wname = NULL;
+
+    fmap = CreateFileMappingW(file, NULL, PAGE_READONLY, 0, 0, NULL);
+    if(!fmap)
+    {
+        ERR("Failed to create map for %s: %lu\n", fname, GetLastError());
+        CloseHandle(file);
+        return ret;
+    }
+
+    ptr = MapViewOfFile(fmap, FILE_MAP_READ, 0, 0, 0);
+    if(!ptr)
+    {
+        ERR("Failed to map %s: %lu\n", fname, GetLastError());
+        CloseHandle(fmap);
+        CloseHandle(file);
+        return ret;
+    }
+
+    if(VirtualQuery(ptr, &meminfo, sizeof(meminfo)) != sizeof(meminfo))
+    {
+        ERR("Failed to get map size for %s: %lu\n", fname, GetLastError());
+        UnmapViewOfFile(ptr);
+        CloseHandle(fmap);
+        CloseHandle(file);
+        return ret;
+    }
+
+    ret.file = file;
+    ret.fmap = fmap;
+    ret.ptr = ptr;
+    ret.len = meminfo.RegionSize;
+    return ret;
+}
+
+void UnmapFileMem(const struct FileMapping *mapping)
+{
+    UnmapViewOfFile(mapping->ptr);
+    CloseHandle(mapping->fmap);
+    CloseHandle(mapping->file);
+}
+
+#else
+
+al_string GetProcPath(void)
+{
+    al_string ret = AL_STRING_INIT_STATIC();
+    const char *fname;
+    char *pathname, *sep;
+    size_t pathlen;
+    ssize_t len;
+
+    pathlen = 256;
+    pathname = malloc(pathlen);
+
+    fname = "/proc/self/exe";
+    len = readlink(fname, pathname, pathlen);
+    if(len == -1 && errno == ENOENT)
+    {
+        fname = "/proc/self/file";
+        len = readlink(fname, pathname, pathlen);
+    }
+
+    while(len > 0 && (size_t)len == pathlen)
+    {
+        free(pathname);
+        pathlen <<= 1;
+        pathname = malloc(pathlen);
+        len = readlink(fname, pathname, pathlen);
+    }
+    if(len <= 0)
+    {
+        free(pathname);
+        WARN("Failed to readlink %s: %s\n", fname, strerror(errno));
+        return ret;
+    }
+
+    pathname[len] = 0;
+    sep = strrchr(pathname, '/');
+    if(sep)
+        al_string_copy_range(&ret, pathname, sep);
+    else
+        al_string_copy_cstr(&ret, pathname);
+    free(pathname);
+
+    TRACE("Got: %s\n", al_string_get_cstr(ret));
+    return ret;
+}
+
+
+#ifdef HAVE_DLFCN_H
+
+void *LoadLib(const char *name)
+{
+    const char *err;
+    void *handle;
+
+    dlerror();
+    handle = dlopen(name, RTLD_NOW);
+    if((err=dlerror()) != NULL)
+        handle = NULL;
+    return handle;
+}
+void CloseLib(void *handle)
+{ dlclose(handle); }
+void *GetSymbol(void *handle, const char *name)
+{
+    const char *err;
+    void *sym;
+
+    dlerror();
+    sym = dlsym(handle, name);
+    if((err=dlerror()) != NULL)
+    {
+        WARN("Failed to load %s: %s\n", name, err);
+        sym = NULL;
+    }
+    return sym;
+}
+
+#endif /* HAVE_DLFCN_H */
+
+void al_print(const char *type, const char *func, const char *fmt, ...)
+{
+    va_list ap;
+
+    va_start(ap, fmt);
+    fprintf(LogFile, "AL lib: %s %s: ", type, func);
+    vfprintf(LogFile, fmt, ap);
+    va_end(ap);
+
+    fflush(LogFile);
+}
+
+
+static void DirectorySearch(const char *path, const char *ext, vector_al_string *results)
+{
+    size_t extlen = strlen(ext);
+    DIR *dir;
+
+    TRACE("Searching %s for *%s\n", path, ext);
+    dir = opendir(path);
+    if(dir != NULL)
+    {
+        size_t base = VECTOR_SIZE(*results);
+        struct dirent *dirent;
+        while((dirent=readdir(dir)) != NULL)
+        {
+            al_string str;
+            size_t len;
+            if(strcmp(dirent->d_name, ".") == 0 || strcmp(dirent->d_name, "..") == 0)
+                continue;
+
+            len = strlen(dirent->d_name);
+            if(!(len > extlen))
+                continue;
+            if(strcasecmp(dirent->d_name+len-extlen, ext) != 0)
+                continue;
+
+            AL_STRING_INIT(str);
+            al_string_copy_cstr(&str, path);
+            if(VECTOR_BACK(str) != '/')
+                al_string_append_char(&str, '/');
+            al_string_append_cstr(&str, dirent->d_name);
+            TRACE("Got result %s\n", al_string_get_cstr(str));
+            VECTOR_PUSH_BACK(*results, str);
+        }
+        closedir(dir);
+
+        if(VECTOR_SIZE(*results) > base)
+            qsort(VECTOR_BEGIN(*results)+base, VECTOR_SIZE(*results)-base,
+                  sizeof(VECTOR_FRONT(*results)), StringSortCompare);
+    }
+}
+
+vector_al_string SearchDataFiles(const char *ext, const char *subdir)
+{
+    static RefCount search_lock;
+    vector_al_string results = VECTOR_INIT_STATIC();
+
+    while(ATOMIC_EXCHANGE(uint, &search_lock, 1) == 1)
+        althrd_yield();
+
+    if(subdir[0] == '/')
+        DirectorySearch(subdir, ext, &results);
+    else
+    {
+        al_string path = AL_STRING_INIT_STATIC();
+        const char *str, *next;
+        char cwdbuf[PATH_MAX];
+
+        /* Search the app-local directory. */
+        if((str=getenv("ALSOFT_LOCAL_PATH")) && *str != '\0')
+            DirectorySearch(str, ext, &results);
+        else if(getcwd(cwdbuf, sizeof(cwdbuf)))
+            DirectorySearch(cwdbuf, ext, &results);
+        else
+            DirectorySearch(".", ext, &results);
+
+        // Search local data dir
+        if((str=getenv("XDG_DATA_HOME")) != NULL && str[0] != '\0')
+        {
+            al_string_copy_cstr(&path, str);
+            if(VECTOR_BACK(path) != '/')
+                al_string_append_char(&path, '/');
+            al_string_append_cstr(&path, subdir);
+            DirectorySearch(al_string_get_cstr(path), ext, &results);
+        }
+        else if((str=getenv("HOME")) != NULL && str[0] != '\0')
+        {
+            al_string_copy_cstr(&path, str);
+            if(VECTOR_BACK(path) == '/')
+            {
+                VECTOR_POP_BACK(path);
+                *VECTOR_END(path) = 0;
+            }
+            al_string_append_cstr(&path, "/.local/share/");
+            al_string_append_cstr(&path, subdir);
+            DirectorySearch(al_string_get_cstr(path), ext, &results);
+        }
+
+        // Search global data dirs
+        if((str=getenv("XDG_DATA_DIRS")) == NULL || str[0] == '\0')
+            str = "/usr/local/share/:/usr/share/";
+
+        next = str;
+        while((str=next) != NULL && str[0] != '\0')
+        {
+            next = strchr(str, ':');
+            if(!next)
+                al_string_copy_cstr(&path, str);
+            else
+            {
+                al_string_copy_range(&path, str, next);
+                ++next;
+            }
+            if(!al_string_empty(path))
+            {
+                if(VECTOR_BACK(path) != '/')
+                    al_string_append_char(&path, '/');
+                al_string_append_cstr(&path, subdir);
+
+                DirectorySearch(al_string_get_cstr(path), ext, &results);
+            }
+        }
+
+        al_string_deinit(&path);
+    }
+
+    ATOMIC_STORE(&search_lock, 0);
+
+    return results;
+}
+
+
+struct FileMapping MapFileToMem(const char *fname)
+{
+    struct FileMapping ret = { -1, NULL, 0 };
+    struct stat sbuf;
+    void *ptr;
+    int fd;
+
+    fd = open(fname, O_RDONLY, 0);
+    if(fd == -1)
+    {
+        ERR("Failed to open %s: (%d) %s\n", fname, errno, strerror(errno));
+        return ret;
+    }
+    if(fstat(fd, &sbuf) == -1)
+    {
+        ERR("Failed to stat %s: (%d) %s\n", fname, errno, strerror(errno));
+        close(fd);
+        return ret;
+    }
+
+    ptr = mmap(NULL, sbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+    if(ptr == MAP_FAILED)
+    {
+        ERR("Failed to map %s: (%d) %s\n", fname, errno, strerror(errno));
+        close(fd);
+        return ret;
+    }
+
+    ret.fd = fd;
+    ret.ptr = ptr;
+    ret.len = sbuf.st_size;
+    return ret;
+}
+
+void UnmapFileMem(const struct FileMapping *mapping)
+{
+    munmap(mapping->ptr, mapping->len);
+    close(mapping->fd);
+}
+
+#endif
+
+
+void SetRTPriority(void)
+{
+    ALboolean failed = AL_FALSE;
+
+#ifdef _WIN32
+    if(RTPrioLevel > 0)
+        failed = !SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
+#elif defined(HAVE_PTHREAD_SETSCHEDPARAM) && !defined(__OpenBSD__)
+    if(RTPrioLevel > 0)
+    {
+        struct sched_param param;
+        /* Use the minimum real-time priority possible for now (on Linux this
+         * should be 1 for SCHED_RR) */
+        param.sched_priority = sched_get_priority_min(SCHED_RR);
+        failed = !!pthread_setschedparam(pthread_self(), SCHED_RR, &param);
+    }
+#else
+    /* Real-time priority not available */
+    failed = (RTPrioLevel>0);
+#endif
+    if(failed)
+        ERR("Failed to set priority level for thread\n");
+}
+
+
+extern inline void al_string_deinit(al_string *str);
+extern inline size_t al_string_length(const_al_string str);
+extern inline ALboolean al_string_empty(const_al_string str);
+extern inline const al_string_char_type *al_string_get_cstr(const_al_string str);
+
+void al_string_clear(al_string *str)
+{
+    if(!al_string_empty(*str))
+    {
+        /* Reserve one more character than the total size of the string. This
+         * is to ensure we have space to add a null terminator in the string
+         * data so it can be used as a C-style string.
+         */
+        VECTOR_RESIZE(*str, 0, 1);
+        VECTOR_ELEM(*str, 0) = 0;
+    }
+}
+
+static inline int al_string_compare(const al_string_char_type *str1, size_t str1len,
+                                    const al_string_char_type *str2, size_t str2len)
+{
+    size_t complen = (str1len < str2len) ? str1len : str2len;
+    int ret = memcmp(str1, str2, complen);
+    if(ret == 0)
+    {
+        if(str1len > str2len) return  1;
+        if(str1len < str2len) return -1;
+    }
+    return ret;
+}
+int al_string_cmp(const_al_string str1, const_al_string str2)
+{
+    return al_string_compare(&VECTOR_FRONT(str1), al_string_length(str1),
+                             &VECTOR_FRONT(str2), al_string_length(str2));
+}
+int al_string_cmp_cstr(const_al_string str1, const al_string_char_type *str2)
+{
+    return al_string_compare(&VECTOR_FRONT(str1), al_string_length(str1),
+                             str2, strlen(str2));
+}
+
+void al_string_copy(al_string *str, const_al_string from)
+{
+    size_t len = al_string_length(from);
+    size_t i;
+
+    VECTOR_RESIZE(*str, len, len+1);
+    for(i = 0;i < len;i++)
+        VECTOR_ELEM(*str, i) = VECTOR_ELEM(from, i);
+    VECTOR_ELEM(*str, i) = 0;
+}
+
+void al_string_copy_cstr(al_string *str, const al_string_char_type *from)
+{
+    size_t len = strlen(from);
+    size_t i;
+
+    VECTOR_RESIZE(*str, len, len+1);
+    for(i = 0;i < len;i++)
+        VECTOR_ELEM(*str, i) = from[i];
+    VECTOR_ELEM(*str, i) = 0;
+}
+
+void al_string_copy_range(al_string *str, const al_string_char_type *from, const al_string_char_type *to)
+{
+    size_t len = to - from;
+    size_t i;
+
+    VECTOR_RESIZE(*str, len, len+1);
+    for(i = 0;i < len;i++)
+        VECTOR_ELEM(*str, i) = from[i];
+    VECTOR_ELEM(*str, i) = 0;
+}
+
+void al_string_append_char(al_string *str, const al_string_char_type c)
+{
+    size_t len = al_string_length(*str);
+    VECTOR_RESIZE(*str, len, len+2);
+    VECTOR_PUSH_BACK(*str, c);
+    VECTOR_ELEM(*str, len+1) = 0;
+}
+
+void al_string_append_cstr(al_string *str, const al_string_char_type *from)
+{
+    size_t len = strlen(from);
+    if(len != 0)
+    {
+        size_t base = al_string_length(*str);
+        size_t i;
+
+        VECTOR_RESIZE(*str, base+len, base+len+1);
+        for(i = 0;i < len;i++)
+            VECTOR_ELEM(*str, base+i) = from[i];
+        VECTOR_ELEM(*str, base+i) = 0;
+    }
+}
+
+void al_string_append_range(al_string *str, const al_string_char_type *from, const al_string_char_type *to)
+{
+    size_t len = to - from;
+    if(len != 0)
+    {
+        size_t base = al_string_length(*str);
+        size_t i;
+
+        VECTOR_RESIZE(*str, base+len, base+len+1);
+        for(i = 0;i < len;i++)
+            VECTOR_ELEM(*str, base+i) = from[i];
+        VECTOR_ELEM(*str, base+i) = 0;
+    }
+}
+
+#ifdef _WIN32
+void al_string_copy_wcstr(al_string *str, const wchar_t *from)
+{
+    int len;
+    if((len=WideCharToMultiByte(CP_UTF8, 0, from, -1, NULL, 0, NULL, NULL)) > 0)
+    {
+        VECTOR_RESIZE(*str, len-1, len);
+        WideCharToMultiByte(CP_UTF8, 0, from, -1, &VECTOR_FRONT(*str), len, NULL, NULL);
+        VECTOR_ELEM(*str, len-1) = 0;
+    }
+}
+
+void al_string_append_wcstr(al_string *str, const wchar_t *from)
+{
+    int len;
+    if((len=WideCharToMultiByte(CP_UTF8, 0, from, -1, NULL, 0, NULL, NULL)) > 0)
+    {
+        size_t base = al_string_length(*str);
+        VECTOR_RESIZE(*str, base+len-1, base+len);
+        WideCharToMultiByte(CP_UTF8, 0, from, -1, &VECTOR_ELEM(*str, base), len, NULL, NULL);
+        VECTOR_ELEM(*str, base+len-1) = 0;
+    }
+}
+
+void al_string_append_wrange(al_string *str, const wchar_t *from, const wchar_t *to)
+{
+    int len;
+    if((len=WideCharToMultiByte(CP_UTF8, 0, from, (int)(to-from), NULL, 0, NULL, NULL)) > 0)
+    {
+        size_t base = al_string_length(*str);
+        VECTOR_RESIZE(*str, base+len, base+len+1);
+        WideCharToMultiByte(CP_UTF8, 0, from, (int)(to-from), &VECTOR_ELEM(*str, base), len+1, NULL, NULL);
+        VECTOR_ELEM(*str, base+len) = 0;
+    }
+}
+#endif

+ 1054 - 0
Engine/lib/openal-soft/Alc/hrtf.c

@@ -0,0 +1,1054 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 2011 by Chris Robinson
+ * This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the
+ *  Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <ctype.h>
+
+#include "AL/al.h"
+#include "AL/alc.h"
+#include "alMain.h"
+#include "alSource.h"
+#include "alu.h"
+#include "bformatdec.h"
+#include "hrtf.h"
+
+#include "compat.h"
+#include "almalloc.h"
+
+
+/* Current data set limits defined by the makehrtf utility. */
+#define MIN_IR_SIZE                  (8)
+#define MAX_IR_SIZE                  (128)
+#define MOD_IR_SIZE                  (8)
+
+#define MIN_EV_COUNT                 (5)
+#define MAX_EV_COUNT                 (128)
+
+#define MIN_AZ_COUNT                 (1)
+#define MAX_AZ_COUNT                 (128)
+
+static const ALchar magicMarker00[8] = "MinPHR00";
+static const ALchar magicMarker01[8] = "MinPHR01";
+
+/* First value for pass-through coefficients (remaining are 0), used for omni-
+ * directional sounds. */
+static const ALfloat PassthruCoeff = 32767.0f * 0.707106781187f/*sqrt(0.5)*/;
+
+static struct Hrtf *LoadedHrtfs = NULL;
+
+
+/* Calculate the elevation index given the polar elevation in radians. This
+ * will return an index between 0 and (evcount - 1). Assumes the FPU is in
+ * round-to-zero mode.
+ */
+static ALuint CalcEvIndex(ALuint evcount, ALfloat ev)
+{
+    ev = (F_PI_2 + ev) * (evcount-1) / F_PI;
+    return minu(fastf2u(ev + 0.5f), evcount-1);
+}
+
+/* Calculate the azimuth index given the polar azimuth in radians. This will
+ * return an index between 0 and (azcount - 1). Assumes the FPU is in round-to-
+ * zero mode.
+ */
+static ALuint CalcAzIndex(ALuint azcount, ALfloat az)
+{
+    az = (F_TAU + az) * azcount / F_TAU;
+    return fastf2u(az + 0.5f) % azcount;
+}
+
+/* Calculates static HRIR coefficients and delays for the given polar elevation
+ * and azimuth in radians. The coefficients are normalized and attenuated by
+ * the specified gain.
+ */
+void GetHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azimuth, ALfloat spread, ALfloat gain, ALfloat (*coeffs)[2], ALuint *delays)
+{
+    ALuint evidx, azidx, lidx, ridx;
+    ALuint azcount, evoffset;
+    ALfloat dirfact;
+    ALuint i;
+
+    dirfact = 1.0f - (spread / F_TAU);
+
+    /* Claculate elevation index. */
+    evidx = CalcEvIndex(Hrtf->evCount, elevation);
+    azcount = Hrtf->azCount[evidx];
+    evoffset = Hrtf->evOffset[evidx];
+
+    /* Calculate azimuth index. */
+    azidx = CalcAzIndex(Hrtf->azCount[evidx], azimuth);
+
+    /* Calculate the HRIR indices for left and right channels. */
+    lidx = evoffset + azidx;
+    ridx = evoffset + ((azcount-azidx) % azcount);
+
+    /* Calculate the HRIR delays. */
+    delays[0] = fastf2u(Hrtf->delays[lidx]*dirfact + 0.5f) << HRTFDELAY_BITS;
+    delays[1] = fastf2u(Hrtf->delays[ridx]*dirfact + 0.5f) << HRTFDELAY_BITS;
+
+    /* Calculate the sample offsets for the HRIR indices. */
+    lidx *= Hrtf->irSize;
+    ridx *= Hrtf->irSize;
+
+    /* Calculate the normalized and attenuated HRIR coefficients. Zero the
+     * coefficients if gain is too low.
+     */
+    if(gain > 0.0001f)
+    {
+        gain /= 32767.0f;
+
+        i = 0;
+        coeffs[i][0] = lerp(PassthruCoeff, Hrtf->coeffs[lidx+i], dirfact)*gain;
+        coeffs[i][1] = lerp(PassthruCoeff, Hrtf->coeffs[ridx+i], dirfact)*gain;
+        for(i = 1;i < Hrtf->irSize;i++)
+        {
+            coeffs[i][0] = Hrtf->coeffs[lidx+i]*gain * dirfact;
+            coeffs[i][1] = Hrtf->coeffs[ridx+i]*gain * dirfact;
+        }
+    }
+    else
+    {
+        for(i = 0;i < Hrtf->irSize;i++)
+        {
+            coeffs[i][0] = 0.0f;
+            coeffs[i][1] = 0.0f;
+        }
+    }
+}
+
+
+ALuint BuildBFormatHrtf(const struct Hrtf *Hrtf, ALfloat (*coeffs)[HRIR_LENGTH][2], ALuint NumChannels)
+{
+    static const struct {
+        ALfloat elevation;
+        ALfloat azimuth;
+    } Ambi3DPoints[14] = {
+        { DEG2RAD( 90.0f), DEG2RAD(   0.0f) },
+        { DEG2RAD( 35.0f), DEG2RAD( -45.0f) },
+        { DEG2RAD( 35.0f), DEG2RAD(  45.0f) },
+        { DEG2RAD( 35.0f), DEG2RAD( 135.0f) },
+        { DEG2RAD( 35.0f), DEG2RAD(-135.0f) },
+        { DEG2RAD(  0.0f), DEG2RAD(   0.0f) },
+        { DEG2RAD(  0.0f), DEG2RAD(  90.0f) },
+        { DEG2RAD(  0.0f), DEG2RAD( 180.0f) },
+        { DEG2RAD(  0.0f), DEG2RAD( -90.0f) },
+        { DEG2RAD(-35.0f), DEG2RAD( -45.0f) },
+        { DEG2RAD(-35.0f), DEG2RAD(  45.0f) },
+        { DEG2RAD(-35.0f), DEG2RAD( 135.0f) },
+        { DEG2RAD(-35.0f), DEG2RAD(-135.0f) },
+        { DEG2RAD(-90.0f), DEG2RAD(   0.0f) },
+    };
+    static const ALfloat Ambi3DMatrix[14][2][MAX_AMBI_COEFFS] = {
+        { { 0.078851598f,  0.000000000f,  0.070561967f,  0.000000000f }, { 0.0269973975f,  0.0000000000f,  0.0467610443f,  0.0000000000f } },
+        { { 0.124051278f,  0.059847972f,  0.059847972f,  0.059847972f }, { 0.0269973975f,  0.0269973975f,  0.0269973975f,  0.0269973975f } },
+        { { 0.124051278f, -0.059847972f,  0.059847972f,  0.059847972f }, { 0.0269973975f, -0.0269973975f,  0.0269973975f,  0.0269973975f } },
+        { { 0.124051278f, -0.059847972f,  0.059847972f, -0.059847972f }, { 0.0269973975f, -0.0269973975f,  0.0269973975f, -0.0269973975f } },
+        { { 0.124051278f,  0.059847972f,  0.059847972f, -0.059847972f }, { 0.0269973975f,  0.0269973975f,  0.0269973975f, -0.0269973975f } },
+        { { 0.078851598f,  0.000000000f,  0.000000000f,  0.070561967f }, { 0.0269973975f,  0.0000000000f,  0.0000000000f,  0.0467610443f } },
+        { { 0.078851598f, -0.070561967f,  0.000000000f,  0.000000000f }, { 0.0269973975f, -0.0467610443f,  0.0000000000f,  0.0000000000f } },
+        { { 0.078851598f,  0.000000000f,  0.000000000f, -0.070561967f }, { 0.0269973975f,  0.0000000000f,  0.0000000000f, -0.0467610443f } },
+        { { 0.078851598f,  0.070561967f,  0.000000000f,  0.000000000f }, { 0.0269973975f,  0.0467610443f,  0.0000000000f,  0.0000000000f } },
+        { { 0.124051278f,  0.059847972f, -0.059847972f,  0.059847972f }, { 0.0269973975f,  0.0269973975f, -0.0269973975f,  0.0269973975f } },
+        { { 0.124051278f, -0.059847972f, -0.059847972f,  0.059847972f }, { 0.0269973975f, -0.0269973975f, -0.0269973975f,  0.0269973975f } },
+        { { 0.124051278f, -0.059847972f, -0.059847972f, -0.059847972f }, { 0.0269973975f, -0.0269973975f, -0.0269973975f, -0.0269973975f } },
+        { { 0.124051278f,  0.059847972f, -0.059847972f, -0.059847972f }, { 0.0269973975f,  0.0269973975f, -0.0269973975f, -0.0269973975f } },
+        { { 0.078851598f,  0.000000000f, -0.070561967f,  0.000000000f }, { 0.0269973975f,  0.0000000000f, -0.0467610443f,  0.0000000000f } },
+    };
+/* Change this to 2 for dual-band HRTF processing. May require a higher quality
+ * band-splitter, or better calculation of the new IR length to deal with the
+ * tail generated by the filter.
+ */
+#define NUM_BANDS 2
+    BandSplitter splitter;
+    ALfloat temps[3][HRIR_LENGTH];
+    ALuint lidx[14], ridx[14];
+    ALuint min_delay = HRTF_HISTORY_LENGTH;
+    ALuint max_length = 0;
+    ALuint i, j, c, b;
+
+    assert(NumChannels == 4);
+
+    for(c = 0;c < COUNTOF(Ambi3DPoints);c++)
+    {
+        ALuint evidx, azidx;
+        ALuint evoffset;
+        ALuint azcount;
+
+        /* Calculate elevation index. */
+        evidx = (ALuint)floorf((F_PI_2 + Ambi3DPoints[c].elevation) *
+                               (Hrtf->evCount-1)/F_PI + 0.5f);
+        evidx = minu(evidx, Hrtf->evCount-1);
+
+        azcount = Hrtf->azCount[evidx];
+        evoffset = Hrtf->evOffset[evidx];
+
+        /* Calculate azimuth index for this elevation. */
+        azidx = (ALuint)floorf((F_TAU+Ambi3DPoints[c].azimuth) *
+                               azcount/F_TAU + 0.5f) % azcount;
+
+        /* Calculate indices for left and right channels. */
+        lidx[c] = evoffset + azidx;
+        ridx[c] = evoffset + ((azcount-azidx) % azcount);
+
+        min_delay = minu(min_delay, minu(Hrtf->delays[lidx[c]], Hrtf->delays[ridx[c]]));
+    }
+
+    memset(temps, 0, sizeof(temps));
+    bandsplit_init(&splitter, 400.0f / (ALfloat)Hrtf->sampleRate);
+    for(c = 0;c < COUNTOF(Ambi3DMatrix);c++)
+    {
+        const ALshort *fir;
+        ALuint delay;
+
+        /* Convert the left FIR from shorts to float */
+        fir = &Hrtf->coeffs[lidx[c] * Hrtf->irSize];
+        if(NUM_BANDS == 1)
+        {
+            for(i = 0;i < Hrtf->irSize;i++)
+                temps[0][i] = fir[i] / 32767.0f;
+        }
+        else
+        {
+            /* Band-split left HRIR into low and high frequency responses. */
+            bandsplit_clear(&splitter);
+            for(i = 0;i < Hrtf->irSize;i++)
+                temps[2][i] = fir[i] / 32767.0f;
+            bandsplit_process(&splitter, temps[0], temps[1], temps[2], HRIR_LENGTH);
+        }
+
+        /* Add to the left output coefficients with the specified delay. */
+        delay = Hrtf->delays[lidx[c]] - min_delay;
+        for(i = 0;i < NumChannels;++i)
+        {
+            for(b = 0;b < NUM_BANDS;b++)
+            {
+                ALuint k = 0;
+                for(j = delay;j < HRIR_LENGTH;++j)
+                    coeffs[i][j][0] += temps[b][k++] * Ambi3DMatrix[c][b][i];
+            }
+        }
+        max_length = maxu(max_length, minu(delay + Hrtf->irSize, HRIR_LENGTH));
+
+        /* Convert the right FIR from shorts to float */
+        fir = &Hrtf->coeffs[ridx[c] * Hrtf->irSize];
+        if(NUM_BANDS == 1)
+        {
+            for(i = 0;i < Hrtf->irSize;i++)
+                temps[0][i] = fir[i] / 32767.0f;
+        }
+        else
+        {
+            /* Band-split right HRIR into low and high frequency responses. */
+            bandsplit_clear(&splitter);
+            for(i = 0;i < Hrtf->irSize;i++)
+                temps[2][i] = fir[i] / 32767.0f;
+            bandsplit_process(&splitter, temps[0], temps[1], temps[2], HRIR_LENGTH);
+        }
+
+        /* Add to the right output coefficients with the specified delay. */
+        delay = Hrtf->delays[ridx[c]] - min_delay;
+        for(i = 0;i < NumChannels;++i)
+        {
+            for(b = 0;b < NUM_BANDS;b++)
+            {
+                ALuint k = 0;
+                for(j = delay;j < HRIR_LENGTH;++j)
+                    coeffs[i][j][1] += temps[b][k++] * Ambi3DMatrix[c][b][i];
+            }
+        }
+        max_length = maxu(max_length, minu(delay + Hrtf->irSize, HRIR_LENGTH));
+    }
+    TRACE("Skipped min delay: %u, new combined length: %u\n", min_delay, max_length);
+#undef NUM_BANDS
+
+    return max_length;
+}
+
+
+static struct Hrtf *LoadHrtf00(const ALubyte *data, size_t datalen, const_al_string filename)
+{
+    const ALubyte maxDelay = HRTF_HISTORY_LENGTH-1;
+    struct Hrtf *Hrtf = NULL;
+    ALboolean failed = AL_FALSE;
+    ALuint rate = 0, irCount = 0;
+    ALushort irSize = 0;
+    ALubyte evCount = 0;
+    ALubyte *azCount = NULL;
+    ALushort *evOffset = NULL;
+    ALshort *coeffs = NULL;
+    const ALubyte *delays = NULL;
+    ALuint i, j;
+
+    if(datalen < 9)
+    {
+        ERR("Unexpected end of %s data (req %d, rem "SZFMT")\n",
+            al_string_get_cstr(filename), 9, datalen);
+        return NULL;
+    }
+
+    rate  = *(data++);
+    rate |= *(data++)<<8;
+    rate |= *(data++)<<16;
+    rate |= *(data++)<<24;
+    datalen -= 4;
+
+    irCount  = *(data++);
+    irCount |= *(data++)<<8;
+    datalen -= 2;
+
+    irSize  = *(data++);
+    irSize |= *(data++)<<8;
+    datalen -= 2;
+
+    evCount = *(data++);
+    datalen -= 1;
+
+    if(irSize < MIN_IR_SIZE || irSize > MAX_IR_SIZE || (irSize%MOD_IR_SIZE))
+    {
+        ERR("Unsupported HRIR size: irSize=%d (%d to %d by %d)\n",
+            irSize, MIN_IR_SIZE, MAX_IR_SIZE, MOD_IR_SIZE);
+        failed = AL_TRUE;
+    }
+    if(evCount < MIN_EV_COUNT || evCount > MAX_EV_COUNT)
+    {
+        ERR("Unsupported elevation count: evCount=%d (%d to %d)\n",
+            evCount, MIN_EV_COUNT, MAX_EV_COUNT);
+        failed = AL_TRUE;
+    }
+    if(failed)
+        return NULL;
+
+    if(datalen < evCount*2)
+    {
+        ERR("Unexpected end of %s data (req %d, rem "SZFMT")\n",
+            al_string_get_cstr(filename), evCount*2, datalen);
+        return NULL;
+    }
+
+    azCount = malloc(sizeof(azCount[0])*evCount);
+    evOffset = malloc(sizeof(evOffset[0])*evCount);
+    if(azCount == NULL || evOffset == NULL)
+    {
+        ERR("Out of memory.\n");
+        failed = AL_TRUE;
+    }
+
+    if(!failed)
+    {
+        evOffset[0]  = *(data++);
+        evOffset[0] |= *(data++)<<8;
+        datalen -= 2;
+        for(i = 1;i < evCount;i++)
+        {
+            evOffset[i]  = *(data++);
+            evOffset[i] |= *(data++)<<8;
+            datalen -= 2;
+            if(evOffset[i] <= evOffset[i-1])
+            {
+                ERR("Invalid evOffset: evOffset[%d]=%d (last=%d)\n",
+                    i, evOffset[i], evOffset[i-1]);
+                failed = AL_TRUE;
+            }
+
+            azCount[i-1] = evOffset[i] - evOffset[i-1];
+            if(azCount[i-1] < MIN_AZ_COUNT || azCount[i-1] > MAX_AZ_COUNT)
+            {
+                ERR("Unsupported azimuth count: azCount[%d]=%d (%d to %d)\n",
+                    i-1, azCount[i-1], MIN_AZ_COUNT, MAX_AZ_COUNT);
+                failed = AL_TRUE;
+            }
+        }
+        if(irCount <= evOffset[i-1])
+        {
+            ERR("Invalid evOffset: evOffset[%d]=%d (irCount=%d)\n",
+                i-1, evOffset[i-1], irCount);
+            failed = AL_TRUE;
+        }
+
+        azCount[i-1] = irCount - evOffset[i-1];
+        if(azCount[i-1] < MIN_AZ_COUNT || azCount[i-1] > MAX_AZ_COUNT)
+        {
+            ERR("Unsupported azimuth count: azCount[%d]=%d (%d to %d)\n",
+                i-1, azCount[i-1], MIN_AZ_COUNT, MAX_AZ_COUNT);
+            failed = AL_TRUE;
+        }
+    }
+
+    if(!failed)
+    {
+        coeffs = malloc(sizeof(coeffs[0])*irSize*irCount);
+        if(coeffs == NULL)
+        {
+            ERR("Out of memory.\n");
+            failed = AL_TRUE;
+        }
+    }
+
+    if(!failed)
+    {
+        size_t reqsize = 2*irSize*irCount + irCount;
+        if(datalen < reqsize)
+        {
+            ERR("Unexpected end of %s data (req "SZFMT", rem "SZFMT")\n",
+                al_string_get_cstr(filename), reqsize, datalen);
+            failed = AL_TRUE;
+        }
+    }
+
+    if(!failed)
+    {
+        for(i = 0;i < irCount*irSize;i+=irSize)
+        {
+            for(j = 0;j < irSize;j++)
+            {
+                coeffs[i+j]  = *(data++);
+                coeffs[i+j] |= *(data++)<<8;
+                datalen -= 2;
+            }
+        }
+
+        delays = data;
+        data += irCount;
+        datalen -= irCount;
+        for(i = 0;i < irCount;i++)
+        {
+            if(delays[i] > maxDelay)
+            {
+                ERR("Invalid delays[%d]: %d (%d)\n", i, delays[i], maxDelay);
+                failed = AL_TRUE;
+            }
+        }
+    }
+
+    if(!failed)
+    {
+        size_t total = sizeof(struct Hrtf);
+        total += sizeof(azCount[0])*evCount;
+        total  = (total+1)&~1; /* Align for (u)short fields */
+        total += sizeof(evOffset[0])*evCount;
+        total += sizeof(coeffs[0])*irSize*irCount;
+        total += sizeof(delays[0])*irCount;
+        total += al_string_length(filename)+1;
+
+        Hrtf = al_calloc(16, total);
+        if(Hrtf == NULL)
+        {
+            ERR("Out of memory.\n");
+            failed = AL_TRUE;
+        }
+    }
+
+    if(!failed)
+    {
+        char *base = (char*)Hrtf;
+        uintptr_t offset = sizeof(*Hrtf);
+
+        Hrtf->sampleRate = rate;
+        Hrtf->irSize = irSize;
+        Hrtf->evCount = evCount;
+        Hrtf->azCount = ((ALubyte*)(base + offset)); offset += evCount*sizeof(Hrtf->azCount[0]);
+        offset = (offset+1)&~1; /* Align for (u)short fields */
+        Hrtf->evOffset = ((ALushort*)(base + offset)); offset += evCount*sizeof(Hrtf->evOffset[0]);
+        Hrtf->coeffs = ((ALshort*)(base + offset)); offset += irSize*irCount*sizeof(Hrtf->coeffs[0]);
+        Hrtf->delays = ((ALubyte*)(base + offset)); offset += irCount*sizeof(Hrtf->delays[0]);
+        Hrtf->filename = ((char*)(base + offset));
+        Hrtf->next = NULL;
+
+        memcpy((void*)Hrtf->azCount, azCount, sizeof(azCount[0])*evCount);
+        memcpy((void*)Hrtf->evOffset, evOffset, sizeof(evOffset[0])*evCount);
+        memcpy((void*)Hrtf->coeffs, coeffs, sizeof(coeffs[0])*irSize*irCount);
+        memcpy((void*)Hrtf->delays, delays, sizeof(delays[0])*irCount);
+        memcpy((void*)Hrtf->filename, al_string_get_cstr(filename), al_string_length(filename)+1);
+    }
+
+    free(azCount);
+    free(evOffset);
+    free(coeffs);
+    return Hrtf;
+}
+
+static struct Hrtf *LoadHrtf01(const ALubyte *data, size_t datalen, const_al_string filename)
+{
+    const ALubyte maxDelay = HRTF_HISTORY_LENGTH-1;
+    struct Hrtf *Hrtf = NULL;
+    ALboolean failed = AL_FALSE;
+    ALuint rate = 0, irCount = 0;
+    ALubyte irSize = 0, evCount = 0;
+    const ALubyte *azCount = NULL;
+    ALushort *evOffset = NULL;
+    ALshort *coeffs = NULL;
+    const ALubyte *delays = NULL;
+    ALuint i, j;
+
+    if(datalen < 6)
+    {
+        ERR("Unexpected end of %s data (req %d, rem "SZFMT"\n",
+            al_string_get_cstr(filename), 6, datalen);
+        return NULL;
+    }
+
+    rate  = *(data++);
+    rate |= *(data++)<<8;
+    rate |= *(data++)<<16;
+    rate |= *(data++)<<24;
+    datalen -= 4;
+
+    irSize = *(data++);
+    datalen -= 1;
+
+    evCount = *(data++);
+    datalen -= 1;
+
+    if(irSize < MIN_IR_SIZE || irSize > MAX_IR_SIZE || (irSize%MOD_IR_SIZE))
+    {
+        ERR("Unsupported HRIR size: irSize=%d (%d to %d by %d)\n",
+            irSize, MIN_IR_SIZE, MAX_IR_SIZE, MOD_IR_SIZE);
+        failed = AL_TRUE;
+    }
+    if(evCount < MIN_EV_COUNT || evCount > MAX_EV_COUNT)
+    {
+        ERR("Unsupported elevation count: evCount=%d (%d to %d)\n",
+            evCount, MIN_EV_COUNT, MAX_EV_COUNT);
+        failed = AL_TRUE;
+    }
+    if(failed)
+        return NULL;
+
+    if(datalen < evCount)
+    {
+        ERR("Unexpected end of %s data (req %d, rem "SZFMT"\n",
+            al_string_get_cstr(filename), evCount, datalen);
+        return NULL;
+    }
+
+    azCount = data;
+    data += evCount;
+    datalen -= evCount;
+
+    evOffset = malloc(sizeof(evOffset[0])*evCount);
+    if(azCount == NULL || evOffset == NULL)
+    {
+        ERR("Out of memory.\n");
+        failed = AL_TRUE;
+    }
+
+    if(!failed)
+    {
+        for(i = 0;i < evCount;i++)
+        {
+            if(azCount[i] < MIN_AZ_COUNT || azCount[i] > MAX_AZ_COUNT)
+            {
+                ERR("Unsupported azimuth count: azCount[%d]=%d (%d to %d)\n",
+                    i, azCount[i], MIN_AZ_COUNT, MAX_AZ_COUNT);
+                failed = AL_TRUE;
+            }
+        }
+    }
+
+    if(!failed)
+    {
+        evOffset[0] = 0;
+        irCount = azCount[0];
+        for(i = 1;i < evCount;i++)
+        {
+            evOffset[i] = evOffset[i-1] + azCount[i-1];
+            irCount += azCount[i];
+        }
+
+        coeffs = malloc(sizeof(coeffs[0])*irSize*irCount);
+        if(coeffs == NULL)
+        {
+            ERR("Out of memory.\n");
+            failed = AL_TRUE;
+        }
+    }
+
+    if(!failed)
+    {
+        size_t reqsize = 2*irSize*irCount + irCount;
+        if(datalen < reqsize)
+        {
+            ERR("Unexpected end of %s data (req "SZFMT", rem "SZFMT"\n",
+                al_string_get_cstr(filename), reqsize, datalen);
+            failed = AL_TRUE;
+        }
+    }
+
+    if(!failed)
+    {
+        for(i = 0;i < irCount*irSize;i+=irSize)
+        {
+            for(j = 0;j < irSize;j++)
+            {
+                ALshort coeff;
+                coeff  = *(data++);
+                coeff |= *(data++)<<8;
+                datalen -= 2;
+                coeffs[i+j] = coeff;
+            }
+        }
+
+        delays = data;
+        data += irCount;
+        datalen -= irCount;
+        for(i = 0;i < irCount;i++)
+        {
+            if(delays[i] > maxDelay)
+            {
+                ERR("Invalid delays[%d]: %d (%d)\n", i, delays[i], maxDelay);
+                failed = AL_TRUE;
+            }
+        }
+    }
+
+    if(!failed)
+    {
+        size_t total = sizeof(struct Hrtf);
+        total += sizeof(azCount[0])*evCount;
+        total  = (total+1)&~1; /* Align for (u)short fields */
+        total += sizeof(evOffset[0])*evCount;
+        total += sizeof(coeffs[0])*irSize*irCount;
+        total += sizeof(delays[0])*irCount;
+        total += al_string_length(filename)+1;
+
+        Hrtf = al_calloc(16, total);
+        if(Hrtf == NULL)
+        {
+            ERR("Out of memory.\n");
+            failed = AL_TRUE;
+        }
+    }
+
+    if(!failed)
+    {
+        char *base = (char*)Hrtf;
+        uintptr_t offset = sizeof(*Hrtf);
+
+        Hrtf->sampleRate = rate;
+        Hrtf->irSize = irSize;
+        Hrtf->evCount = evCount;
+        Hrtf->azCount = ((ALubyte*)(base + offset)); offset += evCount*sizeof(Hrtf->azCount[0]);
+        offset = (offset+1)&~1; /* Align for (u)short fields */
+        Hrtf->evOffset = ((ALushort*)(base + offset)); offset += evCount*sizeof(Hrtf->evOffset[0]);
+        Hrtf->coeffs = ((ALshort*)(base + offset)); offset += irSize*irCount*sizeof(Hrtf->coeffs[0]);
+        Hrtf->delays = ((ALubyte*)(base + offset)); offset += irCount*sizeof(Hrtf->delays[0]);
+        Hrtf->filename = ((char*)(base + offset));
+        Hrtf->next = NULL;
+
+        memcpy((void*)Hrtf->azCount, azCount, sizeof(azCount[0])*evCount);
+        memcpy((void*)Hrtf->evOffset, evOffset, sizeof(evOffset[0])*evCount);
+        memcpy((void*)Hrtf->coeffs, coeffs, sizeof(coeffs[0])*irSize*irCount);
+        memcpy((void*)Hrtf->delays, delays, sizeof(delays[0])*irCount);
+        memcpy((void*)Hrtf->filename, al_string_get_cstr(filename), al_string_length(filename)+1);
+    }
+
+    free(evOffset);
+    free(coeffs);
+    return Hrtf;
+}
+
+static void AddFileEntry(vector_HrtfEntry *list, al_string *filename)
+{
+    HrtfEntry entry = { AL_STRING_INIT_STATIC(), NULL };
+    struct Hrtf *hrtf = NULL;
+    const HrtfEntry *iter;
+    struct FileMapping fmap;
+    const char *name;
+    const char *ext;
+    int i;
+
+#define MATCH_FNAME(i) (al_string_cmp_cstr(*filename, (i)->hrtf->filename) == 0)
+    VECTOR_FIND_IF(iter, const HrtfEntry, *list, MATCH_FNAME);
+    if(iter != VECTOR_END(*list))
+    {
+        TRACE("Skipping duplicate file entry %s\n", al_string_get_cstr(*filename));
+        goto done;
+    }
+#undef MATCH_FNAME
+
+    entry.hrtf = LoadedHrtfs;
+    while(entry.hrtf)
+    {
+        if(al_string_cmp_cstr(*filename, entry.hrtf->filename) == 0)
+        {
+            TRACE("Skipping load of already-loaded file %s\n", al_string_get_cstr(*filename));
+            goto skip_load;
+        }
+        entry.hrtf = entry.hrtf->next;
+    }
+
+    TRACE("Loading %s...\n", al_string_get_cstr(*filename));
+    fmap = MapFileToMem(al_string_get_cstr(*filename));
+    if(fmap.ptr == NULL)
+    {
+        ERR("Could not open %s\n", al_string_get_cstr(*filename));
+        goto done;
+    }
+
+    if(fmap.len < sizeof(magicMarker01))
+        ERR("%s data is too short ("SZFMT" bytes)\n", al_string_get_cstr(*filename), fmap.len);
+    else if(memcmp(fmap.ptr, magicMarker01, sizeof(magicMarker01)) == 0)
+    {
+        TRACE("Detected data set format v1\n");
+        hrtf = LoadHrtf01((const ALubyte*)fmap.ptr+sizeof(magicMarker01),
+            fmap.len-sizeof(magicMarker01), *filename
+        );
+    }
+    else if(memcmp(fmap.ptr, magicMarker00, sizeof(magicMarker00)) == 0)
+    {
+        TRACE("Detected data set format v0\n");
+        hrtf = LoadHrtf00((const ALubyte*)fmap.ptr+sizeof(magicMarker00),
+            fmap.len-sizeof(magicMarker00), *filename
+        );
+    }
+    else
+        ERR("Invalid header in %s: \"%.8s\"\n", al_string_get_cstr(*filename), (const char*)fmap.ptr);
+    UnmapFileMem(&fmap);
+
+    if(!hrtf)
+    {
+        ERR("Failed to load %s\n", al_string_get_cstr(*filename));
+        goto done;
+    }
+
+    hrtf->next = LoadedHrtfs;
+    LoadedHrtfs = hrtf;
+    TRACE("Loaded HRTF support for format: %s %uhz\n",
+            DevFmtChannelsString(DevFmtStereo), hrtf->sampleRate);
+    entry.hrtf = hrtf;
+
+skip_load:
+    /* TODO: Get a human-readable name from the HRTF data (possibly coming in a
+     * format update). */
+    name = strrchr(al_string_get_cstr(*filename), '/');
+    if(!name) name = strrchr(al_string_get_cstr(*filename), '\\');
+    if(!name) name = al_string_get_cstr(*filename);
+    else ++name;
+
+    ext = strrchr(name, '.');
+
+    i = 0;
+    do {
+        if(!ext)
+            al_string_copy_cstr(&entry.name, name);
+        else
+            al_string_copy_range(&entry.name, name, ext);
+        if(i != 0)
+        {
+            char str[64];
+            snprintf(str, sizeof(str), " #%d", i+1);
+            al_string_append_cstr(&entry.name, str);
+        }
+        ++i;
+
+#define MATCH_NAME(i)  (al_string_cmp(entry.name, (i)->name) == 0)
+        VECTOR_FIND_IF(iter, const HrtfEntry, *list, MATCH_NAME);
+#undef MATCH_NAME
+    } while(iter != VECTOR_END(*list));
+
+    TRACE("Adding entry \"%s\" from file \"%s\"\n", al_string_get_cstr(entry.name),
+          al_string_get_cstr(*filename));
+    VECTOR_PUSH_BACK(*list, entry);
+
+done:
+    al_string_deinit(filename);
+}
+
+/* Unfortunate that we have to duplicate AddFileEntry to take a memory buffer
+ * for input instead of opening the given filename.
+ */
+static void AddBuiltInEntry(vector_HrtfEntry *list, const ALubyte *data, size_t datalen, al_string *filename)
+{
+    HrtfEntry entry = { AL_STRING_INIT_STATIC(), NULL };
+    struct Hrtf *hrtf = NULL;
+    const HrtfEntry *iter;
+    int i;
+
+#define MATCH_FNAME(i) (al_string_cmp_cstr(*filename, (i)->hrtf->filename) == 0)
+    VECTOR_FIND_IF(iter, const HrtfEntry, *list, MATCH_FNAME);
+    if(iter != VECTOR_END(*list))
+    {
+        TRACE("Skipping duplicate file entry %s\n", al_string_get_cstr(*filename));
+        goto done;
+    }
+#undef MATCH_FNAME
+
+    entry.hrtf = LoadedHrtfs;
+    while(entry.hrtf)
+    {
+        if(al_string_cmp_cstr(*filename, entry.hrtf->filename) == 0)
+        {
+            TRACE("Skipping load of already-loaded file %s\n", al_string_get_cstr(*filename));
+            goto skip_load;
+        }
+        entry.hrtf = entry.hrtf->next;
+    }
+
+    TRACE("Loading %s...\n", al_string_get_cstr(*filename));
+    if(datalen < sizeof(magicMarker01))
+    {
+        ERR("%s data is too short ("SZFMT" bytes)\n", al_string_get_cstr(*filename), datalen);
+        goto done;
+    }
+
+    if(memcmp(data, magicMarker01, sizeof(magicMarker01)) == 0)
+    {
+        TRACE("Detected data set format v1\n");
+        hrtf = LoadHrtf01(data+sizeof(magicMarker01),
+            datalen-sizeof(magicMarker01), *filename
+        );
+    }
+    else if(memcmp(data, magicMarker00, sizeof(magicMarker00)) == 0)
+    {
+        TRACE("Detected data set format v0\n");
+        hrtf = LoadHrtf00(data+sizeof(magicMarker00),
+            datalen-sizeof(magicMarker00), *filename
+        );
+    }
+    else
+        ERR("Invalid header in %s: \"%.8s\"\n", al_string_get_cstr(*filename), data);
+
+    if(!hrtf)
+    {
+        ERR("Failed to load %s\n", al_string_get_cstr(*filename));
+        goto done;
+    }
+
+    hrtf->next = LoadedHrtfs;
+    LoadedHrtfs = hrtf;
+    TRACE("Loaded HRTF support for format: %s %uhz\n",
+            DevFmtChannelsString(DevFmtStereo), hrtf->sampleRate);
+    entry.hrtf = hrtf;
+
+skip_load:
+    i = 0;
+    do {
+        al_string_copy(&entry.name, *filename);
+        if(i != 0)
+        {
+            char str[64];
+            snprintf(str, sizeof(str), " #%d", i+1);
+            al_string_append_cstr(&entry.name, str);
+        }
+        ++i;
+
+#define MATCH_NAME(i)  (al_string_cmp(entry.name, (i)->name) == 0)
+        VECTOR_FIND_IF(iter, const HrtfEntry, *list, MATCH_NAME);
+#undef MATCH_NAME
+    } while(iter != VECTOR_END(*list));
+
+    TRACE("Adding built-in entry \"%s\"\n", al_string_get_cstr(entry.name));
+    VECTOR_PUSH_BACK(*list, entry);
+
+done:
+    al_string_deinit(filename);
+}
+
+
+#ifndef ALSOFT_EMBED_HRTF_DATA
+#define IDR_DEFAULT_44100_MHR 0
+#define IDR_DEFAULT_48000_MHR 1
+
+static const ALubyte *GetResource(int UNUSED(name), size_t *size)
+{
+    *size = 0;
+    return NULL;
+}
+
+#else
+#include "hrtf_res.h"
+
+#ifdef _WIN32
+static const ALubyte *GetResource(int name, size_t *size)
+{
+    HMODULE handle;
+    HGLOBAL res;
+    HRSRC rc;
+
+    GetModuleHandleExW(
+        GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT | GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
+        (LPCWSTR)GetResource, &handle
+    );
+    rc = FindResourceW(handle, MAKEINTRESOURCEW(name), MAKEINTRESOURCEW(MHRTYPE));
+    res = LoadResource(handle, rc);
+
+    *size = SizeofResource(handle, rc);
+    return LockResource(res);
+}
+
+#else
+
+extern const ALubyte _binary_default_44100_mhr_start[] HIDDEN_DECL;
+extern const ALubyte _binary_default_44100_mhr_end[] HIDDEN_DECL;
+extern const ALubyte _binary_default_44100_mhr_size[] HIDDEN_DECL;
+
+extern const ALubyte _binary_default_48000_mhr_start[] HIDDEN_DECL;
+extern const ALubyte _binary_default_48000_mhr_end[] HIDDEN_DECL;
+extern const ALubyte _binary_default_48000_mhr_size[] HIDDEN_DECL;
+
+static const ALubyte *GetResource(int name, size_t *size)
+{
+    if(name == IDR_DEFAULT_44100_MHR)
+    {
+        /* Make sure all symbols are referenced, to ensure the compiler won't
+         * ignore the declarations and lose the visibility attribute used to
+         * hide them (would be nice if ld or objcopy could automatically mark
+         * them as hidden when generating them, but apparently they can't).
+         */
+        const void *volatile ptr =_binary_default_44100_mhr_size;
+        (void)ptr;
+        *size = _binary_default_44100_mhr_end - _binary_default_44100_mhr_start;
+        return _binary_default_44100_mhr_start;
+    }
+    if(name == IDR_DEFAULT_48000_MHR)
+    {
+        const void *volatile ptr =_binary_default_48000_mhr_size;
+        (void)ptr;
+        *size = _binary_default_48000_mhr_end - _binary_default_48000_mhr_start;
+        return _binary_default_48000_mhr_start;
+    }
+    *size = 0;
+    return NULL;
+}
+#endif
+#endif
+
+vector_HrtfEntry EnumerateHrtf(const_al_string devname)
+{
+    vector_HrtfEntry list = VECTOR_INIT_STATIC();
+    const char *defaulthrtf = "";
+    const char *pathlist = "";
+    bool usedefaults = true;
+
+    if(ConfigValueStr(al_string_get_cstr(devname), NULL, "hrtf-paths", &pathlist))
+    {
+        while(pathlist && *pathlist)
+        {
+            const char *next, *end;
+
+            while(isspace(*pathlist) || *pathlist == ',')
+                pathlist++;
+            if(*pathlist == '\0')
+                continue;
+
+            next = strchr(pathlist, ',');
+            if(next)
+                end = next++;
+            else
+            {
+                end = pathlist + strlen(pathlist);
+                usedefaults = false;
+            }
+
+            while(end != pathlist && isspace(*(end-1)))
+                --end;
+            if(end != pathlist)
+            {
+                al_string pname = AL_STRING_INIT_STATIC();
+                vector_al_string flist;
+
+                al_string_append_range(&pname, pathlist, end);
+
+                flist = SearchDataFiles(".mhr", al_string_get_cstr(pname));
+                VECTOR_FOR_EACH_PARAMS(al_string, flist, AddFileEntry, &list);
+                VECTOR_DEINIT(flist);
+
+                al_string_deinit(&pname);
+            }
+
+            pathlist = next;
+        }
+    }
+    else if(ConfigValueExists(al_string_get_cstr(devname), NULL, "hrtf_tables"))
+        ERR("The hrtf_tables option is deprecated, please use hrtf-paths instead.\n");
+
+    if(usedefaults)
+    {
+        vector_al_string flist;
+        const ALubyte *rdata;
+        size_t rsize;
+
+        flist = SearchDataFiles(".mhr", "openal/hrtf");
+        VECTOR_FOR_EACH_PARAMS(al_string, flist, AddFileEntry, &list);
+        VECTOR_DEINIT(flist);
+
+        rdata = GetResource(IDR_DEFAULT_44100_MHR, &rsize);
+        if(rdata != NULL && rsize > 0)
+        {
+            al_string ename = AL_STRING_INIT_STATIC();
+            al_string_copy_cstr(&ename, "Built-In 44100hz");
+            AddBuiltInEntry(&list, rdata, rsize, &ename);
+        }
+
+        rdata = GetResource(IDR_DEFAULT_48000_MHR, &rsize);
+        if(rdata != NULL && rsize > 0)
+        {
+            al_string ename = AL_STRING_INIT_STATIC();
+            al_string_copy_cstr(&ename, "Built-In 48000hz");
+            AddBuiltInEntry(&list, rdata, rsize, &ename);
+        }
+    }
+
+    if(VECTOR_SIZE(list) > 1 && ConfigValueStr(al_string_get_cstr(devname), NULL, "default-hrtf", &defaulthrtf))
+    {
+        const HrtfEntry *iter;
+        /* Find the preferred HRTF and move it to the front of the list. */
+#define FIND_ENTRY(i)  (al_string_cmp_cstr((i)->name, defaulthrtf) == 0)
+        VECTOR_FIND_IF(iter, const HrtfEntry, list, FIND_ENTRY);
+#undef FIND_ENTRY
+        if(iter == VECTOR_END(list))
+            WARN("Failed to find default HRTF \"%s\"\n", defaulthrtf);
+        else if(iter != VECTOR_BEGIN(list))
+        {
+            HrtfEntry entry = *iter;
+            memmove(&VECTOR_ELEM(list,1), &VECTOR_ELEM(list,0),
+                    (iter-VECTOR_BEGIN(list))*sizeof(HrtfEntry));
+            VECTOR_ELEM(list,0) = entry;
+        }
+    }
+
+    return list;
+}
+
+void FreeHrtfList(vector_HrtfEntry *list)
+{
+#define CLEAR_ENTRY(i) do {           \
+    al_string_deinit(&(i)->name);     \
+} while(0)
+    VECTOR_FOR_EACH(HrtfEntry, *list, CLEAR_ENTRY);
+    VECTOR_DEINIT(*list);
+#undef CLEAR_ENTRY
+}
+
+
+void FreeHrtfs(void)
+{
+    struct Hrtf *Hrtf = LoadedHrtfs;
+    LoadedHrtfs = NULL;
+
+    while(Hrtf != NULL)
+    {
+        struct Hrtf *next = Hrtf->next;
+        al_free(Hrtf);
+        Hrtf = next;
+    }
+}

+ 52 - 0
Engine/lib/openal-soft/Alc/hrtf.h

@@ -0,0 +1,52 @@
+#ifndef ALC_HRTF_H
+#define ALC_HRTF_H
+
+#include "AL/al.h"
+#include "AL/alc.h"
+
+#include "alstring.h"
+
+
+struct Hrtf {
+    ALuint sampleRate;
+    ALuint irSize;
+    ALubyte evCount;
+
+    const ALubyte *azCount;
+    const ALushort *evOffset;
+    const ALshort *coeffs;
+    const ALubyte *delays;
+
+    const char *filename;
+    struct Hrtf *next;
+};
+
+typedef struct HrtfEntry {
+    al_string name;
+
+    const struct Hrtf *hrtf;
+} HrtfEntry;
+TYPEDEF_VECTOR(HrtfEntry, vector_HrtfEntry)
+
+#define HRIR_BITS        (7)
+#define HRIR_LENGTH      (1<<HRIR_BITS)
+#define HRIR_MASK        (HRIR_LENGTH-1)
+#define HRTFDELAY_BITS    (20)
+#define HRTFDELAY_FRACONE (1<<HRTFDELAY_BITS)
+#define HRTFDELAY_MASK    (HRTFDELAY_FRACONE-1)
+
+void FreeHrtfs(void);
+
+vector_HrtfEntry EnumerateHrtf(const_al_string devname);
+void FreeHrtfList(vector_HrtfEntry *list);
+
+void GetHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azimuth, ALfloat spread, ALfloat gain, ALfloat (*coeffs)[2], ALuint *delays);
+
+/* Produces HRTF filter coefficients for decoding B-Format. The result will
+ * have ACN ordering with N3D normalization. NumChannels must currently be 4,
+ * for first-order. Returns the maximum impulse-response length of the
+ * generated coefficients.
+ */
+ALuint BuildBFormatHrtf(const struct Hrtf *Hrtf, ALfloat (*coeffs)[HRIR_LENGTH][2], ALuint NumChannels);
+
+#endif /* ALC_HRTF_H */

+ 5 - 0
Engine/lib/openal-soft/Alc/hrtf_res.h

@@ -0,0 +1,5 @@
+
+#define MHRTYPE 256
+
+#define IDR_DEFAULT_44100_MHR 0
+#define IDR_DEFAULT_48000_MHR 1

+ 4 - 0
Engine/lib/openal-soft/Alc/hrtf_res.rc

@@ -0,0 +1,4 @@
+#include "hrtf_res.h"
+
+IDR_DEFAULT_44100_MHR MHRTYPE "../hrtf/default-44100.mhr"
+IDR_DEFAULT_48000_MHR MHRTYPE "../hrtf/default-48000.mhr"

+ 702 - 0
Engine/lib/openal-soft/Alc/mixer.c

@@ -0,0 +1,702 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 1999-2007 by authors.
+ * This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the
+ *  Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#include "config.h"
+
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+
+#include "alMain.h"
+#include "AL/al.h"
+#include "AL/alc.h"
+#include "alSource.h"
+#include "alBuffer.h"
+#include "alListener.h"
+#include "alAuxEffectSlot.h"
+#include "alu.h"
+
+#include "mixer_defs.h"
+
+
+static_assert((INT_MAX>>FRACTIONBITS)/MAX_PITCH > BUFFERSIZE,
+              "MAX_PITCH and/or BUFFERSIZE are too large for FRACTIONBITS!");
+
+extern inline void InitiatePositionArrays(ALuint frac, ALuint increment, ALuint *restrict frac_arr, ALuint *restrict pos_arr, ALuint size);
+
+alignas(16) union ResamplerCoeffs ResampleCoeffs;
+
+
+enum Resampler {
+    PointResampler,
+    LinearResampler,
+    FIR4Resampler,
+    FIR8Resampler,
+    BSincResampler,
+
+    ResamplerDefault = LinearResampler
+};
+
+/* FIR8 requires 3 extra samples before the current position, and 4 after. */
+static_assert(MAX_PRE_SAMPLES >= 3, "MAX_PRE_SAMPLES must be at least 3!");
+static_assert(MAX_POST_SAMPLES >= 4, "MAX_POST_SAMPLES must be at least 4!");
+
+
+static MixerFunc MixSamples = Mix_C;
+static HrtfMixerFunc MixHrtfSamples = MixHrtf_C;
+static ResamplerFunc ResampleSamples = Resample_point32_C;
+
+MixerFunc SelectMixer(void)
+{
+#ifdef HAVE_SSE
+    if((CPUCapFlags&CPU_CAP_SSE))
+        return Mix_SSE;
+#endif
+#ifdef HAVE_NEON
+    if((CPUCapFlags&CPU_CAP_NEON))
+        return Mix_Neon;
+#endif
+
+    return Mix_C;
+}
+
+RowMixerFunc SelectRowMixer(void)
+{
+#ifdef HAVE_SSE
+    if((CPUCapFlags&CPU_CAP_SSE))
+        return MixRow_SSE;
+#endif
+#ifdef HAVE_NEON
+    if((CPUCapFlags&CPU_CAP_NEON))
+        return MixRow_Neon;
+#endif
+    return MixRow_C;
+}
+
+static inline HrtfMixerFunc SelectHrtfMixer(void)
+{
+#ifdef HAVE_SSE
+    if((CPUCapFlags&CPU_CAP_SSE))
+        return MixHrtf_SSE;
+#endif
+#ifdef HAVE_NEON
+    if((CPUCapFlags&CPU_CAP_NEON))
+        return MixHrtf_Neon;
+#endif
+
+    return MixHrtf_C;
+}
+
+static inline ResamplerFunc SelectResampler(enum Resampler resampler)
+{
+    switch(resampler)
+    {
+        case PointResampler:
+            return Resample_point32_C;
+        case LinearResampler:
+#ifdef HAVE_SSE4_1
+            if((CPUCapFlags&CPU_CAP_SSE4_1))
+                return Resample_lerp32_SSE41;
+#endif
+#ifdef HAVE_SSE2
+            if((CPUCapFlags&CPU_CAP_SSE2))
+                return Resample_lerp32_SSE2;
+#endif
+            return Resample_lerp32_C;
+        case FIR4Resampler:
+#ifdef HAVE_SSE4_1
+            if((CPUCapFlags&CPU_CAP_SSE4_1))
+                return Resample_fir4_32_SSE41;
+#endif
+#ifdef HAVE_SSE3
+            if((CPUCapFlags&CPU_CAP_SSE3))
+                return Resample_fir4_32_SSE3;
+#endif
+            return Resample_fir4_32_C;
+        case FIR8Resampler:
+#ifdef HAVE_SSE4_1
+            if((CPUCapFlags&CPU_CAP_SSE4_1))
+                return Resample_fir8_32_SSE41;
+#endif
+#ifdef HAVE_SSE3
+            if((CPUCapFlags&CPU_CAP_SSE3))
+                return Resample_fir8_32_SSE3;
+#endif
+            return Resample_fir8_32_C;
+        case BSincResampler:
+#ifdef HAVE_SSE
+            if((CPUCapFlags&CPU_CAP_SSE))
+                return Resample_bsinc32_SSE;
+#endif
+            return Resample_bsinc32_C;
+    }
+
+    return Resample_point32_C;
+}
+
+
+/* The sinc resampler makes use of a Kaiser window to limit the needed sample
+ * points to 4 and 8, respectively.
+ */
+
+#ifndef M_PI
+#define M_PI                         (3.14159265358979323846)
+#endif
+static inline double Sinc(double x)
+{
+    if(x == 0.0) return 1.0;
+    return sin(x*M_PI) / (x*M_PI);
+}
+
+/* The zero-order modified Bessel function of the first kind, used for the
+ * Kaiser window.
+ *
+ *   I_0(x) = sum_{k=0}^inf (1 / k!)^2 (x / 2)^(2 k)
+ *          = sum_{k=0}^inf ((x / 2)^k / k!)^2
+ */
+static double BesselI_0(double x)
+{
+    double term, sum, x2, y, last_sum;
+    int k;
+
+    /* Start at k=1 since k=0 is trivial. */
+    term = 1.0;
+    sum = 1.0;
+    x2 = x / 2.0;
+    k = 1;
+
+    /* Let the integration converge until the term of the sum is no longer
+     * significant.
+     */
+    do {
+        y = x2 / k;
+        k ++;
+        last_sum = sum;
+        term *= y * y;
+        sum += term;
+    } while(sum != last_sum);
+    return sum;
+}
+
+/* Calculate a Kaiser window from the given beta value and a normalized k
+ * [-1, 1].
+ *
+ *   w(k) = { I_0(B sqrt(1 - k^2)) / I_0(B),  -1 <= k <= 1
+ *          { 0,                              elsewhere.
+ *
+ * Where k can be calculated as:
+ *
+ *   k = i / l,         where -l <= i <= l.
+ *
+ * or:
+ *
+ *   k = 2 i / M - 1,   where 0 <= i <= M.
+ */
+static inline double Kaiser(double b, double k)
+{
+    if(k <= -1.0 || k >= 1.0) return 0.0;
+    return BesselI_0(b * sqrt(1.0 - (k*k))) / BesselI_0(b);
+}
+
+static inline double CalcKaiserBeta(double rejection)
+{
+    if(rejection > 50.0)
+        return 0.1102 * (rejection - 8.7);
+    if(rejection >= 21.0)
+        return (0.5842 * pow(rejection - 21.0, 0.4)) +
+               (0.07886 * (rejection - 21.0));
+    return 0.0;
+}
+
+static float SincKaiser(double r, double x)
+{
+    /* Limit rippling to -60dB. */
+    return (float)(Kaiser(CalcKaiserBeta(60.0), x / r) * Sinc(x));
+}
+
+
+void aluInitMixer(void)
+{
+    enum Resampler resampler = ResamplerDefault;
+    const char *str;
+    ALuint i;
+
+    if(ConfigValueStr(NULL, NULL, "resampler", &str))
+    {
+        if(strcasecmp(str, "point") == 0 || strcasecmp(str, "none") == 0)
+            resampler = PointResampler;
+        else if(strcasecmp(str, "linear") == 0)
+            resampler = LinearResampler;
+        else if(strcasecmp(str, "sinc4") == 0)
+            resampler = FIR4Resampler;
+        else if(strcasecmp(str, "sinc8") == 0)
+            resampler = FIR8Resampler;
+        else if(strcasecmp(str, "bsinc") == 0)
+            resampler = BSincResampler;
+        else if(strcasecmp(str, "cubic") == 0)
+        {
+            WARN("Resampler option \"cubic\" is deprecated, using sinc4\n");
+            resampler = FIR4Resampler;
+        }
+        else
+        {
+            char *end;
+            long n = strtol(str, &end, 0);
+            if(*end == '\0' && (n == PointResampler || n == LinearResampler || n == FIR4Resampler))
+                resampler = n;
+            else
+                WARN("Invalid resampler: %s\n", str);
+        }
+    }
+
+    if(resampler == FIR8Resampler)
+        for(i = 0;i < FRACTIONONE;i++)
+        {
+            ALdouble mu = (ALdouble)i / FRACTIONONE;
+            ResampleCoeffs.FIR8[i][0] = SincKaiser(4.0, mu - -3.0);
+            ResampleCoeffs.FIR8[i][1] = SincKaiser(4.0, mu - -2.0);
+            ResampleCoeffs.FIR8[i][2] = SincKaiser(4.0, mu - -1.0);
+            ResampleCoeffs.FIR8[i][3] = SincKaiser(4.0, mu -  0.0);
+            ResampleCoeffs.FIR8[i][4] = SincKaiser(4.0, mu -  1.0);
+            ResampleCoeffs.FIR8[i][5] = SincKaiser(4.0, mu -  2.0);
+            ResampleCoeffs.FIR8[i][6] = SincKaiser(4.0, mu -  3.0);
+            ResampleCoeffs.FIR8[i][7] = SincKaiser(4.0, mu -  4.0);
+        }
+    else if(resampler == FIR4Resampler)
+        for(i = 0;i < FRACTIONONE;i++)
+        {
+            ALdouble mu = (ALdouble)i / FRACTIONONE;
+            ResampleCoeffs.FIR4[i][0] = SincKaiser(2.0, mu - -1.0);
+            ResampleCoeffs.FIR4[i][1] = SincKaiser(2.0, mu -  0.0);
+            ResampleCoeffs.FIR4[i][2] = SincKaiser(2.0, mu -  1.0);
+            ResampleCoeffs.FIR4[i][3] = SincKaiser(2.0, mu -  2.0);
+        }
+
+    MixHrtfSamples = SelectHrtfMixer();
+    MixSamples = SelectMixer();
+    ResampleSamples = SelectResampler(resampler);
+}
+
+
+static inline ALfloat Sample_ALbyte(ALbyte val)
+{ return val * (1.0f/127.0f); }
+
+static inline ALfloat Sample_ALshort(ALshort val)
+{ return val * (1.0f/32767.0f); }
+
+static inline ALfloat Sample_ALfloat(ALfloat val)
+{ return val; }
+
+#define DECL_TEMPLATE(T)                                                      \
+static inline void Load_##T(ALfloat *dst, const T *src, ALuint srcstep, ALuint samples)\
+{                                                                             \
+    ALuint i;                                                                 \
+    for(i = 0;i < samples;i++)                                                \
+        dst[i] = Sample_##T(src[i*srcstep]);                                  \
+}
+
+DECL_TEMPLATE(ALbyte)
+DECL_TEMPLATE(ALshort)
+DECL_TEMPLATE(ALfloat)
+
+#undef DECL_TEMPLATE
+
+static void LoadSamples(ALfloat *dst, const ALvoid *src, ALuint srcstep, enum FmtType srctype, ALuint samples)
+{
+    switch(srctype)
+    {
+        case FmtByte:
+            Load_ALbyte(dst, src, srcstep, samples);
+            break;
+        case FmtShort:
+            Load_ALshort(dst, src, srcstep, samples);
+            break;
+        case FmtFloat:
+            Load_ALfloat(dst, src, srcstep, samples);
+            break;
+    }
+}
+
+static inline void SilenceSamples(ALfloat *dst, ALuint samples)
+{
+    ALuint i;
+    for(i = 0;i < samples;i++)
+        dst[i] = 0.0f;
+}
+
+
+static const ALfloat *DoFilters(ALfilterState *lpfilter, ALfilterState *hpfilter,
+                                ALfloat *restrict dst, const ALfloat *restrict src,
+                                ALuint numsamples, enum ActiveFilters type)
+{
+    ALuint i;
+    switch(type)
+    {
+        case AF_None:
+            ALfilterState_processPassthru(lpfilter, src, numsamples);
+            ALfilterState_processPassthru(hpfilter, src, numsamples);
+            break;
+
+        case AF_LowPass:
+            ALfilterState_process(lpfilter, dst, src, numsamples);
+            ALfilterState_processPassthru(hpfilter, dst, numsamples);
+            return dst;
+        case AF_HighPass:
+            ALfilterState_processPassthru(lpfilter, src, numsamples);
+            ALfilterState_process(hpfilter, dst, src, numsamples);
+            return dst;
+
+        case AF_BandPass:
+            for(i = 0;i < numsamples;)
+            {
+                ALfloat temp[256];
+                ALuint todo = minu(256, numsamples-i);
+
+                ALfilterState_process(lpfilter, temp, src+i, todo);
+                ALfilterState_process(hpfilter, dst+i, temp, todo);
+                i += todo;
+            }
+            return dst;
+    }
+    return src;
+}
+
+
+ALvoid MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALuint SamplesToDo)
+{
+    ResamplerFunc Resample;
+    ALbufferlistitem *BufferListItem;
+    ALuint DataPosInt, DataPosFrac;
+    ALboolean Looping;
+    ALuint increment;
+    ALenum State;
+    ALuint OutPos;
+    ALuint NumChannels;
+    ALuint SampleSize;
+    ALint64 DataSize64;
+    ALuint Counter;
+    ALuint IrSize;
+    ALuint chan, send, j;
+
+    /* Get source info */
+    State          = AL_PLAYING; /* Only called while playing. */
+    BufferListItem = ATOMIC_LOAD(&Source->current_buffer);
+    DataPosInt     = ATOMIC_LOAD(&Source->position, almemory_order_relaxed);
+    DataPosFrac    = ATOMIC_LOAD(&Source->position_fraction, almemory_order_relaxed);
+    Looping        = ATOMIC_LOAD(&Source->looping, almemory_order_relaxed);
+    NumChannels    = Source->NumChannels;
+    SampleSize     = Source->SampleSize;
+    increment      = voice->Step;
+
+    IrSize = (Device->Hrtf.Handle ? Device->Hrtf.Handle->irSize : 0);
+
+    Resample = ((increment == FRACTIONONE && DataPosFrac == 0) ?
+                Resample_copy32_C : ResampleSamples);
+
+    Counter = voice->Moving ? SamplesToDo : 0;
+    OutPos = 0;
+    do {
+        ALuint SrcBufferSize, DstBufferSize;
+
+        /* Figure out how many buffer samples will be needed */
+        DataSize64  = SamplesToDo-OutPos;
+        DataSize64 *= increment;
+        DataSize64 += DataPosFrac+FRACTIONMASK;
+        DataSize64 >>= FRACTIONBITS;
+        DataSize64 += MAX_POST_SAMPLES+MAX_PRE_SAMPLES;
+
+        SrcBufferSize = (ALuint)mini64(DataSize64, BUFFERSIZE);
+
+        /* Figure out how many samples we can actually mix from this. */
+        DataSize64  = SrcBufferSize;
+        DataSize64 -= MAX_POST_SAMPLES+MAX_PRE_SAMPLES;
+        DataSize64 <<= FRACTIONBITS;
+        DataSize64 -= DataPosFrac;
+
+        DstBufferSize = (ALuint)((DataSize64+(increment-1)) / increment);
+        DstBufferSize = minu(DstBufferSize, (SamplesToDo-OutPos));
+
+        /* Some mixers like having a multiple of 4, so try to give that unless
+         * this is the last update. */
+        if(OutPos+DstBufferSize < SamplesToDo)
+            DstBufferSize &= ~3;
+
+        for(chan = 0;chan < NumChannels;chan++)
+        {
+            const ALfloat *ResampledData;
+            ALfloat *SrcData = Device->SourceData;
+            ALuint SrcDataSize;
+
+            /* Load the previous samples into the source data first. */
+            memcpy(SrcData, voice->PrevSamples[chan], MAX_PRE_SAMPLES*sizeof(ALfloat));
+            SrcDataSize = MAX_PRE_SAMPLES;
+
+            if(Source->SourceType == AL_STATIC)
+            {
+                const ALbuffer *ALBuffer = BufferListItem->buffer;
+                const ALubyte *Data = ALBuffer->data;
+                ALuint DataSize;
+                ALuint pos;
+
+                /* Offset buffer data to current channel */
+                Data += chan*SampleSize;
+
+                /* If current pos is beyond the loop range, do not loop */
+                if(Looping == AL_FALSE || DataPosInt >= (ALuint)ALBuffer->LoopEnd)
+                {
+                    Looping = AL_FALSE;
+
+                    /* Load what's left to play from the source buffer, and
+                     * clear the rest of the temp buffer */
+                    pos = DataPosInt;
+                    DataSize = minu(SrcBufferSize - SrcDataSize, ALBuffer->SampleLen - pos);
+
+                    LoadSamples(&SrcData[SrcDataSize], &Data[pos * NumChannels*SampleSize],
+                                NumChannels, ALBuffer->FmtType, DataSize);
+                    SrcDataSize += DataSize;
+
+                    SilenceSamples(&SrcData[SrcDataSize], SrcBufferSize - SrcDataSize);
+                    SrcDataSize += SrcBufferSize - SrcDataSize;
+                }
+                else
+                {
+                    ALuint LoopStart = ALBuffer->LoopStart;
+                    ALuint LoopEnd   = ALBuffer->LoopEnd;
+
+                    /* Load what's left of this loop iteration, then load
+                     * repeats of the loop section */
+                    pos = DataPosInt;
+                    DataSize = LoopEnd - pos;
+                    DataSize = minu(SrcBufferSize - SrcDataSize, DataSize);
+
+                    LoadSamples(&SrcData[SrcDataSize], &Data[pos * NumChannels*SampleSize],
+                                NumChannels, ALBuffer->FmtType, DataSize);
+                    SrcDataSize += DataSize;
+
+                    DataSize = LoopEnd-LoopStart;
+                    while(SrcBufferSize > SrcDataSize)
+                    {
+                        DataSize = minu(SrcBufferSize - SrcDataSize, DataSize);
+
+                        LoadSamples(&SrcData[SrcDataSize], &Data[LoopStart * NumChannels*SampleSize],
+                                    NumChannels, ALBuffer->FmtType, DataSize);
+                        SrcDataSize += DataSize;
+                    }
+                }
+            }
+            else
+            {
+                /* Crawl the buffer queue to fill in the temp buffer */
+                ALbufferlistitem *tmpiter = BufferListItem;
+                ALuint pos = DataPosInt;
+
+                while(tmpiter && SrcBufferSize > SrcDataSize)
+                {
+                    const ALbuffer *ALBuffer;
+                    if((ALBuffer=tmpiter->buffer) != NULL)
+                    {
+                        const ALubyte *Data = ALBuffer->data;
+                        ALuint DataSize = ALBuffer->SampleLen;
+
+                        /* Skip the data already played */
+                        if(DataSize <= pos)
+                            pos -= DataSize;
+                        else
+                        {
+                            Data += (pos*NumChannels + chan)*SampleSize;
+                            DataSize -= pos;
+                            pos -= pos;
+
+                            DataSize = minu(SrcBufferSize - SrcDataSize, DataSize);
+                            LoadSamples(&SrcData[SrcDataSize], Data, NumChannels,
+                                        ALBuffer->FmtType, DataSize);
+                            SrcDataSize += DataSize;
+                        }
+                    }
+                    tmpiter = tmpiter->next;
+                    if(!tmpiter && Looping)
+                        tmpiter = ATOMIC_LOAD(&Source->queue);
+                    else if(!tmpiter)
+                    {
+                        SilenceSamples(&SrcData[SrcDataSize], SrcBufferSize - SrcDataSize);
+                        SrcDataSize += SrcBufferSize - SrcDataSize;
+                    }
+                }
+            }
+
+            /* Store the last source samples used for next time. */
+            memcpy(voice->PrevSamples[chan],
+                &SrcData[(increment*DstBufferSize + DataPosFrac)>>FRACTIONBITS],
+                MAX_PRE_SAMPLES*sizeof(ALfloat)
+            );
+
+            /* Now resample, then filter and mix to the appropriate outputs. */
+            ResampledData = Resample(&voice->SincState,
+                &SrcData[MAX_PRE_SAMPLES], DataPosFrac, increment,
+                Device->ResampledData, DstBufferSize
+            );
+            {
+                DirectParams *parms = &voice->Chan[chan].Direct;
+                const ALfloat *samples;
+
+                samples = DoFilters(
+                    &parms->LowPass, &parms->HighPass, Device->FilteredData,
+                    ResampledData, DstBufferSize, parms->FilterType
+                );
+                if(!voice->IsHrtf)
+                {
+                    if(!Counter)
+                        memcpy(parms->Gains.Current, parms->Gains.Target,
+                               sizeof(parms->Gains.Current));
+                    MixSamples(samples, voice->DirectOut.Channels, voice->DirectOut.Buffer,
+                        parms->Gains.Current, parms->Gains.Target, Counter, OutPos, DstBufferSize
+                    );
+                }
+                else
+                {
+                    MixHrtfParams hrtfparams;
+                    int lidx, ridx;
+
+                    if(!Counter)
+                    {
+                        parms->Hrtf.Current = parms->Hrtf.Target;
+                        for(j = 0;j < HRIR_LENGTH;j++)
+                        {
+                            hrtfparams.Steps.Coeffs[j][0] = 0.0f;
+                            hrtfparams.Steps.Coeffs[j][1] = 0.0f;
+                        }
+                        hrtfparams.Steps.Delay[0] = 0;
+                        hrtfparams.Steps.Delay[1] = 0;
+                    }
+                    else
+                    {
+                        ALfloat delta = 1.0f / (ALfloat)Counter;
+                        ALfloat coeffdiff;
+                        ALint delaydiff;
+                        for(j = 0;j < IrSize;j++)
+                        {
+                            coeffdiff = parms->Hrtf.Target.Coeffs[j][0] - parms->Hrtf.Current.Coeffs[j][0];
+                            hrtfparams.Steps.Coeffs[j][0] = coeffdiff * delta;
+                            coeffdiff = parms->Hrtf.Target.Coeffs[j][1] - parms->Hrtf.Current.Coeffs[j][1];
+                            hrtfparams.Steps.Coeffs[j][1] = coeffdiff * delta;
+                        }
+                        delaydiff = (ALint)(parms->Hrtf.Target.Delay[0] - parms->Hrtf.Current.Delay[0]);
+                        hrtfparams.Steps.Delay[0] = fastf2i((ALfloat)delaydiff * delta);
+                        delaydiff = (ALint)(parms->Hrtf.Target.Delay[1] - parms->Hrtf.Current.Delay[1]);
+                        hrtfparams.Steps.Delay[1] = fastf2i((ALfloat)delaydiff * delta);
+                    }
+                    hrtfparams.Target = &parms->Hrtf.Target;
+                    hrtfparams.Current = &parms->Hrtf.Current;
+
+                    lidx = GetChannelIdxByName(Device->RealOut, FrontLeft);
+                    ridx = GetChannelIdxByName(Device->RealOut, FrontRight);
+                    assert(lidx != -1 && ridx != -1);
+
+                    MixHrtfSamples(voice->DirectOut.Buffer, lidx, ridx, samples, Counter,
+                                   voice->Offset, OutPos, IrSize, &hrtfparams,
+                                   &parms->Hrtf.State, DstBufferSize);
+                }
+            }
+
+            for(send = 0;send < Device->NumAuxSends;send++)
+            {
+                SendParams *parms = &voice->Chan[chan].Send[send];
+                const ALfloat *samples;
+
+                if(!voice->SendOut[send].Buffer)
+                    continue;
+
+                samples = DoFilters(
+                    &parms->LowPass, &parms->HighPass, Device->FilteredData,
+                    ResampledData, DstBufferSize, parms->FilterType
+                );
+
+                if(!Counter)
+                    memcpy(parms->Gains.Current, parms->Gains.Target,
+                           sizeof(parms->Gains.Current));
+                MixSamples(samples, voice->SendOut[send].Channels, voice->SendOut[send].Buffer,
+                    parms->Gains.Current, parms->Gains.Target, Counter, OutPos, DstBufferSize
+                );
+            }
+        }
+        /* Update positions */
+        DataPosFrac += increment*DstBufferSize;
+        DataPosInt  += DataPosFrac>>FRACTIONBITS;
+        DataPosFrac &= FRACTIONMASK;
+
+        OutPos += DstBufferSize;
+        voice->Offset += DstBufferSize;
+        Counter = maxu(DstBufferSize, Counter) - DstBufferSize;
+
+        /* Handle looping sources */
+        while(1)
+        {
+            const ALbuffer *ALBuffer;
+            ALuint DataSize = 0;
+            ALuint LoopStart = 0;
+            ALuint LoopEnd = 0;
+
+            if((ALBuffer=BufferListItem->buffer) != NULL)
+            {
+                DataSize = ALBuffer->SampleLen;
+                LoopStart = ALBuffer->LoopStart;
+                LoopEnd = ALBuffer->LoopEnd;
+                if(LoopEnd > DataPosInt)
+                    break;
+            }
+
+            if(Looping && Source->SourceType == AL_STATIC)
+            {
+                assert(LoopEnd > LoopStart);
+                DataPosInt = ((DataPosInt-LoopStart)%(LoopEnd-LoopStart)) + LoopStart;
+                break;
+            }
+
+            if(DataSize > DataPosInt)
+                break;
+
+            if(!(BufferListItem=BufferListItem->next))
+            {
+                if(Looping)
+                    BufferListItem = ATOMIC_LOAD(&Source->queue);
+                else
+                {
+                    State = AL_STOPPED;
+                    BufferListItem = NULL;
+                    DataPosInt = 0;
+                    DataPosFrac = 0;
+                    break;
+                }
+            }
+
+            DataPosInt -= DataSize;
+        }
+    } while(State == AL_PLAYING && OutPos < SamplesToDo);
+
+    voice->Moving = AL_TRUE;
+
+    /* Update source info */
+    Source->state = State;
+    ATOMIC_STORE(&Source->current_buffer,    BufferListItem, almemory_order_relaxed);
+    ATOMIC_STORE(&Source->position,          DataPosInt, almemory_order_relaxed);
+    ATOMIC_STORE(&Source->position_fraction, DataPosFrac);
+}

+ 228 - 0
Engine/lib/openal-soft/Alc/mixer_c.c

@@ -0,0 +1,228 @@
+#include "config.h"
+
+#include <assert.h>
+
+#include "alMain.h"
+#include "alu.h"
+#include "alSource.h"
+#include "alAuxEffectSlot.h"
+
+
+static inline ALfloat point32(const ALfloat *restrict vals, ALuint UNUSED(frac))
+{ return vals[0]; }
+static inline ALfloat lerp32(const ALfloat *restrict vals, ALuint frac)
+{ return lerp(vals[0], vals[1], frac * (1.0f/FRACTIONONE)); }
+static inline ALfloat fir4_32(const ALfloat *restrict vals, ALuint frac)
+{ return resample_fir4(vals[-1], vals[0], vals[1], vals[2], frac); }
+static inline ALfloat fir8_32(const ALfloat *restrict vals, ALuint frac)
+{ return resample_fir8(vals[-3], vals[-2], vals[-1], vals[0], vals[1], vals[2], vals[3], vals[4], frac); }
+
+
+const ALfloat *Resample_copy32_C(const BsincState* UNUSED(state), const ALfloat *restrict src, ALuint UNUSED(frac),
+  ALuint UNUSED(increment), ALfloat *restrict dst, ALuint numsamples)
+{
+#if defined(HAVE_SSE) || defined(HAVE_NEON)
+    /* Avoid copying the source data if it's aligned like the destination. */
+    if((((intptr_t)src)&15) == (((intptr_t)dst)&15))
+        return src;
+#endif
+    memcpy(dst, src, numsamples*sizeof(ALfloat));
+    return dst;
+}
+
+#define DECL_TEMPLATE(Sampler)                                                \
+const ALfloat *Resample_##Sampler##_C(const BsincState* UNUSED(state),        \
+  const ALfloat *restrict src, ALuint frac, ALuint increment,                 \
+  ALfloat *restrict dst, ALuint numsamples)                                   \
+{                                                                             \
+    ALuint i;                                                                 \
+    for(i = 0;i < numsamples;i++)                                             \
+    {                                                                         \
+        dst[i] = Sampler(src, frac);                                          \
+                                                                              \
+        frac += increment;                                                    \
+        src  += frac>>FRACTIONBITS;                                           \
+        frac &= FRACTIONMASK;                                                 \
+    }                                                                         \
+    return dst;                                                               \
+}
+
+DECL_TEMPLATE(point32)
+DECL_TEMPLATE(lerp32)
+DECL_TEMPLATE(fir4_32)
+DECL_TEMPLATE(fir8_32)
+
+#undef DECL_TEMPLATE
+
+const ALfloat *Resample_bsinc32_C(const BsincState *state, const ALfloat *restrict src,
+                                  ALuint frac, ALuint increment, ALfloat *restrict dst,
+                                  ALuint dstlen)
+{
+    const ALfloat *fil, *scd, *phd, *spd;
+    const ALfloat sf = state->sf;
+    const ALuint m = state->m;
+    const ALint l = state->l;
+    ALuint j_f, pi, i;
+    ALfloat pf, r;
+    ALint j_s;
+
+    for(i = 0;i < dstlen;i++)
+    {
+        // Calculate the phase index and factor.
+#define FRAC_PHASE_BITDIFF (FRACTIONBITS-BSINC_PHASE_BITS)
+        pi = frac >> FRAC_PHASE_BITDIFF;
+        pf = (frac & ((1<<FRAC_PHASE_BITDIFF)-1)) * (1.0f/(1<<FRAC_PHASE_BITDIFF));
+#undef FRAC_PHASE_BITDIFF
+
+        fil = state->coeffs[pi].filter;
+        scd = state->coeffs[pi].scDelta;
+        phd = state->coeffs[pi].phDelta;
+        spd = state->coeffs[pi].spDelta;
+
+        // Apply the scale and phase interpolated filter.
+        r = 0.0f;
+        for(j_f = 0,j_s = l;j_f < m;j_f++,j_s++)
+            r += (fil[j_f] + sf*scd[j_f] + pf*(phd[j_f] + sf*spd[j_f])) *
+                    src[j_s];
+        dst[i] = r;
+
+        frac += increment;
+        src  += frac>>FRACTIONBITS;
+        frac &= FRACTIONMASK;
+    }
+    return dst;
+}
+
+
+void ALfilterState_processC(ALfilterState *filter, ALfloat *restrict dst, const ALfloat *restrict src, ALuint numsamples)
+{
+    ALuint i;
+    if(numsamples > 1)
+    {
+        dst[0] = filter->b0 * src[0] +
+                 filter->b1 * filter->x[0] +
+                 filter->b2 * filter->x[1] -
+                 filter->a1 * filter->y[0] -
+                 filter->a2 * filter->y[1];
+        dst[1] = filter->b0 * src[1] +
+                 filter->b1 * src[0] +
+                 filter->b2 * filter->x[0] -
+                 filter->a1 * dst[0] -
+                 filter->a2 * filter->y[0];
+        for(i = 2;i < numsamples;i++)
+            dst[i] = filter->b0 * src[i] +
+                     filter->b1 * src[i-1] +
+                     filter->b2 * src[i-2] -
+                     filter->a1 * dst[i-1] -
+                     filter->a2 * dst[i-2];
+        filter->x[0] = src[i-1];
+        filter->x[1] = src[i-2];
+        filter->y[0] = dst[i-1];
+        filter->y[1] = dst[i-2];
+    }
+    else if(numsamples == 1)
+    {
+        dst[0] = filter->b0 * src[0] +
+                 filter->b1 * filter->x[0] +
+                 filter->b2 * filter->x[1] -
+                 filter->a1 * filter->y[0] -
+                 filter->a2 * filter->y[1];
+        filter->x[1] = filter->x[0];
+        filter->x[0] = src[0];
+        filter->y[1] = filter->y[0];
+        filter->y[0] = dst[0];
+    }
+}
+
+
+static inline void ApplyCoeffsStep(ALuint Offset, ALfloat (*restrict Values)[2],
+                                   const ALuint IrSize,
+                                   ALfloat (*restrict Coeffs)[2],
+                                   const ALfloat (*restrict CoeffStep)[2],
+                                   ALfloat left, ALfloat right)
+{
+    ALuint c;
+    for(c = 0;c < IrSize;c++)
+    {
+        const ALuint off = (Offset+c)&HRIR_MASK;
+        Values[off][0] += Coeffs[c][0] * left;
+        Values[off][1] += Coeffs[c][1] * right;
+        Coeffs[c][0] += CoeffStep[c][0];
+        Coeffs[c][1] += CoeffStep[c][1];
+    }
+}
+
+static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2],
+                               const ALuint IrSize,
+                               ALfloat (*restrict Coeffs)[2],
+                               ALfloat left, ALfloat right)
+{
+    ALuint c;
+    for(c = 0;c < IrSize;c++)
+    {
+        const ALuint off = (Offset+c)&HRIR_MASK;
+        Values[off][0] += Coeffs[c][0] * left;
+        Values[off][1] += Coeffs[c][1] * right;
+    }
+}
+
+#define MixHrtf MixHrtf_C
+#define MixDirectHrtf MixDirectHrtf_C
+#include "mixer_inc.c"
+#undef MixHrtf
+
+
+void Mix_C(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
+           ALfloat *CurrentGains, const ALfloat *TargetGains, ALuint Counter, ALuint OutPos,
+           ALuint BufferSize)
+{
+    ALfloat gain, delta, step;
+    ALuint c;
+
+    delta = (Counter > 0) ? 1.0f/(ALfloat)Counter : 0.0f;
+
+    for(c = 0;c < OutChans;c++)
+    {
+        ALuint pos = 0;
+        gain = CurrentGains[c];
+        step = (TargetGains[c] - gain) * delta;
+        if(fabsf(step) > FLT_EPSILON)
+        {
+            ALuint minsize = minu(BufferSize, Counter);
+            for(;pos < minsize;pos++)
+            {
+                OutBuffer[c][OutPos+pos] += data[pos]*gain;
+                gain += step;
+            }
+            if(pos == Counter)
+                gain = TargetGains[c];
+            CurrentGains[c] = gain;
+        }
+
+        if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
+            continue;
+        for(;pos < BufferSize;pos++)
+            OutBuffer[c][OutPos+pos] += data[pos]*gain;
+    }
+}
+
+/* Basically the inverse of the above. Rather than one input going to multiple
+ * outputs (each with its own gain), it's multiple inputs (each with its own
+ * gain) going to one output. This applies one row (vs one column) of a matrix
+ * transform. And as the matrices are more or less static once set up, no
+ * stepping is necessary.
+ */
+void MixRow_C(ALfloat *OutBuffer, const ALfloat *Gains, const ALfloat (*restrict data)[BUFFERSIZE], ALuint InChans, ALuint InPos, ALuint BufferSize)
+{
+    ALuint c, i;
+
+    for(c = 0;c < InChans;c++)
+    {
+        ALfloat gain = Gains[c];
+        if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
+            continue;
+
+        for(i = 0;i < BufferSize;i++)
+            OutBuffer[i] += data[c][InPos+i] * gain;
+    }
+}

+ 110 - 0
Engine/lib/openal-soft/Alc/mixer_defs.h

@@ -0,0 +1,110 @@
+#ifndef MIXER_DEFS_H
+#define MIXER_DEFS_H
+
+#include "AL/alc.h"
+#include "AL/al.h"
+#include "alMain.h"
+#include "alu.h"
+
+struct MixGains;
+
+struct MixHrtfParams;
+struct HrtfState;
+
+/* C resamplers */
+const ALfloat *Resample_copy32_C(const BsincState *state, const ALfloat *restrict src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen);
+const ALfloat *Resample_point32_C(const BsincState *state, const ALfloat *restrict src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen);
+const ALfloat *Resample_lerp32_C(const BsincState *state, const ALfloat *restrict src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen);
+const ALfloat *Resample_fir4_32_C(const BsincState *state, const ALfloat *restrict src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen);
+const ALfloat *Resample_fir8_32_C(const BsincState *state, const ALfloat *restrict src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen);
+const ALfloat *Resample_bsinc32_C(const BsincState *state, const ALfloat *restrict src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen);
+
+
+/* C mixers */
+void MixHrtf_C(ALfloat (*restrict OutBuffer)[BUFFERSIZE], ALuint lidx, ALuint ridx,
+               const ALfloat *data, ALuint Counter, ALuint Offset, ALuint OutPos,
+               const ALuint IrSize, const struct MixHrtfParams *hrtfparams,
+               struct HrtfState *hrtfstate, ALuint BufferSize);
+void MixDirectHrtf_C(ALfloat (*restrict OutBuffer)[BUFFERSIZE], ALuint lidx, ALuint ridx,
+                     const ALfloat *data, ALuint Offset, const ALuint IrSize,
+                     ALfloat (*restrict Coeffs)[2], ALfloat (*restrict Values)[2],
+                     ALuint BufferSize);
+void Mix_C(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
+           ALfloat *CurrentGains, const ALfloat *TargetGains, ALuint Counter, ALuint OutPos,
+           ALuint BufferSize);
+void MixRow_C(ALfloat *OutBuffer, const ALfloat *Gains,
+              const ALfloat (*restrict data)[BUFFERSIZE], ALuint InChans,
+              ALuint InPos, ALuint BufferSize);
+
+/* SSE mixers */
+void MixHrtf_SSE(ALfloat (*restrict OutBuffer)[BUFFERSIZE], ALuint lidx, ALuint ridx,
+                 const ALfloat *data, ALuint Counter, ALuint Offset, ALuint OutPos,
+                 const ALuint IrSize, const struct MixHrtfParams *hrtfparams,
+                 struct HrtfState *hrtfstate, ALuint BufferSize);
+void MixDirectHrtf_SSE(ALfloat (*restrict OutBuffer)[BUFFERSIZE], ALuint lidx, ALuint ridx,
+                       const ALfloat *data, ALuint Offset, const ALuint IrSize,
+                       ALfloat (*restrict Coeffs)[2], ALfloat (*restrict Values)[2],
+                       ALuint BufferSize);
+void Mix_SSE(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
+             ALfloat *CurrentGains, const ALfloat *TargetGains, ALuint Counter, ALuint OutPos,
+             ALuint BufferSize);
+void MixRow_SSE(ALfloat *OutBuffer, const ALfloat *Gains,
+                const ALfloat (*restrict data)[BUFFERSIZE], ALuint InChans,
+                ALuint InPos, ALuint BufferSize);
+
+/* SSE resamplers */
+inline void InitiatePositionArrays(ALuint frac, ALuint increment, ALuint *restrict frac_arr, ALuint *restrict pos_arr, ALuint size)
+{
+    ALuint i;
+
+    pos_arr[0] = 0;
+    frac_arr[0] = frac;
+    for(i = 1;i < size;i++)
+    {
+        ALuint frac_tmp = frac_arr[i-1] + increment;
+        pos_arr[i] = pos_arr[i-1] + (frac_tmp>>FRACTIONBITS);
+        frac_arr[i] = frac_tmp&FRACTIONMASK;
+    }
+}
+
+const ALfloat *Resample_bsinc32_SSE(const BsincState *state, const ALfloat *restrict src, ALuint frac,
+                                    ALuint increment, ALfloat *restrict dst, ALuint dstlen);
+
+const ALfloat *Resample_lerp32_SSE2(const BsincState *state, const ALfloat *restrict src,
+                                    ALuint frac, ALuint increment, ALfloat *restrict dst,
+                                    ALuint numsamples);
+const ALfloat *Resample_lerp32_SSE41(const BsincState *state, const ALfloat *restrict src,
+                                     ALuint frac, ALuint increment, ALfloat *restrict dst,
+                                     ALuint numsamples);
+
+const ALfloat *Resample_fir4_32_SSE3(const BsincState *state, const ALfloat *restrict src,
+                                     ALuint frac, ALuint increment, ALfloat *restrict dst,
+                                     ALuint numsamples);
+const ALfloat *Resample_fir4_32_SSE41(const BsincState *state, const ALfloat *restrict src,
+                                      ALuint frac, ALuint increment, ALfloat *restrict dst,
+                                      ALuint numsamples);
+
+const ALfloat *Resample_fir8_32_SSE3(const BsincState *state, const ALfloat *restrict src,
+                                     ALuint frac, ALuint increment, ALfloat *restrict dst,
+                                     ALuint numsamples);
+const ALfloat *Resample_fir8_32_SSE41(const BsincState *state, const ALfloat *restrict src,
+                                      ALuint frac, ALuint increment, ALfloat *restrict dst,
+                                      ALuint numsamples);
+
+/* Neon mixers */
+void MixHrtf_Neon(ALfloat (*restrict OutBuffer)[BUFFERSIZE], ALuint lidx, ALuint ridx,
+                  const ALfloat *data, ALuint Counter, ALuint Offset, ALuint OutPos,
+                  const ALuint IrSize, const struct MixHrtfParams *hrtfparams,
+                  struct HrtfState *hrtfstate, ALuint BufferSize);
+void MixDirectHrtf_Neon(ALfloat (*restrict OutBuffer)[BUFFERSIZE], ALuint lidx, ALuint ridx,
+                        const ALfloat *data, ALuint Offset, const ALuint IrSize,
+                        ALfloat (*restrict Coeffs)[2], ALfloat (*restrict Values)[2],
+                        ALuint BufferSize);
+void Mix_Neon(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
+              ALfloat *CurrentGains, const ALfloat *TargetGains, ALuint Counter, ALuint OutPos,
+              ALuint BufferSize);
+void MixRow_Neon(ALfloat *OutBuffer, const ALfloat *Gains,
+                 const ALfloat (*restrict data)[BUFFERSIZE], ALuint InChans,
+                 ALuint InPos, ALuint BufferSize);
+
+#endif /* MIXER_DEFS_H */

+ 149 - 0
Engine/lib/openal-soft/Alc/mixer_inc.c

@@ -0,0 +1,149 @@
+#include "config.h"
+
+#include "alMain.h"
+#include "alSource.h"
+
+#include "hrtf.h"
+#include "mixer_defs.h"
+#include "align.h"
+#include "alu.h"
+
+
+#define MAX_UPDATE_SAMPLES 128
+
+
+static inline void ApplyCoeffsStep(ALuint Offset, ALfloat (*restrict Values)[2],
+                                   const ALuint irSize,
+                                   ALfloat (*restrict Coeffs)[2],
+                                   const ALfloat (*restrict CoeffStep)[2],
+                                   ALfloat left, ALfloat right);
+static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2],
+                               const ALuint irSize,
+                               ALfloat (*restrict Coeffs)[2],
+                               ALfloat left, ALfloat right);
+
+
+void MixHrtf(ALfloat (*restrict OutBuffer)[BUFFERSIZE], ALuint lidx, ALuint ridx,
+             const ALfloat *data, ALuint Counter, ALuint Offset, ALuint OutPos,
+             const ALuint IrSize, const MixHrtfParams *hrtfparams, HrtfState *hrtfstate,
+             ALuint BufferSize)
+{
+    ALfloat (*Coeffs)[2] = hrtfparams->Current->Coeffs;
+    ALuint Delay[2] = { hrtfparams->Current->Delay[0], hrtfparams->Current->Delay[1] };
+    ALfloat out[MAX_UPDATE_SAMPLES][2];
+    ALfloat left, right;
+    ALuint minsize;
+    ALuint pos, i;
+
+    pos = 0;
+    if(Counter == 0)
+        goto skip_stepping;
+
+    minsize = minu(BufferSize, Counter);
+    while(pos < minsize)
+    {
+        ALuint todo = minu(minsize-pos, MAX_UPDATE_SAMPLES);
+
+        for(i = 0;i < todo;i++)
+        {
+            hrtfstate->History[Offset&HRTF_HISTORY_MASK] = data[pos++];
+            left  = lerp(hrtfstate->History[(Offset-(Delay[0]>>HRTFDELAY_BITS))&HRTF_HISTORY_MASK],
+                         hrtfstate->History[(Offset-(Delay[0]>>HRTFDELAY_BITS)-1)&HRTF_HISTORY_MASK],
+                         (Delay[0]&HRTFDELAY_MASK)*(1.0f/HRTFDELAY_FRACONE));
+            right = lerp(hrtfstate->History[(Offset-(Delay[1]>>HRTFDELAY_BITS))&HRTF_HISTORY_MASK],
+                         hrtfstate->History[(Offset-(Delay[1]>>HRTFDELAY_BITS)-1)&HRTF_HISTORY_MASK],
+                         (Delay[1]&HRTFDELAY_MASK)*(1.0f/HRTFDELAY_FRACONE));
+
+            Delay[0] += hrtfparams->Steps.Delay[0];
+            Delay[1] += hrtfparams->Steps.Delay[1];
+
+            hrtfstate->Values[(Offset+IrSize)&HRIR_MASK][0] = 0.0f;
+            hrtfstate->Values[(Offset+IrSize)&HRIR_MASK][1] = 0.0f;
+            Offset++;
+
+            ApplyCoeffsStep(Offset, hrtfstate->Values, IrSize, Coeffs, hrtfparams->Steps.Coeffs, left, right);
+            out[i][0] = hrtfstate->Values[Offset&HRIR_MASK][0];
+            out[i][1] = hrtfstate->Values[Offset&HRIR_MASK][1];
+        }
+
+        for(i = 0;i < todo;i++)
+            OutBuffer[lidx][OutPos+i] += out[i][0];
+        for(i = 0;i < todo;i++)
+            OutBuffer[ridx][OutPos+i] += out[i][1];
+        OutPos += todo;
+    }
+
+    if(pos == Counter)
+    {
+        *hrtfparams->Current = *hrtfparams->Target;
+        Delay[0] = hrtfparams->Target->Delay[0];
+        Delay[1] = hrtfparams->Target->Delay[1];
+    }
+    else
+    {
+        hrtfparams->Current->Delay[0] = Delay[0];
+        hrtfparams->Current->Delay[1] = Delay[1];
+    }
+
+skip_stepping:
+    Delay[0] >>= HRTFDELAY_BITS;
+    Delay[1] >>= HRTFDELAY_BITS;
+    while(pos < BufferSize)
+    {
+        ALuint todo = minu(BufferSize-pos, MAX_UPDATE_SAMPLES);
+
+        for(i = 0;i < todo;i++)
+        {
+            hrtfstate->History[Offset&HRTF_HISTORY_MASK] = data[pos++];
+            left = hrtfstate->History[(Offset-Delay[0])&HRTF_HISTORY_MASK];
+            right = hrtfstate->History[(Offset-Delay[1])&HRTF_HISTORY_MASK];
+
+            hrtfstate->Values[(Offset+IrSize)&HRIR_MASK][0] = 0.0f;
+            hrtfstate->Values[(Offset+IrSize)&HRIR_MASK][1] = 0.0f;
+            Offset++;
+
+            ApplyCoeffs(Offset, hrtfstate->Values, IrSize, Coeffs, left, right);
+            out[i][0] = hrtfstate->Values[Offset&HRIR_MASK][0];
+            out[i][1] = hrtfstate->Values[Offset&HRIR_MASK][1];
+        }
+
+        for(i = 0;i < todo;i++)
+            OutBuffer[lidx][OutPos+i] += out[i][0];
+        for(i = 0;i < todo;i++)
+            OutBuffer[ridx][OutPos+i] += out[i][1];
+        OutPos += todo;
+    }
+}
+
+void MixDirectHrtf(ALfloat (*restrict OutBuffer)[BUFFERSIZE], ALuint lidx, ALuint ridx,
+                   const ALfloat *data, ALuint Offset, const ALuint IrSize,
+                   ALfloat (*restrict Coeffs)[2], ALfloat (*restrict Values)[2],
+                   ALuint BufferSize)
+{
+    ALfloat out[MAX_UPDATE_SAMPLES][2];
+    ALfloat insample;
+    ALuint pos, i;
+
+    for(pos = 0;pos < BufferSize;)
+    {
+        ALuint todo = minu(BufferSize-pos, MAX_UPDATE_SAMPLES);
+
+        for(i = 0;i < todo;i++)
+        {
+            Values[(Offset+IrSize)&HRIR_MASK][0] = 0.0f;
+            Values[(Offset+IrSize)&HRIR_MASK][1] = 0.0f;
+            Offset++;
+
+            insample = *(data++);
+            ApplyCoeffs(Offset, Values, IrSize, Coeffs, insample, insample);
+            out[i][0] = Values[Offset&HRIR_MASK][0];
+            out[i][1] = Values[Offset&HRIR_MASK][1];
+        }
+
+        for(i = 0;i < todo;i++)
+            OutBuffer[lidx][pos+i] += out[i][0];
+        for(i = 0;i < todo;i++)
+            OutBuffer[ridx][pos+i] += out[i][1];
+        pos += todo;
+    }
+}

+ 173 - 0
Engine/lib/openal-soft/Alc/mixer_neon.c

@@ -0,0 +1,173 @@
+#include "config.h"
+
+#include <arm_neon.h>
+
+#include "AL/al.h"
+#include "AL/alc.h"
+#include "alMain.h"
+#include "alu.h"
+#include "hrtf.h"
+
+
+static inline void ApplyCoeffsStep(ALuint Offset, ALfloat (*restrict Values)[2],
+                                   const ALuint IrSize,
+                                   ALfloat (*restrict Coeffs)[2],
+                                   const ALfloat (*restrict CoeffStep)[2],
+                                   ALfloat left, ALfloat right)
+{
+    ALuint c;
+    float32x4_t leftright4;
+    {
+        float32x2_t leftright2 = vdup_n_f32(0.0);
+        leftright2 = vset_lane_f32(left, leftright2, 0);
+        leftright2 = vset_lane_f32(right, leftright2, 1);
+        leftright4 = vcombine_f32(leftright2, leftright2);
+    }
+    for(c = 0;c < IrSize;c += 2)
+    {
+        const ALuint o0 = (Offset+c)&HRIR_MASK;
+        const ALuint o1 = (o0+1)&HRIR_MASK;
+        float32x4_t vals = vcombine_f32(vld1_f32((float32_t*)&Values[o0][0]),
+                                        vld1_f32((float32_t*)&Values[o1][0]));
+        float32x4_t coefs = vld1q_f32((float32_t*)&Coeffs[c][0]);
+        float32x4_t deltas = vld1q_f32(&CoeffStep[c][0]);
+
+        vals = vmlaq_f32(vals, coefs, leftright4);
+        coefs = vaddq_f32(coefs, deltas);
+
+        vst1_f32((float32_t*)&Values[o0][0], vget_low_f32(vals));
+        vst1_f32((float32_t*)&Values[o1][0], vget_high_f32(vals));
+        vst1q_f32(&Coeffs[c][0], coefs);
+    }
+}
+
+static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2],
+                               const ALuint IrSize,
+                               ALfloat (*restrict Coeffs)[2],
+                               ALfloat left, ALfloat right)
+{
+    ALuint c;
+    float32x4_t leftright4;
+    {
+        float32x2_t leftright2 = vdup_n_f32(0.0);
+        leftright2 = vset_lane_f32(left, leftright2, 0);
+        leftright2 = vset_lane_f32(right, leftright2, 1);
+        leftright4 = vcombine_f32(leftright2, leftright2);
+    }
+    for(c = 0;c < IrSize;c += 2)
+    {
+        const ALuint o0 = (Offset+c)&HRIR_MASK;
+        const ALuint o1 = (o0+1)&HRIR_MASK;
+        float32x4_t vals = vcombine_f32(vld1_f32((float32_t*)&Values[o0][0]),
+                                        vld1_f32((float32_t*)&Values[o1][0]));
+        float32x4_t coefs = vld1q_f32((float32_t*)&Coeffs[c][0]);
+
+        vals = vmlaq_f32(vals, coefs, leftright4);
+
+        vst1_f32((float32_t*)&Values[o0][0], vget_low_f32(vals));
+        vst1_f32((float32_t*)&Values[o1][0], vget_high_f32(vals));
+    }
+}
+
+#define MixHrtf MixHrtf_Neon
+#define MixDirectHrtf MixDirectHrtf_Neon
+#include "mixer_inc.c"
+#undef MixHrtf
+
+
+void Mix_Neon(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
+              ALfloat *CurrentGains, const ALfloat *TargetGains, ALuint Counter, ALuint OutPos,
+              ALuint BufferSize)
+{
+    ALfloat gain, delta, step;
+    float32x4_t gain4;
+    ALuint c;
+
+    delta = (Counter > 0) ? 1.0f/(ALfloat)Counter : 0.0f;
+
+    for(c = 0;c < OutChans;c++)
+    {
+        ALuint pos = 0;
+        gain = CurrentGains[c];
+        step = (TargetGains[c] - gain) * delta;
+        if(fabsf(step) > FLT_EPSILON)
+        {
+            ALuint minsize = minu(BufferSize, Counter);
+            /* Mix with applying gain steps in aligned multiples of 4. */
+            if(minsize-pos > 3)
+            {
+                float32x4_t step4;
+                gain4 = vsetq_lane_f32(gain, gain4, 0);
+                gain4 = vsetq_lane_f32(gain + step, gain4, 1);
+                gain4 = vsetq_lane_f32(gain + step + step, gain4, 2);
+                gain4 = vsetq_lane_f32(gain + step + step + step, gain4, 3);
+                step4 = vdupq_n_f32(step + step + step + step);
+                do {
+                    const float32x4_t val4 = vld1q_f32(&data[pos]);
+                    float32x4_t dry4 = vld1q_f32(&OutBuffer[c][OutPos+pos]);
+                    dry4 = vmlaq_f32(dry4, val4, gain4);
+                    gain4 = vaddq_f32(gain4, step4);
+                    vst1q_f32(&OutBuffer[c][OutPos+pos], dry4);
+                    pos += 4;
+                } while(minsize-pos > 3);
+                /* NOTE: gain4 now represents the next four gains after the
+                 * last four mixed samples, so the lowest element represents
+                 * the next gain to apply.
+                 */
+                gain = vgetq_lane_f32(gain4, 0);
+            }
+            /* Mix with applying left over gain steps that aren't aligned multiples of 4. */
+            for(;pos < minsize;pos++)
+            {
+                OutBuffer[c][OutPos+pos] += data[pos]*gain;
+                gain += step;
+            }
+            if(pos == Counter)
+                gain = TargetGains[c];
+            CurrentGains[c] = gain;
+
+            /* Mix until pos is aligned with 4 or the mix is done. */
+            minsize = minu(BufferSize, (pos+3)&~3);
+            for(;pos < minsize;pos++)
+                OutBuffer[c][OutPos+pos] += data[pos]*gain;
+        }
+
+        if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
+            continue;
+        gain4 = vdupq_n_f32(gain);
+        for(;BufferSize-pos > 3;pos += 4)
+        {
+            const float32x4_t val4 = vld1q_f32(&data[pos]);
+            float32x4_t dry4 = vld1q_f32(&OutBuffer[c][OutPos+pos]);
+            dry4 = vmlaq_f32(dry4, val4, gain4);
+            vst1q_f32(&OutBuffer[c][OutPos+pos], dry4);
+        }
+        for(;pos < BufferSize;pos++)
+            OutBuffer[c][OutPos+pos] += data[pos]*gain;
+    }
+}
+
+void MixRow_Neon(ALfloat *OutBuffer, const ALfloat *Gains, const ALfloat (*restrict data)[BUFFERSIZE], ALuint InChans, ALuint InPos, ALuint BufferSize)
+{
+    float32x4_t gain4;
+    ALuint c;
+
+    for(c = 0;c < InChans;c++)
+    {
+        ALuint pos = 0;
+        ALfloat gain = Gains[c];
+        if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
+            continue;
+
+        gain4 = vdupq_n_f32(gain);
+        for(;BufferSize-pos > 3;pos += 4)
+        {
+            const float32x4_t val4 = vld1q_f32(&data[c][InPos+pos]);
+            float32x4_t dry4 = vld1q_f32(&OutBuffer[pos]);
+            dry4 = vmlaq_f32(dry4, val4, gain4);
+            vst1q_f32(&OutBuffer[pos], dry4);
+        }
+        for(;pos < BufferSize;pos++)
+            OutBuffer[pos] += data[c][InPos+pos]*gain;
+    }
+}

+ 292 - 0
Engine/lib/openal-soft/Alc/mixer_sse.c

@@ -0,0 +1,292 @@
+#include "config.h"
+
+#include <xmmintrin.h>
+
+#include "AL/al.h"
+#include "AL/alc.h"
+#include "alMain.h"
+#include "alu.h"
+
+#include "alSource.h"
+#include "alAuxEffectSlot.h"
+#include "mixer_defs.h"
+
+
+const ALfloat *Resample_bsinc32_SSE(const BsincState *state, const ALfloat *restrict src,
+                                    ALuint frac, ALuint increment, ALfloat *restrict dst,
+                                    ALuint dstlen)
+{
+    const __m128 sf4 = _mm_set1_ps(state->sf);
+    const ALuint m = state->m;
+    const ALint l = state->l;
+    const ALfloat *fil, *scd, *phd, *spd;
+    ALuint pi, j_f, i;
+    ALfloat pf;
+    ALint j_s;
+    __m128 r4;
+
+    for(i = 0;i < dstlen;i++)
+    {
+        // Calculate the phase index and factor.
+#define FRAC_PHASE_BITDIFF (FRACTIONBITS-BSINC_PHASE_BITS)
+        pi = frac >> FRAC_PHASE_BITDIFF;
+        pf = (frac & ((1<<FRAC_PHASE_BITDIFF)-1)) * (1.0f/(1<<FRAC_PHASE_BITDIFF));
+#undef FRAC_PHASE_BITDIFF
+
+        fil = state->coeffs[pi].filter;
+        scd = state->coeffs[pi].scDelta;
+        phd = state->coeffs[pi].phDelta;
+        spd = state->coeffs[pi].spDelta;
+
+        // Apply the scale and phase interpolated filter.
+        r4 = _mm_setzero_ps();
+        {
+            const __m128 pf4 = _mm_set1_ps(pf);
+            for(j_f = 0,j_s = l;j_f < m;j_f+=4,j_s+=4)
+            {
+                const __m128 f4 = _mm_add_ps(
+                    _mm_add_ps(
+                        _mm_load_ps(&fil[j_f]),
+                        _mm_mul_ps(sf4, _mm_load_ps(&scd[j_f]))
+                    ),
+                    _mm_mul_ps(
+                        pf4,
+                        _mm_add_ps(
+                            _mm_load_ps(&phd[j_f]),
+                            _mm_mul_ps(sf4, _mm_load_ps(&spd[j_f]))
+                        )
+                    )
+                );
+                r4 = _mm_add_ps(r4, _mm_mul_ps(f4, _mm_loadu_ps(&src[j_s])));
+            }
+        }
+        r4 = _mm_add_ps(r4, _mm_shuffle_ps(r4, r4, _MM_SHUFFLE(0, 1, 2, 3)));
+        r4 = _mm_add_ps(r4, _mm_movehl_ps(r4, r4));
+        dst[i] = _mm_cvtss_f32(r4);
+
+        frac += increment;
+        src  += frac>>FRACTIONBITS;
+        frac &= FRACTIONMASK;
+    }
+    return dst;
+}
+
+
+static inline void ApplyCoeffsStep(ALuint Offset, ALfloat (*restrict Values)[2],
+                                   const ALuint IrSize,
+                                   ALfloat (*restrict Coeffs)[2],
+                                   const ALfloat (*restrict CoeffStep)[2],
+                                   ALfloat left, ALfloat right)
+{
+    const __m128 lrlr = _mm_setr_ps(left, right, left, right);
+    __m128 coeffs, deltas, imp0, imp1;
+    __m128 vals = _mm_setzero_ps();
+    ALuint i;
+
+    if((Offset&1))
+    {
+        const ALuint o0 = Offset&HRIR_MASK;
+        const ALuint o1 = (Offset+IrSize-1)&HRIR_MASK;
+
+        coeffs = _mm_load_ps(&Coeffs[0][0]);
+        deltas = _mm_load_ps(&CoeffStep[0][0]);
+        vals = _mm_loadl_pi(vals, (__m64*)&Values[o0][0]);
+        imp0 = _mm_mul_ps(lrlr, coeffs);
+        coeffs = _mm_add_ps(coeffs, deltas);
+        vals = _mm_add_ps(imp0, vals);
+        _mm_store_ps(&Coeffs[0][0], coeffs);
+        _mm_storel_pi((__m64*)&Values[o0][0], vals);
+        for(i = 1;i < IrSize-1;i += 2)
+        {
+            const ALuint o2 = (Offset+i)&HRIR_MASK;
+
+            coeffs = _mm_load_ps(&Coeffs[i+1][0]);
+            deltas = _mm_load_ps(&CoeffStep[i+1][0]);
+            vals = _mm_load_ps(&Values[o2][0]);
+            imp1 = _mm_mul_ps(lrlr, coeffs);
+            coeffs = _mm_add_ps(coeffs, deltas);
+            imp0 = _mm_shuffle_ps(imp0, imp1, _MM_SHUFFLE(1, 0, 3, 2));
+            vals = _mm_add_ps(imp0, vals);
+            _mm_store_ps(&Coeffs[i+1][0], coeffs);
+            _mm_store_ps(&Values[o2][0], vals);
+            imp0 = imp1;
+        }
+        vals = _mm_loadl_pi(vals, (__m64*)&Values[o1][0]);
+        imp0 = _mm_movehl_ps(imp0, imp0);
+        vals = _mm_add_ps(imp0, vals);
+        _mm_storel_pi((__m64*)&Values[o1][0], vals);
+    }
+    else
+    {
+        for(i = 0;i < IrSize;i += 2)
+        {
+            const ALuint o = (Offset + i)&HRIR_MASK;
+
+            coeffs = _mm_load_ps(&Coeffs[i][0]);
+            deltas = _mm_load_ps(&CoeffStep[i][0]);
+            vals = _mm_load_ps(&Values[o][0]);
+            imp0 = _mm_mul_ps(lrlr, coeffs);
+            coeffs = _mm_add_ps(coeffs, deltas);
+            vals = _mm_add_ps(imp0, vals);
+            _mm_store_ps(&Coeffs[i][0], coeffs);
+            _mm_store_ps(&Values[o][0], vals);
+        }
+    }
+}
+
+static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2],
+                               const ALuint IrSize,
+                               ALfloat (*restrict Coeffs)[2],
+                               ALfloat left, ALfloat right)
+{
+    const __m128 lrlr = _mm_setr_ps(left, right, left, right);
+    __m128 vals = _mm_setzero_ps();
+    __m128 coeffs;
+    ALuint i;
+
+    if((Offset&1))
+    {
+        const ALuint o0 = Offset&HRIR_MASK;
+        const ALuint o1 = (Offset+IrSize-1)&HRIR_MASK;
+        __m128 imp0, imp1;
+
+        coeffs = _mm_load_ps(&Coeffs[0][0]);
+        vals = _mm_loadl_pi(vals, (__m64*)&Values[o0][0]);
+        imp0 = _mm_mul_ps(lrlr, coeffs);
+        vals = _mm_add_ps(imp0, vals);
+        _mm_storel_pi((__m64*)&Values[o0][0], vals);
+        for(i = 1;i < IrSize-1;i += 2)
+        {
+            const ALuint o2 = (Offset+i)&HRIR_MASK;
+
+            coeffs = _mm_load_ps(&Coeffs[i+1][0]);
+            vals = _mm_load_ps(&Values[o2][0]);
+            imp1 = _mm_mul_ps(lrlr, coeffs);
+            imp0 = _mm_shuffle_ps(imp0, imp1, _MM_SHUFFLE(1, 0, 3, 2));
+            vals = _mm_add_ps(imp0, vals);
+            _mm_store_ps(&Values[o2][0], vals);
+            imp0 = imp1;
+        }
+        vals = _mm_loadl_pi(vals, (__m64*)&Values[o1][0]);
+        imp0 = _mm_movehl_ps(imp0, imp0);
+        vals = _mm_add_ps(imp0, vals);
+        _mm_storel_pi((__m64*)&Values[o1][0], vals);
+    }
+    else
+    {
+        for(i = 0;i < IrSize;i += 2)
+        {
+            const ALuint o = (Offset + i)&HRIR_MASK;
+
+            coeffs = _mm_load_ps(&Coeffs[i][0]);
+            vals = _mm_load_ps(&Values[o][0]);
+            vals = _mm_add_ps(vals, _mm_mul_ps(lrlr, coeffs));
+            _mm_store_ps(&Values[o][0], vals);
+        }
+    }
+}
+
+#define MixHrtf MixHrtf_SSE
+#define MixDirectHrtf MixDirectHrtf_SSE
+#include "mixer_inc.c"
+#undef MixHrtf
+
+
+void Mix_SSE(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
+             ALfloat *CurrentGains, const ALfloat *TargetGains, ALuint Counter, ALuint OutPos,
+             ALuint BufferSize)
+{
+    ALfloat gain, delta, step;
+    __m128 gain4;
+    ALuint c;
+
+    delta = (Counter > 0) ? 1.0f/(ALfloat)Counter : 0.0f;
+
+    for(c = 0;c < OutChans;c++)
+    {
+        ALuint pos = 0;
+        gain = CurrentGains[c];
+        step = (TargetGains[c] - gain) * delta;
+        if(fabsf(step) > FLT_EPSILON)
+        {
+            ALuint minsize = minu(BufferSize, Counter);
+            /* Mix with applying gain steps in aligned multiples of 4. */
+            if(minsize-pos > 3)
+            {
+                __m128 step4;
+                gain4 = _mm_setr_ps(
+                    gain,
+                    gain + step,
+                    gain + step + step,
+                    gain + step + step + step
+                );
+                step4 = _mm_set1_ps(step + step + step + step);
+                do {
+                    const __m128 val4 = _mm_load_ps(&data[pos]);
+                    __m128 dry4 = _mm_load_ps(&OutBuffer[c][OutPos+pos]);
+                    dry4 = _mm_add_ps(dry4, _mm_mul_ps(val4, gain4));
+                    gain4 = _mm_add_ps(gain4, step4);
+                    _mm_store_ps(&OutBuffer[c][OutPos+pos], dry4);
+                    pos += 4;
+                } while(minsize-pos > 3);
+                /* NOTE: gain4 now represents the next four gains after the
+                 * last four mixed samples, so the lowest element represents
+                 * the next gain to apply.
+                 */
+                gain = _mm_cvtss_f32(gain4);
+            }
+            /* Mix with applying left over gain steps that aren't aligned multiples of 4. */
+            for(;pos < minsize;pos++)
+            {
+                OutBuffer[c][OutPos+pos] += data[pos]*gain;
+                gain += step;
+            }
+            if(pos == Counter)
+                gain = TargetGains[c];
+            CurrentGains[c] = gain;
+
+            /* Mix until pos is aligned with 4 or the mix is done. */
+            minsize = minu(BufferSize, (pos+3)&~3);
+            for(;pos < minsize;pos++)
+                OutBuffer[c][OutPos+pos] += data[pos]*gain;
+        }
+
+        if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
+            continue;
+        gain4 = _mm_set1_ps(gain);
+        for(;BufferSize-pos > 3;pos += 4)
+        {
+            const __m128 val4 = _mm_load_ps(&data[pos]);
+            __m128 dry4 = _mm_load_ps(&OutBuffer[c][OutPos+pos]);
+            dry4 = _mm_add_ps(dry4, _mm_mul_ps(val4, gain4));
+            _mm_store_ps(&OutBuffer[c][OutPos+pos], dry4);
+        }
+        for(;pos < BufferSize;pos++)
+            OutBuffer[c][OutPos+pos] += data[pos]*gain;
+    }
+}
+
+void MixRow_SSE(ALfloat *OutBuffer, const ALfloat *Gains, const ALfloat (*restrict data)[BUFFERSIZE], ALuint InChans, ALuint InPos, ALuint BufferSize)
+{
+    __m128 gain4;
+    ALuint c;
+
+    for(c = 0;c < InChans;c++)
+    {
+        ALuint pos = 0;
+        ALfloat gain = Gains[c];
+        if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
+            continue;
+
+        gain4 = _mm_set1_ps(gain);
+        for(;BufferSize-pos > 3;pos += 4)
+        {
+            const __m128 val4 = _mm_load_ps(&data[c][InPos+pos]);
+            __m128 dry4 = _mm_load_ps(&OutBuffer[pos]);
+            dry4 = _mm_add_ps(dry4, _mm_mul_ps(val4, gain4));
+            _mm_store_ps(&OutBuffer[pos], dry4);
+        }
+        for(;pos < BufferSize;pos++)
+            OutBuffer[pos] += data[c][InPos+pos]*gain;
+    }
+}

+ 82 - 0
Engine/lib/openal-soft/Alc/mixer_sse2.c

@@ -0,0 +1,82 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 2014 by Timothy Arceri <[email protected]>.
+ * This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the
+ *  Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#include "config.h"
+
+#include <xmmintrin.h>
+#include <emmintrin.h>
+
+#include "alu.h"
+#include "mixer_defs.h"
+
+
+const ALfloat *Resample_lerp32_SSE2(const BsincState* UNUSED(state), const ALfloat *restrict src,
+                                    ALuint frac, ALuint increment, ALfloat *restrict dst,
+                                    ALuint numsamples)
+{
+    const __m128i increment4 = _mm_set1_epi32(increment*4);
+    const __m128 fracOne4 = _mm_set1_ps(1.0f/FRACTIONONE);
+    const __m128i fracMask4 = _mm_set1_epi32(FRACTIONMASK);
+    union { alignas(16) ALuint i[4]; float f[4]; } pos_;
+    union { alignas(16) ALuint i[4]; float f[4]; } frac_;
+    __m128i frac4, pos4;
+    ALuint pos;
+    ALuint i;
+
+    InitiatePositionArrays(frac, increment, frac_.i, pos_.i, 4);
+
+    frac4 = _mm_castps_si128(_mm_load_ps(frac_.f));
+    pos4 = _mm_castps_si128(_mm_load_ps(pos_.f));
+
+    for(i = 0;numsamples-i > 3;i += 4)
+    {
+        const __m128 val1 = _mm_setr_ps(src[pos_.i[0]], src[pos_.i[1]], src[pos_.i[2]], src[pos_.i[3]]);
+        const __m128 val2 = _mm_setr_ps(src[pos_.i[0]+1], src[pos_.i[1]+1], src[pos_.i[2]+1], src[pos_.i[3]+1]);
+
+        /* val1 + (val2-val1)*mu */
+        const __m128 r0 = _mm_sub_ps(val2, val1);
+        const __m128 mu = _mm_mul_ps(_mm_cvtepi32_ps(frac4), fracOne4);
+        const __m128 out = _mm_add_ps(val1, _mm_mul_ps(mu, r0));
+
+        _mm_store_ps(&dst[i], out);
+
+        frac4 = _mm_add_epi32(frac4, increment4);
+        pos4 = _mm_add_epi32(pos4, _mm_srli_epi32(frac4, FRACTIONBITS));
+        frac4 = _mm_and_si128(frac4, fracMask4);
+
+        _mm_store_ps(pos_.f, _mm_castsi128_ps(pos4));
+    }
+
+    /* NOTE: These four elements represent the position *after* the last four
+     * samples, so the lowest element is the next position to resample.
+     */
+    pos = pos_.i[0];
+    frac = _mm_cvtsi128_si32(frac4);
+
+    for(;i < numsamples;i++)
+    {
+        dst[i] = lerp(src[pos], src[pos+1], frac * (1.0f/FRACTIONONE));
+
+        frac += increment;
+        pos  += frac>>FRACTIONBITS;
+        frac &= FRACTIONMASK;
+    }
+    return dst;
+}

+ 164 - 0
Engine/lib/openal-soft/Alc/mixer_sse3.c

@@ -0,0 +1,164 @@
+/**
+ * OpenAL cross platform audio library, SSE3 mixer functions
+ *
+ * Copyright (C) 2014 by Timothy Arceri <[email protected]>.
+ * Copyright (C) 2015 by Chris Robinson <[email protected]>.
+ *
+ * This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the
+ *  Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#include "config.h"
+
+#include <xmmintrin.h>
+#include <emmintrin.h>
+#include <pmmintrin.h>
+
+#include "alu.h"
+#include "mixer_defs.h"
+
+
+const ALfloat *Resample_fir4_32_SSE3(const BsincState* UNUSED(state), const ALfloat *restrict src,
+                                     ALuint frac, ALuint increment, ALfloat *restrict dst,
+                                     ALuint numsamples)
+{
+    const __m128i increment4 = _mm_set1_epi32(increment*4);
+    const __m128i fracMask4 = _mm_set1_epi32(FRACTIONMASK);
+    union { alignas(16) ALuint i[4]; float f[4]; } pos_;
+    union { alignas(16) ALuint i[4]; float f[4]; } frac_;
+    __m128i frac4, pos4;
+    ALuint pos;
+    ALuint i;
+
+    InitiatePositionArrays(frac, increment, frac_.i, pos_.i, 4);
+
+    frac4 = _mm_castps_si128(_mm_load_ps(frac_.f));
+    pos4 = _mm_castps_si128(_mm_load_ps(pos_.f));
+
+    --src;
+    for(i = 0;numsamples-i > 3;i += 4)
+    {
+        const __m128 val0 = _mm_loadu_ps(&src[pos_.i[0]]);
+        const __m128 val1 = _mm_loadu_ps(&src[pos_.i[1]]);
+        const __m128 val2 = _mm_loadu_ps(&src[pos_.i[2]]);
+        const __m128 val3 = _mm_loadu_ps(&src[pos_.i[3]]);
+        __m128 k0 = _mm_load_ps(ResampleCoeffs.FIR4[frac_.i[0]]);
+        __m128 k1 = _mm_load_ps(ResampleCoeffs.FIR4[frac_.i[1]]);
+        __m128 k2 = _mm_load_ps(ResampleCoeffs.FIR4[frac_.i[2]]);
+        __m128 k3 = _mm_load_ps(ResampleCoeffs.FIR4[frac_.i[3]]);
+        __m128 out;
+
+        k0 = _mm_mul_ps(k0, val0);
+        k1 = _mm_mul_ps(k1, val1);
+        k2 = _mm_mul_ps(k2, val2);
+        k3 = _mm_mul_ps(k3, val3);
+        k0 = _mm_hadd_ps(k0, k1);
+        k2 = _mm_hadd_ps(k2, k3);
+        out = _mm_hadd_ps(k0, k2);
+
+        _mm_store_ps(&dst[i], out);
+
+        frac4 = _mm_add_epi32(frac4, increment4);
+        pos4 = _mm_add_epi32(pos4, _mm_srli_epi32(frac4, FRACTIONBITS));
+        frac4 = _mm_and_si128(frac4, fracMask4);
+
+        _mm_store_ps(pos_.f, _mm_castsi128_ps(pos4));
+        _mm_store_ps(frac_.f, _mm_castsi128_ps(frac4));
+    }
+
+    /* NOTE: These four elements represent the position *after* the last four
+     * samples, so the lowest element is the next position to resample.
+     */
+    pos = pos_.i[0];
+    frac = frac_.i[0];
+
+    for(;i < numsamples;i++)
+    {
+        dst[i] = resample_fir4(src[pos], src[pos+1], src[pos+2], src[pos+3], frac);
+
+        frac += increment;
+        pos  += frac>>FRACTIONBITS;
+        frac &= FRACTIONMASK;
+    }
+    return dst;
+}
+
+const ALfloat *Resample_fir8_32_SSE3(const BsincState* UNUSED(state), const ALfloat *restrict src,
+                                     ALuint frac, ALuint increment, ALfloat *restrict dst,
+                                     ALuint numsamples)
+{
+    const __m128i increment4 = _mm_set1_epi32(increment*4);
+    const __m128i fracMask4 = _mm_set1_epi32(FRACTIONMASK);
+    union { alignas(16) ALuint i[4]; float f[4]; } pos_;
+    union { alignas(16) ALuint i[4]; float f[4]; } frac_;
+    __m128i frac4, pos4;
+    ALuint pos;
+    ALuint i, j;
+
+    InitiatePositionArrays(frac, increment, frac_.i, pos_.i, 4);
+
+    frac4 = _mm_castps_si128(_mm_load_ps(frac_.f));
+    pos4 = _mm_castps_si128(_mm_load_ps(pos_.f));
+
+    src -= 3;
+    for(i = 0;numsamples-i > 3;i += 4)
+    {
+        __m128 out[2];
+        for(j = 0;j < 8;j+=4)
+        {
+            const __m128 val0 = _mm_loadu_ps(&src[pos_.i[0]+j]);
+            const __m128 val1 = _mm_loadu_ps(&src[pos_.i[1]+j]);
+            const __m128 val2 = _mm_loadu_ps(&src[pos_.i[2]+j]);
+            const __m128 val3 = _mm_loadu_ps(&src[pos_.i[3]+j]);
+            __m128 k0 = _mm_load_ps(&ResampleCoeffs.FIR8[frac_.i[0]][j]);
+            __m128 k1 = _mm_load_ps(&ResampleCoeffs.FIR8[frac_.i[1]][j]);
+            __m128 k2 = _mm_load_ps(&ResampleCoeffs.FIR8[frac_.i[2]][j]);
+            __m128 k3 = _mm_load_ps(&ResampleCoeffs.FIR8[frac_.i[3]][j]);
+
+            k0 = _mm_mul_ps(k0, val0);
+            k1 = _mm_mul_ps(k1, val1);
+            k2 = _mm_mul_ps(k2, val2);
+            k3 = _mm_mul_ps(k3, val3);
+            k0 = _mm_hadd_ps(k0, k1);
+            k2 = _mm_hadd_ps(k2, k3);
+            out[j>>2] = _mm_hadd_ps(k0, k2);
+        }
+
+        out[0] = _mm_add_ps(out[0], out[1]);
+        _mm_store_ps(&dst[i], out[0]);
+
+        frac4 = _mm_add_epi32(frac4, increment4);
+        pos4 = _mm_add_epi32(pos4, _mm_srli_epi32(frac4, FRACTIONBITS));
+        frac4 = _mm_and_si128(frac4, fracMask4);
+
+        _mm_store_ps(pos_.f, _mm_castsi128_ps(pos4));
+        _mm_store_ps(frac_.f, _mm_castsi128_ps(frac4));
+    }
+
+    pos = pos_.i[0];
+    frac = frac_.i[0];
+
+    for(;i < numsamples;i++)
+    {
+        dst[i] = resample_fir8(src[pos  ], src[pos+1], src[pos+2], src[pos+3],
+                               src[pos+4], src[pos+5], src[pos+6], src[pos+7], frac);
+
+        frac += increment;
+        pos  += frac>>FRACTIONBITS;
+        frac &= FRACTIONMASK;
+    }
+    return dst;
+}

+ 227 - 0
Engine/lib/openal-soft/Alc/mixer_sse41.c

@@ -0,0 +1,227 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 2014 by Timothy Arceri <[email protected]>.
+ * This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the
+ *  Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#include "config.h"
+
+#include <xmmintrin.h>
+#include <emmintrin.h>
+#include <smmintrin.h>
+
+#include "alu.h"
+#include "mixer_defs.h"
+
+
+const ALfloat *Resample_lerp32_SSE41(const BsincState* UNUSED(state), const ALfloat *restrict src,
+                                     ALuint frac, ALuint increment, ALfloat *restrict dst,
+                                     ALuint numsamples)
+{
+    const __m128i increment4 = _mm_set1_epi32(increment*4);
+    const __m128 fracOne4 = _mm_set1_ps(1.0f/FRACTIONONE);
+    const __m128i fracMask4 = _mm_set1_epi32(FRACTIONMASK);
+    union { alignas(16) ALuint i[4]; float f[4]; } pos_;
+    union { alignas(16) ALuint i[4]; float f[4]; } frac_;
+    __m128i frac4, pos4;
+    ALuint pos;
+    ALuint i;
+
+    InitiatePositionArrays(frac, increment, frac_.i, pos_.i, 4);
+
+    frac4 = _mm_castps_si128(_mm_load_ps(frac_.f));
+    pos4 = _mm_castps_si128(_mm_load_ps(pos_.f));
+
+    for(i = 0;numsamples-i > 3;i += 4)
+    {
+        const __m128 val1 = _mm_setr_ps(src[pos_.i[0]], src[pos_.i[1]], src[pos_.i[2]], src[pos_.i[3]]);
+        const __m128 val2 = _mm_setr_ps(src[pos_.i[0]+1], src[pos_.i[1]+1], src[pos_.i[2]+1], src[pos_.i[3]+1]);
+
+        /* val1 + (val2-val1)*mu */
+        const __m128 r0 = _mm_sub_ps(val2, val1);
+        const __m128 mu = _mm_mul_ps(_mm_cvtepi32_ps(frac4), fracOne4);
+        const __m128 out = _mm_add_ps(val1, _mm_mul_ps(mu, r0));
+
+        _mm_store_ps(&dst[i], out);
+
+        frac4 = _mm_add_epi32(frac4, increment4);
+        pos4 = _mm_add_epi32(pos4, _mm_srli_epi32(frac4, FRACTIONBITS));
+        frac4 = _mm_and_si128(frac4, fracMask4);
+
+        pos_.i[0] = _mm_extract_epi32(pos4, 0);
+        pos_.i[1] = _mm_extract_epi32(pos4, 1);
+        pos_.i[2] = _mm_extract_epi32(pos4, 2);
+        pos_.i[3] = _mm_extract_epi32(pos4, 3);
+    }
+
+    /* NOTE: These four elements represent the position *after* the last four
+     * samples, so the lowest element is the next position to resample.
+     */
+    pos = pos_.i[0];
+    frac = _mm_cvtsi128_si32(frac4);
+
+    for(;i < numsamples;i++)
+    {
+        dst[i] = lerp(src[pos], src[pos+1], frac * (1.0f/FRACTIONONE));
+
+        frac += increment;
+        pos  += frac>>FRACTIONBITS;
+        frac &= FRACTIONMASK;
+    }
+    return dst;
+}
+
+const ALfloat *Resample_fir4_32_SSE41(const BsincState* UNUSED(state), const ALfloat *restrict src,
+                                      ALuint frac, ALuint increment, ALfloat *restrict dst,
+                                      ALuint numsamples)
+{
+    const __m128i increment4 = _mm_set1_epi32(increment*4);
+    const __m128i fracMask4 = _mm_set1_epi32(FRACTIONMASK);
+    union { alignas(16) ALuint i[4]; float f[4]; } pos_;
+    union { alignas(16) ALuint i[4]; float f[4]; } frac_;
+    __m128i frac4, pos4;
+    ALuint pos;
+    ALuint i;
+
+    InitiatePositionArrays(frac, increment, frac_.i, pos_.i, 4);
+
+    frac4 = _mm_castps_si128(_mm_load_ps(frac_.f));
+    pos4 = _mm_castps_si128(_mm_load_ps(pos_.f));
+
+    --src;
+    for(i = 0;numsamples-i > 3;i += 4)
+    {
+        const __m128 val0 = _mm_loadu_ps(&src[pos_.i[0]]);
+        const __m128 val1 = _mm_loadu_ps(&src[pos_.i[1]]);
+        const __m128 val2 = _mm_loadu_ps(&src[pos_.i[2]]);
+        const __m128 val3 = _mm_loadu_ps(&src[pos_.i[3]]);
+        __m128 k0 = _mm_load_ps(ResampleCoeffs.FIR4[frac_.i[0]]);
+        __m128 k1 = _mm_load_ps(ResampleCoeffs.FIR4[frac_.i[1]]);
+        __m128 k2 = _mm_load_ps(ResampleCoeffs.FIR4[frac_.i[2]]);
+        __m128 k3 = _mm_load_ps(ResampleCoeffs.FIR4[frac_.i[3]]);
+        __m128 out;
+
+        k0 = _mm_mul_ps(k0, val0);
+        k1 = _mm_mul_ps(k1, val1);
+        k2 = _mm_mul_ps(k2, val2);
+        k3 = _mm_mul_ps(k3, val3);
+        k0 = _mm_hadd_ps(k0, k1);
+        k2 = _mm_hadd_ps(k2, k3);
+        out = _mm_hadd_ps(k0, k2);
+
+        _mm_store_ps(&dst[i], out);
+
+        frac4 = _mm_add_epi32(frac4, increment4);
+        pos4 = _mm_add_epi32(pos4, _mm_srli_epi32(frac4, FRACTIONBITS));
+        frac4 = _mm_and_si128(frac4, fracMask4);
+
+        pos_.i[0] = _mm_extract_epi32(pos4, 0);
+        pos_.i[1] = _mm_extract_epi32(pos4, 1);
+        pos_.i[2] = _mm_extract_epi32(pos4, 2);
+        pos_.i[3] = _mm_extract_epi32(pos4, 3);
+        frac_.i[0] = _mm_extract_epi32(frac4, 0);
+        frac_.i[1] = _mm_extract_epi32(frac4, 1);
+        frac_.i[2] = _mm_extract_epi32(frac4, 2);
+        frac_.i[3] = _mm_extract_epi32(frac4, 3);
+    }
+
+    pos = pos_.i[0];
+    frac = frac_.i[0];
+
+    for(;i < numsamples;i++)
+    {
+        dst[i] = resample_fir4(src[pos], src[pos+1], src[pos+2], src[pos+3], frac);
+
+        frac += increment;
+        pos  += frac>>FRACTIONBITS;
+        frac &= FRACTIONMASK;
+    }
+    return dst;
+}
+
+const ALfloat *Resample_fir8_32_SSE41(const BsincState* UNUSED(state), const ALfloat *restrict src,
+                                      ALuint frac, ALuint increment, ALfloat *restrict dst,
+                                      ALuint numsamples)
+{
+    const __m128i increment4 = _mm_set1_epi32(increment*4);
+    const __m128i fracMask4 = _mm_set1_epi32(FRACTIONMASK);
+    union { alignas(16) ALuint i[4]; float f[4]; } pos_;
+    union { alignas(16) ALuint i[4]; float f[4]; } frac_;
+    __m128i frac4, pos4;
+    ALuint pos;
+    ALuint i, j;
+
+    InitiatePositionArrays(frac, increment, frac_.i, pos_.i, 4);
+
+    frac4 = _mm_castps_si128(_mm_load_ps(frac_.f));
+    pos4 = _mm_castps_si128(_mm_load_ps(pos_.f));
+
+    src -= 3;
+    for(i = 0;numsamples-i > 3;i += 4)
+    {
+        __m128 out[2];
+        for(j = 0;j < 8;j+=4)
+        {
+            const __m128 val0 = _mm_loadu_ps(&src[pos_.i[0]+j]);
+            const __m128 val1 = _mm_loadu_ps(&src[pos_.i[1]+j]);
+            const __m128 val2 = _mm_loadu_ps(&src[pos_.i[2]+j]);
+            const __m128 val3 = _mm_loadu_ps(&src[pos_.i[3]+j]);
+            __m128 k0 = _mm_load_ps(&ResampleCoeffs.FIR8[frac_.i[0]][j]);
+            __m128 k1 = _mm_load_ps(&ResampleCoeffs.FIR8[frac_.i[1]][j]);
+            __m128 k2 = _mm_load_ps(&ResampleCoeffs.FIR8[frac_.i[2]][j]);
+            __m128 k3 = _mm_load_ps(&ResampleCoeffs.FIR8[frac_.i[3]][j]);
+
+            k0 = _mm_mul_ps(k0, val0);
+            k1 = _mm_mul_ps(k1, val1);
+            k2 = _mm_mul_ps(k2, val2);
+            k3 = _mm_mul_ps(k3, val3);
+            k0 = _mm_hadd_ps(k0, k1);
+            k2 = _mm_hadd_ps(k2, k3);
+            out[j>>2] = _mm_hadd_ps(k0, k2);
+        }
+
+        out[0] = _mm_add_ps(out[0], out[1]);
+        _mm_store_ps(&dst[i], out[0]);
+
+        frac4 = _mm_add_epi32(frac4, increment4);
+        pos4 = _mm_add_epi32(pos4, _mm_srli_epi32(frac4, FRACTIONBITS));
+        frac4 = _mm_and_si128(frac4, fracMask4);
+
+        pos_.i[0] = _mm_extract_epi32(pos4, 0);
+        pos_.i[1] = _mm_extract_epi32(pos4, 1);
+        pos_.i[2] = _mm_extract_epi32(pos4, 2);
+        pos_.i[3] = _mm_extract_epi32(pos4, 3);
+        frac_.i[0] = _mm_extract_epi32(frac4, 0);
+        frac_.i[1] = _mm_extract_epi32(frac4, 1);
+        frac_.i[2] = _mm_extract_epi32(frac4, 2);
+        frac_.i[3] = _mm_extract_epi32(frac4, 3);
+    }
+
+    pos = pos_.i[0];
+    frac = frac_.i[0];
+
+    for(;i < numsamples;i++)
+    {
+        dst[i] = resample_fir8(src[pos  ], src[pos+1], src[pos+2], src[pos+3],
+                               src[pos+4], src[pos+5], src[pos+6], src[pos+7], frac);
+
+        frac += increment;
+        pos  += frac>>FRACTIONBITS;
+        frac &= FRACTIONMASK;
+    }
+    return dst;
+}

+ 1037 - 0
Engine/lib/openal-soft/Alc/panning.c

@@ -0,0 +1,1037 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 1999-2010 by authors.
+ * This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the
+ *  Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#include "config.h"
+
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+
+#include "alMain.h"
+#include "alAuxEffectSlot.h"
+#include "alu.h"
+#include "bool.h"
+#include "ambdec.h"
+#include "bformatdec.h"
+#include "uhjfilter.h"
+#include "bs2b.h"
+
+
+extern inline void CalcXYZCoeffs(ALfloat x, ALfloat y, ALfloat z, ALfloat spread, ALfloat coeffs[MAX_AMBI_COEFFS]);
+
+
+#define ZERO_ORDER_SCALE    0.0f
+#define FIRST_ORDER_SCALE   1.0f
+#define SECOND_ORDER_SCALE  (1.0f / 1.22474f)
+#define THIRD_ORDER_SCALE   (1.0f / 1.30657f)
+
+
+static const ALuint FuMa2ACN[MAX_AMBI_COEFFS] = {
+    0,  /* W */
+    3,  /* X */
+    1,  /* Y */
+    2,  /* Z */
+    6,  /* R */
+    7,  /* S */
+    5,  /* T */
+    8,  /* U */
+    4,  /* V */
+    12, /* K */
+    13, /* L */
+    11, /* M */
+    14, /* N */
+    10, /* O */
+    15, /* P */
+    9,  /* Q */
+};
+static const ALuint ACN2ACN[MAX_AMBI_COEFFS] = {
+    0,  1,  2,  3,  4,  5,  6,  7,
+    8,  9, 10, 11, 12, 13, 14, 15
+};
+
+/* NOTE: These are scale factors as applied to Ambisonics content. Decoder
+ * coefficients should be divided by these values to get proper N3D scalings.
+ */
+static const ALfloat UnitScale[MAX_AMBI_COEFFS] = {
+    1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
+    1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f
+};
+static const ALfloat SN3D2N3DScale[MAX_AMBI_COEFFS] = {
+    1.000000000f, /* ACN  0 (W), sqrt(1) */
+    1.732050808f, /* ACN  1 (Y), sqrt(3) */
+    1.732050808f, /* ACN  2 (Z), sqrt(3) */
+    1.732050808f, /* ACN  3 (X), sqrt(3) */
+    2.236067978f, /* ACN  4 (V), sqrt(5) */
+    2.236067978f, /* ACN  5 (T), sqrt(5) */
+    2.236067978f, /* ACN  6 (R), sqrt(5) */
+    2.236067978f, /* ACN  7 (S), sqrt(5) */
+    2.236067978f, /* ACN  8 (U), sqrt(5) */
+    2.645751311f, /* ACN  9 (Q), sqrt(7) */
+    2.645751311f, /* ACN 10 (O), sqrt(7) */
+    2.645751311f, /* ACN 11 (M), sqrt(7) */
+    2.645751311f, /* ACN 12 (K), sqrt(7) */
+    2.645751311f, /* ACN 13 (L), sqrt(7) */
+    2.645751311f, /* ACN 14 (N), sqrt(7) */
+    2.645751311f, /* ACN 15 (P), sqrt(7) */
+};
+static const ALfloat FuMa2N3DScale[MAX_AMBI_COEFFS] = {
+    1.414213562f, /* ACN  0 (W), sqrt(2) */
+    1.732050808f, /* ACN  1 (Y), sqrt(3) */
+    1.732050808f, /* ACN  2 (Z), sqrt(3) */
+    1.732050808f, /* ACN  3 (X), sqrt(3) */
+    1.936491673f, /* ACN  4 (V), sqrt(15)/2 */
+    1.936491673f, /* ACN  5 (T), sqrt(15)/2 */
+    2.236067978f, /* ACN  6 (R), sqrt(5) */
+    1.936491673f, /* ACN  7 (S), sqrt(15)/2 */
+    1.936491673f, /* ACN  8 (U), sqrt(15)/2 */
+    2.091650066f, /* ACN  9 (Q), sqrt(35/8) */
+    1.972026594f, /* ACN 10 (O), sqrt(35)/3 */
+    2.231093404f, /* ACN 11 (M), sqrt(224/45) */
+    2.645751311f, /* ACN 12 (K), sqrt(7) */
+    2.231093404f, /* ACN 13 (L), sqrt(224/45) */
+    1.972026594f, /* ACN 14 (N), sqrt(35)/3 */
+    2.091650066f, /* ACN 15 (P), sqrt(35/8) */
+};
+
+
+void CalcDirectionCoeffs(const ALfloat dir[3], ALfloat spread, ALfloat coeffs[MAX_AMBI_COEFFS])
+{
+    /* Convert from OpenAL coords to Ambisonics. */
+    ALfloat x = -dir[2];
+    ALfloat y = -dir[0];
+    ALfloat z =  dir[1];
+
+    /* Zeroth-order */
+    coeffs[0]  = 1.0f; /* ACN 0 = 1 */
+    /* First-order */
+    coeffs[1]  = 1.732050808f * y; /* ACN 1 = sqrt(3) * Y */
+    coeffs[2]  = 1.732050808f * z; /* ACN 2 = sqrt(3) * Z */
+    coeffs[3]  = 1.732050808f * x; /* ACN 3 = sqrt(3) * X */
+    /* Second-order */
+    coeffs[4]  = 3.872983346f * x * y;             /* ACN 4 = sqrt(15) * X * Y */
+    coeffs[5]  = 3.872983346f * y * z;             /* ACN 5 = sqrt(15) * Y * Z */
+    coeffs[6]  = 1.118033989f * (3.0f*z*z - 1.0f); /* ACN 6 = sqrt(5)/2 * (3*Z*Z - 1) */
+    coeffs[7]  = 3.872983346f * x * z;             /* ACN 7 = sqrt(15) * X * Z */
+    coeffs[8]  = 1.936491673f * (x*x - y*y);       /* ACN 8 = sqrt(15)/2 * (X*X - Y*Y) */
+    /* Third-order */
+    coeffs[9]  =  2.091650066f * y * (3.0f*x*x - y*y);  /* ACN  9 = sqrt(35/8) * Y * (3*X*X - Y*Y) */
+    coeffs[10] = 10.246950766f * z * x * y;             /* ACN 10 = sqrt(105) * Z * X * Y */
+    coeffs[11] =  1.620185175f * y * (5.0f*z*z - 1.0f); /* ACN 11 = sqrt(21/8) * Y * (5*Z*Z - 1) */
+    coeffs[12] =  1.322875656f * z * (5.0f*z*z - 3.0f); /* ACN 12 = sqrt(7)/2 * Z * (5*Z*Z - 3) */
+    coeffs[13] =  1.620185175f * x * (5.0f*z*z - 1.0f); /* ACN 13 = sqrt(21/8) * X * (5*Z*Z - 1) */
+    coeffs[14] =  5.123475383f * z * (x*x - y*y);       /* ACN 14 = sqrt(105)/2 * Z * (X*X - Y*Y) */
+    coeffs[15] =  2.091650066f * x * (x*x - 3.0f*y*y);  /* ACN 15 = sqrt(35/8) * X * (X*X - 3*Y*Y) */
+
+    if(spread > 0.0f)
+    {
+        /* Implement the spread by using a spherical source that subtends the
+         * angle spread. See:
+         * http://www.ppsloan.org/publications/StupidSH36.pdf - Appendix A3
+         *
+         * When adjusted for N3D normalization instead of SN3D, these
+         * calculations are:
+         *
+         * ZH0 = -sqrt(pi) * (-1+ca);
+         * ZH1 =  0.5*sqrt(pi) * sa*sa;
+         * ZH2 = -0.5*sqrt(pi) * ca*(-1+ca)*(ca+1);
+         * ZH3 = -0.125*sqrt(pi) * (-1+ca)*(ca+1)*(5*ca*ca - 1);
+         * ZH4 = -0.125*sqrt(pi) * ca*(-1+ca)*(ca+1)*(7*ca*ca - 3);
+         * ZH5 = -0.0625*sqrt(pi) * (-1+ca)*(ca+1)*(21*ca*ca*ca*ca - 14*ca*ca + 1);
+         *
+         * The gain of the source is compensated for size, so that the
+         * loundness doesn't depend on the spread. That is, the factors are
+         * scaled so that ZH0 remains 1 regardless of the spread. Thus:
+         *
+         * ZH0 = 1.0f;
+         * ZH1 = 0.5f * (ca+1.0f);
+         * ZH2 = 0.5f * (ca+1.0f)*ca;
+         * ZH3 = 0.125f * (ca+1.0f)*(5.0f*ca*ca - 1.0f);
+         * ZH4 = 0.125f * (ca+1.0f)*(7.0f*ca*ca - 3.0f)*ca;
+         * ZH5 = 0.0625f * (ca+1.0f)*(21.0f*ca*ca*ca*ca - 14.0f*ca*ca + 1.0f);
+         */
+        ALfloat ca = cosf(spread * 0.5f);
+
+        ALfloat ZH0_norm = 1.0f;
+        ALfloat ZH1_norm = 0.5f * (ca+1.f);
+        ALfloat ZH2_norm = 0.5f * (ca+1.f)*ca;
+        ALfloat ZH3_norm = 0.125f * (ca+1.f)*(5.f*ca*ca-1.f);
+
+        /* Zeroth-order */
+        coeffs[0]  *= ZH0_norm;
+        /* First-order */
+        coeffs[1]  *= ZH1_norm;
+        coeffs[2]  *= ZH1_norm;
+        coeffs[3]  *= ZH1_norm;
+        /* Second-order */
+        coeffs[4]  *= ZH2_norm;
+        coeffs[5]  *= ZH2_norm;
+        coeffs[6]  *= ZH2_norm;
+        coeffs[7]  *= ZH2_norm;
+        coeffs[8]  *= ZH2_norm;
+        /* Third-order */
+        coeffs[9]  *= ZH3_norm;
+        coeffs[10] *= ZH3_norm;
+        coeffs[11] *= ZH3_norm;
+        coeffs[12] *= ZH3_norm;
+        coeffs[13] *= ZH3_norm;
+        coeffs[14] *= ZH3_norm;
+        coeffs[15] *= ZH3_norm;
+    }
+}
+
+void CalcAngleCoeffs(ALfloat azimuth, ALfloat elevation, ALfloat spread, ALfloat coeffs[MAX_AMBI_COEFFS])
+{
+    ALfloat dir[3] = {
+        sinf(azimuth) * cosf(elevation),
+        sinf(elevation),
+        -cosf(azimuth) * cosf(elevation)
+    };
+    CalcDirectionCoeffs(dir, spread, coeffs);
+}
+
+
+void ComputeAmbientGainsMC(const ChannelConfig *chancoeffs, ALuint numchans, ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS])
+{
+    ALuint i;
+
+    for(i = 0;i < numchans;i++)
+    {
+        // The W coefficients are based on a mathematical average of the
+        // output. The square root of the base average provides for a more
+        // perceptual average volume, better suited to non-directional gains.
+        gains[i] = sqrtf(chancoeffs[i][0]) * ingain;
+    }
+    for(;i < MAX_OUTPUT_CHANNELS;i++)
+        gains[i] = 0.0f;
+}
+
+void ComputeAmbientGainsBF(const BFChannelConfig *chanmap, ALuint numchans, ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS])
+{
+    ALfloat gain = 0.0f;
+    ALuint i;
+
+    for(i = 0;i < numchans;i++)
+    {
+        if(chanmap[i].Index == 0)
+            gain += chanmap[i].Scale;
+    }
+    gains[0] = gain * 1.414213562f * ingain;
+    for(i = 1;i < MAX_OUTPUT_CHANNELS;i++)
+        gains[i] = 0.0f;
+}
+
+void ComputePanningGainsMC(const ChannelConfig *chancoeffs, ALuint numchans, ALuint numcoeffs, const ALfloat coeffs[MAX_AMBI_COEFFS], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS])
+{
+    ALuint i, j;
+
+    for(i = 0;i < numchans;i++)
+    {
+        float gain = 0.0f;
+        for(j = 0;j < numcoeffs;j++)
+            gain += chancoeffs[i][j]*coeffs[j];
+        gains[i] = gain * ingain;
+    }
+    for(;i < MAX_OUTPUT_CHANNELS;i++)
+        gains[i] = 0.0f;
+}
+
+void ComputePanningGainsBF(const BFChannelConfig *chanmap, ALuint numchans, const ALfloat coeffs[MAX_AMBI_COEFFS], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS])
+{
+    ALuint i;
+
+    for(i = 0;i < numchans;i++)
+        gains[i] = chanmap[i].Scale * coeffs[chanmap[i].Index] * ingain;
+    for(;i < MAX_OUTPUT_CHANNELS;i++)
+        gains[i] = 0.0f;
+}
+
+void ComputeFirstOrderGainsMC(const ChannelConfig *chancoeffs, ALuint numchans, const ALfloat mtx[4], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS])
+{
+    ALuint i, j;
+
+    for(i = 0;i < numchans;i++)
+    {
+        float gain = 0.0f;
+        for(j = 0;j < 4;j++)
+            gain += chancoeffs[i][j] * mtx[j];
+        gains[i] = gain * ingain;
+    }
+    for(;i < MAX_OUTPUT_CHANNELS;i++)
+        gains[i] = 0.0f;
+}
+
+void ComputeFirstOrderGainsBF(const BFChannelConfig *chanmap, ALuint numchans, const ALfloat mtx[4], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS])
+{
+    ALuint i;
+
+    for(i = 0;i < numchans;i++)
+        gains[i] = chanmap[i].Scale * mtx[chanmap[i].Index] * ingain;
+    for(;i < MAX_OUTPUT_CHANNELS;i++)
+        gains[i] = 0.0f;
+}
+
+
+static inline const char *GetLabelFromChannel(enum Channel channel)
+{
+    switch(channel)
+    {
+        case FrontLeft: return "front-left";
+        case FrontRight: return "front-right";
+        case FrontCenter: return "front-center";
+        case LFE: return "lfe";
+        case BackLeft: return "back-left";
+        case BackRight: return "back-right";
+        case BackCenter: return "back-center";
+        case SideLeft: return "side-left";
+        case SideRight: return "side-right";
+
+        case UpperFrontLeft: return "upper-front-left";
+        case UpperFrontRight: return "upper-front-right";
+        case UpperBackLeft: return "upper-back-left";
+        case UpperBackRight: return "upper-back-right";
+        case LowerFrontLeft: return "lower-front-left";
+        case LowerFrontRight: return "lower-front-right";
+        case LowerBackLeft: return "lower-back-left";
+        case LowerBackRight: return "lower-back-right";
+
+        case Aux0: return "aux-0";
+        case Aux1: return "aux-1";
+        case Aux2: return "aux-2";
+        case Aux3: return "aux-3";
+        case Aux4: return "aux-4";
+        case Aux5: return "aux-5";
+        case Aux6: return "aux-6";
+        case Aux7: return "aux-7";
+        case Aux8: return "aux-8";
+        case Aux9: return "aux-9";
+        case Aux10: return "aux-10";
+        case Aux11: return "aux-11";
+        case Aux12: return "aux-12";
+        case Aux13: return "aux-13";
+        case Aux14: return "aux-14";
+        case Aux15: return "aux-15";
+
+        case InvalidChannel: break;
+    }
+    return "(unknown)";
+}
+
+
+typedef struct ChannelMap {
+    enum Channel ChanName;
+    ChannelConfig Config;
+} ChannelMap;
+
+static void SetChannelMap(const enum Channel *devchans, ChannelConfig *ambicoeffs,
+                          const ChannelMap *chanmap, size_t count, ALuint *outcount,
+                          ALboolean isfuma)
+{
+    const ALuint *acnmap = isfuma ? FuMa2ACN : ACN2ACN;
+    const ALfloat *n3dscale = isfuma ? FuMa2N3DScale : UnitScale;
+    size_t j, k;
+    ALuint i;
+
+    for(i = 0;i < MAX_OUTPUT_CHANNELS && devchans[i] != InvalidChannel;i++)
+    {
+        if(devchans[i] == LFE)
+        {
+            for(j = 0;j < MAX_AMBI_COEFFS;j++)
+                ambicoeffs[i][j] = 0.0f;
+            continue;
+        }
+
+        for(j = 0;j < count;j++)
+        {
+            if(devchans[i] != chanmap[j].ChanName)
+                continue;
+
+            for(k = 0;k < MAX_AMBI_COEFFS;++k)
+            {
+                ALuint acn = acnmap[k];
+                ambicoeffs[i][acn] = chanmap[j].Config[k] / n3dscale[acn];
+            }
+            break;
+        }
+        if(j == count)
+            ERR("Failed to match %s channel (%u) in channel map\n", GetLabelFromChannel(devchans[i]), i);
+    }
+    *outcount = i;
+}
+
+static bool MakeSpeakerMap(ALCdevice *device, const AmbDecConf *conf, ALuint speakermap[MAX_OUTPUT_CHANNELS])
+{
+    ALuint i;
+
+    for(i = 0;i < conf->NumSpeakers;i++)
+    {
+        int c = -1;
+
+        /* NOTE: AmbDec does not define any standard speaker names, however
+         * for this to work we have to by able to find the output channel
+         * the speaker definition corresponds to. Therefore, OpenAL Soft
+         * requires these channel labels to be recognized:
+         *
+         * LF = Front left
+         * RF = Front right
+         * LS = Side left
+         * RS = Side right
+         * LB = Back left
+         * RB = Back right
+         * CE = Front center
+         * CB = Back center
+         *
+         * Additionally, surround51 will acknowledge back speakers for side
+         * channels, and surround51rear will acknowledge side speakers for
+         * back channels, to avoid issues with an ambdec expecting 5.1 to
+         * use the side channels when the device is configured for back,
+         * and vice-versa.
+         */
+        if(al_string_cmp_cstr(conf->Speakers[i].Name, "LF") == 0)
+            c = GetChannelIdxByName(device->RealOut, FrontLeft);
+        else if(al_string_cmp_cstr(conf->Speakers[i].Name, "RF") == 0)
+            c = GetChannelIdxByName(device->RealOut, FrontRight);
+        else if(al_string_cmp_cstr(conf->Speakers[i].Name, "CE") == 0)
+            c = GetChannelIdxByName(device->RealOut, FrontCenter);
+        else if(al_string_cmp_cstr(conf->Speakers[i].Name, "LS") == 0)
+        {
+            if(device->FmtChans == DevFmtX51Rear)
+                c = GetChannelIdxByName(device->RealOut, BackLeft);
+            else
+                c = GetChannelIdxByName(device->RealOut, SideLeft);
+        }
+        else if(al_string_cmp_cstr(conf->Speakers[i].Name, "RS") == 0)
+        {
+            if(device->FmtChans == DevFmtX51Rear)
+                c = GetChannelIdxByName(device->RealOut, BackRight);
+            else
+                c = GetChannelIdxByName(device->RealOut, SideRight);
+        }
+        else if(al_string_cmp_cstr(conf->Speakers[i].Name, "LB") == 0)
+        {
+            if(device->FmtChans == DevFmtX51)
+                c = GetChannelIdxByName(device->RealOut, SideLeft);
+            else
+                c = GetChannelIdxByName(device->RealOut, BackLeft);
+        }
+        else if(al_string_cmp_cstr(conf->Speakers[i].Name, "RB") == 0)
+        {
+            if(device->FmtChans == DevFmtX51)
+                c = GetChannelIdxByName(device->RealOut, SideRight);
+            else
+                c = GetChannelIdxByName(device->RealOut, BackRight);
+        }
+        else if(al_string_cmp_cstr(conf->Speakers[i].Name, "CB") == 0)
+            c = GetChannelIdxByName(device->RealOut, BackCenter);
+        else
+        {
+            const char *name = al_string_get_cstr(conf->Speakers[i].Name);
+            unsigned int n;
+            char ch;
+
+            if(sscanf(name, "AUX%u%c", &n, &ch) == 1 && n < 16)
+                c = GetChannelIdxByName(device->RealOut, Aux0+n);
+            else
+            {
+                ERR("AmbDec speaker label \"%s\" not recognized\n", name);
+                return false;
+            }
+        }
+        if(c == -1)
+        {
+            ERR("Failed to lookup AmbDec speaker label %s\n",
+                al_string_get_cstr(conf->Speakers[i].Name));
+            return false;
+        }
+        speakermap[i] = c;
+    }
+
+    return true;
+}
+
+
+/* NOTE: These decoder coefficients are using FuMa channel ordering and
+ * normalization, since that's what was produced by the Ambisonic Decoder
+ * Toolbox. SetChannelMap will convert them to N3D.
+ */
+static const ChannelMap MonoCfg[1] = {
+    { FrontCenter, { 1.414213562f } },
+}, StereoCfg[2] = {
+    { FrontLeft,   { 0.707106781f, 0.0f,  0.5f, 0.0f } },
+    { FrontRight,  { 0.707106781f, 0.0f, -0.5f, 0.0f } },
+}, QuadCfg[4] = {
+    { FrontLeft,   { 0.353553f,  0.306186f,  0.306186f, 0.0f,  0.0f, 0.0f, 0.0f,  0.000000f,  0.125000f } },
+    { FrontRight,  { 0.353553f,  0.306186f, -0.306186f, 0.0f,  0.0f, 0.0f, 0.0f,  0.000000f, -0.125000f } },
+    { BackLeft,    { 0.353553f, -0.306186f,  0.306186f, 0.0f,  0.0f, 0.0f, 0.0f,  0.000000f, -0.125000f } },
+    { BackRight,   { 0.353553f, -0.306186f, -0.306186f, 0.0f,  0.0f, 0.0f, 0.0f,  0.000000f,  0.125000f } },
+}, X51SideCfg[5] = {
+    { FrontLeft,   { 0.208954f,  0.199518f,  0.223424f, 0.0f,  0.0f, 0.0f, 0.0f, -0.012543f,  0.144260f } },
+    { FrontRight,  { 0.208950f,  0.199514f, -0.223425f, 0.0f,  0.0f, 0.0f, 0.0f, -0.012544f, -0.144258f } },
+    { FrontCenter, { 0.109403f,  0.168250f, -0.000002f, 0.0f,  0.0f, 0.0f, 0.0f,  0.100431f, -0.000001f } },
+    { SideLeft,    { 0.470934f, -0.346484f,  0.327504f, 0.0f,  0.0f, 0.0f, 0.0f, -0.022188f, -0.041113f } },
+    { SideRight,   { 0.470936f, -0.346480f, -0.327507f, 0.0f,  0.0f, 0.0f, 0.0f, -0.022186f,  0.041114f } },
+}, X51RearCfg[5] = {
+    { FrontLeft,   { 0.208954f,  0.199518f,  0.223424f, 0.0f,  0.0f, 0.0f, 0.0f, -0.012543f,  0.144260f } },
+    { FrontRight,  { 0.208950f,  0.199514f, -0.223425f, 0.0f,  0.0f, 0.0f, 0.0f, -0.012544f, -0.144258f } },
+    { FrontCenter, { 0.109403f,  0.168250f, -0.000002f, 0.0f,  0.0f, 0.0f, 0.0f,  0.100431f, -0.000001f } },
+    { BackLeft,    { 0.470934f, -0.346484f,  0.327504f, 0.0f,  0.0f, 0.0f, 0.0f, -0.022188f, -0.041113f } },
+    { BackRight,   { 0.470936f, -0.346480f, -0.327507f, 0.0f,  0.0f, 0.0f, 0.0f, -0.022186f,  0.041114f } },
+}, X61Cfg[6] = {
+    { FrontLeft,   { 0.167065f,  0.200583f,  0.172695f, 0.0f, 0.0f, 0.0f, 0.0f,  0.029855f,  0.186407f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.039241f,  0.068910f } },
+    { FrontRight,  { 0.167065f,  0.200583f, -0.172695f, 0.0f, 0.0f, 0.0f, 0.0f,  0.029855f, -0.186407f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.039241f, -0.068910f } },
+    { FrontCenter, { 0.109403f,  0.179490f,  0.000000f, 0.0f, 0.0f, 0.0f, 0.0f,  0.142031f,  0.000000f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,  0.072024f,  0.000000f } },
+    { BackCenter,  { 0.353556f, -0.461940f,  0.000000f, 0.0f, 0.0f, 0.0f, 0.0f,  0.165723f,  0.000000f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,  0.000000f,  0.000000f } },
+    { SideLeft,    { 0.289151f, -0.081301f,  0.401292f, 0.0f, 0.0f, 0.0f, 0.0f, -0.188208f, -0.071420f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,  0.010099f, -0.032897f } },
+    { SideRight,   { 0.289151f, -0.081301f, -0.401292f, 0.0f, 0.0f, 0.0f, 0.0f, -0.188208f,  0.071420f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,  0.010099f,  0.032897f } },
+}, X71Cfg[7] = {
+    { FrontLeft,   { 0.167065f,  0.200583f,  0.172695f, 0.0f, 0.0f, 0.0f, 0.0f,  0.029855f,  0.186407f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.039241f,  0.068910f } },
+    { FrontRight,  { 0.167065f,  0.200583f, -0.172695f, 0.0f, 0.0f, 0.0f, 0.0f,  0.029855f, -0.186407f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.039241f, -0.068910f } },
+    { FrontCenter, { 0.109403f,  0.179490f,  0.000000f, 0.0f, 0.0f, 0.0f, 0.0f,  0.142031f,  0.000000f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,  0.072024f,  0.000000f } },
+    { BackLeft,    { 0.224752f, -0.295009f,  0.170325f, 0.0f, 0.0f, 0.0f, 0.0f,  0.105349f, -0.182473f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,  0.000000f,  0.065799f } },
+    { BackRight,   { 0.224752f, -0.295009f, -0.170325f, 0.0f, 0.0f, 0.0f, 0.0f,  0.105349f,  0.182473f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,  0.000000f, -0.065799f } },
+    { SideLeft,    { 0.224739f,  0.000000f,  0.340644f, 0.0f, 0.0f, 0.0f, 0.0f, -0.210697f,  0.000000f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,  0.000000f, -0.065795f } },
+    { SideRight,   { 0.224739f,  0.000000f, -0.340644f, 0.0f, 0.0f, 0.0f, 0.0f, -0.210697f,  0.000000f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,  0.000000f,  0.065795f } },
+};
+
+static void InitPanning(ALCdevice *device)
+{
+    const ChannelMap *chanmap = NULL;
+    ALuint coeffcount = 0;
+    ALfloat ambiscale;
+    size_t count = 0;
+    ALuint i, j;
+
+    ambiscale = 1.0f;
+    switch(device->FmtChans)
+    {
+        case DevFmtMono:
+            count = COUNTOF(MonoCfg);
+            chanmap = MonoCfg;
+            ambiscale = ZERO_ORDER_SCALE;
+            coeffcount = 1;
+            break;
+
+        case DevFmtStereo:
+            count = COUNTOF(StereoCfg);
+            chanmap = StereoCfg;
+            ambiscale = FIRST_ORDER_SCALE;
+            coeffcount = 4;
+            break;
+
+        case DevFmtQuad:
+            count = COUNTOF(QuadCfg);
+            chanmap = QuadCfg;
+            ambiscale = SECOND_ORDER_SCALE;
+            coeffcount = 9;
+            break;
+
+        case DevFmtX51:
+            count = COUNTOF(X51SideCfg);
+            chanmap = X51SideCfg;
+            ambiscale = SECOND_ORDER_SCALE;
+            coeffcount = 9;
+            break;
+
+        case DevFmtX51Rear:
+            count = COUNTOF(X51RearCfg);
+            chanmap = X51RearCfg;
+            ambiscale = SECOND_ORDER_SCALE;
+            coeffcount = 9;
+            break;
+
+        case DevFmtX61:
+            count = COUNTOF(X61Cfg);
+            chanmap = X61Cfg;
+            ambiscale = THIRD_ORDER_SCALE;
+            coeffcount = 16;
+            break;
+
+        case DevFmtX71:
+            count = COUNTOF(X71Cfg);
+            chanmap = X71Cfg;
+            ambiscale = THIRD_ORDER_SCALE;
+            coeffcount = 16;
+            break;
+
+        case DevFmtAmbi1:
+        case DevFmtAmbi2:
+        case DevFmtAmbi3:
+            break;
+    }
+
+    if(device->FmtChans >= DevFmtAmbi1 && device->FmtChans <= DevFmtAmbi3)
+    {
+        const ALuint *acnmap = (device->AmbiFmt == AmbiFormat_FuMa) ? FuMa2ACN : ACN2ACN;
+        const ALfloat *n3dscale = (device->AmbiFmt == AmbiFormat_FuMa) ? FuMa2N3DScale :
+                                  (device->AmbiFmt == AmbiFormat_ACN_SN3D) ? SN3D2N3DScale :
+                                  /*(device->AmbiFmt == AmbiFormat_ACN_N3D) ?*/ UnitScale;
+
+        count = (device->FmtChans == DevFmtAmbi3) ? 16 :
+                (device->FmtChans == DevFmtAmbi2) ? 9 :
+                (device->FmtChans == DevFmtAmbi1) ? 4 : 1;
+        for(i = 0;i < count;i++)
+        {
+            ALuint acn = acnmap[i];
+            device->Dry.Ambi.Map[i].Scale = 1.0f/n3dscale[acn];
+            device->Dry.Ambi.Map[i].Index = acn;
+        }
+        device->Dry.CoeffCount = 0;
+        device->Dry.NumChannels = count;
+
+        if(device->FmtChans == DevFmtAmbi1)
+        {
+            device->FOAOut.Ambi = device->Dry.Ambi;
+            device->FOAOut.CoeffCount = device->Dry.CoeffCount;
+        }
+        else
+        {
+            /* FOA output is always ACN+N3D for higher-order ambisonic output.
+             * The upsampler expects this and will convert it for output.
+             */
+            memset(&device->FOAOut.Ambi, 0, sizeof(device->FOAOut.Ambi));
+            for(i = 0;i < 4;i++)
+            {
+                device->FOAOut.Ambi.Map[i].Scale = 1.0f;
+                device->FOAOut.Ambi.Map[i].Index = i;
+            }
+            device->FOAOut.CoeffCount = 0;
+
+            ambiup_reset(device->AmbiUp, device);
+        }
+    }
+    else
+    {
+        SetChannelMap(device->RealOut.ChannelName, device->Dry.Ambi.Coeffs,
+                      chanmap, count, &device->Dry.NumChannels, AL_TRUE);
+        device->Dry.CoeffCount = coeffcount;
+
+        memset(&device->FOAOut.Ambi, 0, sizeof(device->FOAOut.Ambi));
+        for(i = 0;i < device->Dry.NumChannels;i++)
+        {
+            device->FOAOut.Ambi.Coeffs[i][0] = device->Dry.Ambi.Coeffs[i][0];
+            for(j = 1;j < 4;j++)
+                device->FOAOut.Ambi.Coeffs[i][j] = device->Dry.Ambi.Coeffs[i][j] * ambiscale;
+        }
+        device->FOAOut.CoeffCount = 4;
+    }
+}
+
+static void InitCustomPanning(ALCdevice *device, const AmbDecConf *conf, const ALuint speakermap[MAX_OUTPUT_CHANNELS])
+{
+    ChannelMap chanmap[MAX_OUTPUT_CHANNELS];
+    const ALfloat *coeff_scale = UnitScale;
+    ALfloat ambiscale = 1.0f;
+    ALuint i, j;
+
+    if(conf->FreqBands != 1)
+        ERR("Basic renderer uses the high-frequency matrix as single-band (xover_freq = %.0fhz)\n",
+            conf->XOverFreq);
+
+    if(conf->ChanMask > 0x1ff)
+        ambiscale = THIRD_ORDER_SCALE;
+    else if(conf->ChanMask > 0xf)
+        ambiscale = SECOND_ORDER_SCALE;
+    else if(conf->ChanMask > 0x1)
+        ambiscale = FIRST_ORDER_SCALE;
+    else
+        ambiscale = 0.0f;
+
+    if(conf->CoeffScale == ADS_SN3D)
+        coeff_scale = SN3D2N3DScale;
+    else if(conf->CoeffScale == ADS_FuMa)
+        coeff_scale = FuMa2N3DScale;
+
+    for(i = 0;i < conf->NumSpeakers;i++)
+    {
+        ALuint chan = speakermap[i];
+        ALfloat gain;
+        ALuint k = 0;
+
+        for(j = 0;j < MAX_AMBI_COEFFS;j++)
+            chanmap[i].Config[j] = 0.0f;
+
+        chanmap[i].ChanName = device->RealOut.ChannelName[chan];
+        for(j = 0;j < MAX_AMBI_COEFFS;j++)
+        {
+            if(j == 0) gain = conf->HFOrderGain[0];
+            else if(j == 1) gain = conf->HFOrderGain[1];
+            else if(j == 4) gain = conf->HFOrderGain[2];
+            else if(j == 9) gain = conf->HFOrderGain[3];
+            if((conf->ChanMask&(1<<j)))
+                chanmap[i].Config[j] = conf->HFMatrix[i][k++] / coeff_scale[j] * gain;
+        }
+    }
+
+    SetChannelMap(device->RealOut.ChannelName, device->Dry.Ambi.Coeffs, chanmap,
+                  conf->NumSpeakers, &device->Dry.NumChannels, AL_FALSE);
+    device->Dry.CoeffCount = (conf->ChanMask > 0x1ff) ? 16 :
+                             (conf->ChanMask > 0xf) ? 9 : 4;
+
+    memset(&device->FOAOut.Ambi, 0, sizeof(device->FOAOut.Ambi));
+    for(i = 0;i < device->Dry.NumChannels;i++)
+    {
+        device->FOAOut.Ambi.Coeffs[i][0] = device->Dry.Ambi.Coeffs[i][0];
+        for(j = 1;j < 4;j++)
+            device->FOAOut.Ambi.Coeffs[i][j] = device->Dry.Ambi.Coeffs[i][j] * ambiscale;
+    }
+    device->FOAOut.CoeffCount = 4;
+}
+
+static void InitHQPanning(ALCdevice *device, const AmbDecConf *conf, const ALuint speakermap[MAX_OUTPUT_CHANNELS])
+{
+    const char *devname;
+    int decflags = 0;
+    size_t count;
+    ALuint i;
+
+    devname = al_string_get_cstr(device->DeviceName);
+    if(GetConfigValueBool(devname, "decoder", "distance-comp", 1))
+        decflags |= BFDF_DistanceComp;
+
+    if((conf->ChanMask&AMBI_PERIPHONIC_MASK))
+    {
+        count = (conf->ChanMask > 0x1ff) ? 16 :
+                (conf->ChanMask > 0xf) ? 9 : 4;
+        for(i = 0;i < count;i++)
+        {
+            device->Dry.Ambi.Map[i].Scale = 1.0f;
+            device->Dry.Ambi.Map[i].Index = i;
+        }
+    }
+    else
+    {
+        static const int map[MAX_AMBI2D_COEFFS] = { 0, 1, 3, 4, 8, 9, 15 };
+
+        count = (conf->ChanMask > 0x1ff) ? 7 :
+                (conf->ChanMask > 0xf) ? 5 : 3;
+        for(i = 0;i < count;i++)
+        {
+            device->Dry.Ambi.Map[i].Scale = 1.0f;
+            device->Dry.Ambi.Map[i].Index = map[i];
+        }
+    }
+    device->Dry.CoeffCount = 0;
+    device->Dry.NumChannels = count;
+
+    TRACE("Enabling %s-band %s-order%s ambisonic decoder\n",
+        (conf->FreqBands == 1) ? "single" : "dual",
+        (conf->ChanMask > 0xf) ? (conf->ChanMask > 0x1ff) ? "third" : "second" : "first",
+        (conf->ChanMask&AMBI_PERIPHONIC_MASK) ? " periphonic" : ""
+    );
+    bformatdec_reset(device->AmbiDecoder, conf, count, device->Frequency,
+                     speakermap, decflags);
+
+    if(bformatdec_getOrder(device->AmbiDecoder) < 2)
+    {
+        device->FOAOut.Ambi = device->Dry.Ambi;
+        device->FOAOut.CoeffCount = device->Dry.CoeffCount;
+    }
+    else
+    {
+        memset(&device->FOAOut.Ambi, 0, sizeof(device->FOAOut.Ambi));
+        for(i = 0;i < 4;i++)
+        {
+            device->FOAOut.Ambi.Map[i].Scale = 1.0f;
+            device->FOAOut.Ambi.Map[i].Index = i;
+        }
+        device->FOAOut.CoeffCount = 0;
+    }
+}
+
+static void InitHrtfPanning(ALCdevice *device)
+{
+    size_t count = 4;
+    ALuint i;
+
+    for(i = 0;i < count;i++)
+    {
+        device->Dry.Ambi.Map[i].Scale = 1.0f;
+        device->Dry.Ambi.Map[i].Index = i;
+    }
+    device->Dry.CoeffCount = 0;
+    device->Dry.NumChannels = count;
+
+    device->FOAOut.Ambi = device->Dry.Ambi;
+    device->FOAOut.CoeffCount = device->Dry.CoeffCount;
+
+    memset(device->Hrtf.Coeffs, 0, sizeof(device->Hrtf.Coeffs));
+    device->Hrtf.IrSize = BuildBFormatHrtf(device->Hrtf.Handle,
+        device->Hrtf.Coeffs, device->Dry.NumChannels
+    );
+
+    /* Round up to the nearest multiple of 8 */
+    device->Hrtf.IrSize = (device->Hrtf.IrSize+7)&~7;
+}
+
+static void InitUhjPanning(ALCdevice *device)
+{
+    size_t count = 3;
+    ALuint i;
+
+    for(i = 0;i < count;i++)
+    {
+        ALuint acn = FuMa2ACN[i];
+        device->Dry.Ambi.Map[i].Scale = 1.0f/FuMa2N3DScale[acn];
+        device->Dry.Ambi.Map[i].Index = acn;
+    }
+    device->Dry.CoeffCount = 0;
+    device->Dry.NumChannels = count;
+
+    device->FOAOut.Ambi = device->Dry.Ambi;
+    device->FOAOut.CoeffCount = device->Dry.CoeffCount;
+}
+
+void aluInitRenderer(ALCdevice *device, ALint hrtf_id, enum HrtfRequestMode hrtf_appreq, enum HrtfRequestMode hrtf_userreq)
+{
+    const char *mode;
+    bool headphones;
+    int bs2blevel;
+    size_t i;
+
+    device->Hrtf.Handle = NULL;
+    al_string_clear(&device->Hrtf.Name);
+    device->Render_Mode = NormalRender;
+
+    memset(&device->Dry.Ambi, 0, sizeof(device->Dry.Ambi));
+    device->Dry.CoeffCount = 0;
+    device->Dry.NumChannels = 0;
+
+    if(device->FmtChans != DevFmtStereo)
+    {
+        ALuint speakermap[MAX_OUTPUT_CHANNELS];
+        const char *devname, *layout = NULL;
+        AmbDecConf conf, *pconf = NULL;
+
+        if(hrtf_appreq == Hrtf_Enable)
+            device->Hrtf.Status = ALC_HRTF_UNSUPPORTED_FORMAT_SOFT;
+
+        ambdec_init(&conf);
+
+        devname = al_string_get_cstr(device->DeviceName);
+        switch(device->FmtChans)
+        {
+            case DevFmtQuad: layout = "quad"; break;
+            case DevFmtX51: layout = "surround51"; break;
+            case DevFmtX51Rear: layout = "surround51rear"; break;
+            case DevFmtX61: layout = "surround61"; break;
+            case DevFmtX71: layout = "surround71"; break;
+            /* Mono, Stereo, and Ambisonics output don't use custom decoders. */
+            case DevFmtMono:
+            case DevFmtStereo:
+            case DevFmtAmbi1:
+            case DevFmtAmbi2:
+            case DevFmtAmbi3:
+                break;
+        }
+        if(layout)
+        {
+            const char *fname;
+            if(ConfigValueStr(devname, "decoder", layout, &fname))
+            {
+                if(!ambdec_load(&conf, fname))
+                    ERR("Failed to load layout file %s\n", fname);
+                else
+                {
+                    if(conf.ChanMask > 0xffff)
+                        ERR("Unsupported channel mask 0x%04x (max 0xffff)\n", conf.ChanMask);
+                    else
+                    {
+                        if(MakeSpeakerMap(device, &conf, speakermap))
+                            pconf = &conf;
+                    }
+                }
+            }
+        }
+
+        if(pconf && GetConfigValueBool(devname, "decoder", "hq-mode", 0))
+        {
+            ambiup_free(device->AmbiUp);
+            device->AmbiUp = NULL;
+            if(!device->AmbiDecoder)
+                device->AmbiDecoder = bformatdec_alloc();
+        }
+        else
+        {
+            bformatdec_free(device->AmbiDecoder);
+            device->AmbiDecoder = NULL;
+            if(device->FmtChans > DevFmtAmbi1 && device->FmtChans <= DevFmtAmbi3)
+            {
+                if(!device->AmbiUp)
+                    device->AmbiUp = ambiup_alloc();
+            }
+            else
+            {
+                ambiup_free(device->AmbiUp);
+                device->AmbiUp = NULL;
+            }
+        }
+
+        if(!pconf)
+            InitPanning(device);
+        else if(device->AmbiDecoder)
+            InitHQPanning(device, pconf, speakermap);
+        else
+            InitCustomPanning(device, pconf, speakermap);
+
+        ambdec_deinit(&conf);
+        return;
+    }
+
+    ambiup_free(device->AmbiUp);
+    device->AmbiUp = NULL;
+    bformatdec_free(device->AmbiDecoder);
+    device->AmbiDecoder = NULL;
+
+    headphones = device->IsHeadphones;
+    if(device->Type != Loopback)
+    {
+        const char *mode;
+        if(ConfigValueStr(al_string_get_cstr(device->DeviceName), NULL, "stereo-mode", &mode))
+        {
+            if(strcasecmp(mode, "headphones") == 0)
+                headphones = true;
+            else if(strcasecmp(mode, "speakers") == 0)
+                headphones = false;
+            else if(strcasecmp(mode, "auto") != 0)
+                ERR("Unexpected stereo-mode: %s\n", mode);
+        }
+    }
+
+    if(hrtf_userreq == Hrtf_Default)
+    {
+        bool usehrtf = (headphones && hrtf_appreq != Hrtf_Disable) ||
+                       (hrtf_appreq == Hrtf_Enable);
+        if(!usehrtf) goto no_hrtf;
+
+        device->Hrtf.Status = ALC_HRTF_ENABLED_SOFT;
+        if(headphones && hrtf_appreq != Hrtf_Disable)
+            device->Hrtf.Status = ALC_HRTF_HEADPHONES_DETECTED_SOFT;
+    }
+    else
+    {
+        if(hrtf_userreq != Hrtf_Enable)
+        {
+            if(hrtf_appreq == Hrtf_Enable)
+                device->Hrtf.Status = ALC_HRTF_DENIED_SOFT;
+            goto no_hrtf;
+        }
+        device->Hrtf.Status = ALC_HRTF_REQUIRED_SOFT;
+    }
+
+    if(VECTOR_SIZE(device->Hrtf.List) == 0)
+    {
+        VECTOR_DEINIT(device->Hrtf.List);
+        device->Hrtf.List = EnumerateHrtf(device->DeviceName);
+    }
+
+    if(hrtf_id >= 0 && (size_t)hrtf_id < VECTOR_SIZE(device->Hrtf.List))
+    {
+        const HrtfEntry *entry = &VECTOR_ELEM(device->Hrtf.List, hrtf_id);
+        if(entry->hrtf->sampleRate == device->Frequency)
+        {
+            device->Hrtf.Handle = entry->hrtf;
+            al_string_copy(&device->Hrtf.Name, entry->name);
+        }
+    }
+
+    for(i = 0;!device->Hrtf.Handle && i < VECTOR_SIZE(device->Hrtf.List);i++)
+    {
+        const HrtfEntry *entry = &VECTOR_ELEM(device->Hrtf.List, i);
+        if(entry->hrtf->sampleRate == device->Frequency)
+        {
+            device->Hrtf.Handle = entry->hrtf;
+            al_string_copy(&device->Hrtf.Name, entry->name);
+        }
+    }
+
+    if(device->Hrtf.Handle)
+    {
+        device->Render_Mode = HrtfRender;
+        if(ConfigValueStr(al_string_get_cstr(device->DeviceName), NULL, "hrtf-mode", &mode))
+        {
+            if(strcasecmp(mode, "full") == 0)
+                device->Render_Mode = HrtfRender;
+            else if(strcasecmp(mode, "basic") == 0)
+                device->Render_Mode = NormalRender;
+            else
+                ERR("Unexpected hrtf-mode: %s\n", mode);
+        }
+
+        TRACE("HRTF enabled, \"%s\"\n", al_string_get_cstr(device->Hrtf.Name));
+        InitHrtfPanning(device);
+        return;
+    }
+    device->Hrtf.Status = ALC_HRTF_UNSUPPORTED_FORMAT_SOFT;
+
+no_hrtf:
+    TRACE("HRTF disabled\n");
+
+    bs2blevel = ((headphones && hrtf_appreq != Hrtf_Disable) ||
+                 (hrtf_appreq == Hrtf_Enable)) ? 5 : 0;
+    if(device->Type != Loopback)
+        ConfigValueInt(al_string_get_cstr(device->DeviceName), NULL, "cf_level", &bs2blevel);
+    if(bs2blevel > 0 && bs2blevel <= 6)
+    {
+        device->Bs2b = al_calloc(16, sizeof(*device->Bs2b));
+        bs2b_set_params(device->Bs2b, bs2blevel, device->Frequency);
+        device->Render_Mode = StereoPair;
+        TRACE("BS2B enabled\n");
+        InitPanning(device);
+        return;
+    }
+
+    TRACE("BS2B disabled\n");
+
+    device->Render_Mode = NormalRender;
+    if(ConfigValueStr(al_string_get_cstr(device->DeviceName), NULL, "stereo-panning", &mode))
+    {
+        if(strcasecmp(mode, "paired") == 0)
+            device->Render_Mode = StereoPair;
+        else if(strcasecmp(mode, "uhj") != 0)
+            ERR("Unexpected stereo-panning: %s\n", mode);
+    }
+    if(device->Render_Mode == NormalRender)
+    {
+        device->Uhj_Encoder = al_calloc(16, sizeof(Uhj2Encoder));
+        TRACE("UHJ enabled\n");
+        InitUhjPanning(device);
+        return;
+    }
+
+    TRACE("UHJ disabled\n");
+    InitPanning(device);
+}
+
+
+void aluInitEffectPanning(ALeffectslot *slot)
+{
+    ALuint i;
+
+    memset(slot->ChanMap, 0, sizeof(slot->ChanMap));
+    slot->NumChannels = 0;
+
+    for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
+    {
+        slot->ChanMap[i].Scale = 1.0f;
+        slot->ChanMap[i].Index = i;
+    }
+    slot->NumChannels = i;
+}

+ 134 - 0
Engine/lib/openal-soft/Alc/uhjfilter.c

@@ -0,0 +1,134 @@
+
+#include "config.h"
+
+#include "alu.h"
+#include "uhjfilter.h"
+
+/* This is the maximum number of samples processed for each inner loop
+ * iteration. */
+#define MAX_UPDATE_SAMPLES  128
+
+
+static const ALfloat Filter1Coeff[4] = {
+    0.6923878f, 0.9360654322959f, 0.9882295226860f, 0.9987488452737f
+};
+static const ALfloat Filter2Coeff[4] = {
+    0.4021921162426f, 0.8561710882420f, 0.9722909545651f, 0.9952884791278f
+};
+
+static void allpass_process(AllPassState *state, ALfloat *restrict dst, const ALfloat *restrict src, const ALfloat aa, ALuint todo)
+{
+    ALuint i;
+
+    if(todo > 1)
+    {
+        dst[0] = aa*(src[0] + state->y[1]) - state->x[1];
+        dst[1] = aa*(src[1] + state->y[0]) - state->x[0];
+        for(i = 2;i < todo;i++)
+            dst[i] = aa*(src[i] + dst[i-2]) - src[i-2];
+        state->x[1] = src[i-2];
+        state->x[0] = src[i-1];
+        state->y[1] = dst[i-2];
+        state->y[0] = dst[i-1];
+    }
+    else if(todo == 1)
+    {
+        dst[0] = aa*(src[0] + state->y[1]) - state->x[1];
+        state->x[1] = state->x[0];
+        state->x[0] = src[0];
+        state->y[1] = state->y[0];
+        state->y[0] = dst[0];
+    }
+}
+
+
+/* NOTE: There seems to be a bit of an inconsistency in how this encoding is
+ * supposed to work. Some references, such as
+ *
+ * http://members.tripod.com/martin_leese/Ambisonic/UHJ_file_format.html
+ *
+ * specify a pre-scaling of sqrt(2) on the W channel input, while other
+ * references, such as
+ *
+ * https://en.wikipedia.org/wiki/Ambisonic_UHJ_format#Encoding.5B1.5D
+ * and
+ * https://wiki.xiph.org/Ambisonics#UHJ_format
+ *
+ * do not. The sqrt(2) scaling is in line with B-Format decoder coefficients
+ * which include such a scaling for the W channel input, however the original
+ * source for this equation is a 1985 paper by Michael Gerzon, which does not
+ * apparently include the scaling. Applying the extra scaling creates a louder
+ * result with a narrower stereo image compared to not scaling, and I don't
+ * know which is the intended result.
+ */
+
+void EncodeUhj2(Uhj2Encoder *enc, ALfloat *restrict LeftOut, ALfloat *restrict RightOut, ALfloat (*restrict InSamples)[BUFFERSIZE], ALuint SamplesToDo)
+{
+    ALfloat D[MAX_UPDATE_SAMPLES], S[MAX_UPDATE_SAMPLES];
+    ALfloat temp[2][MAX_UPDATE_SAMPLES];
+    ALuint base, i;
+
+    for(base = 0;base < SamplesToDo;)
+    {
+        ALuint todo = minu(SamplesToDo - base, MAX_UPDATE_SAMPLES);
+
+        /* D = 0.6554516*Y */
+        for(i = 0;i < todo;i++)
+            temp[0][i] = 0.6554516f*InSamples[2][base+i];
+        allpass_process(&enc->Filter1_Y[0], temp[1], temp[0],
+                        Filter1Coeff[0]*Filter1Coeff[0], todo);
+        allpass_process(&enc->Filter1_Y[1], temp[0], temp[1],
+                        Filter1Coeff[1]*Filter1Coeff[1], todo);
+        allpass_process(&enc->Filter1_Y[2], temp[1], temp[0],
+                        Filter1Coeff[2]*Filter1Coeff[2], todo);
+        /* NOTE: Filter1 requires a 1 sample delay for the final output, so
+         * take the last processed sample from the previous run as the first
+         * output sample.
+         */
+        D[0] = enc->Filter1_Y[3].y[0];
+        allpass_process(&enc->Filter1_Y[3], temp[0], temp[1],
+                        Filter1Coeff[3]*Filter1Coeff[3], todo);
+        for(i = 1;i < todo;i++)
+            D[i] = temp[0][i-1];
+
+        /* D += j(-0.3420201*W + 0.5098604*X) */
+        for(i = 0;i < todo;i++)
+            temp[0][i] = -0.3420201f*InSamples[0][base+i] +
+                          0.5098604f*InSamples[1][base+i];
+        allpass_process(&enc->Filter2_WX[0], temp[1], temp[0],
+                        Filter2Coeff[0]*Filter2Coeff[0], todo);
+        allpass_process(&enc->Filter2_WX[1], temp[0], temp[1],
+                        Filter2Coeff[1]*Filter2Coeff[1], todo);
+        allpass_process(&enc->Filter2_WX[2], temp[1], temp[0],
+                        Filter2Coeff[2]*Filter2Coeff[2], todo);
+        allpass_process(&enc->Filter2_WX[3], temp[0], temp[1],
+                        Filter2Coeff[3]*Filter2Coeff[3], todo);
+        for(i = 0;i < todo;i++)
+            D[i] += temp[0][i];
+
+        /* S = 0.9396926*W + 0.1855740*X */
+        for(i = 0;i < todo;i++)
+            temp[0][i] = 0.9396926f*InSamples[0][base+i] +
+                         0.1855740f*InSamples[1][base+i];
+        allpass_process(&enc->Filter1_WX[0], temp[1], temp[0],
+                        Filter1Coeff[0]*Filter1Coeff[0], todo);
+        allpass_process(&enc->Filter1_WX[1], temp[0], temp[1],
+                        Filter1Coeff[1]*Filter1Coeff[1], todo);
+        allpass_process(&enc->Filter1_WX[2], temp[1], temp[0],
+                        Filter1Coeff[2]*Filter1Coeff[2], todo);
+        S[0] = enc->Filter1_WX[3].y[0];
+        allpass_process(&enc->Filter1_WX[3], temp[0], temp[1],
+                        Filter1Coeff[3]*Filter1Coeff[3], todo);
+        for(i = 1;i < todo;i++)
+            S[i] = temp[0][i-1];
+
+        /* Left = (S + D)/2.0 */
+        for(i = 0;i < todo;i++)
+            *(LeftOut++) += (S[i] + D[i]) * 0.5f;
+        /* Right = (S - D)/2.0 */
+        for(i = 0;i < todo;i++)
+            *(RightOut++) += (S[i] - D[i]) * 0.5f;
+
+        base += todo;
+    }
+}

+ 48 - 0
Engine/lib/openal-soft/Alc/uhjfilter.h

@@ -0,0 +1,48 @@
+#ifndef UHJFILTER_H
+#define UHJFILTER_H
+
+#include "AL/al.h"
+
+#include "alMain.h"
+
+typedef struct AllPassState {
+    ALfloat x[2]; /* Last two input samples */
+    ALfloat y[2]; /* Last two output samples */
+} AllPassState;
+
+/* Encoding 2-channel UHJ from B-Format is done as:
+ *
+ * S = 0.9396926*W + 0.1855740*X
+ * D = j(-0.3420201*W + 0.5098604*X) + 0.6554516*Y
+ *
+ * Left = (S + D)/2.0
+ * Right = (S - D)/2.0
+ *
+ * where j is a wide-band +90 degree phase shift.
+ *
+ * The phase shift is done using a Hilbert transform, described here:
+ * https://web.archive.org/web/20060708031958/http://www.biochem.oulu.fi/~oniemita/dsp/hilbert/
+ * It works using 2 sets of 4 chained filters. The first filter chain produces
+ * a phase shift of varying magnitude over a wide range of frequencies, while
+ * the second filter chain produces a phase shift 90 degrees ahead of the
+ * first over the same range.
+ *
+ * Combining these two stages requires the use of three filter chains. S-
+ * channel output uses a Filter1 chain on the W and X channel mix, while the D-
+ * channel output uses a Filter1 chain on the Y channel plus a Filter2 chain on
+ * the W and X channel mix. This results in the W and X input mix on the D-
+ * channel output having the required +90 degree phase shift relative to the
+ * other inputs.
+ */
+
+typedef struct Uhj2Encoder {
+    AllPassState Filter1_WX[4];
+    AllPassState Filter1_Y[4];
+    AllPassState Filter2_WX[4];
+} Uhj2Encoder;
+
+/* Encodes a 2-channel UHJ (stereo-compatible) signal from a B-Format input
+ * signal. */
+void EncodeUhj2(Uhj2Encoder *enc, ALfloat *restrict LeftOut, ALfloat *restrict RightOut, ALfloat (*restrict InSamples)[BUFFERSIZE], ALuint SamplesToDo);
+
+#endif /* UHJFILTER_H */

+ 110 - 0
Engine/lib/openal-soft/Alc/vector.h

@@ -0,0 +1,110 @@
+#ifndef AL_VECTOR_H
+#define AL_VECTOR_H
+
+#include <stdlib.h>
+
+#include <AL/al.h>
+
+#include "almalloc.h"
+
+
+#define TYPEDEF_VECTOR(T, N) typedef struct {                                 \
+    size_t Capacity;                                                          \
+    size_t Size;                                                              \
+    T Data[];                                                                 \
+} _##N;                                                                       \
+typedef _##N* N;                                                              \
+typedef const _##N* const_##N;
+
+#define VECTOR(T) struct {                                                    \
+    size_t Capacity;                                                          \
+    size_t Size;                                                              \
+    T Data[];                                                                 \
+}*
+
+#define VECTOR_INIT(_x)       do { (_x) = NULL; } while(0)
+#define VECTOR_INIT_STATIC()  NULL
+#define VECTOR_DEINIT(_x)     do { al_free((_x)); (_x) = NULL; } while(0)
+
+#define VECTOR_RESIZE(_x, _s, _c) do {                                        \
+    size_t _size = (_s);                                                      \
+    size_t _cap = (_c);                                                       \
+    if(_size > _cap)                                                          \
+        _cap = _size;                                                         \
+                                                                              \
+    if(!(_x) && _cap == 0)                                                    \
+        break;                                                                \
+                                                                              \
+    if(((_x) ? (_x)->Capacity : 0) < _cap)                                    \
+    {                                                                         \
+        size_t old_size = ((_x) ? (_x)->Size : 0);                            \
+        void *temp;                                                           \
+                                                                              \
+        temp = al_calloc(16, sizeof(*(_x)) + sizeof((_x)->Data[0])*_cap);     \
+        assert(temp != NULL);                                                 \
+        if((_x))                                                              \
+            memcpy(((ALubyte*)temp)+sizeof(*(_x)), (_x)->Data,                \
+                   sizeof((_x)->Data[0])*old_size);                           \
+                                                                              \
+        al_free((_x));                                                        \
+        (_x) = temp;                                                          \
+        (_x)->Capacity = _cap;                                                \
+    }                                                                         \
+    (_x)->Size = _size;                                                       \
+} while(0)                                                                    \
+
+#define VECTOR_CAPACITY(_x) ((_x) ? (_x)->Capacity : 0)
+#define VECTOR_SIZE(_x)     ((_x) ? (_x)->Size : 0)
+
+#define VECTOR_BEGIN(_x) ((_x) ? (_x)->Data + 0 : NULL)
+#define VECTOR_END(_x)   ((_x) ? (_x)->Data + (_x)->Size : NULL)
+
+#define VECTOR_PUSH_BACK(_x, _obj) do {      \
+    size_t _pbsize = VECTOR_SIZE(_x)+1;      \
+    VECTOR_RESIZE(_x, _pbsize, _pbsize);     \
+    (_x)->Data[(_x)->Size-1] = (_obj);       \
+} while(0)
+#define VECTOR_POP_BACK(_x) ((void)((_x)->Size--))
+
+#define VECTOR_BACK(_x)  ((_x)->Data[(_x)->Size-1])
+#define VECTOR_FRONT(_x) ((_x)->Data[0])
+
+#define VECTOR_ELEM(_x, _o) ((_x)->Data[(_o)])
+
+#define VECTOR_FOR_EACH(_t, _x, _f)  do {                                     \
+    _t *_iter = VECTOR_BEGIN((_x));                                           \
+    _t *_end = VECTOR_END((_x));                                              \
+    for(;_iter != _end;++_iter)                                               \
+        _f(_iter);                                                            \
+} while(0)
+
+#define VECTOR_FOR_EACH_PARAMS(_t, _x, _f, ...)  do {                         \
+    _t *_iter = VECTOR_BEGIN((_x));                                           \
+    _t *_end = VECTOR_END((_x));                                              \
+    for(;_iter != _end;++_iter)                                               \
+        _f(__VA_ARGS__, _iter);                                               \
+} while(0)
+
+#define VECTOR_FIND_IF(_i, _t, _x, _f)  do {                                  \
+    _t *_iter = VECTOR_BEGIN((_x));                                           \
+    _t *_end = VECTOR_END((_x));                                              \
+    for(;_iter != _end;++_iter)                                               \
+    {                                                                         \
+        if(_f(_iter))                                                         \
+            break;                                                            \
+    }                                                                         \
+    (_i) = _iter;                                                             \
+} while(0)
+
+#define VECTOR_FIND_IF_PARMS(_i, _t, _x, _f, ...)  do {                       \
+    _t *_iter = VECTOR_BEGIN((_x));                                           \
+    _t *_end = VECTOR_END((_x));                                              \
+    for(;_iter != _end;++_iter)                                               \
+    {                                                                         \
+        if(_f(__VA_ARGS__, _iter))                                            \
+            break;                                                            \
+    }                                                                         \
+    (_i) = _iter;                                                             \
+} while(0)
+
+#endif /* AL_VECTOR_H */

+ 1468 - 0
Engine/lib/openal-soft/CMakeLists.txt

@@ -0,0 +1,1468 @@
+# CMake build file list for OpenAL
+
+CMAKE_MINIMUM_REQUIRED(VERSION 2.8.5)
+
+PROJECT(OpenAL)
+
+IF(COMMAND CMAKE_POLICY)
+    CMAKE_POLICY(SET CMP0003 NEW)
+    CMAKE_POLICY(SET CMP0005 NEW)
+    IF(POLICY CMP0042)
+        CMAKE_POLICY(SET CMP0042 NEW)
+    ENDIF(POLICY CMP0042)
+ENDIF(COMMAND CMAKE_POLICY)
+
+SET(CMAKE_MODULE_PATH "${OpenAL_SOURCE_DIR}/cmake")
+
+INCLUDE(CheckFunctionExists)
+INCLUDE(CheckLibraryExists)
+INCLUDE(CheckSharedFunctionExists)
+INCLUDE(CheckIncludeFile)
+INCLUDE(CheckIncludeFiles)
+INCLUDE(CheckSymbolExists)
+INCLUDE(CheckCCompilerFlag)
+INCLUDE(CheckCSourceCompiles)
+INCLUDE(CheckTypeSize)
+include(CheckStructHasMember)
+include(CheckFileOffsetBits)
+include(GNUInstallDirs)
+
+
+SET(CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS TRUE)
+
+
+OPTION(ALSOFT_DLOPEN  "Check for the dlopen API for loading optional libs"  ON)
+
+OPTION(ALSOFT_WERROR  "Treat compile warnings as errors"      OFF)
+
+OPTION(ALSOFT_UTILS          "Build and install utility programs"         ON)
+OPTION(ALSOFT_NO_CONFIG_UTIL "Disable building the alsoft-config utility" OFF)
+
+OPTION(ALSOFT_EXAMPLES  "Build and install example programs"  ON)
+OPTION(ALSOFT_TESTS     "Build and install test programs"     ON)
+
+OPTION(ALSOFT_CONFIG "Install alsoft.conf sample configuration file" ON)
+OPTION(ALSOFT_HRTF_DEFS "Install HRTF definition files" ON)
+OPTION(ALSOFT_AMBDEC_PRESETS "Install AmbDec preset files" ON)
+OPTION(ALSOFT_INSTALL "Install headers and libraries" ON)
+
+if(DEFINED SHARE_INSTALL_DIR)
+    message(WARNING "SHARE_INSTALL_DIR is deprecated.  Use the variables provided by the GNUInstallDirs module instead")
+    set(CMAKE_INSTALL_DATADIR "${SHARE_INSTALL_DIR}")
+endif()
+
+if(DEFINED LIB_SUFFIX)
+    message(WARNING "LIB_SUFFIX is deprecated.  Use the variables provided by the GNUInstallDirs module instead")
+endif()
+
+
+IF(NOT WIN32)
+    SET(LIBNAME openal)
+ELSE()
+    SET(LIBNAME OpenAL32)
+    ADD_DEFINITIONS("-D_WIN32 -D_WIN32_WINNT=0x0502")
+
+    # This option is mainly for static linking OpenAL Soft into another project
+    # that already defines the IDs. It is up to that project to ensure all
+    # required IDs are defined.
+    OPTION(ALSOFT_NO_UID_DEFS "Do not define GUIDs, IIDs, CLSIDs, or PropertyKeys" OFF)
+
+    IF(MINGW)
+        OPTION(ALSOFT_BUILD_IMPORT_LIB "Build an import .lib using dlltool (requires sed)" ON)
+        IF(NOT DLLTOOL)
+            IF(HOST)
+                SET(DLLTOOL "${HOST}-dlltool")
+            ELSE()
+                SET(DLLTOOL "dlltool")
+            ENDIF()
+        ENDIF()
+    ENDIF()
+ENDIF()
+
+
+# QNX's gcc do not uses /usr/include and /usr/lib pathes by default
+IF ("${CMAKE_C_PLATFORM_ID}" STREQUAL "QNX")
+    ADD_DEFINITIONS("-I/usr/include")
+    SET(EXTRA_LIBS ${EXTRA_LIBS} -L/usr/lib)
+ENDIF()
+
+IF(NOT LIBTYPE)
+    SET(LIBTYPE SHARED)
+ENDIF()
+
+SET(LIB_MAJOR_VERSION "1")
+SET(LIB_MINOR_VERSION "17")
+SET(LIB_REVISION "2")
+SET(LIB_VERSION "${LIB_MAJOR_VERSION}.${LIB_MINOR_VERSION}.${LIB_REVISION}")
+
+SET(EXPORT_DECL "")
+SET(ALIGN_DECL "")
+
+
+CHECK_TYPE_SIZE("long" SIZEOF_LONG)
+CHECK_TYPE_SIZE("long long" SIZEOF_LONG_LONG)
+
+
+CHECK_C_COMPILER_FLAG(-std=c11 HAVE_STD_C11)
+IF(HAVE_STD_C11)
+    SET(CMAKE_C_FLAGS "-std=c11 ${CMAKE_C_FLAGS}")
+ELSE()
+    CHECK_C_COMPILER_FLAG(-std=c99 HAVE_STD_C99)
+    IF(HAVE_STD_C99)
+        SET(CMAKE_C_FLAGS "-std=c99 ${CMAKE_C_FLAGS}")
+    ENDIF()
+ENDIF()
+
+if(NOT WIN32)
+    # Check if _POSIX_C_SOURCE and _XOPEN_SOURCE needs to be set for POSIX functions
+    CHECK_SYMBOL_EXISTS(posix_memalign stdlib.h HAVE_POSIX_MEMALIGN_DEFAULT)
+    IF(NOT HAVE_POSIX_MEMALIGN_DEFAULT)
+        SET(OLD_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS})
+        SET(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -D_POSIX_C_SOURCE=200112L -D_XOPEN_SOURCE=500")
+        CHECK_SYMBOL_EXISTS(posix_memalign stdlib.h HAVE_POSIX_MEMALIGN_POSIX)
+        IF(NOT HAVE_POSIX_MEMALIGN_POSIX)
+            SET(CMAKE_REQUIRED_FLAGS ${OLD_REQUIRED_FLAGS})
+        ELSE()
+            ADD_DEFINITIONS(-D_POSIX_C_SOURCE=200112L -D_XOPEN_SOURCE=500)
+        ENDIF()
+    ENDIF()
+    UNSET(OLD_REQUIRED_FLAGS)
+ENDIF()
+
+# Set defines for large file support
+CHECK_FILE_OFFSET_BITS()
+IF(_FILE_OFFSET_BITS)
+    ADD_DEFINITIONS(-D_FILE_OFFSET_BITS=${_FILE_OFFSET_BITS})
+    SET(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -D_FILE_OFFSET_BITS=${_FILE_OFFSET_BITS}")
+ENDIF()
+ADD_DEFINITIONS(-D_LARGEFILE_SOURCE -D_LARGE_FILES)
+SET(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -D_LARGEFILE_SOURCE -D_LARGE_FILES")
+
+# MSVC may need workarounds for C99 restrict and inline
+IF(MSVC)
+    # TODO: Once we truly require C99, these restrict and inline checks should go
+    # away.
+    CHECK_C_SOURCE_COMPILES("int *restrict foo;
+                             int main() {return 0;}" HAVE_RESTRICT)
+    IF(NOT HAVE_RESTRICT)
+        ADD_DEFINITIONS("-Drestrict=")
+        SET(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -Drestrict=")
+    ENDIF()
+
+    CHECK_C_SOURCE_COMPILES("inline void foo(void) { }
+                             int main() {return 0;}" HAVE_INLINE)
+    IF(NOT HAVE_INLINE)
+        CHECK_C_SOURCE_COMPILES("__inline void foo(void) { }
+                                 int main() {return 0;}" HAVE___INLINE)
+        IF(NOT HAVE___INLINE)
+            MESSAGE(FATAL_ERROR "No inline keyword found, please report!")
+        ENDIF()
+
+        ADD_DEFINITIONS(-Dinline=__inline)
+        SET(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -Dinline=__inline")
+    ENDIF()
+ENDIF()
+
+# Make sure we have C99-style inline semantics with GCC (4.3 or newer).
+IF(CMAKE_COMPILER_IS_GNUCC)
+    SET(OLD_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}")
+    # Force no inlining for the next test.
+    SET(CMAKE_REQUIRED_FLAGS "${OLD_REQUIRED_FLAGS} -fno-inline")
+
+    CHECK_C_SOURCE_COMPILES("extern inline int foo() { return 0; }
+                             int main() {return foo();}" INLINE_IS_C99)
+    IF(NOT INLINE_IS_C99)
+        MESSAGE(FATAL_ERROR "Your compiler does not seem to have C99 inline semantics!
+                             Please update your compiler for better C99 compliance.")
+    ENDIF()
+
+    SET(CMAKE_REQUIRED_FLAGS "${OLD_REQUIRED_FLAGS}")
+ENDIF()
+
+# Check if we have a proper timespec declaration
+CHECK_STRUCT_HAS_MEMBER("struct timespec" tv_sec time.h HAVE_STRUCT_TIMESPEC)
+IF(HAVE_STRUCT_TIMESPEC)
+    # Define it here so we don't have to include config.h for it
+    ADD_DEFINITIONS("-DHAVE_STRUCT_TIMESPEC")
+ENDIF()
+
+# Some systems may need libatomic for C11 atomic functions to work
+SET(OLD_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES})
+SET(CMAKE_REQUIRED_LIBRARIES ${OLD_REQUIRED_LIBRARIES} atomic)
+CHECK_C_SOURCE_COMPILES("#include <stdatomic.h>
+int _Atomic foo = ATOMIC_VAR_INIT(0);
+int main()
+{
+    return atomic_fetch_add(&foo, 2);
+}"
+HAVE_LIBATOMIC)
+IF(NOT HAVE_LIBATOMIC)
+    SET(CMAKE_REQUIRED_LIBRARIES "${OLD_REQUIRED_LIBRARIES}")
+ELSE()
+    SET(EXTRA_LIBS atomic ${EXTRA_LIBS})
+ENDIF()
+UNSET(OLD_REQUIRED_LIBRARIES)
+
+# Check if we have C99 variable length arrays
+CHECK_C_SOURCE_COMPILES(
+"int main(int argc, char *argv[])
+ {
+     volatile int tmp[argc];
+     tmp[0] = argv[0][0];
+     return tmp[0];
+ }"
+HAVE_C99_VLA)
+
+# Check if we have C99 bool
+CHECK_C_SOURCE_COMPILES(
+"int main(int argc, char *argv[])
+ {
+     volatile _Bool ret;
+     ret = (argc > 1) ? 1 : 0;
+     return ret ? -1 : 0;
+ }"
+HAVE_C99_BOOL)
+
+# Check if we have C11 static_assert
+CHECK_C_SOURCE_COMPILES(
+"int main()
+ {
+     _Static_assert(sizeof(int) == sizeof(int), \"What\");
+     return 0;
+ }"
+HAVE_C11_STATIC_ASSERT)
+
+# Check if we have C11 alignas
+CHECK_C_SOURCE_COMPILES(
+"_Alignas(16) int foo;
+ int main()
+ {
+     return 0;
+ }"
+HAVE_C11_ALIGNAS)
+
+# Check if we have C11 _Atomic
+CHECK_C_SOURCE_COMPILES(
+"#include <stdatomic.h>
+ const int _Atomic foo = ATOMIC_VAR_INIT(~0);
+ int _Atomic bar = ATOMIC_VAR_INIT(0);
+ int main()
+ {
+     atomic_fetch_add(&bar, 2);
+     return atomic_load(&foo);
+ }"
+HAVE_C11_ATOMIC)
+
+# Add definitions, compiler switches, etc.
+INCLUDE_DIRECTORIES("${OpenAL_SOURCE_DIR}/include" "${OpenAL_BINARY_DIR}")
+IF(CMAKE_VERSION VERSION_LESS "2.8.8")
+    INCLUDE_DIRECTORIES("${OpenAL_SOURCE_DIR}/OpenAL32/Include" "${OpenAL_SOURCE_DIR}/Alc")
+    IF(WIN32 AND ALSOFT_NO_UID_DEFS)
+        ADD_DEFINITIONS("-DAL_NO_UID_DEFS")
+    ENDIF()
+ENDIF()
+
+IF(NOT CMAKE_BUILD_TYPE)
+    SET(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING
+        "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel."
+        FORCE)
+ENDIF()
+IF(NOT CMAKE_DEBUG_POSTFIX)
+    SET(CMAKE_DEBUG_POSTFIX "" CACHE STRING
+        "Library postfix for debug builds. Normally left blank."
+        FORCE)
+ENDIF()
+
+SET(EXTRA_CFLAGS "")
+IF(MSVC)
+    ADD_DEFINITIONS(-D_CRT_SECURE_NO_WARNINGS)
+    ADD_DEFINITIONS(-D_CRT_NONSTDC_NO_DEPRECATE)
+    SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} /wd4098")
+
+    IF(NOT DXSDK_DIR)
+        STRING(REGEX REPLACE "\\\\" "/" DXSDK_DIR "$ENV{DXSDK_DIR}")
+    ELSE()
+        STRING(REGEX REPLACE "\\\\" "/" DXSDK_DIR "${DXSDK_DIR}")
+    ENDIF()
+    IF(DXSDK_DIR)
+        MESSAGE(STATUS "Using DirectX SDK directory: ${DXSDK_DIR}")
+    ENDIF()
+
+    OPTION(FORCE_STATIC_VCRT "Force /MT for static VC runtimes" OFF)
+    IF(FORCE_STATIC_VCRT)
+        FOREACH(flag_var
+                CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE
+                CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO)
+            IF(${flag_var} MATCHES "/MD")
+                STRING(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}")
+            ENDIF()
+        ENDFOREACH(flag_var)
+    ENDIF()
+ELSE()
+    SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -Winline -Wall")
+    CHECK_C_COMPILER_FLAG(-Wextra HAVE_W_EXTRA)
+    IF(HAVE_W_EXTRA)
+        SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -Wextra")
+    ENDIF()
+
+    IF(ALSOFT_WERROR)
+        SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -Werror")
+    ENDIF()
+
+    # Force enable -fPIC for CMake versions before 2.8.9 (later versions have
+    # the POSITION_INDEPENDENT_CODE target property). The static common library
+    # will be linked into the dynamic openal library, which requires all its
+    # code to be position-independent.
+    IF(CMAKE_VERSION VERSION_LESS "2.8.9" AND NOT WIN32)
+        CHECK_C_COMPILER_FLAG(-fPIC HAVE_FPIC_SWITCH)
+        IF(HAVE_FPIC_SWITCH)
+            SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -fPIC")
+        ENDIF()
+    ENDIF()
+
+    # We want RelWithDebInfo to actually include debug stuff (define _DEBUG
+    # instead of NDEBUG)
+    FOREACH(flag_var  CMAKE_C_FLAGS_RELWITHDEBINFO CMAKE_CXX_FLAGS_RELWITHDEBINFO)
+        IF(${flag_var} MATCHES "-DNDEBUG")
+            STRING(REGEX REPLACE "-DNDEBUG" "-D_DEBUG" ${flag_var} "${${flag_var}}")
+        ENDIF()
+    ENDFOREACH()
+
+    CHECK_C_SOURCE_COMPILES("int foo() __attribute__((destructor));
+                             int main() {return 0;}" HAVE_GCC_DESTRUCTOR)
+
+    option(ALSOFT_STATIC_LIBGCC "Force -static-libgcc for static GCC runtimes" OFF)
+    if(ALSOFT_STATIC_LIBGCC)
+        set(OLD_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES})
+        set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} -static-libgcc)
+        check_c_source_compiles(
+"#include <stdlib.h>
+int main()
+{
+    return 0;
+}"
+            HAVE_STATIC_LIBGCC_SWITCH
+        )
+        if(HAVE_STATIC_LIBGCC_SWITCH)
+            set(EXTRA_LIBS ${EXTRA_LIBS} -static-libgcc)
+        endif()
+        set(CMAKE_REQUIRED_LIBRARIES ${OLD_REQUIRED_LIBRARIES})
+        unset(OLD_REQUIRED_LIBRARIES)
+    endif()
+ENDIF()
+
+# Set visibility/export options if available
+SET(HIDDEN_DECL "")
+IF(WIN32)
+    SET(EXPORT_DECL "__declspec(dllexport)")
+    IF(NOT MINGW)
+        SET(ALIGN_DECL "__declspec(align(x))")
+    ELSE()
+        SET(ALIGN_DECL "__declspec(aligned(x))")
+    ENDIF()
+ELSE()
+    SET(OLD_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}")
+    # Yes GCC, really don't accept visibility modes you don't support
+    SET(CMAKE_REQUIRED_FLAGS "${OLD_REQUIRED_FLAGS} -Wattributes -Werror")
+
+    CHECK_C_SOURCE_COMPILES("int foo() __attribute__((visibility(\"protected\")));
+                             int main() {return 0;}" HAVE_GCC_PROTECTED_VISIBILITY)
+    IF(HAVE_GCC_PROTECTED_VISIBILITY)
+        SET(EXPORT_DECL "__attribute__((visibility(\"protected\")))")
+    ELSE()
+        CHECK_C_SOURCE_COMPILES("int foo() __attribute__((visibility(\"default\")));
+                                 int main() {return 0;}" HAVE_GCC_DEFAULT_VISIBILITY)
+        IF(HAVE_GCC_DEFAULT_VISIBILITY)
+            SET(EXPORT_DECL "__attribute__((visibility(\"default\")))")
+        ENDIF()
+    ENDIF()
+
+    IF(HAVE_GCC_PROTECTED_VISIBILITY OR HAVE_GCC_DEFAULT_VISIBILITY)
+        CHECK_C_COMPILER_FLAG(-fvisibility=hidden HAVE_VISIBILITY_HIDDEN_SWITCH)
+        IF(HAVE_VISIBILITY_HIDDEN_SWITCH)
+            SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -fvisibility=hidden")
+            SET(HIDDEN_DECL "__attribute__((visibility(\"hidden\")))")
+        ENDIF()
+    ENDIF()
+
+    CHECK_C_SOURCE_COMPILES("int foo __attribute__((aligned(16)));
+                             int main() {return 0;}" HAVE_ATTRIBUTE_ALIGNED)
+    IF(HAVE_ATTRIBUTE_ALIGNED)
+        SET(ALIGN_DECL "__attribute__((aligned(x)))")
+    ENDIF()
+
+    SET(CMAKE_REQUIRED_FLAGS "${OLD_REQUIRED_FLAGS}")
+ENDIF()
+
+SET(SSE_SWITCH "")
+SET(SSE2_SWITCH "")
+SET(SSE3_SWITCH "")
+SET(SSE4_1_SWITCH "")
+SET(FPU_NEON_SWITCH "")
+IF(NOT MSVC)
+    CHECK_C_COMPILER_FLAG(-msse HAVE_MSSE_SWITCH)
+    IF(HAVE_MSSE_SWITCH)
+        SET(SSE_SWITCH "-msse")
+    ENDIF()
+    CHECK_C_COMPILER_FLAG(-msse2 HAVE_MSSE2_SWITCH)
+    IF(HAVE_MSSE2_SWITCH)
+        SET(SSE2_SWITCH "-msse2")
+    ENDIF()
+    CHECK_C_COMPILER_FLAG(-msse3 HAVE_MSSE3_SWITCH)
+    IF(HAVE_MSSE3_SWITCH)
+        SET(SSE3_SWITCH "-msse3")
+    ENDIF()
+    CHECK_C_COMPILER_FLAG(-msse4.1 HAVE_MSSE4_1_SWITCH)
+    IF(HAVE_MSSE4_1_SWITCH)
+        SET(SSE4_1_SWITCH "-msse4.1")
+    ENDIF()
+    CHECK_C_COMPILER_FLAG(-mfpu=neon HAVE_MFPU_NEON_SWITCH)
+    IF(HAVE_MFPU_NEON_SWITCH)
+        SET(FPU_NEON_SWITCH "-mfpu=neon")
+    ENDIF()
+ENDIF()
+
+CHECK_C_SOURCE_COMPILES("int foo(const char *str, ...) __attribute__((format(printf, 1, 2)));
+                         int main() {return 0;}" HAVE_GCC_FORMAT)
+
+CHECK_INCLUDE_FILE(stdbool.h HAVE_STDBOOL_H)
+CHECK_INCLUDE_FILE(stdalign.h HAVE_STDALIGN_H)
+CHECK_INCLUDE_FILE(malloc.h HAVE_MALLOC_H)
+CHECK_INCLUDE_FILE(dirent.h HAVE_DIRENT_H)
+CHECK_INCLUDE_FILE(strings.h HAVE_STRINGS_H)
+CHECK_INCLUDE_FILE(cpuid.h HAVE_CPUID_H)
+CHECK_INCLUDE_FILE(intrin.h HAVE_INTRIN_H)
+CHECK_INCLUDE_FILE(sys/sysconf.h HAVE_SYS_SYSCONF_H)
+CHECK_INCLUDE_FILE(fenv.h HAVE_FENV_H)
+CHECK_INCLUDE_FILE(float.h HAVE_FLOAT_H)
+CHECK_INCLUDE_FILE(ieeefp.h HAVE_IEEEFP_H)
+CHECK_INCLUDE_FILE(guiddef.h HAVE_GUIDDEF_H)
+IF(NOT HAVE_GUIDDEF_H)
+    CHECK_INCLUDE_FILE(initguid.h HAVE_INITGUID_H)
+ENDIF()
+
+# Some systems need libm for some of the following math functions to work
+CHECK_LIBRARY_EXISTS(m pow "" HAVE_LIBM)
+IF(HAVE_LIBM)
+    SET(EXTRA_LIBS m ${EXTRA_LIBS})
+    SET(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} m)
+ENDIF()
+
+# Check for the dlopen API (for dynamicly loading backend libs)
+IF(ALSOFT_DLOPEN)
+    CHECK_LIBRARY_EXISTS(dl dlopen "" HAVE_LIBDL)
+    IF(HAVE_LIBDL)
+        SET(EXTRA_LIBS dl ${EXTRA_LIBS})
+        SET(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} dl)
+    ENDIF()
+
+    CHECK_INCLUDE_FILE(dlfcn.h HAVE_DLFCN_H)
+ENDIF()
+
+# Check for a cpuid intrinsic
+IF(HAVE_CPUID_H)
+    CHECK_C_SOURCE_COMPILES("#include <cpuid.h>
+        int main()
+        {
+            unsigned int eax, ebx, ecx, edx;
+            return __get_cpuid(0, &eax, &ebx, &ecx, &edx);
+        }" HAVE_GCC_GET_CPUID)
+ENDIF()
+IF(HAVE_INTRIN_H)
+    CHECK_C_SOURCE_COMPILES("#include <intrin.h>
+        int main()
+        {
+            int regs[4];
+            __cpuid(regs, 0);
+            return regs[0];
+        }" HAVE_CPUID_INTRINSIC)
+ENDIF()
+
+CHECK_SYMBOL_EXISTS(aligned_alloc    stdlib.h HAVE_ALIGNED_ALLOC)
+CHECK_SYMBOL_EXISTS(posix_memalign   stdlib.h HAVE_POSIX_MEMALIGN)
+CHECK_SYMBOL_EXISTS(_aligned_malloc  malloc.h HAVE__ALIGNED_MALLOC)
+CHECK_SYMBOL_EXISTS(lrintf math.h HAVE_LRINTF)
+CHECK_SYMBOL_EXISTS(modff  math.h HAVE_MODFF)
+IF(NOT HAVE_C99_VLA)
+    CHECK_SYMBOL_EXISTS(alloca malloc.h HAVE_ALLOCA)
+    IF(NOT HAVE_ALLOCA)
+        MESSAGE(FATAL_ERROR "No alloca function found, please report!")
+    ENDIF()
+ENDIF()
+
+IF(HAVE_FLOAT_H)
+    CHECK_SYMBOL_EXISTS(_controlfp float.h HAVE__CONTROLFP)
+    CHECK_SYMBOL_EXISTS(__control87_2 float.h HAVE___CONTROL87_2)
+ENDIF()
+
+CHECK_FUNCTION_EXISTS(stat HAVE_STAT)
+CHECK_FUNCTION_EXISTS(strtof HAVE_STRTOF)
+CHECK_FUNCTION_EXISTS(strcasecmp HAVE_STRCASECMP)
+IF(NOT HAVE_STRCASECMP)
+    CHECK_FUNCTION_EXISTS(_stricmp HAVE__STRICMP)
+    IF(NOT HAVE__STRICMP)
+        MESSAGE(FATAL_ERROR "No case-insensitive compare function found, please report!")
+    ENDIF()
+
+    ADD_DEFINITIONS(-Dstrcasecmp=_stricmp)
+ENDIF()
+
+CHECK_FUNCTION_EXISTS(strncasecmp HAVE_STRNCASECMP)
+IF(NOT HAVE_STRNCASECMP)
+    CHECK_FUNCTION_EXISTS(_strnicmp HAVE__STRNICMP)
+    IF(NOT HAVE__STRNICMP)
+        MESSAGE(FATAL_ERROR "No case-insensitive size-limitted compare function found, please report!")
+    ENDIF()
+
+    ADD_DEFINITIONS(-Dstrncasecmp=_strnicmp)
+ENDIF()
+
+CHECK_SYMBOL_EXISTS(strnlen string.h HAVE_STRNLEN)
+CHECK_SYMBOL_EXISTS(snprintf stdio.h HAVE_SNPRINTF)
+IF(NOT HAVE_SNPRINTF)
+    CHECK_FUNCTION_EXISTS(_snprintf HAVE__SNPRINTF)
+    IF(NOT HAVE__SNPRINTF)
+        MESSAGE(FATAL_ERROR "No snprintf function found, please report!")
+    ENDIF()
+
+    ADD_DEFINITIONS(-Dsnprintf=_snprintf)
+ENDIF()
+
+CHECK_SYMBOL_EXISTS(isfinite math.h HAVE_ISFINITE)
+IF(NOT HAVE_ISFINITE)
+    CHECK_FUNCTION_EXISTS(finite HAVE_FINITE)
+    IF(NOT HAVE_FINITE)
+        CHECK_FUNCTION_EXISTS(_finite HAVE__FINITE)
+        IF(NOT HAVE__FINITE)
+            MESSAGE(FATAL_ERROR "No isfinite function found, please report!")
+        ENDIF()
+        ADD_DEFINITIONS(-Disfinite=_finite)
+    ELSE()
+        ADD_DEFINITIONS(-Disfinite=finite)
+    ENDIF()
+ENDIF()
+
+CHECK_SYMBOL_EXISTS(isnan math.h HAVE_ISNAN)
+IF(NOT HAVE_ISNAN)
+    CHECK_FUNCTION_EXISTS(_isnan HAVE__ISNAN)
+    IF(NOT HAVE__ISNAN)
+        MESSAGE(FATAL_ERROR "No isnan function found, please report!")
+    ENDIF()
+
+    ADD_DEFINITIONS(-Disnan=_isnan)
+ENDIF()
+
+
+# Check if we have Windows headers
+CHECK_INCLUDE_FILE(windows.h HAVE_WINDOWS_H -D_WIN32_WINNT=0x0502)
+IF(NOT HAVE_WINDOWS_H)
+    CHECK_SYMBOL_EXISTS(gettimeofday sys/time.h HAVE_GETTIMEOFDAY)
+    IF(NOT HAVE_GETTIMEOFDAY)
+        MESSAGE(FATAL_ERROR "No timing function found!")
+    ENDIF()
+
+    CHECK_SYMBOL_EXISTS(nanosleep time.h HAVE_NANOSLEEP)
+    IF(NOT HAVE_NANOSLEEP)
+        MESSAGE(FATAL_ERROR "No sleep function found!")
+    ENDIF()
+
+    # We need pthreads outside of Windows
+    CHECK_INCLUDE_FILE(pthread.h HAVE_PTHREAD_H)
+    IF(NOT HAVE_PTHREAD_H)
+        MESSAGE(FATAL_ERROR "PThreads is required for non-Windows builds!")
+    ENDIF()
+    # Some systems need pthread_np.h to get recursive mutexes
+    CHECK_INCLUDE_FILES("pthread.h;pthread_np.h" HAVE_PTHREAD_NP_H)
+
+    CHECK_C_COMPILER_FLAG(-pthread HAVE_PTHREAD)
+    IF(HAVE_PTHREAD)
+        SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -pthread")
+        SET(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -pthread")
+        SET(EXTRA_LIBS ${EXTRA_LIBS} -pthread)
+    ENDIF()
+
+    CHECK_LIBRARY_EXISTS(pthread pthread_create "" HAVE_LIBPTHREAD)
+    IF(HAVE_LIBPTHREAD)
+        SET(EXTRA_LIBS pthread ${EXTRA_LIBS})
+    ENDIF()
+
+    CHECK_SYMBOL_EXISTS(pthread_setschedparam pthread.h HAVE_PTHREAD_SETSCHEDPARAM)
+
+    IF(HAVE_PTHREAD_NP_H)
+        CHECK_SYMBOL_EXISTS(pthread_setname_np "pthread.h;pthread_np.h" HAVE_PTHREAD_SETNAME_NP)
+        IF(NOT HAVE_PTHREAD_SETNAME_NP)
+            CHECK_SYMBOL_EXISTS(pthread_set_name_np "pthread.h;pthread_np.h" HAVE_PTHREAD_SET_NAME_NP)
+        ELSE()
+            CHECK_C_SOURCE_COMPILES("
+#include <pthread.h>
+#include <pthread_np.h>
+int main()
+{
+    pthread_setname_np(\"testname\");
+    return 0;
+}"
+                PTHREAD_SETNAME_NP_ONE_PARAM
+            )
+        ENDIF()
+        CHECK_SYMBOL_EXISTS(pthread_mutexattr_setkind_np "pthread.h;pthread_np.h" HAVE_PTHREAD_MUTEXATTR_SETKIND_NP)
+    ELSE()
+        CHECK_SYMBOL_EXISTS(pthread_setname_np pthread.h HAVE_PTHREAD_SETNAME_NP)
+        IF(NOT HAVE_PTHREAD_SETNAME_NP)
+            CHECK_SYMBOL_EXISTS(pthread_set_name_np pthread.h HAVE_PTHREAD_SET_NAME_NP)
+        ELSE()
+            CHECK_C_SOURCE_COMPILES("
+#include <pthread.h>
+int main()
+{
+    pthread_setname_np(\"testname\");
+    return 0;
+}"
+                PTHREAD_SETNAME_NP_ONE_PARAM
+            )
+        ENDIF()
+        CHECK_SYMBOL_EXISTS(pthread_mutexattr_setkind_np pthread.h HAVE_PTHREAD_MUTEXATTR_SETKIND_NP)
+    ENDIF()
+
+    CHECK_SYMBOL_EXISTS(pthread_mutex_timedlock pthread.h HAVE_PTHREAD_MUTEX_TIMEDLOCK)
+
+    CHECK_LIBRARY_EXISTS(rt clock_gettime "" HAVE_LIBRT)
+    IF(HAVE_LIBRT)
+        SET(EXTRA_LIBS rt ${EXTRA_LIBS})
+    ENDIF()
+ENDIF()
+
+# Check for a 64-bit type
+CHECK_INCLUDE_FILE(stdint.h HAVE_STDINT_H)
+IF(NOT HAVE_STDINT_H)
+    IF(HAVE_WINDOWS_H)
+        CHECK_C_SOURCE_COMPILES("#define _WIN32_WINNT 0x0502
+                                 #include <windows.h>
+                                 __int64 foo;
+                                 int main() {return 0;}" HAVE___INT64)
+    ENDIF()
+    IF(NOT HAVE___INT64)
+        IF(NOT SIZEOF_LONG MATCHES "8")
+            IF(NOT SIZEOF_LONG_LONG MATCHES "8")
+                MESSAGE(FATAL_ERROR "No 64-bit types found, please report!")
+            ENDIF()
+        ENDIF()
+    ENDIF()
+ENDIF()
+
+
+SET(COMMON_OBJS  common/almalloc.c
+                 common/atomic.c
+                 common/rwlock.c
+                 common/threads.c
+                 common/uintmap.c
+)
+SET(OPENAL_OBJS  OpenAL32/alAuxEffectSlot.c
+                 OpenAL32/alBuffer.c
+                 OpenAL32/alEffect.c
+                 OpenAL32/alError.c
+                 OpenAL32/alExtension.c
+                 OpenAL32/alFilter.c
+                 OpenAL32/alListener.c
+                 OpenAL32/alSource.c
+                 OpenAL32/alState.c
+                 OpenAL32/alThunk.c
+                 OpenAL32/sample_cvt.c
+)
+SET(ALC_OBJS  Alc/ALc.c
+              Alc/ALu.c
+              Alc/alcConfig.c
+              Alc/alcRing.c
+              Alc/bs2b.c
+              Alc/effects/chorus.c
+              Alc/effects/compressor.c
+              Alc/effects/dedicated.c
+              Alc/effects/distortion.c
+              Alc/effects/echo.c
+              Alc/effects/equalizer.c
+              Alc/effects/flanger.c
+              Alc/effects/modulator.c
+              Alc/effects/null.c
+              Alc/effects/reverb.c
+              Alc/helpers.c
+              Alc/bsinc.c
+              Alc/hrtf.c
+              Alc/uhjfilter.c
+              Alc/ambdec.c
+              Alc/bformatdec.c
+              Alc/panning.c
+              Alc/mixer.c
+              Alc/mixer_c.c
+)
+
+
+SET(CPU_EXTS "Default")
+SET(HAVE_SSE        0)
+SET(HAVE_SSE2       0)
+SET(HAVE_SSE3       0)
+SET(HAVE_SSE4_1     0)
+SET(HAVE_NEON       0)
+
+SET(HAVE_ALSA       0)
+SET(HAVE_OSS        0)
+SET(HAVE_SOLARIS    0)
+SET(HAVE_SNDIO      0)
+SET(HAVE_QSA        0)
+SET(HAVE_DSOUND     0)
+SET(HAVE_MMDEVAPI   0)
+SET(HAVE_WINMM      0)
+SET(HAVE_PORTAUDIO  0)
+SET(HAVE_PULSEAUDIO 0)
+SET(HAVE_COREAUDIO  0)
+SET(HAVE_OPENSL     0)
+SET(HAVE_WAVE       0)
+
+# Check for SSE support
+OPTION(ALSOFT_REQUIRE_SSE "Require SSE support" OFF)
+CHECK_INCLUDE_FILE(xmmintrin.h HAVE_XMMINTRIN_H "${SSE_SWITCH}")
+IF(HAVE_XMMINTRIN_H)
+    OPTION(ALSOFT_CPUEXT_SSE "Enable SSE support" ON)
+    IF(ALSOFT_CPUEXT_SSE)
+        IF(ALIGN_DECL OR HAVE_C11_ALIGNAS)
+            SET(HAVE_SSE 1)
+            SET(ALC_OBJS  ${ALC_OBJS} Alc/mixer_sse.c)
+            IF(SSE_SWITCH)
+                SET_SOURCE_FILES_PROPERTIES(Alc/mixer_sse.c PROPERTIES
+                                            COMPILE_FLAGS "${SSE_SWITCH}")
+            ENDIF()
+            SET(CPU_EXTS "${CPU_EXTS}, SSE")
+        ENDIF()
+    ENDIF()
+ENDIF()
+IF(ALSOFT_REQUIRE_SSE AND NOT HAVE_SSE)
+    MESSAGE(FATAL_ERROR "Failed to enabled required SSE CPU extensions")
+ENDIF()
+
+OPTION(ALSOFT_REQUIRE_SSE2 "Require SSE2 support" OFF)
+CHECK_INCLUDE_FILE(emmintrin.h HAVE_EMMINTRIN_H "${SSE2_SWITCH}")
+IF(HAVE_EMMINTRIN_H)
+    OPTION(ALSOFT_CPUEXT_SSE2 "Enable SSE2 support" ON)
+    IF(HAVE_SSE AND ALSOFT_CPUEXT_SSE2)
+        IF(ALIGN_DECL OR HAVE_C11_ALIGNAS)
+            SET(HAVE_SSE2 1)
+            SET(ALC_OBJS  ${ALC_OBJS} Alc/mixer_sse2.c)
+            IF(SSE2_SWITCH)
+                SET_SOURCE_FILES_PROPERTIES(Alc/mixer_sse2.c PROPERTIES
+                                            COMPILE_FLAGS "${SSE2_SWITCH}")
+            ENDIF()
+            SET(CPU_EXTS "${CPU_EXTS}, SSE2")
+        ENDIF()
+    ENDIF()
+ENDIF()
+IF(ALSOFT_REQUIRE_SSE2 AND NOT HAVE_SSE2)
+    MESSAGE(FATAL_ERROR "Failed to enable required SSE2 CPU extensions")
+ENDIF()
+
+OPTION(ALSOFT_REQUIRE_SSE2 "Require SSE3 support" OFF)
+CHECK_INCLUDE_FILE(pmmintrin.h HAVE_PMMINTRIN_H "${SSE3_SWITCH}")
+IF(HAVE_EMMINTRIN_H)
+    OPTION(ALSOFT_CPUEXT_SSE3 "Enable SSE3 support" ON)
+    IF(HAVE_SSE2 AND ALSOFT_CPUEXT_SSE3)
+        IF(ALIGN_DECL OR HAVE_C11_ALIGNAS)
+            SET(HAVE_SSE3 1)
+            SET(ALC_OBJS  ${ALC_OBJS} Alc/mixer_sse3.c)
+            IF(SSE2_SWITCH)
+                SET_SOURCE_FILES_PROPERTIES(Alc/mixer_sse3.c PROPERTIES
+                                            COMPILE_FLAGS "${SSE3_SWITCH}")
+            ENDIF()
+            SET(CPU_EXTS "${CPU_EXTS}, SSE3")
+        ENDIF()
+    ENDIF()
+ENDIF()
+IF(ALSOFT_REQUIRE_SSE3 AND NOT HAVE_SSE3)
+    MESSAGE(FATAL_ERROR "Failed to enable required SSE3 CPU extensions")
+ENDIF()
+
+OPTION(ALSOFT_REQUIRE_SSE4_1 "Require SSE4.1 support" OFF)
+CHECK_INCLUDE_FILE(smmintrin.h HAVE_SMMINTRIN_H "${SSE4_1_SWITCH}")
+IF(HAVE_SMMINTRIN_H)
+    OPTION(ALSOFT_CPUEXT_SSE4_1 "Enable SSE4.1 support" ON)
+    IF(HAVE_SSE2 AND ALSOFT_CPUEXT_SSE4_1)
+        IF(ALIGN_DECL OR HAVE_C11_ALIGNAS)
+            SET(HAVE_SSE4_1 1)
+            SET(ALC_OBJS  ${ALC_OBJS} Alc/mixer_sse41.c)
+            IF(SSE4_1_SWITCH)
+                SET_SOURCE_FILES_PROPERTIES(Alc/mixer_sse41.c PROPERTIES
+                                            COMPILE_FLAGS "${SSE4_1_SWITCH}")
+            ENDIF()
+            SET(CPU_EXTS "${CPU_EXTS}, SSE4.1")
+        ENDIF()
+    ENDIF()
+ENDIF()
+IF(ALSOFT_REQUIRE_SSE4_1 AND NOT HAVE_SSE4_1)
+    MESSAGE(FATAL_ERROR "Failed to enable required SSE4.1 CPU extensions")
+ENDIF()
+
+# Check for ARM Neon support
+OPTION(ALSOFT_REQUIRE_NEON "Require ARM Neon support" OFF)
+CHECK_INCLUDE_FILE(arm_neon.h HAVE_ARM_NEON_H)
+IF(HAVE_ARM_NEON_H)
+    OPTION(ALSOFT_CPUEXT_NEON "Enable ARM Neon support" ON)
+    IF(ALSOFT_CPUEXT_NEON)
+        SET(HAVE_NEON 1)
+        SET(ALC_OBJS  ${ALC_OBJS} Alc/mixer_neon.c)
+        IF(FPU_NEON_SWITCH)
+            SET_SOURCE_FILES_PROPERTIES(Alc/mixer_neon.c PROPERTIES
+                                        COMPILE_FLAGS "${FPU_NEON_SWITCH}")
+        ENDIF()
+        SET(CPU_EXTS "${CPU_EXTS}, Neon")
+    ENDIF()
+ENDIF()
+IF(ALSOFT_REQUIRE_NEON AND NOT HAVE_NEON)
+    MESSAGE(FATAL_ERROR "Failed to enabled required ARM Neon CPU extensions")
+ENDIF()
+
+
+IF(WIN32 OR HAVE_DLFCN_H)
+    SET(IS_LINKED "")
+    MACRO(ADD_BACKEND_LIBS _LIBS)
+    ENDMACRO()
+ELSE()
+    SET(IS_LINKED " (linked)")
+    MACRO(ADD_BACKEND_LIBS _LIBS)
+        SET(EXTRA_LIBS ${_LIBS} ${EXTRA_LIBS})
+    ENDMACRO()
+ENDIF()
+
+SET(BACKENDS "")
+SET(ALC_OBJS  ${ALC_OBJS}
+              Alc/backends/base.c
+              # Default backends, always available
+              Alc/backends/loopback.c
+              Alc/backends/null.c
+)
+
+# Check ALSA backend
+OPTION(ALSOFT_REQUIRE_ALSA "Require ALSA backend" OFF)
+FIND_PACKAGE(ALSA)
+IF(ALSA_FOUND)
+    OPTION(ALSOFT_BACKEND_ALSA "Enable ALSA backend" ON)
+    IF(ALSOFT_BACKEND_ALSA)
+        SET(HAVE_ALSA 1)
+        SET(BACKENDS  "${BACKENDS} ALSA${IS_LINKED},")
+        SET(ALC_OBJS  ${ALC_OBJS} Alc/backends/alsa.c)
+        ADD_BACKEND_LIBS(${ALSA_LIBRARIES})
+        IF(CMAKE_VERSION VERSION_LESS "2.8.8")
+            INCLUDE_DIRECTORIES(${ALSA_INCLUDE_DIRS})
+        ENDIF()
+    ENDIF()
+ENDIF()
+IF(ALSOFT_REQUIRE_ALSA AND NOT HAVE_ALSA)
+    MESSAGE(FATAL_ERROR "Failed to enabled required ALSA backend")
+ENDIF()
+
+# Check OSS backend
+OPTION(ALSOFT_REQUIRE_OSS "Require OSS backend" OFF)
+FIND_PACKAGE(OSS)
+IF(OSS_FOUND)
+    OPTION(ALSOFT_BACKEND_OSS "Enable OSS backend" ON)
+    IF(ALSOFT_BACKEND_OSS)
+        SET(HAVE_OSS 1)
+        SET(BACKENDS  "${BACKENDS} OSS,")
+        SET(ALC_OBJS  ${ALC_OBJS} Alc/backends/oss.c)
+        IF(CMAKE_VERSION VERSION_LESS "2.8.8")
+            INCLUDE_DIRECTORIES(${OSS_INCLUDE_DIRS})
+        ENDIF()
+    ENDIF()
+ENDIF()
+IF(ALSOFT_REQUIRE_OSS AND NOT HAVE_OSS)
+    MESSAGE(FATAL_ERROR "Failed to enabled required OSS backend")
+ENDIF()
+
+# Check Solaris backend
+OPTION(ALSOFT_REQUIRE_SOLARIS "Require Solaris backend" OFF)
+FIND_PACKAGE(AudioIO)
+IF(AUDIOIO_FOUND)
+    OPTION(ALSOFT_BACKEND_SOLARIS "Enable Solaris backend" ON)
+    IF(ALSOFT_BACKEND_SOLARIS)
+        SET(HAVE_SOLARIS 1)
+        SET(BACKENDS  "${BACKENDS} Solaris,")
+        SET(ALC_OBJS  ${ALC_OBJS} Alc/backends/solaris.c)
+        IF(CMAKE_VERSION VERSION_LESS "2.8.8")
+            INCLUDE_DIRECTORIES(${AUDIOIO_INCLUDE_DIRS})
+        ENDIF()
+    ENDIF()
+ENDIF()
+IF(ALSOFT_REQUIRE_SOLARIS AND NOT HAVE_SOLARIS)
+    MESSAGE(FATAL_ERROR "Failed to enabled required Solaris backend")
+ENDIF()
+
+# Check SndIO backend
+OPTION(ALSOFT_REQUIRE_SNDIO "Require SndIO backend" OFF)
+FIND_PACKAGE(SoundIO)
+IF(SOUNDIO_FOUND)
+    OPTION(ALSOFT_BACKEND_SNDIO "Enable SndIO backend" ON)
+    IF(ALSOFT_BACKEND_SNDIO)
+        SET(HAVE_SNDIO 1)
+        SET(BACKENDS  "${BACKENDS} SndIO (linked),")
+        SET(ALC_OBJS  ${ALC_OBJS} Alc/backends/sndio.c)
+        SET(EXTRA_LIBS ${SOUNDIO_LIBRARIES} ${EXTRA_LIBS})
+        IF(CMAKE_VERSION VERSION_LESS "2.8.8")
+            INCLUDE_DIRECTORIES(${SOUNDIO_INCLUDE_DIRS})
+        ENDIF()
+    ENDIF()
+ENDIF()
+IF(ALSOFT_REQUIRE_SNDIO AND NOT HAVE_SNDIO)
+    MESSAGE(FATAL_ERROR "Failed to enabled required SndIO backend")
+ENDIF()
+
+# Check QSA backend
+OPTION(ALSOFT_REQUIRE_QSA "Require QSA backend" OFF)
+FIND_PACKAGE(QSA)
+IF(QSA_FOUND)
+    OPTION(ALSOFT_BACKEND_QSA "Enable QSA backend" ON)
+    IF(ALSOFT_BACKEND_QSA)
+        SET(HAVE_QSA 1)
+        SET(BACKENDS  "${BACKENDS} QSA (linked),")
+        SET(ALC_OBJS  ${ALC_OBJS} Alc/backends/qsa.c)
+        SET(EXTRA_LIBS ${QSA_LIBRARIES} ${EXTRA_LIBS})
+        IF(CMAKE_VERSION VERSION_LESS "2.8.8")
+            INCLUDE_DIRECTORIES(${QSA_INCLUDE_DIRS})
+        ENDIF()
+    ENDIF()
+ENDIF()
+IF(ALSOFT_REQUIRE_QSA AND NOT HAVE_QSA)
+    MESSAGE(FATAL_ERROR "Failed to enabled required QSA backend")
+ENDIF()
+
+# Check Windows-only backends
+OPTION(ALSOFT_REQUIRE_WINMM "Require Windows Multimedia backend" OFF)
+OPTION(ALSOFT_REQUIRE_DSOUND "Require DirectSound backend" OFF)
+OPTION(ALSOFT_REQUIRE_MMDEVAPI "Require MMDevApi backend" OFF)
+IF(HAVE_WINDOWS_H)
+    # Check MMSystem backend
+    CHECK_INCLUDE_FILES("windows.h;mmsystem.h" HAVE_MMSYSTEM_H -D_WIN32_WINNT=0x0502)
+    IF(HAVE_MMSYSTEM_H)
+        CHECK_SHARED_FUNCTION_EXISTS(waveOutOpen "windows.h;mmsystem.h" winmm "" HAVE_LIBWINMM)
+        IF(HAVE_LIBWINMM)
+            OPTION(ALSOFT_BACKEND_WINMM "Enable Windows Multimedia backend" ON)
+            IF(ALSOFT_BACKEND_WINMM)
+                SET(HAVE_WINMM 1)
+                SET(BACKENDS  "${BACKENDS} WinMM,")
+                SET(ALC_OBJS  ${ALC_OBJS} Alc/backends/winmm.c)
+                SET(EXTRA_LIBS winmm ${EXTRA_LIBS})
+            ENDIF()
+        ENDIF()
+    ENDIF()
+
+    # Check DSound backend
+    FIND_PACKAGE(DSound)
+    IF(DSOUND_FOUND)
+        OPTION(ALSOFT_BACKEND_DSOUND "Enable DirectSound backend" ON)
+        IF(ALSOFT_BACKEND_DSOUND)
+            SET(HAVE_DSOUND 1)
+            SET(BACKENDS  "${BACKENDS} DirectSound${IS_LINKED},")
+            SET(ALC_OBJS  ${ALC_OBJS} Alc/backends/dsound.c)
+            ADD_BACKEND_LIBS(${DSOUND_LIBRARIES})
+            IF(CMAKE_VERSION VERSION_LESS "2.8.8")
+                INCLUDE_DIRECTORIES(${DSOUND_INCLUDE_DIRS})
+            ENDIF()
+        ENDIF()
+    ENDIF()
+
+    # Check for MMDevApi backend
+    CHECK_INCLUDE_FILE(mmdeviceapi.h HAVE_MMDEVICEAPI_H)
+    IF(HAVE_MMDEVICEAPI_H)
+        OPTION(ALSOFT_BACKEND_MMDEVAPI "Enable MMDevApi backend" ON)
+        IF(ALSOFT_BACKEND_MMDEVAPI)
+            SET(HAVE_MMDEVAPI 1)
+            SET(BACKENDS  "${BACKENDS} MMDevApi,")
+            SET(ALC_OBJS  ${ALC_OBJS} Alc/backends/mmdevapi.c)
+        ENDIF()
+    ENDIF()
+ENDIF()
+IF(ALSOFT_REQUIRE_WINMM AND NOT HAVE_WINMM)
+    MESSAGE(FATAL_ERROR "Failed to enabled required WinMM backend")
+ENDIF()
+IF(ALSOFT_REQUIRE_DSOUND AND NOT HAVE_DSOUND)
+    MESSAGE(FATAL_ERROR "Failed to enabled required DSound backend")
+ENDIF()
+IF(ALSOFT_REQUIRE_MMDEVAPI AND NOT HAVE_MMDEVAPI)
+    MESSAGE(FATAL_ERROR "Failed to enabled required MMDevApi backend")
+ENDIF()
+
+# Check PortAudio backend
+OPTION(ALSOFT_REQUIRE_PORTAUDIO "Require PortAudio backend" OFF)
+FIND_PACKAGE(PortAudio)
+IF(PORTAUDIO_FOUND)
+    OPTION(ALSOFT_BACKEND_PORTAUDIO "Enable PortAudio backend" ON)
+    IF(ALSOFT_BACKEND_PORTAUDIO)
+        SET(HAVE_PORTAUDIO 1)
+        SET(BACKENDS  "${BACKENDS} PortAudio${IS_LINKED},")
+        SET(ALC_OBJS  ${ALC_OBJS} Alc/backends/portaudio.c)
+        ADD_BACKEND_LIBS(${PORTAUDIO_LIBRARIES})
+        IF(CMAKE_VERSION VERSION_LESS "2.8.8")
+            INCLUDE_DIRECTORIES(${PORTAUDIO_INCLUDE_DIRS})
+        ENDIF()
+    ENDIF()
+ENDIF()
+IF(ALSOFT_REQUIRE_PORTAUDIO AND NOT HAVE_PORTAUDIO)
+    MESSAGE(FATAL_ERROR "Failed to enabled required PortAudio backend")
+ENDIF()
+
+# Check PulseAudio backend
+OPTION(ALSOFT_REQUIRE_PULSEAUDIO "Require PulseAudio backend" OFF)
+FIND_PACKAGE(PulseAudio)
+IF(PULSEAUDIO_FOUND)
+    OPTION(ALSOFT_BACKEND_PULSEAUDIO "Enable PulseAudio backend" ON)
+    IF(ALSOFT_BACKEND_PULSEAUDIO)
+        SET(HAVE_PULSEAUDIO 1)
+        SET(BACKENDS  "${BACKENDS} PulseAudio${IS_LINKED},")
+        SET(ALC_OBJS  ${ALC_OBJS} Alc/backends/pulseaudio.c)
+        ADD_BACKEND_LIBS(${PULSEAUDIO_LIBRARIES})
+        IF(CMAKE_VERSION VERSION_LESS "2.8.8")
+            INCLUDE_DIRECTORIES(${PULSEAUDIO_INCLUDE_DIRS})
+        ENDIF()
+    ENDIF()
+ENDIF()
+IF(ALSOFT_REQUIRE_PULSEAUDIO AND NOT HAVE_PULSEAUDIO)
+    MESSAGE(FATAL_ERROR "Failed to enabled required PulseAudio backend")
+ENDIF()
+
+# Check JACK backend
+OPTION(ALSOFT_REQUIRE_JACK "Require JACK backend" OFF)
+FIND_PACKAGE(JACK)
+IF(JACK_FOUND)
+    OPTION(ALSOFT_BACKEND_JACK "Enable JACK backend" ON)
+    IF(ALSOFT_BACKEND_JACK)
+        SET(HAVE_JACK 1)
+        SET(BACKENDS  "${BACKENDS} JACK${IS_LINKED},")
+        SET(ALC_OBJS  ${ALC_OBJS} Alc/backends/jack.c)
+        ADD_BACKEND_LIBS(${JACK_LIBRARIES})
+        IF(CMAKE_VERSION VERSION_LESS "2.8.8")
+            INCLUDE_DIRECTORIES(${JACK_INCLUDE_DIRS})
+        ENDIF()
+    ENDIF()
+ENDIF()
+IF(ALSOFT_REQUIRE_JACK AND NOT HAVE_JACK)
+    MESSAGE(FATAL_ERROR "Failed to enabled required JACK backend")
+ENDIF()
+
+# Check CoreAudio backend
+OPTION(ALSOFT_REQUIRE_COREAUDIO "Require CoreAudio backend" OFF)
+FIND_LIBRARY(COREAUDIO_FRAMEWORK
+             NAMES CoreAudio
+             PATHS /System/Library/Frameworks
+)
+IF(COREAUDIO_FRAMEWORK)
+    OPTION(ALSOFT_BACKEND_COREAUDIO "Enable CoreAudio backend" ON)
+    IF(ALSOFT_BACKEND_COREAUDIO)
+        SET(HAVE_COREAUDIO 1)
+        SET(ALC_OBJS  ${ALC_OBJS} Alc/backends/coreaudio.c)
+        SET(BACKENDS  "${BACKENDS} CoreAudio,")
+        SET(EXTRA_LIBS ${COREAUDIO_FRAMEWORK} ${EXTRA_LIBS})
+        SET(EXTRA_LIBS /System/Library/Frameworks/AudioUnit.framework ${EXTRA_LIBS})
+        SET(EXTRA_LIBS /System/Library/Frameworks/ApplicationServices.framework ${EXTRA_LIBS})
+
+        # Some versions of OSX may need the AudioToolbox framework. Add it if
+        # it's found.
+        FIND_LIBRARY(AUDIOTOOLBOX_LIBRARY
+                     NAMES AudioToolbox
+                     PATHS ~/Library/Frameworks
+                           /Library/Frameworks
+                           /System/Library/Frameworks
+                    )
+        IF(AUDIOTOOLBOX_LIBRARY)
+            SET(EXTRA_LIBS ${AUDIOTOOLBOX_LIBRARY} ${EXTRA_LIBS})
+        ENDIF()
+    ENDIF()
+ENDIF()
+IF(ALSOFT_REQUIRE_COREAUDIO AND NOT HAVE_COREAUDIO)
+    MESSAGE(FATAL_ERROR "Failed to enabled required CoreAudio backend")
+ENDIF()
+
+# Check for OpenSL (Android) backend
+OPTION(ALSOFT_REQUIRE_OPENSL "Require OpenSL backend" OFF)
+CHECK_INCLUDE_FILES("SLES/OpenSLES.h;SLES/OpenSLES_Android.h" HAVE_SLES_OPENSLES_ANDROID_H)
+IF(HAVE_SLES_OPENSLES_ANDROID_H)
+    CHECK_SHARED_FUNCTION_EXISTS(slCreateEngine "SLES/OpenSLES.h" OpenSLES "" HAVE_LIBOPENSLES)
+    IF(HAVE_LIBOPENSLES)
+        OPTION(ALSOFT_BACKEND_OPENSL "Enable OpenSL backend" ON)
+        IF(ALSOFT_BACKEND_OPENSL)
+            SET(HAVE_OPENSL 1)
+            SET(ALC_OBJS  ${ALC_OBJS} Alc/backends/opensl.c)
+            SET(BACKENDS  "${BACKENDS} OpenSL,")
+            SET(EXTRA_LIBS OpenSLES ${EXTRA_LIBS})
+        ENDIF()
+    ENDIF()
+ENDIF()
+IF(ALSOFT_REQUIRE_OPENSL AND NOT HAVE_OPENSL)
+    MESSAGE(FATAL_ERROR "Failed to enabled required OpenSL backend")
+ENDIF()
+
+# Optionally enable the Wave Writer backend
+OPTION(ALSOFT_BACKEND_WAVE "Enable Wave Writer backend" ON)
+IF(ALSOFT_BACKEND_WAVE)
+    SET(HAVE_WAVE 1)
+    SET(ALC_OBJS  ${ALC_OBJS} Alc/backends/wave.c)
+    SET(BACKENDS  "${BACKENDS} WaveFile,")
+ENDIF()
+
+# This is always available
+SET(BACKENDS  "${BACKENDS} Null")
+
+option(ALSOFT_EMBED_HRTF_DATA "Embed the HRTF data files (increases library footprint)" OFF)
+if(ALSOFT_EMBED_HRTF_DATA)
+    if(WIN32)
+        set(ALC_OBJS  ${ALC_OBJS} Alc/hrtf_res.rc)
+    else()
+        set(FILENAMES default-44100.mhr default-48000.mhr)
+        foreach(FILENAME ${FILENAMES})
+            set(outfile ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/${FILENAME}${CMAKE_C_OUTPUT_EXTENSION})
+            add_custom_command(OUTPUT ${outfile}
+                DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/hrtf/${FILENAME}"
+                WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/hrtf"
+                COMMAND "${CMAKE_LINKER}" -r -b binary -o "${outfile}" ${FILENAME}
+                COMMAND "${CMAKE_OBJCOPY}" --rename-section .data=.rodata,alloc,load,readonly,data,contents "${outfile}" "${outfile}"
+                COMMENT "Generating ${FILENAME}${CMAKE_C_OUTPUT_EXTENSION}"
+                VERBATIM
+            )
+            set(ALC_OBJS  ${ALC_OBJS} ${outfile})
+        endforeach()
+        unset(outfile)
+        unset(FILENAMES)
+    endif()
+endif()
+
+
+IF(ALSOFT_UTILS AND NOT ALSOFT_NO_CONFIG_UTIL)
+    add_subdirectory(utils/alsoft-config)
+ENDIF()
+IF(ALSOFT_EXAMPLES)
+    FIND_PACKAGE(SDL2)
+    IF(SDL2_FOUND)
+        FIND_PACKAGE(SDL_sound)
+        IF(SDL_SOUND_FOUND AND CMAKE_VERSION VERSION_LESS "2.8.8")
+            INCLUDE_DIRECTORIES(${SDL2_INCLUDE_DIR} ${SDL_SOUND_INCLUDE_DIR})
+        ENDIF()
+        FIND_PACKAGE(FFmpeg COMPONENTS AVFORMAT AVCODEC AVUTIL SWSCALE SWRESAMPLE)
+        IF(FFMPEG_FOUND AND CMAKE_VERSION VERSION_LESS "2.8.8")
+            INCLUDE_DIRECTORIES(${FFMPEG_INCLUDE_DIRS})
+        ENDIF()
+    ENDIF()
+ENDIF()
+
+IF(LIBTYPE STREQUAL "STATIC")
+    ADD_DEFINITIONS(-DAL_LIBTYPE_STATIC)
+    SET(PKG_CONFIG_CFLAGS -DAL_LIBTYPE_STATIC ${PKG_CONFIG_CFLAGS})
+ENDIF()
+
+# Needed for openal.pc.in
+SET(prefix ${CMAKE_INSTALL_PREFIX})
+SET(exec_prefix "\${prefix}")
+SET(libdir "\${exec_prefix}/${CMAKE_INSTALL_LIBDIR}")
+SET(bindir "\${exec_prefix}/${CMAKE_INSTALL_BINDIR}")
+SET(includedir "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}")
+SET(PACKAGE_VERSION "${LIB_VERSION}")
+
+# End configuration
+CONFIGURE_FILE(
+    "${OpenAL_SOURCE_DIR}/config.h.in"
+    "${OpenAL_BINARY_DIR}/config.h")
+CONFIGURE_FILE(
+    "${OpenAL_SOURCE_DIR}/openal.pc.in"
+    "${OpenAL_BINARY_DIR}/openal.pc"
+    @ONLY)
+
+# Build a common library with reusable helpers
+ADD_LIBRARY(common STATIC ${COMMON_OBJS})
+SET_PROPERTY(TARGET common APPEND PROPERTY COMPILE_FLAGS ${EXTRA_CFLAGS})
+IF(NOT LIBTYPE STREQUAL "STATIC")
+    SET_PROPERTY(TARGET common PROPERTY POSITION_INDEPENDENT_CODE TRUE)
+ENDIF()
+
+# Build main library
+IF(LIBTYPE STREQUAL "STATIC")
+    ADD_LIBRARY(${LIBNAME} STATIC ${COMMON_OBJS} ${OPENAL_OBJS} ${ALC_OBJS})
+ELSE()
+    ADD_LIBRARY(${LIBNAME} SHARED ${OPENAL_OBJS} ${ALC_OBJS})
+ENDIF()
+SET_PROPERTY(TARGET ${LIBNAME} APPEND PROPERTY COMPILE_FLAGS ${EXTRA_CFLAGS})
+SET_PROPERTY(TARGET ${LIBNAME} APPEND PROPERTY COMPILE_DEFINITIONS AL_BUILD_LIBRARY AL_ALEXT_PROTOTYPES)
+IF(WIN32 AND ALSOFT_NO_UID_DEFS)
+    SET_PROPERTY(TARGET ${LIBNAME} APPEND PROPERTY COMPILE_DEFINITIONS AL_NO_UID_DEFS)
+ENDIF()
+SET_PROPERTY(TARGET ${LIBNAME} APPEND PROPERTY INCLUDE_DIRECTORIES "${OpenAL_SOURCE_DIR}/OpenAL32/Include" "${OpenAL_SOURCE_DIR}/Alc")
+IF(HAVE_ALSA)
+    SET_PROPERTY(TARGET ${LIBNAME} APPEND PROPERTY INCLUDE_DIRECTORIES ${ALSA_INCLUDE_DIRS})
+ENDIF()
+IF(HAVE_OSS)
+    SET_PROPERTY(TARGET ${LIBNAME} APPEND PROPERTY INCLUDE_DIRECTORIES ${OSS_INCLUDE_DIRS})
+ENDIF()
+IF(HAVE_SOLARIS)
+    SET_PROPERTY(TARGET ${LIBNAME} APPEND PROPERTY INCLUDE_DIRECTORIES ${AUDIOIO_INCLUDE_DIRS})
+ENDIF()
+IF(HAVE_SNDIO)
+    SET_PROPERTY(TARGET ${LIBNAME} APPEND PROPERTY INCLUDE_DIRECTORIES ${SOUNDIO_INCLUDE_DIRS})
+ENDIF()
+IF(HAVE_QSA)
+    SET_PROPERTY(TARGET ${LIBNAME} APPEND PROPERTY INCLUDE_DIRECTORIES ${QSA_INCLUDE_DIRS})
+ENDIF()
+IF(HAVE_DSOUND)
+    SET_PROPERTY(TARGET ${LIBNAME} APPEND PROPERTY INCLUDE_DIRECTORIES ${DSOUND_INCLUDE_DIRS})
+ENDIF()
+IF(HAVE_PORTAUDIO)
+    SET_PROPERTY(TARGET ${LIBNAME} APPEND PROPERTY INCLUDE_DIRECTORIES ${PORTAUDIO_INCLUDE_DIRS})
+ENDIF()
+IF(HAVE_PULSEAUDIO)
+    SET_PROPERTY(TARGET ${LIBNAME} APPEND PROPERTY INCLUDE_DIRECTORIES ${PULSEAUDIO_INCLUDE_DIRS})
+ENDIF()
+IF(HAVE_JACK)
+    SET_PROPERTY(TARGET ${LIBNAME} APPEND PROPERTY INCLUDE_DIRECTORIES ${JACK_INCLUDE_DIRS})
+ENDIF()
+SET_TARGET_PROPERTIES(${LIBNAME} PROPERTIES VERSION ${LIB_VERSION}
+                                            SOVERSION ${LIB_MAJOR_VERSION})
+IF(WIN32 AND NOT LIBTYPE STREQUAL "STATIC")
+    SET_TARGET_PROPERTIES(${LIBNAME} PROPERTIES PREFIX "")
+
+    IF(MINGW AND ALSOFT_BUILD_IMPORT_LIB)
+        FIND_PROGRAM(SED_EXECUTABLE NAMES sed DOC "sed executable")
+        FIND_PROGRAM(DLLTOOL_EXECUTABLE NAMES "${DLLTOOL}" DOC "dlltool executable")
+        IF(NOT SED_EXECUTABLE OR NOT DLLTOOL_EXECUTABLE)
+            MESSAGE(STATUS "")
+            IF(NOT SED_EXECUTABLE)
+                MESSAGE(STATUS "WARNING: Cannot find sed, disabling .def/.lib generation")
+            ENDIF()
+            IF(NOT DLLTOOL_EXECUTABLE)
+                MESSAGE(STATUS "WARNING: Cannot find dlltool, disabling .def/.lib generation")
+            ENDIF()
+        ELSE()
+            SET_TARGET_PROPERTIES(${LIBNAME} PROPERTIES LINK_FLAGS "-Wl,--output-def,${LIBNAME}.def")
+            ADD_CUSTOM_COMMAND(TARGET ${LIBNAME} POST_BUILD
+                COMMAND "${SED_EXECUTABLE}" -i -e "s/ @[^ ]*//" ${LIBNAME}.def
+                COMMAND "${DLLTOOL_EXECUTABLE}" -d ${LIBNAME}.def -l ${LIBNAME}.lib -D ${LIBNAME}.dll
+                COMMENT "Stripping ordinals from ${LIBNAME}.def and generating ${LIBNAME}.lib..."
+                VERBATIM
+            )
+        ENDIF()
+    ENDIF()
+ENDIF()
+
+TARGET_LINK_LIBRARIES(${LIBNAME} common ${EXTRA_LIBS})
+
+IF(ALSOFT_INSTALL)
+    # Add an install target here
+    INSTALL(TARGETS ${LIBNAME}
+            RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+            LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+            ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+    )
+    INSTALL(FILES include/AL/al.h
+                  include/AL/alc.h
+                  include/AL/alext.h
+                  include/AL/efx.h
+                  include/AL/efx-creative.h
+                  include/AL/efx-presets.h
+            DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/AL
+    )
+    INSTALL(FILES "${OpenAL_BINARY_DIR}/openal.pc"
+            DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
+ENDIF()
+
+
+MESSAGE(STATUS "")
+MESSAGE(STATUS "Building OpenAL with support for the following backends:")
+MESSAGE(STATUS "    ${BACKENDS}")
+MESSAGE(STATUS "")
+MESSAGE(STATUS "Building with support for CPU extensions:")
+MESSAGE(STATUS "    ${CPU_EXTS}")
+MESSAGE(STATUS "")
+
+IF(WIN32)
+    IF(NOT HAVE_DSOUND)
+        MESSAGE(STATUS "WARNING: Building the Windows version without DirectSound output")
+        MESSAGE(STATUS "         This is probably NOT what you want!")
+        MESSAGE(STATUS "")
+    ENDIF()
+ENDIF()
+
+if(ALSOFT_EMBED_HRTF_DATA)
+    message(STATUS "Embedding HRTF datasets")
+    message(STATUS "")
+endif()
+
+# Install alsoft.conf configuration file
+IF(ALSOFT_CONFIG)
+    INSTALL(FILES alsoftrc.sample
+            DESTINATION ${CMAKE_INSTALL_DATADIR}/openal
+    )
+    MESSAGE(STATUS "Installing sample configuration")
+    MESSAGE(STATUS "")
+ENDIF()
+
+# Install HRTF definitions
+IF(ALSOFT_HRTF_DEFS)
+    INSTALL(FILES hrtf/default-44100.mhr
+                  hrtf/default-48000.mhr
+            DESTINATION ${CMAKE_INSTALL_DATADIR}/openal/hrtf
+    )
+    MESSAGE(STATUS "Installing HRTF definitions")
+    MESSAGE(STATUS "")
+ENDIF()
+
+# Install AmbDec presets
+IF(ALSOFT_AMBDEC_PRESETS)
+    INSTALL(FILES presets/3D7.1.ambdec
+                  presets/hexagon.ambdec
+                  presets/itu5.1.ambdec
+                  presets/rectangle.ambdec
+                  presets/square.ambdec
+                  presets/presets.txt
+            DESTINATION ${CMAKE_INSTALL_DATADIR}/openal/presets
+    )
+    MESSAGE(STATUS "Installing AmbDec presets")
+    MESSAGE(STATUS "")
+ENDIF()
+
+IF(ALSOFT_UTILS)
+    ADD_EXECUTABLE(openal-info utils/openal-info.c)
+    SET_PROPERTY(TARGET openal-info APPEND PROPERTY COMPILE_FLAGS ${EXTRA_CFLAGS})
+    TARGET_LINK_LIBRARIES(openal-info ${LIBNAME})
+
+    ADD_EXECUTABLE(makehrtf utils/makehrtf.c)
+    SET_PROPERTY(TARGET makehrtf APPEND PROPERTY COMPILE_FLAGS ${EXTRA_CFLAGS})
+    IF(HAVE_LIBM)
+        TARGET_LINK_LIBRARIES(makehrtf m)
+    ENDIF()
+
+    ADD_EXECUTABLE(bsincgen utils/bsincgen.c)
+    SET_PROPERTY(TARGET bsincgen APPEND PROPERTY COMPILE_FLAGS ${EXTRA_CFLAGS})
+    IF(HAVE_LIBM)
+        TARGET_LINK_LIBRARIES(bsincgen m)
+    ENDIF()
+
+    IF(ALSOFT_INSTALL)
+        INSTALL(TARGETS openal-info makehrtf bsincgen
+                RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+                LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+                ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+        )
+    ENDIF()
+
+    MESSAGE(STATUS "Building utility programs")
+    IF(TARGET alsoft-config)
+        MESSAGE(STATUS "Building configuration program")
+    ENDIF()
+    MESSAGE(STATUS "")
+ENDIF()
+
+IF(ALSOFT_TESTS)
+    ADD_LIBRARY(test-common STATIC examples/common/alhelpers.c)
+    SET_PROPERTY(TARGET test-common APPEND PROPERTY COMPILE_FLAGS ${EXTRA_CFLAGS})
+
+    ADD_EXECUTABLE(altonegen examples/altonegen.c)
+    TARGET_LINK_LIBRARIES(altonegen test-common ${LIBNAME})
+    SET_PROPERTY(TARGET altonegen APPEND PROPERTY COMPILE_FLAGS ${EXTRA_CFLAGS})
+
+    IF(ALSOFT_INSTALL)
+        INSTALL(TARGETS altonegen
+                RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+                LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+                ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+        )
+    ENDIF()
+
+    MESSAGE(STATUS "Building test programs")
+    MESSAGE(STATUS "")
+ENDIF()
+
+IF(ALSOFT_EXAMPLES)
+    IF(SDL2_FOUND AND SDL_SOUND_FOUND)
+        ADD_LIBRARY(ex-common STATIC examples/common/alhelpers.c
+                                     examples/common/sdl_sound.c)
+        SET_PROPERTY(TARGET ex-common APPEND PROPERTY COMPILE_FLAGS ${EXTRA_CFLAGS})
+        SET_PROPERTY(TARGET ex-common APPEND PROPERTY INCLUDE_DIRECTORIES ${SDL2_INCLUDE_DIR}
+                                                                          ${SDL_SOUND_INCLUDE_DIR})
+
+        ADD_EXECUTABLE(alstream examples/alstream.c)
+        TARGET_LINK_LIBRARIES(alstream ex-common ${SDL_SOUND_LIBRARIES} ${SDL2_LIBRARY}
+                                       common ${LIBNAME})
+        SET_PROPERTY(TARGET alstream APPEND PROPERTY COMPILE_FLAGS ${EXTRA_CFLAGS})
+        SET_PROPERTY(TARGET alstream APPEND PROPERTY INCLUDE_DIRECTORIES ${SDL2_INCLUDE_DIR}
+                                                                         ${SDL_SOUND_INCLUDE_DIR})
+
+        ADD_EXECUTABLE(alreverb examples/alreverb.c)
+        TARGET_LINK_LIBRARIES(alreverb ex-common ${SDL_SOUND_LIBRARIES} ${SDL2_LIBRARY}
+                                       common ${LIBNAME})
+        SET_PROPERTY(TARGET alreverb APPEND PROPERTY COMPILE_FLAGS ${EXTRA_CFLAGS})
+        SET_PROPERTY(TARGET alreverb APPEND PROPERTY INCLUDE_DIRECTORIES ${SDL2_INCLUDE_DIR}
+                                                                         ${SDL_SOUND_INCLUDE_DIR})
+
+        ADD_EXECUTABLE(allatency examples/allatency.c)
+        TARGET_LINK_LIBRARIES(allatency ex-common ${SDL_SOUND_LIBRARIES} ${SDL2_LIBRARY}
+                                        common ${LIBNAME})
+        SET_PROPERTY(TARGET allatency APPEND PROPERTY COMPILE_FLAGS ${EXTRA_CFLAGS})
+        SET_PROPERTY(TARGET allatency APPEND PROPERTY INCLUDE_DIRECTORIES ${SDL2_INCLUDE_DIR}
+                                                                          ${SDL_SOUND_INCLUDE_DIR})
+
+        ADD_EXECUTABLE(alloopback examples/alloopback.c)
+        TARGET_LINK_LIBRARIES(alloopback ex-common ${SDL_SOUND_LIBRARIES} ${SDL2_LIBRARY}
+                                         common ${LIBNAME})
+        SET_PROPERTY(TARGET alloopback APPEND PROPERTY COMPILE_FLAGS ${EXTRA_CFLAGS})
+        SET_PROPERTY(TARGET alloopback APPEND PROPERTY INCLUDE_DIRECTORIES ${SDL2_INCLUDE_DIR}
+                                                                           ${SDL_SOUND_INCLUDE_DIR})
+
+        ADD_EXECUTABLE(alhrtf examples/alhrtf.c)
+        TARGET_LINK_LIBRARIES(alhrtf ex-common ${SDL_SOUND_LIBRARIES} ${SDL2_LIBRARY}
+                                     common ${LIBNAME})
+        SET_PROPERTY(TARGET alhrtf APPEND PROPERTY COMPILE_FLAGS ${EXTRA_CFLAGS})
+        SET_PROPERTY(TARGET alhrtf APPEND PROPERTY INCLUDE_DIRECTORIES ${SDL2_INCLUDE_DIR}
+                                                                       ${SDL_SOUND_INCLUDE_DIR})
+
+        IF(ALSOFT_INSTALL)
+            INSTALL(TARGETS alstream alreverb allatency alloopback alhrtf
+                    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+                    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+                    ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+            )
+        ENDIF()
+
+        SET(FFVER_OK FALSE)
+        IF(FFMPEG_FOUND)
+            SET(FFVER_OK TRUE)
+            IF(AVFORMAT_VERSION VERSION_LESS "55.33.100")
+                MESSAGE(STATUS "libavformat is too old! (${AVFORMAT_VERSION}, wanted 55.33.100)")
+                SET(FFVER_OK FALSE)
+            ENDIF()
+            IF(AVCODEC_VERSION VERSION_LESS "55.52.102")
+                MESSAGE(STATUS "libavcodec is too old! (${AVCODEC_VERSION}, wanted 55.52.102)")
+                SET(FFVER_OK FALSE)
+            ENDIF()
+            IF(AVUTIL_VERSION VERSION_LESS "52.66.100")
+                MESSAGE(STATUS "libavutil is too old! (${AVUTIL_VERSION}, wanted 52.66.100)")
+                SET(FFVER_OK FALSE)
+            ENDIF()
+            IF(SWSCALE_VERSION VERSION_LESS "2.5.102")
+                MESSAGE(STATUS "libswscale is too old! (${SWSCALE_VERSION}, wanted 2.5.102)")
+                SET(FFVER_OK FALSE)
+            ENDIF()
+            IF(SWRESAMPLE_VERSION VERSION_LESS "0.18.100")
+                MESSAGE(STATUS "libswresample is too old! (${SWRESAMPLE_VERSION}, wanted 0.18.100)")
+                SET(FFVER_OK FALSE)
+            ENDIF()
+        ENDIF()
+        IF(FFVER_OK AND NOT MSVC)
+            ADD_EXECUTABLE(alffplay examples/alffplay.c)
+            TARGET_LINK_LIBRARIES(alffplay common ex-common ${SDL2_LIBRARY} ${LIBNAME} ${FFMPEG_LIBRARIES})
+            SET_PROPERTY(TARGET alffplay APPEND PROPERTY COMPILE_FLAGS ${EXTRA_CFLAGS})
+            SET_PROPERTY(TARGET alffplay APPEND PROPERTY INCLUDE_DIRECTORIES ${SDL2_INCLUDE_DIR}
+                                                                             ${FFMPEG_INCLUDE_DIRS})
+
+            IF(ALSOFT_INSTALL)
+                INSTALL(TARGETS alffplay
+                        RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+                        LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+                        ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+                )
+            ENDIF()
+            MESSAGE(STATUS "Building SDL and FFmpeg example programs")
+        ELSE()
+            MESSAGE(STATUS "Building SDL example programs")
+        ENDIF()
+        MESSAGE(STATUS "")
+    ENDIF()
+ENDIF()

+ 481 - 0
Engine/lib/openal-soft/COPYING

@@ -0,0 +1,481 @@
+                  GNU LIBRARY GENERAL PUBLIC LICENSE
+                       Version 2, June 1991
+
+ Copyright (C) 1991 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the library GPL.  It is
+ numbered 2 because it goes with version 2 of the ordinary GPL.]
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Library General Public License, applies to some
+specially designated Free Software Foundation software, and to any
+other libraries whose authors decide to use it.  You can use it for
+your libraries, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if
+you distribute copies of the library, or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link a program with the library, you must provide
+complete object files to the recipients so that they can relink them
+with the library, after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  Our method of protecting your rights has two steps: (1) copyright
+the library, and (2) offer you this license which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  Also, for each distributor's protection, we want to make certain
+that everyone understands that there is no warranty for this free
+library.  If the library is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original
+version, so that any problems introduced by others will not reflect on
+the original authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that companies distributing free
+software will individually obtain patent licenses, thus in effect
+transforming the program into proprietary software.  To prevent this,
+we have made it clear that any patent must be licensed for everyone's
+free use or not licensed at all.
+
+  Most GNU software, including some libraries, is covered by the ordinary
+GNU General Public License, which was designed for utility programs.  This
+license, the GNU Library General Public License, applies to certain
+designated libraries.  This license is quite different from the ordinary
+one; be sure to read it in full, and don't assume that anything in it is
+the same as in the ordinary license.
+
+  The reason we have a separate public license for some libraries is that
+they blur the distinction we usually make between modifying or adding to a
+program and simply using it.  Linking a program with a library, without
+changing the library, is in some sense simply using the library, and is
+analogous to running a utility program or application program.  However, in
+a textual and legal sense, the linked executable is a combined work, a
+derivative of the original library, and the ordinary General Public License
+treats it as such.
+
+  Because of this blurred distinction, using the ordinary General
+Public License for libraries did not effectively promote software
+sharing, because most developers did not use the libraries.  We
+concluded that weaker conditions might promote sharing better.
+
+  However, unrestricted linking of non-free programs would deprive the
+users of those programs of all benefit from the free status of the
+libraries themselves.  This Library General Public License is intended to
+permit developers of non-free programs to use free libraries, while
+preserving your freedom as a user of such programs to change the free
+libraries that are incorporated in them.  (We have not seen how to achieve
+this as regards changes in header files, but we have achieved it as regards
+changes in the actual functions of the Library.)  The hope is that this
+will lead to faster development of free libraries.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, while the latter only
+works together with the library.
+
+  Note that it is possible for a library to be covered by the ordinary
+General Public License rather than by this special one.
+
+                  GNU LIBRARY GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library which
+contains a notice placed by the copyright holder or other authorized
+party saying it may be distributed under the terms of this Library
+General Public License (also called "this License").  Each licensee is
+addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+  
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+  6. As an exception to the Sections above, you may also compile or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    c) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    d) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the source code distributed need not include anything that is normally
+distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Library General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+                            NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!

+ 188 - 0
Engine/lib/openal-soft/ChangeLog

@@ -0,0 +1,188 @@
+openal-soft-1.17.2:
+
+    Implemented device enumeration for OSSv4.
+
+    Fixed building on OSX.
+
+    Fixed building on non-Windows systems without POSIX-2008.
+
+    Fixed Dedicated Dialog and Dedicated LFE effect output.
+
+    Added a build option to override the share install dir.
+
+    Added a build option to static-link libgcc for MinGW.
+
+openal-soft-1.17.1:
+
+    Fixed building with JACK and without PulseAudio.
+
+    Fixed building on FreeBSD.
+
+    Fixed the ALSA backend's allow-resampler option.
+
+    Fixed handling of inexact ALSA period counts.
+
+    Altered device naming scheme on Windows backends to better match other
+    drivers.
+
+    Updated the CoreAudio backend to use the AudioComponent API. This clears up
+    deprecation warnings for OSX 10.11, although requires OSX 10.6 or newer.
+
+openal-soft-1.17.0:
+
+    Implemented a JACK playback backend.
+
+    Implemented the AL_EXT_BFORMAT and AL_EXT_MULAW_BFORMAT extensions.
+
+    Implemented the ALC_SOFT_HRTF extension.
+
+    Implemented C, SSE3, and SSE4.1 based 4- and 8-point Sinc resamplers.
+
+    Implemented a C and SSE based band-limited Sinc resampler. This does 12- to
+    24-point Sinc resampling, and performs anti-aliasing.
+
+    Implemented B-Format output support for the wave file writer. This creates
+    FuMa-style first-order Ambisonics wave files (AMB format).
+
+    Implemented a stereo-mode config option for treating stereo modes as either
+    speakers or headphones.
+
+    Implemented per-device configuration options.
+
+    Fixed handling of PulseAudio and MMDevAPI devices that have identical
+    descriptions.
+
+    Fixed a potential lockup when stopping playback of suspended PulseAudio devices.
+
+    Fixed logging of Unicode characters on Windows.
+
+    Fixed 5.1 surround sound channels. By default it will now use the side
+    channels for the surround output. A configuration using rear channels is
+    still available.
+
+    Fixed the QSA backend potentially altering the capture format.
+
+    Fixed detecting MMDevAPI's default device.
+
+    Fixed returning the default capture device name.
+
+    Fixed mixing property calculations when deferring context updates.
+
+    Altered the behavior of alcSuspendContext and alcProcessContext to better
+    match certain Windows drivers.
+
+    Altered the panning algorithm, utilizing Ambisonics for better side and
+    back positioning cues with surround sound output.
+
+    Improved support for certain older Windows apps.
+
+    Improved the alffplay example to support surround sound streams.
+
+    Improved support for building as a sub-project.
+
+    Added an HRTF playback example.
+
+    Added a tone generator output test.
+
+    Added a toolchain to help with cross-compiling to Android.
+
+openal-soft-1.16.0:
+
+    Implemented EFX Chorus, Flanger, Distortion, Equalizer, and Compressor
+    effects.
+
+    Implemented high-pass and band-pass EFX filters.
+
+    Implemented the high-pass filter for the EAXReverb effect.
+
+    Implemented SSE2 and SSE4.1 linear resamplers.
+
+    Implemented Neon-enhanced non-HRTF mixers.
+
+    Implemented a QSA backend, for QNX.
+
+    Implemented the ALC_SOFT_pause_device, AL_SOFT_deferred_updates,
+    AL_SOFT_block_alignment, AL_SOFT_MSADPCM, and AL_SOFT_source_length
+    extensions.
+
+    Fixed resetting mmdevapi backend devices.
+
+    Fixed clamping when converting 32-bit float samples to integer.
+
+    Fixed modulation range in the Modulator effect.
+
+    Several fixes for the OpenSL playback backend.
+
+    Fixed device specifier names that have Unicode characters on Windows.
+
+    Added support for filenames and paths with Unicode (UTF-8) characters on
+    Windows.
+
+    Added support for alsoft.conf config files found in XDG Base Directory
+    Specification locations (XDG_CONFIG_DIRS and XDG_CONFIG_HOME, or their
+    defaults) on non-Windows systems.
+
+    Added a GUI configuration utility (requires Qt 4.8).
+
+    Added support for environment variable expansion in config options (not
+    keys or section names).
+
+    Added an example that uses SDL2 and ffmpeg.
+
+    Modified examples to use SDL_sound.
+
+    Modified CMake config option names for better sorting.
+
+    HRTF data sets specified in the hrtf_tables config option may now be
+    relative or absolute filenames.
+
+    Made the default HRTF data set an external file, and added a data set for
+    48khz playback in addition to 44.1khz.
+
+    Added support for C11 atomic methods.
+
+    Improved support for some non-GNU build systems.
+
+openal-soft-1.15.1:
+
+    Fixed a regression with retrieving the source's AL_GAIN property.
+
+openal-soft-1.15:
+
+    Fixed device enumeration with the OSS backend.
+
+    Reorganized internal mixing logic, so unneeded steps can potentially be
+    skipped for better performance.
+
+    Removed the lookup table for calculating the mixing pans. The panning is
+    now calculated directly for better precision.
+
+    Improved the panning of stereo source channels when using stereo output.
+
+    Improved source filter quality on send paths.
+
+    Added a config option to allow PulseAudio to move streams between devices.
+
+    The PulseAudio backend will now attempt to spawn a server by default.
+
+    Added a workaround for a DirectSound bug relating to float32 output.
+
+    Added SSE-based mixers, for HRTF and non-HRTF mixing.
+
+    Added support for the new AL_SOFT_source_latency extension.
+
+    Improved ALSA capture by avoiding an extra buffer when using sizes
+    supported by the underlying device.
+
+    Improved the makehrtf utility to support new options and input formats.
+
+    Modified the CFLAGS declared in the pkg-config file so the "AL/" portion of
+    the header includes can optionally be omitted.
+
+    Added a couple example code programs to show how to apply reverb, and
+    retrieve latency.
+
+    The configuration sample is now installed into the share/openal/ directory
+    instead of /etc/openal.
+
+    The configuration sample now gets installed by default.

+ 183 - 0
Engine/lib/openal-soft/OpenAL32/Include/alAuxEffectSlot.h

@@ -0,0 +1,183 @@
+#ifndef _AL_AUXEFFECTSLOT_H_
+#define _AL_AUXEFFECTSLOT_H_
+
+#include "alMain.h"
+#include "alEffect.h"
+
+#include "align.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ALeffectStateVtable;
+struct ALeffectslot;
+
+typedef struct ALeffectState {
+    RefCount Ref;
+    const struct ALeffectStateVtable *vtbl;
+
+    ALfloat (*OutBuffer)[BUFFERSIZE];
+    ALuint OutChannels;
+} ALeffectState;
+
+void ALeffectState_Construct(ALeffectState *state);
+void ALeffectState_Destruct(ALeffectState *state);
+
+struct ALeffectStateVtable {
+    void (*const Destruct)(ALeffectState *state);
+
+    ALboolean (*const deviceUpdate)(ALeffectState *state, ALCdevice *device);
+    void (*const update)(ALeffectState *state, const ALCdevice *device, const struct ALeffectslot *slot, const union ALeffectProps *props);
+    void (*const process)(ALeffectState *state, ALuint samplesToDo, const ALfloat (*restrict samplesIn)[BUFFERSIZE], ALfloat (*restrict samplesOut)[BUFFERSIZE], ALuint numChannels);
+
+    void (*const Delete)(void *ptr);
+};
+
+#define DEFINE_ALEFFECTSTATE_VTABLE(T)                                        \
+DECLARE_THUNK(T, ALeffectState, void, Destruct)                               \
+DECLARE_THUNK1(T, ALeffectState, ALboolean, deviceUpdate, ALCdevice*)         \
+DECLARE_THUNK3(T, ALeffectState, void, update, const ALCdevice*, const ALeffectslot*, const ALeffectProps*) \
+DECLARE_THUNK4(T, ALeffectState, void, process, ALuint, const ALfloatBUFFERSIZE*restrict, ALfloatBUFFERSIZE*restrict, ALuint) \
+static void T##_ALeffectState_Delete(void *ptr)                               \
+{ return T##_Delete(STATIC_UPCAST(T, ALeffectState, (ALeffectState*)ptr)); }  \
+                                                                              \
+static const struct ALeffectStateVtable T##_ALeffectState_vtable = {          \
+    T##_ALeffectState_Destruct,                                               \
+                                                                              \
+    T##_ALeffectState_deviceUpdate,                                           \
+    T##_ALeffectState_update,                                                 \
+    T##_ALeffectState_process,                                                \
+                                                                              \
+    T##_ALeffectState_Delete,                                                 \
+}
+
+
+struct ALeffectStateFactoryVtable;
+
+typedef struct ALeffectStateFactory {
+    const struct ALeffectStateFactoryVtable *vtbl;
+} ALeffectStateFactory;
+
+struct ALeffectStateFactoryVtable {
+    ALeffectState *(*const create)(ALeffectStateFactory *factory);
+};
+
+#define DEFINE_ALEFFECTSTATEFACTORY_VTABLE(T)                                 \
+DECLARE_THUNK(T, ALeffectStateFactory, ALeffectState*, create)                \
+                                                                              \
+static const struct ALeffectStateFactoryVtable T##_ALeffectStateFactory_vtable = { \
+    T##_ALeffectStateFactory_create,                                          \
+}
+
+
+#define MAX_EFFECT_CHANNELS (4)
+
+
+struct ALeffectslotProps {
+    ATOMIC(ALfloat)   Gain;
+    ATOMIC(ALboolean) AuxSendAuto;
+
+    ATOMIC(ALenum) Type;
+    ALeffectProps Props;
+
+    ATOMIC(ALeffectState*) State;
+
+    ATOMIC(struct ALeffectslotProps*) next;
+};
+
+
+typedef struct ALeffectslot {
+    ALboolean NeedsUpdate;
+
+    ALfloat   Gain;
+    ALboolean AuxSendAuto;
+
+    struct {
+        ALenum Type;
+        ALeffectProps Props;
+
+        ALeffectState *State;
+    } Effect;
+
+    RefCount ref;
+
+    ATOMIC(struct ALeffectslotProps*) Update;
+    ATOMIC(struct ALeffectslotProps*) FreeList;
+
+    struct {
+        ALfloat   Gain;
+        ALboolean AuxSendAuto;
+
+        ALenum EffectType;
+        ALeffectState *EffectState;
+
+        ALfloat RoomRolloff; /* Added to the source's room rolloff, not multiplied. */
+        ALfloat DecayTime;
+        ALfloat AirAbsorptionGainHF;
+    } Params;
+
+    /* Self ID */
+    ALuint id;
+
+    ALuint NumChannels;
+    BFChannelConfig ChanMap[MAX_EFFECT_CHANNELS];
+    /* Wet buffer configuration is ACN channel order with N3D scaling:
+     * * Channel 0 is the unattenuated mono signal.
+     * * Channel 1 is OpenAL -X
+     * * Channel 2 is OpenAL Y
+     * * Channel 3 is OpenAL -Z
+     * Consequently, effects that only want to work with mono input can use
+     * channel 0 by itself. Effects that want multichannel can process the
+     * ambisonics signal and make a B-Format pan (ComputeFirstOrderGains) for
+     * first-order device output (FOAOut).
+     */
+    alignas(16) ALfloat WetBuffer[MAX_EFFECT_CHANNELS][BUFFERSIZE];
+
+    ATOMIC(struct ALeffectslot*) next;
+} ALeffectslot;
+
+inline void LockEffectSlotsRead(ALCcontext *context)
+{ LockUIntMapRead(&context->EffectSlotMap); }
+inline void UnlockEffectSlotsRead(ALCcontext *context)
+{ UnlockUIntMapRead(&context->EffectSlotMap); }
+inline void LockEffectSlotsWrite(ALCcontext *context)
+{ LockUIntMapWrite(&context->EffectSlotMap); }
+inline void UnlockEffectSlotsWrite(ALCcontext *context)
+{ UnlockUIntMapWrite(&context->EffectSlotMap); }
+
+inline struct ALeffectslot *LookupEffectSlot(ALCcontext *context, ALuint id)
+{ return (struct ALeffectslot*)LookupUIntMapKeyNoLock(&context->EffectSlotMap, id); }
+inline struct ALeffectslot *RemoveEffectSlot(ALCcontext *context, ALuint id)
+{ return (struct ALeffectslot*)RemoveUIntMapKeyNoLock(&context->EffectSlotMap, id); }
+
+ALenum InitEffectSlot(ALeffectslot *slot);
+void DeinitEffectSlot(ALeffectslot *slot);
+void UpdateEffectSlotProps(ALeffectslot *slot);
+void UpdateAllEffectSlotProps(ALCcontext *context);
+ALvoid ReleaseALAuxiliaryEffectSlots(ALCcontext *Context);
+
+
+ALeffectStateFactory *ALnullStateFactory_getFactory(void);
+ALeffectStateFactory *ALreverbStateFactory_getFactory(void);
+ALeffectStateFactory *ALchorusStateFactory_getFactory(void);
+ALeffectStateFactory *ALcompressorStateFactory_getFactory(void);
+ALeffectStateFactory *ALdistortionStateFactory_getFactory(void);
+ALeffectStateFactory *ALechoStateFactory_getFactory(void);
+ALeffectStateFactory *ALequalizerStateFactory_getFactory(void);
+ALeffectStateFactory *ALflangerStateFactory_getFactory(void);
+ALeffectStateFactory *ALmodulatorStateFactory_getFactory(void);
+
+ALeffectStateFactory *ALdedicatedStateFactory_getFactory(void);
+
+
+ALenum InitializeEffect(ALCdevice *Device, ALeffectslot *EffectSlot, ALeffect *effect);
+
+void InitEffectFactoryMap(void);
+void DeinitEffectFactoryMap(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 130 - 0
Engine/lib/openal-soft/OpenAL32/Include/alBuffer.h

@@ -0,0 +1,130 @@
+#ifndef _AL_BUFFER_H_
+#define _AL_BUFFER_H_
+
+#include "alMain.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* User formats */
+enum UserFmtType {
+    UserFmtByte   = AL_BYTE_SOFT,
+    UserFmtUByte  = AL_UNSIGNED_BYTE_SOFT,
+    UserFmtShort  = AL_SHORT_SOFT,
+    UserFmtUShort = AL_UNSIGNED_SHORT_SOFT,
+    UserFmtInt    = AL_INT_SOFT,
+    UserFmtUInt   = AL_UNSIGNED_INT_SOFT,
+    UserFmtFloat  = AL_FLOAT_SOFT,
+    UserFmtDouble = AL_DOUBLE_SOFT,
+    UserFmtByte3  = AL_BYTE3_SOFT,
+    UserFmtUByte3 = AL_UNSIGNED_BYTE3_SOFT,
+    UserFmtMulaw  = AL_MULAW_SOFT,
+    UserFmtAlaw   = 0x10000000,
+    UserFmtIMA4,
+    UserFmtMSADPCM,
+};
+enum UserFmtChannels {
+    UserFmtMono      = AL_MONO_SOFT,
+    UserFmtStereo    = AL_STEREO_SOFT,
+    UserFmtRear      = AL_REAR_SOFT,
+    UserFmtQuad      = AL_QUAD_SOFT,
+    UserFmtX51       = AL_5POINT1_SOFT, /* (WFX order) */
+    UserFmtX61       = AL_6POINT1_SOFT, /* (WFX order) */
+    UserFmtX71       = AL_7POINT1_SOFT, /* (WFX order) */
+    UserFmtBFormat2D = AL_BFORMAT2D_SOFT, /* WXY */
+    UserFmtBFormat3D = AL_BFORMAT3D_SOFT, /* WXYZ */
+};
+
+ALuint BytesFromUserFmt(enum UserFmtType type);
+ALuint ChannelsFromUserFmt(enum UserFmtChannels chans);
+inline ALuint FrameSizeFromUserFmt(enum UserFmtChannels chans, enum UserFmtType type)
+{
+    return ChannelsFromUserFmt(chans) * BytesFromUserFmt(type);
+}
+
+
+/* Storable formats */
+enum FmtType {
+    FmtByte  = UserFmtByte,
+    FmtShort = UserFmtShort,
+    FmtFloat = UserFmtFloat,
+};
+enum FmtChannels {
+    FmtMono   = UserFmtMono,
+    FmtStereo = UserFmtStereo,
+    FmtRear   = UserFmtRear,
+    FmtQuad   = UserFmtQuad,
+    FmtX51    = UserFmtX51,
+    FmtX61    = UserFmtX61,
+    FmtX71    = UserFmtX71,
+    FmtBFormat2D = UserFmtBFormat2D,
+    FmtBFormat3D = UserFmtBFormat3D,
+};
+#define MAX_INPUT_CHANNELS  (8)
+
+ALuint BytesFromFmt(enum FmtType type);
+ALuint ChannelsFromFmt(enum FmtChannels chans);
+inline ALuint FrameSizeFromFmt(enum FmtChannels chans, enum FmtType type)
+{
+    return ChannelsFromFmt(chans) * BytesFromFmt(type);
+}
+
+
+typedef struct ALbuffer {
+    ALvoid  *data;
+
+    ALsizei  Frequency;
+    ALenum   Format;
+    ALsizei  SampleLen;
+
+    enum FmtChannels FmtChannels;
+    enum FmtType     FmtType;
+    ALuint BytesAlloc;
+
+    enum UserFmtChannels OriginalChannels;
+    enum UserFmtType     OriginalType;
+    ALsizei              OriginalSize;
+    ALsizei              OriginalAlign;
+
+    ALsizei  LoopStart;
+    ALsizei  LoopEnd;
+
+    ATOMIC(ALsizei) UnpackAlign;
+    ATOMIC(ALsizei) PackAlign;
+
+    /* Number of times buffer was attached to a source (deletion can only occur when 0) */
+    RefCount ref;
+
+    RWLock lock;
+
+    /* Self ID */
+    ALuint id;
+} ALbuffer;
+
+ALbuffer *NewBuffer(ALCcontext *context);
+void DeleteBuffer(ALCdevice *device, ALbuffer *buffer);
+
+ALenum LoadData(ALbuffer *buffer, ALuint freq, ALenum NewFormat, ALsizei frames, enum UserFmtChannels SrcChannels, enum UserFmtType SrcType, const ALvoid *data, ALsizei align, ALboolean storesrc);
+
+inline void LockBuffersRead(ALCdevice *device)
+{ LockUIntMapRead(&device->BufferMap); }
+inline void UnlockBuffersRead(ALCdevice *device)
+{ UnlockUIntMapRead(&device->BufferMap); }
+inline void LockBuffersWrite(ALCdevice *device)
+{ LockUIntMapWrite(&device->BufferMap); }
+inline void UnlockBuffersWrite(ALCdevice *device)
+{ UnlockUIntMapWrite(&device->BufferMap); }
+
+inline struct ALbuffer *LookupBuffer(ALCdevice *device, ALuint id)
+{ return (struct ALbuffer*)LookupUIntMapKeyNoLock(&device->BufferMap, id); }
+inline struct ALbuffer *RemoveBuffer(ALCdevice *device, ALuint id)
+{ return (struct ALbuffer*)RemoveUIntMapKeyNoLock(&device->BufferMap, id); }
+
+ALvoid ReleaseALBuffers(ALCdevice *device);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 196 - 0
Engine/lib/openal-soft/OpenAL32/Include/alEffect.h

@@ -0,0 +1,196 @@
+#ifndef _AL_EFFECT_H_
+#define _AL_EFFECT_H_
+
+#include "alMain.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ALeffect;
+
+enum {
+    EAXREVERB = 0,
+    REVERB,
+    CHORUS,
+    COMPRESSOR,
+    DISTORTION,
+    ECHO,
+    EQUALIZER,
+    FLANGER,
+    MODULATOR,
+    DEDICATED,
+
+    MAX_EFFECTS
+};
+extern ALboolean DisabledEffects[MAX_EFFECTS];
+
+extern ALfloat ReverbBoost;
+extern ALboolean EmulateEAXReverb;
+
+struct ALeffectVtable {
+    void (*const setParami)(struct ALeffect *effect, ALCcontext *context, ALenum param, ALint val);
+    void (*const setParamiv)(struct ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals);
+    void (*const setParamf)(struct ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val);
+    void (*const setParamfv)(struct ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals);
+
+    void (*const getParami)(const struct ALeffect *effect, ALCcontext *context, ALenum param, ALint *val);
+    void (*const getParamiv)(const struct ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals);
+    void (*const getParamf)(const struct ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val);
+    void (*const getParamfv)(const struct ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals);
+};
+
+#define DEFINE_ALEFFECT_VTABLE(T)           \
+const struct ALeffectVtable T##_vtable = {  \
+    T##_setParami, T##_setParamiv,          \
+    T##_setParamf, T##_setParamfv,          \
+    T##_getParami, T##_getParamiv,          \
+    T##_getParamf, T##_getParamfv,          \
+}
+
+extern const struct ALeffectVtable ALeaxreverb_vtable;
+extern const struct ALeffectVtable ALreverb_vtable;
+extern const struct ALeffectVtable ALchorus_vtable;
+extern const struct ALeffectVtable ALcompressor_vtable;
+extern const struct ALeffectVtable ALdistortion_vtable;
+extern const struct ALeffectVtable ALecho_vtable;
+extern const struct ALeffectVtable ALequalizer_vtable;
+extern const struct ALeffectVtable ALflanger_vtable;
+extern const struct ALeffectVtable ALmodulator_vtable;
+extern const struct ALeffectVtable ALnull_vtable;
+extern const struct ALeffectVtable ALdedicated_vtable;
+
+
+typedef union ALeffectProps {
+    struct {
+        // Shared Reverb Properties
+        ALfloat Density;
+        ALfloat Diffusion;
+        ALfloat Gain;
+        ALfloat GainHF;
+        ALfloat DecayTime;
+        ALfloat DecayHFRatio;
+        ALfloat ReflectionsGain;
+        ALfloat ReflectionsDelay;
+        ALfloat LateReverbGain;
+        ALfloat LateReverbDelay;
+        ALfloat AirAbsorptionGainHF;
+        ALfloat RoomRolloffFactor;
+        ALboolean DecayHFLimit;
+
+        // Additional EAX Reverb Properties
+        ALfloat GainLF;
+        ALfloat DecayLFRatio;
+        ALfloat ReflectionsPan[3];
+        ALfloat LateReverbPan[3];
+        ALfloat EchoTime;
+        ALfloat EchoDepth;
+        ALfloat ModulationTime;
+        ALfloat ModulationDepth;
+        ALfloat HFReference;
+        ALfloat LFReference;
+    } Reverb;
+
+    struct {
+        ALint Waveform;
+        ALint Phase;
+        ALfloat Rate;
+        ALfloat Depth;
+        ALfloat Feedback;
+        ALfloat Delay;
+    } Chorus;
+
+    struct {
+        ALboolean OnOff;
+    } Compressor;
+
+    struct {
+        ALfloat Edge;
+        ALfloat Gain;
+        ALfloat LowpassCutoff;
+        ALfloat EQCenter;
+        ALfloat EQBandwidth;
+    } Distortion;
+
+    struct {
+        ALfloat Delay;
+        ALfloat LRDelay;
+
+        ALfloat Damping;
+        ALfloat Feedback;
+
+        ALfloat Spread;
+    } Echo;
+
+    struct {
+        ALfloat LowCutoff;
+        ALfloat LowGain;
+        ALfloat Mid1Center;
+        ALfloat Mid1Gain;
+        ALfloat Mid1Width;
+        ALfloat Mid2Center;
+        ALfloat Mid2Gain;
+        ALfloat Mid2Width;
+        ALfloat HighCutoff;
+        ALfloat HighGain;
+    } Equalizer;
+
+    struct {
+        ALint Waveform;
+        ALint Phase;
+        ALfloat Rate;
+        ALfloat Depth;
+        ALfloat Feedback;
+        ALfloat Delay;
+    } Flanger;
+
+    struct {
+        ALfloat Frequency;
+        ALfloat HighPassCutoff;
+        ALint Waveform;
+    } Modulator;
+
+    struct {
+        ALfloat Gain;
+    } Dedicated;
+} ALeffectProps;
+
+typedef struct ALeffect {
+    // Effect type (AL_EFFECT_NULL, ...)
+    ALenum type;
+
+    ALeffectProps Props;
+
+    const struct ALeffectVtable *vtbl;
+
+    /* Self ID */
+    ALuint id;
+} ALeffect;
+
+inline void LockEffectsRead(ALCdevice *device)
+{ LockUIntMapRead(&device->EffectMap); }
+inline void UnlockEffectsRead(ALCdevice *device)
+{ UnlockUIntMapRead(&device->EffectMap); }
+inline void LockEffectsWrite(ALCdevice *device)
+{ LockUIntMapWrite(&device->EffectMap); }
+inline void UnlockEffectsWrite(ALCdevice *device)
+{ UnlockUIntMapWrite(&device->EffectMap); }
+
+inline struct ALeffect *LookupEffect(ALCdevice *device, ALuint id)
+{ return (struct ALeffect*)LookupUIntMapKeyNoLock(&device->EffectMap, id); }
+inline struct ALeffect *RemoveEffect(ALCdevice *device, ALuint id)
+{ return (struct ALeffect*)RemoveUIntMapKeyNoLock(&device->EffectMap, id); }
+
+inline ALboolean IsReverbEffect(ALenum type)
+{ return type == AL_EFFECT_REVERB || type == AL_EFFECT_EAXREVERB; }
+
+ALenum InitEffect(ALeffect *effect);
+ALvoid ReleaseALEffects(ALCdevice *device);
+
+ALvoid LoadReverbPreset(const char *name, ALeffect *effect);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 33 - 0
Engine/lib/openal-soft/OpenAL32/Include/alError.h

@@ -0,0 +1,33 @@
+#ifndef _AL_ERROR_H_
+#define _AL_ERROR_H_
+
+#include "alMain.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern ALboolean TrapALError;
+
+ALvoid alSetError(ALCcontext *Context, ALenum errorCode);
+
+#define SET_ERROR_AND_RETURN(ctx, err) do {                                    \
+    alSetError((ctx), (err));                                                  \
+    return;                                                                    \
+} while(0)
+
+#define SET_ERROR_AND_RETURN_VALUE(ctx, err, val) do {                         \
+    alSetError((ctx), (err));                                                  \
+    return (val);                                                              \
+} while(0)
+
+#define SET_ERROR_AND_GOTO(ctx, err, lbl) do {                                 \
+    alSetError((ctx), (err));                                                  \
+    goto lbl;                                                                  \
+} while(0)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 155 - 0
Engine/lib/openal-soft/OpenAL32/Include/alFilter.h

@@ -0,0 +1,155 @@
+#ifndef _AL_FILTER_H_
+#define _AL_FILTER_H_
+
+#include "alMain.h"
+
+#include "math_defs.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define LOWPASSFREQREF  (5000.0f)
+#define HIGHPASSFREQREF  (250.0f)
+
+
+/* Filters implementation is based on the "Cookbook formulae for audio
+ * EQ biquad filter coefficients" by Robert Bristow-Johnson
+ * http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt
+ */
+/* Implementation note: For the shelf filters, the specified gain is for the
+ * reference frequency, which is the centerpoint of the transition band. This
+ * better matches EFX filter design. To set the gain for the shelf itself, use
+ * the square root of the desired linear gain (or halve the dB gain).
+ */
+
+typedef enum ALfilterType {
+    /** EFX-style low-pass filter, specifying a gain and reference frequency. */
+    ALfilterType_HighShelf,
+    /** EFX-style high-pass filter, specifying a gain and reference frequency. */
+    ALfilterType_LowShelf,
+    /** Peaking filter, specifying a gain and reference frequency. */
+    ALfilterType_Peaking,
+
+    /** Low-pass cut-off filter, specifying a cut-off frequency. */
+    ALfilterType_LowPass,
+    /** High-pass cut-off filter, specifying a cut-off frequency. */
+    ALfilterType_HighPass,
+    /** Band-pass filter, specifying a center frequency. */
+    ALfilterType_BandPass,
+} ALfilterType;
+
+typedef struct ALfilterState {
+    ALfloat x[2]; /* History of two last input samples  */
+    ALfloat y[2]; /* History of two last output samples */
+    ALfloat a1, a2; /* Transfer function coefficients "a" (a0 is pre-applied) */
+    ALfloat b0, b1, b2; /* Transfer function coefficients "b" */
+} ALfilterState;
+/* Currently only a C-based filter process method is implemented. */
+#define ALfilterState_process ALfilterState_processC
+
+/* Calculates the rcpQ (i.e. 1/Q) coefficient for shelving filters, using the
+ * reference gain and shelf slope parameter.
+ * 0 < gain
+ * 0 < slope <= 1
+ */
+inline ALfloat calc_rcpQ_from_slope(ALfloat gain, ALfloat slope)
+{
+    return sqrtf((gain + 1.0f/gain)*(1.0f/slope - 1.0f) + 2.0f);
+}
+/* Calculates the rcpQ (i.e. 1/Q) coefficient for filters, using the frequency
+ * multiple (i.e. ref_freq / sampling_freq) and bandwidth.
+ * 0 < freq_mult < 0.5.
+ */
+inline ALfloat calc_rcpQ_from_bandwidth(ALfloat freq_mult, ALfloat bandwidth)
+{
+    ALfloat w0 = F_TAU * freq_mult;
+    return 2.0f*sinhf(logf(2.0f)/2.0f*bandwidth*w0/sinf(w0));
+}
+
+inline void ALfilterState_clear(ALfilterState *filter)
+{
+    filter->x[0] = 0.0f;
+    filter->x[1] = 0.0f;
+    filter->y[0] = 0.0f;
+    filter->y[1] = 0.0f;
+}
+
+void ALfilterState_setParams(ALfilterState *filter, ALfilterType type, ALfloat gain, ALfloat freq_mult, ALfloat rcpQ);
+
+void ALfilterState_processC(ALfilterState *filter, ALfloat *restrict dst, const ALfloat *restrict src, ALuint numsamples);
+
+inline void ALfilterState_processPassthru(ALfilterState *filter, const ALfloat *restrict src, ALuint numsamples)
+{
+    if(numsamples >= 2)
+    {
+        filter->x[1] = src[numsamples-2];
+        filter->x[0] = src[numsamples-1];
+        filter->y[1] = src[numsamples-2];
+        filter->y[0] = src[numsamples-1];
+    }
+    else if(numsamples == 1)
+    {
+        filter->x[1] = filter->x[0];
+        filter->x[0] = src[0];
+        filter->y[1] = filter->y[0];
+        filter->y[0] = src[0];
+    }
+}
+
+
+typedef struct ALfilter {
+    // Filter type (AL_FILTER_NULL, ...)
+    ALenum type;
+
+    ALfloat Gain;
+    ALfloat GainHF;
+    ALfloat HFReference;
+    ALfloat GainLF;
+    ALfloat LFReference;
+
+    void (*SetParami)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALint val);
+    void (*SetParamiv)(struct ALfilter *filter, ALCcontext *context, ALenum param, const ALint *vals);
+    void (*SetParamf)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALfloat val);
+    void (*SetParamfv)(struct ALfilter *filter, ALCcontext *context, ALenum param, const ALfloat *vals);
+
+    void (*GetParami)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALint *val);
+    void (*GetParamiv)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALint *vals);
+    void (*GetParamf)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *val);
+    void (*GetParamfv)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *vals);
+
+    /* Self ID */
+    ALuint id;
+} ALfilter;
+
+#define ALfilter_SetParami(x, c, p, v)  ((x)->SetParami((x),(c),(p),(v)))
+#define ALfilter_SetParamiv(x, c, p, v) ((x)->SetParamiv((x),(c),(p),(v)))
+#define ALfilter_SetParamf(x, c, p, v)  ((x)->SetParamf((x),(c),(p),(v)))
+#define ALfilter_SetParamfv(x, c, p, v) ((x)->SetParamfv((x),(c),(p),(v)))
+
+#define ALfilter_GetParami(x, c, p, v)  ((x)->GetParami((x),(c),(p),(v)))
+#define ALfilter_GetParamiv(x, c, p, v) ((x)->GetParamiv((x),(c),(p),(v)))
+#define ALfilter_GetParamf(x, c, p, v)  ((x)->GetParamf((x),(c),(p),(v)))
+#define ALfilter_GetParamfv(x, c, p, v) ((x)->GetParamfv((x),(c),(p),(v)))
+
+inline void LockFiltersRead(ALCdevice *device)
+{ LockUIntMapRead(&device->FilterMap); }
+inline void UnlockFiltersRead(ALCdevice *device)
+{ UnlockUIntMapRead(&device->FilterMap); }
+inline void LockFiltersWrite(ALCdevice *device)
+{ LockUIntMapWrite(&device->FilterMap); }
+inline void UnlockFiltersWrite(ALCdevice *device)
+{ UnlockUIntMapWrite(&device->FilterMap); }
+
+inline struct ALfilter *LookupFilter(ALCdevice *device, ALuint id)
+{ return (struct ALfilter*)LookupUIntMapKeyNoLock(&device->FilterMap, id); }
+inline struct ALfilter *RemoveFilter(ALCdevice *device, ALuint id)
+{ return (struct ALfilter*)RemoveUIntMapKeyNoLock(&device->FilterMap, id); }
+
+ALvoid ReleaseALFilters(ALCdevice *device);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 66 - 0
Engine/lib/openal-soft/OpenAL32/Include/alListener.h

@@ -0,0 +1,66 @@
+#ifndef _AL_LISTENER_H_
+#define _AL_LISTENER_H_
+
+#include "alMain.h"
+#include "alu.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ALlistenerProps {
+    ATOMIC(ALfloat) Position[3];
+    ATOMIC(ALfloat) Velocity[3];
+    ATOMIC(ALfloat) Forward[3];
+    ATOMIC(ALfloat) Up[3];
+    ATOMIC(ALfloat) Gain;
+    ATOMIC(ALfloat) MetersPerUnit;
+
+    ATOMIC(ALfloat) DopplerFactor;
+    ATOMIC(ALfloat) DopplerVelocity;
+    ATOMIC(ALfloat) SpeedOfSound;
+    ATOMIC(ALboolean) SourceDistanceModel;
+    ATOMIC(enum DistanceModel) DistanceModel;
+
+    ATOMIC(struct ALlistenerProps*) next;
+};
+
+typedef struct ALlistener {
+    volatile ALfloat Position[3];
+    volatile ALfloat Velocity[3];
+    volatile ALfloat Forward[3];
+    volatile ALfloat Up[3];
+    volatile ALfloat Gain;
+    volatile ALfloat MetersPerUnit;
+
+    /* Pointer to the most recent property values that are awaiting an update.
+     */
+    ATOMIC(struct ALlistenerProps*) Update;
+
+    /* A linked list of unused property containers, free to use for future
+     * updates.
+     */
+    ATOMIC(struct ALlistenerProps*) FreeList;
+
+    struct {
+        aluMatrixf Matrix;
+        aluVector  Velocity;
+
+        ALfloat Gain;
+        ALfloat MetersPerUnit;
+
+        ALfloat DopplerFactor;
+        ALfloat SpeedOfSound;
+
+        ALboolean SourceDistanceModel;
+        enum DistanceModel DistanceModel;
+    } Params;
+} ALlistener;
+
+void UpdateListenerProps(ALCcontext *context);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 967 - 0
Engine/lib/openal-soft/OpenAL32/Include/alMain.h

@@ -0,0 +1,967 @@
+#ifndef AL_MAIN_H
+#define AL_MAIN_H
+
+#include <string.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <math.h>
+#include <limits.h>
+
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+
+#ifdef HAVE_FENV_H
+#include <fenv.h>
+#endif
+
+#include "AL/al.h"
+#include "AL/alc.h"
+#include "AL/alext.h"
+
+
+#if defined(_WIN64)
+#define SZFMT "%I64u"
+#elif defined(_WIN32)
+#define SZFMT "%u"
+#else
+#define SZFMT "%zu"
+#endif
+
+
+#include "static_assert.h"
+#include "align.h"
+#include "atomic.h"
+#include "uintmap.h"
+#include "vector.h"
+#include "alstring.h"
+#include "almalloc.h"
+#include "threads.h"
+
+#include "hrtf.h"
+
+#ifndef ALC_SOFT_device_clock
+#define ALC_SOFT_device_clock 1
+typedef int64_t ALCint64SOFT;
+typedef uint64_t ALCuint64SOFT;
+#define ALC_DEVICE_CLOCK_SOFT                    0x1600
+#define ALC_DEVICE_LATENCY_SOFT                  0x1601
+#define ALC_DEVICE_CLOCK_LATENCY_SOFT            0x1602
+typedef void (ALC_APIENTRY*LPALCGETINTEGER64VSOFT)(ALCdevice *device, ALCenum pname, ALsizei size, ALCint64SOFT *values);
+#ifdef AL_ALEXT_PROTOTYPES
+ALC_API void ALC_APIENTRY alcGetInteger64vSOFT(ALCdevice *device, ALCenum pname, ALsizei size, ALCint64SOFT *values);
+#endif
+#endif
+
+#ifndef AL_SOFT_buffer_samples2
+#define AL_SOFT_buffer_samples2 1
+/* Channel configurations */
+#define AL_MONO_SOFT                             0x1500
+#define AL_STEREO_SOFT                           0x1501
+#define AL_REAR_SOFT                             0x1502
+#define AL_QUAD_SOFT                             0x1503
+#define AL_5POINT1_SOFT                          0x1504
+#define AL_6POINT1_SOFT                          0x1505
+#define AL_7POINT1_SOFT                          0x1506
+#define AL_BFORMAT2D_SOFT                        0x1507
+#define AL_BFORMAT3D_SOFT                        0x1508
+
+/* Sample types */
+#define AL_BYTE_SOFT                             0x1400
+#define AL_UNSIGNED_BYTE_SOFT                    0x1401
+#define AL_SHORT_SOFT                            0x1402
+#define AL_UNSIGNED_SHORT_SOFT                   0x1403
+#define AL_INT_SOFT                              0x1404
+#define AL_UNSIGNED_INT_SOFT                     0x1405
+#define AL_FLOAT_SOFT                            0x1406
+#define AL_DOUBLE_SOFT                           0x1407
+#define AL_BYTE3_SOFT                            0x1408
+#define AL_UNSIGNED_BYTE3_SOFT                   0x1409
+#define AL_MULAW_SOFT                            0x140A
+
+/* Storage formats */
+#define AL_MONO8_SOFT                            0x1100
+#define AL_MONO16_SOFT                           0x1101
+#define AL_MONO32F_SOFT                          0x10010
+#define AL_STEREO8_SOFT                          0x1102
+#define AL_STEREO16_SOFT                         0x1103
+#define AL_STEREO32F_SOFT                        0x10011
+#define AL_QUAD8_SOFT                            0x1204
+#define AL_QUAD16_SOFT                           0x1205
+#define AL_QUAD32F_SOFT                          0x1206
+#define AL_REAR8_SOFT                            0x1207
+#define AL_REAR16_SOFT                           0x1208
+#define AL_REAR32F_SOFT                          0x1209
+#define AL_5POINT1_8_SOFT                        0x120A
+#define AL_5POINT1_16_SOFT                       0x120B
+#define AL_5POINT1_32F_SOFT                      0x120C
+#define AL_6POINT1_8_SOFT                        0x120D
+#define AL_6POINT1_16_SOFT                       0x120E
+#define AL_6POINT1_32F_SOFT                      0x120F
+#define AL_7POINT1_8_SOFT                        0x1210
+#define AL_7POINT1_16_SOFT                       0x1211
+#define AL_7POINT1_32F_SOFT                      0x1212
+#define AL_BFORMAT2D_8_SOFT                      0x20021
+#define AL_BFORMAT2D_16_SOFT                     0x20022
+#define AL_BFORMAT2D_32F_SOFT                    0x20023
+#define AL_BFORMAT3D_8_SOFT                      0x20031
+#define AL_BFORMAT3D_16_SOFT                     0x20032
+#define AL_BFORMAT3D_32F_SOFT                    0x20033
+
+/* Buffer attributes */
+#define AL_INTERNAL_FORMAT_SOFT                  0x2008
+#define AL_BYTE_LENGTH_SOFT                      0x2009
+#define AL_SAMPLE_LENGTH_SOFT                    0x200A
+#define AL_SEC_LENGTH_SOFT                       0x200B
+
+#if 0
+typedef void (AL_APIENTRY*LPALBUFFERSAMPLESSOFT)(ALuint,ALuint,ALenum,ALsizei,ALenum,ALenum,const ALvoid*);
+typedef void (AL_APIENTRY*LPALGETBUFFERSAMPLESSOFT)(ALuint,ALsizei,ALsizei,ALenum,ALenum,ALvoid*);
+typedef ALboolean (AL_APIENTRY*LPALISBUFFERFORMATSUPPORTEDSOFT)(ALenum);
+#ifdef AL_ALEXT_PROTOTYPES
+AL_API void AL_APIENTRY alBufferSamplesSOFT(ALuint buffer, ALuint samplerate, ALenum internalformat, ALsizei samples, ALenum channels, ALenum type, const ALvoid *data);
+AL_API void AL_APIENTRY alGetBufferSamplesSOFT(ALuint buffer, ALsizei offset, ALsizei samples, ALenum channels, ALenum type, ALvoid *data);
+AL_API ALboolean AL_APIENTRY alIsBufferFormatSupportedSOFT(ALenum format);
+#endif
+#endif
+#endif
+
+
+#ifdef __GNUC__
+/* Because of a long-standing deficiency in C, you're not allowed to implicitly
+ * cast a pointer-to-type-array to a pointer-to-const-type-array. For example,
+ *
+ * int (*ptr)[10];
+ * const int (*cptr)[10] = ptr;
+ *
+ * is not allowed and most compilers will generate noisy warnings about
+ * incompatible types, even though it just makes the array elements const.
+ * Clang will allow it if you make the array type a typedef, like this:
+ *
+ * typedef int int10[10];
+ * int10 *ptr;
+ * const int10 *cptr = ptr;
+ *
+ * however GCC does not and still issues the incompatible type warning. The
+ * "proper" way to fix it is to add an explicit cast for the constified type,
+ * but that removes the vast majority of otherwise useful type-checking you'd
+ * get, and runs the risk of improper casts if types are later changed. Leaving
+ * it non-const can also be an issue if you use it as a function parameter, and
+ * happen to have a const type as input (and also reduce the capabilities of
+ * the compiler to better optimize the function).
+ *
+ * So to work around the problem, we use a macro. The macro first assigns the
+ * incoming variable to the specified non-const type to ensure it's the correct
+ * type, then casts the variable as the desired constified type. Very ugly, but
+ * I'd rather not have hundreds of lines of warnings because I want to tell the
+ * compiler that some array(s) can't be changed by the code, or have lots of
+ * error-prone casts.
+ */
+#define SAFE_CONST(T, var) __extension__({                                    \
+    T _tmp = (var);                                                           \
+    (const T)_tmp;                                                            \
+})
+#else
+/* Non-GNU-compatible compilers have to use a straight cast with no extra
+ * checks, due to the lack of multi-statement expressions.
+ */
+#define SAFE_CONST(T, var) ((const T)(var))
+#endif
+
+
+typedef ALint64SOFT ALint64;
+typedef ALuint64SOFT ALuint64;
+
+#ifndef U64
+#if defined(_MSC_VER)
+#define U64(x) ((ALuint64)(x##ui64))
+#elif SIZEOF_LONG == 8
+#define U64(x) ((ALuint64)(x##ul))
+#elif SIZEOF_LONG_LONG == 8
+#define U64(x) ((ALuint64)(x##ull))
+#endif
+#endif
+
+#ifndef UINT64_MAX
+#define UINT64_MAX U64(18446744073709551615)
+#endif
+
+#ifndef UNUSED
+#if defined(__cplusplus)
+#define UNUSED(x)
+#elif defined(__GNUC__)
+#define UNUSED(x) UNUSED_##x __attribute__((unused))
+#elif defined(__LCLINT__)
+#define UNUSED(x) /*@unused@*/ x
+#else
+#define UNUSED(x) x
+#endif
+#endif
+
+#ifdef __GNUC__
+#define DECL_FORMAT(x, y, z) __attribute__((format(x, (y), (z))))
+#else
+#define DECL_FORMAT(x, y, z)
+#endif
+
+#if defined(__GNUC__) && defined(__i386__)
+/* force_align_arg_pointer is required for proper function arguments aligning
+ * when SSE code is used. Some systems (Windows, QNX) do not guarantee our
+ * thread functions will be properly aligned on the stack, even though GCC may
+ * generate code with the assumption that it is. */
+#define FORCE_ALIGN __attribute__((force_align_arg_pointer))
+#else
+#define FORCE_ALIGN
+#endif
+
+#ifdef HAVE_C99_VLA
+#define DECL_VLA(T, _name, _size)  T _name[(_size)]
+#else
+#define DECL_VLA(T, _name, _size)  T *_name = alloca((_size) * sizeof(T))
+#endif
+
+#ifndef PATH_MAX
+#ifdef MAX_PATH
+#define PATH_MAX MAX_PATH
+#else
+#define PATH_MAX 4096
+#endif
+#endif
+
+
+static const union {
+    ALuint u;
+    ALubyte b[sizeof(ALuint)];
+} EndianTest = { 1 };
+#define IS_LITTLE_ENDIAN (EndianTest.b[0] == 1)
+
+#define COUNTOF(x) (sizeof((x))/sizeof((x)[0]))
+
+
+#define DERIVE_FROM_TYPE(t)          t t##_parent
+#define STATIC_CAST(to, obj)         (&(obj)->to##_parent)
+#ifdef __GNUC__
+#define STATIC_UPCAST(to, from, obj) __extension__({                          \
+    static_assert(__builtin_types_compatible_p(from, __typeof(*(obj))),       \
+                  "Invalid upcast object from type");                         \
+    (to*)((char*)(obj) - offsetof(to, from##_parent));                        \
+})
+#else
+#define STATIC_UPCAST(to, from, obj) ((to*)((char*)(obj) - offsetof(to, from##_parent)))
+#endif
+
+#define DECLARE_FORWARD(T1, T2, rettype, func)                                \
+rettype T1##_##func(T1 *obj)                                                  \
+{ return T2##_##func(STATIC_CAST(T2, obj)); }
+
+#define DECLARE_FORWARD1(T1, T2, rettype, func, argtype1)                     \
+rettype T1##_##func(T1 *obj, argtype1 a)                                      \
+{ return T2##_##func(STATIC_CAST(T2, obj), a); }
+
+#define DECLARE_FORWARD2(T1, T2, rettype, func, argtype1, argtype2)           \
+rettype T1##_##func(T1 *obj, argtype1 a, argtype2 b)                          \
+{ return T2##_##func(STATIC_CAST(T2, obj), a, b); }
+
+#define DECLARE_FORWARD3(T1, T2, rettype, func, argtype1, argtype2, argtype3) \
+rettype T1##_##func(T1 *obj, argtype1 a, argtype2 b, argtype3 c)              \
+{ return T2##_##func(STATIC_CAST(T2, obj), a, b, c); }
+
+
+#define GET_VTABLE1(T1)     (&(T1##_vtable))
+#define GET_VTABLE2(T1, T2) (&(T1##_##T2##_vtable))
+
+#define SET_VTABLE1(T1, obj)     ((obj)->vtbl = GET_VTABLE1(T1))
+#define SET_VTABLE2(T1, T2, obj) (STATIC_CAST(T2, obj)->vtbl = GET_VTABLE2(T1, T2))
+
+#define DECLARE_THUNK(T1, T2, rettype, func)                                  \
+static rettype T1##_##T2##_##func(T2 *obj)                                    \
+{ return T1##_##func(STATIC_UPCAST(T1, T2, obj)); }
+
+#define DECLARE_THUNK1(T1, T2, rettype, func, argtype1)                       \
+static rettype T1##_##T2##_##func(T2 *obj, argtype1 a)                        \
+{ return T1##_##func(STATIC_UPCAST(T1, T2, obj), a); }
+
+#define DECLARE_THUNK2(T1, T2, rettype, func, argtype1, argtype2)             \
+static rettype T1##_##T2##_##func(T2 *obj, argtype1 a, argtype2 b)            \
+{ return T1##_##func(STATIC_UPCAST(T1, T2, obj), a, b); }
+
+#define DECLARE_THUNK3(T1, T2, rettype, func, argtype1, argtype2, argtype3)   \
+static rettype T1##_##T2##_##func(T2 *obj, argtype1 a, argtype2 b, argtype3 c) \
+{ return T1##_##func(STATIC_UPCAST(T1, T2, obj), a, b, c); }
+
+#define DECLARE_THUNK4(T1, T2, rettype, func, argtype1, argtype2, argtype3, argtype4) \
+static rettype T1##_##T2##_##func(T2 *obj, argtype1 a, argtype2 b, argtype3 c, argtype4 d) \
+{ return T1##_##func(STATIC_UPCAST(T1, T2, obj), a, b, c, d); }
+
+#define DECLARE_DEFAULT_ALLOCATORS(T)                                         \
+static void* T##_New(size_t size) { return al_malloc(16, size); }             \
+static void T##_Delete(void *ptr) { al_free(ptr); }
+
+/* Helper to extract an argument list for VCALL. Not used directly. */
+#define EXTRACT_VCALL_ARGS(...)  __VA_ARGS__))
+
+/* Call a "virtual" method on an object, with arguments. */
+#define V(obj, func)  ((obj)->vtbl->func((obj), EXTRACT_VCALL_ARGS
+/* Call a "virtual" method on an object, with no arguments. */
+#define V0(obj, func) ((obj)->vtbl->func((obj) EXTRACT_VCALL_ARGS
+
+#define DELETE_OBJ(obj) do {                                                  \
+    if((obj) != NULL)                                                         \
+    {                                                                         \
+        V0((obj),Destruct)();                                                 \
+        V0((obj),Delete)();                                                   \
+    }                                                                         \
+} while(0)
+
+
+#define EXTRACT_NEW_ARGS(...)  __VA_ARGS__);                                  \
+    }                                                                         \
+} while(0)
+
+#define NEW_OBJ(_res, T) do {                                                 \
+    _res = T##_New(sizeof(T));                                                \
+    if(_res)                                                                  \
+    {                                                                         \
+        memset(_res, 0, sizeof(T));                                           \
+        T##_Construct(_res, EXTRACT_NEW_ARGS
+#define NEW_OBJ0(_res, T) do {                                                \
+    _res = T##_New(sizeof(T));                                                \
+    if(_res)                                                                  \
+    {                                                                         \
+        memset(_res, 0, sizeof(T));                                           \
+        T##_Construct(_res EXTRACT_NEW_ARGS
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct Hrtf;
+
+
+#define DEFAULT_OUTPUT_RATE  (44100)
+#define MIN_OUTPUT_RATE      (8000)
+
+
+/* Find the next power-of-2 for non-power-of-2 numbers. */
+inline ALuint NextPowerOf2(ALuint value)
+{
+    if(value > 0)
+    {
+        value--;
+        value |= value>>1;
+        value |= value>>2;
+        value |= value>>4;
+        value |= value>>8;
+        value |= value>>16;
+    }
+    return value+1;
+}
+
+/* Fast float-to-int conversion. Assumes the FPU is already in round-to-zero
+ * mode. */
+inline ALint fastf2i(ALfloat f)
+{
+#ifdef HAVE_LRINTF
+    return lrintf(f);
+#elif defined(_MSC_VER) && defined(_M_IX86)
+    ALint i;
+    __asm fld f
+    __asm fistp i
+    return i;
+#else
+    return (ALint)f;
+#endif
+}
+
+/* Fast float-to-uint conversion. Assumes the FPU is already in round-to-zero
+ * mode. */
+inline ALuint fastf2u(ALfloat f)
+{ return fastf2i(f); }
+
+
+enum DevProbe {
+    ALL_DEVICE_PROBE,
+    CAPTURE_DEVICE_PROBE
+};
+
+typedef struct {
+    ALCenum (*OpenPlayback)(ALCdevice*, const ALCchar*);
+    void (*ClosePlayback)(ALCdevice*);
+    ALCboolean (*ResetPlayback)(ALCdevice*);
+    ALCboolean (*StartPlayback)(ALCdevice*);
+    void (*StopPlayback)(ALCdevice*);
+
+    ALCenum (*OpenCapture)(ALCdevice*, const ALCchar*);
+    void (*CloseCapture)(ALCdevice*);
+    void (*StartCapture)(ALCdevice*);
+    void (*StopCapture)(ALCdevice*);
+    ALCenum (*CaptureSamples)(ALCdevice*, void*, ALCuint);
+    ALCuint (*AvailableSamples)(ALCdevice*);
+} BackendFuncs;
+
+ALCboolean alc_sndio_init(BackendFuncs *func_list);
+void alc_sndio_deinit(void);
+void alc_sndio_probe(enum DevProbe type);
+ALCboolean alc_ca_init(BackendFuncs *func_list);
+void alc_ca_deinit(void);
+void alc_ca_probe(enum DevProbe type);
+ALCboolean alc_opensl_init(BackendFuncs *func_list);
+void alc_opensl_deinit(void);
+void alc_opensl_probe(enum DevProbe type);
+ALCboolean alc_qsa_init(BackendFuncs *func_list);
+void alc_qsa_deinit(void);
+void alc_qsa_probe(enum DevProbe type);
+
+struct ALCbackend;
+
+
+enum DistanceModel {
+    InverseDistanceClamped  = AL_INVERSE_DISTANCE_CLAMPED,
+    LinearDistanceClamped   = AL_LINEAR_DISTANCE_CLAMPED,
+    ExponentDistanceClamped = AL_EXPONENT_DISTANCE_CLAMPED,
+    InverseDistance  = AL_INVERSE_DISTANCE,
+    LinearDistance   = AL_LINEAR_DISTANCE,
+    ExponentDistance = AL_EXPONENT_DISTANCE,
+    DisableDistance  = AL_NONE,
+
+    DefaultDistanceModel = InverseDistanceClamped
+};
+
+enum Channel {
+    FrontLeft = 0,
+    FrontRight,
+    FrontCenter,
+    LFE,
+    BackLeft,
+    BackRight,
+    BackCenter,
+    SideLeft,
+    SideRight,
+
+    UpperFrontLeft,
+    UpperFrontRight,
+    UpperBackLeft,
+    UpperBackRight,
+    LowerFrontLeft,
+    LowerFrontRight,
+    LowerBackLeft,
+    LowerBackRight,
+
+    Aux0,
+    Aux1,
+    Aux2,
+    Aux3,
+    Aux4,
+    Aux5,
+    Aux6,
+    Aux7,
+    Aux8,
+    Aux9,
+    Aux10,
+    Aux11,
+    Aux12,
+    Aux13,
+    Aux14,
+    Aux15,
+
+    InvalidChannel
+};
+
+
+/* Device formats */
+enum DevFmtType {
+    DevFmtByte   = ALC_BYTE_SOFT,
+    DevFmtUByte  = ALC_UNSIGNED_BYTE_SOFT,
+    DevFmtShort  = ALC_SHORT_SOFT,
+    DevFmtUShort = ALC_UNSIGNED_SHORT_SOFT,
+    DevFmtInt    = ALC_INT_SOFT,
+    DevFmtUInt   = ALC_UNSIGNED_INT_SOFT,
+    DevFmtFloat  = ALC_FLOAT_SOFT,
+
+    DevFmtTypeDefault = DevFmtFloat
+};
+enum DevFmtChannels {
+    DevFmtMono   = ALC_MONO_SOFT,
+    DevFmtStereo = ALC_STEREO_SOFT,
+    DevFmtQuad   = ALC_QUAD_SOFT,
+    DevFmtX51    = ALC_5POINT1_SOFT,
+    DevFmtX61    = ALC_6POINT1_SOFT,
+    DevFmtX71    = ALC_7POINT1_SOFT,
+
+    /* Similar to 5.1, except using rear channels instead of sides */
+    DevFmtX51Rear = 0x80000000,
+
+    /* Ambisonic formats should be kept together */
+    DevFmtAmbi1,
+    DevFmtAmbi2,
+    DevFmtAmbi3,
+
+    DevFmtChannelsDefault = DevFmtStereo
+};
+#define MAX_OUTPUT_CHANNELS  (16)
+
+ALuint BytesFromDevFmt(enum DevFmtType type);
+ALuint ChannelsFromDevFmt(enum DevFmtChannels chans);
+inline ALuint FrameSizeFromDevFmt(enum DevFmtChannels chans, enum DevFmtType type)
+{
+    return ChannelsFromDevFmt(chans) * BytesFromDevFmt(type);
+}
+
+enum AmbiFormat {
+    AmbiFormat_FuMa,     /* FuMa channel order and normalization */
+    AmbiFormat_ACN_SN3D, /* ACN channel order and SN3D normalization */
+    AmbiFormat_ACN_N3D,  /* ACN channel order and N3D normalization */
+
+    AmbiFormat_Default = AmbiFormat_ACN_SN3D
+};
+
+
+extern const struct EffectList {
+    const char *name;
+    int type;
+    const char *ename;
+    ALenum val;
+} EffectList[];
+
+
+enum DeviceType {
+    Playback,
+    Capture,
+    Loopback
+};
+
+
+enum RenderMode {
+    NormalRender,
+    StereoPair,
+    HrtfRender
+};
+
+
+/* The maximum number of Ambisonics coefficients. For a given order (o), the
+ * size needed will be (o+1)**2, thus zero-order has 1, first-order has 4,
+ * second-order has 9, third-order has 16, and fourth-order has 25.
+ */
+#define MAX_AMBI_ORDER  3
+#define MAX_AMBI_COEFFS ((MAX_AMBI_ORDER+1) * (MAX_AMBI_ORDER+1))
+
+/* A bitmask of ambisonic channels with height information. If none of these
+ * channels are used/needed, there's no height (e.g. with most surround sound
+ * speaker setups). This only specifies up to 4th order, which is the highest
+ * order a 32-bit mask value can specify (a 64-bit mask could handle up to 7th
+ * order). This is ACN ordering, with bit 0 being ACN 0, etc.
+ */
+#define AMBI_PERIPHONIC_MASK (0xfe7ce4)
+
+/* The maximum number of Ambisonic coefficients for 2D (non-periphonic)
+ * representation. This is 2 per each order above zero-order, plus 1 for zero-
+ * order. Or simply, o*2 + 1.
+ */
+#define MAX_AMBI2D_COEFFS (MAX_AMBI_ORDER*2 + 1)
+
+
+typedef ALfloat ChannelConfig[MAX_AMBI_COEFFS];
+typedef struct BFChannelConfig {
+    ALfloat Scale;
+    ALuint Index;
+} BFChannelConfig;
+
+typedef union AmbiConfig {
+    /* Ambisonic coefficients for mixing to the dry buffer. */
+    ChannelConfig Coeffs[MAX_OUTPUT_CHANNELS];
+    /* Coefficient channel mapping for mixing to the dry buffer. */
+    BFChannelConfig Map[MAX_OUTPUT_CHANNELS];
+} AmbiConfig;
+
+
+#define HRTF_HISTORY_BITS   (6)
+#define HRTF_HISTORY_LENGTH (1<<HRTF_HISTORY_BITS)
+#define HRTF_HISTORY_MASK   (HRTF_HISTORY_LENGTH-1)
+
+typedef struct HrtfState {
+    alignas(16) ALfloat History[HRTF_HISTORY_LENGTH];
+    alignas(16) ALfloat Values[HRIR_LENGTH][2];
+} HrtfState;
+
+typedef struct HrtfParams {
+    alignas(16) ALfloat Coeffs[HRIR_LENGTH][2];
+    ALuint Delay[2];
+} HrtfParams;
+
+
+/* Size for temporary storage of buffer data, in ALfloats. Larger values need
+ * more memory, while smaller values may need more iterations. The value needs
+ * to be a sensible size, however, as it constrains the max stepping value used
+ * for mixing, as well as the maximum number of samples per mixing iteration.
+ */
+#define BUFFERSIZE (2048u)
+
+struct ALCdevice_struct
+{
+    RefCount ref;
+
+    ALCboolean Connected;
+    enum DeviceType Type;
+
+    ALuint Frequency;
+    ALuint UpdateSize;
+    ALuint NumUpdates;
+    enum DevFmtChannels FmtChans;
+    enum DevFmtType     FmtType;
+    ALboolean IsHeadphones;
+    /* For DevFmtAmbi* output only, specifies the channel order and
+     * normalization.
+     */
+    enum AmbiFormat AmbiFmt;
+
+    al_string DeviceName;
+
+    ATOMIC(ALCenum) LastError;
+
+    // Maximum number of sources that can be created
+    ALuint SourcesMax;
+    // Maximum number of slots that can be created
+    ALuint AuxiliaryEffectSlotMax;
+
+    ALCuint NumMonoSources;
+    ALCuint NumStereoSources;
+    ALuint  NumAuxSends;
+
+    // Map of Buffers for this device
+    UIntMap BufferMap;
+
+    // Map of Effects for this device
+    UIntMap EffectMap;
+
+    // Map of Filters for this device
+    UIntMap FilterMap;
+
+    /* HRTF filter tables */
+    struct {
+        vector_HrtfEntry List;
+        al_string Name;
+        ALCenum Status;
+        const struct Hrtf *Handle;
+
+        /* HRTF filter state for dry buffer content */
+        alignas(16) ALfloat Values[4][HRIR_LENGTH][2];
+        alignas(16) ALfloat Coeffs[4][HRIR_LENGTH][2];
+        ALuint Offset;
+        ALuint IrSize;
+    } Hrtf;
+
+    /* UHJ encoder state */
+    struct Uhj2Encoder *Uhj_Encoder;
+
+    /* High quality Ambisonic decoder */
+    struct BFormatDec *AmbiDecoder;
+
+    /* Stereo-to-binaural filter */
+    struct bs2b *Bs2b;
+
+    /* First-order ambisonic upsampler for higher-order output */
+    struct AmbiUpsampler *AmbiUp;
+
+    /* Rendering mode. */
+    enum RenderMode Render_Mode;
+
+    // Device flags
+    ALuint Flags;
+
+    ALuint64 ClockBase;
+    ALuint SamplesDone;
+
+    /* Temp storage used for each source when mixing. */
+    alignas(16) ALfloat SourceData[BUFFERSIZE];
+    alignas(16) ALfloat ResampledData[BUFFERSIZE];
+    alignas(16) ALfloat FilteredData[BUFFERSIZE];
+
+    /* The "dry" path corresponds to the main output. */
+    struct {
+        AmbiConfig Ambi;
+        /* Number of coefficients in each Ambi.Coeffs to mix together (4 for
+         * first-order, 9 for second-order, etc). If the count is 0, Ambi.Map
+         * is used instead to map each output to a coefficient index.
+         */
+        ALuint CoeffCount;
+
+        ALfloat (*Buffer)[BUFFERSIZE];
+        ALuint NumChannels;
+    } Dry;
+
+    /* First-order ambisonics output, to be upsampled to the dry buffer if different. */
+    struct {
+        AmbiConfig Ambi;
+        /* Will only be 4 or 0. */
+        ALuint CoeffCount;
+
+        ALfloat (*Buffer)[BUFFERSIZE];
+        ALuint NumChannels;
+    } FOAOut;
+
+    /* "Real" output, which will be written to the device buffer. May alias the
+     * dry buffer.
+     */
+    struct {
+        enum Channel ChannelName[MAX_OUTPUT_CHANNELS];
+
+        ALfloat (*Buffer)[BUFFERSIZE];
+        ALuint NumChannels;
+    } RealOut;
+
+    /* Running count of the mixer invocations, in 31.1 fixed point. This
+     * actually increments *twice* when mixing, first at the start and then at
+     * the end, so the bottom bit indicates if the device is currently mixing
+     * and the upper bits indicates how many mixes have been done.
+     */
+    RefCount MixCount;
+
+    /* Default effect slot */
+    struct ALeffectslot *DefaultSlot;
+
+    // Contexts created on this device
+    ATOMIC(ALCcontext*) ContextList;
+
+    almtx_t BackendLock;
+    struct ALCbackend *Backend;
+
+    void *ExtraData; // For the backend's use
+
+    ALCdevice *volatile next;
+
+    /* Memory space used by the default slot (Playback devices only) */
+    alignas(16) ALCbyte _slot_mem[];
+};
+
+// Frequency was requested by the app or config file
+#define DEVICE_FREQUENCY_REQUEST                 (1u<<1)
+// Channel configuration was requested by the config file
+#define DEVICE_CHANNELS_REQUEST                  (1u<<2)
+// Sample type was requested by the config file
+#define DEVICE_SAMPLE_TYPE_REQUEST               (1u<<3)
+
+// Specifies if the DSP is paused at user request
+#define DEVICE_PAUSED                            (1u<<30)
+
+// Specifies if the device is currently running
+#define DEVICE_RUNNING                           (1u<<31)
+
+
+/* Nanosecond resolution for the device clock time. */
+#define DEVICE_CLOCK_RES  U64(1000000000)
+
+
+/* Must be less than 15 characters (16 including terminating null) for
+ * compatibility with pthread_setname_np limitations. */
+#define MIXER_THREAD_NAME "alsoft-mixer"
+
+#define RECORD_THREAD_NAME "alsoft-record"
+
+
+struct ALCcontext_struct {
+    RefCount ref;
+
+    struct ALlistener *Listener;
+
+    UIntMap SourceMap;
+    UIntMap EffectSlotMap;
+
+    ATOMIC(ALenum) LastError;
+
+    enum DistanceModel DistanceModel;
+    ALboolean SourceDistanceModel;
+
+    ALfloat DopplerFactor;
+    ALfloat DopplerVelocity;
+    ALfloat SpeedOfSound;
+    ATOMIC(ALenum) DeferUpdates;
+
+    RWLock PropLock;
+
+    /* Counter for the pre-mixing updates, in 31.1 fixed point (lowest bit
+     * indicates if updates are currently happening).
+     */
+    RefCount UpdateCount;
+    ATOMIC(ALenum) HoldUpdates;
+
+    ALfloat GainBoost;
+
+    struct ALvoice *Voices;
+    ALsizei VoiceCount;
+    ALsizei MaxVoices;
+
+    ATOMIC(struct ALeffectslot*) ActiveAuxSlotList;
+
+    ALCdevice  *Device;
+    const ALCchar *ExtensionList;
+
+    ALCcontext *volatile next;
+
+    /* Memory space used by the listener */
+    alignas(16) ALCbyte _listener_mem[];
+};
+
+ALCcontext *GetContextRef(void);
+
+void ALCcontext_IncRef(ALCcontext *context);
+void ALCcontext_DecRef(ALCcontext *context);
+
+void AppendAllDevicesList(const ALCchar *name);
+void AppendCaptureDeviceList(const ALCchar *name);
+
+void ALCdevice_Lock(ALCdevice *device);
+void ALCdevice_Unlock(ALCdevice *device);
+
+void ALCcontext_DeferUpdates(ALCcontext *context, ALenum type);
+void ALCcontext_ProcessUpdates(ALCcontext *context);
+
+inline void LockContext(ALCcontext *context)
+{ ALCdevice_Lock(context->Device); }
+
+inline void UnlockContext(ALCcontext *context)
+{ ALCdevice_Unlock(context->Device); }
+
+enum {
+    DeferOff = AL_FALSE,
+    DeferAll,
+    DeferAllowPlay
+};
+
+
+typedef struct {
+#ifdef HAVE_FENV_H
+    DERIVE_FROM_TYPE(fenv_t);
+#else
+    int state;
+#endif
+#ifdef HAVE_SSE
+    int sse_state;
+#endif
+} FPUCtl;
+void SetMixerFPUMode(FPUCtl *ctl);
+void RestoreFPUMode(const FPUCtl *ctl);
+
+
+typedef struct ll_ringbuffer ll_ringbuffer_t;
+typedef struct ll_ringbuffer_data {
+    char *buf;
+    size_t len;
+} ll_ringbuffer_data_t;
+ll_ringbuffer_t *ll_ringbuffer_create(size_t sz, size_t elem_sz);
+void ll_ringbuffer_free(ll_ringbuffer_t *rb);
+void ll_ringbuffer_get_read_vector(const ll_ringbuffer_t *rb, ll_ringbuffer_data_t *vec);
+void ll_ringbuffer_get_write_vector(const ll_ringbuffer_t *rb, ll_ringbuffer_data_t *vec);
+size_t ll_ringbuffer_read(ll_ringbuffer_t *rb, char *dest, size_t cnt);
+size_t ll_ringbuffer_peek(ll_ringbuffer_t *rb, char *dest, size_t cnt);
+void ll_ringbuffer_read_advance(ll_ringbuffer_t *rb, size_t cnt);
+size_t ll_ringbuffer_read_space(const ll_ringbuffer_t *rb);
+int ll_ringbuffer_mlock(ll_ringbuffer_t *rb);
+void ll_ringbuffer_reset(ll_ringbuffer_t *rb);
+size_t ll_ringbuffer_write(ll_ringbuffer_t *rb, const char *src, size_t cnt);
+void ll_ringbuffer_write_advance(ll_ringbuffer_t *rb, size_t cnt);
+size_t ll_ringbuffer_write_space(const ll_ringbuffer_t *rb);
+
+void ReadALConfig(void);
+void FreeALConfig(void);
+int ConfigValueExists(const char *devName, const char *blockName, const char *keyName);
+const char *GetConfigValue(const char *devName, const char *blockName, const char *keyName, const char *def);
+int GetConfigValueBool(const char *devName, const char *blockName, const char *keyName, int def);
+int ConfigValueStr(const char *devName, const char *blockName, const char *keyName, const char **ret);
+int ConfigValueInt(const char *devName, const char *blockName, const char *keyName, int *ret);
+int ConfigValueUInt(const char *devName, const char *blockName, const char *keyName, unsigned int *ret);
+int ConfigValueFloat(const char *devName, const char *blockName, const char *keyName, float *ret);
+int ConfigValueBool(const char *devName, const char *blockName, const char *keyName, int *ret);
+
+void SetRTPriority(void);
+
+void SetDefaultChannelOrder(ALCdevice *device);
+void SetDefaultWFXChannelOrder(ALCdevice *device);
+
+const ALCchar *DevFmtTypeString(enum DevFmtType type);
+const ALCchar *DevFmtChannelsString(enum DevFmtChannels chans);
+
+/**
+ * GetChannelIdxByName
+ *
+ * Returns the index for the given channel name (e.g. FrontCenter), or -1 if it
+ * doesn't exist.
+ */
+inline ALint GetChannelIndex(const enum Channel names[MAX_OUTPUT_CHANNELS], enum Channel chan)
+{
+    ALint i;
+    for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
+    {
+        if(names[i] == chan)
+            return i;
+    }
+    return -1;
+}
+#define GetChannelIdxByName(x, c) GetChannelIndex((x).ChannelName, (c))
+
+extern FILE *LogFile;
+
+#if defined(__GNUC__) && !defined(_WIN32) && !defined(IN_IDE_PARSER)
+#define AL_PRINT(T, MSG, ...) fprintf(LogFile, "AL lib: %s %s: "MSG, T, __FUNCTION__ , ## __VA_ARGS__)
+#else
+void al_print(const char *type, const char *func, const char *fmt, ...) DECL_FORMAT(printf, 3,4);
+#define AL_PRINT(T, ...) al_print((T), __FUNCTION__, __VA_ARGS__)
+#endif
+
+enum LogLevel {
+    NoLog,
+    LogError,
+    LogWarning,
+    LogTrace,
+    LogRef
+};
+extern enum LogLevel LogLevel;
+
+#define TRACEREF(...) do {                                                    \
+    if(LogLevel >= LogRef)                                                    \
+        AL_PRINT("(--)", __VA_ARGS__);                                        \
+} while(0)
+
+#define TRACE(...) do {                                                       \
+    if(LogLevel >= LogTrace)                                                  \
+        AL_PRINT("(II)", __VA_ARGS__);                                        \
+} while(0)
+
+#define WARN(...) do {                                                        \
+    if(LogLevel >= LogWarning)                                                \
+        AL_PRINT("(WW)", __VA_ARGS__);                                        \
+} while(0)
+
+#define ERR(...) do {                                                         \
+    if(LogLevel >= LogError)                                                  \
+        AL_PRINT("(EE)", __VA_ARGS__);                                        \
+} while(0)
+
+
+extern ALint RTPrioLevel;
+
+
+extern ALuint CPUCapFlags;
+enum {
+    CPU_CAP_SSE    = 1<<0,
+    CPU_CAP_SSE2   = 1<<1,
+    CPU_CAP_SSE3   = 1<<2,
+    CPU_CAP_SSE4_1 = 1<<3,
+    CPU_CAP_NEON   = 1<<4,
+};
+
+void FillCPUCaps(ALuint capfilter);
+
+vector_al_string SearchDataFiles(const char *match, const char *subdir);
+
+/* Small hack to use a pointer-to-array type as a normal argument type.
+ * Shouldn't be used directly. */
+typedef ALfloat ALfloatBUFFERSIZE[BUFFERSIZE];
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 231 - 0
Engine/lib/openal-soft/OpenAL32/Include/alSource.h

@@ -0,0 +1,231 @@
+#ifndef _AL_SOURCE_H_
+#define _AL_SOURCE_H_
+
+#define MAX_SENDS                 4
+
+#include "alMain.h"
+#include "alu.h"
+#include "hrtf.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ALbuffer;
+struct ALsource;
+struct ALsourceProps;
+
+
+typedef struct ALbufferlistitem {
+    struct ALbuffer *buffer;
+    struct ALbufferlistitem *volatile next;
+} ALbufferlistitem;
+
+
+struct ALsourceProps {
+    ATOMIC(ALfloat)   Pitch;
+    ATOMIC(ALfloat)   Gain;
+    ATOMIC(ALfloat)   OuterGain;
+    ATOMIC(ALfloat)   MinGain;
+    ATOMIC(ALfloat)   MaxGain;
+    ATOMIC(ALfloat)   InnerAngle;
+    ATOMIC(ALfloat)   OuterAngle;
+    ATOMIC(ALfloat)   RefDistance;
+    ATOMIC(ALfloat)   MaxDistance;
+    ATOMIC(ALfloat)   RollOffFactor;
+    ATOMIC(ALfloat)   Position[3];
+    ATOMIC(ALfloat)   Velocity[3];
+    ATOMIC(ALfloat)   Direction[3];
+    ATOMIC(ALfloat)   Orientation[2][3];
+    ATOMIC(ALboolean) HeadRelative;
+    ATOMIC(enum DistanceModel) DistanceModel;
+    ATOMIC(ALboolean) DirectChannels;
+
+    ATOMIC(ALboolean) DryGainHFAuto;
+    ATOMIC(ALboolean) WetGainAuto;
+    ATOMIC(ALboolean) WetGainHFAuto;
+    ATOMIC(ALfloat)   OuterGainHF;
+
+    ATOMIC(ALfloat) AirAbsorptionFactor;
+    ATOMIC(ALfloat) RoomRolloffFactor;
+    ATOMIC(ALfloat) DopplerFactor;
+
+    ATOMIC(ALfloat) StereoPan[2];
+
+    ATOMIC(ALfloat) Radius;
+
+    /** Direct filter and auxiliary send info. */
+    struct {
+        ATOMIC(ALfloat) Gain;
+        ATOMIC(ALfloat) GainHF;
+        ATOMIC(ALfloat) HFReference;
+        ATOMIC(ALfloat) GainLF;
+        ATOMIC(ALfloat) LFReference;
+    } Direct;
+    struct {
+        ATOMIC(struct ALeffectslot*) Slot;
+        ATOMIC(ALfloat) Gain;
+        ATOMIC(ALfloat) GainHF;
+        ATOMIC(ALfloat) HFReference;
+        ATOMIC(ALfloat) GainLF;
+        ATOMIC(ALfloat) LFReference;
+    } Send[MAX_SENDS];
+
+    ATOMIC(struct ALsourceProps*) next;
+};
+
+
+typedef struct ALvoice {
+    struct ALsourceProps Props;
+
+    struct ALsource *volatile Source;
+
+    /** Current target parameters used for mixing. */
+    ALint Step;
+
+    /* If not 'moving', gain/coefficients are set directly without fading. */
+    ALboolean Moving;
+
+    ALboolean IsHrtf;
+
+    ALuint Offset; /* Number of output samples mixed since starting. */
+
+    alignas(16) ALfloat PrevSamples[MAX_INPUT_CHANNELS][MAX_PRE_SAMPLES];
+
+    BsincState SincState;
+
+    struct {
+        ALfloat (*Buffer)[BUFFERSIZE];
+        ALuint Channels;
+    } DirectOut;
+
+    struct {
+        ALfloat (*Buffer)[BUFFERSIZE];
+        ALuint Channels;
+    } SendOut[MAX_SENDS];
+
+    struct {
+        DirectParams Direct;
+        SendParams Send[MAX_SENDS];
+    } Chan[MAX_INPUT_CHANNELS];
+} ALvoice;
+
+
+typedef struct ALsource {
+    /** Source properties. */
+    ALfloat   Pitch;
+    ALfloat   Gain;
+    ALfloat   OuterGain;
+    ALfloat   MinGain;
+    ALfloat   MaxGain;
+    ALfloat   InnerAngle;
+    ALfloat   OuterAngle;
+    ALfloat   RefDistance;
+    ALfloat   MaxDistance;
+    ALfloat   RollOffFactor;
+    ALfloat   Position[3];
+    ALfloat   Velocity[3];
+    ALfloat   Direction[3];
+    ALfloat   Orientation[2][3];
+    ALboolean HeadRelative;
+    enum DistanceModel DistanceModel;
+    ALboolean DirectChannels;
+
+    ALboolean DryGainHFAuto;
+    ALboolean WetGainAuto;
+    ALboolean WetGainHFAuto;
+    ALfloat   OuterGainHF;
+
+    ALfloat AirAbsorptionFactor;
+    ALfloat RoomRolloffFactor;
+    ALfloat DopplerFactor;
+
+    /* NOTE: Stereo pan angles are specified in radians, counter-clockwise
+     * rather than clockwise.
+     */
+    ALfloat StereoPan[2];
+
+    ALfloat Radius;
+
+    /** Direct filter and auxiliary send info. */
+    struct {
+        ALfloat Gain;
+        ALfloat GainHF;
+        ALfloat HFReference;
+        ALfloat GainLF;
+        ALfloat LFReference;
+    } Direct;
+    struct {
+        struct ALeffectslot *Slot;
+        ALfloat Gain;
+        ALfloat GainHF;
+        ALfloat HFReference;
+        ALfloat GainLF;
+        ALfloat LFReference;
+    } Send[MAX_SENDS];
+
+    /**
+     * Last user-specified offset, and the offset type (bytes, samples, or
+     * seconds).
+     */
+    ALdouble Offset;
+    ALenum   OffsetType;
+
+    /** Source type (static, streaming, or undetermined) */
+    ALint SourceType;
+
+    /** Source state (initial, playing, paused, or stopped) */
+    ALenum state;
+    ALenum new_state;
+
+    /** Source Buffer Queue info. */
+    RWLock queue_lock;
+    ATOMIC(ALbufferlistitem*) queue;
+    ATOMIC(ALbufferlistitem*) current_buffer;
+
+    /**
+     * Source offset in samples, relative to the currently playing buffer, NOT
+     * the whole queue, and the fractional (fixed-point) offset to the next
+     * sample.
+     */
+    ATOMIC(ALuint) position;
+    ATOMIC(ALuint) position_fraction;
+
+    ATOMIC(ALboolean) looping;
+
+    /** Current buffer sample info. */
+    ALuint NumChannels;
+    ALuint SampleSize;
+
+    ATOMIC(struct ALsourceProps*) Update;
+    ATOMIC(struct ALsourceProps*) FreeList;
+
+    /** Self ID */
+    ALuint id;
+} ALsource;
+
+inline void LockSourcesRead(ALCcontext *context)
+{ LockUIntMapRead(&context->SourceMap); }
+inline void UnlockSourcesRead(ALCcontext *context)
+{ UnlockUIntMapRead(&context->SourceMap); }
+inline void LockSourcesWrite(ALCcontext *context)
+{ LockUIntMapWrite(&context->SourceMap); }
+inline void UnlockSourcesWrite(ALCcontext *context)
+{ UnlockUIntMapWrite(&context->SourceMap); }
+
+inline struct ALsource *LookupSource(ALCcontext *context, ALuint id)
+{ return (struct ALsource*)LookupUIntMapKeyNoLock(&context->SourceMap, id); }
+inline struct ALsource *RemoveSource(ALCcontext *context, ALuint id)
+{ return (struct ALsource*)RemoveUIntMapKeyNoLock(&context->SourceMap, id); }
+
+void UpdateAllSourceProps(ALCcontext *context);
+ALvoid SetSourceState(ALsource *Source, ALCcontext *Context, ALenum state);
+ALboolean ApplyOffset(ALsource *Source);
+
+ALvoid ReleaseALSources(ALCcontext *Context);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 20 - 0
Engine/lib/openal-soft/OpenAL32/Include/alThunk.h

@@ -0,0 +1,20 @@
+#ifndef ALTHUNK_H
+#define ALTHUNK_H
+
+#include "alMain.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void ThunkInit(void);
+void ThunkExit(void);
+ALenum NewThunkEntry(ALuint *index);
+void FreeThunkEntry(ALuint index);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif //ALTHUNK_H
+

+ 363 - 0
Engine/lib/openal-soft/OpenAL32/Include/alu.h

@@ -0,0 +1,363 @@
+#ifndef _ALU_H_
+#define _ALU_H_
+
+#include <limits.h>
+#include <math.h>
+#ifdef HAVE_FLOAT_H
+#include <float.h>
+#endif
+#ifdef HAVE_IEEEFP_H
+#include <ieeefp.h>
+#endif
+
+#include "alMain.h"
+#include "alBuffer.h"
+#include "alFilter.h"
+#include "alAuxEffectSlot.h"
+
+#include "hrtf.h"
+#include "align.h"
+#include "math_defs.h"
+
+
+#define MAX_PITCH  (255)
+
+/* Maximum number of buffer samples before the current pos needed for resampling. */
+#define MAX_PRE_SAMPLES 12
+
+/* Maximum number of buffer samples after the current pos needed for resampling. */
+#define MAX_POST_SAMPLES 12
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ALsource;
+struct ALsourceProps;
+struct ALvoice;
+struct ALeffectslot;
+struct ALbuffer;
+
+
+/* The number of distinct scale and phase intervals within the filter table. */
+#define BSINC_SCALE_BITS  4
+#define BSINC_SCALE_COUNT (1<<BSINC_SCALE_BITS)
+#define BSINC_PHASE_BITS  4
+#define BSINC_PHASE_COUNT (1<<BSINC_PHASE_BITS)
+
+/* Interpolator state.  Kind of a misnomer since the interpolator itself is
+ * stateless.  This just keeps it from having to recompute scale-related
+ * mappings for every sample.
+ */
+typedef struct BsincState {
+    ALfloat sf; /* Scale interpolation factor. */
+    ALuint m;   /* Coefficient count. */
+    ALint l;    /* Left coefficient offset. */
+    struct {
+        const ALfloat *filter;   /* Filter coefficients. */
+        const ALfloat *scDelta;  /* Scale deltas. */
+        const ALfloat *phDelta;  /* Phase deltas. */
+        const ALfloat *spDelta;  /* Scale-phase deltas. */
+    } coeffs[BSINC_PHASE_COUNT];
+} BsincState;
+
+
+typedef union aluVector {
+    alignas(16) ALfloat v[4];
+} aluVector;
+
+inline void aluVectorSet(aluVector *vector, ALfloat x, ALfloat y, ALfloat z, ALfloat w)
+{
+    vector->v[0] = x;
+    vector->v[1] = y;
+    vector->v[2] = z;
+    vector->v[3] = w;
+}
+
+
+typedef union aluMatrixf {
+    alignas(16) ALfloat m[4][4];
+} aluMatrixf;
+extern const aluMatrixf IdentityMatrixf;
+
+inline void aluMatrixfSetRow(aluMatrixf *matrix, ALuint row,
+                             ALfloat m0, ALfloat m1, ALfloat m2, ALfloat m3)
+{
+    matrix->m[row][0] = m0;
+    matrix->m[row][1] = m1;
+    matrix->m[row][2] = m2;
+    matrix->m[row][3] = m3;
+}
+
+inline void aluMatrixfSet(aluMatrixf *matrix, ALfloat m00, ALfloat m01, ALfloat m02, ALfloat m03,
+                                              ALfloat m10, ALfloat m11, ALfloat m12, ALfloat m13,
+                                              ALfloat m20, ALfloat m21, ALfloat m22, ALfloat m23,
+                                              ALfloat m30, ALfloat m31, ALfloat m32, ALfloat m33)
+{
+    aluMatrixfSetRow(matrix, 0, m00, m01, m02, m03);
+    aluMatrixfSetRow(matrix, 1, m10, m11, m12, m13);
+    aluMatrixfSetRow(matrix, 2, m20, m21, m22, m23);
+    aluMatrixfSetRow(matrix, 3, m30, m31, m32, m33);
+}
+
+
+enum ActiveFilters {
+    AF_None = 0,
+    AF_LowPass = 1,
+    AF_HighPass = 2,
+    AF_BandPass = AF_LowPass | AF_HighPass
+};
+
+
+typedef struct MixHrtfParams {
+    const HrtfParams *Target;
+    HrtfParams *Current;
+    struct {
+        alignas(16) ALfloat Coeffs[HRIR_LENGTH][2];
+        ALint Delay[2];
+    } Steps;
+} MixHrtfParams;
+
+typedef struct DirectParams {
+    enum ActiveFilters FilterType;
+    ALfilterState LowPass;
+    ALfilterState HighPass;
+
+    struct {
+        HrtfParams Current;
+        HrtfParams Target;
+        HrtfState State;
+    } Hrtf;
+
+    struct {
+        ALfloat Current[MAX_OUTPUT_CHANNELS];
+        ALfloat Target[MAX_OUTPUT_CHANNELS];
+    } Gains;
+} DirectParams;
+
+typedef struct SendParams {
+    enum ActiveFilters FilterType;
+    ALfilterState LowPass;
+    ALfilterState HighPass;
+
+    struct {
+        ALfloat Current[MAX_OUTPUT_CHANNELS];
+        ALfloat Target[MAX_OUTPUT_CHANNELS];
+    } Gains;
+} SendParams;
+
+
+typedef const ALfloat* (*ResamplerFunc)(const BsincState *state,
+    const ALfloat *restrict src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen
+);
+
+typedef void (*MixerFunc)(const ALfloat *data, ALuint OutChans,
+                          ALfloat (*restrict OutBuffer)[BUFFERSIZE], ALfloat *CurrentGains,
+                          const ALfloat *TargetGains, ALuint Counter, ALuint OutPos,
+                          ALuint BufferSize);
+typedef void (*RowMixerFunc)(ALfloat *OutBuffer, const ALfloat *gains,
+                             const ALfloat (*restrict data)[BUFFERSIZE], ALuint InChans,
+                             ALuint InPos, ALuint BufferSize);
+typedef void (*HrtfMixerFunc)(ALfloat (*restrict OutBuffer)[BUFFERSIZE], ALuint lidx, ALuint ridx,
+                              const ALfloat *data, ALuint Counter, ALuint Offset, ALuint OutPos,
+                              const ALuint IrSize, const MixHrtfParams *hrtfparams,
+                              HrtfState *hrtfstate, ALuint BufferSize);
+typedef void (*HrtfDirectMixerFunc)(ALfloat (*restrict OutBuffer)[BUFFERSIZE],
+                                    ALuint lidx, ALuint ridx, const ALfloat *data, ALuint Offset,
+                                    const ALuint IrSize, ALfloat (*restrict Coeffs)[2],
+                                    ALfloat (*restrict Values)[2], ALuint BufferSize);
+
+
+#define GAIN_MIX_MAX  (16.0f) /* +24dB */
+
+#define GAIN_SILENCE_THRESHOLD  (0.00001f) /* -100dB */
+
+#define SPEEDOFSOUNDMETRESPERSEC  (343.3f)
+#define AIRABSORBGAINHF           (0.99426f) /* -0.05dB */
+
+#define FRACTIONBITS (12)
+#define FRACTIONONE  (1<<FRACTIONBITS)
+#define FRACTIONMASK (FRACTIONONE-1)
+
+
+inline ALfloat minf(ALfloat a, ALfloat b)
+{ return ((a > b) ? b : a); }
+inline ALfloat maxf(ALfloat a, ALfloat b)
+{ return ((a > b) ? a : b); }
+inline ALfloat clampf(ALfloat val, ALfloat min, ALfloat max)
+{ return minf(max, maxf(min, val)); }
+
+inline ALdouble mind(ALdouble a, ALdouble b)
+{ return ((a > b) ? b : a); }
+inline ALdouble maxd(ALdouble a, ALdouble b)
+{ return ((a > b) ? a : b); }
+inline ALdouble clampd(ALdouble val, ALdouble min, ALdouble max)
+{ return mind(max, maxd(min, val)); }
+
+inline ALuint minu(ALuint a, ALuint b)
+{ return ((a > b) ? b : a); }
+inline ALuint maxu(ALuint a, ALuint b)
+{ return ((a > b) ? a : b); }
+inline ALuint clampu(ALuint val, ALuint min, ALuint max)
+{ return minu(max, maxu(min, val)); }
+
+inline ALint mini(ALint a, ALint b)
+{ return ((a > b) ? b : a); }
+inline ALint maxi(ALint a, ALint b)
+{ return ((a > b) ? a : b); }
+inline ALint clampi(ALint val, ALint min, ALint max)
+{ return mini(max, maxi(min, val)); }
+
+inline ALint64 mini64(ALint64 a, ALint64 b)
+{ return ((a > b) ? b : a); }
+inline ALint64 maxi64(ALint64 a, ALint64 b)
+{ return ((a > b) ? a : b); }
+inline ALint64 clampi64(ALint64 val, ALint64 min, ALint64 max)
+{ return mini64(max, maxi64(min, val)); }
+
+inline ALuint64 minu64(ALuint64 a, ALuint64 b)
+{ return ((a > b) ? b : a); }
+inline ALuint64 maxu64(ALuint64 a, ALuint64 b)
+{ return ((a > b) ? a : b); }
+inline ALuint64 clampu64(ALuint64 val, ALuint64 min, ALuint64 max)
+{ return minu64(max, maxu64(min, val)); }
+
+
+union ResamplerCoeffs {
+    ALfloat FIR4[FRACTIONONE][4];
+    ALfloat FIR8[FRACTIONONE][8];
+};
+extern alignas(16) union ResamplerCoeffs ResampleCoeffs;
+
+extern alignas(16) const ALfloat bsincTab[18840];
+
+
+inline ALfloat lerp(ALfloat val1, ALfloat val2, ALfloat mu)
+{
+    return val1 + (val2-val1)*mu;
+}
+inline ALfloat resample_fir4(ALfloat val0, ALfloat val1, ALfloat val2, ALfloat val3, ALuint frac)
+{
+    const ALfloat *k = ResampleCoeffs.FIR4[frac];
+    return k[0]*val0 + k[1]*val1 + k[2]*val2 + k[3]*val3;
+}
+inline ALfloat resample_fir8(ALfloat val0, ALfloat val1, ALfloat val2, ALfloat val3, ALfloat val4, ALfloat val5, ALfloat val6, ALfloat val7, ALuint frac)
+{
+    const ALfloat *k = ResampleCoeffs.FIR8[frac];
+    return k[0]*val0 + k[1]*val1 + k[2]*val2 + k[3]*val3 +
+           k[4]*val4 + k[5]*val5 + k[6]*val6 + k[7]*val7;
+}
+
+
+enum HrtfRequestMode {
+    Hrtf_Default = 0,
+    Hrtf_Enable = 1,
+    Hrtf_Disable = 2,
+};
+
+
+void aluInitMixer(void);
+
+MixerFunc SelectMixer(void);
+RowMixerFunc SelectRowMixer(void);
+
+/* aluInitRenderer
+ *
+ * Set up the appropriate panning method and mixing method given the device
+ * properties.
+ */
+void aluInitRenderer(ALCdevice *device, ALint hrtf_id, enum HrtfRequestMode hrtf_appreq, enum HrtfRequestMode hrtf_userreq);
+
+void aluInitEffectPanning(struct ALeffectslot *slot);
+
+/**
+ * CalcDirectionCoeffs
+ *
+ * Calculates ambisonic coefficients based on a direction vector. The vector
+ * must be normalized (unit length), and the spread is the angular width of the
+ * sound (0...tau).
+ */
+void CalcDirectionCoeffs(const ALfloat dir[3], ALfloat spread, ALfloat coeffs[MAX_AMBI_COEFFS]);
+
+/**
+ * CalcXYZCoeffs
+ *
+ * Same as CalcDirectionCoeffs except the direction is specified as separate x,
+ * y, and z parameters instead of an array.
+ */
+inline void CalcXYZCoeffs(ALfloat x, ALfloat y, ALfloat z, ALfloat spread, ALfloat coeffs[MAX_AMBI_COEFFS])
+{
+    ALfloat dir[3] = { x, y, z };
+    CalcDirectionCoeffs(dir, spread, coeffs);
+}
+
+/**
+ * CalcAngleCoeffs
+ *
+ * Calculates ambisonic coefficients based on azimuth and elevation. The
+ * azimuth and elevation parameters are in radians, going right and up
+ * respectively.
+ */
+void CalcAngleCoeffs(ALfloat azimuth, ALfloat elevation, ALfloat spread, ALfloat coeffs[MAX_AMBI_COEFFS]);
+
+/**
+ * ComputeAmbientGains
+ *
+ * Computes channel gains for ambient, omni-directional sounds.
+ */
+#define ComputeAmbientGains(b, g, o) do {                                     \
+    if((b).CoeffCount > 0)                                                    \
+        ComputeAmbientGainsMC((b).Ambi.Coeffs, (b).NumChannels, g, o);        \
+    else                                                                      \
+        ComputeAmbientGainsBF((b).Ambi.Map, (b).NumChannels, g, o);           \
+} while (0)
+void ComputeAmbientGainsMC(const ChannelConfig *chancoeffs, ALuint numchans, ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]);
+void ComputeAmbientGainsBF(const BFChannelConfig *chanmap, ALuint numchans, ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]);
+
+/**
+ * ComputePanningGains
+ *
+ * Computes panning gains using the given channel decoder coefficients and the
+ * pre-calculated direction or angle coefficients.
+ */
+#define ComputePanningGains(b, c, g, o) do {                                  \
+    if((b).CoeffCount > 0)                                                    \
+        ComputePanningGainsMC((b).Ambi.Coeffs, (b).NumChannels, (b).CoeffCount, c, g, o);\
+    else                                                                      \
+        ComputePanningGainsBF((b).Ambi.Map, (b).NumChannels, c, g, o);        \
+} while (0)
+void ComputePanningGainsMC(const ChannelConfig *chancoeffs, ALuint numchans, ALuint numcoeffs, const ALfloat coeffs[MAX_AMBI_COEFFS], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]);
+void ComputePanningGainsBF(const BFChannelConfig *chanmap, ALuint numchans, const ALfloat coeffs[MAX_AMBI_COEFFS], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]);
+
+/**
+ * ComputeFirstOrderGains
+ *
+ * Sets channel gains for a first-order ambisonics input channel. The matrix is
+ * a 1x4 'slice' of a transform matrix for the input channel, used to scale and
+ * orient the sound samples.
+ */
+#define ComputeFirstOrderGains(b, m, g, o) do {                               \
+    if((b).CoeffCount > 0)                                                    \
+        ComputeFirstOrderGainsMC((b).Ambi.Coeffs, (b).NumChannels, m, g, o);  \
+    else                                                                      \
+        ComputeFirstOrderGainsBF((b).Ambi.Map, (b).NumChannels, m, g, o);     \
+} while (0)
+void ComputeFirstOrderGainsMC(const ChannelConfig *chancoeffs, ALuint numchans, const ALfloat mtx[4], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]);
+void ComputeFirstOrderGainsBF(const BFChannelConfig *chanmap, ALuint numchans, const ALfloat mtx[4], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]);
+
+
+ALvoid MixSource(struct ALvoice *voice, struct ALsource *source, ALCdevice *Device, ALuint SamplesToDo);
+
+ALvoid aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size);
+/* Caller must lock the device. */
+ALvoid aluHandleDisconnect(ALCdevice *device);
+
+extern ALfloat ConeScale;
+extern ALfloat ZScale;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 94 - 0
Engine/lib/openal-soft/OpenAL32/Include/bs2b.h

@@ -0,0 +1,94 @@
+/*-
+ * Copyright (c) 2005 Boris Mikhaylov
+ *
+ * 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.
+ */
+
+#ifndef BS2B_H
+#define BS2B_H
+
+/* Number of crossfeed levels */
+#define BS2B_CLEVELS           3
+
+/* Normal crossfeed levels */
+#define BS2B_HIGH_CLEVEL       3
+#define BS2B_MIDDLE_CLEVEL     2
+#define BS2B_LOW_CLEVEL        1
+
+/* Easy crossfeed levels */
+#define BS2B_HIGH_ECLEVEL      BS2B_HIGH_CLEVEL    + BS2B_CLEVELS
+#define BS2B_MIDDLE_ECLEVEL    BS2B_MIDDLE_CLEVEL  + BS2B_CLEVELS
+#define BS2B_LOW_ECLEVEL       BS2B_LOW_CLEVEL     + BS2B_CLEVELS
+
+/* Default crossfeed levels */
+#define BS2B_DEFAULT_CLEVEL    BS2B_HIGH_ECLEVEL
+/* Default sample rate (Hz) */
+#define BS2B_DEFAULT_SRATE     44100
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+struct bs2b {
+    int level;  /* Crossfeed level */
+    int srate;   /* Sample rate (Hz) */
+
+    /* Lowpass IIR filter coefficients */
+    float a0_lo;
+    float b1_lo;
+
+    /* Highboost IIR filter coefficients */
+    float a0_hi;
+    float a1_hi;
+    float b1_hi;
+
+    /* Buffer of last filtered sample.
+     * [0] - first channel, [1] - second channel
+     */
+    struct t_last_sample {
+        float asis;
+        float lo;
+        float hi;
+    } last_sample[2];
+};
+
+/* Clear buffers and set new coefficients with new crossfeed level and sample
+ * rate values.
+ * level - crossfeed level of *LEVEL values.
+ * srate - sample rate by Hz.
+ */
+void bs2b_set_params(struct bs2b *bs2b, int level, int srate);
+
+/* Return current crossfeed level value */
+int bs2b_get_level(struct bs2b *bs2b);
+
+/* Return current sample rate value */
+int bs2b_get_srate(struct bs2b *bs2b);
+
+/* Clear buffer */
+void bs2b_clear(struct bs2b *bs2b);
+
+void bs2b_cross_feed(struct bs2b *bs2b, float *restrict Left, float *restrict Right, unsigned int SamplesToDo);
+
+#ifdef __cplusplus
+}    /* extern "C" */
+#endif /* __cplusplus */
+
+#endif /* BS2B_H */

+ 9 - 0
Engine/lib/openal-soft/OpenAL32/Include/sample_cvt.h

@@ -0,0 +1,9 @@
+#ifndef SAMPLE_CVT_H
+#define SAMPLE_CVT_H
+
+#include "AL/al.h"
+#include "alBuffer.h"
+
+void ConvertData(ALvoid *dst, enum UserFmtType dstType, const ALvoid *src, enum UserFmtType srcType, ALsizei numchans, ALsizei len, ALsizei align);
+
+#endif /* SAMPLE_CVT_H */

+ 715 - 0
Engine/lib/openal-soft/OpenAL32/alAuxEffectSlot.c

@@ -0,0 +1,715 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 1999-2007 by authors.
+ * This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the
+ *  Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <math.h>
+
+#include "AL/al.h"
+#include "AL/alc.h"
+#include "alMain.h"
+#include "alAuxEffectSlot.h"
+#include "alThunk.h"
+#include "alError.h"
+#include "alListener.h"
+#include "alSource.h"
+
+#include "almalloc.h"
+
+
+extern inline void LockEffectSlotsRead(ALCcontext *context);
+extern inline void UnlockEffectSlotsRead(ALCcontext *context);
+extern inline void LockEffectSlotsWrite(ALCcontext *context);
+extern inline void UnlockEffectSlotsWrite(ALCcontext *context);
+extern inline struct ALeffectslot *LookupEffectSlot(ALCcontext *context, ALuint id);
+extern inline struct ALeffectslot *RemoveEffectSlot(ALCcontext *context, ALuint id);
+
+static void RemoveEffectSlotList(ALCcontext *Context, ALeffectslot *slot);
+
+static UIntMap EffectStateFactoryMap;
+static inline ALeffectStateFactory *getFactoryByType(ALenum type)
+{
+    ALeffectStateFactory* (*getFactory)(void) = LookupUIntMapKey(&EffectStateFactoryMap, type);
+    if(getFactory != NULL)
+        return getFactory();
+    return NULL;
+}
+
+static void ALeffectState_IncRef(ALeffectState *state);
+static void ALeffectState_DecRef(ALeffectState *state);
+
+#define DO_UPDATEPROPS() do {                                                 \
+    if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))          \
+        UpdateEffectSlotProps(slot);                                          \
+    else                                                                      \
+        slot->NeedsUpdate = AL_TRUE;                                          \
+} while(0)
+
+
+AL_API ALvoid AL_APIENTRY alGenAuxiliaryEffectSlots(ALsizei n, ALuint *effectslots)
+{
+    ALCcontext *context;
+    ALeffectslot *first, *last;
+    ALsizei cur;
+    ALenum err;
+
+    context = GetContextRef();
+    if(!context) return;
+
+    if(!(n >= 0))
+        SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+
+    first = last = NULL;
+    for(cur = 0;cur < n;cur++)
+    {
+        ALeffectslot *slot = al_calloc(16, sizeof(ALeffectslot));
+        err = AL_OUT_OF_MEMORY;
+        if(!slot || (err=InitEffectSlot(slot)) != AL_NO_ERROR)
+        {
+            al_free(slot);
+            alDeleteAuxiliaryEffectSlots(cur, effectslots);
+            SET_ERROR_AND_GOTO(context, err, done);
+        }
+
+        err = NewThunkEntry(&slot->id);
+        if(err == AL_NO_ERROR)
+            err = InsertUIntMapEntry(&context->EffectSlotMap, slot->id, slot);
+        if(err != AL_NO_ERROR)
+        {
+            FreeThunkEntry(slot->id);
+            ALeffectState_DecRef(slot->Effect.State);
+            if(slot->Params.EffectState)
+                ALeffectState_DecRef(slot->Params.EffectState);
+            al_free(slot);
+
+            alDeleteAuxiliaryEffectSlots(cur, effectslots);
+            SET_ERROR_AND_GOTO(context, err, done);
+        }
+
+        aluInitEffectPanning(slot);
+
+        if(!first) first = slot;
+        if(last) ATOMIC_STORE(&last->next, slot, almemory_order_relaxed);
+        last = slot;
+
+        effectslots[cur] = slot->id;
+    }
+    if(last != NULL)
+    {
+        ALeffectslot *root = ATOMIC_LOAD(&context->ActiveAuxSlotList);
+        do {
+            ATOMIC_STORE(&last->next, root, almemory_order_relaxed);
+        } while(!ATOMIC_COMPARE_EXCHANGE_WEAK(ALeffectslot*, &context->ActiveAuxSlotList,
+                                              &root, first));
+    }
+
+done:
+    ALCcontext_DecRef(context);
+}
+
+AL_API ALvoid AL_APIENTRY alDeleteAuxiliaryEffectSlots(ALsizei n, const ALuint *effectslots)
+{
+    ALCcontext *context;
+    ALeffectslot *slot;
+    ALsizei i;
+
+    context = GetContextRef();
+    if(!context) return;
+
+    LockEffectSlotsWrite(context);
+    if(!(n >= 0))
+        SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+    for(i = 0;i < n;i++)
+    {
+        if((slot=LookupEffectSlot(context, effectslots[i])) == NULL)
+            SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
+        if(ReadRef(&slot->ref) != 0)
+            SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done);
+    }
+
+    // All effectslots are valid
+    for(i = 0;i < n;i++)
+    {
+        if((slot=RemoveEffectSlot(context, effectslots[i])) == NULL)
+            continue;
+        FreeThunkEntry(slot->id);
+
+        RemoveEffectSlotList(context, slot);
+        DeinitEffectSlot(slot);
+
+        memset(slot, 0, sizeof(*slot));
+        al_free(slot);
+    }
+
+done:
+    UnlockEffectSlotsWrite(context);
+    ALCcontext_DecRef(context);
+}
+
+AL_API ALboolean AL_APIENTRY alIsAuxiliaryEffectSlot(ALuint effectslot)
+{
+    ALCcontext *context;
+    ALboolean  ret;
+
+    context = GetContextRef();
+    if(!context) return AL_FALSE;
+
+    LockEffectSlotsRead(context);
+    ret = (LookupEffectSlot(context, effectslot) ? AL_TRUE : AL_FALSE);
+    UnlockEffectSlotsRead(context);
+
+    ALCcontext_DecRef(context);
+
+    return ret;
+}
+
+AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint value)
+{
+    ALCdevice *device;
+    ALCcontext *context;
+    ALeffectslot *slot;
+    ALeffect *effect = NULL;
+    ALenum err;
+
+    context = GetContextRef();
+    if(!context) return;
+
+    WriteLock(&context->PropLock);
+    LockEffectSlotsRead(context);
+    if((slot=LookupEffectSlot(context, effectslot)) == NULL)
+        SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
+    switch(param)
+    {
+    case AL_EFFECTSLOT_EFFECT:
+        device = context->Device;
+
+        LockEffectsRead(device);
+        effect = (value ? LookupEffect(device, value) : NULL);
+        if(!(value == 0 || effect != NULL))
+        {
+            UnlockEffectsRead(device);
+            SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+        }
+        err = InitializeEffect(device, slot, effect);
+        UnlockEffectsRead(device);
+
+        if(err != AL_NO_ERROR)
+            SET_ERROR_AND_GOTO(context, err, done);
+        break;
+
+    case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
+        if(!(value == AL_TRUE || value == AL_FALSE))
+            SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+        slot->AuxSendAuto = value;
+        break;
+
+    default:
+        SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
+    }
+    DO_UPDATEPROPS();
+
+done:
+    UnlockEffectSlotsRead(context);
+    WriteUnlock(&context->PropLock);
+    ALCcontext_DecRef(context);
+}
+
+AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, const ALint *values)
+{
+    ALCcontext *context;
+
+    switch(param)
+    {
+    case AL_EFFECTSLOT_EFFECT:
+    case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
+        alAuxiliaryEffectSloti(effectslot, param, values[0]);
+        return;
+    }
+
+    context = GetContextRef();
+    if(!context) return;
+
+    LockEffectSlotsRead(context);
+    if(LookupEffectSlot(context, effectslot) == NULL)
+        SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
+    switch(param)
+    {
+    default:
+        SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
+    }
+
+done:
+    UnlockEffectSlotsRead(context);
+    ALCcontext_DecRef(context);
+}
+
+AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat value)
+{
+    ALCcontext *context;
+    ALeffectslot *slot;
+
+    context = GetContextRef();
+    if(!context) return;
+
+    WriteLock(&context->PropLock);
+    LockEffectSlotsRead(context);
+    if((slot=LookupEffectSlot(context, effectslot)) == NULL)
+        SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
+    switch(param)
+    {
+    case AL_EFFECTSLOT_GAIN:
+        if(!(value >= 0.0f && value <= 1.0f))
+            SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+        slot->Gain = value;
+        break;
+
+    default:
+        SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
+    }
+    DO_UPDATEPROPS();
+
+done:
+    UnlockEffectSlotsRead(context);
+    WriteUnlock(&context->PropLock);
+    ALCcontext_DecRef(context);
+}
+
+AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, const ALfloat *values)
+{
+    ALCcontext *context;
+
+    switch(param)
+    {
+    case AL_EFFECTSLOT_GAIN:
+        alAuxiliaryEffectSlotf(effectslot, param, values[0]);
+        return;
+    }
+
+    context = GetContextRef();
+    if(!context) return;
+
+    LockEffectSlotsRead(context);
+    if(LookupEffectSlot(context, effectslot) == NULL)
+        SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
+    switch(param)
+    {
+    default:
+        SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
+    }
+
+done:
+    UnlockEffectSlotsRead(context);
+    ALCcontext_DecRef(context);
+}
+
+AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint *value)
+{
+    ALCcontext *context;
+    ALeffectslot *slot;
+
+    context = GetContextRef();
+    if(!context) return;
+
+    LockEffectSlotsRead(context);
+    if((slot=LookupEffectSlot(context, effectslot)) == NULL)
+        SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
+    switch(param)
+    {
+    case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
+        *value = slot->AuxSendAuto;
+        break;
+
+    default:
+        SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
+    }
+
+done:
+    UnlockEffectSlotsRead(context);
+    ALCcontext_DecRef(context);
+}
+
+AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, ALint *values)
+{
+    ALCcontext *context;
+
+    switch(param)
+    {
+    case AL_EFFECTSLOT_EFFECT:
+    case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
+        alGetAuxiliaryEffectSloti(effectslot, param, values);
+        return;
+    }
+
+    context = GetContextRef();
+    if(!context) return;
+
+    LockEffectSlotsRead(context);
+    if(LookupEffectSlot(context, effectslot) == NULL)
+        SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
+    switch(param)
+    {
+    default:
+        SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
+    }
+
+done:
+    UnlockEffectSlotsRead(context);
+    ALCcontext_DecRef(context);
+}
+
+AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat *value)
+{
+    ALCcontext *context;
+    ALeffectslot *slot;
+
+    context = GetContextRef();
+    if(!context) return;
+
+    LockEffectSlotsRead(context);
+    if((slot=LookupEffectSlot(context, effectslot)) == NULL)
+        SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
+    switch(param)
+    {
+    case AL_EFFECTSLOT_GAIN:
+        *value = slot->Gain;
+        break;
+
+    default:
+        SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
+    }
+
+done:
+    UnlockEffectSlotsRead(context);
+    ALCcontext_DecRef(context);
+}
+
+AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, ALfloat *values)
+{
+    ALCcontext *context;
+
+    switch(param)
+    {
+    case AL_EFFECTSLOT_GAIN:
+        alGetAuxiliaryEffectSlotf(effectslot, param, values);
+        return;
+    }
+
+    context = GetContextRef();
+    if(!context) return;
+
+    LockEffectSlotsRead(context);
+    if(LookupEffectSlot(context, effectslot) == NULL)
+        SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
+    switch(param)
+    {
+    default:
+        SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
+    }
+
+done:
+    UnlockEffectSlotsRead(context);
+    ALCcontext_DecRef(context);
+}
+
+
+static void RemoveEffectSlotList(ALCcontext *context, ALeffectslot *slot)
+{
+    ALCdevice *device = context->Device;
+    ALeffectslot *root, *next;
+
+    root = slot;
+    next = ATOMIC_LOAD(&slot->next);
+    if(!ATOMIC_COMPARE_EXCHANGE_STRONG(ALeffectslot*, &context->ActiveAuxSlotList, &root, next))
+    {
+        ALeffectslot *cur;
+        do {
+            cur = root;
+            root = slot;
+        } while(!ATOMIC_COMPARE_EXCHANGE_STRONG(ALeffectslot*, &cur->next, &root, next));
+    }
+    /* Wait for any mix that may be using these effect slots to finish. */
+    while((ReadRef(&device->MixCount)&1) != 0)
+        althrd_yield();
+}
+
+
+void InitEffectFactoryMap(void)
+{
+    InitUIntMap(&EffectStateFactoryMap, ~0);
+
+    InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_NULL, ALnullStateFactory_getFactory);
+    InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_EAXREVERB, ALreverbStateFactory_getFactory);
+    InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_REVERB, ALreverbStateFactory_getFactory);
+    InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_CHORUS, ALchorusStateFactory_getFactory);
+    InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_COMPRESSOR, ALcompressorStateFactory_getFactory);
+    InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_DISTORTION, ALdistortionStateFactory_getFactory);
+    InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_ECHO, ALechoStateFactory_getFactory);
+    InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_EQUALIZER, ALequalizerStateFactory_getFactory);
+    InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_FLANGER, ALflangerStateFactory_getFactory);
+    InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_RING_MODULATOR, ALmodulatorStateFactory_getFactory);
+    InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_DEDICATED_DIALOGUE, ALdedicatedStateFactory_getFactory);
+    InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT, ALdedicatedStateFactory_getFactory);
+}
+
+void DeinitEffectFactoryMap(void)
+{
+    ResetUIntMap(&EffectStateFactoryMap);
+}
+
+
+ALenum InitializeEffect(ALCdevice *Device, ALeffectslot *EffectSlot, ALeffect *effect)
+{
+    ALenum newtype = (effect ? effect->type : AL_EFFECT_NULL);
+    struct ALeffectslotProps *props;
+    ALeffectState *State;
+
+    if(newtype != EffectSlot->Effect.Type)
+    {
+        ALeffectStateFactory *factory;
+        FPUCtl oldMode;
+
+        factory = getFactoryByType(newtype);
+        if(!factory)
+        {
+            ERR("Failed to find factory for effect type 0x%04x\n", newtype);
+            return AL_INVALID_ENUM;
+        }
+        State = V0(factory,create)();
+        if(!State) return AL_OUT_OF_MEMORY;
+
+        SetMixerFPUMode(&oldMode);
+        almtx_lock(&Device->BackendLock);
+        State->OutBuffer = Device->Dry.Buffer;
+        State->OutChannels = Device->Dry.NumChannels;
+        if(V(State,deviceUpdate)(Device) == AL_FALSE)
+        {
+            almtx_unlock(&Device->BackendLock);
+            RestoreFPUMode(&oldMode);
+            ALeffectState_DecRef(State);
+            return AL_OUT_OF_MEMORY;
+        }
+        almtx_unlock(&Device->BackendLock);
+        RestoreFPUMode(&oldMode);
+
+        if(!effect)
+        {
+            EffectSlot->Effect.Type = AL_EFFECT_NULL;
+            memset(&EffectSlot->Effect.Props, 0, sizeof(EffectSlot->Effect.Props));
+        }
+        else
+        {
+            EffectSlot->Effect.Type = effect->type;
+            EffectSlot->Effect.Props = effect->Props;
+        }
+
+        ALeffectState_DecRef(EffectSlot->Effect.State);
+        EffectSlot->Effect.State = State;
+    }
+    else if(effect)
+        EffectSlot->Effect.Props = effect->Props;
+
+    /* Remove state references from old effect slot property updates. */
+    props = ATOMIC_LOAD(&EffectSlot->FreeList);
+    while(props)
+    {
+        State = ATOMIC_EXCHANGE(ALeffectState*, &props->State, NULL, almemory_order_relaxed);
+        if(State) ALeffectState_DecRef(State);
+        props = ATOMIC_LOAD(&props->next, almemory_order_relaxed);
+    }
+
+    return AL_NO_ERROR;
+}
+
+
+static void ALeffectState_IncRef(ALeffectState *state)
+{
+    uint ref;
+    ref = IncrementRef(&state->Ref);
+    TRACEREF("%p increasing refcount to %u\n", state, ref);
+}
+
+static void ALeffectState_DecRef(ALeffectState *state)
+{
+    uint ref;
+    ref = DecrementRef(&state->Ref);
+    TRACEREF("%p decreasing refcount to %u\n", state, ref);
+    if(ref == 0) DELETE_OBJ(state);
+}
+
+
+void ALeffectState_Construct(ALeffectState *state)
+{
+    InitRef(&state->Ref, 1);
+
+    state->OutBuffer = NULL;
+    state->OutChannels = 0;
+}
+
+void ALeffectState_Destruct(ALeffectState *UNUSED(state))
+{
+}
+
+
+ALenum InitEffectSlot(ALeffectslot *slot)
+{
+    ALeffectStateFactory *factory;
+
+    slot->Effect.Type = AL_EFFECT_NULL;
+
+    factory = getFactoryByType(AL_EFFECT_NULL);
+    if(!(slot->Effect.State=V0(factory,create)()))
+        return AL_OUT_OF_MEMORY;
+
+    slot->NeedsUpdate = AL_FALSE;
+    slot->Gain = 1.0;
+    slot->AuxSendAuto = AL_TRUE;
+    InitRef(&slot->ref, 0);
+
+    ATOMIC_INIT(&slot->Update, NULL);
+    ATOMIC_INIT(&slot->FreeList, NULL);
+
+    slot->Params.Gain = 1.0f;
+    slot->Params.AuxSendAuto = AL_TRUE;
+    ALeffectState_IncRef(slot->Effect.State);
+    slot->Params.EffectState = slot->Effect.State;
+    slot->Params.RoomRolloff = 0.0f;
+    slot->Params.DecayTime = 0.0f;
+    slot->Params.AirAbsorptionGainHF = 1.0f;
+
+    ATOMIC_INIT(&slot->next, NULL);
+
+    return AL_NO_ERROR;
+}
+
+void DeinitEffectSlot(ALeffectslot *slot)
+{
+    struct ALeffectslotProps *props;
+    ALeffectState *state;
+    size_t count = 0;
+
+    props = ATOMIC_LOAD(&slot->Update);
+    if(props)
+    {
+        state = ATOMIC_LOAD(&props->State, almemory_order_relaxed);
+        if(state) ALeffectState_DecRef(state);
+        TRACE("Freed unapplied AuxiliaryEffectSlot update %p\n", props);
+        al_free(props);
+    }
+    props = ATOMIC_LOAD(&slot->FreeList, almemory_order_relaxed);
+    while(props)
+    {
+        struct ALeffectslotProps *next;
+        state = ATOMIC_LOAD(&props->State, almemory_order_relaxed);
+        next = ATOMIC_LOAD(&props->next, almemory_order_relaxed);
+        if(state) ALeffectState_DecRef(state);
+        al_free(props);
+        props = next;
+        ++count;
+    }
+    TRACE("Freed "SZFMT" AuxiliaryEffectSlot property object%s\n", count, (count==1)?"":"s");
+
+    ALeffectState_DecRef(slot->Effect.State);
+    if(slot->Params.EffectState)
+        ALeffectState_DecRef(slot->Params.EffectState);
+}
+
+void UpdateEffectSlotProps(ALeffectslot *slot)
+{
+    struct ALeffectslotProps *props;
+    ALeffectState *oldstate;
+
+    /* Get an unused property container, or allocate a new one as needed. */
+    props = ATOMIC_LOAD(&slot->FreeList, almemory_order_relaxed);
+    if(!props)
+        props = al_calloc(16, sizeof(*props));
+    else
+    {
+        struct ALeffectslotProps *next;
+        do {
+            next = ATOMIC_LOAD(&props->next, almemory_order_relaxed);
+        } while(ATOMIC_COMPARE_EXCHANGE_WEAK(struct ALeffectslotProps*,
+                &slot->FreeList, &props, next, almemory_order_seq_cst,
+                almemory_order_consume) == 0);
+    }
+
+    /* Copy in current property values. */
+    ATOMIC_STORE(&props->Gain, slot->Gain, almemory_order_relaxed);
+    ATOMIC_STORE(&props->AuxSendAuto, slot->AuxSendAuto, almemory_order_relaxed);
+
+    ATOMIC_STORE(&props->Type, slot->Effect.Type, almemory_order_relaxed);
+    props->Props = slot->Effect.Props;
+    /* Swap out any stale effect state object there may be in the container, to
+     * delete it.
+     */
+    ALeffectState_IncRef(slot->Effect.State);
+    oldstate = ATOMIC_EXCHANGE(ALeffectState*, &props->State, slot->Effect.State,
+                               almemory_order_relaxed);
+
+    /* Set the new container for updating internal parameters. */
+    props = ATOMIC_EXCHANGE(struct ALeffectslotProps*, &slot->Update, props,
+                            almemory_order_acq_rel);
+    if(props)
+    {
+        /* If there was an unused update container, put it back in the
+         * freelist.
+         */
+        struct ALeffectslotProps *first = ATOMIC_LOAD(&slot->FreeList);
+        do {
+            ATOMIC_STORE(&props->next, first, almemory_order_relaxed);
+        } while(ATOMIC_COMPARE_EXCHANGE_WEAK(struct ALeffectslotProps*,
+                &slot->FreeList, &first, props) == 0);
+    }
+
+    if(oldstate)
+        ALeffectState_DecRef(oldstate);
+}
+
+void UpdateAllEffectSlotProps(ALCcontext *context)
+{
+    ALeffectslot *slot;
+
+    LockEffectSlotsRead(context);
+    slot = ATOMIC_LOAD(&context->ActiveAuxSlotList);
+    while(slot)
+    {
+        if(slot->NeedsUpdate)
+            UpdateEffectSlotProps(slot);
+        slot->NeedsUpdate = AL_FALSE;
+        slot = ATOMIC_LOAD(&slot->next, almemory_order_relaxed);
+    }
+    UnlockEffectSlotsRead(context);
+}
+
+ALvoid ReleaseALAuxiliaryEffectSlots(ALCcontext *Context)
+{
+    ALsizei pos;
+    for(pos = 0;pos < Context->EffectSlotMap.size;pos++)
+    {
+        ALeffectslot *temp = Context->EffectSlotMap.values[pos];
+        Context->EffectSlotMap.values[pos] = NULL;
+
+        DeinitEffectSlot(temp);
+
+        FreeThunkEntry(temp->id);
+        memset(temp, 0, sizeof(ALeffectslot));
+        al_free(temp);
+    }
+}

+ 1415 - 0
Engine/lib/openal-soft/OpenAL32/alBuffer.c

@@ -0,0 +1,1415 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 1999-2007 by authors.
+ * This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the
+ *  Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <limits.h>
+#ifdef HAVE_MALLOC_H
+#include <malloc.h>
+#endif
+
+#include "alMain.h"
+#include "alu.h"
+#include "alError.h"
+#include "alBuffer.h"
+#include "alThunk.h"
+#include "sample_cvt.h"
+
+
+extern inline void LockBuffersRead(ALCdevice *device);
+extern inline void UnlockBuffersRead(ALCdevice *device);
+extern inline void LockBuffersWrite(ALCdevice *device);
+extern inline void UnlockBuffersWrite(ALCdevice *device);
+extern inline struct ALbuffer *LookupBuffer(ALCdevice *device, ALuint id);
+extern inline struct ALbuffer *RemoveBuffer(ALCdevice *device, ALuint id);
+extern inline ALuint FrameSizeFromUserFmt(enum UserFmtChannels chans, enum UserFmtType type);
+extern inline ALuint FrameSizeFromFmt(enum FmtChannels chans, enum FmtType type);
+
+static ALboolean IsValidType(ALenum type);
+static ALboolean IsValidChannels(ALenum channels);
+static ALboolean DecomposeUserFormat(ALenum format, enum UserFmtChannels *chans, enum UserFmtType *type);
+static ALboolean DecomposeFormat(ALenum format, enum FmtChannels *chans, enum FmtType *type);
+static ALboolean SanitizeAlignment(enum UserFmtType type, ALsizei *align);
+
+
+AL_API ALvoid AL_APIENTRY alGenBuffers(ALsizei n, ALuint *buffers)
+{
+    ALCcontext *context;
+    ALsizei cur = 0;
+
+    context = GetContextRef();
+    if(!context) return;
+
+    if(!(n >= 0))
+        SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+
+    for(cur = 0;cur < n;cur++)
+    {
+        ALbuffer *buffer = NewBuffer(context);
+        if(!buffer)
+        {
+            alDeleteBuffers(cur, buffers);
+            break;
+        }
+
+        buffers[cur] = buffer->id;
+    }
+
+done:
+    ALCcontext_DecRef(context);
+}
+
+AL_API ALvoid AL_APIENTRY alDeleteBuffers(ALsizei n, const ALuint *buffers)
+{
+    ALCdevice *device;
+    ALCcontext *context;
+    ALbuffer *ALBuf;
+    ALsizei i;
+
+    context = GetContextRef();
+    if(!context) return;
+
+    device = context->Device;
+
+    LockBuffersWrite(device);
+    if(!(n >= 0))
+        SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+
+    for(i = 0;i < n;i++)
+    {
+        if(!buffers[i])
+            continue;
+
+        /* Check for valid Buffer ID */
+        if((ALBuf=LookupBuffer(device, buffers[i])) == NULL)
+            SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
+        if(ReadRef(&ALBuf->ref) != 0)
+            SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done);
+    }
+
+    for(i = 0;i < n;i++)
+    {
+        if((ALBuf=LookupBuffer(device, buffers[i])) != NULL)
+            DeleteBuffer(device, ALBuf);
+    }
+
+done:
+    UnlockBuffersWrite(device);
+    ALCcontext_DecRef(context);
+}
+
+AL_API ALboolean AL_APIENTRY alIsBuffer(ALuint buffer)
+{
+    ALCcontext *context;
+    ALboolean ret;
+
+    context = GetContextRef();
+    if(!context) return AL_FALSE;
+
+    LockBuffersRead(context->Device);
+    ret = ((!buffer || LookupBuffer(context->Device, buffer)) ?
+           AL_TRUE : AL_FALSE);
+    UnlockBuffersRead(context->Device);
+
+    ALCcontext_DecRef(context);
+
+    return ret;
+}
+
+
+AL_API ALvoid AL_APIENTRY alBufferData(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei freq)
+{
+    enum UserFmtChannels srcchannels = UserFmtMono;
+    enum UserFmtType srctype = UserFmtByte;
+    ALCdevice *device;
+    ALCcontext *context;
+    ALbuffer *albuf;
+    ALenum newformat = AL_NONE;
+    ALuint framesize;
+    ALsizei align;
+    ALenum err;
+
+    context = GetContextRef();
+    if(!context) return;
+
+    device = context->Device;
+    LockBuffersRead(device);
+    if((albuf=LookupBuffer(device, buffer)) == NULL)
+        SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
+    if(!(size >= 0 && freq > 0))
+        SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+    if(DecomposeUserFormat(format, &srcchannels, &srctype) == AL_FALSE)
+        SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
+
+    align = ATOMIC_LOAD(&albuf->UnpackAlign);
+    if(SanitizeAlignment(srctype, &align) == AL_FALSE)
+        SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+    switch(srctype)
+    {
+        case UserFmtByte:
+        case UserFmtUByte:
+        case UserFmtShort:
+        case UserFmtUShort:
+        case UserFmtFloat:
+            framesize = FrameSizeFromUserFmt(srcchannels, srctype) * align;
+            if((size%framesize) != 0)
+                SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+
+            err = LoadData(albuf, freq, format, size/framesize*align,
+                           srcchannels, srctype, data, align, AL_TRUE);
+            if(err != AL_NO_ERROR)
+                SET_ERROR_AND_GOTO(context, err, done);
+            break;
+
+        case UserFmtInt:
+        case UserFmtUInt:
+        case UserFmtByte3:
+        case UserFmtUByte3:
+        case UserFmtDouble:
+            framesize = FrameSizeFromUserFmt(srcchannels, srctype) * align;
+            if((size%framesize) != 0)
+                SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+
+            switch(srcchannels)
+            {
+                case UserFmtMono: newformat = AL_FORMAT_MONO_FLOAT32; break;
+                case UserFmtStereo: newformat = AL_FORMAT_STEREO_FLOAT32; break;
+                case UserFmtRear: newformat = AL_FORMAT_REAR32; break;
+                case UserFmtQuad: newformat = AL_FORMAT_QUAD32; break;
+                case UserFmtX51: newformat = AL_FORMAT_51CHN32; break;
+                case UserFmtX61: newformat = AL_FORMAT_61CHN32; break;
+                case UserFmtX71: newformat = AL_FORMAT_71CHN32; break;
+                case UserFmtBFormat2D: newformat = AL_FORMAT_BFORMAT2D_FLOAT32; break;
+                case UserFmtBFormat3D: newformat = AL_FORMAT_BFORMAT3D_FLOAT32; break;
+            }
+            err = LoadData(albuf, freq, newformat, size/framesize*align,
+                           srcchannels, srctype, data, align, AL_TRUE);
+            if(err != AL_NO_ERROR)
+                SET_ERROR_AND_GOTO(context, err, done);
+            break;
+
+        case UserFmtMulaw:
+        case UserFmtAlaw:
+            framesize = FrameSizeFromUserFmt(srcchannels, srctype) * align;
+            if((size%framesize) != 0)
+                SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+
+            switch(srcchannels)
+            {
+                case UserFmtMono: newformat = AL_FORMAT_MONO16; break;
+                case UserFmtStereo: newformat = AL_FORMAT_STEREO16; break;
+                case UserFmtRear: newformat = AL_FORMAT_REAR16; break;
+                case UserFmtQuad: newformat = AL_FORMAT_QUAD16; break;
+                case UserFmtX51: newformat = AL_FORMAT_51CHN16; break;
+                case UserFmtX61: newformat = AL_FORMAT_61CHN16; break;
+                case UserFmtX71: newformat = AL_FORMAT_71CHN16; break;
+                case UserFmtBFormat2D: newformat = AL_FORMAT_BFORMAT2D_16; break;
+                case UserFmtBFormat3D: newformat = AL_FORMAT_BFORMAT3D_16; break;
+            }
+            err = LoadData(albuf, freq, newformat, size/framesize*align,
+                           srcchannels, srctype, data, align, AL_TRUE);
+            if(err != AL_NO_ERROR)
+                SET_ERROR_AND_GOTO(context, err, done);
+            break;
+
+        case UserFmtIMA4:
+            framesize  = (align-1)/2 + 4;
+            framesize *= ChannelsFromUserFmt(srcchannels);
+            if((size%framesize) != 0)
+                SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+
+            switch(srcchannels)
+            {
+                case UserFmtMono: newformat = AL_FORMAT_MONO16; break;
+                case UserFmtStereo: newformat = AL_FORMAT_STEREO16; break;
+                case UserFmtRear: newformat = AL_FORMAT_REAR16; break;
+                case UserFmtQuad: newformat = AL_FORMAT_QUAD16; break;
+                case UserFmtX51: newformat = AL_FORMAT_51CHN16; break;
+                case UserFmtX61: newformat = AL_FORMAT_61CHN16; break;
+                case UserFmtX71: newformat = AL_FORMAT_71CHN16; break;
+                case UserFmtBFormat2D: newformat = AL_FORMAT_BFORMAT2D_16; break;
+                case UserFmtBFormat3D: newformat = AL_FORMAT_BFORMAT3D_16; break;
+            }
+            err = LoadData(albuf, freq, newformat, size/framesize*align,
+                           srcchannels, srctype, data, align, AL_TRUE);
+            if(err != AL_NO_ERROR)
+                SET_ERROR_AND_GOTO(context, err, done);
+            break;
+
+        case UserFmtMSADPCM:
+            framesize  = (align-2)/2 + 7;
+            framesize *= ChannelsFromUserFmt(srcchannels);
+            if((size%framesize) != 0)
+                SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+
+            switch(srcchannels)
+            {
+                case UserFmtMono: newformat = AL_FORMAT_MONO16; break;
+                case UserFmtStereo: newformat = AL_FORMAT_STEREO16; break;
+                case UserFmtRear: newformat = AL_FORMAT_REAR16; break;
+                case UserFmtQuad: newformat = AL_FORMAT_QUAD16; break;
+                case UserFmtX51: newformat = AL_FORMAT_51CHN16; break;
+                case UserFmtX61: newformat = AL_FORMAT_61CHN16; break;
+                case UserFmtX71: newformat = AL_FORMAT_71CHN16; break;
+                case UserFmtBFormat2D: newformat = AL_FORMAT_BFORMAT2D_16; break;
+                case UserFmtBFormat3D: newformat = AL_FORMAT_BFORMAT3D_16; break;
+            }
+            err = LoadData(albuf, freq, newformat, size/framesize*align,
+                           srcchannels, srctype, data, align, AL_TRUE);
+            if(err != AL_NO_ERROR)
+                SET_ERROR_AND_GOTO(context, err, done);
+            break;
+    }
+
+done:
+    UnlockBuffersRead(device);
+    ALCcontext_DecRef(context);
+}
+
+AL_API ALvoid AL_APIENTRY alBufferSubDataSOFT(ALuint buffer, ALenum format, const ALvoid *data, ALsizei offset, ALsizei length)
+{
+    enum UserFmtChannels srcchannels = UserFmtMono;
+    enum UserFmtType srctype = UserFmtByte;
+    ALCdevice *device;
+    ALCcontext *context;
+    ALbuffer *albuf;
+    ALuint byte_align;
+    ALuint channels;
+    ALuint bytes;
+    ALsizei align;
+
+    context = GetContextRef();
+    if(!context) return;
+
+    device = context->Device;
+    LockBuffersRead(device);
+    if((albuf=LookupBuffer(device, buffer)) == NULL)
+        SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
+    if(!(length >= 0 && offset >= 0))
+        SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+    if(DecomposeUserFormat(format, &srcchannels, &srctype) == AL_FALSE)
+        SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
+
+    WriteLock(&albuf->lock);
+    align = ATOMIC_LOAD(&albuf->UnpackAlign);
+    if(SanitizeAlignment(srctype, &align) == AL_FALSE)
+    {
+        WriteUnlock(&albuf->lock);
+        SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+    }
+    if(srcchannels != albuf->OriginalChannels || srctype != albuf->OriginalType)
+    {
+        WriteUnlock(&albuf->lock);
+        SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
+    }
+    if(align != albuf->OriginalAlign)
+    {
+        WriteUnlock(&albuf->lock);
+        SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
+    }
+
+    if(albuf->OriginalType == UserFmtIMA4)
+    {
+        byte_align  = (albuf->OriginalAlign-1)/2 + 4;
+        byte_align *= ChannelsFromUserFmt(albuf->OriginalChannels);
+    }
+    else if(albuf->OriginalType == UserFmtMSADPCM)
+    {
+        byte_align  = (albuf->OriginalAlign-2)/2 + 7;
+        byte_align *= ChannelsFromUserFmt(albuf->OriginalChannels);
+    }
+    else
+    {
+        byte_align  = albuf->OriginalAlign;
+        byte_align *= FrameSizeFromUserFmt(albuf->OriginalChannels,
+                                           albuf->OriginalType);
+    }
+
+    if(offset > albuf->OriginalSize || length > albuf->OriginalSize-offset ||
+       (offset%byte_align) != 0 || (length%byte_align) != 0)
+    {
+        WriteUnlock(&albuf->lock);
+        SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+    }
+
+    channels = ChannelsFromFmt(albuf->FmtChannels);
+    bytes = BytesFromFmt(albuf->FmtType);
+    /* offset -> byte offset, length -> sample count */
+    offset = offset/byte_align * channels*bytes;
+    length = length/byte_align * albuf->OriginalAlign;
+
+    ConvertData((char*)albuf->data+offset, (enum UserFmtType)albuf->FmtType,
+                data, srctype, channels, length, align);
+    WriteUnlock(&albuf->lock);
+
+done:
+    UnlockBuffersRead(device);
+    ALCcontext_DecRef(context);
+}
+
+
+AL_API void AL_APIENTRY alBufferSamplesSOFT(ALuint buffer,
+  ALuint samplerate, ALenum internalformat, ALsizei samples,
+  ALenum channels, ALenum type, const ALvoid *data)
+{
+    ALCdevice *device;
+    ALCcontext *context;
+    ALbuffer *albuf;
+    ALsizei align;
+    ALenum err;
+
+    context = GetContextRef();
+    if(!context) return;
+
+    device = context->Device;
+    LockBuffersRead(device);
+    if((albuf=LookupBuffer(device, buffer)) == NULL)
+        SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
+    if(!(samples >= 0 && samplerate != 0))
+        SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+    if(IsValidType(type) == AL_FALSE || IsValidChannels(channels) == AL_FALSE)
+        SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
+
+    align = ATOMIC_LOAD(&albuf->UnpackAlign);
+    if(SanitizeAlignment(type, &align) == AL_FALSE)
+        SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+    if((samples%align) != 0)
+        SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+
+    err = LoadData(albuf, samplerate, internalformat, samples,
+                   channels, type, data, align, AL_FALSE);
+    if(err != AL_NO_ERROR)
+        SET_ERROR_AND_GOTO(context, err, done);
+
+done:
+    UnlockBuffersRead(device);
+    ALCcontext_DecRef(context);
+}
+
+AL_API void AL_APIENTRY alBufferSubSamplesSOFT(ALuint buffer,
+  ALsizei offset, ALsizei samples,
+  ALenum channels, ALenum type, const ALvoid *data)
+{
+    ALCdevice *device;
+    ALCcontext *context;
+    ALbuffer *albuf;
+    ALsizei align;
+
+    context = GetContextRef();
+    if(!context) return;
+
+    device = context->Device;
+    LockBuffersRead(device);
+    if((albuf=LookupBuffer(device, buffer)) == NULL)
+        SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
+    if(!(samples >= 0 && offset >= 0))
+        SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+    if(IsValidType(type) == AL_FALSE)
+        SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
+
+    WriteLock(&albuf->lock);
+    align = ATOMIC_LOAD(&albuf->UnpackAlign);
+    if(SanitizeAlignment(type, &align) == AL_FALSE)
+    {
+        WriteUnlock(&albuf->lock);
+        SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+    }
+    if(channels != (ALenum)albuf->FmtChannels)
+    {
+        WriteUnlock(&albuf->lock);
+        SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
+    }
+    if(offset > albuf->SampleLen || samples > albuf->SampleLen-offset)
+    {
+        WriteUnlock(&albuf->lock);
+        SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+    }
+    if((samples%align) != 0)
+    {
+        WriteUnlock(&albuf->lock);
+        SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+    }
+
+    /* offset -> byte offset */
+    offset *= FrameSizeFromFmt(albuf->FmtChannels, albuf->FmtType);
+    ConvertData((char*)albuf->data+offset, (enum UserFmtType)albuf->FmtType,
+                data, type, ChannelsFromFmt(albuf->FmtChannels), samples, align);
+    WriteUnlock(&albuf->lock);
+
+done:
+    UnlockBuffersRead(device);
+    ALCcontext_DecRef(context);
+}
+
+AL_API void AL_APIENTRY alGetBufferSamplesSOFT(ALuint buffer,
+  ALsizei offset, ALsizei samples,
+  ALenum channels, ALenum type, ALvoid *data)
+{
+    ALCdevice *device;
+    ALCcontext *context;
+    ALbuffer *albuf;
+    ALsizei align;
+
+    context = GetContextRef();
+    if(!context) return;
+
+    device = context->Device;
+    LockBuffersRead(device);
+    if((albuf=LookupBuffer(device, buffer)) == NULL)
+        SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
+    if(!(samples >= 0 && offset >= 0))
+        SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+    if(IsValidType(type) == AL_FALSE)
+        SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
+
+    ReadLock(&albuf->lock);
+    align = ATOMIC_LOAD(&albuf->PackAlign);
+    if(SanitizeAlignment(type, &align) == AL_FALSE)
+    {
+        ReadUnlock(&albuf->lock);
+        SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+    }
+    if(channels != (ALenum)albuf->FmtChannels)
+    {
+        ReadUnlock(&albuf->lock);
+        SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
+    }
+    if(offset > albuf->SampleLen || samples > albuf->SampleLen-offset)
+    {
+        ReadUnlock(&albuf->lock);
+        SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+    }
+    if((samples%align) != 0)
+    {
+        ReadUnlock(&albuf->lock);
+        SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+    }
+
+    /* offset -> byte offset */
+    offset *= FrameSizeFromFmt(albuf->FmtChannels, albuf->FmtType);
+    ConvertData(data, type, (char*)albuf->data+offset, (enum UserFmtType)albuf->FmtType,
+                ChannelsFromFmt(albuf->FmtChannels), samples, align);
+    ReadUnlock(&albuf->lock);
+
+done:
+    UnlockBuffersRead(device);
+    ALCcontext_DecRef(context);
+}
+
+AL_API ALboolean AL_APIENTRY alIsBufferFormatSupportedSOFT(ALenum format)
+{
+    enum FmtChannels dstchannels;
+    enum FmtType dsttype;
+    ALCcontext *context;
+    ALboolean ret;
+
+    context = GetContextRef();
+    if(!context) return AL_FALSE;
+
+    ret = DecomposeFormat(format, &dstchannels, &dsttype);
+
+    ALCcontext_DecRef(context);
+
+    return ret;
+}
+
+
+AL_API void AL_APIENTRY alBufferf(ALuint buffer, ALenum param, ALfloat UNUSED(value))
+{
+    ALCdevice *device;
+    ALCcontext *context;
+
+    context = GetContextRef();
+    if(!context) return;
+
+    device = context->Device;
+    LockBuffersRead(device);
+    if(LookupBuffer(device, buffer) == NULL)
+        SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
+
+    switch(param)
+    {
+    default:
+        SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
+    }
+
+done:
+    UnlockBuffersRead(device);
+    ALCcontext_DecRef(context);
+}
+
+
+AL_API void AL_APIENTRY alBuffer3f(ALuint buffer, ALenum param, ALfloat UNUSED(value1), ALfloat UNUSED(value2), ALfloat UNUSED(value3))
+{
+    ALCdevice *device;
+    ALCcontext *context;
+
+    context = GetContextRef();
+    if(!context) return;
+
+    device = context->Device;
+    LockBuffersRead(device);
+    if(LookupBuffer(device, buffer) == NULL)
+        SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
+
+    switch(param)
+    {
+    default:
+        SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
+    }
+
+done:
+    UnlockBuffersRead(device);
+    ALCcontext_DecRef(context);
+}
+
+
+AL_API void AL_APIENTRY alBufferfv(ALuint buffer, ALenum param, const ALfloat *values)
+{
+    ALCdevice *device;
+    ALCcontext *context;
+
+    context = GetContextRef();
+    if(!context) return;
+
+    device = context->Device;
+    LockBuffersRead(device);
+    if(LookupBuffer(device, buffer) == NULL)
+        SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
+
+    if(!(values))
+        SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+    switch(param)
+    {
+    default:
+        SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
+    }
+
+done:
+    UnlockBuffersRead(device);
+    ALCcontext_DecRef(context);
+}
+
+
+AL_API void AL_APIENTRY alBufferi(ALuint buffer, ALenum param, ALint value)
+{
+    ALCdevice *device;
+    ALCcontext *context;
+    ALbuffer *albuf;
+
+    context = GetContextRef();
+    if(!context) return;
+
+    device = context->Device;
+    LockBuffersRead(device);
+    if((albuf=LookupBuffer(device, buffer)) == NULL)
+        SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
+
+    switch(param)
+    {
+    case AL_UNPACK_BLOCK_ALIGNMENT_SOFT:
+        if(!(value >= 0))
+            SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+        ATOMIC_STORE(&albuf->UnpackAlign, value);
+        break;
+
+    case AL_PACK_BLOCK_ALIGNMENT_SOFT:
+        if(!(value >= 0))
+            SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+        ATOMIC_STORE(&albuf->PackAlign, value);
+        break;
+
+    default:
+        SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
+    }
+
+done:
+    UnlockBuffersRead(device);
+    ALCcontext_DecRef(context);
+}
+
+
+AL_API void AL_APIENTRY alBuffer3i(ALuint buffer, ALenum param, ALint UNUSED(value1), ALint UNUSED(value2), ALint UNUSED(value3))
+{
+    ALCdevice *device;
+    ALCcontext *context;
+
+    context = GetContextRef();
+    if(!context) return;
+
+    device = context->Device;
+    if(LookupBuffer(device, buffer) == NULL)
+        SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
+
+    switch(param)
+    {
+    default:
+        SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
+    }
+
+done:
+    ALCcontext_DecRef(context);
+}
+
+
+AL_API void AL_APIENTRY alBufferiv(ALuint buffer, ALenum param, const ALint *values)
+{
+    ALCdevice *device;
+    ALCcontext *context;
+    ALbuffer *albuf;
+
+    if(values)
+    {
+        switch(param)
+        {
+            case AL_UNPACK_BLOCK_ALIGNMENT_SOFT:
+            case AL_PACK_BLOCK_ALIGNMENT_SOFT:
+                alBufferi(buffer, param, values[0]);
+                return;
+        }
+    }
+
+    context = GetContextRef();
+    if(!context) return;
+
+    device = context->Device;
+    LockBuffersRead(device);
+    if((albuf=LookupBuffer(device, buffer)) == NULL)
+        SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
+
+    if(!(values))
+        SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+    switch(param)
+    {
+    case AL_LOOP_POINTS_SOFT:
+        WriteLock(&albuf->lock);
+        if(ReadRef(&albuf->ref) != 0)
+        {
+            WriteUnlock(&albuf->lock);
+            SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done);
+        }
+        if(values[0] >= values[1] || values[0] < 0 ||
+           values[1] > albuf->SampleLen)
+        {
+            WriteUnlock(&albuf->lock);
+            SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+        }
+
+        albuf->LoopStart = values[0];
+        albuf->LoopEnd = values[1];
+        WriteUnlock(&albuf->lock);
+        break;
+
+    default:
+        SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
+    }
+
+done:
+    UnlockBuffersRead(device);
+    ALCcontext_DecRef(context);
+}
+
+
+AL_API ALvoid AL_APIENTRY alGetBufferf(ALuint buffer, ALenum param, ALfloat *value)
+{
+    ALCdevice *device;
+    ALCcontext *context;
+    ALbuffer *albuf;
+
+    context = GetContextRef();
+    if(!context) return;
+
+    device = context->Device;
+    LockBuffersRead(device);
+    if((albuf=LookupBuffer(device, buffer)) == NULL)
+        SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
+
+    if(!(value))
+        SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+    switch(param)
+    {
+    case AL_SEC_LENGTH_SOFT:
+        ReadLock(&albuf->lock);
+        if(albuf->SampleLen != 0)
+            *value = albuf->SampleLen / (ALfloat)albuf->Frequency;
+        else
+            *value = 0.0f;
+        ReadUnlock(&albuf->lock);
+        break;
+
+    default:
+        SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
+    }
+
+done:
+    UnlockBuffersRead(device);
+    ALCcontext_DecRef(context);
+}
+
+
+AL_API void AL_APIENTRY alGetBuffer3f(ALuint buffer, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3)
+{
+    ALCdevice *device;
+    ALCcontext *context;
+
+    context = GetContextRef();
+    if(!context) return;
+
+    device = context->Device;
+    LockBuffersRead(device);
+    if(LookupBuffer(device, buffer) == NULL)
+        SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
+
+    if(!(value1 && value2 && value3))
+        SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+    switch(param)
+    {
+    default:
+        SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
+    }
+
+done:
+    UnlockBuffersRead(device);
+    ALCcontext_DecRef(context);
+}
+
+
+AL_API void AL_APIENTRY alGetBufferfv(ALuint buffer, ALenum param, ALfloat *values)
+{
+    ALCdevice *device;
+    ALCcontext *context;
+
+    switch(param)
+    {
+    case AL_SEC_LENGTH_SOFT:
+        alGetBufferf(buffer, param, values);
+        return;
+    }
+
+    context = GetContextRef();
+    if(!context) return;
+
+    device = context->Device;
+    LockBuffersRead(device);
+    if(LookupBuffer(device, buffer) == NULL)
+        SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
+
+    if(!(values))
+        SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+    switch(param)
+    {
+    default:
+        SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
+    }
+
+done:
+    UnlockBuffersRead(device);
+    ALCcontext_DecRef(context);
+}
+
+
+AL_API ALvoid AL_APIENTRY alGetBufferi(ALuint buffer, ALenum param, ALint *value)
+{
+    ALCdevice *device;
+    ALCcontext *context;
+    ALbuffer *albuf;
+
+    context = GetContextRef();
+    if(!context) return;
+
+    device = context->Device;
+    LockBuffersRead(device);
+    if((albuf=LookupBuffer(device, buffer)) == NULL)
+        SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
+
+    if(!(value))
+        SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+    switch(param)
+    {
+    case AL_FREQUENCY:
+        *value = albuf->Frequency;
+        break;
+
+    case AL_BITS:
+        *value = BytesFromFmt(albuf->FmtType) * 8;
+        break;
+
+    case AL_CHANNELS:
+        *value = ChannelsFromFmt(albuf->FmtChannels);
+        break;
+
+    case AL_SIZE:
+        ReadLock(&albuf->lock);
+        *value = albuf->SampleLen * FrameSizeFromFmt(albuf->FmtChannels,
+                                                     albuf->FmtType);
+        ReadUnlock(&albuf->lock);
+        break;
+
+    case AL_INTERNAL_FORMAT_SOFT:
+        *value = albuf->Format;
+        break;
+
+    case AL_BYTE_LENGTH_SOFT:
+        *value = albuf->OriginalSize;
+        break;
+
+    case AL_SAMPLE_LENGTH_SOFT:
+        *value = albuf->SampleLen;
+        break;
+
+    case AL_UNPACK_BLOCK_ALIGNMENT_SOFT:
+        *value = ATOMIC_LOAD(&albuf->UnpackAlign);
+        break;
+
+    case AL_PACK_BLOCK_ALIGNMENT_SOFT:
+        *value = ATOMIC_LOAD(&albuf->PackAlign);
+        break;
+
+    default:
+        SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
+    }
+
+done:
+    UnlockBuffersRead(device);
+    ALCcontext_DecRef(context);
+}
+
+
+AL_API void AL_APIENTRY alGetBuffer3i(ALuint buffer, ALenum param, ALint *value1, ALint *value2, ALint *value3)
+{
+    ALCdevice *device;
+    ALCcontext *context;
+
+    context = GetContextRef();
+    if(!context) return;
+
+    device = context->Device;
+    LockBuffersRead(device);
+    if(LookupBuffer(device, buffer) == NULL)
+        SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
+
+    if(!(value1 && value2 && value3))
+        SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+    switch(param)
+    {
+    default:
+        SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
+    }
+
+done:
+    UnlockBuffersRead(device);
+    ALCcontext_DecRef(context);
+}
+
+
+AL_API void AL_APIENTRY alGetBufferiv(ALuint buffer, ALenum param, ALint *values)
+{
+    ALCdevice *device;
+    ALCcontext *context;
+    ALbuffer   *albuf;
+
+    switch(param)
+    {
+    case AL_FREQUENCY:
+    case AL_BITS:
+    case AL_CHANNELS:
+    case AL_SIZE:
+    case AL_INTERNAL_FORMAT_SOFT:
+    case AL_BYTE_LENGTH_SOFT:
+    case AL_SAMPLE_LENGTH_SOFT:
+    case AL_UNPACK_BLOCK_ALIGNMENT_SOFT:
+    case AL_PACK_BLOCK_ALIGNMENT_SOFT:
+        alGetBufferi(buffer, param, values);
+        return;
+    }
+
+    context = GetContextRef();
+    if(!context) return;
+
+    device = context->Device;
+    LockBuffersRead(device);
+    if((albuf=LookupBuffer(device, buffer)) == NULL)
+        SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
+
+    if(!(values))
+        SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+    switch(param)
+    {
+    case AL_LOOP_POINTS_SOFT:
+        ReadLock(&albuf->lock);
+        values[0] = albuf->LoopStart;
+        values[1] = albuf->LoopEnd;
+        ReadUnlock(&albuf->lock);
+        break;
+
+    default:
+        SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
+    }
+
+done:
+    UnlockBuffersRead(device);
+    ALCcontext_DecRef(context);
+}
+
+
+/*
+ * LoadData
+ *
+ * Loads the specified data into the buffer, using the specified formats.
+ * Currently, the new format must have the same channel configuration as the
+ * original format.
+ */
+ALenum LoadData(ALbuffer *ALBuf, ALuint freq, ALenum NewFormat, ALsizei frames, enum UserFmtChannels SrcChannels, enum UserFmtType SrcType, const ALvoid *data, ALsizei align, ALboolean storesrc)
+{
+    enum FmtChannels DstChannels = FmtMono;
+    enum FmtType DstType = FmtByte;
+    ALuint NewChannels, NewBytes;
+    ALuint64 newsize;
+
+    if(DecomposeFormat(NewFormat, &DstChannels, &DstType) == AL_FALSE)
+        return AL_INVALID_ENUM;
+    if((long)SrcChannels != (long)DstChannels)
+        return AL_INVALID_ENUM;
+
+    NewChannels = ChannelsFromFmt(DstChannels);
+    NewBytes = BytesFromFmt(DstType);
+
+    newsize = frames;
+    newsize *= NewBytes;
+    newsize *= NewChannels;
+    if(newsize > INT_MAX)
+        return AL_OUT_OF_MEMORY;
+
+    WriteLock(&ALBuf->lock);
+    if(ReadRef(&ALBuf->ref) != 0)
+    {
+        WriteUnlock(&ALBuf->lock);
+        return AL_INVALID_OPERATION;
+    }
+
+    /* Round up to the next 16-byte multiple. This could reallocate only when
+     * increasing or the new size is less than half the current, but then the
+     * buffer's AL_SIZE would not be very reliable for accounting buffer memory
+     * usage, and reporting the real size could cause problems for apps that
+     * use AL_SIZE to try to get the buffer's play length.
+     */
+    newsize = (newsize+15) & ~0xf;
+    if(newsize != ALBuf->BytesAlloc)
+    {
+        void *temp = al_calloc(16, (size_t)newsize);
+        if(!temp && newsize)
+        {
+            WriteUnlock(&ALBuf->lock);
+            return AL_OUT_OF_MEMORY;
+        }
+        al_free(ALBuf->data);
+        ALBuf->data = temp;
+        ALBuf->BytesAlloc = (ALuint)newsize;
+    }
+
+    if(data != NULL)
+        ConvertData(ALBuf->data, (enum UserFmtType)DstType, data, SrcType, NewChannels, frames, align);
+
+    if(storesrc)
+    {
+        ALBuf->OriginalChannels = SrcChannels;
+        ALBuf->OriginalType     = SrcType;
+        if(SrcType == UserFmtIMA4)
+        {
+            ALsizei byte_align = ((align-1)/2 + 4) * ChannelsFromUserFmt(SrcChannels);
+            ALBuf->OriginalSize  = frames / align * byte_align;
+            ALBuf->OriginalAlign = align;
+        }
+        else if(SrcType == UserFmtMSADPCM)
+        {
+            ALsizei byte_align = ((align-2)/2 + 7) * ChannelsFromUserFmt(SrcChannels);
+            ALBuf->OriginalSize  = frames / align * byte_align;
+            ALBuf->OriginalAlign = align;
+        }
+        else
+        {
+            ALBuf->OriginalSize  = frames * FrameSizeFromUserFmt(SrcChannels, SrcType);
+            ALBuf->OriginalAlign = 1;
+        }
+    }
+    else
+    {
+        ALBuf->OriginalChannels = (enum UserFmtChannels)DstChannels;
+        ALBuf->OriginalType     = (enum UserFmtType)DstType;
+        ALBuf->OriginalSize     = frames * NewBytes * NewChannels;
+        ALBuf->OriginalAlign    = 1;
+    }
+
+    ALBuf->Frequency = freq;
+    ALBuf->FmtChannels = DstChannels;
+    ALBuf->FmtType = DstType;
+    ALBuf->Format = NewFormat;
+
+    ALBuf->SampleLen = frames;
+    ALBuf->LoopStart = 0;
+    ALBuf->LoopEnd = ALBuf->SampleLen;
+
+    WriteUnlock(&ALBuf->lock);
+    return AL_NO_ERROR;
+}
+
+
+ALuint BytesFromUserFmt(enum UserFmtType type)
+{
+    switch(type)
+    {
+    case UserFmtByte: return sizeof(ALbyte);
+    case UserFmtUByte: return sizeof(ALubyte);
+    case UserFmtShort: return sizeof(ALshort);
+    case UserFmtUShort: return sizeof(ALushort);
+    case UserFmtInt: return sizeof(ALint);
+    case UserFmtUInt: return sizeof(ALuint);
+    case UserFmtFloat: return sizeof(ALfloat);
+    case UserFmtDouble: return sizeof(ALdouble);
+    case UserFmtByte3: return sizeof(ALbyte[3]);
+    case UserFmtUByte3: return sizeof(ALubyte[3]);
+    case UserFmtMulaw: return sizeof(ALubyte);
+    case UserFmtAlaw: return sizeof(ALubyte);
+    case UserFmtIMA4: break; /* not handled here */
+    case UserFmtMSADPCM: break; /* not handled here */
+    }
+    return 0;
+}
+ALuint ChannelsFromUserFmt(enum UserFmtChannels chans)
+{
+    switch(chans)
+    {
+    case UserFmtMono: return 1;
+    case UserFmtStereo: return 2;
+    case UserFmtRear: return 2;
+    case UserFmtQuad: return 4;
+    case UserFmtX51: return 6;
+    case UserFmtX61: return 7;
+    case UserFmtX71: return 8;
+    case UserFmtBFormat2D: return 3;
+    case UserFmtBFormat3D: return 4;
+    }
+    return 0;
+}
+static ALboolean DecomposeUserFormat(ALenum format, enum UserFmtChannels *chans,
+                                     enum UserFmtType *type)
+{
+    static const struct {
+        ALenum format;
+        enum UserFmtChannels channels;
+        enum UserFmtType type;
+    } list[] = {
+        { AL_FORMAT_MONO8,             UserFmtMono, UserFmtUByte   },
+        { AL_FORMAT_MONO16,            UserFmtMono, UserFmtShort   },
+        { AL_FORMAT_MONO_FLOAT32,      UserFmtMono, UserFmtFloat   },
+        { AL_FORMAT_MONO_DOUBLE_EXT,   UserFmtMono, UserFmtDouble  },
+        { AL_FORMAT_MONO_IMA4,         UserFmtMono, UserFmtIMA4    },
+        { AL_FORMAT_MONO_MSADPCM_SOFT, UserFmtMono, UserFmtMSADPCM },
+        { AL_FORMAT_MONO_MULAW,        UserFmtMono, UserFmtMulaw   },
+        { AL_FORMAT_MONO_ALAW_EXT,     UserFmtMono, UserFmtAlaw    },
+
+        { AL_FORMAT_STEREO8,             UserFmtStereo, UserFmtUByte   },
+        { AL_FORMAT_STEREO16,            UserFmtStereo, UserFmtShort   },
+        { AL_FORMAT_STEREO_FLOAT32,      UserFmtStereo, UserFmtFloat   },
+        { AL_FORMAT_STEREO_DOUBLE_EXT,   UserFmtStereo, UserFmtDouble  },
+        { AL_FORMAT_STEREO_IMA4,         UserFmtStereo, UserFmtIMA4    },
+        { AL_FORMAT_STEREO_MSADPCM_SOFT, UserFmtStereo, UserFmtMSADPCM },
+        { AL_FORMAT_STEREO_MULAW,        UserFmtStereo, UserFmtMulaw   },
+        { AL_FORMAT_STEREO_ALAW_EXT,     UserFmtStereo, UserFmtAlaw    },
+
+        { AL_FORMAT_REAR8,      UserFmtRear, UserFmtUByte },
+        { AL_FORMAT_REAR16,     UserFmtRear, UserFmtShort },
+        { AL_FORMAT_REAR32,     UserFmtRear, UserFmtFloat },
+        { AL_FORMAT_REAR_MULAW, UserFmtRear, UserFmtMulaw },
+
+        { AL_FORMAT_QUAD8_LOKI,  UserFmtQuad, UserFmtUByte },
+        { AL_FORMAT_QUAD16_LOKI, UserFmtQuad, UserFmtShort },
+
+        { AL_FORMAT_QUAD8,      UserFmtQuad, UserFmtUByte },
+        { AL_FORMAT_QUAD16,     UserFmtQuad, UserFmtShort },
+        { AL_FORMAT_QUAD32,     UserFmtQuad, UserFmtFloat },
+        { AL_FORMAT_QUAD_MULAW, UserFmtQuad, UserFmtMulaw },
+
+        { AL_FORMAT_51CHN8,      UserFmtX51, UserFmtUByte },
+        { AL_FORMAT_51CHN16,     UserFmtX51, UserFmtShort },
+        { AL_FORMAT_51CHN32,     UserFmtX51, UserFmtFloat },
+        { AL_FORMAT_51CHN_MULAW, UserFmtX51, UserFmtMulaw },
+
+        { AL_FORMAT_61CHN8,      UserFmtX61, UserFmtUByte },
+        { AL_FORMAT_61CHN16,     UserFmtX61, UserFmtShort },
+        { AL_FORMAT_61CHN32,     UserFmtX61, UserFmtFloat },
+        { AL_FORMAT_61CHN_MULAW, UserFmtX61, UserFmtMulaw },
+
+        { AL_FORMAT_71CHN8,      UserFmtX71, UserFmtUByte },
+        { AL_FORMAT_71CHN16,     UserFmtX71, UserFmtShort },
+        { AL_FORMAT_71CHN32,     UserFmtX71, UserFmtFloat },
+        { AL_FORMAT_71CHN_MULAW, UserFmtX71, UserFmtMulaw },
+
+        { AL_FORMAT_BFORMAT2D_8,       UserFmtBFormat2D, UserFmtUByte },
+        { AL_FORMAT_BFORMAT2D_16,      UserFmtBFormat2D, UserFmtShort },
+        { AL_FORMAT_BFORMAT2D_FLOAT32, UserFmtBFormat2D, UserFmtFloat },
+        { AL_FORMAT_BFORMAT2D_MULAW,   UserFmtBFormat2D, UserFmtMulaw },
+
+        { AL_FORMAT_BFORMAT3D_8,       UserFmtBFormat3D, UserFmtUByte },
+        { AL_FORMAT_BFORMAT3D_16,      UserFmtBFormat3D, UserFmtShort },
+        { AL_FORMAT_BFORMAT3D_FLOAT32, UserFmtBFormat3D, UserFmtFloat },
+        { AL_FORMAT_BFORMAT3D_MULAW,   UserFmtBFormat3D, UserFmtMulaw },
+    };
+    ALuint i;
+
+    for(i = 0;i < COUNTOF(list);i++)
+    {
+        if(list[i].format == format)
+        {
+            *chans = list[i].channels;
+            *type  = list[i].type;
+            return AL_TRUE;
+        }
+    }
+
+    return AL_FALSE;
+}
+
+ALuint BytesFromFmt(enum FmtType type)
+{
+    switch(type)
+    {
+    case FmtByte: return sizeof(ALbyte);
+    case FmtShort: return sizeof(ALshort);
+    case FmtFloat: return sizeof(ALfloat);
+    }
+    return 0;
+}
+ALuint ChannelsFromFmt(enum FmtChannels chans)
+{
+    switch(chans)
+    {
+    case FmtMono: return 1;
+    case FmtStereo: return 2;
+    case FmtRear: return 2;
+    case FmtQuad: return 4;
+    case FmtX51: return 6;
+    case FmtX61: return 7;
+    case FmtX71: return 8;
+    case FmtBFormat2D: return 3;
+    case FmtBFormat3D: return 4;
+    }
+    return 0;
+}
+static ALboolean DecomposeFormat(ALenum format, enum FmtChannels *chans, enum FmtType *type)
+{
+    static const struct {
+        ALenum format;
+        enum FmtChannels channels;
+        enum FmtType type;
+    } list[] = {
+        { AL_MONO8_SOFT,   FmtMono, FmtByte  },
+        { AL_MONO16_SOFT,  FmtMono, FmtShort },
+        { AL_MONO32F_SOFT, FmtMono, FmtFloat },
+
+        { AL_STEREO8_SOFT,   FmtStereo, FmtByte  },
+        { AL_STEREO16_SOFT,  FmtStereo, FmtShort },
+        { AL_STEREO32F_SOFT, FmtStereo, FmtFloat },
+
+        { AL_REAR8_SOFT,   FmtRear, FmtByte  },
+        { AL_REAR16_SOFT,  FmtRear, FmtShort },
+        { AL_REAR32F_SOFT, FmtRear, FmtFloat },
+
+        { AL_FORMAT_QUAD8_LOKI,  FmtQuad, FmtByte  },
+        { AL_FORMAT_QUAD16_LOKI, FmtQuad, FmtShort },
+
+        { AL_QUAD8_SOFT,   FmtQuad, FmtByte  },
+        { AL_QUAD16_SOFT,  FmtQuad, FmtShort },
+        { AL_QUAD32F_SOFT, FmtQuad, FmtFloat },
+
+        { AL_5POINT1_8_SOFT,   FmtX51, FmtByte  },
+        { AL_5POINT1_16_SOFT,  FmtX51, FmtShort },
+        { AL_5POINT1_32F_SOFT, FmtX51, FmtFloat },
+
+        { AL_6POINT1_8_SOFT,   FmtX61, FmtByte  },
+        { AL_6POINT1_16_SOFT,  FmtX61, FmtShort },
+        { AL_6POINT1_32F_SOFT, FmtX61, FmtFloat },
+
+        { AL_7POINT1_8_SOFT,   FmtX71, FmtByte  },
+        { AL_7POINT1_16_SOFT,  FmtX71, FmtShort },
+        { AL_7POINT1_32F_SOFT, FmtX71, FmtFloat },
+
+        { AL_BFORMAT2D_8_SOFT,   FmtBFormat2D, FmtByte },
+        { AL_BFORMAT2D_16_SOFT,  FmtBFormat2D, FmtShort },
+        { AL_BFORMAT2D_32F_SOFT, FmtBFormat2D, FmtFloat },
+
+        { AL_BFORMAT3D_8_SOFT,   FmtBFormat3D, FmtByte },
+        { AL_BFORMAT3D_16_SOFT,  FmtBFormat3D, FmtShort },
+        { AL_BFORMAT3D_32F_SOFT, FmtBFormat3D, FmtFloat },
+    };
+    ALuint i;
+
+    for(i = 0;i < COUNTOF(list);i++)
+    {
+        if(list[i].format == format)
+        {
+            *chans = list[i].channels;
+            *type  = list[i].type;
+            return AL_TRUE;
+        }
+    }
+
+    return AL_FALSE;
+}
+
+static ALboolean SanitizeAlignment(enum UserFmtType type, ALsizei *align)
+{
+    if(*align < 0)
+        return AL_FALSE;
+
+    if(*align == 0)
+    {
+        if(type == UserFmtIMA4)
+        {
+            /* Here is where things vary:
+             * nVidia and Apple use 64+1 sample frames per block -> block_size=36 bytes per channel
+             * Most PC sound software uses 2040+1 sample frames per block -> block_size=1024 bytes per channel
+             */
+            *align = 65;
+        }
+        else if(type == UserFmtMSADPCM)
+            *align = 64;
+        else
+            *align = 1;
+        return AL_TRUE;
+    }
+
+    if(type == UserFmtIMA4)
+    {
+        /* IMA4 block alignment must be a multiple of 8, plus 1. */
+        return ((*align)&7) == 1;
+    }
+    if(type == UserFmtMSADPCM)
+    {
+        /* MSADPCM block alignment must be a multiple of 2. */
+        /* FIXME: Too strict? Might only require align*channels to be a
+         * multiple of 2. */
+        return ((*align)&1) == 0;
+    }
+
+    return AL_TRUE;
+}
+
+
+static ALboolean IsValidType(ALenum type)
+{
+    switch(type)
+    {
+        case AL_BYTE_SOFT:
+        case AL_UNSIGNED_BYTE_SOFT:
+        case AL_SHORT_SOFT:
+        case AL_UNSIGNED_SHORT_SOFT:
+        case AL_INT_SOFT:
+        case AL_UNSIGNED_INT_SOFT:
+        case AL_FLOAT_SOFT:
+        case AL_DOUBLE_SOFT:
+        case AL_BYTE3_SOFT:
+        case AL_UNSIGNED_BYTE3_SOFT:
+        case AL_MULAW_SOFT:
+            return AL_TRUE;
+    }
+    return AL_FALSE;
+}
+
+static ALboolean IsValidChannels(ALenum channels)
+{
+    switch(channels)
+    {
+        case AL_MONO_SOFT:
+        case AL_STEREO_SOFT:
+        case AL_REAR_SOFT:
+        case AL_QUAD_SOFT:
+        case AL_5POINT1_SOFT:
+        case AL_6POINT1_SOFT:
+        case AL_7POINT1_SOFT:
+        case AL_BFORMAT2D_SOFT:
+        case AL_BFORMAT3D_SOFT:
+            return AL_TRUE;
+    }
+    return AL_FALSE;
+}
+
+
+ALbuffer *NewBuffer(ALCcontext *context)
+{
+    ALCdevice *device = context->Device;
+    ALbuffer *buffer;
+    ALenum err;
+
+    buffer = al_calloc(16, sizeof(ALbuffer));
+    if(!buffer)
+        SET_ERROR_AND_RETURN_VALUE(context, AL_OUT_OF_MEMORY, NULL);
+    RWLockInit(&buffer->lock);
+
+    err = NewThunkEntry(&buffer->id);
+    if(err == AL_NO_ERROR)
+        err = InsertUIntMapEntry(&device->BufferMap, buffer->id, buffer);
+    if(err != AL_NO_ERROR)
+    {
+        FreeThunkEntry(buffer->id);
+        memset(buffer, 0, sizeof(ALbuffer));
+        al_free(buffer);
+
+        SET_ERROR_AND_RETURN_VALUE(context, err, NULL);
+    }
+
+    return buffer;
+}
+
+void DeleteBuffer(ALCdevice *device, ALbuffer *buffer)
+{
+    RemoveBuffer(device, buffer->id);
+    FreeThunkEntry(buffer->id);
+
+    al_free(buffer->data);
+
+    memset(buffer, 0, sizeof(*buffer));
+    al_free(buffer);
+}
+
+
+/*
+ *    ReleaseALBuffers()
+ *
+ *    INTERNAL: Called to destroy any buffers that still exist on the device
+ */
+ALvoid ReleaseALBuffers(ALCdevice *device)
+{
+    ALsizei i;
+    for(i = 0;i < device->BufferMap.size;i++)
+    {
+        ALbuffer *temp = device->BufferMap.values[i];
+        device->BufferMap.values[i] = NULL;
+
+        al_free(temp->data);
+
+        FreeThunkEntry(temp->id);
+        memset(temp, 0, sizeof(ALbuffer));
+        al_free(temp);
+    }
+}

+ 716 - 0
Engine/lib/openal-soft/OpenAL32/alEffect.c

@@ -0,0 +1,716 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 1999-2007 by authors.
+ * This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the
+ *  Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <math.h>
+#include <float.h>
+
+#include "AL/al.h"
+#include "AL/alc.h"
+#include "alMain.h"
+#include "alEffect.h"
+#include "alThunk.h"
+#include "alError.h"
+
+
+ALboolean DisabledEffects[MAX_EFFECTS];
+
+extern inline void LockEffectsRead(ALCdevice *device);
+extern inline void UnlockEffectsRead(ALCdevice *device);
+extern inline void LockEffectsWrite(ALCdevice *device);
+extern inline void UnlockEffectsWrite(ALCdevice *device);
+extern inline struct ALeffect *LookupEffect(ALCdevice *device, ALuint id);
+extern inline struct ALeffect *RemoveEffect(ALCdevice *device, ALuint id);
+extern inline ALboolean IsReverbEffect(ALenum type);
+
+static void InitEffectParams(ALeffect *effect, ALenum type);
+
+
+AL_API ALvoid AL_APIENTRY alGenEffects(ALsizei n, ALuint *effects)
+{
+    ALCdevice *device;
+    ALCcontext *context;
+    ALsizei cur;
+
+    context = GetContextRef();
+    if(!context) return;
+
+    if(!(n >= 0))
+        SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+
+    device = context->Device;
+    for(cur = 0;cur < n;cur++)
+    {
+        ALeffect *effect = al_calloc(16, sizeof(ALeffect));
+        ALenum err = AL_OUT_OF_MEMORY;
+        if(!effect || (err=InitEffect(effect)) != AL_NO_ERROR)
+        {
+            al_free(effect);
+            alDeleteEffects(cur, effects);
+            SET_ERROR_AND_GOTO(context, err, done);
+        }
+
+        err = NewThunkEntry(&effect->id);
+        if(err == AL_NO_ERROR)
+            err = InsertUIntMapEntry(&device->EffectMap, effect->id, effect);
+        if(err != AL_NO_ERROR)
+        {
+            FreeThunkEntry(effect->id);
+            memset(effect, 0, sizeof(ALeffect));
+            al_free(effect);
+
+            alDeleteEffects(cur, effects);
+            SET_ERROR_AND_GOTO(context, err, done);
+        }
+
+        effects[cur] = effect->id;
+    }
+
+done:
+    ALCcontext_DecRef(context);
+}
+
+AL_API ALvoid AL_APIENTRY alDeleteEffects(ALsizei n, const ALuint *effects)
+{
+    ALCdevice *device;
+    ALCcontext *context;
+    ALeffect *effect;
+    ALsizei i;
+
+    context = GetContextRef();
+    if(!context) return;
+
+    device = context->Device;
+    LockEffectsWrite(device);
+    if(!(n >= 0))
+        SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+    for(i = 0;i < n;i++)
+    {
+        if(effects[i] && LookupEffect(device, effects[i]) == NULL)
+            SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
+    }
+    for(i = 0;i < n;i++)
+    {
+        if((effect=RemoveEffect(device, effects[i])) == NULL)
+            continue;
+        FreeThunkEntry(effect->id);
+
+        memset(effect, 0, sizeof(*effect));
+        al_free(effect);
+    }
+
+done:
+    UnlockEffectsWrite(device);
+    ALCcontext_DecRef(context);
+}
+
+AL_API ALboolean AL_APIENTRY alIsEffect(ALuint effect)
+{
+    ALCcontext *Context;
+    ALboolean  result;
+
+    Context = GetContextRef();
+    if(!Context) return AL_FALSE;
+
+    LockEffectsRead(Context->Device);
+    result = ((!effect || LookupEffect(Context->Device, effect)) ?
+              AL_TRUE : AL_FALSE);
+    UnlockEffectsRead(Context->Device);
+
+    ALCcontext_DecRef(Context);
+
+    return result;
+}
+
+AL_API ALvoid AL_APIENTRY alEffecti(ALuint effect, ALenum param, ALint value)
+{
+    ALCcontext *Context;
+    ALCdevice  *Device;
+    ALeffect   *ALEffect;
+
+    Context = GetContextRef();
+    if(!Context) return;
+
+    Device = Context->Device;
+    LockEffectsWrite(Device);
+    if((ALEffect=LookupEffect(Device, effect)) == NULL)
+        alSetError(Context, AL_INVALID_NAME);
+    else
+    {
+        if(param == AL_EFFECT_TYPE)
+        {
+            ALboolean isOk = (value == AL_EFFECT_NULL);
+            ALint i;
+            for(i = 0;!isOk && EffectList[i].val;i++)
+            {
+                if(value == EffectList[i].val &&
+                   !DisabledEffects[EffectList[i].type])
+                    isOk = AL_TRUE;
+            }
+
+            if(isOk)
+                InitEffectParams(ALEffect, value);
+            else
+                alSetError(Context, AL_INVALID_VALUE);
+        }
+        else
+        {
+            /* Call the appropriate handler */
+            V(ALEffect,setParami)(Context, param, value);
+        }
+    }
+    UnlockEffectsWrite(Device);
+
+    ALCcontext_DecRef(Context);
+}
+
+AL_API ALvoid AL_APIENTRY alEffectiv(ALuint effect, ALenum param, const ALint *values)
+{
+    ALCcontext *Context;
+    ALCdevice  *Device;
+    ALeffect   *ALEffect;
+
+    switch(param)
+    {
+        case AL_EFFECT_TYPE:
+            alEffecti(effect, param, values[0]);
+            return;
+    }
+
+    Context = GetContextRef();
+    if(!Context) return;
+
+    Device = Context->Device;
+    LockEffectsWrite(Device);
+    if((ALEffect=LookupEffect(Device, effect)) == NULL)
+        alSetError(Context, AL_INVALID_NAME);
+    else
+    {
+        /* Call the appropriate handler */
+        V(ALEffect,setParamiv)(Context, param, values);
+    }
+    UnlockEffectsWrite(Device);
+
+    ALCcontext_DecRef(Context);
+}
+
+AL_API ALvoid AL_APIENTRY alEffectf(ALuint effect, ALenum param, ALfloat value)
+{
+    ALCcontext *Context;
+    ALCdevice  *Device;
+    ALeffect   *ALEffect;
+
+    Context = GetContextRef();
+    if(!Context) return;
+
+    Device = Context->Device;
+    LockEffectsWrite(Device);
+    if((ALEffect=LookupEffect(Device, effect)) == NULL)
+        alSetError(Context, AL_INVALID_NAME);
+    else
+    {
+        /* Call the appropriate handler */
+        V(ALEffect,setParamf)(Context, param, value);
+    }
+    UnlockEffectsWrite(Device);
+
+    ALCcontext_DecRef(Context);
+}
+
+AL_API ALvoid AL_APIENTRY alEffectfv(ALuint effect, ALenum param, const ALfloat *values)
+{
+    ALCcontext *Context;
+    ALCdevice  *Device;
+    ALeffect   *ALEffect;
+
+    Context = GetContextRef();
+    if(!Context) return;
+
+    Device = Context->Device;
+    LockEffectsWrite(Device);
+    if((ALEffect=LookupEffect(Device, effect)) == NULL)
+        alSetError(Context, AL_INVALID_NAME);
+    else
+    {
+        /* Call the appropriate handler */
+        V(ALEffect,setParamfv)(Context, param, values);
+    }
+    UnlockEffectsWrite(Device);
+
+    ALCcontext_DecRef(Context);
+}
+
+AL_API ALvoid AL_APIENTRY alGetEffecti(ALuint effect, ALenum param, ALint *value)
+{
+    ALCcontext *Context;
+    ALCdevice  *Device;
+    ALeffect   *ALEffect;
+
+    Context = GetContextRef();
+    if(!Context) return;
+
+    Device = Context->Device;
+    LockEffectsRead(Device);
+    if((ALEffect=LookupEffect(Device, effect)) == NULL)
+        alSetError(Context, AL_INVALID_NAME);
+    else
+    {
+        if(param == AL_EFFECT_TYPE)
+            *value = ALEffect->type;
+        else
+        {
+            /* Call the appropriate handler */
+            V(ALEffect,getParami)(Context, param, value);
+        }
+    }
+    UnlockEffectsRead(Device);
+
+    ALCcontext_DecRef(Context);
+}
+
+AL_API ALvoid AL_APIENTRY alGetEffectiv(ALuint effect, ALenum param, ALint *values)
+{
+    ALCcontext *Context;
+    ALCdevice  *Device;
+    ALeffect   *ALEffect;
+
+    switch(param)
+    {
+        case AL_EFFECT_TYPE:
+            alGetEffecti(effect, param, values);
+            return;
+    }
+
+    Context = GetContextRef();
+    if(!Context) return;
+
+    Device = Context->Device;
+    LockEffectsRead(Device);
+    if((ALEffect=LookupEffect(Device, effect)) == NULL)
+        alSetError(Context, AL_INVALID_NAME);
+    else
+    {
+        /* Call the appropriate handler */
+        V(ALEffect,getParamiv)(Context, param, values);
+    }
+    UnlockEffectsRead(Device);
+
+    ALCcontext_DecRef(Context);
+}
+
+AL_API ALvoid AL_APIENTRY alGetEffectf(ALuint effect, ALenum param, ALfloat *value)
+{
+    ALCcontext *Context;
+    ALCdevice  *Device;
+    ALeffect   *ALEffect;
+
+    Context = GetContextRef();
+    if(!Context) return;
+
+    Device = Context->Device;
+    LockEffectsRead(Device);
+    if((ALEffect=LookupEffect(Device, effect)) == NULL)
+        alSetError(Context, AL_INVALID_NAME);
+    else
+    {
+        /* Call the appropriate handler */
+        V(ALEffect,getParamf)(Context, param, value);
+    }
+    UnlockEffectsRead(Device);
+
+    ALCcontext_DecRef(Context);
+}
+
+AL_API ALvoid AL_APIENTRY alGetEffectfv(ALuint effect, ALenum param, ALfloat *values)
+{
+    ALCcontext *Context;
+    ALCdevice  *Device;
+    ALeffect   *ALEffect;
+
+    Context = GetContextRef();
+    if(!Context) return;
+
+    Device = Context->Device;
+    LockEffectsRead(Device);
+    if((ALEffect=LookupEffect(Device, effect)) == NULL)
+        alSetError(Context, AL_INVALID_NAME);
+    else
+    {
+        /* Call the appropriate handler */
+        V(ALEffect,getParamfv)(Context, param, values);
+    }
+    UnlockEffectsRead(Device);
+
+    ALCcontext_DecRef(Context);
+}
+
+
+ALenum InitEffect(ALeffect *effect)
+{
+    InitEffectParams(effect, AL_EFFECT_NULL);
+    return AL_NO_ERROR;
+}
+
+ALvoid ReleaseALEffects(ALCdevice *device)
+{
+    ALsizei i;
+    for(i = 0;i < device->EffectMap.size;i++)
+    {
+        ALeffect *temp = device->EffectMap.values[i];
+        device->EffectMap.values[i] = NULL;
+
+        // Release effect structure
+        FreeThunkEntry(temp->id);
+        memset(temp, 0, sizeof(ALeffect));
+        al_free(temp);
+    }
+}
+
+
+static void InitEffectParams(ALeffect *effect, ALenum type)
+{
+    switch(type)
+    {
+    case AL_EFFECT_EAXREVERB:
+        effect->Props.Reverb.Density   = AL_EAXREVERB_DEFAULT_DENSITY;
+        effect->Props.Reverb.Diffusion = AL_EAXREVERB_DEFAULT_DIFFUSION;
+        effect->Props.Reverb.Gain   = AL_EAXREVERB_DEFAULT_GAIN;
+        effect->Props.Reverb.GainHF = AL_EAXREVERB_DEFAULT_GAINHF;
+        effect->Props.Reverb.GainLF = AL_EAXREVERB_DEFAULT_GAINLF;
+        effect->Props.Reverb.DecayTime    = AL_EAXREVERB_DEFAULT_DECAY_TIME;
+        effect->Props.Reverb.DecayHFRatio = AL_EAXREVERB_DEFAULT_DECAY_HFRATIO;
+        effect->Props.Reverb.DecayLFRatio = AL_EAXREVERB_DEFAULT_DECAY_LFRATIO;
+        effect->Props.Reverb.ReflectionsGain   = AL_EAXREVERB_DEFAULT_REFLECTIONS_GAIN;
+        effect->Props.Reverb.ReflectionsDelay  = AL_EAXREVERB_DEFAULT_REFLECTIONS_DELAY;
+        effect->Props.Reverb.ReflectionsPan[0] = AL_EAXREVERB_DEFAULT_REFLECTIONS_PAN_XYZ;
+        effect->Props.Reverb.ReflectionsPan[1] = AL_EAXREVERB_DEFAULT_REFLECTIONS_PAN_XYZ;
+        effect->Props.Reverb.ReflectionsPan[2] = AL_EAXREVERB_DEFAULT_REFLECTIONS_PAN_XYZ;
+        effect->Props.Reverb.LateReverbGain   = AL_EAXREVERB_DEFAULT_LATE_REVERB_GAIN;
+        effect->Props.Reverb.LateReverbDelay  = AL_EAXREVERB_DEFAULT_LATE_REVERB_DELAY;
+        effect->Props.Reverb.LateReverbPan[0] = AL_EAXREVERB_DEFAULT_LATE_REVERB_PAN_XYZ;
+        effect->Props.Reverb.LateReverbPan[1] = AL_EAXREVERB_DEFAULT_LATE_REVERB_PAN_XYZ;
+        effect->Props.Reverb.LateReverbPan[2] = AL_EAXREVERB_DEFAULT_LATE_REVERB_PAN_XYZ;
+        effect->Props.Reverb.EchoTime  = AL_EAXREVERB_DEFAULT_ECHO_TIME;
+        effect->Props.Reverb.EchoDepth = AL_EAXREVERB_DEFAULT_ECHO_DEPTH;
+        effect->Props.Reverb.ModulationTime  = AL_EAXREVERB_DEFAULT_MODULATION_TIME;
+        effect->Props.Reverb.ModulationDepth = AL_EAXREVERB_DEFAULT_MODULATION_DEPTH;
+        effect->Props.Reverb.AirAbsorptionGainHF = AL_EAXREVERB_DEFAULT_AIR_ABSORPTION_GAINHF;
+        effect->Props.Reverb.HFReference = AL_EAXREVERB_DEFAULT_HFREFERENCE;
+        effect->Props.Reverb.LFReference = AL_EAXREVERB_DEFAULT_LFREFERENCE;
+        effect->Props.Reverb.RoomRolloffFactor = AL_EAXREVERB_DEFAULT_ROOM_ROLLOFF_FACTOR;
+        effect->Props.Reverb.DecayHFLimit = AL_EAXREVERB_DEFAULT_DECAY_HFLIMIT;
+        SET_VTABLE1(ALeaxreverb, effect);
+        break;
+    case AL_EFFECT_REVERB:
+        effect->Props.Reverb.Density   = AL_REVERB_DEFAULT_DENSITY;
+        effect->Props.Reverb.Diffusion = AL_REVERB_DEFAULT_DIFFUSION;
+        effect->Props.Reverb.Gain   = AL_REVERB_DEFAULT_GAIN;
+        effect->Props.Reverb.GainHF = AL_REVERB_DEFAULT_GAINHF;
+        effect->Props.Reverb.GainLF = 1.0f;
+        effect->Props.Reverb.DecayTime    = AL_REVERB_DEFAULT_DECAY_TIME;
+        effect->Props.Reverb.DecayHFRatio = AL_REVERB_DEFAULT_DECAY_HFRATIO;
+        effect->Props.Reverb.DecayLFRatio = 1.0f;
+        effect->Props.Reverb.ReflectionsGain   = AL_REVERB_DEFAULT_REFLECTIONS_GAIN;
+        effect->Props.Reverb.ReflectionsDelay  = AL_REVERB_DEFAULT_REFLECTIONS_DELAY;
+        effect->Props.Reverb.ReflectionsPan[0] = 0.0f;
+        effect->Props.Reverb.ReflectionsPan[1] = 0.0f;
+        effect->Props.Reverb.ReflectionsPan[2] = 0.0f;
+        effect->Props.Reverb.LateReverbGain   = AL_REVERB_DEFAULT_LATE_REVERB_GAIN;
+        effect->Props.Reverb.LateReverbDelay  = AL_REVERB_DEFAULT_LATE_REVERB_DELAY;
+        effect->Props.Reverb.LateReverbPan[0] = 0.0f;
+        effect->Props.Reverb.LateReverbPan[1] = 0.0f;
+        effect->Props.Reverb.LateReverbPan[2] = 0.0f;
+        effect->Props.Reverb.EchoTime  = 0.25f;
+        effect->Props.Reverb.EchoDepth = 0.0f;
+        effect->Props.Reverb.ModulationTime  = 0.25f;
+        effect->Props.Reverb.ModulationDepth = 0.0f;
+        effect->Props.Reverb.AirAbsorptionGainHF = AL_REVERB_DEFAULT_AIR_ABSORPTION_GAINHF;
+        effect->Props.Reverb.HFReference = 5000.0f;
+        effect->Props.Reverb.LFReference = 250.0f;
+        effect->Props.Reverb.RoomRolloffFactor = AL_REVERB_DEFAULT_ROOM_ROLLOFF_FACTOR;
+        effect->Props.Reverb.DecayHFLimit = AL_REVERB_DEFAULT_DECAY_HFLIMIT;
+        SET_VTABLE1(ALreverb, effect);
+        break;
+    case AL_EFFECT_CHORUS:
+        effect->Props.Chorus.Waveform = AL_CHORUS_DEFAULT_WAVEFORM;
+        effect->Props.Chorus.Phase = AL_CHORUS_DEFAULT_PHASE;
+        effect->Props.Chorus.Rate = AL_CHORUS_DEFAULT_RATE;
+        effect->Props.Chorus.Depth = AL_CHORUS_DEFAULT_DEPTH;
+        effect->Props.Chorus.Feedback = AL_CHORUS_DEFAULT_FEEDBACK;
+        effect->Props.Chorus.Delay = AL_CHORUS_DEFAULT_DELAY;
+        SET_VTABLE1(ALchorus, effect);
+        break;
+    case AL_EFFECT_COMPRESSOR:
+        effect->Props.Compressor.OnOff = AL_COMPRESSOR_DEFAULT_ONOFF;
+        SET_VTABLE1(ALcompressor, effect);
+        break;
+    case AL_EFFECT_DISTORTION:
+        effect->Props.Distortion.Edge = AL_DISTORTION_DEFAULT_EDGE;
+        effect->Props.Distortion.Gain = AL_DISTORTION_DEFAULT_GAIN;
+        effect->Props.Distortion.LowpassCutoff = AL_DISTORTION_DEFAULT_LOWPASS_CUTOFF;
+        effect->Props.Distortion.EQCenter = AL_DISTORTION_DEFAULT_EQCENTER;
+        effect->Props.Distortion.EQBandwidth = AL_DISTORTION_DEFAULT_EQBANDWIDTH;
+        SET_VTABLE1(ALdistortion, effect);
+        break;
+    case AL_EFFECT_ECHO:
+        effect->Props.Echo.Delay    = AL_ECHO_DEFAULT_DELAY;
+        effect->Props.Echo.LRDelay  = AL_ECHO_DEFAULT_LRDELAY;
+        effect->Props.Echo.Damping  = AL_ECHO_DEFAULT_DAMPING;
+        effect->Props.Echo.Feedback = AL_ECHO_DEFAULT_FEEDBACK;
+        effect->Props.Echo.Spread   = AL_ECHO_DEFAULT_SPREAD;
+        SET_VTABLE1(ALecho, effect);
+        break;
+    case AL_EFFECT_EQUALIZER:
+        effect->Props.Equalizer.LowCutoff = AL_EQUALIZER_DEFAULT_LOW_CUTOFF;
+        effect->Props.Equalizer.LowGain = AL_EQUALIZER_DEFAULT_LOW_GAIN;
+        effect->Props.Equalizer.Mid1Center = AL_EQUALIZER_DEFAULT_MID1_CENTER;
+        effect->Props.Equalizer.Mid1Gain = AL_EQUALIZER_DEFAULT_MID1_GAIN;
+        effect->Props.Equalizer.Mid1Width = AL_EQUALIZER_DEFAULT_MID1_WIDTH;
+        effect->Props.Equalizer.Mid2Center = AL_EQUALIZER_DEFAULT_MID2_CENTER;
+        effect->Props.Equalizer.Mid2Gain = AL_EQUALIZER_DEFAULT_MID2_GAIN;
+        effect->Props.Equalizer.Mid2Width = AL_EQUALIZER_DEFAULT_MID2_WIDTH;
+        effect->Props.Equalizer.HighCutoff = AL_EQUALIZER_DEFAULT_HIGH_CUTOFF;
+        effect->Props.Equalizer.HighGain = AL_EQUALIZER_DEFAULT_HIGH_GAIN;
+        SET_VTABLE1(ALequalizer, effect);
+        break;
+    case AL_EFFECT_FLANGER:
+        effect->Props.Flanger.Waveform = AL_FLANGER_DEFAULT_WAVEFORM;
+        effect->Props.Flanger.Phase = AL_FLANGER_DEFAULT_PHASE;
+        effect->Props.Flanger.Rate = AL_FLANGER_DEFAULT_RATE;
+        effect->Props.Flanger.Depth = AL_FLANGER_DEFAULT_DEPTH;
+        effect->Props.Flanger.Feedback = AL_FLANGER_DEFAULT_FEEDBACK;
+        effect->Props.Flanger.Delay = AL_FLANGER_DEFAULT_DELAY;
+        SET_VTABLE1(ALflanger, effect);
+        break;
+    case AL_EFFECT_RING_MODULATOR:
+        effect->Props.Modulator.Frequency      = AL_RING_MODULATOR_DEFAULT_FREQUENCY;
+        effect->Props.Modulator.HighPassCutoff = AL_RING_MODULATOR_DEFAULT_HIGHPASS_CUTOFF;
+        effect->Props.Modulator.Waveform       = AL_RING_MODULATOR_DEFAULT_WAVEFORM;
+        SET_VTABLE1(ALmodulator, effect);
+        break;
+    case AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT:
+    case AL_EFFECT_DEDICATED_DIALOGUE:
+        effect->Props.Dedicated.Gain = 1.0f;
+        SET_VTABLE1(ALdedicated, effect);
+        break;
+    default:
+        SET_VTABLE1(ALnull, effect);
+        break;
+    }
+    effect->type = type;
+}
+
+
+#include "AL/efx-presets.h"
+
+#define DECL(x) { #x, EFX_REVERB_PRESET_##x }
+static const struct {
+    const char name[32];
+    EFXEAXREVERBPROPERTIES props;
+} reverblist[] = {
+    DECL(GENERIC),
+    DECL(PADDEDCELL),
+    DECL(ROOM),
+    DECL(BATHROOM),
+    DECL(LIVINGROOM),
+    DECL(STONEROOM),
+    DECL(AUDITORIUM),
+    DECL(CONCERTHALL),
+    DECL(CAVE),
+    DECL(ARENA),
+    DECL(HANGAR),
+    DECL(CARPETEDHALLWAY),
+    DECL(HALLWAY),
+    DECL(STONECORRIDOR),
+    DECL(ALLEY),
+    DECL(FOREST),
+    DECL(CITY),
+    DECL(MOUNTAINS),
+    DECL(QUARRY),
+    DECL(PLAIN),
+    DECL(PARKINGLOT),
+    DECL(SEWERPIPE),
+    DECL(UNDERWATER),
+    DECL(DRUGGED),
+    DECL(DIZZY),
+    DECL(PSYCHOTIC),
+
+    DECL(CASTLE_SMALLROOM),
+    DECL(CASTLE_SHORTPASSAGE),
+    DECL(CASTLE_MEDIUMROOM),
+    DECL(CASTLE_LARGEROOM),
+    DECL(CASTLE_LONGPASSAGE),
+    DECL(CASTLE_HALL),
+    DECL(CASTLE_CUPBOARD),
+    DECL(CASTLE_COURTYARD),
+    DECL(CASTLE_ALCOVE),
+
+    DECL(FACTORY_SMALLROOM),
+    DECL(FACTORY_SHORTPASSAGE),
+    DECL(FACTORY_MEDIUMROOM),
+    DECL(FACTORY_LARGEROOM),
+    DECL(FACTORY_LONGPASSAGE),
+    DECL(FACTORY_HALL),
+    DECL(FACTORY_CUPBOARD),
+    DECL(FACTORY_COURTYARD),
+    DECL(FACTORY_ALCOVE),
+
+    DECL(ICEPALACE_SMALLROOM),
+    DECL(ICEPALACE_SHORTPASSAGE),
+    DECL(ICEPALACE_MEDIUMROOM),
+    DECL(ICEPALACE_LARGEROOM),
+    DECL(ICEPALACE_LONGPASSAGE),
+    DECL(ICEPALACE_HALL),
+    DECL(ICEPALACE_CUPBOARD),
+    DECL(ICEPALACE_COURTYARD),
+    DECL(ICEPALACE_ALCOVE),
+
+    DECL(SPACESTATION_SMALLROOM),
+    DECL(SPACESTATION_SHORTPASSAGE),
+    DECL(SPACESTATION_MEDIUMROOM),
+    DECL(SPACESTATION_LARGEROOM),
+    DECL(SPACESTATION_LONGPASSAGE),
+    DECL(SPACESTATION_HALL),
+    DECL(SPACESTATION_CUPBOARD),
+    DECL(SPACESTATION_ALCOVE),
+
+    DECL(WOODEN_SMALLROOM),
+    DECL(WOODEN_SHORTPASSAGE),
+    DECL(WOODEN_MEDIUMROOM),
+    DECL(WOODEN_LARGEROOM),
+    DECL(WOODEN_LONGPASSAGE),
+    DECL(WOODEN_HALL),
+    DECL(WOODEN_CUPBOARD),
+    DECL(WOODEN_COURTYARD),
+    DECL(WOODEN_ALCOVE),
+
+    DECL(SPORT_EMPTYSTADIUM),
+    DECL(SPORT_SQUASHCOURT),
+    DECL(SPORT_SMALLSWIMMINGPOOL),
+    DECL(SPORT_LARGESWIMMINGPOOL),
+    DECL(SPORT_GYMNASIUM),
+    DECL(SPORT_FULLSTADIUM),
+    DECL(SPORT_STADIUMTANNOY),
+
+    DECL(PREFAB_WORKSHOP),
+    DECL(PREFAB_SCHOOLROOM),
+    DECL(PREFAB_PRACTISEROOM),
+    DECL(PREFAB_OUTHOUSE),
+    DECL(PREFAB_CARAVAN),
+
+    DECL(DOME_TOMB),
+    DECL(PIPE_SMALL),
+    DECL(DOME_SAINTPAULS),
+    DECL(PIPE_LONGTHIN),
+    DECL(PIPE_LARGE),
+    DECL(PIPE_RESONANT),
+
+    DECL(OUTDOORS_BACKYARD),
+    DECL(OUTDOORS_ROLLINGPLAINS),
+    DECL(OUTDOORS_DEEPCANYON),
+    DECL(OUTDOORS_CREEK),
+    DECL(OUTDOORS_VALLEY),
+
+    DECL(MOOD_HEAVEN),
+    DECL(MOOD_HELL),
+    DECL(MOOD_MEMORY),
+
+    DECL(DRIVING_COMMENTATOR),
+    DECL(DRIVING_PITGARAGE),
+    DECL(DRIVING_INCAR_RACER),
+    DECL(DRIVING_INCAR_SPORTS),
+    DECL(DRIVING_INCAR_LUXURY),
+    DECL(DRIVING_FULLGRANDSTAND),
+    DECL(DRIVING_EMPTYGRANDSTAND),
+    DECL(DRIVING_TUNNEL),
+
+    DECL(CITY_STREETS),
+    DECL(CITY_SUBWAY),
+    DECL(CITY_MUSEUM),
+    DECL(CITY_LIBRARY),
+    DECL(CITY_UNDERPASS),
+    DECL(CITY_ABANDONED),
+
+    DECL(DUSTYROOM),
+    DECL(CHAPEL),
+    DECL(SMALLWATERROOM),
+};
+#undef DECL
+
+ALvoid LoadReverbPreset(const char *name, ALeffect *effect)
+{
+    size_t i;
+
+    if(strcasecmp(name, "NONE") == 0)
+    {
+        InitEffectParams(effect, AL_EFFECT_NULL);
+        TRACE("Loading reverb '%s'\n", "NONE");
+        return;
+    }
+
+    if(!DisabledEffects[EAXREVERB])
+        InitEffectParams(effect, AL_EFFECT_EAXREVERB);
+    else if(!DisabledEffects[REVERB])
+        InitEffectParams(effect, AL_EFFECT_REVERB);
+    else
+        InitEffectParams(effect, AL_EFFECT_NULL);
+    for(i = 0;i < COUNTOF(reverblist);i++)
+    {
+        const EFXEAXREVERBPROPERTIES *props;
+
+        if(strcasecmp(name, reverblist[i].name) != 0)
+            continue;
+
+        TRACE("Loading reverb '%s'\n", reverblist[i].name);
+        props = &reverblist[i].props;
+        effect->Props.Reverb.Density   = props->flDensity;
+        effect->Props.Reverb.Diffusion = props->flDiffusion;
+        effect->Props.Reverb.Gain   = props->flGain;
+        effect->Props.Reverb.GainHF = props->flGainHF;
+        effect->Props.Reverb.GainLF = props->flGainLF;
+        effect->Props.Reverb.DecayTime    = props->flDecayTime;
+        effect->Props.Reverb.DecayHFRatio = props->flDecayHFRatio;
+        effect->Props.Reverb.DecayLFRatio = props->flDecayLFRatio;
+        effect->Props.Reverb.ReflectionsGain   = props->flReflectionsGain;
+        effect->Props.Reverb.ReflectionsDelay  = props->flReflectionsDelay;
+        effect->Props.Reverb.ReflectionsPan[0] = props->flReflectionsPan[0];
+        effect->Props.Reverb.ReflectionsPan[1] = props->flReflectionsPan[1];
+        effect->Props.Reverb.ReflectionsPan[2] = props->flReflectionsPan[2];
+        effect->Props.Reverb.LateReverbGain   = props->flLateReverbGain;
+        effect->Props.Reverb.LateReverbDelay  = props->flLateReverbDelay;
+        effect->Props.Reverb.LateReverbPan[0] = props->flLateReverbPan[0];
+        effect->Props.Reverb.LateReverbPan[1] = props->flLateReverbPan[1];
+        effect->Props.Reverb.LateReverbPan[2] = props->flLateReverbPan[2];
+        effect->Props.Reverb.EchoTime  = props->flEchoTime;
+        effect->Props.Reverb.EchoDepth = props->flEchoDepth;
+        effect->Props.Reverb.ModulationTime  = props->flModulationTime;
+        effect->Props.Reverb.ModulationDepth = props->flModulationDepth;
+        effect->Props.Reverb.AirAbsorptionGainHF = props->flAirAbsorptionGainHF;
+        effect->Props.Reverb.HFReference = props->flHFReference;
+        effect->Props.Reverb.LFReference = props->flLFReference;
+        effect->Props.Reverb.RoomRolloffFactor = props->flRoomRolloffFactor;
+        effect->Props.Reverb.DecayHFLimit = props->iDecayHFLimit;
+        return;
+    }
+
+    WARN("Reverb preset '%s' not found\n", name);
+}

+ 77 - 0
Engine/lib/openal-soft/OpenAL32/alError.c

@@ -0,0 +1,77 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 1999-2000 by authors.
+ * This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the
+ *  Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#include "config.h"
+
+#include <signal.h>
+
+#ifdef HAVE_WINDOWS_H
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#endif
+
+#include "alMain.h"
+#include "AL/alc.h"
+#include "alError.h"
+
+ALboolean TrapALError = AL_FALSE;
+
+ALvoid alSetError(ALCcontext *Context, ALenum errorCode)
+{
+    ALenum curerr = AL_NO_ERROR;
+    if(TrapALError)
+    {
+#ifdef _WIN32
+        /* DebugBreak will cause an exception if there is no debugger */
+        if(IsDebuggerPresent())
+            DebugBreak();
+#elif defined(SIGTRAP)
+        raise(SIGTRAP);
+#endif
+    }
+    ATOMIC_COMPARE_EXCHANGE_STRONG(ALenum, &Context->LastError, &curerr, errorCode);
+}
+
+AL_API ALenum AL_APIENTRY alGetError(void)
+{
+    ALCcontext *Context;
+    ALenum errorCode;
+
+    Context = GetContextRef();
+    if(!Context)
+    {
+        if(TrapALError)
+        {
+#ifdef _WIN32
+            if(IsDebuggerPresent())
+                DebugBreak();
+#elif defined(SIGTRAP)
+            raise(SIGTRAP);
+#endif
+        }
+        return AL_INVALID_OPERATION;
+    }
+
+    errorCode = ATOMIC_EXCHANGE(ALenum, &Context->LastError, AL_NO_ERROR);
+
+    ALCcontext_DecRef(Context);
+
+    return errorCode;
+}

+ 103 - 0
Engine/lib/openal-soft/OpenAL32/alExtension.c

@@ -0,0 +1,103 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 1999-2007 by authors.
+ * This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the
+ *  Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "alError.h"
+#include "alMain.h"
+#include "alFilter.h"
+#include "alEffect.h"
+#include "alAuxEffectSlot.h"
+#include "alSource.h"
+#include "alBuffer.h"
+#include "AL/al.h"
+#include "AL/alc.h"
+
+
+const struct EffectList EffectList[] = {
+    { "eaxreverb",  EAXREVERB,  "AL_EFFECT_EAXREVERB",      AL_EFFECT_EAXREVERB },
+    { "reverb",     REVERB,     "AL_EFFECT_REVERB",         AL_EFFECT_REVERB },
+    { "chorus",     CHORUS,     "AL_EFFECT_CHORUS",         AL_EFFECT_CHORUS },
+    { "compressor", COMPRESSOR, "AL_EFFECT_COMPRESSOR",     AL_EFFECT_COMPRESSOR },
+    { "distortion", DISTORTION, "AL_EFFECT_DISTORTION",     AL_EFFECT_DISTORTION },
+    { "echo",       ECHO,       "AL_EFFECT_ECHO",           AL_EFFECT_ECHO },
+    { "equalizer",  EQUALIZER,  "AL_EFFECT_EQUALIZER",      AL_EFFECT_EQUALIZER },
+    { "flanger",    FLANGER,    "AL_EFFECT_FLANGER",        AL_EFFECT_FLANGER },
+    { "modulator",  MODULATOR,  "AL_EFFECT_RING_MODULATOR", AL_EFFECT_RING_MODULATOR },
+    { "dedicated",  DEDICATED,  "AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT", AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT },
+    { "dedicated",  DEDICATED,  "AL_EFFECT_DEDICATED_DIALOGUE", AL_EFFECT_DEDICATED_DIALOGUE },
+    { NULL, 0, NULL, (ALenum)0 }
+};
+
+
+AL_API ALboolean AL_APIENTRY alIsExtensionPresent(const ALchar *extName)
+{
+    ALboolean ret = AL_FALSE;
+    ALCcontext *context;
+    const char *ptr;
+    size_t len;
+
+    context = GetContextRef();
+    if(!context) return AL_FALSE;
+
+    if(!(extName))
+        SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+
+    len = strlen(extName);
+    ptr = context->ExtensionList;
+    while(ptr && *ptr)
+    {
+        if(strncasecmp(ptr, extName, len) == 0 &&
+           (ptr[len] == '\0' || isspace(ptr[len])))
+        {
+            ret = AL_TRUE;
+            break;
+        }
+        if((ptr=strchr(ptr, ' ')) != NULL)
+        {
+            do {
+                ++ptr;
+            } while(isspace(*ptr));
+        }
+    }
+
+done:
+    ALCcontext_DecRef(context);
+    return ret;
+}
+
+
+AL_API ALvoid* AL_APIENTRY alGetProcAddress(const ALchar *funcName)
+{
+    if(!funcName)
+        return NULL;
+    return alcGetProcAddress(NULL, funcName);
+}
+
+AL_API ALenum AL_APIENTRY alGetEnumValue(const ALchar *enumName)
+{
+    if(!enumName)
+        return (ALenum)0;
+    return alcGetEnumValue(NULL, enumName);
+}

+ 718 - 0
Engine/lib/openal-soft/OpenAL32/alFilter.c

@@ -0,0 +1,718 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 1999-2007 by authors.
+ * This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the
+ *  Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+
+#include "alMain.h"
+#include "alu.h"
+#include "alFilter.h"
+#include "alThunk.h"
+#include "alError.h"
+
+
+extern inline void LockFiltersRead(ALCdevice *device);
+extern inline void UnlockFiltersRead(ALCdevice *device);
+extern inline void LockFiltersWrite(ALCdevice *device);
+extern inline void UnlockFiltersWrite(ALCdevice *device);
+extern inline struct ALfilter *LookupFilter(ALCdevice *device, ALuint id);
+extern inline struct ALfilter *RemoveFilter(ALCdevice *device, ALuint id);
+extern inline void ALfilterState_clear(ALfilterState *filter);
+extern inline void ALfilterState_processPassthru(ALfilterState *filter, const ALfloat *restrict src, ALuint numsamples);
+extern inline ALfloat calc_rcpQ_from_slope(ALfloat gain, ALfloat slope);
+extern inline ALfloat calc_rcpQ_from_bandwidth(ALfloat freq_mult, ALfloat bandwidth);
+
+static void InitFilterParams(ALfilter *filter, ALenum type);
+
+
+AL_API ALvoid AL_APIENTRY alGenFilters(ALsizei n, ALuint *filters)
+{
+    ALCdevice *device;
+    ALCcontext *context;
+    ALsizei cur = 0;
+    ALenum err;
+
+    context = GetContextRef();
+    if(!context) return;
+
+    if(!(n >= 0))
+        SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+
+    device = context->Device;
+    for(cur = 0;cur < n;cur++)
+    {
+        ALfilter *filter = al_calloc(16, sizeof(ALfilter));
+        if(!filter)
+        {
+            alDeleteFilters(cur, filters);
+            SET_ERROR_AND_GOTO(context, AL_OUT_OF_MEMORY, done);
+        }
+        InitFilterParams(filter, AL_FILTER_NULL);
+
+        err = NewThunkEntry(&filter->id);
+        if(err == AL_NO_ERROR)
+            err = InsertUIntMapEntry(&device->FilterMap, filter->id, filter);
+        if(err != AL_NO_ERROR)
+        {
+            FreeThunkEntry(filter->id);
+            memset(filter, 0, sizeof(ALfilter));
+            al_free(filter);
+
+            alDeleteFilters(cur, filters);
+            SET_ERROR_AND_GOTO(context, err, done);
+        }
+
+        filters[cur] = filter->id;
+    }
+
+done:
+    ALCcontext_DecRef(context);
+}
+
+AL_API ALvoid AL_APIENTRY alDeleteFilters(ALsizei n, const ALuint *filters)
+{
+    ALCdevice *device;
+    ALCcontext *context;
+    ALfilter *filter;
+    ALsizei i;
+
+    context = GetContextRef();
+    if(!context) return;
+
+    device = context->Device;
+    LockFiltersWrite(device);
+    if(!(n >= 0))
+        SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+    for(i = 0;i < n;i++)
+    {
+        if(filters[i] && LookupFilter(device, filters[i]) == NULL)
+            SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
+    }
+    for(i = 0;i < n;i++)
+    {
+        if((filter=RemoveFilter(device, filters[i])) == NULL)
+            continue;
+        FreeThunkEntry(filter->id);
+
+        memset(filter, 0, sizeof(*filter));
+        al_free(filter);
+    }
+
+done:
+    UnlockFiltersWrite(device);
+    ALCcontext_DecRef(context);
+}
+
+AL_API ALboolean AL_APIENTRY alIsFilter(ALuint filter)
+{
+    ALCcontext *Context;
+    ALboolean  result;
+
+    Context = GetContextRef();
+    if(!Context) return AL_FALSE;
+
+    LockFiltersRead(Context->Device);
+    result = ((!filter || LookupFilter(Context->Device, filter)) ?
+              AL_TRUE : AL_FALSE);
+    UnlockFiltersRead(Context->Device);
+
+    ALCcontext_DecRef(Context);
+
+    return result;
+}
+
+AL_API ALvoid AL_APIENTRY alFilteri(ALuint filter, ALenum param, ALint value)
+{
+    ALCcontext *Context;
+    ALCdevice  *Device;
+    ALfilter   *ALFilter;
+
+    Context = GetContextRef();
+    if(!Context) return;
+
+    Device = Context->Device;
+    LockFiltersWrite(Device);
+    if((ALFilter=LookupFilter(Device, filter)) == NULL)
+        alSetError(Context, AL_INVALID_NAME);
+    else
+    {
+        if(param == AL_FILTER_TYPE)
+        {
+            if(value == AL_FILTER_NULL || value == AL_FILTER_LOWPASS ||
+               value == AL_FILTER_HIGHPASS || value == AL_FILTER_BANDPASS)
+                InitFilterParams(ALFilter, value);
+            else
+                alSetError(Context, AL_INVALID_VALUE);
+        }
+        else
+        {
+            /* Call the appropriate handler */
+            ALfilter_SetParami(ALFilter, Context, param, value);
+        }
+    }
+    UnlockFiltersWrite(Device);
+
+    ALCcontext_DecRef(Context);
+}
+
+AL_API ALvoid AL_APIENTRY alFilteriv(ALuint filter, ALenum param, const ALint *values)
+{
+    ALCcontext *Context;
+    ALCdevice  *Device;
+    ALfilter   *ALFilter;
+
+    switch(param)
+    {
+        case AL_FILTER_TYPE:
+            alFilteri(filter, param, values[0]);
+            return;
+    }
+
+    Context = GetContextRef();
+    if(!Context) return;
+
+    Device = Context->Device;
+    LockFiltersWrite(Device);
+    if((ALFilter=LookupFilter(Device, filter)) == NULL)
+        alSetError(Context, AL_INVALID_NAME);
+    else
+    {
+        /* Call the appropriate handler */
+        ALfilter_SetParamiv(ALFilter, Context, param, values);
+    }
+    UnlockFiltersWrite(Device);
+
+    ALCcontext_DecRef(Context);
+}
+
+AL_API ALvoid AL_APIENTRY alFilterf(ALuint filter, ALenum param, ALfloat value)
+{
+    ALCcontext *Context;
+    ALCdevice  *Device;
+    ALfilter   *ALFilter;
+
+    Context = GetContextRef();
+    if(!Context) return;
+
+    Device = Context->Device;
+    LockFiltersWrite(Device);
+    if((ALFilter=LookupFilter(Device, filter)) == NULL)
+        alSetError(Context, AL_INVALID_NAME);
+    else
+    {
+        /* Call the appropriate handler */
+        ALfilter_SetParamf(ALFilter, Context, param, value);
+    }
+    UnlockFiltersWrite(Device);
+
+    ALCcontext_DecRef(Context);
+}
+
+AL_API ALvoid AL_APIENTRY alFilterfv(ALuint filter, ALenum param, const ALfloat *values)
+{
+    ALCcontext *Context;
+    ALCdevice  *Device;
+    ALfilter   *ALFilter;
+
+    Context = GetContextRef();
+    if(!Context) return;
+
+    Device = Context->Device;
+    LockFiltersWrite(Device);
+    if((ALFilter=LookupFilter(Device, filter)) == NULL)
+        alSetError(Context, AL_INVALID_NAME);
+    else
+    {
+        /* Call the appropriate handler */
+        ALfilter_SetParamfv(ALFilter, Context, param, values);
+    }
+    UnlockFiltersWrite(Device);
+
+    ALCcontext_DecRef(Context);
+}
+
+AL_API ALvoid AL_APIENTRY alGetFilteri(ALuint filter, ALenum param, ALint *value)
+{
+    ALCcontext *Context;
+    ALCdevice  *Device;
+    ALfilter   *ALFilter;
+
+    Context = GetContextRef();
+    if(!Context) return;
+
+    Device = Context->Device;
+    LockFiltersRead(Device);
+    if((ALFilter=LookupFilter(Device, filter)) == NULL)
+        alSetError(Context, AL_INVALID_NAME);
+    else
+    {
+        if(param == AL_FILTER_TYPE)
+            *value = ALFilter->type;
+        else
+        {
+            /* Call the appropriate handler */
+            ALfilter_GetParami(ALFilter, Context, param, value);
+        }
+    }
+    UnlockFiltersRead(Device);
+
+    ALCcontext_DecRef(Context);
+}
+
+AL_API ALvoid AL_APIENTRY alGetFilteriv(ALuint filter, ALenum param, ALint *values)
+{
+    ALCcontext *Context;
+    ALCdevice  *Device;
+    ALfilter   *ALFilter;
+
+    switch(param)
+    {
+        case AL_FILTER_TYPE:
+            alGetFilteri(filter, param, values);
+            return;
+    }
+
+    Context = GetContextRef();
+    if(!Context) return;
+
+    Device = Context->Device;
+    LockFiltersRead(Device);
+    if((ALFilter=LookupFilter(Device, filter)) == NULL)
+        alSetError(Context, AL_INVALID_NAME);
+    else
+    {
+        /* Call the appropriate handler */
+        ALfilter_GetParamiv(ALFilter, Context, param, values);
+    }
+    UnlockFiltersRead(Device);
+
+    ALCcontext_DecRef(Context);
+}
+
+AL_API ALvoid AL_APIENTRY alGetFilterf(ALuint filter, ALenum param, ALfloat *value)
+{
+    ALCcontext *Context;
+    ALCdevice  *Device;
+    ALfilter   *ALFilter;
+
+    Context = GetContextRef();
+    if(!Context) return;
+
+    Device = Context->Device;
+    LockFiltersRead(Device);
+    if((ALFilter=LookupFilter(Device, filter)) == NULL)
+        alSetError(Context, AL_INVALID_NAME);
+    else
+    {
+        /* Call the appropriate handler */
+        ALfilter_GetParamf(ALFilter, Context, param, value);
+    }
+    UnlockFiltersRead(Device);
+
+    ALCcontext_DecRef(Context);
+}
+
+AL_API ALvoid AL_APIENTRY alGetFilterfv(ALuint filter, ALenum param, ALfloat *values)
+{
+    ALCcontext *Context;
+    ALCdevice  *Device;
+    ALfilter   *ALFilter;
+
+    Context = GetContextRef();
+    if(!Context) return;
+
+    Device = Context->Device;
+    LockFiltersRead(Device);
+    if((ALFilter=LookupFilter(Device, filter)) == NULL)
+        alSetError(Context, AL_INVALID_NAME);
+    else
+    {
+        /* Call the appropriate handler */
+        ALfilter_GetParamfv(ALFilter, Context, param, values);
+    }
+    UnlockFiltersRead(Device);
+
+    ALCcontext_DecRef(Context);
+}
+
+
+void ALfilterState_setParams(ALfilterState *filter, ALfilterType type, ALfloat gain, ALfloat freq_mult, ALfloat rcpQ)
+{
+    ALfloat alpha, sqrtgain_alpha_2;
+    ALfloat w0, sin_w0, cos_w0;
+    ALfloat a[3] = { 1.0f, 0.0f, 0.0f };
+    ALfloat b[3] = { 1.0f, 0.0f, 0.0f };
+
+    // Limit gain to -100dB
+    gain = maxf(gain, 0.00001f);
+
+    w0 = F_TAU * freq_mult;
+    sin_w0 = sinf(w0);
+    cos_w0 = cosf(w0);
+    alpha = sin_w0/2.0f * rcpQ;
+
+    /* Calculate filter coefficients depending on filter type */
+    switch(type)
+    {
+        case ALfilterType_HighShelf:
+            sqrtgain_alpha_2 = 2.0f * sqrtf(gain) * alpha;
+            b[0] =       gain*((gain+1.0f) + (gain-1.0f)*cos_w0 + sqrtgain_alpha_2);
+            b[1] = -2.0f*gain*((gain-1.0f) + (gain+1.0f)*cos_w0                   );
+            b[2] =       gain*((gain+1.0f) + (gain-1.0f)*cos_w0 - sqrtgain_alpha_2);
+            a[0] =             (gain+1.0f) - (gain-1.0f)*cos_w0 + sqrtgain_alpha_2;
+            a[1] =  2.0f*     ((gain-1.0f) - (gain+1.0f)*cos_w0                   );
+            a[2] =             (gain+1.0f) - (gain-1.0f)*cos_w0 - sqrtgain_alpha_2;
+            break;
+        case ALfilterType_LowShelf:
+            sqrtgain_alpha_2 = 2.0f * sqrtf(gain) * alpha;
+            b[0] =       gain*((gain+1.0f) - (gain-1.0f)*cos_w0 + sqrtgain_alpha_2);
+            b[1] =  2.0f*gain*((gain-1.0f) - (gain+1.0f)*cos_w0                   );
+            b[2] =       gain*((gain+1.0f) - (gain-1.0f)*cos_w0 - sqrtgain_alpha_2);
+            a[0] =             (gain+1.0f) + (gain-1.0f)*cos_w0 + sqrtgain_alpha_2;
+            a[1] = -2.0f*     ((gain-1.0f) + (gain+1.0f)*cos_w0                   );
+            a[2] =             (gain+1.0f) + (gain-1.0f)*cos_w0 - sqrtgain_alpha_2;
+            break;
+        case ALfilterType_Peaking:
+            gain = sqrtf(gain);
+            b[0] =  1.0f + alpha * gain;
+            b[1] = -2.0f * cos_w0;
+            b[2] =  1.0f - alpha * gain;
+            a[0] =  1.0f + alpha / gain;
+            a[1] = -2.0f * cos_w0;
+            a[2] =  1.0f - alpha / gain;
+            break;
+
+        case ALfilterType_LowPass:
+            b[0] = (1.0f - cos_w0) / 2.0f;
+            b[1] =  1.0f - cos_w0;
+            b[2] = (1.0f - cos_w0) / 2.0f;
+            a[0] =  1.0f + alpha;
+            a[1] = -2.0f * cos_w0;
+            a[2] =  1.0f - alpha;
+            break;
+        case ALfilterType_HighPass:
+            b[0] =  (1.0f + cos_w0) / 2.0f;
+            b[1] = -(1.0f + cos_w0);
+            b[2] =  (1.0f + cos_w0) / 2.0f;
+            a[0] =   1.0f + alpha;
+            a[1] =  -2.0f * cos_w0;
+            a[2] =   1.0f - alpha;
+            break;
+        case ALfilterType_BandPass:
+            b[0] =  alpha;
+            b[1] =  0;
+            b[2] = -alpha;
+            a[0] =  1.0f + alpha;
+            a[1] = -2.0f * cos_w0;
+            a[2] =  1.0f - alpha;
+            break;
+    }
+
+    filter->a1 = a[1] / a[0];
+    filter->a2 = a[2] / a[0];
+    filter->b0 = b[0] / a[0];
+    filter->b1 = b[1] / a[0];
+    filter->b2 = b[2] / a[0];
+}
+
+
+static void lp_SetParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val))
+{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
+static void lp_SetParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), const ALint *UNUSED(vals))
+{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
+static void lp_SetParamf(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat val)
+{
+    switch(param)
+    {
+        case AL_LOWPASS_GAIN:
+            if(!(val >= AL_LOWPASS_MIN_GAIN && val <= AL_LOWPASS_MAX_GAIN))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            filter->Gain = val;
+            break;
+
+        case AL_LOWPASS_GAINHF:
+            if(!(val >= AL_LOWPASS_MIN_GAINHF && val <= AL_LOWPASS_MAX_GAINHF))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            filter->GainHF = val;
+            break;
+
+        default:
+            SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+    }
+}
+static void lp_SetParamfv(ALfilter *filter, ALCcontext *context, ALenum param, const ALfloat *vals)
+{
+    lp_SetParamf(filter, context, param, vals[0]);
+}
+
+static void lp_GetParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val))
+{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
+static void lp_GetParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(vals))
+{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
+static void lp_GetParamf(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *val)
+{
+    switch(param)
+    {
+        case AL_LOWPASS_GAIN:
+            *val = filter->Gain;
+            break;
+
+        case AL_LOWPASS_GAINHF:
+            *val = filter->GainHF;
+            break;
+
+        default:
+            SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+    }
+}
+static void lp_GetParamfv(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *vals)
+{
+    lp_GetParamf(filter, context, param, vals);
+}
+
+
+static void hp_SetParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val))
+{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
+static void hp_SetParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), const ALint *UNUSED(vals))
+{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
+static void hp_SetParamf(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat val)
+{
+    switch(param)
+    {
+        case AL_HIGHPASS_GAIN:
+            if(!(val >= AL_HIGHPASS_MIN_GAIN && val <= AL_HIGHPASS_MAX_GAIN))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            filter->Gain = val;
+            break;
+
+        case AL_HIGHPASS_GAINLF:
+            if(!(val >= AL_HIGHPASS_MIN_GAINLF && val <= AL_HIGHPASS_MAX_GAINLF))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            filter->GainLF = val;
+            break;
+
+        default:
+            SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+    }
+}
+static void hp_SetParamfv(ALfilter *filter, ALCcontext *context, ALenum param, const ALfloat *vals)
+{
+    hp_SetParamf(filter, context, param, vals[0]);
+}
+
+static void hp_GetParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val))
+{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
+static void hp_GetParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(vals))
+{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
+static void hp_GetParamf(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *val)
+{
+    switch(param)
+    {
+        case AL_HIGHPASS_GAIN:
+            *val = filter->Gain;
+            break;
+
+        case AL_HIGHPASS_GAINLF:
+            *val = filter->GainLF;
+            break;
+
+        default:
+            SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+    }
+}
+static void hp_GetParamfv(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *vals)
+{
+    hp_GetParamf(filter, context, param, vals);
+}
+
+
+static void bp_SetParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val))
+{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
+static void bp_SetParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), const ALint *UNUSED(vals))
+{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
+static void bp_SetParamf(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat val)
+{
+    switch(param)
+    {
+        case AL_BANDPASS_GAIN:
+            if(!(val >= AL_BANDPASS_MIN_GAIN && val <= AL_BANDPASS_MAX_GAIN))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            filter->Gain = val;
+            break;
+
+        case AL_BANDPASS_GAINHF:
+            if(!(val >= AL_BANDPASS_MIN_GAINHF && val <= AL_BANDPASS_MAX_GAINHF))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            filter->GainHF = val;
+            break;
+
+        case AL_BANDPASS_GAINLF:
+            if(!(val >= AL_BANDPASS_MIN_GAINLF && val <= AL_BANDPASS_MAX_GAINLF))
+                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+            filter->GainLF = val;
+            break;
+
+        default:
+            SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+    }
+}
+static void bp_SetParamfv(ALfilter *filter, ALCcontext *context, ALenum param, const ALfloat *vals)
+{
+    bp_SetParamf(filter, context, param, vals[0]);
+}
+
+static void bp_GetParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val))
+{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
+static void bp_GetParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(vals))
+{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
+static void bp_GetParamf(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *val)
+{
+    switch(param)
+    {
+        case AL_BANDPASS_GAIN:
+            *val = filter->Gain;
+            break;
+
+        case AL_BANDPASS_GAINHF:
+            *val = filter->GainHF;
+            break;
+
+        case AL_BANDPASS_GAINLF:
+            *val = filter->GainLF;
+            break;
+
+        default:
+            SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+    }
+}
+static void bp_GetParamfv(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *vals)
+{
+    bp_GetParamf(filter, context, param, vals);
+}
+
+
+static void null_SetParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val))
+{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
+static void null_SetParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), const ALint *UNUSED(vals))
+{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
+static void null_SetParamf(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALfloat UNUSED(val))
+{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
+static void null_SetParamfv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), const ALfloat *UNUSED(vals))
+{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
+
+static void null_GetParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val))
+{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
+static void null_GetParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(vals))
+{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
+static void null_GetParamf(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALfloat *UNUSED(val))
+{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
+static void null_GetParamfv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALfloat *UNUSED(vals))
+{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
+
+
+ALvoid ReleaseALFilters(ALCdevice *device)
+{
+    ALsizei i;
+    for(i = 0;i < device->FilterMap.size;i++)
+    {
+        ALfilter *temp = device->FilterMap.values[i];
+        device->FilterMap.values[i] = NULL;
+
+        // Release filter structure
+        FreeThunkEntry(temp->id);
+        memset(temp, 0, sizeof(ALfilter));
+        al_free(temp);
+    }
+}
+
+
+static void InitFilterParams(ALfilter *filter, ALenum type)
+{
+    if(type == AL_FILTER_LOWPASS)
+    {
+        filter->Gain = AL_LOWPASS_DEFAULT_GAIN;
+        filter->GainHF = AL_LOWPASS_DEFAULT_GAINHF;
+        filter->HFReference = LOWPASSFREQREF;
+        filter->GainLF = 1.0f;
+        filter->LFReference = HIGHPASSFREQREF;
+
+        filter->SetParami  = lp_SetParami;
+        filter->SetParamiv = lp_SetParamiv;
+        filter->SetParamf  = lp_SetParamf;
+        filter->SetParamfv = lp_SetParamfv;
+        filter->GetParami  = lp_GetParami;
+        filter->GetParamiv = lp_GetParamiv;
+        filter->GetParamf  = lp_GetParamf;
+        filter->GetParamfv = lp_GetParamfv;
+    }
+    else if(type == AL_FILTER_HIGHPASS)
+    {
+        filter->Gain = AL_HIGHPASS_DEFAULT_GAIN;
+        filter->GainHF = 1.0f;
+        filter->HFReference = LOWPASSFREQREF;
+        filter->GainLF = AL_HIGHPASS_DEFAULT_GAINLF;
+        filter->LFReference = HIGHPASSFREQREF;
+
+        filter->SetParami  = hp_SetParami;
+        filter->SetParamiv = hp_SetParamiv;
+        filter->SetParamf  = hp_SetParamf;
+        filter->SetParamfv = hp_SetParamfv;
+        filter->GetParami  = hp_GetParami;
+        filter->GetParamiv = hp_GetParamiv;
+        filter->GetParamf  = hp_GetParamf;
+        filter->GetParamfv = hp_GetParamfv;
+    }
+    else if(type == AL_FILTER_BANDPASS)
+    {
+        filter->Gain = AL_BANDPASS_DEFAULT_GAIN;
+        filter->GainHF = AL_BANDPASS_DEFAULT_GAINHF;
+        filter->HFReference = LOWPASSFREQREF;
+        filter->GainLF = AL_BANDPASS_DEFAULT_GAINLF;
+        filter->LFReference = HIGHPASSFREQREF;
+
+        filter->SetParami  = bp_SetParami;
+        filter->SetParamiv = bp_SetParamiv;
+        filter->SetParamf  = bp_SetParamf;
+        filter->SetParamfv = bp_SetParamfv;
+        filter->GetParami  = bp_GetParami;
+        filter->GetParamiv = bp_GetParamiv;
+        filter->GetParamf  = bp_GetParamf;
+        filter->GetParamfv = bp_GetParamfv;
+    }
+    else
+    {
+        filter->Gain = 1.0f;
+        filter->GainHF = 1.0f;
+        filter->HFReference = LOWPASSFREQREF;
+        filter->GainLF = 1.0f;
+        filter->LFReference = HIGHPASSFREQREF;
+
+        filter->SetParami  = null_SetParami;
+        filter->SetParamiv = null_SetParamiv;
+        filter->SetParamf  = null_SetParamf;
+        filter->SetParamfv = null_SetParamfv;
+        filter->GetParami  = null_GetParami;
+        filter->GetParamiv = null_GetParamiv;
+        filter->GetParamf  = null_GetParamf;
+        filter->GetParamfv = null_GetParamfv;
+    }
+    filter->type = type;
+}

+ 515 - 0
Engine/lib/openal-soft/OpenAL32/alListener.c

@@ -0,0 +1,515 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 1999-2000 by authors.
+ * This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the
+ *  Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#include "config.h"
+
+#include "alMain.h"
+#include "AL/alc.h"
+#include "alError.h"
+#include "alListener.h"
+#include "alSource.h"
+
+AL_API ALvoid AL_APIENTRY alListenerf(ALenum param, ALfloat value)
+{
+    ALCcontext *context;
+
+    context = GetContextRef();
+    if(!context) return;
+
+    WriteLock(&context->PropLock);
+    switch(param)
+    {
+    case AL_GAIN:
+        if(!(value >= 0.0f && isfinite(value)))
+            SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+        context->Listener->Gain = value;
+        break;
+
+    case AL_METERS_PER_UNIT:
+        if(!(value >= 0.0f && isfinite(value)))
+            SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+        context->Listener->MetersPerUnit = value;
+        break;
+
+    default:
+        SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
+    }
+    if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
+        UpdateListenerProps(context);
+
+done:
+    WriteUnlock(&context->PropLock);
+    ALCcontext_DecRef(context);
+}
+
+
+AL_API ALvoid AL_APIENTRY alListener3f(ALenum param, ALfloat value1, ALfloat value2, ALfloat value3)
+{
+    ALCcontext *context;
+
+    context = GetContextRef();
+    if(!context) return;
+
+    WriteLock(&context->PropLock);
+    switch(param)
+    {
+    case AL_POSITION:
+        if(!(isfinite(value1) && isfinite(value2) && isfinite(value3)))
+            SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+        context->Listener->Position[0] = value1;
+        context->Listener->Position[1] = value2;
+        context->Listener->Position[2] = value3;
+        break;
+
+    case AL_VELOCITY:
+        if(!(isfinite(value1) && isfinite(value2) && isfinite(value3)))
+            SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+        context->Listener->Velocity[0] = value1;
+        context->Listener->Velocity[1] = value2;
+        context->Listener->Velocity[2] = value3;
+        break;
+
+    default:
+        SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
+    }
+    if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
+        UpdateListenerProps(context);
+
+done:
+    WriteUnlock(&context->PropLock);
+    ALCcontext_DecRef(context);
+}
+
+
+AL_API ALvoid AL_APIENTRY alListenerfv(ALenum param, const ALfloat *values)
+{
+    ALCcontext *context;
+
+    if(values)
+    {
+        switch(param)
+        {
+        case AL_GAIN:
+        case AL_METERS_PER_UNIT:
+            alListenerf(param, values[0]);
+            return;
+
+        case AL_POSITION:
+        case AL_VELOCITY:
+            alListener3f(param, values[0], values[1], values[2]);
+            return;
+        }
+    }
+
+    context = GetContextRef();
+    if(!context) return;
+
+    WriteLock(&context->PropLock);
+    if(!(values))
+        SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+    switch(param)
+    {
+    case AL_ORIENTATION:
+        if(!(isfinite(values[0]) && isfinite(values[1]) && isfinite(values[2]) &&
+             isfinite(values[3]) && isfinite(values[4]) && isfinite(values[5])))
+            SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+        /* AT then UP */
+        context->Listener->Forward[0] = values[0];
+        context->Listener->Forward[1] = values[1];
+        context->Listener->Forward[2] = values[2];
+        context->Listener->Up[0] = values[3];
+        context->Listener->Up[1] = values[4];
+        context->Listener->Up[2] = values[5];
+        break;
+
+    default:
+        SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
+    }
+    if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
+        UpdateListenerProps(context);
+
+done:
+    WriteUnlock(&context->PropLock);
+    ALCcontext_DecRef(context);
+}
+
+
+AL_API ALvoid AL_APIENTRY alListeneri(ALenum param, ALint UNUSED(value))
+{
+    ALCcontext *context;
+
+    context = GetContextRef();
+    if(!context) return;
+
+    WriteLock(&context->PropLock);
+    switch(param)
+    {
+    default:
+        SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
+    }
+    if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
+        UpdateListenerProps(context);
+
+done:
+    WriteUnlock(&context->PropLock);
+    ALCcontext_DecRef(context);
+}
+
+
+AL_API void AL_APIENTRY alListener3i(ALenum param, ALint value1, ALint value2, ALint value3)
+{
+    ALCcontext *context;
+
+    switch(param)
+    {
+    case AL_POSITION:
+    case AL_VELOCITY:
+        alListener3f(param, (ALfloat)value1, (ALfloat)value2, (ALfloat)value3);
+        return;
+    }
+
+    context = GetContextRef();
+    if(!context) return;
+
+    WriteLock(&context->PropLock);
+    switch(param)
+    {
+    default:
+        SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
+    }
+    if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
+        UpdateListenerProps(context);
+
+done:
+    WriteUnlock(&context->PropLock);
+    ALCcontext_DecRef(context);
+}
+
+
+AL_API void AL_APIENTRY alListeneriv(ALenum param, const ALint *values)
+{
+    ALCcontext *context;
+
+    if(values)
+    {
+        ALfloat fvals[6];
+        switch(param)
+        {
+        case AL_POSITION:
+        case AL_VELOCITY:
+            alListener3f(param, (ALfloat)values[0], (ALfloat)values[1], (ALfloat)values[2]);
+            return;
+
+        case AL_ORIENTATION:
+            fvals[0] = (ALfloat)values[0];
+            fvals[1] = (ALfloat)values[1];
+            fvals[2] = (ALfloat)values[2];
+            fvals[3] = (ALfloat)values[3];
+            fvals[4] = (ALfloat)values[4];
+            fvals[5] = (ALfloat)values[5];
+            alListenerfv(param, fvals);
+            return;
+        }
+    }
+
+    context = GetContextRef();
+    if(!context) return;
+
+    WriteLock(&context->PropLock);
+    if(!(values))
+        SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+    switch(param)
+    {
+    default:
+        SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
+    }
+    if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
+        UpdateListenerProps(context);
+
+done:
+    WriteUnlock(&context->PropLock);
+    ALCcontext_DecRef(context);
+}
+
+
+AL_API ALvoid AL_APIENTRY alGetListenerf(ALenum param, ALfloat *value)
+{
+    ALCcontext *context;
+
+    context = GetContextRef();
+    if(!context) return;
+
+    ReadLock(&context->PropLock);
+    if(!(value))
+        SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+    switch(param)
+    {
+    case AL_GAIN:
+        *value = context->Listener->Gain;
+        break;
+
+    case AL_METERS_PER_UNIT:
+        *value = context->Listener->MetersPerUnit;
+        break;
+
+    default:
+        SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
+    }
+
+done:
+    ReadUnlock(&context->PropLock);
+    ALCcontext_DecRef(context);
+}
+
+
+AL_API ALvoid AL_APIENTRY alGetListener3f(ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3)
+{
+    ALCcontext *context;
+
+    context = GetContextRef();
+    if(!context) return;
+
+    ReadLock(&context->PropLock);
+    if(!(value1 && value2 && value3))
+        SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+    switch(param)
+    {
+    case AL_POSITION:
+        *value1 = context->Listener->Position[0];
+        *value2 = context->Listener->Position[1];
+        *value3 = context->Listener->Position[2];
+        break;
+
+    case AL_VELOCITY:
+        *value1 = context->Listener->Velocity[0];
+        *value2 = context->Listener->Velocity[1];
+        *value3 = context->Listener->Velocity[2];
+        break;
+
+    default:
+        SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
+    }
+
+done:
+    ReadUnlock(&context->PropLock);
+    ALCcontext_DecRef(context);
+}
+
+
+AL_API ALvoid AL_APIENTRY alGetListenerfv(ALenum param, ALfloat *values)
+{
+    ALCcontext *context;
+
+    switch(param)
+    {
+    case AL_GAIN:
+    case AL_METERS_PER_UNIT:
+        alGetListenerf(param, values);
+        return;
+
+    case AL_POSITION:
+    case AL_VELOCITY:
+        alGetListener3f(param, values+0, values+1, values+2);
+        return;
+    }
+
+    context = GetContextRef();
+    if(!context) return;
+
+    ReadLock(&context->PropLock);
+    if(!(values))
+        SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+    switch(param)
+    {
+    case AL_ORIENTATION:
+        // AT then UP
+        values[0] = context->Listener->Forward[0];
+        values[1] = context->Listener->Forward[1];
+        values[2] = context->Listener->Forward[2];
+        values[3] = context->Listener->Up[0];
+        values[4] = context->Listener->Up[1];
+        values[5] = context->Listener->Up[2];
+        break;
+
+    default:
+        SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
+    }
+
+done:
+    ReadUnlock(&context->PropLock);
+    ALCcontext_DecRef(context);
+}
+
+
+AL_API ALvoid AL_APIENTRY alGetListeneri(ALenum param, ALint *value)
+{
+    ALCcontext *context;
+
+    context = GetContextRef();
+    if(!context) return;
+
+    ReadLock(&context->PropLock);
+    if(!(value))
+        SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+    switch(param)
+    {
+    default:
+        SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
+    }
+
+done:
+    ReadUnlock(&context->PropLock);
+    ALCcontext_DecRef(context);
+}
+
+
+AL_API void AL_APIENTRY alGetListener3i(ALenum param, ALint *value1, ALint *value2, ALint *value3)
+{
+    ALCcontext *context;
+
+    context = GetContextRef();
+    if(!context) return;
+
+    ReadLock(&context->PropLock);
+    if(!(value1 && value2 && value3))
+        SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+    switch (param)
+    {
+    case AL_POSITION:
+        *value1 = (ALint)context->Listener->Position[0];
+        *value2 = (ALint)context->Listener->Position[1];
+        *value3 = (ALint)context->Listener->Position[2];
+        break;
+
+    case AL_VELOCITY:
+        *value1 = (ALint)context->Listener->Velocity[0];
+        *value2 = (ALint)context->Listener->Velocity[1];
+        *value3 = (ALint)context->Listener->Velocity[2];
+        break;
+
+    default:
+        SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
+    }
+
+done:
+    ReadUnlock(&context->PropLock);
+    ALCcontext_DecRef(context);
+}
+
+
+AL_API void AL_APIENTRY alGetListeneriv(ALenum param, ALint* values)
+{
+    ALCcontext *context;
+
+    switch(param)
+    {
+    case AL_POSITION:
+    case AL_VELOCITY:
+        alGetListener3i(param, values+0, values+1, values+2);
+        return;
+    }
+
+    context = GetContextRef();
+    if(!context) return;
+
+    ReadLock(&context->PropLock);
+    if(!(values))
+        SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+    switch(param)
+    {
+    case AL_ORIENTATION:
+        // AT then UP
+        values[0] = (ALint)context->Listener->Forward[0];
+        values[1] = (ALint)context->Listener->Forward[1];
+        values[2] = (ALint)context->Listener->Forward[2];
+        values[3] = (ALint)context->Listener->Up[0];
+        values[4] = (ALint)context->Listener->Up[1];
+        values[5] = (ALint)context->Listener->Up[2];
+        break;
+
+    default:
+        SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
+    }
+
+done:
+    ReadUnlock(&context->PropLock);
+    ALCcontext_DecRef(context);
+}
+
+
+void UpdateListenerProps(ALCcontext *context)
+{
+    ALlistener *listener = context->Listener;
+    struct ALlistenerProps *props;
+
+    /* Get an unused proprty container, or allocate a new one as needed. */
+    props = ATOMIC_LOAD(&listener->FreeList, almemory_order_acquire);
+    if(!props)
+        props = al_calloc(16, sizeof(*props));
+    else
+    {
+        struct ALlistenerProps *next;
+        do {
+            next = ATOMIC_LOAD(&props->next, almemory_order_relaxed);
+        } while(ATOMIC_COMPARE_EXCHANGE_WEAK(struct ALlistenerProps*,
+                &listener->FreeList, &props, next, almemory_order_seq_cst,
+                almemory_order_consume) == 0);
+    }
+
+    /* Copy in current property values. */
+    ATOMIC_STORE(&props->Position[0], listener->Position[0], almemory_order_relaxed);
+    ATOMIC_STORE(&props->Position[1], listener->Position[1], almemory_order_relaxed);
+    ATOMIC_STORE(&props->Position[2], listener->Position[2], almemory_order_relaxed);
+
+    ATOMIC_STORE(&props->Velocity[0], listener->Velocity[0], almemory_order_relaxed);
+    ATOMIC_STORE(&props->Velocity[1], listener->Velocity[1], almemory_order_relaxed);
+    ATOMIC_STORE(&props->Velocity[2], listener->Velocity[2], almemory_order_relaxed);
+
+    ATOMIC_STORE(&props->Forward[0], listener->Forward[0], almemory_order_relaxed);
+    ATOMIC_STORE(&props->Forward[1], listener->Forward[1], almemory_order_relaxed);
+    ATOMIC_STORE(&props->Forward[2], listener->Forward[2], almemory_order_relaxed);
+    ATOMIC_STORE(&props->Up[0], listener->Up[0], almemory_order_relaxed);
+    ATOMIC_STORE(&props->Up[1], listener->Up[1], almemory_order_relaxed);
+    ATOMIC_STORE(&props->Up[2], listener->Up[2], almemory_order_relaxed);
+
+    ATOMIC_STORE(&props->Gain, listener->Gain, almemory_order_relaxed);
+    ATOMIC_STORE(&props->MetersPerUnit, listener->MetersPerUnit, almemory_order_relaxed);
+
+    ATOMIC_STORE(&props->DopplerFactor, context->DopplerFactor, almemory_order_relaxed);
+    ATOMIC_STORE(&props->DopplerVelocity, context->DopplerVelocity, almemory_order_relaxed);
+    ATOMIC_STORE(&props->SpeedOfSound, context->SpeedOfSound, almemory_order_relaxed);
+
+    ATOMIC_STORE(&props->SourceDistanceModel, context->SourceDistanceModel, almemory_order_relaxed);
+    ATOMIC_STORE(&props->DistanceModel, context->DistanceModel, almemory_order_relaxed);
+
+    /* Set the new container for updating internal parameters. */
+    props = ATOMIC_EXCHANGE(struct ALlistenerProps*, &listener->Update, props, almemory_order_acq_rel);
+    if(props)
+    {
+        /* If there was an unused update container, put it back in the
+         * freelist.
+         */
+        struct ALlistenerProps *first = ATOMIC_LOAD(&listener->FreeList);
+        do {
+            ATOMIC_STORE(&props->next, first, almemory_order_relaxed);
+        } while(ATOMIC_COMPARE_EXCHANGE_WEAK(struct ALlistenerProps*,
+                &listener->FreeList, &first, props) == 0);
+    }
+}

+ 3401 - 0
Engine/lib/openal-soft/OpenAL32/alSource.c

@@ -0,0 +1,3401 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 1999-2007 by authors.
+ * This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the
+ *  Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <limits.h>
+#include <math.h>
+#include <float.h>
+
+#include "AL/al.h"
+#include "AL/alc.h"
+#include "alMain.h"
+#include "alError.h"
+#include "alSource.h"
+#include "alBuffer.h"
+#include "alThunk.h"
+#include "alAuxEffectSlot.h"
+
+#include "backends/base.h"
+
+#include "threads.h"
+#include "almalloc.h"
+
+
+extern inline void LockSourcesRead(ALCcontext *context);
+extern inline void UnlockSourcesRead(ALCcontext *context);
+extern inline void LockSourcesWrite(ALCcontext *context);
+extern inline void UnlockSourcesWrite(ALCcontext *context);
+extern inline struct ALsource *LookupSource(ALCcontext *context, ALuint id);
+extern inline struct ALsource *RemoveSource(ALCcontext *context, ALuint id);
+
+static void InitSourceParams(ALsource *Source);
+static void DeinitSource(ALsource *source);
+static void UpdateSourceProps(ALsource *source, ALuint num_sends);
+static ALint64 GetSourceSampleOffset(ALsource *Source, ALCdevice *device, ALuint64 *clocktime);
+static ALdouble GetSourceSecOffset(ALsource *Source, ALCdevice *device, ALuint64 *clocktime);
+static ALdouble GetSourceOffset(ALsource *Source, ALenum name, ALCdevice *device);
+static ALboolean GetSampleOffset(ALsource *Source, ALuint *offset, ALuint *frac);
+
+typedef enum SourceProp {
+    srcPitch = AL_PITCH,
+    srcGain = AL_GAIN,
+    srcMinGain = AL_MIN_GAIN,
+    srcMaxGain = AL_MAX_GAIN,
+    srcMaxDistance = AL_MAX_DISTANCE,
+    srcRolloffFactor = AL_ROLLOFF_FACTOR,
+    srcDopplerFactor = AL_DOPPLER_FACTOR,
+    srcConeOuterGain = AL_CONE_OUTER_GAIN,
+    srcSecOffset = AL_SEC_OFFSET,
+    srcSampleOffset = AL_SAMPLE_OFFSET,
+    srcByteOffset = AL_BYTE_OFFSET,
+    srcConeInnerAngle = AL_CONE_INNER_ANGLE,
+    srcConeOuterAngle = AL_CONE_OUTER_ANGLE,
+    srcRefDistance = AL_REFERENCE_DISTANCE,
+
+    srcPosition = AL_POSITION,
+    srcVelocity = AL_VELOCITY,
+    srcDirection = AL_DIRECTION,
+
+    srcSourceRelative = AL_SOURCE_RELATIVE,
+    srcLooping = AL_LOOPING,
+    srcBuffer = AL_BUFFER,
+    srcSourceState = AL_SOURCE_STATE,
+    srcBuffersQueued = AL_BUFFERS_QUEUED,
+    srcBuffersProcessed = AL_BUFFERS_PROCESSED,
+    srcSourceType = AL_SOURCE_TYPE,
+
+    /* ALC_EXT_EFX */
+    srcConeOuterGainHF = AL_CONE_OUTER_GAINHF,
+    srcAirAbsorptionFactor = AL_AIR_ABSORPTION_FACTOR,
+    srcRoomRolloffFactor =  AL_ROOM_ROLLOFF_FACTOR,
+    srcDirectFilterGainHFAuto = AL_DIRECT_FILTER_GAINHF_AUTO,
+    srcAuxSendFilterGainAuto = AL_AUXILIARY_SEND_FILTER_GAIN_AUTO,
+    srcAuxSendFilterGainHFAuto = AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO,
+    srcDirectFilter = AL_DIRECT_FILTER,
+    srcAuxSendFilter = AL_AUXILIARY_SEND_FILTER,
+
+    /* AL_SOFT_direct_channels */
+    srcDirectChannelsSOFT = AL_DIRECT_CHANNELS_SOFT,
+
+    /* AL_EXT_source_distance_model */
+    srcDistanceModel = AL_DISTANCE_MODEL,
+
+    srcByteLengthSOFT = AL_BYTE_LENGTH_SOFT,
+    srcSampleLengthSOFT = AL_SAMPLE_LENGTH_SOFT,
+    srcSecLengthSOFT = AL_SEC_LENGTH_SOFT,
+
+    /* AL_SOFT_source_latency */
+    srcSampleOffsetLatencySOFT = AL_SAMPLE_OFFSET_LATENCY_SOFT,
+    srcSecOffsetLatencySOFT = AL_SEC_OFFSET_LATENCY_SOFT,
+
+    /* AL_EXT_STEREO_ANGLES */
+    srcAngles = AL_STEREO_ANGLES,
+
+    /* AL_EXT_SOURCE_RADIUS */
+    srcRadius = AL_SOURCE_RADIUS,
+
+    /* AL_EXT_BFORMAT */
+    srcOrientation = AL_ORIENTATION,
+} SourceProp;
+
+static ALboolean SetSourcefv(ALsource *Source, ALCcontext *Context, SourceProp prop, const ALfloat *values);
+static ALboolean SetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp prop, const ALint *values);
+static ALboolean SetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp prop, const ALint64SOFT *values);
+
+static ALboolean GetSourcedv(ALsource *Source, ALCcontext *Context, SourceProp prop, ALdouble *values);
+static ALboolean GetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp prop, ALint *values);
+static ALboolean GetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp prop, ALint64 *values);
+
+static inline bool SourceShouldUpdate(const ALsource *source, const ALCcontext *context)
+{
+    return (source->state == AL_PLAYING || source->state == AL_PAUSED) &&
+           !ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire);
+}
+
+static ALint FloatValsByProp(ALenum prop)
+{
+    if(prop != (ALenum)((SourceProp)prop))
+        return 0;
+    switch((SourceProp)prop)
+    {
+        case AL_PITCH:
+        case AL_GAIN:
+        case AL_MIN_GAIN:
+        case AL_MAX_GAIN:
+        case AL_MAX_DISTANCE:
+        case AL_ROLLOFF_FACTOR:
+        case AL_DOPPLER_FACTOR:
+        case AL_CONE_OUTER_GAIN:
+        case AL_SEC_OFFSET:
+        case AL_SAMPLE_OFFSET:
+        case AL_BYTE_OFFSET:
+        case AL_CONE_INNER_ANGLE:
+        case AL_CONE_OUTER_ANGLE:
+        case AL_REFERENCE_DISTANCE:
+        case AL_CONE_OUTER_GAINHF:
+        case AL_AIR_ABSORPTION_FACTOR:
+        case AL_ROOM_ROLLOFF_FACTOR:
+        case AL_DIRECT_FILTER_GAINHF_AUTO:
+        case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
+        case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
+        case AL_DIRECT_CHANNELS_SOFT:
+        case AL_DISTANCE_MODEL:
+        case AL_SOURCE_RELATIVE:
+        case AL_LOOPING:
+        case AL_SOURCE_STATE:
+        case AL_BUFFERS_QUEUED:
+        case AL_BUFFERS_PROCESSED:
+        case AL_SOURCE_TYPE:
+        case AL_BYTE_LENGTH_SOFT:
+        case AL_SAMPLE_LENGTH_SOFT:
+        case AL_SEC_LENGTH_SOFT:
+        case AL_SOURCE_RADIUS:
+            return 1;
+
+        case AL_STEREO_ANGLES:
+            return 2;
+
+        case AL_POSITION:
+        case AL_VELOCITY:
+        case AL_DIRECTION:
+            return 3;
+
+        case AL_ORIENTATION:
+            return 6;
+
+        case AL_SEC_OFFSET_LATENCY_SOFT:
+            break; /* Double only */
+
+        case AL_BUFFER:
+        case AL_DIRECT_FILTER:
+        case AL_AUXILIARY_SEND_FILTER:
+            break; /* i/i64 only */
+        case AL_SAMPLE_OFFSET_LATENCY_SOFT:
+            break; /* i64 only */
+    }
+    return 0;
+}
+static ALint DoubleValsByProp(ALenum prop)
+{
+    if(prop != (ALenum)((SourceProp)prop))
+        return 0;
+    switch((SourceProp)prop)
+    {
+        case AL_PITCH:
+        case AL_GAIN:
+        case AL_MIN_GAIN:
+        case AL_MAX_GAIN:
+        case AL_MAX_DISTANCE:
+        case AL_ROLLOFF_FACTOR:
+        case AL_DOPPLER_FACTOR:
+        case AL_CONE_OUTER_GAIN:
+        case AL_SEC_OFFSET:
+        case AL_SAMPLE_OFFSET:
+        case AL_BYTE_OFFSET:
+        case AL_CONE_INNER_ANGLE:
+        case AL_CONE_OUTER_ANGLE:
+        case AL_REFERENCE_DISTANCE:
+        case AL_CONE_OUTER_GAINHF:
+        case AL_AIR_ABSORPTION_FACTOR:
+        case AL_ROOM_ROLLOFF_FACTOR:
+        case AL_DIRECT_FILTER_GAINHF_AUTO:
+        case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
+        case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
+        case AL_DIRECT_CHANNELS_SOFT:
+        case AL_DISTANCE_MODEL:
+        case AL_SOURCE_RELATIVE:
+        case AL_LOOPING:
+        case AL_SOURCE_STATE:
+        case AL_BUFFERS_QUEUED:
+        case AL_BUFFERS_PROCESSED:
+        case AL_SOURCE_TYPE:
+        case AL_BYTE_LENGTH_SOFT:
+        case AL_SAMPLE_LENGTH_SOFT:
+        case AL_SEC_LENGTH_SOFT:
+        case AL_SOURCE_RADIUS:
+            return 1;
+
+        case AL_SEC_OFFSET_LATENCY_SOFT:
+        case AL_STEREO_ANGLES:
+            return 2;
+
+        case AL_POSITION:
+        case AL_VELOCITY:
+        case AL_DIRECTION:
+            return 3;
+
+        case AL_ORIENTATION:
+            return 6;
+
+        case AL_BUFFER:
+        case AL_DIRECT_FILTER:
+        case AL_AUXILIARY_SEND_FILTER:
+            break; /* i/i64 only */
+        case AL_SAMPLE_OFFSET_LATENCY_SOFT:
+            break; /* i64 only */
+    }
+    return 0;
+}
+
+static ALint IntValsByProp(ALenum prop)
+{
+    if(prop != (ALenum)((SourceProp)prop))
+        return 0;
+    switch((SourceProp)prop)
+    {
+        case AL_PITCH:
+        case AL_GAIN:
+        case AL_MIN_GAIN:
+        case AL_MAX_GAIN:
+        case AL_MAX_DISTANCE:
+        case AL_ROLLOFF_FACTOR:
+        case AL_DOPPLER_FACTOR:
+        case AL_CONE_OUTER_GAIN:
+        case AL_SEC_OFFSET:
+        case AL_SAMPLE_OFFSET:
+        case AL_BYTE_OFFSET:
+        case AL_CONE_INNER_ANGLE:
+        case AL_CONE_OUTER_ANGLE:
+        case AL_REFERENCE_DISTANCE:
+        case AL_CONE_OUTER_GAINHF:
+        case AL_AIR_ABSORPTION_FACTOR:
+        case AL_ROOM_ROLLOFF_FACTOR:
+        case AL_DIRECT_FILTER_GAINHF_AUTO:
+        case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
+        case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
+        case AL_DIRECT_CHANNELS_SOFT:
+        case AL_DISTANCE_MODEL:
+        case AL_SOURCE_RELATIVE:
+        case AL_LOOPING:
+        case AL_BUFFER:
+        case AL_SOURCE_STATE:
+        case AL_BUFFERS_QUEUED:
+        case AL_BUFFERS_PROCESSED:
+        case AL_SOURCE_TYPE:
+        case AL_DIRECT_FILTER:
+        case AL_BYTE_LENGTH_SOFT:
+        case AL_SAMPLE_LENGTH_SOFT:
+        case AL_SEC_LENGTH_SOFT:
+        case AL_SOURCE_RADIUS:
+            return 1;
+
+        case AL_POSITION:
+        case AL_VELOCITY:
+        case AL_DIRECTION:
+        case AL_AUXILIARY_SEND_FILTER:
+            return 3;
+
+        case AL_ORIENTATION:
+            return 6;
+
+        case AL_SAMPLE_OFFSET_LATENCY_SOFT:
+            break; /* i64 only */
+        case AL_SEC_OFFSET_LATENCY_SOFT:
+            break; /* Double only */
+        case AL_STEREO_ANGLES:
+            break; /* Float/double only */
+    }
+    return 0;
+}
+static ALint Int64ValsByProp(ALenum prop)
+{
+    if(prop != (ALenum)((SourceProp)prop))
+        return 0;
+    switch((SourceProp)prop)
+    {
+        case AL_PITCH:
+        case AL_GAIN:
+        case AL_MIN_GAIN:
+        case AL_MAX_GAIN:
+        case AL_MAX_DISTANCE:
+        case AL_ROLLOFF_FACTOR:
+        case AL_DOPPLER_FACTOR:
+        case AL_CONE_OUTER_GAIN:
+        case AL_SEC_OFFSET:
+        case AL_SAMPLE_OFFSET:
+        case AL_BYTE_OFFSET:
+        case AL_CONE_INNER_ANGLE:
+        case AL_CONE_OUTER_ANGLE:
+        case AL_REFERENCE_DISTANCE:
+        case AL_CONE_OUTER_GAINHF:
+        case AL_AIR_ABSORPTION_FACTOR:
+        case AL_ROOM_ROLLOFF_FACTOR:
+        case AL_DIRECT_FILTER_GAINHF_AUTO:
+        case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
+        case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
+        case AL_DIRECT_CHANNELS_SOFT:
+        case AL_DISTANCE_MODEL:
+        case AL_SOURCE_RELATIVE:
+        case AL_LOOPING:
+        case AL_BUFFER:
+        case AL_SOURCE_STATE:
+        case AL_BUFFERS_QUEUED:
+        case AL_BUFFERS_PROCESSED:
+        case AL_SOURCE_TYPE:
+        case AL_DIRECT_FILTER:
+        case AL_BYTE_LENGTH_SOFT:
+        case AL_SAMPLE_LENGTH_SOFT:
+        case AL_SEC_LENGTH_SOFT:
+        case AL_SOURCE_RADIUS:
+            return 1;
+
+        case AL_SAMPLE_OFFSET_LATENCY_SOFT:
+            return 2;
+
+        case AL_POSITION:
+        case AL_VELOCITY:
+        case AL_DIRECTION:
+        case AL_AUXILIARY_SEND_FILTER:
+            return 3;
+
+        case AL_ORIENTATION:
+            return 6;
+
+        case AL_SEC_OFFSET_LATENCY_SOFT:
+            break; /* Double only */
+        case AL_STEREO_ANGLES:
+            break; /* Float/double only */
+    }
+    return 0;
+}
+
+
+#define CHECKVAL(x) do {                                                      \
+    if(!(x))                                                                  \
+        SET_ERROR_AND_RETURN_VALUE(Context, AL_INVALID_VALUE, AL_FALSE);      \
+} while(0)
+
+#define DO_UPDATEPROPS() do {                                                 \
+    if(SourceShouldUpdate(Source, Context))                                   \
+        UpdateSourceProps(Source, device->NumAuxSends);                       \
+} while(0)
+
+static ALboolean SetSourcefv(ALsource *Source, ALCcontext *Context, SourceProp prop, const ALfloat *values)
+{
+    ALCdevice *device = Context->Device;
+    ALint ival;
+
+    switch(prop)
+    {
+        case AL_BYTE_LENGTH_SOFT:
+        case AL_SAMPLE_LENGTH_SOFT:
+        case AL_SEC_LENGTH_SOFT:
+        case AL_SEC_OFFSET_LATENCY_SOFT:
+            /* Query only */
+            SET_ERROR_AND_RETURN_VALUE(Context, AL_INVALID_OPERATION, AL_FALSE);
+
+        case AL_PITCH:
+            CHECKVAL(*values >= 0.0f);
+
+            Source->Pitch = *values;
+            DO_UPDATEPROPS();
+            return AL_TRUE;
+
+        case AL_CONE_INNER_ANGLE:
+            CHECKVAL(*values >= 0.0f && *values <= 360.0f);
+
+            Source->InnerAngle = *values;
+            DO_UPDATEPROPS();
+            return AL_TRUE;
+
+        case AL_CONE_OUTER_ANGLE:
+            CHECKVAL(*values >= 0.0f && *values <= 360.0f);
+
+            Source->OuterAngle = *values;
+            DO_UPDATEPROPS();
+            return AL_TRUE;
+
+        case AL_GAIN:
+            CHECKVAL(*values >= 0.0f);
+
+            Source->Gain = *values;
+            DO_UPDATEPROPS();
+            return AL_TRUE;
+
+        case AL_MAX_DISTANCE:
+            CHECKVAL(*values >= 0.0f);
+
+            Source->MaxDistance = *values;
+            DO_UPDATEPROPS();
+            return AL_TRUE;
+
+        case AL_ROLLOFF_FACTOR:
+            CHECKVAL(*values >= 0.0f);
+
+            Source->RollOffFactor = *values;
+            DO_UPDATEPROPS();
+            return AL_TRUE;
+
+        case AL_REFERENCE_DISTANCE:
+            CHECKVAL(*values >= 0.0f);
+
+            Source->RefDistance = *values;
+            DO_UPDATEPROPS();
+            return AL_TRUE;
+
+        case AL_MIN_GAIN:
+            CHECKVAL(*values >= 0.0f);
+
+            Source->MinGain = *values;
+            DO_UPDATEPROPS();
+            return AL_TRUE;
+
+        case AL_MAX_GAIN:
+            CHECKVAL(*values >= 0.0f);
+
+            Source->MaxGain = *values;
+            DO_UPDATEPROPS();
+            return AL_TRUE;
+
+        case AL_CONE_OUTER_GAIN:
+            CHECKVAL(*values >= 0.0f && *values <= 1.0f);
+
+            Source->OuterGain = *values;
+            DO_UPDATEPROPS();
+            return AL_TRUE;
+
+        case AL_CONE_OUTER_GAINHF:
+            CHECKVAL(*values >= 0.0f && *values <= 1.0f);
+
+            Source->OuterGainHF = *values;
+            DO_UPDATEPROPS();
+            return AL_TRUE;
+
+        case AL_AIR_ABSORPTION_FACTOR:
+            CHECKVAL(*values >= 0.0f && *values <= 10.0f);
+
+            Source->AirAbsorptionFactor = *values;
+            DO_UPDATEPROPS();
+            return AL_TRUE;
+
+        case AL_ROOM_ROLLOFF_FACTOR:
+            CHECKVAL(*values >= 0.0f && *values <= 10.0f);
+
+            Source->RoomRolloffFactor = *values;
+            DO_UPDATEPROPS();
+            return AL_TRUE;
+
+        case AL_DOPPLER_FACTOR:
+            CHECKVAL(*values >= 0.0f && *values <= 1.0f);
+
+            Source->DopplerFactor = *values;
+            DO_UPDATEPROPS();
+            return AL_TRUE;
+
+        case AL_SEC_OFFSET:
+        case AL_SAMPLE_OFFSET:
+        case AL_BYTE_OFFSET:
+            CHECKVAL(*values >= 0.0f);
+
+            Source->OffsetType = prop;
+            Source->Offset = *values;
+
+            if((Source->state == AL_PLAYING || Source->state == AL_PAUSED) &&
+               !ATOMIC_LOAD(&Context->DeferUpdates, almemory_order_acquire))
+            {
+                LockContext(Context);
+                WriteLock(&Source->queue_lock);
+                if(ApplyOffset(Source) == AL_FALSE)
+                {
+                    WriteUnlock(&Source->queue_lock);
+                    UnlockContext(Context);
+                    SET_ERROR_AND_RETURN_VALUE(Context, AL_INVALID_VALUE, AL_FALSE);
+                }
+                WriteUnlock(&Source->queue_lock);
+                UnlockContext(Context);
+            }
+            return AL_TRUE;
+
+        case AL_SOURCE_RADIUS:
+            CHECKVAL(*values >= 0.0f && isfinite(*values));
+
+            Source->Radius = *values;
+            DO_UPDATEPROPS();
+            return AL_TRUE;
+
+        case AL_STEREO_ANGLES:
+            CHECKVAL(isfinite(values[0]) && isfinite(values[1]));
+
+            Source->StereoPan[0] = values[0];
+            Source->StereoPan[1] = values[1];
+            DO_UPDATEPROPS();
+            return AL_TRUE;
+
+
+        case AL_POSITION:
+            CHECKVAL(isfinite(values[0]) && isfinite(values[1]) && isfinite(values[2]));
+
+            Source->Position[0] = values[0];
+            Source->Position[1] = values[1];
+            Source->Position[2] = values[2];
+            DO_UPDATEPROPS();
+            return AL_TRUE;
+
+        case AL_VELOCITY:
+            CHECKVAL(isfinite(values[0]) && isfinite(values[1]) && isfinite(values[2]));
+
+            Source->Velocity[0] = values[0];
+            Source->Velocity[1] = values[1];
+            Source->Velocity[2] = values[2];
+            DO_UPDATEPROPS();
+            return AL_TRUE;
+
+        case AL_DIRECTION:
+            CHECKVAL(isfinite(values[0]) && isfinite(values[1]) && isfinite(values[2]));
+
+            Source->Direction[0] = values[0];
+            Source->Direction[1] = values[1];
+            Source->Direction[2] = values[2];
+            DO_UPDATEPROPS();
+            return AL_TRUE;
+
+        case AL_ORIENTATION:
+            CHECKVAL(isfinite(values[0]) && isfinite(values[1]) && isfinite(values[2]) &&
+                     isfinite(values[3]) && isfinite(values[4]) && isfinite(values[5]));
+
+            Source->Orientation[0][0] = values[0];
+            Source->Orientation[0][1] = values[1];
+            Source->Orientation[0][2] = values[2];
+            Source->Orientation[1][0] = values[3];
+            Source->Orientation[1][1] = values[4];
+            Source->Orientation[1][2] = values[5];
+            DO_UPDATEPROPS();
+            return AL_TRUE;
+
+
+        case AL_SOURCE_RELATIVE:
+        case AL_LOOPING:
+        case AL_SOURCE_STATE:
+        case AL_SOURCE_TYPE:
+        case AL_DISTANCE_MODEL:
+        case AL_DIRECT_FILTER_GAINHF_AUTO:
+        case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
+        case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
+        case AL_DIRECT_CHANNELS_SOFT:
+            ival = (ALint)values[0];
+            return SetSourceiv(Source, Context, prop, &ival);
+
+        case AL_BUFFERS_QUEUED:
+        case AL_BUFFERS_PROCESSED:
+            ival = (ALint)((ALuint)values[0]);
+            return SetSourceiv(Source, Context, prop, &ival);
+
+        case AL_BUFFER:
+        case AL_DIRECT_FILTER:
+        case AL_AUXILIARY_SEND_FILTER:
+        case AL_SAMPLE_OFFSET_LATENCY_SOFT:
+            break;
+    }
+
+    ERR("Unexpected property: 0x%04x\n", prop);
+    SET_ERROR_AND_RETURN_VALUE(Context, AL_INVALID_ENUM, AL_FALSE);
+}
+
+static ALboolean SetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp prop, const ALint *values)
+{
+    ALCdevice *device = Context->Device;
+    ALbuffer  *buffer = NULL;
+    ALfilter  *filter = NULL;
+    ALeffectslot *slot = NULL;
+    ALbufferlistitem *oldlist;
+    ALbufferlistitem *newlist;
+    ALfloat fvals[6];
+
+    switch(prop)
+    {
+        case AL_SOURCE_STATE:
+        case AL_SOURCE_TYPE:
+        case AL_BUFFERS_QUEUED:
+        case AL_BUFFERS_PROCESSED:
+        case AL_BYTE_LENGTH_SOFT:
+        case AL_SAMPLE_LENGTH_SOFT:
+        case AL_SEC_LENGTH_SOFT:
+            /* Query only */
+            SET_ERROR_AND_RETURN_VALUE(Context, AL_INVALID_OPERATION, AL_FALSE);
+
+        case AL_SOURCE_RELATIVE:
+            CHECKVAL(*values == AL_FALSE || *values == AL_TRUE);
+
+            Source->HeadRelative = (ALboolean)*values;
+            DO_UPDATEPROPS();
+            return AL_TRUE;
+
+        case AL_LOOPING:
+            CHECKVAL(*values == AL_FALSE || *values == AL_TRUE);
+
+            WriteLock(&Source->queue_lock);
+            ATOMIC_STORE(&Source->looping, *values);
+            WriteUnlock(&Source->queue_lock);
+            return AL_TRUE;
+
+        case AL_BUFFER:
+            LockBuffersRead(device);
+            if(!(*values == 0 || (buffer=LookupBuffer(device, *values)) != NULL))
+            {
+                UnlockBuffersRead(device);
+                SET_ERROR_AND_RETURN_VALUE(Context, AL_INVALID_VALUE, AL_FALSE);
+            }
+
+            WriteLock(&Source->queue_lock);
+            if(!(Source->state == AL_STOPPED || Source->state == AL_INITIAL))
+            {
+                WriteUnlock(&Source->queue_lock);
+                UnlockBuffersRead(device);
+                SET_ERROR_AND_RETURN_VALUE(Context, AL_INVALID_OPERATION, AL_FALSE);
+            }
+
+            if(buffer != NULL)
+            {
+                /* Add the selected buffer to a one-item queue */
+                newlist = malloc(sizeof(ALbufferlistitem));
+                newlist->buffer = buffer;
+                newlist->next = NULL;
+                IncrementRef(&buffer->ref);
+
+                /* Source is now Static */
+                Source->SourceType = AL_STATIC;
+
+                ReadLock(&buffer->lock);
+                Source->NumChannels = ChannelsFromFmt(buffer->FmtChannels);
+                Source->SampleSize  = BytesFromFmt(buffer->FmtType);
+                ReadUnlock(&buffer->lock);
+            }
+            else
+            {
+                /* Source is now Undetermined */
+                Source->SourceType = AL_UNDETERMINED;
+                newlist = NULL;
+            }
+            oldlist = ATOMIC_EXCHANGE(ALbufferlistitem*, &Source->queue, newlist);
+            ATOMIC_STORE(&Source->current_buffer, newlist);
+            WriteUnlock(&Source->queue_lock);
+            UnlockBuffersRead(device);
+
+            /* Delete all elements in the previous queue */
+            while(oldlist != NULL)
+            {
+                ALbufferlistitem *temp = oldlist;
+                oldlist = temp->next;
+
+                if(temp->buffer)
+                    DecrementRef(&temp->buffer->ref);
+                free(temp);
+            }
+            return AL_TRUE;
+
+        case AL_SEC_OFFSET:
+        case AL_SAMPLE_OFFSET:
+        case AL_BYTE_OFFSET:
+            CHECKVAL(*values >= 0);
+
+            Source->OffsetType = prop;
+            Source->Offset = *values;
+
+            if((Source->state == AL_PLAYING || Source->state == AL_PAUSED) &&
+                !ATOMIC_LOAD(&Context->DeferUpdates, almemory_order_acquire))
+            {
+                LockContext(Context);
+                WriteLock(&Source->queue_lock);
+                if(ApplyOffset(Source) == AL_FALSE)
+                {
+                    WriteUnlock(&Source->queue_lock);
+                    UnlockContext(Context);
+                    SET_ERROR_AND_RETURN_VALUE(Context, AL_INVALID_VALUE, AL_FALSE);
+                }
+                WriteUnlock(&Source->queue_lock);
+                UnlockContext(Context);
+            }
+            return AL_TRUE;
+
+        case AL_DIRECT_FILTER:
+            LockFiltersRead(device);
+            if(!(*values == 0 || (filter=LookupFilter(device, *values)) != NULL))
+            {
+                UnlockFiltersRead(device);
+                SET_ERROR_AND_RETURN_VALUE(Context, AL_INVALID_VALUE, AL_FALSE);
+            }
+
+            if(!filter)
+            {
+                Source->Direct.Gain = 1.0f;
+                Source->Direct.GainHF = 1.0f;
+                Source->Direct.HFReference = LOWPASSFREQREF;
+                Source->Direct.GainLF = 1.0f;
+                Source->Direct.LFReference = HIGHPASSFREQREF;
+            }
+            else
+            {
+                Source->Direct.Gain = filter->Gain;
+                Source->Direct.GainHF = filter->GainHF;
+                Source->Direct.HFReference = filter->HFReference;
+                Source->Direct.GainLF = filter->GainLF;
+                Source->Direct.LFReference = filter->LFReference;
+            }
+            UnlockFiltersRead(device);
+            DO_UPDATEPROPS();
+            return AL_TRUE;
+
+        case AL_DIRECT_FILTER_GAINHF_AUTO:
+            CHECKVAL(*values == AL_FALSE || *values == AL_TRUE);
+
+            Source->DryGainHFAuto = *values;
+            DO_UPDATEPROPS();
+            return AL_TRUE;
+
+        case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
+            CHECKVAL(*values == AL_FALSE || *values == AL_TRUE);
+
+            Source->WetGainAuto = *values;
+            DO_UPDATEPROPS();
+            return AL_TRUE;
+
+        case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
+            CHECKVAL(*values == AL_FALSE || *values == AL_TRUE);
+
+            Source->WetGainHFAuto = *values;
+            DO_UPDATEPROPS();
+            return AL_TRUE;
+
+        case AL_DIRECT_CHANNELS_SOFT:
+            CHECKVAL(*values == AL_FALSE || *values == AL_TRUE);
+
+            Source->DirectChannels = *values;
+            DO_UPDATEPROPS();
+            return AL_TRUE;
+
+        case AL_DISTANCE_MODEL:
+            CHECKVAL(*values == AL_NONE ||
+                     *values == AL_INVERSE_DISTANCE ||
+                     *values == AL_INVERSE_DISTANCE_CLAMPED ||
+                     *values == AL_LINEAR_DISTANCE ||
+                     *values == AL_LINEAR_DISTANCE_CLAMPED ||
+                     *values == AL_EXPONENT_DISTANCE ||
+                     *values == AL_EXPONENT_DISTANCE_CLAMPED);
+
+            Source->DistanceModel = *values;
+            if(Context->SourceDistanceModel)
+                DO_UPDATEPROPS();
+            return AL_TRUE;
+
+
+        case AL_AUXILIARY_SEND_FILTER:
+            LockEffectSlotsRead(Context);
+            LockFiltersRead(device);
+            if(!((ALuint)values[1] < device->NumAuxSends &&
+                 (values[0] == 0 || (slot=LookupEffectSlot(Context, values[0])) != NULL) &&
+                 (values[2] == 0 || (filter=LookupFilter(device, values[2])) != NULL)))
+            {
+                UnlockFiltersRead(device);
+                UnlockEffectSlotsRead(Context);
+                SET_ERROR_AND_RETURN_VALUE(Context, AL_INVALID_VALUE, AL_FALSE);
+            }
+
+            if(!filter)
+            {
+                /* Disable filter */
+                Source->Send[values[1]].Gain = 1.0f;
+                Source->Send[values[1]].GainHF = 1.0f;
+                Source->Send[values[1]].HFReference = LOWPASSFREQREF;
+                Source->Send[values[1]].GainLF = 1.0f;
+                Source->Send[values[1]].LFReference = HIGHPASSFREQREF;
+            }
+            else
+            {
+                Source->Send[values[1]].Gain = filter->Gain;
+                Source->Send[values[1]].GainHF = filter->GainHF;
+                Source->Send[values[1]].HFReference = filter->HFReference;
+                Source->Send[values[1]].GainLF = filter->GainLF;
+                Source->Send[values[1]].LFReference = filter->LFReference;
+            }
+            UnlockFiltersRead(device);
+
+            if(slot != Source->Send[values[1]].Slot &&
+               (Source->state == AL_PLAYING || Source->state == AL_PAUSED))
+            {
+                /* Add refcount on the new slot, and release the previous slot */
+                if(slot) IncrementRef(&slot->ref);
+                if(Source->Send[values[1]].Slot)
+                    DecrementRef(&Source->Send[values[1]].Slot->ref);
+                Source->Send[values[1]].Slot = slot;
+
+                /* We must force an update if the auxiliary slot changed on a
+                 * playing source, in case the slot is about to be deleted.
+                 */
+                UpdateSourceProps(Source, device->NumAuxSends);
+            }
+            else
+            {
+                if(slot) IncrementRef(&slot->ref);
+                if(Source->Send[values[1]].Slot)
+                    DecrementRef(&Source->Send[values[1]].Slot->ref);
+                Source->Send[values[1]].Slot = slot;
+                DO_UPDATEPROPS();
+            }
+            UnlockEffectSlotsRead(Context);
+
+            return AL_TRUE;
+
+
+        /* 1x float */
+        case AL_CONE_INNER_ANGLE:
+        case AL_CONE_OUTER_ANGLE:
+        case AL_PITCH:
+        case AL_GAIN:
+        case AL_MIN_GAIN:
+        case AL_MAX_GAIN:
+        case AL_REFERENCE_DISTANCE:
+        case AL_ROLLOFF_FACTOR:
+        case AL_CONE_OUTER_GAIN:
+        case AL_MAX_DISTANCE:
+        case AL_DOPPLER_FACTOR:
+        case AL_CONE_OUTER_GAINHF:
+        case AL_AIR_ABSORPTION_FACTOR:
+        case AL_ROOM_ROLLOFF_FACTOR:
+        case AL_SOURCE_RADIUS:
+            fvals[0] = (ALfloat)*values;
+            return SetSourcefv(Source, Context, (int)prop, fvals);
+
+        /* 3x float */
+        case AL_POSITION:
+        case AL_VELOCITY:
+        case AL_DIRECTION:
+            fvals[0] = (ALfloat)values[0];
+            fvals[1] = (ALfloat)values[1];
+            fvals[2] = (ALfloat)values[2];
+            return SetSourcefv(Source, Context, (int)prop, fvals);
+
+        /* 6x float */
+        case AL_ORIENTATION:
+            fvals[0] = (ALfloat)values[0];
+            fvals[1] = (ALfloat)values[1];
+            fvals[2] = (ALfloat)values[2];
+            fvals[3] = (ALfloat)values[3];
+            fvals[4] = (ALfloat)values[4];
+            fvals[5] = (ALfloat)values[5];
+            return SetSourcefv(Source, Context, (int)prop, fvals);
+
+        case AL_SAMPLE_OFFSET_LATENCY_SOFT:
+        case AL_SEC_OFFSET_LATENCY_SOFT:
+        case AL_STEREO_ANGLES:
+            break;
+    }
+
+    ERR("Unexpected property: 0x%04x\n", prop);
+    SET_ERROR_AND_RETURN_VALUE(Context, AL_INVALID_ENUM, AL_FALSE);
+}
+
+static ALboolean SetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp prop, const ALint64SOFT *values)
+{
+    ALfloat fvals[6];
+    ALint   ivals[3];
+
+    switch(prop)
+    {
+        case AL_SOURCE_TYPE:
+        case AL_BUFFERS_QUEUED:
+        case AL_BUFFERS_PROCESSED:
+        case AL_SOURCE_STATE:
+        case AL_SAMPLE_OFFSET_LATENCY_SOFT:
+        case AL_BYTE_LENGTH_SOFT:
+        case AL_SAMPLE_LENGTH_SOFT:
+        case AL_SEC_LENGTH_SOFT:
+            /* Query only */
+            SET_ERROR_AND_RETURN_VALUE(Context, AL_INVALID_OPERATION, AL_FALSE);
+
+
+        /* 1x int */
+        case AL_SOURCE_RELATIVE:
+        case AL_LOOPING:
+        case AL_SEC_OFFSET:
+        case AL_SAMPLE_OFFSET:
+        case AL_BYTE_OFFSET:
+        case AL_DIRECT_FILTER_GAINHF_AUTO:
+        case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
+        case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
+        case AL_DIRECT_CHANNELS_SOFT:
+        case AL_DISTANCE_MODEL:
+            CHECKVAL(*values <= INT_MAX && *values >= INT_MIN);
+
+            ivals[0] = (ALint)*values;
+            return SetSourceiv(Source, Context, (int)prop, ivals);
+
+        /* 1x uint */
+        case AL_BUFFER:
+        case AL_DIRECT_FILTER:
+            CHECKVAL(*values <= UINT_MAX && *values >= 0);
+
+            ivals[0] = (ALuint)*values;
+            return SetSourceiv(Source, Context, (int)prop, ivals);
+
+        /* 3x uint */
+        case AL_AUXILIARY_SEND_FILTER:
+            CHECKVAL(values[0] <= UINT_MAX && values[0] >= 0 &&
+                     values[1] <= UINT_MAX && values[1] >= 0 &&
+                     values[2] <= UINT_MAX && values[2] >= 0);
+
+            ivals[0] = (ALuint)values[0];
+            ivals[1] = (ALuint)values[1];
+            ivals[2] = (ALuint)values[2];
+            return SetSourceiv(Source, Context, (int)prop, ivals);
+
+        /* 1x float */
+        case AL_CONE_INNER_ANGLE:
+        case AL_CONE_OUTER_ANGLE:
+        case AL_PITCH:
+        case AL_GAIN:
+        case AL_MIN_GAIN:
+        case AL_MAX_GAIN:
+        case AL_REFERENCE_DISTANCE:
+        case AL_ROLLOFF_FACTOR:
+        case AL_CONE_OUTER_GAIN:
+        case AL_MAX_DISTANCE:
+        case AL_DOPPLER_FACTOR:
+        case AL_CONE_OUTER_GAINHF:
+        case AL_AIR_ABSORPTION_FACTOR:
+        case AL_ROOM_ROLLOFF_FACTOR:
+        case AL_SOURCE_RADIUS:
+            fvals[0] = (ALfloat)*values;
+            return SetSourcefv(Source, Context, (int)prop, fvals);
+
+        /* 3x float */
+        case AL_POSITION:
+        case AL_VELOCITY:
+        case AL_DIRECTION:
+            fvals[0] = (ALfloat)values[0];
+            fvals[1] = (ALfloat)values[1];
+            fvals[2] = (ALfloat)values[2];
+            return SetSourcefv(Source, Context, (int)prop, fvals);
+
+        /* 6x float */
+        case AL_ORIENTATION:
+            fvals[0] = (ALfloat)values[0];
+            fvals[1] = (ALfloat)values[1];
+            fvals[2] = (ALfloat)values[2];
+            fvals[3] = (ALfloat)values[3];
+            fvals[4] = (ALfloat)values[4];
+            fvals[5] = (ALfloat)values[5];
+            return SetSourcefv(Source, Context, (int)prop, fvals);
+
+        case AL_SEC_OFFSET_LATENCY_SOFT:
+        case AL_STEREO_ANGLES:
+            break;
+    }
+
+    ERR("Unexpected property: 0x%04x\n", prop);
+    SET_ERROR_AND_RETURN_VALUE(Context, AL_INVALID_ENUM, AL_FALSE);
+}
+
+#undef CHECKVAL
+
+
+static ALboolean GetSourcedv(ALsource *Source, ALCcontext *Context, SourceProp prop, ALdouble *values)
+{
+    ALCdevice *device = Context->Device;
+    ALbufferlistitem *BufferList;
+    ClockLatency clocktime;
+    ALuint64 srcclock;
+    ALint ivals[3];
+    ALboolean err;
+
+    switch(prop)
+    {
+        case AL_GAIN:
+            *values = Source->Gain;
+            return AL_TRUE;
+
+        case AL_PITCH:
+            *values = Source->Pitch;
+            return AL_TRUE;
+
+        case AL_MAX_DISTANCE:
+            *values = Source->MaxDistance;
+            return AL_TRUE;
+
+        case AL_ROLLOFF_FACTOR:
+            *values = Source->RollOffFactor;
+            return AL_TRUE;
+
+        case AL_REFERENCE_DISTANCE:
+            *values = Source->RefDistance;
+            return AL_TRUE;
+
+        case AL_CONE_INNER_ANGLE:
+            *values = Source->InnerAngle;
+            return AL_TRUE;
+
+        case AL_CONE_OUTER_ANGLE:
+            *values = Source->OuterAngle;
+            return AL_TRUE;
+
+        case AL_MIN_GAIN:
+            *values = Source->MinGain;
+            return AL_TRUE;
+
+        case AL_MAX_GAIN:
+            *values = Source->MaxGain;
+            return AL_TRUE;
+
+        case AL_CONE_OUTER_GAIN:
+            *values = Source->OuterGain;
+            return AL_TRUE;
+
+        case AL_SEC_OFFSET:
+        case AL_SAMPLE_OFFSET:
+        case AL_BYTE_OFFSET:
+            *values = GetSourceOffset(Source, prop, device);
+            return AL_TRUE;
+
+        case AL_CONE_OUTER_GAINHF:
+            *values = Source->OuterGainHF;
+            return AL_TRUE;
+
+        case AL_AIR_ABSORPTION_FACTOR:
+            *values = Source->AirAbsorptionFactor;
+            return AL_TRUE;
+
+        case AL_ROOM_ROLLOFF_FACTOR:
+            *values = Source->RoomRolloffFactor;
+            return AL_TRUE;
+
+        case AL_DOPPLER_FACTOR:
+            *values = Source->DopplerFactor;
+            return AL_TRUE;
+
+        case AL_SEC_LENGTH_SOFT:
+            ReadLock(&Source->queue_lock);
+            if(!(BufferList=ATOMIC_LOAD(&Source->queue)))
+                *values = 0;
+            else
+            {
+                ALint length = 0;
+                ALsizei freq = 1;
+                do {
+                    ALbuffer *buffer = BufferList->buffer;
+                    if(buffer && buffer->SampleLen > 0)
+                    {
+                        freq = buffer->Frequency;
+                        length += buffer->SampleLen;
+                    }
+                } while((BufferList=BufferList->next) != NULL);
+                *values = (ALdouble)length / (ALdouble)freq;
+            }
+            ReadUnlock(&Source->queue_lock);
+            return AL_TRUE;
+
+        case AL_SOURCE_RADIUS:
+            *values = Source->Radius;
+            return AL_TRUE;
+
+        case AL_STEREO_ANGLES:
+            values[0] = Source->StereoPan[0];
+            values[1] = Source->StereoPan[1];
+            return AL_TRUE;
+
+        case AL_SEC_OFFSET_LATENCY_SOFT:
+            /* Get the source offset with the clock time first. Then get the
+             * clock time with the device latency. Order is important.
+             */
+            values[0] = GetSourceSecOffset(Source, device, &srcclock);
+            clocktime = V0(device->Backend,getClockLatency)();
+            if(srcclock == (ALuint64)clocktime.ClockTime)
+                values[1] = (ALdouble)clocktime.Latency / 1000000000.0;
+            else
+            {
+                /* If the clock time incremented, reduce the latency by that
+                 * much since it's that much closer to the source offset it got
+                 * earlier.
+                 */
+                ALuint64 diff = clocktime.ClockTime - srcclock;
+                values[1] = (ALdouble)(clocktime.Latency - minu64(clocktime.Latency, diff)) /
+                            1000000000.0;
+            }
+            return AL_TRUE;
+
+        case AL_POSITION:
+            values[0] = Source->Position[0];
+            values[1] = Source->Position[1];
+            values[2] = Source->Position[2];
+            return AL_TRUE;
+
+        case AL_VELOCITY:
+            values[0] = Source->Velocity[0];
+            values[1] = Source->Velocity[1];
+            values[2] = Source->Velocity[2];
+            return AL_TRUE;
+
+        case AL_DIRECTION:
+            values[0] = Source->Direction[0];
+            values[1] = Source->Direction[1];
+            values[2] = Source->Direction[2];
+            return AL_TRUE;
+
+        case AL_ORIENTATION:
+            values[0] = Source->Orientation[0][0];
+            values[1] = Source->Orientation[0][1];
+            values[2] = Source->Orientation[0][2];
+            values[3] = Source->Orientation[1][0];
+            values[4] = Source->Orientation[1][1];
+            values[5] = Source->Orientation[1][2];
+            return AL_TRUE;
+
+        /* 1x int */
+        case AL_SOURCE_RELATIVE:
+        case AL_LOOPING:
+        case AL_SOURCE_STATE:
+        case AL_BUFFERS_QUEUED:
+        case AL_BUFFERS_PROCESSED:
+        case AL_SOURCE_TYPE:
+        case AL_DIRECT_FILTER_GAINHF_AUTO:
+        case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
+        case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
+        case AL_DIRECT_CHANNELS_SOFT:
+        case AL_BYTE_LENGTH_SOFT:
+        case AL_SAMPLE_LENGTH_SOFT:
+        case AL_DISTANCE_MODEL:
+            if((err=GetSourceiv(Source, Context, (int)prop, ivals)) != AL_FALSE)
+                *values = (ALdouble)ivals[0];
+            return err;
+
+        case AL_BUFFER:
+        case AL_DIRECT_FILTER:
+        case AL_AUXILIARY_SEND_FILTER:
+        case AL_SAMPLE_OFFSET_LATENCY_SOFT:
+            break;
+    }
+
+    ERR("Unexpected property: 0x%04x\n", prop);
+    SET_ERROR_AND_RETURN_VALUE(Context, AL_INVALID_ENUM, AL_FALSE);
+}
+
+static ALboolean GetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp prop, ALint *values)
+{
+    ALbufferlistitem *BufferList;
+    ALdouble dvals[6];
+    ALboolean err;
+
+    switch(prop)
+    {
+        case AL_SOURCE_RELATIVE:
+            *values = Source->HeadRelative;
+            return AL_TRUE;
+
+        case AL_LOOPING:
+            *values = ATOMIC_LOAD(&Source->looping);
+            return AL_TRUE;
+
+        case AL_BUFFER:
+            ReadLock(&Source->queue_lock);
+            BufferList = (Source->SourceType == AL_STATIC) ? ATOMIC_LOAD(&Source->queue) :
+                                                             ATOMIC_LOAD(&Source->current_buffer);
+            *values = (BufferList && BufferList->buffer) ? BufferList->buffer->id : 0;
+            ReadUnlock(&Source->queue_lock);
+            return AL_TRUE;
+
+        case AL_SOURCE_STATE:
+            *values = Source->state;
+            return AL_TRUE;
+
+        case AL_BYTE_LENGTH_SOFT:
+            ReadLock(&Source->queue_lock);
+            if(!(BufferList=ATOMIC_LOAD(&Source->queue)))
+                *values = 0;
+            else
+            {
+                ALint length = 0;
+                do {
+                    ALbuffer *buffer = BufferList->buffer;
+                    if(buffer && buffer->SampleLen > 0)
+                    {
+                        ALuint byte_align, sample_align;
+                        if(buffer->OriginalType == UserFmtIMA4)
+                        {
+                            ALsizei align = (buffer->OriginalAlign-1)/2 + 4;
+                            byte_align = align * ChannelsFromFmt(buffer->FmtChannels);
+                            sample_align = buffer->OriginalAlign;
+                        }
+                        else if(buffer->OriginalType == UserFmtMSADPCM)
+                        {
+                            ALsizei align = (buffer->OriginalAlign-2)/2 + 7;
+                            byte_align = align * ChannelsFromFmt(buffer->FmtChannels);
+                            sample_align = buffer->OriginalAlign;
+                        }
+                        else
+                        {
+                            ALsizei align = buffer->OriginalAlign;
+                            byte_align = align * ChannelsFromFmt(buffer->FmtChannels);
+                            sample_align = buffer->OriginalAlign;
+                        }
+
+                        length += buffer->SampleLen / sample_align * byte_align;
+                    }
+                } while((BufferList=BufferList->next) != NULL);
+                *values = length;
+            }
+            ReadUnlock(&Source->queue_lock);
+            return AL_TRUE;
+
+        case AL_SAMPLE_LENGTH_SOFT:
+            ReadLock(&Source->queue_lock);
+            if(!(BufferList=ATOMIC_LOAD(&Source->queue)))
+                *values = 0;
+            else
+            {
+                ALint length = 0;
+                do {
+                    ALbuffer *buffer = BufferList->buffer;
+                    if(buffer) length += buffer->SampleLen;
+                } while((BufferList=BufferList->next) != NULL);
+                *values = length;
+            }
+            ReadUnlock(&Source->queue_lock);
+            return AL_TRUE;
+
+        case AL_BUFFERS_QUEUED:
+            ReadLock(&Source->queue_lock);
+            if(!(BufferList=ATOMIC_LOAD(&Source->queue)))
+                *values = 0;
+            else
+            {
+                ALsizei count = 0;
+                do {
+                    ++count;
+                } while((BufferList=BufferList->next) != NULL);
+                *values = count;
+            }
+            ReadUnlock(&Source->queue_lock);
+            return AL_TRUE;
+
+        case AL_BUFFERS_PROCESSED:
+            ReadLock(&Source->queue_lock);
+            if(ATOMIC_LOAD(&Source->looping) || Source->SourceType != AL_STREAMING)
+            {
+                /* Buffers on a looping source are in a perpetual state of
+                 * PENDING, so don't report any as PROCESSED */
+                *values = 0;
+            }
+            else
+            {
+                const ALbufferlistitem *BufferList = ATOMIC_LOAD(&Source->queue);
+                const ALbufferlistitem *Current = ATOMIC_LOAD(&Source->current_buffer);
+                ALsizei played = 0;
+                while(BufferList && BufferList != Current)
+                {
+                    played++;
+                    BufferList = BufferList->next;
+                }
+                *values = played;
+            }
+            ReadUnlock(&Source->queue_lock);
+            return AL_TRUE;
+
+        case AL_SOURCE_TYPE:
+            *values = Source->SourceType;
+            return AL_TRUE;
+
+        case AL_DIRECT_FILTER_GAINHF_AUTO:
+            *values = Source->DryGainHFAuto;
+            return AL_TRUE;
+
+        case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
+            *values = Source->WetGainAuto;
+            return AL_TRUE;
+
+        case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
+            *values = Source->WetGainHFAuto;
+            return AL_TRUE;
+
+        case AL_DIRECT_CHANNELS_SOFT:
+            *values = Source->DirectChannels;
+            return AL_TRUE;
+
+        case AL_DISTANCE_MODEL:
+            *values = Source->DistanceModel;
+            return AL_TRUE;
+
+        /* 1x float/double */
+        case AL_CONE_INNER_ANGLE:
+        case AL_CONE_OUTER_ANGLE:
+        case AL_PITCH:
+        case AL_GAIN:
+        case AL_MIN_GAIN:
+        case AL_MAX_GAIN:
+        case AL_REFERENCE_DISTANCE:
+        case AL_ROLLOFF_FACTOR:
+        case AL_CONE_OUTER_GAIN:
+        case AL_MAX_DISTANCE:
+        case AL_SEC_OFFSET:
+        case AL_SAMPLE_OFFSET:
+        case AL_BYTE_OFFSET:
+        case AL_DOPPLER_FACTOR:
+        case AL_AIR_ABSORPTION_FACTOR:
+        case AL_ROOM_ROLLOFF_FACTOR:
+        case AL_CONE_OUTER_GAINHF:
+        case AL_SEC_LENGTH_SOFT:
+        case AL_SOURCE_RADIUS:
+            if((err=GetSourcedv(Source, Context, prop, dvals)) != AL_FALSE)
+                *values = (ALint)dvals[0];
+            return err;
+
+        /* 3x float/double */
+        case AL_POSITION:
+        case AL_VELOCITY:
+        case AL_DIRECTION:
+            if((err=GetSourcedv(Source, Context, prop, dvals)) != AL_FALSE)
+            {
+                values[0] = (ALint)dvals[0];
+                values[1] = (ALint)dvals[1];
+                values[2] = (ALint)dvals[2];
+            }
+            return err;
+
+        /* 6x float/double */
+        case AL_ORIENTATION:
+            if((err=GetSourcedv(Source, Context, prop, dvals)) != AL_FALSE)
+            {
+                values[0] = (ALint)dvals[0];
+                values[1] = (ALint)dvals[1];
+                values[2] = (ALint)dvals[2];
+                values[3] = (ALint)dvals[3];
+                values[4] = (ALint)dvals[4];
+                values[5] = (ALint)dvals[5];
+            }
+            return err;
+
+        case AL_SAMPLE_OFFSET_LATENCY_SOFT:
+            break; /* i64 only */
+        case AL_SEC_OFFSET_LATENCY_SOFT:
+            break; /* Double only */
+        case AL_STEREO_ANGLES:
+            break; /* Float/double only */
+
+        case AL_DIRECT_FILTER:
+        case AL_AUXILIARY_SEND_FILTER:
+            break; /* ??? */
+    }
+
+    ERR("Unexpected property: 0x%04x\n", prop);
+    SET_ERROR_AND_RETURN_VALUE(Context, AL_INVALID_ENUM, AL_FALSE);
+}
+
+static ALboolean GetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp prop, ALint64 *values)
+{
+    ALCdevice *device = Context->Device;
+    ClockLatency clocktime;
+    ALuint64 srcclock;
+    ALdouble dvals[6];
+    ALint ivals[3];
+    ALboolean err;
+
+    switch(prop)
+    {
+        case AL_SAMPLE_OFFSET_LATENCY_SOFT:
+            /* Get the source offset with the clock time first. Then get the
+             * clock time with the device latency. Order is important.
+             */
+            values[0] = GetSourceSampleOffset(Source, device, &srcclock);
+            clocktime = V0(device->Backend,getClockLatency)();
+            if(srcclock == (ALuint64)clocktime.ClockTime)
+                values[1] = clocktime.Latency;
+            else
+            {
+                /* If the clock time incremented, reduce the latency by that
+                 * much since it's that much closer to the source offset it got
+                 * earlier.
+                 */
+                ALuint64 diff = clocktime.ClockTime - srcclock;
+                values[1] = clocktime.Latency - minu64(clocktime.Latency, diff);
+            }
+            return AL_TRUE;
+
+        /* 1x float/double */
+        case AL_CONE_INNER_ANGLE:
+        case AL_CONE_OUTER_ANGLE:
+        case AL_PITCH:
+        case AL_GAIN:
+        case AL_MIN_GAIN:
+        case AL_MAX_GAIN:
+        case AL_REFERENCE_DISTANCE:
+        case AL_ROLLOFF_FACTOR:
+        case AL_CONE_OUTER_GAIN:
+        case AL_MAX_DISTANCE:
+        case AL_SEC_OFFSET:
+        case AL_SAMPLE_OFFSET:
+        case AL_BYTE_OFFSET:
+        case AL_DOPPLER_FACTOR:
+        case AL_AIR_ABSORPTION_FACTOR:
+        case AL_ROOM_ROLLOFF_FACTOR:
+        case AL_CONE_OUTER_GAINHF:
+        case AL_SEC_LENGTH_SOFT:
+        case AL_SOURCE_RADIUS:
+            if((err=GetSourcedv(Source, Context, prop, dvals)) != AL_FALSE)
+                *values = (ALint64)dvals[0];
+            return err;
+
+        /* 3x float/double */
+        case AL_POSITION:
+        case AL_VELOCITY:
+        case AL_DIRECTION:
+            if((err=GetSourcedv(Source, Context, prop, dvals)) != AL_FALSE)
+            {
+                values[0] = (ALint64)dvals[0];
+                values[1] = (ALint64)dvals[1];
+                values[2] = (ALint64)dvals[2];
+            }
+            return err;
+
+        /* 6x float/double */
+        case AL_ORIENTATION:
+            if((err=GetSourcedv(Source, Context, prop, dvals)) != AL_FALSE)
+            {
+                values[0] = (ALint64)dvals[0];
+                values[1] = (ALint64)dvals[1];
+                values[2] = (ALint64)dvals[2];
+                values[3] = (ALint64)dvals[3];
+                values[4] = (ALint64)dvals[4];
+                values[5] = (ALint64)dvals[5];
+            }
+            return err;
+
+        /* 1x int */
+        case AL_SOURCE_RELATIVE:
+        case AL_LOOPING:
+        case AL_SOURCE_STATE:
+        case AL_BUFFERS_QUEUED:
+        case AL_BUFFERS_PROCESSED:
+        case AL_BYTE_LENGTH_SOFT:
+        case AL_SAMPLE_LENGTH_SOFT:
+        case AL_SOURCE_TYPE:
+        case AL_DIRECT_FILTER_GAINHF_AUTO:
+        case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
+        case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
+        case AL_DIRECT_CHANNELS_SOFT:
+        case AL_DISTANCE_MODEL:
+            if((err=GetSourceiv(Source, Context, prop, ivals)) != AL_FALSE)
+                *values = ivals[0];
+            return err;
+
+        /* 1x uint */
+        case AL_BUFFER:
+        case AL_DIRECT_FILTER:
+            if((err=GetSourceiv(Source, Context, prop, ivals)) != AL_FALSE)
+                *values = (ALuint)ivals[0];
+            return err;
+
+        /* 3x uint */
+        case AL_AUXILIARY_SEND_FILTER:
+            if((err=GetSourceiv(Source, Context, prop, ivals)) != AL_FALSE)
+            {
+                values[0] = (ALuint)ivals[0];
+                values[1] = (ALuint)ivals[1];
+                values[2] = (ALuint)ivals[2];
+            }
+            return err;
+
+        case AL_SEC_OFFSET_LATENCY_SOFT:
+            break; /* Double only */
+        case AL_STEREO_ANGLES:
+            break; /* Float/double only */
+    }
+
+    ERR("Unexpected property: 0x%04x\n", prop);
+    SET_ERROR_AND_RETURN_VALUE(Context, AL_INVALID_ENUM, AL_FALSE);
+}
+
+
+AL_API ALvoid AL_APIENTRY alGenSources(ALsizei n, ALuint *sources)
+{
+    ALCcontext *context;
+    ALsizei cur = 0;
+    ALenum err;
+
+    context = GetContextRef();
+    if(!context) return;
+
+    if(!(n >= 0))
+        SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+    for(cur = 0;cur < n;cur++)
+    {
+        ALsource *source = al_calloc(16, sizeof(ALsource));
+        if(!source)
+        {
+            alDeleteSources(cur, sources);
+            SET_ERROR_AND_GOTO(context, AL_OUT_OF_MEMORY, done);
+        }
+        InitSourceParams(source);
+
+        err = NewThunkEntry(&source->id);
+        if(err == AL_NO_ERROR)
+            err = InsertUIntMapEntry(&context->SourceMap, source->id, source);
+        if(err != AL_NO_ERROR)
+        {
+            FreeThunkEntry(source->id);
+            memset(source, 0, sizeof(ALsource));
+            al_free(source);
+
+            alDeleteSources(cur, sources);
+            SET_ERROR_AND_GOTO(context, err, done);
+        }
+
+        sources[cur] = source->id;
+    }
+
+done:
+    ALCcontext_DecRef(context);
+}
+
+
+AL_API ALvoid AL_APIENTRY alDeleteSources(ALsizei n, const ALuint *sources)
+{
+    ALCcontext *context;
+    ALsource *Source;
+    ALsizei i;
+
+    context = GetContextRef();
+    if(!context) return;
+
+    LockSourcesWrite(context);
+    if(!(n >= 0))
+        SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+
+    /* Check that all Sources are valid */
+    for(i = 0;i < n;i++)
+    {
+        if(LookupSource(context, sources[i]) == NULL)
+            SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
+    }
+    for(i = 0;i < n;i++)
+    {
+        ALvoice *voice, *voice_end;
+
+        if((Source=RemoveSource(context, sources[i])) == NULL)
+            continue;
+        FreeThunkEntry(Source->id);
+
+        LockContext(context);
+        voice = context->Voices;
+        voice_end = voice + context->VoiceCount;
+        while(voice != voice_end)
+        {
+            ALsource *old = Source;
+            if(COMPARE_EXCHANGE(&voice->Source, &old, NULL))
+                break;
+            voice++;
+        }
+        UnlockContext(context);
+
+        DeinitSource(Source);
+
+        memset(Source, 0, sizeof(*Source));
+        al_free(Source);
+    }
+
+done:
+    UnlockSourcesWrite(context);
+    ALCcontext_DecRef(context);
+}
+
+
+AL_API ALboolean AL_APIENTRY alIsSource(ALuint source)
+{
+    ALCcontext *context;
+    ALboolean ret;
+
+    context = GetContextRef();
+    if(!context) return AL_FALSE;
+
+    LockSourcesRead(context);
+    ret = (LookupSource(context, source) ? AL_TRUE : AL_FALSE);
+    UnlockSourcesRead(context);
+
+    ALCcontext_DecRef(context);
+
+    return ret;
+}
+
+
+AL_API ALvoid AL_APIENTRY alSourcef(ALuint source, ALenum param, ALfloat value)
+{
+    ALCcontext *Context;
+    ALsource   *Source;
+
+    Context = GetContextRef();
+    if(!Context) return;
+
+    WriteLock(&Context->PropLock);
+    LockSourcesRead(Context);
+    if((Source=LookupSource(Context, source)) == NULL)
+        alSetError(Context, AL_INVALID_NAME);
+    else if(!(FloatValsByProp(param) == 1))
+        alSetError(Context, AL_INVALID_ENUM);
+    else
+        SetSourcefv(Source, Context, param, &value);
+    UnlockSourcesRead(Context);
+    WriteUnlock(&Context->PropLock);
+
+    ALCcontext_DecRef(Context);
+}
+
+AL_API ALvoid AL_APIENTRY alSource3f(ALuint source, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3)
+{
+    ALCcontext *Context;
+    ALsource   *Source;
+
+    Context = GetContextRef();
+    if(!Context) return;
+
+    WriteLock(&Context->PropLock);
+    LockSourcesRead(Context);
+    if((Source=LookupSource(Context, source)) == NULL)
+        alSetError(Context, AL_INVALID_NAME);
+    else if(!(FloatValsByProp(param) == 3))
+        alSetError(Context, AL_INVALID_ENUM);
+    else
+    {
+        ALfloat fvals[3] = { value1, value2, value3 };
+        SetSourcefv(Source, Context, param, fvals);
+    }
+    UnlockSourcesRead(Context);
+    WriteUnlock(&Context->PropLock);
+
+    ALCcontext_DecRef(Context);
+}
+
+AL_API ALvoid AL_APIENTRY alSourcefv(ALuint source, ALenum param, const ALfloat *values)
+{
+    ALCcontext *Context;
+    ALsource   *Source;
+
+    Context = GetContextRef();
+    if(!Context) return;
+
+    WriteLock(&Context->PropLock);
+    LockSourcesRead(Context);
+    if((Source=LookupSource(Context, source)) == NULL)
+        alSetError(Context, AL_INVALID_NAME);
+    else if(!values)
+        alSetError(Context, AL_INVALID_VALUE);
+    else if(!(FloatValsByProp(param) > 0))
+        alSetError(Context, AL_INVALID_ENUM);
+    else
+        SetSourcefv(Source, Context, param, values);
+    UnlockSourcesRead(Context);
+    WriteUnlock(&Context->PropLock);
+
+    ALCcontext_DecRef(Context);
+}
+
+
+AL_API ALvoid AL_APIENTRY alSourcedSOFT(ALuint source, ALenum param, ALdouble value)
+{
+    ALCcontext *Context;
+    ALsource   *Source;
+
+    Context = GetContextRef();
+    if(!Context) return;
+
+    WriteLock(&Context->PropLock);
+    LockSourcesRead(Context);
+    if((Source=LookupSource(Context, source)) == NULL)
+        alSetError(Context, AL_INVALID_NAME);
+    else if(!(DoubleValsByProp(param) == 1))
+        alSetError(Context, AL_INVALID_ENUM);
+    else
+    {
+        ALfloat fval = (ALfloat)value;
+        SetSourcefv(Source, Context, param, &fval);
+    }
+    UnlockSourcesRead(Context);
+    WriteUnlock(&Context->PropLock);
+
+    ALCcontext_DecRef(Context);
+}
+
+AL_API ALvoid AL_APIENTRY alSource3dSOFT(ALuint source, ALenum param, ALdouble value1, ALdouble value2, ALdouble value3)
+{
+    ALCcontext *Context;
+    ALsource   *Source;
+
+    Context = GetContextRef();
+    if(!Context) return;
+
+    WriteLock(&Context->PropLock);
+    LockSourcesRead(Context);
+    if((Source=LookupSource(Context, source)) == NULL)
+        alSetError(Context, AL_INVALID_NAME);
+    else if(!(DoubleValsByProp(param) == 3))
+        alSetError(Context, AL_INVALID_ENUM);
+    else
+    {
+        ALfloat fvals[3] = { (ALfloat)value1, (ALfloat)value2, (ALfloat)value3 };
+        SetSourcefv(Source, Context, param, fvals);
+    }
+    UnlockSourcesRead(Context);
+    WriteUnlock(&Context->PropLock);
+
+    ALCcontext_DecRef(Context);
+}
+
+AL_API ALvoid AL_APIENTRY alSourcedvSOFT(ALuint source, ALenum param, const ALdouble *values)
+{
+    ALCcontext *Context;
+    ALsource   *Source;
+    ALint      count;
+
+    Context = GetContextRef();
+    if(!Context) return;
+
+    WriteLock(&Context->PropLock);
+    LockSourcesRead(Context);
+    if((Source=LookupSource(Context, source)) == NULL)
+        alSetError(Context, AL_INVALID_NAME);
+    else if(!values)
+        alSetError(Context, AL_INVALID_VALUE);
+    else if(!((count=DoubleValsByProp(param)) > 0 && count <= 6))
+        alSetError(Context, AL_INVALID_ENUM);
+    else
+    {
+        ALfloat fvals[6];
+        ALint i;
+
+        for(i = 0;i < count;i++)
+            fvals[i] = (ALfloat)values[i];
+        SetSourcefv(Source, Context, param, fvals);
+    }
+    UnlockSourcesRead(Context);
+    WriteUnlock(&Context->PropLock);
+
+    ALCcontext_DecRef(Context);
+}
+
+
+AL_API ALvoid AL_APIENTRY alSourcei(ALuint source, ALenum param, ALint value)
+{
+    ALCcontext *Context;
+    ALsource   *Source;
+
+    Context = GetContextRef();
+    if(!Context) return;
+
+    WriteLock(&Context->PropLock);
+    LockSourcesRead(Context);
+    if((Source=LookupSource(Context, source)) == NULL)
+        alSetError(Context, AL_INVALID_NAME);
+    else if(!(IntValsByProp(param) == 1))
+        alSetError(Context, AL_INVALID_ENUM);
+    else
+        SetSourceiv(Source, Context, param, &value);
+    UnlockSourcesRead(Context);
+    WriteUnlock(&Context->PropLock);
+
+    ALCcontext_DecRef(Context);
+}
+
+AL_API void AL_APIENTRY alSource3i(ALuint source, ALenum param, ALint value1, ALint value2, ALint value3)
+{
+    ALCcontext *Context;
+    ALsource   *Source;
+
+    Context = GetContextRef();
+    if(!Context) return;
+
+    WriteLock(&Context->PropLock);
+    LockSourcesRead(Context);
+    if((Source=LookupSource(Context, source)) == NULL)
+        alSetError(Context, AL_INVALID_NAME);
+    else if(!(IntValsByProp(param) == 3))
+        alSetError(Context, AL_INVALID_ENUM);
+    else
+    {
+        ALint ivals[3] = { value1, value2, value3 };
+        SetSourceiv(Source, Context, param, ivals);
+    }
+    UnlockSourcesRead(Context);
+    WriteUnlock(&Context->PropLock);
+
+    ALCcontext_DecRef(Context);
+}
+
+AL_API void AL_APIENTRY alSourceiv(ALuint source, ALenum param, const ALint *values)
+{
+    ALCcontext *Context;
+    ALsource   *Source;
+
+    Context = GetContextRef();
+    if(!Context) return;
+
+    WriteLock(&Context->PropLock);
+    LockSourcesRead(Context);
+    if((Source=LookupSource(Context, source)) == NULL)
+        alSetError(Context, AL_INVALID_NAME);
+    else if(!values)
+        alSetError(Context, AL_INVALID_VALUE);
+    else if(!(IntValsByProp(param) > 0))
+        alSetError(Context, AL_INVALID_ENUM);
+    else
+        SetSourceiv(Source, Context, param, values);
+    UnlockSourcesRead(Context);
+    WriteUnlock(&Context->PropLock);
+
+    ALCcontext_DecRef(Context);
+}
+
+
+AL_API ALvoid AL_APIENTRY alSourcei64SOFT(ALuint source, ALenum param, ALint64SOFT value)
+{
+    ALCcontext *Context;
+    ALsource   *Source;
+
+    Context = GetContextRef();
+    if(!Context) return;
+
+    WriteLock(&Context->PropLock);
+    LockSourcesRead(Context);
+    if((Source=LookupSource(Context, source)) == NULL)
+        alSetError(Context, AL_INVALID_NAME);
+    else if(!(Int64ValsByProp(param) == 1))
+        alSetError(Context, AL_INVALID_ENUM);
+    else
+        SetSourcei64v(Source, Context, param, &value);
+    UnlockSourcesRead(Context);
+    WriteUnlock(&Context->PropLock);
+
+    ALCcontext_DecRef(Context);
+}
+
+AL_API void AL_APIENTRY alSource3i64SOFT(ALuint source, ALenum param, ALint64SOFT value1, ALint64SOFT value2, ALint64SOFT value3)
+{
+    ALCcontext *Context;
+    ALsource   *Source;
+
+    Context = GetContextRef();
+    if(!Context) return;
+
+    WriteLock(&Context->PropLock);
+    LockSourcesRead(Context);
+    if((Source=LookupSource(Context, source)) == NULL)
+        alSetError(Context, AL_INVALID_NAME);
+    else if(!(Int64ValsByProp(param) == 3))
+        alSetError(Context, AL_INVALID_ENUM);
+    else
+    {
+        ALint64SOFT i64vals[3] = { value1, value2, value3 };
+        SetSourcei64v(Source, Context, param, i64vals);
+    }
+    UnlockSourcesRead(Context);
+    WriteUnlock(&Context->PropLock);
+
+    ALCcontext_DecRef(Context);
+}
+
+AL_API void AL_APIENTRY alSourcei64vSOFT(ALuint source, ALenum param, const ALint64SOFT *values)
+{
+    ALCcontext *Context;
+    ALsource   *Source;
+
+    Context = GetContextRef();
+    if(!Context) return;
+
+    WriteLock(&Context->PropLock);
+    LockSourcesRead(Context);
+    if((Source=LookupSource(Context, source)) == NULL)
+        alSetError(Context, AL_INVALID_NAME);
+    else if(!values)
+        alSetError(Context, AL_INVALID_VALUE);
+    else if(!(Int64ValsByProp(param) > 0))
+        alSetError(Context, AL_INVALID_ENUM);
+    else
+        SetSourcei64v(Source, Context, param, values);
+    UnlockSourcesRead(Context);
+    WriteUnlock(&Context->PropLock);
+
+    ALCcontext_DecRef(Context);
+}
+
+
+AL_API ALvoid AL_APIENTRY alGetSourcef(ALuint source, ALenum param, ALfloat *value)
+{
+    ALCcontext *Context;
+    ALsource   *Source;
+
+    Context = GetContextRef();
+    if(!Context) return;
+
+    ReadLock(&Context->PropLock);
+    LockSourcesRead(Context);
+    if((Source=LookupSource(Context, source)) == NULL)
+        alSetError(Context, AL_INVALID_NAME);
+    else if(!value)
+        alSetError(Context, AL_INVALID_VALUE);
+    else if(!(FloatValsByProp(param) == 1))
+        alSetError(Context, AL_INVALID_ENUM);
+    else
+    {
+        ALdouble dval;
+        if(GetSourcedv(Source, Context, param, &dval))
+            *value = (ALfloat)dval;
+    }
+    UnlockSourcesRead(Context);
+    ReadUnlock(&Context->PropLock);
+
+    ALCcontext_DecRef(Context);
+}
+
+
+AL_API ALvoid AL_APIENTRY alGetSource3f(ALuint source, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3)
+{
+    ALCcontext *Context;
+    ALsource   *Source;
+
+    Context = GetContextRef();
+    if(!Context) return;
+
+    ReadLock(&Context->PropLock);
+    LockSourcesRead(Context);
+    if((Source=LookupSource(Context, source)) == NULL)
+        alSetError(Context, AL_INVALID_NAME);
+    else if(!(value1 && value2 && value3))
+        alSetError(Context, AL_INVALID_VALUE);
+    else if(!(FloatValsByProp(param) == 3))
+        alSetError(Context, AL_INVALID_ENUM);
+    else
+    {
+        ALdouble dvals[3];
+        if(GetSourcedv(Source, Context, param, dvals))
+        {
+            *value1 = (ALfloat)dvals[0];
+            *value2 = (ALfloat)dvals[1];
+            *value3 = (ALfloat)dvals[2];
+        }
+    }
+    UnlockSourcesRead(Context);
+    ReadUnlock(&Context->PropLock);
+
+    ALCcontext_DecRef(Context);
+}
+
+
+AL_API ALvoid AL_APIENTRY alGetSourcefv(ALuint source, ALenum param, ALfloat *values)
+{
+    ALCcontext *Context;
+    ALsource   *Source;
+    ALint      count;
+
+    Context = GetContextRef();
+    if(!Context) return;
+
+    ReadLock(&Context->PropLock);
+    LockSourcesRead(Context);
+    if((Source=LookupSource(Context, source)) == NULL)
+        alSetError(Context, AL_INVALID_NAME);
+    else if(!values)
+        alSetError(Context, AL_INVALID_VALUE);
+    else if(!((count=FloatValsByProp(param)) > 0 && count <= 6))
+        alSetError(Context, AL_INVALID_ENUM);
+    else
+    {
+        ALdouble dvals[6];
+        if(GetSourcedv(Source, Context, param, dvals))
+        {
+            ALint i;
+            for(i = 0;i < count;i++)
+                values[i] = (ALfloat)dvals[i];
+        }
+    }
+    UnlockSourcesRead(Context);
+    ReadUnlock(&Context->PropLock);
+
+    ALCcontext_DecRef(Context);
+}
+
+
+AL_API void AL_APIENTRY alGetSourcedSOFT(ALuint source, ALenum param, ALdouble *value)
+{
+    ALCcontext *Context;
+    ALsource   *Source;
+
+    Context = GetContextRef();
+    if(!Context) return;
+
+    ReadLock(&Context->PropLock);
+    LockSourcesRead(Context);
+    if((Source=LookupSource(Context, source)) == NULL)
+        alSetError(Context, AL_INVALID_NAME);
+    else if(!value)
+        alSetError(Context, AL_INVALID_VALUE);
+    else if(!(DoubleValsByProp(param) == 1))
+        alSetError(Context, AL_INVALID_ENUM);
+    else
+        GetSourcedv(Source, Context, param, value);
+    UnlockSourcesRead(Context);
+    ReadUnlock(&Context->PropLock);
+
+    ALCcontext_DecRef(Context);
+}
+
+AL_API void AL_APIENTRY alGetSource3dSOFT(ALuint source, ALenum param, ALdouble *value1, ALdouble *value2, ALdouble *value3)
+{
+    ALCcontext *Context;
+    ALsource   *Source;
+
+    Context = GetContextRef();
+    if(!Context) return;
+
+    ReadLock(&Context->PropLock);
+    LockSourcesRead(Context);
+    if((Source=LookupSource(Context, source)) == NULL)
+        alSetError(Context, AL_INVALID_NAME);
+    else if(!(value1 && value2 && value3))
+        alSetError(Context, AL_INVALID_VALUE);
+    else if(!(DoubleValsByProp(param) == 3))
+        alSetError(Context, AL_INVALID_ENUM);
+    else
+    {
+        ALdouble dvals[3];
+        if(GetSourcedv(Source, Context, param, dvals))
+        {
+            *value1 = dvals[0];
+            *value2 = dvals[1];
+            *value3 = dvals[2];
+        }
+    }
+    UnlockSourcesRead(Context);
+    ReadUnlock(&Context->PropLock);
+
+    ALCcontext_DecRef(Context);
+}
+
+AL_API void AL_APIENTRY alGetSourcedvSOFT(ALuint source, ALenum param, ALdouble *values)
+{
+    ALCcontext *Context;
+    ALsource   *Source;
+
+    Context = GetContextRef();
+    if(!Context) return;
+
+    ReadLock(&Context->PropLock);
+    LockSourcesRead(Context);
+    if((Source=LookupSource(Context, source)) == NULL)
+        alSetError(Context, AL_INVALID_NAME);
+    else if(!values)
+        alSetError(Context, AL_INVALID_VALUE);
+    else if(!(DoubleValsByProp(param) > 0))
+        alSetError(Context, AL_INVALID_ENUM);
+    else
+        GetSourcedv(Source, Context, param, values);
+    UnlockSourcesRead(Context);
+    ReadUnlock(&Context->PropLock);
+
+    ALCcontext_DecRef(Context);
+}
+
+
+AL_API ALvoid AL_APIENTRY alGetSourcei(ALuint source, ALenum param, ALint *value)
+{
+    ALCcontext *Context;
+    ALsource   *Source;
+
+    Context = GetContextRef();
+    if(!Context) return;
+
+    ReadLock(&Context->PropLock);
+    LockSourcesRead(Context);
+    if((Source=LookupSource(Context, source)) == NULL)
+        alSetError(Context, AL_INVALID_NAME);
+    else if(!value)
+        alSetError(Context, AL_INVALID_VALUE);
+    else if(!(IntValsByProp(param) == 1))
+        alSetError(Context, AL_INVALID_ENUM);
+    else
+        GetSourceiv(Source, Context, param, value);
+    UnlockSourcesRead(Context);
+    ReadUnlock(&Context->PropLock);
+
+    ALCcontext_DecRef(Context);
+}
+
+
+AL_API void AL_APIENTRY alGetSource3i(ALuint source, ALenum param, ALint *value1, ALint *value2, ALint *value3)
+{
+    ALCcontext *Context;
+    ALsource   *Source;
+
+    Context = GetContextRef();
+    if(!Context) return;
+
+    ReadLock(&Context->PropLock);
+    LockSourcesRead(Context);
+    if((Source=LookupSource(Context, source)) == NULL)
+        alSetError(Context, AL_INVALID_NAME);
+    else if(!(value1 && value2 && value3))
+        alSetError(Context, AL_INVALID_VALUE);
+    else if(!(IntValsByProp(param) == 3))
+        alSetError(Context, AL_INVALID_ENUM);
+    else
+    {
+        ALint ivals[3];
+        if(GetSourceiv(Source, Context, param, ivals))
+        {
+            *value1 = ivals[0];
+            *value2 = ivals[1];
+            *value3 = ivals[2];
+        }
+    }
+    UnlockSourcesRead(Context);
+    ReadUnlock(&Context->PropLock);
+
+    ALCcontext_DecRef(Context);
+}
+
+
+AL_API void AL_APIENTRY alGetSourceiv(ALuint source, ALenum param, ALint *values)
+{
+    ALCcontext *Context;
+    ALsource   *Source;
+
+    Context = GetContextRef();
+    if(!Context) return;
+
+    ReadLock(&Context->PropLock);
+    LockSourcesRead(Context);
+    if((Source=LookupSource(Context, source)) == NULL)
+        alSetError(Context, AL_INVALID_NAME);
+    else if(!values)
+        alSetError(Context, AL_INVALID_VALUE);
+    else if(!(IntValsByProp(param) > 0))
+        alSetError(Context, AL_INVALID_ENUM);
+    else
+        GetSourceiv(Source, Context, param, values);
+    UnlockSourcesRead(Context);
+    ReadUnlock(&Context->PropLock);
+
+    ALCcontext_DecRef(Context);
+}
+
+
+AL_API void AL_APIENTRY alGetSourcei64SOFT(ALuint source, ALenum param, ALint64SOFT *value)
+{
+    ALCcontext *Context;
+    ALsource   *Source;
+
+    Context = GetContextRef();
+    if(!Context) return;
+
+    ReadLock(&Context->PropLock);
+    LockSourcesRead(Context);
+    if((Source=LookupSource(Context, source)) == NULL)
+        alSetError(Context, AL_INVALID_NAME);
+    else if(!value)
+        alSetError(Context, AL_INVALID_VALUE);
+    else if(!(Int64ValsByProp(param) == 1))
+        alSetError(Context, AL_INVALID_ENUM);
+    else
+        GetSourcei64v(Source, Context, param, value);
+    UnlockSourcesRead(Context);
+    ReadUnlock(&Context->PropLock);
+
+    ALCcontext_DecRef(Context);
+}
+
+AL_API void AL_APIENTRY alGetSource3i64SOFT(ALuint source, ALenum param, ALint64SOFT *value1, ALint64SOFT *value2, ALint64SOFT *value3)
+{
+    ALCcontext *Context;
+    ALsource   *Source;
+
+    Context = GetContextRef();
+    if(!Context) return;
+
+    ReadLock(&Context->PropLock);
+    LockSourcesRead(Context);
+    if((Source=LookupSource(Context, source)) == NULL)
+        alSetError(Context, AL_INVALID_NAME);
+    else if(!(value1 && value2 && value3))
+        alSetError(Context, AL_INVALID_VALUE);
+    else if(!(Int64ValsByProp(param) == 3))
+        alSetError(Context, AL_INVALID_ENUM);
+    else
+    {
+        ALint64 i64vals[3];
+        if(GetSourcei64v(Source, Context, param, i64vals))
+        {
+            *value1 = i64vals[0];
+            *value2 = i64vals[1];
+            *value3 = i64vals[2];
+        }
+    }
+    UnlockSourcesRead(Context);
+    ReadUnlock(&Context->PropLock);
+
+    ALCcontext_DecRef(Context);
+}
+
+AL_API void AL_APIENTRY alGetSourcei64vSOFT(ALuint source, ALenum param, ALint64SOFT *values)
+{
+    ALCcontext *Context;
+    ALsource   *Source;
+
+    Context = GetContextRef();
+    if(!Context) return;
+
+    ReadLock(&Context->PropLock);
+    LockSourcesRead(Context);
+    if((Source=LookupSource(Context, source)) == NULL)
+        alSetError(Context, AL_INVALID_NAME);
+    else if(!values)
+        alSetError(Context, AL_INVALID_VALUE);
+    else if(!(Int64ValsByProp(param) > 0))
+        alSetError(Context, AL_INVALID_ENUM);
+    else
+        GetSourcei64v(Source, Context, param, values);
+    UnlockSourcesRead(Context);
+    ReadUnlock(&Context->PropLock);
+
+    ALCcontext_DecRef(Context);
+}
+
+
+AL_API ALvoid AL_APIENTRY alSourcePlay(ALuint source)
+{
+    alSourcePlayv(1, &source);
+}
+AL_API ALvoid AL_APIENTRY alSourcePlayv(ALsizei n, const ALuint *sources)
+{
+    ALCcontext *context;
+    ALsource *source;
+    ALsizei i;
+
+    context = GetContextRef();
+    if(!context) return;
+
+    LockSourcesRead(context);
+    if(!(n >= 0))
+        SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+    for(i = 0;i < n;i++)
+    {
+        if(!LookupSource(context, sources[i]))
+            SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
+    }
+
+    LockContext(context);
+    while(n > context->MaxVoices-context->VoiceCount)
+    {
+        ALvoice *temp = NULL;
+        ALsizei newcount;
+
+        newcount = context->MaxVoices << 1;
+        if(newcount > 0)
+            temp = al_malloc(16, newcount * sizeof(context->Voices[0]));
+        if(!temp)
+        {
+            UnlockContext(context);
+            SET_ERROR_AND_GOTO(context, AL_OUT_OF_MEMORY, done);
+        }
+        memcpy(temp, context->Voices, context->MaxVoices * sizeof(temp[0]));
+        memset(&temp[context->MaxVoices], 0, (newcount-context->MaxVoices) * sizeof(temp[0]));
+
+        al_free(context->Voices);
+        context->Voices = temp;
+        context->MaxVoices = newcount;
+    }
+
+    if(ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire) == DeferAll)
+    {
+        for(i = 0;i < n;i++)
+        {
+            source = LookupSource(context, sources[i]);
+            source->new_state = AL_PLAYING;
+        }
+    }
+    else
+    {
+        for(i = 0;i < n;i++)
+        {
+            source = LookupSource(context, sources[i]);
+            SetSourceState(source, context, AL_PLAYING);
+        }
+    }
+    UnlockContext(context);
+
+done:
+    UnlockSourcesRead(context);
+    ALCcontext_DecRef(context);
+}
+
+AL_API ALvoid AL_APIENTRY alSourcePause(ALuint source)
+{
+    alSourcePausev(1, &source);
+}
+AL_API ALvoid AL_APIENTRY alSourcePausev(ALsizei n, const ALuint *sources)
+{
+    ALCcontext *context;
+    ALsource *source;
+    ALsizei i;
+
+    context = GetContextRef();
+    if(!context) return;
+
+    LockSourcesRead(context);
+    if(!(n >= 0))
+        SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+    for(i = 0;i < n;i++)
+    {
+        if(!LookupSource(context, sources[i]))
+            SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
+    }
+
+    LockContext(context);
+    if(ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
+    {
+        for(i = 0;i < n;i++)
+        {
+            source = LookupSource(context, sources[i]);
+            source->new_state = AL_PAUSED;
+        }
+    }
+    else
+    {
+        for(i = 0;i < n;i++)
+        {
+            source = LookupSource(context, sources[i]);
+            SetSourceState(source, context, AL_PAUSED);
+        }
+    }
+    UnlockContext(context);
+
+done:
+    UnlockSourcesRead(context);
+    ALCcontext_DecRef(context);
+}
+
+AL_API ALvoid AL_APIENTRY alSourceStop(ALuint source)
+{
+    alSourceStopv(1, &source);
+}
+AL_API ALvoid AL_APIENTRY alSourceStopv(ALsizei n, const ALuint *sources)
+{
+    ALCcontext *context;
+    ALsource *source;
+    ALsizei i;
+
+    context = GetContextRef();
+    if(!context) return;
+
+    LockSourcesRead(context);
+    if(!(n >= 0))
+        SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+    for(i = 0;i < n;i++)
+    {
+        if(!LookupSource(context, sources[i]))
+            SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
+    }
+
+    LockContext(context);
+    for(i = 0;i < n;i++)
+    {
+        source = LookupSource(context, sources[i]);
+        source->new_state = AL_NONE;
+        SetSourceState(source, context, AL_STOPPED);
+    }
+    UnlockContext(context);
+
+done:
+    UnlockSourcesRead(context);
+    ALCcontext_DecRef(context);
+}
+
+AL_API ALvoid AL_APIENTRY alSourceRewind(ALuint source)
+{
+    alSourceRewindv(1, &source);
+}
+AL_API ALvoid AL_APIENTRY alSourceRewindv(ALsizei n, const ALuint *sources)
+{
+    ALCcontext *context;
+    ALsource *source;
+    ALsizei i;
+
+    context = GetContextRef();
+    if(!context) return;
+
+    LockSourcesRead(context);
+    if(!(n >= 0))
+        SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+    for(i = 0;i < n;i++)
+    {
+        if(!LookupSource(context, sources[i]))
+            SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
+    }
+
+    LockContext(context);
+    for(i = 0;i < n;i++)
+    {
+        source = LookupSource(context, sources[i]);
+        source->new_state = AL_NONE;
+        SetSourceState(source, context, AL_INITIAL);
+    }
+    UnlockContext(context);
+
+done:
+    UnlockSourcesRead(context);
+    ALCcontext_DecRef(context);
+}
+
+
+AL_API ALvoid AL_APIENTRY alSourceQueueBuffers(ALuint src, ALsizei nb, const ALuint *buffers)
+{
+    ALCdevice *device;
+    ALCcontext *context;
+    ALsource *source;
+    ALsizei i;
+    ALbufferlistitem *BufferListStart;
+    ALbufferlistitem *BufferList;
+    ALbuffer *BufferFmt = NULL;
+
+    if(nb == 0)
+        return;
+
+    context = GetContextRef();
+    if(!context) return;
+
+    device = context->Device;
+
+    LockSourcesRead(context);
+    if(!(nb >= 0))
+        SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+    if((source=LookupSource(context, src)) == NULL)
+        SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
+
+    WriteLock(&source->queue_lock);
+    if(source->SourceType == AL_STATIC)
+    {
+        WriteUnlock(&source->queue_lock);
+        /* Can't queue on a Static Source */
+        SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done);
+    }
+
+    /* Check for a valid Buffer, for its frequency and format */
+    BufferList = ATOMIC_LOAD(&source->queue);
+    while(BufferList)
+    {
+        if(BufferList->buffer)
+        {
+            BufferFmt = BufferList->buffer;
+            break;
+        }
+        BufferList = BufferList->next;
+    }
+
+    LockBuffersRead(device);
+    BufferListStart = NULL;
+    BufferList = NULL;
+    for(i = 0;i < nb;i++)
+    {
+        ALbuffer *buffer = NULL;
+        if(buffers[i] && (buffer=LookupBuffer(device, buffers[i])) == NULL)
+        {
+            WriteUnlock(&source->queue_lock);
+            SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, buffer_error);
+        }
+
+        if(!BufferListStart)
+        {
+            BufferListStart = malloc(sizeof(ALbufferlistitem));
+            BufferList = BufferListStart;
+        }
+        else
+        {
+            BufferList->next = malloc(sizeof(ALbufferlistitem));
+            BufferList = BufferList->next;
+        }
+        BufferList->buffer = buffer;
+        BufferList->next = NULL;
+        if(!buffer) continue;
+
+        /* Hold a read lock on each buffer being queued while checking all
+         * provided buffers. This is done so other threads don't see an extra
+         * reference on some buffers if this operation ends up failing. */
+        ReadLock(&buffer->lock);
+        IncrementRef(&buffer->ref);
+
+        if(BufferFmt == NULL)
+        {
+            BufferFmt = buffer;
+
+            source->NumChannels = ChannelsFromFmt(buffer->FmtChannels);
+            source->SampleSize  = BytesFromFmt(buffer->FmtType);
+        }
+        else if(BufferFmt->Frequency != buffer->Frequency ||
+                BufferFmt->OriginalChannels != buffer->OriginalChannels ||
+                BufferFmt->OriginalType != buffer->OriginalType)
+        {
+            WriteUnlock(&source->queue_lock);
+            SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, buffer_error);
+
+        buffer_error:
+            /* A buffer failed (invalid ID or format), so unlock and release
+             * each buffer we had. */
+            while(BufferListStart)
+            {
+                ALbufferlistitem *next = BufferListStart->next;
+                if((buffer=BufferListStart->buffer) != NULL)
+                {
+                    DecrementRef(&buffer->ref);
+                    ReadUnlock(&buffer->lock);
+                }
+                free(BufferListStart);
+                BufferListStart = next;
+            }
+            UnlockBuffersRead(device);
+            goto done;
+        }
+    }
+    /* All buffers good, unlock them now. */
+    BufferList = BufferListStart;
+    while(BufferList != NULL)
+    {
+        ALbuffer *buffer = BufferList->buffer;
+        if(buffer) ReadUnlock(&buffer->lock);
+        BufferList = BufferList->next;
+    }
+    UnlockBuffersRead(device);
+
+    /* Source is now streaming */
+    source->SourceType = AL_STREAMING;
+
+    BufferList = NULL;
+    if(!ATOMIC_COMPARE_EXCHANGE_STRONG(ALbufferlistitem*, &source->queue, &BufferList, BufferListStart))
+    {
+        /* Queue head is not NULL, append to the end of the queue */
+        while(BufferList->next != NULL)
+            BufferList = BufferList->next;
+        BufferList->next = BufferListStart;
+    }
+    /* If the current buffer was at the end (NULL), put it at the start of the newly queued
+     * buffers.
+     */
+    BufferList = NULL;
+    ATOMIC_COMPARE_EXCHANGE_STRONG(ALbufferlistitem*, &source->current_buffer, &BufferList, BufferListStart);
+    WriteUnlock(&source->queue_lock);
+
+done:
+    UnlockSourcesRead(context);
+    ALCcontext_DecRef(context);
+}
+
+AL_API ALvoid AL_APIENTRY alSourceUnqueueBuffers(ALuint src, ALsizei nb, ALuint *buffers)
+{
+    ALCcontext *context;
+    ALsource *source;
+    ALbufferlistitem *OldHead;
+    ALbufferlistitem *OldTail;
+    ALbufferlistitem *Current;
+    ALsizei i = 0;
+
+    if(nb == 0)
+        return;
+
+    context = GetContextRef();
+    if(!context) return;
+
+    LockSourcesRead(context);
+    if(!(nb >= 0))
+        SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+
+    if((source=LookupSource(context, src)) == NULL)
+        SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
+
+    WriteLock(&source->queue_lock);
+    if(ATOMIC_LOAD(&source->looping) || source->SourceType != AL_STREAMING)
+    {
+        WriteUnlock(&source->queue_lock);
+        /* Trying to unqueue buffers on a looping or non-streaming source. */
+        SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+    }
+
+    /* Find the new buffer queue head */
+    OldTail = ATOMIC_LOAD(&source->queue);
+    Current = ATOMIC_LOAD(&source->current_buffer);
+    if(OldTail != Current)
+    {
+        for(i = 1;i < nb;i++)
+        {
+            ALbufferlistitem *next = OldTail->next;
+            if(!next || next == Current) break;
+            OldTail = next;
+        }
+    }
+    if(i != nb)
+    {
+        WriteUnlock(&source->queue_lock);
+        /* Trying to unqueue pending buffers. */
+        SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+    }
+
+    /* Swap it, and cut the new head from the old. */
+    OldHead = ATOMIC_EXCHANGE(ALbufferlistitem*, &source->queue, OldTail->next);
+    if(OldTail->next)
+    {
+        ALCdevice *device = context->Device;
+        uint count;
+
+        /* Once the active mix (if any) is done, it's safe to cut the old tail
+         * from the new head.
+         */
+        if(((count=ReadRef(&device->MixCount))&1) != 0)
+        {
+            while(count == ReadRef(&device->MixCount))
+                althrd_yield();
+        }
+        OldTail->next = NULL;
+    }
+    WriteUnlock(&source->queue_lock);
+
+    while(OldHead != NULL)
+    {
+        ALbufferlistitem *next = OldHead->next;
+        ALbuffer *buffer = OldHead->buffer;
+
+        if(!buffer)
+            *(buffers++) = 0;
+        else
+        {
+            *(buffers++) = buffer->id;
+            DecrementRef(&buffer->ref);
+        }
+
+        free(OldHead);
+        OldHead = next;
+    }
+
+done:
+    UnlockSourcesRead(context);
+    ALCcontext_DecRef(context);
+}
+
+
+static void InitSourceParams(ALsource *Source)
+{
+    ALuint i;
+
+    RWLockInit(&Source->queue_lock);
+
+    Source->InnerAngle = 360.0f;
+    Source->OuterAngle = 360.0f;
+    Source->Pitch = 1.0f;
+    Source->Position[0] = 0.0f;
+    Source->Position[1] = 0.0f;
+    Source->Position[2] = 0.0f;
+    Source->Velocity[0] = 0.0f;
+    Source->Velocity[1] = 0.0f;
+    Source->Velocity[2] = 0.0f;
+    Source->Direction[0] = 0.0f;
+    Source->Direction[1] = 0.0f;
+    Source->Direction[2] = 0.0f;
+    Source->Orientation[0][0] =  0.0f;
+    Source->Orientation[0][1] =  0.0f;
+    Source->Orientation[0][2] = -1.0f;
+    Source->Orientation[1][0] =  0.0f;
+    Source->Orientation[1][1] =  1.0f;
+    Source->Orientation[1][2] =  0.0f;
+    Source->RefDistance = 1.0f;
+    Source->MaxDistance = FLT_MAX;
+    Source->RollOffFactor = 1.0f;
+    Source->Gain = 1.0f;
+    Source->MinGain = 0.0f;
+    Source->MaxGain = 1.0f;
+    Source->OuterGain = 0.0f;
+    Source->OuterGainHF = 1.0f;
+
+    Source->DryGainHFAuto = AL_TRUE;
+    Source->WetGainAuto = AL_TRUE;
+    Source->WetGainHFAuto = AL_TRUE;
+    Source->AirAbsorptionFactor = 0.0f;
+    Source->RoomRolloffFactor = 0.0f;
+    Source->DopplerFactor = 1.0f;
+    Source->DirectChannels = AL_FALSE;
+
+    Source->StereoPan[0] = DEG2RAD( 30.0f);
+    Source->StereoPan[1] = DEG2RAD(-30.0f);
+
+    Source->Radius = 0.0f;
+
+    Source->DistanceModel = DefaultDistanceModel;
+
+    Source->Direct.Gain = 1.0f;
+    Source->Direct.GainHF = 1.0f;
+    Source->Direct.HFReference = LOWPASSFREQREF;
+    Source->Direct.GainLF = 1.0f;
+    Source->Direct.LFReference = HIGHPASSFREQREF;
+    for(i = 0;i < MAX_SENDS;i++)
+    {
+        Source->Send[i].Gain = 1.0f;
+        Source->Send[i].GainHF = 1.0f;
+        Source->Send[i].HFReference = LOWPASSFREQREF;
+        Source->Send[i].GainLF = 1.0f;
+        Source->Send[i].LFReference = HIGHPASSFREQREF;
+    }
+
+    Source->Offset = 0.0;
+    Source->OffsetType = AL_NONE;
+    Source->SourceType = AL_UNDETERMINED;
+    Source->state = AL_INITIAL;
+    Source->new_state = AL_NONE;
+
+    ATOMIC_INIT(&Source->queue, NULL);
+    ATOMIC_INIT(&Source->current_buffer, NULL);
+
+    ATOMIC_INIT(&Source->position, 0);
+    ATOMIC_INIT(&Source->position_fraction, 0);
+
+    ATOMIC_INIT(&Source->looping, AL_FALSE);
+
+    ATOMIC_INIT(&Source->Update, NULL);
+    ATOMIC_INIT(&Source->FreeList, NULL);
+}
+
+static void DeinitSource(ALsource *source)
+{
+    ALbufferlistitem *BufferList;
+    struct ALsourceProps *props;
+    size_t count = 0;
+    size_t i;
+
+    props = ATOMIC_LOAD(&source->Update);
+    if(props) al_free(props);
+
+    props = ATOMIC_LOAD(&source->FreeList, almemory_order_relaxed);
+    while(props)
+    {
+        struct ALsourceProps *next;
+        next = ATOMIC_LOAD(&props->next, almemory_order_relaxed);
+        al_free(props);
+        props = next;
+        ++count;
+    }
+    /* This is excessively spammy if it traces every source destruction, so
+     * just warn if it was unexpectedly large.
+     */
+    if(count > 3)
+        WARN("Freed "SZFMT" Source property objects\n", count);
+
+    BufferList = ATOMIC_EXCHANGE(ALbufferlistitem*, &source->queue, NULL);
+    while(BufferList != NULL)
+    {
+        ALbufferlistitem *next = BufferList->next;
+        if(BufferList->buffer != NULL)
+            DecrementRef(&BufferList->buffer->ref);
+        free(BufferList);
+        BufferList = next;
+    }
+
+    for(i = 0;i < MAX_SENDS;++i)
+    {
+        if(source->Send[i].Slot)
+            DecrementRef(&source->Send[i].Slot->ref);
+        source->Send[i].Slot = NULL;
+    }
+}
+
+static void UpdateSourceProps(ALsource *source, ALuint num_sends)
+{
+    struct ALsourceProps *props;
+    size_t i;
+
+    /* Get an unused property container, or allocate a new one as needed. */
+    props = ATOMIC_LOAD(&source->FreeList, almemory_order_acquire);
+    if(!props)
+        props = al_calloc(16, sizeof(*props));
+    else
+    {
+        struct ALsourceProps *next;
+        do {
+            next = ATOMIC_LOAD(&props->next, almemory_order_relaxed);
+        } while(ATOMIC_COMPARE_EXCHANGE_WEAK(struct ALsourceProps*,
+                &source->FreeList, &props, next, almemory_order_seq_cst,
+                almemory_order_consume) == 0);
+    }
+
+    /* Copy in current property values. */
+    ATOMIC_STORE(&props->Pitch, source->Pitch, almemory_order_relaxed);
+    ATOMIC_STORE(&props->Gain, source->Gain, almemory_order_relaxed);
+    ATOMIC_STORE(&props->OuterGain, source->OuterGain, almemory_order_relaxed);
+    ATOMIC_STORE(&props->MinGain, source->MinGain, almemory_order_relaxed);
+    ATOMIC_STORE(&props->MaxGain, source->MaxGain, almemory_order_relaxed);
+    ATOMIC_STORE(&props->InnerAngle, source->InnerAngle, almemory_order_relaxed);
+    ATOMIC_STORE(&props->OuterAngle, source->OuterAngle, almemory_order_relaxed);
+    ATOMIC_STORE(&props->RefDistance, source->RefDistance, almemory_order_relaxed);
+    ATOMIC_STORE(&props->MaxDistance, source->MaxDistance, almemory_order_relaxed);
+    ATOMIC_STORE(&props->RollOffFactor, source->RollOffFactor, almemory_order_relaxed);
+    for(i = 0;i < 3;i++)
+        ATOMIC_STORE(&props->Position[i], source->Position[i], almemory_order_relaxed);
+    for(i = 0;i < 3;i++)
+        ATOMIC_STORE(&props->Velocity[i], source->Velocity[i], almemory_order_relaxed);
+    for(i = 0;i < 3;i++)
+        ATOMIC_STORE(&props->Direction[i], source->Direction[i], almemory_order_relaxed);
+    for(i = 0;i < 2;i++)
+    {
+        size_t j;
+        for(j = 0;j < 3;j++)
+            ATOMIC_STORE(&props->Orientation[i][j], source->Orientation[i][j],
+                         almemory_order_relaxed);
+    }
+    ATOMIC_STORE(&props->HeadRelative, source->HeadRelative, almemory_order_relaxed);
+    ATOMIC_STORE(&props->DistanceModel, source->DistanceModel, almemory_order_relaxed);
+    ATOMIC_STORE(&props->DirectChannels, source->DirectChannels, almemory_order_relaxed);
+
+    ATOMIC_STORE(&props->DryGainHFAuto, source->DryGainHFAuto, almemory_order_relaxed);
+    ATOMIC_STORE(&props->WetGainAuto, source->WetGainAuto, almemory_order_relaxed);
+    ATOMIC_STORE(&props->WetGainHFAuto, source->WetGainHFAuto, almemory_order_relaxed);
+    ATOMIC_STORE(&props->OuterGainHF, source->OuterGainHF, almemory_order_relaxed);
+
+    ATOMIC_STORE(&props->AirAbsorptionFactor, source->AirAbsorptionFactor, almemory_order_relaxed);
+    ATOMIC_STORE(&props->RoomRolloffFactor, source->RoomRolloffFactor, almemory_order_relaxed);
+    ATOMIC_STORE(&props->DopplerFactor, source->DopplerFactor, almemory_order_relaxed);
+
+    ATOMIC_STORE(&props->StereoPan[0], source->StereoPan[0], almemory_order_relaxed);
+    ATOMIC_STORE(&props->StereoPan[1], source->StereoPan[1], almemory_order_relaxed);
+
+    ATOMIC_STORE(&props->Radius, source->Radius, almemory_order_relaxed);
+
+    ATOMIC_STORE(&props->Direct.Gain, source->Direct.Gain, almemory_order_relaxed);
+    ATOMIC_STORE(&props->Direct.GainHF, source->Direct.GainHF, almemory_order_relaxed);
+    ATOMIC_STORE(&props->Direct.HFReference, source->Direct.HFReference, almemory_order_relaxed);
+    ATOMIC_STORE(&props->Direct.GainLF, source->Direct.GainLF, almemory_order_relaxed);
+    ATOMIC_STORE(&props->Direct.LFReference, source->Direct.LFReference, almemory_order_relaxed);
+
+    for(i = 0;i < num_sends;i++)
+    {
+        ATOMIC_STORE(&props->Send[i].Slot, source->Send[i].Slot, almemory_order_relaxed);
+        ATOMIC_STORE(&props->Send[i].Gain, source->Send[i].Gain, almemory_order_relaxed);
+        ATOMIC_STORE(&props->Send[i].GainHF, source->Send[i].GainHF, almemory_order_relaxed);
+        ATOMIC_STORE(&props->Send[i].HFReference, source->Send[i].HFReference,
+                     almemory_order_relaxed);
+        ATOMIC_STORE(&props->Send[i].GainLF, source->Send[i].GainLF, almemory_order_relaxed);
+        ATOMIC_STORE(&props->Send[i].LFReference, source->Send[i].LFReference,
+                     almemory_order_relaxed);
+    }
+
+    /* Set the new container for updating internal parameters. */
+    props = ATOMIC_EXCHANGE(struct ALsourceProps*, &source->Update, props, almemory_order_acq_rel);
+    if(props)
+    {
+        /* If there was an unused update container, put it back in the
+         * freelist.
+         */
+        struct ALsourceProps *first = ATOMIC_LOAD(&source->FreeList);
+        do {
+            ATOMIC_STORE(&props->next, first, almemory_order_relaxed);
+        } while(ATOMIC_COMPARE_EXCHANGE_WEAK(struct ALsourceProps*,
+                &source->FreeList, &first, props) == 0);
+    }
+}
+
+void UpdateAllSourceProps(ALCcontext *context)
+{
+    ALuint num_sends = context->Device->NumAuxSends;
+    ALsizei pos;
+
+    for(pos = 0;pos < context->VoiceCount;pos++)
+    {
+        ALvoice *voice = &context->Voices[pos];
+        ALsource *source = voice->Source;
+        if(source != NULL && (source->state == AL_PLAYING ||
+                              source->state == AL_PAUSED))
+            UpdateSourceProps(source, num_sends);
+    }
+}
+
+
+/* SetSourceState
+ *
+ * Sets the source's new play state given its current state.
+ */
+ALvoid SetSourceState(ALsource *Source, ALCcontext *Context, ALenum state)
+{
+    WriteLock(&Source->queue_lock);
+    if(state == AL_PLAYING)
+    {
+        ALCdevice *device = Context->Device;
+        ALbufferlistitem *BufferList;
+        ALboolean discontinuity;
+        ALvoice *voice = NULL;
+        ALsizei i;
+
+        /* Check that there is a queue containing at least one valid, non zero
+         * length Buffer. */
+        BufferList = ATOMIC_LOAD(&Source->queue);
+        while(BufferList)
+        {
+            ALbuffer *buffer;
+            if((buffer=BufferList->buffer) != NULL && buffer->SampleLen > 0)
+                break;
+            BufferList = BufferList->next;
+        }
+
+        if(Source->state != AL_PAUSED)
+        {
+            Source->state = AL_PLAYING;
+            ATOMIC_STORE(&Source->current_buffer, BufferList, almemory_order_relaxed);
+            ATOMIC_STORE(&Source->position, 0, almemory_order_relaxed);
+            ATOMIC_STORE(&Source->position_fraction, 0);
+            discontinuity = AL_TRUE;
+        }
+        else
+        {
+            Source->state = AL_PLAYING;
+            discontinuity = AL_FALSE;
+        }
+
+        // Check if an Offset has been set
+        if(Source->OffsetType != AL_NONE)
+        {
+            ApplyOffset(Source);
+            /* discontinuity = AL_TRUE;??? */
+        }
+
+        /* If there's nothing to play, or device is disconnected, go right to
+         * stopped */
+        if(!BufferList || !device->Connected)
+            goto do_stop;
+
+        /* Make sure this source isn't already active, while looking for an
+         * unused active source slot to put it in. */
+        for(i = 0;i < Context->VoiceCount;i++)
+        {
+            ALsource *old = Source;
+            if(COMPARE_EXCHANGE(&Context->Voices[i].Source, &old, NULL))
+            {
+                if(voice == NULL)
+                {
+                    voice = &Context->Voices[i];
+                    voice->Source = Source;
+                }
+                break;
+            }
+            old = NULL;
+            if(voice == NULL && COMPARE_EXCHANGE(&Context->Voices[i].Source, &old, Source))
+                voice = &Context->Voices[i];
+        }
+        if(voice == NULL)
+        {
+            voice = &Context->Voices[Context->VoiceCount++];
+            voice->Source = Source;
+        }
+
+        if(discontinuity)
+        {
+            /* Clear previous samples if playback is discontinuous. */
+            memset(voice->PrevSamples, 0, sizeof(voice->PrevSamples));
+
+            /* Clear the stepping value so the mixer knows not to mix this
+             * until the update gets applied.
+             */
+            voice->Step = 0;
+        }
+
+        voice->Moving = AL_FALSE;
+        for(i = 0;i < MAX_INPUT_CHANNELS;i++)
+        {
+            ALsizei j;
+            for(j = 0;j < HRTF_HISTORY_LENGTH;j++)
+                voice->Chan[i].Direct.Hrtf.State.History[j] = 0.0f;
+            for(j = 0;j < HRIR_LENGTH;j++)
+            {
+                voice->Chan[i].Direct.Hrtf.State.Values[j][0] = 0.0f;
+                voice->Chan[i].Direct.Hrtf.State.Values[j][1] = 0.0f;
+            }
+        }
+
+        UpdateSourceProps(Source, device->NumAuxSends);
+    }
+    else if(state == AL_PAUSED)
+    {
+        if(Source->state == AL_PLAYING)
+            Source->state = AL_PAUSED;
+    }
+    else if(state == AL_STOPPED)
+    {
+    do_stop:
+        if(Source->state != AL_INITIAL)
+        {
+            Source->state = AL_STOPPED;
+            ATOMIC_STORE(&Source->current_buffer, NULL);
+        }
+        Source->OffsetType = AL_NONE;
+        Source->Offset = 0.0;
+    }
+    else if(state == AL_INITIAL)
+    {
+        if(Source->state != AL_INITIAL)
+        {
+            Source->state = AL_INITIAL;
+            ATOMIC_STORE(&Source->current_buffer, ATOMIC_LOAD(&Source->queue),
+                         almemory_order_relaxed);
+            ATOMIC_STORE(&Source->position, 0, almemory_order_relaxed);
+            ATOMIC_STORE(&Source->position_fraction, 0);
+        }
+        Source->OffsetType = AL_NONE;
+        Source->Offset = 0.0;
+    }
+    WriteUnlock(&Source->queue_lock);
+}
+
+/* GetSourceSampleOffset
+ *
+ * Gets the current read offset for the given Source, in 32.32 fixed-point
+ * samples. The offset is relative to the start of the queue (not the start of
+ * the current buffer).
+ */
+static ALint64 GetSourceSampleOffset(ALsource *Source, ALCdevice *device, ALuint64 *clocktime)
+{
+    const ALbufferlistitem *BufferList;
+    const ALbufferlistitem *Current;
+    ALuint64 readPos;
+    ALuint refcount;
+
+    ReadLock(&Source->queue_lock);
+    if(Source->state != AL_PLAYING && Source->state != AL_PAUSED)
+    {
+        ReadUnlock(&Source->queue_lock);
+        do {
+            while(((refcount=ReadRef(&device->MixCount))&1))
+                althrd_yield();
+            *clocktime = GetDeviceClockTime(device);
+        } while(refcount != ReadRef(&device->MixCount));
+        return 0;
+    }
+
+    do {
+        while(((refcount=ReadRef(&device->MixCount))&1))
+            althrd_yield();
+        *clocktime = GetDeviceClockTime(device);
+
+        BufferList = ATOMIC_LOAD(&Source->queue, almemory_order_relaxed);
+        Current = ATOMIC_LOAD(&Source->current_buffer, almemory_order_relaxed);
+
+        readPos  = (ALuint64)ATOMIC_LOAD(&Source->position, almemory_order_relaxed) << 32;
+        readPos |= (ALuint64)ATOMIC_LOAD(&Source->position_fraction, almemory_order_relaxed) <<
+                   (32-FRACTIONBITS);
+    } while(refcount != ReadRef(&device->MixCount));
+    while(BufferList && BufferList != Current)
+    {
+        if(BufferList->buffer)
+            readPos += (ALuint64)BufferList->buffer->SampleLen << 32;
+        BufferList = BufferList->next;
+    }
+
+    ReadUnlock(&Source->queue_lock);
+    return (ALint64)minu64(readPos, U64(0x7fffffffffffffff));
+}
+
+/* GetSourceSecOffset
+ *
+ * Gets the current read offset for the given Source, in seconds. The offset is
+ * relative to the start of the queue (not the start of the current buffer).
+ */
+static ALdouble GetSourceSecOffset(ALsource *Source, ALCdevice *device, ALuint64 *clocktime)
+{
+    const ALbufferlistitem *BufferList;
+    const ALbufferlistitem *Current;
+    const ALbuffer *Buffer = NULL;
+    ALuint64 readPos;
+    ALuint refcount;
+
+    ReadLock(&Source->queue_lock);
+    if(Source->state != AL_PLAYING && Source->state != AL_PAUSED)
+    {
+        ReadUnlock(&Source->queue_lock);
+        do {
+            while(((refcount=ReadRef(&device->MixCount))&1))
+                althrd_yield();
+            *clocktime = GetDeviceClockTime(device);
+        } while(refcount != ReadRef(&device->MixCount));
+        return 0.0;
+    }
+
+    do {
+        while(((refcount=ReadRef(&device->MixCount))&1))
+            althrd_yield();
+        *clocktime = GetDeviceClockTime(device);
+
+        BufferList = ATOMIC_LOAD(&Source->queue, almemory_order_relaxed);
+        Current = ATOMIC_LOAD(&Source->current_buffer, almemory_order_relaxed);
+
+        readPos  = (ALuint64)ATOMIC_LOAD(&Source->position, almemory_order_relaxed)<<FRACTIONBITS;
+        readPos |= (ALuint64)ATOMIC_LOAD(&Source->position_fraction, almemory_order_relaxed);
+    } while(refcount != ReadRef(&device->MixCount));
+    while(BufferList && BufferList != Current)
+    {
+        const ALbuffer *buffer = BufferList->buffer;
+        if(buffer != NULL)
+        {
+            if(!Buffer) Buffer = buffer;
+            readPos += (ALuint64)buffer->SampleLen << FRACTIONBITS;
+        }
+        BufferList = BufferList->next;
+    }
+
+    while(BufferList && !Buffer)
+    {
+        Buffer = BufferList->buffer;
+        BufferList = BufferList->next;
+    }
+    assert(Buffer != NULL);
+
+    ReadUnlock(&Source->queue_lock);
+    return (ALdouble)readPos / (ALdouble)FRACTIONONE / (ALdouble)Buffer->Frequency;
+}
+
+/* GetSourceOffset
+ *
+ * Gets the current read offset for the given Source, in the appropriate format
+ * (Bytes, Samples or Seconds). The offset is relative to the start of the
+ * queue (not the start of the current buffer).
+ */
+static ALdouble GetSourceOffset(ALsource *Source, ALenum name, ALCdevice *device)
+{
+    const ALbufferlistitem *BufferList;
+    const ALbufferlistitem *Current;
+    const ALbuffer *Buffer = NULL;
+    ALboolean readFin = AL_FALSE;
+    ALuint readPos, readPosFrac;
+    ALuint totalBufferLen;
+    ALdouble offset = 0.0;
+    ALboolean looping;
+    ALuint refcount;
+
+    ReadLock(&Source->queue_lock);
+    if(Source->state != AL_PLAYING && Source->state != AL_PAUSED)
+    {
+        ReadUnlock(&Source->queue_lock);
+        return 0.0;
+    }
+
+    totalBufferLen = 0;
+    do {
+        while(((refcount=ReadRef(&device->MixCount))&1))
+            althrd_yield();
+        BufferList = ATOMIC_LOAD(&Source->queue, almemory_order_relaxed);
+        Current = ATOMIC_LOAD(&Source->current_buffer, almemory_order_relaxed);
+
+        readPos = ATOMIC_LOAD(&Source->position, almemory_order_relaxed);
+        readPosFrac = ATOMIC_LOAD(&Source->position_fraction, almemory_order_relaxed);
+
+        looping = ATOMIC_LOAD(&Source->looping, almemory_order_relaxed);
+    } while(refcount != ReadRef(&device->MixCount));
+
+    while(BufferList != NULL)
+    {
+        const ALbuffer *buffer;
+        readFin = readFin || (BufferList == Current);
+        if((buffer=BufferList->buffer) != NULL)
+        {
+            if(!Buffer) Buffer = buffer;
+            totalBufferLen += buffer->SampleLen;
+            if(!readFin) readPos += buffer->SampleLen;
+        }
+        BufferList = BufferList->next;
+    }
+    assert(Buffer != NULL);
+
+    if(looping)
+        readPos %= totalBufferLen;
+    else
+    {
+        /* Wrap back to 0 */
+        if(readPos >= totalBufferLen)
+            readPos = readPosFrac = 0;
+    }
+
+    switch(name)
+    {
+        case AL_SEC_OFFSET:
+            offset = (readPos + (ALdouble)readPosFrac/FRACTIONONE)/Buffer->Frequency;
+            break;
+
+        case AL_SAMPLE_OFFSET:
+            offset = readPos + (ALdouble)readPosFrac/FRACTIONONE;
+            break;
+
+        case AL_BYTE_OFFSET:
+            if(Buffer->OriginalType == UserFmtIMA4)
+            {
+                ALsizei align = (Buffer->OriginalAlign-1)/2 + 4;
+                ALuint BlockSize = align * ChannelsFromFmt(Buffer->FmtChannels);
+                ALuint FrameBlockSize = Buffer->OriginalAlign;
+
+                /* Round down to nearest ADPCM block */
+                offset = (ALdouble)(readPos / FrameBlockSize * BlockSize);
+            }
+            else if(Buffer->OriginalType == UserFmtMSADPCM)
+            {
+                ALsizei align = (Buffer->OriginalAlign-2)/2 + 7;
+                ALuint BlockSize = align * ChannelsFromFmt(Buffer->FmtChannels);
+                ALuint FrameBlockSize = Buffer->OriginalAlign;
+
+                /* Round down to nearest ADPCM block */
+                offset = (ALdouble)(readPos / FrameBlockSize * BlockSize);
+            }
+            else
+            {
+                ALuint FrameSize = FrameSizeFromUserFmt(Buffer->OriginalChannels, Buffer->OriginalType);
+                offset = (ALdouble)(readPos * FrameSize);
+            }
+            break;
+    }
+
+    ReadUnlock(&Source->queue_lock);
+    return offset;
+}
+
+
+/* ApplyOffset
+ *
+ * Apply the stored playback offset to the Source. This function will update
+ * the number of buffers "played" given the stored offset.
+ */
+ALboolean ApplyOffset(ALsource *Source)
+{
+    ALbufferlistitem *BufferList;
+    const ALbuffer *Buffer;
+    ALuint bufferLen, totalBufferLen;
+    ALuint offset=0, frac=0;
+
+    /* Get sample frame offset */
+    if(!GetSampleOffset(Source, &offset, &frac))
+        return AL_FALSE;
+
+    totalBufferLen = 0;
+    BufferList = ATOMIC_LOAD(&Source->queue);
+    while(BufferList && totalBufferLen <= offset)
+    {
+        Buffer = BufferList->buffer;
+        bufferLen = Buffer ? Buffer->SampleLen : 0;
+
+        if(bufferLen > offset-totalBufferLen)
+        {
+            /* Offset is in this buffer */
+            ATOMIC_STORE(&Source->current_buffer, BufferList, almemory_order_relaxed);
+
+            ATOMIC_STORE(&Source->position, offset - totalBufferLen, almemory_order_relaxed);
+            ATOMIC_STORE(&Source->position_fraction, frac);
+            return AL_TRUE;
+        }
+
+        totalBufferLen += bufferLen;
+
+        BufferList = BufferList->next;
+    }
+
+    /* Offset is out of range of the queue */
+    return AL_FALSE;
+}
+
+
+/* GetSampleOffset
+ *
+ * Retrieves the sample offset into the Source's queue (from the Sample, Byte
+ * or Second offset supplied by the application). This takes into account the
+ * fact that the buffer format may have been modifed since.
+ */
+static ALboolean GetSampleOffset(ALsource *Source, ALuint *offset, ALuint *frac)
+{
+    const ALbuffer *Buffer = NULL;
+    const ALbufferlistitem *BufferList;
+    ALdouble dbloff, dblfrac;
+
+    /* Find the first valid Buffer in the Queue */
+    BufferList = ATOMIC_LOAD(&Source->queue);
+    while(BufferList)
+    {
+        if(BufferList->buffer)
+        {
+            Buffer = BufferList->buffer;
+            break;
+        }
+        BufferList = BufferList->next;
+    }
+    if(!Buffer)
+    {
+        Source->OffsetType = AL_NONE;
+        Source->Offset = 0.0;
+        return AL_FALSE;
+    }
+
+    switch(Source->OffsetType)
+    {
+    case AL_BYTE_OFFSET:
+        /* Determine the ByteOffset (and ensure it is block aligned) */
+        *offset = (ALuint)Source->Offset;
+        if(Buffer->OriginalType == UserFmtIMA4)
+        {
+            ALsizei align = (Buffer->OriginalAlign-1)/2 + 4;
+            *offset /= align * ChannelsFromUserFmt(Buffer->OriginalChannels);
+            *offset *= Buffer->OriginalAlign;
+        }
+        else if(Buffer->OriginalType == UserFmtMSADPCM)
+        {
+            ALsizei align = (Buffer->OriginalAlign-2)/2 + 7;
+            *offset /= align * ChannelsFromUserFmt(Buffer->OriginalChannels);
+            *offset *= Buffer->OriginalAlign;
+        }
+        else
+            *offset /= FrameSizeFromUserFmt(Buffer->OriginalChannels, Buffer->OriginalType);
+        *frac = 0;
+        break;
+
+    case AL_SAMPLE_OFFSET:
+        dblfrac = modf(Source->Offset, &dbloff);
+        *offset = (ALuint)mind(dbloff, UINT_MAX);
+        *frac = (ALuint)mind(dblfrac*FRACTIONONE, FRACTIONONE-1.0);
+        break;
+
+    case AL_SEC_OFFSET:
+        dblfrac = modf(Source->Offset*Buffer->Frequency, &dbloff);
+        *offset = (ALuint)mind(dbloff, UINT_MAX);
+        *frac = (ALuint)mind(dblfrac*FRACTIONONE, FRACTIONONE-1.0);
+        break;
+    }
+    Source->OffsetType = AL_NONE;
+    Source->Offset = 0.0;
+
+    return AL_TRUE;
+}
+
+
+/* ReleaseALSources
+ *
+ * Destroys all sources in the source map.
+ */
+ALvoid ReleaseALSources(ALCcontext *Context)
+{
+    ALsizei pos;
+    for(pos = 0;pos < Context->SourceMap.size;pos++)
+    {
+        ALsource *temp = Context->SourceMap.values[pos];
+        Context->SourceMap.values[pos] = NULL;
+
+        DeinitSource(temp);
+
+        FreeThunkEntry(temp->id);
+        memset(temp, 0, sizeof(*temp));
+        al_free(temp);
+    }
+}

+ 687 - 0
Engine/lib/openal-soft/OpenAL32/alState.c

@@ -0,0 +1,687 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 1999-2000 by authors.
+ * This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the
+ *  Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include "alMain.h"
+#include "AL/alc.h"
+#include "AL/al.h"
+#include "AL/alext.h"
+#include "alError.h"
+#include "alListener.h"
+#include "alSource.h"
+#include "alAuxEffectSlot.h"
+
+#include "backends/base.h"
+
+
+static const ALchar alVendor[] = "OpenAL Community";
+static const ALchar alVersion[] = "1.1 ALSOFT "ALSOFT_VERSION;
+static const ALchar alRenderer[] = "OpenAL Soft";
+
+// Error Messages
+static const ALchar alNoError[] = "No Error";
+static const ALchar alErrInvalidName[] = "Invalid Name";
+static const ALchar alErrInvalidEnum[] = "Invalid Enum";
+static const ALchar alErrInvalidValue[] = "Invalid Value";
+static const ALchar alErrInvalidOp[] = "Invalid Operation";
+static const ALchar alErrOutOfMemory[] = "Out of Memory";
+
+AL_API ALvoid AL_APIENTRY alEnable(ALenum capability)
+{
+    ALCcontext *context;
+
+    context = GetContextRef();
+    if(!context) return;
+
+    WriteLock(&context->PropLock);
+    switch(capability)
+    {
+    case AL_SOURCE_DISTANCE_MODEL:
+        context->SourceDistanceModel = AL_TRUE;
+        break;
+
+    default:
+        SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
+    }
+    if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
+        UpdateListenerProps(context);
+
+done:
+    WriteUnlock(&context->PropLock);
+    ALCcontext_DecRef(context);
+}
+
+AL_API ALvoid AL_APIENTRY alDisable(ALenum capability)
+{
+    ALCcontext *context;
+
+    context = GetContextRef();
+    if(!context) return;
+
+    WriteLock(&context->PropLock);
+    switch(capability)
+    {
+    case AL_SOURCE_DISTANCE_MODEL:
+        context->SourceDistanceModel = AL_FALSE;
+        break;
+
+    default:
+        SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
+    }
+    if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
+        UpdateListenerProps(context);
+
+done:
+    WriteUnlock(&context->PropLock);
+    ALCcontext_DecRef(context);
+}
+
+AL_API ALboolean AL_APIENTRY alIsEnabled(ALenum capability)
+{
+    ALCcontext *context;
+    ALboolean value=AL_FALSE;
+
+    context = GetContextRef();
+    if(!context) return AL_FALSE;
+
+    switch(capability)
+    {
+    case AL_SOURCE_DISTANCE_MODEL:
+        value = context->SourceDistanceModel;
+        break;
+
+    default:
+        SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
+    }
+
+done:
+    ALCcontext_DecRef(context);
+
+    return value;
+}
+
+AL_API ALboolean AL_APIENTRY alGetBoolean(ALenum pname)
+{
+    ALCcontext *context;
+    ALboolean value=AL_FALSE;
+
+    context = GetContextRef();
+    if(!context) return AL_FALSE;
+
+    switch(pname)
+    {
+    case AL_DOPPLER_FACTOR:
+        if(context->DopplerFactor != 0.0f)
+            value = AL_TRUE;
+        break;
+
+    case AL_DOPPLER_VELOCITY:
+        if(context->DopplerVelocity != 0.0f)
+            value = AL_TRUE;
+        break;
+
+    case AL_DISTANCE_MODEL:
+        if(context->DistanceModel == AL_INVERSE_DISTANCE_CLAMPED)
+            value = AL_TRUE;
+        break;
+
+    case AL_SPEED_OF_SOUND:
+        if(context->SpeedOfSound != 0.0f)
+            value = AL_TRUE;
+        break;
+
+    case AL_DEFERRED_UPDATES_SOFT:
+        if(ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire) == DeferAll)
+            value = AL_TRUE;
+        break;
+
+    case AL_GAIN_LIMIT_SOFT:
+        if(GAIN_MIX_MAX/context->GainBoost != 0.0f)
+            value = AL_TRUE;
+        break;
+
+    default:
+        SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
+    }
+
+done:
+    ALCcontext_DecRef(context);
+
+    return value;
+}
+
+AL_API ALdouble AL_APIENTRY alGetDouble(ALenum pname)
+{
+    ALCcontext *context;
+    ALdouble value = 0.0;
+
+    context = GetContextRef();
+    if(!context) return 0.0;
+
+    switch(pname)
+    {
+    case AL_DOPPLER_FACTOR:
+        value = (ALdouble)context->DopplerFactor;
+        break;
+
+    case AL_DOPPLER_VELOCITY:
+        value = (ALdouble)context->DopplerVelocity;
+        break;
+
+    case AL_DISTANCE_MODEL:
+        value = (ALdouble)context->DistanceModel;
+        break;
+
+    case AL_SPEED_OF_SOUND:
+        value = (ALdouble)context->SpeedOfSound;
+        break;
+
+    case AL_DEFERRED_UPDATES_SOFT:
+        if(ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire) == DeferAll)
+            value = (ALdouble)AL_TRUE;
+        break;
+
+    case AL_GAIN_LIMIT_SOFT:
+        value = (ALdouble)GAIN_MIX_MAX/context->GainBoost;
+        break;
+
+    default:
+        SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
+    }
+
+done:
+    ALCcontext_DecRef(context);
+
+    return value;
+}
+
+AL_API ALfloat AL_APIENTRY alGetFloat(ALenum pname)
+{
+    ALCcontext *context;
+    ALfloat value = 0.0f;
+
+    context = GetContextRef();
+    if(!context) return 0.0f;
+
+    switch(pname)
+    {
+    case AL_DOPPLER_FACTOR:
+        value = context->DopplerFactor;
+        break;
+
+    case AL_DOPPLER_VELOCITY:
+        value = context->DopplerVelocity;
+        break;
+
+    case AL_DISTANCE_MODEL:
+        value = (ALfloat)context->DistanceModel;
+        break;
+
+    case AL_SPEED_OF_SOUND:
+        value = context->SpeedOfSound;
+        break;
+
+    case AL_DEFERRED_UPDATES_SOFT:
+        if(ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire) == DeferAll)
+            value = (ALfloat)AL_TRUE;
+        break;
+
+    case AL_GAIN_LIMIT_SOFT:
+        value = GAIN_MIX_MAX/context->GainBoost;
+        break;
+
+    default:
+        SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
+    }
+
+done:
+    ALCcontext_DecRef(context);
+
+    return value;
+}
+
+AL_API ALint AL_APIENTRY alGetInteger(ALenum pname)
+{
+    ALCcontext *context;
+    ALint value = 0;
+
+    context = GetContextRef();
+    if(!context) return 0;
+
+    switch(pname)
+    {
+    case AL_DOPPLER_FACTOR:
+        value = (ALint)context->DopplerFactor;
+        break;
+
+    case AL_DOPPLER_VELOCITY:
+        value = (ALint)context->DopplerVelocity;
+        break;
+
+    case AL_DISTANCE_MODEL:
+        value = (ALint)context->DistanceModel;
+        break;
+
+    case AL_SPEED_OF_SOUND:
+        value = (ALint)context->SpeedOfSound;
+        break;
+
+    case AL_DEFERRED_UPDATES_SOFT:
+        if(ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire) == DeferAll)
+            value = (ALint)AL_TRUE;
+        break;
+
+    case AL_GAIN_LIMIT_SOFT:
+        value = (ALint)(GAIN_MIX_MAX/context->GainBoost);
+        break;
+
+    default:
+        SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
+    }
+
+done:
+    ALCcontext_DecRef(context);
+
+    return value;
+}
+
+AL_API ALint64SOFT AL_APIENTRY alGetInteger64SOFT(ALenum pname)
+{
+    ALCcontext *context;
+    ALint64SOFT value = 0;
+
+    context = GetContextRef();
+    if(!context) return 0;
+
+    switch(pname)
+    {
+    case AL_DOPPLER_FACTOR:
+        value = (ALint64SOFT)context->DopplerFactor;
+        break;
+
+    case AL_DOPPLER_VELOCITY:
+        value = (ALint64SOFT)context->DopplerVelocity;
+        break;
+
+    case AL_DISTANCE_MODEL:
+        value = (ALint64SOFT)context->DistanceModel;
+        break;
+
+    case AL_SPEED_OF_SOUND:
+        value = (ALint64SOFT)context->SpeedOfSound;
+        break;
+
+    case AL_DEFERRED_UPDATES_SOFT:
+        if(ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire) == DeferAll)
+            value = (ALint64SOFT)AL_TRUE;
+        break;
+
+    case AL_GAIN_LIMIT_SOFT:
+        value = (ALint64SOFT)(GAIN_MIX_MAX/context->GainBoost);
+        break;
+
+    default:
+        SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
+    }
+
+done:
+    ALCcontext_DecRef(context);
+
+    return value;
+}
+
+AL_API ALvoid AL_APIENTRY alGetBooleanv(ALenum pname, ALboolean *values)
+{
+    ALCcontext *context;
+
+    if(values)
+    {
+        switch(pname)
+        {
+            case AL_DOPPLER_FACTOR:
+            case AL_DOPPLER_VELOCITY:
+            case AL_DISTANCE_MODEL:
+            case AL_SPEED_OF_SOUND:
+            case AL_DEFERRED_UPDATES_SOFT:
+            case AL_GAIN_LIMIT_SOFT:
+                values[0] = alGetBoolean(pname);
+                return;
+        }
+    }
+
+    context = GetContextRef();
+    if(!context) return;
+
+    if(!(values))
+        SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+    switch(pname)
+    {
+    default:
+        SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
+    }
+
+done:
+    ALCcontext_DecRef(context);
+}
+
+AL_API ALvoid AL_APIENTRY alGetDoublev(ALenum pname, ALdouble *values)
+{
+    ALCcontext *context;
+
+    if(values)
+    {
+        switch(pname)
+        {
+            case AL_DOPPLER_FACTOR:
+            case AL_DOPPLER_VELOCITY:
+            case AL_DISTANCE_MODEL:
+            case AL_SPEED_OF_SOUND:
+            case AL_DEFERRED_UPDATES_SOFT:
+            case AL_GAIN_LIMIT_SOFT:
+                values[0] = alGetDouble(pname);
+                return;
+        }
+    }
+
+    context = GetContextRef();
+    if(!context) return;
+
+    if(!(values))
+        SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+    switch(pname)
+    {
+    default:
+        SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
+    }
+
+done:
+    ALCcontext_DecRef(context);
+}
+
+AL_API ALvoid AL_APIENTRY alGetFloatv(ALenum pname, ALfloat *values)
+{
+    ALCcontext *context;
+
+    if(values)
+    {
+        switch(pname)
+        {
+            case AL_DOPPLER_FACTOR:
+            case AL_DOPPLER_VELOCITY:
+            case AL_DISTANCE_MODEL:
+            case AL_SPEED_OF_SOUND:
+            case AL_DEFERRED_UPDATES_SOFT:
+            case AL_GAIN_LIMIT_SOFT:
+                values[0] = alGetFloat(pname);
+                return;
+        }
+    }
+
+    context = GetContextRef();
+    if(!context) return;
+
+    if(!(values))
+        SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+    switch(pname)
+    {
+    default:
+        SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
+    }
+
+done:
+    ALCcontext_DecRef(context);
+}
+
+AL_API ALvoid AL_APIENTRY alGetIntegerv(ALenum pname, ALint *values)
+{
+    ALCcontext *context;
+
+    if(values)
+    {
+        switch(pname)
+        {
+            case AL_DOPPLER_FACTOR:
+            case AL_DOPPLER_VELOCITY:
+            case AL_DISTANCE_MODEL:
+            case AL_SPEED_OF_SOUND:
+            case AL_DEFERRED_UPDATES_SOFT:
+            case AL_GAIN_LIMIT_SOFT:
+                values[0] = alGetInteger(pname);
+                return;
+        }
+    }
+
+    context = GetContextRef();
+    if(!context) return;
+
+    switch(pname)
+    {
+    default:
+        SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
+    }
+
+done:
+    ALCcontext_DecRef(context);
+}
+
+AL_API void AL_APIENTRY alGetInteger64vSOFT(ALenum pname, ALint64SOFT *values)
+{
+    ALCcontext *context;
+
+    if(values)
+    {
+        switch(pname)
+        {
+            case AL_DOPPLER_FACTOR:
+            case AL_DOPPLER_VELOCITY:
+            case AL_DISTANCE_MODEL:
+            case AL_SPEED_OF_SOUND:
+            case AL_DEFERRED_UPDATES_SOFT:
+            case AL_GAIN_LIMIT_SOFT:
+                values[0] = alGetInteger64SOFT(pname);
+                return;
+        }
+    }
+
+    context = GetContextRef();
+    if(!context) return;
+
+    switch(pname)
+    {
+    default:
+        SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
+    }
+
+done:
+    ALCcontext_DecRef(context);
+}
+
+AL_API const ALchar* AL_APIENTRY alGetString(ALenum pname)
+{
+    const ALchar *value = NULL;
+    ALCcontext *context;
+
+    context = GetContextRef();
+    if(!context) return NULL;
+
+    switch(pname)
+    {
+    case AL_VENDOR:
+        value = alVendor;
+        break;
+
+    case AL_VERSION:
+        value = alVersion;
+        break;
+
+    case AL_RENDERER:
+        value = alRenderer;
+        break;
+
+    case AL_EXTENSIONS:
+        value = context->ExtensionList;
+        break;
+
+    case AL_NO_ERROR:
+        value = alNoError;
+        break;
+
+    case AL_INVALID_NAME:
+        value = alErrInvalidName;
+        break;
+
+    case AL_INVALID_ENUM:
+        value = alErrInvalidEnum;
+        break;
+
+    case AL_INVALID_VALUE:
+        value = alErrInvalidValue;
+        break;
+
+    case AL_INVALID_OPERATION:
+        value = alErrInvalidOp;
+        break;
+
+    case AL_OUT_OF_MEMORY:
+        value = alErrOutOfMemory;
+        break;
+
+    default:
+        SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
+    }
+
+done:
+    ALCcontext_DecRef(context);
+
+    return value;
+}
+
+AL_API ALvoid AL_APIENTRY alDopplerFactor(ALfloat value)
+{
+    ALCcontext *context;
+
+    context = GetContextRef();
+    if(!context) return;
+
+    if(!(value >= 0.0f && isfinite(value)))
+        SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+
+    WriteLock(&context->PropLock);
+    context->DopplerFactor = value;
+    if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
+        UpdateListenerProps(context);
+    WriteUnlock(&context->PropLock);
+
+done:
+    ALCcontext_DecRef(context);
+}
+
+AL_API ALvoid AL_APIENTRY alDopplerVelocity(ALfloat value)
+{
+    ALCcontext *context;
+
+    context = GetContextRef();
+    if(!context) return;
+
+    if(!(value >= 0.0f && isfinite(value)))
+        SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+
+    WriteLock(&context->PropLock);
+    context->DopplerVelocity = value;
+    if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
+        UpdateListenerProps(context);
+    WriteUnlock(&context->PropLock);
+
+done:
+    ALCcontext_DecRef(context);
+}
+
+AL_API ALvoid AL_APIENTRY alSpeedOfSound(ALfloat value)
+{
+    ALCcontext *context;
+
+    context = GetContextRef();
+    if(!context) return;
+
+    if(!(value > 0.0f && isfinite(value)))
+        SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+
+    WriteLock(&context->PropLock);
+    context->SpeedOfSound = value;
+    if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
+        UpdateListenerProps(context);
+    WriteUnlock(&context->PropLock);
+
+done:
+    ALCcontext_DecRef(context);
+}
+
+AL_API ALvoid AL_APIENTRY alDistanceModel(ALenum value)
+{
+    ALCcontext *context;
+
+    context = GetContextRef();
+    if(!context) return;
+
+    if(!(value == AL_INVERSE_DISTANCE || value == AL_INVERSE_DISTANCE_CLAMPED ||
+         value == AL_LINEAR_DISTANCE || value == AL_LINEAR_DISTANCE_CLAMPED ||
+         value == AL_EXPONENT_DISTANCE || value == AL_EXPONENT_DISTANCE_CLAMPED ||
+         value == AL_NONE))
+        SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+
+    WriteLock(&context->PropLock);
+    context->DistanceModel = value;
+    if(!context->SourceDistanceModel)
+    {
+        if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
+            UpdateListenerProps(context);
+    }
+    WriteUnlock(&context->PropLock);
+
+done:
+    ALCcontext_DecRef(context);
+}
+
+
+AL_API ALvoid AL_APIENTRY alDeferUpdatesSOFT(void)
+{
+    ALCcontext *context;
+
+    context = GetContextRef();
+    if(!context) return;
+
+    ALCcontext_DeferUpdates(context, DeferAll);
+
+    ALCcontext_DecRef(context);
+}
+
+AL_API ALvoid AL_APIENTRY alProcessUpdatesSOFT(void)
+{
+    ALCcontext *context;
+
+    context = GetContextRef();
+    if(!context) return;
+
+    ALCcontext_ProcessUpdates(context);
+
+    ALCcontext_DecRef(context);
+}

+ 105 - 0
Engine/lib/openal-soft/OpenAL32/alThunk.c

@@ -0,0 +1,105 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 1999-2007 by authors.
+ * This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the
+ *  Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+
+#include "alMain.h"
+#include "alThunk.h"
+
+#include "almalloc.h"
+
+
+static ATOMIC(ALenum) *ThunkArray;
+static ALuint          ThunkArraySize;
+static RWLock ThunkLock;
+
+void ThunkInit(void)
+{
+    RWLockInit(&ThunkLock);
+    ThunkArraySize = 1024;
+    ThunkArray = al_calloc(16, ThunkArraySize * sizeof(*ThunkArray));
+}
+
+void ThunkExit(void)
+{
+    al_free(ThunkArray);
+    ThunkArray = NULL;
+    ThunkArraySize = 0;
+}
+
+ALenum NewThunkEntry(ALuint *index)
+{
+    void *NewList;
+    ALuint i;
+
+    ReadLock(&ThunkLock);
+    for(i = 0;i < ThunkArraySize;i++)
+    {
+        if(ATOMIC_EXCHANGE(ALenum, &ThunkArray[i], AL_TRUE) == AL_FALSE)
+        {
+            ReadUnlock(&ThunkLock);
+            *index = i+1;
+            return AL_NO_ERROR;
+        }
+    }
+    ReadUnlock(&ThunkLock);
+
+    WriteLock(&ThunkLock);
+    /* Double-check that there's still no free entries, in case another
+     * invocation just came through and increased the size of the array.
+     */
+    for(;i < ThunkArraySize;i++)
+    {
+        if(ATOMIC_EXCHANGE(ALenum, &ThunkArray[i], AL_TRUE) == AL_FALSE)
+        {
+            WriteUnlock(&ThunkLock);
+            *index = i+1;
+            return AL_NO_ERROR;
+        }
+    }
+
+    NewList = al_calloc(16, ThunkArraySize*2 * sizeof(*ThunkArray));
+    if(!NewList)
+    {
+        WriteUnlock(&ThunkLock);
+        ERR("Realloc failed to increase to %u entries!\n", ThunkArraySize*2);
+        return AL_OUT_OF_MEMORY;
+    }
+    memcpy(NewList, ThunkArray, ThunkArraySize*sizeof(*ThunkArray));
+    al_free(ThunkArray);
+    ThunkArray = NewList;
+    ThunkArraySize *= 2;
+
+    ATOMIC_STORE(&ThunkArray[i], AL_TRUE);
+    WriteUnlock(&ThunkLock);
+
+    *index = i+1;
+    return AL_NO_ERROR;
+}
+
+void FreeThunkEntry(ALuint index)
+{
+    ReadLock(&ThunkLock);
+    if(index > 0 && index <= ThunkArraySize)
+        ATOMIC_STORE(&ThunkArray[index-1], AL_FALSE);
+    ReadUnlock(&ThunkLock);
+}

+ 1112 - 0
Engine/lib/openal-soft/OpenAL32/sample_cvt.c

@@ -0,0 +1,1112 @@
+
+#include "config.h"
+
+#include "sample_cvt.h"
+
+#ifdef HAVE_ALLOCA_H
+#include <alloca.h>
+#endif
+#ifdef HAVE_MALLOC_H
+#include <malloc.h>
+#endif
+
+#include "AL/al.h"
+#include "alu.h"
+#include "alBuffer.h"
+
+
+/* IMA ADPCM Stepsize table */
+static const int IMAStep_size[89] = {
+       7,    8,    9,   10,   11,   12,   13,   14,   16,   17,   19,
+      21,   23,   25,   28,   31,   34,   37,   41,   45,   50,   55,
+      60,   66,   73,   80,   88,   97,  107,  118,  130,  143,  157,
+     173,  190,  209,  230,  253,  279,  307,  337,  371,  408,  449,
+     494,  544,  598,  658,  724,  796,  876,  963, 1060, 1166, 1282,
+    1411, 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, 3327, 3660,
+    4026, 4428, 4871, 5358, 5894, 6484, 7132, 7845, 8630, 9493,10442,
+   11487,12635,13899,15289,16818,18500,20350,22358,24633,27086,29794,
+   32767
+};
+
+/* IMA4 ADPCM Codeword decode table */
+static const int IMA4Codeword[16] = {
+    1, 3, 5, 7, 9, 11, 13, 15,
+   -1,-3,-5,-7,-9,-11,-13,-15,
+};
+
+/* IMA4 ADPCM Step index adjust decode table */
+static const int IMA4Index_adjust[16] = {
+   -1,-1,-1,-1, 2, 4, 6, 8,
+   -1,-1,-1,-1, 2, 4, 6, 8
+};
+
+
+/* MSADPCM Adaption table */
+static const int MSADPCMAdaption[16] = {
+    230, 230, 230, 230, 307, 409, 512, 614,
+    768, 614, 512, 409, 307, 230, 230, 230
+};
+
+/* MSADPCM Adaption Coefficient tables */
+static const int MSADPCMAdaptionCoeff[7][2] = {
+    { 256,    0 },
+    { 512, -256 },
+    {   0,    0 },
+    { 192,   64 },
+    { 240,    0 },
+    { 460, -208 },
+    { 392, -232 }
+};
+
+
+/* A quick'n'dirty lookup table to decode a muLaw-encoded byte sample into a
+ * signed 16-bit sample */
+static const ALshort muLawDecompressionTable[256] = {
+    -32124,-31100,-30076,-29052,-28028,-27004,-25980,-24956,
+    -23932,-22908,-21884,-20860,-19836,-18812,-17788,-16764,
+    -15996,-15484,-14972,-14460,-13948,-13436,-12924,-12412,
+    -11900,-11388,-10876,-10364, -9852, -9340, -8828, -8316,
+     -7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140,
+     -5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092,
+     -3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004,
+     -2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980,
+     -1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436,
+     -1372, -1308, -1244, -1180, -1116, -1052,  -988,  -924,
+      -876,  -844,  -812,  -780,  -748,  -716,  -684,  -652,
+      -620,  -588,  -556,  -524,  -492,  -460,  -428,  -396,
+      -372,  -356,  -340,  -324,  -308,  -292,  -276,  -260,
+      -244,  -228,  -212,  -196,  -180,  -164,  -148,  -132,
+      -120,  -112,  -104,   -96,   -88,   -80,   -72,   -64,
+       -56,   -48,   -40,   -32,   -24,   -16,    -8,     0,
+     32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956,
+     23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764,
+     15996, 15484, 14972, 14460, 13948, 13436, 12924, 12412,
+     11900, 11388, 10876, 10364,  9852,  9340,  8828,  8316,
+      7932,  7676,  7420,  7164,  6908,  6652,  6396,  6140,
+      5884,  5628,  5372,  5116,  4860,  4604,  4348,  4092,
+      3900,  3772,  3644,  3516,  3388,  3260,  3132,  3004,
+      2876,  2748,  2620,  2492,  2364,  2236,  2108,  1980,
+      1884,  1820,  1756,  1692,  1628,  1564,  1500,  1436,
+      1372,  1308,  1244,  1180,  1116,  1052,   988,   924,
+       876,   844,   812,   780,   748,   716,   684,   652,
+       620,   588,   556,   524,   492,   460,   428,   396,
+       372,   356,   340,   324,   308,   292,   276,   260,
+       244,   228,   212,   196,   180,   164,   148,   132,
+       120,   112,   104,    96,    88,    80,    72,    64,
+        56,    48,    40,    32,    24,    16,     8,     0
+};
+
+/* Values used when encoding a muLaw sample */
+static const int muLawBias = 0x84;
+static const int muLawClip = 32635;
+static const char muLawCompressTable[256] = {
+     0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,
+     4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+     5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+     5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+     6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+     6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+     6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+     6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+     7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+     7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+     7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+     7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+     7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+     7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+     7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+     7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7
+};
+
+
+/* A quick'n'dirty lookup table to decode an aLaw-encoded byte sample into a
+ * signed 16-bit sample */
+static const ALshort aLawDecompressionTable[256] = {
+     -5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736,
+     -7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784,
+     -2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368,
+     -3776, -3648, -4032, -3904, -3264, -3136, -3520, -3392,
+    -22016,-20992,-24064,-23040,-17920,-16896,-19968,-18944,
+    -30208,-29184,-32256,-31232,-26112,-25088,-28160,-27136,
+    -11008,-10496,-12032,-11520, -8960, -8448, -9984, -9472,
+    -15104,-14592,-16128,-15616,-13056,-12544,-14080,-13568,
+      -344,  -328,  -376,  -360,  -280,  -264,  -312,  -296,
+      -472,  -456,  -504,  -488,  -408,  -392,  -440,  -424,
+       -88,   -72,  -120,  -104,   -24,    -8,   -56,   -40,
+      -216,  -200,  -248,  -232,  -152,  -136,  -184,  -168,
+     -1376, -1312, -1504, -1440, -1120, -1056, -1248, -1184,
+     -1888, -1824, -2016, -1952, -1632, -1568, -1760, -1696,
+      -688,  -656,  -752,  -720,  -560,  -528,  -624,  -592,
+      -944,  -912, -1008,  -976,  -816,  -784,  -880,  -848,
+      5504,  5248,  6016,  5760,  4480,  4224,  4992,  4736,
+      7552,  7296,  8064,  7808,  6528,  6272,  7040,  6784,
+      2752,  2624,  3008,  2880,  2240,  2112,  2496,  2368,
+      3776,  3648,  4032,  3904,  3264,  3136,  3520,  3392,
+     22016, 20992, 24064, 23040, 17920, 16896, 19968, 18944,
+     30208, 29184, 32256, 31232, 26112, 25088, 28160, 27136,
+     11008, 10496, 12032, 11520,  8960,  8448,  9984,  9472,
+     15104, 14592, 16128, 15616, 13056, 12544, 14080, 13568,
+       344,   328,   376,   360,   280,   264,   312,   296,
+       472,   456,   504,   488,   408,   392,   440,   424,
+        88,    72,   120,   104,    24,     8,    56,    40,
+       216,   200,   248,   232,   152,   136,   184,   168,
+      1376,  1312,  1504,  1440,  1120,  1056,  1248,  1184,
+      1888,  1824,  2016,  1952,  1632,  1568,  1760,  1696,
+       688,   656,   752,   720,   560,   528,   624,   592,
+       944,   912,  1008,   976,   816,   784,   880,   848
+};
+
+/* Values used when encoding an aLaw sample */
+static const int aLawClip = 32635;
+static const char aLawCompressTable[128] = {
+    1,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,
+    5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+    6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+    6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+    7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+    7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+    7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+    7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7
+};
+
+
+typedef ALubyte ALmulaw;
+typedef ALubyte ALalaw;
+typedef ALubyte ALima4;
+typedef ALubyte ALmsadpcm;
+typedef struct {
+    ALbyte b[3];
+} ALbyte3;
+static_assert(sizeof(ALbyte3)==sizeof(ALbyte[3]), "ALbyte3 size is not 3");
+typedef struct {
+    ALubyte b[3];
+} ALubyte3;
+static_assert(sizeof(ALubyte3)==sizeof(ALubyte[3]), "ALubyte3 size is not 3");
+
+static inline ALshort DecodeMuLaw(ALmulaw val)
+{ return muLawDecompressionTable[val]; }
+
+static ALmulaw EncodeMuLaw(ALshort val)
+{
+    ALint mant, exp, sign;
+
+    sign = (val>>8) & 0x80;
+    if(sign)
+    {
+        /* -32768 doesn't properly negate on a short; it results in itself.
+         * So clamp to -32767 */
+        val = maxi(val, -32767);
+        val = -val;
+    }
+
+    val = mini(val, muLawClip);
+    val += muLawBias;
+
+    exp = muLawCompressTable[(val>>7) & 0xff];
+    mant = (val >> (exp+3)) & 0x0f;
+
+    return ~(sign | (exp<<4) | mant);
+}
+
+static inline ALshort DecodeALaw(ALalaw val)
+{ return aLawDecompressionTable[val]; }
+
+static ALalaw EncodeALaw(ALshort val)
+{
+    ALint mant, exp, sign;
+
+    sign = ((~val) >> 8) & 0x80;
+    if(!sign)
+    {
+        val = maxi(val, -32767);
+        val = -val;
+    }
+    val = mini(val, aLawClip);
+
+    if(val >= 256)
+    {
+        exp = aLawCompressTable[(val>>8) & 0x7f];
+        mant = (val >> (exp+3)) & 0x0f;
+    }
+    else
+    {
+        exp = 0;
+        mant = val >> 4;
+    }
+
+    return ((exp<<4) | mant) ^ (sign^0x55);
+}
+
+static void DecodeIMA4Block(ALshort *dst, const ALima4 *src, ALint numchans, ALsizei align)
+{
+    ALint sample[MAX_INPUT_CHANNELS], index[MAX_INPUT_CHANNELS];
+    ALuint code[MAX_INPUT_CHANNELS];
+    ALsizei j,k,c;
+
+    for(c = 0;c < numchans;c++)
+    {
+        sample[c]  = *(src++);
+        sample[c] |= *(src++) << 8;
+        sample[c]  = (sample[c]^0x8000) - 32768;
+        index[c]  = *(src++);
+        index[c] |= *(src++) << 8;
+        index[c]  = (index[c]^0x8000) - 32768;
+
+        index[c] = clampi(index[c], 0, 88);
+
+        dst[c] = sample[c];
+    }
+
+    for(j = 1;j < align;j += 8)
+    {
+        for(c = 0;c < numchans;c++)
+        {
+            code[c]  = *(src++);
+            code[c] |= *(src++) << 8;
+            code[c] |= *(src++) << 16;
+            code[c] |= *(src++) << 24;
+        }
+
+        for(k = 0;k < 8;k++)
+        {
+            for(c = 0;c < numchans;c++)
+            {
+                int nibble = code[c]&0xf;
+                code[c] >>= 4;
+
+                sample[c] += IMA4Codeword[nibble] * IMAStep_size[index[c]] / 8;
+                sample[c] = clampi(sample[c], -32768, 32767);
+
+                index[c] += IMA4Index_adjust[nibble];
+                index[c] = clampi(index[c], 0, 88);
+
+                dst[(j+k)*numchans + c] = sample[c];
+            }
+        }
+    }
+}
+
+static void EncodeIMA4Block(ALima4 *dst, const ALshort *src, ALint *sample, ALint *index, ALint numchans, ALsizei align)
+{
+    ALsizei j,k,c;
+
+    for(c = 0;c < numchans;c++)
+    {
+        int diff = src[c] - sample[c];
+        int step = IMAStep_size[index[c]];
+        int nibble;
+
+        nibble = 0;
+        if(diff < 0)
+        {
+            nibble = 0x8;
+            diff = -diff;
+        }
+
+        diff = mini(step*2, diff);
+        nibble |= (diff*8/step - 1) / 2;
+
+        sample[c] += IMA4Codeword[nibble] * step / 8;
+        sample[c] = clampi(sample[c], -32768, 32767);
+
+        index[c] += IMA4Index_adjust[nibble];
+        index[c] = clampi(index[c], 0, 88);
+
+        *(dst++) = sample[c] & 0xff;
+        *(dst++) = (sample[c]>>8) & 0xff;
+        *(dst++) = index[c] & 0xff;
+        *(dst++) = (index[c]>>8) & 0xff;
+    }
+
+    for(j = 1;j < align;j += 8)
+    {
+        for(c = 0;c < numchans;c++)
+        {
+            for(k = 0;k < 8;k++)
+            {
+                int diff = src[(j+k)*numchans + c] - sample[c];
+                int step = IMAStep_size[index[c]];
+                int nibble;
+
+                nibble = 0;
+                if(diff < 0)
+                {
+                    nibble = 0x8;
+                    diff = -diff;
+                }
+
+                diff = mini(step*2, diff);
+                nibble |= (diff*8/step - 1) / 2;
+
+                sample[c] += IMA4Codeword[nibble] * step / 8;
+                sample[c] = clampi(sample[c], -32768, 32767);
+
+                index[c] += IMA4Index_adjust[nibble];
+                index[c] = clampi(index[c], 0, 88);
+
+                if(!(k&1)) *dst = nibble;
+                else *(dst++) |= nibble<<4;
+            }
+        }
+    }
+}
+
+
+static void DecodeMSADPCMBlock(ALshort *dst, const ALmsadpcm *src, ALint numchans, ALsizei align)
+{
+    ALubyte blockpred[MAX_INPUT_CHANNELS];
+    ALint delta[MAX_INPUT_CHANNELS];
+    ALshort samples[MAX_INPUT_CHANNELS][2];
+    ALint i, j;
+
+    for(i = 0;i < numchans;i++)
+    {
+        blockpred[i] = *(src++);
+        blockpred[i] = minu(blockpred[i], 6);
+    }
+    for(i = 0;i < numchans;i++)
+    {
+        delta[i]  = *(src++);
+        delta[i] |= *(src++) << 8;
+        delta[i]  = (delta[i]^0x8000) - 0x8000;
+    }
+    for(i = 0;i < numchans;i++)
+    {
+        samples[i][0]  = *(src++);
+        samples[i][0] |= *(src++) << 8;
+        samples[i][0]  = (samples[i][0]^0x8000) - 0x8000;
+    }
+    for(i = 0;i < numchans;i++)
+    {
+        samples[i][1]  = *(src++);
+        samples[i][1] |= *(src++) << 8;
+        samples[i][1]  = (samples[i][1]^0x8000) - 0x8000;
+    }
+
+    /* Second sample is written first. */
+    for(i = 0;i < numchans;i++)
+        *(dst++) = samples[i][1];
+    for(i = 0;i < numchans;i++)
+        *(dst++) = samples[i][0];
+
+    for(j = 2;j < align;j++)
+    {
+        for(i = 0;i < numchans;i++)
+        {
+            const ALint num = (j*numchans) + i;
+            ALint nibble, pred;
+
+            /* Read the nibble (first is in the upper bits). */
+            if(!(num&1))
+                nibble = (*src>>4)&0x0f;
+            else
+                nibble = (*(src++))&0x0f;
+
+            pred  = (samples[i][0]*MSADPCMAdaptionCoeff[blockpred[i]][0] +
+                     samples[i][1]*MSADPCMAdaptionCoeff[blockpred[i]][1]) / 256;
+            pred += ((nibble^0x08) - 0x08) * delta[i];
+            pred  = clampi(pred, -32768, 32767);
+
+            samples[i][1] = samples[i][0];
+            samples[i][0] = pred;
+
+            delta[i] = (MSADPCMAdaption[nibble] * delta[i]) / 256;
+            delta[i] = maxi(16, delta[i]);
+
+            *(dst++) = pred;
+        }
+    }
+}
+
+/* NOTE: This encoder is pretty dumb/simplistic. Some kind of pre-processing
+ * that tries to find the optimal block predictors would be nice, at least. A
+ * multi-pass method that can generate better deltas would be good, too. */
+static void EncodeMSADPCMBlock(ALmsadpcm *dst, const ALshort *src, ALint *sample, ALint numchans, ALsizei align)
+{
+    ALubyte blockpred[MAX_INPUT_CHANNELS];
+    ALint delta[MAX_INPUT_CHANNELS];
+    ALshort samples[MAX_INPUT_CHANNELS][2];
+    ALint i, j;
+
+    /* Block predictor */
+    for(i = 0;i < numchans;i++)
+    {
+        /* FIXME: Calculate something better. */
+        blockpred[i] = 0;
+        *(dst++) = blockpred[i];
+    }
+    /* Initial delta */
+    for(i = 0;i < numchans;i++)
+    {
+        delta[i] = 16;
+        *(dst++) = (delta[i]   ) & 0xff;
+        *(dst++) = (delta[i]>>8) & 0xff;
+    }
+    /* Initial sample 1 */
+    for(i = 0;i < numchans;i++)
+    {
+        samples[i][0] = src[1*numchans + i];
+        *(dst++) = (samples[i][0]   ) & 0xff;
+        *(dst++) = (samples[i][0]>>8) & 0xff;
+    }
+    /* Initial sample 2 */
+    for(i = 0;i < numchans;i++)
+    {
+        samples[i][1] = src[i];
+        *(dst++) = (samples[i][1]   ) & 0xff;
+        *(dst++) = (samples[i][1]>>8) & 0xff;
+    }
+
+    for(j = 2;j < align;j++)
+    {
+        for(i = 0;i < numchans;i++)
+        {
+            const ALint num = (j*numchans) + i;
+            ALint nibble = 0;
+            ALint bias;
+
+            sample[i] = (samples[i][0]*MSADPCMAdaptionCoeff[blockpred[i]][0] +
+                         samples[i][1]*MSADPCMAdaptionCoeff[blockpred[i]][1]) / 256;
+
+            nibble = src[num] - sample[i];
+            if(nibble >= 0)
+                bias = delta[i] / 2;
+            else
+                bias = -delta[i] / 2;
+
+            nibble = (nibble + bias) / delta[i];
+            nibble = clampi(nibble, -8, 7)&0x0f;
+
+            sample[i] += ((nibble^0x08)-0x08) * delta[i];
+            sample[i]  = clampi(sample[i], -32768, 32767);
+
+            samples[i][1] = samples[i][0];
+            samples[i][0] = sample[i];
+
+            delta[i] = (MSADPCMAdaption[nibble] * delta[i]) / 256;
+            delta[i] = maxi(16, delta[i]);
+
+            if(!(num&1))
+                *dst = nibble << 4;
+            else
+            {
+                *dst |= nibble;
+                dst++;
+            }
+        }
+    }
+}
+
+
+static inline ALint DecodeByte3(ALbyte3 val)
+{
+    if(IS_LITTLE_ENDIAN)
+        return (val.b[2]<<16) | (((ALubyte)val.b[1])<<8) | ((ALubyte)val.b[0]);
+    return (val.b[0]<<16) | (((ALubyte)val.b[1])<<8) | ((ALubyte)val.b[2]);
+}
+
+static inline ALbyte3 EncodeByte3(ALint val)
+{
+    if(IS_LITTLE_ENDIAN)
+    {
+        ALbyte3 ret = {{ val, val>>8, val>>16 }};
+        return ret;
+    }
+    else
+    {
+        ALbyte3 ret = {{ val>>16, val>>8, val }};
+        return ret;
+    }
+}
+
+static inline ALint DecodeUByte3(ALubyte3 val)
+{
+    if(IS_LITTLE_ENDIAN)
+        return (val.b[2]<<16) | (val.b[1]<<8) | (val.b[0]);
+    return (val.b[0]<<16) | (val.b[1]<<8) | val.b[2];
+}
+
+static inline ALubyte3 EncodeUByte3(ALint val)
+{
+    if(IS_LITTLE_ENDIAN)
+    {
+        ALubyte3 ret = {{ val, val>>8, val>>16 }};
+        return ret;
+    }
+    else
+    {
+        ALubyte3 ret = {{ val>>16, val>>8, val }};
+        return ret;
+    }
+}
+
+
+/* Define same-type pass-through sample conversion functions (excludes ADPCM,
+ * which are block-based). */
+#define DECL_TEMPLATE(T) \
+static inline T Conv_##T##_##T(T val) { return val; }
+
+DECL_TEMPLATE(ALbyte);
+DECL_TEMPLATE(ALubyte);
+DECL_TEMPLATE(ALshort);
+DECL_TEMPLATE(ALushort);
+DECL_TEMPLATE(ALint);
+DECL_TEMPLATE(ALuint);
+DECL_TEMPLATE(ALbyte3);
+DECL_TEMPLATE(ALubyte3);
+DECL_TEMPLATE(ALalaw);
+DECL_TEMPLATE(ALmulaw);
+
+/* Slightly special handling for floats and doubles (converts NaN to 0, and
+ * allows float<->double pass-through).
+ */
+static inline ALfloat Conv_ALfloat_ALfloat(ALfloat val)
+{ return (val==val) ? val : 0.0f; }
+static inline ALfloat Conv_ALfloat_ALdouble(ALdouble val)
+{ return (val==val) ? (ALfloat)val : 0.0f; }
+static inline ALdouble Conv_ALdouble_ALfloat(ALfloat val)
+{ return (val==val) ? (ALdouble)val : 0.0; }
+static inline ALdouble Conv_ALdouble_ALdouble(ALdouble val)
+{ return (val==val) ? val : 0.0; }
+
+#undef DECL_TEMPLATE
+
+/* Define alternate-sign functions. */
+#define DECL_TEMPLATE(T1, T2, O)                                  \
+static inline T1 Conv_##T1##_##T2(T2 val) { return (T1)val - O; } \
+static inline T2 Conv_##T2##_##T1(T1 val) { return (T2)val + O; }
+
+DECL_TEMPLATE(ALbyte, ALubyte, 128);
+DECL_TEMPLATE(ALshort, ALushort, 32768);
+DECL_TEMPLATE(ALint, ALuint, 2147483648u);
+
+#undef DECL_TEMPLATE
+
+/* Define int-type to int-type functions */
+#define DECL_TEMPLATE(T, ST, UT, SH)                                          \
+static inline T Conv_##T##_##ST(ST val){ return val >> SH; }                  \
+static inline T Conv_##T##_##UT(UT val){ return Conv_##ST##_##UT(val) >> SH; }\
+static inline ST Conv_##ST##_##T(T val){ return val << SH; }                  \
+static inline UT Conv_##UT##_##T(T val){ return Conv_##UT##_##ST(val << SH); }
+
+#define DECL_TEMPLATE2(T1, T2, SH)          \
+DECL_TEMPLATE(AL##T1, AL##T2, ALu##T2, SH)  \
+DECL_TEMPLATE(ALu##T1, ALu##T2, AL##T2, SH)
+
+DECL_TEMPLATE2(byte,  short, 8)
+DECL_TEMPLATE2(short, int,   16)
+DECL_TEMPLATE2(byte,  int,   24)
+
+#undef DECL_TEMPLATE2
+#undef DECL_TEMPLATE
+
+/* Define int-type to fp functions */
+#define DECL_TEMPLATE(T, ST, UT, OP)                                          \
+static inline T Conv_##T##_##ST(ST val) { return (T)val * OP; }               \
+static inline T Conv_##T##_##UT(UT val) { return (T)Conv_##ST##_##UT(val) * OP; }
+
+#define DECL_TEMPLATE2(T1, T2, OP)     \
+DECL_TEMPLATE(T1, AL##T2, ALu##T2, OP)
+
+DECL_TEMPLATE2(ALfloat, byte, (1.0f/127.0f))
+DECL_TEMPLATE2(ALdouble, byte, (1.0/127.0))
+DECL_TEMPLATE2(ALfloat, short, (1.0f/32767.0f))
+DECL_TEMPLATE2(ALdouble, short, (1.0/32767.0))
+DECL_TEMPLATE2(ALdouble, int, (1.0/2147483647.0))
+
+/* Special handling for int32 to float32, since it would overflow. */
+static inline ALfloat Conv_ALfloat_ALint(ALint val)
+{ return (ALfloat)(val>>7) * (1.0f/16777215.0f); }
+static inline ALfloat Conv_ALfloat_ALuint(ALuint val)
+{ return (ALfloat)(Conv_ALint_ALuint(val)>>7) * (1.0f/16777215.0f); }
+
+#undef DECL_TEMPLATE2
+#undef DECL_TEMPLATE
+
+/* Define fp to int-type functions */
+#define DECL_TEMPLATE(FT, T, smin, smax)        \
+static inline AL##T Conv_AL##T##_##FT(FT val)   \
+{                                               \
+    if(val > 1.0f) return smax;                 \
+    if(val < -1.0f) return smin;                \
+    return (AL##T)(val * (FT)smax);             \
+}                                               \
+static inline ALu##T Conv_ALu##T##_##FT(FT val) \
+{ return Conv_ALu##T##_AL##T(Conv_AL##T##_##FT(val)); }
+
+DECL_TEMPLATE(ALfloat, byte, -128, 127)
+DECL_TEMPLATE(ALdouble, byte, -128, 127)
+DECL_TEMPLATE(ALfloat, short, -32768, 32767)
+DECL_TEMPLATE(ALdouble, short, -32768, 32767)
+DECL_TEMPLATE(ALdouble, int, -2147483647-1, 2147483647)
+
+/* Special handling for float32 to int32, since it would overflow. */
+static inline ALint Conv_ALint_ALfloat(ALfloat val)
+{
+    if(val > 1.0f) return 2147483647;
+    if(val < -1.0f) return -2147483647-1;
+    return (ALint)(val * 16777215.0f) << 7;
+}
+static inline ALuint Conv_ALuint_ALfloat(ALfloat val)
+{ return Conv_ALuint_ALint(Conv_ALint_ALfloat(val)); }
+
+#undef DECL_TEMPLATE
+
+/* Define byte3 and ubyte3 functions (goes through int and uint functions). */
+#define DECL_TEMPLATE(T)                            \
+static inline ALbyte3 Conv_ALbyte3_##T(T val)       \
+{ return EncodeByte3(Conv_ALint_##T(val)>>8); }     \
+static inline T Conv_##T##_ALbyte3(ALbyte3 val)     \
+{ return Conv_##T##_ALint(DecodeByte3(val)<<8); }   \
+                                                    \
+static inline ALubyte3 Conv_ALubyte3_##T(T val)     \
+{ return EncodeUByte3(Conv_ALuint_##T(val)>>8); }   \
+static inline T Conv_##T##_ALubyte3(ALubyte3 val)   \
+{ return Conv_##T##_ALuint(DecodeUByte3(val)<<8); }
+
+DECL_TEMPLATE(ALbyte)
+DECL_TEMPLATE(ALubyte)
+DECL_TEMPLATE(ALshort)
+DECL_TEMPLATE(ALushort)
+DECL_TEMPLATE(ALint)
+DECL_TEMPLATE(ALuint)
+DECL_TEMPLATE(ALfloat)
+DECL_TEMPLATE(ALdouble)
+
+#undef DECL_TEMPLATE
+
+/* Define byte3 <-> ubyte3 functions. */
+static inline ALbyte3 Conv_ALbyte3_ALubyte3(ALubyte3 val)
+{ return EncodeByte3(DecodeUByte3(val)-8388608); }
+static inline ALubyte3 Conv_ALubyte3_ALbyte3(ALbyte3 val)
+{ return EncodeUByte3(DecodeByte3(val)+8388608); }
+
+/* Define muLaw and aLaw functions (goes through short functions). */
+#define DECL_TEMPLATE(T)                                                      \
+static inline ALmulaw Conv_ALmulaw_##T(T val)                                 \
+{ return EncodeMuLaw(Conv_ALshort_##T(val)); }                                \
+static inline T Conv_##T##_ALmulaw(ALmulaw val)                               \
+{ return Conv_##T##_ALshort(DecodeMuLaw(val)); }                              \
+                                                                              \
+static inline ALalaw Conv_ALalaw_##T(T val)                                   \
+{ return EncodeALaw(Conv_ALshort_##T(val)); }                                 \
+static inline T Conv_##T##_ALalaw(ALalaw val)                                 \
+{ return Conv_##T##_ALshort(DecodeALaw(val)); }
+
+DECL_TEMPLATE(ALbyte)
+DECL_TEMPLATE(ALubyte)
+DECL_TEMPLATE(ALshort)
+DECL_TEMPLATE(ALushort)
+DECL_TEMPLATE(ALint)
+DECL_TEMPLATE(ALuint)
+DECL_TEMPLATE(ALfloat)
+DECL_TEMPLATE(ALdouble)
+DECL_TEMPLATE(ALbyte3)
+DECL_TEMPLATE(ALubyte3)
+
+#undef DECL_TEMPLATE
+
+/* Define muLaw <-> aLaw functions. */
+static inline ALalaw Conv_ALalaw_ALmulaw(ALmulaw val)
+{ return EncodeALaw(DecodeMuLaw(val)); }
+static inline ALmulaw Conv_ALmulaw_ALalaw(ALalaw val)
+{ return EncodeMuLaw(DecodeALaw(val)); }
+
+
+#define DECL_TEMPLATE(T1, T2)                                                 \
+static void Convert_##T1##_##T2(T1 *dst, const T2 *src, ALuint numchans,      \
+                                ALuint len, ALsizei UNUSED(align))            \
+{                                                                             \
+    ALuint i, j;                                                              \
+    for(i = 0;i < len;i++)                                                    \
+    {                                                                         \
+        for(j = 0;j < numchans;j++)                                           \
+            *(dst++) = Conv_##T1##_##T2(*(src++));                            \
+    }                                                                         \
+}
+
+#define DECL_TEMPLATE2(T)  \
+DECL_TEMPLATE(T, ALbyte)   \
+DECL_TEMPLATE(T, ALubyte)  \
+DECL_TEMPLATE(T, ALshort)  \
+DECL_TEMPLATE(T, ALushort) \
+DECL_TEMPLATE(T, ALint)    \
+DECL_TEMPLATE(T, ALuint)   \
+DECL_TEMPLATE(T, ALfloat)  \
+DECL_TEMPLATE(T, ALdouble) \
+DECL_TEMPLATE(T, ALmulaw)  \
+DECL_TEMPLATE(T, ALalaw)   \
+DECL_TEMPLATE(T, ALbyte3)  \
+DECL_TEMPLATE(T, ALubyte3)
+
+DECL_TEMPLATE2(ALbyte)
+DECL_TEMPLATE2(ALubyte)
+DECL_TEMPLATE2(ALshort)
+DECL_TEMPLATE2(ALushort)
+DECL_TEMPLATE2(ALint)
+DECL_TEMPLATE2(ALuint)
+DECL_TEMPLATE2(ALfloat)
+DECL_TEMPLATE2(ALdouble)
+DECL_TEMPLATE2(ALmulaw)
+DECL_TEMPLATE2(ALalaw)
+DECL_TEMPLATE2(ALbyte3)
+DECL_TEMPLATE2(ALubyte3)
+
+#undef DECL_TEMPLATE2
+#undef DECL_TEMPLATE
+
+#define DECL_TEMPLATE(T)                                                      \
+static void Convert_##T##_ALima4(T *dst, const ALima4 *src, ALuint numchans,  \
+                                 ALuint len, ALuint align)                    \
+{                                                                             \
+    ALsizei byte_align = ((align-1)/2 + 4) * numchans;                        \
+    DECL_VLA(ALshort, tmp, align*numchans);                                   \
+    ALuint i, j, k;                                                           \
+                                                                              \
+    assert(align > 0 && (len%align) == 0);                                    \
+    for(i = 0;i < len;i += align)                                             \
+    {                                                                         \
+        DecodeIMA4Block(tmp, src, numchans, align);                           \
+        src += byte_align;                                                    \
+                                                                              \
+        for(j = 0;j < align;j++)                                              \
+        {                                                                     \
+            for(k = 0;k < numchans;k++)                                       \
+                *(dst++) = Conv_##T##_ALshort(tmp[j*numchans + k]);           \
+        }                                                                     \
+    }                                                                         \
+}
+
+DECL_TEMPLATE(ALbyte)
+DECL_TEMPLATE(ALubyte)
+static void Convert_ALshort_ALima4(ALshort *dst, const ALima4 *src, ALuint numchans,
+                                   ALuint len, ALuint align)
+{
+    ALsizei byte_align = ((align-1)/2 + 4) * numchans;
+    ALuint i;
+
+    assert(align > 0 && (len%align) == 0);
+    for(i = 0;i < len;i += align)
+    {
+        DecodeIMA4Block(dst, src, numchans, align);
+        src += byte_align;
+        dst += align*numchans;
+    }
+}
+DECL_TEMPLATE(ALushort)
+DECL_TEMPLATE(ALint)
+DECL_TEMPLATE(ALuint)
+DECL_TEMPLATE(ALfloat)
+DECL_TEMPLATE(ALdouble)
+DECL_TEMPLATE(ALmulaw)
+DECL_TEMPLATE(ALalaw)
+DECL_TEMPLATE(ALbyte3)
+DECL_TEMPLATE(ALubyte3)
+
+#undef DECL_TEMPLATE
+
+#define DECL_TEMPLATE(T)                                                      \
+static void Convert_ALima4_##T(ALima4 *dst, const T *src, ALuint numchans,    \
+                               ALuint len, ALuint align)                      \
+{                                                                             \
+    ALint sample[MAX_INPUT_CHANNELS] = {0,0,0,0,0,0,0,0};                     \
+    ALint index[MAX_INPUT_CHANNELS] = {0,0,0,0,0,0,0,0};                      \
+    ALsizei byte_align = ((align-1)/2 + 4) * numchans;                        \
+    DECL_VLA(ALshort, tmp, align*numchans);                                   \
+    ALuint i, j, k;                                                           \
+                                                                              \
+    assert(align > 0 && (len%align) == 0);                                    \
+    for(i = 0;i < len;i += align)                                             \
+    {                                                                         \
+        for(j = 0;j < align;j++)                                              \
+        {                                                                     \
+            for(k = 0;k < numchans;k++)                                       \
+                tmp[j*numchans + k] = Conv_ALshort_##T(*(src++));             \
+        }                                                                     \
+        EncodeIMA4Block(dst, tmp, sample, index, numchans, align);            \
+        dst += byte_align;                                                    \
+    }                                                                         \
+}
+
+DECL_TEMPLATE(ALbyte)
+DECL_TEMPLATE(ALubyte)
+static void Convert_ALima4_ALshort(ALima4 *dst, const ALshort *src,
+                                   ALuint numchans, ALuint len, ALuint align)
+{
+    ALint sample[MAX_INPUT_CHANNELS] = {0,0,0,0,0,0,0,0};
+    ALint index[MAX_INPUT_CHANNELS] = {0,0,0,0,0,0,0,0};
+    ALsizei byte_align = ((align-1)/2 + 4) * numchans;
+    ALuint i;
+
+    assert(align > 0 && (len%align) == 0);
+    for(i = 0;i < len;i += align)
+    {
+        EncodeIMA4Block(dst, src, sample, index, numchans, align);
+        src += align*numchans;
+        dst += byte_align;
+    }
+}
+DECL_TEMPLATE(ALushort)
+DECL_TEMPLATE(ALint)
+DECL_TEMPLATE(ALuint)
+DECL_TEMPLATE(ALfloat)
+DECL_TEMPLATE(ALdouble)
+DECL_TEMPLATE(ALmulaw)
+DECL_TEMPLATE(ALalaw)
+DECL_TEMPLATE(ALbyte3)
+DECL_TEMPLATE(ALubyte3)
+
+#undef DECL_TEMPLATE
+
+
+#define DECL_TEMPLATE(T)                                                      \
+static void Convert_##T##_ALmsadpcm(T *dst, const ALmsadpcm *src,             \
+                                    ALuint numchans, ALuint len,              \
+                                    ALuint align)                             \
+{                                                                             \
+    ALsizei byte_align = ((align-2)/2 + 7) * numchans;                        \
+    DECL_VLA(ALshort, tmp, align*numchans);                                   \
+    ALuint i, j, k;                                                           \
+                                                                              \
+    assert(align > 1 && (len%align) == 0);                                    \
+    for(i = 0;i < len;i += align)                                             \
+    {                                                                         \
+        DecodeMSADPCMBlock(tmp, src, numchans, align);                        \
+        src += byte_align;                                                    \
+                                                                              \
+        for(j = 0;j < align;j++)                                              \
+        {                                                                     \
+            for(k = 0;k < numchans;k++)                                       \
+                *(dst++) = Conv_##T##_ALshort(tmp[j*numchans + k]);           \
+        }                                                                     \
+    }                                                                         \
+}
+
+DECL_TEMPLATE(ALbyte)
+DECL_TEMPLATE(ALubyte)
+static void Convert_ALshort_ALmsadpcm(ALshort *dst, const ALmsadpcm *src,
+                                      ALuint numchans, ALuint len,
+                                      ALuint align)
+{
+    ALsizei byte_align = ((align-2)/2 + 7) * numchans;
+    ALuint i;
+
+    assert(align > 1 && (len%align) == 0);
+    for(i = 0;i < len;i += align)
+    {
+        DecodeMSADPCMBlock(dst, src, numchans, align);
+        src += byte_align;
+        dst += align*numchans;
+    }
+}
+DECL_TEMPLATE(ALushort)
+DECL_TEMPLATE(ALint)
+DECL_TEMPLATE(ALuint)
+DECL_TEMPLATE(ALfloat)
+DECL_TEMPLATE(ALdouble)
+DECL_TEMPLATE(ALmulaw)
+DECL_TEMPLATE(ALalaw)
+DECL_TEMPLATE(ALbyte3)
+DECL_TEMPLATE(ALubyte3)
+
+#undef DECL_TEMPLATE
+
+#define DECL_TEMPLATE(T)                                                      \
+static void Convert_ALmsadpcm_##T(ALmsadpcm *dst, const T *src,               \
+                                  ALuint numchans, ALuint len, ALuint align)  \
+{                                                                             \
+    ALint sample[MAX_INPUT_CHANNELS] = {0,0,0,0,0,0,0,0};                     \
+    ALsizei byte_align = ((align-2)/2 + 7) * numchans;                        \
+    DECL_VLA(ALshort, tmp, align*numchans);                                   \
+    ALuint i, j, k;                                                           \
+                                                                              \
+    assert(align > 1 && (len%align) == 0);                                    \
+    for(i = 0;i < len;i += align)                                             \
+    {                                                                         \
+        for(j = 0;j < align;j++)                                              \
+        {                                                                     \
+            for(k = 0;k < numchans;k++)                                       \
+                tmp[j*numchans + k] = Conv_ALshort_##T(*(src++));             \
+        }                                                                     \
+        EncodeMSADPCMBlock(dst, tmp, sample, numchans, align);                \
+        dst += byte_align;                                                    \
+    }                                                                         \
+}
+
+DECL_TEMPLATE(ALbyte)
+DECL_TEMPLATE(ALubyte)
+static void Convert_ALmsadpcm_ALshort(ALmsadpcm *dst, const ALshort *src,
+                                      ALuint numchans, ALuint len, ALuint align)
+{
+    ALint sample[MAX_INPUT_CHANNELS] = {0,0,0,0,0,0,0,0};
+    ALsizei byte_align = ((align-2)/2 + 7) * numchans;
+    ALuint i;
+
+    assert(align > 1 && (len%align) == 0);
+    for(i = 0;i < len;i += align)
+    {
+        EncodeMSADPCMBlock(dst, src, sample, numchans, align);
+        src += align*numchans;
+        dst += byte_align;
+    }
+}
+DECL_TEMPLATE(ALushort)
+DECL_TEMPLATE(ALint)
+DECL_TEMPLATE(ALuint)
+DECL_TEMPLATE(ALfloat)
+DECL_TEMPLATE(ALdouble)
+DECL_TEMPLATE(ALmulaw)
+DECL_TEMPLATE(ALalaw)
+DECL_TEMPLATE(ALbyte3)
+DECL_TEMPLATE(ALubyte3)
+
+#undef DECL_TEMPLATE
+
+/* NOTE: We don't store compressed samples internally, so these conversions
+ * should never happen. */
+static void Convert_ALima4_ALima4(ALima4* UNUSED(dst), const ALima4* UNUSED(src),
+                                  ALuint UNUSED(numchans), ALuint UNUSED(len),
+                                  ALuint UNUSED(align))
+{
+    ERR("Unexpected IMA4-to-IMA4 conversion!\n");
+}
+
+static void Convert_ALmsadpcm_ALmsadpcm(ALmsadpcm* UNUSED(dst), const ALmsadpcm* UNUSED(src),
+                                        ALuint UNUSED(numchans), ALuint UNUSED(len),
+                                        ALuint UNUSED(align))
+{
+    ERR("Unexpected MSADPCM-to-MSADPCM conversion!\n");
+}
+
+static void Convert_ALmsadpcm_ALima4(ALmsadpcm* UNUSED(dst), const ALima4* UNUSED(src),
+                                     ALuint UNUSED(numchans), ALuint UNUSED(len),
+                                     ALuint UNUSED(align))
+{
+    ERR("Unexpected IMA4-to-MSADPCM conversion!\n");
+}
+
+static void Convert_ALima4_ALmsadpcm(ALima4* UNUSED(dst), const ALmsadpcm* UNUSED(src),
+                                     ALuint UNUSED(numchans), ALuint UNUSED(len),
+                                     ALuint UNUSED(align))
+{
+    ERR("Unexpected MSADPCM-to-IMA4 conversion!\n");
+}
+
+
+#define DECL_TEMPLATE(T)                                                      \
+static void Convert_##T(T *dst, const ALvoid *src, enum UserFmtType srcType,  \
+                        ALsizei numchans, ALsizei len, ALsizei align)         \
+{                                                                             \
+    switch(srcType)                                                           \
+    {                                                                         \
+        case UserFmtByte:                                                     \
+            Convert_##T##_ALbyte(dst, src, numchans, len, align);             \
+            break;                                                            \
+        case UserFmtUByte:                                                    \
+            Convert_##T##_ALubyte(dst, src, numchans, len, align);            \
+            break;                                                            \
+        case UserFmtShort:                                                    \
+            Convert_##T##_ALshort(dst, src, numchans, len, align);            \
+            break;                                                            \
+        case UserFmtUShort:                                                   \
+            Convert_##T##_ALushort(dst, src, numchans, len, align);           \
+            break;                                                            \
+        case UserFmtInt:                                                      \
+            Convert_##T##_ALint(dst, src, numchans, len, align);              \
+            break;                                                            \
+        case UserFmtUInt:                                                     \
+            Convert_##T##_ALuint(dst, src, numchans, len, align);             \
+            break;                                                            \
+        case UserFmtFloat:                                                    \
+            Convert_##T##_ALfloat(dst, src, numchans, len, align);            \
+            break;                                                            \
+        case UserFmtDouble:                                                   \
+            Convert_##T##_ALdouble(dst, src, numchans, len, align);           \
+            break;                                                            \
+        case UserFmtMulaw:                                                    \
+            Convert_##T##_ALmulaw(dst, src, numchans, len, align);            \
+            break;                                                            \
+        case UserFmtAlaw:                                                     \
+            Convert_##T##_ALalaw(dst, src, numchans, len, align);             \
+            break;                                                            \
+        case UserFmtIMA4:                                                     \
+            Convert_##T##_ALima4(dst, src, numchans, len, align);             \
+            break;                                                            \
+        case UserFmtMSADPCM:                                                  \
+            Convert_##T##_ALmsadpcm(dst, src, numchans, len, align);          \
+            break;                                                            \
+        case UserFmtByte3:                                                    \
+            Convert_##T##_ALbyte3(dst, src, numchans, len, align);            \
+            break;                                                            \
+        case UserFmtUByte3:                                                   \
+            Convert_##T##_ALubyte3(dst, src, numchans, len, align);           \
+            break;                                                            \
+    }                                                                         \
+}
+
+DECL_TEMPLATE(ALbyte)
+DECL_TEMPLATE(ALubyte)
+DECL_TEMPLATE(ALshort)
+DECL_TEMPLATE(ALushort)
+DECL_TEMPLATE(ALint)
+DECL_TEMPLATE(ALuint)
+DECL_TEMPLATE(ALfloat)
+DECL_TEMPLATE(ALdouble)
+DECL_TEMPLATE(ALmulaw)
+DECL_TEMPLATE(ALalaw)
+DECL_TEMPLATE(ALima4)
+DECL_TEMPLATE(ALmsadpcm)
+DECL_TEMPLATE(ALbyte3)
+DECL_TEMPLATE(ALubyte3)
+
+#undef DECL_TEMPLATE
+
+
+void ConvertData(ALvoid *dst, enum UserFmtType dstType, const ALvoid *src, enum UserFmtType srcType, ALsizei numchans, ALsizei len, ALsizei align)
+{
+    switch(dstType)
+    {
+        case UserFmtByte:
+            Convert_ALbyte(dst, src, srcType, numchans, len, align);
+            break;
+        case UserFmtUByte:
+            Convert_ALubyte(dst, src, srcType, numchans, len, align);
+            break;
+        case UserFmtShort:
+            Convert_ALshort(dst, src, srcType, numchans, len, align);
+            break;
+        case UserFmtUShort:
+            Convert_ALushort(dst, src, srcType, numchans, len, align);
+            break;
+        case UserFmtInt:
+            Convert_ALint(dst, src, srcType, numchans, len, align);
+            break;
+        case UserFmtUInt:
+            Convert_ALuint(dst, src, srcType, numchans, len, align);
+            break;
+        case UserFmtFloat:
+            Convert_ALfloat(dst, src, srcType, numchans, len, align);
+            break;
+        case UserFmtDouble:
+            Convert_ALdouble(dst, src, srcType, numchans, len, align);
+            break;
+        case UserFmtMulaw:
+            Convert_ALmulaw(dst, src, srcType, numchans, len, align);
+            break;
+        case UserFmtAlaw:
+            Convert_ALalaw(dst, src, srcType, numchans, len, align);
+            break;
+        case UserFmtIMA4:
+            Convert_ALima4(dst, src, srcType, numchans, len, align);
+            break;
+        case UserFmtMSADPCM:
+            Convert_ALmsadpcm(dst, src, srcType, numchans, len, align);
+            break;
+        case UserFmtByte3:
+            Convert_ALbyte3(dst, src, srcType, numchans, len, align);
+            break;
+        case UserFmtUByte3:
+            Convert_ALubyte3(dst, src, srcType, numchans, len, align);
+            break;
+    }
+}

+ 55 - 0
Engine/lib/openal-soft/README

@@ -0,0 +1,55 @@
+Source Install
+==============
+
+To install OpenAL Soft, use your favorite shell to go into the build/
+directory, and run:
+
+cmake ..
+
+Assuming configuration went well, you can then build it, typically using GNU
+Make (KDevelop, MSVC, and others are possible depending on your system setup
+and CMake configuration).
+
+Please Note: Double check that the appropriate backends were detected. Often,
+complaints of no sound, crashing, and missing devices can be solved by making
+sure the correct backends are being used. CMake's output will identify which
+backends were enabled.
+
+For most systems, you will likely want to make sure ALSA, OSS, and PulseAudio
+were detected (if your target system uses them). For Windows, make sure
+DirectSound was detected.
+
+
+Utilities
+=========
+
+The source package comes with an informational utility, openal-info, and is
+built by default. It prints out information provided by the ALC and AL sub-
+systems, including discovered devices, version information, and extensions.
+
+
+Configuration
+=============
+
+OpenAL Soft can be configured on a per-user and per-system basis. This allows
+users and sysadmins to control information provided to applications, as well
+as application-agnostic behavior of the library. See alsoftrc.sample for
+available settings.
+
+
+Acknowledgements
+================
+
+Special thanks go to:
+
+Creative Labs for the original source code this is based off of.
+
+Christopher Fitzgerald for the current reverb effect implementation, and
+helping with the low-pass and HRTF filters.
+
+Christian Borss for the 3D panning code previous versions used as a base.
+
+Ben Davis for the idea behind a previous version of the click-removal code.
+
+Richard Furse for helping with my understanding of Ambisonics that is used by
+the various parts of the library.

+ 39 - 0
Engine/lib/openal-soft/XCompile-Android.txt

@@ -0,0 +1,39 @@
+# Cross-compiling requires CMake 2.6 or newer. Example:
+# cmake .. -DCMAKE_TOOLCHAIN_FILE=../XCompile-Android.txt -DHOST=arm-linux-androideabi
+# Where 'arm-linux-androideabi' is the host prefix for the cross-compiler. If
+# you already have a toolchain file setup, you may use that instead of this
+# file. Make sure to set CMAKE_FIND_ROOT_PATH to where the NDK toolchain was
+# installed (e.g. "$ENV{HOME}/toolchains/arm-linux-androideabi-r10c-21").
+
+# the name of the target operating system
+SET(CMAKE_SYSTEM_NAME Linux)
+
+# which compilers to use for C and C++
+SET(CMAKE_C_COMPILER "${HOST}-gcc")
+SET(CMAKE_CXX_COMPILER "${HOST}-g++")
+SET(CMAKE_RC_COMPILER "${HOST}-windres")
+
+# here is the target environment located
+SET(CMAKE_FIND_ROOT_PATH "SET THIS TO THE NDK TOOLCHAIN'S INSTALL PATH")
+
+# here is where stuff gets installed to
+SET(CMAKE_INSTALL_PREFIX "${CMAKE_FIND_ROOT_PATH}" CACHE STRING "Install path prefix, prepended onto install directories." FORCE)
+
+# adjust the default behaviour of the FIND_XXX() commands:
+# search headers and libraries in the target environment, search 
+# programs in the host environment
+set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
+set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
+set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
+
+# set env vars so that pkg-config will look in the appropriate directory for
+# .pc files (as there seems to be no way to force using ${HOST}-pkg-config)
+set(ENV{PKG_CONFIG_LIBDIR} "${CMAKE_INSTALL_PREFIX}/lib/pkgconfig")
+set(ENV{PKG_CONFIG_PATH} "")
+
+# Qt4 tools
+SET(QT_QMAKE_EXECUTABLE ${HOST}-qmake)
+SET(QT_MOC_EXECUTABLE ${HOST}-moc)
+SET(QT_RCC_EXECUTABLE ${HOST}-rcc)
+SET(QT_UIC_EXECUTABLE ${HOST}-uic)
+SET(QT_LRELEASE_EXECUTABLE ${HOST}-lrelease)

+ 37 - 0
Engine/lib/openal-soft/XCompile.txt

@@ -0,0 +1,37 @@
+# Cross-compiling requires CMake 2.6 or newer. Example:
+# cmake .. -DCMAKE_TOOLCHAIN_FILE=../XCompile.txt -DHOST=i686-w64-mingw32
+# Where 'i686-w64-mingw32' is the host prefix for your cross-compiler. If you
+# already have a toolchain file setup, you may use that instead of this file.
+
+# the name of the target operating system
+SET(CMAKE_SYSTEM_NAME Windows)
+
+# which compilers to use for C and C++
+SET(CMAKE_C_COMPILER "${HOST}-gcc")
+SET(CMAKE_CXX_COMPILER "${HOST}-g++")
+SET(CMAKE_RC_COMPILER "${HOST}-windres")
+
+# here is the target environment located
+SET(CMAKE_FIND_ROOT_PATH "/usr/${HOST}")
+
+# here is where stuff gets installed to
+SET(CMAKE_INSTALL_PREFIX "${CMAKE_FIND_ROOT_PATH}" CACHE STRING "Install path prefix, prepended onto install directories." FORCE)
+
+# adjust the default behaviour of the FIND_XXX() commands:
+# search headers and libraries in the target environment, search 
+# programs in the host environment
+set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
+set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
+set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
+
+# set env vars so that pkg-config will look in the appropriate directory for
+# .pc files (as there seems to be no way to force using ${HOST}-pkg-config)
+set(ENV{PKG_CONFIG_LIBDIR} "${CMAKE_INSTALL_PREFIX}/lib/pkgconfig")
+set(ENV{PKG_CONFIG_PATH} "")
+
+# Qt4 tools
+SET(QT_QMAKE_EXECUTABLE ${HOST}-qmake)
+SET(QT_MOC_EXECUTABLE ${HOST}-moc)
+SET(QT_RCC_EXECUTABLE ${HOST}-rcc)
+SET(QT_UIC_EXECUTABLE ${HOST}-uic)
+SET(QT_LRELEASE_EXECUTABLE ${HOST}-lrelease)

+ 456 - 0
Engine/lib/openal-soft/alsoftrc.sample

@@ -0,0 +1,456 @@
+# OpenAL config file.
+#
+# Option blocks may appear multiple times, and duplicated options will take the
+# last value specified. Environment variables may be specified within option
+# values, and are automatically substituted when the config file is loaded.
+# Environment variable names may only contain alpha-numeric characters (a-z,
+# A-Z, 0-9) and underscores (_), and are prefixed with $. For example,
+# specifying "$HOME/file.ext" would typically result in something like
+# "/home/user/file.ext". To specify an actual "$" character, use "$$".
+#
+# Device-specific values may be specified by including the device name in the
+# block name, with "general" replaced by the device name. That is, general
+# options for the device "Name of Device" would be in the [Name of Device]
+# block, while ALSA options would be in the [alsa/Name of Device] block.
+# Options marked as "(global)" are not influenced by the device.
+#
+# The system-wide settings can be put in /etc/openal/alsoft.conf and user-
+# specific override settings in $HOME/.alsoftrc.
+# For Windows, these settings should go into $AppData\alsoft.ini
+#
+# Option and block names are case-senstive. The supplied values are only hints
+# and may not be honored (though generally it'll try to get as close as
+# possible). Note: options that are left unset may default to app- or system-
+# specified values. These are the current available settings:
+
+##
+## General stuff
+##
+[general]
+
+## disable-cpu-exts: (global)
+#  Disables use of specialized methods that use specific CPU intrinsics.
+#  Certain methods may utilize CPU extensions for improved performance, and
+#  this option is useful for preventing some or all of those methods from being
+#  used. The available extensions are: sse, sse2, sse3, sse4.1, and neon.
+#  Specifying 'all' disables use of all such specialized methods.
+#disable-cpu-exts =
+
+## drivers: (global)
+#  Sets the backend driver list order, comma-seperated. Unknown backends and
+#  duplicated names are ignored. Unlisted backends won't be considered for use
+#  unless the list is ended with a comma (e.g. 'oss,' will try OSS first before
+#  other backends, while 'oss' will try OSS only). Backends prepended with -
+#  won't be considered for use (e.g. '-oss,' will try all available backends
+#  except OSS). An empty list means to try all backends.
+#drivers =
+
+## channels:
+#  Sets the output channel configuration. If left unspecified, one will try to
+#  be detected from the system, and defaulting to stereo. The available values
+#  are: mono, stereo, quad, surround51, surround51rear, surround61, surround71,
+#  ambi1, ambi2, ambi3. Note that the ambi* configurations provide ambisonic
+#  channels of the given order (using ACN ordering and SN3D normalization by
+#  default), which need to be decoded to play correctly on speakers.
+#channels =
+
+## sample-type:
+#  Sets the output sample type. Currently, all mixing is done with 32-bit float
+#  and converted to the output sample type as needed. Available values are:
+#  int8    - signed 8-bit int
+#  uint8   - unsigned 8-bit int
+#  int16   - signed 16-bit int
+#  uint16  - unsigned 16-bit int
+#  int32   - signed 32-bit int
+#  uint32  - unsigned 32-bit int
+#  float32 - 32-bit float
+#sample-type = float32
+
+## frequency:
+#  Sets the output frequency. If left unspecified it will try to detect a
+#  default from the system, otherwise it will default to 44100.
+#frequency =
+
+## period_size:
+#  Sets the update period size, in frames. This is the number of frames needed
+#  for each mixing update. Acceptable values range between 64 and 8192.
+#period_size = 1024
+
+## periods:
+#  Sets the number of update periods. Higher values create a larger mix ahead,
+#  which helps protect against skips when the CPU is under load, but increases
+#  the delay between a sound getting mixed and being heard. Acceptable values
+#  range between 2 and 16.
+#periods = 4
+
+## stereo-mode:
+#  Specifies if stereo output is treated as being headphones or speakers. With
+#  headphones, HRTF or crossfeed filters may be used for better audio quality.
+#  Valid settings are auto, speakers, and headphones.
+#stereo-mode = auto
+
+## stereo-panning:
+#  Specifies the panning method for non-HRTF stereo output. uhj (default)
+#  creates stereo-compatible two-channel UHJ output, which encodes some
+#  surround sound information, while paired uses standard pair-wise panning
+#  between -30 and +30 degrees. If crossfeed filters are used, uhj panning is
+#  disabled.
+#stereo-panning = uhj
+
+## ambi-format:
+#  Specifies the channel order and normalization for the "ambi*" set of channel
+#  configurations. Valid settings are: fuma, acn+sn3d, acn+n3d
+#ambi-format = acn+sn3d
+
+## hrtf:
+#  Controls HRTF processing. These filters provide better spatialization of
+#  sounds while using headphones, but do require a bit more CPU power. The
+#  default filters will only work with 44100hz or 48000hz stereo output. While
+#  HRTF is used, the cf_level option is ignored. Setting this to auto (default)
+#  will allow HRTF to be used when headphones are detected or the app requests
+#  it, while setting true or false will forcefully enable or disable HRTF
+#  respectively.
+#hrtf = auto
+
+## default-hrtf:
+#  Specifies the default HRTF to use. When multiple HRTFs are available, this
+#  determines the preferred one to use if none are specifically requested. Note
+#  that this is the enumerated HRTF name, not necessarily the filename.
+#default-hrtf =
+
+## hrtf-paths:
+#  Specifies a comma-separated list of paths containing HRTF data sets. The
+#  format of the files are described in docs/hrtf.txt. The files within the
+#  directories must have the .mhr file extension to be recognized. By default,
+#  OS-dependent data paths will be used. They will also be used if the list
+#  ends with a comma. On Windows this is:
+#  $AppData\openal\hrtf
+#  And on other systems, it's (in order):
+#  $XDG_DATA_HOME/openal/hrtf  (defaults to $HOME/.local/share/openal/hrtf)
+#  $XDG_DATA_DIRS/openal/hrtf  (defaults to /usr/local/share/openal/hrtf and
+#                               /usr/share/openal/hrtf)
+#hrtf-paths =
+
+## cf_level:
+#  Sets the crossfeed level for stereo output. Valid values are:
+#  0 - No crossfeed
+#  1 - Low crossfeed
+#  2 - Middle crossfeed
+#  3 - High crossfeed (virtual speakers are closer to itself)
+#  4 - Low easy crossfeed
+#  5 - Middle easy crossfeed
+#  6 - High easy crossfeed
+#  Users of headphones may want to try various settings. Has no effect on non-
+#  stereo modes.
+#cf_level = 0
+
+## resampler: (global)
+#  Selects the resampler used when mixing sources. Valid values are:
+#  point - nearest sample, no interpolation
+#  linear - extrapolates samples using a linear slope between samples
+#  sinc4 - extrapolates samples using a 4-point Sinc filter
+#  sinc8 - extrapolates samples using an 8-point Sinc filter
+#  bsinc - extrapolates samples using a band-limited Sinc filter (varying
+#          between 12 and 24 points, with anti-aliasing)
+#  Specifying other values will result in using the default (linear).
+#resampler = linear
+
+## rt-prio: (global)
+#  Sets real-time priority for the mixing thread. Not all drivers may use this
+#  (eg. PortAudio) as they already control the priority of the mixing thread.
+#  0 and negative values will disable it. Note that this may constitute a
+#  security risk since a real-time priority thread can indefinitely block
+#  normal-priority threads if it fails to wait. As such, the default is
+#  disabled.
+#rt-prio = 0
+
+## sources:
+#  Sets the maximum number of allocatable sources. Lower values may help for
+#  systems with apps that try to play more sounds than the CPU can handle.
+#sources = 256
+
+## slots:
+#  Sets the maximum number of Auxiliary Effect Slots an app can create. A slot
+#  can use a non-negligible amount of CPU time if an effect is set on it even
+#  if no sources are feeding it, so this may help when apps use more than the
+#  system can handle.
+#slots = 4
+
+## sends:
+#  Sets the number of auxiliary sends per source. When not specified (default),
+#  it allows the app to request how many it wants. The maximum value currently
+#  possible is 4.
+#sends =
+
+## volume-adjust:
+#  A global volume adjustment for source output, expressed in decibels. The
+#  value is logarithmic, so +6 will be a scale of (approximately) 2x, +12 will
+#  be a scale of 4x, etc. Similarly, -6 will be x1/2, and -12 is about x1/4. A
+#  value of 0 means no change.
+#volume-adjust = 0
+
+## excludefx: (global)
+#  Sets which effects to exclude, preventing apps from using them. This can
+#  help for apps that try to use effects which are too CPU intensive for the
+#  system to handle. Available effects are: eaxreverb,reverb,chorus,compressor,
+#  distortion,echo,equalizer,flanger,modulator,dedicated
+#excludefx =
+
+## default-reverb: (global)
+#  A reverb preset that applies by default to all sources on send 0
+#  (applications that set their own slots on send 0 will override this).
+#  Available presets are: None, Generic, PaddedCell, Room, Bathroom,
+#  Livingroom, Stoneroom, Auditorium, ConcertHall, Cave, Arena, Hangar,
+#  CarpetedHallway, Hallway, StoneCorridor, Alley, Forest, City, Moutains,
+#  Quarry, Plain, ParkingLot, SewerPipe, Underwater, Drugged, Dizzy, Psychotic.
+#default-reverb =
+
+## trap-alc-error: (global)
+#  Generates a SIGTRAP signal when an ALC device error is generated, on systems
+#  that support it. This helps when debugging, while trying to find the cause
+#  of a device error. On Windows, a breakpoint exception is generated.
+#trap-alc-error = false
+
+## trap-al-error: (global)
+#  Generates a SIGTRAP signal when an AL context error is generated, on systems
+#  that support it. This helps when debugging, while trying to find the cause
+#  of a context error. On Windows, a breakpoint exception is generated.
+#trap-al-error = false
+
+##
+## Ambisonic decoder stuff
+##
+[decoder]
+
+## hq-mode:
+#  Enables a high-quality ambisonic decoder. This mode is capable of frequency-
+#  dependent processing, creating a better reproduction of 3D sound rendering
+#  over surround sound speakers. Enabling this also requires specifying decoder
+#  configuration files for the appropriate speaker configuration you intend to
+#  use (see the quad, surround51, etc options below). Currently, up to third-
+#  order decoding is supported.
+hq-mode = false
+
+## distance-comp:
+#  Enables compensation for the speakers' relative distances to the listener.
+#  This applies the necessary delays and attenuation to make the speakers
+#  behave as though they are all equidistant, which is important for proper
+#  playback of 3D sound rendering. Requires the high-quality ambisonic decoder,
+#  as well as the proper distances to be specified in the decoder configuration
+#  file.
+distance-comp = true
+
+## quad:
+#  Decoder configuration file for Quadrophonic channel output. See
+#  docs/ambdec.txt for a description of the file format.
+quad =
+
+## surround51:
+#  Decoder configuration file for 5.1 Surround (Side) channel output. See
+#  docs/ambdec.txt for a description of the file format.
+surround51 =
+
+## surround51rear:
+#  Decoder configuration file for 5.1 Surround (Rear) channel output. See
+#  docs/ambdec.txt for a description of the file format.
+surround51rear =
+
+## surround61:
+#  Decoder configuration file for 6.1 Surround channel output. See
+#  docs/ambdec.txt for a description of the file format.
+surround61 =
+
+## surround71:
+#  Decoder configuration file for 7.1 Surround channel output. See
+#  docs/ambdec.txt for a description of the file format. Note: This can be used
+#  to enable 3D7.1 with the appropriate configuration and speaker placement,
+#  see docs/3D7.1.txt.
+surround71 =
+
+##
+## Reverb effect stuff (includes EAX reverb)
+##
+[reverb]
+
+## boost: (global)
+#  A global amplification for reverb output, expressed in decibels. The value
+#  is logarithmic, so +6 will be a scale of (approximately) 2x, +12 will be a
+#  scale of 4x, etc. Similarly, -6 will be about half, and -12 about 1/4th. A
+#  value of 0 means no change.
+#boost = 0
+
+## emulate-eax: (global)
+#  Allows the standard reverb effect to be used in place of EAX reverb. EAX
+#  reverb processing is a bit more CPU intensive than standard, so this option
+#  allows a simpler effect to be used at the loss of some quality.
+#emulate-eax = false
+
+##
+## PulseAudio backend stuff
+##
+[pulse]
+
+## spawn-server: (global)
+#  Attempts to autospawn a PulseAudio server whenever needed (initializing the
+#  backend, enumerating devices, etc). Setting autospawn to false in Pulse's
+#  client.conf will still prevent autospawning even if this is set to true.
+#spawn-server = true
+
+## allow-moves: (global)
+#  Allows PulseAudio to move active streams to different devices. Note that the
+#  device specifier (seen by applications) will not be updated when this
+#  occurs, and neither will the AL device configuration (sample rate, format,
+#  etc).
+#allow-moves = false
+
+## fix-rate:
+#  Specifies whether to match the playback stream's sample rate to the device's
+#  sample rate. Enabling this forces OpenAL Soft to mix sources and effects
+#  directly to the actual output rate, avoiding a second resample pass by the
+#  PulseAudio server.
+#fix-rate = false
+
+##
+## ALSA backend stuff
+##
+[alsa]
+
+## device: (global)
+#  Sets the device name for the default playback device.
+#device = default
+
+## device-prefix: (global)
+#  Sets the prefix used by the discovered (non-default) playback devices. This
+#  will be appended with "CARD=c,DEV=d", where c is the card id and d is the
+#  device index for the requested device name.
+#device-prefix = plughw:
+
+## device-prefix-*: (global)
+#  Card- and device-specific prefixes may be used to override the device-prefix
+#  option. The option may specify the card id (eg, device-prefix-NVidia), or
+#  the card id and device index (eg, device-prefix-NVidia-0). The card id is
+#  case-sensitive.
+#device-prefix- =
+
+## capture: (global)
+#  Sets the device name for the default capture device.
+#capture = default
+
+## capture-prefix: (global)
+#  Sets the prefix used by the discovered (non-default) capture devices. This
+#  will be appended with "CARD=c,DEV=d", where c is the card id and d is the
+#  device number for the requested device name.
+#capture-prefix = plughw:
+
+## capture-prefix-*: (global)
+#  Card- and device-specific prefixes may be used to override the
+#  capture-prefix option. The option may specify the card id (eg,
+#  capture-prefix-NVidia), or the card id and device index (eg,
+#  capture-prefix-NVidia-0). The card id is case-sensitive.
+#capture-prefix- =
+
+## mmap:
+#  Sets whether to try using mmap mode (helps reduce latencies and CPU
+#  consumption). If mmap isn't available, it will automatically fall back to
+#  non-mmap mode. True, yes, on, and non-0 values will attempt to use mmap. 0
+#  and anything else will force mmap off.
+#mmap = true
+
+## allow-resampler:
+#  Specifies whether to allow ALSA's built-in resampler. Enabling this will
+#  allow the playback device to be set to a different sample rate than the
+#  actual output, causing ALSA to apply its own resampling pass after OpenAL
+#  Soft resamples and mixes the sources and effects for output.
+#allow-resampler = false
+
+##
+## OSS backend stuff
+##
+[oss]
+
+## device: (global)
+#  Sets the device name for OSS output.
+#device = /dev/dsp
+
+## capture: (global)
+#  Sets the device name for OSS capture.
+#capture = /dev/dsp
+
+##
+## Solaris backend stuff
+##
+[solaris]
+
+## device: (global)
+#  Sets the device name for Solaris output.
+#device = /dev/audio
+
+##
+## QSA backend stuff
+##
+[qsa]
+
+##
+## JACK backend stuff
+##
+[jack]
+
+## spawn-server: (global)
+#  Attempts to autospawn a JACK server whenever needed (initializing the
+#  backend, opening devices, etc).
+#spawn-server = false
+
+## buffer-size:
+#  Sets the update buffer size, in samples, that the backend will keep buffered
+#  to handle the server's real-time processing requests. This value must be a
+#  power of 2, or else it will be rounded up to the next power of 2. If it is
+#  less than JACK's buffer update size, it will be clamped. This option may
+#  be useful in case the server's update size is too small and doesn't give the
+#  mixer time to keep enough audio available for the processing requests.
+#buffer-size = 0
+
+##
+## MMDevApi backend stuff
+##
+[mmdevapi]
+
+##
+## DirectSound backend stuff
+##
+[dsound]
+
+##
+## Windows Multimedia backend stuff
+##
+[winmm]
+
+##
+## PortAudio backend stuff
+##
+[port]
+
+## device: (global)
+#  Sets the device index for output. Negative values will use the default as
+#  given by PortAudio itself.
+#device = -1
+
+## capture: (global)
+#  Sets the device index for capture. Negative values will use the default as
+#  given by PortAudio itself.
+#capture = -1
+
+##
+## Wave File Writer stuff
+##
+[wave]
+
+## file: (global)
+#  Sets the filename of the wave file to write to. An empty name prevents the
+#  backend from opening, even when explicitly requested.
+#  THIS WILL OVERWRITE EXISTING FILES WITHOUT QUESTION!
+#file =
+
+## bformat: (global)
+#  Creates AMB format files using first-order ambisonics instead of a standard
+#  single- or multi-channel .wav file.
+#bformat = false

+ 14 - 0
Engine/lib/openal-soft/appveyor.yml

@@ -0,0 +1,14 @@
+version: 1.17.2.{build}
+
+environment:
+    matrix:
+      - GEN: "Visual Studio 14 2015"
+        CFG: Release
+      - GEN: "Visual Studio 14 2015 Win64"
+        CFG: Release
+
+build_script:
+    - cd build
+    - cmake .. -G"%GEN%"
+    - cmake --build . --config %CFG% --clean-first
+

+ 9 - 0
Engine/lib/openal-soft/cmake/CheckFileOffsetBits.c

@@ -0,0 +1,9 @@
+#include <sys/types.h>
+
+#define KB ((off_t)(1024))
+#define MB ((off_t)(KB*1024))
+#define GB ((off_t)(MB*1024))
+int tb[((GB+GB+GB) > GB) ? 1 : -1];
+
+int main()
+{ return 0; }

+ 39 - 0
Engine/lib/openal-soft/cmake/CheckFileOffsetBits.cmake

@@ -0,0 +1,39 @@
+# - Check if the _FILE_OFFSET_BITS macro is needed for large files
+# CHECK_FILE_OFFSET_BITS()
+#
+# The following variables may be set before calling this macro to
+# modify the way the check is run:
+#
+#  CMAKE_REQUIRED_FLAGS = string of compile command line flags
+#  CMAKE_REQUIRED_DEFINITIONS = list of macros to define (-DFOO=bar)
+#  CMAKE_REQUIRED_INCLUDES = list of include directories
+# Copyright (c) 2009, Chris Robinson
+#
+# Redistribution and use is allowed according to the terms of the LGPL license.
+
+
+MACRO(CHECK_FILE_OFFSET_BITS)
+
+  IF(NOT DEFINED _FILE_OFFSET_BITS)
+    MESSAGE(STATUS "Checking _FILE_OFFSET_BITS for large files")
+    TRY_COMPILE(__WITHOUT_FILE_OFFSET_BITS_64
+      ${CMAKE_CURRENT_BINARY_DIR}
+      ${CMAKE_CURRENT_SOURCE_DIR}/cmake/CheckFileOffsetBits.c
+      COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS})
+    IF(NOT __WITHOUT_FILE_OFFSET_BITS_64)
+      TRY_COMPILE(__WITH_FILE_OFFSET_BITS_64
+        ${CMAKE_CURRENT_BINARY_DIR}
+        ${CMAKE_CURRENT_SOURCE_DIR}/cmake/CheckFileOffsetBits.c
+        COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} -D_FILE_OFFSET_BITS=64)
+    ENDIF(NOT __WITHOUT_FILE_OFFSET_BITS_64)
+
+    IF(NOT __WITHOUT_FILE_OFFSET_BITS_64 AND __WITH_FILE_OFFSET_BITS_64)
+      SET(_FILE_OFFSET_BITS 64 CACHE INTERNAL "_FILE_OFFSET_BITS macro needed for large files")
+      MESSAGE(STATUS "Checking _FILE_OFFSET_BITS for large files - 64")
+    ELSE(NOT __WITHOUT_FILE_OFFSET_BITS_64 AND __WITH_FILE_OFFSET_BITS_64)
+      SET(_FILE_OFFSET_BITS "" CACHE INTERNAL "_FILE_OFFSET_BITS macro needed for large files")
+      MESSAGE(STATUS "Checking _FILE_OFFSET_BITS for large files - not needed")
+    ENDIF(NOT __WITHOUT_FILE_OFFSET_BITS_64 AND __WITH_FILE_OFFSET_BITS_64)
+  ENDIF(NOT DEFINED _FILE_OFFSET_BITS)
+
+ENDMACRO(CHECK_FILE_OFFSET_BITS)

+ 92 - 0
Engine/lib/openal-soft/cmake/CheckSharedFunctionExists.cmake

@@ -0,0 +1,92 @@
+# - Check if a symbol exists as a function, variable, or macro
+# CHECK_SYMBOL_EXISTS(<symbol> <files> <variable>)
+#
+# Check that the <symbol> is available after including given header
+# <files> and store the result in a <variable>.  Specify the list
+# of files in one argument as a semicolon-separated list.
+#
+# If the header files define the symbol as a macro it is considered
+# available and assumed to work.  If the header files declare the
+# symbol as a function or variable then the symbol must also be
+# available for linking.  If the symbol is a type or enum value
+# it will not be recognized (consider using CheckTypeSize or
+# CheckCSourceCompiles).
+#
+# The following variables may be set before calling this macro to
+# modify the way the check is run:
+#
+#  CMAKE_REQUIRED_FLAGS = string of compile command line flags
+#  CMAKE_REQUIRED_DEFINITIONS = list of macros to define (-DFOO=bar)
+#  CMAKE_REQUIRED_INCLUDES = list of include directories
+#  CMAKE_REQUIRED_LIBRARIES = list of libraries to link
+
+#=============================================================================
+# Copyright 2003-2011 Kitware, Inc.
+#
+# Distributed under the OSI-approved BSD License (the "License");
+# see accompanying file Copyright.txt for details.
+#
+# This software is distributed WITHOUT ANY WARRANTY; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the License for more information.
+#=============================================================================
+# (To distribute this file outside of CMake, substitute the full
+#  License text for the above reference.)
+
+MACRO(CHECK_SHARED_FUNCTION_EXISTS SYMBOL FILES LIBRARY LOCATION VARIABLE)
+  IF("${VARIABLE}" MATCHES "^${VARIABLE}$")
+    SET(CMAKE_CONFIGURABLE_FILE_CONTENT "/* */\n")
+    SET(MACRO_CHECK_SYMBOL_EXISTS_FLAGS ${CMAKE_REQUIRED_FLAGS})
+    IF(CMAKE_REQUIRED_LIBRARIES)
+      SET(CHECK_SYMBOL_EXISTS_LIBS 
+        "-DLINK_LIBRARIES:STRING=${CMAKE_REQUIRED_LIBRARIES};${LIBRARY}")
+    ELSE(CMAKE_REQUIRED_LIBRARIES)
+      SET(CHECK_SYMBOL_EXISTS_LIBS
+        "-DLINK_LIBRARIES:STRING=${LIBRARY}")
+    ENDIF(CMAKE_REQUIRED_LIBRARIES)
+    IF(CMAKE_REQUIRED_INCLUDES)
+      SET(CMAKE_SYMBOL_EXISTS_INCLUDES
+        "-DINCLUDE_DIRECTORIES:STRING=${CMAKE_REQUIRED_INCLUDES}")
+    ELSE(CMAKE_REQUIRED_INCLUDES)
+      SET(CMAKE_SYMBOL_EXISTS_INCLUDES)
+    ENDIF(CMAKE_REQUIRED_INCLUDES)
+    FOREACH(FILE ${FILES})
+      SET(CMAKE_CONFIGURABLE_FILE_CONTENT
+        "${CMAKE_CONFIGURABLE_FILE_CONTENT}#include <${FILE}>\n")
+    ENDFOREACH(FILE)
+    SET(CMAKE_CONFIGURABLE_FILE_CONTENT
+      "${CMAKE_CONFIGURABLE_FILE_CONTENT}\nvoid cmakeRequireSymbol(int dummy,...){(void)dummy;}\nint main()\n{\n  cmakeRequireSymbol(0,&${SYMBOL});\n  return 0;\n}\n")
+
+    CONFIGURE_FILE("${CMAKE_ROOT}/Modules/CMakeConfigurableFile.in"
+      "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/CheckSymbolExists.c" @ONLY)
+
+    MESSAGE(STATUS "Looking for ${SYMBOL} in ${LIBRARY}")
+    TRY_COMPILE(${VARIABLE}
+      ${CMAKE_CURRENT_BINARY_DIR}
+      ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/CheckSymbolExists.c
+      COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS}
+      CMAKE_FLAGS 
+      -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_SYMBOL_EXISTS_FLAGS}
+      -DLINK_DIRECTORIES:STRING=${LOCATION}
+      "${CHECK_SYMBOL_EXISTS_LIBS}"
+      "${CMAKE_SYMBOL_EXISTS_INCLUDES}"
+      OUTPUT_VARIABLE OUTPUT)
+    IF(${VARIABLE})
+      MESSAGE(STATUS "Looking for ${SYMBOL} in ${LIBRARY} - found")
+      SET(${VARIABLE} 1 CACHE INTERNAL "Have symbol ${SYMBOL} in ${LIBRARY}")
+      FILE(APPEND ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log
+        "Determining if the ${SYMBOL} "
+        "exist in ${LIBRARY} passed with the following output:\n"
+        "${OUTPUT}\nFile ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/CheckSymbolExists.c:\n"
+        "${CMAKE_CONFIGURABLE_FILE_CONTENT}\n")
+    ELSE(${VARIABLE})
+      MESSAGE(STATUS "Looking for ${SYMBOL} in ${LIBRARY} - not found.")
+      SET(${VARIABLE} "" CACHE INTERNAL "Have symbol ${SYMBOL} in ${LIBRARY}")
+      FILE(APPEND ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log
+        "Determining if the ${SYMBOL} "
+        "exist in ${LIBRARY} failed with the following output:\n"
+        "${OUTPUT}\nFile ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/CheckSymbolExists.c:\n"
+        "${CMAKE_CONFIGURABLE_FILE_CONTENT}\n")
+    ENDIF(${VARIABLE})
+  ENDIF("${VARIABLE}" MATCHES "^${VARIABLE}$")
+ENDMACRO(CHECK_SHARED_FUNCTION_EXISTS)

+ 73 - 0
Engine/lib/openal-soft/cmake/FindALSA.cmake

@@ -0,0 +1,73 @@
+# - Find alsa
+# Find the alsa libraries (asound)
+#
+#  This module defines the following variables:
+#     ALSA_FOUND       - True if ALSA_INCLUDE_DIR & ALSA_LIBRARY are found
+#     ALSA_LIBRARIES   - Set when ALSA_LIBRARY is found
+#     ALSA_INCLUDE_DIRS - Set when ALSA_INCLUDE_DIR is found
+#
+#     ALSA_INCLUDE_DIR - where to find asoundlib.h, etc.
+#     ALSA_LIBRARY     - the asound library
+#     ALSA_VERSION_STRING - the version of alsa found (since CMake 2.8.8)
+#
+
+#=============================================================================
+# Copyright 2009-2011 Kitware, Inc.
+# Copyright 2009-2011 Philip Lowman <[email protected]>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#  * Redistributions of source code must retain the above copyright notice,
+#    this list of conditions and the following disclaimer.
+#
+#  * Redistributions in binary form must reproduce the above copyright notice,
+#    this list of conditions and the following disclaimer in the documentation
+#    and/or other materials provided with the distribution.
+#
+#  * The names of Kitware, Inc., the Insight Consortium, or the names of
+#    any consortium members, or of any contributors, may not be used to
+#    endorse or promote products derived from this software without
+#    specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS''
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR
+# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#=============================================================================
+
+find_path(ALSA_INCLUDE_DIR NAMES alsa/asoundlib.h
+          DOC "The ALSA (asound) include directory"
+)
+
+find_library(ALSA_LIBRARY NAMES asound
+             DOC "The ALSA (asound) library"
+)
+
+if(ALSA_INCLUDE_DIR AND EXISTS "${ALSA_INCLUDE_DIR}/alsa/version.h")
+  file(STRINGS "${ALSA_INCLUDE_DIR}/alsa/version.h" alsa_version_str REGEX "^#define[\t ]+SND_LIB_VERSION_STR[\t ]+\".*\"")
+
+  string(REGEX REPLACE "^.*SND_LIB_VERSION_STR[\t ]+\"([^\"]*)\".*$" "\\1" ALSA_VERSION_STRING "${alsa_version_str}")
+  unset(alsa_version_str)
+endif()
+
+# handle the QUIETLY and REQUIRED arguments and set ALSA_FOUND to TRUE if
+# all listed variables are TRUE
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(ALSA
+                                  REQUIRED_VARS ALSA_LIBRARY ALSA_INCLUDE_DIR
+                                  VERSION_VAR ALSA_VERSION_STRING)
+
+if(ALSA_FOUND)
+  set( ALSA_LIBRARIES ${ALSA_LIBRARY} )
+  set( ALSA_INCLUDE_DIRS ${ALSA_INCLUDE_DIR} )
+endif()
+
+mark_as_advanced(ALSA_INCLUDE_DIR ALSA_LIBRARY)

+ 21 - 0
Engine/lib/openal-soft/cmake/FindAudioIO.cmake

@@ -0,0 +1,21 @@
+# - Find AudioIO includes and libraries
+#
+#   AUDIOIO_FOUND        - True if AUDIOIO_INCLUDE_DIR is found
+#   AUDIOIO_INCLUDE_DIRS - Set when AUDIOIO_INCLUDE_DIR is found
+#
+#   AUDIOIO_INCLUDE_DIR - where to find sys/audioio.h, etc.
+#
+
+find_path(AUDIOIO_INCLUDE_DIR
+          NAMES sys/audioio.h
+          DOC "The AudioIO include directory"
+)
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(AudioIO  REQUIRED_VARS AUDIOIO_INCLUDE_DIR)
+
+if(AUDIOIO_FOUND)
+    set(AUDIOIO_INCLUDE_DIRS ${AUDIOIO_INCLUDE_DIR})
+endif()
+
+mark_as_advanced(AUDIOIO_INCLUDE_DIR)

+ 35 - 0
Engine/lib/openal-soft/cmake/FindDSound.cmake

@@ -0,0 +1,35 @@
+# - Find DirectSound includes and libraries
+#
+#   DSOUND_FOUND        - True if DSOUND_INCLUDE_DIR & DSOUND_LIBRARY are found
+#   DSOUND_LIBRARIES    - Set when DSOUND_LIBRARY is found
+#   DSOUND_INCLUDE_DIRS - Set when DSOUND_INCLUDE_DIR is found
+#
+#   DSOUND_INCLUDE_DIR - where to find dsound.h, etc.
+#   DSOUND_LIBRARY     - the dsound library
+#
+
+find_path(DSOUND_INCLUDE_DIR
+          NAMES dsound.h
+          PATHS "${DXSDK_DIR}"
+          PATH_SUFFIXES include
+          DOC "The DirectSound include directory"
+)
+
+find_library(DSOUND_LIBRARY
+             NAMES dsound
+             PATHS "${DXSDK_DIR}"
+             PATH_SUFFIXES lib lib/x86 lib/x64
+             DOC "The DirectSound library"
+)
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(DSound
+    REQUIRED_VARS DSOUND_LIBRARY DSOUND_INCLUDE_DIR
+)
+
+if(DSOUND_FOUND)
+    set(DSOUND_LIBRARIES ${DSOUND_LIBRARY})
+    set(DSOUND_INCLUDE_DIRS ${DSOUND_INCLUDE_DIR})
+endif()
+
+mark_as_advanced(DSOUND_INCLUDE_DIR DSOUND_LIBRARY)

+ 173 - 0
Engine/lib/openal-soft/cmake/FindFFmpeg.cmake

@@ -0,0 +1,173 @@
+# vim: ts=2 sw=2
+# - Try to find the required ffmpeg components(default: AVFORMAT, AVUTIL, AVCODEC)
+#
+# Once done this will define
+#  FFMPEG_FOUND         - System has the all required components.
+#  FFMPEG_INCLUDE_DIRS  - Include directory necessary for using the required components headers.
+#  FFMPEG_LIBRARIES     - Link these to use the required ffmpeg components.
+#  FFMPEG_DEFINITIONS   - Compiler switches required for using the required ffmpeg components.
+#
+# For each of the components it will additionaly set.
+#   - AVCODEC
+#   - AVDEVICE
+#   - AVFORMAT
+#   - AVUTIL
+#   - POSTPROC
+#   - SWSCALE
+#   - SWRESAMPLE
+# the following variables will be defined
+#  <component>_FOUND        - System has <component>
+#  <component>_INCLUDE_DIRS - Include directory necessary for using the <component> headers
+#  <component>_LIBRARIES    - Link these to use <component>
+#  <component>_DEFINITIONS  - Compiler switches required for using <component>
+#  <component>_VERSION      - The components version
+#
+# Copyright (c) 2006, Matthias Kretz, <[email protected]>
+# Copyright (c) 2008, Alexander Neundorf, <[email protected]>
+# Copyright (c) 2011, Michael Jansen, <[email protected]>
+#
+# Redistribution and use is allowed according to the terms of the BSD license.
+
+include(FindPackageHandleStandardArgs)
+
+if(NOT FFmpeg_FIND_COMPONENTS)
+    set(FFmpeg_FIND_COMPONENTS AVFORMAT AVCODEC AVUTIL)
+endif()
+
+#
+### Macro: set_component_found
+#
+# Marks the given component as found if both *_LIBRARIES AND *_INCLUDE_DIRS is present.
+#
+macro(set_component_found _component)
+    if(${_component}_LIBRARIES AND ${_component}_INCLUDE_DIRS)
+        # message(STATUS "  - ${_component} found.")
+        set(${_component}_FOUND TRUE)
+    else()
+        # message(STATUS "  - ${_component} not found.")
+    endif()
+endmacro()
+
+#
+### Macro: find_component
+#
+# Checks for the given component by invoking pkgconfig and then looking up the libraries and
+# include directories.
+#
+macro(find_component _component _pkgconfig _library _header)
+    if(NOT WIN32)
+        # use pkg-config to get the directories and then use these values
+        # in the FIND_PATH() and FIND_LIBRARY() calls
+        find_package(PkgConfig)
+        if(PKG_CONFIG_FOUND)
+            pkg_check_modules(PC_${_component} ${_pkgconfig})
+        endif()
+    endif()
+
+    find_path(${_component}_INCLUDE_DIRS ${_header}
+        HINTS
+            ${FFMPEGSDK_INC}
+            ${PC_LIB${_component}_INCLUDEDIR}
+            ${PC_LIB${_component}_INCLUDE_DIRS}
+        PATH_SUFFIXES
+            ffmpeg
+    )
+
+    find_library(${_component}_LIBRARIES NAMES ${_library}
+        HINTS
+            ${FFMPEGSDK_LIB}
+            ${PC_LIB${_component}_LIBDIR}
+            ${PC_LIB${_component}_LIBRARY_DIRS}
+    )
+
+    STRING(REGEX REPLACE "/.*" "/version.h" _ver_header ${_header})
+    if(EXISTS "${${_component}_INCLUDE_DIRS}/${_ver_header}")
+        file(STRINGS "${${_component}_INCLUDE_DIRS}/${_ver_header}" version_str REGEX "^#define[\t ]+LIB${_component}_VERSION_M.*")
+
+        foreach(_str "${version_str}")
+            if(NOT version_maj)
+                string(REGEX REPLACE "^.*LIB${_component}_VERSION_MAJOR[\t ]+([0-9]*).*$" "\\1" version_maj "${_str}")
+            endif()
+            if(NOT version_min)
+                string(REGEX REPLACE "^.*LIB${_component}_VERSION_MINOR[\t ]+([0-9]*).*$" "\\1" version_min "${_str}")
+            endif()
+            if(NOT version_mic)
+                string(REGEX REPLACE "^.*LIB${_component}_VERSION_MICRO[\t ]+([0-9]*).*$" "\\1" version_mic "${_str}")
+            endif()
+        endforeach()
+        unset(version_str)
+
+        set(${_component}_VERSION "${version_maj}.${version_min}.${version_mic}" CACHE STRING "The ${_component} version number.")
+        unset(version_maj)
+        unset(version_min)
+        unset(version_mic)
+    endif(EXISTS "${${_component}_INCLUDE_DIRS}/${_ver_header}")
+    set(${_component}_VERSION     ${PC_${_component}_VERSION}      CACHE STRING "The ${_component} version number.")
+    set(${_component}_DEFINITIONS ${PC_${_component}_CFLAGS_OTHER} CACHE STRING "The ${_component} CFLAGS.")
+
+    set_component_found(${_component})
+
+    mark_as_advanced(
+        ${_component}_INCLUDE_DIRS
+        ${_component}_LIBRARIES
+        ${_component}_DEFINITIONS
+        ${_component}_VERSION)
+endmacro()
+
+
+set(FFMPEGSDK $ENV{FFMPEG_HOME})
+if(FFMPEGSDK)
+    set(FFMPEGSDK_INC "${FFMPEGSDK}/include")
+    set(FFMPEGSDK_LIB "${FFMPEGSDK}/lib")
+endif()
+
+# Check for all possible components.
+find_component(AVCODEC    libavcodec    avcodec    libavcodec/avcodec.h)
+find_component(AVFORMAT   libavformat   avformat   libavformat/avformat.h)
+find_component(AVDEVICE   libavdevice   avdevice   libavdevice/avdevice.h)
+find_component(AVUTIL     libavutil     avutil     libavutil/avutil.h)
+find_component(SWSCALE    libswscale    swscale    libswscale/swscale.h)
+find_component(SWRESAMPLE libswresample swresample libswresample/swresample.h)
+find_component(POSTPROC   libpostproc   postproc   libpostproc/postprocess.h)
+
+# Check if the required components were found and add their stuff to the FFMPEG_* vars.
+foreach(_component ${FFmpeg_FIND_COMPONENTS})
+    if(${_component}_FOUND)
+        # message(STATUS "Required component ${_component} present.")
+        set(FFMPEG_LIBRARIES   ${FFMPEG_LIBRARIES}   ${${_component}_LIBRARIES})
+        set(FFMPEG_DEFINITIONS ${FFMPEG_DEFINITIONS} ${${_component}_DEFINITIONS})
+        list(APPEND FFMPEG_INCLUDE_DIRS ${${_component}_INCLUDE_DIRS})
+    else()
+        # message(STATUS "Required component ${_component} missing.")
+    endif()
+endforeach()
+
+# Build the include path and library list with duplicates removed.
+if(FFMPEG_INCLUDE_DIRS)
+    list(REMOVE_DUPLICATES FFMPEG_INCLUDE_DIRS)
+endif()
+
+if(FFMPEG_LIBRARIES)
+    list(REMOVE_DUPLICATES FFMPEG_LIBRARIES)
+endif()
+
+# cache the vars.
+set(FFMPEG_INCLUDE_DIRS ${FFMPEG_INCLUDE_DIRS} CACHE STRING "The FFmpeg include directories." FORCE)
+set(FFMPEG_LIBRARIES    ${FFMPEG_LIBRARIES}    CACHE STRING "The FFmpeg libraries." FORCE)
+set(FFMPEG_DEFINITIONS  ${FFMPEG_DEFINITIONS}  CACHE STRING "The FFmpeg cflags." FORCE)
+
+mark_as_advanced(FFMPEG_INCLUDE_DIRS FFMPEG_LIBRARIES FFMPEG_DEFINITIONS)
+
+# Now set the noncached _FOUND vars for the components.
+foreach(_component AVCODEC AVDEVICE AVFORMAT AVUTIL POSTPROCESS SWRESAMPLE SWSCALE)
+    set_component_found(${_component})
+endforeach ()
+
+# Compile the list of required vars
+set(_FFmpeg_REQUIRED_VARS FFMPEG_LIBRARIES FFMPEG_INCLUDE_DIRS)
+foreach(_component ${FFmpeg_FIND_COMPONENTS})
+    list(APPEND _FFmpeg_REQUIRED_VARS ${_component}_LIBRARIES ${_component}_INCLUDE_DIRS)
+endforeach()
+
+# Give a nice error message if some of the required vars are missing.
+find_package_handle_standard_args(FFmpeg DEFAULT_MSG ${_FFmpeg_REQUIRED_VARS})

+ 60 - 0
Engine/lib/openal-soft/cmake/FindJACK.cmake

@@ -0,0 +1,60 @@
+# - Find JACK
+# Find the JACK libraries
+#
+#  This module defines the following variables:
+#     JACK_FOUND        - True if JACK_INCLUDE_DIR & JACK_LIBRARY are found
+#     JACK_INCLUDE_DIRS - where to find jack.h, etc.
+#     JACK_LIBRARIES    - the jack library
+#
+
+#=============================================================================
+# Copyright 2009-2011 Kitware, Inc.
+# Copyright 2009-2011 Philip Lowman <[email protected]>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#  * Redistributions of source code must retain the above copyright notice,
+#    this list of conditions and the following disclaimer.
+#
+#  * Redistributions in binary form must reproduce the above copyright notice,
+#    this list of conditions and the following disclaimer in the documentation
+#    and/or other materials provided with the distribution.
+#
+#  * The names of Kitware, Inc., the Insight Consortium, or the names of
+#    any consortium members, or of any contributors, may not be used to
+#    endorse or promote products derived from this software without
+#    specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS''
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR
+# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#=============================================================================
+
+find_path(JACK_INCLUDE_DIR NAMES jack/jack.h
+          DOC "The JACK include directory"
+)
+
+find_library(JACK_LIBRARY NAMES jack
+             DOC "The JACK library"
+)
+
+# handle the QUIETLY and REQUIRED arguments and set JACK_FOUND to TRUE if
+# all listed variables are TRUE
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(JACK REQUIRED_VARS JACK_LIBRARY JACK_INCLUDE_DIR)
+
+if(JACK_FOUND)
+    set(JACK_LIBRARIES ${JACK_LIBRARY})
+    set(JACK_INCLUDE_DIRS ${JACK_INCLUDE_DIR})
+endif()
+
+mark_as_advanced(JACK_INCLUDE_DIR JACK_LIBRARY)

+ 21 - 0
Engine/lib/openal-soft/cmake/FindOSS.cmake

@@ -0,0 +1,21 @@
+# - Find OSS includes
+#
+#   OSS_FOUND        - True if OSS_INCLUDE_DIR is found
+#   OSS_INCLUDE_DIRS - Set when OSS_INCLUDE_DIR is found
+#
+#   OSS_INCLUDE_DIR - where to find sys/soundcard.h, etc.
+#
+
+find_path(OSS_INCLUDE_DIR
+          NAMES sys/soundcard.h
+          DOC "The OSS include directory"
+)
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(OSS  REQUIRED_VARS OSS_INCLUDE_DIR)
+
+if(OSS_FOUND)
+    set(OSS_INCLUDE_DIRS ${OSS_INCLUDE_DIR})
+endif()
+
+mark_as_advanced(OSS_INCLUDE_DIR)

Some files were not shown because too many files changed in this diff