Browse Source

Update OpenAL Soft to 1.19.1.

Alex Szpakowski 7 years ago
parent
commit
1f9c102832
87 changed files with 4276 additions and 2738 deletions
  1. 1 1
      CMakeLists.txt
  2. 4 6
      libs/openal-soft/.gitignore
  3. 101 118
      libs/openal-soft/Alc/ALc.c
  4. 134 143
      libs/openal-soft/Alc/ALu.c
  5. 23 0
      libs/openal-soft/Alc/alconfig.c
  6. 19 10
      libs/openal-soft/Alc/backends/alsa.c
  7. 1 0
      libs/openal-soft/Alc/backends/base.c
  8. 13 3
      libs/openal-soft/Alc/backends/base.h
  9. 14 8
      libs/openal-soft/Alc/backends/coreaudio.c
  10. 10 9
      libs/openal-soft/Alc/backends/dsound.c
  11. 2 2
      libs/openal-soft/Alc/backends/jack.c
  12. 2 2
      libs/openal-soft/Alc/backends/loopback.c
  13. 3 4
      libs/openal-soft/Alc/backends/null.c
  14. 68 72
      libs/openal-soft/Alc/backends/opensl.c
  15. 11 20
      libs/openal-soft/Alc/backends/oss.c
  16. 3 6
      libs/openal-soft/Alc/backends/portaudio.c
  17. 11 10
      libs/openal-soft/Alc/backends/pulseaudio.c
  18. 13 18
      libs/openal-soft/Alc/backends/qsa.c
  19. 5 4
      libs/openal-soft/Alc/backends/sdl2.c
  20. 312 54
      libs/openal-soft/Alc/backends/sndio.c
  21. 3 3
      libs/openal-soft/Alc/backends/solaris.c
  22. 10 9
      libs/openal-soft/Alc/backends/wasapi.c
  23. 3 3
      libs/openal-soft/Alc/backends/wave.c
  24. 9 15
      libs/openal-soft/Alc/backends/winmm.c
  25. 6 106
      libs/openal-soft/Alc/bformatdec.c
  26. 0 34
      libs/openal-soft/Alc/bformatdec.h
  27. 0 8
      libs/openal-soft/Alc/compat.h
  28. 2 1
      libs/openal-soft/Alc/converter.c
  29. 321 0
      libs/openal-soft/Alc/effects/autowah.c
  30. 7 7
      libs/openal-soft/Alc/effects/chorus.c
  31. 59 66
      libs/openal-soft/Alc/effects/compressor.c
  32. 1 1
      libs/openal-soft/Alc/effects/dedicated.c
  33. 10 12
      libs/openal-soft/Alc/effects/distortion.c
  34. 19 25
      libs/openal-soft/Alc/effects/echo.c
  35. 22 22
      libs/openal-soft/Alc/effects/equalizer.c
  36. 329 0
      libs/openal-soft/Alc/effects/fshifter.c
  37. 33 30
      libs/openal-soft/Alc/effects/modulator.c
  38. 80 135
      libs/openal-soft/Alc/effects/pshifter.c
  39. 227 429
      libs/openal-soft/Alc/effects/reverb.c
  40. 19 25
      libs/openal-soft/Alc/filters/defs.h
  41. 32 36
      libs/openal-soft/Alc/filters/filter.c
  42. 83 85
      libs/openal-soft/Alc/filters/nfc.c
  43. 9 9
      libs/openal-soft/Alc/filters/nfc.h
  44. 109 0
      libs/openal-soft/Alc/filters/splitter.c
  45. 40 0
      libs/openal-soft/Alc/filters/splitter.h
  46. 7 10
      libs/openal-soft/Alc/fpu_modes.h
  47. 66 130
      libs/openal-soft/Alc/helpers.c
  48. 63 43
      libs/openal-soft/Alc/hrtf.c
  49. 3 8
      libs/openal-soft/Alc/hrtf.h
  50. 8 0
      libs/openal-soft/Alc/inprogext.h
  51. 429 131
      libs/openal-soft/Alc/mastering.c
  52. 32 40
      libs/openal-soft/Alc/mastering.h
  53. 1 1
      libs/openal-soft/Alc/mixer/defs.h
  54. 31 17
      libs/openal-soft/Alc/mixer/hrtf_inc.c
  55. 51 52
      libs/openal-soft/Alc/mixer/mixer_c.c
  56. 92 70
      libs/openal-soft/Alc/mixer/mixer_neon.c
  57. 65 44
      libs/openal-soft/Alc/mixer/mixer_sse.c
  58. 16 14
      libs/openal-soft/Alc/mixer/mixer_sse2.c
  59. 16 17
      libs/openal-soft/Alc/mixer/mixer_sse41.c
  60. 28 27
      libs/openal-soft/Alc/mixvoice.c
  61. 40 87
      libs/openal-soft/Alc/panning.c
  62. 33 55
      libs/openal-soft/Alc/uhjfilter.c
  63. 3 3
      libs/openal-soft/Alc/uhjfilter.h
  64. 160 69
      libs/openal-soft/CMakeLists.txt
  65. 8 52
      libs/openal-soft/COPYING
  66. 67 0
      libs/openal-soft/ChangeLog
  67. 4 2
      libs/openal-soft/OpenAL32/Include/alAuxEffectSlot.h
  68. 18 1
      libs/openal-soft/OpenAL32/Include/alEffect.h
  69. 189 32
      libs/openal-soft/OpenAL32/Include/alMain.h
  70. 53 43
      libs/openal-soft/OpenAL32/Include/alu.h
  71. 12 6
      libs/openal-soft/OpenAL32/alAuxEffectSlot.c
  72. 15 0
      libs/openal-soft/OpenAL32/alEffect.c
  73. 6 3
      libs/openal-soft/OpenAL32/alFilter.c
  74. 122 8
      libs/openal-soft/OpenAL32/alSource.c
  75. 25 38
      libs/openal-soft/OpenAL32/event.c
  76. 3 2
      libs/openal-soft/alsoftrc.sample
  77. 1 1
      libs/openal-soft/appveyor.yml
  78. 92 0
      libs/openal-soft/common/alcomplex.c
  79. 71 0
      libs/openal-soft/common/alcomplex.h
  80. 21 2
      libs/openal-soft/common/math_defs.h
  81. 5 1
      libs/openal-soft/common/threads.c
  82. 3 0
      libs/openal-soft/config.h.in
  83. 90 55
      libs/openal-soft/examples/alffplay.cpp
  84. 4 2
      libs/openal-soft/utils/alsoft-config/CMakeLists.txt
  85. 8 0
      libs/openal-soft/utils/alsoft-config/mainwindow.cpp
  86. 40 8
      libs/openal-soft/utils/alsoft-config/mainwindow.ui
  87. 92 113
      libs/openal-soft/utils/makehrtf.c

+ 1 - 1
CMakeLists.txt

@@ -160,7 +160,7 @@ set(MEGA_LIBTHEORA_VER "1.1.1")
 set(MEGA_MPG123_VER "1.25.6")
 set(MEGA_FREETYPE_VER "2.8.1")
 set(MEGA_SDL2_VER "2.0.7-2088cd828335")
-set(MEGA_OPENAL_VER "1.18.2")
+set(MEGA_OPENAL_VER "1.19.1")
 set(MEGA_MODPLUG_VER "0.8.8.4")
 
 set(SKIP_INSTALL_ALL TRUE)

+ 4 - 6
libs/openal-soft/.gitignore

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

+ 101 - 118
libs/openal-soft/Alc/ALc.c

@@ -75,14 +75,14 @@ static struct BackendInfo BackendList[] = {
 #ifdef HAVE_COREAUDIO
     { "core", ALCcoreAudioBackendFactory_getFactory },
 #endif
-#ifdef HAVE_OSS
-    { "oss", ALCossBackendFactory_getFactory },
-#endif
 #ifdef HAVE_SOLARIS
     { "solaris", ALCsolarisBackendFactory_getFactory },
 #endif
 #ifdef HAVE_SNDIO
-    { "sndio", ALCsndioBackendFactory_getFactory },
+    { "sndio", SndioBackendFactory_getFactory },
+#endif
+#ifdef HAVE_OSS
+    { "oss", ALCossBackendFactory_getFactory },
 #endif
 #ifdef HAVE_QSA
     { "qsa", ALCqsaBackendFactory_getFactory },
@@ -550,14 +550,12 @@ static const struct {
     DECL(AL_EFFECT_ECHO),
     DECL(AL_EFFECT_FLANGER),
     DECL(AL_EFFECT_PITCH_SHIFTER),
-#if 0
     DECL(AL_EFFECT_FREQUENCY_SHIFTER),
+#if 0
     DECL(AL_EFFECT_VOCAL_MORPHER),
 #endif
     DECL(AL_EFFECT_RING_MODULATOR),
-#if 0
     DECL(AL_EFFECT_AUTOWAH),
-#endif
     DECL(AL_EFFECT_COMPRESSOR),
     DECL(AL_EFFECT_EQUALIZER),
     DECL(AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT),
@@ -632,6 +630,10 @@ static const struct {
     DECL(AL_FLANGER_FEEDBACK),
     DECL(AL_FLANGER_DELAY),
 
+    DECL(AL_FREQUENCY_SHIFTER_FREQUENCY),
+    DECL(AL_FREQUENCY_SHIFTER_LEFT_DIRECTION),
+    DECL(AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION),
+
     DECL(AL_RING_MODULATOR_FREQUENCY),
     DECL(AL_RING_MODULATOR_HIGHPASS_CUTOFF),
     DECL(AL_RING_MODULATOR_WAVEFORM),
@@ -654,6 +656,11 @@ static const struct {
 
     DECL(AL_DEDICATED_GAIN),
 
+    DECL(AL_AUTOWAH_ATTACK_TIME),
+    DECL(AL_AUTOWAH_RELEASE_TIME),
+    DECL(AL_AUTOWAH_RESONANCE),
+    DECL(AL_AUTOWAH_PEAK_GAIN),
+
     DECL(AL_NUM_RESAMPLERS_SOFT),
     DECL(AL_DEFAULT_RESAMPLER_SOFT),
     DECL(AL_SOURCE_RESAMPLER_SOFT),
@@ -721,6 +728,7 @@ static const ALchar alExtList[] =
     "AL_SOFT_deferred_updates "
     "AL_SOFT_direct_channels "
     "AL_SOFTX_events "
+    "AL_SOFTX_filter_gain_ex "
     "AL_SOFT_gain_clamp_ex "
     "AL_SOFT_loop_points "
     "AL_SOFTX_map_buffer "
@@ -1157,75 +1165,6 @@ static void alc_initconfig(void)
 }
 #define DO_INITCONFIG() alcall_once(&alc_config_once, alc_initconfig)
 
-#ifdef __ANDROID__
-#include <jni.h>
-
-static JavaVM *gJavaVM;
-static pthread_key_t gJVMThreadKey;
-
-static void CleanupJNIEnv(void* UNUSED(ptr))
-{
-    JCALL0(gJavaVM,DetachCurrentThread)();
-}
-
-void *Android_GetJNIEnv(void)
-{
-    if(!gJavaVM)
-    {
-        WARN("gJavaVM is NULL!\n");
-        return NULL;
-    }
-
-    /* http://developer.android.com/guide/practices/jni.html
-     *
-     * All threads are Linux threads, scheduled by the kernel. They're usually
-     * started from managed code (using Thread.start), but they can also be
-     * created elsewhere and then attached to the JavaVM. For example, a thread
-     * started with pthread_create can be attached with the JNI
-     * AttachCurrentThread or AttachCurrentThreadAsDaemon functions. Until a
-     * thread is attached, it has no JNIEnv, and cannot make JNI calls.
-     * Attaching a natively-created thread causes a java.lang.Thread object to
-     * be constructed and added to the "main" ThreadGroup, making it visible to
-     * the debugger. Calling AttachCurrentThread on an already-attached thread
-     * is a no-op.
-     */
-    JNIEnv *env = pthread_getspecific(gJVMThreadKey);
-    if(!env)
-    {
-        int status = JCALL(gJavaVM,AttachCurrentThread)(&env, NULL);
-        if(status < 0)
-        {
-            ERR("Failed to attach current thread\n");
-            return NULL;
-        }
-        pthread_setspecific(gJVMThreadKey, env);
-    }
-    return env;
-}
-
-/* Automatically called by JNI. */
-JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void* UNUSED(reserved))
-{
-    void *env;
-    int err;
-
-    gJavaVM = jvm;
-    if(JCALL(gJavaVM,GetEnv)(&env, JNI_VERSION_1_4) != JNI_OK)
-    {
-        ERR("Failed to get JNIEnv with JNI_VERSION_1_4\n");
-        return JNI_ERR;
-    }
-
-    /* Create gJVMThreadKey so we can keep track of the JNIEnv assigned to each
-     * thread. The JNIEnv *must* be detached before the thread is destroyed.
-     */
-    if((err=pthread_key_create(&gJVMThreadKey, CleanupJNIEnv)) != 0)
-        ERR("pthread_key_create failed: %d\n", err);
-    pthread_setspecific(gJVMThreadKey, env);
-    return JNI_VERSION_1_4;
-}
-#endif
-
 
 /************************************************
  * Library deinitialization
@@ -1306,7 +1245,7 @@ static void ProbeDevices(al_string *list, struct BackendInfo *backendinfo, enum
     if(backendinfo->getFactory)
     {
         ALCbackendFactory *factory = backendinfo->getFactory();
-        V(factory,probe)(type);
+        V(factory,probe)(type, list);
     }
 
     UnlockLists();
@@ -1316,17 +1255,6 @@ static void ProbeAllDevicesList(void)
 static void ProbeCaptureDeviceList(void)
 { ProbeDevices(&alcCaptureDeviceList, &CaptureBackend, CAPTURE_DEVICE_PROBE); }
 
-static void AppendDevice(const ALCchar *name, al_string *devnames)
-{
-    size_t len = strlen(name);
-    if(len > 0)
-        alstr_append_range(devnames, name, name+len+1);
-}
-void AppendAllDevicesList(const ALCchar *name)
-{ AppendDevice(name, &alcAllDevicesList); }
-void AppendCaptureDeviceList(const ALCchar *name)
-{ AppendDevice(name, &alcCaptureDeviceList); }
-
 
 /************************************************
  * Device format information
@@ -1707,10 +1635,11 @@ static void alcSetError(ALCdevice *device, ALCenum errorCode)
 }
 
 
-struct Compressor *CreateDeviceLimiter(const ALCdevice *device)
+static struct Compressor *CreateDeviceLimiter(const ALCdevice *device, const ALfloat threshold)
 {
-    return CompressorInit(0.0f, 0.0f, AL_FALSE, AL_TRUE, 0.0f, 0.0f, 0.5f, 2.0f,
-                          0.0f, -3.0f, 3.0f, device->Frequency);
+    return CompressorInit(device->RealOut.NumChannels, device->Frequency,
+        AL_TRUE, AL_TRUE, AL_TRUE, AL_TRUE, AL_TRUE, 0.001f, 0.002f,
+        0.0f, 0.0f, threshold, INFINITY, 0.0f, 0.020f, 0.200f);
 }
 
 /* UpdateClockBase
@@ -1737,7 +1666,7 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
 {
     enum HrtfRequestMode hrtf_userreq = Hrtf_Default;
     enum HrtfRequestMode hrtf_appreq = Hrtf_Default;
-    ALCenum gainLimiter = device->Limiter ? ALC_TRUE : ALC_FALSE;
+    ALCenum gainLimiter = device->LimiterState;
     const ALsizei old_sends = device->NumAuxSends;
     ALsizei new_sends = device->NumAuxSends;
     enum DevFmtChannels oldChans;
@@ -2053,6 +1982,7 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
     device->RealOut.NumChannels = 0;
 
     UpdateClockBase(device);
+    device->FixedLatency = 0;
 
     device->DitherSeed = DITHER_RNG_SEED;
 
@@ -2213,9 +2143,12 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
                     break;
             }
         }
-        else if(depth > 24)
-            depth = 24;
-        device->DitherDepth = (depth > 0) ? powf(2.0f, (ALfloat)(depth-1)) : 0.0f;
+
+        if(depth > 0)
+        {
+            depth = clampi(depth, 2, 24);
+            device->DitherDepth = powf(2.0f, (ALfloat)(depth-1));
+        }
     }
     if(!(device->DitherDepth > 0.0f))
         TRACE("Dithering disabled\n");
@@ -2223,19 +2156,57 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
         TRACE("Dithering enabled (%g-bit, %g)\n", log2f(device->DitherDepth)+1.0f,
               device->DitherDepth);
 
+    device->LimiterState = gainLimiter;
     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
-     * ALC_FALSE. We default to on, so ALC_DONT_CARE_SOFT is the same as
-     * ALC_TRUE.
+     * ALC_FALSE. For ALC_DONT_CARE_SOFT, use the limiter for integer-based
+     * output (where samples must be clamped), and don't for floating-point
+     * (which can take unclamped samples).
      */
+    if(gainLimiter == ALC_DONT_CARE_SOFT)
+    {
+        switch(device->FmtType)
+        {
+            case DevFmtByte:
+            case DevFmtUByte:
+            case DevFmtShort:
+            case DevFmtUShort:
+            case DevFmtInt:
+            case DevFmtUInt:
+                gainLimiter = ALC_TRUE;
+                break;
+            case DevFmtFloat:
+                gainLimiter = ALC_FALSE;
+                break;
+        }
+    }
     if(gainLimiter != ALC_FALSE)
     {
-        if(!device->Limiter || device->Frequency != GetCompressorSampleRate(device->Limiter))
+        ALfloat thrshld = 1.0f;
+        switch(device->FmtType)
         {
-            al_free(device->Limiter);
-            device->Limiter = CreateDeviceLimiter(device);
+            case DevFmtByte:
+            case DevFmtUByte:
+                thrshld = 127.0f / 128.0f;
+                break;
+            case DevFmtShort:
+            case DevFmtUShort:
+                thrshld = 32767.0f / 32768.0f;
+                break;
+            case DevFmtInt:
+            case DevFmtUInt:
+            case DevFmtFloat:
+                break;
         }
+        if(device->DitherDepth > 0.0f)
+            thrshld -= 1.0f / device->DitherDepth;
+
+        al_free(device->Limiter);
+        device->Limiter = CreateDeviceLimiter(device, log10f(thrshld) * 20.0f);
+        device->FixedLatency += (ALuint)(GetCompressorLookAhead(device->Limiter) *
+                                         DEVICE_CLOCK_RES / device->Frequency);
     }
     else
     {
@@ -2246,6 +2217,8 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
 
     aluSelectPostProcess(device);
 
+    TRACE("Fixed device latency: %uns\n", device->FixedLatency);
+
     /* Need to delay returning failure until replacement Send arrays have been
      * allocated with the appropriate size.
      */
@@ -2400,11 +2373,13 @@ static void InitDevice(ALCdevice *device, enum DeviceType type)
     device->Flags = 0;
     device->Render_Mode = NormalRender;
     device->AvgSpeakerDist = 0.0f;
+    device->LimiterState = ALC_DONT_CARE_SOFT;
 
     ATOMIC_INIT(&device->ContextList, NULL);
 
     device->ClockBase = 0;
     device->SamplesDone = 0;
+    device->FixedLatency = 0;
 
     device->SourcesMax = 0;
     device->AuxiliaryEffectSlotMax = 0;
@@ -2638,7 +2613,6 @@ static ALvoid InitContext(ALCcontext *Context)
     Context->MetersPerUnit = AL_DEFAULT_METERS_PER_UNIT;
     ATOMIC_FLAG_TEST_AND_SET(&Context->PropsClean, almemory_order_relaxed);
     ATOMIC_INIT(&Context->DeferUpdates, AL_FALSE);
-    almtx_init(&Context->EventThrdLock, almtx_plain);
     alsem_init(&Context->EventSem, 0);
     Context->AsyncEvents = NULL;
     ATOMIC_INIT(&Context->EnabledEvts, 0);
@@ -2665,6 +2639,11 @@ static ALvoid InitContext(ALCcontext *Context)
                                           listener->Params.MetersPerUnit;
     listener->Params.SourceDistanceModel = Context->SourceDistanceModel;
     listener->Params.DistanceModel = Context->DistanceModel;
+
+
+    Context->AsyncEvents = ll_ringbuffer_create(63, sizeof(AsyncEvent), false);
+    if(althrd_create(&Context->EventThread, EventThread, Context) != althrd_success)
+        ERR("Failed to start event thread! Expect problems.\n");
 }
 
 
@@ -2773,17 +2752,7 @@ static void FreeContext(ALCcontext *context)
     }
     TRACE("Freed "SZFMT" listener property object%s\n", count, (count==1)?"":"s");
 
-    if(ATOMIC_EXCHANGE(&context->EnabledEvts, 0, almemory_order_acq_rel))
-    {
-        static const AsyncEvent kill_evt = { 0 };
-        while(ll_ringbuffer_write(context->AsyncEvents, (const char*)&kill_evt, 1) == 0)
-            althrd_yield();
-        alsem_post(&context->EventSem);
-        althrd_join(context->EventThread, NULL);
-    }
-
     almtx_destroy(&context->EventCbLock);
-    almtx_destroy(&context->EventThrdLock);
     alsem_destroy(&context->EventSem);
 
     ll_ringbuffer_free(context->AsyncEvents);
@@ -2807,6 +2776,7 @@ static void FreeContext(ALCcontext *context)
  */
 static bool ReleaseContext(ALCcontext *context, ALCdevice *device)
 {
+    static const AsyncEvent kill_evt = ASYNC_EVENT(EventType_KillThread);
     ALCcontext *origctx, *newhead;
     bool ret = true;
 
@@ -2839,6 +2809,16 @@ static bool ReleaseContext(ALCcontext *context, ALCdevice *device)
         ret = !!newhead;
     V0(device->Backend,unlock)();
 
+    /* Make sure the context is finished and no longer processing in the mixer
+     * before sending the message queue kill event. The backend's lock does
+     * this, although waiting for a non-odd mix count would work too.
+     */
+
+    while(ll_ringbuffer_write(context->AsyncEvents, (const char*)&kill_evt, 1) == 0)
+        althrd_yield();
+    alsem_post(&context->EventSem);
+    althrd_join(context->EventThread, NULL);
+
     ALCcontext_DecRef(context);
     return ret;
 }
@@ -3620,7 +3600,7 @@ ALC_API void ALC_APIENTRY alcGetInteger64vSOFT(ALCdevice *device, ALCenum pname,
                     values[i++] = ALC_OUTPUT_LIMITER_SOFT;
                     values[i++] = device->Limiter ? ALC_TRUE : ALC_FALSE;
 
-                    clock = V0(device->Backend,getClockLatency)();
+                    clock = GetClockLatency(device);
                     values[i++] = ALC_DEVICE_CLOCK_SOFT;
                     values[i++] = clock.ClockTime;
 
@@ -3646,7 +3626,7 @@ ALC_API void ALC_APIENTRY alcGetInteger64vSOFT(ALCdevice *device, ALCenum pname,
 
             case ALC_DEVICE_LATENCY_SOFT:
                 almtx_lock(&device->BackendLock);
-                clock = V0(device->Backend,getClockLatency)();
+                clock = GetClockLatency(device);
                 almtx_unlock(&device->BackendLock);
                 *values = clock.Latency;
                 break;
@@ -3657,7 +3637,7 @@ ALC_API void ALC_APIENTRY alcGetInteger64vSOFT(ALCdevice *device, ALCenum pname,
                 else
                 {
                     almtx_lock(&device->BackendLock);
-                    clock = V0(device->Backend,getClockLatency)();
+                    clock = GetClockLatency(device);
                     almtx_unlock(&device->BackendLock);
                     values[0] = clock.ClockTime;
                     values[1] = clock.Latency;
@@ -4073,6 +4053,7 @@ ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *deviceName)
     device->IsHeadphones = AL_FALSE;
     device->AmbiLayout = AmbiLayout_Default;
     device->AmbiScale = AmbiNorm_Default;
+    device->LimiterState = ALC_TRUE;
     device->NumUpdates = 3;
     device->UpdateSize = 1024;
 
@@ -4211,8 +4192,6 @@ ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *deviceName)
             ERR("Unsupported ambi-format: %s\n", fmt);
     }
 
-    device->Limiter = CreateDeviceLimiter(device);
-
     {
         ALCdevice *head = ATOMIC_LOAD_SEQ(&DeviceList);
         do {
@@ -4394,6 +4373,12 @@ ALC_API ALCboolean ALC_APIENTRY alcCaptureCloseDevice(ALCdevice *device)
     }
     UnlockLists();
 
+    almtx_lock(&device->BackendLock);
+    if((device->Flags&DEVICE_RUNNING))
+        V0(device->Backend,stop)();
+    device->Flags &= ~DEVICE_RUNNING;
+    almtx_unlock(&device->BackendLock);
+
     ALCdevice_DecRef(device);
 
     return ALC_TRUE;
@@ -4534,8 +4519,6 @@ ALC_API ALCdevice* ALC_APIENTRY alcLoopbackOpenDeviceSOFT(const ALCchar *deviceN
     // Open the "backend"
     V(device->Backend,open)("Loopback");
 
-    device->Limiter = CreateDeviceLimiter(device);
-
     {
         ALCdevice *head = ATOMIC_LOAD_SEQ(&DeviceList);
         do {

+ 134 - 143
libs/openal-soft/Alc/ALu.c

@@ -39,6 +39,7 @@
 #include "bformatdec.h"
 #include "static_assert.h"
 #include "ringbuffer.h"
+#include "filters/splitter.h"
 
 #include "mixer/defs.h"
 #include "fpu_modes.h"
@@ -144,16 +145,6 @@ static inline HrtfDirectMixerFunc SelectHrtfMixer(void)
 }
 
 
-/* Prior to VS2013, MSVC lacks the round() family of functions. */
-#if defined(_MSC_VER) && _MSC_VER < 1800
-static float roundf(float val)
-{
-    if(val < 0.0f)
-        return ceilf(val-0.5f);
-    return floorf(val+0.5f);
-}
-#endif
-
 /* This RNG method was created based on the math found in opusdec. It's quick,
  * and starting with a seed value of 22222, is suitable for generating
  * whitenoise.
@@ -220,32 +211,31 @@ void aluInit(void)
 
 static void SendSourceStoppedEvent(ALCcontext *context, ALuint id)
 {
+    AsyncEvent evt = ASYNC_EVENT(EventType_SourceStateChange);
     ALbitfieldSOFT enabledevt;
-    AsyncEvent evt;
     size_t strpos;
     ALuint scale;
 
     enabledevt = ATOMIC_LOAD(&context->EnabledEvts, almemory_order_acquire);
     if(!(enabledevt&EventType_SourceStateChange)) return;
 
-    evt.EnumType = EventType_SourceStateChange;
-    evt.Type = AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT;
-    evt.ObjectId = id;
-    evt.Param = AL_STOPPED;
+    evt.u.user.type = AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT;
+    evt.u.user.id = id;
+    evt.u.user.param = AL_STOPPED;
 
     /* Normally snprintf would be used, but this is called from the mixer and
      * that function's not real-time safe, so we have to construct it manually.
      */
-    strcpy(evt.Message, "Source ID "); strpos = 10;
+    strcpy(evt.u.user.msg, "Source ID "); strpos = 10;
     scale = 1000000000;
     while(scale > 0 && scale > id)
         scale /= 10;
     while(scale > 0)
     {
-        evt.Message[strpos++] = '0' + ((id/scale)%10);
+        evt.u.user.msg[strpos++] = '0' + ((id/scale)%10);
         scale /= 10;
     }
-    strcpy(evt.Message+strpos, " state changed to AL_STOPPED");
+    strcpy(evt.u.user.msg+strpos, " state changed to AL_STOPPED");
 
     if(ll_ringbuffer_write(context->AsyncEvents, (const char*)&evt, 1) == 1)
         alsem_post(&context->EventSem);
@@ -304,26 +294,24 @@ static void ProcessUhj(ALCdevice *device, ALsizei SamplesToDo)
 {
     int lidx = GetChannelIdxByName(&device->RealOut, FrontLeft);
     int ridx = GetChannelIdxByName(&device->RealOut, FrontRight);
-    if(LIKELY(lidx != -1 && ridx != -1))
-    {
-        /* Encode to stereo-compatible 2-channel UHJ output. */
-        EncodeUhj2(device->Uhj_Encoder,
-            device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
-            device->Dry.Buffer, SamplesToDo
-        );
-    }
+    assert(lidx != -1 && ridx != -1);
+
+    /* Encode to stereo-compatible 2-channel UHJ output. */
+    EncodeUhj2(device->Uhj_Encoder,
+        device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
+        device->Dry.Buffer, SamplesToDo
+    );
 }
 
 static void ProcessBs2b(ALCdevice *device, ALsizei SamplesToDo)
 {
     int lidx = GetChannelIdxByName(&device->RealOut, FrontLeft);
     int ridx = GetChannelIdxByName(&device->RealOut, FrontRight);
-    if(LIKELY(lidx != -1 && ridx != -1))
-    {
-        /* Apply binaural/crossfeed filter */
-        bs2b_cross_feed(device->Bs2b, device->RealOut.Buffer[lidx],
-                        device->RealOut.Buffer[ridx], SamplesToDo);
-    }
+    assert(lidx != -1 && ridx != -1);
+
+    /* Apply binaural/crossfeed filter */
+    bs2b_cross_feed(device->Bs2b, device->RealOut.Buffer[lidx],
+                    device->RealOut.Buffer[ridx], SamplesToDo);
 }
 
 void aluSelectPostProcess(ALCdevice *device)
@@ -343,9 +331,7 @@ void aluSelectPostProcess(ALCdevice *device)
 }
 
 
-/* Prepares the interpolator for a given rate (determined by increment).  A
- * result of AL_FALSE indicates that the filter output will completely cut
- * the input signal.
+/* Prepares the interpolator for a given rate (determined by increment).
  *
  * With a bit of work, and a trade of memory for CPU cost, this could be
  * modified for use with an interpolated increment for buttery-smooth pitch
@@ -353,29 +339,24 @@ void aluSelectPostProcess(ALCdevice *device)
  */
 void BsincPrepare(const ALuint increment, BsincState *state, const BSincTable *table)
 {
-    ALfloat sf;
-    ALsizei si;
+    ALfloat sf = 0.0f;
+    ALsizei si = BSINC_SCALE_COUNT-1;
 
     if(increment > FRACTIONONE)
     {
         sf = (ALfloat)FRACTIONONE / increment;
         sf = maxf(0.0f, (BSINC_SCALE_COUNT-1) * (sf-table->scaleBase) * table->scaleRange);
-        si = fastf2i(sf);
+        si = float2int(sf);
         /* The interpolation factor is fit to this diagonally-symmetric curve
          * to reduce the transition ripple caused by interpolating different
          * scales of the sinc function.
          */
         sf = 1.0f - cosf(asinf(sf - si));
     }
-    else
-    {
-        sf = 0.0f;
-        si = BSINC_SCALE_COUNT - 1;
-    }
 
     state->sf = sf;
     state->m = table->m[si];
-    state->l = -((state->m/2) - 1);
+    state->l = (state->m/2) - 1;
     state->filter = table->Tab + table->filterOffset[si];
 }
 
@@ -481,12 +462,40 @@ static bool CalcEffectSlotParams(ALeffectslot *slot, ALCcontext *context, bool f
             slot->Params.AirAbsorptionGainHF = 1.0f;
         }
 
-        /* Swap effect states. No need to play with the ref counts since they
-         * keep the same number of refs.
-         */
         state = props->State;
-        props->State = slot->Params.EffectState;
-        slot->Params.EffectState = state;
+
+        if(state == slot->Params.EffectState)
+        {
+            /* If the effect state is the same as current, we can decrement its
+             * count safely to remove it from the update object (it can't reach
+             * 0 refs since the current params also hold a reference).
+             */
+            DecrementRef(&state->Ref);
+            props->State = NULL;
+        }
+        else
+        {
+            /* Otherwise, replace it and send off the old one with a release
+             * event.
+             */
+            AsyncEvent evt = ASYNC_EVENT(EventType_ReleaseEffectState);
+            evt.u.EffectState = slot->Params.EffectState;
+
+            slot->Params.EffectState = state;
+            props->State = NULL;
+
+            if(LIKELY(ll_ringbuffer_write(context->AsyncEvents, (const char*)&evt, 1) != 0))
+                alsem_post(&context->EventSem);
+            else
+            {
+                /* If writing the event failed, the queue was probably full.
+                 * Store the old state in the property object where it can
+                 * eventually be cleaned up sometime later (not ideal, but
+                 * better than blocking or leaking).
+                 */
+                props->State = evt.u.EffectState;
+            }
+        }
 
         ATOMIC_REPLACE_HEAD(struct ALeffectslotProps*, &context->FreeEffectslotProps, props);
     }
@@ -662,24 +671,26 @@ static void CalcPanningAndFilters(ALvoice *voice, const ALfloat Azi, const ALflo
                 NfcFilterAdjust(&voice->Direct.Params[0].NFCtrlFilter, w0);
 
                 for(i = 0;i < MAX_AMBI_ORDER+1;i++)
-                    voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
+                    voice->Direct.ChannelsPerOrder[i] = Device->NumChannelsPerOrder[i];
                 voice->Flags |= VOICE_HAS_NFC;
             }
 
-            if(Device->Render_Mode == StereoPair)
-                CalcAnglePairwiseCoeffs(Azi, Elev, Spread, coeffs);
-            else
-                CalcAngleCoeffs(Azi, Elev, Spread, coeffs);
+            /* A scalar of 1.5 for plain stereo results in +/-60 degrees being
+             * moved to +/-90 degrees for direct right and left speaker
+             * responses.
+             */
+            CalcAngleCoeffs((Device->Render_Mode==StereoPair) ? ScaleAzimuthFront(Azi, 1.5f) : Azi,
+                            Elev, Spread, coeffs);
 
             /* NOTE: W needs to be scaled by sqrt(2) due to FuMa normalization. */
-            ComputeDryPanGains(&Device->Dry, coeffs, DryGain*1.414213562f,
+            ComputePanGains(&Device->Dry, coeffs, DryGain*SQRTF_2,
                                voice->Direct.Params[0].Gains.Target);
             for(i = 0;i < NumSends;i++)
             {
                 const ALeffectslot *Slot = SendSlots[i];
                 if(Slot)
-                    ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
-                        coeffs, WetGain[i]*1.414213562f, voice->Send[i].Params[0].Gains.Target
+                    ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs,
+                        WetGain[i]*SQRTF_2, voice->Send[i].Params[0].Gains.Target
                     );
             }
         }
@@ -688,8 +699,6 @@ static void CalcPanningAndFilters(ALvoice *voice, const ALfloat Azi, const ALflo
             /* Local B-Format sources have their XYZ channels rotated according
              * to the orientation.
              */
-            const ALfloat sqrt_2 = sqrtf(2.0f);
-            const ALfloat sqrt_3 = sqrtf(3.0f);
             ALfloat N[3], V[3], U[3];
             aluMatrixf matrix;
 
@@ -732,25 +741,25 @@ static void CalcPanningAndFilters(ALvoice *voice, const ALfloat Azi, const ALflo
              * outputs on the columns.
              */
             aluMatrixfSet(&matrix,
-                // ACN0         ACN1          ACN2          ACN3
-                sqrt_2,         0.0f,         0.0f,         0.0f, // Ambi W
-                  0.0f, -N[0]*sqrt_3,  N[1]*sqrt_3, -N[2]*sqrt_3, // Ambi X
-                  0.0f,  U[0]*sqrt_3, -U[1]*sqrt_3,  U[2]*sqrt_3, // Ambi Y
-                  0.0f, -V[0]*sqrt_3,  V[1]*sqrt_3, -V[2]*sqrt_3  // Ambi Z
+                // ACN0           ACN1           ACN2           ACN3
+                SQRTF_2,          0.0f,          0.0f,          0.0f, // Ambi W
+                   0.0f, -N[0]*SQRTF_3,  N[1]*SQRTF_3, -N[2]*SQRTF_3, // Ambi X
+                   0.0f,  U[0]*SQRTF_3, -U[1]*SQRTF_3,  U[2]*SQRTF_3, // Ambi Y
+                   0.0f, -V[0]*SQRTF_3,  V[1]*SQRTF_3, -V[2]*SQRTF_3  // Ambi Z
             );
 
             voice->Direct.Buffer = Device->FOAOut.Buffer;
             voice->Direct.Channels = Device->FOAOut.NumChannels;
             for(c = 0;c < num_channels;c++)
-                ComputeFirstOrderGains(&Device->FOAOut, matrix.m[c], DryGain,
-                                       voice->Direct.Params[c].Gains.Target);
+                ComputePanGains(&Device->FOAOut, matrix.m[c], DryGain,
+                                voice->Direct.Params[c].Gains.Target);
             for(i = 0;i < NumSends;i++)
             {
                 const ALeffectslot *Slot = SendSlots[i];
                 if(Slot)
                 {
                     for(c = 0;c < num_channels;c++)
-                        ComputeFirstOrderGainsBF(Slot->ChanMap, Slot->NumChannels,
+                        ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
                             matrix.m[c], WetGain[i], voice->Send[i].Params[c].Gains.Target
                         );
                 }
@@ -906,17 +915,15 @@ static void CalcPanningAndFilters(ALvoice *voice, const ALfloat Azi, const ALflo
                     NfcFilterAdjust(&voice->Direct.Params[c].NFCtrlFilter, w0);
 
                 for(i = 0;i < MAX_AMBI_ORDER+1;i++)
-                    voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
+                    voice->Direct.ChannelsPerOrder[i] = Device->NumChannelsPerOrder[i];
                 voice->Flags |= VOICE_HAS_NFC;
             }
 
             /* Calculate the directional coefficients once, which apply to all
              * input channels.
              */
-            if(Device->Render_Mode == StereoPair)
-                CalcAnglePairwiseCoeffs(Azi, Elev, Spread, coeffs);
-            else
-                CalcAngleCoeffs(Azi, Elev, Spread, coeffs);
+            CalcAngleCoeffs((Device->Render_Mode==StereoPair) ? ScaleAzimuthFront(Azi, 1.5f) : Azi,
+                            Elev, Spread, coeffs);
 
             for(c = 0;c < num_channels;c++)
             {
@@ -931,9 +938,8 @@ static void CalcPanningAndFilters(ALvoice *voice, const ALfloat Azi, const ALflo
                     continue;
                 }
 
-                ComputeDryPanGains(&Device->Dry,
-                    coeffs, DryGain * downmix_gain, voice->Direct.Params[c].Gains.Target
-                );
+                ComputePanGains(&Device->Dry, coeffs, DryGain * downmix_gain,
+                                voice->Direct.Params[c].Gains.Target);
             }
 
             for(i = 0;i < NumSends;i++)
@@ -969,7 +975,7 @@ static void CalcPanningAndFilters(ALvoice *voice, const ALfloat Azi, const ALflo
                     NfcFilterAdjust(&voice->Direct.Params[c].NFCtrlFilter, w0);
 
                 for(i = 0;i < MAX_AMBI_ORDER+1;i++)
-                    voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
+                    voice->Direct.ChannelsPerOrder[i] = Device->NumChannelsPerOrder[i];
                 voice->Flags |= VOICE_HAS_NFC;
             }
 
@@ -988,14 +994,14 @@ static void CalcPanningAndFilters(ALvoice *voice, const ALfloat Azi, const ALflo
                     continue;
                 }
 
-                if(Device->Render_Mode == StereoPair)
-                    CalcAnglePairwiseCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs);
-                else
-                    CalcAngleCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs);
-                ComputeDryPanGains(&Device->Dry,
-                    coeffs, DryGain, voice->Direct.Params[c].Gains.Target
+                CalcAngleCoeffs(
+                    (Device->Render_Mode==StereoPair) ? ScaleAzimuthFront(chans[c].angle, 3.0f)
+                                                      : chans[c].angle,
+                    chans[c].elevation, Spread, coeffs
                 );
 
+                ComputePanGains(&Device->Dry, coeffs, DryGain,
+                                voice->Direct.Params[c].Gains.Target);
                 for(i = 0;i < NumSends;i++)
                 {
                     const ALeffectslot *Slot = SendSlots[i];
@@ -1017,20 +1023,20 @@ static void CalcPanningAndFilters(ALvoice *voice, const ALfloat Azi, const ALflo
         voice->Direct.FilterType = AF_None;
         if(gainHF != 1.0f) voice->Direct.FilterType |= AF_LowPass;
         if(gainLF != 1.0f) voice->Direct.FilterType |= AF_HighPass;
-        BiquadState_setParams(
+        BiquadFilter_setParams(
             &voice->Direct.Params[0].LowPass, BiquadType_HighShelf,
             gainHF, hfScale, calc_rcpQ_from_slope(gainHF, 1.0f)
         );
-        BiquadState_setParams(
+        BiquadFilter_setParams(
             &voice->Direct.Params[0].HighPass, BiquadType_LowShelf,
             gainLF, lfScale, calc_rcpQ_from_slope(gainLF, 1.0f)
         );
         for(c = 1;c < num_channels;c++)
         {
-            BiquadState_copyParams(&voice->Direct.Params[c].LowPass,
-                                   &voice->Direct.Params[0].LowPass);
-            BiquadState_copyParams(&voice->Direct.Params[c].HighPass,
-                                   &voice->Direct.Params[0].HighPass);
+            BiquadFilter_copyParams(&voice->Direct.Params[c].LowPass,
+                                    &voice->Direct.Params[0].LowPass);
+            BiquadFilter_copyParams(&voice->Direct.Params[c].HighPass,
+                                    &voice->Direct.Params[0].HighPass);
         }
     }
     for(i = 0;i < NumSends;i++)
@@ -1043,20 +1049,20 @@ static void CalcPanningAndFilters(ALvoice *voice, const ALfloat Azi, const ALflo
         voice->Send[i].FilterType = AF_None;
         if(gainHF != 1.0f) voice->Send[i].FilterType |= AF_LowPass;
         if(gainLF != 1.0f) voice->Send[i].FilterType |= AF_HighPass;
-        BiquadState_setParams(
+        BiquadFilter_setParams(
             &voice->Send[i].Params[0].LowPass, BiquadType_HighShelf,
             gainHF, hfScale, calc_rcpQ_from_slope(gainHF, 1.0f)
         );
-        BiquadState_setParams(
+        BiquadFilter_setParams(
             &voice->Send[i].Params[0].HighPass, BiquadType_LowShelf,
             gainLF, lfScale, calc_rcpQ_from_slope(gainLF, 1.0f)
         );
         for(c = 1;c < num_channels;c++)
         {
-            BiquadState_copyParams(&voice->Send[i].Params[c].LowPass,
-                                   &voice->Send[i].Params[0].LowPass);
-            BiquadState_copyParams(&voice->Send[i].Params[c].HighPass,
-                                   &voice->Send[i].Params[0].HighPass);
+            BiquadFilter_copyParams(&voice->Send[i].Params[c].LowPass,
+                                    &voice->Send[i].Params[0].LowPass);
+            BiquadFilter_copyParams(&voice->Send[i].Params[c].HighPass,
+                                    &voice->Send[i].Params[0].HighPass);
         }
     }
 }
@@ -1098,7 +1104,7 @@ static void CalcNonAttnSourceParams(ALvoice *voice, const struct ALvoiceProps *p
     if(Pitch > (ALfloat)MAX_PITCH)
         voice->Step = MAX_PITCH<<FRACTIONBITS;
     else
-        voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
+        voice->Step = maxi(fastf2i(Pitch * FRACTIONONE), 1);
     if(props->Resampler == BSinc24Resampler)
         BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc24);
     else if(props->Resampler == BSinc12Resampler)
@@ -1459,7 +1465,7 @@ static void CalcAttnSourceParams(ALvoice *voice, const struct ALvoiceProps *prop
     if(Pitch > (ALfloat)MAX_PITCH)
         voice->Step = MAX_PITCH<<FRACTIONBITS;
     else
-        voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
+        voice->Step = maxi(fastf2i(Pitch * FRACTIONONE), 1);
     if(props->Resampler == BSinc24Resampler)
         BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc24);
     else if(props->Resampler == BSinc12Resampler)
@@ -1628,7 +1634,7 @@ static void ApplyDistanceComp(ALfloat (*restrict Samples)[BUFFERSIZE], DistanceC
             continue;
         }
 
-        if(SamplesToDo >= base)
+        if(LIKELY(SamplesToDo >= base))
         {
             for(i = 0;i < base;i++)
                 Values[i] = distbuf[i];
@@ -1656,6 +1662,9 @@ static void ApplyDither(ALfloat (*restrict Samples)[BUFFERSIZE], ALuint *dither_
     ALuint seed = *dither_seed;
     ALsizei c, i;
 
+    ASSUME(numchans > 0);
+    ASSUME(SamplesToDo > 0);
+
     /* 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.
@@ -1669,7 +1678,7 @@ static void ApplyDither(ALfloat (*restrict Samples)[BUFFERSIZE], ALuint *dither_
             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;
+            samples[i] = fast_roundf(val) * invscale;
         }
     }
     *dither_seed = seed;
@@ -1680,9 +1689,10 @@ static inline ALfloat Conv_ALfloat(ALfloat val)
 { return 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
-     * exponent helps out, effectively giving 25 bits).
+    /* Floats have a 23-bit mantissa. There is an implied 1 bit in the mantissa
+     * along with the sign bit, giving 25 bits total, so [-16777216, +16777216]
+     * is the max value a normalized float can be scaled to before losing
+     * precision.
      */
     return fastf2i(clampf(val*16777216.0f, -16777216.0f, 16777215.0f))<<7;
 }
@@ -1707,6 +1717,10 @@ static void Write##A(const ALfloat (*restrict InBuffer)[BUFFERSIZE],          \
                      ALsizei numchans)                                        \
 {                                                                             \
     ALsizei i, j;                                                             \
+                                                                              \
+    ASSUME(numchans > 0);                                                     \
+    ASSUME(SamplesToDo > 0);                                                  \
+                                                                              \
     for(j = 0;j < numchans;j++)                                               \
     {                                                                         \
         const ALfloat *restrict in = ASSUME_ALIGNED(InBuffer[j], 16);         \
@@ -1824,41 +1838,29 @@ void aluMixData(ALCdevice *device, ALvoid *OutBuffer, ALsizei NumSamples)
                           SamplesToDo, device->RealOut.NumChannels);
 
         if(device->Limiter)
-            ApplyCompression(device->Limiter, device->RealOut.NumChannels, SamplesToDo,
-                             device->RealOut.Buffer);
+            ApplyCompression(device->Limiter, SamplesToDo, device->RealOut.Buffer);
 
         if(device->DitherDepth > 0.0f)
             ApplyDither(device->RealOut.Buffer, &device->DitherSeed, device->DitherDepth,
                         SamplesToDo, device->RealOut.NumChannels);
 
-        if(OutBuffer)
+        if(LIKELY(OutBuffer))
         {
             ALfloat (*Buffer)[BUFFERSIZE] = device->RealOut.Buffer;
             ALsizei Channels = device->RealOut.NumChannels;
 
             switch(device->FmtType)
             {
-                case DevFmtByte:
-                    WriteI8(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
-                    break;
-                case DevFmtUByte:
-                    WriteUI8(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
-                    break;
-                case DevFmtShort:
-                    WriteI16(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
-                    break;
-                case DevFmtUShort:
-                    WriteUI16(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
-                    break;
-                case DevFmtInt:
-                    WriteI32(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
-                    break;
-                case DevFmtUInt:
-                    WriteUI32(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
-                    break;
-                case DevFmtFloat:
-                    WriteF32(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
-                    break;
+#define HANDLE_WRITE(T, S) case T:                                            \
+    Write##S(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels); break;
+                HANDLE_WRITE(DevFmtByte, I8)
+                HANDLE_WRITE(DevFmtUByte, UI8)
+                HANDLE_WRITE(DevFmtShort, I16)
+                HANDLE_WRITE(DevFmtUShort, UI16)
+                HANDLE_WRITE(DevFmtInt, I32)
+                HANDLE_WRITE(DevFmtUInt, UI32)
+                HANDLE_WRITE(DevFmtFloat, F32)
+#undef HANDLE_WRITE
             }
         }
 
@@ -1870,35 +1872,24 @@ void aluMixData(ALCdevice *device, ALvoid *OutBuffer, ALsizei NumSamples)
 
 void aluHandleDisconnect(ALCdevice *device, const char *msg, ...)
 {
+    AsyncEvent evt = ASYNC_EVENT(EventType_Disconnected);
     ALCcontext *ctx;
-    AsyncEvent evt;
     va_list args;
     int msglen;
 
     if(!ATOMIC_EXCHANGE(&device->Connected, AL_FALSE, almemory_order_acq_rel))
         return;
 
-    evt.EnumType = EventType_Disconnected;
-    evt.Type = AL_EVENT_TYPE_DISCONNECTED_SOFT;
-    evt.ObjectId = 0;
-    evt.Param = 0;
+    evt.u.user.type = AL_EVENT_TYPE_DISCONNECTED_SOFT;
+    evt.u.user.id = 0;
+    evt.u.user.param = 0;
 
     va_start(args, msg);
-    msglen = vsnprintf(evt.Message, sizeof(evt.Message), msg, args);
+    msglen = vsnprintf(evt.u.user.msg, sizeof(evt.u.user.msg), msg, args);
     va_end(args);
 
-    if(msglen < 0 || (size_t)msglen >= sizeof(evt.Message))
-    {
-        evt.Message[sizeof(evt.Message)-1] = 0;
-        msglen = (int)strlen(evt.Message);
-    }
-    if(msglen > 0)
-        msg = evt.Message;
-    else
-    {
-        msg = "<internal error constructing message>";
-        msglen = (int)strlen(msg);
-    }
+    if(msglen < 0 || (size_t)msglen >= sizeof(evt.u.user.msg))
+        evt.u.user.msg[sizeof(evt.u.user.msg)-1] = 0;
 
     ctx = ATOMIC_LOAD_SEQ(&device->ContextList);
     while(ctx)

+ 23 - 0
libs/openal-soft/Alc/alconfig.c

@@ -36,6 +36,9 @@
 #include <windows.h>
 #include <shlobj.h>
 #endif
+#ifdef __APPLE__
+#include <CoreFoundation/CoreFoundation.h>
+#endif
 
 #include "alMain.h"
 #include "alconfig.h"
@@ -478,6 +481,26 @@ void ReadALConfig(void)
         alstr_clear(&fname);
     }
 
+#ifdef __APPLE__
+    CFBundleRef mainBundle = CFBundleGetMainBundle();
+    if(mainBundle)
+    {
+        unsigned char fileName[PATH_MAX];
+        CFURLRef configURL;
+
+        if((configURL=CFBundleCopyResourceURL(mainBundle, CFSTR(".alsoftrc"), CFSTR(""), NULL)) &&
+           CFURLGetFileSystemRepresentation(configURL, true, fileName, sizeof(fileName)))
+        {
+            f = al_fopen((const char*)fileName, "r");
+            if(f)
+            {
+                LoadConfigFromFile(f);
+                fclose(f);
+            }
+        }
+    }
+#endif
+
     if((str=getenv("HOME")) != NULL && *str)
     {
         alstr_copy_cstr(&fname, str);

+ 19 - 10
libs/openal-soft/Alc/backends/alsa.c

@@ -1146,16 +1146,24 @@ error2:
     ll_ringbuffer_free(self->ring);
     self->ring = NULL;
     snd_pcm_close(self->pcmHandle);
+    self->pcmHandle = NULL;
 
     return ALC_INVALID_VALUE;
 }
 
 static ALCboolean ALCcaptureAlsa_start(ALCcaptureAlsa *self)
 {
-    int err = snd_pcm_start(self->pcmHandle);
+    int err = snd_pcm_prepare(self->pcmHandle);
+    if(err < 0)
+        ERR("prepare failed: %s\n", snd_strerror(err));
+    else
+    {
+        err = snd_pcm_start(self->pcmHandle);
+        if(err < 0)
+            ERR("start failed: %s\n", snd_strerror(err));
+    }
     if(err < 0)
     {
-        ERR("start failed: %s\n", snd_strerror(err));
         aluHandleDisconnect(STATIC_CAST(ALCbackend, self)->mDevice, "Capture state failure: %s",
                             snd_strerror(err));
         return ALC_FALSE;
@@ -1367,11 +1375,6 @@ static ClockLatency ALCcaptureAlsa_getClockLatency(ALCcaptureAlsa *self)
 }
 
 
-static inline void AppendAllDevicesList2(const DevMap *entry)
-{ AppendAllDevicesList(alstr_get_cstr(entry->name)); }
-static inline void AppendCaptureDeviceList2(const DevMap *entry)
-{ AppendCaptureDeviceList(alstr_get_cstr(entry->name)); }
-
 typedef struct ALCalsaBackendFactory {
     DERIVE_FROM_TYPE(ALCbackendFactory);
 } ALCalsaBackendFactory;
@@ -1409,19 +1412,25 @@ static ALCboolean ALCalsaBackendFactory_querySupport(ALCalsaBackendFactory* UNUS
     return ALC_FALSE;
 }
 
-static void ALCalsaBackendFactory_probe(ALCalsaBackendFactory* UNUSED(self), enum DevProbe type)
+static void ALCalsaBackendFactory_probe(ALCalsaBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
 {
     switch(type)
     {
+#define APPEND_OUTNAME(i) do {                                                \
+    if(!alstr_empty((i)->name))                                               \
+        alstr_append_range(outnames, VECTOR_BEGIN((i)->name),                 \
+                           VECTOR_END((i)->name)+1);                          \
+} while(0)
         case ALL_DEVICE_PROBE:
             probe_devices(SND_PCM_STREAM_PLAYBACK, &PlaybackDevices);
-            VECTOR_FOR_EACH(const DevMap, PlaybackDevices, AppendAllDevicesList2);
+            VECTOR_FOR_EACH(const DevMap, PlaybackDevices, APPEND_OUTNAME);
             break;
 
         case CAPTURE_DEVICE_PROBE:
             probe_devices(SND_PCM_STREAM_CAPTURE, &CaptureDevices);
-            VECTOR_FOR_EACH(const DevMap, CaptureDevices, AppendCaptureDeviceList2);
+            VECTOR_FOR_EACH(const DevMap, CaptureDevices, APPEND_OUTNAME);
             break;
+#undef APPEND_OUTNAME
     }
 }
 

+ 1 - 0
libs/openal-soft/Alc/backends/base.c

@@ -12,6 +12,7 @@
 extern inline ALuint64 GetDeviceClockTime(ALCdevice *device);
 extern inline void ALCdevice_Lock(ALCdevice *device);
 extern inline void ALCdevice_Unlock(ALCdevice *device);
+extern inline ClockLatency GetClockLatency(ALCdevice *device);
 
 /* Base ALCbackend method implementations. */
 void ALCbackend_Construct(ALCbackend *self, ALCdevice *device)

+ 13 - 3
libs/openal-soft/Alc/backends/base.h

@@ -3,6 +3,7 @@
 
 #include "alMain.h"
 #include "threads.h"
+#include "alstring.h"
 
 
 #ifdef __cplusplus
@@ -115,7 +116,7 @@ struct ALCbackendFactoryVtable {
 
     ALCboolean (*const querySupport)(ALCbackendFactory *self, ALCbackend_Type type);
 
-    void (*const probe)(ALCbackendFactory *self, enum DevProbe type);
+    void (*const probe)(ALCbackendFactory *self, enum DevProbe type, al_string *outnames);
 
     ALCbackend* (*const createBackend)(ALCbackendFactory *self, ALCdevice *device, ALCbackend_Type type);
 };
@@ -124,7 +125,7 @@ struct ALCbackendFactoryVtable {
 DECLARE_THUNK(T, ALCbackendFactory, ALCboolean, init)                         \
 DECLARE_THUNK(T, ALCbackendFactory, void, deinit)                             \
 DECLARE_THUNK1(T, ALCbackendFactory, ALCboolean, querySupport, ALCbackend_Type) \
-DECLARE_THUNK1(T, ALCbackendFactory, void, probe, enum DevProbe)              \
+DECLARE_THUNK2(T, ALCbackendFactory, void, probe, enum DevProbe, al_string*)  \
 DECLARE_THUNK2(T, ALCbackendFactory, ALCbackend*, createBackend, ALCdevice*, ALCbackend_Type) \
                                                                               \
 static const struct ALCbackendFactoryVtable T##_ALCbackendFactory_vtable = {  \
@@ -142,7 +143,7 @@ ALCbackendFactory *ALCcoreAudioBackendFactory_getFactory(void);
 ALCbackendFactory *ALCossBackendFactory_getFactory(void);
 ALCbackendFactory *ALCjackBackendFactory_getFactory(void);
 ALCbackendFactory *ALCsolarisBackendFactory_getFactory(void);
-ALCbackendFactory *ALCsndioBackendFactory_getFactory(void);
+ALCbackendFactory *SndioBackendFactory_getFactory(void);
 ALCbackendFactory *ALCqsaBackendFactory_getFactory(void);
 ALCbackendFactory *ALCwasapiBackendFactory_getFactory(void);
 ALCbackendFactory *ALCdsoundBackendFactory_getFactory(void);
@@ -161,6 +162,15 @@ inline void ALCdevice_Lock(ALCdevice *device)
 inline void ALCdevice_Unlock(ALCdevice *device)
 { V0(device->Backend,unlock)(); }
 
+
+inline ClockLatency GetClockLatency(ALCdevice *device)
+{
+    ClockLatency ret = V0(device->Backend,getClockLatency)();
+    ret.Latency += device->FixedLatency;
+    return ret;
+}
+
+
 #ifdef __cplusplus
 } /* extern "C" */
 #endif

+ 14 - 8
libs/openal-soft/Alc/backends/coreaudio.c

@@ -28,7 +28,6 @@
 #include "alu.h"
 #include "ringbuffer.h"
 
-#include <CoreServices/CoreServices.h>
 #include <unistd.h>
 #include <AudioUnit/AudioUnit.h>
 #include <AudioToolbox/AudioToolbox.h>
@@ -112,7 +111,11 @@ static ALCenum ALCcoreAudioPlayback_open(ALCcoreAudioPlayback *self, const ALCch
 
     /* open the default output unit */
     desc.componentType = kAudioUnitType_Output;
+#if TARGET_OS_IOS
+    desc.componentSubType = kAudioUnitSubType_RemoteIO;
+#else
     desc.componentSubType = kAudioUnitSubType_DefaultOutput;
+#endif
     desc.componentManufacturer = kAudioUnitManufacturer_Apple;
     desc.componentFlags = 0;
     desc.componentFlagsMask = 0;
@@ -451,7 +454,6 @@ static ALCenum ALCcoreAudioCapture_open(ALCcoreAudioCapture *self, const ALCchar
     AudioStreamBasicDescription outputFormat;     // The AudioUnit output format
     AURenderCallbackStruct input;
     AudioComponentDescription desc;
-    AudioDeviceID inputDevice;
     UInt32 outputFrameCount;
     UInt32 propertySize;
     AudioObjectPropertyAddress propertyAddress;
@@ -465,7 +467,11 @@ static ALCenum ALCcoreAudioCapture_open(ALCcoreAudioCapture *self, const ALCchar
         return ALC_INVALID_VALUE;
 
     desc.componentType = kAudioUnitType_Output;
+#if TARGET_OS_IOS
+    desc.componentSubType = kAudioUnitSubType_RemoteIO;
+#else
     desc.componentSubType = kAudioUnitSubType_HALOutput;
+#endif
     desc.componentManufacturer = kAudioUnitManufacturer_Apple;
     desc.componentFlags = 0;
     desc.componentFlagsMask = 0;
@@ -504,7 +510,9 @@ static ALCenum ALCcoreAudioCapture_open(ALCcoreAudioCapture *self, const ALCchar
         goto error;
     }
 
+#if !TARGET_OS_IOS
     // Get the default input device
+    AudioDeviceID inputDevice = kAudioDeviceUnknown;
 
     propertySize = sizeof(AudioDeviceID);
     propertyAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice;
@@ -517,7 +525,6 @@ static ALCenum ALCcoreAudioCapture_open(ALCcoreAudioCapture *self, const ALCchar
         ERR("AudioObjectGetPropertyData failed\n");
         goto error;
     }
-
     if(inputDevice == kAudioDeviceUnknown)
     {
         ERR("No input device found\n");
@@ -531,6 +538,7 @@ static ALCenum ALCcoreAudioCapture_open(ALCcoreAudioCapture *self, const ALCchar
         ERR("AudioUnitSetProperty failed\n");
         goto error;
     }
+#endif
 
     // set capture callback
     input.inputProc = ALCcoreAudioCapture_RecordProc;
@@ -752,7 +760,7 @@ ALCbackendFactory *ALCcoreAudioBackendFactory_getFactory(void);
 static ALCboolean ALCcoreAudioBackendFactory_init(ALCcoreAudioBackendFactory *self);
 static DECLARE_FORWARD(ALCcoreAudioBackendFactory, ALCbackendFactory, void, deinit)
 static ALCboolean ALCcoreAudioBackendFactory_querySupport(ALCcoreAudioBackendFactory *self, ALCbackend_Type type);
-static void ALCcoreAudioBackendFactory_probe(ALCcoreAudioBackendFactory *self, enum DevProbe type);
+static void ALCcoreAudioBackendFactory_probe(ALCcoreAudioBackendFactory *self, enum DevProbe type, al_string *outnames);
 static ALCbackend* ALCcoreAudioBackendFactory_createBackend(ALCcoreAudioBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
 DEFINE_ALCBACKENDFACTORY_VTABLE(ALCcoreAudioBackendFactory);
 
@@ -776,15 +784,13 @@ static ALCboolean ALCcoreAudioBackendFactory_querySupport(ALCcoreAudioBackendFac
     return ALC_FALSE;
 }
 
-static void ALCcoreAudioBackendFactory_probe(ALCcoreAudioBackendFactory* UNUSED(self), enum DevProbe type)
+static void ALCcoreAudioBackendFactory_probe(ALCcoreAudioBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
 {
     switch(type)
     {
         case ALL_DEVICE_PROBE:
-            AppendAllDevicesList(ca_device);
-            break;
         case CAPTURE_DEVICE_PROBE:
-            AppendCaptureDeviceList(ca_device);
+            alstr_append_range(outnames, ca_device, ca_device+sizeof(ca_device));
             break;
     }
 }

+ 10 - 9
libs/openal-soft/Alc/backends/dsound.c

@@ -969,11 +969,6 @@ done:
 }
 
 
-static inline void AppendAllDevicesList2(const DevMap *entry)
-{ AppendAllDevicesList(alstr_get_cstr(entry->name)); }
-static inline void AppendCaptureDeviceList2(const DevMap *entry)
-{ AppendCaptureDeviceList(alstr_get_cstr(entry->name)); }
-
 typedef struct ALCdsoundBackendFactory {
     DERIVE_FROM_TYPE(ALCbackendFactory);
 } ALCdsoundBackendFactory;
@@ -984,7 +979,7 @@ ALCbackendFactory *ALCdsoundBackendFactory_getFactory(void);
 static ALCboolean ALCdsoundBackendFactory_init(ALCdsoundBackendFactory *self);
 static void ALCdsoundBackendFactory_deinit(ALCdsoundBackendFactory *self);
 static ALCboolean ALCdsoundBackendFactory_querySupport(ALCdsoundBackendFactory *self, ALCbackend_Type type);
-static void ALCdsoundBackendFactory_probe(ALCdsoundBackendFactory *self, enum DevProbe type);
+static void ALCdsoundBackendFactory_probe(ALCdsoundBackendFactory *self, enum DevProbe type, al_string *outnames);
 static ALCbackend* ALCdsoundBackendFactory_createBackend(ALCdsoundBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
 DEFINE_ALCBACKENDFACTORY_VTABLE(ALCdsoundBackendFactory);
 
@@ -1028,7 +1023,7 @@ static ALCboolean ALCdsoundBackendFactory_querySupport(ALCdsoundBackendFactory*
     return ALC_FALSE;
 }
 
-static void ALCdsoundBackendFactory_probe(ALCdsoundBackendFactory* UNUSED(self), enum DevProbe type)
+static void ALCdsoundBackendFactory_probe(ALCdsoundBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
 {
     HRESULT hr, hrcom;
 
@@ -1036,12 +1031,17 @@ static void ALCdsoundBackendFactory_probe(ALCdsoundBackendFactory* UNUSED(self),
     hrcom = CoInitialize(NULL);
     switch(type)
     {
+#define APPEND_OUTNAME(e) do {                                                \
+    if(!alstr_empty((e)->name))                                               \
+        alstr_append_range(outnames, VECTOR_BEGIN((e)->name),                 \
+                           VECTOR_END((e)->name)+1);                          \
+} while(0)
         case ALL_DEVICE_PROBE:
             clear_devlist(&PlaybackDevices);
             hr = DirectSoundEnumerateW(DSoundEnumDevices, &PlaybackDevices);
             if(FAILED(hr))
                 ERR("Error enumerating DirectSound playback devices (0x%lx)!\n", hr);
-            VECTOR_FOR_EACH(const DevMap, PlaybackDevices, AppendAllDevicesList2);
+            VECTOR_FOR_EACH(const DevMap, PlaybackDevices, APPEND_OUTNAME);
             break;
 
         case CAPTURE_DEVICE_PROBE:
@@ -1049,8 +1049,9 @@ static void ALCdsoundBackendFactory_probe(ALCdsoundBackendFactory* UNUSED(self),
             hr = DirectSoundCaptureEnumerateW(DSoundEnumDevices, &CaptureDevices);
             if(FAILED(hr))
                 ERR("Error enumerating DirectSound capture devices (0x%lx)!\n", hr);
-            VECTOR_FOR_EACH(const DevMap, CaptureDevices, AppendCaptureDeviceList2);
+            VECTOR_FOR_EACH(const DevMap, CaptureDevices, APPEND_OUTNAME);
             break;
+#undef APPEND_OUTNAME
     }
     if(SUCCEEDED(hrcom))
         CoUninitialize();

+ 2 - 2
libs/openal-soft/Alc/backends/jack.c

@@ -571,12 +571,12 @@ static ALCboolean ALCjackBackendFactory_querySupport(ALCjackBackendFactory* UNUS
     return ALC_FALSE;
 }
 
-static void ALCjackBackendFactory_probe(ALCjackBackendFactory* UNUSED(self), enum DevProbe type)
+static void ALCjackBackendFactory_probe(ALCjackBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
 {
     switch(type)
     {
         case ALL_DEVICE_PROBE:
-            AppendAllDevicesList(jackDevice);
+            alstr_append_range(outnames, jackDevice, jackDevice+sizeof(jackDevice));
             break;
 
         case CAPTURE_DEVICE_PROBE:

+ 2 - 2
libs/openal-soft/Alc/backends/loopback.c

@@ -87,7 +87,7 @@ ALCbackendFactory *ALCloopbackFactory_getFactory(void);
 static ALCboolean ALCloopbackFactory_init(ALCloopbackFactory *self);
 static DECLARE_FORWARD(ALCloopbackFactory, ALCbackendFactory, void, deinit)
 static ALCboolean ALCloopbackFactory_querySupport(ALCloopbackFactory *self, ALCbackend_Type type);
-static void ALCloopbackFactory_probe(ALCloopbackFactory *self, enum DevProbe type);
+static void ALCloopbackFactory_probe(ALCloopbackFactory *self, enum DevProbe type, al_string *outnames);
 static ALCbackend* ALCloopbackFactory_createBackend(ALCloopbackFactory *self, ALCdevice *device, ALCbackend_Type type);
 DEFINE_ALCBACKENDFACTORY_VTABLE(ALCloopbackFactory);
 
@@ -110,7 +110,7 @@ static ALCboolean ALCloopbackFactory_querySupport(ALCloopbackFactory* UNUSED(sel
     return ALC_FALSE;
 }
 
-static void ALCloopbackFactory_probe(ALCloopbackFactory* UNUSED(self), enum DevProbe UNUSED(type))
+static void ALCloopbackFactory_probe(ALCloopbackFactory* UNUSED(self), enum DevProbe UNUSED(type), al_string* UNUSED(outnames))
 {
 }
 

+ 3 - 4
libs/openal-soft/Alc/backends/null.c

@@ -171,7 +171,7 @@ ALCbackendFactory *ALCnullBackendFactory_getFactory(void);
 static ALCboolean ALCnullBackendFactory_init(ALCnullBackendFactory *self);
 static DECLARE_FORWARD(ALCnullBackendFactory, ALCbackendFactory, void, deinit)
 static ALCboolean ALCnullBackendFactory_querySupport(ALCnullBackendFactory *self, ALCbackend_Type type);
-static void ALCnullBackendFactory_probe(ALCnullBackendFactory *self, enum DevProbe type);
+static void ALCnullBackendFactory_probe(ALCnullBackendFactory *self, enum DevProbe type, al_string *outnames);
 static ALCbackend* ALCnullBackendFactory_createBackend(ALCnullBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
 DEFINE_ALCBACKENDFACTORY_VTABLE(ALCnullBackendFactory);
 
@@ -195,14 +195,13 @@ static ALCboolean ALCnullBackendFactory_querySupport(ALCnullBackendFactory* UNUS
     return ALC_FALSE;
 }
 
-static void ALCnullBackendFactory_probe(ALCnullBackendFactory* UNUSED(self), enum DevProbe type)
+static void ALCnullBackendFactory_probe(ALCnullBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
 {
     switch(type)
     {
         case ALL_DEVICE_PROBE:
-            AppendAllDevicesList(nullDevice);
-            break;
         case CAPTURE_DEVICE_PROBE:
+            alstr_append_range(outnames, nullDevice, nullDevice+sizeof(nullDevice));
             break;
     }
 }

+ 68 - 72
libs/openal-soft/Alc/backends/opensl.c

@@ -206,6 +206,9 @@ static void ALCopenslPlayback_Destruct(ALCopenslPlayback* self)
     self->mEngineObj = NULL;
     self->mEngine = NULL;
 
+    ll_ringbuffer_free(self->mRing);
+    self->mRing = NULL;
+
     alsem_destroy(&self->mSem);
 
     ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
@@ -251,19 +254,16 @@ static int ALCopenslPlayback_mixerProc(void *arg)
         result = VCALL(self->mBufferQueueObj,GetInterface)(SL_IID_PLAY, &player);
         PRINTERR(result, "bufferQueue->GetInterface SL_IID_PLAY");
     }
+
+    ALCopenslPlayback_lock(self);
     if(SL_RESULT_SUCCESS != result)
-    {
-        ALCopenslPlayback_lock(self);
         aluHandleDisconnect(device, "Failed to get playback buffer: 0x%08x", result);
-        ALCopenslPlayback_unlock(self);
-        return 1;
-    }
 
-    ALCopenslPlayback_lock(self);
-    while(!ATOMIC_LOAD(&self->mKillNow, almemory_order_acquire) &&
+    while(SL_RESULT_SUCCESS == result &&
+          !ATOMIC_LOAD(&self->mKillNow, almemory_order_acquire) &&
           ATOMIC_LOAD(&device->Connected, almemory_order_acquire))
     {
-        size_t todo, len0, len1;
+        size_t todo;
 
         if(ll_ringbuffer_write_space(self->mRing) == 0)
         {
@@ -292,34 +292,33 @@ static int ALCopenslPlayback_mixerProc(void *arg)
         }
 
         ll_ringbuffer_get_write_vector(self->mRing, data);
-        todo = data[0].len+data[1].len;
 
-        len0 = minu(todo, data[0].len);
-        len1 = minu(todo-len0, data[1].len);
+        aluMixData(device, data[0].buf, data[0].len*device->UpdateSize);
+        if(data[1].len > 0)
+            aluMixData(device, data[1].buf, data[1].len*device->UpdateSize);
 
-        aluMixData(device, data[0].buf, len0*device->UpdateSize);
-        for(size_t i = 0;i < len0;i++)
-        {
-            result = VCALL(bufferQueue,Enqueue)(data[0].buf, device->UpdateSize*self->mFrameSize);
-            PRINTERR(result, "bufferQueue->Enqueue");
-            if(SL_RESULT_SUCCESS == result)
-                ll_ringbuffer_write_advance(self->mRing, 1);
-
-            data[0].buf += device->UpdateSize*self->mFrameSize;
-        }
+        todo = data[0].len+data[1].len;
+        ll_ringbuffer_write_advance(self->mRing, todo);
 
-        if(len1 > 0)
+        for(size_t i = 0;i < todo;i++)
         {
-            aluMixData(device, data[1].buf, len1*device->UpdateSize);
-            for(size_t i = 0;i < len1;i++)
+            if(!data[0].len)
             {
-                result = VCALL(bufferQueue,Enqueue)(data[1].buf, device->UpdateSize*self->mFrameSize);
-                PRINTERR(result, "bufferQueue->Enqueue");
-                if(SL_RESULT_SUCCESS == result)
-                    ll_ringbuffer_write_advance(self->mRing, 1);
+                data[0] = data[1];
+                data[1].buf = NULL;
+                data[1].len = 0;
+            }
 
-                data[1].buf += device->UpdateSize*self->mFrameSize;
+            result = VCALL(bufferQueue,Enqueue)(data[0].buf, device->UpdateSize*self->mFrameSize);
+            PRINTERR(result, "bufferQueue->Enqueue");
+            if(SL_RESULT_SUCCESS != result)
+            {
+                aluHandleDisconnect(device, "Failed to queue audio: 0x%08x", result);
+                break;
             }
+
+            data[0].len--;
+            data[0].buf += device->UpdateSize*self->mFrameSize;
         }
     }
     ALCopenslPlayback_unlock(self);
@@ -392,19 +391,24 @@ static ALCboolean ALCopenslPlayback_reset(ALCopenslPlayback *self)
     SLInterfaceID ids[2];
     SLboolean reqs[2];
     SLresult result;
-    JNIEnv *env;
 
     if(self->mBufferQueueObj != NULL)
         VCALL0(self->mBufferQueueObj,Destroy)();
     self->mBufferQueueObj = NULL;
 
+    ll_ringbuffer_free(self->mRing);
+    self->mRing = NULL;
+
     sampleRate = device->Frequency;
-    if(!(device->Flags&DEVICE_FREQUENCY_REQUEST) && (env=Android_GetJNIEnv()) != NULL)
+#if 0
+    if(!(device->Flags&DEVICE_FREQUENCY_REQUEST))
     {
         /* FIXME: Disabled until I figure out how to get the Context needed for
          * the getSystemService call.
          */
-#if 0
+        JNIEnv *env = Android_GetJNIEnv();
+        jobject jctx = Android_GetContext();
+
         /* Get necessary stuff for using java.lang.Integer,
          * android.content.Context, and android.media.AudioManager.
          */
@@ -440,7 +444,7 @@ static ALCboolean ALCopenslPlayback_reset(ALCopenslPlayback *self)
         /* Now make the calls. */
         //AudioManager audMgr = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
         strobj = JCALL(env,GetStaticObjectField)(ctx_cls, ctx_audsvc);
-        jobject audMgr = JCALL(env,CallObjectMethod)(ctx_cls, ctx_getSysSvc, strobj);
+        jobject audMgr = JCALL(env,CallObjectMethod)(jctx, ctx_getSysSvc, strobj);
         strchars = JCALL(env,GetStringUTFChars)(strobj, NULL);
         TRACE("Context.getSystemService(%s) = %p\n", strchars, audMgr);
         JCALL(env,ReleaseStringUTFChars)(strobj, strchars);
@@ -461,8 +465,8 @@ static ALCboolean ALCopenslPlayback_reset(ALCopenslPlayback *self)
 
         if(!sampleRate) sampleRate = device->Frequency;
         else sampleRate = maxu(sampleRate, MIN_OUTPUT_RATE);
-#endif
     }
+#endif
 
     if(sampleRate != device->Frequency)
     {
@@ -546,6 +550,18 @@ static ALCboolean ALCopenslPlayback_reset(ALCopenslPlayback *self)
         result = VCALL(self->mBufferQueueObj,Realize)(SL_BOOLEAN_FALSE);
         PRINTERR(result, "bufferQueue->Realize");
     }
+    if(SL_RESULT_SUCCESS == result)
+    {
+        self->mRing = ll_ringbuffer_create(device->NumUpdates,
+            self->mFrameSize*device->UpdateSize, true
+        );
+        if(!self->mRing)
+        {
+            ERR("Out of memory allocating ring buffer %ux%u %u\n", device->UpdateSize,
+                device->NumUpdates, self->mFrameSize);
+            result = SL_RESULT_MEMORY_FAILURE;
+        }
+    }
 
     if(SL_RESULT_SUCCESS != result)
     {
@@ -561,13 +577,10 @@ static ALCboolean ALCopenslPlayback_reset(ALCopenslPlayback *self)
 
 static ALCboolean ALCopenslPlayback_start(ALCopenslPlayback *self)
 {
-    ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
     SLAndroidSimpleBufferQueueItf bufferQueue;
     SLresult result;
 
-    ll_ringbuffer_free(self->mRing);
-    self->mRing = ll_ringbuffer_create(device->NumUpdates, self->mFrameSize*device->UpdateSize,
-                                       true);
+    ll_ringbuffer_reset(self->mRing);
 
     result = VCALL(self->mBufferQueueObj,GetInterface)(SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
                                                        &bufferQueue);
@@ -634,9 +647,6 @@ static void ALCopenslPlayback_stop(ALCopenslPlayback *self)
         } while(SL_RESULT_SUCCESS == result && state.count > 0);
         PRINTERR(result, "bufferQueue->GetState");
     }
-
-    ll_ringbuffer_free(self->mRing);
-    self->mRing = NULL;
 }
 
 static ClockLatency ALCopenslPlayback_getClockLatency(ALCopenslPlayback *self)
@@ -713,9 +723,6 @@ static void ALCopenslCapture_Construct(ALCopenslCapture *self, ALCdevice *device
 
 static void ALCopenslCapture_Destruct(ALCopenslCapture *self)
 {
-    ll_ringbuffer_free(self->mRing);
-    self->mRing = NULL;
-
     if(self->mRecordObj != NULL)
         VCALL0(self->mRecordObj,Destroy)();
     self->mRecordObj = NULL;
@@ -725,6 +732,9 @@ static void ALCopenslCapture_Destruct(ALCopenslCapture *self)
     self->mEngineObj = NULL;
     self->mEngine = NULL;
 
+    ll_ringbuffer_free(self->mRing);
+    self->mRing = NULL;
+
     ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
 }
 
@@ -843,8 +853,9 @@ static ALCenum ALCopenslCapture_open(ALCopenslCapture *self, const ALCchar *name
 
     if(SL_RESULT_SUCCESS == result)
     {
-        self->mRing = ll_ringbuffer_create(device->NumUpdates, device->UpdateSize*self->mFrameSize,
-                                           false);
+        self->mRing = ll_ringbuffer_create(device->NumUpdates,
+            device->UpdateSize*self->mFrameSize, false
+        );
 
         result = VCALL(self->mRecordObj,GetInterface)(SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
                                                       &bufferQueue);
@@ -941,14 +952,16 @@ static ALCenum ALCopenslCapture_captureSamples(ALCopenslCapture *self, ALCvoid *
     SLAndroidSimpleBufferQueueItf bufferQueue;
     ll_ringbuffer_data_t data[2];
     SLresult result;
-    size_t advance;
     ALCuint i;
 
+    result = VCALL(self->mRecordObj,GetInterface)(SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
+                                                  &bufferQueue);
+    PRINTERR(result, "recordObj->GetInterface");
+
     /* Read the desired samples from the ring buffer then advance its read
      * pointer.
      */
     ll_ringbuffer_get_read_vector(self->mRing, data);
-    advance = 0;
     for(i = 0;i < samples;)
     {
         ALCuint rem = minu(samples - i, device->UpdateSize - self->mSplOffset);
@@ -961,7 +974,11 @@ static ALCenum ALCopenslCapture_captureSamples(ALCopenslCapture *self, ALCvoid *
         {
             /* Finished a chunk, reset the offset and advance the read pointer. */
             self->mSplOffset = 0;
-            advance++;
+
+            ll_ringbuffer_read_advance(self->mRing, 1);
+            result = VCALL(bufferQueue,Enqueue)(data[0].buf, chunk_size);
+            PRINTERR(result, "bufferQueue->Enqueue");
+            if(SL_RESULT_SUCCESS != result) break;
 
             data[0].len--;
             if(!data[0].len)
@@ -972,24 +989,6 @@ static ALCenum ALCopenslCapture_captureSamples(ALCopenslCapture *self, ALCvoid *
 
         i += rem;
     }
-    ll_ringbuffer_read_advance(self->mRing, advance);
-
-    result = VCALL(self->mRecordObj,GetInterface)(SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
-                                                  &bufferQueue);
-    PRINTERR(result, "recordObj->GetInterface");
-
-    /* Enqueue any newly-writable chunks in the ring buffer. */
-    ll_ringbuffer_get_write_vector(self->mRing, data);
-    for(i = 0;i < data[0].len && SL_RESULT_SUCCESS == result;i++)
-    {
-        result = VCALL(bufferQueue,Enqueue)(data[0].buf + chunk_size*i, chunk_size);
-        PRINTERR(result, "bufferQueue->Enqueue");
-    }
-    for(i = 0;i < data[1].len && SL_RESULT_SUCCESS == result;i++)
-    {
-        result = VCALL(bufferQueue,Enqueue)(data[1].buf + chunk_size*i, chunk_size);
-        PRINTERR(result, "bufferQueue->Enqueue");
-    }
 
     if(SL_RESULT_SUCCESS != result)
     {
@@ -1030,16 +1029,13 @@ static ALCboolean ALCopenslBackendFactory_querySupport(ALCopenslBackendFactory*
     return ALC_FALSE;
 }
 
-static void ALCopenslBackendFactory_probe(ALCopenslBackendFactory* UNUSED(self), enum DevProbe type)
+static void ALCopenslBackendFactory_probe(ALCopenslBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
 {
     switch(type)
     {
         case ALL_DEVICE_PROBE:
-            AppendAllDevicesList(opensl_device);
-            break;
-
         case CAPTURE_DEVICE_PROBE:
-            AppendAllDevicesList(opensl_device);
+            alstr_append_range(outnames, opensl_device, opensl_device+sizeof(opensl_device));
             break;
     }
 }

+ 11 - 20
libs/openal-soft/Alc/backends/oss.c

@@ -786,7 +786,7 @@ ALCbackendFactory *ALCossBackendFactory_getFactory(void);
 static ALCboolean ALCossBackendFactory_init(ALCossBackendFactory *self);
 static void ALCossBackendFactory_deinit(ALCossBackendFactory *self);
 static ALCboolean ALCossBackendFactory_querySupport(ALCossBackendFactory *self, ALCbackend_Type type);
-static void ALCossBackendFactory_probe(ALCossBackendFactory *self, enum DevProbe type);
+static void ALCossBackendFactory_probe(ALCossBackendFactory *self, enum DevProbe type, al_string *outnames);
 static ALCbackend* ALCossBackendFactory_createBackend(ALCossBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
 DEFINE_ALCBACKENDFACTORY_VTABLE(ALCossBackendFactory);
 
@@ -820,40 +820,31 @@ ALCboolean ALCossBackendFactory_querySupport(ALCossBackendFactory* UNUSED(self),
     return ALC_FALSE;
 }
 
-void ALCossBackendFactory_probe(ALCossBackendFactory* UNUSED(self), enum DevProbe type)
+void ALCossBackendFactory_probe(ALCossBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
 {
-    struct oss_device *cur;
+    struct oss_device *cur = NULL;
     switch(type)
     {
         case ALL_DEVICE_PROBE:
             ALCossListFree(&oss_playback);
             ALCossListPopulate(&oss_playback, DSP_CAP_OUTPUT);
             cur = &oss_playback;
-            while(cur != NULL)
-            {
-#ifdef HAVE_STAT
-                struct stat buf;
-                if(stat(cur->path, &buf) == 0)
-#endif
-                    AppendAllDevicesList(cur->handle);
-                cur = cur->next;
-            }
             break;
 
         case CAPTURE_DEVICE_PROBE:
             ALCossListFree(&oss_capture);
             ALCossListPopulate(&oss_capture, DSP_CAP_INPUT);
             cur = &oss_capture;
-            while(cur != NULL)
-            {
+            break;
+    }
+    while(cur != NULL)
+    {
 #ifdef HAVE_STAT
-                struct stat buf;
-                if(stat(cur->path, &buf) == 0)
+        struct stat buf;
+        if(stat(cur->path, &buf) == 0)
 #endif
-                    AppendCaptureDeviceList(cur->handle);
-                cur = cur->next;
-            }
-            break;
+            alstr_append_range(outnames, cur->handle, cur->handle+strlen(cur->handle)+1);
+        cur = cur->next;
     }
 }
 

+ 3 - 6
libs/openal-soft/Alc/backends/portaudio.c

@@ -484,9 +484,8 @@ typedef struct ALCportBackendFactory {
 static ALCboolean ALCportBackendFactory_init(ALCportBackendFactory *self);
 static void ALCportBackendFactory_deinit(ALCportBackendFactory *self);
 static ALCboolean ALCportBackendFactory_querySupport(ALCportBackendFactory *self, ALCbackend_Type type);
-static void ALCportBackendFactory_probe(ALCportBackendFactory *self, enum DevProbe type);
+static void ALCportBackendFactory_probe(ALCportBackendFactory *self, enum DevProbe type, al_string *outnames);
 static ALCbackend* ALCportBackendFactory_createBackend(ALCportBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
-
 DEFINE_ALCBACKENDFACTORY_VTABLE(ALCportBackendFactory);
 
 
@@ -518,15 +517,13 @@ static ALCboolean ALCportBackendFactory_querySupport(ALCportBackendFactory* UNUS
     return ALC_FALSE;
 }
 
-static void ALCportBackendFactory_probe(ALCportBackendFactory* UNUSED(self), enum DevProbe type)
+static void ALCportBackendFactory_probe(ALCportBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
 {
     switch(type)
     {
         case ALL_DEVICE_PROBE:
-            AppendAllDevicesList(pa_device);
-            break;
         case CAPTURE_DEVICE_PROBE:
-            AppendCaptureDeviceList(pa_device);
+            alstr_append_range(outnames, pa_device, pa_device+sizeof(pa_device));
             break;
     }
 }

+ 11 - 10
libs/openal-soft/Alc/backends/pulseaudio.c

@@ -1760,9 +1760,8 @@ typedef struct ALCpulseBackendFactory {
 static ALCboolean ALCpulseBackendFactory_init(ALCpulseBackendFactory *self);
 static void ALCpulseBackendFactory_deinit(ALCpulseBackendFactory *self);
 static ALCboolean ALCpulseBackendFactory_querySupport(ALCpulseBackendFactory *self, ALCbackend_Type type);
-static void ALCpulseBackendFactory_probe(ALCpulseBackendFactory *self, enum DevProbe type);
+static void ALCpulseBackendFactory_probe(ALCpulseBackendFactory *self, enum DevProbe type, al_string *outnames);
 static ALCbackend* ALCpulseBackendFactory_createBackend(ALCpulseBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
-
 DEFINE_ALCBACKENDFACTORY_VTABLE(ALCpulseBackendFactory);
 
 
@@ -1835,23 +1834,25 @@ static ALCboolean ALCpulseBackendFactory_querySupport(ALCpulseBackendFactory* UN
     return ALC_FALSE;
 }
 
-static void ALCpulseBackendFactory_probe(ALCpulseBackendFactory* UNUSED(self), enum DevProbe type)
+static void ALCpulseBackendFactory_probe(ALCpulseBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
 {
     switch(type)
     {
+#define APPEND_OUTNAME(e) do {                                                \
+    if(!alstr_empty((e)->name))                                               \
+        alstr_append_range(outnames, VECTOR_BEGIN((e)->name),                 \
+                           VECTOR_END((e)->name)+1);                          \
+} while(0)
         case ALL_DEVICE_PROBE:
             ALCpulsePlayback_probeDevices();
-#define APPEND_ALL_DEVICES_LIST(e)  AppendAllDevicesList(alstr_get_cstr((e)->name))
-            VECTOR_FOR_EACH(const DevMap, PlaybackDevices, APPEND_ALL_DEVICES_LIST);
-#undef APPEND_ALL_DEVICES_LIST
+            VECTOR_FOR_EACH(const DevMap, PlaybackDevices, APPEND_OUTNAME);
             break;
 
         case CAPTURE_DEVICE_PROBE:
             ALCpulseCapture_probeDevices();
-#define APPEND_CAPTURE_DEVICE_LIST(e) AppendCaptureDeviceList(alstr_get_cstr((e)->name))
-            VECTOR_FOR_EACH(const DevMap, CaptureDevices, APPEND_CAPTURE_DEVICE_LIST);
-#undef APPEND_CAPTURE_DEVICE_LIST
+            VECTOR_FOR_EACH(const DevMap, CaptureDevices, APPEND_OUTNAME);
             break;
+#undef APPEND_OUTNAME
     }
 }
 
@@ -1899,7 +1900,7 @@ static ALCboolean ALCpulseBackendFactory_querySupport(ALCpulseBackendFactory* UN
     return ALC_FALSE;
 }
 
-static void ALCpulseBackendFactory_probe(ALCpulseBackendFactory* UNUSED(self), enum DevProbe UNUSED(type))
+static void ALCpulseBackendFactory_probe(ALCpulseBackendFactory* UNUSED(self), enum DevProbe UNUSED(type), al_string* UNUSED(outnames))
 {
 }
 

+ 13 - 18
libs/openal-soft/Alc/backends/qsa.c

@@ -119,6 +119,9 @@ static void deviceList(int type, vector_DevMap *devmap)
     if(max_cards < 0)
         return;
 
+#define FREE_NAME(iter) free((iter)->name)
+    VECTOR_FOR_EACH(DevMap, *devmap, FREE_NAME);
+#undef FREE_NAME
     VECTOR_RESIZE(*devmap, 0, max_cards+1);
 
     entry.name = strdup(qsaDevice);
@@ -989,7 +992,7 @@ typedef struct ALCqsaBackendFactory {
 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 void ALCqsaBackendFactory_probe(ALCqsaBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames);
 static ALCbackend* ALCqsaBackendFactory_createBackend(ALCqsaBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type);
 DEFINE_ALCBACKENDFACTORY_VTABLE(ALCqsaBackendFactory);
 
@@ -1016,33 +1019,25 @@ static ALCboolean ALCqsaBackendFactory_querySupport(ALCqsaBackendFactory* UNUSED
     return ALC_FALSE;
 }
 
-static void ALCqsaBackendFactory_probe(ALCqsaBackendFactory* UNUSED(self), enum DevProbe type)
+static void ALCqsaBackendFactory_probe(ALCqsaBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
 {
     switch (type)
     {
+#define APPEND_OUTNAME(e) do {                                                \
+    const char *n_ = (e)->name;                                               \
+    if(n_ && n_[0])                                                           \
+        alstr_append_range(outnames, n_, n_+strlen(n_)+1);                    \
+} while(0)
         case ALL_DEVICE_PROBE:
-#define FREE_NAME(iter) free((iter)->name)
-            VECTOR_FOR_EACH(DevMap, DeviceNameMap, FREE_NAME);
-            VECTOR_RESIZE(DeviceNameMap, 0, 0);
-#undef FREE_NAME
-
             deviceList(SND_PCM_CHANNEL_PLAYBACK, &DeviceNameMap);
-#define APPEND_DEVICE(iter) AppendAllDevicesList((iter)->name)
-            VECTOR_FOR_EACH(const DevMap, DeviceNameMap, APPEND_DEVICE);
-#undef APPEND_DEVICE
+            VECTOR_FOR_EACH(const DevMap, DeviceNameMap, APPEND_OUTNAME);
             break;
 
         case CAPTURE_DEVICE_PROBE:
-#define FREE_NAME(iter) free((iter)->name)
-            VECTOR_FOR_EACH(DevMap, CaptureNameMap, FREE_NAME);
-            VECTOR_RESIZE(CaptureNameMap, 0, 0);
-#undef FREE_NAME
-
             deviceList(SND_PCM_CHANNEL_CAPTURE, &CaptureNameMap);
-#define APPEND_DEVICE(iter) AppendCaptureDeviceList((iter)->name)
-            VECTOR_FOR_EACH(const DevMap, CaptureNameMap, APPEND_DEVICE);
-#undef APPEND_DEVICE
+            VECTOR_FOR_EACH(const DevMap, CaptureNameMap, APPEND_OUTNAME);
             break;
+#undef APPEND_OUTNAME
     }
 }
 

+ 5 - 4
libs/openal-soft/Alc/backends/sdl2.c

@@ -221,7 +221,7 @@ ALCbackendFactory *ALCsdl2BackendFactory_getFactory(void);
 static ALCboolean ALCsdl2BackendFactory_init(ALCsdl2BackendFactory *self);
 static void ALCsdl2BackendFactory_deinit(ALCsdl2BackendFactory *self);
 static ALCboolean ALCsdl2BackendFactory_querySupport(ALCsdl2BackendFactory *self, ALCbackend_Type type);
-static void ALCsdl2BackendFactory_probe(ALCsdl2BackendFactory *self, enum DevProbe type);
+static void ALCsdl2BackendFactory_probe(ALCsdl2BackendFactory *self, enum DevProbe type, al_string *outnames);
 static ALCbackend* ALCsdl2BackendFactory_createBackend(ALCsdl2BackendFactory *self, ALCdevice *device, ALCbackend_Type type);
 DEFINE_ALCBACKENDFACTORY_VTABLE(ALCsdl2BackendFactory);
 
@@ -252,7 +252,7 @@ static ALCboolean ALCsdl2BackendFactory_querySupport(ALCsdl2BackendFactory* UNUS
     return ALC_FALSE;
 }
 
-static void ALCsdl2BackendFactory_probe(ALCsdl2BackendFactory* UNUSED(self), enum DevProbe type)
+static void ALCsdl2BackendFactory_probe(ALCsdl2BackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
 {
     int num_devices, i;
     al_string name;
@@ -263,12 +263,13 @@ static void ALCsdl2BackendFactory_probe(ALCsdl2BackendFactory* UNUSED(self), enu
     AL_STRING_INIT(name);
     num_devices = SDL_GetNumAudioDevices(SDL_FALSE);
 
-    AppendAllDevicesList(defaultDeviceName);
+    alstr_append_range(outnames, defaultDeviceName, defaultDeviceName+sizeof(defaultDeviceName));
     for(i = 0;i < num_devices;++i)
     {
         alstr_copy_cstr(&name, DEVNAME_PREFIX);
         alstr_append_cstr(&name, SDL_GetAudioDeviceName(i, SDL_FALSE));
-        AppendAllDevicesList(alstr_get_cstr(name));
+        if(!alstr_empty(name))
+            alstr_append_range(outnames, VECTOR_BEGIN(name), VECTOR_END(name)+1);
     }
     alstr_reset(&name);
 }

+ 312 - 54
libs/openal-soft/Alc/backends/sndio.c

@@ -27,15 +27,17 @@
 #include "alMain.h"
 #include "alu.h"
 #include "threads.h"
+#include "ringbuffer.h"
 
 #include "backends/base.h"
 
 #include <sndio.h>
 
 
+static const ALCchar sndio_device[] = "SndIO Default";
 
 
-typedef struct ALCsndioBackend {
+typedef struct SndioPlayback {
     DERIVE_FROM_TYPE(ALCbackend);
 
     struct sio_hdl *sndHandle;
@@ -45,40 +47,37 @@ typedef struct ALCsndioBackend {
 
     ATOMIC(int) killNow;
     althrd_t thread;
-} ALCsndioBackend;
-
-static int ALCsndioBackend_mixerProc(void *ptr);
-
-static void ALCsndioBackend_Construct(ALCsndioBackend *self, ALCdevice *device);
-static void ALCsndioBackend_Destruct(ALCsndioBackend *self);
-static ALCenum ALCsndioBackend_open(ALCsndioBackend *self, const ALCchar *name);
-static ALCboolean ALCsndioBackend_reset(ALCsndioBackend *self);
-static ALCboolean ALCsndioBackend_start(ALCsndioBackend *self);
-static void ALCsndioBackend_stop(ALCsndioBackend *self);
-static DECLARE_FORWARD2(ALCsndioBackend, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
-static DECLARE_FORWARD(ALCsndioBackend, ALCbackend, ALCuint, availableSamples)
-static DECLARE_FORWARD(ALCsndioBackend, ALCbackend, ClockLatency, getClockLatency)
-static DECLARE_FORWARD(ALCsndioBackend, ALCbackend, void, lock)
-static DECLARE_FORWARD(ALCsndioBackend, ALCbackend, void, unlock)
-DECLARE_DEFAULT_ALLOCATORS(ALCsndioBackend)
+} SndioPlayback;
 
-DEFINE_ALCBACKEND_VTABLE(ALCsndioBackend);
+static int SndioPlayback_mixerProc(void *ptr);
 
+static void SndioPlayback_Construct(SndioPlayback *self, ALCdevice *device);
+static void SndioPlayback_Destruct(SndioPlayback *self);
+static ALCenum SndioPlayback_open(SndioPlayback *self, const ALCchar *name);
+static ALCboolean SndioPlayback_reset(SndioPlayback *self);
+static ALCboolean SndioPlayback_start(SndioPlayback *self);
+static void SndioPlayback_stop(SndioPlayback *self);
+static DECLARE_FORWARD2(SndioPlayback, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
+static DECLARE_FORWARD(SndioPlayback, ALCbackend, ALCuint, availableSamples)
+static DECLARE_FORWARD(SndioPlayback, ALCbackend, ClockLatency, getClockLatency)
+static DECLARE_FORWARD(SndioPlayback, ALCbackend, void, lock)
+static DECLARE_FORWARD(SndioPlayback, ALCbackend, void, unlock)
+DECLARE_DEFAULT_ALLOCATORS(SndioPlayback)
 
-static const ALCchar sndio_device[] = "SndIO Default";
+DEFINE_ALCBACKEND_VTABLE(SndioPlayback);
 
 
-static void ALCsndioBackend_Construct(ALCsndioBackend *self, ALCdevice *device)
+static void SndioPlayback_Construct(SndioPlayback *self, ALCdevice *device)
 {
     ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
-    SET_VTABLE2(ALCsndioBackend, ALCbackend, self);
+    SET_VTABLE2(SndioPlayback, ALCbackend, self);
 
     self->sndHandle = NULL;
     self->mix_data = NULL;
     ATOMIC_INIT(&self->killNow, AL_TRUE);
 }
 
-static void ALCsndioBackend_Destruct(ALCsndioBackend *self)
+static void SndioPlayback_Destruct(SndioPlayback *self)
 {
     if(self->sndHandle)
         sio_close(self->sndHandle);
@@ -91,9 +90,9 @@ static void ALCsndioBackend_Destruct(ALCsndioBackend *self)
 }
 
 
-static int ALCsndioBackend_mixerProc(void *ptr)
+static int SndioPlayback_mixerProc(void *ptr)
 {
-    ALCsndioBackend *self = (ALCsndioBackend*)ptr;
+    SndioPlayback *self = (SndioPlayback*)ptr;
     ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
     ALsizei frameSize;
     size_t wrote;
@@ -109,9 +108,9 @@ static int ALCsndioBackend_mixerProc(void *ptr)
         ALsizei len = self->data_size;
         ALubyte *WritePtr = self->mix_data;
 
-        ALCsndioBackend_lock(self);
+        SndioPlayback_lock(self);
         aluMixData(device, WritePtr, len/frameSize);
-        ALCsndioBackend_unlock(self);
+        SndioPlayback_unlock(self);
         while(len > 0 && !ATOMIC_LOAD(&self->killNow, almemory_order_acquire))
         {
             wrote = sio_write(self->sndHandle, WritePtr, len);
@@ -133,7 +132,7 @@ static int ALCsndioBackend_mixerProc(void *ptr)
 }
 
 
-static ALCenum ALCsndioBackend_open(ALCsndioBackend *self, const ALCchar *name)
+static ALCenum SndioPlayback_open(SndioPlayback *self, const ALCchar *name)
 {
     ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
 
@@ -154,7 +153,7 @@ static ALCenum ALCsndioBackend_open(ALCsndioBackend *self, const ALCchar *name)
     return ALC_NO_ERROR;
 }
 
-static ALCboolean ALCsndioBackend_reset(ALCsndioBackend *self)
+static ALCboolean SndioPlayback_reset(SndioPlayback *self)
 {
     ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
     struct sio_par par;
@@ -239,7 +238,7 @@ static ALCboolean ALCsndioBackend_reset(ALCsndioBackend *self)
     return ALC_TRUE;
 }
 
-static ALCboolean ALCsndioBackend_start(ALCsndioBackend *self)
+static ALCboolean SndioPlayback_start(SndioPlayback *self)
 {
     ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
 
@@ -256,7 +255,7 @@ static ALCboolean ALCsndioBackend_start(ALCsndioBackend *self)
     }
 
     ATOMIC_STORE(&self->killNow, AL_FALSE, almemory_order_release);
-    if(althrd_create(&self->thread, ALCsndioBackend_mixerProc, self) != althrd_success)
+    if(althrd_create(&self->thread, SndioPlayback_mixerProc, self) != althrd_success)
     {
         sio_stop(self->sndHandle);
         return ALC_FALSE;
@@ -265,7 +264,7 @@ static ALCboolean ALCsndioBackend_start(ALCsndioBackend *self)
     return ALC_TRUE;
 }
 
-static void ALCsndioBackend_stop(ALCsndioBackend *self)
+static void SndioPlayback_stop(SndioPlayback *self)
 {
     int res;
 
@@ -281,59 +280,318 @@ static void ALCsndioBackend_stop(ALCsndioBackend *self)
 }
 
 
-typedef struct ALCsndioBackendFactory {
-    DERIVE_FROM_TYPE(ALCbackendFactory);
-} ALCsndioBackendFactory;
-#define ALCSNDIOBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCsndioBackendFactory, ALCbackendFactory) } }
+typedef struct SndioCapture {
+    DERIVE_FROM_TYPE(ALCbackend);
+
+    struct sio_hdl *sndHandle;
+
+    ll_ringbuffer_t *ring;
+
+    ATOMIC(int) killNow;
+    althrd_t thread;
+} SndioCapture;
+
+static int SndioCapture_recordProc(void *ptr);
 
-ALCbackendFactory *ALCsndioBackendFactory_getFactory(void);
+static void SndioCapture_Construct(SndioCapture *self, ALCdevice *device);
+static void SndioCapture_Destruct(SndioCapture *self);
+static ALCenum SndioCapture_open(SndioCapture *self, const ALCchar *name);
+static DECLARE_FORWARD(SndioCapture, ALCbackend, ALCboolean, reset)
+static ALCboolean SndioCapture_start(SndioCapture *self);
+static void SndioCapture_stop(SndioCapture *self);
+static ALCenum SndioCapture_captureSamples(SndioCapture *self, void *buffer, ALCuint samples);
+static ALCuint SndioCapture_availableSamples(SndioCapture *self);
+static DECLARE_FORWARD(SndioCapture, ALCbackend, ClockLatency, getClockLatency)
+static DECLARE_FORWARD(SndioCapture, ALCbackend, void, lock)
+static DECLARE_FORWARD(SndioCapture, ALCbackend, void, unlock)
+DECLARE_DEFAULT_ALLOCATORS(SndioCapture)
 
-static ALCboolean ALCsndioBackendFactory_init(ALCsndioBackendFactory *self);
-static DECLARE_FORWARD(ALCsndioBackendFactory, ALCbackendFactory, void, deinit)
-static ALCboolean ALCsndioBackendFactory_querySupport(ALCsndioBackendFactory *self, ALCbackend_Type type);
-static void ALCsndioBackendFactory_probe(ALCsndioBackendFactory *self, enum DevProbe type);
-static ALCbackend* ALCsndioBackendFactory_createBackend(ALCsndioBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
-DEFINE_ALCBACKENDFACTORY_VTABLE(ALCsndioBackendFactory);
+DEFINE_ALCBACKEND_VTABLE(SndioCapture);
 
 
-ALCbackendFactory *ALCsndioBackendFactory_getFactory(void)
+static void SndioCapture_Construct(SndioCapture *self, ALCdevice *device)
 {
-    static ALCsndioBackendFactory factory = ALCSNDIOBACKENDFACTORY_INITIALIZER;
-    return STATIC_CAST(ALCbackendFactory, &factory);
+    ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
+    SET_VTABLE2(SndioCapture, ALCbackend, self);
+
+    self->sndHandle = NULL;
+    self->ring = NULL;
+    ATOMIC_INIT(&self->killNow, AL_TRUE);
+}
+
+static void SndioCapture_Destruct(SndioCapture *self)
+{
+    if(self->sndHandle)
+        sio_close(self->sndHandle);
+    self->sndHandle = NULL;
+
+    ll_ringbuffer_free(self->ring);
+    self->ring = NULL;
+
+    ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
+}
+
+
+static int SndioCapture_recordProc(void* ptr)
+{
+    SndioCapture *self = (SndioCapture*)ptr;
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+    ALsizei frameSize;
+
+    SetRTPriority();
+    althrd_setname(althrd_current(), RECORD_THREAD_NAME);
+
+    frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
+
+    while(!ATOMIC_LOAD(&self->killNow, almemory_order_acquire) &&
+          ATOMIC_LOAD(&device->Connected, almemory_order_acquire))
+    {
+        ll_ringbuffer_data_t data[2];
+        size_t total, todo;
+
+        ll_ringbuffer_get_write_vector(self->ring, data);
+        todo = data[0].len + data[1].len;
+        if(todo == 0)
+        {
+            static char junk[4096];
+            sio_read(self->sndHandle, junk, minz(sizeof(junk)/frameSize, device->UpdateSize)*frameSize);
+            continue;
+        }
+
+        total = 0;
+        data[0].len *= frameSize;
+        data[1].len *= frameSize;
+        todo = minz(todo, device->UpdateSize) * frameSize;
+        while(total < todo)
+        {
+            size_t got;
+
+            if(!data[0].len)
+                data[0] = data[1];
+
+            got = sio_read(self->sndHandle, data[0].buf, minz(todo-total, data[0].len));
+            if(!got)
+            {
+                SndioCapture_lock(self);
+                aluHandleDisconnect(device, "Failed to read capture samples");
+                SndioCapture_unlock(self);
+                break;
+            }
+
+            data[0].buf += got;
+            data[0].len -= got;
+            total += got;
+        }
+        ll_ringbuffer_write_advance(self->ring, total / frameSize);
+    }
+
+    return 0;
+}
+
+
+static ALCenum SndioCapture_open(SndioCapture *self, const ALCchar *name)
+{
+    ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
+    struct sio_par par;
+
+    if(!name)
+        name = sndio_device;
+    else if(strcmp(name, sndio_device) != 0)
+        return ALC_INVALID_VALUE;
+
+    self->sndHandle = sio_open(NULL, SIO_REC, 0);
+    if(self->sndHandle == NULL)
+    {
+        ERR("Could not open device\n");
+        return ALC_INVALID_VALUE;
+    }
+
+    sio_initpar(&par);
+
+    switch(device->FmtType)
+    {
+        case DevFmtByte:
+            par.bps = 1;
+            par.sig = 1;
+            break;
+        case DevFmtUByte:
+            par.bps = 1;
+            par.sig = 0;
+            break;
+        case DevFmtShort:
+            par.bps = 2;
+            par.sig = 1;
+            break;
+        case DevFmtUShort:
+            par.bps = 2;
+            par.sig = 0;
+            break;
+        case DevFmtInt:
+            par.bps = 4;
+            par.sig = 1;
+            break;
+        case DevFmtUInt:
+            par.bps = 4;
+            par.sig = 0;
+            break;
+        case DevFmtFloat:
+            ERR("%s capture samples not supported\n", DevFmtTypeString(device->FmtType));
+            return ALC_INVALID_VALUE;
+    }
+    par.bits = par.bps * 8;
+    par.le = SIO_LE_NATIVE;
+    par.msb = SIO_LE_NATIVE ? 0 : 1;
+    par.rchan = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
+    par.rate = device->Frequency;
+
+    par.appbufsz = maxu(device->UpdateSize*device->NumUpdates, (device->Frequency+9)/10);
+    par.round = clampu(par.appbufsz/device->NumUpdates, (device->Frequency+99)/100,
+                       (device->Frequency+19)/20);
+
+    device->UpdateSize = par.round;
+    device->NumUpdates = maxu(par.appbufsz/par.round, 1);
+
+    if(!sio_setpar(self->sndHandle, &par) || !sio_getpar(self->sndHandle, &par))
+    {
+        ERR("Failed to set device parameters\n");
+        return ALC_INVALID_VALUE;
+    }
+
+    if(par.bits != par.bps*8)
+    {
+        ERR("Padded samples not supported (%u of %u bits)\n", par.bits, par.bps*8);
+        return ALC_INVALID_VALUE;
+    }
+
+    if(!((device->FmtType == DevFmtByte && par.bits == 8 && par.sig != 0) ||
+         (device->FmtType == DevFmtUByte && par.bits == 8 && par.sig == 0) ||
+         (device->FmtType == DevFmtShort && par.bits == 16 && par.sig != 0) ||
+         (device->FmtType == DevFmtUShort && par.bits == 16 && par.sig == 0) ||
+         (device->FmtType == DevFmtInt && par.bits == 32 && par.sig != 0) ||
+         (device->FmtType == DevFmtUInt && par.bits == 32 && par.sig == 0)) ||
+       ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder) != (ALsizei)par.rchan ||
+       device->Frequency != par.rate)
+    {
+        ERR("Failed to set format %s %s %uhz, got %c%u %u-channel %uhz instead\n",
+            DevFmtTypeString(device->FmtType), DevFmtChannelsString(device->FmtChans),
+            device->Frequency, par.sig?'s':'u', par.bits, par.rchan, par.rate);
+        return ALC_INVALID_VALUE;
+    }
+
+    self->ring = ll_ringbuffer_create(device->UpdateSize*device->NumUpdates, par.bps*par.rchan, 0);
+    if(!self->ring)
+    {
+        ERR("Failed to allocate %u-byte ringbuffer\n",
+            device->UpdateSize*device->NumUpdates*par.bps*par.rchan);
+        return ALC_OUT_OF_MEMORY;
+    }
+
+    SetDefaultChannelOrder(device);
+
+    alstr_copy_cstr(&device->DeviceName, name);
+
+    return ALC_NO_ERROR;
+}
+
+static ALCboolean SndioCapture_start(SndioCapture *self)
+{
+    if(!sio_start(self->sndHandle))
+    {
+        ERR("Error starting playback\n");
+        return ALC_FALSE;
+    }
+
+    ATOMIC_STORE(&self->killNow, AL_FALSE, almemory_order_release);
+    if(althrd_create(&self->thread, SndioCapture_recordProc, self) != althrd_success)
+    {
+        sio_stop(self->sndHandle);
+        return ALC_FALSE;
+    }
+
+    return ALC_TRUE;
+}
+
+static void SndioCapture_stop(SndioCapture *self)
+{
+    int res;
+
+    if(ATOMIC_EXCHANGE(&self->killNow, AL_TRUE, almemory_order_acq_rel))
+        return;
+    althrd_join(self->thread, &res);
+
+    if(!sio_stop(self->sndHandle))
+        ERR("Error stopping device\n");
+}
+
+static ALCenum SndioCapture_captureSamples(SndioCapture *self, void *buffer, ALCuint samples)
+{
+    ll_ringbuffer_read(self->ring, buffer, samples);
+    return ALC_NO_ERROR;
 }
 
+static ALCuint SndioCapture_availableSamples(SndioCapture *self)
+{
+    return ll_ringbuffer_read_space(self->ring);
+}
+
+
+typedef struct SndioBackendFactory {
+    DERIVE_FROM_TYPE(ALCbackendFactory);
+} SndioBackendFactory;
+#define SNDIOBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(SndioBackendFactory, ALCbackendFactory) } }
+
+ALCbackendFactory *SndioBackendFactory_getFactory(void);
 
-static ALCboolean ALCsndioBackendFactory_init(ALCsndioBackendFactory* UNUSED(self))
+static ALCboolean SndioBackendFactory_init(SndioBackendFactory *self);
+static DECLARE_FORWARD(SndioBackendFactory, ALCbackendFactory, void, deinit)
+static ALCboolean SndioBackendFactory_querySupport(SndioBackendFactory *self, ALCbackend_Type type);
+static void SndioBackendFactory_probe(SndioBackendFactory *self, enum DevProbe type, al_string *outnames);
+static ALCbackend* SndioBackendFactory_createBackend(SndioBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
+DEFINE_ALCBACKENDFACTORY_VTABLE(SndioBackendFactory);
+
+ALCbackendFactory *SndioBackendFactory_getFactory(void)
+{
+    static SndioBackendFactory factory = SNDIOBACKENDFACTORY_INITIALIZER;
+    return STATIC_CAST(ALCbackendFactory, &factory);
+}
+
+static ALCboolean SndioBackendFactory_init(SndioBackendFactory* UNUSED(self))
 {
     /* No dynamic loading */
     return ALC_TRUE;
 }
 
-static ALCboolean ALCsndioBackendFactory_querySupport(ALCsndioBackendFactory* UNUSED(self), ALCbackend_Type type)
+static ALCboolean SndioBackendFactory_querySupport(SndioBackendFactory* UNUSED(self), ALCbackend_Type type)
 {
-    if(type == ALCbackend_Playback)
+    if(type == ALCbackend_Playback || type == ALCbackend_Capture)
         return ALC_TRUE;
     return ALC_FALSE;
 }
 
-static void ALCsndioBackendFactory_probe(ALCsndioBackendFactory* UNUSED(self), enum DevProbe type)
+static void SndioBackendFactory_probe(SndioBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
 {
     switch(type)
     {
         case ALL_DEVICE_PROBE:
-            AppendAllDevicesList(sndio_device);
-            break;
         case CAPTURE_DEVICE_PROBE:
+            alstr_append_range(outnames, sndio_device, sndio_device+sizeof(sndio_device));
             break;
     }
 }
 
-static ALCbackend* ALCsndioBackendFactory_createBackend(ALCsndioBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
+static ALCbackend* SndioBackendFactory_createBackend(SndioBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
 {
     if(type == ALCbackend_Playback)
     {
-        ALCsndioBackend *backend;
-        NEW_OBJ(backend, ALCsndioBackend)(device);
+        SndioPlayback *backend;
+        NEW_OBJ(backend, SndioPlayback)(device);
+        if(!backend) return NULL;
+        return STATIC_CAST(ALCbackend, backend);
+    }
+    if(type == ALCbackend_Capture)
+    {
+        SndioCapture *backend;
+        NEW_OBJ(backend, SndioCapture)(device);
         if(!backend) return NULL;
         return STATIC_CAST(ALCbackend, backend);
     }

+ 3 - 3
libs/openal-soft/Alc/backends/solaris.c

@@ -302,7 +302,7 @@ ALCbackendFactory *ALCsolarisBackendFactory_getFactory(void);
 static ALCboolean ALCsolarisBackendFactory_init(ALCsolarisBackendFactory *self);
 static DECLARE_FORWARD(ALCsolarisBackendFactory, ALCbackendFactory, void, deinit)
 static ALCboolean ALCsolarisBackendFactory_querySupport(ALCsolarisBackendFactory *self, ALCbackend_Type type);
-static void ALCsolarisBackendFactory_probe(ALCsolarisBackendFactory *self, enum DevProbe type);
+static void ALCsolarisBackendFactory_probe(ALCsolarisBackendFactory *self, enum DevProbe type, al_string *outnames);
 static ALCbackend* ALCsolarisBackendFactory_createBackend(ALCsolarisBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
 DEFINE_ALCBACKENDFACTORY_VTABLE(ALCsolarisBackendFactory);
 
@@ -327,7 +327,7 @@ static ALCboolean ALCsolarisBackendFactory_querySupport(ALCsolarisBackendFactory
     return ALC_FALSE;
 }
 
-static void ALCsolarisBackendFactory_probe(ALCsolarisBackendFactory* UNUSED(self), enum DevProbe type)
+static void ALCsolarisBackendFactory_probe(ALCsolarisBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
 {
     switch(type)
     {
@@ -337,7 +337,7 @@ static void ALCsolarisBackendFactory_probe(ALCsolarisBackendFactory* UNUSED(self
             struct stat buf;
             if(stat(solaris_driver, &buf) == 0)
 #endif
-                AppendAllDevicesList(solaris_device);
+                alstr_append_range(outnames, solaris_device, solaris_device+sizeof(solaris_device));
         }
         break;
 

+ 10 - 9
libs/openal-soft/Alc/backends/wasapi.c

@@ -1919,11 +1919,6 @@ ALCenum ALCwasapiCapture_captureSamples(ALCwasapiCapture *self, ALCvoid *buffer,
 }
 
 
-static inline void AppendAllDevicesList2(const DevMap *entry)
-{ AppendAllDevicesList(alstr_get_cstr(entry->name)); }
-static inline void AppendCaptureDeviceList2(const DevMap *entry)
-{ AppendCaptureDeviceList(alstr_get_cstr(entry->name)); }
-
 typedef struct ALCwasapiBackendFactory {
     DERIVE_FROM_TYPE(ALCbackendFactory);
 } ALCwasapiBackendFactory;
@@ -1932,7 +1927,7 @@ typedef struct ALCwasapiBackendFactory {
 static ALCboolean ALCwasapiBackendFactory_init(ALCwasapiBackendFactory *self);
 static void ALCwasapiBackendFactory_deinit(ALCwasapiBackendFactory *self);
 static ALCboolean ALCwasapiBackendFactory_querySupport(ALCwasapiBackendFactory *self, ALCbackend_Type type);
-static void ALCwasapiBackendFactory_probe(ALCwasapiBackendFactory *self, enum DevProbe type);
+static void ALCwasapiBackendFactory_probe(ALCwasapiBackendFactory *self, enum DevProbe type, al_string *outnames);
 static ALCbackend* ALCwasapiBackendFactory_createBackend(ALCwasapiBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
 
 DEFINE_ALCBACKENDFACTORY_VTABLE(ALCwasapiBackendFactory);
@@ -1989,7 +1984,7 @@ static ALCboolean ALCwasapiBackendFactory_querySupport(ALCwasapiBackendFactory*
     return ALC_FALSE;
 }
 
-static void ALCwasapiBackendFactory_probe(ALCwasapiBackendFactory* UNUSED(self), enum DevProbe type)
+static void ALCwasapiBackendFactory_probe(ALCwasapiBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
 {
     ThreadRequest req = { NULL, 0 };
 
@@ -2003,13 +1998,19 @@ static void ALCwasapiBackendFactory_probe(ALCwasapiBackendFactory* UNUSED(self),
             hr = WaitForResponse(&req);
         if(SUCCEEDED(hr)) switch(type)
         {
+#define APPEND_OUTNAME(e) do {                                                \
+    if(!alstr_empty((e)->name))                                               \
+        alstr_append_range(outnames, VECTOR_BEGIN((e)->name),                 \
+                           VECTOR_END((e)->name)+1);                          \
+} while(0)
         case ALL_DEVICE_PROBE:
-            VECTOR_FOR_EACH(const DevMap, PlaybackDevices, AppendAllDevicesList2);
+            VECTOR_FOR_EACH(const DevMap, PlaybackDevices, APPEND_OUTNAME);
             break;
 
         case CAPTURE_DEVICE_PROBE:
-            VECTOR_FOR_EACH(const DevMap, CaptureDevices, AppendCaptureDeviceList2);
+            VECTOR_FOR_EACH(const DevMap, CaptureDevices, APPEND_OUTNAME);
             break;
+#undef APPEND_OUTNAME
         }
         CloseHandle(req.FinishedEvt);
         req.FinishedEvt = NULL;

+ 3 - 3
libs/openal-soft/Alc/backends/wave.c

@@ -403,7 +403,7 @@ ALCbackendFactory *ALCwaveBackendFactory_getFactory(void);
 static ALCboolean ALCwaveBackendFactory_init(ALCwaveBackendFactory *self);
 static DECLARE_FORWARD(ALCwaveBackendFactory, ALCbackendFactory, void, deinit)
 static ALCboolean ALCwaveBackendFactory_querySupport(ALCwaveBackendFactory *self, ALCbackend_Type type);
-static void ALCwaveBackendFactory_probe(ALCwaveBackendFactory *self, enum DevProbe type);
+static void ALCwaveBackendFactory_probe(ALCwaveBackendFactory *self, enum DevProbe type, al_string *outnames);
 static ALCbackend* ALCwaveBackendFactory_createBackend(ALCwaveBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
 DEFINE_ALCBACKENDFACTORY_VTABLE(ALCwaveBackendFactory);
 
@@ -427,12 +427,12 @@ static ALCboolean ALCwaveBackendFactory_querySupport(ALCwaveBackendFactory* UNUS
     return ALC_FALSE;
 }
 
-static void ALCwaveBackendFactory_probe(ALCwaveBackendFactory* UNUSED(self), enum DevProbe type)
+static void ALCwaveBackendFactory_probe(ALCwaveBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
 {
     switch(type)
     {
         case ALL_DEVICE_PROBE:
-            AppendAllDevicesList(waveDevice);
+            alstr_append_range(outnames, waveDevice, waveDevice+sizeof(waveDevice));
             break;
         case CAPTURE_DEVICE_PROBE:
             break;

+ 9 - 15
libs/openal-soft/Alc/backends/winmm.c

@@ -700,17 +700,6 @@ static ALCuint ALCwinmmCapture_availableSamples(ALCwinmmCapture *self)
 }
 
 
-static inline void AppendAllDevicesList2(const al_string *name)
-{
-    if(!alstr_empty(*name))
-        AppendAllDevicesList(alstr_get_cstr(*name));
-}
-static inline void AppendCaptureDeviceList2(const al_string *name)
-{
-    if(!alstr_empty(*name))
-        AppendCaptureDeviceList(alstr_get_cstr(*name));
-}
-
 typedef struct ALCwinmmBackendFactory {
     DERIVE_FROM_TYPE(ALCbackendFactory);
 } ALCwinmmBackendFactory;
@@ -719,7 +708,7 @@ typedef struct ALCwinmmBackendFactory {
 static ALCboolean ALCwinmmBackendFactory_init(ALCwinmmBackendFactory *self);
 static void ALCwinmmBackendFactory_deinit(ALCwinmmBackendFactory *self);
 static ALCboolean ALCwinmmBackendFactory_querySupport(ALCwinmmBackendFactory *self, ALCbackend_Type type);
-static void ALCwinmmBackendFactory_probe(ALCwinmmBackendFactory *self, enum DevProbe type);
+static void ALCwinmmBackendFactory_probe(ALCwinmmBackendFactory *self, enum DevProbe type, al_string *outnames);
 static ALCbackend* ALCwinmmBackendFactory_createBackend(ALCwinmmBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
 
 DEFINE_ALCBACKENDFACTORY_VTABLE(ALCwinmmBackendFactory);
@@ -749,19 +738,24 @@ static ALCboolean ALCwinmmBackendFactory_querySupport(ALCwinmmBackendFactory* UN
     return ALC_FALSE;
 }
 
-static void ALCwinmmBackendFactory_probe(ALCwinmmBackendFactory* UNUSED(self), enum DevProbe type)
+static void ALCwinmmBackendFactory_probe(ALCwinmmBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
 {
     switch(type)
     {
+#define APPEND_OUTNAME(n) do {                                                \
+    if(!alstr_empty(*(n)))                                                    \
+        alstr_append_range(outnames, VECTOR_BEGIN(*(n)), VECTOR_END(*(n))+1); \
+} while(0)
         case ALL_DEVICE_PROBE:
             ProbePlaybackDevices();
-            VECTOR_FOR_EACH(const al_string, PlaybackDevices, AppendAllDevicesList2);
+            VECTOR_FOR_EACH(const al_string, PlaybackDevices, APPEND_OUTNAME);
             break;
 
         case CAPTURE_DEVICE_PROBE:
             ProbeCaptureDevices();
-            VECTOR_FOR_EACH(const al_string, CaptureDevices, AppendCaptureDeviceList2);
+            VECTOR_FOR_EACH(const al_string, CaptureDevices, APPEND_OUTNAME);
             break;
+#undef APPEND_OUTNAME
     }
 }
 

+ 6 - 106
libs/openal-soft/Alc/bformatdec.c

@@ -3,6 +3,7 @@
 
 #include "bformatdec.h"
 #include "ambdec.h"
+#include "filters/splitter.h"
 #include "alu.h"
 
 #include "bool.h"
@@ -10,107 +11,6 @@
 #include "almalloc.h"
 
 
-void bandsplit_init(BandSplitter *splitter, ALfloat f0norm)
-{
-    ALfloat w = f0norm * F_TAU;
-    ALfloat cw = cosf(w);
-    if(cw > FLT_EPSILON)
-        splitter->coeff = (sinf(w) - 1.0f) / cw;
-    else
-        splitter->coeff = cw * -0.5f;
-
-    splitter->lp_z1 = 0.0f;
-    splitter->lp_z2 = 0.0f;
-    splitter->hp_z1 = 0.0f;
-}
-
-void bandsplit_clear(BandSplitter *splitter)
-{
-    splitter->lp_z1 = 0.0f;
-    splitter->lp_z2 = 0.0f;
-    splitter->hp_z1 = 0.0f;
-}
-
-void bandsplit_process(BandSplitter *splitter, ALfloat *restrict hpout, ALfloat *restrict lpout,
-                       const ALfloat *input, ALsizei count)
-{
-    ALfloat lp_coeff, hp_coeff, lp_y, hp_y, d;
-    ALfloat lp_z1, lp_z2, hp_z1;
-    ALsizei i;
-
-    hp_coeff = splitter->coeff;
-    lp_coeff = splitter->coeff*0.5f + 0.5f;
-    lp_z1 = splitter->lp_z1;
-    lp_z2 = splitter->lp_z2;
-    hp_z1 = splitter->hp_z1;
-    for(i = 0;i < count;i++)
-    {
-        ALfloat in = input[i];
-
-        /* Low-pass sample processing. */
-        d = (in - lp_z1) * lp_coeff;
-        lp_y = lp_z1 + d;
-        lp_z1 = lp_y + d;
-
-        d = (lp_y - lp_z2) * lp_coeff;
-        lp_y = lp_z2 + d;
-        lp_z2 = lp_y + d;
-
-        lpout[i] = lp_y;
-
-        /* All-pass sample processing. */
-        d = in - hp_coeff*hp_z1;
-        hp_y = hp_z1 + hp_coeff*d;
-        hp_z1 = d;
-
-        /* High-pass generated from removing low-passed output. */
-        hpout[i] = hp_y - lp_y;
-    }
-    splitter->lp_z1 = lp_z1;
-    splitter->lp_z2 = lp_z2;
-    splitter->hp_z1 = hp_z1;
-}
-
-
-void splitterap_init(SplitterAllpass *splitter, ALfloat f0norm)
-{
-    ALfloat w = f0norm * 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;
-}
-
-
 /* NOTE: These are scale factors as applied to Ambisonics content. Decoder
  * coefficients should be divided by these values to get proper N3D scalings.
  */
@@ -538,7 +438,7 @@ void ambiup_reset(struct AmbiUpsampler *ambiup, const ALCdevice *device, ALfloat
         {
             ALfloat coeffs[MAX_AMBI_COEFFS] = { 0.0f };
             CalcDirectionCoeffs(Ambi3DPoints[k], 0.0f, coeffs);
-            ComputeDryPanGains(&device->Dry, coeffs, 1.0f, encgains[k]);
+            ComputePanGains(&device->Dry, coeffs, 1.0f, encgains[k]);
         }
 
         /* Combine the matrices that do the in->virt and virt->out conversions
@@ -550,11 +450,11 @@ void ambiup_reset(struct AmbiUpsampler *ambiup, const ALCdevice *device, ALfloat
         {
             for(j = 0;j < device->Dry.NumChannels;j++)
             {
-                ALfloat gain=0.0f;
+                ALdouble gain = 0.0;
                 for(k = 0;k < COUNTOF(Ambi3DDecoder);k++)
-                    gain += Ambi3DDecoder[k][i] * encgains[k][j];
-                ambiup->Gains[i][j][HF_BAND] = gain * Ambi3DDecoderHFScale[i];
-                ambiup->Gains[i][j][LF_BAND] = gain;
+                    gain += (ALdouble)Ambi3DDecoder[k][i] * encgains[k][j];
+                ambiup->Gains[i][j][HF_BAND] = (ALfloat)(gain * Ambi3DDecoderHFScale[i]);
+                ambiup->Gains[i][j][LF_BAND] = (ALfloat)gain;
             }
         }
     }

+ 0 - 34
libs/openal-soft/Alc/bformatdec.h

@@ -54,38 +54,4 @@ void ambiup_reset(struct AmbiUpsampler *ambiup, const ALCdevice *device, ALfloat
 
 void ambiup_process(struct AmbiUpsampler *ambiup, ALfloat (*restrict OutBuffer)[BUFFERSIZE], ALsizei OutChannels, const ALfloat (*restrict InSamples)[BUFFERSIZE], ALsizei SamplesToDo);
 
-
-/* Band splitter. Splits a signal into two phase-matching frequency bands. */
-typedef struct BandSplitter {
-    ALfloat coeff;
-    ALfloat lp_z1;
-    ALfloat lp_z2;
-    ALfloat hp_z1;
-} BandSplitter;
-
-void bandsplit_init(BandSplitter *splitter, ALfloat f0norm);
-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 f0norm);
-void splitterap_clear(SplitterAllpass *splitter);
-void splitterap_process(SplitterAllpass *splitter, ALfloat *restrict samples, ALsizei count);
-
-
-typedef struct FrontStablizer {
-    SplitterAllpass APFilter[MAX_OUTPUT_CHANNELS];
-    BandSplitter LFilter, RFilter;
-    alignas(16) ALfloat LSplit[2][BUFFERSIZE];
-    alignas(16) ALfloat RSplit[2][BUFFERSIZE];
-} FrontStablizer;
-
 #endif /* BFORMATDEC_H */

+ 0 - 8
libs/openal-soft/Alc/compat.h

@@ -50,14 +50,6 @@ void CloseLib(void *handle);
 void *GetSymbol(void *handle, const char *name);
 #endif
 
-#ifdef __ANDROID__
-#define JCALL(obj, func)  ((*(obj))->func((obj), EXTRACT_VCALL_ARGS
-#define JCALL0(obj, func)  ((*(obj))->func((obj) EXTRACT_VCALL_ARGS
-
-/** Returns a JNIEnv*. */
-void *Android_GetJNIEnv(void);
-#endif
-
 #ifdef __cplusplus
 } /* extern "C" */
 #endif

+ 2 - 1
libs/openal-soft/Alc/converter.c

@@ -27,7 +27,8 @@ SampleConverter *CreateSampleConverter(enum DevFmtType srcType, enum DevFmtType
 
     /* Have to set the mixer FPU mode since that's what the resampler code expects. */
     START_MIXER_MODE();
-    step = fastf2i((ALfloat)mind((ALdouble)srcRate / dstRate, MAX_PITCH)*FRACTIONONE + 0.5f);
+    step = (ALsizei)mind(((ALdouble)srcRate/dstRate*FRACTIONONE) + 0.5,
+                         MAX_PITCH * FRACTIONONE);
     converter->mIncrement = maxi(step, 1);
     if(converter->mIncrement == FRACTIONONE)
         converter->mResample = Resample_copy_C;

+ 321 - 0
libs/openal-soft/Alc/effects/autowah.c

@@ -0,0 +1,321 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 2018 by Raul Herraiz.
+ * This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the
+ *  Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#include "config.h"
+
+#include <math.h>
+#include <stdlib.h>
+
+#include "alMain.h"
+#include "alAuxEffectSlot.h"
+#include "alError.h"
+#include "alu.h"
+#include "filters/defs.h"
+
+#define MIN_FREQ 20.0f
+#define MAX_FREQ 2500.0f
+#define Q_FACTOR 5.0f
+
+typedef struct ALautowahState {
+    DERIVE_FROM_TYPE(ALeffectState);
+
+    /* Effect parameters */
+    ALfloat AttackRate;
+    ALfloat ReleaseRate;
+    ALfloat ResonanceGain;
+    ALfloat PeakGain;
+    ALfloat FreqMinNorm;
+    ALfloat BandwidthNorm;
+    ALfloat env_delay;
+
+    /* Filter components derived from the envelope. */
+    struct {
+        ALfloat cos_w0;
+        ALfloat alpha;
+    } Env[BUFFERSIZE];
+
+    struct {
+        /* Effect filters' history. */
+        struct {
+            ALfloat z1, z2;
+        } Filter;
+
+        /* Effect gains for each output channel */
+        ALfloat CurrentGains[MAX_OUTPUT_CHANNELS];
+        ALfloat TargetGains[MAX_OUTPUT_CHANNELS];
+    } Chans[MAX_EFFECT_CHANNELS];
+
+    /* Effects buffers */
+    alignas(16) ALfloat BufferOut[BUFFERSIZE];
+} ALautowahState;
+
+static ALvoid ALautowahState_Destruct(ALautowahState *state);
+static ALboolean ALautowahState_deviceUpdate(ALautowahState *state, ALCdevice *device);
+static ALvoid ALautowahState_update(ALautowahState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props);
+static ALvoid ALautowahState_process(ALautowahState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
+DECLARE_DEFAULT_ALLOCATORS(ALautowahState)
+
+DEFINE_ALEFFECTSTATE_VTABLE(ALautowahState);
+
+static void ALautowahState_Construct(ALautowahState *state)
+{
+    ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
+    SET_VTABLE2(ALautowahState, ALeffectState, state);
+}
+
+static ALvoid ALautowahState_Destruct(ALautowahState *state)
+{
+    ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
+}
+
+static ALboolean ALautowahState_deviceUpdate(ALautowahState *state, ALCdevice *UNUSED(device))
+{
+    /* (Re-)initializing parameters and clear the buffers. */
+    ALsizei i, j;
+
+    state->AttackRate    = 1.0f;
+    state->ReleaseRate   = 1.0f;
+    state->ResonanceGain = 10.0f;
+    state->PeakGain      = 4.5f;
+    state->FreqMinNorm   = 4.5e-4f;
+    state->BandwidthNorm = 0.05f;
+    state->env_delay     = 0.0f;
+
+    memset(state->Env, 0, sizeof(state->Env));
+
+    for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
+    {
+        for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
+            state->Chans[i].CurrentGains[j] = 0.0f;
+        state->Chans[i].Filter.z1 = 0.0f;
+        state->Chans[i].Filter.z2 = 0.0f;
+    }
+
+    return AL_TRUE;
+}
+
+static ALvoid ALautowahState_update(ALautowahState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props)
+{
+    const ALCdevice *device = context->Device;
+    ALfloat ReleaseTime;
+    ALsizei i;
+
+    ReleaseTime = clampf(props->Autowah.ReleaseTime, 0.001f, 1.0f);
+
+    state->AttackRate    = expf(-1.0f / (props->Autowah.AttackTime*device->Frequency));
+    state->ReleaseRate   = expf(-1.0f / (ReleaseTime*device->Frequency));
+    /* 0-20dB Resonance Peak gain */
+    state->ResonanceGain = sqrtf(log10f(props->Autowah.Resonance)*10.0f / 3.0f);
+    state->PeakGain      = 1.0f - log10f(props->Autowah.PeakGain/AL_AUTOWAH_MAX_PEAK_GAIN);
+    state->FreqMinNorm   = MIN_FREQ / device->Frequency;
+    state->BandwidthNorm = (MAX_FREQ-MIN_FREQ) / device->Frequency;
+
+    STATIC_CAST(ALeffectState,state)->OutBuffer = device->FOAOut.Buffer;
+    STATIC_CAST(ALeffectState,state)->OutChannels = device->FOAOut.NumChannels;
+    for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
+        ComputePanGains(&device->FOAOut, IdentityMatrixf.m[i], slot->Params.Gain,
+                        state->Chans[i].TargetGains);
+}
+
+static ALvoid ALautowahState_process(ALautowahState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
+{
+    const ALfloat attack_rate = state->AttackRate;
+    const ALfloat release_rate = state->ReleaseRate;
+    const ALfloat res_gain = state->ResonanceGain;
+    const ALfloat peak_gain = state->PeakGain;
+    const ALfloat freq_min = state->FreqMinNorm;
+    const ALfloat bandwidth = state->BandwidthNorm;
+    ALfloat env_delay;
+    ALsizei c, i;
+
+    env_delay = state->env_delay;
+    for(i = 0;i < SamplesToDo;i++)
+    {
+        ALfloat w0, sample, a;
+
+        /* Envelope follower described on the book: Audio Effects, Theory,
+         * Implementation and Application.
+         */
+        sample = peak_gain * fabsf(SamplesIn[0][i]);
+        a = (sample > env_delay) ? attack_rate : release_rate;
+        env_delay = lerp(sample, env_delay, a);
+
+        /* Calculate the cos and alpha components for this sample's filter. */
+        w0 = minf((bandwidth*env_delay + freq_min), 0.46f) * F_TAU;
+        state->Env[i].cos_w0 = cosf(w0);
+        state->Env[i].alpha = sinf(w0)/(2.0f * Q_FACTOR);
+    }
+    state->env_delay = env_delay;
+
+    for(c = 0;c < MAX_EFFECT_CHANNELS; c++)
+    {
+        /* This effectively inlines BiquadFilter_setParams for a peaking
+         * filter and BiquadFilter_processC. The alpha and cosine components
+         * for the filter coefficients were previously calculated with the
+         * envelope. Because the filter changes for each sample, the
+         * coefficients are transient and don't need to be held.
+         */
+        ALfloat z1 = state->Chans[c].Filter.z1;
+        ALfloat z2 = state->Chans[c].Filter.z2;
+
+        for(i = 0;i < SamplesToDo;i++)
+        {
+            const ALfloat alpha = state->Env[i].alpha;
+            const ALfloat cos_w0 = state->Env[i].cos_w0;
+            ALfloat input, output;
+            ALfloat a[3], b[3];
+
+            b[0] =  1.0f + alpha*res_gain;
+            b[1] = -2.0f * cos_w0;
+            b[2] =  1.0f - alpha*res_gain;
+            a[0] =  1.0f + alpha/res_gain;
+            a[1] = -2.0f * cos_w0;
+            a[2] =  1.0f - alpha/res_gain;
+
+            input = SamplesIn[c][i];
+            output = input*(b[0]/a[0]) + z1;
+            z1 = input*(b[1]/a[0]) - output*(a[1]/a[0]) + z2;
+            z2 = input*(b[2]/a[0]) - output*(a[2]/a[0]);
+            state->BufferOut[i] = output;
+        }
+        state->Chans[c].Filter.z1 = z1;
+        state->Chans[c].Filter.z2 = z2;
+
+        /* Now, mix the processed sound data to the output. */
+        MixSamples(state->BufferOut, NumChannels, SamplesOut, state->Chans[c].CurrentGains,
+                   state->Chans[c].TargetGains, SamplesToDo, 0, SamplesToDo);
+    }
+}
+
+typedef struct AutowahStateFactory {
+    DERIVE_FROM_TYPE(EffectStateFactory);
+} AutowahStateFactory;
+
+static ALeffectState *AutowahStateFactory_create(AutowahStateFactory *UNUSED(factory))
+{
+    ALautowahState *state;
+
+    NEW_OBJ0(state, ALautowahState)();
+    if(!state) return NULL;
+
+    return STATIC_CAST(ALeffectState, state);
+}
+
+DEFINE_EFFECTSTATEFACTORY_VTABLE(AutowahStateFactory);
+
+EffectStateFactory *AutowahStateFactory_getFactory(void)
+{
+    static AutowahStateFactory AutowahFactory = { { GET_VTABLE2(AutowahStateFactory, EffectStateFactory) } };
+
+    return STATIC_CAST(EffectStateFactory, &AutowahFactory);
+}
+
+void ALautowah_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
+{
+    ALeffectProps *props = &effect->Props;
+    switch(param)
+    {
+        case AL_AUTOWAH_ATTACK_TIME:
+            if(!(val >= AL_AUTOWAH_MIN_ATTACK_TIME && val <= AL_AUTOWAH_MAX_ATTACK_TIME))
+                SETERR_RETURN(context, AL_INVALID_VALUE,,"Autowah attack time out of range");
+            props->Autowah.AttackTime = val;
+            break;
+
+        case AL_AUTOWAH_RELEASE_TIME:
+            if(!(val >= AL_AUTOWAH_MIN_RELEASE_TIME && val <= AL_AUTOWAH_MAX_RELEASE_TIME))
+                SETERR_RETURN(context, AL_INVALID_VALUE,,"Autowah release time out of range");
+            props->Autowah.ReleaseTime = val;
+            break;
+
+        case AL_AUTOWAH_RESONANCE:
+            if(!(val >= AL_AUTOWAH_MIN_RESONANCE && val <= AL_AUTOWAH_MAX_RESONANCE))
+                SETERR_RETURN(context, AL_INVALID_VALUE,,"Autowah resonance out of range");
+            props->Autowah.Resonance = val;
+            break;
+
+        case AL_AUTOWAH_PEAK_GAIN:
+            if(!(val >= AL_AUTOWAH_MIN_PEAK_GAIN && val <= AL_AUTOWAH_MAX_PEAK_GAIN))
+                SETERR_RETURN(context, AL_INVALID_VALUE,,"Autowah peak gain out of range");
+            props->Autowah.PeakGain = val;
+            break;
+
+        default:
+            alSetError(context, AL_INVALID_ENUM, "Invalid autowah float property 0x%04x", param);
+    }
+}
+
+void ALautowah_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
+{
+    ALautowah_setParamf(effect, context, param, vals[0]);
+}
+
+void ALautowah_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint UNUSED(val))
+{
+    alSetError(context, AL_INVALID_ENUM, "Invalid autowah integer property 0x%04x", param);
+}
+
+void ALautowah_setParamiv(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, const ALint *UNUSED(vals))
+{
+    alSetError(context, AL_INVALID_ENUM, "Invalid autowah integer vector property 0x%04x", param);
+}
+
+void ALautowah_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(val))
+{
+    alSetError(context, AL_INVALID_ENUM, "Invalid autowah integer property 0x%04x", param);
+}
+void ALautowah_getParamiv(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(vals))
+{
+    alSetError(context, AL_INVALID_ENUM, "Invalid autowah integer vector property 0x%04x", param);
+}
+
+void ALautowah_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
+{
+
+    const ALeffectProps *props = &effect->Props;
+    switch(param)
+    {
+        case AL_AUTOWAH_ATTACK_TIME:
+            *val = props->Autowah.AttackTime;
+            break;
+
+        case AL_AUTOWAH_RELEASE_TIME:
+            *val = props->Autowah.ReleaseTime;
+            break;
+
+        case AL_AUTOWAH_RESONANCE:
+            *val = props->Autowah.Resonance;
+            break;
+
+        case AL_AUTOWAH_PEAK_GAIN:
+            *val = props->Autowah.PeakGain;
+            break;
+
+        default:
+            alSetError(context, AL_INVALID_ENUM, "Invalid autowah float property 0x%04x", param);
+    }
+
+}
+
+void ALautowah_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
+{
+    ALautowah_getParamf(effect, context, param, vals);
+}
+
+DEFINE_ALEFFECT_VTABLE(ALautowah);

+ 7 - 7
libs/openal-soft/Alc/effects/chorus.c

@@ -98,7 +98,7 @@ static ALboolean ALchorusState_deviceUpdate(ALchorusState *state, ALCdevice *Dev
     const ALfloat max_delay = maxf(AL_CHORUS_MAX_DELAY, AL_FLANGER_MAX_DELAY);
     ALsizei maxlen;
 
-    maxlen = NextPowerOf2(fastf2i(max_delay*2.0f*Device->Frequency) + 1);
+    maxlen = NextPowerOf2(float2int(max_delay*2.0f*Device->Frequency) + 1u);
     if(maxlen <= 0) return AL_FALSE;
 
     if(maxlen != state->BufferLength)
@@ -140,7 +140,7 @@ static ALvoid ALchorusState_update(ALchorusState *state, const ALCcontext *Conte
     /* The LFO depth is scaled to be relative to the sample delay. Clamp the
      * delay and depth to allow enough padding for resampling.
      */
-    state->delay = maxi(fastf2i(props->Chorus.Delay*frequency*FRACTIONONE + 0.5f),
+    state->delay = maxi(float2int(props->Chorus.Delay*frequency*FRACTIONONE + 0.5f),
                         mindelay);
     state->depth = minf(props->Chorus.Depth * state->delay,
                         (ALfloat)(state->delay - mindelay));
@@ -149,9 +149,9 @@ static ALvoid ALchorusState_update(ALchorusState *state, const ALCcontext *Conte
 
     /* Gains for left and right sides */
     CalcAngleCoeffs(-F_PI_2, 0.0f, 0.0f, coeffs);
-    ComputeDryPanGains(&device->Dry, coeffs, Slot->Params.Gain, state->Gains[0].Target);
+    ComputePanGains(&device->Dry, coeffs, Slot->Params.Gain, state->Gains[0].Target);
     CalcAngleCoeffs( F_PI_2, 0.0f, 0.0f, coeffs);
-    ComputeDryPanGains(&device->Dry, coeffs, Slot->Params.Gain, state->Gains[1].Target);
+    ComputePanGains(&device->Dry, coeffs, Slot->Params.Gain, state->Gains[1].Target);
 
     phase = props->Chorus.Phase;
     rate = props->Chorus.Rate;
@@ -167,10 +167,10 @@ static ALvoid ALchorusState_update(ALchorusState *state, const ALCcontext *Conte
         /* Calculate LFO coefficient (number of samples per cycle). Limit the
          * max range to avoid overflow when calculating the displacement.
          */
-        ALsizei lfo_range = mini(fastf2i(frequency/rate + 0.5f), INT_MAX/360 - 180);
+        ALsizei lfo_range = float2int(minf(frequency/rate + 0.5f, (ALfloat)(INT_MAX/360 - 180)));
 
-        state->lfo_offset = fastf2i((ALfloat)state->lfo_offset/state->lfo_range*
-                                    lfo_range + 0.5f) % lfo_range;
+        state->lfo_offset = float2int((ALfloat)state->lfo_offset/state->lfo_range*
+                                      lfo_range + 0.5f) % lfo_range;
         state->lfo_range = lfo_range;
         switch(state->waveform)
         {

+ 59 - 66
libs/openal-soft/Alc/effects/compressor.c

@@ -27,6 +27,13 @@
 #include "alu.h"
 
 
+#define AMP_ENVELOPE_MIN  0.5f
+#define AMP_ENVELOPE_MAX  2.0f
+
+#define ATTACK_TIME  0.1f /* 100ms to rise from min to max */
+#define RELEASE_TIME 0.2f /* 200ms to drop from max to min */
+
+
 typedef struct ALcompressorState {
     DERIVE_FROM_TYPE(ALeffectState);
 
@@ -35,9 +42,9 @@ typedef struct ALcompressorState {
 
     /* Effect parameters */
     ALboolean Enabled;
-    ALfloat AttackRate;
-    ALfloat ReleaseRate;
-    ALfloat GainCtrl;
+    ALfloat AttackMult;
+    ALfloat ReleaseMult;
+    ALfloat EnvFollower;
 } ALcompressorState;
 
 static ALvoid ALcompressorState_Destruct(ALcompressorState *state);
@@ -55,9 +62,9 @@ static void ALcompressorState_Construct(ALcompressorState *state)
     SET_VTABLE2(ALcompressorState, ALeffectState, state);
 
     state->Enabled = AL_TRUE;
-    state->AttackRate = 0.0f;
-    state->ReleaseRate = 0.0f;
-    state->GainCtrl = 1.0f;
+    state->AttackMult = 1.0f;
+    state->ReleaseMult = 1.0f;
+    state->EnvFollower = 1.0f;
 }
 
 static ALvoid ALcompressorState_Destruct(ALcompressorState *state)
@@ -67,11 +74,17 @@ static ALvoid ALcompressorState_Destruct(ALcompressorState *state)
 
 static ALboolean ALcompressorState_deviceUpdate(ALcompressorState *state, ALCdevice *device)
 {
-    const ALfloat attackTime = device->Frequency * 0.2f; /* 200ms Attack */
-    const ALfloat releaseTime = device->Frequency * 0.4f; /* 400ms Release */
-
-    state->AttackRate = 1.0f / attackTime;
-    state->ReleaseRate = 1.0f / releaseTime;
+    /* Number of samples to do a full attack and release (non-integer sample
+     * counts are okay).
+     */
+    const ALfloat attackCount  = (ALfloat)device->Frequency * ATTACK_TIME;
+    const ALfloat releaseCount = (ALfloat)device->Frequency * RELEASE_TIME;
+
+    /* Calculate per-sample multipliers to attack and release at the desired
+     * rates.
+     */
+    state->AttackMult  = powf(AMP_ENVELOPE_MAX/AMP_ENVELOPE_MIN, 1.0f/attackCount);
+    state->ReleaseMult = powf(AMP_ENVELOPE_MIN/AMP_ENVELOPE_MAX, 1.0f/releaseCount);
 
     return AL_TRUE;
 }
@@ -86,8 +99,7 @@ static ALvoid ALcompressorState_update(ALcompressorState *state, const ALCcontex
     STATIC_CAST(ALeffectState,state)->OutBuffer = device->FOAOut.Buffer;
     STATIC_CAST(ALeffectState,state)->OutChannels = device->FOAOut.NumChannels;
     for(i = 0;i < 4;i++)
-        ComputeFirstOrderGains(&device->FOAOut, IdentityMatrixf.m[i],
-                               slot->Params.Gain, state->Gain[i]);
+        ComputePanGains(&device->FOAOut, IdentityMatrixf.m[i], slot->Params.Gain, state->Gain[i]);
 }
 
 static ALvoid ALcompressorState_process(ALcompressorState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
@@ -97,71 +109,52 @@ static ALvoid ALcompressorState_process(ALcompressorState *state, ALsizei Sample
 
     for(base = 0;base < SamplesToDo;)
     {
-        ALfloat temps[64][4];
-        ALsizei td = mini(64, SamplesToDo-base);
-
-        /* Load samples into the temp buffer first. */
-        for(j = 0;j < 4;j++)
-        {
-            for(i = 0;i < td;i++)
-                temps[i][j] = SamplesIn[j][i+base];
-        }
+        ALfloat gains[256];
+        ALsizei td = mini(256, SamplesToDo-base);
+        ALfloat env = state->EnvFollower;
 
+        /* Generate the per-sample gains from the signal envelope. */
         if(state->Enabled)
         {
-            ALfloat gain = state->GainCtrl;
-            ALfloat output, amplitude;
-
-            for(i = 0;i < td;i++)
+            for(i = 0;i < td;++i)
             {
-                /* Roughly calculate the maximum amplitude from the 4-channel
-                 * signal, and attack or release the gain control to reach it.
+                /* Clamp the absolute amplitude to the defined envelope limits,
+                 * then attack or release the envelope to reach it.
+                 */
+                ALfloat amplitude = clampf(fabsf(SamplesIn[0][base+i]),
+                                           AMP_ENVELOPE_MIN, AMP_ENVELOPE_MAX);
+                if(amplitude > env)
+                    env = minf(env*state->AttackMult, amplitude);
+                else if(amplitude < env)
+                    env = maxf(env*state->ReleaseMult, amplitude);
+
+                /* Apply the reciprocal of the envelope to normalize the volume
+                 * (compress the dynamic range).
                  */
-                amplitude = fabsf(temps[i][0]);
-                amplitude = maxf(amplitude + fabsf(temps[i][1]),
-                                 maxf(amplitude + fabsf(temps[i][2]),
-                                      amplitude + fabsf(temps[i][3])));
-                if(amplitude > gain)
-                    gain = minf(gain+state->AttackRate, amplitude);
-                else if(amplitude < gain)
-                    gain = maxf(gain-state->ReleaseRate, amplitude);
-
-                /* Apply the inverse of the gain control to normalize/compress
-                 * the volume. */
-                output = 1.0f / clampf(gain, 0.5f, 2.0f);
-                for(j = 0;j < 4;j++)
-                    temps[i][j] *= output;
+                gains[i] = 1.0f / env;
             }
-
-            state->GainCtrl = gain;
         }
         else
         {
-            ALfloat gain = state->GainCtrl;
-            ALfloat output, amplitude;
-
-            for(i = 0;i < td;i++)
+            /* Same as above, except the amplitude is forced to 1. This helps
+             * ensure smooth gain changes when the compressor is turned on and
+             * off.
+             */
+            for(i = 0;i < td;++i)
             {
-                /* Same as above, except the amplitude is forced to 1. This
-                 * helps ensure smooth gain changes when the compressor is
-                 * turned on and off.
-                 */
-                amplitude = 1.0f;
-                if(amplitude > gain)
-                    gain = minf(gain+state->AttackRate, amplitude);
-                else if(amplitude < gain)
-                    gain = maxf(gain-state->ReleaseRate, amplitude);
-
-                output = 1.0f / clampf(gain, 0.5f, 2.0f);
-                for(j = 0;j < 4;j++)
-                    temps[i][j] *= output;
-            }
+                ALfloat amplitude = 1.0f;
+                if(amplitude > env)
+                    env = minf(env*state->AttackMult, amplitude);
+                else if(amplitude < env)
+                    env = maxf(env*state->ReleaseMult, amplitude);
 
-            state->GainCtrl = gain;
+                gains[i] = 1.0f / env;
+            }
         }
+        state->EnvFollower = env;
 
-        /* Now mix to the output. */
-        for(j = 0;j < 4;j++)
+        /* Now compress the signal amplitude to output. */
+        for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
         {
             for(k = 0;k < NumChannels;k++)
             {
@@ -170,7 +163,7 @@ static ALvoid ALcompressorState_process(ALcompressorState *state, ALsizei Sample
                     continue;
 
                 for(i = 0;i < td;i++)
-                    SamplesOut[k][base+i] += gain * temps[i][j];
+                    SamplesOut[k][base+i] += SamplesIn[j][base+i] * gains[i] * gain;
             }
         }
 

+ 1 - 1
libs/openal-soft/Alc/effects/dedicated.c

@@ -102,7 +102,7 @@ static ALvoid ALdedicatedState_update(ALdedicatedState *state, const ALCcontext
 
             STATIC_CAST(ALeffectState,state)->OutBuffer = device->Dry.Buffer;
             STATIC_CAST(ALeffectState,state)->OutChannels = device->Dry.NumChannels;
-            ComputeDryPanGains(&device->Dry, coeffs, Gain, state->TargetGains);
+            ComputePanGains(&device->Dry, coeffs, Gain, state->TargetGains);
         }
     }
 }

+ 10 - 12
libs/openal-soft/Alc/effects/distortion.c

@@ -37,8 +37,8 @@ typedef struct ALdistortionState {
     ALfloat Gain[MAX_OUTPUT_CHANNELS];
 
     /* Effect parameters */
-    BiquadState lowpass;
-    BiquadState bandpass;
+    BiquadFilter lowpass;
+    BiquadFilter bandpass;
     ALfloat attenuation;
     ALfloat edge_coeff;
 
@@ -58,9 +58,6 @@ static void ALdistortionState_Construct(ALdistortionState *state)
 {
     ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
     SET_VTABLE2(ALdistortionState, ALeffectState, state);
-
-    BiquadState_clear(&state->lowpass);
-    BiquadState_clear(&state->bandpass);
 }
 
 static ALvoid ALdistortionState_Destruct(ALdistortionState *state)
@@ -68,8 +65,10 @@ static ALvoid ALdistortionState_Destruct(ALdistortionState *state)
     ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
 }
 
-static ALboolean ALdistortionState_deviceUpdate(ALdistortionState *UNUSED(state), ALCdevice *UNUSED(device))
+static ALboolean ALdistortionState_deviceUpdate(ALdistortionState *state, ALCdevice *UNUSED(device))
 {
+    BiquadFilter_clear(&state->lowpass);
+    BiquadFilter_clear(&state->bandpass);
     return AL_TRUE;
 }
 
@@ -93,20 +92,19 @@ static ALvoid ALdistortionState_update(ALdistortionState *state, const ALCcontex
     /* Multiply sampling frequency by the amount of oversampling done during
      * processing.
      */
-    BiquadState_setParams(&state->lowpass, BiquadType_LowPass, 1.0f,
+    BiquadFilter_setParams(&state->lowpass, BiquadType_LowPass, 1.0f,
         cutoff / (frequency*4.0f), calc_rcpQ_from_bandwidth(cutoff / (frequency*4.0f), bandwidth)
     );
 
     cutoff = props->Distortion.EQCenter;
     /* Convert bandwidth in Hz to octaves. */
     bandwidth = props->Distortion.EQBandwidth / (cutoff * 0.67f);
-    BiquadState_setParams(&state->bandpass, BiquadType_BandPass, 1.0f,
+    BiquadFilter_setParams(&state->bandpass, BiquadType_BandPass, 1.0f,
         cutoff / (frequency*4.0f), calc_rcpQ_from_bandwidth(cutoff / (frequency*4.0f), bandwidth)
     );
 
     CalcAngleCoeffs(0.0f, 0.0f, 0.0f, coeffs);
-    ComputeDryPanGains(&device->Dry, coeffs, slot->Params.Gain * props->Distortion.Gain,
-                       state->Gain);
+    ComputePanGains(&device->Dry, coeffs, slot->Params.Gain*props->Distortion.Gain, state->Gain);
 }
 
 static ALvoid ALdistortionState_process(ALdistortionState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
@@ -136,7 +134,7 @@ static ALvoid ALdistortionState_process(ALdistortionState *state, ALsizei Sample
          * (which is fortunately first step of distortion). So combine three
          * operations into the one.
          */
-        BiquadState_process(&state->lowpass, buffer[1], buffer[0], todo);
+        BiquadFilter_process(&state->lowpass, buffer[1], buffer[0], todo);
 
         /* Second step, do distortion using waveshaper function to emulate
          * signal processing during tube overdriving. Three steps of
@@ -155,7 +153,7 @@ static ALvoid ALdistortionState_process(ALdistortionState *state, ALsizei Sample
         }
 
         /* Third step, do bandpass filtering of distorted signal. */
-        BiquadState_process(&state->bandpass, buffer[1], buffer[0], todo);
+        BiquadFilter_process(&state->bandpass, buffer[1], buffer[0], todo);
 
         todo >>= 2;
         for(k = 0;k < NumChannels;k++)

+ 19 - 25
libs/openal-soft/Alc/effects/echo.c

@@ -52,7 +52,7 @@ typedef struct ALechoState {
 
     ALfloat FeedGain;
 
-    BiquadState Filter;
+    BiquadFilter Filter;
 } ALechoState;
 
 static ALvoid ALechoState_Destruct(ALechoState *state);
@@ -76,7 +76,7 @@ static void ALechoState_Construct(ALechoState *state)
     state->Tap[1].delay = 0;
     state->Offset = 0;
 
-    BiquadState_clear(&state->Filter);
+    BiquadFilter_clear(&state->Filter);
 }
 
 static ALvoid ALechoState_Destruct(ALechoState *state)
@@ -92,8 +92,8 @@ static ALboolean ALechoState_deviceUpdate(ALechoState *state, ALCdevice *Device)
 
     // Use the next power of 2 for the buffer length, so the tap offsets can be
     // wrapped using a mask instead of a modulo
-    maxlen = fastf2i(AL_ECHO_MAX_DELAY*Device->Frequency + 0.5f) +
-             fastf2i(AL_ECHO_MAX_LRDELAY*Device->Frequency + 0.5f);
+    maxlen = float2int(AL_ECHO_MAX_DELAY*Device->Frequency + 0.5f) +
+             float2int(AL_ECHO_MAX_LRDELAY*Device->Frequency + 0.5f);
     maxlen = NextPowerOf2(maxlen);
     if(maxlen <= 0) return AL_FALSE;
 
@@ -120,8 +120,8 @@ static ALvoid ALechoState_update(ALechoState *state, const ALCcontext *context,
     ALfloat coeffs[MAX_AMBI_COEFFS];
     ALfloat gainhf, lrpan, spread;
 
-    state->Tap[0].delay = maxi(fastf2i(props->Echo.Delay*frequency + 0.5f), 1);
-    state->Tap[1].delay = fastf2i(props->Echo.LRDelay*frequency + 0.5f);
+    state->Tap[0].delay = maxi(float2int(props->Echo.Delay*frequency + 0.5f), 1);
+    state->Tap[1].delay = float2int(props->Echo.LRDelay*frequency + 0.5f);
     state->Tap[1].delay += state->Tap[0].delay;
 
     spread = props->Echo.Spread;
@@ -135,17 +135,17 @@ static ALvoid ALechoState_update(ALechoState *state, const ALCcontext *context,
     state->FeedGain = props->Echo.Feedback;
 
     gainhf = maxf(1.0f - props->Echo.Damping, 0.0625f); /* Limit -24dB */
-    BiquadState_setParams(&state->Filter, BiquadType_HighShelf,
-                          gainhf, LOWPASSFREQREF/frequency,
-                          calc_rcpQ_from_slope(gainhf, 1.0f));
+    BiquadFilter_setParams(&state->Filter, BiquadType_HighShelf,
+        gainhf, LOWPASSFREQREF/frequency, calc_rcpQ_from_slope(gainhf, 1.0f)
+    );
 
     /* First tap panning */
     CalcAngleCoeffs(-F_PI_2*lrpan, 0.0f, spread, coeffs);
-    ComputeDryPanGains(&device->Dry, coeffs, slot->Params.Gain, state->Gains[0].Target);
+    ComputePanGains(&device->Dry, coeffs, slot->Params.Gain, state->Gains[0].Target);
 
     /* Second tap panning */
     CalcAngleCoeffs( F_PI_2*lrpan, 0.0f, spread, coeffs);
-    ComputeDryPanGains(&device->Dry, coeffs, slot->Params.Gain, state->Gains[1].Target);
+    ComputePanGains(&device->Dry, coeffs, slot->Params.Gain, state->Gains[1].Target);
 }
 
 static ALvoid ALechoState_process(ALechoState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
@@ -155,14 +155,12 @@ static ALvoid ALechoState_process(ALechoState *state, ALsizei SamplesToDo, const
     const ALsizei tap2 = state->Tap[1].delay;
     ALfloat *restrict delaybuf = state->SampleBuffer;
     ALsizei offset = state->Offset;
-    ALfloat x[2], y[2], in, out;
+    ALfloat z1, z2, in, out;
     ALsizei base;
     ALsizei c, i;
 
-    x[0] = state->Filter.x[0];
-    x[1] = state->Filter.x[1];
-    y[0] = state->Filter.y[0];
-    y[1] = state->Filter.y[1];
+    z1 = state->Filter.z1;
+    z2 = state->Filter.z2;
     for(base = 0;base < SamplesToDo;)
     {
         alignas(16) ALfloat temps[2][128];
@@ -182,11 +180,9 @@ static ALvoid ALechoState_process(ALechoState *state, ALsizei SamplesToDo, const
              * feedback attenuation.
              */
             in = temps[1][i];
-            out = in*state->Filter.b0 +
-                  x[0]*state->Filter.b1 + x[1]*state->Filter.b2 -
-                  y[0]*state->Filter.a1 - y[1]*state->Filter.a2;
-            x[1] = x[0]; x[0] = in;
-            y[1] = y[0]; y[0] = out;
+            out = in*state->Filter.b0 + z1;
+            z1 = in*state->Filter.b1 - out*state->Filter.a1 + z2;
+            z2 = in*state->Filter.b2 - out*state->Filter.a2;
 
             delaybuf[offset&mask] += out * state->FeedGain;
             offset++;
@@ -198,10 +194,8 @@ static ALvoid ALechoState_process(ALechoState *state, ALsizei SamplesToDo, const
 
         base += td;
     }
-    state->Filter.x[0] = x[0];
-    state->Filter.x[1] = x[1];
-    state->Filter.y[0] = y[0];
-    state->Filter.y[1] = y[1];
+    state->Filter.z1 = z1;
+    state->Filter.z2 = z2;
 
     state->Offset = offset;
 }

+ 22 - 22
libs/openal-soft/Alc/effects/equalizer.c

@@ -76,12 +76,12 @@ typedef struct ALequalizerState {
     DERIVE_FROM_TYPE(ALeffectState);
 
     struct {
+        /* Effect parameters */
+        BiquadFilter filter[4];
+
         /* Effect gains for each channel */
         ALfloat CurrentGains[MAX_OUTPUT_CHANNELS];
         ALfloat TargetGains[MAX_OUTPUT_CHANNELS];
-
-        /* Effect parameters */
-        BiquadState filter[4];
     } Chans[MAX_EFFECT_CHANNELS];
 
     ALfloat SampleBuffer[MAX_EFFECT_CHANNELS][BUFFERSIZE];
@@ -114,7 +114,7 @@ static ALboolean ALequalizerState_deviceUpdate(ALequalizerState *state, ALCdevic
     for(i = 0; i < MAX_EFFECT_CHANNELS;i++)
     {
         for(j = 0;j < 4;j++)
-            BiquadState_clear(&state->Chans[i].filter[j]);
+            BiquadFilter_clear(&state->Chans[i].filter[j]);
         for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
             state->Chans[i].CurrentGains[j] = 0.0f;
     }
@@ -128,25 +128,19 @@ static ALvoid ALequalizerState_update(ALequalizerState *state, const ALCcontext
     ALfloat gain, f0norm;
     ALuint i;
 
-    STATIC_CAST(ALeffectState,state)->OutBuffer = device->FOAOut.Buffer;
-    STATIC_CAST(ALeffectState,state)->OutChannels = device->FOAOut.NumChannels;
-    for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
-        ComputeFirstOrderGains(&device->FOAOut, IdentityMatrixf.m[i],
-                               slot->Params.Gain, state->Chans[i].TargetGains);
-
     /* Calculate coefficients for the each type of filter. Note that the shelf
      * filters' gain is for the reference frequency, which is the centerpoint
      * of the transition band.
      */
     gain = maxf(sqrtf(props->Equalizer.LowGain), 0.0625f); /* Limit -24dB */
     f0norm = props->Equalizer.LowCutoff/frequency;
-    BiquadState_setParams(&state->Chans[0].filter[0], BiquadType_LowShelf,
+    BiquadFilter_setParams(&state->Chans[0].filter[0], BiquadType_LowShelf,
         gain, f0norm, calc_rcpQ_from_slope(gain, 0.75f)
     );
 
     gain = maxf(props->Equalizer.Mid1Gain, 0.0625f);
     f0norm = props->Equalizer.Mid1Center/frequency;
-    BiquadState_setParams(&state->Chans[0].filter[1], BiquadType_Peaking,
+    BiquadFilter_setParams(&state->Chans[0].filter[1], BiquadType_Peaking,
         gain, f0norm, calc_rcpQ_from_bandwidth(
             f0norm, props->Equalizer.Mid1Width
         )
@@ -154,7 +148,7 @@ static ALvoid ALequalizerState_update(ALequalizerState *state, const ALCcontext
 
     gain = maxf(props->Equalizer.Mid2Gain, 0.0625f);
     f0norm = props->Equalizer.Mid2Center/frequency;
-    BiquadState_setParams(&state->Chans[0].filter[2], BiquadType_Peaking,
+    BiquadFilter_setParams(&state->Chans[0].filter[2], BiquadType_Peaking,
         gain, f0norm, calc_rcpQ_from_bandwidth(
             f0norm, props->Equalizer.Mid2Width
         )
@@ -162,18 +156,24 @@ static ALvoid ALequalizerState_update(ALequalizerState *state, const ALCcontext
 
     gain = maxf(sqrtf(props->Equalizer.HighGain), 0.0625f);
     f0norm = props->Equalizer.HighCutoff/frequency;
-    BiquadState_setParams(&state->Chans[0].filter[3], BiquadType_HighShelf,
+    BiquadFilter_setParams(&state->Chans[0].filter[3], BiquadType_HighShelf,
         gain, f0norm, calc_rcpQ_from_slope(gain, 0.75f)
     );
 
     /* Copy the filter coefficients for the other input channels. */
     for(i = 1;i < MAX_EFFECT_CHANNELS;i++)
     {
-        BiquadState_copyParams(&state->Chans[i].filter[0], &state->Chans[0].filter[0]);
-        BiquadState_copyParams(&state->Chans[i].filter[1], &state->Chans[0].filter[1]);
-        BiquadState_copyParams(&state->Chans[i].filter[2], &state->Chans[0].filter[2]);
-        BiquadState_copyParams(&state->Chans[i].filter[3], &state->Chans[0].filter[3]);
+        BiquadFilter_copyParams(&state->Chans[i].filter[0], &state->Chans[0].filter[0]);
+        BiquadFilter_copyParams(&state->Chans[i].filter[1], &state->Chans[0].filter[1]);
+        BiquadFilter_copyParams(&state->Chans[i].filter[2], &state->Chans[0].filter[2]);
+        BiquadFilter_copyParams(&state->Chans[i].filter[3], &state->Chans[0].filter[3]);
     }
+
+    STATIC_CAST(ALeffectState,state)->OutBuffer = device->FOAOut.Buffer;
+    STATIC_CAST(ALeffectState,state)->OutChannels = device->FOAOut.NumChannels;
+    for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
+        ComputePanGains(&device->FOAOut, IdentityMatrixf.m[i], slot->Params.Gain,
+                        state->Chans[i].TargetGains);
 }
 
 static ALvoid ALequalizerState_process(ALequalizerState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
@@ -183,10 +183,10 @@ static ALvoid ALequalizerState_process(ALequalizerState *state, ALsizei SamplesT
 
     for(c = 0;c < MAX_EFFECT_CHANNELS;c++)
     {
-        BiquadState_process(&state->Chans[c].filter[0], temps[0], SamplesIn[c], SamplesToDo);
-        BiquadState_process(&state->Chans[c].filter[1], temps[1], temps[0], SamplesToDo);
-        BiquadState_process(&state->Chans[c].filter[2], temps[2], temps[1], SamplesToDo);
-        BiquadState_process(&state->Chans[c].filter[3], temps[3], temps[2], SamplesToDo);
+        BiquadFilter_process(&state->Chans[c].filter[0], temps[0], SamplesIn[c], SamplesToDo);
+        BiquadFilter_process(&state->Chans[c].filter[1], temps[1], temps[0], SamplesToDo);
+        BiquadFilter_process(&state->Chans[c].filter[2], temps[2], temps[1], SamplesToDo);
+        BiquadFilter_process(&state->Chans[c].filter[3], temps[3], temps[2], SamplesToDo);
 
         MixSamples(temps[3], NumChannels, SamplesOut,
             state->Chans[c].CurrentGains, state->Chans[c].TargetGains,

+ 329 - 0
libs/openal-soft/Alc/effects/fshifter.c

@@ -0,0 +1,329 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 2018 by Raul Herraiz.
+ * This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the
+ *  Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#include "config.h"
+
+#include <math.h>
+#include <stdlib.h>
+
+#include "alMain.h"
+#include "alAuxEffectSlot.h"
+#include "alError.h"
+#include "alu.h"
+#include "filters/defs.h"
+
+#include "alcomplex.h"
+
+#define HIL_SIZE 1024
+#define OVERSAMP (1<<2)
+
+#define HIL_STEP     (HIL_SIZE / OVERSAMP)
+#define FIFO_LATENCY (HIL_STEP * (OVERSAMP-1))
+
+
+typedef struct ALfshifterState {
+    DERIVE_FROM_TYPE(ALeffectState);
+
+    /* Effect parameters */
+    ALsizei  count;
+    ALsizei  PhaseStep;
+    ALsizei  Phase;
+    ALdouble ld_sign;
+
+    /*Effects buffers*/ 
+    ALfloat   InFIFO[HIL_SIZE];
+    ALcomplex OutFIFO[HIL_SIZE];
+    ALcomplex OutputAccum[HIL_SIZE];
+    ALcomplex Analytic[HIL_SIZE];
+    ALcomplex Outdata[BUFFERSIZE];
+
+    alignas(16) ALfloat BufferOut[BUFFERSIZE];
+
+    /* Effect gains for each output channel */
+    ALfloat CurrentGains[MAX_OUTPUT_CHANNELS];
+    ALfloat TargetGains[MAX_OUTPUT_CHANNELS];
+} ALfshifterState;
+
+static ALvoid ALfshifterState_Destruct(ALfshifterState *state);
+static ALboolean ALfshifterState_deviceUpdate(ALfshifterState *state, ALCdevice *device);
+static ALvoid ALfshifterState_update(ALfshifterState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props);
+static ALvoid ALfshifterState_process(ALfshifterState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
+DECLARE_DEFAULT_ALLOCATORS(ALfshifterState)
+
+DEFINE_ALEFFECTSTATE_VTABLE(ALfshifterState);
+
+/* Define a Hann window, used to filter the HIL input and output. */
+alignas(16) static ALdouble HannWindow[HIL_SIZE];
+
+static void InitHannWindow(void)
+{
+    ALsizei i;
+
+    /* Create lookup table of the Hann window for the desired size, i.e. HIL_SIZE */
+    for(i = 0;i < HIL_SIZE>>1;i++)
+    {
+        ALdouble val = sin(M_PI * (ALdouble)i / (ALdouble)(HIL_SIZE-1));
+        HannWindow[i] = HannWindow[HIL_SIZE-1-i] = val * val;
+    }
+}
+
+static alonce_flag HannInitOnce = AL_ONCE_FLAG_INIT;
+
+static void ALfshifterState_Construct(ALfshifterState *state)
+{
+    ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
+    SET_VTABLE2(ALfshifterState, ALeffectState, state);
+
+    alcall_once(&HannInitOnce, InitHannWindow);
+}
+
+static ALvoid ALfshifterState_Destruct(ALfshifterState *state)
+{
+    ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
+}
+
+static ALboolean ALfshifterState_deviceUpdate(ALfshifterState *state, ALCdevice *UNUSED(device))
+{
+    /* (Re-)initializing parameters and clear the buffers. */
+    state->count     = FIFO_LATENCY;
+    state->PhaseStep = 0;
+    state->Phase     = 0;
+    state->ld_sign   = 1.0;
+
+    memset(state->InFIFO,      0, sizeof(state->InFIFO));
+    memset(state->OutFIFO,     0, sizeof(state->OutFIFO));
+    memset(state->OutputAccum, 0, sizeof(state->OutputAccum));
+    memset(state->Analytic,    0, sizeof(state->Analytic));
+
+    memset(state->CurrentGains, 0, sizeof(state->CurrentGains));
+    memset(state->TargetGains,  0, sizeof(state->TargetGains));
+
+    return AL_TRUE;
+}
+
+static ALvoid ALfshifterState_update(ALfshifterState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props)
+{
+    const ALCdevice *device = context->Device;
+    ALfloat coeffs[MAX_AMBI_COEFFS];
+    ALfloat step;
+
+    step = props->Fshifter.Frequency / (ALfloat)device->Frequency;
+    state->PhaseStep = fastf2i(minf(step, 0.5f) * FRACTIONONE);
+
+    switch(props->Fshifter.LeftDirection)
+    {
+        case AL_FREQUENCY_SHIFTER_DIRECTION_DOWN:
+            state->ld_sign = -1.0;
+            break;
+
+        case AL_FREQUENCY_SHIFTER_DIRECTION_UP:
+            state->ld_sign = 1.0;
+            break;
+
+        case AL_FREQUENCY_SHIFTER_DIRECTION_OFF:
+            state->Phase = 0;
+            state->PhaseStep = 0;
+            break;
+    }
+
+    CalcAngleCoeffs(0.0f, 0.0f, 0.0f, coeffs);
+    ComputePanGains(&device->Dry, coeffs, slot->Params.Gain, state->TargetGains);
+}
+
+static ALvoid ALfshifterState_process(ALfshifterState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
+{
+    static const ALcomplex complex_zero = { 0.0, 0.0 };
+    ALfloat *restrict BufferOut = state->BufferOut;
+    ALsizei j, k, base;
+
+    for(base = 0;base < SamplesToDo;)
+    {
+        ALsizei todo = mini(HIL_SIZE-state->count, SamplesToDo-base);
+
+        ASSUME(todo > 0);
+
+        /* Fill FIFO buffer with samples data */
+        k = state->count;
+        for(j = 0;j < todo;j++,k++)
+        {
+            state->InFIFO[k] = SamplesIn[0][base+j];
+            state->Outdata[base+j]  = state->OutFIFO[k-FIFO_LATENCY];
+        }
+        state->count += todo;
+        base += todo;
+
+        /* Check whether FIFO buffer is filled */
+        if(state->count < HIL_SIZE) continue;
+
+        state->count = FIFO_LATENCY;
+
+        /* Real signal windowing and store in Analytic buffer */
+        for(k = 0;k < HIL_SIZE;k++)
+        {
+            state->Analytic[k].Real = state->InFIFO[k] * HannWindow[k];
+            state->Analytic[k].Imag = 0.0;
+        }
+
+        /* Processing signal by Discrete Hilbert Transform (analytical signal). */
+        complex_hilbert(state->Analytic, HIL_SIZE);
+
+        /* Windowing and add to output accumulator */
+        for(k = 0;k < HIL_SIZE;k++)
+        {
+            state->OutputAccum[k].Real += 2.0/OVERSAMP*HannWindow[k]*state->Analytic[k].Real;
+            state->OutputAccum[k].Imag += 2.0/OVERSAMP*HannWindow[k]*state->Analytic[k].Imag;
+        }
+
+        /* Shift accumulator, input & output FIFO */
+        for(k = 0;k < HIL_STEP;k++) state->OutFIFO[k] = state->OutputAccum[k];
+        for(j = 0;k < HIL_SIZE;k++,j++) state->OutputAccum[j] = state->OutputAccum[k];
+        for(;j < HIL_SIZE;j++) state->OutputAccum[j] = complex_zero;
+        for(k = 0;k < FIFO_LATENCY;k++)
+            state->InFIFO[k] = state->InFIFO[k+HIL_STEP];
+    }
+
+    /* Process frequency shifter using the analytic signal obtained. */
+    for(k = 0;k < SamplesToDo;k++)
+    {
+        ALdouble phase = state->Phase * ((1.0/FRACTIONONE) * 2.0*M_PI);
+        BufferOut[k] = (ALfloat)(state->Outdata[k].Real*cos(phase) +
+                                 state->Outdata[k].Imag*sin(phase)*state->ld_sign);
+
+        state->Phase += state->PhaseStep;
+        state->Phase &= FRACTIONMASK;
+    }
+
+    /* Now, mix the processed sound data to the output. */
+    MixSamples(BufferOut, NumChannels, SamplesOut, state->CurrentGains, state->TargetGains,
+               maxi(SamplesToDo, 512), 0, SamplesToDo);
+}
+
+typedef struct FshifterStateFactory {
+    DERIVE_FROM_TYPE(EffectStateFactory);
+} FshifterStateFactory;
+
+static ALeffectState *FshifterStateFactory_create(FshifterStateFactory *UNUSED(factory))
+{
+    ALfshifterState *state;
+
+    NEW_OBJ0(state, ALfshifterState)();
+    if(!state) return NULL;
+
+    return STATIC_CAST(ALeffectState, state);
+}
+
+DEFINE_EFFECTSTATEFACTORY_VTABLE(FshifterStateFactory);
+
+EffectStateFactory *FshifterStateFactory_getFactory(void)
+{
+    static FshifterStateFactory FshifterFactory = { { GET_VTABLE2(FshifterStateFactory, EffectStateFactory) } };
+
+    return STATIC_CAST(EffectStateFactory, &FshifterFactory);
+}
+
+void ALfshifter_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
+{
+    ALeffectProps *props = &effect->Props;
+    switch(param)
+    {
+        case AL_FREQUENCY_SHIFTER_FREQUENCY:
+            if(!(val >= AL_FREQUENCY_SHIFTER_MIN_FREQUENCY && val <= AL_FREQUENCY_SHIFTER_MAX_FREQUENCY))
+                SETERR_RETURN(context, AL_INVALID_VALUE,,"Frequency shifter frequency out of range");
+            props->Fshifter.Frequency = val;
+            break;
+
+        default:
+            alSetError(context, AL_INVALID_ENUM, "Invalid frequency shifter float property 0x%04x", param);
+    }
+}
+
+void ALfshifter_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
+{
+    ALfshifter_setParamf(effect, context, param, vals[0]);
+}
+
+void ALfshifter_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
+{
+    ALeffectProps *props = &effect->Props;
+    switch(param)
+    {
+        case AL_FREQUENCY_SHIFTER_LEFT_DIRECTION:
+            if(!(val >= AL_FREQUENCY_SHIFTER_MIN_LEFT_DIRECTION && val <= AL_FREQUENCY_SHIFTER_MAX_LEFT_DIRECTION))
+                SETERR_RETURN(context, AL_INVALID_VALUE,,"Frequency shifter left direction out of range");
+            props->Fshifter.LeftDirection = val;
+            break;
+
+        case AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION:
+            if(!(val >= AL_FREQUENCY_SHIFTER_MIN_RIGHT_DIRECTION && val <= AL_FREQUENCY_SHIFTER_MAX_RIGHT_DIRECTION))
+                SETERR_RETURN(context, AL_INVALID_VALUE,,"Frequency shifter right direction out of range");
+            props->Fshifter.RightDirection = val;
+            break;
+
+        default:
+            alSetError(context, AL_INVALID_ENUM, "Invalid frequency shifter integer property 0x%04x", param);
+    }
+}
+void ALfshifter_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
+{
+    ALfshifter_setParami(effect, context, param, vals[0]);
+}
+
+void ALfshifter_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
+{
+    const ALeffectProps *props = &effect->Props;
+    switch(param)
+    {
+        case AL_FREQUENCY_SHIFTER_LEFT_DIRECTION:
+            *val = props->Fshifter.LeftDirection;
+            break;
+        case AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION:
+            *val = props->Fshifter.RightDirection;
+            break;
+        default:
+            alSetError(context, AL_INVALID_ENUM, "Invalid frequency shifter integer property 0x%04x", param);
+    }
+}
+void ALfshifter_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
+{
+    ALfshifter_getParami(effect, context, param, vals);
+}
+
+void ALfshifter_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
+{
+
+    const ALeffectProps *props = &effect->Props;
+    switch(param)
+    {
+        case AL_FREQUENCY_SHIFTER_FREQUENCY:
+            *val = props->Fshifter.Frequency;
+            break;
+
+        default:
+            alSetError(context, AL_INVALID_ENUM, "Invalid frequency shifter float property 0x%04x", param);
+    }
+
+}
+
+void ALfshifter_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
+{
+    ALfshifter_getParamf(effect, context, param, vals);
+}
+
+DEFINE_ALEFFECT_VTABLE(ALfshifter);

+ 33 - 30
libs/openal-soft/Alc/effects/modulator.c

@@ -40,10 +40,8 @@ typedef struct ALmodulatorState {
     ALsizei index;
     ALsizei step;
 
-    alignas(16) ALfloat ModSamples[MAX_UPDATE_SAMPLES];
-
     struct {
-        BiquadState Filter;
+        BiquadFilter Filter;
 
         ALfloat CurrentGains[MAX_OUTPUT_CHANNELS];
         ALfloat TargetGains[MAX_OUTPUT_CHANNELS];
@@ -65,17 +63,22 @@ DEFINE_ALEFFECTSTATE_VTABLE(ALmodulatorState);
 
 static inline ALfloat Sin(ALsizei index)
 {
-    return sinf(index*(F_TAU/WAVEFORM_FRACONE) - F_PI)*0.5f + 0.5f;
+    return sinf((ALfloat)index * (F_TAU / WAVEFORM_FRACONE));
 }
 
 static inline ALfloat Saw(ALsizei index)
 {
-    return (ALfloat)index / WAVEFORM_FRACONE;
+    return (ALfloat)index*(2.0f/WAVEFORM_FRACONE) - 1.0f;
 }
 
 static inline ALfloat Square(ALsizei index)
 {
-    return (ALfloat)((index >> (WAVEFORM_FRACBITS - 1)) & 1);
+    return (ALfloat)(((index>>(WAVEFORM_FRACBITS-2))&2) - 1);
+}
+
+static inline ALfloat One(ALsizei UNUSED(index))
+{
+    return 1.0f;
 }
 
 #define DECL_TEMPLATE(func)                                                   \
@@ -94,6 +97,7 @@ static void Modulate##func(ALfloat *restrict dst, ALsizei index,              \
 DECL_TEMPLATE(Sin)
 DECL_TEMPLATE(Saw)
 DECL_TEMPLATE(Square)
+DECL_TEMPLATE(One)
 
 #undef DECL_TEMPLATE
 
@@ -117,7 +121,7 @@ static ALboolean ALmodulatorState_deviceUpdate(ALmodulatorState *state, ALCdevic
     ALsizei i, j;
     for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
     {
-        BiquadState_clear(&state->Chans[i].Filter);
+        BiquadFilter_clear(&state->Chans[i].Filter);
         for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
             state->Chans[i].CurrentGains[j] = 0.0f;
     }
@@ -127,48 +131,45 @@ static ALboolean ALmodulatorState_deviceUpdate(ALmodulatorState *state, ALCdevic
 static ALvoid ALmodulatorState_update(ALmodulatorState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props)
 {
     const ALCdevice *device = context->Device;
-    ALfloat cw, a;
+    ALfloat f0norm;
     ALsizei i;
 
-    if(props->Modulator.Waveform == AL_RING_MODULATOR_SINUSOID)
+    state->step = fastf2i(props->Modulator.Frequency / (ALfloat)device->Frequency *
+                          WAVEFORM_FRACONE);
+    state->step = clampi(state->step, 0, WAVEFORM_FRACONE-1);
+
+    if(state->step == 0)
+        state->GetSamples = ModulateOne;
+    else if(props->Modulator.Waveform == AL_RING_MODULATOR_SINUSOID)
         state->GetSamples = ModulateSin;
     else if(props->Modulator.Waveform == AL_RING_MODULATOR_SAWTOOTH)
         state->GetSamples = ModulateSaw;
     else /*if(Slot->Params.EffectProps.Modulator.Waveform == AL_RING_MODULATOR_SQUARE)*/
         state->GetSamples = ModulateSquare;
 
-    state->step = fastf2i(props->Modulator.Frequency*WAVEFORM_FRACONE /
-                          device->Frequency);
-    state->step = clampi(state->step, 1, WAVEFORM_FRACONE-1);
-
-    /* Custom filter coeffs, which match the old version instead of a low-shelf. */
-    cw = cosf(F_TAU * props->Modulator.HighPassCutoff / device->Frequency);
-    a = (2.0f-cw) - sqrtf(powf(2.0f-cw, 2.0f) - 1.0f);
-
-    state->Chans[0].Filter.b0 = a;
-    state->Chans[0].Filter.b1 = -a;
-    state->Chans[0].Filter.b2 = 0.0f;
-    state->Chans[0].Filter.a1 = -a;
-    state->Chans[0].Filter.a2 = 0.0f;
+    f0norm = props->Modulator.HighPassCutoff / (ALfloat)device->Frequency;
+    f0norm = clampf(f0norm, 1.0f/512.0f, 0.49f);
+    /* Bandwidth value is constant in octaves. */
+    BiquadFilter_setParams(&state->Chans[0].Filter, BiquadType_HighPass, 1.0f,
+                           f0norm, calc_rcpQ_from_bandwidth(f0norm, 0.75f));
     for(i = 1;i < MAX_EFFECT_CHANNELS;i++)
-        BiquadState_copyParams(&state->Chans[i].Filter, &state->Chans[0].Filter);
+        BiquadFilter_copyParams(&state->Chans[i].Filter, &state->Chans[0].Filter);
 
     STATIC_CAST(ALeffectState,state)->OutBuffer = device->FOAOut.Buffer;
     STATIC_CAST(ALeffectState,state)->OutChannels = device->FOAOut.NumChannels;
     for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
-        ComputeFirstOrderGains(&device->FOAOut, IdentityMatrixf.m[i],
-                               slot->Params.Gain, state->Chans[i].TargetGains);
+        ComputePanGains(&device->FOAOut, IdentityMatrixf.m[i], slot->Params.Gain,
+                        state->Chans[i].TargetGains);
 }
 
 static ALvoid ALmodulatorState_process(ALmodulatorState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
 {
-    ALfloat *restrict modsamples = ASSUME_ALIGNED(state->ModSamples, 16);
     const ALsizei step = state->step;
     ALsizei base;
 
     for(base = 0;base < SamplesToDo;)
     {
-        alignas(16) ALfloat temps[2][MAX_UPDATE_SAMPLES];
+        alignas(16) ALfloat modsamples[MAX_UPDATE_SAMPLES];
         ALsizei td = mini(MAX_UPDATE_SAMPLES, SamplesToDo-base);
         ALsizei c, i;
 
@@ -178,11 +179,13 @@ static ALvoid ALmodulatorState_process(ALmodulatorState *state, ALsizei SamplesT
 
         for(c = 0;c < MAX_EFFECT_CHANNELS;c++)
         {
-            BiquadState_process(&state->Chans[c].Filter, temps[0], &SamplesIn[c][base], td);
+            alignas(16) ALfloat temps[MAX_UPDATE_SAMPLES];
+
+            BiquadFilter_process(&state->Chans[c].Filter, temps, &SamplesIn[c][base], td);
             for(i = 0;i < td;i++)
-                temps[1][i] = temps[0][i] * modsamples[i];
+                temps[i] *= modsamples[i];
 
-            MixSamples(temps[1], NumChannels, SamplesOut, state->Chans[c].CurrentGains,
+            MixSamples(temps, NumChannels, SamplesOut, state->Chans[c].CurrentGains,
                        state->Chans[c].TargetGains, SamplesToDo-base, base, td);
         }
 

+ 80 - 135
libs/openal-soft/Alc/effects/pshifter.c

@@ -29,6 +29,8 @@
 #include "alu.h"
 #include "filters/defs.h"
 
+#include "alcomplex.h"
+
 
 #define STFT_SIZE      1024
 #define STFT_HALF_SIZE (STFT_SIZE>>1)
@@ -37,35 +39,33 @@
 #define STFT_STEP    (STFT_SIZE / OVERSAMP)
 #define FIFO_LATENCY (STFT_STEP * (OVERSAMP-1))
 
-typedef struct ALcomplex {
-    ALfloat Real;
-    ALfloat Imag;
-} ALcomplex;
 
 typedef struct ALphasor {
-    ALfloat Amplitude;
-    ALfloat Phase;
+    ALdouble Amplitude;
+    ALdouble Phase;
 } ALphasor;
 
 typedef struct ALFrequencyDomain {
-    ALfloat Amplitude;
-    ALfloat Frequency;
+    ALdouble Amplitude;
+    ALdouble Frequency;
 } ALfrequencyDomain;
 
+
 typedef struct ALpshifterState {
     DERIVE_FROM_TYPE(ALeffectState);
 
     /* Effect parameters */
     ALsizei count;
+    ALsizei PitchShiftI;
     ALfloat PitchShift;
     ALfloat FreqPerBin;
 
     /*Effects buffers*/
     ALfloat InFIFO[STFT_SIZE];
     ALfloat OutFIFO[STFT_STEP];
-    ALfloat LastPhase[STFT_HALF_SIZE+1];
-    ALfloat SumPhase[STFT_HALF_SIZE+1];
-    ALfloat OutputAccum[STFT_SIZE];
+    ALdouble LastPhase[STFT_HALF_SIZE+1];
+    ALdouble SumPhase[STFT_HALF_SIZE+1];
+    ALdouble OutputAccum[STFT_SIZE];
 
     ALcomplex FFTbuffer[STFT_SIZE];
 
@@ -89,7 +89,7 @@ DEFINE_ALEFFECTSTATE_VTABLE(ALpshifterState);
 
 
 /* Define a Hann window, used to filter the STFT input and output. */
-alignas(16) static ALfloat HannWindow[STFT_SIZE];
+alignas(16) static ALdouble HannWindow[STFT_SIZE];
 
 static void InitHannWindow(void)
 {
@@ -99,124 +99,65 @@ static void InitHannWindow(void)
     for(i = 0;i < STFT_SIZE>>1;i++)
     {
         ALdouble val = sin(M_PI * (ALdouble)i / (ALdouble)(STFT_SIZE-1));
-        HannWindow[i] = HannWindow[STFT_SIZE-(i+1)] = (ALfloat)(val * val);
+        HannWindow[i] = HannWindow[STFT_SIZE-1-i] = val * val;
     }
 }
 static alonce_flag HannInitOnce = AL_ONCE_FLAG_INIT;
 
 
+static inline ALint double2int(ALdouble d)
+{
+#if ((defined(__GNUC__) || defined(__clang__)) && (defined(__i386__) || defined(__x86_64__)) && \
+     !defined(__SSE2_MATH__)) || (defined(_MSC_VER) && defined(_M_IX86_FP) && _M_IX86_FP < 2)
+    ALint sign, shift;
+    ALint64 mant;
+    union {
+        ALdouble d;
+        ALint64 i64;
+    } conv;
+
+    conv.d = d;
+    sign = (conv.i64>>63) | 1;
+    shift = ((conv.i64>>52)&0x7ff) - (1023+52);
+
+    /* Over/underflow */
+    if(UNLIKELY(shift >= 63 || shift < -52))
+        return 0;
+
+    mant = (conv.i64&I64(0xfffffffffffff)) | I64(0x10000000000000);
+    if(LIKELY(shift < 0))
+        return (ALint)(mant >> -shift) * sign;
+    return (ALint)(mant << shift) * sign;
+
+#else
+
+    return (ALint)d;
+#endif
+}
+
+
 /* Converts ALcomplex to ALphasor */
 static inline ALphasor rect2polar(ALcomplex number)
 {
     ALphasor polar;
 
-    polar.Amplitude = sqrtf(number.Real*number.Real + number.Imag*number.Imag);
-    polar.Phase     = atan2f(number.Imag , number.Real);
+    polar.Amplitude = sqrt(number.Real*number.Real + number.Imag*number.Imag);
+    polar.Phase     = atan2(number.Imag, number.Real);
 
     return polar;
 }
 
 /* Converts ALphasor to ALcomplex */
-static inline ALcomplex polar2rect(ALphasor  number)
+static inline ALcomplex polar2rect(ALphasor number)
 {
     ALcomplex cartesian;
 
-    cartesian.Real = number.Amplitude * cosf(number.Phase);
-    cartesian.Imag = number.Amplitude * sinf(number.Phase);
+    cartesian.Real = number.Amplitude * cos(number.Phase);
+    cartesian.Imag = number.Amplitude * sin(number.Phase);
 
     return cartesian;
 }
 
-/* Addition of two complex numbers (ALcomplex format) */
-static inline ALcomplex complex_add(ALcomplex a, ALcomplex b)
-{
-    ALcomplex result;
-
-    result.Real = a.Real + b.Real;
-    result.Imag = a.Imag + b.Imag;
-
-    return result;
-}
-
-/* Subtraction of two complex numbers (ALcomplex format) */
-static inline ALcomplex complex_sub(ALcomplex a, ALcomplex b)
-{
-    ALcomplex result;
-
-    result.Real = a.Real - b.Real;
-    result.Imag = a.Imag - b.Imag;
-
-    return result;
-}
-
-/* Multiplication of two complex numbers (ALcomplex format) */
-static inline ALcomplex complex_mult(ALcomplex a, ALcomplex b)
-{
-    ALcomplex result;
-
-    result.Real = a.Real*b.Real - a.Imag*b.Imag;
-    result.Imag = a.Imag*b.Real + a.Real*b.Imag;
-
-    return result;
-}
-
-/* Iterative implementation of 2-radix FFT (In-place algorithm). Sign = -1 is
- * FFT and 1 is iFFT (inverse). Fills FFTBuffer[0...FFTSize-1] with the
- * Discrete Fourier Transform (DFT) of the time domain data stored in
- * FFTBuffer[0...FFTSize-1]. FFTBuffer is an array of complex numbers
- * (ALcomplex), FFTSize MUST BE power of two.
- */
-static inline ALvoid FFT(ALcomplex *FFTBuffer, ALsizei FFTSize, ALfloat Sign)
-{
-    ALsizei i, j, k, mask, step, step2;
-    ALcomplex temp, u, w;
-    ALfloat arg;
-
-    /* Bit-reversal permutation applied to a sequence of FFTSize items */
-    for(i = 1;i < FFTSize-1;i++)
-    {
-        for(mask = 0x1, j = 0;mask < FFTSize;mask <<= 1)
-        {
-            if((i&mask) != 0)
-                j++;
-            j <<= 1;
-        }
-        j >>= 1;
-
-        if(i < j)
-        {
-            temp         = FFTBuffer[i];
-            FFTBuffer[i] = FFTBuffer[j];
-            FFTBuffer[j] = temp;
-        }
-    }
-
-    /* Iterative form of Danielson–Lanczos lemma */
-    for(i = 1, step = 2;i < FFTSize;i<<=1, step<<=1)
-    {
-        step2 = step >> 1;
-        arg   = F_PI / step2;
-
-        w.Real = cosf(arg);
-        w.Imag = sinf(arg) * Sign;
-
-        u.Real = 1.0f;
-        u.Imag = 0.0f;
-
-        for(j = 0;j < step2;j++)
-        {
-            for(k = j;k < FFTSize;k+=step)
-            {
-                temp               = complex_mult(FFTBuffer[k+step2], u);
-                FFTBuffer[k+step2] = complex_sub(FFTBuffer[k], temp);
-                FFTBuffer[k]       = complex_add(FFTBuffer[k], temp);
-            }
-
-            u = complex_mult(u, w);
-        }
-    }
-}
-
 
 static void ALpshifterState_Construct(ALpshifterState *state)
 {
@@ -234,9 +175,10 @@ static ALvoid ALpshifterState_Destruct(ALpshifterState *state)
 static ALboolean ALpshifterState_deviceUpdate(ALpshifterState *state, ALCdevice *device)
 {
     /* (Re-)initializing parameters and clear the buffers. */
-    state->count      = FIFO_LATENCY;
-    state->PitchShift = 1.0f;
-    state->FreqPerBin = device->Frequency / (ALfloat)STFT_SIZE;
+    state->count       = FIFO_LATENCY;
+    state->PitchShiftI = FRACTIONONE;
+    state->PitchShift  = 1.0f;
+    state->FreqPerBin  = device->Frequency / (ALfloat)STFT_SIZE;
 
     memset(state->InFIFO,          0, sizeof(state->InFIFO));
     memset(state->OutFIFO,         0, sizeof(state->OutFIFO));
@@ -257,13 +199,16 @@ static ALvoid ALpshifterState_update(ALpshifterState *state, const ALCcontext *c
 {
     const ALCdevice *device = context->Device;
     ALfloat coeffs[MAX_AMBI_COEFFS];
+    float pitch;
 
-    state->PitchShift = powf(2.0f,
+    pitch = powf(2.0f,
         (ALfloat)(props->Pshifter.CoarseTune*100 + props->Pshifter.FineTune) / 1200.0f
     );
+    state->PitchShiftI = fastf2i(pitch*FRACTIONONE);
+    state->PitchShift  = state->PitchShiftI * (1.0f/FRACTIONONE);
 
     CalcAngleCoeffs(0.0f, 0.0f, 0.0f, coeffs);
-    ComputeDryPanGains(&device->Dry, coeffs, slot->Params.Gain, state->TargetGains);
+    ComputePanGains(&device->Dry, coeffs, slot->Params.Gain, state->TargetGains);
 }
 
 static ALvoid ALpshifterState_process(ALpshifterState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
@@ -272,8 +217,8 @@ static ALvoid ALpshifterState_process(ALpshifterState *state, ALsizei SamplesToD
      * http://blogs.zynaptiq.com/bernsee/pitch-shifting-using-the-ft/
      */
 
-    static const ALfloat expected = F_TAU / (ALfloat)OVERSAMP;
-    const ALfloat freq_per_bin = state->FreqPerBin;
+    static const ALdouble expected = M_PI*2.0 / OVERSAMP;
+    const ALdouble freq_per_bin = state->FreqPerBin;
     ALfloat *restrict bufferOut = state->BufferOut;
     ALsizei count = state->count;
     ALsizei i, j, k;
@@ -296,12 +241,12 @@ static ALvoid ALpshifterState_process(ALpshifterState *state, ALsizei SamplesToD
         for(k = 0;k < STFT_SIZE;k++)
         {
             state->FFTbuffer[k].Real = state->InFIFO[k] * HannWindow[k];
-            state->FFTbuffer[k].Imag = 0.0f;
+            state->FFTbuffer[k].Imag = 0.0;
         }
 
         /* ANALYSIS */
         /* Apply FFT to FFTbuffer data */
-        FFT(state->FFTbuffer, STFT_SIZE, -1.0f);
+        complex_fft(state->FFTbuffer, STFT_SIZE, -1.0);
 
         /* Analyze the obtained data. Since the real FFT is symmetric, only
          * STFT_HALF_SIZE+1 samples are needed.
@@ -309,18 +254,18 @@ static ALvoid ALpshifterState_process(ALpshifterState *state, ALsizei SamplesToD
         for(k = 0;k < STFT_HALF_SIZE+1;k++)
         {
             ALphasor component;
-            ALfloat tmp;
+            ALdouble tmp;
             ALint qpd;
 
             /* Compute amplitude and phase */
             component = rect2polar(state->FFTbuffer[k]);
 
             /* Compute phase difference and subtract expected phase difference */
-            tmp = (component.Phase - state->LastPhase[k]) - (ALfloat)k*expected;
+            tmp = (component.Phase - state->LastPhase[k]) - k*expected;
 
             /* Map delta phase into +/- Pi interval */
-            qpd = fastf2i(tmp / F_PI);
-            tmp -= F_PI * (ALfloat)(qpd + (qpd%2));
+            qpd = double2int(tmp / M_PI);
+            tmp -= M_PI * (qpd + (qpd%2));
 
             /* Get deviation from bin frequency from the +/- Pi interval */
             tmp /= expected;
@@ -329,8 +274,8 @@ static ALvoid ALpshifterState_process(ALpshifterState *state, ALsizei SamplesToD
              * for maintain the gain (because half of bins are used) and store
              * amplitude and true frequency in analysis buffer.
              */
-            state->Analysis_buffer[k].Amplitude = 2.0f * component.Amplitude;
-            state->Analysis_buffer[k].Frequency = ((ALfloat)k + tmp) * freq_per_bin;
+            state->Analysis_buffer[k].Amplitude = 2.0 * component.Amplitude;
+            state->Analysis_buffer[k].Frequency = (k + tmp) * freq_per_bin;
 
             /* Store actual phase[k] for the calculations in the next frame*/
             state->LastPhase[k] = component.Phase;
@@ -340,13 +285,13 @@ static ALvoid ALpshifterState_process(ALpshifterState *state, ALsizei SamplesToD
         /* pitch shifting */
         for(k = 0;k < STFT_HALF_SIZE+1;k++)
         {
-            state->Syntesis_buffer[k].Amplitude = 0.0f;
-            state->Syntesis_buffer[k].Frequency = 0.0f;
+            state->Syntesis_buffer[k].Amplitude = 0.0;
+            state->Syntesis_buffer[k].Frequency = 0.0;
         }
 
         for(k = 0;k < STFT_HALF_SIZE+1;k++)
         {
-            j = fastf2i((ALfloat)k * state->PitchShift);
+            j = (k*state->PitchShiftI) >> FRACTIONBITS;
             if(j >= STFT_HALF_SIZE+1) break;
 
             state->Syntesis_buffer[j].Amplitude += state->Analysis_buffer[k].Amplitude;
@@ -359,13 +304,13 @@ static ALvoid ALpshifterState_process(ALpshifterState *state, ALsizei SamplesToD
         for(k = 0;k < STFT_HALF_SIZE+1;k++)
         {
             ALphasor component;
-            ALfloat tmp;
+            ALdouble tmp;
 
             /* Compute bin deviation from scaled freq */
-            tmp = state->Syntesis_buffer[k].Frequency/freq_per_bin - (ALfloat)k;
+            tmp = state->Syntesis_buffer[k].Frequency/freq_per_bin - k;
 
             /* Calculate actual delta phase and accumulate it to get bin phase */
-            state->SumPhase[k] += ((ALfloat)k + tmp) * expected;
+            state->SumPhase[k] += (k + tmp) * expected;
 
             component.Amplitude = state->Syntesis_buffer[k].Amplitude;
             component.Phase     = state->SumPhase[k];
@@ -376,22 +321,22 @@ static ALvoid ALpshifterState_process(ALpshifterState *state, ALsizei SamplesToD
         /* zero negative frequencies for recontruct a real signal */
         for(k = STFT_HALF_SIZE+1;k < STFT_SIZE;k++)
         {
-            state->FFTbuffer[k].Real = 0.0f;
-            state->FFTbuffer[k].Imag = 0.0f;
+            state->FFTbuffer[k].Real = 0.0;
+            state->FFTbuffer[k].Imag = 0.0;
         }
 
         /* Apply iFFT to buffer data */
-        FFT(state->FFTbuffer, STFT_SIZE, 1.0f);
+        complex_fft(state->FFTbuffer, STFT_SIZE, 1.0);
 
         /* Windowing and add to output */
         for(k = 0;k < STFT_SIZE;k++)
             state->OutputAccum[k] += HannWindow[k] * state->FFTbuffer[k].Real /
-                                     (0.5f * STFT_HALF_SIZE * OVERSAMP);
+                                     (0.5 * STFT_HALF_SIZE * OVERSAMP);
 
         /* Shift accumulator, input & output FIFO */
-        for(k = 0;k < STFT_STEP;k++) state->OutFIFO[k] = state->OutputAccum[k];
+        for(k = 0;k < STFT_STEP;k++) state->OutFIFO[k] = (ALfloat)state->OutputAccum[k];
         for(j = 0;k < STFT_SIZE;k++,j++) state->OutputAccum[j] = state->OutputAccum[k];
-        for(;j < STFT_SIZE;j++) state->OutputAccum[j] = 0.0f;
+        for(;j < STFT_SIZE;j++) state->OutputAccum[j] = 0.0;
         for(k = 0;k < FIFO_LATENCY;k++)
             state->InFIFO[k] = state->InFIFO[k+STFT_STEP];
     }

File diff suppressed because it is too large
+ 227 - 429
libs/openal-soft/Alc/effects/reverb.c


+ 19 - 25
libs/openal-soft/Alc/filters/defs.h

@@ -30,14 +30,14 @@ typedef enum BiquadType {
     BiquadType_BandPass,
 } BiquadType;
 
-typedef struct BiquadState {
-    ALfloat x[2]; /* History of two last input samples  */
-    ALfloat y[2]; /* History of two last output samples */
-    ALfloat b0, b1, b2; /* Transfer function coefficients "b" */
-    ALfloat a1, a2; /* Transfer function coefficients "a" (a0 is pre-applied) */
-} BiquadState;
+typedef struct BiquadFilter {
+    ALfloat z1, z2; /* Last two delayed components for direct form II. */
+    ALfloat b0, b1, b2; /* Transfer function coefficients "b" (numerator) */
+    ALfloat a1, a2; /* Transfer function coefficients "a" (denominator; a0 is
+                     * pre-applied). */
+} BiquadFilter;
 /* Currently only a C-based filter process method is implemented. */
-#define BiquadState_process BiquadState_processC
+#define BiquadFilter_process BiquadFilter_processC
 
 /**
  * Calculates the rcpQ (i.e. 1/Q) coefficient for shelving filters, using the
@@ -61,12 +61,10 @@ inline ALfloat calc_rcpQ_from_bandwidth(ALfloat f0norm, ALfloat bandwidth)
     return 2.0f*sinhf(logf(2.0f)/2.0f*bandwidth*w0/sinf(w0));
 }
 
-inline void BiquadState_clear(BiquadState *filter)
+inline void BiquadFilter_clear(BiquadFilter *filter)
 {
-    filter->x[0] = 0.0f;
-    filter->x[1] = 0.0f;
-    filter->y[0] = 0.0f;
-    filter->y[1] = 0.0f;
+    filter->z1 = 0.0f;
+    filter->z2 = 0.0f;
 }
 
 /**
@@ -84,9 +82,9 @@ inline void BiquadState_clear(BiquadState *filter)
  *             band. Can be generated from calc_rcpQ_from_slope or
  *             calc_rcpQ_from_bandwidth depending on the available data.
  */
-void BiquadState_setParams(BiquadState *filter, BiquadType type, ALfloat gain, ALfloat f0norm, ALfloat rcpQ);
+void BiquadFilter_setParams(BiquadFilter *filter, BiquadType type, ALfloat gain, ALfloat f0norm, ALfloat rcpQ);
 
-inline void BiquadState_copyParams(BiquadState *restrict dst, const BiquadState *restrict src)
+inline void BiquadFilter_copyParams(BiquadFilter *restrict dst, const BiquadFilter *restrict src)
 {
     dst->b0 = src->b0;
     dst->b1 = src->b1;
@@ -95,23 +93,19 @@ inline void BiquadState_copyParams(BiquadState *restrict dst, const BiquadState
     dst->a2 = src->a2;
 }
 
-void BiquadState_processC(BiquadState *filter, ALfloat *restrict dst, const ALfloat *restrict src, ALsizei numsamples);
+void BiquadFilter_processC(BiquadFilter *filter, ALfloat *restrict dst, const ALfloat *restrict src, ALsizei numsamples);
 
-inline void BiquadState_processPassthru(BiquadState *filter, const ALfloat *restrict src, ALsizei numsamples)
+inline void BiquadFilter_passthru(BiquadFilter *filter, ALsizei numsamples)
 {
-    if(numsamples >= 2)
+    if(LIKELY(numsamples >= 2))
     {
-        filter->x[1] = src[numsamples-2];
-        filter->x[0] = src[numsamples-1];
-        filter->y[1] = src[numsamples-2];
-        filter->y[0] = src[numsamples-1];
+        filter->z1 = 0.0f;
+        filter->z2 = 0.0f;
     }
     else if(numsamples == 1)
     {
-        filter->x[1] = filter->x[0];
-        filter->x[0] = src[0];
-        filter->y[1] = filter->y[0];
-        filter->y[0] = src[0];
+        filter->z1 = filter->z2;
+        filter->z2 = 0.0f;
     }
 }
 

+ 32 - 36
libs/openal-soft/Alc/filters/filter.c

@@ -7,14 +7,14 @@
 #include "alMain.h"
 #include "defs.h"
 
-extern inline void BiquadState_clear(BiquadState *filter);
-extern inline void BiquadState_copyParams(BiquadState *restrict dst, const BiquadState *restrict src);
-extern inline void BiquadState_processPassthru(BiquadState *filter, const ALfloat *restrict src, ALsizei numsamples);
+extern inline void BiquadFilter_clear(BiquadFilter *filter);
+extern inline void BiquadFilter_copyParams(BiquadFilter *restrict dst, const BiquadFilter *restrict src);
+extern inline void BiquadFilter_passthru(BiquadFilter *filter, ALsizei numsamples);
 extern inline ALfloat calc_rcpQ_from_slope(ALfloat gain, ALfloat slope);
 extern inline ALfloat calc_rcpQ_from_bandwidth(ALfloat f0norm, ALfloat bandwidth);
 
 
-void BiquadState_setParams(BiquadState *filter, BiquadType type, ALfloat gain, ALfloat f0norm, ALfloat rcpQ)
+void BiquadFilter_setParams(BiquadFilter *filter, BiquadType type, ALfloat gain, ALfloat f0norm, ALfloat rcpQ)
 {
     ALfloat alpha, sqrtgain_alpha_2;
     ALfloat w0, sin_w0, cos_w0;
@@ -94,40 +94,36 @@ void BiquadState_setParams(BiquadState *filter, BiquadType type, ALfloat gain, A
 }
 
 
-void BiquadState_processC(BiquadState *filter, ALfloat *restrict dst, const ALfloat *restrict src, ALsizei numsamples)
+void BiquadFilter_processC(BiquadFilter *filter, ALfloat *restrict dst, const ALfloat *restrict src, ALsizei numsamples)
 {
+    const ALfloat a1 = filter->a1;
+    const ALfloat a2 = filter->a2;
+    const ALfloat b0 = filter->b0;
+    const ALfloat b1 = filter->b1;
+    const ALfloat b2 = filter->b2;
+    ALfloat z1 = filter->z1;
+    ALfloat z2 = filter->z2;
     ALsizei i;
-    if(LIKELY(numsamples > 1))
-    {
-        ALfloat x0 = filter->x[0];
-        ALfloat x1 = filter->x[1];
-        ALfloat y0 = filter->y[0];
-        ALfloat y1 = filter->y[1];
-
-        for(i = 0;i < numsamples;i++)
-        {
-            dst[i] = filter->b0* src[i] +
-                     filter->b1*x0 + filter->b2*x1 -
-                     filter->a1*y0 - filter->a2*y1;
-            y1 = y0; y0 = dst[i];
-            x1 = x0; x0 = src[i];
-        }
-
-        filter->x[0] = x0;
-        filter->x[1] = x1;
-        filter->y[0] = y0;
-        filter->y[1] = y1;
-    }
-    else if(numsamples == 1)
+
+    ASSUME(numsamples > 0);
+
+    /* Processing loop is Transposed Direct Form II. This requires less storage
+     * compared to Direct Form I (only two delay components, instead of a four-
+     * sample history; the last two inputs and outputs), and works better for
+     * floating-point which favors summing similarly-sized values while being
+     * less bothered by overflow.
+     *
+     * See: http://www.earlevel.com/main/2003/02/28/biquads/
+     */
+    for(i = 0;i < numsamples;i++)
     {
-        dst[0] = filter->b0 * src[0] +
-                 filter->b1 * filter->x[0] +
-                 filter->b2 * filter->x[1] -
-                 filter->a1 * filter->y[0] -
-                 filter->a2 * filter->y[1];
-        filter->x[1] = filter->x[0];
-        filter->x[0] = src[0];
-        filter->y[1] = filter->y[0];
-        filter->y[0] = dst[0];
+        ALfloat input = src[i];
+        ALfloat output = input*b0 + z1;
+        z1 = input*b1 - output*a1 + z2;
+        z2 = input*b2 - output*a2;
+        dst[i] = output;
     }
+
+    filter->z1 = z1;
+    filter->z2 = z2;
 }

+ 83 - 85
libs/openal-soft/Alc/filters/nfc.c

@@ -2,6 +2,7 @@
 #include "config.h"
 
 #include "nfc.h"
+#include "alMain.h"
 
 #include <string.h>
 
@@ -57,25 +58,25 @@ static void NfcFilterCreate1(struct NfcFilter1 *nfc, const float w0, const float
     float b_00, g_0;
     float r;
 
-    nfc->g = 1.0f;
-    nfc->coeffs[0] = 1.0f;
+    nfc->base_gain = 1.0f;
+    nfc->gain = 1.0f;
 
     /* Calculate bass-boost coefficients. */
     r = 0.5f * w0;
     b_00 = B[1][0] * r;
     g_0 = 1.0f + b_00;
 
-    nfc->coeffs[0] *= g_0;
-    nfc->coeffs[1] = (2.0f * b_00) / g_0;
+    nfc->gain *= g_0;
+    nfc->b1 = 2.0f * b_00 / g_0;
 
     /* Calculate bass-cut coefficients. */
     r = 0.5f * w1;
     b_00 = B[1][0] * r;
     g_0 = 1.0f + b_00;
 
-    nfc->g /= g_0;
-    nfc->coeffs[0] /= g_0;
-    nfc->coeffs[1+1] = (2.0f * b_00) / g_0;
+    nfc->base_gain /= g_0;
+    nfc->gain /= g_0;
+    nfc->a1 = 2.0f * b_00 / g_0;
 }
 
 static void NfcFilterAdjust1(struct NfcFilter1 *nfc, const float w0)
@@ -87,8 +88,8 @@ static void NfcFilterAdjust1(struct NfcFilter1 *nfc, const float w0)
     b_00 = B[1][0] * r;
     g_0 = 1.0f + b_00;
 
-    nfc->coeffs[0] = nfc->g * g_0;
-    nfc->coeffs[1] = (2.0f * b_00) / g_0;
+    nfc->gain = nfc->base_gain * g_0;
+    nfc->b1 = 2.0f * b_00 / g_0;
 }
 
 
@@ -97,8 +98,8 @@ static void NfcFilterCreate2(struct NfcFilter2 *nfc, const float w0, const float
     float b_10, b_11, g_1;
     float r;
 
-    nfc->g = 1.0f;
-    nfc->coeffs[0] = 1.0f;
+    nfc->base_gain = 1.0f;
+    nfc->gain = 1.0f;
 
     /* Calculate bass-boost coefficients. */
     r = 0.5f * w0;
@@ -106,9 +107,9 @@ static void NfcFilterCreate2(struct NfcFilter2 *nfc, const float w0, const float
     b_11 = B[2][1] * r * r;
     g_1 = 1.0f + b_10 + b_11;
 
-    nfc->coeffs[0] *= g_1;
-    nfc->coeffs[1] = ((2.0f * b_10) + (4.0f * b_11)) / g_1;
-    nfc->coeffs[2] = (4.0f * b_11) / g_1;
+    nfc->gain *= g_1;
+    nfc->b1 = (2.0f*b_10 + 4.0f*b_11) / g_1;
+    nfc->b2 = 4.0f * b_11 / g_1;
 
     /* Calculate bass-cut coefficients. */
     r = 0.5f * w1;
@@ -116,10 +117,10 @@ static void NfcFilterCreate2(struct NfcFilter2 *nfc, const float w0, const float
     b_11 = B[2][1] * r * r;
     g_1 = 1.0f + b_10 + b_11;
 
-    nfc->g /= g_1;
-    nfc->coeffs[0] /= g_1;
-    nfc->coeffs[2+1] = ((2.0f * b_10) + (4.0f * b_11)) / g_1;
-    nfc->coeffs[2+2] = (4.0f * b_11) / g_1;
+    nfc->base_gain /= g_1;
+    nfc->gain /= g_1;
+    nfc->a1 = (2.0f*b_10 + 4.0f*b_11) / g_1;
+    nfc->a2 = 4.0f * b_11 / g_1;
 }
 
 static void NfcFilterAdjust2(struct NfcFilter2 *nfc, const float w0)
@@ -132,9 +133,9 @@ static void NfcFilterAdjust2(struct NfcFilter2 *nfc, const float w0)
     b_11 = B[2][1] * r * r;
     g_1 = 1.0f + b_10 + b_11;
 
-    nfc->coeffs[0] = nfc->g * g_1;
-    nfc->coeffs[1] = ((2.0f * b_10) + (4.0f * b_11)) / g_1;
-    nfc->coeffs[2] = (4.0f * b_11) / g_1;
+    nfc->gain = nfc->base_gain * g_1;
+    nfc->b1 = (2.0f*b_10 + 4.0f*b_11) / g_1;
+    nfc->b2 = 4.0f * b_11 / g_1;
 }
 
 
@@ -144,8 +145,8 @@ static void NfcFilterCreate3(struct NfcFilter3 *nfc, const float w0, const float
     float b_00, g_0;
     float r;
 
-    nfc->g = 1.0f;
-    nfc->coeffs[0] = 1.0f;
+    nfc->base_gain = 1.0f;
+    nfc->gain = 1.0f;
 
     /* Calculate bass-boost coefficients. */
     r = 0.5f * w0;
@@ -153,15 +154,15 @@ static void NfcFilterCreate3(struct NfcFilter3 *nfc, const float w0, const float
     b_11 = B[3][1] * r * r;
     g_1 = 1.0f + b_10 + b_11;
 
-    nfc->coeffs[0] *= g_1;
-    nfc->coeffs[1] = ((2.0f * b_10) + (4.0f * b_11)) / g_1;
-    nfc->coeffs[2] = (4.0f * b_11) / g_1;
+    nfc->gain *= g_1;
+    nfc->b1 = (2.0f*b_10 + 4.0f*b_11) / g_1;
+    nfc->b2 = 4.0f * b_11 / g_1;
 
     b_00 = B[3][2] * r;
     g_0 = 1.0f + b_00;
 
-    nfc->coeffs[0] *= g_0;
-    nfc->coeffs[2+1] = (2.0f * b_00) / g_0;
+    nfc->gain *= g_0;
+    nfc->b3 = 2.0f * b_00 / g_0;
 
     /* Calculate bass-cut coefficients. */
     r = 0.5f * w1;
@@ -169,17 +170,17 @@ static void NfcFilterCreate3(struct NfcFilter3 *nfc, const float w0, const float
     b_11 = B[3][1] * r * r;
     g_1 = 1.0f + b_10 + b_11;
 
-    nfc->g /= g_1;
-    nfc->coeffs[0] /= g_1;
-    nfc->coeffs[3+1] = ((2.0f * b_10) + (4.0f * b_11)) / g_1;
-    nfc->coeffs[3+2] = (4.0f * b_11) / g_1;
-    
+    nfc->base_gain /= g_1;
+    nfc->gain /= g_1;
+    nfc->a1 = (2.0f*b_10 + 4.0f*b_11) / g_1;
+    nfc->a2 = 4.0f * b_11 / g_1;
+
     b_00 = B[3][2] * r;
     g_0 = 1.0f + b_00;
 
-    nfc->g /= g_0;
-    nfc->coeffs[0] /= g_0;
-    nfc->coeffs[3+2+1] = (2.0f * b_00) / g_0;
+    nfc->base_gain /= g_0;
+    nfc->gain /= g_0;
+    nfc->a3 = 2.0f * b_00 / g_0;
 }
 
 static void NfcFilterAdjust3(struct NfcFilter3 *nfc, const float w0)
@@ -193,15 +194,15 @@ static void NfcFilterAdjust3(struct NfcFilter3 *nfc, const float w0)
     b_11 = B[3][1] * r * r;
     g_1 = 1.0f + b_10 + b_11;
 
-    nfc->coeffs[0] = nfc->g * g_1;
-    nfc->coeffs[1] = ((2.0f * b_10) + (4.0f * b_11)) / g_1;
-    nfc->coeffs[2] = (4.0f * b_11) / g_1;
+    nfc->gain = nfc->base_gain * g_1;
+    nfc->b1 = (2.0f*b_10 + 4.0f*b_11) / g_1;
+    nfc->b2 = 4.0f * b_11 / g_1;
 
     b_00 = B[3][2] * r;
     g_0 = 1.0f + b_00;
 
-    nfc->coeffs[0] *= g_0;
-    nfc->coeffs[2+1] = (2.0f * b_00) / g_0;
+    nfc->gain *= g_0;
+    nfc->b3 = 2.0f * b_00 / g_0;
 }
 
 
@@ -223,86 +224,83 @@ void NfcFilterAdjust(NfcFilter *nfc, const float w0)
 
 void NfcFilterProcess1(NfcFilter *nfc, float *restrict dst, const float *restrict src, const int count)
 {
-    const float b0 = nfc->first.coeffs[0];
-    const float a0 = nfc->first.coeffs[1];
-    const float a1 = nfc->first.coeffs[2];
-    float z1 = nfc->first.history[0];
+    const float gain = nfc->first.gain;
+    const float b1 = nfc->first.b1;
+    const float a1 = nfc->first.a1;
+    float z1 = nfc->first.z[0];
     int i;
 
+    ASSUME(count > 0);
+
     for(i = 0;i < count;i++)
     {
-        float out = src[i] * b0;
-        float y;
-
-        y = out - (a1*z1);
-        out = y + (a0*z1);
+        float y = src[i]*gain - a1*z1;
+        float out = y + b1*z1;
         z1 += y;
 
         dst[i] = out;
     }
-    nfc->first.history[0] = z1;
+    nfc->first.z[0] = z1;
 }
 
 void NfcFilterProcess2(NfcFilter *nfc, float *restrict dst, const float *restrict src, const int count)
 {
-    const float b0 = nfc->second.coeffs[0];
-    const float a00 = nfc->second.coeffs[1];
-    const float a01 = nfc->second.coeffs[2];
-    const float a10 = nfc->second.coeffs[3];
-    const float a11 = nfc->second.coeffs[4];
-    float z1 = nfc->second.history[0];
-    float z2 = nfc->second.history[1];
+    const float gain = nfc->second.gain;
+    const float b1 = nfc->second.b1;
+    const float b2 = nfc->second.b2;
+    const float a1 = nfc->second.a1;
+    const float a2 = nfc->second.a2;
+    float z1 = nfc->second.z[0];
+    float z2 = nfc->second.z[1];
     int i;
 
+    ASSUME(count > 0);
+
     for(i = 0;i < count;i++)
     {
-        float out = src[i] * b0;
-        float y;
-
-        y = out - (a10*z1) - (a11*z2);
-        out = y + (a00*z1) + (a01*z2);
+        float y = src[i]*gain - a1*z1 - a2*z2;
+        float out = y + b1*z1 + b2*z2;
         z2 += z1;
         z1 += y;
 
         dst[i] = out;
     }
-    nfc->second.history[0] = z1;
-    nfc->second.history[1] = z2;
+    nfc->second.z[0] = z1;
+    nfc->second.z[1] = z2;
 }
 
 void NfcFilterProcess3(NfcFilter *nfc, float *restrict dst, const float *restrict src, const int count)
 {
-    const float b0 = nfc->third.coeffs[0];
-    const float a00 = nfc->third.coeffs[1];
-    const float a01 = nfc->third.coeffs[2];
-    const float a02 = nfc->third.coeffs[3];
-    const float a10 = nfc->third.coeffs[4];
-    const float a11 = nfc->third.coeffs[5];
-    const float a12 = nfc->third.coeffs[6];
-    float z1 = nfc->third.history[0];
-    float z2 = nfc->third.history[1];
-    float z3 = nfc->third.history[2];
+    const float gain = nfc->third.gain;
+    const float b1 = nfc->third.b1;
+    const float b2 = nfc->third.b2;
+    const float b3 = nfc->third.b3;
+    const float a1 = nfc->third.a1;
+    const float a2 = nfc->third.a2;
+    const float a3 = nfc->third.a3;
+    float z1 = nfc->third.z[0];
+    float z2 = nfc->third.z[1];
+    float z3 = nfc->third.z[2];
     int i;
 
+    ASSUME(count > 0);
+
     for(i = 0;i < count;i++)
     {
-        float out = src[i] * b0;
-        float y;
-
-        y = out - (a10*z1) - (a11*z2);
-        out = y + (a00*z1) + (a01*z2);
+        float y = src[i]*gain - a1*z1 - a2*z2;
+        float out = y + b1*z1 + b2*z2;
         z2 += z1;
         z1 += y;
 
-        y = out - (a12*z3);
-        out = y + (a02*z3);
+        y = out - a3*z3;
+        out = y + b3*z3;
         z3 += y;
 
         dst[i] = out;
     }
-    nfc->third.history[0] = z1;
-    nfc->third.history[1] = z2;
-    nfc->third.history[2] = z3;
+    nfc->third.z[0] = z1;
+    nfc->third.z[1] = z2;
+    nfc->third.z[2] = z3;
 }
 
 #if 0 /* Original methods the above are derived from. */

+ 9 - 9
libs/openal-soft/Alc/filters/nfc.h

@@ -2,19 +2,19 @@
 #define FILTER_NFC_H
 
 struct NfcFilter1 {
-    float g;
-    float coeffs[1*2 + 1];
-    float history[1];
+    float base_gain, gain;
+    float b1, a1;
+    float z[1];
 };
 struct NfcFilter2 {
-    float g;
-    float coeffs[2*2 + 1];
-    float history[2];
+    float base_gain, gain;
+    float b1, b2, a1, a2;
+    float z[2];
 };
 struct NfcFilter3 {
-    float g;
-    float coeffs[3*2 + 1];
-    float history[3];
+    float base_gain, gain;
+    float b1, b2, b3, a1, a2, a3;
+    float z[3];
 };
 
 typedef struct NfcFilter {

+ 109 - 0
libs/openal-soft/Alc/filters/splitter.c

@@ -0,0 +1,109 @@
+
+#include "config.h"
+
+#include "splitter.h"
+
+#include "math_defs.h"
+
+
+void bandsplit_init(BandSplitter *splitter, ALfloat f0norm)
+{
+    ALfloat w = f0norm * F_TAU;
+    ALfloat cw = cosf(w);
+    if(cw > FLT_EPSILON)
+        splitter->coeff = (sinf(w) - 1.0f) / cw;
+    else
+        splitter->coeff = cw * -0.5f;
+
+    splitter->lp_z1 = 0.0f;
+    splitter->lp_z2 = 0.0f;
+    splitter->hp_z1 = 0.0f;
+}
+
+void bandsplit_clear(BandSplitter *splitter)
+{
+    splitter->lp_z1 = 0.0f;
+    splitter->lp_z2 = 0.0f;
+    splitter->hp_z1 = 0.0f;
+}
+
+void bandsplit_process(BandSplitter *splitter, ALfloat *restrict hpout, ALfloat *restrict lpout,
+                       const ALfloat *input, ALsizei count)
+{
+    ALfloat lp_coeff, hp_coeff, lp_y, hp_y, d;
+    ALfloat lp_z1, lp_z2, hp_z1;
+    ALsizei i;
+
+    ASSUME(count > 0);
+
+    hp_coeff = splitter->coeff;
+    lp_coeff = splitter->coeff*0.5f + 0.5f;
+    lp_z1 = splitter->lp_z1;
+    lp_z2 = splitter->lp_z2;
+    hp_z1 = splitter->hp_z1;
+    for(i = 0;i < count;i++)
+    {
+        ALfloat in = input[i];
+
+        /* Low-pass sample processing. */
+        d = (in - lp_z1) * lp_coeff;
+        lp_y = lp_z1 + d;
+        lp_z1 = lp_y + d;
+
+        d = (lp_y - lp_z2) * lp_coeff;
+        lp_y = lp_z2 + d;
+        lp_z2 = lp_y + d;
+
+        lpout[i] = lp_y;
+
+        /* All-pass sample processing. */
+        hp_y = in*hp_coeff + hp_z1;
+        hp_z1 = in - hp_y*hp_coeff;
+
+        /* High-pass generated from removing low-passed output. */
+        hpout[i] = hp_y - lp_y;
+    }
+    splitter->lp_z1 = lp_z1;
+    splitter->lp_z2 = lp_z2;
+    splitter->hp_z1 = hp_z1;
+}
+
+
+void splitterap_init(SplitterAllpass *splitter, ALfloat f0norm)
+{
+    ALfloat w = f0norm * 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, in, out;
+    ALfloat z1;
+    ALsizei i;
+
+    ASSUME(count > 0);
+
+    coeff = splitter->coeff;
+    z1 = splitter->z1;
+    for(i = 0;i < count;i++)
+    {
+        in = samples[i];
+
+        out = in*coeff + z1;
+        z1 = in - out*coeff;
+
+        samples[i] = out;
+    }
+    splitter->z1 = z1;
+}

+ 40 - 0
libs/openal-soft/Alc/filters/splitter.h

@@ -0,0 +1,40 @@
+#ifndef FILTER_SPLITTER_H
+#define FILTER_SPLITTER_H
+
+#include "alMain.h"
+
+
+/* Band splitter. Splits a signal into two phase-matching frequency bands. */
+typedef struct BandSplitter {
+    ALfloat coeff;
+    ALfloat lp_z1;
+    ALfloat lp_z2;
+    ALfloat hp_z1;
+} BandSplitter;
+
+void bandsplit_init(BandSplitter *splitter, ALfloat f0norm);
+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 f0norm);
+void splitterap_clear(SplitterAllpass *splitter);
+void splitterap_process(SplitterAllpass *splitter, ALfloat *restrict samples, ALsizei count);
+
+
+typedef struct FrontStablizer {
+    SplitterAllpass APFilter[MAX_OUTPUT_CHANNELS];
+    BandSplitter LFilter, RFilter;
+    alignas(16) ALfloat LSplit[2][BUFFERSIZE];
+    alignas(16) ALfloat RSplit[2][BUFFERSIZE];
+} FrontStablizer;
+
+#endif /* FILTER_SPLITTER_H */

+ 7 - 10
libs/openal-soft/Alc/fpu_modes.h

@@ -7,16 +7,13 @@
 
 
 typedef struct FPUCtl {
-#ifdef HAVE_FENV_H
-    fenv_t flt_env;
-#ifdef _WIN32
-    int round_mode;
-#endif
-#else
-    int state;
-#endif
-#ifdef HAVE_SSE
-    int sse_state;
+#if defined(__GNUC__) && defined(HAVE_SSE)
+    unsigned int sse_state;
+#elif defined(HAVE___CONTROL87_2)
+    unsigned int state;
+    unsigned int sse_state;
+#elif defined(HAVE__CONTROLFP)
+    unsigned int state;
 #endif
 } FPUCtl;
 void SetMixerFPUMode(FPUCtl *ctl);

+ 66 - 130
libs/openal-soft/Alc/helpers.c

@@ -124,6 +124,8 @@ 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 ALint fastf2i(ALfloat f);
+extern inline int float2int(float f);
+extern inline float fast_roundf(float f);
 #ifndef __GNUC__
 #if defined(HAVE_BITSCANFORWARD64_INTRINSIC)
 extern inline int msvc64_ctz64(ALuint64 v);
@@ -136,6 +138,20 @@ extern inline int fallback_ctz64(ALuint64 value);
 #endif
 
 
+#if defined(HAVE_GCC_GET_CPUID) && (defined(__i386__) || defined(__x86_64__) || \
+                                    defined(_M_IX86) || defined(_M_X64))
+typedef unsigned int reg_type;
+static inline void get_cpuid(int f, reg_type *regs)
+{ __get_cpuid(f, &regs[0], &regs[1], &regs[2], &regs[3]); }
+#define CAN_GET_CPUID
+#elif defined(HAVE_CPUID_INTRINSIC) && (defined(__i386__) || defined(__x86_64__) || \
+                                        defined(_M_IX86) || defined(_M_X64))
+typedef int reg_type;
+static inline void get_cpuid(int f, reg_type *regs)
+{ (__cpuid)(regs, f); }
+#define CAN_GET_CPUID
+#endif
+
 int CPUCapFlags = 0;
 
 void FillCPUCaps(int capfilter)
@@ -144,58 +160,13 @@ void FillCPUCaps(int capfilter)
 
 /* FIXME: We really should get this for all available CPUs in case different
  * CPUs have different caps (is that possible on one machine?). */
-#if defined(HAVE_GCC_GET_CPUID) && (defined(__i386__) || defined(__x86_64__) || \
-                                    defined(_M_IX86) || defined(_M_X64))
+#ifdef CAN_GET_CPUID
     union {
-        unsigned int regs[4];
-        char str[sizeof(unsigned int[4])];
-    } cpuinf[3];
-
-    if(!__get_cpuid(0, &cpuinf[0].regs[0], &cpuinf[0].regs[1], &cpuinf[0].regs[2], &cpuinf[0].regs[3]))
-        ERR("Failed to get CPUID\n");
-    else
-    {
-        unsigned int maxfunc = cpuinf[0].regs[0];
-        unsigned int maxextfunc = 0;
-
-        if(__get_cpuid(0x80000000, &cpuinf[0].regs[0], &cpuinf[0].regs[1], &cpuinf[0].regs[2], &cpuinf[0].regs[3]))
-            maxextfunc = cpuinf[0].regs[0];
-        TRACE("Detected max CPUID function: 0x%x (ext. 0x%x)\n", maxfunc, maxextfunc);
-
-        TRACE("Vendor ID: \"%.4s%.4s%.4s\"\n", cpuinf[0].str+4, cpuinf[0].str+12, cpuinf[0].str+8);
-        if(maxextfunc >= 0x80000004 &&
-           __get_cpuid(0x80000002, &cpuinf[0].regs[0], &cpuinf[0].regs[1], &cpuinf[0].regs[2], &cpuinf[0].regs[3]) &&
-           __get_cpuid(0x80000003, &cpuinf[1].regs[0], &cpuinf[1].regs[1], &cpuinf[1].regs[2], &cpuinf[1].regs[3]) &&
-           __get_cpuid(0x80000004, &cpuinf[2].regs[0], &cpuinf[2].regs[1], &cpuinf[2].regs[2], &cpuinf[2].regs[3]))
-            TRACE("Name: \"%.16s%.16s%.16s\"\n", cpuinf[0].str, cpuinf[1].str, cpuinf[2].str);
+        reg_type regs[4];
+        char str[sizeof(reg_type[4])];
+    } cpuinf[3] = {{ { 0, 0, 0, 0 } }};
 
-        if(maxfunc >= 1 &&
-           __get_cpuid(1, &cpuinf[0].regs[0], &cpuinf[0].regs[1], &cpuinf[0].regs[2], &cpuinf[0].regs[3]))
-        {
-            if((cpuinf[0].regs[3]&(1<<25)))
-            {
-                caps |= CPU_CAP_SSE;
-                if((cpuinf[0].regs[3]&(1<<26)))
-                {
-                    caps |= CPU_CAP_SSE2;
-                    if((cpuinf[0].regs[2]&(1<<0)))
-                    {
-                        caps |= CPU_CAP_SSE3;
-                        if((cpuinf[0].regs[2]&(1<<19)))
-                            caps |= CPU_CAP_SSE4_1;
-                    }
-                }
-            }
-        }
-    }
-#elif defined(HAVE_CPUID_INTRINSIC) && (defined(__i386__) || defined(__x86_64__) || \
-                                        defined(_M_IX86) || defined(_M_X64))
-    union {
-        int regs[4];
-        char str[sizeof(int[4])];
-    } cpuinf[3];
-
-    (__cpuid)(cpuinf[0].regs, 0);
+    get_cpuid(0, cpuinf[0].regs);
     if(cpuinf[0].regs[0] == 0)
         ERR("Failed to get CPUID\n");
     else
@@ -203,7 +174,7 @@ void FillCPUCaps(int capfilter)
         unsigned int maxfunc = cpuinf[0].regs[0];
         unsigned int maxextfunc;
 
-        (__cpuid)(cpuinf[0].regs, 0x80000000);
+        get_cpuid(0x80000000, cpuinf[0].regs);
         maxextfunc = cpuinf[0].regs[0];
 
         TRACE("Detected max CPUID function: 0x%x (ext. 0x%x)\n", maxfunc, maxextfunc);
@@ -211,29 +182,23 @@ void FillCPUCaps(int capfilter)
         TRACE("Vendor ID: \"%.4s%.4s%.4s\"\n", cpuinf[0].str+4, cpuinf[0].str+12, cpuinf[0].str+8);
         if(maxextfunc >= 0x80000004)
         {
-            (__cpuid)(cpuinf[0].regs, 0x80000002);
-            (__cpuid)(cpuinf[1].regs, 0x80000003);
-            (__cpuid)(cpuinf[2].regs, 0x80000004);
+            get_cpuid(0x80000002, cpuinf[0].regs);
+            get_cpuid(0x80000003, cpuinf[1].regs);
+            get_cpuid(0x80000004, cpuinf[2].regs);
             TRACE("Name: \"%.16s%.16s%.16s\"\n", cpuinf[0].str, cpuinf[1].str, cpuinf[2].str);
         }
 
         if(maxfunc >= 1)
         {
-            (__cpuid)(cpuinf[0].regs, 1);
+            get_cpuid(1, cpuinf[0].regs);
             if((cpuinf[0].regs[3]&(1<<25)))
-            {
                 caps |= CPU_CAP_SSE;
-                if((cpuinf[0].regs[3]&(1<<26)))
-                {
-                    caps |= CPU_CAP_SSE2;
-                    if((cpuinf[0].regs[2]&(1<<0)))
-                    {
-                        caps |= CPU_CAP_SSE3;
-                        if((cpuinf[0].regs[2]&(1<<19)))
-                            caps |= CPU_CAP_SSE4_1;
-                    }
-                }
-            }
+            if((caps&CPU_CAP_SSE) && (cpuinf[0].regs[3]&(1<<26)))
+                caps |= CPU_CAP_SSE2;
+            if((caps&CPU_CAP_SSE2) && (cpuinf[0].regs[2]&(1<<0)))
+                caps |= CPU_CAP_SSE3;
+            if((caps&CPU_CAP_SSE3) && (cpuinf[0].regs[2]&(1<<19)))
+                caps |= CPU_CAP_SSE4_1;
         }
     }
 #else
@@ -258,22 +223,32 @@ void FillCPUCaps(int capfilter)
         ERR("Failed to open /proc/cpuinfo, cannot check for NEON support\n");
     else
     {
+        al_string features = AL_STRING_INIT_STATIC();
         char buf[256];
+
         while(fgets(buf, sizeof(buf), file) != NULL)
         {
-            size_t len;
-            char *str;
-
             if(strncmp(buf, "Features\t:", 10) != 0)
                 continue;
 
-            len = strlen(buf);
-            while(len > 0 && isspace(buf[len-1]))
-                buf[--len] = 0;
+            alstr_copy_cstr(&features, buf+10);
+            while(VECTOR_BACK(features) != '\n')
+            {
+                if(fgets(buf, sizeof(buf), file) == NULL)
+                    break;
+                alstr_append_cstr(&features, buf);
+            }
+            break;
+        }
+        fclose(file);
+        file = NULL;
 
-            TRACE("Got features string:%s\n", buf+10);
+        if(!alstr_empty(features))
+        {
+            const char *str = alstr_get_cstr(features);
+            while(isspace(str[0])) ++str;
 
-            str = buf;
+            TRACE("Got features string:%s\n", str);
             while((str=strstr(str, "neon")) != NULL)
             {
                 if(isspace(*(str-1)) && (str[4] == 0 || isspace(str[4])))
@@ -281,13 +256,11 @@ void FillCPUCaps(int capfilter)
                     caps |= CPU_CAP_NEON;
                     break;
                 }
-                str++;
+                ++str;
             }
-            break;
         }
 
-        fclose(file);
-        file = NULL;
+        alstr_reset(&features);
     }
 #endif
 
@@ -305,81 +278,44 @@ void FillCPUCaps(int capfilter)
 
 void SetMixerFPUMode(FPUCtl *ctl)
 {
-#ifdef HAVE_FENV_H
-    fegetenv(&ctl->flt_env);
-#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? */
-    if((CPUCapFlags&CPU_CAP_SSE))
-        __asm__ __volatile__("stmxcsr %0" : "=m" (*&ctl->sse_state));
-#endif
-
-#ifdef FE_TOWARDZERO
-    fesetround(FE_TOWARDZERO);
-#endif
 #if defined(__GNUC__) && defined(HAVE_SSE)
     if((CPUCapFlags&CPU_CAP_SSE))
     {
-        int sseState = ctl->sse_state;
-        sseState |= 0x6000; /* set round-to-zero */
+        __asm__ __volatile__("stmxcsr %0" : "=m" (*&ctl->sse_state));
+        unsigned int sseState = ctl->sse_state;
         sseState |= 0x8000; /* set flush-to-zero */
         if((CPUCapFlags&CPU_CAP_SSE2))
             sseState |= 0x0040; /* set denormals-are-zero */
         __asm__ __volatile__("ldmxcsr %0" : : "m" (*&sseState));
     }
-#endif
 
 #elif defined(HAVE___CONTROL87_2)
 
-    int mode;
-    __control87_2(0, 0, &ctl->state, NULL);
-    __control87_2(_RC_CHOP, _MCW_RC, &mode, NULL);
-#ifdef HAVE_SSE
-    if((CPUCapFlags&CPU_CAP_SSE))
-    {
-        __control87_2(0, 0, NULL, &ctl->sse_state);
-        __control87_2(_RC_CHOP|_DN_FLUSH, _MCW_RC|_MCW_DN, NULL, &mode);
-    }
-#endif
+    __control87_2(0, 0, &ctl->state, &ctl->sse_state);
+    _control87(_DN_FLUSH, _MCW_DN);
 
 #elif defined(HAVE__CONTROLFP)
 
     ctl->state = _controlfp(0, 0);
-    (void)_controlfp(_RC_CHOP, _MCW_RC);
+    _controlfp(_DN_FLUSH, _MCW_DN);
 #endif
 }
 
 void RestoreFPUMode(const FPUCtl *ctl)
 {
-#ifdef HAVE_FENV_H
-    fesetenv(&ctl->flt_env);
-#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));
-#endif
 
 #elif defined(HAVE___CONTROL87_2)
 
     int mode;
-    __control87_2(ctl->state, _MCW_RC, &mode, NULL);
-#ifdef HAVE_SSE
-    if((CPUCapFlags&CPU_CAP_SSE))
-        __control87_2(ctl->sse_state, _MCW_RC|_MCW_DN, NULL, &mode);
-#endif
+    __control87_2(ctl->state, _MCW_DN, &mode, NULL);
+    __control87_2(ctl->sse_state, _MCW_DN, NULL, &mode);
 
 #elif defined(HAVE__CONTROLFP)
 
-    _controlfp(ctl->state, _MCW_RC);
+    _controlfp(ctl->state, _MCW_DN);
 #endif
 }
 
@@ -748,13 +684,13 @@ void GetProcBinary(al_string *path, al_string *fname)
     size_t pathlen;
 
 #ifdef __FreeBSD__
-    int mib[4] = { CTL_KERN, KERN_PROC_ARGS, getpid() };
-    if(sysctl(mib, 3, NULL, &pathlen, NULL, 0) == -1)
-        WARN("Failed to sysctl kern.procargs.%d: %s\n", mib[2], strerror(errno));
+    int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };
+    if(sysctl(mib, 4, NULL, &pathlen, NULL, 0) == -1)
+        WARN("Failed to sysctl kern.proc.pathname: %s\n", strerror(errno));
     else
     {
         pathname = malloc(pathlen + 1);
-        sysctl(mib, 3, (void*)pathname, &pathlen, NULL, 0);
+        sysctl(mib, 4, (void*)pathname, &pathlen, NULL, 0);
         pathname[pathlen] = 0;
     }
 #endif
@@ -1169,8 +1105,8 @@ void alstr_copy_range(al_string *str, const al_string_char_type *from, const al_
 void alstr_append_char(al_string *str, const al_string_char_type c)
 {
     size_t len = alstr_length(*str);
-    VECTOR_RESIZE(*str, len, len+2);
-    VECTOR_PUSH_BACK(*str, c);
+    VECTOR_RESIZE(*str, len+1, len+2);
+    VECTOR_BACK(*str) = c;
     VECTOR_ELEM(*str, len+1) = 0;
 }
 

+ 63 - 43
libs/openal-soft/Alc/hrtf.c

@@ -28,9 +28,9 @@
 #include "alMain.h"
 #include "alSource.h"
 #include "alu.h"
-#include "bformatdec.h"
 #include "hrtf.h"
 #include "alconfig.h"
+#include "filters/splitter.h"
 
 #include "compat.h"
 #include "almalloc.h"
@@ -74,31 +74,29 @@ static struct HrtfEntry *LoadedHrtfs = NULL;
 
 
 /* Calculate the elevation index given the polar elevation in radians. This
- * will return an index between 0 and (evcount - 1). Assumes the FPU is in
- * round-to-zero mode.
+ * will return an index between 0 and (evcount - 1).
  */
 static ALsizei CalcEvIndex(ALsizei evcount, ALfloat ev, ALfloat *mu)
 {
     ALsizei idx;
     ev = (F_PI_2+ev) * (evcount-1) / F_PI;
-    idx = mini(fastf2i(ev), evcount-1);
+    idx = float2int(ev);
 
     *mu = ev - idx;
-    return idx;
+    return mini(idx, evcount-1);
 }
 
 /* Calculate the azimuth index given the polar azimuth in radians. This will
- * return an index between 0 and (azcount - 1). Assumes the FPU is in round-to-
- * zero mode.
+ * return an index between 0 and (azcount - 1).
  */
 static ALsizei CalcAzIndex(ALsizei azcount, ALfloat az, ALfloat *mu)
 {
     ALsizei idx;
     az = (F_TAU+az) * azcount / F_TAU;
 
-    idx = fastf2i(az) % azcount;
-    *mu = az - floorf(az);
-    return idx;
+    idx = float2int(az);
+    *mu = az - idx;
+    return idx % azcount;
 }
 
 /* Calculates static HRIR coefficients and delays for the given polar elevation
@@ -160,11 +158,11 @@ void GetHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azimuth,
     /* Calculate the blended HRIR delays. */
     delays[0] = fastf2i(
         Hrtf->delays[idx[0]][0]*blend[0] + Hrtf->delays[idx[1]][0]*blend[1] +
-        Hrtf->delays[idx[2]][0]*blend[2] + Hrtf->delays[idx[3]][0]*blend[3] + 0.5f
+        Hrtf->delays[idx[2]][0]*blend[2] + Hrtf->delays[idx[3]][0]*blend[3]
     );
     delays[1] = fastf2i(
         Hrtf->delays[idx[0]][1]*blend[0] + Hrtf->delays[idx[1]][1]*blend[1] +
-        Hrtf->delays[idx[2]][1]*blend[2] + Hrtf->delays[idx[3]][1]*blend[3] + 0.5f
+        Hrtf->delays[idx[2]][1]*blend[2] + Hrtf->delays[idx[3]][1]*blend[3]
     );
 
     /* Calculate the sample offsets for the HRIR indices. */
@@ -173,6 +171,7 @@ void GetHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azimuth,
     idx[2] *= Hrtf->irSize;
     idx[3] *= Hrtf->irSize;
 
+    ASSUME(Hrtf->irSize >= MIN_IR_SIZE && (Hrtf->irSize%MOD_IR_SIZE) == 0);
     coeffs = ASSUME_ALIGNED(coeffs, 16);
     /* Calculate the blended HRIR coefficients. */
     coeffs[0][0] = PassthruCoeff * (1.0f-dirfact);
@@ -202,13 +201,16 @@ void BuildBFormatHrtf(const struct Hrtf *Hrtf, DirectHrtfState *state, ALsizei N
  */
 #define NUM_BANDS 2
     BandSplitter splitter;
-    ALsizei idx[HRTF_AMBI_MAX_CHANNELS];
+    ALdouble (*tmpres)[HRIR_LENGTH][2];
+    ALsizei *restrict idx;
     ALsizei min_delay = HRTF_HISTORY_LENGTH;
     ALsizei max_delay = 0;
     ALfloat temps[3][HRIR_LENGTH];
-    ALsizei max_length = 0;
+    ALsizei max_length;
     ALsizei i, c, b;
 
+    idx = al_calloc(DEF_ALIGN, AmbiCount*sizeof(*idx));
+
     for(c = 0;c < AmbiCount;c++)
     {
         ALuint evidx, azidx;
@@ -216,48 +218,43 @@ void BuildBFormatHrtf(const struct Hrtf *Hrtf, DirectHrtfState *state, ALsizei N
         ALuint azcount;
 
         /* Calculate elevation index. */
-        evidx = (ALsizei)floorf((F_PI_2 + AmbiPoints[c].Elev) *
-                                (Hrtf->evCount-1)/F_PI + 0.5f);
+        evidx = (ALsizei)((F_PI_2+AmbiPoints[c].Elev) * (Hrtf->evCount-1) / F_PI + 0.5f);
         evidx = clampi(evidx, 0, Hrtf->evCount-1);
 
         azcount = Hrtf->azCount[evidx];
         evoffset = Hrtf->evOffset[evidx];
 
         /* Calculate azimuth index for this elevation. */
-        azidx = (ALsizei)floorf((F_TAU+AmbiPoints[c].Azim) *
-                                azcount/F_TAU + 0.5f) % azcount;
+        azidx = (ALsizei)((F_TAU+AmbiPoints[c].Azim) * azcount / F_TAU + 0.5f) % azcount;
 
         /* Calculate indices for left and right channels. */
         idx[c] = evoffset + azidx;
+
+        min_delay = mini(min_delay, mini(Hrtf->delays[idx[c]][0], Hrtf->delays[idx[c]][1]));
+        max_delay = maxi(max_delay, maxi(Hrtf->delays[idx[c]][0], Hrtf->delays[idx[c]][1]));
     }
 
+    tmpres = al_calloc(16, NumChannels * sizeof(*tmpres));
+
     memset(temps, 0, sizeof(temps));
     bandsplit_init(&splitter, 400.0f / (ALfloat)Hrtf->sampleRate);
     for(c = 0;c < AmbiCount;c++)
     {
         const ALfloat (*fir)[2] = &Hrtf->coeffs[idx[c] * Hrtf->irSize];
-        const ALsizei res_delay = mini(Hrtf->delays[idx[c]][0], Hrtf->delays[idx[c]][1]);
-        ALsizei ldelay = Hrtf->delays[idx[c]][0] - res_delay;
-        ALsizei rdelay = Hrtf->delays[idx[c]][1] - res_delay;
-
-        min_delay = mini(min_delay, res_delay);
-        max_delay = maxi(max_delay, res_delay);
-
-        max_length = maxi(max_length,
-            mini(maxi(ldelay, rdelay) + Hrtf->irSize, HRIR_LENGTH)
-        );
+        ALsizei ldelay = Hrtf->delays[idx[c]][0] - min_delay;
+        ALsizei rdelay = Hrtf->delays[idx[c]][1] - min_delay;
 
         if(NUM_BANDS == 1)
         {
             for(i = 0;i < NumChannels;++i)
             {
-                ALfloat hfgain = AmbiOrderHFGain[(ALsizei)floor(sqrt(i))];
+                ALdouble mult = (ALdouble)AmbiOrderHFGain[(ALsizei)sqrt(i)] * AmbiMatrix[c][i];
                 ALsizei lidx = ldelay, ridx = rdelay;
                 ALsizei j = 0;
                 while(lidx < HRIR_LENGTH && ridx < HRIR_LENGTH && j < Hrtf->irSize)
                 {
-                    state->Chan[i].Coeffs[lidx++][0] += fir[j][0] * AmbiMatrix[c][i] * hfgain;
-                    state->Chan[i].Coeffs[ridx++][1] += fir[j][1] * AmbiMatrix[c][i] * hfgain;
+                    tmpres[i][lidx++][0] += fir[j][0] * mult;
+                    tmpres[i][ridx++][1] += fir[j][1] * mult;
                     j++;
                 }
             }
@@ -273,15 +270,14 @@ void BuildBFormatHrtf(const struct Hrtf *Hrtf, DirectHrtfState *state, ALsizei N
             /* Apply left ear response with delay. */
             for(i = 0;i < NumChannels;++i)
             {
-                ALfloat hfgain = AmbiOrderHFGain[(ALsizei)floor(sqrt(i))];
+                ALfloat hfgain = AmbiOrderHFGain[(ALsizei)sqrt(i)];
                 for(b = 0;b < NUM_BANDS;b++)
                 {
+                    ALdouble mult = AmbiMatrix[c][i] * (ALdouble)((b==0) ? hfgain : 1.0);
                     ALsizei lidx = ldelay;
                     ALsizei j = 0;
                     while(lidx < HRIR_LENGTH)
-                        state->Chan[i].Coeffs[lidx++][0] += temps[b][j++] * AmbiMatrix[c][i] *
-                                                            hfgain;
-                    hfgain = 1.0f;
+                        tmpres[i][lidx++][0] += temps[b][j++] * mult;
                 }
             }
 
@@ -294,25 +290,49 @@ void BuildBFormatHrtf(const struct Hrtf *Hrtf, DirectHrtfState *state, ALsizei N
             /* Apply right ear response with delay. */
             for(i = 0;i < NumChannels;++i)
             {
-                ALfloat hfgain = AmbiOrderHFGain[(ALsizei)floor(sqrt(i))];
+                ALfloat hfgain = AmbiOrderHFGain[(ALsizei)sqrt(i)];
                 for(b = 0;b < NUM_BANDS;b++)
                 {
+                    ALdouble mult = AmbiMatrix[c][i] * (ALdouble)((b==0) ? hfgain : 1.0);
                     ALsizei ridx = rdelay;
                     ALsizei j = 0;
                     while(ridx < HRIR_LENGTH)
-                        state->Chan[i].Coeffs[ridx++][1] += temps[b][j++] * AmbiMatrix[c][i] *
-                                                            hfgain;
-                    hfgain = 1.0f;
+                        tmpres[i][ridx++][1] += temps[b][j++] * mult;
                 }
             }
         }
     }
+
+    for(i = 0;i < NumChannels;++i)
+    {
+        int idx;
+        for(idx = 0;idx < HRIR_LENGTH;idx++)
+        {
+            state->Chan[i].Coeffs[idx][0] = (ALfloat)tmpres[i][idx][0];
+            state->Chan[i].Coeffs[idx][1] = (ALfloat)tmpres[i][idx][1];
+        }
+    }
+    al_free(tmpres);
+    tmpres = NULL;
+    al_free(idx);
+    idx = NULL;
+
+    if(NUM_BANDS == 1)
+        max_length = mini(max_delay-min_delay + Hrtf->irSize, HRIR_LENGTH);
+    else
+    {
+        /* Increase the IR size by 2/3rds to account for the tail generated by
+         * the band-split filter.
+         */
+        const ALsizei irsize = mini(Hrtf->irSize*5/3, HRIR_LENGTH);
+        max_length = mini(max_delay-min_delay + irsize, HRIR_LENGTH);
+    }
     /* Round up to the next IR size multiple. */
     max_length += MOD_IR_SIZE-1;
     max_length -= max_length%MOD_IR_SIZE;
 
-    TRACE("Skipped delay min: %d, max: %d, new FIR length: %d\n", min_delay, max_delay,
-          max_length);
+    TRACE("Skipped delay: %d, max delay: %d, new FIR length: %d\n",
+          min_delay, max_delay-min_delay, max_length);
     state->IrSize = max_length;
 #undef NUM_BANDS
 }
@@ -1014,12 +1034,12 @@ static void AddFileEntry(vector_EnumeratedHrtf *list, const_al_string filename)
             /* Check if this entry has already been added to the list. */
 #define MATCH_ENTRY(i) (loaded_entry == (i)->hrtf)
             VECTOR_FIND_IF(iter, const EnumeratedHrtf, *list, MATCH_ENTRY);
+#undef MATCH_ENTRY
             if(iter != VECTOR_END(*list))
             {
                 TRACE("Skipping duplicate file entry %s\n", alstr_get_cstr(filename));
                 return;
             }
-#undef MATCH_FNAME
 
             break;
         }
@@ -1093,12 +1113,12 @@ static void AddBuiltInEntry(vector_EnumeratedHrtf *list, const_al_string filenam
         {
 #define MATCH_ENTRY(i) (loaded_entry == (i)->hrtf)
             VECTOR_FIND_IF(iter, const EnumeratedHrtf, *list, MATCH_ENTRY);
+#undef MATCH_ENTRY
             if(iter != VECTOR_END(*list))
             {
                 TRACE("Skipping duplicate file entry %s\n", alstr_get_cstr(filename));
                 return;
             }
-#undef MATCH_FNAME
 
             break;
         }

+ 3 - 8
libs/openal-soft/Alc/hrtf.h

@@ -9,12 +9,6 @@
 #include "atomic.h"
 
 
-/* The maximum number of virtual speakers used to generate HRTF coefficients
- * for decoding B-Format.
- */
-#define HRTF_AMBI_MAX_CHANNELS 18
-
-
 #define HRTF_HISTORY_BITS   (6)
 #define HRTF_HISTORY_LENGTH (1<<HRTF_HISTORY_BITS)
 #define HRTF_HISTORY_MASK   (HRTF_HISTORY_LENGTH-1)
@@ -81,8 +75,9 @@ void GetHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azimuth,
 
 /**
  * Produces HRTF filter coefficients for decoding B-Format, given a set of
- * virtual speaker positions and HF/LF matrices for decoding to them. The
- * returned coefficients are ordered and scaled according to the matrices.
+ * virtual speaker positions, a matching decoding matrix, and per-order high-
+ * frequency gains for the decoder. The calculated impulse responses are
+ * ordered and scaled according to the matrix input.
  */
 void BuildBFormatHrtf(const struct Hrtf *Hrtf, DirectHrtfState *state, ALsizei NumChannels, const struct AngularPoint *AmbiPoints, const ALfloat (*restrict AmbiMatrix)[MAX_AMBI_COEFFS], ALsizei AmbiCount, const ALfloat *restrict AmbiOrderHFGain);
 

+ 8 - 0
libs/openal-soft/Alc/inprogext.h

@@ -72,6 +72,14 @@ AL_API void AL_APIENTRY alGetPointervSOFT(ALenum pname, void **values);
 #endif
 #endif
 
+#ifndef AL_SOFT_buffer_layers
+#define AL_SOFT_buffer_layers
+typedef void (AL_APIENTRY*LPALSOURCEQUEUEBUFFERLAYERSSOFT)(ALuint src, ALsizei nb, const ALuint *buffers);
+#ifdef AL_ALEXT_PROTOTYPES
+AL_API void AL_APIENTRY alSourceQueueBufferLayersSOFT(ALuint src, ALsizei nb, const ALuint *buffers);
+#endif
+#endif
+
 #ifdef __cplusplus
 } /* extern "C" */
 #endif

+ 429 - 131
libs/openal-soft/Alc/mastering.c

@@ -5,228 +5,526 @@
 #include "mastering.h"
 #include "alu.h"
 #include "almalloc.h"
+#include "static_assert.h"
 
 
-extern inline ALuint GetCompressorSampleRate(const Compressor *Comp);
+/* These structures assume BUFFERSIZE is a power of 2. */
+static_assert((BUFFERSIZE & (BUFFERSIZE-1)) == 0, "BUFFERSIZE is not a power of 2");
 
-#define RMS_WINDOW_SIZE (1<<7)
-#define RMS_WINDOW_MASK (RMS_WINDOW_SIZE-1)
-#define RMS_VALUE_MAX  (1<<24)
+typedef struct SlidingHold {
+    ALfloat Values[BUFFERSIZE];
+    ALsizei Expiries[BUFFERSIZE];
+    ALsizei LowerIndex;
+    ALsizei UpperIndex;
+    ALsizei Length;
+} SlidingHold;
 
-static_assert(RMS_VALUE_MAX < (UINT_MAX / RMS_WINDOW_SIZE), "RMS_VALUE_MAX is too big");
+/* General topology and basic automation was based on the following paper:
+ *
+ *   D. Giannoulis, M. Massberg and J. D. Reiss,
+ *   "Parameter Automation in a Dynamic Range Compressor,"
+ *   Journal of the Audio Engineering Society, v61 (10), Oct. 2013
+ *
+ * Available (along with supplemental reading) at:
+ *
+ *   http://c4dm.eecs.qmul.ac.uk/audioengineering/compressors/
+ */
+typedef struct Compressor {
+    ALsizei NumChans;
+    ALuint SampleRate;
+
+    struct {
+        ALuint Knee : 1;
+        ALuint Attack : 1;
+        ALuint Release : 1;
+        ALuint PostGain : 1;
+        ALuint Declip : 1;
+    } Auto;
+
+    ALsizei LookAhead;
+
+    ALfloat PreGain;
+    ALfloat PostGain;
+
+    ALfloat Threshold;
+    ALfloat Slope;
+    ALfloat Knee;
+
+    ALfloat Attack;
+    ALfloat Release;
+
+    alignas(16) ALfloat SideChain[2*BUFFERSIZE];
+    alignas(16) ALfloat CrestFactor[BUFFERSIZE];
+
+    SlidingHold *Hold;
+    ALfloat (*Delay)[BUFFERSIZE];
+    ALsizei DelayIndex;
+
+    ALfloat CrestCoeff;
+    ALfloat GainEstimate;
+    ALfloat AdaptCoeff;
+
+    ALfloat LastPeakSq;
+    ALfloat LastRmsSq;
+    ALfloat LastRelease;
+    ALfloat LastAttack;
+    ALfloat LastGainDev;
+} Compressor;
 
 
-/* Multichannel compression is linked via one of two modes:
+/* This sliding hold follows the input level with an instant attack and a
+ * fixed duration hold before an instant release to the next highest level.
+ * It is a sliding window maximum (descending maxima) implementation based on
+ * Richard Harter's ascending minima algorithm available at:
  *
- *   Summed - Absolute sum of all channels.
- *   Maxed  - Absolute maximum of any channel.
+ *   http://www.richardhartersworld.com/cri/2001/slidingmin.html
  */
-static void SumChannels(Compressor *Comp, const ALsizei NumChans, const ALsizei SamplesToDo,
-                        ALfloat (*restrict OutBuffer)[BUFFERSIZE])
+static ALfloat UpdateSlidingHold(SlidingHold *Hold, const ALsizei i, const ALfloat in)
 {
-    ALsizei c, i;
+    const ALsizei mask = BUFFERSIZE - 1;
+    const ALsizei length = Hold->Length;
+    ALfloat *restrict values = Hold->Values;
+    ALsizei *restrict expiries = Hold->Expiries;
+    ALsizei lowerIndex = Hold->LowerIndex;
+    ALsizei upperIndex = Hold->UpperIndex;
 
-    for(i = 0;i < SamplesToDo;i++)
-        Comp->Envelope[i] = 0.0f;
+    if(i >= expiries[upperIndex])
+        upperIndex = (upperIndex + 1) & mask;
 
-    for(c = 0;c < NumChans;c++)
+    if(in >= values[upperIndex])
     {
-        for(i = 0;i < SamplesToDo;i++)
-            Comp->Envelope[i] += OutBuffer[c][i];
+        values[upperIndex] = in;
+        expiries[upperIndex] = i + length;
+        lowerIndex = upperIndex;
+    }
+    else
+    {
+        do {
+            do {
+                if(!(in >= values[lowerIndex]))
+                    goto found_place;
+            } while(lowerIndex--);
+            lowerIndex = mask;
+        } while(1);
+    found_place:
+
+        lowerIndex = (lowerIndex + 1) & mask;
+        values[lowerIndex] = in;
+        expiries[lowerIndex] = i + length;
     }
 
-    for(i = 0;i < SamplesToDo;i++)
-        Comp->Envelope[i] = fabsf(Comp->Envelope[i]);
+    Hold->LowerIndex = lowerIndex;
+    Hold->UpperIndex = upperIndex;
+
+    return values[upperIndex];
+}
+
+static void ShiftSlidingHold(SlidingHold *Hold, const ALsizei n)
+{
+    const ALsizei lowerIndex = Hold->LowerIndex;
+    ALsizei *restrict expiries = Hold->Expiries;
+    ALsizei i = Hold->UpperIndex;
+
+    if(lowerIndex < i)
+    {
+        for(;i < BUFFERSIZE;i++)
+            expiries[i] -= n;
+        i = 0;
+    }
+    for(;i < lowerIndex;i++)
+        expiries[i] -= n;
+
+    expiries[i] -= n;
 }
 
-static void MaxChannels(Compressor *Comp, const ALsizei NumChans, const ALsizei SamplesToDo,
-                        ALfloat (*restrict OutBuffer)[BUFFERSIZE])
+/* Multichannel compression is linked via the absolute maximum of all
+ * channels.
+ */
+static void LinkChannels(Compressor *Comp, const ALsizei SamplesToDo, ALfloat (*restrict OutBuffer)[BUFFERSIZE])
 {
+    const ALsizei index = Comp->LookAhead;
+    const ALsizei numChans = Comp->NumChans;
+    ALfloat *restrict sideChain = Comp->SideChain;
     ALsizei c, i;
 
+    ASSUME(SamplesToDo > 0);
+    ASSUME(numChans > 0);
+
     for(i = 0;i < SamplesToDo;i++)
-        Comp->Envelope[i] = 0.0f;
+        sideChain[index + i] = 0.0f;
 
-    for(c = 0;c < NumChans;c++)
+    for(c = 0;c < numChans;c++)
     {
+        ALsizei offset = index;
         for(i = 0;i < SamplesToDo;i++)
-            Comp->Envelope[i] = maxf(Comp->Envelope[i], fabsf(OutBuffer[c][i]));
+        {
+            sideChain[offset] = maxf(sideChain[offset], fabsf(OutBuffer[c][i]));
+            ++offset;
+        }
     }
 }
 
-/* Envelope detection/sensing can be done via:
- *
- *   RMS  - Rectangular windowed root mean square of linking stage.
- *   Peak - Implicit output from linking stage.
+/* This calculates the squared crest factor of the control signal for the
+ * basic automation of the attack/release times.  As suggested by the paper,
+ * it uses an instantaneous squared peak detector and a squared RMS detector
+ * both with 200ms release times.
  */
-static void RmsDetection(Compressor *Comp, const ALsizei SamplesToDo)
+static void CrestDetector(Compressor *Comp, const ALsizei SamplesToDo)
 {
-    ALuint sum = Comp->RmsSum;
-    ALuint *window = Comp->RmsWindow;
-    ALsizei index = Comp->RmsIndex;
+    const ALfloat a_crest = Comp->CrestCoeff;
+    const ALsizei index = Comp->LookAhead;
+    const ALfloat *restrict sideChain = Comp->SideChain;
+    ALfloat *restrict crestFactor = Comp->CrestFactor;
+    ALfloat y2_peak = Comp->LastPeakSq;
+    ALfloat y2_rms = Comp->LastRmsSq;
     ALsizei i;
 
+    ASSUME(SamplesToDo > 0);
+
     for(i = 0;i < SamplesToDo;i++)
     {
-        ALfloat sig = Comp->Envelope[i];
-
-        sum -= window[index];
-        window[index] = fastf2i(minf(sig * sig * 65536.0f, RMS_VALUE_MAX));
-        sum += window[index];
-        index = (index + 1) & RMS_WINDOW_MASK;
+        ALfloat x_abs = sideChain[index + i];
+        ALfloat x2 = maxf(0.000001f, x_abs * x_abs);
 
-        Comp->Envelope[i] = sqrtf(sum / 65536.0f / RMS_WINDOW_SIZE);
+        y2_peak = maxf(x2, lerp(x2, y2_peak, a_crest));
+        y2_rms = lerp(x2, y2_rms, a_crest);
+        crestFactor[i] = y2_peak / y2_rms;
     }
 
-    Comp->RmsSum = sum;
-    Comp->RmsIndex = index;
+    Comp->LastPeakSq = y2_peak;
+    Comp->LastRmsSq = y2_rms;
 }
 
-/* This isn't a very sophisticated envelope follower, but it gets the job
- * done.  First, it operates at logarithmic scales to keep transitions
- * appropriate for human hearing.  Second, it can apply adaptive (automated)
- * attack/release adjustments based on the signal.
+/* The side-chain starts with a simple peak detector (based on the absolute
+ * value of the incoming signal) and performs most of its operations in the
+ * log domain.
  */
-static void FollowEnvelope(Compressor *Comp, const ALsizei SamplesToDo)
+static void PeakDetector(Compressor *Comp, const ALsizei SamplesToDo)
 {
-    ALfloat attackMin = Comp->AttackMin;
-    ALfloat attackMax = Comp->AttackMax;
-    ALfloat releaseMin = Comp->ReleaseMin;
-    ALfloat releaseMax = Comp->ReleaseMax;
-    ALfloat last = Comp->EnvLast;
+    const ALsizei index = Comp->LookAhead;
+    ALfloat *restrict sideChain = Comp->SideChain;
     ALsizei i;
 
+    ASSUME(SamplesToDo > 0);
+
     for(i = 0;i < SamplesToDo;i++)
     {
-        ALfloat env = maxf(-6.0f, log10f(Comp->Envelope[i]));
-        ALfloat slope = minf(1.0f, fabsf(env - last) / 4.5f);
+        const ALuint offset = index + i;
+        const ALfloat x_abs = sideChain[offset];
 
-        if(env > last)
-            last = minf(env, last + lerp(attackMin, attackMax, 1.0f - (slope * slope)));
-        else
-            last = maxf(env, last + lerp(releaseMin, releaseMax, 1.0f - (slope * slope)));
+        sideChain[offset] = logf(maxf(0.000001f, x_abs));
+    }
+}
 
-        Comp->Envelope[i] = last;
+/* An optional hold can be used to extend the peak detector so it can more
+ * solidly detect fast transients.  This is best used when operating as a
+ * limiter.
+ */
+static void PeakHoldDetector(Compressor *Comp, const ALsizei SamplesToDo)
+{
+    const ALsizei index = Comp->LookAhead;
+    ALfloat *restrict sideChain = Comp->SideChain;
+    SlidingHold *hold = Comp->Hold;
+    ALsizei i;
+
+    ASSUME(SamplesToDo > 0);
+
+    for(i = 0;i < SamplesToDo;i++)
+    {
+        const ALsizei offset = index + i;
+        const ALfloat x_abs = sideChain[offset];
+        const ALfloat x_G = logf(maxf(0.000001f, x_abs));
+
+        sideChain[offset] = UpdateSlidingHold(hold, i, x_G);
     }
 
-    Comp->EnvLast = last;
+    ShiftSlidingHold(hold, SamplesToDo);
 }
 
-/* The envelope is converted to control gain with an optional soft knee. */
-static void EnvelopeGain(Compressor *Comp, const ALsizei SamplesToDo, const ALfloat Slope)
+/* This is the heart of the feed-forward compressor.  It operates in the log
+ * domain (to better match human hearing) and can apply some basic automation
+ * to knee width, attack/release times, make-up/post gain, and clipping
+ * reduction.
+ */
+static void GainCompressor(Compressor *Comp, const ALsizei SamplesToDo)
 {
+    const bool autoKnee = Comp->Auto.Knee;
+    const bool autoAttack = Comp->Auto.Attack;
+    const bool autoRelease = Comp->Auto.Release;
+    const bool autoPostGain = Comp->Auto.PostGain;
+    const bool autoDeclip = Comp->Auto.Declip;
+    const ALsizei lookAhead = Comp->LookAhead;
     const ALfloat threshold = Comp->Threshold;
-    const ALfloat knee = Comp->Knee;
+    const ALfloat slope = Comp->Slope;
+    const ALfloat attack = Comp->Attack;
+    const ALfloat release = Comp->Release;
+    const ALfloat c_est = Comp->GainEstimate;
+    const ALfloat a_adp = Comp->AdaptCoeff;
+    const ALfloat *restrict crestFactor = Comp->CrestFactor;
+    ALfloat *restrict sideChain = Comp->SideChain;
+    ALfloat postGain = Comp->PostGain;
+    ALfloat knee = Comp->Knee;
+    ALfloat t_att = attack;
+    ALfloat t_rel = release - attack;
+    ALfloat a_att = expf(-1.0f / t_att);
+    ALfloat a_rel = expf(-1.0f / t_rel);
+    ALfloat y_1 = Comp->LastRelease;
+    ALfloat y_L = Comp->LastAttack;
+    ALfloat c_dev = Comp->LastGainDev;
     ALsizei i;
 
-    if(!(knee > 0.0f))
+    ASSUME(SamplesToDo > 0);
+
+    for(i = 0;i < SamplesToDo;i++)
     {
-        for(i = 0;i < SamplesToDo;i++)
+        const ALfloat y2_crest = crestFactor[i];
+        const ALfloat x_G = sideChain[lookAhead + i];
+        const ALfloat x_over = x_G - threshold;
+        ALfloat knee_h;
+        ALfloat y_G;
+        ALfloat x_L;
+
+        if(autoKnee)
+            knee = maxf(0.0f, 2.5f * (c_dev + c_est));
+        knee_h = 0.5f * knee;
+
+        /* This is the gain computer.  It applies a static compression curve
+         * to the control signal.
+         */
+        if(x_over <= -knee_h)
+            y_G = 0.0f;
+        else if(fabsf(x_over) < knee_h)
+            y_G = (x_over + knee_h) * (x_over + knee_h) / (2.0f * knee);
+        else
+            y_G = x_over;
+
+        x_L = -slope * y_G;
+
+        if(autoAttack)
         {
-            ALfloat gain = Slope * (threshold - Comp->Envelope[i]);
-            Comp->Envelope[i] = powf(10.0f, minf(0.0f, gain));
+            t_att = 2.0f * attack / y2_crest;
+            a_att = expf(-1.0f / t_att);
         }
+
+        if(autoRelease)
+        {
+            t_rel = 2.0f * release / y2_crest - t_att;
+            a_rel = expf(-1.0f / t_rel);
+        }
+
+        /* Gain smoothing (ballistics) is done via a smooth decoupled peak
+         * detector.  The attack time is subtracted from the release time
+         * above to compensate for the chained operating mode.
+         */
+        y_1 = maxf(x_L, lerp(x_L, y_1, a_rel));
+        y_L = lerp(y_1, y_L, a_att);
+
+        /* Knee width and make-up gain automation make use of a smoothed
+         * measurement of deviation between the control signal and estimate.
+         * The estimate is also used to bias the measurement to hot-start its
+         * average.
+         */
+        c_dev = lerp(-y_L - c_est, c_dev, a_adp);
+
+        if(autoPostGain)
+        {
+            /* Clipping reduction is only viable when make-up gain is being
+             * automated.  It modifies the deviation to further attenuate the
+             * control signal when clipping is detected.  The adaptation
+             * time is sufficiently long enough to suppress further clipping
+             * at the same output level.
+             */
+            if(autoDeclip)
+                c_dev = maxf(c_dev, sideChain[i] - y_L - threshold - c_est);
+
+            postGain = -(c_dev + c_est);
+        }
+
+        sideChain[i] = expf(postGain - y_L);
     }
-    else
-    {
-        const ALfloat lower = threshold - (0.5f * knee);
-        const ALfloat upper = threshold + (0.5f * knee);
-        const ALfloat m = 0.5f * Slope / knee;
 
+    Comp->LastRelease = y_1;
+    Comp->LastAttack = y_L;
+    Comp->LastGainDev = c_dev;
+}
+
+/* Combined with the hold time, a look-ahead delay can improve handling of
+ * fast transients by allowing the envelope time to converge prior to
+ * reaching the offending impulse.  This is best used when operating as a
+ * limiter.
+ */
+static void SignalDelay(Compressor *Comp, const ALsizei SamplesToDo, ALfloat (*restrict OutBuffer)[BUFFERSIZE])
+{
+    const ALsizei mask = BUFFERSIZE - 1;
+    const ALsizei numChans = Comp->NumChans;
+    const ALsizei indexIn = Comp->DelayIndex;
+    const ALsizei indexOut = Comp->DelayIndex - Comp->LookAhead;
+    ALfloat (*restrict delay)[BUFFERSIZE] = Comp->Delay;
+    ALsizei c, i;
+
+    ASSUME(SamplesToDo > 0);
+    ASSUME(numChans > 0);
+
+    for(c = 0;c < numChans;c++)
+    {
         for(i = 0;i < SamplesToDo;i++)
         {
-            ALfloat env = Comp->Envelope[i];
-            ALfloat gain;
-
-            if(env > lower && env < upper)
-                gain = m * (env - lower) * (lower - env);
-            else
-                gain = Slope * (threshold - env);
+            ALfloat sig = OutBuffer[c][i];
 
-            Comp->Envelope[i] = powf(10.0f, minf(0.0f, gain));
+            OutBuffer[c][i] = delay[c][(indexOut + i) & mask];
+            delay[c][(indexIn + i) & mask] = sig;
         }
     }
-}
 
+    Comp->DelayIndex = (indexIn + SamplesToDo) & mask;
+}
 
-Compressor *CompressorInit(const ALfloat PreGainDb, const ALfloat PostGainDb,
-                           const ALboolean SummedLink, const ALboolean RmsSensing,
-                           const ALfloat AttackTimeMin, const ALfloat AttackTimeMax,
-                           const ALfloat ReleaseTimeMin, const ALfloat ReleaseTimeMax,
-                           const ALfloat Ratio, const ALfloat ThresholdDb,
-                           const ALfloat KneeDb, const ALuint SampleRate)
+/* The compressor is initialized with the following settings:
+ *
+ *   NumChans       - Number of channels to process.
+ *   SampleRate     - Sample rate to process.
+ *   AutoKnee       - Whether to automate the knee width parameter.
+ *   AutoAttack     - Whether to automate the attack time parameter.
+ *   AutoRelease    - Whether to automate the release time parameter.
+ *   AutoPostGain   - Whether to automate the make-up (post) gain parameter.
+ *   AutoDeclip     - Whether to automate clipping reduction.  Ignored when
+ *                    not automating make-up gain.
+ *   LookAheadTime  - Look-ahead time (in seconds).
+ *   HoldTime       - Peak hold-time (in seconds).
+ *   PreGainDb      - Gain applied before detection (in dB).
+ *   PostGainDb     - Make-up gain applied after compression (in dB).
+ *   ThresholdDb    - Triggering threshold (in dB).
+ *   Ratio          - Compression ratio (x:1).  Set to INFINITY for true
+ *                    limiting.  Ignored when automating knee width.
+ *   KneeDb         - Knee width (in dB).  Ignored when automating knee
+ *                    width.
+ *   AttackTimeMin  - Attack time (in seconds).  Acts as a maximum when
+ *                    automating attack time.
+ *   ReleaseTimeMin - Release time (in seconds).  Acts as a maximum when
+ *                    automating release time.
+ */
+Compressor* CompressorInit(const ALsizei NumChans, const ALuint SampleRate,
+                           const ALboolean AutoKnee, const ALboolean AutoAttack,
+                           const ALboolean AutoRelease, const ALboolean AutoPostGain,
+                           const ALboolean AutoDeclip, const ALfloat LookAheadTime,
+                           const ALfloat HoldTime, const ALfloat PreGainDb,
+                           const ALfloat PostGainDb, const ALfloat ThresholdDb,
+                           const ALfloat Ratio, const ALfloat KneeDb,
+                           const ALfloat AttackTime, const ALfloat ReleaseTime)
 {
     Compressor *Comp;
+    ALsizei lookAhead;
+    ALsizei hold;
     size_t size;
-    ALsizei i;
+
+    lookAhead = (ALsizei)clampf(roundf(LookAheadTime*SampleRate), 0.0f, BUFFERSIZE-1);
+    hold = (ALsizei)clampf(roundf(HoldTime*SampleRate), 0.0f, BUFFERSIZE-1);
+    /* The sliding hold implementation doesn't handle a length of 1. A 1-sample
+     * hold is useless anyway, it would only ever give back what was just given
+     * to it.
+     */
+    if(hold == 1)
+        hold = 0;
 
     size = sizeof(*Comp);
-    if(RmsSensing)
-        size += sizeof(Comp->RmsWindow[0]) * RMS_WINDOW_SIZE;
-    Comp = al_calloc(16, size);
+    if(lookAhead > 0)
+    {
+        size += sizeof(*Comp->Delay) * NumChans;
+        if(hold > 0)
+            size += sizeof(*Comp->Hold);
+    }
 
-    Comp->PreGain = powf(10.0f, PreGainDb / 20.0f);
-    Comp->PostGain = powf(10.0f, PostGainDb / 20.0f);
-    Comp->SummedLink = SummedLink;
-    Comp->AttackMin = 1.0f / maxf(0.000001f, AttackTimeMin * SampleRate * logf(10.0f));
-    Comp->AttackMax = 1.0f / maxf(0.000001f, AttackTimeMax * SampleRate * logf(10.0f));
-    Comp->ReleaseMin = -1.0f / maxf(0.000001f, ReleaseTimeMin * SampleRate * logf(10.0f));
-    Comp->ReleaseMax = -1.0f / maxf(0.000001f, ReleaseTimeMax * SampleRate * logf(10.0f));
-    Comp->Ratio = Ratio;
-    Comp->Threshold = ThresholdDb / 20.0f;
-    Comp->Knee = maxf(0.0f, KneeDb / 20.0f);
+    Comp = al_calloc(16, size);
+    Comp->NumChans = NumChans;
     Comp->SampleRate = SampleRate;
+    Comp->Auto.Knee = AutoKnee;
+    Comp->Auto.Attack = AutoAttack;
+    Comp->Auto.Release = AutoRelease;
+    Comp->Auto.PostGain = AutoPostGain;
+    Comp->Auto.Declip = AutoPostGain && AutoDeclip;
+    Comp->LookAhead = lookAhead;
+    Comp->PreGain = powf(10.0f, PreGainDb / 20.0f);
+    Comp->PostGain = PostGainDb * logf(10.0f) / 20.0f;
+    Comp->Threshold = ThresholdDb * logf(10.0f) / 20.0f;
+    Comp->Slope = 1.0f / maxf(1.0f, Ratio) - 1.0f;
+    Comp->Knee = maxf(0.0f, KneeDb * logf(10.0f) / 20.0f);
+    Comp->Attack = maxf(1.0f, AttackTime * SampleRate);
+    Comp->Release = maxf(1.0f, ReleaseTime * SampleRate);
+
+    /* Knee width automation actually treats the compressor as a limiter.  By
+     * varying the knee width, it can effectively be seen as applying
+     * compression over a wide range of ratios.
+     */
+    if(AutoKnee)
+        Comp->Slope = -1.0f;
+
+    if(lookAhead > 0)
+    {
+        if(hold > 0)
+        {
+            Comp->Hold = (SlidingHold*)(Comp + 1);
+            Comp->Hold->Values[0] = -INFINITY;
+            Comp->Hold->Expiries[0] = hold;
+            Comp->Hold->Length = hold;
+            Comp->Delay = (ALfloat(*)[])(Comp->Hold + 1);
+        }
+        else
+        {
+            Comp->Delay = (ALfloat(*)[])(Comp + 1);
+        }
+    }
 
-    Comp->RmsSum = 0;
-    if(RmsSensing)
-        Comp->RmsWindow = (ALuint*)(Comp+1);
-    else
-        Comp->RmsWindow = NULL;
-    Comp->RmsIndex = 0;
-
-    for(i = 0;i < BUFFERSIZE;i++)
-        Comp->Envelope[i] = 0.0f;
-    Comp->EnvLast = -6.0f;
+    Comp->CrestCoeff = expf(-1.0f / (0.200f * SampleRate)); // 200ms
+    Comp->GainEstimate = Comp->Threshold * -0.5f * Comp->Slope;
+    Comp->AdaptCoeff = expf(-1.0f / (2.0f * SampleRate)); // 2s
 
     return Comp;
 }
 
-void ApplyCompression(Compressor *Comp, const ALsizei NumChans, const ALsizei SamplesToDo,
-                      ALfloat (*restrict OutBuffer)[BUFFERSIZE])
+void ApplyCompression(Compressor *Comp, const ALsizei SamplesToDo, ALfloat (*restrict OutBuffer)[BUFFERSIZE])
 {
+    const ALsizei numChans = Comp->NumChans;
+    const ALfloat preGain = Comp->PreGain;
+    ALfloat *restrict sideChain;
     ALsizei c, i;
 
-    if(Comp->PreGain != 1.0f)
+    ASSUME(SamplesToDo > 0);
+    ASSUME(numChans > 0);
+
+    if(preGain != 1.0f)
     {
-        for(c = 0;c < NumChans;c++)
+        for(c = 0;c < numChans;c++)
         {
             for(i = 0;i < SamplesToDo;i++)
-                OutBuffer[c][i] *= Comp->PreGain;
+                OutBuffer[c][i] *= preGain;
         }
     }
 
-    if(Comp->SummedLink)
-        SumChannels(Comp, NumChans, SamplesToDo, OutBuffer);
-    else
-        MaxChannels(Comp, NumChans, SamplesToDo, OutBuffer);
+    LinkChannels(Comp, SamplesToDo, OutBuffer);
 
-    if(Comp->RmsWindow)
-        RmsDetection(Comp, SamplesToDo);
-    FollowEnvelope(Comp, SamplesToDo);
+    if(Comp->Auto.Attack || Comp->Auto.Release)
+        CrestDetector(Comp, SamplesToDo);
 
-    if(Comp->Ratio > 0.0f)
-        EnvelopeGain(Comp, SamplesToDo, 1.0f - (1.0f / Comp->Ratio));
+    if(Comp->Hold)
+        PeakHoldDetector(Comp, SamplesToDo);
     else
-        EnvelopeGain(Comp, SamplesToDo, 1.0f);
+        PeakDetector(Comp, SamplesToDo);
 
-    if(Comp->PostGain != 1.0f)
-    {
-        for(i = 0;i < SamplesToDo;i++)
-            Comp->Envelope[i] *= Comp->PostGain;
-    }
-    for(c = 0;c < NumChans;c++)
+    GainCompressor(Comp, SamplesToDo);
+
+    if(Comp->Delay)
+        SignalDelay(Comp, SamplesToDo, OutBuffer);
+
+    sideChain = Comp->SideChain;
+    for(c = 0;c < numChans;c++)
     {
         for(i = 0;i < SamplesToDo;i++)
-            OutBuffer[c][i] *= Comp->Envelope[i];
+            OutBuffer[c][i] *= sideChain[i];
     }
+
+    memmove(sideChain, sideChain+SamplesToDo, Comp->LookAhead*sizeof(ALfloat));
 }
+
+
+ALsizei GetCompressorLookAhead(const Compressor *Comp)
+{ return Comp->LookAhead; }

+ 32 - 40
libs/openal-soft/Alc/mastering.h

@@ -6,52 +6,44 @@
 /* For BUFFERSIZE. */
 #include "alMain.h"
 
-typedef struct Compressor {
-    ALfloat PreGain;
-    ALfloat PostGain;
-    ALboolean SummedLink;
-    ALfloat AttackMin;
-    ALfloat AttackMax;
-    ALfloat ReleaseMin;
-    ALfloat ReleaseMax;
-    ALfloat Ratio;
-    ALfloat Threshold;
-    ALfloat Knee;
-    ALuint SampleRate;
+struct Compressor;
 
-    ALuint RmsSum;
-    ALuint *RmsWindow;
-    ALsizei RmsIndex;
-    ALfloat Envelope[BUFFERSIZE];
-    ALfloat EnvLast;
-} Compressor;
-
-/* The compressor requires the following information for proper
- * initialization:
+/* The compressor is initialized with the following settings:
  *
+ *   NumChans       - Number of channels to process.
+ *   SampleRate     - Sample rate to process.
+ *   AutoKnee       - Whether to automate the knee width parameter.
+ *   AutoAttack     - Whether to automate the attack time parameter.
+ *   AutoRelease    - Whether to automate the release time parameter.
+ *   AutoPostGain   - Whether to automate the make-up (post) gain parameter.
+ *   AutoDeclip     - Whether to automate clipping reduction.  Ignored when
+ *                    not automating make-up gain.
+ *   LookAheadTime  - Look-ahead time (in seconds).
+ *   HoldTime       - Peak hold-time (in seconds).
  *   PreGainDb      - Gain applied before detection (in dB).
- *   PostGainDb     - Gain applied after compression (in dB).
- *   SummedLink     - Whether to use summed (true) or maxed (false) linking.
- *   RmsSensing     - Whether to use RMS (true) or Peak (false) sensing.
- *   AttackTimeMin  - Minimum attack time (in seconds).
- *   AttackTimeMax  - Maximum attack time.  Automates when min != max.
- *   ReleaseTimeMin - Minimum release time (in seconds).
- *   ReleaseTimeMax - Maximum release time.  Automates when min != max.
- *   Ratio          - Compression ratio (x:1).  Set to 0 for true limiter.
+ *   PostGainDb     - Make-up gain applied after compression (in dB).
  *   ThresholdDb    - Triggering threshold (in dB).
- *   KneeDb         - Knee width (below threshold; in dB).
- *   SampleRate     - Sample rate to process.
+ *   Ratio          - Compression ratio (x:1).  Set to INFINIFTY for true
+ *                    limiting.  Ignored when automating knee width.
+ *   KneeDb         - Knee width (in dB).  Ignored when automating knee
+ *                    width.
+ *   AttackTimeMin  - Attack time (in seconds).  Acts as a maximum when
+ *                    automating attack time.
+ *   ReleaseTimeMin - Release time (in seconds).  Acts as a maximum when
+ *                    automating release time.
  */
-Compressor *CompressorInit(const ALfloat PreGainDb, const ALfloat PostGainDb,
-    const ALboolean SummedLink, const ALboolean RmsSensing, const ALfloat AttackTimeMin,
-    const ALfloat AttackTimeMax, const ALfloat ReleaseTimeMin, const ALfloat ReleaseTimeMax,
-    const ALfloat Ratio, const ALfloat ThresholdDb, const ALfloat KneeDb,
-    const ALuint SampleRate);
-
-void ApplyCompression(struct Compressor *Comp, const ALsizei NumChans, const ALsizei SamplesToDo,
+struct Compressor* CompressorInit(const ALsizei NumChans, const ALuint SampleRate,
+    const ALboolean AutoKnee, const ALboolean AutoAttack,
+    const ALboolean AutoRelease, const ALboolean AutoPostGain,
+    const ALboolean AutoDeclip, const ALfloat LookAheadTime,
+    const ALfloat HoldTime, const ALfloat PreGainDb,
+    const ALfloat PostGainDb, const ALfloat ThresholdDb,
+    const ALfloat Ratio, const ALfloat KneeDb,
+    const ALfloat AttackTime, const ALfloat ReleaseTime);
+
+void ApplyCompression(struct Compressor *Comp, const ALsizei SamplesToDo,
                       ALfloat (*restrict OutBuffer)[BUFFERSIZE]);
 
-inline ALuint GetCompressorSampleRate(const Compressor *Comp)
-{ return Comp->SampleRate; }
+ALsizei GetCompressorLookAhead(const struct Compressor *Comp);
 
 #endif /* MASTERING_H */

+ 1 - 1
libs/openal-soft/Alc/mixer/defs.h

@@ -62,7 +62,7 @@ void MixRow_SSE(ALfloat *OutBuffer, const ALfloat *Gains,
                 ALsizei InPos, ALsizei BufferSize);
 
 /* SSE resamplers */
-inline void InitiatePositionArrays(ALsizei frac, ALint increment, ALsizei *restrict frac_arr, ALint *restrict pos_arr, ALsizei size)
+inline void InitiatePositionArrays(ALsizei frac, ALint increment, ALsizei *restrict frac_arr, ALsizei *restrict pos_arr, ALsizei size)
 {
     ALsizei i;
 

+ 31 - 17
libs/openal-soft/Alc/mixer/hrtf_inc.c

@@ -22,18 +22,24 @@ void MixHrtf(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
 {
     const ALfloat (*Coeffs)[2] = ASSUME_ALIGNED(hrtfparams->Coeffs, 16);
     const ALsizei Delay[2] = { hrtfparams->Delay[0], hrtfparams->Delay[1] };
-    ALfloat gainstep = hrtfparams->GainStep;
-    ALfloat gain = hrtfparams->Gain;
+    const ALfloat gainstep = hrtfparams->GainStep;
+    const ALfloat gain = hrtfparams->Gain;
+    ALfloat g, stepcount = 0.0f;
     ALfloat left, right;
     ALsizei i;
 
+    ASSUME(IrSize >= 4);
+    ASSUME(BufferSize > 0);
+
     LeftOut  += OutPos;
     RightOut += OutPos;
     for(i = 0;i < BufferSize;i++)
     {
         hrtfstate->History[Offset&HRTF_HISTORY_MASK] = *(data++);
-        left = hrtfstate->History[(Offset-Delay[0])&HRTF_HISTORY_MASK]*gain;
-        right = hrtfstate->History[(Offset-Delay[1])&HRTF_HISTORY_MASK]*gain;
+
+        g = gain + gainstep*stepcount;
+        left = hrtfstate->History[(Offset-Delay[0])&HRTF_HISTORY_MASK]*g;
+        right = hrtfstate->History[(Offset-Delay[1])&HRTF_HISTORY_MASK]*g;
 
         hrtfstate->Values[(Offset+IrSize-1)&HRIR_MASK][0] = 0.0f;
         hrtfstate->Values[(Offset+IrSize-1)&HRIR_MASK][1] = 0.0f;
@@ -42,10 +48,10 @@ void MixHrtf(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
         *(LeftOut++)  += hrtfstate->Values[Offset&HRIR_MASK][0];
         *(RightOut++) += hrtfstate->Values[Offset&HRIR_MASK][1];
 
-        gain += gainstep;
+        stepcount += 1.0f;
         Offset++;
     }
-    hrtfparams->Gain = gain;
+    hrtfparams->Gain = gain + gainstep*stepcount;
 }
 
 void MixHrtfBlend(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
@@ -56,15 +62,19 @@ void MixHrtfBlend(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
 {
     const ALfloat (*OldCoeffs)[2] = ASSUME_ALIGNED(oldparams->Coeffs, 16);
     const ALsizei OldDelay[2] = { oldparams->Delay[0], oldparams->Delay[1] };
-    ALfloat oldGain = oldparams->Gain;
-    ALfloat oldGainStep = -oldGain / (ALfloat)BufferSize;
+    const ALfloat oldGain = oldparams->Gain;
+    const ALfloat oldGainStep = -oldGain / (ALfloat)BufferSize;
     const ALfloat (*NewCoeffs)[2] = ASSUME_ALIGNED(newparams->Coeffs, 16);
     const ALsizei NewDelay[2] = { newparams->Delay[0], newparams->Delay[1] };
-    ALfloat newGain = newparams->Gain;
-    ALfloat newGainStep = newparams->GainStep;
+    const ALfloat newGain = newparams->Gain;
+    const ALfloat newGainStep = newparams->GainStep;
+    ALfloat g, stepcount = 0.0f;
     ALfloat left, right;
     ALsizei i;
 
+    ASSUME(IrSize >= 4);
+    ASSUME(BufferSize > 0);
+
     LeftOut  += OutPos;
     RightOut += OutPos;
     for(i = 0;i < BufferSize;i++)
@@ -74,22 +84,23 @@ void MixHrtfBlend(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
 
         hrtfstate->History[Offset&HRTF_HISTORY_MASK] = *(data++);
 
-        left = hrtfstate->History[(Offset-OldDelay[0])&HRTF_HISTORY_MASK]*oldGain;
-        right = hrtfstate->History[(Offset-OldDelay[1])&HRTF_HISTORY_MASK]*oldGain;
+        g = oldGain + oldGainStep*stepcount;
+        left = hrtfstate->History[(Offset-OldDelay[0])&HRTF_HISTORY_MASK]*g;
+        right = hrtfstate->History[(Offset-OldDelay[1])&HRTF_HISTORY_MASK]*g;
         ApplyCoeffs(Offset, hrtfstate->Values, IrSize, OldCoeffs, left, right);
 
-        left = hrtfstate->History[(Offset-NewDelay[0])&HRTF_HISTORY_MASK]*newGain;
-        right = hrtfstate->History[(Offset-NewDelay[1])&HRTF_HISTORY_MASK]*newGain;
+        g = newGain + newGainStep*stepcount;
+        left = hrtfstate->History[(Offset-NewDelay[0])&HRTF_HISTORY_MASK]*g;
+        right = hrtfstate->History[(Offset-NewDelay[1])&HRTF_HISTORY_MASK]*g;
         ApplyCoeffs(Offset, hrtfstate->Values, IrSize, NewCoeffs, left, right);
 
         *(LeftOut++)  += hrtfstate->Values[Offset&HRIR_MASK][0];
         *(RightOut++) += hrtfstate->Values[Offset&HRIR_MASK][1];
 
-        oldGain += oldGainStep;
-        newGain += newGainStep;
+        stepcount += 1.0f;
         Offset++;
     }
-    newparams->Gain = newGain;
+    newparams->Gain = newGain + newGainStep*stepcount;
 }
 
 void MixDirectHrtf(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
@@ -100,6 +111,9 @@ void MixDirectHrtf(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
     ALfloat insample;
     ALsizei i;
 
+    ASSUME(IrSize >= 4);
+    ASSUME(BufferSize > 0);
+
     for(i = 0;i < BufferSize;i++)
     {
         Values[(Offset+IrSize)&HRIR_MASK][0] = 0.0f;

+ 51 - 52
libs/openal-soft/Alc/mixer/mixer_c.c

@@ -9,12 +9,37 @@
 #include "defs.h"
 
 
-static inline ALfloat do_point(const ALfloat *restrict vals, ALsizei UNUSED(frac))
+static inline ALfloat do_point(const InterpState* UNUSED(state), const ALfloat *restrict vals, ALsizei UNUSED(frac))
 { return vals[0]; }
-static inline ALfloat do_lerp(const ALfloat *restrict vals, ALsizei frac)
+static inline ALfloat do_lerp(const InterpState* UNUSED(state), const ALfloat *restrict vals, ALsizei frac)
 { return lerp(vals[0], vals[1], frac * (1.0f/FRACTIONONE)); }
-static inline ALfloat do_cubic(const ALfloat *restrict vals, ALsizei frac)
+static inline ALfloat do_cubic(const InterpState* UNUSED(state), const ALfloat *restrict vals, ALsizei frac)
 { return cubic(vals[0], vals[1], vals[2], vals[3], frac * (1.0f/FRACTIONONE)); }
+static inline ALfloat do_bsinc(const InterpState *state, const ALfloat *restrict vals, ALsizei frac)
+{
+    const ALfloat *fil, *scd, *phd, *spd;
+    ALsizei j_f, pi;
+    ALfloat pf, r;
+
+    ASSUME(state->bsinc.m > 0);
+
+    // Calculate the phase index and factor.
+#define FRAC_PHASE_BITDIFF (FRACTIONBITS-BSINC_PHASE_BITS)
+    pi = frac >> FRAC_PHASE_BITDIFF;
+    pf = (frac & ((1<<FRAC_PHASE_BITDIFF)-1)) * (1.0f/(1<<FRAC_PHASE_BITDIFF));
+#undef FRAC_PHASE_BITDIFF
+
+    fil = ASSUME_ALIGNED(state->bsinc.filter + state->bsinc.m*pi*4, 16);
+    scd = ASSUME_ALIGNED(fil + state->bsinc.m, 16);
+    phd = ASSUME_ALIGNED(scd + state->bsinc.m, 16);
+    spd = ASSUME_ALIGNED(phd + state->bsinc.m, 16);
+
+    // Apply the scale and phase interpolated filter.
+    r = 0.0f;
+    for(j_f = 0;j_f < state->bsinc.m;j_f++)
+        r += (fil[j_f] + state->bsinc.sf*scd[j_f] + pf*(phd[j_f] + state->bsinc.sf*spd[j_f])) * vals[j_f];
+    return r;
+}
 
 const ALfloat *Resample_copy_C(const InterpState* UNUSED(state),
   const ALfloat *restrict src, ALsizei UNUSED(frac), ALint UNUSED(increment),
@@ -30,16 +55,19 @@ const ALfloat *Resample_copy_C(const InterpState* UNUSED(state),
 }
 
 #define DECL_TEMPLATE(Tag, Sampler, O)                                        \
-const ALfloat *Resample_##Tag##_C(const InterpState* UNUSED(state),           \
+const ALfloat *Resample_##Tag##_C(const InterpState *state,                   \
   const ALfloat *restrict src, ALsizei frac, ALint increment,                 \
   ALfloat *restrict dst, ALsizei numsamples)                                  \
 {                                                                             \
+    const InterpState istate = *state;                                        \
     ALsizei i;                                                                \
                                                                               \
+    ASSUME(numsamples > 0);                                                   \
+                                                                              \
     src -= O;                                                                 \
     for(i = 0;i < numsamples;i++)                                             \
     {                                                                         \
-        dst[i] = Sampler(src, frac);                                          \
+        dst[i] = Sampler(&istate, src, frac);                                 \
                                                                               \
         frac += increment;                                                    \
         src  += frac>>FRACTIONBITS;                                           \
@@ -51,47 +79,10 @@ const ALfloat *Resample_##Tag##_C(const InterpState* UNUSED(state),           \
 DECL_TEMPLATE(point, do_point, 0)
 DECL_TEMPLATE(lerp, do_lerp, 0)
 DECL_TEMPLATE(cubic, do_cubic, 1)
+DECL_TEMPLATE(bsinc, do_bsinc, istate.bsinc.l)
 
 #undef DECL_TEMPLATE
 
-const ALfloat *Resample_bsinc_C(const InterpState *state, const ALfloat *restrict src,
-                                ALsizei frac, ALint increment, ALfloat *restrict dst,
-                                ALsizei dstlen)
-{
-    const ALfloat *fil, *scd, *phd, *spd;
-    const ALfloat *const filter = state->bsinc.filter;
-    const ALfloat sf = state->bsinc.sf;
-    const ALsizei m = state->bsinc.m;
-    ALsizei j_f, pi, i;
-    ALfloat pf, r;
-
-    src += state->bsinc.l;
-    for(i = 0;i < dstlen;i++)
-    {
-        // Calculate the phase index and factor.
-#define FRAC_PHASE_BITDIFF (FRACTIONBITS-BSINC_PHASE_BITS)
-        pi = frac >> FRAC_PHASE_BITDIFF;
-        pf = (frac & ((1<<FRAC_PHASE_BITDIFF)-1)) * (1.0f/(1<<FRAC_PHASE_BITDIFF));
-#undef FRAC_PHASE_BITDIFF
-
-        fil = ASSUME_ALIGNED(filter + m*pi*4, 16);
-        scd = ASSUME_ALIGNED(fil + m, 16);
-        phd = ASSUME_ALIGNED(scd + m, 16);
-        spd = ASSUME_ALIGNED(phd + m, 16);
-
-        // Apply the scale and phase interpolated filter.
-        r = 0.0f;
-        for(j_f = 0;j_f < m;j_f++)
-            r += (fil[j_f] + sf*scd[j_f] + pf*(phd[j_f] + sf*spd[j_f])) * src[j_f];
-        dst[i] = r;
-
-        frac += increment;
-        src  += frac>>FRACTIONBITS;
-        frac &= FRACTIONMASK;
-    }
-    return dst;
-}
-
 
 static inline void ApplyCoeffs(ALsizei Offset, ALfloat (*restrict Values)[2],
                                const ALsizei IrSize,
@@ -111,33 +102,38 @@ static inline void ApplyCoeffs(ALsizei Offset, ALfloat (*restrict Values)[2],
 #define MixHrtfBlend MixHrtfBlend_C
 #define MixDirectHrtf MixDirectHrtf_C
 #include "hrtf_inc.c"
-#undef MixHrtf
 
 
 void Mix_C(const ALfloat *data, ALsizei OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
            ALfloat *CurrentGains, const ALfloat *TargetGains, ALsizei Counter, ALsizei OutPos,
            ALsizei BufferSize)
 {
-    ALfloat gain, delta, step;
+    const ALfloat delta = (Counter > 0) ? 1.0f/(ALfloat)Counter : 0.0f;
     ALsizei c;
 
-    delta = (Counter > 0) ? 1.0f/(ALfloat)Counter : 0.0f;
+    ASSUME(OutChans > 0);
+    ASSUME(BufferSize > 0);
 
     for(c = 0;c < OutChans;c++)
     {
         ALsizei pos = 0;
-        gain = CurrentGains[c];
-        step = (TargetGains[c] - gain) * delta;
-        if(fabsf(step) > FLT_EPSILON)
+        ALfloat gain = CurrentGains[c];
+        const ALfloat diff = TargetGains[c] - gain;
+
+        if(fabsf(diff) > FLT_EPSILON)
         {
             ALsizei minsize = mini(BufferSize, Counter);
+            const ALfloat step = diff * delta;
+            ALfloat step_count = 0.0f;
             for(;pos < minsize;pos++)
             {
-                OutBuffer[c][OutPos+pos] += data[pos]*gain;
-                gain += step;
+                OutBuffer[c][OutPos+pos] += data[pos] * (gain + step*step_count);
+                step_count += 1.0f;
             }
             if(pos == Counter)
                 gain = TargetGains[c];
+            else
+                gain += step*step_count;
             CurrentGains[c] = gain;
         }
 
@@ -158,9 +154,12 @@ void MixRow_C(ALfloat *OutBuffer, const ALfloat *Gains, const ALfloat (*restrict
 {
     ALsizei c, i;
 
+    ASSUME(InChans > 0);
+    ASSUME(BufferSize > 0);
+
     for(c = 0;c < InChans;c++)
     {
-        ALfloat gain = Gains[c];
+        const ALfloat gain = Gains[c];
         if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
             continue;
 

+ 92 - 70
libs/openal-soft/Alc/mixer/mixer_neon.c

@@ -17,21 +17,25 @@ const ALfloat *Resample_lerp_Neon(const InterpState* UNUSED(state),
     const int32x4_t increment4 = vdupq_n_s32(increment*4);
     const float32x4_t fracOne4 = vdupq_n_f32(1.0f/FRACTIONONE);
     const int32x4_t fracMask4 = vdupq_n_s32(FRACTIONMASK);
-    alignas(16) ALint pos_[4];
-    alignas(16) ALsizei frac_[4];
-    int32x4_t pos4;
-    int32x4_t frac4;
-    ALsizei i;
+    alignas(16) ALsizei pos_[4], frac_[4];
+    int32x4_t pos4, frac4;
+    ALsizei todo, pos, i;
 
-    InitiatePositionArrays(frac, increment, frac_, pos_, 4);
+    ASSUME(numsamples > 0);
 
+    InitiatePositionArrays(frac, increment, frac_, pos_, 4);
     frac4 = vld1q_s32(frac_);
     pos4 = vld1q_s32(pos_);
 
-    for(i = 0;numsamples-i > 3;i += 4)
+    todo = numsamples & ~3;
+    for(i = 0;i < todo;i += 4)
     {
-        const float32x4_t val1 = (float32x4_t){src[pos_[0]], src[pos_[1]], src[pos_[2]], src[pos_[3]]};
-        const float32x4_t val2 = (float32x4_t){src[pos_[0]+1], src[pos_[1]+1], src[pos_[2]+1], src[pos_[3]+1]};
+        const int pos0 = vgetq_lane_s32(pos4, 0);
+        const int pos1 = vgetq_lane_s32(pos4, 1);
+        const int pos2 = vgetq_lane_s32(pos4, 2);
+        const int pos3 = vgetq_lane_s32(pos4, 3);
+        const float32x4_t val1 = (float32x4_t){src[pos0], src[pos1], src[pos2], src[pos3]};
+        const float32x4_t val2 = (float32x4_t){src[pos0+1], src[pos1+1], src[pos2+1], src[pos3+1]};
 
         /* val1 + (val2-val1)*mu */
         const float32x4_t r0 = vsubq_f32(val2, val1);
@@ -43,25 +47,21 @@ const ALfloat *Resample_lerp_Neon(const InterpState* UNUSED(state),
         frac4 = vaddq_s32(frac4, increment4);
         pos4 = vaddq_s32(pos4, vshrq_n_s32(frac4, FRACTIONBITS));
         frac4 = vandq_s32(frac4, fracMask4);
-
-        vst1q_s32(pos_, pos4);
     }
 
-    if(i < numsamples)
+    /* NOTE: These four elements represent the position *after* the last four
+     * samples, so the lowest element is the next position to resample.
+     */
+    pos = vgetq_lane_s32(pos4, 0);
+    frac = vgetq_lane_s32(frac4, 0);
+
+    for(;i < numsamples;++i)
     {
-        /* NOTE: These four elements represent the position *after* the last
-         * four samples, so the lowest element is the next position to
-         * resample.
-         */
-        ALint pos = pos_[0];
-        frac = vgetq_lane_s32(frac4, 0);
-        do {
-            dst[i] = lerp(src[pos], src[pos+1], frac * (1.0f/FRACTIONONE));
-
-            frac += increment;
-            pos  += frac>>FRACTIONBITS;
-            frac &= FRACTIONMASK;
-        } while(++i < numsamples);
+        dst[i] = lerp(src[pos], src[pos+1], frac * (1.0f/FRACTIONONE));
+
+        frac += increment;
+        pos  += frac>>FRACTIONBITS;
+        frac &= FRACTIONMASK;
     }
     return dst;
 }
@@ -78,7 +78,10 @@ const ALfloat *Resample_bsinc_Neon(const InterpState *state,
     float32x4_t r4;
     ALfloat pf;
 
-    src += state->bsinc.l;
+    ASSUME(m > 0);
+    ASSUME(dstlen > 0);
+
+    src -= state->bsinc.l;
     for(i = 0;i < dstlen;i++)
     {
         // Calculate the phase index and factor.
@@ -96,16 +99,20 @@ const ALfloat *Resample_bsinc_Neon(const InterpState *state,
         // Apply the scale and phase interpolated filter.
         r4 = vdupq_n_f32(0.0f);
         {
+            const ALsizei count = m >> 2;
             const float32x4_t pf4 = vdupq_n_f32(pf);
-            for(j = 0;j < m;j+=4,fil++,scd++,phd++,spd++)
+
+            ASSUME(count > 0);
+
+            for(j = 0;j < count;j++)
             {
                 /* f = ((fil + sf*scd) + pf*(phd + sf*spd)) */
                 const float32x4_t f4 = vmlaq_f32(
-                    vmlaq_f32(*fil, sf4, *scd),
-                    pf4, vmlaq_f32(*phd, sf4, *spd)
+                    vmlaq_f32(fil[j], sf4, scd[j]),
+                    pf4, vmlaq_f32(phd[j], sf4, spd[j])
                 );
                 /* r += f*src */
-                r4 = vmlaq_f32(r4, f4, vld1q_f32(&src[j]));
+                r4 = vmlaq_f32(r4, f4, vld1q_f32(&src[j*4]));
             }
         }
         r4 = vaddq_f32(r4, vcombine_f32(vrev64_f32(vget_high_f32(r4)),
@@ -154,61 +161,69 @@ static inline void ApplyCoeffs(ALsizei Offset, ALfloat (*restrict Values)[2],
 #define MixHrtfBlend MixHrtfBlend_Neon
 #define MixDirectHrtf MixDirectHrtf_Neon
 #include "hrtf_inc.c"
-#undef MixHrtf
 
 
 void Mix_Neon(const ALfloat *data, ALsizei OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
               ALfloat *CurrentGains, const ALfloat *TargetGains, ALsizei Counter, ALsizei OutPos,
               ALsizei BufferSize)
 {
-    ALfloat gain, delta, step;
-    float32x4_t gain4;
+    const ALfloat delta = (Counter > 0) ? 1.0f/(ALfloat)Counter : 0.0f;
     ALsizei c;
 
+    ASSUME(OutChans > 0);
+    ASSUME(BufferSize > 0);
     data = ASSUME_ALIGNED(data, 16);
     OutBuffer = ASSUME_ALIGNED(OutBuffer, 16);
 
-    delta = (Counter > 0) ? 1.0f/(ALfloat)Counter : 0.0f;
-
     for(c = 0;c < OutChans;c++)
     {
         ALsizei pos = 0;
-        gain = CurrentGains[c];
-        step = (TargetGains[c] - gain) * delta;
-        if(fabsf(step) > FLT_EPSILON)
+        ALfloat gain = CurrentGains[c];
+        const ALfloat diff = TargetGains[c] - gain;
+
+        if(fabsf(diff) > FLT_EPSILON)
         {
             ALsizei minsize = mini(BufferSize, Counter);
+            const ALfloat step = diff * delta;
+            ALfloat step_count = 0.0f;
             /* Mix with applying gain steps in aligned multiples of 4. */
-            if(minsize-pos > 3)
+            if(LIKELY(minsize > 3))
             {
-                float32x4_t step4;
-                gain4 = vsetq_lane_f32(gain, gain4, 0);
-                gain4 = vsetq_lane_f32(gain + step, gain4, 1);
-                gain4 = vsetq_lane_f32(gain + step + step, gain4, 2);
-                gain4 = vsetq_lane_f32(gain + step + step + step, gain4, 3);
-                step4 = vdupq_n_f32(step + step + step + step);
+                const float32x4_t four4 = vdupq_n_f32(4.0f);
+                const float32x4_t step4 = vdupq_n_f32(step);
+                const float32x4_t gain4 = vdupq_n_f32(gain);
+                float32x4_t step_count4 = vsetq_lane_f32(0.0f,
+                    vsetq_lane_f32(1.0f,
+                    vsetq_lane_f32(2.0f,
+                    vsetq_lane_f32(3.0f, vdupq_n_f32(0.0f), 3),
+                    2), 1), 0
+                );
+                ALsizei todo = minsize >> 2;
+
                 do {
                     const float32x4_t val4 = vld1q_f32(&data[pos]);
                     float32x4_t dry4 = vld1q_f32(&OutBuffer[c][OutPos+pos]);
-                    dry4 = vmlaq_f32(dry4, val4, gain4);
-                    gain4 = vaddq_f32(gain4, step4);
+                    dry4 = vmlaq_f32(dry4, val4, vmlaq_f32(gain4, step4, step_count4));
+                    step_count4 = vaddq_f32(step_count4, four4);
                     vst1q_f32(&OutBuffer[c][OutPos+pos], dry4);
                     pos += 4;
-                } while(minsize-pos > 3);
-                /* NOTE: gain4 now represents the next four gains after the
-                 * last four mixed samples, so the lowest element represents
-                 * the next gain to apply.
+                } while(--todo);
+                /* NOTE: step_count4 now represents the next four counts after
+                 * the last four mixed samples, so the lowest element
+                 * represents the next step count to apply.
                  */
-                gain = vgetq_lane_f32(gain4, 0);
+                step_count = vgetq_lane_f32(step_count4, 0);
             }
             /* Mix with applying left over gain steps that aren't aligned multiples of 4. */
             for(;pos < minsize;pos++)
             {
-                OutBuffer[c][OutPos+pos] += data[pos]*gain;
-                gain += step;
+                OutBuffer[c][OutPos+pos] += data[pos]*(gain + step*step_count);
+                step_count += 1.0f;
             }
             if(pos == Counter)
                 gain = TargetGains[c];
+            else
+                gain += step*step_count;
             CurrentGains[c] = gain;
 
             /* Mix until pos is aligned with 4 or the mix is done. */
@@ -219,13 +234,17 @@ void Mix_Neon(const ALfloat *data, ALsizei OutChans, ALfloat (*restrict OutBuffe
 
         if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
             continue;
-        gain4 = vdupq_n_f32(gain);
-        for(;BufferSize-pos > 3;pos += 4)
+        if(LIKELY(BufferSize-pos > 3))
         {
-            const float32x4_t val4 = vld1q_f32(&data[pos]);
-            float32x4_t dry4 = vld1q_f32(&OutBuffer[c][OutPos+pos]);
-            dry4 = vmlaq_f32(dry4, val4, gain4);
-            vst1q_f32(&OutBuffer[c][OutPos+pos], dry4);
+            ALsizei todo = (BufferSize-pos) >> 2;
+            const float32x4_t gain4 = vdupq_n_f32(gain);
+            do {
+                const float32x4_t val4 = vld1q_f32(&data[pos]);
+                float32x4_t dry4 = vld1q_f32(&OutBuffer[c][OutPos+pos]);
+                dry4 = vmlaq_f32(dry4, val4, gain4);
+                vst1q_f32(&OutBuffer[c][OutPos+pos], dry4);
+                pos += 4;
+            } while(--todo);
         }
         for(;pos < BufferSize;pos++)
             OutBuffer[c][OutPos+pos] += data[pos]*gain;
@@ -234,26 +253,29 @@ void Mix_Neon(const ALfloat *data, ALsizei OutChans, ALfloat (*restrict OutBuffe
 
 void MixRow_Neon(ALfloat *OutBuffer, const ALfloat *Gains, const ALfloat (*restrict data)[BUFFERSIZE], ALsizei InChans, ALsizei InPos, ALsizei BufferSize)
 {
-    float32x4_t gain4;
     ALsizei c;
 
-    data = ASSUME_ALIGNED(data, 16);
-    OutBuffer = ASSUME_ALIGNED(OutBuffer, 16);
+    ASSUME(InChans > 0);
+    ASSUME(BufferSize > 0);
 
     for(c = 0;c < InChans;c++)
     {
         ALsizei pos = 0;
-        ALfloat gain = Gains[c];
+        const ALfloat gain = Gains[c];
         if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
             continue;
 
-        gain4 = vdupq_n_f32(gain);
-        for(;BufferSize-pos > 3;pos += 4)
+        if(LIKELY(BufferSize > 3))
         {
-            const float32x4_t val4 = vld1q_f32(&data[c][InPos+pos]);
-            float32x4_t dry4 = vld1q_f32(&OutBuffer[pos]);
-            dry4 = vmlaq_f32(dry4, val4, gain4);
-            vst1q_f32(&OutBuffer[pos], dry4);
+            ALsizei todo = BufferSize >> 2;
+            float32x4_t gain4 = vdupq_n_f32(gain);
+            do {
+                const float32x4_t val4 = vld1q_f32(&data[c][InPos+pos]);
+                float32x4_t dry4 = vld1q_f32(&OutBuffer[pos]);
+                dry4 = vmlaq_f32(dry4, val4, gain4);
+                vst1q_f32(&OutBuffer[pos], dry4);
+                pos += 4;
+            } while(--todo);
         }
         for(;pos < BufferSize;pos++)
             OutBuffer[pos] += data[c][InPos+pos]*gain;

+ 65 - 44
libs/openal-soft/Alc/mixer/mixer_sse.c

@@ -24,7 +24,10 @@ const ALfloat *Resample_bsinc_SSE(const InterpState *state, const ALfloat *restr
     ALfloat pf;
     __m128 r4;
 
-    src += state->bsinc.l;
+    ASSUME(m > 0);
+    ASSUME(dstlen > 0);
+
+    src -= state->bsinc.l;
     for(i = 0;i < dstlen;i++)
     {
         // Calculate the phase index and factor.
@@ -42,17 +45,21 @@ const ALfloat *Resample_bsinc_SSE(const InterpState *state, const ALfloat *restr
         // Apply the scale and phase interpolated filter.
         r4 = _mm_setzero_ps();
         {
+            const ALsizei count = m >> 2;
             const __m128 pf4 = _mm_set1_ps(pf);
+
+            ASSUME(count > 0);
+
 #define MLA4(x, y, z) _mm_add_ps(x, _mm_mul_ps(y, z))
-            for(j = 0;j < m;j+=4,fil++,scd++,phd++,spd++)
+            for(j = 0;j < count;j++)
             {
                 /* f = ((fil + sf*scd) + pf*(phd + sf*spd)) */
                 const __m128 f4 = MLA4(
-                    MLA4(*fil, sf4, *scd),
-                    pf4, MLA4(*phd, sf4, *spd)
+                    MLA4(fil[j], sf4, scd[j]),
+                    pf4, MLA4(phd[j], sf4, spd[j])
                 );
                 /* r += f*src */
-                r4 = MLA4(r4, f4, _mm_loadu_ps(&src[j]));
+                r4 = MLA4(r4, f4, _mm_loadu_ps(&src[j*4]));
             }
 #undef MLA4
         }
@@ -126,60 +133,64 @@ static inline void ApplyCoeffs(ALsizei Offset, ALfloat (*restrict Values)[2],
 #define MixHrtfBlend MixHrtfBlend_SSE
 #define MixDirectHrtf MixDirectHrtf_SSE
 #include "hrtf_inc.c"
-#undef MixHrtf
 
 
 void Mix_SSE(const ALfloat *data, ALsizei OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
              ALfloat *CurrentGains, const ALfloat *TargetGains, ALsizei Counter, ALsizei OutPos,
              ALsizei BufferSize)
 {
-    ALfloat gain, delta, step;
-    __m128 gain4;
+    const ALfloat delta = (Counter > 0) ? 1.0f/(ALfloat)Counter : 0.0f;
     ALsizei c;
 
-    delta = (Counter > 0) ? 1.0f/(ALfloat)Counter : 0.0f;
+    ASSUME(OutChans > 0);
+    ASSUME(BufferSize > 0);
 
     for(c = 0;c < OutChans;c++)
     {
         ALsizei pos = 0;
-        gain = CurrentGains[c];
-        step = (TargetGains[c] - gain) * delta;
-        if(fabsf(step) > FLT_EPSILON)
+        ALfloat gain = CurrentGains[c];
+        const ALfloat diff = TargetGains[c] - gain;
+
+        if(fabsf(diff) > FLT_EPSILON)
         {
             ALsizei minsize = mini(BufferSize, Counter);
+            const ALfloat step = diff * delta;
+            ALfloat step_count = 0.0f;
             /* Mix with applying gain steps in aligned multiples of 4. */
-            if(minsize-pos > 3)
+            if(LIKELY(minsize > 3))
             {
-                __m128 step4;
-                gain4 = _mm_setr_ps(
-                    gain,
-                    gain + step,
-                    gain + step + step,
-                    gain + step + step + step
-                );
-                step4 = _mm_set1_ps(step + step + step + step);
+                const __m128 four4 = _mm_set1_ps(4.0f);
+                const __m128 step4 = _mm_set1_ps(step);
+                const __m128 gain4 = _mm_set1_ps(gain);
+                __m128 step_count4 = _mm_setr_ps(0.0f, 1.0f, 2.0f, 3.0f);
+                ALsizei todo = minsize >> 2;
                 do {
                     const __m128 val4 = _mm_load_ps(&data[pos]);
                     __m128 dry4 = _mm_load_ps(&OutBuffer[c][OutPos+pos]);
-                    dry4 = _mm_add_ps(dry4, _mm_mul_ps(val4, gain4));
-                    gain4 = _mm_add_ps(gain4, step4);
+#define MLA4(x, y, z) _mm_add_ps(x, _mm_mul_ps(y, z))
+                    /* dry += val * (gain + step*step_count) */
+                    dry4 = MLA4(dry4, val4, MLA4(gain4, step4, step_count4));
+#undef MLA4
                     _mm_store_ps(&OutBuffer[c][OutPos+pos], dry4);
+                    step_count4 = _mm_add_ps(step_count4, four4);
                     pos += 4;
-                } while(minsize-pos > 3);
-                /* NOTE: gain4 now represents the next four gains after the
-                 * last four mixed samples, so the lowest element represents
-                 * the next gain to apply.
+                } while(--todo);
+                /* NOTE: step_count4 now represents the next four counts after
+                 * the last four mixed samples, so the lowest element
+                 * represents the next step count to apply.
                  */
-                gain = _mm_cvtss_f32(gain4);
+                step_count = _mm_cvtss_f32(step_count4);
             }
             /* Mix with applying left over gain steps that aren't aligned multiples of 4. */
             for(;pos < minsize;pos++)
             {
-                OutBuffer[c][OutPos+pos] += data[pos]*gain;
-                gain += step;
+                OutBuffer[c][OutPos+pos] += data[pos]*(gain + step*step_count);
+                step_count += 1.0f;
             }
             if(pos == Counter)
                 gain = TargetGains[c];
+            else
+                gain += step*step_count;
             CurrentGains[c] = gain;
 
             /* Mix until pos is aligned with 4 or the mix is done. */
@@ -190,13 +201,17 @@ void Mix_SSE(const ALfloat *data, ALsizei OutChans, ALfloat (*restrict OutBuffer
 
         if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
             continue;
-        gain4 = _mm_set1_ps(gain);
-        for(;BufferSize-pos > 3;pos += 4)
+        if(LIKELY(BufferSize-pos > 3))
         {
-            const __m128 val4 = _mm_load_ps(&data[pos]);
-            __m128 dry4 = _mm_load_ps(&OutBuffer[c][OutPos+pos]);
-            dry4 = _mm_add_ps(dry4, _mm_mul_ps(val4, gain4));
-            _mm_store_ps(&OutBuffer[c][OutPos+pos], dry4);
+            ALsizei todo = (BufferSize-pos) >> 2;
+            const __m128 gain4 = _mm_set1_ps(gain);
+            do {
+                const __m128 val4 = _mm_load_ps(&data[pos]);
+                __m128 dry4 = _mm_load_ps(&OutBuffer[c][OutPos+pos]);
+                dry4 = _mm_add_ps(dry4, _mm_mul_ps(val4, gain4));
+                _mm_store_ps(&OutBuffer[c][OutPos+pos], dry4);
+                pos += 4;
+            } while(--todo);
         }
         for(;pos < BufferSize;pos++)
             OutBuffer[c][OutPos+pos] += data[pos]*gain;
@@ -205,23 +220,29 @@ void Mix_SSE(const ALfloat *data, ALsizei OutChans, ALfloat (*restrict OutBuffer
 
 void MixRow_SSE(ALfloat *OutBuffer, const ALfloat *Gains, const ALfloat (*restrict data)[BUFFERSIZE], ALsizei InChans, ALsizei InPos, ALsizei BufferSize)
 {
-    __m128 gain4;
     ALsizei c;
 
+    ASSUME(InChans > 0);
+    ASSUME(BufferSize > 0);
+
     for(c = 0;c < InChans;c++)
     {
         ALsizei pos = 0;
-        ALfloat gain = Gains[c];
+        const ALfloat gain = Gains[c];
         if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
             continue;
 
-        gain4 = _mm_set1_ps(gain);
-        for(;BufferSize-pos > 3;pos += 4)
+        if(LIKELY(BufferSize > 3))
         {
-            const __m128 val4 = _mm_load_ps(&data[c][InPos+pos]);
-            __m128 dry4 = _mm_load_ps(&OutBuffer[pos]);
-            dry4 = _mm_add_ps(dry4, _mm_mul_ps(val4, gain4));
-            _mm_store_ps(&OutBuffer[pos], dry4);
+            ALsizei todo = BufferSize >> 2;
+            const __m128 gain4 = _mm_set1_ps(gain);
+            do {
+                const __m128 val4 = _mm_load_ps(&data[c][InPos+pos]);
+                __m128 dry4 = _mm_load_ps(&OutBuffer[pos]);
+                dry4 = _mm_add_ps(dry4, _mm_mul_ps(val4, gain4));
+                _mm_store_ps(&OutBuffer[pos], dry4);
+                pos += 4;
+            } while(--todo);
         }
         for(;pos < BufferSize;pos++)
             OutBuffer[pos] += data[c][InPos+pos]*gain;

+ 16 - 14
libs/openal-soft/Alc/mixer/mixer_sse2.c

@@ -34,21 +34,25 @@ const ALfloat *Resample_lerp_SSE2(const InterpState* UNUSED(state),
     const __m128i increment4 = _mm_set1_epi32(increment*4);
     const __m128 fracOne4 = _mm_set1_ps(1.0f/FRACTIONONE);
     const __m128i fracMask4 = _mm_set1_epi32(FRACTIONMASK);
-    union { alignas(16) ALint i[4]; float f[4]; } pos_;
-    union { alignas(16) ALsizei i[4]; float f[4]; } frac_;
+    alignas(16) ALsizei pos_[4], frac_[4];
     __m128i frac4, pos4;
-    ALint pos;
-    ALsizei i;
+    ALsizei todo, pos, i;
 
-    InitiatePositionArrays(frac, increment, frac_.i, pos_.i, 4);
+    ASSUME(numsamples > 0);
 
-    frac4 = _mm_castps_si128(_mm_load_ps(frac_.f));
-    pos4 = _mm_castps_si128(_mm_load_ps(pos_.f));
+    InitiatePositionArrays(frac, increment, frac_, pos_, 4);
+    frac4 = _mm_setr_epi32(frac_[0], frac_[1], frac_[2], frac_[3]);
+    pos4 = _mm_setr_epi32(pos_[0], pos_[1], pos_[2], pos_[3]);
 
-    for(i = 0;numsamples-i > 3;i += 4)
+    todo = numsamples & ~3;
+    for(i = 0;i < todo;i += 4)
     {
-        const __m128 val1 = _mm_setr_ps(src[pos_.i[0]], src[pos_.i[1]], src[pos_.i[2]], src[pos_.i[3]]);
-        const __m128 val2 = _mm_setr_ps(src[pos_.i[0]+1], src[pos_.i[1]+1], src[pos_.i[2]+1], src[pos_.i[3]+1]);
+        const int pos0 = _mm_cvtsi128_si32(_mm_shuffle_epi32(pos4, _MM_SHUFFLE(0, 0, 0, 0)));
+        const int pos1 = _mm_cvtsi128_si32(_mm_shuffle_epi32(pos4, _MM_SHUFFLE(1, 1, 1, 1)));
+        const int pos2 = _mm_cvtsi128_si32(_mm_shuffle_epi32(pos4, _MM_SHUFFLE(2, 2, 2, 2)));
+        const int pos3 = _mm_cvtsi128_si32(_mm_shuffle_epi32(pos4, _MM_SHUFFLE(3, 3, 3, 3)));
+        const __m128 val1 = _mm_setr_ps(src[pos0  ], src[pos1  ], src[pos2  ], src[pos3  ]);
+        const __m128 val2 = _mm_setr_ps(src[pos0+1], src[pos1+1], src[pos2+1], src[pos3+1]);
 
         /* val1 + (val2-val1)*mu */
         const __m128 r0 = _mm_sub_ps(val2, val1);
@@ -60,17 +64,15 @@ const ALfloat *Resample_lerp_SSE2(const InterpState* UNUSED(state),
         frac4 = _mm_add_epi32(frac4, increment4);
         pos4 = _mm_add_epi32(pos4, _mm_srli_epi32(frac4, FRACTIONBITS));
         frac4 = _mm_and_si128(frac4, fracMask4);
-
-        _mm_store_ps(pos_.f, _mm_castsi128_ps(pos4));
     }
 
     /* NOTE: These four elements represent the position *after* the last four
      * samples, so the lowest element is the next position to resample.
      */
-    pos = pos_.i[0];
+    pos = _mm_cvtsi128_si32(pos4);
     frac = _mm_cvtsi128_si32(frac4);
 
-    for(;i < numsamples;i++)
+    for(;i < numsamples;++i)
     {
         dst[i] = lerp(src[pos], src[pos+1], frac * (1.0f/FRACTIONONE));
 

+ 16 - 17
libs/openal-soft/Alc/mixer/mixer_sse41.c

@@ -35,21 +35,25 @@ const ALfloat *Resample_lerp_SSE41(const InterpState* UNUSED(state),
     const __m128i increment4 = _mm_set1_epi32(increment*4);
     const __m128 fracOne4 = _mm_set1_ps(1.0f/FRACTIONONE);
     const __m128i fracMask4 = _mm_set1_epi32(FRACTIONMASK);
-    union { alignas(16) ALint i[4]; float f[4]; } pos_;
-    union { alignas(16) ALsizei i[4]; float f[4]; } frac_;
+    alignas(16) ALsizei pos_[4], frac_[4];
     __m128i frac4, pos4;
-    ALint pos;
-    ALsizei i;
+    ALsizei todo, pos, i;
 
-    InitiatePositionArrays(frac, increment, frac_.i, pos_.i, 4);
+    ASSUME(numsamples > 0);
 
-    frac4 = _mm_castps_si128(_mm_load_ps(frac_.f));
-    pos4 = _mm_castps_si128(_mm_load_ps(pos_.f));
+    InitiatePositionArrays(frac, increment, frac_, pos_, 4);
+    frac4 = _mm_setr_epi32(frac_[0], frac_[1], frac_[2], frac_[3]);
+    pos4 = _mm_setr_epi32(pos_[0], pos_[1], pos_[2], pos_[3]);
 
-    for(i = 0;numsamples-i > 3;i += 4)
+    todo = numsamples & ~3;
+    for(i = 0;i < todo;i += 4)
     {
-        const __m128 val1 = _mm_setr_ps(src[pos_.i[0]], src[pos_.i[1]], src[pos_.i[2]], src[pos_.i[3]]);
-        const __m128 val2 = _mm_setr_ps(src[pos_.i[0]+1], src[pos_.i[1]+1], src[pos_.i[2]+1], src[pos_.i[3]+1]);
+        const int pos0 = _mm_extract_epi32(pos4, 0);
+        const int pos1 = _mm_extract_epi32(pos4, 1);
+        const int pos2 = _mm_extract_epi32(pos4, 2);
+        const int pos3 = _mm_extract_epi32(pos4, 3);
+        const __m128 val1 = _mm_setr_ps(src[pos0  ], src[pos1  ], src[pos2  ], src[pos3  ]);
+        const __m128 val2 = _mm_setr_ps(src[pos0+1], src[pos1+1], src[pos2+1], src[pos3+1]);
 
         /* val1 + (val2-val1)*mu */
         const __m128 r0 = _mm_sub_ps(val2, val1);
@@ -61,20 +65,15 @@ const ALfloat *Resample_lerp_SSE41(const InterpState* UNUSED(state),
         frac4 = _mm_add_epi32(frac4, increment4);
         pos4 = _mm_add_epi32(pos4, _mm_srli_epi32(frac4, FRACTIONBITS));
         frac4 = _mm_and_si128(frac4, fracMask4);
-
-        pos_.i[0] = _mm_extract_epi32(pos4, 0);
-        pos_.i[1] = _mm_extract_epi32(pos4, 1);
-        pos_.i[2] = _mm_extract_epi32(pos4, 2);
-        pos_.i[3] = _mm_extract_epi32(pos4, 3);
     }
 
     /* NOTE: These four elements represent the position *after* the last four
      * samples, so the lowest element is the next position to resample.
      */
-    pos = pos_.i[0];
+    pos = _mm_cvtsi128_si32(pos4);
     frac = _mm_cvtsi128_si32(frac4);
 
-    for(;i < numsamples;i++)
+    for(;i < numsamples;++i)
     {
         dst[i] = lerp(src[pos], src[pos+1], frac * (1.0f/FRACTIONONE));
 

+ 28 - 27
libs/openal-soft/Alc/mixvoice.c

@@ -45,7 +45,7 @@
 static_assert((INT_MAX>>FRACTIONBITS)/MAX_PITCH > BUFFERSIZE,
               "MAX_PITCH and/or BUFFERSIZE are too large for FRACTIONBITS!");
 
-extern inline void InitiatePositionArrays(ALsizei frac, ALint increment, ALsizei *restrict frac_arr, ALint *restrict pos_arr, ALsizei size);
+extern inline void InitiatePositionArrays(ALsizei frac, ALint increment, ALsizei *restrict frac_arr, ALsizei *restrict pos_arr, ALsizei size);
 
 
 /* BSinc24 requires up to 23 extra samples before the current position, and 24 after. */
@@ -197,12 +197,11 @@ void aluInitMixer(void)
 static void SendAsyncEvent(ALCcontext *context, ALuint enumtype, ALenum type,
                            ALuint objid, ALuint param, const char *msg)
 {
-    AsyncEvent evt;
-    evt.EnumType = enumtype;
-    evt.Type = type;
-    evt.ObjectId = objid;
-    evt.Param = param;
-    strcpy(evt.Message, msg);
+    AsyncEvent evt = ASYNC_EVENT(enumtype);
+    evt.u.user.type = type;
+    evt.u.user.id = objid;
+    evt.u.user.param = param;
+    strcpy(evt.u.user.msg, msg);
     if(ll_ringbuffer_write(context->AsyncEvents, (const char*)&evt, 1) == 1)
         alsem_post(&context->EventSem);
 }
@@ -263,7 +262,7 @@ static void LoadSamples(ALfloat *restrict dst, const ALvoid *restrict src, ALint
 }
 
 
-static const ALfloat *DoFilters(BiquadState *lpfilter, BiquadState *hpfilter,
+static const ALfloat *DoFilters(BiquadFilter *lpfilter, BiquadFilter *hpfilter,
                                 ALfloat *restrict dst, const ALfloat *restrict src,
                                 ALsizei numsamples, enum ActiveFilters type)
 {
@@ -271,17 +270,17 @@ static const ALfloat *DoFilters(BiquadState *lpfilter, BiquadState *hpfilter,
     switch(type)
     {
         case AF_None:
-            BiquadState_processPassthru(lpfilter, src, numsamples);
-            BiquadState_processPassthru(hpfilter, src, numsamples);
+            BiquadFilter_passthru(lpfilter, numsamples);
+            BiquadFilter_passthru(hpfilter, numsamples);
             break;
 
         case AF_LowPass:
-            BiquadState_process(lpfilter, dst, src, numsamples);
-            BiquadState_processPassthru(hpfilter, dst, numsamples);
+            BiquadFilter_process(lpfilter, dst, src, numsamples);
+            BiquadFilter_passthru(hpfilter, numsamples);
             return dst;
         case AF_HighPass:
-            BiquadState_processPassthru(lpfilter, src, numsamples);
-            BiquadState_process(hpfilter, dst, src, numsamples);
+            BiquadFilter_passthru(lpfilter, numsamples);
+            BiquadFilter_process(hpfilter, dst, src, numsamples);
             return dst;
 
         case AF_BandPass:
@@ -290,8 +289,8 @@ static const ALfloat *DoFilters(BiquadState *lpfilter, BiquadState *hpfilter,
                 ALfloat temp[256];
                 ALsizei todo = mini(256, numsamples-i);
 
-                BiquadState_process(lpfilter, temp, src+i, todo);
-                BiquadState_process(hpfilter, dst+i, temp, todo);
+                BiquadFilter_process(lpfilter, temp, src+i, todo);
+                BiquadFilter_process(hpfilter, dst+i, temp, todo);
                 i += todo;
             }
             return dst;
@@ -487,6 +486,7 @@ ALboolean MixSource(ALvoice *voice, ALuint SourceID, ALCcontext *Context, ALsize
                 while(tmpiter && SrcBufferSize > FilledAmt)
                 {
                     ALsizei SizeToDo = SrcBufferSize - FilledAmt;
+                    ALsizei CompLen = 0;
                     ALsizei i;
 
                     for(i = 0;i < tmpiter->num_buffers;i++)
@@ -499,23 +499,24 @@ ALboolean MixSource(ALvoice *voice, ALuint SourceID, ALCcontext *Context, ALsize
                             const ALubyte *Data = ALBuffer->data;
                             Data += (pos*NumChannels + chan)*SampleSize;
 
-                            DataSize = minu(SizeToDo, DataSize - pos);
+                            DataSize = mini(SizeToDo, DataSize - pos);
+                            CompLen = maxi(CompLen, DataSize);
+
                             LoadSamples(&SrcData[FilledAmt], Data, NumChannels,
                                         ALBuffer->FmtType, DataSize);
                         }
                     }
-                    if(pos > tmpiter->max_samples)
+                    if(UNLIKELY(!CompLen))
                         pos -= tmpiter->max_samples;
                     else
                     {
-                        FilledAmt += tmpiter->max_samples - pos;
+                        FilledAmt += CompLen;
+                        if(SrcBufferSize <= FilledAmt)
+                            break;
                         pos = 0;
                     }
-                    if(SrcBufferSize > FilledAmt)
-                    {
-                        tmpiter = ATOMIC_LOAD(&tmpiter->next, almemory_order_acquire);
-                        if(!tmpiter) tmpiter = BufferLoopItem;
-                    }
+                    tmpiter = ATOMIC_LOAD(&tmpiter->next, almemory_order_acquire);
+                    if(!tmpiter) tmpiter = BufferLoopItem;
                 }
             }
 
@@ -729,8 +730,10 @@ ALboolean MixSource(ALvoice *voice, ALuint SourceID, ALCcontext *Context, ALsize
             if(BufferListItem->max_samples > DataPosInt)
                 break;
 
+            DataPosInt -= BufferListItem->max_samples;
+
             buffers_done += BufferListItem->num_buffers;
-            BufferListItem = ATOMIC_LOAD(&BufferListItem->next, almemory_order_acquire);
+            BufferListItem = ATOMIC_LOAD(&BufferListItem->next, almemory_order_relaxed);
             if(!BufferListItem && !(BufferListItem=BufferLoopItem))
             {
                 isplaying = false;
@@ -738,8 +741,6 @@ ALboolean MixSource(ALvoice *voice, ALuint SourceID, ALCcontext *Context, ALsize
                 DataPosFrac = 0;
                 break;
             }
-
-            DataPosInt -= BufferListItem->max_samples;
         }
     } while(isplaying && OutPos < SamplesToDo);
 

+ 40 - 87
libs/openal-soft/Alc/panning.c

@@ -33,13 +33,15 @@
 #include "bool.h"
 #include "ambdec.h"
 #include "bformatdec.h"
+#include "filters/splitter.h"
 #include "uhjfilter.h"
 #include "bs2b.h"
 
 
+extern inline void CalcDirectionCoeffs(const ALfloat dir[3], ALfloat spread, ALfloat coeffs[MAX_AMBI_COEFFS]);
 extern inline void CalcAngleCoeffs(ALfloat azimuth, ALfloat elevation, ALfloat spread, ALfloat coeffs[MAX_AMBI_COEFFS]);
-extern inline void ComputeDryPanGains(const DryMixParams *dry, const ALfloat coeffs[MAX_AMBI_COEFFS], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]);
-extern inline void ComputeFirstOrderGains(const BFMixParams *foa, const ALfloat mtx[4], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]);
+extern inline float ScaleAzimuthFront(float azimuth, float scale);
+extern inline void ComputePanGains(const MixParams *dry, const ALfloat*restrict coeffs, ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]);
 
 
 static const ALsizei FuMa2ACN[MAX_AMBI_COEFFS] = {
@@ -66,19 +68,15 @@ static const ALsizei ACN2ACN[MAX_AMBI_COEFFS] = {
 };
 
 
-void CalcDirectionCoeffs(const ALfloat dir[3], ALfloat spread, ALfloat coeffs[MAX_AMBI_COEFFS])
+void CalcAmbiCoeffs(const ALfloat y, const ALfloat z, const ALfloat x, const ALfloat spread,
+                    ALfloat coeffs[MAX_AMBI_COEFFS])
 {
-    /* Convert from OpenAL coords to Ambisonics. */
-    ALfloat x = -dir[2];
-    ALfloat y = -dir[0];
-    ALfloat z =  dir[1];
-
     /* Zeroth-order */
     coeffs[0]  = 1.0f; /* ACN 0 = 1 */
     /* First-order */
-    coeffs[1]  = 1.732050808f * y; /* ACN 1 = sqrt(3) * Y */
-    coeffs[2]  = 1.732050808f * z; /* ACN 2 = sqrt(3) * Z */
-    coeffs[3]  = 1.732050808f * x; /* ACN 3 = sqrt(3) * X */
+    coeffs[1]  = SQRTF_3 * y; /* ACN 1 = sqrt(3) * Y */
+    coeffs[2]  = SQRTF_3 * z; /* ACN 2 = sqrt(3) * Z */
+    coeffs[3]  = SQRTF_3 * x; /* ACN 3 = sqrt(3) * X */
     /* Second-order */
     coeffs[4]  = 3.872983346f * x * y;             /* ACN 4 = sqrt(15) * X * Y */
     coeffs[5]  = 3.872983346f * y * z;             /* ACN 5 = sqrt(15) * Y * Z */
@@ -152,16 +150,8 @@ void CalcDirectionCoeffs(const ALfloat dir[3], ALfloat spread, ALfloat coeffs[MA
     }
 }
 
-void CalcAnglePairwiseCoeffs(ALfloat azimuth, ALfloat elevation, ALfloat spread, ALfloat coeffs[MAX_AMBI_COEFFS])
-{
-    ALfloat sign = (azimuth < 0.0f) ? -1.0f : 1.0f;
-    if(!(fabsf(azimuth) > F_PI_2))
-        azimuth = minf(fabsf(azimuth) * F_PI_2 / (F_PI/6.0f), F_PI_2) * sign;
-    CalcAngleCoeffs(azimuth, elevation, spread, coeffs);
-}
-
 
-void ComputePanningGainsMC(const ChannelConfig *chancoeffs, ALsizei numchans, ALsizei numcoeffs, const ALfloat coeffs[MAX_AMBI_COEFFS], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS])
+void ComputePanningGainsMC(const ChannelConfig *chancoeffs, ALsizei numchans, ALsizei numcoeffs, const ALfloat*restrict coeffs, ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS])
 {
     ALsizei i, j;
 
@@ -176,7 +166,7 @@ void ComputePanningGainsMC(const ChannelConfig *chancoeffs, ALsizei numchans, AL
         gains[i] = 0.0f;
 }
 
-void ComputePanningGainsBF(const BFChannelConfig *chanmap, ALsizei numchans, const ALfloat coeffs[MAX_AMBI_COEFFS], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS])
+void ComputePanningGainsBF(const BFChannelConfig *chanmap, ALsizei numchans, const ALfloat*restrict coeffs, ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS])
 {
     ALsizei i;
 
@@ -186,31 +176,6 @@ void ComputePanningGainsBF(const BFChannelConfig *chanmap, ALsizei numchans, con
         gains[i] = 0.0f;
 }
 
-void ComputeFirstOrderGainsMC(const ChannelConfig *chancoeffs, ALsizei numchans, const ALfloat mtx[4], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS])
-{
-    ALsizei i, j;
-
-    for(i = 0;i < numchans;i++)
-    {
-        float gain = 0.0f;
-        for(j = 0;j < 4;j++)
-            gain += chancoeffs[i][j] * mtx[j];
-        gains[i] = clampf(gain, 0.0f, 1.0f) * ingain;
-    }
-    for(;i < MAX_OUTPUT_CHANNELS;i++)
-        gains[i] = 0.0f;
-}
-
-void ComputeFirstOrderGainsBF(const BFChannelConfig *chanmap, ALsizei numchans, const ALfloat mtx[4], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS])
-{
-    ALsizei i;
-
-    for(i = 0;i < numchans;i++)
-        gains[i] = chanmap[i].Scale * mtx[chanmap[i].Index] * ingain;
-    for(;i < MAX_OUTPUT_CHANNELS;i++)
-        gains[i] = 0.0f;
-}
-
 
 static inline const char *GetLabelFromChannel(enum Channel channel)
 {
@@ -383,8 +348,8 @@ static bool MakeSpeakerMap(ALCdevice *device, const AmbDecConf *conf, ALsizei sp
 static const ChannelMap MonoCfg[1] = {
     { FrontCenter, { 1.0f } },
 }, StereoCfg[2] = {
-    { FrontLeft,   { 5.00000000e-1f,  2.88675135e-1f, 0.0f,  5.77350269e-2f } },
-    { FrontRight,  { 5.00000000e-1f, -2.88675135e-1f, 0.0f,  5.77350269e-2f } },
+    { FrontLeft,   { 5.00000000e-1f,  2.88675135e-1f, 0.0f,  5.52305643e-2f } },
+    { FrontRight,  { 5.00000000e-1f, -2.88675135e-1f, 0.0f,  5.52305643e-2f } },
 }, QuadCfg[4] = {
     { BackLeft,    { 3.53553391e-1f,  2.04124145e-1f, 0.0f, -2.04124145e-1f } },
     { FrontLeft,   { 3.53553391e-1f,  2.04124145e-1f, 0.0f,  2.04124145e-1f } },
@@ -415,7 +380,8 @@ static const ChannelMap MonoCfg[1] = {
     { BackRight,   { 2.04124145e-1f, -1.08880247e-1f, 0.0f, -1.88586120e-1f,  1.29099444e-1f, 0.0f, 0.0f, 0.0f,  7.45355993e-2f, -3.73460789e-2f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,  0.00000000e+0f } },
 };
 
-static void InitNearFieldCtrl(ALCdevice *device, ALfloat ctrl_dist, ALsizei order, bool periphonic)
+static void InitNearFieldCtrl(ALCdevice *device, ALfloat ctrl_dist, ALsizei order,
+                              const ALsizei *restrict chans_per_order)
 {
     const char *devname = alstr_get_cstr(device->DeviceName);
     ALsizei i;
@@ -426,16 +392,12 @@ static void InitNearFieldCtrl(ALCdevice *device, ALfloat ctrl_dist, ALsizei orde
          * be used when rendering to an ambisonic buffer.
          */
         device->AvgSpeakerDist = minf(ctrl_dist, 10.0f);
+        TRACE("Using near-field reference distance: %.2f meters\n", device->AvgSpeakerDist);
 
-        device->Dry.NumChannelsPerOrder[0] = 1;
-        if(periphonic)
-            for(i = 1;i < order+1;i++)
-                device->Dry.NumChannelsPerOrder[i] = (i+1)*(i+1) - i*i;
-        else
-            for(i = 1;i < order+1;i++)
-                device->Dry.NumChannelsPerOrder[i] = (i*2+1) - ((i-1)*2+1);
+        for(i = 0;i < order+1;i++)
+            device->NumChannelsPerOrder[i] = chans_per_order[i];
         for(;i < MAX_AMBI_ORDER+1;i++)
-            device->Dry.NumChannelsPerOrder[i] = 0;
+            device->NumChannelsPerOrder[i] = 0;
     }
 }
 
@@ -609,9 +571,12 @@ static void InitPanning(ALCdevice *device)
 
         if(ConfigValueFloat(devname, "decoder", "nfc-ref-delay", &nfc_delay) && nfc_delay > 0.0f)
         {
+            static const ALsizei chans_per_order[MAX_AMBI_ORDER+1] = {
+                1, 3, 5, 7
+            };
             nfc_delay = clampf(nfc_delay, 0.001f, 1000.0f);
             InitNearFieldCtrl(device, nfc_delay * SPEEDOFSOUNDMETRESPERSEC,
-                              device->AmbiOrder, true);
+                              device->AmbiOrder, chans_per_order);
         }
     }
     else
@@ -727,6 +692,8 @@ static void InitCustomPanning(ALCdevice *device, const AmbDecConf *conf, const A
 
 static void InitHQPanning(ALCdevice *device, const AmbDecConf *conf, const ALsizei speakermap[MAX_OUTPUT_CHANNELS])
 {
+    static const ALsizei chans_per_order2d[MAX_AMBI_ORDER+1] = { 1, 2, 2, 2 };
+    static const ALsizei chans_per_order3d[MAX_AMBI_ORDER+1] = { 1, 3, 5, 7 };
     ALfloat avg_dist;
     ALsizei count;
     ALsizei i;
@@ -803,7 +770,7 @@ static void InitHQPanning(ALCdevice *device, const AmbDecConf *conf, const ALsiz
     avg_dist /= (ALfloat)conf->NumSpeakers;
     InitNearFieldCtrl(device, avg_dist,
         (conf->ChanMask > 0x1ff) ? 3 : (conf->ChanMask > 0xf) ? 2 : 1,
-        !!(conf->ChanMask&AMBI_PERIPHONIC_MASK)
+        (conf->ChanMask&AMBI_PERIPHONIC_MASK) ? chans_per_order3d : chans_per_order2d
     );
 
     InitDistanceComp(device, conf, speakermap);
@@ -814,10 +781,10 @@ static void InitHrtfPanning(ALCdevice *device)
     /* NOTE: azimuth goes clockwise. */
     static const struct AngularPoint AmbiPoints[] = {
         { DEG2RAD( 90.0f), DEG2RAD(   0.0f) },
-        { DEG2RAD( 35.0f), DEG2RAD(  45.0f) },
-        { DEG2RAD( 35.0f), DEG2RAD( 135.0f) },
-        { DEG2RAD( 35.0f), DEG2RAD(-135.0f) },
-        { DEG2RAD( 35.0f), DEG2RAD( -45.0f) },
+        { DEG2RAD( 35.2643897f), DEG2RAD(  45.0f) },
+        { DEG2RAD( 35.2643897f), DEG2RAD( 135.0f) },
+        { DEG2RAD( 35.2643897f), DEG2RAD(-135.0f) },
+        { DEG2RAD( 35.2643897f), DEG2RAD( -45.0f) },
         { DEG2RAD(  0.0f), DEG2RAD(   0.0f) },
         { DEG2RAD(  0.0f), DEG2RAD(  45.0f) },
         { DEG2RAD(  0.0f), DEG2RAD(  90.0f) },
@@ -826,10 +793,10 @@ static void InitHrtfPanning(ALCdevice *device)
         { DEG2RAD(  0.0f), DEG2RAD(-135.0f) },
         { DEG2RAD(  0.0f), DEG2RAD( -90.0f) },
         { DEG2RAD(  0.0f), DEG2RAD( -45.0f) },
-        { DEG2RAD(-35.0f), DEG2RAD(  45.0f) },
-        { DEG2RAD(-35.0f), DEG2RAD( 135.0f) },
-        { DEG2RAD(-35.0f), DEG2RAD(-135.0f) },
-        { DEG2RAD(-35.0f), DEG2RAD( -45.0f) },
+        { DEG2RAD(-35.2643897f), DEG2RAD(  45.0f) },
+        { DEG2RAD(-35.2643897f), DEG2RAD( 135.0f) },
+        { DEG2RAD(-35.2643897f), DEG2RAD(-135.0f) },
+        { DEG2RAD(-35.2643897f), DEG2RAD( -45.0f) },
         { DEG2RAD(-90.0f), DEG2RAD(   0.0f) },
     };
     static const ALfloat AmbiMatrixFOA[][MAX_AMBI_COEFFS] = {
@@ -872,11 +839,12 @@ static void InitHrtfPanning(ALCdevice *device)
         { 5.55555556e-02f,  0.00000000e+00f, -1.23717915e-01f,  0.00000000e+00f,  0.00000000e+00f,  0.00000000e+00f },
     };
     static const ALfloat AmbiOrderHFGainFOA[MAX_AMBI_ORDER+1] = {
-        1.00000000e+00f, 5.77350269e-01f
+        3.00000000e+00f, 1.73205081e+00f
     }, AmbiOrderHFGainHOA[MAX_AMBI_ORDER+1] = {
-        9.80580676e-01f, 7.59554525e-01f, 3.92232270e-01f
+        2.40192231e+00f, 1.86052102e+00f, 9.60768923e-01f
     };
     static const ALsizei IndexMap[6] = { 0, 1, 2, 3, 4, 8 };
+    static const ALsizei ChansPerOrder[MAX_AMBI_ORDER+1] = { 1, 3, 2, 0 };
     const ALfloat (*restrict AmbiMatrix)[MAX_AMBI_COEFFS] = AmbiMatrixFOA;
     const ALfloat *restrict AmbiOrderHFGain = AmbiOrderHFGainFOA;
     ALsizei count = 4;
@@ -884,7 +852,6 @@ static void InitHrtfPanning(ALCdevice *device)
 
     static_assert(COUNTOF(AmbiPoints) == COUNTOF(AmbiMatrixFOA), "FOA Ambisonic HRTF mismatch");
     static_assert(COUNTOF(AmbiPoints) == COUNTOF(AmbiMatrixHOA), "HOA Ambisonic HRTF mismatch");
-    static_assert(COUNTOF(AmbiPoints) <= HRTF_AMBI_MAX_CHANNELS, "HRTF_AMBI_MAX_CHANNELS is too small");
 
     if(device->AmbiUp)
     {
@@ -931,22 +898,8 @@ static void InitHrtfPanning(ALCdevice *device)
         AmbiOrderHFGain
     );
 
-    if(GetConfigValueBool(alstr_get_cstr(device->DeviceName), "decoder", "nfc", 1) &&
-       device->HrtfHandle->distance > 0.0f)
-    {
-        /* NFC is only used when AvgSpeakerDist is greater than 0, and can only
-         * be used when rendering to an ambisonic buffer.
-         */
-        device->AvgSpeakerDist = minf(device->HrtfHandle->distance, 10.0f);
-
-        i = 0;
-        device->Dry.NumChannelsPerOrder[i++] = 1;
-        device->Dry.NumChannelsPerOrder[i++] = 3;
-        if(device->AmbiUp)
-            device->Dry.NumChannelsPerOrder[i++] = 2;
-        while(i < MAX_AMBI_ORDER+1)
-            device->Dry.NumChannelsPerOrder[i++] = 0;
-    }
+    InitNearFieldCtrl(device, device->HrtfHandle->distance, device->AmbiUp ? 2 : 1,
+                      ChansPerOrder);
 }
 
 static void InitUhjPanning(ALCdevice *device)
@@ -989,7 +942,7 @@ void aluInitRenderer(ALCdevice *device, ALint hrtf_id, enum HrtfRequestMode hrtf
     device->Dry.CoeffCount = 0;
     device->Dry.NumChannels = 0;
     for(i = 0;i < MAX_AMBI_ORDER+1;i++)
-        device->Dry.NumChannelsPerOrder[i] = 0;
+        device->NumChannelsPerOrder[i] = 0;
 
     device->AvgSpeakerDist = 0.0f;
     memset(device->ChannelDelay, 0, sizeof(device->ChannelDelay));

+ 33 - 55
libs/openal-soft/Alc/uhjfilter.c

@@ -9,44 +9,29 @@
 #define MAX_UPDATE_SAMPLES  128
 
 
-static const ALfloat Filter1Coeff[4] = {
-    0.6923878f, 0.9360654322959f, 0.9882295226860f, 0.9987488452737f
+static const ALfloat Filter1CoeffSqr[4] = {
+    0.479400865589f, 0.876218493539f, 0.976597589508f, 0.997499255936f
 };
-static const ALfloat Filter2Coeff[4] = {
-    0.4021921162426f, 0.8561710882420f, 0.9722909545651f, 0.9952884791278f
+static const ALfloat Filter2CoeffSqr[4] = {
+    0.161758498368f, 0.733028932341f, 0.945349700329f, 0.990599156685f
 };
 
 static void allpass_process(AllPassState *state, ALfloat *restrict dst, const ALfloat *restrict src, const ALfloat aa, ALsizei todo)
 {
+    ALfloat z1 = state->z[0];
+    ALfloat z2 = state->z[1];
     ALsizei i;
 
-    if(LIKELY(todo > 1))
+    for(i = 0;i < todo;i++)
     {
-        ALfloat x0 = state->x[0];
-        ALfloat x1 = state->x[1];
-        ALfloat y0 = state->y[0];
-        ALfloat y1 = state->y[1];
-
-        for(i = 0;i < todo;i++)
-        {
-            dst[i] = aa*(src[i] + y1) - x1;
-            y1 = y0; y0 = dst[i];
-            x1 = x0; x0 = src[i];
-        }
-
-        state->x[0] = x0;
-        state->x[1] = x1;
-        state->y[0] = y0;
-        state->y[1] = y1;
-    }
-    else if(todo == 1)
-    {
-        dst[0] = aa*(src[0] + state->y[1]) - state->x[1];
-        state->x[1] = state->x[0];
-        state->x[0] = src[0];
-        state->y[1] = state->y[0];
-        state->y[0] = dst[0];
+        ALfloat input = src[i];
+        ALfloat output = input*aa + z1;
+        z1 = z2; z2 = output*aa - input;
+        dst[i] = output;
     }
+
+    state->z[0] = z1;
+    state->z[1] = z2;
 }
 
 
@@ -76,41 +61,37 @@ void EncodeUhj2(Uhj2Encoder *enc, ALfloat *restrict LeftOut, ALfloat *restrict R
     ALfloat temp[2][MAX_UPDATE_SAMPLES];
     ALsizei base, i;
 
+    ASSUME(SamplesToDo > 0);
+
     for(base = 0;base < SamplesToDo;)
     {
         ALsizei todo = mini(SamplesToDo - base, MAX_UPDATE_SAMPLES);
+        ASSUME(todo > 0);
 
         /* D = 0.6554516*Y */
         for(i = 0;i < todo;i++)
             temp[0][i] = 0.6554516f*InSamples[2][base+i];
-        allpass_process(&enc->Filter1_Y[0], temp[1], temp[0],
-                        Filter1Coeff[0]*Filter1Coeff[0], todo);
-        allpass_process(&enc->Filter1_Y[1], temp[0], temp[1],
-                        Filter1Coeff[1]*Filter1Coeff[1], todo);
-        allpass_process(&enc->Filter1_Y[2], temp[1], temp[0],
-                        Filter1Coeff[2]*Filter1Coeff[2], todo);
+        allpass_process(&enc->Filter1_Y[0], temp[1], temp[0], Filter1CoeffSqr[0], todo);
+        allpass_process(&enc->Filter1_Y[1], temp[0], temp[1], Filter1CoeffSqr[1], todo);
+        allpass_process(&enc->Filter1_Y[2], temp[1], temp[0], Filter1CoeffSqr[2], todo);
+        allpass_process(&enc->Filter1_Y[3], temp[0], temp[1], Filter1CoeffSqr[3], todo);
         /* NOTE: Filter1 requires a 1 sample delay for the final output, so
          * take the last processed sample from the previous run as the first
          * output sample.
          */
-        D[0] = enc->Filter1_Y[3].y[0];
-        allpass_process(&enc->Filter1_Y[3], temp[0], temp[1],
-                        Filter1Coeff[3]*Filter1Coeff[3], todo);
+        D[0] = enc->LastY;
         for(i = 1;i < todo;i++)
             D[i] = temp[0][i-1];
+        enc->LastY = temp[0][i-1];
 
         /* D += j(-0.3420201*W + 0.5098604*X) */
         for(i = 0;i < todo;i++)
             temp[0][i] = -0.3420201f*InSamples[0][base+i] +
                           0.5098604f*InSamples[1][base+i];
-        allpass_process(&enc->Filter2_WX[0], temp[1], temp[0],
-                        Filter2Coeff[0]*Filter2Coeff[0], todo);
-        allpass_process(&enc->Filter2_WX[1], temp[0], temp[1],
-                        Filter2Coeff[1]*Filter2Coeff[1], todo);
-        allpass_process(&enc->Filter2_WX[2], temp[1], temp[0],
-                        Filter2Coeff[2]*Filter2Coeff[2], todo);
-        allpass_process(&enc->Filter2_WX[3], temp[0], temp[1],
-                        Filter2Coeff[3]*Filter2Coeff[3], todo);
+        allpass_process(&enc->Filter2_WX[0], temp[1], temp[0], Filter2CoeffSqr[0], todo);
+        allpass_process(&enc->Filter2_WX[1], temp[0], temp[1], Filter2CoeffSqr[1], todo);
+        allpass_process(&enc->Filter2_WX[2], temp[1], temp[0], Filter2CoeffSqr[2], todo);
+        allpass_process(&enc->Filter2_WX[3], temp[0], temp[1], Filter2CoeffSqr[3], todo);
         for(i = 0;i < todo;i++)
             D[i] += temp[0][i];
 
@@ -118,17 +99,14 @@ void EncodeUhj2(Uhj2Encoder *enc, ALfloat *restrict LeftOut, ALfloat *restrict R
         for(i = 0;i < todo;i++)
             temp[0][i] = 0.9396926f*InSamples[0][base+i] +
                          0.1855740f*InSamples[1][base+i];
-        allpass_process(&enc->Filter1_WX[0], temp[1], temp[0],
-                        Filter1Coeff[0]*Filter1Coeff[0], todo);
-        allpass_process(&enc->Filter1_WX[1], temp[0], temp[1],
-                        Filter1Coeff[1]*Filter1Coeff[1], todo);
-        allpass_process(&enc->Filter1_WX[2], temp[1], temp[0],
-                        Filter1Coeff[2]*Filter1Coeff[2], todo);
-        S[0] = enc->Filter1_WX[3].y[0];
-        allpass_process(&enc->Filter1_WX[3], temp[0], temp[1],
-                        Filter1Coeff[3]*Filter1Coeff[3], todo);
+        allpass_process(&enc->Filter1_WX[0], temp[1], temp[0], Filter1CoeffSqr[0], todo);
+        allpass_process(&enc->Filter1_WX[1], temp[0], temp[1], Filter1CoeffSqr[1], todo);
+        allpass_process(&enc->Filter1_WX[2], temp[1], temp[0], Filter1CoeffSqr[2], todo);
+        allpass_process(&enc->Filter1_WX[3], temp[0], temp[1], Filter1CoeffSqr[3], todo);
+        S[0] = enc->LastWX;
         for(i = 1;i < todo;i++)
             S[i] = temp[0][i-1];
+        enc->LastWX = temp[0][i-1];
 
         /* Left = (S + D)/2.0 */
         for(i = 0;i < todo;i++)

+ 3 - 3
libs/openal-soft/Alc/uhjfilter.h

@@ -6,8 +6,7 @@
 #include "alMain.h"
 
 typedef struct AllPassState {
-    ALfloat x[2]; /* Last two input samples */
-    ALfloat y[2]; /* Last two output samples */
+    ALfloat z[2];
 } AllPassState;
 
 /* Encoding 2-channel UHJ from B-Format is done as:
@@ -36,9 +35,10 @@ typedef struct AllPassState {
  */
 
 typedef struct Uhj2Encoder {
-    AllPassState Filter1_WX[4];
     AllPassState Filter1_Y[4];
     AllPassState Filter2_WX[4];
+    AllPassState Filter1_WX[4];
+    ALfloat LastY, LastWX;
 } Uhj2Encoder;
 
 /* Encodes a 2-channel UHJ (stereo-compatible) signal from a B-Format input

+ 160 - 69
libs/openal-soft/CMakeLists.txt

@@ -41,16 +41,16 @@ OPTION(ALSOFT_DLOPEN  "Check for the dlopen API for loading optional libs"  ON)
 
 OPTION(ALSOFT_WERROR  "Treat compile warnings as errors"      OFF)
 
-OPTION(ALSOFT_UTILS          "Build and install utility programs"         OFF)
-OPTION(ALSOFT_NO_CONFIG_UTIL "Disable building the alsoft-config utility" ON)
+OPTION(ALSOFT_UTILS          "Build and install utility programs"         ON)
+OPTION(ALSOFT_NO_CONFIG_UTIL "Disable building the alsoft-config utility" OFF)
 
-OPTION(ALSOFT_EXAMPLES  "Build and install example programs"  OFF)
-OPTION(ALSOFT_TESTS     "Build and install test programs"     OFF)
+OPTION(ALSOFT_EXAMPLES  "Build and install example programs"  ON)
+OPTION(ALSOFT_TESTS     "Build and install test programs"     ON)
 
-OPTION(ALSOFT_CONFIG "Install alsoft.conf sample configuration file" OFF)
-OPTION(ALSOFT_HRTF_DEFS "Install HRTF definition files" OFF)
-OPTION(ALSOFT_AMBDEC_PRESETS "Install AmbDec preset files" OFF)
-OPTION(ALSOFT_INSTALL "Install headers and libraries" OFF)
+OPTION(ALSOFT_CONFIG "Install alsoft.conf sample configuration file" ON)
+OPTION(ALSOFT_HRTF_DEFS "Install HRTF definition files" ON)
+OPTION(ALSOFT_AMBDEC_PRESETS "Install AmbDec preset files" ON)
+OPTION(ALSOFT_INSTALL "Install headers and libraries" ON)
 
 if(DEFINED SHARE_INSTALL_DIR)
     message(WARNING "SHARE_INSTALL_DIR is deprecated.  Use the variables provided by the GNUInstallDirs module instead")
@@ -102,8 +102,8 @@ IF(NOT LIBTYPE)
 ENDIF()
 
 SET(LIB_MAJOR_VERSION "1")
-SET(LIB_MINOR_VERSION "18")
-SET(LIB_REVISION "2")
+SET(LIB_MINOR_VERSION "19")
+SET(LIB_REVISION "1")
 SET(LIB_VERSION "${LIB_MAJOR_VERSION}.${LIB_MINOR_VERSION}.${LIB_REVISION}")
 
 SET(EXPORT_DECL "")
@@ -432,6 +432,48 @@ IF(HAVE_MFPU_NEON_SWITCH)
     SET(FPU_NEON_SWITCH "-mfpu=neon")
 ENDIF()
 
+SET(FPMATH_SET "0")
+IF(CMAKE_SIZEOF_VOID_P MATCHES "4")
+    IF(SSE_SWITCH OR MSVC)
+        OPTION(ALSOFT_ENABLE_SSE_CODEGEN "Enable SSE code generation instead of x87 for 32-bit targets." TRUE)
+    ENDIF()
+    IF(SSE2_SWITCH OR MSVC)
+        OPTION(ALSOFT_ENABLE_SSE2_CODEGEN "Enable SSE2 code generation instead of x87 for 32-bit targets." TRUE)
+    ENDIF()
+
+    IF(ALSOFT_ENABLE_SSE2_CODEGEN)
+        IF(SSE2_SWITCH)
+            CHECK_C_COMPILER_FLAG("${SSE2_SWITCH} -mfpmath=sse" HAVE_MFPMATH_SSE_2)
+            IF(HAVE_MFPMATH_SSE_2)
+                SET(C_FLAGS ${C_FLAGS} ${SSE2_SWITCH} -mfpmath=sse)
+                SET(FPMATH_SET 2)
+            ENDIF()
+        ELSEIF(MSVC)
+            CHECK_C_COMPILER_FLAG("/arch:SSE2" HAVE_ARCH_SSE2)
+            IF(HAVE_ARCH_SSE2)
+                SET(C_FLAGS ${C_FLAGS} "/arch:SSE2")
+                SET(FPMATH_SET 2)
+            ENDIF()
+        ENDIF()
+    ENDIF()
+    IF(ALSOFT_ENABLE_SSE_CODEGEN AND NOT FPMATH_SET)
+        IF(SSE_SWITCH)
+            CHECK_C_COMPILER_FLAG("${SSE_SWITCH} -mfpmath=sse" HAVE_MFPMATH_SSE)
+            IF(HAVE_MFPMATH_SSE)
+                SET(C_FLAGS ${C_FLAGS} ${SSE_SWITCH} -mfpmath=sse)
+                SET(FPMATH_SET 1)
+            ENDIF()
+        ELSEIF(MSVC)
+            CHECK_C_COMPILER_FLAG("/arch:SSE" HAVE_ARCH_SSE)
+            IF(HAVE_ARCH_SSE)
+                SET(C_FLAGS ${C_FLAGS} "/arch:SSE")
+                SET(FPMATH_SET 1)
+            ENDIF()
+        ENDIF()
+    ENDIF()
+ENDIF()
+
+
 CHECK_C_SOURCE_COMPILES("int foo(const char *str, ...) __attribute__((format(printf, 1, 2)));
                          int main() {return 0;}" HAVE_GCC_FORMAT)
 
@@ -512,6 +554,7 @@ CHECK_SYMBOL_EXISTS(lrintf math.h HAVE_LRINTF)
 CHECK_SYMBOL_EXISTS(modff  math.h HAVE_MODFF)
 CHECK_SYMBOL_EXISTS(log2f  math.h HAVE_LOG2F)
 CHECK_SYMBOL_EXISTS(cbrtf  math.h HAVE_CBRTF)
+CHECK_SYMBOL_EXISTS(copysignf math.h HAVE_COPYSIGNF)
 
 IF(HAVE_FLOAT_H)
     CHECK_SYMBOL_EXISTS(_controlfp float.h HAVE__CONTROLFP)
@@ -699,51 +742,100 @@ IF(NOT HAVE_STDINT_H)
 ENDIF()
 
 
-SET(COMMON_OBJS  common/almalloc.c
-                 common/atomic.c
-                 common/rwlock.c
-                 common/threads.c
-                 common/uintmap.c
+SET(COMMON_OBJS
+    common/alcomplex.c
+    common/alcomplex.h
+    common/align.h
+    common/almalloc.c
+    common/almalloc.h
+    common/atomic.c
+    common/atomic.h
+    common/bool.h
+    common/math_defs.h
+    common/rwlock.c
+    common/rwlock.h
+    common/static_assert.h
+    common/threads.c
+    common/threads.h
+    common/uintmap.c
+    common/uintmap.h
 )
-SET(OPENAL_OBJS  OpenAL32/alAuxEffectSlot.c
-                 OpenAL32/alBuffer.c
-                 OpenAL32/alEffect.c
-                 OpenAL32/alError.c
-                 OpenAL32/alExtension.c
-                 OpenAL32/alFilter.c
-                 OpenAL32/alListener.c
-                 OpenAL32/alSource.c
-                 OpenAL32/alState.c
-                 OpenAL32/event.c
-                 OpenAL32/sample_cvt.c
+SET(OPENAL_OBJS
+    OpenAL32/Include/bs2b.h
+    OpenAL32/Include/alMain.h
+    OpenAL32/Include/alu.h
+
+    OpenAL32/Include/alAuxEffectSlot.h
+    OpenAL32/alAuxEffectSlot.c
+    OpenAL32/Include/alBuffer.h
+    OpenAL32/alBuffer.c
+    OpenAL32/Include/alEffect.h
+    OpenAL32/alEffect.c
+    OpenAL32/Include/alError.h
+    OpenAL32/alError.c
+    OpenAL32/alExtension.c
+    OpenAL32/Include/alFilter.h
+    OpenAL32/alFilter.c
+    OpenAL32/Include/alListener.h
+    OpenAL32/alListener.c
+    OpenAL32/Include/alSource.h
+    OpenAL32/alSource.c
+    OpenAL32/alState.c
+    OpenAL32/event.c
+    OpenAL32/Include/sample_cvt.h
+    OpenAL32/sample_cvt.c
 )
-SET(ALC_OBJS  Alc/ALc.c
-              Alc/ALu.c
-              Alc/alconfig.c
-              Alc/bs2b.c
-              Alc/converter.c
-              Alc/mastering.c
-              Alc/ringbuffer.c
-              Alc/effects/chorus.c
-              Alc/effects/compressor.c
-              Alc/effects/dedicated.c
-              Alc/effects/distortion.c
-              Alc/effects/echo.c
-              Alc/effects/equalizer.c
-              Alc/effects/modulator.c
-              Alc/effects/null.c
-              Alc/effects/pshifter.c
-              Alc/effects/reverb.c
-              Alc/filters/filter.c
-              Alc/filters/nfc.c
-              Alc/helpers.c
-              Alc/hrtf.c
-              Alc/uhjfilter.c
-              Alc/ambdec.c
-              Alc/bformatdec.c
-              Alc/panning.c
-              Alc/mixvoice.c
-              Alc/mixer/mixer_c.c
+SET(ALC_OBJS
+    Alc/ALc.c
+    Alc/ALu.c
+    Alc/alconfig.c
+    Alc/alconfig.h
+    Alc/bs2b.c
+    Alc/converter.c
+    Alc/converter.h
+    Alc/inprogext.h
+    Alc/mastering.c
+    Alc/mastering.h
+    Alc/ringbuffer.c
+    Alc/ringbuffer.h
+    Alc/effects/autowah.c
+    Alc/effects/chorus.c
+    Alc/effects/compressor.c
+    Alc/effects/dedicated.c
+    Alc/effects/distortion.c
+    Alc/effects/echo.c
+    Alc/effects/equalizer.c
+    Alc/effects/fshifter.c
+    Alc/effects/modulator.c
+    Alc/effects/null.c
+    Alc/effects/pshifter.c
+    Alc/effects/reverb.c
+    Alc/filters/defs.h
+    Alc/filters/filter.c
+    Alc/filters/nfc.c
+    Alc/filters/nfc.h
+    Alc/filters/splitter.c
+    Alc/filters/splitter.h
+    Alc/helpers.c
+    Alc/alstring.h
+    Alc/compat.h
+    Alc/cpu_caps.h
+    Alc/fpu_modes.h
+    Alc/logging.h
+    Alc/vector.h
+    Alc/hrtf.c
+    Alc/hrtf.h
+    Alc/uhjfilter.c
+    Alc/uhjfilter.h
+    Alc/ambdec.c
+    Alc/ambdec.h
+    Alc/bformatdec.c
+    Alc/bformatdec.h
+    Alc/panning.c
+    Alc/polymorphism.h
+    Alc/mixvoice.c
+    Alc/mixer/defs.h
+    Alc/mixer/mixer_c.c
 )
 
 
@@ -810,7 +902,7 @@ IF(ALSOFT_REQUIRE_SSE2 AND NOT HAVE_SSE2)
     MESSAGE(FATAL_ERROR "Failed to enable required SSE2 CPU extensions")
 ENDIF()
 
-OPTION(ALSOFT_REQUIRE_SSE2 "Require SSE3 support" OFF)
+OPTION(ALSOFT_REQUIRE_SSE3 "Require SSE3 support" OFF)
 CHECK_INCLUDE_FILE(pmmintrin.h HAVE_PMMINTRIN_H "${SSE3_SWITCH}")
 IF(HAVE_EMMINTRIN_H)
     OPTION(ALSOFT_CPUEXT_SSE3 "Enable SSE3 support" ON)
@@ -883,10 +975,11 @@ ENDIF()
 
 SET(BACKENDS "")
 SET(ALC_OBJS  ${ALC_OBJS}
-              Alc/backends/base.c
-              # Default backends, always available
-              Alc/backends/loopback.c
-              Alc/backends/null.c
+    Alc/backends/base.c
+    Alc/backends/base.h
+    # Default backends, always available
+    Alc/backends/loopback.c
+    Alc/backends/null.c
 )
 
 # Check ALSA backend
@@ -1209,7 +1302,7 @@ ADD_CUSTOM_TARGET(native-tools
     VERBATIM
 )
 
-option(ALSOFT_EMBED_HRTF_DATA "Embed the HRTF data files (increases library footprint)" OFF)
+option(ALSOFT_EMBED_HRTF_DATA "Embed the HRTF data files (increases library footprint)" ON)
 if(ALSOFT_EMBED_HRTF_DATA)
     MACRO(make_hrtf_header FILENAME VARNAME)
         SET(infile  "${OpenAL_SOURCE_DIR}/hrtf/${FILENAME}")
@@ -1319,7 +1412,7 @@ ELSE()
     ENDIF()
 
     IF(WIN32 AND ALSOFT_BUILD_ROUTER)
-        ADD_LIBRARY(OpenAL SHARED router/router.c router/alc.c router/al.c)
+        ADD_LIBRARY(OpenAL SHARED router/router.c router/router.h router/alc.c router/al.c)
         TARGET_COMPILE_DEFINITIONS(OpenAL
             PRIVATE AL_BUILD_LIBRARY AL_ALEXT_PROTOTYPES ${CPP_DEFS})
         TARGET_COMPILE_OPTIONS(OpenAL PRIVATE ${C_FLAGS})
@@ -1351,13 +1444,10 @@ TARGET_INCLUDE_DIRECTORIES(${IMPL_TARGET}
 TARGET_COMPILE_OPTIONS(${IMPL_TARGET} PRIVATE ${C_FLAGS})
 TARGET_LINK_LIBRARIES(${IMPL_TARGET}
     PRIVATE ${LINKER_FLAGS} ${COMMON_LIB} ${EXTRA_LIBS} ${MATH_LIB})
-TARGET_INCLUDE_DIRECTORIES(${IMPL_TARGET} PUBLIC include)
 IF(TARGET build_version)
     ADD_DEPENDENCIES(${IMPL_TARGET} build_version)
 ENDIF()
 
-SET(OPENAL_LIB_NAME ${IMPL_TARGET} PARENT_SCOPE)
-
 IF(WIN32 AND MINGW AND ALSOFT_BUILD_IMPORT_LIB AND NOT LIBTYPE STREQUAL "STATIC")
     FIND_PROGRAM(SED_EXECUTABLE NAMES sed DOC "sed executable")
     FIND_PROGRAM(DLLTOOL_EXECUTABLE NAMES "${DLLTOOL}" DOC "dlltool executable")
@@ -1425,6 +1515,10 @@ MESSAGE(STATUS "")
 MESSAGE(STATUS "Building with support for CPU extensions:")
 MESSAGE(STATUS "    ${CPU_EXTS}")
 MESSAGE(STATUS "")
+IF(FPMATH_SET)
+    MESSAGE(STATUS "Building with SSE${FPMATH_SET} codegen")
+    MESSAGE(STATUS "")
+ENDIF()
 
 IF(WIN32)
     IF(NOT HAVE_DSOUND)
@@ -1480,7 +1574,7 @@ IF(ALSOFT_UTILS)
 
     SET(MAKEHRTF_SRCS  utils/makehrtf.c)
     IF(NOT HAVE_GETOPT)
-        SET(MAKEHRTF_SRCS  ${MAKEHRTF_SRCS} utils/getopt.c)
+        SET(MAKEHRTF_SRCS  ${MAKEHRTF_SRCS} utils/getopt.c utils/getopt.h)
     ENDIF()
     ADD_EXECUTABLE(makehrtf ${MAKEHRTF_SRCS})
     TARGET_COMPILE_DEFINITIONS(makehrtf PRIVATE ${CPP_DEFS})
@@ -1652,7 +1746,8 @@ IF(ALSOFT_EXAMPLES)
                 PRIVATE ${SDL2_INCLUDE_DIR} ${FFMPEG_INCLUDE_DIRS})
             TARGET_COMPILE_OPTIONS(alffplay PRIVATE ${C_FLAGS})
             TARGET_LINK_LIBRARIES(alffplay
-                PRIVATE ${LINKER_FLAGS} ${SDL2_LIBRARY} ${FFMPEG_LIBRARIES} common OpenAL)
+                PRIVATE ${LINKER_FLAGS} ${SDL2_LIBRARY} ${FFMPEG_LIBRARIES} ex-common common
+                        OpenAL)
 
             IF(ALSOFT_INSTALL)
                 INSTALL(TARGETS alffplay
@@ -1666,7 +1761,3 @@ IF(ALSOFT_EXAMPLES)
         MESSAGE(STATUS "")
     ENDIF()
 ENDIF()
-
-if(MEGA)
-    install(TARGETS ${IMPL_TARGET} RUNTIME DESTINATION . LIBRARY DESTINATION .)
-ENDIF()

+ 8 - 52
libs/openal-soft/COPYING

@@ -51,7 +51,7 @@ library.  If the library is modified by someone else and passed on, we
 want its recipients to know that what they have is not the original
 version, so that any problems introduced by others will not reflect on
 the original authors' reputations.
-
+
   Finally, any free program is threatened constantly by software
 patents.  We wish to avoid the danger that companies distributing free
 software will individually obtain patent licenses, thus in effect
@@ -98,7 +98,7 @@ works together with the library.
 
   Note that it is possible for a library to be covered by the ordinary
 General Public License rather than by this special one.
-
+
                   GNU LIBRARY GENERAL PUBLIC LICENSE
    TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
 
@@ -145,7 +145,7 @@ Library.
   You may charge a fee for the physical act of transferring a copy,
 and you may at your option offer warranty protection in exchange for a
 fee.
-
+
   2. You may modify your copy or copies of the Library or any portion
 of it, thus forming a work based on the Library, and copy and
 distribute such modifications or work under the terms of Section 1
@@ -203,7 +203,7 @@ instead of to this License.  (If a newer version than version 2 of the
 ordinary GNU General Public License has appeared, then you can specify
 that version instead if you wish.)  Do not make any other change in
 these notices.
-
+
   Once this change is made in a given copy, it is irreversible for
 that copy, so the ordinary GNU General Public License applies to all
 subsequent copies and derivative works made from that copy.
@@ -254,7 +254,7 @@ Library will still fall under Section 6.)
 distribute the object code for the work under the terms of Section 6.
 Any executables containing that work also fall under Section 6,
 whether or not they are linked directly with the Library itself.
-
+
   6. As an exception to the Sections above, you may also compile or
 link a "work that uses the Library" with the Library to produce a
 work containing portions of the Library, and distribute that work
@@ -308,7 +308,7 @@ restrictions of other proprietary libraries that do not normally
 accompany the operating system.  Such a contradiction means you cannot
 use both them and the Library together in an executable that you
 distribute.
-
+
   7. You may place library facilities that are a work based on the
 Library side-by-side in a single library together with other library
 facilities not covered by this License, and distribute such a combined
@@ -349,7 +349,7 @@ subject to these terms and conditions.  You may not impose any further
 restrictions on the recipients' exercise of the rights granted herein.
 You are not responsible for enforcing compliance by third parties to
 this License.
-
+
   11. If, as a consequence of a court judgment or allegation of patent
 infringement or for any other reason (not limited to patent issues),
 conditions are imposed on you (whether by court order, agreement or
@@ -401,7 +401,7 @@ conditions either of that version or of any later version published by
 the Free Software Foundation.  If the Library does not specify a
 license version number, you may choose any version ever published by
 the Free Software Foundation.
-
+
   14. If you wish to incorporate parts of the Library into other free
 programs whose distribution conditions are incompatible with these,
 write to the author to ask for permission.  For software which is
@@ -435,47 +435,3 @@ SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
 DAMAGES.
 
                      END OF TERMS AND CONDITIONS
-
-           How to Apply These Terms to Your New Libraries
-
-  If you develop a new library, and you want it to be of the greatest
-possible use to the public, we recommend making it free software that
-everyone can redistribute and change.  You can do so by permitting
-redistribution under these terms (or, alternatively, under the terms of the
-ordinary General Public License).
-
-  To apply these terms, attach the following notices to the library.  It is
-safest to attach them to the start of each source file to most effectively
-convey the exclusion of warranty; and each file should have at least the
-"copyright" line and a pointer to where the full notice is found.
-
-    <one line to give the library's name and a brief idea of what it does.>
-    Copyright (C) <year>  <name of author>
-
-    This library is free software; you can redistribute it and/or
-    modify it under the terms of the GNU Library General Public
-    License as published by the Free Software Foundation; either
-    version 2 of the License, or (at your option) any later version.
-
-    This library is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-    Library General Public License for more details.
-
-    You should have received a copy of the GNU Library General Public
-    License along with this library; if not, write to the Free Software
-    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
-
-Also add information on how to contact you by electronic and paper mail.
-
-You should also get your employer (if you work as a programmer) or your
-school, if any, to sign a "copyright disclaimer" for the library, if
-necessary.  Here is a sample; alter the names:
-
-  Yoyodyne, Inc., hereby disclaims all copyright interest in the
-  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
-
-  <signature of Ty Coon>, 1 April 1990
-  Ty Coon, President of Vice
-
-That's all there is to it!

+ 67 - 0
libs/openal-soft/ChangeLog

@@ -1,3 +1,70 @@
+openal-soft-1.19.1:
+
+    Implemented capture support for the SoundIO backend.
+
+    Fixed source buffer queues potentially not playing properly when a queue
+    entry completes.
+
+    Fixed possible unexpected failures when generating auxiliary effect slots.
+
+    Fixed a crash with certain reverb or device settings.
+
+    Fixed OpenSL capture.
+
+    Improved output limiter response, better ensuring the sample amplitude is
+    clamped for output.
+
+openal-soft-1.19.0:
+
+    Implemented the ALC_SOFT_device_clock extension.
+
+    Implemented the Pitch Shifter, Frequency Shifter, and Autowah effects.
+
+    Fixed compiling on FreeBSD systems that use freebsd-lib 9.1.
+
+    Fixed compiling on NetBSD.
+
+    Fixed the reverb effect's density scale and panning parameters.
+
+    Fixed use of the WASAPI backend with certain games, which caused odd COM
+    initialization errors.
+
+    Increased the number of virtual channels for decoding Ambisonics to HRTF
+    output.
+
+    Changed 32-bit x86 builds to use SSE2 math by default for performance.
+    Build-time options are available to use just SSE1 or x87 instead.
+
+    Replaced the 4-point Sinc resampler with a more efficient cubic resampler.
+
+    Renamed the MMDevAPI backend to WASAPI.
+
+    Added support for 24-bit, dual-ear HRTF data sets. The built-in data set
+    has been updated to 24-bit.
+
+    Added a 24- to 48-point band-limited Sinc resampler.
+
+    Added an SDL2 playback backend. Disabled by default to avoid a dependency
+    on SDL2.
+
+    Improved the performance and quality of the Chorus and Flanger effects.
+
+    Improved the efficiency of the band-limited Sinc resampler.
+
+    Improved the Sinc resampler's transition band to avoid over-attenuating
+    higher frequencies.
+
+    Improved the performance of some filter operations.
+
+    Improved the efficiency of object ID lookups.
+
+    Improved the efficienty of internal voice/source synchronization.
+
+    Improved AL call error logging with contextualized messages.
+
+    Removed the reverb effect's modulation stage. Due to the lack of reference
+    for its intended behavior and strength.
+
 openal-soft-1.18.2:
 
     Fixed resetting the FPU rounding mode after certain function calls on

+ 4 - 2
libs/openal-soft/OpenAL32/Include/alAuxEffectSlot.h

@@ -145,8 +145,8 @@ typedef struct ALeffectslot {
      * * Channel 3 is OpenAL -Z * sqrt(3)
      * Consequently, effects that only want to work with mono input can use
      * channel 0 by itself. Effects that want multichannel can process the
-     * ambisonics signal and make a B-Format pan (ComputeFirstOrderGains) for
-     * first-order device output (FOAOut).
+     * ambisonics signal and make a B-Format source pan for first-order device
+     * output (FOAOut).
      */
     alignas(16) ALfloat WetBuffer[MAX_EFFECT_CHANNELS][BUFFERSIZE];
 } ALeffectslot;
@@ -160,12 +160,14 @@ ALvoid ReleaseALAuxiliaryEffectSlots(ALCcontext *Context);
 
 EffectStateFactory *NullStateFactory_getFactory(void);
 EffectStateFactory *ReverbStateFactory_getFactory(void);
+EffectStateFactory *AutowahStateFactory_getFactory(void);
 EffectStateFactory *ChorusStateFactory_getFactory(void);
 EffectStateFactory *CompressorStateFactory_getFactory(void);
 EffectStateFactory *DistortionStateFactory_getFactory(void);
 EffectStateFactory *EchoStateFactory_getFactory(void);
 EffectStateFactory *EqualizerStateFactory_getFactory(void);
 EffectStateFactory *FlangerStateFactory_getFactory(void);
+EffectStateFactory *FshifterStateFactory_getFactory(void);
 EffectStateFactory *ModulatorStateFactory_getFactory(void);
 EffectStateFactory *PshifterStateFactory_getFactory(void);
 

+ 18 - 1
libs/openal-soft/OpenAL32/Include/alEffect.h

@@ -12,12 +12,14 @@ struct ALeffect;
 enum {
     EAXREVERB_EFFECT = 0,
     REVERB_EFFECT,
+    AUTOWAH_EFFECT,
     CHORUS_EFFECT,
     COMPRESSOR_EFFECT,
     DISTORTION_EFFECT,
     ECHO_EFFECT,
     EQUALIZER_EFFECT,
     FLANGER_EFFECT,
+    FSHIFTER_EFFECT,
     MODULATOR_EFFECT,
     PSHIFTER_EFFECT,
     DEDICATED_EFFECT,
@@ -33,7 +35,7 @@ struct EffectList {
     int type;
     ALenum val;
 };
-#define EFFECTLIST_SIZE 12
+#define EFFECTLIST_SIZE 14
 extern const struct EffectList EffectList[EFFECTLIST_SIZE];
 
 
@@ -59,12 +61,14 @@ const struct ALeffectVtable T##_vtable = {  \
 
 extern const struct ALeffectVtable ALeaxreverb_vtable;
 extern const struct ALeffectVtable ALreverb_vtable;
+extern const struct ALeffectVtable ALautowah_vtable;
 extern const struct ALeffectVtable ALchorus_vtable;
 extern const struct ALeffectVtable ALcompressor_vtable;
 extern const struct ALeffectVtable ALdistortion_vtable;
 extern const struct ALeffectVtable ALecho_vtable;
 extern const struct ALeffectVtable ALequalizer_vtable;
 extern const struct ALeffectVtable ALflanger_vtable;
+extern const struct ALeffectVtable ALfshifter_vtable;
 extern const struct ALeffectVtable ALmodulator_vtable;
 extern const struct ALeffectVtable ALnull_vtable;
 extern const struct ALeffectVtable ALpshifter_vtable;
@@ -101,6 +105,13 @@ typedef union ALeffectProps {
         ALfloat LFReference;
     } Reverb;
 
+    struct {
+        ALfloat AttackTime;
+        ALfloat ReleaseTime;
+        ALfloat Resonance;
+        ALfloat PeakGain;
+    } Autowah;
+
     struct {
         ALint Waveform;
         ALint Phase;
@@ -145,6 +156,12 @@ typedef union ALeffectProps {
         ALfloat HighGain;
     } Equalizer;
 
+    struct {
+        ALfloat Frequency;
+        ALint LeftDirection;
+        ALint RightDirection;
+    } Fshifter;
+
     struct {
         ALfloat Frequency;
         ALfloat HighPassCutoff;

+ 189 - 32
libs/openal-soft/OpenAL32/Include/alMain.h

@@ -40,13 +40,39 @@
 #define SZFMT "%zu"
 #endif
 
+#ifdef __has_builtin
+#define HAS_BUILTIN __has_builtin
+#else
+#define HAS_BUILTIN(x) (0)
+#endif
 
 #ifdef __GNUC__
+/* LIKELY optimizes the case where the condition is true. The condition is not
+ * required to be true, but it can result in more optimal code for the true
+ * path at the expense of a less optimal false path.
+ */
 #define LIKELY(x) __builtin_expect(!!(x), !0)
+/* The opposite of LIKELY, optimizing the case where the condition is false. */
 #define UNLIKELY(x) __builtin_expect(!!(x), 0)
+/* Unlike LIKELY, ASSUME requires the condition to be true or else it invokes
+ * undefined behavior. It's essentially an assert without actually checking the
+ * condition at run-time, allowing for stronger optimizations than LIKELY.
+ */
+#if HAS_BUILTIN(__builtin_assume)
+#define ASSUME __builtin_assume
 #else
+#define ASSUME(x) do { if(!(x)) __builtin_unreachable(); } while(0)
+#endif
+
+#else
+
 #define LIKELY(x) (!!(x))
 #define UNLIKELY(x) (!!(x))
+#ifdef _MSC_VER
+#define ASSUME __assume
+#else
+#define ASSUME(x) ((void)0)
+#endif
 #endif
 
 #ifndef UINT64_MAX
@@ -89,15 +115,25 @@ typedef ALuint64SOFT ALuint64;
 #endif
 #endif
 
+#ifndef I64
+#if defined(_MSC_VER)
+#define I64(x) ((ALint64)(x##i64))
+#elif SIZEOF_LONG == 8
+#define I64(x) ((ALint64)(x##l))
+#elif SIZEOF_LONG_LONG == 8
+#define I64(x) ((ALint64)(x##ll))
+#endif
+#endif
+
 /* Define a CTZ64 macro (count trailing zeros, for 64-bit integers). The result
  * is *UNDEFINED* if the value is 0.
  */
 #ifdef __GNUC__
 
 #if SIZEOF_LONG == 8
-#define CTZ64(x) __builtin_ctzl(x)
+#define CTZ64 __builtin_ctzl
 #else
-#define CTZ64(x) __builtin_ctzll(x)
+#define CTZ64 __builtin_ctzll
 #endif
 
 #elif defined(HAVE_BITSCANFORWARD64_INTRINSIC)
@@ -108,7 +144,7 @@ inline int msvc64_ctz64(ALuint64 v)
     _BitScanForward64(&idx, v);
     return (int)idx;
 }
-#define CTZ64(x) msvc64_ctz64(x)
+#define CTZ64 msvc64_ctz64
 
 #elif defined(HAVE_BITSCANFORWARD_INTRINSIC)
 
@@ -122,7 +158,7 @@ inline int msvc_ctz64(ALuint64 v)
     }
     return (int)idx;
 }
-#define CTZ64(x) msvc_ctz64(x)
+#define CTZ64 msvc_ctz64
 
 #else
 
@@ -145,14 +181,18 @@ inline int fallback_ctz64(ALuint64 value)
 {
     return fallback_popcnt64(~value & (value - 1));
 }
-#define CTZ64(x) fallback_ctz64(x)
+#define CTZ64 fallback_ctz64
 #endif
 
+#if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__)
+#define IS_LITTLE_ENDIAN (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
+#else
 static const union {
     ALuint u;
     ALubyte b[sizeof(ALuint)];
 } EndianTest = { 1 };
 #define IS_LITTLE_ENDIAN (EndianTest.b[0] == 1)
+#endif
 
 #define COUNTOF(x) (sizeof(x) / sizeof(0[x]))
 
@@ -200,22 +240,132 @@ inline size_t RoundUp(size_t value, size_t r)
     return value - (value%r);
 }
 
-/* Fast float-to-int conversion. Assumes the FPU is already in round-to-zero
- * mode. */
+/* Fast float-to-int conversion. No particular rounding mode is assumed; the
+ * IEEE-754 default is round-to-nearest with ties-to-even, though an app could
+ * change it on its own threads. On some systems, a truncating conversion may
+ * always be the fastest method.
+ */
 inline ALint fastf2i(ALfloat f)
 {
-#ifdef HAVE_LRINTF
-    return lrintf(f);
-#elif defined(_MSC_VER) && defined(_M_IX86)
+#if defined(HAVE_INTRIN_H) && ((defined(_M_IX86_FP) && (_M_IX86_FP > 0)) || defined(_M_X64))
+    return _mm_cvt_ss2si(_mm_set1_ps(f));
+
+#elif defined(_MSC_VER) && defined(_M_IX86_FP)
+
     ALint i;
     __asm fld f
     __asm fistp i
     return i;
+
+#elif (defined(__GNUC__) || defined(__clang__)) && (defined(__i386__) || defined(__x86_64__))
+
+    ALint i;
+#ifdef __SSE_MATH__
+    __asm__("cvtss2si %1, %0" : "=r"(i) : "x"(f));
 #else
+    __asm__ __volatile__("fistpl %0" : "=m"(i) : "t"(f) : "st");
+#endif
+    return i;
+
+    /* On GCC when compiling with -fno-math-errno, lrintf can be inlined to
+     * some simple instructions. Clang does not inline it, always generating a
+     * libc call, while MSVC's implementation is horribly slow, so always fall
+     * back to a normal integer conversion for them.
+     */
+#elif defined(HAVE_LRINTF) && !defined(_MSC_VER) && !defined(__clang__)
+
+    return lrintf(f);
+
+#else
+
     return (ALint)f;
 #endif
 }
 
+/* Converts float-to-int using standard behavior (truncation). */
+inline int float2int(float f)
+{
+#if ((defined(__GNUC__) || defined(__clang__)) && (defined(__i386__) || defined(__x86_64__)) && \
+     !defined(__SSE_MATH__)) || (defined(_MSC_VER) && defined(_M_IX86_FP) && _M_IX86_FP == 0)
+    ALint sign, shift, mant;
+    union {
+        ALfloat f;
+        ALint i;
+    } conv;
+
+    conv.f = f;
+    sign = (conv.i>>31) | 1;
+    shift = ((conv.i>>23)&0xff) - (127+23);
+
+    /* Over/underflow */
+    if(UNLIKELY(shift >= 31 || shift < -23))
+        return 0;
+
+    mant = (conv.i&0x7fffff) | 0x800000;
+    if(LIKELY(shift < 0))
+        return (mant >> -shift) * sign;
+    return (mant << shift) * sign;
+
+#else
+
+    return (ALint)f;
+#endif
+}
+
+/* Rounds a float to the nearest integral value, according to the current
+ * rounding mode. This is essentially an inlined version of rintf, although
+ * makes fewer promises (e.g. -0 or -0.25 rounded to 0 may result in +0).
+ */
+inline float fast_roundf(float f)
+{
+#if (defined(__GNUC__) || defined(__clang__)) && (defined(__i386__) || defined(__x86_64__)) && \
+    !defined(__SSE_MATH__)
+
+    float out;
+    __asm__ __volatile__("frndint" : "=t"(out) : "0"(f));
+    return out;
+
+#else
+
+    /* Integral limit, where sub-integral precision is not available for
+     * floats.
+     */
+    static const float ilim[2] = {
+         8388608.0f /*  0x1.0p+23 */,
+        -8388608.0f /* -0x1.0p+23 */
+    };
+    ALuint sign, expo;
+    union {
+        ALfloat f;
+        ALuint i;
+    } conv;
+
+    conv.f = f;
+    sign = (conv.i>>31)&0x01;
+    expo = (conv.i>>23)&0xff;
+
+    if(UNLIKELY(expo >= 150/*+23*/))
+    {
+        /* An exponent (base-2) of 23 or higher is incapable of sub-integral
+         * precision, so it's already an integral value. We don't need to worry
+         * about infinity or NaN here.
+         */
+        return f;
+    }
+    /* Adding the integral limit to the value (with a matching sign) forces a
+     * result that has no sub-integral precision, and is consequently forced to
+     * round to an integral value. Removing the integral limit then restores
+     * the initial value rounded to the integral. The compiler should not
+     * optimize this out because of non-associative rules on floating-point
+     * math (as long as you don't use -fassociative-math,
+     * -funsafe-math-optimizations, -ffast-math, or -Ofast, in which case this
+     * may break).
+     */
+    f += ilim[sign];
+    return f - ilim[sign];
+#endif
+}
+
 
 enum DevProbe {
     ALL_DEVICE_PROBE,
@@ -432,7 +582,7 @@ typedef struct DistanceComp {
  */
 #define BUFFERSIZE 2048
 
-typedef struct DryMixParams {
+typedef struct MixParams {
     AmbiConfig Ambi;
     /* Number of coefficients in each Ambi.Coeffs to mix together (4 for first-
      * order, 9 for second-order, etc). If the count is 0, Ambi.Map is used
@@ -442,17 +592,7 @@ typedef struct DryMixParams {
 
     ALfloat (*Buffer)[BUFFERSIZE];
     ALsizei NumChannels;
-    ALsizei NumChannelsPerOrder[MAX_AMBI_ORDER+1];
-} DryMixParams;
-
-typedef struct BFMixParams {
-    AmbiConfig Ambi;
-    /* Will only be 4 or 0. */
-    ALsizei CoeffCount;
-
-    ALfloat (*Buffer)[BUFFERSIZE];
-    ALsizei NumChannels;
-} BFMixParams;
+} MixParams;
 
 typedef struct RealMixParams {
     enum Channel ChannelName[MAX_OUTPUT_CHANNELS];
@@ -482,6 +622,8 @@ struct ALCdevice_struct {
     enum AmbiLayout AmbiLayout;
     enum AmbiNorm   AmbiScale;
 
+    ALCenum LimiterState;
+
     al_string DeviceName;
 
     ATOMIC(ALCenum) LastError;
@@ -536,15 +678,17 @@ struct ALCdevice_struct {
 
     ALuint64 ClockBase;
     ALuint SamplesDone;
+    ALuint FixedLatency;
 
     /* Temp storage used for mixer processing. */
     alignas(16) ALfloat TempBuffer[4][BUFFERSIZE];
 
     /* The "dry" path corresponds to the main output. */
-    DryMixParams Dry;
+    MixParams Dry;
+    ALsizei NumChannelsPerOrder[MAX_AMBI_ORDER+1];
 
     /* First-order ambisonics output, to be upsampled to the dry buffer if different. */
-    BFMixParams FOAOut;
+    MixParams FOAOut;
 
     /* "Real" output, which will be written to the device buffer. May alias the
      * dry buffer.
@@ -609,21 +753,35 @@ struct ALCdevice_struct {
 
 
 enum {
+    /* End event thread processing. */
+    EventType_KillThread = 0,
+
+    /* User event types. */
     EventType_SourceStateChange = 1<<0,
     EventType_BufferCompleted   = 1<<1,
     EventType_Error             = 1<<2,
     EventType_Performance       = 1<<3,
     EventType_Deprecated        = 1<<4,
     EventType_Disconnected      = 1<<5,
+
+    /* Internal events. */
+    EventType_ReleaseEffectState = 65536,
 };
 
 typedef struct AsyncEvent {
     unsigned int EnumType;
-    ALenum Type;
-    ALuint ObjectId;
-    ALuint Param;
-    ALchar Message[1008];
+    union {
+        char dummy;
+        struct {
+            ALenum type;
+            ALuint id;
+            ALuint param;
+            ALchar msg[1008];
+        } user;
+        struct ALeffectState *EffectState;
+    } u;
 } AsyncEvent;
+#define ASYNC_EVENT(t) { t, { 0 } }
 
 struct ALCcontext_struct {
     RefCount ref;
@@ -676,7 +834,6 @@ struct ALCcontext_struct {
 
     ATOMIC(struct ALeffectslotArray*) ActiveAuxSlots;
 
-    almtx_t EventThrdLock;
     althrd_t EventThread;
     alsem_t EventSem;
     struct ll_ringbuffer *AsyncEvents;
@@ -706,9 +863,6 @@ void ALCcontext_ProcessUpdates(ALCcontext *context);
 
 void AllocateVoices(ALCcontext *context, ALsizei num_voices, ALsizei old_sends);
 
-void AppendAllDevicesList(const ALCchar *name);
-void AppendCaptureDeviceList(const ALCchar *name);
-
 
 extern ALint RTPrioLevel;
 void SetRTPriority(void);
@@ -754,6 +908,9 @@ inline void UnlockEffectSlotList(ALCcontext *context)
 { almtx_unlock(&context->EffectSlotLock); }
 
 
+int EventThread(void *arg);
+
+
 vector_al_string SearchDataFiles(const char *match, const char *subdir);
 
 #ifdef __cplusplus

+ 53 - 43
libs/openal-soft/OpenAL32/Include/alu.h

@@ -74,7 +74,7 @@ extern enum Resampler ResamplerDefault;
 typedef struct BsincState {
     ALfloat sf; /* Scale interpolation factor. */
     ALsizei m;  /* Coefficient count. */
-    ALint l;    /* Left coefficient offset. */
+    ALsizei l;  /* Left coefficient offset. */
     /* Filter coefficients, followed by the scale, phase, and scale-phase
      * delta coefficients. Starting at phase index 0, each subsequent phase
      * index follows contiguously.
@@ -153,8 +153,8 @@ typedef struct MixHrtfParams {
 
 
 typedef struct DirectParams {
-    BiquadState LowPass;
-    BiquadState HighPass;
+    BiquadFilter LowPass;
+    BiquadFilter HighPass;
 
     NfcFilter NFCtrlFilter;
 
@@ -171,8 +171,8 @@ typedef struct DirectParams {
 } DirectParams;
 
 typedef struct SendParams {
-    BiquadState LowPass;
-    BiquadState HighPass;
+    BiquadFilter LowPass;
+    BiquadFilter HighPass;
 
     struct {
         ALfloat Current[MAX_OUTPUT_CHANNELS];
@@ -430,14 +430,35 @@ void aluInitEffectPanning(struct ALeffectslot *slot);
 
 void aluSelectPostProcess(ALCdevice *device);
 
+/**
+ * Calculates ambisonic encoder coefficients using the X, Y, and Z direction
+ * components, which must represent a normalized (unit length) vector, and the
+ * spread is the angular width of the sound (0...tau).
+ *
+ * NOTE: The components use ambisonic coordinates. As a result:
+ *
+ * Ambisonic Y = OpenAL -X
+ * Ambisonic Z = OpenAL Y
+ * Ambisonic X = OpenAL -Z
+ *
+ * The components are ordered such that OpenAL's X, Y, and Z are the first,
+ * second, and third parameters respectively -- simply negate X and Z.
+ */
+void CalcAmbiCoeffs(const ALfloat y, const ALfloat z, const ALfloat x, const ALfloat spread,
+                    ALfloat coeffs[MAX_AMBI_COEFFS]);
+
 /**
  * CalcDirectionCoeffs
  *
- * Calculates ambisonic coefficients based on a direction vector. The vector
- * must be normalized (unit length), and the spread is the angular width of the
- * sound (0...tau).
+ * Calculates ambisonic coefficients based on an OpenAL direction vector. The
+ * vector must be normalized (unit length), and the spread is the angular width
+ * of the sound (0...tau).
  */
-void CalcDirectionCoeffs(const ALfloat dir[3], ALfloat spread, ALfloat coeffs[MAX_AMBI_COEFFS]);
+inline void CalcDirectionCoeffs(const ALfloat dir[3], ALfloat spread, ALfloat coeffs[MAX_AMBI_COEFFS])
+{
+    /* Convert from OpenAL coords to Ambisonics. */
+    CalcAmbiCoeffs(-dir[0], dir[1], -dir[2], spread, coeffs);
+}
 
 /**
  * CalcAngleCoeffs
@@ -448,34 +469,40 @@ void CalcDirectionCoeffs(const ALfloat dir[3], ALfloat spread, ALfloat coeffs[MA
  */
 inline void CalcAngleCoeffs(ALfloat azimuth, ALfloat elevation, ALfloat spread, ALfloat coeffs[MAX_AMBI_COEFFS])
 {
-    ALfloat dir[3] = {
-        sinf(azimuth) * cosf(elevation),
-        sinf(elevation),
-        -cosf(azimuth) * cosf(elevation)
-    };
-    CalcDirectionCoeffs(dir, spread, coeffs);
+    ALfloat x = -sinf(azimuth) * cosf(elevation);
+    ALfloat y = sinf(elevation);
+    ALfloat z = cosf(azimuth) * cosf(elevation);
+
+    CalcAmbiCoeffs(x, y, z, spread, coeffs);
 }
 
 /**
- * CalcAnglePairwiseCoeffs
+ * ScaleAzimuthFront
  *
- * Calculates ambisonic coefficients based on azimuth and elevation. The
- * azimuth and elevation parameters are in radians, going right and up
- * respectively. This pairwise variant warps the result such that +30 azimuth
- * is full right, and -30 azimuth is full left.
+ * Scales the given azimuth toward the side (+/- pi/2 radians) for positions in
+ * front.
  */
-void CalcAnglePairwiseCoeffs(ALfloat azimuth, ALfloat elevation, ALfloat spread, ALfloat coeffs[MAX_AMBI_COEFFS]);
+inline float ScaleAzimuthFront(float azimuth, float scale)
+{
+    ALfloat sign = copysignf(1.0f, azimuth);
+    if(!(fabsf(azimuth) > F_PI_2))
+        return minf(fabsf(azimuth) * scale, F_PI_2) * sign;
+    return azimuth;
+}
+
 
+void ComputePanningGainsMC(const ChannelConfig *chancoeffs, ALsizei numchans, ALsizei numcoeffs, const ALfloat*restrict coeffs, ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]);
+void ComputePanningGainsBF(const BFChannelConfig *chanmap, ALsizei numchans, const ALfloat*restrict coeffs, ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]);
 
-void ComputePanningGainsMC(const ChannelConfig *chancoeffs, ALsizei numchans, ALsizei numcoeffs, const ALfloat coeffs[MAX_AMBI_COEFFS], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]);
-void ComputePanningGainsBF(const BFChannelConfig *chanmap, ALsizei numchans, const ALfloat coeffs[MAX_AMBI_COEFFS], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]);
 /**
- * ComputeDryPanGains
+ * ComputePanGains
  *
  * Computes panning gains using the given channel decoder coefficients and the
- * pre-calculated direction or angle coefficients.
+ * pre-calculated direction or angle coefficients. For B-Format sources, the
+ * coeffs are a 'slice' of a transform matrix for the input channel, used to
+ * scale and orient the sound samples.
  */
-inline void ComputeDryPanGains(const DryMixParams *dry, const ALfloat coeffs[MAX_AMBI_COEFFS], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS])
+inline void ComputePanGains(const MixParams *dry, const ALfloat*restrict coeffs, ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS])
 {
     if(dry->CoeffCount > 0)
         ComputePanningGainsMC(dry->Ambi.Coeffs, dry->NumChannels, dry->CoeffCount,
@@ -484,23 +511,6 @@ inline void ComputeDryPanGains(const DryMixParams *dry, const ALfloat coeffs[MAX
         ComputePanningGainsBF(dry->Ambi.Map, dry->NumChannels, coeffs, ingain, gains);
 }
 
-void ComputeFirstOrderGainsMC(const ChannelConfig *chancoeffs, ALsizei numchans, const ALfloat mtx[4], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]);
-void ComputeFirstOrderGainsBF(const BFChannelConfig *chanmap, ALsizei numchans, const ALfloat mtx[4], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]);
-/**
- * ComputeFirstOrderGains
- *
- * Sets channel gains for a first-order ambisonics input channel. The matrix is
- * a 1x4 'slice' of a transform matrix for the input channel, used to scale and
- * orient the sound samples.
- */
-inline void ComputeFirstOrderGains(const BFMixParams *foa, const ALfloat mtx[4], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS])
-{
-    if(foa->CoeffCount > 0)
-        ComputeFirstOrderGainsMC(foa->Ambi.Coeffs, foa->NumChannels, mtx, ingain, gains);
-    else
-        ComputeFirstOrderGainsBF(foa->Ambi.Map, foa->NumChannels, mtx, ingain, gains);
-}
-
 
 ALboolean MixSource(struct ALvoice *voice, ALuint SourceID, ALCcontext *Context, ALsizei SamplesToDo);
 

+ 12 - 6
libs/openal-soft/OpenAL32/alAuxEffectSlot.c

@@ -48,12 +48,14 @@ static const struct {
     { AL_EFFECT_NULL, NullStateFactory_getFactory },
     { AL_EFFECT_EAXREVERB, ReverbStateFactory_getFactory },
     { AL_EFFECT_REVERB, ReverbStateFactory_getFactory },
+    { AL_EFFECT_AUTOWAH, AutowahStateFactory_getFactory },
     { AL_EFFECT_CHORUS, ChorusStateFactory_getFactory },
     { AL_EFFECT_COMPRESSOR, CompressorStateFactory_getFactory },
     { AL_EFFECT_DISTORTION, DistortionStateFactory_getFactory },
     { AL_EFFECT_ECHO, EchoStateFactory_getFactory },
     { AL_EFFECT_EQUALIZER, EqualizerStateFactory_getFactory },
     { AL_EFFECT_FLANGER, FlangerStateFactory_getFactory },
+    { AL_EFFECT_FREQUENCY_SHIFTER, FshifterStateFactory_getFactory },
     { AL_EFFECT_RING_MODULATOR, ModulatorStateFactory_getFactory },
     { AL_EFFECT_PITCH_SHIFTER, PshifterStateFactory_getFactory},
     { AL_EFFECT_DEDICATED_DIALOGUE, DedicatedStateFactory_getFactory },
@@ -120,12 +122,6 @@ AL_API ALvoid AL_APIENTRY alGenAuxiliaryEffectSlots(ALsizei n, ALuint *effectslo
 
     LockEffectSlotList(context);
     device = context->Device;
-    if(device->AuxiliaryEffectSlotMax - VECTOR_SIZE(context->EffectSlotList) < (ALuint)n)
-    {
-        UnlockEffectSlotList(context);
-        SETERR_GOTO(context, AL_OUT_OF_MEMORY, done, "Exceeding %u auxiliary effect slot limit",
-                    device->AuxiliaryEffectSlotMax);
-    }
     for(cur = 0;cur < n;cur++)
     {
         ALeffectslotPtr *iter = VECTOR_BEGIN(context->EffectSlotList);
@@ -140,6 +136,13 @@ AL_API ALvoid AL_APIENTRY alGenAuxiliaryEffectSlots(ALsizei n, ALuint *effectslo
         }
         if(iter == end)
         {
+            if(device->AuxiliaryEffectSlotMax == VECTOR_SIZE(context->EffectSlotList))
+            {
+                UnlockEffectSlotList(context);
+                alDeleteAuxiliaryEffectSlots(cur, effectslots);
+                SETERR_GOTO(context, AL_OUT_OF_MEMORY, done,
+                    "Exceeding %u auxiliary effect slot limit", device->AuxiliaryEffectSlotMax);
+            }
             VECTOR_PUSH_BACK(context->EffectSlotList, NULL);
             iter = &VECTOR_BACK(context->EffectSlotList);
         }
@@ -750,6 +753,9 @@ void UpdateEffectSlotProps(ALeffectslot *slot, ALCcontext *context)
         /* If there was an unused update container, put it back in the
          * freelist.
          */
+        if(props->State)
+            ALeffectState_DecRef(props->State);
+        props->State = NULL;
         ATOMIC_REPLACE_HEAD(struct ALeffectslotProps*, &context->FreeEffectslotProps, props);
     }
 

+ 15 - 0
libs/openal-soft/OpenAL32/alEffect.c

@@ -38,12 +38,14 @@ extern inline ALboolean IsReverbEffect(ALenum type);
 const struct EffectList EffectList[EFFECTLIST_SIZE] = {
     { "eaxreverb",  EAXREVERB_EFFECT,  AL_EFFECT_EAXREVERB },
     { "reverb",     REVERB_EFFECT,     AL_EFFECT_REVERB },
+    { "autowah",    AUTOWAH_EFFECT,    AL_EFFECT_AUTOWAH },
     { "chorus",     CHORUS_EFFECT,     AL_EFFECT_CHORUS },
     { "compressor", COMPRESSOR_EFFECT, AL_EFFECT_COMPRESSOR },
     { "distortion", DISTORTION_EFFECT, AL_EFFECT_DISTORTION },
     { "echo",       ECHO_EFFECT,       AL_EFFECT_ECHO },
     { "equalizer",  EQUALIZER_EFFECT,  AL_EFFECT_EQUALIZER },
     { "flanger",    FLANGER_EFFECT,    AL_EFFECT_FLANGER },
+    { "fshifter",   FSHIFTER_EFFECT,   AL_EFFECT_FREQUENCY_SHIFTER },
     { "modulator",  MODULATOR_EFFECT,  AL_EFFECT_RING_MODULATOR },
     { "pshifter",   PSHIFTER_EFFECT,   AL_EFFECT_PITCH_SHIFTER },
     { "dedicated",  DEDICATED_EFFECT,  AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT },
@@ -533,6 +535,13 @@ static void InitEffectParams(ALeffect *effect, ALenum type)
         effect->Props.Reverb.DecayHFLimit = AL_REVERB_DEFAULT_DECAY_HFLIMIT;
         effect->vtab = &ALreverb_vtable;
         break;
+    case AL_EFFECT_AUTOWAH:
+        effect->Props.Autowah.AttackTime = AL_AUTOWAH_DEFAULT_ATTACK_TIME;
+        effect->Props.Autowah.ReleaseTime = AL_AUTOWAH_DEFAULT_RELEASE_TIME;
+        effect->Props.Autowah.Resonance = AL_AUTOWAH_DEFAULT_RESONANCE;
+        effect->Props.Autowah.PeakGain = AL_AUTOWAH_DEFAULT_PEAK_GAIN;
+        effect->vtab = &ALautowah_vtable;
+        break;
     case AL_EFFECT_CHORUS:
         effect->Props.Chorus.Waveform = AL_CHORUS_DEFAULT_WAVEFORM;
         effect->Props.Chorus.Phase = AL_CHORUS_DEFAULT_PHASE;
@@ -584,6 +593,12 @@ static void InitEffectParams(ALeffect *effect, ALenum type)
         effect->Props.Chorus.Delay = AL_FLANGER_DEFAULT_DELAY;
         effect->vtab = &ALflanger_vtable;
         break;
+        case AL_EFFECT_FREQUENCY_SHIFTER:
+        effect->Props.Fshifter.Frequency      = AL_FREQUENCY_SHIFTER_DEFAULT_FREQUENCY;
+        effect->Props.Fshifter.LeftDirection  = AL_FREQUENCY_SHIFTER_DEFAULT_LEFT_DIRECTION;
+        effect->Props.Fshifter.RightDirection = AL_FREQUENCY_SHIFTER_DEFAULT_RIGHT_DIRECTION;
+        effect->vtab = &ALfshifter_vtable;
+        break;
     case AL_EFFECT_RING_MODULATOR:
         effect->Props.Modulator.Frequency      = AL_RING_MODULATOR_DEFAULT_FREQUENCY;
         effect->Props.Modulator.HighPassCutoff = AL_RING_MODULATOR_DEFAULT_HIGHPASS_CUTOFF;

+ 6 - 3
libs/openal-soft/OpenAL32/alFilter.c

@@ -28,6 +28,9 @@
 #include "alError.h"
 
 
+#define FILTER_MIN_GAIN 0.0f
+#define FILTER_MAX_GAIN 4.0f /* +12dB */
+
 extern inline void LockFilterList(ALCdevice *device);
 extern inline void UnlockFilterList(ALCdevice *device);
 
@@ -347,7 +350,7 @@ static void ALlowpass_setParamf(ALfilter *filter, ALCcontext *context, ALenum pa
     switch(param)
     {
         case AL_LOWPASS_GAIN:
-            if(!(val >= AL_LOWPASS_MIN_GAIN && val <= AL_LOWPASS_MAX_GAIN))
+            if(!(val >= FILTER_MIN_GAIN && val <= FILTER_MAX_GAIN))
                 SETERR_RETURN(context, AL_INVALID_VALUE,, "Low-pass gain %f out of range", val);
             filter->Gain = val;
             break;
@@ -400,7 +403,7 @@ static void ALhighpass_setParamf(ALfilter *filter, ALCcontext *context, ALenum p
     switch(param)
     {
         case AL_HIGHPASS_GAIN:
-            if(!(val >= AL_HIGHPASS_MIN_GAIN && val <= AL_HIGHPASS_MAX_GAIN))
+            if(!(val >= FILTER_MIN_GAIN && val <= FILTER_MAX_GAIN))
                 SETERR_RETURN(context, AL_INVALID_VALUE,, "High-pass gain out of range");
             filter->Gain = val;
             break;
@@ -453,7 +456,7 @@ static void ALbandpass_setParamf(ALfilter *filter, ALCcontext *context, ALenum p
     switch(param)
     {
         case AL_BANDPASS_GAIN:
-            if(!(val >= AL_BANDPASS_MIN_GAIN && val <= AL_BANDPASS_MAX_GAIN))
+            if(!(val >= FILTER_MIN_GAIN && val <= FILTER_MAX_GAIN))
                 SETERR_RETURN(context, AL_INVALID_VALUE,, "Band-pass gain out of range");
             filter->Gain = val;
             break;

+ 122 - 8
libs/openal-soft/OpenAL32/alSource.c

@@ -229,17 +229,16 @@ static inline bool SourceShouldUpdate(ALsource *source, ALCcontext *context)
 /** Can only be called while the mixer is locked! */
 static void SendStateChangeEvent(ALCcontext *context, ALuint id, ALenum state)
 {
+    AsyncEvent evt = ASYNC_EVENT(EventType_SourceStateChange);
     ALbitfieldSOFT enabledevt;
-    AsyncEvent evt;
 
     enabledevt = ATOMIC_LOAD(&context->EnabledEvts, almemory_order_acquire);
     if(!(enabledevt&EventType_SourceStateChange)) return;
 
-    evt.EnumType = EventType_SourceStateChange;
-    evt.Type = AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT;
-    evt.ObjectId = id;
-    evt.Param = state;
-    snprintf(evt.Message, sizeof(evt.Message), "Source ID %u state changed to %s", id,
+    evt.u.user.type = AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT;
+    evt.u.user.id = id;
+    evt.u.user.param = state;
+    snprintf(evt.u.user.msg, sizeof(evt.u.user.msg), "Source ID %u state changed to %s", id,
         (state==AL_INITIAL) ? "AL_INITIAL" :
         (state==AL_PLAYING) ? "AL_PLAYING" :
         (state==AL_PAUSED) ? "AL_PAUSED" :
@@ -1296,7 +1295,7 @@ static ALboolean GetSourcedv(ALsource *Source, ALCcontext *Context, SourceProp p
              */
             values[0] = GetSourceSecOffset(Source, Context, &srcclock);
             almtx_lock(&device->BackendLock);
-            clocktime = V0(device->Backend,getClockLatency)();
+            clocktime = GetClockLatency(device);
             almtx_unlock(&device->BackendLock);
             if(srcclock == (ALuint64)clocktime.ClockTime)
                 values[1] = (ALdouble)clocktime.Latency / 1000000000.0;
@@ -1560,7 +1559,7 @@ static ALboolean GetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp
              */
             values[0] = GetSourceSampleOffset(Source, Context, &srcclock);
             almtx_lock(&device->BackendLock);
-            clocktime = V0(device->Backend,getClockLatency)();
+            clocktime = GetClockLatency(device);
             almtx_unlock(&device->BackendLock);
             if(srcclock == (ALuint64)clocktime.ClockTime)
                 values[1] = clocktime.Latency;
@@ -2826,6 +2825,121 @@ done:
     ALCcontext_DecRef(context);
 }
 
+AL_API void AL_APIENTRY alSourceQueueBufferLayersSOFT(ALuint src, ALsizei nb, const ALuint *buffers)
+{
+    ALCdevice *device;
+    ALCcontext *context;
+    ALbufferlistitem *BufferListStart;
+    ALbufferlistitem *BufferList;
+    ALbuffer *BufferFmt = NULL;
+    ALsource *source;
+    ALsizei i;
+
+    if(nb == 0)
+        return;
+
+    context = GetContextRef();
+    if(!context) return;
+
+    device = context->Device;
+
+    LockSourceList(context);
+    if(!(nb >= 0 && nb < 16))
+        SETERR_GOTO(context, AL_INVALID_VALUE, done, "Queueing %d buffer layers", nb);
+    if((source=LookupSource(context, src)) == NULL)
+        SETERR_GOTO(context, AL_INVALID_NAME, done, "Invalid source ID %u", src);
+
+    if(source->SourceType == AL_STATIC)
+    {
+        /* Can't queue on a Static Source */
+        SETERR_GOTO(context, AL_INVALID_OPERATION, done, "Queueing onto static source %u", src);
+    }
+
+    /* Check for a valid Buffer, for its frequency and format */
+    BufferList = source->queue;
+    while(BufferList)
+    {
+        for(i = 0;i < BufferList->num_buffers;i++)
+        {
+            if((BufferFmt=BufferList->buffers[i]) != NULL)
+                break;
+        }
+        if(BufferFmt) break;
+        BufferList = ATOMIC_LOAD(&BufferList->next, almemory_order_relaxed);
+    }
+
+    LockBufferList(device);
+    BufferListStart = al_calloc(DEF_ALIGN, FAM_SIZE(ALbufferlistitem, buffers, nb));
+    BufferList = BufferListStart;
+    ATOMIC_INIT(&BufferList->next, NULL);
+    BufferList->max_samples = 0;
+    BufferList->num_buffers = 0;
+    for(i = 0;i < nb;i++)
+    {
+        ALbuffer *buffer = NULL;
+        if(buffers[i] && (buffer=LookupBuffer(device, buffers[i])) == NULL)
+            SETERR_GOTO(context, AL_INVALID_NAME, buffer_error, "Queueing invalid buffer ID %u",
+                        buffers[i]);
+
+        BufferList->buffers[BufferList->num_buffers++] = buffer;
+        if(!buffer) continue;
+
+        IncrementRef(&buffer->ref);
+
+        BufferList->max_samples = maxi(BufferList->max_samples, buffer->SampleLen);
+
+        if(buffer->MappedAccess != 0 && !(buffer->MappedAccess&AL_MAP_PERSISTENT_BIT_SOFT))
+            SETERR_GOTO(context, AL_INVALID_OPERATION, buffer_error,
+                        "Queueing non-persistently mapped buffer %u", buffer->id);
+
+        if(BufferFmt == NULL)
+            BufferFmt = buffer;
+        else if(BufferFmt->Frequency != buffer->Frequency ||
+                BufferFmt->FmtChannels != buffer->FmtChannels ||
+                BufferFmt->OriginalType != buffer->OriginalType)
+        {
+            alSetError(context, AL_INVALID_OPERATION, "Queueing buffer with mismatched format");
+
+        buffer_error:
+            /* A buffer failed (invalid ID or format), so unlock and release
+             * each buffer we had. */
+            while(BufferListStart)
+            {
+                ALbufferlistitem *next = ATOMIC_LOAD(&BufferListStart->next,
+                                                     almemory_order_relaxed);
+                for(i = 0;i < BufferListStart->num_buffers;i++)
+                {
+                    if((buffer=BufferListStart->buffers[i]) != NULL)
+                        DecrementRef(&buffer->ref);
+                }
+                al_free(BufferListStart);
+                BufferListStart = next;
+            }
+            UnlockBufferList(device);
+            goto done;
+        }
+    }
+    /* All buffers good. */
+    UnlockBufferList(device);
+
+    /* Source is now streaming */
+    source->SourceType = AL_STREAMING;
+
+    if(!(BufferList=source->queue))
+        source->queue = BufferListStart;
+    else
+    {
+        ALbufferlistitem *next;
+        while((next=ATOMIC_LOAD(&BufferList->next, almemory_order_relaxed)) != NULL)
+            BufferList = next;
+        ATOMIC_STORE(&BufferList->next, BufferListStart, almemory_order_release);
+    }
+
+done:
+    UnlockSourceList(context);
+    ALCcontext_DecRef(context);
+}
+
 AL_API ALvoid AL_APIENTRY alSourceUnqueueBuffers(ALuint src, ALsizei nb, ALuint *buffers)
 {
     ALCcontext *context;

+ 25 - 38
libs/openal-soft/OpenAL32/event.c

@@ -6,19 +6,16 @@
 #include "AL/alext.h"
 #include "alMain.h"
 #include "alError.h"
+#include "alAuxEffectSlot.h"
 #include "ringbuffer.h"
 
 
-static int EventThread(void *arg)
+int EventThread(void *arg)
 {
     ALCcontext *context = arg;
+    bool quitnow = false;
 
-    /* Clear all pending posts on the semaphore. */
-    while(alsem_trywait(&context->EventSem) == althrd_success)
-    {
-    }
-
-    while(1)
+    while(!quitnow)
     {
         ALbitfieldSOFT enabledevts;
         AsyncEvent evt;
@@ -28,14 +25,24 @@ static int EventThread(void *arg)
             alsem_wait(&context->EventSem);
             continue;
         }
-        if(!evt.EnumType)
-            break;
 
         almtx_lock(&context->EventCbLock);
-        enabledevts = ATOMIC_LOAD(&context->EnabledEvts, almemory_order_acquire);
-        if(context->EventCb && (enabledevts&evt.EnumType) == evt.EnumType)
-            context->EventCb(evt.Type, evt.ObjectId, evt.Param, (ALsizei)strlen(evt.Message),
-                             evt.Message, context->EventParam);
+        do {
+            quitnow = evt.EnumType == EventType_KillThread;
+            if(quitnow) break;
+
+            if(evt.EnumType == EventType_ReleaseEffectState)
+            {
+                ALeffectState_DecRef(evt.u.EffectState);
+                continue;
+            }
+
+            enabledevts = ATOMIC_LOAD(&context->EnabledEvts, almemory_order_acquire);
+            if(context->EventCb && (enabledevts&evt.EnumType) == evt.EnumType)
+                context->EventCb(evt.u.user.type, evt.u.user.id, evt.u.user.param,
+                    (ALsizei)strlen(evt.u.user.msg), evt.u.user.msg, context->EventParam
+                );
+        } while(ll_ringbuffer_read(context->AsyncEvents, (char*)&evt, 1) != 0);
         almtx_unlock(&context->EventCbLock);
     }
     return 0;
@@ -46,7 +53,6 @@ AL_API void AL_APIENTRY alEventControlSOFT(ALsizei count, const ALenum *types, A
     ALCcontext *context;
     ALbitfieldSOFT enabledevts;
     ALbitfieldSOFT flags = 0;
-    bool isrunning;
     ALsizei i;
 
     context = GetContextRef();
@@ -74,13 +80,9 @@ AL_API void AL_APIENTRY alEventControlSOFT(ALsizei count, const ALenum *types, A
             SETERR_GOTO(context, AL_INVALID_ENUM, done, "Invalid event type 0x%04x", types[i]);
     }
 
-    almtx_lock(&context->EventThrdLock);
     if(enable)
     {
-        if(!context->AsyncEvents)
-            context->AsyncEvents = ll_ringbuffer_create(63, sizeof(AsyncEvent), false);
         enabledevts = ATOMIC_LOAD(&context->EnabledEvts, almemory_order_relaxed);
-        isrunning = !!enabledevts;
         while(ATOMIC_COMPARE_EXCHANGE_WEAK(&context->EnabledEvts, &enabledevts, enabledevts|flags,
                                            almemory_order_acq_rel, almemory_order_acquire) == 0)
         {
@@ -88,35 +90,20 @@ AL_API void AL_APIENTRY alEventControlSOFT(ALsizei count, const ALenum *types, A
              * just try again.
              */
         }
-        if(!isrunning && flags)
-            althrd_create(&context->EventThread, EventThread, context);
     }
     else
     {
         enabledevts = ATOMIC_LOAD(&context->EnabledEvts, almemory_order_relaxed);
-        isrunning = !!enabledevts;
         while(ATOMIC_COMPARE_EXCHANGE_WEAK(&context->EnabledEvts, &enabledevts, enabledevts&~flags,
                                            almemory_order_acq_rel, almemory_order_acquire) == 0)
         {
         }
-        if(isrunning && !(enabledevts&~flags))
-        {
-            static const AsyncEvent kill_evt = { 0 };
-            while(ll_ringbuffer_write(context->AsyncEvents, (const char*)&kill_evt, 1) == 0)
-                althrd_yield();
-            alsem_post(&context->EventSem);
-            althrd_join(context->EventThread, NULL);
-        }
-        else
-        {
-            /* Wait to ensure the event handler sees the changed flags before
-             * returning.
-             */
-            almtx_lock(&context->EventCbLock);
-            almtx_unlock(&context->EventCbLock);
-        }
+        /* Wait to ensure the event handler sees the changed flags before
+         * returning.
+         */
+        almtx_lock(&context->EventCbLock);
+        almtx_unlock(&context->EventCbLock);
     }
-    almtx_unlock(&context->EventThrdLock);
 
 done:
     ALCcontext_DecRef(context);

+ 3 - 2
libs/openal-soft/alsoftrc.sample

@@ -219,8 +219,9 @@
 ## excludefx: (global)
 #  Sets which effects to exclude, preventing apps from using them. This can
 #  help for apps that try to use effects which are too CPU intensive for the
-#  system to handle. Available effects are: eaxreverb,reverb,chorus,compressor,
-#  distortion,echo,equalizer,flanger,modulator,dedicated,pshifter
+#  system to handle. Available effects are: eaxreverb,reverb,autowah,chorus,
+#  compressor,distortion,echo,equalizer,flanger,modulator,dedicated,pshifter,
+#  fshifter
 #excludefx =
 
 ## default-reverb: (global)

+ 1 - 1
libs/openal-soft/appveyor.yml

@@ -1,4 +1,4 @@
-version: 1.18.2.{build}
+version: 1.19.0.{build}
 
 environment:
     matrix:

+ 92 - 0
libs/openal-soft/common/alcomplex.c

@@ -0,0 +1,92 @@
+
+#include "config.h"
+
+#include "alcomplex.h"
+#include "math_defs.h"
+
+
+extern inline ALcomplex complex_add(ALcomplex a, ALcomplex b);
+extern inline ALcomplex complex_sub(ALcomplex a, ALcomplex b);
+extern inline ALcomplex complex_mult(ALcomplex a, ALcomplex b);
+
+void complex_fft(ALcomplex *FFTBuffer, ALsizei FFTSize, ALdouble Sign)
+{
+    ALsizei i, j, k, mask, step, step2;
+    ALcomplex temp, u, w;
+    ALdouble arg;
+
+    /* Bit-reversal permutation applied to a sequence of FFTSize items */
+    for(i = 1;i < FFTSize-1;i++)
+    {
+        for(mask = 0x1, j = 0;mask < FFTSize;mask <<= 1)
+        {
+            if((i&mask) != 0)
+                j++;
+            j <<= 1;
+        }
+        j >>= 1;
+
+        if(i < j)
+        {
+            temp         = FFTBuffer[i];
+            FFTBuffer[i] = FFTBuffer[j];
+            FFTBuffer[j] = temp;
+        }
+    }
+
+    /* Iterative form of Danielson–Lanczos lemma */
+    for(i = 1, step = 2;i < FFTSize;i<<=1, step<<=1)
+    {
+        step2 = step >> 1;
+        arg   = M_PI / step2;
+
+        w.Real = cos(arg);
+        w.Imag = sin(arg) * Sign;
+
+        u.Real = 1.0;
+        u.Imag = 0.0;
+
+        for(j = 0;j < step2;j++)
+        {
+            for(k = j;k < FFTSize;k+=step)
+            {
+                temp               = complex_mult(FFTBuffer[k+step2], u);
+                FFTBuffer[k+step2] = complex_sub(FFTBuffer[k], temp);
+                FFTBuffer[k]       = complex_add(FFTBuffer[k], temp);
+            }
+
+            u = complex_mult(u, w);
+        }
+    }
+}
+
+void complex_hilbert(ALcomplex *Buffer, ALsizei size)
+{
+    const ALdouble inverse_size = 1.0/(ALdouble)size;
+    ALsizei todo, i;
+
+    for(i = 0;i < size;i++)
+        Buffer[i].Imag = 0.0;
+
+    complex_fft(Buffer, size, 1.0);
+
+    todo = size >> 1;
+    Buffer[0].Real *= inverse_size;
+    Buffer[0].Imag *= inverse_size;
+    for(i = 1;i < todo;i++)
+    {
+        Buffer[i].Real *= 2.0*inverse_size;
+        Buffer[i].Imag *= 2.0*inverse_size;
+    }
+    Buffer[i].Real *= inverse_size;
+    Buffer[i].Imag *= inverse_size;
+    i++;
+
+    for(;i < size;i++)
+    {
+        Buffer[i].Real = 0.0;
+        Buffer[i].Imag = 0.0;
+    }
+
+    complex_fft(Buffer, size, -1.0);
+}

+ 71 - 0
libs/openal-soft/common/alcomplex.h

@@ -0,0 +1,71 @@
+#ifndef ALCOMPLEX_H
+#define ALCOMPLEX_H
+
+#include "AL/al.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct ALcomplex {
+    ALdouble Real;
+    ALdouble Imag;
+} ALcomplex;
+
+/** Addition of two complex numbers. */
+inline ALcomplex complex_add(ALcomplex a, ALcomplex b)
+{
+    ALcomplex result;
+
+    result.Real = a.Real + b.Real;
+    result.Imag = a.Imag + b.Imag;
+
+    return result;
+}
+
+/** Subtraction of two complex numbers. */
+inline ALcomplex complex_sub(ALcomplex a, ALcomplex b)
+{
+    ALcomplex result;
+
+    result.Real = a.Real - b.Real;
+    result.Imag = a.Imag - b.Imag;
+
+    return result;
+}
+
+/** Multiplication of two complex numbers. */
+inline ALcomplex complex_mult(ALcomplex a, ALcomplex b)
+{
+    ALcomplex result;
+
+    result.Real = a.Real*b.Real - a.Imag*b.Imag;
+    result.Imag = a.Imag*b.Real + a.Real*b.Imag;
+
+    return result;
+}
+
+/**
+ * Iterative implementation of 2-radix FFT (In-place algorithm). Sign = -1 is
+ * FFT and 1 is iFFT (inverse). Fills FFTBuffer[0...FFTSize-1] with the
+ * Discrete Fourier Transform (DFT) of the time domain data stored in
+ * FFTBuffer[0...FFTSize-1]. FFTBuffer is an array of complex numbers, FFTSize
+ * MUST BE power of two.
+ */
+void complex_fft(ALcomplex *FFTBuffer, ALsizei FFTSize, ALdouble Sign);
+
+/**
+ * Calculate the complex helical sequence (discrete-time analytical signal) of
+ * the given input using the discrete Hilbert transform (In-place algorithm).
+ * Fills Buffer[0...size-1] with the discrete-time analytical signal stored in
+ * Buffer[0...size-1]. Buffer is an array of complex numbers, size MUST BE
+ * power of two.
+ */
+void complex_hilbert(ALcomplex *Buffer, ALsizei size);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif /* ALCOMPLEX_H */

+ 21 - 2
libs/openal-soft/common/math_defs.h

@@ -18,6 +18,12 @@
 #define FLT_EPSILON (1.19209290e-07f)
 #endif
 
+#define SQRT_2 1.41421356237309504880
+#define SQRT_3 1.73205080756887719318
+
+#define SQRTF_2 1.41421356237309504880f
+#define SQRTF_3 1.73205080756887719318f
+
 #ifndef HUGE_VALF
 static const union msvc_inf_hack {
     unsigned char b[4];
@@ -40,7 +46,20 @@ static inline float cbrtf(float f)
 }
 #endif
 
-#define DEG2RAD(x)  ((float)(x) * (F_PI/180.0f))
-#define RAD2DEG(x)  ((float)(x) * (180.0f/F_PI))
+#ifndef HAVE_COPYSIGNF
+static inline float copysignf(float x, float y)
+{
+    union {
+        float f;
+        unsigned int u;
+    } ux = { x }, uy = { y };
+    ux.u &= 0x7fffffffu;
+    ux.u |= (uy.u&0x80000000u);
+    return ux.f;
+}
+#endif
+
+#define DEG2RAD(x)  ((float)(x) * (float)(M_PI/180.0))
+#define RAD2DEG(x)  ((float)(x) * (float)(180.0/M_PI))
 
 #endif /* AL_MATH_DEFS_H */

+ 5 - 1
libs/openal-soft/common/threads.c

@@ -428,7 +428,11 @@ void althrd_thread_detach(void)
     {
         void *ptr = altss_get(TlsDestructors.keys[i]);
         altss_dtor_t callback = (altss_dtor_t)TlsDestructors.values[i];
-        if(ptr && callback) callback(ptr);
+        if(ptr)
+        {
+            if(callback) callback(ptr);
+            altss_set(TlsDestructors.keys[i], NULL);
+        }
     }
     UnlockUIntMapRead(&TlsDestructors);
 }

+ 3 - 0
libs/openal-soft/config.h.in

@@ -98,6 +98,9 @@
 /* Define if we have the cbrtf function */
 #cmakedefine HAVE_CBRTF
 
+/* Define if we have the copysignf function */
+#cmakedefine HAVE_COPYSIGNF
+
 /* Define if we have the strtof function */
 #cmakedefine HAVE_STRTOF
 

+ 90 - 55
libs/openal-soft/examples/alffplay.cpp

@@ -39,7 +39,16 @@ extern "C" {
 #include "AL/al.h"
 #include "AL/alext.h"
 
+#include "common/alhelpers.h"
+
 extern "C" {
+/* Undefine this to disable use of experimental extensions. Don't use for
+ * production code! Interfaces and behavior may change prior to being
+ * finalized.
+ */
+#define ALLOW_EXPERIMENTAL_EXTS
+
+#ifdef ALLOW_EXPERIMENTAL_EXTS
 #ifndef AL_SOFT_map_buffer
 #define AL_SOFT_map_buffer 1
 typedef unsigned int ALbitfieldSOFT;
@@ -71,10 +80,15 @@ typedef void (AL_APIENTRY*LPALEVENTCALLBACKSOFT)(ALEVENTPROCSOFT callback, void
 typedef void* (AL_APIENTRY*LPALGETPOINTERSOFT)(ALenum pname);
 typedef void (AL_APIENTRY*LPALGETPOINTERVSOFT)(ALenum pname, void **values);
 #endif
+#endif /* ALLOW_EXPERIMENTAL_EXTS */
 }
 
 namespace {
 
+#ifndef M_PI
+#define M_PI (3.14159265358979323846)
+#endif
+
 using nanoseconds = std::chrono::nanoseconds;
 using microseconds = std::chrono::microseconds;
 using milliseconds = std::chrono::milliseconds;
@@ -84,15 +98,20 @@ using seconds_d64 = std::chrono::duration<double>;
 const std::string AppName("alffplay");
 
 bool EnableDirectOut = false;
+bool EnableWideStereo = false;
 LPALGETSOURCEI64VSOFT alGetSourcei64vSOFT;
 LPALCGETINTEGER64VSOFT alcGetInteger64vSOFT;
 
+#ifdef AL_SOFT_map_buffer
 LPALBUFFERSTORAGESOFT alBufferStorageSOFT;
 LPALMAPBUFFERSOFT alMapBufferSOFT;
 LPALUNMAPBUFFERSOFT alUnmapBufferSOFT;
+#endif
 
+#ifdef AL_SOFT_events
 LPALEVENTCONTROLSOFT alEventControlSOFT;
 LPALEVENTCALLBACKSOFT alEventCallbackSOFT;
+#endif
 
 const seconds AVNoSyncThreshold(10);
 
@@ -258,9 +277,11 @@ struct AudioState {
         av_freep(&mSamples);
     }
 
+#ifdef AL_SOFT_events
     static void AL_APIENTRY EventCallback(ALenum eventType, ALuint object, ALuint param,
                                           ALsizei length, const ALchar *message,
                                           void *userParam);
+#endif
 
     nanoseconds getClockNoLock();
     nanoseconds getClock()
@@ -683,6 +704,7 @@ bool AudioState::readAudio(uint8_t *samples, int length)
 }
 
 
+#ifdef AL_SOFT_events
 void AL_APIENTRY AudioState::EventCallback(ALenum eventType, ALuint object, ALuint param,
                                            ALsizei length, const ALchar *message,
                                            void *userParam)
@@ -699,7 +721,7 @@ void AL_APIENTRY AudioState::EventCallback(ALenum eventType, ALuint object, ALui
         return;
     }
 
-    std::cout<< "---- AL Event on AudioState "<<self<<" ----\nEvent: ";
+    std::cout<< "\n---- AL Event on AudioState "<<self<<" ----\nEvent: ";
     switch(eventType)
     {
         case AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT: std::cout<< "Buffer completed"; break;
@@ -712,8 +734,8 @@ void AL_APIENTRY AudioState::EventCallback(ALenum eventType, ALuint object, ALui
                              std::dec<<std::setw(0)<<std::setfill(' '); break;
     }
     std::cout<< "\n"
-        "Object ID: "<<object<<'\n'<<
-        "Parameter: "<<param<<'\n'<<
+        "Object ID: "<<object<<"\n"
+        "Parameter: "<<param<<"\n"
         "Message: "<<std::string(message, length)<<"\n----"<<
         std::endl;
 
@@ -726,24 +748,27 @@ void AL_APIENTRY AudioState::EventCallback(ALenum eventType, ALuint object, ALui
         self->mSrcCond.notify_one();
     }
 }
+#endif
 
 int AudioState::handler()
 {
-    const std::array<ALenum,6> types{{
-        AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT, AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT,
-        AL_EVENT_TYPE_ERROR_SOFT, AL_EVENT_TYPE_PERFORMANCE_SOFT, AL_EVENT_TYPE_DEPRECATED_SOFT,
-        AL_EVENT_TYPE_DISCONNECTED_SOFT
-    }};
     std::unique_lock<std::mutex> lock(mSrcMutex);
     milliseconds sleep_time = AudioBufferTime / 3;
     ALenum fmt;
 
+#ifdef AL_SOFT_events
+    const std::array<ALenum,6> evt_types{{
+        AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT, AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT,
+        AL_EVENT_TYPE_ERROR_SOFT, AL_EVENT_TYPE_PERFORMANCE_SOFT, AL_EVENT_TYPE_DEPRECATED_SOFT,
+        AL_EVENT_TYPE_DISCONNECTED_SOFT
+    }};
     if(alEventControlSOFT)
     {
-        alEventControlSOFT(types.size(), types.data(), AL_TRUE);
+        alEventControlSOFT(evt_types.size(), evt_types.data(), AL_TRUE);
         alEventCallbackSOFT(EventCallback, this);
         sleep_time = AudioBufferTotalTime;
     }
+#endif
 
     /* Find a suitable format for OpenAL. */
     mDstChanLayout = 0;
@@ -885,13 +910,17 @@ int AudioState::handler()
 
     if(EnableDirectOut)
         alSourcei(mSource, AL_DIRECT_CHANNELS_SOFT, AL_TRUE);
+    if(EnableWideStereo)
+    {
+        ALfloat angles[2] = { (ALfloat)(M_PI/3.0), (ALfloat)(-M_PI/3.0) };
+        alSourcefv(mSource, AL_STEREO_ANGLES, angles);
+    }
 
     if(alGetError() != AL_NO_ERROR)
         goto finish;
 
-    if(!alBufferStorageSOFT)
-        samples = av_malloc(buffer_len);
-    else
+#ifdef AL_SOFT_map_buffer
+    if(alBufferStorageSOFT)
     {
         for(ALuint bufid : mBuffers)
             alBufferStorageSOFT(bufid, mFormat, nullptr, buffer_len, mCodecCtx->sample_rate,
@@ -902,6 +931,9 @@ int AudioState::handler()
             samples = av_malloc(buffer_len);
         }
     }
+    else
+#endif
+        samples = av_malloc(buffer_len);
 
     while(alGetError() == AL_NO_ERROR && !mMovie.mQuit.load(std::memory_order_relaxed) &&
           mConnected.test_and_set(std::memory_order_relaxed))
@@ -924,15 +956,19 @@ int AudioState::handler()
         {
             ALuint bufid = mBuffers[mBufferIdx];
 
-            uint8_t *ptr = reinterpret_cast<uint8_t*>(
-                samples ? samples : alMapBufferSOFT(bufid, 0, buffer_len, AL_MAP_WRITE_BIT_SOFT)
+            uint8_t *ptr = reinterpret_cast<uint8_t*>(samples
+#ifdef AL_SOFT_map_buffer
+                ? samples : alMapBufferSOFT(bufid, 0, buffer_len, AL_MAP_WRITE_BIT_SOFT)
+#endif
             );
             if(!ptr) break;
 
             /* Read the next chunk of data, filling the buffer, and queue it on
              * the source */
             bool got_audio = readAudio(ptr, buffer_len);
+#ifdef AL_SOFT_map_buffer
             if(!samples) alUnmapBufferSOFT(bufid);
+#endif
             if(!got_audio) break;
 
             if(samples)
@@ -973,11 +1009,13 @@ int AudioState::handler()
 finish:
     av_freep(&samples);
 
+#ifdef AL_SOFT_events
     if(alEventControlSOFT)
     {
-        alEventControlSOFT(types.size(), types.data(), AL_FALSE);
+        alEventControlSOFT(evt_types.size(), evt_types.data(), AL_FALSE);
         alEventCallbackSOFT(nullptr, nullptr);
     }
+#endif
 
     return 0;
 }
@@ -1681,41 +1719,21 @@ int main(int argc, char *argv[])
     SDL_RenderPresent(renderer);
 
     /* Open an audio device */
-    int fileidx = 1;
-    ALCdevice *device = [argc,argv,&fileidx]() -> ALCdevice*
-    {
-        ALCdevice *dev = NULL;
-        if(argc > 3 && strcmp(argv[1], "-device") == 0)
-        {
-            fileidx = 3;
-            dev = alcOpenDevice(argv[2]);
-            if(dev) return dev;
-            std::cerr<< "Failed to open \""<<argv[2]<<"\" - trying default" <<std::endl;
-        }
-        return alcOpenDevice(nullptr);
-    }();
-    ALCcontext *context = alcCreateContext(device, nullptr);
-    if(!context || alcMakeContextCurrent(context) == ALC_FALSE)
+    ++argv; --argc;
+    if(InitAL(&argv, &argc))
     {
         std::cerr<< "Failed to set up audio device" <<std::endl;
-        if(context)
-            alcDestroyContext(context);
         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(alcIsExtensionPresent(device, "ALC_SOFT_device_clock"))
-    {
-        std::cout<< "Found ALC_SOFT_device_clock" <<std::endl;
-        alcGetInteger64vSOFT = reinterpret_cast<LPALCGETINTEGER64VSOFT>(
-            alcGetProcAddress(device, "alcGetInteger64vSOFT")
-        );
+    { auto device = alcGetContextsDevice(alcGetCurrentContext());
+        if(alcIsExtensionPresent(device, "ALC_SOFT_device_clock"))
+        {
+            std::cout<< "Found ALC_SOFT_device_clock" <<std::endl;
+            alcGetInteger64vSOFT = reinterpret_cast<LPALCGETINTEGER64VSOFT>(
+                alcGetProcAddress(device, "alcGetInteger64vSOFT")
+            );
+        }
     }
 
     if(alIsExtensionPresent("AL_SOFT_source_latency"))
@@ -1725,6 +1743,7 @@ int main(int argc, char *argv[])
             alGetProcAddress("alGetSourcei64vSOFT")
         );
     }
+#ifdef AL_SOFT_map_buffer
     if(alIsExtensionPresent("AL_SOFTX_map_buffer"))
     {
         std::cout<< "Found AL_SOFT_map_buffer" <<std::endl;
@@ -1735,6 +1754,8 @@ int main(int argc, char *argv[])
         alUnmapBufferSOFT = reinterpret_cast<LPALUNMAPBUFFERSOFT>(
             alGetProcAddress("alUnmapBufferSOFT"));
     }
+#endif
+#ifdef AL_SOFT_events
     if(alIsExtensionPresent("AL_SOFTX_events"))
     {
         std::cout<< "Found AL_SOFT_events" <<std::endl;
@@ -1743,17 +1764,33 @@ int main(int argc, char *argv[])
         alEventCallbackSOFT = reinterpret_cast<LPALEVENTCALLBACKSOFT>(
             alGetProcAddress("alEventCallbackSOFT"));
     }
+#endif
 
-    if(fileidx < argc && strcmp(argv[fileidx], "-direct") == 0)
+    int fileidx = 0;
+    for(;fileidx < argc;++fileidx)
     {
-        ++fileidx;
-        if(!alIsExtensionPresent("AL_SOFT_direct_channels"))
-            std::cerr<< "AL_SOFT_direct_channels not supported for direct output" <<std::endl;
-        else
+        if(strcmp(argv[fileidx], "-direct") == 0)
+        {
+            if(!alIsExtensionPresent("AL_SOFT_direct_channels"))
+                std::cerr<< "AL_SOFT_direct_channels not supported for direct output" <<std::endl;
+            else
+            {
+                std::cout<< "Found AL_SOFT_direct_channels" <<std::endl;
+                EnableDirectOut = true;
+            }
+        }
+        else if(strcmp(argv[fileidx], "-wide") == 0)
         {
-            std::cout<< "Found AL_SOFT_direct_channels" <<std::endl;
-            EnableDirectOut = true;
+            if(!alIsExtensionPresent("AL_EXT_STEREO_ANGLES"))
+                std::cerr<< "AL_EXT_STEREO_ANGLES not supported for wide stereo" <<std::endl;
+            else
+            {
+                std::cout<< "Found AL_EXT_STEREO_ANGLES" <<std::endl;
+                EnableWideStereo = true;
+            }
         }
+        else
+            break;
     }
 
     while(fileidx < argc && !movState)
@@ -1858,9 +1895,7 @@ int main(int argc, char *argv[])
                 /* Nothing more to play. Shut everything down and quit. */
                 movState = nullptr;
 
-                alcMakeContextCurrent(nullptr);
-                alcDestroyContext(context);
-                alcCloseDevice(device);
+                CloseAL();
 
                 SDL_DestroyRenderer(renderer);
                 renderer = nullptr;

+ 4 - 2
libs/openal-soft/utils/alsoft-config/CMakeLists.txt

@@ -4,8 +4,10 @@ option(ALSOFT_NO_QT5 "Use Qt4 instead of Qt5 for alsoft-config" FALSE)
 
 include_directories("${alsoft-config_BINARY_DIR}")
 
-set(alsoft-config_SRCS  main.cpp
-                        mainwindow.cpp
+set(alsoft-config_SRCS
+    main.cpp
+    mainwindow.cpp
+    mainwindow.h
 )
 set(alsoft-config_UIS  mainwindow.ui)
 set(alsoft-config_MOCS  mainwindow.h)

+ 8 - 0
libs/openal-soft/utils/alsoft-config/mainwindow.cpp

@@ -372,12 +372,14 @@ MainWindow::MainWindow(QWidget *parent) :
     connect(ui->defaultReverbComboBox, SIGNAL(currentIndexChanged(const QString&)), this, SLOT(enableApplyButton()));
     connect(ui->enableEaxReverbCheck, SIGNAL(stateChanged(int)), this, SLOT(enableApplyButton()));
     connect(ui->enableStdReverbCheck, SIGNAL(stateChanged(int)), this, SLOT(enableApplyButton()));
+    connect(ui->enableAutowahCheck, SIGNAL(stateChanged(int)), this, SLOT(enableApplyButton()));
     connect(ui->enableChorusCheck, SIGNAL(stateChanged(int)), this, SLOT(enableApplyButton()));
     connect(ui->enableCompressorCheck, SIGNAL(stateChanged(int)), this, SLOT(enableApplyButton()));
     connect(ui->enableDistortionCheck, SIGNAL(stateChanged(int)), this, SLOT(enableApplyButton()));
     connect(ui->enableEchoCheck, SIGNAL(stateChanged(int)), this, SLOT(enableApplyButton()));
     connect(ui->enableEqualizerCheck, SIGNAL(stateChanged(int)), this, SLOT(enableApplyButton()));
     connect(ui->enableFlangerCheck, SIGNAL(stateChanged(int)), this, SLOT(enableApplyButton()));
+    connect(ui->enableFrequencyShifterCheck, SIGNAL(stateChanged(int)), this, SLOT(enableApplyButton()));
     connect(ui->enableModulatorCheck, SIGNAL(stateChanged(int)), this, SLOT(enableApplyButton()));
     connect(ui->enableDedicatedCheck, SIGNAL(stateChanged(int)), this, SLOT(enableApplyButton()));
     connect(ui->enablePitchShifterCheck, SIGNAL(stateChanged(int)), this, SLOT(enableApplyButton()));
@@ -838,12 +840,14 @@ void MainWindow::loadConfig(const QString &fname)
         *iter = iter->trimmed();
     ui->enableEaxReverbCheck->setChecked(!excludefx.contains("eaxreverb", Qt::CaseInsensitive));
     ui->enableStdReverbCheck->setChecked(!excludefx.contains("reverb", Qt::CaseInsensitive));
+    ui->enableAutowahCheck->setChecked(!excludefx.contains("autowah", Qt::CaseInsensitive));
     ui->enableChorusCheck->setChecked(!excludefx.contains("chorus", Qt::CaseInsensitive));
     ui->enableCompressorCheck->setChecked(!excludefx.contains("compressor", Qt::CaseInsensitive));
     ui->enableDistortionCheck->setChecked(!excludefx.contains("distortion", Qt::CaseInsensitive));
     ui->enableEchoCheck->setChecked(!excludefx.contains("echo", Qt::CaseInsensitive));
     ui->enableEqualizerCheck->setChecked(!excludefx.contains("equalizer", Qt::CaseInsensitive));
     ui->enableFlangerCheck->setChecked(!excludefx.contains("flanger", Qt::CaseInsensitive));
+    ui->enableFrequencyShifterCheck->setChecked(!excludefx.contains("fshifter", Qt::CaseInsensitive));
     ui->enableModulatorCheck->setChecked(!excludefx.contains("modulator", Qt::CaseInsensitive));
     ui->enableDedicatedCheck->setChecked(!excludefx.contains("dedicated", Qt::CaseInsensitive));
     ui->enablePitchShifterCheck->setChecked(!excludefx.contains("pshifter", Qt::CaseInsensitive));
@@ -1044,6 +1048,8 @@ void MainWindow::saveConfig(const QString &fname) const
         strlist.append("eaxreverb");
     if(!ui->enableStdReverbCheck->isChecked())
         strlist.append("reverb");
+    if(!ui->enableAutowahCheck->isChecked())
+        strlist.append("autowah");
     if(!ui->enableChorusCheck->isChecked())
         strlist.append("chorus");
     if(!ui->enableDistortionCheck->isChecked())
@@ -1056,6 +1062,8 @@ void MainWindow::saveConfig(const QString &fname) const
         strlist.append("equalizer");
     if(!ui->enableFlangerCheck->isChecked())
         strlist.append("flanger");
+    if(!ui->enableFrequencyShifterCheck->isChecked())
+        strlist.append("fshifter");
     if(!ui->enableModulatorCheck->isChecked())
         strlist.append("modulator");
     if(!ui->enableDedicatedCheck->isChecked())

+ 40 - 8
libs/openal-soft/utils/alsoft-config/mainwindow.ui

@@ -1933,7 +1933,7 @@ be useful for preventing those extensions from being used.</string>
         <x>10</x>
         <y>100</y>
         <width>511</width>
-        <height>231</height>
+        <height>241</height>
        </rect>
       </property>
       <property name="toolTip">
@@ -2011,8 +2011,8 @@ for the system to handle.</string>
       <widget class="QCheckBox" name="enableEchoCheck">
        <property name="geometry">
         <rect>
-         <x>320</x>
-         <y>30</y>
+         <x>70</x>
+         <y>180</y>
          <width>131</width>
          <height>21</height>
         </rect>
@@ -2028,7 +2028,7 @@ for the system to handle.</string>
        <property name="geometry">
         <rect>
          <x>320</x>
-         <y>60</y>
+         <y>30</y>
          <width>131</width>
          <height>21</height>
         </rect>
@@ -2060,7 +2060,7 @@ for the system to handle.</string>
        <property name="geometry">
         <rect>
          <x>320</x>
-         <y>120</y>
+         <y>150</y>
          <width>131</width>
          <height>21</height>
         </rect>
@@ -2076,7 +2076,7 @@ for the system to handle.</string>
        <property name="geometry">
         <rect>
          <x>320</x>
-         <y>150</y>
+         <y>180</y>
          <width>131</width>
          <height>21</height>
         </rect>
@@ -2111,8 +2111,8 @@ added by the ALC_EXT_DEDICATED extension.</string>
       <widget class="QCheckBox" name="enablePitchShifterCheck">
        <property name="geometry">
         <rect>
-         <x>70</x>
-         <y>180</y>
+         <x>320</x>
+         <y>120</y>
          <width>131</width>
          <height>21</height>
         </rect>
@@ -2124,6 +2124,38 @@ added by the ALC_EXT_DEDICATED extension.</string>
         <bool>true</bool>
        </property>
       </widget>
+      <widget class="QCheckBox" name="enableFrequencyShifterCheck">
+       <property name="geometry">
+        <rect>
+         <x>320</x>
+         <y>60</y>
+         <width>131</width>
+         <height>21</height>
+        </rect>
+       </property>
+       <property name="text">
+        <string>Frequency Shifter</string>
+       </property>
+       <property name="checked">
+        <bool>true</bool>
+       </property>
+      </widget>
+      <widget class="QCheckBox" name="enableAutowahCheck">
+       <property name="geometry">
+        <rect>
+         <x>70</x>
+         <y>210</y>
+         <width>131</width>
+         <height>21</height>
+        </rect>
+       </property>
+       <property name="text">
+        <string>Autowah</string>
+       </property>
+       <property name="checked">
+        <bool>true</bool>
+       </property>
+      </widget>
      </widget>
      <widget class="QLabel" name="label_13">
       <property name="geometry">

+ 92 - 113
libs/openal-soft/utils/makehrtf.c

@@ -988,46 +988,30 @@ static Complex *CreateComplexes(size_t n)
     return a;
 }
 
-/* Fast Fourier transform routines.  The number of points must be a power of
- * two.  In-place operation is possible only if both the real and imaginary
- * parts are in-place together.
+/* Fast Fourier transform routines. The number of points must be a power of
+ * two.
  */
 
 // Performs bit-reversal ordering.
-static void FftArrange(const uint n, const Complex *in, Complex *out)
+static void FftArrange(const uint n, Complex *inout)
 {
     uint rk, k, m;
 
-    if(in == out)
-    {
-        // Handle in-place arrangement.
-        rk = 0;
-        for(k = 0;k < n;k++)
-        {
-            if(rk > k)
-            {
-                Complex temp = in[rk];
-                out[rk] = in[k];
-                out[k] = temp;
-            }
-            m = n;
-            while(rk&(m >>= 1))
-                rk &= ~m;
-            rk |= m;
-        }
-    }
-    else
+    // Handle in-place arrangement.
+    rk = 0;
+    for(k = 0;k < n;k++)
     {
-        // Handle copy arrangement.
-        rk = 0;
-        for(k = 0;k < n;k++)
+        if(rk > k)
         {
-            out[rk] = in[k];
-            m = n;
-            while(rk&(m >>= 1))
-                rk &= ~m;
-            rk |= m;
+            Complex temp = inout[rk];
+            inout[rk] = inout[k];
+            inout[k] = temp;
         }
+
+        m = n;
+        while(rk&(m >>= 1))
+            rk &= ~m;
+        rk |= m;
     }
 }
 
@@ -1061,23 +1045,23 @@ static void FftSummation(const int n, const double s, Complex *cplx)
 }
 
 // Performs a forward FFT.
-static void FftForward(const uint n, const Complex *in, Complex *out)
+static void FftForward(const uint n, Complex *inout)
 {
-    FftArrange(n, in, out);
-    FftSummation(n, 1.0, out);
+    FftArrange(n, inout);
+    FftSummation(n, 1.0, inout);
 }
 
 // Performs an inverse FFT.
-static void FftInverse(const uint n, const Complex *in, Complex *out)
+static void FftInverse(const uint n, Complex *inout)
 {
     double f;
     uint i;
 
-    FftArrange(n, in, out);
-    FftSummation(n, -1.0, out);
+    FftArrange(n, inout);
+    FftSummation(n, -1.0, inout);
     f = 1.0 / n;
     for(i = 0;i < n;i++)
-        out[i] = c_muls(out[i], f);
+        inout[i] = c_muls(inout[i], f);
 }
 
 /* Calculate the complex helical sequence (or discrete-time analytical signal)
@@ -1085,30 +1069,22 @@ static void FftInverse(const uint n, const Complex *in, Complex *out)
  * 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 Complex *in, Complex *out)
+static void Hilbert(const uint n, Complex *inout)
 {
     uint i;
 
-    if(in == out)
-    {
-        // Handle in-place operation.
-        for(i = 0;i < n;i++)
-            out[i].Imag = 0.0;
-    }
-    else
-    {
-        // Handle copy operation.
-        for(i = 0;i < n;i++)
-            out[i] = MakeComplex(in[i].Real, 0.0);
-    }
-    FftInverse(n, out, out);
+    // Handle in-place operation.
+    for(i = 0;i < n;i++)
+        inout[i].Imag = 0.0;
+
+    FftInverse(n, inout);
     for(i = 1;i < (n+1)/2;i++)
-        out[i] = c_muls(out[i], 2.0);
+        inout[i] = c_muls(inout[i], 2.0);
     /* Increment i if n is even. */
     i += (n&1)^1;
     for(;i < n;i++)
-        out[i] = MakeComplex(0.0, 0.0);
-    FftForward(n, out, out);
+        inout[i] = MakeComplex(0.0, 0.0);
+    FftForward(n, inout);
 }
 
 /* Calculate the magnitude response of the given input.  This is used in
@@ -1175,7 +1151,7 @@ static void MinimumPhase(const uint n, const double *in, Complex *out)
         mags[i] = mags[n - i];
         out[i] = out[n - i];
     }
-    Hilbert(n, out, out);
+    Hilbert(n, out);
     // Remove any DC offset the filter has.
     mags[0] = EPSILON;
     for(i = 0;i < n;i++)
@@ -2073,7 +2049,7 @@ static void AverageHrirMagnitude(const uint points, const uint n, const double *
         h[i] = MakeComplex(hrir[i], 0.0);
     for(;i < n;i++)
         h[i] = MakeComplex(0.0, 0.0);
-    FftForward(n, h, h);
+    FftForward(n, h);
     MagnitudeResponse(n, h, r);
     for(i = 0;i < m;i++)
         mag[i] = Lerp(mag[i], r[i], f);
@@ -2194,16 +2170,16 @@ static void DiffuseFieldEqualize(const uint channels, const uint m, const double
 {
     uint ti, fi, ei, ai, i;
 
-    for(ti = 0;ti < channels;ti++)
+    for(fi = 0;fi < hData->mFdCount;fi++)
     {
-        for(fi = 0;fi < hData->mFdCount;fi++)
+        for(ei = hData->mFds[fi].mEvStart;ei < hData->mFds[fi].mEvCount;ei++)
         {
-            for(ei = hData->mFds[fi].mEvStart;ei < hData->mFds[fi].mEvCount;ei++)
+            for(ai = 0;ai < hData->mFds[fi].mEvs[ei].mAzCount;ai++)
             {
-                for(ai = 0;ai < hData->mFds[fi].mEvs[ei].mAzCount;ai++)
-                {
-                    HrirAzT *azd = &hData->mFds[fi].mEvs[ei].mAzs[ai];
+                HrirAzT *azd = &hData->mFds[fi].mEvs[ei].mAzs[ai];
 
+                for(ti = 0;ti < channels;ti++)
+                {
                     for(i = 0;i < m;i++)
                         azd->mIrs[ti][i] /= dfa[(ti * m) + i];
                 }
@@ -2232,18 +2208,18 @@ static void ReconstructHrirs(const HrirDataT *hData)
     count = pcdone = lastpc = 0;
     printf("%3d%% done.", pcdone);
     fflush(stdout);
-    for(ti = 0;ti < channels;ti++)
+    for(fi = 0;fi < hData->mFdCount;fi++)
     {
-        for(fi = 0;fi < hData->mFdCount;fi++)
+        for(ei = hData->mFds[fi].mEvStart;ei < hData->mFds[fi].mEvCount;ei++)
         {
-            for(ei = hData->mFds[fi].mEvStart;ei < hData->mFds[fi].mEvCount;ei++)
+            for(ai = 0;ai < hData->mFds[fi].mEvs[ei].mAzCount;ai++)
             {
-                for(ai = 0;ai < hData->mFds[fi].mEvs[ei].mAzCount;ai++)
-                {
-                    HrirAzT *azd = &hData->mFds[fi].mEvs[ei].mAzs[ai];
+                HrirAzT *azd = &hData->mFds[fi].mEvs[ei].mAzs[ai];
 
+                for(ti = 0;ti < channels;ti++)
+                {
                     MinimumPhase(n, azd->mIrs[ti], h);
-                    FftInverse(n, h, h);
+                    FftInverse(n, h);
                     for(i = 0;i < hData->mIrPoints;i++)
                         azd->mIrs[ti][i] = h[i].Real;
                     pcdone = ++count * 100 / total;
@@ -2270,18 +2246,16 @@ static void ResampleHrirs(const uint rate, HrirDataT *hData)
     ResamplerT rs;
 
     ResamplerSetup(&rs, hData->mIrRate, rate);
-    for(ti = 0;ti < channels;ti++)
+    for(fi = 0;fi < hData->mFdCount;fi++)
     {
-        for(fi = 0;fi < hData->mFdCount;fi++)
+        for(ei = hData->mFds[fi].mEvStart;ei < hData->mFds[fi].mEvCount;ei++)
         {
-            for(ei = hData->mFds[fi].mEvStart;ei < hData->mFds[fi].mEvCount;ei++)
+            for(ai = 0;ai < hData->mFds[fi].mEvs[ei].mAzCount;ai++)
             {
-                for(ai = 0;ai < hData->mFds[fi].mEvs[ei].mAzCount;ai++)
-                {
-                    HrirAzT *azd = &hData->mFds[fi].mEvs[ei].mAzs[ai];
+                HrirAzT *azd = &hData->mFds[fi].mEvs[ei].mAzs[ai];
 
+                for(ti = 0;ti < channels;ti++)
                     ResamplerRun(&rs, n, azd->mIrs[ti], n, azd->mIrs[ti]);
-                }
             }
         }
     }
@@ -2313,13 +2287,14 @@ static void SynthesizeOnsets(HrirDataT *hData)
     uint ti, fi, oi, ai, ei, a0, a1;
     double t, of, af;
 
-    for(ti = 0;ti < channels;ti++)
+    for(fi = 0;fi < hData->mFdCount;fi++)
     {
-        for(fi = 0;fi < hData->mFdCount;fi++)
+        if(hData->mFds[fi].mEvStart <= 0)
+            continue;
+        oi = hData->mFds[fi].mEvStart;
+
+        for(ti = 0;ti < channels;ti++)
         {
-            if(hData->mFds[fi].mEvStart <= 0)
-                continue;
-            oi = hData->mFds[fi].mEvStart;
             t = 0.0;
             for(ai = 0;ai < hData->mFds[fi].mEvs[oi].mAzCount;ai++)
                 t += hData->mFds[fi].mEvs[oi].mAzs[ai].mDelays[ti];
@@ -2330,7 +2305,12 @@ static void SynthesizeOnsets(HrirDataT *hData)
                 for(ai = 0;ai < hData->mFds[fi].mEvs[ei].mAzCount;ai++)
                 {
                     CalcAzIndices(hData, fi, oi, hData->mFds[fi].mEvs[ei].mAzs[ai].mAzimuth, &a0, &a1, &af);
-                    hData->mFds[fi].mEvs[ei].mAzs[ai].mDelays[ti] = Lerp(hData->mFds[fi].mEvs[0].mAzs[0].mDelays[ti], Lerp(hData->mFds[fi].mEvs[oi].mAzs[a0].mDelays[ti], hData->mFds[fi].mEvs[oi].mAzs[a1].mDelays[ti], af), of);
+                    hData->mFds[fi].mEvs[ei].mAzs[ai].mDelays[ti] = Lerp(
+                        hData->mFds[fi].mEvs[0].mAzs[0].mDelays[ti],
+                        Lerp(hData->mFds[fi].mEvs[oi].mAzs[a0].mDelays[ti],
+                             hData->mFds[fi].mEvs[oi].mAzs[a1].mDelays[ti], af),
+                        of
+                    );
                 }
             }
         }
@@ -2421,16 +2401,16 @@ static void NormalizeHrirs(const HrirDataT *hData)
     uint ti, fi, ei, ai, i;
     double maxLevel = 0.0;
 
-    for(ti = 0;ti < channels;ti++)
+    for(fi = 0;fi < hData->mFdCount;fi++)
     {
-        for(fi = 0;fi < hData->mFdCount;fi++)
+        for(ei = 0;ei < hData->mFds[fi].mEvCount;ei++)
         {
-            for(ei = 0;ei < hData->mFds[fi].mEvCount;ei++)
+            for(ai = 0;ai < hData->mFds[fi].mEvs[ei].mAzCount;ai++)
             {
-                for(ai = 0;ai < hData->mFds[fi].mEvs[ei].mAzCount;ai++)
-                {
-                    HrirAzT *azd = &hData->mFds[fi].mEvs[ei].mAzs[ai];
+                HrirAzT *azd = &hData->mFds[fi].mEvs[ei].mAzs[ai];
 
+                for(ti = 0;ti < channels;ti++)
+                {
                     for(i = 0;i < n;i++)
                         maxLevel = fmax(fabs(azd->mIrs[ti][i]), maxLevel);
                 }
@@ -2438,16 +2418,16 @@ static void NormalizeHrirs(const HrirDataT *hData)
         }
     }
     maxLevel = 1.01 * maxLevel;
-    for(ti = 0;ti < channels;ti++)
+    for(fi = 0;fi < hData->mFdCount;fi++)
     {
-        for(fi = 0;fi < hData->mFdCount;fi++)
+        for(ei = 0;ei < hData->mFds[fi].mEvCount;ei++)
         {
-            for(ei = 0;ei < hData->mFds[fi].mEvCount;ei++)
+            for(ai = 0;ai < hData->mFds[fi].mEvs[ei].mAzCount;ai++)
             {
-                for(ai = 0;ai < hData->mFds[fi].mEvs[ei].mAzCount;ai++)
-                {
-                    HrirAzT *azd = &hData->mFds[fi].mEvs[ei].mAzs[ai];
+                HrirAzT *azd = &hData->mFds[fi].mEvs[ei].mAzs[ai];
 
+                for(ti = 0;ti < channels;ti++)
+                {
                     for(i = 0;i < n;i++)
                         azd->mIrs[ti][i] /= maxLevel;
                 }
@@ -2481,16 +2461,16 @@ static void CalculateHrtds(const HeadModelT model, const double radius, HrirData
 
     if(model == HM_DATASET)
     {
-        for(ti = 0;ti < channels;ti++)
+        for(fi = 0;fi < hData->mFdCount;fi++)
         {
-            for(fi = 0;fi < hData->mFdCount;fi++)
+            for(ei = 0;ei < hData->mFds[fi].mEvCount;ei++)
             {
-                for(ei = 0;ei < hData->mFds[fi].mEvCount;ei++)
+                for(ai = 0;ai < hData->mFds[fi].mEvs[ei].mAzCount;ai++)
                 {
-                    for(ai = 0;ai < hData->mFds[fi].mEvs[ei].mAzCount;ai++)
-                    {
-                        HrirAzT *azd = &hData->mFds[fi].mEvs[ei].mAzs[ai];
+                    HrirAzT *azd = &hData->mFds[fi].mEvs[ei].mAzs[ai];
 
+                    for(ti = 0;ti < channels;ti++)
+                    {
                         t = azd->mDelays[ti] * radius / hData->mRadius;
                         azd->mDelays[ti] = t;
                         maxHrtd = fmax(t, maxHrtd);
@@ -2502,18 +2482,18 @@ static void CalculateHrtds(const HeadModelT model, const double radius, HrirData
     }
     else
     {
-        for(ti = 0;ti < channels;ti++)
+        for(fi = 0;fi < hData->mFdCount;fi++)
         {
-            for(fi = 0;fi < hData->mFdCount;fi++)
+            for(ei = 0;ei < hData->mFds[fi].mEvCount;ei++)
             {
-                for(ei = 0;ei < hData->mFds[fi].mEvCount;ei++)
+                HrirEvT *evd = &hData->mFds[fi].mEvs[ei];
+
+                for(ai = 0;ai < evd->mAzCount;ai++)
                 {
-                    HrirEvT *evd = &hData->mFds[fi].mEvs[ei];
+                    HrirAzT *azd = &evd->mAzs[ai];
 
-                    for(ai = 0;ai < evd->mAzCount;ai++)
+                    for(ti = 0;ti < channels;ti++)
                     {
-                        HrirAzT *azd = &evd->mAzs[ai];
-
                         t = CalcLTD(evd->mElevation, azd->mAzimuth, radius, hData->mFds[fi].mDistance);
                         azd->mDelays[ti] = t;
                         maxHrtd = fmax(t, maxHrtd);
@@ -2523,11 +2503,11 @@ static void CalculateHrtds(const HeadModelT model, const double radius, HrirData
             }
         }
     }
-    for(ti = 0;ti < channels;ti++)
+    for(fi = 0;fi < hData->mFdCount;fi++)
     {
-        for(fi = 0;fi < hData->mFdCount;fi++)
+        for(ei = 0;ei < hData->mFds[fi].mEvCount;ei++)
         {
-            for(ei = 0;ei < hData->mFds[fi].mEvCount;ei++)
+            for(ti = 0;ti < channels;ti++)
             {
                 for(ai = 0;ai < hData->mFds[fi].mEvs[ei].mAzCount;ai++)
                     hData->mFds[fi].mEvs[ei].mAzs[ai].mDelays[ti] -= minHrtd;
@@ -2618,8 +2598,7 @@ static void FreeHrirData(HrirDataT *hData)
         {
             if(hData->mFds[0].mEvs[0].mAzs)
             {
-                if(hData->mFds[0].mEvs[0].mAzs[0].mIrs[0] != NULL)
-                    free(hData->mFds[0].mEvs[0].mAzs[0].mIrs[0]);
+                free(hData->mFds[0].mEvs[0].mAzs[0].mIrs[0]);
                 free(hData->mFds[0].mEvs[0].mAzs);
             }
             free(hData->mFds[0].mEvs);

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