[openal-soft] 01/02: New upstream version 1.18.2

Bret Curtis psi29a at gmail.com
Wed Jan 3 14:25:10 UTC 2018


This is an automated email from the git hooks/post-receive script.

psi29a-guest pushed a commit to branch master
in repository openal-soft.

commit f652d5d2e21794ae86310ffc690990be59a4feca
Author: Bret Curtis <psi29a at gmail.com>
Date:   Wed Jan 3 15:24:53 2018 +0100

    New upstream version 1.18.2
---
 Alc/ALc.c                      | 256 ++++++-----
 Alc/ALu.c                      | 274 +++++-------
 Alc/backends/base.c            | 154 -------
 Alc/backends/base.h            |   3 +-
 Alc/backends/jack.c            |  10 +-
 Alc/backends/mmdevapi.c        |  45 +-
 Alc/backends/oss.c             |  16 +-
 Alc/backends/pulseaudio.c      |  19 +-
 Alc/backends/qsa.c             | 248 +++++++++--
 Alc/backends/wave.c            |   5 +-
 Alc/bformatdec.c               |  39 ++
 Alc/bformatdec.h               |  12 +
 Alc/converter.c                |  46 +-
 Alc/converter.h                |   1 +
 Alc/effects/chorus.c           | 128 +++---
 Alc/effects/flanger.c          | 127 +++---
 Alc/effects/modulator.c        |   2 +-
 Alc/effects/reverb.c           | 111 ++---
 Alc/helpers.c                  |  36 +-
 Alc/hrtf.c                     | 108 ++---
 Alc/hrtf.h                     |   4 -
 Alc/mastering.c                |   2 +-
 Alc/mixer.c                    |  34 +-
 Alc/panning.c                  |  16 +-
 CMakeLists.txt                 | 250 +++++++----
 ChangeLog                      |  34 ++
 OpenAL32/Include/alListener.h  |   2 +-
 OpenAL32/Include/alMain.h      |  71 +--
 OpenAL32/Include/alu.h         |   4 +-
 OpenAL32/alAuxEffectSlot.c     |   7 +-
 OpenAL32/alSource.c            |  12 +-
 alsoftrc.sample                |  13 +-
 appveyor.yml                   |   2 +-
 cmake/FindOSS.cmake            |  14 +-
 common/math_defs.h             |  13 +-
 common/threads.c               |   6 +-
 config.h.in                    |   6 +
 examples/alffplay.cpp          |  35 +-
 examples/alrecord.c            | 386 +++++++++++++++++
 examples/altonegen.c           |  69 ++-
 hrtf/default-44100.mhr         | Bin 53853 -> 53853 bytes
 hrtf/default-48000.mhr         | Bin 53853 -> 53853 bytes
 presets/itu5.1-nocenter.ambdec |  46 ++
 router/al.c                    | 132 ++++++
 router/alc.c                   | 956 +++++++++++++++++++++++++++++++++++++++++
 router/router.c                | 512 ++++++++++++++++++++++
 router/router.h                | 196 +++++++++
 utils/makehrtf.c               | 134 +++---
 48 files changed, 3503 insertions(+), 1093 deletions(-)

diff --git a/Alc/ALc.c b/Alc/ALc.c
index b31c4f0..8c032dd 100644
--- a/Alc/ALc.c
+++ b/Alc/ALc.c
@@ -54,57 +54,52 @@
 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 },
+    { "jack", ALCjackBackendFactory_getFactory },
 #endif
 #ifdef HAVE_PULSEAUDIO
-    { "pulse", ALCpulseBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs },
+    { "pulse", ALCpulseBackendFactory_getFactory },
 #endif
 #ifdef HAVE_ALSA
-    { "alsa", ALCalsaBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs },
+    { "alsa", ALCalsaBackendFactory_getFactory },
 #endif
 #ifdef HAVE_COREAUDIO
-    { "core", ALCcoreAudioBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs },
+    { "core", ALCcoreAudioBackendFactory_getFactory },
 #endif
 #ifdef HAVE_OSS
-    { "oss", ALCossBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs },
+    { "oss", ALCossBackendFactory_getFactory },
 #endif
 #ifdef HAVE_SOLARIS
-    { "solaris", ALCsolarisBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs },
+    { "solaris", ALCsolarisBackendFactory_getFactory },
 #endif
 #ifdef HAVE_SNDIO
-    { "sndio", ALCsndioBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs },
+    { "sndio", ALCsndioBackendFactory_getFactory },
 #endif
 #ifdef HAVE_QSA
-    { "qsa", NULL, alc_qsa_init, alc_qsa_deinit, alc_qsa_probe, EmptyFuncs },
+    { "qsa", ALCqsaBackendFactory_getFactory },
 #endif
 #ifdef HAVE_MMDEVAPI
-    { "mmdevapi", ALCmmdevBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs },
+    { "mmdevapi", ALCmmdevBackendFactory_getFactory },
 #endif
 #ifdef HAVE_DSOUND
-    { "dsound", ALCdsoundBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs },
+    { "dsound", ALCdsoundBackendFactory_getFactory },
 #endif
 #ifdef HAVE_WINMM
-    { "winmm", ALCwinmmBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs },
+    { "winmm", ALCwinmmBackendFactory_getFactory },
 #endif
 #ifdef HAVE_PORTAUDIO
-    { "port", ALCportBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs },
+    { "port", ALCportBackendFactory_getFactory },
 #endif
 #ifdef HAVE_OPENSL
-    { "opensl", ALCopenslBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs },
+    { "opensl", ALCopenslBackendFactory_getFactory },
 #endif
 
-    { "null", ALCnullBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs },
+    { "null", ALCnullBackendFactory_getFactory },
 #ifdef HAVE_WAVE
-    { "wave", ALCwaveBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs },
+    { "wave", ALCwaveBackendFactory_getFactory },
 #endif
 };
 static ALsizei BackendListSize = COUNTOF(BackendList);
@@ -1091,43 +1086,20 @@ static void alc_initconfig(void)
 
     for(i = 0;i < BackendListSize && (!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))
+        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(BackendList[i].Funcs.OpenPlayback && !PlaybackBackend.name)
+        if(!PlaybackBackend.name && V(factory,querySupport)(ALCbackend_Playback))
         {
             PlaybackBackend = BackendList[i];
             TRACE("Added \"%s\" for playback\n", PlaybackBackend.name);
         }
-        if(BackendList[i].Funcs.OpenCapture && !CaptureBackend.name)
+        if(!CaptureBackend.name && V(factory,querySupport)(ALCbackend_Capture))
         {
             CaptureBackend = BackendList[i];
             TRACE("Added \"%s\" for capture\n", CaptureBackend.name);
@@ -1241,7 +1213,6 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void* UNUSED(reserved))
     pthread_setspecific(gJVMThreadKey, env);
     return JNI_VERSION_1_4;
 }
-
 #endif
 
 
@@ -1299,13 +1270,8 @@ static void alc_deinit(void)
 
     for(i = 0;i < BackendListSize;i++)
     {
-        if(!BackendList[i].getFactory)
-            BackendList[i].Deinit();
-        else
-        {
-            ALCbackendFactory *factory = BackendList[i].getFactory();
-            V0(factory,deinit)();
-        }
+        ALCbackendFactory *factory = BackendList[i].getFactory();
+        V0(factory,deinit)();
     }
     {
         ALCbackendFactory *factory = ALCloopbackFactory_getFactory();
@@ -1321,18 +1287,16 @@ static void alc_deinit(void)
  ************************************************/
 static void ProbeDevices(al_string *list, struct BackendInfo *backendinfo, enum DevProbe type)
 {
+    ALCbackendFactory *factory;
+
     DO_INITCONFIG();
 
     LockLists();
     alstr_clear(list);
 
-    if(backendinfo->Probe)
-        backendinfo->Probe(type);
-    else if(backendinfo->getFactory)
-    {
-        ALCbackendFactory *factory = backendinfo->getFactory();
-        V(factory,probe)(type);
-    }
+    factory = backendinfo->getFactory();
+    V(factory,probe)(type);
+
     UnlockLists();
 }
 static void ProbeAllDevicesList(void)
@@ -1777,7 +1741,6 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
     ALCsizei hrtf_id = -1;
     ALCcontext *context;
     ALCuint oldFreq;
-    FPUCtl oldMode;
     size_t size;
     ALCsizei i;
     int val;
@@ -2222,6 +2185,39 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
           device->SourcesMax, device->NumMonoSources, device->NumStereoSources,
           device->AuxiliaryEffectSlotMax, device->NumAuxSends);
 
+    device->DitherDepth = 0.0f;
+    if(GetConfigValueBool(alstr_get_cstr(device->DeviceName), NULL, "dither", 1))
+    {
+        ALint depth = 0;
+        ConfigValueInt(alstr_get_cstr(device->DeviceName), NULL, "dither-depth", &depth);
+        if(depth <= 0)
+        {
+            switch(device->FmtType)
+            {
+                case DevFmtByte:
+                case DevFmtUByte:
+                    depth = 8;
+                    break;
+                case DevFmtShort:
+                case DevFmtUShort:
+                    depth = 16;
+                    break;
+                case DevFmtInt:
+                case DevFmtUInt:
+                case DevFmtFloat:
+                    break;
+            }
+        }
+        else if(depth > 24)
+            depth = 24;
+        device->DitherDepth = (depth > 0) ? powf(2.0f, (ALfloat)(depth-1)) : 0.0f;
+    }
+    if(!(device->DitherDepth > 0.0f))
+        TRACE("Dithering disabled\n");
+    else
+        TRACE("Dithering enabled (%g-bit, %g)\n", log2f(device->DitherDepth)+1.0f,
+              device->DitherDepth);
+
     if(ConfigValueBool(alstr_get_cstr(device->DeviceName), NULL, "output-limiter", &val))
         gainLimiter = val ? ALC_TRUE : ALC_FALSE;
     /* Valid values for gainLimiter are ALC_DONT_CARE_SOFT, ALC_TRUE, and
@@ -2247,25 +2243,25 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
      * allocated with the appropriate size.
      */
     update_failed = AL_FALSE;
-    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)
-            update_failed = AL_TRUE;
-        else
-            UpdateEffectSlotProps(slot);
-    }
-
+    START_MIXER_MODE();
     context = ATOMIC_LOAD_SEQ(&device->ContextList);
     while(context)
     {
         ALsizei pos;
 
+        if(context->DefaultSlot)
+        {
+            ALeffectslot *slot = context->DefaultSlot;
+            ALeffectState *state = slot->Effect.State;
+
+            state->OutBuffer = device->Dry.Buffer;
+            state->OutChannels = device->Dry.NumChannels;
+            if(V(state,deviceUpdate)(device) == AL_FALSE)
+                update_failed = AL_TRUE;
+            else
+                UpdateEffectSlotProps(slot);
+        }
+
         WriteLock(&context->PropLock);
         LockUIntMapRead(&context->EffectSlotMap);
         for(pos = 0;pos < context->EffectSlotMap.size;pos++)
@@ -2362,7 +2358,7 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
 
         context = context->next;
     }
-    RestoreFPUMode(&oldMode);
+    END_MIXER_MODE();
     if(update_failed)
         return ALC_INVALID_DEVICE;
 
@@ -2393,12 +2389,6 @@ static ALCvoid FreeDevice(ALCdevice *device)
 
     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,
@@ -2559,8 +2549,17 @@ static ALvoid InitContext(ALCcontext *Context)
     InitUIntMap(&Context->SourceMap, Context->Device->SourcesMax);
     InitUIntMap(&Context->EffectSlotMap, Context->Device->AuxiliaryEffectSlotMax);
 
-    auxslots = al_calloc(DEF_ALIGN, sizeof(struct ALeffectslotArray));
-    auxslots->count = 0;
+    if(Context->DefaultSlot)
+    {
+        auxslots = al_calloc(DEF_ALIGN, FAM_SIZE(struct ALeffectslotArray, slot, 1));
+        auxslots->count = 1;
+        auxslots->slot[0] = Context->DefaultSlot;
+    }
+    else
+    {
+        auxslots = al_calloc(DEF_ALIGN, sizeof(struct ALeffectslotArray));
+        auxslots->count = 0;
+    }
     ATOMIC_INIT(&Context->ActiveAuxSlots, auxslots);
 
     //Set globals
@@ -2590,6 +2589,12 @@ static void FreeContext(ALCcontext *context)
 
     TRACE("%p\n", context);
 
+    if(context->DefaultSlot)
+    {
+        DeinitEffectSlot(context->DefaultSlot);
+        context->DefaultSlot = NULL;
+    }
+
     auxslots = ATOMIC_EXCHANGE_PTR(&context->ActiveAuxSlots, NULL, almemory_order_relaxed);
     al_free(auxslots);
 
@@ -3598,7 +3603,10 @@ ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCin
 
     ATOMIC_STORE_SEQ(&device->LastError, ALC_NO_ERROR);
 
-    ALContext = al_calloc(16, sizeof(ALCcontext)+sizeof(ALlistener));
+    if(device->Type == Playback && DefaultEffect.type != AL_EFFECT_NULL)
+        ALContext = al_calloc(16, sizeof(ALCcontext)+sizeof(ALlistener)+sizeof(ALeffectslot));
+    else
+        ALContext = al_calloc(16, sizeof(ALCcontext)+sizeof(ALlistener));
     if(!ALContext)
     {
         almtx_unlock(&device->BackendLock);
@@ -3610,6 +3618,7 @@ ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCin
 
     InitRef(&ALContext->ref, 1);
     ALContext->Listener = (ALlistener*)ALContext->_listener_mem;
+    ALContext->DefaultSlot = NULL;
 
     ALContext->Voices = NULL;
     ALContext->VoiceCount = 0;
@@ -3621,9 +3630,6 @@ ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCin
     {
         almtx_unlock(&device->BackendLock);
 
-        al_free(ALContext->Voices);
-        ALContext->Voices = NULL;
-
         al_free(ALContext);
         ALContext = NULL;
 
@@ -3639,6 +3645,18 @@ ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCin
     }
     AllocateVoices(ALContext, 256, device->NumAuxSends);
 
+    if(DefaultEffect.type != AL_EFFECT_NULL && device->Type == Playback)
+    {
+        ALContext->DefaultSlot = (ALeffectslot*)(ALContext->_listener_mem + sizeof(ALlistener));
+        if(InitEffectSlot(ALContext->DefaultSlot) == AL_NO_ERROR)
+            aluInitEffectPanning(ALContext->DefaultSlot);
+        else
+        {
+            ALContext->DefaultSlot = NULL;
+            ERR("Failed to initialize the default effect slot\n");
+        }
+    }
+
     ALCdevice_IncRef(ALContext->Device);
     InitContext(ALContext);
 
@@ -3666,6 +3684,14 @@ ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCin
     }
     almtx_unlock(&device->BackendLock);
 
+    if(ALContext->DefaultSlot)
+    {
+        if(InitializeEffect(device, ALContext->DefaultSlot, &DefaultEffect) == AL_NO_ERROR)
+            UpdateEffectSlotProps(ALContext->DefaultSlot);
+        else
+            ERR("Failed to initialize the default effect\n");
+    }
+
     ALCdevice_DecRef(device);
 
     TRACE("Created context %p\n", ALContext);
@@ -3801,6 +3827,7 @@ ALC_API ALCdevice* ALC_APIENTRY alcGetContextsDevice(ALCcontext *Context)
  */
 ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *deviceName)
 {
+    ALCbackendFactory *factory;
     const ALCchar *fmt;
     ALCdevice *device;
     ALCenum err;
@@ -3826,7 +3853,7 @@ ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *deviceName)
     ))
         deviceName = NULL;
 
-    device = al_calloc(16, sizeof(ALCdevice)+sizeof(ALeffectslot));
+    device = al_calloc(16, sizeof(ALCdevice));
     if(!device)
     {
         alcSetError(NULL, ALC_OUT_OF_MEMORY);
@@ -3887,14 +3914,8 @@ ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *deviceName)
     device->NumUpdates = 3;
     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);
-    }
+    factory = PlaybackBackend.getFactory();
+    device->Backend = V(factory,createBackend)(device, ALCbackend_Playback);
     if(!device->Backend)
     {
         al_free(device);
@@ -4026,32 +4047,8 @@ ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *deviceName)
             ERR("Unsupported ambi-format: %s\n", fmt);
     }
 
-    device->DitherEnabled = GetConfigValueBool(
-        alstr_get_cstr(device->DeviceName), NULL, "dither", 1
-    );
-
     device->Limiter = CreateDeviceLimiter(device);
 
-    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_SEQ(&DeviceList);
         do {
@@ -4126,6 +4123,7 @@ ALC_API ALCboolean ALC_APIENTRY alcCloseDevice(ALCdevice *device)
  ************************************************/
 ALC_API ALCdevice* ALC_APIENTRY alcCaptureOpenDevice(const ALCchar *deviceName, ALCuint frequency, ALCenum format, ALCsizei samples)
 {
+    ALCbackendFactory *factory;
     ALCdevice *device = NULL;
     ALCenum err;
     ALCsizei i;
@@ -4183,14 +4181,8 @@ ALC_API ALCdevice* ALC_APIENTRY alcCaptureOpenDevice(const ALCchar *deviceName,
         device->ChannelDelay[i].Buffer = NULL;
     }
 
-    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);
-    }
+    factory = CaptureBackend.getFactory();
+    device->Backend = V(factory,createBackend)(device, ALCbackend_Capture);
     if(!device->Backend)
     {
         al_free(device);
@@ -4450,8 +4442,6 @@ ALC_API ALCdevice* ALC_APIENTRY alcLoopbackOpenDeviceSOFT(const ALCchar *deviceN
     // Open the "backend"
     V(device->Backend,open)("Loopback");
 
-    device->DitherEnabled = GetConfigValueBool(NULL, NULL, "dither", 1);
-
     device->Limiter = CreateDeviceLimiter(device);
 
     {
diff --git a/Alc/ALu.c b/Alc/ALu.c
index 11b3fd2..fa9634b 100644
--- a/Alc/ALu.c
+++ b/Alc/ALu.c
@@ -142,11 +142,11 @@ static inline HrtfDirectMixerFunc SelectHrtfMixer(void)
 
 /* Prior to VS2013, MSVC lacks the round() family of functions. */
 #if defined(_MSC_VER) && _MSC_VER < 1800
-static long lroundf(float val)
+static float roundf(float val)
 {
-    if(val < 0.0)
-        return fastf2i(ceilf(val-0.5f));
-    return fastf2i(floorf(val+0.5f));
+    if(val < 0.0f)
+        return ceilf(val-0.5f);
+    return floorf(val+0.5f);
 }
 #endif
 
@@ -214,7 +214,7 @@ static aluVector aluMatrixfVector(const aluMatrixf *mtx, const aluVector *vec)
  * modified for use with an interpolated increment for buttery-smooth pitch
  * changes.
  */
-static ALboolean BsincPrepare(const ALuint increment, BsincState *state)
+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 };
@@ -231,7 +231,7 @@ static ALboolean BsincPrepare(const ALuint increment, BsincState *state)
         { 24, 24, 24, 24, 24, 24, 24, 20, 20, 20, 16, 16, 16, 12, 12, 0 }
     };
     ALfloat sf;
-    ALuint si, pi;
+    ALsizei si, pi;
     ALboolean uncut = AL_TRUE;
 
     if(increment > FRACTIONONE)
@@ -249,7 +249,7 @@ static ALboolean BsincPrepare(const ALuint increment, BsincState *state)
         else
         {
             sf = (BSINC_SCALE_COUNT - 1) * (sf - scaleBase) * scaleRange;
-            si = fastf2u(sf);
+            si = fastf2i(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.
@@ -1030,7 +1030,7 @@ static void CalcNonAttnSourceParams(ALvoice *voice, const struct ALvoiceProps *p
     {
         SendSlots[i] = props->Send[i].Slot;
         if(!SendSlots[i] && i == 0)
-            SendSlots[i] = Device->DefaultSlot;
+            SendSlots[i] = ALContext->DefaultSlot;
         if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
         {
             SendSlots[i] = NULL;
@@ -1100,7 +1100,7 @@ static void CalcAttnSourceParams(ALvoice *voice, const struct ALvoiceProps *prop
     {
         SendSlots[i] = props->Send[i].Slot;
         if(!SendSlots[i] && i == 0)
-            SendSlots[i] = Device->DefaultSlot;
+            SendSlots[i] = ALContext->DefaultSlot;
         if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
         {
             SendSlots[i] = NULL;
@@ -1526,11 +1526,37 @@ static void ApplyDistanceComp(ALfloatBUFFERSIZE *restrict Samples, DistanceComp
     }
 }
 
+static void ApplyDither(ALfloatBUFFERSIZE *restrict Samples, ALuint *dither_seed,
+                        const ALfloat quant_scale, const ALsizei SamplesToDo,
+                        const ALsizei numchans)
+{
+    const ALfloat invscale = 1.0f / quant_scale;
+    ALuint seed = *dither_seed;
+    ALsizei c, i;
+
+    /* Dithering. Step 1, generate whitenoise (uniform distribution of random
+     * values between -1 and +1). Step 2 is to add the noise to the samples,
+     * before rounding and after scaling up to the desired quantization depth.
+     */
+    for(c = 0;c < numchans;c++)
+    {
+        ALfloat *restrict samples = Samples[c];
+        for(i = 0;i < SamplesToDo;i++)
+        {
+            ALfloat val = samples[i] * quant_scale;
+            ALuint rng0 = dither_rng(&seed);
+            ALuint rng1 = dither_rng(&seed);
+            val += (ALfloat)(rng0*(1.0/UINT_MAX) - rng1*(1.0/UINT_MAX));
+            samples[i] = roundf(val) * invscale;
+        }
+    }
+    *dither_seed = seed;
+}
+
 
-/* NOTE: Non-dithered conversions have unused extra parameters. */
-static inline ALfloat aluF2F(ALfloat val, ...)
+static inline ALfloat Conv_ALfloat(ALfloat val)
 { return val; }
-static inline ALint aluF2I(ALfloat val, ...)
+static inline ALint Conv_ALint(ALfloat val)
 {
     /* Floats only have a 24-bit mantissa, so [-16777216, +16777216] is the max
      * integer range normalized floats can be safely converted to (a bit of the
@@ -1538,87 +1564,58 @@ static inline ALint aluF2I(ALfloat val, ...)
      */
     return fastf2i(clampf(val*16777216.0f, -16777216.0f, 16777215.0f))<<7;
 }
-static inline ALshort aluF2S(ALfloat val, ...)
+static inline ALshort Conv_ALshort(ALfloat val)
 { return fastf2i(clampf(val*32768.0f, -32768.0f, 32767.0f)); }
-static inline ALbyte aluF2B(ALfloat val, ...)
+static inline ALbyte Conv_ALbyte(ALfloat val)
 { return fastf2i(clampf(val*128.0f, -128.0f, 127.0f)); }
 
-/* Dithered conversion functions. Only applies to 8- and 16-bit output for now,
- * as 32-bit int and float are at the limits of the rendered sample depth. This
- * can change if the dithering bit depth becomes configurable (effectively
- * quantizing to a lower bit depth than the output is capable of).
- */
-static inline ALshort aluF2SDithered(ALfloat val, const ALfloat dither_val)
-{
-    val = val*32768.0f + dither_val;
-    return lroundf(clampf(val, -32768.0f, 32767.0f));
-}
-static inline ALbyte aluF2BDithered(ALfloat val, const ALfloat dither_val)
-{
-    val = val*128.0f + dither_val;
-    return lroundf(clampf(val, -128.0f, 127.0f));
-}
-
 /* Define unsigned output variations. */
-#define DECL_TEMPLATE(T, Name, func, O)                     \
-static inline T Name(ALfloat val, const ALfloat dither_val) \
-{ return func(val, dither_val)+O; }
+#define DECL_TEMPLATE(T, func, O)                             \
+static inline T Conv_##T(ALfloat val) { return func(val)+O; }
 
-DECL_TEMPLATE(ALubyte, aluF2UB, aluF2B, 128)
-DECL_TEMPLATE(ALushort, aluF2US, aluF2S, 32768)
-DECL_TEMPLATE(ALuint, aluF2UI, aluF2I, 2147483648u)
-DECL_TEMPLATE(ALubyte, aluF2UBDithered, aluF2BDithered, 128)
-DECL_TEMPLATE(ALushort, aluF2USDithered, aluF2SDithered, 32768)
+DECL_TEMPLATE(ALubyte, Conv_ALbyte, 128)
+DECL_TEMPLATE(ALushort, Conv_ALshort, 32768)
+DECL_TEMPLATE(ALuint, Conv_ALint, 2147483648u)
 
 #undef DECL_TEMPLATE
 
-#define DECL_TEMPLATE(T, D, func)                                             \
-static void Write##T##D(const ALfloatBUFFERSIZE *InBuffer, ALvoid *OutBuffer, \
-                        const ALfloat *restrict DitherValues,                 \
-                        ALsizei SamplesToDo, ALsizei numchans)                \
+#define DECL_TEMPLATE(T, A)                                                   \
+static void Write##A(const ALfloatBUFFERSIZE *InBuffer, ALvoid *OutBuffer,    \
+                     ALsizei Offset, ALsizei SamplesToDo, ALsizei numchans)   \
 {                                                                             \
     ALsizei i, j;                                                             \
     for(j = 0;j < numchans;j++)                                               \
     {                                                                         \
         const ALfloat *restrict in = ASSUME_ALIGNED(InBuffer[j], 16);         \
-        T *restrict out = (T*)OutBuffer + j;                                  \
+        T *restrict out = (T*)OutBuffer + Offset*numchans + j;                \
                                                                               \
         for(i = 0;i < SamplesToDo;i++)                                        \
-            out[i*numchans] = func(in[i], DitherValues[i]);                   \
+            out[i*numchans] = Conv_##T(in[i]);                                \
     }                                                                         \
 }
 
-DECL_TEMPLATE(ALfloat, /*no dither*/, aluF2F)
-DECL_TEMPLATE(ALuint, /*no dither*/, aluF2UI)
-DECL_TEMPLATE(ALint, /*no dither*/, aluF2I)
-DECL_TEMPLATE(ALushort, /*no dither*/, aluF2US)
-DECL_TEMPLATE(ALshort, /*no dither*/, aluF2S)
-DECL_TEMPLATE(ALubyte, /*no dither*/, aluF2UB)
-DECL_TEMPLATE(ALbyte, /*no dither*/, aluF2B)
-
-DECL_TEMPLATE(ALushort, _Dithered, aluF2USDithered)
-DECL_TEMPLATE(ALshort, _Dithered, aluF2SDithered)
-DECL_TEMPLATE(ALubyte, _Dithered, aluF2UBDithered)
-DECL_TEMPLATE(ALbyte, _Dithered, aluF2BDithered)
+DECL_TEMPLATE(ALfloat, F32)
+DECL_TEMPLATE(ALuint, UI32)
+DECL_TEMPLATE(ALint, I32)
+DECL_TEMPLATE(ALushort, UI16)
+DECL_TEMPLATE(ALshort, I16)
+DECL_TEMPLATE(ALubyte, UI8)
+DECL_TEMPLATE(ALbyte, I8)
 
 #undef DECL_TEMPLATE
 
 
-void aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size)
+void aluMixData(ALCdevice *device, ALvoid *OutBuffer, ALsizei NumSamples)
 {
     ALsizei SamplesToDo;
-    ALvoice **voice, **voice_end;
-    ALeffectslot *slot;
-    ALsource *source;
+    ALsizei SamplesDone;
     ALCcontext *ctx;
-    FPUCtl oldMode;
     ALsizei i, c;
 
-    SetMixerFPUMode(&oldMode);
-
-    while(size > 0)
+    START_MIXER_MODE();
+    for(SamplesDone = 0;SamplesDone < NumSamples;)
     {
-        SamplesToDo = mini(size, BUFFERSIZE);
+        SamplesToDo = mini(NumSamples-SamplesDone, BUFFERSIZE);
         for(c = 0;c < device->Dry.NumChannels;c++)
             memset(device->Dry.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
         if(device->Dry.Buffer != device->FOAOut.Buffer)
@@ -1630,13 +1627,6 @@ void aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size)
 
         IncrementRef(&device->MixCount);
 
-        if((slot=device->DefaultSlot) != NULL)
-        {
-            CalcEffectSlotParams(device->DefaultSlot, device);
-            for(c = 0;c < slot->NumChannels;c++)
-                memset(slot->WetBuffer[c], 0, SamplesToDo*sizeof(ALfloat));
-        }
-
         ctx = ATOMIC_LOAD(&device->ContextList, almemory_order_acquire);
         while(ctx)
         {
@@ -1653,18 +1643,17 @@ void aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size)
             }
 
             /* source processing */
-            voice = ctx->Voices;
-            voice_end = voice + ctx->VoiceCount;
-            for(;voice != voice_end;++voice)
+            for(i = 0;i < ctx->VoiceCount;i++)
             {
-                source = ATOMIC_LOAD(&(*voice)->Source, almemory_order_acquire);
-                if(source && ATOMIC_LOAD(&(*voice)->Playing, almemory_order_relaxed) &&
-                   (*voice)->Step > 0)
+                ALvoice *voice = ctx->Voices[i];
+                ALsource *source = ATOMIC_LOAD(&voice->Source, almemory_order_acquire);
+                if(source && ATOMIC_LOAD(&voice->Playing, almemory_order_relaxed) &&
+                   voice->Step > 0)
                 {
-                    if(!MixSource(*voice, source, device, SamplesToDo))
+                    if(!MixSource(voice, source, device, SamplesToDo))
                     {
-                        ATOMIC_STORE(&(*voice)->Source, NULL, almemory_order_relaxed);
-                        ATOMIC_STORE(&(*voice)->Playing, false, almemory_order_release);
+                        ATOMIC_STORE(&voice->Source, NULL, almemory_order_relaxed);
+                        ATOMIC_STORE(&voice->Playing, false, almemory_order_release);
                     }
                 }
             }
@@ -1681,14 +1670,6 @@ void aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size)
             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
@@ -1770,133 +1751,80 @@ void aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size)
             }
         }
 
-        if(buffer)
+        if(OutBuffer)
         {
-            ALfloat (*OutBuffer)[BUFFERSIZE] = device->RealOut.Buffer;
-            ALsizei OutChannels = device->RealOut.NumChannels;
-            struct Compressor *Limiter = device->Limiter;
-            ALfloat *DitherValues;
+            ALfloat (*Buffer)[BUFFERSIZE] = device->RealOut.Buffer;
+            ALsizei Channels = device->RealOut.NumChannels;
 
             /* Use NFCtrlData for temp value storage. */
-            ApplyDistanceComp(OutBuffer, device->ChannelDelay, device->NFCtrlData,
-                              SamplesToDo, OutChannels);
+            ApplyDistanceComp(Buffer, device->ChannelDelay, device->NFCtrlData,
+                              SamplesToDo, Channels);
 
-            if(Limiter)
-                ApplyCompression(Limiter, OutChannels, SamplesToDo, OutBuffer);
+            if(device->Limiter)
+                ApplyCompression(device->Limiter, Channels, SamplesToDo, Buffer);
 
-            /* Dithering. Step 1, generate whitenoise (uniform distribution of
-             * random values between -1 and +1). Use NFCtrlData for random
-             * value storage. Step 2 is to add the noise to the samples, before
-             * rounding and after scaling up to the desired quantization depth,
-             * which occurs in the sample conversion stage.
-             */
-            if(!device->DitherEnabled)
-                memset(device->NFCtrlData, 0, SamplesToDo*sizeof(ALfloat));
-            else
-            {
-                ALuint dither_seed = device->DitherSeed;
-                ALsizei i;
-
-                for(i = 0;i < SamplesToDo;i++)
-                {
-                    ALuint rng0 = dither_rng(&dither_seed);
-                    ALuint rng1 = dither_rng(&dither_seed);
-                    device->NFCtrlData[i] = (ALfloat)(rng0*(1.0/UINT_MAX) - rng1*(1.0/UINT_MAX));
-                }
-                device->DitherSeed = dither_seed;
-            }
-            DitherValues = device->NFCtrlData;
+            if(device->DitherDepth > 0.0f)
+                ApplyDither(Buffer, &device->DitherSeed, device->DitherDepth, SamplesToDo,
+                            Channels);
 
-#define WRITE(T, D, a, b, c, d, e) do {                                       \
-    Write##T##D(SAFE_CONST(ALfloatBUFFERSIZE*,(a)), (b), (c), (d), (e));      \
-    buffer = (T*)buffer + (d)*(e);                                            \
-} while(0)
             switch(device->FmtType)
             {
                 case DevFmtByte:
-                    if(device->DitherEnabled)
-                        WRITE(ALbyte, _Dithered, OutBuffer, buffer, DitherValues,
-                              SamplesToDo, OutChannels);
-                    else
-                        WRITE(ALbyte, /*no dither*/, OutBuffer, buffer, DitherValues,
-                              SamplesToDo, OutChannels);
+                    WriteI8(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
                     break;
                 case DevFmtUByte:
-                    if(device->DitherEnabled)
-                        WRITE(ALubyte, _Dithered, OutBuffer, buffer, DitherValues,
-                              SamplesToDo, OutChannels);
-                    else
-                        WRITE(ALubyte, /*no dither*/, OutBuffer, buffer, DitherValues,
-                              SamplesToDo, OutChannels);
+                    WriteUI8(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
                     break;
                 case DevFmtShort:
-                    if(device->DitherEnabled)
-                        WRITE(ALshort, _Dithered, OutBuffer, buffer, DitherValues,
-                              SamplesToDo, OutChannels);
-                    else
-                        WRITE(ALshort, /*no dither*/, OutBuffer, buffer, DitherValues,
-                              SamplesToDo, OutChannels);
+                    WriteI16(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
                     break;
                 case DevFmtUShort:
-                    if(device->DitherEnabled)
-                        WRITE(ALushort, _Dithered, OutBuffer, buffer, DitherValues,
-                              SamplesToDo, OutChannels);
-                    else
-                        WRITE(ALushort, /*no dither*/, OutBuffer, buffer, DitherValues,
-                              SamplesToDo, OutChannels);
+                    WriteUI16(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
                     break;
                 case DevFmtInt:
-                    WRITE(ALint, /*no dither*/, OutBuffer, buffer, DitherValues,
-                          SamplesToDo, OutChannels);
+                    WriteI32(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
                     break;
                 case DevFmtUInt:
-                    WRITE(ALuint, /*no dither*/, OutBuffer, buffer, DitherValues,
-                          SamplesToDo, OutChannels);
+                    WriteUI32(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
                     break;
                 case DevFmtFloat:
-                    WRITE(ALfloat, /*no dither*/, OutBuffer, buffer, DitherValues,
-                          SamplesToDo, OutChannels);
+                    WriteF32(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
                     break;
             }
-#undef WRITE
         }
 
-        size -= SamplesToDo;
+        SamplesDone += SamplesToDo;
     }
-
-    RestoreFPUMode(&oldMode);
+    END_MIXER_MODE();
 }
 
 
 void aluHandleDisconnect(ALCdevice *device)
 {
-    ALCcontext *Context;
+    ALCcontext *ctx;
 
     device->Connected = ALC_FALSE;
 
-    Context = ATOMIC_LOAD_SEQ(&device->ContextList);
-    while(Context)
+    ctx = ATOMIC_LOAD_SEQ(&device->ContextList);
+    while(ctx)
     {
-        ALvoice **voice, **voice_end;
-
-        voice = Context->Voices;
-        voice_end = voice + Context->VoiceCount;
-        while(voice != voice_end)
+        ALsizei i;
+        for(i = 0;i < ctx->VoiceCount;i++)
         {
-            ALsource *source = ATOMIC_EXCHANGE_PTR(&(*voice)->Source, NULL,
-                                                   almemory_order_acq_rel);
-            ATOMIC_STORE(&(*voice)->Playing, false, almemory_order_release);
+            ALvoice *voice = ctx->Voices[i];
+            ALsource *source;
+
+            source = ATOMIC_EXCHANGE_PTR(&voice->Source, NULL, almemory_order_acq_rel);
+            ATOMIC_STORE(&voice->Playing, false, almemory_order_release);
 
             if(source)
             {
                 ALenum playing = AL_PLAYING;
                 (void)(ATOMIC_COMPARE_EXCHANGE_STRONG_SEQ(&source->state, &playing, AL_STOPPED));
             }
-
-            voice++;
         }
-        Context->VoiceCount = 0;
+        ctx->VoiceCount = 0;
 
-        Context = Context->next;
+        ctx = ctx->next;
     }
 }
diff --git a/Alc/backends/base.c b/Alc/backends/base.c
index 902c431..5f568d8 100644
--- a/Alc/backends/base.c
+++ b/Alc/backends/base.c
@@ -79,157 +79,3 @@ void ALCbackend_unlock(ALCbackend *self)
 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;
-}
diff --git a/Alc/backends/base.h b/Alc/backends/base.h
index 961a4d1..8464fde 100644
--- a/Alc/backends/base.h
+++ b/Alc/backends/base.h
@@ -142,6 +142,7 @@ ALCbackendFactory *ALCossBackendFactory_getFactory(void);
 ALCbackendFactory *ALCjackBackendFactory_getFactory(void);
 ALCbackendFactory *ALCsolarisBackendFactory_getFactory(void);
 ALCbackendFactory *ALCsndioBackendFactory_getFactory(void);
+ALCbackendFactory *ALCqsaBackendFactory_getFactory(void);
 ALCbackendFactory *ALCmmdevBackendFactory_getFactory(void);
 ALCbackendFactory *ALCdsoundBackendFactory_getFactory(void);
 ALCbackendFactory *ALCwinmmBackendFactory_getFactory(void);
@@ -151,6 +152,4 @@ 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 */
diff --git a/Alc/backends/jack.c b/Alc/backends/jack.c
index 85d5d9c..ceff0b3 100644
--- a/Alc/backends/jack.c
+++ b/Alc/backends/jack.c
@@ -63,6 +63,7 @@ static const ALCchar jackDevice[] = "JACK Default";
 static void *jack_handle;
 #define MAKE_FUNC(f) static __typeof(f) * p##f
 JACK_FUNCS(MAKE_FUNC);
+static __typeof(jack_error_callback) * pjack_error_callback;
 #undef MAKE_FUNC
 
 #define jack_client_open pjack_client_open
@@ -84,6 +85,7 @@ JACK_FUNCS(MAKE_FUNC);
 #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
+#define jack_error_callback (*pjack_error_callback)
 #endif
 
 
@@ -120,6 +122,10 @@ static ALCboolean jack_load(void)
 } while(0)
         JACK_FUNCS(LOAD_FUNC);
 #undef LOAD_FUNC
+        /* Optional symbols. These don't exist in all versions of JACK. */
+#define LOAD_SYM(f) p##f = GetSymbol(jack_handle, #f)
+        LOAD_SYM(jack_error_callback);
+#undef LOAD_SYM
 
         if(error)
         {
@@ -556,6 +562,7 @@ typedef struct ALCjackBackendFactory {
 
 static ALCboolean ALCjackBackendFactory_init(ALCjackBackendFactory* UNUSED(self))
 {
+    void (*old_error_cb)(const char*);
     jack_client_t *client;
     jack_status_t status;
 
@@ -565,9 +572,10 @@ static ALCboolean ALCjackBackendFactory_init(ALCjackBackendFactory* UNUSED(self)
     if(!GetConfigValueBool(NULL, "jack", "spawn-server", 0))
         ClientOptions |= JackNoStartServer;
 
+    old_error_cb = (&jack_error_callback ? jack_error_callback : NULL);
     jack_set_error_function(jack_msg_handler);
     client = jack_client_open("alsoft", ClientOptions, &status, NULL);
-    jack_set_error_function(NULL);
+    jack_set_error_function(old_error_cb);
     if(client == NULL)
     {
         WARN("jack_client_open() failed, 0x%02x\n", status);
diff --git a/Alc/backends/mmdevapi.c b/Alc/backends/mmdevapi.c
index 43d8429..9afb62f 100644
--- a/Alc/backends/mmdevapi.c
+++ b/Alc/backends/mmdevapi.c
@@ -65,6 +65,8 @@ DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_GUID, 0x1da5d803, 0xd492, 0x4edd, 0x8c, 0x
 #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 REFTIME_PER_SEC ((REFERENCE_TIME)10000000)
+
 #define DEVNAME_HEAD "OpenAL Soft on "
 
 
@@ -109,6 +111,15 @@ typedef struct {
 #define WM_USER_Enumerate   (WM_USER+5)
 #define WM_USER_Last        (WM_USER+5)
 
+static const char MessageStr[WM_USER_Last+1-WM_USER][20] = {
+    "Open Device",
+    "Reset Device",
+    "Start Device",
+    "Stop Device",
+    "Close Device",
+    "Enumerate Devices",
+};
+
 static inline void ReturnMsgResponse(ThreadRequest *req, HRESULT res)
 {
     req->result = res;
@@ -403,7 +414,11 @@ static DWORD CALLBACK ALCmmdevProxy_messageHandler(void *ptr)
     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);
+        TRACE("Got message \"%s\" (0x%04x, lparam=%p, wparam=%p)\n",
+            (msg.message >= WM_USER && msg.message <= WM_USER_Last) ?
+            MessageStr[msg.message-WM_USER] : "Unknown",
+            msg.message, (void*)msg.lParam, (void*)msg.wParam
+        );
         switch(msg.message)
         {
         case WM_USER_OpenDevice:
@@ -891,8 +906,8 @@ static HRESULT ALCmmdevPlayback_resetProxy(ALCmmdevPlayback *self)
     CoTaskMemFree(wfx);
     wfx = NULL;
 
-    buf_time = ((REFERENCE_TIME)device->UpdateSize*device->NumUpdates*10000000 +
-                                device->Frequency-1) / device->Frequency;
+    buf_time = ScaleCeil(device->UpdateSize*device->NumUpdates, REFTIME_PER_SEC,
+                         device->Frequency);
 
     if(!(device->Flags&DEVICE_FREQUENCY_REQUEST))
         device->Frequency = OutputType.Format.nSamplesPerSec;
@@ -1081,7 +1096,7 @@ static HRESULT ALCmmdevPlayback_resetProxy(ALCmmdevPlayback *self)
     hr = IAudioClient_GetDevicePeriod(self->client, &min_per, NULL);
     if(SUCCEEDED(hr))
     {
-        min_len = (UINT32)((min_per*device->Frequency + 10000000-1) / 10000000);
+        min_len = (UINT32)ScaleCeil(min_per, device->Frequency, REFTIME_PER_SEC);
         /* 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;
@@ -1600,8 +1615,12 @@ static HRESULT ALCmmdevCapture_resetProxy(ALCmmdevCapture *self)
     }
     self->client = ptr;
 
-    buf_time = ((REFERENCE_TIME)device->UpdateSize*device->NumUpdates*10000000 +
-                                device->Frequency-1) / device->Frequency;
+    buf_time = ScaleCeil(device->UpdateSize*device->NumUpdates, REFTIME_PER_SEC,
+                         device->Frequency);
+    // Make sure buffer is at least 100ms in size
+    buf_time = maxu64(buf_time, REFTIME_PER_SEC/10);
+    device->UpdateSize = (ALuint)ScaleCeil(buf_time, device->Frequency, REFTIME_PER_SEC) /
+                         device->NumUpdates;
 
     OutputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
     switch(device->FmtChans)
@@ -1741,9 +1760,10 @@ static HRESULT ALCmmdevCapture_resetProxy(ALCmmdevCapture *self)
                                                    device->FmtChans);
         if(!self->ChannelConv)
         {
-            ERR("Failed to create stereo-to-mono converter\n");
+            ERR("Failed to create %s stereo-to-mono converter\n", DevFmtTypeString(srcType));
             return E_FAIL;
         }
+        TRACE("Created %s stereo-to-mono converter\n", DevFmtTypeString(srcType));
         /* The channel converter always outputs float, so change the input type
          * for the resampler/type-converter.
          */
@@ -1755,9 +1775,10 @@ static HRESULT ALCmmdevCapture_resetProxy(ALCmmdevCapture *self)
                                                    device->FmtChans);
         if(!self->ChannelConv)
         {
-            ERR("Failed to create mono-to-stereo converter\n");
+            ERR("Failed to create %s mono-to-stereo converter\n", DevFmtTypeString(srcType));
             return E_FAIL;
         }
+        TRACE("Created %s mono-to-stereo converter\n", DevFmtTypeString(srcType));
         srcType = DevFmtFloat;
     }
 
@@ -1769,12 +1790,14 @@ static HRESULT ALCmmdevCapture_resetProxy(ALCmmdevCapture *self)
         );
         if(!self->SampleConv)
         {
-            ERR("Failed to create converter for format, dst: %s %s %uhz, src: %d-bit %luhz\n",
+            ERR("Failed to create converter for %s format, dst: %s %uhz, src: %s %luhz\n",
                 DevFmtChannelsString(device->FmtChans), DevFmtTypeString(device->FmtType),
-                device->Frequency, OutputType.Format.wBitsPerSample,
-                OutputType.Format.nSamplesPerSec);
+                device->Frequency, DevFmtTypeString(srcType), OutputType.Format.nSamplesPerSec);
             return E_FAIL;
         }
+        TRACE("Created converter for %s format, dst: %s %uhz, src: %s %luhz\n",
+              DevFmtChannelsString(device->FmtChans), DevFmtTypeString(device->FmtType),
+              device->Frequency, DevFmtTypeString(srcType), OutputType.Format.nSamplesPerSec);
     }
 
     hr = IAudioClient_Initialize(self->client,
diff --git a/Alc/backends/oss.c b/Alc/backends/oss.c
index 33ea55e..9c63d9d 100644
--- a/Alc/backends/oss.c
+++ b/Alc/backends/oss.c
@@ -163,12 +163,12 @@ static void ALCossListPopulate(struct oss_device *devlist, int type_flag)
 
     if((fd=open("/dev/mixer", O_RDONLY)) < 0)
     {
-        ERR("Could not open /dev/mixer\n");
+        TRACE("Could not open /dev/mixer: %s\n", strerror(errno));
         return;
     }
     if(ioctl(fd, SNDCTL_SYSINFO, &si) == -1)
     {
-        ERR("SNDCTL_SYSINFO failed: %s\n", strerror(errno));
+        TRACE("SNDCTL_SYSINFO failed: %s\n", strerror(errno));
         goto done;
     }
     for(i = 0;i < si.numaudios;i++)
@@ -821,7 +821,11 @@ void ALCossBackendFactory_probe(ALCossBackendFactory* UNUSED(self), enum DevProb
             cur = &oss_playback;
             while(cur != NULL)
             {
-                AppendAllDevicesList(cur->handle);
+#ifdef HAVE_STAT
+                struct stat buf;
+                if(stat(cur->path, &buf) == 0)
+#endif
+                    AppendAllDevicesList(cur->handle);
                 cur = cur->next;
             }
             break;
@@ -832,7 +836,11 @@ void ALCossBackendFactory_probe(ALCossBackendFactory* UNUSED(self), enum DevProb
             cur = &oss_capture;
             while(cur != NULL)
             {
-                AppendCaptureDeviceList(cur->handle);
+#ifdef HAVE_STAT
+                struct stat buf;
+                if(stat(cur->path, &buf) == 0)
+#endif
+                    AppendCaptureDeviceList(cur->handle);
                 cur = cur->next;
             }
             break;
diff --git a/Alc/backends/pulseaudio.c b/Alc/backends/pulseaudio.c
index 43761e2..8c5469b 100644
--- a/Alc/backends/pulseaudio.c
+++ b/Alc/backends/pulseaudio.c
@@ -817,7 +817,6 @@ static int ALCpulsePlayback_mixerProc(void *ptr)
 
     pa_threaded_mainloop_lock(self->loop);
     frame_size = pa_frame_size(&self->spec);
-    buffer_size = device->UpdateSize * device->NumUpdates * frame_size;
 
     while(!self->killNow && device->Connected)
     {
@@ -830,15 +829,17 @@ static int ALCpulsePlayback_mixerProc(void *ptr)
         }
 
         /* Make sure we're going to write at least 2 'periods' (minreqs), in
-         * case the server increased it since starting playback.
+         * case the server increased it since starting playback. Also round up
+         * the number of writable periods if it's not an integer count.
          */
-        buffer_size = maxu(buffer_size, self->attr.minreq*2);
+        buffer_size = maxu((self->attr.tlength + self->attr.minreq/2) / self->attr.minreq, 2) *
+                      self->attr.minreq;
 
         /* NOTE: This assumes pa_stream_writable_size returns between 0 and
          * tlength, else there will be more latency than intended.
          */
         len = mini(len - (ssize_t)self->attr.tlength, 0) + buffer_size;
-        if(len < self->attr.minreq)
+        if(len < (int32_t)self->attr.minreq)
         {
             if(pa_stream_is_corked(self->stream))
             {
@@ -968,7 +969,6 @@ static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self)
     const char *mapname = NULL;
     pa_channel_map chanmap;
     pa_operation *o;
-    ALuint len;
 
     pa_threaded_mainloop_lock(self->loop);
 
@@ -1107,11 +1107,10 @@ static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self)
     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->NumUpdates = (ALuint)clampu64(
+        (self->attr.tlength + self->attr.minreq/2) / self->attr.minreq, 2, 16
     );
-    device->UpdateSize = len;
+    device->UpdateSize = self->attr.minreq / pa_frame_size(&self->spec);
 
     /* 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
@@ -1121,7 +1120,7 @@ static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self)
      */
     if(self->attr.prebuf != 0)
     {
-        len = self->attr.prebuf / pa_frame_size(&self->spec);
+        ALuint 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);
diff --git a/Alc/backends/qsa.c b/Alc/backends/qsa.c
index 9da55b5..58193c9 100644
--- a/Alc/backends/qsa.c
+++ b/Alc/backends/qsa.c
@@ -159,10 +159,33 @@ static void deviceList(int type, vector_DevMap *devmap)
 }
 
 
-FORCE_ALIGN static int qsa_proc_playback(void* ptr)
+/* Wrappers to use an old-style backend with the new interface. */
+typedef struct PlaybackWrapper {
+    DERIVE_FROM_TYPE(ALCbackend);
+    qsa_data *ExtraData;
+} PlaybackWrapper;
+
+static void PlaybackWrapper_Construct(PlaybackWrapper *self, ALCdevice *device);
+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);
+
+
+FORCE_ALIGN static int qsa_proc_playback(void *ptr)
 {
-    ALCdevice* device=(ALCdevice*)ptr;
-    qsa_data* data=(qsa_data*)device->ExtraData;
+    PlaybackWrapper *self = ptr;
+    ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
+    qsa_data *data = self->ExtraData;
     snd_pcm_channel_status_t status;
     struct sched_param param;
     struct timeval timeout;
@@ -250,8 +273,9 @@ FORCE_ALIGN static int qsa_proc_playback(void* ptr)
 /* Playback */
 /************/
 
-static ALCenum qsa_open_playback(ALCdevice* device, const ALCchar* deviceName)
+static ALCenum qsa_open_playback(PlaybackWrapper *self, const ALCchar* deviceName)
 {
+    ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
     qsa_data *data;
     int card, dev;
     int status;
@@ -299,14 +323,14 @@ static ALCenum qsa_open_playback(ALCdevice* device, const ALCchar* deviceName)
     }
 
     alstr_copy_cstr(&device->DeviceName, deviceName);
-    device->ExtraData = data;
+    self->ExtraData = data;
 
     return ALC_NO_ERROR;
 }
 
-static void qsa_close_playback(ALCdevice* device)
+static void qsa_close_playback(PlaybackWrapper *self)
 {
-    qsa_data* data=(qsa_data*)device->ExtraData;
+    qsa_data *data = self->ExtraData;
 
     if (data->buffer!=NULL)
     {
@@ -317,12 +341,13 @@ static void qsa_close_playback(ALCdevice* device)
     snd_pcm_close(data->pcmHandle);
     free(data);
 
-    device->ExtraData=NULL;
+    self->ExtraData = NULL;
 }
 
-static ALCboolean qsa_reset_playback(ALCdevice* device)
+static ALCboolean qsa_reset_playback(PlaybackWrapper *self)
 {
-    qsa_data* data=(qsa_data*)device->ExtraData;
+    ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
+    qsa_data *data = self->ExtraData;
     int32_t format=-1;
 
     switch(device->FmtType)
@@ -567,20 +592,20 @@ static ALCboolean qsa_reset_playback(ALCdevice* device)
     return ALC_TRUE;
 }
 
-static ALCboolean qsa_start_playback(ALCdevice* device)
+static ALCboolean qsa_start_playback(PlaybackWrapper *self)
 {
-    qsa_data *data = (qsa_data*)device->ExtraData;
+    qsa_data *data = self->ExtraData;
 
     data->killNow = 0;
-    if(althrd_create(&data->thread, qsa_proc_playback, device) != althrd_success)
+    if(althrd_create(&data->thread, qsa_proc_playback, self) != althrd_success)
         return ALC_FALSE;
 
     return ALC_TRUE;
 }
 
-static void qsa_stop_playback(ALCdevice* device)
+static void qsa_stop_playback(PlaybackWrapper *self)
 {
-    qsa_data *data = (qsa_data*)device->ExtraData;
+    qsa_data *data = self->ExtraData;
     int res;
 
     if(data->killNow)
@@ -590,12 +615,70 @@ static void qsa_stop_playback(ALCdevice* device)
     althrd_join(data->thread, &res);
 }
 
+
+static void PlaybackWrapper_Construct(PlaybackWrapper *self, ALCdevice *device)
+{
+    ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
+    SET_VTABLE2(PlaybackWrapper, ALCbackend, self);
+
+    self->ExtraData = NULL;
+}
+
+static ALCenum PlaybackWrapper_open(PlaybackWrapper *self, const ALCchar *name)
+{
+    return qsa_open_playback(self, name);
+}
+
+static void PlaybackWrapper_close(PlaybackWrapper *self)
+{
+    qsa_close_playback(self);
+}
+
+static ALCboolean PlaybackWrapper_reset(PlaybackWrapper *self)
+{
+    return qsa_reset_playback(self);
+}
+
+static ALCboolean PlaybackWrapper_start(PlaybackWrapper *self)
+{
+    return qsa_start_playback(self);
+}
+
+static void PlaybackWrapper_stop(PlaybackWrapper *self)
+{
+    qsa_stop_playback(self);
+}
+
+
+
 /***********/
 /* Capture */
 /***********/
 
-static ALCenum qsa_open_capture(ALCdevice* device, const ALCchar* deviceName)
+typedef struct CaptureWrapper {
+    DERIVE_FROM_TYPE(ALCbackend);
+    qsa_data *ExtraData;
+} CaptureWrapper;
+
+static void CaptureWrapper_Construct(CaptureWrapper *self, ALCdevice *device);
+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 ALCenum qsa_open_capture(CaptureWrapper *self, const ALCchar *deviceName)
 {
+    ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
     qsa_data *data;
     int card, dev;
     int format=-1;
@@ -646,7 +729,7 @@ static ALCenum qsa_open_capture(ALCdevice* device, const ALCchar* deviceName)
     }
 
     alstr_copy_cstr(&device->DeviceName, deviceName);
-    device->ExtraData = data;
+    self->ExtraData = data;
 
     switch (device->FmtType)
     {
@@ -699,7 +782,6 @@ static ALCenum qsa_open_capture(ALCdevice* device, const ALCchar* deviceName)
     {
         snd_pcm_close(data->pcmHandle);
         free(data);
-        device->ExtraData=NULL;
 
         return ALC_INVALID_VALUE;
     }
@@ -707,20 +789,20 @@ static ALCenum qsa_open_capture(ALCdevice* device, const ALCchar* deviceName)
     return ALC_NO_ERROR;
 }
 
-static void qsa_close_capture(ALCdevice* device)
+static void qsa_close_capture(CaptureWrapper *self)
 {
-    qsa_data* data=(qsa_data*)device->ExtraData;
+    qsa_data *data = self->ExtraData;
 
     if (data->pcmHandle!=NULL)
         snd_pcm_close(data->pcmHandle);
 
     free(data);
-    device->ExtraData=NULL;
+    self->ExtraData = NULL;
 }
 
-static void qsa_start_capture(ALCdevice* device)
+static void qsa_start_capture(CaptureWrapper *self)
 {
-    qsa_data* data=(qsa_data*)device->ExtraData;
+    qsa_data *data = self->ExtraData;
     int rstatus;
 
     if ((rstatus=snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_CAPTURE))<0)
@@ -740,16 +822,16 @@ static void qsa_start_capture(ALCdevice* device)
     snd_pcm_capture_go(data->pcmHandle);
 }
 
-static void qsa_stop_capture(ALCdevice* device)
+static void qsa_stop_capture(CaptureWrapper *self)
 {
-    qsa_data* data=(qsa_data*)device->ExtraData;
-
+    qsa_data *data = self->ExtraData;
     snd_pcm_capture_flush(data->pcmHandle);
 }
 
-static ALCuint qsa_available_samples(ALCdevice* device)
+static ALCuint qsa_available_samples(CaptureWrapper *self)
 {
-    qsa_data* data=(qsa_data*)device->ExtraData;
+    ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
+    qsa_data *data = self->ExtraData;
     snd_pcm_channel_status_t status;
     ALint frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
     ALint free_size;
@@ -778,9 +860,10 @@ static ALCuint qsa_available_samples(ALCdevice* device)
     return free_size/frame_size;
 }
 
-static ALCenum qsa_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint samples)
+static ALCenum qsa_capture_samples(CaptureWrapper *self, ALCvoid *buffer, ALCuint samples)
 {
-    qsa_data* data=(qsa_data*)device->ExtraData;
+    ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
+    qsa_data *data = self->ExtraData;
     char* read_ptr;
     snd_pcm_channel_status_t status;
     fd_set rfds;
@@ -853,27 +936,65 @@ static ALCenum qsa_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint s
     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)
+static void CaptureWrapper_Construct(CaptureWrapper *self, ALCdevice *device)
+{
+    ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
+    SET_VTABLE2(CaptureWrapper, ALCbackend, self);
+
+    self->ExtraData = NULL;
+}
+
+static ALCenum CaptureWrapper_open(CaptureWrapper *self, const ALCchar *name)
+{
+    return qsa_open_capture(self, name);
+}
+
+static void CaptureWrapper_close(CaptureWrapper *self)
+{
+    qsa_close_capture(self);
+}
+
+static ALCboolean CaptureWrapper_start(CaptureWrapper *self)
+{
+    qsa_start_capture(self);
+    return ALC_TRUE;
+}
+
+static void CaptureWrapper_stop(CaptureWrapper *self)
+{
+    qsa_stop_capture(self);
+}
+
+static ALCenum CaptureWrapper_captureSamples(CaptureWrapper *self, void *buffer, ALCuint samples)
+{
+    return qsa_capture_samples(self, buffer, samples);
+}
+
+static ALCuint CaptureWrapper_availableSamples(CaptureWrapper *self)
+{
+    return qsa_available_samples(self);
+}
+
+
+typedef struct ALCqsaBackendFactory {
+    DERIVE_FROM_TYPE(ALCbackendFactory);
+} ALCqsaBackendFactory;
+#define ALCQSABACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCqsaBackendFactory, ALCbackendFactory) } }
+
+static ALCboolean ALCqsaBackendFactory_init(ALCqsaBackendFactory* UNUSED(self));
+static void ALCqsaBackendFactory_deinit(ALCqsaBackendFactory* UNUSED(self));
+static ALCboolean ALCqsaBackendFactory_querySupport(ALCqsaBackendFactory* UNUSED(self), ALCbackend_Type type);
+static void ALCqsaBackendFactory_probe(ALCqsaBackendFactory* UNUSED(self), enum DevProbe type);
+static ALCbackend* ALCqsaBackendFactory_createBackend(ALCqsaBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type);
+DEFINE_ALCBACKENDFACTORY_VTABLE(ALCqsaBackendFactory);
+
+static ALCboolean ALCqsaBackendFactory_init(ALCqsaBackendFactory* UNUSED(self))
 {
-    *func_list = qsa_funcs;
     return ALC_TRUE;
 }
 
-void alc_qsa_deinit(void)
+static void ALCqsaBackendFactory_deinit(ALCqsaBackendFactory* UNUSED(self))
 {
 #define FREE_NAME(iter) free((iter)->name)
     VECTOR_FOR_EACH(DevMap, DeviceNameMap, FREE_NAME);
@@ -884,7 +1005,14 @@ void alc_qsa_deinit(void)
 #undef FREE_NAME
 }
 
-void alc_qsa_probe(enum DevProbe type)
+static ALCboolean ALCqsaBackendFactory_querySupport(ALCqsaBackendFactory* UNUSED(self), ALCbackend_Type type)
+{
+    if(type == ALCbackend_Playback || type == ALCbackend_Capture)
+        return ALC_TRUE;
+    return ALC_FALSE;
+}
+
+static void ALCqsaBackendFactory_probe(ALCqsaBackendFactory* UNUSED(self), enum DevProbe type)
 {
     switch (type)
     {
@@ -913,3 +1041,29 @@ void alc_qsa_probe(enum DevProbe type)
             break;
     }
 }
+
+static ALCbackend* ALCqsaBackendFactory_createBackend(ALCqsaBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
+{
+    if(type == ALCbackend_Playback)
+    {
+        PlaybackWrapper *backend;
+        NEW_OBJ(backend, PlaybackWrapper)(device);
+        if(!backend) return NULL;
+        return STATIC_CAST(ALCbackend, backend);
+    }
+    if(type == ALCbackend_Capture)
+    {
+        CaptureWrapper *backend;
+        NEW_OBJ(backend, CaptureWrapper)(device);
+        if(!backend) return NULL;
+        return STATIC_CAST(ALCbackend, backend);
+    }
+
+    return NULL;
+}
+
+ALCbackendFactory *ALCqsaBackendFactory_getFactory(void)
+{
+    static ALCqsaBackendFactory factory = ALCQSABACKENDFACTORY_INITIALIZER;
+    return STATIC_CAST(ALCbackendFactory, &factory);
+}
diff --git a/Alc/backends/wave.c b/Alc/backends/wave.c
index c72a632..bdf76ed 100644
--- a/Alc/backends/wave.c
+++ b/Alc/backends/wave.c
@@ -320,8 +320,9 @@ static ALCboolean ALCwaveBackend_reset(ALCwaveBackend *self)
     // 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);
+    val = fwrite((device->FmtType == DevFmtFloat) ?
+                 (isbformat ? SUBTYPE_BFORMAT_FLOAT : SUBTYPE_FLOAT) :
+                 (isbformat ? SUBTYPE_BFORMAT_PCM : SUBTYPE_PCM), 1, 16, self->mFile);
     (void)val;
 
     fputs("data", self->mFile);
diff --git a/Alc/bformatdec.c b/Alc/bformatdec.c
index 92a2aec..ba6daac 100644
--- a/Alc/bformatdec.c
+++ b/Alc/bformatdec.c
@@ -75,6 +75,45 @@ void bandsplit_process(BandSplitter *splitter, ALfloat *restrict hpout, ALfloat
 }
 
 
+void splitterap_init(SplitterAllpass *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->z1 = 0.0f;
+}
+
+void splitterap_clear(SplitterAllpass *splitter)
+{
+    splitter->z1 = 0.0f;
+}
+
+void splitterap_process(SplitterAllpass *splitter, ALfloat *restrict samples, ALsizei count)
+{
+    ALfloat coeff, d, x;
+    ALfloat z1;
+    ALsizei i;
+
+    coeff = splitter->coeff;
+    z1 = splitter->z1;
+    for(i = 0;i < count;i++)
+    {
+        x = samples[i];
+
+        d = x - coeff*z1;
+        x = z1 + coeff*d;
+        z1 = d;
+
+        samples[i] = x;
+    }
+    splitter->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
diff --git a/Alc/bformatdec.h b/Alc/bformatdec.h
index 162f265..8f44fc2 100644
--- a/Alc/bformatdec.h
+++ b/Alc/bformatdec.h
@@ -60,4 +60,16 @@ void bandsplit_clear(BandSplitter *splitter);
 void bandsplit_process(BandSplitter *splitter, ALfloat *restrict hpout, ALfloat *restrict lpout,
                        const ALfloat *input, ALsizei count);
 
+/* The all-pass portion of the band splitter. Applies the same phase shift
+ * without splitting the signal.
+ */
+typedef struct SplitterAllpass {
+    ALfloat coeff;
+    ALfloat z1;
+} SplitterAllpass;
+
+void splitterap_init(SplitterAllpass *splitter, ALfloat freq_mult);
+void splitterap_clear(SplitterAllpass *splitter);
+void splitterap_process(SplitterAllpass *splitter, ALfloat *restrict samples, ALsizei count);
+
 #endif /* BFORMATDEC_H */
diff --git a/Alc/converter.c b/Alc/converter.c
index f1a3a96..5cfe703 100644
--- a/Alc/converter.c
+++ b/Alc/converter.c
@@ -9,6 +9,7 @@
 SampleConverter *CreateSampleConverter(enum DevFmtType srcType, enum DevFmtType dstType, ALsizei numchans, ALsizei srcRate, ALsizei dstRate)
 {
     SampleConverter *converter;
+    ALsizei step;
 
     if(numchans <= 0 || srcRate <= 0 || dstRate <= 0)
         return NULL;
@@ -21,17 +22,21 @@ SampleConverter *CreateSampleConverter(enum DevFmtType srcType, enum DevFmtType
     converter->mDstTypeSize = BytesFromDevFmt(dstType);
 
     converter->mSrcPrepCount = 0;
-
     converter->mFracOffset = 0;
-    converter->mIncrement = (ALsizei)clampu64((ALuint64)srcRate*FRACTIONONE/dstRate,
-                                              1, MAX_PITCH*FRACTIONONE);
+
+    /* Have to set the mixer FPU mode since that's what the resampler code expects. */
+    START_MIXER_MODE();
+    step = fastf2i(minf((ALdouble)srcRate / dstRate, MAX_PITCH)*FRACTIONONE + 0.5f);
+    converter->mIncrement = maxi(step, 1);
     if(converter->mIncrement == FRACTIONONE)
         converter->mResample = Resample_copy32_C;
     else
     {
         /* TODO: Allow other resamplers. */
-        converter->mResample = SelectResampler(LinearResampler);
+        BsincPrepare(converter->mIncrement, &converter->mState.bsinc);
+        converter->mResample = SelectResampler(BSincResampler);
     }
+    END_MIXER_MODE();
 
     return converter;
 }
@@ -59,7 +64,7 @@ static inline ALfloat Sample_ALushort(ALushort val)
 static inline ALfloat Sample_ALint(ALint val)
 { return (val>>7) * (1.0f/16777216.0f); }
 static inline ALfloat Sample_ALuint(ALuint val)
-{ return ((ALint)(val>>7) - 16777216) * (1.0f/16777216.0f); }
+{ return Sample_ALint(val - INT_MAX - 1); }
 
 static inline ALfloat Sample_ALfloat(ALfloat val)
 { return val; }
@@ -113,17 +118,17 @@ static void LoadSamples(ALfloat *dst, const ALvoid *src, ALint srcstep, enum Dev
 
 
 static inline ALbyte ALbyte_Sample(ALfloat val)
-{ return (ALbyte)clampf(val*128.0f, -128.0f, 127.0f); }
+{ return fastf2i(clampf(val*128.0f, -128.0f, 127.0f)); }
 static inline ALubyte ALubyte_Sample(ALfloat val)
 { return ALbyte_Sample(val)+128; }
 
 static inline ALshort ALshort_Sample(ALfloat val)
-{ return (ALshort)clampf(val*32768.0f, -32768.0f, 32767.0f); }
+{ return fastf2i(clampf(val*32768.0f, -32768.0f, 32767.0f)); }
 static inline ALushort ALushort_Sample(ALfloat val)
 { return ALshort_Sample(val)+32768; }
 
 static inline ALint ALint_Sample(ALfloat val)
-{ return (ALint)clampf(val*16777216.0f, -16777216.0f, 16777215.0f) << 7; }
+{ return fastf2i(clampf(val*16777216.0f, -16777216.0f, 16777215.0f)) << 7; }
 static inline ALuint ALuint_Sample(ALfloat val)
 { return ALint_Sample(val)+INT_MAX+1; }
 
@@ -194,6 +199,12 @@ ALsizei SampleConverterAvailableOut(SampleConverter *converter, ALsizei srcframe
         prepcount = 0;
     }
 
+    if(srcframes < 1)
+    {
+        /* No output samples if there's no input samples. */
+        return 0;
+    }
+
     if(prepcount < MAX_POST_SAMPLES+MAX_PRE_SAMPLES &&
        MAX_POST_SAMPLES+MAX_PRE_SAMPLES-prepcount >= srcframes)
     {
@@ -208,7 +219,7 @@ ALsizei SampleConverterAvailableOut(SampleConverter *converter, ALsizei srcframe
     DataSize64 -= DataPosFrac;
 
     /* If we have a full prep, we can generate at least one sample. */
-    return (ALsizei)clampu64(DataSize64/increment, 1, INT_MAX);
+    return (ALsizei)clampu64((DataSize64 + increment-1)/increment, 1, BUFFERSIZE);
 }
 
 
@@ -219,7 +230,8 @@ ALsizei SampleConverterInput(SampleConverter *converter, const ALvoid **src, ALs
     const ALsizei increment = converter->mIncrement;
     ALsizei pos = 0;
 
-    while(pos < dstframes)
+    START_MIXER_MODE();
+    while(pos < dstframes && *srcframes > 0)
     {
         ALfloat *restrict SrcData = ASSUME_ALIGNED(converter->mSrcSamples, 16);
         ALfloat *restrict DstData = ASSUME_ALIGNED(converter->mDstSamples, 16);
@@ -241,7 +253,8 @@ ALsizei SampleConverterInput(SampleConverter *converter, const ALvoid **src, ALs
             }
             *src = (const ALbyte*)*src + SrcFrameSize*-prepcount;
             *srcframes += prepcount;
-            prepcount = 0;
+            converter->mSrcPrepCount = 0;
+            continue;
         }
         toread = mini(*srcframes, BUFFERSIZE-(MAX_POST_SAMPLES+MAX_PRE_SAMPLES));
 
@@ -253,7 +266,7 @@ ALsizei SampleConverterInput(SampleConverter *converter, const ALvoid **src, ALs
              */
             for(chan = 0;chan < converter->mNumChannels;chan++)
                 LoadSamples(&converter->Chan[chan].mPrevSamples[prepcount],
-                    (const ALbyte*)src + converter->mSrcTypeSize*chan,
+                    (const ALbyte*)*src + converter->mSrcTypeSize*chan,
                     converter->mNumChannels, converter->mSrcType, toread
                 );
 
@@ -269,13 +282,13 @@ ALsizei SampleConverterInput(SampleConverter *converter, const ALvoid **src, ALs
         DataSize64 -= DataPosFrac;
 
         /* If we have a full prep, we can generate at least one sample. */
-        DstSize = (ALsizei)clampu64(DataSize64/increment, 1, BUFFERSIZE);
+        DstSize = (ALsizei)clampu64((DataSize64 + increment-1)/increment, 1, BUFFERSIZE);
         DstSize = mini(DstSize, dstframes-pos);
 
         for(chan = 0;chan < converter->mNumChannels;chan++)
         {
             const ALbyte *SrcSamples = (const ALbyte*)*src + converter->mSrcTypeSize*chan;
-            ALbyte *DstSamples = (ALbyte*)dst + converter->mSrcTypeSize*chan;
+            ALbyte *DstSamples = (ALbyte*)dst + converter->mDstTypeSize*chan;
             const ALfloat *ResampledData;
             ALsizei SrcDataEnd;
 
@@ -294,7 +307,7 @@ ALsizei SampleConverterInput(SampleConverter *converter, const ALvoid **src, ALs
             SrcDataEnd = (DataPosFrac + increment*DstSize)>>FRACTIONBITS;
             if(SrcDataEnd >= prepcount+toread)
                 memset(converter->Chan[chan].mPrevSamples, 0,
-                    sizeof(converter->Chan[chan].mPrevSamples));
+                       sizeof(converter->Chan[chan].mPrevSamples));
             else
             {
                 size_t len = mini(MAX_PRE_SAMPLES+MAX_POST_SAMPLES, prepcount+toread-SrcDataEnd);
@@ -305,7 +318,7 @@ ALsizei SampleConverterInput(SampleConverter *converter, const ALvoid **src, ALs
             }
 
             /* Now resample, and store the result in the output buffer. */
-            ResampledData = converter->mResample(NULL,
+            ResampledData = converter->mResample(&converter->mState,
                 SrcData+MAX_PRE_SAMPLES, DataPosFrac, increment,
                 DstData, DstSize
             );
@@ -329,6 +342,7 @@ ALsizei SampleConverterInput(SampleConverter *converter, const ALvoid **src, ALs
         dst = (ALbyte*)dst + DstFrameSize*DstSize;
         pos += DstSize;
     }
+    END_MIXER_MODE();
 
     return pos;
 }
diff --git a/Alc/converter.h b/Alc/converter.h
index db3f1da..9934fa4 100644
--- a/Alc/converter.h
+++ b/Alc/converter.h
@@ -19,6 +19,7 @@ typedef struct SampleConverter {
 
     ALsizei mFracOffset;
     ALsizei mIncrement;
+    InterpState mState;
     ResamplerFunc mResample;
 
     alignas(16) ALfloat mSrcSamples[BUFFERSIZE];
diff --git a/Alc/effects/chorus.c b/Alc/effects/chorus.c
index 62f2e53..f4383aa 100644
--- a/Alc/effects/chorus.c
+++ b/Alc/effects/chorus.c
@@ -91,7 +91,7 @@ static ALboolean ALchorusState_deviceUpdate(ALchorusState *state, ALCdevice *Dev
     ALsizei maxlen;
     ALsizei it;
 
-    maxlen = fastf2u(AL_CHORUS_MAX_DELAY * 2.0f * Device->Frequency) + 1;
+    maxlen = fastf2i(AL_CHORUS_MAX_DELAY * 2.0f * Device->Frequency) + 1;
     maxlen = NextPowerOf2(maxlen);
 
     if(maxlen != state->BufferLength)
@@ -172,107 +172,99 @@ static ALvoid ALchorusState_update(ALchorusState *state, const ALCdevice *Device
     }
 }
 
-static inline void Triangle(ALint *delay_left, ALint *delay_right, ALuint offset, const ALchorusState *state)
+static void GetTriangleDelays(ALint *restrict delays, ALsizei offset, const ALsizei lfo_range,
+                              const ALfloat lfo_scale, const ALfloat depth, const ALsizei delay,
+                              const ALsizei todo)
 {
-    ALfloat lfo_value;
-
-    lfo_value = 1.0f - fabsf(2.0f - state->lfo_scale*(offset%state->lfo_range));
-    lfo_value *= state->depth;
-    *delay_left = fastf2i(lfo_value) + state->delay;
-
-    offset += state->lfo_disp;
-    lfo_value = 1.0f - fabsf(2.0f - state->lfo_scale*(offset%state->lfo_range));
-    lfo_value *= state->depth;
-    *delay_right = fastf2i(lfo_value) + state->delay;
+    ALsizei i;
+    for(i = 0;i < todo;i++)
+    {
+        delays[i] = fastf2i((1.0f - fabsf(2.0f - lfo_scale*offset)) * depth) + delay;
+        offset = (offset+1)%lfo_range;
+    }
 }
 
-static inline void Sinusoid(ALint *delay_left, ALint *delay_right, ALuint offset, const ALchorusState *state)
+static void GetSinusoidDelays(ALint *restrict delays, ALsizei offset, const ALsizei lfo_range,
+                              const ALfloat lfo_scale, const ALfloat depth, const ALsizei delay,
+                              const ALsizei todo)
 {
-    ALfloat lfo_value;
-
-    lfo_value = sinf(state->lfo_scale*(offset%state->lfo_range));
-    lfo_value *= state->depth;
-    *delay_left = fastf2i(lfo_value) + state->delay;
-
-    offset += state->lfo_disp;
-    lfo_value = sinf(state->lfo_scale*(offset%state->lfo_range));
-    lfo_value *= state->depth;
-    *delay_right = fastf2i(lfo_value) + state->delay;
-}
-
-#define DECL_TEMPLATE(Func)                                                   \
-static void Process##Func(ALchorusState *state, const ALsizei SamplesToDo,    \
-  const ALfloat *restrict SamplesIn, ALfloat (*restrict out)[2])              \
-{                                                                             \
-    const ALsizei bufmask = state->BufferLength-1;                            \
-    ALfloat *restrict leftbuf = state->SampleBuffer[0];                       \
-    ALfloat *restrict rightbuf = state->SampleBuffer[1];                      \
-    ALsizei offset = state->offset;                                           \
-    const ALfloat feedback = state->feedback;                                 \
-    ALsizei it;                                                               \
-                                                                              \
-    for(it = 0;it < SamplesToDo;it++)                                         \
-    {                                                                         \
-        ALint delay_left, delay_right;                                        \
-        Func(&delay_left, &delay_right, offset, state);                       \
-                                                                              \
-        leftbuf[offset&bufmask] = SamplesIn[it];                              \
-        out[it][0] = leftbuf[(offset-delay_left)&bufmask];                    \
-        leftbuf[offset&bufmask] += out[it][0] * feedback;                     \
-                                                                              \
-        rightbuf[offset&bufmask] = SamplesIn[it];                             \
-        out[it][1] = rightbuf[(offset-delay_right)&bufmask];                  \
-        rightbuf[offset&bufmask] += out[it][1] * feedback;                    \
-                                                                              \
-        offset++;                                                             \
-    }                                                                         \
-    state->offset = offset;                                                   \
+    ALsizei i;
+    for(i = 0;i < todo;i++)
+    {
+        delays[i] = fastf2i(sinf(lfo_scale*offset) * depth) + delay;
+        offset = (offset+1)%lfo_range;
+    }
 }
 
-DECL_TEMPLATE(Triangle)
-DECL_TEMPLATE(Sinusoid)
-
-#undef DECL_TEMPLATE
 
 static ALvoid ALchorusState_process(ALchorusState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
 {
-    ALsizei it, kt;
+    ALfloat *restrict leftbuf = state->SampleBuffer[0];
+    ALfloat *restrict rightbuf = state->SampleBuffer[1];
+    const ALsizei bufmask = state->BufferLength-1;
+    const ALfloat feedback = state->feedback;
+    ALsizei offset = state->offset;
+    ALsizei i, c;
     ALsizei base;
 
     for(base = 0;base < SamplesToDo;)
     {
+        const ALsizei todo = mini(128, SamplesToDo-base);
         ALfloat temps[128][2];
-        ALsizei td = mini(128, SamplesToDo-base);
+        ALint moddelays[2][128];
 
         switch(state->waveform)
         {
             case CWF_Triangle:
-                ProcessTriangle(state, td, SamplesIn[0]+base, temps);
+                GetTriangleDelays(moddelays[0], offset%state->lfo_range, state->lfo_range,
+                                  state->lfo_scale, state->depth, state->delay, todo);
+                GetTriangleDelays(moddelays[1], (offset+state->lfo_disp)%state->lfo_range,
+                                  state->lfo_range, state->lfo_scale, state->depth, state->delay,
+                                  todo);
                 break;
             case CWF_Sinusoid:
-                ProcessSinusoid(state, td, SamplesIn[0]+base, temps);
+                GetSinusoidDelays(moddelays[0], offset%state->lfo_range, state->lfo_range,
+                                  state->lfo_scale, state->depth, state->delay, todo);
+                GetSinusoidDelays(moddelays[1], (offset+state->lfo_disp)%state->lfo_range,
+                                  state->lfo_range, state->lfo_scale, state->depth, state->delay,
+                                  todo);
                 break;
         }
 
-        for(kt = 0;kt < NumChannels;kt++)
+        for(i = 0;i < todo;i++)
+        {
+            leftbuf[offset&bufmask] = SamplesIn[0][base+i];
+            temps[i][0] = leftbuf[(offset-moddelays[0][i])&bufmask] * feedback;
+            leftbuf[offset&bufmask] += temps[i][0];
+
+            rightbuf[offset&bufmask] = SamplesIn[0][base+i];
+            temps[i][1] = rightbuf[(offset-moddelays[1][i])&bufmask] * feedback;
+            rightbuf[offset&bufmask] += temps[i][1];
+
+            offset++;
+        }
+
+        for(c = 0;c < NumChannels;c++)
         {
-            ALfloat gain = state->Gain[0][kt];
+            ALfloat gain = state->Gain[0][c];
             if(fabsf(gain) > GAIN_SILENCE_THRESHOLD)
             {
-                for(it = 0;it < td;it++)
-                    SamplesOut[kt][it+base] += temps[it][0] * gain;
+                for(i = 0;i < todo;i++)
+                    SamplesOut[c][i+base] += temps[i][0] * gain;
             }
 
-            gain = state->Gain[1][kt];
+            gain = state->Gain[1][c];
             if(fabsf(gain) > GAIN_SILENCE_THRESHOLD)
             {
-                for(it = 0;it < td;it++)
-                    SamplesOut[kt][it+base] += temps[it][1] * gain;
+                for(i = 0;i < todo;i++)
+                    SamplesOut[c][i+base] += temps[i][1] * gain;
             }
         }
 
-        base += td;
+        base += todo;
     }
+
+    state->offset = offset;
 }
 
 
diff --git a/Alc/effects/flanger.c b/Alc/effects/flanger.c
index d330511..b25b95c 100644
--- a/Alc/effects/flanger.c
+++ b/Alc/effects/flanger.c
@@ -172,107 +172,98 @@ static ALvoid ALflangerState_update(ALflangerState *state, const ALCdevice *Devi
     }
 }
 
-static inline void Triangle(ALint *delay_left, ALint *delay_right, ALuint offset, const ALflangerState *state)
+static void GetTriangleDelays(ALint *restrict delays, ALsizei offset, const ALsizei lfo_range,
+                              const ALfloat lfo_scale, const ALfloat depth, const ALsizei delay,
+                              const ALsizei todo)
 {
-    ALfloat lfo_value;
-
-    lfo_value = 1.0f - fabsf(2.0f - state->lfo_scale*(offset%state->lfo_range));
-    lfo_value *= state->depth;
-    *delay_left = fastf2i(lfo_value) + state->delay;
-
-    offset += state->lfo_disp;
-    lfo_value = 1.0f - fabsf(2.0f - state->lfo_scale*(offset%state->lfo_range));
-    lfo_value *= state->depth;
-    *delay_right = fastf2i(lfo_value) + state->delay;
+    ALsizei i;
+    for(i = 0;i < todo;i++)
+    {
+        delays[i] = fastf2i((1.0f - fabsf(2.0f - lfo_scale*offset)) * depth) + delay;
+        offset = (offset+1)%lfo_range;
+    }
 }
 
-static inline void Sinusoid(ALint *delay_left, ALint *delay_right, ALuint offset, const ALflangerState *state)
+static void GetSinusoidDelays(ALint *restrict delays, ALsizei offset, const ALsizei lfo_range,
+                              const ALfloat lfo_scale, const ALfloat depth, const ALsizei delay,
+                              const ALsizei todo)
 {
-    ALfloat lfo_value;
-
-    lfo_value = sinf(state->lfo_scale*(offset%state->lfo_range));
-    lfo_value *= state->depth;
-    *delay_left = fastf2i(lfo_value) + state->delay;
-
-    offset += state->lfo_disp;
-    lfo_value = sinf(state->lfo_scale*(offset%state->lfo_range));
-    lfo_value *= state->depth;
-    *delay_right = fastf2i(lfo_value) + state->delay;
-}
-
-#define DECL_TEMPLATE(Func)                                                   \
-static void Process##Func(ALflangerState *state, const ALsizei SamplesToDo,   \
-  const ALfloat *restrict SamplesIn, ALfloat (*restrict out)[2])              \
-{                                                                             \
-    const ALsizei bufmask = state->BufferLength-1;                            \
-    ALfloat *restrict leftbuf = state->SampleBuffer[0];                       \
-    ALfloat *restrict rightbuf = state->SampleBuffer[1];                      \
-    ALsizei offset = state->offset;                                           \
-    const ALfloat feedback = state->feedback;                                 \
-    ALsizei it;                                                               \
-                                                                              \
-    for(it = 0;it < SamplesToDo;it++)                                         \
-    {                                                                         \
-        ALint delay_left, delay_right;                                        \
-        Func(&delay_left, &delay_right, offset, state);                       \
-                                                                              \
-        leftbuf[offset&bufmask] = SamplesIn[it];                              \
-        out[it][0] = leftbuf[(offset-delay_left)&bufmask];                    \
-        leftbuf[offset&bufmask] += out[it][0] * feedback;                     \
-                                                                              \
-        rightbuf[offset&bufmask] = SamplesIn[it];                             \
-        out[it][1] = rightbuf[(offset-delay_right)&bufmask];                  \
-        rightbuf[offset&bufmask] += out[it][1] * feedback;                    \
-                                                                              \
-        offset++;                                                             \
-    }                                                                         \
-    state->offset = offset;                                                   \
+    ALsizei i;
+    for(i = 0;i < todo;i++)
+    {
+        delays[i] = fastf2i(sinf(lfo_scale*offset) * depth) + delay;
+        offset = (offset+1)%lfo_range;
+    }
 }
 
-DECL_TEMPLATE(Triangle)
-DECL_TEMPLATE(Sinusoid)
-
-#undef DECL_TEMPLATE
-
 static ALvoid ALflangerState_process(ALflangerState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
 {
-    ALsizei it, kt;
+    ALfloat *restrict leftbuf = state->SampleBuffer[0];
+    ALfloat *restrict rightbuf = state->SampleBuffer[1];
+    const ALsizei bufmask = state->BufferLength-1;
+    const ALfloat feedback = state->feedback;
+    ALsizei offset = state->offset;
+    ALsizei i, c;
     ALsizei base;
 
     for(base = 0;base < SamplesToDo;)
     {
+        const ALsizei todo = mini(128, SamplesToDo-base);
         ALfloat temps[128][2];
-        ALsizei td = mini(128, SamplesToDo-base);
+        ALint moddelays[2][128];
 
         switch(state->waveform)
         {
             case FWF_Triangle:
-                ProcessTriangle(state, td, SamplesIn[0]+base, temps);
+                GetTriangleDelays(moddelays[0], offset%state->lfo_range, state->lfo_range,
+                                  state->lfo_scale, state->depth, state->delay, todo);
+                GetTriangleDelays(moddelays[1], (offset+state->lfo_disp)%state->lfo_range,
+                                  state->lfo_range, state->lfo_scale, state->depth, state->delay,
+                                  todo);
                 break;
             case FWF_Sinusoid:
-                ProcessSinusoid(state, td, SamplesIn[0]+base, temps);
+                GetSinusoidDelays(moddelays[0], offset%state->lfo_range, state->lfo_range,
+                                  state->lfo_scale, state->depth, state->delay, todo);
+                GetSinusoidDelays(moddelays[1], (offset+state->lfo_disp)%state->lfo_range,
+                                  state->lfo_range, state->lfo_scale, state->depth, state->delay,
+                                  todo);
                 break;
         }
 
-        for(kt = 0;kt < NumChannels;kt++)
+        for(i = 0;i < todo;i++)
         {
-            ALfloat gain = state->Gain[0][kt];
+            leftbuf[offset&bufmask] = SamplesIn[0][base+i];
+            temps[i][0] = leftbuf[(offset-moddelays[0][i])&bufmask] * feedback;
+            leftbuf[offset&bufmask] += temps[i][0];
+
+            rightbuf[offset&bufmask] = SamplesIn[0][base+i];
+            temps[i][1] = rightbuf[(offset-moddelays[1][i])&bufmask] * feedback;
+            rightbuf[offset&bufmask] += temps[i][1];
+
+            offset++;
+        }
+
+        for(c = 0;c < NumChannels;c++)
+        {
+            ALfloat gain = state->Gain[0][c];
             if(fabsf(gain) > GAIN_SILENCE_THRESHOLD)
             {
-                for(it = 0;it < td;it++)
-                    SamplesOut[kt][it+base] += temps[it][0] * gain;
+                for(i = 0;i < todo;i++)
+                    SamplesOut[c][i+base] += temps[i][0] * gain;
             }
 
-            gain = state->Gain[1][kt];
+            gain = state->Gain[1][c];
             if(fabsf(gain) > GAIN_SILENCE_THRESHOLD)
             {
-                for(it = 0;it < td;it++)
-                    SamplesOut[kt][it+base] += temps[it][1] * gain;
+                for(i = 0;i < todo;i++)
+                    SamplesOut[c][i+base] += temps[i][1] * gain;
             }
         }
 
-        base += td;
+        base += todo;
     }
+
+    state->offset = offset;
 }
 
 
diff --git a/Alc/effects/modulator.c b/Alc/effects/modulator.c
index e9026a6..5de88fc 100644
--- a/Alc/effects/modulator.c
+++ b/Alc/effects/modulator.c
@@ -127,7 +127,7 @@ static ALvoid ALmodulatorState_update(ALmodulatorState *state, const ALCdevice *
     else /*if(Slot->Params.EffectProps.Modulator.Waveform == AL_RING_MODULATOR_SQUARE)*/
         state->Process = ModulateSquare;
 
-    state->step = fastf2u(props->Modulator.Frequency*WAVEFORM_FRACONE /
+    state->step = fastf2i(props->Modulator.Frequency*WAVEFORM_FRACONE /
                           Device->Frequency);
     if(state->step == 0) state->step = 1;
 
diff --git a/Alc/effects/reverb.c b/Alc/effects/reverb.c
index f5d32d9..867abbc 100644
--- a/Alc/effects/reverb.c
+++ b/Alc/effects/reverb.c
@@ -331,21 +331,6 @@ ALfloat ReverbBoost = 1.0f;
  */
 ALboolean EmulateEAXReverb = AL_FALSE;
 
-/* This coefficient is used to define the sinus depth according to the
- * modulation depth property. This value must be below 1, which would cause the
- * sampler to stall on the downswing, and above 1 it will cause it to sample
- * backwards.
- */
-static const ALfloat MODULATION_DEPTH_COEFF = 1.0f / 2048.0f;
-
-/* 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;
-
 /* The all-pass and delay lines have a variable length dependent on the
  * effect's density parameter.  The resulting density multiplier is:
  *
@@ -473,20 +458,33 @@ static const ALfloat LATE_LINE_LENGTHS[4] =
     9.709681e-3f, 1.223343e-2f, 1.689561e-2f, 1.941936e-2f
 };
 
-/* HACK: Workaround for a modff bug in 32-bit Windows, which attempts to write
- * a 64-bit double to the 32-bit float parameter.
+/* This coefficient is used to define the sinus depth according to the
+ * modulation depth property. This value must be below half the shortest late
+ * line length (0.0097/2 = ~0.0048), otherwise with certain parameters (high
+ * mod time, low density) the downswing can sample before the input.
  */
-#if defined(_WIN32) && !defined (_M_X64) && !defined(_M_ARM)
-static inline float hack_modff(float x, float *y)
+static const ALfloat MODULATION_DEPTH_COEFF = 1.0f / 4096.0f;
+
+/* 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;
+
+
+/* Prior to VS2013, MSVC lacks the round() family of functions. */
+#if defined(_MSC_VER) && _MSC_VER < 1800
+static inline long lroundf(float val)
 {
-    double di;
-    double df = modf((double)x, &di);
-    *y = (float)di;
-    return (float)df;
+    if(val < 0.0)
+        return fastf2i(ceilf(val-0.5f));
+    return fastf2i(floorf(val+0.5f));
 }
-#define modff hack_modff
 #endif
 
+
 /**************************************
  *  Device Update                     *
  **************************************/
@@ -513,7 +511,7 @@ static ALuint CalcLineLength(const ALfloat length, const ptrdiff_t offset, const
     /* All line lengths are powers of 2, calculated from their lengths in
      * seconds, rounded up.
      */
-    samples = fastf2u(ceilf(length*frequency));
+    samples = fastf2i(ceilf(length*frequency));
     samples = NextPowerOf2(samples + extra);
 
     /* All lines share a single sample buffer. */
@@ -629,7 +627,7 @@ static ALboolean ALreverbState_deviceUpdate(ALreverbState *State, ALCdevice *Dev
 
     /* The late feed taps are set a fixed position past the latest delay tap. */
     for(i = 0;i < 4;i++)
-        State->LateFeedTap = fastf2u((AL_EAXREVERB_MAX_REFLECTIONS_DELAY +
+        State->LateFeedTap = fastf2i((AL_EAXREVERB_MAX_REFLECTIONS_DELAY +
                                       EARLY_TAP_LENGTHS[3]*multiplier) *
                                      frequency);
 
@@ -752,7 +750,7 @@ static inline void CalcHighpassCoeffs(const ALfloat gain, const ALfloat w, ALflo
     g = maxf(0.001f, gain);
     g2 = g * g;
     cw = cosf(w);
-    p = g / (g*cw + sqrt((cw - 1.0f) * (g2*cw + g2 - 2.0f)));
+    p = g / (g*cw + sqrtf((cw - 1.0f) * (g2*cw + g2 - 2.0f)));
 
     coeffs[0] = p;
     coeffs[1] = -p;
@@ -1057,7 +1055,7 @@ static ALvoid UpdateModulator(const ALfloat modTime, const ALfloat modDepth,
      * (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);
+    range = maxi(fastf2i(modTime*frequency), 1);
     State->Mod.Index = (ALuint)(State->Mod.Index * (ALuint64)range /
                                 State->Mod.Range);
     State->Mod.Range = range;
@@ -1067,12 +1065,12 @@ static ALvoid UpdateModulator(const ALfloat modTime, const ALfloat modDepth,
      * time changes the pitch, creating the modulation effect. The scale needs
      * to be multiplied by the modulation time so that a given depth produces a
      * consistent shift in frequency over all ranges of time. Since the depth
-     * is applied to a sinus value, it needs to be halved once for the sinus
-     * range (-1...+1 to 0...1) and again for the sinus swing in time (half of
-     * it is spent decreasing the frequency, half is spent increasing it).
+     * is applied to a sinus value, it needs to be halved 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;
+    State->Mod.Depth = modDepth * MODULATION_DEPTH_COEFF * modTime / 2.0f *
+                       frequency;
 }
 
 /* Update the offsets for the main effect delay line. */
@@ -1096,13 +1094,13 @@ static ALvoid UpdateDelayLine(const ALfloat earlyDelay, const ALfloat lateDelay,
     for(i = 0;i < 4;i++)
     {
         length = earlyDelay + EARLY_TAP_LENGTHS[i]*multiplier;
-        State->EarlyDelayTap[i][1] = fastf2u(length * frequency);
+        State->EarlyDelayTap[i][1] = fastf2i(length * frequency);
 
         length = EARLY_TAP_LENGTHS[i]*multiplier;
         State->EarlyDelayCoeff[i] = CalcDecayCoeff(length, decayTime);
 
         length = lateDelay + (LATE_LINE_LENGTHS[i] - LATE_LINE_LENGTHS[0])*0.25f*multiplier;
-        State->LateDelayTap[i][1] = State->LateFeedTap + fastf2u(length * frequency);
+        State->LateDelayTap[i][1] = State->LateFeedTap + fastf2i(length * frequency);
     }
 }
 
@@ -1126,7 +1124,7 @@ static ALvoid UpdateEarlyLines(const ALfloat density, const ALfloat decayTime, c
         length = EARLY_LINE_LENGTHS[i] * multiplier;
 
         /* Calculate the delay offset for each delay line. */
-        State->Early.Offset[i][1] = fastf2u(length * frequency);
+        State->Early.Offset[i][1] = fastf2i(length * frequency);
 
         /* Calculate the gain (coefficient) for each line. */
         State->Early.Coeff[i] = CalcDecayCoeff(length, decayTime);
@@ -1183,7 +1181,7 @@ static ALvoid UpdateLateLines(const ALfloat density, const ALfloat diffusion, co
         length = lerp(LATE_LINE_LENGTHS[i] * multiplier, echoTime, echoDepth);
 
         /* Calculate the delay offset for each delay line. */
-        State->Late.Offset[i][1] = fastf2u(length * frequency);
+        State->Late.Offset[i][1] = fastf2i(length * frequency);
 
         /* Approximate the absorption that the vector all-pass would exhibit
          * given the current diffusion so we don't have to process a full T60
@@ -1446,7 +1444,7 @@ static inline ALvoid DelayLineIn4Rev(DelayLineI *Delay, ALsizei offset, const AL
         Delay->Line[offset][i] = in[3-i];
 }
 
-static void CalcModulationDelays(ALreverbState *State, ALfloat *restrict delays, const ALsizei todo)
+static void CalcModulationDelays(ALreverbState *State, ALint *restrict delays, const ALsizei todo)
 {
     ALfloat sinus, range;
     ALsizei index, i;
@@ -1456,10 +1454,9 @@ static void CalcModulationDelays(ALreverbState *State, ALfloat *restrict delays,
     for(i = 0;i < todo;i++)
     {
         /* Calculate the sinus rhythm (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.
+         * sampling rate).
          */
-        sinus = 1.0f - cosf(F_TAU * index / State->Mod.Range);
+        sinus = sinf(F_TAU * index / State->Mod.Range);
 
         /* Step the modulation index forward, keeping it bound to its range. */
         index = (index+1) % State->Mod.Range;
@@ -1470,8 +1467,8 @@ static void CalcModulationDelays(ALreverbState *State, ALfloat *restrict delays,
          */
         range = lerp(range, State->Mod.Depth, State->Mod.Coeff);
 
-        /* Calculate the read offset with fraction. */
-        delays[i] = range*sinus;
+        /* Calculate the read offset. */
+        delays[i] = lroundf(range*sinus);
     }
     State->Mod.Index = index;
     State->Mod.Filter = range;
@@ -1686,14 +1683,13 @@ static ALvoid LateReverb_##T(ALreverbState *State, const ALsizei todo,        \
     const ALfloat apFeedCoeff = State->ApFeedCoeff;                           \
     const ALfloat mixX = State->MixX;                                         \
     const ALfloat mixY = State->MixY;                                         \
-    ALfloat fdelay, frac;                                                     \
+    ALint moddelay[MAX_UPDATE_SAMPLES];                                       \
     ALsizei delay;                                                            \
     ALsizei offset;                                                           \
     ALsizei i, j;                                                             \
     ALfloat f[4];                                                             \
                                                                               \
-    /* Calculations modulation delays, uing the output as temp storage. */    \
-    CalcModulationDelays(State, &out[0][0], todo);                            \
+    CalcModulationDelays(State, moddelay, todo);                              \
                                                                               \
     offset = State->Offset;                                                   \
     for(i = 0;i < todo;i++)                                                   \
@@ -1704,31 +1700,12 @@ static ALvoid LateReverb_##T(ALreverbState *State, const ALsizei todo,        \
                 offset-State->LateDelayTap[j][1], j, fade                     \
             ) * State->Late.DensityGain;                                      \
                                                                               \
-        /* Separate the integer offset and fraction between it and the next   \
-         * sample.                                                            \
-         */                                                                   \
-        frac = modff(out[0][i], &fdelay);                                     \
-        delay = offset - fastf2i(fdelay);                                     \
-                                                                              \
+        delay = offset - moddelay[i];                                         \
         for(j = 0;j < 4;j++)                                                  \
-        {                                                                     \
-            ALfloat out0, out1;                                               \
-                                                                              \
-            /* Get the two samples crossed by the offset delay. */            \
-            out0 = DELAY_OUT_##T(&State->Late.Delay,                          \
+            f[j] += DELAY_OUT_##T(&State->Late.Delay,                         \
                 delay-State->Late.Offset[j][0],                               \
                 delay-State->Late.Offset[j][1], j, fade                       \
             );                                                                \
-            out1 = DELAY_OUT_##T(&State->Late.Delay,                          \
-                delay-State->Late.Offset[j][0]-1,                             \
-                delay-State->Late.Offset[j][1]-1, j, fade                     \
-            );                                                                \
-                                                                              \
-            /* The modulated result is obtained by linearly interpolating the \
-             * two samples that were acquired above.                          \
-             */                                                               \
-            f[j] += lerp(out0, out1, frac);                                   \
-        }                                                                     \
                                                                               \
         for(j = 0;j < 4;j++)                                                  \
             f[j] = LateT60Filter(j, f[j], State);                             \
diff --git a/Alc/helpers.c b/Alc/helpers.c
index 8e685c7..7f6349b 100644
--- a/Alc/helpers.c
+++ b/Alc/helpers.c
@@ -40,6 +40,11 @@
 #include <dirent.h>
 #endif
 
+#ifdef __FreeBSD__
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#endif
+
 #ifndef AL_NO_UID_DEFS
 #if defined(HAVE_GUIDDEF_H) || defined(HAVE_INITGUID_H)
 #define INITGUID
@@ -113,8 +118,10 @@ DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_GUID, 0x1da5d803, 0xd492, 0x4edd, 0x8c, 0x
 
 extern inline ALuint NextPowerOf2(ALuint value);
 extern inline size_t RoundUp(size_t value, size_t r);
+extern inline ALuint64 ScaleRound(ALuint64 val, ALuint64 new_scale, ALuint64 old_scale);
+extern inline ALuint64 ScaleFloor(ALuint64 val, ALuint64 new_scale, ALuint64 old_scale);
+extern inline ALuint64 ScaleCeil(ALuint64 val, ALuint64 new_scale, ALuint64 old_scale);
 extern inline ALint fastf2i(ALfloat f);
-extern inline ALuint fastf2u(ALfloat f);
 
 
 ALuint CPUCapFlags = 0;
@@ -289,6 +296,13 @@ void SetMixerFPUMode(FPUCtl *ctl)
 {
 #ifdef HAVE_FENV_H
     fegetenv(STATIC_CAST(fenv_t, ctl));
+#ifdef _WIN32
+    /* HACK: A nasty bug in MinGW-W64 causes fegetenv and fesetenv to not save
+     * and restore the FPU rounding mode, so we have to do it manually. Don't
+     * know if this also applies to MSVC.
+     */
+    ctl->round_mode = fegetround();
+#endif
 #if defined(__GNUC__) && defined(HAVE_SSE)
     /* FIXME: Some fegetenv implementations can get the SSE environment too?
      * How to tell when it does? */
@@ -335,6 +349,9 @@ void RestoreFPUMode(const FPUCtl *ctl)
 {
 #ifdef HAVE_FENV_H
     fesetenv(STATIC_CAST(fenv_t, ctl));
+#ifdef _WIN32
+    fesetround(ctl->round_mode);
+#endif
 #if defined(__GNUC__) && defined(HAVE_SSE)
     if((CPUCapFlags&CPU_CAP_SSE))
         __asm__ __volatile__("ldmxcsr %0" : : "m" (*&ctl->sse_state));
@@ -707,9 +724,22 @@ void UnmapFileMem(const struct FileMapping *mapping)
 al_string GetProcPath(void)
 {
     al_string ret = AL_STRING_INIT_STATIC();
-    const char *fname;
     char *pathname, *sep;
     size_t pathlen;
+
+#ifdef __FreeBSD__
+    int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
+    mib[3] = getpid();
+    if (sysctl(mib, 4, NULL, &pathlen, NULL, 0) == -1) {
+        WARN("Failed to sysctl kern.proc.pathname.%d: %s\n", mib[3], strerror(errno));
+        return ret;
+    }
+
+    pathname = malloc(pathlen + 1);
+    sysctl(mib, 4, (void*)pathname, &pathlen, NULL, 0);
+    pathname[pathlen] = 0;
+#else
+    const char *fname;
     ssize_t len;
 
     pathlen = 256;
@@ -738,6 +768,8 @@ al_string GetProcPath(void)
     }
 
     pathname[len] = 0;
+#endif
+
     sep = strrchr(pathname, '/');
     if(sep)
         alstr_copy_range(&ret, pathname, sep);
diff --git a/Alc/hrtf.c b/Alc/hrtf.c
index 3017113..d996815 100644
--- a/Alc/hrtf.c
+++ b/Alc/hrtf.c
@@ -37,7 +37,7 @@
 
 /* Current data set limits defined by the makehrtf utility. */
 #define MIN_IR_SIZE                  (8)
-#define MAX_IR_SIZE                  (128)
+#define MAX_IR_SIZE                  (512)
 #define MOD_IR_SIZE                  (8)
 
 #define MIN_EV_COUNT                 (5)
@@ -284,8 +284,10 @@ ALsizei BuildBFormatHrtf(const struct Hrtf *Hrtf, DirectHrtfState *state, ALsize
             }
         }
     }
-    TRACE("Skipped min delay: %d, new combined length: %d\n", min_delay, max_length);
+    /* Round up to the next IR size multiple. */
+    max_length = RoundUp(max_length, MOD_IR_SIZE);
 
+    TRACE("Skipped min delay: %d, new combined length: %d\n", min_delay, max_length);
     return max_length;
 #undef NUM_BANDS
 }
@@ -360,6 +362,41 @@ static struct Hrtf *CreateHrtfStore(ALuint rate, ALsizei irSize, ALsizei evCount
     return Hrtf;
 }
 
+static ALubyte GetLE_ALubyte(const ALubyte **data, size_t *len)
+{
+    ALubyte ret = (*data)[0];
+    *data += 1; *len -= 1;
+    return ret;
+}
+
+static ALshort GetLE_ALshort(const ALubyte **data, size_t *len)
+{
+    ALshort ret = (*data)[0] | ((*data)[1]<<8);
+    *data += 2; *len -= 2;
+    return ret;
+}
+
+static ALushort GetLE_ALushort(const ALubyte **data, size_t *len)
+{
+    ALushort ret = (*data)[0] | ((*data)[1]<<8);
+    *data += 2; *len -= 2;
+    return ret;
+}
+
+static ALint GetLE_ALuint(const ALubyte **data, size_t *len)
+{
+    ALint ret = (*data)[0] | ((*data)[1]<<8) | ((*data)[2]<<16) | ((*data)[3]<<24);
+    *data += 4; *len -= 4;
+    return ret;
+}
+
+static const ALubyte *Get_ALubytePtr(const ALubyte **data, size_t *len, size_t size)
+{
+    const ALubyte *ret = *data;
+    *data += size; *len -= size;
+    return ret;
+}
+
 static struct Hrtf *LoadHrtf00(const ALubyte *data, size_t datalen, const char *filename)
 {
     const ALubyte maxDelay = HRTF_HISTORY_LENGTH-1;
@@ -381,22 +418,13 @@ static struct Hrtf *LoadHrtf00(const ALubyte *data, size_t datalen, const char *
         return NULL;
     }
 
-    rate  = *(data++);
-    rate |= *(data++)<<8;
-    rate |= *(data++)<<16;
-    rate |= *(data++)<<24;
-    datalen -= 4;
+    rate = GetLE_ALuint(&data, &datalen);
 
-    irCount  = *(data++);
-    irCount |= *(data++)<<8;
-    datalen -= 2;
+    irCount = GetLE_ALushort(&data, &datalen);
 
-    irSize  = *(data++);
-    irSize |= *(data++)<<8;
-    datalen -= 2;
+    irSize = GetLE_ALushort(&data, &datalen);
 
-    evCount = *(data++);
-    datalen -= 1;
+    evCount = GetLE_ALubyte(&data, &datalen);
 
     if(irSize < MIN_IR_SIZE || irSize > MAX_IR_SIZE || (irSize%MOD_IR_SIZE))
     {
@@ -429,14 +457,10 @@ static struct Hrtf *LoadHrtf00(const ALubyte *data, size_t datalen, const char *
 
     if(!failed)
     {
-        evOffset[0]  = *(data++);
-        evOffset[0] |= *(data++)<<8;
-        datalen -= 2;
+        evOffset[0] = GetLE_ALushort(&data, &datalen);
         for(i = 1;i < evCount;i++)
         {
-            evOffset[i]  = *(data++);
-            evOffset[i] |= *(data++)<<8;
-            datalen -= 2;
+            evOffset[i] = GetLE_ALushort(&data, &datalen);
             if(evOffset[i] <= evOffset[i-1])
             {
                 ERR("Invalid evOffset: evOffset[%d]=%d (last=%d)\n",
@@ -492,22 +516,15 @@ static struct Hrtf *LoadHrtf00(const ALubyte *data, size_t datalen, const char *
 
     if(!failed)
     {
-        for(i = 0;i < irCount*irSize;i+=irSize)
+        for(i = 0;i < irCount;i++)
         {
             for(j = 0;j < irSize;j++)
-            {
-                ALshort coeff;
-                coeff  = *(data++);
-                coeff |= *(data++)<<8;
-                datalen -= 2;
-                coeffs[i+j][0] = coeff / 32768.0f;
-            }
+                coeffs[i*irSize + j][0] = GetLE_ALshort(&data, &datalen) / 32768.0f;
         }
 
         for(i = 0;i < irCount;i++)
         {
-            delays[i][0] = *(data++);
-            datalen -= 1;
+            delays[i][0] = GetLE_ALubyte(&data, &datalen);
             if(delays[i][0] > maxDelay)
             {
                 ERR("Invalid delays[%d]: %d (%d)\n", i, delays[i][0], maxDelay);
@@ -567,17 +584,11 @@ static struct Hrtf *LoadHrtf01(const ALubyte *data, size_t datalen, const char *
         return NULL;
     }
 
-    rate  = *(data++);
-    rate |= *(data++)<<8;
-    rate |= *(data++)<<16;
-    rate |= *(data++)<<24;
-    datalen -= 4;
+    rate = GetLE_ALuint(&data, &datalen);
 
-    irSize = *(data++);
-    datalen -= 1;
+    irSize = GetLE_ALubyte(&data, &datalen);
 
-    evCount = *(data++);
-    datalen -= 1;
+    evCount = GetLE_ALubyte(&data, &datalen);
 
     if(irSize < MIN_IR_SIZE || irSize > MAX_IR_SIZE || (irSize%MOD_IR_SIZE))
     {
@@ -600,9 +611,7 @@ static struct Hrtf *LoadHrtf01(const ALubyte *data, size_t datalen, const char *
         return NULL;
     }
 
-    azCount = data;
-    data += evCount;
-    datalen -= evCount;
+    azCount = Get_ALubytePtr(&data, &datalen, evCount);
 
     evOffset = malloc(sizeof(evOffset[0])*evCount);
     if(azCount == NULL || evOffset == NULL)
@@ -656,22 +665,15 @@ static struct Hrtf *LoadHrtf01(const ALubyte *data, size_t datalen, const char *
 
     if(!failed)
     {
-        for(i = 0;i < irCount*irSize;i+=irSize)
+        for(i = 0;i < irCount;i++)
         {
             for(j = 0;j < irSize;j++)
-            {
-                ALshort coeff;
-                coeff  = *(data++);
-                coeff |= *(data++)<<8;
-                datalen -= 2;
-                coeffs[i+j][0] = coeff / 32768.0f;
-            }
+                coeffs[i*irSize + j][0] = GetLE_ALshort(&data, &datalen) / 32768.0f;
         }
 
         for(i = 0;i < irCount;i++)
         {
-            delays[i][0] = *(data++);
-            datalen -= 1;
+            delays[i][0] = GetLE_ALubyte(&data, &datalen);
             if(delays[i][0] > maxDelay)
             {
                 ERR("Invalid delays[%d]: %d (%d)\n", i, delays[i][0], maxDelay);
diff --git a/Alc/hrtf.h b/Alc/hrtf.h
index 186ba3b..5de28f9 100644
--- a/Alc/hrtf.h
+++ b/Alc/hrtf.h
@@ -9,10 +9,6 @@
 #include "atomic.h"
 
 
-#define HRTFDELAY_BITS    (20)
-#define HRTFDELAY_FRACONE (1<<HRTFDELAY_BITS)
-#define HRTFDELAY_MASK    (HRTFDELAY_FRACONE-1)
-
 /* The maximum number of virtual speakers used to generate HRTF coefficients
  * for decoding B-Format.
  */
diff --git a/Alc/mastering.c b/Alc/mastering.c
index c9f5beb..9de5fd5 100644
--- a/Alc/mastering.c
+++ b/Alc/mastering.c
@@ -89,7 +89,7 @@ static void RmsDetection(Compressor *Comp, const ALsizei SamplesToDo)
         ALfloat sig = Comp->Envelope[i];
 
         sum -= window[index];
-        window[index] = fastf2u(minf(sig * sig * 65536.0f, RMS_VALUE_MAX));
+        window[index] = fastf2i(minf(sig * sig * 65536.0f, RMS_VALUE_MAX));
         sum += window[index];
         index = (index + 1) & RMS_WINDOW_MASK;
 
diff --git a/Alc/mixer.c b/Alc/mixer.c
index 56d6520..b5c4605 100644
--- a/Alc/mixer.c
+++ b/Alc/mixer.c
@@ -471,13 +471,8 @@ ALboolean MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALsizei
                         );
                     else
                     {
-                        static void (*const NfcUpdate[MAX_AMBI_ORDER])(
-                            NfcFilter*,float*,const float*,const int
-                        ) = {
-                            NfcFilterUpdate1, NfcFilterUpdate2, NfcFilterUpdate3
-                        };
                         ALfloat *nfcsamples = Device->NFCtrlData;
-                        ALsizei ord, chanoffset = 0;
+                        ALsizei chanoffset = 0;
 
                         MixSamples(samples,
                             voice->Direct.ChannelsPerOrder[0], voice->Direct.Buffer,
@@ -485,18 +480,21 @@ ALboolean MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALsizei
                             DstBufferSize
                         );
                         chanoffset += voice->Direct.ChannelsPerOrder[0];
-                        for(ord = 1;ord < MAX_AMBI_ORDER+1;ord++)
-                        {
-                            if(voice->Direct.ChannelsPerOrder[ord] <= 0)
-                                break;
-                            NfcUpdate[ord-1](&parms->NFCtrlFilter[ord-1], nfcsamples, samples,
-                                             DstBufferSize);
-                            MixSamples(nfcsamples, voice->Direct.ChannelsPerOrder[ord],
-                                voice->Direct.Buffer+chanoffset, parms->Gains.Current+chanoffset,
-                                parms->Gains.Target+chanoffset, Counter, OutPos, DstBufferSize
-                            );
-                            chanoffset += voice->Direct.ChannelsPerOrder[ord];
-                        }
+#define APPLY_NFC_MIX(order)                                                  \
+    if(voice->Direct.ChannelsPerOrder[order] > 0)                             \
+    {                                                                         \
+        NfcFilterUpdate##order(&parms->NFCtrlFilter[order-1], nfcsamples,     \
+                               samples, DstBufferSize);                       \
+        MixSamples(nfcsamples, voice->Direct.ChannelsPerOrder[order],         \
+            voice->Direct.Buffer+chanoffset, parms->Gains.Current+chanoffset, \
+            parms->Gains.Target+chanoffset, Counter, OutPos, DstBufferSize    \
+        );                                                                    \
+        chanoffset += voice->Direct.ChannelsPerOrder[order];                  \
+    }
+                        APPLY_NFC_MIX(1)
+                        APPLY_NFC_MIX(2)
+                        APPLY_NFC_MIX(3)
+#undef APPLY_NFC_MIX
                     }
                 }
                 else
diff --git a/Alc/panning.c b/Alc/panning.c
index c00421c..0abf18d 100644
--- a/Alc/panning.c
+++ b/Alc/panning.c
@@ -152,8 +152,7 @@ void CalcDirectionCoeffs(const ALfloat dir[3], ALfloat spread, ALfloat coeffs[MA
          * 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:
+         * loundness doesn't depend on the spread. Thus:
          *
          * ZH0 = 1.0f;
          * ZH1 = 0.5f * (ca+1.0f);
@@ -163,11 +162,13 @@ void CalcDirectionCoeffs(const ALfloat dir[3], ALfloat spread, ALfloat coeffs[MA
          * ZH5 = 0.0625f * (ca+1.0f)*(21.0f*ca*ca*ca*ca - 14.0f*ca*ca + 1.0f);
          */
         ALfloat ca = cosf(spread * 0.5f);
+        /* Increase the source volume by up to +3dB for a full spread. */
+        ALfloat scale = sqrtf(1.0f + spread/F_TAU);
 
-        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);
+        ALfloat ZH0_norm = scale;
+        ALfloat ZH1_norm = 0.5f * (ca+1.f) * scale;
+        ALfloat ZH2_norm = 0.5f * (ca+1.f)*ca * scale;
+        ALfloat ZH3_norm = 0.125f * (ca+1.f)*(5.f*ca*ca-1.f) * scale;
 
         /* Zeroth-order */
         coeffs[0]  *= ZH0_norm;
@@ -963,9 +964,6 @@ static void InitHrtfPanning(ALCdevice *device)
         device->Hrtf, device->Dry.NumChannels,
         AmbiPoints, AmbiMatrix, COUNTOF(AmbiPoints)
     );
-
-    /* Round up to the nearest multiple of 8 */
-    device->Hrtf->IrSize = (device->Hrtf->IrSize+7)&~7;
 }
 
 static void InitUhjPanning(ALCdevice *device)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6fcdb96..a871f4c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -65,6 +65,8 @@ endif()
 IF(WIN32)
     ADD_DEFINITIONS("-D_WIN32 -D_WIN32_WINNT=0x0502")
 
+    OPTION(ALSOFT_BUILD_ROUTER  "Build the router (EXPERIMENTAL; creates OpenAL32.dll and soft_oal.dll)"  OFF)
+
     # 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.
@@ -98,7 +100,7 @@ ENDIF()
 
 SET(LIB_MAJOR_VERSION "1")
 SET(LIB_MINOR_VERSION "18")
-SET(LIB_REVISION "0")
+SET(LIB_REVISION "2")
 SET(LIB_VERSION "${LIB_MAJOR_VERSION}.${LIB_MINOR_VERSION}.${LIB_REVISION}")
 
 SET(EXPORT_DECL "")
@@ -417,27 +419,26 @@ 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()
+
+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()
 
 CHECK_C_SOURCE_COMPILES("int foo(const char *str, ...) __attribute__((format(printf, 1, 2)));
@@ -501,6 +502,7 @@ 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)
+CHECK_SYMBOL_EXISTS(log2f  math.h HAVE_LOG2F)
 IF(NOT HAVE_C99_VLA)
     CHECK_SYMBOL_EXISTS(alloca malloc.h HAVE_ALLOCA)
     IF(NOT HAVE_ALLOCA)
@@ -621,6 +623,16 @@ int main()
 }"
                 PTHREAD_SETNAME_NP_ONE_PARAM
             )
+            CHECK_C_SOURCE_COMPILES("
+#include <pthread.h>
+#include <pthread_np.h>
+int main()
+{
+    pthread_setname_np(pthread_self(), \"%s\", \"testname\");
+    return 0;
+}"
+                PTHREAD_SETNAME_NP_THREE_PARAMS
+            )
         ENDIF()
         CHECK_SYMBOL_EXISTS(pthread_mutexattr_setkind_np "pthread.h;pthread_np.h" HAVE_PTHREAD_MUTEXATTR_SETKIND_NP)
     ELSE()
@@ -637,6 +649,15 @@ int main()
 }"
                 PTHREAD_SETNAME_NP_ONE_PARAM
             )
+            CHECK_C_SOURCE_COMPILES("
+#include <pthread.h>
+int main()
+{
+    pthread_setname_np(pthread_self(), \"%s\", \"testname\");
+    return 0;
+}"
+                PTHREAD_SETNAME_NP_THREE_PARAMS
+            )
         ENDIF()
         CHECK_SYMBOL_EXISTS(pthread_mutexattr_setkind_np pthread.h HAVE_PTHREAD_MUTEXATTR_SETKIND_NP)
     ENDIF()
@@ -882,6 +903,9 @@ IF(OSS_FOUND)
         SET(HAVE_OSS 1)
         SET(BACKENDS  "${BACKENDS} OSS,")
         SET(ALC_OBJS  ${ALC_OBJS} Alc/backends/oss.c)
+        IF(OSS_LIBRARIES)
+            SET(EXTRA_LIBS ${OSS_LIBRARIES} ${EXTRA_LIBS})
+        ENDIF()
     ENDIF()
 ENDIF()
 IF(ALSOFT_REQUIRE_OSS AND NOT HAVE_OSS)
@@ -1192,91 +1216,111 @@ CONFIGURE_FILE(
     "${OpenAL_BINARY_DIR}/openal.pc"
     @ONLY)
 
+MACRO(ADD_INCLUDE_DIRS TRGT TESTVAR INCVAR)
+    IF(${TESTVAR})
+        SET_PROPERTY(TARGET ${TRGT} APPEND PROPERTY INCLUDE_DIRECTORIES ${${INCVAR}})
+    ENDIF()
+ENDMACRO()
+
+UNSET(HAS_ROUTER)
+SET(IMPL_TARGET OpenAL)
+
 # Build main library
 IF(LIBTYPE STREQUAL "STATIC")
     ADD_LIBRARY(OpenAL STATIC ${COMMON_OBJS} ${OPENAL_OBJS} ${ALC_OBJS})
 ELSE()
-    ADD_LIBRARY(OpenAL SHARED ${COMMON_OBJS} ${OPENAL_OBJS} ${ALC_OBJS})
+    IF(WIN32 AND ALSOFT_BUILD_ROUTER)
+        ADD_LIBRARY(OpenAL SHARED router/router.c router/alc.c router/al.c ${COMMON_OBJS})
+        SET_PROPERTY(TARGET OpenAL APPEND PROPERTY COMPILE_FLAGS ${EXTRA_CFLAGS})
+        SET_PROPERTY(TARGET OpenAL APPEND PROPERTY
+            COMPILE_DEFINITIONS AL_BUILD_LIBRARY AL_ALEXT_PROTOTYPES
+        )
+        SET_PROPERTY(TARGET OpenAL APPEND_STRING PROPERTY LINK_FLAGS ${EXTRA_LDFLAGS})
+        IF(MSVC)
+            SET_PROPERTY(TARGET OpenAL APPEND_STRING PROPERTY LINK_FLAGS " /SUBSYSTEM:WINDOWS")
+        ELSEIF(CMAKE_COMPILER_IS_GNUCC)
+            SET_PROPERTY(TARGET OpenAL APPEND_STRING PROPERTY LINK_FLAGS " -mwindows")
+        ENDIF()
+        SET_TARGET_PROPERTIES(OpenAL PROPERTIES PREFIX "")
+        SET_TARGET_PROPERTIES(OpenAL PROPERTIES OUTPUT_NAME ${LIBNAME})
+        IF(TARGET build_version)
+            ADD_DEPENDENCIES(OpenAL build_version)
+        ENDIF()
+        SET(HAS_ROUTER 1)
+
+        SET(LIBNAME "soft_oal")
+        SET(IMPL_TARGET soft_oal)
+    ENDIF()
+
+    ADD_LIBRARY(${IMPL_TARGET} SHARED ${COMMON_OBJS} ${OPENAL_OBJS} ${ALC_OBJS})
+    IF(WIN32)
+        SET_TARGET_PROPERTIES(${IMPL_TARGET} PROPERTIES PREFIX "")
+    ENDIF()
 ENDIF()
-SET_PROPERTY(TARGET OpenAL APPEND PROPERTY COMPILE_FLAGS ${EXTRA_CFLAGS})
-SET_PROPERTY(TARGET OpenAL APPEND PROPERTY COMPILE_DEFINITIONS AL_BUILD_LIBRARY AL_ALEXT_PROTOTYPES)
+SET_TARGET_PROPERTIES(${IMPL_TARGET} PROPERTIES OUTPUT_NAME ${LIBNAME}
+    VERSION ${LIB_VERSION}
+    SOVERSION ${LIB_MAJOR_VERSION}
+)
+SET_PROPERTY(TARGET ${IMPL_TARGET} APPEND PROPERTY INCLUDE_DIRECTORIES
+    "${OpenAL_SOURCE_DIR}/OpenAL32/Include" "${OpenAL_SOURCE_DIR}/Alc"
+)
+ADD_INCLUDE_DIRS(${IMPL_TARGET} HAVE_ALSA       ALSA_INCLUDE_DIRS)
+ADD_INCLUDE_DIRS(${IMPL_TARGET} HAVE_OSS        OSS_INCLUDE_DIRS)
+ADD_INCLUDE_DIRS(${IMPL_TARGET} HAVE_SOLARIS    AUDIOIO_INCLUDE_DIRS)
+ADD_INCLUDE_DIRS(${IMPL_TARGET} HAVE_SNDIO      SOUNDIO_INCLUDE_DIRS)
+ADD_INCLUDE_DIRS(${IMPL_TARGET} HAVE_QSA        QSA_INCLUDE_DIRS)
+ADD_INCLUDE_DIRS(${IMPL_TARGET} HAVE_DSOUND     DSOUND_INCLUDE_DIRS)
+ADD_INCLUDE_DIRS(${IMPL_TARGET} HAVE_PORTAUDIO  PORTAUDIO_INCLUDE_DIRS)
+ADD_INCLUDE_DIRS(${IMPL_TARGET} HAVE_PULSEAUDIO PULSEAUDIO_INCLUDE_DIRS)
+ADD_INCLUDE_DIRS(${IMPL_TARGET} HAVE_JACK       JACK_INCLUDE_DIRS)
+SET_PROPERTY(TARGET ${IMPL_TARGET} APPEND PROPERTY
+    COMPILE_DEFINITIONS AL_BUILD_LIBRARY AL_ALEXT_PROTOTYPES
+)
 IF(WIN32 AND ALSOFT_NO_UID_DEFS)
-    SET_PROPERTY(TARGET OpenAL APPEND PROPERTY COMPILE_DEFINITIONS AL_NO_UID_DEFS)
+    SET_PROPERTY(TARGET ${IMPL_TARGET} APPEND PROPERTY COMPILE_DEFINITIONS AL_NO_UID_DEFS)
 ENDIF()
-SET_PROPERTY(TARGET OpenAL APPEND PROPERTY INCLUDE_DIRECTORIES "${OpenAL_SOURCE_DIR}/OpenAL32/Include" "${OpenAL_SOURCE_DIR}/Alc")
-IF(HAVE_ALSA)
-    SET_PROPERTY(TARGET OpenAL APPEND PROPERTY INCLUDE_DIRECTORIES ${ALSA_INCLUDE_DIRS})
-ENDIF()
-IF(HAVE_OSS)
-    SET_PROPERTY(TARGET OpenAL APPEND PROPERTY INCLUDE_DIRECTORIES ${OSS_INCLUDE_DIRS})
-ENDIF()
-IF(HAVE_SOLARIS)
-    SET_PROPERTY(TARGET OpenAL APPEND PROPERTY INCLUDE_DIRECTORIES ${AUDIOIO_INCLUDE_DIRS})
-ENDIF()
-IF(HAVE_SNDIO)
-    SET_PROPERTY(TARGET OpenAL APPEND PROPERTY INCLUDE_DIRECTORIES ${SOUNDIO_INCLUDE_DIRS})
-ENDIF()
-IF(HAVE_QSA)
-    SET_PROPERTY(TARGET OpenAL APPEND PROPERTY INCLUDE_DIRECTORIES ${QSA_INCLUDE_DIRS})
-ENDIF()
-IF(HAVE_DSOUND)
-    SET_PROPERTY(TARGET OpenAL APPEND PROPERTY INCLUDE_DIRECTORIES ${DSOUND_INCLUDE_DIRS})
-ENDIF()
-IF(HAVE_PORTAUDIO)
-    SET_PROPERTY(TARGET OpenAL APPEND PROPERTY INCLUDE_DIRECTORIES ${PORTAUDIO_INCLUDE_DIRS})
-ENDIF()
-IF(HAVE_PULSEAUDIO)
-    SET_PROPERTY(TARGET OpenAL APPEND PROPERTY INCLUDE_DIRECTORIES ${PULSEAUDIO_INCLUDE_DIRS})
-ENDIF()
-IF(HAVE_JACK)
-    SET_PROPERTY(TARGET OpenAL APPEND PROPERTY INCLUDE_DIRECTORIES ${JACK_INCLUDE_DIRS})
+SET_PROPERTY(TARGET ${IMPL_TARGET} APPEND PROPERTY COMPILE_FLAGS ${EXTRA_CFLAGS})
+SET_PROPERTY(TARGET ${IMPL_TARGET} APPEND_STRING PROPERTY LINK_FLAGS ${EXTRA_LDFLAGS})
+
+TARGET_LINK_LIBRARIES(${IMPL_TARGET} ${EXTRA_LIBS})
+IF(TARGET build_version)
+    ADD_DEPENDENCIES(${IMPL_TARGET} build_version)
 ENDIF()
+
 IF(WIN32)
     IF(MSVC)
-        SET_PROPERTY(TARGET OpenAL APPEND_STRING PROPERTY LINK_FLAGS " /SUBSYSTEM:WINDOWS")
+        SET_PROPERTY(TARGET ${IMPL_TARGET} APPEND_STRING PROPERTY LINK_FLAGS " /SUBSYSTEM:WINDOWS")
     ELSEIF(CMAKE_COMPILER_IS_GNUCC)
-        SET_PROPERTY(TARGET OpenAL APPEND_STRING PROPERTY LINK_FLAGS " -mwindows")
-    ENDIF()
-ENDIF()
-
-SET_TARGET_PROPERTIES(OpenAL PROPERTIES VERSION ${LIB_VERSION}
-                                        SOVERSION ${LIB_MAJOR_VERSION})
-SET_TARGET_PROPERTIES(OpenAL PROPERTIES OUTPUT_NAME ${LIBNAME})
-
-if(WIN32 AND NOT LIBTYPE STREQUAL "STATIC")
-    SET_TARGET_PROPERTIES(OpenAL 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")
+        SET_PROPERTY(TARGET ${IMPL_TARGET} APPEND_STRING PROPERTY LINK_FLAGS " -mwindows")
+    ENDIF()
+
+    if(NOT LIBTYPE STREQUAL "STATIC")
+        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_PROPERTY(TARGET OpenAL APPEND_STRING PROPERTY LINK_FLAGS
+                             " -Wl,--output-def,OpenAL32.def")
+                ADD_CUSTOM_COMMAND(TARGET OpenAL POST_BUILD
+                    COMMAND "${SED_EXECUTABLE}" -i -e "s/ @[^ ]*//" OpenAL32.def
+                    COMMAND "${DLLTOOL_EXECUTABLE}" -d OpenAL32.def -l OpenAL32.lib -D OpenAL32.dll
+                    COMMENT "Stripping ordinals from OpenAL32.def and generating OpenAL32.lib..."
+                    VERBATIM
+                )
             ENDIF()
-        ELSE()
-            SET(EXTRA_LDFLAGS "${EXTRA_LDFLAGS} -Wl,--output-def,OpenAL32.def")
-            ADD_CUSTOM_COMMAND(TARGET OpenAL POST_BUILD
-                COMMAND "${SED_EXECUTABLE}" -i -e "s/ @[^ ]*//" OpenAL32.def
-                COMMAND "${DLLTOOL_EXECUTABLE}" -d OpenAL32.def -l OpenAL32.lib -D OpenAL32.dll
-                COMMENT "Stripping ordinals from OpenAL32.def and generating OpenAL32.lib..."
-                VERBATIM
-            )
         ENDIF()
     ENDIF()
 ENDIF()
 
-SET_PROPERTY(TARGET OpenAL APPEND_STRING PROPERTY LINK_FLAGS ${EXTRA_LDFLAGS})
-TARGET_LINK_LIBRARIES(OpenAL ${EXTRA_LIBS})
-IF(TARGET build_version)
-    ADD_DEPENDENCIES(OpenAL build_version)
-ENDIF()
-
 IF(ALSOFT_INSTALL)
-    # Add an install target here
     INSTALL(TARGETS OpenAL EXPORT OpenAL
             RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
             LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
@@ -1300,9 +1344,19 @@ IF(ALSOFT_INSTALL)
     )
     INSTALL(FILES "${OpenAL_BINARY_DIR}/openal.pc"
             DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
+    IF(TARGET soft_oal)
+        INSTALL(TARGETS soft_oal
+                RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+        )
+    ENDIF()
 ENDIF()
 
 
+if(HAS_ROUTER)
+    message(STATUS "")
+    message(STATUS "Building DLL router")
+endif()
+
 MESSAGE(STATUS "")
 MESSAGE(STATUS "Building OpenAL with support for the following backends:")
 MESSAGE(STATUS "    ${BACKENDS}")
@@ -1409,6 +1463,20 @@ IF(ALSOFT_TESTS)
 ENDIF()
 
 IF(ALSOFT_EXAMPLES)
+    ADD_EXECUTABLE(alrecord examples/alrecord.c ${COMMON_OBJS})
+    TARGET_LINK_LIBRARIES(alrecord OpenAL)
+    SET_PROPERTY(TARGET alrecord APPEND PROPERTY COMPILE_FLAGS ${EXTRA_CFLAGS})
+
+    IF(ALSOFT_INSTALL)
+        INSTALL(TARGETS alrecord
+                RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+                LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+                ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+        )
+    ENDIF()
+
+    MESSAGE(STATUS "Building example programs")
+
     IF(SDL2_FOUND)
         IF(SDL_SOUND_FOUND)
             SET(EX_COMMON_OBJS  examples/common/alhelpers.c)
@@ -1485,7 +1553,7 @@ IF(ALSOFT_EXAMPLES)
         ENDIF()
         IF(FFVER_OK)
             ADD_EXECUTABLE(alffplay examples/alffplay.cpp ${COMMON_OBJS})
-            TARGET_LINK_LIBRARIES(alffplay ${SDL2_LIBRARY} OpenAL ${FFMPEG_LIBRARIES})
+            TARGET_LINK_LIBRARIES(alffplay ${SDL2_LIBRARY} ${FFMPEG_LIBRARIES} OpenAL)
             SET_PROPERTY(TARGET alffplay APPEND PROPERTY COMPILE_FLAGS ${EXTRA_CFLAGS})
             SET_PROPERTY(TARGET alffplay APPEND PROPERTY
                 INCLUDE_DIRECTORIES ${SDL2_INCLUDE_DIR} ${FFMPEG_INCLUDE_DIRS}
diff --git a/ChangeLog b/ChangeLog
index 44afc3f..5300be1 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,37 @@
+openal-soft-1.18.2:
+
+    Fixed resetting the FPU rounding mode after certain function calls on
+    Windows.
+
+    Fixed use of SSE intrinsics when building with Clang on Windows.
+
+    Fixed a crash with the JACK backend when using JACK1.
+
+    Fixed use of pthread_setnane_np on NetBSD.
+
+    Fixed building on FreeBSD with an older freebsd-lib.
+
+    OSS now links with libossaudio if found at build time (for NetBSD).
+
+openal-soft-1.18.1:
+
+    Fixed an issue where resuming a source might not restart playing it.
+
+    Fixed PulseAudio playback when the configured stream length is much less
+    than the requested length.
+
+    Fixed MMDevAPI capture with sample rates not matching the backing device.
+
+    Fixed int32 output for the Wave Writer.
+
+    Fixed enumeration of OSS devices that are missing device files.
+
+    Added correct retrieval of the executable's path on FreeBSD.
+
+    Added a config option to specify the dithering depth.
+
+    Added a 5.1 decoder preset that excludes front-center output.
+
 openal-soft-1.18.0:
 
     Implemented the AL_EXT_STEREO_ANGLES and AL_EXT_SOURCE_RADIUS extensions.
diff --git a/OpenAL32/Include/alListener.h b/OpenAL32/Include/alListener.h
index 9a7f9d4..f1a92b0 100644
--- a/OpenAL32/Include/alListener.h
+++ b/OpenAL32/Include/alListener.h
@@ -26,7 +26,7 @@ struct ALlistenerProps {
 };
 
 typedef struct ALlistener {
-    ALfloat Position[3];
+    alignas(16) ALfloat Position[3];
     ALfloat Velocity[3];
     ALfloat Forward[3];
     ALfloat Up[3];
diff --git a/OpenAL32/Include/alMain.h b/OpenAL32/Include/alMain.h
index 6e17651..b14d674 100644
--- a/OpenAL32/Include/alMain.h
+++ b/OpenAL32/Include/alMain.h
@@ -409,6 +409,24 @@ inline size_t RoundUp(size_t value, size_t r)
     return value - (value%r);
 }
 
+/* Scales the given value using 64-bit integer math, rounding the result. */
+inline ALuint64 ScaleRound(ALuint64 val, ALuint64 new_scale, ALuint64 old_scale)
+{
+    return (val*new_scale + old_scale/2) / old_scale;
+}
+
+/* Scales the given value using 64-bit integer math, flooring the result. */
+inline ALuint64 ScaleFloor(ALuint64 val, ALuint64 new_scale, ALuint64 old_scale)
+{
+    return val * new_scale / old_scale;
+}
+
+/* Scales the given value using 64-bit integer math, ceiling the result. */
+inline ALuint64 ScaleCeil(ALuint64 val, ALuint64 new_scale, ALuint64 old_scale)
+{
+    return (val*new_scale + old_scale-1) / old_scale;
+}
+
 /* Fast float-to-int conversion. Assumes the FPU is already in round-to-zero
  * mode. */
 inline ALint fastf2i(ALfloat f)
@@ -425,36 +443,12 @@ inline ALint fastf2i(ALfloat 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_qsa_init(BackendFuncs *func_list);
-void alc_qsa_deinit(void);
-void alc_qsa_probe(enum DevProbe type);
-
 struct ALCbackend;
 
 
@@ -794,7 +788,7 @@ struct ALCdevice_struct
     DistanceComp ChannelDelay[MAX_OUTPUT_CHANNELS];
 
     /* Dithering control. */
-    bool DitherEnabled;
+    ALfloat DitherDepth;
     ALuint DitherSeed;
 
     /* Running count of the mixer invocations, in 31.1 fixed point. This
@@ -804,21 +798,13 @@ struct ALCdevice_struct
      */
     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
@@ -880,12 +866,15 @@ struct ALCcontext_struct {
 
     ATOMIC(struct ALeffectslotArray*) ActiveAuxSlots;
 
+    /* Default effect slot */
+    struct ALeffectslot *DefaultSlot;
+
     ALCdevice  *Device;
     const ALCchar *ExtensionList;
 
     ALCcontext *volatile next;
 
-    /* Memory space used by the listener */
+    /* Memory space used by the listener (and possibly default effect slot) */
     alignas(16) ALCbyte _listener_mem[];
 };
 
@@ -909,6 +898,9 @@ void ALCcontext_ProcessUpdates(ALCcontext *context);
 typedef struct {
 #ifdef HAVE_FENV_H
     DERIVE_FROM_TYPE(fenv_t);
+#ifdef _WIN32
+    int round_mode;
+#endif
 #else
     int state;
 #endif
@@ -918,6 +910,17 @@ typedef struct {
 } FPUCtl;
 void SetMixerFPUMode(FPUCtl *ctl);
 void RestoreFPUMode(const FPUCtl *ctl);
+#ifdef __GNUC__
+/* Use an alternate macro set with GCC to avoid accidental continue or break
+ * statements within the mixer mode.
+ */
+#define START_MIXER_MODE() __extension__({ FPUCtl _oldMode; SetMixerFPUMode(&_oldMode);
+#define END_MIXER_MODE() RestoreFPUMode(&_oldMode); })
+#else
+#define START_MIXER_MODE() do { FPUCtl _oldMode; SetMixerFPUMode(&_oldMode);
+#define END_MIXER_MODE() RestoreFPUMode(&_oldMode); } while(0)
+#endif
+#define LEAVE_MIXER_MODE() RestoreFPUMode(&_oldMode)
 
 
 typedef struct ll_ringbuffer ll_ringbuffer_t;
diff --git a/OpenAL32/Include/alu.h b/OpenAL32/Include/alu.h
index 0b3799c..794bdec 100644
--- a/OpenAL32/Include/alu.h
+++ b/OpenAL32/Include/alu.h
@@ -85,6 +85,8 @@ typedef union InterpState {
     BsincState bsinc;
 } InterpState;
 
+ALboolean BsincPrepare(const ALuint increment, BsincState *state);
+
 typedef const ALfloat* (*ResamplerFunc)(const InterpState *state,
     const ALfloat *restrict src, ALsizei frac, ALint increment,
     ALfloat *restrict dst, ALsizei dstlen
@@ -500,7 +502,7 @@ void ComputeFirstOrderGainsBF(const BFChannelConfig *chanmap, ALsizei numchans,
 
 ALboolean MixSource(struct ALvoice *voice, struct ALsource *Source, ALCdevice *Device, ALsizei SamplesToDo);
 
-void aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size);
+void aluMixData(ALCdevice *device, ALvoid *OutBuffer, ALsizei NumSamples);
 /* Caller must lock the device. */
 void aluHandleDisconnect(ALCdevice *device);
 
diff --git a/OpenAL32/alAuxEffectSlot.c b/OpenAL32/alAuxEffectSlot.c
index cd2c1e0..02288a3 100644
--- a/OpenAL32/alAuxEffectSlot.c
+++ b/OpenAL32/alAuxEffectSlot.c
@@ -506,7 +506,6 @@ ALenum InitializeEffect(ALCdevice *Device, ALeffectslot *EffectSlot, ALeffect *e
     if(newtype != EffectSlot->Effect.Type)
     {
         ALeffectStateFactory *factory;
-        FPUCtl oldMode;
 
         factory = getFactoryByType(newtype);
         if(!factory)
@@ -517,19 +516,19 @@ ALenum InitializeEffect(ALCdevice *Device, ALeffectslot *EffectSlot, ALeffect *e
         State = V0(factory,create)();
         if(!State) return AL_OUT_OF_MEMORY;
 
-        SetMixerFPUMode(&oldMode);
+        START_MIXER_MODE();
         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);
+            LEAVE_MIXER_MODE();
             ALeffectState_DecRef(State);
             return AL_OUT_OF_MEMORY;
         }
         almtx_unlock(&Device->BackendLock);
-        RestoreFPUMode(&oldMode);
+        END_MIXER_MODE();
 
         if(!effect)
         {
diff --git a/OpenAL32/alSource.c b/OpenAL32/alSource.c
index 46f23a7..be191fd 100644
--- a/OpenAL32/alSource.c
+++ b/OpenAL32/alSource.c
@@ -2502,17 +2502,7 @@ AL_API ALvoid AL_APIENTRY alSourcePlayv(ALsizei n, const ALuint *sources)
 
             case AL_PAUSED:
                 assert(voice != NULL);
-                /* A source that's paused simply resumes. Clear its mixing
-                 * parameters and mark it as 'fading' so it fades in from
-                 * silence.
-                 */
-                voice->Step = 0;
-                voice->Flags |= VOICE_IS_FADING;
-                memset(voice->Direct.Params, 0, sizeof(voice->Direct.Params[0])*
-                                                voice->NumChannels);
-                for(s = 0;s < device->NumAuxSends;s++)
-                    memset(voice->Send[s].Params, 0, sizeof(voice->Send[s].Params[0])*
-                                                     voice->NumChannels);
+                /* A source that's paused simply resumes. */
                 ATOMIC_STORE(&voice->Playing, true, almemory_order_release);
                 ATOMIC_STORE(&source->state, AL_PLAYING, almemory_order_release);
                 goto finish_play;
diff --git a/alsoftrc.sample b/alsoftrc.sample
index 26d494f..3e7d0ee 100644
--- a/alsoftrc.sample
+++ b/alsoftrc.sample
@@ -188,11 +188,18 @@
 #output-limiter = true
 
 ## dither:
-#  Applies dithering on the final mix for 8- and 16-bit output. This replaces
-#  the distortion created by nearest-value quantization with low-level
-#  whitenoise.
+#  Applies dithering on the final mix, for 8- and 16-bit output by default.
+#  This replaces the distortion created by nearest-value quantization with low-
+#  level whitenoise.
 #dither = true
 
+## dither-depth:
+#  Quantization bit-depth for dithered output. A value of 0 (or less) will
+#  match the output sample depth. For int32, uint32, and float32 output, 0 will
+#  disable dithering because they're at or beyond the rendered precision. The
+#  maximum dither depth is 24.
+#dither-depth = 0
+
 ## 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
diff --git a/appveyor.yml b/appveyor.yml
index 9c12535..eab4f39 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,4 +1,4 @@
-version: 1.18.0.{build}
+version: 1.18.2.{build}
 
 environment:
     matrix:
diff --git a/cmake/FindOSS.cmake b/cmake/FindOSS.cmake
index 88ee66a..feffb45 100644
--- a/cmake/FindOSS.cmake
+++ b/cmake/FindOSS.cmake
@@ -2,8 +2,10 @@
 #
 #   OSS_FOUND        - True if OSS_INCLUDE_DIR is found
 #   OSS_INCLUDE_DIRS - Set when OSS_INCLUDE_DIR is found
+#   OSS_LIBRARIES    - Set when OSS_LIBRARY is found
 #
 #   OSS_INCLUDE_DIR - where to find sys/soundcard.h, etc.
+#   OSS_LIBRARY     - where to find libossaudio (optional).
 #
 
 find_path(OSS_INCLUDE_DIR
@@ -11,11 +13,21 @@ find_path(OSS_INCLUDE_DIR
           DOC "The OSS include directory"
 )
 
+find_library(OSS_LIBRARY
+             NAMES ossaudio
+             DOC "Optional OSS library"
+)
+
 include(FindPackageHandleStandardArgs)
 find_package_handle_standard_args(OSS  REQUIRED_VARS OSS_INCLUDE_DIR)
 
 if(OSS_FOUND)
     set(OSS_INCLUDE_DIRS ${OSS_INCLUDE_DIR})
+    if(OSS_LIBRARY)
+        set(OSS_LIBRARIES ${OSS_LIBRARY})
+    else()
+        unset(OSS_LIBRARIES)
+    endif()
 endif()
 
-mark_as_advanced(OSS_INCLUDE_DIR)
+mark_as_advanced(OSS_INCLUDE_DIR OSS_LIBRARY)
diff --git a/common/math_defs.h b/common/math_defs.h
index 8756488..cbe9091 100644
--- a/common/math_defs.h
+++ b/common/math_defs.h
@@ -15,7 +15,18 @@
 #endif
 
 #ifndef HUGE_VALF
-#define HUGE_VALF  (1.0f/0.0f)
+static const union msvc_inf_hack {
+    unsigned char b[4];
+    float f;
+} msvc_inf_union = {{ 0x00, 0x00, 0x80, 0x7F }};
+#define HUGE_VALF (msvc_inf_union.f)
+#endif
+
+#ifndef HAVE_LOG2F
+static inline float log2f(float f)
+{
+    return logf(f) / logf(2.0f);
+}
 #endif
 
 #define DEG2RAD(x)  ((float)(x) * (F_PI/180.0f))
diff --git a/common/threads.c b/common/threads.c
index 0a019d0..dbd196e 100644
--- a/common/threads.c
+++ b/common/threads.c
@@ -257,7 +257,7 @@ int alcnd_timedwait(alcnd_t *cond, almtx_t *mtx, const struct timespec *time_poi
     else
     {
         sleeptime  = (time_point->tv_nsec - curtime.tv_nsec + 999999)/1000000;
-        sleeptime += (time_point->tv_sec - curtime.tv_sec)*1000;
+        sleeptime += (DWORD)(time_point->tv_sec - curtime.tv_sec)*1000;
         if(SleepConditionVariableCS(cond, mtx, sleeptime) != 0)
             return althrd_success;
     }
@@ -364,7 +364,7 @@ int alcnd_timedwait(alcnd_t *cond, almtx_t *mtx, const struct timespec *time_poi
     else
     {
         sleeptime  = (time_point->tv_nsec - curtime.tv_nsec + 999999)/1000000;
-        sleeptime += (time_point->tv_sec - curtime.tv_sec)*1000;
+        sleeptime += (DWORD)(time_point->tv_sec - curtime.tv_sec)*1000;
     }
 
     IncrementRef(&icond->wait_count);
@@ -500,6 +500,8 @@ void althrd_setname(althrd_t thr, const char *name)
 #if defined(PTHREAD_SETNAME_NP_ONE_PARAM)
     if(althrd_equal(thr, althrd_current()))
         pthread_setname_np(name);
+#elif defined(PTHREAD_SETNAME_NP_THREE_PARAMS)
+    pthread_setname_np(thr, "%s", (void*)name);
 #else
     pthread_setname_np(thr, name);
 #endif
diff --git a/config.h.in b/config.h.in
index a71b54f..b0fc5d2 100644
--- a/config.h.in
+++ b/config.h.in
@@ -80,6 +80,9 @@
 /* Define if we have the modff function */
 #cmakedefine HAVE_MODFF
 
+/* Define if we have the log2f function */
+#cmakedefine HAVE_LOG2F
+
 /* Define if we have the strtof function */
 #cmakedefine HAVE_STRTOF
 
@@ -191,6 +194,9 @@
 /* Define if pthread_setname_np() only accepts one parameter */
 #cmakedefine PTHREAD_SETNAME_NP_ONE_PARAM
 
+/* Define if pthread_setname_np() accepts three parameters */
+#cmakedefine PTHREAD_SETNAME_NP_THREE_PARAMS
+
 /* Define if we have pthread_set_name_np() */
 #cmakedefine HAVE_PTHREAD_SET_NAME_NP
 
diff --git a/examples/alffplay.cpp b/examples/alffplay.cpp
index b2033b9..10c0055 100644
--- a/examples/alffplay.cpp
+++ b/examples/alffplay.cpp
@@ -16,6 +16,7 @@
 #include <atomic>
 #include <mutex>
 #include <deque>
+#include <array>
 
 extern "C" {
 #include "libavcodec/avcodec.h"
@@ -40,6 +41,7 @@ namespace
 
 static const std::string AppName("alffplay");
 
+static bool do_direct_out = false;
 static bool has_latency_check = false;
 static LPALGETSOURCEDVSOFT alGetSourcedvSOFT;
 
@@ -730,6 +732,17 @@ int AudioState::handler()
     alGenBuffers(AUDIO_BUFFER_QUEUE_SIZE, mBuffers);
     alGenSources(1, &mSource);
 
+    if(do_direct_out)
+    {
+        if(!alIsExtensionPresent("AL_SOFT_direct_channels"))
+            std::cerr<< "AL_SOFT_direct_channels not supported for direct output" <<std::endl;
+        else
+        {
+            alSourcei(mSource, AL_DIRECT_CHANNELS_SOFT, AL_TRUE);
+            std::cout<< "Direct out enabled" <<std::endl;
+        }
+    }
+
     while(alGetError() == AL_NO_ERROR && !mMovie->mQuit.load())
     {
         /* First remove any processed buffers. */
@@ -1358,7 +1371,7 @@ int main(int argc, char *argv[])
 
     if(argc < 2)
     {
-        std::cerr<< "Usage: "<<argv[0]<<" [-device <device name>] <files...>" <<std::endl;
+        std::cerr<< "Usage: "<<argv[0]<<" [-device <device name>] [-direct] <files...>" <<std::endl;
         return 1;
     }
     /* Register all formats and codecs */
@@ -1418,12 +1431,9 @@ int main(int argc, char *argv[])
         ALCdevice *dev = NULL;
         if(argc > 3 && strcmp(argv[1], "-device") == 0)
         {
+            fileidx = 3;
             dev = alcOpenDevice(argv[2]);
-            if(dev)
-            {
-                fileidx = 3;
-                return dev;
-            }
+            if(dev) return dev;
             std::cerr<< "Failed to open \""<<argv[2]<<"\" - trying default" <<std::endl;
         }
         return alcOpenDevice(nullptr);
@@ -1437,6 +1447,19 @@ int main(int argc, char *argv[])
         return 1;
     }
 
+    const ALCchar *name = nullptr;
+    if(alcIsExtensionPresent(device, "ALC_ENUMERATE_ALL_EXT"))
+        name = alcGetString(device, ALC_ALL_DEVICES_SPECIFIER);
+    if(!name || alcGetError(device) != AL_NO_ERROR)
+        name = alcGetString(device, ALC_DEVICE_SPECIFIER);
+    std::cout<< "Opened \""<<name<<"\"" <<std::endl;
+
+    if(fileidx < argc && strcmp(argv[fileidx], "-direct") == 0)
+    {
+        ++fileidx;
+        do_direct_out = true;
+    }
+
     while(fileidx < argc && !movState)
     {
         movState = std::unique_ptr<MovieState>(new MovieState(argv[fileidx++]));
diff --git a/examples/alrecord.c b/examples/alrecord.c
new file mode 100644
index 0000000..0835b46
--- /dev/null
+++ b/examples/alrecord.c
@@ -0,0 +1,386 @@
+/*
+ * OpenAL Recording Example
+ *
+ * Copyright (c) 2017 by Chris Robinson <chris.kcat at gmail.com>
+ *
+ * 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.
+ */
+
+/* This file contains a relatively simple recorder. */
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+
+#include "AL/al.h"
+#include "AL/alc.h"
+#include "AL/alext.h"
+
+#include "common/alhelpers.h"
+
+
+#if defined(_WIN64)
+#define SZFMT "%I64u"
+#elif defined(_WIN32)
+#define SZFMT "%u"
+#else
+#define SZFMT "%zu"
+#endif
+
+
+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 Recorder {
+    ALCdevice *mDevice;
+
+    FILE *mFile;
+    long mDataSizeOffset;
+    size_t mDataSize;
+    float mRecTime;
+
+    int mChannels;
+    int mBits;
+    int mSampleRate;
+    size_t mFrameSize;
+    ALbyte *mBuffer;
+    ALsizei mBufferSize;
+} Recorder;
+
+int main(int argc, char **argv)
+{
+    static const char optlist[] =
+"    --channels/-c <channels>  Set channel count (1 or 2)\n"
+"    --bits/-b <bits>          Set channel count (8, 16, or 32)\n"
+"    --rate/-r <rate>          Set sample rate (8000 to 96000)\n"
+"    --time/-t <time>          Time in seconds to record (1 to 10)\n"
+"    --outfile/-o <filename>   Output filename (default: record.wav)";
+    const char *fname = "record.wav";
+    const char *devname = NULL;
+    const char *progname;
+    Recorder recorder;
+    long total_size;
+    ALenum format;
+    ALCenum err;
+
+    progname = argv[0];
+    if(argc < 2)
+    {
+        fprintf(stderr, "Record from a device to a wav file.\n\n"
+                "Usage: %s [-device <name>] [options...]\n\n"
+                "Available options:\n%s\n", progname, optlist);
+        return 0;
+    }
+
+    recorder.mDevice = NULL;
+    recorder.mFile = NULL;
+    recorder.mDataSizeOffset = 0;
+    recorder.mDataSize = 0;
+    recorder.mRecTime = 4.0f;
+    recorder.mChannels = 1;
+    recorder.mBits = 16;
+    recorder.mSampleRate = 44100;
+    recorder.mFrameSize = recorder.mChannels * recorder.mBits / 8;
+    recorder.mBuffer = NULL;
+    recorder.mBufferSize = 0;
+
+    argv++; argc--;
+    if(argc > 1 && strcmp(argv[0], "-device") == 0)
+    {
+        devname = argv[1];
+        argv += 2;
+        argc -= 2;
+    }
+
+    while(argc > 0)
+    {
+        char *end;
+        if(strcmp(argv[0], "--") == 0)
+            break;
+        else if(strcmp(argv[0], "--channels") == 0 || strcmp(argv[0], "-c") == 0)
+        {
+            if(!(argc > 1))
+            {
+                fprintf(stderr, "Missing argument for option: %s\n", argv[0]);
+                return 1;
+            }
+
+            recorder.mChannels = strtol(argv[1], &end, 0);
+            if((recorder.mChannels != 1 && recorder.mChannels != 2) || (end && *end != '\0'))
+            {
+                fprintf(stderr, "Invalid channels: %s\n", argv[1]);
+                return 1;
+            }
+            argv += 2;
+            argc -= 2;
+        }
+        else if(strcmp(argv[0], "--bits") == 0 || strcmp(argv[0], "-b") == 0)
+        {
+            if(!(argc > 1))
+            {
+                fprintf(stderr, "Missing argument for option: %s\n", argv[0]);
+                return 1;
+            }
+
+            recorder.mBits = strtol(argv[1], &end, 0);
+            if((recorder.mBits != 8 && recorder.mBits != 16 && recorder.mBits != 32) ||
+               (end && *end != '\0'))
+            {
+                fprintf(stderr, "Invalid bit count: %s\n", argv[1]);
+                return 1;
+            }
+            argv += 2;
+            argc -= 2;
+        }
+        else if(strcmp(argv[0], "--rate") == 0 || strcmp(argv[0], "-r") == 0)
+        {
+            if(!(argc > 1))
+            {
+                fprintf(stderr, "Missing argument for option: %s\n", argv[0]);
+                return 1;
+            }
+
+            recorder.mSampleRate = strtol(argv[1], &end, 0);
+            if(!(recorder.mSampleRate >= 8000 && recorder.mSampleRate <= 96000) || (end && *end != '\0'))
+            {
+                fprintf(stderr, "Invalid sample rate: %s\n", argv[1]);
+                return 1;
+            }
+            argv += 2;
+            argc -= 2;
+        }
+        else if(strcmp(argv[0], "--time") == 0 || strcmp(argv[0], "-t") == 0)
+        {
+            if(!(argc > 1))
+            {
+                fprintf(stderr, "Missing argument for option: %s\n", argv[0]);
+                return 1;
+            }
+
+            recorder.mRecTime = strtod(argv[1], &end);
+            if(!(recorder.mRecTime >= 1.0f && recorder.mRecTime <= 10.0f) || (end && *end != '\0'))
+            {
+                fprintf(stderr, "Invalid record time: %s\n", argv[1]);
+                return 1;
+            }
+            argv += 2;
+            argc -= 2;
+        }
+        else if(strcmp(argv[0], "--outfile") == 0 || strcmp(argv[0], "-o") == 0)
+        {
+            if(!(argc > 1))
+            {
+                fprintf(stderr, "Missing argument for option: %s\n", argv[0]);
+                return 1;
+            }
+
+            fname = argv[1];
+            argv += 2;
+            argc -= 2;
+        }
+        else if(strcmp(argv[0], "--help") == 0 || strcmp(argv[0], "-h") == 0)
+        {
+            fprintf(stderr, "Record from a device to a wav file.\n\n"
+                    "Usage: %s [-device <name>] [options...]\n\n"
+                    "Available options:\n%s\n", progname, optlist);
+            return 0;
+        }
+        else
+        {
+            fprintf(stderr, "Invalid option '%s'.\n\n"
+                    "Usage: %s [-device <name>] [options...]\n\n"
+                    "Available options:\n%s\n", argv[0], progname, optlist);
+            return 0;
+        }
+    }
+
+    recorder.mFrameSize = recorder.mChannels * recorder.mBits / 8;
+
+    format = AL_NONE;
+    if(recorder.mChannels == 1)
+    {
+        if(recorder.mBits == 8)
+            format = AL_FORMAT_MONO8;
+        else if(recorder.mBits == 16)
+            format = AL_FORMAT_MONO16;
+        else if(recorder.mBits == 32)
+            format = AL_FORMAT_MONO_FLOAT32;
+    }
+    else if(recorder.mChannels == 2)
+    {
+        if(recorder.mBits == 8)
+            format = AL_FORMAT_STEREO8;
+        else if(recorder.mBits == 16)
+            format = AL_FORMAT_STEREO16;
+        else if(recorder.mBits == 32)
+            format = AL_FORMAT_STEREO_FLOAT32;
+    }
+
+    recorder.mDevice = alcCaptureOpenDevice(devname, recorder.mSampleRate, format, 32768);
+    if(!recorder.mDevice)
+    {
+        fprintf(stderr, "Failed to open %s, %s %d-bit, %s, %dhz (%d samples)\n",
+            devname ? devname : "default device",
+            (recorder.mBits == 32) ? "Float" :
+            (recorder.mBits !=  8) ? "Signed" : "Unsigned", recorder.mBits,
+            (recorder.mChannels == 1) ? "Mono" : "Stereo", recorder.mSampleRate,
+            32768
+        );
+        return 1;
+    }
+    fprintf(stderr, "Opened \"%s\"\n", alcGetString(
+        recorder.mDevice, ALC_CAPTURE_DEVICE_SPECIFIER
+    ));
+
+    recorder.mFile = fopen(fname, "wb");
+    if(!recorder.mFile)
+    {
+        fprintf(stderr, "Failed to open '%s' for writing\n", fname);
+        alcCaptureCloseDevice(recorder.mDevice);
+        return 1;
+    }
+
+    fputs("RIFF", recorder.mFile);
+    fwrite32le(0xFFFFFFFF, recorder.mFile); // 'RIFF' header len; filled in at close
+
+    fputs("WAVE", recorder.mFile);
+
+    fputs("fmt ", recorder.mFile);
+    fwrite32le(18, recorder.mFile); // 'fmt ' header len
+
+    // 16-bit val, format type id (1 = integer PCM, 3 = float PCM)
+    fwrite16le((recorder.mBits == 32) ? 0x0003 : 0x0001, recorder.mFile);
+    // 16-bit val, channel count
+    fwrite16le(recorder.mChannels, recorder.mFile);
+    // 32-bit val, frequency
+    fwrite32le(recorder.mSampleRate, recorder.mFile);
+    // 32-bit val, bytes per second
+    fwrite32le(recorder.mSampleRate * recorder.mFrameSize, recorder.mFile);
+    // 16-bit val, frame size
+    fwrite16le(recorder.mFrameSize, recorder.mFile);
+    // 16-bit val, bits per sample
+    fwrite16le(recorder.mBits, recorder.mFile);
+    // 16-bit val, extra byte count
+    fwrite16le(0, recorder.mFile);
+
+    fputs("data", recorder.mFile);
+    fwrite32le(0xFFFFFFFF, recorder.mFile); // 'data' header len; filled in at close
+
+    recorder.mDataSizeOffset = ftell(recorder.mFile) - 4;
+    if(ferror(recorder.mFile) || recorder.mDataSizeOffset < 0)
+    {
+        fprintf(stderr, "Error writing header: %s\n", strerror(errno));
+        fclose(recorder.mFile);
+        alcCaptureCloseDevice(recorder.mDevice);
+        return 1;
+    }
+
+    fprintf(stderr, "Recording '%s', %s %d-bit, %s, %dhz (%g second%s)\n", fname,
+        (recorder.mBits == 32) ? "Float" :
+        (recorder.mBits !=  8) ? "Signed" : "Unsigned", recorder.mBits,
+        (recorder.mChannels == 1) ? "Mono" : "Stereo", recorder.mSampleRate,
+        recorder.mRecTime, (recorder.mRecTime != 1.0f) ? "s" : ""
+    );
+
+    alcCaptureStart(recorder.mDevice);
+    while((double)recorder.mDataSize/(double)recorder.mSampleRate < recorder.mRecTime &&
+          (err=alcGetError(recorder.mDevice)) == ALC_NO_ERROR && !ferror(recorder.mFile))
+    {
+        ALCint count = 0;
+        fprintf(stderr, "\rCaptured "SZFMT" samples", recorder.mDataSize);
+        alcGetIntegerv(recorder.mDevice, ALC_CAPTURE_SAMPLES, 1, &count);
+        if(count < 1)
+        {
+            al_nssleep(10000000);
+            continue;
+        }
+        if(count > recorder.mBufferSize)
+        {
+            ALbyte *data = calloc(recorder.mFrameSize, count);
+            free(recorder.mBuffer);
+            recorder.mBuffer = data;
+            recorder.mBufferSize = count;
+        }
+        alcCaptureSamples(recorder.mDevice, recorder.mBuffer, count);
+#if defined(__BYTE_ORDER) && __BYTE_ORDER == __BIG_ENDIAN
+        /* Byteswap multibyte samples on big-endian systems (wav needs little-
+         * endian, and OpenAL gives the system's native-endian).
+         */
+        if(recorder.mBits == 16)
+        {
+            ALCint i;
+            for(i = 0;i < count*recorder.mChannels;i++)
+            {
+                ALbyte b = recorder.mBuffer[i*2 + 0];
+                recorder.mBuffer[i*2 + 0] = recorder.mBuffer[i*2 + 1];
+                recorder.mBuffer[i*2 + 1] = b;
+            }
+        }
+        else if(recorder.mBits == 32)
+        {
+            ALCint i;
+            for(i = 0;i < count*recorder.mChannels;i++)
+            {
+                ALbyte b0 = recorder.mBuffer[i*4 + 0];
+                ALbyte b1 = recorder.mBuffer[i*4 + 1];
+                recorder.mBuffer[i*4 + 0] = recorder.mBuffer[i*4 + 3];
+                recorder.mBuffer[i*4 + 1] = recorder.mBuffer[i*4 + 2];
+                recorder.mBuffer[i*4 + 2] = b1;
+                recorder.mBuffer[i*4 + 3] = b0;
+            }
+        }
+#endif
+        recorder.mDataSize += fwrite(recorder.mBuffer, recorder.mFrameSize, count, recorder.mFile);
+    }
+    alcCaptureStop(recorder.mDevice);
+    fprintf(stderr, "\rCaptured "SZFMT" samples\n", recorder.mDataSize);
+    if(err != ALC_NO_ERROR)
+        fprintf(stderr, "Got device error 0x%04x: %s\n", err, alcGetString(recorder.mDevice, err));
+
+    alcCaptureCloseDevice(recorder.mDevice);
+    recorder.mDevice = NULL;
+
+    free(recorder.mBuffer);
+    recorder.mBuffer = NULL;
+    recorder.mBufferSize = 0;
+
+    total_size = ftell(recorder.mFile);
+    if(fseek(recorder.mFile, recorder.mDataSizeOffset, SEEK_SET) == 0)
+    {
+        fwrite32le(recorder.mDataSize*recorder.mFrameSize, recorder.mFile);
+        if(fseek(recorder.mFile, 4, SEEK_SET) == 0)
+            fwrite32le(total_size - 8, recorder.mFile);
+    }
+
+    fclose(recorder.mFile);
+    recorder.mFile = NULL;
+
+    return 0;
+}
diff --git a/examples/altonegen.c b/examples/altonegen.c
index 422e6d6..628e695 100644
--- a/examples/altonegen.c
+++ b/examples/altonegen.c
@@ -35,6 +35,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <assert.h>
+#include <limits.h>
 #include <math.h>
 
 #include "AL/al.h"
@@ -53,6 +54,7 @@ enum WaveType {
     WT_Sawtooth,
     WT_Triangle,
     WT_Impulse,
+    WT_WhiteNoise,
 };
 
 static const char *GetWaveTypeName(enum WaveType type)
@@ -64,10 +66,17 @@ static const char *GetWaveTypeName(enum WaveType type)
         case WT_Sawtooth: return "sawtooth";
         case WT_Triangle: return "triangle";
         case WT_Impulse: return "impulse";
+        case WT_WhiteNoise: return "noise";
     }
     return "(unknown)";
 }
 
+static inline ALuint dither_rng(ALuint *seed)
+{
+    *seed = (*seed * 96314165) + 907633515;
+    return *seed;
+}
+
 static void ApplySin(ALfloat *data, ALdouble g, ALuint srate, ALuint freq)
 {
     ALdouble smps_per_cycle = (ALdouble)srate / freq;
@@ -81,6 +90,7 @@ static void ApplySin(ALfloat *data, ALdouble g, ALuint srate, ALuint freq)
  */
 static ALuint CreateWave(enum WaveType type, ALuint freq, ALuint srate)
 {
+    ALuint seed = 22222;
     ALint data_size;
     ALfloat *data;
     ALuint buffer;
@@ -89,25 +99,44 @@ static ALuint CreateWave(enum WaveType type, ALuint freq, ALuint srate)
 
     data_size = srate * sizeof(ALfloat);
     data = calloc(1, data_size);
-    if(type == WT_Sine)
-        ApplySin(data, 1.0, srate, freq);
-    else if(type == WT_Square)
-        for(i = 1;freq*i < srate/2;i+=2)
-            ApplySin(data, 4.0/M_PI * 1.0/i, srate, freq*i);
-    else if(type == WT_Sawtooth)
-        for(i = 1;freq*i < srate/2;i++)
-            ApplySin(data, 2.0/M_PI * ((i&1)*2 - 1.0) / i, srate, freq*i);
-    else if(type == WT_Triangle)
-        for(i = 1;freq*i < srate/2;i+=2)
-            ApplySin(data, 8.0/(M_PI*M_PI) * (1.0 - (i&2)) / (i*i), srate, freq*i);
-    else if(type == WT_Impulse)
+    switch(type)
     {
-        /* NOTE: Impulse isn't really a waveform, but it can still be useful to
-         * test (other than resampling, the ALSOFT_DEFAULT_REVERB environment
-         * variable can prove useful here to test the reverb response).
-         */
-        for(i = 0;i < srate;i++)
-            data[i] = (i%(srate/freq)) ? 0.0f : 1.0f;
+        case WT_Sine:
+            ApplySin(data, 1.0, srate, freq);
+            break;
+        case WT_Square:
+            for(i = 1;freq*i < srate/2;i+=2)
+                ApplySin(data, 4.0/M_PI * 1.0/i, srate, freq*i);
+            break;
+        case WT_Sawtooth:
+            for(i = 1;freq*i < srate/2;i++)
+                ApplySin(data, 2.0/M_PI * ((i&1)*2 - 1.0) / i, srate, freq*i);
+            break;
+        case WT_Triangle:
+            for(i = 1;freq*i < srate/2;i+=2)
+                ApplySin(data, 8.0/(M_PI*M_PI) * (1.0 - (i&2)) / (i*i), srate, freq*i);
+            break;
+        case WT_Impulse:
+            /* NOTE: Impulse isn't handled using additive synthesis, and is
+             * instead just a non-0 sample at a given rate. This can still be
+             * useful to test (other than resampling, the ALSOFT_DEFAULT_REVERB
+             * environment variable can prove useful here to test the reverb
+             * response).
+             */
+            for(i = 0;i < srate;i++)
+                data[i] = (i%(srate/freq)) ? 0.0f : 1.0f;
+            break;
+        case WT_WhiteNoise:
+            /* NOTE: WhiteNoise is just uniform set of uncorrelated values, and
+             * is not influenced by the waveform frequency.
+             */
+            for(i = 0;i < srate;i++)
+            {
+                ALuint rng0 = dither_rng(&seed);
+                ALuint rng1 = dither_rng(&seed);
+                data[i] = (ALfloat)(rng0*(1.0/UINT_MAX) - rng1*(1.0/UINT_MAX));
+            }
+            break;
     }
 
     /* Buffer the audio data into a new buffer object. */
@@ -166,7 +195,7 @@ int main(int argc, char *argv[])
 "  --help/-h                 This help text\n"
 "  -t <seconds>              Time to play a tone (default 5 seconds)\n"
 "  --waveform/-w <type>      Waveform type: sine (default), square, sawtooth,\n"
-"                                triangle, impulse\n"
+"                                triangle, impulse, noise\n"
 "  --freq/-f <hz>            Tone frequency (default 1000 hz)\n"
 "  --srate/-s <sample rate>  Sampling rate (default output rate)\n",
                 appname
@@ -192,6 +221,8 @@ int main(int argc, char *argv[])
                 wavetype = WT_Triangle;
             else if(strcmp(argv[i], "impulse") == 0)
                 wavetype = WT_Impulse;
+            else if(strcmp(argv[i], "noise") == 0)
+                wavetype = WT_WhiteNoise;
             else
                 fprintf(stderr, "Unhandled waveform: %s\n", argv[i]);
         }
diff --git a/hrtf/default-44100.mhr b/hrtf/default-44100.mhr
index e0d0bf4..7150159 100644
Binary files a/hrtf/default-44100.mhr and b/hrtf/default-44100.mhr differ
diff --git a/hrtf/default-48000.mhr b/hrtf/default-48000.mhr
index 7addfa7..cee0971 100644
Binary files a/hrtf/default-48000.mhr and b/hrtf/default-48000.mhr differ
diff --git a/presets/itu5.1-nocenter.ambdec b/presets/itu5.1-nocenter.ambdec
new file mode 100644
index 0000000..23839d0
--- /dev/null
+++ b/presets/itu5.1-nocenter.ambdec
@@ -0,0 +1,46 @@
+# AmbDec configuration
+# Written by Ambisonic Decoder Toolbox, version 8.0
+
+# input channel order: WYXVU
+
+/description     itu50-noCenter_2h0p_allrad_5200_rE_max_1_band
+
+# Although unused in this configuration, the front-center is declared here so
+# that an appropriate distance may be set (for proper delaying or attenuating
+# of dialog and such which feed it directly). It otherwise does not contribute
+# to positional sound output.
+
+/version            3
+
+/dec/chan_mask      11b
+/dec/freq_bands     1
+/dec/speakers       5
+/dec/coeff_scale    fuma
+
+/opt/input_scale    fuma
+/opt/nfeff_comp     input
+/opt/delay_comp     on
+/opt/level_comp     on
+/opt/xover_freq     400.000000
+/opt/xover_ratio    0.000000
+
+/speakers/{
+#           id  dist         azim      elev      conn
+#-----------------------------------------------------------------------
+add_spkr    LS  1.000000   110.000000  0.000000  system:playback_3
+add_spkr    LF  1.000000    30.000000  0.000000  system:playback_1
+add_spkr    CE  1.000000     0.000000  0.000000  system:playback_5
+add_spkr    RF  1.000000   -30.000000  0.000000  system:playback_2
+add_spkr    RS  1.000000  -110.000000  0.000000  system:playback_4
+/}
+
+/matrix/{
+order_gain  1.00000000e+00 8.66025404e-01 5.00000000e-01 0.000000
+add_row  4.70934222e-01  3.78169605e-01 -4.00084750e-01 -8.22264454e-02 -4.43765986e-02
+add_row  2.66639870e-01  2.55418584e-01  3.32591390e-01  2.82949132e-01  8.16816772e-02
+add_row  0.00000000e+00  0.00000000e+00  0.00000000e+00  0.00000000e+00  0.00000000e+00
+add_row  2.66634915e-01 -2.55421639e-01  3.32586482e-01 -2.82947688e-01  8.16782588e-02
+add_row  4.70935891e-01 -3.78173080e-01 -4.00080588e-01  8.22279700e-02 -4.43716394e-02
+/}
+
+/end
diff --git a/router/al.c b/router/al.c
new file mode 100644
index 0000000..3349ad1
--- /dev/null
+++ b/router/al.c
@@ -0,0 +1,132 @@
+
+#include "config.h"
+
+#include <stddef.h>
+
+#include "AL/al.h"
+#include "router.h"
+
+
+ATOMIC(DriverIface*) CurrentCtxDriver = ATOMIC_INIT_STATIC(NULL);
+
+#define DECL_THUNK1(R,n,T1) AL_API R AL_APIENTRY n(T1 a)                      \
+{                                                                             \
+    DriverIface *iface = altss_get(ThreadCtxDriver);                          \
+    if(!iface) iface = ATOMIC_LOAD(&CurrentCtxDriver, almemory_order_acquire);\
+    return iface->n(a);                                                       \
+}
+#define DECL_THUNK2(R,n,T1,T2) AL_API R AL_APIENTRY n(T1 a, T2 b) \
+{                                                                             \
+    DriverIface *iface = altss_get(ThreadCtxDriver);                          \
+    if(!iface) iface = ATOMIC_LOAD(&CurrentCtxDriver, almemory_order_acquire);\
+    return iface->n(a, b);                                                    \
+}
+#define DECL_THUNK3(R,n,T1,T2,T3) AL_API R AL_APIENTRY n(T1 a, T2 b, T3 c) \
+{                                                                             \
+    DriverIface *iface = altss_get(ThreadCtxDriver);                          \
+    if(!iface) iface = ATOMIC_LOAD(&CurrentCtxDriver, almemory_order_acquire);\
+    return iface->n(a, b, c);                                                 \
+}
+#define DECL_THUNK4(R,n,T1,T2,T3,T4) AL_API R AL_APIENTRY n(T1 a, T2 b, T3 c, T4 d) \
+{                                                                             \
+    DriverIface *iface = altss_get(ThreadCtxDriver);                          \
+    if(!iface) iface = ATOMIC_LOAD(&CurrentCtxDriver, almemory_order_acquire);\
+    return iface->n(a, b, c, d);                                              \
+}
+#define DECL_THUNK5(R,n,T1,T2,T3,T4,T5) AL_API R AL_APIENTRY n(T1 a, T2 b, T3 c, T4 d, T5 e) \
+{                                                                             \
+    DriverIface *iface = altss_get(ThreadCtxDriver);                          \
+    if(!iface) iface = ATOMIC_LOAD(&CurrentCtxDriver, almemory_order_acquire);\
+    return iface->n(a, b, c, d, e);                                           \
+}
+
+
+/* Ugly hack for some apps calling alGetError without a current context, and
+ * expecting it to be AL_NO_ERROR.
+ */
+AL_API ALenum AL_APIENTRY alGetError(void)
+{
+    DriverIface *iface = altss_get(ThreadCtxDriver);
+    if(!iface) iface = ATOMIC_LOAD(&CurrentCtxDriver, almemory_order_acquire);
+    return iface ? iface->alGetError() : AL_NO_ERROR;
+}
+
+
+DECL_THUNK1(void, alDopplerFactor, ALfloat)
+DECL_THUNK1(void, alDopplerVelocity, ALfloat)
+DECL_THUNK1(void, alSpeedOfSound, ALfloat)
+DECL_THUNK1(void, alDistanceModel, ALenum)
+
+DECL_THUNK1(void, alEnable, ALenum)
+DECL_THUNK1(void, alDisable, ALenum)
+DECL_THUNK1(ALboolean, alIsEnabled, ALenum)
+
+DECL_THUNK1(const ALchar*, alGetString, ALenum)
+DECL_THUNK2(void, alGetBooleanv, ALenum, ALboolean*)
+DECL_THUNK2(void, alGetIntegerv, ALenum, ALint*)
+DECL_THUNK2(void, alGetFloatv, ALenum, ALfloat*)
+DECL_THUNK2(void, alGetDoublev, ALenum, ALdouble*)
+DECL_THUNK1(ALboolean, alGetBoolean, ALenum)
+DECL_THUNK1(ALint, alGetInteger, ALenum)
+DECL_THUNK1(ALfloat, alGetFloat, ALenum)
+DECL_THUNK1(ALdouble, alGetDouble, ALenum)
+
+DECL_THUNK1(ALboolean, alIsExtensionPresent, const ALchar*)
+DECL_THUNK1(void*, alGetProcAddress, const ALchar*)
+DECL_THUNK1(ALenum, alGetEnumValue, const ALchar*)
+
+DECL_THUNK2(void, alListenerf, ALenum, ALfloat)
+DECL_THUNK4(void, alListener3f, ALenum, ALfloat, ALfloat, ALfloat)
+DECL_THUNK2(void, alListenerfv, ALenum, const ALfloat*)
+DECL_THUNK2(void, alListeneri, ALenum, ALint)
+DECL_THUNK4(void, alListener3i, ALenum, ALint, ALint, ALint)
+DECL_THUNK2(void, alListeneriv, ALenum, const ALint*)
+DECL_THUNK2(void, alGetListenerf, ALenum, ALfloat*)
+DECL_THUNK4(void, alGetListener3f, ALenum, ALfloat*, ALfloat*, ALfloat*)
+DECL_THUNK2(void, alGetListenerfv, ALenum, ALfloat*)
+DECL_THUNK2(void, alGetListeneri, ALenum, ALint*)
+DECL_THUNK4(void, alGetListener3i, ALenum, ALint*, ALint*, ALint*)
+DECL_THUNK2(void, alGetListeneriv, ALenum, ALint*)
+
+DECL_THUNK2(void, alGenSources, ALsizei, ALuint*)
+DECL_THUNK2(void, alDeleteSources, ALsizei, const ALuint*)
+DECL_THUNK1(ALboolean, alIsSource, ALuint)
+DECL_THUNK3(void, alSourcef, ALuint, ALenum, ALfloat)
+DECL_THUNK5(void, alSource3f, ALuint, ALenum, ALfloat, ALfloat, ALfloat)
+DECL_THUNK3(void, alSourcefv, ALuint, ALenum, const ALfloat*)
+DECL_THUNK3(void, alSourcei, ALuint, ALenum, ALint)
+DECL_THUNK5(void, alSource3i, ALuint, ALenum, ALint, ALint, ALint)
+DECL_THUNK3(void, alSourceiv, ALuint, ALenum, const ALint*)
+DECL_THUNK3(void, alGetSourcef, ALuint, ALenum, ALfloat*)
+DECL_THUNK5(void, alGetSource3f, ALuint, ALenum, ALfloat*, ALfloat*, ALfloat*)
+DECL_THUNK3(void, alGetSourcefv, ALuint, ALenum, ALfloat*)
+DECL_THUNK3(void, alGetSourcei, ALuint, ALenum, ALint*)
+DECL_THUNK5(void, alGetSource3i, ALuint, ALenum, ALint*, ALint*, ALint*)
+DECL_THUNK3(void, alGetSourceiv, ALuint, ALenum, ALint*)
+DECL_THUNK2(void, alSourcePlayv, ALsizei, const ALuint*)
+DECL_THUNK2(void, alSourceStopv, ALsizei, const ALuint*)
+DECL_THUNK2(void, alSourceRewindv, ALsizei, const ALuint*)
+DECL_THUNK2(void, alSourcePausev, ALsizei, const ALuint*)
+DECL_THUNK1(void, alSourcePlay, ALuint)
+DECL_THUNK1(void, alSourceStop, ALuint)
+DECL_THUNK1(void, alSourceRewind, ALuint)
+DECL_THUNK1(void, alSourcePause, ALuint)
+DECL_THUNK3(void, alSourceQueueBuffers, ALuint, ALsizei, const ALuint*)
+DECL_THUNK3(void, alSourceUnqueueBuffers, ALuint, ALsizei, ALuint*)
+
+DECL_THUNK2(void, alGenBuffers, ALsizei, ALuint*)
+DECL_THUNK2(void, alDeleteBuffers, ALsizei, const ALuint*)
+DECL_THUNK1(ALboolean, alIsBuffer, ALuint)
+DECL_THUNK3(void, alBufferf, ALuint, ALenum, ALfloat)
+DECL_THUNK5(void, alBuffer3f, ALuint, ALenum, ALfloat, ALfloat, ALfloat)
+DECL_THUNK3(void, alBufferfv, ALuint, ALenum, const ALfloat*)
+DECL_THUNK3(void, alBufferi, ALuint, ALenum, ALint)
+DECL_THUNK5(void, alBuffer3i, ALuint, ALenum, ALint, ALint, ALint)
+DECL_THUNK3(void, alBufferiv, ALuint, ALenum, const ALint*)
+DECL_THUNK3(void, alGetBufferf, ALuint, ALenum, ALfloat*)
+DECL_THUNK5(void, alGetBuffer3f, ALuint, ALenum, ALfloat*, ALfloat*, ALfloat*)
+DECL_THUNK3(void, alGetBufferfv, ALuint, ALenum, ALfloat*)
+DECL_THUNK3(void, alGetBufferi, ALuint, ALenum, ALint*)
+DECL_THUNK5(void, alGetBuffer3i, ALuint, ALenum, ALint*, ALint*, ALint*)
+DECL_THUNK3(void, alGetBufferiv, ALuint, ALenum, ALint*)
+DECL_THUNK5(void, alBufferData, ALuint, ALenum, const ALvoid*, ALsizei, ALsizei)
diff --git a/router/alc.c b/router/alc.c
new file mode 100644
index 0000000..946c7d4
--- /dev/null
+++ b/router/alc.c
@@ -0,0 +1,956 @@
+
+#include "config.h"
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "AL/alc.h"
+#include "router.h"
+#include "almalloc.h"
+
+
+#define COUNTOF(x)  (sizeof(x)/sizeof(x[0]))
+
+#define DECL(x) { #x, (ALCvoid*)(x) }
+static const struct {
+    const ALCchar *funcName;
+    ALCvoid *address;
+} 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(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),
+};
+#undef DECL
+
+#define DECL(x) { #x, (x) }
+static const struct {
+    const ALCchar *enumName;
+    ALCenum value;
+} alcEnumerations[] = {
+    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_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_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_STEREO8),
+    DECL(AL_FORMAT_STEREO16),
+
+    DECL(AL_FREQUENCY),
+    DECL(AL_BITS),
+    DECL(AL_CHANNELS),
+    DECL(AL_SIZE),
+
+    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_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),
+};
+#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";
+static const ALCchar alcExtensionList[] =
+    "ALC_ENUMERATE_ALL_EXT ALC_ENUMERATION_EXT ALC_EXT_CAPTURE "
+    "ALC_EXT_thread_local_context";
+
+static const ALCint alcMajorVersion = 1;
+static const ALCint alcMinorVersion = 1;
+
+
+static almtx_t EnumerationLock;
+static almtx_t ContextSwitchLock;
+
+static ATOMIC(ALCenum) LastError = ATOMIC_INIT_STATIC(ALC_NO_ERROR);
+static PtrIntMap DeviceIfaceMap = PTRINTMAP_STATIC_INITIALIZE;
+static PtrIntMap ContextIfaceMap = PTRINTMAP_STATIC_INITIALIZE;
+
+
+typedef struct EnumeratedList {
+    ALCchar *Names;
+    ALCchar *NamesEnd;
+    ALCint *Indicies;
+    ALCsizei IndexSize;
+} EnumeratedList;
+static EnumeratedList DevicesList = { NULL, NULL, NULL, 0 };
+static EnumeratedList AllDevicesList = { NULL, NULL, NULL, 0 };
+static EnumeratedList CaptureDevicesList = { NULL, NULL, NULL, 0 };
+
+static void ClearDeviceList(EnumeratedList *list)
+{
+    al_free(list->Names);
+    list->Names = NULL;
+    list->NamesEnd = NULL;
+
+    al_free(list->Indicies);
+    list->Indicies = NULL;
+    list->IndexSize = 0;
+}
+
+static void AppendDeviceList(EnumeratedList *list, const ALCchar *names, ALint idx)
+{
+    const ALCchar *name_end = names;
+    ALCsizei count = 0;
+    ALCchar *new_list;
+    ALCint *new_indicies;
+    size_t len;
+    ALCsizei i;
+
+    if(!name_end)
+        return;
+    while(*name_end)
+    {
+        TRACE("Enumerated \"%s\", driver %d\n", name_end, idx);
+        count++;
+        name_end += strlen(name_end)+1;
+    }
+    if(names == name_end)
+        return;
+
+    len = (list->NamesEnd - list->Names) + (name_end - names);
+    new_list = al_calloc(DEF_ALIGN, len + 1);
+    memcpy(new_list,  list->Names, list->NamesEnd - list->Names);
+    memcpy(new_list + (list->NamesEnd - list->Names), names, name_end - names);
+    al_free(list->Names);
+    list->Names = new_list;
+    list->NamesEnd = list->Names + len;
+
+    new_indicies = al_calloc(16, sizeof(ALCint)*(list->IndexSize + count));
+    for(i = 0;i < list->IndexSize;i++)
+        new_indicies[i] = list->Indicies[i];
+    for(i = 0;i < count;i++)
+        new_indicies[list->IndexSize+i] = idx;
+    al_free(list->Indicies);
+    list->Indicies = new_indicies;
+    list->IndexSize += count;
+}
+
+static ALint GetDriverIndexForName(const EnumeratedList *list, const ALCchar *name)
+{
+    const ALCchar *devnames = list->Names;
+    const ALCint *index = list->Indicies;
+
+    while(devnames && *devnames)
+    {
+        if(strcmp(name, devnames) == 0)
+            return *index;
+        devnames += strlen(devnames)+1;
+        index++;
+    }
+    return -1;
+}
+
+void InitALC(void)
+{
+    almtx_init(&EnumerationLock, almtx_recursive);
+    almtx_init(&ContextSwitchLock, almtx_plain);
+}
+
+void ReleaseALC(void)
+{
+    ClearDeviceList(&DevicesList);
+    ClearDeviceList(&AllDevicesList);
+    ClearDeviceList(&CaptureDevicesList);
+
+    ResetPtrIntMap(&ContextIfaceMap);
+    ResetPtrIntMap(&DeviceIfaceMap);
+
+    almtx_destroy(&ContextSwitchLock);
+    almtx_destroy(&EnumerationLock);
+}
+
+
+ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *devicename)
+{
+    ALCdevice *device = NULL;
+    ALint idx;
+
+    /* Prior to the enumeration extension, apps would hardcode these names as a
+     * quality hint for the wrapper driver. Ignore them since there's no sane
+     * way to map them.
+     */
+    if(devicename && (devicename[0] == '\0' ||
+                      strcmp(devicename, "DirectSound3D") == 0 ||
+                      strcmp(devicename, "DirectSound") == 0 ||
+                      strcmp(devicename, "MMSYSTEM") == 0))
+        devicename = NULL;
+    if(devicename)
+    {
+        almtx_lock(&EnumerationLock);
+        if(!DevicesList.Names)
+            (void)alcGetString(NULL, ALC_DEVICE_SPECIFIER);
+        idx = GetDriverIndexForName(&DevicesList, devicename);
+        if(idx < 0)
+        {
+            if(!AllDevicesList.Names)
+                (void)alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER);
+            idx = GetDriverIndexForName(&AllDevicesList, devicename);
+        }
+        almtx_unlock(&EnumerationLock);
+        if(idx < 0)
+        {
+            ATOMIC_STORE_SEQ(&LastError, ALC_INVALID_VALUE);
+            TRACE("Failed to find driver for name \"%s\"\n", devicename);
+            return NULL;
+        }
+        TRACE("Found driver %d for name \"%s\"\n", idx, devicename);
+        device = DriverList[idx].alcOpenDevice(devicename);
+    }
+    else
+    {
+        int i;
+        for(i = 0;i < DriverListSize;i++)
+        {
+            if(DriverList[i].ALCVer >= MAKE_ALC_VER(1, 1) ||
+               DriverList[i].alcIsExtensionPresent(NULL, "ALC_ENUMERATION_EXT"))
+            {
+                idx = i;
+                TRACE("Using default device from driver %d\n", idx);
+                device = DriverList[idx].alcOpenDevice(NULL);
+                break;
+            }
+        }
+    }
+
+    if(device)
+    {
+        if(InsertPtrIntMapEntry(&DeviceIfaceMap, device, idx) != ALC_NO_ERROR)
+        {
+            DriverList[idx].alcCloseDevice(device);
+            device = NULL;
+        }
+    }
+
+    return device;
+}
+
+ALC_API ALCboolean ALC_APIENTRY alcCloseDevice(ALCdevice *device)
+{
+    ALint idx;
+
+    if(!device || (idx=LookupPtrIntMapKey(&DeviceIfaceMap, device)) < 0)
+    {
+        ATOMIC_STORE_SEQ(&LastError, ALC_INVALID_DEVICE);
+        return ALC_FALSE;
+    }
+    if(!DriverList[idx].alcCloseDevice(device))
+        return ALC_FALSE;
+    RemovePtrIntMapKey(&DeviceIfaceMap, device);
+    return ALC_TRUE;
+}
+
+
+ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCint *attrlist)
+{
+    ALCcontext *context;
+    ALint idx;
+
+    if(!device || (idx=LookupPtrIntMapKey(&DeviceIfaceMap, device)) < 0)
+    {
+        ATOMIC_STORE_SEQ(&LastError, ALC_INVALID_DEVICE);
+        return ALC_FALSE;
+    }
+    context = DriverList[idx].alcCreateContext(device, attrlist);
+    if(context)
+    {
+        if(InsertPtrIntMapEntry(&ContextIfaceMap, context, idx) != ALC_NO_ERROR)
+        {
+            DriverList[idx].alcDestroyContext(context);
+            context = NULL;
+        }
+    }
+
+    return context;
+}
+
+ALC_API ALCboolean ALC_APIENTRY alcMakeContextCurrent(ALCcontext *context)
+{
+    ALint idx = -1;
+
+    almtx_lock(&ContextSwitchLock);
+    if(context)
+    {
+        idx = LookupPtrIntMapKey(&ContextIfaceMap, context);
+        if(idx < 0)
+        {
+            ATOMIC_STORE_SEQ(&LastError, ALC_INVALID_CONTEXT);
+            almtx_unlock(&ContextSwitchLock);
+            return ALC_FALSE;
+        }
+        if(!DriverList[idx].alcMakeContextCurrent(context))
+        {
+            almtx_unlock(&ContextSwitchLock);
+            return ALC_FALSE;
+        }
+    }
+
+    /* Unset the context from the old driver if it's different from the new
+     * current one.
+     */
+    if(idx < 0)
+    {
+        DriverIface *oldiface = altss_get(ThreadCtxDriver);
+        if(oldiface) oldiface->alcSetThreadContext(NULL);
+        oldiface = ATOMIC_EXCHANGE_PTR_SEQ(&CurrentCtxDriver, NULL);
+        if(oldiface) oldiface->alcMakeContextCurrent(NULL);
+    }
+    else
+    {
+        DriverIface *oldiface = altss_get(ThreadCtxDriver);
+        if(oldiface && oldiface != &DriverList[idx])
+            oldiface->alcSetThreadContext(NULL);
+        oldiface = ATOMIC_EXCHANGE_PTR_SEQ(&CurrentCtxDriver, &DriverList[idx]);
+        if(oldiface && oldiface != &DriverList[idx])
+            oldiface->alcMakeContextCurrent(NULL);
+    }
+    almtx_unlock(&ContextSwitchLock);
+    altss_set(ThreadCtxDriver, NULL);
+
+    return ALC_TRUE;
+}
+
+ALC_API void ALC_APIENTRY alcProcessContext(ALCcontext *context)
+{
+    if(context)
+    {
+        ALint idx = LookupPtrIntMapKey(&ContextIfaceMap, context);
+        if(idx >= 0)
+            return DriverList[idx].alcProcessContext(context);
+    }
+    ATOMIC_STORE_SEQ(&LastError, ALC_INVALID_CONTEXT);
+}
+
+ALC_API void ALC_APIENTRY alcSuspendContext(ALCcontext *context)
+{
+    if(context)
+    {
+        ALint idx = LookupPtrIntMapKey(&ContextIfaceMap, context);
+        if(idx >= 0)
+            return DriverList[idx].alcSuspendContext(context);
+    }
+    ATOMIC_STORE_SEQ(&LastError, ALC_INVALID_CONTEXT);
+}
+
+ALC_API void ALC_APIENTRY alcDestroyContext(ALCcontext *context)
+{
+    ALint idx;
+
+    if(!context || (idx=LookupPtrIntMapKey(&ContextIfaceMap, context)) < 0)
+    {
+        ATOMIC_STORE_SEQ(&LastError, ALC_INVALID_CONTEXT);
+        return;
+    }
+
+    DriverList[idx].alcDestroyContext(context);
+    RemovePtrIntMapKey(&ContextIfaceMap, context);
+}
+
+ALC_API ALCcontext* ALC_APIENTRY alcGetCurrentContext(void)
+{
+    DriverIface *iface = altss_get(ThreadCtxDriver);
+    if(!iface) iface = ATOMIC_LOAD_SEQ(&CurrentCtxDriver);
+    return iface ? iface->alcGetCurrentContext() : NULL;
+}
+
+ALC_API ALCdevice* ALC_APIENTRY alcGetContextsDevice(ALCcontext *context)
+{
+    if(context)
+    {
+        ALint idx = LookupPtrIntMapKey(&ContextIfaceMap, context);
+        if(idx >= 0)
+            return DriverList[idx].alcGetContextsDevice(context);
+    }
+    ATOMIC_STORE_SEQ(&LastError, ALC_INVALID_CONTEXT);
+    return NULL;
+}
+
+
+ALC_API ALCenum ALC_APIENTRY alcGetError(ALCdevice *device)
+{
+    if(device)
+    {
+        ALint idx = LookupPtrIntMapKey(&DeviceIfaceMap, device);
+        if(idx < 0) return ALC_INVALID_DEVICE;
+        return DriverList[idx].alcGetError(device);
+    }
+    return ATOMIC_EXCHANGE_SEQ(&LastError, ALC_NO_ERROR);
+}
+
+ALC_API ALCboolean ALC_APIENTRY alcIsExtensionPresent(ALCdevice *device, const ALCchar *extname)
+{
+    const char *ptr;
+    size_t len;
+
+    if(device)
+    {
+        ALint idx = LookupPtrIntMapKey(&DeviceIfaceMap, device);
+        if(idx < 0)
+        {
+            ATOMIC_STORE_SEQ(&LastError, ALC_INVALID_DEVICE);
+            return ALC_FALSE;
+        }
+        return DriverList[idx].alcIsExtensionPresent(device, extname);
+    }
+
+    len = strlen(extname);
+    ptr = alcExtensionList;
+    while(ptr && *ptr)
+    {
+        if(strncasecmp(ptr, extname, len) == 0 && (ptr[len] == '\0' || isspace(ptr[len])))
+            return ALC_TRUE;
+        if((ptr=strchr(ptr, ' ')) != NULL)
+        {
+            do {
+                ++ptr;
+            } while(isspace(*ptr));
+        }
+    }
+    return ALC_FALSE;
+}
+
+ALC_API void* ALC_APIENTRY alcGetProcAddress(ALCdevice *device, const ALCchar *funcname)
+{
+    size_t i;
+
+    if(device)
+    {
+        ALint idx = LookupPtrIntMapKey(&DeviceIfaceMap, device);
+        if(idx < 0)
+        {
+            ATOMIC_STORE_SEQ(&LastError, ALC_INVALID_DEVICE);
+            return NULL;
+        }
+        return DriverList[idx].alcGetProcAddress(device, funcname);
+    }
+
+    for(i = 0;i < COUNTOF(alcFunctions);i++)
+    {
+        if(strcmp(funcname, alcFunctions[i].funcName) == 0)
+            return alcFunctions[i].address;
+    }
+    return NULL;
+}
+
+ALC_API ALCenum ALC_APIENTRY alcGetEnumValue(ALCdevice *device, const ALCchar *enumname)
+{
+    size_t i;
+
+    if(device)
+    {
+        ALint idx = LookupPtrIntMapKey(&DeviceIfaceMap, device);
+        if(idx < 0)
+        {
+            ATOMIC_STORE_SEQ(&LastError, ALC_INVALID_DEVICE);
+            return 0;
+        }
+        return DriverList[idx].alcGetEnumValue(device, enumname);
+    }
+
+    for(i = 0;i < COUNTOF(alcEnumerations);i++)
+    {
+        if(strcmp(enumname, alcEnumerations[i].enumName) == 0)
+            return alcEnumerations[i].value;
+    }
+    return 0;
+}
+
+ALC_API const ALCchar* ALC_APIENTRY alcGetString(ALCdevice *device, ALCenum param)
+{
+    ALsizei i = 0;
+
+    if(device)
+    {
+        ALint idx = LookupPtrIntMapKey(&DeviceIfaceMap, device);
+        if(idx < 0)
+        {
+            ATOMIC_STORE_SEQ(&LastError, ALC_INVALID_DEVICE);
+            return NULL;
+        }
+        return DriverList[idx].alcGetString(device, param);
+    }
+
+    switch(param)
+    {
+    case ALC_NO_ERROR:
+        return alcNoError;
+    case ALC_INVALID_ENUM:
+        return alcErrInvalidEnum;
+    case ALC_INVALID_VALUE:
+        return alcErrInvalidValue;
+    case ALC_INVALID_DEVICE:
+        return alcErrInvalidDevice;
+    case ALC_INVALID_CONTEXT:
+        return alcErrInvalidContext;
+    case ALC_OUT_OF_MEMORY:
+        return alcErrOutOfMemory;
+    case ALC_EXTENSIONS:
+        return alcExtensionList;
+
+    case ALC_DEVICE_SPECIFIER:
+        almtx_lock(&EnumerationLock);
+        ClearDeviceList(&DevicesList);
+        for(i = 0;i < DriverListSize;i++)
+        {
+            /* Only enumerate names from drivers that support it. */
+            if(DriverList[i].ALCVer >= MAKE_ALC_VER(1, 1) ||
+               DriverList[i].alcIsExtensionPresent(NULL, "ALC_ENUMERATION_EXT"))
+                AppendDeviceList(&DevicesList,
+                    DriverList[i].alcGetString(NULL, ALC_DEVICE_SPECIFIER), i
+                );
+        }
+        almtx_unlock(&EnumerationLock);
+        return DevicesList.Names;
+
+    case ALC_ALL_DEVICES_SPECIFIER:
+        almtx_lock(&EnumerationLock);
+        ClearDeviceList(&AllDevicesList);
+        for(i = 0;i < DriverListSize;i++)
+        {
+            /* If the driver doesn't support ALC_ENUMERATE_ALL_EXT, substitute
+             * standard enumeration.
+             */
+            if(DriverList[i].alcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT"))
+                AppendDeviceList(&AllDevicesList,
+                    DriverList[i].alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER), i
+                );
+            else if(DriverList[i].ALCVer >= MAKE_ALC_VER(1, 1) ||
+                    DriverList[i].alcIsExtensionPresent(NULL, "ALC_ENUMERATION_EXT"))
+                AppendDeviceList(&AllDevicesList,
+                    DriverList[i].alcGetString(NULL, ALC_DEVICE_SPECIFIER), i
+                );
+        }
+        almtx_unlock(&EnumerationLock);
+        return AllDevicesList.Names;
+
+    case ALC_CAPTURE_DEVICE_SPECIFIER:
+        almtx_lock(&EnumerationLock);
+        ClearDeviceList(&CaptureDevicesList);
+        for(i = 0;i < DriverListSize;i++)
+        {
+            if(DriverList[i].ALCVer >= MAKE_ALC_VER(1, 1) ||
+               DriverList[i].alcIsExtensionPresent(NULL, "ALC_EXT_CAPTURE"))
+                AppendDeviceList(&CaptureDevicesList,
+                    DriverList[i].alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER), i
+                );
+        }
+        almtx_unlock(&EnumerationLock);
+        return CaptureDevicesList.Names;
+
+    case ALC_DEFAULT_DEVICE_SPECIFIER:
+        for(i = 0;i < DriverListSize;i++)
+        {
+            if(DriverList[i].ALCVer >= MAKE_ALC_VER(1, 1) ||
+               DriverList[i].alcIsExtensionPresent(NULL, "ALC_ENUMERATION_EXT"))
+                return DriverList[i].alcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER);
+        }
+        return "";
+
+    case ALC_DEFAULT_ALL_DEVICES_SPECIFIER:
+        for(i = 0;i < DriverListSize;i++)
+        {
+            if(DriverList[i].alcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT"))
+                return DriverList[i].alcGetString(NULL, ALC_DEFAULT_ALL_DEVICES_SPECIFIER);
+        }
+        return "";
+
+    case ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER:
+        for(i = 0;i < DriverListSize;i++)
+        {
+            if(DriverList[i].ALCVer >= MAKE_ALC_VER(1, 1) ||
+               DriverList[i].alcIsExtensionPresent(NULL, "ALC_EXT_CAPTURE"))
+                return DriverList[i].alcGetString(NULL, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER);
+        }
+        return "";
+
+    default:
+        ATOMIC_STORE_SEQ(&LastError, ALC_INVALID_ENUM);
+        break;
+    }
+    return NULL;
+}
+
+ALC_API void ALC_APIENTRY alcGetIntegerv(ALCdevice *device, ALCenum param, ALCsizei size, ALCint *values)
+{
+    if(device)
+    {
+        ALint idx = LookupPtrIntMapKey(&DeviceIfaceMap, device);
+        if(idx < 0)
+        {
+            ATOMIC_STORE_SEQ(&LastError, ALC_INVALID_DEVICE);
+            return;
+        }
+        return DriverList[idx].alcGetIntegerv(device, param, size, values);
+    }
+
+    if(size <= 0 || values == NULL)
+    {
+        ATOMIC_STORE_SEQ(&LastError, ALC_INVALID_VALUE);
+        return;
+    }
+
+    switch(param)
+    {
+        case ALC_MAJOR_VERSION:
+            if(size >= 1)
+            {
+                values[0] = alcMajorVersion;
+                return;
+            }
+            /*fall-through*/
+        case ALC_MINOR_VERSION:
+            if(size >= 1)
+            {
+                values[0] = alcMinorVersion;
+                return;
+            }
+            ATOMIC_STORE_SEQ(&LastError, ALC_INVALID_VALUE);
+            return;
+
+        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:
+            ATOMIC_STORE_SEQ(&LastError, ALC_INVALID_DEVICE);
+            return;
+
+        default:
+            ATOMIC_STORE_SEQ(&LastError, ALC_INVALID_ENUM);
+            return;
+    }
+}
+
+
+ALC_API ALCdevice* ALC_APIENTRY alcCaptureOpenDevice(const ALCchar *devicename, ALCuint frequency, ALCenum format, ALCsizei buffersize)
+{
+    ALCdevice *device = NULL;
+    ALint idx;
+
+    if(devicename && devicename[0] == '\0')
+        devicename = NULL;
+    if(devicename)
+    {
+        almtx_lock(&EnumerationLock);
+        if(!CaptureDevicesList.Names)
+            (void)alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER);
+        idx = GetDriverIndexForName(&CaptureDevicesList, devicename);
+        almtx_unlock(&EnumerationLock);
+        if(idx < 0)
+        {
+            ATOMIC_STORE_SEQ(&LastError, ALC_INVALID_VALUE);
+            TRACE("Failed to find driver for name \"%s\"\n", devicename);
+            return NULL;
+        }
+        TRACE("Found driver %d for name \"%s\"\n", idx, devicename);
+        device = DriverList[idx].alcCaptureOpenDevice(
+            devicename, frequency, format, buffersize
+        );
+    }
+    else
+    {
+        int i;
+        for(i = 0;i < DriverListSize;i++)
+        {
+            if(DriverList[i].ALCVer >= MAKE_ALC_VER(1, 1) ||
+               DriverList[i].alcIsExtensionPresent(NULL, "ALC_EXT_CAPTURE"))
+            {
+                idx = i;
+                TRACE("Using default capture device from driver %d\n", idx);
+                device = DriverList[idx].alcCaptureOpenDevice(
+                    NULL, frequency, format, buffersize
+                );
+                break;
+            }
+        }
+    }
+
+    if(device)
+    {
+        if(InsertPtrIntMapEntry(&DeviceIfaceMap, device, idx) != ALC_NO_ERROR)
+        {
+            DriverList[idx].alcCaptureCloseDevice(device);
+            device = NULL;
+        }
+    }
+
+    return device;
+}
+
+ALC_API ALCboolean ALC_APIENTRY alcCaptureCloseDevice(ALCdevice *device)
+{
+    ALint idx;
+
+    if(!device || (idx=LookupPtrIntMapKey(&DeviceIfaceMap, device)) < 0)
+    {
+        ATOMIC_STORE_SEQ(&LastError, ALC_INVALID_DEVICE);
+        return ALC_FALSE;
+    }
+    if(!DriverList[idx].alcCaptureCloseDevice(device))
+        return ALC_FALSE;
+    RemovePtrIntMapKey(&DeviceIfaceMap, device);
+    return ALC_TRUE;
+}
+
+ALC_API void ALC_APIENTRY alcCaptureStart(ALCdevice *device)
+{
+    if(device)
+    {
+        ALint idx = LookupPtrIntMapKey(&DeviceIfaceMap, device);
+        if(idx >= 0)
+            return DriverList[idx].alcCaptureStart(device);
+    }
+    ATOMIC_STORE_SEQ(&LastError, ALC_INVALID_DEVICE);
+}
+
+ALC_API void ALC_APIENTRY alcCaptureStop(ALCdevice *device)
+{
+    if(device)
+    {
+        ALint idx = LookupPtrIntMapKey(&DeviceIfaceMap, device);
+        if(idx >= 0)
+            return DriverList[idx].alcCaptureStop(device);
+    }
+    ATOMIC_STORE_SEQ(&LastError, ALC_INVALID_DEVICE);
+}
+
+ALC_API void ALC_APIENTRY alcCaptureSamples(ALCdevice *device, ALCvoid *buffer, ALCsizei samples)
+{
+    if(device)
+    {
+        ALint idx = LookupPtrIntMapKey(&DeviceIfaceMap, device);
+        if(idx >= 0)
+            return DriverList[idx].alcCaptureSamples(device, buffer, samples);
+    }
+    ATOMIC_STORE_SEQ(&LastError, ALC_INVALID_DEVICE);
+}
+
+
+ALC_API ALCboolean ALC_APIENTRY alcSetThreadContext(ALCcontext *context)
+{
+    ALCenum err = ALC_INVALID_CONTEXT;
+    ALint idx;
+
+    if(!context)
+    {
+        DriverIface *oldiface = altss_get(ThreadCtxDriver);
+        if(oldiface && !oldiface->alcSetThreadContext(NULL))
+            return ALC_FALSE;
+        altss_set(ThreadCtxDriver, NULL);
+        return ALC_TRUE;
+    }
+
+    idx = LookupPtrIntMapKey(&ContextIfaceMap, context);
+    if(idx >= 0)
+    {
+        if(DriverList[idx].alcSetThreadContext(context))
+        {
+            DriverIface *oldiface = altss_get(ThreadCtxDriver);
+            if(oldiface != &DriverList[idx])
+            {
+                altss_set(ThreadCtxDriver, &DriverList[idx]);
+                if(oldiface) oldiface->alcSetThreadContext(NULL);
+            }
+            return ALC_TRUE;
+        }
+        err = DriverList[idx].alcGetError(NULL);
+    }
+    ATOMIC_STORE_SEQ(&LastError, err);
+    return ALC_FALSE;
+}
+
+ALC_API ALCcontext* ALC_APIENTRY alcGetThreadContext(void)
+{
+    DriverIface *iface = altss_get(ThreadCtxDriver);
+    if(iface) return iface->alcGetThreadContext();
+    return NULL;
+}
diff --git a/router/router.c b/router/router.c
new file mode 100644
index 0000000..0497a1d
--- /dev/null
+++ b/router/router.c
@@ -0,0 +1,512 @@
+
+#include "config.h"
+
+#include "router.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "AL/alc.h"
+#include "AL/al.h"
+#include "almalloc.h"
+
+#include "version.h"
+
+
+DriverIface *DriverList = NULL;
+int DriverListSize = 0;
+static int DriverListSizeMax = 0;
+
+altss_t ThreadCtxDriver;
+
+enum LogLevel LogLevel = LogLevel_Error;
+FILE *LogFile;
+
+static void LoadDriverList(void);
+
+
+BOOL APIENTRY DllMain(HINSTANCE UNUSED(module), DWORD reason, void* UNUSED(reserved))
+{
+    const char *str;
+    int i;
+
+    switch(reason)
+    {
+        case DLL_PROCESS_ATTACH:
+            LogFile = stderr;
+            str = getenv("ALROUTER_LOGFILE");
+            if(str && *str != '\0')
+            {
+                FILE *f = fopen(str, "w");
+                if(f == NULL)
+                    ERR("Could not open log file: %s\n", str);
+                else
+                    LogFile = f;
+            }
+            str = getenv("ALROUTER_LOGLEVEL");
+            if(str && *str != '\0')
+            {
+                char *end = NULL;
+                long l = strtol(str, &end, 0);
+                if(!end || *end != '\0')
+                    ERR("Invalid log level value: %s\n", str);
+                else if(l < LogLevel_None || l > LogLevel_Trace)
+                    ERR("Log level out of range: %s\n", str);
+                else
+                    LogLevel = l;
+            }
+            TRACE("Initializing router v0.1-%s %s\n", ALSOFT_GIT_COMMIT_HASH, ALSOFT_GIT_BRANCH);
+            LoadDriverList();
+
+            altss_create(&ThreadCtxDriver, NULL);
+            InitALC();
+            break;
+
+        case DLL_THREAD_ATTACH:
+        case DLL_THREAD_DETACH:
+            break;
+
+        case DLL_PROCESS_DETACH:
+            ReleaseALC();
+            altss_delete(ThreadCtxDriver);
+
+            for(i = 0;i < DriverListSize;i++)
+            {
+                if(DriverList[i].Module)
+                    FreeLibrary(DriverList[i].Module);
+            }
+            al_free(DriverList);
+            DriverList = NULL;
+            DriverListSize = 0;
+            DriverListSizeMax = 0;
+
+            if(LogFile && LogFile != stderr)
+                fclose(LogFile);
+            LogFile = NULL;
+            break;
+    }
+    return TRUE;
+}
+
+
+#ifdef __GNUC__
+#define CAST_FUNC(x) (__typeof(x))
+#else
+#define CAST_FUNC(x) (void*)
+#endif
+
+static void AddModule(HMODULE module, const WCHAR *name)
+{
+    DriverIface newdrv;
+    int err = 0;
+    int i;
+
+    for(i = 0;i < DriverListSize;i++)
+    {
+        if(DriverList[i].Module == module)
+        {
+            TRACE("Skipping already-loaded module %p\n", module);
+            FreeLibrary(module);
+            return;
+        }
+        if(wcscmp(DriverList[i].Name, name) == 0)
+        {
+            TRACE("Skipping similarly-named module %ls\n", name);
+            FreeLibrary(module);
+            return;
+        }
+    }
+
+    if(DriverListSize == DriverListSizeMax)
+    {
+        int newmax = DriverListSizeMax ? DriverListSizeMax<<1 : 4;
+        void *newlist = al_calloc(DEF_ALIGN, sizeof(DriverList[0])*newmax);
+        if(!newlist) return;
+
+        memcpy(newlist, DriverList, DriverListSize*sizeof(DriverList[0]));
+        al_free(DriverList);
+        DriverList = newlist;
+        DriverListSizeMax = newmax;
+    }
+
+    memset(&newdrv, 0, sizeof(newdrv));
+    /* Load required functions. */
+#define LOAD_PROC(x) do {                                                     \
+    newdrv.x = CAST_FUNC(newdrv.x) GetProcAddress(module, #x);                \
+    if(!newdrv.x)                                                             \
+    {                                                                         \
+        ERR("Failed to find entry point for %s in %ls\n", #x, name);          \
+        err = 1;                                                              \
+    }                                                                         \
+} while(0)
+    LOAD_PROC(alcCreateContext);
+    LOAD_PROC(alcMakeContextCurrent);
+    LOAD_PROC(alcProcessContext);
+    LOAD_PROC(alcSuspendContext);
+    LOAD_PROC(alcDestroyContext);
+    LOAD_PROC(alcGetCurrentContext);
+    LOAD_PROC(alcGetContextsDevice);
+    LOAD_PROC(alcOpenDevice);
+    LOAD_PROC(alcCloseDevice);
+    LOAD_PROC(alcGetError);
+    LOAD_PROC(alcIsExtensionPresent);
+    LOAD_PROC(alcGetProcAddress);
+    LOAD_PROC(alcGetEnumValue);
+    LOAD_PROC(alcGetString);
+    LOAD_PROC(alcGetIntegerv);
+    LOAD_PROC(alcCaptureOpenDevice);
+    LOAD_PROC(alcCaptureCloseDevice);
+    LOAD_PROC(alcCaptureStart);
+    LOAD_PROC(alcCaptureStop);
+    LOAD_PROC(alcCaptureSamples);
+
+    LOAD_PROC(alEnable);
+    LOAD_PROC(alDisable);
+    LOAD_PROC(alIsEnabled);
+    LOAD_PROC(alGetString);
+    LOAD_PROC(alGetBooleanv);
+    LOAD_PROC(alGetIntegerv);
+    LOAD_PROC(alGetFloatv);
+    LOAD_PROC(alGetDoublev);
+    LOAD_PROC(alGetBoolean);
+    LOAD_PROC(alGetInteger);
+    LOAD_PROC(alGetFloat);
+    LOAD_PROC(alGetDouble);
+    LOAD_PROC(alGetError);
+    LOAD_PROC(alIsExtensionPresent);
+    LOAD_PROC(alGetProcAddress);
+    LOAD_PROC(alGetEnumValue);
+    LOAD_PROC(alListenerf);
+    LOAD_PROC(alListener3f);
+    LOAD_PROC(alListenerfv);
+    LOAD_PROC(alListeneri);
+    LOAD_PROC(alListener3i);
+    LOAD_PROC(alListeneriv);
+    LOAD_PROC(alGetListenerf);
+    LOAD_PROC(alGetListener3f);
+    LOAD_PROC(alGetListenerfv);
+    LOAD_PROC(alGetListeneri);
+    LOAD_PROC(alGetListener3i);
+    LOAD_PROC(alGetListeneriv);
+    LOAD_PROC(alGenSources);
+    LOAD_PROC(alDeleteSources);
+    LOAD_PROC(alIsSource);
+    LOAD_PROC(alSourcef);
+    LOAD_PROC(alSource3f);
+    LOAD_PROC(alSourcefv);
+    LOAD_PROC(alSourcei);
+    LOAD_PROC(alSource3i);
+    LOAD_PROC(alSourceiv);
+    LOAD_PROC(alGetSourcef);
+    LOAD_PROC(alGetSource3f);
+    LOAD_PROC(alGetSourcefv);
+    LOAD_PROC(alGetSourcei);
+    LOAD_PROC(alGetSource3i);
+    LOAD_PROC(alGetSourceiv);
+    LOAD_PROC(alSourcePlayv);
+    LOAD_PROC(alSourceStopv);
+    LOAD_PROC(alSourceRewindv);
+    LOAD_PROC(alSourcePausev);
+    LOAD_PROC(alSourcePlay);
+    LOAD_PROC(alSourceStop);
+    LOAD_PROC(alSourceRewind);
+    LOAD_PROC(alSourcePause);
+    LOAD_PROC(alSourceQueueBuffers);
+    LOAD_PROC(alSourceUnqueueBuffers);
+    LOAD_PROC(alGenBuffers);
+    LOAD_PROC(alDeleteBuffers);
+    LOAD_PROC(alIsBuffer);
+    LOAD_PROC(alBufferf);
+    LOAD_PROC(alBuffer3f);
+    LOAD_PROC(alBufferfv);
+    LOAD_PROC(alBufferi);
+    LOAD_PROC(alBuffer3i);
+    LOAD_PROC(alBufferiv);
+    LOAD_PROC(alGetBufferf);
+    LOAD_PROC(alGetBuffer3f);
+    LOAD_PROC(alGetBufferfv);
+    LOAD_PROC(alGetBufferi);
+    LOAD_PROC(alGetBuffer3i);
+    LOAD_PROC(alGetBufferiv);
+    LOAD_PROC(alBufferData);
+    LOAD_PROC(alDopplerFactor);
+    LOAD_PROC(alDopplerVelocity);
+    LOAD_PROC(alSpeedOfSound);
+    LOAD_PROC(alDistanceModel);
+    if(!err)
+    {
+        ALCint alc_ver[2] = { 0, 0 };
+        wcsncpy(newdrv.Name, name, 32);
+        newdrv.Module = module;
+        newdrv.alcGetIntegerv(NULL, ALC_MAJOR_VERSION, 1, &alc_ver[0]);
+        newdrv.alcGetIntegerv(NULL, ALC_MINOR_VERSION, 1, &alc_ver[1]);
+        if(newdrv.alcGetError(NULL) == ALC_NO_ERROR)
+            newdrv.ALCVer = MAKE_ALC_VER(alc_ver[0], alc_ver[1]);
+        else
+            newdrv.ALCVer = MAKE_ALC_VER(1, 0);
+
+#undef LOAD_PROC
+#define LOAD_PROC(x) do {                                                     \
+    newdrv.x = CAST_FUNC(newdrv.x) newdrv.alcGetProcAddress(NULL, #x);        \
+    if(!newdrv.x)                                                             \
+    {                                                                         \
+        ERR("Failed to find entry point for %s in %ls\n", #x, name);          \
+        err = 1;                                                              \
+    }                                                                         \
+} while(0)
+        if(newdrv.alcIsExtensionPresent(NULL, "ALC_EXT_thread_local_context"))
+        {
+            LOAD_PROC(alcSetThreadContext);
+            LOAD_PROC(alcGetThreadContext);
+        }
+    }
+
+    if(!err)
+    {
+        TRACE("Loaded module %p, %ls, ALC %d.%d\n", module, name,
+              newdrv.ALCVer>>8, newdrv.ALCVer&255);
+        DriverList[DriverListSize++] = newdrv;
+    }
+#undef LOAD_PROC
+}
+
+static void SearchDrivers(WCHAR *path)
+{
+    WCHAR srchPath[MAX_PATH+1] = L"";
+    WIN32_FIND_DATAW fdata;
+    HANDLE srchHdl;
+
+    TRACE("Searching for drivers in %ls...\n", path);
+    wcsncpy(srchPath, path, MAX_PATH);
+    wcsncat(srchPath, L"\\*oal.dll", MAX_PATH - lstrlenW(srchPath));
+    srchHdl = FindFirstFileW(srchPath, &fdata);
+    if(srchHdl != INVALID_HANDLE_VALUE)
+    {
+        do {
+            HMODULE mod;
+
+            wcsncpy(srchPath, path, MAX_PATH);
+            wcsncat(srchPath, L"\\", MAX_PATH - lstrlenW(srchPath));
+            wcsncat(srchPath, fdata.cFileName, MAX_PATH - lstrlenW(srchPath));
+            TRACE("Found %ls\n", srchPath);
+
+            mod = LoadLibraryW(srchPath);
+            if(!mod)
+                WARN("Could not load %ls\n", srchPath);
+            else
+                AddModule(mod, fdata.cFileName);
+        } while(FindNextFileW(srchHdl, &fdata));
+        FindClose(srchHdl);
+    }
+}
+
+static WCHAR *strrchrW(WCHAR *str, WCHAR ch)
+{
+    WCHAR *res = NULL;
+    while(str && *str != '\0')
+    {
+        if(*str == ch)
+            res = str;
+        ++str;
+    }
+    return res;
+}
+
+static int GetLoadedModuleDirectory(const WCHAR *name, WCHAR *moddir, DWORD length)
+{
+    HMODULE module = NULL;
+    WCHAR *sep0, *sep1;
+
+    if(name)
+    {
+        module = GetModuleHandleW(name);
+        if(!module) return 0;
+    }
+
+    if(GetModuleFileNameW(module, moddir, length) == 0)
+        return 0;
+
+    sep0 = strrchrW(moddir, '/');
+    if(sep0) sep1 = strrchrW(sep0+1, '\\');
+    else sep1 = strrchrW(moddir, '\\');
+
+    if(sep1) *sep1 = '\0';
+    else if(sep0) *sep0 = '\0';
+    else *moddir = '\0';
+
+    return 1;
+}
+
+void LoadDriverList(void)
+{
+    WCHAR path[MAX_PATH+1] = L"";
+    int len;
+
+    if(GetLoadedModuleDirectory(L"OpenAL32.dll", path, MAX_PATH))
+        SearchDrivers(path);
+
+    GetCurrentDirectoryW(MAX_PATH, path);
+    len = lstrlenW(path);
+    if(len > 0 && (path[len-1] == '\\' || path[len-1] == '/'))
+        path[len-1] = '\0';
+    SearchDrivers(path);
+
+    if(GetLoadedModuleDirectory(NULL, path, MAX_PATH))
+        SearchDrivers(path);
+
+    GetSystemDirectoryW(path, MAX_PATH);
+    len = lstrlenW(path);
+    if(len > 0 && (path[len-1] == '\\' || path[len-1] == '/'))
+        path[len-1] = '\0';
+    SearchDrivers(path);
+}
+
+
+void InitPtrIntMap(PtrIntMap *map)
+{
+    map->keys = NULL;
+    map->values = NULL;
+    map->size = 0;
+    map->capacity = 0;
+    RWLockInit(&map->lock);
+}
+
+void ResetPtrIntMap(PtrIntMap *map)
+{
+    WriteLock(&map->lock);
+    al_free(map->keys);
+    map->keys = NULL;
+    map->values = NULL;
+    map->size = 0;
+    map->capacity = 0;
+    WriteUnlock(&map->lock);
+}
+
+ALenum InsertPtrIntMapEntry(PtrIntMap *map, ALvoid *key, ALint value)
+{
+    ALsizei pos = 0;
+
+    WriteLock(&map->lock);
+    if(map->size > 0)
+    {
+        ALsizei count = map->size;
+        do {
+            ALsizei step = count>>1;
+            ALsizei i = pos+step;
+            if(!(map->keys[i] < key))
+                count = step;
+            else
+            {
+                pos = i+1;
+                count -= step+1;
+            }
+        } while(count > 0);
+    }
+
+    if(pos == map->size || map->keys[pos] != key)
+    {
+        if(map->size == map->capacity)
+        {
+            ALvoid **keys = NULL;
+            ALint *values;
+            ALsizei newcap;
+
+            newcap = (map->capacity ? (map->capacity<<1) : 4);
+            if(newcap > map->capacity)
+                keys = al_calloc(16, (sizeof(map->keys[0])+sizeof(map->values[0]))*newcap);
+            if(!keys)
+            {
+                WriteUnlock(&map->lock);
+                return AL_OUT_OF_MEMORY;
+            }
+            values = (ALint*)&keys[newcap];
+
+            if(map->keys)
+            {
+                memcpy(keys, map->keys, map->size*sizeof(map->keys[0]));
+                memcpy(values, map->values, map->size*sizeof(map->values[0]));
+            }
+            al_free(map->keys);
+            map->keys = keys;
+            map->values = values;
+            map->capacity = newcap;
+        }
+
+        if(pos < map->size)
+        {
+            memmove(&map->keys[pos+1], &map->keys[pos],
+                    (map->size-pos)*sizeof(map->keys[0]));
+            memmove(&map->values[pos+1], &map->values[pos],
+                    (map->size-pos)*sizeof(map->values[0]));
+        }
+        map->size++;
+    }
+    map->keys[pos] = key;
+    map->values[pos] = value;
+    WriteUnlock(&map->lock);
+
+    return AL_NO_ERROR;
+}
+
+ALint RemovePtrIntMapKey(PtrIntMap *map, ALvoid *key)
+{
+    ALint ret = -1;
+    WriteLock(&map->lock);
+    if(map->size > 0)
+    {
+        ALsizei pos = 0;
+        ALsizei count = map->size;
+        do {
+            ALsizei step = count>>1;
+            ALsizei i = pos+step;
+            if(!(map->keys[i] < key))
+                count = step;
+            else
+            {
+                pos = i+1;
+                count -= step+1;
+            }
+        } while(count > 0);
+        if(pos < map->size && map->keys[pos] == key)
+        {
+            ret = map->values[pos];
+            if(pos < map->size-1)
+            {
+                memmove(&map->keys[pos], &map->keys[pos+1],
+                        (map->size-1-pos)*sizeof(map->keys[0]));
+                memmove(&map->values[pos], &map->values[pos+1],
+                        (map->size-1-pos)*sizeof(map->values[0]));
+            }
+            map->size--;
+        }
+    }
+    WriteUnlock(&map->lock);
+    return ret;
+}
+
+ALint LookupPtrIntMapKey(PtrIntMap *map, ALvoid *key)
+{
+    ALint ret = -1;
+    ReadLock(&map->lock);
+    if(map->size > 0)
+    {
+        ALsizei pos = 0;
+        ALsizei count = map->size;
+        do {
+            ALsizei step = count>>1;
+            ALsizei i = pos+step;
+            if(!(map->keys[i] < key))
+                count = step;
+            else
+            {
+                pos = i+1;
+                count -= step+1;
+            }
+        } while(count > 0);
+        if(pos < map->size && map->keys[pos] == key)
+            ret = map->values[pos];
+    }
+    ReadUnlock(&map->lock);
+    return ret;
+}
diff --git a/router/router.h b/router/router.h
new file mode 100644
index 0000000..ee885d8
--- /dev/null
+++ b/router/router.h
@@ -0,0 +1,196 @@
+#ifndef ROUTER_ROUTER_H
+#define ROUTER_ROUTER_H
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <winnt.h>
+
+#include <stdio.h>
+
+#include "AL/alc.h"
+#include "AL/al.h"
+#include "AL/alext.h"
+#include "atomic.h"
+#include "rwlock.h"
+#include "threads.h"
+
+
+#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
+
+#define MAKE_ALC_VER(major, minor) (((major)<<8) | (minor))
+
+typedef struct DriverIface {
+    WCHAR Name[32];
+    HMODULE Module;
+    int ALCVer;
+
+    LPALCCREATECONTEXT alcCreateContext;
+    LPALCMAKECONTEXTCURRENT alcMakeContextCurrent;
+    LPALCPROCESSCONTEXT alcProcessContext;
+    LPALCSUSPENDCONTEXT alcSuspendContext;
+    LPALCDESTROYCONTEXT alcDestroyContext;
+    LPALCGETCURRENTCONTEXT alcGetCurrentContext;
+    LPALCGETCONTEXTSDEVICE alcGetContextsDevice;
+    LPALCOPENDEVICE alcOpenDevice;
+    LPALCCLOSEDEVICE alcCloseDevice;
+    LPALCGETERROR alcGetError;
+    LPALCISEXTENSIONPRESENT alcIsExtensionPresent;
+    LPALCGETPROCADDRESS alcGetProcAddress;
+    LPALCGETENUMVALUE alcGetEnumValue;
+    LPALCGETSTRING alcGetString;
+    LPALCGETINTEGERV alcGetIntegerv;
+    LPALCCAPTUREOPENDEVICE alcCaptureOpenDevice;
+    LPALCCAPTURECLOSEDEVICE alcCaptureCloseDevice;
+    LPALCCAPTURESTART alcCaptureStart;
+    LPALCCAPTURESTOP alcCaptureStop;
+    LPALCCAPTURESAMPLES alcCaptureSamples;
+
+    PFNALCSETTHREADCONTEXTPROC alcSetThreadContext;
+    PFNALCGETTHREADCONTEXTPROC alcGetThreadContext;
+
+    LPALENABLE alEnable;
+    LPALDISABLE alDisable;
+    LPALISENABLED alIsEnabled;
+    LPALGETSTRING alGetString;
+    LPALGETBOOLEANV alGetBooleanv;
+    LPALGETINTEGERV alGetIntegerv;
+    LPALGETFLOATV alGetFloatv;
+    LPALGETDOUBLEV alGetDoublev;
+    LPALGETBOOLEAN alGetBoolean;
+    LPALGETINTEGER alGetInteger;
+    LPALGETFLOAT alGetFloat;
+    LPALGETDOUBLE alGetDouble;
+    LPALGETERROR alGetError;
+    LPALISEXTENSIONPRESENT alIsExtensionPresent;
+    LPALGETPROCADDRESS alGetProcAddress;
+    LPALGETENUMVALUE alGetEnumValue;
+    LPALLISTENERF alListenerf;
+    LPALLISTENER3F alListener3f;
+    LPALLISTENERFV alListenerfv;
+    LPALLISTENERI alListeneri;
+    LPALLISTENER3I alListener3i;
+    LPALLISTENERIV alListeneriv;
+    LPALGETLISTENERF alGetListenerf;
+    LPALGETLISTENER3F alGetListener3f;
+    LPALGETLISTENERFV alGetListenerfv;
+    LPALGETLISTENERI alGetListeneri;
+    LPALGETLISTENER3I alGetListener3i;
+    LPALGETLISTENERIV alGetListeneriv;
+    LPALGENSOURCES alGenSources;
+    LPALDELETESOURCES alDeleteSources;
+    LPALISSOURCE alIsSource;
+    LPALSOURCEF alSourcef;
+    LPALSOURCE3F alSource3f;
+    LPALSOURCEFV alSourcefv;
+    LPALSOURCEI alSourcei;
+    LPALSOURCE3I alSource3i;
+    LPALSOURCEIV alSourceiv;
+    LPALGETSOURCEF alGetSourcef;
+    LPALGETSOURCE3F alGetSource3f;
+    LPALGETSOURCEFV alGetSourcefv;
+    LPALGETSOURCEI alGetSourcei;
+    LPALGETSOURCE3I alGetSource3i;
+    LPALGETSOURCEIV alGetSourceiv;
+    LPALSOURCEPLAYV alSourcePlayv;
+    LPALSOURCESTOPV alSourceStopv;
+    LPALSOURCEREWINDV alSourceRewindv;
+    LPALSOURCEPAUSEV alSourcePausev;
+    LPALSOURCEPLAY alSourcePlay;
+    LPALSOURCESTOP alSourceStop;
+    LPALSOURCEREWIND alSourceRewind;
+    LPALSOURCEPAUSE alSourcePause;
+    LPALSOURCEQUEUEBUFFERS alSourceQueueBuffers;
+    LPALSOURCEUNQUEUEBUFFERS alSourceUnqueueBuffers;
+    LPALGENBUFFERS alGenBuffers;
+    LPALDELETEBUFFERS alDeleteBuffers;
+    LPALISBUFFER alIsBuffer;
+    LPALBUFFERF alBufferf;
+    LPALBUFFER3F alBuffer3f;
+    LPALBUFFERFV alBufferfv;
+    LPALBUFFERI alBufferi;
+    LPALBUFFER3I alBuffer3i;
+    LPALBUFFERIV alBufferiv;
+    LPALGETBUFFERF alGetBufferf;
+    LPALGETBUFFER3F alGetBuffer3f;
+    LPALGETBUFFERFV alGetBufferfv;
+    LPALGETBUFFERI alGetBufferi;
+    LPALGETBUFFER3I alGetBuffer3i;
+    LPALGETBUFFERIV alGetBufferiv;
+    LPALBUFFERDATA alBufferData;
+    LPALDOPPLERFACTOR alDopplerFactor;
+    LPALDOPPLERVELOCITY alDopplerVelocity;
+    LPALSPEEDOFSOUND alSpeedOfSound;
+    LPALDISTANCEMODEL alDistanceModel;
+} DriverIface;
+
+extern DriverIface *DriverList;
+extern int DriverListSize;
+
+extern altss_t ThreadCtxDriver;
+extern ATOMIC(DriverIface*) CurrentCtxDriver;
+
+
+typedef struct PtrIntMap {
+    ALvoid **keys;
+    /* Shares memory with keys. */
+    ALint *values;
+
+    ALsizei size;
+    ALsizei capacity;
+    RWLock lock;
+} PtrIntMap;
+#define PTRINTMAP_STATIC_INITIALIZE { NULL, NULL, 0, 0, RWLOCK_STATIC_INITIALIZE }
+
+void InitPtrIntMap(PtrIntMap *map);
+void ResetPtrIntMap(PtrIntMap *map);
+ALenum InsertPtrIntMapEntry(PtrIntMap *map, ALvoid *key, ALint value);
+ALint RemovePtrIntMapKey(PtrIntMap *map, ALvoid *key);
+ALint LookupPtrIntMapKey(PtrIntMap *map, ALvoid *key);
+
+
+void InitALC(void);
+void ReleaseALC(void);
+
+
+enum LogLevel {
+    LogLevel_None  = 0,
+    LogLevel_Error = 1,
+    LogLevel_Warn  = 2,
+    LogLevel_Trace = 3,
+};
+extern enum LogLevel LogLevel;
+extern FILE *LogFile;
+
+#define TRACE(...) do {                                   \
+    if(LogLevel >= LogLevel_Trace)                        \
+    {                                                     \
+        fprintf(LogFile, "AL Router (II): " __VA_ARGS__); \
+        fflush(LogFile);                                  \
+    }                                                     \
+} while(0)
+#define WARN(...) do {                                    \
+    if(LogLevel >= LogLevel_Warn)                         \
+    {                                                     \
+        fprintf(LogFile, "AL Router (WW): " __VA_ARGS__); \
+        fflush(LogFile);                                  \
+    }                                                     \
+} while(0)
+#define ERR(...) do {                                     \
+    if(LogLevel >= LogLevel_Error)                        \
+    {                                                     \
+        fprintf(LogFile, "AL Router (EE): " __VA_ARGS__); \
+        fflush(LogFile);                                  \
+    }                                                     \
+} while(0)
+
+#endif /* ROUTER_ROUTER_H */
diff --git a/utils/makehrtf.c b/utils/makehrtf.c
index 704bf62..547c80d 100644
--- a/utils/makehrtf.c
+++ b/utils/makehrtf.c
@@ -2,7 +2,7 @@
  * HRTF utility for producing and demonstrating the process of creating an
  * OpenAL Soft compatible HRIR data set.
  *
- * Copyright (C) 2011-2014  Christopher Fitzgerald
+ * Copyright (C) 2011-2017  Christopher Fitzgerald
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -83,7 +83,7 @@
 #endif
 
 // The epsilon used to maintain signal stability.
-#define EPSILON                      (1e-15)
+#define EPSILON                      (1e-9)
 
 // Constants for accessing the token reader's ring buffer.
 #define TR_RING_BITS                 (16)
@@ -145,16 +145,16 @@
 #define MAX_ASCII_BITS               (32)
 
 // The limits to the FFT window size override on the command line.
-#define MIN_FFTSIZE                  (512)
-#define MAX_FFTSIZE                  (16384)
+#define MIN_FFTSIZE                  (65536)
+#define MAX_FFTSIZE                  (131072)
 
 // The limits to the equalization range limit on the command line.
 #define MIN_LIMIT                    (2.0)
 #define MAX_LIMIT                    (120.0)
 
 // The limits to the truncation window size on the command line.
-#define MIN_TRUNCSIZE                (8)
-#define MAX_TRUNCSIZE                (128)
+#define MIN_TRUNCSIZE                (16)
+#define MAX_TRUNCSIZE                (512)
 
 // The limits to the custom head radius on the command line.
 #define MIN_CUSTOM_RADIUS            (0.05)
@@ -165,6 +165,7 @@
 #define MOD_TRUNCSIZE                (8)
 
 // The defaults for the command line options.
+#define DEFAULT_FFTSIZE              (65536)
 #define DEFAULT_EQUALIZE             (1)
 #define DEFAULT_SURFACE              (1)
 #define DEFAULT_LIMIT                (24.0)
@@ -985,10 +986,10 @@ static void FftInverse(const uint n, const double *inR, const double *inI, doubl
     }
 }
 
-/* Calculate the complex helical sequence (or discrete-time analytical
- * signal) of the given input using the Hilbert transform.  Given the
- * negative natural logarithm of a signal's magnitude response, the imaginary
- * components can be used as the angles for minimum-phase reconstruction.
+/* Calculate the complex helical sequence (or discrete-time analytical signal)
+ * of the given input using the Hilbert transform. Given the natural logarithm
+ * of a signal's magnitude response, the imaginary components can be used as
+ * the angles for minimum-phase reconstruction.
  */
 static void Hilbert(const uint n, const double *in, double *outR, double *outI)
 {
@@ -1009,24 +1010,20 @@ static void Hilbert(const uint n, const double *in, double *outR, double *outI)
             outI[i] = 0.0;
         }
     }
-    FftForward(n, outR, outI, outR, outI);
-    /* Currently the Fourier routines operate only on point counts that are
-     * powers of two.  If that changes and n is odd, the following conditional
-     * should be:  i < (n + 1) / 2.
-     */
-    for(i = 1;i < (n/2);i++)
+    FftInverse(n, outR, outI, outR, outI);
+    for(i = 1;i < (n+1)/2;i++)
     {
         outR[i] *= 2.0;
         outI[i] *= 2.0;
     }
-    // If n is odd, the following increment should be skipped.
-    i++;
+    /* Increment i if n is even. */
+    i += (n&1)^1;
     for(;i < n;i++)
     {
         outR[i] = 0.0;
         outI[i] = 0.0;
     }
-    FftInverse(n, outR, outI, outR, outI);
+    FftForward(n, outR, outI, outR, outI);
 }
 
 /* Calculate the magnitude response of the given input.  This is used in
@@ -1080,15 +1077,15 @@ static void LimitMagnitudeResponse(const uint n, const double limit, const doubl
 static void MinimumPhase(const uint n, const double *in, double *outR, double *outI)
 {
     const uint m = 1 + (n / 2);
-    double aR, aI;
     double *mags;
+    double aR, aI;
     uint i;
 
     mags = CreateArray(n);
     for(i = 0;i < m;i++)
     {
-        mags[i] = fmax(in[i], EPSILON);
-        outR[i] = -log(mags[i]);
+        mags[i] = fmax(EPSILON, in[i]);
+        outR[i] = log(mags[i]);
     }
     for(;i < n;i++)
     {
@@ -1097,9 +1094,8 @@ static void MinimumPhase(const uint n, const double *in, double *outR, double *o
     }
     Hilbert(n, outR, outR, outI);
     // Remove any DC offset the filter has.
-    outR[0] = 0.0;
-    outI[0] = 0.0;
-    for(i = 1;i < n;i++)
+    mags[0] = EPSILON;
+    for(i = 0;i < n;i++)
     {
         ComplexExp(0.0, outI[i], &aR, &aI);
         ComplexMul(mags[i], 0.0, aR, aI, &outR[i], &outI[i]);
@@ -1272,13 +1268,13 @@ static void ResamplerSetup(ResamplerT *rs, const uint srcRate, const uint dstRat
      */
     if(rs->mP > rs->mQ)
     {
-        cutoff = 0.45 / rs->mP;
-        width = 0.1 / rs->mP;
+        cutoff = 0.475 / rs->mP;
+        width = 0.05 / rs->mP;
     }
     else
     {
-        cutoff = 0.45 / rs->mQ;
-        width = 0.1 / rs->mQ;
+        cutoff = 0.475 / rs->mQ;
+        width = 0.05 / rs->mQ;
     }
     // A rejection of -180 dB is used for the stop band.
     l = CalcKaiserOrder(180.0, width) / 2;
@@ -1923,12 +1919,13 @@ static int StoreMhr(const HrirDataT *hData, const char *filename)
 
 // Calculate the onset time of an HRIR and average it with any existing
 // timing for its elevation and azimuth.
-static void AverageHrirOnset(const double *hrir, const uint n, const double f, const uint ei, const uint ai, const HrirDataT *hData)
+static void AverageHrirOnset(const double *hrir, const double f, const uint ei, const uint ai, const HrirDataT *hData)
 {
     double mag;
-    uint i, j;
+    uint n, i, j;
 
     mag = 0.0;
+    n = hData->mIrPoints;
     for(i = 0;i < n;i++)
         mag = fmax(fabs(hrir[i]), mag);
     mag *= 0.15;
@@ -1943,7 +1940,7 @@ static void AverageHrirOnset(const double *hrir, const uint n, const double f, c
 
 // Calculate the magnitude response of an HRIR and average it with any
 // existing responses for its elevation and azimuth.
-static void AverageHrirMagnitude(const double *hrir, const uint npoints, const double f, const uint ei, const uint ai, const HrirDataT *hData)
+static void AverageHrirMagnitude(const double *hrir, const double f, const uint ei, const uint ai, const HrirDataT *hData)
 {
     double *re, *im;
     uint n, m, i, j;
@@ -1951,7 +1948,7 @@ static void AverageHrirMagnitude(const double *hrir, const uint npoints, const d
     n = hData->mFftSize;
     re = CreateArray(n);
     im = CreateArray(n);
-    for(i = 0;i < npoints;i++)
+    for(i = 0;i < hData->mIrPoints;i++)
     {
         re[i] = hrir[i];
         im[i] = 0.0;
@@ -2107,6 +2104,23 @@ static void ReconstructHrirs(const HrirDataT *hData)
     DestroyArray (re);
 }
 
+// Resamples the HRIRs for use at the given sampling rate.
+static void ResampleHrirs(const uint rate, HrirDataT *hData)
+{
+    uint n, step, start, end, j;
+    ResamplerT rs;
+
+    ResamplerSetup(&rs, hData->mIrRate, rate);
+    n = hData->mIrPoints;
+    step = hData->mIrSize;
+    start = hData->mEvOffset[hData->mEvStart] * step;
+    end = hData->mIrCount * step;
+    for(j = start;j < end;j += step)
+        ResamplerRun(&rs, n, &hData->mHrirs[j], n, &hData->mHrirs[j]);
+    ResamplerClear(&rs);
+    hData->mIrRate = rate;
+}
+
 /* Given an elevation index and an azimuth, calculate the indices of the two
  * HRIRs that bound the coordinate along with a factor for calculating the
  * continous HRIR using interpolation.
@@ -2343,14 +2357,10 @@ static int ProcessMetrics(TokenReaderT *tr, const uint fftSize, const uint trunc
                 return 0;
             }
             hData->mIrPoints = points;
-            hData->mFftSize = fftSize;
             if(fftSize <= 0)
             {
-                points = 1;
-                while(points < (4 * hData->mIrPoints))
-                    points <<= 1;
-                hData->mFftSize = points;
-                hData->mIrSize = 1 + (points / 2);
+                hData->mFftSize = DEFAULT_FFTSIZE;
+                hData->mIrSize = 1 + (DEFAULT_FFTSIZE / 2);
             }
             else
             {
@@ -2596,37 +2606,17 @@ static int ReadSourceRef(TokenReaderT *tr, SourceRefT *src)
 }
 
 // Process the list of sources in the data set definition.
-static int ProcessSources(const HeadModelT model, const uint dstRate, TokenReaderT *tr, HrirDataT *hData)
+static int ProcessSources(const HeadModelT model, TokenReaderT *tr, HrirDataT *hData)
 {
     uint *setCount, *setFlag;
     uint line, col, ei, ai;
-    uint res_points;
     SourceRefT src;
     double factor;
     double *hrir;
-    ResamplerT rs;
-
-    ResamplerSetup(&rs, hData->mIrRate, dstRate);
-    /* Scale the number of IR points for resampling. This could be improved by
-     * also including space for the resampler build-up and fall-off (rs.mL*2),
-     * instead of clipping them off, but that could affect the HRTDs. It's not
-     * a big deal to exclude them for sources that aren't already minimum-
-     * phase).
-     */
-    res_points = (uint)(((uint64)hData->mIrPoints*dstRate + hData->mIrRate-1) /
-                        hData->mIrRate);
-    /* Clamp to the IR size to prevent overflow, and don't go less than the
-     * original point count.
-     */
-    if(res_points > hData->mIrSize)
-        res_points = hData->mIrSize;
-    else if(res_points < hData->mIrPoints)
-        res_points = hData->mIrPoints;
 
     setCount = (uint*)calloc(hData->mEvCount, sizeof(uint));
     setFlag = (uint*)calloc(hData->mIrCount, sizeof(uint));
-    hrir = CreateArray(res_points);
-
+    hrir = CreateArray(hData->mIrPoints);
     while(TrIsOperator(tr, "["))
     {
         TrIndication(tr, & line, & col);
@@ -2651,13 +2641,9 @@ static int ProcessSources(const HeadModelT model, const uint dstRate, TokenReade
             if(!LoadSource(&src, hData->mIrRate, hData->mIrPoints, hrir))
                 goto error;
 
-            if(hData->mIrRate != dstRate)
-                ResamplerRun(&rs, hData->mIrPoints, hrir,
-                             res_points, hrir);
-
             if(model == HM_DATASET)
-                AverageHrirOnset(hrir, res_points, 1.0 / factor, ei, ai, hData);
-            AverageHrirMagnitude(hrir, res_points, 1.0 / factor, ei, ai, hData);
+                AverageHrirOnset(hrir, 1.0 / factor, ei, ai, hData);
+            AverageHrirMagnitude(hrir, 1.0 / factor, ei, ai, hData);
             factor += 1.0;
             if(!TrIsOperator(tr, "+"))
                 break;
@@ -2666,8 +2652,6 @@ static int ProcessSources(const HeadModelT model, const uint dstRate, TokenReade
         setFlag[hData->mEvOffset[ei] + ai] = 1;
         setCount[ei]++;
     }
-    hData->mIrPoints = res_points;
-    hData->mIrRate = dstRate;
 
     ei = 0;
     while(ei < hData->mEvCount && setCount[ei] < 1)
@@ -2681,7 +2665,6 @@ static int ProcessSources(const HeadModelT model, const uint dstRate, TokenReade
         {
             if(!TrLoad(tr))
             {
-                ResamplerClear(&rs);
                 DestroyArray(hrir);
                 free(setFlag);
                 free(setCount);
@@ -2696,7 +2679,6 @@ static int ProcessSources(const HeadModelT model, const uint dstRate, TokenReade
         TrError(tr, "Missing source references.\n");
 
 error:
-    ResamplerClear(&rs);
     DestroyArray(hrir);
     free(setFlag);
     free(setCount);
@@ -2747,7 +2729,7 @@ static int ProcessDefinition(const char *inName, const uint outRate, const uint
     }
     hData.mHrirs = CreateArray(hData.mIrCount * hData.mIrSize);
     hData.mHrtds = CreateArray(hData.mIrCount);
-    if(!ProcessSources(model, outRate ? outRate : hData.mIrRate, &tr, &hData))
+    if(!ProcessSources(model, &tr, &hData))
     {
         DestroyArray(hData.mHrtds);
         DestroyArray(hData.mHrirs);
@@ -2768,6 +2750,11 @@ static int ProcessDefinition(const char *inName, const uint outRate, const uint
     }
     fprintf(stdout, "Performing minimum phase reconstruction...\n");
     ReconstructHrirs(&hData);
+    if(outRate != 0 && outRate != hData.mIrRate)
+    {
+        fprintf(stdout, "Resampling HRIRs...\n");
+        ResampleHrirs(outRate, &hData);
+    }
     fprintf(stdout, "Truncating minimum-phase HRIRs...\n");
     hData.mIrPoints = truncSize;
     fprintf(stdout, "Synthesizing missing elevations...\n");
@@ -2809,8 +2796,7 @@ static void PrintHelp(const char *argv0, FILE *ofile)
     fprintf(ofile, "Options:\n");
     fprintf(ofile, " -r=<rate>       Change the data set sample rate to the specified value and\n");
     fprintf(ofile, "                 resample the HRIRs accordingly.\n");
-    fprintf(ofile, " -f=<points>     Override the FFT window size (defaults to the first power-\n");
-    fprintf(ofile, "                 of-two that fits four times the number of HRIR points).\n");
+    fprintf(ofile, " -f=<points>     Override the FFT window size (default: %u).\n", DEFAULT_FFTSIZE);
     fprintf(ofile, " -e={on|off}     Toggle diffuse-field equalization (default: %s).\n", (DEFAULT_EQUALIZE ? "on" : "off"));
     fprintf(ofile, " -s={on|off}     Toggle surface-weighted diffuse-field average (default: %s).\n", (DEFAULT_SURFACE ? "on" : "off"));
     fprintf(ofile, " -l={<dB>|none}  Specify a limit to the magnitude range of the diffuse-field\n");

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-games/openal-soft.git



More information about the Pkg-games-commits mailing list