Browse Source

Update OpenAL Soft to 1.18.2

Alex Szpakowski 8 years ago
parent
commit
b160006eb1
100 changed files with 19809 additions and 8001 deletions
  1. 69 4
      libs/openal-soft/.travis.yml
  2. 322 257
      libs/openal-soft/Alc/ALc.c
  3. 1138 949
      libs/openal-soft/Alc/ALu.c
  4. 96 10
      libs/openal-soft/Alc/alcConfig.c
  5. 63 147
      libs/openal-soft/Alc/alcRing.c
  6. 18 17
      libs/openal-soft/Alc/alstring.h
  7. 566 0
      libs/openal-soft/Alc/ambdec.c
  8. 46 0
      libs/openal-soft/Alc/ambdec.h
  9. 125 71
      libs/openal-soft/Alc/backends/alsa.c
  10. 25 159
      libs/openal-soft/Alc/backends/base.c
  11. 23 6
      libs/openal-soft/Alc/backends/base.h
  12. 286 176
      libs/openal-soft/Alc/backends/coreaudio.c
  13. 117 115
      libs/openal-soft/Alc/backends/dsound.c
  14. 62 32
      libs/openal-soft/Alc/backends/jack.c
  15. 2 2
      libs/openal-soft/Alc/backends/loopback.c
  16. 315 102
      libs/openal-soft/Alc/backends/mmdevapi.c
  17. 4 2
      libs/openal-soft/Alc/backends/null.c
  18. 869 167
      libs/openal-soft/Alc/backends/opensl.c
  19. 171 123
      libs/openal-soft/Alc/backends/oss.c
  20. 8 6
      libs/openal-soft/Alc/backends/portaudio.c
  21. 207 92
      libs/openal-soft/Alc/backends/pulseaudio.c
  22. 261 109
      libs/openal-soft/Alc/backends/qsa.c
  23. 131 79
      libs/openal-soft/Alc/backends/sndio.c
  24. 59 35
      libs/openal-soft/Alc/backends/solaris.c
  25. 43 30
      libs/openal-soft/Alc/backends/wave.c
  26. 45 43
      libs/openal-soft/Alc/backends/winmm.c
  27. 612 0
      libs/openal-soft/Alc/bformatdec.c
  28. 75 0
      libs/openal-soft/Alc/bformatdec.h
  29. 56 1
      libs/openal-soft/Alc/bs2b.c
  30. 4100 0
      libs/openal-soft/Alc/bsinc.c
  31. 25 0
      libs/openal-soft/Alc/compat.h
  32. 466 0
      libs/openal-soft/Alc/converter.c
  33. 55 0
      libs/openal-soft/Alc/converter.h
  34. 0 270
      libs/openal-soft/Alc/effects/autowah.c
  35. 114 103
      libs/openal-soft/Alc/effects/chorus.c
  36. 78 41
      libs/openal-soft/Alc/effects/compressor.c
  37. 49 24
      libs/openal-soft/Alc/effects/dedicated.c
  38. 76 74
      libs/openal-soft/Alc/effects/distortion.c
  39. 82 51
      libs/openal-soft/Alc/effects/echo.c
  40. 97 58
      libs/openal-soft/Alc/effects/equalizer.c
  41. 114 104
      libs/openal-soft/Alc/effects/flanger.c
  42. 97 87
      libs/openal-soft/Alc/effects/modulator.c
  43. 32 15
      libs/openal-soft/Alc/effects/null.c
  44. 399 535
      libs/openal-soft/Alc/effects/reverb.c
  45. 325 669
      libs/openal-soft/Alc/helpers.c
  46. 759 505
      libs/openal-soft/Alc/hrtf.c
  47. 34 22
      libs/openal-soft/Alc/hrtf.h
  48. 255 0
      libs/openal-soft/Alc/mastering.c
  49. 278 245
      libs/openal-soft/Alc/mixer.c
  50. 99 72
      libs/openal-soft/Alc/mixer_c.c
  51. 92 43
      libs/openal-soft/Alc/mixer_defs.h
  52. 88 53
      libs/openal-soft/Alc/mixer_inc.c
  53. 245 53
      libs/openal-soft/Alc/mixer_neon.c
  54. 75 124
      libs/openal-soft/Alc/mixer_sse.c
  55. 7 6
      libs/openal-soft/Alc/mixer_sse2.c
  56. 11 76
      libs/openal-soft/Alc/mixer_sse3.c
  57. 18 88
      libs/openal-soft/Alc/mixer_sse41.c
  58. 418 0
      libs/openal-soft/Alc/nfcfilter.c
  59. 37 0
      libs/openal-soft/Alc/nfcfilter.h
  60. 1036 322
      libs/openal-soft/Alc/panning.c
  61. 134 0
      libs/openal-soft/Alc/uhjfilter.c
  62. 49 0
      libs/openal-soft/Alc/uhjfilter.h
  63. 43 55
      libs/openal-soft/Alc/vector.h
  64. 458 253
      libs/openal-soft/CMakeLists.txt
  65. 117 0
      libs/openal-soft/ChangeLog
  66. 87 14
      libs/openal-soft/OpenAL32/Include/alAuxEffectSlot.h
  67. 31 23
      libs/openal-soft/OpenAL32/Include/alBuffer.h
  68. 21 21
      libs/openal-soft/OpenAL32/Include/alEffect.h
  69. 22 23
      libs/openal-soft/OpenAL32/Include/alFilter.h
  70. 44 7
      libs/openal-soft/OpenAL32/Include/alListener.h
  71. 494 150
      libs/openal-soft/OpenAL32/Include/alMain.h
  72. 71 91
      libs/openal-soft/OpenAL32/Include/alSource.h
  73. 293 107
      libs/openal-soft/OpenAL32/Include/alu.h
  74. 5 36
      libs/openal-soft/OpenAL32/Include/bs2b.h
  75. 283 106
      libs/openal-soft/OpenAL32/alAuxEffectSlot.c
  76. 108 58
      libs/openal-soft/OpenAL32/alBuffer.c
  77. 34 18
      libs/openal-soft/OpenAL32/alEffect.c
  78. 7 2
      libs/openal-soft/OpenAL32/alError.c
  79. 11 14
      libs/openal-soft/OpenAL32/alExtension.c
  80. 35 14
      libs/openal-soft/OpenAL32/alFilter.c
  81. 110 42
      libs/openal-soft/OpenAL32/alListener.c
  82. 340 157
      libs/openal-soft/OpenAL32/alSource.c
  83. 155 11
      libs/openal-soft/OpenAL32/alState.c
  84. 15 10
      libs/openal-soft/OpenAL32/alThunk.c
  85. 106 371
      libs/openal-soft/OpenAL32/sample_cvt.c
  86. 125 19
      libs/openal-soft/alsoftrc.sample
  87. 19 0
      libs/openal-soft/appveyor.yml
  88. 2 2
      libs/openal-soft/cmake/CheckSharedFunctionExists.cmake
  89. 16 10
      libs/openal-soft/cmake/FindDSound.cmake
  90. 6 0
      libs/openal-soft/cmake/FindFFmpeg.cmake
  91. 13 1
      libs/openal-soft/cmake/FindOSS.cmake
  92. 626 0
      libs/openal-soft/cmake/FindWindowsSDK.cmake
  93. 0 0
      libs/openal-soft/common/align.h
  94. 62 0
      libs/openal-soft/common/almalloc.c
  95. 21 0
      libs/openal-soft/common/almalloc.h
  96. 0 3
      libs/openal-soft/common/atomic.c
  97. 425 0
      libs/openal-soft/common/atomic.h
  98. 0 0
      libs/openal-soft/common/bool.h
  99. 35 0
      libs/openal-soft/common/math_defs.h
  100. 11 9
      libs/openal-soft/common/rwlock.c

+ 69 - 4
libs/openal-soft/.travis.yml

@@ -1,5 +1,70 @@
-os:
-  - linux
-  - osx
 language: c
-script: cmake . && make -j2
+matrix:
+  include:
+    - os: linux
+      dist: trusty
+    - os: linux
+      dist: trusty
+      env:
+        - BUILD_ANDROID=true
+    - os: osx
+sudo: required
+cache:
+  directories:
+    - $HOME/android-ndk-r14
+install:
+  - >
+    if [[ "${TRAVIS_OS_NAME}" == "linux" && -z "${BUILD_ANDROID}" ]]; then
+      # Install pulseaudio, portaudio, ALSA, JACK dependencies for
+      # corresponding backends.
+      # Install Qt5 dependency for alsoft-config.
+      sudo apt-get install -qq \
+        libpulse-dev \
+        portaudio19-dev \
+        libasound2-dev \
+        libjack-dev \
+        qtbase5-dev
+    fi
+  - >
+    if [[ "${TRAVIS_OS_NAME}" == "linux" && "${BUILD_ANDROID}" == "true" ]]; then
+      if [[ ! -d ~/android-ndk-r14 || -z "$(ls -A ~/android-ndk-r14)" ]]; then
+        curl -o ~/android-ndk.zip https://dl.google.com/android/repository/android-ndk-r14-linux-x86_64.zip
+        unzip -q ~/android-ndk.zip -d ~ \
+          'android-ndk-r14/build/cmake/*' \
+          'android-ndk-r14/platforms/android-9/arch-arm/*' \
+          'android-ndk-r14/source.properties' \
+          'android-ndk-r14/sources/cxx-stl/gnu-libstdc++/4.9/libs/armeabi-v7a/*' \
+          'android-ndk-r14/sysroot/*' \
+          'android-ndk-r14/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/*' \
+          'android-ndk-r14/toolchains/llvm/prebuilt/linux-x86_64/*'
+        sed -i -e 's/VERSION 3.6.0/VERSION 3.2/' ~/android-ndk-r14/build/cmake/android.toolchain.cmake
+      fi
+    fi
+script:
+  - >
+    if [[ "${TRAVIS_OS_NAME}" == "linux" && -z "${BUILD_ANDROID}" ]]; then
+      cmake \
+        -DALSOFT_REQUIRE_ALSA=ON \
+        -DALSOFT_REQUIRE_OSS=ON \
+        -DALSOFT_REQUIRE_PORTAUDIO=ON \
+        -DALSOFT_REQUIRE_PULSEAUDIO=ON \
+        -DALSOFT_REQUIRE_JACK=ON \
+        -DALSOFT_EMBED_HRTF_DATA=YES \
+        .
+    fi
+  - >
+    if [[ "${TRAVIS_OS_NAME}" == "linux" && "${BUILD_ANDROID}" == "true" ]]; then
+      cmake \
+        -DCMAKE_TOOLCHAIN_FILE=~/android-ndk-r14/build/cmake/android.toolchain.cmake \
+        -DALSOFT_REQUIRE_OPENSL=ON \
+        -DALSOFT_EMBED_HRTF_DATA=YES \
+        .
+    fi
+  - >
+    if [[ "${TRAVIS_OS_NAME}" == "osx" ]]; then
+      cmake \
+        -DALSOFT_REQUIRE_COREAUDIO=ON \
+        -DALSOFT_EMBED_HRTF_DATA=YES \
+        .
+    fi
+  - make -j2

File diff suppressed because it is too large
+ 322 - 257
libs/openal-soft/Alc/ALc.c


File diff suppressed because it is too large
+ 1138 - 949
libs/openal-soft/Alc/ALu.c


+ 96 - 10
libs/openal-soft/Alc/alcConfig.c

@@ -233,7 +233,61 @@ static void LoadConfigFromFile(FILE *f)
                 curSection[0] = 0;
             else
             {
-                strncpy(curSection, section, sizeof(curSection)-1);
+                size_t len, p = 0;
+                do {
+                    char *nextp = strchr(section, '%');
+                    if(!nextp)
+                    {
+                        strncpy(curSection+p, section, sizeof(curSection)-1-p);
+                        break;
+                    }
+
+                    len = nextp - section;
+                    if(len > sizeof(curSection)-1-p)
+                        len = sizeof(curSection)-1-p;
+                    strncpy(curSection+p, section, len);
+                    p += len;
+                    section = nextp;
+
+                    if(((section[1] >= '0' && section[1] <= '9') ||
+                        (section[1] >= 'a' && section[1] <= 'f') ||
+                        (section[1] >= 'A' && section[1] <= 'F')) &&
+                       ((section[2] >= '0' && section[2] <= '9') ||
+                        (section[2] >= 'a' && section[2] <= 'f') ||
+                        (section[2] >= 'A' && section[2] <= 'F')))
+                    {
+                        unsigned char b = 0;
+                        if(section[1] >= '0' && section[1] <= '9')
+                            b = (section[1]-'0') << 4;
+                        else if(section[1] >= 'a' && section[1] <= 'f')
+                            b = (section[1]-'a'+0xa) << 4;
+                        else if(section[1] >= 'A' && section[1] <= 'F')
+                            b = (section[1]-'A'+0x0a) << 4;
+                        if(section[2] >= '0' && section[2] <= '9')
+                            b |= (section[2]-'0');
+                        else if(section[2] >= 'a' && section[2] <= 'f')
+                            b |= (section[2]-'a'+0xa);
+                        else if(section[2] >= 'A' && section[2] <= 'F')
+                            b |= (section[2]-'A'+0x0a);
+                        if(p < sizeof(curSection)-1)
+                            curSection[p++] = b;
+                        section += 3;
+                    }
+                    else if(section[1] == '%')
+                    {
+                        if(p < sizeof(curSection)-1)
+                            curSection[p++] = '%';
+                        section += 2;
+                    }
+                    else
+                    {
+                        if(p < sizeof(curSection)-1)
+                            curSection[p++] = '%';
+                        section += 1;
+                    }
+                    if(p < sizeof(curSection)-1)
+                        curSection[p] = 0;
+                } while(p < sizeof(curSection)-1 && *section != 0);
                 curSection[sizeof(curSection)-1] = 0;
             }
 
@@ -313,44 +367,61 @@ void ReadALConfig(void)
 {
     WCHAR buffer[PATH_MAX];
     const WCHAR *str;
+    al_string ppath;
     FILE *f;
 
     if(SHGetSpecialFolderPathW(NULL, buffer, CSIDL_APPDATA, FALSE) != FALSE)
     {
         al_string filepath = AL_STRING_INIT_STATIC();
-        al_string_copy_wcstr(&filepath, buffer);
-        al_string_append_cstr(&filepath, "\\alsoft.ini");
+        alstr_copy_wcstr(&filepath, buffer);
+        alstr_append_cstr(&filepath, "\\alsoft.ini");
 
-        TRACE("Loading config %s...\n", al_string_get_cstr(filepath));
-        f = al_fopen(al_string_get_cstr(filepath), "rt");
+        TRACE("Loading config %s...\n", alstr_get_cstr(filepath));
+        f = al_fopen(alstr_get_cstr(filepath), "rt");
+        if(f)
+        {
+            LoadConfigFromFile(f);
+            fclose(f);
+        }
+        alstr_reset(&filepath);
+    }
+
+    ppath = GetProcPath();
+    if(!alstr_empty(ppath))
+    {
+        alstr_append_cstr(&ppath, "\\alsoft.ini");
+        TRACE("Loading config %s...\n", alstr_get_cstr(ppath));
+        f = al_fopen(alstr_get_cstr(ppath), "r");
         if(f)
         {
             LoadConfigFromFile(f);
             fclose(f);
         }
-        al_string_deinit(&filepath);
     }
 
     if((str=_wgetenv(L"ALSOFT_CONF")) != NULL && *str)
     {
         al_string filepath = AL_STRING_INIT_STATIC();
-        al_string_copy_wcstr(&filepath, str);
+        alstr_copy_wcstr(&filepath, str);
 
-        TRACE("Loading config %s...\n", al_string_get_cstr(filepath));
-        f = al_fopen(al_string_get_cstr(filepath), "rt");
+        TRACE("Loading config %s...\n", alstr_get_cstr(filepath));
+        f = al_fopen(alstr_get_cstr(filepath), "rt");
         if(f)
         {
             LoadConfigFromFile(f);
             fclose(f);
         }
-        al_string_deinit(&filepath);
+        alstr_reset(&filepath);
     }
+
+    alstr_reset(&ppath);
 }
 #else
 void ReadALConfig(void)
 {
     char buffer[PATH_MAX];
     const char *str;
+    al_string ppath;
     FILE *f;
 
     str = "/etc/openal/alsoft.conf";
@@ -430,6 +501,19 @@ void ReadALConfig(void)
         }
     }
 
+    ppath = GetProcPath();
+    if(!alstr_empty(ppath))
+    {
+        alstr_append_cstr(&ppath, "/alsoft.conf");
+        TRACE("Loading config %s...\n", alstr_get_cstr(ppath));
+        f = al_fopen(alstr_get_cstr(ppath), "r");
+        if(f)
+        {
+            LoadConfigFromFile(f);
+            fclose(f);
+        }
+    }
+
     if((str=getenv("ALSOFT_CONF")) != NULL && *str)
     {
         TRACE("Loading config %s...\n", str);
@@ -440,6 +524,8 @@ void ReadALConfig(void)
             fclose(f);
         }
     }
+
+    alstr_reset(&ppath);
 }
 #endif
 

+ 63 - 147
libs/openal-soft/Alc/alcRing.c

@@ -25,117 +25,17 @@
 
 #include "alMain.h"
 #include "threads.h"
+#include "almalloc.h"
 #include "compat.h"
 
 
-struct RingBuffer {
-    ALubyte *mem;
-
-    ALsizei frame_size;
-    ALsizei length;
-    ALint read_pos;
-    ALint write_pos;
-
-    almtx_t mtx;
-};
-
-
-RingBuffer *CreateRingBuffer(ALsizei frame_size, ALsizei length)
-{
-    RingBuffer *ring = calloc(1, sizeof(*ring) + ((length+1) * frame_size));
-    if(ring)
-    {
-        ring->mem = (ALubyte*)(ring+1);
-
-        ring->frame_size = frame_size;
-        ring->length = length+1;
-        ring->read_pos = 0;
-        ring->write_pos = 0;
-
-        almtx_init(&ring->mtx, almtx_plain);
-    }
-    return ring;
-}
-
-void DestroyRingBuffer(RingBuffer *ring)
-{
-    if(ring)
-    {
-        almtx_destroy(&ring->mtx);
-        free(ring);
-    }
-}
-
-ALsizei RingBufferSize(RingBuffer *ring)
-{
-    ALsizei s;
-
-    almtx_lock(&ring->mtx);
-    s = (ring->write_pos-ring->read_pos+ring->length) % ring->length;
-    almtx_unlock(&ring->mtx);
-
-    return s;
-}
-
-void WriteRingBuffer(RingBuffer *ring, const ALubyte *data, ALsizei len)
-{
-    int remain;
-
-    almtx_lock(&ring->mtx);
-
-    remain = (ring->read_pos-ring->write_pos-1+ring->length) % ring->length;
-    if(remain < len) len = remain;
-
-    if(len > 0)
-    {
-        remain = ring->length - ring->write_pos;
-        if(remain < len)
-        {
-            memcpy(ring->mem+(ring->write_pos*ring->frame_size), data,
-                   remain*ring->frame_size);
-            memcpy(ring->mem, data+(remain*ring->frame_size),
-                   (len-remain)*ring->frame_size);
-        }
-        else
-            memcpy(ring->mem+(ring->write_pos*ring->frame_size), data,
-                   len*ring->frame_size);
-
-        ring->write_pos += len;
-        ring->write_pos %= ring->length;
-    }
-
-    almtx_unlock(&ring->mtx);
-}
-
-void ReadRingBuffer(RingBuffer *ring, ALubyte *data, ALsizei len)
-{
-    int remain;
-
-    almtx_lock(&ring->mtx);
-
-    remain = ring->length - ring->read_pos;
-    if(remain < len)
-    {
-        memcpy(data, ring->mem+(ring->read_pos*ring->frame_size), remain*ring->frame_size);
-        memcpy(data+(remain*ring->frame_size), ring->mem, (len-remain)*ring->frame_size);
-    }
-    else
-        memcpy(data, ring->mem+(ring->read_pos*ring->frame_size), len*ring->frame_size);
-
-    ring->read_pos += len;
-    ring->read_pos %= ring->length;
-
-    almtx_unlock(&ring->mtx);
-}
-
-
 /* NOTE: This lockless ringbuffer implementation is copied from JACK, extended
  * to include an element size. Consequently, parameters and return values for a
  * size or count is in 'elements', not bytes. Additionally, it only supports
  * single-consumer/single-provider operation. */
 struct ll_ringbuffer {
-    volatile size_t write_ptr;
-    volatile size_t read_ptr;
+    ATOMIC(size_t) write_ptr;
+    ATOMIC(size_t) read_ptr;
     size_t size;
     size_t size_mask;
     size_t elem_size;
@@ -158,11 +58,11 @@ ll_ringbuffer_t *ll_ringbuffer_create(size_t sz, size_t elem_sz)
     rb = al_malloc(16, sizeof(*rb) + power_of_two*elem_sz);
     if(!rb) return NULL;
 
+    ATOMIC_INIT(&rb->write_ptr, 0);
+    ATOMIC_INIT(&rb->read_ptr, 0);
     rb->size = power_of_two;
     rb->size_mask = rb->size - 1;
     rb->elem_size = elem_sz;
-    rb->write_ptr = 0;
-    rb->read_ptr = 0;
     rb->mlocked = 0;
     return rb;
 }
@@ -184,7 +84,7 @@ void ll_ringbuffer_free(ll_ringbuffer_t *rb)
 int ll_ringbuffer_mlock(ll_ringbuffer_t *rb)
 {
 #ifdef USE_MLOCK
-    if(!rb->locked && mlock(rb, sizeof(*rb) + rb->size*rb->elem_size))
+    if(!rb->mlocked && mlock(rb, sizeof(*rb) + rb->size*rb->elem_size))
         return -1;
 #endif /* USE_MLOCK */
     rb->mlocked = 1;
@@ -194,8 +94,8 @@ int ll_ringbuffer_mlock(ll_ringbuffer_t *rb)
 /* Reset the read and write pointers to zero. This is not thread safe. */
 void ll_ringbuffer_reset(ll_ringbuffer_t *rb)
 {
-    rb->read_ptr = 0;
-    rb->write_ptr = 0;
+    ATOMIC_STORE(&rb->write_ptr, 0, almemory_order_release);
+    ATOMIC_STORE(&rb->read_ptr, 0, almemory_order_release);
     memset(rb->buf, 0, rb->size*rb->elem_size);
 }
 
@@ -203,23 +103,24 @@ void ll_ringbuffer_reset(ll_ringbuffer_t *rb)
  * elements in front of the read pointer and behind the write pointer. */
 size_t ll_ringbuffer_read_space(const ll_ringbuffer_t *rb)
 {
-    size_t w = rb->write_ptr;
-    size_t r = rb->read_ptr;
-    return (rb->size+w-r) & rb->size_mask;
+    size_t w = ATOMIC_LOAD(&CONST_CAST(ll_ringbuffer_t*,rb)->write_ptr, almemory_order_acquire);
+    size_t r = ATOMIC_LOAD(&CONST_CAST(ll_ringbuffer_t*,rb)->read_ptr, almemory_order_acquire);
+    return (w-r) & rb->size_mask;
 }
 /* Return the number of elements available for writing. This is the number of
  * elements in front of the write pointer and behind the read pointer. */
 size_t ll_ringbuffer_write_space(const ll_ringbuffer_t *rb)
 {
-    size_t w = rb->write_ptr;
-    size_t r = rb->read_ptr;
-    return (rb->size+r-w-1) & rb->size_mask;
+    size_t w = ATOMIC_LOAD(&CONST_CAST(ll_ringbuffer_t*,rb)->write_ptr, almemory_order_acquire);
+    size_t r = ATOMIC_LOAD(&CONST_CAST(ll_ringbuffer_t*,rb)->read_ptr, almemory_order_acquire);
+    return (r-w-1) & rb->size_mask;
 }
 
 /* The copying data reader. Copy at most `cnt' elements from `rb' to `dest'.
  * Returns the actual number of elements copied. */
 size_t ll_ringbuffer_read(ll_ringbuffer_t *rb, char *dest, size_t cnt)
 {
+    size_t read_ptr;
     size_t free_cnt;
     size_t cnt2;
     size_t to_read;
@@ -229,10 +130,12 @@ size_t ll_ringbuffer_read(ll_ringbuffer_t *rb, char *dest, size_t cnt)
     if(free_cnt == 0) return 0;
 
     to_read = (cnt > free_cnt) ? free_cnt : cnt;
-    cnt2 = rb->read_ptr + to_read;
+    read_ptr = ATOMIC_LOAD(&rb->read_ptr, almemory_order_relaxed) & rb->size_mask;
+
+    cnt2 = read_ptr + to_read;
     if(cnt2 > rb->size)
     {
-        n1 = rb->size - rb->read_ptr;
+        n1 = rb->size - read_ptr;
         n2 = cnt2 & rb->size_mask;
     }
     else
@@ -241,13 +144,15 @@ size_t ll_ringbuffer_read(ll_ringbuffer_t *rb, char *dest, size_t cnt)
         n2 = 0;
     }
 
-    memcpy(dest, &(rb->buf[rb->read_ptr*rb->elem_size]), n1*rb->elem_size);
-    rb->read_ptr = (rb->read_ptr + n1) & rb->size_mask;
+    memcpy(dest, &rb->buf[read_ptr*rb->elem_size], n1*rb->elem_size);
+    read_ptr += n1;
     if(n2)
     {
-        memcpy(dest + n1*rb->elem_size, &(rb->buf[rb->read_ptr*rb->elem_size]), n2*rb->elem_size);
-        rb->read_ptr = (rb->read_ptr + n2) & rb->size_mask;
+        memcpy(dest + n1*rb->elem_size, &rb->buf[(read_ptr&rb->size_mask)*rb->elem_size],
+               n2*rb->elem_size);
+        read_ptr += n2;
     }
+    ATOMIC_STORE(&rb->read_ptr, read_ptr, almemory_order_release);
     return to_read;
 }
 
@@ -260,17 +165,18 @@ size_t ll_ringbuffer_peek(ll_ringbuffer_t *rb, char *dest, size_t cnt)
     size_t cnt2;
     size_t to_read;
     size_t n1, n2;
-    size_t tmp_read_ptr;
+    size_t read_ptr;
 
-    tmp_read_ptr = rb->read_ptr;
     free_cnt = ll_ringbuffer_read_space(rb);
     if(free_cnt == 0) return 0;
 
     to_read = (cnt > free_cnt) ? free_cnt : cnt;
-    cnt2 = tmp_read_ptr + to_read;
+    read_ptr = ATOMIC_LOAD(&rb->read_ptr, almemory_order_relaxed) & rb->size_mask;
+
+    cnt2 = read_ptr + to_read;
     if(cnt2 > rb->size)
     {
-        n1 = rb->size - tmp_read_ptr;
+        n1 = rb->size - read_ptr;
         n2 = cnt2 & rb->size_mask;
     }
     else
@@ -279,10 +185,13 @@ size_t ll_ringbuffer_peek(ll_ringbuffer_t *rb, char *dest, size_t cnt)
         n2 = 0;
     }
 
-    memcpy(dest, &(rb->buf[tmp_read_ptr*rb->elem_size]), n1*rb->elem_size);
-    tmp_read_ptr = (tmp_read_ptr + n1) & rb->size_mask;
+    memcpy(dest, &rb->buf[read_ptr*rb->elem_size], n1*rb->elem_size);
     if(n2)
-        memcpy(dest + n1*rb->elem_size, &(rb->buf[tmp_read_ptr*rb->elem_size]), n2*rb->elem_size);
+    {
+        read_ptr += n1;
+        memcpy(dest + n1*rb->elem_size, &rb->buf[(read_ptr&rb->size_mask)*rb->elem_size],
+               n2*rb->elem_size);
+    }
     return to_read;
 }
 
@@ -290,6 +199,7 @@ size_t ll_ringbuffer_peek(ll_ringbuffer_t *rb, char *dest, size_t cnt)
  * Returns the actual number of elements copied. */
 size_t ll_ringbuffer_write(ll_ringbuffer_t *rb, const char *src, size_t cnt)
 {
+    size_t write_ptr;
     size_t free_cnt;
     size_t cnt2;
     size_t to_write;
@@ -299,10 +209,12 @@ size_t ll_ringbuffer_write(ll_ringbuffer_t *rb, const char *src, size_t cnt)
     if(free_cnt == 0) return 0;
 
     to_write = (cnt > free_cnt) ? free_cnt : cnt;
-    cnt2 = rb->write_ptr + to_write;
+    write_ptr = ATOMIC_LOAD(&rb->write_ptr, almemory_order_relaxed) & rb->size_mask;
+
+    cnt2 = write_ptr + to_write;
     if(cnt2 > rb->size)
     {
-        n1 = rb->size - rb->write_ptr;
+        n1 = rb->size - write_ptr;
         n2 = cnt2 & rb->size_mask;
     }
     else
@@ -311,28 +223,28 @@ size_t ll_ringbuffer_write(ll_ringbuffer_t *rb, const char *src, size_t cnt)
         n2 = 0;
     }
 
-    memcpy(&(rb->buf[rb->write_ptr*rb->elem_size]), src, n1*rb->elem_size);
-    rb->write_ptr = (rb->write_ptr + n1) & rb->size_mask;
+    memcpy(&rb->buf[write_ptr*rb->elem_size], src, n1*rb->elem_size);
+    write_ptr += n1;
     if(n2)
     {
-        memcpy(&(rb->buf[rb->write_ptr*rb->elem_size]), src + n1*rb->elem_size, n2*rb->elem_size);
-        rb->write_ptr = (rb->write_ptr + n2) & rb->size_mask;
+        memcpy(&rb->buf[(write_ptr&rb->size_mask)*rb->elem_size], src + n1*rb->elem_size,
+               n2*rb->elem_size);
+        write_ptr += n2;
     }
+    ATOMIC_STORE(&rb->write_ptr, write_ptr, almemory_order_release);
     return to_write;
 }
 
 /* Advance the read pointer `cnt' places. */
 void ll_ringbuffer_read_advance(ll_ringbuffer_t *rb, size_t cnt)
 {
-    size_t tmp = (rb->read_ptr + cnt) & rb->size_mask;
-    rb->read_ptr = tmp;
+    ATOMIC_ADD(&rb->read_ptr, cnt, almemory_order_acq_rel);
 }
 
 /* Advance the write pointer `cnt' places. */
 void ll_ringbuffer_write_advance(ll_ringbuffer_t *rb, size_t cnt)
 {
-    size_t tmp = (rb->write_ptr + cnt) & rb->size_mask;
-    rb->write_ptr = tmp;
+    ATOMIC_ADD(&rb->write_ptr, cnt, almemory_order_acq_rel);
 }
 
 /* The non-copying data reader. `vec' is an array of two places. Set the values
@@ -344,16 +256,18 @@ void ll_ringbuffer_get_read_vector(const ll_ringbuffer_t *rb, ll_ringbuffer_data
     size_t cnt2;
     size_t w, r;
 
-    w = rb->write_ptr;
-    r = rb->read_ptr;
-    free_cnt = (rb->size+w-r) & rb->size_mask;
+    w = ATOMIC_LOAD(&CONST_CAST(ll_ringbuffer_t*,rb)->write_ptr, almemory_order_acquire);
+    r = ATOMIC_LOAD(&CONST_CAST(ll_ringbuffer_t*,rb)->read_ptr, almemory_order_acquire);
+    w &= rb->size_mask;
+    r &= rb->size_mask;
+    free_cnt = (w-r) & rb->size_mask;
 
     cnt2 = r + free_cnt;
     if(cnt2 > rb->size)
     {
         /* Two part vector: the rest of the buffer after the current write ptr,
          * plus some from the start of the buffer. */
-        vec[0].buf = (char*)&(rb->buf[r*rb->elem_size]);
+        vec[0].buf = (char*)&rb->buf[r*rb->elem_size];
         vec[0].len = rb->size - r;
         vec[1].buf = (char*)rb->buf;
         vec[1].len = cnt2 & rb->size_mask;
@@ -361,7 +275,7 @@ void ll_ringbuffer_get_read_vector(const ll_ringbuffer_t *rb, ll_ringbuffer_data
     else
     {
         /* Single part vector: just the rest of the buffer */
-        vec[0].buf = (char*)&(rb->buf[r*rb->elem_size]);
+        vec[0].buf = (char*)&rb->buf[r*rb->elem_size];
         vec[0].len = free_cnt;
         vec[1].buf = NULL;
         vec[1].len = 0;
@@ -377,23 +291,25 @@ void ll_ringbuffer_get_write_vector(const ll_ringbuffer_t *rb, ll_ringbuffer_dat
     size_t cnt2;
     size_t w, r;
 
-    w = rb->write_ptr;
-    r = rb->read_ptr;
-    free_cnt = (rb->size+r-w-1) & rb->size_mask;
+    w = ATOMIC_LOAD(&CONST_CAST(ll_ringbuffer_t*,rb)->write_ptr, almemory_order_acquire);
+    r = ATOMIC_LOAD(&CONST_CAST(ll_ringbuffer_t*,rb)->read_ptr, almemory_order_acquire);
+    w &= rb->size_mask;
+    r &= rb->size_mask;
+    free_cnt = (r-w-1) & rb->size_mask;
 
     cnt2 = w + free_cnt;
     if(cnt2 > rb->size)
     {
         /* Two part vector: the rest of the buffer after the current write ptr,
          * plus some from the start of the buffer. */
-        vec[0].buf = (char*)&(rb->buf[w*rb->elem_size]);
+        vec[0].buf = (char*)&rb->buf[w*rb->elem_size];
         vec[0].len = rb->size - w;
         vec[1].buf = (char*)rb->buf;
         vec[1].len = cnt2 & rb->size_mask;
     }
     else
     {
-        vec[0].buf = (char*)&(rb->buf[w*rb->elem_size]);
+        vec[0].buf = (char*)&rb->buf[w*rb->elem_size];
         vec[0].len = free_cnt;
         vec[1].buf = NULL;
         vec[1].len = 0;

+ 18 - 17
libs/openal-soft/Alc/alstring.h

@@ -10,39 +10,40 @@ typedef char al_string_char_type;
 TYPEDEF_VECTOR(al_string_char_type, al_string)
 TYPEDEF_VECTOR(al_string, vector_al_string)
 
-inline void al_string_deinit(al_string *str)
+inline void alstr_reset(al_string *str)
 { VECTOR_DEINIT(*str); }
 #define AL_STRING_INIT(_x)       do { (_x) = (al_string)NULL; } while(0)
 #define AL_STRING_INIT_STATIC()  ((al_string)NULL)
-#define AL_STRING_DEINIT(_x)     al_string_deinit(&(_x))
+#define AL_STRING_DEINIT(_x)     alstr_reset(&(_x))
 
-inline size_t al_string_length(const_al_string str)
+inline size_t alstr_length(const_al_string str)
 { return VECTOR_SIZE(str); }
 
-inline ALboolean al_string_empty(const_al_string str)
-{ return al_string_length(str) == 0; }
+inline ALboolean alstr_empty(const_al_string str)
+{ return alstr_length(str) == 0; }
 
-inline const al_string_char_type *al_string_get_cstr(const_al_string str)
+inline const al_string_char_type *alstr_get_cstr(const_al_string str)
 { return str ? &VECTOR_FRONT(str) : ""; }
 
-void al_string_clear(al_string *str);
+void alstr_clear(al_string *str);
 
-int al_string_cmp(const_al_string str1, const_al_string str2);
-int al_string_cmp_cstr(const_al_string str1, const al_string_char_type *str2);
+int alstr_cmp(const_al_string str1, const_al_string str2);
+int alstr_cmp_cstr(const_al_string str1, const al_string_char_type *str2);
 
-void al_string_copy(al_string *str, const_al_string from);
-void al_string_copy_cstr(al_string *str, const al_string_char_type *from);
+void alstr_copy(al_string *str, const_al_string from);
+void alstr_copy_cstr(al_string *str, const al_string_char_type *from);
+void alstr_copy_range(al_string *str, const al_string_char_type *from, const al_string_char_type *to);
 
-void al_string_append_char(al_string *str, const al_string_char_type c);
-void al_string_append_cstr(al_string *str, const al_string_char_type *from);
-void al_string_append_range(al_string *str, const al_string_char_type *from, const al_string_char_type *to);
+void alstr_append_char(al_string *str, const al_string_char_type c);
+void alstr_append_cstr(al_string *str, const al_string_char_type *from);
+void alstr_append_range(al_string *str, const al_string_char_type *from, const al_string_char_type *to);
 
 #ifdef _WIN32
 #include <wchar.h>
 /* Windows-only methods to deal with WideChar strings. */
-void al_string_copy_wcstr(al_string *str, const wchar_t *from);
-void al_string_append_wcstr(al_string *str, const wchar_t *from);
-void al_string_append_wrange(al_string *str, const wchar_t *from, const wchar_t *to);
+void alstr_copy_wcstr(al_string *str, const wchar_t *from);
+void alstr_append_wcstr(al_string *str, const wchar_t *from);
+void alstr_append_wrange(al_string *str, const wchar_t *from, const wchar_t *to);
 #endif
 
 #endif /* ALSTRING_H */

+ 566 - 0
libs/openal-soft/Alc/ambdec.c

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

+ 46 - 0
libs/openal-soft/Alc/ambdec.h

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

+ 125 - 71
libs/openal-soft/Alc/backends/alsa.c

@@ -199,15 +199,21 @@ static ALCboolean alsa_load(void)
 #ifdef HAVE_DYNLOAD
     if(!alsa_handle)
     {
+        al_string missing_funcs = AL_STRING_INIT_STATIC();
+
         alsa_handle = LoadLib("libasound.so.2");
         if(!alsa_handle)
+        {
+            WARN("Failed to load %s\n", "libasound.so.2");
             return ALC_FALSE;
+        }
 
         error = ALC_FALSE;
 #define LOAD_FUNC(f) do {                                                     \
     p##f = GetSymbol(alsa_handle, #f);                                        \
     if(p##f == NULL) {                                                        \
         error = ALC_TRUE;                                                     \
+        alstr_append_cstr(&missing_funcs, "\n" #f);                           \
     }                                                                         \
 } while(0)
         ALSA_FUNCS(LOAD_FUNC);
@@ -215,10 +221,11 @@ static ALCboolean alsa_load(void)
 
         if(error)
         {
+            WARN("Missing expected functions:%s\n", alstr_get_cstr(missing_funcs));
             CloseLib(alsa_handle);
             alsa_handle = NULL;
-            return ALC_FALSE;
         }
+        alstr_reset(&missing_funcs);
     }
 #endif
 
@@ -237,16 +244,13 @@ static vector_DevMap CaptureDevices;
 
 static void clear_devlist(vector_DevMap *devlist)
 {
-    DevMap *iter, *end;
-
-    iter = VECTOR_ITER_BEGIN(*devlist);
-    end = VECTOR_ITER_END(*devlist);
-    for(;iter != end;iter++)
-    {
-        AL_STRING_DEINIT(iter->name);
-        AL_STRING_DEINIT(iter->device_name);
-    }
-    VECTOR_RESIZE(*devlist, 0);
+#define FREE_DEV(i) do {                                                      \
+    AL_STRING_DEINIT((i)->name);                                              \
+    AL_STRING_DEINIT((i)->device_name);                                       \
+} while(0)
+    VECTOR_FOR_EACH(DevMap, *devlist, FREE_DEV);
+    VECTOR_RESIZE(*devlist, 0, 0);
+#undef FREE_DEV
 }
 
 
@@ -272,11 +276,45 @@ static void probe_devices(snd_pcm_stream_t stream, vector_DevMap *DeviceList)
 
     AL_STRING_INIT(entry.name);
     AL_STRING_INIT(entry.device_name);
-    al_string_copy_cstr(&entry.name, alsaDevice);
-    al_string_copy_cstr(&entry.device_name, GetConfigValue(NULL, "alsa", (stream==SND_PCM_STREAM_PLAYBACK) ?
-                                                           "device" : "capture", "default"));
+    alstr_copy_cstr(&entry.name, alsaDevice);
+    alstr_copy_cstr(&entry.device_name, GetConfigValue(
+        NULL, "alsa", (stream==SND_PCM_STREAM_PLAYBACK) ? "device" : "capture", "default"
+    ));
     VECTOR_PUSH_BACK(*DeviceList, entry);
 
+    if(stream == SND_PCM_STREAM_PLAYBACK)
+    {
+        const char *customdevs, *sep, *next;
+        next = GetConfigValue(NULL, "alsa", "custom-devices", "");
+        while((customdevs=next) != NULL && customdevs[0])
+        {
+            next = strchr(customdevs, ';');
+            sep = strchr(customdevs, '=');
+            if(!sep)
+            {
+                al_string spec = AL_STRING_INIT_STATIC();
+                if(next)
+                    alstr_copy_range(&spec, customdevs, next++);
+                else
+                    alstr_copy_cstr(&spec, customdevs);
+                ERR("Invalid ALSA device specification \"%s\"\n", alstr_get_cstr(spec));
+                alstr_reset(&spec);
+                continue;
+            }
+
+            AL_STRING_INIT(entry.name);
+            AL_STRING_INIT(entry.device_name);
+            alstr_copy_range(&entry.name, customdevs, sep++);
+            if(next)
+                alstr_copy_range(&entry.device_name, sep, next++);
+            else
+                alstr_copy_cstr(&entry.device_name, sep);
+            TRACE("Got device \"%s\", \"%s\"\n", alstr_get_cstr(entry.name),
+                  alstr_get_cstr(entry.device_name));
+            VECTOR_PUSH_BACK(*DeviceList, entry);
+        }
+    }
+
     card = -1;
     if((err=snd_card_next(&card)) < 0)
         ERR("Failed to find a card: %s\n", snd_strerror(err));
@@ -321,7 +359,8 @@ static void probe_devices(snd_pcm_stream_t stream, vector_DevMap *DeviceList)
             snd_pcm_info_set_device(pcminfo, dev);
             snd_pcm_info_set_subdevice(pcminfo, 0);
             snd_pcm_info_set_stream(pcminfo, stream);
-            if((err = snd_ctl_pcm_info(handle, pcminfo)) < 0) {
+            if((err = snd_ctl_pcm_info(handle, pcminfo)) < 0)
+            {
                 if(err != -ENOENT)
                     ERR("control digital audio info (hw:%d): %s\n", card, snd_strerror(err));
                 continue;
@@ -333,15 +372,15 @@ static void probe_devices(snd_pcm_stream_t stream, vector_DevMap *DeviceList)
             ConfigValueStr(NULL, "alsa", name, &device_prefix);
 
             snprintf(name, sizeof(name), "%s, %s (CARD=%s,DEV=%d)",
-                        cardname, devname, cardid, dev);
+                     cardname, devname, cardid, dev);
             snprintf(device, sizeof(device), "%sCARD=%s,DEV=%d",
-                        device_prefix, cardid, dev);
+                     device_prefix, cardid, dev);
 
             TRACE("Got device \"%s\", \"%s\"\n", name, device);
             AL_STRING_INIT(entry.name);
             AL_STRING_INIT(entry.device_name);
-            al_string_copy_cstr(&entry.name, name);
-            al_string_copy_cstr(&entry.device_name, device);
+            alstr_copy_cstr(&entry.name, name);
+            alstr_copy_cstr(&entry.device_name, device);
             VECTOR_PUSH_BACK(*DeviceList, entry);
         }
         snd_ctl_close(handle);
@@ -413,7 +452,7 @@ static ALCboolean ALCplaybackAlsa_start(ALCplaybackAlsa *self);
 static void ALCplaybackAlsa_stop(ALCplaybackAlsa *self);
 static DECLARE_FORWARD2(ALCplaybackAlsa, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
 static DECLARE_FORWARD(ALCplaybackAlsa, ALCbackend, ALCuint, availableSamples)
-static ALint64 ALCplaybackAlsa_getLatency(ALCplaybackAlsa *self);
+static ClockLatency ALCplaybackAlsa_getClockLatency(ALCplaybackAlsa *self);
 static DECLARE_FORWARD(ALCplaybackAlsa, ALCbackend, void, lock)
 static DECLARE_FORWARD(ALCplaybackAlsa, ALCbackend, void, unlock)
 DECLARE_DEFAULT_ALLOCATORS(ALCplaybackAlsa)
@@ -588,7 +627,9 @@ static int ALCplaybackAlsa_mixerNoMMapProc(void *ptr)
             {
             case -EAGAIN:
                 continue;
+#if ESTRPIPE != EPIPE
             case -ESTRPIPE:
+#endif
             case -EPIPE:
             case -EINTR:
                 ret = snd_pcm_recover(self->pcmHandle, ret, 1);
@@ -630,12 +671,12 @@ static ALCenum ALCplaybackAlsa_open(ALCplaybackAlsa *self, const ALCchar *name)
         if(VECTOR_SIZE(PlaybackDevices) == 0)
             probe_devices(SND_PCM_STREAM_PLAYBACK, &PlaybackDevices);
 
-#define MATCH_NAME(i)  (al_string_cmp_cstr((i)->name, name) == 0)
+#define MATCH_NAME(i)  (alstr_cmp_cstr((i)->name, name) == 0)
         VECTOR_FIND_IF(iter, const DevMap, PlaybackDevices, MATCH_NAME);
 #undef MATCH_NAME
-        if(iter == VECTOR_ITER_END(PlaybackDevices))
+        if(iter == VECTOR_END(PlaybackDevices))
             return ALC_INVALID_VALUE;
-        driver = al_string_get_cstr(iter->device_name);
+        driver = alstr_get_cstr(iter->device_name);
     }
     else
     {
@@ -654,7 +695,7 @@ static ALCenum ALCplaybackAlsa_open(ALCplaybackAlsa *self, const ALCchar *name)
     /* Free alsa's global config tree. Otherwise valgrind reports a ton of leaks. */
     snd_config_update_free_global();
 
-    al_string_copy_cstr(&device->DeviceName, name);
+    alstr_copy_cstr(&device->DeviceName, name);
 
     return ALC_NO_ERROR;
 }
@@ -705,7 +746,7 @@ static ALCboolean ALCplaybackAlsa_reset(ALCplaybackAlsa *self)
             break;
     }
 
-    allowmmap = GetConfigValueBool(al_string_get_cstr(device->DeviceName), "alsa", "mmap", 1);
+    allowmmap = GetConfigValueBool(alstr_get_cstr(device->DeviceName), "alsa", "mmap", 1);
     periods = device->NumUpdates;
     periodLen = (ALuint64)device->UpdateSize * 1000000 / device->Frequency;
     bufferLen = periodLen * periods;
@@ -749,7 +790,7 @@ static ALCboolean ALCplaybackAlsa_reset(ALCplaybackAlsa *self)
     }
     CHECK(snd_pcm_hw_params_set_format(self->pcmHandle, hp, format));
     /* test and set channels (implicitly sets frame bits) */
-    if(snd_pcm_hw_params_test_channels(self->pcmHandle, hp, ChannelsFromDevFmt(device->FmtChans)) < 0)
+    if(snd_pcm_hw_params_test_channels(self->pcmHandle, hp, ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder)) < 0)
     {
         static const enum DevFmtChannels channellist[] = {
             DevFmtStereo,
@@ -762,20 +803,24 @@ static ALCboolean ALCplaybackAlsa_reset(ALCplaybackAlsa *self)
 
         for(k = 0;k < COUNTOF(channellist);k++)
         {
-            if(snd_pcm_hw_params_test_channels(self->pcmHandle, hp, ChannelsFromDevFmt(channellist[k])) >= 0)
+            if(snd_pcm_hw_params_test_channels(self->pcmHandle, hp, ChannelsFromDevFmt(channellist[k], 0)) >= 0)
             {
                 device->FmtChans = channellist[k];
+                device->AmbiOrder = 0;
                 break;
             }
         }
     }
-    CHECK(snd_pcm_hw_params_set_channels(self->pcmHandle, hp, ChannelsFromDevFmt(device->FmtChans)));
+    CHECK(snd_pcm_hw_params_set_channels(self->pcmHandle, hp, ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder)));
     /* set rate (implicitly constrains period/buffer parameters) */
-    if(!GetConfigValueBool(al_string_get_cstr(device->DeviceName), "alsa", "allow-resampler", 0))
+    if(!GetConfigValueBool(alstr_get_cstr(device->DeviceName), "alsa", "allow-resampler", 0) ||
+       !(device->Flags&DEVICE_FREQUENCY_REQUEST))
     {
         if(snd_pcm_hw_params_set_rate_resample(self->pcmHandle, hp, 0) < 0)
             ERR("Failed to disable ALSA resampler\n");
     }
+    else if(snd_pcm_hw_params_set_rate_resample(self->pcmHandle, hp, 1) < 0)
+        ERR("Failed to enable ALSA resampler\n");
     CHECK(snd_pcm_hw_params_set_rate_near(self->pcmHandle, hp, &rate, NULL));
     /* set buffer time (implicitly constrains period/buffer parameters) */
     if((err=snd_pcm_hw_params_set_buffer_time_near(self->pcmHandle, hp, &bufferLen, NULL)) < 0)
@@ -840,7 +885,7 @@ static ALCboolean ALCplaybackAlsa_start(ALCplaybackAlsa *self)
     self->size = snd_pcm_frames_to_bytes(self->pcmHandle, device->UpdateSize);
     if(access == SND_PCM_ACCESS_RW_INTERLEAVED)
     {
-        self->buffer = malloc(self->size);
+        self->buffer = al_malloc(16, self->size);
         if(!self->buffer)
         {
             ERR("buffer malloc failed\n");
@@ -862,7 +907,7 @@ static ALCboolean ALCplaybackAlsa_start(ALCplaybackAlsa *self)
     if(althrd_create(&self->thread, thread_func, self) != althrd_success)
     {
         ERR("Could not create playback thread\n");
-        free(self->buffer);
+        al_free(self->buffer);
         self->buffer = NULL;
         return ALC_FALSE;
     }
@@ -885,22 +930,29 @@ static void ALCplaybackAlsa_stop(ALCplaybackAlsa *self)
     self->killNow = 1;
     althrd_join(self->thread, &res);
 
-    free(self->buffer);
+    al_free(self->buffer);
     self->buffer = NULL;
 }
 
-static ALint64 ALCplaybackAlsa_getLatency(ALCplaybackAlsa *self)
+static ClockLatency ALCplaybackAlsa_getClockLatency(ALCplaybackAlsa *self)
 {
     ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
     snd_pcm_sframes_t delay = 0;
+    ClockLatency ret;
     int err;
 
+    ALCplaybackAlsa_lock(self);
+    ret.ClockTime = GetDeviceClockTime(device);
     if((err=snd_pcm_delay(self->pcmHandle, &delay)) < 0)
     {
         ERR("Failed to get pcm delay: %s\n", snd_strerror(err));
-        return 0;
+        delay = 0;
     }
-    return maxi64((ALint64)delay*1000000000/device->Frequency, 0);
+    if(delay < 0) delay = 0;
+    ret.Latency = delay * DEVICE_CLOCK_RES / device->Frequency;
+    ALCplaybackAlsa_unlock(self);
+
+    return ret;
 }
 
 
@@ -913,7 +965,7 @@ typedef struct ALCcaptureAlsa {
     ALsizei size;
 
     ALboolean doCapture;
-    RingBuffer *ring;
+    ll_ringbuffer_t *ring;
 
     snd_pcm_sframes_t last_avail;
 } ALCcaptureAlsa;
@@ -927,7 +979,7 @@ static ALCboolean ALCcaptureAlsa_start(ALCcaptureAlsa *self);
 static void ALCcaptureAlsa_stop(ALCcaptureAlsa *self);
 static ALCenum ALCcaptureAlsa_captureSamples(ALCcaptureAlsa *self, ALCvoid *buffer, ALCuint samples);
 static ALCuint ALCcaptureAlsa_availableSamples(ALCcaptureAlsa *self);
-static ALint64 ALCcaptureAlsa_getLatency(ALCcaptureAlsa *self);
+static ClockLatency ALCcaptureAlsa_getClockLatency(ALCcaptureAlsa *self);
 static DECLARE_FORWARD(ALCcaptureAlsa, ALCbackend, void, lock)
 static DECLARE_FORWARD(ALCcaptureAlsa, ALCbackend, void, unlock)
 DECLARE_DEFAULT_ALLOCATORS(ALCcaptureAlsa)
@@ -961,12 +1013,12 @@ static ALCenum ALCcaptureAlsa_open(ALCcaptureAlsa *self, const ALCchar *name)
         if(VECTOR_SIZE(CaptureDevices) == 0)
             probe_devices(SND_PCM_STREAM_CAPTURE, &CaptureDevices);
 
-#define MATCH_NAME(i)  (al_string_cmp_cstr((i)->name, name) == 0)
+#define MATCH_NAME(i)  (alstr_cmp_cstr((i)->name, name) == 0)
         VECTOR_FIND_IF(iter, const DevMap, CaptureDevices, MATCH_NAME);
 #undef MATCH_NAME
-        if(iter == VECTOR_ITER_END(CaptureDevices))
+        if(iter == VECTOR_END(CaptureDevices))
             return ALC_INVALID_VALUE;
-        driver = al_string_get_cstr(iter->device_name);
+        driver = alstr_get_cstr(iter->device_name);
     }
     else
     {
@@ -1023,7 +1075,7 @@ static ALCenum ALCcaptureAlsa_open(ALCcaptureAlsa *self, const ALCchar *name)
     /* set format (implicitly sets sample bits) */
     CHECK(snd_pcm_hw_params_set_format(self->pcmHandle, hp, format));
     /* set channels (implicitly sets frame bits) */
-    CHECK(snd_pcm_hw_params_set_channels(self->pcmHandle, hp, ChannelsFromDevFmt(device->FmtChans)));
+    CHECK(snd_pcm_hw_params_set_channels(self->pcmHandle, hp, ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder)));
     /* set rate (implicitly constrains period/buffer parameters) */
     CHECK(snd_pcm_hw_params_set_rate(self->pcmHandle, hp, device->Frequency, 0));
     /* set buffer size in frame units (implicitly sets period size/bytes/time and buffer time/bytes) */
@@ -1045,24 +1097,18 @@ static ALCenum ALCcaptureAlsa_open(ALCcaptureAlsa *self, const ALCchar *name)
 
     if(needring)
     {
-        self->ring = CreateRingBuffer(FrameSizeFromDevFmt(device->FmtChans, device->FmtType),
-                                      device->UpdateSize*device->NumUpdates);
+        self->ring = ll_ringbuffer_create(
+            device->UpdateSize*device->NumUpdates + 1,
+            FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder)
+        );
         if(!self->ring)
         {
             ERR("ring buffer create failed\n");
             goto error2;
         }
-
-        self->size = snd_pcm_frames_to_bytes(self->pcmHandle, periodSizeInFrames);
-        self->buffer = malloc(self->size);
-        if(!self->buffer)
-        {
-            ERR("buffer malloc failed\n");
-            goto error2;
-        }
     }
 
-    al_string_copy_cstr(&device->DeviceName, name);
+    alstr_copy_cstr(&device->DeviceName, name);
 
     return ALC_NO_ERROR;
 
@@ -1071,9 +1117,7 @@ error:
     if(hp) snd_pcm_hw_params_free(hp);
 
 error2:
-    free(self->buffer);
-    self->buffer = NULL;
-    DestroyRingBuffer(self->ring);
+    ll_ringbuffer_free(self->ring);
     self->ring = NULL;
     snd_pcm_close(self->pcmHandle);
 
@@ -1083,9 +1127,9 @@ error2:
 static void ALCcaptureAlsa_close(ALCcaptureAlsa *self)
 {
     snd_pcm_close(self->pcmHandle);
-    DestroyRingBuffer(self->ring);
+    ll_ringbuffer_free(self->ring);
 
-    free(self->buffer);
+    al_free(self->buffer);
     self->buffer = NULL;
 }
 
@@ -1120,11 +1164,11 @@ static void ALCcaptureAlsa_stop(ALCcaptureAlsa *self)
         void *ptr;
 
         size = snd_pcm_frames_to_bytes(self->pcmHandle, avail);
-        ptr = malloc(size);
+        ptr = al_malloc(16, size);
         if(ptr)
         {
             ALCcaptureAlsa_captureSamples(self, ptr, avail);
-            free(self->buffer);
+            al_free(self->buffer);
             self->buffer = ptr;
             self->size = size;
         }
@@ -1141,7 +1185,7 @@ static ALCenum ALCcaptureAlsa_captureSamples(ALCcaptureAlsa *self, ALCvoid *buff
 
     if(self->ring)
     {
-        ReadRingBuffer(self->ring, buffer, samples);
+        ll_ringbuffer_read(self->ring, buffer, samples);
         return ALC_NO_ERROR;
     }
 
@@ -1166,7 +1210,7 @@ static ALCenum ALCcaptureAlsa_captureSamples(ALCcaptureAlsa *self, ALCvoid *buff
             }
             else
             {
-                free(self->buffer);
+                al_free(self->buffer);
                 self->buffer = NULL;
                 self->size = 0;
             }
@@ -1244,12 +1288,15 @@ static ALCuint ALCcaptureAlsa_availableSamples(ALCcaptureAlsa *self)
 
     while(avail > 0)
     {
+        ll_ringbuffer_data_t vec[2];
         snd_pcm_sframes_t amt;
 
-        amt = snd_pcm_bytes_to_frames(self->pcmHandle, self->size);
-        if(avail < amt) amt = avail;
+        ll_ringbuffer_get_write_vector(self->ring, vec);
+        if(vec[0].len == 0) break;
 
-        amt = snd_pcm_readi(self->pcmHandle, self->buffer, amt);
+        amt = (vec[0].len < (snd_pcm_uframes_t)avail) ?
+              vec[0].len : (snd_pcm_uframes_t)avail;
+        amt = snd_pcm_readi(self->pcmHandle, vec[0].buf, amt);
         if(amt < 0)
         {
             ERR("read error: %s\n", snd_strerror(amt));
@@ -1273,32 +1320,39 @@ static ALCuint ALCcaptureAlsa_availableSamples(ALCcaptureAlsa *self)
             continue;
         }
 
-        WriteRingBuffer(self->ring, self->buffer, amt);
+        ll_ringbuffer_write_advance(self->ring, amt);
         avail -= amt;
     }
 
-    return RingBufferSize(self->ring);
+    return ll_ringbuffer_read_space(self->ring);
 }
 
-static ALint64 ALCcaptureAlsa_getLatency(ALCcaptureAlsa *self)
+static ClockLatency ALCcaptureAlsa_getClockLatency(ALCcaptureAlsa *self)
 {
     ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
     snd_pcm_sframes_t delay = 0;
+    ClockLatency ret;
     int err;
 
+    ALCcaptureAlsa_lock(self);
+    ret.ClockTime = GetDeviceClockTime(device);
     if((err=snd_pcm_delay(self->pcmHandle, &delay)) < 0)
     {
         ERR("Failed to get pcm delay: %s\n", snd_strerror(err));
-        return 0;
+        delay = 0;
     }
-    return maxi64((ALint64)delay*1000000000/device->Frequency, 0);
+    if(delay < 0) delay = 0;
+    ret.Latency = delay * DEVICE_CLOCK_RES / device->Frequency;
+    ALCcaptureAlsa_unlock(self);
+
+    return ret;
 }
 
 
 static inline void AppendAllDevicesList2(const DevMap *entry)
-{ AppendAllDevicesList(al_string_get_cstr(entry->name)); }
+{ AppendAllDevicesList(alstr_get_cstr(entry->name)); }
 static inline void AppendCaptureDeviceList2(const DevMap *entry)
-{ AppendCaptureDeviceList(al_string_get_cstr(entry->name)); }
+{ AppendCaptureDeviceList(alstr_get_cstr(entry->name)); }
 
 typedef struct ALCalsaBackendFactory {
     DERIVE_FROM_TYPE(ALCbackendFactory);

+ 25 - 159
libs/openal-soft/Alc/backends/base.c

@@ -4,17 +4,19 @@
 #include <stdlib.h>
 
 #include "alMain.h"
+#include "alu.h"
 
 #include "backends/base.h"
 
 
+extern inline ALuint64 GetDeviceClockTime(ALCdevice *device);
+
 /* Base ALCbackend method implementations. */
 void ALCbackend_Construct(ALCbackend *self, ALCdevice *device)
 {
-    int ret;
-    self->mDevice = device;
-    ret = almtx_init(&self->mMutex, almtx_recursive);
+    int ret = almtx_init(&self->mMutex, almtx_recursive);
     assert(ret == althrd_success);
+    self->mDevice = device;
 }
 
 void ALCbackend_Destruct(ALCbackend *self)
@@ -37,9 +39,27 @@ ALCuint ALCbackend_availableSamples(ALCbackend* UNUSED(self))
     return 0;
 }
 
-ALint64 ALCbackend_getLatency(ALCbackend* UNUSED(self))
+ClockLatency ALCbackend_getClockLatency(ALCbackend *self)
 {
-    return 0;
+    ALCdevice *device = self->mDevice;
+    ALuint refcount;
+    ClockLatency ret;
+
+    do {
+        while(((refcount=ATOMIC_LOAD(&device->MixCount, almemory_order_acquire))&1))
+            althrd_yield();
+        ret.ClockTime = GetDeviceClockTime(device);
+        ATOMIC_THREAD_FENCE(almemory_order_acquire);
+    } while(refcount != ATOMIC_LOAD(&device->MixCount, almemory_order_relaxed));
+
+    /* NOTE: The device will generally have about all but one periods filled at
+     * any given time during playback. Without a more accurate measurement from
+     * the output, this is an okay approximation.
+     */
+    ret.Latency = device->UpdateSize * DEVICE_CLOCK_RES / device->Frequency *
+                  maxu(device->NumUpdates-1, 1);
+
+    return ret;
 }
 
 void ALCbackend_lock(ALCbackend *self)
@@ -59,157 +79,3 @@ void ALCbackend_unlock(ALCbackend *self)
 void ALCbackendFactory_deinit(ALCbackendFactory* UNUSED(self))
 {
 }
-
-
-/* Wrappers to use an old-style backend with the new interface. */
-typedef struct PlaybackWrapper {
-    DERIVE_FROM_TYPE(ALCbackend);
-
-    const BackendFuncs *Funcs;
-} PlaybackWrapper;
-
-static void PlaybackWrapper_Construct(PlaybackWrapper *self, ALCdevice *device, const BackendFuncs *funcs);
-static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, void, Destruct)
-static ALCenum PlaybackWrapper_open(PlaybackWrapper *self, const ALCchar *name);
-static void PlaybackWrapper_close(PlaybackWrapper *self);
-static ALCboolean PlaybackWrapper_reset(PlaybackWrapper *self);
-static ALCboolean PlaybackWrapper_start(PlaybackWrapper *self);
-static void PlaybackWrapper_stop(PlaybackWrapper *self);
-static DECLARE_FORWARD2(PlaybackWrapper, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
-static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, ALCuint, availableSamples)
-static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, ALint64, getLatency)
-static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, void, lock)
-static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, void, unlock)
-DECLARE_DEFAULT_ALLOCATORS(PlaybackWrapper)
-DEFINE_ALCBACKEND_VTABLE(PlaybackWrapper);
-
-static void PlaybackWrapper_Construct(PlaybackWrapper *self, ALCdevice *device, const BackendFuncs *funcs)
-{
-    ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
-    SET_VTABLE2(PlaybackWrapper, ALCbackend, self);
-
-    self->Funcs = funcs;
-}
-
-static ALCenum PlaybackWrapper_open(PlaybackWrapper *self, const ALCchar *name)
-{
-    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
-    return self->Funcs->OpenPlayback(device, name);
-}
-
-static void PlaybackWrapper_close(PlaybackWrapper *self)
-{
-    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
-    self->Funcs->ClosePlayback(device);
-}
-
-static ALCboolean PlaybackWrapper_reset(PlaybackWrapper *self)
-{
-    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
-    return self->Funcs->ResetPlayback(device);
-}
-
-static ALCboolean PlaybackWrapper_start(PlaybackWrapper *self)
-{
-    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
-    return self->Funcs->StartPlayback(device);
-}
-
-static void PlaybackWrapper_stop(PlaybackWrapper *self)
-{
-    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
-    self->Funcs->StopPlayback(device);
-}
-
-
-typedef struct CaptureWrapper {
-    DERIVE_FROM_TYPE(ALCbackend);
-
-    const BackendFuncs *Funcs;
-} CaptureWrapper;
-
-static void CaptureWrapper_Construct(CaptureWrapper *self, ALCdevice *device, const BackendFuncs *funcs);
-static DECLARE_FORWARD(CaptureWrapper, ALCbackend, void, Destruct)
-static ALCenum CaptureWrapper_open(CaptureWrapper *self, const ALCchar *name);
-static void CaptureWrapper_close(CaptureWrapper *self);
-static DECLARE_FORWARD(CaptureWrapper, ALCbackend, ALCboolean, reset)
-static ALCboolean CaptureWrapper_start(CaptureWrapper *self);
-static void CaptureWrapper_stop(CaptureWrapper *self);
-static ALCenum CaptureWrapper_captureSamples(CaptureWrapper *self, void *buffer, ALCuint samples);
-static ALCuint CaptureWrapper_availableSamples(CaptureWrapper *self);
-static DECLARE_FORWARD(CaptureWrapper, ALCbackend, ALint64, getLatency)
-static DECLARE_FORWARD(CaptureWrapper, ALCbackend, void, lock)
-static DECLARE_FORWARD(CaptureWrapper, ALCbackend, void, unlock)
-DECLARE_DEFAULT_ALLOCATORS(CaptureWrapper)
-DEFINE_ALCBACKEND_VTABLE(CaptureWrapper);
-
-static void CaptureWrapper_Construct(CaptureWrapper *self, ALCdevice *device, const BackendFuncs *funcs)
-{
-    ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
-    SET_VTABLE2(CaptureWrapper, ALCbackend, self);
-
-    self->Funcs = funcs;
-}
-
-static ALCenum CaptureWrapper_open(CaptureWrapper *self, const ALCchar *name)
-{
-    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
-    return self->Funcs->OpenCapture(device, name);
-}
-
-static void CaptureWrapper_close(CaptureWrapper *self)
-{
-    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
-    self->Funcs->CloseCapture(device);
-}
-
-static ALCboolean CaptureWrapper_start(CaptureWrapper *self)
-{
-    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
-    self->Funcs->StartCapture(device);
-    return ALC_TRUE;
-}
-
-static void CaptureWrapper_stop(CaptureWrapper *self)
-{
-    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
-    self->Funcs->StopCapture(device);
-}
-
-static ALCenum CaptureWrapper_captureSamples(CaptureWrapper *self, void *buffer, ALCuint samples)
-{
-    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
-    return self->Funcs->CaptureSamples(device, buffer, samples);
-}
-
-static ALCuint CaptureWrapper_availableSamples(CaptureWrapper *self)
-{
-    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
-    return self->Funcs->AvailableSamples(device);
-}
-
-
-ALCbackend *create_backend_wrapper(ALCdevice *device, const BackendFuncs *funcs, ALCbackend_Type type)
-{
-    if(type == ALCbackend_Playback)
-    {
-        PlaybackWrapper *backend;
-
-        NEW_OBJ(backend, PlaybackWrapper)(device, funcs);
-        if(!backend) return NULL;
-
-        return STATIC_CAST(ALCbackend, backend);
-    }
-
-    if(type == ALCbackend_Capture)
-    {
-        CaptureWrapper *backend;
-
-        NEW_OBJ(backend, CaptureWrapper)(device, funcs);
-        if(!backend) return NULL;
-
-        return STATIC_CAST(ALCbackend, backend);
-    }
-
-    return NULL;
-}

+ 23 - 6
libs/openal-soft/Alc/backends/base.h

@@ -5,6 +5,21 @@
 #include "threads.h"
 
 
+typedef struct ClockLatency {
+    ALint64 ClockTime;
+    ALint64 Latency;
+} ClockLatency;
+
+/* Helper to get the current clock time from the device's ClockBase, and
+ * SamplesDone converted from the sample rate.
+ */
+inline ALuint64 GetDeviceClockTime(ALCdevice *device)
+{
+    return device->ClockBase + (device->SamplesDone * DEVICE_CLOCK_RES /
+                                device->Frequency);
+}
+
+
 struct ALCbackendVtable;
 
 typedef struct ALCbackend {
@@ -20,7 +35,7 @@ void ALCbackend_Destruct(ALCbackend *self);
 ALCboolean ALCbackend_reset(ALCbackend *self);
 ALCenum ALCbackend_captureSamples(ALCbackend *self, void *buffer, ALCuint samples);
 ALCuint ALCbackend_availableSamples(ALCbackend *self);
-ALint64 ALCbackend_getLatency(ALCbackend *self);
+ClockLatency ALCbackend_getClockLatency(ALCbackend *self);
 void ALCbackend_lock(ALCbackend *self);
 void ALCbackend_unlock(ALCbackend *self);
 
@@ -37,7 +52,7 @@ struct ALCbackendVtable {
     ALCenum (*const captureSamples)(ALCbackend*, void*, ALCuint);
     ALCuint (*const availableSamples)(ALCbackend*);
 
-    ALint64 (*const getLatency)(ALCbackend*);
+    ClockLatency (*const getClockLatency)(ALCbackend*);
 
     void (*const lock)(ALCbackend*);
     void (*const unlock)(ALCbackend*);
@@ -54,7 +69,7 @@ DECLARE_THUNK(T, ALCbackend, ALCboolean, start)                               \
 DECLARE_THUNK(T, ALCbackend, void, stop)                                      \
 DECLARE_THUNK2(T, ALCbackend, ALCenum, captureSamples, void*, ALCuint)        \
 DECLARE_THUNK(T, ALCbackend, ALCuint, availableSamples)                       \
-DECLARE_THUNK(T, ALCbackend, ALint64, getLatency)                             \
+DECLARE_THUNK(T, ALCbackend, ClockLatency, getClockLatency)                   \
 DECLARE_THUNK(T, ALCbackend, void, lock)                                      \
 DECLARE_THUNK(T, ALCbackend, void, unlock)                                    \
 static void T##_ALCbackend_Delete(void *ptr)                                  \
@@ -70,7 +85,7 @@ static const struct ALCbackendVtable T##_ALCbackend_vtable = {                \
     T##_ALCbackend_stop,                                                      \
     T##_ALCbackend_captureSamples,                                            \
     T##_ALCbackend_availableSamples,                                          \
-    T##_ALCbackend_getLatency,                                                \
+    T##_ALCbackend_getClockLatency,                                           \
     T##_ALCbackend_lock,                                                      \
     T##_ALCbackend_unlock,                                                    \
                                                                               \
@@ -122,17 +137,19 @@ static const struct ALCbackendFactoryVtable T##_ALCbackendFactory_vtable = {  \
 
 ALCbackendFactory *ALCpulseBackendFactory_getFactory(void);
 ALCbackendFactory *ALCalsaBackendFactory_getFactory(void);
+ALCbackendFactory *ALCcoreAudioBackendFactory_getFactory(void);
 ALCbackendFactory *ALCossBackendFactory_getFactory(void);
 ALCbackendFactory *ALCjackBackendFactory_getFactory(void);
 ALCbackendFactory *ALCsolarisBackendFactory_getFactory(void);
+ALCbackendFactory *ALCsndioBackendFactory_getFactory(void);
+ALCbackendFactory *ALCqsaBackendFactory_getFactory(void);
 ALCbackendFactory *ALCmmdevBackendFactory_getFactory(void);
 ALCbackendFactory *ALCdsoundBackendFactory_getFactory(void);
 ALCbackendFactory *ALCwinmmBackendFactory_getFactory(void);
 ALCbackendFactory *ALCportBackendFactory_getFactory(void);
+ALCbackendFactory *ALCopenslBackendFactory_getFactory(void);
 ALCbackendFactory *ALCnullBackendFactory_getFactory(void);
 ALCbackendFactory *ALCwaveBackendFactory_getFactory(void);
 ALCbackendFactory *ALCloopbackFactory_getFactory(void);
 
-ALCbackend *create_backend_wrapper(ALCdevice *device, const BackendFuncs *funcs, ALCbackend_Type type);
-
 #endif /* AL_BACKENDS_BASE_H */

+ 286 - 176
libs/openal-soft/Alc/backends/coreaudio.c

@@ -33,6 +33,8 @@
 #include <AudioUnit/AudioUnit.h>
 #include <AudioToolbox/AudioToolbox.h>
 
+#include "backends/base.h"
+
 
 typedef struct {
     AudioUnit audioUnit;
@@ -45,23 +47,12 @@ typedef struct {
     AudioBufferList *bufferList;           // Buffer for data coming from the input device
     ALCvoid *resampleBuffer;               // Buffer for returned RingBuffer data when resampling
 
-    RingBuffer *ring;
+    ll_ringbuffer_t *ring;
 } ca_data;
 
 static const ALCchar ca_device[] = "CoreAudio Default";
 
 
-static void destroy_buffer_list(AudioBufferList* list)
-{
-    if(list)
-    {
-        UInt32 i;
-        for(i = 0;i < list->mNumberBuffers;i++)
-            free(list->mBuffers[i].mData);
-        free(list);
-    }
-}
-
 static AudioBufferList* allocate_buffer_list(UInt32 channelCount, UInt32 byteSize)
 {
     AudioBufferList *list;
@@ -83,68 +74,85 @@ static AudioBufferList* allocate_buffer_list(UInt32 channelCount, UInt32 byteSiz
     return list;
 }
 
-static OSStatus ca_callback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp,
-                            UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData)
+static void destroy_buffer_list(AudioBufferList* list)
 {
-    ALCdevice *device = (ALCdevice*)inRefCon;
-    ca_data *data = (ca_data*)device->ExtraData;
-
-    aluMixData(device, ioData->mBuffers[0].mData,
-               ioData->mBuffers[0].mDataByteSize / data->frameSize);
-
-    return noErr;
+    if(list)
+    {
+        UInt32 i;
+        for(i = 0;i < list->mNumberBuffers;i++)
+            free(list->mBuffers[i].mData);
+        free(list);
+    }
 }
 
-static OSStatus ca_capture_conversion_callback(AudioConverterRef inAudioConverter, UInt32 *ioNumberDataPackets,
-        AudioBufferList *ioData, AudioStreamPacketDescription **outDataPacketDescription, void* inUserData)
-{
-    ALCdevice *device = (ALCdevice*)inUserData;
-    ca_data *data = (ca_data*)device->ExtraData;
 
-    // Read from the ring buffer and store temporarily in a large buffer
-    ReadRingBuffer(data->ring, data->resampleBuffer, (ALsizei)(*ioNumberDataPackets));
+typedef struct ALCcoreAudioPlayback {
+    DERIVE_FROM_TYPE(ALCbackend);
 
-    // Set the input data
-    ioData->mNumberBuffers = 1;
-    ioData->mBuffers[0].mNumberChannels = data->format.mChannelsPerFrame;
-    ioData->mBuffers[0].mData = data->resampleBuffer;
-    ioData->mBuffers[0].mDataByteSize = (*ioNumberDataPackets) * data->format.mBytesPerFrame;
+    AudioUnit audioUnit;
 
-    return noErr;
+    ALuint frameSize;
+    AudioStreamBasicDescription format;    // This is the OpenAL format as a CoreAudio ASBD
+} ALCcoreAudioPlayback;
+
+static void ALCcoreAudioPlayback_Construct(ALCcoreAudioPlayback *self, ALCdevice *device);
+static void ALCcoreAudioPlayback_Destruct(ALCcoreAudioPlayback *self);
+static ALCenum ALCcoreAudioPlayback_open(ALCcoreAudioPlayback *self, const ALCchar *name);
+static void ALCcoreAudioPlayback_close(ALCcoreAudioPlayback *self);
+static ALCboolean ALCcoreAudioPlayback_reset(ALCcoreAudioPlayback *self);
+static ALCboolean ALCcoreAudioPlayback_start(ALCcoreAudioPlayback *self);
+static void ALCcoreAudioPlayback_stop(ALCcoreAudioPlayback *self);
+static DECLARE_FORWARD2(ALCcoreAudioPlayback, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
+static DECLARE_FORWARD(ALCcoreAudioPlayback, ALCbackend, ALCuint, availableSamples)
+static DECLARE_FORWARD(ALCcoreAudioPlayback, ALCbackend, ClockLatency, getClockLatency)
+static DECLARE_FORWARD(ALCcoreAudioPlayback, ALCbackend, void, lock)
+static DECLARE_FORWARD(ALCcoreAudioPlayback, ALCbackend, void, unlock)
+DECLARE_DEFAULT_ALLOCATORS(ALCcoreAudioPlayback)
+
+DEFINE_ALCBACKEND_VTABLE(ALCcoreAudioPlayback);
+
+
+static void ALCcoreAudioPlayback_Construct(ALCcoreAudioPlayback *self, ALCdevice *device)
+{
+    ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
+    SET_VTABLE2(ALCcoreAudioPlayback, ALCbackend, self);
+
+    self->frameSize = 0;
+    memset(&self->format, 0, sizeof(self->format));
 }
 
-static OSStatus ca_capture_callback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags,
-                                    const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber,
-                                    UInt32 inNumberFrames, AudioBufferList *ioData)
+static void ALCcoreAudioPlayback_Destruct(ALCcoreAudioPlayback *self)
 {
-    ALCdevice *device = (ALCdevice*)inRefCon;
-    ca_data *data = (ca_data*)device->ExtraData;
-    AudioUnitRenderActionFlags flags = 0;
-    OSStatus err;
+    ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
+}
 
-    // fill the bufferList with data from the input device
-    err = AudioUnitRender(data->audioUnit, &flags, inTimeStamp, 1, inNumberFrames, data->bufferList);
-    if(err != noErr)
-    {
-        ERR("AudioUnitRender error: %d\n", err);
-        return err;
-    }
 
-    WriteRingBuffer(data->ring, data->bufferList->mBuffers[0].mData, inNumberFrames);
+static OSStatus ALCcoreAudioPlayback_MixerProc(void *inRefCon,
+  AudioUnitRenderActionFlags* UNUSED(ioActionFlags), const AudioTimeStamp* UNUSED(inTimeStamp),
+  UInt32 UNUSED(inBusNumber), UInt32 UNUSED(inNumberFrames), AudioBufferList *ioData)
+{
+    ALCcoreAudioPlayback *self = inRefCon;
+    ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
+
+    ALCdevice_Lock(device);
+    aluMixData(device, ioData->mBuffers[0].mData,
+               ioData->mBuffers[0].mDataByteSize / self->frameSize);
+    ALCdevice_Unlock(device);
 
     return noErr;
 }
 
-static ALCenum ca_open_playback(ALCdevice *device, const ALCchar *deviceName)
+
+static ALCenum ALCcoreAudioPlayback_open(ALCcoreAudioPlayback *self, const ALCchar *name)
 {
+    ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
     AudioComponentDescription desc;
     AudioComponent comp;
-    ca_data *data;
     OSStatus err;
 
-    if(!deviceName)
-        deviceName = ca_device;
-    else if(strcmp(deviceName, ca_device) != 0)
+    if(!name)
+        name = ca_device;
+    else if(strcmp(name, ca_device) != 0)
         return ALC_INVALID_VALUE;
 
     /* open the default output unit */
@@ -161,57 +169,47 @@ static ALCenum ca_open_playback(ALCdevice *device, const ALCchar *deviceName)
         return ALC_INVALID_VALUE;
     }
 
-    data = calloc(1, sizeof(*data));
-
-    err = AudioComponentInstanceNew(comp, &data->audioUnit);
+    err = AudioComponentInstanceNew(comp, &self->audioUnit);
     if(err != noErr)
     {
         ERR("AudioComponentInstanceNew failed\n");
-        free(data);
         return ALC_INVALID_VALUE;
     }
 
     /* init and start the default audio unit... */
-    err = AudioUnitInitialize(data->audioUnit);
+    err = AudioUnitInitialize(self->audioUnit);
     if(err != noErr)
     {
         ERR("AudioUnitInitialize failed\n");
-        AudioComponentInstanceDispose(data->audioUnit);
-        free(data);
+        AudioComponentInstanceDispose(self->audioUnit);
         return ALC_INVALID_VALUE;
     }
 
-    al_string_copy_cstr(&device->DeviceName, deviceName);
-    device->ExtraData = data;
+    alstr_copy_cstr(&device->DeviceName, name);
     return ALC_NO_ERROR;
 }
 
-static void ca_close_playback(ALCdevice *device)
+static void ALCcoreAudioPlayback_close(ALCcoreAudioPlayback *self)
 {
-    ca_data *data = (ca_data*)device->ExtraData;
-
-    AudioUnitUninitialize(data->audioUnit);
-    AudioComponentInstanceDispose(data->audioUnit);
-
-    free(data);
-    device->ExtraData = NULL;
+    AudioUnitUninitialize(self->audioUnit);
+    AudioComponentInstanceDispose(self->audioUnit);
 }
 
-static ALCboolean ca_reset_playback(ALCdevice *device)
+static ALCboolean ALCcoreAudioPlayback_reset(ALCcoreAudioPlayback *self)
 {
-    ca_data *data = (ca_data*)device->ExtraData;
+    ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
     AudioStreamBasicDescription streamFormat;
     AURenderCallbackStruct input;
     OSStatus err;
     UInt32 size;
 
-    err = AudioUnitUninitialize(data->audioUnit);
+    err = AudioUnitUninitialize(self->audioUnit);
     if(err != noErr)
         ERR("-- AudioUnitUninitialize failed.\n");
 
     /* retrieve default output unit's properties (output side) */
     size = sizeof(AudioStreamBasicDescription);
-    err = AudioUnitGetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &streamFormat, &size);
+    err = AudioUnitGetProperty(self->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &streamFormat, &size);
     if(err != noErr || size != sizeof(AudioStreamBasicDescription))
     {
         ERR("AudioUnitGetProperty failed\n");
@@ -229,7 +227,7 @@ static ALCboolean ca_reset_playback(ALCdevice *device)
 #endif
 
     /* set default output unit's input side to match output side */
-    err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &streamFormat, size);
+    err = AudioUnitSetProperty(self->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &streamFormat, size);
     if(err != noErr)
     {
         ERR("AudioUnitSetProperty failed\n");
@@ -238,7 +236,7 @@ static ALCboolean ca_reset_playback(ALCdevice *device)
 
     if(device->Frequency != streamFormat.mSampleRate)
     {
-        device->UpdateSize = (ALuint)((ALuint64)device->UpdateSize *
+        device->NumUpdates = (ALuint)((ALuint64)device->NumUpdates *
                                       streamFormat.mSampleRate /
                                       device->Frequency);
         device->Frequency = streamFormat.mSampleRate;
@@ -313,7 +311,7 @@ static ALCboolean ca_reset_playback(ALCdevice *device)
     streamFormat.mFormatFlags |= kAudioFormatFlagsNativeEndian |
                                  kLinearPCMFormatFlagIsPacked;
 
-    err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &streamFormat, sizeof(AudioStreamBasicDescription));
+    err = AudioUnitSetProperty(self->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &streamFormat, sizeof(AudioStreamBasicDescription));
     if(err != noErr)
     {
         ERR("AudioUnitSetProperty failed\n");
@@ -321,11 +319,11 @@ static ALCboolean ca_reset_playback(ALCdevice *device)
     }
 
     /* setup callback */
-    data->frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
-    input.inputProc = ca_callback;
-    input.inputProcRefCon = device;
+    self->frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
+    input.inputProc = ALCcoreAudioPlayback_MixerProc;
+    input.inputProcRefCon = self;
 
-    err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &input, sizeof(AURenderCallbackStruct));
+    err = AudioUnitSetProperty(self->audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &input, sizeof(AURenderCallbackStruct));
     if(err != noErr)
     {
         ERR("AudioUnitSetProperty failed\n");
@@ -333,7 +331,7 @@ static ALCboolean ca_reset_playback(ALCdevice *device)
     }
 
     /* init the default audio unit... */
-    err = AudioUnitInitialize(data->audioUnit);
+    err = AudioUnitInitialize(self->audioUnit);
     if(err != noErr)
     {
         ERR("AudioUnitInitialize failed\n");
@@ -343,12 +341,9 @@ static ALCboolean ca_reset_playback(ALCdevice *device)
     return ALC_TRUE;
 }
 
-static ALCboolean ca_start_playback(ALCdevice *device)
+static ALCboolean ALCcoreAudioPlayback_start(ALCcoreAudioPlayback *self)
 {
-    ca_data *data = (ca_data*)device->ExtraData;
-    OSStatus err;
-
-    err = AudioOutputUnitStart(data->audioUnit);
+    OSStatus err = AudioOutputUnitStart(self->audioUnit);
     if(err != noErr)
     {
         ERR("AudioOutputUnitStart failed\n");
@@ -358,18 +353,107 @@ static ALCboolean ca_start_playback(ALCdevice *device)
     return ALC_TRUE;
 }
 
-static void ca_stop_playback(ALCdevice *device)
+static void ALCcoreAudioPlayback_stop(ALCcoreAudioPlayback *self)
+{
+    OSStatus err = AudioOutputUnitStop(self->audioUnit);
+    if(err != noErr)
+        ERR("AudioOutputUnitStop failed\n");
+}
+
+
+
+
+typedef struct ALCcoreAudioCapture {
+    DERIVE_FROM_TYPE(ALCbackend);
+
+    AudioUnit audioUnit;
+
+    ALuint frameSize;
+    ALdouble sampleRateRatio;              // Ratio of hardware sample rate / requested sample rate
+    AudioStreamBasicDescription format;    // This is the OpenAL format as a CoreAudio ASBD
+
+    AudioConverterRef audioConverter;      // Sample rate converter if needed
+    AudioBufferList *bufferList;           // Buffer for data coming from the input device
+    ALCvoid *resampleBuffer;               // Buffer for returned RingBuffer data when resampling
+
+    ll_ringbuffer_t *ring;
+} ALCcoreAudioCapture;
+
+static void ALCcoreAudioCapture_Construct(ALCcoreAudioCapture *self, ALCdevice *device);
+static void ALCcoreAudioCapture_Destruct(ALCcoreAudioCapture *self);
+static ALCenum ALCcoreAudioCapture_open(ALCcoreAudioCapture *self, const ALCchar *name);
+static void ALCcoreAudioCapture_close(ALCcoreAudioCapture *self);
+static DECLARE_FORWARD(ALCcoreAudioCapture, ALCbackend, ALCboolean, reset)
+static ALCboolean ALCcoreAudioCapture_start(ALCcoreAudioCapture *self);
+static void ALCcoreAudioCapture_stop(ALCcoreAudioCapture *self);
+static ALCenum ALCcoreAudioCapture_captureSamples(ALCcoreAudioCapture *self, ALCvoid *buffer, ALCuint samples);
+static ALCuint ALCcoreAudioCapture_availableSamples(ALCcoreAudioCapture *self);
+static DECLARE_FORWARD(ALCcoreAudioCapture, ALCbackend, ClockLatency, getClockLatency)
+static DECLARE_FORWARD(ALCcoreAudioCapture, ALCbackend, void, lock)
+static DECLARE_FORWARD(ALCcoreAudioCapture, ALCbackend, void, unlock)
+DECLARE_DEFAULT_ALLOCATORS(ALCcoreAudioCapture)
+
+DEFINE_ALCBACKEND_VTABLE(ALCcoreAudioCapture);
+
+
+static void ALCcoreAudioCapture_Construct(ALCcoreAudioCapture *self, ALCdevice *device)
+{
+    ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
+    SET_VTABLE2(ALCcoreAudioCapture, ALCbackend, self);
+
+}
+
+static void ALCcoreAudioCapture_Destruct(ALCcoreAudioCapture *self)
+{
+    ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
+}
+
+
+static OSStatus ALCcoreAudioCapture_RecordProc(void *inRefCon,
+  AudioUnitRenderActionFlags* UNUSED(ioActionFlags),
+  const AudioTimeStamp *inTimeStamp, UInt32 UNUSED(inBusNumber),
+  UInt32 inNumberFrames, AudioBufferList* UNUSED(ioData))
 {
-    ca_data *data = (ca_data*)device->ExtraData;
+    ALCcoreAudioCapture *self = inRefCon;
+    AudioUnitRenderActionFlags flags = 0;
     OSStatus err;
 
-    err = AudioOutputUnitStop(data->audioUnit);
+    // fill the bufferList with data from the input device
+    err = AudioUnitRender(self->audioUnit, &flags, inTimeStamp, 1, inNumberFrames, self->bufferList);
     if(err != noErr)
-        ERR("AudioOutputUnitStop failed\n");
+    {
+        ERR("AudioUnitRender error: %d\n", err);
+        return err;
+    }
+
+    ll_ringbuffer_write(self->ring, self->bufferList->mBuffers[0].mData, inNumberFrames);
+
+    return noErr;
+}
+
+static OSStatus ALCcoreAudioCapture_ConvertCallback(AudioConverterRef UNUSED(inAudioConverter),
+  UInt32 *ioNumberDataPackets, AudioBufferList *ioData,
+  AudioStreamPacketDescription** UNUSED(outDataPacketDescription),
+  void *inUserData)
+{
+    ALCcoreAudioCapture *self = inUserData;
+
+    // Read from the ring buffer and store temporarily in a large buffer
+    ll_ringbuffer_read(self->ring, self->resampleBuffer, *ioNumberDataPackets);
+
+    // Set the input data
+    ioData->mNumberBuffers = 1;
+    ioData->mBuffers[0].mNumberChannels = self->format.mChannelsPerFrame;
+    ioData->mBuffers[0].mData = self->resampleBuffer;
+    ioData->mBuffers[0].mDataByteSize = (*ioNumberDataPackets) * self->format.mBytesPerFrame;
+
+    return noErr;
 }
 
-static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
+
+static ALCenum ALCcoreAudioCapture_open(ALCcoreAudioCapture *self, const ALCchar *name)
 {
+    ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
     AudioStreamBasicDescription requestedFormat;  // The application requested format
     AudioStreamBasicDescription hardwareFormat;   // The hardware format
     AudioStreamBasicDescription outputFormat;     // The AudioUnit output format
@@ -381,12 +465,11 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
     AudioObjectPropertyAddress propertyAddress;
     UInt32 enableIO;
     AudioComponent comp;
-    ca_data *data;
     OSStatus err;
 
-    if(!deviceName)
-        deviceName = ca_device;
-    else if(strcmp(deviceName, ca_device) != 0)
+    if(!name)
+        name = ca_device;
+    else if(strcmp(name, ca_device) != 0)
         return ALC_INVALID_VALUE;
 
     desc.componentType = kAudioUnitType_Output;
@@ -403,11 +486,8 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
         return ALC_INVALID_VALUE;
     }
 
-    data = calloc(1, sizeof(*data));
-    device->ExtraData = data;
-
     // Open the component
-    err = AudioComponentInstanceNew(comp, &data->audioUnit);
+    err = AudioComponentInstanceNew(comp, &self->audioUnit);
     if(err != noErr)
     {
         ERR("AudioComponentInstanceNew failed\n");
@@ -416,7 +496,7 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
 
     // Turn off AudioUnit output
     enableIO = 0;
-    err = AudioUnitSetProperty(data->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, &enableIO, sizeof(ALuint));
+    err = AudioUnitSetProperty(self->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, &enableIO, sizeof(ALuint));
     if(err != noErr)
     {
         ERR("AudioUnitSetProperty failed\n");
@@ -425,7 +505,7 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
 
     // Turn on AudioUnit input
     enableIO = 1;
-    err = AudioUnitSetProperty(data->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &enableIO, sizeof(ALuint));
+    err = AudioUnitSetProperty(self->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &enableIO, sizeof(ALuint));
     if(err != noErr)
     {
         ERR("AudioUnitSetProperty failed\n");
@@ -453,7 +533,7 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
     }
 
     // Track the input device
-    err = AudioUnitSetProperty(data->audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &inputDevice, sizeof(AudioDeviceID));
+    err = AudioUnitSetProperty(self->audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &inputDevice, sizeof(AudioDeviceID));
     if(err != noErr)
     {
         ERR("AudioUnitSetProperty failed\n");
@@ -461,10 +541,10 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
     }
 
     // set capture callback
-    input.inputProc = ca_capture_callback;
-    input.inputProcRefCon = device;
+    input.inputProc = ALCcoreAudioCapture_RecordProc;
+    input.inputProcRefCon = self;
 
-    err = AudioUnitSetProperty(data->audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &input, sizeof(AURenderCallbackStruct));
+    err = AudioUnitSetProperty(self->audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &input, sizeof(AURenderCallbackStruct));
     if(err != noErr)
     {
         ERR("AudioUnitSetProperty failed\n");
@@ -472,7 +552,7 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
     }
 
     // Initialize the device
-    err = AudioUnitInitialize(data->audioUnit);
+    err = AudioUnitInitialize(self->audioUnit);
     if(err != noErr)
     {
         ERR("AudioUnitInitialize failed\n");
@@ -481,7 +561,7 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
 
     // Get the hardware format
     propertySize = sizeof(AudioStreamBasicDescription);
-    err = AudioUnitGetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 1, &hardwareFormat, &propertySize);
+    err = AudioUnitGetProperty(self->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 1, &hardwareFormat, &propertySize);
     if(err != noErr || propertySize != sizeof(AudioStreamBasicDescription))
     {
         ERR("AudioUnitGetProperty failed\n");
@@ -528,7 +608,7 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
         case DevFmtX51Rear:
         case DevFmtX61:
         case DevFmtX71:
-        case DevFmtBFormat3D:
+        case DevFmtAmbi3D:
             ERR("%s not supported\n", DevFmtChannelsString(device->FmtChans));
             goto error;
     }
@@ -541,8 +621,8 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
     requestedFormat.mFramesPerPacket = 1;
 
     // save requested format description for later use
-    data->format = requestedFormat;
-    data->frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
+    self->format = requestedFormat;
+    self->frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
 
     // Use intermediate format for sample rate conversion (outputFormat)
     // Set sample rate to the same as hardware for resampling later
@@ -550,11 +630,11 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
     outputFormat.mSampleRate = hardwareFormat.mSampleRate;
 
     // Determine sample rate ratio for resampling
-    data->sampleRateRatio = outputFormat.mSampleRate / device->Frequency;
+    self->sampleRateRatio = outputFormat.mSampleRate / device->Frequency;
 
     // The output format should be the requested format, but using the hardware sample rate
     // This is because the AudioUnit will automatically scale other properties, except for sample rate
-    err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, (void *)&outputFormat, sizeof(outputFormat));
+    err = AudioUnitSetProperty(self->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, (void *)&outputFormat, sizeof(outputFormat));
     if(err != noErr)
     {
         ERR("AudioUnitSetProperty failed\n");
@@ -562,8 +642,8 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
     }
 
     // Set the AudioUnit output format frame count
-    outputFrameCount = device->UpdateSize * data->sampleRateRatio;
-    err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Output, 0, &outputFrameCount, sizeof(outputFrameCount));
+    outputFrameCount = device->UpdateSize * self->sampleRateRatio;
+    err = AudioUnitSetProperty(self->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Output, 0, &outputFrameCount, sizeof(outputFrameCount));
     if(err != noErr)
     {
         ERR("AudioUnitSetProperty failed: %d\n", err);
@@ -571,7 +651,7 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
     }
 
     // Set up sample converter
-    err = AudioConverterNew(&outputFormat, &requestedFormat, &data->audioConverter);
+    err = AudioConverterNew(&outputFormat, &requestedFormat, &self->audioConverter);
     if(err != noErr)
     {
         ERR("AudioConverterNew failed: %d\n", err);
@@ -579,71 +659,71 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
     }
 
     // Create a buffer for use in the resample callback
-    data->resampleBuffer = malloc(device->UpdateSize * data->frameSize * data->sampleRateRatio);
+    self->resampleBuffer = malloc(device->UpdateSize * self->frameSize * self->sampleRateRatio);
 
     // Allocate buffer for the AudioUnit output
-    data->bufferList = allocate_buffer_list(outputFormat.mChannelsPerFrame, device->UpdateSize * data->frameSize * data->sampleRateRatio);
-    if(data->bufferList == NULL)
+    self->bufferList = allocate_buffer_list(outputFormat.mChannelsPerFrame, device->UpdateSize * self->frameSize * self->sampleRateRatio);
+    if(self->bufferList == NULL)
         goto error;
 
-    data->ring = CreateRingBuffer(data->frameSize, (device->UpdateSize * data->sampleRateRatio) * device->NumUpdates);
-    if(data->ring == NULL)
-        goto error;
+    self->ring = ll_ringbuffer_create(
+        device->UpdateSize*self->sampleRateRatio*device->NumUpdates + 1,
+        self->frameSize
+    );
+    if(!self->ring) goto error;
 
-    al_string_copy_cstr(&device->DeviceName, deviceName);
+    alstr_copy_cstr(&device->DeviceName, name);
 
     return ALC_NO_ERROR;
 
 error:
-    DestroyRingBuffer(data->ring);
-    free(data->resampleBuffer);
-    destroy_buffer_list(data->bufferList);
+    ll_ringbuffer_free(self->ring);
+    self->ring = NULL;
+    free(self->resampleBuffer);
+    destroy_buffer_list(self->bufferList);
 
-    if(data->audioConverter)
-        AudioConverterDispose(data->audioConverter);
-    if(data->audioUnit)
-        AudioComponentInstanceDispose(data->audioUnit);
-
-    free(data);
-    device->ExtraData = NULL;
+    if(self->audioConverter)
+        AudioConverterDispose(self->audioConverter);
+    if(self->audioUnit)
+        AudioComponentInstanceDispose(self->audioUnit);
 
     return ALC_INVALID_VALUE;
 }
 
-static void ca_close_capture(ALCdevice *device)
+
+static void ALCcoreAudioCapture_close(ALCcoreAudioCapture *self)
 {
-    ca_data *data = (ca_data*)device->ExtraData;
+    ll_ringbuffer_free(self->ring);
+    self->ring = NULL;
 
-    DestroyRingBuffer(data->ring);
-    free(data->resampleBuffer);
-    destroy_buffer_list(data->bufferList);
+    free(self->resampleBuffer);
 
-    AudioConverterDispose(data->audioConverter);
-    AudioComponentInstanceDispose(data->audioUnit);
+    destroy_buffer_list(self->bufferList);
 
-    free(data);
-    device->ExtraData = NULL;
+    AudioConverterDispose(self->audioConverter);
+    AudioComponentInstanceDispose(self->audioUnit);
 }
 
-static void ca_start_capture(ALCdevice *device)
+static ALCboolean ALCcoreAudioCapture_start(ALCcoreAudioCapture *self)
 {
-    ca_data *data = (ca_data*)device->ExtraData;
-    OSStatus err = AudioOutputUnitStart(data->audioUnit);
+    OSStatus err = AudioOutputUnitStart(self->audioUnit);
     if(err != noErr)
+    {
         ERR("AudioOutputUnitStart failed\n");
+        return ALC_FALSE;
+    }
+    return ALC_TRUE;
 }
 
-static void ca_stop_capture(ALCdevice *device)
+static void ALCcoreAudioCapture_stop(ALCcoreAudioCapture *self)
 {
-    ca_data *data = (ca_data*)device->ExtraData;
-    OSStatus err = AudioOutputUnitStop(data->audioUnit);
+    OSStatus err = AudioOutputUnitStop(self->audioUnit);
     if(err != noErr)
         ERR("AudioOutputUnitStop failed\n");
 }
 
-static ALCenum ca_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint samples)
+static ALCenum ALCcoreAudioCapture_captureSamples(ALCcoreAudioCapture *self, ALCvoid *buffer, ALCuint samples)
 {
-    ca_data *data = (ca_data*)device->ExtraData;
     AudioBufferList *list;
     UInt32 frameCount;
     OSStatus err;
@@ -657,14 +737,15 @@ static ALCenum ca_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint sa
 
     // Point the resampling buffer to the capture buffer
     list->mNumberBuffers = 1;
-    list->mBuffers[0].mNumberChannels = data->format.mChannelsPerFrame;
-    list->mBuffers[0].mDataByteSize = samples * data->frameSize;
+    list->mBuffers[0].mNumberChannels = self->format.mChannelsPerFrame;
+    list->mBuffers[0].mDataByteSize = samples * self->frameSize;
     list->mBuffers[0].mData = buffer;
 
     // Resample into another AudioBufferList
     frameCount = samples;
-    err = AudioConverterFillComplexBuffer(data->audioConverter, ca_capture_conversion_callback,
-                                          device, &frameCount, list, NULL);
+    err = AudioConverterFillComplexBuffer(self->audioConverter,
+        ALCcoreAudioCapture_ConvertCallback, self, &frameCount, list, NULL
+    );
     if(err != noErr)
     {
         ERR("AudioConverterFillComplexBuffer error: %d\n", err);
@@ -673,38 +754,47 @@ static ALCenum ca_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint sa
     return ALC_NO_ERROR;
 }
 
-static ALCuint ca_available_samples(ALCdevice *device)
+static ALCuint ALCcoreAudioCapture_availableSamples(ALCcoreAudioCapture *self)
 {
-    ca_data *data = device->ExtraData;
-    return RingBufferSize(data->ring) / data->sampleRateRatio;
+    return ll_ringbuffer_read_space(self->ring) / self->sampleRateRatio;
 }
 
 
-static const BackendFuncs ca_funcs = {
-    ca_open_playback,
-    ca_close_playback,
-    ca_reset_playback,
-    ca_start_playback,
-    ca_stop_playback,
-    ca_open_capture,
-    ca_close_capture,
-    ca_start_capture,
-    ca_stop_capture,
-    ca_capture_samples,
-    ca_available_samples
-};
-
-ALCboolean alc_ca_init(BackendFuncs *func_list)
+typedef struct ALCcoreAudioBackendFactory {
+    DERIVE_FROM_TYPE(ALCbackendFactory);
+} ALCcoreAudioBackendFactory;
+#define ALCCOREAUDIOBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCcoreAudioBackendFactory, ALCbackendFactory) } }
+
+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 ALCbackend* ALCcoreAudioBackendFactory_createBackend(ALCcoreAudioBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
+DEFINE_ALCBACKENDFACTORY_VTABLE(ALCcoreAudioBackendFactory);
+
+
+ALCbackendFactory *ALCcoreAudioBackendFactory_getFactory(void)
+{
+    static ALCcoreAudioBackendFactory factory = ALCCOREAUDIOBACKENDFACTORY_INITIALIZER;
+    return STATIC_CAST(ALCbackendFactory, &factory);
+}
+
+
+static ALCboolean ALCcoreAudioBackendFactory_init(ALCcoreAudioBackendFactory* UNUSED(self))
 {
-    *func_list = ca_funcs;
     return ALC_TRUE;
 }
 
-void alc_ca_deinit(void)
+static ALCboolean ALCcoreAudioBackendFactory_querySupport(ALCcoreAudioBackendFactory* UNUSED(self), ALCbackend_Type type)
 {
+    if(type == ALCbackend_Playback || ALCbackend_Capture)
+        return ALC_TRUE;
+    return ALC_FALSE;
 }
 
-void alc_ca_probe(enum DevProbe type)
+static void ALCcoreAudioBackendFactory_probe(ALCcoreAudioBackendFactory* UNUSED(self), enum DevProbe type)
 {
     switch(type)
     {
@@ -716,3 +806,23 @@ void alc_ca_probe(enum DevProbe type)
             break;
     }
 }
+
+static ALCbackend* ALCcoreAudioBackendFactory_createBackend(ALCcoreAudioBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
+{
+    if(type == ALCbackend_Playback)
+    {
+        ALCcoreAudioPlayback *backend;
+        NEW_OBJ(backend, ALCcoreAudioPlayback)(device);
+        if(!backend) return NULL;
+        return STATIC_CAST(ALCbackend, backend);
+    }
+    if(type == ALCbackend_Capture)
+    {
+        ALCcoreAudioCapture *backend;
+        NEW_OBJ(backend, ALCcoreAudioCapture)(device);
+        if(!backend) return NULL;
+        return STATIC_CAST(ALCbackend, backend);
+    }
+
+    return NULL;
+}

+ 117 - 115
libs/openal-soft/Alc/backends/dsound.c

@@ -123,7 +123,7 @@ static void clear_devlist(vector_DevMap *list)
 {
 #define DEINIT_STR(i) AL_STRING_DEINIT((i)->name)
     VECTOR_FOR_EACH(DevMap, *list, DEINIT_STR);
-    VECTOR_RESIZE(*list, 0);
+    VECTOR_RESIZE(*list, 0, 0);
 #undef DEINIT_STR
 }
 
@@ -145,18 +145,18 @@ static BOOL CALLBACK DSoundEnumDevices(GUID *guid, const WCHAR *desc, const WCHA
     {
         const DevMap *iter;
 
-        al_string_copy_cstr(&entry.name, DEVNAME_HEAD);
-        al_string_append_wcstr(&entry.name, desc);
+        alstr_copy_cstr(&entry.name, DEVNAME_HEAD);
+        alstr_append_wcstr(&entry.name, desc);
         if(count != 0)
         {
             char str[64];
             snprintf(str, sizeof(str), " #%d", count+1);
-            al_string_append_cstr(&entry.name, str);
+            alstr_append_cstr(&entry.name, str);
         }
 
-#define MATCH_ENTRY(i) (al_string_cmp(entry.name, (i)->name) == 0)
+#define MATCH_ENTRY(i) (alstr_cmp(entry.name, (i)->name) == 0)
         VECTOR_FIND_IF(iter, const DevMap, *devices, MATCH_ENTRY);
-        if(iter == VECTOR_ITER_END(*devices)) break;
+        if(iter == VECTOR_END(*devices)) break;
 #undef MATCH_ENTRY
         count++;
     }
@@ -165,7 +165,7 @@ static BOOL CALLBACK DSoundEnumDevices(GUID *guid, const WCHAR *desc, const WCHA
     hr = StringFromCLSID(guid, &guidstr);
     if(SUCCEEDED(hr))
     {
-        TRACE("Got device \"%s\", GUID \"%ls\"\n", al_string_get_cstr(entry.name), guidstr);
+        TRACE("Got device \"%s\", GUID \"%ls\"\n", alstr_get_cstr(entry.name), guidstr);
         CoTaskMemFree(guidstr);
     }
 
@@ -199,7 +199,7 @@ static ALCboolean ALCdsoundPlayback_start(ALCdsoundPlayback *self);
 static void ALCdsoundPlayback_stop(ALCdsoundPlayback *self);
 static DECLARE_FORWARD2(ALCdsoundPlayback, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
 static DECLARE_FORWARD(ALCdsoundPlayback, ALCbackend, ALCuint, availableSamples)
-static DECLARE_FORWARD(ALCdsoundPlayback, ALCbackend, ALint64, getLatency)
+static DECLARE_FORWARD(ALCdsoundPlayback, ALCbackend, ClockLatency, getClockLatency)
 static DECLARE_FORWARD(ALCdsoundPlayback, ALCbackend, void, lock)
 static DECLARE_FORWARD(ALCdsoundPlayback, ALCbackend, void, unlock)
 DECLARE_DEFAULT_ALLOCATORS(ALCdsoundPlayback)
@@ -244,7 +244,7 @@ FORCE_ALIGN static int ALCdsoundPlayback_mixerProc(void *ptr)
         return 1;
     }
 
-    FrameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
+    FrameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
     FragSize = device->UpdateSize * FrameSize;
 
     IDirectSoundBuffer_GetCurrentPosition(self->Buffer, &LastCursor, NULL);
@@ -299,8 +299,10 @@ FORCE_ALIGN static int ALCdsoundPlayback_mixerProc(void *ptr)
         if(SUCCEEDED(err))
         {
             // If we have an active context, mix data directly into output buffer otherwise fill with silence
+            ALCdevice_Lock(device);
             aluMixData(device, WritePtr1, WriteCnt1/FrameSize);
             aluMixData(device, WritePtr2, WriteCnt2/FrameSize);
+            ALCdevice_Unlock(device);
 
             // Unlock output buffer only when successfully locked
             IDirectSoundBuffer_Unlock(self->Buffer, WritePtr1, WriteCnt1, WritePtr2, WriteCnt2);
@@ -341,23 +343,23 @@ static ALCenum ALCdsoundPlayback_open(ALCdsoundPlayback *self, const ALCchar *de
 
     if(!deviceName && VECTOR_SIZE(PlaybackDevices) > 0)
     {
-        deviceName = al_string_get_cstr(VECTOR_FRONT(PlaybackDevices).name);
+        deviceName = alstr_get_cstr(VECTOR_FRONT(PlaybackDevices).name);
         guid = &VECTOR_FRONT(PlaybackDevices).guid;
     }
     else
     {
         const DevMap *iter;
 
-#define MATCH_NAME(i)  (al_string_cmp_cstr((i)->name, deviceName) == 0)
+#define MATCH_NAME(i)  (alstr_cmp_cstr((i)->name, deviceName) == 0)
         VECTOR_FIND_IF(iter, const DevMap, PlaybackDevices, MATCH_NAME);
 #undef MATCH_NAME
-        if(iter == VECTOR_ITER_END(PlaybackDevices))
+        if(iter == VECTOR_END(PlaybackDevices))
             return ALC_INVALID_VALUE;
         guid = &iter->guid;
     }
 
     hr = DS_OK;
-    self->NotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+    self->NotifyEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
     if(self->NotifyEvent == NULL)
         hr = E_FAIL;
 
@@ -379,7 +381,7 @@ static ALCenum ALCdsoundPlayback_open(ALCdsoundPlayback *self, const ALCchar *de
         return ALC_INVALID_VALUE;
     }
 
-    al_string_copy_cstr(&device->DeviceName, deviceName);
+    alstr_copy_cstr(&device->DeviceName, deviceName);
 
     return ALC_NO_ERROR;
 }
@@ -472,7 +474,7 @@ static ALCboolean ALCdsoundPlayback_reset(ALCdsoundPlayback *self)
             case DevFmtMono:
                 OutputType.dwChannelMask = SPEAKER_FRONT_CENTER;
                 break;
-            case DevFmtBFormat3D:
+            case DevFmtAmbi3D:
                 device->FmtChans = DevFmtStereo;
                 /*fall-through*/
             case DevFmtStereo:
@@ -525,7 +527,7 @@ static ALCboolean ALCdsoundPlayback_reset(ALCdsoundPlayback *self)
 retry_open:
         hr = S_OK;
         OutputType.Format.wFormatTag = WAVE_FORMAT_PCM;
-        OutputType.Format.nChannels = ChannelsFromDevFmt(device->FmtChans);
+        OutputType.Format.nChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
         OutputType.Format.wBitsPerSample = BytesFromDevFmt(device->FmtType) * 8;
         OutputType.Format.nBlockAlign = OutputType.Format.nChannels*OutputType.Format.wBitsPerSample/8;
         OutputType.Format.nSamplesPerSec = device->Frequency;
@@ -653,7 +655,8 @@ typedef struct ALCdsoundCapture {
     IDirectSoundCaptureBuffer *DSCbuffer;
     DWORD BufferBytes;
     DWORD Cursor;
-    RingBuffer *Ring;
+
+    ll_ringbuffer_t *Ring;
 } ALCdsoundCapture;
 
 static void ALCdsoundCapture_Construct(ALCdsoundCapture *self, ALCdevice *device);
@@ -665,7 +668,7 @@ static ALCboolean ALCdsoundCapture_start(ALCdsoundCapture *self);
 static void ALCdsoundCapture_stop(ALCdsoundCapture *self);
 static ALCenum ALCdsoundCapture_captureSamples(ALCdsoundCapture *self, ALCvoid *buffer, ALCuint samples);
 static ALCuint ALCdsoundCapture_availableSamples(ALCdsoundCapture *self);
-static DECLARE_FORWARD(ALCdsoundCapture, ALCbackend, ALint64, getLatency)
+static DECLARE_FORWARD(ALCdsoundCapture, ALCbackend, ClockLatency, getClockLatency)
 static DECLARE_FORWARD(ALCdsoundCapture, ALCbackend, void, lock)
 static DECLARE_FORWARD(ALCdsoundCapture, ALCbackend, void, unlock)
 DECLARE_DEFAULT_ALLOCATORS(ALCdsoundCapture)
@@ -701,17 +704,17 @@ static ALCenum ALCdsoundCapture_open(ALCdsoundCapture *self, const ALCchar *devi
 
     if(!deviceName && VECTOR_SIZE(CaptureDevices) > 0)
     {
-        deviceName = al_string_get_cstr(VECTOR_FRONT(CaptureDevices).name);
+        deviceName = alstr_get_cstr(VECTOR_FRONT(CaptureDevices).name);
         guid = &VECTOR_FRONT(CaptureDevices).guid;
     }
     else
     {
         const DevMap *iter;
 
-#define MATCH_NAME(i)  (al_string_cmp_cstr((i)->name, deviceName) == 0)
+#define MATCH_NAME(i)  (alstr_cmp_cstr((i)->name, deviceName) == 0)
         VECTOR_FIND_IF(iter, const DevMap, CaptureDevices, MATCH_NAME);
 #undef MATCH_NAME
-        if(iter == VECTOR_ITER_END(CaptureDevices))
+        if(iter == VECTOR_END(CaptureDevices))
             return ALC_INVALID_VALUE;
         guid = &iter->guid;
     }
@@ -731,99 +734,98 @@ static ALCenum ALCdsoundCapture_open(ALCdsoundCapture *self, const ALCchar *devi
             break;
     }
 
-    //DirectSoundCapture Init code
-    hr = DirectSoundCaptureCreate(guid, &self->DSC, NULL);
-    if(SUCCEEDED(hr))
+    memset(&InputType, 0, sizeof(InputType));
+    switch(device->FmtChans)
     {
-        memset(&InputType, 0, sizeof(InputType));
-
-        switch(device->FmtChans)
-        {
-            case DevFmtMono:
-                InputType.dwChannelMask = SPEAKER_FRONT_CENTER;
-                break;
-            case DevFmtStereo:
-                InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
-                                          SPEAKER_FRONT_RIGHT;
-                break;
-            case DevFmtQuad:
-                InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
-                                          SPEAKER_FRONT_RIGHT |
-                                          SPEAKER_BACK_LEFT |
-                                          SPEAKER_BACK_RIGHT;
-                break;
-            case DevFmtX51:
-                InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
-                                          SPEAKER_FRONT_RIGHT |
-                                          SPEAKER_FRONT_CENTER |
-                                          SPEAKER_LOW_FREQUENCY |
-                                          SPEAKER_SIDE_LEFT |
-                                          SPEAKER_SIDE_RIGHT;
-                break;
-            case DevFmtX51Rear:
-                InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
-                                          SPEAKER_FRONT_RIGHT |
-                                          SPEAKER_FRONT_CENTER |
-                                          SPEAKER_LOW_FREQUENCY |
-                                          SPEAKER_BACK_LEFT |
-                                          SPEAKER_BACK_RIGHT;
-                break;
-            case DevFmtX61:
-                InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
-                                          SPEAKER_FRONT_RIGHT |
-                                          SPEAKER_FRONT_CENTER |
-                                          SPEAKER_LOW_FREQUENCY |
-                                          SPEAKER_BACK_CENTER |
-                                          SPEAKER_SIDE_LEFT |
-                                          SPEAKER_SIDE_RIGHT;
-                break;
-            case DevFmtX71:
-                InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
-                                          SPEAKER_FRONT_RIGHT |
-                                          SPEAKER_FRONT_CENTER |
-                                          SPEAKER_LOW_FREQUENCY |
-                                          SPEAKER_BACK_LEFT |
-                                          SPEAKER_BACK_RIGHT |
-                                          SPEAKER_SIDE_LEFT |
-                                          SPEAKER_SIDE_RIGHT;
-                break;
-            case DevFmtBFormat3D:
-                break;
-        }
+        case DevFmtMono:
+            InputType.dwChannelMask = SPEAKER_FRONT_CENTER;
+            break;
+        case DevFmtStereo:
+            InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
+                                      SPEAKER_FRONT_RIGHT;
+            break;
+        case DevFmtQuad:
+            InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
+                                      SPEAKER_FRONT_RIGHT |
+                                      SPEAKER_BACK_LEFT |
+                                      SPEAKER_BACK_RIGHT;
+            break;
+        case DevFmtX51:
+            InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
+                                      SPEAKER_FRONT_RIGHT |
+                                      SPEAKER_FRONT_CENTER |
+                                      SPEAKER_LOW_FREQUENCY |
+                                      SPEAKER_SIDE_LEFT |
+                                      SPEAKER_SIDE_RIGHT;
+            break;
+        case DevFmtX51Rear:
+            InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
+                                      SPEAKER_FRONT_RIGHT |
+                                      SPEAKER_FRONT_CENTER |
+                                      SPEAKER_LOW_FREQUENCY |
+                                      SPEAKER_BACK_LEFT |
+                                      SPEAKER_BACK_RIGHT;
+            break;
+        case DevFmtX61:
+            InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
+                                      SPEAKER_FRONT_RIGHT |
+                                      SPEAKER_FRONT_CENTER |
+                                      SPEAKER_LOW_FREQUENCY |
+                                      SPEAKER_BACK_CENTER |
+                                      SPEAKER_SIDE_LEFT |
+                                      SPEAKER_SIDE_RIGHT;
+            break;
+        case DevFmtX71:
+            InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
+                                      SPEAKER_FRONT_RIGHT |
+                                      SPEAKER_FRONT_CENTER |
+                                      SPEAKER_LOW_FREQUENCY |
+                                      SPEAKER_BACK_LEFT |
+                                      SPEAKER_BACK_RIGHT |
+                                      SPEAKER_SIDE_LEFT |
+                                      SPEAKER_SIDE_RIGHT;
+            break;
+        case DevFmtAmbi3D:
+            WARN("%s capture not supported\n", DevFmtChannelsString(device->FmtChans));
+            return ALC_INVALID_ENUM;
+    }
 
-        InputType.Format.wFormatTag = WAVE_FORMAT_PCM;
-        InputType.Format.nChannels = ChannelsFromDevFmt(device->FmtChans);
-        InputType.Format.wBitsPerSample = BytesFromDevFmt(device->FmtType) * 8;
-        InputType.Format.nBlockAlign = InputType.Format.nChannels*InputType.Format.wBitsPerSample/8;
-        InputType.Format.nSamplesPerSec = device->Frequency;
-        InputType.Format.nAvgBytesPerSec = InputType.Format.nSamplesPerSec*InputType.Format.nBlockAlign;
-        InputType.Format.cbSize = 0;
+    InputType.Format.wFormatTag = WAVE_FORMAT_PCM;
+    InputType.Format.nChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
+    InputType.Format.wBitsPerSample = BytesFromDevFmt(device->FmtType) * 8;
+    InputType.Format.nBlockAlign = InputType.Format.nChannels*InputType.Format.wBitsPerSample/8;
+    InputType.Format.nSamplesPerSec = device->Frequency;
+    InputType.Format.nAvgBytesPerSec = InputType.Format.nSamplesPerSec*InputType.Format.nBlockAlign;
+    InputType.Format.cbSize = 0;
+    InputType.Samples.wValidBitsPerSample = InputType.Format.wBitsPerSample;
+    if(device->FmtType == DevFmtFloat)
+        InputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
+    else
+        InputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
 
-        if(InputType.Format.nChannels > 2 || device->FmtType == DevFmtFloat)
-        {
-            InputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
-            InputType.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
-            InputType.Samples.wValidBitsPerSample = InputType.Format.wBitsPerSample;
-            if(device->FmtType == DevFmtFloat)
-                InputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
-            else
-                InputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
-        }
+    if(InputType.Format.nChannels > 2 || device->FmtType == DevFmtFloat)
+    {
+        InputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
+        InputType.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
+    }
 
-        samples = device->UpdateSize * device->NumUpdates;
-        samples = maxu(samples, 100 * device->Frequency / 1000);
+    samples = device->UpdateSize * device->NumUpdates;
+    samples = maxu(samples, 100 * device->Frequency / 1000);
 
-        memset(&DSCBDescription, 0, sizeof(DSCBUFFERDESC));
-        DSCBDescription.dwSize = sizeof(DSCBUFFERDESC);
-        DSCBDescription.dwFlags = 0;
-        DSCBDescription.dwBufferBytes = samples * InputType.Format.nBlockAlign;
-        DSCBDescription.lpwfxFormat = &InputType.Format;
+    memset(&DSCBDescription, 0, sizeof(DSCBUFFERDESC));
+    DSCBDescription.dwSize = sizeof(DSCBUFFERDESC);
+    DSCBDescription.dwFlags = 0;
+    DSCBDescription.dwBufferBytes = samples * InputType.Format.nBlockAlign;
+    DSCBDescription.lpwfxFormat = &InputType.Format;
 
+    //DirectSoundCapture Init code
+    hr = DirectSoundCaptureCreate(guid, &self->DSC, NULL);
+    if(SUCCEEDED(hr))
         hr = IDirectSoundCapture_CreateCaptureBuffer(self->DSC, &DSCBDescription, &self->DSCbuffer, NULL);
-    }
     if(SUCCEEDED(hr))
     {
-         self->Ring = CreateRingBuffer(InputType.Format.nBlockAlign, device->UpdateSize * device->NumUpdates);
+         self->Ring = ll_ringbuffer_create(device->UpdateSize*device->NumUpdates + 1,
+                                           InputType.Format.nBlockAlign);
          if(self->Ring == NULL)
              hr = DSERR_OUTOFMEMORY;
     }
@@ -832,7 +834,7 @@ static ALCenum ALCdsoundCapture_open(ALCdsoundCapture *self, const ALCchar *devi
     {
         ERR("Device init failed: 0x%08lx\n", hr);
 
-        DestroyRingBuffer(self->Ring);
+        ll_ringbuffer_free(self->Ring);
         self->Ring = NULL;
         if(self->DSCbuffer != NULL)
             IDirectSoundCaptureBuffer_Release(self->DSCbuffer);
@@ -847,14 +849,14 @@ static ALCenum ALCdsoundCapture_open(ALCdsoundCapture *self, const ALCchar *devi
     self->BufferBytes = DSCBDescription.dwBufferBytes;
     SetDefaultWFXChannelOrder(device);
 
-    al_string_copy_cstr(&device->DeviceName, deviceName);
+    alstr_copy_cstr(&device->DeviceName, deviceName);
 
     return ALC_NO_ERROR;
 }
 
 static void ALCdsoundCapture_close(ALCdsoundCapture *self)
 {
-    DestroyRingBuffer(self->Ring);
+    ll_ringbuffer_free(self->Ring);
     self->Ring = NULL;
 
     if(self->DSCbuffer != NULL)
@@ -897,7 +899,7 @@ static void ALCdsoundCapture_stop(ALCdsoundCapture *self)
 
 static ALCenum ALCdsoundCapture_captureSamples(ALCdsoundCapture *self, ALCvoid *buffer, ALCuint samples)
 {
-    ReadRingBuffer(self->Ring, buffer, samples);
+    ll_ringbuffer_read(self->Ring, buffer, samples);
     return ALC_NO_ERROR;
 }
 
@@ -913,7 +915,7 @@ static ALCuint ALCdsoundCapture_availableSamples(ALCdsoundCapture *self)
     if(!device->Connected)
         goto done;
 
-    FrameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
+    FrameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
     BufferBytes = self->BufferBytes;
     LastCursor = self->Cursor;
 
@@ -929,9 +931,9 @@ static ALCuint ALCdsoundCapture_availableSamples(ALCdsoundCapture *self)
     }
     if(SUCCEEDED(hr))
     {
-        WriteRingBuffer(self->Ring, ReadPtr1, ReadCnt1/FrameSize);
+        ll_ringbuffer_write(self->Ring, ReadPtr1, ReadCnt1/FrameSize);
         if(ReadPtr2 != NULL)
-            WriteRingBuffer(self->Ring, ReadPtr2, ReadCnt2/FrameSize);
+            ll_ringbuffer_write(self->Ring, ReadPtr2, ReadCnt2/FrameSize);
         hr = IDirectSoundCaptureBuffer_Unlock(self->DSCbuffer,
                                               ReadPtr1, ReadCnt1,
                                               ReadPtr2, ReadCnt2);
@@ -945,14 +947,14 @@ static ALCuint ALCdsoundCapture_availableSamples(ALCdsoundCapture *self)
     }
 
 done:
-    return RingBufferSize(self->Ring);
+    return ll_ringbuffer_read_space(self->Ring);
 }
 
 
 static inline void AppendAllDevicesList2(const DevMap *entry)
-{ AppendAllDevicesList(al_string_get_cstr(entry->name)); }
+{ AppendAllDevicesList(alstr_get_cstr(entry->name)); }
 static inline void AppendCaptureDeviceList2(const DevMap *entry)
-{ AppendCaptureDeviceList(al_string_get_cstr(entry->name)); }
+{ AppendCaptureDeviceList(alstr_get_cstr(entry->name)); }
 
 typedef struct ALCdsoundBackendFactory {
     DERIVE_FROM_TYPE(ALCbackendFactory);

+ 62 - 32
libs/openal-soft/Alc/backends/jack.c

@@ -54,6 +54,7 @@ static const ALCchar jackDevice[] = "JACK Default";
     MAGIC(jack_get_ports);         \
     MAGIC(jack_free);              \
     MAGIC(jack_get_sample_rate);   \
+    MAGIC(jack_set_error_function); \
     MAGIC(jack_set_process_callback); \
     MAGIC(jack_set_buffer_size_callback); \
     MAGIC(jack_set_buffer_size);   \
@@ -62,6 +63,7 @@ static const ALCchar jackDevice[] = "JACK Default";
 static void *jack_handle;
 #define MAKE_FUNC(f) static __typeof(f) * p##f
 JACK_FUNCS(MAKE_FUNC);
+static __typeof(jack_error_callback) * pjack_error_callback;
 #undef MAKE_FUNC
 
 #define jack_client_open pjack_client_open
@@ -78,10 +80,12 @@ JACK_FUNCS(MAKE_FUNC);
 #define jack_get_ports pjack_get_ports
 #define jack_free pjack_free
 #define jack_get_sample_rate pjack_get_sample_rate
+#define jack_set_error_function pjack_set_error_function
 #define jack_set_process_callback pjack_set_process_callback
 #define jack_set_buffer_size_callback pjack_set_buffer_size_callback
 #define jack_set_buffer_size pjack_set_buffer_size
 #define jack_get_buffer_size pjack_get_buffer_size
+#define jack_error_callback (*pjack_error_callback)
 #endif
 
 
@@ -94,26 +98,42 @@ static ALCboolean jack_load(void)
 #ifdef HAVE_DYNLOAD
     if(!jack_handle)
     {
-        jack_handle = LoadLib("libjack.so.0");
+        al_string missing_funcs = AL_STRING_INIT_STATIC();
+
+#ifdef _WIN32
+#define JACKLIB "libjack.dll"
+#else
+#define JACKLIB "libjack.so.0"
+#endif
+        jack_handle = LoadLib(JACKLIB);
         if(!jack_handle)
+        {
+            WARN("Failed to load %s\n", JACKLIB);
             return ALC_FALSE;
+        }
 
         error = ALC_FALSE;
 #define LOAD_FUNC(f) do {                                                     \
     p##f = GetSymbol(jack_handle, #f);                                        \
     if(p##f == NULL) {                                                        \
         error = ALC_TRUE;                                                     \
+        alstr_append_cstr(&missing_funcs, "\n" #f);                           \
     }                                                                         \
 } while(0)
         JACK_FUNCS(LOAD_FUNC);
 #undef LOAD_FUNC
+        /* Optional symbols. These don't exist in all versions of JACK. */
+#define LOAD_SYM(f) p##f = GetSymbol(jack_handle, #f)
+        LOAD_SYM(jack_error_callback);
+#undef LOAD_SYM
 
         if(error)
         {
+            WARN("Missing expected functions:%s\n", alstr_get_cstr(missing_funcs));
             CloseLib(jack_handle);
             jack_handle = NULL;
-            return ALC_FALSE;
         }
+        alstr_reset(&missing_funcs);
     }
 #endif
 
@@ -148,9 +168,9 @@ static ALCboolean ALCjackPlayback_start(ALCjackPlayback *self);
 static void ALCjackPlayback_stop(ALCjackPlayback *self);
 static DECLARE_FORWARD2(ALCjackPlayback, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
 static DECLARE_FORWARD(ALCjackPlayback, ALCbackend, ALCuint, availableSamples)
-static ALint64 ALCjackPlayback_getLatency(ALCjackPlayback *self);
-static void ALCjackPlayback_lock(ALCjackPlayback *self);
-static void ALCjackPlayback_unlock(ALCjackPlayback *self);
+static ClockLatency ALCjackPlayback_getClockLatency(ALCjackPlayback *self);
+static DECLARE_FORWARD(ALCjackPlayback, ALCbackend, void, lock)
+static DECLARE_FORWARD(ALCjackPlayback, ALCbackend, void, unlock)
 DECLARE_DEFAULT_ALLOCATORS(ALCjackPlayback)
 
 DEFINE_ALCBACKEND_VTABLE(ALCjackPlayback);
@@ -204,15 +224,19 @@ static int ALCjackPlayback_bufferSizeNotify(jack_nframes_t numframes, void *arg)
     ALCjackPlayback_lock(self);
     device->UpdateSize = numframes;
     device->NumUpdates = 2;
-    TRACE("%u update size x%u\n", device->UpdateSize, device->NumUpdates);
 
     bufsize = device->UpdateSize;
-    if(ConfigValueUInt(al_string_get_cstr(device->DeviceName), "jack", "buffer-size", &bufsize))
+    if(ConfigValueUInt(alstr_get_cstr(device->DeviceName), "jack", "buffer-size", &bufsize))
         bufsize = maxu(NextPowerOf2(bufsize), device->UpdateSize);
     bufsize += device->UpdateSize;
+    device->NumUpdates = bufsize / device->UpdateSize;
+
+    TRACE("%u update size x%u\n", device->UpdateSize, device->NumUpdates);
 
     ll_ringbuffer_free(self->Ring);
-    self->Ring = ll_ringbuffer_create(bufsize, FrameSizeFromDevFmt(device->FmtChans, device->FmtType));
+    self->Ring = ll_ringbuffer_create(bufsize,
+        FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder)
+    );
     if(!self->Ring)
     {
         ERR("Failed to reallocate ringbuffer\n");
@@ -230,7 +254,7 @@ static int ALCjackPlayback_process(jack_nframes_t numframes, void *arg)
     ll_ringbuffer_data_t data[2];
     jack_nframes_t total = 0;
     jack_nframes_t todo;
-    ALuint i, c, numchans;
+    ALsizei i, c, numchans;
 
     ll_ringbuffer_get_read_vector(self->Ring, data);
 
@@ -241,8 +265,9 @@ static int ALCjackPlayback_process(jack_nframes_t numframes, void *arg)
     todo = minu(numframes, data[0].len);
     for(c = 0;c < numchans;c++)
     {
-        for(i = 0;i < todo;i++)
-            out[c][i] = ((ALfloat*)data[0].buf)[i*numchans + c];
+        const ALfloat *restrict in = ((ALfloat*)data[0].buf) + c;
+        for(i = 0;(jack_nframes_t)i < todo;i++)
+            out[c][i] = in[i*numchans];
         out[c] += todo;
     }
     total += todo;
@@ -252,8 +277,9 @@ static int ALCjackPlayback_process(jack_nframes_t numframes, void *arg)
     {
         for(c = 0;c < numchans;c++)
         {
-            for(i = 0;i < todo;i++)
-                out[c][i] = ((ALfloat*)data[1].buf)[i*numchans + c];
+            const ALfloat *restrict in = ((ALfloat*)data[1].buf) + c;
+            for(i = 0;(jack_nframes_t)i < todo;i++)
+                out[c][i] = in[i*numchans];
             out[c] += todo;
         }
         total += todo;
@@ -267,7 +293,7 @@ static int ALCjackPlayback_process(jack_nframes_t numframes, void *arg)
         todo = numframes-total;
         for(c = 0;c < numchans;c++)
         {
-            for(i = 0;i < todo;i++)
+            for(i = 0;(jack_nframes_t)i < todo;i++)
                 out[c][i] = 0.0f;
         }
     }
@@ -355,7 +381,7 @@ static ALCenum ALCjackPlayback_open(ALCjackPlayback *self, const ALCchar *name)
     jack_set_process_callback(self->Client, ALCjackPlayback_process, self);
     jack_set_buffer_size_callback(self->Client, ALCjackPlayback_bufferSizeNotify, self);
 
-    al_string_copy_cstr(&device->DeviceName, name);
+    alstr_copy_cstr(&device->DeviceName, name);
 
     return ALC_NO_ERROR;
 }
@@ -377,7 +403,7 @@ static void ALCjackPlayback_close(ALCjackPlayback *self)
 static ALCboolean ALCjackPlayback_reset(ALCjackPlayback *self)
 {
     ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
-    ALuint numchans, i;
+    ALsizei numchans, i;
     ALuint bufsize;
 
     for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
@@ -397,14 +423,15 @@ static ALCboolean ALCjackPlayback_reset(ALCjackPlayback *self)
     device->NumUpdates = 2;
 
     bufsize = device->UpdateSize;
-    if(ConfigValueUInt(al_string_get_cstr(device->DeviceName), "jack", "buffer-size", &bufsize))
+    if(ConfigValueUInt(alstr_get_cstr(device->DeviceName), "jack", "buffer-size", &bufsize))
         bufsize = maxu(NextPowerOf2(bufsize), device->UpdateSize);
     bufsize += device->UpdateSize;
+    device->NumUpdates = bufsize / device->UpdateSize;
 
     /* Force 32-bit float output. */
     device->FmtType = DevFmtFloat;
 
-    numchans = ChannelsFromDevFmt(device->FmtChans);
+    numchans = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
     for(i = 0;i < numchans;i++)
     {
         char name[64];
@@ -433,7 +460,9 @@ static ALCboolean ALCjackPlayback_reset(ALCjackPlayback *self)
     }
 
     ll_ringbuffer_free(self->Ring);
-    self->Ring = ll_ringbuffer_create(bufsize, FrameSizeFromDevFmt(device->FmtChans, device->FmtType));
+    self->Ring = ll_ringbuffer_create(bufsize,
+        FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder)
+    );
     if(!self->Ring)
     {
         ERR("Failed to allocate ringbuffer\n");
@@ -448,7 +477,7 @@ static ALCboolean ALCjackPlayback_reset(ALCjackPlayback *self)
 static ALCboolean ALCjackPlayback_start(ALCjackPlayback *self)
 {
     const char **ports;
-    ALuint i;
+    ALsizei i;
 
     if(jack_activate(self->Client))
     {
@@ -506,30 +535,26 @@ static void ALCjackPlayback_stop(ALCjackPlayback *self)
 }
 
 
-static ALint64 ALCjackPlayback_getLatency(ALCjackPlayback *self)
+static ClockLatency ALCjackPlayback_getClockLatency(ALCjackPlayback *self)
 {
     ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
-    ALint64 latency;
+    ClockLatency ret;
 
     ALCjackPlayback_lock(self);
-    latency = ll_ringbuffer_read_space(self->Ring);
+    ret.ClockTime = GetDeviceClockTime(device);
+    ret.Latency = ll_ringbuffer_read_space(self->Ring) * DEVICE_CLOCK_RES /
+                  device->Frequency;
     ALCjackPlayback_unlock(self);
 
-    return latency * 1000000000 / device->Frequency;
+    return ret;
 }
 
 
-static void ALCjackPlayback_lock(ALCjackPlayback *self)
+static void jack_msg_handler(const char *message)
 {
-    almtx_lock(&STATIC_CAST(ALCbackend,self)->mMutex);
+    WARN("%s\n", message);
 }
 
-static void ALCjackPlayback_unlock(ALCjackPlayback *self)
-{
-    almtx_unlock(&STATIC_CAST(ALCbackend,self)->mMutex);
-}
-
-
 typedef struct ALCjackBackendFactory {
     DERIVE_FROM_TYPE(ALCbackendFactory);
 } ALCjackBackendFactory;
@@ -537,6 +562,7 @@ typedef struct ALCjackBackendFactory {
 
 static ALCboolean ALCjackBackendFactory_init(ALCjackBackendFactory* UNUSED(self))
 {
+    void (*old_error_cb)(const char*);
     jack_client_t *client;
     jack_status_t status;
 
@@ -545,7 +571,11 @@ static ALCboolean ALCjackBackendFactory_init(ALCjackBackendFactory* UNUSED(self)
 
     if(!GetConfigValueBool(NULL, "jack", "spawn-server", 0))
         ClientOptions |= JackNoStartServer;
+
+    old_error_cb = (&jack_error_callback ? jack_error_callback : NULL);
+    jack_set_error_function(jack_msg_handler);
     client = jack_client_open("alsoft", ClientOptions, &status, NULL);
+    jack_set_error_function(old_error_cb);
     if(client == NULL)
     {
         WARN("jack_client_open() failed, 0x%02x\n", status);

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

@@ -41,7 +41,7 @@ static ALCboolean ALCloopback_start(ALCloopback *self);
 static void ALCloopback_stop(ALCloopback *self);
 static DECLARE_FORWARD2(ALCloopback, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
 static DECLARE_FORWARD(ALCloopback, ALCbackend, ALCuint, availableSamples)
-static DECLARE_FORWARD(ALCloopback, ALCbackend, ALint64, getLatency)
+static DECLARE_FORWARD(ALCloopback, ALCbackend, ClockLatency, getClockLatency)
 static DECLARE_FORWARD(ALCloopback, ALCbackend, void, lock)
 static DECLARE_FORWARD(ALCloopback, ALCbackend, void, unlock)
 DECLARE_DEFAULT_ALLOCATORS(ALCloopback)
@@ -59,7 +59,7 @@ static ALCenum ALCloopback_open(ALCloopback *self, const ALCchar *name)
 {
     ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
 
-    al_string_copy_cstr(&device->DeviceName, name);
+    alstr_copy_cstr(&device->DeviceName, name);
     return ALC_NO_ERROR;
 }
 

+ 315 - 102
libs/openal-soft/Alc/backends/mmdevapi.c

@@ -25,6 +25,7 @@
 #include <stdio.h>
 #include <memory.h>
 
+#include <wtypes.h>
 #include <mmdeviceapi.h>
 #include <audioclient.h>
 #include <cguid.h>
@@ -43,6 +44,7 @@
 #include "threads.h"
 #include "compat.h"
 #include "alstring.h"
+#include "converter.h"
 
 #include "backends/base.h"
 
@@ -52,6 +54,7 @@ DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0
 
 DEFINE_DEVPROPKEY(DEVPKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80,0x20, 0x67,0xd1,0x46,0xa8,0x50,0xe0, 14);
 DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_FormFactor, 0x1da5d803, 0xd492, 0x4edd, 0x8c,0x23, 0xe0,0xc0,0xff,0xee,0x7f,0x0e, 0);
+DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_GUID, 0x1da5d803, 0xd492, 0x4edd, 0x8c, 0x23,0xe0, 0xc0,0xff,0xee,0x7f,0x0e, 4 );
 
 #define MONO SPEAKER_FRONT_CENTER
 #define STEREO (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT)
@@ -62,11 +65,14 @@ DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_FormFactor, 0x1da5d803, 0xd492, 0x4edd, 0x
 #define X7DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
 #define X7DOT1_WIDE (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT|SPEAKER_FRONT_LEFT_OF_CENTER|SPEAKER_FRONT_RIGHT_OF_CENTER)
 
+#define REFTIME_PER_SEC ((REFERENCE_TIME)10000000)
+
 #define DEVNAME_HEAD "OpenAL Soft on "
 
 
 typedef struct {
     al_string name;
+    al_string endpoint_guid; // obtained from PKEY_AudioEndpoint_GUID , set to "Unknown device GUID" if absent.
     WCHAR *devid;
 } DevMap;
 TYPEDEF_VECTOR(DevMap, vector_DevMap)
@@ -75,11 +81,12 @@ static void clear_devlist(vector_DevMap *list)
 {
 #define CLEAR_DEVMAP(i) do {     \
     AL_STRING_DEINIT((i)->name); \
+    AL_STRING_DEINIT((i)->endpoint_guid); \
     free((i)->devid);            \
     (i)->devid = NULL;           \
 } while(0)
     VECTOR_FOR_EACH(DevMap, *list, CLEAR_DEVMAP);
-    VECTOR_RESIZE(*list, 0);
+    VECTOR_RESIZE(*list, 0, 0);
 #undef CLEAR_DEVMAP
 }
 
@@ -104,6 +111,15 @@ typedef struct {
 #define WM_USER_Enumerate   (WM_USER+5)
 #define WM_USER_Last        (WM_USER+5)
 
+static const char MessageStr[WM_USER_Last+1-WM_USER][20] = {
+    "Open Device",
+    "Reset Device",
+    "Start Device",
+    "Stop Device",
+    "Close Device",
+    "Enumerate Devices",
+};
+
 static inline void ReturnMsgResponse(ThreadRequest *req, HRESULT res)
 {
     req->result = res;
@@ -119,19 +135,21 @@ static HRESULT WaitForResponse(ThreadRequest *req)
 }
 
 
-static void get_device_name(IMMDevice *device, al_string *name)
+static void get_device_name_and_guid(IMMDevice *device, al_string *name, al_string *guid)
 {
     IPropertyStore *ps;
     PROPVARIANT pvname;
+    PROPVARIANT pvguid;
     HRESULT hr;
 
-    al_string_copy_cstr(name, DEVNAME_HEAD);
+    alstr_copy_cstr(name, DEVNAME_HEAD);
 
     hr = IMMDevice_OpenPropertyStore(device, STGM_READ, &ps);
     if(FAILED(hr))
     {
         WARN("OpenPropertyStore failed: 0x%08lx\n", hr);
-        al_string_append_cstr(name, "Unknown Device Name");
+        alstr_append_cstr(name, "Unknown Device Name");
+        if(guid!=NULL)alstr_copy_cstr(guid, "Unknown Device GUID");
         return;
     }
 
@@ -141,17 +159,37 @@ static void get_device_name(IMMDevice *device, al_string *name)
     if(FAILED(hr))
     {
         WARN("GetValue Device_FriendlyName failed: 0x%08lx\n", hr);
-        al_string_append_cstr(name, "Unknown Device Name");
+        alstr_append_cstr(name, "Unknown Device Name");
     }
     else if(pvname.vt == VT_LPWSTR)
-        al_string_append_wcstr(name, pvname.pwszVal);
+        alstr_append_wcstr(name, pvname.pwszVal);
     else
     {
         WARN("Unexpected PROPVARIANT type: 0x%04x\n", pvname.vt);
-        al_string_append_cstr(name, "Unknown Device Name");
+        alstr_append_cstr(name, "Unknown Device Name");
     }
-
     PropVariantClear(&pvname);
+
+    if(guid!=NULL){
+        PropVariantInit(&pvguid);
+
+        hr = IPropertyStore_GetValue(ps, (const PROPERTYKEY*)&PKEY_AudioEndpoint_GUID, &pvguid);
+        if(FAILED(hr))
+        {
+            WARN("GetValue AudioEndpoint_GUID failed: 0x%08lx\n", hr);
+            alstr_copy_cstr(guid, "Unknown Device GUID");
+        }
+        else if(pvguid.vt == VT_LPWSTR)
+            alstr_copy_wcstr(guid, pvguid.pwszVal);
+        else
+        {
+            WARN("Unexpected PROPVARIANT type: 0x%04x\n", pvguid.vt);
+            alstr_copy_cstr(guid, "Unknown Device GUID");
+        }
+
+        PropVariantClear(&pvguid);
+    }
+
     IPropertyStore_Release(ps);
 }
 
@@ -185,7 +223,7 @@ static void get_device_formfactor(IMMDevice *device, EndpointFormFactor *formfac
 }
 
 
-static void add_device(IMMDevice *device, LPCWSTR devid, vector_DevMap *list)
+static void add_device(IMMDevice *device, const WCHAR *devid, vector_DevMap *list)
 {
     int count = 0;
     al_string tmpname;
@@ -193,38 +231,39 @@ static void add_device(IMMDevice *device, LPCWSTR devid, vector_DevMap *list)
 
     AL_STRING_INIT(tmpname);
     AL_STRING_INIT(entry.name);
+    AL_STRING_INIT(entry.endpoint_guid);
 
     entry.devid = strdupW(devid);
-    get_device_name(device, &tmpname);
+    get_device_name_and_guid(device, &tmpname, &entry.endpoint_guid);
 
     while(1)
     {
         const DevMap *iter;
 
-        al_string_copy(&entry.name, tmpname);
+        alstr_copy(&entry.name, tmpname);
         if(count != 0)
         {
             char str[64];
             snprintf(str, sizeof(str), " #%d", count+1);
-            al_string_append_cstr(&entry.name, str);
+            alstr_append_cstr(&entry.name, str);
         }
 
-#define MATCH_ENTRY(i) (al_string_cmp(entry.name, (i)->name) == 0)
+#define MATCH_ENTRY(i) (alstr_cmp(entry.name, (i)->name) == 0)
         VECTOR_FIND_IF(iter, const DevMap, *list, MATCH_ENTRY);
-        if(iter == VECTOR_ITER_END(*list)) break;
+        if(iter == VECTOR_END(*list)) break;
 #undef MATCH_ENTRY
         count++;
     }
 
-    TRACE("Got device \"%s\", \"%ls\"\n", al_string_get_cstr(entry.name), entry.devid);
+    TRACE("Got device \"%s\", \"%s\", \"%ls\"\n", alstr_get_cstr(entry.name), alstr_get_cstr(entry.endpoint_guid), entry.devid);
     VECTOR_PUSH_BACK(*list, entry);
 
     AL_STRING_DEINIT(tmpname);
 }
 
-static LPWSTR get_device_id(IMMDevice *device)
+static WCHAR *get_device_id(IMMDevice *device)
 {
-    LPWSTR devid;
+    WCHAR *devid;
     HRESULT hr;
 
     hr = IMMDevice_GetId(device, &devid);
@@ -241,7 +280,7 @@ static HRESULT probe_devices(IMMDeviceEnumerator *devenum, EDataFlow flowdir, ve
 {
     IMMDeviceCollection *coll;
     IMMDevice *defdev = NULL;
-    LPWSTR defdevid = NULL;
+    WCHAR *defdevid = NULL;
     HRESULT hr;
     UINT count;
     UINT i;
@@ -258,11 +297,7 @@ static HRESULT probe_devices(IMMDeviceEnumerator *devenum, EDataFlow flowdir, ve
     if(SUCCEEDED(hr) && count > 0)
     {
         clear_devlist(list);
-        if(!VECTOR_RESERVE(*list, count))
-        {
-            IMMDeviceCollection_Release(coll);
-            return E_OUTOFMEMORY;
-        }
+        VECTOR_RESIZE(*list, 0, count);
 
         hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(devenum, flowdir,
                                                          eMultimedia, &defdev);
@@ -277,7 +312,7 @@ static HRESULT probe_devices(IMMDeviceEnumerator *devenum, EDataFlow flowdir, ve
     for(i = 0;i < count;++i)
     {
         IMMDevice *device;
-        LPWSTR devid;
+        WCHAR *devid;
 
         hr = IMMDeviceCollection_Item(coll, i, &device);
         if(FAILED(hr)) continue;
@@ -379,7 +414,11 @@ static DWORD CALLBACK ALCmmdevProxy_messageHandler(void *ptr)
     TRACE("Starting message loop\n");
     while(GetMessage(&msg, NULL, WM_USER_First, WM_USER_Last))
     {
-        TRACE("Got message %u (lparam=%p, wparam=%p)\n", msg.message, (void*)msg.lParam, (void*)msg.wParam);
+        TRACE("Got message \"%s\" (0x%04x, lparam=%p, wparam=%p)\n",
+            (msg.message >= WM_USER && msg.message <= WM_USER_Last) ?
+            MessageStr[msg.message-WM_USER] : "Unknown",
+            msg.message, (void*)msg.lParam, (void*)msg.wParam
+        );
         switch(msg.message)
         {
         case WM_USER_OpenDevice:
@@ -508,7 +547,7 @@ static void ALCmmdevPlayback_stop(ALCmmdevPlayback *self);
 static void ALCmmdevPlayback_stopProxy(ALCmmdevPlayback *self);
 static DECLARE_FORWARD2(ALCmmdevPlayback, ALCbackend, ALCenum, captureSamples, ALCvoid*, ALCuint)
 static DECLARE_FORWARD(ALCmmdevPlayback, ALCbackend, ALCuint, availableSamples)
-static ALint64 ALCmmdevPlayback_getLatency(ALCmmdevPlayback *self);
+static ClockLatency ALCmmdevPlayback_getClockLatency(ALCmmdevPlayback *self);
 static DECLARE_FORWARD(ALCmmdevPlayback, ALCbackend, void, lock)
 static DECLARE_FORWARD(ALCmmdevPlayback, ALCbackend, void, unlock)
 DECLARE_DEFAULT_ALLOCATORS(ALCmmdevPlayback)
@@ -606,10 +645,10 @@ FORCE_ALIGN static int ALCmmdevPlayback_mixerProc(void *arg)
         hr = IAudioRenderClient_GetBuffer(self->render, len, &buffer);
         if(SUCCEEDED(hr))
         {
-            V0(device->Backend,lock)();
+            ALCmmdevPlayback_lock(self);
             aluMixData(device, buffer, len);
             self->Padding = written + len;
-            V0(device->Backend,unlock)();
+            ALCmmdevPlayback_unlock(self);
             hr = IAudioRenderClient_ReleaseBuffer(self->render, len, 0);
         }
         if(FAILED(hr))
@@ -667,13 +706,12 @@ static ALCboolean MakeExtensible(WAVEFORMATEXTENSIBLE *out, const WAVEFORMATEX *
     return ALC_TRUE;
 }
 
-
 static ALCenum ALCmmdevPlayback_open(ALCmmdevPlayback *self, const ALCchar *deviceName)
 {
     HRESULT hr = S_OK;
 
-    self->NotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
-    self->MsgEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+    self->NotifyEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
+    self->MsgEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
     if(self->NotifyEvent == NULL || self->MsgEvent == NULL)
     {
         ERR("Failed to create message events: %lu\n", GetLastError());
@@ -694,18 +732,32 @@ static ALCenum ALCmmdevPlayback_open(ALCmmdevPlayback *self, const ALCchar *devi
             }
 
             hr = E_FAIL;
-#define MATCH_NAME(i) (al_string_cmp_cstr((i)->name, deviceName) == 0)
+#define MATCH_NAME(i) (alstr_cmp_cstr((i)->name, deviceName) == 0 ||        \
+                       alstr_cmp_cstr((i)->endpoint_guid, deviceName) == 0)
             VECTOR_FIND_IF(iter, const DevMap, PlaybackDevices, MATCH_NAME);
-            if(iter == VECTOR_ITER_END(PlaybackDevices))
+#undef MATCH_NAME
+            if(iter == VECTOR_END(PlaybackDevices))
+            {
+                int len;
+                if((len=MultiByteToWideChar(CP_UTF8, 0, deviceName, -1, NULL, 0)) > 0)
+                {
+                    WCHAR *wname = calloc(sizeof(WCHAR), len);
+                    MultiByteToWideChar(CP_UTF8, 0, deviceName, -1, wname, len);
+#define MATCH_NAME(i) (wcscmp((i)->devid, wname) == 0)
+                    VECTOR_FIND_IF(iter, const DevMap, PlaybackDevices, MATCH_NAME);
+#undef MATCH_NAME
+                    free(wname);
+                }
+            }
+            if(iter == VECTOR_END(PlaybackDevices))
                 WARN("Failed to find device name matching \"%s\"\n", deviceName);
             else
             {
                 ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
                 self->devid = strdupW(iter->devid);
-                al_string_copy(&device->DeviceName, iter->name);
+                alstr_copy(&device->DeviceName, iter->name);
                 hr = S_OK;
             }
-#undef MATCH_NAME
         }
     }
 
@@ -761,8 +813,8 @@ static HRESULT ALCmmdevPlayback_openProxy(ALCmmdevPlayback *self)
     if(SUCCEEDED(hr))
     {
         self->client = ptr;
-        if(al_string_empty(device->DeviceName))
-            get_device_name(self->mmdev, &device->DeviceName);
+        if(alstr_empty(device->DeviceName))
+            get_device_name_and_guid(self->mmdev, &device->DeviceName, NULL);
     }
 
     if(FAILED(hr))
@@ -854,8 +906,8 @@ static HRESULT ALCmmdevPlayback_resetProxy(ALCmmdevPlayback *self)
     CoTaskMemFree(wfx);
     wfx = NULL;
 
-    buf_time = ((REFERENCE_TIME)device->UpdateSize*device->NumUpdates*10000000 +
-                                device->Frequency-1) / device->Frequency;
+    buf_time = ScaleCeil(device->UpdateSize*device->NumUpdates, REFTIME_PER_SEC,
+                         device->Frequency);
 
     if(!(device->Flags&DEVICE_FREQUENCY_REQUEST))
         device->Frequency = OutputType.Format.nSamplesPerSec;
@@ -885,7 +937,7 @@ static HRESULT ALCmmdevPlayback_resetProxy(ALCmmdevPlayback *self)
             OutputType.Format.nChannels = 1;
             OutputType.dwChannelMask = MONO;
             break;
-        case DevFmtBFormat3D:
+        case DevFmtAmbi3D:
             device->FmtChans = DevFmtStereo;
             /*fall-through*/
         case DevFmtStereo:
@@ -1026,7 +1078,9 @@ static HRESULT ALCmmdevPlayback_resetProxy(ALCmmdevPlayback *self)
         OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
     }
     get_device_formfactor(self->mmdev, &formfactor);
-    device->IsHeadphones = (device->FmtChans == DevFmtStereo && formfactor == Headphones);
+    device->IsHeadphones = (device->FmtChans == DevFmtStereo &&
+                            (formfactor == Headphones || formfactor == Headset)
+                           );
 
     SetDefaultWFXChannelOrder(device);
 
@@ -1042,7 +1096,7 @@ static HRESULT ALCmmdevPlayback_resetProxy(ALCmmdevPlayback *self)
     hr = IAudioClient_GetDevicePeriod(self->client, &min_per, NULL);
     if(SUCCEEDED(hr))
     {
-        min_len = (UINT32)((min_per*device->Frequency + 10000000-1) / 10000000);
+        min_len = (UINT32)ScaleCeil(min_per, device->Frequency, REFTIME_PER_SEC);
         /* Find the nearest multiple of the period size to the update size */
         if(min_len < device->UpdateSize)
             min_len *= (device->UpdateSize + min_len/2)/min_len;
@@ -1139,10 +1193,17 @@ static void ALCmmdevPlayback_stopProxy(ALCmmdevPlayback *self)
 }
 
 
-static ALint64 ALCmmdevPlayback_getLatency(ALCmmdevPlayback *self)
+static ClockLatency ALCmmdevPlayback_getClockLatency(ALCmmdevPlayback *self)
 {
     ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
-    return (ALint64)self->Padding * 1000000000 / device->Frequency;
+    ClockLatency ret;
+
+    ALCmmdevPlayback_lock(self);
+    ret.ClockTime = GetDeviceClockTime(device);
+    ret.Latency = self->Padding * DEVICE_CLOCK_RES / device->Frequency;
+    ALCmmdevPlayback_unlock(self);
+
+    return ret;
 }
 
 
@@ -1159,6 +1220,8 @@ typedef struct ALCmmdevCapture {
 
     HANDLE MsgEvent;
 
+    ChannelConverter *ChannelConv;
+    SampleConverter *SampleConv;
     ll_ringbuffer_t *Ring;
 
     volatile int killNow;
@@ -1181,7 +1244,7 @@ static void ALCmmdevCapture_stop(ALCmmdevCapture *self);
 static void ALCmmdevCapture_stopProxy(ALCmmdevCapture *self);
 static ALCenum ALCmmdevCapture_captureSamples(ALCmmdevCapture *self, ALCvoid *buffer, ALCuint samples);
 static ALuint ALCmmdevCapture_availableSamples(ALCmmdevCapture *self);
-static DECLARE_FORWARD(ALCmmdevCapture, ALCbackend, ALint64, getLatency)
+static DECLARE_FORWARD(ALCmmdevCapture, ALCbackend, ClockLatency, getClockLatency)
 static DECLARE_FORWARD(ALCmmdevCapture, ALCbackend, void, lock)
 static DECLARE_FORWARD(ALCmmdevCapture, ALCbackend, void, unlock)
 DECLARE_DEFAULT_ALLOCATORS(ALCmmdevCapture)
@@ -1206,6 +1269,8 @@ static void ALCmmdevCapture_Construct(ALCmmdevCapture *self, ALCdevice *device)
 
     self->MsgEvent = NULL;
 
+    self->ChannelConv = NULL;
+    self->SampleConv = NULL;
     self->Ring = NULL;
 
     self->killNow = 0;
@@ -1216,6 +1281,9 @@ static void ALCmmdevCapture_Destruct(ALCmmdevCapture *self)
     ll_ringbuffer_free(self->Ring);
     self->Ring = NULL;
 
+    DestroySampleConverter(&self->SampleConv);
+    DestroyChannelConverter(&self->ChannelConv);
+
     if(self->NotifyEvent != NULL)
         CloseHandle(self->NotifyEvent);
     self->NotifyEvent = NULL;
@@ -1235,6 +1303,8 @@ FORCE_ALIGN int ALCmmdevCapture_recordProc(void *arg)
 {
     ALCmmdevCapture *self = arg;
     ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+    ALfloat *samples = NULL;
+    size_t samplesmax = 0;
     HRESULT hr;
 
     hr = CoInitialize(NULL);
@@ -1257,33 +1327,75 @@ FORCE_ALIGN int ALCmmdevCapture_recordProc(void *arg)
         hr = IAudioCaptureClient_GetNextPacketSize(self->capture, &avail);
         if(FAILED(hr))
             ERR("Failed to get next packet size: 0x%08lx\n", hr);
-        else while(avail > 0 && SUCCEEDED(hr))
+        else if(avail > 0)
         {
             UINT32 numsamples;
             DWORD flags;
-            BYTE *data;
+            BYTE *rdata;
 
             hr = IAudioCaptureClient_GetBuffer(self->capture,
-                &data, &numsamples, &flags, NULL, NULL
+                &rdata, &numsamples, &flags, NULL, NULL
             );
             if(FAILED(hr))
-            {
                 ERR("Failed to get capture buffer: 0x%08lx\n", hr);
-                break;
-            }
-
-            ll_ringbuffer_write(self->Ring, (char*)data, numsamples);
-
-            hr = IAudioCaptureClient_ReleaseBuffer(self->capture, numsamples);
-            if(FAILED(hr))
+            else
             {
-                ERR("Failed to release capture buffer: 0x%08lx\n", hr);
-                break;
+                ll_ringbuffer_data_t data[2];
+                size_t dstframes = 0;
+
+                if(self->ChannelConv)
+                {
+                    if(samplesmax < numsamples)
+                    {
+                        size_t newmax = RoundUp(numsamples, 4096);
+                        ALfloat *tmp = al_calloc(DEF_ALIGN, newmax*2*sizeof(ALfloat));
+                        al_free(samples);
+                        samples = tmp;
+                        samplesmax = newmax;
+                    }
+                    ChannelConverterInput(self->ChannelConv, rdata, samples, numsamples);
+                    rdata = (BYTE*)samples;
+                }
+
+                ll_ringbuffer_get_write_vector(self->Ring, data);
+
+                if(self->SampleConv)
+                {
+                    const ALvoid *srcdata = rdata;
+                    ALsizei srcframes = numsamples;
+
+                    dstframes = SampleConverterInput(self->SampleConv,
+                        &srcdata, &srcframes, data[0].buf, data[0].len
+                    );
+                    if(srcframes > 0 && dstframes == data[0].len && data[1].len > 0)
+                    {
+                        /* If some source samples remain, all of the first dest
+                         * block was filled, and there's space in the second
+                         * dest block, do another run for the second block.
+                         */
+                        dstframes += SampleConverterInput(self->SampleConv,
+                            &srcdata, &srcframes, data[1].buf, data[1].len
+                        );
+                    }
+                }
+                else
+                {
+                    size_t framesize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType,
+                                                           device->AmbiOrder);
+                    ALuint len1 = minu(data[0].len, numsamples);
+                    ALuint len2 = minu(data[1].len, numsamples-len1);
+
+                    memcpy(data[0].buf, rdata, len1*framesize);
+                    if(len2 > 0)
+                        memcpy(data[1].buf, rdata+len1*framesize, len2*framesize);
+                    dstframes = len1 + len2;
+                }
+
+                ll_ringbuffer_write_advance(self->Ring, dstframes);
+
+                hr = IAudioCaptureClient_ReleaseBuffer(self->capture, numsamples);
+                if(FAILED(hr)) ERR("Failed to release capture buffer: 0x%08lx\n", hr);
             }
-
-            hr = IAudioCaptureClient_GetNextPacketSize(self->capture, &avail);
-            if(FAILED(hr))
-                ERR("Failed to get next packet size: 0x%08lx\n", hr);
         }
 
         if(FAILED(hr))
@@ -1299,6 +1411,10 @@ FORCE_ALIGN int ALCmmdevCapture_recordProc(void *arg)
             ERR("WaitForSingleObjectEx error: 0x%lx\n", res);
     }
 
+    al_free(samples);
+    samples = NULL;
+    samplesmax = 0;
+
     CoUninitialize();
     return 0;
 }
@@ -1308,8 +1424,8 @@ static ALCenum ALCmmdevCapture_open(ALCmmdevCapture *self, const ALCchar *device
 {
     HRESULT hr = S_OK;
 
-    self->NotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
-    self->MsgEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+    self->NotifyEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
+    self->MsgEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
     if(self->NotifyEvent == NULL || self->MsgEvent == NULL)
     {
         ERR("Failed to create message events: %lu\n", GetLastError());
@@ -1330,18 +1446,32 @@ static ALCenum ALCmmdevCapture_open(ALCmmdevCapture *self, const ALCchar *device
             }
 
             hr = E_FAIL;
-#define MATCH_NAME(i) (al_string_cmp_cstr((i)->name, deviceName) == 0)
+#define MATCH_NAME(i) (alstr_cmp_cstr((i)->name, deviceName) == 0 ||        \
+                       alstr_cmp_cstr((i)->endpoint_guid, deviceName) == 0)
             VECTOR_FIND_IF(iter, const DevMap, CaptureDevices, MATCH_NAME);
-            if(iter == VECTOR_ITER_END(CaptureDevices))
+#undef MATCH_NAME
+            if(iter == VECTOR_END(CaptureDevices))
+            {
+                int len;
+                if((len=MultiByteToWideChar(CP_UTF8, 0, deviceName, -1, NULL, 0)) > 0)
+                {
+                    WCHAR *wname = calloc(sizeof(WCHAR), len);
+                    MultiByteToWideChar(CP_UTF8, 0, deviceName, -1, wname, len);
+#define MATCH_NAME(i) (wcscmp((i)->devid, wname) == 0)
+                    VECTOR_FIND_IF(iter, const DevMap, CaptureDevices, MATCH_NAME);
+#undef MATCH_NAME
+                    free(wname);
+                }
+            }
+            if(iter == VECTOR_END(CaptureDevices))
                 WARN("Failed to find device name matching \"%s\"\n", deviceName);
             else
             {
                 ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
                 self->devid = strdupW(iter->devid);
-                al_string_copy(&device->DeviceName, iter->name);
+                alstr_copy(&device->DeviceName, iter->name);
                 hr = S_OK;
             }
-#undef MATCH_NAME
         }
     }
 
@@ -1415,8 +1545,8 @@ static HRESULT ALCmmdevCapture_openProxy(ALCmmdevCapture *self)
     if(SUCCEEDED(hr))
     {
         self->client = ptr;
-        if(al_string_empty(device->DeviceName))
-            get_device_name(self->mmdev, &device->DeviceName);
+        if(alstr_empty(device->DeviceName))
+            get_device_name_and_guid(self->mmdev, &device->DeviceName, NULL);
     }
 
     if(FAILED(hr))
@@ -1467,6 +1597,7 @@ static HRESULT ALCmmdevCapture_resetProxy(ALCmmdevCapture *self)
     ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
     WAVEFORMATEXTENSIBLE OutputType;
     WAVEFORMATEX *wfx = NULL;
+    enum DevFmtType srcType;
     REFERENCE_TIME buf_time;
     UINT32 buffer_len;
     void *ptr = NULL;
@@ -1484,8 +1615,12 @@ static HRESULT ALCmmdevCapture_resetProxy(ALCmmdevCapture *self)
     }
     self->client = ptr;
 
-    buf_time = ((REFERENCE_TIME)device->UpdateSize*device->NumUpdates*10000000 +
-                                device->Frequency-1) / device->Frequency;
+    buf_time = ScaleCeil(device->UpdateSize*device->NumUpdates, REFTIME_PER_SEC,
+                         device->Frequency);
+    // Make sure buffer is at least 100ms in size
+    buf_time = maxu64(buf_time, REFTIME_PER_SEC/10);
+    device->UpdateSize = (ALuint)ScaleCeil(buf_time, device->Frequency, REFTIME_PER_SEC) /
+                         device->NumUpdates;
 
     OutputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
     switch(device->FmtChans)
@@ -1519,38 +1654,33 @@ static HRESULT ALCmmdevCapture_resetProxy(ALCmmdevCapture *self)
             OutputType.dwChannelMask = X7DOT1;
             break;
 
-        case DevFmtBFormat3D:
+        case DevFmtAmbi3D:
             return E_FAIL;
     }
     switch(device->FmtType)
     {
+        /* NOTE: Signedness doesn't matter, the converter will handle it. */
+        case DevFmtByte:
         case DevFmtUByte:
             OutputType.Format.wBitsPerSample = 8;
-            OutputType.Samples.wValidBitsPerSample = 8;
             OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
             break;
         case DevFmtShort:
+        case DevFmtUShort:
             OutputType.Format.wBitsPerSample = 16;
-            OutputType.Samples.wValidBitsPerSample = 16;
             OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
             break;
         case DevFmtInt:
+        case DevFmtUInt:
             OutputType.Format.wBitsPerSample = 32;
-            OutputType.Samples.wValidBitsPerSample = 32;
             OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
             break;
         case DevFmtFloat:
             OutputType.Format.wBitsPerSample = 32;
-            OutputType.Samples.wValidBitsPerSample = 32;
             OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
             break;
-
-        case DevFmtByte:
-        case DevFmtUShort:
-        case DevFmtUInt:
-            WARN("%s capture samples not supported\n", DevFmtTypeString(device->FmtType));
-            return E_FAIL;
     }
+    OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
     OutputType.Format.nSamplesPerSec = device->Frequency;
 
     OutputType.Format.nBlockAlign = OutputType.Format.nChannels *
@@ -1568,26 +1698,107 @@ static HRESULT ALCmmdevCapture_resetProxy(ALCmmdevCapture *self)
         return hr;
     }
 
-    /* FIXME: We should do conversion/resampling if we didn't get a matching format. */
-    if(wfx->nSamplesPerSec != OutputType.Format.nSamplesPerSec ||
-       wfx->wBitsPerSample != OutputType.Format.wBitsPerSample ||
-       wfx->nChannels != OutputType.Format.nChannels ||
-       wfx->nBlockAlign != OutputType.Format.nBlockAlign)
+    DestroySampleConverter(&self->SampleConv);
+    DestroyChannelConverter(&self->ChannelConv);
+
+    if(wfx != NULL)
     {
-        ERR("Did not get matching format, wanted: %s %s %uhz, got: %d channel(s) %d-bit %luhz\n",
-            DevFmtChannelsString(device->FmtChans), DevFmtTypeString(device->FmtType), device->Frequency,
-            wfx->nChannels, wfx->wBitsPerSample, wfx->nSamplesPerSec);
+        if(!(wfx->nChannels == OutputType.Format.nChannels ||
+             (wfx->nChannels == 1 && OutputType.Format.nChannels == 2) ||
+             (wfx->nChannels == 2 && OutputType.Format.nChannels == 1)))
+        {
+            ERR("Failed to get matching format, wanted: %s %s %uhz, got: %d channel%s %d-bit %luhz\n",
+                DevFmtChannelsString(device->FmtChans), DevFmtTypeString(device->FmtType),
+                device->Frequency, wfx->nChannels, (wfx->nChannels==1)?"":"s", wfx->wBitsPerSample,
+                wfx->nSamplesPerSec);
+            CoTaskMemFree(wfx);
+            return E_FAIL;
+        }
+
+        if(!MakeExtensible(&OutputType, wfx))
+        {
+            CoTaskMemFree(wfx);
+            return E_FAIL;
+        }
         CoTaskMemFree(wfx);
-        return E_FAIL;
+        wfx = NULL;
     }
 
-    if(!MakeExtensible(&OutputType, wfx))
+    if(IsEqualGUID(&OutputType.SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))
     {
-        CoTaskMemFree(wfx);
+        if(OutputType.Format.wBitsPerSample == 8)
+            srcType = DevFmtUByte;
+        else if(OutputType.Format.wBitsPerSample == 16)
+            srcType = DevFmtShort;
+        else if(OutputType.Format.wBitsPerSample == 32)
+            srcType = DevFmtInt;
+        else
+        {
+            ERR("Unhandled integer bit depth: %d\n", OutputType.Format.wBitsPerSample);
+            return E_FAIL;
+        }
+    }
+    else if(IsEqualGUID(&OutputType.SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))
+    {
+        if(OutputType.Format.wBitsPerSample == 32)
+            srcType = DevFmtFloat;
+        else
+        {
+            ERR("Unhandled float bit depth: %d\n", OutputType.Format.wBitsPerSample);
+            return E_FAIL;
+        }
+    }
+    else
+    {
+        ERR("Unhandled format sub-type\n");
         return E_FAIL;
     }
-    CoTaskMemFree(wfx);
-    wfx = NULL;
+
+    if(device->FmtChans == DevFmtMono && OutputType.Format.nChannels == 2)
+    {
+        self->ChannelConv = CreateChannelConverter(srcType, DevFmtStereo,
+                                                   device->FmtChans);
+        if(!self->ChannelConv)
+        {
+            ERR("Failed to create %s stereo-to-mono converter\n", DevFmtTypeString(srcType));
+            return E_FAIL;
+        }
+        TRACE("Created %s stereo-to-mono converter\n", DevFmtTypeString(srcType));
+        /* The channel converter always outputs float, so change the input type
+         * for the resampler/type-converter.
+         */
+        srcType = DevFmtFloat;
+    }
+    else if(device->FmtChans == DevFmtStereo && OutputType.Format.nChannels == 1)
+    {
+        self->ChannelConv = CreateChannelConverter(srcType, DevFmtMono,
+                                                   device->FmtChans);
+        if(!self->ChannelConv)
+        {
+            ERR("Failed to create %s mono-to-stereo converter\n", DevFmtTypeString(srcType));
+            return E_FAIL;
+        }
+        TRACE("Created %s mono-to-stereo converter\n", DevFmtTypeString(srcType));
+        srcType = DevFmtFloat;
+    }
+
+    if(device->Frequency != OutputType.Format.nSamplesPerSec || device->FmtType != srcType)
+    {
+        self->SampleConv = CreateSampleConverter(
+            srcType, device->FmtType, ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder),
+            OutputType.Format.nSamplesPerSec, device->Frequency
+        );
+        if(!self->SampleConv)
+        {
+            ERR("Failed to create converter for %s format, dst: %s %uhz, src: %s %luhz\n",
+                DevFmtChannelsString(device->FmtChans), DevFmtTypeString(device->FmtType),
+                device->Frequency, DevFmtTypeString(srcType), OutputType.Format.nSamplesPerSec);
+            return E_FAIL;
+        }
+        TRACE("Created converter for %s format, dst: %s %uhz, src: %s %luhz\n",
+              DevFmtChannelsString(device->FmtChans), DevFmtTypeString(device->FmtType),
+              device->Frequency, DevFmtTypeString(srcType), OutputType.Format.nSamplesPerSec);
+    }
 
     hr = IAudioClient_Initialize(self->client,
         AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
@@ -1608,7 +1819,9 @@ static HRESULT ALCmmdevCapture_resetProxy(ALCmmdevCapture *self)
 
     buffer_len = maxu(device->UpdateSize*device->NumUpdates + 1, buffer_len);
     ll_ringbuffer_free(self->Ring);
-    self->Ring = ll_ringbuffer_create(buffer_len, OutputType.Format.nBlockAlign);
+    self->Ring = ll_ringbuffer_create(buffer_len,
+        FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder)
+    );
     if(!self->Ring)
     {
         ERR("Failed to allocate capture ring buffer\n");
@@ -1713,9 +1926,9 @@ ALCenum ALCmmdevCapture_captureSamples(ALCmmdevCapture *self, ALCvoid *buffer, A
 
 
 static inline void AppendAllDevicesList2(const DevMap *entry)
-{ AppendAllDevicesList(al_string_get_cstr(entry->name)); }
+{ AppendAllDevicesList(alstr_get_cstr(entry->name)); }
 static inline void AppendCaptureDeviceList2(const DevMap *entry)
-{ AppendCaptureDeviceList(al_string_get_cstr(entry->name)); }
+{ AppendCaptureDeviceList(alstr_get_cstr(entry->name)); }
 
 typedef struct ALCmmdevBackendFactory {
     DERIVE_FROM_TYPE(ALCbackendFactory);
@@ -1739,7 +1952,7 @@ static BOOL MMDevApiLoad(void)
         ThreadRequest req;
         InitResult = E_FAIL;
 
-        req.FinishedEvt = CreateEvent(NULL, FALSE, FALSE, NULL);
+        req.FinishedEvt = CreateEventW(NULL, FALSE, FALSE, NULL);
         if(req.FinishedEvt == NULL)
             ERR("Failed to create event: %lu\n", GetLastError());
         else
@@ -1787,7 +2000,7 @@ static ALCboolean ALCmmdevBackendFactory_querySupport(ALCmmdevBackendFactory* UN
      * stereo input, for example, and the app asks for 22050hz mono,
      * initialization will fail.
      */
-    if(type == ALCbackend_Playback /*|| type == ALCbackend_Capture*/)
+    if(type == ALCbackend_Playback || type == ALCbackend_Capture)
         return ALC_TRUE;
     return ALC_FALSE;
 }
@@ -1796,7 +2009,7 @@ static void ALCmmdevBackendFactory_probe(ALCmmdevBackendFactory* UNUSED(self), e
 {
     ThreadRequest req = { NULL, 0 };
 
-    req.FinishedEvt = CreateEvent(NULL, FALSE, FALSE, NULL);
+    req.FinishedEvt = CreateEventW(NULL, FALSE, FALSE, NULL);
     if(req.FinishedEvt == NULL)
         ERR("Failed to create event: %lu\n", GetLastError());
     else

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

@@ -51,7 +51,7 @@ static ALCboolean ALCnullBackend_start(ALCnullBackend *self);
 static void ALCnullBackend_stop(ALCnullBackend *self);
 static DECLARE_FORWARD2(ALCnullBackend, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
 static DECLARE_FORWARD(ALCnullBackend, ALCbackend, ALCuint, availableSamples)
-static DECLARE_FORWARD(ALCnullBackend, ALCbackend, ALint64, getLatency)
+static DECLARE_FORWARD(ALCnullBackend, ALCbackend, ClockLatency, getClockLatency)
 static DECLARE_FORWARD(ALCnullBackend, ALCbackend, void, lock)
 static DECLARE_FORWARD(ALCnullBackend, ALCbackend, void, unlock)
 DECLARE_DEFAULT_ALLOCATORS(ALCnullBackend)
@@ -109,7 +109,9 @@ static int ALCnullBackend_mixerProc(void *ptr)
             al_nssleep(restTime);
         else while(avail-done >= device->UpdateSize)
         {
+            ALCnullBackend_lock(self);
             aluMixData(device, NULL, device->UpdateSize);
+            ALCnullBackend_unlock(self);
             done += device->UpdateSize;
         }
     }
@@ -128,7 +130,7 @@ static ALCenum ALCnullBackend_open(ALCnullBackend *self, const ALCchar *name)
         return ALC_INVALID_VALUE;
 
     device = STATIC_CAST(ALCbackend, self)->mDevice;
-    al_string_copy_cstr(&device->DeviceName, name);
+    alstr_copy_cstr(&device->DeviceName, name);
 
     return ALC_NO_ERROR;
 }

+ 869 - 167
libs/openal-soft/Alc/backends/opensl.c

@@ -22,38 +22,24 @@
 #include "config.h"
 
 #include <stdlib.h>
+#include <jni.h>
 
 #include "alMain.h"
 #include "alu.h"
+#include "compat.h"
 #include "threads.h"
 
+#include "backends/base.h"
+
 #include <SLES/OpenSLES.h>
 #include <SLES/OpenSLES_Android.h>
+#include <SLES/OpenSLES_AndroidConfiguration.h>
 
 /* Helper macros */
 #define VCALL(obj, func)  ((*(obj))->func((obj), EXTRACT_VCALL_ARGS
 #define VCALL0(obj, func)  ((*(obj))->func((obj) EXTRACT_VCALL_ARGS
 
 
-typedef struct {
-    /* engine interfaces */
-    SLObjectItf engineObject;
-    SLEngineItf engine;
-
-    /* output mix interfaces */
-    SLObjectItf outputMix;
-
-    /* buffer queue player interfaces */
-    SLObjectItf bufferQueueObject;
-
-    void *buffer;
-    ALuint bufferSize;
-    ALuint curBuffer;
-
-    ALuint frameSize;
-} osl_data;
-
-
 static const ALCchar opensl_device[] = "OpenSL";
 
 
@@ -79,10 +65,31 @@ static SLuint32 GetChannelMask(enum DevFmtChannels chans)
                                SL_SPEAKER_FRONT_CENTER|SL_SPEAKER_LOW_FREQUENCY|
                                SL_SPEAKER_BACK_LEFT|SL_SPEAKER_BACK_RIGHT|
                                SL_SPEAKER_SIDE_LEFT|SL_SPEAKER_SIDE_RIGHT;
-        case DevFmtBFormat3D: break;
+        case DevFmtAmbi3D:
+            break;
+    }
+    return 0;
+}
+
+#ifdef SL_DATAFORMAT_PCM_EX
+static SLuint32 GetTypeRepresentation(enum DevFmtType type)
+{
+    switch(type)
+    {
+        case DevFmtUByte:
+        case DevFmtUShort:
+        case DevFmtUInt:
+            return SL_PCM_REPRESENTATION_UNSIGNED_INT;
+        case DevFmtByte:
+        case DevFmtShort:
+        case DevFmtInt:
+            return SL_PCM_REPRESENTATION_SIGNED_INT;
+        case DevFmtFloat:
+            return SL_PCM_REPRESENTATION_FLOAT;
     }
     return 0;
 }
+#endif
 
 static const char *res_str(SLresult result)
 {
@@ -123,168 +130,463 @@ static const char *res_str(SLresult result)
         ERR("%s: %s\n", (s), res_str((x)));                                      \
 } while(0)
 
+
+typedef struct ALCopenslPlayback {
+    DERIVE_FROM_TYPE(ALCbackend);
+
+    /* engine interfaces */
+    SLObjectItf mEngineObj;
+    SLEngineItf mEngine;
+
+    /* output mix interfaces */
+    SLObjectItf mOutputMix;
+
+    /* buffer queue player interfaces */
+    SLObjectItf mBufferQueueObj;
+
+    ll_ringbuffer_t *mRing;
+    alcnd_t mCond;
+
+    ALsizei mFrameSize;
+
+    ATOMIC(ALenum) mKillNow;
+    althrd_t mThread;
+} ALCopenslPlayback;
+
+static void ALCopenslPlayback_process(SLAndroidSimpleBufferQueueItf bq, void *context);
+static int ALCopenslPlayback_mixerProc(void *arg);
+
+static void ALCopenslPlayback_Construct(ALCopenslPlayback *self, ALCdevice *device);
+static void ALCopenslPlayback_Destruct(ALCopenslPlayback *self);
+static ALCenum ALCopenslPlayback_open(ALCopenslPlayback *self, const ALCchar *name);
+static void ALCopenslPlayback_close(ALCopenslPlayback *self);
+static ALCboolean ALCopenslPlayback_reset(ALCopenslPlayback *self);
+static ALCboolean ALCopenslPlayback_start(ALCopenslPlayback *self);
+static void ALCopenslPlayback_stop(ALCopenslPlayback *self);
+static DECLARE_FORWARD2(ALCopenslPlayback, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
+static DECLARE_FORWARD(ALCopenslPlayback, ALCbackend, ALCuint, availableSamples)
+static ClockLatency ALCopenslPlayback_getClockLatency(ALCopenslPlayback *self);
+static DECLARE_FORWARD(ALCopenslPlayback, ALCbackend, void, lock)
+static DECLARE_FORWARD(ALCopenslPlayback, ALCbackend, void, unlock)
+DECLARE_DEFAULT_ALLOCATORS(ALCopenslPlayback)
+
+DEFINE_ALCBACKEND_VTABLE(ALCopenslPlayback);
+
+
+static void ALCopenslPlayback_Construct(ALCopenslPlayback *self, ALCdevice *device)
+{
+    ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
+    SET_VTABLE2(ALCopenslPlayback, ALCbackend, self);
+
+    self->mEngineObj = NULL;
+    self->mEngine = NULL;
+    self->mOutputMix = NULL;
+    self->mBufferQueueObj = NULL;
+
+    self->mRing = NULL;
+    alcnd_init(&self->mCond);
+
+    self->mFrameSize = 0;
+
+    ATOMIC_INIT(&self->mKillNow, AL_FALSE);
+}
+
+static void ALCopenslPlayback_Destruct(ALCopenslPlayback* self)
+{
+    if(self->mBufferQueueObj != NULL)
+        VCALL0(self->mBufferQueueObj,Destroy)();
+    self->mBufferQueueObj = NULL;
+
+    if(self->mOutputMix != NULL)
+        VCALL0(self->mOutputMix,Destroy)();
+    self->mOutputMix = NULL;
+
+    if(self->mEngineObj != NULL)
+        VCALL0(self->mEngineObj,Destroy)();
+    self->mEngineObj = NULL;
+    self->mEngine = NULL;
+
+    ll_ringbuffer_free(self->mRing);
+    self->mRing = NULL;
+
+    alcnd_destroy(&self->mCond);
+
+    ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
+}
+
+
 /* this callback handler is called every time a buffer finishes playing */
-static void opensl_callback(SLAndroidSimpleBufferQueueItf bq, void *context)
+static void ALCopenslPlayback_process(SLAndroidSimpleBufferQueueItf UNUSED(bq), void *context)
+{
+    ALCopenslPlayback *self = context;
+
+    /* A note on the ringbuffer usage: The buffer queue seems to hold on to the
+     * pointer passed to the Enqueue method, rather than copying the audio.
+     * Consequently, the ringbuffer contains the audio that is currently queued
+     * and waiting to play. This process() callback is called when a buffer is
+     * finished, so we simply move the read pointer up to indicate the space is
+     * available for writing again, and wake up the mixer thread to mix and
+     * queue more audio.
+     */
+    ll_ringbuffer_read_advance(self->mRing, 1);
+
+    alcnd_signal(&self->mCond);
+}
+
+
+static int ALCopenslPlayback_mixerProc(void *arg)
 {
-    ALCdevice *Device = context;
-    osl_data *data = Device->ExtraData;
-    ALvoid *buf;
+    ALCopenslPlayback *self = arg;
+    ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
+    SLAndroidSimpleBufferQueueItf bufferQueue;
+    ll_ringbuffer_data_t data[2];
+    SLPlayItf player;
     SLresult result;
+    size_t padding;
 
-    buf = (ALbyte*)data->buffer + data->curBuffer*data->bufferSize;
-    aluMixData(Device, buf, data->bufferSize/data->frameSize);
+    SetRTPriority();
+    althrd_setname(althrd_current(), MIXER_THREAD_NAME);
 
-    result = VCALL(bq,Enqueue)(buf, data->bufferSize);
-    PRINTERR(result, "bq->Enqueue");
+    result = VCALL(self->mBufferQueueObj,GetInterface)(SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
+                                                       &bufferQueue);
+    PRINTERR(result, "bufferQueue->GetInterface SL_IID_ANDROIDSIMPLEBUFFERQUEUE");
+    if(SL_RESULT_SUCCESS == result)
+    {
+        result = VCALL(self->mBufferQueueObj,GetInterface)(SL_IID_PLAY, &player);
+        PRINTERR(result, "bufferQueue->GetInterface SL_IID_PLAY");
+    }
+    if(SL_RESULT_SUCCESS != result)
+    {
+        ALCopenslPlayback_lock(self);
+        aluHandleDisconnect(device);
+        ALCopenslPlayback_unlock(self);
+        return 1;
+    }
+
+    /* NOTE: The ringbuffer will be larger than the desired buffer metrics.
+     * Calculate the amount of extra space so we know how much to keep unused.
+     */
+    padding = ll_ringbuffer_write_space(self->mRing) - device->NumUpdates;
+
+    ALCopenslPlayback_lock(self);
+    while(ATOMIC_LOAD_SEQ(&self->mKillNow) == AL_FALSE && device->Connected)
+    {
+        size_t todo, len0, len1;
+
+        if(ll_ringbuffer_write_space(self->mRing) <= padding)
+        {
+            SLuint32 state = 0;
+
+            result = VCALL(player,GetPlayState)(&state);
+            PRINTERR(result, "player->GetPlayState");
+            if(SL_RESULT_SUCCESS == result && state != SL_PLAYSTATE_PLAYING)
+            {
+                result = VCALL(player,SetPlayState)(SL_PLAYSTATE_PLAYING);
+                PRINTERR(result, "player->SetPlayState");
+            }
+            if(SL_RESULT_SUCCESS != result)
+            {
+                aluHandleDisconnect(device);
+                break;
+            }
+
+            /* NOTE: Unfortunately, there is an unavoidable race condition
+             * here. It's possible for the process() method to run, updating
+             * the read pointer and signaling the condition variable, in
+             * between checking the write size and waiting for the condition
+             * variable here. This will cause alcnd_wait to wait until the
+             * *next* process() invocation signals the condition variable
+             * again.
+             *
+             * However, this should only happen if the mixer is running behind
+             * anyway (as ideally we'll be asleep in alcnd_wait by the time the
+             * process() method is invoked), so this behavior is not completely
+             * unwarranted. It's unfortunate since it'll be wasting time
+             * sleeping that could be used to catch up, but there's no way
+             * around it without blocking in the process() method.
+             */
+            if(ll_ringbuffer_write_space(self->mRing) <= padding)
+            {
+                alcnd_wait(&self->mCond, &STATIC_CAST(ALCbackend,self)->mMutex);
+                continue;
+            }
+        }
+
+        ll_ringbuffer_get_write_vector(self->mRing, data);
+        todo = data[0].len+data[1].len - padding;
 
-    data->curBuffer = (data->curBuffer+1) % Device->NumUpdates;
+        len0 = minu(todo, data[0].len);
+        len1 = minu(todo-len0, data[1].len);
+
+        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;
+        }
+
+        if(len1 > 0)
+        {
+            aluMixData(device, data[1].buf, len1*device->UpdateSize);
+            for(size_t i = 0;i < len1;i++)
+            {
+                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[1].buf += device->UpdateSize*self->mFrameSize;
+            }
+        }
+    }
+    ALCopenslPlayback_unlock(self);
+
+    return 0;
 }
 
 
-static ALCenum opensl_open_playback(ALCdevice *Device, const ALCchar *deviceName)
+static ALCenum ALCopenslPlayback_open(ALCopenslPlayback *self, const ALCchar *name)
 {
-    osl_data *data = NULL;
+    ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
     SLresult result;
 
-    if(!deviceName)
-        deviceName = opensl_device;
-    else if(strcmp(deviceName, opensl_device) != 0)
+    if(!name)
+        name = opensl_device;
+    else if(strcmp(name, opensl_device) != 0)
         return ALC_INVALID_VALUE;
 
-    data = calloc(1, sizeof(*data));
-    if(!data)
-        return ALC_OUT_OF_MEMORY;
-
     // create engine
-    result = slCreateEngine(&data->engineObject, 0, NULL, 0, NULL, NULL);
+    result = slCreateEngine(&self->mEngineObj, 0, NULL, 0, NULL, NULL);
     PRINTERR(result, "slCreateEngine");
     if(SL_RESULT_SUCCESS == result)
     {
-        result = VCALL(data->engineObject,Realize)(SL_BOOLEAN_FALSE);
+        result = VCALL(self->mEngineObj,Realize)(SL_BOOLEAN_FALSE);
         PRINTERR(result, "engine->Realize");
     }
     if(SL_RESULT_SUCCESS == result)
     {
-        result = VCALL(data->engineObject,GetInterface)(SL_IID_ENGINE, &data->engine);
+        result = VCALL(self->mEngineObj,GetInterface)(SL_IID_ENGINE, &self->mEngine);
         PRINTERR(result, "engine->GetInterface");
     }
     if(SL_RESULT_SUCCESS == result)
     {
-        result = VCALL(data->engine,CreateOutputMix)(&data->outputMix, 0, NULL, NULL);
+        result = VCALL(self->mEngine,CreateOutputMix)(&self->mOutputMix, 0, NULL, NULL);
         PRINTERR(result, "engine->CreateOutputMix");
     }
     if(SL_RESULT_SUCCESS == result)
     {
-        result = VCALL(data->outputMix,Realize)(SL_BOOLEAN_FALSE);
+        result = VCALL(self->mOutputMix,Realize)(SL_BOOLEAN_FALSE);
         PRINTERR(result, "outputMix->Realize");
     }
 
     if(SL_RESULT_SUCCESS != result)
     {
-        if(data->outputMix != NULL)
-            VCALL0(data->outputMix,Destroy)();
-        data->outputMix = NULL;
+        if(self->mOutputMix != NULL)
+            VCALL0(self->mOutputMix,Destroy)();
+        self->mOutputMix = NULL;
 
-        if(data->engineObject != NULL)
-            VCALL0(data->engineObject,Destroy)();
-        data->engineObject = NULL;
-        data->engine = NULL;
+        if(self->mEngineObj != NULL)
+            VCALL0(self->mEngineObj,Destroy)();
+        self->mEngineObj = NULL;
+        self->mEngine = NULL;
 
-        free(data);
         return ALC_INVALID_VALUE;
     }
 
-    al_string_copy_cstr(&Device->DeviceName, deviceName);
-    Device->ExtraData = data;
+    alstr_copy_cstr(&device->DeviceName, name);
 
     return ALC_NO_ERROR;
 }
 
-
-static void opensl_close_playback(ALCdevice *Device)
+static void ALCopenslPlayback_close(ALCopenslPlayback *self)
 {
-    osl_data *data = Device->ExtraData;
-
-    if(data->bufferQueueObject != NULL)
-        VCALL0(data->bufferQueueObject,Destroy)();
-    data->bufferQueueObject = NULL;
+    if(self->mBufferQueueObj != NULL)
+        VCALL0(self->mBufferQueueObj,Destroy)();
+    self->mBufferQueueObj = NULL;
 
-    VCALL0(data->outputMix,Destroy)();
-    data->outputMix = NULL;
+    VCALL0(self->mOutputMix,Destroy)();
+    self->mOutputMix = NULL;
 
-    VCALL0(data->engineObject,Destroy)();
-    data->engineObject = NULL;
-    data->engine = NULL;
-
-    free(data);
-    Device->ExtraData = NULL;
+    VCALL0(self->mEngineObj,Destroy)();
+    self->mEngineObj = NULL;
+    self->mEngine = NULL;
 }
 
-static ALCboolean opensl_reset_playback(ALCdevice *Device)
+static ALCboolean ALCopenslPlayback_reset(ALCopenslPlayback *self)
 {
-    osl_data *data = Device->ExtraData;
+    ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
     SLDataLocator_AndroidSimpleBufferQueue loc_bufq;
     SLDataLocator_OutputMix loc_outmix;
-    SLDataFormat_PCM format_pcm;
     SLDataSource audioSrc;
     SLDataSink audioSnk;
-    SLInterfaceID id;
-    SLboolean req;
+    ALuint sampleRate;
+    SLInterfaceID ids[2];
+    SLboolean reqs[2];
     SLresult result;
+    JNIEnv *env;
 
+    if(self->mBufferQueueObj != NULL)
+        VCALL0(self->mBufferQueueObj,Destroy)();
+    self->mBufferQueueObj = NULL;
 
-    Device->UpdateSize = (ALuint64)Device->UpdateSize * 44100 / Device->Frequency;
-    Device->UpdateSize = Device->UpdateSize * Device->NumUpdates / 2;
-    Device->NumUpdates = 2;
+    sampleRate = device->Frequency;
+    if(!(device->Flags&DEVICE_FREQUENCY_REQUEST) && (env=Android_GetJNIEnv()) != NULL)
+    {
+        /* FIXME: Disabled until I figure out how to get the Context needed for
+         * the getSystemService call.
+         */
+#if 0
+        /* Get necessary stuff for using java.lang.Integer,
+         * android.content.Context, and android.media.AudioManager.
+         */
+        jclass int_cls = JCALL(env,FindClass)("java/lang/Integer");
+        jmethodID int_parseint = JCALL(env,GetStaticMethodID)(int_cls,
+            "parseInt", "(Ljava/lang/String;)I"
+        );
+        TRACE("Integer: %p, parseInt: %p\n", int_cls, int_parseint);
+
+        jclass ctx_cls = JCALL(env,FindClass)("android/content/Context");
+        jfieldID ctx_audsvc = JCALL(env,GetStaticFieldID)(ctx_cls,
+            "AUDIO_SERVICE", "Ljava/lang/String;"
+        );
+        jmethodID ctx_getSysSvc = JCALL(env,GetMethodID)(ctx_cls,
+            "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;"
+        );
+        TRACE("Context: %p, AUDIO_SERVICE: %p, getSystemService: %p\n",
+              ctx_cls, ctx_audsvc, ctx_getSysSvc);
+
+        jclass audmgr_cls = JCALL(env,FindClass)("android/media/AudioManager");
+        jfieldID audmgr_prop_out_srate = JCALL(env,GetStaticFieldID)(audmgr_cls,
+            "PROPERTY_OUTPUT_SAMPLE_RATE", "Ljava/lang/String;"
+        );
+        jmethodID audmgr_getproperty = JCALL(env,GetMethodID)(audmgr_cls,
+            "getProperty", "(Ljava/lang/String;)Ljava/lang/String;"
+        );
+        TRACE("AudioManager: %p, PROPERTY_OUTPUT_SAMPLE_RATE: %p, getProperty: %p\n",
+              audmgr_cls, audmgr_prop_out_srate, audmgr_getproperty);
+
+        const char *strchars;
+        jstring strobj;
+
+        /* 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);
+        strchars = JCALL(env,GetStringUTFChars)(strobj, NULL);
+        TRACE("Context.getSystemService(%s) = %p\n", strchars, audMgr);
+        JCALL(env,ReleaseStringUTFChars)(strobj, strchars);
+
+        //String srateStr = audMgr.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE);
+        strobj = JCALL(env,GetStaticObjectField)(audmgr_cls, audmgr_prop_out_srate);
+        jstring srateStr = JCALL(env,CallObjectMethod)(audMgr, audmgr_getproperty, strobj);
+        strchars = JCALL(env,GetStringUTFChars)(strobj, NULL);
+        TRACE("audMgr.getProperty(%s) = %p\n", strchars, srateStr);
+        JCALL(env,ReleaseStringUTFChars)(strobj, strchars);
+
+        //int sampleRate = Integer.parseInt(srateStr);
+        sampleRate = JCALL(env,CallStaticIntMethod)(int_cls, int_parseint, srateStr);
+
+        strchars = JCALL(env,GetStringUTFChars)(srateStr, NULL);
+        TRACE("Got system sample rate %uhz (%s)\n", sampleRate, strchars);
+        JCALL(env,ReleaseStringUTFChars)(srateStr, strchars);
+
+        if(!sampleRate) sampleRate = device->Frequency;
+        else sampleRate = maxu(sampleRate, MIN_OUTPUT_RATE);
+#endif
+    }
 
-    Device->Frequency = 44100;
-    Device->FmtChans = DevFmtStereo;
-    Device->FmtType = DevFmtShort;
+    if(sampleRate != device->Frequency)
+    {
+        device->NumUpdates = (device->NumUpdates*sampleRate + (device->Frequency>>1)) /
+                             device->Frequency;
+        device->NumUpdates = maxu(device->NumUpdates, 2);
+        device->Frequency = sampleRate;
+    }
 
-    SetDefaultWFXChannelOrder(Device);
+    device->FmtChans = DevFmtStereo;
+    device->FmtType = DevFmtShort;
 
+    SetDefaultWFXChannelOrder(device);
+    self->mFrameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
 
-    id  = SL_IID_ANDROIDSIMPLEBUFFERQUEUE;
-    req = SL_BOOLEAN_TRUE;
 
     loc_bufq.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
-    loc_bufq.numBuffers = Device->NumUpdates;
-
+    loc_bufq.numBuffers = device->NumUpdates;
+
+#ifdef SL_DATAFORMAT_PCM_EX
+    SLDataFormat_PCM_EX format_pcm;
+    format_pcm.formatType = SL_DATAFORMAT_PCM_EX;
+    format_pcm.numChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
+    format_pcm.sampleRate = device->Frequency * 1000;
+    format_pcm.bitsPerSample = BytesFromDevFmt(device->FmtType) * 8;
+    format_pcm.containerSize = format_pcm.bitsPerSample;
+    format_pcm.channelMask = GetChannelMask(device->FmtChans);
+    format_pcm.endianness = IS_LITTLE_ENDIAN ? SL_BYTEORDER_LITTLEENDIAN :
+                                               SL_BYTEORDER_BIGENDIAN;
+    format_pcm.representation = GetTypeRepresentation(device->FmtType);
+#else
+    SLDataFormat_PCM format_pcm;
     format_pcm.formatType = SL_DATAFORMAT_PCM;
-    format_pcm.numChannels = ChannelsFromDevFmt(Device->FmtChans);
-    format_pcm.samplesPerSec = Device->Frequency * 1000;
-    format_pcm.bitsPerSample = BytesFromDevFmt(Device->FmtType) * 8;
+    format_pcm.numChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
+    format_pcm.samplesPerSec = device->Frequency * 1000;
+    format_pcm.bitsPerSample = BytesFromDevFmt(device->FmtType) * 8;
     format_pcm.containerSize = format_pcm.bitsPerSample;
-    format_pcm.channelMask = GetChannelMask(Device->FmtChans);
+    format_pcm.channelMask = GetChannelMask(device->FmtChans);
     format_pcm.endianness = IS_LITTLE_ENDIAN ? SL_BYTEORDER_LITTLEENDIAN :
                                                SL_BYTEORDER_BIGENDIAN;
+#endif
 
     audioSrc.pLocator = &loc_bufq;
     audioSrc.pFormat = &format_pcm;
 
     loc_outmix.locatorType = SL_DATALOCATOR_OUTPUTMIX;
-    loc_outmix.outputMix = data->outputMix;
+    loc_outmix.outputMix = self->mOutputMix;
     audioSnk.pLocator = &loc_outmix;
     audioSnk.pFormat = NULL;
 
 
-    if(data->bufferQueueObject != NULL)
-        VCALL0(data->bufferQueueObject,Destroy)();
-    data->bufferQueueObject = NULL;
+    ids[0]  = SL_IID_ANDROIDSIMPLEBUFFERQUEUE;
+    reqs[0] = SL_BOOLEAN_TRUE;
+    ids[1]  = SL_IID_ANDROIDCONFIGURATION;
+    reqs[1] = SL_BOOLEAN_FALSE;
 
-    result = VCALL(data->engine,CreateAudioPlayer)(&data->bufferQueueObject, &audioSrc, &audioSnk, 1, &id, &req);
+    result = VCALL(self->mEngine,CreateAudioPlayer)(&self->mBufferQueueObj,
+        &audioSrc, &audioSnk, COUNTOF(ids), ids, reqs
+    );
     PRINTERR(result, "engine->CreateAudioPlayer");
     if(SL_RESULT_SUCCESS == result)
     {
-        result = VCALL(data->bufferQueueObject,Realize)(SL_BOOLEAN_FALSE);
+        /* Set the stream type to "media" (games, music, etc), if possible. */
+        SLAndroidConfigurationItf config;
+        result = VCALL(self->mBufferQueueObj,GetInterface)(SL_IID_ANDROIDCONFIGURATION, &config);
+        PRINTERR(result, "bufferQueue->GetInterface SL_IID_ANDROIDCONFIGURATION");
+        if(SL_RESULT_SUCCESS == result)
+        {
+            SLint32 streamType = SL_ANDROID_STREAM_MEDIA;
+            result = VCALL(config,SetConfiguration)(SL_ANDROID_KEY_STREAM_TYPE,
+                &streamType, sizeof(streamType)
+            );
+            PRINTERR(result, "config->SetConfiguration");
+        }
+
+        /* Clear any error since this was optional. */
+        result = SL_RESULT_SUCCESS;
+    }
+    if(SL_RESULT_SUCCESS == result)
+    {
+        result = VCALL(self->mBufferQueueObj,Realize)(SL_BOOLEAN_FALSE);
         PRINTERR(result, "bufferQueue->Realize");
     }
 
     if(SL_RESULT_SUCCESS != result)
     {
-        if(data->bufferQueueObject != NULL)
-            VCALL0(data->bufferQueueObject,Destroy)();
-        data->bufferQueueObject = NULL;
+        if(self->mBufferQueueObj != NULL)
+            VCALL0(self->mBufferQueueObj,Destroy)();
+        self->mBufferQueueObj = NULL;
 
         return ALC_FALSE;
     }
@@ -292,142 +594,542 @@ static ALCboolean opensl_reset_playback(ALCdevice *Device)
     return ALC_TRUE;
 }
 
-static ALCboolean opensl_start_playback(ALCdevice *Device)
+static ALCboolean ALCopenslPlayback_start(ALCopenslPlayback *self)
+{
+    ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
+    SLAndroidSimpleBufferQueueItf bufferQueue;
+    SLresult result;
+
+    ll_ringbuffer_free(self->mRing);
+    /* NOTE: Add an extra update since one period's worth of audio in the ring
+     * buffer will always be left unfilled because one element of the ring
+     * buffer will not be writeable, and we only write in period-sized chunks.
+     */
+    self->mRing = ll_ringbuffer_create(device->NumUpdates + 1,
+                                       self->mFrameSize*device->UpdateSize);
+
+    result = VCALL(self->mBufferQueueObj,GetInterface)(SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
+                                                       &bufferQueue);
+    PRINTERR(result, "bufferQueue->GetInterface");
+    if(SL_RESULT_SUCCESS != result)
+        return ALC_FALSE;
+
+    result = VCALL(bufferQueue,RegisterCallback)(ALCopenslPlayback_process, self);
+    PRINTERR(result, "bufferQueue->RegisterCallback");
+    if(SL_RESULT_SUCCESS != result)
+        return ALC_FALSE;
+
+    ATOMIC_STORE_SEQ(&self->mKillNow, AL_FALSE);
+    if(althrd_create(&self->mThread, ALCopenslPlayback_mixerProc, self) != althrd_success)
+    {
+        ERR("Failed to start mixer thread\n");
+        return ALC_FALSE;
+    }
+
+    return ALC_TRUE;
+}
+
+
+static void ALCopenslPlayback_stop(ALCopenslPlayback *self)
 {
-    osl_data *data = Device->ExtraData;
     SLAndroidSimpleBufferQueueItf bufferQueue;
     SLPlayItf player;
     SLresult result;
-    ALuint i;
+    int res;
+
+    if(ATOMIC_EXCHANGE_SEQ(&self->mKillNow, AL_TRUE))
+        return;
+
+    /* Lock the backend to ensure we don't flag the mixer to die and signal the
+     * mixer to wake up in between it checking the flag and going to sleep and
+     * wait for a wakeup (potentially leading to it never waking back up to see
+     * the flag).
+     */
+    ALCopenslPlayback_lock(self);
+    ALCopenslPlayback_unlock(self);
+    alcnd_signal(&self->mCond);
+    althrd_join(self->mThread, &res);
+
+    result = VCALL(self->mBufferQueueObj,GetInterface)(SL_IID_PLAY, &player);
+    PRINTERR(result, "bufferQueue->GetInterface");
+    if(SL_RESULT_SUCCESS == result)
+    {
+        result = VCALL(player,SetPlayState)(SL_PLAYSTATE_STOPPED);
+        PRINTERR(result, "player->SetPlayState");
+    }
 
-    result = VCALL(data->bufferQueueObject,GetInterface)(SL_IID_BUFFERQUEUE, &bufferQueue);
+    result = VCALL(self->mBufferQueueObj,GetInterface)(SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
+                                                       &bufferQueue);
     PRINTERR(result, "bufferQueue->GetInterface");
     if(SL_RESULT_SUCCESS == result)
     {
-        result = VCALL(bufferQueue,RegisterCallback)(opensl_callback, Device);
+        result = VCALL0(bufferQueue,Clear)();
+        PRINTERR(result, "bufferQueue->Clear");
+    }
+    if(SL_RESULT_SUCCESS == result)
+    {
+        result = VCALL(bufferQueue,RegisterCallback)(NULL, NULL);
         PRINTERR(result, "bufferQueue->RegisterCallback");
     }
     if(SL_RESULT_SUCCESS == result)
     {
-        data->frameSize = FrameSizeFromDevFmt(Device->FmtChans, Device->FmtType);
-        data->bufferSize = Device->UpdateSize * data->frameSize;
-        data->buffer = calloc(Device->NumUpdates, data->bufferSize);
-        if(!data->buffer)
-        {
-            result = SL_RESULT_MEMORY_FAILURE;
-            PRINTERR(result, "calloc");
-        }
+        SLAndroidSimpleBufferQueueState state;
+        do {
+            althrd_yield();
+            result = VCALL(bufferQueue,GetState)(&state);
+        } while(SL_RESULT_SUCCESS == result && state.count > 0);
+        PRINTERR(result, "bufferQueue->GetState");
     }
-    /* enqueue the first buffer to kick off the callbacks */
-    for(i = 0;i < Device->NumUpdates;i++)
+
+    ll_ringbuffer_free(self->mRing);
+    self->mRing = NULL;
+}
+
+static ClockLatency ALCopenslPlayback_getClockLatency(ALCopenslPlayback *self)
+{
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+    ClockLatency ret;
+
+    ALCopenslPlayback_lock(self);
+    ret.ClockTime = GetDeviceClockTime(device);
+    ret.Latency = ll_ringbuffer_read_space(self->mRing)*device->UpdateSize *
+                  DEVICE_CLOCK_RES / device->Frequency;
+    ALCopenslPlayback_unlock(self);
+
+    return ret;
+}
+
+
+typedef struct ALCopenslCapture {
+    DERIVE_FROM_TYPE(ALCbackend);
+
+    /* engine interfaces */
+    SLObjectItf mEngineObj;
+    SLEngineItf mEngine;
+
+    /* recording interfaces */
+    SLObjectItf mRecordObj;
+
+    ll_ringbuffer_t *mRing;
+    ALCuint mSplOffset;
+
+    ALsizei mFrameSize;
+} ALCopenslCapture;
+
+static void ALCopenslCapture_process(SLAndroidSimpleBufferQueueItf bq, void *context);
+
+static void ALCopenslCapture_Construct(ALCopenslCapture *self, ALCdevice *device);
+static void ALCopenslCapture_Destruct(ALCopenslCapture *self);
+static ALCenum ALCopenslCapture_open(ALCopenslCapture *self, const ALCchar *name);
+static void ALCopenslCapture_close(ALCopenslCapture *self);
+static DECLARE_FORWARD(ALCopenslCapture, ALCbackend, ALCboolean, reset)
+static ALCboolean ALCopenslCapture_start(ALCopenslCapture *self);
+static void ALCopenslCapture_stop(ALCopenslCapture *self);
+static ALCenum ALCopenslCapture_captureSamples(ALCopenslCapture *self, ALCvoid *buffer, ALCuint samples);
+static ALCuint ALCopenslCapture_availableSamples(ALCopenslCapture *self);
+static DECLARE_FORWARD(ALCopenslCapture, ALCbackend, ClockLatency, getClockLatency)
+static DECLARE_FORWARD(ALCopenslCapture, ALCbackend, void, lock)
+static DECLARE_FORWARD(ALCopenslCapture, ALCbackend, void, unlock)
+DECLARE_DEFAULT_ALLOCATORS(ALCopenslCapture)
+DEFINE_ALCBACKEND_VTABLE(ALCopenslCapture);
+
+
+static void ALCopenslCapture_process(SLAndroidSimpleBufferQueueItf UNUSED(bq), void *context)
+{
+    ALCopenslCapture *self = context;
+    /* A new chunk has been written into the ring buffer, advance it. */
+    ll_ringbuffer_write_advance(self->mRing, 1);
+}
+
+
+static void ALCopenslCapture_Construct(ALCopenslCapture *self, ALCdevice *device)
+{
+    ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
+    SET_VTABLE2(ALCopenslCapture, ALCbackend, self);
+
+    self->mEngineObj = NULL;
+    self->mEngine = NULL;
+
+    self->mRecordObj = NULL;
+
+    self->mRing = NULL;
+    self->mSplOffset = 0;
+
+    self->mFrameSize = 0;
+}
+
+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;
+
+    if(self->mEngineObj != NULL)
+        VCALL0(self->mEngineObj,Destroy)();
+    self->mEngineObj = NULL;
+    self->mEngine = NULL;
+
+    ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
+}
+
+static ALCenum ALCopenslCapture_open(ALCopenslCapture *self, const ALCchar *name)
+{
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+    SLDataLocator_AndroidSimpleBufferQueue loc_bq;
+    SLAndroidSimpleBufferQueueItf bufferQueue;
+    SLDataLocator_IODevice loc_dev;
+    SLDataSource audioSrc;
+    SLDataSink audioSnk;
+    SLresult result;
+
+    if(!name)
+        name = opensl_device;
+    else if(strcmp(name, opensl_device) != 0)
+        return ALC_INVALID_VALUE;
+
+    result = slCreateEngine(&self->mEngineObj, 0, NULL, 0, NULL, NULL);
+    PRINTERR(result, "slCreateEngine");
+    if(SL_RESULT_SUCCESS == result)
     {
+        result = VCALL(self->mEngineObj,Realize)(SL_BOOLEAN_FALSE);
+        PRINTERR(result, "engine->Realize");
+    }
+    if(SL_RESULT_SUCCESS == result)
+    {
+        result = VCALL(self->mEngineObj,GetInterface)(SL_IID_ENGINE, &self->mEngine);
+        PRINTERR(result, "engine->GetInterface");
+    }
+    if(SL_RESULT_SUCCESS == result)
+    {
+        /* Ensure the total length is at least 100ms */
+        ALsizei length = maxi(device->NumUpdates * device->UpdateSize,
+                              device->Frequency / 10);
+        /* Ensure the per-chunk length is at least 10ms, and no more than 50ms. */
+        ALsizei update_len = clampi(device->NumUpdates*device->UpdateSize / 3,
+                                    device->Frequency / 100,
+                                    device->Frequency / 100 * 5);
+
+        device->UpdateSize = update_len;
+        device->NumUpdates = (length+update_len-1) / update_len;
+
+        self->mFrameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
+    }
+    loc_dev.locatorType = SL_DATALOCATOR_IODEVICE;
+    loc_dev.deviceType = SL_IODEVICE_AUDIOINPUT;
+    loc_dev.deviceID = SL_DEFAULTDEVICEID_AUDIOINPUT;
+    loc_dev.device = NULL;
+
+    audioSrc.pLocator = &loc_dev;
+    audioSrc.pFormat = NULL;
+
+    loc_bq.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
+    loc_bq.numBuffers = device->NumUpdates;
+
+#ifdef SL_DATAFORMAT_PCM_EX
+    SLDataFormat_PCM_EX format_pcm;
+    format_pcm.formatType = SL_DATAFORMAT_PCM_EX;
+    format_pcm.numChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
+    format_pcm.sampleRate = device->Frequency * 1000;
+    format_pcm.bitsPerSample = BytesFromDevFmt(device->FmtType) * 8;
+    format_pcm.containerSize = format_pcm.bitsPerSample;
+    format_pcm.channelMask = GetChannelMask(device->FmtChans);
+    format_pcm.endianness = IS_LITTLE_ENDIAN ? SL_BYTEORDER_LITTLEENDIAN :
+                                               SL_BYTEORDER_BIGENDIAN;
+    format_pcm.representation = GetTypeRepresentation(device->FmtType);
+#else
+    SLDataFormat_PCM format_pcm;
+    format_pcm.formatType = SL_DATAFORMAT_PCM;
+    format_pcm.numChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
+    format_pcm.samplesPerSec = device->Frequency * 1000;
+    format_pcm.bitsPerSample = BytesFromDevFmt(device->FmtType) * 8;
+    format_pcm.containerSize = format_pcm.bitsPerSample;
+    format_pcm.channelMask = GetChannelMask(device->FmtChans);
+    format_pcm.endianness = IS_LITTLE_ENDIAN ? SL_BYTEORDER_LITTLEENDIAN :
+                                               SL_BYTEORDER_BIGENDIAN;
+#endif
+
+    audioSnk.pLocator = &loc_bq;
+    audioSnk.pFormat = &format_pcm;
+
+    if(SL_RESULT_SUCCESS == result)
+    {
+        const SLInterfaceID ids[2] = { SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_ANDROIDCONFIGURATION };
+        const SLboolean reqs[2] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_FALSE };
+
+        result = VCALL(self->mEngine,CreateAudioRecorder)(&self->mRecordObj,
+            &audioSrc, &audioSnk, COUNTOF(ids), ids, reqs
+        );
+        PRINTERR(result, "engine->CreateAudioRecorder");
+    }
+    if(SL_RESULT_SUCCESS == result)
+    {
+        /* Set the record preset to "generic", if possible. */
+        SLAndroidConfigurationItf config;
+        result = VCALL(self->mRecordObj,GetInterface)(SL_IID_ANDROIDCONFIGURATION, &config);
+        PRINTERR(result, "recordObj->GetInterface SL_IID_ANDROIDCONFIGURATION");
         if(SL_RESULT_SUCCESS == result)
         {
-            ALvoid *buf = (ALbyte*)data->buffer + i*data->bufferSize;
-            result = VCALL(bufferQueue,Enqueue)(buf, data->bufferSize);
-            PRINTERR(result, "bufferQueue->Enqueue");
+            SLuint32 preset = SL_ANDROID_RECORDING_PRESET_GENERIC;
+            result = VCALL(config,SetConfiguration)(SL_ANDROID_KEY_RECORDING_PRESET,
+                &preset, sizeof(preset)
+            );
+            PRINTERR(result, "config->SetConfiguration");
         }
+
+        /* Clear any error since this was optional. */
+        result = SL_RESULT_SUCCESS;
     }
-    data->curBuffer = 0;
     if(SL_RESULT_SUCCESS == result)
     {
-        result = VCALL(data->bufferQueueObject,GetInterface)(SL_IID_PLAY, &player);
-        PRINTERR(result, "bufferQueue->GetInterface");
+        result = VCALL(self->mRecordObj,Realize)(SL_BOOLEAN_FALSE);
+        PRINTERR(result, "recordObj->Realize");
     }
+
     if(SL_RESULT_SUCCESS == result)
     {
-        result = VCALL(player,SetPlayState)(SL_PLAYSTATE_PLAYING);
-        PRINTERR(result, "player->SetPlayState");
+        self->mRing = ll_ringbuffer_create(device->NumUpdates + 1,
+                                           device->UpdateSize * self->mFrameSize);
+
+        result = VCALL(self->mRecordObj,GetInterface)(SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
+                                                      &bufferQueue);
+        PRINTERR(result, "recordObj->GetInterface");
+    }
+    if(SL_RESULT_SUCCESS == result)
+    {
+        result = VCALL(bufferQueue,RegisterCallback)(ALCopenslCapture_process, self);
+        PRINTERR(result, "bufferQueue->RegisterCallback");
+    }
+    if(SL_RESULT_SUCCESS == result)
+    {
+        ALsizei chunk_size = device->UpdateSize * self->mFrameSize;
+        ll_ringbuffer_data_t data[2];
+        size_t i;
+
+        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)
     {
-        if(data->bufferQueueObject != NULL)
-            VCALL0(data->bufferQueueObject,Destroy)();
-        data->bufferQueueObject = NULL;
+        if(self->mRecordObj != NULL)
+            VCALL0(self->mRecordObj,Destroy)();
+        self->mRecordObj = NULL;
 
-        free(data->buffer);
-        data->buffer = NULL;
-        data->bufferSize = 0;
+        if(self->mEngineObj != NULL)
+            VCALL0(self->mEngineObj,Destroy)();
+        self->mEngineObj = NULL;
+        self->mEngine = NULL;
 
-        return ALC_FALSE;
+        return ALC_INVALID_VALUE;
     }
 
-    return ALC_TRUE;
+    alstr_copy_cstr(&device->DeviceName, name);
+
+    return ALC_NO_ERROR;
 }
 
+static void ALCopenslCapture_close(ALCopenslCapture *self)
+{
+    ll_ringbuffer_free(self->mRing);
+    self->mRing = NULL;
+
+    if(self->mRecordObj != NULL)
+        VCALL0(self->mRecordObj,Destroy)();
+    self->mRecordObj = NULL;
 
-static void opensl_stop_playback(ALCdevice *Device)
+    if(self->mEngineObj != NULL)
+        VCALL0(self->mEngineObj,Destroy)();
+    self->mEngineObj = NULL;
+    self->mEngine = NULL;
+}
+
+static ALCboolean ALCopenslCapture_start(ALCopenslCapture *self)
 {
-    osl_data *data = Device->ExtraData;
-    SLPlayItf player;
-    SLAndroidSimpleBufferQueueItf bufferQueue;
+    SLRecordItf record;
     SLresult result;
 
-    result = VCALL(data->bufferQueueObject,GetInterface)(SL_IID_PLAY, &player);
-    PRINTERR(result, "bufferQueue->GetInterface");
+    result = VCALL(self->mRecordObj,GetInterface)(SL_IID_RECORD, &record);
+    PRINTERR(result, "recordObj->GetInterface");
+
     if(SL_RESULT_SUCCESS == result)
     {
-        result = VCALL(player,SetPlayState)(SL_PLAYSTATE_STOPPED);
-        PRINTERR(result, "player->SetPlayState");
+        result = VCALL(record,SetRecordState)(SL_RECORDSTATE_RECORDING);
+        PRINTERR(result, "record->SetRecordState");
     }
 
-    result = VCALL(data->bufferQueueObject,GetInterface)(SL_IID_BUFFERQUEUE, &bufferQueue);
-    PRINTERR(result, "bufferQueue->GetInterface");
-    if(SL_RESULT_SUCCESS == result)
+    if(SL_RESULT_SUCCESS != result)
     {
-        result = VCALL0(bufferQueue,Clear)();
-        PRINTERR(result, "bufferQueue->Clear");
+        ALCopenslCapture_lock(self);
+        aluHandleDisconnect(STATIC_CAST(ALCbackend, self)->mDevice);
+        ALCopenslCapture_unlock(self);
+        return ALC_FALSE;
     }
+
+    return ALC_TRUE;
+}
+
+static void ALCopenslCapture_stop(ALCopenslCapture *self)
+{
+    SLRecordItf record;
+    SLresult result;
+
+    result = VCALL(self->mRecordObj,GetInterface)(SL_IID_RECORD, &record);
+    PRINTERR(result, "recordObj->GetInterface");
+
     if(SL_RESULT_SUCCESS == result)
     {
-        SLAndroidSimpleBufferQueueState state;
-        do {
-            althrd_yield();
-            result = VCALL(bufferQueue,GetState)(&state);
-        } while(SL_RESULT_SUCCESS == result && state.count > 0);
-        PRINTERR(result, "bufferQueue->GetState");
+        result = VCALL(record,SetRecordState)(SL_RECORDSTATE_PAUSED);
+        PRINTERR(result, "record->SetRecordState");
     }
+}
 
-    free(data->buffer);
-    data->buffer = NULL;
-    data->bufferSize = 0;
+static ALCenum ALCopenslCapture_captureSamples(ALCopenslCapture *self, ALCvoid *buffer, ALCuint samples)
+{
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+    ALsizei chunk_size = device->UpdateSize * self->mFrameSize;
+    SLAndroidSimpleBufferQueueItf bufferQueue;
+    ll_ringbuffer_data_t data[2];
+    SLresult result;
+    size_t advance;
+    ALCuint i;
+
+    /* 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);
+        memcpy((ALCbyte*)buffer + i*self->mFrameSize,
+               data[0].buf + self->mSplOffset*self->mFrameSize,
+               rem * self->mFrameSize);
+
+        self->mSplOffset += rem;
+        if(self->mSplOffset == device->UpdateSize)
+        {
+            /* Finished a chunk, reset the offset and advance the read pointer. */
+            self->mSplOffset = 0;
+            advance++;
+
+            data[0].len--;
+            if(!data[0].len)
+                data[0] = data[1];
+            else
+                data[0].buf += chunk_size;
+        }
+
+        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)
+    {
+        ALCopenslCapture_lock(self);
+        aluHandleDisconnect(device);
+        ALCopenslCapture_unlock(self);
+        return ALC_INVALID_DEVICE;
+    }
+
+    return ALC_NO_ERROR;
 }
 
+static ALCuint ALCopenslCapture_availableSamples(ALCopenslCapture *self)
+{
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+    return ll_ringbuffer_read_space(self->mRing) * device->UpdateSize;
+}
 
-static const BackendFuncs opensl_funcs = {
-    opensl_open_playback,
-    opensl_close_playback,
-    opensl_reset_playback,
-    opensl_start_playback,
-    opensl_stop_playback,
-    NULL,
-    NULL,
-    NULL,
-    NULL,
-    NULL,
-    NULL
-};
 
+typedef struct ALCopenslBackendFactory {
+    DERIVE_FROM_TYPE(ALCbackendFactory);
+} ALCopenslBackendFactory;
+#define ALCOPENSLBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCopenslBackendFactory, ALCbackendFactory) } }
 
-ALCboolean alc_opensl_init(BackendFuncs *func_list)
+static ALCboolean ALCopenslBackendFactory_init(ALCopenslBackendFactory* UNUSED(self))
 {
-    *func_list = opensl_funcs;
     return ALC_TRUE;
 }
 
-void alc_opensl_deinit(void)
+static void ALCopenslBackendFactory_deinit(ALCopenslBackendFactory* UNUSED(self))
 {
 }
 
-void alc_opensl_probe(enum DevProbe type)
+static ALCboolean ALCopenslBackendFactory_querySupport(ALCopenslBackendFactory* UNUSED(self), ALCbackend_Type type)
+{
+    if(type == ALCbackend_Playback || type == ALCbackend_Capture)
+        return ALC_TRUE;
+    return ALC_FALSE;
+}
+
+static void ALCopenslBackendFactory_probe(ALCopenslBackendFactory* UNUSED(self), enum DevProbe type)
 {
     switch(type)
     {
         case ALL_DEVICE_PROBE:
             AppendAllDevicesList(opensl_device);
             break;
+
         case CAPTURE_DEVICE_PROBE:
+            AppendAllDevicesList(opensl_device);
             break;
     }
 }
+
+static ALCbackend* ALCopenslBackendFactory_createBackend(ALCopenslBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
+{
+    if(type == ALCbackend_Playback)
+    {
+        ALCopenslPlayback *backend;
+        NEW_OBJ(backend, ALCopenslPlayback)(device);
+        if(!backend) return NULL;
+        return STATIC_CAST(ALCbackend, backend);
+    }
+    if(type == ALCbackend_Capture)
+    {
+        ALCopenslCapture *backend;
+        NEW_OBJ(backend, ALCopenslCapture)(device);
+        if(!backend) return NULL;
+        return STATIC_CAST(ALCbackend, backend);
+    }
+
+    return NULL;
+}
+
+DEFINE_ALCBACKENDFACTORY_VTABLE(ALCopenslBackendFactory);
+
+
+ALCbackendFactory *ALCopenslBackendFactory_getFactory(void)
+{
+    static ALCopenslBackendFactory factory = ALCOPENSLBACKENDFACTORY_INITIALIZER;
+    return STATIC_CAST(ALCbackendFactory, &factory);
+}

+ 171 - 123
libs/openal-soft/Alc/backends/oss.c

@@ -88,7 +88,9 @@ static struct oss_device oss_capture = {
 
 #ifdef ALC_OSS_COMPAT
 
-static void ALCossListPopulate(struct oss_device *UNUSED(playback), struct oss_device *UNUSED(capture))
+#define DSP_CAP_OUTPUT 0x00020000
+#define DSP_CAP_INPUT 0x00010000
+static void ALCossListPopulate(struct oss_device *UNUSED(devlist), int UNUSED(type_flag))
 {
 }
 
@@ -153,7 +155,7 @@ static void ALCossListAppend(struct oss_device *list, const char *handle, size_t
     TRACE("Got device \"%s\", \"%s\"\n", next->handle, next->path);
 }
 
-static void ALCossListPopulate(struct oss_device *playback, struct oss_device *capture)
+static void ALCossListPopulate(struct oss_device *devlist, int type_flag)
 {
     struct oss_sysinfo si;
     struct oss_audioinfo ai;
@@ -161,12 +163,12 @@ static void ALCossListPopulate(struct oss_device *playback, struct oss_device *c
 
     if((fd=open("/dev/mixer", O_RDONLY)) < 0)
     {
-        ERR("Could not open /dev/mixer\n");
+        TRACE("Could not open /dev/mixer: %s\n", strerror(errno));
         return;
     }
     if(ioctl(fd, SNDCTL_SYSINFO, &si) == -1)
     {
-        ERR("SNDCTL_SYSINFO failed: %s\n", strerror(errno));
+        TRACE("SNDCTL_SYSINFO failed: %s\n", strerror(errno));
         goto done;
     }
     for(i = 0;i < si.numaudios;i++)
@@ -193,10 +195,9 @@ static void ALCossListPopulate(struct oss_device *playback, struct oss_device *c
             len = strnlen(ai.name, sizeof(ai.name));
             handle = ai.name;
         }
-        if((ai.caps&DSP_CAP_INPUT) && capture != NULL)
-            ALCossListAppend(capture, handle, len, ai.devnode, strnlen(ai.devnode, sizeof(ai.devnode)));
-        if((ai.caps&DSP_CAP_OUTPUT) && playback != NULL)
-            ALCossListAppend(playback, handle, len, ai.devnode, strnlen(ai.devnode, sizeof(ai.devnode)));
+        if((ai.caps&type_flag))
+            ALCossListAppend(devlist, handle, len, ai.devnode,
+                             strnlen(ai.devnode, sizeof(ai.devnode)));
     }
 
 done:
@@ -242,7 +243,7 @@ typedef struct ALCplaybackOSS {
     ALubyte *mix_data;
     int data_size;
 
-    volatile int killNow;
+    ATOMIC(ALenum) killNow;
     althrd_t thread;
 } ALCplaybackOSS;
 
@@ -257,7 +258,7 @@ static ALCboolean ALCplaybackOSS_start(ALCplaybackOSS *self);
 static void ALCplaybackOSS_stop(ALCplaybackOSS *self);
 static DECLARE_FORWARD2(ALCplaybackOSS, ALCbackend, ALCenum, captureSamples, ALCvoid*, ALCuint)
 static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, ALCuint, availableSamples)
-static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, ALint64, getLatency)
+static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, ClockLatency, getClockLatency)
 static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, void, lock)
 static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, void, unlock)
 DECLARE_DEFAULT_ALLOCATORS(ALCplaybackOSS)
@@ -268,42 +269,64 @@ static int ALCplaybackOSS_mixerProc(void *ptr)
 {
     ALCplaybackOSS *self = (ALCplaybackOSS*)ptr;
     ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
-    ALint frameSize;
+    struct timeval timeout;
+    ALubyte *write_ptr;
+    ALint frame_size;
+    ALint to_write;
     ssize_t wrote;
+    fd_set wfds;
+    int sret;
 
     SetRTPriority();
     althrd_setname(althrd_current(), MIXER_THREAD_NAME);
 
-    frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
+    frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
 
-    while(!self->killNow && device->Connected)
+    ALCplaybackOSS_lock(self);
+    while(!ATOMIC_LOAD_SEQ(&self->killNow) && device->Connected)
     {
-        ALint len = self->data_size;
-        ALubyte *WritePtr = self->mix_data;
+        FD_ZERO(&wfds);
+        FD_SET(self->fd, &wfds);
+        timeout.tv_sec = 1;
+        timeout.tv_usec = 0;
+
+        ALCplaybackOSS_unlock(self);
+        sret = select(self->fd+1, NULL, &wfds, NULL, &timeout);
+        ALCplaybackOSS_lock(self);
+        if(sret < 0)
+        {
+            if(errno == EINTR)
+                continue;
+            ERR("select failed: %s\n", strerror(errno));
+            aluHandleDisconnect(device);
+            break;
+        }
+        else if(sret == 0)
+        {
+            WARN("select timeout\n");
+            continue;
+        }
 
-        aluMixData(device, WritePtr, len/frameSize);
-        while(len > 0 && !self->killNow)
+        write_ptr = self->mix_data;
+        to_write = self->data_size;
+        aluMixData(device, write_ptr, to_write/frame_size);
+        while(to_write > 0 && !ATOMIC_LOAD_SEQ(&self->killNow))
         {
-            wrote = write(self->fd, WritePtr, len);
+            wrote = write(self->fd, write_ptr, to_write);
             if(wrote < 0)
             {
-                if(errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR)
-                {
-                    ERR("write failed: %s\n", strerror(errno));
-                    ALCplaybackOSS_lock(self);
-                    aluHandleDisconnect(device);
-                    ALCplaybackOSS_unlock(self);
-                    break;
-                }
-
-                al_nssleep(1000000);
-                continue;
+                if(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
+                    continue;
+                ERR("write failed: %s\n", strerror(errno));
+                aluHandleDisconnect(device);
+                break;
             }
 
-            len -= wrote;
-            WritePtr += wrote;
+            to_write -= wrote;
+            write_ptr += wrote;
         }
     }
+    ALCplaybackOSS_unlock(self);
 
     return 0;
 }
@@ -313,6 +336,8 @@ static void ALCplaybackOSS_Construct(ALCplaybackOSS *self, ALCdevice *device)
 {
     ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
     SET_VTABLE2(ALCplaybackOSS, ALCbackend, self);
+
+    ATOMIC_INIT(&self->killNow, AL_FALSE);
 }
 
 static ALCenum ALCplaybackOSS_open(ALCplaybackOSS *self, const ALCchar *name)
@@ -320,22 +345,28 @@ static ALCenum ALCplaybackOSS_open(ALCplaybackOSS *self, const ALCchar *name)
     struct oss_device *dev = &oss_playback;
     ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
 
-    if(!name)
+    if(!name || strcmp(name, dev->handle) == 0)
         name = dev->handle;
     else
     {
-        while (dev != NULL)
+        if(!dev->next)
+        {
+            ALCossListPopulate(&oss_playback, DSP_CAP_OUTPUT);
+            dev = &oss_playback;
+        }
+        while(dev != NULL)
         {
             if (strcmp(dev->handle, name) == 0)
                 break;
             dev = dev->next;
         }
-        if (dev == NULL)
+        if(dev == NULL)
+        {
+            WARN("Could not find \"%s\" in device list\n", name);
             return ALC_INVALID_VALUE;
+        }
     }
 
-    self->killNow = 0;
-
     self->fd = open(dev->path, O_WRONLY);
     if(self->fd == -1)
     {
@@ -343,7 +374,7 @@ static ALCenum ALCplaybackOSS_open(ALCplaybackOSS *self, const ALCchar *name)
         return ALC_INVALID_VALUE;
     }
 
-    al_string_copy_cstr(&device->DeviceName, name);
+    alstr_copy_cstr(&device->DeviceName, name);
 
     return ALC_NO_ERROR;
 }
@@ -387,18 +418,11 @@ static ALCboolean ALCplaybackOSS_reset(ALCplaybackOSS *self)
     }
 
     periods = device->NumUpdates;
-    numChannels = ChannelsFromDevFmt(device->FmtChans);
-    frameSize = numChannels * BytesFromDevFmt(device->FmtType);
-
+    numChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
     ossSpeed = device->Frequency;
-    log2FragmentSize = log2i(device->UpdateSize * frameSize);
-
-    /* according to the OSS spec, 16 bytes are the minimum */
-    if (log2FragmentSize < 4)
-        log2FragmentSize = 4;
-    /* Subtract one period since the temp mixing buffer counts as one. Still
-     * need at least two on the card, though. */
-    if(periods > 2) periods--;
+    frameSize = numChannels * BytesFromDevFmt(device->FmtType);
+    /* According to the OSS spec, 16 bytes (log2(16)) is the minimum. */
+    log2FragmentSize = maxi(log2i(device->UpdateSize*frameSize), 4);
     numFragmentsLogSize = (periods << 16) | log2FragmentSize;
 
 #define CHECKERR(func) if((func) < 0) {                                       \
@@ -420,7 +444,7 @@ static ALCboolean ALCplaybackOSS_reset(ALCplaybackOSS *self)
     }
 #undef CHECKERR
 
-    if((int)ChannelsFromDevFmt(device->FmtChans) != numChannels)
+    if((int)ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder) != numChannels)
     {
         ERR("Failed to set %s, got %d channels instead\n", DevFmtChannelsString(device->FmtChans), numChannels);
         return ALC_FALSE;
@@ -436,7 +460,7 @@ static ALCboolean ALCplaybackOSS_reset(ALCplaybackOSS *self)
 
     device->Frequency = ossSpeed;
     device->UpdateSize = info.fragsize / frameSize;
-    device->NumUpdates = info.fragments + 1;
+    device->NumUpdates = info.fragments;
 
     SetDefaultChannelOrder(device);
 
@@ -447,10 +471,12 @@ static ALCboolean ALCplaybackOSS_start(ALCplaybackOSS *self)
 {
     ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
 
-    self->data_size = device->UpdateSize * FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
+    self->data_size = device->UpdateSize * FrameSizeFromDevFmt(
+        device->FmtChans, device->FmtType, device->AmbiOrder
+    );
     self->mix_data = calloc(1, self->data_size);
 
-    self->killNow = 0;
+    ATOMIC_STORE_SEQ(&self->killNow, AL_FALSE);
     if(althrd_create(&self->thread, ALCplaybackOSS_mixerProc, self) != althrd_success)
     {
         free(self->mix_data);
@@ -465,10 +491,8 @@ static void ALCplaybackOSS_stop(ALCplaybackOSS *self)
 {
     int res;
 
-    if(self->killNow)
+    if(ATOMIC_EXCHANGE_SEQ(&self->killNow, AL_TRUE))
         return;
-
-    self->killNow = 1;
     althrd_join(self->thread, &res);
 
     if(ioctl(self->fd, SNDCTL_DSP_RESET) != 0)
@@ -484,13 +508,9 @@ typedef struct ALCcaptureOSS {
 
     int fd;
 
-    ALubyte *read_data;
-    int data_size;
-
-    RingBuffer *ring;
-    int doCapture;
+    ll_ringbuffer_t *ring;
 
-    volatile int killNow;
+    ATOMIC(ALenum) killNow;
     althrd_t thread;
 } ALCcaptureOSS;
 
@@ -505,7 +525,7 @@ static ALCboolean ALCcaptureOSS_start(ALCcaptureOSS *self);
 static void ALCcaptureOSS_stop(ALCcaptureOSS *self);
 static ALCenum ALCcaptureOSS_captureSamples(ALCcaptureOSS *self, ALCvoid *buffer, ALCuint samples);
 static ALCuint ALCcaptureOSS_availableSamples(ALCcaptureOSS *self);
-static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, ALint64, getLatency)
+static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, ClockLatency, getClockLatency)
 static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, void, lock)
 static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, void, unlock)
 DECLARE_DEFAULT_ALLOCATORS(ALCcaptureOSS)
@@ -516,32 +536,55 @@ static int ALCcaptureOSS_recordProc(void *ptr)
 {
     ALCcaptureOSS *self = (ALCcaptureOSS*)ptr;
     ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
-    int frameSize;
-    int amt;
+    struct timeval timeout;
+    int frame_size;
+    fd_set rfds;
+    ssize_t amt;
+    int sret;
 
     SetRTPriority();
     althrd_setname(althrd_current(), RECORD_THREAD_NAME);
 
-    frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
+    frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
 
-    while(!self->killNow)
+    while(!ATOMIC_LOAD_SEQ(&self->killNow))
     {
-        amt = read(self->fd, self->read_data, self->data_size);
-        if(amt < 0)
+        ll_ringbuffer_data_t vec[2];
+
+        FD_ZERO(&rfds);
+        FD_SET(self->fd, &rfds);
+        timeout.tv_sec = 1;
+        timeout.tv_usec = 0;
+
+        sret = select(self->fd+1, &rfds, NULL, NULL, &timeout);
+        if(sret < 0)
         {
-            ERR("read failed: %s\n", strerror(errno));
-            ALCcaptureOSS_lock(self);
+            if(errno == EINTR)
+                continue;
+            ERR("select failed: %s\n", strerror(errno));
             aluHandleDisconnect(device);
-            ALCcaptureOSS_unlock(self);
             break;
         }
-        if(amt == 0)
+        else if(sret == 0)
         {
-            al_nssleep(1000000);
+            WARN("select timeout\n");
             continue;
         }
-        if(self->doCapture)
-            WriteRingBuffer(self->ring, self->read_data, amt/frameSize);
+
+        ll_ringbuffer_get_write_vector(self->ring, vec);
+        if(vec[0].len > 0)
+        {
+            amt = read(self->fd, vec[0].buf, vec[0].len*frame_size);
+            if(amt < 0)
+            {
+                ERR("read failed: %s\n", strerror(errno));
+                ALCcaptureOSS_lock(self);
+                aluHandleDisconnect(device);
+                ALCcaptureOSS_unlock(self);
+                break;
+            }
+            ll_ringbuffer_write_advance(self->ring, amt/frame_size);
+        }
     }
 
     return 0;
@@ -552,6 +595,8 @@ static void ALCcaptureOSS_Construct(ALCcaptureOSS *self, ALCdevice *device)
 {
     ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
     SET_VTABLE2(ALCcaptureOSS, ALCbackend, self);
+
+    ATOMIC_INIT(&self->killNow, AL_FALSE);
 }
 
 static ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name)
@@ -568,18 +613,26 @@ static ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name)
     int ossSpeed;
     char *err;
 
-    if(!name)
+    if(!name || strcmp(name, dev->handle) == 0)
         name = dev->handle;
     else
     {
-        while (dev != NULL)
+        if(!dev->next)
+        {
+            ALCossListPopulate(&oss_capture, DSP_CAP_INPUT);
+            dev = &oss_capture;
+        }
+        while(dev != NULL)
         {
             if (strcmp(dev->handle, name) == 0)
                 break;
             dev = dev->next;
         }
-        if (dev == NULL)
+        if(dev == NULL)
+        {
+            WARN("Could not find \"%s\" in device list\n", name);
             return ALC_INVALID_VALUE;
+        }
     }
 
     self->fd = open(dev->path, O_RDONLY);
@@ -609,7 +662,7 @@ static ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name)
     }
 
     periods = 4;
-    numChannels = ChannelsFromDevFmt(device->FmtChans);
+    numChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
     frameSize = numChannels * BytesFromDevFmt(device->FmtType);
     ossSpeed = device->Frequency;
     log2FragmentSize = log2i(device->UpdateSize * device->NumUpdates *
@@ -639,7 +692,7 @@ static ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name)
     }
 #undef CHECKERR
 
-    if((int)ChannelsFromDevFmt(device->FmtChans) != numChannels)
+    if((int)ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder) != numChannels)
     {
         ERR("Failed to set %s, got %d channels instead\n", DevFmtChannelsString(device->FmtChans), numChannels);
         close(self->fd);
@@ -657,7 +710,7 @@ static ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name)
         return ALC_INVALID_VALUE;
     }
 
-    self->ring = CreateRingBuffer(frameSize, device->UpdateSize * device->NumUpdates);
+    self->ring = ll_ringbuffer_create(device->UpdateSize*device->NumUpdates + 1, frameSize);
     if(!self->ring)
     {
         ERR("Ring buffer create failed\n");
@@ -666,60 +719,50 @@ static ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name)
         return ALC_OUT_OF_MEMORY;
     }
 
-    self->data_size = info.fragsize;
-    self->read_data = calloc(1, self->data_size);
-
-    self->killNow = 0;
-    if(althrd_create(&self->thread, ALCcaptureOSS_recordProc, self) != althrd_success)
-    {
-        device->ExtraData = NULL;
-        close(self->fd);
-        self->fd = -1;
-        return ALC_OUT_OF_MEMORY;
-    }
-
-    al_string_copy_cstr(&device->DeviceName, name);
+    alstr_copy_cstr(&device->DeviceName, name);
 
     return ALC_NO_ERROR;
 }
 
 static void ALCcaptureOSS_close(ALCcaptureOSS *self)
 {
-    int res;
-
-    self->killNow = 1;
-    althrd_join(self->thread, &res);
-
     close(self->fd);
     self->fd = -1;
 
-    DestroyRingBuffer(self->ring);
+    ll_ringbuffer_free(self->ring);
     self->ring = NULL;
-
-    free(self->read_data);
-    self->read_data = NULL;
 }
 
 static ALCboolean ALCcaptureOSS_start(ALCcaptureOSS *self)
 {
-    self->doCapture = 1;
+    ATOMIC_STORE_SEQ(&self->killNow, AL_FALSE);
+    if(althrd_create(&self->thread, ALCcaptureOSS_recordProc, self) != althrd_success)
+        return ALC_FALSE;
     return ALC_TRUE;
 }
 
 static void ALCcaptureOSS_stop(ALCcaptureOSS *self)
 {
-    self->doCapture = 0;
+    int res;
+
+    if(ATOMIC_EXCHANGE_SEQ(&self->killNow, AL_TRUE))
+        return;
+
+    althrd_join(self->thread, &res);
+
+    if(ioctl(self->fd, SNDCTL_DSP_RESET) != 0)
+        ERR("Error resetting device: %s\n", strerror(errno));
 }
 
 static ALCenum ALCcaptureOSS_captureSamples(ALCcaptureOSS *self, ALCvoid *buffer, ALCuint samples)
 {
-    ReadRingBuffer(self->ring, buffer, samples);
+    ll_ringbuffer_read(self->ring, buffer, samples);
     return ALC_NO_ERROR;
 }
 
 static ALCuint ALCcaptureOSS_availableSamples(ALCcaptureOSS *self)
 {
-    return RingBufferSize(self->ring);
+    return ll_ringbuffer_read_space(self->ring);
 }
 
 
@@ -769,33 +812,38 @@ ALCboolean ALCossBackendFactory_querySupport(ALCossBackendFactory* UNUSED(self),
 
 void ALCossBackendFactory_probe(ALCossBackendFactory* UNUSED(self), enum DevProbe type)
 {
+    struct oss_device *cur;
     switch(type)
     {
         case ALL_DEVICE_PROBE:
-        {
-            struct oss_device *cur = &oss_playback;
-            ALCossListFree(cur);
-            ALCossListPopulate(cur, NULL);
-            while (cur != NULL)
+            ALCossListFree(&oss_playback);
+            ALCossListPopulate(&oss_playback, DSP_CAP_OUTPUT);
+            cur = &oss_playback;
+            while(cur != NULL)
             {
-                AppendAllDevicesList(cur->handle);
+#ifdef HAVE_STAT
+                struct stat buf;
+                if(stat(cur->path, &buf) == 0)
+#endif
+                    AppendAllDevicesList(cur->handle);
                 cur = cur->next;
             }
-        }
-        break;
+            break;
 
         case CAPTURE_DEVICE_PROBE:
-        {
-            struct oss_device *cur = &oss_capture;
-            ALCossListFree(cur);
-            ALCossListPopulate(NULL, cur);
-            while (cur != NULL)
+            ALCossListFree(&oss_capture);
+            ALCossListPopulate(&oss_capture, DSP_CAP_INPUT);
+            cur = &oss_capture;
+            while(cur != NULL)
             {
-                AppendCaptureDeviceList(cur->handle);
+#ifdef HAVE_STAT
+                struct stat buf;
+                if(stat(cur->path, &buf) == 0)
+#endif
+                    AppendCaptureDeviceList(cur->handle);
                 cur = cur->next;
             }
-        }
-        break;
+            break;
     }
 }
 

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

@@ -145,7 +145,7 @@ static ALCboolean ALCportPlayback_start(ALCportPlayback *self);
 static void ALCportPlayback_stop(ALCportPlayback *self);
 static DECLARE_FORWARD2(ALCportPlayback, ALCbackend, ALCenum, captureSamples, ALCvoid*, ALCuint)
 static DECLARE_FORWARD(ALCportPlayback, ALCbackend, ALCuint, availableSamples)
-static DECLARE_FORWARD(ALCportPlayback, ALCbackend, ALint64, getLatency)
+static DECLARE_FORWARD(ALCportPlayback, ALCbackend, ClockLatency, getClockLatency)
 static DECLARE_FORWARD(ALCportPlayback, ALCbackend, void, lock)
 static DECLARE_FORWARD(ALCportPlayback, ALCbackend, void, unlock)
 DECLARE_DEFAULT_ALLOCATORS(ALCportPlayback)
@@ -177,7 +177,9 @@ static int ALCportPlayback_WriteCallback(const void *UNUSED(inputBuffer), void *
 {
     ALCportPlayback *self = userData;
 
+    ALCportPlayback_lock(self);
     aluMixData(STATIC_CAST(ALCbackend, self)->mDevice, outputBuffer, framesPerBuffer);
+    ALCportPlayback_unlock(self);
     return 0;
 }
 
@@ -243,7 +245,7 @@ retry_open:
         return ALC_INVALID_VALUE;
     }
 
-    al_string_copy_cstr(&device->DeviceName, name);
+    alstr_copy_cstr(&device->DeviceName, name);
 
     return ALC_NO_ERROR;
 
@@ -340,7 +342,7 @@ static ALCboolean ALCportCapture_start(ALCportCapture *self);
 static void ALCportCapture_stop(ALCportCapture *self);
 static ALCenum ALCportCapture_captureSamples(ALCportCapture *self, ALCvoid *buffer, ALCuint samples);
 static ALCuint ALCportCapture_availableSamples(ALCportCapture *self);
-static DECLARE_FORWARD(ALCportCapture, ALCbackend, ALint64, getLatency)
+static DECLARE_FORWARD(ALCportCapture, ALCbackend, ClockLatency, getClockLatency)
 static DECLARE_FORWARD(ALCportCapture, ALCbackend, void, lock)
 static DECLARE_FORWARD(ALCportCapture, ALCbackend, void, unlock)
 DECLARE_DEFAULT_ALLOCATORS(ALCportCapture)
@@ -397,7 +399,7 @@ static ALCenum ALCportCapture_open(ALCportCapture *self, const ALCchar *name)
 
     samples = device->UpdateSize * device->NumUpdates;
     samples = maxu(samples, 100 * device->Frequency / 1000);
-    frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
+    frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
 
     self->ring = ll_ringbuffer_create(samples, frame_size);
     if(self->ring == NULL) return ALC_INVALID_VALUE;
@@ -431,7 +433,7 @@ static ALCenum ALCportCapture_open(ALCportCapture *self, const ALCchar *name)
             ERR("%s samples not supported\n", DevFmtTypeString(device->FmtType));
             return ALC_INVALID_VALUE;
     }
-    self->params.channelCount = ChannelsFromDevFmt(device->FmtChans);
+    self->params.channelCount = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
 
     err = Pa_OpenStream(&self->stream, &self->params, NULL,
         device->Frequency, paFramesPerBufferUnspecified, paNoFlag,
@@ -443,7 +445,7 @@ static ALCenum ALCportCapture_open(ALCportCapture *self, const ALCchar *name)
         return ALC_INVALID_VALUE;
     }
 
-    al_string_copy_cstr(&device->DeviceName, name);
+    alstr_copy_cstr(&device->DeviceName, name);
 
     return ALC_NO_ERROR;
 }

+ 207 - 92
libs/openal-soft/Alc/backends/pulseaudio.c

@@ -182,6 +182,8 @@ static ALCboolean pulse_load(void)
 #ifdef HAVE_DYNLOAD
     if(!pa_handle)
     {
+        al_string missing_funcs = AL_STRING_INIT_STATIC();
+
 #ifdef _WIN32
 #define PALIB "libpulse-0.dll"
 #elif defined(__APPLE__) && defined(__MACH__)
@@ -191,12 +193,16 @@ static ALCboolean pulse_load(void)
 #endif
         pa_handle = LoadLib(PALIB);
         if(!pa_handle)
+        {
+            WARN("Failed to load %s\n", PALIB);
             return ALC_FALSE;
+        }
 
 #define LOAD_FUNC(x) do {                                                     \
     p##x = GetSymbol(pa_handle, #x);                                          \
     if(!(p##x)) {                                                             \
         ret = ALC_FALSE;                                                      \
+        alstr_append_cstr(&missing_funcs, "\n" #x);                           \
     }                                                                         \
 } while(0)
         LOAD_FUNC(pa_context_unref);
@@ -270,9 +276,11 @@ static ALCboolean pulse_load(void)
 
         if(ret == ALC_FALSE)
         {
+            WARN("Missing expected functions:%s\n", alstr_get_cstr(missing_funcs));
             CloseLib(pa_handle);
             pa_handle = NULL;
         }
+        alstr_reset(&missing_funcs);
     }
 #endif /* HAVE_DYNLOAD */
     return ret;
@@ -443,7 +451,7 @@ static void clear_devlist(vector_DevMap *list)
 #define DEINIT_STRS(i)  (AL_STRING_DEINIT((i)->name),AL_STRING_DEINIT((i)->device_name))
     VECTOR_FOR_EACH(DevMap, *list, DEINIT_STRS);
 #undef DEINIT_STRS
-    VECTOR_RESIZE(*list, 0);
+    VECTOR_RESIZE(*list, 0, 0);
 }
 
 
@@ -489,7 +497,7 @@ static ALCboolean ALCpulsePlayback_start(ALCpulsePlayback *self);
 static void ALCpulsePlayback_stop(ALCpulsePlayback *self);
 static DECLARE_FORWARD2(ALCpulsePlayback, ALCbackend, ALCenum, captureSamples, ALCvoid*, ALCuint)
 static DECLARE_FORWARD(ALCpulsePlayback, ALCbackend, ALCuint, availableSamples)
-static ALint64 ALCpulsePlayback_getLatency(ALCpulsePlayback *self);
+static ClockLatency ALCpulsePlayback_getClockLatency(ALCpulsePlayback *self);
 static void ALCpulsePlayback_lock(ALCpulsePlayback *self);
 static void ALCpulsePlayback_unlock(ALCpulsePlayback *self);
 DECLARE_DEFAULT_ALLOCATORS(ALCpulsePlayback)
@@ -525,35 +533,35 @@ static void ALCpulsePlayback_deviceCallback(pa_context *UNUSED(context), const p
         return;
     }
 
-#define MATCH_INFO_NAME(iter) (al_string_cmp_cstr((iter)->device_name, info->name) == 0)
+#define MATCH_INFO_NAME(iter) (alstr_cmp_cstr((iter)->device_name, info->name) == 0)
     VECTOR_FIND_IF(iter, const DevMap, PlaybackDevices, MATCH_INFO_NAME);
-    if(iter != VECTOR_ITER_END(PlaybackDevices)) return;
+    if(iter != VECTOR_END(PlaybackDevices)) return;
 #undef MATCH_INFO_NAME
 
     AL_STRING_INIT(entry.name);
     AL_STRING_INIT(entry.device_name);
 
-    al_string_copy_cstr(&entry.device_name, info->name);
+    alstr_copy_cstr(&entry.device_name, info->name);
 
     count = 0;
     while(1)
     {
-        al_string_copy_cstr(&entry.name, info->description);
+        alstr_copy_cstr(&entry.name, info->description);
         if(count != 0)
         {
             char str[64];
             snprintf(str, sizeof(str), " #%d", count+1);
-            al_string_append_cstr(&entry.name, str);
+            alstr_append_cstr(&entry.name, str);
         }
 
-#define MATCH_ENTRY(i) (al_string_cmp(entry.name, (i)->name) == 0)
+#define MATCH_ENTRY(i) (alstr_cmp(entry.name, (i)->name) == 0)
         VECTOR_FIND_IF(iter, const DevMap, PlaybackDevices, MATCH_ENTRY);
-        if(iter == VECTOR_ITER_END(PlaybackDevices)) break;
+        if(iter == VECTOR_END(PlaybackDevices)) break;
 #undef MATCH_ENTRY
         count++;
     }
 
-    TRACE("Got device \"%s\", \"%s\"\n", al_string_get_cstr(entry.name), al_string_get_cstr(entry.device_name));
+    TRACE("Got device \"%s\", \"%s\"\n", alstr_get_cstr(entry.name), alstr_get_cstr(entry.device_name));
 
     VECTOR_PUSH_BACK(PlaybackDevices, entry);
 }
@@ -618,6 +626,11 @@ static void ALCpulsePlayback_bufferAttrCallback(pa_stream *stream, void *pdata)
 
     self->attr = *pa_stream_get_buffer_attr(stream);
     TRACE("minreq=%d, tlength=%d, prebuf=%d\n", self->attr.minreq, self->attr.tlength, self->attr.prebuf);
+    /* FIXME: Update the device's UpdateSize (and/or NumUpdates) using the new
+     * buffer attributes? Changing UpdateSize will change the ALC_REFRESH
+     * property, which probably shouldn't change between device resets. But
+     * leaving it alone means ALC_REFRESH will be off.
+     */
 }
 
 static void ALCpulsePlayback_contextStateCallback(pa_context *context, void *pdata)
@@ -729,7 +742,7 @@ static void ALCpulsePlayback_sinkNameCallback(pa_context *UNUSED(context), const
         return;
     }
 
-    al_string_copy_cstr(&device->DeviceName, info->description);
+    alstr_copy_cstr(&device->DeviceName, info->description);
 }
 
 
@@ -737,9 +750,9 @@ static void ALCpulsePlayback_streamMovedCallback(pa_stream *stream, void *pdata)
 {
     ALCpulsePlayback *self = pdata;
 
-    al_string_copy_cstr(&self->device_name, pa_stream_get_device_name(stream));
+    alstr_copy_cstr(&self->device_name, pa_stream_get_device_name(stream));
 
-    TRACE("Stream moved to %s\n", al_string_get_cstr(self->device_name));
+    TRACE("Stream moved to %s\n", alstr_get_cstr(self->device_name));
 }
 
 
@@ -751,6 +764,13 @@ static pa_stream *ALCpulsePlayback_connectStream(const char *device_name,
     pa_stream_state_t state;
     pa_stream *stream;
 
+    if(!device_name)
+    {
+        device_name = getenv("ALSOFT_PULSE_DEFAULT");
+        if(device_name && !device_name[0])
+            device_name = NULL;
+    }
+
     stream = pa_stream_new_with_proplist(context, "Playback Stream", spec, chanmap, prop_filter);
     if(!stream)
     {
@@ -789,7 +809,6 @@ static int ALCpulsePlayback_mixerProc(void *ptr)
     ALCpulsePlayback *self = ptr;
     ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
     ALuint buffer_size;
-    ALint update_size;
     size_t frame_size;
     ssize_t len;
 
@@ -798,18 +817,31 @@ static int ALCpulsePlayback_mixerProc(void *ptr)
 
     pa_threaded_mainloop_lock(self->loop);
     frame_size = pa_frame_size(&self->spec);
-    update_size = device->UpdateSize * frame_size;
-
-    /* Sanitize buffer metrics, in case we actually have less than what we
-     * asked for. */
-    buffer_size = minu(update_size*device->NumUpdates, self->attr.tlength);
-    update_size = minu(update_size, buffer_size/2);
-    do {
-        len = pa_stream_writable_size(self->stream) - self->attr.tlength +
-              buffer_size;
-        if(len < update_size)
+
+    while(!self->killNow && device->Connected)
+    {
+        len = pa_stream_writable_size(self->stream);
+        if(len < 0)
         {
-            if(pa_stream_is_corked(self->stream) == 1)
+            ERR("Failed to get writable size: %ld", (long)len);
+            aluHandleDisconnect(device);
+            break;
+        }
+
+        /* Make sure we're going to write at least 2 'periods' (minreqs), in
+         * case the server increased it since starting playback. Also round up
+         * the number of writable periods if it's not an integer count.
+         */
+        buffer_size = maxu((self->attr.tlength + self->attr.minreq/2) / self->attr.minreq, 2) *
+                      self->attr.minreq;
+
+        /* NOTE: This assumes pa_stream_writable_size returns between 0 and
+         * tlength, else there will be more latency than intended.
+         */
+        len = mini(len - (ssize_t)self->attr.tlength, 0) + buffer_size;
+        if(len < (int32_t)self->attr.minreq)
+        {
+            if(pa_stream_is_corked(self->stream))
             {
                 pa_operation *o;
                 o = pa_stream_cork(self->stream, 0, NULL, NULL);
@@ -818,11 +850,12 @@ static int ALCpulsePlayback_mixerProc(void *ptr)
             pa_threaded_mainloop_wait(self->loop);
             continue;
         }
-        len -= len%update_size;
+        len -= len%self->attr.minreq;
 
         while(len > 0)
         {
             size_t newlen = len;
+            int ret;
             void *buf;
             pa_free_cb_t free_func = NULL;
 
@@ -834,10 +867,15 @@ static int ALCpulsePlayback_mixerProc(void *ptr)
 
             aluMixData(device, buf, newlen/frame_size);
 
-            pa_stream_write(self->stream, buf, newlen, free_func, 0, PA_SEEK_RELATIVE);
+            ret = pa_stream_write(self->stream, buf, newlen, free_func, 0, PA_SEEK_RELATIVE);
+            if(ret != PA_OK)
+            {
+                ERR("Failed to write to stream: %d, %s\n", ret, pa_strerror(ret));
+                break;
+            }
             len -= newlen;
         }
-    } while(!self->killNow && device->Connected);
+    }
     pa_threaded_mainloop_unlock(self->loop);
 
     return 0;
@@ -858,12 +896,12 @@ static ALCenum ALCpulsePlayback_open(ALCpulsePlayback *self, const ALCchar *name
         if(VECTOR_SIZE(PlaybackDevices) == 0)
             ALCpulsePlayback_probeDevices();
 
-#define MATCH_NAME(iter) (al_string_cmp_cstr((iter)->name, name) == 0)
+#define MATCH_NAME(iter) (alstr_cmp_cstr((iter)->name, name) == 0)
         VECTOR_FIND_IF(iter, const DevMap, PlaybackDevices, MATCH_NAME);
 #undef MATCH_NAME
-        if(iter == VECTOR_ITER_END(PlaybackDevices))
+        if(iter == VECTOR_END(PlaybackDevices))
             return ALC_INVALID_VALUE;
-        pulse_name = al_string_get_cstr(iter->device_name);
+        pulse_name = alstr_get_cstr(iter->device_name);
         dev_name = iter->name;
     }
 
@@ -894,11 +932,11 @@ static ALCenum ALCpulsePlayback_open(ALCpulsePlayback *self, const ALCchar *name
     }
     pa_stream_set_moved_callback(self->stream, ALCpulsePlayback_streamMovedCallback, self);
 
-    al_string_copy_cstr(&self->device_name, pa_stream_get_device_name(self->stream));
-    if(al_string_empty(dev_name))
+    alstr_copy_cstr(&self->device_name, pa_stream_get_device_name(self->stream));
+    if(alstr_empty(dev_name))
     {
         pa_operation *o = pa_context_get_sink_info_by_name(
-            self->context, al_string_get_cstr(self->device_name),
+            self->context, alstr_get_cstr(self->device_name),
             ALCpulsePlayback_sinkNameCallback, self
         );
         wait_for_operation(o, self->loop);
@@ -906,7 +944,7 @@ static ALCenum ALCpulsePlayback_open(ALCpulsePlayback *self, const ALCchar *name
     else
     {
         ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
-        al_string_copy(&device->DeviceName, dev_name);
+        alstr_copy(&device->DeviceName, dev_name);
     }
 
     pa_threaded_mainloop_unlock(self->loop);
@@ -921,7 +959,7 @@ static void ALCpulsePlayback_close(ALCpulsePlayback *self)
     self->context = NULL;
     self->stream = NULL;
 
-    al_string_clear(&self->device_name);
+    alstr_clear(&self->device_name);
 }
 
 static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self)
@@ -931,7 +969,6 @@ static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self)
     const char *mapname = NULL;
     pa_channel_map chanmap;
     pa_operation *o;
-    ALuint len;
 
     pa_threaded_mainloop_lock(self->loop);
 
@@ -946,11 +983,11 @@ static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self)
         self->stream = NULL;
     }
 
-    o = pa_context_get_sink_info_by_name(self->context, al_string_get_cstr(self->device_name),
+    o = pa_context_get_sink_info_by_name(self->context, alstr_get_cstr(self->device_name),
                                          ALCpulsePlayback_sinkInfoCallback, self);
     wait_for_operation(o, self->loop);
 
-    if(GetConfigValueBool(al_string_get_cstr(device->DeviceName), "pulse", "fix-rate", 0) ||
+    if(GetConfigValueBool(alstr_get_cstr(device->DeviceName), "pulse", "fix-rate", 0) ||
        !(device->Flags&DEVICE_FREQUENCY_REQUEST))
         flags |= PA_STREAM_FIX_RATE;
     flags |= PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE;
@@ -984,7 +1021,7 @@ static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self)
             break;
     }
     self->spec.rate = device->Frequency;
-    self->spec.channels = ChannelsFromDevFmt(device->FmtChans);
+    self->spec.channels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
 
     if(pa_sample_spec_valid(&self->spec) == 0)
     {
@@ -998,7 +1035,7 @@ static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self)
         case DevFmtMono:
             mapname = "mono";
             break;
-        case DevFmtBFormat3D:
+        case DevFmtAmbi3D:
             device->FmtChans = DevFmtStereo;
             /*fall-through*/
         case DevFmtStereo:
@@ -1034,9 +1071,9 @@ static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self)
     self->attr.tlength = self->attr.minreq * maxu(device->NumUpdates, 2);
     self->attr.maxlength = -1;
 
-    self->stream = ALCpulsePlayback_connectStream(al_string_get_cstr(self->device_name),
-                                                  self->loop, self->context, flags,
-                                                  &self->attr, &self->spec, &chanmap);
+    self->stream = ALCpulsePlayback_connectStream(alstr_get_cstr(self->device_name),
+        self->loop, self->context, flags, &self->attr, &self->spec, &chanmap
+    );
     if(!self->stream)
     {
         pa_threaded_mainloop_unlock(self->loop);
@@ -1051,10 +1088,12 @@ static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self)
     {
         /* Server updated our playback rate, so modify the buffer attribs
          * accordingly. */
-        device->NumUpdates = (ALuint)((ALdouble)device->NumUpdates / device->Frequency *
-                                      self->spec.rate + 0.5);
+        device->NumUpdates = (ALuint)clampd(
+            (ALdouble)device->NumUpdates/device->Frequency*self->spec.rate + 0.5, 2.0, 16.0
+        );
+
         self->attr.minreq  = device->UpdateSize * pa_frame_size(&self->spec);
-        self->attr.tlength = self->attr.minreq * clampu(device->NumUpdates, 2, 16);
+        self->attr.tlength = self->attr.minreq * device->NumUpdates;
         self->attr.maxlength = -1;
         self->attr.prebuf  = 0;
 
@@ -1068,10 +1107,30 @@ static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self)
     pa_stream_set_buffer_attr_callback(self->stream, ALCpulsePlayback_bufferAttrCallback, self);
     ALCpulsePlayback_bufferAttrCallback(self->stream, self);
 
-    len = self->attr.minreq / pa_frame_size(&self->spec);
-    device->NumUpdates = (ALuint)((ALdouble)device->NumUpdates/len*device->UpdateSize + 0.5);
-    device->NumUpdates = clampu(device->NumUpdates, 2, 16);
-    device->UpdateSize = len;
+    device->NumUpdates = (ALuint)clampu64(
+        (self->attr.tlength + self->attr.minreq/2) / self->attr.minreq, 2, 16
+    );
+    device->UpdateSize = self->attr.minreq / pa_frame_size(&self->spec);
+
+    /* HACK: prebuf should be 0 as that's what we set it to. However on some
+     * systems it comes back as non-0, so we have to make sure the device will
+     * write enough audio to start playback. The lack of manual start control
+     * may have unintended consequences, but it's better than not starting at
+     * all.
+     */
+    if(self->attr.prebuf != 0)
+    {
+        ALuint len = self->attr.prebuf / pa_frame_size(&self->spec);
+        if(len <= device->UpdateSize*device->NumUpdates)
+            ERR("Non-0 prebuf, %u samples (%u bytes), device has %u samples\n",
+                len, self->attr.prebuf, device->UpdateSize*device->NumUpdates);
+        else
+        {
+            ERR("Large prebuf, %u samples (%u bytes), increasing device from %u samples",
+                len, self->attr.prebuf, device->UpdateSize*device->NumUpdates);
+            device->NumUpdates = (len+device->UpdateSize-1) / device->UpdateSize;
+        }
+    }
 
     pa_threaded_mainloop_unlock(self->loop);
     return ALC_TRUE;
@@ -1113,11 +1172,14 @@ static void ALCpulsePlayback_stop(ALCpulsePlayback *self)
 }
 
 
-static ALint64 ALCpulsePlayback_getLatency(ALCpulsePlayback *self)
+static ClockLatency ALCpulsePlayback_getClockLatency(ALCpulsePlayback *self)
 {
     pa_usec_t latency = 0;
+    ClockLatency ret;
     int neg, err;
 
+    pa_threaded_mainloop_lock(self->loop);
+    ret.ClockTime = GetDeviceClockTime(STATIC_CAST(ALCbackend,self)->mDevice);
     if((err=pa_stream_get_latency(self->stream, &latency, &neg)) != 0)
     {
         /* FIXME: if err = -PA_ERR_NODATA, it means we were called too soon
@@ -1126,11 +1188,14 @@ static ALint64 ALCpulsePlayback_getLatency(ALCpulsePlayback *self)
          * dummy value? Either way, it shouldn't be 0. */
         if(err != -PA_ERR_NODATA)
             ERR("Failed to get stream latency: 0x%x\n", err);
-        return 0;
+        latency = 0;
+        neg = 0;
     }
-
     if(neg) latency = 0;
-    return (ALint64)minu64(latency, U64(0x7fffffffffffffff)/1000) * 1000;
+    ret.Latency = minu64(latency, U64(0xffffffffffffffff)/1000) * 1000;
+    pa_threaded_mainloop_unlock(self->loop);
+
+    return ret;
 }
 
 
@@ -1186,7 +1251,7 @@ static ALCboolean ALCpulseCapture_start(ALCpulseCapture *self);
 static void ALCpulseCapture_stop(ALCpulseCapture *self);
 static ALCenum ALCpulseCapture_captureSamples(ALCpulseCapture *self, ALCvoid *buffer, ALCuint samples);
 static ALCuint ALCpulseCapture_availableSamples(ALCpulseCapture *self);
-static ALint64 ALCpulseCapture_getLatency(ALCpulseCapture *self);
+static ClockLatency ALCpulseCapture_getClockLatency(ALCpulseCapture *self);
 static void ALCpulseCapture_lock(ALCpulseCapture *self);
 static void ALCpulseCapture_unlock(ALCpulseCapture *self);
 DECLARE_DEFAULT_ALLOCATORS(ALCpulseCapture)
@@ -1222,35 +1287,35 @@ static void ALCpulseCapture_deviceCallback(pa_context *UNUSED(context), const pa
         return;
     }
 
-#define MATCH_INFO_NAME(iter) (al_string_cmp_cstr((iter)->device_name, info->name) == 0)
+#define MATCH_INFO_NAME(iter) (alstr_cmp_cstr((iter)->device_name, info->name) == 0)
     VECTOR_FIND_IF(iter, const DevMap, CaptureDevices, MATCH_INFO_NAME);
-    if(iter != VECTOR_ITER_END(CaptureDevices)) return;
+    if(iter != VECTOR_END(CaptureDevices)) return;
 #undef MATCH_INFO_NAME
 
     AL_STRING_INIT(entry.name);
     AL_STRING_INIT(entry.device_name);
 
-    al_string_copy_cstr(&entry.device_name, info->name);
+    alstr_copy_cstr(&entry.device_name, info->name);
 
     count = 0;
     while(1)
     {
-        al_string_copy_cstr(&entry.name, info->description);
+        alstr_copy_cstr(&entry.name, info->description);
         if(count != 0)
         {
             char str[64];
             snprintf(str, sizeof(str), " #%d", count+1);
-            al_string_append_cstr(&entry.name, str);
+            alstr_append_cstr(&entry.name, str);
         }
 
-#define MATCH_ENTRY(i) (al_string_cmp(entry.name, (i)->name) == 0)
+#define MATCH_ENTRY(i) (alstr_cmp(entry.name, (i)->name) == 0)
         VECTOR_FIND_IF(iter, const DevMap, CaptureDevices, MATCH_ENTRY);
-        if(iter == VECTOR_ITER_END(CaptureDevices)) break;
+        if(iter == VECTOR_END(CaptureDevices)) break;
 #undef MATCH_ENTRY
         count++;
     }
 
-    TRACE("Got device \"%s\", \"%s\"\n", al_string_get_cstr(entry.name), al_string_get_cstr(entry.device_name));
+    TRACE("Got device \"%s\", \"%s\"\n", alstr_get_cstr(entry.name), alstr_get_cstr(entry.device_name));
 
     VECTOR_PUSH_BACK(CaptureDevices, entry);
 }
@@ -1343,7 +1408,7 @@ static void ALCpulseCapture_sourceNameCallback(pa_context *UNUSED(context), cons
         return;
     }
 
-    al_string_copy_cstr(&device->DeviceName, info->description);
+    alstr_copy_cstr(&device->DeviceName, info->description);
 }
 
 
@@ -1351,9 +1416,9 @@ static void ALCpulseCapture_streamMovedCallback(pa_stream *stream, void *pdata)
 {
     ALCpulseCapture *self = pdata;
 
-    al_string_copy_cstr(&self->device_name, pa_stream_get_device_name(stream));
+    alstr_copy_cstr(&self->device_name, pa_stream_get_device_name(stream));
 
-    TRACE("Stream moved to %s\n", al_string_get_cstr(self->device_name));
+    TRACE("Stream moved to %s\n", alstr_get_cstr(self->device_name));
 }
 
 
@@ -1403,6 +1468,7 @@ static ALCenum ALCpulseCapture_open(ALCpulseCapture *self, const ALCchar *name)
     ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
     const char *pulse_name = NULL;
     pa_stream_flags_t flags = 0;
+    const char *mapname = NULL;
     pa_channel_map chanmap;
     ALuint samples;
 
@@ -1413,13 +1479,13 @@ static ALCenum ALCpulseCapture_open(ALCpulseCapture *self, const ALCchar *name)
         if(VECTOR_SIZE(CaptureDevices) == 0)
             ALCpulseCapture_probeDevices();
 
-#define MATCH_NAME(iter) (al_string_cmp_cstr((iter)->name, name) == 0)
+#define MATCH_NAME(iter) (alstr_cmp_cstr((iter)->name, name) == 0)
         VECTOR_FIND_IF(iter, const DevMap, CaptureDevices, MATCH_NAME);
 #undef MATCH_NAME
-        if(iter == VECTOR_ITER_END(CaptureDevices))
+        if(iter == VECTOR_END(CaptureDevices))
             return ALC_INVALID_VALUE;
-        pulse_name = al_string_get_cstr(iter->device_name);
-        al_string_copy(&device->DeviceName, iter->name);
+        pulse_name = alstr_get_cstr(iter->device_name);
+        alstr_copy(&device->DeviceName, iter->name);
     }
 
     if(!pulse_open(&self->loop, &self->context, ALCpulseCapture_contextStateCallback, self))
@@ -1427,9 +1493,6 @@ static ALCenum ALCpulseCapture_open(ALCpulseCapture *self, const ALCchar *name)
 
     pa_threaded_mainloop_lock(self->loop);
 
-    self->spec.rate = device->Frequency;
-    self->spec.channels = ChannelsFromDevFmt(device->FmtChans);
-
     switch(device->FmtType)
     {
         case DevFmtUByte:
@@ -1452,6 +1515,44 @@ static ALCenum ALCpulseCapture_open(ALCpulseCapture *self, const ALCchar *name)
             goto fail;
     }
 
+    switch(device->FmtChans)
+    {
+        case DevFmtMono:
+            mapname = "mono";
+            break;
+        case DevFmtStereo:
+            mapname = "front-left,front-right";
+            break;
+        case DevFmtQuad:
+            mapname = "front-left,front-right,rear-left,rear-right";
+            break;
+        case DevFmtX51:
+            mapname = "front-left,front-right,front-center,lfe,side-left,side-right";
+            break;
+        case DevFmtX51Rear:
+            mapname = "front-left,front-right,front-center,lfe,rear-left,rear-right";
+            break;
+        case DevFmtX61:
+            mapname = "front-left,front-right,front-center,lfe,rear-center,side-left,side-right";
+            break;
+        case DevFmtX71:
+            mapname = "front-left,front-right,front-center,lfe,rear-left,rear-right,side-left,side-right";
+            break;
+        case DevFmtAmbi3D:
+            ERR("%s capture samples not supported\n", DevFmtChannelsString(device->FmtChans));
+            pa_threaded_mainloop_unlock(self->loop);
+            goto fail;
+    }
+    if(!pa_channel_map_parse(&chanmap, mapname))
+    {
+        ERR("Failed to build channel map for %s\n", DevFmtChannelsString(device->FmtChans));
+        pa_threaded_mainloop_unlock(self->loop);
+        return ALC_FALSE;
+    }
+
+    self->spec.rate = device->Frequency;
+    self->spec.channels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
+
     if(pa_sample_spec_valid(&self->spec) == 0)
     {
         ERR("Invalid sample format\n");
@@ -1481,9 +1582,9 @@ static ALCenum ALCpulseCapture_open(ALCpulseCapture *self, const ALCchar *name)
         flags |= PA_STREAM_DONT_MOVE;
 
     TRACE("Connecting to \"%s\"\n", pulse_name ? pulse_name : "(default)");
-    self->stream = ALCpulseCapture_connectStream(pulse_name, self->loop, self->context,
-                                                 flags, &self->attr, &self->spec,
-                                                 &chanmap);
+    self->stream = ALCpulseCapture_connectStream(pulse_name,
+        self->loop, self->context, flags, &self->attr, &self->spec, &chanmap
+    );
     if(!self->stream)
     {
         pa_threaded_mainloop_unlock(self->loop);
@@ -1492,11 +1593,11 @@ static ALCenum ALCpulseCapture_open(ALCpulseCapture *self, const ALCchar *name)
     pa_stream_set_moved_callback(self->stream, ALCpulseCapture_streamMovedCallback, self);
     pa_stream_set_state_callback(self->stream, ALCpulseCapture_streamStateCallback, self);
 
-    al_string_copy_cstr(&self->device_name, pa_stream_get_device_name(self->stream));
-    if(al_string_empty(device->DeviceName))
+    alstr_copy_cstr(&self->device_name, pa_stream_get_device_name(self->stream));
+    if(alstr_empty(device->DeviceName))
     {
         pa_operation *o = pa_context_get_source_info_by_name(
-            self->context, al_string_get_cstr(self->device_name),
+            self->context, alstr_get_cstr(self->device_name),
             ALCpulseCapture_sourceNameCallback, self
         );
         wait_for_operation(o, self->loop);
@@ -1521,23 +1622,26 @@ static void ALCpulseCapture_close(ALCpulseCapture *self)
     self->context = NULL;
     self->stream = NULL;
 
-    al_string_clear(&self->device_name);
+    alstr_clear(&self->device_name);
 }
 
 static ALCboolean ALCpulseCapture_start(ALCpulseCapture *self)
 {
     pa_operation *o;
+    pa_threaded_mainloop_lock(self->loop);
     o = pa_stream_cork(self->stream, 0, stream_success_callback, self->loop);
     wait_for_operation(o, self->loop);
-
+    pa_threaded_mainloop_unlock(self->loop);
     return ALC_TRUE;
 }
 
 static void ALCpulseCapture_stop(ALCpulseCapture *self)
 {
     pa_operation *o;
+    pa_threaded_mainloop_lock(self->loop);
     o = pa_stream_cork(self->stream, 1, stream_success_callback, self->loop);
     wait_for_operation(o, self->loop);
+    pa_threaded_mainloop_unlock(self->loop);
 }
 
 static ALCenum ALCpulseCapture_captureSamples(ALCpulseCapture *self, ALCvoid *buffer, ALCuint samples)
@@ -1548,6 +1652,7 @@ static ALCenum ALCpulseCapture_captureSamples(ALCpulseCapture *self, ALCvoid *bu
     /* Capture is done in fragment-sized chunks, so we loop until we get all
      * that's available */
     self->last_readable -= todo;
+    pa_threaded_mainloop_lock(self->loop);
     while(todo > 0)
     {
         size_t rem = todo;
@@ -1587,6 +1692,7 @@ static ALCenum ALCpulseCapture_captureSamples(ALCpulseCapture *self, ALCvoid *bu
             self->cap_len = 0;
         }
     }
+    pa_threaded_mainloop_unlock(self->loop);
     if(todo > 0)
         memset(buffer, ((device->FmtType==DevFmtUByte) ? 0x80 : 0), todo);
 
@@ -1600,7 +1706,9 @@ static ALCuint ALCpulseCapture_availableSamples(ALCpulseCapture *self)
 
     if(device->Connected)
     {
-        ssize_t got = pa_stream_readable_size(self->stream);
+        ssize_t got;
+        pa_threaded_mainloop_lock(self->loop);
+        got = pa_stream_readable_size(self->stream);
         if(got < 0)
         {
             ERR("pa_stream_readable_size() failed: %s\n", pa_strerror(got));
@@ -1608,6 +1716,7 @@ static ALCuint ALCpulseCapture_availableSamples(ALCpulseCapture *self)
         }
         else if((size_t)got > self->cap_len)
             readable += got - self->cap_len;
+        pa_threaded_mainloop_unlock(self->loop);
     }
 
     if(self->last_readable < readable)
@@ -1616,19 +1725,25 @@ static ALCuint ALCpulseCapture_availableSamples(ALCpulseCapture *self)
 }
 
 
-static ALint64 ALCpulseCapture_getLatency(ALCpulseCapture *self)
+static ClockLatency ALCpulseCapture_getClockLatency(ALCpulseCapture *self)
 {
     pa_usec_t latency = 0;
-    int neg;
+    ClockLatency ret;
+    int neg, err;
 
-    if(pa_stream_get_latency(self->stream, &latency, &neg) != 0)
+    pa_threaded_mainloop_lock(self->loop);
+    ret.ClockTime = GetDeviceClockTime(STATIC_CAST(ALCbackend,self)->mDevice);
+    if((err=pa_stream_get_latency(self->stream, &latency, &neg)) != 0)
     {
-        ERR("Failed to get stream latency!\n");
-        return 0;
+        ERR("Failed to get stream latency: 0x%x\n", err);
+        latency = 0;
+        neg = 0;
     }
-
     if(neg) latency = 0;
-    return (ALint64)minu64(latency, U64(0x7fffffffffffffff)/1000) * 1000;
+    ret.Latency = minu64(latency, U64(0xffffffffffffffff)/1000) * 1000;
+    pa_threaded_mainloop_unlock(self->loop);
+
+    return ret;
 }
 
 
@@ -1732,14 +1847,14 @@ static void ALCpulseBackendFactory_probe(ALCpulseBackendFactory* UNUSED(self), e
     {
         case ALL_DEVICE_PROBE:
             ALCpulsePlayback_probeDevices();
-#define APPEND_ALL_DEVICES_LIST(e)  AppendAllDevicesList(al_string_get_cstr((e)->name))
+#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
             break;
 
         case CAPTURE_DEVICE_PROBE:
             ALCpulseCapture_probeDevices();
-#define APPEND_CAPTURE_DEVICE_LIST(e) AppendCaptureDeviceList(al_string_get_cstr((e)->name))
+#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
             break;

+ 261 - 109
libs/openal-soft/Alc/backends/qsa.c

@@ -33,6 +33,8 @@
 #include "alu.h"
 #include "threads.h"
 
+#include "backends/base.h"
+
 
 typedef struct {
     snd_pcm_t* pcmHandle;
@@ -117,8 +119,7 @@ static void deviceList(int type, vector_DevMap *devmap)
     if(max_cards < 0)
         return;
 
-    VECTOR_RESERVE(*devmap, max_cards+1);
-    VECTOR_RESIZE(*devmap, 0);
+    VECTOR_RESIZE(*devmap, 0, max_cards+1);
 
     entry.name = strdup(qsaDevice);
     entry.card = 0;
@@ -158,17 +159,40 @@ static void deviceList(int type, vector_DevMap *devmap)
 }
 
 
-FORCE_ALIGN static int qsa_proc_playback(void* ptr)
+/* Wrappers to use an old-style backend with the new interface. */
+typedef struct PlaybackWrapper {
+    DERIVE_FROM_TYPE(ALCbackend);
+    qsa_data *ExtraData;
+} PlaybackWrapper;
+
+static void PlaybackWrapper_Construct(PlaybackWrapper *self, ALCdevice *device);
+static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, void, Destruct)
+static ALCenum PlaybackWrapper_open(PlaybackWrapper *self, const ALCchar *name);
+static void PlaybackWrapper_close(PlaybackWrapper *self);
+static ALCboolean PlaybackWrapper_reset(PlaybackWrapper *self);
+static ALCboolean PlaybackWrapper_start(PlaybackWrapper *self);
+static void PlaybackWrapper_stop(PlaybackWrapper *self);
+static DECLARE_FORWARD2(PlaybackWrapper, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
+static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, ALCuint, availableSamples)
+static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, ClockLatency, getClockLatency)
+static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, void, lock)
+static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, void, unlock)
+DECLARE_DEFAULT_ALLOCATORS(PlaybackWrapper)
+DEFINE_ALCBACKEND_VTABLE(PlaybackWrapper);
+
+
+FORCE_ALIGN static int qsa_proc_playback(void *ptr)
 {
-    ALCdevice* device=(ALCdevice*)ptr;
-    qsa_data* data=(qsa_data*)device->ExtraData;
-    char* write_ptr;
-    int avail;
+    PlaybackWrapper *self = ptr;
+    ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
+    qsa_data *data = self->ExtraData;
     snd_pcm_channel_status_t status;
     struct sched_param param;
-    fd_set wfds;
-    int selectret;
     struct timeval timeout;
+    char* write_ptr;
+    fd_set wfds;
+    ALint len;
+    int sret;
 
     SetRTPriority();
     althrd_setname(althrd_current(), MIXER_THREAD_NAME);
@@ -178,59 +202,55 @@ FORCE_ALIGN static int qsa_proc_playback(void* ptr)
     param.sched_priority=param.sched_curpriority+1;
     SchedSet(0, 0, SCHED_NOCHANGE, &param);
 
-    ALint frame_size=FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
+    const ALint frame_size = FrameSizeFromDevFmt(
+        device->FmtChans, device->FmtType, device->AmbiOrder
+    );
 
-    while (!data->killNow)
+    V0(device->Backend,lock)();
+    while(!data->killNow)
     {
-        ALint len=data->size;
-        write_ptr=data->buffer;
-
-        avail=len/frame_size;
-        aluMixData(device, write_ptr, avail);
+        FD_ZERO(&wfds);
+        FD_SET(data->audio_fd, &wfds);
+        timeout.tv_sec=2;
+        timeout.tv_usec=0;
 
-        while (len>0 && !data->killNow)
+        /* Select also works like time slice to OS */
+        V0(device->Backend,unlock)();
+        sret = select(data->audio_fd+1, NULL, &wfds, NULL, &timeout);
+        V0(device->Backend,lock)();
+        if(sret == -1)
         {
-            FD_ZERO(&wfds);
-            FD_SET(data->audio_fd, &wfds);
-            timeout.tv_sec=2;
-            timeout.tv_usec=0;
-
-            /* Select also works like time slice to OS */
-            selectret=select(data->audio_fd+1, NULL, &wfds, NULL, &timeout);
-            switch (selectret)
-            {
-                case -1:
-                     aluHandleDisconnect(device);
-                     return 1;
-                case 0:
-                     break;
-                default:
-                     if (FD_ISSET(data->audio_fd, &wfds))
-                     {
-                         break;
-                     }
-                     break;
-            }
-
-            int wrote=snd_pcm_plugin_write(data->pcmHandle, write_ptr, len);
+            ERR("select error: %s\n", strerror(errno));
+            aluHandleDisconnect(device);
+            break;
+        }
+        if(sret == 0)
+        {
+            ERR("select timeout\n");
+            continue;
+        }
 
-            if (wrote<=0)
+        len = data->size;
+        write_ptr = data->buffer;
+        aluMixData(device, write_ptr, len/frame_size);
+        while(len>0 && !data->killNow)
+        {
+            int wrote = snd_pcm_plugin_write(data->pcmHandle, write_ptr, len);
+            if(wrote <= 0)
             {
-                if ((errno==EAGAIN) || (errno==EWOULDBLOCK))
-                {
+                if(errno==EAGAIN || errno==EWOULDBLOCK)
                     continue;
-                }
 
-                memset(&status, 0, sizeof (status));
-                status.channel=SND_PCM_CHANNEL_PLAYBACK;
+                memset(&status, 0, sizeof(status));
+                status.channel = SND_PCM_CHANNEL_PLAYBACK;
 
                 snd_pcm_plugin_status(data->pcmHandle, &status);
 
                 /* we need to reinitialize the sound channel if we've underrun the buffer */
-                if ((status.status==SND_PCM_STATUS_UNDERRUN) ||
-                    (status.status==SND_PCM_STATUS_READY))
+                if(status.status == SND_PCM_STATUS_UNDERRUN ||
+                   status.status == SND_PCM_STATUS_READY)
                 {
-                    if ((snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_PLAYBACK))<0)
+                    if(snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_PLAYBACK) < 0)
                     {
                         aluHandleDisconnect(device);
                         break;
@@ -239,11 +259,12 @@ FORCE_ALIGN static int qsa_proc_playback(void* ptr)
             }
             else
             {
-                write_ptr+=wrote;
-                len-=wrote;
+                write_ptr += wrote;
+                len -= wrote;
             }
         }
     }
+    V0(device->Backend,unlock)();
 
     return 0;
 }
@@ -252,8 +273,9 @@ FORCE_ALIGN static int qsa_proc_playback(void* ptr)
 /* Playback */
 /************/
 
-static ALCenum qsa_open_playback(ALCdevice* device, const ALCchar* deviceName)
+static ALCenum qsa_open_playback(PlaybackWrapper *self, const ALCchar* deviceName)
 {
+    ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
     qsa_data *data;
     int card, dev;
     int status;
@@ -277,7 +299,7 @@ static ALCenum qsa_open_playback(ALCdevice* device, const ALCchar* deviceName)
 #define MATCH_DEVNAME(iter) ((iter)->name && strcmp(deviceName, (iter)->name)==0)
         VECTOR_FIND_IF(iter, const DevMap, DeviceNameMap, MATCH_DEVNAME);
 #undef MATCH_DEVNAME
-        if(iter == VECTOR_ITER_END(DeviceNameMap))
+        if(iter == VECTOR_END(DeviceNameMap))
         {
             free(data);
             return ALC_INVALID_DEVICE;
@@ -300,15 +322,15 @@ static ALCenum qsa_open_playback(ALCdevice* device, const ALCchar* deviceName)
         return ALC_INVALID_DEVICE;
     }
 
-    al_string_copy_cstr(&device->DeviceName, deviceName);
-    device->ExtraData = data;
+    alstr_copy_cstr(&device->DeviceName, deviceName);
+    self->ExtraData = data;
 
     return ALC_NO_ERROR;
 }
 
-static void qsa_close_playback(ALCdevice* device)
+static void qsa_close_playback(PlaybackWrapper *self)
 {
-    qsa_data* data=(qsa_data*)device->ExtraData;
+    qsa_data *data = self->ExtraData;
 
     if (data->buffer!=NULL)
     {
@@ -319,12 +341,13 @@ static void qsa_close_playback(ALCdevice* device)
     snd_pcm_close(data->pcmHandle);
     free(data);
 
-    device->ExtraData=NULL;
+    self->ExtraData = NULL;
 }
 
-static ALCboolean qsa_reset_playback(ALCdevice* device)
+static ALCboolean qsa_reset_playback(PlaybackWrapper *self)
 {
-    qsa_data* data=(qsa_data*)device->ExtraData;
+    ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
+    qsa_data *data = self->ExtraData;
     int32_t format=-1;
 
     switch(device->FmtType)
@@ -365,14 +388,14 @@ static ALCboolean qsa_reset_playback(ALCdevice* device)
     data->cparams.start_mode=SND_PCM_START_FULL;
     data->cparams.stop_mode=SND_PCM_STOP_STOP;
 
-    data->cparams.buf.block.frag_size=device->UpdateSize*
-        ChannelsFromDevFmt(device->FmtChans)*BytesFromDevFmt(device->FmtType);
+    data->cparams.buf.block.frag_size=device->UpdateSize *
+        FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
     data->cparams.buf.block.frags_max=device->NumUpdates;
     data->cparams.buf.block.frags_min=device->NumUpdates;
 
     data->cparams.format.interleave=1;
     data->cparams.format.rate=device->Frequency;
-    data->cparams.format.voices=ChannelsFromDevFmt(device->FmtChans);
+    data->cparams.format.voices=ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
     data->cparams.format.format=format;
 
     if ((snd_pcm_plugin_params(data->pcmHandle, &data->cparams))<0)
@@ -556,7 +579,7 @@ static ALCboolean qsa_reset_playback(ALCdevice* device)
     SetDefaultChannelOrder(device);
 
     device->UpdateSize=data->csetup.buf.block.frag_size/
-        (ChannelsFromDevFmt(device->FmtChans)*BytesFromDevFmt(device->FmtType));
+        FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
     device->NumUpdates=data->csetup.buf.block.frags;
 
     data->size=data->csetup.buf.block.frag_size;
@@ -569,20 +592,20 @@ static ALCboolean qsa_reset_playback(ALCdevice* device)
     return ALC_TRUE;
 }
 
-static ALCboolean qsa_start_playback(ALCdevice* device)
+static ALCboolean qsa_start_playback(PlaybackWrapper *self)
 {
-    qsa_data *data = (qsa_data*)device->ExtraData;
+    qsa_data *data = self->ExtraData;
 
     data->killNow = 0;
-    if(althrd_create(&data->thread, qsa_proc_playback, device) != althrd_success)
+    if(althrd_create(&data->thread, qsa_proc_playback, self) != althrd_success)
         return ALC_FALSE;
 
     return ALC_TRUE;
 }
 
-static void qsa_stop_playback(ALCdevice* device)
+static void qsa_stop_playback(PlaybackWrapper *self)
 {
-    qsa_data *data = (qsa_data*)device->ExtraData;
+    qsa_data *data = self->ExtraData;
     int res;
 
     if(data->killNow)
@@ -592,12 +615,70 @@ static void qsa_stop_playback(ALCdevice* device)
     althrd_join(data->thread, &res);
 }
 
+
+static void PlaybackWrapper_Construct(PlaybackWrapper *self, ALCdevice *device)
+{
+    ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
+    SET_VTABLE2(PlaybackWrapper, ALCbackend, self);
+
+    self->ExtraData = NULL;
+}
+
+static ALCenum PlaybackWrapper_open(PlaybackWrapper *self, const ALCchar *name)
+{
+    return qsa_open_playback(self, name);
+}
+
+static void PlaybackWrapper_close(PlaybackWrapper *self)
+{
+    qsa_close_playback(self);
+}
+
+static ALCboolean PlaybackWrapper_reset(PlaybackWrapper *self)
+{
+    return qsa_reset_playback(self);
+}
+
+static ALCboolean PlaybackWrapper_start(PlaybackWrapper *self)
+{
+    return qsa_start_playback(self);
+}
+
+static void PlaybackWrapper_stop(PlaybackWrapper *self)
+{
+    qsa_stop_playback(self);
+}
+
+
+
 /***********/
 /* Capture */
 /***********/
 
-static ALCenum qsa_open_capture(ALCdevice* device, const ALCchar* deviceName)
+typedef struct CaptureWrapper {
+    DERIVE_FROM_TYPE(ALCbackend);
+    qsa_data *ExtraData;
+} CaptureWrapper;
+
+static void CaptureWrapper_Construct(CaptureWrapper *self, ALCdevice *device);
+static DECLARE_FORWARD(CaptureWrapper, ALCbackend, void, Destruct)
+static ALCenum CaptureWrapper_open(CaptureWrapper *self, const ALCchar *name);
+static void CaptureWrapper_close(CaptureWrapper *self);
+static DECLARE_FORWARD(CaptureWrapper, ALCbackend, ALCboolean, reset)
+static ALCboolean CaptureWrapper_start(CaptureWrapper *self);
+static void CaptureWrapper_stop(CaptureWrapper *self);
+static ALCenum CaptureWrapper_captureSamples(CaptureWrapper *self, void *buffer, ALCuint samples);
+static ALCuint CaptureWrapper_availableSamples(CaptureWrapper *self);
+static DECLARE_FORWARD(CaptureWrapper, ALCbackend, ClockLatency, getClockLatency)
+static DECLARE_FORWARD(CaptureWrapper, ALCbackend, void, lock)
+static DECLARE_FORWARD(CaptureWrapper, ALCbackend, void, unlock)
+DECLARE_DEFAULT_ALLOCATORS(CaptureWrapper)
+DEFINE_ALCBACKEND_VTABLE(CaptureWrapper);
+
+
+static ALCenum qsa_open_capture(CaptureWrapper *self, const ALCchar *deviceName)
 {
+    ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
     qsa_data *data;
     int card, dev;
     int format=-1;
@@ -624,7 +705,7 @@ static ALCenum qsa_open_capture(ALCdevice* device, const ALCchar* deviceName)
 #define MATCH_DEVNAME(iter) ((iter)->name && strcmp(deviceName, (iter)->name)==0)
         VECTOR_FIND_IF(iter, const DevMap, CaptureNameMap, MATCH_DEVNAME);
 #undef MATCH_DEVNAME
-        if(iter == VECTOR_ITER_END(CaptureNameMap))
+        if(iter == VECTOR_END(CaptureNameMap))
         {
             free(data);
             return ALC_INVALID_DEVICE;
@@ -647,8 +728,8 @@ static ALCenum qsa_open_capture(ALCdevice* device, const ALCchar* deviceName)
         return ALC_INVALID_DEVICE;
     }
 
-    al_string_copy_cstr(&device->DeviceName, deviceName);
-    device->ExtraData = data;
+    alstr_copy_cstr(&device->DeviceName, deviceName);
+    self->ExtraData = data;
 
     switch (device->FmtType)
     {
@@ -688,20 +769,19 @@ static ALCenum qsa_open_capture(ALCdevice* device, const ALCchar* deviceName)
     data->cparams.stop_mode=SND_PCM_STOP_STOP;
 
     data->cparams.buf.block.frag_size=device->UpdateSize*
-        ChannelsFromDevFmt(device->FmtChans)*BytesFromDevFmt(device->FmtType);
+        FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
     data->cparams.buf.block.frags_max=device->NumUpdates;
     data->cparams.buf.block.frags_min=device->NumUpdates;
 
     data->cparams.format.interleave=1;
     data->cparams.format.rate=device->Frequency;
-    data->cparams.format.voices=ChannelsFromDevFmt(device->FmtChans);
+    data->cparams.format.voices=ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
     data->cparams.format.format=format;
 
     if(snd_pcm_plugin_params(data->pcmHandle, &data->cparams) < 0)
     {
         snd_pcm_close(data->pcmHandle);
         free(data);
-        device->ExtraData=NULL;
 
         return ALC_INVALID_VALUE;
     }
@@ -709,20 +789,20 @@ static ALCenum qsa_open_capture(ALCdevice* device, const ALCchar* deviceName)
     return ALC_NO_ERROR;
 }
 
-static void qsa_close_capture(ALCdevice* device)
+static void qsa_close_capture(CaptureWrapper *self)
 {
-    qsa_data* data=(qsa_data*)device->ExtraData;
+    qsa_data *data = self->ExtraData;
 
     if (data->pcmHandle!=NULL)
         snd_pcm_close(data->pcmHandle);
 
     free(data);
-    device->ExtraData=NULL;
+    self->ExtraData = NULL;
 }
 
-static void qsa_start_capture(ALCdevice* device)
+static void qsa_start_capture(CaptureWrapper *self)
 {
-    qsa_data* data=(qsa_data*)device->ExtraData;
+    qsa_data *data = self->ExtraData;
     int rstatus;
 
     if ((rstatus=snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_CAPTURE))<0)
@@ -742,18 +822,18 @@ static void qsa_start_capture(ALCdevice* device)
     snd_pcm_capture_go(data->pcmHandle);
 }
 
-static void qsa_stop_capture(ALCdevice* device)
+static void qsa_stop_capture(CaptureWrapper *self)
 {
-    qsa_data* data=(qsa_data*)device->ExtraData;
-
+    qsa_data *data = self->ExtraData;
     snd_pcm_capture_flush(data->pcmHandle);
 }
 
-static ALCuint qsa_available_samples(ALCdevice* device)
+static ALCuint qsa_available_samples(CaptureWrapper *self)
 {
-    qsa_data* data=(qsa_data*)device->ExtraData;
+    ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
+    qsa_data *data = self->ExtraData;
     snd_pcm_channel_status_t status;
-    ALint frame_size=FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
+    ALint frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
     ALint free_size;
     int rstatus;
 
@@ -780,16 +860,17 @@ static ALCuint qsa_available_samples(ALCdevice* device)
     return free_size/frame_size;
 }
 
-static ALCenum qsa_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint samples)
+static ALCenum qsa_capture_samples(CaptureWrapper *self, ALCvoid *buffer, ALCuint samples)
 {
-    qsa_data* data=(qsa_data*)device->ExtraData;
+    ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
+    qsa_data *data = self->ExtraData;
     char* read_ptr;
     snd_pcm_channel_status_t status;
     fd_set rfds;
     int selectret;
     struct timeval timeout;
     int bytes_read;
-    ALint frame_size=FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
+    ALint frame_size=FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
     ALint len=samples*frame_size;
     int rstatus;
 
@@ -855,27 +936,65 @@ static ALCenum qsa_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint s
     return ALC_NO_ERROR;
 }
 
-static const BackendFuncs qsa_funcs= {
-    qsa_open_playback,
-    qsa_close_playback,
-    qsa_reset_playback,
-    qsa_start_playback,
-    qsa_stop_playback,
-    qsa_open_capture,
-    qsa_close_capture,
-    qsa_start_capture,
-    qsa_stop_capture,
-    qsa_capture_samples,
-    qsa_available_samples
-};
 
-ALCboolean alc_qsa_init(BackendFuncs* func_list)
+static void CaptureWrapper_Construct(CaptureWrapper *self, ALCdevice *device)
+{
+    ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
+    SET_VTABLE2(CaptureWrapper, ALCbackend, self);
+
+    self->ExtraData = NULL;
+}
+
+static ALCenum CaptureWrapper_open(CaptureWrapper *self, const ALCchar *name)
+{
+    return qsa_open_capture(self, name);
+}
+
+static void CaptureWrapper_close(CaptureWrapper *self)
+{
+    qsa_close_capture(self);
+}
+
+static ALCboolean CaptureWrapper_start(CaptureWrapper *self)
+{
+    qsa_start_capture(self);
+    return ALC_TRUE;
+}
+
+static void CaptureWrapper_stop(CaptureWrapper *self)
+{
+    qsa_stop_capture(self);
+}
+
+static ALCenum CaptureWrapper_captureSamples(CaptureWrapper *self, void *buffer, ALCuint samples)
+{
+    return qsa_capture_samples(self, buffer, samples);
+}
+
+static ALCuint CaptureWrapper_availableSamples(CaptureWrapper *self)
+{
+    return qsa_available_samples(self);
+}
+
+
+typedef struct ALCqsaBackendFactory {
+    DERIVE_FROM_TYPE(ALCbackendFactory);
+} ALCqsaBackendFactory;
+#define ALCQSABACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCqsaBackendFactory, ALCbackendFactory) } }
+
+static ALCboolean ALCqsaBackendFactory_init(ALCqsaBackendFactory* UNUSED(self));
+static void ALCqsaBackendFactory_deinit(ALCqsaBackendFactory* UNUSED(self));
+static ALCboolean ALCqsaBackendFactory_querySupport(ALCqsaBackendFactory* UNUSED(self), ALCbackend_Type type);
+static void ALCqsaBackendFactory_probe(ALCqsaBackendFactory* UNUSED(self), enum DevProbe type);
+static ALCbackend* ALCqsaBackendFactory_createBackend(ALCqsaBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type);
+DEFINE_ALCBACKENDFACTORY_VTABLE(ALCqsaBackendFactory);
+
+static ALCboolean ALCqsaBackendFactory_init(ALCqsaBackendFactory* UNUSED(self))
 {
-    *func_list = qsa_funcs;
     return ALC_TRUE;
 }
 
-void alc_qsa_deinit(void)
+static void ALCqsaBackendFactory_deinit(ALCqsaBackendFactory* UNUSED(self))
 {
 #define FREE_NAME(iter) free((iter)->name)
     VECTOR_FOR_EACH(DevMap, DeviceNameMap, FREE_NAME);
@@ -886,15 +1005,22 @@ void alc_qsa_deinit(void)
 #undef FREE_NAME
 }
 
-void alc_qsa_probe(enum DevProbe type)
+static ALCboolean ALCqsaBackendFactory_querySupport(ALCqsaBackendFactory* UNUSED(self), ALCbackend_Type type)
+{
+    if(type == ALCbackend_Playback || type == ALCbackend_Capture)
+        return ALC_TRUE;
+    return ALC_FALSE;
+}
+
+static void ALCqsaBackendFactory_probe(ALCqsaBackendFactory* UNUSED(self), enum DevProbe type)
 {
     switch (type)
     {
         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
-            VECTOR_RESIZE(DeviceNameMap, 0);
 
             deviceList(SND_PCM_CHANNEL_PLAYBACK, &DeviceNameMap);
 #define APPEND_DEVICE(iter) AppendAllDevicesList((iter)->name)
@@ -905,8 +1031,8 @@ void alc_qsa_probe(enum DevProbe type)
         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
-            VECTOR_RESIZE(CaptureNameMap, 0);
 
             deviceList(SND_PCM_CHANNEL_CAPTURE, &CaptureNameMap);
 #define APPEND_DEVICE(iter) AppendCaptureDeviceList((iter)->name)
@@ -915,3 +1041,29 @@ void alc_qsa_probe(enum DevProbe type)
             break;
     }
 }
+
+static ALCbackend* ALCqsaBackendFactory_createBackend(ALCqsaBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
+{
+    if(type == ALCbackend_Playback)
+    {
+        PlaybackWrapper *backend;
+        NEW_OBJ(backend, PlaybackWrapper)(device);
+        if(!backend) return NULL;
+        return STATIC_CAST(ALCbackend, backend);
+    }
+    if(type == ALCbackend_Capture)
+    {
+        CaptureWrapper *backend;
+        NEW_OBJ(backend, CaptureWrapper)(device);
+        if(!backend) return NULL;
+        return STATIC_CAST(ALCbackend, backend);
+    }
+
+    return NULL;
+}
+
+ALCbackendFactory *ALCqsaBackendFactory_getFactory(void)
+{
+    static ALCqsaBackendFactory factory = ALCQSABACKENDFACTORY_INITIALIZER;
+    return STATIC_CAST(ALCbackendFactory, &factory);
+}

+ 131 - 79
libs/openal-soft/Alc/backends/sndio.c

@@ -28,19 +28,16 @@
 #include "alu.h"
 #include "threads.h"
 
-#include <sndio.h>
+#include "backends/base.h"
 
+#include <sndio.h>
 
-static const ALCchar sndio_device[] = "SndIO Default";
 
 
-static ALCboolean sndio_load(void)
-{
-    return ALC_TRUE;
-}
 
+typedef struct ALCsndioBackend {
+    DERIVE_FROM_TYPE(ALCbackend);
 
-typedef struct {
     struct sio_hdl *sndHandle;
 
     ALvoid *mix_data;
@@ -48,30 +45,72 @@ typedef struct {
 
     volatile int killNow;
     althrd_t thread;
-} sndio_data;
+} 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 void ALCsndioBackend_close(ALCsndioBackend *self);
+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)
+
+DEFINE_ALCBACKEND_VTABLE(ALCsndioBackend);
+
+
+static const ALCchar sndio_device[] = "SndIO Default";
+
+
+static void ALCsndioBackend_Construct(ALCsndioBackend *self, ALCdevice *device)
+{
+    ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
+    SET_VTABLE2(ALCsndioBackend, ALCbackend, self);
+}
+
+static void ALCsndioBackend_Destruct(ALCsndioBackend *self)
+{
+    if(self->sndHandle)
+        sio_close(self->sndHandle);
+    self->sndHandle = NULL;
+
+    al_free(self->mix_data);
+    self->mix_data = NULL;
+
+    ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
+}
 
 
-static int sndio_proc(void *ptr)
+static int ALCsndioBackend_mixerProc(void *ptr)
 {
-    ALCdevice *device = ptr;
-    sndio_data *data = device->ExtraData;
+    ALCsndioBackend *self = (ALCsndioBackend*)ptr;
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
     ALsizei frameSize;
     size_t wrote;
 
     SetRTPriority();
     althrd_setname(althrd_current(), MIXER_THREAD_NAME);
 
-    frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
+    frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
 
-    while(!data->killNow && device->Connected)
+    while(!self->killNow && device->Connected)
     {
-        ALsizei len = data->data_size;
-        ALubyte *WritePtr = data->mix_data;
+        ALsizei len = self->data_size;
+        ALubyte *WritePtr = self->mix_data;
 
+        ALCsndioBackend_lock(self);
         aluMixData(device, WritePtr, len/frameSize);
-        while(len > 0 && !data->killNow)
+        ALCsndioBackend_unlock(self);
+        while(len > 0 && !self->killNow)
         {
-            wrote = sio_write(data->sndHandle, WritePtr, len);
+            wrote = sio_write(self->sndHandle, WritePtr, len);
             if(wrote == 0)
             {
                 ERR("sio_write failed\n");
@@ -90,45 +129,36 @@ static int sndio_proc(void *ptr)
 }
 
 
-
-static ALCenum sndio_open_playback(ALCdevice *device, const ALCchar *deviceName)
+static ALCenum ALCsndioBackend_open(ALCsndioBackend *self, const ALCchar *name)
 {
-    sndio_data *data;
+    ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
 
-    if(!deviceName)
-        deviceName = sndio_device;
-    else if(strcmp(deviceName, sndio_device) != 0)
+    if(!name)
+        name = sndio_device;
+    else if(strcmp(name, sndio_device) != 0)
         return ALC_INVALID_VALUE;
 
-    data = calloc(1, sizeof(*data));
-    data->killNow = 0;
-
-    data->sndHandle = sio_open(NULL, SIO_PLAY, 0);
-    if(data->sndHandle == NULL)
+    self->sndHandle = sio_open(NULL, SIO_PLAY, 0);
+    if(self->sndHandle == NULL)
     {
-        free(data);
         ERR("Could not open device\n");
         return ALC_INVALID_VALUE;
     }
 
-    al_string_copy_cstr(&device->DeviceName, deviceName);
-    device->ExtraData = data;
+    alstr_copy_cstr(&device->DeviceName, name);
 
     return ALC_NO_ERROR;
 }
 
-static void sndio_close_playback(ALCdevice *device)
+static void ALCsndioBackend_close(ALCsndioBackend *self)
 {
-    sndio_data *data = device->ExtraData;
-
-    sio_close(data->sndHandle);
-    free(data);
-    device->ExtraData = NULL;
+    sio_close(self->sndHandle);
+    self->sndHandle = NULL;
 }
 
-static ALCboolean sndio_reset_playback(ALCdevice *device)
+static ALCboolean ALCsndioBackend_reset(ALCsndioBackend *self)
 {
-    sndio_data *data = device->ExtraData;
+    ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
     struct sio_par par;
 
     sio_initpar(&par);
@@ -170,7 +200,7 @@ static ALCboolean sndio_reset_playback(ALCdevice *device)
     par.appbufsz = device->UpdateSize * (device->NumUpdates-1);
     if(!par.appbufsz) par.appbufsz = device->UpdateSize;
 
-    if(!sio_setpar(data->sndHandle, &par) || !sio_getpar(data->sndHandle, &par))
+    if(!sio_setpar(self->sndHandle, &par) || !sio_getpar(self->sndHandle, &par))
     {
         ERR("Failed to set device parameters\n");
         return ALC_FALSE;
@@ -211,77 +241,86 @@ static ALCboolean sndio_reset_playback(ALCdevice *device)
     return ALC_TRUE;
 }
 
-static ALCboolean sndio_start_playback(ALCdevice *device)
+static ALCboolean ALCsndioBackend_start(ALCsndioBackend *self)
 {
-    sndio_data *data = device->ExtraData;
+    ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
 
-    if(!sio_start(data->sndHandle))
+    self->data_size = device->UpdateSize * FrameSizeFromDevFmt(
+        device->FmtChans, device->FmtType, device->AmbiOrder
+    );
+    al_free(self->mix_data);
+    self->mix_data = al_calloc(16, self->data_size);
+
+    if(!sio_start(self->sndHandle))
     {
         ERR("Error starting playback\n");
         return ALC_FALSE;
     }
 
-    data->data_size = device->UpdateSize * FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
-    data->mix_data = calloc(1, data->data_size);
-
-    data->killNow = 0;
-    if(althrd_create(&data->thread, sndio_proc, device) != althrd_success)
+    self->killNow = 0;
+    if(althrd_create(&self->thread, ALCsndioBackend_mixerProc, self) != althrd_success)
     {
-        sio_stop(data->sndHandle);
-        free(data->mix_data);
-        data->mix_data = NULL;
+        sio_stop(self->sndHandle);
         return ALC_FALSE;
     }
 
     return ALC_TRUE;
 }
 
-static void sndio_stop_playback(ALCdevice *device)
+static void ALCsndioBackend_stop(ALCsndioBackend *self)
 {
-    sndio_data *data = device->ExtraData;
     int res;
 
-    if(data->killNow)
+    if(self->killNow)
         return;
 
-    data->killNow = 1;
-    althrd_join(data->thread, &res);
+    self->killNow = 1;
+    althrd_join(self->thread, &res);
 
-    if(!sio_stop(data->sndHandle))
+    if(!sio_stop(self->sndHandle))
         ERR("Error stopping device\n");
 
-    free(data->mix_data);
-    data->mix_data = NULL;
+    al_free(self->mix_data);
+    self->mix_data = NULL;
 }
 
 
-static const BackendFuncs sndio_funcs = {
-    sndio_open_playback,
-    sndio_close_playback,
-    sndio_reset_playback,
-    sndio_start_playback,
-    sndio_stop_playback,
-    NULL,
-    NULL,
-    NULL,
-    NULL,
-    NULL,
-    NULL
-};
-
-ALCboolean alc_sndio_init(BackendFuncs *func_list)
+typedef struct ALCsndioBackendFactory {
+    DERIVE_FROM_TYPE(ALCbackendFactory);
+} ALCsndioBackendFactory;
+#define ALCSNDIOBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCsndioBackendFactory, ALCbackendFactory) } }
+
+ALCbackendFactory *ALCsndioBackendFactory_getFactory(void);
+
+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);
+
+
+ALCbackendFactory *ALCsndioBackendFactory_getFactory(void)
 {
-    if(!sndio_load())
-        return ALC_FALSE;
-    *func_list = sndio_funcs;
+    static ALCsndioBackendFactory factory = ALCSNDIOBACKENDFACTORY_INITIALIZER;
+    return STATIC_CAST(ALCbackendFactory, &factory);
+}
+
+
+static ALCboolean ALCsndioBackendFactory_init(ALCsndioBackendFactory* UNUSED(self))
+{
+    /* No dynamic loading */
     return ALC_TRUE;
 }
 
-void alc_sndio_deinit(void)
+static ALCboolean ALCsndioBackendFactory_querySupport(ALCsndioBackendFactory* UNUSED(self), ALCbackend_Type type)
 {
+    if(type == ALCbackend_Playback)
+        return ALC_TRUE;
+    return ALC_FALSE;
 }
 
-void alc_sndio_probe(enum DevProbe type)
+static void ALCsndioBackendFactory_probe(ALCsndioBackendFactory* UNUSED(self), enum DevProbe type)
 {
     switch(type)
     {
@@ -292,3 +331,16 @@ void alc_sndio_probe(enum DevProbe type)
             break;
     }
 }
+
+static ALCbackend* ALCsndioBackendFactory_createBackend(ALCsndioBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
+{
+    if(type == ALCbackend_Playback)
+    {
+        ALCsndioBackend *backend;
+        NEW_OBJ(backend, ALCsndioBackend)(device);
+        if(!backend) return NULL;
+        return STATIC_CAST(ALCbackend, backend);
+    }
+
+    return NULL;
+}

+ 59 - 35
libs/openal-soft/Alc/backends/solaris.c

@@ -50,7 +50,7 @@ typedef struct ALCsolarisBackend {
     ALubyte *mix_data;
     int data_size;
 
-    volatile int killNow;
+    ATOMIC(ALenum) killNow;
     althrd_t thread;
 } ALCsolarisBackend;
 
@@ -65,7 +65,7 @@ static ALCboolean ALCsolarisBackend_start(ALCsolarisBackend *self);
 static void ALCsolarisBackend_stop(ALCsolarisBackend *self);
 static DECLARE_FORWARD2(ALCsolarisBackend, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
 static DECLARE_FORWARD(ALCsolarisBackend, ALCbackend, ALCuint, availableSamples)
-static DECLARE_FORWARD(ALCsolarisBackend, ALCbackend, ALint64, getLatency)
+static DECLARE_FORWARD(ALCsolarisBackend, ALCbackend, ClockLatency, getClockLatency)
 static DECLARE_FORWARD(ALCsolarisBackend, ALCbackend, void, lock)
 static DECLARE_FORWARD(ALCsolarisBackend, ALCbackend, void, unlock)
 DECLARE_DEFAULT_ALLOCATORS(ALCsolarisBackend)
@@ -84,6 +84,7 @@ static void ALCsolarisBackend_Construct(ALCsolarisBackend *self, ALCdevice *devi
     SET_VTABLE2(ALCsolarisBackend, ALCbackend, self);
 
     self->fd = -1;
+    ATOMIC_INIT(&self->killNow, AL_FALSE);
 }
 
 static void ALCsolarisBackend_Destruct(ALCsolarisBackend *self)
@@ -103,43 +104,65 @@ static void ALCsolarisBackend_Destruct(ALCsolarisBackend *self)
 static int ALCsolarisBackend_mixerProc(void *ptr)
 {
     ALCsolarisBackend *self = ptr;
-    ALCdevice *Device = STATIC_CAST(ALCbackend,self)->mDevice;
-    ALint frameSize;
-    int wrote;
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+    struct timeval timeout;
+    ALubyte *write_ptr;
+    ALint frame_size;
+    ALint to_write;
+    ssize_t wrote;
+    fd_set wfds;
+    int sret;
 
     SetRTPriority();
     althrd_setname(althrd_current(), MIXER_THREAD_NAME);
 
-    frameSize = FrameSizeFromDevFmt(Device->FmtChans, Device->FmtType);
+    frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
 
-    while(!self->killNow && Device->Connected)
+    ALCsolarisBackend_lock(self);
+    while(!ATOMIC_LOAD_SEQ(&self->killNow) && device->Connected)
     {
-        ALint len = self->data_size;
-        ALubyte *WritePtr = self->mix_data;
+        FD_ZERO(&wfds);
+        FD_SET(self->fd, &wfds);
+        timeout.tv_sec = 1;
+        timeout.tv_usec = 0;
+
+        ALCsolarisBackend_unlock(self);
+        sret = select(self->fd+1, NULL, &wfds, NULL, &timeout);
+        ALCsolarisBackend_lock(self);
+        if(sret < 0)
+        {
+            if(errno == EINTR)
+                continue;
+            ERR("select failed: %s\n", strerror(errno));
+            aluHandleDisconnect(device);
+            break;
+        }
+        else if(sret == 0)
+        {
+            WARN("select timeout\n");
+            continue;
+        }
 
-        aluMixData(Device, WritePtr, len/frameSize);
-        while(len > 0 && !self->killNow)
+        write_ptr = self->mix_data;
+        to_write = self->data_size;
+        aluMixData(device, write_ptr, to_write/frame_size);
+        while(to_write > 0 && !ATOMIC_LOAD_SEQ(&self->killNow))
         {
-            wrote = write(self->fd, WritePtr, len);
+            wrote = write(self->fd, write_ptr, to_write);
             if(wrote < 0)
             {
-                if(errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR)
-                {
-                    ERR("write failed: %s\n", strerror(errno));
-                    ALCsolarisBackend_lock(self);
-                    aluHandleDisconnect(Device);
-                    ALCsolarisBackend_unlock(self);
-                    break;
-                }
-
-                al_nssleep(1000000);
-                continue;
+                if(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
+                    continue;
+                ERR("write failed: %s\n", strerror(errno));
+                aluHandleDisconnect(device);
+                break;
             }
 
-            len -= wrote;
-            WritePtr += wrote;
+            to_write -= wrote;
+            write_ptr += wrote;
         }
     }
+    ALCsolarisBackend_unlock(self);
 
     return 0;
 }
@@ -162,7 +185,7 @@ static ALCenum ALCsolarisBackend_open(ALCsolarisBackend *self, const ALCchar *na
     }
 
     device = STATIC_CAST(ALCbackend,self)->mDevice;
-    al_string_copy_cstr(&device->DeviceName, name);
+    alstr_copy_cstr(&device->DeviceName, name);
 
     return ALC_NO_ERROR;
 }
@@ -177,8 +200,8 @@ static ALCboolean ALCsolarisBackend_reset(ALCsolarisBackend *self)
 {
     ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
     audio_info_t info;
-    ALuint frameSize;
-    int numChannels;
+    ALsizei frameSize;
+    ALsizei numChannels;
 
     AUDIO_INITINFO(&info);
 
@@ -186,7 +209,7 @@ static ALCboolean ALCsolarisBackend_reset(ALCsolarisBackend *self)
 
     if(device->FmtChans != DevFmtMono)
         device->FmtChans = DevFmtStereo;
-    numChannels = ChannelsFromDevFmt(device->FmtChans);
+    numChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
     info.play.channels = numChannels;
 
     switch(device->FmtType)
@@ -220,9 +243,9 @@ static ALCboolean ALCsolarisBackend_reset(ALCsolarisBackend *self)
         return ALC_FALSE;
     }
 
-    if(ChannelsFromDevFmt(device->FmtChans) != info.play.channels)
+    if(ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder) != (ALsizei)info.play.channels)
     {
-        ERR("Could not set %d channels, got %d instead\n", ChannelsFromDevFmt(device->FmtChans), info.play.channels);
+        ERR("Failed to set %s, got %u channels instead\n", DevFmtChannelsString(device->FmtChans), info.play.channels);
         return ALC_FALSE;
     }
 
@@ -242,7 +265,9 @@ static ALCboolean ALCsolarisBackend_reset(ALCsolarisBackend *self)
     SetDefaultChannelOrder(device);
 
     free(self->mix_data);
-    self->data_size = device->UpdateSize * FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
+    self->data_size = device->UpdateSize * FrameSizeFromDevFmt(
+        device->FmtChans, device->FmtType, device->AmbiOrder
+    );
     self->mix_data = calloc(1, self->data_size);
 
     return ALC_TRUE;
@@ -250,7 +275,7 @@ static ALCboolean ALCsolarisBackend_reset(ALCsolarisBackend *self)
 
 static ALCboolean ALCsolarisBackend_start(ALCsolarisBackend *self)
 {
-    self->killNow = 0;
+    ATOMIC_STORE_SEQ(&self->killNow, AL_FALSE);
     if(althrd_create(&self->thread, ALCsolarisBackend_mixerProc, self) != althrd_success)
         return ALC_FALSE;
     return ALC_TRUE;
@@ -260,10 +285,9 @@ static void ALCsolarisBackend_stop(ALCsolarisBackend *self)
 {
     int res;
 
-    if(self->killNow)
+    if(ATOMIC_EXCHANGE_SEQ(&self->killNow, AL_TRUE))
         return;
 
-    self->killNow = 1;
     althrd_join(self->thread, &res);
 
     if(ioctl(self->fd, AUDIO_DRAIN) < 0)

+ 43 - 30
libs/openal-soft/Alc/backends/wave.c

@@ -91,7 +91,7 @@ static ALCboolean ALCwaveBackend_start(ALCwaveBackend *self);
 static void ALCwaveBackend_stop(ALCwaveBackend *self);
 static DECLARE_FORWARD2(ALCwaveBackend, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
 static DECLARE_FORWARD(ALCwaveBackend, ALCbackend, ALCuint, availableSamples)
-static DECLARE_FORWARD(ALCwaveBackend, ALCbackend, ALint64, getLatency)
+static DECLARE_FORWARD(ALCwaveBackend, ALCbackend, ClockLatency, getClockLatency)
 static DECLARE_FORWARD(ALCwaveBackend, ALCbackend, void, lock)
 static DECLARE_FORWARD(ALCwaveBackend, ALCbackend, void, unlock)
 DECLARE_DEFAULT_ALLOCATORS(ALCwaveBackend)
@@ -127,7 +127,7 @@ static int ALCwaveBackend_mixerProc(void *ptr)
 
     althrd_setname(althrd_current(), MIXER_THREAD_NAME);
 
-    frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
+    frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
 
     done = 0;
     if(altimespec_get(&start, AL_TIME_UTC) != AL_TIME_UTC)
@@ -157,37 +157,41 @@ static int ALCwaveBackend_mixerProc(void *ptr)
             al_nssleep(restTime);
         else while(avail-done >= device->UpdateSize)
         {
+            ALCwaveBackend_lock(self);
             aluMixData(device, self->mBuffer, device->UpdateSize);
+            ALCwaveBackend_unlock(self);
             done += device->UpdateSize;
 
             if(!IS_LITTLE_ENDIAN)
             {
                 ALuint bytesize = BytesFromDevFmt(device->FmtType);
-                ALubyte *bytes = self->mBuffer;
                 ALuint i;
 
-                if(bytesize == 1)
+                if(bytesize == 2)
                 {
-                    for(i = 0;i < self->mSize;i++)
-                        fputc(bytes[i], self->mFile);
-                }
-                else if(bytesize == 2)
-                {
-                    for(i = 0;i < self->mSize;i++)
-                        fputc(bytes[i^1], self->mFile);
+                    ALushort *samples = self->mBuffer;
+                    ALuint len = self->mSize / 2;
+                    for(i = 0;i < len;i++)
+                    {
+                        ALushort samp = samples[i];
+                        samples[i] = (samp>>8) | (samp<<8);
+                    }
                 }
                 else if(bytesize == 4)
                 {
-                    for(i = 0;i < self->mSize;i++)
-                        fputc(bytes[i^3], self->mFile);
+                    ALuint *samples = self->mBuffer;
+                    ALuint len = self->mSize / 4;
+                    for(i = 0;i < len;i++)
+                    {
+                        ALuint samp = samples[i];
+                        samples[i] = (samp>>24) | ((samp>>8)&0x0000ff00) |
+                                     ((samp<<8)&0x00ff0000) | (samp<<24);
+                    }
                 }
             }
-            else
-            {
-                fs = fwrite(self->mBuffer, frameSize, device->UpdateSize,
-                            self->mFile);
-                (void)fs;
-            }
+
+            fs = fwrite(self->mBuffer, frameSize, device->UpdateSize, self->mFile);
+            (void)fs;
             if(ferror(self->mFile))
             {
                 ERR("Error writing to file\n");
@@ -224,7 +228,7 @@ static ALCenum ALCwaveBackend_open(ALCwaveBackend *self, const ALCchar *name)
     }
 
     device = STATIC_CAST(ALCbackend, self)->mDevice;
-    al_string_copy_cstr(&device->DeviceName, name);
+    alstr_copy_cstr(&device->DeviceName, name);
 
     return ALC_NO_ERROR;
 }
@@ -247,7 +251,10 @@ static ALCboolean ALCwaveBackend_reset(ALCwaveBackend *self)
     clearerr(self->mFile);
 
     if(GetConfigValueBool(NULL, "wave", "bformat", 0))
-        device->FmtChans = DevFmtBFormat3D;
+    {
+        device->FmtChans = DevFmtAmbi3D;
+        device->AmbiOrder = 1;
+    }
 
     switch(device->FmtType)
     {
@@ -275,20 +282,23 @@ static ALCboolean ALCwaveBackend_reset(ALCwaveBackend *self)
         case DevFmtX51Rear: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x010 | 0x020; break;
         case DevFmtX61: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x100 | 0x200 | 0x400; break;
         case DevFmtX71: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x010 | 0x020 | 0x200 | 0x400; break;
-        case DevFmtBFormat3D:
+        case DevFmtAmbi3D:
+            /* .amb output requires FuMa */
+            device->AmbiLayout = AmbiLayout_FuMa;
+            device->AmbiScale = AmbiNorm_FuMa;
             isbformat = 1;
             chanmask = 0;
             break;
     }
     bits = BytesFromDevFmt(device->FmtType) * 8;
-    channels = ChannelsFromDevFmt(device->FmtChans);
+    channels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
 
-    fprintf(self->mFile, "RIFF");
+    fputs("RIFF", self->mFile);
     fwrite32le(0xFFFFFFFF, self->mFile); // 'RIFF' header len; filled in at close
 
-    fprintf(self->mFile, "WAVE");
+    fputs("WAVE", self->mFile);
 
-    fprintf(self->mFile, "fmt ");
+    fputs("fmt ", self->mFile);
     fwrite32le(40, self->mFile); // 'fmt ' header len; 40 bytes for EXTENSIBLE
 
     // 16-bit val, format type id (extensible: 0xFFFE)
@@ -310,11 +320,12 @@ static ALCboolean ALCwaveBackend_reset(ALCwaveBackend *self)
     // 32-bit val, channel mask
     fwrite32le(chanmask, self->mFile);
     // 16 byte GUID, sub-type format
-    val = fwrite(((bits==32) ? (isbformat ? SUBTYPE_BFORMAT_FLOAT : SUBTYPE_FLOAT) :
-                               (isbformat ? SUBTYPE_BFORMAT_PCM : SUBTYPE_PCM)), 1, 16, self->mFile);
+    val = fwrite((device->FmtType == DevFmtFloat) ?
+                 (isbformat ? SUBTYPE_BFORMAT_FLOAT : SUBTYPE_FLOAT) :
+                 (isbformat ? SUBTYPE_BFORMAT_PCM : SUBTYPE_PCM), 1, 16, self->mFile);
     (void)val;
 
-    fprintf(self->mFile, "data");
+    fputs("data", self->mFile);
     fwrite32le(0xFFFFFFFF, self->mFile); // 'data' header len; filled in at close
 
     if(ferror(self->mFile))
@@ -333,7 +344,9 @@ static ALCboolean ALCwaveBackend_start(ALCwaveBackend *self)
 {
     ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
 
-    self->mSize = device->UpdateSize * FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
+    self->mSize = device->UpdateSize * FrameSizeFromDevFmt(
+        device->FmtChans, device->FmtType, device->AmbiOrder
+    );
     self->mBuffer = malloc(self->mSize);
     if(!self->mBuffer)
     {

+ 45 - 43
libs/openal-soft/Alc/backends/winmm.c

@@ -45,8 +45,8 @@ static vector_al_string CaptureDevices;
 
 static void clear_devlist(vector_al_string *list)
 {
-    VECTOR_FOR_EACH(al_string, *list, al_string_deinit);
-    VECTOR_RESIZE(*list, 0);
+    VECTOR_FOR_EACH(al_string, *list, alstr_reset);
+    VECTOR_RESIZE(*list, 0, 0);
 }
 
 
@@ -58,7 +58,7 @@ static void ProbePlaybackDevices(void)
     clear_devlist(&PlaybackDevices);
 
     numdevs = waveOutGetNumDevs();
-    VECTOR_RESERVE(PlaybackDevices, numdevs);
+    VECTOR_RESIZE(PlaybackDevices, 0, numdevs);
     for(i = 0;i < numdevs;i++)
     {
         WAVEOUTCAPSW WaveCaps;
@@ -71,23 +71,23 @@ static void ProbePlaybackDevices(void)
             ALuint count = 0;
             while(1)
             {
-                al_string_copy_cstr(&dname, DEVNAME_HEAD);
-                al_string_append_wcstr(&dname, WaveCaps.szPname);
+                alstr_copy_cstr(&dname, DEVNAME_HEAD);
+                alstr_append_wcstr(&dname, WaveCaps.szPname);
                 if(count != 0)
                 {
                     char str[64];
                     snprintf(str, sizeof(str), " #%d", count+1);
-                    al_string_append_cstr(&dname, str);
+                    alstr_append_cstr(&dname, str);
                 }
                 count++;
 
-#define MATCH_ENTRY(i) (al_string_cmp(dname, *(i)) == 0)
+#define MATCH_ENTRY(i) (alstr_cmp(dname, *(i)) == 0)
                 VECTOR_FIND_IF(iter, const al_string, PlaybackDevices, MATCH_ENTRY);
-                if(iter == VECTOR_ITER_END(PlaybackDevices)) break;
+                if(iter == VECTOR_END(PlaybackDevices)) break;
 #undef MATCH_ENTRY
             }
 
-            TRACE("Got device \"%s\", ID %u\n", al_string_get_cstr(dname), i);
+            TRACE("Got device \"%s\", ID %u\n", alstr_get_cstr(dname), i);
         }
         VECTOR_PUSH_BACK(PlaybackDevices, dname);
     }
@@ -101,7 +101,7 @@ static void ProbeCaptureDevices(void)
     clear_devlist(&CaptureDevices);
 
     numdevs = waveInGetNumDevs();
-    VECTOR_RESERVE(CaptureDevices, numdevs);
+    VECTOR_RESIZE(CaptureDevices, 0, numdevs);
     for(i = 0;i < numdevs;i++)
     {
         WAVEINCAPSW WaveCaps;
@@ -114,23 +114,23 @@ static void ProbeCaptureDevices(void)
             ALuint count = 0;
             while(1)
             {
-                al_string_copy_cstr(&dname, DEVNAME_HEAD);
-                al_string_append_wcstr(&dname, WaveCaps.szPname);
+                alstr_copy_cstr(&dname, DEVNAME_HEAD);
+                alstr_append_wcstr(&dname, WaveCaps.szPname);
                 if(count != 0)
                 {
                     char str[64];
                     snprintf(str, sizeof(str), " #%d", count+1);
-                    al_string_append_cstr(&dname, str);
+                    alstr_append_cstr(&dname, str);
                 }
                 count++;
 
-#define MATCH_ENTRY(i) (al_string_cmp(dname, *(i)) == 0)
+#define MATCH_ENTRY(i) (alstr_cmp(dname, *(i)) == 0)
                 VECTOR_FIND_IF(iter, const al_string, CaptureDevices, MATCH_ENTRY);
-                if(iter == VECTOR_ITER_END(CaptureDevices)) break;
+                if(iter == VECTOR_END(CaptureDevices)) break;
 #undef MATCH_ENTRY
             }
 
-            TRACE("Got device \"%s\", ID %u\n", al_string_get_cstr(dname), i);
+            TRACE("Got device \"%s\", ID %u\n", alstr_get_cstr(dname), i);
         }
         VECTOR_PUSH_BACK(CaptureDevices, dname);
     }
@@ -164,7 +164,7 @@ static ALCboolean ALCwinmmPlayback_start(ALCwinmmPlayback *self);
 static void ALCwinmmPlayback_stop(ALCwinmmPlayback *self);
 static DECLARE_FORWARD2(ALCwinmmPlayback, ALCbackend, ALCenum, captureSamples, ALCvoid*, ALCuint)
 static DECLARE_FORWARD(ALCwinmmPlayback, ALCbackend, ALCuint, availableSamples)
-static DECLARE_FORWARD(ALCwinmmPlayback, ALCbackend, ALint64, getLatency)
+static DECLARE_FORWARD(ALCwinmmPlayback, ALCbackend, ClockLatency, getClockLatency)
 static DECLARE_FORWARD(ALCwinmmPlayback, ALCbackend, void, lock)
 static DECLARE_FORWARD(ALCwinmmPlayback, ALCbackend, void, unlock)
 DECLARE_DEFAULT_ALLOCATORS(ALCwinmmPlayback)
@@ -232,8 +232,10 @@ FORCE_ALIGN static int ALCwinmmPlayback_mixerProc(void *arg)
         }
 
         WaveHdr = ((WAVEHDR*)msg.lParam);
+        ALCwinmmPlayback_lock(self);
         aluMixData(device, WaveHdr->lpData, WaveHdr->dwBufferLength /
                                             self->Format.nBlockAlign);
+        ALCwinmmPlayback_unlock(self);
 
         // Send buffer back to play more data
         waveOutWrite(self->OutHdl, WaveHdr, sizeof(WAVEHDR));
@@ -255,14 +257,14 @@ static ALCenum ALCwinmmPlayback_open(ALCwinmmPlayback *self, const ALCchar *devi
         ProbePlaybackDevices();
 
     // Find the Device ID matching the deviceName if valid
-#define MATCH_DEVNAME(iter) (!al_string_empty(*(iter)) && \
-                             (!deviceName || al_string_cmp_cstr(*(iter), deviceName) == 0))
+#define MATCH_DEVNAME(iter) (!alstr_empty(*(iter)) && \
+                             (!deviceName || alstr_cmp_cstr(*(iter), deviceName) == 0))
     VECTOR_FIND_IF(iter, const al_string, PlaybackDevices, MATCH_DEVNAME);
-    if(iter == VECTOR_ITER_END(PlaybackDevices))
+    if(iter == VECTOR_END(PlaybackDevices))
         return ALC_INVALID_VALUE;
 #undef MATCH_DEVNAME
 
-    DeviceID = (UINT)(iter - VECTOR_ITER_BEGIN(PlaybackDevices));
+    DeviceID = (UINT)(iter - VECTOR_BEGIN(PlaybackDevices));
 
 retry_open:
     memset(&self->Format, 0, sizeof(WAVEFORMATEX));
@@ -298,7 +300,7 @@ retry_open:
         goto failure;
     }
 
-    al_string_copy(&device->DeviceName, VECTOR_ELEM(PlaybackDevices, DeviceID));
+    alstr_copy(&device->DeviceName, VECTOR_ELEM(PlaybackDevices, DeviceID));
     return ALC_NO_ERROR;
 
 failure:
@@ -380,7 +382,7 @@ static ALCboolean ALCwinmmPlayback_start(ALCwinmmPlayback *self)
 
     // Create 4 Buffers
     BufferSize  = device->UpdateSize*device->NumUpdates / 4;
-    BufferSize *= FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
+    BufferSize *= FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
 
     BufferData = calloc(4, BufferSize);
     for(i = 0;i < 4;i++)
@@ -430,7 +432,7 @@ typedef struct ALCwinmmCapture {
 
     HWAVEIN InHdl;
 
-    RingBuffer *Ring;
+    ll_ringbuffer_t *Ring;
 
     WAVEFORMATEX Format;
 
@@ -451,7 +453,7 @@ static ALCboolean ALCwinmmCapture_start(ALCwinmmCapture *self);
 static void ALCwinmmCapture_stop(ALCwinmmCapture *self);
 static ALCenum ALCwinmmCapture_captureSamples(ALCwinmmCapture *self, ALCvoid *buffer, ALCuint samples);
 static ALCuint ALCwinmmCapture_availableSamples(ALCwinmmCapture *self);
-static DECLARE_FORWARD(ALCwinmmCapture, ALCbackend, ALint64, getLatency)
+static DECLARE_FORWARD(ALCwinmmCapture, ALCbackend, ClockLatency, getClockLatency)
 static DECLARE_FORWARD(ALCwinmmCapture, ALCbackend, void, lock)
 static DECLARE_FORWARD(ALCwinmmCapture, ALCbackend, void, unlock)
 DECLARE_DEFAULT_ALLOCATORS(ALCwinmmCapture)
@@ -514,8 +516,9 @@ static int ALCwinmmCapture_captureProc(void *arg)
             break;
 
         WaveHdr = ((WAVEHDR*)msg.lParam);
-        WriteRingBuffer(self->Ring, (ALubyte*)WaveHdr->lpData,
-                        WaveHdr->dwBytesRecorded/self->Format.nBlockAlign);
+        ll_ringbuffer_write(self->Ring, WaveHdr->lpData,
+            WaveHdr->dwBytesRecorded / self->Format.nBlockAlign
+        );
 
         // Send buffer back to capture more data
         waveInAddBuffer(self->InHdl, WaveHdr, sizeof(WAVEHDR));
@@ -541,13 +544,13 @@ static ALCenum ALCwinmmCapture_open(ALCwinmmCapture *self, const ALCchar *name)
         ProbeCaptureDevices();
 
     // Find the Device ID matching the deviceName if valid
-#define MATCH_DEVNAME(iter) (!al_string_empty(*(iter)) &&  (!name || al_string_cmp_cstr(*iter, name) == 0))
+#define MATCH_DEVNAME(iter) (!alstr_empty(*(iter)) && (!name || alstr_cmp_cstr(*iter, name) == 0))
     VECTOR_FIND_IF(iter, const al_string, CaptureDevices, MATCH_DEVNAME);
-    if(iter == VECTOR_ITER_END(CaptureDevices))
+    if(iter == VECTOR_END(CaptureDevices))
         return ALC_INVALID_VALUE;
 #undef MATCH_DEVNAME
 
-    DeviceID = (UINT)(iter - VECTOR_ITER_BEGIN(CaptureDevices));
+    DeviceID = (UINT)(iter - VECTOR_BEGIN(CaptureDevices));
 
     switch(device->FmtChans)
     {
@@ -560,7 +563,7 @@ static ALCenum ALCwinmmCapture_open(ALCwinmmCapture *self, const ALCchar *name)
         case DevFmtX51Rear:
         case DevFmtX61:
         case DevFmtX71:
-        case DevFmtBFormat3D:
+        case DevFmtAmbi3D:
             return ALC_INVALID_ENUM;
     }
 
@@ -581,7 +584,7 @@ static ALCenum ALCwinmmCapture_open(ALCwinmmCapture *self, const ALCchar *name)
     memset(&self->Format, 0, sizeof(WAVEFORMATEX));
     self->Format.wFormatTag = ((device->FmtType == DevFmtFloat) ?
                                WAVE_FORMAT_IEEE_FLOAT : WAVE_FORMAT_PCM);
-    self->Format.nChannels = ChannelsFromDevFmt(device->FmtChans);
+    self->Format.nChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
     self->Format.wBitsPerSample = BytesFromDevFmt(device->FmtType) * 8;
     self->Format.nBlockAlign = self->Format.wBitsPerSample *
                                self->Format.nChannels / 8;
@@ -603,7 +606,7 @@ static ALCenum ALCwinmmCapture_open(ALCwinmmCapture *self, const ALCchar *name)
     if(CapturedDataSize < (self->Format.nSamplesPerSec / 10))
         CapturedDataSize = self->Format.nSamplesPerSec / 10;
 
-    self->Ring = CreateRingBuffer(self->Format.nBlockAlign, CapturedDataSize);
+    self->Ring = ll_ringbuffer_create(CapturedDataSize+1, self->Format.nBlockAlign);
     if(!self->Ring) goto failure;
 
     InitRef(&self->WaveBuffersCommitted, 0);
@@ -633,7 +636,7 @@ static ALCenum ALCwinmmCapture_open(ALCwinmmCapture *self, const ALCchar *name)
     if(althrd_create(&self->thread, ALCwinmmCapture_captureProc, self) != althrd_success)
         goto failure;
 
-    al_string_copy(&device->DeviceName, VECTOR_ELEM(CaptureDevices, DeviceID));
+    alstr_copy(&device->DeviceName, VECTOR_ELEM(CaptureDevices, DeviceID));
     return ALC_NO_ERROR;
 
 failure:
@@ -644,8 +647,7 @@ failure:
         free(BufferData);
     }
 
-    if(self->Ring)
-        DestroyRingBuffer(self->Ring);
+    ll_ringbuffer_free(self->Ring);
     self->Ring = NULL;
 
     if(self->InHdl)
@@ -678,7 +680,7 @@ static void ALCwinmmCapture_close(ALCwinmmCapture *self)
     }
     free(buffer);
 
-    DestroyRingBuffer(self->Ring);
+    ll_ringbuffer_free(self->Ring);
     self->Ring = NULL;
 
     // Close the Wave device
@@ -699,25 +701,25 @@ static void ALCwinmmCapture_stop(ALCwinmmCapture *self)
 
 static ALCenum ALCwinmmCapture_captureSamples(ALCwinmmCapture *self, ALCvoid *buffer, ALCuint samples)
 {
-    ReadRingBuffer(self->Ring, buffer, samples);
+    ll_ringbuffer_read(self->Ring, buffer, samples);
     return ALC_NO_ERROR;
 }
 
 static ALCuint ALCwinmmCapture_availableSamples(ALCwinmmCapture *self)
 {
-    return RingBufferSize(self->Ring);
+    return ll_ringbuffer_read_space(self->Ring);
 }
 
 
 static inline void AppendAllDevicesList2(const al_string *name)
 {
-    if(!al_string_empty(*name))
-        AppendAllDevicesList(al_string_get_cstr(*name));
+    if(!alstr_empty(*name))
+        AppendAllDevicesList(alstr_get_cstr(*name));
 }
 static inline void AppendCaptureDeviceList2(const al_string *name)
 {
-    if(!al_string_empty(*name))
-        AppendCaptureDeviceList(al_string_get_cstr(*name));
+    if(!alstr_empty(*name))
+        AppendCaptureDeviceList(alstr_get_cstr(*name));
 }
 
 typedef struct ALCwinmmBackendFactory {

+ 612 - 0
libs/openal-soft/Alc/bformatdec.c

@@ -0,0 +1,612 @@
+
+#include "config.h"
+
+#include "bformatdec.h"
+#include "ambdec.h"
+#include "mixer_defs.h"
+#include "alu.h"
+
+#include "bool.h"
+#include "threads.h"
+#include "almalloc.h"
+
+
+void bandsplit_init(BandSplitter *splitter, ALfloat freq_mult)
+{
+    ALfloat w = freq_mult * F_TAU;
+    ALfloat cw = cosf(w);
+    if(cw > FLT_EPSILON)
+        splitter->coeff = (sinf(w) - 1.0f) / cw;
+    else
+        splitter->coeff = cw * -0.5f;
+
+    splitter->lp_z1 = 0.0f;
+    splitter->lp_z2 = 0.0f;
+    splitter->hp_z1 = 0.0f;
+}
+
+void bandsplit_clear(BandSplitter *splitter)
+{
+    splitter->lp_z1 = 0.0f;
+    splitter->lp_z2 = 0.0f;
+    splitter->hp_z1 = 0.0f;
+}
+
+void bandsplit_process(BandSplitter *splitter, ALfloat *restrict hpout, ALfloat *restrict lpout,
+                       const ALfloat *input, ALsizei count)
+{
+    ALfloat coeff, d, x;
+    ALfloat z1, z2;
+    ALsizei i;
+
+    coeff = splitter->coeff*0.5f + 0.5f;
+    z1 = splitter->lp_z1;
+    z2 = splitter->lp_z2;
+    for(i = 0;i < count;i++)
+    {
+        x = input[i];
+
+        d = (x - z1) * coeff;
+        x = z1 + d;
+        z1 = x + d;
+
+        d = (x - z2) * coeff;
+        x = z2 + d;
+        z2 = x + d;
+
+        lpout[i] = x;
+    }
+    splitter->lp_z1 = z1;
+    splitter->lp_z2 = z2;
+
+    coeff = splitter->coeff;
+    z1 = splitter->hp_z1;
+    for(i = 0;i < count;i++)
+    {
+        x = input[i];
+
+        d = x - coeff*z1;
+        x = z1 + coeff*d;
+        z1 = d;
+
+        hpout[i] = x - lpout[i];
+    }
+    splitter->hp_z1 = z1;
+}
+
+
+void splitterap_init(SplitterAllpass *splitter, ALfloat freq_mult)
+{
+    ALfloat w = freq_mult * F_TAU;
+    ALfloat cw = cosf(w);
+    if(cw > FLT_EPSILON)
+        splitter->coeff = (sinf(w) - 1.0f) / cw;
+    else
+        splitter->coeff = cw * -0.5f;
+
+    splitter->z1 = 0.0f;
+}
+
+void splitterap_clear(SplitterAllpass *splitter)
+{
+    splitter->z1 = 0.0f;
+}
+
+void splitterap_process(SplitterAllpass *splitter, ALfloat *restrict samples, ALsizei count)
+{
+    ALfloat coeff, d, x;
+    ALfloat z1;
+    ALsizei i;
+
+    coeff = splitter->coeff;
+    z1 = splitter->z1;
+    for(i = 0;i < count;i++)
+    {
+        x = samples[i];
+
+        d = x - coeff*z1;
+        x = z1 + coeff*d;
+        z1 = d;
+
+        samples[i] = x;
+    }
+    splitter->z1 = z1;
+}
+
+
+static const ALfloat UnitScale[MAX_AMBI_COEFFS] = {
+    1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
+    1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f
+};
+static const ALfloat SN3D2N3DScale[MAX_AMBI_COEFFS] = {
+    1.000000000f, /* ACN  0 (W), sqrt(1) */
+    1.732050808f, /* ACN  1 (Y), sqrt(3) */
+    1.732050808f, /* ACN  2 (Z), sqrt(3) */
+    1.732050808f, /* ACN  3 (X), sqrt(3) */
+    2.236067978f, /* ACN  4 (V), sqrt(5) */
+    2.236067978f, /* ACN  5 (T), sqrt(5) */
+    2.236067978f, /* ACN  6 (R), sqrt(5) */
+    2.236067978f, /* ACN  7 (S), sqrt(5) */
+    2.236067978f, /* ACN  8 (U), sqrt(5) */
+    2.645751311f, /* ACN  9 (Q), sqrt(7) */
+    2.645751311f, /* ACN 10 (O), sqrt(7) */
+    2.645751311f, /* ACN 11 (M), sqrt(7) */
+    2.645751311f, /* ACN 12 (K), sqrt(7) */
+    2.645751311f, /* ACN 13 (L), sqrt(7) */
+    2.645751311f, /* ACN 14 (N), sqrt(7) */
+    2.645751311f, /* ACN 15 (P), sqrt(7) */
+};
+static const ALfloat FuMa2N3DScale[MAX_AMBI_COEFFS] = {
+    1.414213562f, /* ACN  0 (W), sqrt(2) */
+    1.732050808f, /* ACN  1 (Y), sqrt(3) */
+    1.732050808f, /* ACN  2 (Z), sqrt(3) */
+    1.732050808f, /* ACN  3 (X), sqrt(3) */
+    1.936491673f, /* ACN  4 (V), sqrt(15)/2 */
+    1.936491673f, /* ACN  5 (T), sqrt(15)/2 */
+    2.236067978f, /* ACN  6 (R), sqrt(5) */
+    1.936491673f, /* ACN  7 (S), sqrt(15)/2 */
+    1.936491673f, /* ACN  8 (U), sqrt(15)/2 */
+    2.091650066f, /* ACN  9 (Q), sqrt(35/8) */
+    1.972026594f, /* ACN 10 (O), sqrt(35)/3 */
+    2.231093404f, /* ACN 11 (M), sqrt(224/45) */
+    2.645751311f, /* ACN 12 (K), sqrt(7) */
+    2.231093404f, /* ACN 13 (L), sqrt(224/45) */
+    1.972026594f, /* ACN 14 (N), sqrt(35)/3 */
+    2.091650066f, /* ACN 15 (P), sqrt(35/8) */
+};
+
+
+enum FreqBand {
+    FB_HighFreq,
+    FB_LowFreq,
+    FB_Max
+};
+
+/* These points are in AL coordinates! */
+static const ALfloat Ambi3DPoints[8][3] = {
+    { -0.577350269f,  0.577350269f, -0.577350269f },
+    {  0.577350269f,  0.577350269f, -0.577350269f },
+    { -0.577350269f,  0.577350269f,  0.577350269f },
+    {  0.577350269f,  0.577350269f,  0.577350269f },
+    { -0.577350269f, -0.577350269f, -0.577350269f },
+    {  0.577350269f, -0.577350269f, -0.577350269f },
+    { -0.577350269f, -0.577350269f,  0.577350269f },
+    {  0.577350269f, -0.577350269f,  0.577350269f },
+};
+static const ALfloat Ambi3DDecoder[8][FB_Max][MAX_AMBI_COEFFS] = {
+    { { 0.25f,  0.1443375672f,  0.1443375672f,  0.1443375672f }, { 0.125f,  0.125f,  0.125f,  0.125f } },
+    { { 0.25f, -0.1443375672f,  0.1443375672f,  0.1443375672f }, { 0.125f, -0.125f,  0.125f,  0.125f } },
+    { { 0.25f,  0.1443375672f,  0.1443375672f, -0.1443375672f }, { 0.125f,  0.125f,  0.125f, -0.125f } },
+    { { 0.25f, -0.1443375672f,  0.1443375672f, -0.1443375672f }, { 0.125f, -0.125f,  0.125f, -0.125f } },
+    { { 0.25f,  0.1443375672f, -0.1443375672f,  0.1443375672f }, { 0.125f,  0.125f, -0.125f,  0.125f } },
+    { { 0.25f, -0.1443375672f, -0.1443375672f,  0.1443375672f }, { 0.125f, -0.125f, -0.125f,  0.125f } },
+    { { 0.25f,  0.1443375672f, -0.1443375672f, -0.1443375672f }, { 0.125f,  0.125f, -0.125f, -0.125f } },
+    { { 0.25f, -0.1443375672f, -0.1443375672f, -0.1443375672f }, { 0.125f, -0.125f, -0.125f, -0.125f } },
+};
+
+
+static RowMixerFunc MixMatrixRow = MixRow_C;
+
+
+static alonce_flag bformatdec_inited = AL_ONCE_FLAG_INIT;
+
+static void init_bformatdec(void)
+{
+    MixMatrixRow = SelectRowMixer();
+}
+
+
+/* NOTE: BandSplitter filters are unused with single-band decoding */
+typedef struct BFormatDec {
+    ALboolean Enabled[MAX_OUTPUT_CHANNELS];
+
+    union {
+        alignas(16) ALfloat Dual[MAX_OUTPUT_CHANNELS][FB_Max][MAX_AMBI_COEFFS];
+        alignas(16) ALfloat Single[MAX_OUTPUT_CHANNELS][MAX_AMBI_COEFFS];
+    } Matrix;
+
+    BandSplitter XOver[MAX_AMBI_COEFFS];
+
+    ALfloat (*Samples)[BUFFERSIZE];
+    /* These two alias into Samples */
+    ALfloat (*SamplesHF)[BUFFERSIZE];
+    ALfloat (*SamplesLF)[BUFFERSIZE];
+
+    alignas(16) ALfloat ChannelMix[BUFFERSIZE];
+
+    struct {
+        BandSplitter XOver;
+        ALfloat Gains[FB_Max];
+    } UpSampler[4];
+
+    ALsizei NumChannels;
+    ALboolean DualBand;
+} BFormatDec;
+
+BFormatDec *bformatdec_alloc()
+{
+    alcall_once(&bformatdec_inited, init_bformatdec);
+    return al_calloc(16, sizeof(BFormatDec));
+}
+
+void bformatdec_free(BFormatDec *dec)
+{
+    if(dec)
+    {
+        al_free(dec->Samples);
+        dec->Samples = NULL;
+        dec->SamplesHF = NULL;
+        dec->SamplesLF = NULL;
+
+        memset(dec, 0, sizeof(*dec));
+        al_free(dec);
+    }
+}
+
+void bformatdec_reset(BFormatDec *dec, const AmbDecConf *conf, ALsizei chancount, ALuint srate, const ALsizei chanmap[MAX_OUTPUT_CHANNELS])
+{
+    static const ALsizei map2DTo3D[MAX_AMBI2D_COEFFS] = {
+        0,  1, 3,  4, 8,  9, 15
+    };
+    const ALfloat *coeff_scale = UnitScale;
+    bool periphonic;
+    ALfloat ratio;
+    ALsizei i;
+
+    al_free(dec->Samples);
+    dec->Samples = NULL;
+    dec->SamplesHF = NULL;
+    dec->SamplesLF = NULL;
+
+    dec->NumChannels = chancount;
+    dec->Samples = al_calloc(16, dec->NumChannels*2 * sizeof(dec->Samples[0]));
+    dec->SamplesHF = dec->Samples;
+    dec->SamplesLF = dec->SamplesHF + dec->NumChannels;
+
+    for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
+        dec->Enabled[i] = AL_FALSE;
+    for(i = 0;i < conf->NumSpeakers;i++)
+        dec->Enabled[chanmap[i]] = AL_TRUE;
+
+    if(conf->CoeffScale == ADS_SN3D)
+        coeff_scale = SN3D2N3DScale;
+    else if(conf->CoeffScale == ADS_FuMa)
+        coeff_scale = FuMa2N3DScale;
+
+    memset(dec->UpSampler, 0, sizeof(dec->UpSampler));
+    ratio = 400.0f / (ALfloat)srate;
+    for(i = 0;i < 4;i++)
+        bandsplit_init(&dec->UpSampler[i].XOver, ratio);
+    if((conf->ChanMask&AMBI_PERIPHONIC_MASK))
+    {
+        periphonic = true;
+
+        dec->UpSampler[0].Gains[FB_HighFreq] = (dec->NumChannels > 9) ? W_SCALE3D_THIRD :
+                                               (dec->NumChannels > 4) ? W_SCALE3D_SECOND : 1.0f;
+        dec->UpSampler[0].Gains[FB_LowFreq] = 1.0f;
+        for(i = 1;i < 4;i++)
+        {
+            dec->UpSampler[i].Gains[FB_HighFreq] = (dec->NumChannels > 9) ? XYZ_SCALE3D_THIRD :
+                                                   (dec->NumChannels > 4) ? XYZ_SCALE3D_SECOND : 1.0f;
+            dec->UpSampler[i].Gains[FB_LowFreq] = 1.0f;
+        }
+    }
+    else
+    {
+        periphonic = false;
+
+        dec->UpSampler[0].Gains[FB_HighFreq] = (dec->NumChannels > 5) ? W_SCALE2D_THIRD :
+                                               (dec->NumChannels > 3) ? W_SCALE2D_SECOND : 1.0f;
+        dec->UpSampler[0].Gains[FB_LowFreq] = 1.0f;
+        for(i = 1;i < 3;i++)
+        {
+            dec->UpSampler[i].Gains[FB_HighFreq] = (dec->NumChannels > 5) ? XYZ_SCALE2D_THIRD :
+                                                   (dec->NumChannels > 3) ? XYZ_SCALE2D_SECOND : 1.0f;
+            dec->UpSampler[i].Gains[FB_LowFreq] = 1.0f;
+        }
+        dec->UpSampler[3].Gains[FB_HighFreq] = 0.0f;
+        dec->UpSampler[3].Gains[FB_LowFreq] = 0.0f;
+    }
+
+    memset(&dec->Matrix, 0, sizeof(dec->Matrix));
+    if(conf->FreqBands == 1)
+    {
+        dec->DualBand = AL_FALSE;
+        for(i = 0;i < conf->NumSpeakers;i++)
+        {
+            ALsizei chan = chanmap[i];
+            ALfloat gain;
+            ALsizei j, k;
+
+            if(!periphonic)
+            {
+                for(j = 0,k = 0;j < MAX_AMBI2D_COEFFS;j++)
+                {
+                    ALsizei l = map2DTo3D[j];
+                    if(j == 0) gain = conf->HFOrderGain[0];
+                    else if(j == 1) gain = conf->HFOrderGain[1];
+                    else if(j == 3) gain = conf->HFOrderGain[2];
+                    else if(j == 5) gain = conf->HFOrderGain[3];
+                    if((conf->ChanMask&(1<<l)))
+                        dec->Matrix.Single[chan][j] = conf->HFMatrix[i][k++] / coeff_scale[l] *
+                                                      gain;
+                }
+            }
+            else
+            {
+                for(j = 0,k = 0;j < MAX_AMBI_COEFFS;j++)
+                {
+                    if(j == 0) gain = conf->HFOrderGain[0];
+                    else if(j == 1) gain = conf->HFOrderGain[1];
+                    else if(j == 4) gain = conf->HFOrderGain[2];
+                    else if(j == 9) gain = conf->HFOrderGain[3];
+                    if((conf->ChanMask&(1<<j)))
+                        dec->Matrix.Single[chan][j] = conf->HFMatrix[i][k++] / coeff_scale[j] *
+                                                      gain;
+                }
+            }
+        }
+    }
+    else
+    {
+        dec->DualBand = AL_TRUE;
+
+        ratio = conf->XOverFreq / (ALfloat)srate;
+        for(i = 0;i < MAX_AMBI_COEFFS;i++)
+            bandsplit_init(&dec->XOver[i], ratio);
+
+        ratio = powf(10.0f, conf->XOverRatio / 40.0f);
+        for(i = 0;i < conf->NumSpeakers;i++)
+        {
+            ALsizei chan = chanmap[i];
+            ALfloat gain;
+            ALsizei j, k;
+
+            if(!periphonic)
+            {
+                for(j = 0,k = 0;j < MAX_AMBI2D_COEFFS;j++)
+                {
+                    ALsizei l = map2DTo3D[j];
+                    if(j == 0) gain = conf->HFOrderGain[0] * ratio;
+                    else if(j == 1) gain = conf->HFOrderGain[1] * ratio;
+                    else if(j == 3) gain = conf->HFOrderGain[2] * ratio;
+                    else if(j == 5) gain = conf->HFOrderGain[3] * ratio;
+                    if((conf->ChanMask&(1<<l)))
+                        dec->Matrix.Dual[chan][FB_HighFreq][j] = conf->HFMatrix[i][k++] /
+                                                                 coeff_scale[l] * gain;
+                }
+                for(j = 0,k = 0;j < MAX_AMBI2D_COEFFS;j++)
+                {
+                    ALsizei l = map2DTo3D[j];
+                    if(j == 0) gain = conf->LFOrderGain[0] / ratio;
+                    else if(j == 1) gain = conf->LFOrderGain[1] / ratio;
+                    else if(j == 3) gain = conf->LFOrderGain[2] / ratio;
+                    else if(j == 5) gain = conf->LFOrderGain[3] / ratio;
+                    if((conf->ChanMask&(1<<l)))
+                        dec->Matrix.Dual[chan][FB_LowFreq][j] = conf->LFMatrix[i][k++] /
+                                                                coeff_scale[l] * gain;
+                }
+            }
+            else
+            {
+                for(j = 0,k = 0;j < MAX_AMBI_COEFFS;j++)
+                {
+                    if(j == 0) gain = conf->HFOrderGain[0] * ratio;
+                    else if(j == 1) gain = conf->HFOrderGain[1] * ratio;
+                    else if(j == 4) gain = conf->HFOrderGain[2] * ratio;
+                    else if(j == 9) gain = conf->HFOrderGain[3] * ratio;
+                    if((conf->ChanMask&(1<<j)))
+                        dec->Matrix.Dual[chan][FB_HighFreq][j] = conf->HFMatrix[i][k++] /
+                                                                 coeff_scale[j] * gain;
+                }
+                for(j = 0,k = 0;j < MAX_AMBI_COEFFS;j++)
+                {
+                    if(j == 0) gain = conf->LFOrderGain[0] / ratio;
+                    else if(j == 1) gain = conf->LFOrderGain[1] / ratio;
+                    else if(j == 4) gain = conf->LFOrderGain[2] / ratio;
+                    else if(j == 9) gain = conf->LFOrderGain[3] / ratio;
+                    if((conf->ChanMask&(1<<j)))
+                        dec->Matrix.Dual[chan][FB_LowFreq][j] = conf->LFMatrix[i][k++] /
+                                                                coeff_scale[j] * gain;
+                }
+            }
+        }
+    }
+}
+
+
+void bformatdec_process(struct BFormatDec *dec, ALfloat (*restrict OutBuffer)[BUFFERSIZE], ALsizei OutChannels, const ALfloat (*restrict InSamples)[BUFFERSIZE], ALsizei SamplesToDo)
+{
+    ALsizei chan, i;
+
+    OutBuffer = ASSUME_ALIGNED(OutBuffer, 16);
+    if(dec->DualBand)
+    {
+        for(i = 0;i < dec->NumChannels;i++)
+            bandsplit_process(&dec->XOver[i], dec->SamplesHF[i], dec->SamplesLF[i],
+                              InSamples[i], SamplesToDo);
+
+        for(chan = 0;chan < OutChannels;chan++)
+        {
+            if(!dec->Enabled[chan])
+                continue;
+
+            memset(dec->ChannelMix, 0, SamplesToDo*sizeof(ALfloat));
+            MixMatrixRow(dec->ChannelMix, dec->Matrix.Dual[chan][FB_HighFreq],
+                SAFE_CONST(ALfloatBUFFERSIZE*,dec->SamplesHF), dec->NumChannels, 0,
+                SamplesToDo
+            );
+            MixMatrixRow(dec->ChannelMix, dec->Matrix.Dual[chan][FB_LowFreq],
+                SAFE_CONST(ALfloatBUFFERSIZE*,dec->SamplesLF), dec->NumChannels, 0,
+                SamplesToDo
+            );
+
+            for(i = 0;i < SamplesToDo;i++)
+                OutBuffer[chan][i] += dec->ChannelMix[i];
+        }
+    }
+    else
+    {
+        for(chan = 0;chan < OutChannels;chan++)
+        {
+            if(!dec->Enabled[chan])
+                continue;
+
+            memset(dec->ChannelMix, 0, SamplesToDo*sizeof(ALfloat));
+            MixMatrixRow(dec->ChannelMix, dec->Matrix.Single[chan], InSamples,
+                         dec->NumChannels, 0, SamplesToDo);
+
+            for(i = 0;i < SamplesToDo;i++)
+                OutBuffer[chan][i] += dec->ChannelMix[i];
+        }
+    }
+}
+
+
+void bformatdec_upSample(struct BFormatDec *dec, ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat (*restrict InSamples)[BUFFERSIZE], ALsizei InChannels, ALsizei SamplesToDo)
+{
+    ALsizei i;
+
+    /* This up-sampler leverages the differences observed in dual-band second-
+     * and third-order decoder matrices compared to first-order. For the same
+     * output channel configuration, the low-frequency matrix has identical
+     * coefficients in the shared input channels, while the high-frequency
+     * matrix has extra scalars applied to the W channel and X/Y/Z channels.
+     * Mixing the first-order content into the higher-order stream with the
+     * appropriate counter-scales applied to the HF response results in the
+     * subsequent higher-order decode generating the same response as a first-
+     * order decode.
+     */
+    for(i = 0;i < InChannels;i++)
+    {
+        /* First, split the first-order components into low and high frequency
+         * bands.
+         */
+        bandsplit_process(&dec->UpSampler[i].XOver,
+            dec->Samples[FB_HighFreq], dec->Samples[FB_LowFreq],
+            InSamples[i], SamplesToDo
+        );
+
+        /* Now write each band to the output. */
+        MixMatrixRow(OutBuffer[i], dec->UpSampler[i].Gains,
+            SAFE_CONST(ALfloatBUFFERSIZE*,dec->Samples), FB_Max, 0,
+            SamplesToDo
+        );
+    }
+}
+
+
+#define INVALID_UPSAMPLE_INDEX INT_MAX
+
+static ALsizei GetACNIndex(const BFChannelConfig *chans, ALsizei numchans, ALsizei acn)
+{
+    ALsizei i;
+    for(i = 0;i < numchans;i++)
+    {
+        if(chans[i].Index == acn)
+            return i;
+    }
+    return INVALID_UPSAMPLE_INDEX;
+}
+#define GetChannelForACN(b, a) GetACNIndex((b).Ambi.Map, (b).NumChannels, (a))
+
+typedef struct AmbiUpsampler {
+    alignas(16) ALfloat Samples[FB_Max][BUFFERSIZE];
+
+    BandSplitter XOver[4];
+
+    ALfloat Gains[4][MAX_OUTPUT_CHANNELS][FB_Max];
+} AmbiUpsampler;
+
+AmbiUpsampler *ambiup_alloc()
+{
+    alcall_once(&bformatdec_inited, init_bformatdec);
+    return al_calloc(16, sizeof(AmbiUpsampler));
+}
+
+void ambiup_free(struct AmbiUpsampler *ambiup)
+{
+    al_free(ambiup);
+}
+
+void ambiup_reset(struct AmbiUpsampler *ambiup, const ALCdevice *device)
+{
+    ALfloat ratio;
+    size_t i;
+
+    ratio = 400.0f / (ALfloat)device->Frequency;
+    for(i = 0;i < 4;i++)
+        bandsplit_init(&ambiup->XOver[i], ratio);
+
+    memset(ambiup->Gains, 0, sizeof(ambiup->Gains));
+    if(device->Dry.CoeffCount > 0)
+    {
+        ALfloat encgains[8][MAX_OUTPUT_CHANNELS];
+        ALsizei j;
+        size_t k;
+
+        for(i = 0;i < COUNTOF(Ambi3DPoints);i++)
+        {
+            ALfloat coeffs[MAX_AMBI_COEFFS] = { 0.0f };
+            CalcDirectionCoeffs(Ambi3DPoints[i], 0.0f, coeffs);
+            ComputePanningGains(device->Dry, coeffs, 1.0f, encgains[i]);
+        }
+
+        /* Combine the matrices that do the in->virt and virt->out conversions
+         * so we get a single in->out conversion. NOTE: the Encoder matrix
+         * (encgains) and output are transposed, so the input channels line up
+         * with the rows and the output channels line up with the columns.
+         */
+        for(i = 0;i < 4;i++)
+        {
+            for(j = 0;j < device->Dry.NumChannels;j++)
+            {
+                ALfloat hfgain=0.0f, lfgain=0.0f;
+                for(k = 0;k < COUNTOF(Ambi3DDecoder);k++)
+                {
+                    hfgain += Ambi3DDecoder[k][FB_HighFreq][i]*encgains[k][j];
+                    lfgain += Ambi3DDecoder[k][FB_LowFreq][i]*encgains[k][j];
+                }
+                ambiup->Gains[i][j][FB_HighFreq] = hfgain;
+                ambiup->Gains[i][j][FB_LowFreq] = lfgain;
+            }
+        }
+    }
+    else
+    {
+        /* Assumes full 3D/periphonic on the input and output mixes! */
+        ALfloat w_scale = (device->Dry.NumChannels > 9) ? W_SCALE3D_THIRD :
+                          (device->Dry.NumChannels > 4) ? W_SCALE3D_SECOND : 1.0f;
+        ALfloat xyz_scale = (device->Dry.NumChannels > 9) ? XYZ_SCALE3D_THIRD :
+                            (device->Dry.NumChannels > 4) ? XYZ_SCALE3D_SECOND : 1.0f;
+        for(i = 0;i < 4;i++)
+        {
+            ALsizei index = GetChannelForACN(device->Dry, i);
+            if(index != INVALID_UPSAMPLE_INDEX)
+            {
+                ALfloat scale = device->Dry.Ambi.Map[index].Scale;
+                ambiup->Gains[i][index][FB_HighFreq] = scale * ((i==0) ? w_scale : xyz_scale);
+                ambiup->Gains[i][index][FB_LowFreq] = scale;
+            }
+        }
+    }
+}
+
+void ambiup_process(struct AmbiUpsampler *ambiup, ALfloat (*restrict OutBuffer)[BUFFERSIZE], ALsizei OutChannels, const ALfloat (*restrict InSamples)[BUFFERSIZE], ALsizei SamplesToDo)
+{
+    ALsizei i, j;
+
+    for(i = 0;i < 4;i++)
+    {
+        bandsplit_process(&ambiup->XOver[i],
+            ambiup->Samples[FB_HighFreq], ambiup->Samples[FB_LowFreq],
+            InSamples[i], SamplesToDo
+        );
+
+        for(j = 0;j < OutChannels;j++)
+            MixMatrixRow(OutBuffer[j], ambiup->Gains[i][j],
+                SAFE_CONST(ALfloatBUFFERSIZE*,ambiup->Samples), FB_Max, 0,
+                SamplesToDo
+            );
+    }
+}

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

@@ -0,0 +1,75 @@
+#ifndef BFORMATDEC_H
+#define BFORMATDEC_H
+
+#include "alMain.h"
+
+
+/* These are the necessary scales for first-order HF responses to play over
+ * higher-order 2D (non-periphonic) decoders.
+ */
+#define W_SCALE2D_SECOND   1.224744871f /* sqrt(1.5) */
+#define XYZ_SCALE2D_SECOND 1.0f
+#define W_SCALE2D_THIRD   1.414213562f /* sqrt(2) */
+#define XYZ_SCALE2D_THIRD 1.082392196f
+
+/* These are the necessary scales for first-order HF responses to play over
+ * higher-order 3D (periphonic) decoders.
+ */
+#define W_SCALE3D_SECOND   1.341640787f /* sqrt(1.8) */
+#define XYZ_SCALE3D_SECOND 1.0f
+#define W_SCALE3D_THIRD   1.695486018f
+#define XYZ_SCALE3D_THIRD 1.136697713f
+
+
+struct AmbDecConf;
+struct BFormatDec;
+struct AmbiUpsampler;
+
+
+struct BFormatDec *bformatdec_alloc();
+void bformatdec_free(struct BFormatDec *dec);
+void bformatdec_reset(struct BFormatDec *dec, const struct AmbDecConf *conf, ALsizei chancount, ALuint srate, const ALsizei chanmap[MAX_OUTPUT_CHANNELS]);
+
+/* Decodes the ambisonic input to the given output channels. */
+void bformatdec_process(struct BFormatDec *dec, ALfloat (*restrict OutBuffer)[BUFFERSIZE], ALsizei OutChannels, const ALfloat (*restrict InSamples)[BUFFERSIZE], ALsizei SamplesToDo);
+
+/* Up-samples a first-order input to the decoder's configuration. */
+void bformatdec_upSample(struct BFormatDec *dec, ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat (*restrict InSamples)[BUFFERSIZE], ALsizei InChannels, ALsizei SamplesToDo);
+
+
+/* Stand-alone first-order upsampler. Kept here because it shares some stuff
+ * with bformatdec.
+ */
+struct AmbiUpsampler *ambiup_alloc();
+void ambiup_free(struct AmbiUpsampler *ambiup);
+void ambiup_reset(struct AmbiUpsampler *ambiup, const ALCdevice *device);
+
+void ambiup_process(struct AmbiUpsampler *ambiup, ALfloat (*restrict OutBuffer)[BUFFERSIZE], 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 freq_mult);
+void bandsplit_clear(BandSplitter *splitter);
+void bandsplit_process(BandSplitter *splitter, ALfloat *restrict hpout, ALfloat *restrict lpout,
+                       const ALfloat *input, ALsizei count);
+
+/* The all-pass portion of the band splitter. Applies the same phase shift
+ * without splitting the signal.
+ */
+typedef struct SplitterAllpass {
+    ALfloat coeff;
+    ALfloat z1;
+} SplitterAllpass;
+
+void splitterap_init(SplitterAllpass *splitter, ALfloat freq_mult);
+void splitterap_clear(SplitterAllpass *splitter);
+void splitterap_process(SplitterAllpass *splitter, ALfloat *restrict samples, ALsizei count);
+
+#endif /* BFORMATDEC_H */

+ 56 - 1
libs/openal-soft/Alc/bs2b.c

@@ -129,4 +129,59 @@ void bs2b_clear(struct bs2b *bs2b)
     memset(&bs2b->last_sample, 0, sizeof(bs2b->last_sample));
 } /* bs2b_clear */
 
-extern inline void bs2b_cross_feed(struct bs2b *bs2b, float *restrict samples);
+void bs2b_cross_feed(struct bs2b *bs2b, float *restrict Left, float *restrict Right, int SamplesToDo)
+{
+    float lsamples[128][2];
+    float rsamples[128][2];
+    int base;
+
+    for(base = 0;base < SamplesToDo;)
+    {
+        int todo = mini(128, SamplesToDo-base);
+        int i;
+
+        /* Process left input */
+        lsamples[0][0] = bs2b->a0_lo*Left[0] +
+                         bs2b->b1_lo*bs2b->last_sample[0].lo;
+        lsamples[0][1] = bs2b->a0_hi*Left[0] +
+                         bs2b->a1_hi*bs2b->last_sample[0].asis +
+                         bs2b->b1_hi*bs2b->last_sample[0].hi;
+        for(i = 1;i < todo;i++)
+        {
+            lsamples[i][0] = bs2b->a0_lo*Left[i] +
+                             bs2b->b1_lo*lsamples[i-1][0];
+            lsamples[i][1] = bs2b->a0_hi*Left[i] +
+                             bs2b->a1_hi*Left[i-1] +
+                             bs2b->b1_hi*lsamples[i-1][1];
+        }
+        bs2b->last_sample[0].asis = Left[i-1];
+        bs2b->last_sample[0].lo = lsamples[i-1][0];
+        bs2b->last_sample[0].hi = lsamples[i-1][1];
+
+        /* Process right input */
+        rsamples[0][0] = bs2b->a0_lo*Right[0] +
+                         bs2b->b1_lo*bs2b->last_sample[1].lo;
+        rsamples[0][1] = bs2b->a0_hi*Right[0] +
+                         bs2b->a1_hi*bs2b->last_sample[1].asis +
+                         bs2b->b1_hi*bs2b->last_sample[1].hi;
+        for(i = 1;i < todo;i++)
+        {
+            rsamples[i][0] = bs2b->a0_lo*Right[i] +
+                             bs2b->b1_lo*rsamples[i-1][0];
+            rsamples[i][1] = bs2b->a0_hi*Right[i] +
+                             bs2b->a1_hi*Right[i-1] +
+                             bs2b->b1_hi*rsamples[i-1][1];
+        }
+        bs2b->last_sample[1].asis = Right[i-1];
+        bs2b->last_sample[1].lo = rsamples[i-1][0];
+        bs2b->last_sample[1].hi = rsamples[i-1][1];
+
+        /* Crossfeed */
+        for(i = 0;i < todo;i++)
+            *(Left++) = lsamples[i][1] + rsamples[i][0];
+        for(i = 0;i < todo;i++)
+            *(Right++) = rsamples[i][1] + lsamples[i][0];
+
+        base += todo;
+    }
+} /* bs2b_cross_feed */

+ 4100 - 0
libs/openal-soft/Alc/bsinc.c

@@ -979,3 +979,4103 @@ alignas(16) const ALfloat bsincTab[18840] =
     /* 12,14 */ -3.311614162e-04f, +9.124383184e-04f, -9.731965741e-04f, -9.055999228e-04f, +3.928365474e-03f, -4.572939653e-03f, +1.133926469e-03f, +3.134198780e-03f, -4.073990388e-03f, +1.902468489e-03f, +1.684748882e-04f, -6.042449626e-04f,
     /* 12,15 */ -3.212754781e-04f, +8.205283947e-04f, -6.994376016e-04f, -1.285631838e-03f, +4.064044128e-03f, -4.167864669e-03f, +3.806742210e-04f, +3.684471854e-03f, -4.112050532e-03f, +1.619691310e-03f, +4.291307465e-04f, -7.165252154e-04f
 };
+
+alignas(16) const ALfloat sinc4Tab[4096][4] =
+{
+    { +1.972846707e-17f, +1.000000000e+00f, +1.972846707e-17f, -7.947592825e-19f },
+    { -1.234844220e-04f, +9.999998638e-01f, +1.236324705e-04f, -2.493930525e-06f },
+    { -2.468208365e-04f, +9.999994551e-01f, +2.474130305e-04f, -4.998192808e-06f },
+    { -3.700092848e-04f, +9.999987739e-01f, +3.713417210e-04f, -7.512801591e-06f },
+    { -4.930498082e-04f, +9.999978203e-01f, +4.954185828e-04f, -1.003777162e-05f },
+    { -6.159424479e-04f, +9.999965942e-01f, +6.196436566e-04f, -1.257311763e-05f },
+    { -7.386872455e-04f, +9.999950956e-01f, +7.440169831e-04f, -1.511885438e-05f },
+    { -8.612842424e-04f, +9.999933246e-01f, +8.685386028e-04f, -1.767499661e-05f },
+    { -9.837334803e-04f, +9.999912812e-01f, +9.932085563e-04f, -2.024155907e-05f },
+    { -1.106035001e-03f, +9.999889652e-01f, +1.118026884e-03f, -2.281855651e-05f },
+    { -1.228188846e-03f, +9.999863768e-01f, +1.242993626e-03f, -2.540600368e-05f },
+    { -1.350195058e-03f, +9.999835160e-01f, +1.368108822e-03f, -2.800391533e-05f },
+    { -1.472053677e-03f, +9.999803827e-01f, +1.493372514e-03f, -3.061230620e-05f },
+    { -1.593764748e-03f, +9.999769770e-01f, +1.618784740e-03f, -3.323119106e-05f },
+    { -1.715328311e-03f, +9.999732988e-01f, +1.744345541e-03f, -3.586058466e-05f },
+    { -1.836744408e-03f, +9.999693481e-01f, +1.870054956e-03f, -3.850050175e-05f },
+    { -1.958013083e-03f, +9.999651250e-01f, +1.995913026e-03f, -4.115095708e-05f },
+    { -2.079134377e-03f, +9.999606295e-01f, +2.121919790e-03f, -4.381196542e-05f },
+    { -2.200108333e-03f, +9.999558615e-01f, +2.248075287e-03f, -4.648354151e-05f },
+    { -2.320934994e-03f, +9.999508211e-01f, +2.374379558e-03f, -4.916570012e-05f },
+    { -2.441614402e-03f, +9.999455083e-01f, +2.500832641e-03f, -5.185845600e-05f },
+    { -2.562146601e-03f, +9.999399230e-01f, +2.627434576e-03f, -5.456182391e-05f },
+    { -2.682531632e-03f, +9.999340653e-01f, +2.754185402e-03f, -5.727581862e-05f },
+    { -2.802769538e-03f, +9.999279352e-01f, +2.881085158e-03f, -6.000045487e-05f },
+    { -2.922860364e-03f, +9.999215326e-01f, +3.008133883e-03f, -6.273574744e-05f },
+    { -3.042804151e-03f, +9.999148577e-01f, +3.135331617e-03f, -6.548171107e-05f },
+    { -3.162600944e-03f, +9.999079103e-01f, +3.262678397e-03f, -6.823836055e-05f },
+    { -3.282250785e-03f, +9.999006906e-01f, +3.390174264e-03f, -7.100571061e-05f },
+    { -3.401753717e-03f, +9.998931984e-01f, +3.517819255e-03f, -7.378377604e-05f },
+    { -3.521109785e-03f, +9.998854338e-01f, +3.645613409e-03f, -7.657257158e-05f },
+    { -3.640319031e-03f, +9.998773969e-01f, +3.773556765e-03f, -7.937211202e-05f },
+    { -3.759381500e-03f, +9.998690875e-01f, +3.901649361e-03f, -8.218241209e-05f },
+    { -3.878297235e-03f, +9.998605058e-01f, +4.029891236e-03f, -8.500348659e-05f },
+    { -3.997066279e-03f, +9.998516517e-01f, +4.158282427e-03f, -8.783535025e-05f },
+    { -4.115688677e-03f, +9.998425252e-01f, +4.286822973e-03f, -9.067801786e-05f },
+    { -4.234164473e-03f, +9.998331264e-01f, +4.415512912e-03f, -9.353150418e-05f },
+    { -4.352493710e-03f, +9.998234552e-01f, +4.544352281e-03f, -9.639582397e-05f },
+    { -4.470676433e-03f, +9.998135116e-01f, +4.673341119e-03f, -9.927099199e-05f },
+    { -4.588712686e-03f, +9.998032957e-01f, +4.802479464e-03f, -1.021570230e-04f },
+    { -4.706602513e-03f, +9.997928075e-01f, +4.931767353e-03f, -1.050539318e-04f },
+    { -4.824345959e-03f, +9.997820469e-01f, +5.061204824e-03f, -1.079617332e-04f },
+    { -4.941943068e-03f, +9.997710141e-01f, +5.190791913e-03f, -1.108804418e-04f },
+    { -5.059393885e-03f, +9.997597089e-01f, +5.320528660e-03f, -1.138100725e-04f },
+    { -5.176698454e-03f, +9.997481313e-01f, +5.450415101e-03f, -1.167506400e-04f },
+    { -5.293856820e-03f, +9.997362815e-01f, +5.580451273e-03f, -1.197021592e-04f },
+    { -5.410869028e-03f, +9.997241594e-01f, +5.710637213e-03f, -1.226646447e-04f },
+    { -5.527735122e-03f, +9.997117650e-01f, +5.840972959e-03f, -1.256381113e-04f },
+    { -5.644455148e-03f, +9.996990983e-01f, +5.971458547e-03f, -1.286225739e-04f },
+    { -5.761029151e-03f, +9.996861593e-01f, +6.102094014e-03f, -1.316180471e-04f },
+    { -5.877457176e-03f, +9.996729481e-01f, +6.232879398e-03f, -1.346245458e-04f },
+    { -5.993739268e-03f, +9.996594646e-01f, +6.363814734e-03f, -1.376420846e-04f },
+    { -6.109875472e-03f, +9.996457089e-01f, +6.494900059e-03f, -1.406706784e-04f },
+    { -6.225865834e-03f, +9.996316809e-01f, +6.626135411e-03f, -1.437103420e-04f },
+    { -6.341710400e-03f, +9.996173807e-01f, +6.757520824e-03f, -1.467610900e-04f },
+    { -6.457409214e-03f, +9.996028083e-01f, +6.889056337e-03f, -1.498229373e-04f },
+    { -6.572962323e-03f, +9.995879637e-01f, +7.020741983e-03f, -1.528958987e-04f },
+    { -6.688369772e-03f, +9.995728468e-01f, +7.152577801e-03f, -1.559799888e-04f },
+    { -6.803631606e-03f, +9.995574578e-01f, +7.284563826e-03f, -1.590752225e-04f },
+    { -6.918747873e-03f, +9.995417966e-01f, +7.416700094e-03f, -1.621816145e-04f },
+    { -7.033718618e-03f, +9.995258632e-01f, +7.548986640e-03f, -1.652991797e-04f },
+    { -7.148543887e-03f, +9.995096577e-01f, +7.681423501e-03f, -1.684279326e-04f },
+    { -7.263223725e-03f, +9.994931800e-01f, +7.814010713e-03f, -1.715678882e-04f },
+    { -7.377758181e-03f, +9.994764302e-01f, +7.946748310e-03f, -1.747190612e-04f },
+    { -7.492147298e-03f, +9.994594082e-01f, +8.079636328e-03f, -1.778814663e-04f },
+    { -7.606391125e-03f, +9.994421141e-01f, +8.212674803e-03f, -1.810551183e-04f },
+    { -7.720489707e-03f, +9.994245479e-01f, +8.345863769e-03f, -1.842400320e-04f },
+    { -7.834443092e-03f, +9.994067096e-01f, +8.479203263e-03f, -1.874362221e-04f },
+    { -7.948251325e-03f, +9.993885992e-01f, +8.612693320e-03f, -1.906437034e-04f },
+    { -8.061914453e-03f, +9.993702168e-01f, +8.746333973e-03f, -1.938624906e-04f },
+    { -8.175432524e-03f, +9.993515622e-01f, +8.880125258e-03f, -1.970925985e-04f },
+    { -8.288805583e-03f, +9.993326357e-01f, +9.014067211e-03f, -2.003340419e-04f },
+    { -8.402033679e-03f, +9.993134371e-01f, +9.148159865e-03f, -2.035868356e-04f },
+    { -8.515116858e-03f, +9.992939664e-01f, +9.282403256e-03f, -2.068509942e-04f },
+    { -8.628055168e-03f, +9.992742238e-01f, +9.416797418e-03f, -2.101265325e-04f },
+    { -8.740848654e-03f, +9.992542091e-01f, +9.551342385e-03f, -2.134134654e-04f },
+    { -8.853497366e-03f, +9.992339225e-01f, +9.686038192e-03f, -2.167118075e-04f },
+    { -8.966001349e-03f, +9.992133639e-01f, +9.820884872e-03f, -2.200215735e-04f },
+    { -9.078360653e-03f, +9.991925333e-01f, +9.955882461e-03f, -2.233427784e-04f },
+    { -9.190575323e-03f, +9.991714307e-01f, +1.009103099e-02f, -2.266754367e-04f },
+    { -9.302645409e-03f, +9.991500563e-01f, +1.022633050e-02f, -2.300195634e-04f },
+    { -9.414570956e-03f, +9.991284099e-01f, +1.036178102e-02f, -2.333751730e-04f },
+    { -9.526352014e-03f, +9.991064916e-01f, +1.049738258e-02f, -2.367422804e-04f },
+    { -9.637988631e-03f, +9.990843014e-01f, +1.063313522e-02f, -2.401209002e-04f },
+    { -9.749480853e-03f, +9.990618393e-01f, +1.076903897e-02f, -2.435110474e-04f },
+    { -9.860828729e-03f, +9.990391053e-01f, +1.090509386e-02f, -2.469127365e-04f },
+    { -9.972032308e-03f, +9.990160995e-01f, +1.104129994e-02f, -2.503259824e-04f },
+    { -1.008309164e-02f, +9.989928219e-01f, +1.117765722e-02f, -2.537507998e-04f },
+    { -1.019400677e-02f, +9.989692724e-01f, +1.131416575e-02f, -2.571872034e-04f },
+    { -1.030477774e-02f, +9.989454512e-01f, +1.145082556e-02f, -2.606352080e-04f },
+    { -1.041540461e-02f, +9.989213581e-01f, +1.158763668e-02f, -2.640948284e-04f },
+    { -1.052588743e-02f, +9.988969933e-01f, +1.172459914e-02f, -2.675660791e-04f },
+    { -1.063622623e-02f, +9.988723567e-01f, +1.186171298e-02f, -2.710489751e-04f },
+    { -1.074642108e-02f, +9.988474483e-01f, +1.199897823e-02f, -2.745435310e-04f },
+    { -1.085647202e-02f, +9.988222682e-01f, +1.213639492e-02f, -2.780497616e-04f },
+    { -1.096637910e-02f, +9.987968164e-01f, +1.227396308e-02f, -2.815676816e-04f },
+    { -1.107614236e-02f, +9.987710929e-01f, +1.241168275e-02f, -2.850973058e-04f },
+    { -1.118576186e-02f, +9.987450978e-01f, +1.254955397e-02f, -2.886386488e-04f },
+    { -1.129523765e-02f, +9.987188309e-01f, +1.268757675e-02f, -2.921917254e-04f },
+    { -1.140456977e-02f, +9.986922924e-01f, +1.282575114e-02f, -2.957565503e-04f },
+    { -1.151375828e-02f, +9.986654823e-01f, +1.296407716e-02f, -2.993331383e-04f },
+    { -1.162280322e-02f, +9.986384006e-01f, +1.310255485e-02f, -3.029215041e-04f },
+    { -1.173170465e-02f, +9.986110473e-01f, +1.324118424e-02f, -3.065216624e-04f },
+    { -1.184046260e-02f, +9.985834223e-01f, +1.337996537e-02f, -3.101336280e-04f },
+    { -1.194907714e-02f, +9.985555259e-01f, +1.351889825e-02f, -3.137574155e-04f },
+    { -1.205754831e-02f, +9.985273578e-01f, +1.365798293e-02f, -3.173930397e-04f },
+    { -1.216587616e-02f, +9.984989183e-01f, +1.379721944e-02f, -3.210405152e-04f },
+    { -1.227406075e-02f, +9.984702072e-01f, +1.393660781e-02f, -3.246998569e-04f },
+    { -1.238210211e-02f, +9.984412247e-01f, +1.407614806e-02f, -3.283710794e-04f },
+    { -1.249000030e-02f, +9.984119707e-01f, +1.421584024e-02f, -3.320541975e-04f },
+    { -1.259775538e-02f, +9.983824452e-01f, +1.435568436e-02f, -3.357492258e-04f },
+    { -1.270536739e-02f, +9.983526483e-01f, +1.449568047e-02f, -3.394561791e-04f },
+    { -1.281283638e-02f, +9.983225800e-01f, +1.463582860e-02f, -3.431750720e-04f },
+    { -1.292016240e-02f, +9.982922403e-01f, +1.477612876e-02f, -3.469059193e-04f },
+    { -1.302734550e-02f, +9.982616292e-01f, +1.491658101e-02f, -3.506487357e-04f },
+    { -1.313438573e-02f, +9.982307467e-01f, +1.505718535e-02f, -3.544035359e-04f },
+    { -1.324128315e-02f, +9.981995929e-01f, +1.519794184e-02f, -3.581703346e-04f },
+    { -1.334803780e-02f, +9.981681678e-01f, +1.533885049e-02f, -3.619491465e-04f },
+    { -1.345464973e-02f, +9.981364714e-01f, +1.547991134e-02f, -3.657399862e-04f },
+    { -1.356111900e-02f, +9.981045038e-01f, +1.562112441e-02f, -3.695428686e-04f },
+    { -1.366744566e-02f, +9.980722648e-01f, +1.576248975e-02f, -3.733578082e-04f },
+    { -1.377362975e-02f, +9.980397546e-01f, +1.590400737e-02f, -3.771848197e-04f },
+    { -1.387967133e-02f, +9.980069732e-01f, +1.604567730e-02f, -3.810239179e-04f },
+    { -1.398557045e-02f, +9.979739207e-01f, +1.618749959e-02f, -3.848751175e-04f },
+    { -1.409132716e-02f, +9.979405969e-01f, +1.632947425e-02f, -3.887384331e-04f },
+    { -1.419694151e-02f, +9.979070020e-01f, +1.647160132e-02f, -3.926138794e-04f },
+    { -1.430241355e-02f, +9.978731359e-01f, +1.661388082e-02f, -3.965014710e-04f },
+    { -1.440774333e-02f, +9.978389988e-01f, +1.675631280e-02f, -4.004012227e-04f },
+    { -1.451293091e-02f, +9.978045905e-01f, +1.689889726e-02f, -4.043131492e-04f },
+    { -1.461797634e-02f, +9.977699112e-01f, +1.704163425e-02f, -4.082372651e-04f },
+    { -1.472287966e-02f, +9.977349608e-01f, +1.718452380e-02f, -4.121735850e-04f },
+    { -1.482764093e-02f, +9.976997395e-01f, +1.732756592e-02f, -4.161221238e-04f },
+    { -1.493226021e-02f, +9.976642471e-01f, +1.747076066e-02f, -4.200828959e-04f },
+    { -1.503673754e-02f, +9.976284837e-01f, +1.761410804e-02f, -4.240559161e-04f },
+    { -1.514107297e-02f, +9.975924494e-01f, +1.775760809e-02f, -4.280411991e-04f },
+    { -1.524526657e-02f, +9.975561442e-01f, +1.790126083e-02f, -4.320387594e-04f },
+    { -1.534931837e-02f, +9.975195680e-01f, +1.804506631e-02f, -4.360486119e-04f },
+    { -1.545322843e-02f, +9.974827210e-01f, +1.818902453e-02f, -4.400707710e-04f },
+    { -1.555699680e-02f, +9.974456031e-01f, +1.833313554e-02f, -4.441052515e-04f },
+    { -1.566062355e-02f, +9.974082143e-01f, +1.847739937e-02f, -4.481520680e-04f },
+    { -1.576410871e-02f, +9.973705548e-01f, +1.862181603e-02f, -4.522112352e-04f },
+    { -1.586745234e-02f, +9.973326244e-01f, +1.876638556e-02f, -4.562827677e-04f },
+    { -1.597065449e-02f, +9.972944233e-01f, +1.891110799e-02f, -4.603666801e-04f },
+    { -1.607371522e-02f, +9.972559515e-01f, +1.905598334e-02f, -4.644629872e-04f },
+    { -1.617663458e-02f, +9.972172089e-01f, +1.920101164e-02f, -4.685717034e-04f },
+    { -1.627941262e-02f, +9.971781956e-01f, +1.934619292e-02f, -4.726928435e-04f },
+    { -1.638204939e-02f, +9.971389117e-01f, +1.949152721e-02f, -4.768264221e-04f },
+    { -1.648454495e-02f, +9.970993571e-01f, +1.963701453e-02f, -4.809724538e-04f },
+    { -1.658689935e-02f, +9.970595319e-01f, +1.978265492e-02f, -4.851309533e-04f },
+    { -1.668911265e-02f, +9.970194362e-01f, +1.992844839e-02f, -4.893019351e-04f },
+    { -1.679118489e-02f, +9.969790698e-01f, +2.007439498e-02f, -4.934854139e-04f },
+    { -1.689311613e-02f, +9.969384329e-01f, +2.022049471e-02f, -4.976814042e-04f },
+    { -1.699490642e-02f, +9.968975255e-01f, +2.036674762e-02f, -5.018899208e-04f },
+    { -1.709655581e-02f, +9.968563476e-01f, +2.051315372e-02f, -5.061109782e-04f },
+    { -1.719806437e-02f, +9.968148993e-01f, +2.065971304e-02f, -5.103445911e-04f },
+    { -1.729943214e-02f, +9.967731805e-01f, +2.080642562e-02f, -5.145907739e-04f },
+    { -1.740065918e-02f, +9.967311913e-01f, +2.095329147e-02f, -5.188495414e-04f },
+    { -1.750174553e-02f, +9.966889317e-01f, +2.110031063e-02f, -5.231209082e-04f },
+    { -1.760269126e-02f, +9.966464017e-01f, +2.124748312e-02f, -5.274048887e-04f },
+    { -1.770349642e-02f, +9.966036014e-01f, +2.139480896e-02f, -5.317014977e-04f },
+    { -1.780416106e-02f, +9.965605309e-01f, +2.154228819e-02f, -5.360107497e-04f },
+    { -1.790468523e-02f, +9.965171900e-01f, +2.168992083e-02f, -5.403326592e-04f },
+    { -1.800506899e-02f, +9.964735789e-01f, +2.183770690e-02f, -5.446672410e-04f },
+    { -1.810531240e-02f, +9.964296976e-01f, +2.198564643e-02f, -5.490145094e-04f },
+    { -1.820541550e-02f, +9.963855461e-01f, +2.213373945e-02f, -5.533744792e-04f },
+    { -1.830537836e-02f, +9.963411244e-01f, +2.228198598e-02f, -5.577471650e-04f },
+    { -1.840520102e-02f, +9.962964326e-01f, +2.243038605e-02f, -5.621325811e-04f },
+    { -1.850488354e-02f, +9.962514707e-01f, +2.257893968e-02f, -5.665307423e-04f },
+    { -1.860442598e-02f, +9.962062387e-01f, +2.272764690e-02f, -5.709416631e-04f },
+    { -1.870382839e-02f, +9.961607366e-01f, +2.287650774e-02f, -5.753653581e-04f },
+    { -1.880309082e-02f, +9.961149645e-01f, +2.302552221e-02f, -5.798018418e-04f },
+    { -1.890221333e-02f, +9.960689225e-01f, +2.317469035e-02f, -5.842511287e-04f },
+    { -1.900119597e-02f, +9.960226105e-01f, +2.332401218e-02f, -5.887132334e-04f },
+    { -1.910003880e-02f, +9.959760285e-01f, +2.347348772e-02f, -5.931881705e-04f },
+    { -1.919874188e-02f, +9.959291766e-01f, +2.362311700e-02f, -5.976759545e-04f },
+    { -1.929730525e-02f, +9.958820549e-01f, +2.377290004e-02f, -6.021765999e-04f },
+    { -1.939572898e-02f, +9.958346633e-01f, +2.392283688e-02f, -6.066901213e-04f },
+    { -1.949401312e-02f, +9.957870019e-01f, +2.407292752e-02f, -6.112165332e-04f },
+    { -1.959215772e-02f, +9.957390708e-01f, +2.422317200e-02f, -6.157558500e-04f },
+    { -1.969016283e-02f, +9.956908698e-01f, +2.437357035e-02f, -6.203080865e-04f },
+    { -1.978802853e-02f, +9.956423992e-01f, +2.452412258e-02f, -6.248732570e-04f },
+    { -1.988575485e-02f, +9.955936589e-01f, +2.467482872e-02f, -6.294513760e-04f },
+    { -1.998334185e-02f, +9.955446489e-01f, +2.482568879e-02f, -6.340424582e-04f },
+    { -2.008078960e-02f, +9.954953692e-01f, +2.497670282e-02f, -6.386465180e-04f },
+    { -2.017809815e-02f, +9.954458200e-01f, +2.512787083e-02f, -6.432635698e-04f },
+    { -2.027526754e-02f, +9.953960012e-01f, +2.527919285e-02f, -6.478936283e-04f },
+    { -2.037229785e-02f, +9.953459129e-01f, +2.543066889e-02f, -6.525367078e-04f },
+    { -2.046918912e-02f, +9.952955551e-01f, +2.558229899e-02f, -6.571928229e-04f },
+    { -2.056594140e-02f, +9.952449278e-01f, +2.573408316e-02f, -6.618619881e-04f },
+    { -2.066255477e-02f, +9.951940310e-01f, +2.588602143e-02f, -6.665442179e-04f },
+    { -2.075902926e-02f, +9.951428649e-01f, +2.603811383e-02f, -6.712395266e-04f },
+    { -2.085536494e-02f, +9.950914294e-01f, +2.619036036e-02f, -6.759479289e-04f },
+    { -2.095156187e-02f, +9.950397245e-01f, +2.634276107e-02f, -6.806694392e-04f },
+    { -2.104762010e-02f, +9.949877504e-01f, +2.649531596e-02f, -6.854040719e-04f },
+    { -2.114353968e-02f, +9.949355069e-01f, +2.664802507e-02f, -6.901518415e-04f },
+    { -2.123932068e-02f, +9.948829943e-01f, +2.680088842e-02f, -6.949127625e-04f },
+    { -2.133496314e-02f, +9.948302124e-01f, +2.695390602e-02f, -6.996868493e-04f },
+    { -2.143046713e-02f, +9.947771613e-01f, +2.710707790e-02f, -7.044741164e-04f },
+    { -2.152583271e-02f, +9.947238411e-01f, +2.726040409e-02f, -7.092745782e-04f },
+    { -2.162105992e-02f, +9.946702518e-01f, +2.741388460e-02f, -7.140882492e-04f },
+    { -2.171614883e-02f, +9.946163934e-01f, +2.756751946e-02f, -7.189151437e-04f },
+    { -2.181109950e-02f, +9.945622660e-01f, +2.772130869e-02f, -7.237552763e-04f },
+    { -2.190591197e-02f, +9.945078696e-01f, +2.787525232e-02f, -7.286086614e-04f },
+    { -2.200058631e-02f, +9.944532042e-01f, +2.802935035e-02f, -7.334753133e-04f },
+    { -2.209512257e-02f, +9.943982699e-01f, +2.818360282e-02f, -7.383552465e-04f },
+    { -2.218952082e-02f, +9.943430666e-01f, +2.833800975e-02f, -7.432484754e-04f },
+    { -2.228378110e-02f, +9.942875945e-01f, +2.849257115e-02f, -7.481550144e-04f },
+    { -2.237790348e-02f, +9.942318536e-01f, +2.864728706e-02f, -7.530748780e-04f },
+    { -2.247188802e-02f, +9.941758439e-01f, +2.880215748e-02f, -7.580080805e-04f },
+    { -2.256573476e-02f, +9.941195654e-01f, +2.895718245e-02f, -7.629546363e-04f },
+    { -2.265944377e-02f, +9.940630182e-01f, +2.911236198e-02f, -7.679145598e-04f },
+    { -2.275301511e-02f, +9.940062023e-01f, +2.926769610e-02f, -7.728878654e-04f },
+    { -2.284644883e-02f, +9.939491178e-01f, +2.942318481e-02f, -7.778745675e-04f },
+    { -2.293974499e-02f, +9.938917647e-01f, +2.957882816e-02f, -7.828746805e-04f },
+    { -2.303290364e-02f, +9.938341429e-01f, +2.973462615e-02f, -7.878882187e-04f },
+    { -2.312592486e-02f, +9.937762527e-01f, +2.989057881e-02f, -7.929151965e-04f },
+    { -2.321880869e-02f, +9.937180939e-01f, +3.004668616e-02f, -7.979556282e-04f },
+    { -2.331155519e-02f, +9.936596666e-01f, +3.020294821e-02f, -8.030095283e-04f },
+    { -2.340416442e-02f, +9.936009710e-01f, +3.035936500e-02f, -8.080769110e-04f },
+    { -2.349663644e-02f, +9.935420069e-01f, +3.051593653e-02f, -8.131577908e-04f },
+    { -2.358897131e-02f, +9.934827745e-01f, +3.067266283e-02f, -8.182521819e-04f },
+    { -2.368116908e-02f, +9.934232738e-01f, +3.082954391e-02f, -8.233600987e-04f },
+    { -2.377322981e-02f, +9.933635047e-01f, +3.098657981e-02f, -8.284815556e-04f },
+    { -2.386515357e-02f, +9.933034675e-01f, +3.114377053e-02f, -8.336165668e-04f },
+    { -2.395694040e-02f, +9.932431620e-01f, +3.130111610e-02f, -8.387651466e-04f },
+    { -2.404859038e-02f, +9.931825884e-01f, +3.145861654e-02f, -8.439273095e-04f },
+    { -2.414010355e-02f, +9.931217466e-01f, +3.161627187e-02f, -8.491030697e-04f },
+    { -2.423147998e-02f, +9.930606368e-01f, +3.177408210e-02f, -8.542924415e-04f },
+    { -2.432271972e-02f, +9.929992589e-01f, +3.193204725e-02f, -8.594954392e-04f },
+    { -2.441382283e-02f, +9.929376130e-01f, +3.209016735e-02f, -8.647120771e-04f },
+    { -2.450478938e-02f, +9.928756991e-01f, +3.224844242e-02f, -8.699423695e-04f },
+    { -2.459561942e-02f, +9.928135173e-01f, +3.240687246e-02f, -8.751863308e-04f },
+    { -2.468631300e-02f, +9.927510676e-01f, +3.256545750e-02f, -8.804439750e-04f },
+    { -2.477687020e-02f, +9.926883501e-01f, +3.272419757e-02f, -8.857153167e-04f },
+    { -2.486729107e-02f, +9.926253647e-01f, +3.288309267e-02f, -8.910003699e-04f },
+    { -2.495757566e-02f, +9.925621116e-01f, +3.304214283e-02f, -8.962991490e-04f },
+    { -2.504772404e-02f, +9.924985907e-01f, +3.320134807e-02f, -9.016116683e-04f },
+    { -2.513773626e-02f, +9.924348021e-01f, +3.336070839e-02f, -9.069379419e-04f },
+    { -2.522761239e-02f, +9.923707459e-01f, +3.352022383e-02f, -9.122779842e-04f },
+    { -2.531735249e-02f, +9.923064221e-01f, +3.367989440e-02f, -9.176318093e-04f },
+    { -2.540695661e-02f, +9.922418307e-01f, +3.383972011e-02f, -9.229994316e-04f },
+    { -2.549642481e-02f, +9.921769718e-01f, +3.399970099e-02f, -9.283808652e-04f },
+    { -2.558575716e-02f, +9.921118453e-01f, +3.415983706e-02f, -9.337761243e-04f },
+    { -2.567495371e-02f, +9.920464515e-01f, +3.432012832e-02f, -9.391852233e-04f },
+    { -2.576401452e-02f, +9.919807902e-01f, +3.448057480e-02f, -9.446081762e-04f },
+    { -2.585293966e-02f, +9.919148616e-01f, +3.464117652e-02f, -9.500449974e-04f },
+    { -2.594172918e-02f, +9.918486656e-01f, +3.480193349e-02f, -9.554957009e-04f },
+    { -2.603038315e-02f, +9.917822024e-01f, +3.496284573e-02f, -9.609603011e-04f },
+    { -2.611890161e-02f, +9.917154719e-01f, +3.512391325e-02f, -9.664388120e-04f },
+    { -2.620728464e-02f, +9.916484743e-01f, +3.528513608e-02f, -9.719312480e-04f },
+    { -2.629553230e-02f, +9.915812094e-01f, +3.544651423e-02f, -9.774376231e-04f },
+    { -2.638364463e-02f, +9.915136775e-01f, +3.560804772e-02f, -9.829579515e-04f },
+    { -2.647162171e-02f, +9.914458785e-01f, +3.576973656e-02f, -9.884922474e-04f },
+    { -2.655946360e-02f, +9.913778125e-01f, +3.593158078e-02f, -9.940405249e-04f },
+    { -2.664717035e-02f, +9.913094795e-01f, +3.609358038e-02f, -9.996027983e-04f },
+    { -2.673474203e-02f, +9.912408796e-01f, +3.625573538e-02f, -1.005179082e-03f },
+    { -2.682217869e-02f, +9.911720128e-01f, +3.641804581e-02f, -1.010769389e-03f },
+    { -2.690948040e-02f, +9.911028791e-01f, +3.658051167e-02f, -1.016373735e-03f },
+    { -2.699664722e-02f, +9.910334786e-01f, +3.674313298e-02f, -1.021992133e-03f },
+    { -2.708367921e-02f, +9.909638114e-01f, +3.690590976e-02f, -1.027624597e-03f },
+    { -2.717057642e-02f, +9.908938774e-01f, +3.706884203e-02f, -1.033271142e-03f },
+    { -2.725733893e-02f, +9.908236768e-01f, +3.723192979e-02f, -1.038931782e-03f },
+    { -2.734396678e-02f, +9.907532095e-01f, +3.739517307e-02f, -1.044606531e-03f },
+    { -2.743046005e-02f, +9.906824756e-01f, +3.755857188e-02f, -1.050295402e-03f },
+    { -2.751681879e-02f, +9.906114753e-01f, +3.772212623e-02f, -1.055998411e-03f },
+    { -2.760304307e-02f, +9.905402084e-01f, +3.788583615e-02f, -1.061715570e-03f },
+    { -2.768913294e-02f, +9.904686751e-01f, +3.804970164e-02f, -1.067446895e-03f },
+    { -2.777508847e-02f, +9.903968753e-01f, +3.821372273e-02f, -1.073192399e-03f },
+    { -2.786090972e-02f, +9.903248092e-01f, +3.837789942e-02f, -1.078952096e-03f },
+    { -2.794659675e-02f, +9.902524768e-01f, +3.854223173e-02f, -1.084726001e-03f },
+    { -2.803214962e-02f, +9.901798782e-01f, +3.870671968e-02f, -1.090514127e-03f },
+    { -2.811756839e-02f, +9.901070133e-01f, +3.887136328e-02f, -1.096316489e-03f },
+    { -2.820285313e-02f, +9.900338822e-01f, +3.903616254e-02f, -1.102133100e-03f },
+    { -2.828800389e-02f, +9.899604850e-01f, +3.920111748e-02f, -1.107963974e-03f },
+    { -2.837302075e-02f, +9.898868218e-01f, +3.936622812e-02f, -1.113809126e-03f },
+    { -2.845790375e-02f, +9.898128925e-01f, +3.953149447e-02f, -1.119668570e-03f },
+    { -2.854265296e-02f, +9.897386972e-01f, +3.969691653e-02f, -1.125542319e-03f },
+    { -2.862726845e-02f, +9.896642360e-01f, +3.986249434e-02f, -1.131430388e-03f },
+    { -2.871175028e-02f, +9.895895088e-01f, +4.002822789e-02f, -1.137332791e-03f },
+    { -2.879609850e-02f, +9.895145159e-01f, +4.019411721e-02f, -1.143249541e-03f },
+    { -2.888031318e-02f, +9.894392571e-01f, +4.036016230e-02f, -1.149180652e-03f },
+    { -2.896439439e-02f, +9.893637326e-01f, +4.052636319e-02f, -1.155126140e-03f },
+    { -2.904834218e-02f, +9.892879424e-01f, +4.069271988e-02f, -1.161086016e-03f },
+    { -2.913215661e-02f, +9.892118865e-01f, +4.085923238e-02f, -1.167060297e-03f },
+    { -2.921583776e-02f, +9.891355651e-01f, +4.102590072e-02f, -1.173048994e-03f },
+    { -2.929938568e-02f, +9.890589780e-01f, +4.119272490e-02f, -1.179052123e-03f },
+    { -2.938280043e-02f, +9.889821255e-01f, +4.135970494e-02f, -1.185069698e-03f },
+    { -2.946608208e-02f, +9.889050075e-01f, +4.152684085e-02f, -1.191101731e-03f },
+    { -2.954923069e-02f, +9.888276241e-01f, +4.169413264e-02f, -1.197148238e-03f },
+    { -2.963224632e-02f, +9.887499753e-01f, +4.186158033e-02f, -1.203209232e-03f },
+    { -2.971512904e-02f, +9.886720613e-01f, +4.202918393e-02f, -1.209284727e-03f },
+    { -2.979787891e-02f, +9.885938819e-01f, +4.219694344e-02f, -1.215374737e-03f },
+    { -2.988049599e-02f, +9.885154374e-01f, +4.236485889e-02f, -1.221479275e-03f },
+    { -2.996298034e-02f, +9.884367276e-01f, +4.253293028e-02f, -1.227598357e-03f },
+    { -3.004533202e-02f, +9.883577528e-01f, +4.270115763e-02f, -1.233731995e-03f },
+    { -3.012755111e-02f, +9.882785129e-01f, +4.286954095e-02f, -1.239880203e-03f },
+    { -3.020963766e-02f, +9.881990080e-01f, +4.303808025e-02f, -1.246042996e-03f },
+    { -3.029159174e-02f, +9.881192381e-01f, +4.320677555e-02f, -1.252220388e-03f },
+    { -3.037341341e-02f, +9.880392034e-01f, +4.337562684e-02f, -1.258412391e-03f },
+    { -3.045510273e-02f, +9.879589037e-01f, +4.354463416e-02f, -1.264619020e-03f },
+    { -3.053665977e-02f, +9.878783393e-01f, +4.371379750e-02f, -1.270840289e-03f },
+    { -3.061808459e-02f, +9.877975101e-01f, +4.388311688e-02f, -1.277076211e-03f },
+    { -3.069937725e-02f, +9.877164161e-01f, +4.405259231e-02f, -1.283326801e-03f },
+    { -3.078053782e-02f, +9.876350576e-01f, +4.422222380e-02f, -1.289592072e-03f },
+    { -3.086156636e-02f, +9.875534344e-01f, +4.439201136e-02f, -1.295872038e-03f },
+    { -3.094246293e-02f, +9.874715467e-01f, +4.456195501e-02f, -1.302166714e-03f },
+    { -3.102322761e-02f, +9.873893944e-01f, +4.473205475e-02f, -1.308476111e-03f },
+    { -3.110386044e-02f, +9.873069778e-01f, +4.490231060e-02f, -1.314800245e-03f },
+    { -3.118436150e-02f, +9.872242967e-01f, +4.507272256e-02f, -1.321139130e-03f },
+    { -3.126473085e-02f, +9.871413513e-01f, +4.524329065e-02f, -1.327492778e-03f },
+    { -3.134496856e-02f, +9.870581415e-01f, +4.541401487e-02f, -1.333861204e-03f },
+    { -3.142507468e-02f, +9.869746676e-01f, +4.558489524e-02f, -1.340244422e-03f },
+    { -3.150504929e-02f, +9.868909295e-01f, +4.575593177e-02f, -1.346642444e-03f },
+    { -3.158489244e-02f, +9.868069272e-01f, +4.592712446e-02f, -1.353055286e-03f },
+    { -3.166460421e-02f, +9.867226608e-01f, +4.609847333e-02f, -1.359482960e-03f },
+    { -3.174418465e-02f, +9.866381305e-01f, +4.626997838e-02f, -1.365925481e-03f },
+    { -3.182363382e-02f, +9.865533361e-01f, +4.644163964e-02f, -1.372382862e-03f },
+    { -3.190295181e-02f, +9.864682779e-01f, +4.661345709e-02f, -1.378855117e-03f },
+    { -3.198213866e-02f, +9.863829557e-01f, +4.678543077e-02f, -1.385342260e-03f },
+    { -3.206119444e-02f, +9.862973698e-01f, +4.695756066e-02f, -1.391844303e-03f },
+    { -3.214011922e-02f, +9.862115201e-01f, +4.712984679e-02f, -1.398361262e-03f },
+    { -3.221891307e-02f, +9.861254067e-01f, +4.730228917e-02f, -1.404893149e-03f },
+    { -3.229757604e-02f, +9.860390297e-01f, +4.747488779e-02f, -1.411439979e-03f },
+    { -3.237610820e-02f, +9.859523890e-01f, +4.764764268e-02f, -1.418001764e-03f },
+    { -3.245450962e-02f, +9.858654848e-01f, +4.782055384e-02f, -1.424578519e-03f },
+    { -3.253278036e-02f, +9.857783172e-01f, +4.799362127e-02f, -1.431170257e-03f },
+    { -3.261092049e-02f, +9.856908861e-01f, +4.816684499e-02f, -1.437776992e-03f },
+    { -3.268893007e-02f, +9.856031917e-01f, +4.834022501e-02f, -1.444398738e-03f },
+    { -3.276680917e-02f, +9.855152339e-01f, +4.851376133e-02f, -1.451035507e-03f },
+    { -3.284455785e-02f, +9.854270129e-01f, +4.868745396e-02f, -1.457687314e-03f },
+    { -3.292217617e-02f, +9.853385286e-01f, +4.886130292e-02f, -1.464354173e-03f },
+    { -3.299966421e-02f, +9.852497812e-01f, +4.903530820e-02f, -1.471036096e-03f },
+    { -3.307702203e-02f, +9.851607708e-01f, +4.920946982e-02f, -1.477733098e-03f },
+    { -3.315424969e-02f, +9.850714973e-01f, +4.938378778e-02f, -1.484445192e-03f },
+    { -3.323134726e-02f, +9.849819608e-01f, +4.955826209e-02f, -1.491172391e-03f },
+    { -3.330831480e-02f, +9.848921614e-01f, +4.973289276e-02f, -1.497914710e-03f },
+    { -3.338515239e-02f, +9.848020991e-01f, +4.990767980e-02f, -1.504672161e-03f },
+    { -3.346186007e-02f, +9.847117740e-01f, +5.008262322e-02f, -1.511444759e-03f },
+    { -3.353843793e-02f, +9.846211862e-01f, +5.025772301e-02f, -1.518232516e-03f },
+    { -3.361488603e-02f, +9.845303356e-01f, +5.043297919e-02f, -1.525035446e-03f },
+    { -3.369120443e-02f, +9.844392225e-01f, +5.060839177e-02f, -1.531853564e-03f },
+    { -3.376739320e-02f, +9.843478467e-01f, +5.078396076e-02f, -1.538686881e-03f },
+    { -3.384345240e-02f, +9.842562085e-01f, +5.095968615e-02f, -1.545535412e-03f },
+    { -3.391938210e-02f, +9.841643077e-01f, +5.113556795e-02f, -1.552399171e-03f },
+    { -3.399518237e-02f, +9.840721446e-01f, +5.131160618e-02f, -1.559278170e-03f },
+    { -3.407085327e-02f, +9.839797191e-01f, +5.148780084e-02f, -1.566172424e-03f },
+    { -3.414639488e-02f, +9.838870314e-01f, +5.166415193e-02f, -1.573081945e-03f },
+    { -3.422180724e-02f, +9.837940814e-01f, +5.184065947e-02f, -1.580006747e-03f },
+    { -3.429709044e-02f, +9.837008692e-01f, +5.201732345e-02f, -1.586946844e-03f },
+    { -3.437224453e-02f, +9.836073950e-01f, +5.219414389e-02f, -1.593902249e-03f },
+    { -3.444726959e-02f, +9.835136587e-01f, +5.237112079e-02f, -1.600872975e-03f },
+    { -3.452216568e-02f, +9.834196603e-01f, +5.254825415e-02f, -1.607859036e-03f },
+    { -3.459693287e-02f, +9.833254001e-01f, +5.272554398e-02f, -1.614860445e-03f },
+    { -3.467157122e-02f, +9.832308780e-01f, +5.290299029e-02f, -1.621877216e-03f },
+    { -3.474608080e-02f, +9.831360941e-01f, +5.308059308e-02f, -1.628909362e-03f },
+    { -3.482046167e-02f, +9.830410484e-01f, +5.325835236e-02f, -1.635956896e-03f },
+    { -3.489471391e-02f, +9.829457410e-01f, +5.343626813e-02f, -1.643019832e-03f },
+    { -3.496883758e-02f, +9.828501720e-01f, +5.361434040e-02f, -1.650098183e-03f },
+    { -3.504283275e-02f, +9.827543414e-01f, +5.379256918e-02f, -1.657191962e-03f },
+    { -3.511669948e-02f, +9.826582494e-01f, +5.397095446e-02f, -1.664301183e-03f },
+    { -3.519043784e-02f, +9.825618958e-01f, +5.414949625e-02f, -1.671425859e-03f },
+    { -3.526404790e-02f, +9.824652809e-01f, +5.432819456e-02f, -1.678566004e-03f },
+    { -3.533752973e-02f, +9.823684047e-01f, +5.450704939e-02f, -1.685721630e-03f },
+    { -3.541088338e-02f, +9.822712671e-01f, +5.468606075e-02f, -1.692892751e-03f },
+    { -3.548410894e-02f, +9.821738684e-01f, +5.486522863e-02f, -1.700079381e-03f },
+    { -3.555720646e-02f, +9.820762086e-01f, +5.504455306e-02f, -1.707281532e-03f },
+    { -3.563017602e-02f, +9.819782876e-01f, +5.522403402e-02f, -1.714499218e-03f },
+    { -3.570301768e-02f, +9.818801056e-01f, +5.540367152e-02f, -1.721732452e-03f },
+    { -3.577573150e-02f, +9.817816627e-01f, +5.558346557e-02f, -1.728981247e-03f },
+    { -3.584831757e-02f, +9.816829589e-01f, +5.576341617e-02f, -1.736245617e-03f },
+    { -3.592077593e-02f, +9.815839943e-01f, +5.594352333e-02f, -1.743525575e-03f },
+    { -3.599310667e-02f, +9.814847688e-01f, +5.612378704e-02f, -1.750821133e-03f },
+    { -3.606530984e-02f, +9.813852827e-01f, +5.630420731e-02f, -1.758132306e-03f },
+    { -3.613738552e-02f, +9.812855360e-01f, +5.648478415e-02f, -1.765459107e-03f },
+    { -3.620933378e-02f, +9.811855286e-01f, +5.666551755e-02f, -1.772801548e-03f },
+    { -3.628115468e-02f, +9.810852607e-01f, +5.684640752e-02f, -1.780159642e-03f },
+    { -3.635284829e-02f, +9.809847324e-01f, +5.702745407e-02f, -1.787533404e-03f },
+    { -3.642441467e-02f, +9.808839437e-01f, +5.720865720e-02f, -1.794922846e-03f },
+    { -3.649585390e-02f, +9.807828947e-01f, +5.739001690e-02f, -1.802327981e-03f },
+    { -3.656716604e-02f, +9.806815854e-01f, +5.757153318e-02f, -1.809748823e-03f },
+    { -3.663835117e-02f, +9.805800159e-01f, +5.775320605e-02f, -1.817185384e-03f },
+    { -3.670940934e-02f, +9.804781863e-01f, +5.793503550e-02f, -1.824637677e-03f },
+    { -3.678034064e-02f, +9.803760966e-01f, +5.811702155e-02f, -1.832105717e-03f },
+    { -3.685114512e-02f, +9.802737469e-01f, +5.829916418e-02f, -1.839589515e-03f },
+    { -3.692182285e-02f, +9.801711372e-01f, +5.848146341e-02f, -1.847089085e-03f },
+    { -3.699237391e-02f, +9.800682677e-01f, +5.866391923e-02f, -1.854604440e-03f },
+    { -3.706279835e-02f, +9.799651384e-01f, +5.884653165e-02f, -1.862135594e-03f },
+    { -3.713309626e-02f, +9.798617493e-01f, +5.902930066e-02f, -1.869682558e-03f },
+    { -3.720326769e-02f, +9.797581006e-01f, +5.921222628e-02f, -1.877245347e-03f },
+    { -3.727331272e-02f, +9.796541922e-01f, +5.939530849e-02f, -1.884823973e-03f },
+    { -3.734323142e-02f, +9.795500243e-01f, +5.957854731e-02f, -1.892418449e-03f },
+    { -3.741302385e-02f, +9.794455969e-01f, +5.976194274e-02f, -1.900028788e-03f },
+    { -3.748269008e-02f, +9.793409101e-01f, +5.994549476e-02f, -1.907655004e-03f },
+    { -3.755223018e-02f, +9.792359640e-01f, +6.012920340e-02f, -1.915297108e-03f },
+    { -3.762164423e-02f, +9.791307585e-01f, +6.031306864e-02f, -1.922955116e-03f },
+    { -3.769093228e-02f, +9.790252939e-01f, +6.049709048e-02f, -1.930629038e-03f },
+    { -3.776009441e-02f, +9.789195701e-01f, +6.068126894e-02f, -1.938318888e-03f },
+    { -3.782913069e-02f, +9.788135872e-01f, +6.086560401e-02f, -1.946024680e-03f },
+    { -3.789804118e-02f, +9.787073453e-01f, +6.105009568e-02f, -1.953746425e-03f },
+    { -3.796682595e-02f, +9.786008444e-01f, +6.123474396e-02f, -1.961484138e-03f },
+    { -3.803548508e-02f, +9.784940847e-01f, +6.141954886e-02f, -1.969237830e-03f },
+    { -3.810401864e-02f, +9.783870661e-01f, +6.160451036e-02f, -1.977007515e-03f },
+    { -3.817242668e-02f, +9.782797888e-01f, +6.178962847e-02f, -1.984793206e-03f },
+    { -3.824070929e-02f, +9.781722528e-01f, +6.197490319e-02f, -1.992594915e-03f },
+    { -3.830886653e-02f, +9.780644583e-01f, +6.216033452e-02f, -2.000412656e-03f },
+    { -3.837689847e-02f, +9.779564051e-01f, +6.234592246e-02f, -2.008246441e-03f },
+    { -3.844480518e-02f, +9.778480935e-01f, +6.253166701e-02f, -2.016096283e-03f },
+    { -3.851258673e-02f, +9.777395235e-01f, +6.271756817e-02f, -2.023962196e-03f },
+    { -3.858024318e-02f, +9.776306952e-01f, +6.290362593e-02f, -2.031844191e-03f },
+    { -3.864777462e-02f, +9.775216086e-01f, +6.308984030e-02f, -2.039742281e-03f },
+    { -3.871518110e-02f, +9.774122638e-01f, +6.327621127e-02f, -2.047656480e-03f },
+    { -3.878246270e-02f, +9.773026608e-01f, +6.346273885e-02f, -2.055586801e-03f },
+    { -3.884961949e-02f, +9.771927998e-01f, +6.364942303e-02f, -2.063533255e-03f },
+    { -3.891665153e-02f, +9.770826809e-01f, +6.383626381e-02f, -2.071495856e-03f },
+    { -3.898355890e-02f, +9.769723040e-01f, +6.402326120e-02f, -2.079474617e-03f },
+    { -3.905034167e-02f, +9.768616692e-01f, +6.421041518e-02f, -2.087469549e-03f },
+    { -3.911699991e-02f, +9.767507766e-01f, +6.439772576e-02f, -2.095480667e-03f },
+    { -3.918353368e-02f, +9.766396264e-01f, +6.458519293e-02f, -2.103507983e-03f },
+    { -3.924994306e-02f, +9.765282185e-01f, +6.477281670e-02f, -2.111551509e-03f },
+    { -3.931622812e-02f, +9.764165530e-01f, +6.496059705e-02f, -2.119611258e-03f },
+    { -3.938238892e-02f, +9.763046301e-01f, +6.514853400e-02f, -2.127687243e-03f },
+    { -3.944842554e-02f, +9.761924497e-01f, +6.533662753e-02f, -2.135779477e-03f },
+    { -3.951433805e-02f, +9.760800119e-01f, +6.552487765e-02f, -2.143887971e-03f },
+    { -3.958012651e-02f, +9.759673169e-01f, +6.571328435e-02f, -2.152012740e-03f },
+    { -3.964579101e-02f, +9.758543646e-01f, +6.590184763e-02f, -2.160153795e-03f },
+    { -3.971133160e-02f, +9.757411552e-01f, +6.609056749e-02f, -2.168311149e-03f },
+    { -3.977674836e-02f, +9.756276888e-01f, +6.627944392e-02f, -2.176484815e-03f },
+    { -3.984204137e-02f, +9.755139653e-01f, +6.646847692e-02f, -2.184674805e-03f },
+    { -3.990721068e-02f, +9.753999849e-01f, +6.665766648e-02f, -2.192881133e-03f },
+    { -3.997225637e-02f, +9.752857476e-01f, +6.684701262e-02f, -2.201103809e-03f },
+    { -4.003717851e-02f, +9.751712536e-01f, +6.703651531e-02f, -2.209342848e-03f },
+    { -4.010197718e-02f, +9.750565028e-01f, +6.722617456e-02f, -2.217598262e-03f },
+    { -4.016665244e-02f, +9.749414954e-01f, +6.741599037e-02f, -2.225870062e-03f },
+    { -4.023120436e-02f, +9.748262314e-01f, +6.760596272e-02f, -2.234158263e-03f },
+    { -4.029563302e-02f, +9.747107110e-01f, +6.779609163e-02f, -2.242462875e-03f },
+    { -4.035993848e-02f, +9.745949341e-01f, +6.798637707e-02f, -2.250783913e-03f },
+    { -4.042412082e-02f, +9.744789009e-01f, +6.817681906e-02f, -2.259121387e-03f },
+    { -4.048818011e-02f, +9.743626113e-01f, +6.836741758e-02f, -2.267475312e-03f },
+    { -4.055211641e-02f, +9.742460656e-01f, +6.855817263e-02f, -2.275845698e-03f },
+    { -4.061592981e-02f, +9.741292638e-01f, +6.874908421e-02f, -2.284232560e-03f },
+    { -4.067962036e-02f, +9.740122059e-01f, +6.894015231e-02f, -2.292635908e-03f },
+    { -4.074318815e-02f, +9.738948920e-01f, +6.913137692e-02f, -2.301055756e-03f },
+    { -4.080663324e-02f, +9.737773222e-01f, +6.932275805e-02f, -2.309492116e-03f },
+    { -4.086995570e-02f, +9.736594966e-01f, +6.951429569e-02f, -2.317945001e-03f },
+    { -4.093315561e-02f, +9.735414153e-01f, +6.970598983e-02f, -2.326414422e-03f },
+    { -4.099623304e-02f, +9.734230782e-01f, +6.989784046e-02f, -2.334900392e-03f },
+    { -4.105918805e-02f, +9.733044855e-01f, +7.008984759e-02f, -2.343402924e-03f },
+    { -4.112202073e-02f, +9.731856373e-01f, +7.028201121e-02f, -2.351922030e-03f },
+    { -4.118473113e-02f, +9.730665337e-01f, +7.047433131e-02f, -2.360457723e-03f },
+    { -4.124731934e-02f, +9.729471746e-01f, +7.066680788e-02f, -2.369010014e-03f },
+    { -4.130978543e-02f, +9.728275603e-01f, +7.085944093e-02f, -2.377578915e-03f },
+    { -4.137212946e-02f, +9.727076907e-01f, +7.105223044e-02f, -2.386164441e-03f },
+    { -4.143435151e-02f, +9.725875660e-01f, +7.124517640e-02f, -2.394766601e-03f },
+    { -4.149645164e-02f, +9.724671862e-01f, +7.143827882e-02f, -2.403385410e-03f },
+    { -4.155842994e-02f, +9.723465513e-01f, +7.163153769e-02f, -2.412020878e-03f },
+    { -4.162028648e-02f, +9.722256616e-01f, +7.182495300e-02f, -2.420673020e-03f },
+    { -4.168202131e-02f, +9.721045170e-01f, +7.201852474e-02f, -2.429341845e-03f },
+    { -4.174363453e-02f, +9.719831176e-01f, +7.221225291e-02f, -2.438027368e-03f },
+    { -4.180512620e-02f, +9.718614635e-01f, +7.240613750e-02f, -2.446729599e-03f },
+    { -4.186649638e-02f, +9.717395548e-01f, +7.260017851e-02f, -2.455448552e-03f },
+    { -4.192774516e-02f, +9.716173916e-01f, +7.279437592e-02f, -2.464184239e-03f },
+    { -4.198887261e-02f, +9.714949738e-01f, +7.298872973e-02f, -2.472936671e-03f },
+    { -4.204987879e-02f, +9.713723017e-01f, +7.318323994e-02f, -2.481705861e-03f },
+    { -4.211076378e-02f, +9.712493753e-01f, +7.337790653e-02f, -2.490491821e-03f },
+    { -4.217152766e-02f, +9.711261947e-01f, +7.357272950e-02f, -2.499294563e-03f },
+    { -4.223217049e-02f, +9.710027598e-01f, +7.376770885e-02f, -2.508114099e-03f },
+    { -4.229269235e-02f, +9.708790709e-01f, +7.396284455e-02f, -2.516950442e-03f },
+    { -4.235309331e-02f, +9.707551280e-01f, +7.415813662e-02f, -2.525803603e-03f },
+    { -4.241337343e-02f, +9.706309312e-01f, +7.435358503e-02f, -2.534673595e-03f },
+    { -4.247353281e-02f, +9.705064805e-01f, +7.454918978e-02f, -2.543560430e-03f },
+    { -4.253357150e-02f, +9.703817761e-01f, +7.474495086e-02f, -2.552464119e-03f },
+    { -4.259348958e-02f, +9.702568180e-01f, +7.494086827e-02f, -2.561384675e-03f },
+    { -4.265328712e-02f, +9.701316062e-01f, +7.513694200e-02f, -2.570322110e-03f },
+    { -4.271296420e-02f, +9.700061410e-01f, +7.533317203e-02f, -2.579276435e-03f },
+    { -4.277252088e-02f, +9.698804222e-01f, +7.552955836e-02f, -2.588247664e-03f },
+    { -4.283195724e-02f, +9.697544501e-01f, +7.572610098e-02f, -2.597235807e-03f },
+    { -4.289127336e-02f, +9.696282248e-01f, +7.592279989e-02f, -2.606240877e-03f },
+    { -4.295046931e-02f, +9.695017462e-01f, +7.611965506e-02f, -2.615262886e-03f },
+    { -4.300954515e-02f, +9.693750144e-01f, +7.631666650e-02f, -2.624301846e-03f },
+    { -4.306850096e-02f, +9.692480296e-01f, +7.651383420e-02f, -2.633357769e-03f },
+    { -4.312733682e-02f, +9.691207919e-01f, +7.671115814e-02f, -2.642430666e-03f },
+    { -4.318605280e-02f, +9.689933012e-01f, +7.690863832e-02f, -2.651520549e-03f },
+    { -4.324464897e-02f, +9.688655578e-01f, +7.710627472e-02f, -2.660627431e-03f },
+    { -4.330312541e-02f, +9.687375615e-01f, +7.730406734e-02f, -2.669751324e-03f },
+    { -4.336148218e-02f, +9.686093127e-01f, +7.750201617e-02f, -2.678892238e-03f },
+    { -4.341971936e-02f, +9.684808113e-01f, +7.770012120e-02f, -2.688050187e-03f },
+    { -4.347783703e-02f, +9.683520574e-01f, +7.789838241e-02f, -2.697225181e-03f },
+    { -4.353583525e-02f, +9.682230510e-01f, +7.809679980e-02f, -2.706417234e-03f },
+    { -4.359371410e-02f, +9.680937924e-01f, +7.829537336e-02f, -2.715626355e-03f },
+    { -4.365147366e-02f, +9.679642815e-01f, +7.849410308e-02f, -2.724852558e-03f },
+    { -4.370911400e-02f, +9.678345184e-01f, +7.869298894e-02f, -2.734095855e-03f },
+    { -4.376663518e-02f, +9.677045033e-01f, +7.889203094e-02f, -2.743356256e-03f },
+    { -4.382403729e-02f, +9.675742361e-01f, +7.909122907e-02f, -2.752633774e-03f },
+    { -4.388132040e-02f, +9.674437171e-01f, +7.929058330e-02f, -2.761928420e-03f },
+    { -4.393848458e-02f, +9.673129462e-01f, +7.949009365e-02f, -2.771240206e-03f },
+    { -4.399552991e-02f, +9.671819235e-01f, +7.968976008e-02f, -2.780569145e-03f },
+    { -4.405245646e-02f, +9.670506492e-01f, +7.988958260e-02f, -2.789915247e-03f },
+    { -4.410926430e-02f, +9.669191233e-01f, +8.008956119e-02f, -2.799278524e-03f },
+    { -4.416595350e-02f, +9.667873459e-01f, +8.028969584e-02f, -2.808658988e-03f },
+    { -4.422252415e-02f, +9.666553170e-01f, +8.048998653e-02f, -2.818056650e-03f },
+    { -4.427897631e-02f, +9.665230369e-01f, +8.069043326e-02f, -2.827471523e-03f },
+    { -4.433531006e-02f, +9.663905054e-01f, +8.089103602e-02f, -2.836903618e-03f },
+    { -4.439152547e-02f, +9.662577228e-01f, +8.109179479e-02f, -2.846352946e-03f },
+    { -4.444762262e-02f, +9.661246891e-01f, +8.129270955e-02f, -2.855819519e-03f },
+    { -4.450360157e-02f, +9.659914044e-01f, +8.149378031e-02f, -2.865303349e-03f },
+    { -4.455946242e-02f, +9.658578688e-01f, +8.169500704e-02f, -2.874804447e-03f },
+    { -4.461520522e-02f, +9.657240824e-01f, +8.189638974e-02f, -2.884322825e-03f },
+    { -4.467083005e-02f, +9.655900451e-01f, +8.209792838e-02f, -2.893858495e-03f },
+    { -4.472633699e-02f, +9.654557573e-01f, +8.229962297e-02f, -2.903411467e-03f },
+    { -4.478172612e-02f, +9.653212188e-01f, +8.250147348e-02f, -2.912981754e-03f },
+    { -4.483699749e-02f, +9.651864298e-01f, +8.270347990e-02f, -2.922569366e-03f },
+    { -4.489215120e-02f, +9.650513905e-01f, +8.290564223e-02f, -2.932174316e-03f },
+    { -4.494718732e-02f, +9.649161008e-01f, +8.310796044e-02f, -2.941796615e-03f },
+    { -4.500210591e-02f, +9.647805608e-01f, +8.331043452e-02f, -2.951436274e-03f },
+    { -4.505690705e-02f, +9.646447707e-01f, +8.351306447e-02f, -2.961093304e-03f },
+    { -4.511159083e-02f, +9.645087305e-01f, +8.371585026e-02f, -2.970767718e-03f },
+    { -4.516615730e-02f, +9.643724404e-01f, +8.391879188e-02f, -2.980459527e-03f },
+    { -4.522060655e-02f, +9.642359003e-01f, +8.412188933e-02f, -2.990168741e-03f },
+    { -4.527493866e-02f, +9.640991104e-01f, +8.432514258e-02f, -2.999895373e-03f },
+    { -4.532915369e-02f, +9.639620708e-01f, +8.452855162e-02f, -3.009639433e-03f },
+    { -4.538325172e-02f, +9.638247816e-01f, +8.473211644e-02f, -3.019400934e-03f },
+    { -4.543723283e-02f, +9.636872427e-01f, +8.493583703e-02f, -3.029179886e-03f },
+    { -4.549109708e-02f, +9.635494545e-01f, +8.513971336e-02f, -3.038976300e-03f },
+    { -4.554484457e-02f, +9.634114168e-01f, +8.534374543e-02f, -3.048790189e-03f },
+    { -4.559847535e-02f, +9.632731298e-01f, +8.554793322e-02f, -3.058621563e-03f },
+    { -4.565198951e-02f, +9.631345937e-01f, +8.575227672e-02f, -3.068470433e-03f },
+    { -4.570538712e-02f, +9.629958084e-01f, +8.595677591e-02f, -3.078336812e-03f },
+    { -4.575866826e-02f, +9.628567741e-01f, +8.616143077e-02f, -3.088220709e-03f },
+    { -4.581183299e-02f, +9.627174908e-01f, +8.636624129e-02f, -3.098122137e-03f },
+    { -4.586488140e-02f, +9.625779587e-01f, +8.657120747e-02f, -3.108041106e-03f },
+    { -4.591781356e-02f, +9.624381778e-01f, +8.677632927e-02f, -3.117977628e-03f },
+    { -4.597062955e-02f, +9.622981482e-01f, +8.698160668e-02f, -3.127931713e-03f },
+    { -4.602332944e-02f, +9.621578700e-01f, +8.718703970e-02f, -3.137903374e-03f },
+    { -4.607591330e-02f, +9.620173433e-01f, +8.739262830e-02f, -3.147892621e-03f },
+    { -4.612838122e-02f, +9.618765682e-01f, +8.759837247e-02f, -3.157899465e-03f },
+    { -4.618073326e-02f, +9.617355447e-01f, +8.780427220e-02f, -3.167923918e-03f },
+    { -4.623296951e-02f, +9.615942731e-01f, +8.801032746e-02f, -3.177965990e-03f },
+    { -4.628509003e-02f, +9.614527532e-01f, +8.821653824e-02f, -3.188025693e-03f },
+    { -4.633709491e-02f, +9.613109853e-01f, +8.842290452e-02f, -3.198103037e-03f },
+    { -4.638898421e-02f, +9.611689695e-01f, +8.862942629e-02f, -3.208198035e-03f },
+    { -4.644075802e-02f, +9.610267057e-01f, +8.883610354e-02f, -3.218310696e-03f },
+    { -4.649241641e-02f, +9.608841941e-01f, +8.904293624e-02f, -3.228441031e-03f },
+    { -4.654395945e-02f, +9.607414349e-01f, +8.924992437e-02f, -3.238589053e-03f },
+    { -4.659538723e-02f, +9.605984280e-01f, +8.945706793e-02f, -3.248754771e-03f },
+    { -4.664669981e-02f, +9.604551736e-01f, +8.966436689e-02f, -3.258938197e-03f },
+    { -4.669789727e-02f, +9.603116718e-01f, +8.987182124e-02f, -3.269139342e-03f },
+    { -4.674897969e-02f, +9.601679226e-01f, +9.007943096e-02f, -3.279358217e-03f },
+    { -4.679994715e-02f, +9.600239261e-01f, +9.028719604e-02f, -3.289594832e-03f },
+    { -4.685079971e-02f, +9.598796825e-01f, +9.049511645e-02f, -3.299849199e-03f },
+    { -4.690153746e-02f, +9.597351919e-01f, +9.070319217e-02f, -3.310121328e-03f },
+    { -4.695216048e-02f, +9.595904542e-01f, +9.091142320e-02f, -3.320411230e-03f },
+    { -4.700266883e-02f, +9.594454696e-01f, +9.111980950e-02f, -3.330718916e-03f },
+    { -4.705306259e-02f, +9.593002383e-01f, +9.132835108e-02f, -3.341044398e-03f },
+    { -4.710334184e-02f, +9.591547602e-01f, +9.153704790e-02f, -3.351387685e-03f },
+    { -4.715350666e-02f, +9.590090355e-01f, +9.174589994e-02f, -3.361748789e-03f },
+    { -4.720355712e-02f, +9.588630643e-01f, +9.195490720e-02f, -3.372127720e-03f },
+    { -4.725349330e-02f, +9.587168467e-01f, +9.216406964e-02f, -3.382524489e-03f },
+    { -4.730331527e-02f, +9.585703827e-01f, +9.237338726e-02f, -3.392939107e-03f },
+    { -4.735302311e-02f, +9.584236724e-01f, +9.258286004e-02f, -3.403371585e-03f },
+    { -4.740261690e-02f, +9.582767160e-01f, +9.279248795e-02f, -3.413821934e-03f },
+    { -4.745209672e-02f, +9.581295136e-01f, +9.300227097e-02f, -3.424290163e-03f },
+    { -4.750146263e-02f, +9.579820651e-01f, +9.321220909e-02f, -3.434776284e-03f },
+    { -4.755071471e-02f, +9.578343708e-01f, +9.342230229e-02f, -3.445280308e-03f },
+    { -4.759985305e-02f, +9.576864307e-01f, +9.363255055e-02f, -3.455802245e-03f },
+    { -4.764887772e-02f, +9.575382449e-01f, +9.384295385e-02f, -3.466342106e-03f },
+    { -4.769778879e-02f, +9.573898135e-01f, +9.405351217e-02f, -3.476899901e-03f },
+    { -4.774658634e-02f, +9.572411365e-01f, +9.426422548e-02f, -3.487475641e-03f },
+    { -4.779527045e-02f, +9.570922142e-01f, +9.447509378e-02f, -3.498069337e-03f },
+    { -4.784384120e-02f, +9.569430465e-01f, +9.468611704e-02f, -3.508680999e-03f },
+    { -4.789229866e-02f, +9.567936336e-01f, +9.489729524e-02f, -3.519310638e-03f },
+    { -4.794064290e-02f, +9.566439756e-01f, +9.510862835e-02f, -3.529958264e-03f },
+    { -4.798887401e-02f, +9.564940726e-01f, +9.532011637e-02f, -3.540623888e-03f },
+    { -4.803699206e-02f, +9.563439245e-01f, +9.553175927e-02f, -3.551307520e-03f },
+    { -4.808499713e-02f, +9.561935317e-01f, +9.574355703e-02f, -3.562009171e-03f },
+    { -4.813288929e-02f, +9.560428941e-01f, +9.595550962e-02f, -3.572728851e-03f },
+    { -4.818066862e-02f, +9.558920118e-01f, +9.616761703e-02f, -3.583466571e-03f },
+    { -4.822833521e-02f, +9.557408849e-01f, +9.637987924e-02f, -3.594222342e-03f },
+    { -4.827588911e-02f, +9.555895136e-01f, +9.659229622e-02f, -3.604996173e-03f },
+    { -4.832333042e-02f, +9.554378979e-01f, +9.680486796e-02f, -3.615788075e-03f },
+    { -4.837065921e-02f, +9.552860379e-01f, +9.701759443e-02f, -3.626598058e-03f },
+    { -4.841787556e-02f, +9.551339337e-01f, +9.723047561e-02f, -3.637426133e-03f },
+    { -4.846497954e-02f, +9.549815854e-01f, +9.744351149e-02f, -3.648272311e-03f },
+    { -4.851197123e-02f, +9.548289931e-01f, +9.765670203e-02f, -3.659136601e-03f },
+    { -4.855885071e-02f, +9.546761570e-01f, +9.787004721e-02f, -3.670019013e-03f },
+    { -4.860561805e-02f, +9.545230770e-01f, +9.808354703e-02f, -3.680919559e-03f },
+    { -4.865227334e-02f, +9.543697533e-01f, +9.829720144e-02f, -3.691838248e-03f },
+    { -4.869881664e-02f, +9.542161860e-01f, +9.851101044e-02f, -3.702775091e-03f },
+    { -4.874524804e-02f, +9.540623752e-01f, +9.872497399e-02f, -3.713730097e-03f },
+    { -4.879156762e-02f, +9.539083209e-01f, +9.893909208e-02f, -3.724703278e-03f },
+    { -4.883777544e-02f, +9.537540234e-01f, +9.915336469e-02f, -3.735694643e-03f },
+    { -4.888387160e-02f, +9.535994826e-01f, +9.936779178e-02f, -3.746704203e-03f },
+    { -4.892985616e-02f, +9.534446987e-01f, +9.958237334e-02f, -3.757731967e-03f },
+    { -4.897572920e-02f, +9.532896717e-01f, +9.979710935e-02f, -3.768777946e-03f },
+    { -4.902149080e-02f, +9.531344018e-01f, +1.000119998e-01f, -3.779842151e-03f },
+    { -4.906714105e-02f, +9.529788891e-01f, +1.002270446e-01f, -3.790924590e-03f },
+    { -4.911268001e-02f, +9.528231336e-01f, +1.004422438e-01f, -3.802025274e-03f },
+    { -4.915810776e-02f, +9.526671355e-01f, +1.006575974e-01f, -3.813144214e-03f },
+    { -4.920342438e-02f, +9.525108949e-01f, +1.008731053e-01f, -3.824281419e-03f },
+    { -4.924862995e-02f, +9.523544118e-01f, +1.010887674e-01f, -3.835436900e-03f },
+    { -4.929372455e-02f, +9.521976863e-01f, +1.013045839e-01f, -3.846610666e-03f },
+    { -4.933870825e-02f, +9.520407186e-01f, +1.015205546e-01f, -3.857802728e-03f },
+    { -4.938358114e-02f, +9.518835087e-01f, +1.017366796e-01f, -3.869013095e-03f },
+    { -4.942834328e-02f, +9.517260568e-01f, +1.019529588e-01f, -3.880241778e-03f },
+    { -4.947299476e-02f, +9.515683630e-01f, +1.021693921e-01f, -3.891488786e-03f },
+    { -4.951753566e-02f, +9.514104273e-01f, +1.023859797e-01f, -3.902754129e-03f },
+    { -4.956196605e-02f, +9.512522498e-01f, +1.026027213e-01f, -3.914037818e-03f },
+    { -4.960628601e-02f, +9.510938306e-01f, +1.028196171e-01f, -3.925339862e-03f },
+    { -4.965049562e-02f, +9.509351700e-01f, +1.030366669e-01f, -3.936660271e-03f },
+    { -4.969459496e-02f, +9.507762678e-01f, +1.032538708e-01f, -3.947999054e-03f },
+    { -4.973858410e-02f, +9.506171243e-01f, +1.034712288e-01f, -3.959356223e-03f },
+    { -4.978246313e-02f, +9.504577395e-01f, +1.036887408e-01f, -3.970731786e-03f },
+    { -4.982623211e-02f, +9.502981136e-01f, +1.039064067e-01f, -3.982125754e-03f },
+    { -4.986989114e-02f, +9.501382466e-01f, +1.041242266e-01f, -3.993538136e-03f },
+    { -4.991344028e-02f, +9.499781386e-01f, +1.043422005e-01f, -4.004968942e-03f },
+    { -4.995687962e-02f, +9.498177898e-01f, +1.045603282e-01f, -4.016418182e-03f },
+    { -5.000020923e-02f, +9.496572003e-01f, +1.047786099e-01f, -4.027885865e-03f },
+    { -5.004342920e-02f, +9.494963700e-01f, +1.049970454e-01f, -4.039372002e-03f },
+    { -5.008653959e-02f, +9.493352993e-01f, +1.052156347e-01f, -4.050876601e-03f },
+    { -5.012954049e-02f, +9.491739880e-01f, +1.054343779e-01f, -4.062399673e-03f },
+    { -5.017243198e-02f, +9.490124364e-01f, +1.056532748e-01f, -4.073941228e-03f },
+    { -5.021521414e-02f, +9.488506446e-01f, +1.058723254e-01f, -4.085501274e-03f },
+    { -5.025788704e-02f, +9.486886126e-01f, +1.060915299e-01f, -4.097079822e-03f },
+    { -5.030045076e-02f, +9.485263405e-01f, +1.063108880e-01f, -4.108676881e-03f },
+    { -5.034290538e-02f, +9.483638285e-01f, +1.065303997e-01f, -4.120292461e-03f },
+    { -5.038525098e-02f, +9.482010767e-01f, +1.067500652e-01f, -4.131926571e-03f },
+    { -5.042748764e-02f, +9.480380851e-01f, +1.069698842e-01f, -4.143579220e-03f },
+    { -5.046961543e-02f, +9.478748538e-01f, +1.071898568e-01f, -4.155250420e-03f },
+    { -5.051163444e-02f, +9.477113830e-01f, +1.074099830e-01f, -4.166940178e-03f },
+    { -5.055354474e-02f, +9.475476728e-01f, +1.076302628e-01f, -4.178648505e-03f },
+    { -5.059534642e-02f, +9.473837233e-01f, +1.078506960e-01f, -4.190375409e-03f },
+    { -5.063703954e-02f, +9.472195345e-01f, +1.080712828e-01f, -4.202120901e-03f },
+    { -5.067862420e-02f, +9.470551065e-01f, +1.082920229e-01f, -4.213884989e-03f },
+    { -5.072010046e-02f, +9.468904396e-01f, +1.085129166e-01f, -4.225667684e-03f },
+    { -5.076146841e-02f, +9.467255337e-01f, +1.087339636e-01f, -4.237468994e-03f },
+    { -5.080272812e-02f, +9.465603890e-01f, +1.089551640e-01f, -4.249288929e-03f },
+    { -5.084387968e-02f, +9.463950056e-01f, +1.091765177e-01f, -4.261127499e-03f },
+    { -5.088492316e-02f, +9.462293836e-01f, +1.093980247e-01f, -4.272984712e-03f },
+    { -5.092585865e-02f, +9.460635230e-01f, +1.096196851e-01f, -4.284860578e-03f },
+    { -5.096668621e-02f, +9.458974241e-01f, +1.098414987e-01f, -4.296755107e-03f },
+    { -5.100740594e-02f, +9.457310868e-01f, +1.100634655e-01f, -4.308668306e-03f },
+    { -5.104801790e-02f, +9.455645113e-01f, +1.102855855e-01f, -4.320600187e-03f },
+    { -5.108852219e-02f, +9.453976978e-01f, +1.105078587e-01f, -4.332550758e-03f },
+    { -5.112891887e-02f, +9.452306462e-01f, +1.107302850e-01f, -4.344520028e-03f },
+    { -5.116920802e-02f, +9.450633568e-01f, +1.109528645e-01f, -4.356508006e-03f },
+    { -5.120938973e-02f, +9.448958296e-01f, +1.111755970e-01f, -4.368514702e-03f },
+    { -5.124946408e-02f, +9.447280647e-01f, +1.113984826e-01f, -4.380540125e-03f },
+    { -5.128943114e-02f, +9.445600623e-01f, +1.116215212e-01f, -4.392584284e-03f },
+    { -5.132929099e-02f, +9.443918223e-01f, +1.118447128e-01f, -4.404647188e-03f },
+    { -5.136904371e-02f, +9.442233450e-01f, +1.120680573e-01f, -4.416728846e-03f },
+    { -5.140868939e-02f, +9.440546305e-01f, +1.122915548e-01f, -4.428829267e-03f },
+    { -5.144822810e-02f, +9.438856788e-01f, +1.125152052e-01f, -4.440948461e-03f },
+    { -5.148765991e-02f, +9.437164901e-01f, +1.127390085e-01f, -4.453086436e-03f },
+    { -5.152698491e-02f, +9.435470644e-01f, +1.129629646e-01f, -4.465243201e-03f },
+    { -5.156620319e-02f, +9.433774019e-01f, +1.131870735e-01f, -4.477418765e-03f },
+    { -5.160531481e-02f, +9.432075026e-01f, +1.134113352e-01f, -4.489613138e-03f },
+    { -5.164431986e-02f, +9.430373667e-01f, +1.136357497e-01f, -4.501826329e-03f },
+    { -5.168321841e-02f, +9.428669944e-01f, +1.138603169e-01f, -4.514058345e-03f },
+    { -5.172201055e-02f, +9.426963856e-01f, +1.140850368e-01f, -4.526309197e-03f },
+    { -5.176069636e-02f, +9.425255405e-01f, +1.143099093e-01f, -4.538578892e-03f },
+    { -5.179927591e-02f, +9.423544592e-01f, +1.145349345e-01f, -4.550867441e-03f },
+    { -5.183774929e-02f, +9.421831418e-01f, +1.147601122e-01f, -4.563174852e-03f },
+    { -5.187611657e-02f, +9.420115885e-01f, +1.149854425e-01f, -4.575501133e-03f },
+    { -5.191437783e-02f, +9.418397993e-01f, +1.152109254e-01f, -4.587846293e-03f },
+    { -5.195253316e-02f, +9.416677743e-01f, +1.154365607e-01f, -4.600210342e-03f },
+    { -5.199058263e-02f, +9.414955136e-01f, +1.156623486e-01f, -4.612593288e-03f },
+    { -5.202852632e-02f, +9.413230174e-01f, +1.158882888e-01f, -4.624995140e-03f },
+    { -5.206636432e-02f, +9.411502858e-01f, +1.161143815e-01f, -4.637415906e-03f },
+    { -5.210409670e-02f, +9.409773188e-01f, +1.163406265e-01f, -4.649855595e-03f },
+    { -5.214172354e-02f, +9.408041166e-01f, +1.165670239e-01f, -4.662314217e-03f },
+    { -5.217924493e-02f, +9.406306792e-01f, +1.167935736e-01f, -4.674791779e-03f },
+    { -5.221666093e-02f, +9.404570069e-01f, +1.170202755e-01f, -4.687288290e-03f },
+    { -5.225397164e-02f, +9.402830997e-01f, +1.172471298e-01f, -4.699803759e-03f },
+    { -5.229117713e-02f, +9.401089576e-01f, +1.174741362e-01f, -4.712338195e-03f },
+    { -5.232827748e-02f, +9.399345809e-01f, +1.177012947e-01f, -4.724891606e-03f },
+    { -5.236527277e-02f, +9.397599696e-01f, +1.179286055e-01f, -4.737464000e-03f },
+    { -5.240216308e-02f, +9.395851238e-01f, +1.181560683e-01f, -4.750055387e-03f },
+    { -5.243894849e-02f, +9.394100437e-01f, +1.183836832e-01f, -4.762665774e-03f },
+    { -5.247562909e-02f, +9.392347294e-01f, +1.186114502e-01f, -4.775295171e-03f },
+    { -5.251220495e-02f, +9.390591808e-01f, +1.188393692e-01f, -4.787943585e-03f },
+    { -5.254867615e-02f, +9.388833983e-01f, +1.190674401e-01f, -4.800611026e-03f },
+    { -5.258504277e-02f, +9.387073818e-01f, +1.192956630e-01f, -4.813297501e-03f },
+    { -5.262130489e-02f, +9.385311316e-01f, +1.195240378e-01f, -4.826003020e-03f },
+    { -5.265746259e-02f, +9.383546476e-01f, +1.197525644e-01f, -4.838727590e-03f },
+    { -5.269351596e-02f, +9.381779301e-01f, +1.199812429e-01f, -4.851471219e-03f },
+    { -5.272946506e-02f, +9.380009790e-01f, +1.202100732e-01f, -4.864233917e-03f },
+    { -5.276531000e-02f, +9.378237946e-01f, +1.204390553e-01f, -4.877015692e-03f },
+    { -5.280105083e-02f, +9.376463769e-01f, +1.206681891e-01f, -4.889816551e-03f },
+    { -5.283668764e-02f, +9.374687261e-01f, +1.208974746e-01f, -4.902636504e-03f },
+    { -5.287222052e-02f, +9.372908423e-01f, +1.211269118e-01f, -4.915475558e-03f },
+    { -5.290764955e-02f, +9.371127255e-01f, +1.213565006e-01f, -4.928333721e-03f },
+    { -5.294297479e-02f, +9.369343759e-01f, +1.215862410e-01f, -4.941211003e-03f },
+    { -5.297819635e-02f, +9.367557936e-01f, +1.218161330e-01f, -4.954107411e-03f },
+    { -5.301331428e-02f, +9.365769788e-01f, +1.220461765e-01f, -4.967022953e-03f },
+    { -5.304832868e-02f, +9.363979314e-01f, +1.222763714e-01f, -4.979957637e-03f },
+    { -5.308323963e-02f, +9.362186517e-01f, +1.225067179e-01f, -4.992911472e-03f },
+    { -5.311804720e-02f, +9.360391397e-01f, +1.227372157e-01f, -5.005884466e-03f },
+    { -5.315275148e-02f, +9.358593955e-01f, +1.229678649e-01f, -5.018876627e-03f },
+    { -5.318735255e-02f, +9.356794194e-01f, +1.231986655e-01f, -5.031887963e-03f },
+    { -5.322185049e-02f, +9.354992113e-01f, +1.234296174e-01f, -5.044918481e-03f },
+    { -5.325624537e-02f, +9.353187714e-01f, +1.236607205e-01f, -5.057968191e-03f },
+    { -5.329053728e-02f, +9.351380998e-01f, +1.238919749e-01f, -5.071037100e-03f },
+    { -5.332472630e-02f, +9.349571966e-01f, +1.241233805e-01f, -5.084125216e-03f },
+    { -5.335881251e-02f, +9.347760620e-01f, +1.243549372e-01f, -5.097232546e-03f },
+    { -5.339279600e-02f, +9.345946960e-01f, +1.245866451e-01f, -5.110359100e-03f },
+    { -5.342667683e-02f, +9.344130987e-01f, +1.248185041e-01f, -5.123504885e-03f },
+    { -5.346045510e-02f, +9.342312704e-01f, +1.250505141e-01f, -5.136669908e-03f },
+    { -5.349413088e-02f, +9.340492110e-01f, +1.252826751e-01f, -5.149854178e-03f },
+    { -5.352770425e-02f, +9.338669207e-01f, +1.255149871e-01f, -5.163057703e-03f },
+    { -5.356117530e-02f, +9.336843996e-01f, +1.257474501e-01f, -5.176280490e-03f },
+    { -5.359454410e-02f, +9.335016478e-01f, +1.259800639e-01f, -5.189522547e-03f },
+    { -5.362781074e-02f, +9.333186655e-01f, +1.262128286e-01f, -5.202783882e-03f },
+    { -5.366097530e-02f, +9.331354527e-01f, +1.264457441e-01f, -5.216064503e-03f },
+    { -5.369403785e-02f, +9.329520096e-01f, +1.266788105e-01f, -5.229364417e-03f },
+    { -5.372699848e-02f, +9.327683363e-01f, +1.269120275e-01f, -5.242683633e-03f },
+    { -5.375985728e-02f, +9.325844328e-01f, +1.271453953e-01f, -5.256022157e-03f },
+    { -5.379261431e-02f, +9.324002994e-01f, +1.273789138e-01f, -5.269379998e-03f },
+    { -5.382526966e-02f, +9.322159361e-01f, +1.276125829e-01f, -5.282757164e-03f },
+    { -5.385782342e-02f, +9.320313430e-01f, +1.278464026e-01f, -5.296153661e-03f },
+    { -5.389027566e-02f, +9.318465203e-01f, +1.280803728e-01f, -5.309569498e-03f },
+    { -5.392262647e-02f, +9.316614680e-01f, +1.283144936e-01f, -5.323004682e-03f },
+    { -5.395487592e-02f, +9.314761864e-01f, +1.285487648e-01f, -5.336459220e-03f },
+    { -5.398702410e-02f, +9.312906754e-01f, +1.287831865e-01f, -5.349933121e-03f },
+    { -5.401907109e-02f, +9.311049353e-01f, +1.290177586e-01f, -5.363426391e-03f },
+    { -5.405101697e-02f, +9.309189660e-01f, +1.292524811e-01f, -5.376939039e-03f },
+    { -5.408286181e-02f, +9.307327679e-01f, +1.294873538e-01f, -5.390471071e-03f },
+    { -5.411460571e-02f, +9.305463408e-01f, +1.297223769e-01f, -5.404022496e-03f },
+    { -5.414624875e-02f, +9.303596851e-01f, +1.299575502e-01f, -5.417593320e-03f },
+    { -5.417779099e-02f, +9.301728008e-01f, +1.301928737e-01f, -5.431183551e-03f },
+    { -5.420923253e-02f, +9.299856879e-01f, +1.304283474e-01f, -5.444793197e-03f },
+    { -5.424057345e-02f, +9.297983467e-01f, +1.306639711e-01f, -5.458422264e-03f },
+    { -5.427181382e-02f, +9.296107773e-01f, +1.308997450e-01f, -5.472070761e-03f },
+    { -5.430295374e-02f, +9.294229797e-01f, +1.311356689e-01f, -5.485738694e-03f },
+    { -5.433399327e-02f, +9.292349540e-01f, +1.313717428e-01f, -5.499426070e-03f },
+    { -5.436493251e-02f, +9.290467005e-01f, +1.316079667e-01f, -5.513132898e-03f },
+    { -5.439577153e-02f, +9.288582192e-01f, +1.318443405e-01f, -5.526859184e-03f },
+    { -5.442651041e-02f, +9.286695102e-01f, +1.320808642e-01f, -5.540604935e-03f },
+    { -5.445714924e-02f, +9.284805736e-01f, +1.323175377e-01f, -5.554370159e-03f },
+    { -5.448768810e-02f, +9.282914097e-01f, +1.325543610e-01f, -5.568154863e-03f },
+    { -5.451812706e-02f, +9.281020184e-01f, +1.327913341e-01f, -5.581959054e-03f },
+    { -5.454846622e-02f, +9.279123998e-01f, +1.330284568e-01f, -5.595782739e-03f },
+    { -5.457870565e-02f, +9.277225543e-01f, +1.332657293e-01f, -5.609625925e-03f },
+    { -5.460884543e-02f, +9.275324817e-01f, +1.335031514e-01f, -5.623488619e-03f },
+    { -5.463888565e-02f, +9.273421823e-01f, +1.337407230e-01f, -5.637370829e-03f },
+    { -5.466882638e-02f, +9.271516562e-01f, +1.339784442e-01f, -5.651272561e-03f },
+    { -5.469866771e-02f, +9.269609034e-01f, +1.342163150e-01f, -5.665193823e-03f },
+    { -5.472840972e-02f, +9.267699242e-01f, +1.344543351e-01f, -5.679134621e-03f },
+    { -5.475805249e-02f, +9.265787186e-01f, +1.346925048e-01f, -5.693094963e-03f },
+    { -5.478759611e-02f, +9.263872867e-01f, +1.349308237e-01f, -5.707074854e-03f },
+    { -5.481704064e-02f, +9.261956287e-01f, +1.351692920e-01f, -5.721074303e-03f },
+    { -5.484638619e-02f, +9.260037447e-01f, +1.354079097e-01f, -5.735093317e-03f },
+    { -5.487563282e-02f, +9.258116348e-01f, +1.356466765e-01f, -5.749131901e-03f },
+    { -5.490478063e-02f, +9.256192991e-01f, +1.358855926e-01f, -5.763190063e-03f },
+    { -5.493382968e-02f, +9.254267378e-01f, +1.361246578e-01f, -5.777267810e-03f },
+    { -5.496278007e-02f, +9.252339509e-01f, +1.363638722e-01f, -5.791365148e-03f },
+    { -5.499163187e-02f, +9.250409386e-01f, +1.366032356e-01f, -5.805482085e-03f },
+    { -5.502038517e-02f, +9.248477010e-01f, +1.368427480e-01f, -5.819618626e-03f },
+    { -5.504904005e-02f, +9.246542383e-01f, +1.370824095e-01f, -5.833774780e-03f },
+    { -5.507759658e-02f, +9.244605504e-01f, +1.373222199e-01f, -5.847950552e-03f },
+    { -5.510605486e-02f, +9.242666377e-01f, +1.375621792e-01f, -5.862145949e-03f },
+    { -5.513441497e-02f, +9.240725001e-01f, +1.378022873e-01f, -5.876360977e-03f },
+    { -5.516267698e-02f, +9.238781378e-01f, +1.380425443e-01f, -5.890595645e-03f },
+    { -5.519084097e-02f, +9.236835509e-01f, +1.382829500e-01f, -5.904849957e-03f },
+    { -5.521890704e-02f, +9.234887396e-01f, +1.385235045e-01f, -5.919123921e-03f },
+    { -5.524687526e-02f, +9.232937039e-01f, +1.387642076e-01f, -5.933417543e-03f },
+    { -5.527474572e-02f, +9.230984441e-01f, +1.390050594e-01f, -5.947730830e-03f },
+    { -5.530251848e-02f, +9.229029601e-01f, +1.392460598e-01f, -5.962063788e-03f },
+    { -5.533019365e-02f, +9.227072522e-01f, +1.394872087e-01f, -5.976416423e-03f },
+    { -5.535777130e-02f, +9.225113204e-01f, +1.397285061e-01f, -5.990788743e-03f },
+    { -5.538525151e-02f, +9.223151649e-01f, +1.399699520e-01f, -6.005180754e-03f },
+    { -5.541263436e-02f, +9.221187858e-01f, +1.402115463e-01f, -6.019592461e-03f },
+    { -5.543991994e-02f, +9.219221832e-01f, +1.404532889e-01f, -6.034023872e-03f },
+    { -5.546710832e-02f, +9.217253573e-01f, +1.406951799e-01f, -6.048474993e-03f },
+    { -5.549419960e-02f, +9.215283081e-01f, +1.409372192e-01f, -6.062945830e-03f },
+    { -5.552119385e-02f, +9.213310358e-01f, +1.411794066e-01f, -6.077436389e-03f },
+    { -5.554809115e-02f, +9.211335405e-01f, +1.414217423e-01f, -6.091946678e-03f },
+    { -5.557489159e-02f, +9.209358223e-01f, +1.416642261e-01f, -6.106476701e-03f },
+    { -5.560159525e-02f, +9.207378813e-01f, +1.419068580e-01f, -6.121026465e-03f },
+    { -5.562820221e-02f, +9.205397178e-01f, +1.421496379e-01f, -6.135595977e-03f },
+    { -5.565471255e-02f, +9.203413317e-01f, +1.423925658e-01f, -6.150185242e-03f },
+    { -5.568112636e-02f, +9.201427233e-01f, +1.426356417e-01f, -6.164794268e-03f },
+    { -5.570744371e-02f, +9.199438926e-01f, +1.428788655e-01f, -6.179423059e-03f },
+    { -5.573366470e-02f, +9.197448398e-01f, +1.431222371e-01f, -6.194071622e-03f },
+    { -5.575978940e-02f, +9.195455649e-01f, +1.433657566e-01f, -6.208739964e-03f },
+    { -5.578581789e-02f, +9.193460682e-01f, +1.436094238e-01f, -6.223428090e-03f },
+    { -5.581175026e-02f, +9.191463497e-01f, +1.438532387e-01f, -6.238136006e-03f },
+    { -5.583758659e-02f, +9.189464096e-01f, +1.440972013e-01f, -6.252863718e-03f },
+    { -5.586332696e-02f, +9.187462479e-01f, +1.443413115e-01f, -6.267611233e-03f },
+    { -5.588897145e-02f, +9.185458649e-01f, +1.445855693e-01f, -6.282378556e-03f },
+    { -5.591452015e-02f, +9.183452606e-01f, +1.448299746e-01f, -6.297165693e-03f },
+    { -5.593997314e-02f, +9.181444351e-01f, +1.450745274e-01f, -6.311972650e-03f },
+    { -5.596533050e-02f, +9.179433886e-01f, +1.453192276e-01f, -6.326799433e-03f },
+    { -5.599059231e-02f, +9.177421213e-01f, +1.455640752e-01f, -6.341646048e-03f },
+    { -5.601575866e-02f, +9.175406331e-01f, +1.458090702e-01f, -6.356512501e-03f },
+    { -5.604082963e-02f, +9.173389243e-01f, +1.460542124e-01f, -6.371398797e-03f },
+    { -5.606580530e-02f, +9.171369950e-01f, +1.462995018e-01f, -6.386304943e-03f },
+    { -5.609068575e-02f, +9.169348454e-01f, +1.465449385e-01f, -6.401230944e-03f },
+    { -5.611547107e-02f, +9.167324754e-01f, +1.467905223e-01f, -6.416176806e-03f },
+    { -5.614016133e-02f, +9.165298853e-01f, +1.470362531e-01f, -6.431142534e-03f },
+    { -5.616475663e-02f, +9.163270752e-01f, +1.472821311e-01f, -6.446128134e-03f },
+    { -5.618925704e-02f, +9.161240451e-01f, +1.475281560e-01f, -6.461133612e-03f },
+    { -5.621366265e-02f, +9.159207954e-01f, +1.477743278e-01f, -6.476158974e-03f },
+    { -5.623797353e-02f, +9.157173259e-01f, +1.480206466e-01f, -6.491204225e-03f },
+    { -5.626218978e-02f, +9.155136370e-01f, +1.482671122e-01f, -6.506269371e-03f },
+    { -5.628631147e-02f, +9.153097287e-01f, +1.485137245e-01f, -6.521354417e-03f },
+    { -5.631033869e-02f, +9.151056011e-01f, +1.487604837e-01f, -6.536459369e-03f },
+    { -5.633427152e-02f, +9.149012543e-01f, +1.490073895e-01f, -6.551584232e-03f },
+    { -5.635811004e-02f, +9.146966886e-01f, +1.492544420e-01f, -6.566729012e-03f },
+    { -5.638185433e-02f, +9.144919040e-01f, +1.495016410e-01f, -6.581893714e-03f },
+    { -5.640550448e-02f, +9.142869006e-01f, +1.497489866e-01f, -6.597078344e-03f },
+    { -5.642906057e-02f, +9.140816786e-01f, +1.499964787e-01f, -6.612282907e-03f },
+    { -5.645252268e-02f, +9.138762381e-01f, +1.502441173e-01f, -6.627507408e-03f },
+    { -5.647589090e-02f, +9.136705792e-01f, +1.504919022e-01f, -6.642751853e-03f },
+    { -5.649916531e-02f, +9.134647021e-01f, +1.507398335e-01f, -6.658016248e-03f },
+    { -5.652234599e-02f, +9.132586068e-01f, +1.509879110e-01f, -6.673300597e-03f },
+    { -5.654543302e-02f, +9.130522936e-01f, +1.512361348e-01f, -6.688604905e-03f },
+    { -5.656842649e-02f, +9.128457625e-01f, +1.514845048e-01f, -6.703929179e-03f },
+    { -5.659132648e-02f, +9.126390136e-01f, +1.517330210e-01f, -6.719273423e-03f },
+    { -5.661413307e-02f, +9.124320471e-01f, +1.519816832e-01f, -6.734637642e-03f },
+    { -5.663684634e-02f, +9.122248632e-01f, +1.522304914e-01f, -6.750021842e-03f },
+    { -5.665946638e-02f, +9.120174619e-01f, +1.524794457e-01f, -6.765426027e-03f },
+    { -5.668199328e-02f, +9.118098434e-01f, +1.527285458e-01f, -6.780850204e-03f },
+    { -5.670442710e-02f, +9.116020078e-01f, +1.529777918e-01f, -6.796294376e-03f },
+    { -5.672676795e-02f, +9.113939552e-01f, +1.532271837e-01f, -6.811758549e-03f },
+    { -5.674901589e-02f, +9.111856858e-01f, +1.534767213e-01f, -6.827242728e-03f },
+    { -5.677117101e-02f, +9.109771996e-01f, +1.537264047e-01f, -6.842746919e-03f },
+    { -5.679323340e-02f, +9.107684969e-01f, +1.539762337e-01f, -6.858271125e-03f },
+    { -5.681520314e-02f, +9.105595777e-01f, +1.542262084e-01f, -6.873815352e-03f },
+    { -5.683708031e-02f, +9.103504422e-01f, +1.544763286e-01f, -6.889379605e-03f },
+    { -5.685886499e-02f, +9.101410905e-01f, +1.547265943e-01f, -6.904963889e-03f },
+    { -5.688055727e-02f, +9.099315228e-01f, +1.549770055e-01f, -6.920568209e-03f },
+    { -5.690215723e-02f, +9.097217391e-01f, +1.552275620e-01f, -6.936192569e-03f },
+    { -5.692366496e-02f, +9.095117396e-01f, +1.554782640e-01f, -6.951836974e-03f },
+    { -5.694508053e-02f, +9.093015244e-01f, +1.557291112e-01f, -6.967501430e-03f },
+    { -5.696640402e-02f, +9.090910937e-01f, +1.559801037e-01f, -6.983185940e-03f },
+    { -5.698763554e-02f, +9.088804476e-01f, +1.562312413e-01f, -6.998890510e-03f },
+    { -5.700877514e-02f, +9.086695861e-01f, +1.564825241e-01f, -7.014615145e-03f },
+    { -5.702982292e-02f, +9.084585096e-01f, +1.567339520e-01f, -7.030359849e-03f },
+    { -5.705077897e-02f, +9.082472180e-01f, +1.569855249e-01f, -7.046124626e-03f },
+    { -5.707164336e-02f, +9.080357115e-01f, +1.572372428e-01f, -7.061909482e-03f },
+    { -5.709241618e-02f, +9.078239902e-01f, +1.574891056e-01f, -7.077714420e-03f },
+    { -5.711309750e-02f, +9.076120543e-01f, +1.577411133e-01f, -7.093539446e-03f },
+    { -5.713368743e-02f, +9.073999039e-01f, +1.579932657e-01f, -7.109384565e-03f },
+    { -5.715418602e-02f, +9.071875391e-01f, +1.582455630e-01f, -7.125249780e-03f },
+    { -5.717459338e-02f, +9.069749601e-01f, +1.584980049e-01f, -7.141135096e-03f },
+    { -5.719490958e-02f, +9.067621670e-01f, +1.587505915e-01f, -7.157040517e-03f },
+    { -5.721513471e-02f, +9.065491599e-01f, +1.590033227e-01f, -7.172966049e-03f },
+    { -5.723526885e-02f, +9.063359389e-01f, +1.592561984e-01f, -7.188911695e-03f },
+    { -5.725531208e-02f, +9.061225043e-01f, +1.595092186e-01f, -7.204877460e-03f },
+    { -5.727526448e-02f, +9.059088560e-01f, +1.597623832e-01f, -7.220863348e-03f },
+    { -5.729512615e-02f, +9.056949943e-01f, +1.600156922e-01f, -7.236869364e-03f },
+    { -5.731489716e-02f, +9.054809193e-01f, +1.602691455e-01f, -7.252895512e-03f },
+    { -5.733457759e-02f, +9.052666311e-01f, +1.605227430e-01f, -7.268941796e-03f },
+    { -5.735416753e-02f, +9.050521298e-01f, +1.607764848e-01f, -7.285008220e-03f },
+    { -5.737366706e-02f, +9.048374156e-01f, +1.610303707e-01f, -7.301094788e-03f },
+    { -5.739307627e-02f, +9.046224887e-01f, +1.612844007e-01f, -7.317201506e-03f },
+    { -5.741239524e-02f, +9.044073490e-01f, +1.615385747e-01f, -7.333328376e-03f },
+    { -5.743162405e-02f, +9.041919969e-01f, +1.617928927e-01f, -7.349475404e-03f },
+    { -5.745076279e-02f, +9.039764323e-01f, +1.620473546e-01f, -7.365642593e-03f },
+    { -5.746981153e-02f, +9.037606555e-01f, +1.623019604e-01f, -7.381829947e-03f },
+    { -5.748877036e-02f, +9.035446666e-01f, +1.625567100e-01f, -7.398037471e-03f },
+    { -5.750763937e-02f, +9.033284656e-01f, +1.628116034e-01f, -7.414265167e-03f },
+    { -5.752641864e-02f, +9.031120528e-01f, +1.630666404e-01f, -7.430513042e-03f },
+    { -5.754510825e-02f, +9.028954283e-01f, +1.633218211e-01f, -7.446781098e-03f },
+    { -5.756370829e-02f, +9.026785922e-01f, +1.635771453e-01f, -7.463069339e-03f },
+    { -5.758221883e-02f, +9.024615447e-01f, +1.638326131e-01f, -7.479377769e-03f },
+    { -5.760063997e-02f, +9.022442858e-01f, +1.640882243e-01f, -7.495706392e-03f },
+    { -5.761897178e-02f, +9.020268157e-01f, +1.643439789e-01f, -7.512055213e-03f },
+    { -5.763721435e-02f, +9.018091345e-01f, +1.645998769e-01f, -7.528424234e-03f },
+    { -5.765536777e-02f, +9.015912425e-01f, +1.648559182e-01f, -7.544813460e-03f },
+    { -5.767343210e-02f, +9.013731396e-01f, +1.651121027e-01f, -7.561222894e-03f },
+    { -5.769140745e-02f, +9.011548261e-01f, +1.653684303e-01f, -7.577652540e-03f },
+    { -5.770929389e-02f, +9.009363021e-01f, +1.656249011e-01f, -7.594102403e-03f },
+    { -5.772709151e-02f, +9.007175676e-01f, +1.658815150e-01f, -7.610572484e-03f },
+    { -5.774480038e-02f, +9.004986230e-01f, +1.661382718e-01f, -7.627062789e-03f },
+    { -5.776242060e-02f, +9.002794682e-01f, +1.663951716e-01f, -7.643573321e-03f },
+    { -5.777995224e-02f, +9.000601034e-01f, +1.666522142e-01f, -7.660104084e-03f },
+    { -5.779739540e-02f, +8.998405288e-01f, +1.669093997e-01f, -7.676655080e-03f },
+    { -5.781475014e-02f, +8.996207445e-01f, +1.671667279e-01f, -7.693226314e-03f },
+    { -5.783201657e-02f, +8.994007506e-01f, +1.674241988e-01f, -7.709817790e-03f },
+    { -5.784919475e-02f, +8.991805472e-01f, +1.676818124e-01f, -7.726429509e-03f },
+    { -5.786628478e-02f, +8.989601346e-01f, +1.679395686e-01f, -7.743061477e-03f },
+    { -5.788328673e-02f, +8.987395128e-01f, +1.681974672e-01f, -7.759713697e-03f },
+    { -5.790020070e-02f, +8.985186819e-01f, +1.684555084e-01f, -7.776386171e-03f },
+    { -5.791702676e-02f, +8.982976422e-01f, +1.687136919e-01f, -7.793078904e-03f },
+    { -5.793376500e-02f, +8.980763937e-01f, +1.689720178e-01f, -7.809791898e-03f },
+    { -5.795041550e-02f, +8.978549365e-01f, +1.692304860e-01f, -7.826525157e-03f },
+    { -5.796697834e-02f, +8.976332709e-01f, +1.694890963e-01f, -7.843278685e-03f },
+    { -5.798345361e-02f, +8.974113969e-01f, +1.697478489e-01f, -7.860052484e-03f },
+    { -5.799984140e-02f, +8.971893148e-01f, +1.700067435e-01f, -7.876846558e-03f },
+    { -5.801614178e-02f, +8.969670245e-01f, +1.702657802e-01f, -7.893660909e-03f },
+    { -5.803235484e-02f, +8.967445263e-01f, +1.705249588e-01f, -7.910495542e-03f },
+    { -5.804848066e-02f, +8.965218203e-01f, +1.707842794e-01f, -7.927350459e-03f },
+    { -5.806451933e-02f, +8.962989066e-01f, +1.710437419e-01f, -7.944225664e-03f },
+    { -5.808047093e-02f, +8.960757854e-01f, +1.713033461e-01f, -7.961121159e-03f },
+    { -5.809633554e-02f, +8.958524568e-01f, +1.715630920e-01f, -7.978036948e-03f },
+    { -5.811211325e-02f, +8.956289209e-01f, +1.718229797e-01f, -7.994973033e-03f },
+    { -5.812780414e-02f, +8.954051779e-01f, +1.720830089e-01f, -8.011929418e-03f },
+    { -5.814340830e-02f, +8.951812279e-01f, +1.723431797e-01f, -8.028906105e-03f },
+    { -5.815892580e-02f, +8.949570711e-01f, +1.726034920e-01f, -8.045903098e-03f },
+    { -5.817435673e-02f, +8.947327076e-01f, +1.728639457e-01f, -8.062920399e-03f },
+    { -5.818970119e-02f, +8.945081375e-01f, +1.731245407e-01f, -8.079958012e-03f },
+    { -5.820495924e-02f, +8.942833610e-01f, +1.733852771e-01f, -8.097015938e-03f },
+    { -5.822013097e-02f, +8.940583782e-01f, +1.736461547e-01f, -8.114094182e-03f },
+    { -5.823521647e-02f, +8.938331892e-01f, +1.739071734e-01f, -8.131192745e-03f },
+    { -5.825021582e-02f, +8.936077942e-01f, +1.741683333e-01f, -8.148311631e-03f },
+    { -5.826512910e-02f, +8.933821934e-01f, +1.744296343e-01f, -8.165450843e-03f },
+    { -5.827995640e-02f, +8.931563868e-01f, +1.746910762e-01f, -8.182610382e-03f },
+    { -5.829469780e-02f, +8.929303746e-01f, +1.749526590e-01f, -8.199790252e-03f },
+    { -5.830935339e-02f, +8.927041569e-01f, +1.752143827e-01f, -8.216990455e-03f },
+    { -5.832392325e-02f, +8.924777339e-01f, +1.754762472e-01f, -8.234210994e-03f },
+    { -5.833840746e-02f, +8.922511057e-01f, +1.757382524e-01f, -8.251451871e-03f },
+    { -5.835280610e-02f, +8.920242725e-01f, +1.760003983e-01f, -8.268713090e-03f },
+    { -5.836711927e-02f, +8.917972344e-01f, +1.762626848e-01f, -8.285994652e-03f },
+    { -5.838134704e-02f, +8.915699915e-01f, +1.765251119e-01f, -8.303296561e-03f },
+    { -5.839548949e-02f, +8.913425440e-01f, +1.767876794e-01f, -8.320618817e-03f },
+    { -5.840954672e-02f, +8.911148920e-01f, +1.770503873e-01f, -8.337961425e-03f },
+    { -5.842351880e-02f, +8.908870357e-01f, +1.773132356e-01f, -8.355324387e-03f },
+    { -5.843740582e-02f, +8.906589751e-01f, +1.775762242e-01f, -8.372707704e-03f },
+    { -5.845120786e-02f, +8.904307105e-01f, +1.778393529e-01f, -8.390111379e-03f },
+    { -5.846492501e-02f, +8.902022419e-01f, +1.781026219e-01f, -8.407535414e-03f },
+    { -5.847855735e-02f, +8.899735696e-01f, +1.783660309e-01f, -8.424979813e-03f },
+    { -5.849210496e-02f, +8.897446936e-01f, +1.786295800e-01f, -8.442444576e-03f },
+    { -5.850556793e-02f, +8.895156142e-01f, +1.788932690e-01f, -8.459929707e-03f },
+    { -5.851894634e-02f, +8.892863313e-01f, +1.791570979e-01f, -8.477435207e-03f },
+    { -5.853224028e-02f, +8.890568452e-01f, +1.794210666e-01f, -8.494961078e-03f },
+    { -5.854544983e-02f, +8.888271561e-01f, +1.796851751e-01f, -8.512507324e-03f },
+    { -5.855857506e-02f, +8.885972640e-01f, +1.799494234e-01f, -8.530073945e-03f },
+    { -5.857161608e-02f, +8.883671691e-01f, +1.802138112e-01f, -8.547660944e-03f },
+    { -5.858457295e-02f, +8.881368715e-01f, +1.804783386e-01f, -8.565268323e-03f },
+    { -5.859744577e-02f, +8.879063714e-01f, +1.807430055e-01f, -8.582896084e-03f },
+    { -5.861023462e-02f, +8.876756689e-01f, +1.810078118e-01f, -8.600544229e-03f },
+    { -5.862293958e-02f, +8.874447642e-01f, +1.812727576e-01f, -8.618212759e-03f },
+    { -5.863556073e-02f, +8.872136574e-01f, +1.815378426e-01f, -8.635901678e-03f },
+    { -5.864809816e-02f, +8.869823487e-01f, +1.818030668e-01f, -8.653610986e-03f },
+    { -5.866055196e-02f, +8.867508381e-01f, +1.820684302e-01f, -8.671340686e-03f },
+    { -5.867292220e-02f, +8.865191259e-01f, +1.823339328e-01f, -8.689090780e-03f },
+    { -5.868520898e-02f, +8.862872121e-01f, +1.825995743e-01f, -8.706861268e-03f },
+    { -5.869741236e-02f, +8.860550970e-01f, +1.828653548e-01f, -8.724652154e-03f },
+    { -5.870953245e-02f, +8.858227806e-01f, +1.831312743e-01f, -8.742463438e-03f },
+    { -5.872156932e-02f, +8.855902631e-01f, +1.833973325e-01f, -8.760295123e-03f },
+    { -5.873352306e-02f, +8.853575446e-01f, +1.836635296e-01f, -8.778147210e-03f },
+    { -5.874539374e-02f, +8.851246253e-01f, +1.839298653e-01f, -8.796019700e-03f },
+    { -5.875718146e-02f, +8.848915054e-01f, +1.841963397e-01f, -8.813912596e-03f },
+    { -5.876888630e-02f, +8.846581849e-01f, +1.844629526e-01f, -8.831825899e-03f },
+    { -5.878050834e-02f, +8.844246640e-01f, +1.847297040e-01f, -8.849759611e-03f },
+    { -5.879204767e-02f, +8.841909429e-01f, +1.849965939e-01f, -8.867713732e-03f },
+    { -5.880350436e-02f, +8.839570217e-01f, +1.852636221e-01f, -8.885688265e-03f },
+    { -5.881487851e-02f, +8.837229005e-01f, +1.855307886e-01f, -8.903683211e-03f },
+    { -5.882617019e-02f, +8.834885795e-01f, +1.857980933e-01f, -8.921698572e-03f },
+    { -5.883737950e-02f, +8.832540589e-01f, +1.860655362e-01f, -8.939734348e-03f },
+    { -5.884850651e-02f, +8.830193387e-01f, +1.863331172e-01f, -8.957790542e-03f },
+    { -5.885955131e-02f, +8.827844191e-01f, +1.866008361e-01f, -8.975867154e-03f },
+    { -5.887051398e-02f, +8.825493003e-01f, +1.868686931e-01f, -8.993964185e-03f },
+    { -5.888139461e-02f, +8.823139824e-01f, +1.871366879e-01f, -9.012081638e-03f },
+    { -5.889219328e-02f, +8.820784655e-01f, +1.874048205e-01f, -9.030219514e-03f },
+    { -5.890291007e-02f, +8.818427498e-01f, +1.876730909e-01f, -9.048377812e-03f },
+    { -5.891354507e-02f, +8.816068354e-01f, +1.879414989e-01f, -9.066556536e-03f },
+    { -5.892409837e-02f, +8.813707226e-01f, +1.882100446e-01f, -9.084755685e-03f },
+    { -5.893457003e-02f, +8.811344113e-01f, +1.884787277e-01f, -9.102975262e-03f },
+    { -5.894496016e-02f, +8.808979018e-01f, +1.887475484e-01f, -9.121215266e-03f },
+    { -5.895526884e-02f, +8.806611943e-01f, +1.890165064e-01f, -9.139475700e-03f },
+    { -5.896549614e-02f, +8.804242888e-01f, +1.892856017e-01f, -9.157756564e-03f },
+    { -5.897564215e-02f, +8.801871854e-01f, +1.895548343e-01f, -9.176057859e-03f },
+    { -5.898570696e-02f, +8.799498845e-01f, +1.898242041e-01f, -9.194379586e-03f },
+    { -5.899569065e-02f, +8.797123860e-01f, +1.900937110e-01f, -9.212721746e-03f },
+    { -5.900559330e-02f, +8.794746902e-01f, +1.903633550e-01f, -9.231084340e-03f },
+    { -5.901541499e-02f, +8.792367971e-01f, +1.906331359e-01f, -9.249467369e-03f },
+    { -5.902515582e-02f, +8.789987070e-01f, +1.909030537e-01f, -9.267870833e-03f },
+    { -5.903481587e-02f, +8.787604199e-01f, +1.911731083e-01f, -9.286294734e-03f },
+    { -5.904439521e-02f, +8.785219361e-01f, +1.914432997e-01f, -9.304739072e-03f },
+    { -5.905389393e-02f, +8.782832556e-01f, +1.917136278e-01f, -9.323203848e-03f },
+    { -5.906331213e-02f, +8.780443786e-01f, +1.919840925e-01f, -9.341689062e-03f },
+    { -5.907264987e-02f, +8.778053053e-01f, +1.922546937e-01f, -9.360194716e-03f },
+    { -5.908190725e-02f, +8.775660358e-01f, +1.925254314e-01f, -9.378720810e-03f },
+    { -5.909108434e-02f, +8.773265703e-01f, +1.927963055e-01f, -9.397267344e-03f },
+    { -5.910018124e-02f, +8.770869088e-01f, +1.930673159e-01f, -9.415834320e-03f },
+    { -5.910919802e-02f, +8.768470516e-01f, +1.933384626e-01f, -9.434421737e-03f },
+    { -5.911813478e-02f, +8.766069987e-01f, +1.936097455e-01f, -9.453029596e-03f },
+    { -5.912699159e-02f, +8.763667504e-01f, +1.938811645e-01f, -9.471657898e-03f },
+    { -5.913576853e-02f, +8.761263068e-01f, +1.941527195e-01f, -9.490306643e-03f },
+    { -5.914446570e-02f, +8.758856680e-01f, +1.944244105e-01f, -9.508975831e-03f },
+    { -5.915308317e-02f, +8.756448342e-01f, +1.946962373e-01f, -9.527665464e-03f },
+    { -5.916162104e-02f, +8.754038055e-01f, +1.949682000e-01f, -9.546375540e-03f },
+    { -5.917007938e-02f, +8.751625821e-01f, +1.952402985e-01f, -9.565106062e-03f },
+    { -5.917845827e-02f, +8.749211640e-01f, +1.955125326e-01f, -9.583857028e-03f },
+    { -5.918675781e-02f, +8.746795516e-01f, +1.957849023e-01f, -9.602628439e-03f },
+    { -5.919497807e-02f, +8.744377448e-01f, +1.960574076e-01f, -9.621420295e-03f },
+    { -5.920311914e-02f, +8.741957439e-01f, +1.963300483e-01f, -9.640232597e-03f },
+    { -5.921118110e-02f, +8.739535491e-01f, +1.966028244e-01f, -9.659065344e-03f },
+    { -5.921916404e-02f, +8.737111603e-01f, +1.968757357e-01f, -9.677918538e-03f },
+    { -5.922706804e-02f, +8.734685779e-01f, +1.971487824e-01f, -9.696792177e-03f },
+    { -5.923489319e-02f, +8.732258019e-01f, +1.974219642e-01f, -9.715686262e-03f },
+    { -5.924263957e-02f, +8.729828325e-01f, +1.976952810e-01f, -9.734600793e-03f },
+    { -5.925030726e-02f, +8.727396699e-01f, +1.979687329e-01f, -9.753535770e-03f },
+    { -5.925789634e-02f, +8.724963141e-01f, +1.982423197e-01f, -9.772491192e-03f },
+    { -5.926540690e-02f, +8.722527654e-01f, +1.985160414e-01f, -9.791467061e-03f },
+    { -5.927283903e-02f, +8.720090239e-01f, +1.987898979e-01f, -9.810463375e-03f },
+    { -5.928019281e-02f, +8.717650897e-01f, +1.990638891e-01f, -9.829480135e-03f },
+    { -5.928746832e-02f, +8.715209630e-01f, +1.993380149e-01f, -9.848517340e-03f },
+    { -5.929466565e-02f, +8.712766439e-01f, +1.996122753e-01f, -9.867574990e-03f },
+    { -5.930178487e-02f, +8.710321326e-01f, +1.998866701e-01f, -9.886653086e-03f },
+    { -5.930882608e-02f, +8.707874293e-01f, +2.001611994e-01f, -9.905751626e-03f },
+    { -5.931578936e-02f, +8.705425340e-01f, +2.004358631e-01f, -9.924870610e-03f },
+    { -5.932267479e-02f, +8.702974470e-01f, +2.007106609e-01f, -9.944010038e-03f },
+    { -5.932948245e-02f, +8.700521683e-01f, +2.009855930e-01f, -9.963169910e-03f },
+    { -5.933621243e-02f, +8.698066982e-01f, +2.012606592e-01f, -9.982350225e-03f },
+    { -5.934286482e-02f, +8.695610368e-01f, +2.015358594e-01f, -1.000155098e-02f },
+    { -5.934943969e-02f, +8.693151841e-01f, +2.018111936e-01f, -1.002077218e-02f },
+    { -5.935593714e-02f, +8.690691405e-01f, +2.020866616e-01f, -1.004001383e-02f },
+    { -5.936235724e-02f, +8.688229060e-01f, +2.023622635e-01f, -1.005927591e-02f },
+    { -5.936870008e-02f, +8.685764808e-01f, +2.026379991e-01f, -1.007855843e-02f },
+    { -5.937496574e-02f, +8.683298650e-01f, +2.029138683e-01f, -1.009786140e-02f },
+    { -5.938115430e-02f, +8.680830587e-01f, +2.031898712e-01f, -1.011718480e-02f },
+    { -5.938726586e-02f, +8.678360622e-01f, +2.034660075e-01f, -1.013652864e-02f },
+    { -5.939330049e-02f, +8.675888756e-01f, +2.037422772e-01f, -1.015589292e-02f },
+    { -5.939925828e-02f, +8.673414990e-01f, +2.040186803e-01f, -1.017527764e-02f },
+    { -5.940513931e-02f, +8.670939326e-01f, +2.042952167e-01f, -1.019468279e-02f },
+    { -5.941094367e-02f, +8.668461765e-01f, +2.045718862e-01f, -1.021410838e-02f },
+    { -5.941667144e-02f, +8.665982309e-01f, +2.048486889e-01f, -1.023355441e-02f },
+    { -5.942232269e-02f, +8.663500959e-01f, +2.051256246e-01f, -1.025302087e-02f },
+    { -5.942789753e-02f, +8.661017718e-01f, +2.054026933e-01f, -1.027250776e-02f },
+    { -5.943339603e-02f, +8.658532585e-01f, +2.056798948e-01f, -1.029201508e-02f },
+    { -5.943881827e-02f, +8.656045563e-01f, +2.059572292e-01f, -1.031154284e-02f },
+    { -5.944416434e-02f, +8.653556654e-01f, +2.062346962e-01f, -1.033109103e-02f },
+    { -5.944943433e-02f, +8.651065858e-01f, +2.065122959e-01f, -1.035065964e-02f },
+    { -5.945462831e-02f, +8.648573178e-01f, +2.067900282e-01f, -1.037024868e-02f },
+    { -5.945974637e-02f, +8.646078615e-01f, +2.070678930e-01f, -1.038985815e-02f },
+    { -5.946478859e-02f, +8.643582170e-01f, +2.073458902e-01f, -1.040948805e-02f },
+    { -5.946975506e-02f, +8.641083845e-01f, +2.076240197e-01f, -1.042913837e-02f },
+    { -5.947464586e-02f, +8.638583642e-01f, +2.079022815e-01f, -1.044880911e-02f },
+    { -5.947946108e-02f, +8.636081562e-01f, +2.081806754e-01f, -1.046850028e-02f },
+    { -5.948420080e-02f, +8.633577606e-01f, +2.084592015e-01f, -1.048821187e-02f },
+    { -5.948886510e-02f, +8.631071776e-01f, +2.087378595e-01f, -1.050794387e-02f },
+    { -5.949345407e-02f, +8.628564073e-01f, +2.090166495e-01f, -1.052769630e-02f },
+    { -5.949796779e-02f, +8.626054500e-01f, +2.092955714e-01f, -1.054746914e-02f },
+    { -5.950240634e-02f, +8.623543057e-01f, +2.095746250e-01f, -1.056726239e-02f },
+    { -5.950676981e-02f, +8.621029746e-01f, +2.098538103e-01f, -1.058707606e-02f },
+    { -5.951105828e-02f, +8.618514569e-01f, +2.101331273e-01f, -1.060691015e-02f },
+    { -5.951527184e-02f, +8.615997527e-01f, +2.104125758e-01f, -1.062676464e-02f },
+    { -5.951941056e-02f, +8.613478622e-01f, +2.106921557e-01f, -1.064663954e-02f },
+    { -5.952347454e-02f, +8.610957855e-01f, +2.109718671e-01f, -1.066653486e-02f },
+    { -5.952746386e-02f, +8.608435227e-01f, +2.112517097e-01f, -1.068645057e-02f },
+    { -5.953137860e-02f, +8.605910741e-01f, +2.115316835e-01f, -1.070638669e-02f },
+    { -5.953521884e-02f, +8.603384398e-01f, +2.118117885e-01f, -1.072634322e-02f },
+    { -5.953898467e-02f, +8.600856199e-01f, +2.120920245e-01f, -1.074632015e-02f },
+    { -5.954267617e-02f, +8.598326146e-01f, +2.123723915e-01f, -1.076631747e-02f },
+    { -5.954629343e-02f, +8.595794240e-01f, +2.126528894e-01f, -1.078633519e-02f },
+    { -5.954983652e-02f, +8.593260484e-01f, +2.129335181e-01f, -1.080637331e-02f },
+    { -5.955330554e-02f, +8.590724878e-01f, +2.132142776e-01f, -1.082643182e-02f },
+    { -5.955670057e-02f, +8.588187423e-01f, +2.134951677e-01f, -1.084651073e-02f },
+    { -5.956002168e-02f, +8.585648123e-01f, +2.137761883e-01f, -1.086661002e-02f },
+    { -5.956326897e-02f, +8.583106977e-01f, +2.140573395e-01f, -1.088672970e-02f },
+    { -5.956644252e-02f, +8.580563988e-01f, +2.143386210e-01f, -1.090686977e-02f },
+    { -5.956954241e-02f, +8.578019157e-01f, +2.146200329e-01f, -1.092703022e-02f },
+    { -5.957256872e-02f, +8.575472486e-01f, +2.149015750e-01f, -1.094721106e-02f },
+    { -5.957552154e-02f, +8.572923976e-01f, +2.151832473e-01f, -1.096741227e-02f },
+    { -5.957840096e-02f, +8.570373629e-01f, +2.154650497e-01f, -1.098763386e-02f },
+    { -5.958120705e-02f, +8.567821446e-01f, +2.157469820e-01f, -1.100787583e-02f },
+    { -5.958393990e-02f, +8.565267430e-01f, +2.160290443e-01f, -1.102813817e-02f },
+    { -5.958659959e-02f, +8.562711580e-01f, +2.163112364e-01f, -1.104842088e-02f },
+    { -5.958918621e-02f, +8.560153900e-01f, +2.165935582e-01f, -1.106872396e-02f },
+    { -5.959169984e-02f, +8.557594390e-01f, +2.168760097e-01f, -1.108904740e-02f },
+    { -5.959414057e-02f, +8.555033052e-01f, +2.171585908e-01f, -1.110939121e-02f },
+    { -5.959650847e-02f, +8.552469888e-01f, +2.174413013e-01f, -1.112975538e-02f },
+    { -5.959880364e-02f, +8.549904899e-01f, +2.177241413e-01f, -1.115013991e-02f },
+    { -5.960102614e-02f, +8.547338087e-01f, +2.180071107e-01f, -1.117054480e-02f },
+    { -5.960317608e-02f, +8.544769453e-01f, +2.182902092e-01f, -1.119097004e-02f },
+    { -5.960525353e-02f, +8.542198999e-01f, +2.185734369e-01f, -1.121141563e-02f },
+    { -5.960725858e-02f, +8.539626726e-01f, +2.188567938e-01f, -1.123188157e-02f },
+    { -5.960919130e-02f, +8.537052637e-01f, +2.191402796e-01f, -1.125236786e-02f },
+    { -5.961105179e-02f, +8.534476731e-01f, +2.194238943e-01f, -1.127287449e-02f },
+    { -5.961284013e-02f, +8.531899012e-01f, +2.197076378e-01f, -1.129340147e-02f },
+    { -5.961455639e-02f, +8.529319481e-01f, +2.199915101e-01f, -1.131394878e-02f },
+    { -5.961620067e-02f, +8.526738139e-01f, +2.202755110e-01f, -1.133451643e-02f },
+    { -5.961777305e-02f, +8.524154987e-01f, +2.205596406e-01f, -1.135510441e-02f },
+    { -5.961927360e-02f, +8.521570028e-01f, +2.208438986e-01f, -1.137571272e-02f },
+    { -5.962070242e-02f, +8.518983262e-01f, +2.211282850e-01f, -1.139634135e-02f },
+    { -5.962205959e-02f, +8.516394693e-01f, +2.214127997e-01f, -1.141699032e-02f },
+    { -5.962334519e-02f, +8.513804320e-01f, +2.216974426e-01f, -1.143765960e-02f },
+    { -5.962455930e-02f, +8.511212145e-01f, +2.219822138e-01f, -1.145834920e-02f },
+    { -5.962570201e-02f, +8.508618171e-01f, +2.222671129e-01f, -1.147905912e-02f },
+    { -5.962677341e-02f, +8.506022399e-01f, +2.225521401e-01f, -1.149978935e-02f },
+    { -5.962777357e-02f, +8.503424830e-01f, +2.228372951e-01f, -1.152053989e-02f },
+    { -5.962870257e-02f, +8.500825465e-01f, +2.231225780e-01f, -1.154131073e-02f },
+    { -5.962956051e-02f, +8.498224307e-01f, +2.234079885e-01f, -1.156210188e-02f },
+    { -5.963034747e-02f, +8.495621358e-01f, +2.236935267e-01f, -1.158291333e-02f },
+    { -5.963106352e-02f, +8.493016617e-01f, +2.239791925e-01f, -1.160374507e-02f },
+    { -5.963170876e-02f, +8.490410088e-01f, +2.242649857e-01f, -1.162459711e-02f },
+    { -5.963228326e-02f, +8.487801771e-01f, +2.245509063e-01f, -1.164546944e-02f },
+    { -5.963278711e-02f, +8.485191669e-01f, +2.248369542e-01f, -1.166636205e-02f },
+    { -5.963322039e-02f, +8.482579783e-01f, +2.251231293e-01f, -1.168727495e-02f },
+    { -5.963358319e-02f, +8.479966114e-01f, +2.254094315e-01f, -1.170820813e-02f },
+    { -5.963387559e-02f, +8.477350664e-01f, +2.256958607e-01f, -1.172916159e-02f },
+    { -5.963409767e-02f, +8.474733435e-01f, +2.259824169e-01f, -1.175013531e-02f },
+    { -5.963424952e-02f, +8.472114428e-01f, +2.262690999e-01f, -1.177112931e-02f },
+    { -5.963433122e-02f, +8.469493644e-01f, +2.265559097e-01f, -1.179214358e-02f },
+    { -5.963434285e-02f, +8.466871086e-01f, +2.268428462e-01f, -1.181317810e-02f },
+    { -5.963428449e-02f, +8.464246755e-01f, +2.271299092e-01f, -1.183423289e-02f },
+    { -5.963415624e-02f, +8.461620653e-01f, +2.274170988e-01f, -1.185530793e-02f },
+    { -5.963395817e-02f, +8.458992780e-01f, +2.277044148e-01f, -1.187640322e-02f },
+    { -5.963369036e-02f, +8.456363139e-01f, +2.279918571e-01f, -1.189751876e-02f },
+    { -5.963335291e-02f, +8.453731732e-01f, +2.282794257e-01f, -1.191865455e-02f },
+    { -5.963294588e-02f, +8.451098559e-01f, +2.285671204e-01f, -1.193981057e-02f },
+    { -5.963246938e-02f, +8.448463623e-01f, +2.288549412e-01f, -1.196098683e-02f },
+    { -5.963192347e-02f, +8.445826924e-01f, +2.291428879e-01f, -1.198218333e-02f },
+    { -5.963130824e-02f, +8.443188466e-01f, +2.294309606e-01f, -1.200340005e-02f },
+    { -5.963062378e-02f, +8.440548248e-01f, +2.297191590e-01f, -1.202463700e-02f },
+    { -5.962987017e-02f, +8.437906274e-01f, +2.300074831e-01f, -1.204589416e-02f },
+    { -5.962904749e-02f, +8.435262543e-01f, +2.302959329e-01f, -1.206717155e-02f },
+    { -5.962815583e-02f, +8.432617059e-01f, +2.305845082e-01f, -1.208846914e-02f },
+    { -5.962719526e-02f, +8.429969822e-01f, +2.308732090e-01f, -1.210978695e-02f },
+    { -5.962616588e-02f, +8.427320835e-01f, +2.311620351e-01f, -1.213112496e-02f },
+    { -5.962506776e-02f, +8.424670098e-01f, +2.314509865e-01f, -1.215248317e-02f },
+    { -5.962390099e-02f, +8.422017613e-01f, +2.317400630e-01f, -1.217386158e-02f },
+    { -5.962266566e-02f, +8.419363383e-01f, +2.320292646e-01f, -1.219526018e-02f },
+    { -5.962136183e-02f, +8.416707408e-01f, +2.323185913e-01f, -1.221667896e-02f },
+    { -5.961998961e-02f, +8.414049690e-01f, +2.326080428e-01f, -1.223811793e-02f },
+    { -5.961854906e-02f, +8.411390231e-01f, +2.328976192e-01f, -1.225957708e-02f },
+    { -5.961704028e-02f, +8.408729033e-01f, +2.331873202e-01f, -1.228105640e-02f },
+    { -5.961546335e-02f, +8.406066096e-01f, +2.334771460e-01f, -1.230255589e-02f },
+    { -5.961381834e-02f, +8.403401423e-01f, +2.337670962e-01f, -1.232407555e-02f },
+    { -5.961210535e-02f, +8.400735015e-01f, +2.340571709e-01f, -1.234561537e-02f },
+    { -5.961032446e-02f, +8.398066874e-01f, +2.343473700e-01f, -1.236717535e-02f },
+    { -5.960847575e-02f, +8.395397001e-01f, +2.346376934e-01f, -1.238875548e-02f },
+    { -5.960655929e-02f, +8.392725398e-01f, +2.349281409e-01f, -1.241035575e-02f },
+    { -5.960457519e-02f, +8.390052067e-01f, +2.352187125e-01f, -1.243197617e-02f },
+    { -5.960252351e-02f, +8.387377009e-01f, +2.355094081e-01f, -1.245361672e-02f },
+    { -5.960040434e-02f, +8.384700225e-01f, +2.358002277e-01f, -1.247527741e-02f },
+    { -5.959821777e-02f, +8.382021718e-01f, +2.360911710e-01f, -1.249695823e-02f },
+    { -5.959596388e-02f, +8.379341490e-01f, +2.363822381e-01f, -1.251865917e-02f },
+    { -5.959364274e-02f, +8.376659540e-01f, +2.366734288e-01f, -1.254038023e-02f },
+    { -5.959125445e-02f, +8.373975872e-01f, +2.369647431e-01f, -1.256212141e-02f },
+    { -5.958879909e-02f, +8.371290487e-01f, +2.372561808e-01f, -1.258388269e-02f },
+    { -5.958627674e-02f, +8.368603387e-01f, +2.375477418e-01f, -1.260566408e-02f },
+    { -5.958368747e-02f, +8.365914572e-01f, +2.378394262e-01f, -1.262746557e-02f },
+    { -5.958103139e-02f, +8.363224045e-01f, +2.381312337e-01f, -1.264928715e-02f },
+    { -5.957830856e-02f, +8.360531808e-01f, +2.384231642e-01f, -1.267112882e-02f },
+    { -5.957551907e-02f, +8.357837861e-01f, +2.387152178e-01f, -1.269299057e-02f },
+    { -5.957266301e-02f, +8.355142207e-01f, +2.390073943e-01f, -1.271487241e-02f },
+    { -5.956974045e-02f, +8.352444848e-01f, +2.392996936e-01f, -1.273677432e-02f },
+    { -5.956675149e-02f, +8.349745784e-01f, +2.395921155e-01f, -1.275869629e-02f },
+    { -5.956369619e-02f, +8.347045017e-01f, +2.398846601e-01f, -1.278063833e-02f },
+    { -5.956057466e-02f, +8.344342550e-01f, +2.401773273e-01f, -1.280260043e-02f },
+    { -5.955738696e-02f, +8.341638383e-01f, +2.404701168e-01f, -1.282458258e-02f },
+    { -5.955413318e-02f, +8.338932518e-01f, +2.407630287e-01f, -1.284658477e-02f },
+    { -5.955081340e-02f, +8.336224957e-01f, +2.410560629e-01f, -1.286860701e-02f },
+    { -5.954742771e-02f, +8.333515702e-01f, +2.413492191e-01f, -1.289064929e-02f },
+    { -5.954397620e-02f, +8.330804754e-01f, +2.416424975e-01f, -1.291271159e-02f },
+    { -5.954045893e-02f, +8.328092115e-01f, +2.419358978e-01f, -1.293479392e-02f },
+    { -5.953687600e-02f, +8.325377786e-01f, +2.422294200e-01f, -1.295689627e-02f },
+    { -5.953322748e-02f, +8.322661770e-01f, +2.425230639e-01f, -1.297901864e-02f },
+    { -5.952951347e-02f, +8.319944067e-01f, +2.428168296e-01f, -1.300116101e-02f },
+    { -5.952573404e-02f, +8.317224679e-01f, +2.431107168e-01f, -1.302332339e-02f },
+    { -5.952188928e-02f, +8.314503608e-01f, +2.434047255e-01f, -1.304550576e-02f },
+    { -5.951797926e-02f, +8.311780856e-01f, +2.436988556e-01f, -1.306770813e-02f },
+    { -5.951400408e-02f, +8.309056424e-01f, +2.439931070e-01f, -1.308993048e-02f },
+    { -5.950996381e-02f, +8.306330313e-01f, +2.442874796e-01f, -1.311217281e-02f },
+    { -5.950585853e-02f, +8.303602527e-01f, +2.445819733e-01f, -1.313443511e-02f },
+    { -5.950168834e-02f, +8.300873065e-01f, +2.448765881e-01f, -1.315671739e-02f },
+    { -5.949745330e-02f, +8.298141930e-01f, +2.451713237e-01f, -1.317901962e-02f },
+    { -5.949315351e-02f, +8.295409124e-01f, +2.454661802e-01f, -1.320134181e-02f },
+    { -5.948878905e-02f, +8.292674647e-01f, +2.457611574e-01f, -1.322368395e-02f },
+    { -5.948436000e-02f, +8.289938502e-01f, +2.460562553e-01f, -1.324604604e-02f },
+    { -5.947986644e-02f, +8.287200691e-01f, +2.463514737e-01f, -1.326842806e-02f },
+    { -5.947530845e-02f, +8.284461214e-01f, +2.466468125e-01f, -1.329083001e-02f },
+    { -5.947068612e-02f, +8.281720074e-01f, +2.469422717e-01f, -1.331325190e-02f },
+    { -5.946599953e-02f, +8.278977272e-01f, +2.472378511e-01f, -1.333569370e-02f },
+    { -5.946124877e-02f, +8.276232810e-01f, +2.475335507e-01f, -1.335815541e-02f },
+    { -5.945643390e-02f, +8.273486689e-01f, +2.478293703e-01f, -1.338063703e-02f },
+    { -5.945155503e-02f, +8.270738912e-01f, +2.481253099e-01f, -1.340313855e-02f },
+    { -5.944661222e-02f, +8.267989479e-01f, +2.484213694e-01f, -1.342565996e-02f },
+    { -5.944160557e-02f, +8.265238393e-01f, +2.487175486e-01f, -1.344820127e-02f },
+    { -5.943653515e-02f, +8.262485655e-01f, +2.490138475e-01f, -1.347076245e-02f },
+    { -5.943140104e-02f, +8.259731267e-01f, +2.493102660e-01f, -1.349334351e-02f },
+    { -5.942620334e-02f, +8.256975230e-01f, +2.496068040e-01f, -1.351594444e-02f },
+    { -5.942094212e-02f, +8.254217546e-01f, +2.499034613e-01f, -1.353856523e-02f },
+    { -5.941561746e-02f, +8.251458217e-01f, +2.502002379e-01f, -1.356120587e-02f },
+    { -5.941022944e-02f, +8.248697244e-01f, +2.504971337e-01f, -1.358386636e-02f },
+    { -5.940477816e-02f, +8.245934629e-01f, +2.507941486e-01f, -1.360654670e-02f },
+    { -5.939926369e-02f, +8.243170373e-01f, +2.510912825e-01f, -1.362924687e-02f },
+    { -5.939368611e-02f, +8.240404479e-01f, +2.513885353e-01f, -1.365196686e-02f },
+    { -5.938804550e-02f, +8.237636948e-01f, +2.516859068e-01f, -1.367470668e-02f },
+    { -5.938234196e-02f, +8.234867781e-01f, +2.519833971e-01f, -1.369746632e-02f },
+    { -5.937657555e-02f, +8.232096981e-01f, +2.522810059e-01f, -1.372024576e-02f },
+    { -5.937074636e-02f, +8.229324548e-01f, +2.525787333e-01f, -1.374304500e-02f },
+    { -5.936485448e-02f, +8.226550485e-01f, +2.528765790e-01f, -1.376586404e-02f },
+    { -5.935889999e-02f, +8.223774794e-01f, +2.531745431e-01f, -1.378870286e-02f },
+    { -5.935288297e-02f, +8.220997475e-01f, +2.534726253e-01f, -1.381156147e-02f },
+    { -5.934680349e-02f, +8.218218531e-01f, +2.537708257e-01f, -1.383443984e-02f },
+    { -5.934066165e-02f, +8.215437962e-01f, +2.540691440e-01f, -1.385733799e-02f },
+    { -5.933445753e-02f, +8.212655772e-01f, +2.543675803e-01f, -1.388025589e-02f },
+    { -5.932819120e-02f, +8.209871961e-01f, +2.546661344e-01f, -1.390319354e-02f },
+    { -5.932186275e-02f, +8.207086532e-01f, +2.549648062e-01f, -1.392615094e-02f },
+    { -5.931547226e-02f, +8.204299485e-01f, +2.552635955e-01f, -1.394912807e-02f },
+    { -5.930901982e-02f, +8.201510823e-01f, +2.555625024e-01f, -1.397212493e-02f },
+    { -5.930250550e-02f, +8.198720547e-01f, +2.558615267e-01f, -1.399514152e-02f },
+    { -5.929592939e-02f, +8.195928658e-01f, +2.561606683e-01f, -1.401817782e-02f },
+    { -5.928929157e-02f, +8.193135159e-01f, +2.564599271e-01f, -1.404123383e-02f },
+    { -5.928259213e-02f, +8.190340052e-01f, +2.567593031e-01f, -1.406430953e-02f },
+    { -5.927583113e-02f, +8.187543337e-01f, +2.570587960e-01f, -1.408740493e-02f },
+    { -5.926900867e-02f, +8.184745017e-01f, +2.573584058e-01f, -1.411052002e-02f },
+    { -5.926212483e-02f, +8.181945092e-01f, +2.576581325e-01f, -1.413365478e-02f },
+    { -5.925517969e-02f, +8.179143566e-01f, +2.579579758e-01f, -1.415680921e-02f },
+    { -5.924817333e-02f, +8.176340439e-01f, +2.582579358e-01f, -1.417998331e-02f },
+    { -5.924110584e-02f, +8.173535714e-01f, +2.585580122e-01f, -1.420317705e-02f },
+    { -5.923397729e-02f, +8.170729391e-01f, +2.588582051e-01f, -1.422639045e-02f },
+    { -5.922678777e-02f, +8.167921473e-01f, +2.591585142e-01f, -1.424962348e-02f },
+    { -5.921953735e-02f, +8.165111960e-01f, +2.594589396e-01f, -1.427287614e-02f },
+    { -5.921222613e-02f, +8.162300856e-01f, +2.597594810e-01f, -1.429614843e-02f },
+    { -5.920485418e-02f, +8.159488162e-01f, +2.600601385e-01f, -1.431944033e-02f },
+    { -5.919742158e-02f, +8.156673878e-01f, +2.603609118e-01f, -1.434275184e-02f },
+    { -5.918992842e-02f, +8.153858007e-01f, +2.606618010e-01f, -1.436608295e-02f },
+    { -5.918237478e-02f, +8.151040551e-01f, +2.609628058e-01f, -1.438943365e-02f },
+    { -5.917476074e-02f, +8.148221512e-01f, +2.612639263e-01f, -1.441280393e-02f },
+    { -5.916708638e-02f, +8.145400890e-01f, +2.615651622e-01f, -1.443619379e-02f },
+    { -5.915935179e-02f, +8.142578688e-01f, +2.618665135e-01f, -1.445960321e-02f },
+    { -5.915155704e-02f, +8.139754907e-01f, +2.621679801e-01f, -1.448303220e-02f },
+    { -5.914370221e-02f, +8.136929549e-01f, +2.624695618e-01f, -1.450648073e-02f },
+    { -5.913578740e-02f, +8.134102615e-01f, +2.627712587e-01f, -1.452994881e-02f },
+    { -5.912781267e-02f, +8.131274108e-01f, +2.630730705e-01f, -1.455343641e-02f },
+    { -5.911977812e-02f, +8.128444029e-01f, +2.633749972e-01f, -1.457694355e-02f },
+    { -5.911168382e-02f, +8.125612380e-01f, +2.636770386e-01f, -1.460047020e-02f },
+    { -5.910352985e-02f, +8.122779162e-01f, +2.639791948e-01f, -1.462401636e-02f },
+    { -5.909531630e-02f, +8.119944377e-01f, +2.642814655e-01f, -1.464758202e-02f },
+    { -5.908704325e-02f, +8.117108027e-01f, +2.645838506e-01f, -1.467116717e-02f },
+    { -5.907871078e-02f, +8.114270114e-01f, +2.648863501e-01f, -1.469477181e-02f },
+    { -5.907031897e-02f, +8.111430638e-01f, +2.651889639e-01f, -1.471839591e-02f },
+    { -5.906186790e-02f, +8.108589603e-01f, +2.654916918e-01f, -1.474203949e-02f },
+    { -5.905335765e-02f, +8.105747008e-01f, +2.657945338e-01f, -1.476570252e-02f },
+    { -5.904478831e-02f, +8.102902857e-01f, +2.660974897e-01f, -1.478938500e-02f },
+    { -5.903615996e-02f, +8.100057151e-01f, +2.664005594e-01f, -1.481308692e-02f },
+    { -5.902747268e-02f, +8.097209892e-01f, +2.667037429e-01f, -1.483680827e-02f },
+    { -5.901872654e-02f, +8.094361081e-01f, +2.670070400e-01f, -1.486054904e-02f },
+    { -5.900992164e-02f, +8.091510719e-01f, +2.673104506e-01f, -1.488430923e-02f },
+    { -5.900105805e-02f, +8.088658810e-01f, +2.676139746e-01f, -1.490808882e-02f },
+    { -5.899213585e-02f, +8.085805354e-01f, +2.679176120e-01f, -1.493188780e-02f },
+    { -5.898315513e-02f, +8.082950352e-01f, +2.682213626e-01f, -1.495570618e-02f },
+    { -5.897411596e-02f, +8.080093808e-01f, +2.685252263e-01f, -1.497954392e-02f },
+    { -5.896501843e-02f, +8.077235722e-01f, +2.688292029e-01f, -1.500340104e-02f },
+    { -5.895586262e-02f, +8.074376096e-01f, +2.691332925e-01f, -1.502727751e-02f },
+    { -5.894664861e-02f, +8.071514932e-01f, +2.694374949e-01f, -1.505117334e-02f },
+    { -5.893737648e-02f, +8.068652231e-01f, +2.697418099e-01f, -1.507508850e-02f },
+    { -5.892804632e-02f, +8.065787996e-01f, +2.700462376e-01f, -1.509902300e-02f },
+    { -5.891865819e-02f, +8.062922227e-01f, +2.703507777e-01f, -1.512297682e-02f },
+    { -5.890921220e-02f, +8.060054928e-01f, +2.706554301e-01f, -1.514694995e-02f },
+    { -5.889970841e-02f, +8.057186098e-01f, +2.709601949e-01f, -1.517094238e-02f },
+    { -5.889014690e-02f, +8.054315741e-01f, +2.712650718e-01f, -1.519495411e-02f },
+    { -5.888052777e-02f, +8.051443857e-01f, +2.715700607e-01f, -1.521898512e-02f },
+    { -5.887085108e-02f, +8.048570449e-01f, +2.718751616e-01f, -1.524303541e-02f },
+    { -5.886111692e-02f, +8.045695518e-01f, +2.721803743e-01f, -1.526710496e-02f },
+    { -5.885132538e-02f, +8.042819066e-01f, +2.724856987e-01f, -1.529119377e-02f },
+    { -5.884147653e-02f, +8.039941094e-01f, +2.727911348e-01f, -1.531530183e-02f },
+    { -5.883157045e-02f, +8.037061604e-01f, +2.730966824e-01f, -1.533942912e-02f },
+    { -5.882160722e-02f, +8.034180599e-01f, +2.734023414e-01f, -1.536357563e-02f },
+    { -5.881158693e-02f, +8.031298079e-01f, +2.737081117e-01f, -1.538774137e-02f },
+    { -5.880150966e-02f, +8.028414047e-01f, +2.740139932e-01f, -1.541192631e-02f },
+    { -5.879137549e-02f, +8.025528503e-01f, +2.743199858e-01f, -1.543613045e-02f },
+    { -5.878118449e-02f, +8.022641451e-01f, +2.746260894e-01f, -1.546035377e-02f },
+    { -5.877093675e-02f, +8.019752891e-01f, +2.749323038e-01f, -1.548459628e-02f },
+    { -5.876063235e-02f, +8.016862825e-01f, +2.752386291e-01f, -1.550885795e-02f },
+    { -5.875027137e-02f, +8.013971255e-01f, +2.755450649e-01f, -1.553313878e-02f },
+    { -5.873985390e-02f, +8.011078183e-01f, +2.758516114e-01f, -1.555743875e-02f },
+    { -5.872938000e-02f, +8.008183610e-01f, +2.761582682e-01f, -1.558175787e-02f },
+    { -5.871884977e-02f, +8.005287538e-01f, +2.764650354e-01f, -1.560609611e-02f },
+    { -5.870826329e-02f, +8.002389969e-01f, +2.767719129e-01f, -1.563045347e-02f },
+    { -5.869762062e-02f, +7.999490904e-01f, +2.770789004e-01f, -1.565482993e-02f },
+    { -5.868692187e-02f, +7.996590346e-01f, +2.773859980e-01f, -1.567922549e-02f },
+    { -5.867616710e-02f, +7.993688295e-01f, +2.776932054e-01f, -1.570364014e-02f },
+    { -5.866535639e-02f, +7.990784754e-01f, +2.780005227e-01f, -1.572807386e-02f },
+    { -5.865448983e-02f, +7.987879725e-01f, +2.783079496e-01f, -1.575252665e-02f },
+    { -5.864356750e-02f, +7.984973209e-01f, +2.786154861e-01f, -1.577699850e-02f },
+    { -5.863258948e-02f, +7.982065207e-01f, +2.789231321e-01f, -1.580148939e-02f },
+    { -5.862155585e-02f, +7.979155722e-01f, +2.792308874e-01f, -1.582599931e-02f },
+    { -5.861046669e-02f, +7.976244755e-01f, +2.795387520e-01f, -1.585052826e-02f },
+    { -5.859932207e-02f, +7.973332308e-01f, +2.798467258e-01f, -1.587507622e-02f },
+    { -5.858812209e-02f, +7.970418383e-01f, +2.801548085e-01f, -1.589964318e-02f },
+    { -5.857686682e-02f, +7.967502981e-01f, +2.804630002e-01f, -1.592422914e-02f },
+    { -5.856555634e-02f, +7.964586105e-01f, +2.807713007e-01f, -1.594883407e-02f },
+    { -5.855419074e-02f, +7.961667755e-01f, +2.810797098e-01f, -1.597345798e-02f },
+    { -5.854277008e-02f, +7.958747933e-01f, +2.813882276e-01f, -1.599810084e-02f },
+    { -5.853129446e-02f, +7.955826642e-01f, +2.816968539e-01f, -1.602276266e-02f },
+    { -5.851976395e-02f, +7.952903884e-01f, +2.820055885e-01f, -1.604744341e-02f },
+    { -5.850817864e-02f, +7.949979658e-01f, +2.823144314e-01f, -1.607214309e-02f },
+    { -5.849653859e-02f, +7.947053969e-01f, +2.826233824e-01f, -1.609686168e-02f },
+    { -5.848484391e-02f, +7.944126816e-01f, +2.829324415e-01f, -1.612159918e-02f },
+    { -5.847309465e-02f, +7.941198202e-01f, +2.832416085e-01f, -1.614635558e-02f },
+    { -5.846129092e-02f, +7.938268129e-01f, +2.835508833e-01f, -1.617113085e-02f },
+    { -5.844943277e-02f, +7.935336599e-01f, +2.838602658e-01f, -1.619592500e-02f },
+    { -5.843752031e-02f, +7.932403612e-01f, +2.841697559e-01f, -1.622073801e-02f },
+    { -5.842555359e-02f, +7.929469172e-01f, +2.844793535e-01f, -1.624556986e-02f },
+    { -5.841353272e-02f, +7.926533279e-01f, +2.847890584e-01f, -1.627042056e-02f },
+    { -5.840145776e-02f, +7.923595935e-01f, +2.850988707e-01f, -1.629529008e-02f },
+    { -5.838932879e-02f, +7.920657142e-01f, +2.854087900e-01f, -1.632017842e-02f },
+    { -5.837714590e-02f, +7.917716902e-01f, +2.857188164e-01f, -1.634508556e-02f },
+    { -5.836490917e-02f, +7.914775217e-01f, +2.860289498e-01f, -1.637001149e-02f },
+    { -5.835261867e-02f, +7.911832088e-01f, +2.863391899e-01f, -1.639495620e-02f },
+    { -5.834027449e-02f, +7.908887516e-01f, +2.866495368e-01f, -1.641991969e-02f },
+    { -5.832787671e-02f, +7.905941505e-01f, +2.869599902e-01f, -1.644490193e-02f },
+    { -5.831542540e-02f, +7.902994055e-01f, +2.872705501e-01f, -1.646990291e-02f },
+    { -5.830292065e-02f, +7.900045168e-01f, +2.875812164e-01f, -1.649492264e-02f },
+    { -5.829036253e-02f, +7.897094846e-01f, +2.878919890e-01f, -1.651996108e-02f },
+    { -5.827775113e-02f, +7.894143091e-01f, +2.882028676e-01f, -1.654501824e-02f },
+    { -5.826508653e-02f, +7.891189904e-01f, +2.885138523e-01f, -1.657009409e-02f },
+    { -5.825236880e-02f, +7.888235287e-01f, +2.888249429e-01f, -1.659518863e-02f },
+    { -5.823959802e-02f, +7.885279242e-01f, +2.891361394e-01f, -1.662030185e-02f },
+    { -5.822677428e-02f, +7.882321771e-01f, +2.894474415e-01f, -1.664543373e-02f },
+    { -5.821389766e-02f, +7.879362875e-01f, +2.897588491e-01f, -1.667058426e-02f },
+    { -5.820096823e-02f, +7.876402556e-01f, +2.900703623e-01f, -1.669575344e-02f },
+    { -5.818798608e-02f, +7.873440816e-01f, +2.903819808e-01f, -1.672094124e-02f },
+    { -5.817495128e-02f, +7.870477656e-01f, +2.906937045e-01f, -1.674614765e-02f },
+    { -5.816186392e-02f, +7.867513079e-01f, +2.910055333e-01f, -1.677137267e-02f },
+    { -5.814872407e-02f, +7.864547086e-01f, +2.913174672e-01f, -1.679661628e-02f },
+    { -5.813553181e-02f, +7.861579679e-01f, +2.916295059e-01f, -1.682187847e-02f },
+    { -5.812228722e-02f, +7.858610859e-01f, +2.919416495e-01f, -1.684715923e-02f },
+    { -5.810899039e-02f, +7.855640629e-01f, +2.922538976e-01f, -1.687245854e-02f },
+    { -5.809564139e-02f, +7.852668990e-01f, +2.925662504e-01f, -1.689777639e-02f },
+    { -5.808224030e-02f, +7.849695943e-01f, +2.928787076e-01f, -1.692311278e-02f },
+    { -5.806878721e-02f, +7.846721491e-01f, +2.931912691e-01f, -1.694846768e-02f },
+    { -5.805528218e-02f, +7.843745636e-01f, +2.935039348e-01f, -1.697384108e-02f },
+    { -5.804172530e-02f, +7.840768378e-01f, +2.938167045e-01f, -1.699923298e-02f },
+    { -5.802811666e-02f, +7.837789720e-01f, +2.941295783e-01f, -1.702464336e-02f },
+    { -5.801445632e-02f, +7.834809664e-01f, +2.944425560e-01f, -1.705007220e-02f },
+    { -5.800074437e-02f, +7.831828211e-01f, +2.947556373e-01f, -1.707551950e-02f },
+    { -5.798698089e-02f, +7.828845363e-01f, +2.950688223e-01f, -1.710098524e-02f },
+    { -5.797316596e-02f, +7.825861121e-01f, +2.953821109e-01f, -1.712646941e-02f },
+    { -5.795929965e-02f, +7.822875489e-01f, +2.956955028e-01f, -1.715197200e-02f },
+    { -5.794538205e-02f, +7.819888466e-01f, +2.960089980e-01f, -1.717749299e-02f },
+    { -5.793141324e-02f, +7.816900056e-01f, +2.963225964e-01f, -1.720303237e-02f },
+    { -5.791739329e-02f, +7.813910260e-01f, +2.966362979e-01f, -1.722859012e-02f },
+    { -5.790332228e-02f, +7.810919079e-01f, +2.969501022e-01f, -1.725416625e-02f },
+    { -5.788920029e-02f, +7.807926515e-01f, +2.972640094e-01f, -1.727976072e-02f },
+    { -5.787502741e-02f, +7.804932570e-01f, +2.975780194e-01f, -1.730537353e-02f },
+    { -5.786080371e-02f, +7.801937247e-01f, +2.978921319e-01f, -1.733100467e-02f },
+    { -5.784652927e-02f, +7.798940546e-01f, +2.982063468e-01f, -1.735665412e-02f },
+    { -5.783220417e-02f, +7.795942469e-01f, +2.985206642e-01f, -1.738232187e-02f },
+    { -5.781782849e-02f, +7.792943018e-01f, +2.988350837e-01f, -1.740800791e-02f },
+    { -5.780340230e-02f, +7.789942195e-01f, +2.991496054e-01f, -1.743371222e-02f },
+    { -5.778892569e-02f, +7.786940002e-01f, +2.994642291e-01f, -1.745943479e-02f },
+    { -5.777439874e-02f, +7.783936440e-01f, +2.997789547e-01f, -1.748517560e-02f },
+    { -5.775982152e-02f, +7.780931511e-01f, +3.000937821e-01f, -1.751093465e-02f },
+    { -5.774519411e-02f, +7.777925218e-01f, +3.004087111e-01f, -1.753671192e-02f },
+    { -5.773051660e-02f, +7.774917560e-01f, +3.007237417e-01f, -1.756250739e-02f },
+    { -5.771578906e-02f, +7.771908542e-01f, +3.010388736e-01f, -1.758832105e-02f },
+    { -5.770101157e-02f, +7.768898163e-01f, +3.013541069e-01f, -1.761415290e-02f },
+    { -5.768618420e-02f, +7.765886426e-01f, +3.016694414e-01f, -1.764000291e-02f },
+    { -5.767130705e-02f, +7.762873333e-01f, +3.019848769e-01f, -1.766587107e-02f },
+    { -5.765638018e-02f, +7.759858886e-01f, +3.023004134e-01f, -1.769175736e-02f },
+    { -5.764140368e-02f, +7.756843086e-01f, +3.026160507e-01f, -1.771766179e-02f },
+    { -5.762637762e-02f, +7.753825935e-01f, +3.029317887e-01f, -1.774358432e-02f },
+    { -5.761130208e-02f, +7.750807434e-01f, +3.032476274e-01f, -1.776952495e-02f },
+    { -5.759617715e-02f, +7.747787586e-01f, +3.035635665e-01f, -1.779548366e-02f },
+    { -5.758100289e-02f, +7.744766393e-01f, +3.038796059e-01f, -1.782146044e-02f },
+    { -5.756577939e-02f, +7.741743855e-01f, +3.041957456e-01f, -1.784745527e-02f },
+    { -5.755050673e-02f, +7.738719975e-01f, +3.045119855e-01f, -1.787346814e-02f },
+    { -5.753518499e-02f, +7.735694755e-01f, +3.048283253e-01f, -1.789949905e-02f },
+    { -5.751981424e-02f, +7.732668197e-01f, +3.051447650e-01f, -1.792554796e-02f },
+    { -5.750439457e-02f, +7.729640301e-01f, +3.054613045e-01f, -1.795161487e-02f },
+    { -5.748892604e-02f, +7.726611070e-01f, +3.057779436e-01f, -1.797769977e-02f },
+    { -5.747340875e-02f, +7.723580506e-01f, +3.060946822e-01f, -1.800380263e-02f },
+    { -5.745784276e-02f, +7.720548611e-01f, +3.064115203e-01f, -1.802992345e-02f },
+    { -5.744222816e-02f, +7.717515386e-01f, +3.067284576e-01f, -1.805606221e-02f },
+    { -5.742656503e-02f, +7.714480832e-01f, +3.070454941e-01f, -1.808221890e-02f },
+    { -5.741085343e-02f, +7.711444953e-01f, +3.073626297e-01f, -1.810839350e-02f },
+    { -5.739509346e-02f, +7.708407749e-01f, +3.076798642e-01f, -1.813458600e-02f },
+    { -5.737928519e-02f, +7.705369222e-01f, +3.079971975e-01f, -1.816079639e-02f },
+    { -5.736342870e-02f, +7.702329374e-01f, +3.083146294e-01f, -1.818702464e-02f },
+    { -5.734752407e-02f, +7.699288208e-01f, +3.086321600e-01f, -1.821327074e-02f },
+    { -5.733157137e-02f, +7.696245724e-01f, +3.089497890e-01f, -1.823953469e-02f },
+    { -5.731557068e-02f, +7.693201924e-01f, +3.092675163e-01f, -1.826581646e-02f },
+    { -5.729952209e-02f, +7.690156811e-01f, +3.095853418e-01f, -1.829211604e-02f },
+    { -5.728342566e-02f, +7.687110385e-01f, +3.099032654e-01f, -1.831843341e-02f },
+    { -5.726728148e-02f, +7.684062649e-01f, +3.102212870e-01f, -1.834476857e-02f },
+    { -5.725108962e-02f, +7.681013605e-01f, +3.105394064e-01f, -1.837112149e-02f },
+    { -5.723485017e-02f, +7.677963254e-01f, +3.108576236e-01f, -1.839749217e-02f },
+    { -5.721856321e-02f, +7.674911598e-01f, +3.111759383e-01f, -1.842388058e-02f },
+    { -5.720222880e-02f, +7.671858639e-01f, +3.114943506e-01f, -1.845028671e-02f },
+    { -5.718584702e-02f, +7.668804379e-01f, +3.118128601e-01f, -1.847671054e-02f },
+    { -5.716941797e-02f, +7.665748819e-01f, +3.121314670e-01f, -1.850315207e-02f },
+    { -5.715294170e-02f, +7.662691962e-01f, +3.124501709e-01f, -1.852961127e-02f },
+    { -5.713641831e-02f, +7.659633808e-01f, +3.127689718e-01f, -1.855608813e-02f },
+    { -5.711984787e-02f, +7.656574360e-01f, +3.130878696e-01f, -1.858258264e-02f },
+    { -5.710323045e-02f, +7.653513620e-01f, +3.134068642e-01f, -1.860909478e-02f },
+    { -5.708656614e-02f, +7.650451589e-01f, +3.137259554e-01f, -1.863562453e-02f },
+    { -5.706985501e-02f, +7.647388269e-01f, +3.140451431e-01f, -1.866217188e-02f },
+    { -5.705309714e-02f, +7.644323662e-01f, +3.143644272e-01f, -1.868873682e-02f },
+    { -5.703629260e-02f, +7.641257769e-01f, +3.146838075e-01f, -1.871531932e-02f },
+    { -5.701944149e-02f, +7.638190593e-01f, +3.150032840e-01f, -1.874191938e-02f },
+    { -5.700254386e-02f, +7.635122136e-01f, +3.153228565e-01f, -1.876853697e-02f },
+    { -5.698559981e-02f, +7.632052398e-01f, +3.156425249e-01f, -1.879517209e-02f },
+    { -5.696860941e-02f, +7.628981382e-01f, +3.159622890e-01f, -1.882182471e-02f },
+    { -5.695157273e-02f, +7.625909090e-01f, +3.162821488e-01f, -1.884849482e-02f },
+    { -5.693448985e-02f, +7.622835524e-01f, +3.166021042e-01f, -1.887518241e-02f },
+    { -5.691736086e-02f, +7.619760684e-01f, +3.169221549e-01f, -1.890188745e-02f },
+    { -5.690018582e-02f, +7.616684574e-01f, +3.172423009e-01f, -1.892860994e-02f },
+    { -5.688296482e-02f, +7.613607194e-01f, +3.175625421e-01f, -1.895534986e-02f },
+    { -5.686569793e-02f, +7.610528546e-01f, +3.178828783e-01f, -1.898210719e-02f },
+    { -5.684838523e-02f, +7.607448634e-01f, +3.182033094e-01f, -1.900888191e-02f },
+    { -5.683102681e-02f, +7.604367457e-01f, +3.185238353e-01f, -1.903567401e-02f },
+    { -5.681362272e-02f, +7.601285018e-01f, +3.188444558e-01f, -1.906248348e-02f },
+    { -5.679617306e-02f, +7.598201319e-01f, +3.191651709e-01f, -1.908931029e-02f },
+    { -5.677867790e-02f, +7.595116361e-01f, +3.194859804e-01f, -1.911615443e-02f },
+    { -5.676113731e-02f, +7.592030147e-01f, +3.198068842e-01f, -1.914301589e-02f },
+    { -5.674355138e-02f, +7.588942678e-01f, +3.201278822e-01f, -1.916989465e-02f },
+    { -5.672592019e-02f, +7.585853955e-01f, +3.204489742e-01f, -1.919679069e-02f },
+    { -5.670824380e-02f, +7.582763982e-01f, +3.207701601e-01f, -1.922370400e-02f },
+    { -5.669052229e-02f, +7.579672759e-01f, +3.210914398e-01f, -1.925063455e-02f },
+    { -5.667275575e-02f, +7.576580288e-01f, +3.214128132e-01f, -1.927758234e-02f },
+    { -5.665494425e-02f, +7.573486571e-01f, +3.217342801e-01f, -1.930454735e-02f },
+    { -5.663708786e-02f, +7.570391610e-01f, +3.220558404e-01f, -1.933152956e-02f },
+    { -5.661918667e-02f, +7.567295407e-01f, +3.223774940e-01f, -1.935852895e-02f },
+    { -5.660124075e-02f, +7.564197963e-01f, +3.226992408e-01f, -1.938554550e-02f },
+    { -5.658325018e-02f, +7.561099280e-01f, +3.230210806e-01f, -1.941257921e-02f },
+    { -5.656521503e-02f, +7.557999361e-01f, +3.233430133e-01f, -1.943963005e-02f },
+    { -5.654713538e-02f, +7.554898206e-01f, +3.236650389e-01f, -1.946669801e-02f },
+    { -5.652901131e-02f, +7.551795818e-01f, +3.239871571e-01f, -1.949378307e-02f },
+    { -5.651084290e-02f, +7.548692198e-01f, +3.243093678e-01f, -1.952088522e-02f },
+    { -5.649263022e-02f, +7.545587349e-01f, +3.246316709e-01f, -1.954800443e-02f },
+    { -5.647437335e-02f, +7.542481271e-01f, +3.249540663e-01f, -1.957514069e-02f },
+    { -5.645607236e-02f, +7.539373968e-01f, +3.252765539e-01f, -1.960229398e-02f },
+    { -5.643772734e-02f, +7.536265440e-01f, +3.255991335e-01f, -1.962946429e-02f },
+    { -5.641933835e-02f, +7.533155690e-01f, +3.259218051e-01f, -1.965665160e-02f },
+    { -5.640090548e-02f, +7.530044718e-01f, +3.262445684e-01f, -1.968385589e-02f },
+    { -5.638242880e-02f, +7.526932528e-01f, +3.265674233e-01f, -1.971107714e-02f },
+    { -5.636390839e-02f, +7.523819121e-01f, +3.268903698e-01f, -1.973831534e-02f },
+    { -5.634534432e-02f, +7.520704498e-01f, +3.272134077e-01f, -1.976557047e-02f },
+    { -5.632673668e-02f, +7.517588662e-01f, +3.275365369e-01f, -1.979284251e-02f },
+    { -5.630808553e-02f, +7.514471614e-01f, +3.278597572e-01f, -1.982013145e-02f },
+    { -5.628939096e-02f, +7.511353356e-01f, +3.281830685e-01f, -1.984743727e-02f },
+    { -5.627065304e-02f, +7.508233890e-01f, +3.285064707e-01f, -1.987475995e-02f },
+    { -5.625187185e-02f, +7.505113218e-01f, +3.288299637e-01f, -1.990209947e-02f },
+    { -5.623304746e-02f, +7.501991341e-01f, +3.291535473e-01f, -1.992945582e-02f },
+    { -5.621417995e-02f, +7.498868261e-01f, +3.294772215e-01f, -1.995682897e-02f },
+    { -5.619526939e-02f, +7.495743981e-01f, +3.298009860e-01f, -1.998421892e-02f },
+    { -5.617631587e-02f, +7.492618501e-01f, +3.301248407e-01f, -2.001162564e-02f },
+    { -5.615731946e-02f, +7.489491824e-01f, +3.304487856e-01f, -2.003904911e-02f },
+    { -5.613828023e-02f, +7.486363951e-01f, +3.307728205e-01f, -2.006648932e-02f },
+    { -5.611919827e-02f, +7.483234885e-01f, +3.310969453e-01f, -2.009394625e-02f },
+    { -5.610007364e-02f, +7.480104627e-01f, +3.314211598e-01f, -2.012141988e-02f },
+    { -5.608090642e-02f, +7.476973179e-01f, +3.317454639e-01f, -2.014891020e-02f },
+    { -5.606169669e-02f, +7.473840543e-01f, +3.320698576e-01f, -2.017641718e-02f },
+    { -5.604244453e-02f, +7.470706720e-01f, +3.323943405e-01f, -2.020394081e-02f },
+    { -5.602315001e-02f, +7.467571712e-01f, +3.327189127e-01f, -2.023148107e-02f },
+    { -5.600381321e-02f, +7.464435522e-01f, +3.330435740e-01f, -2.025903794e-02f },
+    { -5.598443420e-02f, +7.461298151e-01f, +3.333683243e-01f, -2.028661141e-02f },
+    { -5.596501306e-02f, +7.458159600e-01f, +3.336931634e-01f, -2.031420145e-02f },
+    { -5.594554986e-02f, +7.455019872e-01f, +3.340180912e-01f, -2.034180804e-02f },
+    { -5.592604469e-02f, +7.451878969e-01f, +3.343431076e-01f, -2.036943118e-02f },
+    { -5.590649761e-02f, +7.448736891e-01f, +3.346682125e-01f, -2.039707084e-02f },
+    { -5.588690871e-02f, +7.445593642e-01f, +3.349934057e-01f, -2.042472700e-02f },
+    { -5.586727805e-02f, +7.442449222e-01f, +3.353186871e-01f, -2.045239964e-02f },
+    { -5.584760571e-02f, +7.439303635e-01f, +3.356440565e-01f, -2.048008875e-02f },
+    { -5.582789178e-02f, +7.436156880e-01f, +3.359695139e-01f, -2.050779431e-02f },
+    { -5.580813632e-02f, +7.433008961e-01f, +3.362950591e-01f, -2.053551629e-02f },
+    { -5.578833941e-02f, +7.429859879e-01f, +3.366206920e-01f, -2.056325469e-02f },
+    { -5.576850113e-02f, +7.426709636e-01f, +3.369464124e-01f, -2.059100947e-02f },
+    { -5.574862155e-02f, +7.423558234e-01f, +3.372722202e-01f, -2.061878063e-02f },
+    { -5.572870075e-02f, +7.420405674e-01f, +3.375981153e-01f, -2.064656814e-02f },
+    { -5.570873880e-02f, +7.417251959e-01f, +3.379240976e-01f, -2.067437199e-02f },
+    { -5.568873578e-02f, +7.414097090e-01f, +3.382501669e-01f, -2.070219216e-02f },
+    { -5.566869176e-02f, +7.410941069e-01f, +3.385763231e-01f, -2.073002862e-02f },
+    { -5.564860682e-02f, +7.407783897e-01f, +3.389025660e-01f, -2.075788136e-02f },
+    { -5.562848104e-02f, +7.404625577e-01f, +3.392288956e-01f, -2.078575035e-02f },
+    { -5.560831448e-02f, +7.401466111e-01f, +3.395553117e-01f, -2.081363559e-02f },
+    { -5.558810723e-02f, +7.398305500e-01f, +3.398818142e-01f, -2.084153705e-02f },
+    { -5.556785936e-02f, +7.395143746e-01f, +3.402084029e-01f, -2.086945472e-02f },
+    { -5.554757095e-02f, +7.391980851e-01f, +3.405350777e-01f, -2.089738856e-02f },
+    { -5.552724207e-02f, +7.388816817e-01f, +3.408618385e-01f, -2.092533857e-02f },
+    { -5.550687279e-02f, +7.385651645e-01f, +3.411886851e-01f, -2.095330472e-02f },
+    { -5.548646320e-02f, +7.382485338e-01f, +3.415156174e-01f, -2.098128700e-02f },
+    { -5.546601336e-02f, +7.379317896e-01f, +3.418426354e-01f, -2.100928539e-02f },
+    { -5.544552335e-02f, +7.376149323e-01f, +3.421697387e-01f, -2.103729986e-02f },
+    { -5.542499324e-02f, +7.372979620e-01f, +3.424969274e-01f, -2.106533040e-02f },
+    { -5.540442312e-02f, +7.369808788e-01f, +3.428242013e-01f, -2.109337699e-02f },
+    { -5.538381306e-02f, +7.366636830e-01f, +3.431515602e-01f, -2.112143960e-02f },
+    { -5.536316312e-02f, +7.363463747e-01f, +3.434790041e-01f, -2.114951822e-02f },
+    { -5.534247339e-02f, +7.360289541e-01f, +3.438065327e-01f, -2.117761283e-02f },
+    { -5.532174395e-02f, +7.357114214e-01f, +3.441341461e-01f, -2.120572342e-02f },
+    { -5.530097485e-02f, +7.353937768e-01f, +3.444618439e-01f, -2.123384995e-02f },
+    { -5.528016619e-02f, +7.350760204e-01f, +3.447896261e-01f, -2.126199241e-02f },
+    { -5.525931803e-02f, +7.347581525e-01f, +3.451174926e-01f, -2.129015078e-02f },
+    { -5.523843046e-02f, +7.344401732e-01f, +3.454454432e-01f, -2.131832504e-02f },
+    { -5.521750353e-02f, +7.341220827e-01f, +3.457734778e-01f, -2.134651517e-02f },
+    { -5.519653734e-02f, +7.338038812e-01f, +3.461015962e-01f, -2.137472115e-02f },
+    { -5.517553195e-02f, +7.334855689e-01f, +3.464297984e-01f, -2.140294296e-02f },
+    { -5.515448744e-02f, +7.331671459e-01f, +3.467580842e-01f, -2.143118058e-02f },
+    { -5.513340388e-02f, +7.328486125e-01f, +3.470864534e-01f, -2.145943399e-02f },
+    { -5.511228134e-02f, +7.325299688e-01f, +3.474149060e-01f, -2.148770317e-02f },
+    { -5.509111991e-02f, +7.322112150e-01f, +3.477434418e-01f, -2.151598810e-02f },
+    { -5.506991966e-02f, +7.318923513e-01f, +3.480720606e-01f, -2.154428875e-02f },
+    { -5.504868065e-02f, +7.315733778e-01f, +3.484007623e-01f, -2.157260512e-02f },
+    { -5.502740298e-02f, +7.312542948e-01f, +3.487295469e-01f, -2.160093718e-02f },
+    { -5.500608670e-02f, +7.309351024e-01f, +3.490584141e-01f, -2.162928490e-02f },
+    { -5.498473189e-02f, +7.306158008e-01f, +3.493873638e-01f, -2.165764827e-02f },
+    { -5.496333863e-02f, +7.302963903e-01f, +3.497163959e-01f, -2.168602727e-02f },
+    { -5.494190699e-02f, +7.299768709e-01f, +3.500455102e-01f, -2.171442188e-02f },
+    { -5.492043705e-02f, +7.296572429e-01f, +3.503747067e-01f, -2.174283207e-02f },
+    { -5.489892888e-02f, +7.293375064e-01f, +3.507039852e-01f, -2.177125783e-02f },
+    { -5.487738256e-02f, +7.290176616e-01f, +3.510333455e-01f, -2.179969913e-02f },
+    { -5.485579815e-02f, +7.286977088e-01f, +3.513627875e-01f, -2.182815596e-02f },
+    { -5.483417574e-02f, +7.283776481e-01f, +3.516923111e-01f, -2.185662829e-02f },
+    { -5.481251539e-02f, +7.280574796e-01f, +3.520219162e-01f, -2.188511610e-02f },
+    { -5.479081718e-02f, +7.277372036e-01f, +3.523516026e-01f, -2.191361938e-02f },
+    { -5.476908119e-02f, +7.274168203e-01f, +3.526813701e-01f, -2.194213809e-02f },
+    { -5.474730748e-02f, +7.270963298e-01f, +3.530112187e-01f, -2.197067223e-02f },
+    { -5.472549614e-02f, +7.267757323e-01f, +3.533411482e-01f, -2.199922176e-02f },
+    { -5.470364724e-02f, +7.264550280e-01f, +3.536711584e-01f, -2.202778667e-02f },
+    { -5.468176084e-02f, +7.261342170e-01f, +3.540012493e-01f, -2.205636694e-02f },
+    { -5.465983703e-02f, +7.258132997e-01f, +3.543314207e-01f, -2.208496254e-02f },
+    { -5.463787587e-02f, +7.254922761e-01f, +3.546616725e-01f, -2.211357346e-02f },
+    { -5.461587745e-02f, +7.251711464e-01f, +3.549920045e-01f, -2.214219967e-02f },
+    { -5.459384183e-02f, +7.248499108e-01f, +3.553224165e-01f, -2.217084115e-02f },
+    { -5.457176909e-02f, +7.245285695e-01f, +3.556529085e-01f, -2.219949788e-02f },
+    { -5.454965931e-02f, +7.242071227e-01f, +3.559834804e-01f, -2.222816984e-02f },
+    { -5.452751254e-02f, +7.238855706e-01f, +3.563141319e-01f, -2.225685701e-02f },
+    { -5.450532888e-02f, +7.235639133e-01f, +3.566448630e-01f, -2.228555936e-02f },
+    { -5.448310839e-02f, +7.232421510e-01f, +3.569756734e-01f, -2.231427688e-02f },
+    { -5.446085115e-02f, +7.229202840e-01f, +3.573065632e-01f, -2.234300954e-02f },
+    { -5.443855722e-02f, +7.225983123e-01f, +3.576375321e-01f, -2.237175733e-02f },
+    { -5.441622669e-02f, +7.222762363e-01f, +3.579685799e-01f, -2.240052021e-02f },
+    { -5.439385962e-02f, +7.219540560e-01f, +3.582997067e-01f, -2.242929816e-02f },
+    { -5.437145610e-02f, +7.216317716e-01f, +3.586309121e-01f, -2.245809118e-02f },
+    { -5.434901618e-02f, +7.213093834e-01f, +3.589621962e-01f, -2.248689923e-02f },
+    { -5.432653996e-02f, +7.209868915e-01f, +3.592935587e-01f, -2.251572229e-02f },
+    { -5.430402749e-02f, +7.206642961e-01f, +3.596249995e-01f, -2.254456034e-02f },
+    { -5.428147885e-02f, +7.203415974e-01f, +3.599565185e-01f, -2.257341336e-02f },
+    { -5.425889412e-02f, +7.200187955e-01f, +3.602881155e-01f, -2.260228132e-02f },
+    { -5.423627337e-02f, +7.196958907e-01f, +3.606197904e-01f, -2.263116421e-02f },
+    { -5.421361667e-02f, +7.193728831e-01f, +3.609515431e-01f, -2.266006200e-02f },
+    { -5.419092410e-02f, +7.190497730e-01f, +3.612833734e-01f, -2.268897467e-02f },
+    { -5.416819572e-02f, +7.187265605e-01f, +3.616152812e-01f, -2.271790220e-02f },
+    { -5.414543161e-02f, +7.184032457e-01f, +3.619472663e-01f, -2.274684456e-02f },
+    { -5.412263185e-02f, +7.180798289e-01f, +3.622793287e-01f, -2.277580174e-02f },
+    { -5.409979650e-02f, +7.177563103e-01f, +3.626114681e-01f, -2.280477371e-02f },
+    { -5.407692564e-02f, +7.174326900e-01f, +3.629436844e-01f, -2.283376044e-02f },
+    { -5.405401935e-02f, +7.171089682e-01f, +3.632759775e-01f, -2.286276192e-02f },
+    { -5.403107768e-02f, +7.167851452e-01f, +3.636083473e-01f, -2.289177812e-02f },
+    { -5.400810073e-02f, +7.164612210e-01f, +3.639407936e-01f, -2.292080903e-02f },
+    { -5.398508856e-02f, +7.161371959e-01f, +3.642733163e-01f, -2.294985461e-02f },
+    { -5.396204124e-02f, +7.158130701e-01f, +3.646059152e-01f, -2.297891485e-02f },
+    { -5.393895884e-02f, +7.154888437e-01f, +3.649385903e-01f, -2.300798972e-02f },
+    { -5.391584144e-02f, +7.151645170e-01f, +3.652713412e-01f, -2.303707919e-02f },
+    { -5.389268911e-02f, +7.148400900e-01f, +3.656041680e-01f, -2.306618326e-02f },
+    { -5.386950193e-02f, +7.145155631e-01f, +3.659370705e-01f, -2.309530189e-02f },
+    { -5.384627996e-02f, +7.141909364e-01f, +3.662700485e-01f, -2.312443506e-02f },
+    { -5.382302328e-02f, +7.138662100e-01f, +3.666031020e-01f, -2.315358275e-02f },
+    { -5.379973196e-02f, +7.135413841e-01f, +3.669362306e-01f, -2.318274493e-02f },
+    { -5.377640607e-02f, +7.132164590e-01f, +3.672694344e-01f, -2.321192159e-02f },
+    { -5.375304569e-02f, +7.128914348e-01f, +3.676027132e-01f, -2.324111270e-02f },
+    { -5.372965088e-02f, +7.125663118e-01f, +3.679360669e-01f, -2.327031823e-02f },
+    { -5.370622173e-02f, +7.122410900e-01f, +3.682694952e-01f, -2.329953816e-02f },
+    { -5.368275829e-02f, +7.119157697e-01f, +3.686029981e-01f, -2.332877247e-02f },
+    { -5.365926065e-02f, +7.115903510e-01f, +3.689365754e-01f, -2.335802114e-02f },
+    { -5.363572888e-02f, +7.112648342e-01f, +3.692702270e-01f, -2.338728414e-02f },
+    { -5.361216304e-02f, +7.109392194e-01f, +3.696039527e-01f, -2.341656145e-02f },
+    { -5.358856321e-02f, +7.106135068e-01f, +3.699377525e-01f, -2.344585305e-02f },
+    { -5.356492947e-02f, +7.102876966e-01f, +3.702716261e-01f, -2.347515891e-02f },
+    { -5.354126188e-02f, +7.099617891e-01f, +3.706055734e-01f, -2.350447901e-02f },
+    { -5.351756051e-02f, +7.096357842e-01f, +3.709395943e-01f, -2.353381332e-02f },
+    { -5.349382545e-02f, +7.093096823e-01f, +3.712736886e-01f, -2.356316182e-02f },
+    { -5.347005675e-02f, +7.089834836e-01f, +3.716078562e-01f, -2.359252450e-02f },
+    { -5.344625449e-02f, +7.086571882e-01f, +3.719420970e-01f, -2.362190131e-02f },
+    { -5.342241875e-02f, +7.083307963e-01f, +3.722764108e-01f, -2.365129225e-02f },
+    { -5.339854959e-02f, +7.080043080e-01f, +3.726107975e-01f, -2.368069728e-02f },
+    { -5.337464709e-02f, +7.076777237e-01f, +3.729452569e-01f, -2.371011639e-02f },
+    { -5.335071132e-02f, +7.073510434e-01f, +3.732797888e-01f, -2.373954954e-02f },
+    { -5.332674235e-02f, +7.070242673e-01f, +3.736143933e-01f, -2.376899672e-02f },
+    { -5.330274025e-02f, +7.066973957e-01f, +3.739490700e-01f, -2.379845790e-02f },
+    { -5.327870509e-02f, +7.063704287e-01f, +3.742838189e-01f, -2.382793305e-02f },
+    { -5.325463695e-02f, +7.060433665e-01f, +3.746186398e-01f, -2.385742216e-02f },
+    { -5.323053590e-02f, +7.057162093e-01f, +3.749535326e-01f, -2.388692520e-02f },
+    { -5.320640200e-02f, +7.053889572e-01f, +3.752884971e-01f, -2.391644214e-02f },
+    { -5.318223533e-02f, +7.050616105e-01f, +3.756235333e-01f, -2.394597296e-02f },
+    { -5.315803596e-02f, +7.047341694e-01f, +3.759586408e-01f, -2.397551763e-02f },
+    { -5.313380397e-02f, +7.044066339e-01f, +3.762938197e-01f, -2.400507614e-02f },
+    { -5.310953941e-02f, +7.040790044e-01f, +3.766290698e-01f, -2.403464845e-02f },
+    { -5.308524237e-02f, +7.037512810e-01f, +3.769643909e-01f, -2.406423454e-02f },
+    { -5.306091292e-02f, +7.034234638e-01f, +3.772997828e-01f, -2.409383439e-02f },
+    { -5.303655112e-02f, +7.030955531e-01f, +3.776352455e-01f, -2.412344797e-02f },
+    { -5.301215705e-02f, +7.027675491e-01f, +3.779707788e-01f, -2.415307527e-02f },
+    { -5.298773078e-02f, +7.024394519e-01f, +3.783063825e-01f, -2.418271624e-02f },
+    { -5.296327238e-02f, +7.021112617e-01f, +3.786420566e-01f, -2.421237087e-02f },
+    { -5.293878193e-02f, +7.017829788e-01f, +3.789778008e-01f, -2.424203914e-02f },
+    { -5.291425948e-02f, +7.014546032e-01f, +3.793136151e-01f, -2.427172102e-02f },
+    { -5.288970511e-02f, +7.011261352e-01f, +3.796494992e-01f, -2.430141648e-02f },
+    { -5.286511890e-02f, +7.007975749e-01f, +3.799854530e-01f, -2.433112550e-02f },
+    { -5.284050092e-02f, +7.004689226e-01f, +3.803214765e-01f, -2.436084805e-02f },
+    { -5.281585123e-02f, +7.001401785e-01f, +3.806575694e-01f, -2.439058412e-02f },
+    { -5.279116991e-02f, +6.998113426e-01f, +3.809937316e-01f, -2.442033366e-02f },
+    { -5.276645702e-02f, +6.994824153e-01f, +3.813299629e-01f, -2.445009667e-02f },
+    { -5.274171264e-02f, +6.991533966e-01f, +3.816662633e-01f, -2.447987311e-02f },
+    { -5.271693684e-02f, +6.988242869e-01f, +3.820026325e-01f, -2.450966296e-02f },
+    { -5.269212969e-02f, +6.984950861e-01f, +3.823390705e-01f, -2.453946619e-02f },
+    { -5.266729126e-02f, +6.981657947e-01f, +3.826755771e-01f, -2.456928278e-02f },
+    { -5.264242161e-02f, +6.978364126e-01f, +3.830121520e-01f, -2.459911271e-02f },
+    { -5.261752083e-02f, +6.975069402e-01f, +3.833487953e-01f, -2.462895594e-02f },
+    { -5.259258898e-02f, +6.971773775e-01f, +3.836855068e-01f, -2.465881245e-02f },
+    { -5.256762613e-02f, +6.968477249e-01f, +3.840222862e-01f, -2.468868221e-02f },
+    { -5.254263236e-02f, +6.965179824e-01f, +3.843591335e-01f, -2.471856521e-02f },
+    { -5.251760772e-02f, +6.961881503e-01f, +3.846960485e-01f, -2.474846141e-02f },
+    { -5.249255230e-02f, +6.958582288e-01f, +3.850330311e-01f, -2.477837080e-02f },
+    { -5.246746616e-02f, +6.955282179e-01f, +3.853700811e-01f, -2.480829333e-02f },
+    { -5.244234938e-02f, +6.951981180e-01f, +3.857071983e-01f, -2.483822900e-02f },
+    { -5.241720201e-02f, +6.948679292e-01f, +3.860443828e-01f, -2.486817776e-02f },
+    { -5.239202415e-02f, +6.945376516e-01f, +3.863816342e-01f, -2.489813960e-02f },
+    { -5.236681584e-02f, +6.942072855e-01f, +3.867189524e-01f, -2.492811449e-02f },
+    { -5.234157717e-02f, +6.938768311e-01f, +3.870563373e-01f, -2.495810241e-02f },
+    { -5.231630820e-02f, +6.935462886e-01f, +3.873937888e-01f, -2.498810332e-02f },
+    { -5.229100900e-02f, +6.932156580e-01f, +3.877313067e-01f, -2.501811720e-02f },
+    { -5.226567965e-02f, +6.928849397e-01f, +3.880688909e-01f, -2.504814403e-02f },
+    { -5.224032022e-02f, +6.925541338e-01f, +3.884065411e-01f, -2.507818379e-02f },
+    { -5.221493076e-02f, +6.922232404e-01f, +3.887442574e-01f, -2.510823643e-02f },
+    { -5.218951136e-02f, +6.918922599e-01f, +3.890820394e-01f, -2.513830194e-02f },
+    { -5.216406208e-02f, +6.915611922e-01f, +3.894198871e-01f, -2.516838029e-02f },
+    { -5.213858300e-02f, +6.912300378e-01f, +3.897578004e-01f, -2.519847146e-02f },
+    { -5.211307417e-02f, +6.908987966e-01f, +3.900957790e-01f, -2.522857541e-02f },
+    { -5.208753568e-02f, +6.905674690e-01f, +3.904338229e-01f, -2.525869212e-02f },
+    { -5.206196759e-02f, +6.902360551e-01f, +3.907719318e-01f, -2.528882157e-02f },
+    { -5.203636997e-02f, +6.899045550e-01f, +3.911101057e-01f, -2.531896373e-02f },
+    { -5.201074290e-02f, +6.895729691e-01f, +3.914483444e-01f, -2.534911857e-02f },
+    { -5.198508643e-02f, +6.892412974e-01f, +3.917866477e-01f, -2.537928607e-02f },
+    { -5.195940064e-02f, +6.889095401e-01f, +3.921250156e-01f, -2.540946619e-02f },
+    { -5.193368560e-02f, +6.885776975e-01f, +3.924634478e-01f, -2.543965891e-02f },
+    { -5.190794138e-02f, +6.882457697e-01f, +3.928019442e-01f, -2.546986421e-02f },
+    { -5.188216805e-02f, +6.879137568e-01f, +3.931405046e-01f, -2.550008206e-02f },
+    { -5.185636568e-02f, +6.875816592e-01f, +3.934791290e-01f, -2.553031243e-02f },
+    { -5.183053433e-02f, +6.872494769e-01f, +3.938178171e-01f, -2.556055529e-02f },
+    { -5.180467408e-02f, +6.869172102e-01f, +3.941565688e-01f, -2.559081062e-02f },
+    { -5.177878499e-02f, +6.865848593e-01f, +3.944953840e-01f, -2.562107838e-02f },
+    { -5.175286714e-02f, +6.862524243e-01f, +3.948342626e-01f, -2.565135856e-02f },
+    { -5.172692059e-02f, +6.859199054e-01f, +3.951732042e-01f, -2.568165113e-02f },
+    { -5.170094541e-02f, +6.855873028e-01f, +3.955122090e-01f, -2.571195605e-02f },
+    { -5.167494167e-02f, +6.852546167e-01f, +3.958512766e-01f, -2.574227331e-02f },
+    { -5.164890945e-02f, +6.849218472e-01f, +3.961904069e-01f, -2.577260287e-02f },
+    { -5.162284880e-02f, +6.845889947e-01f, +3.965295998e-01f, -2.580294470e-02f },
+    { -5.159675980e-02f, +6.842560591e-01f, +3.968688551e-01f, -2.583329878e-02f },
+    { -5.157064252e-02f, +6.839230409e-01f, +3.972081727e-01f, -2.586366508e-02f },
+    { -5.154449702e-02f, +6.835899400e-01f, +3.975475525e-01f, -2.589404358e-02f },
+    { -5.151832338e-02f, +6.832567567e-01f, +3.978869942e-01f, -2.592443425e-02f },
+    { -5.149212166e-02f, +6.829234913e-01f, +3.982264977e-01f, -2.595483705e-02f },
+    { -5.146589193e-02f, +6.825901438e-01f, +3.985660630e-01f, -2.598525196e-02f },
+    { -5.143963427e-02f, +6.822567144e-01f, +3.989056898e-01f, -2.601567895e-02f },
+    { -5.141334873e-02f, +6.819232035e-01f, +3.992453779e-01f, -2.604611800e-02f },
+    { -5.138703539e-02f, +6.815896110e-01f, +3.995851274e-01f, -2.607656908e-02f },
+    { -5.136069431e-02f, +6.812559373e-01f, +3.999249379e-01f, -2.610703216e-02f },
+    { -5.133432558e-02f, +6.809221825e-01f, +4.002648093e-01f, -2.613750720e-02f },
+    { -5.130792924e-02f, +6.805883468e-01f, +4.006047415e-01f, -2.616799420e-02f },
+    { -5.128150537e-02f, +6.802544304e-01f, +4.009447344e-01f, -2.619849310e-02f },
+    { -5.125505405e-02f, +6.799204334e-01f, +4.012847878e-01f, -2.622900390e-02f },
+    { -5.122857533e-02f, +6.795863561e-01f, +4.016249015e-01f, -2.625952655e-02f },
+    { -5.120206929e-02f, +6.792521986e-01f, +4.019650754e-01f, -2.629006103e-02f },
+    { -5.117553600e-02f, +6.789179612e-01f, +4.023053093e-01f, -2.632060732e-02f },
+    { -5.114897551e-02f, +6.785836440e-01f, +4.026456031e-01f, -2.635116538e-02f },
+    { -5.112238791e-02f, +6.782492471e-01f, +4.029859567e-01f, -2.638173519e-02f },
+    { -5.109577326e-02f, +6.779147709e-01f, +4.033263699e-01f, -2.641231671e-02f },
+    { -5.106913162e-02f, +6.775802154e-01f, +4.036668425e-01f, -2.644290992e-02f },
+    { -5.104246307e-02f, +6.772455809e-01f, +4.040073744e-01f, -2.647351480e-02f },
+    { -5.101576767e-02f, +6.769108675e-01f, +4.043479654e-01f, -2.650413130e-02f },
+    { -5.098904550e-02f, +6.765760755e-01f, +4.046886155e-01f, -2.653475941e-02f },
+    { -5.096229661e-02f, +6.762412050e-01f, +4.050293243e-01f, -2.656539910e-02f },
+    { -5.093552108e-02f, +6.759062562e-01f, +4.053700919e-01f, -2.659605033e-02f },
+    { -5.090871897e-02f, +6.755712292e-01f, +4.057109180e-01f, -2.662671308e-02f },
+    { -5.088189036e-02f, +6.752361244e-01f, +4.060518025e-01f, -2.665738731e-02f },
+    { -5.085503531e-02f, +6.749009418e-01f, +4.063927452e-01f, -2.668807301e-02f },
+    { -5.082815388e-02f, +6.745656817e-01f, +4.067337460e-01f, -2.671877013e-02f },
+    { -5.080124615e-02f, +6.742303442e-01f, +4.070748047e-01f, -2.674947866e-02f },
+    { -5.077431218e-02f, +6.738949295e-01f, +4.074159212e-01f, -2.678019856e-02f },
+    { -5.074735205e-02f, +6.735594378e-01f, +4.077570954e-01f, -2.681092981e-02f },
+    { -5.072036581e-02f, +6.732238693e-01f, +4.080983270e-01f, -2.684167237e-02f },
+    { -5.069335353e-02f, +6.728882242e-01f, +4.084396159e-01f, -2.687242621e-02f },
+    { -5.066631529e-02f, +6.725525027e-01f, +4.087809621e-01f, -2.690319131e-02f },
+    { -5.063925115e-02f, +6.722167049e-01f, +4.091223652e-01f, -2.693396764e-02f },
+    { -5.061216118e-02f, +6.718808311e-01f, +4.094638252e-01f, -2.696475516e-02f },
+    { -5.058504544e-02f, +6.715448814e-01f, +4.098053420e-01f, -2.699555385e-02f },
+    { -5.055790401e-02f, +6.712088560e-01f, +4.101469153e-01f, -2.702636369e-02f },
+    { -5.053073694e-02f, +6.708727551e-01f, +4.104885450e-01f, -2.705718463e-02f },
+    { -5.050354431e-02f, +6.705365788e-01f, +4.108302310e-01f, -2.708801665e-02f },
+    { -5.047632619e-02f, +6.702003275e-01f, +4.111719731e-01f, -2.711885972e-02f },
+    { -5.044908263e-02f, +6.698640012e-01f, +4.115137711e-01f, -2.714971381e-02f },
+    { -5.042181372e-02f, +6.695276002e-01f, +4.118556250e-01f, -2.718057889e-02f },
+    { -5.039451951e-02f, +6.691911246e-01f, +4.121975345e-01f, -2.721145493e-02f },
+    { -5.036720007e-02f, +6.688545746e-01f, +4.125394995e-01f, -2.724234191e-02f },
+    { -5.033985546e-02f, +6.685179504e-01f, +4.128815199e-01f, -2.727323979e-02f },
+    { -5.031248577e-02f, +6.681812522e-01f, +4.132235955e-01f, -2.730414853e-02f },
+    { -5.028509105e-02f, +6.678444802e-01f, +4.135657261e-01f, -2.733506812e-02f },
+    { -5.025767136e-02f, +6.675076346e-01f, +4.139079116e-01f, -2.736599852e-02f },
+    { -5.023022678e-02f, +6.671707155e-01f, +4.142501518e-01f, -2.739693971e-02f },
+    { -5.020275738e-02f, +6.668337231e-01f, +4.145924466e-01f, -2.742789164e-02f },
+    { -5.017526321e-02f, +6.664966577e-01f, +4.149347959e-01f, -2.745885430e-02f },
+    { -5.014774435e-02f, +6.661595193e-01f, +4.152771994e-01f, -2.748982764e-02f },
+    { -5.012020087e-02f, +6.658223083e-01f, +4.156196570e-01f, -2.752081165e-02f },
+    { -5.009263282e-02f, +6.654850247e-01f, +4.159621686e-01f, -2.755180629e-02f },
+    { -5.006504027e-02f, +6.651476689e-01f, +4.163047340e-01f, -2.758281153e-02f },
+    { -5.003742330e-02f, +6.648102408e-01f, +4.166473531e-01f, -2.761382734e-02f },
+    { -5.000978197e-02f, +6.644727409e-01f, +4.169900257e-01f, -2.764485368e-02f },
+    { -4.998211634e-02f, +6.641351691e-01f, +4.173327517e-01f, -2.767589054e-02f },
+    { -4.995442649e-02f, +6.637975258e-01f, +4.176755308e-01f, -2.770693788e-02f },
+    { -4.992671247e-02f, +6.634598110e-01f, +4.180183630e-01f, -2.773799566e-02f },
+    { -4.989897435e-02f, +6.631220251e-01f, +4.183612481e-01f, -2.776906386e-02f },
+    { -4.987121220e-02f, +6.627841681e-01f, +4.187041859e-01f, -2.780014245e-02f },
+    { -4.984342609e-02f, +6.624462403e-01f, +4.190471763e-01f, -2.783123139e-02f },
+    { -4.981561608e-02f, +6.621082419e-01f, +4.193902192e-01f, -2.786233066e-02f },
+    { -4.978778224e-02f, +6.617701729e-01f, +4.197333143e-01f, -2.789344022e-02f },
+    { -4.975992463e-02f, +6.614320337e-01f, +4.200764615e-01f, -2.792456005e-02f },
+    { -4.973204333e-02f, +6.610938245e-01f, +4.204196607e-01f, -2.795569010e-02f },
+    { -4.970413838e-02f, +6.607555453e-01f, +4.207629117e-01f, -2.798683036e-02f },
+    { -4.967620987e-02f, +6.604171963e-01f, +4.211062143e-01f, -2.801798079e-02f },
+    { -4.964825786e-02f, +6.600787779e-01f, +4.214495685e-01f, -2.804914136e-02f },
+    { -4.962028241e-02f, +6.597402901e-01f, +4.217929740e-01f, -2.808031203e-02f },
+    { -4.959228359e-02f, +6.594017331e-01f, +4.221364307e-01f, -2.811149279e-02f },
+    { -4.956426147e-02f, +6.590631072e-01f, +4.224799384e-01f, -2.814268358e-02f },
+    { -4.953621610e-02f, +6.587244125e-01f, +4.228234970e-01f, -2.817388439e-02f },
+    { -4.950814756e-02f, +6.583856492e-01f, +4.231671063e-01f, -2.820509519e-02f },
+    { -4.948005591e-02f, +6.580468175e-01f, +4.235107662e-01f, -2.823631593e-02f },
+    { -4.945194122e-02f, +6.577079175e-01f, +4.238544765e-01f, -2.826754660e-02f },
+    { -4.942380356e-02f, +6.573689495e-01f, +4.241982371e-01f, -2.829878715e-02f },
+    { -4.939564297e-02f, +6.570299137e-01f, +4.245420477e-01f, -2.833003756e-02f },
+    { -4.936745955e-02f, +6.566908102e-01f, +4.248859083e-01f, -2.836129779e-02f },
+    { -4.933925334e-02f, +6.563516392e-01f, +4.252298187e-01f, -2.839256782e-02f },
+    { -4.931102441e-02f, +6.560124009e-01f, +4.255737788e-01f, -2.842384761e-02f },
+    { -4.928277284e-02f, +6.556730955e-01f, +4.259177883e-01f, -2.845513713e-02f },
+    { -4.925449868e-02f, +6.553337232e-01f, +4.262618471e-01f, -2.848643635e-02f },
+    { -4.922620200e-02f, +6.549942842e-01f, +4.266059551e-01f, -2.851774523e-02f },
+    { -4.919788286e-02f, +6.546547786e-01f, +4.269501121e-01f, -2.854906375e-02f },
+    { -4.916954133e-02f, +6.543152066e-01f, +4.272943179e-01f, -2.858039187e-02f },
+    { -4.914117748e-02f, +6.539755685e-01f, +4.276385725e-01f, -2.861172956e-02f },
+    { -4.911279137e-02f, +6.536358644e-01f, +4.279828756e-01f, -2.864307678e-02f },
+    { -4.908438306e-02f, +6.532960945e-01f, +4.283272270e-01f, -2.867443352e-02f },
+    { -4.905595262e-02f, +6.529562589e-01f, +4.286716267e-01f, -2.870579972e-02f },
+    { -4.902750012e-02f, +6.526163580e-01f, +4.290160745e-01f, -2.873717537e-02f },
+    { -4.899902562e-02f, +6.522763918e-01f, +4.293605701e-01f, -2.876856043e-02f },
+    { -4.897052918e-02f, +6.519363605e-01f, +4.297051136e-01f, -2.879995486e-02f },
+    { -4.894201087e-02f, +6.515962644e-01f, +4.300497046e-01f, -2.883135864e-02f },
+    { -4.891347075e-02f, +6.512561036e-01f, +4.303943430e-01f, -2.886277173e-02f },
+    { -4.888490889e-02f, +6.509158783e-01f, +4.307390288e-01f, -2.889419410e-02f },
+    { -4.885632535e-02f, +6.505755887e-01f, +4.310837617e-01f, -2.892562571e-02f },
+    { -4.882772020e-02f, +6.502352350e-01f, +4.314285415e-01f, -2.895706654e-02f },
+    { -4.879909350e-02f, +6.498948173e-01f, +4.317733681e-01f, -2.898851656e-02f },
+    { -4.877044532e-02f, +6.495543359e-01f, +4.321182414e-01f, -2.901997572e-02f },
+    { -4.874177572e-02f, +6.492137910e-01f, +4.324631612e-01f, -2.905144399e-02f },
+    { -4.871308476e-02f, +6.488731826e-01f, +4.328081273e-01f, -2.908292135e-02f },
+    { -4.868437251e-02f, +6.485325111e-01f, +4.331531396e-01f, -2.911440776e-02f },
+    { -4.865563904e-02f, +6.481917765e-01f, +4.334981980e-01f, -2.914590319e-02f },
+    { -4.862688440e-02f, +6.478509792e-01f, +4.338433021e-01f, -2.917740760e-02f },
+    { -4.859810867e-02f, +6.475101192e-01f, +4.341884520e-01f, -2.920892096e-02f },
+    { -4.856931190e-02f, +6.471691968e-01f, +4.345336475e-01f, -2.924044325e-02f },
+    { -4.854049416e-02f, +6.468282121e-01f, +4.348788883e-01f, -2.927197441e-02f },
+    { -4.851165552e-02f, +6.464871653e-01f, +4.352241743e-01f, -2.930351443e-02f },
+    { -4.848279603e-02f, +6.461460567e-01f, +4.355695055e-01f, -2.933506327e-02f },
+    { -4.845391577e-02f, +6.458048863e-01f, +4.359148815e-01f, -2.936662089e-02f },
+    { -4.842501479e-02f, +6.454636545e-01f, +4.362603023e-01f, -2.939818727e-02f },
+    { -4.839609316e-02f, +6.451223613e-01f, +4.366057677e-01f, -2.942976236e-02f },
+    { -4.836715094e-02f, +6.447810070e-01f, +4.369512775e-01f, -2.946134614e-02f },
+    { -4.833818821e-02f, +6.444395917e-01f, +4.372968316e-01f, -2.949293857e-02f },
+    { -4.830920501e-02f, +6.440981157e-01f, +4.376424298e-01f, -2.952453962e-02f },
+    { -4.828020142e-02f, +6.437565791e-01f, +4.379880720e-01f, -2.955614925e-02f },
+    { -4.825117750e-02f, +6.434149821e-01f, +4.383337580e-01f, -2.958776743e-02f },
+    { -4.822213331e-02f, +6.430733249e-01f, +4.386794876e-01f, -2.961939414e-02f },
+    { -4.819306891e-02f, +6.427316077e-01f, +4.390252607e-01f, -2.965102932e-02f },
+    { -4.816398438e-02f, +6.423898306e-01f, +4.393710771e-01f, -2.968267296e-02f },
+    { -4.813487976e-02f, +6.420479939e-01f, +4.397169367e-01f, -2.971432501e-02f },
+    { -4.810575513e-02f, +6.417060978e-01f, +4.400628392e-01f, -2.974598544e-02f },
+    { -4.807661056e-02f, +6.413641423e-01f, +4.404087847e-01f, -2.977765422e-02f },
+    { -4.804744609e-02f, +6.410221278e-01f, +4.407547728e-01f, -2.980933131e-02f },
+    { -4.801826180e-02f, +6.406800544e-01f, +4.411008034e-01f, -2.984101669e-02f },
+    { -4.798905775e-02f, +6.403379223e-01f, +4.414468764e-01f, -2.987271031e-02f },
+    { -4.795983400e-02f, +6.399957316e-01f, +4.417929916e-01f, -2.990441214e-02f },
+    { -4.793059062e-02f, +6.396534826e-01f, +4.421391489e-01f, -2.993612215e-02f },
+    { -4.790132766e-02f, +6.393111754e-01f, +4.424853480e-01f, -2.996784030e-02f },
+    { -4.787204520e-02f, +6.389688103e-01f, +4.428315888e-01f, -2.999956656e-02f },
+    { -4.784274329e-02f, +6.386263874e-01f, +4.431778713e-01f, -3.003130090e-02f },
+    { -4.781342200e-02f, +6.382839069e-01f, +4.435241951e-01f, -3.006304328e-02f },
+    { -4.778408139e-02f, +6.379413690e-01f, +4.438705601e-01f, -3.009479366e-02f },
+    { -4.775472152e-02f, +6.375987738e-01f, +4.442169663e-01f, -3.012655201e-02f },
+    { -4.772534246e-02f, +6.372561217e-01f, +4.445634133e-01f, -3.015831830e-02f },
+    { -4.769594427e-02f, +6.369134126e-01f, +4.449099012e-01f, -3.019009249e-02f },
+    { -4.766652701e-02f, +6.365706469e-01f, +4.452564296e-01f, -3.022187454e-02f },
+    { -4.763709074e-02f, +6.362278248e-01f, +4.456029984e-01f, -3.025366443e-02f },
+    { -4.760763553e-02f, +6.358849463e-01f, +4.459496076e-01f, -3.028546212e-02f },
+    { -4.757816144e-02f, +6.355420117e-01f, +4.462962568e-01f, -3.031726757e-02f },
+    { -4.754866853e-02f, +6.351990212e-01f, +4.466429460e-01f, -3.034908075e-02f },
+    { -4.751915687e-02f, +6.348559750e-01f, +4.469896750e-01f, -3.038090162e-02f },
+    { -4.748962651e-02f, +6.345128733e-01f, +4.473364437e-01f, -3.041273015e-02f },
+    { -4.746007752e-02f, +6.341697161e-01f, +4.476832518e-01f, -3.044456630e-02f },
+    { -4.743050997e-02f, +6.338265038e-01f, +4.480300992e-01f, -3.047641003e-02f },
+    { -4.740092391e-02f, +6.334832365e-01f, +4.483769858e-01f, -3.050826133e-02f },
+    { -4.737131940e-02f, +6.331399144e-01f, +4.487239113e-01f, -3.054012013e-02f },
+    { -4.734169651e-02f, +6.327965377e-01f, +4.490708757e-01f, -3.057198642e-02f },
+    { -4.731205530e-02f, +6.324531066e-01f, +4.494178788e-01f, -3.060386016e-02f },
+    { -4.728239584e-02f, +6.321096212e-01f, +4.497649203e-01f, -3.063574131e-02f },
+    { -4.725271818e-02f, +6.317660818e-01f, +4.501120002e-01f, -3.066762983e-02f },
+    { -4.722302238e-02f, +6.314224885e-01f, +4.504591183e-01f, -3.069952570e-02f },
+    { -4.719330852e-02f, +6.310788415e-01f, +4.508062744e-01f, -3.073142887e-02f },
+    { -4.716357665e-02f, +6.307351410e-01f, +4.511534683e-01f, -3.076333930e-02f },
+    { -4.713382682e-02f, +6.303913872e-01f, +4.515006999e-01f, -3.079525698e-02f },
+    { -4.710405912e-02f, +6.300475802e-01f, +4.518479691e-01f, -3.082718185e-02f },
+    { -4.707427359e-02f, +6.297037204e-01f, +4.521952756e-01f, -3.085911388e-02f },
+    { -4.704447029e-02f, +6.293598077e-01f, +4.525426193e-01f, -3.089105304e-02f },
+    { -4.701464930e-02f, +6.290158425e-01f, +4.528900001e-01f, -3.092299929e-02f },
+    { -4.698481067e-02f, +6.286718249e-01f, +4.532374178e-01f, -3.095495259e-02f },
+    { -4.695495446e-02f, +6.283277552e-01f, +4.535848721e-01f, -3.098691291e-02f },
+    { -4.692508073e-02f, +6.279836334e-01f, +4.539323631e-01f, -3.101888022e-02f },
+    { -4.689518955e-02f, +6.276394598e-01f, +4.542798904e-01f, -3.105085447e-02f },
+    { -4.686528098e-02f, +6.272952345e-01f, +4.546274539e-01f, -3.108283564e-02f },
+    { -4.683535507e-02f, +6.269509578e-01f, +4.549750535e-01f, -3.111482367e-02f },
+    { -4.680541190e-02f, +6.266066299e-01f, +4.553226890e-01f, -3.114681855e-02f },
+    { -4.677545152e-02f, +6.262622508e-01f, +4.556703603e-01f, -3.117882023e-02f },
+    { -4.674547399e-02f, +6.259178209e-01f, +4.560180671e-01f, -3.121082867e-02f },
+    { -4.671547937e-02f, +6.255733402e-01f, +4.563658094e-01f, -3.124284384e-02f },
+    { -4.668546773e-02f, +6.252288091e-01f, +4.567135869e-01f, -3.127486571e-02f },
+    { -4.665543913e-02f, +6.248842275e-01f, +4.570613994e-01f, -3.130689423e-02f },
+    { -4.662539362e-02f, +6.245395959e-01f, +4.574092469e-01f, -3.133892937e-02f },
+    { -4.659533127e-02f, +6.241949143e-01f, +4.577571292e-01f, -3.137097110e-02f },
+    { -4.656525215e-02f, +6.238501829e-01f, +4.581050460e-01f, -3.140301937e-02f },
+    { -4.653515630e-02f, +6.235054019e-01f, +4.584529973e-01f, -3.143507415e-02f },
+    { -4.650504379e-02f, +6.231605715e-01f, +4.588009829e-01f, -3.146713541e-02f },
+    { -4.647491468e-02f, +6.228156918e-01f, +4.591490025e-01f, -3.149920310e-02f },
+    { -4.644476904e-02f, +6.224707632e-01f, +4.594970561e-01f, -3.153127720e-02f },
+    { -4.641460692e-02f, +6.221257857e-01f, +4.598451435e-01f, -3.156335765e-02f },
+    { -4.638442839e-02f, +6.217807595e-01f, +4.601932645e-01f, -3.159544443e-02f },
+    { -4.635423350e-02f, +6.214356849e-01f, +4.605414189e-01f, -3.162753750e-02f },
+    { -4.632402231e-02f, +6.210905619e-01f, +4.608896066e-01f, -3.165963682e-02f },
+    { -4.629379489e-02f, +6.207453909e-01f, +4.612378274e-01f, -3.169174236e-02f },
+    { -4.626355130e-02f, +6.204001719e-01f, +4.615860812e-01f, -3.172385407e-02f },
+    { -4.623329159e-02f, +6.200549052e-01f, +4.619343678e-01f, -3.175597193e-02f },
+    { -4.620301584e-02f, +6.197095909e-01f, +4.622826870e-01f, -3.178809589e-02f },
+    { -4.617272409e-02f, +6.193642293e-01f, +4.626310387e-01f, -3.182022591e-02f },
+    { -4.614241641e-02f, +6.190188205e-01f, +4.629794226e-01f, -3.185236196e-02f },
+    { -4.611209285e-02f, +6.186733647e-01f, +4.633278387e-01f, -3.188450401e-02f },
+    { -4.608175349e-02f, +6.183278621e-01f, +4.636762867e-01f, -3.191665200e-02f },
+    { -4.605139837e-02f, +6.179823129e-01f, +4.640247666e-01f, -3.194880592e-02f },
+    { -4.602102757e-02f, +6.176367173e-01f, +4.643732781e-01f, -3.198096571e-02f },
+    { -4.599064113e-02f, +6.172910754e-01f, +4.647218210e-01f, -3.201313134e-02f },
+    { -4.596023912e-02f, +6.169453874e-01f, +4.650703953e-01f, -3.204530278e-02f },
+    { -4.592982160e-02f, +6.165996536e-01f, +4.654190007e-01f, -3.207747998e-02f },
+    { -4.589938863e-02f, +6.162538740e-01f, +4.657676371e-01f, -3.210966291e-02f },
+    { -4.586894027e-02f, +6.159080490e-01f, +4.661163043e-01f, -3.214185153e-02f },
+    { -4.583847658e-02f, +6.155621786e-01f, +4.664650021e-01f, -3.217404580e-02f },
+    { -4.580799762e-02f, +6.152162631e-01f, +4.668137304e-01f, -3.220624568e-02f },
+    { -4.577750344e-02f, +6.148703026e-01f, +4.671624891e-01f, -3.223845114e-02f },
+    { -4.574699411e-02f, +6.145242974e-01f, +4.675112778e-01f, -3.227066214e-02f },
+    { -4.571646969e-02f, +6.141782476e-01f, +4.678600966e-01f, -3.230287864e-02f },
+    { -4.568593024e-02f, +6.138321534e-01f, +4.682089452e-01f, -3.233510060e-02f },
+    { -4.565537582e-02f, +6.134860150e-01f, +4.685578234e-01f, -3.236732799e-02f },
+    { -4.562480648e-02f, +6.131398326e-01f, +4.689067311e-01f, -3.239956076e-02f },
+    { -4.559422228e-02f, +6.127936063e-01f, +4.692556681e-01f, -3.243179888e-02f },
+    { -4.556362330e-02f, +6.124473364e-01f, +4.696046343e-01f, -3.246404230e-02f },
+    { -4.553300957e-02f, +6.121010230e-01f, +4.699536294e-01f, -3.249629100e-02f },
+    { -4.550238117e-02f, +6.117546663e-01f, +4.703026534e-01f, -3.252854493e-02f },
+    { -4.547173816e-02f, +6.114082665e-01f, +4.706517061e-01f, -3.256080405e-02f },
+    { -4.544108058e-02f, +6.110618238e-01f, +4.710007872e-01f, -3.259306833e-02f },
+    { -4.541040851e-02f, +6.107153384e-01f, +4.713498966e-01f, -3.262533772e-02f },
+    { -4.537972200e-02f, +6.103688104e-01f, +4.716990342e-01f, -3.265761219e-02f },
+    { -4.534902111e-02f, +6.100222401e-01f, +4.720481998e-01f, -3.268989170e-02f },
+    { -4.531830590e-02f, +6.096756276e-01f, +4.723973932e-01f, -3.272217621e-02f },
+    { -4.528757642e-02f, +6.093289732e-01f, +4.727466142e-01f, -3.275446568e-02f },
+    { -4.525683274e-02f, +6.089822769e-01f, +4.730958628e-01f, -3.278676008e-02f },
+    { -4.522607492e-02f, +6.086355390e-01f, +4.734451386e-01f, -3.281905935e-02f },
+    { -4.519530301e-02f, +6.082887597e-01f, +4.737944416e-01f, -3.285136347e-02f },
+    { -4.516451708e-02f, +6.079419391e-01f, +4.741437716e-01f, -3.288367240e-02f },
+    { -4.513371718e-02f, +6.075950775e-01f, +4.744931285e-01f, -3.291598609e-02f },
+    { -4.510290336e-02f, +6.072481750e-01f, +4.748425119e-01f, -3.294830451e-02f },
+    { -4.507207570e-02f, +6.069012318e-01f, +4.751919219e-01f, -3.298062762e-02f },
+    { -4.504123425e-02f, +6.065542481e-01f, +4.755413581e-01f, -3.301295538e-02f },
+    { -4.501037906e-02f, +6.062072240e-01f, +4.758908206e-01f, -3.304528775e-02f },
+    { -4.497951020e-02f, +6.058601599e-01f, +4.762403090e-01f, -3.307762468e-02f },
+    { -4.494862772e-02f, +6.055130558e-01f, +4.765898232e-01f, -3.310996615e-02f },
+    { -4.491773168e-02f, +6.051659119e-01f, +4.769393630e-01f, -3.314231211e-02f },
+    { -4.488682214e-02f, +6.048187284e-01f, +4.772889284e-01f, -3.317466252e-02f },
+    { -4.485589916e-02f, +6.044715056e-01f, +4.776385190e-01f, -3.320701734e-02f },
+    { -4.482496280e-02f, +6.041242435e-01f, +4.779881348e-01f, -3.323937653e-02f },
+    { -4.479401311e-02f, +6.037769424e-01f, +4.783377756e-01f, -3.327174006e-02f },
+    { -4.476305016e-02f, +6.034296025e-01f, +4.786874411e-01f, -3.330410788e-02f },
+    { -4.473207400e-02f, +6.030822239e-01f, +4.790371313e-01f, -3.333647996e-02f },
+    { -4.470108468e-02f, +6.027348068e-01f, +4.793868460e-01f, -3.336885624e-02f },
+    { -4.467008228e-02f, +6.023873514e-01f, +4.797365850e-01f, -3.340123671e-02f },
+    { -4.463906683e-02f, +6.020398580e-01f, +4.800863481e-01f, -3.343362130e-02f },
+    { -4.460803842e-02f, +6.016923266e-01f, +4.804361352e-01f, -3.346600999e-02f },
+    { -4.457699708e-02f, +6.013447575e-01f, +4.807859461e-01f, -3.349840273e-02f },
+    { -4.454594288e-02f, +6.009971508e-01f, +4.811357806e-01f, -3.353079949e-02f },
+    { -4.451487588e-02f, +6.006495068e-01f, +4.814856386e-01f, -3.356320022e-02f },
+    { -4.448379613e-02f, +6.003018256e-01f, +4.818355198e-01f, -3.359560488e-02f },
+    { -4.445270370e-02f, +5.999541074e-01f, +4.821854242e-01f, -3.362801344e-02f },
+    { -4.442159863e-02f, +5.996063524e-01f, +4.825353516e-01f, -3.366042585e-02f },
+    { -4.439048099e-02f, +5.992585607e-01f, +4.828853017e-01f, -3.369284207e-02f },
+    { -4.435935084e-02f, +5.989107327e-01f, +4.832352745e-01f, -3.372526206e-02f },
+    { -4.432820823e-02f, +5.985628683e-01f, +4.835852697e-01f, -3.375768579e-02f },
+    { -4.429705321e-02f, +5.982149679e-01f, +4.839352871e-01f, -3.379011321e-02f },
+    { -4.426588586e-02f, +5.978670316e-01f, +4.842853267e-01f, -3.382254427e-02f },
+    { -4.423470622e-02f, +5.975190596e-01f, +4.846353882e-01f, -3.385497895e-02f },
+    { -4.420351435e-02f, +5.971710521e-01f, +4.849854715e-01f, -3.388741720e-02f },
+    { -4.417231031e-02f, +5.968230092e-01f, +4.853355764e-01f, -3.391985897e-02f },
+    { -4.414109415e-02f, +5.964749312e-01f, +4.856857028e-01f, -3.395230423e-02f },
+    { -4.410986594e-02f, +5.961268182e-01f, +4.860358504e-01f, -3.398475294e-02f },
+    { -4.407862573e-02f, +5.957786704e-01f, +4.863860191e-01f, -3.401720506e-02f },
+    { -4.404737358e-02f, +5.954304881e-01f, +4.867362087e-01f, -3.404966054e-02f },
+    { -4.401610954e-02f, +5.950822713e-01f, +4.870864191e-01f, -3.408211934e-02f },
+    { -4.398483367e-02f, +5.947340203e-01f, +4.874366500e-01f, -3.411458143e-02f },
+    { -4.395354603e-02f, +5.943857352e-01f, +4.877869014e-01f, -3.414704676e-02f },
+    { -4.392224667e-02f, +5.940374163e-01f, +4.881371730e-01f, -3.417951529e-02f },
+    { -4.389093566e-02f, +5.936890637e-01f, +4.884874647e-01f, -3.421198698e-02f },
+    { -4.385961304e-02f, +5.933406776e-01f, +4.888377763e-01f, -3.424446179e-02f },
+    { -4.382827888e-02f, +5.929922582e-01f, +4.891881076e-01f, -3.427693968e-02f },
+    { -4.379693323e-02f, +5.926438057e-01f, +4.895384585e-01f, -3.430942060e-02f },
+    { -4.376557615e-02f, +5.922953202e-01f, +4.898888288e-01f, -3.434190452e-02f },
+    { -4.373420769e-02f, +5.919468020e-01f, +4.902392184e-01f, -3.437439139e-02f },
+    { -4.370282792e-02f, +5.915982512e-01f, +4.905896269e-01f, -3.440688117e-02f },
+    { -4.367143688e-02f, +5.912496680e-01f, +4.909400544e-01f, -3.443937382e-02f },
+    { -4.364003464e-02f, +5.909010526e-01f, +4.912905006e-01f, -3.447186930e-02f },
+    { -4.360862124e-02f, +5.905524052e-01f, +4.916409653e-01f, -3.450436756e-02f },
+    { -4.357719676e-02f, +5.902037260e-01f, +4.919914484e-01f, -3.453686857e-02f },
+    { -4.354576123e-02f, +5.898550151e-01f, +4.923419497e-01f, -3.456937229e-02f },
+    { -4.351431473e-02f, +5.895062727e-01f, +4.926924690e-01f, -3.460187866e-02f },
+    { -4.348285730e-02f, +5.891574990e-01f, +4.930430062e-01f, -3.463438766e-02f },
+    { -4.345138900e-02f, +5.888086942e-01f, +4.933935611e-01f, -3.466689923e-02f },
+    { -4.341990989e-02f, +5.884598586e-01f, +4.937441335e-01f, -3.469941334e-02f },
+    { -4.338842003e-02f, +5.881109921e-01f, +4.940947233e-01f, -3.473192994e-02f },
+    { -4.335691946e-02f, +5.877620952e-01f, +4.944453302e-01f, -3.476444900e-02f },
+    { -4.332540825e-02f, +5.874131678e-01f, +4.947959542e-01f, -3.479697046e-02f },
+    { -4.329388645e-02f, +5.870642103e-01f, +4.951465949e-01f, -3.482949429e-02f },
+    { -4.326235411e-02f, +5.867152227e-01f, +4.954972524e-01f, -3.486202044e-02f },
+    { -4.323081130e-02f, +5.863662054e-01f, +4.958479263e-01f, -3.489454888e-02f },
+    { -4.319925807e-02f, +5.860171584e-01f, +4.961986166e-01f, -3.492707956e-02f },
+    { -4.316769447e-02f, +5.856680820e-01f, +4.965493230e-01f, -3.495961243e-02f },
+    { -4.313612056e-02f, +5.853189763e-01f, +4.969000454e-01f, -3.499214746e-02f },
+    { -4.310453640e-02f, +5.849698415e-01f, +4.972507836e-01f, -3.502468461e-02f },
+    { -4.307294203e-02f, +5.846206778e-01f, +4.976015374e-01f, -3.505722382e-02f },
+    { -4.304133752e-02f, +5.842714854e-01f, +4.979523067e-01f, -3.508976506e-02f },
+    { -4.300972293e-02f, +5.839222644e-01f, +4.983030913e-01f, -3.512230829e-02f },
+    { -4.297809830e-02f, +5.835730151e-01f, +4.986538910e-01f, -3.515485346e-02f },
+    { -4.294646369e-02f, +5.832237377e-01f, +4.990047056e-01f, -3.518740053e-02f },
+    { -4.291481916e-02f, +5.828744322e-01f, +4.993555351e-01f, -3.521994946e-02f },
+    { -4.288316476e-02f, +5.825250990e-01f, +4.997063791e-01f, -3.525250020e-02f },
+    { -4.285150055e-02f, +5.821757381e-01f, +5.000572376e-01f, -3.528505271e-02f },
+    { -4.281982659e-02f, +5.818263498e-01f, +5.004081103e-01f, -3.531760695e-02f },
+    { -4.278814292e-02f, +5.814769342e-01f, +5.007589972e-01f, -3.535016287e-02f },
+    { -4.275644960e-02f, +5.811274916e-01f, +5.011098979e-01f, -3.538272044e-02f },
+    { -4.272474669e-02f, +5.807780221e-01f, +5.014608124e-01f, -3.541527960e-02f },
+    { -4.269303424e-02f, +5.804285259e-01f, +5.018117405e-01f, -3.544784032e-02f },
+    { -4.266131231e-02f, +5.800790031e-01f, +5.021626819e-01f, -3.548040255e-02f },
+    { -4.262958096e-02f, +5.797294541e-01f, +5.025136366e-01f, -3.551296626e-02f },
+    { -4.259784023e-02f, +5.793798788e-01f, +5.028646044e-01f, -3.554553138e-02f },
+    { -4.256609018e-02f, +5.790302776e-01f, +5.032155850e-01f, -3.557809789e-02f },
+    { -4.253433087e-02f, +5.786806507e-01f, +5.035665783e-01f, -3.561066574e-02f },
+    { -4.250256235e-02f, +5.783309981e-01f, +5.039175842e-01f, -3.564323489e-02f },
+    { -4.247078467e-02f, +5.779813201e-01f, +5.042686025e-01f, -3.567580528e-02f },
+    { -4.243899790e-02f, +5.776316168e-01f, +5.046196329e-01f, -3.570837689e-02f },
+    { -4.240720208e-02f, +5.772818885e-01f, +5.049706753e-01f, -3.574094966e-02f },
+    { -4.237539726e-02f, +5.769321353e-01f, +5.053217296e-01f, -3.577352355e-02f },
+    { -4.234358351e-02f, +5.765823574e-01f, +5.056727956e-01f, -3.580609851e-02f },
+    { -4.231176088e-02f, +5.762325550e-01f, +5.060238731e-01f, -3.583867451e-02f },
+    { -4.227992942e-02f, +5.758827283e-01f, +5.063749619e-01f, -3.587125150e-02f },
+    { -4.224808919e-02f, +5.755328774e-01f, +5.067260619e-01f, -3.590382944e-02f },
+    { -4.221624023e-02f, +5.751830026e-01f, +5.070771728e-01f, -3.593640828e-02f },
+    { -4.218438261e-02f, +5.748331040e-01f, +5.074282945e-01f, -3.596898797e-02f },
+    { -4.215251638e-02f, +5.744831818e-01f, +5.077794269e-01f, -3.600156848e-02f },
+    { -4.212064159e-02f, +5.741332362e-01f, +5.081305697e-01f, -3.603414976e-02f },
+    { -4.208875830e-02f, +5.737832673e-01f, +5.084817229e-01f, -3.606673176e-02f },
+    { -4.205686655e-02f, +5.734332754e-01f, +5.088328861e-01f, -3.609931444e-02f },
+    { -4.202496641e-02f, +5.730832607e-01f, +5.091840593e-01f, -3.613189776e-02f },
+    { -4.199305793e-02f, +5.727332232e-01f, +5.095352422e-01f, -3.616448168e-02f },
+    { -4.196114116e-02f, +5.723831633e-01f, +5.098864348e-01f, -3.619706614e-02f },
+    { -4.192921615e-02f, +5.720330810e-01f, +5.102376367e-01f, -3.622965110e-02f },
+    { -4.189728297e-02f, +5.716829766e-01f, +5.105888479e-01f, -3.626223652e-02f },
+    { -4.186534165e-02f, +5.713328502e-01f, +5.109400682e-01f, -3.629482236e-02f },
+    { -4.183339226e-02f, +5.709827020e-01f, +5.112912974e-01f, -3.632740857e-02f },
+    { -4.180143485e-02f, +5.706325323e-01f, +5.116425353e-01f, -3.635999510e-02f },
+    { -4.176946947e-02f, +5.702823411e-01f, +5.119937817e-01f, -3.639258191e-02f },
+    { -4.173749618e-02f, +5.699321287e-01f, +5.123450366e-01f, -3.642516896e-02f },
+    { -4.170551503e-02f, +5.695818953e-01f, +5.126962996e-01f, -3.645775620e-02f },
+    { -4.167352607e-02f, +5.692316410e-01f, +5.130475706e-01f, -3.649034358e-02f },
+    { -4.164152936e-02f, +5.688813660e-01f, +5.133988495e-01f, -3.652293107e-02f },
+    { -4.160952494e-02f, +5.685310706e-01f, +5.137501361e-01f, -3.655551861e-02f },
+    { -4.157751288e-02f, +5.681807548e-01f, +5.141014302e-01f, -3.658810617e-02f },
+    { -4.154549322e-02f, +5.678304188e-01f, +5.144527316e-01f, -3.662069369e-02f },
+    { -4.151346602e-02f, +5.674800629e-01f, +5.148040402e-01f, -3.665328113e-02f },
+    { -4.148143133e-02f, +5.671296873e-01f, +5.151553558e-01f, -3.668586844e-02f },
+    { -4.144938921e-02f, +5.667792920e-01f, +5.155066781e-01f, -3.671845559e-02f },
+    { -4.141733970e-02f, +5.664288773e-01f, +5.158580071e-01f, -3.675104252e-02f },
+    { -4.138528287e-02f, +5.660784434e-01f, +5.162093426e-01f, -3.678362919e-02f },
+    { -4.135321875e-02f, +5.657279905e-01f, +5.165606843e-01f, -3.681621556e-02f },
+    { -4.132114741e-02f, +5.653775186e-01f, +5.169120321e-01f, -3.684880158e-02f },
+    { -4.128906890e-02f, +5.650270281e-01f, +5.172633859e-01f, -3.688138720e-02f },
+    { -4.125698327e-02f, +5.646765191e-01f, +5.176147454e-01f, -3.691397237e-02f },
+    { -4.122489058e-02f, +5.643259918e-01f, +5.179661105e-01f, -3.694655706e-02f },
+    { -4.119279087e-02f, +5.639754463e-01f, +5.183174810e-01f, -3.697914122e-02f },
+    { -4.116068419e-02f, +5.636248829e-01f, +5.186688568e-01f, -3.701172480e-02f },
+    { -4.112857061e-02f, +5.632743017e-01f, +5.190202376e-01f, -3.704430775e-02f },
+    { -4.109645017e-02f, +5.629237029e-01f, +5.193716233e-01f, -3.707689003e-02f },
+    { -4.106432293e-02f, +5.625730867e-01f, +5.197230137e-01f, -3.710947160e-02f },
+    { -4.103218893e-02f, +5.622224532e-01f, +5.200744086e-01f, -3.714205241e-02f },
+    { -4.100004824e-02f, +5.618718027e-01f, +5.204258079e-01f, -3.717463240e-02f },
+    { -4.096790089e-02f, +5.615211353e-01f, +5.207772113e-01f, -3.720721154e-02f },
+    { -4.093574695e-02f, +5.611704512e-01f, +5.211286188e-01f, -3.723978979e-02f },
+    { -4.090358647e-02f, +5.608197506e-01f, +5.214800301e-01f, -3.727236708e-02f },
+    { -4.087141949e-02f, +5.604690337e-01f, +5.218314450e-01f, -3.730494338e-02f },
+    { -4.083924608e-02f, +5.601183006e-01f, +5.221828634e-01f, -3.733751865e-02f },
+    { -4.080706628e-02f, +5.597675516e-01f, +5.225342852e-01f, -3.737009283e-02f },
+    { -4.077488014e-02f, +5.594167868e-01f, +5.228857100e-01f, -3.740266587e-02f },
+    { -4.074268771e-02f, +5.590660064e-01f, +5.232371378e-01f, -3.743523774e-02f },
+    { -4.071048906e-02f, +5.587152105e-01f, +5.235885684e-01f, -3.746780839e-02f },
+    { -4.067828422e-02f, +5.583643994e-01f, +5.239400015e-01f, -3.750037777e-02f },
+    { -4.064607325e-02f, +5.580135732e-01f, +5.242914371e-01f, -3.753294582e-02f },
+    { -4.061385621e-02f, +5.576627322e-01f, +5.246428750e-01f, -3.756551252e-02f },
+    { -4.058163314e-02f, +5.573118764e-01f, +5.249943149e-01f, -3.759807780e-02f },
+    { -4.054940409e-02f, +5.569610062e-01f, +5.253457567e-01f, -3.763064163e-02f },
+    { -4.051716913e-02f, +5.566101215e-01f, +5.256972002e-01f, -3.766320395e-02f },
+    { -4.048492829e-02f, +5.562592228e-01f, +5.260486453e-01f, -3.769576473e-02f },
+    { -4.045268163e-02f, +5.559083100e-01f, +5.264000917e-01f, -3.772832390e-02f },
+    { -4.042042920e-02f, +5.555573834e-01f, +5.267515394e-01f, -3.776088143e-02f },
+    { -4.038817105e-02f, +5.552064433e-01f, +5.271029880e-01f, -3.779343727e-02f },
+    { -4.035590724e-02f, +5.548554896e-01f, +5.274544375e-01f, -3.782599137e-02f },
+    { -4.032363781e-02f, +5.545045228e-01f, +5.278058876e-01f, -3.785854369e-02f },
+    { -4.029136282e-02f, +5.541535428e-01f, +5.281573382e-01f, -3.789109417e-02f },
+    { -4.025908231e-02f, +5.538025499e-01f, +5.285087891e-01f, -3.792364277e-02f },
+    { -4.022679634e-02f, +5.534515443e-01f, +5.288602402e-01f, -3.795618945e-02f },
+    { -4.019450496e-02f, +5.531005262e-01f, +5.292116912e-01f, -3.798873415e-02f },
+    { -4.016220822e-02f, +5.527494957e-01f, +5.295631419e-01f, -3.802127683e-02f },
+    { -4.012990617e-02f, +5.523984531e-01f, +5.299145923e-01f, -3.805381745e-02f },
+    { -4.009759887e-02f, +5.520473984e-01f, +5.302660421e-01f, -3.808635594e-02f },
+    { -4.006528635e-02f, +5.516963319e-01f, +5.306174912e-01f, -3.811889228e-02f },
+    { -4.003296867e-02f, +5.513452538e-01f, +5.309689393e-01f, -3.815142640e-02f },
+    { -4.000064589e-02f, +5.509941643e-01f, +5.313203863e-01f, -3.818395826e-02f },
+    { -3.996831805e-02f, +5.506430634e-01f, +5.316718320e-01f, -3.821648782e-02f },
+    { -3.993598521e-02f, +5.502919514e-01f, +5.320232763e-01f, -3.824901502e-02f },
+    { -3.990364741e-02f, +5.499408286e-01f, +5.323747189e-01f, -3.828153982e-02f },
+    { -3.987130471e-02f, +5.495896949e-01f, +5.327261597e-01f, -3.831406217e-02f },
+    { -3.983895715e-02f, +5.492385508e-01f, +5.330775985e-01f, -3.834658202e-02f },
+    { -3.980660479e-02f, +5.488873962e-01f, +5.334290351e-01f, -3.837909933e-02f },
+    { -3.977424767e-02f, +5.485362314e-01f, +5.337804694e-01f, -3.841161404e-02f },
+    { -3.974188585e-02f, +5.481850566e-01f, +5.341319012e-01f, -3.844412612e-02f },
+    { -3.970951938e-02f, +5.478338720e-01f, +5.344833302e-01f, -3.847663550e-02f },
+    { -3.967714830e-02f, +5.474826777e-01f, +5.348347564e-01f, -3.850914215e-02f },
+    { -3.964477267e-02f, +5.471314739e-01f, +5.351861795e-01f, -3.854164601e-02f },
+    { -3.961239253e-02f, +5.467802608e-01f, +5.355375994e-01f, -3.857414703e-02f },
+    { -3.958000795e-02f, +5.464290386e-01f, +5.358890159e-01f, -3.860664518e-02f },
+    { -3.954761896e-02f, +5.460778075e-01f, +5.362404287e-01f, -3.863914039e-02f },
+    { -3.951522561e-02f, +5.457265676e-01f, +5.365918378e-01f, -3.867163263e-02f },
+    { -3.948282796e-02f, +5.453753190e-01f, +5.369432430e-01f, -3.870412184e-02f },
+    { -3.945042606e-02f, +5.450240621e-01f, +5.372946440e-01f, -3.873660797e-02f },
+    { -3.941801995e-02f, +5.446727970e-01f, +5.376460407e-01f, -3.876909098e-02f },
+    { -3.938560969e-02f, +5.443215238e-01f, +5.379974330e-01f, -3.880157082e-02f },
+    { -3.935319533e-02f, +5.439702427e-01f, +5.383488206e-01f, -3.883404743e-02f },
+    { -3.932077690e-02f, +5.436189539e-01f, +5.387002033e-01f, -3.886652078e-02f },
+    { -3.928835448e-02f, +5.432676576e-01f, +5.390515810e-01f, -3.889899081e-02f },
+    { -3.925592809e-02f, +5.429163540e-01f, +5.394029536e-01f, -3.893145747e-02f },
+    { -3.922349780e-02f, +5.425650432e-01f, +5.397543207e-01f, -3.896392071e-02f },
+    { -3.919106365e-02f, +5.422137254e-01f, +5.401056823e-01f, -3.899638049e-02f },
+    { -3.915862569e-02f, +5.418624009e-01f, +5.404570382e-01f, -3.902883675e-02f },
+    { -3.912618397e-02f, +5.415110697e-01f, +5.408083882e-01f, -3.906128945e-02f },
+    { -3.909373854e-02f, +5.411597321e-01f, +5.411597321e-01f, -3.909373854e-02f },
+    { -3.906128945e-02f, +5.408083882e-01f, +5.415110697e-01f, -3.912618397e-02f },
+    { -3.902883675e-02f, +5.404570382e-01f, +5.418624009e-01f, -3.915862569e-02f },
+    { -3.899638049e-02f, +5.401056823e-01f, +5.422137254e-01f, -3.919106365e-02f },
+    { -3.896392071e-02f, +5.397543207e-01f, +5.425650432e-01f, -3.922349780e-02f },
+    { -3.893145747e-02f, +5.394029536e-01f, +5.429163540e-01f, -3.925592809e-02f },
+    { -3.889899081e-02f, +5.390515810e-01f, +5.432676576e-01f, -3.928835448e-02f },
+    { -3.886652078e-02f, +5.387002033e-01f, +5.436189539e-01f, -3.932077690e-02f },
+    { -3.883404743e-02f, +5.383488206e-01f, +5.439702427e-01f, -3.935319533e-02f },
+    { -3.880157082e-02f, +5.379974330e-01f, +5.443215238e-01f, -3.938560969e-02f },
+    { -3.876909098e-02f, +5.376460407e-01f, +5.446727970e-01f, -3.941801995e-02f },
+    { -3.873660797e-02f, +5.372946440e-01f, +5.450240621e-01f, -3.945042606e-02f },
+    { -3.870412184e-02f, +5.369432430e-01f, +5.453753190e-01f, -3.948282796e-02f },
+    { -3.867163263e-02f, +5.365918378e-01f, +5.457265676e-01f, -3.951522561e-02f },
+    { -3.863914039e-02f, +5.362404287e-01f, +5.460778075e-01f, -3.954761896e-02f },
+    { -3.860664518e-02f, +5.358890159e-01f, +5.464290386e-01f, -3.958000795e-02f },
+    { -3.857414703e-02f, +5.355375994e-01f, +5.467802608e-01f, -3.961239253e-02f },
+    { -3.854164601e-02f, +5.351861795e-01f, +5.471314739e-01f, -3.964477267e-02f },
+    { -3.850914215e-02f, +5.348347564e-01f, +5.474826777e-01f, -3.967714830e-02f },
+    { -3.847663550e-02f, +5.344833302e-01f, +5.478338720e-01f, -3.970951938e-02f },
+    { -3.844412612e-02f, +5.341319012e-01f, +5.481850566e-01f, -3.974188585e-02f },
+    { -3.841161404e-02f, +5.337804694e-01f, +5.485362314e-01f, -3.977424767e-02f },
+    { -3.837909933e-02f, +5.334290351e-01f, +5.488873962e-01f, -3.980660479e-02f },
+    { -3.834658202e-02f, +5.330775985e-01f, +5.492385508e-01f, -3.983895715e-02f },
+    { -3.831406217e-02f, +5.327261597e-01f, +5.495896949e-01f, -3.987130471e-02f },
+    { -3.828153982e-02f, +5.323747189e-01f, +5.499408286e-01f, -3.990364741e-02f },
+    { -3.824901502e-02f, +5.320232763e-01f, +5.502919514e-01f, -3.993598521e-02f },
+    { -3.821648782e-02f, +5.316718320e-01f, +5.506430634e-01f, -3.996831805e-02f },
+    { -3.818395826e-02f, +5.313203863e-01f, +5.509941643e-01f, -4.000064589e-02f },
+    { -3.815142640e-02f, +5.309689393e-01f, +5.513452538e-01f, -4.003296867e-02f },
+    { -3.811889228e-02f, +5.306174912e-01f, +5.516963319e-01f, -4.006528635e-02f },
+    { -3.808635594e-02f, +5.302660421e-01f, +5.520473984e-01f, -4.009759887e-02f },
+    { -3.805381745e-02f, +5.299145923e-01f, +5.523984531e-01f, -4.012990617e-02f },
+    { -3.802127683e-02f, +5.295631419e-01f, +5.527494957e-01f, -4.016220822e-02f },
+    { -3.798873415e-02f, +5.292116912e-01f, +5.531005262e-01f, -4.019450496e-02f },
+    { -3.795618945e-02f, +5.288602402e-01f, +5.534515443e-01f, -4.022679634e-02f },
+    { -3.792364277e-02f, +5.285087891e-01f, +5.538025499e-01f, -4.025908231e-02f },
+    { -3.789109417e-02f, +5.281573382e-01f, +5.541535428e-01f, -4.029136282e-02f },
+    { -3.785854369e-02f, +5.278058876e-01f, +5.545045228e-01f, -4.032363781e-02f },
+    { -3.782599137e-02f, +5.274544375e-01f, +5.548554896e-01f, -4.035590724e-02f },
+    { -3.779343727e-02f, +5.271029880e-01f, +5.552064433e-01f, -4.038817105e-02f },
+    { -3.776088143e-02f, +5.267515394e-01f, +5.555573834e-01f, -4.042042920e-02f },
+    { -3.772832390e-02f, +5.264000917e-01f, +5.559083100e-01f, -4.045268163e-02f },
+    { -3.769576473e-02f, +5.260486453e-01f, +5.562592228e-01f, -4.048492829e-02f },
+    { -3.766320395e-02f, +5.256972002e-01f, +5.566101215e-01f, -4.051716913e-02f },
+    { -3.763064163e-02f, +5.253457567e-01f, +5.569610062e-01f, -4.054940409e-02f },
+    { -3.759807780e-02f, +5.249943149e-01f, +5.573118764e-01f, -4.058163314e-02f },
+    { -3.756551252e-02f, +5.246428750e-01f, +5.576627322e-01f, -4.061385621e-02f },
+    { -3.753294582e-02f, +5.242914371e-01f, +5.580135732e-01f, -4.064607325e-02f },
+    { -3.750037777e-02f, +5.239400015e-01f, +5.583643994e-01f, -4.067828422e-02f },
+    { -3.746780839e-02f, +5.235885684e-01f, +5.587152105e-01f, -4.071048906e-02f },
+    { -3.743523774e-02f, +5.232371378e-01f, +5.590660064e-01f, -4.074268771e-02f },
+    { -3.740266587e-02f, +5.228857100e-01f, +5.594167868e-01f, -4.077488014e-02f },
+    { -3.737009283e-02f, +5.225342852e-01f, +5.597675516e-01f, -4.080706628e-02f },
+    { -3.733751865e-02f, +5.221828634e-01f, +5.601183006e-01f, -4.083924608e-02f },
+    { -3.730494338e-02f, +5.218314450e-01f, +5.604690337e-01f, -4.087141949e-02f },
+    { -3.727236708e-02f, +5.214800301e-01f, +5.608197506e-01f, -4.090358647e-02f },
+    { -3.723978979e-02f, +5.211286188e-01f, +5.611704512e-01f, -4.093574695e-02f },
+    { -3.720721154e-02f, +5.207772113e-01f, +5.615211353e-01f, -4.096790089e-02f },
+    { -3.717463240e-02f, +5.204258079e-01f, +5.618718027e-01f, -4.100004824e-02f },
+    { -3.714205241e-02f, +5.200744086e-01f, +5.622224532e-01f, -4.103218893e-02f },
+    { -3.710947160e-02f, +5.197230137e-01f, +5.625730867e-01f, -4.106432293e-02f },
+    { -3.707689003e-02f, +5.193716233e-01f, +5.629237029e-01f, -4.109645017e-02f },
+    { -3.704430775e-02f, +5.190202376e-01f, +5.632743017e-01f, -4.112857061e-02f },
+    { -3.701172480e-02f, +5.186688568e-01f, +5.636248829e-01f, -4.116068419e-02f },
+    { -3.697914122e-02f, +5.183174810e-01f, +5.639754463e-01f, -4.119279087e-02f },
+    { -3.694655706e-02f, +5.179661105e-01f, +5.643259918e-01f, -4.122489058e-02f },
+    { -3.691397237e-02f, +5.176147454e-01f, +5.646765191e-01f, -4.125698327e-02f },
+    { -3.688138720e-02f, +5.172633859e-01f, +5.650270281e-01f, -4.128906890e-02f },
+    { -3.684880158e-02f, +5.169120321e-01f, +5.653775186e-01f, -4.132114741e-02f },
+    { -3.681621556e-02f, +5.165606843e-01f, +5.657279905e-01f, -4.135321875e-02f },
+    { -3.678362919e-02f, +5.162093426e-01f, +5.660784434e-01f, -4.138528287e-02f },
+    { -3.675104252e-02f, +5.158580071e-01f, +5.664288773e-01f, -4.141733970e-02f },
+    { -3.671845559e-02f, +5.155066781e-01f, +5.667792920e-01f, -4.144938921e-02f },
+    { -3.668586844e-02f, +5.151553558e-01f, +5.671296873e-01f, -4.148143133e-02f },
+    { -3.665328113e-02f, +5.148040402e-01f, +5.674800629e-01f, -4.151346602e-02f },
+    { -3.662069369e-02f, +5.144527316e-01f, +5.678304188e-01f, -4.154549322e-02f },
+    { -3.658810617e-02f, +5.141014302e-01f, +5.681807548e-01f, -4.157751288e-02f },
+    { -3.655551861e-02f, +5.137501361e-01f, +5.685310706e-01f, -4.160952494e-02f },
+    { -3.652293107e-02f, +5.133988495e-01f, +5.688813660e-01f, -4.164152936e-02f },
+    { -3.649034358e-02f, +5.130475706e-01f, +5.692316410e-01f, -4.167352607e-02f },
+    { -3.645775620e-02f, +5.126962996e-01f, +5.695818953e-01f, -4.170551503e-02f },
+    { -3.642516896e-02f, +5.123450366e-01f, +5.699321287e-01f, -4.173749618e-02f },
+    { -3.639258191e-02f, +5.119937817e-01f, +5.702823411e-01f, -4.176946947e-02f },
+    { -3.635999510e-02f, +5.116425353e-01f, +5.706325323e-01f, -4.180143485e-02f },
+    { -3.632740857e-02f, +5.112912974e-01f, +5.709827020e-01f, -4.183339226e-02f },
+    { -3.629482236e-02f, +5.109400682e-01f, +5.713328502e-01f, -4.186534165e-02f },
+    { -3.626223652e-02f, +5.105888479e-01f, +5.716829766e-01f, -4.189728297e-02f },
+    { -3.622965110e-02f, +5.102376367e-01f, +5.720330810e-01f, -4.192921615e-02f },
+    { -3.619706614e-02f, +5.098864348e-01f, +5.723831633e-01f, -4.196114116e-02f },
+    { -3.616448168e-02f, +5.095352422e-01f, +5.727332232e-01f, -4.199305793e-02f },
+    { -3.613189776e-02f, +5.091840593e-01f, +5.730832607e-01f, -4.202496641e-02f },
+    { -3.609931444e-02f, +5.088328861e-01f, +5.734332754e-01f, -4.205686655e-02f },
+    { -3.606673176e-02f, +5.084817229e-01f, +5.737832673e-01f, -4.208875830e-02f },
+    { -3.603414976e-02f, +5.081305697e-01f, +5.741332362e-01f, -4.212064159e-02f },
+    { -3.600156848e-02f, +5.077794269e-01f, +5.744831818e-01f, -4.215251638e-02f },
+    { -3.596898797e-02f, +5.074282945e-01f, +5.748331040e-01f, -4.218438261e-02f },
+    { -3.593640828e-02f, +5.070771728e-01f, +5.751830026e-01f, -4.221624023e-02f },
+    { -3.590382944e-02f, +5.067260619e-01f, +5.755328774e-01f, -4.224808919e-02f },
+    { -3.587125150e-02f, +5.063749619e-01f, +5.758827283e-01f, -4.227992942e-02f },
+    { -3.583867451e-02f, +5.060238731e-01f, +5.762325550e-01f, -4.231176088e-02f },
+    { -3.580609851e-02f, +5.056727956e-01f, +5.765823574e-01f, -4.234358351e-02f },
+    { -3.577352355e-02f, +5.053217296e-01f, +5.769321353e-01f, -4.237539726e-02f },
+    { -3.574094966e-02f, +5.049706753e-01f, +5.772818885e-01f, -4.240720208e-02f },
+    { -3.570837689e-02f, +5.046196329e-01f, +5.776316168e-01f, -4.243899790e-02f },
+    { -3.567580528e-02f, +5.042686025e-01f, +5.779813201e-01f, -4.247078467e-02f },
+    { -3.564323489e-02f, +5.039175842e-01f, +5.783309981e-01f, -4.250256235e-02f },
+    { -3.561066574e-02f, +5.035665783e-01f, +5.786806507e-01f, -4.253433087e-02f },
+    { -3.557809789e-02f, +5.032155850e-01f, +5.790302776e-01f, -4.256609018e-02f },
+    { -3.554553138e-02f, +5.028646044e-01f, +5.793798788e-01f, -4.259784023e-02f },
+    { -3.551296626e-02f, +5.025136366e-01f, +5.797294541e-01f, -4.262958096e-02f },
+    { -3.548040255e-02f, +5.021626819e-01f, +5.800790031e-01f, -4.266131231e-02f },
+    { -3.544784032e-02f, +5.018117405e-01f, +5.804285259e-01f, -4.269303424e-02f },
+    { -3.541527960e-02f, +5.014608124e-01f, +5.807780221e-01f, -4.272474669e-02f },
+    { -3.538272044e-02f, +5.011098979e-01f, +5.811274916e-01f, -4.275644960e-02f },
+    { -3.535016287e-02f, +5.007589972e-01f, +5.814769342e-01f, -4.278814292e-02f },
+    { -3.531760695e-02f, +5.004081103e-01f, +5.818263498e-01f, -4.281982659e-02f },
+    { -3.528505271e-02f, +5.000572376e-01f, +5.821757381e-01f, -4.285150055e-02f },
+    { -3.525250020e-02f, +4.997063791e-01f, +5.825250990e-01f, -4.288316476e-02f },
+    { -3.521994946e-02f, +4.993555351e-01f, +5.828744322e-01f, -4.291481916e-02f },
+    { -3.518740053e-02f, +4.990047056e-01f, +5.832237377e-01f, -4.294646369e-02f },
+    { -3.515485346e-02f, +4.986538910e-01f, +5.835730151e-01f, -4.297809830e-02f },
+    { -3.512230829e-02f, +4.983030913e-01f, +5.839222644e-01f, -4.300972293e-02f },
+    { -3.508976506e-02f, +4.979523067e-01f, +5.842714854e-01f, -4.304133752e-02f },
+    { -3.505722382e-02f, +4.976015374e-01f, +5.846206778e-01f, -4.307294203e-02f },
+    { -3.502468461e-02f, +4.972507836e-01f, +5.849698415e-01f, -4.310453640e-02f },
+    { -3.499214746e-02f, +4.969000454e-01f, +5.853189763e-01f, -4.313612056e-02f },
+    { -3.495961243e-02f, +4.965493230e-01f, +5.856680820e-01f, -4.316769447e-02f },
+    { -3.492707956e-02f, +4.961986166e-01f, +5.860171584e-01f, -4.319925807e-02f },
+    { -3.489454888e-02f, +4.958479263e-01f, +5.863662054e-01f, -4.323081130e-02f },
+    { -3.486202044e-02f, +4.954972524e-01f, +5.867152227e-01f, -4.326235411e-02f },
+    { -3.482949429e-02f, +4.951465949e-01f, +5.870642103e-01f, -4.329388645e-02f },
+    { -3.479697046e-02f, +4.947959542e-01f, +5.874131678e-01f, -4.332540825e-02f },
+    { -3.476444900e-02f, +4.944453302e-01f, +5.877620952e-01f, -4.335691946e-02f },
+    { -3.473192994e-02f, +4.940947233e-01f, +5.881109921e-01f, -4.338842003e-02f },
+    { -3.469941334e-02f, +4.937441335e-01f, +5.884598586e-01f, -4.341990989e-02f },
+    { -3.466689923e-02f, +4.933935611e-01f, +5.888086942e-01f, -4.345138900e-02f },
+    { -3.463438766e-02f, +4.930430062e-01f, +5.891574990e-01f, -4.348285730e-02f },
+    { -3.460187866e-02f, +4.926924690e-01f, +5.895062727e-01f, -4.351431473e-02f },
+    { -3.456937229e-02f, +4.923419497e-01f, +5.898550151e-01f, -4.354576123e-02f },
+    { -3.453686857e-02f, +4.919914484e-01f, +5.902037260e-01f, -4.357719676e-02f },
+    { -3.450436756e-02f, +4.916409653e-01f, +5.905524052e-01f, -4.360862124e-02f },
+    { -3.447186930e-02f, +4.912905006e-01f, +5.909010526e-01f, -4.364003464e-02f },
+    { -3.443937382e-02f, +4.909400544e-01f, +5.912496680e-01f, -4.367143688e-02f },
+    { -3.440688117e-02f, +4.905896269e-01f, +5.915982512e-01f, -4.370282792e-02f },
+    { -3.437439139e-02f, +4.902392184e-01f, +5.919468020e-01f, -4.373420769e-02f },
+    { -3.434190452e-02f, +4.898888288e-01f, +5.922953202e-01f, -4.376557615e-02f },
+    { -3.430942060e-02f, +4.895384585e-01f, +5.926438057e-01f, -4.379693323e-02f },
+    { -3.427693968e-02f, +4.891881076e-01f, +5.929922582e-01f, -4.382827888e-02f },
+    { -3.424446179e-02f, +4.888377763e-01f, +5.933406776e-01f, -4.385961304e-02f },
+    { -3.421198698e-02f, +4.884874647e-01f, +5.936890637e-01f, -4.389093566e-02f },
+    { -3.417951529e-02f, +4.881371730e-01f, +5.940374163e-01f, -4.392224667e-02f },
+    { -3.414704676e-02f, +4.877869014e-01f, +5.943857352e-01f, -4.395354603e-02f },
+    { -3.411458143e-02f, +4.874366500e-01f, +5.947340203e-01f, -4.398483367e-02f },
+    { -3.408211934e-02f, +4.870864191e-01f, +5.950822713e-01f, -4.401610954e-02f },
+    { -3.404966054e-02f, +4.867362087e-01f, +5.954304881e-01f, -4.404737358e-02f },
+    { -3.401720506e-02f, +4.863860191e-01f, +5.957786704e-01f, -4.407862573e-02f },
+    { -3.398475294e-02f, +4.860358504e-01f, +5.961268182e-01f, -4.410986594e-02f },
+    { -3.395230423e-02f, +4.856857028e-01f, +5.964749312e-01f, -4.414109415e-02f },
+    { -3.391985897e-02f, +4.853355764e-01f, +5.968230092e-01f, -4.417231031e-02f },
+    { -3.388741720e-02f, +4.849854715e-01f, +5.971710521e-01f, -4.420351435e-02f },
+    { -3.385497895e-02f, +4.846353882e-01f, +5.975190596e-01f, -4.423470622e-02f },
+    { -3.382254427e-02f, +4.842853267e-01f, +5.978670316e-01f, -4.426588586e-02f },
+    { -3.379011321e-02f, +4.839352871e-01f, +5.982149679e-01f, -4.429705321e-02f },
+    { -3.375768579e-02f, +4.835852697e-01f, +5.985628683e-01f, -4.432820823e-02f },
+    { -3.372526206e-02f, +4.832352745e-01f, +5.989107327e-01f, -4.435935084e-02f },
+    { -3.369284207e-02f, +4.828853017e-01f, +5.992585607e-01f, -4.439048099e-02f },
+    { -3.366042585e-02f, +4.825353516e-01f, +5.996063524e-01f, -4.442159863e-02f },
+    { -3.362801344e-02f, +4.821854242e-01f, +5.999541074e-01f, -4.445270370e-02f },
+    { -3.359560488e-02f, +4.818355198e-01f, +6.003018256e-01f, -4.448379613e-02f },
+    { -3.356320022e-02f, +4.814856386e-01f, +6.006495068e-01f, -4.451487588e-02f },
+    { -3.353079949e-02f, +4.811357806e-01f, +6.009971508e-01f, -4.454594288e-02f },
+    { -3.349840273e-02f, +4.807859461e-01f, +6.013447575e-01f, -4.457699708e-02f },
+    { -3.346600999e-02f, +4.804361352e-01f, +6.016923266e-01f, -4.460803842e-02f },
+    { -3.343362130e-02f, +4.800863481e-01f, +6.020398580e-01f, -4.463906683e-02f },
+    { -3.340123671e-02f, +4.797365850e-01f, +6.023873514e-01f, -4.467008228e-02f },
+    { -3.336885624e-02f, +4.793868460e-01f, +6.027348068e-01f, -4.470108468e-02f },
+    { -3.333647996e-02f, +4.790371313e-01f, +6.030822239e-01f, -4.473207400e-02f },
+    { -3.330410788e-02f, +4.786874411e-01f, +6.034296025e-01f, -4.476305016e-02f },
+    { -3.327174006e-02f, +4.783377756e-01f, +6.037769424e-01f, -4.479401311e-02f },
+    { -3.323937653e-02f, +4.779881348e-01f, +6.041242435e-01f, -4.482496280e-02f },
+    { -3.320701734e-02f, +4.776385190e-01f, +6.044715056e-01f, -4.485589916e-02f },
+    { -3.317466252e-02f, +4.772889284e-01f, +6.048187284e-01f, -4.488682214e-02f },
+    { -3.314231211e-02f, +4.769393630e-01f, +6.051659119e-01f, -4.491773168e-02f },
+    { -3.310996615e-02f, +4.765898232e-01f, +6.055130558e-01f, -4.494862772e-02f },
+    { -3.307762468e-02f, +4.762403090e-01f, +6.058601599e-01f, -4.497951020e-02f },
+    { -3.304528775e-02f, +4.758908206e-01f, +6.062072240e-01f, -4.501037906e-02f },
+    { -3.301295538e-02f, +4.755413581e-01f, +6.065542481e-01f, -4.504123425e-02f },
+    { -3.298062762e-02f, +4.751919219e-01f, +6.069012318e-01f, -4.507207570e-02f },
+    { -3.294830451e-02f, +4.748425119e-01f, +6.072481750e-01f, -4.510290336e-02f },
+    { -3.291598609e-02f, +4.744931285e-01f, +6.075950775e-01f, -4.513371718e-02f },
+    { -3.288367240e-02f, +4.741437716e-01f, +6.079419391e-01f, -4.516451708e-02f },
+    { -3.285136347e-02f, +4.737944416e-01f, +6.082887597e-01f, -4.519530301e-02f },
+    { -3.281905935e-02f, +4.734451386e-01f, +6.086355390e-01f, -4.522607492e-02f },
+    { -3.278676008e-02f, +4.730958628e-01f, +6.089822769e-01f, -4.525683274e-02f },
+    { -3.275446568e-02f, +4.727466142e-01f, +6.093289732e-01f, -4.528757642e-02f },
+    { -3.272217621e-02f, +4.723973932e-01f, +6.096756276e-01f, -4.531830590e-02f },
+    { -3.268989170e-02f, +4.720481998e-01f, +6.100222401e-01f, -4.534902111e-02f },
+    { -3.265761219e-02f, +4.716990342e-01f, +6.103688104e-01f, -4.537972200e-02f },
+    { -3.262533772e-02f, +4.713498966e-01f, +6.107153384e-01f, -4.541040851e-02f },
+    { -3.259306833e-02f, +4.710007872e-01f, +6.110618238e-01f, -4.544108058e-02f },
+    { -3.256080405e-02f, +4.706517061e-01f, +6.114082665e-01f, -4.547173816e-02f },
+    { -3.252854493e-02f, +4.703026534e-01f, +6.117546663e-01f, -4.550238117e-02f },
+    { -3.249629100e-02f, +4.699536294e-01f, +6.121010230e-01f, -4.553300957e-02f },
+    { -3.246404230e-02f, +4.696046343e-01f, +6.124473364e-01f, -4.556362330e-02f },
+    { -3.243179888e-02f, +4.692556681e-01f, +6.127936063e-01f, -4.559422228e-02f },
+    { -3.239956076e-02f, +4.689067311e-01f, +6.131398326e-01f, -4.562480648e-02f },
+    { -3.236732799e-02f, +4.685578234e-01f, +6.134860150e-01f, -4.565537582e-02f },
+    { -3.233510060e-02f, +4.682089452e-01f, +6.138321534e-01f, -4.568593024e-02f },
+    { -3.230287864e-02f, +4.678600966e-01f, +6.141782476e-01f, -4.571646969e-02f },
+    { -3.227066214e-02f, +4.675112778e-01f, +6.145242974e-01f, -4.574699411e-02f },
+    { -3.223845114e-02f, +4.671624891e-01f, +6.148703026e-01f, -4.577750344e-02f },
+    { -3.220624568e-02f, +4.668137304e-01f, +6.152162631e-01f, -4.580799762e-02f },
+    { -3.217404580e-02f, +4.664650021e-01f, +6.155621786e-01f, -4.583847658e-02f },
+    { -3.214185153e-02f, +4.661163043e-01f, +6.159080490e-01f, -4.586894027e-02f },
+    { -3.210966291e-02f, +4.657676371e-01f, +6.162538740e-01f, -4.589938863e-02f },
+    { -3.207747998e-02f, +4.654190007e-01f, +6.165996536e-01f, -4.592982160e-02f },
+    { -3.204530278e-02f, +4.650703953e-01f, +6.169453874e-01f, -4.596023912e-02f },
+    { -3.201313134e-02f, +4.647218210e-01f, +6.172910754e-01f, -4.599064113e-02f },
+    { -3.198096571e-02f, +4.643732781e-01f, +6.176367173e-01f, -4.602102757e-02f },
+    { -3.194880592e-02f, +4.640247666e-01f, +6.179823129e-01f, -4.605139837e-02f },
+    { -3.191665200e-02f, +4.636762867e-01f, +6.183278621e-01f, -4.608175349e-02f },
+    { -3.188450401e-02f, +4.633278387e-01f, +6.186733647e-01f, -4.611209285e-02f },
+    { -3.185236196e-02f, +4.629794226e-01f, +6.190188205e-01f, -4.614241641e-02f },
+    { -3.182022591e-02f, +4.626310387e-01f, +6.193642293e-01f, -4.617272409e-02f },
+    { -3.178809589e-02f, +4.622826870e-01f, +6.197095909e-01f, -4.620301584e-02f },
+    { -3.175597193e-02f, +4.619343678e-01f, +6.200549052e-01f, -4.623329159e-02f },
+    { -3.172385407e-02f, +4.615860812e-01f, +6.204001719e-01f, -4.626355130e-02f },
+    { -3.169174236e-02f, +4.612378274e-01f, +6.207453909e-01f, -4.629379489e-02f },
+    { -3.165963682e-02f, +4.608896066e-01f, +6.210905619e-01f, -4.632402231e-02f },
+    { -3.162753750e-02f, +4.605414189e-01f, +6.214356849e-01f, -4.635423350e-02f },
+    { -3.159544443e-02f, +4.601932645e-01f, +6.217807595e-01f, -4.638442839e-02f },
+    { -3.156335765e-02f, +4.598451435e-01f, +6.221257857e-01f, -4.641460692e-02f },
+    { -3.153127720e-02f, +4.594970561e-01f, +6.224707632e-01f, -4.644476904e-02f },
+    { -3.149920310e-02f, +4.591490025e-01f, +6.228156918e-01f, -4.647491468e-02f },
+    { -3.146713541e-02f, +4.588009829e-01f, +6.231605715e-01f, -4.650504379e-02f },
+    { -3.143507415e-02f, +4.584529973e-01f, +6.235054019e-01f, -4.653515630e-02f },
+    { -3.140301937e-02f, +4.581050460e-01f, +6.238501829e-01f, -4.656525215e-02f },
+    { -3.137097110e-02f, +4.577571292e-01f, +6.241949143e-01f, -4.659533127e-02f },
+    { -3.133892937e-02f, +4.574092469e-01f, +6.245395959e-01f, -4.662539362e-02f },
+    { -3.130689423e-02f, +4.570613994e-01f, +6.248842275e-01f, -4.665543913e-02f },
+    { -3.127486571e-02f, +4.567135869e-01f, +6.252288091e-01f, -4.668546773e-02f },
+    { -3.124284384e-02f, +4.563658094e-01f, +6.255733402e-01f, -4.671547937e-02f },
+    { -3.121082867e-02f, +4.560180671e-01f, +6.259178209e-01f, -4.674547399e-02f },
+    { -3.117882023e-02f, +4.556703603e-01f, +6.262622508e-01f, -4.677545152e-02f },
+    { -3.114681855e-02f, +4.553226890e-01f, +6.266066299e-01f, -4.680541190e-02f },
+    { -3.111482367e-02f, +4.549750535e-01f, +6.269509578e-01f, -4.683535507e-02f },
+    { -3.108283564e-02f, +4.546274539e-01f, +6.272952345e-01f, -4.686528098e-02f },
+    { -3.105085447e-02f, +4.542798904e-01f, +6.276394598e-01f, -4.689518955e-02f },
+    { -3.101888022e-02f, +4.539323631e-01f, +6.279836334e-01f, -4.692508073e-02f },
+    { -3.098691291e-02f, +4.535848721e-01f, +6.283277552e-01f, -4.695495446e-02f },
+    { -3.095495259e-02f, +4.532374178e-01f, +6.286718249e-01f, -4.698481067e-02f },
+    { -3.092299929e-02f, +4.528900001e-01f, +6.290158425e-01f, -4.701464930e-02f },
+    { -3.089105304e-02f, +4.525426193e-01f, +6.293598077e-01f, -4.704447029e-02f },
+    { -3.085911388e-02f, +4.521952756e-01f, +6.297037204e-01f, -4.707427359e-02f },
+    { -3.082718185e-02f, +4.518479691e-01f, +6.300475802e-01f, -4.710405912e-02f },
+    { -3.079525698e-02f, +4.515006999e-01f, +6.303913872e-01f, -4.713382682e-02f },
+    { -3.076333930e-02f, +4.511534683e-01f, +6.307351410e-01f, -4.716357665e-02f },
+    { -3.073142887e-02f, +4.508062744e-01f, +6.310788415e-01f, -4.719330852e-02f },
+    { -3.069952570e-02f, +4.504591183e-01f, +6.314224885e-01f, -4.722302238e-02f },
+    { -3.066762983e-02f, +4.501120002e-01f, +6.317660818e-01f, -4.725271818e-02f },
+    { -3.063574131e-02f, +4.497649203e-01f, +6.321096212e-01f, -4.728239584e-02f },
+    { -3.060386016e-02f, +4.494178788e-01f, +6.324531066e-01f, -4.731205530e-02f },
+    { -3.057198642e-02f, +4.490708757e-01f, +6.327965377e-01f, -4.734169651e-02f },
+    { -3.054012013e-02f, +4.487239113e-01f, +6.331399144e-01f, -4.737131940e-02f },
+    { -3.050826133e-02f, +4.483769858e-01f, +6.334832365e-01f, -4.740092391e-02f },
+    { -3.047641003e-02f, +4.480300992e-01f, +6.338265038e-01f, -4.743050997e-02f },
+    { -3.044456630e-02f, +4.476832518e-01f, +6.341697161e-01f, -4.746007752e-02f },
+    { -3.041273015e-02f, +4.473364437e-01f, +6.345128733e-01f, -4.748962651e-02f },
+    { -3.038090162e-02f, +4.469896750e-01f, +6.348559750e-01f, -4.751915687e-02f },
+    { -3.034908075e-02f, +4.466429460e-01f, +6.351990212e-01f, -4.754866853e-02f },
+    { -3.031726757e-02f, +4.462962568e-01f, +6.355420117e-01f, -4.757816144e-02f },
+    { -3.028546212e-02f, +4.459496076e-01f, +6.358849463e-01f, -4.760763553e-02f },
+    { -3.025366443e-02f, +4.456029984e-01f, +6.362278248e-01f, -4.763709074e-02f },
+    { -3.022187454e-02f, +4.452564296e-01f, +6.365706469e-01f, -4.766652701e-02f },
+    { -3.019009249e-02f, +4.449099012e-01f, +6.369134126e-01f, -4.769594427e-02f },
+    { -3.015831830e-02f, +4.445634133e-01f, +6.372561217e-01f, -4.772534246e-02f },
+    { -3.012655201e-02f, +4.442169663e-01f, +6.375987738e-01f, -4.775472152e-02f },
+    { -3.009479366e-02f, +4.438705601e-01f, +6.379413690e-01f, -4.778408139e-02f },
+    { -3.006304328e-02f, +4.435241951e-01f, +6.382839069e-01f, -4.781342200e-02f },
+    { -3.003130090e-02f, +4.431778713e-01f, +6.386263874e-01f, -4.784274329e-02f },
+    { -2.999956656e-02f, +4.428315888e-01f, +6.389688103e-01f, -4.787204520e-02f },
+    { -2.996784030e-02f, +4.424853480e-01f, +6.393111754e-01f, -4.790132766e-02f },
+    { -2.993612215e-02f, +4.421391489e-01f, +6.396534826e-01f, -4.793059062e-02f },
+    { -2.990441214e-02f, +4.417929916e-01f, +6.399957316e-01f, -4.795983400e-02f },
+    { -2.987271031e-02f, +4.414468764e-01f, +6.403379223e-01f, -4.798905775e-02f },
+    { -2.984101669e-02f, +4.411008034e-01f, +6.406800544e-01f, -4.801826180e-02f },
+    { -2.980933131e-02f, +4.407547728e-01f, +6.410221278e-01f, -4.804744609e-02f },
+    { -2.977765422e-02f, +4.404087847e-01f, +6.413641423e-01f, -4.807661056e-02f },
+    { -2.974598544e-02f, +4.400628392e-01f, +6.417060978e-01f, -4.810575513e-02f },
+    { -2.971432501e-02f, +4.397169367e-01f, +6.420479939e-01f, -4.813487976e-02f },
+    { -2.968267296e-02f, +4.393710771e-01f, +6.423898306e-01f, -4.816398438e-02f },
+    { -2.965102932e-02f, +4.390252607e-01f, +6.427316077e-01f, -4.819306891e-02f },
+    { -2.961939414e-02f, +4.386794876e-01f, +6.430733249e-01f, -4.822213331e-02f },
+    { -2.958776743e-02f, +4.383337580e-01f, +6.434149821e-01f, -4.825117750e-02f },
+    { -2.955614925e-02f, +4.379880720e-01f, +6.437565791e-01f, -4.828020142e-02f },
+    { -2.952453962e-02f, +4.376424298e-01f, +6.440981157e-01f, -4.830920501e-02f },
+    { -2.949293857e-02f, +4.372968316e-01f, +6.444395917e-01f, -4.833818821e-02f },
+    { -2.946134614e-02f, +4.369512775e-01f, +6.447810070e-01f, -4.836715094e-02f },
+    { -2.942976236e-02f, +4.366057677e-01f, +6.451223613e-01f, -4.839609316e-02f },
+    { -2.939818727e-02f, +4.362603023e-01f, +6.454636545e-01f, -4.842501479e-02f },
+    { -2.936662089e-02f, +4.359148815e-01f, +6.458048863e-01f, -4.845391577e-02f },
+    { -2.933506327e-02f, +4.355695055e-01f, +6.461460567e-01f, -4.848279603e-02f },
+    { -2.930351443e-02f, +4.352241743e-01f, +6.464871653e-01f, -4.851165552e-02f },
+    { -2.927197441e-02f, +4.348788883e-01f, +6.468282121e-01f, -4.854049416e-02f },
+    { -2.924044325e-02f, +4.345336475e-01f, +6.471691968e-01f, -4.856931190e-02f },
+    { -2.920892096e-02f, +4.341884520e-01f, +6.475101192e-01f, -4.859810867e-02f },
+    { -2.917740760e-02f, +4.338433021e-01f, +6.478509792e-01f, -4.862688440e-02f },
+    { -2.914590319e-02f, +4.334981980e-01f, +6.481917765e-01f, -4.865563904e-02f },
+    { -2.911440776e-02f, +4.331531396e-01f, +6.485325111e-01f, -4.868437251e-02f },
+    { -2.908292135e-02f, +4.328081273e-01f, +6.488731826e-01f, -4.871308476e-02f },
+    { -2.905144399e-02f, +4.324631612e-01f, +6.492137910e-01f, -4.874177572e-02f },
+    { -2.901997572e-02f, +4.321182414e-01f, +6.495543359e-01f, -4.877044532e-02f },
+    { -2.898851656e-02f, +4.317733681e-01f, +6.498948173e-01f, -4.879909350e-02f },
+    { -2.895706654e-02f, +4.314285415e-01f, +6.502352350e-01f, -4.882772020e-02f },
+    { -2.892562571e-02f, +4.310837617e-01f, +6.505755887e-01f, -4.885632535e-02f },
+    { -2.889419410e-02f, +4.307390288e-01f, +6.509158783e-01f, -4.888490889e-02f },
+    { -2.886277173e-02f, +4.303943430e-01f, +6.512561036e-01f, -4.891347075e-02f },
+    { -2.883135864e-02f, +4.300497046e-01f, +6.515962644e-01f, -4.894201087e-02f },
+    { -2.879995486e-02f, +4.297051136e-01f, +6.519363605e-01f, -4.897052918e-02f },
+    { -2.876856043e-02f, +4.293605701e-01f, +6.522763918e-01f, -4.899902562e-02f },
+    { -2.873717537e-02f, +4.290160745e-01f, +6.526163580e-01f, -4.902750012e-02f },
+    { -2.870579972e-02f, +4.286716267e-01f, +6.529562589e-01f, -4.905595262e-02f },
+    { -2.867443352e-02f, +4.283272270e-01f, +6.532960945e-01f, -4.908438306e-02f },
+    { -2.864307678e-02f, +4.279828756e-01f, +6.536358644e-01f, -4.911279137e-02f },
+    { -2.861172956e-02f, +4.276385725e-01f, +6.539755685e-01f, -4.914117748e-02f },
+    { -2.858039187e-02f, +4.272943179e-01f, +6.543152066e-01f, -4.916954133e-02f },
+    { -2.854906375e-02f, +4.269501121e-01f, +6.546547786e-01f, -4.919788286e-02f },
+    { -2.851774523e-02f, +4.266059551e-01f, +6.549942842e-01f, -4.922620200e-02f },
+    { -2.848643635e-02f, +4.262618471e-01f, +6.553337232e-01f, -4.925449868e-02f },
+    { -2.845513713e-02f, +4.259177883e-01f, +6.556730955e-01f, -4.928277284e-02f },
+    { -2.842384761e-02f, +4.255737788e-01f, +6.560124009e-01f, -4.931102441e-02f },
+    { -2.839256782e-02f, +4.252298187e-01f, +6.563516392e-01f, -4.933925334e-02f },
+    { -2.836129779e-02f, +4.248859083e-01f, +6.566908102e-01f, -4.936745955e-02f },
+    { -2.833003756e-02f, +4.245420477e-01f, +6.570299137e-01f, -4.939564297e-02f },
+    { -2.829878715e-02f, +4.241982371e-01f, +6.573689495e-01f, -4.942380356e-02f },
+    { -2.826754660e-02f, +4.238544765e-01f, +6.577079175e-01f, -4.945194122e-02f },
+    { -2.823631593e-02f, +4.235107662e-01f, +6.580468175e-01f, -4.948005591e-02f },
+    { -2.820509519e-02f, +4.231671063e-01f, +6.583856492e-01f, -4.950814756e-02f },
+    { -2.817388439e-02f, +4.228234970e-01f, +6.587244125e-01f, -4.953621610e-02f },
+    { -2.814268358e-02f, +4.224799384e-01f, +6.590631072e-01f, -4.956426147e-02f },
+    { -2.811149279e-02f, +4.221364307e-01f, +6.594017331e-01f, -4.959228359e-02f },
+    { -2.808031203e-02f, +4.217929740e-01f, +6.597402901e-01f, -4.962028241e-02f },
+    { -2.804914136e-02f, +4.214495685e-01f, +6.600787779e-01f, -4.964825786e-02f },
+    { -2.801798079e-02f, +4.211062143e-01f, +6.604171963e-01f, -4.967620987e-02f },
+    { -2.798683036e-02f, +4.207629117e-01f, +6.607555453e-01f, -4.970413838e-02f },
+    { -2.795569010e-02f, +4.204196607e-01f, +6.610938245e-01f, -4.973204333e-02f },
+    { -2.792456005e-02f, +4.200764615e-01f, +6.614320337e-01f, -4.975992463e-02f },
+    { -2.789344022e-02f, +4.197333143e-01f, +6.617701729e-01f, -4.978778224e-02f },
+    { -2.786233066e-02f, +4.193902192e-01f, +6.621082419e-01f, -4.981561608e-02f },
+    { -2.783123139e-02f, +4.190471763e-01f, +6.624462403e-01f, -4.984342609e-02f },
+    { -2.780014245e-02f, +4.187041859e-01f, +6.627841681e-01f, -4.987121220e-02f },
+    { -2.776906386e-02f, +4.183612481e-01f, +6.631220251e-01f, -4.989897435e-02f },
+    { -2.773799566e-02f, +4.180183630e-01f, +6.634598110e-01f, -4.992671247e-02f },
+    { -2.770693788e-02f, +4.176755308e-01f, +6.637975258e-01f, -4.995442649e-02f },
+    { -2.767589054e-02f, +4.173327517e-01f, +6.641351691e-01f, -4.998211634e-02f },
+    { -2.764485368e-02f, +4.169900257e-01f, +6.644727409e-01f, -5.000978197e-02f },
+    { -2.761382734e-02f, +4.166473531e-01f, +6.648102408e-01f, -5.003742330e-02f },
+    { -2.758281153e-02f, +4.163047340e-01f, +6.651476689e-01f, -5.006504027e-02f },
+    { -2.755180629e-02f, +4.159621686e-01f, +6.654850247e-01f, -5.009263282e-02f },
+    { -2.752081165e-02f, +4.156196570e-01f, +6.658223083e-01f, -5.012020087e-02f },
+    { -2.748982764e-02f, +4.152771994e-01f, +6.661595193e-01f, -5.014774435e-02f },
+    { -2.745885430e-02f, +4.149347959e-01f, +6.664966577e-01f, -5.017526321e-02f },
+    { -2.742789164e-02f, +4.145924466e-01f, +6.668337231e-01f, -5.020275738e-02f },
+    { -2.739693971e-02f, +4.142501518e-01f, +6.671707155e-01f, -5.023022678e-02f },
+    { -2.736599852e-02f, +4.139079116e-01f, +6.675076346e-01f, -5.025767136e-02f },
+    { -2.733506812e-02f, +4.135657261e-01f, +6.678444802e-01f, -5.028509105e-02f },
+    { -2.730414853e-02f, +4.132235955e-01f, +6.681812522e-01f, -5.031248577e-02f },
+    { -2.727323979e-02f, +4.128815199e-01f, +6.685179504e-01f, -5.033985546e-02f },
+    { -2.724234191e-02f, +4.125394995e-01f, +6.688545746e-01f, -5.036720007e-02f },
+    { -2.721145493e-02f, +4.121975345e-01f, +6.691911246e-01f, -5.039451951e-02f },
+    { -2.718057889e-02f, +4.118556250e-01f, +6.695276002e-01f, -5.042181372e-02f },
+    { -2.714971381e-02f, +4.115137711e-01f, +6.698640012e-01f, -5.044908263e-02f },
+    { -2.711885972e-02f, +4.111719731e-01f, +6.702003275e-01f, -5.047632619e-02f },
+    { -2.708801665e-02f, +4.108302310e-01f, +6.705365788e-01f, -5.050354431e-02f },
+    { -2.705718463e-02f, +4.104885450e-01f, +6.708727551e-01f, -5.053073694e-02f },
+    { -2.702636369e-02f, +4.101469153e-01f, +6.712088560e-01f, -5.055790401e-02f },
+    { -2.699555385e-02f, +4.098053420e-01f, +6.715448814e-01f, -5.058504544e-02f },
+    { -2.696475516e-02f, +4.094638252e-01f, +6.718808311e-01f, -5.061216118e-02f },
+    { -2.693396764e-02f, +4.091223652e-01f, +6.722167049e-01f, -5.063925115e-02f },
+    { -2.690319131e-02f, +4.087809621e-01f, +6.725525027e-01f, -5.066631529e-02f },
+    { -2.687242621e-02f, +4.084396159e-01f, +6.728882242e-01f, -5.069335353e-02f },
+    { -2.684167237e-02f, +4.080983270e-01f, +6.732238693e-01f, -5.072036581e-02f },
+    { -2.681092981e-02f, +4.077570954e-01f, +6.735594378e-01f, -5.074735205e-02f },
+    { -2.678019856e-02f, +4.074159212e-01f, +6.738949295e-01f, -5.077431218e-02f },
+    { -2.674947866e-02f, +4.070748047e-01f, +6.742303442e-01f, -5.080124615e-02f },
+    { -2.671877013e-02f, +4.067337460e-01f, +6.745656817e-01f, -5.082815388e-02f },
+    { -2.668807301e-02f, +4.063927452e-01f, +6.749009418e-01f, -5.085503531e-02f },
+    { -2.665738731e-02f, +4.060518025e-01f, +6.752361244e-01f, -5.088189036e-02f },
+    { -2.662671308e-02f, +4.057109180e-01f, +6.755712292e-01f, -5.090871897e-02f },
+    { -2.659605033e-02f, +4.053700919e-01f, +6.759062562e-01f, -5.093552108e-02f },
+    { -2.656539910e-02f, +4.050293243e-01f, +6.762412050e-01f, -5.096229661e-02f },
+    { -2.653475941e-02f, +4.046886155e-01f, +6.765760755e-01f, -5.098904550e-02f },
+    { -2.650413130e-02f, +4.043479654e-01f, +6.769108675e-01f, -5.101576767e-02f },
+    { -2.647351480e-02f, +4.040073744e-01f, +6.772455809e-01f, -5.104246307e-02f },
+    { -2.644290992e-02f, +4.036668425e-01f, +6.775802154e-01f, -5.106913162e-02f },
+    { -2.641231671e-02f, +4.033263699e-01f, +6.779147709e-01f, -5.109577326e-02f },
+    { -2.638173519e-02f, +4.029859567e-01f, +6.782492471e-01f, -5.112238791e-02f },
+    { -2.635116538e-02f, +4.026456031e-01f, +6.785836440e-01f, -5.114897551e-02f },
+    { -2.632060732e-02f, +4.023053093e-01f, +6.789179612e-01f, -5.117553600e-02f },
+    { -2.629006103e-02f, +4.019650754e-01f, +6.792521986e-01f, -5.120206929e-02f },
+    { -2.625952655e-02f, +4.016249015e-01f, +6.795863561e-01f, -5.122857533e-02f },
+    { -2.622900390e-02f, +4.012847878e-01f, +6.799204334e-01f, -5.125505405e-02f },
+    { -2.619849310e-02f, +4.009447344e-01f, +6.802544304e-01f, -5.128150537e-02f },
+    { -2.616799420e-02f, +4.006047415e-01f, +6.805883468e-01f, -5.130792924e-02f },
+    { -2.613750720e-02f, +4.002648093e-01f, +6.809221825e-01f, -5.133432558e-02f },
+    { -2.610703216e-02f, +3.999249379e-01f, +6.812559373e-01f, -5.136069431e-02f },
+    { -2.607656908e-02f, +3.995851274e-01f, +6.815896110e-01f, -5.138703539e-02f },
+    { -2.604611800e-02f, +3.992453779e-01f, +6.819232035e-01f, -5.141334873e-02f },
+    { -2.601567895e-02f, +3.989056898e-01f, +6.822567144e-01f, -5.143963427e-02f },
+    { -2.598525196e-02f, +3.985660630e-01f, +6.825901438e-01f, -5.146589193e-02f },
+    { -2.595483705e-02f, +3.982264977e-01f, +6.829234913e-01f, -5.149212166e-02f },
+    { -2.592443425e-02f, +3.978869942e-01f, +6.832567567e-01f, -5.151832338e-02f },
+    { -2.589404358e-02f, +3.975475525e-01f, +6.835899400e-01f, -5.154449702e-02f },
+    { -2.586366508e-02f, +3.972081727e-01f, +6.839230409e-01f, -5.157064252e-02f },
+    { -2.583329878e-02f, +3.968688551e-01f, +6.842560591e-01f, -5.159675980e-02f },
+    { -2.580294470e-02f, +3.965295998e-01f, +6.845889947e-01f, -5.162284880e-02f },
+    { -2.577260287e-02f, +3.961904069e-01f, +6.849218472e-01f, -5.164890945e-02f },
+    { -2.574227331e-02f, +3.958512766e-01f, +6.852546167e-01f, -5.167494167e-02f },
+    { -2.571195605e-02f, +3.955122090e-01f, +6.855873028e-01f, -5.170094541e-02f },
+    { -2.568165113e-02f, +3.951732042e-01f, +6.859199054e-01f, -5.172692059e-02f },
+    { -2.565135856e-02f, +3.948342626e-01f, +6.862524243e-01f, -5.175286714e-02f },
+    { -2.562107838e-02f, +3.944953840e-01f, +6.865848593e-01f, -5.177878499e-02f },
+    { -2.559081062e-02f, +3.941565688e-01f, +6.869172102e-01f, -5.180467408e-02f },
+    { -2.556055529e-02f, +3.938178171e-01f, +6.872494769e-01f, -5.183053433e-02f },
+    { -2.553031243e-02f, +3.934791290e-01f, +6.875816592e-01f, -5.185636568e-02f },
+    { -2.550008206e-02f, +3.931405046e-01f, +6.879137568e-01f, -5.188216805e-02f },
+    { -2.546986421e-02f, +3.928019442e-01f, +6.882457697e-01f, -5.190794138e-02f },
+    { -2.543965891e-02f, +3.924634478e-01f, +6.885776975e-01f, -5.193368560e-02f },
+    { -2.540946619e-02f, +3.921250156e-01f, +6.889095401e-01f, -5.195940064e-02f },
+    { -2.537928607e-02f, +3.917866477e-01f, +6.892412974e-01f, -5.198508643e-02f },
+    { -2.534911857e-02f, +3.914483444e-01f, +6.895729691e-01f, -5.201074290e-02f },
+    { -2.531896373e-02f, +3.911101057e-01f, +6.899045550e-01f, -5.203636997e-02f },
+    { -2.528882157e-02f, +3.907719318e-01f, +6.902360551e-01f, -5.206196759e-02f },
+    { -2.525869212e-02f, +3.904338229e-01f, +6.905674690e-01f, -5.208753568e-02f },
+    { -2.522857541e-02f, +3.900957790e-01f, +6.908987966e-01f, -5.211307417e-02f },
+    { -2.519847146e-02f, +3.897578004e-01f, +6.912300378e-01f, -5.213858300e-02f },
+    { -2.516838029e-02f, +3.894198871e-01f, +6.915611922e-01f, -5.216406208e-02f },
+    { -2.513830194e-02f, +3.890820394e-01f, +6.918922599e-01f, -5.218951136e-02f },
+    { -2.510823643e-02f, +3.887442574e-01f, +6.922232404e-01f, -5.221493076e-02f },
+    { -2.507818379e-02f, +3.884065411e-01f, +6.925541338e-01f, -5.224032022e-02f },
+    { -2.504814403e-02f, +3.880688909e-01f, +6.928849397e-01f, -5.226567965e-02f },
+    { -2.501811720e-02f, +3.877313067e-01f, +6.932156580e-01f, -5.229100900e-02f },
+    { -2.498810332e-02f, +3.873937888e-01f, +6.935462886e-01f, -5.231630820e-02f },
+    { -2.495810241e-02f, +3.870563373e-01f, +6.938768311e-01f, -5.234157717e-02f },
+    { -2.492811449e-02f, +3.867189524e-01f, +6.942072855e-01f, -5.236681584e-02f },
+    { -2.489813960e-02f, +3.863816342e-01f, +6.945376516e-01f, -5.239202415e-02f },
+    { -2.486817776e-02f, +3.860443828e-01f, +6.948679292e-01f, -5.241720201e-02f },
+    { -2.483822900e-02f, +3.857071983e-01f, +6.951981180e-01f, -5.244234938e-02f },
+    { -2.480829333e-02f, +3.853700811e-01f, +6.955282179e-01f, -5.246746616e-02f },
+    { -2.477837080e-02f, +3.850330311e-01f, +6.958582288e-01f, -5.249255230e-02f },
+    { -2.474846141e-02f, +3.846960485e-01f, +6.961881503e-01f, -5.251760772e-02f },
+    { -2.471856521e-02f, +3.843591335e-01f, +6.965179824e-01f, -5.254263236e-02f },
+    { -2.468868221e-02f, +3.840222862e-01f, +6.968477249e-01f, -5.256762613e-02f },
+    { -2.465881245e-02f, +3.836855068e-01f, +6.971773775e-01f, -5.259258898e-02f },
+    { -2.462895594e-02f, +3.833487953e-01f, +6.975069402e-01f, -5.261752083e-02f },
+    { -2.459911271e-02f, +3.830121520e-01f, +6.978364126e-01f, -5.264242161e-02f },
+    { -2.456928278e-02f, +3.826755771e-01f, +6.981657947e-01f, -5.266729126e-02f },
+    { -2.453946619e-02f, +3.823390705e-01f, +6.984950861e-01f, -5.269212969e-02f },
+    { -2.450966296e-02f, +3.820026325e-01f, +6.988242869e-01f, -5.271693684e-02f },
+    { -2.447987311e-02f, +3.816662633e-01f, +6.991533966e-01f, -5.274171264e-02f },
+    { -2.445009667e-02f, +3.813299629e-01f, +6.994824153e-01f, -5.276645702e-02f },
+    { -2.442033366e-02f, +3.809937316e-01f, +6.998113426e-01f, -5.279116991e-02f },
+    { -2.439058412e-02f, +3.806575694e-01f, +7.001401785e-01f, -5.281585123e-02f },
+    { -2.436084805e-02f, +3.803214765e-01f, +7.004689226e-01f, -5.284050092e-02f },
+    { -2.433112550e-02f, +3.799854530e-01f, +7.007975749e-01f, -5.286511890e-02f },
+    { -2.430141648e-02f, +3.796494992e-01f, +7.011261352e-01f, -5.288970511e-02f },
+    { -2.427172102e-02f, +3.793136151e-01f, +7.014546032e-01f, -5.291425948e-02f },
+    { -2.424203914e-02f, +3.789778008e-01f, +7.017829788e-01f, -5.293878193e-02f },
+    { -2.421237087e-02f, +3.786420566e-01f, +7.021112617e-01f, -5.296327238e-02f },
+    { -2.418271624e-02f, +3.783063825e-01f, +7.024394519e-01f, -5.298773078e-02f },
+    { -2.415307527e-02f, +3.779707788e-01f, +7.027675491e-01f, -5.301215705e-02f },
+    { -2.412344797e-02f, +3.776352455e-01f, +7.030955531e-01f, -5.303655112e-02f },
+    { -2.409383439e-02f, +3.772997828e-01f, +7.034234638e-01f, -5.306091292e-02f },
+    { -2.406423454e-02f, +3.769643909e-01f, +7.037512810e-01f, -5.308524237e-02f },
+    { -2.403464845e-02f, +3.766290698e-01f, +7.040790044e-01f, -5.310953941e-02f },
+    { -2.400507614e-02f, +3.762938197e-01f, +7.044066339e-01f, -5.313380397e-02f },
+    { -2.397551763e-02f, +3.759586408e-01f, +7.047341694e-01f, -5.315803596e-02f },
+    { -2.394597296e-02f, +3.756235333e-01f, +7.050616105e-01f, -5.318223533e-02f },
+    { -2.391644214e-02f, +3.752884971e-01f, +7.053889572e-01f, -5.320640200e-02f },
+    { -2.388692520e-02f, +3.749535326e-01f, +7.057162093e-01f, -5.323053590e-02f },
+    { -2.385742216e-02f, +3.746186398e-01f, +7.060433665e-01f, -5.325463695e-02f },
+    { -2.382793305e-02f, +3.742838189e-01f, +7.063704287e-01f, -5.327870509e-02f },
+    { -2.379845790e-02f, +3.739490700e-01f, +7.066973957e-01f, -5.330274025e-02f },
+    { -2.376899672e-02f, +3.736143933e-01f, +7.070242673e-01f, -5.332674235e-02f },
+    { -2.373954954e-02f, +3.732797888e-01f, +7.073510434e-01f, -5.335071132e-02f },
+    { -2.371011639e-02f, +3.729452569e-01f, +7.076777237e-01f, -5.337464709e-02f },
+    { -2.368069728e-02f, +3.726107975e-01f, +7.080043080e-01f, -5.339854959e-02f },
+    { -2.365129225e-02f, +3.722764108e-01f, +7.083307963e-01f, -5.342241875e-02f },
+    { -2.362190131e-02f, +3.719420970e-01f, +7.086571882e-01f, -5.344625449e-02f },
+    { -2.359252450e-02f, +3.716078562e-01f, +7.089834836e-01f, -5.347005675e-02f },
+    { -2.356316182e-02f, +3.712736886e-01f, +7.093096823e-01f, -5.349382545e-02f },
+    { -2.353381332e-02f, +3.709395943e-01f, +7.096357842e-01f, -5.351756051e-02f },
+    { -2.350447901e-02f, +3.706055734e-01f, +7.099617891e-01f, -5.354126188e-02f },
+    { -2.347515891e-02f, +3.702716261e-01f, +7.102876966e-01f, -5.356492947e-02f },
+    { -2.344585305e-02f, +3.699377525e-01f, +7.106135068e-01f, -5.358856321e-02f },
+    { -2.341656145e-02f, +3.696039527e-01f, +7.109392194e-01f, -5.361216304e-02f },
+    { -2.338728414e-02f, +3.692702270e-01f, +7.112648342e-01f, -5.363572888e-02f },
+    { -2.335802114e-02f, +3.689365754e-01f, +7.115903510e-01f, -5.365926065e-02f },
+    { -2.332877247e-02f, +3.686029981e-01f, +7.119157697e-01f, -5.368275829e-02f },
+    { -2.329953816e-02f, +3.682694952e-01f, +7.122410900e-01f, -5.370622173e-02f },
+    { -2.327031823e-02f, +3.679360669e-01f, +7.125663118e-01f, -5.372965088e-02f },
+    { -2.324111270e-02f, +3.676027132e-01f, +7.128914348e-01f, -5.375304569e-02f },
+    { -2.321192159e-02f, +3.672694344e-01f, +7.132164590e-01f, -5.377640607e-02f },
+    { -2.318274493e-02f, +3.669362306e-01f, +7.135413841e-01f, -5.379973196e-02f },
+    { -2.315358275e-02f, +3.666031020e-01f, +7.138662100e-01f, -5.382302328e-02f },
+    { -2.312443506e-02f, +3.662700485e-01f, +7.141909364e-01f, -5.384627996e-02f },
+    { -2.309530189e-02f, +3.659370705e-01f, +7.145155631e-01f, -5.386950193e-02f },
+    { -2.306618326e-02f, +3.656041680e-01f, +7.148400900e-01f, -5.389268911e-02f },
+    { -2.303707919e-02f, +3.652713412e-01f, +7.151645170e-01f, -5.391584144e-02f },
+    { -2.300798972e-02f, +3.649385903e-01f, +7.154888437e-01f, -5.393895884e-02f },
+    { -2.297891485e-02f, +3.646059152e-01f, +7.158130701e-01f, -5.396204124e-02f },
+    { -2.294985461e-02f, +3.642733163e-01f, +7.161371959e-01f, -5.398508856e-02f },
+    { -2.292080903e-02f, +3.639407936e-01f, +7.164612210e-01f, -5.400810073e-02f },
+    { -2.289177812e-02f, +3.636083473e-01f, +7.167851452e-01f, -5.403107768e-02f },
+    { -2.286276192e-02f, +3.632759775e-01f, +7.171089682e-01f, -5.405401935e-02f },
+    { -2.283376044e-02f, +3.629436844e-01f, +7.174326900e-01f, -5.407692564e-02f },
+    { -2.280477371e-02f, +3.626114681e-01f, +7.177563103e-01f, -5.409979650e-02f },
+    { -2.277580174e-02f, +3.622793287e-01f, +7.180798289e-01f, -5.412263185e-02f },
+    { -2.274684456e-02f, +3.619472663e-01f, +7.184032457e-01f, -5.414543161e-02f },
+    { -2.271790220e-02f, +3.616152812e-01f, +7.187265605e-01f, -5.416819572e-02f },
+    { -2.268897467e-02f, +3.612833734e-01f, +7.190497730e-01f, -5.419092410e-02f },
+    { -2.266006200e-02f, +3.609515431e-01f, +7.193728831e-01f, -5.421361667e-02f },
+    { -2.263116421e-02f, +3.606197904e-01f, +7.196958907e-01f, -5.423627337e-02f },
+    { -2.260228132e-02f, +3.602881155e-01f, +7.200187955e-01f, -5.425889412e-02f },
+    { -2.257341336e-02f, +3.599565185e-01f, +7.203415974e-01f, -5.428147885e-02f },
+    { -2.254456034e-02f, +3.596249995e-01f, +7.206642961e-01f, -5.430402749e-02f },
+    { -2.251572229e-02f, +3.592935587e-01f, +7.209868915e-01f, -5.432653996e-02f },
+    { -2.248689923e-02f, +3.589621962e-01f, +7.213093834e-01f, -5.434901618e-02f },
+    { -2.245809118e-02f, +3.586309121e-01f, +7.216317716e-01f, -5.437145610e-02f },
+    { -2.242929816e-02f, +3.582997067e-01f, +7.219540560e-01f, -5.439385962e-02f },
+    { -2.240052021e-02f, +3.579685799e-01f, +7.222762363e-01f, -5.441622669e-02f },
+    { -2.237175733e-02f, +3.576375321e-01f, +7.225983123e-01f, -5.443855722e-02f },
+    { -2.234300954e-02f, +3.573065632e-01f, +7.229202840e-01f, -5.446085115e-02f },
+    { -2.231427688e-02f, +3.569756734e-01f, +7.232421510e-01f, -5.448310839e-02f },
+    { -2.228555936e-02f, +3.566448630e-01f, +7.235639133e-01f, -5.450532888e-02f },
+    { -2.225685701e-02f, +3.563141319e-01f, +7.238855706e-01f, -5.452751254e-02f },
+    { -2.222816984e-02f, +3.559834804e-01f, +7.242071227e-01f, -5.454965931e-02f },
+    { -2.219949788e-02f, +3.556529085e-01f, +7.245285695e-01f, -5.457176909e-02f },
+    { -2.217084115e-02f, +3.553224165e-01f, +7.248499108e-01f, -5.459384183e-02f },
+    { -2.214219967e-02f, +3.549920045e-01f, +7.251711464e-01f, -5.461587745e-02f },
+    { -2.211357346e-02f, +3.546616725e-01f, +7.254922761e-01f, -5.463787587e-02f },
+    { -2.208496254e-02f, +3.543314207e-01f, +7.258132997e-01f, -5.465983703e-02f },
+    { -2.205636694e-02f, +3.540012493e-01f, +7.261342170e-01f, -5.468176084e-02f },
+    { -2.202778667e-02f, +3.536711584e-01f, +7.264550280e-01f, -5.470364724e-02f },
+    { -2.199922176e-02f, +3.533411482e-01f, +7.267757323e-01f, -5.472549614e-02f },
+    { -2.197067223e-02f, +3.530112187e-01f, +7.270963298e-01f, -5.474730748e-02f },
+    { -2.194213809e-02f, +3.526813701e-01f, +7.274168203e-01f, -5.476908119e-02f },
+    { -2.191361938e-02f, +3.523516026e-01f, +7.277372036e-01f, -5.479081718e-02f },
+    { -2.188511610e-02f, +3.520219162e-01f, +7.280574796e-01f, -5.481251539e-02f },
+    { -2.185662829e-02f, +3.516923111e-01f, +7.283776481e-01f, -5.483417574e-02f },
+    { -2.182815596e-02f, +3.513627875e-01f, +7.286977088e-01f, -5.485579815e-02f },
+    { -2.179969913e-02f, +3.510333455e-01f, +7.290176616e-01f, -5.487738256e-02f },
+    { -2.177125783e-02f, +3.507039852e-01f, +7.293375064e-01f, -5.489892888e-02f },
+    { -2.174283207e-02f, +3.503747067e-01f, +7.296572429e-01f, -5.492043705e-02f },
+    { -2.171442188e-02f, +3.500455102e-01f, +7.299768709e-01f, -5.494190699e-02f },
+    { -2.168602727e-02f, +3.497163959e-01f, +7.302963903e-01f, -5.496333863e-02f },
+    { -2.165764827e-02f, +3.493873638e-01f, +7.306158008e-01f, -5.498473189e-02f },
+    { -2.162928490e-02f, +3.490584141e-01f, +7.309351024e-01f, -5.500608670e-02f },
+    { -2.160093718e-02f, +3.487295469e-01f, +7.312542948e-01f, -5.502740298e-02f },
+    { -2.157260512e-02f, +3.484007623e-01f, +7.315733778e-01f, -5.504868065e-02f },
+    { -2.154428875e-02f, +3.480720606e-01f, +7.318923513e-01f, -5.506991966e-02f },
+    { -2.151598810e-02f, +3.477434418e-01f, +7.322112150e-01f, -5.509111991e-02f },
+    { -2.148770317e-02f, +3.474149060e-01f, +7.325299688e-01f, -5.511228134e-02f },
+    { -2.145943399e-02f, +3.470864534e-01f, +7.328486125e-01f, -5.513340388e-02f },
+    { -2.143118058e-02f, +3.467580842e-01f, +7.331671459e-01f, -5.515448744e-02f },
+    { -2.140294296e-02f, +3.464297984e-01f, +7.334855689e-01f, -5.517553195e-02f },
+    { -2.137472115e-02f, +3.461015962e-01f, +7.338038812e-01f, -5.519653734e-02f },
+    { -2.134651517e-02f, +3.457734778e-01f, +7.341220827e-01f, -5.521750353e-02f },
+    { -2.131832504e-02f, +3.454454432e-01f, +7.344401732e-01f, -5.523843046e-02f },
+    { -2.129015078e-02f, +3.451174926e-01f, +7.347581525e-01f, -5.525931803e-02f },
+    { -2.126199241e-02f, +3.447896261e-01f, +7.350760204e-01f, -5.528016619e-02f },
+    { -2.123384995e-02f, +3.444618439e-01f, +7.353937768e-01f, -5.530097485e-02f },
+    { -2.120572342e-02f, +3.441341461e-01f, +7.357114214e-01f, -5.532174395e-02f },
+    { -2.117761283e-02f, +3.438065327e-01f, +7.360289541e-01f, -5.534247339e-02f },
+    { -2.114951822e-02f, +3.434790041e-01f, +7.363463747e-01f, -5.536316312e-02f },
+    { -2.112143960e-02f, +3.431515602e-01f, +7.366636830e-01f, -5.538381306e-02f },
+    { -2.109337699e-02f, +3.428242013e-01f, +7.369808788e-01f, -5.540442312e-02f },
+    { -2.106533040e-02f, +3.424969274e-01f, +7.372979620e-01f, -5.542499324e-02f },
+    { -2.103729986e-02f, +3.421697387e-01f, +7.376149323e-01f, -5.544552335e-02f },
+    { -2.100928539e-02f, +3.418426354e-01f, +7.379317896e-01f, -5.546601336e-02f },
+    { -2.098128700e-02f, +3.415156174e-01f, +7.382485338e-01f, -5.548646320e-02f },
+    { -2.095330472e-02f, +3.411886851e-01f, +7.385651645e-01f, -5.550687279e-02f },
+    { -2.092533857e-02f, +3.408618385e-01f, +7.388816817e-01f, -5.552724207e-02f },
+    { -2.089738856e-02f, +3.405350777e-01f, +7.391980851e-01f, -5.554757095e-02f },
+    { -2.086945472e-02f, +3.402084029e-01f, +7.395143746e-01f, -5.556785936e-02f },
+    { -2.084153705e-02f, +3.398818142e-01f, +7.398305500e-01f, -5.558810723e-02f },
+    { -2.081363559e-02f, +3.395553117e-01f, +7.401466111e-01f, -5.560831448e-02f },
+    { -2.078575035e-02f, +3.392288956e-01f, +7.404625577e-01f, -5.562848104e-02f },
+    { -2.075788136e-02f, +3.389025660e-01f, +7.407783897e-01f, -5.564860682e-02f },
+    { -2.073002862e-02f, +3.385763231e-01f, +7.410941069e-01f, -5.566869176e-02f },
+    { -2.070219216e-02f, +3.382501669e-01f, +7.414097090e-01f, -5.568873578e-02f },
+    { -2.067437199e-02f, +3.379240976e-01f, +7.417251959e-01f, -5.570873880e-02f },
+    { -2.064656814e-02f, +3.375981153e-01f, +7.420405674e-01f, -5.572870075e-02f },
+    { -2.061878063e-02f, +3.372722202e-01f, +7.423558234e-01f, -5.574862155e-02f },
+    { -2.059100947e-02f, +3.369464124e-01f, +7.426709636e-01f, -5.576850113e-02f },
+    { -2.056325469e-02f, +3.366206920e-01f, +7.429859879e-01f, -5.578833941e-02f },
+    { -2.053551629e-02f, +3.362950591e-01f, +7.433008961e-01f, -5.580813632e-02f },
+    { -2.050779431e-02f, +3.359695139e-01f, +7.436156880e-01f, -5.582789178e-02f },
+    { -2.048008875e-02f, +3.356440565e-01f, +7.439303635e-01f, -5.584760571e-02f },
+    { -2.045239964e-02f, +3.353186871e-01f, +7.442449222e-01f, -5.586727805e-02f },
+    { -2.042472700e-02f, +3.349934057e-01f, +7.445593642e-01f, -5.588690871e-02f },
+    { -2.039707084e-02f, +3.346682125e-01f, +7.448736891e-01f, -5.590649761e-02f },
+    { -2.036943118e-02f, +3.343431076e-01f, +7.451878969e-01f, -5.592604469e-02f },
+    { -2.034180804e-02f, +3.340180912e-01f, +7.455019872e-01f, -5.594554986e-02f },
+    { -2.031420145e-02f, +3.336931634e-01f, +7.458159600e-01f, -5.596501306e-02f },
+    { -2.028661141e-02f, +3.333683243e-01f, +7.461298151e-01f, -5.598443420e-02f },
+    { -2.025903794e-02f, +3.330435740e-01f, +7.464435522e-01f, -5.600381321e-02f },
+    { -2.023148107e-02f, +3.327189127e-01f, +7.467571712e-01f, -5.602315001e-02f },
+    { -2.020394081e-02f, +3.323943405e-01f, +7.470706720e-01f, -5.604244453e-02f },
+    { -2.017641718e-02f, +3.320698576e-01f, +7.473840543e-01f, -5.606169669e-02f },
+    { -2.014891020e-02f, +3.317454639e-01f, +7.476973179e-01f, -5.608090642e-02f },
+    { -2.012141988e-02f, +3.314211598e-01f, +7.480104627e-01f, -5.610007364e-02f },
+    { -2.009394625e-02f, +3.310969453e-01f, +7.483234885e-01f, -5.611919827e-02f },
+    { -2.006648932e-02f, +3.307728205e-01f, +7.486363951e-01f, -5.613828023e-02f },
+    { -2.003904911e-02f, +3.304487856e-01f, +7.489491824e-01f, -5.615731946e-02f },
+    { -2.001162564e-02f, +3.301248407e-01f, +7.492618501e-01f, -5.617631587e-02f },
+    { -1.998421892e-02f, +3.298009860e-01f, +7.495743981e-01f, -5.619526939e-02f },
+    { -1.995682897e-02f, +3.294772215e-01f, +7.498868261e-01f, -5.621417995e-02f },
+    { -1.992945582e-02f, +3.291535473e-01f, +7.501991341e-01f, -5.623304746e-02f },
+    { -1.990209947e-02f, +3.288299637e-01f, +7.505113218e-01f, -5.625187185e-02f },
+    { -1.987475995e-02f, +3.285064707e-01f, +7.508233890e-01f, -5.627065304e-02f },
+    { -1.984743727e-02f, +3.281830685e-01f, +7.511353356e-01f, -5.628939096e-02f },
+    { -1.982013145e-02f, +3.278597572e-01f, +7.514471614e-01f, -5.630808553e-02f },
+    { -1.979284251e-02f, +3.275365369e-01f, +7.517588662e-01f, -5.632673668e-02f },
+    { -1.976557047e-02f, +3.272134077e-01f, +7.520704498e-01f, -5.634534432e-02f },
+    { -1.973831534e-02f, +3.268903698e-01f, +7.523819121e-01f, -5.636390839e-02f },
+    { -1.971107714e-02f, +3.265674233e-01f, +7.526932528e-01f, -5.638242880e-02f },
+    { -1.968385589e-02f, +3.262445684e-01f, +7.530044718e-01f, -5.640090548e-02f },
+    { -1.965665160e-02f, +3.259218051e-01f, +7.533155690e-01f, -5.641933835e-02f },
+    { -1.962946429e-02f, +3.255991335e-01f, +7.536265440e-01f, -5.643772734e-02f },
+    { -1.960229398e-02f, +3.252765539e-01f, +7.539373968e-01f, -5.645607236e-02f },
+    { -1.957514069e-02f, +3.249540663e-01f, +7.542481271e-01f, -5.647437335e-02f },
+    { -1.954800443e-02f, +3.246316709e-01f, +7.545587349e-01f, -5.649263022e-02f },
+    { -1.952088522e-02f, +3.243093678e-01f, +7.548692198e-01f, -5.651084290e-02f },
+    { -1.949378307e-02f, +3.239871571e-01f, +7.551795818e-01f, -5.652901131e-02f },
+    { -1.946669801e-02f, +3.236650389e-01f, +7.554898206e-01f, -5.654713538e-02f },
+    { -1.943963005e-02f, +3.233430133e-01f, +7.557999361e-01f, -5.656521503e-02f },
+    { -1.941257921e-02f, +3.230210806e-01f, +7.561099280e-01f, -5.658325018e-02f },
+    { -1.938554550e-02f, +3.226992408e-01f, +7.564197963e-01f, -5.660124075e-02f },
+    { -1.935852895e-02f, +3.223774940e-01f, +7.567295407e-01f, -5.661918667e-02f },
+    { -1.933152956e-02f, +3.220558404e-01f, +7.570391610e-01f, -5.663708786e-02f },
+    { -1.930454735e-02f, +3.217342801e-01f, +7.573486571e-01f, -5.665494425e-02f },
+    { -1.927758234e-02f, +3.214128132e-01f, +7.576580288e-01f, -5.667275575e-02f },
+    { -1.925063455e-02f, +3.210914398e-01f, +7.579672759e-01f, -5.669052229e-02f },
+    { -1.922370400e-02f, +3.207701601e-01f, +7.582763982e-01f, -5.670824380e-02f },
+    { -1.919679069e-02f, +3.204489742e-01f, +7.585853955e-01f, -5.672592019e-02f },
+    { -1.916989465e-02f, +3.201278822e-01f, +7.588942678e-01f, -5.674355138e-02f },
+    { -1.914301589e-02f, +3.198068842e-01f, +7.592030147e-01f, -5.676113731e-02f },
+    { -1.911615443e-02f, +3.194859804e-01f, +7.595116361e-01f, -5.677867790e-02f },
+    { -1.908931029e-02f, +3.191651709e-01f, +7.598201319e-01f, -5.679617306e-02f },
+    { -1.906248348e-02f, +3.188444558e-01f, +7.601285018e-01f, -5.681362272e-02f },
+    { -1.903567401e-02f, +3.185238353e-01f, +7.604367457e-01f, -5.683102681e-02f },
+    { -1.900888191e-02f, +3.182033094e-01f, +7.607448634e-01f, -5.684838523e-02f },
+    { -1.898210719e-02f, +3.178828783e-01f, +7.610528546e-01f, -5.686569793e-02f },
+    { -1.895534986e-02f, +3.175625421e-01f, +7.613607194e-01f, -5.688296482e-02f },
+    { -1.892860994e-02f, +3.172423009e-01f, +7.616684574e-01f, -5.690018582e-02f },
+    { -1.890188745e-02f, +3.169221549e-01f, +7.619760684e-01f, -5.691736086e-02f },
+    { -1.887518241e-02f, +3.166021042e-01f, +7.622835524e-01f, -5.693448985e-02f },
+    { -1.884849482e-02f, +3.162821488e-01f, +7.625909090e-01f, -5.695157273e-02f },
+    { -1.882182471e-02f, +3.159622890e-01f, +7.628981382e-01f, -5.696860941e-02f },
+    { -1.879517209e-02f, +3.156425249e-01f, +7.632052398e-01f, -5.698559981e-02f },
+    { -1.876853697e-02f, +3.153228565e-01f, +7.635122136e-01f, -5.700254386e-02f },
+    { -1.874191938e-02f, +3.150032840e-01f, +7.638190593e-01f, -5.701944149e-02f },
+    { -1.871531932e-02f, +3.146838075e-01f, +7.641257769e-01f, -5.703629260e-02f },
+    { -1.868873682e-02f, +3.143644272e-01f, +7.644323662e-01f, -5.705309714e-02f },
+    { -1.866217188e-02f, +3.140451431e-01f, +7.647388269e-01f, -5.706985501e-02f },
+    { -1.863562453e-02f, +3.137259554e-01f, +7.650451589e-01f, -5.708656614e-02f },
+    { -1.860909478e-02f, +3.134068642e-01f, +7.653513620e-01f, -5.710323045e-02f },
+    { -1.858258264e-02f, +3.130878696e-01f, +7.656574360e-01f, -5.711984787e-02f },
+    { -1.855608813e-02f, +3.127689718e-01f, +7.659633808e-01f, -5.713641831e-02f },
+    { -1.852961127e-02f, +3.124501709e-01f, +7.662691962e-01f, -5.715294170e-02f },
+    { -1.850315207e-02f, +3.121314670e-01f, +7.665748819e-01f, -5.716941797e-02f },
+    { -1.847671054e-02f, +3.118128601e-01f, +7.668804379e-01f, -5.718584702e-02f },
+    { -1.845028671e-02f, +3.114943506e-01f, +7.671858639e-01f, -5.720222880e-02f },
+    { -1.842388058e-02f, +3.111759383e-01f, +7.674911598e-01f, -5.721856321e-02f },
+    { -1.839749217e-02f, +3.108576236e-01f, +7.677963254e-01f, -5.723485017e-02f },
+    { -1.837112149e-02f, +3.105394064e-01f, +7.681013605e-01f, -5.725108962e-02f },
+    { -1.834476857e-02f, +3.102212870e-01f, +7.684062649e-01f, -5.726728148e-02f },
+    { -1.831843341e-02f, +3.099032654e-01f, +7.687110385e-01f, -5.728342566e-02f },
+    { -1.829211604e-02f, +3.095853418e-01f, +7.690156811e-01f, -5.729952209e-02f },
+    { -1.826581646e-02f, +3.092675163e-01f, +7.693201924e-01f, -5.731557068e-02f },
+    { -1.823953469e-02f, +3.089497890e-01f, +7.696245724e-01f, -5.733157137e-02f },
+    { -1.821327074e-02f, +3.086321600e-01f, +7.699288208e-01f, -5.734752407e-02f },
+    { -1.818702464e-02f, +3.083146294e-01f, +7.702329374e-01f, -5.736342870e-02f },
+    { -1.816079639e-02f, +3.079971975e-01f, +7.705369222e-01f, -5.737928519e-02f },
+    { -1.813458600e-02f, +3.076798642e-01f, +7.708407749e-01f, -5.739509346e-02f },
+    { -1.810839350e-02f, +3.073626297e-01f, +7.711444953e-01f, -5.741085343e-02f },
+    { -1.808221890e-02f, +3.070454941e-01f, +7.714480832e-01f, -5.742656503e-02f },
+    { -1.805606221e-02f, +3.067284576e-01f, +7.717515386e-01f, -5.744222816e-02f },
+    { -1.802992345e-02f, +3.064115203e-01f, +7.720548611e-01f, -5.745784276e-02f },
+    { -1.800380263e-02f, +3.060946822e-01f, +7.723580506e-01f, -5.747340875e-02f },
+    { -1.797769977e-02f, +3.057779436e-01f, +7.726611070e-01f, -5.748892604e-02f },
+    { -1.795161487e-02f, +3.054613045e-01f, +7.729640301e-01f, -5.750439457e-02f },
+    { -1.792554796e-02f, +3.051447650e-01f, +7.732668197e-01f, -5.751981424e-02f },
+    { -1.789949905e-02f, +3.048283253e-01f, +7.735694755e-01f, -5.753518499e-02f },
+    { -1.787346814e-02f, +3.045119855e-01f, +7.738719975e-01f, -5.755050673e-02f },
+    { -1.784745527e-02f, +3.041957456e-01f, +7.741743855e-01f, -5.756577939e-02f },
+    { -1.782146044e-02f, +3.038796059e-01f, +7.744766393e-01f, -5.758100289e-02f },
+    { -1.779548366e-02f, +3.035635665e-01f, +7.747787586e-01f, -5.759617715e-02f },
+    { -1.776952495e-02f, +3.032476274e-01f, +7.750807434e-01f, -5.761130208e-02f },
+    { -1.774358432e-02f, +3.029317887e-01f, +7.753825935e-01f, -5.762637762e-02f },
+    { -1.771766179e-02f, +3.026160507e-01f, +7.756843086e-01f, -5.764140368e-02f },
+    { -1.769175736e-02f, +3.023004134e-01f, +7.759858886e-01f, -5.765638018e-02f },
+    { -1.766587107e-02f, +3.019848769e-01f, +7.762873333e-01f, -5.767130705e-02f },
+    { -1.764000291e-02f, +3.016694414e-01f, +7.765886426e-01f, -5.768618420e-02f },
+    { -1.761415290e-02f, +3.013541069e-01f, +7.768898163e-01f, -5.770101157e-02f },
+    { -1.758832105e-02f, +3.010388736e-01f, +7.771908542e-01f, -5.771578906e-02f },
+    { -1.756250739e-02f, +3.007237417e-01f, +7.774917560e-01f, -5.773051660e-02f },
+    { -1.753671192e-02f, +3.004087111e-01f, +7.777925218e-01f, -5.774519411e-02f },
+    { -1.751093465e-02f, +3.000937821e-01f, +7.780931511e-01f, -5.775982152e-02f },
+    { -1.748517560e-02f, +2.997789547e-01f, +7.783936440e-01f, -5.777439874e-02f },
+    { -1.745943479e-02f, +2.994642291e-01f, +7.786940002e-01f, -5.778892569e-02f },
+    { -1.743371222e-02f, +2.991496054e-01f, +7.789942195e-01f, -5.780340230e-02f },
+    { -1.740800791e-02f, +2.988350837e-01f, +7.792943018e-01f, -5.781782849e-02f },
+    { -1.738232187e-02f, +2.985206642e-01f, +7.795942469e-01f, -5.783220417e-02f },
+    { -1.735665412e-02f, +2.982063468e-01f, +7.798940546e-01f, -5.784652927e-02f },
+    { -1.733100467e-02f, +2.978921319e-01f, +7.801937247e-01f, -5.786080371e-02f },
+    { -1.730537353e-02f, +2.975780194e-01f, +7.804932570e-01f, -5.787502741e-02f },
+    { -1.727976072e-02f, +2.972640094e-01f, +7.807926515e-01f, -5.788920029e-02f },
+    { -1.725416625e-02f, +2.969501022e-01f, +7.810919079e-01f, -5.790332228e-02f },
+    { -1.722859012e-02f, +2.966362979e-01f, +7.813910260e-01f, -5.791739329e-02f },
+    { -1.720303237e-02f, +2.963225964e-01f, +7.816900056e-01f, -5.793141324e-02f },
+    { -1.717749299e-02f, +2.960089980e-01f, +7.819888466e-01f, -5.794538205e-02f },
+    { -1.715197200e-02f, +2.956955028e-01f, +7.822875489e-01f, -5.795929965e-02f },
+    { -1.712646941e-02f, +2.953821109e-01f, +7.825861121e-01f, -5.797316596e-02f },
+    { -1.710098524e-02f, +2.950688223e-01f, +7.828845363e-01f, -5.798698089e-02f },
+    { -1.707551950e-02f, +2.947556373e-01f, +7.831828211e-01f, -5.800074437e-02f },
+    { -1.705007220e-02f, +2.944425560e-01f, +7.834809664e-01f, -5.801445632e-02f },
+    { -1.702464336e-02f, +2.941295783e-01f, +7.837789720e-01f, -5.802811666e-02f },
+    { -1.699923298e-02f, +2.938167045e-01f, +7.840768378e-01f, -5.804172530e-02f },
+    { -1.697384108e-02f, +2.935039348e-01f, +7.843745636e-01f, -5.805528218e-02f },
+    { -1.694846768e-02f, +2.931912691e-01f, +7.846721491e-01f, -5.806878721e-02f },
+    { -1.692311278e-02f, +2.928787076e-01f, +7.849695943e-01f, -5.808224030e-02f },
+    { -1.689777639e-02f, +2.925662504e-01f, +7.852668990e-01f, -5.809564139e-02f },
+    { -1.687245854e-02f, +2.922538976e-01f, +7.855640629e-01f, -5.810899039e-02f },
+    { -1.684715923e-02f, +2.919416495e-01f, +7.858610859e-01f, -5.812228722e-02f },
+    { -1.682187847e-02f, +2.916295059e-01f, +7.861579679e-01f, -5.813553181e-02f },
+    { -1.679661628e-02f, +2.913174672e-01f, +7.864547086e-01f, -5.814872407e-02f },
+    { -1.677137267e-02f, +2.910055333e-01f, +7.867513079e-01f, -5.816186392e-02f },
+    { -1.674614765e-02f, +2.906937045e-01f, +7.870477656e-01f, -5.817495128e-02f },
+    { -1.672094124e-02f, +2.903819808e-01f, +7.873440816e-01f, -5.818798608e-02f },
+    { -1.669575344e-02f, +2.900703623e-01f, +7.876402556e-01f, -5.820096823e-02f },
+    { -1.667058426e-02f, +2.897588491e-01f, +7.879362875e-01f, -5.821389766e-02f },
+    { -1.664543373e-02f, +2.894474415e-01f, +7.882321771e-01f, -5.822677428e-02f },
+    { -1.662030185e-02f, +2.891361394e-01f, +7.885279242e-01f, -5.823959802e-02f },
+    { -1.659518863e-02f, +2.888249429e-01f, +7.888235287e-01f, -5.825236880e-02f },
+    { -1.657009409e-02f, +2.885138523e-01f, +7.891189904e-01f, -5.826508653e-02f },
+    { -1.654501824e-02f, +2.882028676e-01f, +7.894143091e-01f, -5.827775113e-02f },
+    { -1.651996108e-02f, +2.878919890e-01f, +7.897094846e-01f, -5.829036253e-02f },
+    { -1.649492264e-02f, +2.875812164e-01f, +7.900045168e-01f, -5.830292065e-02f },
+    { -1.646990291e-02f, +2.872705501e-01f, +7.902994055e-01f, -5.831542540e-02f },
+    { -1.644490193e-02f, +2.869599902e-01f, +7.905941505e-01f, -5.832787671e-02f },
+    { -1.641991969e-02f, +2.866495368e-01f, +7.908887516e-01f, -5.834027449e-02f },
+    { -1.639495620e-02f, +2.863391899e-01f, +7.911832088e-01f, -5.835261867e-02f },
+    { -1.637001149e-02f, +2.860289498e-01f, +7.914775217e-01f, -5.836490917e-02f },
+    { -1.634508556e-02f, +2.857188164e-01f, +7.917716902e-01f, -5.837714590e-02f },
+    { -1.632017842e-02f, +2.854087900e-01f, +7.920657142e-01f, -5.838932879e-02f },
+    { -1.629529008e-02f, +2.850988707e-01f, +7.923595935e-01f, -5.840145776e-02f },
+    { -1.627042056e-02f, +2.847890584e-01f, +7.926533279e-01f, -5.841353272e-02f },
+    { -1.624556986e-02f, +2.844793535e-01f, +7.929469172e-01f, -5.842555359e-02f },
+    { -1.622073801e-02f, +2.841697559e-01f, +7.932403612e-01f, -5.843752031e-02f },
+    { -1.619592500e-02f, +2.838602658e-01f, +7.935336599e-01f, -5.844943277e-02f },
+    { -1.617113085e-02f, +2.835508833e-01f, +7.938268129e-01f, -5.846129092e-02f },
+    { -1.614635558e-02f, +2.832416085e-01f, +7.941198202e-01f, -5.847309465e-02f },
+    { -1.612159918e-02f, +2.829324415e-01f, +7.944126816e-01f, -5.848484391e-02f },
+    { -1.609686168e-02f, +2.826233824e-01f, +7.947053969e-01f, -5.849653859e-02f },
+    { -1.607214309e-02f, +2.823144314e-01f, +7.949979658e-01f, -5.850817864e-02f },
+    { -1.604744341e-02f, +2.820055885e-01f, +7.952903884e-01f, -5.851976395e-02f },
+    { -1.602276266e-02f, +2.816968539e-01f, +7.955826642e-01f, -5.853129446e-02f },
+    { -1.599810084e-02f, +2.813882276e-01f, +7.958747933e-01f, -5.854277008e-02f },
+    { -1.597345798e-02f, +2.810797098e-01f, +7.961667755e-01f, -5.855419074e-02f },
+    { -1.594883407e-02f, +2.807713007e-01f, +7.964586105e-01f, -5.856555634e-02f },
+    { -1.592422914e-02f, +2.804630002e-01f, +7.967502981e-01f, -5.857686682e-02f },
+    { -1.589964318e-02f, +2.801548085e-01f, +7.970418383e-01f, -5.858812209e-02f },
+    { -1.587507622e-02f, +2.798467258e-01f, +7.973332308e-01f, -5.859932207e-02f },
+    { -1.585052826e-02f, +2.795387520e-01f, +7.976244755e-01f, -5.861046669e-02f },
+    { -1.582599931e-02f, +2.792308874e-01f, +7.979155722e-01f, -5.862155585e-02f },
+    { -1.580148939e-02f, +2.789231321e-01f, +7.982065207e-01f, -5.863258948e-02f },
+    { -1.577699850e-02f, +2.786154861e-01f, +7.984973209e-01f, -5.864356750e-02f },
+    { -1.575252665e-02f, +2.783079496e-01f, +7.987879725e-01f, -5.865448983e-02f },
+    { -1.572807386e-02f, +2.780005227e-01f, +7.990784754e-01f, -5.866535639e-02f },
+    { -1.570364014e-02f, +2.776932054e-01f, +7.993688295e-01f, -5.867616710e-02f },
+    { -1.567922549e-02f, +2.773859980e-01f, +7.996590346e-01f, -5.868692187e-02f },
+    { -1.565482993e-02f, +2.770789004e-01f, +7.999490904e-01f, -5.869762062e-02f },
+    { -1.563045347e-02f, +2.767719129e-01f, +8.002389969e-01f, -5.870826329e-02f },
+    { -1.560609611e-02f, +2.764650354e-01f, +8.005287538e-01f, -5.871884977e-02f },
+    { -1.558175787e-02f, +2.761582682e-01f, +8.008183610e-01f, -5.872938000e-02f },
+    { -1.555743875e-02f, +2.758516114e-01f, +8.011078183e-01f, -5.873985390e-02f },
+    { -1.553313878e-02f, +2.755450649e-01f, +8.013971255e-01f, -5.875027137e-02f },
+    { -1.550885795e-02f, +2.752386291e-01f, +8.016862825e-01f, -5.876063235e-02f },
+    { -1.548459628e-02f, +2.749323038e-01f, +8.019752891e-01f, -5.877093675e-02f },
+    { -1.546035377e-02f, +2.746260894e-01f, +8.022641451e-01f, -5.878118449e-02f },
+    { -1.543613045e-02f, +2.743199858e-01f, +8.025528503e-01f, -5.879137549e-02f },
+    { -1.541192631e-02f, +2.740139932e-01f, +8.028414047e-01f, -5.880150966e-02f },
+    { -1.538774137e-02f, +2.737081117e-01f, +8.031298079e-01f, -5.881158693e-02f },
+    { -1.536357563e-02f, +2.734023414e-01f, +8.034180599e-01f, -5.882160722e-02f },
+    { -1.533942912e-02f, +2.730966824e-01f, +8.037061604e-01f, -5.883157045e-02f },
+    { -1.531530183e-02f, +2.727911348e-01f, +8.039941094e-01f, -5.884147653e-02f },
+    { -1.529119377e-02f, +2.724856987e-01f, +8.042819066e-01f, -5.885132538e-02f },
+    { -1.526710496e-02f, +2.721803743e-01f, +8.045695518e-01f, -5.886111692e-02f },
+    { -1.524303541e-02f, +2.718751616e-01f, +8.048570449e-01f, -5.887085108e-02f },
+    { -1.521898512e-02f, +2.715700607e-01f, +8.051443857e-01f, -5.888052777e-02f },
+    { -1.519495411e-02f, +2.712650718e-01f, +8.054315741e-01f, -5.889014690e-02f },
+    { -1.517094238e-02f, +2.709601949e-01f, +8.057186098e-01f, -5.889970841e-02f },
+    { -1.514694995e-02f, +2.706554301e-01f, +8.060054928e-01f, -5.890921220e-02f },
+    { -1.512297682e-02f, +2.703507777e-01f, +8.062922227e-01f, -5.891865819e-02f },
+    { -1.509902300e-02f, +2.700462376e-01f, +8.065787996e-01f, -5.892804632e-02f },
+    { -1.507508850e-02f, +2.697418099e-01f, +8.068652231e-01f, -5.893737648e-02f },
+    { -1.505117334e-02f, +2.694374949e-01f, +8.071514932e-01f, -5.894664861e-02f },
+    { -1.502727751e-02f, +2.691332925e-01f, +8.074376096e-01f, -5.895586262e-02f },
+    { -1.500340104e-02f, +2.688292029e-01f, +8.077235722e-01f, -5.896501843e-02f },
+    { -1.497954392e-02f, +2.685252263e-01f, +8.080093808e-01f, -5.897411596e-02f },
+    { -1.495570618e-02f, +2.682213626e-01f, +8.082950352e-01f, -5.898315513e-02f },
+    { -1.493188780e-02f, +2.679176120e-01f, +8.085805354e-01f, -5.899213585e-02f },
+    { -1.490808882e-02f, +2.676139746e-01f, +8.088658810e-01f, -5.900105805e-02f },
+    { -1.488430923e-02f, +2.673104506e-01f, +8.091510719e-01f, -5.900992164e-02f },
+    { -1.486054904e-02f, +2.670070400e-01f, +8.094361081e-01f, -5.901872654e-02f },
+    { -1.483680827e-02f, +2.667037429e-01f, +8.097209892e-01f, -5.902747268e-02f },
+    { -1.481308692e-02f, +2.664005594e-01f, +8.100057151e-01f, -5.903615996e-02f },
+    { -1.478938500e-02f, +2.660974897e-01f, +8.102902857e-01f, -5.904478831e-02f },
+    { -1.476570252e-02f, +2.657945338e-01f, +8.105747008e-01f, -5.905335765e-02f },
+    { -1.474203949e-02f, +2.654916918e-01f, +8.108589603e-01f, -5.906186790e-02f },
+    { -1.471839591e-02f, +2.651889639e-01f, +8.111430638e-01f, -5.907031897e-02f },
+    { -1.469477181e-02f, +2.648863501e-01f, +8.114270114e-01f, -5.907871078e-02f },
+    { -1.467116717e-02f, +2.645838506e-01f, +8.117108027e-01f, -5.908704325e-02f },
+    { -1.464758202e-02f, +2.642814655e-01f, +8.119944377e-01f, -5.909531630e-02f },
+    { -1.462401636e-02f, +2.639791948e-01f, +8.122779162e-01f, -5.910352985e-02f },
+    { -1.460047020e-02f, +2.636770386e-01f, +8.125612380e-01f, -5.911168382e-02f },
+    { -1.457694355e-02f, +2.633749972e-01f, +8.128444029e-01f, -5.911977812e-02f },
+    { -1.455343641e-02f, +2.630730705e-01f, +8.131274108e-01f, -5.912781267e-02f },
+    { -1.452994881e-02f, +2.627712587e-01f, +8.134102615e-01f, -5.913578740e-02f },
+    { -1.450648073e-02f, +2.624695618e-01f, +8.136929549e-01f, -5.914370221e-02f },
+    { -1.448303220e-02f, +2.621679801e-01f, +8.139754907e-01f, -5.915155704e-02f },
+    { -1.445960321e-02f, +2.618665135e-01f, +8.142578688e-01f, -5.915935179e-02f },
+    { -1.443619379e-02f, +2.615651622e-01f, +8.145400890e-01f, -5.916708638e-02f },
+    { -1.441280393e-02f, +2.612639263e-01f, +8.148221512e-01f, -5.917476074e-02f },
+    { -1.438943365e-02f, +2.609628058e-01f, +8.151040551e-01f, -5.918237478e-02f },
+    { -1.436608295e-02f, +2.606618010e-01f, +8.153858007e-01f, -5.918992842e-02f },
+    { -1.434275184e-02f, +2.603609118e-01f, +8.156673878e-01f, -5.919742158e-02f },
+    { -1.431944033e-02f, +2.600601385e-01f, +8.159488162e-01f, -5.920485418e-02f },
+    { -1.429614843e-02f, +2.597594810e-01f, +8.162300856e-01f, -5.921222613e-02f },
+    { -1.427287614e-02f, +2.594589396e-01f, +8.165111960e-01f, -5.921953735e-02f },
+    { -1.424962348e-02f, +2.591585142e-01f, +8.167921473e-01f, -5.922678777e-02f },
+    { -1.422639045e-02f, +2.588582051e-01f, +8.170729391e-01f, -5.923397729e-02f },
+    { -1.420317705e-02f, +2.585580122e-01f, +8.173535714e-01f, -5.924110584e-02f },
+    { -1.417998331e-02f, +2.582579358e-01f, +8.176340439e-01f, -5.924817333e-02f },
+    { -1.415680921e-02f, +2.579579758e-01f, +8.179143566e-01f, -5.925517969e-02f },
+    { -1.413365478e-02f, +2.576581325e-01f, +8.181945092e-01f, -5.926212483e-02f },
+    { -1.411052002e-02f, +2.573584058e-01f, +8.184745017e-01f, -5.926900867e-02f },
+    { -1.408740493e-02f, +2.570587960e-01f, +8.187543337e-01f, -5.927583113e-02f },
+    { -1.406430953e-02f, +2.567593031e-01f, +8.190340052e-01f, -5.928259213e-02f },
+    { -1.404123383e-02f, +2.564599271e-01f, +8.193135159e-01f, -5.928929157e-02f },
+    { -1.401817782e-02f, +2.561606683e-01f, +8.195928658e-01f, -5.929592939e-02f },
+    { -1.399514152e-02f, +2.558615267e-01f, +8.198720547e-01f, -5.930250550e-02f },
+    { -1.397212493e-02f, +2.555625024e-01f, +8.201510823e-01f, -5.930901982e-02f },
+    { -1.394912807e-02f, +2.552635955e-01f, +8.204299485e-01f, -5.931547226e-02f },
+    { -1.392615094e-02f, +2.549648062e-01f, +8.207086532e-01f, -5.932186275e-02f },
+    { -1.390319354e-02f, +2.546661344e-01f, +8.209871961e-01f, -5.932819120e-02f },
+    { -1.388025589e-02f, +2.543675803e-01f, +8.212655772e-01f, -5.933445753e-02f },
+    { -1.385733799e-02f, +2.540691440e-01f, +8.215437962e-01f, -5.934066165e-02f },
+    { -1.383443984e-02f, +2.537708257e-01f, +8.218218531e-01f, -5.934680349e-02f },
+    { -1.381156147e-02f, +2.534726253e-01f, +8.220997475e-01f, -5.935288297e-02f },
+    { -1.378870286e-02f, +2.531745431e-01f, +8.223774794e-01f, -5.935889999e-02f },
+    { -1.376586404e-02f, +2.528765790e-01f, +8.226550485e-01f, -5.936485448e-02f },
+    { -1.374304500e-02f, +2.525787333e-01f, +8.229324548e-01f, -5.937074636e-02f },
+    { -1.372024576e-02f, +2.522810059e-01f, +8.232096981e-01f, -5.937657555e-02f },
+    { -1.369746632e-02f, +2.519833971e-01f, +8.234867781e-01f, -5.938234196e-02f },
+    { -1.367470668e-02f, +2.516859068e-01f, +8.237636948e-01f, -5.938804550e-02f },
+    { -1.365196686e-02f, +2.513885353e-01f, +8.240404479e-01f, -5.939368611e-02f },
+    { -1.362924687e-02f, +2.510912825e-01f, +8.243170373e-01f, -5.939926369e-02f },
+    { -1.360654670e-02f, +2.507941486e-01f, +8.245934629e-01f, -5.940477816e-02f },
+    { -1.358386636e-02f, +2.504971337e-01f, +8.248697244e-01f, -5.941022944e-02f },
+    { -1.356120587e-02f, +2.502002379e-01f, +8.251458217e-01f, -5.941561746e-02f },
+    { -1.353856523e-02f, +2.499034613e-01f, +8.254217546e-01f, -5.942094212e-02f },
+    { -1.351594444e-02f, +2.496068040e-01f, +8.256975230e-01f, -5.942620334e-02f },
+    { -1.349334351e-02f, +2.493102660e-01f, +8.259731267e-01f, -5.943140104e-02f },
+    { -1.347076245e-02f, +2.490138475e-01f, +8.262485655e-01f, -5.943653515e-02f },
+    { -1.344820127e-02f, +2.487175486e-01f, +8.265238393e-01f, -5.944160557e-02f },
+    { -1.342565996e-02f, +2.484213694e-01f, +8.267989479e-01f, -5.944661222e-02f },
+    { -1.340313855e-02f, +2.481253099e-01f, +8.270738912e-01f, -5.945155503e-02f },
+    { -1.338063703e-02f, +2.478293703e-01f, +8.273486689e-01f, -5.945643390e-02f },
+    { -1.335815541e-02f, +2.475335507e-01f, +8.276232810e-01f, -5.946124877e-02f },
+    { -1.333569370e-02f, +2.472378511e-01f, +8.278977272e-01f, -5.946599953e-02f },
+    { -1.331325190e-02f, +2.469422717e-01f, +8.281720074e-01f, -5.947068612e-02f },
+    { -1.329083001e-02f, +2.466468125e-01f, +8.284461214e-01f, -5.947530845e-02f },
+    { -1.326842806e-02f, +2.463514737e-01f, +8.287200691e-01f, -5.947986644e-02f },
+    { -1.324604604e-02f, +2.460562553e-01f, +8.289938502e-01f, -5.948436000e-02f },
+    { -1.322368395e-02f, +2.457611574e-01f, +8.292674647e-01f, -5.948878905e-02f },
+    { -1.320134181e-02f, +2.454661802e-01f, +8.295409124e-01f, -5.949315351e-02f },
+    { -1.317901962e-02f, +2.451713237e-01f, +8.298141930e-01f, -5.949745330e-02f },
+    { -1.315671739e-02f, +2.448765881e-01f, +8.300873065e-01f, -5.950168834e-02f },
+    { -1.313443511e-02f, +2.445819733e-01f, +8.303602527e-01f, -5.950585853e-02f },
+    { -1.311217281e-02f, +2.442874796e-01f, +8.306330313e-01f, -5.950996381e-02f },
+    { -1.308993048e-02f, +2.439931070e-01f, +8.309056424e-01f, -5.951400408e-02f },
+    { -1.306770813e-02f, +2.436988556e-01f, +8.311780856e-01f, -5.951797926e-02f },
+    { -1.304550576e-02f, +2.434047255e-01f, +8.314503608e-01f, -5.952188928e-02f },
+    { -1.302332339e-02f, +2.431107168e-01f, +8.317224679e-01f, -5.952573404e-02f },
+    { -1.300116101e-02f, +2.428168296e-01f, +8.319944067e-01f, -5.952951347e-02f },
+    { -1.297901864e-02f, +2.425230639e-01f, +8.322661770e-01f, -5.953322748e-02f },
+    { -1.295689627e-02f, +2.422294200e-01f, +8.325377786e-01f, -5.953687600e-02f },
+    { -1.293479392e-02f, +2.419358978e-01f, +8.328092115e-01f, -5.954045893e-02f },
+    { -1.291271159e-02f, +2.416424975e-01f, +8.330804754e-01f, -5.954397620e-02f },
+    { -1.289064929e-02f, +2.413492191e-01f, +8.333515702e-01f, -5.954742771e-02f },
+    { -1.286860701e-02f, +2.410560629e-01f, +8.336224957e-01f, -5.955081340e-02f },
+    { -1.284658477e-02f, +2.407630287e-01f, +8.338932518e-01f, -5.955413318e-02f },
+    { -1.282458258e-02f, +2.404701168e-01f, +8.341638383e-01f, -5.955738696e-02f },
+    { -1.280260043e-02f, +2.401773273e-01f, +8.344342550e-01f, -5.956057466e-02f },
+    { -1.278063833e-02f, +2.398846601e-01f, +8.347045017e-01f, -5.956369619e-02f },
+    { -1.275869629e-02f, +2.395921155e-01f, +8.349745784e-01f, -5.956675149e-02f },
+    { -1.273677432e-02f, +2.392996936e-01f, +8.352444848e-01f, -5.956974045e-02f },
+    { -1.271487241e-02f, +2.390073943e-01f, +8.355142207e-01f, -5.957266301e-02f },
+    { -1.269299057e-02f, +2.387152178e-01f, +8.357837861e-01f, -5.957551907e-02f },
+    { -1.267112882e-02f, +2.384231642e-01f, +8.360531808e-01f, -5.957830856e-02f },
+    { -1.264928715e-02f, +2.381312337e-01f, +8.363224045e-01f, -5.958103139e-02f },
+    { -1.262746557e-02f, +2.378394262e-01f, +8.365914572e-01f, -5.958368747e-02f },
+    { -1.260566408e-02f, +2.375477418e-01f, +8.368603387e-01f, -5.958627674e-02f },
+    { -1.258388269e-02f, +2.372561808e-01f, +8.371290487e-01f, -5.958879909e-02f },
+    { -1.256212141e-02f, +2.369647431e-01f, +8.373975872e-01f, -5.959125445e-02f },
+    { -1.254038023e-02f, +2.366734288e-01f, +8.376659540e-01f, -5.959364274e-02f },
+    { -1.251865917e-02f, +2.363822381e-01f, +8.379341490e-01f, -5.959596388e-02f },
+    { -1.249695823e-02f, +2.360911710e-01f, +8.382021718e-01f, -5.959821777e-02f },
+    { -1.247527741e-02f, +2.358002277e-01f, +8.384700225e-01f, -5.960040434e-02f },
+    { -1.245361672e-02f, +2.355094081e-01f, +8.387377009e-01f, -5.960252351e-02f },
+    { -1.243197617e-02f, +2.352187125e-01f, +8.390052067e-01f, -5.960457519e-02f },
+    { -1.241035575e-02f, +2.349281409e-01f, +8.392725398e-01f, -5.960655929e-02f },
+    { -1.238875548e-02f, +2.346376934e-01f, +8.395397001e-01f, -5.960847575e-02f },
+    { -1.236717535e-02f, +2.343473700e-01f, +8.398066874e-01f, -5.961032446e-02f },
+    { -1.234561537e-02f, +2.340571709e-01f, +8.400735015e-01f, -5.961210535e-02f },
+    { -1.232407555e-02f, +2.337670962e-01f, +8.403401423e-01f, -5.961381834e-02f },
+    { -1.230255589e-02f, +2.334771460e-01f, +8.406066096e-01f, -5.961546335e-02f },
+    { -1.228105640e-02f, +2.331873202e-01f, +8.408729033e-01f, -5.961704028e-02f },
+    { -1.225957708e-02f, +2.328976192e-01f, +8.411390231e-01f, -5.961854906e-02f },
+    { -1.223811793e-02f, +2.326080428e-01f, +8.414049690e-01f, -5.961998961e-02f },
+    { -1.221667896e-02f, +2.323185913e-01f, +8.416707408e-01f, -5.962136183e-02f },
+    { -1.219526018e-02f, +2.320292646e-01f, +8.419363383e-01f, -5.962266566e-02f },
+    { -1.217386158e-02f, +2.317400630e-01f, +8.422017613e-01f, -5.962390099e-02f },
+    { -1.215248317e-02f, +2.314509865e-01f, +8.424670098e-01f, -5.962506776e-02f },
+    { -1.213112496e-02f, +2.311620351e-01f, +8.427320835e-01f, -5.962616588e-02f },
+    { -1.210978695e-02f, +2.308732090e-01f, +8.429969822e-01f, -5.962719526e-02f },
+    { -1.208846914e-02f, +2.305845082e-01f, +8.432617059e-01f, -5.962815583e-02f },
+    { -1.206717155e-02f, +2.302959329e-01f, +8.435262543e-01f, -5.962904749e-02f },
+    { -1.204589416e-02f, +2.300074831e-01f, +8.437906274e-01f, -5.962987017e-02f },
+    { -1.202463700e-02f, +2.297191590e-01f, +8.440548248e-01f, -5.963062378e-02f },
+    { -1.200340005e-02f, +2.294309606e-01f, +8.443188466e-01f, -5.963130824e-02f },
+    { -1.198218333e-02f, +2.291428879e-01f, +8.445826924e-01f, -5.963192347e-02f },
+    { -1.196098683e-02f, +2.288549412e-01f, +8.448463623e-01f, -5.963246938e-02f },
+    { -1.193981057e-02f, +2.285671204e-01f, +8.451098559e-01f, -5.963294588e-02f },
+    { -1.191865455e-02f, +2.282794257e-01f, +8.453731732e-01f, -5.963335291e-02f },
+    { -1.189751876e-02f, +2.279918571e-01f, +8.456363139e-01f, -5.963369036e-02f },
+    { -1.187640322e-02f, +2.277044148e-01f, +8.458992780e-01f, -5.963395817e-02f },
+    { -1.185530793e-02f, +2.274170988e-01f, +8.461620653e-01f, -5.963415624e-02f },
+    { -1.183423289e-02f, +2.271299092e-01f, +8.464246755e-01f, -5.963428449e-02f },
+    { -1.181317810e-02f, +2.268428462e-01f, +8.466871086e-01f, -5.963434285e-02f },
+    { -1.179214358e-02f, +2.265559097e-01f, +8.469493644e-01f, -5.963433122e-02f },
+    { -1.177112931e-02f, +2.262690999e-01f, +8.472114428e-01f, -5.963424952e-02f },
+    { -1.175013531e-02f, +2.259824169e-01f, +8.474733435e-01f, -5.963409767e-02f },
+    { -1.172916159e-02f, +2.256958607e-01f, +8.477350664e-01f, -5.963387559e-02f },
+    { -1.170820813e-02f, +2.254094315e-01f, +8.479966114e-01f, -5.963358319e-02f },
+    { -1.168727495e-02f, +2.251231293e-01f, +8.482579783e-01f, -5.963322039e-02f },
+    { -1.166636205e-02f, +2.248369542e-01f, +8.485191669e-01f, -5.963278711e-02f },
+    { -1.164546944e-02f, +2.245509063e-01f, +8.487801771e-01f, -5.963228326e-02f },
+    { -1.162459711e-02f, +2.242649857e-01f, +8.490410088e-01f, -5.963170876e-02f },
+    { -1.160374507e-02f, +2.239791925e-01f, +8.493016617e-01f, -5.963106352e-02f },
+    { -1.158291333e-02f, +2.236935267e-01f, +8.495621358e-01f, -5.963034747e-02f },
+    { -1.156210188e-02f, +2.234079885e-01f, +8.498224307e-01f, -5.962956051e-02f },
+    { -1.154131073e-02f, +2.231225780e-01f, +8.500825465e-01f, -5.962870257e-02f },
+    { -1.152053989e-02f, +2.228372951e-01f, +8.503424830e-01f, -5.962777357e-02f },
+    { -1.149978935e-02f, +2.225521401e-01f, +8.506022399e-01f, -5.962677341e-02f },
+    { -1.147905912e-02f, +2.222671129e-01f, +8.508618171e-01f, -5.962570201e-02f },
+    { -1.145834920e-02f, +2.219822138e-01f, +8.511212145e-01f, -5.962455930e-02f },
+    { -1.143765960e-02f, +2.216974426e-01f, +8.513804320e-01f, -5.962334519e-02f },
+    { -1.141699032e-02f, +2.214127997e-01f, +8.516394693e-01f, -5.962205959e-02f },
+    { -1.139634135e-02f, +2.211282850e-01f, +8.518983262e-01f, -5.962070242e-02f },
+    { -1.137571272e-02f, +2.208438986e-01f, +8.521570028e-01f, -5.961927360e-02f },
+    { -1.135510441e-02f, +2.205596406e-01f, +8.524154987e-01f, -5.961777305e-02f },
+    { -1.133451643e-02f, +2.202755110e-01f, +8.526738139e-01f, -5.961620067e-02f },
+    { -1.131394878e-02f, +2.199915101e-01f, +8.529319481e-01f, -5.961455639e-02f },
+    { -1.129340147e-02f, +2.197076378e-01f, +8.531899012e-01f, -5.961284013e-02f },
+    { -1.127287449e-02f, +2.194238943e-01f, +8.534476731e-01f, -5.961105179e-02f },
+    { -1.125236786e-02f, +2.191402796e-01f, +8.537052637e-01f, -5.960919130e-02f },
+    { -1.123188157e-02f, +2.188567938e-01f, +8.539626726e-01f, -5.960725858e-02f },
+    { -1.121141563e-02f, +2.185734369e-01f, +8.542198999e-01f, -5.960525353e-02f },
+    { -1.119097004e-02f, +2.182902092e-01f, +8.544769453e-01f, -5.960317608e-02f },
+    { -1.117054480e-02f, +2.180071107e-01f, +8.547338087e-01f, -5.960102614e-02f },
+    { -1.115013991e-02f, +2.177241413e-01f, +8.549904899e-01f, -5.959880364e-02f },
+    { -1.112975538e-02f, +2.174413013e-01f, +8.552469888e-01f, -5.959650847e-02f },
+    { -1.110939121e-02f, +2.171585908e-01f, +8.555033052e-01f, -5.959414057e-02f },
+    { -1.108904740e-02f, +2.168760097e-01f, +8.557594390e-01f, -5.959169984e-02f },
+    { -1.106872396e-02f, +2.165935582e-01f, +8.560153900e-01f, -5.958918621e-02f },
+    { -1.104842088e-02f, +2.163112364e-01f, +8.562711580e-01f, -5.958659959e-02f },
+    { -1.102813817e-02f, +2.160290443e-01f, +8.565267430e-01f, -5.958393990e-02f },
+    { -1.100787583e-02f, +2.157469820e-01f, +8.567821446e-01f, -5.958120705e-02f },
+    { -1.098763386e-02f, +2.154650497e-01f, +8.570373629e-01f, -5.957840096e-02f },
+    { -1.096741227e-02f, +2.151832473e-01f, +8.572923976e-01f, -5.957552154e-02f },
+    { -1.094721106e-02f, +2.149015750e-01f, +8.575472486e-01f, -5.957256872e-02f },
+    { -1.092703022e-02f, +2.146200329e-01f, +8.578019157e-01f, -5.956954241e-02f },
+    { -1.090686977e-02f, +2.143386210e-01f, +8.580563988e-01f, -5.956644252e-02f },
+    { -1.088672970e-02f, +2.140573395e-01f, +8.583106977e-01f, -5.956326897e-02f },
+    { -1.086661002e-02f, +2.137761883e-01f, +8.585648123e-01f, -5.956002168e-02f },
+    { -1.084651073e-02f, +2.134951677e-01f, +8.588187423e-01f, -5.955670057e-02f },
+    { -1.082643182e-02f, +2.132142776e-01f, +8.590724878e-01f, -5.955330554e-02f },
+    { -1.080637331e-02f, +2.129335181e-01f, +8.593260484e-01f, -5.954983652e-02f },
+    { -1.078633519e-02f, +2.126528894e-01f, +8.595794240e-01f, -5.954629343e-02f },
+    { -1.076631747e-02f, +2.123723915e-01f, +8.598326146e-01f, -5.954267617e-02f },
+    { -1.074632015e-02f, +2.120920245e-01f, +8.600856199e-01f, -5.953898467e-02f },
+    { -1.072634322e-02f, +2.118117885e-01f, +8.603384398e-01f, -5.953521884e-02f },
+    { -1.070638669e-02f, +2.115316835e-01f, +8.605910741e-01f, -5.953137860e-02f },
+    { -1.068645057e-02f, +2.112517097e-01f, +8.608435227e-01f, -5.952746386e-02f },
+    { -1.066653486e-02f, +2.109718671e-01f, +8.610957855e-01f, -5.952347454e-02f },
+    { -1.064663954e-02f, +2.106921557e-01f, +8.613478622e-01f, -5.951941056e-02f },
+    { -1.062676464e-02f, +2.104125758e-01f, +8.615997527e-01f, -5.951527184e-02f },
+    { -1.060691015e-02f, +2.101331273e-01f, +8.618514569e-01f, -5.951105828e-02f },
+    { -1.058707606e-02f, +2.098538103e-01f, +8.621029746e-01f, -5.950676981e-02f },
+    { -1.056726239e-02f, +2.095746250e-01f, +8.623543057e-01f, -5.950240634e-02f },
+    { -1.054746914e-02f, +2.092955714e-01f, +8.626054500e-01f, -5.949796779e-02f },
+    { -1.052769630e-02f, +2.090166495e-01f, +8.628564073e-01f, -5.949345407e-02f },
+    { -1.050794387e-02f, +2.087378595e-01f, +8.631071776e-01f, -5.948886510e-02f },
+    { -1.048821187e-02f, +2.084592015e-01f, +8.633577606e-01f, -5.948420080e-02f },
+    { -1.046850028e-02f, +2.081806754e-01f, +8.636081562e-01f, -5.947946108e-02f },
+    { -1.044880911e-02f, +2.079022815e-01f, +8.638583642e-01f, -5.947464586e-02f },
+    { -1.042913837e-02f, +2.076240197e-01f, +8.641083845e-01f, -5.946975506e-02f },
+    { -1.040948805e-02f, +2.073458902e-01f, +8.643582170e-01f, -5.946478859e-02f },
+    { -1.038985815e-02f, +2.070678930e-01f, +8.646078615e-01f, -5.945974637e-02f },
+    { -1.037024868e-02f, +2.067900282e-01f, +8.648573178e-01f, -5.945462831e-02f },
+    { -1.035065964e-02f, +2.065122959e-01f, +8.651065858e-01f, -5.944943433e-02f },
+    { -1.033109103e-02f, +2.062346962e-01f, +8.653556654e-01f, -5.944416434e-02f },
+    { -1.031154284e-02f, +2.059572292e-01f, +8.656045563e-01f, -5.943881827e-02f },
+    { -1.029201508e-02f, +2.056798948e-01f, +8.658532585e-01f, -5.943339603e-02f },
+    { -1.027250776e-02f, +2.054026933e-01f, +8.661017718e-01f, -5.942789753e-02f },
+    { -1.025302087e-02f, +2.051256246e-01f, +8.663500959e-01f, -5.942232269e-02f },
+    { -1.023355441e-02f, +2.048486889e-01f, +8.665982309e-01f, -5.941667144e-02f },
+    { -1.021410838e-02f, +2.045718862e-01f, +8.668461765e-01f, -5.941094367e-02f },
+    { -1.019468279e-02f, +2.042952167e-01f, +8.670939326e-01f, -5.940513931e-02f },
+    { -1.017527764e-02f, +2.040186803e-01f, +8.673414990e-01f, -5.939925828e-02f },
+    { -1.015589292e-02f, +2.037422772e-01f, +8.675888756e-01f, -5.939330049e-02f },
+    { -1.013652864e-02f, +2.034660075e-01f, +8.678360622e-01f, -5.938726586e-02f },
+    { -1.011718480e-02f, +2.031898712e-01f, +8.680830587e-01f, -5.938115430e-02f },
+    { -1.009786140e-02f, +2.029138683e-01f, +8.683298650e-01f, -5.937496574e-02f },
+    { -1.007855843e-02f, +2.026379991e-01f, +8.685764808e-01f, -5.936870008e-02f },
+    { -1.005927591e-02f, +2.023622635e-01f, +8.688229060e-01f, -5.936235724e-02f },
+    { -1.004001383e-02f, +2.020866616e-01f, +8.690691405e-01f, -5.935593714e-02f },
+    { -1.002077218e-02f, +2.018111936e-01f, +8.693151841e-01f, -5.934943969e-02f },
+    { -1.000155098e-02f, +2.015358594e-01f, +8.695610368e-01f, -5.934286482e-02f },
+    { -9.982350225e-03f, +2.012606592e-01f, +8.698066982e-01f, -5.933621243e-02f },
+    { -9.963169910e-03f, +2.009855930e-01f, +8.700521683e-01f, -5.932948245e-02f },
+    { -9.944010038e-03f, +2.007106609e-01f, +8.702974470e-01f, -5.932267479e-02f },
+    { -9.924870610e-03f, +2.004358631e-01f, +8.705425340e-01f, -5.931578936e-02f },
+    { -9.905751626e-03f, +2.001611994e-01f, +8.707874293e-01f, -5.930882608e-02f },
+    { -9.886653086e-03f, +1.998866701e-01f, +8.710321326e-01f, -5.930178487e-02f },
+    { -9.867574990e-03f, +1.996122753e-01f, +8.712766439e-01f, -5.929466565e-02f },
+    { -9.848517340e-03f, +1.993380149e-01f, +8.715209630e-01f, -5.928746832e-02f },
+    { -9.829480135e-03f, +1.990638891e-01f, +8.717650897e-01f, -5.928019281e-02f },
+    { -9.810463375e-03f, +1.987898979e-01f, +8.720090239e-01f, -5.927283903e-02f },
+    { -9.791467061e-03f, +1.985160414e-01f, +8.722527654e-01f, -5.926540690e-02f },
+    { -9.772491192e-03f, +1.982423197e-01f, +8.724963141e-01f, -5.925789634e-02f },
+    { -9.753535770e-03f, +1.979687329e-01f, +8.727396699e-01f, -5.925030726e-02f },
+    { -9.734600793e-03f, +1.976952810e-01f, +8.729828325e-01f, -5.924263957e-02f },
+    { -9.715686262e-03f, +1.974219642e-01f, +8.732258019e-01f, -5.923489319e-02f },
+    { -9.696792177e-03f, +1.971487824e-01f, +8.734685779e-01f, -5.922706804e-02f },
+    { -9.677918538e-03f, +1.968757357e-01f, +8.737111603e-01f, -5.921916404e-02f },
+    { -9.659065344e-03f, +1.966028244e-01f, +8.739535491e-01f, -5.921118110e-02f },
+    { -9.640232597e-03f, +1.963300483e-01f, +8.741957439e-01f, -5.920311914e-02f },
+    { -9.621420295e-03f, +1.960574076e-01f, +8.744377448e-01f, -5.919497807e-02f },
+    { -9.602628439e-03f, +1.957849023e-01f, +8.746795516e-01f, -5.918675781e-02f },
+    { -9.583857028e-03f, +1.955125326e-01f, +8.749211640e-01f, -5.917845827e-02f },
+    { -9.565106062e-03f, +1.952402985e-01f, +8.751625821e-01f, -5.917007938e-02f },
+    { -9.546375540e-03f, +1.949682000e-01f, +8.754038055e-01f, -5.916162104e-02f },
+    { -9.527665464e-03f, +1.946962373e-01f, +8.756448342e-01f, -5.915308317e-02f },
+    { -9.508975831e-03f, +1.944244105e-01f, +8.758856680e-01f, -5.914446570e-02f },
+    { -9.490306643e-03f, +1.941527195e-01f, +8.761263068e-01f, -5.913576853e-02f },
+    { -9.471657898e-03f, +1.938811645e-01f, +8.763667504e-01f, -5.912699159e-02f },
+    { -9.453029596e-03f, +1.936097455e-01f, +8.766069987e-01f, -5.911813478e-02f },
+    { -9.434421737e-03f, +1.933384626e-01f, +8.768470516e-01f, -5.910919802e-02f },
+    { -9.415834320e-03f, +1.930673159e-01f, +8.770869088e-01f, -5.910018124e-02f },
+    { -9.397267344e-03f, +1.927963055e-01f, +8.773265703e-01f, -5.909108434e-02f },
+    { -9.378720810e-03f, +1.925254314e-01f, +8.775660358e-01f, -5.908190725e-02f },
+    { -9.360194716e-03f, +1.922546937e-01f, +8.778053053e-01f, -5.907264987e-02f },
+    { -9.341689062e-03f, +1.919840925e-01f, +8.780443786e-01f, -5.906331213e-02f },
+    { -9.323203848e-03f, +1.917136278e-01f, +8.782832556e-01f, -5.905389393e-02f },
+    { -9.304739072e-03f, +1.914432997e-01f, +8.785219361e-01f, -5.904439521e-02f },
+    { -9.286294734e-03f, +1.911731083e-01f, +8.787604199e-01f, -5.903481587e-02f },
+    { -9.267870833e-03f, +1.909030537e-01f, +8.789987070e-01f, -5.902515582e-02f },
+    { -9.249467369e-03f, +1.906331359e-01f, +8.792367971e-01f, -5.901541499e-02f },
+    { -9.231084340e-03f, +1.903633550e-01f, +8.794746902e-01f, -5.900559330e-02f },
+    { -9.212721746e-03f, +1.900937110e-01f, +8.797123860e-01f, -5.899569065e-02f },
+    { -9.194379586e-03f, +1.898242041e-01f, +8.799498845e-01f, -5.898570696e-02f },
+    { -9.176057859e-03f, +1.895548343e-01f, +8.801871854e-01f, -5.897564215e-02f },
+    { -9.157756564e-03f, +1.892856017e-01f, +8.804242888e-01f, -5.896549614e-02f },
+    { -9.139475700e-03f, +1.890165064e-01f, +8.806611943e-01f, -5.895526884e-02f },
+    { -9.121215266e-03f, +1.887475484e-01f, +8.808979018e-01f, -5.894496016e-02f },
+    { -9.102975262e-03f, +1.884787277e-01f, +8.811344113e-01f, -5.893457003e-02f },
+    { -9.084755685e-03f, +1.882100446e-01f, +8.813707226e-01f, -5.892409837e-02f },
+    { -9.066556536e-03f, +1.879414989e-01f, +8.816068354e-01f, -5.891354507e-02f },
+    { -9.048377812e-03f, +1.876730909e-01f, +8.818427498e-01f, -5.890291007e-02f },
+    { -9.030219514e-03f, +1.874048205e-01f, +8.820784655e-01f, -5.889219328e-02f },
+    { -9.012081638e-03f, +1.871366879e-01f, +8.823139824e-01f, -5.888139461e-02f },
+    { -8.993964185e-03f, +1.868686931e-01f, +8.825493003e-01f, -5.887051398e-02f },
+    { -8.975867154e-03f, +1.866008361e-01f, +8.827844191e-01f, -5.885955131e-02f },
+    { -8.957790542e-03f, +1.863331172e-01f, +8.830193387e-01f, -5.884850651e-02f },
+    { -8.939734348e-03f, +1.860655362e-01f, +8.832540589e-01f, -5.883737950e-02f },
+    { -8.921698572e-03f, +1.857980933e-01f, +8.834885795e-01f, -5.882617019e-02f },
+    { -8.903683211e-03f, +1.855307886e-01f, +8.837229005e-01f, -5.881487851e-02f },
+    { -8.885688265e-03f, +1.852636221e-01f, +8.839570217e-01f, -5.880350436e-02f },
+    { -8.867713732e-03f, +1.849965939e-01f, +8.841909429e-01f, -5.879204767e-02f },
+    { -8.849759611e-03f, +1.847297040e-01f, +8.844246640e-01f, -5.878050834e-02f },
+    { -8.831825899e-03f, +1.844629526e-01f, +8.846581849e-01f, -5.876888630e-02f },
+    { -8.813912596e-03f, +1.841963397e-01f, +8.848915054e-01f, -5.875718146e-02f },
+    { -8.796019700e-03f, +1.839298653e-01f, +8.851246253e-01f, -5.874539374e-02f },
+    { -8.778147210e-03f, +1.836635296e-01f, +8.853575446e-01f, -5.873352306e-02f },
+    { -8.760295123e-03f, +1.833973325e-01f, +8.855902631e-01f, -5.872156932e-02f },
+    { -8.742463438e-03f, +1.831312743e-01f, +8.858227806e-01f, -5.870953245e-02f },
+    { -8.724652154e-03f, +1.828653548e-01f, +8.860550970e-01f, -5.869741236e-02f },
+    { -8.706861268e-03f, +1.825995743e-01f, +8.862872121e-01f, -5.868520898e-02f },
+    { -8.689090780e-03f, +1.823339328e-01f, +8.865191259e-01f, -5.867292220e-02f },
+    { -8.671340686e-03f, +1.820684302e-01f, +8.867508381e-01f, -5.866055196e-02f },
+    { -8.653610986e-03f, +1.818030668e-01f, +8.869823487e-01f, -5.864809816e-02f },
+    { -8.635901678e-03f, +1.815378426e-01f, +8.872136574e-01f, -5.863556073e-02f },
+    { -8.618212759e-03f, +1.812727576e-01f, +8.874447642e-01f, -5.862293958e-02f },
+    { -8.600544229e-03f, +1.810078118e-01f, +8.876756689e-01f, -5.861023462e-02f },
+    { -8.582896084e-03f, +1.807430055e-01f, +8.879063714e-01f, -5.859744577e-02f },
+    { -8.565268323e-03f, +1.804783386e-01f, +8.881368715e-01f, -5.858457295e-02f },
+    { -8.547660944e-03f, +1.802138112e-01f, +8.883671691e-01f, -5.857161608e-02f },
+    { -8.530073945e-03f, +1.799494234e-01f, +8.885972640e-01f, -5.855857506e-02f },
+    { -8.512507324e-03f, +1.796851751e-01f, +8.888271561e-01f, -5.854544983e-02f },
+    { -8.494961078e-03f, +1.794210666e-01f, +8.890568452e-01f, -5.853224028e-02f },
+    { -8.477435207e-03f, +1.791570979e-01f, +8.892863313e-01f, -5.851894634e-02f },
+    { -8.459929707e-03f, +1.788932690e-01f, +8.895156142e-01f, -5.850556793e-02f },
+    { -8.442444576e-03f, +1.786295800e-01f, +8.897446936e-01f, -5.849210496e-02f },
+    { -8.424979813e-03f, +1.783660309e-01f, +8.899735696e-01f, -5.847855735e-02f },
+    { -8.407535414e-03f, +1.781026219e-01f, +8.902022419e-01f, -5.846492501e-02f },
+    { -8.390111379e-03f, +1.778393529e-01f, +8.904307105e-01f, -5.845120786e-02f },
+    { -8.372707704e-03f, +1.775762242e-01f, +8.906589751e-01f, -5.843740582e-02f },
+    { -8.355324387e-03f, +1.773132356e-01f, +8.908870357e-01f, -5.842351880e-02f },
+    { -8.337961425e-03f, +1.770503873e-01f, +8.911148920e-01f, -5.840954672e-02f },
+    { -8.320618817e-03f, +1.767876794e-01f, +8.913425440e-01f, -5.839548949e-02f },
+    { -8.303296561e-03f, +1.765251119e-01f, +8.915699915e-01f, -5.838134704e-02f },
+    { -8.285994652e-03f, +1.762626848e-01f, +8.917972344e-01f, -5.836711927e-02f },
+    { -8.268713090e-03f, +1.760003983e-01f, +8.920242725e-01f, -5.835280610e-02f },
+    { -8.251451871e-03f, +1.757382524e-01f, +8.922511057e-01f, -5.833840746e-02f },
+    { -8.234210994e-03f, +1.754762472e-01f, +8.924777339e-01f, -5.832392325e-02f },
+    { -8.216990455e-03f, +1.752143827e-01f, +8.927041569e-01f, -5.830935339e-02f },
+    { -8.199790252e-03f, +1.749526590e-01f, +8.929303746e-01f, -5.829469780e-02f },
+    { -8.182610382e-03f, +1.746910762e-01f, +8.931563868e-01f, -5.827995640e-02f },
+    { -8.165450843e-03f, +1.744296343e-01f, +8.933821934e-01f, -5.826512910e-02f },
+    { -8.148311631e-03f, +1.741683333e-01f, +8.936077942e-01f, -5.825021582e-02f },
+    { -8.131192745e-03f, +1.739071734e-01f, +8.938331892e-01f, -5.823521647e-02f },
+    { -8.114094182e-03f, +1.736461547e-01f, +8.940583782e-01f, -5.822013097e-02f },
+    { -8.097015938e-03f, +1.733852771e-01f, +8.942833610e-01f, -5.820495924e-02f },
+    { -8.079958012e-03f, +1.731245407e-01f, +8.945081375e-01f, -5.818970119e-02f },
+    { -8.062920399e-03f, +1.728639457e-01f, +8.947327076e-01f, -5.817435673e-02f },
+    { -8.045903098e-03f, +1.726034920e-01f, +8.949570711e-01f, -5.815892580e-02f },
+    { -8.028906105e-03f, +1.723431797e-01f, +8.951812279e-01f, -5.814340830e-02f },
+    { -8.011929418e-03f, +1.720830089e-01f, +8.954051779e-01f, -5.812780414e-02f },
+    { -7.994973033e-03f, +1.718229797e-01f, +8.956289209e-01f, -5.811211325e-02f },
+    { -7.978036948e-03f, +1.715630920e-01f, +8.958524568e-01f, -5.809633554e-02f },
+    { -7.961121159e-03f, +1.713033461e-01f, +8.960757854e-01f, -5.808047093e-02f },
+    { -7.944225664e-03f, +1.710437419e-01f, +8.962989066e-01f, -5.806451933e-02f },
+    { -7.927350459e-03f, +1.707842794e-01f, +8.965218203e-01f, -5.804848066e-02f },
+    { -7.910495542e-03f, +1.705249588e-01f, +8.967445263e-01f, -5.803235484e-02f },
+    { -7.893660909e-03f, +1.702657802e-01f, +8.969670245e-01f, -5.801614178e-02f },
+    { -7.876846558e-03f, +1.700067435e-01f, +8.971893148e-01f, -5.799984140e-02f },
+    { -7.860052484e-03f, +1.697478489e-01f, +8.974113969e-01f, -5.798345361e-02f },
+    { -7.843278685e-03f, +1.694890963e-01f, +8.976332709e-01f, -5.796697834e-02f },
+    { -7.826525157e-03f, +1.692304860e-01f, +8.978549365e-01f, -5.795041550e-02f },
+    { -7.809791898e-03f, +1.689720178e-01f, +8.980763937e-01f, -5.793376500e-02f },
+    { -7.793078904e-03f, +1.687136919e-01f, +8.982976422e-01f, -5.791702676e-02f },
+    { -7.776386171e-03f, +1.684555084e-01f, +8.985186819e-01f, -5.790020070e-02f },
+    { -7.759713697e-03f, +1.681974672e-01f, +8.987395128e-01f, -5.788328673e-02f },
+    { -7.743061477e-03f, +1.679395686e-01f, +8.989601346e-01f, -5.786628478e-02f },
+    { -7.726429509e-03f, +1.676818124e-01f, +8.991805472e-01f, -5.784919475e-02f },
+    { -7.709817790e-03f, +1.674241988e-01f, +8.994007506e-01f, -5.783201657e-02f },
+    { -7.693226314e-03f, +1.671667279e-01f, +8.996207445e-01f, -5.781475014e-02f },
+    { -7.676655080e-03f, +1.669093997e-01f, +8.998405288e-01f, -5.779739540e-02f },
+    { -7.660104084e-03f, +1.666522142e-01f, +9.000601034e-01f, -5.777995224e-02f },
+    { -7.643573321e-03f, +1.663951716e-01f, +9.002794682e-01f, -5.776242060e-02f },
+    { -7.627062789e-03f, +1.661382718e-01f, +9.004986230e-01f, -5.774480038e-02f },
+    { -7.610572484e-03f, +1.658815150e-01f, +9.007175676e-01f, -5.772709151e-02f },
+    { -7.594102403e-03f, +1.656249011e-01f, +9.009363021e-01f, -5.770929389e-02f },
+    { -7.577652540e-03f, +1.653684303e-01f, +9.011548261e-01f, -5.769140745e-02f },
+    { -7.561222894e-03f, +1.651121027e-01f, +9.013731396e-01f, -5.767343210e-02f },
+    { -7.544813460e-03f, +1.648559182e-01f, +9.015912425e-01f, -5.765536777e-02f },
+    { -7.528424234e-03f, +1.645998769e-01f, +9.018091345e-01f, -5.763721435e-02f },
+    { -7.512055213e-03f, +1.643439789e-01f, +9.020268157e-01f, -5.761897178e-02f },
+    { -7.495706392e-03f, +1.640882243e-01f, +9.022442858e-01f, -5.760063997e-02f },
+    { -7.479377769e-03f, +1.638326131e-01f, +9.024615447e-01f, -5.758221883e-02f },
+    { -7.463069339e-03f, +1.635771453e-01f, +9.026785922e-01f, -5.756370829e-02f },
+    { -7.446781098e-03f, +1.633218211e-01f, +9.028954283e-01f, -5.754510825e-02f },
+    { -7.430513042e-03f, +1.630666404e-01f, +9.031120528e-01f, -5.752641864e-02f },
+    { -7.414265167e-03f, +1.628116034e-01f, +9.033284656e-01f, -5.750763937e-02f },
+    { -7.398037471e-03f, +1.625567100e-01f, +9.035446666e-01f, -5.748877036e-02f },
+    { -7.381829947e-03f, +1.623019604e-01f, +9.037606555e-01f, -5.746981153e-02f },
+    { -7.365642593e-03f, +1.620473546e-01f, +9.039764323e-01f, -5.745076279e-02f },
+    { -7.349475404e-03f, +1.617928927e-01f, +9.041919969e-01f, -5.743162405e-02f },
+    { -7.333328376e-03f, +1.615385747e-01f, +9.044073490e-01f, -5.741239524e-02f },
+    { -7.317201506e-03f, +1.612844007e-01f, +9.046224887e-01f, -5.739307627e-02f },
+    { -7.301094788e-03f, +1.610303707e-01f, +9.048374156e-01f, -5.737366706e-02f },
+    { -7.285008220e-03f, +1.607764848e-01f, +9.050521298e-01f, -5.735416753e-02f },
+    { -7.268941796e-03f, +1.605227430e-01f, +9.052666311e-01f, -5.733457759e-02f },
+    { -7.252895512e-03f, +1.602691455e-01f, +9.054809193e-01f, -5.731489716e-02f },
+    { -7.236869364e-03f, +1.600156922e-01f, +9.056949943e-01f, -5.729512615e-02f },
+    { -7.220863348e-03f, +1.597623832e-01f, +9.059088560e-01f, -5.727526448e-02f },
+    { -7.204877460e-03f, +1.595092186e-01f, +9.061225043e-01f, -5.725531208e-02f },
+    { -7.188911695e-03f, +1.592561984e-01f, +9.063359389e-01f, -5.723526885e-02f },
+    { -7.172966049e-03f, +1.590033227e-01f, +9.065491599e-01f, -5.721513471e-02f },
+    { -7.157040517e-03f, +1.587505915e-01f, +9.067621670e-01f, -5.719490958e-02f },
+    { -7.141135096e-03f, +1.584980049e-01f, +9.069749601e-01f, -5.717459338e-02f },
+    { -7.125249780e-03f, +1.582455630e-01f, +9.071875391e-01f, -5.715418602e-02f },
+    { -7.109384565e-03f, +1.579932657e-01f, +9.073999039e-01f, -5.713368743e-02f },
+    { -7.093539446e-03f, +1.577411133e-01f, +9.076120543e-01f, -5.711309750e-02f },
+    { -7.077714420e-03f, +1.574891056e-01f, +9.078239902e-01f, -5.709241618e-02f },
+    { -7.061909482e-03f, +1.572372428e-01f, +9.080357115e-01f, -5.707164336e-02f },
+    { -7.046124626e-03f, +1.569855249e-01f, +9.082472180e-01f, -5.705077897e-02f },
+    { -7.030359849e-03f, +1.567339520e-01f, +9.084585096e-01f, -5.702982292e-02f },
+    { -7.014615145e-03f, +1.564825241e-01f, +9.086695861e-01f, -5.700877514e-02f },
+    { -6.998890510e-03f, +1.562312413e-01f, +9.088804476e-01f, -5.698763554e-02f },
+    { -6.983185940e-03f, +1.559801037e-01f, +9.090910937e-01f, -5.696640402e-02f },
+    { -6.967501430e-03f, +1.557291112e-01f, +9.093015244e-01f, -5.694508053e-02f },
+    { -6.951836974e-03f, +1.554782640e-01f, +9.095117396e-01f, -5.692366496e-02f },
+    { -6.936192569e-03f, +1.552275620e-01f, +9.097217391e-01f, -5.690215723e-02f },
+    { -6.920568209e-03f, +1.549770055e-01f, +9.099315228e-01f, -5.688055727e-02f },
+    { -6.904963889e-03f, +1.547265943e-01f, +9.101410905e-01f, -5.685886499e-02f },
+    { -6.889379605e-03f, +1.544763286e-01f, +9.103504422e-01f, -5.683708031e-02f },
+    { -6.873815352e-03f, +1.542262084e-01f, +9.105595777e-01f, -5.681520314e-02f },
+    { -6.858271125e-03f, +1.539762337e-01f, +9.107684969e-01f, -5.679323340e-02f },
+    { -6.842746919e-03f, +1.537264047e-01f, +9.109771996e-01f, -5.677117101e-02f },
+    { -6.827242728e-03f, +1.534767213e-01f, +9.111856858e-01f, -5.674901589e-02f },
+    { -6.811758549e-03f, +1.532271837e-01f, +9.113939552e-01f, -5.672676795e-02f },
+    { -6.796294376e-03f, +1.529777918e-01f, +9.116020078e-01f, -5.670442710e-02f },
+    { -6.780850204e-03f, +1.527285458e-01f, +9.118098434e-01f, -5.668199328e-02f },
+    { -6.765426027e-03f, +1.524794457e-01f, +9.120174619e-01f, -5.665946638e-02f },
+    { -6.750021842e-03f, +1.522304914e-01f, +9.122248632e-01f, -5.663684634e-02f },
+    { -6.734637642e-03f, +1.519816832e-01f, +9.124320471e-01f, -5.661413307e-02f },
+    { -6.719273423e-03f, +1.517330210e-01f, +9.126390136e-01f, -5.659132648e-02f },
+    { -6.703929179e-03f, +1.514845048e-01f, +9.128457625e-01f, -5.656842649e-02f },
+    { -6.688604905e-03f, +1.512361348e-01f, +9.130522936e-01f, -5.654543302e-02f },
+    { -6.673300597e-03f, +1.509879110e-01f, +9.132586068e-01f, -5.652234599e-02f },
+    { -6.658016248e-03f, +1.507398335e-01f, +9.134647021e-01f, -5.649916531e-02f },
+    { -6.642751853e-03f, +1.504919022e-01f, +9.136705792e-01f, -5.647589090e-02f },
+    { -6.627507408e-03f, +1.502441173e-01f, +9.138762381e-01f, -5.645252268e-02f },
+    { -6.612282907e-03f, +1.499964787e-01f, +9.140816786e-01f, -5.642906057e-02f },
+    { -6.597078344e-03f, +1.497489866e-01f, +9.142869006e-01f, -5.640550448e-02f },
+    { -6.581893714e-03f, +1.495016410e-01f, +9.144919040e-01f, -5.638185433e-02f },
+    { -6.566729012e-03f, +1.492544420e-01f, +9.146966886e-01f, -5.635811004e-02f },
+    { -6.551584232e-03f, +1.490073895e-01f, +9.149012543e-01f, -5.633427152e-02f },
+    { -6.536459369e-03f, +1.487604837e-01f, +9.151056011e-01f, -5.631033869e-02f },
+    { -6.521354417e-03f, +1.485137245e-01f, +9.153097287e-01f, -5.628631147e-02f },
+    { -6.506269371e-03f, +1.482671122e-01f, +9.155136370e-01f, -5.626218978e-02f },
+    { -6.491204225e-03f, +1.480206466e-01f, +9.157173259e-01f, -5.623797353e-02f },
+    { -6.476158974e-03f, +1.477743278e-01f, +9.159207954e-01f, -5.621366265e-02f },
+    { -6.461133612e-03f, +1.475281560e-01f, +9.161240451e-01f, -5.618925704e-02f },
+    { -6.446128134e-03f, +1.472821311e-01f, +9.163270752e-01f, -5.616475663e-02f },
+    { -6.431142534e-03f, +1.470362531e-01f, +9.165298853e-01f, -5.614016133e-02f },
+    { -6.416176806e-03f, +1.467905223e-01f, +9.167324754e-01f, -5.611547107e-02f },
+    { -6.401230944e-03f, +1.465449385e-01f, +9.169348454e-01f, -5.609068575e-02f },
+    { -6.386304943e-03f, +1.462995018e-01f, +9.171369950e-01f, -5.606580530e-02f },
+    { -6.371398797e-03f, +1.460542124e-01f, +9.173389243e-01f, -5.604082963e-02f },
+    { -6.356512501e-03f, +1.458090702e-01f, +9.175406331e-01f, -5.601575866e-02f },
+    { -6.341646048e-03f, +1.455640752e-01f, +9.177421213e-01f, -5.599059231e-02f },
+    { -6.326799433e-03f, +1.453192276e-01f, +9.179433886e-01f, -5.596533050e-02f },
+    { -6.311972650e-03f, +1.450745274e-01f, +9.181444351e-01f, -5.593997314e-02f },
+    { -6.297165693e-03f, +1.448299746e-01f, +9.183452606e-01f, -5.591452015e-02f },
+    { -6.282378556e-03f, +1.445855693e-01f, +9.185458649e-01f, -5.588897145e-02f },
+    { -6.267611233e-03f, +1.443413115e-01f, +9.187462479e-01f, -5.586332696e-02f },
+    { -6.252863718e-03f, +1.440972013e-01f, +9.189464096e-01f, -5.583758659e-02f },
+    { -6.238136006e-03f, +1.438532387e-01f, +9.191463497e-01f, -5.581175026e-02f },
+    { -6.223428090e-03f, +1.436094238e-01f, +9.193460682e-01f, -5.578581789e-02f },
+    { -6.208739964e-03f, +1.433657566e-01f, +9.195455649e-01f, -5.575978940e-02f },
+    { -6.194071622e-03f, +1.431222371e-01f, +9.197448398e-01f, -5.573366470e-02f },
+    { -6.179423059e-03f, +1.428788655e-01f, +9.199438926e-01f, -5.570744371e-02f },
+    { -6.164794268e-03f, +1.426356417e-01f, +9.201427233e-01f, -5.568112636e-02f },
+    { -6.150185242e-03f, +1.423925658e-01f, +9.203413317e-01f, -5.565471255e-02f },
+    { -6.135595977e-03f, +1.421496379e-01f, +9.205397178e-01f, -5.562820221e-02f },
+    { -6.121026465e-03f, +1.419068580e-01f, +9.207378813e-01f, -5.560159525e-02f },
+    { -6.106476701e-03f, +1.416642261e-01f, +9.209358223e-01f, -5.557489159e-02f },
+    { -6.091946678e-03f, +1.414217423e-01f, +9.211335405e-01f, -5.554809115e-02f },
+    { -6.077436389e-03f, +1.411794066e-01f, +9.213310358e-01f, -5.552119385e-02f },
+    { -6.062945830e-03f, +1.409372192e-01f, +9.215283081e-01f, -5.549419960e-02f },
+    { -6.048474993e-03f, +1.406951799e-01f, +9.217253573e-01f, -5.546710832e-02f },
+    { -6.034023872e-03f, +1.404532889e-01f, +9.219221832e-01f, -5.543991994e-02f },
+    { -6.019592461e-03f, +1.402115463e-01f, +9.221187858e-01f, -5.541263436e-02f },
+    { -6.005180754e-03f, +1.399699520e-01f, +9.223151649e-01f, -5.538525151e-02f },
+    { -5.990788743e-03f, +1.397285061e-01f, +9.225113204e-01f, -5.535777130e-02f },
+    { -5.976416423e-03f, +1.394872087e-01f, +9.227072522e-01f, -5.533019365e-02f },
+    { -5.962063788e-03f, +1.392460598e-01f, +9.229029601e-01f, -5.530251848e-02f },
+    { -5.947730830e-03f, +1.390050594e-01f, +9.230984441e-01f, -5.527474572e-02f },
+    { -5.933417543e-03f, +1.387642076e-01f, +9.232937039e-01f, -5.524687526e-02f },
+    { -5.919123921e-03f, +1.385235045e-01f, +9.234887396e-01f, -5.521890704e-02f },
+    { -5.904849957e-03f, +1.382829500e-01f, +9.236835509e-01f, -5.519084097e-02f },
+    { -5.890595645e-03f, +1.380425443e-01f, +9.238781378e-01f, -5.516267698e-02f },
+    { -5.876360977e-03f, +1.378022873e-01f, +9.240725001e-01f, -5.513441497e-02f },
+    { -5.862145949e-03f, +1.375621792e-01f, +9.242666377e-01f, -5.510605486e-02f },
+    { -5.847950552e-03f, +1.373222199e-01f, +9.244605504e-01f, -5.507759658e-02f },
+    { -5.833774780e-03f, +1.370824095e-01f, +9.246542383e-01f, -5.504904005e-02f },
+    { -5.819618626e-03f, +1.368427480e-01f, +9.248477010e-01f, -5.502038517e-02f },
+    { -5.805482085e-03f, +1.366032356e-01f, +9.250409386e-01f, -5.499163187e-02f },
+    { -5.791365148e-03f, +1.363638722e-01f, +9.252339509e-01f, -5.496278007e-02f },
+    { -5.777267810e-03f, +1.361246578e-01f, +9.254267378e-01f, -5.493382968e-02f },
+    { -5.763190063e-03f, +1.358855926e-01f, +9.256192991e-01f, -5.490478063e-02f },
+    { -5.749131901e-03f, +1.356466765e-01f, +9.258116348e-01f, -5.487563282e-02f },
+    { -5.735093317e-03f, +1.354079097e-01f, +9.260037447e-01f, -5.484638619e-02f },
+    { -5.721074303e-03f, +1.351692920e-01f, +9.261956287e-01f, -5.481704064e-02f },
+    { -5.707074854e-03f, +1.349308237e-01f, +9.263872867e-01f, -5.478759611e-02f },
+    { -5.693094963e-03f, +1.346925048e-01f, +9.265787186e-01f, -5.475805249e-02f },
+    { -5.679134621e-03f, +1.344543351e-01f, +9.267699242e-01f, -5.472840972e-02f },
+    { -5.665193823e-03f, +1.342163150e-01f, +9.269609034e-01f, -5.469866771e-02f },
+    { -5.651272561e-03f, +1.339784442e-01f, +9.271516562e-01f, -5.466882638e-02f },
+    { -5.637370829e-03f, +1.337407230e-01f, +9.273421823e-01f, -5.463888565e-02f },
+    { -5.623488619e-03f, +1.335031514e-01f, +9.275324817e-01f, -5.460884543e-02f },
+    { -5.609625925e-03f, +1.332657293e-01f, +9.277225543e-01f, -5.457870565e-02f },
+    { -5.595782739e-03f, +1.330284568e-01f, +9.279123998e-01f, -5.454846622e-02f },
+    { -5.581959054e-03f, +1.327913341e-01f, +9.281020184e-01f, -5.451812706e-02f },
+    { -5.568154863e-03f, +1.325543610e-01f, +9.282914097e-01f, -5.448768810e-02f },
+    { -5.554370159e-03f, +1.323175377e-01f, +9.284805736e-01f, -5.445714924e-02f },
+    { -5.540604935e-03f, +1.320808642e-01f, +9.286695102e-01f, -5.442651041e-02f },
+    { -5.526859184e-03f, +1.318443405e-01f, +9.288582192e-01f, -5.439577153e-02f },
+    { -5.513132898e-03f, +1.316079667e-01f, +9.290467005e-01f, -5.436493251e-02f },
+    { -5.499426070e-03f, +1.313717428e-01f, +9.292349540e-01f, -5.433399327e-02f },
+    { -5.485738694e-03f, +1.311356689e-01f, +9.294229797e-01f, -5.430295374e-02f },
+    { -5.472070761e-03f, +1.308997450e-01f, +9.296107773e-01f, -5.427181382e-02f },
+    { -5.458422264e-03f, +1.306639711e-01f, +9.297983467e-01f, -5.424057345e-02f },
+    { -5.444793197e-03f, +1.304283474e-01f, +9.299856879e-01f, -5.420923253e-02f },
+    { -5.431183551e-03f, +1.301928737e-01f, +9.301728008e-01f, -5.417779099e-02f },
+    { -5.417593320e-03f, +1.299575502e-01f, +9.303596851e-01f, -5.414624875e-02f },
+    { -5.404022496e-03f, +1.297223769e-01f, +9.305463408e-01f, -5.411460571e-02f },
+    { -5.390471071e-03f, +1.294873538e-01f, +9.307327679e-01f, -5.408286181e-02f },
+    { -5.376939039e-03f, +1.292524811e-01f, +9.309189660e-01f, -5.405101697e-02f },
+    { -5.363426391e-03f, +1.290177586e-01f, +9.311049353e-01f, -5.401907109e-02f },
+    { -5.349933121e-03f, +1.287831865e-01f, +9.312906754e-01f, -5.398702410e-02f },
+    { -5.336459220e-03f, +1.285487648e-01f, +9.314761864e-01f, -5.395487592e-02f },
+    { -5.323004682e-03f, +1.283144936e-01f, +9.316614680e-01f, -5.392262647e-02f },
+    { -5.309569498e-03f, +1.280803728e-01f, +9.318465203e-01f, -5.389027566e-02f },
+    { -5.296153661e-03f, +1.278464026e-01f, +9.320313430e-01f, -5.385782342e-02f },
+    { -5.282757164e-03f, +1.276125829e-01f, +9.322159361e-01f, -5.382526966e-02f },
+    { -5.269379998e-03f, +1.273789138e-01f, +9.324002994e-01f, -5.379261431e-02f },
+    { -5.256022157e-03f, +1.271453953e-01f, +9.325844328e-01f, -5.375985728e-02f },
+    { -5.242683633e-03f, +1.269120275e-01f, +9.327683363e-01f, -5.372699848e-02f },
+    { -5.229364417e-03f, +1.266788105e-01f, +9.329520096e-01f, -5.369403785e-02f },
+    { -5.216064503e-03f, +1.264457441e-01f, +9.331354527e-01f, -5.366097530e-02f },
+    { -5.202783882e-03f, +1.262128286e-01f, +9.333186655e-01f, -5.362781074e-02f },
+    { -5.189522547e-03f, +1.259800639e-01f, +9.335016478e-01f, -5.359454410e-02f },
+    { -5.176280490e-03f, +1.257474501e-01f, +9.336843996e-01f, -5.356117530e-02f },
+    { -5.163057703e-03f, +1.255149871e-01f, +9.338669207e-01f, -5.352770425e-02f },
+    { -5.149854178e-03f, +1.252826751e-01f, +9.340492110e-01f, -5.349413088e-02f },
+    { -5.136669908e-03f, +1.250505141e-01f, +9.342312704e-01f, -5.346045510e-02f },
+    { -5.123504885e-03f, +1.248185041e-01f, +9.344130987e-01f, -5.342667683e-02f },
+    { -5.110359100e-03f, +1.245866451e-01f, +9.345946960e-01f, -5.339279600e-02f },
+    { -5.097232546e-03f, +1.243549372e-01f, +9.347760620e-01f, -5.335881251e-02f },
+    { -5.084125216e-03f, +1.241233805e-01f, +9.349571966e-01f, -5.332472630e-02f },
+    { -5.071037100e-03f, +1.238919749e-01f, +9.351380998e-01f, -5.329053728e-02f },
+    { -5.057968191e-03f, +1.236607205e-01f, +9.353187714e-01f, -5.325624537e-02f },
+    { -5.044918481e-03f, +1.234296174e-01f, +9.354992113e-01f, -5.322185049e-02f },
+    { -5.031887963e-03f, +1.231986655e-01f, +9.356794194e-01f, -5.318735255e-02f },
+    { -5.018876627e-03f, +1.229678649e-01f, +9.358593955e-01f, -5.315275148e-02f },
+    { -5.005884466e-03f, +1.227372157e-01f, +9.360391397e-01f, -5.311804720e-02f },
+    { -4.992911472e-03f, +1.225067179e-01f, +9.362186517e-01f, -5.308323963e-02f },
+    { -4.979957637e-03f, +1.222763714e-01f, +9.363979314e-01f, -5.304832868e-02f },
+    { -4.967022953e-03f, +1.220461765e-01f, +9.365769788e-01f, -5.301331428e-02f },
+    { -4.954107411e-03f, +1.218161330e-01f, +9.367557936e-01f, -5.297819635e-02f },
+    { -4.941211003e-03f, +1.215862410e-01f, +9.369343759e-01f, -5.294297479e-02f },
+    { -4.928333721e-03f, +1.213565006e-01f, +9.371127255e-01f, -5.290764955e-02f },
+    { -4.915475558e-03f, +1.211269118e-01f, +9.372908423e-01f, -5.287222052e-02f },
+    { -4.902636504e-03f, +1.208974746e-01f, +9.374687261e-01f, -5.283668764e-02f },
+    { -4.889816551e-03f, +1.206681891e-01f, +9.376463769e-01f, -5.280105083e-02f },
+    { -4.877015692e-03f, +1.204390553e-01f, +9.378237946e-01f, -5.276531000e-02f },
+    { -4.864233917e-03f, +1.202100732e-01f, +9.380009790e-01f, -5.272946506e-02f },
+    { -4.851471219e-03f, +1.199812429e-01f, +9.381779301e-01f, -5.269351596e-02f },
+    { -4.838727590e-03f, +1.197525644e-01f, +9.383546476e-01f, -5.265746259e-02f },
+    { -4.826003020e-03f, +1.195240378e-01f, +9.385311316e-01f, -5.262130489e-02f },
+    { -4.813297501e-03f, +1.192956630e-01f, +9.387073818e-01f, -5.258504277e-02f },
+    { -4.800611026e-03f, +1.190674401e-01f, +9.388833983e-01f, -5.254867615e-02f },
+    { -4.787943585e-03f, +1.188393692e-01f, +9.390591808e-01f, -5.251220495e-02f },
+    { -4.775295171e-03f, +1.186114502e-01f, +9.392347294e-01f, -5.247562909e-02f },
+    { -4.762665774e-03f, +1.183836832e-01f, +9.394100437e-01f, -5.243894849e-02f },
+    { -4.750055387e-03f, +1.181560683e-01f, +9.395851238e-01f, -5.240216308e-02f },
+    { -4.737464000e-03f, +1.179286055e-01f, +9.397599696e-01f, -5.236527277e-02f },
+    { -4.724891606e-03f, +1.177012947e-01f, +9.399345809e-01f, -5.232827748e-02f },
+    { -4.712338195e-03f, +1.174741362e-01f, +9.401089576e-01f, -5.229117713e-02f },
+    { -4.699803759e-03f, +1.172471298e-01f, +9.402830997e-01f, -5.225397164e-02f },
+    { -4.687288290e-03f, +1.170202755e-01f, +9.404570069e-01f, -5.221666093e-02f },
+    { -4.674791779e-03f, +1.167935736e-01f, +9.406306792e-01f, -5.217924493e-02f },
+    { -4.662314217e-03f, +1.165670239e-01f, +9.408041166e-01f, -5.214172354e-02f },
+    { -4.649855595e-03f, +1.163406265e-01f, +9.409773188e-01f, -5.210409670e-02f },
+    { -4.637415906e-03f, +1.161143815e-01f, +9.411502858e-01f, -5.206636432e-02f },
+    { -4.624995140e-03f, +1.158882888e-01f, +9.413230174e-01f, -5.202852632e-02f },
+    { -4.612593288e-03f, +1.156623486e-01f, +9.414955136e-01f, -5.199058263e-02f },
+    { -4.600210342e-03f, +1.154365607e-01f, +9.416677743e-01f, -5.195253316e-02f },
+    { -4.587846293e-03f, +1.152109254e-01f, +9.418397993e-01f, -5.191437783e-02f },
+    { -4.575501133e-03f, +1.149854425e-01f, +9.420115885e-01f, -5.187611657e-02f },
+    { -4.563174852e-03f, +1.147601122e-01f, +9.421831418e-01f, -5.183774929e-02f },
+    { -4.550867441e-03f, +1.145349345e-01f, +9.423544592e-01f, -5.179927591e-02f },
+    { -4.538578892e-03f, +1.143099093e-01f, +9.425255405e-01f, -5.176069636e-02f },
+    { -4.526309197e-03f, +1.140850368e-01f, +9.426963856e-01f, -5.172201055e-02f },
+    { -4.514058345e-03f, +1.138603169e-01f, +9.428669944e-01f, -5.168321841e-02f },
+    { -4.501826329e-03f, +1.136357497e-01f, +9.430373667e-01f, -5.164431986e-02f },
+    { -4.489613138e-03f, +1.134113352e-01f, +9.432075026e-01f, -5.160531481e-02f },
+    { -4.477418765e-03f, +1.131870735e-01f, +9.433774019e-01f, -5.156620319e-02f },
+    { -4.465243201e-03f, +1.129629646e-01f, +9.435470644e-01f, -5.152698491e-02f },
+    { -4.453086436e-03f, +1.127390085e-01f, +9.437164901e-01f, -5.148765991e-02f },
+    { -4.440948461e-03f, +1.125152052e-01f, +9.438856788e-01f, -5.144822810e-02f },
+    { -4.428829267e-03f, +1.122915548e-01f, +9.440546305e-01f, -5.140868939e-02f },
+    { -4.416728846e-03f, +1.120680573e-01f, +9.442233450e-01f, -5.136904371e-02f },
+    { -4.404647188e-03f, +1.118447128e-01f, +9.443918223e-01f, -5.132929099e-02f },
+    { -4.392584284e-03f, +1.116215212e-01f, +9.445600623e-01f, -5.128943114e-02f },
+    { -4.380540125e-03f, +1.113984826e-01f, +9.447280647e-01f, -5.124946408e-02f },
+    { -4.368514702e-03f, +1.111755970e-01f, +9.448958296e-01f, -5.120938973e-02f },
+    { -4.356508006e-03f, +1.109528645e-01f, +9.450633568e-01f, -5.116920802e-02f },
+    { -4.344520028e-03f, +1.107302850e-01f, +9.452306462e-01f, -5.112891887e-02f },
+    { -4.332550758e-03f, +1.105078587e-01f, +9.453976978e-01f, -5.108852219e-02f },
+    { -4.320600187e-03f, +1.102855855e-01f, +9.455645113e-01f, -5.104801790e-02f },
+    { -4.308668306e-03f, +1.100634655e-01f, +9.457310868e-01f, -5.100740594e-02f },
+    { -4.296755107e-03f, +1.098414987e-01f, +9.458974241e-01f, -5.096668621e-02f },
+    { -4.284860578e-03f, +1.096196851e-01f, +9.460635230e-01f, -5.092585865e-02f },
+    { -4.272984712e-03f, +1.093980247e-01f, +9.462293836e-01f, -5.088492316e-02f },
+    { -4.261127499e-03f, +1.091765177e-01f, +9.463950056e-01f, -5.084387968e-02f },
+    { -4.249288929e-03f, +1.089551640e-01f, +9.465603890e-01f, -5.080272812e-02f },
+    { -4.237468994e-03f, +1.087339636e-01f, +9.467255337e-01f, -5.076146841e-02f },
+    { -4.225667684e-03f, +1.085129166e-01f, +9.468904396e-01f, -5.072010046e-02f },
+    { -4.213884989e-03f, +1.082920229e-01f, +9.470551065e-01f, -5.067862420e-02f },
+    { -4.202120901e-03f, +1.080712828e-01f, +9.472195345e-01f, -5.063703954e-02f },
+    { -4.190375409e-03f, +1.078506960e-01f, +9.473837233e-01f, -5.059534642e-02f },
+    { -4.178648505e-03f, +1.076302628e-01f, +9.475476728e-01f, -5.055354474e-02f },
+    { -4.166940178e-03f, +1.074099830e-01f, +9.477113830e-01f, -5.051163444e-02f },
+    { -4.155250420e-03f, +1.071898568e-01f, +9.478748538e-01f, -5.046961543e-02f },
+    { -4.143579220e-03f, +1.069698842e-01f, +9.480380851e-01f, -5.042748764e-02f },
+    { -4.131926571e-03f, +1.067500652e-01f, +9.482010767e-01f, -5.038525098e-02f },
+    { -4.120292461e-03f, +1.065303997e-01f, +9.483638285e-01f, -5.034290538e-02f },
+    { -4.108676881e-03f, +1.063108880e-01f, +9.485263405e-01f, -5.030045076e-02f },
+    { -4.097079822e-03f, +1.060915299e-01f, +9.486886126e-01f, -5.025788704e-02f },
+    { -4.085501274e-03f, +1.058723254e-01f, +9.488506446e-01f, -5.021521414e-02f },
+    { -4.073941228e-03f, +1.056532748e-01f, +9.490124364e-01f, -5.017243198e-02f },
+    { -4.062399673e-03f, +1.054343779e-01f, +9.491739880e-01f, -5.012954049e-02f },
+    { -4.050876601e-03f, +1.052156347e-01f, +9.493352993e-01f, -5.008653959e-02f },
+    { -4.039372002e-03f, +1.049970454e-01f, +9.494963700e-01f, -5.004342920e-02f },
+    { -4.027885865e-03f, +1.047786099e-01f, +9.496572003e-01f, -5.000020923e-02f },
+    { -4.016418182e-03f, +1.045603282e-01f, +9.498177898e-01f, -4.995687962e-02f },
+    { -4.004968942e-03f, +1.043422005e-01f, +9.499781386e-01f, -4.991344028e-02f },
+    { -3.993538136e-03f, +1.041242266e-01f, +9.501382466e-01f, -4.986989114e-02f },
+    { -3.982125754e-03f, +1.039064067e-01f, +9.502981136e-01f, -4.982623211e-02f },
+    { -3.970731786e-03f, +1.036887408e-01f, +9.504577395e-01f, -4.978246313e-02f },
+    { -3.959356223e-03f, +1.034712288e-01f, +9.506171243e-01f, -4.973858410e-02f },
+    { -3.947999054e-03f, +1.032538708e-01f, +9.507762678e-01f, -4.969459496e-02f },
+    { -3.936660271e-03f, +1.030366669e-01f, +9.509351700e-01f, -4.965049562e-02f },
+    { -3.925339862e-03f, +1.028196171e-01f, +9.510938306e-01f, -4.960628601e-02f },
+    { -3.914037818e-03f, +1.026027213e-01f, +9.512522498e-01f, -4.956196605e-02f },
+    { -3.902754129e-03f, +1.023859797e-01f, +9.514104273e-01f, -4.951753566e-02f },
+    { -3.891488786e-03f, +1.021693921e-01f, +9.515683630e-01f, -4.947299476e-02f },
+    { -3.880241778e-03f, +1.019529588e-01f, +9.517260568e-01f, -4.942834328e-02f },
+    { -3.869013095e-03f, +1.017366796e-01f, +9.518835087e-01f, -4.938358114e-02f },
+    { -3.857802728e-03f, +1.015205546e-01f, +9.520407186e-01f, -4.933870825e-02f },
+    { -3.846610666e-03f, +1.013045839e-01f, +9.521976863e-01f, -4.929372455e-02f },
+    { -3.835436900e-03f, +1.010887674e-01f, +9.523544118e-01f, -4.924862995e-02f },
+    { -3.824281419e-03f, +1.008731053e-01f, +9.525108949e-01f, -4.920342438e-02f },
+    { -3.813144214e-03f, +1.006575974e-01f, +9.526671355e-01f, -4.915810776e-02f },
+    { -3.802025274e-03f, +1.004422438e-01f, +9.528231336e-01f, -4.911268001e-02f },
+    { -3.790924590e-03f, +1.002270446e-01f, +9.529788891e-01f, -4.906714105e-02f },
+    { -3.779842151e-03f, +1.000119998e-01f, +9.531344018e-01f, -4.902149080e-02f },
+    { -3.768777946e-03f, +9.979710935e-02f, +9.532896717e-01f, -4.897572920e-02f },
+    { -3.757731967e-03f, +9.958237334e-02f, +9.534446987e-01f, -4.892985616e-02f },
+    { -3.746704203e-03f, +9.936779178e-02f, +9.535994826e-01f, -4.888387160e-02f },
+    { -3.735694643e-03f, +9.915336469e-02f, +9.537540234e-01f, -4.883777544e-02f },
+    { -3.724703278e-03f, +9.893909208e-02f, +9.539083209e-01f, -4.879156762e-02f },
+    { -3.713730097e-03f, +9.872497399e-02f, +9.540623752e-01f, -4.874524804e-02f },
+    { -3.702775091e-03f, +9.851101044e-02f, +9.542161860e-01f, -4.869881664e-02f },
+    { -3.691838248e-03f, +9.829720144e-02f, +9.543697533e-01f, -4.865227334e-02f },
+    { -3.680919559e-03f, +9.808354703e-02f, +9.545230770e-01f, -4.860561805e-02f },
+    { -3.670019013e-03f, +9.787004721e-02f, +9.546761570e-01f, -4.855885071e-02f },
+    { -3.659136601e-03f, +9.765670203e-02f, +9.548289931e-01f, -4.851197123e-02f },
+    { -3.648272311e-03f, +9.744351149e-02f, +9.549815854e-01f, -4.846497954e-02f },
+    { -3.637426133e-03f, +9.723047561e-02f, +9.551339337e-01f, -4.841787556e-02f },
+    { -3.626598058e-03f, +9.701759443e-02f, +9.552860379e-01f, -4.837065921e-02f },
+    { -3.615788075e-03f, +9.680486796e-02f, +9.554378979e-01f, -4.832333042e-02f },
+    { -3.604996173e-03f, +9.659229622e-02f, +9.555895136e-01f, -4.827588911e-02f },
+    { -3.594222342e-03f, +9.637987924e-02f, +9.557408849e-01f, -4.822833521e-02f },
+    { -3.583466571e-03f, +9.616761703e-02f, +9.558920118e-01f, -4.818066862e-02f },
+    { -3.572728851e-03f, +9.595550962e-02f, +9.560428941e-01f, -4.813288929e-02f },
+    { -3.562009171e-03f, +9.574355703e-02f, +9.561935317e-01f, -4.808499713e-02f },
+    { -3.551307520e-03f, +9.553175927e-02f, +9.563439245e-01f, -4.803699206e-02f },
+    { -3.540623888e-03f, +9.532011637e-02f, +9.564940726e-01f, -4.798887401e-02f },
+    { -3.529958264e-03f, +9.510862835e-02f, +9.566439756e-01f, -4.794064290e-02f },
+    { -3.519310638e-03f, +9.489729524e-02f, +9.567936336e-01f, -4.789229866e-02f },
+    { -3.508680999e-03f, +9.468611704e-02f, +9.569430465e-01f, -4.784384120e-02f },
+    { -3.498069337e-03f, +9.447509378e-02f, +9.570922142e-01f, -4.779527045e-02f },
+    { -3.487475641e-03f, +9.426422548e-02f, +9.572411365e-01f, -4.774658634e-02f },
+    { -3.476899901e-03f, +9.405351217e-02f, +9.573898135e-01f, -4.769778879e-02f },
+    { -3.466342106e-03f, +9.384295385e-02f, +9.575382449e-01f, -4.764887772e-02f },
+    { -3.455802245e-03f, +9.363255055e-02f, +9.576864307e-01f, -4.759985305e-02f },
+    { -3.445280308e-03f, +9.342230229e-02f, +9.578343708e-01f, -4.755071471e-02f },
+    { -3.434776284e-03f, +9.321220909e-02f, +9.579820651e-01f, -4.750146263e-02f },
+    { -3.424290163e-03f, +9.300227097e-02f, +9.581295136e-01f, -4.745209672e-02f },
+    { -3.413821934e-03f, +9.279248795e-02f, +9.582767160e-01f, -4.740261690e-02f },
+    { -3.403371585e-03f, +9.258286004e-02f, +9.584236724e-01f, -4.735302311e-02f },
+    { -3.392939107e-03f, +9.237338726e-02f, +9.585703827e-01f, -4.730331527e-02f },
+    { -3.382524489e-03f, +9.216406964e-02f, +9.587168467e-01f, -4.725349330e-02f },
+    { -3.372127720e-03f, +9.195490720e-02f, +9.588630643e-01f, -4.720355712e-02f },
+    { -3.361748789e-03f, +9.174589994e-02f, +9.590090355e-01f, -4.715350666e-02f },
+    { -3.351387685e-03f, +9.153704790e-02f, +9.591547602e-01f, -4.710334184e-02f },
+    { -3.341044398e-03f, +9.132835108e-02f, +9.593002383e-01f, -4.705306259e-02f },
+    { -3.330718916e-03f, +9.111980950e-02f, +9.594454696e-01f, -4.700266883e-02f },
+    { -3.320411230e-03f, +9.091142320e-02f, +9.595904542e-01f, -4.695216048e-02f },
+    { -3.310121328e-03f, +9.070319217e-02f, +9.597351919e-01f, -4.690153746e-02f },
+    { -3.299849199e-03f, +9.049511645e-02f, +9.598796825e-01f, -4.685079971e-02f },
+    { -3.289594832e-03f, +9.028719604e-02f, +9.600239261e-01f, -4.679994715e-02f },
+    { -3.279358217e-03f, +9.007943096e-02f, +9.601679226e-01f, -4.674897969e-02f },
+    { -3.269139342e-03f, +8.987182124e-02f, +9.603116718e-01f, -4.669789727e-02f },
+    { -3.258938197e-03f, +8.966436689e-02f, +9.604551736e-01f, -4.664669981e-02f },
+    { -3.248754771e-03f, +8.945706793e-02f, +9.605984280e-01f, -4.659538723e-02f },
+    { -3.238589053e-03f, +8.924992437e-02f, +9.607414349e-01f, -4.654395945e-02f },
+    { -3.228441031e-03f, +8.904293624e-02f, +9.608841941e-01f, -4.649241641e-02f },
+    { -3.218310696e-03f, +8.883610354e-02f, +9.610267057e-01f, -4.644075802e-02f },
+    { -3.208198035e-03f, +8.862942629e-02f, +9.611689695e-01f, -4.638898421e-02f },
+    { -3.198103037e-03f, +8.842290452e-02f, +9.613109853e-01f, -4.633709491e-02f },
+    { -3.188025693e-03f, +8.821653824e-02f, +9.614527532e-01f, -4.628509003e-02f },
+    { -3.177965990e-03f, +8.801032746e-02f, +9.615942731e-01f, -4.623296951e-02f },
+    { -3.167923918e-03f, +8.780427220e-02f, +9.617355447e-01f, -4.618073326e-02f },
+    { -3.157899465e-03f, +8.759837247e-02f, +9.618765682e-01f, -4.612838122e-02f },
+    { -3.147892621e-03f, +8.739262830e-02f, +9.620173433e-01f, -4.607591330e-02f },
+    { -3.137903374e-03f, +8.718703970e-02f, +9.621578700e-01f, -4.602332944e-02f },
+    { -3.127931713e-03f, +8.698160668e-02f, +9.622981482e-01f, -4.597062955e-02f },
+    { -3.117977628e-03f, +8.677632927e-02f, +9.624381778e-01f, -4.591781356e-02f },
+    { -3.108041106e-03f, +8.657120747e-02f, +9.625779587e-01f, -4.586488140e-02f },
+    { -3.098122137e-03f, +8.636624129e-02f, +9.627174908e-01f, -4.581183299e-02f },
+    { -3.088220709e-03f, +8.616143077e-02f, +9.628567741e-01f, -4.575866826e-02f },
+    { -3.078336812e-03f, +8.595677591e-02f, +9.629958084e-01f, -4.570538712e-02f },
+    { -3.068470433e-03f, +8.575227672e-02f, +9.631345937e-01f, -4.565198951e-02f },
+    { -3.058621563e-03f, +8.554793322e-02f, +9.632731298e-01f, -4.559847535e-02f },
+    { -3.048790189e-03f, +8.534374543e-02f, +9.634114168e-01f, -4.554484457e-02f },
+    { -3.038976300e-03f, +8.513971336e-02f, +9.635494545e-01f, -4.549109708e-02f },
+    { -3.029179886e-03f, +8.493583703e-02f, +9.636872427e-01f, -4.543723283e-02f },
+    { -3.019400934e-03f, +8.473211644e-02f, +9.638247816e-01f, -4.538325172e-02f },
+    { -3.009639433e-03f, +8.452855162e-02f, +9.639620708e-01f, -4.532915369e-02f },
+    { -2.999895373e-03f, +8.432514258e-02f, +9.640991104e-01f, -4.527493866e-02f },
+    { -2.990168741e-03f, +8.412188933e-02f, +9.642359003e-01f, -4.522060655e-02f },
+    { -2.980459527e-03f, +8.391879188e-02f, +9.643724404e-01f, -4.516615730e-02f },
+    { -2.970767718e-03f, +8.371585026e-02f, +9.645087305e-01f, -4.511159083e-02f },
+    { -2.961093304e-03f, +8.351306447e-02f, +9.646447707e-01f, -4.505690705e-02f },
+    { -2.951436274e-03f, +8.331043452e-02f, +9.647805608e-01f, -4.500210591e-02f },
+    { -2.941796615e-03f, +8.310796044e-02f, +9.649161008e-01f, -4.494718732e-02f },
+    { -2.932174316e-03f, +8.290564223e-02f, +9.650513905e-01f, -4.489215120e-02f },
+    { -2.922569366e-03f, +8.270347990e-02f, +9.651864298e-01f, -4.483699749e-02f },
+    { -2.912981754e-03f, +8.250147348e-02f, +9.653212188e-01f, -4.478172612e-02f },
+    { -2.903411467e-03f, +8.229962297e-02f, +9.654557573e-01f, -4.472633699e-02f },
+    { -2.893858495e-03f, +8.209792838e-02f, +9.655900451e-01f, -4.467083005e-02f },
+    { -2.884322825e-03f, +8.189638974e-02f, +9.657240824e-01f, -4.461520522e-02f },
+    { -2.874804447e-03f, +8.169500704e-02f, +9.658578688e-01f, -4.455946242e-02f },
+    { -2.865303349e-03f, +8.149378031e-02f, +9.659914044e-01f, -4.450360157e-02f },
+    { -2.855819519e-03f, +8.129270955e-02f, +9.661246891e-01f, -4.444762262e-02f },
+    { -2.846352946e-03f, +8.109179479e-02f, +9.662577228e-01f, -4.439152547e-02f },
+    { -2.836903618e-03f, +8.089103602e-02f, +9.663905054e-01f, -4.433531006e-02f },
+    { -2.827471523e-03f, +8.069043326e-02f, +9.665230369e-01f, -4.427897631e-02f },
+    { -2.818056650e-03f, +8.048998653e-02f, +9.666553170e-01f, -4.422252415e-02f },
+    { -2.808658988e-03f, +8.028969584e-02f, +9.667873459e-01f, -4.416595350e-02f },
+    { -2.799278524e-03f, +8.008956119e-02f, +9.669191233e-01f, -4.410926430e-02f },
+    { -2.789915247e-03f, +7.988958260e-02f, +9.670506492e-01f, -4.405245646e-02f },
+    { -2.780569145e-03f, +7.968976008e-02f, +9.671819235e-01f, -4.399552991e-02f },
+    { -2.771240206e-03f, +7.949009365e-02f, +9.673129462e-01f, -4.393848458e-02f },
+    { -2.761928420e-03f, +7.929058330e-02f, +9.674437171e-01f, -4.388132040e-02f },
+    { -2.752633774e-03f, +7.909122907e-02f, +9.675742361e-01f, -4.382403729e-02f },
+    { -2.743356256e-03f, +7.889203094e-02f, +9.677045033e-01f, -4.376663518e-02f },
+    { -2.734095855e-03f, +7.869298894e-02f, +9.678345184e-01f, -4.370911400e-02f },
+    { -2.724852558e-03f, +7.849410308e-02f, +9.679642815e-01f, -4.365147366e-02f },
+    { -2.715626355e-03f, +7.829537336e-02f, +9.680937924e-01f, -4.359371410e-02f },
+    { -2.706417234e-03f, +7.809679980e-02f, +9.682230510e-01f, -4.353583525e-02f },
+    { -2.697225181e-03f, +7.789838241e-02f, +9.683520574e-01f, -4.347783703e-02f },
+    { -2.688050187e-03f, +7.770012120e-02f, +9.684808113e-01f, -4.341971936e-02f },
+    { -2.678892238e-03f, +7.750201617e-02f, +9.686093127e-01f, -4.336148218e-02f },
+    { -2.669751324e-03f, +7.730406734e-02f, +9.687375615e-01f, -4.330312541e-02f },
+    { -2.660627431e-03f, +7.710627472e-02f, +9.688655578e-01f, -4.324464897e-02f },
+    { -2.651520549e-03f, +7.690863832e-02f, +9.689933012e-01f, -4.318605280e-02f },
+    { -2.642430666e-03f, +7.671115814e-02f, +9.691207919e-01f, -4.312733682e-02f },
+    { -2.633357769e-03f, +7.651383420e-02f, +9.692480296e-01f, -4.306850096e-02f },
+    { -2.624301846e-03f, +7.631666650e-02f, +9.693750144e-01f, -4.300954515e-02f },
+    { -2.615262886e-03f, +7.611965506e-02f, +9.695017462e-01f, -4.295046931e-02f },
+    { -2.606240877e-03f, +7.592279989e-02f, +9.696282248e-01f, -4.289127336e-02f },
+    { -2.597235807e-03f, +7.572610098e-02f, +9.697544501e-01f, -4.283195724e-02f },
+    { -2.588247664e-03f, +7.552955836e-02f, +9.698804222e-01f, -4.277252088e-02f },
+    { -2.579276435e-03f, +7.533317203e-02f, +9.700061410e-01f, -4.271296420e-02f },
+    { -2.570322110e-03f, +7.513694200e-02f, +9.701316062e-01f, -4.265328712e-02f },
+    { -2.561384675e-03f, +7.494086827e-02f, +9.702568180e-01f, -4.259348958e-02f },
+    { -2.552464119e-03f, +7.474495086e-02f, +9.703817761e-01f, -4.253357150e-02f },
+    { -2.543560430e-03f, +7.454918978e-02f, +9.705064805e-01f, -4.247353281e-02f },
+    { -2.534673595e-03f, +7.435358503e-02f, +9.706309312e-01f, -4.241337343e-02f },
+    { -2.525803603e-03f, +7.415813662e-02f, +9.707551280e-01f, -4.235309331e-02f },
+    { -2.516950442e-03f, +7.396284455e-02f, +9.708790709e-01f, -4.229269235e-02f },
+    { -2.508114099e-03f, +7.376770885e-02f, +9.710027598e-01f, -4.223217049e-02f },
+    { -2.499294563e-03f, +7.357272950e-02f, +9.711261947e-01f, -4.217152766e-02f },
+    { -2.490491821e-03f, +7.337790653e-02f, +9.712493753e-01f, -4.211076378e-02f },
+    { -2.481705861e-03f, +7.318323994e-02f, +9.713723017e-01f, -4.204987879e-02f },
+    { -2.472936671e-03f, +7.298872973e-02f, +9.714949738e-01f, -4.198887261e-02f },
+    { -2.464184239e-03f, +7.279437592e-02f, +9.716173916e-01f, -4.192774516e-02f },
+    { -2.455448552e-03f, +7.260017851e-02f, +9.717395548e-01f, -4.186649638e-02f },
+    { -2.446729599e-03f, +7.240613750e-02f, +9.718614635e-01f, -4.180512620e-02f },
+    { -2.438027368e-03f, +7.221225291e-02f, +9.719831176e-01f, -4.174363453e-02f },
+    { -2.429341845e-03f, +7.201852474e-02f, +9.721045170e-01f, -4.168202131e-02f },
+    { -2.420673020e-03f, +7.182495300e-02f, +9.722256616e-01f, -4.162028648e-02f },
+    { -2.412020878e-03f, +7.163153769e-02f, +9.723465513e-01f, -4.155842994e-02f },
+    { -2.403385410e-03f, +7.143827882e-02f, +9.724671862e-01f, -4.149645164e-02f },
+    { -2.394766601e-03f, +7.124517640e-02f, +9.725875660e-01f, -4.143435151e-02f },
+    { -2.386164441e-03f, +7.105223044e-02f, +9.727076907e-01f, -4.137212946e-02f },
+    { -2.377578915e-03f, +7.085944093e-02f, +9.728275603e-01f, -4.130978543e-02f },
+    { -2.369010014e-03f, +7.066680788e-02f, +9.729471746e-01f, -4.124731934e-02f },
+    { -2.360457723e-03f, +7.047433131e-02f, +9.730665337e-01f, -4.118473113e-02f },
+    { -2.351922030e-03f, +7.028201121e-02f, +9.731856373e-01f, -4.112202073e-02f },
+    { -2.343402924e-03f, +7.008984759e-02f, +9.733044855e-01f, -4.105918805e-02f },
+    { -2.334900392e-03f, +6.989784046e-02f, +9.734230782e-01f, -4.099623304e-02f },
+    { -2.326414422e-03f, +6.970598983e-02f, +9.735414153e-01f, -4.093315561e-02f },
+    { -2.317945001e-03f, +6.951429569e-02f, +9.736594966e-01f, -4.086995570e-02f },
+    { -2.309492116e-03f, +6.932275805e-02f, +9.737773222e-01f, -4.080663324e-02f },
+    { -2.301055756e-03f, +6.913137692e-02f, +9.738948920e-01f, -4.074318815e-02f },
+    { -2.292635908e-03f, +6.894015231e-02f, +9.740122059e-01f, -4.067962036e-02f },
+    { -2.284232560e-03f, +6.874908421e-02f, +9.741292638e-01f, -4.061592981e-02f },
+    { -2.275845698e-03f, +6.855817263e-02f, +9.742460656e-01f, -4.055211641e-02f },
+    { -2.267475312e-03f, +6.836741758e-02f, +9.743626113e-01f, -4.048818011e-02f },
+    { -2.259121387e-03f, +6.817681906e-02f, +9.744789009e-01f, -4.042412082e-02f },
+    { -2.250783913e-03f, +6.798637707e-02f, +9.745949341e-01f, -4.035993848e-02f },
+    { -2.242462875e-03f, +6.779609163e-02f, +9.747107110e-01f, -4.029563302e-02f },
+    { -2.234158263e-03f, +6.760596272e-02f, +9.748262314e-01f, -4.023120436e-02f },
+    { -2.225870062e-03f, +6.741599037e-02f, +9.749414954e-01f, -4.016665244e-02f },
+    { -2.217598262e-03f, +6.722617456e-02f, +9.750565028e-01f, -4.010197718e-02f },
+    { -2.209342848e-03f, +6.703651531e-02f, +9.751712536e-01f, -4.003717851e-02f },
+    { -2.201103809e-03f, +6.684701262e-02f, +9.752857476e-01f, -3.997225637e-02f },
+    { -2.192881133e-03f, +6.665766648e-02f, +9.753999849e-01f, -3.990721068e-02f },
+    { -2.184674805e-03f, +6.646847692e-02f, +9.755139653e-01f, -3.984204137e-02f },
+    { -2.176484815e-03f, +6.627944392e-02f, +9.756276888e-01f, -3.977674836e-02f },
+    { -2.168311149e-03f, +6.609056749e-02f, +9.757411552e-01f, -3.971133160e-02f },
+    { -2.160153795e-03f, +6.590184763e-02f, +9.758543646e-01f, -3.964579101e-02f },
+    { -2.152012740e-03f, +6.571328435e-02f, +9.759673169e-01f, -3.958012651e-02f },
+    { -2.143887971e-03f, +6.552487765e-02f, +9.760800119e-01f, -3.951433805e-02f },
+    { -2.135779477e-03f, +6.533662753e-02f, +9.761924497e-01f, -3.944842554e-02f },
+    { -2.127687243e-03f, +6.514853400e-02f, +9.763046301e-01f, -3.938238892e-02f },
+    { -2.119611258e-03f, +6.496059705e-02f, +9.764165530e-01f, -3.931622812e-02f },
+    { -2.111551509e-03f, +6.477281670e-02f, +9.765282185e-01f, -3.924994306e-02f },
+    { -2.103507983e-03f, +6.458519293e-02f, +9.766396264e-01f, -3.918353368e-02f },
+    { -2.095480667e-03f, +6.439772576e-02f, +9.767507766e-01f, -3.911699991e-02f },
+    { -2.087469549e-03f, +6.421041518e-02f, +9.768616692e-01f, -3.905034167e-02f },
+    { -2.079474617e-03f, +6.402326120e-02f, +9.769723040e-01f, -3.898355890e-02f },
+    { -2.071495856e-03f, +6.383626381e-02f, +9.770826809e-01f, -3.891665153e-02f },
+    { -2.063533255e-03f, +6.364942303e-02f, +9.771927998e-01f, -3.884961949e-02f },
+    { -2.055586801e-03f, +6.346273885e-02f, +9.773026608e-01f, -3.878246270e-02f },
+    { -2.047656480e-03f, +6.327621127e-02f, +9.774122638e-01f, -3.871518110e-02f },
+    { -2.039742281e-03f, +6.308984030e-02f, +9.775216086e-01f, -3.864777462e-02f },
+    { -2.031844191e-03f, +6.290362593e-02f, +9.776306952e-01f, -3.858024318e-02f },
+    { -2.023962196e-03f, +6.271756817e-02f, +9.777395235e-01f, -3.851258673e-02f },
+    { -2.016096283e-03f, +6.253166701e-02f, +9.778480935e-01f, -3.844480518e-02f },
+    { -2.008246441e-03f, +6.234592246e-02f, +9.779564051e-01f, -3.837689847e-02f },
+    { -2.000412656e-03f, +6.216033452e-02f, +9.780644583e-01f, -3.830886653e-02f },
+    { -1.992594915e-03f, +6.197490319e-02f, +9.781722528e-01f, -3.824070929e-02f },
+    { -1.984793206e-03f, +6.178962847e-02f, +9.782797888e-01f, -3.817242668e-02f },
+    { -1.977007515e-03f, +6.160451036e-02f, +9.783870661e-01f, -3.810401864e-02f },
+    { -1.969237830e-03f, +6.141954886e-02f, +9.784940847e-01f, -3.803548508e-02f },
+    { -1.961484138e-03f, +6.123474396e-02f, +9.786008444e-01f, -3.796682595e-02f },
+    { -1.953746425e-03f, +6.105009568e-02f, +9.787073453e-01f, -3.789804118e-02f },
+    { -1.946024680e-03f, +6.086560401e-02f, +9.788135872e-01f, -3.782913069e-02f },
+    { -1.938318888e-03f, +6.068126894e-02f, +9.789195701e-01f, -3.776009441e-02f },
+    { -1.930629038e-03f, +6.049709048e-02f, +9.790252939e-01f, -3.769093228e-02f },
+    { -1.922955116e-03f, +6.031306864e-02f, +9.791307585e-01f, -3.762164423e-02f },
+    { -1.915297108e-03f, +6.012920340e-02f, +9.792359640e-01f, -3.755223018e-02f },
+    { -1.907655004e-03f, +5.994549476e-02f, +9.793409101e-01f, -3.748269008e-02f },
+    { -1.900028788e-03f, +5.976194274e-02f, +9.794455969e-01f, -3.741302385e-02f },
+    { -1.892418449e-03f, +5.957854731e-02f, +9.795500243e-01f, -3.734323142e-02f },
+    { -1.884823973e-03f, +5.939530849e-02f, +9.796541922e-01f, -3.727331272e-02f },
+    { -1.877245347e-03f, +5.921222628e-02f, +9.797581006e-01f, -3.720326769e-02f },
+    { -1.869682558e-03f, +5.902930066e-02f, +9.798617493e-01f, -3.713309626e-02f },
+    { -1.862135594e-03f, +5.884653165e-02f, +9.799651384e-01f, -3.706279835e-02f },
+    { -1.854604440e-03f, +5.866391923e-02f, +9.800682677e-01f, -3.699237391e-02f },
+    { -1.847089085e-03f, +5.848146341e-02f, +9.801711372e-01f, -3.692182285e-02f },
+    { -1.839589515e-03f, +5.829916418e-02f, +9.802737469e-01f, -3.685114512e-02f },
+    { -1.832105717e-03f, +5.811702155e-02f, +9.803760966e-01f, -3.678034064e-02f },
+    { -1.824637677e-03f, +5.793503550e-02f, +9.804781863e-01f, -3.670940934e-02f },
+    { -1.817185384e-03f, +5.775320605e-02f, +9.805800159e-01f, -3.663835117e-02f },
+    { -1.809748823e-03f, +5.757153318e-02f, +9.806815854e-01f, -3.656716604e-02f },
+    { -1.802327981e-03f, +5.739001690e-02f, +9.807828947e-01f, -3.649585390e-02f },
+    { -1.794922846e-03f, +5.720865720e-02f, +9.808839437e-01f, -3.642441467e-02f },
+    { -1.787533404e-03f, +5.702745407e-02f, +9.809847324e-01f, -3.635284829e-02f },
+    { -1.780159642e-03f, +5.684640752e-02f, +9.810852607e-01f, -3.628115468e-02f },
+    { -1.772801548e-03f, +5.666551755e-02f, +9.811855286e-01f, -3.620933378e-02f },
+    { -1.765459107e-03f, +5.648478415e-02f, +9.812855360e-01f, -3.613738552e-02f },
+    { -1.758132306e-03f, +5.630420731e-02f, +9.813852827e-01f, -3.606530984e-02f },
+    { -1.750821133e-03f, +5.612378704e-02f, +9.814847688e-01f, -3.599310667e-02f },
+    { -1.743525575e-03f, +5.594352333e-02f, +9.815839943e-01f, -3.592077593e-02f },
+    { -1.736245617e-03f, +5.576341617e-02f, +9.816829589e-01f, -3.584831757e-02f },
+    { -1.728981247e-03f, +5.558346557e-02f, +9.817816627e-01f, -3.577573150e-02f },
+    { -1.721732452e-03f, +5.540367152e-02f, +9.818801056e-01f, -3.570301768e-02f },
+    { -1.714499218e-03f, +5.522403402e-02f, +9.819782876e-01f, -3.563017602e-02f },
+    { -1.707281532e-03f, +5.504455306e-02f, +9.820762086e-01f, -3.555720646e-02f },
+    { -1.700079381e-03f, +5.486522863e-02f, +9.821738684e-01f, -3.548410894e-02f },
+    { -1.692892751e-03f, +5.468606075e-02f, +9.822712671e-01f, -3.541088338e-02f },
+    { -1.685721630e-03f, +5.450704939e-02f, +9.823684047e-01f, -3.533752973e-02f },
+    { -1.678566004e-03f, +5.432819456e-02f, +9.824652809e-01f, -3.526404790e-02f },
+    { -1.671425859e-03f, +5.414949625e-02f, +9.825618958e-01f, -3.519043784e-02f },
+    { -1.664301183e-03f, +5.397095446e-02f, +9.826582494e-01f, -3.511669948e-02f },
+    { -1.657191962e-03f, +5.379256918e-02f, +9.827543414e-01f, -3.504283275e-02f },
+    { -1.650098183e-03f, +5.361434040e-02f, +9.828501720e-01f, -3.496883758e-02f },
+    { -1.643019832e-03f, +5.343626813e-02f, +9.829457410e-01f, -3.489471391e-02f },
+    { -1.635956896e-03f, +5.325835236e-02f, +9.830410484e-01f, -3.482046167e-02f },
+    { -1.628909362e-03f, +5.308059308e-02f, +9.831360941e-01f, -3.474608080e-02f },
+    { -1.621877216e-03f, +5.290299029e-02f, +9.832308780e-01f, -3.467157122e-02f },
+    { -1.614860445e-03f, +5.272554398e-02f, +9.833254001e-01f, -3.459693287e-02f },
+    { -1.607859036e-03f, +5.254825415e-02f, +9.834196603e-01f, -3.452216568e-02f },
+    { -1.600872975e-03f, +5.237112079e-02f, +9.835136587e-01f, -3.444726959e-02f },
+    { -1.593902249e-03f, +5.219414389e-02f, +9.836073950e-01f, -3.437224453e-02f },
+    { -1.586946844e-03f, +5.201732345e-02f, +9.837008692e-01f, -3.429709044e-02f },
+    { -1.580006747e-03f, +5.184065947e-02f, +9.837940814e-01f, -3.422180724e-02f },
+    { -1.573081945e-03f, +5.166415193e-02f, +9.838870314e-01f, -3.414639488e-02f },
+    { -1.566172424e-03f, +5.148780084e-02f, +9.839797191e-01f, -3.407085327e-02f },
+    { -1.559278170e-03f, +5.131160618e-02f, +9.840721446e-01f, -3.399518237e-02f },
+    { -1.552399171e-03f, +5.113556795e-02f, +9.841643077e-01f, -3.391938210e-02f },
+    { -1.545535412e-03f, +5.095968615e-02f, +9.842562085e-01f, -3.384345240e-02f },
+    { -1.538686881e-03f, +5.078396076e-02f, +9.843478467e-01f, -3.376739320e-02f },
+    { -1.531853564e-03f, +5.060839177e-02f, +9.844392225e-01f, -3.369120443e-02f },
+    { -1.525035446e-03f, +5.043297919e-02f, +9.845303356e-01f, -3.361488603e-02f },
+    { -1.518232516e-03f, +5.025772301e-02f, +9.846211862e-01f, -3.353843793e-02f },
+    { -1.511444759e-03f, +5.008262322e-02f, +9.847117740e-01f, -3.346186007e-02f },
+    { -1.504672161e-03f, +4.990767980e-02f, +9.848020991e-01f, -3.338515239e-02f },
+    { -1.497914710e-03f, +4.973289276e-02f, +9.848921614e-01f, -3.330831480e-02f },
+    { -1.491172391e-03f, +4.955826209e-02f, +9.849819608e-01f, -3.323134726e-02f },
+    { -1.484445192e-03f, +4.938378778e-02f, +9.850714973e-01f, -3.315424969e-02f },
+    { -1.477733098e-03f, +4.920946982e-02f, +9.851607708e-01f, -3.307702203e-02f },
+    { -1.471036096e-03f, +4.903530820e-02f, +9.852497812e-01f, -3.299966421e-02f },
+    { -1.464354173e-03f, +4.886130292e-02f, +9.853385286e-01f, -3.292217617e-02f },
+    { -1.457687314e-03f, +4.868745396e-02f, +9.854270129e-01f, -3.284455785e-02f },
+    { -1.451035507e-03f, +4.851376133e-02f, +9.855152339e-01f, -3.276680917e-02f },
+    { -1.444398738e-03f, +4.834022501e-02f, +9.856031917e-01f, -3.268893007e-02f },
+    { -1.437776992e-03f, +4.816684499e-02f, +9.856908861e-01f, -3.261092049e-02f },
+    { -1.431170257e-03f, +4.799362127e-02f, +9.857783172e-01f, -3.253278036e-02f },
+    { -1.424578519e-03f, +4.782055384e-02f, +9.858654848e-01f, -3.245450962e-02f },
+    { -1.418001764e-03f, +4.764764268e-02f, +9.859523890e-01f, -3.237610820e-02f },
+    { -1.411439979e-03f, +4.747488779e-02f, +9.860390297e-01f, -3.229757604e-02f },
+    { -1.404893149e-03f, +4.730228917e-02f, +9.861254067e-01f, -3.221891307e-02f },
+    { -1.398361262e-03f, +4.712984679e-02f, +9.862115201e-01f, -3.214011922e-02f },
+    { -1.391844303e-03f, +4.695756066e-02f, +9.862973698e-01f, -3.206119444e-02f },
+    { -1.385342260e-03f, +4.678543077e-02f, +9.863829557e-01f, -3.198213866e-02f },
+    { -1.378855117e-03f, +4.661345709e-02f, +9.864682779e-01f, -3.190295181e-02f },
+    { -1.372382862e-03f, +4.644163964e-02f, +9.865533361e-01f, -3.182363382e-02f },
+    { -1.365925481e-03f, +4.626997838e-02f, +9.866381305e-01f, -3.174418465e-02f },
+    { -1.359482960e-03f, +4.609847333e-02f, +9.867226608e-01f, -3.166460421e-02f },
+    { -1.353055286e-03f, +4.592712446e-02f, +9.868069272e-01f, -3.158489244e-02f },
+    { -1.346642444e-03f, +4.575593177e-02f, +9.868909295e-01f, -3.150504929e-02f },
+    { -1.340244422e-03f, +4.558489524e-02f, +9.869746676e-01f, -3.142507468e-02f },
+    { -1.333861204e-03f, +4.541401487e-02f, +9.870581415e-01f, -3.134496856e-02f },
+    { -1.327492778e-03f, +4.524329065e-02f, +9.871413513e-01f, -3.126473085e-02f },
+    { -1.321139130e-03f, +4.507272256e-02f, +9.872242967e-01f, -3.118436150e-02f },
+    { -1.314800245e-03f, +4.490231060e-02f, +9.873069778e-01f, -3.110386044e-02f },
+    { -1.308476111e-03f, +4.473205475e-02f, +9.873893944e-01f, -3.102322761e-02f },
+    { -1.302166714e-03f, +4.456195501e-02f, +9.874715467e-01f, -3.094246293e-02f },
+    { -1.295872038e-03f, +4.439201136e-02f, +9.875534344e-01f, -3.086156636e-02f },
+    { -1.289592072e-03f, +4.422222380e-02f, +9.876350576e-01f, -3.078053782e-02f },
+    { -1.283326801e-03f, +4.405259231e-02f, +9.877164161e-01f, -3.069937725e-02f },
+    { -1.277076211e-03f, +4.388311688e-02f, +9.877975101e-01f, -3.061808459e-02f },
+    { -1.270840289e-03f, +4.371379750e-02f, +9.878783393e-01f, -3.053665977e-02f },
+    { -1.264619020e-03f, +4.354463416e-02f, +9.879589037e-01f, -3.045510273e-02f },
+    { -1.258412391e-03f, +4.337562684e-02f, +9.880392034e-01f, -3.037341341e-02f },
+    { -1.252220388e-03f, +4.320677555e-02f, +9.881192381e-01f, -3.029159174e-02f },
+    { -1.246042996e-03f, +4.303808025e-02f, +9.881990080e-01f, -3.020963766e-02f },
+    { -1.239880203e-03f, +4.286954095e-02f, +9.882785129e-01f, -3.012755111e-02f },
+    { -1.233731995e-03f, +4.270115763e-02f, +9.883577528e-01f, -3.004533202e-02f },
+    { -1.227598357e-03f, +4.253293028e-02f, +9.884367276e-01f, -2.996298034e-02f },
+    { -1.221479275e-03f, +4.236485889e-02f, +9.885154374e-01f, -2.988049599e-02f },
+    { -1.215374737e-03f, +4.219694344e-02f, +9.885938819e-01f, -2.979787891e-02f },
+    { -1.209284727e-03f, +4.202918393e-02f, +9.886720613e-01f, -2.971512904e-02f },
+    { -1.203209232e-03f, +4.186158033e-02f, +9.887499753e-01f, -2.963224632e-02f },
+    { -1.197148238e-03f, +4.169413264e-02f, +9.888276241e-01f, -2.954923069e-02f },
+    { -1.191101731e-03f, +4.152684085e-02f, +9.889050075e-01f, -2.946608208e-02f },
+    { -1.185069698e-03f, +4.135970494e-02f, +9.889821255e-01f, -2.938280043e-02f },
+    { -1.179052123e-03f, +4.119272490e-02f, +9.890589780e-01f, -2.929938568e-02f },
+    { -1.173048994e-03f, +4.102590072e-02f, +9.891355651e-01f, -2.921583776e-02f },
+    { -1.167060297e-03f, +4.085923238e-02f, +9.892118865e-01f, -2.913215661e-02f },
+    { -1.161086016e-03f, +4.069271988e-02f, +9.892879424e-01f, -2.904834218e-02f },
+    { -1.155126140e-03f, +4.052636319e-02f, +9.893637326e-01f, -2.896439439e-02f },
+    { -1.149180652e-03f, +4.036016230e-02f, +9.894392571e-01f, -2.888031318e-02f },
+    { -1.143249541e-03f, +4.019411721e-02f, +9.895145159e-01f, -2.879609850e-02f },
+    { -1.137332791e-03f, +4.002822789e-02f, +9.895895088e-01f, -2.871175028e-02f },
+    { -1.131430388e-03f, +3.986249434e-02f, +9.896642360e-01f, -2.862726845e-02f },
+    { -1.125542319e-03f, +3.969691653e-02f, +9.897386972e-01f, -2.854265296e-02f },
+    { -1.119668570e-03f, +3.953149447e-02f, +9.898128925e-01f, -2.845790375e-02f },
+    { -1.113809126e-03f, +3.936622812e-02f, +9.898868218e-01f, -2.837302075e-02f },
+    { -1.107963974e-03f, +3.920111748e-02f, +9.899604850e-01f, -2.828800389e-02f },
+    { -1.102133100e-03f, +3.903616254e-02f, +9.900338822e-01f, -2.820285313e-02f },
+    { -1.096316489e-03f, +3.887136328e-02f, +9.901070133e-01f, -2.811756839e-02f },
+    { -1.090514127e-03f, +3.870671968e-02f, +9.901798782e-01f, -2.803214962e-02f },
+    { -1.084726001e-03f, +3.854223173e-02f, +9.902524768e-01f, -2.794659675e-02f },
+    { -1.078952096e-03f, +3.837789942e-02f, +9.903248092e-01f, -2.786090972e-02f },
+    { -1.073192399e-03f, +3.821372273e-02f, +9.903968753e-01f, -2.777508847e-02f },
+    { -1.067446895e-03f, +3.804970164e-02f, +9.904686751e-01f, -2.768913294e-02f },
+    { -1.061715570e-03f, +3.788583615e-02f, +9.905402084e-01f, -2.760304307e-02f },
+    { -1.055998411e-03f, +3.772212623e-02f, +9.906114753e-01f, -2.751681879e-02f },
+    { -1.050295402e-03f, +3.755857188e-02f, +9.906824756e-01f, -2.743046005e-02f },
+    { -1.044606531e-03f, +3.739517307e-02f, +9.907532095e-01f, -2.734396678e-02f },
+    { -1.038931782e-03f, +3.723192979e-02f, +9.908236768e-01f, -2.725733893e-02f },
+    { -1.033271142e-03f, +3.706884203e-02f, +9.908938774e-01f, -2.717057642e-02f },
+    { -1.027624597e-03f, +3.690590976e-02f, +9.909638114e-01f, -2.708367921e-02f },
+    { -1.021992133e-03f, +3.674313298e-02f, +9.910334786e-01f, -2.699664722e-02f },
+    { -1.016373735e-03f, +3.658051167e-02f, +9.911028791e-01f, -2.690948040e-02f },
+    { -1.010769389e-03f, +3.641804581e-02f, +9.911720128e-01f, -2.682217869e-02f },
+    { -1.005179082e-03f, +3.625573538e-02f, +9.912408796e-01f, -2.673474203e-02f },
+    { -9.996027983e-04f, +3.609358038e-02f, +9.913094795e-01f, -2.664717035e-02f },
+    { -9.940405249e-04f, +3.593158078e-02f, +9.913778125e-01f, -2.655946360e-02f },
+    { -9.884922474e-04f, +3.576973656e-02f, +9.914458785e-01f, -2.647162171e-02f },
+    { -9.829579515e-04f, +3.560804772e-02f, +9.915136775e-01f, -2.638364463e-02f },
+    { -9.774376231e-04f, +3.544651423e-02f, +9.915812094e-01f, -2.629553230e-02f },
+    { -9.719312480e-04f, +3.528513608e-02f, +9.916484743e-01f, -2.620728464e-02f },
+    { -9.664388120e-04f, +3.512391325e-02f, +9.917154719e-01f, -2.611890161e-02f },
+    { -9.609603011e-04f, +3.496284573e-02f, +9.917822024e-01f, -2.603038315e-02f },
+    { -9.554957009e-04f, +3.480193349e-02f, +9.918486656e-01f, -2.594172918e-02f },
+    { -9.500449974e-04f, +3.464117652e-02f, +9.919148616e-01f, -2.585293966e-02f },
+    { -9.446081762e-04f, +3.448057480e-02f, +9.919807902e-01f, -2.576401452e-02f },
+    { -9.391852233e-04f, +3.432012832e-02f, +9.920464515e-01f, -2.567495371e-02f },
+    { -9.337761243e-04f, +3.415983706e-02f, +9.921118453e-01f, -2.558575716e-02f },
+    { -9.283808652e-04f, +3.399970099e-02f, +9.921769718e-01f, -2.549642481e-02f },
+    { -9.229994316e-04f, +3.383972011e-02f, +9.922418307e-01f, -2.540695661e-02f },
+    { -9.176318093e-04f, +3.367989440e-02f, +9.923064221e-01f, -2.531735249e-02f },
+    { -9.122779842e-04f, +3.352022383e-02f, +9.923707459e-01f, -2.522761239e-02f },
+    { -9.069379419e-04f, +3.336070839e-02f, +9.924348021e-01f, -2.513773626e-02f },
+    { -9.016116683e-04f, +3.320134807e-02f, +9.924985907e-01f, -2.504772404e-02f },
+    { -8.962991490e-04f, +3.304214283e-02f, +9.925621116e-01f, -2.495757566e-02f },
+    { -8.910003699e-04f, +3.288309267e-02f, +9.926253647e-01f, -2.486729107e-02f },
+    { -8.857153167e-04f, +3.272419757e-02f, +9.926883501e-01f, -2.477687020e-02f },
+    { -8.804439750e-04f, +3.256545750e-02f, +9.927510676e-01f, -2.468631300e-02f },
+    { -8.751863308e-04f, +3.240687246e-02f, +9.928135173e-01f, -2.459561942e-02f },
+    { -8.699423695e-04f, +3.224844242e-02f, +9.928756991e-01f, -2.450478938e-02f },
+    { -8.647120771e-04f, +3.209016735e-02f, +9.929376130e-01f, -2.441382283e-02f },
+    { -8.594954392e-04f, +3.193204725e-02f, +9.929992589e-01f, -2.432271972e-02f },
+    { -8.542924415e-04f, +3.177408210e-02f, +9.930606368e-01f, -2.423147998e-02f },
+    { -8.491030697e-04f, +3.161627187e-02f, +9.931217466e-01f, -2.414010355e-02f },
+    { -8.439273095e-04f, +3.145861654e-02f, +9.931825884e-01f, -2.404859038e-02f },
+    { -8.387651466e-04f, +3.130111610e-02f, +9.932431620e-01f, -2.395694040e-02f },
+    { -8.336165668e-04f, +3.114377053e-02f, +9.933034675e-01f, -2.386515357e-02f },
+    { -8.284815556e-04f, +3.098657981e-02f, +9.933635047e-01f, -2.377322981e-02f },
+    { -8.233600987e-04f, +3.082954391e-02f, +9.934232738e-01f, -2.368116908e-02f },
+    { -8.182521819e-04f, +3.067266283e-02f, +9.934827745e-01f, -2.358897131e-02f },
+    { -8.131577908e-04f, +3.051593653e-02f, +9.935420069e-01f, -2.349663644e-02f },
+    { -8.080769110e-04f, +3.035936500e-02f, +9.936009710e-01f, -2.340416442e-02f },
+    { -8.030095283e-04f, +3.020294821e-02f, +9.936596666e-01f, -2.331155519e-02f },
+    { -7.979556282e-04f, +3.004668616e-02f, +9.937180939e-01f, -2.321880869e-02f },
+    { -7.929151965e-04f, +2.989057881e-02f, +9.937762527e-01f, -2.312592486e-02f },
+    { -7.878882187e-04f, +2.973462615e-02f, +9.938341429e-01f, -2.303290364e-02f },
+    { -7.828746805e-04f, +2.957882816e-02f, +9.938917647e-01f, -2.293974499e-02f },
+    { -7.778745675e-04f, +2.942318481e-02f, +9.939491178e-01f, -2.284644883e-02f },
+    { -7.728878654e-04f, +2.926769610e-02f, +9.940062023e-01f, -2.275301511e-02f },
+    { -7.679145598e-04f, +2.911236198e-02f, +9.940630182e-01f, -2.265944377e-02f },
+    { -7.629546363e-04f, +2.895718245e-02f, +9.941195654e-01f, -2.256573476e-02f },
+    { -7.580080805e-04f, +2.880215748e-02f, +9.941758439e-01f, -2.247188802e-02f },
+    { -7.530748780e-04f, +2.864728706e-02f, +9.942318536e-01f, -2.237790348e-02f },
+    { -7.481550144e-04f, +2.849257115e-02f, +9.942875945e-01f, -2.228378110e-02f },
+    { -7.432484754e-04f, +2.833800975e-02f, +9.943430666e-01f, -2.218952082e-02f },
+    { -7.383552465e-04f, +2.818360282e-02f, +9.943982699e-01f, -2.209512257e-02f },
+    { -7.334753133e-04f, +2.802935035e-02f, +9.944532042e-01f, -2.200058631e-02f },
+    { -7.286086614e-04f, +2.787525232e-02f, +9.945078696e-01f, -2.190591197e-02f },
+    { -7.237552763e-04f, +2.772130869e-02f, +9.945622660e-01f, -2.181109950e-02f },
+    { -7.189151437e-04f, +2.756751946e-02f, +9.946163934e-01f, -2.171614883e-02f },
+    { -7.140882492e-04f, +2.741388460e-02f, +9.946702518e-01f, -2.162105992e-02f },
+    { -7.092745782e-04f, +2.726040409e-02f, +9.947238411e-01f, -2.152583271e-02f },
+    { -7.044741164e-04f, +2.710707790e-02f, +9.947771613e-01f, -2.143046713e-02f },
+    { -6.996868493e-04f, +2.695390602e-02f, +9.948302124e-01f, -2.133496314e-02f },
+    { -6.949127625e-04f, +2.680088842e-02f, +9.948829943e-01f, -2.123932068e-02f },
+    { -6.901518415e-04f, +2.664802507e-02f, +9.949355069e-01f, -2.114353968e-02f },
+    { -6.854040719e-04f, +2.649531596e-02f, +9.949877504e-01f, -2.104762010e-02f },
+    { -6.806694392e-04f, +2.634276107e-02f, +9.950397245e-01f, -2.095156187e-02f },
+    { -6.759479289e-04f, +2.619036036e-02f, +9.950914294e-01f, -2.085536494e-02f },
+    { -6.712395266e-04f, +2.603811383e-02f, +9.951428649e-01f, -2.075902926e-02f },
+    { -6.665442179e-04f, +2.588602143e-02f, +9.951940310e-01f, -2.066255477e-02f },
+    { -6.618619881e-04f, +2.573408316e-02f, +9.952449278e-01f, -2.056594140e-02f },
+    { -6.571928229e-04f, +2.558229899e-02f, +9.952955551e-01f, -2.046918912e-02f },
+    { -6.525367078e-04f, +2.543066889e-02f, +9.953459129e-01f, -2.037229785e-02f },
+    { -6.478936283e-04f, +2.527919285e-02f, +9.953960012e-01f, -2.027526754e-02f },
+    { -6.432635698e-04f, +2.512787083e-02f, +9.954458200e-01f, -2.017809815e-02f },
+    { -6.386465180e-04f, +2.497670282e-02f, +9.954953692e-01f, -2.008078960e-02f },
+    { -6.340424582e-04f, +2.482568879e-02f, +9.955446489e-01f, -1.998334185e-02f },
+    { -6.294513760e-04f, +2.467482872e-02f, +9.955936589e-01f, -1.988575485e-02f },
+    { -6.248732570e-04f, +2.452412258e-02f, +9.956423992e-01f, -1.978802853e-02f },
+    { -6.203080865e-04f, +2.437357035e-02f, +9.956908698e-01f, -1.969016283e-02f },
+    { -6.157558500e-04f, +2.422317200e-02f, +9.957390708e-01f, -1.959215772e-02f },
+    { -6.112165332e-04f, +2.407292752e-02f, +9.957870019e-01f, -1.949401312e-02f },
+    { -6.066901213e-04f, +2.392283688e-02f, +9.958346633e-01f, -1.939572898e-02f },
+    { -6.021765999e-04f, +2.377290004e-02f, +9.958820549e-01f, -1.929730525e-02f },
+    { -5.976759545e-04f, +2.362311700e-02f, +9.959291766e-01f, -1.919874188e-02f },
+    { -5.931881705e-04f, +2.347348772e-02f, +9.959760285e-01f, -1.910003880e-02f },
+    { -5.887132334e-04f, +2.332401218e-02f, +9.960226105e-01f, -1.900119597e-02f },
+    { -5.842511287e-04f, +2.317469035e-02f, +9.960689225e-01f, -1.890221333e-02f },
+    { -5.798018418e-04f, +2.302552221e-02f, +9.961149645e-01f, -1.880309082e-02f },
+    { -5.753653581e-04f, +2.287650774e-02f, +9.961607366e-01f, -1.870382839e-02f },
+    { -5.709416631e-04f, +2.272764690e-02f, +9.962062387e-01f, -1.860442598e-02f },
+    { -5.665307423e-04f, +2.257893968e-02f, +9.962514707e-01f, -1.850488354e-02f },
+    { -5.621325811e-04f, +2.243038605e-02f, +9.962964326e-01f, -1.840520102e-02f },
+    { -5.577471650e-04f, +2.228198598e-02f, +9.963411244e-01f, -1.830537836e-02f },
+    { -5.533744792e-04f, +2.213373945e-02f, +9.963855461e-01f, -1.820541550e-02f },
+    { -5.490145094e-04f, +2.198564643e-02f, +9.964296976e-01f, -1.810531240e-02f },
+    { -5.446672410e-04f, +2.183770690e-02f, +9.964735789e-01f, -1.800506899e-02f },
+    { -5.403326592e-04f, +2.168992083e-02f, +9.965171900e-01f, -1.790468523e-02f },
+    { -5.360107497e-04f, +2.154228819e-02f, +9.965605309e-01f, -1.780416106e-02f },
+    { -5.317014977e-04f, +2.139480896e-02f, +9.966036014e-01f, -1.770349642e-02f },
+    { -5.274048887e-04f, +2.124748312e-02f, +9.966464017e-01f, -1.760269126e-02f },
+    { -5.231209082e-04f, +2.110031063e-02f, +9.966889317e-01f, -1.750174553e-02f },
+    { -5.188495414e-04f, +2.095329147e-02f, +9.967311913e-01f, -1.740065918e-02f },
+    { -5.145907739e-04f, +2.080642562e-02f, +9.967731805e-01f, -1.729943214e-02f },
+    { -5.103445911e-04f, +2.065971304e-02f, +9.968148993e-01f, -1.719806437e-02f },
+    { -5.061109782e-04f, +2.051315372e-02f, +9.968563476e-01f, -1.709655581e-02f },
+    { -5.018899208e-04f, +2.036674762e-02f, +9.968975255e-01f, -1.699490642e-02f },
+    { -4.976814042e-04f, +2.022049471e-02f, +9.969384329e-01f, -1.689311613e-02f },
+    { -4.934854139e-04f, +2.007439498e-02f, +9.969790698e-01f, -1.679118489e-02f },
+    { -4.893019351e-04f, +1.992844839e-02f, +9.970194362e-01f, -1.668911265e-02f },
+    { -4.851309533e-04f, +1.978265492e-02f, +9.970595319e-01f, -1.658689935e-02f },
+    { -4.809724538e-04f, +1.963701453e-02f, +9.970993571e-01f, -1.648454495e-02f },
+    { -4.768264221e-04f, +1.949152721e-02f, +9.971389117e-01f, -1.638204939e-02f },
+    { -4.726928435e-04f, +1.934619292e-02f, +9.971781956e-01f, -1.627941262e-02f },
+    { -4.685717034e-04f, +1.920101164e-02f, +9.972172089e-01f, -1.617663458e-02f },
+    { -4.644629872e-04f, +1.905598334e-02f, +9.972559515e-01f, -1.607371522e-02f },
+    { -4.603666801e-04f, +1.891110799e-02f, +9.972944233e-01f, -1.597065449e-02f },
+    { -4.562827677e-04f, +1.876638556e-02f, +9.973326244e-01f, -1.586745234e-02f },
+    { -4.522112352e-04f, +1.862181603e-02f, +9.973705548e-01f, -1.576410871e-02f },
+    { -4.481520680e-04f, +1.847739937e-02f, +9.974082143e-01f, -1.566062355e-02f },
+    { -4.441052515e-04f, +1.833313554e-02f, +9.974456031e-01f, -1.555699680e-02f },
+    { -4.400707710e-04f, +1.818902453e-02f, +9.974827210e-01f, -1.545322843e-02f },
+    { -4.360486119e-04f, +1.804506631e-02f, +9.975195680e-01f, -1.534931837e-02f },
+    { -4.320387594e-04f, +1.790126083e-02f, +9.975561442e-01f, -1.524526657e-02f },
+    { -4.280411991e-04f, +1.775760809e-02f, +9.975924494e-01f, -1.514107297e-02f },
+    { -4.240559161e-04f, +1.761410804e-02f, +9.976284837e-01f, -1.503673754e-02f },
+    { -4.200828959e-04f, +1.747076066e-02f, +9.976642471e-01f, -1.493226021e-02f },
+    { -4.161221238e-04f, +1.732756592e-02f, +9.976997395e-01f, -1.482764093e-02f },
+    { -4.121735850e-04f, +1.718452380e-02f, +9.977349608e-01f, -1.472287966e-02f },
+    { -4.082372651e-04f, +1.704163425e-02f, +9.977699112e-01f, -1.461797634e-02f },
+    { -4.043131492e-04f, +1.689889726e-02f, +9.978045905e-01f, -1.451293091e-02f },
+    { -4.004012227e-04f, +1.675631280e-02f, +9.978389988e-01f, -1.440774333e-02f },
+    { -3.965014710e-04f, +1.661388082e-02f, +9.978731359e-01f, -1.430241355e-02f },
+    { -3.926138794e-04f, +1.647160132e-02f, +9.979070020e-01f, -1.419694151e-02f },
+    { -3.887384331e-04f, +1.632947425e-02f, +9.979405969e-01f, -1.409132716e-02f },
+    { -3.848751175e-04f, +1.618749959e-02f, +9.979739207e-01f, -1.398557045e-02f },
+    { -3.810239179e-04f, +1.604567730e-02f, +9.980069732e-01f, -1.387967133e-02f },
+    { -3.771848197e-04f, +1.590400737e-02f, +9.980397546e-01f, -1.377362975e-02f },
+    { -3.733578082e-04f, +1.576248975e-02f, +9.980722648e-01f, -1.366744566e-02f },
+    { -3.695428686e-04f, +1.562112441e-02f, +9.981045038e-01f, -1.356111900e-02f },
+    { -3.657399862e-04f, +1.547991134e-02f, +9.981364714e-01f, -1.345464973e-02f },
+    { -3.619491465e-04f, +1.533885049e-02f, +9.981681678e-01f, -1.334803780e-02f },
+    { -3.581703346e-04f, +1.519794184e-02f, +9.981995929e-01f, -1.324128315e-02f },
+    { -3.544035359e-04f, +1.505718535e-02f, +9.982307467e-01f, -1.313438573e-02f },
+    { -3.506487357e-04f, +1.491658101e-02f, +9.982616292e-01f, -1.302734550e-02f },
+    { -3.469059193e-04f, +1.477612876e-02f, +9.982922403e-01f, -1.292016240e-02f },
+    { -3.431750720e-04f, +1.463582860e-02f, +9.983225800e-01f, -1.281283638e-02f },
+    { -3.394561791e-04f, +1.449568047e-02f, +9.983526483e-01f, -1.270536739e-02f },
+    { -3.357492258e-04f, +1.435568436e-02f, +9.983824452e-01f, -1.259775538e-02f },
+    { -3.320541975e-04f, +1.421584024e-02f, +9.984119707e-01f, -1.249000030e-02f },
+    { -3.283710794e-04f, +1.407614806e-02f, +9.984412247e-01f, -1.238210211e-02f },
+    { -3.246998569e-04f, +1.393660781e-02f, +9.984702072e-01f, -1.227406075e-02f },
+    { -3.210405152e-04f, +1.379721944e-02f, +9.984989183e-01f, -1.216587616e-02f },
+    { -3.173930397e-04f, +1.365798293e-02f, +9.985273578e-01f, -1.205754831e-02f },
+    { -3.137574155e-04f, +1.351889825e-02f, +9.985555259e-01f, -1.194907714e-02f },
+    { -3.101336280e-04f, +1.337996537e-02f, +9.985834223e-01f, -1.184046260e-02f },
+    { -3.065216624e-04f, +1.324118424e-02f, +9.986110473e-01f, -1.173170465e-02f },
+    { -3.029215041e-04f, +1.310255485e-02f, +9.986384006e-01f, -1.162280322e-02f },
+    { -2.993331383e-04f, +1.296407716e-02f, +9.986654823e-01f, -1.151375828e-02f },
+    { -2.957565503e-04f, +1.282575114e-02f, +9.986922924e-01f, -1.140456977e-02f },
+    { -2.921917254e-04f, +1.268757675e-02f, +9.987188309e-01f, -1.129523765e-02f },
+    { -2.886386488e-04f, +1.254955397e-02f, +9.987450978e-01f, -1.118576186e-02f },
+    { -2.850973058e-04f, +1.241168275e-02f, +9.987710929e-01f, -1.107614236e-02f },
+    { -2.815676816e-04f, +1.227396308e-02f, +9.987968164e-01f, -1.096637910e-02f },
+    { -2.780497616e-04f, +1.213639492e-02f, +9.988222682e-01f, -1.085647202e-02f },
+    { -2.745435310e-04f, +1.199897823e-02f, +9.988474483e-01f, -1.074642108e-02f },
+    { -2.710489751e-04f, +1.186171298e-02f, +9.988723567e-01f, -1.063622623e-02f },
+    { -2.675660791e-04f, +1.172459914e-02f, +9.988969933e-01f, -1.052588743e-02f },
+    { -2.640948284e-04f, +1.158763668e-02f, +9.989213581e-01f, -1.041540461e-02f },
+    { -2.606352080e-04f, +1.145082556e-02f, +9.989454512e-01f, -1.030477774e-02f },
+    { -2.571872034e-04f, +1.131416575e-02f, +9.989692724e-01f, -1.019400677e-02f },
+    { -2.537507998e-04f, +1.117765722e-02f, +9.989928219e-01f, -1.008309164e-02f },
+    { -2.503259824e-04f, +1.104129994e-02f, +9.990160995e-01f, -9.972032308e-03f },
+    { -2.469127365e-04f, +1.090509386e-02f, +9.990391053e-01f, -9.860828729e-03f },
+    { -2.435110474e-04f, +1.076903897e-02f, +9.990618393e-01f, -9.749480853e-03f },
+    { -2.401209002e-04f, +1.063313522e-02f, +9.990843014e-01f, -9.637988631e-03f },
+    { -2.367422804e-04f, +1.049738258e-02f, +9.991064916e-01f, -9.526352014e-03f },
+    { -2.333751730e-04f, +1.036178102e-02f, +9.991284099e-01f, -9.414570956e-03f },
+    { -2.300195634e-04f, +1.022633050e-02f, +9.991500563e-01f, -9.302645409e-03f },
+    { -2.266754367e-04f, +1.009103099e-02f, +9.991714307e-01f, -9.190575323e-03f },
+    { -2.233427784e-04f, +9.955882461e-03f, +9.991925333e-01f, -9.078360653e-03f },
+    { -2.200215735e-04f, +9.820884872e-03f, +9.992133639e-01f, -8.966001349e-03f },
+    { -2.167118075e-04f, +9.686038192e-03f, +9.992339225e-01f, -8.853497366e-03f },
+    { -2.134134654e-04f, +9.551342385e-03f, +9.992542091e-01f, -8.740848654e-03f },
+    { -2.101265325e-04f, +9.416797418e-03f, +9.992742238e-01f, -8.628055168e-03f },
+    { -2.068509942e-04f, +9.282403256e-03f, +9.992939664e-01f, -8.515116858e-03f },
+    { -2.035868356e-04f, +9.148159865e-03f, +9.993134371e-01f, -8.402033679e-03f },
+    { -2.003340419e-04f, +9.014067211e-03f, +9.993326357e-01f, -8.288805583e-03f },
+    { -1.970925985e-04f, +8.880125258e-03f, +9.993515622e-01f, -8.175432524e-03f },
+    { -1.938624906e-04f, +8.746333973e-03f, +9.993702168e-01f, -8.061914453e-03f },
+    { -1.906437034e-04f, +8.612693320e-03f, +9.993885992e-01f, -7.948251325e-03f },
+    { -1.874362221e-04f, +8.479203263e-03f, +9.994067096e-01f, -7.834443092e-03f },
+    { -1.842400320e-04f, +8.345863769e-03f, +9.994245479e-01f, -7.720489707e-03f },
+    { -1.810551183e-04f, +8.212674803e-03f, +9.994421141e-01f, -7.606391125e-03f },
+    { -1.778814663e-04f, +8.079636328e-03f, +9.994594082e-01f, -7.492147298e-03f },
+    { -1.747190612e-04f, +7.946748310e-03f, +9.994764302e-01f, -7.377758181e-03f },
+    { -1.715678882e-04f, +7.814010713e-03f, +9.994931800e-01f, -7.263223725e-03f },
+    { -1.684279326e-04f, +7.681423501e-03f, +9.995096577e-01f, -7.148543887e-03f },
+    { -1.652991797e-04f, +7.548986640e-03f, +9.995258632e-01f, -7.033718618e-03f },
+    { -1.621816145e-04f, +7.416700094e-03f, +9.995417966e-01f, -6.918747873e-03f },
+    { -1.590752225e-04f, +7.284563826e-03f, +9.995574578e-01f, -6.803631606e-03f },
+    { -1.559799888e-04f, +7.152577801e-03f, +9.995728468e-01f, -6.688369772e-03f },
+    { -1.528958987e-04f, +7.020741983e-03f, +9.995879637e-01f, -6.572962323e-03f },
+    { -1.498229373e-04f, +6.889056337e-03f, +9.996028083e-01f, -6.457409214e-03f },
+    { -1.467610900e-04f, +6.757520824e-03f, +9.996173807e-01f, -6.341710400e-03f },
+    { -1.437103420e-04f, +6.626135411e-03f, +9.996316809e-01f, -6.225865834e-03f },
+    { -1.406706784e-04f, +6.494900059e-03f, +9.996457089e-01f, -6.109875472e-03f },
+    { -1.376420846e-04f, +6.363814734e-03f, +9.996594646e-01f, -5.993739268e-03f },
+    { -1.346245458e-04f, +6.232879398e-03f, +9.996729481e-01f, -5.877457176e-03f },
+    { -1.316180471e-04f, +6.102094014e-03f, +9.996861593e-01f, -5.761029151e-03f },
+    { -1.286225739e-04f, +5.971458547e-03f, +9.996990983e-01f, -5.644455148e-03f },
+    { -1.256381113e-04f, +5.840972959e-03f, +9.997117650e-01f, -5.527735122e-03f },
+    { -1.226646447e-04f, +5.710637213e-03f, +9.997241594e-01f, -5.410869028e-03f },
+    { -1.197021592e-04f, +5.580451273e-03f, +9.997362815e-01f, -5.293856820e-03f },
+    { -1.167506400e-04f, +5.450415101e-03f, +9.997481313e-01f, -5.176698454e-03f },
+    { -1.138100725e-04f, +5.320528660e-03f, +9.997597089e-01f, -5.059393885e-03f },
+    { -1.108804418e-04f, +5.190791913e-03f, +9.997710141e-01f, -4.941943068e-03f },
+    { -1.079617332e-04f, +5.061204824e-03f, +9.997820469e-01f, -4.824345959e-03f },
+    { -1.050539318e-04f, +4.931767353e-03f, +9.997928075e-01f, -4.706602513e-03f },
+    { -1.021570230e-04f, +4.802479464e-03f, +9.998032957e-01f, -4.588712686e-03f },
+    { -9.927099199e-05f, +4.673341119e-03f, +9.998135116e-01f, -4.470676433e-03f },
+    { -9.639582397e-05f, +4.544352281e-03f, +9.998234552e-01f, -4.352493710e-03f },
+    { -9.353150418e-05f, +4.415512912e-03f, +9.998331264e-01f, -4.234164473e-03f },
+    { -9.067801786e-05f, +4.286822973e-03f, +9.998425252e-01f, -4.115688677e-03f },
+    { -8.783535025e-05f, +4.158282427e-03f, +9.998516517e-01f, -3.997066279e-03f },
+    { -8.500348659e-05f, +4.029891236e-03f, +9.998605058e-01f, -3.878297235e-03f },
+    { -8.218241209e-05f, +3.901649361e-03f, +9.998690875e-01f, -3.759381500e-03f },
+    { -7.937211202e-05f, +3.773556765e-03f, +9.998773969e-01f, -3.640319031e-03f },
+    { -7.657257158e-05f, +3.645613409e-03f, +9.998854338e-01f, -3.521109785e-03f },
+    { -7.378377604e-05f, +3.517819255e-03f, +9.998931984e-01f, -3.401753717e-03f },
+    { -7.100571061e-05f, +3.390174264e-03f, +9.999006906e-01f, -3.282250785e-03f },
+    { -6.823836055e-05f, +3.262678397e-03f, +9.999079103e-01f, -3.162600944e-03f },
+    { -6.548171107e-05f, +3.135331617e-03f, +9.999148577e-01f, -3.042804151e-03f },
+    { -6.273574744e-05f, +3.008133883e-03f, +9.999215326e-01f, -2.922860364e-03f },
+    { -6.000045487e-05f, +2.881085158e-03f, +9.999279352e-01f, -2.802769538e-03f },
+    { -5.727581862e-05f, +2.754185402e-03f, +9.999340653e-01f, -2.682531632e-03f },
+    { -5.456182391e-05f, +2.627434576e-03f, +9.999399230e-01f, -2.562146601e-03f },
+    { -5.185845600e-05f, +2.500832641e-03f, +9.999455083e-01f, -2.441614402e-03f },
+    { -4.916570012e-05f, +2.374379558e-03f, +9.999508211e-01f, -2.320934994e-03f },
+    { -4.648354151e-05f, +2.248075287e-03f, +9.999558615e-01f, -2.200108333e-03f },
+    { -4.381196542e-05f, +2.121919790e-03f, +9.999606295e-01f, -2.079134377e-03f },
+    { -4.115095708e-05f, +1.995913026e-03f, +9.999651250e-01f, -1.958013083e-03f },
+    { -3.850050175e-05f, +1.870054956e-03f, +9.999693481e-01f, -1.836744408e-03f },
+    { -3.586058466e-05f, +1.744345541e-03f, +9.999732988e-01f, -1.715328311e-03f },
+    { -3.323119106e-05f, +1.618784740e-03f, +9.999769770e-01f, -1.593764748e-03f },
+    { -3.061230620e-05f, +1.493372514e-03f, +9.999803827e-01f, -1.472053677e-03f },
+    { -2.800391533e-05f, +1.368108822e-03f, +9.999835160e-01f, -1.350195058e-03f },
+    { -2.540600368e-05f, +1.242993626e-03f, +9.999863768e-01f, -1.228188846e-03f },
+    { -2.281855651e-05f, +1.118026884e-03f, +9.999889652e-01f, -1.106035001e-03f },
+    { -2.024155907e-05f, +9.932085563e-04f, +9.999912812e-01f, -9.837334803e-04f },
+    { -1.767499661e-05f, +8.685386028e-04f, +9.999933246e-01f, -8.612842424e-04f },
+    { -1.511885438e-05f, +7.440169831e-04f, +9.999950956e-01f, -7.386872455e-04f },
+    { -1.257311763e-05f, +6.196436566e-04f, +9.999965942e-01f, -6.159424479e-04f },
+    { -1.003777162e-05f, +4.954185828e-04f, +9.999978203e-01f, -4.930498082e-04f },
+    { -7.512801591e-06f, +3.713417210e-04f, +9.999987739e-01f, -3.700092848e-04f },
+    { -4.998192808e-06f, +2.474130305e-04f, +9.999994551e-01f, -2.468208365e-04f },
+    { -2.493930525e-06f, +1.236324705e-04f, +9.999998638e-01f, -1.234844220e-04f },
+};

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

@@ -1,6 +1,8 @@
 #ifndef AL_COMPAT_H
 #define AL_COMPAT_H
 
+#include "alstring.h"
+
 #ifdef _WIN32
 
 #define WIN32_LEAN_AND_MEAN
@@ -23,10 +25,33 @@ FILE *al_fopen(const char *fname, const char *mode);
 
 #endif
 
+struct FileMapping {
+#ifdef _WIN32
+    HANDLE file;
+    HANDLE fmap;
+#else
+    int fd;
+#endif
+    void *ptr;
+    size_t len;
+};
+struct FileMapping MapFileToMem(const char *fname);
+void UnmapFileMem(const struct FileMapping *mapping);
+
+al_string GetProcPath(void);
+
 #ifdef HAVE_DYNLOAD
 void *LoadLib(const char *name);
 void CloseLib(void *handle);
 void *GetSymbol(void *handle, const char *name);
 #endif
 
+#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
+
 #endif /* AL_COMPAT_H */

+ 466 - 0
libs/openal-soft/Alc/converter.c

@@ -0,0 +1,466 @@
+
+#include "config.h"
+
+#include "converter.h"
+
+#include "mixer_defs.h"
+
+
+SampleConverter *CreateSampleConverter(enum DevFmtType srcType, enum DevFmtType dstType, ALsizei numchans, ALsizei srcRate, ALsizei dstRate)
+{
+    SampleConverter *converter;
+    ALsizei step;
+
+    if(numchans <= 0 || srcRate <= 0 || dstRate <= 0)
+        return NULL;
+
+    converter = al_calloc(16, FAM_SIZE(SampleConverter, Chan, numchans));
+    converter->mSrcType = srcType;
+    converter->mDstType = dstType;
+    converter->mNumChannels = numchans;
+    converter->mSrcTypeSize = BytesFromDevFmt(srcType);
+    converter->mDstTypeSize = BytesFromDevFmt(dstType);
+
+    converter->mSrcPrepCount = 0;
+    converter->mFracOffset = 0;
+
+    /* Have to set the mixer FPU mode since that's what the resampler code expects. */
+    START_MIXER_MODE();
+    step = fastf2i(minf((ALdouble)srcRate / dstRate, MAX_PITCH)*FRACTIONONE + 0.5f);
+    converter->mIncrement = maxi(step, 1);
+    if(converter->mIncrement == FRACTIONONE)
+        converter->mResample = Resample_copy32_C;
+    else
+    {
+        /* TODO: Allow other resamplers. */
+        BsincPrepare(converter->mIncrement, &converter->mState.bsinc);
+        converter->mResample = SelectResampler(BSincResampler);
+    }
+    END_MIXER_MODE();
+
+    return converter;
+}
+
+void DestroySampleConverter(SampleConverter **converter)
+{
+    if(converter)
+    {
+        al_free(*converter);
+        *converter = NULL;
+    }
+}
+
+
+static inline ALfloat Sample_ALbyte(ALbyte val)
+{ return val * (1.0f/128.0f); }
+static inline ALfloat Sample_ALubyte(ALubyte val)
+{ return Sample_ALbyte((ALint)val - 128); }
+
+static inline ALfloat Sample_ALshort(ALshort val)
+{ return val * (1.0f/32768.0f); }
+static inline ALfloat Sample_ALushort(ALushort val)
+{ return Sample_ALshort((ALint)val - 32768); }
+
+static inline ALfloat Sample_ALint(ALint val)
+{ return (val>>7) * (1.0f/16777216.0f); }
+static inline ALfloat Sample_ALuint(ALuint val)
+{ return Sample_ALint(val - INT_MAX - 1); }
+
+static inline ALfloat Sample_ALfloat(ALfloat val)
+{ return val; }
+
+#define DECL_TEMPLATE(T)                                                      \
+static inline void Load_##T(ALfloat *restrict dst, const T *restrict src,     \
+                            ALint srcstep, ALsizei samples)                   \
+{                                                                             \
+    ALsizei i;                                                                \
+    for(i = 0;i < samples;i++)                                                \
+        dst[i] = Sample_##T(src[i*srcstep]);                                  \
+}
+
+DECL_TEMPLATE(ALbyte)
+DECL_TEMPLATE(ALubyte)
+DECL_TEMPLATE(ALshort)
+DECL_TEMPLATE(ALushort)
+DECL_TEMPLATE(ALint)
+DECL_TEMPLATE(ALuint)
+DECL_TEMPLATE(ALfloat)
+
+#undef DECL_TEMPLATE
+
+static void LoadSamples(ALfloat *dst, const ALvoid *src, ALint srcstep, enum DevFmtType srctype, ALsizei samples)
+{
+    switch(srctype)
+    {
+        case DevFmtByte:
+            Load_ALbyte(dst, src, srcstep, samples);
+            break;
+        case DevFmtUByte:
+            Load_ALubyte(dst, src, srcstep, samples);
+            break;
+        case DevFmtShort:
+            Load_ALshort(dst, src, srcstep, samples);
+            break;
+        case DevFmtUShort:
+            Load_ALushort(dst, src, srcstep, samples);
+            break;
+        case DevFmtInt:
+            Load_ALint(dst, src, srcstep, samples);
+            break;
+        case DevFmtUInt:
+            Load_ALuint(dst, src, srcstep, samples);
+            break;
+        case DevFmtFloat:
+            Load_ALfloat(dst, src, srcstep, samples);
+            break;
+    }
+}
+
+
+static inline ALbyte ALbyte_Sample(ALfloat val)
+{ return fastf2i(clampf(val*128.0f, -128.0f, 127.0f)); }
+static inline ALubyte ALubyte_Sample(ALfloat val)
+{ return ALbyte_Sample(val)+128; }
+
+static inline ALshort ALshort_Sample(ALfloat val)
+{ return fastf2i(clampf(val*32768.0f, -32768.0f, 32767.0f)); }
+static inline ALushort ALushort_Sample(ALfloat val)
+{ return ALshort_Sample(val)+32768; }
+
+static inline ALint ALint_Sample(ALfloat val)
+{ return fastf2i(clampf(val*16777216.0f, -16777216.0f, 16777215.0f)) << 7; }
+static inline ALuint ALuint_Sample(ALfloat val)
+{ return ALint_Sample(val)+INT_MAX+1; }
+
+static inline ALfloat ALfloat_Sample(ALfloat val)
+{ return val; }
+
+#define DECL_TEMPLATE(T)                                                      \
+static inline void Store_##T(T *restrict dst, const ALfloat *restrict src,    \
+                             ALint dststep, ALsizei samples)                  \
+{                                                                             \
+    ALsizei i;                                                                \
+    for(i = 0;i < samples;i++)                                                \
+        dst[i*dststep] = T##_Sample(src[i]);                                  \
+}
+
+DECL_TEMPLATE(ALbyte)
+DECL_TEMPLATE(ALubyte)
+DECL_TEMPLATE(ALshort)
+DECL_TEMPLATE(ALushort)
+DECL_TEMPLATE(ALint)
+DECL_TEMPLATE(ALuint)
+DECL_TEMPLATE(ALfloat)
+
+#undef DECL_TEMPLATE
+
+static void StoreSamples(ALvoid *dst, const ALfloat *src, ALint dststep, enum DevFmtType dsttype, ALsizei samples)
+{
+    switch(dsttype)
+    {
+        case DevFmtByte:
+            Store_ALbyte(dst, src, dststep, samples);
+            break;
+        case DevFmtUByte:
+            Store_ALubyte(dst, src, dststep, samples);
+            break;
+        case DevFmtShort:
+            Store_ALshort(dst, src, dststep, samples);
+            break;
+        case DevFmtUShort:
+            Store_ALushort(dst, src, dststep, samples);
+            break;
+        case DevFmtInt:
+            Store_ALint(dst, src, dststep, samples);
+            break;
+        case DevFmtUInt:
+            Store_ALuint(dst, src, dststep, samples);
+            break;
+        case DevFmtFloat:
+            Store_ALfloat(dst, src, dststep, samples);
+            break;
+    }
+}
+
+
+ALsizei SampleConverterAvailableOut(SampleConverter *converter, ALsizei srcframes)
+{
+    ALint prepcount = converter->mSrcPrepCount;
+    ALsizei increment = converter->mIncrement;
+    ALsizei DataPosFrac = converter->mFracOffset;
+    ALuint64 DataSize64;
+
+    if(prepcount < 0)
+    {
+        /* Negative prepcount means we need to skip that many input samples. */
+        if(-prepcount >= srcframes)
+            return 0;
+        srcframes += prepcount;
+        prepcount = 0;
+    }
+
+    if(srcframes < 1)
+    {
+        /* No output samples if there's no input samples. */
+        return 0;
+    }
+
+    if(prepcount < MAX_POST_SAMPLES+MAX_PRE_SAMPLES &&
+       MAX_POST_SAMPLES+MAX_PRE_SAMPLES-prepcount >= srcframes)
+    {
+        /* Not enough input samples to generate an output sample. */
+        return 0;
+    }
+
+    DataSize64  = prepcount;
+    DataSize64 += srcframes;
+    DataSize64 -= MAX_POST_SAMPLES+MAX_PRE_SAMPLES;
+    DataSize64 <<= FRACTIONBITS;
+    DataSize64 -= DataPosFrac;
+
+    /* If we have a full prep, we can generate at least one sample. */
+    return (ALsizei)clampu64((DataSize64 + increment-1)/increment, 1, BUFFERSIZE);
+}
+
+
+ALsizei SampleConverterInput(SampleConverter *converter, const ALvoid **src, ALsizei *srcframes, ALvoid *dst, ALsizei dstframes)
+{
+    const ALsizei SrcFrameSize = converter->mNumChannels * converter->mSrcTypeSize;
+    const ALsizei DstFrameSize = converter->mNumChannels * converter->mDstTypeSize;
+    const ALsizei increment = converter->mIncrement;
+    ALsizei pos = 0;
+
+    START_MIXER_MODE();
+    while(pos < dstframes && *srcframes > 0)
+    {
+        ALfloat *restrict SrcData = ASSUME_ALIGNED(converter->mSrcSamples, 16);
+        ALfloat *restrict DstData = ASSUME_ALIGNED(converter->mDstSamples, 16);
+        ALint prepcount = converter->mSrcPrepCount;
+        ALsizei DataPosFrac = converter->mFracOffset;
+        ALuint64 DataSize64;
+        ALsizei DstSize;
+        ALint toread;
+        ALsizei chan;
+
+        if(prepcount < 0)
+        {
+            /* Negative prepcount means we need to skip that many input samples. */
+            if(-prepcount >= *srcframes)
+            {
+                converter->mSrcPrepCount = prepcount + *srcframes;
+                *srcframes = 0;
+                break;
+            }
+            *src = (const ALbyte*)*src + SrcFrameSize*-prepcount;
+            *srcframes += prepcount;
+            converter->mSrcPrepCount = 0;
+            continue;
+        }
+        toread = mini(*srcframes, BUFFERSIZE-(MAX_POST_SAMPLES+MAX_PRE_SAMPLES));
+
+        if(prepcount < MAX_POST_SAMPLES+MAX_PRE_SAMPLES &&
+           MAX_POST_SAMPLES+MAX_PRE_SAMPLES-prepcount >= toread)
+        {
+            /* Not enough input samples to generate an output sample. Store
+             * what we're given for later.
+             */
+            for(chan = 0;chan < converter->mNumChannels;chan++)
+                LoadSamples(&converter->Chan[chan].mPrevSamples[prepcount],
+                    (const ALbyte*)*src + converter->mSrcTypeSize*chan,
+                    converter->mNumChannels, converter->mSrcType, toread
+                );
+
+            converter->mSrcPrepCount = prepcount + toread;
+            *srcframes = 0;
+            break;
+        }
+
+        DataSize64  = prepcount;
+        DataSize64 += toread;
+        DataSize64 -= MAX_POST_SAMPLES+MAX_PRE_SAMPLES;
+        DataSize64 <<= FRACTIONBITS;
+        DataSize64 -= DataPosFrac;
+
+        /* If we have a full prep, we can generate at least one sample. */
+        DstSize = (ALsizei)clampu64((DataSize64 + increment-1)/increment, 1, BUFFERSIZE);
+        DstSize = mini(DstSize, dstframes-pos);
+
+        for(chan = 0;chan < converter->mNumChannels;chan++)
+        {
+            const ALbyte *SrcSamples = (const ALbyte*)*src + converter->mSrcTypeSize*chan;
+            ALbyte *DstSamples = (ALbyte*)dst + converter->mDstTypeSize*chan;
+            const ALfloat *ResampledData;
+            ALsizei SrcDataEnd;
+
+            /* Load the previous samples into the source data first, then the
+             * new samples from the input buffer.
+             */
+            memcpy(SrcData, converter->Chan[chan].mPrevSamples,
+                   prepcount*sizeof(ALfloat));
+            LoadSamples(SrcData + prepcount, SrcSamples,
+                converter->mNumChannels, converter->mSrcType, toread
+            );
+
+            /* Store as many prep samples for next time as possible, given the
+             * number of output samples being generated.
+             */
+            SrcDataEnd = (DataPosFrac + increment*DstSize)>>FRACTIONBITS;
+            if(SrcDataEnd >= prepcount+toread)
+                memset(converter->Chan[chan].mPrevSamples, 0,
+                       sizeof(converter->Chan[chan].mPrevSamples));
+            else
+            {
+                size_t len = mini(MAX_PRE_SAMPLES+MAX_POST_SAMPLES, prepcount+toread-SrcDataEnd);
+                memcpy(converter->Chan[chan].mPrevSamples, &SrcData[SrcDataEnd],
+                       len*sizeof(ALfloat));
+                memset(converter->Chan[chan].mPrevSamples+len, 0,
+                       sizeof(converter->Chan[chan].mPrevSamples) - len*sizeof(ALfloat));
+            }
+
+            /* Now resample, and store the result in the output buffer. */
+            ResampledData = converter->mResample(&converter->mState,
+                SrcData+MAX_PRE_SAMPLES, DataPosFrac, increment,
+                DstData, DstSize
+            );
+
+            StoreSamples(DstSamples, ResampledData, converter->mNumChannels,
+                         converter->mDstType, DstSize);
+        }
+
+        /* Update the number of prep samples still available, as well as the
+         * fractional offset.
+         */
+        DataPosFrac += increment*DstSize;
+        converter->mSrcPrepCount = mini(MAX_PRE_SAMPLES+MAX_POST_SAMPLES,
+                                        prepcount+toread-(DataPosFrac>>FRACTIONBITS));
+        converter->mFracOffset = DataPosFrac & FRACTIONMASK;
+
+        /* Update the src and dst pointers in case there's still more to do. */
+        *src = (const ALbyte*)*src + SrcFrameSize*(DataPosFrac>>FRACTIONBITS);
+        *srcframes -= mini(*srcframes, (DataPosFrac>>FRACTIONBITS));
+
+        dst = (ALbyte*)dst + DstFrameSize*DstSize;
+        pos += DstSize;
+    }
+    END_MIXER_MODE();
+
+    return pos;
+}
+
+
+ChannelConverter *CreateChannelConverter(enum DevFmtType srcType, enum DevFmtChannels srcChans, enum DevFmtChannels dstChans)
+{
+    ChannelConverter *converter;
+
+    if(srcChans != dstChans && !((srcChans == DevFmtMono && dstChans == DevFmtStereo) ||
+                                 (srcChans == DevFmtStereo && dstChans == DevFmtMono)))
+        return NULL;
+
+    converter = al_calloc(DEF_ALIGN, sizeof(*converter));
+    converter->mSrcType = srcType;
+    converter->mSrcChans = srcChans;
+    converter->mDstChans = dstChans;
+
+    return converter;
+}
+
+void DestroyChannelConverter(ChannelConverter **converter)
+{
+    if(converter)
+    {
+        al_free(*converter);
+        *converter = NULL;
+    }
+}
+
+
+#define DECL_TEMPLATE(T)                                                       \
+static void Mono2Stereo##T(ALfloat *restrict dst, const T *src, ALsizei frames)\
+{                                                                              \
+    ALsizei i;                                                                 \
+    for(i = 0;i < frames;i++)                                                  \
+        dst[i*2 + 1] = dst[i*2 + 0] = Sample_##T(src[i]) * 0.707106781187f;    \
+}                                                                              \
+                                                                               \
+static void Stereo2Mono##T(ALfloat *restrict dst, const T *src, ALsizei frames)\
+{                                                                              \
+    ALsizei i;                                                                 \
+    for(i = 0;i < frames;i++)                                                  \
+        dst[i] = (Sample_##T(src[i*2 + 0])+Sample_##T(src[i*2 + 1])) *         \
+                 0.707106781187f;                                              \
+}
+
+DECL_TEMPLATE(ALbyte)
+DECL_TEMPLATE(ALubyte)
+DECL_TEMPLATE(ALshort)
+DECL_TEMPLATE(ALushort)
+DECL_TEMPLATE(ALint)
+DECL_TEMPLATE(ALuint)
+DECL_TEMPLATE(ALfloat)
+
+#undef DECL_TEMPLATE
+
+void ChannelConverterInput(ChannelConverter *converter, const ALvoid *src, ALfloat *dst, ALsizei frames)
+{
+    if(converter->mSrcChans == converter->mDstChans)
+    {
+        LoadSamples(dst, src, 1, converter->mSrcType,
+                    frames*ChannelsFromDevFmt(converter->mSrcChans, 0));
+        return;
+    }
+
+    if(converter->mSrcChans == DevFmtStereo && converter->mDstChans == DevFmtMono)
+    {
+        switch(converter->mSrcType)
+        {
+            case DevFmtByte:
+                Stereo2MonoALbyte(dst, src, frames);
+                break;
+            case DevFmtUByte:
+                Stereo2MonoALubyte(dst, src, frames);
+                break;
+            case DevFmtShort:
+                Stereo2MonoALshort(dst, src, frames);
+                break;
+            case DevFmtUShort:
+                Stereo2MonoALushort(dst, src, frames);
+                break;
+            case DevFmtInt:
+                Stereo2MonoALint(dst, src, frames);
+                break;
+            case DevFmtUInt:
+                Stereo2MonoALuint(dst, src, frames);
+                break;
+            case DevFmtFloat:
+                Stereo2MonoALfloat(dst, src, frames);
+                break;
+        }
+    }
+    else /*if(converter->mSrcChans == DevFmtMono && converter->mDstChans == DevFmtStereo)*/
+    {
+        switch(converter->mSrcType)
+        {
+            case DevFmtByte:
+                Mono2StereoALbyte(dst, src, frames);
+                break;
+            case DevFmtUByte:
+                Mono2StereoALubyte(dst, src, frames);
+                break;
+            case DevFmtShort:
+                Mono2StereoALshort(dst, src, frames);
+                break;
+            case DevFmtUShort:
+                Mono2StereoALushort(dst, src, frames);
+                break;
+            case DevFmtInt:
+                Mono2StereoALint(dst, src, frames);
+                break;
+            case DevFmtUInt:
+                Mono2StereoALuint(dst, src, frames);
+                break;
+            case DevFmtFloat:
+                Mono2StereoALfloat(dst, src, frames);
+                break;
+        }
+    }
+}

+ 55 - 0
libs/openal-soft/Alc/converter.h

@@ -0,0 +1,55 @@
+#ifndef CONVERTER_H
+#define CONVERTER_H
+
+#include "alMain.h"
+#include "alu.h"
+
+#ifdef __cpluspluc
+extern "C" {
+#endif
+
+typedef struct SampleConverter {
+    enum DevFmtType mSrcType;
+    enum DevFmtType mDstType;
+    ALsizei mNumChannels;
+    ALsizei mSrcTypeSize;
+    ALsizei mDstTypeSize;
+
+    ALint mSrcPrepCount;
+
+    ALsizei mFracOffset;
+    ALsizei mIncrement;
+    InterpState mState;
+    ResamplerFunc mResample;
+
+    alignas(16) ALfloat mSrcSamples[BUFFERSIZE];
+    alignas(16) ALfloat mDstSamples[BUFFERSIZE];
+
+    struct {
+        alignas(16) ALfloat mPrevSamples[MAX_PRE_SAMPLES+MAX_POST_SAMPLES];
+    } Chan[];
+} SampleConverter;
+
+SampleConverter *CreateSampleConverter(enum DevFmtType srcType, enum DevFmtType dstType, ALsizei numchans, ALsizei srcRate, ALsizei dstRate);
+void DestroySampleConverter(SampleConverter **converter);
+
+ALsizei SampleConverterInput(SampleConverter *converter, const ALvoid **src, ALsizei *srcframes, ALvoid *dst, ALsizei dstframes);
+ALsizei SampleConverterAvailableOut(SampleConverter *converter, ALsizei srcframes);
+
+
+typedef struct ChannelConverter {
+    enum DevFmtType mSrcType;
+    enum DevFmtChannels mSrcChans;
+    enum DevFmtChannels mDstChans;
+} ChannelConverter;
+
+ChannelConverter *CreateChannelConverter(enum DevFmtType srcType, enum DevFmtChannels srcChans, enum DevFmtChannels dstChans);
+void DestroyChannelConverter(ChannelConverter **converter);
+
+void ChannelConverterInput(ChannelConverter *converter, const ALvoid *src, ALfloat *dst, ALsizei frames);
+
+#ifdef __cpluspluc
+}
+#endif
+
+#endif /* CONVERTER_H */

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

@@ -1,270 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 2013 by Anis A. Hireche, Nasca Octavian Paul
- * This library is free software; you can redistribute it and/or
- *  modify it under the terms of the GNU Library General Public
- *  License as published by the Free Software Foundation; either
- *  version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- *  Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- *  License along with this library; if not, write to the
- *  Free Software Foundation, Inc.,
- *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
-#include <stdlib.h>
-
-#include "config.h"
-#include "alu.h"
-#include "alFilter.h"
-#include "alError.h"
-#include "alMain.h"
-#include "alAuxEffectSlot.h"
-
-
-/* Auto-wah is simply a low-pass filter with a cutoff frequency that shifts up
- * or down depending on the input signal, and a resonant peak at the cutoff.
- *
- * Currently, we assume a cutoff frequency range of 20hz (no amplitude) to
- * 20khz (peak gain). Peak gain is assumed to be in normalized scale.
- */
-
-typedef struct ALautowahState {
-    DERIVE_FROM_TYPE(ALeffectState);
-
-    /* Effect gains for each channel */
-    ALfloat Gain[MAX_OUTPUT_CHANNELS];
-
-    /* Effect parameters */
-    ALfloat AttackRate;
-    ALfloat ReleaseRate;
-    ALfloat Resonance;
-    ALfloat PeakGain;
-    ALfloat GainCtrl;
-    ALfloat Frequency;
-
-    /* Samples processing */
-    ALfilterState LowPass;
-} ALautowahState;
-
-static ALvoid ALautowahState_Destruct(ALautowahState *UNUSED(state))
-{
-}
-
-static ALboolean ALautowahState_deviceUpdate(ALautowahState *state, ALCdevice *device)
-{
-    state->Frequency = (ALfloat)device->Frequency;
-    return AL_TRUE;
-}
-
-static ALvoid ALautowahState_update(ALautowahState *state, ALCdevice *device, const ALeffectslot *slot)
-{
-    ALfloat attackTime, releaseTime;
-
-    attackTime = slot->EffectProps.Autowah.AttackTime * state->Frequency;
-    releaseTime = slot->EffectProps.Autowah.ReleaseTime * state->Frequency;
-
-    state->AttackRate = powf(1.0f/GAIN_SILENCE_THRESHOLD, 1.0f/attackTime);
-    state->ReleaseRate = powf(GAIN_SILENCE_THRESHOLD/1.0f, 1.0f/releaseTime);
-    state->PeakGain = slot->EffectProps.Autowah.PeakGain;
-    state->Resonance = slot->EffectProps.Autowah.Resonance;
-
-    ComputeAmbientGains(device, slot->Gain, state->Gain);
-}
-
-static ALvoid ALautowahState_process(ALautowahState *state, ALuint SamplesToDo, const ALfloat *SamplesIn, ALfloat (*SamplesOut)[BUFFERSIZE], ALuint NumChannels)
-{
-    ALuint it, kt;
-    ALuint base;
-
-    for(base = 0;base < SamplesToDo;)
-    {
-        ALfloat temps[256];
-        ALuint td = minu(256, SamplesToDo-base);
-        ALfloat gain = state->GainCtrl;
-
-        for(it = 0;it < td;it++)
-        {
-            ALfloat smp = SamplesIn[it+base];
-            ALfloat a[3], b[3];
-            ALfloat alpha, w0;
-            ALfloat amplitude;
-            ALfloat cutoff;
-
-            /* Similar to compressor, we get the current amplitude of the
-             * incoming signal, and attack or release to reach it. */
-            amplitude = fabsf(smp);
-            if(amplitude > gain)
-                gain = minf(gain*state->AttackRate, amplitude);
-            else if(amplitude < gain)
-                gain = maxf(gain*state->ReleaseRate, amplitude);
-            gain = maxf(gain, GAIN_SILENCE_THRESHOLD);
-
-            /* FIXME: What range does the filter cover? */
-            cutoff = lerp(20.0f, 20000.0f, minf(gain/state->PeakGain, 1.0f));
-
-            /* The code below is like calling ALfilterState_setParams with
-             * ALfilterType_LowPass. However, instead of passing a bandwidth,
-             * we use the resonance property for Q. This also inlines the call.
-             */
-            w0 = F_TAU * cutoff / state->Frequency;
-
-            /* FIXME: Resonance controls the resonant peak, or Q. How? Not sure
-             * that Q = resonance*0.1. */
-            alpha = sinf(w0) / (2.0f * state->Resonance*0.1f);
-            b[0] = (1.0f - cosf(w0)) / 2.0f;
-            b[1] =  1.0f - cosf(w0);
-            b[2] = (1.0f - cosf(w0)) / 2.0f;
-            a[0] =  1.0f + alpha;
-            a[1] = -2.0f * cosf(w0);
-            a[2] =  1.0f - alpha;
-
-            state->LowPass.a1 = a[1] / a[0];
-            state->LowPass.a2 = a[2] / a[0];
-            state->LowPass.b1 = b[1] / a[0];
-            state->LowPass.b2 = b[2] / a[0];
-            state->LowPass.input_gain = b[0] / a[0];
-
-            temps[it] = ALfilterState_processSingle(&state->LowPass, smp);
-        }
-        state->GainCtrl = gain;
-
-        for(kt = 0;kt < NumChannels;kt++)
-        {
-            ALfloat gain = state->Gain[kt];
-            if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
-                continue;
-
-            for(it = 0;it < td;it++)
-                SamplesOut[kt][base+it] += gain * temps[it];
-        }
-
-        base += td;
-    }
-}
-
-DECLARE_DEFAULT_ALLOCATORS(ALautowahState)
-
-DEFINE_ALEFFECTSTATE_VTABLE(ALautowahState);
-
-
-typedef struct ALautowahStateFactory {
-    DERIVE_FROM_TYPE(ALeffectStateFactory);
-} ALautowahStateFactory;
-
-static ALeffectState *ALautowahStateFactory_create(ALautowahStateFactory *UNUSED(factory))
-{
-    ALautowahState *state;
-
-    state = ALautowahState_New(sizeof(*state));
-    if(!state) return NULL;
-    SET_VTABLE2(ALautowahState, ALeffectState, state);
-
-    state->AttackRate = 1.0f;
-    state->ReleaseRate = 1.0f;
-    state->Resonance = 2.0f;
-    state->PeakGain = 1.0f;
-    state->GainCtrl = 1.0f;
-
-    ALfilterState_clear(&state->LowPass);
-
-    return STATIC_CAST(ALeffectState, state);
-}
-
-DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALautowahStateFactory);
-
-ALeffectStateFactory *ALautowahStateFactory_getFactory(void)
-{
-    static ALautowahStateFactory AutowahFactory = { { GET_VTABLE2(ALautowahStateFactory, ALeffectStateFactory) } };
-
-    return STATIC_CAST(ALeffectStateFactory, &AutowahFactory);
-}
-
-
-void ALautowah_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val))
-{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
-void ALautowah_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
-{
-    ALautowah_setParami(effect, context, param, vals[0]);
-}
-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))
-                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
-            props->Autowah.AttackTime = val;
-            break;
-
-        case AL_AUTOWAH_RELEASE_TIME:
-            if(!(val >= AL_AUTOWAH_MIN_RELEASE_TIME && val <= AL_AUTOWAH_MAX_RELEASE_TIME))
-                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
-            props->Autowah.ReleaseTime = val;
-            break;
-
-        case AL_AUTOWAH_RESONANCE:
-            if(!(val >= AL_AUTOWAH_MIN_RESONANCE && val <= AL_AUTOWAH_MAX_RESONANCE))
-                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
-            props->Autowah.Resonance = val;
-            break;
-
-        case AL_AUTOWAH_PEAK_GAIN:
-            if(!(val >= AL_AUTOWAH_MIN_PEAK_GAIN && val <= AL_AUTOWAH_MAX_PEAK_GAIN))
-                SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
-            props->Autowah.PeakGain = val;
-            break;
-
-        default:
-            SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
-    }
-}
-void ALautowah_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
-{
-    ALautowah_setParamf(effect, context, param, vals[0]);
-}
-
-void ALautowah_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val))
-{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
-void ALautowah_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
-{
-    ALautowah_getParami(effect, context, param, vals);
-}
-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:
-            SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
-    }
-}
-void ALautowah_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
-{
-    ALautowah_getParamf(effect, context, param, vals);
-}
-
-DEFINE_ALEFFECT_VTABLE(ALautowah);

+ 114 - 103
libs/openal-soft/Alc/effects/chorus.c

@@ -39,9 +39,9 @@ typedef struct ALchorusState {
     DERIVE_FROM_TYPE(ALeffectState);
 
     ALfloat *SampleBuffer[2];
-    ALuint BufferLength;
-    ALuint offset;
-    ALuint lfo_range;
+    ALsizei BufferLength;
+    ALsizei offset;
+    ALsizei lfo_range;
     ALfloat lfo_scale;
     ALint lfo_disp;
 
@@ -55,27 +55,51 @@ typedef struct ALchorusState {
     ALfloat feedback;
 } ALchorusState;
 
+static ALvoid ALchorusState_Destruct(ALchorusState *state);
+static ALboolean ALchorusState_deviceUpdate(ALchorusState *state, ALCdevice *Device);
+static ALvoid ALchorusState_update(ALchorusState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props);
+static ALvoid ALchorusState_process(ALchorusState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
+DECLARE_DEFAULT_ALLOCATORS(ALchorusState)
+
+DEFINE_ALEFFECTSTATE_VTABLE(ALchorusState);
+
+
+static void ALchorusState_Construct(ALchorusState *state)
+{
+    ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
+    SET_VTABLE2(ALchorusState, ALeffectState, state);
+
+    state->BufferLength = 0;
+    state->SampleBuffer[0] = NULL;
+    state->SampleBuffer[1] = NULL;
+    state->offset = 0;
+    state->lfo_range = 1;
+    state->waveform = CWF_Triangle;
+}
+
 static ALvoid ALchorusState_Destruct(ALchorusState *state)
 {
-    free(state->SampleBuffer[0]);
+    al_free(state->SampleBuffer[0]);
     state->SampleBuffer[0] = NULL;
     state->SampleBuffer[1] = NULL;
+
+    ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
 }
 
 static ALboolean ALchorusState_deviceUpdate(ALchorusState *state, ALCdevice *Device)
 {
-    ALuint maxlen;
-    ALuint it;
+    ALsizei maxlen;
+    ALsizei it;
 
-    maxlen = fastf2u(AL_CHORUS_MAX_DELAY * 3.0f * Device->Frequency) + 1;
+    maxlen = fastf2i(AL_CHORUS_MAX_DELAY * 2.0f * Device->Frequency) + 1;
     maxlen = NextPowerOf2(maxlen);
 
     if(maxlen != state->BufferLength)
     {
-        void *temp;
-
-        temp = realloc(state->SampleBuffer[0], maxlen * sizeof(ALfloat) * 2);
+        void *temp = al_calloc(16, maxlen * sizeof(ALfloat) * 2);
         if(!temp) return AL_FALSE;
+
+        al_free(state->SampleBuffer[0]);
         state->SampleBuffer[0] = temp;
         state->SampleBuffer[1] = state->SampleBuffer[0] + maxlen;
 
@@ -91,15 +115,14 @@ static ALboolean ALchorusState_deviceUpdate(ALchorusState *state, ALCdevice *Dev
     return AL_TRUE;
 }
 
-static ALvoid ALchorusState_update(ALchorusState *state, ALCdevice *Device, const ALeffectslot *Slot)
+static ALvoid ALchorusState_update(ALchorusState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props)
 {
-    static const ALfloat left_dir[3] = { -1.0f, 0.0f, 0.0f };
-    static const ALfloat right_dir[3] = { 1.0f, 0.0f, 0.0f };
     ALfloat frequency = (ALfloat)Device->Frequency;
+    ALfloat coeffs[MAX_AMBI_COEFFS];
     ALfloat rate;
     ALint phase;
 
-    switch(Slot->EffectProps.Chorus.Waveform)
+    switch(props->Chorus.Waveform)
     {
         case AL_CHORUS_WAVEFORM_TRIANGLE:
             state->waveform = CWF_Triangle;
@@ -108,16 +131,19 @@ static ALvoid ALchorusState_update(ALchorusState *state, ALCdevice *Device, cons
             state->waveform = CWF_Sinusoid;
             break;
     }
-    state->depth = Slot->EffectProps.Chorus.Depth;
-    state->feedback = Slot->EffectProps.Chorus.Feedback;
-    state->delay = fastf2i(Slot->EffectProps.Chorus.Delay * frequency);
+    state->feedback = props->Chorus.Feedback;
+    state->delay = fastf2i(props->Chorus.Delay * frequency);
+    /* The LFO depth is scaled to be relative to the sample delay. */
+    state->depth = props->Chorus.Depth * state->delay;
 
     /* Gains for left and right sides */
-    ComputeDirectionalGains(Device, left_dir, Slot->Gain, state->Gain[0]);
-    ComputeDirectionalGains(Device, right_dir, Slot->Gain, state->Gain[1]);
+    CalcAngleCoeffs(-F_PI_2, 0.0f, 0.0f, coeffs);
+    ComputePanningGains(Device->Dry, coeffs, Slot->Params.Gain, state->Gain[0]);
+    CalcAngleCoeffs( F_PI_2, 0.0f, 0.0f, coeffs);
+    ComputePanningGains(Device->Dry, coeffs, Slot->Params.Gain, state->Gain[1]);
 
-    phase = Slot->EffectProps.Chorus.Phase;
-    rate = Slot->EffectProps.Chorus.Rate;
+    phase = props->Chorus.Phase;
+    rate = props->Chorus.Rate;
     if(!(rate > 0.0f))
     {
         state->lfo_scale = 0.0f;
@@ -127,7 +153,7 @@ static ALvoid ALchorusState_update(ALchorusState *state, ALCdevice *Device, cons
     else
     {
         /* Calculate LFO coefficient */
-        state->lfo_range = fastf2u(frequency/rate + 0.5f);
+        state->lfo_range = fastf2i(frequency/rate + 0.5f);
         switch(state->waveform)
         {
             case CWF_Triangle:
@@ -139,114 +165,107 @@ static ALvoid ALchorusState_update(ALchorusState *state, ALCdevice *Device, cons
         }
 
         /* Calculate lfo phase displacement */
-        state->lfo_disp = fastf2i(state->lfo_range * (phase/360.0f));
+        if(phase >= 0)
+            state->lfo_disp = fastf2i(state->lfo_range * (phase/360.0f));
+        else
+            state->lfo_disp = fastf2i(state->lfo_range * ((360+phase)/360.0f));
     }
 }
 
-static inline void Triangle(ALint *delay_left, ALint *delay_right, ALuint offset, const ALchorusState *state)
+static void GetTriangleDelays(ALint *restrict delays, ALsizei offset, const ALsizei lfo_range,
+                              const ALfloat lfo_scale, const ALfloat depth, const ALsizei delay,
+                              const ALsizei todo)
 {
-    ALfloat lfo_value;
-
-    lfo_value = 2.0f - fabsf(2.0f - state->lfo_scale*(offset%state->lfo_range));
-    lfo_value *= state->depth * state->delay;
-    *delay_left = fastf2i(lfo_value) + state->delay;
-
-    offset += state->lfo_disp;
-    lfo_value = 2.0f - fabsf(2.0f - state->lfo_scale*(offset%state->lfo_range));
-    lfo_value *= state->depth * state->delay;
-    *delay_right = fastf2i(lfo_value) + state->delay;
+    ALsizei i;
+    for(i = 0;i < todo;i++)
+    {
+        delays[i] = fastf2i((1.0f - fabsf(2.0f - lfo_scale*offset)) * depth) + delay;
+        offset = (offset+1)%lfo_range;
+    }
 }
 
-static inline void Sinusoid(ALint *delay_left, ALint *delay_right, ALuint offset, const ALchorusState *state)
+static void GetSinusoidDelays(ALint *restrict delays, ALsizei offset, const ALsizei lfo_range,
+                              const ALfloat lfo_scale, const ALfloat depth, const ALsizei delay,
+                              const ALsizei todo)
 {
-    ALfloat lfo_value;
-
-    lfo_value = 1.0f + sinf(state->lfo_scale*(offset%state->lfo_range));
-    lfo_value *= state->depth * state->delay;
-    *delay_left = fastf2i(lfo_value) + state->delay;
-
-    offset += state->lfo_disp;
-    lfo_value = 1.0f + sinf(state->lfo_scale*(offset%state->lfo_range));
-    lfo_value *= state->depth * state->delay;
-    *delay_right = fastf2i(lfo_value) + state->delay;
-}
-
-#define DECL_TEMPLATE(Func)                                                   \
-static void Process##Func(ALchorusState *state, const ALuint SamplesToDo,     \
-  const ALfloat *restrict SamplesIn, ALfloat (*restrict out)[2])              \
-{                                                                             \
-    const ALuint bufmask = state->BufferLength-1;                             \
-    ALfloat *restrict leftbuf = state->SampleBuffer[0];                       \
-    ALfloat *restrict rightbuf = state->SampleBuffer[1];                      \
-    ALuint offset = state->offset;                                            \
-    const ALfloat feedback = state->feedback;                                 \
-    ALuint it;                                                                \
-                                                                              \
-    for(it = 0;it < SamplesToDo;it++)                                         \
-    {                                                                         \
-        ALint delay_left, delay_right;                                        \
-        Func(&delay_left, &delay_right, offset, state);                       \
-                                                                              \
-        out[it][0] = leftbuf[(offset-delay_left)&bufmask];                    \
-        leftbuf[offset&bufmask] = (out[it][0]+SamplesIn[it]) * feedback;      \
-                                                                              \
-        out[it][1] = rightbuf[(offset-delay_right)&bufmask];                  \
-        rightbuf[offset&bufmask] = (out[it][1]+SamplesIn[it]) * feedback;     \
-                                                                              \
-        offset++;                                                             \
-    }                                                                         \
-    state->offset = offset;                                                   \
+    ALsizei i;
+    for(i = 0;i < todo;i++)
+    {
+        delays[i] = fastf2i(sinf(lfo_scale*offset) * depth) + delay;
+        offset = (offset+1)%lfo_range;
+    }
 }
 
-DECL_TEMPLATE(Triangle)
-DECL_TEMPLATE(Sinusoid)
-
-#undef DECL_TEMPLATE
 
-static ALvoid ALchorusState_process(ALchorusState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels)
+static ALvoid ALchorusState_process(ALchorusState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
 {
-    ALuint it, kt;
-    ALuint base;
+    ALfloat *restrict leftbuf = state->SampleBuffer[0];
+    ALfloat *restrict rightbuf = state->SampleBuffer[1];
+    const ALsizei bufmask = state->BufferLength-1;
+    const ALfloat feedback = state->feedback;
+    ALsizei offset = state->offset;
+    ALsizei i, c;
+    ALsizei base;
 
     for(base = 0;base < SamplesToDo;)
     {
+        const ALsizei todo = mini(128, SamplesToDo-base);
         ALfloat temps[128][2];
-        ALuint td = minu(128, SamplesToDo-base);
+        ALint moddelays[2][128];
 
         switch(state->waveform)
         {
             case CWF_Triangle:
-                ProcessTriangle(state, td, SamplesIn+base, temps);
+                GetTriangleDelays(moddelays[0], offset%state->lfo_range, state->lfo_range,
+                                  state->lfo_scale, state->depth, state->delay, todo);
+                GetTriangleDelays(moddelays[1], (offset+state->lfo_disp)%state->lfo_range,
+                                  state->lfo_range, state->lfo_scale, state->depth, state->delay,
+                                  todo);
                 break;
             case CWF_Sinusoid:
-                ProcessSinusoid(state, td, SamplesIn+base, temps);
+                GetSinusoidDelays(moddelays[0], offset%state->lfo_range, state->lfo_range,
+                                  state->lfo_scale, state->depth, state->delay, todo);
+                GetSinusoidDelays(moddelays[1], (offset+state->lfo_disp)%state->lfo_range,
+                                  state->lfo_range, state->lfo_scale, state->depth, state->delay,
+                                  todo);
                 break;
         }
 
-        for(kt = 0;kt < NumChannels;kt++)
+        for(i = 0;i < todo;i++)
         {
-            ALfloat gain = state->Gain[0][kt];
+            leftbuf[offset&bufmask] = SamplesIn[0][base+i];
+            temps[i][0] = leftbuf[(offset-moddelays[0][i])&bufmask] * feedback;
+            leftbuf[offset&bufmask] += temps[i][0];
+
+            rightbuf[offset&bufmask] = SamplesIn[0][base+i];
+            temps[i][1] = rightbuf[(offset-moddelays[1][i])&bufmask] * feedback;
+            rightbuf[offset&bufmask] += temps[i][1];
+
+            offset++;
+        }
+
+        for(c = 0;c < NumChannels;c++)
+        {
+            ALfloat gain = state->Gain[0][c];
             if(fabsf(gain) > GAIN_SILENCE_THRESHOLD)
             {
-                for(it = 0;it < td;it++)
-                    SamplesOut[kt][it+base] += temps[it][0] * gain;
+                for(i = 0;i < todo;i++)
+                    SamplesOut[c][i+base] += temps[i][0] * gain;
             }
 
-            gain = state->Gain[1][kt];
+            gain = state->Gain[1][c];
             if(fabsf(gain) > GAIN_SILENCE_THRESHOLD)
             {
-                for(it = 0;it < td;it++)
-                    SamplesOut[kt][it+base] += temps[it][1] * gain;
+                for(i = 0;i < todo;i++)
+                    SamplesOut[c][i+base] += temps[i][1] * gain;
             }
         }
 
-        base += td;
+        base += todo;
     }
-}
 
-DECLARE_DEFAULT_ALLOCATORS(ALchorusState)
-
-DEFINE_ALEFFECTSTATE_VTABLE(ALchorusState);
+    state->offset = offset;
+}
 
 
 typedef struct ALchorusStateFactory {
@@ -257,16 +276,8 @@ static ALeffectState *ALchorusStateFactory_create(ALchorusStateFactory *UNUSED(f
 {
     ALchorusState *state;
 
-    state = ALchorusState_New(sizeof(*state));
+    NEW_OBJ0(state, ALchorusState)();
     if(!state) return NULL;
-    SET_VTABLE2(ALchorusState, ALeffectState, state);
-
-    state->BufferLength = 0;
-    state->SampleBuffer[0] = NULL;
-    state->SampleBuffer[1] = NULL;
-    state->offset = 0;
-    state->lfo_range = 1;
-    state->waveform = CWF_Triangle;
 
     return STATIC_CAST(ALeffectState, state);
 }

+ 78 - 41
libs/openal-soft/Alc/effects/compressor.c

@@ -31,7 +31,7 @@ typedef struct ALcompressorState {
     DERIVE_FROM_TYPE(ALeffectState);
 
     /* Effect gains for each channel */
-    ALfloat Gain[MAX_OUTPUT_CHANNELS];
+    ALfloat Gain[MAX_EFFECT_CHANNELS][MAX_OUTPUT_CHANNELS];
 
     /* Effect parameters */
     ALboolean Enabled;
@@ -40,8 +40,29 @@ typedef struct ALcompressorState {
     ALfloat GainCtrl;
 } ALcompressorState;
 
-static ALvoid ALcompressorState_Destruct(ALcompressorState *UNUSED(state))
+static ALvoid ALcompressorState_Destruct(ALcompressorState *state);
+static ALboolean ALcompressorState_deviceUpdate(ALcompressorState *state, ALCdevice *device);
+static ALvoid ALcompressorState_update(ALcompressorState *state, const ALCdevice *device, const ALeffectslot *slot, const ALeffectProps *props);
+static ALvoid ALcompressorState_process(ALcompressorState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
+DECLARE_DEFAULT_ALLOCATORS(ALcompressorState)
+
+DEFINE_ALEFFECTSTATE_VTABLE(ALcompressorState);
+
+
+static void ALcompressorState_Construct(ALcompressorState *state)
 {
+    ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
+    SET_VTABLE2(ALcompressorState, ALeffectState, state);
+
+    state->Enabled = AL_TRUE;
+    state->AttackRate = 0.0f;
+    state->ReleaseRate = 0.0f;
+    state->GainCtrl = 1.0f;
+}
+
+static ALvoid ALcompressorState_Destruct(ALcompressorState *state)
+{
+    ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
 }
 
 static ALboolean ALcompressorState_deviceUpdate(ALcompressorState *state, ALCdevice *device)
@@ -55,85 +76,107 @@ static ALboolean ALcompressorState_deviceUpdate(ALcompressorState *state, ALCdev
     return AL_TRUE;
 }
 
-static ALvoid ALcompressorState_update(ALcompressorState *state, ALCdevice *device, const ALeffectslot *slot)
+static ALvoid ALcompressorState_update(ALcompressorState *state, const ALCdevice *device, const ALeffectslot *slot, const ALeffectProps *props)
 {
-    state->Enabled = slot->EffectProps.Compressor.OnOff;
+    ALuint i;
+
+    state->Enabled = props->Compressor.OnOff;
 
-    ComputeAmbientGains(device, slot->Gain, state->Gain);
+    STATIC_CAST(ALeffectState,state)->OutBuffer = device->FOAOut.Buffer;
+    STATIC_CAST(ALeffectState,state)->OutChannels = device->FOAOut.NumChannels;
+    for(i = 0;i < 4;i++)
+        ComputeFirstOrderGains(device->FOAOut, IdentityMatrixf.m[i],
+                               slot->Params.Gain, state->Gain[i]);
 }
 
-static ALvoid ALcompressorState_process(ALcompressorState *state, ALuint SamplesToDo, const ALfloat *SamplesIn, ALfloat (*SamplesOut)[BUFFERSIZE], ALuint NumChannels)
+static ALvoid ALcompressorState_process(ALcompressorState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
 {
-    ALuint it, kt;
-    ALuint base;
+    ALsizei i, j, k;
+    ALsizei base;
 
     for(base = 0;base < SamplesToDo;)
     {
-        ALfloat temps[256];
-        ALuint td = minu(256, SamplesToDo-base);
+        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];
+        }
 
         if(state->Enabled)
         {
-            ALfloat output, smp, amplitude;
             ALfloat gain = state->GainCtrl;
+            ALfloat output, amplitude;
 
-            for(it = 0;it < td;it++)
+            for(i = 0;i < td;i++)
             {
-                smp = SamplesIn[it+base];
-
-                amplitude = fabsf(smp);
+                /* Roughly calculate the maximum amplitude from the 4-channel
+                 * signal, and attack or release the gain control to reach it.
+                 */
+                amplitude = fabsf(temps[i][0]);
+                amplitude = maxf(amplitude + fabsf(temps[i][1]),
+                                 maxf(amplitude + fabsf(temps[i][2]),
+                                      amplitude + fabsf(temps[i][3])));
                 if(amplitude > gain)
                     gain = minf(gain+state->AttackRate, amplitude);
                 else if(amplitude < gain)
                     gain = maxf(gain-state->ReleaseRate, amplitude);
-                output = 1.0f / clampf(gain, 0.5f, 2.0f);
 
-                temps[it] = smp * output;
+                /* Apply the inverse of the gain control to normalize/compress
+                 * the volume. */
+                output = 1.0f / clampf(gain, 0.5f, 2.0f);
+                for(j = 0;j < 4;j++)
+                    temps[i][j] *= output;
             }
 
             state->GainCtrl = gain;
         }
         else
         {
-            ALfloat output, smp, amplitude;
             ALfloat gain = state->GainCtrl;
+            ALfloat output, amplitude;
 
-            for(it = 0;it < td;it++)
+            for(i = 0;i < td;i++)
             {
-                smp = SamplesIn[it+base];
-
+                /* 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);
 
-                temps[it] = smp * output;
+                output = 1.0f / clampf(gain, 0.5f, 2.0f);
+                for(j = 0;j < 4;j++)
+                    temps[i][j] *= output;
             }
 
             state->GainCtrl = gain;
         }
 
-
-        for(kt = 0;kt < NumChannels;kt++)
+        /* Now mix to the output. */
+        for(j = 0;j < 4;j++)
         {
-            ALfloat gain = state->Gain[kt];
-            if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
-                continue;
+            for(k = 0;k < NumChannels;k++)
+            {
+                ALfloat gain = state->Gain[j][k];
+                if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
+                    continue;
 
-            for(it = 0;it < td;it++)
-                SamplesOut[kt][base+it] += gain * temps[it];
+                for(i = 0;i < td;i++)
+                    SamplesOut[k][base+i] += gain * temps[i][j];
+            }
         }
 
         base += td;
     }
 }
 
-DECLARE_DEFAULT_ALLOCATORS(ALcompressorState)
-
-DEFINE_ALEFFECTSTATE_VTABLE(ALcompressorState);
-
 
 typedef struct ALcompressorStateFactory {
     DERIVE_FROM_TYPE(ALeffectStateFactory);
@@ -143,14 +186,8 @@ static ALeffectState *ALcompressorStateFactory_create(ALcompressorStateFactory *
 {
     ALcompressorState *state;
 
-    state = ALcompressorState_New(sizeof(*state));
+    NEW_OBJ0(state, ALcompressorState)();
     if(!state) return NULL;
-    SET_VTABLE2(ALcompressorState, ALeffectState, state);
-
-    state->Enabled = AL_TRUE;
-    state->AttackRate = 0.0f;
-    state->ReleaseRate = 0.0f;
-    state->GainCtrl = 1.0f;
 
     return STATIC_CAST(ALeffectState, state);
 }

+ 49 - 24
libs/openal-soft/Alc/effects/dedicated.c

@@ -35,9 +35,29 @@ typedef struct ALdedicatedState {
     ALfloat gains[MAX_OUTPUT_CHANNELS];
 } ALdedicatedState;
 
+static ALvoid ALdedicatedState_Destruct(ALdedicatedState *state);
+static ALboolean ALdedicatedState_deviceUpdate(ALdedicatedState *state, ALCdevice *device);
+static ALvoid ALdedicatedState_update(ALdedicatedState *state, const ALCdevice *device, const ALeffectslot *Slot, const ALeffectProps *props);
+static ALvoid ALdedicatedState_process(ALdedicatedState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
+DECLARE_DEFAULT_ALLOCATORS(ALdedicatedState)
+
+DEFINE_ALEFFECTSTATE_VTABLE(ALdedicatedState);
+
+
+static void ALdedicatedState_Construct(ALdedicatedState *state)
+{
+    ALsizei s;
 
-static ALvoid ALdedicatedState_Destruct(ALdedicatedState *UNUSED(state))
+    ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
+    SET_VTABLE2(ALdedicatedState, ALeffectState, state);
+
+    for(s = 0;s < MAX_OUTPUT_CHANNELS;s++)
+        state->gains[s] = 0.0f;
+}
+
+static ALvoid ALdedicatedState_Destruct(ALdedicatedState *state)
 {
+    ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
 }
 
 static ALboolean ALdedicatedState_deviceUpdate(ALdedicatedState *UNUSED(state), ALCdevice *UNUSED(device))
@@ -45,7 +65,7 @@ static ALboolean ALdedicatedState_deviceUpdate(ALdedicatedState *UNUSED(state),
     return AL_TRUE;
 }
 
-static ALvoid ALdedicatedState_update(ALdedicatedState *state, ALCdevice *device, const ALeffectslot *Slot)
+static ALvoid ALdedicatedState_update(ALdedicatedState *state, const ALCdevice *device, const ALeffectslot *Slot, const ALeffectProps *props)
 {
     ALfloat Gain;
     ALuint i;
@@ -53,47 +73,57 @@ static ALvoid ALdedicatedState_update(ALdedicatedState *state, ALCdevice *device
     for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
         state->gains[i] = 0.0f;
 
-    Gain = Slot->Gain * Slot->EffectProps.Dedicated.Gain;
-    if(Slot->EffectType == AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT)
+    Gain = Slot->Params.Gain * props->Dedicated.Gain;
+    if(Slot->Params.EffectType == AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT)
     {
         int idx;
-        if((idx=GetChannelIdxByName(device, LFE)) != -1)
+        if((idx=GetChannelIdxByName(device->RealOut, LFE)) != -1)
+        {
+            STATIC_CAST(ALeffectState,state)->OutBuffer = device->RealOut.Buffer;
+            STATIC_CAST(ALeffectState,state)->OutChannels = device->RealOut.NumChannels;
             state->gains[idx] = Gain;
+        }
     }
-    else if(Slot->EffectType == AL_EFFECT_DEDICATED_DIALOGUE)
+    else if(Slot->Params.EffectType == AL_EFFECT_DEDICATED_DIALOGUE)
     {
         int idx;
         /* Dialog goes to the front-center speaker if it exists, otherwise it
          * plays from the front-center location. */
-        if((idx=GetChannelIdxByName(device, FrontCenter)) != -1)
+        if((idx=GetChannelIdxByName(device->RealOut, FrontCenter)) != -1)
+        {
+            STATIC_CAST(ALeffectState,state)->OutBuffer = device->RealOut.Buffer;
+            STATIC_CAST(ALeffectState,state)->OutChannels = device->RealOut.NumChannels;
             state->gains[idx] = Gain;
+        }
         else
         {
-            static const ALfloat front_dir[3] = { 0.0f, 0.0f, -1.0f };
-            ComputeDirectionalGains(device, front_dir, Gain, state->gains);
+            ALfloat coeffs[MAX_AMBI_COEFFS];
+            CalcAngleCoeffs(0.0f, 0.0f, 0.0f, coeffs);
+
+            STATIC_CAST(ALeffectState,state)->OutBuffer = device->Dry.Buffer;
+            STATIC_CAST(ALeffectState,state)->OutChannels = device->Dry.NumChannels;
+            ComputePanningGains(device->Dry, coeffs, Gain, state->gains);
         }
     }
 }
 
-static ALvoid ALdedicatedState_process(ALdedicatedState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels)
+static ALvoid ALdedicatedState_process(ALdedicatedState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
 {
-    const ALfloat *gains = state->gains;
-    ALuint i, c;
+    ALsizei i, c;
 
+    SamplesIn = ASSUME_ALIGNED(SamplesIn, 16);
+    SamplesOut = ASSUME_ALIGNED(SamplesOut, 16);
     for(c = 0;c < NumChannels;c++)
     {
-        if(!(fabsf(gains[c]) > GAIN_SILENCE_THRESHOLD))
+        const ALfloat gain = state->gains[c];
+        if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
             continue;
 
         for(i = 0;i < SamplesToDo;i++)
-            SamplesOut[c][i] += SamplesIn[i] * gains[c];
+            SamplesOut[c][i] += SamplesIn[0][i] * gain;
     }
 }
 
-DECLARE_DEFAULT_ALLOCATORS(ALdedicatedState)
-
-DEFINE_ALEFFECTSTATE_VTABLE(ALdedicatedState);
-
 
 typedef struct ALdedicatedStateFactory {
     DERIVE_FROM_TYPE(ALeffectStateFactory);
@@ -102,14 +132,9 @@ typedef struct ALdedicatedStateFactory {
 ALeffectState *ALdedicatedStateFactory_create(ALdedicatedStateFactory *UNUSED(factory))
 {
     ALdedicatedState *state;
-    ALsizei s;
 
-    state = ALdedicatedState_New(sizeof(*state));
+    NEW_OBJ0(state, ALdedicatedState)();
     if(!state) return NULL;
-    SET_VTABLE2(ALdedicatedState, ALeffectState, state);
-
-    for(s = 0;s < MAX_OUTPUT_CHANNELS;s++)
-        state->gains[s] = 0.0f;
 
     return STATIC_CAST(ALeffectState, state);
 }

+ 76 - 74
libs/openal-soft/Alc/effects/distortion.c

@@ -43,8 +43,27 @@ typedef struct ALdistortionState {
     ALfloat edge_coeff;
 } ALdistortionState;
 
-static ALvoid ALdistortionState_Destruct(ALdistortionState *UNUSED(state))
+static ALvoid ALdistortionState_Destruct(ALdistortionState *state);
+static ALboolean ALdistortionState_deviceUpdate(ALdistortionState *state, ALCdevice *device);
+static ALvoid ALdistortionState_update(ALdistortionState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props);
+static ALvoid ALdistortionState_process(ALdistortionState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
+DECLARE_DEFAULT_ALLOCATORS(ALdistortionState)
+
+DEFINE_ALEFFECTSTATE_VTABLE(ALdistortionState);
+
+
+static void ALdistortionState_Construct(ALdistortionState *state)
+{
+    ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
+    SET_VTABLE2(ALdistortionState, ALeffectState, state);
+
+    ALfilterState_clear(&state->lowpass);
+    ALfilterState_clear(&state->bandpass);
+}
+
+static ALvoid ALdistortionState_Destruct(ALdistortionState *state)
 {
+    ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
 }
 
 static ALboolean ALdistortionState_deviceUpdate(ALdistortionState *UNUSED(state), ALCdevice *UNUSED(device))
@@ -52,105 +71,96 @@ static ALboolean ALdistortionState_deviceUpdate(ALdistortionState *UNUSED(state)
     return AL_TRUE;
 }
 
-static ALvoid ALdistortionState_update(ALdistortionState *state, ALCdevice *Device, const ALeffectslot *Slot)
+static ALvoid ALdistortionState_update(ALdistortionState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props)
 {
     ALfloat frequency = (ALfloat)Device->Frequency;
     ALfloat bandwidth;
     ALfloat cutoff;
     ALfloat edge;
 
-    /* Store distorted signal attenuation settings */
-    state->attenuation = Slot->EffectProps.Distortion.Gain;
+    /* Store distorted signal attenuation settings. */
+    state->attenuation = props->Distortion.Gain;
 
-    /* Store waveshaper edge settings */
-    edge = sinf(Slot->EffectProps.Distortion.Edge * (F_PI_2));
+    /* Store waveshaper edge settings. */
+    edge = sinf(props->Distortion.Edge * (F_PI_2));
     edge = minf(edge, 0.99f);
     state->edge_coeff = 2.0f * edge / (1.0f-edge);
 
-    /* Lowpass filter */
-    cutoff = Slot->EffectProps.Distortion.LowpassCutoff;
-    /* Bandwidth value is constant in octaves */
+    cutoff = props->Distortion.LowpassCutoff;
+    /* Bandwidth value is constant in octaves. */
     bandwidth = (cutoff / 2.0f) / (cutoff * 0.67f);
+    /* Multiply sampling frequency by the amount of oversampling done during
+     * processing.
+     */
     ALfilterState_setParams(&state->lowpass, ALfilterType_LowPass, 1.0f,
         cutoff / (frequency*4.0f), calc_rcpQ_from_bandwidth(cutoff / (frequency*4.0f), bandwidth)
     );
 
-    /* Bandpass filter */
-    cutoff = Slot->EffectProps.Distortion.EQCenter;
-    /* Convert bandwidth in Hz to octaves */
-    bandwidth = Slot->EffectProps.Distortion.EQBandwidth / (cutoff * 0.67f);
+    cutoff = props->Distortion.EQCenter;
+    /* Convert bandwidth in Hz to octaves. */
+    bandwidth = props->Distortion.EQBandwidth / (cutoff * 0.67f);
     ALfilterState_setParams(&state->bandpass, ALfilterType_BandPass, 1.0f,
         cutoff / (frequency*4.0f), calc_rcpQ_from_bandwidth(cutoff / (frequency*4.0f), bandwidth)
     );
 
-    ComputeAmbientGains(Device, Slot->Gain, state->Gain);
+    ComputeAmbientGains(Device->Dry, Slot->Params.Gain, state->Gain);
 }
 
-static ALvoid ALdistortionState_process(ALdistortionState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels)
+static ALvoid ALdistortionState_process(ALdistortionState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
 {
     const ALfloat fc = state->edge_coeff;
-    ALuint base;
-    ALuint it;
-    ALuint ot;
-    ALuint kt;
+    ALsizei it, kt;
+    ALsizei base;
 
     for(base = 0;base < SamplesToDo;)
     {
-        float oversample_buffer[64][4];
-        ALuint td = minu(64, SamplesToDo-base);
+        float buffer[2][64 * 4];
+        ALsizei td = mini(64, SamplesToDo-base);
 
-        /* Perform 4x oversampling to avoid aliasing.   */
-        /* Oversampling greatly improves distortion     */
-        /* quality and allows to implement lowpass and  */
-        /* bandpass filters using high frequencies, at  */
-        /* which classic IIR filters became unstable.   */
+        /* Perform 4x oversampling to avoid aliasing. Oversampling greatly
+         * improves distortion quality and allows to implement lowpass and
+         * bandpass filters using high frequencies, at which classic IIR
+         * filters became unstable.
+         */
 
-        /* Fill oversample buffer using zero stuffing */
+        /* Fill oversample buffer using zero stuffing. */
         for(it = 0;it < td;it++)
         {
-            oversample_buffer[it][0] = SamplesIn[it+base];
-            oversample_buffer[it][1] = 0.0f;
-            oversample_buffer[it][2] = 0.0f;
-            oversample_buffer[it][3] = 0.0f;
+            /* Multiply the sample by the amount of oversampling to maintain
+             * the signal's power.
+             */
+            buffer[0][it*4 + 0] = SamplesIn[0][it+base] * 4.0f;
+            buffer[0][it*4 + 1] = 0.0f;
+            buffer[0][it*4 + 2] = 0.0f;
+            buffer[0][it*4 + 3] = 0.0f;
         }
 
-        /* First step, do lowpass filtering of original signal,  */
-        /* additionally perform buffer interpolation and lowpass */
-        /* cutoff for oversampling (which is fortunately first   */
-        /* step of distortion). So combine three operations into */
-        /* the one.                                              */
-        for(it = 0;it < td;it++)
+        /* First step, do lowpass filtering of original signal. Additionally
+         * perform buffer interpolation and lowpass cutoff for oversampling
+         * (which is fortunately first step of distortion). So combine three
+         * operations into the one.
+         */
+        ALfilterState_process(&state->lowpass, buffer[1], buffer[0], td*4);
+
+        /* Second step, do distortion using waveshaper function to emulate
+         * signal processing during tube overdriving. Three steps of
+         * waveshaping are intended to modify waveform without boost/clipping/
+         * attenuation process.
+         */
+        for(it = 0;it < td*4;it++)
         {
-            for(ot = 0;ot < 4;ot++)
-            {
-                ALfloat smp;
-                smp = ALfilterState_processSingle(&state->lowpass, oversample_buffer[it][ot]);
-
-                /* Restore signal power by multiplying sample by amount of oversampling */
-                oversample_buffer[it][ot] = smp * 4.0f;
-            }
-        }
+            ALfloat smp = buffer[1][it];
 
-        for(it = 0;it < td;it++)
-        {
-            /* Second step, do distortion using waveshaper function  */
-            /* to emulate signal processing during tube overdriving. */
-            /* Three steps of waveshaping are intended to modify     */
-            /* waveform without boost/clipping/attenuation process.  */
-            for(ot = 0;ot < 4;ot++)
-            {
-                ALfloat smp = oversample_buffer[it][ot];
-
-                smp = (1.0f + fc) * smp/(1.0f + fc*fabsf(smp));
-                smp = (1.0f + fc) * smp/(1.0f + fc*fabsf(smp)) * -1.0f;
-                smp = (1.0f + fc) * smp/(1.0f + fc*fabsf(smp));
-
-                /* Third step, do bandpass filtering of distorted signal */
-                smp = ALfilterState_processSingle(&state->bandpass, smp);
-                oversample_buffer[it][ot] = smp;
-            }
+            smp = (1.0f + fc) * smp/(1.0f + fc*fabsf(smp));
+            smp = (1.0f + fc) * smp/(1.0f + fc*fabsf(smp)) * -1.0f;
+            smp = (1.0f + fc) * smp/(1.0f + fc*fabsf(smp));
+
+            buffer[0][it] = smp;
         }
 
+        /* Third step, do bandpass filtering of distorted signal. */
+        ALfilterState_process(&state->bandpass, buffer[1], buffer[0], td*4);
+
         for(kt = 0;kt < NumChannels;kt++)
         {
             /* Fourth step, final, do attenuation and perform decimation,
@@ -161,17 +171,13 @@ static ALvoid ALdistortionState_process(ALdistortionState *state, ALuint Samples
                 continue;
 
             for(it = 0;it < td;it++)
-                SamplesOut[kt][base+it] += gain * oversample_buffer[it][0];
+                SamplesOut[kt][base+it] += gain * buffer[1][it*4];
         }
 
         base += td;
     }
 }
 
-DECLARE_DEFAULT_ALLOCATORS(ALdistortionState)
-
-DEFINE_ALEFFECTSTATE_VTABLE(ALdistortionState);
-
 
 typedef struct ALdistortionStateFactory {
     DERIVE_FROM_TYPE(ALeffectStateFactory);
@@ -181,12 +187,8 @@ static ALeffectState *ALdistortionStateFactory_create(ALdistortionStateFactory *
 {
     ALdistortionState *state;
 
-    state = ALdistortionState_New(sizeof(*state));
+    NEW_OBJ0(state, ALdistortionState)();
     if(!state) return NULL;
-    SET_VTABLE2(ALdistortionState, ALeffectState, state);
-
-    ALfilterState_clear(&state->lowpass);
-    ALfilterState_clear(&state->bandpass);
 
     return STATIC_CAST(ALeffectState, state);
 }

+ 82 - 51
libs/openal-soft/Alc/effects/echo.c

@@ -34,14 +34,14 @@ typedef struct ALechoState {
     DERIVE_FROM_TYPE(ALeffectState);
 
     ALfloat *SampleBuffer;
-    ALuint BufferLength;
+    ALsizei BufferLength;
 
     // The echo is two tap. The delay is the number of samples from before the
     // current offset
     struct {
-        ALuint delay;
+        ALsizei delay;
     } Tap[2];
-    ALuint Offset;
+    ALsizei Offset;
     /* The panning gains for the two taps */
     ALfloat Gain[2][MAX_OUTPUT_CHANNELS];
 
@@ -50,28 +50,53 @@ typedef struct ALechoState {
     ALfilterState Filter;
 } ALechoState;
 
+static ALvoid ALechoState_Destruct(ALechoState *state);
+static ALboolean ALechoState_deviceUpdate(ALechoState *state, ALCdevice *Device);
+static ALvoid ALechoState_update(ALechoState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props);
+static ALvoid ALechoState_process(ALechoState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
+DECLARE_DEFAULT_ALLOCATORS(ALechoState)
+
+DEFINE_ALEFFECTSTATE_VTABLE(ALechoState);
+
+
+static void ALechoState_Construct(ALechoState *state)
+{
+    ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
+    SET_VTABLE2(ALechoState, ALeffectState, state);
+
+    state->BufferLength = 0;
+    state->SampleBuffer = NULL;
+
+    state->Tap[0].delay = 0;
+    state->Tap[1].delay = 0;
+    state->Offset = 0;
+
+    ALfilterState_clear(&state->Filter);
+}
+
 static ALvoid ALechoState_Destruct(ALechoState *state)
 {
-    free(state->SampleBuffer);
+    al_free(state->SampleBuffer);
     state->SampleBuffer = NULL;
+    ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
 }
 
 static ALboolean ALechoState_deviceUpdate(ALechoState *state, ALCdevice *Device)
 {
-    ALuint maxlen, i;
+    ALsizei maxlen, i;
 
     // Use the next power of 2 for the buffer length, so the tap offsets can be
     // wrapped using a mask instead of a modulo
-    maxlen  = fastf2u(AL_ECHO_MAX_DELAY * Device->Frequency) + 1;
-    maxlen += fastf2u(AL_ECHO_MAX_LRDELAY * Device->Frequency) + 1;
+    maxlen  = fastf2i(AL_ECHO_MAX_DELAY * Device->Frequency) + 1;
+    maxlen += fastf2i(AL_ECHO_MAX_LRDELAY * Device->Frequency) + 1;
     maxlen  = NextPowerOf2(maxlen);
 
     if(maxlen != state->BufferLength)
     {
-        void *temp;
-
-        temp = realloc(state->SampleBuffer, maxlen * sizeof(ALfloat));
+        void *temp = al_calloc(16, maxlen * sizeof(ALfloat));
         if(!temp) return AL_FALSE;
+
+        al_free(state->SampleBuffer);
         state->SampleBuffer = temp;
         state->BufferLength = maxlen;
     }
@@ -81,50 +106,60 @@ static ALboolean ALechoState_deviceUpdate(ALechoState *state, ALCdevice *Device)
     return AL_TRUE;
 }
 
-static ALvoid ALechoState_update(ALechoState *state, ALCdevice *Device, const ALeffectslot *Slot)
+static ALvoid ALechoState_update(ALechoState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props)
 {
-    ALfloat pandir[3] = { 0.0f, 0.0f, 0.0f };
     ALuint frequency = Device->Frequency;
-    ALfloat gain, lrpan;
+    ALfloat coeffs[MAX_AMBI_COEFFS];
+    ALfloat gain, lrpan, spread;
 
-    state->Tap[0].delay = fastf2u(Slot->EffectProps.Echo.Delay * frequency) + 1;
-    state->Tap[1].delay = fastf2u(Slot->EffectProps.Echo.LRDelay * frequency);
+    state->Tap[0].delay = fastf2i(props->Echo.Delay * frequency) + 1;
+    state->Tap[1].delay = fastf2i(props->Echo.LRDelay * frequency);
     state->Tap[1].delay += state->Tap[0].delay;
 
-    lrpan = Slot->EffectProps.Echo.Spread;
+    spread = props->Echo.Spread;
+    if(spread < 0.0f) lrpan = -1.0f;
+    else lrpan = 1.0f;
+    /* Convert echo spread (where 0 = omni, +/-1 = directional) to coverage
+     * spread (where 0 = point, tau = omni).
+     */
+    spread = asinf(1.0f - fabsf(spread))*4.0f;
 
-    state->FeedGain = Slot->EffectProps.Echo.Feedback;
+    state->FeedGain = props->Echo.Feedback;
 
-    gain = minf(1.0f - Slot->EffectProps.Echo.Damping, 0.01f);
+    gain = maxf(1.0f - props->Echo.Damping, 0.0625f); /* Limit -24dB */
     ALfilterState_setParams(&state->Filter, ALfilterType_HighShelf,
                             gain, LOWPASSFREQREF/frequency,
-                            calc_rcpQ_from_slope(gain, 0.75f));
+                            calc_rcpQ_from_slope(gain, 1.0f));
 
-    gain = Slot->Gain;
+    gain = Slot->Params.Gain;
 
     /* First tap panning */
-    pandir[0] = -lrpan;
-    ComputeDirectionalGains(Device, pandir, gain, state->Gain[0]);
+    CalcAngleCoeffs(-F_PI_2*lrpan, 0.0f, spread, coeffs);
+    ComputePanningGains(Device->Dry, coeffs, gain, state->Gain[0]);
 
     /* Second tap panning */
-    pandir[0] = +lrpan;
-    ComputeDirectionalGains(Device, pandir, gain, state->Gain[1]);
+    CalcAngleCoeffs( F_PI_2*lrpan, 0.0f, spread, coeffs);
+    ComputePanningGains(Device->Dry, coeffs, gain, state->Gain[1]);
 }
 
-static ALvoid ALechoState_process(ALechoState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels)
+static ALvoid ALechoState_process(ALechoState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
 {
-    const ALuint mask = state->BufferLength-1;
-    const ALuint tap1 = state->Tap[0].delay;
-    const ALuint tap2 = state->Tap[1].delay;
-    ALuint offset = state->Offset;
-    ALfloat smp;
-    ALuint base;
-    ALuint i, k;
-
+    const ALsizei mask = state->BufferLength-1;
+    const ALsizei tap1 = state->Tap[0].delay;
+    const ALsizei tap2 = state->Tap[1].delay;
+    ALsizei offset = state->Offset;
+    ALfloat x[2], y[2], in, out;
+    ALsizei base, k;
+    ALsizei 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];
     for(base = 0;base < SamplesToDo;)
     {
         ALfloat temps[128][2];
-        ALuint td = minu(128, SamplesToDo-base);
+        ALsizei td = mini(128, SamplesToDo-base);
 
         for(i = 0;i < td;i++)
         {
@@ -135,8 +170,14 @@ static ALvoid ALechoState_process(ALechoState *state, ALuint SamplesToDo, const
 
             // Apply damping and feedback gain to the second tap, and mix in the
             // new sample
-            smp = ALfilterState_processSingle(&state->Filter, temps[i][1]+SamplesIn[i+base]);
-            state->SampleBuffer[offset&mask] = smp * state->FeedGain;
+            in = temps[i][1] + SamplesIn[0][i+base];
+            out = in*state->Filter.b0 +
+                  x[0]*state->Filter.b1 + x[1]*state->Filter.b2 -
+                  y[0]*state->Filter.a1 - y[1]*state->Filter.a2;
+            x[1] = x[0]; x[0] = in;
+            y[1] = y[0]; y[0] = out;
+
+            state->SampleBuffer[offset&mask] = out * state->FeedGain;
             offset++;
         }
 
@@ -159,14 +200,14 @@ static ALvoid ALechoState_process(ALechoState *state, ALuint 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->Offset = offset;
 }
 
-DECLARE_DEFAULT_ALLOCATORS(ALechoState)
-
-DEFINE_ALEFFECTSTATE_VTABLE(ALechoState);
-
 
 typedef struct ALechoStateFactory {
     DERIVE_FROM_TYPE(ALeffectStateFactory);
@@ -176,18 +217,8 @@ ALeffectState *ALechoStateFactory_create(ALechoStateFactory *UNUSED(factory))
 {
     ALechoState *state;
 
-    state = ALechoState_New(sizeof(*state));
+    NEW_OBJ0(state, ALechoState)();
     if(!state) return NULL;
-    SET_VTABLE2(ALechoState, ALeffectState, state);
-
-    state->BufferLength = 0;
-    state->SampleBuffer = NULL;
-
-    state->Tap[0].delay = 0;
-    state->Tap[1].delay = 0;
-    state->Offset = 0;
-
-    ALfilterState_clear(&state->Filter);
 
     return STATIC_CAST(ALeffectState, state);
 }

+ 97 - 58
libs/openal-soft/Alc/effects/equalizer.c

@@ -71,18 +71,50 @@
  * filter coefficients" by Robert Bristow-Johnson                        *
  * http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt                   */
 
+
+/* The maximum number of sample frames per update. */
+#define MAX_UPDATE_SAMPLES 256
+
 typedef struct ALequalizerState {
     DERIVE_FROM_TYPE(ALeffectState);
 
     /* Effect gains for each channel */
-    ALfloat Gain[MAX_OUTPUT_CHANNELS];
+    ALfloat Gain[MAX_EFFECT_CHANNELS][MAX_OUTPUT_CHANNELS];
 
     /* Effect parameters */
-    ALfilterState filter[4];
+    ALfilterState filter[4][MAX_EFFECT_CHANNELS];
+
+    ALfloat SampleBuffer[4][MAX_EFFECT_CHANNELS][MAX_UPDATE_SAMPLES];
 } ALequalizerState;
 
-static ALvoid ALequalizerState_Destruct(ALequalizerState *UNUSED(state))
+static ALvoid ALequalizerState_Destruct(ALequalizerState *state);
+static ALboolean ALequalizerState_deviceUpdate(ALequalizerState *state, ALCdevice *device);
+static ALvoid ALequalizerState_update(ALequalizerState *state, const ALCdevice *device, const ALeffectslot *slot, const ALeffectProps *props);
+static ALvoid ALequalizerState_process(ALequalizerState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
+DECLARE_DEFAULT_ALLOCATORS(ALequalizerState)
+
+DEFINE_ALEFFECTSTATE_VTABLE(ALequalizerState);
+
+
+static void ALequalizerState_Construct(ALequalizerState *state)
+{
+    int it, ft;
+
+    ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
+    SET_VTABLE2(ALequalizerState, ALeffectState, state);
+
+    /* Initialize sample history only on filter creation to avoid */
+    /* sound clicks if filter settings were changed in runtime.   */
+    for(it = 0; it < 4; it++)
+    {
+        for(ft = 0;ft < MAX_EFFECT_CHANNELS;ft++)
+            ALfilterState_clear(&state->filter[it][ft]);
+    }
+}
+
+static ALvoid ALequalizerState_Destruct(ALequalizerState *state)
 {
+    ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
 }
 
 static ALboolean ALequalizerState_deviceUpdate(ALequalizerState *UNUSED(state), ALCdevice *UNUSED(device))
@@ -90,82 +122,96 @@ static ALboolean ALequalizerState_deviceUpdate(ALequalizerState *UNUSED(state),
     return AL_TRUE;
 }
 
-static ALvoid ALequalizerState_update(ALequalizerState *state, ALCdevice *device, const ALeffectslot *slot)
+static ALvoid ALequalizerState_update(ALequalizerState *state, const ALCdevice *device, const ALeffectslot *slot, const ALeffectProps *props)
 {
     ALfloat frequency = (ALfloat)device->Frequency;
     ALfloat gain, freq_mult;
+    ALuint i;
 
-    ComputeAmbientGains(device, slot->Gain, state->Gain);
+    STATIC_CAST(ALeffectState,state)->OutBuffer = device->FOAOut.Buffer;
+    STATIC_CAST(ALeffectState,state)->OutChannels = device->FOAOut.NumChannels;
+    for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
+        ComputeFirstOrderGains(device->FOAOut, IdentityMatrixf.m[i],
+                               slot->Params.Gain, state->Gain[i]);
 
     /* Calculate coefficients for the each type of filter. Note that the shelf
      * filters' gain is for the reference frequency, which is the centerpoint
      * of the transition band.
      */
-    gain = sqrtf(slot->EffectProps.Equalizer.LowGain);
-    freq_mult = slot->EffectProps.Equalizer.LowCutoff/frequency;
-    ALfilterState_setParams(&state->filter[0], ALfilterType_LowShelf,
+    gain = maxf(sqrtf(props->Equalizer.LowGain), 0.0625f); /* Limit -24dB */
+    freq_mult = props->Equalizer.LowCutoff/frequency;
+    ALfilterState_setParams(&state->filter[0][0], ALfilterType_LowShelf,
         gain, freq_mult, calc_rcpQ_from_slope(gain, 0.75f)
     );
-
-    gain = slot->EffectProps.Equalizer.Mid1Gain;
-    freq_mult = slot->EffectProps.Equalizer.Mid1Center/frequency;
-    ALfilterState_setParams(&state->filter[1], ALfilterType_Peaking,
-        gain, freq_mult, calc_rcpQ_from_bandwidth(freq_mult, slot->EffectProps.Equalizer.Mid1Width)
+    /* Copy the filter coefficients for the other input channels. */
+    for(i = 1;i < MAX_EFFECT_CHANNELS;i++)
+        ALfilterState_copyParams(&state->filter[0][i], &state->filter[0][0]);
+
+    gain = maxf(props->Equalizer.Mid1Gain, 0.0625f);
+    freq_mult = props->Equalizer.Mid1Center/frequency;
+    ALfilterState_setParams(&state->filter[1][0], ALfilterType_Peaking,
+        gain, freq_mult, calc_rcpQ_from_bandwidth(
+            freq_mult, props->Equalizer.Mid1Width
+        )
     );
-
-    gain = slot->EffectProps.Equalizer.Mid2Gain;
-    freq_mult = slot->EffectProps.Equalizer.Mid2Center/frequency;
-    ALfilterState_setParams(&state->filter[2], ALfilterType_Peaking,
-        gain, freq_mult, calc_rcpQ_from_bandwidth(freq_mult, slot->EffectProps.Equalizer.Mid2Width)
+    for(i = 1;i < MAX_EFFECT_CHANNELS;i++)
+        ALfilterState_copyParams(&state->filter[1][i], &state->filter[1][0]);
+
+    gain = maxf(props->Equalizer.Mid2Gain, 0.0625f);
+    freq_mult = props->Equalizer.Mid2Center/frequency;
+    ALfilterState_setParams(&state->filter[2][0], ALfilterType_Peaking,
+        gain, freq_mult, calc_rcpQ_from_bandwidth(
+            freq_mult, props->Equalizer.Mid2Width
+        )
     );
+    for(i = 1;i < MAX_EFFECT_CHANNELS;i++)
+        ALfilterState_copyParams(&state->filter[2][i], &state->filter[2][0]);
 
-    gain = sqrtf(slot->EffectProps.Equalizer.HighGain);
-    freq_mult = slot->EffectProps.Equalizer.HighCutoff/frequency;
-    ALfilterState_setParams(&state->filter[3], ALfilterType_HighShelf,
+    gain = maxf(sqrtf(props->Equalizer.HighGain), 0.0625f);
+    freq_mult = props->Equalizer.HighCutoff/frequency;
+    ALfilterState_setParams(&state->filter[3][0], ALfilterType_HighShelf,
         gain, freq_mult, calc_rcpQ_from_slope(gain, 0.75f)
     );
+    for(i = 1;i < MAX_EFFECT_CHANNELS;i++)
+        ALfilterState_copyParams(&state->filter[3][i], &state->filter[3][0]);
 }
 
-static ALvoid ALequalizerState_process(ALequalizerState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels)
+static ALvoid ALequalizerState_process(ALequalizerState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
 {
-    ALuint base;
-    ALuint it;
-    ALuint kt;
-    ALuint ft;
+    ALfloat (*Samples)[MAX_EFFECT_CHANNELS][MAX_UPDATE_SAMPLES] = state->SampleBuffer;
+    ALsizei it, kt, ft;
+    ALsizei base;
 
     for(base = 0;base < SamplesToDo;)
     {
-        ALfloat temps[256];
-        ALuint td = minu(256, SamplesToDo-base);
-
-        for(it = 0;it < td;it++)
+        ALsizei td = mini(MAX_UPDATE_SAMPLES, SamplesToDo-base);
+
+        for(ft = 0;ft < MAX_EFFECT_CHANNELS;ft++)
+            ALfilterState_process(&state->filter[0][ft], Samples[0][ft], &SamplesIn[ft][base], td);
+        for(ft = 0;ft < MAX_EFFECT_CHANNELS;ft++)
+            ALfilterState_process(&state->filter[1][ft], Samples[1][ft], Samples[0][ft], td);
+        for(ft = 0;ft < MAX_EFFECT_CHANNELS;ft++)
+            ALfilterState_process(&state->filter[2][ft], Samples[2][ft], Samples[1][ft], td);
+        for(ft = 0;ft < MAX_EFFECT_CHANNELS;ft++)
+            ALfilterState_process(&state->filter[3][ft], Samples[3][ft], Samples[2][ft], td);
+
+        for(ft = 0;ft < MAX_EFFECT_CHANNELS;ft++)
         {
-            ALfloat smp = SamplesIn[base+it];
-
-            for(ft = 0;ft < 4;ft++)
-                smp = ALfilterState_processSingle(&state->filter[ft], smp);
-
-            temps[it] = smp;
-        }
-
-        for(kt = 0;kt < NumChannels;kt++)
-        {
-            ALfloat gain = state->Gain[kt];
-            if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
-                continue;
-
-            for(it = 0;it < td;it++)
-                SamplesOut[kt][base+it] += gain * temps[it];
+            for(kt = 0;kt < NumChannels;kt++)
+            {
+                ALfloat gain = state->Gain[ft][kt];
+                if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
+                    continue;
+
+                for(it = 0;it < td;it++)
+                    SamplesOut[kt][base+it] += gain * Samples[3][ft][it];
+            }
         }
 
         base += td;
     }
 }
 
-DECLARE_DEFAULT_ALLOCATORS(ALequalizerState)
-
-DEFINE_ALEFFECTSTATE_VTABLE(ALequalizerState);
-
 
 typedef struct ALequalizerStateFactory {
     DERIVE_FROM_TYPE(ALeffectStateFactory);
@@ -174,16 +220,9 @@ typedef struct ALequalizerStateFactory {
 ALeffectState *ALequalizerStateFactory_create(ALequalizerStateFactory *UNUSED(factory))
 {
     ALequalizerState *state;
-    int it;
 
-    state = ALequalizerState_New(sizeof(*state));
+    NEW_OBJ0(state, ALequalizerState)();
     if(!state) return NULL;
-    SET_VTABLE2(ALequalizerState, ALeffectState, state);
-
-    /* Initialize sample history only on filter creation to avoid */
-    /* sound clicks if filter settings were changed in runtime.   */
-    for(it = 0; it < 4; it++)
-        ALfilterState_clear(&state->filter[it]);
 
     return STATIC_CAST(ALeffectState, state);
 }

+ 114 - 104
libs/openal-soft/Alc/effects/flanger.c

@@ -39,9 +39,9 @@ typedef struct ALflangerState {
     DERIVE_FROM_TYPE(ALeffectState);
 
     ALfloat *SampleBuffer[2];
-    ALuint BufferLength;
-    ALuint offset;
-    ALuint lfo_range;
+    ALsizei BufferLength;
+    ALsizei offset;
+    ALsizei lfo_range;
     ALfloat lfo_scale;
     ALint lfo_disp;
 
@@ -55,27 +55,51 @@ typedef struct ALflangerState {
     ALfloat feedback;
 } ALflangerState;
 
+static ALvoid ALflangerState_Destruct(ALflangerState *state);
+static ALboolean ALflangerState_deviceUpdate(ALflangerState *state, ALCdevice *Device);
+static ALvoid ALflangerState_update(ALflangerState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props);
+static ALvoid ALflangerState_process(ALflangerState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
+DECLARE_DEFAULT_ALLOCATORS(ALflangerState)
+
+DEFINE_ALEFFECTSTATE_VTABLE(ALflangerState);
+
+
+static void ALflangerState_Construct(ALflangerState *state)
+{
+    ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
+    SET_VTABLE2(ALflangerState, ALeffectState, state);
+
+    state->BufferLength = 0;
+    state->SampleBuffer[0] = NULL;
+    state->SampleBuffer[1] = NULL;
+    state->offset = 0;
+    state->lfo_range = 1;
+    state->waveform = FWF_Triangle;
+}
+
 static ALvoid ALflangerState_Destruct(ALflangerState *state)
 {
-    free(state->SampleBuffer[0]);
+    al_free(state->SampleBuffer[0]);
     state->SampleBuffer[0] = NULL;
     state->SampleBuffer[1] = NULL;
+
+    ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
 }
 
 static ALboolean ALflangerState_deviceUpdate(ALflangerState *state, ALCdevice *Device)
 {
-    ALuint maxlen;
-    ALuint it;
+    ALsizei maxlen;
+    ALsizei it;
 
-    maxlen = fastf2u(AL_FLANGER_MAX_DELAY * 3.0f * Device->Frequency) + 1;
+    maxlen = fastf2i(AL_FLANGER_MAX_DELAY * 2.0f * Device->Frequency) + 1;
     maxlen = NextPowerOf2(maxlen);
 
     if(maxlen != state->BufferLength)
     {
-        void *temp;
-
-        temp = realloc(state->SampleBuffer[0], maxlen * sizeof(ALfloat) * 2);
+        void *temp = al_calloc(16, maxlen * sizeof(ALfloat) * 2);
         if(!temp) return AL_FALSE;
+
+        al_free(state->SampleBuffer[0]);
         state->SampleBuffer[0] = temp;
         state->SampleBuffer[1] = state->SampleBuffer[0] + maxlen;
 
@@ -91,15 +115,14 @@ static ALboolean ALflangerState_deviceUpdate(ALflangerState *state, ALCdevice *D
     return AL_TRUE;
 }
 
-static ALvoid ALflangerState_update(ALflangerState *state, ALCdevice *Device, const ALeffectslot *Slot)
+static ALvoid ALflangerState_update(ALflangerState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props)
 {
-    static const ALfloat left_dir[3] = { -1.0f, 0.0f, 0.0f };
-    static const ALfloat right_dir[3] = { 1.0f, 0.0f, 0.0f };
     ALfloat frequency = (ALfloat)Device->Frequency;
+    ALfloat coeffs[MAX_AMBI_COEFFS];
     ALfloat rate;
     ALint phase;
 
-    switch(Slot->EffectProps.Flanger.Waveform)
+    switch(props->Flanger.Waveform)
     {
         case AL_FLANGER_WAVEFORM_TRIANGLE:
             state->waveform = FWF_Triangle;
@@ -108,16 +131,19 @@ static ALvoid ALflangerState_update(ALflangerState *state, ALCdevice *Device, co
             state->waveform = FWF_Sinusoid;
             break;
     }
-    state->depth = Slot->EffectProps.Flanger.Depth;
-    state->feedback = Slot->EffectProps.Flanger.Feedback;
-    state->delay = fastf2i(Slot->EffectProps.Flanger.Delay * frequency);
+    state->feedback = props->Flanger.Feedback;
+    state->delay = fastf2i(props->Flanger.Delay * frequency);
+    /* The LFO depth is scaled to be relative to the sample delay. */
+    state->depth = props->Flanger.Depth * state->delay;
 
     /* Gains for left and right sides */
-    ComputeDirectionalGains(Device, left_dir, Slot->Gain, state->Gain[0]);
-    ComputeDirectionalGains(Device, right_dir, Slot->Gain, state->Gain[1]);
+    CalcAngleCoeffs(-F_PI_2, 0.0f, 0.0f, coeffs);
+    ComputePanningGains(Device->Dry, coeffs, Slot->Params.Gain, state->Gain[0]);
+    CalcAngleCoeffs( F_PI_2, 0.0f, 0.0f, coeffs);
+    ComputePanningGains(Device->Dry, coeffs, Slot->Params.Gain, state->Gain[1]);
 
-    phase = Slot->EffectProps.Flanger.Phase;
-    rate = Slot->EffectProps.Flanger.Rate;
+    phase = props->Flanger.Phase;
+    rate = props->Flanger.Rate;
     if(!(rate > 0.0f))
     {
         state->lfo_scale = 0.0f;
@@ -127,7 +153,7 @@ static ALvoid ALflangerState_update(ALflangerState *state, ALCdevice *Device, co
     else
     {
         /* Calculate LFO coefficient */
-        state->lfo_range = fastf2u(frequency/rate + 0.5f);
+        state->lfo_range = fastf2i(frequency/rate + 0.5f);
         switch(state->waveform)
         {
             case FWF_Triangle:
@@ -139,114 +165,106 @@ static ALvoid ALflangerState_update(ALflangerState *state, ALCdevice *Device, co
         }
 
         /* Calculate lfo phase displacement */
-        state->lfo_disp = fastf2i(state->lfo_range * (phase/360.0f));
+        if(phase >= 0)
+            state->lfo_disp = fastf2i(state->lfo_range * (phase/360.0f));
+        else
+            state->lfo_disp = fastf2i(state->lfo_range * ((360+phase)/360.0f));
     }
 }
 
-static inline void Triangle(ALint *delay_left, ALint *delay_right, ALuint offset, const ALflangerState *state)
+static void GetTriangleDelays(ALint *restrict delays, ALsizei offset, const ALsizei lfo_range,
+                              const ALfloat lfo_scale, const ALfloat depth, const ALsizei delay,
+                              const ALsizei todo)
 {
-    ALfloat lfo_value;
-
-    lfo_value = 2.0f - fabsf(2.0f - state->lfo_scale*(offset%state->lfo_range));
-    lfo_value *= state->depth * state->delay;
-    *delay_left = fastf2i(lfo_value) + state->delay;
-
-    offset += state->lfo_disp;
-    lfo_value = 2.0f - fabsf(2.0f - state->lfo_scale*(offset%state->lfo_range));
-    lfo_value *= state->depth * state->delay;
-    *delay_right = fastf2i(lfo_value) + state->delay;
+    ALsizei i;
+    for(i = 0;i < todo;i++)
+    {
+        delays[i] = fastf2i((1.0f - fabsf(2.0f - lfo_scale*offset)) * depth) + delay;
+        offset = (offset+1)%lfo_range;
+    }
 }
 
-static inline void Sinusoid(ALint *delay_left, ALint *delay_right, ALuint offset, const ALflangerState *state)
+static void GetSinusoidDelays(ALint *restrict delays, ALsizei offset, const ALsizei lfo_range,
+                              const ALfloat lfo_scale, const ALfloat depth, const ALsizei delay,
+                              const ALsizei todo)
 {
-    ALfloat lfo_value;
-
-    lfo_value = 1.0f + sinf(state->lfo_scale*(offset%state->lfo_range));
-    lfo_value *= state->depth * state->delay;
-    *delay_left = fastf2i(lfo_value) + state->delay;
-
-    offset += state->lfo_disp;
-    lfo_value = 1.0f + sinf(state->lfo_scale*(offset%state->lfo_range));
-    lfo_value *= state->depth * state->delay;
-    *delay_right = fastf2i(lfo_value) + state->delay;
-}
-
-#define DECL_TEMPLATE(Func)                                                   \
-static void Process##Func(ALflangerState *state, const ALuint SamplesToDo,    \
-  const ALfloat *restrict SamplesIn, ALfloat (*restrict out)[2])              \
-{                                                                             \
-    const ALuint bufmask = state->BufferLength-1;                             \
-    ALfloat *restrict leftbuf = state->SampleBuffer[0];                       \
-    ALfloat *restrict rightbuf = state->SampleBuffer[1];                      \
-    ALuint offset = state->offset;                                            \
-    const ALfloat feedback = state->feedback;                                 \
-    ALuint it;                                                                \
-                                                                              \
-    for(it = 0;it < SamplesToDo;it++)                                         \
-    {                                                                         \
-        ALint delay_left, delay_right;                                        \
-        Func(&delay_left, &delay_right, offset, state);                       \
-                                                                              \
-        out[it][0] = leftbuf[(offset-delay_left)&bufmask];                    \
-        leftbuf[offset&bufmask] = (out[it][0]+SamplesIn[it]) * feedback;      \
-                                                                              \
-        out[it][1] = rightbuf[(offset-delay_right)&bufmask];                  \
-        rightbuf[offset&bufmask] = (out[it][1]+SamplesIn[it]) * feedback;     \
-                                                                              \
-        offset++;                                                             \
-    }                                                                         \
-    state->offset = offset;                                                   \
+    ALsizei i;
+    for(i = 0;i < todo;i++)
+    {
+        delays[i] = fastf2i(sinf(lfo_scale*offset) * depth) + delay;
+        offset = (offset+1)%lfo_range;
+    }
 }
 
-DECL_TEMPLATE(Triangle)
-DECL_TEMPLATE(Sinusoid)
-
-#undef DECL_TEMPLATE
-
-static ALvoid ALflangerState_process(ALflangerState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels)
+static ALvoid ALflangerState_process(ALflangerState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
 {
-    ALuint it, kt;
-    ALuint base;
+    ALfloat *restrict leftbuf = state->SampleBuffer[0];
+    ALfloat *restrict rightbuf = state->SampleBuffer[1];
+    const ALsizei bufmask = state->BufferLength-1;
+    const ALfloat feedback = state->feedback;
+    ALsizei offset = state->offset;
+    ALsizei i, c;
+    ALsizei base;
 
     for(base = 0;base < SamplesToDo;)
     {
+        const ALsizei todo = mini(128, SamplesToDo-base);
         ALfloat temps[128][2];
-        ALuint td = minu(128, SamplesToDo-base);
+        ALint moddelays[2][128];
 
         switch(state->waveform)
         {
             case FWF_Triangle:
-                ProcessTriangle(state, td, SamplesIn+base, temps);
+                GetTriangleDelays(moddelays[0], offset%state->lfo_range, state->lfo_range,
+                                  state->lfo_scale, state->depth, state->delay, todo);
+                GetTriangleDelays(moddelays[1], (offset+state->lfo_disp)%state->lfo_range,
+                                  state->lfo_range, state->lfo_scale, state->depth, state->delay,
+                                  todo);
                 break;
             case FWF_Sinusoid:
-                ProcessSinusoid(state, td, SamplesIn+base, temps);
+                GetSinusoidDelays(moddelays[0], offset%state->lfo_range, state->lfo_range,
+                                  state->lfo_scale, state->depth, state->delay, todo);
+                GetSinusoidDelays(moddelays[1], (offset+state->lfo_disp)%state->lfo_range,
+                                  state->lfo_range, state->lfo_scale, state->depth, state->delay,
+                                  todo);
                 break;
         }
 
-        for(kt = 0;kt < NumChannels;kt++)
+        for(i = 0;i < todo;i++)
         {
-            ALfloat gain = state->Gain[0][kt];
+            leftbuf[offset&bufmask] = SamplesIn[0][base+i];
+            temps[i][0] = leftbuf[(offset-moddelays[0][i])&bufmask] * feedback;
+            leftbuf[offset&bufmask] += temps[i][0];
+
+            rightbuf[offset&bufmask] = SamplesIn[0][base+i];
+            temps[i][1] = rightbuf[(offset-moddelays[1][i])&bufmask] * feedback;
+            rightbuf[offset&bufmask] += temps[i][1];
+
+            offset++;
+        }
+
+        for(c = 0;c < NumChannels;c++)
+        {
+            ALfloat gain = state->Gain[0][c];
             if(fabsf(gain) > GAIN_SILENCE_THRESHOLD)
             {
-                for(it = 0;it < td;it++)
-                    SamplesOut[kt][it+base] += temps[it][0] * gain;
+                for(i = 0;i < todo;i++)
+                    SamplesOut[c][i+base] += temps[i][0] * gain;
             }
 
-            gain = state->Gain[1][kt];
+            gain = state->Gain[1][c];
             if(fabsf(gain) > GAIN_SILENCE_THRESHOLD)
             {
-                for(it = 0;it < td;it++)
-                    SamplesOut[kt][it+base] += temps[it][1] * gain;
+                for(i = 0;i < todo;i++)
+                    SamplesOut[c][i+base] += temps[i][1] * gain;
             }
         }
 
-        base += td;
+        base += todo;
     }
-}
 
-DECLARE_DEFAULT_ALLOCATORS(ALflangerState)
-
-DEFINE_ALEFFECTSTATE_VTABLE(ALflangerState);
+    state->offset = offset;
+}
 
 
 typedef struct ALflangerStateFactory {
@@ -257,16 +275,8 @@ ALeffectState *ALflangerStateFactory_create(ALflangerStateFactory *UNUSED(factor
 {
     ALflangerState *state;
 
-    state = ALflangerState_New(sizeof(*state));
+    NEW_OBJ0(state, ALflangerState)();
     if(!state) return NULL;
-    SET_VTABLE2(ALflangerState, ALeffectState, state);
-
-    state->BufferLength = 0;
-    state->SampleBuffer[0] = NULL;
-    state->SampleBuffer[1] = NULL;
-    state->offset = 0;
-    state->lfo_range = 1;
-    state->waveform = FWF_Triangle;
 
     return STATIC_CAST(ALeffectState, state);
 }

+ 97 - 87
libs/openal-soft/Alc/effects/modulator.c

@@ -33,78 +33,55 @@
 typedef struct ALmodulatorState {
     DERIVE_FROM_TYPE(ALeffectState);
 
-    enum {
-        SINUSOID,
-        SAWTOOTH,
-        SQUARE
-    } Waveform;
+    void (*Process)(ALfloat*, const ALfloat*, ALsizei, const ALsizei, ALsizei);
 
-    ALuint index;
-    ALuint step;
+    ALsizei index;
+    ALsizei step;
 
-    ALfloat Gain[MAX_OUTPUT_CHANNELS];
+    ALfloat Gain[MAX_EFFECT_CHANNELS][MAX_OUTPUT_CHANNELS];
 
-    ALfilterState Filter;
+    ALfilterState Filter[MAX_EFFECT_CHANNELS];
 } ALmodulatorState;
 
+static ALvoid ALmodulatorState_Destruct(ALmodulatorState *state);
+static ALboolean ALmodulatorState_deviceUpdate(ALmodulatorState *state, ALCdevice *device);
+static ALvoid ALmodulatorState_update(ALmodulatorState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props);
+static ALvoid ALmodulatorState_process(ALmodulatorState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
+DECLARE_DEFAULT_ALLOCATORS(ALmodulatorState)
+
+DEFINE_ALEFFECTSTATE_VTABLE(ALmodulatorState);
+
+
 #define WAVEFORM_FRACBITS  24
 #define WAVEFORM_FRACONE   (1<<WAVEFORM_FRACBITS)
 #define WAVEFORM_FRACMASK  (WAVEFORM_FRACONE-1)
 
-static inline ALfloat Sin(ALuint index)
+static inline ALfloat Sin(ALsizei index)
 {
     return sinf(index*(F_TAU/WAVEFORM_FRACONE) - F_PI)*0.5f + 0.5f;
 }
 
-static inline ALfloat Saw(ALuint index)
+static inline ALfloat Saw(ALsizei index)
 {
     return (ALfloat)index / WAVEFORM_FRACONE;
 }
 
-static inline ALfloat Square(ALuint index)
+static inline ALfloat Square(ALsizei index)
 {
     return (ALfloat)((index >> (WAVEFORM_FRACBITS - 1)) & 1);
 }
 
 #define DECL_TEMPLATE(func)                                                   \
-static void Process##func(ALmodulatorState *state, ALuint SamplesToDo,        \
-  const ALfloat *restrict SamplesIn,                                          \
-  ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels)             \
+static void Modulate##func(ALfloat *restrict dst, const ALfloat *restrict src,\
+                           ALsizei index, const ALsizei step, ALsizei todo)   \
 {                                                                             \
-    const ALuint step = state->step;                                          \
-    ALuint index = state->index;                                              \
-    ALuint base;                                                              \
-                                                                              \
-    for(base = 0;base < SamplesToDo;)                                         \
+    ALsizei i;                                                                \
+    for(i = 0;i < todo;i++)                                                   \
     {                                                                         \
-        ALfloat temps[256];                                                   \
-        ALuint td = minu(256, SamplesToDo-base);                              \
-        ALuint i, k;                                                          \
-                                                                              \
-        for(i = 0;i < td;i++)                                                 \
-        {                                                                     \
-            ALfloat samp;                                                     \
-            samp = SamplesIn[base+i];                                         \
-            samp = ALfilterState_processSingle(&state->Filter, samp);         \
-                                                                              \
-            index += step;                                                    \
-            index &= WAVEFORM_FRACMASK;                                       \
-            temps[i] = samp * func(index);                                    \
-        }                                                                     \
-                                                                              \
-        for(k = 0;k < NumChannels;k++)                                        \
-        {                                                                     \
-            ALfloat gain = state->Gain[k];                                    \
-            if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))                       \
-                continue;                                                     \
-                                                                              \
-            for(i = 0;i < td;i++)                                             \
-                SamplesOut[k][base+i] += gain * temps[i];                     \
-        }                                                                     \
-                                                                              \
-        base += td;                                                           \
+        index += step;                                                        \
+        index &= WAVEFORM_FRACMASK;                                           \
+        dst[i] = src[i] * func(index);                                        \
     }                                                                         \
-    state->index = index;                                                     \
 }
 
 DECL_TEMPLATE(Sin)
@@ -114,8 +91,23 @@ DECL_TEMPLATE(Square)
 #undef DECL_TEMPLATE
 
 
-static ALvoid ALmodulatorState_Destruct(ALmodulatorState *UNUSED(state))
+static void ALmodulatorState_Construct(ALmodulatorState *state)
+{
+    ALuint i;
+
+    ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
+    SET_VTABLE2(ALmodulatorState, ALeffectState, state);
+
+    state->index = 0;
+    state->step = 1;
+
+    for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
+        ALfilterState_clear(&state->Filter[i]);
+}
+
+static ALvoid ALmodulatorState_Destruct(ALmodulatorState *state)
 {
+    ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
 }
 
 static ALboolean ALmodulatorState_deviceUpdate(ALmodulatorState *UNUSED(state), ALCdevice *UNUSED(device))
@@ -123,56 +115,80 @@ static ALboolean ALmodulatorState_deviceUpdate(ALmodulatorState *UNUSED(state),
     return AL_TRUE;
 }
 
-static ALvoid ALmodulatorState_update(ALmodulatorState *state, ALCdevice *Device, const ALeffectslot *Slot)
+static ALvoid ALmodulatorState_update(ALmodulatorState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props)
 {
     ALfloat cw, a;
+    ALsizei i;
 
-    if(Slot->EffectProps.Modulator.Waveform == AL_RING_MODULATOR_SINUSOID)
-        state->Waveform = SINUSOID;
-    else if(Slot->EffectProps.Modulator.Waveform == AL_RING_MODULATOR_SAWTOOTH)
-        state->Waveform = SAWTOOTH;
-    else if(Slot->EffectProps.Modulator.Waveform == AL_RING_MODULATOR_SQUARE)
-        state->Waveform = SQUARE;
+    if(props->Modulator.Waveform == AL_RING_MODULATOR_SINUSOID)
+        state->Process = ModulateSin;
+    else if(props->Modulator.Waveform == AL_RING_MODULATOR_SAWTOOTH)
+        state->Process = ModulateSaw;
+    else /*if(Slot->Params.EffectProps.Modulator.Waveform == AL_RING_MODULATOR_SQUARE)*/
+        state->Process = ModulateSquare;
 
-    state->step = fastf2u(Slot->EffectProps.Modulator.Frequency*WAVEFORM_FRACONE /
+    state->step = fastf2i(props->Modulator.Frequency*WAVEFORM_FRACONE /
                           Device->Frequency);
     if(state->step == 0) state->step = 1;
 
     /* Custom filter coeffs, which match the old version instead of a low-shelf. */
-    cw = cosf(F_TAU * Slot->EffectProps.Modulator.HighPassCutoff / Device->Frequency);
+    cw = cosf(F_TAU * props->Modulator.HighPassCutoff / Device->Frequency);
     a = (2.0f-cw) - sqrtf(powf(2.0f-cw, 2.0f) - 1.0f);
 
-    state->Filter.a1 = -a;
-    state->Filter.a2 = 0.0f;
-    state->Filter.b1 = -a;
-    state->Filter.b2 = 0.0f;
-    state->Filter.input_gain = a;
+    for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
+    {
+        state->Filter[i].b0 = a;
+        state->Filter[i].b1 = -a;
+        state->Filter[i].b2 = 0.0f;
+        state->Filter[i].a1 = -a;
+        state->Filter[i].a2 = 0.0f;
+    }
 
-    ComputeAmbientGains(Device, Slot->Gain, state->Gain);
+    STATIC_CAST(ALeffectState,state)->OutBuffer = Device->FOAOut.Buffer;
+    STATIC_CAST(ALeffectState,state)->OutChannels = Device->FOAOut.NumChannels;
+    for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
+        ComputeFirstOrderGains(Device->FOAOut, IdentityMatrixf.m[i],
+                               Slot->Params.Gain, state->Gain[i]);
 }
 
-static ALvoid ALmodulatorState_process(ALmodulatorState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels)
+static ALvoid ALmodulatorState_process(ALmodulatorState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
 {
-    switch(state->Waveform)
-    {
-        case SINUSOID:
-            ProcessSin(state, SamplesToDo, SamplesIn, SamplesOut, NumChannels);
-            break;
-
-        case SAWTOOTH:
-            ProcessSaw(state, SamplesToDo, SamplesIn, SamplesOut, NumChannels);
-            break;
+    const ALsizei step = state->step;
+    ALsizei index = state->index;
+    ALsizei base;
 
-        case SQUARE:
-            ProcessSquare(state, SamplesToDo, SamplesIn, SamplesOut, NumChannels);
-            break;
+    for(base = 0;base < SamplesToDo;)
+    {
+        ALfloat temps[2][128];
+        ALsizei td = mini(128, SamplesToDo-base);
+        ALsizei i, j, k;
+
+        for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
+        {
+            ALfilterState_process(&state->Filter[j], temps[0], &SamplesIn[j][base], td);
+            state->Process(temps[1], temps[0], index, step, td);
+
+            for(k = 0;k < NumChannels;k++)
+            {
+                ALfloat gain = state->Gain[j][k];
+                if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
+                    continue;
+
+                for(i = 0;i < td;i++)
+                    SamplesOut[k][base+i] += gain * temps[1][i];
+            }
+        }
+
+        for(i = 0;i < td;i++)
+        {
+            index += step;
+            index &= WAVEFORM_FRACMASK;
+        }
+        base += td;
     }
+    state->index = index;
 }
 
-DECLARE_DEFAULT_ALLOCATORS(ALmodulatorState)
-
-DEFINE_ALEFFECTSTATE_VTABLE(ALmodulatorState);
-
 
 typedef struct ALmodulatorStateFactory {
     DERIVE_FROM_TYPE(ALeffectStateFactory);
@@ -182,14 +198,8 @@ static ALeffectState *ALmodulatorStateFactory_create(ALmodulatorStateFactory *UN
 {
     ALmodulatorState *state;
 
-    state = ALmodulatorState_New(sizeof(*state));
+    NEW_OBJ0(state, ALmodulatorState)();
     if(!state) return NULL;
-    SET_VTABLE2(ALmodulatorState, ALeffectState, state);
-
-    state->index = 0;
-    state->step = 1;
-
-    ALfilterState_clear(&state->Filter);
 
     return STATIC_CAST(ALeffectState, state);
 }

+ 32 - 15
libs/openal-soft/Alc/effects/null.c

@@ -13,12 +13,35 @@ typedef struct ALnullState {
     DERIVE_FROM_TYPE(ALeffectState);
 } ALnullState;
 
+/* Forward-declare "virtual" functions to define the vtable with. */
+static ALvoid ALnullState_Destruct(ALnullState *state);
+static ALboolean ALnullState_deviceUpdate(ALnullState *state, ALCdevice *device);
+static ALvoid ALnullState_update(ALnullState *state, const ALCdevice *device, const ALeffectslot *slot, const ALeffectProps *props);
+static ALvoid ALnullState_process(ALnullState *state, ALsizei samplesToDo, const ALfloatBUFFERSIZE*restrict samplesIn, ALfloatBUFFERSIZE*restrict samplesOut, ALsizei NumChannels);
+static void *ALnullState_New(size_t size);
+static void ALnullState_Delete(void *ptr);
+
+/* Define the ALeffectState vtable for this type. */
+DEFINE_ALEFFECTSTATE_VTABLE(ALnullState);
+
+
+/* This constructs the effect state. It's called when the object is first
+ * created. Make sure to call the parent Construct function first, and set the
+ * vtable!
+ */
+static void ALnullState_Construct(ALnullState *state)
+{
+    ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
+    SET_VTABLE2(ALnullState, ALeffectState, state);
+}
 
 /* This destructs (not free!) the effect state. It's called only when the
- * effect slot is no longer used.
+ * effect slot is no longer used. Make sure to call the parent Destruct
+ * function before returning!
  */
-static ALvoid ALnullState_Destruct(ALnullState* UNUSED(state))
+static ALvoid ALnullState_Destruct(ALnullState *state)
 {
+    ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
 }
 
 /* This updates the device-dependant effect state. This is called on
@@ -33,7 +56,7 @@ static ALboolean ALnullState_deviceUpdate(ALnullState* UNUSED(state), ALCdevice*
 /* This updates the effect state. This is called any time the effect is
  * (re)loaded into a slot.
  */
-static ALvoid ALnullState_update(ALnullState* UNUSED(state), ALCdevice* UNUSED(device), const ALeffectslot* UNUSED(slot))
+static ALvoid ALnullState_update(ALnullState* UNUSED(state), const ALCdevice* UNUSED(device), const ALeffectslot* UNUSED(slot), const ALeffectProps* UNUSED(props))
 {
 }
 
@@ -41,29 +64,26 @@ static ALvoid ALnullState_update(ALnullState* UNUSED(state), ALCdevice* UNUSED(d
  * input to the output buffer. The result should be added to the output buffer,
  * not replace it.
  */
-static ALvoid ALnullState_process(ALnullState* UNUSED(state), ALuint UNUSED(samplesToDo), const ALfloat *restrict UNUSED(samplesIn), ALfloatBUFFERSIZE*restrict UNUSED(samplesOut), ALuint UNUSED(NumChannels))
+static ALvoid ALnullState_process(ALnullState* UNUSED(state), ALsizei UNUSED(samplesToDo), const ALfloatBUFFERSIZE*restrict UNUSED(samplesIn), ALfloatBUFFERSIZE*restrict UNUSED(samplesOut), ALsizei UNUSED(NumChannels))
 {
 }
 
 /* This allocates memory to store the object, before it gets constructed.
- * DECLARE_DEFAULT_ALLOCATORS can be used to declate a default method.
+ * DECLARE_DEFAULT_ALLOCATORS can be used to declare a default method.
  */
 static void *ALnullState_New(size_t size)
 {
-    return malloc(size);
+    return al_malloc(16, size);
 }
 
 /* This frees the memory used by the object, after it has been destructed.
- * DECLARE_DEFAULT_ALLOCATORS can be used to declate a default method.
+ * DECLARE_DEFAULT_ALLOCATORS can be used to declare a default method.
  */
 static void ALnullState_Delete(void *ptr)
 {
-    free(ptr);
+    al_free(ptr);
 }
 
-/* Define the forwards and the ALeffectState vtable for this type. */
-DEFINE_ALEFFECTSTATE_VTABLE(ALnullState);
-
 
 typedef struct ALnullStateFactory {
     DERIVE_FROM_TYPE(ALeffectStateFactory);
@@ -74,10 +94,8 @@ ALeffectState *ALnullStateFactory_create(ALnullStateFactory *UNUSED(factory))
 {
     ALnullState *state;
 
-    state = ALnullState_New(sizeof(*state));
+    NEW_OBJ0(state, ALnullState)();
     if(!state) return NULL;
-    /* Set vtables for inherited types. */
-    SET_VTABLE2(ALnullState, ALeffectState, state);
 
     return STATIC_CAST(ALeffectState, state);
 }
@@ -88,7 +106,6 @@ DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALnullStateFactory);
 ALeffectStateFactory *ALnullStateFactory_getFactory(void)
 {
     static ALnullStateFactory NullFactory = { { GET_VTABLE2(ALnullStateFactory, ALeffectStateFactory) } };
-
     return STATIC_CAST(ALeffectStateFactory, &NullFactory);
 }
 

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


File diff suppressed because it is too large
+ 325 - 669
libs/openal-soft/Alc/helpers.c


+ 759 - 505
libs/openal-soft/Alc/hrtf.c

@@ -28,14 +28,16 @@
 #include "alMain.h"
 #include "alSource.h"
 #include "alu.h"
+#include "bformatdec.h"
 #include "hrtf.h"
 
 #include "compat.h"
+#include "almalloc.h"
 
 
 /* Current data set limits defined by the makehrtf utility. */
 #define MIN_IR_SIZE                  (8)
-#define MAX_IR_SIZE                  (128)
+#define MAX_IR_SIZE                  (512)
 #define MOD_IR_SIZE                  (8)
 
 #define MIN_EV_COUNT                 (5)
@@ -44,18 +46,10 @@
 #define MIN_AZ_COUNT                 (1)
 #define MAX_AZ_COUNT                 (128)
 
-struct Hrtf {
-    ALuint sampleRate;
-    ALuint irSize;
-    ALubyte evCount;
-
-    const ALubyte *azCount;
-    const ALushort *evOffset;
-    const ALshort *coeffs;
-    const ALubyte *delays;
-
-    al_string filename;
-    struct Hrtf *next;
+struct HrtfEntry {
+    struct HrtfEntry *next;
+    struct Hrtf *handle;
+    char filename[];
 };
 
 static const ALchar magicMarker00[8] = "MinPHR00";
@@ -63,389 +57,374 @@ static const ALchar magicMarker01[8] = "MinPHR01";
 
 /* First value for pass-through coefficients (remaining are 0), used for omni-
  * directional sounds. */
-static const ALfloat PassthruCoeff = 32767.0f * 0.707106781187f/*sqrt(0.5)*/;
+static const ALfloat PassthruCoeff = 0.707106781187f/*sqrt(0.5)*/;
+
+static ATOMIC_FLAG LoadedHrtfLock = ATOMIC_FLAG_INIT;
+static struct HrtfEntry *LoadedHrtfs = NULL;
 
-static struct Hrtf *LoadedHrtfs = NULL;
 
-/* Calculate the elevation indices given the polar elevation in radians.
- * This will return two indices between 0 and (evcount - 1) and an
- * interpolation factor between 0.0 and 1.0.
+/* Calculate the elevation index given the polar elevation in radians. This
+ * will return an index between 0 and (evcount - 1). Assumes the FPU is in
+ * round-to-zero mode.
  */
-static void CalcEvIndices(ALuint evcount, ALfloat ev, ALuint *evidx, ALfloat *evmu)
+static ALsizei CalcEvIndex(ALsizei evcount, ALfloat ev, ALfloat *mu)
 {
-    ev = (F_PI_2 + ev) * (evcount-1) / F_PI;
-    evidx[0] = fastf2u(ev);
-    evidx[1] = minu(evidx[0] + 1, evcount-1);
-    *evmu = ev - evidx[0];
+    ALsizei idx;
+    ev = (F_PI_2+ev) * (evcount-1) / F_PI;
+    idx = mini(fastf2i(ev), evcount-1);
+
+    *mu = ev - idx;
+    return idx;
 }
 
-/* Calculate the azimuth indices given the polar azimuth in radians.  This
- * will return two indices between 0 and (azcount - 1) and an interpolation
- * factor between 0.0 and 1.0.
+/* Calculate the azimuth index given the polar azimuth in radians. This will
+ * return an index between 0 and (azcount - 1). Assumes the FPU is in round-to-
+ * zero mode.
  */
-static void CalcAzIndices(ALuint azcount, ALfloat az, ALuint *azidx, ALfloat *azmu)
+static ALsizei CalcAzIndex(ALsizei azcount, ALfloat az, ALfloat *mu)
 {
-    az = (F_TAU + az) * azcount / F_TAU;
-    azidx[0] = fastf2u(az) % azcount;
-    azidx[1] = (azidx[0] + 1) % azcount;
-    *azmu = az - floorf(az);
+    ALsizei idx;
+    az = (F_TAU+az) * azcount / F_TAU;
+
+    idx = fastf2i(az) % azcount;
+    *mu = az - floorf(az);
+    return idx;
 }
 
-/* Calculates static HRIR coefficients and delays for the given polar
- * elevation and azimuth in radians.  Linear interpolation is used to
- * increase the apparent resolution of the HRIR data set.  The coefficients
- * are also normalized and attenuated by the specified gain.
+/* Calculates static HRIR coefficients and delays for the given polar elevation
+ * and azimuth in radians. The coefficients are normalized.
  */
-void GetLerpedHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azimuth, ALfloat dirfact, ALfloat gain, ALfloat (*coeffs)[2], ALuint *delays)
+void GetHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azimuth, ALfloat spread, ALfloat (*coeffs)[2], ALsizei *delays)
 {
-    ALuint evidx[2], lidx[4], ridx[4];
-    ALfloat mu[3], blend[4];
-    ALuint i;
-
-    /* Claculate elevation indices and interpolation factor. */
-    CalcEvIndices(Hrtf->evCount, elevation, evidx, &mu[2]);
-
-    for(i = 0;i < 2;i++)
+    ALsizei evidx, azidx, idx[4];
+    ALsizei evoffset;
+    ALfloat emu, amu[2];
+    ALfloat blend[4];
+    ALfloat dirfact;
+    ALsizei i, c;
+
+    dirfact = 1.0f - (spread / F_TAU);
+
+    /* Claculate the lower elevation index. */
+    evidx = CalcEvIndex(Hrtf->evCount, elevation, &emu);
+    evoffset = Hrtf->evOffset[evidx];
+
+    /* Calculate lower azimuth index. */
+    azidx= CalcAzIndex(Hrtf->azCount[evidx], azimuth, &amu[0]);
+
+    /* Calculate the lower HRIR indices. */
+    idx[0] = evoffset + azidx;
+    idx[1] = evoffset + ((azidx+1) % Hrtf->azCount[evidx]);
+    if(evidx < Hrtf->evCount-1)
     {
-        ALuint azcount = Hrtf->azCount[evidx[i]];
-        ALuint evoffset = Hrtf->evOffset[evidx[i]];
-        ALuint azidx[2];
+        /* Increment elevation to the next (upper) index. */
+        evidx++;
+        evoffset = Hrtf->evOffset[evidx];
 
-        /* Calculate azimuth indices and interpolation factor for this elevation. */
-        CalcAzIndices(azcount, azimuth, azidx, &mu[i]);
+        /* Calculate upper azimuth index. */
+        azidx = CalcAzIndex(Hrtf->azCount[evidx], azimuth, &amu[1]);
 
-        /* Calculate a set of linear HRIR indices for left and right channels. */
-        lidx[i*2 + 0] = evoffset + azidx[0];
-        lidx[i*2 + 1] = evoffset + azidx[1];
-        ridx[i*2 + 0] = evoffset + ((azcount-azidx[0]) % azcount);
-        ridx[i*2 + 1] = evoffset + ((azcount-azidx[1]) % azcount);
+        /* Calculate the upper HRIR indices. */
+        idx[2] = evoffset + azidx;
+        idx[3] = evoffset + ((azidx+1) % Hrtf->azCount[evidx]);
+    }
+    else
+    {
+        /* If the lower elevation is the top index, the upper elevation is the
+         * same as the lower.
+         */
+        amu[1] = amu[0];
+        idx[2] = idx[0];
+        idx[3] = idx[1];
     }
 
-    /* Calculate 4 blending weights for 2D bilinear interpolation. */
-    blend[0] = (1.0f-mu[0]) * (1.0f-mu[2]);
-    blend[1] = (     mu[0]) * (1.0f-mu[2]);
-    blend[2] = (1.0f-mu[1]) * (     mu[2]);
-    blend[3] = (     mu[1]) * (     mu[2]);
-
-    /* Calculate the HRIR delays using linear interpolation. */
-    delays[0] = fastf2u((Hrtf->delays[lidx[0]]*blend[0] + Hrtf->delays[lidx[1]]*blend[1] +
-                         Hrtf->delays[lidx[2]]*blend[2] + Hrtf->delays[lidx[3]]*blend[3]) *
-                        dirfact + 0.5f) << HRTFDELAY_BITS;
-    delays[1] = fastf2u((Hrtf->delays[ridx[0]]*blend[0] + Hrtf->delays[ridx[1]]*blend[1] +
-                         Hrtf->delays[ridx[2]]*blend[2] + Hrtf->delays[ridx[3]]*blend[3]) *
-                        dirfact + 0.5f) << HRTFDELAY_BITS;
+    /* Calculate bilinear blending weights, attenuated according to the
+     * directional panning factor.
+     */
+    blend[0] = (1.0f-emu) * (1.0f-amu[0]) * dirfact;
+    blend[1] = (1.0f-emu) * (     amu[0]) * dirfact;
+    blend[2] = (     emu) * (1.0f-amu[1]) * dirfact;
+    blend[3] = (     emu) * (     amu[1]) * dirfact;
+
+    /* 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
+    );
+    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
+    );
 
     /* Calculate the sample offsets for the HRIR indices. */
-    lidx[0] *= Hrtf->irSize;
-    lidx[1] *= Hrtf->irSize;
-    lidx[2] *= Hrtf->irSize;
-    lidx[3] *= Hrtf->irSize;
-    ridx[0] *= Hrtf->irSize;
-    ridx[1] *= Hrtf->irSize;
-    ridx[2] *= Hrtf->irSize;
-    ridx[3] *= Hrtf->irSize;
-
-    /* Calculate the normalized and attenuated HRIR coefficients using linear
-     * interpolation when there is enough gain to warrant it.  Zero the
-     * coefficients if gain is too low.
-     */
-    if(gain > 0.0001f)
+    idx[0] *= Hrtf->irSize;
+    idx[1] *= Hrtf->irSize;
+    idx[2] *= Hrtf->irSize;
+    idx[3] *= Hrtf->irSize;
+
+    /* Calculate the blended HRIR coefficients. */
+    coeffs[0][0] = PassthruCoeff * (1.0f-dirfact);
+    coeffs[0][1] = PassthruCoeff * (1.0f-dirfact);
+    for(i = 1;i < Hrtf->irSize;i++)
     {
-        ALfloat c;
-
-        i = 0;
-        c = (Hrtf->coeffs[lidx[0]+i]*blend[0] + Hrtf->coeffs[lidx[1]+i]*blend[1] +
-             Hrtf->coeffs[lidx[2]+i]*blend[2] + Hrtf->coeffs[lidx[3]+i]*blend[3]);
-        coeffs[i][0] = lerp(PassthruCoeff, c, dirfact) * gain * (1.0f/32767.0f);
-        c = (Hrtf->coeffs[ridx[0]+i]*blend[0] + Hrtf->coeffs[ridx[1]+i]*blend[1] +
-             Hrtf->coeffs[ridx[2]+i]*blend[2] + Hrtf->coeffs[ridx[3]+i]*blend[3]);
-        coeffs[i][1] = lerp(PassthruCoeff, c, dirfact) * gain * (1.0f/32767.0f);
-
-        for(i = 1;i < Hrtf->irSize;i++)
-        {
-            c = (Hrtf->coeffs[lidx[0]+i]*blend[0] + Hrtf->coeffs[lidx[1]+i]*blend[1] +
-                 Hrtf->coeffs[lidx[2]+i]*blend[2] + Hrtf->coeffs[lidx[3]+i]*blend[3]);
-            coeffs[i][0] = lerp(0.0f, c, dirfact) * gain * (1.0f/32767.0f);
-            c = (Hrtf->coeffs[ridx[0]+i]*blend[0] + Hrtf->coeffs[ridx[1]+i]*blend[1] +
-                 Hrtf->coeffs[ridx[2]+i]*blend[2] + Hrtf->coeffs[ridx[3]+i]*blend[3]);
-            coeffs[i][1] = lerp(0.0f, c, dirfact) * gain * (1.0f/32767.0f);
-        }
+        coeffs[i][0] = 0.0f;
+        coeffs[i][1] = 0.0f;
     }
-    else
+    for(c = 0;c < 4;c++)
     {
         for(i = 0;i < Hrtf->irSize;i++)
         {
-            coeffs[i][0] = 0.0f;
-            coeffs[i][1] = 0.0f;
+            coeffs[i][0] += Hrtf->coeffs[idx[c]+i][0] * blend[c];
+            coeffs[i][1] += Hrtf->coeffs[idx[c]+i][1] * blend[c];
         }
     }
 }
 
-/* Calculates the moving HRIR target coefficients, target delays, and
- * stepping values for the given polar elevation and azimuth in radians.
- * Linear interpolation is used to increase the apparent resolution of the
- * HRIR data set.  The coefficients are also normalized and attenuated by the
- * specified gain.  Stepping resolution and count is determined using the
- * given delta factor between 0.0 and 1.0.
- */
-ALuint GetMovingHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azimuth, ALfloat dirfact, ALfloat gain, ALfloat delta, ALint counter, ALfloat (*coeffs)[2], ALuint *delays, ALfloat (*coeffStep)[2], ALint *delayStep)
-{
-    ALuint evidx[2], lidx[4], ridx[4];
-    ALfloat mu[3], blend[4];
-    ALfloat left, right;
-    ALfloat steps;
-    ALuint i;
-
-    /* Claculate elevation indices and interpolation factor. */
-    CalcEvIndices(Hrtf->evCount, elevation, evidx, &mu[2]);
 
-    for(i = 0;i < 2;i++)
+ALsizei BuildBFormatHrtf(const struct Hrtf *Hrtf, DirectHrtfState *state, ALsizei NumChannels, const ALfloat (*restrict AmbiPoints)[2], const ALfloat (*restrict AmbiMatrix)[2][MAX_AMBI_COEFFS], ALsizei AmbiCount)
+{
+/* Set this to 2 for dual-band HRTF processing. May require a higher quality
+ * band-splitter, or better calculation of the new IR length to deal with the
+ * tail generated by the filter.
+ */
+#define NUM_BANDS 2
+    BandSplitter splitter;
+    ALsizei idx[HRTF_AMBI_MAX_CHANNELS];
+    ALsizei min_delay = HRTF_HISTORY_LENGTH;
+    ALfloat temps[3][HRIR_LENGTH];
+    ALsizei max_length = 0;
+    ALsizei i, c, b;
+
+    for(c = 0;c < AmbiCount;c++)
     {
-        ALuint azcount = Hrtf->azCount[evidx[i]];
-        ALuint evoffset = Hrtf->evOffset[evidx[i]];
-        ALuint azidx[2];
+        ALuint evidx, azidx;
+        ALuint evoffset;
+        ALuint azcount;
 
-        /* Calculate azimuth indices and interpolation factor for this elevation. */
-        CalcAzIndices(azcount, azimuth, azidx, &mu[i]);
+        /* Calculate elevation index. */
+        evidx = (ALsizei)floorf((F_PI_2 + AmbiPoints[c][0]) *
+                                (Hrtf->evCount-1)/F_PI + 0.5f);
+        evidx = mini(evidx, Hrtf->evCount-1);
 
-        /* Calculate a set of linear HRIR indices for left and right channels. */
-        lidx[i*2 + 0] = evoffset + azidx[0];
-        lidx[i*2 + 1] = evoffset + azidx[1];
-        ridx[i*2 + 0] = evoffset + ((azcount-azidx[0]) % azcount);
-        ridx[i*2 + 1] = evoffset + ((azcount-azidx[1]) % azcount);
-    }
+        azcount = Hrtf->azCount[evidx];
+        evoffset = Hrtf->evOffset[evidx];
 
-    // Calculate the stepping parameters.
-    steps = maxf(floorf(delta*Hrtf->sampleRate + 0.5f), 1.0f);
-    delta = 1.0f / steps;
+        /* Calculate azimuth index for this elevation. */
+        azidx = (ALsizei)floorf((F_TAU+AmbiPoints[c][1]) *
+                                azcount/F_TAU + 0.5f) % azcount;
 
-    /* Calculate 4 blending weights for 2D bilinear interpolation. */
-    blend[0] = (1.0f-mu[0]) * (1.0f-mu[2]);
-    blend[1] = (     mu[0]) * (1.0f-mu[2]);
-    blend[2] = (1.0f-mu[1]) * (     mu[2]);
-    blend[3] = (     mu[1]) * (     mu[2]);
+        /* Calculate indices for left and right channels. */
+        idx[c] = evoffset + azidx;
 
-    /* Calculate the HRIR delays using linear interpolation.  Then calculate
-     * the delay stepping values using the target and previous running
-     * delays.
-     */
-    left = (ALfloat)(delays[0] - (delayStep[0] * counter));
-    right = (ALfloat)(delays[1] - (delayStep[1] * counter));
-
-    delays[0] = fastf2u((Hrtf->delays[lidx[0]]*blend[0] + Hrtf->delays[lidx[1]]*blend[1] +
-                         Hrtf->delays[lidx[2]]*blend[2] + Hrtf->delays[lidx[3]]*blend[3]) *
-                        dirfact + 0.5f) << HRTFDELAY_BITS;
-    delays[1] = fastf2u((Hrtf->delays[ridx[0]]*blend[0] + Hrtf->delays[ridx[1]]*blend[1] +
-                         Hrtf->delays[ridx[2]]*blend[2] + Hrtf->delays[ridx[3]]*blend[3]) *
-                        dirfact + 0.5f) << HRTFDELAY_BITS;
-
-    delayStep[0] = fastf2i(delta * (delays[0] - left));
-    delayStep[1] = fastf2i(delta * (delays[1] - right));
+        min_delay = mini(min_delay, mini(Hrtf->delays[idx[c]][0], Hrtf->delays[idx[c]][1]));
+    }
 
-    /* Calculate the sample offsets for the HRIR indices. */
-    lidx[0] *= Hrtf->irSize;
-    lidx[1] *= Hrtf->irSize;
-    lidx[2] *= Hrtf->irSize;
-    lidx[3] *= Hrtf->irSize;
-    ridx[0] *= Hrtf->irSize;
-    ridx[1] *= Hrtf->irSize;
-    ridx[2] *= Hrtf->irSize;
-    ridx[3] *= Hrtf->irSize;
-
-    /* Calculate the normalized and attenuated target HRIR coefficients using
-     * linear interpolation when there is enough gain to warrant it.  Zero
-     * the target coefficients if gain is too low.  Then calculate the
-     * coefficient stepping values using the target and previous running
-     * coefficients.
-     */
-    if(gain > 0.0001f)
+    memset(temps, 0, sizeof(temps));
+    bandsplit_init(&splitter, 400.0f / (ALfloat)Hrtf->sampleRate);
+    for(c = 0;c < AmbiCount;c++)
     {
-        ALfloat c;
-
-        i = 0;
-        left = coeffs[i][0] - (coeffStep[i][0] * counter);
-        right = coeffs[i][1] - (coeffStep[i][1] * counter);
+        const ALfloat (*fir)[2] = &Hrtf->coeffs[idx[c] * Hrtf->irSize];
+        ALsizei ldelay = Hrtf->delays[idx[c]][0] - min_delay;
+        ALsizei rdelay = Hrtf->delays[idx[c]][1] - min_delay;
 
-        c = (Hrtf->coeffs[lidx[0]+i]*blend[0] + Hrtf->coeffs[lidx[1]+i]*blend[1] +
-             Hrtf->coeffs[lidx[2]+i]*blend[2] + Hrtf->coeffs[lidx[3]+i]*blend[3]);
-        coeffs[i][0] = lerp(PassthruCoeff, c, dirfact) * gain * (1.0f/32767.0f);
-        c = (Hrtf->coeffs[ridx[0]+i]*blend[0] + Hrtf->coeffs[ridx[1]+i]*blend[1] +
-             Hrtf->coeffs[ridx[2]+i]*blend[2] + Hrtf->coeffs[ridx[3]+i]*blend[3]);
-        coeffs[i][1] = lerp(PassthruCoeff, c, dirfact) * gain * (1.0f/32767.0f);
+        max_length = maxi(max_length,
+            mini(maxi(ldelay, rdelay) + Hrtf->irSize, HRIR_LENGTH)
+        );
 
-        coeffStep[i][0] = delta * (coeffs[i][0] - left);
-        coeffStep[i][1] = delta * (coeffs[i][1] - right);
-
-        for(i = 1;i < Hrtf->irSize;i++)
+        if(NUM_BANDS == 1)
         {
-            left = coeffs[i][0] - (coeffStep[i][0] * counter);
-            right = coeffs[i][1] - (coeffStep[i][1] * counter);
-
-            c = (Hrtf->coeffs[lidx[0]+i]*blend[0] + Hrtf->coeffs[lidx[1]+i]*blend[1] +
-                 Hrtf->coeffs[lidx[2]+i]*blend[2] + Hrtf->coeffs[lidx[3]+i]*blend[3]);
-            coeffs[i][0] = lerp(0.0f, c, dirfact) * gain * (1.0f/32767.0f);
-            c = (Hrtf->coeffs[ridx[0]+i]*blend[0] + Hrtf->coeffs[ridx[1]+i]*blend[1] +
-                 Hrtf->coeffs[ridx[2]+i]*blend[2] + Hrtf->coeffs[ridx[3]+i]*blend[3]);
-            coeffs[i][1] = lerp(0.0f, c, dirfact) * gain * (1.0f/32767.0f);
-
-            coeffStep[i][0] = delta * (coeffs[i][0] - left);
-            coeffStep[i][1] = delta * (coeffs[i][1] - right);
+            for(i = 0;i < NumChannels;++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][0][i];
+                    state->Chan[i].Coeffs[ridx++][1] += fir[j][1] * AmbiMatrix[c][0][i];
+                    j++;
+                }
+            }
         }
-    }
-    else
-    {
-        for(i = 0;i < Hrtf->irSize;i++)
+        else
         {
-            left = coeffs[i][0] - (coeffStep[i][0] * counter);
-            right = coeffs[i][1] - (coeffStep[i][1] * counter);
+            /* Band-split left HRIR into low and high frequency responses. */
+            bandsplit_clear(&splitter);
+            for(i = 0;i < Hrtf->irSize;i++)
+                temps[2][i] = fir[i][0];
+            bandsplit_process(&splitter, temps[0], temps[1], temps[2], HRIR_LENGTH);
+
+            /* Apply left ear response with delay. */
+            for(i = 0;i < NumChannels;++i)
+            {
+                for(b = 0;b < NUM_BANDS;b++)
+                {
+                    ALsizei lidx = ldelay;
+                    ALsizei j = 0;
+                    while(lidx < HRIR_LENGTH)
+                        state->Chan[i].Coeffs[lidx++][0] += temps[b][j++] * AmbiMatrix[c][b][i];
+                }
+            }
 
-            coeffs[i][0] = 0.0f;
-            coeffs[i][1] = 0.0f;
+            /* Band-split right HRIR into low and high frequency responses. */
+            bandsplit_clear(&splitter);
+            for(i = 0;i < Hrtf->irSize;i++)
+                temps[2][i] = fir[i][1];
+            bandsplit_process(&splitter, temps[0], temps[1], temps[2], HRIR_LENGTH);
 
-            coeffStep[i][0] = delta * -left;
-            coeffStep[i][1] = delta * -right;
+            /* Apply right ear response with delay. */
+            for(i = 0;i < NumChannels;++i)
+            {
+                for(b = 0;b < NUM_BANDS;b++)
+                {
+                    ALsizei ridx = rdelay;
+                    ALsizei j = 0;
+                    while(ridx < HRIR_LENGTH)
+                        state->Chan[i].Coeffs[ridx++][1] += temps[b][j++] * AmbiMatrix[c][b][i];
+                }
+            }
         }
     }
+    /* Round up to the next IR size multiple. */
+    max_length = RoundUp(max_length, MOD_IR_SIZE);
 
-    /* The stepping count is the number of samples necessary for the HRIR to
-     * complete its transition.  The mixer will only apply stepping for this
-     * many samples.
-     */
-    return fastf2u(steps);
+    TRACE("Skipped min delay: %d, new combined length: %d\n", min_delay, max_length);
+    return max_length;
+#undef NUM_BANDS
 }
 
 
-/* Calculates HRTF coefficients for B-Format channels (only up to first-order).
- * Note that these will decode a B-Format output mix, which uses FuMa ordering
- * and scaling, not N3D!
- */
-void GetBFormatHrtfCoeffs(const struct Hrtf *Hrtf, const ALuint num_chans, ALfloat (**coeffs_list)[2], ALuint **delay_list)
+static struct Hrtf *CreateHrtfStore(ALuint rate, ALsizei irSize, ALsizei evCount, ALsizei irCount,
+                                    const ALubyte *azCount, const ALushort *evOffset,
+                                    const ALfloat (*coeffs)[2], const ALubyte (*delays)[2],
+                                    const char *filename)
 {
-    ALuint elev_idx, azi_idx;
-    ALfloat scale;
-    ALuint i, c;
-
-    assert(num_chans <= 4);
-
-    for(c = 0;c < num_chans;c++)
-    {
-        ALfloat (*coeffs)[2] = coeffs_list[c];
-        ALuint *delay = delay_list[c];
-
-        for(i = 0;i < Hrtf->irSize;i++)
-        {
-            coeffs[i][0] = 0.0f;
-            coeffs[i][1] = 0.0f;
-        }
-        delay[0] = 0;
-        delay[1] = 0;
-    }
-
-    /* NOTE: HRTF coefficients are generated by combining all the HRIRs in the
-     * dataset, with each entry scaled according to how much it contributes to
-     * the given B-Format channel based on its direction (including negative
-     * contributions!).
-     */
-    scale = 0.0f;
-    for(elev_idx = 0;elev_idx < Hrtf->evCount;elev_idx++)
+    struct Hrtf *Hrtf;
+    size_t total;
+
+    total  = sizeof(struct Hrtf);
+    total += sizeof(Hrtf->azCount[0])*evCount;
+    total  = RoundUp(total, sizeof(ALushort)); /* Align for ushort fields */
+    total += sizeof(Hrtf->evOffset[0])*evCount;
+    total  = RoundUp(total, 16); /* Align for coefficients using SIMD */
+    total += sizeof(Hrtf->coeffs[0])*irSize*irCount;
+    total += sizeof(Hrtf->delays[0])*irCount;
+
+    Hrtf = al_calloc(16, total);
+    if(Hrtf == NULL)
+        ERR("Out of memory allocating storage for %s.\n", filename);
+    else
     {
-        ALfloat elev = (ALfloat)elev_idx/(ALfloat)(Hrtf->evCount-1)*F_PI - F_PI_2;
-        ALuint evoffset = Hrtf->evOffset[elev_idx];
-        ALuint azcount = Hrtf->azCount[elev_idx];
-
-        scale += (ALfloat)azcount;
-
-        for(azi_idx = 0;azi_idx < azcount;azi_idx++)
-        {
-            ALuint lidx, ridx;
-            ALfloat ambi_coeffs[4];
-            ALfloat az, gain;
-            ALfloat x, y, z;
+        uintptr_t offset = sizeof(struct Hrtf);
+        char *base = (char*)Hrtf;
+        ALushort *_evOffset;
+        ALubyte *_azCount;
+        ALubyte (*_delays)[2];
+        ALfloat (*_coeffs)[2];
+        ALsizei i;
+
+        InitRef(&Hrtf->ref, 0);
+        Hrtf->sampleRate = rate;
+        Hrtf->irSize = irSize;
+        Hrtf->evCount = evCount;
 
-            lidx = evoffset + azi_idx;
-            ridx = evoffset + ((azcount-azi_idx) % azcount);
+        /* Set up pointers to storage following the main HRTF struct. */
+        _azCount = (ALubyte*)(base + offset); Hrtf->azCount = _azCount;
+        offset += sizeof(_azCount[0])*evCount;
 
-            az = (ALfloat)azi_idx / (ALfloat)azcount * F_TAU;
-            if(az > F_PI) az -= F_TAU;
+        offset = RoundUp(offset, sizeof(ALushort)); /* Align for ushort fields */
+        _evOffset = (ALushort*)(base + offset); Hrtf->evOffset = _evOffset;
+        offset += sizeof(_evOffset[0])*evCount;
 
-            x = cosf(-az) * cosf(elev);
-            y = sinf(-az) * cosf(elev);
-            z = sinf(elev);
+        offset = RoundUp(offset, 16); /* Align for coefficients using SIMD */
+        _coeffs = (ALfloat(*)[2])(base + offset); Hrtf->coeffs = _coeffs;
+        offset += sizeof(_coeffs[0])*irSize*irCount;
 
-            ambi_coeffs[0] = 1.414213562f;
-            ambi_coeffs[1] = x;
-            ambi_coeffs[2] = y;
-            ambi_coeffs[3] = z;
+        _delays = (ALubyte(*)[2])(base + offset); Hrtf->delays = _delays;
+        offset += sizeof(_delays[0])*irCount;
 
-            for(c = 0;c < num_chans;c++)
-            {
-                ALfloat (*coeffs)[2] = coeffs_list[c];
-                ALuint *delay = delay_list[c];
+        /* Copy input data to storage. */
+        for(i = 0;i < evCount;i++) _azCount[i] = azCount[i];
+        for(i = 0;i < evCount;i++) _evOffset[i] = evOffset[i];
+        for(i = 0;i < irSize*irCount;i++)
+        {
+            _coeffs[i][0] = coeffs[i][0];
+            _coeffs[i][1] = coeffs[i][1];
+        }
+        for(i = 0;i < irCount;i++)
+        {
+            _delays[i][0] = delays[i][0];
+            _delays[i][1] = delays[i][1];
+        }
 
-                /* NOTE: Always include the total delay average since the
-                 * channels need to have matching delays. */
-                delay[0] += Hrtf->delays[lidx];
-                delay[1] += Hrtf->delays[ridx];
+        assert(offset == total);
+    }
 
-                gain = ambi_coeffs[c];
-                if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
-                    continue;
+    return Hrtf;
+}
 
-                for(i = 0;i < Hrtf->irSize;i++)
-                {
-                    coeffs[i][0] += Hrtf->coeffs[lidx*Hrtf->irSize + i]*(1.0f/32767.0f) * gain;
-                    coeffs[i][1] += Hrtf->coeffs[ridx*Hrtf->irSize + i]*(1.0f/32767.0f) * gain;
-                }
-            }
-        }
-    }
+static ALubyte GetLE_ALubyte(const ALubyte **data, size_t *len)
+{
+    ALubyte ret = (*data)[0];
+    *data += 1; *len -= 1;
+    return ret;
+}
 
-    scale = 1.0f/scale;
+static ALshort GetLE_ALshort(const ALubyte **data, size_t *len)
+{
+    ALshort ret = (*data)[0] | ((*data)[1]<<8);
+    *data += 2; *len -= 2;
+    return ret;
+}
 
-    for(c = 0;c < num_chans;c++)
-    {
-        ALfloat (*coeffs)[2] = coeffs_list[c];
-        ALuint *delay = delay_list[c];
+static ALushort GetLE_ALushort(const ALubyte **data, size_t *len)
+{
+    ALushort ret = (*data)[0] | ((*data)[1]<<8);
+    *data += 2; *len -= 2;
+    return ret;
+}
 
-        for(i = 0;i < Hrtf->irSize;i++)
-        {
-            coeffs[i][0] *= scale;
-            coeffs[i][1] *= scale;
-        }
-        delay[0] = minu((ALuint)((ALfloat)delay[0] * scale), HRTF_HISTORY_LENGTH-1);
-        delay[0] <<= HRTFDELAY_BITS;
-        delay[1] = minu((ALuint)((ALfloat)delay[1] * scale), HRTF_HISTORY_LENGTH-1);
-        delay[1] <<= HRTFDELAY_BITS;
-    }
+static ALint GetLE_ALuint(const ALubyte **data, size_t *len)
+{
+    ALint ret = (*data)[0] | ((*data)[1]<<8) | ((*data)[2]<<16) | ((*data)[3]<<24);
+    *data += 4; *len -= 4;
+    return ret;
 }
 
+static const ALubyte *Get_ALubytePtr(const ALubyte **data, size_t *len, size_t size)
+{
+    const ALubyte *ret = *data;
+    *data += size; *len -= size;
+    return ret;
+}
 
-static struct Hrtf *LoadHrtf00(FILE *f)
+static struct Hrtf *LoadHrtf00(const ALubyte *data, size_t datalen, const char *filename)
 {
     const ALubyte maxDelay = HRTF_HISTORY_LENGTH-1;
     struct Hrtf *Hrtf = NULL;
     ALboolean failed = AL_FALSE;
-    ALuint rate = 0, irCount = 0;
+    ALuint rate = 0;
+    ALushort irCount = 0;
     ALushort irSize = 0;
     ALubyte evCount = 0;
     ALubyte *azCount = NULL;
     ALushort *evOffset = NULL;
-    ALshort *coeffs = NULL;
-    ALubyte *delays = NULL;
-    ALuint i, j;
+    ALfloat (*coeffs)[2] = NULL;
+    ALubyte (*delays)[2] = NULL;
+    ALsizei i, j;
 
-    rate  = fgetc(f);
-    rate |= fgetc(f)<<8;
-    rate |= fgetc(f)<<16;
-    rate |= fgetc(f)<<24;
+    if(datalen < 9)
+    {
+        ERR("Unexpected end of %s data (req %d, rem "SZFMT")\n", filename, 9, datalen);
+        return NULL;
+    }
 
-    irCount  = fgetc(f);
-    irCount |= fgetc(f)<<8;
+    rate = GetLE_ALuint(&data, &datalen);
 
-    irSize  = fgetc(f);
-    irSize |= fgetc(f)<<8;
+    irCount = GetLE_ALushort(&data, &datalen);
 
-    evCount = fgetc(f);
+    irSize = GetLE_ALushort(&data, &datalen);
+
+    evCount = GetLE_ALubyte(&data, &datalen);
 
     if(irSize < MIN_IR_SIZE || irSize > MAX_IR_SIZE || (irSize%MOD_IR_SIZE))
     {
@@ -459,10 +438,15 @@ static struct Hrtf *LoadHrtf00(FILE *f)
             evCount, MIN_EV_COUNT, MAX_EV_COUNT);
         failed = AL_TRUE;
     }
-
     if(failed)
         return NULL;
 
+    if(datalen < evCount*2u)
+    {
+        ERR("Unexpected end of %s data (req %d, rem "SZFMT")\n", filename, evCount*2, datalen);
+        return NULL;
+    }
+
     azCount = malloc(sizeof(azCount[0])*evCount);
     evOffset = malloc(sizeof(evOffset[0])*evCount);
     if(azCount == NULL || evOffset == NULL)
@@ -473,12 +457,10 @@ static struct Hrtf *LoadHrtf00(FILE *f)
 
     if(!failed)
     {
-        evOffset[0]  = fgetc(f);
-        evOffset[0] |= fgetc(f)<<8;
+        evOffset[0] = GetLE_ALushort(&data, &datalen);
         for(i = 1;i < evCount;i++)
         {
-            evOffset[i]  = fgetc(f);
-            evOffset[i] |= fgetc(f)<<8;
+            evOffset[i] = GetLE_ALushort(&data, &datalen);
             if(evOffset[i] <= evOffset[i-1])
             {
                 ERR("Invalid evOffset: evOffset[%d]=%d (last=%d)\n",
@@ -523,86 +505,90 @@ static struct Hrtf *LoadHrtf00(FILE *f)
 
     if(!failed)
     {
-        for(i = 0;i < irCount*irSize;i+=irSize)
+        size_t reqsize = 2*irSize*irCount + irCount;
+        if(datalen < reqsize)
+        {
+            ERR("Unexpected end of %s data (req "SZFMT", rem "SZFMT")\n",
+                filename, reqsize, datalen);
+            failed = AL_TRUE;
+        }
+    }
+
+    if(!failed)
+    {
+        for(i = 0;i < irCount;i++)
         {
             for(j = 0;j < irSize;j++)
-            {
-                ALshort coeff;
-                coeff  = fgetc(f);
-                coeff |= fgetc(f)<<8;
-                coeffs[i+j] = coeff;
-            }
+                coeffs[i*irSize + j][0] = GetLE_ALshort(&data, &datalen) / 32768.0f;
         }
+
         for(i = 0;i < irCount;i++)
         {
-            delays[i] = fgetc(f);
-            if(delays[i] > maxDelay)
+            delays[i][0] = GetLE_ALubyte(&data, &datalen);
+            if(delays[i][0] > maxDelay)
             {
-                ERR("Invalid delays[%d]: %d (%d)\n", i, delays[i], maxDelay);
+                ERR("Invalid delays[%d]: %d (%d)\n", i, delays[i][0], maxDelay);
                 failed = AL_TRUE;
             }
         }
-
-        if(feof(f))
-        {
-            ERR("Premature end of data\n");
-            failed = AL_TRUE;
-        }
     }
 
     if(!failed)
     {
-        Hrtf = malloc(sizeof(struct Hrtf));
-        if(Hrtf == NULL)
+        /* Mirror the left ear responses to the right ear. */
+        for(i = 0;i < evCount;i++)
         {
-            ERR("Out of memory.\n");
-            failed = AL_TRUE;
+            ALushort evoffset = evOffset[i];
+            ALubyte azcount = azCount[i];
+            for(j = 0;j < azcount;j++)
+            {
+                ALsizei lidx = evoffset + j;
+                ALsizei ridx = evoffset + ((azcount-j) % azcount);
+                ALsizei k;
+
+                for(k = 0;k < irSize;k++)
+                    coeffs[ridx*irSize + k][1] = coeffs[lidx*irSize + k][0];
+                delays[ridx][1] = delays[lidx][0];
+            }
         }
-    }
 
-    if(!failed)
-    {
-        Hrtf->sampleRate = rate;
-        Hrtf->irSize = irSize;
-        Hrtf->evCount = evCount;
-        Hrtf->azCount = azCount;
-        Hrtf->evOffset = evOffset;
-        Hrtf->coeffs = coeffs;
-        Hrtf->delays = delays;
-        AL_STRING_INIT(Hrtf->filename);
-        Hrtf->next = NULL;
-        return Hrtf;
+        Hrtf = CreateHrtfStore(rate, irSize, evCount, irCount, azCount,
+                               evOffset, coeffs, delays, filename);
     }
 
     free(azCount);
     free(evOffset);
     free(coeffs);
     free(delays);
-    return NULL;
+    return Hrtf;
 }
 
-
-static struct Hrtf *LoadHrtf01(FILE *f)
+static struct Hrtf *LoadHrtf01(const ALubyte *data, size_t datalen, const char *filename)
 {
     const ALubyte maxDelay = HRTF_HISTORY_LENGTH-1;
     struct Hrtf *Hrtf = NULL;
     ALboolean failed = AL_FALSE;
-    ALuint rate = 0, irCount = 0;
-    ALubyte irSize = 0, evCount = 0;
-    ALubyte *azCount = NULL;
+    ALuint rate = 0;
+    ALushort irCount = 0;
+    ALushort irSize = 0;
+    ALubyte evCount = 0;
+    const ALubyte *azCount = NULL;
     ALushort *evOffset = NULL;
-    ALshort *coeffs = NULL;
-    ALubyte *delays = NULL;
-    ALuint i, j;
+    ALfloat (*coeffs)[2] = NULL;
+    ALubyte (*delays)[2] = NULL;
+    ALsizei i, j;
+
+    if(datalen < 6)
+    {
+        ERR("Unexpected end of %s data (req %d, rem "SZFMT"\n", filename, 6, datalen);
+        return NULL;
+    }
 
-    rate  = fgetc(f);
-    rate |= fgetc(f)<<8;
-    rate |= fgetc(f)<<16;
-    rate |= fgetc(f)<<24;
+    rate = GetLE_ALuint(&data, &datalen);
 
-    irSize = fgetc(f);
+    irSize = GetLE_ALubyte(&data, &datalen);
 
-    evCount = fgetc(f);
+    evCount = GetLE_ALubyte(&data, &datalen);
 
     if(irSize < MIN_IR_SIZE || irSize > MAX_IR_SIZE || (irSize%MOD_IR_SIZE))
     {
@@ -616,11 +602,17 @@ static struct Hrtf *LoadHrtf01(FILE *f)
             evCount, MIN_EV_COUNT, MAX_EV_COUNT);
         failed = AL_TRUE;
     }
-
     if(failed)
         return NULL;
 
-    azCount = malloc(sizeof(azCount[0])*evCount);
+    if(datalen < evCount)
+    {
+        ERR("Unexpected end of %s data (req %d, rem "SZFMT"\n", filename, evCount, datalen);
+        return NULL;
+    }
+
+    azCount = Get_ALubytePtr(&data, &datalen, evCount);
+
     evOffset = malloc(sizeof(evOffset[0])*evCount);
     if(azCount == NULL || evOffset == NULL)
     {
@@ -632,7 +624,6 @@ static struct Hrtf *LoadHrtf01(FILE *f)
     {
         for(i = 0;i < evCount;i++)
         {
-            azCount[i] = fgetc(f);
             if(azCount[i] < MIN_AZ_COUNT || azCount[i] > MAX_AZ_COUNT)
             {
                 ERR("Unsupported azimuth count: azCount[%d]=%d (%d to %d)\n",
@@ -663,238 +654,501 @@ static struct Hrtf *LoadHrtf01(FILE *f)
 
     if(!failed)
     {
-        for(i = 0;i < irCount*irSize;i+=irSize)
+        size_t reqsize = 2*irSize*irCount + irCount;
+        if(datalen < reqsize)
+        {
+            ERR("Unexpected end of %s data (req "SZFMT", rem "SZFMT"\n",
+                filename, reqsize, datalen);
+            failed = AL_TRUE;
+        }
+    }
+
+    if(!failed)
+    {
+        for(i = 0;i < irCount;i++)
         {
             for(j = 0;j < irSize;j++)
-            {
-                ALshort coeff;
-                coeff  = fgetc(f);
-                coeff |= fgetc(f)<<8;
-                coeffs[i+j] = coeff;
-            }
+                coeffs[i*irSize + j][0] = GetLE_ALshort(&data, &datalen) / 32768.0f;
         }
+
         for(i = 0;i < irCount;i++)
         {
-            delays[i] = fgetc(f);
-            if(delays[i] > maxDelay)
+            delays[i][0] = GetLE_ALubyte(&data, &datalen);
+            if(delays[i][0] > maxDelay)
             {
-                ERR("Invalid delays[%d]: %d (%d)\n", i, delays[i], maxDelay);
+                ERR("Invalid delays[%d]: %d (%d)\n", i, delays[i][0], maxDelay);
                 failed = AL_TRUE;
             }
         }
-
-        if(feof(f))
-        {
-            ERR("Premature end of data\n");
-            failed = AL_TRUE;
-        }
     }
 
     if(!failed)
     {
-        Hrtf = malloc(sizeof(struct Hrtf));
-        if(Hrtf == NULL)
+        /* Mirror the left ear responses to the right ear. */
+        for(i = 0;i < evCount;i++)
         {
-            ERR("Out of memory.\n");
-            failed = AL_TRUE;
+            ALushort evoffset = evOffset[i];
+            ALubyte azcount = azCount[i];
+            for(j = 0;j < azcount;j++)
+            {
+                ALsizei lidx = evoffset + j;
+                ALsizei ridx = evoffset + ((azcount-j) % azcount);
+                ALsizei k;
+
+                for(k = 0;k < irSize;k++)
+                    coeffs[ridx*irSize + k][1] = coeffs[lidx*irSize + k][0];
+                delays[ridx][1] = delays[lidx][0];
+            }
         }
-    }
 
-    if(!failed)
-    {
-        Hrtf->sampleRate = rate;
-        Hrtf->irSize = irSize;
-        Hrtf->evCount = evCount;
-        Hrtf->azCount = azCount;
-        Hrtf->evOffset = evOffset;
-        Hrtf->coeffs = coeffs;
-        Hrtf->delays = delays;
-        AL_STRING_INIT(Hrtf->filename);
-        Hrtf->next = NULL;
-        return Hrtf;
+        Hrtf = CreateHrtfStore(rate, irSize, evCount, irCount, azCount,
+                               evOffset, coeffs, delays, filename);
     }
 
-    free(azCount);
     free(evOffset);
     free(coeffs);
     free(delays);
-    return NULL;
+    return Hrtf;
 }
 
 
-static void AddFileEntry(vector_HrtfEntry *list, al_string *filename)
+static void AddFileEntry(vector_EnumeratedHrtf *list, const_al_string filename)
 {
-    HrtfEntry entry = { AL_STRING_INIT_STATIC(), *filename, NULL };
-    HrtfEntry *iter;
+    EnumeratedHrtf entry = { AL_STRING_INIT_STATIC(), NULL };
+    struct HrtfEntry *loaded_entry;
+    const EnumeratedHrtf *iter;
     const char *name;
+    const char *ext;
     int i;
 
-    name = strrchr(al_string_get_cstr(entry.filename), '/');
-    if(!name) name = strrchr(al_string_get_cstr(entry.filename), '\\');
-    if(!name) name = al_string_get_cstr(entry.filename);
-    else ++name;
-
-    entry.hrtf = LoadedHrtfs;
-    while(entry.hrtf)
+    /* Check if this file has already been loaded globally. */
+    loaded_entry = LoadedHrtfs;
+    while(loaded_entry)
     {
-        if(al_string_cmp(entry.filename, entry.hrtf->filename) == 0)
+        if(alstr_cmp_cstr(filename, loaded_entry->filename) == 0)
+        {
+            /* 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);
+            if(iter != VECTOR_END(*list))
+            {
+                TRACE("Skipping duplicate file entry %s\n", alstr_get_cstr(filename));
+                return;
+            }
+#undef MATCH_FNAME
+
             break;
-        entry.hrtf = entry.hrtf->next;
+        }
+        loaded_entry = loaded_entry->next;
     }
 
-    if(!entry.hrtf)
+    if(!loaded_entry)
     {
-        struct Hrtf *hrtf = NULL;
-        ALchar magic[8];
-        FILE *f;
+        TRACE("Got new file \"%s\"\n", alstr_get_cstr(filename));
+
+        loaded_entry = al_calloc(DEF_ALIGN,
+            FAM_SIZE(struct HrtfEntry, filename, alstr_length(filename)+1)
+        );
+        loaded_entry->next = LoadedHrtfs;
+        loaded_entry->handle = NULL;
+        strcpy(loaded_entry->filename, alstr_get_cstr(filename));
+        LoadedHrtfs = loaded_entry;
+    }
+
+    /* TODO: Get a human-readable name from the HRTF data (possibly coming in a
+     * format update). */
+    name = strrchr(alstr_get_cstr(filename), '/');
+    if(!name) name = strrchr(alstr_get_cstr(filename), '\\');
+    if(!name) name = alstr_get_cstr(filename);
+    else ++name;
 
-        TRACE("Loading %s...\n", al_string_get_cstr(entry.filename));
-        f = al_fopen(al_string_get_cstr(entry.filename), "rb");
-        if(f == NULL)
+    ext = strrchr(name, '.');
+
+    i = 0;
+    do {
+        if(!ext)
+            alstr_copy_cstr(&entry.name, name);
+        else
+            alstr_copy_range(&entry.name, name, ext);
+        if(i != 0)
         {
-            ERR("Could not open %s\n", al_string_get_cstr(entry.filename));
-            goto error;
+            char str[64];
+            snprintf(str, sizeof(str), " #%d", i+1);
+            alstr_append_cstr(&entry.name, str);
         }
+        ++i;
 
-        if(fread(magic, 1, sizeof(magic), f) != sizeof(magic))
-            ERR("Failed to read header from %s\n", al_string_get_cstr(entry.filename));
-        else
+#define MATCH_NAME(i)  (alstr_cmp(entry.name, (i)->name) == 0)
+        VECTOR_FIND_IF(iter, const EnumeratedHrtf, *list, MATCH_NAME);
+#undef MATCH_NAME
+    } while(iter != VECTOR_END(*list));
+    entry.hrtf = loaded_entry;
+
+    TRACE("Adding entry \"%s\" from file \"%s\"\n", alstr_get_cstr(entry.name),
+          alstr_get_cstr(filename));
+    VECTOR_PUSH_BACK(*list, entry);
+}
+
+/* Unfortunate that we have to duplicate AddFileEntry to take a memory buffer
+ * for input instead of opening the given filename.
+ */
+static void AddBuiltInEntry(vector_EnumeratedHrtf *list, const_al_string filename, size_t residx)
+{
+    EnumeratedHrtf entry = { AL_STRING_INIT_STATIC(), NULL };
+    struct HrtfEntry *loaded_entry;
+    struct Hrtf *hrtf = NULL;
+    const EnumeratedHrtf *iter;
+    const char *name;
+    const char *ext;
+    int i;
+
+    loaded_entry = LoadedHrtfs;
+    while(loaded_entry)
+    {
+        if(alstr_cmp_cstr(filename, loaded_entry->filename) == 0)
         {
-            if(memcmp(magic, magicMarker00, sizeof(magicMarker00)) == 0)
+#define MATCH_ENTRY(i) (loaded_entry == (i)->hrtf)
+            VECTOR_FIND_IF(iter, const EnumeratedHrtf, *list, MATCH_ENTRY);
+            if(iter != VECTOR_END(*list))
             {
-                TRACE("Detected data set format v0\n");
-                hrtf = LoadHrtf00(f);
+                TRACE("Skipping duplicate file entry %s\n", alstr_get_cstr(filename));
+                return;
             }
-            else if(memcmp(magic, magicMarker01, sizeof(magicMarker01)) == 0)
-            {
-                TRACE("Detected data set format v1\n");
-                hrtf = LoadHrtf01(f);
-            }
-            else
-                ERR("Invalid header in %s: \"%.8s\"\n", al_string_get_cstr(entry.filename), magic);
-        }
-        fclose(f);
+#undef MATCH_FNAME
 
-        if(!hrtf)
-        {
-            ERR("Failed to load %s\n", al_string_get_cstr(entry.filename));
-            goto error;
+            break;
         }
+        loaded_entry = loaded_entry->next;
+    }
 
-        al_string_copy(&hrtf->filename, entry.filename);
-        hrtf->next = LoadedHrtfs;
-        LoadedHrtfs = hrtf;
-        TRACE("Loaded HRTF support for format: %s %uhz\n",
-                DevFmtChannelsString(DevFmtStereo), hrtf->sampleRate);
-        entry.hrtf = hrtf;
+    if(!loaded_entry)
+    {
+        size_t namelen = alstr_length(filename)+32;
+
+        TRACE("Got new file \"%s\"\n", alstr_get_cstr(filename));
+
+        loaded_entry = al_calloc(DEF_ALIGN,
+            FAM_SIZE(struct HrtfEntry, filename, namelen)
+        );
+        loaded_entry->next = LoadedHrtfs;
+        loaded_entry->handle = hrtf;
+        snprintf(loaded_entry->filename, namelen,  "!"SZFMT"_%s",
+                 residx, alstr_get_cstr(filename));
+        LoadedHrtfs = loaded_entry;
     }
 
     /* TODO: Get a human-readable name from the HRTF data (possibly coming in a
      * format update). */
+    name = strrchr(alstr_get_cstr(filename), '/');
+    if(!name) name = strrchr(alstr_get_cstr(filename), '\\');
+    if(!name) name = alstr_get_cstr(filename);
+    else ++name;
+
+    ext = strrchr(name, '.');
 
     i = 0;
     do {
-        al_string_copy_cstr(&entry.name, name);
+        if(!ext)
+            alstr_copy_cstr(&entry.name, name);
+        else
+            alstr_copy_range(&entry.name, name, ext);
         if(i != 0)
         {
             char str[64];
             snprintf(str, sizeof(str), " #%d", i+1);
-            al_string_append_cstr(&entry.name, str);
+            alstr_append_cstr(&entry.name, str);
         }
         ++i;
 
-#define MATCH_NAME(i)  (al_string_cmp(entry.name, (i)->name) == 0)
-        VECTOR_FIND_IF(iter, HrtfEntry, *list, MATCH_NAME);
+#define MATCH_NAME(i)  (alstr_cmp(entry.name, (i)->name) == 0)
+        VECTOR_FIND_IF(iter, const EnumeratedHrtf, *list, MATCH_NAME);
 #undef MATCH_NAME
-    } while(iter != VECTOR_ITER_END(*list));
+    } while(iter != VECTOR_END(*list));
+    entry.hrtf = loaded_entry;
 
-    TRACE("Adding entry \"%s\" from file \"%s\"\n", al_string_get_cstr(entry.name),
-          al_string_get_cstr(entry.filename));
+    TRACE("Adding built-in entry \"%s\"\n", alstr_get_cstr(entry.name));
     VECTOR_PUSH_BACK(*list, entry);
-    return;
+}
+
+
+#define IDR_DEFAULT_44100_MHR 1
+#define IDR_DEFAULT_48000_MHR 2
 
-error:
-    al_string_deinit(&entry.filename);
+#ifndef ALSOFT_EMBED_HRTF_DATA
+
+static const ALubyte *GetResource(int UNUSED(name), size_t *size)
+{
+    *size = 0;
+    return NULL;
 }
 
-vector_HrtfEntry EnumerateHrtf(const_al_string devname)
+#else
+
+#include "default-44100.mhr.h"
+#include "default-48000.mhr.h"
+
+static const ALubyte *GetResource(int name, size_t *size)
 {
-    vector_HrtfEntry list = VECTOR_INIT_STATIC();
-    const char *fnamelist = "%s.mhr";
+    if(name == IDR_DEFAULT_44100_MHR)
+    {
+        *size = sizeof(hrtf_default_44100);
+        return hrtf_default_44100;
+    }
+    if(name == IDR_DEFAULT_48000_MHR)
+    {
+        *size = sizeof(hrtf_default_48000);
+        return hrtf_default_48000;
+    }
+    *size = 0;
+    return NULL;
+}
+#endif
+
+vector_EnumeratedHrtf EnumerateHrtf(const_al_string devname)
+{
+    vector_EnumeratedHrtf list = VECTOR_INIT_STATIC();
+    const char *defaulthrtf = "";
+    const char *pathlist = "";
+    bool usedefaults = true;
 
-    ConfigValueStr(al_string_get_cstr(devname), NULL, "hrtf_tables", &fnamelist);
-    while(fnamelist && *fnamelist)
+    if(ConfigValueStr(alstr_get_cstr(devname), NULL, "hrtf-paths", &pathlist))
     {
-        while(isspace(*fnamelist) || *fnamelist == ',')
-            fnamelist++;
-        if(*fnamelist != '\0')
+        al_string pname = AL_STRING_INIT_STATIC();
+        while(pathlist && *pathlist)
         {
             const char *next, *end;
 
-            next = strchr(fnamelist, ',');
-            if(!next)
-                end = fnamelist + strlen(fnamelist);
-            else
+            while(isspace(*pathlist) || *pathlist == ',')
+                pathlist++;
+            if(*pathlist == '\0')
+                continue;
+
+            next = strchr(pathlist, ',');
+            if(next)
                 end = next++;
+            else
+            {
+                end = pathlist + strlen(pathlist);
+                usedefaults = false;
+            }
 
-            while(end != fnamelist && isspace(*(end-1)))
+            while(end != pathlist && isspace(*(end-1)))
                 --end;
-            if(end != fnamelist)
+            if(end != pathlist)
             {
-                al_string fname = AL_STRING_INIT_STATIC();
                 vector_al_string flist;
+                size_t i;
 
-                al_string_append_range(&fname, fnamelist, end);
+                alstr_copy_range(&pname, pathlist, end);
 
-                flist = SearchDataFiles(al_string_get_cstr(fname), "openal/hrtf");
-                VECTOR_FOR_EACH_PARAMS(al_string, flist, AddFileEntry, &list);
+                flist = SearchDataFiles(".mhr", alstr_get_cstr(pname));
+                for(i = 0;i < VECTOR_SIZE(flist);i++)
+                    AddFileEntry(&list, VECTOR_ELEM(flist, i));
+                VECTOR_FOR_EACH(al_string, flist, alstr_reset);
                 VECTOR_DEINIT(flist);
-
-                al_string_deinit(&fname);
             }
 
-            fnamelist = next;
+            pathlist = next;
+        }
+
+        alstr_reset(&pname);
+    }
+    else if(ConfigValueExists(alstr_get_cstr(devname), NULL, "hrtf_tables"))
+        ERR("The hrtf_tables option is deprecated, please use hrtf-paths instead.\n");
+
+    if(usedefaults)
+    {
+        al_string ename = AL_STRING_INIT_STATIC();
+        vector_al_string flist;
+        const ALubyte *rdata;
+        size_t rsize, i;
+
+        flist = SearchDataFiles(".mhr", "openal/hrtf");
+        for(i = 0;i < VECTOR_SIZE(flist);i++)
+            AddFileEntry(&list, VECTOR_ELEM(flist, i));
+        VECTOR_FOR_EACH(al_string, flist, alstr_reset);
+        VECTOR_DEINIT(flist);
+
+        rdata = GetResource(IDR_DEFAULT_44100_MHR, &rsize);
+        if(rdata != NULL && rsize > 0)
+        {
+            alstr_copy_cstr(&ename, "Built-In 44100hz");
+            AddBuiltInEntry(&list, ename, IDR_DEFAULT_44100_MHR);
+        }
+
+        rdata = GetResource(IDR_DEFAULT_48000_MHR, &rsize);
+        if(rdata != NULL && rsize > 0)
+        {
+            alstr_copy_cstr(&ename, "Built-In 48000hz");
+            AddBuiltInEntry(&list, ename, IDR_DEFAULT_48000_MHR);
+        }
+        alstr_reset(&ename);
+    }
+
+    if(VECTOR_SIZE(list) > 1 && ConfigValueStr(alstr_get_cstr(devname), NULL, "default-hrtf", &defaulthrtf))
+    {
+        const EnumeratedHrtf *iter;
+        /* Find the preferred HRTF and move it to the front of the list. */
+#define FIND_ENTRY(i)  (alstr_cmp_cstr((i)->name, defaulthrtf) == 0)
+        VECTOR_FIND_IF(iter, const EnumeratedHrtf, list, FIND_ENTRY);
+#undef FIND_ENTRY
+        if(iter == VECTOR_END(list))
+            WARN("Failed to find default HRTF \"%s\"\n", defaulthrtf);
+        else if(iter != VECTOR_BEGIN(list))
+        {
+            EnumeratedHrtf entry = *iter;
+            memmove(&VECTOR_ELEM(list,1), &VECTOR_ELEM(list,0),
+                    (iter-VECTOR_BEGIN(list))*sizeof(EnumeratedHrtf));
+            VECTOR_ELEM(list,0) = entry;
         }
     }
 
     return list;
 }
 
-void FreeHrtfList(vector_HrtfEntry *list)
+void FreeHrtfList(vector_EnumeratedHrtf *list)
 {
-#define CLEAR_ENTRY(i) do {           \
-    al_string_deinit(&(i)->name);     \
-    al_string_deinit(&(i)->filename); \
-} while(0)
-    VECTOR_FOR_EACH(HrtfEntry, *list, CLEAR_ENTRY);
+#define CLEAR_ENTRY(i) alstr_reset(&(i)->name)
+    VECTOR_FOR_EACH(EnumeratedHrtf, *list, CLEAR_ENTRY);
     VECTOR_DEINIT(*list);
 #undef CLEAR_ENTRY
 }
 
+struct Hrtf *GetLoadedHrtf(struct HrtfEntry *entry)
+{
+    struct Hrtf *hrtf = NULL;
+    struct FileMapping fmap;
+    const ALubyte *rdata;
+    const char *name;
+    size_t residx;
+    size_t rsize;
+    char ch;
+
+    while(ATOMIC_FLAG_TEST_AND_SET(&LoadedHrtfLock, almemory_order_seq_cst))
+        althrd_yield();
+
+    if(entry->handle)
+    {
+        hrtf = entry->handle;
+        Hrtf_IncRef(hrtf);
+        goto done;
+    }
+
+    fmap.ptr = NULL;
+    fmap.len = 0;
+    if(sscanf(entry->filename, "!"SZFMT"%c", &residx, &ch) == 2 && ch == '_')
+    {
+        name = strchr(entry->filename, ch)+1;
+
+        TRACE("Loading %s...\n", name);
+        rdata = GetResource(residx, &rsize);
+        if(rdata == NULL || rsize == 0)
+        {
+            ERR("Could not get resource "SZFMT", %s\n", residx, name);
+            goto done;
+        }
+    }
+    else
+    {
+        name = entry->filename;
+
+        TRACE("Loading %s...\n", entry->filename);
+        fmap = MapFileToMem(entry->filename);
+        if(fmap.ptr == NULL)
+        {
+            ERR("Could not open %s\n", entry->filename);
+            goto done;
+        }
+
+        rdata = fmap.ptr;
+        rsize = fmap.len;
+    }
+
+    if(rsize < sizeof(magicMarker01))
+        ERR("%s data is too short ("SZFMT" bytes)\n", name, rsize);
+    else if(memcmp(rdata, magicMarker01, sizeof(magicMarker01)) == 0)
+    {
+        TRACE("Detected data set format v1\n");
+        hrtf = LoadHrtf01(rdata+sizeof(magicMarker01),
+            rsize-sizeof(magicMarker01), name
+        );
+    }
+    else if(memcmp(rdata, magicMarker00, sizeof(magicMarker00)) == 0)
+    {
+        TRACE("Detected data set format v0\n");
+        hrtf = LoadHrtf00(rdata+sizeof(magicMarker00),
+            rsize-sizeof(magicMarker00), name
+        );
+    }
+    else
+        ERR("Invalid header in %s: \"%.8s\"\n", name, (const char*)rdata);
+    if(fmap.ptr)
+        UnmapFileMem(&fmap);
+
+    if(!hrtf)
+    {
+        ERR("Failed to load %s\n", name);
+        goto done;
+    }
+    entry->handle = hrtf;
+    Hrtf_IncRef(hrtf);
+
+    TRACE("Loaded HRTF support for format: %s %uhz\n",
+          DevFmtChannelsString(DevFmtStereo), hrtf->sampleRate);
+
+done:
+    ATOMIC_FLAG_CLEAR(&LoadedHrtfLock, almemory_order_seq_cst);
+    return hrtf;
+}
+
 
-ALuint GetHrtfSampleRate(const struct Hrtf *Hrtf)
+void Hrtf_IncRef(struct Hrtf *hrtf)
 {
-    return Hrtf->sampleRate;
+    uint ref = IncrementRef(&hrtf->ref);
+    TRACEREF("%p increasing refcount to %u\n", hrtf, ref);
 }
 
-ALuint GetHrtfIrSize(const struct Hrtf *Hrtf)
+void Hrtf_DecRef(struct Hrtf *hrtf)
 {
-    return Hrtf->irSize;
+    struct HrtfEntry *Hrtf;
+    uint ref = DecrementRef(&hrtf->ref);
+    TRACEREF("%p decreasing refcount to %u\n", hrtf, ref);
+    if(ref == 0)
+    {
+        while(ATOMIC_FLAG_TEST_AND_SET(&LoadedHrtfLock, almemory_order_seq_cst))
+            althrd_yield();
+
+        Hrtf = LoadedHrtfs;
+        while(Hrtf != NULL)
+        {
+            /* Need to double-check that it's still unused, as another device
+             * could've reacquired this HRTF after its reference went to 0 and
+             * before the lock was taken.
+             */
+            if(hrtf == Hrtf->handle && ReadRef(&hrtf->ref) == 0)
+            {
+                al_free(Hrtf->handle);
+                Hrtf->handle = NULL;
+                TRACE("Unloaded unused HRTF %s\n", Hrtf->filename);
+            }
+            Hrtf = Hrtf->next;
+        }
+
+        ATOMIC_FLAG_CLEAR(&LoadedHrtfLock, almemory_order_seq_cst);
+    }
 }
 
 
 void FreeHrtfs(void)
 {
-    struct Hrtf *Hrtf = NULL;
+    struct HrtfEntry *Hrtf = LoadedHrtfs;
+    LoadedHrtfs = NULL;
 
-    while((Hrtf=LoadedHrtfs) != NULL)
+    while(Hrtf != NULL)
     {
-        LoadedHrtfs = Hrtf->next;
-        free((void*)Hrtf->azCount);
-        free((void*)Hrtf->evOffset);
-        free((void*)Hrtf->coeffs);
-        free((void*)Hrtf->delays);
-        al_string_deinit(&Hrtf->filename);
-        free(Hrtf);
+        struct HrtfEntry *next = Hrtf->next;
+        al_free(Hrtf->handle);
+        al_free(Hrtf);
+        Hrtf = next;
     }
 }

+ 34 - 22
libs/openal-soft/Alc/hrtf.h

@@ -4,37 +4,49 @@
 #include "AL/al.h"
 #include "AL/alc.h"
 
+#include "alMain.h"
 #include "alstring.h"
+#include "atomic.h"
 
-enum DevFmtChannels;
 
-struct Hrtf;
+/* The maximum number of virtual speakers used to generate HRTF coefficients
+ * for decoding B-Format.
+ */
+#define HRTF_AMBI_MAX_CHANNELS 16
 
-typedef struct HrtfEntry {
-    al_string name;
-    al_string filename;
 
-    const struct Hrtf *hrtf;
-} HrtfEntry;
-TYPEDEF_VECTOR(HrtfEntry, vector_HrtfEntry)
+struct HrtfEntry;
 
-#define HRIR_BITS        (7)
-#define HRIR_LENGTH      (1<<HRIR_BITS)
-#define HRIR_MASK        (HRIR_LENGTH-1)
-#define HRTFDELAY_BITS    (20)
-#define HRTFDELAY_FRACONE (1<<HRTFDELAY_BITS)
-#define HRTFDELAY_MASK    (HRTFDELAY_FRACONE-1)
+struct Hrtf {
+    RefCount ref;
 
-void FreeHrtfs(void);
+    ALuint sampleRate;
+    ALsizei irSize;
+    ALubyte evCount;
+
+    const ALubyte *azCount;
+    const ALushort *evOffset;
+    const ALfloat (*coeffs)[2];
+    const ALubyte (*delays)[2];
+};
 
-vector_HrtfEntry EnumerateHrtf(const_al_string devname);
-void FreeHrtfList(vector_HrtfEntry *list);
 
-ALuint GetHrtfSampleRate(const struct Hrtf *Hrtf);
-ALuint GetHrtfIrSize(const struct Hrtf *Hrtf);
+void FreeHrtfs(void);
 
-void GetLerpedHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azimuth, ALfloat dirfact, ALfloat gain, ALfloat (*coeffs)[2], ALuint *delays);
-ALuint GetMovingHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azimuth, ALfloat dirfact, ALfloat gain, ALfloat delta, ALint counter, ALfloat (*coeffs)[2], ALuint *delays, ALfloat (*coeffStep)[2], ALint *delayStep);
-void GetBFormatHrtfCoeffs(const struct Hrtf *Hrtf, const ALuint num_chans, ALfloat (**coeffs_list)[2], ALuint **delay_list);
+vector_EnumeratedHrtf EnumerateHrtf(const_al_string devname);
+void FreeHrtfList(vector_EnumeratedHrtf *list);
+struct Hrtf *GetLoadedHrtf(struct HrtfEntry *entry);
+void Hrtf_IncRef(struct Hrtf *hrtf);
+void Hrtf_DecRef(struct Hrtf *hrtf);
+
+void GetHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azimuth, ALfloat spread, ALfloat (*coeffs)[2], ALsizei *delays);
+
+/**
+ * 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.
+ * Returns the maximum impulse-response length of the generated coefficients.
+ */
+ALsizei BuildBFormatHrtf(const struct Hrtf *Hrtf, DirectHrtfState *state, ALsizei NumChannels, const ALfloat (*restrict AmbiPoints)[2], const ALfloat (*restrict AmbiMatrix)[2][MAX_AMBI_COEFFS], ALsizei AmbiCount);
 
 #endif /* ALC_HRTF_H */

+ 255 - 0
libs/openal-soft/Alc/mastering.c

@@ -0,0 +1,255 @@
+#include "config.h"
+
+#include <math.h>
+
+#include "alu.h"
+#include "almalloc.h"
+
+#define RMS_WINDOW_SIZE (1<<7)
+#define RMS_WINDOW_MASK (RMS_WINDOW_SIZE-1)
+#define RMS_VALUE_MAX  (1<<24)
+
+#define LOOKAHEAD_SIZE (1<<13)
+#define LOOKAHEAD_MASK (LOOKAHEAD_SIZE-1)
+
+static_assert(RMS_VALUE_MAX < (UINT_MAX / RMS_WINDOW_SIZE), "RMS_VALUE_MAX is too big");
+
+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;
+
+    ALuint RmsSum;
+    ALuint *RmsWindow;
+    ALsizei RmsIndex;
+    ALfloat Envelope[BUFFERSIZE];
+    ALfloat EnvLast;
+} Compressor;
+
+/* Multichannel compression is linked via one of two modes:
+ *
+ *   Summed - Absolute sum of all channels.
+ *   Maxed  - Absolute maximum of any channel.
+ */
+static void SumChannels(Compressor *Comp, const ALsizei NumChans, const ALsizei SamplesToDo,
+                        ALfloat (*restrict OutBuffer)[BUFFERSIZE])
+{
+    ALsizei c, i;
+
+    for(i = 0;i < SamplesToDo;i++)
+        Comp->Envelope[i] = 0.0f;
+
+    for(c = 0;c < NumChans;c++)
+    {
+        for(i = 0;i < SamplesToDo;i++)
+            Comp->Envelope[i] += OutBuffer[c][i];
+    }
+
+    for(i = 0;i < SamplesToDo;i++)
+        Comp->Envelope[i] = fabsf(Comp->Envelope[i]);
+}
+
+static void MaxChannels(Compressor *Comp, const ALsizei NumChans, const ALsizei SamplesToDo,
+                        ALfloat (*restrict OutBuffer)[BUFFERSIZE])
+{
+    ALsizei c, i;
+
+    for(i = 0;i < SamplesToDo;i++)
+        Comp->Envelope[i] = 0.0f;
+
+    for(c = 0;c < NumChans;c++)
+    {
+        for(i = 0;i < SamplesToDo;i++)
+            Comp->Envelope[i] = maxf(Comp->Envelope[i], fabsf(OutBuffer[c][i]));
+    }
+}
+
+/* Envelope detection/sensing can be done via:
+ *
+ *   RMS  - Rectangular windowed root mean square of linking stage.
+ *   Peak - Implicit output from linking stage.
+ */
+static void RmsDetection(Compressor *Comp, const ALsizei SamplesToDo)
+{
+    ALuint sum = Comp->RmsSum;
+    ALuint *window = Comp->RmsWindow;
+    ALsizei index = Comp->RmsIndex;
+    ALsizei i;
+
+    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;
+
+        Comp->Envelope[i] = sqrtf(sum / 65536.0f / RMS_WINDOW_SIZE);
+    }
+
+    Comp->RmsSum = sum;
+    Comp->RmsIndex = index;
+}
+
+/* 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.
+ */
+static void FollowEnvelope(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;
+    ALsizei i;
+
+    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);
+
+        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)));
+
+        Comp->Envelope[i] = last;
+    }
+
+    Comp->EnvLast = last;
+}
+
+/* The envelope is converted to control gain with an optional soft knee. */
+static void EnvelopeGain(Compressor *Comp, const ALsizei SamplesToDo, const ALfloat Slope)
+{
+    const ALfloat threshold = Comp->Threshold;
+    const ALfloat knee = Comp->Knee;
+    ALsizei i;
+
+    if(!(knee > 0.0f))
+    {
+        for(i = 0;i < SamplesToDo;i++)
+        {
+            ALfloat gain = Slope * (threshold - Comp->Envelope[i]);
+            Comp->Envelope[i] = powf(10.0f, minf(0.0f, gain));
+        }
+    }
+    else
+    {
+        const ALfloat lower = threshold - (0.5f * knee);
+        const ALfloat upper = threshold + (0.5f * knee);
+        const ALfloat m = 0.5f * Slope / knee;
+
+        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);
+
+            Comp->Envelope[i] = powf(10.0f, minf(0.0f, gain));
+        }
+    }
+}
+
+
+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)
+{
+    Compressor *Comp;
+    size_t size;
+    ALsizei i;
+
+    size = sizeof(*Comp);
+    if(RmsSensing)
+        size += sizeof(Comp->RmsWindow[0]) * RMS_WINDOW_SIZE;
+    Comp = al_calloc(16, size);
+
+    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->SampleRate = SampleRate;
+
+    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;
+
+    return Comp;
+}
+
+ALuint GetCompressorSampleRate(const Compressor *Comp)
+{
+    return Comp->SampleRate;
+}
+
+void ApplyCompression(Compressor *Comp, const ALsizei NumChans, const ALsizei SamplesToDo,
+                      ALfloat (*restrict OutBuffer)[BUFFERSIZE])
+{
+    ALsizei c, i;
+
+    if(Comp->PreGain != 1.0f)
+    {
+        for(c = 0;c < NumChans;c++)
+        {
+            for(i = 0;i < SamplesToDo;i++)
+                OutBuffer[c][i] *= Comp->PreGain;
+        }
+    }
+
+    if(Comp->SummedLink)
+        SumChannels(Comp, NumChans, SamplesToDo, OutBuffer);
+    else
+        MaxChannels(Comp, NumChans, SamplesToDo, OutBuffer);
+
+    if(Comp->RmsWindow)
+        RmsDetection(Comp, SamplesToDo);
+    FollowEnvelope(Comp, SamplesToDo);
+
+    if(Comp->Ratio > 0.0f)
+        EnvelopeGain(Comp, SamplesToDo, 1.0f - (1.0f / Comp->Ratio));
+    else
+        EnvelopeGain(Comp, SamplesToDo, 1.0f);
+
+    if(Comp->PostGain != 1.0f)
+    {
+        for(i = 0;i < SamplesToDo;i++)
+            Comp->Envelope[i] *= Comp->PostGain;
+    }
+    for(c = 0;c < NumChans;c++)
+    {
+        for(i = 0;i < SamplesToDo;i++)
+            OutBuffer[c][i] *= Comp->Envelope[i];
+    }
+}

+ 278 - 245
libs/openal-soft/Alc/mixer.c

@@ -41,65 +41,83 @@
 static_assert((INT_MAX>>FRACTIONBITS)/MAX_PITCH > BUFFERSIZE,
               "MAX_PITCH and/or BUFFERSIZE are too large for FRACTIONBITS!");
 
-extern inline void InitiatePositionArrays(ALuint frac, ALuint increment, ALuint *frac_arr, ALuint *pos_arr, ALuint size);
+extern inline void InitiatePositionArrays(ALsizei frac, ALint increment, ALsizei *restrict frac_arr, ALint *restrict pos_arr, ALsizei size);
 
-alignas(16) union ResamplerCoeffs ResampleCoeffs;
 
+/* BSinc requires up to 11 extra samples before the current position, and 12 after. */
+static_assert(MAX_PRE_SAMPLES >= 11, "MAX_PRE_SAMPLES must be at least 11!");
+static_assert(MAX_POST_SAMPLES >= 12, "MAX_POST_SAMPLES must be at least 12!");
 
-enum Resampler {
-    PointResampler,
-    LinearResampler,
-    FIR4Resampler,
-    FIR8Resampler,
-    BSincResampler,
 
-    ResamplerDefault = LinearResampler
-};
+enum Resampler ResamplerDefault = LinearResampler;
 
-/* FIR8 requires 3 extra samples before the current position, and 4 after. */
-static_assert(MAX_PRE_SAMPLES >= 3, "MAX_PRE_SAMPLES must be at least 3!");
-static_assert(MAX_POST_SAMPLES >= 4, "MAX_POST_SAMPLES must be at least 4!");
-
-
-static HrtfMixerFunc MixHrtfSamples = MixHrtf_C;
 static MixerFunc MixSamples = Mix_C;
-static ResamplerFunc ResampleSamples = Resample_point32_C;
+static HrtfMixerFunc MixHrtfSamples = MixHrtf_C;
+HrtfMixerBlendFunc MixHrtfBlendSamples = MixHrtfBlend_C;
 
-static inline HrtfMixerFunc SelectHrtfMixer(void)
+MixerFunc SelectMixer(void)
 {
+#ifdef HAVE_NEON
+    if((CPUCapFlags&CPU_CAP_NEON))
+        return Mix_Neon;
+#endif
 #ifdef HAVE_SSE
     if((CPUCapFlags&CPU_CAP_SSE))
-        return MixHrtf_SSE;
+        return Mix_SSE;
 #endif
+    return Mix_C;
+}
+
+RowMixerFunc SelectRowMixer(void)
+{
 #ifdef HAVE_NEON
     if((CPUCapFlags&CPU_CAP_NEON))
-        return MixHrtf_Neon;
+        return MixRow_Neon;
 #endif
-
-    return MixHrtf_C;
+#ifdef HAVE_SSE
+    if((CPUCapFlags&CPU_CAP_SSE))
+        return MixRow_SSE;
+#endif
+    return MixRow_C;
 }
 
-static inline MixerFunc SelectMixer(void)
+static inline HrtfMixerFunc SelectHrtfMixer(void)
 {
+#ifdef HAVE_NEON
+    if((CPUCapFlags&CPU_CAP_NEON))
+        return MixHrtf_Neon;
+#endif
 #ifdef HAVE_SSE
     if((CPUCapFlags&CPU_CAP_SSE))
-        return Mix_SSE;
+        return MixHrtf_SSE;
 #endif
+    return MixHrtf_C;
+}
+
+static inline HrtfMixerBlendFunc SelectHrtfBlendMixer(void)
+{
 #ifdef HAVE_NEON
     if((CPUCapFlags&CPU_CAP_NEON))
-        return Mix_Neon;
+        return MixHrtfBlend_Neon;
 #endif
-
-    return Mix_C;
+#ifdef HAVE_SSE
+    if((CPUCapFlags&CPU_CAP_SSE))
+        return MixHrtfBlend_SSE;
+#endif
+    return MixHrtfBlend_C;
 }
 
-static inline ResamplerFunc SelectResampler(enum Resampler resampler)
+ResamplerFunc SelectResampler(enum Resampler resampler)
 {
     switch(resampler)
     {
         case PointResampler:
             return Resample_point32_C;
         case LinearResampler:
+#ifdef HAVE_NEON
+            if((CPUCapFlags&CPU_CAP_NEON))
+                return Resample_lerp32_Neon;
+#endif
 #ifdef HAVE_SSE4_1
             if((CPUCapFlags&CPU_CAP_SSE4_1))
                 return Resample_lerp32_SSE41;
@@ -110,6 +128,10 @@ static inline ResamplerFunc SelectResampler(enum Resampler resampler)
 #endif
             return Resample_lerp32_C;
         case FIR4Resampler:
+#ifdef HAVE_NEON
+            if((CPUCapFlags&CPU_CAP_NEON))
+                return Resample_fir4_32_Neon;
+#endif
 #ifdef HAVE_SSE4_1
             if((CPUCapFlags&CPU_CAP_SSE4_1))
                 return Resample_fir4_32_SSE41;
@@ -119,17 +141,11 @@ static inline ResamplerFunc SelectResampler(enum Resampler resampler)
                 return Resample_fir4_32_SSE3;
 #endif
             return Resample_fir4_32_C;
-        case FIR8Resampler:
-#ifdef HAVE_SSE4_1
-            if((CPUCapFlags&CPU_CAP_SSE4_1))
-                return Resample_fir8_32_SSE41;
-#endif
-#ifdef HAVE_SSE3
-            if((CPUCapFlags&CPU_CAP_SSE3))
-                return Resample_fir8_32_SSE3;
-#endif
-            return Resample_fir8_32_C;
         case BSincResampler:
+#ifdef HAVE_NEON
+            if((CPUCapFlags&CPU_CAP_NEON))
+                return Resample_bsinc32_Neon;
+#endif
 #ifdef HAVE_SSE
             if((CPUCapFlags&CPU_CAP_SSE))
                 return Resample_bsinc32_SSE;
@@ -141,162 +157,55 @@ static inline ResamplerFunc SelectResampler(enum Resampler resampler)
 }
 
 
-/* The sinc resampler makes use of a Kaiser window to limit the needed sample
- * points to 4 and 8, respectively.
- */
-
-#ifndef M_PI
-#define M_PI                         (3.14159265358979323846)
-#endif
-static inline double Sinc(double x)
-{
-    if(x == 0.0) return 1.0;
-    return sin(x*M_PI) / (x*M_PI);
-}
-
-/* The zero-order modified Bessel function of the first kind, used for the
- * Kaiser window.
- *
- *   I_0(x) = sum_{k=0}^inf (1 / k!)^2 (x / 2)^(2 k)
- *          = sum_{k=0}^inf ((x / 2)^k / k!)^2
- */
-static double BesselI_0(double x)
-{
-    double term, sum, x2, y, last_sum;
-    int k;
-
-    /* Start at k=1 since k=0 is trivial. */
-    term = 1.0;
-    sum = 1.0;
-    x2 = x / 2.0;
-    k = 1;
-
-    /* Let the integration converge until the term of the sum is no longer
-     * significant.
-     */
-    do {
-        y = x2 / k;
-        k ++;
-        last_sum = sum;
-        term *= y * y;
-        sum += term;
-    } while(sum != last_sum);
-    return sum;
-}
-
-/* Calculate a Kaiser window from the given beta value and a normalized k
- * [-1, 1].
- *
- *   w(k) = { I_0(B sqrt(1 - k^2)) / I_0(B),  -1 <= k <= 1
- *          { 0,                              elsewhere.
- *
- * Where k can be calculated as:
- *
- *   k = i / l,         where -l <= i <= l.
- *
- * or:
- *
- *   k = 2 i / M - 1,   where 0 <= i <= M.
- */
-static inline double Kaiser(double b, double k)
-{
-    if(k <= -1.0 || k >= 1.0) return 0.0;
-    return BesselI_0(b * sqrt(1.0 - (k*k))) / BesselI_0(b);
-}
-
-static inline double CalcKaiserBeta(double rejection)
-{
-    if(rejection > 50.0)
-        return 0.1102 * (rejection - 8.7);
-    if(rejection >= 21.0)
-        return (0.5842 * pow(rejection - 21.0, 0.4)) +
-               (0.07886 * (rejection - 21.0));
-    return 0.0;
-}
-
-static float SincKaiser(double r, double x)
-{
-    /* Limit rippling to -60dB. */
-    return (float)(Kaiser(CalcKaiserBeta(60.0), x / r) * Sinc(x));
-}
-
-
 void aluInitMixer(void)
 {
-    enum Resampler resampler = ResamplerDefault;
     const char *str;
-    ALuint i;
 
     if(ConfigValueStr(NULL, NULL, "resampler", &str))
     {
         if(strcasecmp(str, "point") == 0 || strcasecmp(str, "none") == 0)
-            resampler = PointResampler;
+            ResamplerDefault = PointResampler;
         else if(strcasecmp(str, "linear") == 0)
-            resampler = LinearResampler;
+            ResamplerDefault = LinearResampler;
         else if(strcasecmp(str, "sinc4") == 0)
-            resampler = FIR4Resampler;
-        else if(strcasecmp(str, "sinc8") == 0)
-            resampler = FIR8Resampler;
+            ResamplerDefault = FIR4Resampler;
         else if(strcasecmp(str, "bsinc") == 0)
-            resampler = BSincResampler;
-        else if(strcasecmp(str, "cubic") == 0)
+            ResamplerDefault = BSincResampler;
+        else if(strcasecmp(str, "cubic") == 0 || strcasecmp(str, "sinc8") == 0)
         {
-            WARN("Resampler option \"cubic\" is deprecated, using sinc4\n");
-            resampler = FIR4Resampler;
+            WARN("Resampler option \"%s\" is deprecated, using sinc4\n", str);
+            ResamplerDefault = FIR4Resampler;
         }
         else
         {
             char *end;
             long n = strtol(str, &end, 0);
             if(*end == '\0' && (n == PointResampler || n == LinearResampler || n == FIR4Resampler))
-                resampler = n;
+                ResamplerDefault = n;
             else
                 WARN("Invalid resampler: %s\n", str);
         }
     }
 
-    if(resampler == FIR8Resampler)
-        for(i = 0;i < FRACTIONONE;i++)
-        {
-            ALdouble mu = (ALdouble)i / FRACTIONONE;
-            ResampleCoeffs.FIR8[i][0] = SincKaiser(4.0, mu - -3.0);
-            ResampleCoeffs.FIR8[i][1] = SincKaiser(4.0, mu - -2.0);
-            ResampleCoeffs.FIR8[i][2] = SincKaiser(4.0, mu - -1.0);
-            ResampleCoeffs.FIR8[i][3] = SincKaiser(4.0, mu -  0.0);
-            ResampleCoeffs.FIR8[i][4] = SincKaiser(4.0, mu -  1.0);
-            ResampleCoeffs.FIR8[i][5] = SincKaiser(4.0, mu -  2.0);
-            ResampleCoeffs.FIR8[i][6] = SincKaiser(4.0, mu -  3.0);
-            ResampleCoeffs.FIR8[i][7] = SincKaiser(4.0, mu -  4.0);
-        }
-    else if(resampler == FIR4Resampler)
-        for(i = 0;i < FRACTIONONE;i++)
-        {
-            ALdouble mu = (ALdouble)i / FRACTIONONE;
-            ResampleCoeffs.FIR4[i][0] = SincKaiser(2.0, mu - -1.0);
-            ResampleCoeffs.FIR4[i][1] = SincKaiser(2.0, mu -  0.0);
-            ResampleCoeffs.FIR4[i][2] = SincKaiser(2.0, mu -  1.0);
-            ResampleCoeffs.FIR4[i][3] = SincKaiser(2.0, mu -  2.0);
-        }
-
+    MixHrtfBlendSamples = SelectHrtfBlendMixer();
     MixHrtfSamples = SelectHrtfMixer();
     MixSamples = SelectMixer();
-    ResampleSamples = SelectResampler(resampler);
 }
 
 
 static inline ALfloat Sample_ALbyte(ALbyte val)
-{ return val * (1.0f/127.0f); }
+{ return val * (1.0f/128.0f); }
 
 static inline ALfloat Sample_ALshort(ALshort val)
-{ return val * (1.0f/32767.0f); }
+{ return val * (1.0f/32768.0f); }
 
 static inline ALfloat Sample_ALfloat(ALfloat val)
 { return val; }
 
 #define DECL_TEMPLATE(T)                                                      \
-static inline void Load_##T(ALfloat *dst, const T *src, ALuint srcstep, ALuint samples)\
+static inline void Load_##T(ALfloat *dst, const T *src, ALint srcstep, ALsizei samples)\
 {                                                                             \
-    ALuint i;                                                                 \
+    ALsizei i;                                                                \
     for(i = 0;i < samples;i++)                                                \
         dst[i] = Sample_##T(src[i*srcstep]);                                  \
 }
@@ -307,7 +216,7 @@ DECL_TEMPLATE(ALfloat)
 
 #undef DECL_TEMPLATE
 
-static void LoadSamples(ALfloat *dst, const ALvoid *src, ALuint srcstep, enum FmtType srctype, ALuint samples)
+static void LoadSamples(ALfloat *dst, const ALvoid *src, ALint srcstep, enum FmtType srctype, ALsizei samples)
 {
     switch(srctype)
     {
@@ -323,9 +232,9 @@ static void LoadSamples(ALfloat *dst, const ALvoid *src, ALuint srcstep, enum Fm
     }
 }
 
-static inline void SilenceSamples(ALfloat *dst, ALuint samples)
+static inline void SilenceSamples(ALfloat *dst, ALsizei samples)
 {
-    ALuint i;
+    ALsizei i;
     for(i = 0;i < samples;i++)
         dst[i] = 0.0f;
 }
@@ -333,9 +242,9 @@ static inline void SilenceSamples(ALfloat *dst, ALuint samples)
 
 static const ALfloat *DoFilters(ALfilterState *lpfilter, ALfilterState *hpfilter,
                                 ALfloat *restrict dst, const ALfloat *restrict src,
-                                ALuint numsamples, enum ActiveFilters type)
+                                ALsizei numsamples, enum ActiveFilters type)
 {
-    ALuint i;
+    ALsizei i;
     switch(type)
     {
         case AF_None:
@@ -356,7 +265,7 @@ static const ALfloat *DoFilters(ALfilterState *lpfilter, ALfilterState *hpfilter
             for(i = 0;i < numsamples;)
             {
                 ALfloat temp[256];
-                ALuint todo = minu(256, numsamples-i);
+                ALsizei todo = mini(256, numsamples-i);
 
                 ALfilterState_process(lpfilter, temp, src+i, todo);
                 ALfilterState_process(hpfilter, dst+i, temp, todo);
@@ -368,39 +277,45 @@ static const ALfloat *DoFilters(ALfilterState *lpfilter, ALfilterState *hpfilter
 }
 
 
-ALvoid MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALuint SamplesToDo)
+ALboolean MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALsizei SamplesToDo)
 {
-    ResamplerFunc Resample;
     ALbufferlistitem *BufferListItem;
-    ALuint DataPosInt, DataPosFrac;
-    ALboolean Looping;
-    ALuint increment;
-    ALenum State;
-    ALuint OutPos;
-    ALuint NumChannels;
-    ALuint SampleSize;
+    ALbufferlistitem *BufferLoopItem;
+    ALsizei NumChannels, SampleSize;
+    ResamplerFunc Resample;
+    ALsizei DataPosInt;
+    ALsizei DataPosFrac;
     ALint64 DataSize64;
-    ALuint IrSize;
-    ALuint chan, j;
+    ALint increment;
+    ALsizei Counter;
+    ALsizei OutPos;
+    ALsizei IrSize;
+    bool isplaying;
+    bool firstpass;
+    ALsizei chan;
+    ALsizei send;
 
     /* Get source info */
-    State          = Source->state;
-    BufferListItem = ATOMIC_LOAD(&Source->current_buffer);
-    DataPosInt     = Source->position;
-    DataPosFrac    = Source->position_fraction;
-    Looping        = Source->Looping;
-    NumChannels    = Source->NumChannels;
-    SampleSize     = Source->SampleSize;
+    isplaying      = true; /* Will only be called while playing. */
+    DataPosInt     = ATOMIC_LOAD(&voice->position, almemory_order_acquire);
+    DataPosFrac    = ATOMIC_LOAD(&voice->position_fraction, almemory_order_relaxed);
+    BufferListItem = ATOMIC_LOAD(&voice->current_buffer, almemory_order_relaxed);
+    BufferLoopItem = ATOMIC_LOAD(&voice->loop_buffer, almemory_order_relaxed);
+    NumChannels    = voice->NumChannels;
+    SampleSize     = voice->SampleSize;
     increment      = voice->Step;
 
-    IrSize = (Device->Hrtf ? GetHrtfIrSize(Device->Hrtf) : 0);
+    IrSize = (Device->HrtfHandle ? Device->HrtfHandle->irSize : 0);
 
     Resample = ((increment == FRACTIONONE && DataPosFrac == 0) ?
-                Resample_copy32_C : ResampleSamples);
+                Resample_copy32_C : voice->Resampler);
 
+    Counter = (voice->Flags&VOICE_IS_FADING) ? SamplesToDo : 0;
+    firstpass = true;
     OutPos = 0;
+
     do {
-        ALuint SrcBufferSize, DstBufferSize;
+        ALsizei SrcBufferSize, DstBufferSize;
 
         /* Figure out how many buffer samples will be needed */
         DataSize64  = SamplesToDo-OutPos;
@@ -409,7 +324,7 @@ ALvoid MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALuint Sam
         DataSize64 >>= FRACTIONBITS;
         DataSize64 += MAX_POST_SAMPLES+MAX_PRE_SAMPLES;
 
-        SrcBufferSize = (ALuint)mini64(DataSize64, BUFFERSIZE);
+        SrcBufferSize = (ALsizei)mini64(DataSize64, BUFFERSIZE);
 
         /* Figure out how many samples we can actually mix from this. */
         DataSize64  = SrcBufferSize;
@@ -417,8 +332,8 @@ ALvoid MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALuint Sam
         DataSize64 <<= FRACTIONBITS;
         DataSize64 -= DataPosFrac;
 
-        DstBufferSize = (ALuint)((DataSize64+(increment-1)) / increment);
-        DstBufferSize = minu(DstBufferSize, (SamplesToDo-OutPos));
+        DstBufferSize = (ALsizei)((DataSize64+(increment-1)) / increment);
+        DstBufferSize = mini(DstBufferSize, (SamplesToDo-OutPos));
 
         /* Some mixers like having a multiple of 4, so try to give that unless
          * this is the last update. */
@@ -429,7 +344,7 @@ ALvoid MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALuint Sam
         {
             const ALfloat *ResampledData;
             ALfloat *SrcData = Device->SourceData;
-            ALuint SrcDataSize;
+            ALsizei SrcDataSize;
 
             /* Load the previous samples into the source data first. */
             memcpy(SrcData, voice->PrevSamples[chan], MAX_PRE_SAMPLES*sizeof(ALfloat));
@@ -439,23 +354,22 @@ ALvoid MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALuint Sam
             {
                 const ALbuffer *ALBuffer = BufferListItem->buffer;
                 const ALubyte *Data = ALBuffer->data;
-                ALuint DataSize;
-                ALuint pos;
+                ALsizei DataSize;
 
                 /* Offset buffer data to current channel */
                 Data += chan*SampleSize;
 
                 /* If current pos is beyond the loop range, do not loop */
-                if(Looping == AL_FALSE || DataPosInt >= (ALuint)ALBuffer->LoopEnd)
+                if(!BufferLoopItem || DataPosInt >= ALBuffer->LoopEnd)
                 {
-                    Looping = AL_FALSE;
+                    BufferLoopItem = NULL;
 
                     /* Load what's left to play from the source buffer, and
                      * clear the rest of the temp buffer */
-                    pos = DataPosInt;
-                    DataSize = minu(SrcBufferSize - SrcDataSize, ALBuffer->SampleLen - pos);
+                    DataSize = minu(SrcBufferSize - SrcDataSize,
+                                    ALBuffer->SampleLen - DataPosInt);
 
-                    LoadSamples(&SrcData[SrcDataSize], &Data[pos * NumChannels*SampleSize],
+                    LoadSamples(&SrcData[SrcDataSize], &Data[DataPosInt * NumChannels*SampleSize],
                                 NumChannels, ALBuffer->FmtType, DataSize);
                     SrcDataSize += DataSize;
 
@@ -464,23 +378,21 @@ ALvoid MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALuint Sam
                 }
                 else
                 {
-                    ALuint LoopStart = ALBuffer->LoopStart;
-                    ALuint LoopEnd   = ALBuffer->LoopEnd;
+                    ALsizei LoopStart = ALBuffer->LoopStart;
+                    ALsizei LoopEnd   = ALBuffer->LoopEnd;
 
                     /* Load what's left of this loop iteration, then load
                      * repeats of the loop section */
-                    pos = DataPosInt;
-                    DataSize = LoopEnd - pos;
-                    DataSize = minu(SrcBufferSize - SrcDataSize, DataSize);
+                    DataSize = minu(SrcBufferSize - SrcDataSize, LoopEnd - DataPosInt);
 
-                    LoadSamples(&SrcData[SrcDataSize], &Data[pos * NumChannels*SampleSize],
+                    LoadSamples(&SrcData[SrcDataSize], &Data[DataPosInt * NumChannels*SampleSize],
                                 NumChannels, ALBuffer->FmtType, DataSize);
                     SrcDataSize += DataSize;
 
                     DataSize = LoopEnd-LoopStart;
                     while(SrcBufferSize > SrcDataSize)
                     {
-                        DataSize = minu(SrcBufferSize - SrcDataSize, DataSize);
+                        DataSize = mini(SrcBufferSize - SrcDataSize, DataSize);
 
                         LoadSamples(&SrcData[SrcDataSize], &Data[LoopStart * NumChannels*SampleSize],
                                     NumChannels, ALBuffer->FmtType, DataSize);
@@ -492,7 +404,7 @@ ALvoid MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALuint Sam
             {
                 /* Crawl the buffer queue to fill in the temp buffer */
                 ALbufferlistitem *tmpiter = BufferListItem;
-                ALuint pos = DataPosInt;
+                ALsizei pos = DataPosInt;
 
                 while(tmpiter && SrcBufferSize > SrcDataSize)
                 {
@@ -500,7 +412,7 @@ ALvoid MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALuint Sam
                     if((ALBuffer=tmpiter->buffer) != NULL)
                     {
                         const ALubyte *Data = ALBuffer->data;
-                        ALuint DataSize = ALBuffer->SampleLen;
+                        ALsizei DataSize = ALBuffer->SampleLen;
 
                         /* Skip the data already played */
                         if(DataSize <= pos)
@@ -517,9 +429,9 @@ ALvoid MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALuint Sam
                             SrcDataSize += DataSize;
                         }
                     }
-                    tmpiter = tmpiter->next;
-                    if(!tmpiter && Looping)
-                        tmpiter = ATOMIC_LOAD(&Source->queue);
+                    tmpiter = ATOMIC_LOAD(&tmpiter->next, almemory_order_acquire);
+                    if(!tmpiter && BufferLoopItem)
+                        tmpiter = BufferLoopItem;
                     else if(!tmpiter)
                     {
                         SilenceSamples(&SrcData[SrcDataSize], SrcBufferSize - SrcDataSize);
@@ -535,43 +447,164 @@ ALvoid MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALuint Sam
             );
 
             /* Now resample, then filter and mix to the appropriate outputs. */
-            ResampledData = Resample(&voice->SincState,
+            ResampledData = Resample(&voice->ResampleState,
                 &SrcData[MAX_PRE_SAMPLES], DataPosFrac, increment,
                 Device->ResampledData, DstBufferSize
             );
             {
-                DirectParams *parms = &voice->Direct;
+                DirectParams *parms = &voice->Direct.Params[chan];
                 const ALfloat *samples;
 
                 samples = DoFilters(
-                    &parms->Filters[chan].LowPass, &parms->Filters[chan].HighPass,
-                    Device->FilteredData, ResampledData, DstBufferSize,
-                    parms->Filters[chan].ActiveType
+                    &parms->LowPass, &parms->HighPass, Device->FilteredData,
+                    ResampledData, DstBufferSize, voice->Direct.FilterType
                 );
-                if(!voice->IsHrtf)
-                    MixSamples(samples, parms->OutChannels, parms->OutBuffer, parms->Gains[chan],
-                               parms->Counter, OutPos, DstBufferSize);
+                if(!(voice->Flags&VOICE_HAS_HRTF))
+                {
+                    if(!Counter)
+                        memcpy(parms->Gains.Current, parms->Gains.Target,
+                               sizeof(parms->Gains.Current));
+                    if(!(voice->Flags&VOICE_HAS_NFC))
+                        MixSamples(samples, voice->Direct.Channels, voice->Direct.Buffer,
+                            parms->Gains.Current, parms->Gains.Target, Counter, OutPos,
+                            DstBufferSize
+                        );
+                    else
+                    {
+                        ALfloat *nfcsamples = Device->NFCtrlData;
+                        ALsizei chanoffset = 0;
+
+                        MixSamples(samples,
+                            voice->Direct.ChannelsPerOrder[0], voice->Direct.Buffer,
+                            parms->Gains.Current, parms->Gains.Target, Counter, OutPos,
+                            DstBufferSize
+                        );
+                        chanoffset += voice->Direct.ChannelsPerOrder[0];
+#define APPLY_NFC_MIX(order)                                                  \
+    if(voice->Direct.ChannelsPerOrder[order] > 0)                             \
+    {                                                                         \
+        NfcFilterUpdate##order(&parms->NFCtrlFilter[order-1], nfcsamples,     \
+                               samples, DstBufferSize);                       \
+        MixSamples(nfcsamples, voice->Direct.ChannelsPerOrder[order],         \
+            voice->Direct.Buffer+chanoffset, parms->Gains.Current+chanoffset, \
+            parms->Gains.Target+chanoffset, Counter, OutPos, DstBufferSize    \
+        );                                                                    \
+        chanoffset += voice->Direct.ChannelsPerOrder[order];                  \
+    }
+                        APPLY_NFC_MIX(1)
+                        APPLY_NFC_MIX(2)
+                        APPLY_NFC_MIX(3)
+#undef APPLY_NFC_MIX
+                    }
+                }
                 else
-                    MixHrtfSamples(parms->OutBuffer, samples, parms->Counter, voice->Offset,
-                                   OutPos, IrSize, &parms->Hrtf[chan].Params,
-                                   &parms->Hrtf[chan].State, DstBufferSize);
+                {
+                    MixHrtfParams hrtfparams;
+                    ALsizei fademix = 0;
+                    int lidx, ridx;
+
+                    lidx = GetChannelIdxByName(Device->RealOut, FrontLeft);
+                    ridx = GetChannelIdxByName(Device->RealOut, FrontRight);
+                    assert(lidx != -1 && ridx != -1);
+
+                    if(!Counter)
+                    {
+                        /* No fading, just overwrite the old HRTF params. */
+                        parms->Hrtf.Old = parms->Hrtf.Target;
+                    }
+                    else if(!(parms->Hrtf.Old.Gain > GAIN_SILENCE_THRESHOLD))
+                    {
+                        /* The old HRTF params are silent, so overwrite the old
+                         * coefficients with the new, and reset the old gain to
+                         * 0. The future mix will then fade from silence.
+                         */
+                        parms->Hrtf.Old = parms->Hrtf.Target;
+                        parms->Hrtf.Old.Gain = 0.0f;
+                    }
+                    else if(firstpass)
+                    {
+                        ALfloat gain;
+
+                        /* Fade between the coefficients over 128 samples. */
+                        fademix = mini(DstBufferSize, 128);
+
+                        /* The new coefficients need to fade in completely
+                         * since they're replacing the old ones. To keep the
+                         * gain fading consistent, interpolate between the old
+                         * and new target gains given how much of the fade time
+                         * this mix handles.
+                         */
+                        gain = lerp(parms->Hrtf.Old.Gain, parms->Hrtf.Target.Gain,
+                                    minf(1.0f, (ALfloat)fademix/Counter));
+                        hrtfparams.Coeffs = SAFE_CONST(ALfloat2*,parms->Hrtf.Target.Coeffs);
+                        hrtfparams.Delay[0] = parms->Hrtf.Target.Delay[0];
+                        hrtfparams.Delay[1] = parms->Hrtf.Target.Delay[1];
+                        hrtfparams.Gain = 0.0f;
+                        hrtfparams.GainStep = gain / (ALfloat)fademix;
+
+                        MixHrtfBlendSamples(
+                            voice->Direct.Buffer[lidx], voice->Direct.Buffer[ridx],
+                            samples, voice->Offset, OutPos, IrSize, &parms->Hrtf.Old,
+                            &hrtfparams, &parms->Hrtf.State, fademix
+                        );
+                        /* Update the old parameters with the result. */
+                        parms->Hrtf.Old = parms->Hrtf.Target;
+                        if(fademix < Counter)
+                            parms->Hrtf.Old.Gain = hrtfparams.Gain;
+                    }
+
+                    if(fademix < DstBufferSize)
+                    {
+                        ALsizei todo = DstBufferSize - fademix;
+                        ALfloat gain = parms->Hrtf.Target.Gain;
+
+                        /* Interpolate the target gain if the gain fading lasts
+                         * longer than this mix.
+                         */
+                        if(Counter > DstBufferSize)
+                            gain = lerp(parms->Hrtf.Old.Gain, gain,
+                                        (ALfloat)todo/(Counter-fademix));
+
+                        hrtfparams.Coeffs = SAFE_CONST(ALfloat2*,parms->Hrtf.Target.Coeffs);
+                        hrtfparams.Delay[0] = parms->Hrtf.Target.Delay[0];
+                        hrtfparams.Delay[1] = parms->Hrtf.Target.Delay[1];
+                        hrtfparams.Gain = parms->Hrtf.Old.Gain;
+                        hrtfparams.GainStep = (gain - parms->Hrtf.Old.Gain) / (ALfloat)todo;
+                        MixHrtfSamples(
+                            voice->Direct.Buffer[lidx], voice->Direct.Buffer[ridx],
+                            samples+fademix, voice->Offset+fademix, OutPos+fademix, IrSize,
+                            &hrtfparams, &parms->Hrtf.State, todo
+                        );
+                        /* Store the interpolated gain or the final target gain
+                         * depending if the fade is done.
+                         */
+                        if(DstBufferSize < Counter)
+                            parms->Hrtf.Old.Gain = gain;
+                        else
+                            parms->Hrtf.Old.Gain = parms->Hrtf.Target.Gain;
+                    }
+                }
             }
 
-            for(j = 0;j < Device->NumAuxSends;j++)
+            for(send = 0;send < Device->NumAuxSends;send++)
             {
-                SendParams *parms = &voice->Send[j];
+                SendParams *parms = &voice->Send[send].Params[chan];
                 const ALfloat *samples;
 
-                if(!parms->OutBuffer)
+                if(!voice->Send[send].Buffer)
                     continue;
 
                 samples = DoFilters(
-                    &parms->Filters[chan].LowPass, &parms->Filters[chan].HighPass,
-                    Device->FilteredData, ResampledData, DstBufferSize,
-                    parms->Filters[chan].ActiveType
+                    &parms->LowPass, &parms->HighPass, Device->FilteredData,
+                    ResampledData, DstBufferSize, voice->Send[send].FilterType
+                );
+
+                if(!Counter)
+                    memcpy(parms->Gains.Current, parms->Gains.Target,
+                           sizeof(parms->Gains.Current));
+                MixSamples(samples, voice->Send[send].Channels, voice->Send[send].Buffer,
+                    parms->Gains.Current, parms->Gains.Target, Counter, OutPos, DstBufferSize
                 );
-                MixSamples(samples, 1, parms->OutBuffer, &parms->Gains[chan],
-                           parms->Counter, OutPos, DstBufferSize);
             }
         }
         /* Update positions */
@@ -581,17 +614,16 @@ ALvoid MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALuint Sam
 
         OutPos += DstBufferSize;
         voice->Offset += DstBufferSize;
-        voice->Direct.Counter = maxu(voice->Direct.Counter, DstBufferSize) - DstBufferSize;
-        for(j = 0;j < Device->NumAuxSends;j++)
-            voice->Send[j].Counter = maxu(voice->Send[j].Counter, DstBufferSize) - DstBufferSize;
+        Counter = maxi(DstBufferSize, Counter) - DstBufferSize;
+        firstpass = false;
 
         /* Handle looping sources */
         while(1)
         {
             const ALbuffer *ALBuffer;
-            ALuint DataSize = 0;
-            ALuint LoopStart = 0;
-            ALuint LoopEnd = 0;
+            ALsizei DataSize = 0;
+            ALsizei LoopStart = 0;
+            ALsizei LoopEnd = 0;
 
             if((ALBuffer=BufferListItem->buffer) != NULL)
             {
@@ -602,7 +634,7 @@ ALvoid MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALuint Sam
                     break;
             }
 
-            if(Looping && Source->SourceType == AL_STATIC)
+            if(BufferLoopItem && Source->SourceType == AL_STATIC)
             {
                 assert(LoopEnd > LoopStart);
                 DataPosInt = ((DataPosInt-LoopStart)%(LoopEnd-LoopStart)) + LoopStart;
@@ -612,14 +644,13 @@ ALvoid MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALuint Sam
             if(DataSize > DataPosInt)
                 break;
 
-            if(!(BufferListItem=BufferListItem->next))
+            BufferListItem = ATOMIC_LOAD(&BufferListItem->next, almemory_order_acquire);
+            if(!BufferListItem)
             {
-                if(Looping)
-                    BufferListItem = ATOMIC_LOAD(&Source->queue);
-                else
+                BufferListItem = BufferLoopItem;
+                if(!BufferListItem)
                 {
-                    State = AL_STOPPED;
-                    BufferListItem = NULL;
+                    isplaying = false;
                     DataPosInt = 0;
                     DataPosFrac = 0;
                     break;
@@ -628,11 +659,13 @@ ALvoid MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALuint Sam
 
             DataPosInt -= DataSize;
         }
-    } while(State == AL_PLAYING && OutPos < SamplesToDo);
+    } while(isplaying && OutPos < SamplesToDo);
+
+    voice->Flags |= VOICE_IS_FADING;
 
     /* Update source info */
-    Source->state             = State;
-    ATOMIC_STORE(&Source->current_buffer, BufferListItem);
-    Source->position          = DataPosInt;
-    Source->position_fraction = DataPosFrac;
+    ATOMIC_STORE(&voice->position,          DataPosInt, almemory_order_relaxed);
+    ATOMIC_STORE(&voice->position_fraction, DataPosFrac, almemory_order_relaxed);
+    ATOMIC_STORE(&voice->current_buffer,    BufferListItem, almemory_order_release);
+    return isplaying;
 }

+ 99 - 72
libs/openal-soft/Alc/mixer_c.c

@@ -8,18 +8,17 @@
 #include "alAuxEffectSlot.h"
 
 
-static inline ALfloat point32(const ALfloat *vals, ALuint UNUSED(frac))
+static inline ALfloat point32(const ALfloat *restrict vals, ALsizei UNUSED(frac))
 { return vals[0]; }
-static inline ALfloat lerp32(const ALfloat *vals, ALuint frac)
+static inline ALfloat lerp32(const ALfloat *restrict vals, ALsizei frac)
 { return lerp(vals[0], vals[1], frac * (1.0f/FRACTIONONE)); }
-static inline ALfloat fir4_32(const ALfloat *vals, ALuint frac)
+static inline ALfloat fir4_32(const ALfloat *restrict vals, ALsizei frac)
 { return resample_fir4(vals[-1], vals[0], vals[1], vals[2], frac); }
-static inline ALfloat fir8_32(const ALfloat *vals, ALuint frac)
-{ return resample_fir8(vals[-3], vals[-2], vals[-1], vals[0], vals[1], vals[2], vals[3], vals[4], frac); }
 
 
-const ALfloat *Resample_copy32_C(const BsincState* UNUSED(state), const ALfloat *src, ALuint UNUSED(frac),
-  ALuint UNUSED(increment), ALfloat *restrict dst, ALuint numsamples)
+const ALfloat *Resample_copy32_C(const InterpState* UNUSED(state),
+  const ALfloat *restrict src, ALsizei UNUSED(frac), ALint UNUSED(increment),
+  ALfloat *restrict dst, ALsizei numsamples)
 {
 #if defined(HAVE_SSE) || defined(HAVE_NEON)
     /* Avoid copying the source data if it's aligned like the destination. */
@@ -31,11 +30,11 @@ const ALfloat *Resample_copy32_C(const BsincState* UNUSED(state), const ALfloat
 }
 
 #define DECL_TEMPLATE(Sampler)                                                \
-const ALfloat *Resample_##Sampler##_C(const BsincState* UNUSED(state),        \
-  const ALfloat *src, ALuint frac, ALuint increment,                          \
-  ALfloat *restrict dst, ALuint numsamples)                                   \
+const ALfloat *Resample_##Sampler##_C(const InterpState* UNUSED(state),       \
+  const ALfloat *restrict src, ALsizei frac, ALint increment,                 \
+  ALfloat *restrict dst, ALsizei numsamples)                                  \
 {                                                                             \
-    ALuint i;                                                                 \
+    ALsizei i;                                                                \
     for(i = 0;i < numsamples;i++)                                             \
     {                                                                         \
         dst[i] = Sampler(src, frac);                                          \
@@ -50,21 +49,20 @@ const ALfloat *Resample_##Sampler##_C(const BsincState* UNUSED(state),        \
 DECL_TEMPLATE(point32)
 DECL_TEMPLATE(lerp32)
 DECL_TEMPLATE(fir4_32)
-DECL_TEMPLATE(fir8_32)
 
 #undef DECL_TEMPLATE
 
-const ALfloat *Resample_bsinc32_C(const BsincState *state, const ALfloat *src, ALuint frac,
-                                  ALuint increment, ALfloat *restrict dst, ALuint dstlen)
+const ALfloat *Resample_bsinc32_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 sf = state->sf;
-    const ALuint m = state->m;
-    const ALint l = state->l;
-    ALuint j_f, pi, i;
+    const ALfloat sf = state->bsinc.sf;
+    const ALsizei m = state->bsinc.m;
+    ALsizei j_f, pi, i;
     ALfloat pf, r;
-    ALint j_s;
 
+    src += state->bsinc.l;
     for(i = 0;i < dstlen;i++)
     {
         // Calculate the phase index and factor.
@@ -73,16 +71,15 @@ const ALfloat *Resample_bsinc32_C(const BsincState *state, const ALfloat *src, A
         pf = (frac & ((1<<FRAC_PHASE_BITDIFF)-1)) * (1.0f/(1<<FRAC_PHASE_BITDIFF));
 #undef FRAC_PHASE_BITDIFF
 
-        fil = state->coeffs[pi].filter;
-        scd = state->coeffs[pi].scDelta;
-        phd = state->coeffs[pi].phDelta;
-        spd = state->coeffs[pi].spDelta;
+        fil = ASSUME_ALIGNED(state->bsinc.coeffs[pi].filter, 16);
+        scd = ASSUME_ALIGNED(state->bsinc.coeffs[pi].scDelta, 16);
+        phd = ASSUME_ALIGNED(state->bsinc.coeffs[pi].phDelta, 16);
+        spd = ASSUME_ALIGNED(state->bsinc.coeffs[pi].spDelta, 16);
 
         // Apply the scale and phase interpolated filter.
         r = 0.0f;
-        for(j_f = 0,j_s = l;j_f < m;j_f++,j_s++)
-            r += (fil[j_f] + sf*scd[j_f] + pf*(phd[j_f] + sf*spd[j_f])) *
-                    src[j_s];
+        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;
@@ -93,84 +90,93 @@ const ALfloat *Resample_bsinc32_C(const BsincState *state, const ALfloat *src, A
 }
 
 
-void ALfilterState_processC(ALfilterState *filter, ALfloat *restrict dst, const ALfloat *src, ALuint numsamples)
+void ALfilterState_processC(ALfilterState *filter, ALfloat *restrict dst, const ALfloat *restrict src, ALsizei numsamples)
 {
-    ALuint i;
-    for(i = 0;i < numsamples;i++)
-        *(dst++) = ALfilterState_processSingle(filter, *(src++));
-}
-
-
-static inline void SetupCoeffs(ALfloat (*restrict OutCoeffs)[2],
-                               const HrtfParams *hrtfparams,
-                               ALuint IrSize, ALuint Counter)
-{
-    ALuint c;
-    for(c = 0;c < IrSize;c++)
+    ALsizei i;
+    if(numsamples > 1)
     {
-        OutCoeffs[c][0] = hrtfparams->Coeffs[c][0] - (hrtfparams->CoeffStep[c][0]*Counter);
-        OutCoeffs[c][1] = hrtfparams->Coeffs[c][1] - (hrtfparams->CoeffStep[c][1]*Counter);
+        dst[0] = filter->b0 * src[0] +
+                 filter->b1 * filter->x[0] +
+                 filter->b2 * filter->x[1] -
+                 filter->a1 * filter->y[0] -
+                 filter->a2 * filter->y[1];
+        dst[1] = filter->b0 * src[1] +
+                 filter->b1 * src[0] +
+                 filter->b2 * filter->x[0] -
+                 filter->a1 * dst[0] -
+                 filter->a2 * filter->y[0];
+        for(i = 2;i < numsamples;i++)
+            dst[i] = filter->b0 * src[i] +
+                     filter->b1 * src[i-1] +
+                     filter->b2 * src[i-2] -
+                     filter->a1 * dst[i-1] -
+                     filter->a2 * dst[i-2];
+        filter->x[0] = src[i-1];
+        filter->x[1] = src[i-2];
+        filter->y[0] = dst[i-1];
+        filter->y[1] = dst[i-2];
     }
-}
-
-static inline void ApplyCoeffsStep(ALuint Offset, ALfloat (*restrict Values)[2],
-                                   const ALuint IrSize,
-                                   ALfloat (*restrict Coeffs)[2],
-                                   const ALfloat (*restrict CoeffStep)[2],
-                                   ALfloat left, ALfloat right)
-{
-    ALuint c;
-    for(c = 0;c < IrSize;c++)
+    else if(numsamples == 1)
     {
-        const ALuint off = (Offset+c)&HRIR_MASK;
-        Values[off][0] += Coeffs[c][0] * left;
-        Values[off][1] += Coeffs[c][1] * right;
-        Coeffs[c][0] += CoeffStep[c][0];
-        Coeffs[c][1] += CoeffStep[c][1];
+        dst[0] = filter->b0 * src[0] +
+                 filter->b1 * filter->x[0] +
+                 filter->b2 * filter->x[1] -
+                 filter->a1 * filter->y[0] -
+                 filter->a2 * filter->y[1];
+        filter->x[1] = filter->x[0];
+        filter->x[0] = src[0];
+        filter->y[1] = filter->y[0];
+        filter->y[0] = dst[0];
     }
 }
 
-static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2],
-                               const ALuint IrSize,
-                               ALfloat (*restrict Coeffs)[2],
+
+static inline void ApplyCoeffs(ALsizei Offset, ALfloat (*restrict Values)[2],
+                               const ALsizei IrSize,
+                               const ALfloat (*restrict Coeffs)[2],
                                ALfloat left, ALfloat right)
 {
-    ALuint c;
+    ALsizei c;
     for(c = 0;c < IrSize;c++)
     {
-        const ALuint off = (Offset+c)&HRIR_MASK;
+        const ALsizei off = (Offset+c)&HRIR_MASK;
         Values[off][0] += Coeffs[c][0] * left;
         Values[off][1] += Coeffs[c][1] * right;
     }
 }
 
 #define MixHrtf MixHrtf_C
+#define MixHrtfBlend MixHrtfBlend_C
+#define MixDirectHrtf MixDirectHrtf_C
 #include "mixer_inc.c"
 #undef MixHrtf
 
 
-void Mix_C(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
-           MixGains *Gains, ALuint Counter, ALuint OutPos, ALuint BufferSize)
+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, step;
-    ALuint c;
+    ALfloat gain, delta, step;
+    ALsizei c;
+
+    delta = (Counter > 0) ? 1.0f/(ALfloat)Counter : 0.0f;
 
     for(c = 0;c < OutChans;c++)
     {
-        ALuint pos = 0;
-        gain = Gains[c].Current;
-        step = Gains[c].Step;
-        if(step != 0.0f && Counter > 0)
+        ALsizei pos = 0;
+        gain = CurrentGains[c];
+        step = (TargetGains[c] - gain) * delta;
+        if(fabsf(step) > FLT_EPSILON)
         {
-            ALuint minsize = minu(BufferSize, Counter);
+            ALsizei minsize = mini(BufferSize, Counter);
             for(;pos < minsize;pos++)
             {
                 OutBuffer[c][OutPos+pos] += data[pos]*gain;
                 gain += step;
             }
             if(pos == Counter)
-                gain = Gains[c].Target;
-            Gains[c].Current = gain;
+                gain = TargetGains[c];
+            CurrentGains[c] = gain;
         }
 
         if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
@@ -179,3 +185,24 @@ void Mix_C(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[B
             OutBuffer[c][OutPos+pos] += data[pos]*gain;
     }
 }
+
+/* Basically the inverse of the above. Rather than one input going to multiple
+ * outputs (each with its own gain), it's multiple inputs (each with its own
+ * gain) going to one output. This applies one row (vs one column) of a matrix
+ * transform. And as the matrices are more or less static once set up, no
+ * stepping is necessary.
+ */
+void MixRow_C(ALfloat *OutBuffer, const ALfloat *Gains, const ALfloat (*restrict data)[BUFFERSIZE], ALsizei InChans, ALsizei InPos, ALsizei BufferSize)
+{
+    ALsizei c, i;
+
+    for(c = 0;c < InChans;c++)
+    {
+        ALfloat gain = Gains[c];
+        if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
+            continue;
+
+        for(i = 0;i < BufferSize;i++)
+            OutBuffer[i] += data[c][InPos+i] * gain;
+    }
+}

+ 92 - 43
libs/openal-soft/Alc/mixer_defs.h

@@ -8,73 +8,122 @@
 
 struct MixGains;
 
-struct HrtfParams;
+struct MixHrtfParams;
 struct HrtfState;
 
 /* C resamplers */
-const ALfloat *Resample_copy32_C(const BsincState *state, const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen);
-const ALfloat *Resample_point32_C(const BsincState *state, const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen);
-const ALfloat *Resample_lerp32_C(const BsincState *state, const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen);
-const ALfloat *Resample_fir4_32_C(const BsincState *state, const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen);
-const ALfloat *Resample_fir8_32_C(const BsincState *state, const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen);
-const ALfloat *Resample_bsinc32_C(const BsincState *state, const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen);
+const ALfloat *Resample_copy32_C(const InterpState *state, const ALfloat *restrict src, ALsizei frac, ALint increment, ALfloat *restrict dst, ALsizei dstlen);
+const ALfloat *Resample_point32_C(const InterpState *state, const ALfloat *restrict src, ALsizei frac, ALint increment, ALfloat *restrict dst, ALsizei dstlen);
+const ALfloat *Resample_lerp32_C(const InterpState *state, const ALfloat *restrict src, ALsizei frac, ALint increment, ALfloat *restrict dst, ALsizei dstlen);
+const ALfloat *Resample_fir4_32_C(const InterpState *state, const ALfloat *restrict src, ALsizei frac, ALint increment, ALfloat *restrict dst, ALsizei dstlen);
+const ALfloat *Resample_bsinc32_C(const InterpState *state, const ALfloat *restrict src, ALsizei frac, ALint increment, ALfloat *restrict dst, ALsizei dstlen);
 
 
 /* C mixers */
-void MixHrtf_C(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat *data,
-               ALuint Counter, ALuint Offset, ALuint OutPos, const ALuint IrSize,
-               const struct HrtfParams *hrtfparams, struct HrtfState *hrtfstate,
-               ALuint BufferSize);
-void Mix_C(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
-           struct MixGains *Gains, ALuint Counter, ALuint OutPos, ALuint BufferSize);
+void MixHrtf_C(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
+               const ALfloat *data, ALsizei Offset, ALsizei OutPos,
+               const ALsizei IrSize, struct MixHrtfParams *hrtfparams,
+               struct HrtfState *hrtfstate, ALsizei BufferSize);
+void MixHrtfBlend_C(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
+                    const ALfloat *data, ALsizei Offset, ALsizei OutPos,
+                    const ALsizei IrSize, const HrtfParams *oldparams,
+                    MixHrtfParams *newparams, HrtfState *hrtfstate,
+                    ALsizei BufferSize);
+void MixDirectHrtf_C(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
+                     const ALfloat *data, ALsizei Offset, const ALsizei IrSize,
+                     const ALfloat (*restrict Coeffs)[2], ALfloat (*restrict Values)[2],
+                     ALsizei BufferSize);
+void Mix_C(const ALfloat *data, ALsizei OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
+           ALfloat *CurrentGains, const ALfloat *TargetGains, ALsizei Counter, ALsizei OutPos,
+           ALsizei BufferSize);
+void MixRow_C(ALfloat *OutBuffer, const ALfloat *Gains,
+              const ALfloat (*restrict data)[BUFFERSIZE], ALsizei InChans,
+              ALsizei InPos, ALsizei BufferSize);
 
 /* SSE mixers */
-void MixHrtf_SSE(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat *data,
-                 ALuint Counter, ALuint Offset, ALuint OutPos, const ALuint IrSize,
-                 const struct HrtfParams *hrtfparams, struct HrtfState *hrtfstate,
-                 ALuint BufferSize);
-void Mix_SSE(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
-             struct MixGains *Gains, ALuint Counter, ALuint OutPos, ALuint BufferSize);
+void MixHrtf_SSE(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
+                 const ALfloat *data, ALsizei Offset, ALsizei OutPos,
+                 const ALsizei IrSize, struct MixHrtfParams *hrtfparams,
+                 struct HrtfState *hrtfstate, ALsizei BufferSize);
+void MixHrtfBlend_SSE(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
+                      const ALfloat *data, ALsizei Offset, ALsizei OutPos,
+                      const ALsizei IrSize, const HrtfParams *oldparams,
+                      MixHrtfParams *newparams, HrtfState *hrtfstate,
+                      ALsizei BufferSize);
+void MixDirectHrtf_SSE(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
+                       const ALfloat *data, ALsizei Offset, const ALsizei IrSize,
+                       const ALfloat (*restrict Coeffs)[2], ALfloat (*restrict Values)[2],
+                       ALsizei BufferSize);
+void Mix_SSE(const ALfloat *data, ALsizei OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
+             ALfloat *CurrentGains, const ALfloat *TargetGains, ALsizei Counter, ALsizei OutPos,
+             ALsizei BufferSize);
+void MixRow_SSE(ALfloat *OutBuffer, const ALfloat *Gains,
+                const ALfloat (*restrict data)[BUFFERSIZE], ALsizei InChans,
+                ALsizei InPos, ALsizei BufferSize);
 
 /* SSE resamplers */
-inline void InitiatePositionArrays(ALuint frac, ALuint increment, ALuint *frac_arr, ALuint *pos_arr, ALuint size)
+inline void InitiatePositionArrays(ALsizei frac, ALint increment, ALsizei *restrict frac_arr, ALint *restrict pos_arr, ALsizei size)
 {
-    ALuint i;
+    ALsizei i;
 
     pos_arr[0] = 0;
     frac_arr[0] = frac;
     for(i = 1;i < size;i++)
     {
-        ALuint frac_tmp = frac_arr[i-1] + increment;
+        ALint frac_tmp = frac_arr[i-1] + increment;
         pos_arr[i] = pos_arr[i-1] + (frac_tmp>>FRACTIONBITS);
         frac_arr[i] = frac_tmp&FRACTIONMASK;
     }
 }
 
-const ALfloat *Resample_bsinc32_SSE(const BsincState *state, const ALfloat *src, ALuint frac,
-                                    ALuint increment, ALfloat *restrict dst, ALuint dstlen);
+const ALfloat *Resample_lerp32_SSE2(const InterpState *state, const ALfloat *restrict src,
+                                    ALsizei frac, ALint increment, ALfloat *restrict dst,
+                                    ALsizei numsamples);
+const ALfloat *Resample_lerp32_SSE41(const InterpState *state, const ALfloat *restrict src,
+                                     ALsizei frac, ALint increment, ALfloat *restrict dst,
+                                     ALsizei numsamples);
 
-const ALfloat *Resample_lerp32_SSE2(const BsincState *state, const ALfloat *src, ALuint frac, ALuint increment,
-                                    ALfloat *restrict dst, ALuint numsamples);
-const ALfloat *Resample_lerp32_SSE41(const BsincState *state, const ALfloat *src, ALuint frac, ALuint increment,
-                                     ALfloat *restrict dst, ALuint numsamples);
+const ALfloat *Resample_fir4_32_SSE3(const InterpState *state, const ALfloat *restrict src,
+                                     ALsizei frac, ALint increment, ALfloat *restrict dst,
+                                     ALsizei numsamples);
+const ALfloat *Resample_fir4_32_SSE41(const InterpState *state, const ALfloat *restrict src,
+                                      ALsizei frac, ALint increment, ALfloat *restrict dst,
+                                      ALsizei numsamples);
 
-const ALfloat *Resample_fir4_32_SSE3(const BsincState *state, const ALfloat *src, ALuint frac, ALuint increment,
-                                     ALfloat *restrict dst, ALuint numsamples);
-const ALfloat *Resample_fir4_32_SSE41(const BsincState *state, const ALfloat *src, ALuint frac, ALuint increment,
-                                      ALfloat *restrict dst, ALuint numsamples);
-
-const ALfloat *Resample_fir8_32_SSE3(const BsincState *state, const ALfloat *src, ALuint frac, ALuint increment,
-                                     ALfloat *restrict dst, ALuint numsamples);
-const ALfloat *Resample_fir8_32_SSE41(const BsincState *state, const ALfloat *src, ALuint frac, ALuint increment,
-                                      ALfloat *restrict dst, ALuint numsamples);
+const ALfloat *Resample_bsinc32_SSE(const InterpState *state, const ALfloat *restrict src,
+                                    ALsizei frac, ALint increment, ALfloat *restrict dst,
+                                    ALsizei dstlen);
 
 /* Neon mixers */
-void MixHrtf_Neon(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat *data,
-                  ALuint Counter, ALuint Offset, ALuint OutPos, const ALuint IrSize,
-                  const struct HrtfParams *hrtfparams, struct HrtfState *hrtfstate,
-                  ALuint BufferSize);
-void Mix_Neon(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
-              struct MixGains *Gains, ALuint Counter, ALuint OutPos, ALuint BufferSize);
+void MixHrtf_Neon(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
+                  const ALfloat *data, ALsizei Offset, ALsizei OutPos,
+                  const ALsizei IrSize, struct MixHrtfParams *hrtfparams,
+                  struct HrtfState *hrtfstate, ALsizei BufferSize);
+void MixHrtfBlend_Neon(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
+                       const ALfloat *data, ALsizei Offset, ALsizei OutPos,
+                       const ALsizei IrSize, const HrtfParams *oldparams,
+                       MixHrtfParams *newparams, HrtfState *hrtfstate,
+                       ALsizei BufferSize);
+void MixDirectHrtf_Neon(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
+                        const ALfloat *data, ALsizei Offset, const ALsizei IrSize,
+                        const ALfloat (*restrict Coeffs)[2], ALfloat (*restrict Values)[2],
+                        ALsizei BufferSize);
+void Mix_Neon(const ALfloat *data, ALsizei OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
+              ALfloat *CurrentGains, const ALfloat *TargetGains, ALsizei Counter, ALsizei OutPos,
+              ALsizei BufferSize);
+void MixRow_Neon(ALfloat *OutBuffer, const ALfloat *Gains,
+                 const ALfloat (*restrict data)[BUFFERSIZE], ALsizei InChans,
+                 ALsizei InPos, ALsizei BufferSize);
+
+/* Neon resamplers */
+const ALfloat *Resample_lerp32_Neon(const InterpState *state, const ALfloat *restrict src,
+                                    ALsizei frac, ALint increment, ALfloat *restrict dst,
+                                    ALsizei numsamples);
+const ALfloat *Resample_fir4_32_Neon(const InterpState *state, const ALfloat *restrict src,
+                                     ALsizei frac, ALint increment, ALfloat *restrict dst,
+                                     ALsizei numsamples);
+const ALfloat *Resample_bsinc32_Neon(const InterpState *state, const ALfloat *restrict src,
+                                     ALsizei frac, ALint increment, ALfloat *restrict dst,
+                                     ALsizei dstlen);
 
 #endif /* MIXER_DEFS_H */

+ 88 - 53
libs/openal-soft/Alc/mixer_inc.c

@@ -6,74 +6,109 @@
 #include "hrtf.h"
 #include "mixer_defs.h"
 #include "align.h"
+#include "alu.h"
 
 
-static inline void SetupCoeffs(ALfloat (*restrict OutCoeffs)[2],
-                               const HrtfParams *hrtfparams,
-                               ALuint IrSize, ALuint Counter);
-static inline void ApplyCoeffsStep(ALuint Offset, ALfloat (*restrict Values)[2],
-                                   const ALuint irSize,
-                                   ALfloat (*restrict Coeffs)[2],
-                                   const ALfloat (*restrict CoeffStep)[2],
-                                   ALfloat left, ALfloat right);
-static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2],
-                               const ALuint irSize,
-                               ALfloat (*restrict Coeffs)[2],
+static inline void ApplyCoeffs(ALsizei Offset, ALfloat (*restrict Values)[2],
+                               const ALsizei irSize,
+                               const ALfloat (*restrict Coeffs)[2],
                                ALfloat left, ALfloat right);
 
 
-void MixHrtf(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat *data,
-             ALuint Counter, ALuint Offset, ALuint OutPos, const ALuint IrSize,
-             const HrtfParams *hrtfparams, HrtfState *hrtfstate, ALuint BufferSize)
+void MixHrtf(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
+             const ALfloat *data, ALsizei Offset, ALsizei OutPos,
+             const ALsizei IrSize, MixHrtfParams *hrtfparams, HrtfState *hrtfstate,
+             ALsizei BufferSize)
 {
-    alignas(16) ALfloat Coeffs[HRIR_LENGTH][2];
-    ALuint Delay[2];
+    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;
     ALfloat left, right;
-    ALuint pos;
+    ALsizei i;
 
-    SetupCoeffs(Coeffs, hrtfparams, IrSize, Counter);
-    Delay[0] = hrtfparams->Delay[0] - (hrtfparams->DelayStep[0]*Counter);
-    Delay[1] = hrtfparams->Delay[1] - (hrtfparams->DelayStep[1]*Counter);
-
-    pos = 0;
-    for(;pos < BufferSize && pos < Counter;pos++)
+    LeftOut  += OutPos;
+    RightOut += OutPos;
+    for(i = 0;i < BufferSize;i++)
     {
-        hrtfstate->History[Offset&HRTF_HISTORY_MASK] = data[pos];
-        left  = lerp(hrtfstate->History[(Offset-(Delay[0]>>HRTFDELAY_BITS))&HRTF_HISTORY_MASK],
-                     hrtfstate->History[(Offset-(Delay[0]>>HRTFDELAY_BITS)-1)&HRTF_HISTORY_MASK],
-                     (Delay[0]&HRTFDELAY_MASK)*(1.0f/HRTFDELAY_FRACONE));
-        right = lerp(hrtfstate->History[(Offset-(Delay[1]>>HRTFDELAY_BITS))&HRTF_HISTORY_MASK],
-                     hrtfstate->History[(Offset-(Delay[1]>>HRTFDELAY_BITS)-1)&HRTF_HISTORY_MASK],
-                     (Delay[1]&HRTFDELAY_MASK)*(1.0f/HRTFDELAY_FRACONE));
-
-        Delay[0] += hrtfparams->DelayStep[0];
-        Delay[1] += hrtfparams->DelayStep[1];
-
-        hrtfstate->Values[(Offset+IrSize)&HRIR_MASK][0] = 0.0f;
-        hrtfstate->Values[(Offset+IrSize)&HRIR_MASK][1] = 0.0f;
-        Offset++;
+        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;
+
+        hrtfstate->Values[(Offset+IrSize-1)&HRIR_MASK][0] = 0.0f;
+        hrtfstate->Values[(Offset+IrSize-1)&HRIR_MASK][1] = 0.0f;
 
-        ApplyCoeffsStep(Offset, hrtfstate->Values, IrSize, Coeffs, hrtfparams->CoeffStep, left, right);
-        OutBuffer[0][OutPos] += hrtfstate->Values[Offset&HRIR_MASK][0];
-        OutBuffer[1][OutPos] += hrtfstate->Values[Offset&HRIR_MASK][1];
-        OutPos++;
+        ApplyCoeffs(Offset, hrtfstate->Values, IrSize, Coeffs, left, right);
+        *(LeftOut++)  += hrtfstate->Values[Offset&HRIR_MASK][0];
+        *(RightOut++) += hrtfstate->Values[Offset&HRIR_MASK][1];
+
+        gain += gainstep;
+        Offset++;
     }
+    hrtfparams->Gain = gain;
+}
 
-    Delay[0] >>= HRTFDELAY_BITS;
-    Delay[1] >>= HRTFDELAY_BITS;
-    for(;pos < BufferSize;pos++)
+void MixHrtfBlend(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
+                  const ALfloat *data, ALsizei Offset, ALsizei OutPos,
+                  const ALsizei IrSize, const HrtfParams *oldparams,
+                  MixHrtfParams *newparams, HrtfState *hrtfstate,
+                  ALsizei BufferSize)
+{
+    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 (*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;
+    ALfloat left, right;
+    ALsizei i;
+
+    LeftOut  += OutPos;
+    RightOut += OutPos;
+    for(i = 0;i < BufferSize;i++)
     {
-        hrtfstate->History[Offset&HRTF_HISTORY_MASK] = data[pos];
-        left = hrtfstate->History[(Offset-Delay[0])&HRTF_HISTORY_MASK];
-        right = hrtfstate->History[(Offset-Delay[1])&HRTF_HISTORY_MASK];
+        hrtfstate->Values[(Offset+IrSize-1)&HRIR_MASK][0] = 0.0f;
+        hrtfstate->Values[(Offset+IrSize-1)&HRIR_MASK][1] = 0.0f;
+
+        hrtfstate->History[Offset&HRTF_HISTORY_MASK] = *(data++);
 
-        hrtfstate->Values[(Offset+IrSize)&HRIR_MASK][0] = 0.0f;
-        hrtfstate->Values[(Offset+IrSize)&HRIR_MASK][1] = 0.0f;
+        left = hrtfstate->History[(Offset-OldDelay[0])&HRTF_HISTORY_MASK]*oldGain;
+        right = hrtfstate->History[(Offset-OldDelay[1])&HRTF_HISTORY_MASK]*oldGain;
+        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;
+        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;
         Offset++;
+    }
+    newparams->Gain = newGain;
+}
 
-        ApplyCoeffs(Offset, hrtfstate->Values, IrSize, Coeffs, left, right);
-        OutBuffer[0][OutPos] += hrtfstate->Values[Offset&HRIR_MASK][0];
-        OutBuffer[1][OutPos] += hrtfstate->Values[Offset&HRIR_MASK][1];
-        OutPos++;
+void MixDirectHrtf(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
+                   const ALfloat *data, ALsizei Offset, const ALsizei IrSize,
+                   const ALfloat (*restrict Coeffs)[2], ALfloat (*restrict Values)[2],
+                   ALsizei BufferSize)
+{
+    ALfloat insample;
+    ALsizei i;
+
+    for(i = 0;i < BufferSize;i++)
+    {
+        Values[(Offset+IrSize)&HRIR_MASK][0] = 0.0f;
+        Values[(Offset+IrSize)&HRIR_MASK][1] = 0.0f;
+        Offset++;
+
+        insample = *(data++);
+        ApplyCoeffs(Offset, Values, IrSize, Coeffs, insample, insample);
+        *(LeftOut++)  += Values[Offset&HRIR_MASK][0];
+        *(RightOut++) += Values[Offset&HRIR_MASK][1];
     }
 }

+ 245 - 53
libs/openal-soft/Alc/mixer_neon.c

@@ -7,65 +7,195 @@
 #include "alMain.h"
 #include "alu.h"
 #include "hrtf.h"
+#include "mixer_defs.h"
 
 
-static inline void SetupCoeffs(ALfloat (*restrict OutCoeffs)[2],
-                               const HrtfParams *hrtfparams,
-                               ALuint IrSize, ALuint Counter)
+const ALfloat *Resample_lerp32_Neon(const InterpState* UNUSED(state),
+  const ALfloat *restrict src, ALsizei frac, ALint increment,
+  ALfloat *restrict dst, ALsizei numsamples)
 {
-    ALuint c;
-    float32x4_t counter4;
+    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;
+
+    InitiatePositionArrays(frac, increment, frac_, pos_, 4);
+
+    frac4 = vld1q_s32(frac_);
+    pos4 = vld1q_s32(pos_);
+
+    for(i = 0;numsamples-i > 3;i += 4)
     {
-        float32x2_t counter2 = vdup_n_f32(-(float)Counter);
-        counter4 = vcombine_f32(counter2, counter2);
+        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]};
+
+        /* val1 + (val2-val1)*mu */
+        const float32x4_t r0 = vsubq_f32(val2, val1);
+        const float32x4_t mu = vmulq_f32(vcvtq_f32_s32(frac4), fracOne4);
+        const float32x4_t out = vmlaq_f32(val1, mu, r0);
+
+        vst1q_f32(&dst[i], out);
+
+        frac4 = vaddq_s32(frac4, increment4);
+        pos4 = vaddq_s32(pos4, vshrq_n_s32(frac4, FRACTIONBITS));
+        frac4 = vandq_s32(frac4, fracMask4);
+
+        vst1q_s32(pos_, pos4);
     }
-    for(c = 0;c < IrSize;c += 2)
+
+    if(i < numsamples)
     {
-        float32x4_t step4 = vld1q_f32((float32_t*)hrtfparams->CoeffStep[c]);
-        float32x4_t coeffs = vld1q_f32((float32_t*)hrtfparams->Coeffs[c]);
-        coeffs = vmlaq_f32(coeffs, step4, counter4);
-        vst1q_f32((float32_t*)OutCoeffs[c], coeffs);
+        /* 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);
     }
+    return dst;
 }
 
-static inline void ApplyCoeffsStep(ALuint Offset, ALfloat (*restrict Values)[2],
-                                   const ALuint IrSize,
-                                   ALfloat (*restrict Coeffs)[2],
-                                   const ALfloat (*restrict CoeffStep)[2],
-                                   ALfloat left, ALfloat right)
+const ALfloat *Resample_fir4_32_Neon(const InterpState* UNUSED(state),
+  const ALfloat *restrict src, ALsizei frac, ALint increment,
+  ALfloat *restrict dst, ALsizei numsamples)
 {
-    ALuint c;
-    float32x4_t leftright4;
+    const int32x4_t increment4 = vdupq_n_s32(increment*4);
+    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;
+
+    InitiatePositionArrays(frac, increment, frac_, pos_, 4);
+
+    frac4 = vld1q_s32(frac_);
+    pos4 = vld1q_s32(pos_);
+
+    --src;
+    for(i = 0;numsamples-i > 3;i += 4)
     {
-        float32x2_t leftright2 = vdup_n_f32(0.0);
-        leftright2 = vset_lane_f32(left, leftright2, 0);
-        leftright2 = vset_lane_f32(right, leftright2, 1);
-        leftright4 = vcombine_f32(leftright2, leftright2);
+        const float32x4_t val0 = vld1q_f32(&src[pos_[0]]);
+        const float32x4_t val1 = vld1q_f32(&src[pos_[1]]);
+        const float32x4_t val2 = vld1q_f32(&src[pos_[2]]);
+        const float32x4_t val3 = vld1q_f32(&src[pos_[3]]);
+        float32x4_t k0 = vld1q_f32(sinc4Tab[frac_[0]]);
+        float32x4_t k1 = vld1q_f32(sinc4Tab[frac_[1]]);
+        float32x4_t k2 = vld1q_f32(sinc4Tab[frac_[2]]);
+        float32x4_t k3 = vld1q_f32(sinc4Tab[frac_[3]]);
+        float32x4_t out;
+
+        k0 = vmulq_f32(k0, val0);
+        k1 = vmulq_f32(k1, val1);
+        k2 = vmulq_f32(k2, val2);
+        k3 = vmulq_f32(k3, val3);
+        k0 = vcombine_f32(vpadd_f32(vget_low_f32(k0), vget_high_f32(k0)),
+                          vpadd_f32(vget_low_f32(k1), vget_high_f32(k1)));
+        k2 = vcombine_f32(vpadd_f32(vget_low_f32(k2), vget_high_f32(k2)),
+                          vpadd_f32(vget_low_f32(k3), vget_high_f32(k3)));
+        out = vcombine_f32(vpadd_f32(vget_low_f32(k0), vget_high_f32(k0)),
+                           vpadd_f32(vget_low_f32(k2), vget_high_f32(k2)));
+
+        vst1q_f32(&dst[i], out);
+
+        frac4 = vaddq_s32(frac4, increment4);
+        pos4 = vaddq_s32(pos4, vshrq_n_s32(frac4, FRACTIONBITS));
+        frac4 = vandq_s32(frac4, fracMask4);
+
+        vst1q_s32(pos_, pos4);
+        vst1q_s32(frac_, frac4);
     }
-    for(c = 0;c < IrSize;c += 2)
+
+    if(i < numsamples)
     {
-        const ALuint o0 = (Offset+c)&HRIR_MASK;
-        const ALuint o1 = (o0+1)&HRIR_MASK;
-        float32x4_t vals = vcombine_f32(vld1_f32((float32_t*)&Values[o0][0]),
-                                        vld1_f32((float32_t*)&Values[o1][0]));
-        float32x4_t coefs = vld1q_f32((float32_t*)&Coeffs[c][0]);
-        float32x4_t deltas = vld1q_f32(&CoeffStep[c][0]);
+        /* 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 = frac_[0];
+        do {
+            dst[i] = resample_fir4(src[pos], src[pos+1], src[pos+2], src[pos+3], frac);
 
-        vals = vmlaq_f32(vals, coefs, leftright4);
-        coefs = vaddq_f32(coefs, deltas);
+            frac += increment;
+            pos  += frac>>FRACTIONBITS;
+            frac &= FRACTIONMASK;
+        } while(++i < numsamples);
+    }
+    return dst;
+}
 
-        vst1_f32((float32_t*)&Values[o0][0], vget_low_f32(vals));
-        vst1_f32((float32_t*)&Values[o1][0], vget_high_f32(vals));
-        vst1q_f32(&Coeffs[c][0], coefs);
+const ALfloat *Resample_bsinc32_Neon(const InterpState *state,
+  const ALfloat *restrict src, ALsizei frac, ALint increment,
+  ALfloat *restrict dst, ALsizei dstlen)
+{
+    const float32x4_t sf4 = vdupq_n_f32(state->bsinc.sf);
+    const ALsizei m = state->bsinc.m;
+    const ALfloat *fil, *scd, *phd, *spd;
+    ALsizei pi, i, j;
+    float32x4_t r4;
+    ALfloat pf;
+
+    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(state->bsinc.coeffs[pi].filter, 16);
+        scd = ASSUME_ALIGNED(state->bsinc.coeffs[pi].scDelta, 16);
+        phd = ASSUME_ALIGNED(state->bsinc.coeffs[pi].phDelta, 16);
+        spd = ASSUME_ALIGNED(state->bsinc.coeffs[pi].spDelta, 16);
+
+        // Apply the scale and phase interpolated filter.
+        r4 = vdupq_n_f32(0.0f);
+        {
+            const float32x4_t pf4 = vdupq_n_f32(pf);
+            for(j = 0;j < m;j+=4)
+            {
+                /* f = ((fil + sf*scd) + pf*(phd + sf*spd)) */
+                const float32x4_t f4 = vmlaq_f32(vmlaq_f32(vld1q_f32(&fil[j]),
+                                                           sf4, vld1q_f32(&scd[j])),
+                    pf4, vmlaq_f32(vld1q_f32(&phd[j]),
+                        sf4, vld1q_f32(&spd[j])
+                    )
+                );
+                /* r += f*src */
+                r4 = vmlaq_f32(r4, f4, vld1q_f32(&src[j]));
+            }
+        }
+        r4 = vaddq_f32(r4, vcombine_f32(vrev64_f32(vget_high_f32(r4)),
+                                        vrev64_f32(vget_low_f32(r4))));
+        dst[i] = vget_lane_f32(vadd_f32(vget_low_f32(r4), vget_high_f32(r4)), 0);
+
+        frac += increment;
+        src  += frac>>FRACTIONBITS;
+        frac &= FRACTIONMASK;
     }
+    return dst;
 }
 
-static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2],
-                               const ALuint IrSize,
-                               ALfloat (*restrict Coeffs)[2],
+
+static inline void ApplyCoeffs(ALsizei Offset, ALfloat (*restrict Values)[2],
+                               const ALsizei IrSize,
+                               const ALfloat (*restrict Coeffs)[2],
                                ALfloat left, ALfloat right)
 {
-    ALuint c;
+    ALsizei c;
     float32x4_t leftright4;
     {
         float32x2_t leftright2 = vdup_n_f32(0.0);
@@ -73,10 +203,12 @@ static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2],
         leftright2 = vset_lane_f32(right, leftright2, 1);
         leftright4 = vcombine_f32(leftright2, leftright2);
     }
+    Values = ASSUME_ALIGNED(Values, 16);
+    Coeffs = ASSUME_ALIGNED(Coeffs, 16);
     for(c = 0;c < IrSize;c += 2)
     {
-        const ALuint o0 = (Offset+c)&HRIR_MASK;
-        const ALuint o1 = (o0+1)&HRIR_MASK;
+        const ALsizei o0 = (Offset+c)&HRIR_MASK;
+        const ALsizei o1 = (o0+1)&HRIR_MASK;
         float32x4_t vals = vcombine_f32(vld1_f32((float32_t*)&Values[o0][0]),
                                         vld1_f32((float32_t*)&Values[o1][0]));
         float32x4_t coefs = vld1q_f32((float32_t*)&Coeffs[c][0]);
@@ -89,36 +221,68 @@ static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2],
 }
 
 #define MixHrtf MixHrtf_Neon
+#define MixHrtfBlend MixHrtfBlend_Neon
+#define MixDirectHrtf MixDirectHrtf_Neon
 #include "mixer_inc.c"
 #undef MixHrtf
 
 
-void Mix_Neon(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
-              MixGains *Gains, ALuint Counter, ALuint OutPos, ALuint BufferSize)
+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, step;
+    ALfloat gain, delta, step;
     float32x4_t gain4;
-    ALuint c;
+    ALsizei c;
+
+    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++)
     {
-        ALuint pos = 0;
-        gain = Gains[c].Current;
-        step = Gains[c].Step;
-        if(step != 0.0f && Counter > 0)
+        ALsizei pos = 0;
+        gain = CurrentGains[c];
+        step = (TargetGains[c] - gain) * delta;
+        if(fabsf(step) > FLT_EPSILON)
         {
-            ALuint minsize = minu(BufferSize, Counter);
+            ALsizei minsize = mini(BufferSize, Counter);
+            /* Mix with applying gain steps in aligned multiples of 4. */
+            if(minsize-pos > 3)
+            {
+                float32x4_t step4;
+                gain4 = vsetq_lane_f32(gain, gain4, 0);
+                gain4 = vsetq_lane_f32(gain + step, gain4, 1);
+                gain4 = vsetq_lane_f32(gain + step + step, gain4, 2);
+                gain4 = vsetq_lane_f32(gain + step + step + step, gain4, 3);
+                step4 = vdupq_n_f32(step + step + step + step);
+                do {
+                    const float32x4_t val4 = vld1q_f32(&data[pos]);
+                    float32x4_t dry4 = vld1q_f32(&OutBuffer[c][OutPos+pos]);
+                    dry4 = vmlaq_f32(dry4, val4, gain4);
+                    gain4 = vaddq_f32(gain4, step4);
+                    vst1q_f32(&OutBuffer[c][OutPos+pos], dry4);
+                    pos += 4;
+                } while(minsize-pos > 3);
+                /* NOTE: gain4 now represents the next four gains after the
+                 * last four mixed samples, so the lowest element represents
+                 * the next gain to apply.
+                 */
+                gain = vgetq_lane_f32(gain4, 0);
+            }
+            /* Mix with applying left over gain steps that aren't aligned multiples of 4. */
             for(;pos < minsize;pos++)
             {
                 OutBuffer[c][OutPos+pos] += data[pos]*gain;
                 gain += step;
             }
             if(pos == Counter)
-                gain = Gains[c].Target;
-            Gains[c].Current = gain;
+                gain = TargetGains[c];
+            CurrentGains[c] = gain;
 
             /* Mix until pos is aligned with 4 or the mix is done. */
-            minsize = minu(BufferSize, (pos+3)&~3);
+            minsize = mini(BufferSize, (pos+3)&~3);
             for(;pos < minsize;pos++)
                 OutBuffer[c][OutPos+pos] += data[pos]*gain;
         }
@@ -137,3 +301,31 @@ void Mix_Neon(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer
             OutBuffer[c][OutPos+pos] += data[pos]*gain;
     }
 }
+
+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);
+
+    for(c = 0;c < InChans;c++)
+    {
+        ALsizei pos = 0;
+        ALfloat gain = Gains[c];
+        if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
+            continue;
+
+        gain4 = vdupq_n_f32(gain);
+        for(;BufferSize-pos > 3;pos += 4)
+        {
+            const float32x4_t val4 = vld1q_f32(&data[c][InPos+pos]);
+            float32x4_t dry4 = vld1q_f32(&OutBuffer[pos]);
+            dry4 = vmlaq_f32(dry4, val4, gain4);
+            vst1q_f32(&OutBuffer[pos], dry4);
+        }
+        for(;pos < BufferSize;pos++)
+            OutBuffer[pos] += data[c][InPos+pos]*gain;
+    }
+}

+ 75 - 124
libs/openal-soft/Alc/mixer_sse.c

@@ -12,18 +12,18 @@
 #include "mixer_defs.h"
 
 
-const ALfloat *Resample_bsinc32_SSE(const BsincState *state, const ALfloat *src, ALuint frac,
-                                    ALuint increment, ALfloat *restrict dst, ALuint dstlen)
+const ALfloat *Resample_bsinc32_SSE(const InterpState *state, const ALfloat *restrict src,
+                                    ALsizei frac, ALint increment, ALfloat *restrict dst,
+                                    ALsizei dstlen)
 {
-    const __m128 sf4 = _mm_set1_ps(state->sf);
-    const ALuint m = state->m;
-    const ALint l = state->l;
+    const __m128 sf4 = _mm_set1_ps(state->bsinc.sf);
+    const ALsizei m = state->bsinc.m;
     const ALfloat *fil, *scd, *phd, *spd;
-    ALuint pi, j_f, i;
+    ALsizei pi, i, j;
     ALfloat pf;
-    ALint j_s;
     __m128 r4;
 
+    src += state->bsinc.l;
     for(i = 0;i < dstlen;i++)
     {
         // Calculate the phase index and factor.
@@ -32,32 +32,30 @@ const ALfloat *Resample_bsinc32_SSE(const BsincState *state, const ALfloat *src,
         pf = (frac & ((1<<FRAC_PHASE_BITDIFF)-1)) * (1.0f/(1<<FRAC_PHASE_BITDIFF));
 #undef FRAC_PHASE_BITDIFF
 
-        fil = state->coeffs[pi].filter;
-        scd = state->coeffs[pi].scDelta;
-        phd = state->coeffs[pi].phDelta;
-        spd = state->coeffs[pi].spDelta;
+        fil = ASSUME_ALIGNED(state->bsinc.coeffs[pi].filter, 16);
+        scd = ASSUME_ALIGNED(state->bsinc.coeffs[pi].scDelta, 16);
+        phd = ASSUME_ALIGNED(state->bsinc.coeffs[pi].phDelta, 16);
+        spd = ASSUME_ALIGNED(state->bsinc.coeffs[pi].spDelta, 16);
 
         // Apply the scale and phase interpolated filter.
         r4 = _mm_setzero_ps();
         {
             const __m128 pf4 = _mm_set1_ps(pf);
-            for(j_f = 0,j_s = l;j_f < m;j_f+=4,j_s+=4)
+#define LD4(x) _mm_load_ps(x)
+#define ULD4(x) _mm_loadu_ps(x)
+#define MLA4(x, y, z) _mm_add_ps(x, _mm_mul_ps(y, z))
+            for(j = 0;j < m;j+=4)
             {
-                const __m128 f4 = _mm_add_ps(
-                    _mm_add_ps(
-                        _mm_load_ps(&fil[j_f]),
-                        _mm_mul_ps(sf4, _mm_load_ps(&scd[j_f]))
-                    ),
-                    _mm_mul_ps(
-                        pf4,
-                        _mm_add_ps(
-                            _mm_load_ps(&phd[j_f]),
-                            _mm_mul_ps(sf4, _mm_load_ps(&spd[j_f]))
-                        )
-                    )
+                /* f = ((fil + sf*scd) + pf*(phd + sf*spd)) */
+                const __m128 f4 = MLA4(MLA4(LD4(&fil[j]), sf4, LD4(&scd[j])),
+                    pf4, MLA4(LD4(&phd[j]), sf4, LD4(&spd[j]))
                 );
-                r4 = _mm_add_ps(r4, _mm_mul_ps(f4, _mm_loadu_ps(&src[j_s])));
+                /* r += f*src */
+                r4 = MLA4(r4, f4, ULD4(&src[j]));
             }
+#undef MLA4
+#undef ULD4
+#undef LD4
         }
         r4 = _mm_add_ps(r4, _mm_shuffle_ps(r4, r4, _MM_SHUFFLE(0, 1, 2, 3)));
         r4 = _mm_add_ps(r4, _mm_movehl_ps(r4, r4));
@@ -71,99 +69,22 @@ const ALfloat *Resample_bsinc32_SSE(const BsincState *state, const ALfloat *src,
 }
 
 
-static inline void SetupCoeffs(ALfloat (*restrict OutCoeffs)[2],
-                               const HrtfParams *hrtfparams,
-                               ALuint IrSize, ALuint Counter)
-{
-    const __m128 counter4 = _mm_set1_ps((float)Counter);
-    __m128 coeffs, step4;
-    ALuint i;
-
-    for(i = 0;i < IrSize;i += 2)
-    {
-        step4  = _mm_load_ps(&hrtfparams->CoeffStep[i][0]);
-        coeffs = _mm_load_ps(&hrtfparams->Coeffs[i][0]);
-        coeffs = _mm_sub_ps(coeffs, _mm_mul_ps(step4, counter4));
-        _mm_store_ps(&OutCoeffs[i][0], coeffs);
-    }
-}
-
-static inline void ApplyCoeffsStep(ALuint Offset, ALfloat (*restrict Values)[2],
-                                   const ALuint IrSize,
-                                   ALfloat (*restrict Coeffs)[2],
-                                   const ALfloat (*restrict CoeffStep)[2],
-                                   ALfloat left, ALfloat right)
-{
-    const __m128 lrlr = _mm_setr_ps(left, right, left, right);
-    __m128 coeffs, deltas, imp0, imp1;
-    __m128 vals = _mm_setzero_ps();
-    ALuint i;
-
-    if((Offset&1))
-    {
-        const ALuint o0 = Offset&HRIR_MASK;
-        const ALuint o1 = (Offset+IrSize-1)&HRIR_MASK;
-
-        coeffs = _mm_load_ps(&Coeffs[0][0]);
-        deltas = _mm_load_ps(&CoeffStep[0][0]);
-        vals = _mm_loadl_pi(vals, (__m64*)&Values[o0][0]);
-        imp0 = _mm_mul_ps(lrlr, coeffs);
-        coeffs = _mm_add_ps(coeffs, deltas);
-        vals = _mm_add_ps(imp0, vals);
-        _mm_store_ps(&Coeffs[0][0], coeffs);
-        _mm_storel_pi((__m64*)&Values[o0][0], vals);
-        for(i = 1;i < IrSize-1;i += 2)
-        {
-            const ALuint o2 = (Offset+i)&HRIR_MASK;
-
-            coeffs = _mm_load_ps(&Coeffs[i+1][0]);
-            deltas = _mm_load_ps(&CoeffStep[i+1][0]);
-            vals = _mm_load_ps(&Values[o2][0]);
-            imp1 = _mm_mul_ps(lrlr, coeffs);
-            coeffs = _mm_add_ps(coeffs, deltas);
-            imp0 = _mm_shuffle_ps(imp0, imp1, _MM_SHUFFLE(1, 0, 3, 2));
-            vals = _mm_add_ps(imp0, vals);
-            _mm_store_ps(&Coeffs[i+1][0], coeffs);
-            _mm_store_ps(&Values[o2][0], vals);
-            imp0 = imp1;
-        }
-        vals = _mm_loadl_pi(vals, (__m64*)&Values[o1][0]);
-        imp0 = _mm_movehl_ps(imp0, imp0);
-        vals = _mm_add_ps(imp0, vals);
-        _mm_storel_pi((__m64*)&Values[o1][0], vals);
-    }
-    else
-    {
-        for(i = 0;i < IrSize;i += 2)
-        {
-            const ALuint o = (Offset + i)&HRIR_MASK;
-
-            coeffs = _mm_load_ps(&Coeffs[i][0]);
-            deltas = _mm_load_ps(&CoeffStep[i][0]);
-            vals = _mm_load_ps(&Values[o][0]);
-            imp0 = _mm_mul_ps(lrlr, coeffs);
-            coeffs = _mm_add_ps(coeffs, deltas);
-            vals = _mm_add_ps(imp0, vals);
-            _mm_store_ps(&Coeffs[i][0], coeffs);
-            _mm_store_ps(&Values[o][0], vals);
-        }
-    }
-}
-
-static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2],
-                               const ALuint IrSize,
-                               ALfloat (*restrict Coeffs)[2],
+static inline void ApplyCoeffs(ALsizei Offset, ALfloat (*restrict Values)[2],
+                               const ALsizei IrSize,
+                               const ALfloat (*restrict Coeffs)[2],
                                ALfloat left, ALfloat right)
 {
     const __m128 lrlr = _mm_setr_ps(left, right, left, right);
     __m128 vals = _mm_setzero_ps();
     __m128 coeffs;
-    ALuint i;
+    ALsizei i;
 
+    Values = ASSUME_ALIGNED(Values, 16);
+    Coeffs = ASSUME_ALIGNED(Coeffs, 16);
     if((Offset&1))
     {
-        const ALuint o0 = Offset&HRIR_MASK;
-        const ALuint o1 = (Offset+IrSize-1)&HRIR_MASK;
+        const ALsizei o0 = Offset&HRIR_MASK;
+        const ALsizei o1 = (Offset+IrSize-1)&HRIR_MASK;
         __m128 imp0, imp1;
 
         coeffs = _mm_load_ps(&Coeffs[0][0]);
@@ -173,7 +94,7 @@ static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2],
         _mm_storel_pi((__m64*)&Values[o0][0], vals);
         for(i = 1;i < IrSize-1;i += 2)
         {
-            const ALuint o2 = (Offset+i)&HRIR_MASK;
+            const ALsizei o2 = (Offset+i)&HRIR_MASK;
 
             coeffs = _mm_load_ps(&Coeffs[i+1][0]);
             vals = _mm_load_ps(&Values[o2][0]);
@@ -192,7 +113,7 @@ static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2],
     {
         for(i = 0;i < IrSize;i += 2)
         {
-            const ALuint o = (Offset + i)&HRIR_MASK;
+            const ALsizei o = (Offset + i)&HRIR_MASK;
 
             coeffs = _mm_load_ps(&Coeffs[i][0]);
             vals = _mm_load_ps(&Values[o][0]);
@@ -203,25 +124,30 @@ static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2],
 }
 
 #define MixHrtf MixHrtf_SSE
+#define MixHrtfBlend MixHrtfBlend_SSE
+#define MixDirectHrtf MixDirectHrtf_SSE
 #include "mixer_inc.c"
 #undef MixHrtf
 
 
-void Mix_SSE(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
-             MixGains *Gains, ALuint Counter, ALuint OutPos, ALuint BufferSize)
+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, step;
+    ALfloat gain, delta, step;
     __m128 gain4;
-    ALuint c;
+    ALsizei c;
+
+    delta = (Counter > 0) ? 1.0f/(ALfloat)Counter : 0.0f;
 
     for(c = 0;c < OutChans;c++)
     {
-        ALuint pos = 0;
-        gain = Gains[c].Current;
-        step = Gains[c].Step;
-        if(step != 0.0f && Counter > 0)
+        ALsizei pos = 0;
+        gain = CurrentGains[c];
+        step = (TargetGains[c] - gain) * delta;
+        if(fabsf(step) > FLT_EPSILON)
         {
-            ALuint minsize = minu(BufferSize, Counter);
+            ALsizei minsize = mini(BufferSize, Counter);
             /* Mix with applying gain steps in aligned multiples of 4. */
             if(minsize-pos > 3)
             {
@@ -254,11 +180,11 @@ void Mix_SSE(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)
                 gain += step;
             }
             if(pos == Counter)
-                gain = Gains[c].Target;
-            Gains[c].Current = gain;
+                gain = TargetGains[c];
+            CurrentGains[c] = gain;
 
             /* Mix until pos is aligned with 4 or the mix is done. */
-            minsize = minu(BufferSize, (pos+3)&~3);
+            minsize = mini(BufferSize, (pos+3)&~3);
             for(;pos < minsize;pos++)
                 OutBuffer[c][OutPos+pos] += data[pos]*gain;
         }
@@ -277,3 +203,28 @@ void Mix_SSE(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)
             OutBuffer[c][OutPos+pos] += data[pos]*gain;
     }
 }
+
+void MixRow_SSE(ALfloat *OutBuffer, const ALfloat *Gains, const ALfloat (*restrict data)[BUFFERSIZE], ALsizei InChans, ALsizei InPos, ALsizei BufferSize)
+{
+    __m128 gain4;
+    ALsizei c;
+
+    for(c = 0;c < InChans;c++)
+    {
+        ALsizei pos = 0;
+        ALfloat gain = Gains[c];
+        if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
+            continue;
+
+        gain4 = _mm_set1_ps(gain);
+        for(;BufferSize-pos > 3;pos += 4)
+        {
+            const __m128 val4 = _mm_load_ps(&data[c][InPos+pos]);
+            __m128 dry4 = _mm_load_ps(&OutBuffer[pos]);
+            dry4 = _mm_add_ps(dry4, _mm_mul_ps(val4, gain4));
+            _mm_store_ps(&OutBuffer[pos], dry4);
+        }
+        for(;pos < BufferSize;pos++)
+            OutBuffer[pos] += data[c][InPos+pos]*gain;
+    }
+}

+ 7 - 6
libs/openal-soft/Alc/mixer_sse2.c

@@ -27,17 +27,18 @@
 #include "mixer_defs.h"
 
 
-const ALfloat *Resample_lerp32_SSE2(const BsincState* UNUSED(state), const ALfloat *src, ALuint frac, ALuint increment,
-                                    ALfloat *restrict dst, ALuint numsamples)
+const ALfloat *Resample_lerp32_SSE2(const InterpState* UNUSED(state),
+  const ALfloat *restrict src, ALsizei frac, ALint increment,
+  ALfloat *restrict dst, ALsizei numsamples)
 {
     const __m128i increment4 = _mm_set1_epi32(increment*4);
     const __m128 fracOne4 = _mm_set1_ps(1.0f/FRACTIONONE);
     const __m128i fracMask4 = _mm_set1_epi32(FRACTIONMASK);
-    alignas(16) union { ALuint i[4]; float f[4]; } pos_;
-    alignas(16) union { ALuint i[4]; float f[4]; } frac_;
+    union { alignas(16) ALint i[4]; float f[4]; } pos_;
+    union { alignas(16) ALsizei i[4]; float f[4]; } frac_;
     __m128i frac4, pos4;
-    ALuint pos;
-    ALuint i;
+    ALint pos;
+    ALsizei i;
 
     InitiatePositionArrays(frac, increment, frac_.i, pos_.i, 4);
 

+ 11 - 76
libs/openal-soft/Alc/mixer_sse3.c

@@ -31,16 +31,17 @@
 #include "mixer_defs.h"
 
 
-const ALfloat *Resample_fir4_32_SSE3(const BsincState* UNUSED(state), const ALfloat *src, ALuint frac, ALuint increment,
-                                     ALfloat *restrict dst, ALuint numsamples)
+const ALfloat *Resample_fir4_32_SSE3(const InterpState* UNUSED(state),
+  const ALfloat *restrict src, ALsizei frac, ALint increment,
+  ALfloat *restrict dst, ALsizei numsamples)
 {
     const __m128i increment4 = _mm_set1_epi32(increment*4);
     const __m128i fracMask4 = _mm_set1_epi32(FRACTIONMASK);
-    alignas(16) union { ALuint i[4]; float f[4]; } pos_;
-    alignas(16) union { ALuint i[4]; float f[4]; } frac_;
+    union { alignas(16) ALint i[4]; float f[4]; } pos_;
+    union { alignas(16) ALsizei i[4]; float f[4]; } frac_;
     __m128i frac4, pos4;
-    ALuint pos;
-    ALuint i;
+    ALint pos;
+    ALsizei i;
 
     InitiatePositionArrays(frac, increment, frac_.i, pos_.i, 4);
 
@@ -54,10 +55,10 @@ const ALfloat *Resample_fir4_32_SSE3(const BsincState* UNUSED(state), const ALfl
         const __m128 val1 = _mm_loadu_ps(&src[pos_.i[1]]);
         const __m128 val2 = _mm_loadu_ps(&src[pos_.i[2]]);
         const __m128 val3 = _mm_loadu_ps(&src[pos_.i[3]]);
-        __m128 k0 = _mm_load_ps(ResampleCoeffs.FIR4[frac_.i[0]]);
-        __m128 k1 = _mm_load_ps(ResampleCoeffs.FIR4[frac_.i[1]]);
-        __m128 k2 = _mm_load_ps(ResampleCoeffs.FIR4[frac_.i[2]]);
-        __m128 k3 = _mm_load_ps(ResampleCoeffs.FIR4[frac_.i[3]]);
+        __m128 k0 = _mm_load_ps(sinc4Tab[frac_.i[0]]);
+        __m128 k1 = _mm_load_ps(sinc4Tab[frac_.i[1]]);
+        __m128 k2 = _mm_load_ps(sinc4Tab[frac_.i[2]]);
+        __m128 k3 = _mm_load_ps(sinc4Tab[frac_.i[3]]);
         __m128 out;
 
         k0 = _mm_mul_ps(k0, val0);
@@ -94,69 +95,3 @@ const ALfloat *Resample_fir4_32_SSE3(const BsincState* UNUSED(state), const ALfl
     }
     return dst;
 }
-
-const ALfloat *Resample_fir8_32_SSE3(const BsincState* UNUSED(state), const ALfloat *src, ALuint frac, ALuint increment,
-                                     ALfloat *restrict dst, ALuint numsamples)
-{
-    const __m128i increment4 = _mm_set1_epi32(increment*4);
-    const __m128i fracMask4 = _mm_set1_epi32(FRACTIONMASK);
-    alignas(16) union { ALuint i[4]; float f[4]; } pos_;
-    alignas(16) union { ALuint i[4]; float f[4]; } frac_;
-    __m128i frac4, pos4;
-    ALuint pos;
-    ALuint i, j;
-
-    InitiatePositionArrays(frac, increment, frac_.i, pos_.i, 4);
-
-    frac4 = _mm_castps_si128(_mm_load_ps(frac_.f));
-    pos4 = _mm_castps_si128(_mm_load_ps(pos_.f));
-
-    src -= 3;
-    for(i = 0;numsamples-i > 3;i += 4)
-    {
-        __m128 out[2];
-        for(j = 0;j < 8;j+=4)
-        {
-            const __m128 val0 = _mm_loadu_ps(&src[pos_.i[0]+j]);
-            const __m128 val1 = _mm_loadu_ps(&src[pos_.i[1]+j]);
-            const __m128 val2 = _mm_loadu_ps(&src[pos_.i[2]+j]);
-            const __m128 val3 = _mm_loadu_ps(&src[pos_.i[3]+j]);
-            __m128 k0 = _mm_load_ps(&ResampleCoeffs.FIR8[frac_.i[0]][j]);
-            __m128 k1 = _mm_load_ps(&ResampleCoeffs.FIR8[frac_.i[1]][j]);
-            __m128 k2 = _mm_load_ps(&ResampleCoeffs.FIR8[frac_.i[2]][j]);
-            __m128 k3 = _mm_load_ps(&ResampleCoeffs.FIR8[frac_.i[3]][j]);
-
-            k0 = _mm_mul_ps(k0, val0);
-            k1 = _mm_mul_ps(k1, val1);
-            k2 = _mm_mul_ps(k2, val2);
-            k3 = _mm_mul_ps(k3, val3);
-            k0 = _mm_hadd_ps(k0, k1);
-            k2 = _mm_hadd_ps(k2, k3);
-            out[j>>2] = _mm_hadd_ps(k0, k2);
-        }
-
-        out[0] = _mm_add_ps(out[0], out[1]);
-        _mm_store_ps(&dst[i], out[0]);
-
-        frac4 = _mm_add_epi32(frac4, increment4);
-        pos4 = _mm_add_epi32(pos4, _mm_srli_epi32(frac4, FRACTIONBITS));
-        frac4 = _mm_and_si128(frac4, fracMask4);
-
-        _mm_store_ps(pos_.f, _mm_castsi128_ps(pos4));
-        _mm_store_ps(frac_.f, _mm_castsi128_ps(frac4));
-    }
-
-    pos = pos_.i[0];
-    frac = frac_.i[0];
-
-    for(;i < numsamples;i++)
-    {
-        dst[i] = resample_fir8(src[pos  ], src[pos+1], src[pos+2], src[pos+3],
-                               src[pos+4], src[pos+5], src[pos+6], src[pos+7], frac);
-
-        frac += increment;
-        pos  += frac>>FRACTIONBITS;
-        frac &= FRACTIONMASK;
-    }
-    return dst;
-}

+ 18 - 88
libs/openal-soft/Alc/mixer_sse41.c

@@ -28,17 +28,18 @@
 #include "mixer_defs.h"
 
 
-const ALfloat *Resample_lerp32_SSE41(const BsincState* UNUSED(state), const ALfloat *src, ALuint frac, ALuint increment,
-                                     ALfloat *restrict dst, ALuint numsamples)
+const ALfloat *Resample_lerp32_SSE41(const InterpState* UNUSED(state),
+  const ALfloat *restrict src, ALsizei frac, ALint increment,
+  ALfloat *restrict dst, ALsizei numsamples)
 {
     const __m128i increment4 = _mm_set1_epi32(increment*4);
     const __m128 fracOne4 = _mm_set1_ps(1.0f/FRACTIONONE);
     const __m128i fracMask4 = _mm_set1_epi32(FRACTIONMASK);
-    alignas(16) union { ALuint i[4]; float f[4]; } pos_;
-    alignas(16) union { ALuint i[4]; float f[4]; } frac_;
+    union { alignas(16) ALint i[4]; float f[4]; } pos_;
+    union { alignas(16) ALsizei i[4]; float f[4]; } frac_;
     __m128i frac4, pos4;
-    ALuint pos;
-    ALuint i;
+    ALint pos;
+    ALsizei i;
 
     InitiatePositionArrays(frac, increment, frac_.i, pos_.i, 4);
 
@@ -84,16 +85,17 @@ const ALfloat *Resample_lerp32_SSE41(const BsincState* UNUSED(state), const ALfl
     return dst;
 }
 
-const ALfloat *Resample_fir4_32_SSE41(const BsincState* UNUSED(state), const ALfloat *src, ALuint frac, ALuint increment,
-                                      ALfloat *restrict dst, ALuint numsamples)
+const ALfloat *Resample_fir4_32_SSE41(const InterpState* UNUSED(state),
+  const ALfloat *restrict src, ALsizei frac, ALint increment,
+  ALfloat *restrict dst, ALsizei numsamples)
 {
     const __m128i increment4 = _mm_set1_epi32(increment*4);
     const __m128i fracMask4 = _mm_set1_epi32(FRACTIONMASK);
-    alignas(16) union { ALuint i[4]; float f[4]; } pos_;
-    alignas(16) union { ALuint i[4]; float f[4]; } frac_;
+    union { alignas(16) ALint i[4]; float f[4]; } pos_;
+    union { alignas(16) ALsizei i[4]; float f[4]; } frac_;
     __m128i frac4, pos4;
-    ALuint pos;
-    ALuint i;
+    ALint pos;
+    ALsizei i;
 
     InitiatePositionArrays(frac, increment, frac_.i, pos_.i, 4);
 
@@ -107,10 +109,10 @@ const ALfloat *Resample_fir4_32_SSE41(const BsincState* UNUSED(state), const ALf
         const __m128 val1 = _mm_loadu_ps(&src[pos_.i[1]]);
         const __m128 val2 = _mm_loadu_ps(&src[pos_.i[2]]);
         const __m128 val3 = _mm_loadu_ps(&src[pos_.i[3]]);
-        __m128 k0 = _mm_load_ps(ResampleCoeffs.FIR4[frac_.i[0]]);
-        __m128 k1 = _mm_load_ps(ResampleCoeffs.FIR4[frac_.i[1]]);
-        __m128 k2 = _mm_load_ps(ResampleCoeffs.FIR4[frac_.i[2]]);
-        __m128 k3 = _mm_load_ps(ResampleCoeffs.FIR4[frac_.i[3]]);
+        __m128 k0 = _mm_load_ps(sinc4Tab[frac_.i[0]]);
+        __m128 k1 = _mm_load_ps(sinc4Tab[frac_.i[1]]);
+        __m128 k2 = _mm_load_ps(sinc4Tab[frac_.i[2]]);
+        __m128 k3 = _mm_load_ps(sinc4Tab[frac_.i[3]]);
         __m128 out;
 
         k0 = _mm_mul_ps(k0, val0);
@@ -150,75 +152,3 @@ const ALfloat *Resample_fir4_32_SSE41(const BsincState* UNUSED(state), const ALf
     }
     return dst;
 }
-
-const ALfloat *Resample_fir8_32_SSE41(const BsincState* UNUSED(state), const ALfloat *src, ALuint frac, ALuint increment,
-                                      ALfloat *restrict dst, ALuint numsamples)
-{
-    const __m128i increment4 = _mm_set1_epi32(increment*4);
-    const __m128i fracMask4 = _mm_set1_epi32(FRACTIONMASK);
-    alignas(16) union { ALuint i[4]; float f[4]; } pos_;
-    alignas(16) union { ALuint i[4]; float f[4]; } frac_;
-    __m128i frac4, pos4;
-    ALuint pos;
-    ALuint i, j;
-
-    InitiatePositionArrays(frac, increment, frac_.i, pos_.i, 4);
-
-    frac4 = _mm_castps_si128(_mm_load_ps(frac_.f));
-    pos4 = _mm_castps_si128(_mm_load_ps(pos_.f));
-
-    src -= 3;
-    for(i = 0;numsamples-i > 3;i += 4)
-    {
-        __m128 out[2];
-        for(j = 0;j < 8;j+=4)
-        {
-            const __m128 val0 = _mm_loadu_ps(&src[pos_.i[0]+j]);
-            const __m128 val1 = _mm_loadu_ps(&src[pos_.i[1]+j]);
-            const __m128 val2 = _mm_loadu_ps(&src[pos_.i[2]+j]);
-            const __m128 val3 = _mm_loadu_ps(&src[pos_.i[3]+j]);
-            __m128 k0 = _mm_load_ps(&ResampleCoeffs.FIR8[frac_.i[0]][j]);
-            __m128 k1 = _mm_load_ps(&ResampleCoeffs.FIR8[frac_.i[1]][j]);
-            __m128 k2 = _mm_load_ps(&ResampleCoeffs.FIR8[frac_.i[2]][j]);
-            __m128 k3 = _mm_load_ps(&ResampleCoeffs.FIR8[frac_.i[3]][j]);
-
-            k0 = _mm_mul_ps(k0, val0);
-            k1 = _mm_mul_ps(k1, val1);
-            k2 = _mm_mul_ps(k2, val2);
-            k3 = _mm_mul_ps(k3, val3);
-            k0 = _mm_hadd_ps(k0, k1);
-            k2 = _mm_hadd_ps(k2, k3);
-            out[j>>2] = _mm_hadd_ps(k0, k2);
-        }
-
-        out[0] = _mm_add_ps(out[0], out[1]);
-        _mm_store_ps(&dst[i], out[0]);
-
-        frac4 = _mm_add_epi32(frac4, increment4);
-        pos4 = _mm_add_epi32(pos4, _mm_srli_epi32(frac4, FRACTIONBITS));
-        frac4 = _mm_and_si128(frac4, fracMask4);
-
-        pos_.i[0] = _mm_extract_epi32(pos4, 0);
-        pos_.i[1] = _mm_extract_epi32(pos4, 1);
-        pos_.i[2] = _mm_extract_epi32(pos4, 2);
-        pos_.i[3] = _mm_extract_epi32(pos4, 3);
-        frac_.i[0] = _mm_extract_epi32(frac4, 0);
-        frac_.i[1] = _mm_extract_epi32(frac4, 1);
-        frac_.i[2] = _mm_extract_epi32(frac4, 2);
-        frac_.i[3] = _mm_extract_epi32(frac4, 3);
-    }
-
-    pos = pos_.i[0];
-    frac = frac_.i[0];
-
-    for(;i < numsamples;i++)
-    {
-        dst[i] = resample_fir8(src[pos  ], src[pos+1], src[pos+2], src[pos+3],
-                               src[pos+4], src[pos+5], src[pos+6], src[pos+7], frac);
-
-        frac += increment;
-        pos  += frac>>FRACTIONBITS;
-        frac &= FRACTIONMASK;
-    }
-    return dst;
-}

+ 418 - 0
libs/openal-soft/Alc/nfcfilter.c

@@ -0,0 +1,418 @@
+
+#include "config.h"
+
+#include "nfcfilter.h"
+
+#include "alu.h"
+
+
+/* Near-field control filters are the basis for handling the near-field effect.
+ * The near-field effect is a bass-boost present in the directional components
+ * of a recorded signal, created as a result of the wavefront curvature (itself
+ * a function of sound distance). Proper reproduction dictates this be
+ * compensated for using a bass-cut given the playback speaker distance, to
+ * avoid excessive bass in the playback.
+ *
+ * For real-time rendered audio, emulating the near-field effect based on the
+ * sound source's distance, and subsequently compensating for it at output
+ * based on the speaker distances, can create a more realistic perception of
+ * sound distance beyond a simple 1/r attenuation.
+ *
+ * These filters do just that. Each one applies a low-shelf filter, created as
+ * the combination of a bass-boost for a given sound source distance (near-
+ * field emulation) along with a bass-cut for a given control/speaker distance
+ * (near-field compensation).
+ *
+ * Note that it is necessary to apply a cut along with the boost, since the
+ * boost alone is unstable in higher-order ambisonics as it causes an infinite
+ * DC gain (even first-order ambisonics requires there to be no DC offset for
+ * the boost to work). Consequently, ambisonics requires a control parameter to
+ * be used to avoid an unstable boost-only filter. NFC-HOA defines this control
+ * as a reference delay, calculated with:
+ *
+ * reference_delay = control_distance / speed_of_sound
+ *
+ * This means w0 (for input) or w1 (for output) should be set to:
+ *
+ * wN = 1 / (reference_delay * sample_rate)
+ *
+ * when dealing with NFC-HOA content. For FOA input content, which does not
+ * specify a reference_delay variable, w0 should be set to 0 to apply only
+ * near-field compensation for output. It's important that w1 be a finite,
+ * positive, non-0 value or else the bass-boost will become unstable again.
+ * Also, w0 should not be too large compared to w1, to avoid excessively loud
+ * low frequencies.
+ */
+
+static const float B[4][3] = {
+    {    0.0f                             },
+    {    1.0f                             },
+    {    3.0f,     3.0f                   },
+    { 3.6778f,  6.4595f, 2.3222f          },
+  /*{ 4.2076f, 11.4877f, 5.7924f, 9.1401f }*/
+};
+
+void NfcFilterCreate1(NfcFilter *nfc, const float w0, const float w1)
+{
+    float b_00, g_0;
+    float r;
+
+    memset(nfc, 0, sizeof(*nfc));
+
+    nfc->g = 1.0f;
+    nfc->coeffs[0] = 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;
+
+    /* 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;
+}
+
+void NfcFilterAdjust1(NfcFilter *nfc, const float w0)
+{
+    float b_00, g_0;
+    float r;
+
+    r = 0.5f * 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;
+}
+
+void NfcFilterUpdate1(NfcFilter *nfc, ALfloat *restrict dst, const float *restrict src, const int count)
+{
+    const float b0 = nfc->coeffs[0];
+    const float a0 = nfc->coeffs[1];
+    const float a1 = nfc->coeffs[2];
+    float z1 = nfc->history[0];
+    int i;
+
+    for(i = 0;i < count;i++)
+    {
+        float out = src[i] * b0;
+        float y;
+
+        y = out - (a1*z1);
+        out = y + (a0*z1);
+        z1 += y;
+
+        dst[i] = out;
+    }
+    nfc->history[0] = z1;
+}
+
+
+void NfcFilterCreate2(NfcFilter *nfc, const float w0, const float w1)
+{
+    float b_10, b_11, g_1;
+    float r;
+
+    memset(nfc, 0, sizeof(*nfc));
+
+    nfc->g = 1.0f;
+    nfc->coeffs[0] = 1.0f;
+
+    /* Calculate bass-boost coefficients. */
+    r = 0.5f * w0;
+    b_10 = B[2][0] * r;
+    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;
+
+    /* Calculate bass-cut coefficients. */
+    r = 0.5f * w1;
+    b_10 = B[2][0] * r;
+    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;
+}
+
+void NfcFilterAdjust2(NfcFilter *nfc, const float w0)
+{
+    float b_10, b_11, g_1;
+    float r;
+
+    r = 0.5f * w0;
+    b_10 = B[2][0] * r;
+    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;
+}
+
+void NfcFilterUpdate2(NfcFilter *nfc, ALfloat *restrict dst, const float *restrict src, const int count)
+{
+    const float b0 = nfc->coeffs[0];
+    const float a00 = nfc->coeffs[1];
+    const float a01 = nfc->coeffs[2];
+    const float a10 = nfc->coeffs[3];
+    const float a11 = nfc->coeffs[4];
+    float z1 = nfc->history[0];
+    float z2 = nfc->history[1];
+    int i;
+
+    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);
+        z2 += z1;
+        z1 += y;
+
+        dst[i] = out;
+    }
+    nfc->history[0] = z1;
+    nfc->history[1] = z2;
+}
+
+
+void NfcFilterCreate3(NfcFilter *nfc, const float w0, const float w1)
+{
+    float b_10, b_11, g_1;
+    float b_00, g_0;
+    float r;
+
+    memset(nfc, 0, sizeof(*nfc));
+
+    nfc->g = 1.0f;
+    nfc->coeffs[0] = 1.0f;
+
+    /* Calculate bass-boost coefficients. */
+    r = 0.5f * w0;
+    b_10 = B[3][0] * r;
+    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;
+
+    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;
+
+    /* Calculate bass-cut coefficients. */
+    r = 0.5f * w1;
+    b_10 = B[3][0] * r;
+    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;
+    
+    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;
+}
+
+void NfcFilterAdjust3(NfcFilter *nfc, const float w0)
+{
+    float b_10, b_11, g_1;
+    float b_00, g_0;
+    float r;
+
+    r = 0.5f * w0;
+    b_10 = B[3][0] * r;
+    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;
+
+    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;
+}
+
+void NfcFilterUpdate3(NfcFilter *nfc, ALfloat *restrict dst, const float *restrict src, const int count)
+{
+    const float b0 = nfc->coeffs[0];
+    const float a00 = nfc->coeffs[1];
+    const float a01 = nfc->coeffs[2];
+    const float a02 = nfc->coeffs[3];
+    const float a10 = nfc->coeffs[4];
+    const float a11 = nfc->coeffs[5];
+    const float a12 = nfc->coeffs[6];
+    float z1 = nfc->history[0];
+    float z2 = nfc->history[1];
+    float z3 = nfc->history[2];
+    int i;
+
+    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);
+        z2 += z1;
+        z1 += y;
+
+        y = out - (a12*z3);
+        out = y + (a02*z3);
+        z3 += y;
+
+        dst[i] = out;
+    }
+    nfc->history[0] = z1;
+    nfc->history[1] = z2;
+    nfc->history[2] = z3;
+}
+
+
+#if 0 /* Original methods the above are derived from. */
+static void NfcFilterCreate(NfcFilter *nfc, const ALsizei order, const float src_dist, const float ctl_dist, const float rate)
+{
+    static const float B[4][5] = {
+        {                                     },
+        {    1.0f                             },
+        {    3.0f,     3.0f                   },
+        { 3.6778f,  6.4595f, 2.3222f          },
+        { 4.2076f, 11.4877f, 5.7924f, 9.1401f }
+    };
+    float w0 = SPEEDOFSOUNDMETRESPERSEC / (src_dist * rate);
+    float w1 = SPEEDOFSOUNDMETRESPERSEC / (ctl_dist * rate);
+    ALsizei i;
+    float r;
+
+    nfc->g = 1.0f;
+    nfc->coeffs[0] = 1.0f;
+
+    /* NOTE: Slight adjustment from the literature to raise the center
+     * frequency a bit (0.5 -> 1.0).
+     */
+    r = 1.0f * w0;
+    for(i = 0; i < (order-1);i += 2)
+    {
+        float b_10 = B[order][i  ] * r;
+        float b_11 = B[order][i+1] * r * r;
+        float g_1 = 1.0f + b_10 + b_11;
+
+        nfc->b[i] = b_10;
+        nfc->b[i + 1] = b_11;
+        nfc->coeffs[0] *= g_1;
+        nfc->coeffs[i+1] = ((2.0f * b_10) + (4.0f * b_11)) / g_1;
+        nfc->coeffs[i+2] = (4.0f * b_11) / g_1;
+    }
+    if(i < order)
+    {
+        float b_00 = B[order][i] * r;
+        float g_0 = 1.0f + b_00;
+
+        nfc->b[i] = b_00;
+        nfc->coeffs[0] *= g_0;
+        nfc->coeffs[i+1] = (2.0f * b_00) / g_0;
+    }
+
+    r = 1.0f * w1;
+    for(i = 0;i < (order-1);i += 2)
+    {
+        float b_10 = B[order][i  ] * r;
+        float b_11 = B[order][i+1] * r * r;
+        float g_1 = 1.0f + b_10 + b_11;
+
+        nfc->g /= g_1;
+        nfc->coeffs[0] /= g_1;
+        nfc->coeffs[order+i+1] = ((2.0f * b_10) + (4.0f * b_11)) / g_1;
+        nfc->coeffs[order+i+2] = (4.0f * b_11) / g_1;
+    }
+    if(i < order)
+    {
+        float b_00 = B[order][i] * r;
+        float g_0 = 1.0f + b_00;
+
+        nfc->g /= g_0;
+        nfc->coeffs[0] /= g_0;
+        nfc->coeffs[order+i+1] = (2.0f * b_00) / g_0;
+    }
+
+    for(i = 0; i < MAX_AMBI_ORDER; i++)
+        nfc->history[i] = 0.0f;
+}
+
+static void NfcFilterAdjust(NfcFilter *nfc, const float distance)
+{
+    int i;
+
+    nfc->coeffs[0] = nfc->g;
+
+    for(i = 0;i < (nfc->order-1);i += 2)
+    {
+        float b_10 = nfc->b[i] / distance;
+        float b_11 = nfc->b[i+1] / (distance * distance);
+        float g_1 = 1.0f + b_10 + b_11;
+
+        nfc->coeffs[0] *= g_1;
+        nfc->coeffs[i+1] = ((2.0f * b_10) + (4.0f * b_11)) / g_1;
+        nfc->coeffs[i+2] = (4.0f * b_11) / g_1;
+    }
+    if(i < nfc->order)
+    {
+        float b_00 = nfc->b[i] / distance;
+        float g_0 = 1.0f + b_00;
+
+        nfc->coeffs[0] *= g_0;
+        nfc->coeffs[i+1] = (2.0f * b_00) / g_0;
+    }
+}
+
+static float NfcFilterUpdate(const float in, NfcFilter *nfc)
+{
+    int i;
+    float out = in * nfc->coeffs[0];
+
+    for(i = 0;i < (nfc->order-1);i += 2)
+    {
+        float y = out - (nfc->coeffs[nfc->order+i+1] * nfc->history[i]) -
+                        (nfc->coeffs[nfc->order+i+2] * nfc->history[i+1]) + 1.0e-30f;
+        out = y + (nfc->coeffs[i+1]*nfc->history[i]) + (nfc->coeffs[i+2]*nfc->history[i+1]);
+
+        nfc->history[i+1] += nfc->history[i];
+        nfc->history[i] += y;
+    }
+    if(i < nfc->order)
+    {
+        float y = out - (nfc->coeffs[nfc->order+i+1] * nfc->history[i]) + 1.0e-30f;
+
+        out = y + (nfc->coeffs[i+1] * nfc->history[i]);
+        nfc->history[i] += y;
+    }
+
+    return out;
+}
+#endif

+ 37 - 0
libs/openal-soft/Alc/nfcfilter.h

@@ -0,0 +1,37 @@
+#ifndef NFCFILTER_H
+#define NFCFILTER_H
+
+#include "alMain.h"
+
+typedef struct NfcFilter {
+    float g;
+    float coeffs[MAX_AMBI_ORDER*2 + 1];
+    float history[MAX_AMBI_ORDER];
+} NfcFilter;
+
+/* NOTE:
+ * w0 = speed_of_sound / (source_distance * sample_rate);
+ * w1 = speed_of_sound / (control_distance * sample_rate);
+ *
+ * Generally speaking, the control distance should be approximately the average
+ * speaker distance, or based on the reference delay if outputing NFC-HOA. It
+ * must not be negative, 0, or infinite. The source distance should not be too
+ * small relative to the control distance.
+ */
+
+/* Near-field control filter for first-order ambisonic channels (1-3). */
+void NfcFilterCreate1(NfcFilter *nfc, const float w0, const float w1);
+void NfcFilterAdjust1(NfcFilter *nfc, const float w0);
+void NfcFilterUpdate1(NfcFilter *nfc, float *restrict dst, const float *restrict src, const int count);
+
+/* Near-field control filter for second-order ambisonic channels (4-8). */
+void NfcFilterCreate2(NfcFilter *nfc, const float w0, const float w1);
+void NfcFilterAdjust2(NfcFilter *nfc, const float w0);
+void NfcFilterUpdate2(NfcFilter *nfc, float *restrict dst, const float *restrict src, const int count);
+
+/* Near-field control filter for third-order ambisonic channels (9-15). */
+void NfcFilterCreate3(NfcFilter *nfc, const float w0, const float w1);
+void NfcFilterAdjust3(NfcFilter *nfc, const float w0);
+void NfcFilterUpdate3(NfcFilter *nfc, float *restrict dst, const float *restrict src, const int count);
+
+#endif /* NFCFILTER_H */

+ 1036 - 322
libs/openal-soft/Alc/panning.c

@@ -27,19 +27,19 @@
 #include <assert.h>
 
 #include "alMain.h"
-#include "AL/al.h"
-#include "AL/alc.h"
+#include "alAuxEffectSlot.h"
 #include "alu.h"
 #include "bool.h"
+#include "ambdec.h"
+#include "bformatdec.h"
+#include "uhjfilter.h"
+#include "bs2b.h"
 
 
-#define ZERO_ORDER_SCALE    0.0f
-#define FIRST_ORDER_SCALE   1.0f
-#define SECOND_ORDER_SCALE  (1.0f / 1.22474f)
-#define THIRD_ORDER_SCALE   (1.0f / 1.30657f)
+extern inline void CalcAngleCoeffs(ALfloat azimuth, ALfloat elevation, ALfloat spread, ALfloat coeffs[MAX_AMBI_COEFFS]);
 
 
-static const ALuint FuMa2ACN[MAX_AMBI_COEFFS] = {
+static const ALsizei FuMa2ACN[MAX_AMBI_COEFFS] = {
     0,  /* W */
     3,  /* X */
     1,  /* Y */
@@ -57,11 +57,36 @@ static const ALuint FuMa2ACN[MAX_AMBI_COEFFS] = {
     15, /* P */
     9,  /* Q */
 };
+static const ALsizei ACN2ACN[MAX_AMBI_COEFFS] = {
+    0,  1,  2,  3,  4,  5,  6,  7,
+    8,  9, 10, 11, 12, 13, 14, 15
+};
 
-/* NOTE: These are scale factors as applied to Ambisonics content. FuMa
- * decoder coefficients should be divided by these values to get N3D decoder
- * coefficients.
+/* NOTE: These are scale factors as applied to Ambisonics content. Decoder
+ * coefficients should be divided by these values to get proper N3D scalings.
  */
+static const ALfloat UnitScale[MAX_AMBI_COEFFS] = {
+    1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
+    1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f
+};
+static const ALfloat SN3D2N3DScale[MAX_AMBI_COEFFS] = {
+    1.000000000f, /* ACN  0 (W), sqrt(1) */
+    1.732050808f, /* ACN  1 (Y), sqrt(3) */
+    1.732050808f, /* ACN  2 (Z), sqrt(3) */
+    1.732050808f, /* ACN  3 (X), sqrt(3) */
+    2.236067978f, /* ACN  4 (V), sqrt(5) */
+    2.236067978f, /* ACN  5 (T), sqrt(5) */
+    2.236067978f, /* ACN  6 (R), sqrt(5) */
+    2.236067978f, /* ACN  7 (S), sqrt(5) */
+    2.236067978f, /* ACN  8 (U), sqrt(5) */
+    2.645751311f, /* ACN  9 (Q), sqrt(7) */
+    2.645751311f, /* ACN 10 (O), sqrt(7) */
+    2.645751311f, /* ACN 11 (M), sqrt(7) */
+    2.645751311f, /* ACN 12 (K), sqrt(7) */
+    2.645751311f, /* ACN 13 (L), sqrt(7) */
+    2.645751311f, /* ACN 14 (N), sqrt(7) */
+    2.645751311f, /* ACN 15 (P), sqrt(7) */
+};
 static const ALfloat FuMa2N3DScale[MAX_AMBI_COEFFS] = {
     1.414213562f, /* ACN  0 (W), sqrt(2) */
     1.732050808f, /* ACN  1 (Y), sqrt(3) */
@@ -82,35 +107,8 @@ static const ALfloat FuMa2N3DScale[MAX_AMBI_COEFFS] = {
 };
 
 
-void ComputeAmbientGains(const ALCdevice *device, ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS])
-{
-    ALuint i;
-
-    for(i = 0;i < device->NumChannels;i++)
-    {
-        // The W coefficients are based on a mathematical average of the
-        // output. The square root of the base average provides for a more
-        // perceptual average volume, better suited to non-directional gains.
-        gains[i] = sqrtf(device->AmbiCoeffs[i][0]) * ingain;
-    }
-    for(;i < MAX_OUTPUT_CHANNELS;i++)
-        gains[i] = 0.0f;
-}
-
-void ComputeAngleGains(const ALCdevice *device, ALfloat angle, ALfloat elevation, ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS])
-{
-    ALfloat dir[3] = {
-        sinf(angle) * cosf(elevation),
-        sinf(elevation),
-        -cosf(angle) * cosf(elevation)
-    };
-    ComputeDirectionalGains(device, dir, ingain, gains);
-}
-
-void ComputeDirectionalGains(const ALCdevice *device, const ALfloat dir[3], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS])
+void CalcDirectionCoeffs(const ALfloat dir[3], ALfloat spread, ALfloat coeffs[MAX_AMBI_COEFFS])
 {
-    ALfloat coeffs[MAX_AMBI_COEFFS];
-    ALuint i, j;
     /* Convert from OpenAL coords to Ambisonics. */
     ALfloat x = -dir[2];
     ALfloat y = -dir[0];
@@ -137,34 +135,150 @@ void ComputeDirectionalGains(const ALCdevice *device, const ALfloat dir[3], ALfl
     coeffs[14] =  5.123475383f * z * (x*x - y*y);       /* ACN 14 = sqrt(105)/2 * Z * (X*X - Y*Y) */
     coeffs[15] =  2.091650066f * x * (x*x - 3.0f*y*y);  /* ACN 15 = sqrt(35/8) * X * (X*X - 3*Y*Y) */
 
-    for(i = 0;i < device->NumChannels;i++)
+    if(spread > 0.0f)
+    {
+        /* Implement the spread by using a spherical source that subtends the
+         * angle spread. See:
+         * http://www.ppsloan.org/publications/StupidSH36.pdf - Appendix A3
+         *
+         * When adjusted for N3D normalization instead of SN3D, these
+         * calculations are:
+         *
+         * ZH0 = -sqrt(pi) * (-1+ca);
+         * ZH1 =  0.5*sqrt(pi) * sa*sa;
+         * ZH2 = -0.5*sqrt(pi) * ca*(-1+ca)*(ca+1);
+         * ZH3 = -0.125*sqrt(pi) * (-1+ca)*(ca+1)*(5*ca*ca - 1);
+         * ZH4 = -0.125*sqrt(pi) * ca*(-1+ca)*(ca+1)*(7*ca*ca - 3);
+         * ZH5 = -0.0625*sqrt(pi) * (-1+ca)*(ca+1)*(21*ca*ca*ca*ca - 14*ca*ca + 1);
+         *
+         * The gain of the source is compensated for size, so that the
+         * loundness doesn't depend on the spread. Thus:
+         *
+         * ZH0 = 1.0f;
+         * ZH1 = 0.5f * (ca+1.0f);
+         * ZH2 = 0.5f * (ca+1.0f)*ca;
+         * ZH3 = 0.125f * (ca+1.0f)*(5.0f*ca*ca - 1.0f);
+         * ZH4 = 0.125f * (ca+1.0f)*(7.0f*ca*ca - 3.0f)*ca;
+         * ZH5 = 0.0625f * (ca+1.0f)*(21.0f*ca*ca*ca*ca - 14.0f*ca*ca + 1.0f);
+         */
+        ALfloat ca = cosf(spread * 0.5f);
+        /* Increase the source volume by up to +3dB for a full spread. */
+        ALfloat scale = sqrtf(1.0f + spread/F_TAU);
+
+        ALfloat ZH0_norm = scale;
+        ALfloat ZH1_norm = 0.5f * (ca+1.f) * scale;
+        ALfloat ZH2_norm = 0.5f * (ca+1.f)*ca * scale;
+        ALfloat ZH3_norm = 0.125f * (ca+1.f)*(5.f*ca*ca-1.f) * scale;
+
+        /* Zeroth-order */
+        coeffs[0]  *= ZH0_norm;
+        /* First-order */
+        coeffs[1]  *= ZH1_norm;
+        coeffs[2]  *= ZH1_norm;
+        coeffs[3]  *= ZH1_norm;
+        /* Second-order */
+        coeffs[4]  *= ZH2_norm;
+        coeffs[5]  *= ZH2_norm;
+        coeffs[6]  *= ZH2_norm;
+        coeffs[7]  *= ZH2_norm;
+        coeffs[8]  *= ZH2_norm;
+        /* Third-order */
+        coeffs[9]  *= ZH3_norm;
+        coeffs[10] *= ZH3_norm;
+        coeffs[11] *= ZH3_norm;
+        coeffs[12] *= ZH3_norm;
+        coeffs[13] *= ZH3_norm;
+        coeffs[14] *= ZH3_norm;
+        coeffs[15] *= ZH3_norm;
+    }
+}
+
+void 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 ComputeAmbientGainsMC(const ChannelConfig *chancoeffs, ALsizei numchans, ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS])
+{
+    ALsizei i;
+
+    for(i = 0;i < numchans;i++)
+        gains[i] = chancoeffs[i][0] * 1.414213562f * ingain;
+    for(;i < MAX_OUTPUT_CHANNELS;i++)
+        gains[i] = 0.0f;
+}
+
+void ComputeAmbientGainsBF(const BFChannelConfig *chanmap, ALsizei numchans, ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS])
+{
+    ALfloat gain = 0.0f;
+    ALsizei i;
+
+    for(i = 0;i < numchans;i++)
+    {
+        if(chanmap[i].Index == 0)
+            gain += chanmap[i].Scale;
+    }
+    gains[0] = gain * 1.414213562f * ingain;
+    for(i = 1;i < MAX_OUTPUT_CHANNELS;i++)
+        gains[i] = 0.0f;
+}
+
+void ComputePanningGainsMC(const ChannelConfig *chancoeffs, ALsizei numchans, ALsizei numcoeffs, const ALfloat coeffs[MAX_AMBI_COEFFS], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS])
+{
+    ALsizei i, j;
+
+    for(i = 0;i < numchans;i++)
     {
         float gain = 0.0f;
-        for(j = 0;j < MAX_AMBI_COEFFS;j++)
-            gain += device->AmbiCoeffs[i][j]*coeffs[j];
-        gains[i] = gain * ingain;
+        for(j = 0;j < numcoeffs;j++)
+            gain += chancoeffs[i][j]*coeffs[j];
+        gains[i] = clampf(gain, 0.0f, 1.0f) * ingain;
     }
     for(;i < MAX_OUTPUT_CHANNELS;i++)
         gains[i] = 0.0f;
 }
 
-void ComputeBFormatGains(const ALCdevice *device, const ALfloat mtx[4], 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])
 {
-    ALuint i, j;
+    ALsizei i;
 
-    for(i = 0;i < device->NumChannels;i++)
+    for(i = 0;i < numchans;i++)
+        gains[i] = chanmap[i].Scale * coeffs[chanmap[i].Index] * ingain;
+    for(;i < MAX_OUTPUT_CHANNELS;i++)
+        gains[i] = 0.0f;
+}
+
+void ComputeFirstOrderGainsMC(const ChannelConfig *chancoeffs, 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 += device->AmbiCoeffs[i][j] * mtx[j];
-        gains[i] = gain * ingain;
+            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;
+}
+
 
-DECL_CONST static inline const char *GetLabelFromChannel(enum Channel channel)
+static inline const char *GetLabelFromChannel(enum Channel channel)
 {
     switch(channel)
     {
@@ -178,10 +292,31 @@ DECL_CONST static inline const char *GetLabelFromChannel(enum Channel channel)
         case SideLeft: return "side-left";
         case SideRight: return "side-right";
 
-        case BFormatW: return "bformat-w";
-        case BFormatX: return "bformat-x";
-        case BFormatY: return "bformat-y";
-        case BFormatZ: return "bformat-z";
+        case UpperFrontLeft: return "upper-front-left";
+        case UpperFrontRight: return "upper-front-right";
+        case UpperBackLeft: return "upper-back-left";
+        case UpperBackRight: return "upper-back-right";
+        case LowerFrontLeft: return "lower-front-left";
+        case LowerFrontRight: return "lower-front-right";
+        case LowerBackLeft: return "lower-back-left";
+        case LowerBackRight: return "lower-back-right";
+
+        case Aux0: return "aux-0";
+        case Aux1: return "aux-1";
+        case Aux2: return "aux-2";
+        case Aux3: return "aux-3";
+        case Aux4: return "aux-4";
+        case Aux5: return "aux-5";
+        case Aux6: return "aux-6";
+        case Aux7: return "aux-7";
+        case Aux8: return "aux-8";
+        case Aux9: return "aux-9";
+        case Aux10: return "aux-10";
+        case Aux11: return "aux-11";
+        case Aux12: return "aux-12";
+        case Aux13: return "aux-13";
+        case Aux14: return "aux-14";
+        case Aux15: return "aux-15";
 
         case InvalidChannel: break;
     }
@@ -194,364 +329,943 @@ typedef struct ChannelMap {
     ChannelConfig Config;
 } ChannelMap;
 
-static void SetChannelMap(ALCdevice *device, const ChannelMap *chanmap, size_t count, ALfloat ambiscale, ALboolean isfuma)
+static void SetChannelMap(const enum Channel *devchans, ChannelConfig *ambicoeffs,
+                          const ChannelMap *chanmap, size_t count, ALsizei *outcount)
 {
     size_t j, k;
-    ALuint i;
+    ALsizei i;
 
-    device->AmbiScale = ambiscale;
-    for(i = 0;i < MAX_OUTPUT_CHANNELS && device->ChannelName[i] != InvalidChannel;i++)
+    for(i = 0;i < MAX_OUTPUT_CHANNELS && devchans[i] != InvalidChannel;i++)
     {
-        if(device->ChannelName[i] == LFE)
+        if(devchans[i] == LFE)
         {
             for(j = 0;j < MAX_AMBI_COEFFS;j++)
-                device->AmbiCoeffs[i][j] = 0.0f;
+                ambicoeffs[i][j] = 0.0f;
             continue;
         }
 
         for(j = 0;j < count;j++)
         {
-            if(device->ChannelName[i] == chanmap[j].ChanName)
+            if(devchans[i] != chanmap[j].ChanName)
+                continue;
+
+            for(k = 0;k < MAX_AMBI_COEFFS;++k)
+                ambicoeffs[i][k] = chanmap[j].Config[k];
+            break;
+        }
+        if(j == count)
+            ERR("Failed to match %s channel (%u) in channel map\n", GetLabelFromChannel(devchans[i]), i);
+    }
+    *outcount = i;
+}
+
+static bool MakeSpeakerMap(ALCdevice *device, const AmbDecConf *conf, ALsizei speakermap[MAX_OUTPUT_CHANNELS])
+{
+    ALsizei i;
+
+    for(i = 0;i < conf->NumSpeakers;i++)
+    {
+        int c = -1;
+
+        /* NOTE: AmbDec does not define any standard speaker names, however
+         * for this to work we have to by able to find the output channel
+         * the speaker definition corresponds to. Therefore, OpenAL Soft
+         * requires these channel labels to be recognized:
+         *
+         * LF = Front left
+         * RF = Front right
+         * LS = Side left
+         * RS = Side right
+         * LB = Back left
+         * RB = Back right
+         * CE = Front center
+         * CB = Back center
+         *
+         * Additionally, surround51 will acknowledge back speakers for side
+         * channels, and surround51rear will acknowledge side speakers for
+         * back channels, to avoid issues with an ambdec expecting 5.1 to
+         * use the side channels when the device is configured for back,
+         * and vice-versa.
+         */
+        if(alstr_cmp_cstr(conf->Speakers[i].Name, "LF") == 0)
+            c = GetChannelIdxByName(device->RealOut, FrontLeft);
+        else if(alstr_cmp_cstr(conf->Speakers[i].Name, "RF") == 0)
+            c = GetChannelIdxByName(device->RealOut, FrontRight);
+        else if(alstr_cmp_cstr(conf->Speakers[i].Name, "CE") == 0)
+            c = GetChannelIdxByName(device->RealOut, FrontCenter);
+        else if(alstr_cmp_cstr(conf->Speakers[i].Name, "LS") == 0)
+        {
+            if(device->FmtChans == DevFmtX51Rear)
+                c = GetChannelIdxByName(device->RealOut, BackLeft);
+            else
+                c = GetChannelIdxByName(device->RealOut, SideLeft);
+        }
+        else if(alstr_cmp_cstr(conf->Speakers[i].Name, "RS") == 0)
+        {
+            if(device->FmtChans == DevFmtX51Rear)
+                c = GetChannelIdxByName(device->RealOut, BackRight);
+            else
+                c = GetChannelIdxByName(device->RealOut, SideRight);
+        }
+        else if(alstr_cmp_cstr(conf->Speakers[i].Name, "LB") == 0)
+        {
+            if(device->FmtChans == DevFmtX51)
+                c = GetChannelIdxByName(device->RealOut, SideLeft);
+            else
+                c = GetChannelIdxByName(device->RealOut, BackLeft);
+        }
+        else if(alstr_cmp_cstr(conf->Speakers[i].Name, "RB") == 0)
+        {
+            if(device->FmtChans == DevFmtX51)
+                c = GetChannelIdxByName(device->RealOut, SideRight);
+            else
+                c = GetChannelIdxByName(device->RealOut, BackRight);
+        }
+        else if(alstr_cmp_cstr(conf->Speakers[i].Name, "CB") == 0)
+            c = GetChannelIdxByName(device->RealOut, BackCenter);
+        else
+        {
+            const char *name = alstr_get_cstr(conf->Speakers[i].Name);
+            unsigned int n;
+            char ch;
+
+            if(sscanf(name, "AUX%u%c", &n, &ch) == 1 && n < 16)
+                c = GetChannelIdxByName(device->RealOut, Aux0+n);
+            else
             {
-                if(isfuma)
-                {
-                    /* Reformat FuMa -> ACN/N3D */
-                    for(k = 0;k < MAX_AMBI_COEFFS;++k)
-                    {
-                        ALuint acn = FuMa2ACN[k];
-                        device->AmbiCoeffs[i][acn] = chanmap[j].Config[k] / FuMa2N3DScale[acn];
-                    }
-                }
-                else
-                {
-                    for(k = 0;k < MAX_AMBI_COEFFS;++k)
-                        device->AmbiCoeffs[i][k] = chanmap[j].Config[k];
-                }
-                break;
+                ERR("AmbDec speaker label \"%s\" not recognized\n", name);
+                return false;
             }
         }
-        if(j == count)
-            ERR("Failed to match %s channel (%u) in config\n", GetLabelFromChannel(device->ChannelName[i]), i);
+        if(c == -1)
+        {
+            ERR("Failed to lookup AmbDec speaker label %s\n",
+                alstr_get_cstr(conf->Speakers[i].Name));
+            return false;
+        }
+        speakermap[i] = c;
+    }
+
+    return true;
+}
+
+
+static const ChannelMap MonoCfg[1] = {
+    { FrontCenter, { 1.0f } },
+}, StereoCfg[2] = {
+    { FrontLeft,   { 5.00000000e-1f,  2.88675135e-1f, 0.0f,  1.19573156e-1f } },
+    { FrontRight,  { 5.00000000e-1f, -2.88675135e-1f, 0.0f,  1.19573156e-1f } },
+}, 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 } },
+    { FrontRight,  { 3.53553391e-1f, -2.04124145e-1f, 0.0f,  2.04124145e-1f } },
+    { BackRight,   { 3.53553391e-1f, -2.04124145e-1f, 0.0f, -2.04124145e-1f } },
+}, X51SideCfg[5] = {
+    { SideLeft,    { 3.33001372e-1f,  1.89085671e-1f, 0.0f, -2.00041334e-1f, -2.12309737e-2f, 0.0f, 0.0f, 0.0f, -1.14573483e-2f } },
+    { FrontLeft,   { 1.47751298e-1f,  1.28994110e-1f, 0.0f,  1.15190495e-1f,  7.44949143e-2f, 0.0f, 0.0f, 0.0f, -6.47739980e-3f } },
+    { FrontCenter, { 7.73595729e-2f,  0.00000000e+0f, 0.0f,  9.71390298e-2f,  0.00000000e+0f, 0.0f, 0.0f, 0.0f,  5.18625335e-2f } },
+    { FrontRight,  { 1.47751298e-1f, -1.28994110e-1f, 0.0f,  1.15190495e-1f, -7.44949143e-2f, 0.0f, 0.0f, 0.0f, -6.47739980e-3f } },
+    { SideRight,   { 3.33001372e-1f, -1.89085671e-1f, 0.0f, -2.00041334e-1f,  2.12309737e-2f, 0.0f, 0.0f, 0.0f, -1.14573483e-2f } },
+}, X51RearCfg[5] = {
+    { BackLeft,    { 3.33001372e-1f,  1.89085671e-1f, 0.0f, -2.00041334e-1f, -2.12309737e-2f, 0.0f, 0.0f, 0.0f, -1.14573483e-2f } },
+    { FrontLeft,   { 1.47751298e-1f,  1.28994110e-1f, 0.0f,  1.15190495e-1f,  7.44949143e-2f, 0.0f, 0.0f, 0.0f, -6.47739980e-3f } },
+    { FrontCenter, { 7.73595729e-2f,  0.00000000e+0f, 0.0f,  9.71390298e-2f,  0.00000000e+0f, 0.0f, 0.0f, 0.0f,  5.18625335e-2f } },
+    { FrontRight,  { 1.47751298e-1f, -1.28994110e-1f, 0.0f,  1.15190495e-1f, -7.44949143e-2f, 0.0f, 0.0f, 0.0f, -6.47739980e-3f } },
+    { BackRight,   { 3.33001372e-1f, -1.89085671e-1f, 0.0f, -2.00041334e-1f,  2.12309737e-2f, 0.0f, 0.0f, 0.0f, -1.14573483e-2f } },
+}, X61Cfg[6] = {
+    { SideLeft,    { 2.04462744e-1f,  2.17178497e-1f, 0.0f, -4.39990188e-2f, -2.60787329e-2f, 0.0f, 0.0f, 0.0f, -6.87238843e-2f } },
+    { FrontLeft,   { 1.18130342e-1f,  9.34633906e-2f, 0.0f,  1.08553749e-1f,  6.80658795e-2f, 0.0f, 0.0f, 0.0f,  1.08999485e-2f } },
+    { FrontCenter, { 7.73595729e-2f,  0.00000000e+0f, 0.0f,  9.71390298e-2f,  0.00000000e+0f, 0.0f, 0.0f, 0.0f,  5.18625335e-2f } },
+    { FrontRight,  { 1.18130342e-1f, -9.34633906e-2f, 0.0f,  1.08553749e-1f, -6.80658795e-2f, 0.0f, 0.0f, 0.0f,  1.08999485e-2f } },
+    { SideRight,   { 2.04462744e-1f, -2.17178497e-1f, 0.0f, -4.39990188e-2f,  2.60787329e-2f, 0.0f, 0.0f, 0.0f, -6.87238843e-2f } },
+    { BackCenter,  { 2.50001688e-1f,  0.00000000e+0f, 0.0f, -2.50000094e-1f,  0.00000000e+0f, 0.0f, 0.0f, 0.0f,  6.05133395e-2f } },
+}, X71Cfg[6] = {
+    { BackLeft,    { 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 } },
+    { SideLeft,    { 2.04124145e-1f,  2.17760495e-1f, 0.0f,  0.00000000e+0f,  0.00000000e+0f, 0.0f, 0.0f, 0.0f, -1.49071198e-1f, -3.73460789e-2f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,  0.00000000e+0f } },
+    { FrontLeft,   { 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 } },
+    { FrontRight,  { 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 } },
+    { SideRight,   { 2.04124145e-1f, -2.17760495e-1f, 0.0f,  0.00000000e+0f,  0.00000000e+0f, 0.0f, 0.0f, 0.0f, -1.49071198e-1f,  3.73460789e-2f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,  0.00000000e+0f } },
+    { 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)
+{
+    const char *devname = alstr_get_cstr(device->DeviceName);
+    ALsizei i;
+
+    if(GetConfigValueBool(devname, "decoder", "nfc", 1) && ctrl_dist > 0.0f)
+    {
+        /* NFC is only used when AvgSpeakerDist is greater than 0, and
+         * METERS_PER_UNIT is also greater than 0. In addition, NFC can only be
+         * used when rendering to an ambisonic buffer.
+         */
+        device->AvgSpeakerDist = ctrl_dist;
+
+        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 < MAX_AMBI_ORDER+1;i++)
+            device->Dry.NumChannelsPerOrder[i] = 0;
     }
-    device->NumChannels = i;
 }
 
-static bool LoadChannelSetup(ALCdevice *device)
+static void InitDistanceComp(ALCdevice *device, const AmbDecConf *conf, const ALsizei speakermap[MAX_OUTPUT_CHANNELS])
 {
-    static const enum Channel mono_chans[1] = {
-        FrontCenter
-    }, stereo_chans[2] = {
-        FrontLeft, FrontRight
-    }, quad_chans[4] = {
-        FrontLeft, FrontRight,
-        BackLeft, BackRight
-    }, surround51_chans[5] = {
-        FrontLeft, FrontRight, FrontCenter,
-        SideLeft, SideRight
-    }, surround51rear_chans[5] = {
-        FrontLeft, FrontRight, FrontCenter,
-        BackLeft, BackRight
-    }, surround61_chans[6] = {
-        FrontLeft, FrontRight,
-        FrontCenter, BackCenter,
-        SideLeft, SideRight
-    }, surround71_chans[7] = {
-        FrontLeft, FrontRight, FrontCenter,
-        BackLeft, BackRight,
-        SideLeft, SideRight
-    };
-    ChannelMap chanmap[MAX_OUTPUT_CHANNELS];
-    const enum Channel *channels = NULL;
-    const char *layout = NULL;
-    ALfloat ambiscale = 1.0f;
-    size_t count = 0;
-    int isfuma;
-    int order;
-    size_t i;
+    const char *devname = alstr_get_cstr(device->DeviceName);
+    ALfloat maxdist = 0.0f;
+    ALsizei total = 0;
+    ALsizei i;
+
+    for(i = 0;i < conf->NumSpeakers;i++)
+        maxdist = maxf(maxdist, conf->Speakers[i].Distance);
+
+    if(GetConfigValueBool(devname, "decoder", "distance-comp", 1) && maxdist > 0.0f)
+    {
+        ALfloat srate = (ALfloat)device->Frequency;
+        for(i = 0;i < conf->NumSpeakers;i++)
+        {
+            ALsizei chan = speakermap[i];
+            ALfloat delay;
+
+            /* Distance compensation only delays in steps of the sample rate.
+             * This is a bit less accurate since the delay time falls to the
+             * nearest sample time, but it's far simpler as it doesn't have to
+             * deal with phase offsets. This means at 48khz, for instance, the
+             * distance delay will be in steps of about 7 millimeters.
+             */
+            delay = floorf((maxdist-conf->Speakers[i].Distance) / SPEEDOFSOUNDMETRESPERSEC *
+                           srate + 0.5f);
+            if(delay >= (ALfloat)MAX_DELAY_LENGTH)
+                ERR("Delay for speaker \"%s\" exceeds buffer length (%f >= %u)\n",
+                    alstr_get_cstr(conf->Speakers[i].Name), delay, MAX_DELAY_LENGTH);
+
+            device->ChannelDelay[chan].Length = (ALsizei)clampf(
+                delay, 0.0f, (ALfloat)(MAX_DELAY_LENGTH-1)
+            );
+            device->ChannelDelay[chan].Gain = conf->Speakers[i].Distance / maxdist;
+            TRACE("Channel %u \"%s\" distance compensation: %d samples, %f gain\n", chan,
+                  alstr_get_cstr(conf->Speakers[i].Name), device->ChannelDelay[chan].Length,
+                device->ChannelDelay[chan].Gain
+            );
+
+            /* Round up to the next 4th sample, so each channel buffer starts
+             * 16-byte aligned.
+             */
+            total += RoundUp(device->ChannelDelay[chan].Length, 4);
+        }
+    }
+
+    if(total > 0)
+    {
+        device->ChannelDelay[0].Buffer = al_calloc(16, total * sizeof(ALfloat));
+        for(i = 1;i < MAX_OUTPUT_CHANNELS;i++)
+        {
+            size_t len = RoundUp(device->ChannelDelay[i-1].Length, 4);
+            device->ChannelDelay[i].Buffer = device->ChannelDelay[i-1].Buffer + len;
+        }
+    }
+}
+
+static void InitPanning(ALCdevice *device)
+{
+    const ChannelMap *chanmap = NULL;
+    ALsizei coeffcount = 0;
+    ALsizei count = 0;
+    ALsizei i, j;
 
     switch(device->FmtChans)
     {
         case DevFmtMono:
-            layout = "mono";
-            channels = mono_chans;
-            count = COUNTOF(mono_chans);
+            count = COUNTOF(MonoCfg);
+            chanmap = MonoCfg;
+            coeffcount = 1;
             break;
+
         case DevFmtStereo:
-            layout = "stereo";
-            channels = stereo_chans;
-            count = COUNTOF(stereo_chans);
+            count = COUNTOF(StereoCfg);
+            chanmap = StereoCfg;
+            coeffcount = 4;
             break;
+
         case DevFmtQuad:
-            layout = "quad";
-            channels = quad_chans;
-            count = COUNTOF(quad_chans);
+            count = COUNTOF(QuadCfg);
+            chanmap = QuadCfg;
+            coeffcount = 4;
             break;
+
         case DevFmtX51:
-            layout = "surround51";
-            channels = surround51_chans;
-            count = COUNTOF(surround51_chans);
+            count = COUNTOF(X51SideCfg);
+            chanmap = X51SideCfg;
+            coeffcount = 9;
             break;
+
         case DevFmtX51Rear:
-            layout = "surround51rear";
-            channels = surround51rear_chans;
-            count = COUNTOF(surround51rear_chans);
+            count = COUNTOF(X51RearCfg);
+            chanmap = X51RearCfg;
+            coeffcount = 9;
             break;
+
         case DevFmtX61:
-            layout = "surround61";
-            channels = surround61_chans;
-            count = COUNTOF(surround61_chans);
+            count = COUNTOF(X61Cfg);
+            chanmap = X61Cfg;
+            coeffcount = 9;
             break;
+
         case DevFmtX71:
-            layout = "surround71";
-            channels = surround71_chans;
-            count = COUNTOF(surround71_chans);
+            count = COUNTOF(X71Cfg);
+            chanmap = X71Cfg;
+            coeffcount = 16;
             break;
-        case DevFmtBFormat3D:
+
+        case DevFmtAmbi3D:
             break;
     }
 
-    if(!layout)
-        return false;
-    else
+    if(device->FmtChans == DevFmtAmbi3D)
     {
-        char name[32] = {0};
-        const char *type;
-        char eol;
-
-        snprintf(name, sizeof(name), "%s/type", layout);
-        if(!ConfigValueStr(al_string_get_cstr(device->DeviceName), "layouts", name, &type))
-            return false;
+        const char *devname = alstr_get_cstr(device->DeviceName);
+        const ALsizei *acnmap = (device->AmbiLayout == AmbiLayout_FuMa) ? FuMa2ACN : ACN2ACN;
+        const ALfloat *n3dscale = (device->AmbiScale == AmbiNorm_FuMa) ? FuMa2N3DScale :
+                                  (device->AmbiScale == AmbiNorm_SN3D) ? SN3D2N3DScale :
+                                  /*(device->AmbiScale == AmbiNorm_N3D) ?*/ UnitScale;
+        ALfloat nfc_delay = 0.0f;
 
-        if(sscanf(type, " %31[^: ] : %d%c", name, &order, &eol) != 2)
+        count = (device->AmbiOrder == 3) ? 16 :
+                (device->AmbiOrder == 2) ? 9 :
+                (device->AmbiOrder == 1) ? 4 : 1;
+        for(i = 0;i < count;i++)
         {
-            ERR("Invalid type value '%s' (expected name:order) for layout %s\n", type, layout);
-            return false;
+            ALsizei acn = acnmap[i];
+            device->Dry.Ambi.Map[i].Scale = 1.0f/n3dscale[acn];
+            device->Dry.Ambi.Map[i].Index = acn;
         }
+        device->Dry.CoeffCount = 0;
+        device->Dry.NumChannels = count;
 
-        if(strcasecmp(name, "fuma") == 0)
-            isfuma = 1;
-        else if(strcasecmp(name, "n3d") == 0)
-            isfuma = 0;
+        if(device->AmbiOrder < 2)
+        {
+            device->FOAOut.Ambi = device->Dry.Ambi;
+            device->FOAOut.CoeffCount = device->Dry.CoeffCount;
+            device->FOAOut.NumChannels = 0;
+        }
         else
         {
-            ERR("Unhandled type name '%s' (expected FuMa or N3D) for layout %s\n", name, layout);
-            return false;
+            /* FOA output is always ACN+N3D for higher-order ambisonic output.
+             * The upsampler expects this and will convert it for output.
+             */
+            memset(&device->FOAOut.Ambi, 0, sizeof(device->FOAOut.Ambi));
+            for(i = 0;i < 4;i++)
+            {
+                device->FOAOut.Ambi.Map[i].Scale = 1.0f;
+                device->FOAOut.Ambi.Map[i].Index = i;
+            }
+            device->FOAOut.CoeffCount = 0;
+            device->FOAOut.NumChannels = 4;
+
+            ambiup_reset(device->AmbiUp, device);
         }
 
-        if(order == 3)
-            ambiscale = THIRD_ORDER_SCALE;
-        else if(order == 2)
-            ambiscale = SECOND_ORDER_SCALE;
-        else if(order == 1)
-            ambiscale = FIRST_ORDER_SCALE;
-        else if(order == 0)
-            ambiscale = ZERO_ORDER_SCALE;
-        else
+        if(ConfigValueFloat(devname, "decoder", "nfc-ref-delay", &nfc_delay) && nfc_delay > 0.0f)
         {
-            ERR("Unhandled type order %d (expected 0, 1, 2, or 3) for layout %s\n", order, layout);
-            return false;
+            nfc_delay = clampf(nfc_delay, 0.001f, 1000.0f);
+            InitNearFieldCtrl(device, nfc_delay * SPEEDOFSOUNDMETRESPERSEC,
+                              device->AmbiOrder, true);
         }
     }
+    else
+    {
+        ALfloat w_scale, xyz_scale;
 
-    for(i = 0;i < count;i++)
+        SetChannelMap(device->RealOut.ChannelName, device->Dry.Ambi.Coeffs,
+                      chanmap, count, &device->Dry.NumChannels);
+        device->Dry.CoeffCount = coeffcount;
+
+        w_scale = (device->Dry.CoeffCount > 9) ? W_SCALE2D_THIRD :
+                  (device->Dry.CoeffCount > 4) ? W_SCALE2D_SECOND : 1.0f;
+        xyz_scale = (device->Dry.CoeffCount > 9) ? XYZ_SCALE2D_THIRD :
+                    (device->Dry.CoeffCount > 4) ? XYZ_SCALE2D_SECOND : 1.0f;
+
+        memset(&device->FOAOut.Ambi, 0, sizeof(device->FOAOut.Ambi));
+        for(i = 0;i < device->Dry.NumChannels;i++)
+        {
+            device->FOAOut.Ambi.Coeffs[i][0] = device->Dry.Ambi.Coeffs[i][0] * w_scale;
+            for(j = 1;j < 4;j++)
+                device->FOAOut.Ambi.Coeffs[i][j] = device->Dry.Ambi.Coeffs[i][j] * xyz_scale;
+        }
+        device->FOAOut.CoeffCount = 4;
+        device->FOAOut.NumChannels = 0;
+    }
+    device->RealOut.NumChannels = 0;
+}
+
+static void InitCustomPanning(ALCdevice *device, const AmbDecConf *conf, const ALsizei speakermap[MAX_OUTPUT_CHANNELS])
+{
+    ChannelMap chanmap[MAX_OUTPUT_CHANNELS];
+    const ALfloat *coeff_scale = UnitScale;
+    ALfloat w_scale = 1.0f;
+    ALfloat xyz_scale = 1.0f;
+    ALsizei i, j;
+
+    if(conf->FreqBands != 1)
+        ERR("Basic renderer uses the high-frequency matrix as single-band (xover_freq = %.0fhz)\n",
+            conf->XOverFreq);
+
+    if((conf->ChanMask&AMBI_PERIPHONIC_MASK))
     {
-        float coeffs[MAX_AMBI_COEFFS] = {0.0f};
-        const char *channame;
-        char chanlayout[32];
-        const char *value;
-        int props = 0;
-        char eol = 0;
-        int j;
+        if(conf->ChanMask > 0x1ff)
+        {
+            w_scale = W_SCALE3D_THIRD;
+            xyz_scale = XYZ_SCALE3D_THIRD;
+        }
+        else if(conf->ChanMask > 0xf)
+        {
+            w_scale = W_SCALE3D_SECOND;
+            xyz_scale = XYZ_SCALE3D_SECOND;
+        }
+    }
+    else
+    {
+        if(conf->ChanMask > 0x1ff)
+        {
+            w_scale = W_SCALE2D_THIRD;
+            xyz_scale = XYZ_SCALE2D_THIRD;
+        }
+        else if(conf->ChanMask > 0xf)
+        {
+            w_scale = W_SCALE2D_SECOND;
+            xyz_scale = XYZ_SCALE2D_SECOND;
+        }
+    }
 
-        chanmap[i].ChanName = channels[i];
-        channame = GetLabelFromChannel(channels[i]);
+    if(conf->CoeffScale == ADS_SN3D)
+        coeff_scale = SN3D2N3DScale;
+    else if(conf->CoeffScale == ADS_FuMa)
+        coeff_scale = FuMa2N3DScale;
 
-        snprintf(chanlayout, sizeof(chanlayout), "%s/%s", layout, channame);
-        if(!ConfigValueStr(al_string_get_cstr(device->DeviceName), "layouts", chanlayout, &value))
+    for(i = 0;i < conf->NumSpeakers;i++)
+    {
+        ALsizei chan = speakermap[i];
+        ALfloat gain;
+        ALsizei k = 0;
+
+        for(j = 0;j < MAX_AMBI_COEFFS;j++)
+            chanmap[i].Config[j] = 0.0f;
+
+        chanmap[i].ChanName = device->RealOut.ChannelName[chan];
+        for(j = 0;j < MAX_AMBI_COEFFS;j++)
         {
-            ERR("Missing channel %s\n", channame);
-            return false;
+            if(j == 0) gain = conf->HFOrderGain[0];
+            else if(j == 1) gain = conf->HFOrderGain[1];
+            else if(j == 4) gain = conf->HFOrderGain[2];
+            else if(j == 9) gain = conf->HFOrderGain[3];
+            if((conf->ChanMask&(1<<j)))
+                chanmap[i].Config[j] = conf->HFMatrix[i][k++] / coeff_scale[j] * gain;
         }
-        if(order == 3)
-            props = sscanf(value, " %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %c",
-                &coeffs[0],  &coeffs[1],  &coeffs[2],  &coeffs[3],
-                &coeffs[4],  &coeffs[5],  &coeffs[6],  &coeffs[7],
-                &coeffs[8],  &coeffs[9],  &coeffs[10], &coeffs[11],
-                &coeffs[12], &coeffs[13], &coeffs[14], &coeffs[15],
-                &eol
-            );
-        else if(order == 2)
-            props = sscanf(value, " %f %f %f %f %f %f %f %f %f %c",
-                &coeffs[0], &coeffs[1], &coeffs[2],
-                &coeffs[3], &coeffs[4], &coeffs[5],
-                &coeffs[6], &coeffs[7], &coeffs[8],
-                &eol
-            );
-        else if(order == 1)
-            props = sscanf(value, " %f %f %f %f %c",
-                &coeffs[0], &coeffs[1],
-                &coeffs[2], &coeffs[3],
-                &eol
-            );
-        else if(order == 0)
-            props = sscanf(value, " %f %c", &coeffs[0], &eol);
-        if(props == 0)
+    }
+
+    SetChannelMap(device->RealOut.ChannelName, device->Dry.Ambi.Coeffs, chanmap,
+                  conf->NumSpeakers, &device->Dry.NumChannels);
+    device->Dry.CoeffCount = (conf->ChanMask > 0x1ff) ? 16 :
+                             (conf->ChanMask > 0xf) ? 9 : 4;
+
+    memset(&device->FOAOut.Ambi, 0, sizeof(device->FOAOut.Ambi));
+    for(i = 0;i < device->Dry.NumChannels;i++)
+    {
+        device->FOAOut.Ambi.Coeffs[i][0] = device->Dry.Ambi.Coeffs[i][0] * w_scale;
+        for(j = 1;j < 4;j++)
+            device->FOAOut.Ambi.Coeffs[i][j] = device->Dry.Ambi.Coeffs[i][j] * xyz_scale;
+    }
+    device->FOAOut.CoeffCount = 4;
+    device->FOAOut.NumChannels = 0;
+
+    device->RealOut.NumChannels = 0;
+
+    InitDistanceComp(device, conf, speakermap);
+}
+
+static void InitHQPanning(ALCdevice *device, const AmbDecConf *conf, const ALsizei speakermap[MAX_OUTPUT_CHANNELS])
+{
+    ALfloat avg_dist;
+    ALsizei count;
+    ALsizei i;
+
+    if((conf->ChanMask&AMBI_PERIPHONIC_MASK))
+    {
+        count = (conf->ChanMask > 0x1ff) ? 16 :
+                (conf->ChanMask > 0xf) ? 9 : 4;
+        for(i = 0;i < count;i++)
         {
-            ERR("Failed to parse option %s properties\n", chanlayout);
-            return false;
+            device->Dry.Ambi.Map[i].Scale = 1.0f;
+            device->Dry.Ambi.Map[i].Index = i;
         }
+    }
+    else
+    {
+        static const int map[MAX_AMBI2D_COEFFS] = { 0, 1, 3, 4, 8, 9, 15 };
 
-        if(props > (order+1)*(order+1))
+        count = (conf->ChanMask > 0x1ff) ? 7 :
+                (conf->ChanMask > 0xf) ? 5 : 3;
+        for(i = 0;i < count;i++)
         {
-            ERR("Excess elements in option %s (expected %d)\n", chanlayout, (order+1)*(order+1));
-            return false;
+            device->Dry.Ambi.Map[i].Scale = 1.0f;
+            device->Dry.Ambi.Map[i].Index = map[i];
         }
+    }
+    device->Dry.CoeffCount = 0;
+    device->Dry.NumChannels = count;
+
+    TRACE("Enabling %s-band %s-order%s ambisonic decoder\n",
+        (conf->FreqBands == 1) ? "single" : "dual",
+        (conf->ChanMask > 0xf) ? (conf->ChanMask > 0x1ff) ? "third" : "second" : "first",
+        (conf->ChanMask&AMBI_PERIPHONIC_MASK) ? " periphonic" : ""
+    );
+    bformatdec_reset(device->AmbiDecoder, conf, count, device->Frequency, speakermap);
 
-        for(j = 0;j < MAX_AMBI_COEFFS;++j)
-            chanmap[i].Config[j] = coeffs[j];
+    if(!(conf->ChanMask > 0xf))
+    {
+        device->FOAOut.Ambi = device->Dry.Ambi;
+        device->FOAOut.CoeffCount = device->Dry.CoeffCount;
+        device->FOAOut.NumChannels = 0;
     }
-    SetChannelMap(device, chanmap, count, ambiscale, isfuma);
-    return true;
+    else
+    {
+        memset(&device->FOAOut.Ambi, 0, sizeof(device->FOAOut.Ambi));
+        if((conf->ChanMask&AMBI_PERIPHONIC_MASK))
+        {
+            count = 4;
+            for(i = 0;i < count;i++)
+            {
+                device->FOAOut.Ambi.Map[i].Scale = 1.0f;
+                device->FOAOut.Ambi.Map[i].Index = i;
+            }
+        }
+        else
+        {
+            static const int map[3] = { 0, 1, 3 };
+            count = 3;
+            for(i = 0;i < count;i++)
+            {
+                device->FOAOut.Ambi.Map[i].Scale = 1.0f;
+                device->FOAOut.Ambi.Map[i].Index = map[i];
+            }
+        }
+        device->FOAOut.CoeffCount = 0;
+        device->FOAOut.NumChannels = count;
+    }
+
+    device->RealOut.NumChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
+
+    avg_dist = 0.0f;
+    for(i = 0;i < conf->NumSpeakers;i++)
+        avg_dist += conf->Speakers[i].Distance;
+    avg_dist /= (ALfloat)conf->NumSpeakers;
+    InitNearFieldCtrl(device, avg_dist,
+        (conf->ChanMask > 0x1ff) ? 3 : (conf->ChanMask > 0xf) ? 2 : 1,
+        !!(conf->ChanMask&AMBI_PERIPHONIC_MASK)
+    );
+
+    InitDistanceComp(device, conf, speakermap);
 }
 
-ALvoid aluInitPanning(ALCdevice *device)
+static void InitHrtfPanning(ALCdevice *device)
 {
-    /* NOTE: These decoder coefficients are using FuMa channel ordering and
-     * normalization, since that's what was produced by the Ambisonic Decoder
-     * Toolbox. SetChannelMap will convert them to N3D.
-     */
-    static const ChannelMap MonoCfg[1] = {
-        { FrontCenter, { 1.414213562f } },
-    }, StereoCfg[2] = {
-        { FrontLeft,   { 0.707106781f, 0.0f,  0.5f, 0.0f } },
-        { FrontRight,  { 0.707106781f, 0.0f, -0.5f, 0.0f } },
-    }, QuadCfg[4] = {
-        { FrontLeft,   { 0.353553f,  0.306184f,  0.306184f, 0.0f, 0.0f, 0.0f, 0.0f,  0.000000f,  0.117186f } },
-        { FrontRight,  { 0.353553f,  0.306184f, -0.306184f, 0.0f, 0.0f, 0.0f, 0.0f,  0.000000f, -0.117186f } },
-        { BackLeft,    { 0.353553f, -0.306184f,  0.306184f, 0.0f, 0.0f, 0.0f, 0.0f,  0.000000f, -0.117186f } },
-        { BackRight,   { 0.353553f, -0.306184f, -0.306184f, 0.0f, 0.0f, 0.0f, 0.0f,  0.000000f,  0.117186f } },
-    }, X51SideCfg[5] = {
-        { FrontLeft,   { 0.208954f,  0.212846f,  0.238350f, 0.0f, 0.0f, 0.0f, 0.0f, -0.017738f,  0.204014f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.051023f,  0.047490f } },
-        { FrontRight,  { 0.208954f,  0.212846f, -0.238350f, 0.0f, 0.0f, 0.0f, 0.0f, -0.017738f, -0.204014f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.051023f, -0.047490f } },
-        { FrontCenter, { 0.109403f,  0.179490f,  0.000000f, 0.0f, 0.0f, 0.0f, 0.0f,  0.142031f,  0.000000f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,  0.072024f,  0.000000f } },
-        { SideLeft,    { 0.470936f, -0.369626f,  0.349386f, 0.0f, 0.0f, 0.0f, 0.0f, -0.031375f, -0.058144f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.007119f, -0.043968f } },
-        { SideRight,   { 0.470936f, -0.369626f, -0.349386f, 0.0f, 0.0f, 0.0f, 0.0f, -0.031375f,  0.058144f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.007119f,  0.043968f } },
-    }, X51RearCfg[5] = {
-        { FrontLeft,   { 0.208954f,  0.212846f,  0.238350f, 0.0f, 0.0f, 0.0f, 0.0f, -0.017738f,  0.204014f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.051023f,  0.047490f } },
-        { FrontRight,  { 0.208954f,  0.212846f, -0.238350f, 0.0f, 0.0f, 0.0f, 0.0f, -0.017738f, -0.204014f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.051023f, -0.047490f } },
-        { FrontCenter, { 0.109403f,  0.179490f,  0.000000f, 0.0f, 0.0f, 0.0f, 0.0f,  0.142031f,  0.000000f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,  0.072024f,  0.000000f } },
-        { BackLeft,    { 0.470936f, -0.369626f,  0.349386f, 0.0f, 0.0f, 0.0f, 0.0f, -0.031375f, -0.058144f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.007119f, -0.043968f } },
-        { BackRight,   { 0.470936f, -0.369626f, -0.349386f, 0.0f, 0.0f, 0.0f, 0.0f, -0.031375f,  0.058144f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.007119f,  0.043968f } },
-    }, X61Cfg[6] = {
-        { FrontLeft,   { 0.167065f,  0.200583f,  0.172695f, 0.0f, 0.0f, 0.0f, 0.0f,  0.029855f,  0.186407f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.039241f,  0.068910f } },
-        { FrontRight,  { 0.167065f,  0.200583f, -0.172695f, 0.0f, 0.0f, 0.0f, 0.0f,  0.029855f, -0.186407f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.039241f, -0.068910f } },
-        { FrontCenter, { 0.109403f,  0.179490f,  0.000000f, 0.0f, 0.0f, 0.0f, 0.0f,  0.142031f,  0.000000f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,  0.072024f,  0.000000f } },
-        { BackCenter,  { 0.353556f, -0.461940f,  0.000000f, 0.0f, 0.0f, 0.0f, 0.0f,  0.165723f,  0.000000f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,  0.000000f,  0.000000f } },
-        { SideLeft,    { 0.289151f, -0.081301f,  0.401292f, 0.0f, 0.0f, 0.0f, 0.0f, -0.188208f, -0.071420f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,  0.010099f, -0.032897f } },
-        { SideRight,   { 0.289151f, -0.081301f, -0.401292f, 0.0f, 0.0f, 0.0f, 0.0f, -0.188208f,  0.071420f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,  0.010099f,  0.032897f } },
-    }, X71Cfg[7] = {
-        { FrontLeft,   { 0.167065f,  0.200583f,  0.172695f, 0.0f, 0.0f, 0.0f, 0.0f,  0.029855f,  0.186407f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.039241f,  0.068910f } },
-        { FrontRight,  { 0.167065f,  0.200583f, -0.172695f, 0.0f, 0.0f, 0.0f, 0.0f,  0.029855f, -0.186407f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.039241f, -0.068910f } },
-        { FrontCenter, { 0.109403f,  0.179490f,  0.000000f, 0.0f, 0.0f, 0.0f, 0.0f,  0.142031f,  0.000000f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,  0.072024f,  0.000000f } },
-        { BackLeft,    { 0.224752f, -0.295009f,  0.170325f, 0.0f, 0.0f, 0.0f, 0.0f,  0.105349f, -0.182473f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,  0.000000f,  0.065799f } },
-        { BackRight,   { 0.224752f, -0.295009f, -0.170325f, 0.0f, 0.0f, 0.0f, 0.0f,  0.105349f,  0.182473f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,  0.000000f, -0.065799f } },
-        { SideLeft,    { 0.224739f,  0.000000f,  0.340644f, 0.0f, 0.0f, 0.0f, 0.0f, -0.210697f,  0.000000f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,  0.000000f, -0.065795f } },
-        { SideRight,   { 0.224739f,  0.000000f, -0.340644f, 0.0f, 0.0f, 0.0f, 0.0f, -0.210697f,  0.000000f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,  0.000000f,  0.065795f } },
-    }, BFormat3D[4] = {
-        { BFormatW, { 1.0f, 0.0f, 0.0f, 0.0f } },
-        { BFormatX, { 0.0f, 1.0f, 0.0f, 0.0f } },
-        { BFormatY, { 0.0f, 0.0f, 1.0f, 0.0f } },
-        { BFormatZ, { 0.0f, 0.0f, 0.0f, 1.0f } },
+    /* NOTE: azimuth goes clockwise. */
+    static const ALfloat AmbiPoints[][2] = {
+        { DEG2RAD( 90.0f), DEG2RAD(   0.0f) },
+        { DEG2RAD( 35.0f), DEG2RAD( -45.0f) },
+        { DEG2RAD( 35.0f), DEG2RAD(  45.0f) },
+        { DEG2RAD( 35.0f), DEG2RAD( 135.0f) },
+        { DEG2RAD( 35.0f), DEG2RAD(-135.0f) },
+        { DEG2RAD(  0.0f), DEG2RAD(   0.0f) },
+        { DEG2RAD(  0.0f), DEG2RAD(  90.0f) },
+        { DEG2RAD(  0.0f), DEG2RAD( 180.0f) },
+        { DEG2RAD(  0.0f), DEG2RAD( -90.0f) },
+        { DEG2RAD(-35.0f), DEG2RAD( -45.0f) },
+        { DEG2RAD(-35.0f), DEG2RAD(  45.0f) },
+        { DEG2RAD(-35.0f), DEG2RAD( 135.0f) },
+        { DEG2RAD(-35.0f), DEG2RAD(-135.0f) },
+        { DEG2RAD(-90.0f), DEG2RAD(   0.0f) },
     };
-    const ChannelMap *chanmap = NULL;
-    ALfloat ambiscale = 1.0f;
-    size_t count = 0;
+    static const ALfloat AmbiMatrixFOA[][2][MAX_AMBI_COEFFS] = {
+        { { 1.88982237e-001f,  0.00000000e+000f,  1.90399923e-001f,  0.00000000e+000f }, { 7.14285714e-002f,  0.00000000e+000f,  1.24646009e-001f,  0.00000000e+000f } },
+        { { 1.88982237e-001f,  1.09057783e-001f,  1.09208910e-001f,  1.09057783e-001f }, { 7.14285714e-002f,  7.13950780e-002f,  7.14940135e-002f,  7.13950780e-002f } },
+        { { 1.88982237e-001f, -1.09057783e-001f,  1.09208910e-001f,  1.09057783e-001f }, { 7.14285714e-002f, -7.13950780e-002f,  7.14940135e-002f,  7.13950780e-002f } },
+        { { 1.88982237e-001f, -1.09057783e-001f,  1.09208910e-001f, -1.09057783e-001f }, { 7.14285714e-002f, -7.13950780e-002f,  7.14940135e-002f, -7.13950780e-002f } },
+        { { 1.88982237e-001f,  1.09057783e-001f,  1.09208910e-001f, -1.09057783e-001f }, { 7.14285714e-002f,  7.13950780e-002f,  7.14940135e-002f, -7.13950780e-002f } },
+        { { 1.88982237e-001f,  0.00000000e+000f,  0.00000000e+000f,  1.88281281e-001f }, { 7.14285714e-002f,  0.00000000e+000f,  0.00000000e+000f,  1.23259031e-001f } },
+        { { 1.88982237e-001f, -1.88281281e-001f,  0.00000000e+000f,  0.00000000e+000f }, { 7.14285714e-002f, -1.23259031e-001f,  0.00000000e+000f,  0.00000000e+000f } },
+        { { 1.88982237e-001f,  0.00000000e+000f,  0.00000000e+000f, -1.88281281e-001f }, { 7.14285714e-002f,  0.00000000e+000f,  0.00000000e+000f, -1.23259031e-001f } },
+        { { 1.88982237e-001f,  1.88281281e-001f,  0.00000000e+000f,  0.00000000e+000f }, { 7.14285714e-002f,  1.23259031e-001f,  0.00000000e+000f,  0.00000000e+000f } },
+        { { 1.88982237e-001f,  1.09057783e-001f, -1.09208910e-001f,  1.09057783e-001f }, { 7.14285714e-002f,  7.13950780e-002f, -7.14940135e-002f,  7.13950780e-002f } },
+        { { 1.88982237e-001f, -1.09057783e-001f, -1.09208910e-001f,  1.09057783e-001f }, { 7.14285714e-002f, -7.13950780e-002f, -7.14940135e-002f,  7.13950780e-002f } },
+        { { 1.88982237e-001f, -1.09057783e-001f, -1.09208910e-001f, -1.09057783e-001f }, { 7.14285714e-002f, -7.13950780e-002f, -7.14940135e-002f, -7.13950780e-002f } },
+        { { 1.88982237e-001f,  1.09057783e-001f, -1.09208910e-001f, -1.09057783e-001f }, { 7.14285714e-002f,  7.13950780e-002f, -7.14940135e-002f, -7.13950780e-002f } },
+        { { 1.88982237e-001f,  0.00000000e+000f, -1.90399923e-001f,  0.00000000e+000f }, { 7.14285714e-002f,  0.00000000e+000f, -1.24646009e-001f,  0.00000000e+000f } }
+    }, AmbiMatrixHOA[][2][MAX_AMBI_COEFFS] = {
+        { { 1.43315266e-001f,  0.00000000e+000f,  1.90399923e-001f,  0.00000000e+000f,  0.00000000e+000f,  0.00000000e+000f,  1.18020996e-001f,  0.00000000e+000f,  0.00000000e+000f }, { 7.26741039e-002f,  0.00000000e+000f,  1.24646009e-001f,  0.00000000e+000f,  0.00000000e+000f,  0.00000000e+000f,  1.49618920e-001f,  0.00000000e+000f,  0.00000000e+000f } },
+        { { 1.40852210e-001f,  1.09057783e-001f,  1.09208910e-001f,  1.09057783e-001f,  7.58818830e-002f,  7.66295578e-002f, -3.28314629e-004f,  7.66295578e-002f,  0.00000000e+000f }, { 7.14251066e-002f,  7.13950780e-002f,  7.14940135e-002f,  7.13950780e-002f,  9.61978444e-002f,  9.71456952e-002f, -4.16214759e-004f,  9.71456952e-002f,  0.00000000e+000f } },
+        { { 1.40852210e-001f, -1.09057783e-001f,  1.09208910e-001f,  1.09057783e-001f, -7.58818830e-002f, -7.66295578e-002f, -3.28314629e-004f,  7.66295578e-002f,  0.00000000e+000f }, { 7.14251066e-002f, -7.13950780e-002f,  7.14940135e-002f,  7.13950780e-002f, -9.61978444e-002f, -9.71456952e-002f, -4.16214759e-004f,  9.71456952e-002f,  0.00000000e+000f } },
+        { { 1.40852210e-001f, -1.09057783e-001f,  1.09208910e-001f, -1.09057783e-001f,  7.58818830e-002f, -7.66295578e-002f, -3.28314629e-004f, -7.66295578e-002f,  0.00000000e+000f }, { 7.14251066e-002f, -7.13950780e-002f,  7.14940135e-002f, -7.13950780e-002f,  9.61978444e-002f, -9.71456952e-002f, -4.16214759e-004f, -9.71456952e-002f,  0.00000000e+000f } },
+        { { 1.40852210e-001f,  1.09057783e-001f,  1.09208910e-001f, -1.09057783e-001f, -7.58818830e-002f,  7.66295578e-002f, -3.28314629e-004f, -7.66295578e-002f,  0.00000000e+000f }, { 7.14251066e-002f,  7.13950780e-002f,  7.14940135e-002f, -7.13950780e-002f, -9.61978444e-002f,  9.71456952e-002f, -4.16214759e-004f, -9.71456952e-002f,  0.00000000e+000f } },
+        { { 1.39644596e-001f,  0.00000000e+000f,  0.00000000e+000f,  1.88281281e-001f,  0.00000000e+000f,  0.00000000e+000f, -5.83538687e-002f,  0.00000000e+000f,  1.01835015e-001f }, { 7.08127349e-002f,  0.00000000e+000f,  0.00000000e+000f,  1.23259031e-001f,  0.00000000e+000f,  0.00000000e+000f, -7.39770307e-002f,  0.00000000e+000f,  1.29099445e-001f } },
+        { { 1.39644596e-001f, -1.88281281e-001f,  0.00000000e+000f,  0.00000000e+000f,  0.00000000e+000f,  0.00000000e+000f, -5.83538687e-002f,  0.00000000e+000f, -1.01835015e-001f }, { 7.08127349e-002f, -1.23259031e-001f,  0.00000000e+000f,  0.00000000e+000f,  0.00000000e+000f,  0.00000000e+000f, -7.39770307e-002f,  0.00000000e+000f, -1.29099445e-001f } },
+        { { 1.39644596e-001f,  0.00000000e+000f,  0.00000000e+000f, -1.88281281e-001f,  0.00000000e+000f,  0.00000000e+000f, -5.83538687e-002f,  0.00000000e+000f,  1.01835015e-001f }, { 7.08127349e-002f,  0.00000000e+000f,  0.00000000e+000f, -1.23259031e-001f,  0.00000000e+000f,  0.00000000e+000f, -7.39770307e-002f,  0.00000000e+000f,  1.29099445e-001f } },
+        { { 1.39644596e-001f,  1.88281281e-001f,  0.00000000e+000f,  0.00000000e+000f,  0.00000000e+000f,  0.00000000e+000f, -5.83538687e-002f,  0.00000000e+000f, -1.01835015e-001f }, { 7.08127349e-002f,  1.23259031e-001f,  0.00000000e+000f,  0.00000000e+000f,  0.00000000e+000f,  0.00000000e+000f, -7.39770307e-002f,  0.00000000e+000f, -1.29099445e-001f } },
+        { { 1.40852210e-001f,  1.09057783e-001f, -1.09208910e-001f,  1.09057783e-001f,  7.58818830e-002f, -7.66295578e-002f, -3.28314629e-004f, -7.66295578e-002f,  0.00000000e+000f }, { 7.14251066e-002f,  7.13950780e-002f, -7.14940135e-002f,  7.13950780e-002f,  9.61978444e-002f, -9.71456952e-002f, -4.16214759e-004f, -9.71456952e-002f,  0.00000000e+000f } },
+        { { 1.40852210e-001f, -1.09057783e-001f, -1.09208910e-001f,  1.09057783e-001f, -7.58818830e-002f,  7.66295578e-002f, -3.28314629e-004f, -7.66295578e-002f,  0.00000000e+000f }, { 7.14251066e-002f, -7.13950780e-002f, -7.14940135e-002f,  7.13950780e-002f, -9.61978444e-002f,  9.71456952e-002f, -4.16214759e-004f, -9.71456952e-002f,  0.00000000e+000f } },
+        { { 1.40852210e-001f, -1.09057783e-001f, -1.09208910e-001f, -1.09057783e-001f,  7.58818830e-002f,  7.66295578e-002f, -3.28314629e-004f,  7.66295578e-002f,  0.00000000e+000f }, { 7.14251066e-002f, -7.13950780e-002f, -7.14940135e-002f, -7.13950780e-002f,  9.61978444e-002f,  9.71456952e-002f, -4.16214759e-004f,  9.71456952e-002f,  0.00000000e+000f } },
+        { { 1.40852210e-001f,  1.09057783e-001f, -1.09208910e-001f, -1.09057783e-001f, -7.58818830e-002f, -7.66295578e-002f, -3.28314629e-004f,  7.66295578e-002f,  0.00000000e+000f }, { 7.14251066e-002f,  7.13950780e-002f, -7.14940135e-002f, -7.13950780e-002f, -9.61978444e-002f, -9.71456952e-002f, -4.16214759e-004f,  9.71456952e-002f,  0.00000000e+000f } },
+        { { 1.43315266e-001f,  0.00000000e+000f, -1.90399923e-001f,  0.00000000e+000f,  0.00000000e+000f,  0.00000000e+000f,  1.18020996e-001f,  0.00000000e+000f,  0.00000000e+000f }, { 7.26741039e-002f,  0.00000000e+000f, -1.24646009e-001f,  0.00000000e+000f,  0.00000000e+000f,  0.00000000e+000f,  1.49618920e-001f,  0.00000000e+000f,  0.00000000e+000f } },
+    };
+    const ALfloat (*AmbiMatrix)[2][MAX_AMBI_COEFFS] = device->AmbiUp ? AmbiMatrixHOA :
+                                                                       AmbiMatrixFOA;
+    ALsizei count = device->AmbiUp ? 9 : 4;
+    ALsizei i;
 
-    device->AmbiScale = 1.0f;
-    memset(device->AmbiCoeffs, 0, sizeof(device->AmbiCoeffs));
-    device->NumChannels = 0;
+    static_assert(COUNTOF(AmbiPoints) <= HRTF_AMBI_MAX_CHANNELS, "HRTF_AMBI_MAX_CHANNELS is too small");
 
-    if(device->Hrtf)
+    device->Hrtf = al_calloc(16, FAM_SIZE(DirectHrtfState, Chan, count));
+
+    for(i = 0;i < count;i++)
+    {
+        device->Dry.Ambi.Map[i].Scale = 1.0f;
+        device->Dry.Ambi.Map[i].Index = i;
+    }
+    device->Dry.CoeffCount = 0;
+    device->Dry.NumChannels = count;
+
+    if(device->AmbiUp)
     {
-        ALfloat (*coeffs_list[4])[2];
-        ALuint *delay_list[4];
-        ALuint i;
+        memset(&device->FOAOut.Ambi, 0, sizeof(device->FOAOut.Ambi));
+        for(i = 0;i < 4;i++)
+        {
+            device->FOAOut.Ambi.Map[i].Scale = 1.0f;
+            device->FOAOut.Ambi.Map[i].Index = i;
+        }
+        device->FOAOut.CoeffCount = 0;
+        device->FOAOut.NumChannels = 4;
 
-        count = COUNTOF(BFormat3D);
-        chanmap = BFormat3D;
-        ambiscale = 1.0f;
+        ambiup_reset(device->AmbiUp, device);
+    }
+    else
+    {
+        device->FOAOut.Ambi = device->Dry.Ambi;
+        device->FOAOut.CoeffCount = device->Dry.CoeffCount;
+        device->FOAOut.NumChannels = 0;
+    }
 
-        for(i = 0;i < count;i++)
-            device->ChannelName[i] = chanmap[i].ChanName;
-        for(;i < MAX_OUTPUT_CHANNELS;i++)
-            device->ChannelName[i] = InvalidChannel;
-        SetChannelMap(device, chanmap, count, ambiscale, AL_TRUE);
+    device->RealOut.NumChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
 
-        for(i = 0;i < 4;++i)
+    device->Hrtf->IrSize = BuildBFormatHrtf(device->HrtfHandle,
+        device->Hrtf, device->Dry.NumChannels,
+        AmbiPoints, AmbiMatrix, COUNTOF(AmbiPoints)
+    );
+}
+
+static void InitUhjPanning(ALCdevice *device)
+{
+    ALsizei count = 3;
+    ALsizei i;
+
+    for(i = 0;i < count;i++)
+    {
+        ALsizei acn = FuMa2ACN[i];
+        device->Dry.Ambi.Map[i].Scale = 1.0f/FuMa2N3DScale[acn];
+        device->Dry.Ambi.Map[i].Index = acn;
+    }
+    device->Dry.CoeffCount = 0;
+    device->Dry.NumChannels = count;
+
+    device->FOAOut.Ambi = device->Dry.Ambi;
+    device->FOAOut.CoeffCount = device->Dry.CoeffCount;
+    device->FOAOut.NumChannels = 0;
+
+    device->RealOut.NumChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
+}
+
+void aluInitRenderer(ALCdevice *device, ALint hrtf_id, enum HrtfRequestMode hrtf_appreq, enum HrtfRequestMode hrtf_userreq)
+{
+    /* Hold the HRTF the device last used, in case it's used again. */
+    struct Hrtf *old_hrtf = device->HrtfHandle;
+    const char *mode;
+    bool headphones;
+    int bs2blevel;
+    size_t i;
+
+    al_free(device->Hrtf);
+    device->Hrtf = NULL;
+    device->HrtfHandle = NULL;
+    alstr_clear(&device->HrtfName);
+    device->Render_Mode = NormalRender;
+
+    memset(&device->Dry.Ambi, 0, sizeof(device->Dry.Ambi));
+    device->Dry.CoeffCount = 0;
+    device->Dry.NumChannels = 0;
+    for(i = 0;i < MAX_AMBI_ORDER+1;i++)
+        device->Dry.NumChannelsPerOrder[i] = 0;
+
+    device->AvgSpeakerDist = 0.0f;
+    memset(device->ChannelDelay, 0, sizeof(device->ChannelDelay));
+    for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
+    {
+        device->ChannelDelay[i].Gain = 1.0f;
+        device->ChannelDelay[i].Length = 0;
+    }
+
+    if(device->FmtChans != DevFmtStereo)
+    {
+        ALsizei speakermap[MAX_OUTPUT_CHANNELS];
+        const char *devname, *layout = NULL;
+        AmbDecConf conf, *pconf = NULL;
+
+        if(old_hrtf)
+            Hrtf_DecRef(old_hrtf);
+        old_hrtf = NULL;
+        if(hrtf_appreq == Hrtf_Enable)
+            device->HrtfStatus = ALC_HRTF_UNSUPPORTED_FORMAT_SOFT;
+
+        ambdec_init(&conf);
+
+        devname = alstr_get_cstr(device->DeviceName);
+        switch(device->FmtChans)
         {
-            static const enum Channel inputs[4] = { BFormatW, BFormatX, BFormatY, BFormatZ };
-            int chan = GetChannelIdxByName(device, inputs[i]);
-            coeffs_list[i] = device->Hrtf_Params[chan].Coeffs;
-            delay_list[i] = device->Hrtf_Params[chan].Delay;
+            case DevFmtQuad: layout = "quad"; break;
+            case DevFmtX51: /* fall-through */
+            case DevFmtX51Rear: layout = "surround51"; break;
+            case DevFmtX61: layout = "surround61"; break;
+            case DevFmtX71: layout = "surround71"; break;
+            /* Mono, Stereo, and Ambisonics output don't use custom decoders. */
+            case DevFmtMono:
+            case DevFmtStereo:
+            case DevFmtAmbi3D:
+                break;
+        }
+        if(layout)
+        {
+            const char *fname;
+            if(ConfigValueStr(devname, "decoder", layout, &fname))
+            {
+                if(!ambdec_load(&conf, fname))
+                    ERR("Failed to load layout file %s\n", fname);
+                else
+                {
+                    if(conf.ChanMask > 0xffff)
+                        ERR("Unsupported channel mask 0x%04x (max 0xffff)\n", conf.ChanMask);
+                    else
+                    {
+                        if(MakeSpeakerMap(device, &conf, speakermap))
+                            pconf = &conf;
+                    }
+                }
+            }
         }
-        GetBFormatHrtfCoeffs(device->Hrtf, 4, coeffs_list, delay_list);
 
+        if(pconf && GetConfigValueBool(devname, "decoder", "hq-mode", 0))
+        {
+            ambiup_free(device->AmbiUp);
+            device->AmbiUp = NULL;
+            if(!device->AmbiDecoder)
+                device->AmbiDecoder = bformatdec_alloc();
+        }
+        else
+        {
+            bformatdec_free(device->AmbiDecoder);
+            device->AmbiDecoder = NULL;
+            if(device->FmtChans == DevFmtAmbi3D && device->AmbiOrder > 1)
+            {
+                if(!device->AmbiUp)
+                    device->AmbiUp = ambiup_alloc();
+            }
+            else
+            {
+                ambiup_free(device->AmbiUp);
+                device->AmbiUp = NULL;
+            }
+        }
+
+        if(!pconf)
+            InitPanning(device);
+        else if(device->AmbiDecoder)
+            InitHQPanning(device, pconf, speakermap);
+        else
+            InitCustomPanning(device, pconf, speakermap);
+
+        ambdec_deinit(&conf);
         return;
     }
 
-    if(LoadChannelSetup(device))
-        return;
+    bformatdec_free(device->AmbiDecoder);
+    device->AmbiDecoder = NULL;
 
-    switch(device->FmtChans)
+    headphones = device->IsHeadphones;
+    if(device->Type != Loopback)
     {
-        case DevFmtMono:
-            count = COUNTOF(MonoCfg);
-            chanmap = MonoCfg;
-            ambiscale = ZERO_ORDER_SCALE;
-            break;
+        const char *mode;
+        if(ConfigValueStr(alstr_get_cstr(device->DeviceName), NULL, "stereo-mode", &mode))
+        {
+            if(strcasecmp(mode, "headphones") == 0)
+                headphones = true;
+            else if(strcasecmp(mode, "speakers") == 0)
+                headphones = false;
+            else if(strcasecmp(mode, "auto") != 0)
+                ERR("Unexpected stereo-mode: %s\n", mode);
+        }
+    }
 
-        case DevFmtStereo:
-            count = COUNTOF(StereoCfg);
-            chanmap = StereoCfg;
-            ambiscale = FIRST_ORDER_SCALE;
-            break;
+    if(hrtf_userreq == Hrtf_Default)
+    {
+        bool usehrtf = (headphones && hrtf_appreq != Hrtf_Disable) ||
+                       (hrtf_appreq == Hrtf_Enable);
+        if(!usehrtf) goto no_hrtf;
 
-        case DevFmtQuad:
-            count = COUNTOF(QuadCfg);
-            chanmap = QuadCfg;
-            ambiscale = SECOND_ORDER_SCALE;
-            break;
+        device->HrtfStatus = ALC_HRTF_ENABLED_SOFT;
+        if(headphones && hrtf_appreq != Hrtf_Disable)
+            device->HrtfStatus = ALC_HRTF_HEADPHONES_DETECTED_SOFT;
+    }
+    else
+    {
+        if(hrtf_userreq != Hrtf_Enable)
+        {
+            if(hrtf_appreq == Hrtf_Enable)
+                device->HrtfStatus = ALC_HRTF_DENIED_SOFT;
+            goto no_hrtf;
+        }
+        device->HrtfStatus = ALC_HRTF_REQUIRED_SOFT;
+    }
 
-        case DevFmtX51:
-            count = COUNTOF(X51SideCfg);
-            chanmap = X51SideCfg;
-            ambiscale = THIRD_ORDER_SCALE;
-            break;
+    if(VECTOR_SIZE(device->HrtfList) == 0)
+    {
+        VECTOR_DEINIT(device->HrtfList);
+        device->HrtfList = EnumerateHrtf(device->DeviceName);
+    }
 
-        case DevFmtX51Rear:
-            count = COUNTOF(X51RearCfg);
-            chanmap = X51RearCfg;
-            ambiscale = THIRD_ORDER_SCALE;
-            break;
+    if(hrtf_id >= 0 && (size_t)hrtf_id < VECTOR_SIZE(device->HrtfList))
+    {
+        const EnumeratedHrtf *entry = &VECTOR_ELEM(device->HrtfList, hrtf_id);
+        struct Hrtf *hrtf = GetLoadedHrtf(entry->hrtf);
+        if(hrtf && hrtf->sampleRate == device->Frequency)
+        {
+            device->HrtfHandle = hrtf;
+            alstr_copy(&device->HrtfName, entry->name);
+        }
+        else if(hrtf)
+            Hrtf_DecRef(hrtf);
+    }
 
-        case DevFmtX61:
-            count = COUNTOF(X61Cfg);
-            chanmap = X61Cfg;
-            ambiscale = THIRD_ORDER_SCALE;
-            break;
+    for(i = 0;!device->HrtfHandle && i < VECTOR_SIZE(device->HrtfList);i++)
+    {
+        const EnumeratedHrtf *entry = &VECTOR_ELEM(device->HrtfList, i);
+        struct Hrtf *hrtf = GetLoadedHrtf(entry->hrtf);
+        if(hrtf && hrtf->sampleRate == device->Frequency)
+        {
+            device->HrtfHandle = hrtf;
+            alstr_copy(&device->HrtfName, entry->name);
+        }
+        else if(hrtf)
+            Hrtf_DecRef(hrtf);
+    }
 
-        case DevFmtX71:
-            count = COUNTOF(X71Cfg);
-            chanmap = X71Cfg;
-            ambiscale = THIRD_ORDER_SCALE;
-            break;
+    if(device->HrtfHandle)
+    {
+        if(old_hrtf)
+            Hrtf_DecRef(old_hrtf);
+        old_hrtf = NULL;
 
-        case DevFmtBFormat3D:
-            count = COUNTOF(BFormat3D);
-            chanmap = BFormat3D;
-            ambiscale = 1.0f;
-            break;
+        device->Render_Mode = HrtfRender;
+        if(ConfigValueStr(alstr_get_cstr(device->DeviceName), NULL, "hrtf-mode", &mode))
+        {
+            if(strcasecmp(mode, "full") == 0)
+                device->Render_Mode = HrtfRender;
+            else if(strcasecmp(mode, "basic") == 0)
+                device->Render_Mode = NormalRender;
+            else
+                ERR("Unexpected hrtf-mode: %s\n", mode);
+        }
+
+        if(device->Render_Mode == HrtfRender)
+        {
+            /* Don't bother with HOA when using full HRTF rendering. Nothing
+             * needs it, and it eases the CPU/memory load.
+             */
+            ambiup_free(device->AmbiUp);
+            device->AmbiUp = NULL;
+        }
+        else
+        {
+            if(!device->AmbiUp)
+                device->AmbiUp = ambiup_alloc();
+        }
+
+        TRACE("%s HRTF rendering enabled, using \"%s\"\n",
+            ((device->Render_Mode == HrtfRender) ? "Full" : "Basic"),
+            alstr_get_cstr(device->HrtfName)
+        );
+        InitHrtfPanning(device);
+        return;
+    }
+    device->HrtfStatus = ALC_HRTF_UNSUPPORTED_FORMAT_SOFT;
+
+no_hrtf:
+    if(old_hrtf)
+        Hrtf_DecRef(old_hrtf);
+    old_hrtf = NULL;
+    TRACE("HRTF disabled\n");
+
+    device->Render_Mode = StereoPair;
+
+    ambiup_free(device->AmbiUp);
+    device->AmbiUp = NULL;
+
+    bs2blevel = ((headphones && hrtf_appreq != Hrtf_Disable) ||
+                 (hrtf_appreq == Hrtf_Enable)) ? 5 : 0;
+    if(device->Type != Loopback)
+        ConfigValueInt(alstr_get_cstr(device->DeviceName), NULL, "cf_level", &bs2blevel);
+    if(bs2blevel > 0 && bs2blevel <= 6)
+    {
+        device->Bs2b = al_calloc(16, sizeof(*device->Bs2b));
+        bs2b_set_params(device->Bs2b, bs2blevel, device->Frequency);
+        TRACE("BS2B enabled\n");
+        InitPanning(device);
+        return;
     }
 
-    SetChannelMap(device, chanmap, count, ambiscale, AL_TRUE);
+    TRACE("BS2B disabled\n");
+
+    if(ConfigValueStr(alstr_get_cstr(device->DeviceName), NULL, "stereo-encoding", &mode))
+    {
+        if(strcasecmp(mode, "uhj") == 0)
+            device->Render_Mode = NormalRender;
+        else if(strcasecmp(mode, "panpot") != 0)
+            ERR("Unexpected stereo-encoding: %s\n", mode);
+    }
+    if(device->Render_Mode == NormalRender)
+    {
+        device->Uhj_Encoder = al_calloc(16, sizeof(Uhj2Encoder));
+        TRACE("UHJ enabled\n");
+        InitUhjPanning(device);
+        return;
+    }
+
+    TRACE("UHJ disabled\n");
+    InitPanning(device);
+}
+
+
+void aluInitEffectPanning(ALeffectslot *slot)
+{
+    ALsizei i;
+
+    memset(slot->ChanMap, 0, sizeof(slot->ChanMap));
+    slot->NumChannels = 0;
+
+    for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
+    {
+        slot->ChanMap[i].Scale = 1.0f;
+        slot->ChanMap[i].Index = i;
+    }
+    slot->NumChannels = i;
 }

+ 134 - 0
libs/openal-soft/Alc/uhjfilter.c

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

+ 49 - 0
libs/openal-soft/Alc/uhjfilter.h

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

+ 43 - 55
libs/openal-soft/Alc/vector.h

@@ -5,11 +5,8 @@
 
 #include <AL/al.h>
 
-/* "Base" vector type, designed to alias with the actual vector types. */
-typedef struct vector__s {
-    size_t Capacity;
-    size_t Size;
-} *vector_;
+#include "almalloc.h"
+
 
 #define TYPEDEF_VECTOR(T, N) typedef struct {                                 \
     size_t Capacity;                                                          \
@@ -27,38 +24,47 @@ typedef const _##N* const_##N;
 
 #define VECTOR_INIT(_x)       do { (_x) = NULL; } while(0)
 #define VECTOR_INIT_STATIC()  NULL
-#define VECTOR_DEINIT(_x)     do { free((_x)); (_x) = NULL; } while(0)
-
-/* Helper to increase a vector's reserve. Do not call directly. */
-ALboolean vector_reserve(char *ptr, size_t base_size, size_t obj_size, size_t obj_count, ALboolean exact);
-#define VECTOR_RESERVE(_x, _c) (vector_reserve((char*)&(_x), sizeof(*(_x)), sizeof((_x)->Data[0]), (_c), AL_TRUE))
-
-ALboolean vector_resize(char *ptr, size_t base_size, size_t obj_size, size_t obj_count);
-#define VECTOR_RESIZE(_x, _c) (vector_resize((char*)&(_x), sizeof(*(_x)), sizeof((_x)->Data[0]), (_c)))
+#define VECTOR_DEINIT(_x)     do { al_free((_x)); (_x) = NULL; } while(0)
+
+#define VECTOR_RESIZE(_x, _s, _c) do {                                        \
+    size_t _size = (_s);                                                      \
+    size_t _cap = (_c);                                                       \
+    if(_size > _cap)                                                          \
+        _cap = _size;                                                         \
+                                                                              \
+    if(!(_x) && _cap == 0)                                                    \
+        break;                                                                \
+                                                                              \
+    if(((_x) ? (_x)->Capacity : 0) < _cap)                                    \
+    {                                                                         \
+        ptrdiff_t data_offset = (char*)((_x)->Data) - (char*)(_x);            \
+        size_t old_size = ((_x) ? (_x)->Size : 0);                            \
+        void *temp;                                                           \
+                                                                              \
+        temp = al_calloc(16, data_offset + sizeof((_x)->Data[0])*_cap);       \
+        assert(temp != NULL);                                                 \
+        if((_x))                                                              \
+            memcpy(((char*)temp)+data_offset, (_x)->Data,                     \
+                   sizeof((_x)->Data[0])*old_size);                           \
+                                                                              \
+        al_free((_x));                                                        \
+        (_x) = temp;                                                          \
+        (_x)->Capacity = _cap;                                                \
+    }                                                                         \
+    (_x)->Size = _size;                                                       \
+} while(0)                                                                    \
 
 #define VECTOR_CAPACITY(_x) ((_x) ? (_x)->Capacity : 0)
 #define VECTOR_SIZE(_x)     ((_x) ? (_x)->Size : 0)
 
-#define VECTOR_ITER_BEGIN(_x) ((_x) ? (_x)->Data + 0 : NULL)
-#define VECTOR_ITER_END(_x)   ((_x) ? (_x)->Data + (_x)->Size : NULL)
-
-ALboolean vector_insert(char *ptr, size_t base_size, size_t obj_size, void *ins_pos, const void *datstart, const void *datend);
-#ifdef __GNUC__
-#define TYPE_CHECK(T1, T2) __builtin_types_compatible_p(T1, T2)
-#define VECTOR_INSERT(_x, _i, _s, _e) __extension__({                         \
-    ALboolean _r;                                                             \
-    static_assert(TYPE_CHECK(__typeof((_x)->Data[0]), __typeof(*(_i))), "Incompatible insertion iterator"); \
-    static_assert(TYPE_CHECK(__typeof((_x)->Data[0]), __typeof(*(_s))), "Incompatible insertion source type"); \
-    static_assert(TYPE_CHECK(__typeof(*(_s)), __typeof(*(_e))), "Incompatible iterator sources"); \
-    _r = vector_insert((char*)&(_x), sizeof(*(_x)), sizeof((_x)->Data[0]), (_i), (_s), (_e)); \
-    _r;                                                                       \
-})
-#else
-#define VECTOR_INSERT(_x, _i, _s, _e) (vector_insert((char*)&(_x), sizeof(*(_x)), sizeof((_x)->Data[0]), (_i), (_s), (_e)))
-#endif
-
-#define VECTOR_PUSH_BACK(_x, _obj) (vector_reserve((char*)&(_x), sizeof(*(_x)), sizeof((_x)->Data[0]), VECTOR_SIZE(_x)+1, AL_FALSE) && \
-                                    (((_x)->Data[(_x)->Size++] = (_obj)),AL_TRUE))
+#define VECTOR_BEGIN(_x) ((_x) ? (_x)->Data + 0 : NULL)
+#define VECTOR_END(_x)   ((_x) ? (_x)->Data + (_x)->Size : NULL)
+
+#define VECTOR_PUSH_BACK(_x, _obj) do {      \
+    size_t _pbsize = VECTOR_SIZE(_x)+1;      \
+    VECTOR_RESIZE(_x, _pbsize, _pbsize);     \
+    (_x)->Data[(_x)->Size-1] = (_obj);       \
+} while(0)
 #define VECTOR_POP_BACK(_x) ((void)((_x)->Size--))
 
 #define VECTOR_BACK(_x)  ((_x)->Data[(_x)->Size-1])
@@ -67,22 +73,15 @@ ALboolean vector_insert(char *ptr, size_t base_size, size_t obj_size, void *ins_
 #define VECTOR_ELEM(_x, _o) ((_x)->Data[(_o)])
 
 #define VECTOR_FOR_EACH(_t, _x, _f)  do {                                     \
-    _t *_iter = VECTOR_ITER_BEGIN((_x));                                      \
-    _t *_end = VECTOR_ITER_END((_x));                                         \
+    _t *_iter = VECTOR_BEGIN((_x));                                           \
+    _t *_end = VECTOR_END((_x));                                              \
     for(;_iter != _end;++_iter)                                               \
         _f(_iter);                                                            \
 } while(0)
 
-#define VECTOR_FOR_EACH_PARAMS(_t, _x, _f, ...)  do {                         \
-    _t *_iter = VECTOR_ITER_BEGIN((_x));                                      \
-    _t *_end = VECTOR_ITER_END((_x));                                         \
-    for(;_iter != _end;++_iter)                                               \
-        _f(__VA_ARGS__, _iter);                                               \
-} while(0)
-
 #define VECTOR_FIND_IF(_i, _t, _x, _f)  do {                                  \
-    _t *_iter = VECTOR_ITER_BEGIN((_x));                                      \
-    _t *_end = VECTOR_ITER_END((_x));                                         \
+    _t *_iter = VECTOR_BEGIN((_x));                                           \
+    _t *_end = VECTOR_END((_x));                                              \
     for(;_iter != _end;++_iter)                                               \
     {                                                                         \
         if(_f(_iter))                                                         \
@@ -91,15 +90,4 @@ ALboolean vector_insert(char *ptr, size_t base_size, size_t obj_size, void *ins_
     (_i) = _iter;                                                             \
 } while(0)
 
-#define VECTOR_FIND_IF_PARMS(_i, _t, _x, _f, ...)  do {                       \
-    _t *_iter = VECTOR_ITER_BEGIN((_x));                                      \
-    _t *_end = VECTOR_ITER_END((_x));                                         \
-    for(;_iter != _end;++_iter)                                               \
-    {                                                                         \
-        if(_f(__VA_ARGS__, _iter))                                            \
-            break;                                                            \
-    }                                                                         \
-    (_i) = _iter;                                                             \
-} while(0)
-
 #endif /* AL_VECTOR_H */

+ 458 - 253
libs/openal-soft/CMakeLists.txt

@@ -1,15 +1,21 @@
 # CMake build file list for OpenAL
 
-CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
+CMAKE_MINIMUM_REQUIRED(VERSION 3.0.2)
 
 PROJECT(OpenAL)
 
 IF(COMMAND CMAKE_POLICY)
     CMAKE_POLICY(SET CMP0003 NEW)
     CMAKE_POLICY(SET CMP0005 NEW)
+    IF(POLICY CMP0020)
+        CMAKE_POLICY(SET CMP0020 NEW)
+    ENDIF(POLICY CMP0020)
     IF(POLICY CMP0042)
         CMAKE_POLICY(SET CMP0042 NEW)
     ENDIF(POLICY CMP0042)
+    IF(POLICY CMP0054)
+        CMAKE_POLICY(SET CMP0054 NEW)
+    ENDIF(POLICY CMP0054)
 ENDIF(COMMAND CMAKE_POLICY)
 
 SET(CMAKE_MODULE_PATH "${OpenAL_SOURCE_DIR}/cmake")
@@ -21,10 +27,12 @@ INCLUDE(CheckIncludeFile)
 INCLUDE(CheckIncludeFiles)
 INCLUDE(CheckSymbolExists)
 INCLUDE(CheckCCompilerFlag)
+INCLUDE(CheckCXXCompilerFlag)
 INCLUDE(CheckCSourceCompiles)
 INCLUDE(CheckTypeSize)
+include(CheckStructHasMember)
 include(CheckFileOffsetBits)
-
+include(GNUInstallDirs)
 
 SET(CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS TRUE)
 
@@ -33,26 +41,32 @@ 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_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_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")
+    set(CMAKE_INSTALL_DATADIR "${SHARE_INSTALL_DIR}")
+endif()
 
-set(SHARE_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/share" CACHE STRING "The share install dir")
+if(DEFINED LIB_SUFFIX)
+    message(WARNING "LIB_SUFFIX is deprecated.  Use the variables provided by the GNUInstallDirs module instead")
+endif()
 
 
-IF(NOT WIN32)
-    SET(LIBNAME openal)
-ELSE()
-    SET(LIBNAME OpenAL32)
+IF(WIN32)
     ADD_DEFINITIONS("-D_WIN32 -D_WIN32_WINNT=0x0502")
 
+    OPTION(ALSOFT_BUILD_ROUTER  "Build the router (EXPERIMENTAL; creates OpenAL32.dll and soft_oal.dll)"  OFF)
+
     # This option is mainly for static linking OpenAL Soft into another project
     # that already defines the IDs. It is up to that project to ensure all
     # required IDs are defined.
@@ -71,20 +85,21 @@ ELSE()
 ENDIF()
 
 
+SET(EXTRA_LIBS "")
+SET(EXTRA_LDFLAGS "")
+
 # QNX's gcc do not uses /usr/include and /usr/lib pathes by default
 IF ("${CMAKE_C_PLATFORM_ID}" STREQUAL "QNX")
     ADD_DEFINITIONS("-I/usr/include")
-    SET(EXTRA_LIBS ${EXTRA_LIBS} -L/usr/lib)
+    SET(EXTRA_LDFLAGS "${EXTRA_LDFLAGS} -L/usr/lib")
 ENDIF()
 
-SET(OPENAL_LIB_NAME ${LIBNAME} PARENT_SCOPE)
-
 IF(NOT LIBTYPE)
     SET(LIBTYPE SHARED)
 ENDIF()
 
 SET(LIB_MAJOR_VERSION "1")
-SET(LIB_MINOR_VERSION "17")
+SET(LIB_MINOR_VERSION "18")
 SET(LIB_REVISION "2")
 SET(LIB_VERSION "${LIB_MAJOR_VERSION}.${LIB_MINOR_VERSION}.${LIB_REVISION}")
 
@@ -106,17 +121,22 @@ ELSE()
     ENDIF()
 ENDIF()
 
+CHECK_CXX_COMPILER_FLAG(-std=c++11 HAVE_STD_CXX11)
+IF(HAVE_STD_CXX11)
+    SET(CMAKE_CXX_FLAGS "-std=c++11 ${CMAKE_CXX_FLAGS}")
+ENDIF()
+
 if(NOT WIN32)
     # Check if _POSIX_C_SOURCE and _XOPEN_SOURCE needs to be set for POSIX functions
     CHECK_SYMBOL_EXISTS(posix_memalign stdlib.h HAVE_POSIX_MEMALIGN_DEFAULT)
     IF(NOT HAVE_POSIX_MEMALIGN_DEFAULT)
         SET(OLD_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS})
-        SET(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -D_POSIX_C_SOURCE=200112L -D_XOPEN_SOURCE=500")
+        SET(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -D_POSIX_C_SOURCE=200112L -D_XOPEN_SOURCE=600")
         CHECK_SYMBOL_EXISTS(posix_memalign stdlib.h HAVE_POSIX_MEMALIGN_POSIX)
         IF(NOT HAVE_POSIX_MEMALIGN_POSIX)
             SET(CMAKE_REQUIRED_FLAGS ${OLD_REQUIRED_FLAGS})
         ELSE()
-            ADD_DEFINITIONS(-D_POSIX_C_SOURCE=200112L -D_XOPEN_SOURCE=500)
+            ADD_DEFINITIONS(-D_POSIX_C_SOURCE=200112L -D_XOPEN_SOURCE=600)
         ENDIF()
     ENDIF()
     UNSET(OLD_REQUIRED_FLAGS)
@@ -158,6 +178,8 @@ ENDIF()
 
 # Make sure we have C99-style inline semantics with GCC (4.3 or newer).
 IF(CMAKE_COMPILER_IS_GNUCC)
+    SET(CMAKE_C_FLAGS "-fno-gnu89-inline ${CMAKE_C_FLAGS}")
+
     SET(OLD_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}")
     # Force no inlining for the next test.
     SET(CMAKE_REQUIRED_FLAGS "${OLD_REQUIRED_FLAGS} -fno-inline")
@@ -172,6 +194,37 @@ IF(CMAKE_COMPILER_IS_GNUCC)
     SET(CMAKE_REQUIRED_FLAGS "${OLD_REQUIRED_FLAGS}")
 ENDIF()
 
+# Check if we have a proper timespec declaration
+CHECK_STRUCT_HAS_MEMBER("struct timespec" tv_sec time.h HAVE_STRUCT_TIMESPEC)
+IF(HAVE_STRUCT_TIMESPEC)
+    # Define it here so we don't have to include config.h for it
+    ADD_DEFINITIONS("-DHAVE_STRUCT_TIMESPEC")
+ENDIF()
+
+# Some systems may need libatomic for C11 atomic functions to work
+SET(OLD_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES})
+SET(CMAKE_REQUIRED_LIBRARIES ${OLD_REQUIRED_LIBRARIES} atomic)
+CHECK_C_SOURCE_COMPILES("#include <stdatomic.h>
+int _Atomic foo = ATOMIC_VAR_INIT(0);
+int main()
+{
+    return atomic_fetch_add(&foo, 2);
+}"
+HAVE_LIBATOMIC)
+IF(NOT HAVE_LIBATOMIC)
+    SET(CMAKE_REQUIRED_LIBRARIES "${OLD_REQUIRED_LIBRARIES}")
+ELSE()
+    SET(EXTRA_LIBS atomic ${EXTRA_LIBS})
+ENDIF()
+UNSET(OLD_REQUIRED_LIBRARIES)
+
+# Include liblog for Android logging
+CHECK_LIBRARY_EXISTS(log __android_log_print "" HAVE_LIBLOG)
+IF(HAVE_LIBLOG)
+    SET(EXTRA_LIBS log ${EXTRA_LIBS})
+    SET(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} log)
+ENDIF()
+
 # Check if we have C99 variable length arrays
 CHECK_C_SOURCE_COMPILES(
 "int main(int argc, char *argv[])
@@ -213,21 +266,16 @@ HAVE_C11_ALIGNAS)
 # Check if we have C11 _Atomic
 CHECK_C_SOURCE_COMPILES(
 "#include <stdatomic.h>
- const int _Atomic foo = ATOMIC_VAR_INIT(~0);
+ int _Atomic foo = ATOMIC_VAR_INIT(0);
  int main()
  {
-     return atomic_load(&foo);
+     atomic_fetch_add(&foo, 2);
+     return 0;
  }"
 HAVE_C11_ATOMIC)
 
 # Add definitions, compiler switches, etc.
-INCLUDE_DIRECTORIES("${OpenAL_SOURCE_DIR}/include" "${OpenAL_BINARY_DIR}")
-IF(CMAKE_VERSION VERSION_LESS "2.8.8")
-    INCLUDE_DIRECTORIES("${OpenAL_SOURCE_DIR}/OpenAL32/Include" "${OpenAL_SOURCE_DIR}/Alc")
-    IF(WIN32 AND ALSOFT_NO_UID_DEFS)
-        ADD_DEFINITIONS("-DAL_NO_UID_DEFS")
-    ENDIF()
-ENDIF()
+INCLUDE_DIRECTORIES("${OpenAL_SOURCE_DIR}/include" "${OpenAL_SOURCE_DIR}/common" "${OpenAL_BINARY_DIR}")
 
 IF(NOT CMAKE_BUILD_TYPE)
     SET(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING
@@ -240,10 +288,11 @@ IF(NOT CMAKE_DEBUG_POSTFIX)
         FORCE)
 ENDIF()
 
+SET(EXTRA_CFLAGS "")
 IF(MSVC)
     ADD_DEFINITIONS(-D_CRT_SECURE_NO_WARNINGS)
     ADD_DEFINITIONS(-D_CRT_NONSTDC_NO_DEPRECATE)
-    ADD_DEFINITIONS("/wd4098")
+    SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} /wd4098")
 
     IF(NOT DXSDK_DIR)
         STRING(REGEX REPLACE "\\\\" "/" DXSDK_DIR "$ENV{DXSDK_DIR}")
@@ -265,25 +314,14 @@ IF(MSVC)
         ENDFOREACH(flag_var)
     ENDIF()
 ELSE()
-    ADD_DEFINITIONS(-Winline -Wall)
+    SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -Winline -Wall")
     CHECK_C_COMPILER_FLAG(-Wextra HAVE_W_EXTRA)
     IF(HAVE_W_EXTRA)
-        ADD_DEFINITIONS(-Wextra)
+        SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -Wextra")
     ENDIF()
 
     IF(ALSOFT_WERROR)
-        ADD_DEFINITIONS(-Werror)
-    ENDIF()
-
-    # Force enable -fPIC for CMake versions before 2.8.9 (later versions have
-    # the POSITION_INDEPENDENT_CODE target property). The static common library
-    # will be linked into the dynamic openal library, which requires all its
-    # code to be position-independent.
-    IF(CMAKE_VERSION VERSION_LESS "2.8.9" AND NOT WIN32)
-        CHECK_C_COMPILER_FLAG(-fPIC HAVE_FPIC_SWITCH)
-        IF(HAVE_FPIC_SWITCH)
-            ADD_DEFINITIONS(-fPIC)
-        ENDIF()
+        SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -Werror")
     ENDIF()
 
     # We want RelWithDebInfo to actually include debug stuff (define _DEBUG
@@ -294,6 +332,11 @@ ELSE()
         ENDIF()
     ENDFOREACH()
 
+    CHECK_C_COMPILER_FLAG(-fno-math-errno HAVE_FNO_MATH_ERRNO)
+    IF(HAVE_FNO_MATH_ERRNO)
+        SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -fno-math-errno")
+    ENDIF()
+
     CHECK_C_SOURCE_COMPILES("int foo() __attribute__((destructor));
                              int main() {return 0;}" HAVE_GCC_DESTRUCTOR)
 
@@ -310,7 +353,7 @@ int main()
             HAVE_STATIC_LIBGCC_SWITCH
         )
         if(HAVE_STATIC_LIBGCC_SWITCH)
-            set(EXTRA_LIBS ${EXTRA_LIBS} -static-libgcc)
+            set(EXTRA_LDFLAGS "${EXTRA_LDFLAGS} -static-libgcc")
         endif()
         set(CMAKE_REQUIRED_LIBRARIES ${OLD_REQUIRED_LIBRARIES})
         unset(OLD_REQUIRED_LIBRARIES)
@@ -345,7 +388,7 @@ ELSE()
     IF(HAVE_GCC_PROTECTED_VISIBILITY OR HAVE_GCC_DEFAULT_VISIBILITY)
         CHECK_C_COMPILER_FLAG(-fvisibility=hidden HAVE_VISIBILITY_HIDDEN_SWITCH)
         IF(HAVE_VISIBILITY_HIDDEN_SWITCH)
-            ADD_DEFINITIONS(-fvisibility=hidden)
+            SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -fvisibility=hidden")
         ENDIF()
     ENDIF()
 
@@ -358,27 +401,44 @@ ELSE()
     SET(CMAKE_REQUIRED_FLAGS "${OLD_REQUIRED_FLAGS}")
 ENDIF()
 
+CHECK_C_SOURCE_COMPILES("
+int main()
+{
+    float *ptr;
+    ptr = __builtin_assume_aligned(ptr, 16);
+    return 0;
+}" HAVE___BUILTIN_ASSUME_ALIGNED)
+IF(HAVE___BUILTIN_ASSUME_ALIGNED)
+    SET(ASSUME_ALIGNED_DECL "__builtin_assume_aligned(x, y)")
+ELSE()
+    SET(ASSUME_ALIGNED_DECL "x")
+ENDIF()
+
 SET(SSE_SWITCH "")
 SET(SSE2_SWITCH "")
 SET(SSE3_SWITCH "")
 SET(SSE4_1_SWITCH "")
-IF(NOT MSVC)
-    CHECK_C_COMPILER_FLAG(-msse HAVE_MSSE_SWITCH)
-    IF(HAVE_MSSE_SWITCH)
-        SET(SSE_SWITCH "-msse")
-    ENDIF()
-    CHECK_C_COMPILER_FLAG(-msse2 HAVE_MSSE2_SWITCH)
-    IF(HAVE_MSSE2_SWITCH)
-        SET(SSE2_SWITCH "-msse2")
-    ENDIF()
-    CHECK_C_COMPILER_FLAG(-msse3 HAVE_MSSE3_SWITCH)
-    IF(HAVE_MSSE3_SWITCH)
-        SET(SSE3_SWITCH "-msse3")
-    ENDIF()
-    CHECK_C_COMPILER_FLAG(-msse4.1 HAVE_MSSE4_1_SWITCH)
-    IF(HAVE_MSSE4_1_SWITCH)
-        SET(SSE4_1_SWITCH "-msse4.1")
-    ENDIF()
+SET(FPU_NEON_SWITCH "")
+
+CHECK_C_COMPILER_FLAG(-msse HAVE_MSSE_SWITCH)
+IF(HAVE_MSSE_SWITCH)
+    SET(SSE_SWITCH "-msse")
+ENDIF()
+CHECK_C_COMPILER_FLAG(-msse2 HAVE_MSSE2_SWITCH)
+IF(HAVE_MSSE2_SWITCH)
+    SET(SSE2_SWITCH "-msse2")
+ENDIF()
+CHECK_C_COMPILER_FLAG(-msse3 HAVE_MSSE3_SWITCH)
+IF(HAVE_MSSE3_SWITCH)
+    SET(SSE3_SWITCH "-msse3")
+ENDIF()
+CHECK_C_COMPILER_FLAG(-msse4.1 HAVE_MSSE4_1_SWITCH)
+IF(HAVE_MSSE4_1_SWITCH)
+    SET(SSE4_1_SWITCH "-msse4.1")
+ENDIF()
+CHECK_C_COMPILER_FLAG(-mfpu=neon HAVE_MFPU_NEON_SWITCH)
+IF(HAVE_MFPU_NEON_SWITCH)
+    SET(FPU_NEON_SWITCH "-mfpu=neon")
 ENDIF()
 
 CHECK_C_SOURCE_COMPILES("int foo(const char *str, ...) __attribute__((format(printf, 1, 2)));
@@ -442,6 +502,7 @@ CHECK_SYMBOL_EXISTS(posix_memalign   stdlib.h HAVE_POSIX_MEMALIGN)
 CHECK_SYMBOL_EXISTS(_aligned_malloc  malloc.h HAVE__ALIGNED_MALLOC)
 CHECK_SYMBOL_EXISTS(lrintf math.h HAVE_LRINTF)
 CHECK_SYMBOL_EXISTS(modff  math.h HAVE_MODFF)
+CHECK_SYMBOL_EXISTS(log2f  math.h HAVE_LOG2F)
 IF(NOT HAVE_C99_VLA)
     CHECK_SYMBOL_EXISTS(alloca malloc.h HAVE_ALLOCA)
     IF(NOT HAVE_ALLOCA)
@@ -535,9 +596,9 @@ IF(NOT HAVE_WINDOWS_H)
 
     CHECK_C_COMPILER_FLAG(-pthread HAVE_PTHREAD)
     IF(HAVE_PTHREAD)
-        ADD_DEFINITIONS(-pthread)
+        SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -pthread")
         SET(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -pthread")
-        SET(EXTRA_LIBS ${EXTRA_LIBS} -pthread)
+        SET(EXTRA_LDFLAGS "${EXTRA_LDFLAGS} -pthread")
     ENDIF()
 
     CHECK_LIBRARY_EXISTS(pthread pthread_create "" HAVE_LIBPTHREAD)
@@ -562,6 +623,16 @@ int main()
 }"
                 PTHREAD_SETNAME_NP_ONE_PARAM
             )
+            CHECK_C_SOURCE_COMPILES("
+#include <pthread.h>
+#include <pthread_np.h>
+int main()
+{
+    pthread_setname_np(pthread_self(), \"%s\", \"testname\");
+    return 0;
+}"
+                PTHREAD_SETNAME_NP_THREE_PARAMS
+            )
         ENDIF()
         CHECK_SYMBOL_EXISTS(pthread_mutexattr_setkind_np "pthread.h;pthread_np.h" HAVE_PTHREAD_MUTEXATTR_SETKIND_NP)
     ELSE()
@@ -578,6 +649,15 @@ int main()
 }"
                 PTHREAD_SETNAME_NP_ONE_PARAM
             )
+            CHECK_C_SOURCE_COMPILES("
+#include <pthread.h>
+int main()
+{
+    pthread_setname_np(pthread_self(), \"%s\", \"testname\");
+    return 0;
+}"
+                PTHREAD_SETNAME_NP_THREE_PARAMS
+            )
         ENDIF()
         CHECK_SYMBOL_EXISTS(pthread_mutexattr_setkind_np pthread.h HAVE_PTHREAD_MUTEXATTR_SETKIND_NP)
     ENDIF()
@@ -609,7 +689,8 @@ IF(NOT HAVE_STDINT_H)
 ENDIF()
 
 
-SET(COMMON_OBJS  common/atomic.c
+SET(COMMON_OBJS  common/almalloc.c
+                 common/atomic.c
                  common/rwlock.c
                  common/threads.c
                  common/uintmap.c
@@ -631,7 +712,8 @@ SET(ALC_OBJS  Alc/ALc.c
               Alc/alcConfig.c
               Alc/alcRing.c
               Alc/bs2b.c
-              Alc/effects/autowah.c
+              Alc/converter.c
+              Alc/mastering.c
               Alc/effects/chorus.c
               Alc/effects/compressor.c
               Alc/effects/dedicated.c
@@ -645,6 +727,10 @@ SET(ALC_OBJS  Alc/ALc.c
               Alc/helpers.c
               Alc/bsinc.c
               Alc/hrtf.c
+              Alc/uhjfilter.c
+              Alc/ambdec.c
+              Alc/bformatdec.c
+              Alc/nfcfilter.c
               Alc/panning.c
               Alc/mixer.c
               Alc/mixer_c.c
@@ -761,6 +847,10 @@ IF(HAVE_ARM_NEON_H)
     IF(ALSOFT_CPUEXT_NEON)
         SET(HAVE_NEON 1)
         SET(ALC_OBJS  ${ALC_OBJS} Alc/mixer_neon.c)
+        IF(FPU_NEON_SWITCH)
+            SET_SOURCE_FILES_PROPERTIES(Alc/mixer_neon.c PROPERTIES
+                                        COMPILE_FLAGS "${FPU_NEON_SWITCH}")
+        ENDIF()
         SET(CPU_EXTS "${CPU_EXTS}, Neon")
     ENDIF()
 ENDIF()
@@ -798,9 +888,6 @@ IF(ALSA_FOUND)
         SET(BACKENDS  "${BACKENDS} ALSA${IS_LINKED},")
         SET(ALC_OBJS  ${ALC_OBJS} Alc/backends/alsa.c)
         ADD_BACKEND_LIBS(${ALSA_LIBRARIES})
-        IF(CMAKE_VERSION VERSION_LESS "2.8.8")
-            INCLUDE_DIRECTORIES(${ALSA_INCLUDE_DIRS})
-        ENDIF()
     ENDIF()
 ENDIF()
 IF(ALSOFT_REQUIRE_ALSA AND NOT HAVE_ALSA)
@@ -816,8 +903,8 @@ IF(OSS_FOUND)
         SET(HAVE_OSS 1)
         SET(BACKENDS  "${BACKENDS} OSS,")
         SET(ALC_OBJS  ${ALC_OBJS} Alc/backends/oss.c)
-        IF(CMAKE_VERSION VERSION_LESS "2.8.8")
-            INCLUDE_DIRECTORIES(${OSS_INCLUDE_DIRS})
+        IF(OSS_LIBRARIES)
+            SET(EXTRA_LIBS ${OSS_LIBRARIES} ${EXTRA_LIBS})
         ENDIF()
     ENDIF()
 ENDIF()
@@ -834,9 +921,6 @@ IF(AUDIOIO_FOUND)
         SET(HAVE_SOLARIS 1)
         SET(BACKENDS  "${BACKENDS} Solaris,")
         SET(ALC_OBJS  ${ALC_OBJS} Alc/backends/solaris.c)
-        IF(CMAKE_VERSION VERSION_LESS "2.8.8")
-            INCLUDE_DIRECTORIES(${AUDIOIO_INCLUDE_DIRS})
-        ENDIF()
     ENDIF()
 ENDIF()
 IF(ALSOFT_REQUIRE_SOLARIS AND NOT HAVE_SOLARIS)
@@ -853,9 +937,6 @@ IF(SOUNDIO_FOUND)
         SET(BACKENDS  "${BACKENDS} SndIO (linked),")
         SET(ALC_OBJS  ${ALC_OBJS} Alc/backends/sndio.c)
         SET(EXTRA_LIBS ${SOUNDIO_LIBRARIES} ${EXTRA_LIBS})
-        IF(CMAKE_VERSION VERSION_LESS "2.8.8")
-            INCLUDE_DIRECTORIES(${SOUNDIO_INCLUDE_DIRS})
-        ENDIF()
     ENDIF()
 ENDIF()
 IF(ALSOFT_REQUIRE_SNDIO AND NOT HAVE_SNDIO)
@@ -872,9 +953,6 @@ IF(QSA_FOUND)
         SET(BACKENDS  "${BACKENDS} QSA (linked),")
         SET(ALC_OBJS  ${ALC_OBJS} Alc/backends/qsa.c)
         SET(EXTRA_LIBS ${QSA_LIBRARIES} ${EXTRA_LIBS})
-        IF(CMAKE_VERSION VERSION_LESS "2.8.8")
-            INCLUDE_DIRECTORIES(${QSA_INCLUDE_DIRS})
-        ENDIF()
     ENDIF()
 ENDIF()
 IF(ALSOFT_REQUIRE_QSA AND NOT HAVE_QSA)
@@ -910,9 +988,6 @@ IF(HAVE_WINDOWS_H)
             SET(BACKENDS  "${BACKENDS} DirectSound${IS_LINKED},")
             SET(ALC_OBJS  ${ALC_OBJS} Alc/backends/dsound.c)
             ADD_BACKEND_LIBS(${DSOUND_LIBRARIES})
-            IF(CMAKE_VERSION VERSION_LESS "2.8.8")
-                INCLUDE_DIRECTORIES(${DSOUND_INCLUDE_DIRS})
-            ENDIF()
         ENDIF()
     ENDIF()
 
@@ -947,9 +1022,6 @@ IF(PORTAUDIO_FOUND)
         SET(BACKENDS  "${BACKENDS} PortAudio${IS_LINKED},")
         SET(ALC_OBJS  ${ALC_OBJS} Alc/backends/portaudio.c)
         ADD_BACKEND_LIBS(${PORTAUDIO_LIBRARIES})
-        IF(CMAKE_VERSION VERSION_LESS "2.8.8")
-            INCLUDE_DIRECTORIES(${PORTAUDIO_INCLUDE_DIRS})
-        ENDIF()
     ENDIF()
 ENDIF()
 IF(ALSOFT_REQUIRE_PORTAUDIO AND NOT HAVE_PORTAUDIO)
@@ -966,9 +1038,6 @@ IF(PULSEAUDIO_FOUND)
         SET(BACKENDS  "${BACKENDS} PulseAudio${IS_LINKED},")
         SET(ALC_OBJS  ${ALC_OBJS} Alc/backends/pulseaudio.c)
         ADD_BACKEND_LIBS(${PULSEAUDIO_LIBRARIES})
-        IF(CMAKE_VERSION VERSION_LESS "2.8.8")
-            INCLUDE_DIRECTORIES(${PULSEAUDIO_INCLUDE_DIRS})
-        ENDIF()
     ENDIF()
 ENDIF()
 IF(ALSOFT_REQUIRE_PULSEAUDIO AND NOT HAVE_PULSEAUDIO)
@@ -985,9 +1054,6 @@ IF(JACK_FOUND)
         SET(BACKENDS  "${BACKENDS} JACK${IS_LINKED},")
         SET(ALC_OBJS  ${ALC_OBJS} Alc/backends/jack.c)
         ADD_BACKEND_LIBS(${JACK_LIBRARIES})
-        IF(CMAKE_VERSION VERSION_LESS "2.8.8")
-            INCLUDE_DIRECTORIES(${JACK_INCLUDE_DIRS})
-        ENDIF()
     ENDIF()
 ENDIF()
 IF(ALSOFT_REQUIRE_JACK AND NOT HAVE_JACK)
@@ -1057,6 +1123,40 @@ ENDIF()
 # This is always available
 SET(BACKENDS  "${BACKENDS} Null")
 
+option(ALSOFT_EMBED_HRTF_DATA "Embed the HRTF data files (increases library footprint)" OFF)
+if(ALSOFT_EMBED_HRTF_DATA)
+    SET(NATIVE_SRC_DIR  "${OpenAL_SOURCE_DIR}/native-tools/")
+    SET(NATIVE_BIN_DIR  "${OpenAL_BINARY_DIR}/native-tools/")
+    FILE(MAKE_DIRECTORY "${NATIVE_BIN_DIR}")
+
+    SET(BIN2H_COMMAND  "${NATIVE_BIN_DIR}bin2h")
+    ADD_CUSTOM_COMMAND(OUTPUT "${BIN2H_COMMAND}"
+        COMMAND ${CMAKE_COMMAND} "${NATIVE_SRC_DIR}"
+        COMMAND ${CMAKE_COMMAND} --build . --config "Release"
+        WORKING_DIRECTORY "${NATIVE_BIN_DIR}"
+        DEPENDS "${NATIVE_SRC_DIR}CMakeLists.txt" "${NATIVE_SRC_DIR}bin2h.c"
+        VERBATIM
+    )
+
+    MACRO(make_hrtf_header FILENAME VARNAME)
+        SET(infile  "${OpenAL_SOURCE_DIR}/hrtf/${FILENAME}")
+        SET(outfile  "${OpenAL_BINARY_DIR}/${FILENAME}.h")
+
+        ADD_CUSTOM_COMMAND(OUTPUT "${outfile}"
+            COMMAND "${BIN2H_COMMAND}" "${infile}" "${outfile}" ${VARNAME}
+            DEPENDS "${BIN2H_COMMAND}" "${infile}"
+            COMMENT "Generating ${FILENAME}.h"
+            VERBATIM
+        )
+
+        SET(ALC_OBJS  ${ALC_OBJS} "${outfile}")
+    ENDMACRO()
+
+    make_hrtf_header(default-44100.mhr "hrtf_default_44100")
+    make_hrtf_header(default-48000.mhr "hrtf_default_48000")
+endif()
+
+
 IF(ALSOFT_UTILS AND NOT ALSOFT_NO_CONFIG_UTIL)
     add_subdirectory(utils/alsoft-config)
 ENDIF()
@@ -1064,13 +1164,7 @@ IF(ALSOFT_EXAMPLES)
     FIND_PACKAGE(SDL2)
     IF(SDL2_FOUND)
         FIND_PACKAGE(SDL_sound)
-        IF(SDL_SOUND_FOUND AND CMAKE_VERSION VERSION_LESS "2.8.8")
-            INCLUDE_DIRECTORIES(${SDL2_INCLUDE_DIR} ${SDL_SOUND_INCLUDE_DIR})
-        ENDIF()
         FIND_PACKAGE(FFmpeg COMPONENTS AVFORMAT AVCODEC AVUTIL SWSCALE SWRESAMPLE)
-        IF(FFMPEG_FOUND AND CMAKE_VERSION VERSION_LESS "2.8.8")
-            INCLUDE_DIRECTORIES(${FFMPEG_INCLUDE_DIRS})
-        ENDIF()
     ENDIF()
 ENDIF()
 
@@ -1079,12 +1173,38 @@ IF(LIBTYPE STREQUAL "STATIC")
     SET(PKG_CONFIG_CFLAGS -DAL_LIBTYPE_STATIC ${PKG_CONFIG_CFLAGS})
 ENDIF()
 
+IF(NOT WIN32)
+    SET(LIBNAME "openal")
+ELSE()
+    SET(LIBNAME "OpenAL32")
+ENDIF()
+
+FIND_PACKAGE(Git)
+IF(GIT_FOUND AND EXISTS "${OpenAL_SOURCE_DIR}/.git")
+    # Get the current working branch and its latest abbreviated commit hash
+    ADD_CUSTOM_TARGET(build_version
+        ${CMAKE_COMMAND} -D GIT_EXECUTABLE=${GIT_EXECUTABLE}
+                         -D LIB_VERSION=${LIB_VERSION}
+                         -D SRC=${OpenAL_SOURCE_DIR}/version.h.in
+                         -D DST=${OpenAL_BINARY_DIR}/version.h
+                         -P ${OpenAL_SOURCE_DIR}/version.cmake
+        WORKING_DIRECTORY "${OpenAL_SOURCE_DIR}"
+        VERBATIM
+    )
+ELSE()
+    SET(GIT_BRANCH "UNKNOWN")
+    SET(GIT_COMMIT_HASH "unknown")
+    CONFIGURE_FILE(
+        "${OpenAL_SOURCE_DIR}/version.h.in"
+        "${OpenAL_BINARY_DIR}/version.h")
+ENDIF()
+
 # Needed for openal.pc.in
 SET(prefix ${CMAKE_INSTALL_PREFIX})
 SET(exec_prefix "\${prefix}")
-SET(libdir "\${exec_prefix}/lib${LIB_SUFFIX}")
-SET(bindir "\${exec_prefix}/bin")
-SET(includedir "\${prefix}/include")
+SET(libdir "\${exec_prefix}/${CMAKE_INSTALL_LIBDIR}")
+SET(bindir "\${exec_prefix}/${CMAKE_INSTALL_BINDIR}")
+SET(includedir "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}")
 SET(PACKAGE_VERSION "${LIB_VERSION}")
 
 # End configuration
@@ -1096,101 +1216,147 @@ CONFIGURE_FILE(
     "${OpenAL_BINARY_DIR}/openal.pc"
     @ONLY)
 
-# Build a common library with reusable helpers
-ADD_LIBRARY(common STATIC ${COMMON_OBJS})
-IF(NOT LIBTYPE STREQUAL "STATIC")
-    SET_PROPERTY(TARGET common PROPERTY POSITION_INDEPENDENT_CODE TRUE)
-ENDIF()
+MACRO(ADD_INCLUDE_DIRS TRGT TESTVAR INCVAR)
+    IF(${TESTVAR})
+        SET_PROPERTY(TARGET ${TRGT} APPEND PROPERTY INCLUDE_DIRECTORIES ${${INCVAR}})
+    ENDIF()
+ENDMACRO()
+
+UNSET(HAS_ROUTER)
+SET(IMPL_TARGET OpenAL)
 
 # Build main library
 IF(LIBTYPE STREQUAL "STATIC")
-    ADD_LIBRARY(${LIBNAME} STATIC ${COMMON_OBJS} ${OPENAL_OBJS} ${ALC_OBJS})
+    ADD_LIBRARY(OpenAL STATIC ${COMMON_OBJS} ${OPENAL_OBJS} ${ALC_OBJS})
 ELSE()
-    ADD_LIBRARY(${LIBNAME} SHARED ${OPENAL_OBJS} ${ALC_OBJS})
+    IF(WIN32 AND ALSOFT_BUILD_ROUTER)
+        ADD_LIBRARY(OpenAL SHARED router/router.c router/alc.c router/al.c ${COMMON_OBJS})
+        SET_PROPERTY(TARGET OpenAL APPEND PROPERTY COMPILE_FLAGS ${EXTRA_CFLAGS})
+        SET_PROPERTY(TARGET OpenAL APPEND PROPERTY
+            COMPILE_DEFINITIONS AL_BUILD_LIBRARY AL_ALEXT_PROTOTYPES
+        )
+        SET_PROPERTY(TARGET OpenAL APPEND_STRING PROPERTY LINK_FLAGS ${EXTRA_LDFLAGS})
+        IF(MSVC)
+            SET_PROPERTY(TARGET OpenAL APPEND_STRING PROPERTY LINK_FLAGS " /SUBSYSTEM:WINDOWS")
+        ELSEIF(CMAKE_COMPILER_IS_GNUCC)
+            SET_PROPERTY(TARGET OpenAL APPEND_STRING PROPERTY LINK_FLAGS " -mwindows")
+        ENDIF()
+        SET_TARGET_PROPERTIES(OpenAL PROPERTIES PREFIX "")
+        SET_TARGET_PROPERTIES(OpenAL PROPERTIES OUTPUT_NAME ${LIBNAME})
+        IF(TARGET build_version)
+            ADD_DEPENDENCIES(OpenAL build_version)
+        ENDIF()
+        SET(HAS_ROUTER 1)
+
+        SET(LIBNAME "soft_oal")
+        SET(IMPL_TARGET soft_oal)
+    ENDIF()
+
+    ADD_LIBRARY(${IMPL_TARGET} SHARED ${COMMON_OBJS} ${OPENAL_OBJS} ${ALC_OBJS})
+    IF(WIN32)
+        SET_TARGET_PROPERTIES(${IMPL_TARGET} PROPERTIES PREFIX "")
+    ENDIF()
 ENDIF()
-SET_PROPERTY(TARGET ${LIBNAME} APPEND PROPERTY COMPILE_DEFINITIONS AL_BUILD_LIBRARY AL_ALEXT_PROTOTYPES)
+SET_TARGET_PROPERTIES(${IMPL_TARGET} PROPERTIES OUTPUT_NAME ${LIBNAME}
+    VERSION ${LIB_VERSION}
+    SOVERSION ${LIB_MAJOR_VERSION}
+)
+SET_PROPERTY(TARGET ${IMPL_TARGET} APPEND PROPERTY INCLUDE_DIRECTORIES
+    "${OpenAL_SOURCE_DIR}/OpenAL32/Include" "${OpenAL_SOURCE_DIR}/Alc"
+)
+ADD_INCLUDE_DIRS(${IMPL_TARGET} HAVE_ALSA       ALSA_INCLUDE_DIRS)
+ADD_INCLUDE_DIRS(${IMPL_TARGET} HAVE_OSS        OSS_INCLUDE_DIRS)
+ADD_INCLUDE_DIRS(${IMPL_TARGET} HAVE_SOLARIS    AUDIOIO_INCLUDE_DIRS)
+ADD_INCLUDE_DIRS(${IMPL_TARGET} HAVE_SNDIO      SOUNDIO_INCLUDE_DIRS)
+ADD_INCLUDE_DIRS(${IMPL_TARGET} HAVE_QSA        QSA_INCLUDE_DIRS)
+ADD_INCLUDE_DIRS(${IMPL_TARGET} HAVE_DSOUND     DSOUND_INCLUDE_DIRS)
+ADD_INCLUDE_DIRS(${IMPL_TARGET} HAVE_PORTAUDIO  PORTAUDIO_INCLUDE_DIRS)
+ADD_INCLUDE_DIRS(${IMPL_TARGET} HAVE_PULSEAUDIO PULSEAUDIO_INCLUDE_DIRS)
+ADD_INCLUDE_DIRS(${IMPL_TARGET} HAVE_JACK       JACK_INCLUDE_DIRS)
+SET_PROPERTY(TARGET ${IMPL_TARGET} APPEND PROPERTY
+    COMPILE_DEFINITIONS AL_BUILD_LIBRARY AL_ALEXT_PROTOTYPES
+)
 IF(WIN32 AND ALSOFT_NO_UID_DEFS)
-    SET_PROPERTY(TARGET ${LIBNAME} APPEND PROPERTY COMPILE_DEFINITIONS AL_NO_UID_DEFS)
+    SET_PROPERTY(TARGET ${IMPL_TARGET} APPEND PROPERTY COMPILE_DEFINITIONS AL_NO_UID_DEFS)
 ENDIF()
-SET_PROPERTY(TARGET ${LIBNAME} APPEND PROPERTY INCLUDE_DIRECTORIES "${OpenAL_SOURCE_DIR}/OpenAL32/Include" "${OpenAL_SOURCE_DIR}/Alc")
-IF(HAVE_ALSA)
-    SET_PROPERTY(TARGET ${LIBNAME} APPEND PROPERTY INCLUDE_DIRECTORIES ${ALSA_INCLUDE_DIRS})
-ENDIF()
-IF(HAVE_OSS)
-    SET_PROPERTY(TARGET ${LIBNAME} APPEND PROPERTY INCLUDE_DIRECTORIES ${OSS_INCLUDE_DIRS})
-ENDIF()
-IF(HAVE_SOLARIS)
-    SET_PROPERTY(TARGET ${LIBNAME} APPEND PROPERTY INCLUDE_DIRECTORIES ${AUDIOIO_INCLUDE_DIRS})
-ENDIF()
-IF(HAVE_SNDIO)
-    SET_PROPERTY(TARGET ${LIBNAME} APPEND PROPERTY INCLUDE_DIRECTORIES ${SOUNDIO_INCLUDE_DIRS})
-ENDIF()
-IF(HAVE_QSA)
-    SET_PROPERTY(TARGET ${LIBNAME} APPEND PROPERTY INCLUDE_DIRECTORIES ${QSA_INCLUDE_DIRS})
-ENDIF()
-IF(HAVE_DSOUND)
-    SET_PROPERTY(TARGET ${LIBNAME} APPEND PROPERTY INCLUDE_DIRECTORIES ${DSOUND_INCLUDE_DIRS})
-ENDIF()
-IF(HAVE_PORTAUDIO)
-    SET_PROPERTY(TARGET ${LIBNAME} APPEND PROPERTY INCLUDE_DIRECTORIES ${PORTAUDIO_INCLUDE_DIRS})
-ENDIF()
-IF(HAVE_PULSEAUDIO)
-    SET_PROPERTY(TARGET ${LIBNAME} APPEND PROPERTY INCLUDE_DIRECTORIES ${PULSEAUDIO_INCLUDE_DIRS})
-ENDIF()
-IF(HAVE_JACK)
-    SET_PROPERTY(TARGET ${LIBNAME} APPEND PROPERTY INCLUDE_DIRECTORIES ${JACK_INCLUDE_DIRS})
+SET_PROPERTY(TARGET ${IMPL_TARGET} APPEND PROPERTY COMPILE_FLAGS ${EXTRA_CFLAGS})
+SET_PROPERTY(TARGET ${IMPL_TARGET} APPEND_STRING PROPERTY LINK_FLAGS ${EXTRA_LDFLAGS})
+
+TARGET_LINK_LIBRARIES(${IMPL_TARGET} ${EXTRA_LIBS})
+IF(TARGET build_version)
+    ADD_DEPENDENCIES(${IMPL_TARGET} build_version)
 ENDIF()
-SET_TARGET_PROPERTIES(${LIBNAME} PROPERTIES VERSION ${LIB_VERSION}
-                                            SOVERSION ${LIB_MAJOR_VERSION})
-IF(WIN32 AND NOT LIBTYPE STREQUAL "STATIC")
-    SET_TARGET_PROPERTIES(${LIBNAME} PROPERTIES PREFIX "")
 
-    IF(MINGW AND ALSOFT_BUILD_IMPORT_LIB)
-        FIND_PROGRAM(SED_EXECUTABLE NAMES sed DOC "sed executable")
-        FIND_PROGRAM(DLLTOOL_EXECUTABLE NAMES "${DLLTOOL}" DOC "dlltool executable")
-        IF(NOT SED_EXECUTABLE OR NOT DLLTOOL_EXECUTABLE)
-            MESSAGE(STATUS "")
-            IF(NOT SED_EXECUTABLE)
-                MESSAGE(STATUS "WARNING: Cannot find sed, disabling .def/.lib generation")
-            ENDIF()
-            IF(NOT DLLTOOL_EXECUTABLE)
-                MESSAGE(STATUS "WARNING: Cannot find dlltool, disabling .def/.lib generation")
+IF(WIN32)
+    IF(MSVC)
+        SET_PROPERTY(TARGET ${IMPL_TARGET} APPEND_STRING PROPERTY LINK_FLAGS " /SUBSYSTEM:WINDOWS")
+    ELSEIF(CMAKE_COMPILER_IS_GNUCC)
+        SET_PROPERTY(TARGET ${IMPL_TARGET} APPEND_STRING PROPERTY LINK_FLAGS " -mwindows")
+    ENDIF()
+
+    if(NOT LIBTYPE STREQUAL "STATIC")
+        IF(MINGW AND ALSOFT_BUILD_IMPORT_LIB)
+            FIND_PROGRAM(SED_EXECUTABLE NAMES sed DOC "sed executable")
+            FIND_PROGRAM(DLLTOOL_EXECUTABLE NAMES "${DLLTOOL}" DOC "dlltool executable")
+            IF(NOT SED_EXECUTABLE OR NOT DLLTOOL_EXECUTABLE)
+                MESSAGE(STATUS "")
+                IF(NOT SED_EXECUTABLE)
+                    MESSAGE(STATUS "WARNING: Cannot find sed, disabling .def/.lib generation")
+                ENDIF()
+                IF(NOT DLLTOOL_EXECUTABLE)
+                    MESSAGE(STATUS "WARNING: Cannot find dlltool, disabling .def/.lib generation")
+                ENDIF()
+            ELSE()
+                SET_PROPERTY(TARGET OpenAL APPEND_STRING PROPERTY LINK_FLAGS
+                             " -Wl,--output-def,OpenAL32.def")
+                ADD_CUSTOM_COMMAND(TARGET OpenAL POST_BUILD
+                    COMMAND "${SED_EXECUTABLE}" -i -e "s/ @[^ ]*//" OpenAL32.def
+                    COMMAND "${DLLTOOL_EXECUTABLE}" -d OpenAL32.def -l OpenAL32.lib -D OpenAL32.dll
+                    COMMENT "Stripping ordinals from OpenAL32.def and generating OpenAL32.lib..."
+                    VERBATIM
+                )
             ENDIF()
-        ELSE()
-            SET_TARGET_PROPERTIES(${LIBNAME} PROPERTIES LINK_FLAGS "-Wl,--output-def,${LIBNAME}.def")
-            ADD_CUSTOM_COMMAND(TARGET ${LIBNAME} POST_BUILD
-                COMMAND "${SED_EXECUTABLE}" -i -e "s/ @[^ ]*//" ${LIBNAME}.def
-                COMMAND "${DLLTOOL_EXECUTABLE}" -d ${LIBNAME}.def -l ${LIBNAME}.lib -D ${LIBNAME}.dll
-                COMMENT "Stripping ordinals from ${LIBNAME}.def and generating ${LIBNAME}.lib..."
-                VERBATIM
-            )
         ENDIF()
     ENDIF()
 ENDIF()
 
-TARGET_LINK_LIBRARIES(${LIBNAME} common ${EXTRA_LIBS})
-TARGET_INCLUDE_DIRECTORIES(${LIBNAME} PUBLIC include)
-
 IF(ALSOFT_INSTALL)
-    # Add an install target here
-    INSTALL(TARGETS ${LIBNAME}
-            RUNTIME DESTINATION bin
-            LIBRARY DESTINATION "lib${LIB_SUFFIX}"
-            ARCHIVE DESTINATION "lib${LIB_SUFFIX}"
+    INSTALL(TARGETS OpenAL EXPORT OpenAL
+            RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+            LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+            ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+            INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} ${CMAKE_INSTALL_INCLUDEDIR}/AL
     )
+    EXPORT(TARGETS OpenAL
+           NAMESPACE OpenAL::
+           FILE OpenALConfig.cmake)
+    INSTALL(EXPORT OpenAL
+            DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/OpenAL
+            NAMESPACE OpenAL::
+            FILE OpenALConfig.cmake)
     INSTALL(FILES include/AL/al.h
                   include/AL/alc.h
                   include/AL/alext.h
                   include/AL/efx.h
                   include/AL/efx-creative.h
                   include/AL/efx-presets.h
-            DESTINATION include/AL
+            DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/AL
     )
     INSTALL(FILES "${OpenAL_BINARY_DIR}/openal.pc"
-            DESTINATION "lib${LIB_SUFFIX}/pkgconfig")
+            DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
+    IF(TARGET soft_oal)
+        INSTALL(TARGETS soft_oal
+                RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+        )
+    ENDIF()
 ENDIF()
 
 
+if(HAS_ROUTER)
+    message(STATUS "")
+    message(STATUS "Building DLL router")
+endif()
+
 MESSAGE(STATUS "")
 MESSAGE(STATUS "Building OpenAL with support for the following backends:")
 MESSAGE(STATUS "    ${BACKENDS}")
@@ -1207,10 +1373,15 @@ IF(WIN32)
     ENDIF()
 ENDIF()
 
+if(ALSOFT_EMBED_HRTF_DATA)
+    message(STATUS "Embedding HRTF datasets")
+    message(STATUS "")
+endif()
+
 # Install alsoft.conf configuration file
 IF(ALSOFT_CONFIG)
     INSTALL(FILES alsoftrc.sample
-            DESTINATION ${SHARE_INSTALL_DIR}/openal
+            DESTINATION ${CMAKE_INSTALL_DATADIR}/openal
     )
     MESSAGE(STATUS "Installing sample configuration")
     MESSAGE(STATUS "")
@@ -1220,31 +1391,48 @@ ENDIF()
 IF(ALSOFT_HRTF_DEFS)
     INSTALL(FILES hrtf/default-44100.mhr
                   hrtf/default-48000.mhr
-            DESTINATION ${SHARE_INSTALL_DIR}/openal/hrtf
+            DESTINATION ${CMAKE_INSTALL_DATADIR}/openal/hrtf
     )
     MESSAGE(STATUS "Installing HRTF definitions")
     MESSAGE(STATUS "")
 ENDIF()
 
+# Install AmbDec presets
+IF(ALSOFT_AMBDEC_PRESETS)
+    INSTALL(FILES presets/3D7.1.ambdec
+                  presets/hexagon.ambdec
+                  presets/itu5.1.ambdec
+                  presets/rectangle.ambdec
+                  presets/square.ambdec
+                  presets/presets.txt
+            DESTINATION ${CMAKE_INSTALL_DATADIR}/openal/presets
+    )
+    MESSAGE(STATUS "Installing AmbDec presets")
+    MESSAGE(STATUS "")
+ENDIF()
+
 IF(ALSOFT_UTILS)
     ADD_EXECUTABLE(openal-info utils/openal-info.c)
-    TARGET_LINK_LIBRARIES(openal-info ${LIBNAME})
+    SET_PROPERTY(TARGET openal-info APPEND PROPERTY COMPILE_FLAGS ${EXTRA_CFLAGS})
+    TARGET_LINK_LIBRARIES(openal-info OpenAL)
 
     ADD_EXECUTABLE(makehrtf utils/makehrtf.c)
+    SET_PROPERTY(TARGET makehrtf APPEND PROPERTY COMPILE_FLAGS ${EXTRA_CFLAGS})
     IF(HAVE_LIBM)
         TARGET_LINK_LIBRARIES(makehrtf m)
     ENDIF()
 
     ADD_EXECUTABLE(bsincgen utils/bsincgen.c)
+    SET_PROPERTY(TARGET bsincgen APPEND PROPERTY COMPILE_FLAGS ${EXTRA_CFLAGS})
     IF(HAVE_LIBM)
         TARGET_LINK_LIBRARIES(bsincgen m)
     ENDIF()
 
     IF(ALSOFT_INSTALL)
         INSTALL(TARGETS openal-info makehrtf bsincgen
-                RUNTIME DESTINATION bin
-                LIBRARY DESTINATION "lib${LIB_SUFFIX}"
-                ARCHIVE DESTINATION "lib${LIB_SUFFIX}"
+                RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+                LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+                ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
         )
     ENDIF()
 
@@ -1256,113 +1444,130 @@ IF(ALSOFT_UTILS)
 ENDIF()
 
 IF(ALSOFT_TESTS)
-        ADD_LIBRARY(test-common STATIC examples/common/alhelpers.c)
+    SET(TEST_COMMON_OBJS  examples/common/alhelpers.c)
 
-        ADD_EXECUTABLE(altonegen examples/altonegen.c)
-        TARGET_LINK_LIBRARIES(altonegen test-common ${LIBNAME})
+    ADD_EXECUTABLE(altonegen examples/altonegen.c ${COMMON_OBJS} ${TEST_COMMON_OBJS})
+    TARGET_LINK_LIBRARIES(altonegen OpenAL)
+    SET_PROPERTY(TARGET altonegen APPEND PROPERTY COMPILE_FLAGS ${EXTRA_CFLAGS})
 
-        IF(ALSOFT_INSTALL)
-            INSTALL(TARGETS altonegen
-                    RUNTIME DESTINATION bin
-                    LIBRARY DESTINATION "lib${LIB_SUFFIX}"
-                    ARCHIVE DESTINATION "lib${LIB_SUFFIX}"
-            )
-        ENDIF()
+    IF(ALSOFT_INSTALL)
+        INSTALL(TARGETS altonegen
+                RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+                LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+                ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+        )
+    ENDIF()
 
-        MESSAGE(STATUS "Building test programs")
-        MESSAGE(STATUS "")
+    MESSAGE(STATUS "Building test programs")
+    MESSAGE(STATUS "")
 ENDIF()
 
 IF(ALSOFT_EXAMPLES)
-    IF(SDL2_FOUND AND SDL_SOUND_FOUND)
-        ADD_LIBRARY(ex-common STATIC examples/common/alhelpers.c
-                                     examples/common/sdl_sound.c)
-        SET_PROPERTY(TARGET ex-common APPEND PROPERTY INCLUDE_DIRECTORIES ${SDL2_INCLUDE_DIR}
-                                                                          ${SDL_SOUND_INCLUDE_DIR})
-
-        ADD_EXECUTABLE(alstream examples/alstream.c)
-        TARGET_LINK_LIBRARIES(alstream ex-common ${SDL_SOUND_LIBRARIES} ${SDL2_LIBRARY}
-                                       common ${LIBNAME})
-        SET_PROPERTY(TARGET alstream APPEND PROPERTY INCLUDE_DIRECTORIES ${SDL2_INCLUDE_DIR}
-                                                                         ${SDL_SOUND_INCLUDE_DIR})
-
-        ADD_EXECUTABLE(alreverb examples/alreverb.c)
-        TARGET_LINK_LIBRARIES(alreverb ex-common ${SDL_SOUND_LIBRARIES} ${SDL2_LIBRARY}
-                                       common ${LIBNAME})
-        SET_PROPERTY(TARGET alreverb APPEND PROPERTY INCLUDE_DIRECTORIES ${SDL2_INCLUDE_DIR}
-                                                                         ${SDL_SOUND_INCLUDE_DIR})
-
-        ADD_EXECUTABLE(allatency examples/allatency.c)
-        TARGET_LINK_LIBRARIES(allatency ex-common ${SDL_SOUND_LIBRARIES} ${SDL2_LIBRARY}
-                                        common ${LIBNAME})
-        SET_PROPERTY(TARGET allatency APPEND PROPERTY INCLUDE_DIRECTORIES ${SDL2_INCLUDE_DIR}
-                                                                          ${SDL_SOUND_INCLUDE_DIR})
-
-        ADD_EXECUTABLE(alloopback examples/alloopback.c)
-        TARGET_LINK_LIBRARIES(alloopback ex-common ${SDL_SOUND_LIBRARIES} ${SDL2_LIBRARY}
-                                         common ${LIBNAME})
-        SET_PROPERTY(TARGET alloopback APPEND PROPERTY INCLUDE_DIRECTORIES ${SDL2_INCLUDE_DIR}
-                                                                           ${SDL_SOUND_INCLUDE_DIR})
-
-        ADD_EXECUTABLE(alhrtf examples/alhrtf.c)
-        TARGET_LINK_LIBRARIES(alhrtf ex-common ${SDL_SOUND_LIBRARIES} ${SDL2_LIBRARY}
-                                     common ${LIBNAME})
-        SET_PROPERTY(TARGET alhrtf APPEND PROPERTY INCLUDE_DIRECTORIES ${SDL2_INCLUDE_DIR}
-                                                                       ${SDL_SOUND_INCLUDE_DIR})
-
-        IF(ALSOFT_INSTALL)
-            INSTALL(TARGETS alstream alreverb allatency alloopback alhrtf
-                    RUNTIME DESTINATION bin
-                    LIBRARY DESTINATION "lib${LIB_SUFFIX}"
-                    ARCHIVE DESTINATION "lib${LIB_SUFFIX}"
+    ADD_EXECUTABLE(alrecord examples/alrecord.c ${COMMON_OBJS})
+    TARGET_LINK_LIBRARIES(alrecord OpenAL)
+    SET_PROPERTY(TARGET alrecord APPEND PROPERTY COMPILE_FLAGS ${EXTRA_CFLAGS})
+
+    IF(ALSOFT_INSTALL)
+        INSTALL(TARGETS alrecord
+                RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+                LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+                ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+        )
+    ENDIF()
+
+    MESSAGE(STATUS "Building example programs")
+
+    IF(SDL2_FOUND)
+        IF(SDL_SOUND_FOUND)
+            SET(EX_COMMON_OBJS  examples/common/alhelpers.c)
+
+            ADD_EXECUTABLE(alstream examples/alstream.c ${COMMON_OBJS} ${EX_COMMON_OBJS})
+            TARGET_LINK_LIBRARIES(alstream ${SDL_SOUND_LIBRARIES} ${SDL2_LIBRARY} OpenAL)
+            SET_PROPERTY(TARGET alstream APPEND PROPERTY COMPILE_FLAGS ${EXTRA_CFLAGS})
+            SET_PROPERTY(TARGET alstream APPEND PROPERTY
+                INCLUDE_DIRECTORIES ${SDL2_INCLUDE_DIR} ${SDL_SOUND_INCLUDE_DIR}
+            )
+
+            ADD_EXECUTABLE(alreverb examples/alreverb.c ${COMMON_OBJS} ${EX_COMMON_OBJS})
+            TARGET_LINK_LIBRARIES(alreverb ${SDL_SOUND_LIBRARIES} ${SDL2_LIBRARY} OpenAL)
+            SET_PROPERTY(TARGET alreverb APPEND PROPERTY COMPILE_FLAGS ${EXTRA_CFLAGS})
+            SET_PROPERTY(TARGET alreverb APPEND PROPERTY
+                INCLUDE_DIRECTORIES ${SDL2_INCLUDE_DIR} ${SDL_SOUND_INCLUDE_DIR}
+            )
+
+            ADD_EXECUTABLE(allatency examples/allatency.c ${COMMON_OBJS} ${EX_COMMON_OBJS})
+            TARGET_LINK_LIBRARIES(allatency ${SDL_SOUND_LIBRARIES} ${SDL2_LIBRARY} OpenAL)
+            SET_PROPERTY(TARGET allatency APPEND PROPERTY COMPILE_FLAGS ${EXTRA_CFLAGS})
+            SET_PROPERTY(TARGET allatency APPEND PROPERTY
+                INCLUDE_DIRECTORIES ${SDL2_INCLUDE_DIR} ${SDL_SOUND_INCLUDE_DIR}
+            )
+
+            ADD_EXECUTABLE(alloopback examples/alloopback.c ${COMMON_OBJS} ${EX_COMMON_OBJS})
+            TARGET_LINK_LIBRARIES(alloopback ${SDL_SOUND_LIBRARIES} ${SDL2_LIBRARY} OpenAL)
+            SET_PROPERTY(TARGET alloopback APPEND PROPERTY COMPILE_FLAGS ${EXTRA_CFLAGS})
+            SET_PROPERTY(TARGET alloopback APPEND PROPERTY
+                INCLUDE_DIRECTORIES ${SDL2_INCLUDE_DIR} ${SDL_SOUND_INCLUDE_DIR}
+            )
+
+            ADD_EXECUTABLE(alhrtf examples/alhrtf.c ${COMMON_OBJS} ${EX_COMMON_OBJS})
+            TARGET_LINK_LIBRARIES(alhrtf ${SDL_SOUND_LIBRARIES} ${SDL2_LIBRARY} OpenAL)
+            SET_PROPERTY(TARGET alhrtf APPEND PROPERTY COMPILE_FLAGS ${EXTRA_CFLAGS})
+            SET_PROPERTY(TARGET alhrtf APPEND PROPERTY
+                INCLUDE_DIRECTORIES ${SDL2_INCLUDE_DIR} ${SDL_SOUND_INCLUDE_DIR}
             )
+
+            IF(ALSOFT_INSTALL)
+                INSTALL(TARGETS alstream alreverb allatency alloopback alhrtf
+                        RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+                        LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+                        ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+                )
+            ENDIF()
+
+            MESSAGE(STATUS "Building SDL_sound example programs")
         ENDIF()
 
         SET(FFVER_OK FALSE)
         IF(FFMPEG_FOUND)
             SET(FFVER_OK TRUE)
-            IF(AVFORMAT_VERSION VERSION_LESS "55.33.100")
-                MESSAGE(STATUS "libavformat is too old! (${AVFORMAT_VERSION}, wanted 55.33.100)")
+            IF(AVFORMAT_VERSION VERSION_LESS "57.56.101")
+                MESSAGE(STATUS "libavformat is too old! (${AVFORMAT_VERSION}, wanted 57.56.101)")
                 SET(FFVER_OK FALSE)
             ENDIF()
-            IF(AVCODEC_VERSION VERSION_LESS "55.52.102")
-                MESSAGE(STATUS "libavcodec is too old! (${AVCODEC_VERSION}, wanted 55.52.102)")
+            IF(AVCODEC_VERSION VERSION_LESS "57.64.101")
+                MESSAGE(STATUS "libavcodec is too old! (${AVCODEC_VERSION}, wanted 57.64.101)")
                 SET(FFVER_OK FALSE)
             ENDIF()
-            IF(AVUTIL_VERSION VERSION_LESS "52.66.100")
-                MESSAGE(STATUS "libavutil is too old! (${AVUTIL_VERSION}, wanted 52.66.100)")
+            IF(AVUTIL_VERSION VERSION_LESS "55.34.101")
+                MESSAGE(STATUS "libavutil is too old! (${AVUTIL_VERSION}, wanted 55.34.101)")
                 SET(FFVER_OK FALSE)
             ENDIF()
-            IF(SWSCALE_VERSION VERSION_LESS "2.5.102")
-                MESSAGE(STATUS "libswscale is too old! (${SWSCALE_VERSION}, wanted 2.5.102)")
+            IF(SWSCALE_VERSION VERSION_LESS "4.2.100")
+                MESSAGE(STATUS "libswscale is too old! (${SWSCALE_VERSION}, wanted 4.2.100)")
                 SET(FFVER_OK FALSE)
             ENDIF()
-            IF(SWRESAMPLE_VERSION VERSION_LESS "0.18.100")
-                MESSAGE(STATUS "libswresample is too old! (${SWRESAMPLE_VERSION}, wanted 0.18.100)")
+            IF(SWRESAMPLE_VERSION VERSION_LESS "2.3.100")
+                MESSAGE(STATUS "libswresample is too old! (${SWRESAMPLE_VERSION}, wanted 2.3.100)")
                 SET(FFVER_OK FALSE)
             ENDIF()
         ENDIF()
-        IF(FFVER_OK AND NOT MSVC)
-            ADD_EXECUTABLE(alffplay examples/alffplay.c)
-            TARGET_LINK_LIBRARIES(alffplay common ex-common ${SDL2_LIBRARY} ${LIBNAME} ${FFMPEG_LIBRARIES})
-            SET_PROPERTY(TARGET alffplay APPEND PROPERTY INCLUDE_DIRECTORIES ${SDL2_INCLUDE_DIR}
-                                                                             ${FFMPEG_INCLUDE_DIRS})
+        IF(FFVER_OK)
+            ADD_EXECUTABLE(alffplay examples/alffplay.cpp ${COMMON_OBJS})
+            TARGET_LINK_LIBRARIES(alffplay ${SDL2_LIBRARY} ${FFMPEG_LIBRARIES} OpenAL)
+            SET_PROPERTY(TARGET alffplay APPEND PROPERTY COMPILE_FLAGS ${EXTRA_CFLAGS})
+            SET_PROPERTY(TARGET alffplay APPEND PROPERTY
+                INCLUDE_DIRECTORIES ${SDL2_INCLUDE_DIR} ${FFMPEG_INCLUDE_DIRS}
+            )
 
             IF(ALSOFT_INSTALL)
                 INSTALL(TARGETS alffplay
-                        RUNTIME DESTINATION bin
-                        LIBRARY DESTINATION "lib${LIB_SUFFIX}"
-                        ARCHIVE DESTINATION "lib${LIB_SUFFIX}"
+                        RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+                        LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+                        ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
                 )
             ENDIF()
-            MESSAGE(STATUS "Building SDL and FFmpeg example programs")
-        ELSE()
-            MESSAGE(STATUS "Building SDL example programs")
+            MESSAGE(STATUS "Building SDL+FFmpeg example programs")
         ENDIF()
         MESSAGE(STATUS "")
     ENDIF()
 ENDIF()
-
-if(MEGA)
-    install(TARGETS ${LIBNAME} RUNTIME DESTINATION . LIBRARY DESTINATION .)
-ENDIF()

+ 117 - 0
libs/openal-soft/ChangeLog

@@ -1,3 +1,120 @@
+openal-soft-1.18.2:
+
+    Fixed resetting the FPU rounding mode after certain function calls on
+    Windows.
+
+    Fixed use of SSE intrinsics when building with Clang on Windows.
+
+    Fixed a crash with the JACK backend when using JACK1.
+
+    Fixed use of pthread_setnane_np on NetBSD.
+
+    Fixed building on FreeBSD with an older freebsd-lib.
+
+    OSS now links with libossaudio if found at build time (for NetBSD).
+
+openal-soft-1.18.1:
+
+    Fixed an issue where resuming a source might not restart playing it.
+
+    Fixed PulseAudio playback when the configured stream length is much less
+    than the requested length.
+
+    Fixed MMDevAPI capture with sample rates not matching the backing device.
+
+    Fixed int32 output for the Wave Writer.
+
+    Fixed enumeration of OSS devices that are missing device files.
+
+    Added correct retrieval of the executable's path on FreeBSD.
+
+    Added a config option to specify the dithering depth.
+
+    Added a 5.1 decoder preset that excludes front-center output.
+
+openal-soft-1.18.0:
+
+    Implemented the AL_EXT_STEREO_ANGLES and AL_EXT_SOURCE_RADIUS extensions.
+
+    Implemented the AL_SOFT_gain_clamp_ex, AL_SOFT_source_resampler,
+    AL_SOFT_source_spatialize, and ALC_SOFT_output_limiter extensions.
+
+    Implemented 3D processing for some effects. Currently implemented for
+    Reverb, Compressor, Equalizer, and Ring Modulator.
+
+    Implemented 2-channel UHJ output encoding. This needs to be enabled with a
+    config option to be used.
+
+    Implemented dual-band processing for high-quality ambisonic decoding.
+
+    Implemented distance-compensation for surround sound output.
+
+    Implemented near-field emulation and compensation with ambisonic rendering.
+    Currently only applies when using the high-quality ambisonic decoder or
+    ambisonic output, with appropriate config options.
+
+    Implemented an output limiter to reduce the amount of distortion from
+    clipping.
+
+    Implemented dithering for 8-bit and 16-bit output.
+
+    Implemented a config option to select a preferred HRTF.
+
+    Implemented a run-time check for NEON extensions using /proc/cpuinfo.
+
+    Implemented experimental capture support for the OpenSL backend.
+
+    Fixed building on compilers with NEON support but don't default to having
+    NEON enabled.
+
+    Fixed support for JACK on Windows.
+
+    Fixed starting a source while alcSuspendContext is in effect.
+
+    Fixed detection of headsets as headphones, with MMDevAPI.
+
+    Added support for AmbDec config files, for custom ambisonic decoder
+    configurations. Version 3 files only.
+
+    Added backend-specific options to alsoft-config.
+
+    Added first-, second-, and third-order ambisonic output formats. Currently
+    only works with backends that don't rely on channel labels, like JACK,
+    ALSA, and OSS.
+
+    Added a build option to embed the default HRTFs into the lib.
+
+    Added AmbDec presets to enable high-quality ambisonic decoding.
+
+    Added an AmbDec preset for 3D7.1 speaker setups.
+
+    Added documentation regarding Ambisonics, 3D7.1, AmbDec config files, and
+    the provided ambdec presets.
+
+    Added the ability for MMDevAPI to open devices given a Device ID or GUID
+    string.
+
+    Added an option to the example apps to open a specific device.
+
+    Increased the maximum auxiliary send limit to 16 (up from 4). Requires
+    requesting them with the ALC_MAX_AUXILIARY_SENDS context creation
+    attribute.
+
+    Increased the default auxiliary effect slot count to 64 (up from 4).
+
+    Reduced the default period count to 3 (down from 4).
+
+    Slightly improved automatic naming for enumerated HRTFs.
+
+    Improved B-Format decoding with HRTF output.
+
+    Improved internal property handling for better batching behavior.
+
+    Improved performance of certain filter uses.
+
+    Removed support for the AL_SOFT_buffer_samples and AL_SOFT_buffer_sub_data
+    extensions. Due to conflicts with AL_EXT_SOURCE_RADIUS.
+
 openal-soft-1.17.2:
 
     Implemented device enumeration for OSSv4.

+ 87 - 14
libs/openal-soft/OpenAL32/Include/alAuxEffectSlot.h

@@ -4,6 +4,7 @@
 #include "alMain.h"
 #include "alEffect.h"
 
+#include "atomic.h"
 #include "align.h"
 
 #ifdef __cplusplus
@@ -14,15 +15,22 @@ struct ALeffectStateVtable;
 struct ALeffectslot;
 
 typedef struct ALeffectState {
+    RefCount Ref;
     const struct ALeffectStateVtable *vtbl;
+
+    ALfloat (*OutBuffer)[BUFFERSIZE];
+    ALsizei OutChannels;
 } ALeffectState;
 
+void ALeffectState_Construct(ALeffectState *state);
+void ALeffectState_Destruct(ALeffectState *state);
+
 struct ALeffectStateVtable {
     void (*const Destruct)(ALeffectState *state);
 
     ALboolean (*const deviceUpdate)(ALeffectState *state, ALCdevice *device);
-    void (*const update)(ALeffectState *state, ALCdevice *device, const struct ALeffectslot *slot);
-    void (*const process)(ALeffectState *state, ALuint samplesToDo, const ALfloat *restrict samplesIn, ALfloat (*restrict samplesOut)[BUFFERSIZE], ALuint numChannels);
+    void (*const update)(ALeffectState *state, const ALCdevice *device, const struct ALeffectslot *slot, const union ALeffectProps *props);
+    void (*const process)(ALeffectState *state, ALsizei samplesToDo, const ALfloat (*restrict samplesIn)[BUFFERSIZE], ALfloat (*restrict samplesOut)[BUFFERSIZE], ALsizei numChannels);
 
     void (*const Delete)(void *ptr);
 };
@@ -30,8 +38,8 @@ struct ALeffectStateVtable {
 #define DEFINE_ALEFFECTSTATE_VTABLE(T)                                        \
 DECLARE_THUNK(T, ALeffectState, void, Destruct)                               \
 DECLARE_THUNK1(T, ALeffectState, ALboolean, deviceUpdate, ALCdevice*)         \
-DECLARE_THUNK2(T, ALeffectState, void, update, ALCdevice*, const ALeffectslot*) \
-DECLARE_THUNK4(T, ALeffectState, void, process, ALuint, const ALfloat*restrict, ALfloatBUFFERSIZE*restrict, ALuint) \
+DECLARE_THUNK3(T, ALeffectState, void, update, const ALCdevice*, const ALeffectslot*, const ALeffectProps*) \
+DECLARE_THUNK4(T, ALeffectState, void, process, ALsizei, const ALfloatBUFFERSIZE*restrict, ALfloatBUFFERSIZE*restrict, ALsizei) \
 static void T##_ALeffectState_Delete(void *ptr)                               \
 { return T##_Delete(STATIC_UPCAST(T, ALeffectState, (ALeffectState*)ptr)); }  \
                                                                               \
@@ -64,36 +72,101 @@ static const struct ALeffectStateFactoryVtable T##_ALeffectStateFactory_vtable =
 }
 
 
+#define MAX_EFFECT_CHANNELS (4)
+
+
+struct ALeffectslotArray {
+    ALsizei count;
+    struct ALeffectslot *slot[];
+};
+
+
+struct ALeffectslotProps {
+    ALfloat   Gain;
+    ALboolean AuxSendAuto;
+
+    ALenum Type;
+    ALeffectProps Props;
+
+    ALeffectState *State;
+
+    ATOMIC(struct ALeffectslotProps*) next;
+};
+
+
 typedef struct ALeffectslot {
-    ALenum EffectType;
-    ALeffectProps EffectProps;
+    ALfloat   Gain;
+    ALboolean AuxSendAuto;
 
-    volatile ALfloat   Gain;
-    volatile ALboolean AuxSendAuto;
+    struct {
+        ALenum Type;
+        ALeffectProps Props;
 
-    ATOMIC(ALenum) NeedsUpdate;
-    ALeffectState *EffectState;
+        ALeffectState *State;
+    } Effect;
 
-    alignas(16) ALfloat WetBuffer[1][BUFFERSIZE];
+    ATOMIC_FLAG PropsClean;
 
     RefCount ref;
 
+    ATOMIC(struct ALeffectslotProps*) Update;
+    ATOMIC(struct ALeffectslotProps*) FreeList;
+
+    struct {
+        ALfloat   Gain;
+        ALboolean AuxSendAuto;
+
+        ALenum EffectType;
+        ALeffectState *EffectState;
+
+        ALfloat RoomRolloff; /* Added to the source's room rolloff, not multiplied. */
+        ALfloat DecayTime;
+        ALfloat DecayHFRatio;
+        ALboolean DecayHFLimit;
+        ALfloat AirAbsorptionGainHF;
+    } Params;
+
     /* Self ID */
     ALuint id;
+
+    ALsizei NumChannels;
+    BFChannelConfig ChanMap[MAX_EFFECT_CHANNELS];
+    /* Wet buffer configuration is ACN channel order with N3D scaling:
+     * * Channel 0 is the unattenuated mono signal.
+     * * Channel 1 is OpenAL -X
+     * * Channel 2 is OpenAL Y
+     * * Channel 3 is OpenAL -Z
+     * Consequently, effects that only want to work with mono input can use
+     * channel 0 by itself. Effects that want multichannel can process the
+     * ambisonics signal and make a B-Format pan (ComputeFirstOrderGains) for
+     * first-order device output (FOAOut).
+     */
+    alignas(16) ALfloat WetBuffer[MAX_EFFECT_CHANNELS][BUFFERSIZE];
 } ALeffectslot;
 
+inline void LockEffectSlotsRead(ALCcontext *context)
+{ LockUIntMapRead(&context->EffectSlotMap); }
+inline void UnlockEffectSlotsRead(ALCcontext *context)
+{ UnlockUIntMapRead(&context->EffectSlotMap); }
+inline void LockEffectSlotsWrite(ALCcontext *context)
+{ LockUIntMapWrite(&context->EffectSlotMap); }
+inline void UnlockEffectSlotsWrite(ALCcontext *context)
+{ UnlockUIntMapWrite(&context->EffectSlotMap); }
+
 inline struct ALeffectslot *LookupEffectSlot(ALCcontext *context, ALuint id)
-{ return (struct ALeffectslot*)LookupUIntMapKey(&context->EffectSlotMap, id); }
+{ return (struct ALeffectslot*)LookupUIntMapKeyNoLock(&context->EffectSlotMap, id); }
 inline struct ALeffectslot *RemoveEffectSlot(ALCcontext *context, ALuint id)
-{ return (struct ALeffectslot*)RemoveUIntMapKey(&context->EffectSlotMap, id); }
+{ return (struct ALeffectslot*)RemoveUIntMapKeyNoLock(&context->EffectSlotMap, id); }
 
 ALenum InitEffectSlot(ALeffectslot *slot);
+void DeinitEffectSlot(ALeffectslot *slot);
+void UpdateEffectSlotProps(ALeffectslot *slot);
+void UpdateAllEffectSlotProps(ALCcontext *context);
 ALvoid ReleaseALAuxiliaryEffectSlots(ALCcontext *Context);
 
 
 ALeffectStateFactory *ALnullStateFactory_getFactory(void);
 ALeffectStateFactory *ALreverbStateFactory_getFactory(void);
-ALeffectStateFactory *ALautowahStateFactory_getFactory(void);
 ALeffectStateFactory *ALchorusStateFactory_getFactory(void);
 ALeffectStateFactory *ALcompressorStateFactory_getFactory(void);
 ALeffectStateFactory *ALdistortionStateFactory_getFactory(void);

+ 31 - 23
libs/openal-soft/OpenAL32/Include/alBuffer.h

@@ -17,28 +17,26 @@ enum UserFmtType {
     UserFmtUInt   = AL_UNSIGNED_INT_SOFT,
     UserFmtFloat  = AL_FLOAT_SOFT,
     UserFmtDouble = AL_DOUBLE_SOFT,
-    UserFmtByte3  = AL_BYTE3_SOFT,
-    UserFmtUByte3 = AL_UNSIGNED_BYTE3_SOFT,
-    UserFmtMulaw,
-    UserFmtAlaw,
+    UserFmtMulaw  = AL_MULAW_SOFT,
+    UserFmtAlaw   = 0x10000000,
     UserFmtIMA4,
     UserFmtMSADPCM,
 };
 enum UserFmtChannels {
-    UserFmtMono   = AL_MONO_SOFT,
-    UserFmtStereo = AL_STEREO_SOFT,
-    UserFmtRear   = AL_REAR_SOFT,
-    UserFmtQuad   = AL_QUAD_SOFT,
-    UserFmtX51    = AL_5POINT1_SOFT, /* (WFX order) */
-    UserFmtX61    = AL_6POINT1_SOFT, /* (WFX order) */
-    UserFmtX71    = AL_7POINT1_SOFT, /* (WFX order) */
-    UserFmtBFormat2D = 0x10000000, /* WXY */
-    UserFmtBFormat3D, /* WXYZ */
+    UserFmtMono      = AL_MONO_SOFT,
+    UserFmtStereo    = AL_STEREO_SOFT,
+    UserFmtRear      = AL_REAR_SOFT,
+    UserFmtQuad      = AL_QUAD_SOFT,
+    UserFmtX51       = AL_5POINT1_SOFT, /* (WFX order) */
+    UserFmtX61       = AL_6POINT1_SOFT, /* (WFX order) */
+    UserFmtX71       = AL_7POINT1_SOFT, /* (WFX order) */
+    UserFmtBFormat2D = AL_BFORMAT2D_SOFT, /* WXY */
+    UserFmtBFormat3D = AL_BFORMAT3D_SOFT, /* WXYZ */
 };
 
-ALuint BytesFromUserFmt(enum UserFmtType type) DECL_CONST;
-ALuint ChannelsFromUserFmt(enum UserFmtChannels chans) DECL_CONST;
-inline ALuint FrameSizeFromUserFmt(enum UserFmtChannels chans, enum UserFmtType type)
+ALsizei BytesFromUserFmt(enum UserFmtType type);
+ALsizei ChannelsFromUserFmt(enum UserFmtChannels chans);
+inline ALsizei FrameSizeFromUserFmt(enum UserFmtChannels chans, enum UserFmtType type)
 {
     return ChannelsFromUserFmt(chans) * BytesFromUserFmt(type);
 }
@@ -63,9 +61,9 @@ enum FmtChannels {
 };
 #define MAX_INPUT_CHANNELS  (8)
 
-ALuint BytesFromFmt(enum FmtType type) DECL_CONST;
-ALuint ChannelsFromFmt(enum FmtChannels chans) DECL_CONST;
-inline ALuint FrameSizeFromFmt(enum FmtChannels chans, enum FmtType type)
+ALsizei BytesFromFmt(enum FmtType type);
+ALsizei ChannelsFromFmt(enum FmtChannels chans);
+inline ALsizei FrameSizeFromFmt(enum FmtChannels chans, enum FmtType type)
 {
     return ChannelsFromFmt(chans) * BytesFromFmt(type);
 }
@@ -80,14 +78,15 @@ typedef struct ALbuffer {
 
     enum FmtChannels FmtChannels;
     enum FmtType     FmtType;
+    ALuint BytesAlloc;
 
     enum UserFmtChannels OriginalChannels;
     enum UserFmtType     OriginalType;
     ALsizei              OriginalSize;
     ALsizei              OriginalAlign;
 
-    ALsizei  LoopStart;
-    ALsizei  LoopEnd;
+    ALsizei LoopStart;
+    ALsizei LoopEnd;
 
     ATOMIC(ALsizei) UnpackAlign;
     ATOMIC(ALsizei) PackAlign;
@@ -106,10 +105,19 @@ void DeleteBuffer(ALCdevice *device, ALbuffer *buffer);
 
 ALenum LoadData(ALbuffer *buffer, ALuint freq, ALenum NewFormat, ALsizei frames, enum UserFmtChannels SrcChannels, enum UserFmtType SrcType, const ALvoid *data, ALsizei align, ALboolean storesrc);
 
+inline void LockBuffersRead(ALCdevice *device)
+{ LockUIntMapRead(&device->BufferMap); }
+inline void UnlockBuffersRead(ALCdevice *device)
+{ UnlockUIntMapRead(&device->BufferMap); }
+inline void LockBuffersWrite(ALCdevice *device)
+{ LockUIntMapWrite(&device->BufferMap); }
+inline void UnlockBuffersWrite(ALCdevice *device)
+{ UnlockUIntMapWrite(&device->BufferMap); }
+
 inline struct ALbuffer *LookupBuffer(ALCdevice *device, ALuint id)
-{ return (struct ALbuffer*)LookupUIntMapKey(&device->BufferMap, id); }
+{ return (struct ALbuffer*)LookupUIntMapKeyNoLock(&device->BufferMap, id); }
 inline struct ALbuffer *RemoveBuffer(ALCdevice *device, ALuint id)
-{ return (struct ALbuffer*)RemoveUIntMapKey(&device->BufferMap, id); }
+{ return (struct ALbuffer*)RemoveUIntMapKeyNoLock(&device->BufferMap, id); }
 
 ALvoid ReleaseALBuffers(ALCdevice *device);
 

+ 21 - 21
libs/openal-soft/OpenAL32/Include/alEffect.h

@@ -10,17 +10,16 @@ extern "C" {
 struct ALeffect;
 
 enum {
-    EAXREVERB = 0,
-    REVERB,
-    AUTOWAH,
-    CHORUS,
-    COMPRESSOR,
-    DISTORTION,
-    ECHO,
-    EQUALIZER,
-    FLANGER,
-    MODULATOR,
-    DEDICATED,
+    AL__EAXREVERB = 0,
+    AL__REVERB,
+    AL__CHORUS,
+    AL__COMPRESSOR,
+    AL__DISTORTION,
+    AL__ECHO,
+    AL__EQUALIZER,
+    AL__FLANGER,
+    AL__MODULATOR,
+    AL__DEDICATED,
 
     MAX_EFFECTS
 };
@@ -51,7 +50,6 @@ 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;
@@ -93,13 +91,6 @@ typedef union ALeffectProps {
         ALfloat LFReference;
     } Reverb;
 
-    struct {
-        ALfloat AttackTime;
-        ALfloat ReleaseTime;
-        ALfloat PeakGain;
-        ALfloat Resonance;
-    } Autowah;
-
     struct {
         ALint Waveform;
         ALint Phase;
@@ -176,10 +167,19 @@ typedef struct ALeffect {
     ALuint id;
 } ALeffect;
 
+inline void LockEffectsRead(ALCdevice *device)
+{ LockUIntMapRead(&device->EffectMap); }
+inline void UnlockEffectsRead(ALCdevice *device)
+{ UnlockUIntMapRead(&device->EffectMap); }
+inline void LockEffectsWrite(ALCdevice *device)
+{ LockUIntMapWrite(&device->EffectMap); }
+inline void UnlockEffectsWrite(ALCdevice *device)
+{ UnlockUIntMapWrite(&device->EffectMap); }
+
 inline struct ALeffect *LookupEffect(ALCdevice *device, ALuint id)
-{ return (struct ALeffect*)LookupUIntMapKey(&device->EffectMap, id); }
+{ return (struct ALeffect*)LookupUIntMapKeyNoLock(&device->EffectMap, id); }
 inline struct ALeffect *RemoveEffect(ALCdevice *device, ALuint id)
-{ return (struct ALeffect*)RemoveUIntMapKey(&device->EffectMap, id); }
+{ return (struct ALeffect*)RemoveUIntMapKeyNoLock(&device->EffectMap, id); }
 
 inline ALboolean IsReverbEffect(ALenum type)
 { return type == AL_EFFECT_REVERB || type == AL_EFFECT_EAXREVERB; }

+ 22 - 23
libs/openal-soft/OpenAL32/Include/alFilter.h

@@ -42,13 +42,11 @@ typedef enum ALfilterType {
 typedef struct ALfilterState {
     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) */
-    ALfloat b1, b2; /* Transfer function coefficients "b" (b0 is input_gain) */
-    ALfloat input_gain;
-
-    void (*process)(struct ALfilterState *self, ALfloat *restrict dst, const ALfloat *src, ALuint numsamples);
 } ALfilterState;
-#define ALfilterState_process(a, ...) ((a)->process((a), __VA_ARGS__))
+/* Currently only a C-based filter process method is implemented. */
+#define ALfilterState_process ALfilterState_processC
 
 /* Calculates the rcpQ (i.e. 1/Q) coefficient for shelving filters, using the
  * reference gain and shelf slope parameter.
@@ -79,26 +77,18 @@ inline void ALfilterState_clear(ALfilterState *filter)
 
 void ALfilterState_setParams(ALfilterState *filter, ALfilterType type, ALfloat gain, ALfloat freq_mult, ALfloat rcpQ);
 
-inline ALfloat ALfilterState_processSingle(ALfilterState *filter, ALfloat sample)
+inline void ALfilterState_copyParams(ALfilterState *restrict dst, const ALfilterState *restrict src)
 {
-    ALfloat outsmp;
-
-    outsmp = filter->input_gain * sample +
-             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] = sample;
-    filter->y[1] = filter->y[0];
-    filter->y[0] = outsmp;
-
-    return outsmp;
+    dst->b0 = src->b0;
+    dst->b1 = src->b1;
+    dst->b2 = src->b2;
+    dst->a1 = src->a1;
+    dst->a2 = src->a2;
 }
 
-void ALfilterState_processC(ALfilterState *filter, ALfloat *restrict dst, const ALfloat *src, ALuint numsamples);
+void ALfilterState_processC(ALfilterState *filter, ALfloat *restrict dst, const ALfloat *restrict src, ALsizei numsamples);
 
-inline void ALfilterState_processPassthru(ALfilterState *filter, const ALfloat *src, ALuint numsamples)
+inline void ALfilterState_processPassthru(ALfilterState *filter, const ALfloat *restrict src, ALsizei numsamples)
 {
     if(numsamples >= 2)
     {
@@ -151,10 +141,19 @@ typedef struct ALfilter {
 #define ALfilter_GetParamf(x, c, p, v)  ((x)->GetParamf((x),(c),(p),(v)))
 #define ALfilter_GetParamfv(x, c, p, v) ((x)->GetParamfv((x),(c),(p),(v)))
 
+inline void LockFiltersRead(ALCdevice *device)
+{ LockUIntMapRead(&device->FilterMap); }
+inline void UnlockFiltersRead(ALCdevice *device)
+{ UnlockUIntMapRead(&device->FilterMap); }
+inline void LockFiltersWrite(ALCdevice *device)
+{ LockUIntMapWrite(&device->FilterMap); }
+inline void UnlockFiltersWrite(ALCdevice *device)
+{ UnlockUIntMapWrite(&device->FilterMap); }
+
 inline struct ALfilter *LookupFilter(ALCdevice *device, ALuint id)
-{ return (struct ALfilter*)LookupUIntMapKey(&device->FilterMap, id); }
+{ return (struct ALfilter*)LookupUIntMapKeyNoLock(&device->FilterMap, id); }
 inline struct ALfilter *RemoveFilter(ALCdevice *device, ALuint id)
-{ return (struct ALfilter*)RemoveUIntMapKey(&device->FilterMap, id); }
+{ return (struct ALfilter*)RemoveUIntMapKeyNoLock(&device->FilterMap, id); }
 
 ALvoid ReleaseALFilters(ALCdevice *device);
 

+ 44 - 7
libs/openal-soft/OpenAL32/Include/alListener.h

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

+ 494 - 150
libs/openal-soft/OpenAL32/Include/alMain.h

@@ -3,6 +3,7 @@
 
 #include <string.h>
 #include <stdio.h>
+#include <stddef.h>
 #include <stdarg.h>
 #include <assert.h>
 #include <math.h>
@@ -20,36 +21,189 @@
 #include "AL/alc.h"
 #include "AL/alext.h"
 
-
-#if defined(_WIN64)
-#define SZFMT "%I64u"
-#elif defined(_WIN32)
-#define SZFMT "%u"
-#else
-#define SZFMT "%zu"
-#endif
-
-
 #include "static_assert.h"
 #include "align.h"
 #include "atomic.h"
 #include "uintmap.h"
 #include "vector.h"
 #include "alstring.h"
+#include "almalloc.h"
+#include "threads.h"
+
+#ifndef ALC_SOFT_loopback2
+#define ALC_SOFT_loopback2 1
+#define ALC_AMBISONIC_LAYOUT_SOFT                0x1997
+#define ALC_AMBISONIC_SCALING_SOFT               0x1998
+#define ALC_AMBISONIC_ORDER_SOFT                 0x1999
+
+#define ALC_BFORMAT3D_SOFT                       0x1508
 
-#include "hrtf.h"
+/* Ambisonic layouts */
+#define ALC_ACN_SOFT                             0x1600
+#define ALC_FUMA_SOFT                            0x1601
+
+/* Ambisonic scalings (normalization) */
+/*#define ALC_FUMA_SOFT*/
+#define ALC_SN3D_SOFT                            0x1602
+#define ALC_N3D_SOFT                             0x1603
+
+typedef ALCboolean (ALC_APIENTRY*LPALCISAMBISONICFORMATSUPPORTEDSOFT)(ALCdevice *device, ALCenum layout, ALCenum scaling, ALsizei order);
+#ifdef AL_ALEXT_PROTOTYPES
+ALC_API ALCboolean ALC_APIENTRY alcIsAmbisonicFormatSupportedSOFT(ALCdevice *device, ALCenum layout, ALCenum scaling, ALsizei order);
+#endif
+#endif
 
 #ifndef ALC_SOFT_device_clock
 #define ALC_SOFT_device_clock 1
 typedef int64_t ALCint64SOFT;
 typedef uint64_t ALCuint64SOFT;
 #define ALC_DEVICE_CLOCK_SOFT                    0x1600
+#define ALC_DEVICE_LATENCY_SOFT                  0x1601
+#define ALC_DEVICE_CLOCK_LATENCY_SOFT            0x1602
 typedef void (ALC_APIENTRY*LPALCGETINTEGER64VSOFT)(ALCdevice *device, ALCenum pname, ALsizei size, ALCint64SOFT *values);
 #ifdef AL_ALEXT_PROTOTYPES
 ALC_API void ALC_APIENTRY alcGetInteger64vSOFT(ALCdevice *device, ALCenum pname, ALsizei size, ALCint64SOFT *values);
 #endif
 #endif
 
+#ifndef AL_SOFT_buffer_samples2
+#define AL_SOFT_buffer_samples2 1
+/* Channel configurations */
+#define AL_MONO_SOFT                             0x1500
+#define AL_STEREO_SOFT                           0x1501
+#define AL_REAR_SOFT                             0x1502
+#define AL_QUAD_SOFT                             0x1503
+#define AL_5POINT1_SOFT                          0x1504
+#define AL_6POINT1_SOFT                          0x1505
+#define AL_7POINT1_SOFT                          0x1506
+#define AL_BFORMAT2D_SOFT                        0x1507
+#define AL_BFORMAT3D_SOFT                        0x1508
+
+/* Sample types */
+#define AL_BYTE_SOFT                             0x1400
+#define AL_UNSIGNED_BYTE_SOFT                    0x1401
+#define AL_SHORT_SOFT                            0x1402
+#define AL_UNSIGNED_SHORT_SOFT                   0x1403
+#define AL_INT_SOFT                              0x1404
+#define AL_UNSIGNED_INT_SOFT                     0x1405
+#define AL_FLOAT_SOFT                            0x1406
+#define AL_DOUBLE_SOFT                           0x1407
+#define AL_BYTE3_SOFT                            0x1408
+#define AL_UNSIGNED_BYTE3_SOFT                   0x1409
+#define AL_MULAW_SOFT                            0x140A
+
+/* Storage formats */
+#define AL_MONO8_SOFT                            0x1100
+#define AL_MONO16_SOFT                           0x1101
+#define AL_MONO32F_SOFT                          0x10010
+#define AL_STEREO8_SOFT                          0x1102
+#define AL_STEREO16_SOFT                         0x1103
+#define AL_STEREO32F_SOFT                        0x10011
+#define AL_QUAD8_SOFT                            0x1204
+#define AL_QUAD16_SOFT                           0x1205
+#define AL_QUAD32F_SOFT                          0x1206
+#define AL_REAR8_SOFT                            0x1207
+#define AL_REAR16_SOFT                           0x1208
+#define AL_REAR32F_SOFT                          0x1209
+#define AL_5POINT1_8_SOFT                        0x120A
+#define AL_5POINT1_16_SOFT                       0x120B
+#define AL_5POINT1_32F_SOFT                      0x120C
+#define AL_6POINT1_8_SOFT                        0x120D
+#define AL_6POINT1_16_SOFT                       0x120E
+#define AL_6POINT1_32F_SOFT                      0x120F
+#define AL_7POINT1_8_SOFT                        0x1210
+#define AL_7POINT1_16_SOFT                       0x1211
+#define AL_7POINT1_32F_SOFT                      0x1212
+#define AL_BFORMAT2D_8_SOFT                      0x20021
+#define AL_BFORMAT2D_16_SOFT                     0x20022
+#define AL_BFORMAT2D_32F_SOFT                    0x20023
+#define AL_BFORMAT3D_8_SOFT                      0x20031
+#define AL_BFORMAT3D_16_SOFT                     0x20032
+#define AL_BFORMAT3D_32F_SOFT                    0x20033
+
+/* Buffer attributes */
+#define AL_INTERNAL_FORMAT_SOFT                  0x2008
+#define AL_BYTE_LENGTH_SOFT                      0x2009
+#define AL_SAMPLE_LENGTH_SOFT                    0x200A
+#define AL_SEC_LENGTH_SOFT                       0x200B
+
+#if 0
+typedef void (AL_APIENTRY*LPALBUFFERSAMPLESSOFT)(ALuint,ALuint,ALenum,ALsizei,ALenum,ALenum,const ALvoid*);
+typedef void (AL_APIENTRY*LPALGETBUFFERSAMPLESSOFT)(ALuint,ALsizei,ALsizei,ALenum,ALenum,ALvoid*);
+typedef ALboolean (AL_APIENTRY*LPALISBUFFERFORMATSUPPORTEDSOFT)(ALenum);
+#ifdef AL_ALEXT_PROTOTYPES
+AL_API void AL_APIENTRY alBufferSamplesSOFT(ALuint buffer, ALuint samplerate, ALenum internalformat, ALsizei samples, ALenum channels, ALenum type, const ALvoid *data);
+AL_API void AL_APIENTRY alGetBufferSamplesSOFT(ALuint buffer, ALsizei offset, ALsizei samples, ALenum channels, ALenum type, ALvoid *data);
+AL_API ALboolean AL_APIENTRY alIsBufferFormatSupportedSOFT(ALenum format);
+#endif
+#endif
+#endif
+
+
+#if defined(_WIN64)
+#define SZFMT "%I64u"
+#elif defined(_WIN32)
+#define SZFMT "%u"
+#else
+#define SZFMT "%zu"
+#endif
+
+
+#ifdef __GNUC__
+/* Because of a long-standing deficiency in C, you're not allowed to implicitly
+ * cast a pointer-to-type-array to a pointer-to-const-type-array. For example,
+ *
+ * int (*ptr)[10];
+ * const int (*cptr)[10] = ptr;
+ *
+ * is not allowed and most compilers will generate noisy warnings about
+ * incompatible types, even though it just makes the array elements const.
+ * Clang will allow it if you make the array type a typedef, like this:
+ *
+ * typedef int int10[10];
+ * int10 *ptr;
+ * const int10 *cptr = ptr;
+ *
+ * however GCC does not and still issues the incompatible type warning. The
+ * "proper" way to fix it is to add an explicit cast for the constified type,
+ * but that removes the vast majority of otherwise useful type-checking you'd
+ * get, and runs the risk of improper casts if types are later changed. Leaving
+ * it non-const can also be an issue if you use it as a function parameter, and
+ * happen to have a const type as input (and also reduce the capabilities of
+ * the compiler to better optimize the function).
+ *
+ * So to work around the problem, we use a macro. The macro first assigns the
+ * incoming variable to the specified non-const type to ensure it's the correct
+ * type, then casts the variable as the desired constified type. Very ugly, but
+ * I'd rather not have hundreds of lines of warnings because I want to tell the
+ * compiler that some array(s) can't be changed by the code, or have lots of
+ * error-prone casts.
+ */
+#define SAFE_CONST(T, var) __extension__({                                    \
+    T _tmp = (var);                                                           \
+    (const T)_tmp;                                                            \
+})
+#else
+/* Non-GNU-compatible compilers have to use a straight cast with no extra
+ * checks, due to the lack of multi-statement expressions.
+ */
+#define SAFE_CONST(T, var) ((const T)(var))
+#endif
+
+
+#ifdef __GNUC__
+/* This helps cast away the const-ness of a pointer without accidentally
+ * changing the pointer type. This is necessary due to Clang's inability to use
+ * atomic_load on a const _Atomic variable.
+ */
+#define CONST_CAST(T, V) __extension__({                                      \
+    const T _tmp = (V);                                                       \
+    (T)_tmp;                                                                  \
+})
+#else
+#define CONST_CAST(T, V) ((T)(V))
+#endif
+
 
 typedef ALint64SOFT ALint64;
 typedef ALuint64SOFT ALuint64;
@@ -81,13 +235,17 @@ typedef ALuint64SOFT ALuint64;
 #endif
 
 #ifdef __GNUC__
-#define DECL_CONST __attribute__((const))
 #define DECL_FORMAT(x, y, z) __attribute__((format(x, (y), (z))))
 #else
-#define DECL_CONST
 #define DECL_FORMAT(x, y, z)
 #endif
 
+/* Calculates the size of a struct with N elements of a flexible array member.
+ * GCC and Clang allow offsetof(Type, fam[N]) for this, but MSVC seems to have
+ * trouble, so a bit more verbose workaround is needed.
+ */
+#define FAM_SIZE(T, M, N)  (offsetof(T, M) + sizeof(((T*)NULL)->M[0])*(N))
+
 #if defined(__GNUC__) && defined(__i386__)
 /* force_align_arg_pointer is required for proper function arguments aligning
  * when SSE code is used. Some systems (Windows, QNX) do not guarantee our
@@ -119,7 +277,7 @@ static const union {
 } EndianTest = { 1 };
 #define IS_LITTLE_ENDIAN (EndianTest.b[0] == 1)
 
-#define COUNTOF(x) (sizeof((x))/sizeof((x)[0]))
+#define COUNTOF(x) (sizeof(x) / sizeof(0[x]))
 
 
 #define DERIVE_FROM_TYPE(t)          t t##_parent
@@ -208,6 +366,12 @@ static void T##_Delete(void *ptr) { al_free(ptr); }
     {                                                                         \
         memset(_res, 0, sizeof(T));                                           \
         T##_Construct(_res, EXTRACT_NEW_ARGS
+#define NEW_OBJ0(_res, T) do {                                                \
+    _res = T##_New(sizeof(T));                                                \
+    if(_res)                                                                  \
+    {                                                                         \
+        memset(_res, 0, sizeof(T));                                           \
+        T##_Construct(_res EXTRACT_NEW_ARGS
 
 
 #ifdef __cplusplus
@@ -215,6 +379,8 @@ extern "C" {
 #endif
 
 struct Hrtf;
+struct HrtfEntry;
+struct Compressor;
 
 
 #define DEFAULT_OUTPUT_RATE  (44100)
@@ -236,6 +402,31 @@ inline ALuint NextPowerOf2(ALuint value)
     return value+1;
 }
 
+/** Round up a value to the next multiple. */
+inline size_t RoundUp(size_t value, size_t r)
+{
+    value += r-1;
+    return value - (value%r);
+}
+
+/* Scales the given value using 64-bit integer math, rounding the result. */
+inline ALuint64 ScaleRound(ALuint64 val, ALuint64 new_scale, ALuint64 old_scale)
+{
+    return (val*new_scale + old_scale/2) / old_scale;
+}
+
+/* Scales the given value using 64-bit integer math, flooring the result. */
+inline ALuint64 ScaleFloor(ALuint64 val, ALuint64 new_scale, ALuint64 old_scale)
+{
+    return val * new_scale / old_scale;
+}
+
+/* Scales the given value using 64-bit integer math, ceiling the result. */
+inline ALuint64 ScaleCeil(ALuint64 val, ALuint64 new_scale, ALuint64 old_scale)
+{
+    return (val*new_scale + old_scale-1) / old_scale;
+}
+
 /* Fast float-to-int conversion. Assumes the FPU is already in round-to-zero
  * mode. */
 inline ALint fastf2i(ALfloat f)
@@ -252,45 +443,12 @@ inline ALint fastf2i(ALfloat f)
 #endif
 }
 
-/* Fast float-to-uint conversion. Assumes the FPU is already in round-to-zero
- * mode. */
-inline ALuint fastf2u(ALfloat f)
-{ return fastf2i(f); }
-
 
 enum DevProbe {
     ALL_DEVICE_PROBE,
     CAPTURE_DEVICE_PROBE
 };
 
-typedef struct {
-    ALCenum (*OpenPlayback)(ALCdevice*, const ALCchar*);
-    void (*ClosePlayback)(ALCdevice*);
-    ALCboolean (*ResetPlayback)(ALCdevice*);
-    ALCboolean (*StartPlayback)(ALCdevice*);
-    void (*StopPlayback)(ALCdevice*);
-
-    ALCenum (*OpenCapture)(ALCdevice*, const ALCchar*);
-    void (*CloseCapture)(ALCdevice*);
-    void (*StartCapture)(ALCdevice*);
-    void (*StopCapture)(ALCdevice*);
-    ALCenum (*CaptureSamples)(ALCdevice*, void*, ALCuint);
-    ALCuint (*AvailableSamples)(ALCdevice*);
-} BackendFuncs;
-
-ALCboolean alc_sndio_init(BackendFuncs *func_list);
-void alc_sndio_deinit(void);
-void alc_sndio_probe(enum DevProbe type);
-ALCboolean alc_ca_init(BackendFuncs *func_list);
-void alc_ca_deinit(void);
-void alc_ca_probe(enum DevProbe type);
-ALCboolean alc_opensl_init(BackendFuncs *func_list);
-void alc_opensl_deinit(void);
-void alc_opensl_probe(enum DevProbe type);
-ALCboolean alc_qsa_init(BackendFuncs *func_list);
-void alc_qsa_deinit(void);
-void alc_qsa_probe(enum DevProbe type);
-
 struct ALCbackend;
 
 
@@ -317,10 +475,31 @@ enum Channel {
     SideLeft,
     SideRight,
 
-    BFormatW,
-    BFormatX,
-    BFormatY,
-    BFormatZ,
+    UpperFrontLeft,
+    UpperFrontRight,
+    UpperBackLeft,
+    UpperBackRight,
+    LowerFrontLeft,
+    LowerFrontRight,
+    LowerBackLeft,
+    LowerBackRight,
+
+    Aux0,
+    Aux1,
+    Aux2,
+    Aux3,
+    Aux4,
+    Aux5,
+    Aux6,
+    Aux7,
+    Aux8,
+    Aux9,
+    Aux10,
+    Aux11,
+    Aux12,
+    Aux13,
+    Aux14,
+    Aux15,
 
     InvalidChannel
 };
@@ -345,23 +524,37 @@ enum DevFmtChannels {
     DevFmtX51    = ALC_5POINT1_SOFT,
     DevFmtX61    = ALC_6POINT1_SOFT,
     DevFmtX71    = ALC_7POINT1_SOFT,
+    DevFmtAmbi3D = ALC_BFORMAT3D_SOFT,
 
     /* Similar to 5.1, except using rear channels instead of sides */
     DevFmtX51Rear = 0x80000000,
 
-    DevFmtBFormat3D,
-
     DevFmtChannelsDefault = DevFmtStereo
 };
-#define MAX_OUTPUT_CHANNELS  (8)
+#define MAX_OUTPUT_CHANNELS  (16)
 
-ALuint BytesFromDevFmt(enum DevFmtType type) DECL_CONST;
-ALuint ChannelsFromDevFmt(enum DevFmtChannels chans) DECL_CONST;
-inline ALuint FrameSizeFromDevFmt(enum DevFmtChannels chans, enum DevFmtType type)
+ALsizei BytesFromDevFmt(enum DevFmtType type);
+ALsizei ChannelsFromDevFmt(enum DevFmtChannels chans, ALsizei ambiorder);
+inline ALsizei FrameSizeFromDevFmt(enum DevFmtChannels chans, enum DevFmtType type, ALsizei ambiorder)
 {
-    return ChannelsFromDevFmt(chans) * BytesFromDevFmt(type);
+    return ChannelsFromDevFmt(chans, ambiorder) * BytesFromDevFmt(type);
 }
 
+enum AmbiLayout {
+    AmbiLayout_FuMa = ALC_FUMA_SOFT, /* FuMa channel order */
+    AmbiLayout_ACN = ALC_ACN_SOFT,   /* ACN channel order */
+
+    AmbiLayout_Default = AmbiLayout_ACN
+};
+
+enum AmbiNorm {
+    AmbiNorm_FuMa = ALC_FUMA_SOFT, /* FuMa normalization */
+    AmbiNorm_SN3D = ALC_SN3D_SOFT, /* SN3D normalization */
+    AmbiNorm_N3D = ALC_N3D_SOFT,   /* N3D normalization */
+
+    AmbiNorm_Default = AmbiNorm_SN3D
+};
+
 
 extern const struct EffectList {
     const char *name;
@@ -378,25 +571,57 @@ enum DeviceType {
 };
 
 
-enum HrtfMode {
-    DisabledHrtf,
-    BasicHrtf,
-    FullHrtf
+enum RenderMode {
+    NormalRender,
+    StereoPair,
+    HrtfRender
 };
 
 
 /* The maximum number of Ambisonics coefficients. For a given order (o), the
  * size needed will be (o+1)**2, thus zero-order has 1, first-order has 4,
- * second-order has 9, and third-order has 16. */
-#define MAX_AMBI_COEFFS 16
+ * second-order has 9, third-order has 16, and fourth-order has 25.
+ */
+#define MAX_AMBI_ORDER  3
+#define MAX_AMBI_COEFFS ((MAX_AMBI_ORDER+1) * (MAX_AMBI_ORDER+1))
+
+/* A bitmask of ambisonic channels with height information. If none of these
+ * channels are used/needed, there's no height (e.g. with most surround sound
+ * speaker setups). This only specifies up to 4th order, which is the highest
+ * order a 32-bit mask value can specify (a 64-bit mask could handle up to 7th
+ * order). This is ACN ordering, with bit 0 being ACN 0, etc.
+ */
+#define AMBI_PERIPHONIC_MASK (0xfe7ce4)
+
+/* The maximum number of Ambisonic coefficients for 2D (non-periphonic)
+ * representation. This is 2 per each order above zero-order, plus 1 for zero-
+ * order. Or simply, o*2 + 1.
+ */
+#define MAX_AMBI2D_COEFFS (MAX_AMBI_ORDER*2 + 1)
+
 
 typedef ALfloat ChannelConfig[MAX_AMBI_COEFFS];
+typedef struct BFChannelConfig {
+    ALfloat Scale;
+    ALsizei Index;
+} BFChannelConfig;
+
+typedef union AmbiConfig {
+    /* Ambisonic coefficients for mixing to the dry buffer. */
+    ChannelConfig Coeffs[MAX_OUTPUT_CHANNELS];
+    /* Coefficient channel mapping for mixing to the dry buffer. */
+    BFChannelConfig Map[MAX_OUTPUT_CHANNELS];
+} AmbiConfig;
 
 
 #define HRTF_HISTORY_BITS   (6)
 #define HRTF_HISTORY_LENGTH (1<<HRTF_HISTORY_BITS)
 #define HRTF_HISTORY_MASK   (HRTF_HISTORY_LENGTH-1)
 
+#define HRIR_BITS        (7)
+#define HRIR_LENGTH      (1<<HRIR_BITS)
+#define HRIR_MASK        (HRIR_LENGTH-1)
+
 typedef struct HrtfState {
     alignas(16) ALfloat History[HRTF_HISTORY_LENGTH];
     alignas(16) ALfloat Values[HRIR_LENGTH][2];
@@ -404,18 +629,43 @@ typedef struct HrtfState {
 
 typedef struct HrtfParams {
     alignas(16) ALfloat Coeffs[HRIR_LENGTH][2];
-    alignas(16) ALfloat CoeffStep[HRIR_LENGTH][2];
-    ALuint Delay[2];
-    ALint DelayStep[2];
+    ALsizei Delay[2];
+    ALfloat Gain;
 } HrtfParams;
 
+typedef struct DirectHrtfState {
+    /* HRTF filter state for dry buffer content */
+    ALsizei Offset;
+    ALsizei IrSize;
+    struct {
+        alignas(16) ALfloat Values[HRIR_LENGTH][2];
+        alignas(16) ALfloat Coeffs[HRIR_LENGTH][2];
+    } Chan[];
+} DirectHrtfState;
+
+typedef struct EnumeratedHrtf {
+    al_string name;
+
+    struct HrtfEntry *hrtf;
+} EnumeratedHrtf;
+TYPEDEF_VECTOR(EnumeratedHrtf, vector_EnumeratedHrtf)
+
+
+/* Maximum delay in samples for speaker distance compensation. */
+#define MAX_DELAY_LENGTH 1024
+
+typedef struct DistanceComp {
+    ALfloat Gain;
+    ALsizei Length; /* Valid range is [0...MAX_DELAY_LENGTH). */
+    ALfloat *Buffer;
+} DistanceComp;
 
 /* Size for temporary storage of buffer data, in ALfloats. Larger values need
  * more memory, while smaller values may need more iterations. The value needs
  * to be a sensible size, however, as it constrains the max stepping value used
  * for mixing, as well as the maximum number of samples per mixing iteration.
  */
-#define BUFFERSIZE (2048u)
+#define BUFFERSIZE 2048
 
 struct ALCdevice_struct
 {
@@ -424,25 +674,31 @@ struct ALCdevice_struct
     ALCboolean Connected;
     enum DeviceType Type;
 
-    ALuint       Frequency;
-    ALuint       UpdateSize;
-    ALuint       NumUpdates;
+    ALuint Frequency;
+    ALuint UpdateSize;
+    ALuint NumUpdates;
     enum DevFmtChannels FmtChans;
     enum DevFmtType     FmtType;
-    ALboolean    IsHeadphones;
+    ALboolean IsHeadphones;
+    ALsizei AmbiOrder;
+    /* For DevFmtAmbi* output only, specifies the channel order and
+     * normalization.
+     */
+    enum AmbiLayout AmbiLayout;
+    enum AmbiNorm   AmbiScale;
 
     al_string DeviceName;
 
     ATOMIC(ALCenum) LastError;
 
     // Maximum number of sources that can be created
-    ALuint       MaxNoOfSources;
+    ALuint SourcesMax;
     // Maximum number of slots that can be created
-    ALuint       AuxiliaryEffectSlotMax;
+    ALuint AuxiliaryEffectSlotMax;
 
-    ALCuint      NumMonoSources;
-    ALCuint      NumStereoSources;
-    ALuint       NumAuxSends;
+    ALCuint NumMonoSources;
+    ALCuint NumStereoSources;
+    ALsizei NumAuxSends;
 
     // Map of Buffers for this device
     UIntMap BufferMap;
@@ -453,27 +709,31 @@ struct ALCdevice_struct
     // Map of Filters for this device
     UIntMap FilterMap;
 
-    /* HRTF filter tables */
-    vector_HrtfEntry Hrtf_List;
-    al_string Hrtf_Name;
-    const struct Hrtf *Hrtf;
-    ALCenum Hrtf_Status;
-    enum HrtfMode Hrtf_Mode;
-    HrtfState Hrtf_State[MAX_OUTPUT_CHANNELS];
-    HrtfParams Hrtf_Params[MAX_OUTPUT_CHANNELS];
-    ALuint Hrtf_Offset;
-
-    // Stereo-to-binaural filter
+    /* HRTF state and info */
+    DirectHrtfState *Hrtf;
+    al_string HrtfName;
+    struct Hrtf *HrtfHandle;
+    vector_EnumeratedHrtf HrtfList;
+    ALCenum HrtfStatus;
+
+    /* UHJ encoder state */
+    struct Uhj2Encoder *Uhj_Encoder;
+
+    /* High quality Ambisonic decoder */
+    struct BFormatDec *AmbiDecoder;
+
+    /* Stereo-to-binaural filter */
     struct bs2b *Bs2b;
 
+    /* First-order ambisonic upsampler for higher-order output */
+    struct AmbiUpsampler *AmbiUp;
+
+    /* Rendering mode. */
+    enum RenderMode Render_Mode;
+
     // Device flags
     ALuint Flags;
 
-    enum Channel ChannelName[MAX_OUTPUT_CHANNELS];
-    ChannelConfig AmbiCoeffs[MAX_OUTPUT_CHANNELS];
-    ALfloat AmbiScale; /* Scale for first-order XYZ inputs using AmbCoeffs. */
-    ALuint NumChannels;
-
     ALuint64 ClockBase;
     ALuint SamplesDone;
 
@@ -481,9 +741,55 @@ struct ALCdevice_struct
     alignas(16) ALfloat SourceData[BUFFERSIZE];
     alignas(16) ALfloat ResampledData[BUFFERSIZE];
     alignas(16) ALfloat FilteredData[BUFFERSIZE];
+    alignas(16) ALfloat NFCtrlData[BUFFERSIZE];
+
+    /* The "dry" path corresponds to the main output. */
+    struct {
+        AmbiConfig Ambi;
+        /* Number of coefficients in each Ambi.Coeffs to mix together (4 for
+         * first-order, 9 for second-order, etc). If the count is 0, Ambi.Map
+         * is used instead to map each output to a coefficient index.
+         */
+        ALsizei CoeffCount;
+
+        ALfloat (*Buffer)[BUFFERSIZE];
+        ALsizei NumChannels;
+        ALsizei NumChannelsPerOrder[MAX_AMBI_ORDER+1];
+    } Dry;
+
+    /* First-order ambisonics output, to be upsampled to the dry buffer if different. */
+    struct {
+        AmbiConfig Ambi;
+        /* Will only be 4 or 0. */
+        ALsizei CoeffCount;
+
+        ALfloat (*Buffer)[BUFFERSIZE];
+        ALsizei NumChannels;
+    } FOAOut;
+
+    /* "Real" output, which will be written to the device buffer. May alias the
+     * dry buffer.
+     */
+    struct {
+        enum Channel ChannelName[MAX_OUTPUT_CHANNELS];
+
+        ALfloat (*Buffer)[BUFFERSIZE];
+        ALsizei NumChannels;
+    } RealOut;
+
+    struct Compressor *Limiter;
+
+    /* The average speaker distance as determined by the ambdec configuration
+     * (or alternatively, by the NFC-HOA reference delay). Only used for NFC.
+     */
+    ALfloat AvgSpeakerDist;
+
+    /* Delay buffers used to compensate for speaker distances. */
+    DistanceComp ChannelDelay[MAX_OUTPUT_CHANNELS];
 
-    /* Dry path buffer mix. */
-    alignas(16) ALfloat (*DryBuffer)[BUFFERSIZE];
+    /* Dithering control. */
+    ALfloat DitherDepth;
+    ALuint DitherSeed;
 
     /* Running count of the mixer invocations, in 31.1 fixed point. This
      * actually increments *twice* when mixing, first at the start and then at
@@ -492,34 +798,27 @@ struct ALCdevice_struct
      */
     RefCount MixCount;
 
-    /* Default effect slot */
-    struct ALeffectslot *DefaultSlot;
-
     // Contexts created on this device
     ATOMIC(ALCcontext*) ContextList;
 
+    almtx_t BackendLock;
     struct ALCbackend *Backend;
 
-    void *ExtraData; // For the backend's use
-
     ALCdevice *volatile next;
-
-    /* Memory space used by the default slot (Playback devices only) */
-    alignas(16) ALCbyte _slot_mem[];
 };
 
 // Frequency was requested by the app or config file
-#define DEVICE_FREQUENCY_REQUEST                 (1<<1)
+#define DEVICE_FREQUENCY_REQUEST                 (1u<<1)
 // Channel configuration was requested by the config file
-#define DEVICE_CHANNELS_REQUEST                  (1<<2)
+#define DEVICE_CHANNELS_REQUEST                  (1u<<2)
 // Sample type was requested by the config file
-#define DEVICE_SAMPLE_TYPE_REQUEST               (1<<3)
+#define DEVICE_SAMPLE_TYPE_REQUEST               (1u<<3)
 
 // Specifies if the DSP is paused at user request
-#define DEVICE_PAUSED                            (1<<30)
+#define DEVICE_PAUSED                            (1u<<30)
 
 // Specifies if the device is currently running
-#define DEVICE_RUNNING                           (1<<31)
+#define DEVICE_RUNNING                           (1u<<31)
 
 
 /* Nanosecond resolution for the device clock time. */
@@ -533,8 +832,7 @@ struct ALCdevice_struct
 #define RECORD_THREAD_NAME "alsoft-record"
 
 
-struct ALCcontext_struct
-{
+struct ALCcontext_struct {
     RefCount ref;
 
     struct ALlistener *Listener;
@@ -544,28 +842,39 @@ struct ALCcontext_struct
 
     ATOMIC(ALenum) LastError;
 
-    ATOMIC(ALenum) UpdateSources;
+    enum DistanceModel DistanceModel;
+    ALboolean SourceDistanceModel;
+
+    ALfloat DopplerFactor;
+    ALfloat DopplerVelocity;
+    ALfloat SpeedOfSound;
+    ATOMIC(ALenum) DeferUpdates;
 
-    volatile enum DistanceModel DistanceModel;
-    volatile ALboolean SourceDistanceModel;
+    RWLock PropLock;
+
+    /* Counter for the pre-mixing updates, in 31.1 fixed point (lowest bit
+     * indicates if updates are currently happening).
+     */
+    RefCount UpdateCount;
+    ATOMIC(ALenum) HoldUpdates;
 
-    volatile ALfloat DopplerFactor;
-    volatile ALfloat DopplerVelocity;
-    volatile ALfloat SpeedOfSound;
-    volatile ALenum  DeferUpdates;
+    ALfloat GainBoost;
 
-    struct ALvoice *Voices;
+    struct ALvoice **Voices;
     ALsizei VoiceCount;
     ALsizei MaxVoices;
 
-    VECTOR(struct ALeffectslot*) ActiveAuxSlots;
+    ATOMIC(struct ALeffectslotArray*) ActiveAuxSlots;
+
+    /* Default effect slot */
+    struct ALeffectslot *DefaultSlot;
 
     ALCdevice  *Device;
     const ALCchar *ExtensionList;
 
     ALCcontext *volatile next;
 
-    /* Memory space used by the listener */
+    /* Memory space used by the listener (and possibly default effect slot) */
     alignas(16) ALCbyte _listener_mem[];
 };
 
@@ -574,6 +883,8 @@ ALCcontext *GetContextRef(void);
 void ALCcontext_IncRef(ALCcontext *context);
 void ALCcontext_DecRef(ALCcontext *context);
 
+void AllocateVoices(ALCcontext *context, ALsizei num_voices, ALsizei old_sends);
+
 void AppendAllDevicesList(const ALCchar *name);
 void AppendCaptureDeviceList(const ALCchar *name);
 
@@ -583,21 +894,13 @@ void ALCdevice_Unlock(ALCdevice *device);
 void ALCcontext_DeferUpdates(ALCcontext *context);
 void ALCcontext_ProcessUpdates(ALCcontext *context);
 
-inline void LockContext(ALCcontext *context)
-{ ALCdevice_Lock(context->Device); }
-
-inline void UnlockContext(ALCcontext *context)
-{ ALCdevice_Unlock(context->Device); }
-
-
-void *al_malloc(size_t alignment, size_t size);
-void *al_calloc(size_t alignment, size_t size);
-void al_free(void *ptr);
-
 
 typedef struct {
 #ifdef HAVE_FENV_H
     DERIVE_FROM_TYPE(fenv_t);
+#ifdef _WIN32
+    int round_mode;
+#endif
 #else
     int state;
 #endif
@@ -607,15 +910,19 @@ typedef struct {
 } FPUCtl;
 void SetMixerFPUMode(FPUCtl *ctl);
 void RestoreFPUMode(const FPUCtl *ctl);
+#ifdef __GNUC__
+/* Use an alternate macro set with GCC to avoid accidental continue or break
+ * statements within the mixer mode.
+ */
+#define START_MIXER_MODE() __extension__({ FPUCtl _oldMode; SetMixerFPUMode(&_oldMode);
+#define END_MIXER_MODE() RestoreFPUMode(&_oldMode); })
+#else
+#define START_MIXER_MODE() do { FPUCtl _oldMode; SetMixerFPUMode(&_oldMode);
+#define END_MIXER_MODE() RestoreFPUMode(&_oldMode); } while(0)
+#endif
+#define LEAVE_MIXER_MODE() RestoreFPUMode(&_oldMode)
 
 
-typedef struct RingBuffer RingBuffer;
-RingBuffer *CreateRingBuffer(ALsizei frame_size, ALsizei length);
-void DestroyRingBuffer(RingBuffer *ring);
-ALsizei RingBufferSize(RingBuffer *ring);
-void WriteRingBuffer(RingBuffer *ring, const ALubyte *data, ALsizei len);
-void ReadRingBuffer(RingBuffer *ring, ALubyte *data, ALsizei len);
-
 typedef struct ll_ringbuffer ll_ringbuffer_t;
 typedef struct ll_ringbuffer_data {
     char *buf;
@@ -651,26 +958,26 @@ void SetRTPriority(void);
 void SetDefaultChannelOrder(ALCdevice *device);
 void SetDefaultWFXChannelOrder(ALCdevice *device);
 
-const ALCchar *DevFmtTypeString(enum DevFmtType type) DECL_CONST;
-const ALCchar *DevFmtChannelsString(enum DevFmtChannels chans) DECL_CONST;
+const ALCchar *DevFmtTypeString(enum DevFmtType type);
+const ALCchar *DevFmtChannelsString(enum DevFmtChannels chans);
 
 /**
  * GetChannelIdxByName
  *
- * Returns the device's channel index given a channel name (e.g. FrontCenter),
- * or -1 if it doesn't exist.
+ * Returns the index for the given channel name (e.g. FrontCenter), or -1 if it
+ * doesn't exist.
  */
-inline ALint GetChannelIdxByName(const ALCdevice *device, enum Channel chan)
+inline ALint GetChannelIndex(const enum Channel names[MAX_OUTPUT_CHANNELS], enum Channel chan)
 {
-    ALint i = 0;
+    ALint i;
     for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
     {
-        if(device->ChannelName[i] == chan)
+        if(names[i] == chan)
             return i;
     }
     return -1;
 }
-
+#define GetChannelIdxByName(x, c) GetChannelIndex((x).ChannelName, (c))
 
 extern FILE *LogFile;
 
@@ -681,6 +988,13 @@ void al_print(const char *type, const char *func, const char *fmt, ...) DECL_FOR
 #define AL_PRINT(T, ...) al_print((T), __FUNCTION__, __VA_ARGS__)
 #endif
 
+#ifdef __ANDROID__
+#include <android/log.h>
+#define LOG_ANDROID(T, MSG, ...) __android_log_print(T, "openal", "AL lib: %s: "MSG, __FUNCTION__ , ## __VA_ARGS__)
+#else
+#define LOG_ANDROID(T, MSG, ...) ((void)0)
+#endif
+
 enum LogLevel {
     NoLog,
     LogError,
@@ -698,16 +1012,19 @@ extern enum LogLevel LogLevel;
 #define TRACE(...) do {                                                       \
     if(LogLevel >= LogTrace)                                                  \
         AL_PRINT("(II)", __VA_ARGS__);                                        \
+    LOG_ANDROID(ANDROID_LOG_DEBUG, __VA_ARGS__);                              \
 } while(0)
 
 #define WARN(...) do {                                                        \
     if(LogLevel >= LogWarning)                                                \
         AL_PRINT("(WW)", __VA_ARGS__);                                        \
+    LOG_ANDROID(ANDROID_LOG_WARN, __VA_ARGS__);                               \
 } while(0)
 
 #define ERR(...) do {                                                         \
     if(LogLevel >= LogError)                                                  \
         AL_PRINT("(EE)", __VA_ARGS__);                                        \
+    LOG_ANDROID(ANDROID_LOG_ERROR, __VA_ARGS__);                              \
 } while(0)
 
 
@@ -725,14 +1042,41 @@ enum {
 
 void FillCPUCaps(ALuint capfilter);
 
-FILE *OpenDataFile(const char *fname, const char *subdir);
-
 vector_al_string SearchDataFiles(const char *match, const char *subdir);
 
-/* Small hack to use a pointer-to-array type as a normal argument type.
- * Shouldn't be used directly. */
+/* Small hack to use a pointer-to-array types as a normal argument type.
+ * Shouldn't be used directly.
+ */
 typedef ALfloat ALfloatBUFFERSIZE[BUFFERSIZE];
+typedef ALfloat ALfloat2[2];
+
+
+/* The compressor requires the following information for proper
+ * initialization:
+ *
+ *   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.
+ *   ThresholdDb    - Triggering threshold (in dB).
+ *   KneeDb         - Knee width (below threshold; in dB).
+ *   SampleRate     - Sample rate to process.
+ */
+struct 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);
+
+ALuint GetCompressorSampleRate(const struct Compressor *Comp);
 
+void ApplyCompression(struct Compressor *Comp, const ALsizei NumChans, const ALsizei SamplesToDo,
+                      ALfloat (*restrict OutBuffer)[BUFFERSIZE]);
 
 #ifdef __cplusplus
 }

+ 71 - 91
libs/openal-soft/OpenAL32/Include/alSource.h

@@ -1,11 +1,14 @@
 #ifndef _AL_SOURCE_H_
 #define _AL_SOURCE_H_
 
-#define MAX_SENDS                 4
-
+#include "bool.h"
 #include "alMain.h"
 #include "alu.h"
 #include "hrtf.h"
+#include "atomic.h"
+
+#define MAX_SENDS      16
+#define DEFAULT_SENDS  2
 
 #ifdef __cplusplus
 extern "C" {
@@ -17,95 +20,48 @@ struct ALsource;
 
 typedef struct ALbufferlistitem {
     struct ALbuffer *buffer;
-    struct ALbufferlistitem *volatile next;
-    struct ALbufferlistitem *volatile prev;
+    ATOMIC(struct ALbufferlistitem*) next;
 } ALbufferlistitem;
 
 
-typedef struct ALvoice {
-    struct ALsource *volatile Source;
-
-    /** Method to update mixing parameters. */
-    ALvoid (*Update)(struct ALvoice *self, const struct ALsource *source, const ALCcontext *context);
-
-    /** Current target parameters used for mixing. */
-    ALint Step;
-
-    ALboolean IsHrtf;
-
-    ALuint Offset; /* Number of output samples mixed since starting. */
-
-    alignas(16) ALfloat PrevSamples[MAX_INPUT_CHANNELS][MAX_PRE_SAMPLES];
-
-    BsincState SincState;
-
-    DirectParams Direct;
-    SendParams Send[MAX_SENDS];
-} ALvoice;
-
-
 typedef struct ALsource {
     /** Source properties. */
-    volatile ALfloat   Pitch;
-    volatile ALfloat   Gain;
-    volatile ALfloat   OuterGain;
-    volatile ALfloat   MinGain;
-    volatile ALfloat   MaxGain;
-    volatile ALfloat   InnerAngle;
-    volatile ALfloat   OuterAngle;
-    volatile ALfloat   RefDistance;
-    volatile ALfloat   MaxDistance;
-    volatile ALfloat   RollOffFactor;
-    aluVector Position;
-    aluVector Velocity;
-    aluVector Direction;
-    volatile ALfloat   Orientation[2][3];
-    volatile ALboolean HeadRelative;
-    volatile ALboolean Looping;
-    volatile enum DistanceModel DistanceModel;
-    volatile ALboolean DirectChannels;
-
-    volatile ALboolean DryGainHFAuto;
-    volatile ALboolean WetGainAuto;
-    volatile ALboolean WetGainHFAuto;
-    volatile ALfloat   OuterGainHF;
-
-    volatile ALfloat AirAbsorptionFactor;
-    volatile ALfloat RoomRolloffFactor;
-    volatile ALfloat DopplerFactor;
-
-    volatile ALfloat Radius;
-
-    /**
-     * Last user-specified offset, and the offset type (bytes, samples, or
-     * seconds).
-     */
-    ALdouble Offset;
-    ALenum   OffsetType;
-
-    /** Source type (static, streaming, or undetermined) */
-    volatile ALint SourceType;
-
-    /** Source state (initial, playing, paused, or stopped) */
-    volatile ALenum state;
-    ALenum new_state;
-
-    /**
-     * Source offset in samples, relative to the currently playing buffer, NOT
-     * the whole queue, and the fractional (fixed-point) offset to the next
-     * sample.
+    ALfloat   Pitch;
+    ALfloat   Gain;
+    ALfloat   OuterGain;
+    ALfloat   MinGain;
+    ALfloat   MaxGain;
+    ALfloat   InnerAngle;
+    ALfloat   OuterAngle;
+    ALfloat   RefDistance;
+    ALfloat   MaxDistance;
+    ALfloat   RolloffFactor;
+    ALfloat   Position[3];
+    ALfloat   Velocity[3];
+    ALfloat   Direction[3];
+    ALfloat   Orientation[2][3];
+    ALboolean HeadRelative;
+    ALboolean Looping;
+    enum DistanceModel DistanceModel;
+    enum Resampler Resampler;
+    ALboolean DirectChannels;
+    enum SpatializeMode Spatialize;
+
+    ALboolean DryGainHFAuto;
+    ALboolean WetGainAuto;
+    ALboolean WetGainHFAuto;
+    ALfloat   OuterGainHF;
+
+    ALfloat AirAbsorptionFactor;
+    ALfloat RoomRolloffFactor;
+    ALfloat DopplerFactor;
+
+    /* NOTE: Stereo pan angles are specified in radians, counter-clockwise
+     * rather than clockwise.
      */
-    ALuint position;
-    ALuint position_fraction;
-
-    /** Source Buffer Queue info. */
-    ATOMIC(ALbufferlistitem*) queue;
-    ATOMIC(ALbufferlistitem*) current_buffer;
-    RWLock queue_lock;
+    ALfloat StereoPan[2];
 
-    /** Current buffer sample info. */
-    ALuint NumChannels;
-    ALuint SampleSize;
+    ALfloat Radius;
 
     /** Direct filter and auxiliary send info. */
     struct {
@@ -122,22 +78,46 @@ typedef struct ALsource {
         ALfloat HFReference;
         ALfloat GainLF;
         ALfloat LFReference;
-    } Send[MAX_SENDS];
+    } *Send;
+
+    /**
+     * Last user-specified offset, and the offset type (bytes, samples, or
+     * seconds).
+     */
+    ALdouble Offset;
+    ALenum   OffsetType;
+
+    /** Source type (static, streaming, or undetermined) */
+    ALint SourceType;
 
-    /** Source needs to update its mixing parameters. */
-    ATOMIC(ALenum) NeedsUpdate;
+    /** Source state (initial, playing, paused, or stopped) */
+    ATOMIC(ALenum) state;
+
+    /** Source Buffer Queue head. */
+    RWLock queue_lock;
+    ALbufferlistitem *queue;
+
+    ATOMIC_FLAG PropsClean;
 
     /** Self ID */
     ALuint id;
 } ALsource;
 
+inline void LockSourcesRead(ALCcontext *context)
+{ LockUIntMapRead(&context->SourceMap); }
+inline void UnlockSourcesRead(ALCcontext *context)
+{ UnlockUIntMapRead(&context->SourceMap); }
+inline void LockSourcesWrite(ALCcontext *context)
+{ LockUIntMapWrite(&context->SourceMap); }
+inline void UnlockSourcesWrite(ALCcontext *context)
+{ UnlockUIntMapWrite(&context->SourceMap); }
+
 inline struct ALsource *LookupSource(ALCcontext *context, ALuint id)
-{ return (struct ALsource*)LookupUIntMapKey(&context->SourceMap, id); }
+{ return (struct ALsource*)LookupUIntMapKeyNoLock(&context->SourceMap, id); }
 inline struct ALsource *RemoveSource(ALCcontext *context, ALuint id)
-{ return (struct ALsource*)RemoveUIntMapKey(&context->SourceMap, id); }
+{ return (struct ALsource*)RemoveUIntMapKeyNoLock(&context->SourceMap, id); }
 
-ALvoid SetSourceState(ALsource *Source, ALCcontext *Context, ALenum state);
-ALboolean ApplyOffset(ALsource *Source);
+void UpdateAllSourceProps(ALCcontext *context);
 
 ALvoid ReleaseALSources(ALCcontext *Context);
 

+ 293 - 107
libs/openal-soft/OpenAL32/Include/alu.h

@@ -13,9 +13,11 @@
 #include "alMain.h"
 #include "alBuffer.h"
 #include "alFilter.h"
+#include "alAuxEffectSlot.h"
 
 #include "hrtf.h"
 #include "align.h"
+#include "nfcfilter.h"
 #include "math_defs.h"
 
 
@@ -33,9 +35,30 @@ extern "C" {
 #endif
 
 struct ALsource;
+struct ALbufferlistitem;
 struct ALvoice;
+struct ALeffectslot;
 
 
+#define DITHER_RNG_SEED 22222
+
+
+enum SpatializeMode {
+    SpatializeOff = AL_FALSE,
+    SpatializeOn = AL_TRUE,
+    SpatializeAuto = AL_AUTO_SOFT
+};
+
+enum Resampler {
+    PointResampler,
+    LinearResampler,
+    FIR4Resampler,
+    BSincResampler,
+
+    ResamplerMax = BSincResampler
+};
+extern enum Resampler ResamplerDefault;
+
 /* The number of distinct scale and phase intervals within the filter table. */
 #define BSINC_SCALE_BITS  4
 #define BSINC_SCALE_COUNT (1<<BSINC_SCALE_BITS)
@@ -58,6 +81,17 @@ typedef struct BsincState {
     } coeffs[BSINC_PHASE_COUNT];
 } BsincState;
 
+typedef union InterpState {
+    BsincState bsinc;
+} InterpState;
+
+ALboolean BsincPrepare(const ALuint increment, BsincState *state);
+
+typedef const ALfloat* (*ResamplerFunc)(const InterpState *state,
+    const ALfloat *restrict src, ALsizei frac, ALint increment,
+    ALfloat *restrict dst, ALsizei dstlen
+);
+
 
 typedef union aluVector {
     alignas(16) ALfloat v[4];
@@ -75,6 +109,7 @@ inline void aluVectorSet(aluVector *vector, ALfloat x, ALfloat y, ALfloat z, ALf
 typedef union aluMatrixf {
     alignas(16) ALfloat m[4][4];
 } aluMatrixf;
+extern const aluMatrixf IdentityMatrixf;
 
 inline void aluMatrixfSetRow(aluMatrixf *matrix, ALuint row,
                              ALfloat m0, ALfloat m1, ALfloat m2, ALfloat m3)
@@ -97,31 +132,6 @@ inline void aluMatrixfSet(aluMatrixf *matrix, ALfloat m00, ALfloat m01, ALfloat
 }
 
 
-typedef union aluMatrixd {
-    alignas(16) ALdouble m[4][4];
-} aluMatrixd;
-
-inline void aluMatrixdSetRow(aluMatrixd *matrix, ALuint row,
-                             ALdouble m0, ALdouble m1, ALdouble m2, ALdouble m3)
-{
-    matrix->m[row][0] = m0;
-    matrix->m[row][1] = m1;
-    matrix->m[row][2] = m2;
-    matrix->m[row][3] = m3;
-}
-
-inline void aluMatrixdSet(aluMatrixd *matrix, ALdouble m00, ALdouble m01, ALdouble m02, ALdouble m03,
-                                              ALdouble m10, ALdouble m11, ALdouble m12, ALdouble m13,
-                                              ALdouble m20, ALdouble m21, ALdouble m22, ALdouble m23,
-                                              ALdouble m30, ALdouble m31, ALdouble m32, ALdouble m33)
-{
-    aluMatrixdSetRow(matrix, 0, m00, m01, m02, m03);
-    aluMatrixdSetRow(matrix, 1, m10, m11, m12, m13);
-    aluMatrixdSetRow(matrix, 2, m20, m21, m22, m23);
-    aluMatrixdSetRow(matrix, 3, m30, m31, m32, m33);
-}
-
-
 enum ActiveFilters {
     AF_None = 0,
     AF_LowPass = 1,
@@ -130,74 +140,200 @@ enum ActiveFilters {
 };
 
 
-typedef struct MixGains {
-    ALfloat Current;
-    ALfloat Step;
-    ALfloat Target;
-} MixGains;
+typedef struct MixHrtfParams {
+    const ALfloat (*Coeffs)[2];
+    ALsizei Delay[2];
+    ALfloat Gain;
+    ALfloat GainStep;
+} MixHrtfParams;
 
 
 typedef struct DirectParams {
-    ALfloat (*OutBuffer)[BUFFERSIZE];
-    ALuint OutChannels;
+    ALfilterState LowPass;
+    ALfilterState HighPass;
 
-    /* If not 'moving', gain/coefficients are set directly without fading. */
-    ALboolean Moving;
-    /* Stepping counter for gain/coefficient fading. */
-    ALuint Counter;
-    /* Last direction (relative to listener) and gain of a moving source. */
-    aluVector LastDir;
-    ALfloat LastGain;
+    NfcFilter NFCtrlFilter[MAX_AMBI_ORDER];
 
     struct {
-        enum ActiveFilters ActiveType;
-        ALfilterState LowPass;
-        ALfilterState HighPass;
-    } Filters[MAX_INPUT_CHANNELS];
+        HrtfParams Old;
+        HrtfParams Target;
+        HrtfState State;
+    } Hrtf;
 
     struct {
-        HrtfParams Params;
-        HrtfState State;
-    } Hrtf[MAX_INPUT_CHANNELS];
-    MixGains Gains[MAX_INPUT_CHANNELS][MAX_OUTPUT_CHANNELS];
+        ALfloat Current[MAX_OUTPUT_CHANNELS];
+        ALfloat Target[MAX_OUTPUT_CHANNELS];
+    } Gains;
 } DirectParams;
 
 typedef struct SendParams {
-    ALfloat (*OutBuffer)[BUFFERSIZE];
-
-    ALboolean Moving;
-    ALuint Counter;
+    ALfilterState LowPass;
+    ALfilterState HighPass;
 
     struct {
-        enum ActiveFilters ActiveType;
-        ALfilterState LowPass;
-        ALfilterState HighPass;
-    } Filters[MAX_INPUT_CHANNELS];
-
-    /* Gain control, which applies to each input channel to a single (mono)
-     * output buffer. */
-    MixGains Gains[MAX_INPUT_CHANNELS];
+        ALfloat Current[MAX_OUTPUT_CHANNELS];
+        ALfloat Target[MAX_OUTPUT_CHANNELS];
+    } Gains;
 } SendParams;
 
 
-typedef const ALfloat* (*ResamplerFunc)(const BsincState *state,
-    const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen
-);
+struct ALvoiceProps {
+    ATOMIC(struct ALvoiceProps*) next;
+
+    ALfloat Pitch;
+    ALfloat Gain;
+    ALfloat OuterGain;
+    ALfloat MinGain;
+    ALfloat MaxGain;
+    ALfloat InnerAngle;
+    ALfloat OuterAngle;
+    ALfloat RefDistance;
+    ALfloat MaxDistance;
+    ALfloat RolloffFactor;
+    ALfloat Position[3];
+    ALfloat Velocity[3];
+    ALfloat Direction[3];
+    ALfloat Orientation[2][3];
+    ALboolean HeadRelative;
+    enum DistanceModel DistanceModel;
+    enum Resampler Resampler;
+    ALboolean DirectChannels;
+    enum SpatializeMode SpatializeMode;
+
+    ALboolean DryGainHFAuto;
+    ALboolean WetGainAuto;
+    ALboolean WetGainHFAuto;
+    ALfloat   OuterGainHF;
+
+    ALfloat AirAbsorptionFactor;
+    ALfloat RoomRolloffFactor;
+    ALfloat DopplerFactor;
+
+    ALfloat StereoPan[2];
+
+    ALfloat Radius;
+
+    /** Direct filter and auxiliary send info. */
+    struct {
+        ALfloat Gain;
+        ALfloat GainHF;
+        ALfloat HFReference;
+        ALfloat GainLF;
+        ALfloat LFReference;
+    } Direct;
+    struct {
+        struct ALeffectslot *Slot;
+        ALfloat Gain;
+        ALfloat GainHF;
+        ALfloat HFReference;
+        ALfloat GainLF;
+        ALfloat LFReference;
+    } Send[];
+};
+
+/* If not 'fading', gain targets are used directly without fading. */
+#define VOICE_IS_FADING (1<<0)
+#define VOICE_HAS_HRTF  (1<<1)
+#define VOICE_HAS_NFC   (1<<2)
+
+typedef struct ALvoice {
+    struct ALvoiceProps *Props;
+
+    ATOMIC(struct ALvoiceProps*) Update;
+    ATOMIC(struct ALvoiceProps*) FreeList;
+
+    ATOMIC(struct ALsource*) Source;
+    ATOMIC(bool) Playing;
 
-typedef void (*MixerFunc)(const ALfloat *data, ALuint OutChans,
-                          ALfloat (*restrict OutBuffer)[BUFFERSIZE], struct MixGains *Gains,
-                          ALuint Counter, ALuint OutPos, ALuint BufferSize);
-typedef void (*HrtfMixerFunc)(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat *data,
-                              ALuint Counter, ALuint Offset, ALuint OutPos,
-                              const ALuint IrSize, const HrtfParams *hrtfparams,
-                              HrtfState *hrtfstate, ALuint BufferSize);
+    /**
+     * Source offset in samples, relative to the currently playing buffer, NOT
+     * the whole queue, and the fractional (fixed-point) offset to the next
+     * sample.
+     */
+    ATOMIC(ALuint) position;
+    ATOMIC(ALsizei) position_fraction;
 
+    /* Current buffer queue item being played. */
+    ATOMIC(struct ALbufferlistitem*) current_buffer;
+
+    /* Buffer queue item to loop to at end of queue (will be NULL for non-
+     * looping voices).
+     */
+    ATOMIC(struct ALbufferlistitem*) loop_buffer;
+
+    /**
+     * Number of channels and bytes-per-sample for the attached source's
+     * buffer(s).
+     */
+    ALsizei NumChannels;
+    ALsizei SampleSize;
+
+    /** Current target parameters used for mixing. */
+    ALint Step;
+
+    ResamplerFunc Resampler;
+
+    ALuint Flags;
+
+    ALuint Offset; /* Number of output samples mixed since starting. */
+
+    alignas(16) ALfloat PrevSamples[MAX_INPUT_CHANNELS][MAX_PRE_SAMPLES];
+
+    InterpState ResampleState;
+
+    struct {
+        enum ActiveFilters FilterType;
+        DirectParams Params[MAX_INPUT_CHANNELS];
+
+        ALfloat (*Buffer)[BUFFERSIZE];
+        ALsizei Channels;
+        ALsizei ChannelsPerOrder[MAX_AMBI_ORDER+1];
+    } Direct;
+
+    struct {
+        enum ActiveFilters FilterType;
+        SendParams Params[MAX_INPUT_CHANNELS];
+
+        ALfloat (*Buffer)[BUFFERSIZE];
+        ALsizei Channels;
+    } Send[];
+} ALvoice;
+
+void DeinitVoice(ALvoice *voice);
+
+
+typedef void (*MixerFunc)(const ALfloat *data, ALsizei OutChans,
+                          ALfloat (*restrict OutBuffer)[BUFFERSIZE], ALfloat *CurrentGains,
+                          const ALfloat *TargetGains, ALsizei Counter, ALsizei OutPos,
+                          ALsizei BufferSize);
+typedef void (*RowMixerFunc)(ALfloat *OutBuffer, const ALfloat *gains,
+                             const ALfloat (*restrict data)[BUFFERSIZE], ALsizei InChans,
+                             ALsizei InPos, ALsizei BufferSize);
+typedef void (*HrtfMixerFunc)(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
+                              const ALfloat *data, ALsizei Offset, ALsizei OutPos,
+                              const ALsizei IrSize, MixHrtfParams *hrtfparams,
+                              HrtfState *hrtfstate, ALsizei BufferSize);
+typedef void (*HrtfMixerBlendFunc)(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
+                                   const ALfloat *data, ALsizei Offset, ALsizei OutPos,
+                                   const ALsizei IrSize, const HrtfParams *oldparams,
+                                   MixHrtfParams *newparams, HrtfState *hrtfstate,
+                                   ALsizei BufferSize);
+typedef void (*HrtfDirectMixerFunc)(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
+                                    const ALfloat *data, ALsizei Offset, const ALsizei IrSize,
+                                    const ALfloat (*restrict Coeffs)[2],
+                                    ALfloat (*restrict Values)[2], ALsizei BufferSize);
+
+
+#define GAIN_MIX_MAX  (16.0f) /* +24dB */
 
 #define GAIN_SILENCE_THRESHOLD  (0.00001f) /* -100dB */
 
 #define SPEEDOFSOUNDMETRESPERSEC  (343.3f)
 #define AIRABSORBGAINHF           (0.99426f) /* -0.05dB */
 
+/* Target gain for the reverb decay feedback reaching the decay time. */
+#define REVERB_DECAY_GAIN  (0.001f) /* -60 dB */
+
 #define FRACTIONBITS (12)
 #define FRACTIONONE  (1<<FRACTIONBITS)
 #define FRACTIONMASK (FRACTIONONE-1)
@@ -246,79 +382,129 @@ inline ALuint64 clampu64(ALuint64 val, ALuint64 min, ALuint64 max)
 { return minu64(max, maxu64(min, val)); }
 
 
-union ResamplerCoeffs {
-    ALfloat FIR4[FRACTIONONE][4];
-    ALfloat FIR8[FRACTIONONE][8];
-};
-extern alignas(16) union ResamplerCoeffs ResampleCoeffs;
-
 extern alignas(16) const ALfloat bsincTab[18840];
+extern alignas(16) const ALfloat sinc4Tab[FRACTIONONE][4];
 
 
 inline ALfloat lerp(ALfloat val1, ALfloat val2, ALfloat mu)
 {
     return val1 + (val2-val1)*mu;
 }
-inline ALfloat resample_fir4(ALfloat val0, ALfloat val1, ALfloat val2, ALfloat val3, ALuint frac)
-{
-    const ALfloat *k = ResampleCoeffs.FIR4[frac];
-    return k[0]*val0 + k[1]*val1 + k[2]*val2 + k[3]*val3;
-}
-inline ALfloat resample_fir8(ALfloat val0, ALfloat val1, ALfloat val2, ALfloat val3, ALfloat val4, ALfloat val5, ALfloat val6, ALfloat val7, ALuint frac)
+inline ALfloat resample_fir4(ALfloat val0, ALfloat val1, ALfloat val2, ALfloat val3, ALsizei frac)
 {
-    const ALfloat *k = ResampleCoeffs.FIR8[frac];
-    return k[0]*val0 + k[1]*val1 + k[2]*val2 + k[3]*val3 +
-           k[4]*val4 + k[5]*val5 + k[6]*val6 + k[7]*val7;
+    return sinc4Tab[frac][0]*val0 + sinc4Tab[frac][1]*val1 +
+           sinc4Tab[frac][2]*val2 + sinc4Tab[frac][3]*val3;
 }
 
 
+enum HrtfRequestMode {
+    Hrtf_Default = 0,
+    Hrtf_Enable = 1,
+    Hrtf_Disable = 2,
+};
+
 void aluInitMixer(void);
 
-ALvoid aluInitPanning(ALCdevice *Device);
+MixerFunc SelectMixer(void);
+RowMixerFunc SelectRowMixer(void);
+ResamplerFunc SelectResampler(enum Resampler resampler);
+
+/* aluInitRenderer
+ *
+ * Set up the appropriate panning method and mixing method given the device
+ * properties.
+ */
+void aluInitRenderer(ALCdevice *device, ALint hrtf_id, enum HrtfRequestMode hrtf_appreq, enum HrtfRequestMode hrtf_userreq);
+
+void aluInitEffectPanning(struct ALeffectslot *slot);
 
 /**
- * ComputeDirectionalGains
+ * CalcDirectionCoeffs
  *
- * Sets channel gains based on a direction. The direction must be a 3-component
- * vector no longer than 1 unit.
+ * Calculates ambisonic coefficients based on a direction vector. The vector
+ * must be normalized (unit length), and the spread is the angular width of the
+ * sound (0...tau).
  */
-void ComputeDirectionalGains(const ALCdevice *device, const ALfloat dir[3], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]);
+void CalcDirectionCoeffs(const ALfloat dir[3], ALfloat spread, ALfloat coeffs[MAX_AMBI_COEFFS]);
 
 /**
- * ComputeAngleGains
+ * CalcAngleCoeffs
  *
- * Sets channel gains based on angle and elevation. The angle and elevation
- * parameters are in radians, going right and up respectively.
+ * Calculates ambisonic coefficients based on azimuth and elevation. The
+ * azimuth and elevation parameters are in radians, going right and up
+ * respectively.
  */
-void ComputeAngleGains(const ALCdevice *device, ALfloat angle, ALfloat elevation, ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]);
+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);
+}
 
 /**
- * ComputeAmbientGains
+ * CalcAnglePairwiseCoeffs
  *
- * Sets channel gains for ambient, omni-directional sounds.
+ * 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.
  */
-void ComputeAmbientGains(const ALCdevice *device, ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]);
+void CalcAnglePairwiseCoeffs(ALfloat azimuth, ALfloat elevation, ALfloat spread, ALfloat coeffs[MAX_AMBI_COEFFS]);
 
 /**
- * ComputeBFormatGains
+ * ComputeAmbientGains
  *
- * Sets channel gains for a given (first-order) B-Format channel. The matrix is
- * a 1x4 'slice' of the rotation matrix for a given channel used to orient the
- * coefficients.
+ * Computes channel gains for ambient, omni-directional sounds.
  */
-void ComputeBFormatGains(const ALCdevice *device, const ALfloat mtx[4], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]);
+#define ComputeAmbientGains(b, g, o) do {                                     \
+    if((b).CoeffCount > 0)                                                    \
+        ComputeAmbientGainsMC((b).Ambi.Coeffs, (b).NumChannels, g, o);        \
+    else                                                                      \
+        ComputeAmbientGainsBF((b).Ambi.Map, (b).NumChannels, g, o);           \
+} while (0)
+void ComputeAmbientGainsMC(const ChannelConfig *chancoeffs, ALsizei numchans, ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]);
+void ComputeAmbientGainsBF(const BFChannelConfig *chanmap, ALsizei numchans, ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]);
 
+/**
+ * ComputePanningGains
+ *
+ * Computes panning gains using the given channel decoder coefficients and the
+ * pre-calculated direction or angle coefficients.
+ */
+#define ComputePanningGains(b, c, g, o) do {                                  \
+    if((b).CoeffCount > 0)                                                    \
+        ComputePanningGainsMC((b).Ambi.Coeffs, (b).NumChannels, (b).CoeffCount, c, g, o);\
+    else                                                                      \
+        ComputePanningGainsBF((b).Ambi.Map, (b).NumChannels, c, g, o);        \
+} while (0)
+void ComputePanningGainsMC(const ChannelConfig *chancoeffs, 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]);
 
-ALvoid UpdateContextSources(ALCcontext *context);
+/**
+ * ComputeFirstOrderGains
+ *
+ * Sets channel gains for a first-order ambisonics input channel. The matrix is
+ * a 1x4 'slice' of a transform matrix for the input channel, used to scale and
+ * orient the sound samples.
+ */
+#define ComputeFirstOrderGains(b, m, g, o) do {                               \
+    if((b).CoeffCount > 0)                                                    \
+        ComputeFirstOrderGainsMC((b).Ambi.Coeffs, (b).NumChannels, m, g, o);  \
+    else                                                                      \
+        ComputeFirstOrderGainsBF((b).Ambi.Map, (b).NumChannels, m, g, o);     \
+} while (0)
+void ComputeFirstOrderGainsMC(const ChannelConfig *chancoeffs, 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]);
 
-ALvoid CalcSourceParams(struct ALvoice *voice, const struct ALsource *source, const ALCcontext *ALContext);
-ALvoid CalcNonAttnSourceParams(struct ALvoice *voice, const struct ALsource *source, const ALCcontext *ALContext);
 
-ALvoid MixSource(struct ALvoice *voice, struct ALsource *source, ALCdevice *Device, ALuint SamplesToDo);
+ALboolean MixSource(struct ALvoice *voice, struct ALsource *Source, ALCdevice *Device, ALsizei SamplesToDo);
 
-ALvoid aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size);
+void aluMixData(ALCdevice *device, ALvoid *OutBuffer, ALsizei NumSamples);
 /* Caller must lock the device. */
-ALvoid aluHandleDisconnect(ALCdevice *device);
+void aluHandleDisconnect(ALCdevice *device);
 
 extern ALfloat ConeScale;
 extern ALfloat ZScale;

+ 5 - 36
libs/openal-soft/OpenAL32/Include/bs2b.h

@@ -63,10 +63,10 @@ struct bs2b {
      * [0] - first channel, [1] - second channel
      */
     struct t_last_sample {
-        float asis[2];
-        float lo[2];
-        float hi[2];
-    } last_sample;
+        float asis;
+        float lo;
+        float hi;
+    } last_sample[2];
 };
 
 /* Clear buffers and set new coefficients with new crossfeed level and sample
@@ -85,38 +85,7 @@ int bs2b_get_srate(struct bs2b *bs2b);
 /* Clear buffer */
 void bs2b_clear(struct bs2b *bs2b);
 
-/* Crossfeeds one stereo sample that are pointed by sample.
- * [0] - first channel, [1] - second channel.
- * Returns crossfided sample by sample pointer.
- */
-inline void bs2b_cross_feed(struct bs2b *bs2b, float *restrict sample)
-{
-/* Single pole IIR filter.
- * O[n] = a0*I[n] + a1*I[n-1] + b1*O[n-1]
- */
-
-/* Lowpass filter */
-#define lo_filter(in, out_1) (bs2b->a0_lo*(in) + bs2b->b1_lo*(out_1))
-
-/* Highboost filter */
-#define hi_filter(in, in_1, out_1) (bs2b->a0_hi*(in) + bs2b->a1_hi*(in_1) + bs2b->b1_hi*(out_1))
-
-    /* Lowpass filter */
-    bs2b->last_sample.lo[0] = lo_filter(sample[0], bs2b->last_sample.lo[0]);
-    bs2b->last_sample.lo[1] = lo_filter(sample[1], bs2b->last_sample.lo[1]);
-
-    /* Highboost filter */
-    bs2b->last_sample.hi[0] = hi_filter(sample[0], bs2b->last_sample.asis[0], bs2b->last_sample.hi[0]);
-    bs2b->last_sample.hi[1] = hi_filter(sample[1], bs2b->last_sample.asis[1], bs2b->last_sample.hi[1]);
-    bs2b->last_sample.asis[0] = sample[0];
-    bs2b->last_sample.asis[1] = sample[1];
-
-    /* Crossfeed */
-    sample[0] = bs2b->last_sample.hi[0] + bs2b->last_sample.lo[1];
-    sample[1] = bs2b->last_sample.hi[1] + bs2b->last_sample.lo[0];
-#undef hi_filter
-#undef lo_filter
-} /* bs2b_cross_feed */
+void bs2b_cross_feed(struct bs2b *bs2b, float *restrict Left, float *restrict Right, int SamplesToDo);
 
 #ifdef __cplusplus
 }    /* extern "C" */

+ 283 - 106
libs/openal-soft/OpenAL32/alAuxEffectSlot.c

@@ -29,16 +29,19 @@
 #include "alAuxEffectSlot.h"
 #include "alThunk.h"
 #include "alError.h"
+#include "alListener.h"
 #include "alSource.h"
 
+#include "almalloc.h"
 
+
+extern inline void LockEffectSlotsRead(ALCcontext *context);
+extern inline void UnlockEffectSlotsRead(ALCcontext *context);
+extern inline void LockEffectSlotsWrite(ALCcontext *context);
+extern inline void UnlockEffectSlotsWrite(ALCcontext *context);
 extern inline struct ALeffectslot *LookupEffectSlot(ALCcontext *context, ALuint id);
 extern inline struct ALeffectslot *RemoveEffectSlot(ALCcontext *context, ALuint id);
 
-static ALenum AddEffectSlotArray(ALCcontext *Context, ALeffectslot **start, ALsizei count);
-static void RemoveEffectSlotArray(ALCcontext *Context, const ALeffectslot *slot);
-
-
 static UIntMap EffectStateFactoryMap;
 static inline ALeffectStateFactory *getFactoryByType(ALenum type)
 {
@@ -48,24 +51,32 @@ static inline ALeffectStateFactory *getFactoryByType(ALenum type)
     return NULL;
 }
 
+static void ALeffectState_IncRef(ALeffectState *state);
+static void ALeffectState_DecRef(ALeffectState *state);
+
+#define DO_UPDATEPROPS() do {                                                 \
+    if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))          \
+        UpdateEffectSlotProps(slot);                                          \
+    else                                                                      \
+        ATOMIC_FLAG_CLEAR(&slot->PropsClean, almemory_order_release);         \
+} while(0)
+
 
 AL_API ALvoid AL_APIENTRY alGenAuxiliaryEffectSlots(ALsizei n, ALuint *effectslots)
 {
     ALCcontext *context;
-    VECTOR(ALeffectslot*) slotvec;
+    ALeffectslot **tmpslots = NULL;
     ALsizei cur;
     ALenum err;
 
     context = GetContextRef();
     if(!context) return;
 
-    VECTOR_INIT(slotvec);
-
     if(!(n >= 0))
         SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
-    if(!VECTOR_RESERVE(slotvec, n))
-        SET_ERROR_AND_GOTO(context, AL_OUT_OF_MEMORY, done);
+    tmpslots = al_malloc(DEF_ALIGN, sizeof(ALeffectslot*)*n);
 
+    LockEffectSlotsWrite(context);
     for(cur = 0;cur < n;cur++)
     {
         ALeffectslot *slot = al_calloc(16, sizeof(ALeffectslot));
@@ -73,37 +84,57 @@ AL_API ALvoid AL_APIENTRY alGenAuxiliaryEffectSlots(ALsizei n, ALuint *effectslo
         if(!slot || (err=InitEffectSlot(slot)) != AL_NO_ERROR)
         {
             al_free(slot);
+            UnlockEffectSlotsWrite(context);
+
             alDeleteAuxiliaryEffectSlots(cur, effectslots);
             SET_ERROR_AND_GOTO(context, err, done);
         }
 
         err = NewThunkEntry(&slot->id);
         if(err == AL_NO_ERROR)
-            err = InsertUIntMapEntry(&context->EffectSlotMap, slot->id, slot);
+            err = InsertUIntMapEntryNoLock(&context->EffectSlotMap, slot->id, slot);
         if(err != AL_NO_ERROR)
         {
             FreeThunkEntry(slot->id);
-            DELETE_OBJ(slot->EffectState);
+            ALeffectState_DecRef(slot->Effect.State);
+            if(slot->Params.EffectState)
+                ALeffectState_DecRef(slot->Params.EffectState);
             al_free(slot);
+            UnlockEffectSlotsWrite(context);
 
             alDeleteAuxiliaryEffectSlots(cur, effectslots);
             SET_ERROR_AND_GOTO(context, err, done);
         }
 
-        VECTOR_PUSH_BACK(slotvec, slot);
+        aluInitEffectPanning(slot);
 
+        tmpslots[cur] = slot;
         effectslots[cur] = slot->id;
     }
-    err = AddEffectSlotArray(context, VECTOR_ITER_BEGIN(slotvec), n);
-    if(err != AL_NO_ERROR)
+    if(n > 0)
     {
-        alDeleteAuxiliaryEffectSlots(cur, effectslots);
-        SET_ERROR_AND_GOTO(context, err, done);
+        struct ALeffectslotArray *curarray = ATOMIC_LOAD(&context->ActiveAuxSlots, almemory_order_acquire);
+        struct ALeffectslotArray *newarray = NULL;
+        ALsizei newcount = curarray->count + n;
+        ALCdevice *device;
+
+        newarray = al_calloc(DEF_ALIGN, FAM_SIZE(struct ALeffectslotArray, slot, newcount));
+        newarray->count = newcount;
+        memcpy(newarray->slot, tmpslots, sizeof(ALeffectslot*)*n);
+        if(curarray)
+            memcpy(newarray->slot+n, curarray->slot, sizeof(ALeffectslot*)*curarray->count);
+
+        newarray = ATOMIC_EXCHANGE_PTR(&context->ActiveAuxSlots, newarray,
+                                       almemory_order_acq_rel);
+        device = context->Device;
+        while((ATOMIC_LOAD(&device->MixCount, almemory_order_acquire)&1))
+            althrd_yield();
+        al_free(newarray);
     }
+    UnlockEffectSlotsWrite(context);
 
 done:
-    VECTOR_DEINIT(slotvec);
-
+    al_free(tmpslots);
     ALCcontext_DecRef(context);
 }
 
@@ -116,6 +147,7 @@ AL_API ALvoid AL_APIENTRY alDeleteAuxiliaryEffectSlots(ALsizei n, const ALuint *
     context = GetContextRef();
     if(!context) return;
 
+    LockEffectSlotsWrite(context);
     if(!(n >= 0))
         SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
     for(i = 0;i < n;i++)
@@ -127,20 +159,51 @@ AL_API ALvoid AL_APIENTRY alDeleteAuxiliaryEffectSlots(ALsizei n, const ALuint *
     }
 
     // All effectslots are valid
+    if(n > 0)
+    {
+        struct ALeffectslotArray *curarray = ATOMIC_LOAD(&context->ActiveAuxSlots, almemory_order_acquire);
+        struct ALeffectslotArray *newarray = NULL;
+        ALsizei newcount = curarray->count - n;
+        ALCdevice *device;
+        ALsizei j, k;
+
+        assert(newcount >= 0);
+        newarray = al_calloc(DEF_ALIGN, FAM_SIZE(struct ALeffectslotArray, slot, newcount));
+        newarray->count = newcount;
+        for(i = j = 0;i < newarray->count;)
+        {
+            slot = curarray->slot[j++];
+            for(k = 0;k < n;k++)
+            {
+                if(slot->id == effectslots[k])
+                    break;
+            }
+            if(k == n)
+                newarray->slot[i++] = slot;
+        }
+
+        newarray = ATOMIC_EXCHANGE_PTR(&context->ActiveAuxSlots, newarray,
+                                       almemory_order_acq_rel);
+        device = context->Device;
+        while((ATOMIC_LOAD(&device->MixCount, almemory_order_acquire)&1))
+            althrd_yield();
+        al_free(newarray);
+    }
+
     for(i = 0;i < n;i++)
     {
         if((slot=RemoveEffectSlot(context, effectslots[i])) == NULL)
             continue;
         FreeThunkEntry(slot->id);
 
-        RemoveEffectSlotArray(context, slot);
-        DELETE_OBJ(slot->EffectState);
+        DeinitEffectSlot(slot);
 
         memset(slot, 0, sizeof(*slot));
         al_free(slot);
     }
 
 done:
+    UnlockEffectSlotsWrite(context);
     ALCcontext_DecRef(context);
 }
 
@@ -152,7 +215,9 @@ AL_API ALboolean AL_APIENTRY alIsAuxiliaryEffectSlot(ALuint effectslot)
     context = GetContextRef();
     if(!context) return AL_FALSE;
 
+    LockEffectSlotsRead(context);
     ret = (LookupEffectSlot(context, effectslot) ? AL_TRUE : AL_FALSE);
+    UnlockEffectSlotsRead(context);
 
     ALCcontext_DecRef(context);
 
@@ -170,35 +235,43 @@ AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSloti(ALuint effectslot, ALenum param
     context = GetContextRef();
     if(!context) return;
 
-    device = context->Device;
+    WriteLock(&context->PropLock);
+    LockEffectSlotsRead(context);
     if((slot=LookupEffectSlot(context, effectslot)) == NULL)
         SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
     switch(param)
     {
     case AL_EFFECTSLOT_EFFECT:
+        device = context->Device;
+
+        LockEffectsRead(device);
         effect = (value ? LookupEffect(device, value) : NULL);
         if(!(value == 0 || effect != NULL))
+        {
+            UnlockEffectsRead(device);
             SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
-
+        }
         err = InitializeEffect(device, slot, effect);
+        UnlockEffectsRead(device);
+
         if(err != AL_NO_ERROR)
             SET_ERROR_AND_GOTO(context, err, done);
-        ATOMIC_STORE(&context->UpdateSources, AL_TRUE);
         break;
 
     case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
         if(!(value == AL_TRUE || value == AL_FALSE))
             SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
-
         slot->AuxSendAuto = value;
-        ATOMIC_STORE(&context->UpdateSources, AL_TRUE);
         break;
 
     default:
         SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
     }
+    DO_UPDATEPROPS();
 
 done:
+    UnlockEffectSlotsRead(context);
+    WriteUnlock(&context->PropLock);
     ALCcontext_DecRef(context);
 }
 
@@ -217,6 +290,7 @@ AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotiv(ALuint effectslot, ALenum para
     context = GetContextRef();
     if(!context) return;
 
+    LockEffectSlotsRead(context);
     if(LookupEffectSlot(context, effectslot) == NULL)
         SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
     switch(param)
@@ -226,6 +300,7 @@ AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotiv(ALuint effectslot, ALenum para
     }
 
 done:
+    UnlockEffectSlotsRead(context);
     ALCcontext_DecRef(context);
 }
 
@@ -237,6 +312,8 @@ AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotf(ALuint effectslot, ALenum param
     context = GetContextRef();
     if(!context) return;
 
+    WriteLock(&context->PropLock);
+    LockEffectSlotsRead(context);
     if((slot=LookupEffectSlot(context, effectslot)) == NULL)
         SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
     switch(param)
@@ -244,16 +321,17 @@ AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotf(ALuint effectslot, ALenum param
     case AL_EFFECTSLOT_GAIN:
         if(!(value >= 0.0f && value <= 1.0f))
             SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
-
         slot->Gain = value;
-        ATOMIC_STORE(&slot->NeedsUpdate, AL_TRUE);
         break;
 
     default:
         SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
     }
+    DO_UPDATEPROPS();
 
 done:
+    UnlockEffectSlotsRead(context);
+    WriteUnlock(&context->PropLock);
     ALCcontext_DecRef(context);
 }
 
@@ -271,6 +349,7 @@ AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotfv(ALuint effectslot, ALenum para
     context = GetContextRef();
     if(!context) return;
 
+    LockEffectSlotsRead(context);
     if(LookupEffectSlot(context, effectslot) == NULL)
         SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
     switch(param)
@@ -280,6 +359,7 @@ AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotfv(ALuint effectslot, ALenum para
     }
 
 done:
+    UnlockEffectSlotsRead(context);
     ALCcontext_DecRef(context);
 }
 
@@ -291,6 +371,7 @@ AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSloti(ALuint effectslot, ALenum pa
     context = GetContextRef();
     if(!context) return;
 
+    LockEffectSlotsRead(context);
     if((slot=LookupEffectSlot(context, effectslot)) == NULL)
         SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
     switch(param)
@@ -304,6 +385,7 @@ AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSloti(ALuint effectslot, ALenum pa
     }
 
 done:
+    UnlockEffectSlotsRead(context);
     ALCcontext_DecRef(context);
 }
 
@@ -322,6 +404,7 @@ AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotiv(ALuint effectslot, ALenum p
     context = GetContextRef();
     if(!context) return;
 
+    LockEffectSlotsRead(context);
     if(LookupEffectSlot(context, effectslot) == NULL)
         SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
     switch(param)
@@ -331,6 +414,7 @@ AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotiv(ALuint effectslot, ALenum p
     }
 
 done:
+    UnlockEffectSlotsRead(context);
     ALCcontext_DecRef(context);
 }
 
@@ -342,6 +426,7 @@ AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotf(ALuint effectslot, ALenum pa
     context = GetContextRef();
     if(!context) return;
 
+    LockEffectSlotsRead(context);
     if((slot=LookupEffectSlot(context, effectslot)) == NULL)
         SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
     switch(param)
@@ -355,6 +440,7 @@ AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotf(ALuint effectslot, ALenum pa
     }
 
 done:
+    UnlockEffectSlotsRead(context);
     ALCcontext_DecRef(context);
 }
 
@@ -372,6 +458,7 @@ AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotfv(ALuint effectslot, ALenum p
     context = GetContextRef();
     if(!context) return;
 
+    LockEffectSlotsRead(context);
     if(LookupEffectSlot(context, effectslot) == NULL)
         SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
     switch(param)
@@ -381,47 +468,18 @@ AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotfv(ALuint effectslot, ALenum p
     }
 
 done:
+    UnlockEffectSlotsRead(context);
     ALCcontext_DecRef(context);
 }
 
 
-static ALenum AddEffectSlotArray(ALCcontext *context, ALeffectslot **start, ALsizei count)
-{
-    ALenum err = AL_NO_ERROR;
-
-    LockContext(context);
-    if(!VECTOR_INSERT(context->ActiveAuxSlots, VECTOR_ITER_END(context->ActiveAuxSlots), start, start+count))
-        err = AL_OUT_OF_MEMORY;
-    UnlockContext(context);
-
-    return err;
-}
-
-static void RemoveEffectSlotArray(ALCcontext *context, const ALeffectslot *slot)
-{
-    ALeffectslot **iter;
-
-    LockContext(context);
-#define MATCH_SLOT(_i)  (slot == *(_i))
-    VECTOR_FIND_IF(iter, ALeffectslot*, context->ActiveAuxSlots, MATCH_SLOT);
-    if(iter != VECTOR_ITER_END(context->ActiveAuxSlots))
-    {
-        *iter = VECTOR_BACK(context->ActiveAuxSlots);
-        VECTOR_POP_BACK(context->ActiveAuxSlots);
-    }
-#undef MATCH_SLOT
-    UnlockContext(context);
-}
-
-
 void InitEffectFactoryMap(void)
 {
-    InitUIntMap(&EffectStateFactoryMap, ~0);
+    InitUIntMap(&EffectStateFactoryMap, INT_MAX);
 
     InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_NULL, ALnullStateFactory_getFactory);
     InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_EAXREVERB, ALreverbStateFactory_getFactory);
     InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_REVERB, ALreverbStateFactory_getFactory);
-    InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_AUTOWAH, ALautowahStateFactory_getFactory);
     InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_CHORUS, ALchorusStateFactory_getFactory);
     InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_COMPRESSOR, ALcompressorStateFactory_getFactory);
     InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_DISTORTION, ALdistortionStateFactory_getFactory);
@@ -442,12 +500,12 @@ void DeinitEffectFactoryMap(void)
 ALenum InitializeEffect(ALCdevice *Device, ALeffectslot *EffectSlot, ALeffect *effect)
 {
     ALenum newtype = (effect ? effect->type : AL_EFFECT_NULL);
-    ALeffectStateFactory *factory;
+    struct ALeffectslotProps *props;
+    ALeffectState *State;
 
-    if(newtype != EffectSlot->EffectType)
+    if(newtype != EffectSlot->Effect.Type)
     {
-        ALeffectState *State;
-        FPUCtl oldMode;
+        ALeffectStateFactory *factory;
 
         factory = getFactoryByType(newtype);
         if(!factory)
@@ -456,92 +514,211 @@ ALenum InitializeEffect(ALCdevice *Device, ALeffectslot *EffectSlot, ALeffect *e
             return AL_INVALID_ENUM;
         }
         State = V0(factory,create)();
-        if(!State)
-            return AL_OUT_OF_MEMORY;
-
-        SetMixerFPUMode(&oldMode);
+        if(!State) return AL_OUT_OF_MEMORY;
 
-        ALCdevice_Lock(Device);
+        START_MIXER_MODE();
+        almtx_lock(&Device->BackendLock);
+        State->OutBuffer = Device->Dry.Buffer;
+        State->OutChannels = Device->Dry.NumChannels;
         if(V(State,deviceUpdate)(Device) == AL_FALSE)
         {
-            ALCdevice_Unlock(Device);
-            RestoreFPUMode(&oldMode);
-            DELETE_OBJ(State);
+            almtx_unlock(&Device->BackendLock);
+            LEAVE_MIXER_MODE();
+            ALeffectState_DecRef(State);
             return AL_OUT_OF_MEMORY;
         }
+        almtx_unlock(&Device->BackendLock);
+        END_MIXER_MODE();
 
-        State = ExchangePtr((XchgPtr*)&EffectSlot->EffectState, State);
         if(!effect)
         {
-            memset(&EffectSlot->EffectProps, 0, sizeof(EffectSlot->EffectProps));
-            EffectSlot->EffectType = AL_EFFECT_NULL;
+            EffectSlot->Effect.Type = AL_EFFECT_NULL;
+            memset(&EffectSlot->Effect.Props, 0, sizeof(EffectSlot->Effect.Props));
         }
         else
         {
-            memcpy(&EffectSlot->EffectProps, &effect->Props, sizeof(effect->Props));
-            EffectSlot->EffectType = effect->type;
+            EffectSlot->Effect.Type = effect->type;
+            EffectSlot->Effect.Props = effect->Props;
         }
 
-        /* FIXME: This should be done asynchronously, but since the EffectState
-         * object was changed, it needs an update before its Process method can
-         * be called. */
-        ATOMIC_STORE(&EffectSlot->NeedsUpdate, AL_FALSE);
-        V(EffectSlot->EffectState,update)(Device, EffectSlot);
-        ALCdevice_Unlock(Device);
-
-        RestoreFPUMode(&oldMode);
-
-        DELETE_OBJ(State);
-        State = NULL;
+        ALeffectState_DecRef(EffectSlot->Effect.State);
+        EffectSlot->Effect.State = State;
     }
-    else
+    else if(effect)
+        EffectSlot->Effect.Props = effect->Props;
+
+    /* Remove state references from old effect slot property updates. */
+    props = ATOMIC_LOAD_SEQ(&EffectSlot->FreeList);
+    while(props)
     {
-        if(effect)
-        {
-            ALCdevice_Lock(Device);
-            memcpy(&EffectSlot->EffectProps, &effect->Props, sizeof(effect->Props));
-            ALCdevice_Unlock(Device);
-            ATOMIC_STORE(&EffectSlot->NeedsUpdate, AL_TRUE);
-        }
+        if(props->State)
+            ALeffectState_DecRef(props->State);
+        props->State = NULL;
+        props = ATOMIC_LOAD(&props->next, almemory_order_relaxed);
     }
 
     return AL_NO_ERROR;
 }
 
 
+static void ALeffectState_IncRef(ALeffectState *state)
+{
+    uint ref;
+    ref = IncrementRef(&state->Ref);
+    TRACEREF("%p increasing refcount to %u\n", state, ref);
+}
+
+static void ALeffectState_DecRef(ALeffectState *state)
+{
+    uint ref;
+    ref = DecrementRef(&state->Ref);
+    TRACEREF("%p decreasing refcount to %u\n", state, ref);
+    if(ref == 0) DELETE_OBJ(state);
+}
+
+
+void ALeffectState_Construct(ALeffectState *state)
+{
+    InitRef(&state->Ref, 1);
+
+    state->OutBuffer = NULL;
+    state->OutChannels = 0;
+}
+
+void ALeffectState_Destruct(ALeffectState *UNUSED(state))
+{
+}
+
+
 ALenum InitEffectSlot(ALeffectslot *slot)
 {
     ALeffectStateFactory *factory;
-    ALuint i, c;
 
-    slot->EffectType = AL_EFFECT_NULL;
+    slot->Effect.Type = AL_EFFECT_NULL;
 
     factory = getFactoryByType(AL_EFFECT_NULL);
-    if(!(slot->EffectState=V0(factory,create)()))
+    if(!(slot->Effect.State=V0(factory,create)()))
         return AL_OUT_OF_MEMORY;
 
     slot->Gain = 1.0;
     slot->AuxSendAuto = AL_TRUE;
-    ATOMIC_INIT(&slot->NeedsUpdate, AL_FALSE);
-    for(c = 0;c < 1;c++)
-    {
-        for(i = 0;i < BUFFERSIZE;i++)
-            slot->WetBuffer[c][i] = 0.0f;
-    }
+    ATOMIC_FLAG_TEST_AND_SET(&slot->PropsClean, almemory_order_relaxed);
     InitRef(&slot->ref, 0);
 
+    ATOMIC_INIT(&slot->Update, NULL);
+    ATOMIC_INIT(&slot->FreeList, NULL);
+
+    slot->Params.Gain = 1.0f;
+    slot->Params.AuxSendAuto = AL_TRUE;
+    ALeffectState_IncRef(slot->Effect.State);
+    slot->Params.EffectState = slot->Effect.State;
+    slot->Params.RoomRolloff = 0.0f;
+    slot->Params.DecayTime = 0.0f;
+    slot->Params.DecayHFRatio = 0.0f;
+    slot->Params.DecayHFLimit = AL_FALSE;
+    slot->Params.AirAbsorptionGainHF = 1.0f;
+
     return AL_NO_ERROR;
 }
 
+void DeinitEffectSlot(ALeffectslot *slot)
+{
+    struct ALeffectslotProps *props;
+    size_t count = 0;
+
+    props = ATOMIC_LOAD_SEQ(&slot->Update);
+    if(props)
+    {
+        if(props->State) ALeffectState_DecRef(props->State);
+        TRACE("Freed unapplied AuxiliaryEffectSlot update %p\n", props);
+        al_free(props);
+    }
+    props = ATOMIC_LOAD(&slot->FreeList, almemory_order_relaxed);
+    while(props)
+    {
+        struct ALeffectslotProps *next = ATOMIC_LOAD(&props->next, almemory_order_relaxed);
+        if(props->State) ALeffectState_DecRef(props->State);
+        al_free(props);
+        props = next;
+        ++count;
+    }
+    TRACE("Freed "SZFMT" AuxiliaryEffectSlot property object%s\n", count, (count==1)?"":"s");
+
+    ALeffectState_DecRef(slot->Effect.State);
+    if(slot->Params.EffectState)
+        ALeffectState_DecRef(slot->Params.EffectState);
+}
+
+void UpdateEffectSlotProps(ALeffectslot *slot)
+{
+    struct ALeffectslotProps *props;
+    ALeffectState *oldstate;
+
+    /* Get an unused property container, or allocate a new one as needed. */
+    props = ATOMIC_LOAD(&slot->FreeList, almemory_order_relaxed);
+    if(!props)
+        props = al_calloc(16, sizeof(*props));
+    else
+    {
+        struct ALeffectslotProps *next;
+        do {
+            next = ATOMIC_LOAD(&props->next, almemory_order_relaxed);
+        } while(ATOMIC_COMPARE_EXCHANGE_PTR_WEAK(&slot->FreeList, &props, next,
+                almemory_order_seq_cst, almemory_order_acquire) == 0);
+    }
+
+    /* Copy in current property values. */
+    props->Gain = slot->Gain;
+    props->AuxSendAuto = slot->AuxSendAuto;
+
+    props->Type = slot->Effect.Type;
+    props->Props = slot->Effect.Props;
+    /* Swap out any stale effect state object there may be in the container, to
+     * delete it.
+     */
+    ALeffectState_IncRef(slot->Effect.State);
+    oldstate = props->State;
+    props->State = slot->Effect.State;
+
+    /* Set the new container for updating internal parameters. */
+    props = ATOMIC_EXCHANGE_PTR(&slot->Update, props, almemory_order_acq_rel);
+    if(props)
+    {
+        /* If there was an unused update container, put it back in the
+         * freelist.
+         */
+        ATOMIC_REPLACE_HEAD(struct ALeffectslotProps*, &slot->FreeList, props);
+    }
+
+    if(oldstate)
+        ALeffectState_DecRef(oldstate);
+}
+
+void UpdateAllEffectSlotProps(ALCcontext *context)
+{
+    struct ALeffectslotArray *auxslots;
+    ALsizei i;
+
+    LockEffectSlotsRead(context);
+    auxslots = ATOMIC_LOAD(&context->ActiveAuxSlots, almemory_order_acquire);
+    for(i = 0;i < auxslots->count;i++)
+    {
+        ALeffectslot *slot = auxslots->slot[i];
+        if(!ATOMIC_FLAG_TEST_AND_SET(&slot->PropsClean, almemory_order_acq_rel))
+            UpdateEffectSlotProps(slot);
+    }
+    UnlockEffectSlotsRead(context);
+}
+
 ALvoid ReleaseALAuxiliaryEffectSlots(ALCcontext *Context)
 {
     ALsizei pos;
     for(pos = 0;pos < Context->EffectSlotMap.size;pos++)
     {
-        ALeffectslot *temp = Context->EffectSlotMap.array[pos].value;
-        Context->EffectSlotMap.array[pos].value = NULL;
+        ALeffectslot *temp = Context->EffectSlotMap.values[pos];
+        Context->EffectSlotMap.values[pos] = NULL;
 
-        DELETE_OBJ(temp->EffectState);
+        DeinitEffectSlot(temp);
 
         FreeThunkEntry(temp->id);
         memset(temp, 0, sizeof(ALeffectslot));

+ 108 - 58
libs/openal-soft/OpenAL32/alBuffer.c

@@ -36,15 +36,19 @@
 #include "sample_cvt.h"
 
 
+extern inline void LockBuffersRead(ALCdevice *device);
+extern inline void UnlockBuffersRead(ALCdevice *device);
+extern inline void LockBuffersWrite(ALCdevice *device);
+extern inline void UnlockBuffersWrite(ALCdevice *device);
 extern inline struct ALbuffer *LookupBuffer(ALCdevice *device, ALuint id);
 extern inline struct ALbuffer *RemoveBuffer(ALCdevice *device, ALuint id);
-extern inline ALuint FrameSizeFromUserFmt(enum UserFmtChannels chans, enum UserFmtType type);
-extern inline ALuint FrameSizeFromFmt(enum FmtChannels chans, enum FmtType type);
+extern inline ALsizei FrameSizeFromUserFmt(enum UserFmtChannels chans, enum UserFmtType type);
+extern inline ALsizei FrameSizeFromFmt(enum FmtChannels chans, enum FmtType type);
 
-static ALboolean IsValidType(ALenum type) DECL_CONST;
-static ALboolean IsValidChannels(ALenum channels) DECL_CONST;
-static ALboolean DecomposeUserFormat(ALenum format, enum UserFmtChannels *chans, enum UserFmtType *type) DECL_CONST;
-static ALboolean DecomposeFormat(ALenum format, enum FmtChannels *chans, enum FmtType *type) DECL_CONST;
+static ALboolean IsValidType(ALenum type);
+static ALboolean IsValidChannels(ALenum channels);
+static ALboolean DecomposeUserFormat(ALenum format, enum UserFmtChannels *chans, enum UserFmtType *type);
+static ALboolean DecomposeFormat(ALenum format, enum FmtChannels *chans, enum FmtType *type);
 static ALboolean SanitizeAlignment(enum UserFmtType type, ALsizei *align);
 
 
@@ -85,10 +89,12 @@ AL_API ALvoid AL_APIENTRY alDeleteBuffers(ALsizei n, const ALuint *buffers)
     context = GetContextRef();
     if(!context) return;
 
+    device = context->Device;
+
+    LockBuffersWrite(device);
     if(!(n >= 0))
         SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
 
-    device = context->Device;
     for(i = 0;i < n;i++)
     {
         if(!buffers[i])
@@ -108,6 +114,7 @@ AL_API ALvoid AL_APIENTRY alDeleteBuffers(ALsizei n, const ALuint *buffers)
     }
 
 done:
+    UnlockBuffersWrite(device);
     ALCcontext_DecRef(context);
 }
 
@@ -119,8 +126,10 @@ AL_API ALboolean AL_APIENTRY alIsBuffer(ALuint buffer)
     context = GetContextRef();
     if(!context) return AL_FALSE;
 
+    LockBuffersRead(context->Device);
     ret = ((!buffer || LookupBuffer(context->Device, buffer)) ?
            AL_TRUE : AL_FALSE);
+    UnlockBuffersRead(context->Device);
 
     ALCcontext_DecRef(context);
 
@@ -130,13 +139,13 @@ AL_API ALboolean AL_APIENTRY alIsBuffer(ALuint buffer)
 
 AL_API ALvoid AL_APIENTRY alBufferData(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei freq)
 {
-    enum UserFmtChannels srcchannels;
-    enum UserFmtType srctype;
+    enum UserFmtChannels srcchannels = UserFmtMono;
+    enum UserFmtType srctype = UserFmtByte;
     ALCdevice *device;
     ALCcontext *context;
     ALbuffer *albuf;
     ALenum newformat = AL_NONE;
-    ALuint framesize;
+    ALsizei framesize;
     ALsizei align;
     ALenum err;
 
@@ -144,6 +153,7 @@ AL_API ALvoid AL_APIENTRY alBufferData(ALuint buffer, ALenum format, const ALvoi
     if(!context) return;
 
     device = context->Device;
+    LockBuffersRead(device);
     if((albuf=LookupBuffer(device, buffer)) == NULL)
         SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
     if(!(size >= 0 && freq > 0))
@@ -151,7 +161,7 @@ AL_API ALvoid AL_APIENTRY alBufferData(ALuint buffer, ALenum format, const ALvoi
     if(DecomposeUserFormat(format, &srcchannels, &srctype) == AL_FALSE)
         SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
 
-    align = ATOMIC_LOAD(&albuf->UnpackAlign);
+    align = ATOMIC_LOAD_SEQ(&albuf->UnpackAlign);
     if(SanitizeAlignment(srctype, &align) == AL_FALSE)
         SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
     switch(srctype)
@@ -173,8 +183,6 @@ AL_API ALvoid AL_APIENTRY alBufferData(ALuint buffer, ALenum format, const ALvoi
 
         case UserFmtInt:
         case UserFmtUInt:
-        case UserFmtByte3:
-        case UserFmtUByte3:
         case UserFmtDouble:
             framesize = FrameSizeFromUserFmt(srcchannels, srctype) * align;
             if((size%framesize) != 0)
@@ -272,25 +280,27 @@ AL_API ALvoid AL_APIENTRY alBufferData(ALuint buffer, ALenum format, const ALvoi
     }
 
 done:
+    UnlockBuffersRead(device);
     ALCcontext_DecRef(context);
 }
 
 AL_API ALvoid AL_APIENTRY alBufferSubDataSOFT(ALuint buffer, ALenum format, const ALvoid *data, ALsizei offset, ALsizei length)
 {
-    enum UserFmtChannels srcchannels;
-    enum UserFmtType srctype;
+    enum UserFmtChannels srcchannels = UserFmtMono;
+    enum UserFmtType srctype = UserFmtByte;
     ALCdevice *device;
     ALCcontext *context;
     ALbuffer *albuf;
-    ALuint byte_align;
-    ALuint channels;
-    ALuint bytes;
+    ALsizei byte_align;
+    ALsizei channels;
+    ALsizei bytes;
     ALsizei align;
 
     context = GetContextRef();
     if(!context) return;
 
     device = context->Device;
+    LockBuffersRead(device);
     if((albuf=LookupBuffer(device, buffer)) == NULL)
         SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
     if(!(length >= 0 && offset >= 0))
@@ -299,7 +309,7 @@ AL_API ALvoid AL_APIENTRY alBufferSubDataSOFT(ALuint buffer, ALenum format, cons
         SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
 
     WriteLock(&albuf->lock);
-    align = ATOMIC_LOAD(&albuf->UnpackAlign);
+    align = ATOMIC_LOAD_SEQ(&albuf->UnpackAlign);
     if(SanitizeAlignment(srctype, &align) == AL_FALSE)
     {
         WriteUnlock(&albuf->lock);
@@ -351,6 +361,7 @@ AL_API ALvoid AL_APIENTRY alBufferSubDataSOFT(ALuint buffer, ALenum format, cons
     WriteUnlock(&albuf->lock);
 
 done:
+    UnlockBuffersRead(device);
     ALCcontext_DecRef(context);
 }
 
@@ -369,6 +380,7 @@ AL_API void AL_APIENTRY alBufferSamplesSOFT(ALuint buffer,
     if(!context) return;
 
     device = context->Device;
+    LockBuffersRead(device);
     if((albuf=LookupBuffer(device, buffer)) == NULL)
         SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
     if(!(samples >= 0 && samplerate != 0))
@@ -376,7 +388,7 @@ AL_API void AL_APIENTRY alBufferSamplesSOFT(ALuint buffer,
     if(IsValidType(type) == AL_FALSE || IsValidChannels(channels) == AL_FALSE)
         SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
 
-    align = ATOMIC_LOAD(&albuf->UnpackAlign);
+    align = ATOMIC_LOAD_SEQ(&albuf->UnpackAlign);
     if(SanitizeAlignment(type, &align) == AL_FALSE)
         SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
     if((samples%align) != 0)
@@ -388,6 +400,7 @@ AL_API void AL_APIENTRY alBufferSamplesSOFT(ALuint buffer,
         SET_ERROR_AND_GOTO(context, err, done);
 
 done:
+    UnlockBuffersRead(device);
     ALCcontext_DecRef(context);
 }
 
@@ -404,6 +417,7 @@ AL_API void AL_APIENTRY alBufferSubSamplesSOFT(ALuint buffer,
     if(!context) return;
 
     device = context->Device;
+    LockBuffersRead(device);
     if((albuf=LookupBuffer(device, buffer)) == NULL)
         SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
     if(!(samples >= 0 && offset >= 0))
@@ -412,7 +426,7 @@ AL_API void AL_APIENTRY alBufferSubSamplesSOFT(ALuint buffer,
         SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
 
     WriteLock(&albuf->lock);
-    align = ATOMIC_LOAD(&albuf->UnpackAlign);
+    align = ATOMIC_LOAD_SEQ(&albuf->UnpackAlign);
     if(SanitizeAlignment(type, &align) == AL_FALSE)
     {
         WriteUnlock(&albuf->lock);
@@ -441,6 +455,7 @@ AL_API void AL_APIENTRY alBufferSubSamplesSOFT(ALuint buffer,
     WriteUnlock(&albuf->lock);
 
 done:
+    UnlockBuffersRead(device);
     ALCcontext_DecRef(context);
 }
 
@@ -457,6 +472,7 @@ AL_API void AL_APIENTRY alGetBufferSamplesSOFT(ALuint buffer,
     if(!context) return;
 
     device = context->Device;
+    LockBuffersRead(device);
     if((albuf=LookupBuffer(device, buffer)) == NULL)
         SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
     if(!(samples >= 0 && offset >= 0))
@@ -465,7 +481,7 @@ AL_API void AL_APIENTRY alGetBufferSamplesSOFT(ALuint buffer,
         SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
 
     ReadLock(&albuf->lock);
-    align = ATOMIC_LOAD(&albuf->PackAlign);
+    align = ATOMIC_LOAD_SEQ(&albuf->PackAlign);
     if(SanitizeAlignment(type, &align) == AL_FALSE)
     {
         ReadUnlock(&albuf->lock);
@@ -494,6 +510,7 @@ AL_API void AL_APIENTRY alGetBufferSamplesSOFT(ALuint buffer,
     ReadUnlock(&albuf->lock);
 
 done:
+    UnlockBuffersRead(device);
     ALCcontext_DecRef(context);
 }
 
@@ -524,6 +541,7 @@ AL_API void AL_APIENTRY alBufferf(ALuint buffer, ALenum param, ALfloat UNUSED(va
     if(!context) return;
 
     device = context->Device;
+    LockBuffersRead(device);
     if(LookupBuffer(device, buffer) == NULL)
         SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
 
@@ -534,6 +552,7 @@ AL_API void AL_APIENTRY alBufferf(ALuint buffer, ALenum param, ALfloat UNUSED(va
     }
 
 done:
+    UnlockBuffersRead(device);
     ALCcontext_DecRef(context);
 }
 
@@ -547,6 +566,7 @@ AL_API void AL_APIENTRY alBuffer3f(ALuint buffer, ALenum param, ALfloat UNUSED(v
     if(!context) return;
 
     device = context->Device;
+    LockBuffersRead(device);
     if(LookupBuffer(device, buffer) == NULL)
         SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
 
@@ -557,6 +577,7 @@ AL_API void AL_APIENTRY alBuffer3f(ALuint buffer, ALenum param, ALfloat UNUSED(v
     }
 
 done:
+    UnlockBuffersRead(device);
     ALCcontext_DecRef(context);
 }
 
@@ -570,6 +591,7 @@ AL_API void AL_APIENTRY alBufferfv(ALuint buffer, ALenum param, const ALfloat *v
     if(!context) return;
 
     device = context->Device;
+    LockBuffersRead(device);
     if(LookupBuffer(device, buffer) == NULL)
         SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
 
@@ -582,6 +604,7 @@ AL_API void AL_APIENTRY alBufferfv(ALuint buffer, ALenum param, const ALfloat *v
     }
 
 done:
+    UnlockBuffersRead(device);
     ALCcontext_DecRef(context);
 }
 
@@ -596,6 +619,7 @@ AL_API void AL_APIENTRY alBufferi(ALuint buffer, ALenum param, ALint value)
     if(!context) return;
 
     device = context->Device;
+    LockBuffersRead(device);
     if((albuf=LookupBuffer(device, buffer)) == NULL)
         SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
 
@@ -604,13 +628,13 @@ AL_API void AL_APIENTRY alBufferi(ALuint buffer, ALenum param, ALint value)
     case AL_UNPACK_BLOCK_ALIGNMENT_SOFT:
         if(!(value >= 0))
             SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
-        ATOMIC_STORE(&albuf->UnpackAlign, value);
+        ATOMIC_STORE_SEQ(&albuf->UnpackAlign, value);
         break;
 
     case AL_PACK_BLOCK_ALIGNMENT_SOFT:
         if(!(value >= 0))
             SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
-        ATOMIC_STORE(&albuf->PackAlign, value);
+        ATOMIC_STORE_SEQ(&albuf->PackAlign, value);
         break;
 
     default:
@@ -618,6 +642,7 @@ AL_API void AL_APIENTRY alBufferi(ALuint buffer, ALenum param, ALint value)
     }
 
 done:
+    UnlockBuffersRead(device);
     ALCcontext_DecRef(context);
 }
 
@@ -666,6 +691,7 @@ AL_API void AL_APIENTRY alBufferiv(ALuint buffer, ALenum param, const ALint *val
     if(!context) return;
 
     device = context->Device;
+    LockBuffersRead(device);
     if((albuf=LookupBuffer(device, buffer)) == NULL)
         SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
 
@@ -697,6 +723,7 @@ AL_API void AL_APIENTRY alBufferiv(ALuint buffer, ALenum param, const ALint *val
     }
 
 done:
+    UnlockBuffersRead(device);
     ALCcontext_DecRef(context);
 }
 
@@ -711,6 +738,7 @@ AL_API ALvoid AL_APIENTRY alGetBufferf(ALuint buffer, ALenum param, ALfloat *val
     if(!context) return;
 
     device = context->Device;
+    LockBuffersRead(device);
     if((albuf=LookupBuffer(device, buffer)) == NULL)
         SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
 
@@ -732,6 +760,7 @@ AL_API ALvoid AL_APIENTRY alGetBufferf(ALuint buffer, ALenum param, ALfloat *val
     }
 
 done:
+    UnlockBuffersRead(device);
     ALCcontext_DecRef(context);
 }
 
@@ -745,6 +774,7 @@ AL_API void AL_APIENTRY alGetBuffer3f(ALuint buffer, ALenum param, ALfloat *valu
     if(!context) return;
 
     device = context->Device;
+    LockBuffersRead(device);
     if(LookupBuffer(device, buffer) == NULL)
         SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
 
@@ -757,6 +787,7 @@ AL_API void AL_APIENTRY alGetBuffer3f(ALuint buffer, ALenum param, ALfloat *valu
     }
 
 done:
+    UnlockBuffersRead(device);
     ALCcontext_DecRef(context);
 }
 
@@ -777,6 +808,7 @@ AL_API void AL_APIENTRY alGetBufferfv(ALuint buffer, ALenum param, ALfloat *valu
     if(!context) return;
 
     device = context->Device;
+    LockBuffersRead(device);
     if(LookupBuffer(device, buffer) == NULL)
         SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
 
@@ -789,6 +821,7 @@ AL_API void AL_APIENTRY alGetBufferfv(ALuint buffer, ALenum param, ALfloat *valu
     }
 
 done:
+    UnlockBuffersRead(device);
     ALCcontext_DecRef(context);
 }
 
@@ -803,6 +836,7 @@ AL_API ALvoid AL_APIENTRY alGetBufferi(ALuint buffer, ALenum param, ALint *value
     if(!context) return;
 
     device = context->Device;
+    LockBuffersRead(device);
     if((albuf=LookupBuffer(device, buffer)) == NULL)
         SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
 
@@ -842,11 +876,11 @@ AL_API ALvoid AL_APIENTRY alGetBufferi(ALuint buffer, ALenum param, ALint *value
         break;
 
     case AL_UNPACK_BLOCK_ALIGNMENT_SOFT:
-        *value = ATOMIC_LOAD(&albuf->UnpackAlign);
+        *value = ATOMIC_LOAD_SEQ(&albuf->UnpackAlign);
         break;
 
     case AL_PACK_BLOCK_ALIGNMENT_SOFT:
-        *value = ATOMIC_LOAD(&albuf->PackAlign);
+        *value = ATOMIC_LOAD_SEQ(&albuf->PackAlign);
         break;
 
     default:
@@ -854,6 +888,7 @@ AL_API ALvoid AL_APIENTRY alGetBufferi(ALuint buffer, ALenum param, ALint *value
     }
 
 done:
+    UnlockBuffersRead(device);
     ALCcontext_DecRef(context);
 }
 
@@ -867,6 +902,7 @@ AL_API void AL_APIENTRY alGetBuffer3i(ALuint buffer, ALenum param, ALint *value1
     if(!context) return;
 
     device = context->Device;
+    LockBuffersRead(device);
     if(LookupBuffer(device, buffer) == NULL)
         SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
 
@@ -879,6 +915,7 @@ AL_API void AL_APIENTRY alGetBuffer3i(ALuint buffer, ALenum param, ALint *value1
     }
 
 done:
+    UnlockBuffersRead(device);
     ALCcontext_DecRef(context);
 }
 
@@ -908,6 +945,7 @@ AL_API void AL_APIENTRY alGetBufferiv(ALuint buffer, ALenum param, ALint *values
     if(!context) return;
 
     device = context->Device;
+    LockBuffersRead(device);
     if((albuf=LookupBuffer(device, buffer)) == NULL)
         SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
 
@@ -927,6 +965,7 @@ AL_API void AL_APIENTRY alGetBufferiv(ALuint buffer, ALenum param, ALint *values
     }
 
 done:
+    UnlockBuffersRead(device);
     ALCcontext_DecRef(context);
 }
 
@@ -940,14 +979,14 @@ done:
  */
 ALenum LoadData(ALbuffer *ALBuf, ALuint freq, ALenum NewFormat, ALsizei frames, enum UserFmtChannels SrcChannels, enum UserFmtType SrcType, const ALvoid *data, ALsizei align, ALboolean storesrc)
 {
+    enum FmtChannels DstChannels = FmtMono;
+    enum FmtType DstType = FmtByte;
     ALuint NewChannels, NewBytes;
-    enum FmtChannels DstChannels;
-    enum FmtType DstType;
     ALuint64 newsize;
-    ALvoid *temp;
 
-    if(DecomposeFormat(NewFormat, &DstChannels, &DstType) == AL_FALSE ||
-       (long)SrcChannels != (long)DstChannels)
+    if(DecomposeFormat(NewFormat, &DstChannels, &DstType) == AL_FALSE)
+        return AL_INVALID_ENUM;
+    if((long)SrcChannels != (long)DstChannels)
         return AL_INVALID_ENUM;
 
     NewChannels = ChannelsFromFmt(DstChannels);
@@ -966,13 +1005,25 @@ ALenum LoadData(ALbuffer *ALBuf, ALuint freq, ALenum NewFormat, ALsizei frames,
         return AL_INVALID_OPERATION;
     }
 
-    temp = realloc(ALBuf->data, (size_t)newsize);
-    if(!temp && newsize)
+    /* Round up to the next 16-byte multiple. This could reallocate only when
+     * increasing or the new size is less than half the current, but then the
+     * buffer's AL_SIZE would not be very reliable for accounting buffer memory
+     * usage, and reporting the real size could cause problems for apps that
+     * use AL_SIZE to try to get the buffer's play length.
+     */
+    newsize = (newsize+15) & ~0xf;
+    if(newsize != ALBuf->BytesAlloc)
     {
-        WriteUnlock(&ALBuf->lock);
-        return AL_OUT_OF_MEMORY;
+        void *temp = al_calloc(16, (size_t)newsize);
+        if(!temp && newsize)
+        {
+            WriteUnlock(&ALBuf->lock);
+            return AL_OUT_OF_MEMORY;
+        }
+        al_free(ALBuf->data);
+        ALBuf->data = temp;
+        ALBuf->BytesAlloc = (ALuint)newsize;
     }
-    ALBuf->data = temp;
 
     if(data != NULL)
         ConvertData(ALBuf->data, (enum UserFmtType)DstType, data, SrcType, NewChannels, frames, align);
@@ -1021,7 +1072,7 @@ ALenum LoadData(ALbuffer *ALBuf, ALuint freq, ALenum NewFormat, ALsizei frames,
 }
 
 
-ALuint BytesFromUserFmt(enum UserFmtType type)
+ALsizei BytesFromUserFmt(enum UserFmtType type)
 {
     switch(type)
     {
@@ -1033,8 +1084,6 @@ ALuint BytesFromUserFmt(enum UserFmtType type)
     case UserFmtUInt: return sizeof(ALuint);
     case UserFmtFloat: return sizeof(ALfloat);
     case UserFmtDouble: return sizeof(ALdouble);
-    case UserFmtByte3: return sizeof(ALbyte[3]);
-    case UserFmtUByte3: return sizeof(ALubyte[3]);
     case UserFmtMulaw: return sizeof(ALubyte);
     case UserFmtAlaw: return sizeof(ALubyte);
     case UserFmtIMA4: break; /* not handled here */
@@ -1042,7 +1091,7 @@ ALuint BytesFromUserFmt(enum UserFmtType type)
     }
     return 0;
 }
-ALuint ChannelsFromUserFmt(enum UserFmtChannels chans)
+ALsizei ChannelsFromUserFmt(enum UserFmtChannels chans)
 {
     switch(chans)
     {
@@ -1137,7 +1186,7 @@ static ALboolean DecomposeUserFormat(ALenum format, enum UserFmtChannels *chans,
     return AL_FALSE;
 }
 
-ALuint BytesFromFmt(enum FmtType type)
+ALsizei BytesFromFmt(enum FmtType type)
 {
     switch(type)
     {
@@ -1147,7 +1196,7 @@ ALuint BytesFromFmt(enum FmtType type)
     }
     return 0;
 }
-ALuint ChannelsFromFmt(enum FmtChannels chans)
+ALsizei ChannelsFromFmt(enum FmtChannels chans)
 {
     switch(chans)
     {
@@ -1201,13 +1250,13 @@ static ALboolean DecomposeFormat(ALenum format, enum FmtChannels *chans, enum Fm
         { AL_7POINT1_16_SOFT,  FmtX71, FmtShort },
         { AL_7POINT1_32F_SOFT, FmtX71, FmtFloat },
 
-        { AL_FORMAT_BFORMAT2D_8,       FmtBFormat2D, FmtByte },
-        { AL_FORMAT_BFORMAT2D_16,      FmtBFormat2D, FmtShort },
-        { AL_FORMAT_BFORMAT2D_FLOAT32, FmtBFormat2D, FmtFloat },
+        { AL_BFORMAT2D_8_SOFT,   FmtBFormat2D, FmtByte },
+        { AL_BFORMAT2D_16_SOFT,  FmtBFormat2D, FmtShort },
+        { AL_BFORMAT2D_32F_SOFT, FmtBFormat2D, FmtFloat },
 
-        { AL_FORMAT_BFORMAT3D_8,       FmtBFormat3D, FmtByte },
-        { AL_FORMAT_BFORMAT3D_16,      FmtBFormat3D, FmtShort },
-        { AL_FORMAT_BFORMAT3D_FLOAT32, FmtBFormat3D, FmtFloat },
+        { AL_BFORMAT3D_8_SOFT,   FmtBFormat3D, FmtByte },
+        { AL_BFORMAT3D_16_SOFT,  FmtBFormat3D, FmtShort },
+        { AL_BFORMAT3D_32F_SOFT, FmtBFormat3D, FmtFloat },
     };
     ALuint i;
 
@@ -1275,8 +1324,7 @@ static ALboolean IsValidType(ALenum type)
         case AL_UNSIGNED_INT_SOFT:
         case AL_FLOAT_SOFT:
         case AL_DOUBLE_SOFT:
-        case AL_BYTE3_SOFT:
-        case AL_UNSIGNED_BYTE3_SOFT:
+        case AL_MULAW_SOFT:
             return AL_TRUE;
     }
     return AL_FALSE;
@@ -1293,6 +1341,8 @@ static ALboolean IsValidChannels(ALenum channels)
         case AL_5POINT1_SOFT:
         case AL_6POINT1_SOFT:
         case AL_7POINT1_SOFT:
+        case AL_BFORMAT2D_SOFT:
+        case AL_BFORMAT3D_SOFT:
             return AL_TRUE;
     }
     return AL_FALSE;
@@ -1305,7 +1355,7 @@ ALbuffer *NewBuffer(ALCcontext *context)
     ALbuffer *buffer;
     ALenum err;
 
-    buffer = calloc(1, sizeof(ALbuffer));
+    buffer = al_calloc(16, sizeof(ALbuffer));
     if(!buffer)
         SET_ERROR_AND_RETURN_VALUE(context, AL_OUT_OF_MEMORY, NULL);
     RWLockInit(&buffer->lock);
@@ -1317,7 +1367,7 @@ ALbuffer *NewBuffer(ALCcontext *context)
     {
         FreeThunkEntry(buffer->id);
         memset(buffer, 0, sizeof(ALbuffer));
-        free(buffer);
+        al_free(buffer);
 
         SET_ERROR_AND_RETURN_VALUE(context, err, NULL);
     }
@@ -1330,10 +1380,10 @@ void DeleteBuffer(ALCdevice *device, ALbuffer *buffer)
     RemoveBuffer(device, buffer->id);
     FreeThunkEntry(buffer->id);
 
-    free(buffer->data);
+    al_free(buffer->data);
 
     memset(buffer, 0, sizeof(*buffer));
-    free(buffer);
+    al_free(buffer);
 }
 
 
@@ -1347,13 +1397,13 @@ ALvoid ReleaseALBuffers(ALCdevice *device)
     ALsizei i;
     for(i = 0;i < device->BufferMap.size;i++)
     {
-        ALbuffer *temp = device->BufferMap.array[i].value;
-        device->BufferMap.array[i].value = NULL;
+        ALbuffer *temp = device->BufferMap.values[i];
+        device->BufferMap.values[i] = NULL;
 
-        free(temp->data);
+        al_free(temp->data);
 
         FreeThunkEntry(temp->id);
         memset(temp, 0, sizeof(ALbuffer));
-        free(temp);
+        al_free(temp);
     }
 }

+ 34 - 18
libs/openal-soft/OpenAL32/alEffect.c

@@ -34,6 +34,10 @@
 
 ALboolean DisabledEffects[MAX_EFFECTS];
 
+extern inline void LockEffectsRead(ALCdevice *device);
+extern inline void UnlockEffectsRead(ALCdevice *device);
+extern inline void LockEffectsWrite(ALCdevice *device);
+extern inline void UnlockEffectsWrite(ALCdevice *device);
 extern inline struct ALeffect *LookupEffect(ALCdevice *device, ALuint id);
 extern inline struct ALeffect *RemoveEffect(ALCdevice *device, ALuint id);
 extern inline ALboolean IsReverbEffect(ALenum type);
@@ -56,11 +60,11 @@ AL_API ALvoid AL_APIENTRY alGenEffects(ALsizei n, ALuint *effects)
     device = context->Device;
     for(cur = 0;cur < n;cur++)
     {
-        ALeffect *effect = calloc(1, sizeof(ALeffect));
+        ALeffect *effect = al_calloc(16, sizeof(ALeffect));
         ALenum err = AL_OUT_OF_MEMORY;
         if(!effect || (err=InitEffect(effect)) != AL_NO_ERROR)
         {
-            free(effect);
+            al_free(effect);
             alDeleteEffects(cur, effects);
             SET_ERROR_AND_GOTO(context, err, done);
         }
@@ -72,7 +76,7 @@ AL_API ALvoid AL_APIENTRY alGenEffects(ALsizei n, ALuint *effects)
         {
             FreeThunkEntry(effect->id);
             memset(effect, 0, sizeof(ALeffect));
-            free(effect);
+            al_free(effect);
 
             alDeleteEffects(cur, effects);
             SET_ERROR_AND_GOTO(context, err, done);
@@ -95,10 +99,10 @@ AL_API ALvoid AL_APIENTRY alDeleteEffects(ALsizei n, const ALuint *effects)
     context = GetContextRef();
     if(!context) return;
 
+    device = context->Device;
+    LockEffectsWrite(device);
     if(!(n >= 0))
         SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
-
-    device = context->Device;
     for(i = 0;i < n;i++)
     {
         if(effects[i] && LookupEffect(device, effects[i]) == NULL)
@@ -111,10 +115,11 @@ AL_API ALvoid AL_APIENTRY alDeleteEffects(ALsizei n, const ALuint *effects)
         FreeThunkEntry(effect->id);
 
         memset(effect, 0, sizeof(*effect));
-        free(effect);
+        al_free(effect);
     }
 
 done:
+    UnlockEffectsWrite(device);
     ALCcontext_DecRef(context);
 }
 
@@ -126,8 +131,10 @@ AL_API ALboolean AL_APIENTRY alIsEffect(ALuint effect)
     Context = GetContextRef();
     if(!Context) return AL_FALSE;
 
+    LockEffectsRead(Context->Device);
     result = ((!effect || LookupEffect(Context->Device, effect)) ?
               AL_TRUE : AL_FALSE);
+    UnlockEffectsRead(Context->Device);
 
     ALCcontext_DecRef(Context);
 
@@ -144,6 +151,7 @@ AL_API ALvoid AL_APIENTRY alEffecti(ALuint effect, ALenum param, ALint value)
     if(!Context) return;
 
     Device = Context->Device;
+    LockEffectsWrite(Device);
     if((ALEffect=LookupEffect(Device, effect)) == NULL)
         alSetError(Context, AL_INVALID_NAME);
     else
@@ -170,6 +178,7 @@ AL_API ALvoid AL_APIENTRY alEffecti(ALuint effect, ALenum param, ALint value)
             V(ALEffect,setParami)(Context, param, value);
         }
     }
+    UnlockEffectsWrite(Device);
 
     ALCcontext_DecRef(Context);
 }
@@ -191,6 +200,7 @@ AL_API ALvoid AL_APIENTRY alEffectiv(ALuint effect, ALenum param, const ALint *v
     if(!Context) return;
 
     Device = Context->Device;
+    LockEffectsWrite(Device);
     if((ALEffect=LookupEffect(Device, effect)) == NULL)
         alSetError(Context, AL_INVALID_NAME);
     else
@@ -198,6 +208,7 @@ AL_API ALvoid AL_APIENTRY alEffectiv(ALuint effect, ALenum param, const ALint *v
         /* Call the appropriate handler */
         V(ALEffect,setParamiv)(Context, param, values);
     }
+    UnlockEffectsWrite(Device);
 
     ALCcontext_DecRef(Context);
 }
@@ -212,6 +223,7 @@ AL_API ALvoid AL_APIENTRY alEffectf(ALuint effect, ALenum param, ALfloat value)
     if(!Context) return;
 
     Device = Context->Device;
+    LockEffectsWrite(Device);
     if((ALEffect=LookupEffect(Device, effect)) == NULL)
         alSetError(Context, AL_INVALID_NAME);
     else
@@ -219,6 +231,7 @@ AL_API ALvoid AL_APIENTRY alEffectf(ALuint effect, ALenum param, ALfloat value)
         /* Call the appropriate handler */
         V(ALEffect,setParamf)(Context, param, value);
     }
+    UnlockEffectsWrite(Device);
 
     ALCcontext_DecRef(Context);
 }
@@ -233,6 +246,7 @@ AL_API ALvoid AL_APIENTRY alEffectfv(ALuint effect, ALenum param, const ALfloat
     if(!Context) return;
 
     Device = Context->Device;
+    LockEffectsWrite(Device);
     if((ALEffect=LookupEffect(Device, effect)) == NULL)
         alSetError(Context, AL_INVALID_NAME);
     else
@@ -240,6 +254,7 @@ AL_API ALvoid AL_APIENTRY alEffectfv(ALuint effect, ALenum param, const ALfloat
         /* Call the appropriate handler */
         V(ALEffect,setParamfv)(Context, param, values);
     }
+    UnlockEffectsWrite(Device);
 
     ALCcontext_DecRef(Context);
 }
@@ -254,6 +269,7 @@ AL_API ALvoid AL_APIENTRY alGetEffecti(ALuint effect, ALenum param, ALint *value
     if(!Context) return;
 
     Device = Context->Device;
+    LockEffectsRead(Device);
     if((ALEffect=LookupEffect(Device, effect)) == NULL)
         alSetError(Context, AL_INVALID_NAME);
     else
@@ -266,6 +282,7 @@ AL_API ALvoid AL_APIENTRY alGetEffecti(ALuint effect, ALenum param, ALint *value
             V(ALEffect,getParami)(Context, param, value);
         }
     }
+    UnlockEffectsRead(Device);
 
     ALCcontext_DecRef(Context);
 }
@@ -287,6 +304,7 @@ AL_API ALvoid AL_APIENTRY alGetEffectiv(ALuint effect, ALenum param, ALint *valu
     if(!Context) return;
 
     Device = Context->Device;
+    LockEffectsRead(Device);
     if((ALEffect=LookupEffect(Device, effect)) == NULL)
         alSetError(Context, AL_INVALID_NAME);
     else
@@ -294,6 +312,7 @@ AL_API ALvoid AL_APIENTRY alGetEffectiv(ALuint effect, ALenum param, ALint *valu
         /* Call the appropriate handler */
         V(ALEffect,getParamiv)(Context, param, values);
     }
+    UnlockEffectsRead(Device);
 
     ALCcontext_DecRef(Context);
 }
@@ -308,6 +327,7 @@ AL_API ALvoid AL_APIENTRY alGetEffectf(ALuint effect, ALenum param, ALfloat *val
     if(!Context) return;
 
     Device = Context->Device;
+    LockEffectsRead(Device);
     if((ALEffect=LookupEffect(Device, effect)) == NULL)
         alSetError(Context, AL_INVALID_NAME);
     else
@@ -315,6 +335,7 @@ AL_API ALvoid AL_APIENTRY alGetEffectf(ALuint effect, ALenum param, ALfloat *val
         /* Call the appropriate handler */
         V(ALEffect,getParamf)(Context, param, value);
     }
+    UnlockEffectsRead(Device);
 
     ALCcontext_DecRef(Context);
 }
@@ -329,6 +350,7 @@ AL_API ALvoid AL_APIENTRY alGetEffectfv(ALuint effect, ALenum param, ALfloat *va
     if(!Context) return;
 
     Device = Context->Device;
+    LockEffectsRead(Device);
     if((ALEffect=LookupEffect(Device, effect)) == NULL)
         alSetError(Context, AL_INVALID_NAME);
     else
@@ -336,6 +358,7 @@ AL_API ALvoid AL_APIENTRY alGetEffectfv(ALuint effect, ALenum param, ALfloat *va
         /* Call the appropriate handler */
         V(ALEffect,getParamfv)(Context, param, values);
     }
+    UnlockEffectsRead(Device);
 
     ALCcontext_DecRef(Context);
 }
@@ -352,13 +375,13 @@ ALvoid ReleaseALEffects(ALCdevice *device)
     ALsizei i;
     for(i = 0;i < device->EffectMap.size;i++)
     {
-        ALeffect *temp = device->EffectMap.array[i].value;
-        device->EffectMap.array[i].value = NULL;
+        ALeffect *temp = device->EffectMap.values[i];
+        device->EffectMap.values[i] = NULL;
 
         // Release effect structure
         FreeThunkEntry(temp->id);
         memset(temp, 0, sizeof(ALeffect));
-        free(temp);
+        al_free(temp);
     }
 }
 
@@ -427,13 +450,6 @@ static void InitEffectParams(ALeffect *effect, ALenum type)
         effect->Props.Reverb.DecayHFLimit = AL_REVERB_DEFAULT_DECAY_HFLIMIT;
         SET_VTABLE1(ALreverb, effect);
         break;
-    case AL_EFFECT_AUTOWAH:
-        effect->Props.Autowah.AttackTime = AL_AUTOWAH_DEFAULT_ATTACK_TIME;
-        effect->Props.Autowah.PeakGain = AL_AUTOWAH_DEFAULT_PEAK_GAIN;
-        effect->Props.Autowah.ReleaseTime = AL_AUTOWAH_DEFAULT_RELEASE_TIME;
-        effect->Props.Autowah.Resonance = AL_AUTOWAH_DEFAULT_RESONANCE;
-        SET_VTABLE1(ALautowah, effect);
-        break;
     case AL_EFFECT_CHORUS:
         effect->Props.Chorus.Waveform = AL_CHORUS_DEFAULT_WAVEFORM;
         effect->Props.Chorus.Phase = AL_CHORUS_DEFAULT_PHASE;
@@ -651,9 +667,9 @@ ALvoid LoadReverbPreset(const char *name, ALeffect *effect)
         return;
     }
 
-    if(!DisabledEffects[EAXREVERB])
+    if(!DisabledEffects[AL__EAXREVERB])
         InitEffectParams(effect, AL_EFFECT_EAXREVERB);
-    else if(!DisabledEffects[REVERB])
+    else if(!DisabledEffects[AL__REVERB])
         InitEffectParams(effect, AL_EFFECT_REVERB);
     else
         InitEffectParams(effect, AL_EFFECT_NULL);

+ 7 - 2
libs/openal-soft/OpenAL32/alError.c

@@ -36,6 +36,8 @@ ALboolean TrapALError = AL_FALSE;
 ALvoid alSetError(ALCcontext *Context, ALenum errorCode)
 {
     ALenum curerr = AL_NO_ERROR;
+
+    WARN("Error generated on context %p, code 0x%04x\n", Context, errorCode);
     if(TrapALError)
     {
 #ifdef _WIN32
@@ -46,7 +48,8 @@ ALvoid alSetError(ALCcontext *Context, ALenum errorCode)
         raise(SIGTRAP);
 #endif
     }
-    ATOMIC_COMPARE_EXCHANGE_STRONG(ALenum, &Context->LastError, &curerr, errorCode);
+
+    (void)(ATOMIC_COMPARE_EXCHANGE_STRONG_SEQ(&Context->LastError, &curerr, errorCode));
 }
 
 AL_API ALenum AL_APIENTRY alGetError(void)
@@ -57,6 +60,8 @@ AL_API ALenum AL_APIENTRY alGetError(void)
     Context = GetContextRef();
     if(!Context)
     {
+        WARN("Querying error state on null context (implicitly 0x%04x)\n",
+             AL_INVALID_OPERATION);
         if(TrapALError)
         {
 #ifdef _WIN32
@@ -69,7 +74,7 @@ AL_API ALenum AL_APIENTRY alGetError(void)
         return AL_INVALID_OPERATION;
     }
 
-    errorCode = ATOMIC_EXCHANGE(ALenum, &Context->LastError, AL_NO_ERROR);
+    errorCode = ATOMIC_EXCHANGE_SEQ(&Context->LastError, AL_NO_ERROR);
 
     ALCcontext_DecRef(Context);
 

+ 11 - 14
libs/openal-soft/OpenAL32/alExtension.c

@@ -36,20 +36,17 @@
 
 
 const struct EffectList EffectList[] = {
-    { "eaxreverb",  EAXREVERB,  "AL_EFFECT_EAXREVERB",      AL_EFFECT_EAXREVERB },
-    { "reverb",     REVERB,     "AL_EFFECT_REVERB",         AL_EFFECT_REVERB },
-#if 0
-    { "autowah",    AUTOWAH,    "AL_EFFECT_AUTOWAH",        AL_EFFECT_AUTOWAH },
-#endif
-    { "chorus",     CHORUS,     "AL_EFFECT_CHORUS",         AL_EFFECT_CHORUS },
-    { "compressor", COMPRESSOR, "AL_EFFECT_COMPRESSOR",     AL_EFFECT_COMPRESSOR },
-    { "distortion", DISTORTION, "AL_EFFECT_DISTORTION",     AL_EFFECT_DISTORTION },
-    { "echo",       ECHO,       "AL_EFFECT_ECHO",           AL_EFFECT_ECHO },
-    { "equalizer",  EQUALIZER,  "AL_EFFECT_EQUALIZER",      AL_EFFECT_EQUALIZER },
-    { "flanger",    FLANGER,    "AL_EFFECT_FLANGER",        AL_EFFECT_FLANGER },
-    { "modulator",  MODULATOR,  "AL_EFFECT_RING_MODULATOR", AL_EFFECT_RING_MODULATOR },
-    { "dedicated",  DEDICATED,  "AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT", AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT },
-    { "dedicated",  DEDICATED,  "AL_EFFECT_DEDICATED_DIALOGUE", AL_EFFECT_DEDICATED_DIALOGUE },
+    { "eaxreverb",  AL__EAXREVERB,  "AL_EFFECT_EAXREVERB",      AL_EFFECT_EAXREVERB },
+    { "reverb",     AL__REVERB,     "AL_EFFECT_REVERB",         AL_EFFECT_REVERB },
+    { "chorus",     AL__CHORUS,     "AL_EFFECT_CHORUS",         AL_EFFECT_CHORUS },
+    { "compressor", AL__COMPRESSOR, "AL_EFFECT_COMPRESSOR",     AL_EFFECT_COMPRESSOR },
+    { "distortion", AL__DISTORTION, "AL_EFFECT_DISTORTION",     AL_EFFECT_DISTORTION },
+    { "echo",       AL__ECHO,       "AL_EFFECT_ECHO",           AL_EFFECT_ECHO },
+    { "equalizer",  AL__EQUALIZER,  "AL_EFFECT_EQUALIZER",      AL_EFFECT_EQUALIZER },
+    { "flanger",    AL__FLANGER,    "AL_EFFECT_FLANGER",        AL_EFFECT_FLANGER },
+    { "modulator",  AL__MODULATOR,  "AL_EFFECT_RING_MODULATOR", AL_EFFECT_RING_MODULATOR },
+    { "dedicated",  AL__DEDICATED,  "AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT", AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT },
+    { "dedicated",  AL__DEDICATED,  "AL_EFFECT_DEDICATED_DIALOGUE", AL_EFFECT_DEDICATED_DIALOGUE },
     { NULL, 0, NULL, (ALenum)0 }
 };
 

+ 35 - 14
libs/openal-soft/OpenAL32/alFilter.c

@@ -29,11 +29,15 @@
 #include "alError.h"
 
 
+extern inline void LockFiltersRead(ALCdevice *device);
+extern inline void UnlockFiltersRead(ALCdevice *device);
+extern inline void LockFiltersWrite(ALCdevice *device);
+extern inline void UnlockFiltersWrite(ALCdevice *device);
 extern inline struct ALfilter *LookupFilter(ALCdevice *device, ALuint id);
 extern inline struct ALfilter *RemoveFilter(ALCdevice *device, ALuint id);
 extern inline void ALfilterState_clear(ALfilterState *filter);
-extern inline void ALfilterState_processPassthru(ALfilterState *filter, const ALfloat *src, ALuint numsamples);
-extern inline ALfloat ALfilterState_processSingle(ALfilterState *filter, ALfloat sample);
+extern inline void ALfilterState_copyParams(ALfilterState *restrict dst, const ALfilterState *restrict src);
+extern inline void ALfilterState_processPassthru(ALfilterState *filter, const ALfloat *restrict src, ALsizei numsamples);
 extern inline ALfloat calc_rcpQ_from_slope(ALfloat gain, ALfloat slope);
 extern inline ALfloat calc_rcpQ_from_bandwidth(ALfloat freq_mult, ALfloat bandwidth);
 
@@ -56,7 +60,7 @@ AL_API ALvoid AL_APIENTRY alGenFilters(ALsizei n, ALuint *filters)
     device = context->Device;
     for(cur = 0;cur < n;cur++)
     {
-        ALfilter *filter = calloc(1, sizeof(ALfilter));
+        ALfilter *filter = al_calloc(16, sizeof(ALfilter));
         if(!filter)
         {
             alDeleteFilters(cur, filters);
@@ -71,7 +75,7 @@ AL_API ALvoid AL_APIENTRY alGenFilters(ALsizei n, ALuint *filters)
         {
             FreeThunkEntry(filter->id);
             memset(filter, 0, sizeof(ALfilter));
-            free(filter);
+            al_free(filter);
 
             alDeleteFilters(cur, filters);
             SET_ERROR_AND_GOTO(context, err, done);
@@ -94,10 +98,10 @@ AL_API ALvoid AL_APIENTRY alDeleteFilters(ALsizei n, const ALuint *filters)
     context = GetContextRef();
     if(!context) return;
 
+    device = context->Device;
+    LockFiltersWrite(device);
     if(!(n >= 0))
         SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
-
-    device = context->Device;
     for(i = 0;i < n;i++)
     {
         if(filters[i] && LookupFilter(device, filters[i]) == NULL)
@@ -110,10 +114,11 @@ AL_API ALvoid AL_APIENTRY alDeleteFilters(ALsizei n, const ALuint *filters)
         FreeThunkEntry(filter->id);
 
         memset(filter, 0, sizeof(*filter));
-        free(filter);
+        al_free(filter);
     }
 
 done:
+    UnlockFiltersWrite(device);
     ALCcontext_DecRef(context);
 }
 
@@ -125,8 +130,10 @@ AL_API ALboolean AL_APIENTRY alIsFilter(ALuint filter)
     Context = GetContextRef();
     if(!Context) return AL_FALSE;
 
+    LockFiltersRead(Context->Device);
     result = ((!filter || LookupFilter(Context->Device, filter)) ?
               AL_TRUE : AL_FALSE);
+    UnlockFiltersRead(Context->Device);
 
     ALCcontext_DecRef(Context);
 
@@ -143,6 +150,7 @@ AL_API ALvoid AL_APIENTRY alFilteri(ALuint filter, ALenum param, ALint value)
     if(!Context) return;
 
     Device = Context->Device;
+    LockFiltersWrite(Device);
     if((ALFilter=LookupFilter(Device, filter)) == NULL)
         alSetError(Context, AL_INVALID_NAME);
     else
@@ -161,6 +169,7 @@ AL_API ALvoid AL_APIENTRY alFilteri(ALuint filter, ALenum param, ALint value)
             ALfilter_SetParami(ALFilter, Context, param, value);
         }
     }
+    UnlockFiltersWrite(Device);
 
     ALCcontext_DecRef(Context);
 }
@@ -182,6 +191,7 @@ AL_API ALvoid AL_APIENTRY alFilteriv(ALuint filter, ALenum param, const ALint *v
     if(!Context) return;
 
     Device = Context->Device;
+    LockFiltersWrite(Device);
     if((ALFilter=LookupFilter(Device, filter)) == NULL)
         alSetError(Context, AL_INVALID_NAME);
     else
@@ -189,6 +199,7 @@ AL_API ALvoid AL_APIENTRY alFilteriv(ALuint filter, ALenum param, const ALint *v
         /* Call the appropriate handler */
         ALfilter_SetParamiv(ALFilter, Context, param, values);
     }
+    UnlockFiltersWrite(Device);
 
     ALCcontext_DecRef(Context);
 }
@@ -203,6 +214,7 @@ AL_API ALvoid AL_APIENTRY alFilterf(ALuint filter, ALenum param, ALfloat value)
     if(!Context) return;
 
     Device = Context->Device;
+    LockFiltersWrite(Device);
     if((ALFilter=LookupFilter(Device, filter)) == NULL)
         alSetError(Context, AL_INVALID_NAME);
     else
@@ -210,6 +222,7 @@ AL_API ALvoid AL_APIENTRY alFilterf(ALuint filter, ALenum param, ALfloat value)
         /* Call the appropriate handler */
         ALfilter_SetParamf(ALFilter, Context, param, value);
     }
+    UnlockFiltersWrite(Device);
 
     ALCcontext_DecRef(Context);
 }
@@ -224,6 +237,7 @@ AL_API ALvoid AL_APIENTRY alFilterfv(ALuint filter, ALenum param, const ALfloat
     if(!Context) return;
 
     Device = Context->Device;
+    LockFiltersWrite(Device);
     if((ALFilter=LookupFilter(Device, filter)) == NULL)
         alSetError(Context, AL_INVALID_NAME);
     else
@@ -231,6 +245,7 @@ AL_API ALvoid AL_APIENTRY alFilterfv(ALuint filter, ALenum param, const ALfloat
         /* Call the appropriate handler */
         ALfilter_SetParamfv(ALFilter, Context, param, values);
     }
+    UnlockFiltersWrite(Device);
 
     ALCcontext_DecRef(Context);
 }
@@ -245,6 +260,7 @@ AL_API ALvoid AL_APIENTRY alGetFilteri(ALuint filter, ALenum param, ALint *value
     if(!Context) return;
 
     Device = Context->Device;
+    LockFiltersRead(Device);
     if((ALFilter=LookupFilter(Device, filter)) == NULL)
         alSetError(Context, AL_INVALID_NAME);
     else
@@ -257,6 +273,7 @@ AL_API ALvoid AL_APIENTRY alGetFilteri(ALuint filter, ALenum param, ALint *value
             ALfilter_GetParami(ALFilter, Context, param, value);
         }
     }
+    UnlockFiltersRead(Device);
 
     ALCcontext_DecRef(Context);
 }
@@ -278,6 +295,7 @@ AL_API ALvoid AL_APIENTRY alGetFilteriv(ALuint filter, ALenum param, ALint *valu
     if(!Context) return;
 
     Device = Context->Device;
+    LockFiltersRead(Device);
     if((ALFilter=LookupFilter(Device, filter)) == NULL)
         alSetError(Context, AL_INVALID_NAME);
     else
@@ -285,6 +303,7 @@ AL_API ALvoid AL_APIENTRY alGetFilteriv(ALuint filter, ALenum param, ALint *valu
         /* Call the appropriate handler */
         ALfilter_GetParamiv(ALFilter, Context, param, values);
     }
+    UnlockFiltersRead(Device);
 
     ALCcontext_DecRef(Context);
 }
@@ -299,6 +318,7 @@ AL_API ALvoid AL_APIENTRY alGetFilterf(ALuint filter, ALenum param, ALfloat *val
     if(!Context) return;
 
     Device = Context->Device;
+    LockFiltersRead(Device);
     if((ALFilter=LookupFilter(Device, filter)) == NULL)
         alSetError(Context, AL_INVALID_NAME);
     else
@@ -306,6 +326,7 @@ AL_API ALvoid AL_APIENTRY alGetFilterf(ALuint filter, ALenum param, ALfloat *val
         /* Call the appropriate handler */
         ALfilter_GetParamf(ALFilter, Context, param, value);
     }
+    UnlockFiltersRead(Device);
 
     ALCcontext_DecRef(Context);
 }
@@ -320,6 +341,7 @@ AL_API ALvoid AL_APIENTRY alGetFilterfv(ALuint filter, ALenum param, ALfloat *va
     if(!Context) return;
 
     Device = Context->Device;
+    LockFiltersRead(Device);
     if((ALFilter=LookupFilter(Device, filter)) == NULL)
         alSetError(Context, AL_INVALID_NAME);
     else
@@ -327,6 +349,7 @@ AL_API ALvoid AL_APIENTRY alGetFilterfv(ALuint filter, ALenum param, ALfloat *va
         /* Call the appropriate handler */
         ALfilter_GetParamfv(ALFilter, Context, param, values);
     }
+    UnlockFiltersRead(Device);
 
     ALCcontext_DecRef(Context);
 }
@@ -340,7 +363,7 @@ void ALfilterState_setParams(ALfilterState *filter, ALfilterType type, ALfloat g
     ALfloat b[3] = { 1.0f, 0.0f, 0.0f };
 
     // Limit gain to -100dB
-    gain = maxf(gain, 0.00001f);
+    assert(gain > 0.00001f);
 
     w0 = F_TAU * freq_mult;
     sin_w0 = sinf(w0);
@@ -406,11 +429,9 @@ void ALfilterState_setParams(ALfilterState *filter, ALfilterType type, ALfloat g
 
     filter->a1 = a[1] / a[0];
     filter->a2 = a[2] / a[0];
+    filter->b0 = b[0] / a[0];
     filter->b1 = b[1] / a[0];
     filter->b2 = b[2] / a[0];
-    filter->input_gain = b[0] / a[0];
-
-    filter->process = ALfilterState_processC;
 }
 
 
@@ -613,13 +634,13 @@ ALvoid ReleaseALFilters(ALCdevice *device)
     ALsizei i;
     for(i = 0;i < device->FilterMap.size;i++)
     {
-        ALfilter *temp = device->FilterMap.array[i].value;
-        device->FilterMap.array[i].value = NULL;
+        ALfilter *temp = device->FilterMap.values[i];
+        device->FilterMap.values[i] = NULL;
 
         // Release filter structure
         FreeThunkEntry(temp->id);
         memset(temp, 0, sizeof(ALfilter));
-        free(temp);
+        al_free(temp);
     }
 }
 

+ 110 - 42
libs/openal-soft/OpenAL32/alListener.c

@@ -33,29 +33,29 @@ AL_API ALvoid AL_APIENTRY alListenerf(ALenum param, ALfloat value)
     context = GetContextRef();
     if(!context) return;
 
+    WriteLock(&context->PropLock);
     switch(param)
     {
     case AL_GAIN:
         if(!(value >= 0.0f && isfinite(value)))
             SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
-
         context->Listener->Gain = value;
-        ATOMIC_STORE(&context->UpdateSources, AL_TRUE);
         break;
 
     case AL_METERS_PER_UNIT:
         if(!(value >= 0.0f && isfinite(value)))
             SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
-
         context->Listener->MetersPerUnit = value;
-        ATOMIC_STORE(&context->UpdateSources, AL_TRUE);
         break;
 
     default:
         SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
     }
+    if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
+        UpdateListenerProps(context);
 
 done:
+    WriteUnlock(&context->PropLock);
     ALCcontext_DecRef(context);
 }
 
@@ -67,33 +67,33 @@ AL_API ALvoid AL_APIENTRY alListener3f(ALenum param, ALfloat value1, ALfloat val
     context = GetContextRef();
     if(!context) return;
 
+    WriteLock(&context->PropLock);
     switch(param)
     {
     case AL_POSITION:
         if(!(isfinite(value1) && isfinite(value2) && isfinite(value3)))
             SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
-
-        LockContext(context);
-        aluVectorSet(&context->Listener->Position, value1, value2, value3, 1.0f);
-        ATOMIC_STORE(&context->UpdateSources, AL_TRUE);
-        UnlockContext(context);
+        context->Listener->Position[0] = value1;
+        context->Listener->Position[1] = value2;
+        context->Listener->Position[2] = value3;
         break;
 
     case AL_VELOCITY:
         if(!(isfinite(value1) && isfinite(value2) && isfinite(value3)))
             SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
-
-        LockContext(context);
-        aluVectorSet(&context->Listener->Velocity, value1, value2, value3, 0.0f);
-        ATOMIC_STORE(&context->UpdateSources, AL_TRUE);
-        UnlockContext(context);
+        context->Listener->Velocity[0] = value1;
+        context->Listener->Velocity[1] = value2;
+        context->Listener->Velocity[2] = value3;
         break;
 
     default:
         SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
     }
+    if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
+        UpdateListenerProps(context);
 
 done:
+    WriteUnlock(&context->PropLock);
     ALCcontext_DecRef(context);
 }
 
@@ -121,6 +121,7 @@ AL_API ALvoid AL_APIENTRY alListenerfv(ALenum param, const ALfloat *values)
     context = GetContextRef();
     if(!context) return;
 
+    WriteLock(&context->PropLock);
     if(!(values))
         SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
     switch(param)
@@ -129,8 +130,6 @@ AL_API ALvoid AL_APIENTRY alListenerfv(ALenum param, const ALfloat *values)
         if(!(isfinite(values[0]) && isfinite(values[1]) && isfinite(values[2]) &&
              isfinite(values[3]) && isfinite(values[4]) && isfinite(values[5])))
             SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
-
-        LockContext(context);
         /* AT then UP */
         context->Listener->Forward[0] = values[0];
         context->Listener->Forward[1] = values[1];
@@ -138,15 +137,16 @@ AL_API ALvoid AL_APIENTRY alListenerfv(ALenum param, const ALfloat *values)
         context->Listener->Up[0] = values[3];
         context->Listener->Up[1] = values[4];
         context->Listener->Up[2] = values[5];
-        ATOMIC_STORE(&context->UpdateSources, AL_TRUE);
-        UnlockContext(context);
         break;
 
     default:
         SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
     }
+    if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
+        UpdateListenerProps(context);
 
 done:
+    WriteUnlock(&context->PropLock);
     ALCcontext_DecRef(context);
 }
 
@@ -158,13 +158,17 @@ AL_API ALvoid AL_APIENTRY alListeneri(ALenum param, ALint UNUSED(value))
     context = GetContextRef();
     if(!context) return;
 
+    WriteLock(&context->PropLock);
     switch(param)
     {
     default:
         SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
     }
+    if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
+        UpdateListenerProps(context);
 
 done:
+    WriteUnlock(&context->PropLock);
     ALCcontext_DecRef(context);
 }
 
@@ -184,13 +188,17 @@ AL_API void AL_APIENTRY alListener3i(ALenum param, ALint value1, ALint value2, A
     context = GetContextRef();
     if(!context) return;
 
+    WriteLock(&context->PropLock);
     switch(param)
     {
     default:
         SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
     }
+    if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
+        UpdateListenerProps(context);
 
 done:
+    WriteUnlock(&context->PropLock);
     ALCcontext_DecRef(context);
 }
 
@@ -224,6 +232,7 @@ AL_API void AL_APIENTRY alListeneriv(ALenum param, const ALint *values)
     context = GetContextRef();
     if(!context) return;
 
+    WriteLock(&context->PropLock);
     if(!(values))
         SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
     switch(param)
@@ -231,8 +240,11 @@ AL_API void AL_APIENTRY alListeneriv(ALenum param, const ALint *values)
     default:
         SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
     }
+    if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
+        UpdateListenerProps(context);
 
 done:
+    WriteUnlock(&context->PropLock);
     ALCcontext_DecRef(context);
 }
 
@@ -244,6 +256,7 @@ AL_API ALvoid AL_APIENTRY alGetListenerf(ALenum param, ALfloat *value)
     context = GetContextRef();
     if(!context) return;
 
+    ReadLock(&context->PropLock);
     if(!(value))
         SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
     switch(param)
@@ -261,6 +274,7 @@ AL_API ALvoid AL_APIENTRY alGetListenerf(ALenum param, ALfloat *value)
     }
 
 done:
+    ReadUnlock(&context->PropLock);
     ALCcontext_DecRef(context);
 }
 
@@ -272,24 +286,21 @@ AL_API ALvoid AL_APIENTRY alGetListener3f(ALenum param, ALfloat *value1, ALfloat
     context = GetContextRef();
     if(!context) return;
 
+    ReadLock(&context->PropLock);
     if(!(value1 && value2 && value3))
         SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
     switch(param)
     {
     case AL_POSITION:
-        LockContext(context);
-        *value1 = context->Listener->Position.v[0];
-        *value2 = context->Listener->Position.v[1];
-        *value3 = context->Listener->Position.v[2];
-        UnlockContext(context);
+        *value1 = context->Listener->Position[0];
+        *value2 = context->Listener->Position[1];
+        *value3 = context->Listener->Position[2];
         break;
 
     case AL_VELOCITY:
-        LockContext(context);
-        *value1 = context->Listener->Velocity.v[0];
-        *value2 = context->Listener->Velocity.v[1];
-        *value3 = context->Listener->Velocity.v[2];
-        UnlockContext(context);
+        *value1 = context->Listener->Velocity[0];
+        *value2 = context->Listener->Velocity[1];
+        *value3 = context->Listener->Velocity[2];
         break;
 
     default:
@@ -297,6 +308,7 @@ AL_API ALvoid AL_APIENTRY alGetListener3f(ALenum param, ALfloat *value1, ALfloat
     }
 
 done:
+    ReadUnlock(&context->PropLock);
     ALCcontext_DecRef(context);
 }
 
@@ -321,12 +333,12 @@ AL_API ALvoid AL_APIENTRY alGetListenerfv(ALenum param, ALfloat *values)
     context = GetContextRef();
     if(!context) return;
 
+    ReadLock(&context->PropLock);
     if(!(values))
         SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
     switch(param)
     {
     case AL_ORIENTATION:
-        LockContext(context);
         // AT then UP
         values[0] = context->Listener->Forward[0];
         values[1] = context->Listener->Forward[1];
@@ -334,7 +346,6 @@ AL_API ALvoid AL_APIENTRY alGetListenerfv(ALenum param, ALfloat *values)
         values[3] = context->Listener->Up[0];
         values[4] = context->Listener->Up[1];
         values[5] = context->Listener->Up[2];
-        UnlockContext(context);
         break;
 
     default:
@@ -342,6 +353,7 @@ AL_API ALvoid AL_APIENTRY alGetListenerfv(ALenum param, ALfloat *values)
     }
 
 done:
+    ReadUnlock(&context->PropLock);
     ALCcontext_DecRef(context);
 }
 
@@ -353,6 +365,7 @@ AL_API ALvoid AL_APIENTRY alGetListeneri(ALenum param, ALint *value)
     context = GetContextRef();
     if(!context) return;
 
+    ReadLock(&context->PropLock);
     if(!(value))
         SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
     switch(param)
@@ -362,6 +375,7 @@ AL_API ALvoid AL_APIENTRY alGetListeneri(ALenum param, ALint *value)
     }
 
 done:
+    ReadUnlock(&context->PropLock);
     ALCcontext_DecRef(context);
 }
 
@@ -373,24 +387,21 @@ AL_API void AL_APIENTRY alGetListener3i(ALenum param, ALint *value1, ALint *valu
     context = GetContextRef();
     if(!context) return;
 
+    ReadLock(&context->PropLock);
     if(!(value1 && value2 && value3))
         SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
     switch (param)
     {
     case AL_POSITION:
-        LockContext(context);
-        *value1 = (ALint)context->Listener->Position.v[0];
-        *value2 = (ALint)context->Listener->Position.v[1];
-        *value3 = (ALint)context->Listener->Position.v[2];
-        UnlockContext(context);
+        *value1 = (ALint)context->Listener->Position[0];
+        *value2 = (ALint)context->Listener->Position[1];
+        *value3 = (ALint)context->Listener->Position[2];
         break;
 
     case AL_VELOCITY:
-        LockContext(context);
-        *value1 = (ALint)context->Listener->Velocity.v[0];
-        *value2 = (ALint)context->Listener->Velocity.v[1];
-        *value3 = (ALint)context->Listener->Velocity.v[2];
-        UnlockContext(context);
+        *value1 = (ALint)context->Listener->Velocity[0];
+        *value2 = (ALint)context->Listener->Velocity[1];
+        *value3 = (ALint)context->Listener->Velocity[2];
         break;
 
     default:
@@ -398,6 +409,7 @@ AL_API void AL_APIENTRY alGetListener3i(ALenum param, ALint *value1, ALint *valu
     }
 
 done:
+    ReadUnlock(&context->PropLock);
     ALCcontext_DecRef(context);
 }
 
@@ -417,12 +429,12 @@ AL_API void AL_APIENTRY alGetListeneriv(ALenum param, ALint* values)
     context = GetContextRef();
     if(!context) return;
 
+    ReadLock(&context->PropLock);
     if(!(values))
         SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
     switch(param)
     {
     case AL_ORIENTATION:
-        LockContext(context);
         // AT then UP
         values[0] = (ALint)context->Listener->Forward[0];
         values[1] = (ALint)context->Listener->Forward[1];
@@ -430,7 +442,6 @@ AL_API void AL_APIENTRY alGetListeneriv(ALenum param, ALint* values)
         values[3] = (ALint)context->Listener->Up[0];
         values[4] = (ALint)context->Listener->Up[1];
         values[5] = (ALint)context->Listener->Up[2];
-        UnlockContext(context);
         break;
 
     default:
@@ -438,5 +449,62 @@ AL_API void AL_APIENTRY alGetListeneriv(ALenum param, ALint* values)
     }
 
 done:
+    ReadUnlock(&context->PropLock);
     ALCcontext_DecRef(context);
 }
+
+
+void UpdateListenerProps(ALCcontext *context)
+{
+    ALlistener *listener = context->Listener;
+    struct ALlistenerProps *props;
+
+    /* Get an unused proprty container, or allocate a new one as needed. */
+    props = ATOMIC_LOAD(&listener->FreeList, almemory_order_acquire);
+    if(!props)
+        props = al_calloc(16, sizeof(*props));
+    else
+    {
+        struct ALlistenerProps *next;
+        do {
+            next = ATOMIC_LOAD(&props->next, almemory_order_relaxed);
+        } while(ATOMIC_COMPARE_EXCHANGE_PTR_WEAK(&listener->FreeList, &props, next,
+                almemory_order_seq_cst, almemory_order_acquire) == 0);
+    }
+
+    /* Copy in current property values. */
+    props->Position[0] = listener->Position[0];
+    props->Position[1] = listener->Position[1];
+    props->Position[2] = listener->Position[2];
+
+    props->Velocity[0] = listener->Velocity[0];
+    props->Velocity[1] = listener->Velocity[1];
+    props->Velocity[2] = listener->Velocity[2];
+
+    props->Forward[0] = listener->Forward[0];
+    props->Forward[1] = listener->Forward[1];
+    props->Forward[2] = listener->Forward[2];
+    props->Up[0] = listener->Up[0];
+    props->Up[1] = listener->Up[1];
+    props->Up[2] = listener->Up[2];
+
+    props->Gain = listener->Gain;
+    props->MetersPerUnit = listener->MetersPerUnit;
+
+    props->DopplerFactor = context->DopplerFactor;
+    props->DopplerVelocity = context->DopplerVelocity;
+    props->SpeedOfSound = context->SpeedOfSound;
+
+    props->SourceDistanceModel = context->SourceDistanceModel;
+    props->DistanceModel = context->DistanceModel;;
+
+    /* Set the new container for updating internal parameters. */
+    props = ATOMIC_EXCHANGE_PTR(&listener->Update, props, almemory_order_acq_rel);
+    if(props)
+    {
+        /* If there was an unused update container, put it back in the
+         * freelist.
+         */
+        ATOMIC_REPLACE_HEAD(struct ALlistenerProps*, &listener->FreeList, props);
+    }
+}

File diff suppressed because it is too large
+ 340 - 157
libs/openal-soft/OpenAL32/alSource.c


+ 155 - 11
libs/openal-soft/OpenAL32/alState.c

@@ -20,12 +20,15 @@
 
 #include "config.h"
 
+#include "version.h"
+
 #include <stdlib.h>
 #include "alMain.h"
 #include "AL/alc.h"
 #include "AL/al.h"
 #include "AL/alext.h"
 #include "alError.h"
+#include "alListener.h"
 #include "alSource.h"
 #include "alAuxEffectSlot.h"
 
@@ -44,6 +47,12 @@ static const ALchar alErrInvalidValue[] = "Invalid Value";
 static const ALchar alErrInvalidOp[] = "Invalid Operation";
 static const ALchar alErrOutOfMemory[] = "Out of Memory";
 
+/* Resampler strings */
+static const ALchar alPointResampler[] = "Nearest";
+static const ALchar alLinearResampler[] = "Linear";
+static const ALchar alSinc4Resampler[] = "4-Point Sinc";
+static const ALchar alBSincResampler[] = "Band-limited Sinc (12/24)";
+
 AL_API ALvoid AL_APIENTRY alEnable(ALenum capability)
 {
     ALCcontext *context;
@@ -51,18 +60,21 @@ AL_API ALvoid AL_APIENTRY alEnable(ALenum capability)
     context = GetContextRef();
     if(!context) return;
 
+    WriteLock(&context->PropLock);
     switch(capability)
     {
     case AL_SOURCE_DISTANCE_MODEL:
         context->SourceDistanceModel = AL_TRUE;
-        ATOMIC_STORE(&context->UpdateSources, AL_TRUE);
         break;
 
     default:
         SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
     }
+    if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
+        UpdateListenerProps(context);
 
 done:
+    WriteUnlock(&context->PropLock);
     ALCcontext_DecRef(context);
 }
 
@@ -73,18 +85,21 @@ AL_API ALvoid AL_APIENTRY alDisable(ALenum capability)
     context = GetContextRef();
     if(!context) return;
 
+    WriteLock(&context->PropLock);
     switch(capability)
     {
     case AL_SOURCE_DISTANCE_MODEL:
         context->SourceDistanceModel = AL_FALSE;
-        ATOMIC_STORE(&context->UpdateSources, AL_TRUE);
         break;
 
     default:
         SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
     }
+    if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
+        UpdateListenerProps(context);
 
 done:
+    WriteUnlock(&context->PropLock);
     ALCcontext_DecRef(context);
 }
 
@@ -143,7 +158,22 @@ AL_API ALboolean AL_APIENTRY alGetBoolean(ALenum pname)
         break;
 
     case AL_DEFERRED_UPDATES_SOFT:
-        value = context->DeferUpdates;
+        if(ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
+            value = AL_TRUE;
+        break;
+
+    case AL_GAIN_LIMIT_SOFT:
+        if(GAIN_MIX_MAX/context->GainBoost != 0.0f)
+            value = AL_TRUE;
+        break;
+
+    case AL_NUM_RESAMPLERS_SOFT:
+        /* Always non-0. */
+        value = AL_TRUE;
+        break;
+
+    case AL_DEFAULT_RESAMPLER_SOFT:
+        value = ResamplerDefault ? AL_TRUE : AL_FALSE;
         break;
 
     default:
@@ -183,7 +213,20 @@ AL_API ALdouble AL_APIENTRY alGetDouble(ALenum pname)
         break;
 
     case AL_DEFERRED_UPDATES_SOFT:
-        value = (ALdouble)context->DeferUpdates;
+        if(ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
+            value = (ALdouble)AL_TRUE;
+        break;
+
+    case AL_GAIN_LIMIT_SOFT:
+        value = (ALdouble)GAIN_MIX_MAX/context->GainBoost;
+        break;
+
+    case AL_NUM_RESAMPLERS_SOFT:
+        value = (ALdouble)(ResamplerMax + 1);
+        break;
+
+    case AL_DEFAULT_RESAMPLER_SOFT:
+        value = (ALdouble)ResamplerDefault;
         break;
 
     default:
@@ -223,7 +266,20 @@ AL_API ALfloat AL_APIENTRY alGetFloat(ALenum pname)
         break;
 
     case AL_DEFERRED_UPDATES_SOFT:
-        value = (ALfloat)context->DeferUpdates;
+        if(ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
+            value = (ALfloat)AL_TRUE;
+        break;
+
+    case AL_GAIN_LIMIT_SOFT:
+        value = GAIN_MIX_MAX/context->GainBoost;
+        break;
+
+    case AL_NUM_RESAMPLERS_SOFT:
+        value = (ALfloat)(ResamplerMax + 1);
+        break;
+
+    case AL_DEFAULT_RESAMPLER_SOFT:
+        value = (ALfloat)ResamplerDefault;
         break;
 
     default:
@@ -263,7 +319,20 @@ AL_API ALint AL_APIENTRY alGetInteger(ALenum pname)
         break;
 
     case AL_DEFERRED_UPDATES_SOFT:
-        value = (ALint)context->DeferUpdates;
+        if(ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
+            value = (ALint)AL_TRUE;
+        break;
+
+    case AL_GAIN_LIMIT_SOFT:
+        value = (ALint)(GAIN_MIX_MAX/context->GainBoost);
+        break;
+
+    case AL_NUM_RESAMPLERS_SOFT:
+        value = ResamplerMax + 1;
+        break;
+
+    case AL_DEFAULT_RESAMPLER_SOFT:
+        value = ResamplerDefault;
         break;
 
     default:
@@ -303,7 +372,20 @@ AL_API ALint64SOFT AL_APIENTRY alGetInteger64SOFT(ALenum pname)
         break;
 
     case AL_DEFERRED_UPDATES_SOFT:
-        value = (ALint64SOFT)context->DeferUpdates;
+        if(ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
+            value = (ALint64SOFT)AL_TRUE;
+        break;
+
+    case AL_GAIN_LIMIT_SOFT:
+        value = (ALint64SOFT)(GAIN_MIX_MAX/context->GainBoost);
+        break;
+
+    case AL_NUM_RESAMPLERS_SOFT:
+        value = (ALint64SOFT)(ResamplerMax + 1);
+        break;
+
+    case AL_DEFAULT_RESAMPLER_SOFT:
+        value = (ALint64SOFT)ResamplerDefault;
         break;
 
     default:
@@ -329,6 +411,9 @@ AL_API ALvoid AL_APIENTRY alGetBooleanv(ALenum pname, ALboolean *values)
             case AL_DISTANCE_MODEL:
             case AL_SPEED_OF_SOUND:
             case AL_DEFERRED_UPDATES_SOFT:
+            case AL_GAIN_LIMIT_SOFT:
+            case AL_NUM_RESAMPLERS_SOFT:
+            case AL_DEFAULT_RESAMPLER_SOFT:
                 values[0] = alGetBoolean(pname);
                 return;
         }
@@ -362,6 +447,9 @@ AL_API ALvoid AL_APIENTRY alGetDoublev(ALenum pname, ALdouble *values)
             case AL_DISTANCE_MODEL:
             case AL_SPEED_OF_SOUND:
             case AL_DEFERRED_UPDATES_SOFT:
+            case AL_GAIN_LIMIT_SOFT:
+            case AL_NUM_RESAMPLERS_SOFT:
+            case AL_DEFAULT_RESAMPLER_SOFT:
                 values[0] = alGetDouble(pname);
                 return;
         }
@@ -395,6 +483,9 @@ AL_API ALvoid AL_APIENTRY alGetFloatv(ALenum pname, ALfloat *values)
             case AL_DISTANCE_MODEL:
             case AL_SPEED_OF_SOUND:
             case AL_DEFERRED_UPDATES_SOFT:
+            case AL_GAIN_LIMIT_SOFT:
+            case AL_NUM_RESAMPLERS_SOFT:
+            case AL_DEFAULT_RESAMPLER_SOFT:
                 values[0] = alGetFloat(pname);
                 return;
         }
@@ -428,6 +519,9 @@ AL_API ALvoid AL_APIENTRY alGetIntegerv(ALenum pname, ALint *values)
             case AL_DISTANCE_MODEL:
             case AL_SPEED_OF_SOUND:
             case AL_DEFERRED_UPDATES_SOFT:
+            case AL_GAIN_LIMIT_SOFT:
+            case AL_NUM_RESAMPLERS_SOFT:
+            case AL_DEFAULT_RESAMPLER_SOFT:
                 values[0] = alGetInteger(pname);
                 return;
         }
@@ -459,6 +553,9 @@ AL_API void AL_APIENTRY alGetInteger64vSOFT(ALenum pname, ALint64SOFT *values)
             case AL_DISTANCE_MODEL:
             case AL_SPEED_OF_SOUND:
             case AL_DEFERRED_UPDATES_SOFT:
+            case AL_GAIN_LIMIT_SOFT:
+            case AL_NUM_RESAMPLERS_SOFT:
+            case AL_DEFAULT_RESAMPLER_SOFT:
                 values[0] = alGetInteger64SOFT(pname);
                 return;
         }
@@ -547,8 +644,11 @@ AL_API ALvoid AL_APIENTRY alDopplerFactor(ALfloat value)
     if(!(value >= 0.0f && isfinite(value)))
         SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
 
+    WriteLock(&context->PropLock);
     context->DopplerFactor = value;
-    ATOMIC_STORE(&context->UpdateSources, AL_TRUE);
+    if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
+        UpdateListenerProps(context);
+    WriteUnlock(&context->PropLock);
 
 done:
     ALCcontext_DecRef(context);
@@ -564,8 +664,11 @@ AL_API ALvoid AL_APIENTRY alDopplerVelocity(ALfloat value)
     if(!(value >= 0.0f && isfinite(value)))
         SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
 
+    WriteLock(&context->PropLock);
     context->DopplerVelocity = value;
-    ATOMIC_STORE(&context->UpdateSources, AL_TRUE);
+    if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
+        UpdateListenerProps(context);
+    WriteUnlock(&context->PropLock);
 
 done:
     ALCcontext_DecRef(context);
@@ -581,8 +684,11 @@ AL_API ALvoid AL_APIENTRY alSpeedOfSound(ALfloat value)
     if(!(value > 0.0f && isfinite(value)))
         SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
 
+    WriteLock(&context->PropLock);
     context->SpeedOfSound = value;
-    ATOMIC_STORE(&context->UpdateSources, AL_TRUE);
+    if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
+        UpdateListenerProps(context);
+    WriteUnlock(&context->PropLock);
 
 done:
     ALCcontext_DecRef(context);
@@ -601,9 +707,14 @@ AL_API ALvoid AL_APIENTRY alDistanceModel(ALenum value)
          value == AL_NONE))
         SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
 
+    WriteLock(&context->PropLock);
     context->DistanceModel = value;
     if(!context->SourceDistanceModel)
-        ATOMIC_STORE(&context->UpdateSources, AL_TRUE);
+    {
+        if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
+            UpdateListenerProps(context);
+    }
+    WriteUnlock(&context->PropLock);
 
 done:
     ALCcontext_DecRef(context);
@@ -633,3 +744,36 @@ AL_API ALvoid AL_APIENTRY alProcessUpdatesSOFT(void)
 
     ALCcontext_DecRef(context);
 }
+
+
+AL_API const ALchar* AL_APIENTRY alGetStringiSOFT(ALenum pname, ALsizei index)
+{
+    const char *ResamplerNames[] = {
+        alPointResampler, alLinearResampler,
+        alSinc4Resampler, alBSincResampler,
+    };
+    const ALchar *value = NULL;
+    ALCcontext *context;
+
+    static_assert(COUNTOF(ResamplerNames) == ResamplerMax+1, "Incorrect ResamplerNames list");
+
+    context = GetContextRef();
+    if(!context) return NULL;
+
+    switch(pname)
+    {
+    case AL_RESAMPLER_NAME_SOFT:
+        if(index < 0 || (size_t)index >= COUNTOF(ResamplerNames))
+            SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
+        value = ResamplerNames[index];
+        break;
+
+    default:
+        SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
+    }
+
+done:
+    ALCcontext_DecRef(context);
+
+    return value;
+}

+ 15 - 10
libs/openal-soft/OpenAL32/alThunk.c

@@ -25,15 +25,17 @@
 #include "alMain.h"
 #include "alThunk.h"
 
+#include "almalloc.h"
 
-static ATOMIC(ALenum) *ThunkArray;
-static ALuint          ThunkArraySize;
+
+static ATOMIC_FLAG *ThunkArray;
+static ALsizei      ThunkArraySize;
 static RWLock ThunkLock;
 
 void ThunkInit(void)
 {
     RWLockInit(&ThunkLock);
-    ThunkArraySize = 1;
+    ThunkArraySize = 1024;
     ThunkArray = al_calloc(16, ThunkArraySize * sizeof(*ThunkArray));
 }
 
@@ -47,12 +49,12 @@ void ThunkExit(void)
 ALenum NewThunkEntry(ALuint *index)
 {
     void *NewList;
-    ALuint i;
+    ALsizei i;
 
     ReadLock(&ThunkLock);
     for(i = 0;i < ThunkArraySize;i++)
     {
-        if(ATOMIC_EXCHANGE(ALenum, &ThunkArray[i], AL_TRUE) == AL_FALSE)
+        if(!ATOMIC_FLAG_TEST_AND_SET(&ThunkArray[i], almemory_order_acq_rel))
         {
             ReadUnlock(&ThunkLock);
             *index = i+1;
@@ -67,7 +69,7 @@ ALenum NewThunkEntry(ALuint *index)
      */
     for(;i < ThunkArraySize;i++)
     {
-        if(ATOMIC_EXCHANGE(ALenum, &ThunkArray[i], AL_TRUE) == AL_FALSE)
+        if(!ATOMIC_FLAG_TEST_AND_SET(&ThunkArray[i], almemory_order_acq_rel))
         {
             WriteUnlock(&ThunkLock);
             *index = i+1;
@@ -87,17 +89,20 @@ ALenum NewThunkEntry(ALuint *index)
     ThunkArray = NewList;
     ThunkArraySize *= 2;
 
-    ATOMIC_STORE(&ThunkArray[i], AL_TRUE);
+    ATOMIC_FLAG_TEST_AND_SET(&ThunkArray[i], almemory_order_seq_cst);
+    *index = ++i;
+
+    for(;i < ThunkArraySize;i++)
+        ATOMIC_FLAG_CLEAR(&ThunkArray[i], almemory_order_relaxed);
     WriteUnlock(&ThunkLock);
 
-    *index = i+1;
     return AL_NO_ERROR;
 }
 
 void FreeThunkEntry(ALuint index)
 {
     ReadLock(&ThunkLock);
-    if(index > 0 && index <= ThunkArraySize)
-        ATOMIC_STORE(&ThunkArray[index-1], AL_FALSE);
+    if(index > 0 && (ALsizei)index <= ThunkArraySize)
+        ATOMIC_FLAG_CLEAR(&ThunkArray[index-1], almemory_order_release);
     ReadUnlock(&ThunkLock);
 }

+ 106 - 371
libs/openal-soft/OpenAL32/sample_cvt.c

@@ -174,14 +174,6 @@ typedef ALubyte ALmulaw;
 typedef ALubyte ALalaw;
 typedef ALubyte ALima4;
 typedef ALubyte ALmsadpcm;
-typedef struct {
-    ALbyte b[3];
-} ALbyte3;
-static_assert(sizeof(ALbyte3)==sizeof(ALbyte[3]), "ALbyte3 size is not 3");
-typedef struct {
-    ALubyte b[3];
-} ALubyte3;
-static_assert(sizeof(ALubyte3)==sizeof(ALubyte[3]), "ALubyte3 size is not 3");
 
 static inline ALshort DecodeMuLaw(ALmulaw val)
 { return muLawDecompressionTable[val]; }
@@ -498,360 +490,128 @@ static void EncodeMSADPCMBlock(ALmsadpcm *dst, const ALshort *src, ALint *sample
 }
 
 
-static inline ALint DecodeByte3(ALbyte3 val)
-{
-    if(IS_LITTLE_ENDIAN)
-        return (val.b[2]<<16) | (((ALubyte)val.b[1])<<8) | ((ALubyte)val.b[0]);
-    return (val.b[0]<<16) | (((ALubyte)val.b[1])<<8) | ((ALubyte)val.b[2]);
-}
-
-static inline ALbyte3 EncodeByte3(ALint val)
-{
-    if(IS_LITTLE_ENDIAN)
-    {
-        ALbyte3 ret = {{ val, val>>8, val>>16 }};
-        return ret;
-    }
-    else
-    {
-        ALbyte3 ret = {{ val>>16, val>>8, val }};
-        return ret;
-    }
-}
-
-static inline ALint DecodeUByte3(ALubyte3 val)
-{
-    if(IS_LITTLE_ENDIAN)
-        return (val.b[2]<<16) | (val.b[1]<<8) | (val.b[0]);
-    return (val.b[0]<<16) | (val.b[1]<<8) | val.b[2];
-}
-
-static inline ALubyte3 EncodeUByte3(ALint val)
-{
-    if(IS_LITTLE_ENDIAN)
-    {
-        ALubyte3 ret = {{ val, val>>8, val>>16 }};
-        return ret;
-    }
-    else
-    {
-        ALubyte3 ret = {{ val>>16, val>>8, val }};
-        return ret;
-    }
-}
+/* Define same-type pass-through sample conversion functions (excludes ADPCM,
+ * which are block-based). */
+#define DECL_TEMPLATE(T) \
+static inline T Conv_##T##_##T(T val) { return val; }
 
+DECL_TEMPLATE(ALbyte);
+DECL_TEMPLATE(ALubyte);
+DECL_TEMPLATE(ALshort);
+DECL_TEMPLATE(ALushort);
+DECL_TEMPLATE(ALint);
+DECL_TEMPLATE(ALuint);
+DECL_TEMPLATE(ALalaw);
+DECL_TEMPLATE(ALmulaw);
 
-static inline ALbyte Conv_ALbyte_ALbyte(ALbyte val)
-{ return val; }
-static inline ALbyte Conv_ALbyte_ALubyte(ALubyte val)
-{ return val-128; }
-static inline ALbyte Conv_ALbyte_ALshort(ALshort val)
-{ return val>>8; }
-static inline ALbyte Conv_ALbyte_ALushort(ALushort val)
-{ return (val>>8)-128; }
-static inline ALbyte Conv_ALbyte_ALint(ALint val)
-{ return val>>24; }
-static inline ALbyte Conv_ALbyte_ALuint(ALuint val)
-{ return (val>>24)-128; }
-static inline ALbyte Conv_ALbyte_ALfloat(ALfloat val)
-{
-    if(val > 1.0f) return 127;
-    if(val < -1.0f) return -128;
-    return (ALint)(val * 127.0f);
-}
-static inline ALbyte Conv_ALbyte_ALdouble(ALdouble val)
-{
-    if(val > 1.0) return 127;
-    if(val < -1.0) return -128;
-    return (ALint)(val * 127.0);
-}
-static inline ALbyte Conv_ALbyte_ALmulaw(ALmulaw val)
-{ return Conv_ALbyte_ALshort(DecodeMuLaw(val)); }
-static inline ALbyte Conv_ALbyte_ALalaw(ALalaw val)
-{ return Conv_ALbyte_ALshort(DecodeALaw(val)); }
-static inline ALbyte Conv_ALbyte_ALbyte3(ALbyte3 val)
-{ return DecodeByte3(val)>>16; }
-static inline ALbyte Conv_ALbyte_ALubyte3(ALubyte3 val)
-{ return (DecodeUByte3(val)>>16)-128; }
-
-static inline ALubyte Conv_ALubyte_ALbyte(ALbyte val)
-{ return val+128; }
-static inline ALubyte Conv_ALubyte_ALubyte(ALubyte val)
-{ return val; }
-static inline ALubyte Conv_ALubyte_ALshort(ALshort val)
-{ return (val>>8)+128; }
-static inline ALubyte Conv_ALubyte_ALushort(ALushort val)
-{ return val>>8; }
-static inline ALubyte Conv_ALubyte_ALint(ALint val)
-{ return (val>>24)+128; }
-static inline ALubyte Conv_ALubyte_ALuint(ALuint val)
-{ return val>>24; }
-static inline ALubyte Conv_ALubyte_ALfloat(ALfloat val)
-{
-    if(val > 1.0f) return 255;
-    if(val < -1.0f) return 0;
-    return (ALint)(val * 127.0f) + 128;
-}
-static inline ALubyte Conv_ALubyte_ALdouble(ALdouble val)
-{
-    if(val > 1.0) return 255;
-    if(val < -1.0) return 0;
-    return (ALint)(val * 127.0) + 128;
-}
-static inline ALubyte Conv_ALubyte_ALmulaw(ALmulaw val)
-{ return Conv_ALubyte_ALshort(DecodeMuLaw(val)); }
-static inline ALubyte Conv_ALubyte_ALalaw(ALalaw val)
-{ return Conv_ALubyte_ALshort(DecodeALaw(val)); }
-static inline ALubyte Conv_ALubyte_ALbyte3(ALbyte3 val)
-{ return (DecodeByte3(val)>>16)+128; }
-static inline ALubyte Conv_ALubyte_ALubyte3(ALubyte3 val)
-{ return DecodeUByte3(val)>>16; }
-
-static inline ALshort Conv_ALshort_ALbyte(ALbyte val)
-{ return val<<8; }
-static inline ALshort Conv_ALshort_ALubyte(ALubyte val)
-{ return (val-128)<<8; }
-static inline ALshort Conv_ALshort_ALshort(ALshort val)
-{ return val; }
-static inline ALshort Conv_ALshort_ALushort(ALushort val)
-{ return val-32768; }
-static inline ALshort Conv_ALshort_ALint(ALint val)
-{ return val>>16; }
-static inline ALshort Conv_ALshort_ALuint(ALuint val)
-{ return (val>>16)-32768; }
-static inline ALshort Conv_ALshort_ALfloat(ALfloat val)
-{
-    if(val > 1.0f) return 32767;
-    if(val < -1.0f) return -32768;
-    return (ALint)(val * 32767.0f);
-}
-static inline ALshort Conv_ALshort_ALdouble(ALdouble val)
-{
-    if(val > 1.0) return 32767;
-    if(val < -1.0) return -32768;
-    return (ALint)(val * 32767.0);
-}
-static inline ALshort Conv_ALshort_ALmulaw(ALmulaw val)
-{ return Conv_ALshort_ALshort(DecodeMuLaw(val)); }
-static inline ALshort Conv_ALshort_ALalaw(ALalaw val)
-{ return Conv_ALshort_ALshort(DecodeALaw(val)); }
-static inline ALshort Conv_ALshort_ALbyte3(ALbyte3 val)
-{ return DecodeByte3(val)>>8; }
-static inline ALshort Conv_ALshort_ALubyte3(ALubyte3 val)
-{ return (DecodeUByte3(val)>>8)-32768; }
-
-static inline ALushort Conv_ALushort_ALbyte(ALbyte val)
-{ return (val+128)<<8; }
-static inline ALushort Conv_ALushort_ALubyte(ALubyte val)
-{ return val<<8; }
-static inline ALushort Conv_ALushort_ALshort(ALshort val)
-{ return val+32768; }
-static inline ALushort Conv_ALushort_ALushort(ALushort val)
-{ return val; }
-static inline ALushort Conv_ALushort_ALint(ALint val)
-{ return (val>>16)+32768; }
-static inline ALushort Conv_ALushort_ALuint(ALuint val)
-{ return val>>16; }
-static inline ALushort Conv_ALushort_ALfloat(ALfloat val)
-{
-    if(val > 1.0f) return 65535;
-    if(val < -1.0f) return 0;
-    return (ALint)(val * 32767.0f) + 32768;
-}
-static inline ALushort Conv_ALushort_ALdouble(ALdouble val)
-{
-    if(val > 1.0) return 65535;
-    if(val < -1.0) return 0;
-    return (ALint)(val * 32767.0) + 32768;
-}
-static inline ALushort Conv_ALushort_ALmulaw(ALmulaw val)
-{ return Conv_ALushort_ALshort(DecodeMuLaw(val)); }
-static inline ALushort Conv_ALushort_ALalaw(ALalaw val)
-{ return Conv_ALushort_ALshort(DecodeALaw(val)); }
-static inline ALushort Conv_ALushort_ALbyte3(ALbyte3 val)
-{ return (DecodeByte3(val)>>8)+32768; }
-static inline ALushort Conv_ALushort_ALubyte3(ALubyte3 val)
-{ return DecodeUByte3(val)>>8; }
-
-static inline ALint Conv_ALint_ALbyte(ALbyte val)
-{ return val<<24; }
-static inline ALint Conv_ALint_ALubyte(ALubyte val)
-{ return (val-128)<<24; }
-static inline ALint Conv_ALint_ALshort(ALshort val)
-{ return val<<16; }
-static inline ALint Conv_ALint_ALushort(ALushort val)
-{ return (val-32768)<<16; }
-static inline ALint Conv_ALint_ALint(ALint val)
-{ return val; }
-static inline ALint Conv_ALint_ALuint(ALuint val)
-{ return val-2147483648u; }
-static inline ALint Conv_ALint_ALfloat(ALfloat val)
-{
-    if(val > 1.0f) return 2147483647;
-    if(val < -1.0f) return -2147483647-1;
-    return (ALint)(val*16777215.0f) << 7;
-}
-static inline ALint Conv_ALint_ALdouble(ALdouble val)
-{
-    if(val > 1.0) return 2147483647;
-    if(val < -1.0) return -2147483647-1;
-    return (ALint)(val * 2147483647.0);
-}
-static inline ALint Conv_ALint_ALmulaw(ALmulaw val)
-{ return Conv_ALint_ALshort(DecodeMuLaw(val)); }
-static inline ALint Conv_ALint_ALalaw(ALalaw val)
-{ return Conv_ALint_ALshort(DecodeALaw(val)); }
-static inline ALint Conv_ALint_ALbyte3(ALbyte3 val)
-{ return DecodeByte3(val)<<8; }
-static inline ALint Conv_ALint_ALubyte3(ALubyte3 val)
-{ return (DecodeUByte3(val)-8388608)<<8; }
-
-static inline ALuint Conv_ALuint_ALbyte(ALbyte val)
-{ return (val+128)<<24; }
-static inline ALuint Conv_ALuint_ALubyte(ALubyte val)
-{ return val<<24; }
-static inline ALuint Conv_ALuint_ALshort(ALshort val)
-{ return (val+32768)<<16; }
-static inline ALuint Conv_ALuint_ALushort(ALushort val)
-{ return val<<16; }
-static inline ALuint Conv_ALuint_ALint(ALint val)
-{ return val+2147483648u; }
-static inline ALuint Conv_ALuint_ALuint(ALuint val)
-{ return val; }
-static inline ALuint Conv_ALuint_ALfloat(ALfloat val)
-{
-    if(val > 1.0f) return 4294967295u;
-    if(val < -1.0f) return 0;
-    return ((ALint)(val*16777215.0f)<<7) + 2147483648u;
-}
-static inline ALuint Conv_ALuint_ALdouble(ALdouble val)
-{
-    if(val > 1.0) return 4294967295u;
-    if(val < -1.0) return 0;
-    return (ALint)(val * 2147483647.0) + 2147483648u;
-}
-static inline ALuint Conv_ALuint_ALmulaw(ALmulaw val)
-{ return Conv_ALuint_ALshort(DecodeMuLaw(val)); }
-static inline ALuint Conv_ALuint_ALalaw(ALalaw val)
-{ return Conv_ALuint_ALshort(DecodeALaw(val)); }
-static inline ALuint Conv_ALuint_ALbyte3(ALbyte3 val)
-{ return (DecodeByte3(val)+8388608)<<8; }
-static inline ALuint Conv_ALuint_ALubyte3(ALubyte3 val)
-{ return DecodeUByte3(val)<<8; }
-
-static inline ALfloat Conv_ALfloat_ALbyte(ALbyte val)
-{ return val * (1.0f/127.0f); }
-static inline ALfloat Conv_ALfloat_ALubyte(ALubyte val)
-{ return (val-128) * (1.0f/127.0f); }
-static inline ALfloat Conv_ALfloat_ALshort(ALshort val)
-{ return val * (1.0f/32767.0f); }
-static inline ALfloat Conv_ALfloat_ALushort(ALushort val)
-{ return (val-32768) * (1.0f/32767.0f); }
-static inline ALfloat Conv_ALfloat_ALint(ALint val)
-{ return (ALfloat)(val>>7) * (1.0f/16777215.0f); }
-static inline ALfloat Conv_ALfloat_ALuint(ALuint val)
-{ return (ALfloat)((ALint)(val>>7)-16777216) * (1.0f/16777215.0f); }
+/* Slightly special handling for floats and doubles (converts NaN to 0, and
+ * allows float<->double pass-through).
+ */
 static inline ALfloat Conv_ALfloat_ALfloat(ALfloat val)
 { return (val==val) ? val : 0.0f; }
 static inline ALfloat Conv_ALfloat_ALdouble(ALdouble val)
 { return (val==val) ? (ALfloat)val : 0.0f; }
-static inline ALfloat Conv_ALfloat_ALmulaw(ALmulaw val)
-{ return Conv_ALfloat_ALshort(DecodeMuLaw(val)); }
-static inline ALfloat Conv_ALfloat_ALalaw(ALalaw val)
-{ return Conv_ALfloat_ALshort(DecodeALaw(val)); }
-static inline ALfloat Conv_ALfloat_ALbyte3(ALbyte3 val)
-{ return (ALfloat)(DecodeByte3(val) * (1.0/8388607.0)); }
-static inline ALfloat Conv_ALfloat_ALubyte3(ALubyte3 val)
-{ return (ALfloat)((DecodeUByte3(val)-8388608) * (1.0/8388607.0)); }
-
-static inline ALdouble Conv_ALdouble_ALbyte(ALbyte val)
-{ return val * (1.0/127.0); }
-static inline ALdouble Conv_ALdouble_ALubyte(ALubyte val)
-{ return (val-128) * (1.0/127.0); }
-static inline ALdouble Conv_ALdouble_ALshort(ALshort val)
-{ return val * (1.0/32767.0); }
-static inline ALdouble Conv_ALdouble_ALushort(ALushort val)
-{ return (val-32768) * (1.0/32767.0); }
-static inline ALdouble Conv_ALdouble_ALint(ALint val)
-{ return val * (1.0/2147483647.0); }
-static inline ALdouble Conv_ALdouble_ALuint(ALuint val)
-{ return (ALint)(val-2147483648u) * (1.0/2147483647.0); }
 static inline ALdouble Conv_ALdouble_ALfloat(ALfloat val)
-{ return (val==val) ? val : 0.0f; }
+{ return (val==val) ? (ALdouble)val : 0.0; }
 static inline ALdouble Conv_ALdouble_ALdouble(ALdouble val)
 { return (val==val) ? val : 0.0; }
-static inline ALdouble Conv_ALdouble_ALmulaw(ALmulaw val)
-{ return Conv_ALdouble_ALshort(DecodeMuLaw(val)); }
-static inline ALdouble Conv_ALdouble_ALalaw(ALalaw val)
-{ return Conv_ALdouble_ALshort(DecodeALaw(val)); }
-static inline ALdouble Conv_ALdouble_ALbyte3(ALbyte3 val)
-{ return DecodeByte3(val) * (1.0/8388607.0); }
-static inline ALdouble Conv_ALdouble_ALubyte3(ALubyte3 val)
-{ return (DecodeUByte3(val)-8388608) * (1.0/8388607.0); }
 
-#define DECL_TEMPLATE(T)                                                      \
-static inline ALmulaw Conv_ALmulaw_##T(T val)                                 \
-{ return EncodeMuLaw(Conv_ALshort_##T(val)); }
+#undef DECL_TEMPLATE
 
-DECL_TEMPLATE(ALbyte)
-DECL_TEMPLATE(ALubyte)
-DECL_TEMPLATE(ALshort)
-DECL_TEMPLATE(ALushort)
-DECL_TEMPLATE(ALint)
-DECL_TEMPLATE(ALuint)
-DECL_TEMPLATE(ALfloat)
-DECL_TEMPLATE(ALdouble)
-static inline ALmulaw Conv_ALmulaw_ALmulaw(ALmulaw val)
-{ return val; }
-DECL_TEMPLATE(ALalaw)
-DECL_TEMPLATE(ALbyte3)
-DECL_TEMPLATE(ALubyte3)
+/* Define alternate-sign functions. */
+#define DECL_TEMPLATE(T1, T2, O)                                  \
+static inline T1 Conv_##T1##_##T2(T2 val) { return (T1)val - O; } \
+static inline T2 Conv_##T2##_##T1(T1 val) { return (T2)val + O; }
+
+DECL_TEMPLATE(ALbyte, ALubyte, 128);
+DECL_TEMPLATE(ALshort, ALushort, 32768);
+DECL_TEMPLATE(ALint, ALuint, 2147483648u);
 
 #undef DECL_TEMPLATE
 
-#define DECL_TEMPLATE(T)                                                      \
-static inline ALalaw Conv_ALalaw_##T(T val)                                   \
-{ return EncodeALaw(Conv_ALshort_##T(val)); }
+/* Define int-type to int-type functions */
+#define DECL_TEMPLATE(T, ST, UT, SH)                                          \
+static inline T Conv_##T##_##ST(ST val){ return val >> SH; }                  \
+static inline T Conv_##T##_##UT(UT val){ return Conv_##ST##_##UT(val) >> SH; }\
+static inline ST Conv_##ST##_##T(T val){ return val << SH; }                  \
+static inline UT Conv_##UT##_##T(T val){ return Conv_##UT##_##ST(val << SH); }
 
-DECL_TEMPLATE(ALbyte)
-DECL_TEMPLATE(ALubyte)
-DECL_TEMPLATE(ALshort)
-DECL_TEMPLATE(ALushort)
-DECL_TEMPLATE(ALint)
-DECL_TEMPLATE(ALuint)
-DECL_TEMPLATE(ALfloat)
-DECL_TEMPLATE(ALdouble)
-DECL_TEMPLATE(ALmulaw)
-static inline ALalaw Conv_ALalaw_ALalaw(ALalaw val)
-{ return val; }
-DECL_TEMPLATE(ALbyte3)
-DECL_TEMPLATE(ALubyte3)
+#define DECL_TEMPLATE2(T1, T2, SH)          \
+DECL_TEMPLATE(AL##T1, AL##T2, ALu##T2, SH)  \
+DECL_TEMPLATE(ALu##T1, ALu##T2, AL##T2, SH)
+
+DECL_TEMPLATE2(byte,  short, 8)
+DECL_TEMPLATE2(short, int,   16)
+DECL_TEMPLATE2(byte,  int,   24)
 
+#undef DECL_TEMPLATE2
 #undef DECL_TEMPLATE
 
-#define DECL_TEMPLATE(T)                                                      \
-static inline ALbyte3 Conv_ALbyte3_##T(T val)                                 \
-{ return EncodeByte3(Conv_ALint_##T(val)>>8); }
+/* Define int-type to fp functions */
+#define DECL_TEMPLATE(T, ST, UT, OP)                                          \
+static inline T Conv_##T##_##ST(ST val) { return (T)val * OP; }               \
+static inline T Conv_##T##_##UT(UT val) { return (T)Conv_##ST##_##UT(val) * OP; }
 
-DECL_TEMPLATE(ALbyte)
-DECL_TEMPLATE(ALubyte)
-DECL_TEMPLATE(ALshort)
-DECL_TEMPLATE(ALushort)
-DECL_TEMPLATE(ALint)
-DECL_TEMPLATE(ALuint)
-DECL_TEMPLATE(ALfloat)
-DECL_TEMPLATE(ALdouble)
-DECL_TEMPLATE(ALmulaw)
-DECL_TEMPLATE(ALalaw)
-static inline ALbyte3 Conv_ALbyte3_ALbyte3(ALbyte3 val)
-{ return val; }
-DECL_TEMPLATE(ALubyte3)
+#define DECL_TEMPLATE2(T1, T2, OP)     \
+DECL_TEMPLATE(T1, AL##T2, ALu##T2, OP)
+
+DECL_TEMPLATE2(ALfloat, byte, (1.0f/128.0f))
+DECL_TEMPLATE2(ALdouble, byte, (1.0/128.0))
+DECL_TEMPLATE2(ALfloat, short, (1.0f/32768.0f))
+DECL_TEMPLATE2(ALdouble, short, (1.0/32768.0))
+DECL_TEMPLATE2(ALdouble, int, (1.0/2147483648.0))
+
+/* Special handling for int32 to float32, since it would overflow. */
+static inline ALfloat Conv_ALfloat_ALint(ALint val)
+{ return (ALfloat)(val>>7) * (1.0f/16777216.0f); }
+static inline ALfloat Conv_ALfloat_ALuint(ALuint val)
+{ return (ALfloat)(Conv_ALint_ALuint(val)>>7) * (1.0f/16777216.0f); }
 
+#undef DECL_TEMPLATE2
 #undef DECL_TEMPLATE
 
+/* Define fp to int-type functions */
+#define DECL_TEMPLATE(FT, T, smin, smax)        \
+static inline AL##T Conv_AL##T##_##FT(FT val)   \
+{                                               \
+    val *= (FT)smax + 1;                        \
+    if(val >= (FT)smax) return smax;            \
+    if(val <= (FT)smin) return smin;            \
+    return (AL##T)val;                          \
+}                                               \
+static inline ALu##T Conv_ALu##T##_##FT(FT val) \
+{ return Conv_ALu##T##_AL##T(Conv_AL##T##_##FT(val)); }
+
+DECL_TEMPLATE(ALfloat, byte, -128, 127)
+DECL_TEMPLATE(ALdouble, byte, -128, 127)
+DECL_TEMPLATE(ALfloat, short, -32768, 32767)
+DECL_TEMPLATE(ALdouble, short, -32768, 32767)
+DECL_TEMPLATE(ALdouble, int, -2147483647-1, 2147483647)
+
+/* Special handling for float32 to int32, since it would overflow. */
+static inline ALint Conv_ALint_ALfloat(ALfloat val)
+{
+    val *= 16777216.0f;
+    if(val >= 16777215.0f) return 0x7fffff80/*16777215 << 7*/;
+    if(val <= -16777216.0f) return 0x80000000/*-16777216 << 7*/;
+    return (ALint)val << 7;
+}
+static inline ALuint Conv_ALuint_ALfloat(ALfloat val)
+{ return Conv_ALuint_ALint(Conv_ALint_ALfloat(val)); }
+
+#undef DECL_TEMPLATE
+
+/* Define muLaw and aLaw functions (goes through short functions). */
 #define DECL_TEMPLATE(T)                                                      \
-static inline ALubyte3 Conv_ALubyte3_##T(T val)                               \
-{ return EncodeUByte3(Conv_ALuint_##T(val)>>8); }
+static inline ALmulaw Conv_ALmulaw_##T(T val)                                 \
+{ return EncodeMuLaw(Conv_ALshort_##T(val)); }                                \
+static inline T Conv_##T##_ALmulaw(ALmulaw val)                               \
+{ return Conv_##T##_ALshort(DecodeMuLaw(val)); }                              \
+                                                                              \
+static inline ALalaw Conv_ALalaw_##T(T val)                                   \
+{ return EncodeALaw(Conv_ALshort_##T(val)); }                                 \
+static inline T Conv_##T##_ALalaw(ALalaw val)                                 \
+{ return Conv_##T##_ALshort(DecodeALaw(val)); }
 
 DECL_TEMPLATE(ALbyte)
 DECL_TEMPLATE(ALubyte)
@@ -861,14 +621,15 @@ DECL_TEMPLATE(ALint)
 DECL_TEMPLATE(ALuint)
 DECL_TEMPLATE(ALfloat)
 DECL_TEMPLATE(ALdouble)
-DECL_TEMPLATE(ALmulaw)
-DECL_TEMPLATE(ALalaw)
-DECL_TEMPLATE(ALbyte3)
-static inline ALubyte3 Conv_ALubyte3_ALubyte3(ALubyte3 val)
-{ return val; }
 
 #undef DECL_TEMPLATE
 
+/* Define muLaw <-> aLaw functions. */
+static inline ALalaw Conv_ALalaw_ALmulaw(ALmulaw val)
+{ return EncodeALaw(DecodeMuLaw(val)); }
+static inline ALmulaw Conv_ALmulaw_ALalaw(ALalaw val)
+{ return EncodeMuLaw(DecodeALaw(val)); }
+
 
 #define DECL_TEMPLATE(T1, T2)                                                 \
 static void Convert_##T1##_##T2(T1 *dst, const T2 *src, ALuint numchans,      \
@@ -892,9 +653,7 @@ DECL_TEMPLATE(T, ALuint)   \
 DECL_TEMPLATE(T, ALfloat)  \
 DECL_TEMPLATE(T, ALdouble) \
 DECL_TEMPLATE(T, ALmulaw)  \
-DECL_TEMPLATE(T, ALalaw)   \
-DECL_TEMPLATE(T, ALbyte3)  \
-DECL_TEMPLATE(T, ALubyte3)
+DECL_TEMPLATE(T, ALalaw)
 
 DECL_TEMPLATE2(ALbyte)
 DECL_TEMPLATE2(ALubyte)
@@ -906,8 +665,6 @@ DECL_TEMPLATE2(ALfloat)
 DECL_TEMPLATE2(ALdouble)
 DECL_TEMPLATE2(ALmulaw)
 DECL_TEMPLATE2(ALalaw)
-DECL_TEMPLATE2(ALbyte3)
-DECL_TEMPLATE2(ALubyte3)
 
 #undef DECL_TEMPLATE2
 #undef DECL_TEMPLATE
@@ -957,8 +714,6 @@ DECL_TEMPLATE(ALfloat)
 DECL_TEMPLATE(ALdouble)
 DECL_TEMPLATE(ALmulaw)
 DECL_TEMPLATE(ALalaw)
-DECL_TEMPLATE(ALbyte3)
-DECL_TEMPLATE(ALubyte3)
 
 #undef DECL_TEMPLATE
 
@@ -1010,8 +765,6 @@ DECL_TEMPLATE(ALfloat)
 DECL_TEMPLATE(ALdouble)
 DECL_TEMPLATE(ALmulaw)
 DECL_TEMPLATE(ALalaw)
-DECL_TEMPLATE(ALbyte3)
-DECL_TEMPLATE(ALubyte3)
 
 #undef DECL_TEMPLATE
 
@@ -1063,8 +816,6 @@ DECL_TEMPLATE(ALfloat)
 DECL_TEMPLATE(ALdouble)
 DECL_TEMPLATE(ALmulaw)
 DECL_TEMPLATE(ALalaw)
-DECL_TEMPLATE(ALbyte3)
-DECL_TEMPLATE(ALubyte3)
 
 #undef DECL_TEMPLATE
 
@@ -1114,8 +865,6 @@ DECL_TEMPLATE(ALfloat)
 DECL_TEMPLATE(ALdouble)
 DECL_TEMPLATE(ALmulaw)
 DECL_TEMPLATE(ALalaw)
-DECL_TEMPLATE(ALbyte3)
-DECL_TEMPLATE(ALubyte3)
 
 #undef DECL_TEMPLATE
 
@@ -1192,12 +941,6 @@ static void Convert_##T(T *dst, const ALvoid *src, enum UserFmtType srcType,  \
         case UserFmtMSADPCM:                                                  \
             Convert_##T##_ALmsadpcm(dst, src, numchans, len, align);          \
             break;                                                            \
-        case UserFmtByte3:                                                    \
-            Convert_##T##_ALbyte3(dst, src, numchans, len, align);            \
-            break;                                                            \
-        case UserFmtUByte3:                                                   \
-            Convert_##T##_ALubyte3(dst, src, numchans, len, align);           \
-            break;                                                            \
     }                                                                         \
 }
 
@@ -1213,8 +956,6 @@ DECL_TEMPLATE(ALmulaw)
 DECL_TEMPLATE(ALalaw)
 DECL_TEMPLATE(ALima4)
 DECL_TEMPLATE(ALmsadpcm)
-DECL_TEMPLATE(ALbyte3)
-DECL_TEMPLATE(ALubyte3)
 
 #undef DECL_TEMPLATE
 
@@ -1259,11 +1000,5 @@ void ConvertData(ALvoid *dst, enum UserFmtType dstType, const ALvoid *src, enum
         case UserFmtMSADPCM:
             Convert_ALmsadpcm(dst, src, srcType, numchans, len, align);
             break;
-        case UserFmtByte3:
-            Convert_ALbyte3(dst, src, srcType, numchans, len, align);
-            break;
-        case UserFmtUByte3:
-            Convert_ALubyte3(dst, src, srcType, numchans, len, align);
-            break;
     }
 }

+ 125 - 19
libs/openal-soft/alsoftrc.sample

@@ -48,7 +48,10 @@
 ## channels:
 #  Sets the output channel configuration. If left unspecified, one will try to
 #  be detected from the system, and defaulting to stereo. The available values
-#  are: mono, stereo, quad, surround51, surround51rear, surround61, surround71
+#  are: mono, stereo, quad, surround51, surround51rear, surround61, surround71,
+#  ambi1, ambi2, ambi3. Note that the ambi* configurations provide ambisonic
+#  channels of the given order (using ACN ordering and SN3D normalization by
+#  default), which need to be decoded to play correctly on speakers.
 #channels =
 
 ## sample-type:
@@ -78,7 +81,7 @@
 #  which helps protect against skips when the CPU is under load, but increases
 #  the delay between a sound getting mixed and being heard. Acceptable values
 #  range between 2 and 16.
-#periods = 4
+#periods = 3
 
 ## stereo-mode:
 #  Specifies if stereo output is treated as being headphones or speakers. With
@@ -86,6 +89,20 @@
 #  Valid settings are auto, speakers, and headphones.
 #stereo-mode = auto
 
+## stereo-encoding:
+#  Specifies the encoding method for non-HRTF stereo output. 'panpot' (default)
+#  uses standard amplitude panning (aka pair-wise, stereo pair, etc) between
+#  -30 and +30 degrees, while 'uhj' creates stereo-compatible two-channel UHJ
+#  output, which encodes some surround sound information into stereo output
+#  that can be decoded with a surround sound receiver. If crossfeed filters are
+#  used, UHJ is disabled.
+#stereo-encoding = panpot
+
+## ambi-format:
+#  Specifies the channel order and normalization for the "ambi*" set of channel
+#  configurations. Valid settings are: fuma, acn+sn3d, acn+n3d
+#ambi-format = acn+sn3d
+
 ## hrtf:
 #  Controls HRTF processing. These filters provide better spatialization of
 #  sounds while using headphones, but do require a bit more CPU power. The
@@ -96,22 +113,24 @@
 #  respectively.
 #hrtf = auto
 
-## hrtf_tables:
-#  Specifies a comma-separated list of files containing HRTF data sets. The
-#  format of the files are described in hrtf.txt. The filenames may contain
-#  these markers, which will be replaced as needed:
-#  %r - Device sampling rate
-#  %s - Non-greedy string (up to the following matching characters)
-#  %% - Percent sign (%)
-#  The listed files are relative to system-dependant data directories. On
-#  Windows this is:
+## default-hrtf:
+#  Specifies the default HRTF to use. When multiple HRTFs are available, this
+#  determines the preferred one to use if none are specifically requested. Note
+#  that this is the enumerated HRTF name, not necessarily the filename.
+#default-hrtf =
+
+## hrtf-paths:
+#  Specifies a comma-separated list of paths containing HRTF data sets. The
+#  format of the files are described in docs/hrtf.txt. The files within the
+#  directories must have the .mhr file extension to be recognized. By default,
+#  OS-dependent data paths will be used. They will also be used if the list
+#  ends with a comma. On Windows this is:
 #  $AppData\openal\hrtf
 #  And on other systems, it's (in order):
 #  $XDG_DATA_HOME/openal/hrtf  (defaults to $HOME/.local/share/openal/hrtf)
 #  $XDG_DATA_DIRS/openal/hrtf  (defaults to /usr/local/share/openal/hrtf and
 #                               /usr/share/openal/hrtf)
-#  An absolute path may also be specified, if the given file is elsewhere.
-#hrtf_tables = %s.mhr
+#hrtf-paths =
 
 ## cf_level:
 #  Sets the crossfeed level for stereo output. Valid values are:
@@ -131,7 +150,6 @@
 #  point - nearest sample, no interpolation
 #  linear - extrapolates samples using a linear slope between samples
 #  sinc4 - extrapolates samples using a 4-point Sinc filter
-#  sinc8 - extrapolates samples using an 8-point Sinc filter
 #  bsinc - extrapolates samples using a band-limited Sinc filter (varying
 #          between 12 and 24 points, with anti-aliasing)
 #  Specifying other values will result in using the default (linear).
@@ -156,13 +174,38 @@
 #  can use a non-negligible amount of CPU time if an effect is set on it even
 #  if no sources are feeding it, so this may help when apps use more than the
 #  system can handle.
-#slots = 4
+#slots = 64
 
 ## sends:
-#  Sets the number of auxiliary sends per source. When not specified (default),
-#  it allows the app to request how many it wants. The maximum value currently
-#  possible is 4.
-#sends =
+#  Limits the number of auxiliary sends allowed per source. Setting this higher
+#  than the default has no effect.
+#sends = 16
+
+## output-limiter:
+#  Applies a gain limiter on the final mixed output. This reduces the volume
+#  when the output samples would otherwise clamp, avoiding excessive clipping
+#  noise.
+#output-limiter = true
+
+## dither:
+#  Applies dithering on the final mix, for 8- and 16-bit output by default.
+#  This replaces the distortion created by nearest-value quantization with low-
+#  level whitenoise.
+#dither = true
+
+## dither-depth:
+#  Quantization bit-depth for dithered output. A value of 0 (or less) will
+#  match the output sample depth. For int32, uint32, and float32 output, 0 will
+#  disable dithering because they're at or beyond the rendered precision. The
+#  maximum dither depth is 24.
+#dither-depth = 0
+
+## volume-adjust:
+#  A global volume adjustment for source output, expressed in decibels. The
+#  value is logarithmic, so +6 will be a scale of (approximately) 2x, +12 will
+#  be a scale of 4x, etc. Similarly, -6 will be x1/2, and -12 is about x1/4. A
+#  value of 0 means no change.
+#volume-adjust = 0
 
 ## excludefx: (global)
 #  Sets which effects to exclude, preventing apps from using them. This can
@@ -192,6 +235,69 @@
 #  of a context error. On Windows, a breakpoint exception is generated.
 #trap-al-error = false
 
+##
+## Ambisonic decoder stuff
+##
+[decoder]
+
+## hq-mode:
+#  Enables a high-quality ambisonic decoder. This mode is capable of frequency-
+#  dependent processing, creating a better reproduction of 3D sound rendering
+#  over surround sound speakers. Enabling this also requires specifying decoder
+#  configuration files for the appropriate speaker configuration you intend to
+#  use (see the quad, surround51, etc options below). Currently, up to third-
+#  order decoding is supported.
+hq-mode = false
+
+## distance-comp:
+#  Enables compensation for the speakers' relative distances to the listener.
+#  This applies the necessary delays and attenuation to make the speakers
+#  behave as though they are all equidistant, which is important for proper
+#  playback of 3D sound rendering. Requires the proper distances to be
+#  specified in the decoder configuration file.
+distance-comp = true
+
+## nfc:
+#  Enables near-field control filters. This simulates and compensates for low-
+#  frequency effects caused by the curvature of nearby sound-waves, which
+#  creates a more realistic perception of sound distance. Note that the effect
+#  may be stronger or weaker than intended if the application doesn't use or
+#  specify an appropriate unit scale, or if incorrect speaker distances are set
+#  in the decoder configuration file. Requires hq-mode to be enabled.
+nfc = true
+
+## nfc-ref-delay
+#  Specifies the reference delay value for ambisonic output. When channels is
+#  set to one of the ambi* formats, this option enables NFC-HOA output with the
+#  specified Reference Delay parameter. The specified value can then be shared
+#  with an appropriate NFC-HOA decoder to reproduce correct near-field effects.
+#  Keep in mind that despite being designed for higher-order ambisonics, this
+#  applies to first-order output all the same. When left unset, normal output
+#  is created with no near-field simulation.
+nfc-ref-delay =
+
+## quad:
+#  Decoder configuration file for Quadrophonic channel output. See
+#  docs/ambdec.txt for a description of the file format.
+quad =
+
+## surround51:
+#  Decoder configuration file for 5.1 Surround (Side and Rear) channel output.
+#  See docs/ambdec.txt for a description of the file format.
+surround51 =
+
+## surround61:
+#  Decoder configuration file for 6.1 Surround channel output. See
+#  docs/ambdec.txt for a description of the file format.
+surround61 =
+
+## surround71:
+#  Decoder configuration file for 7.1 Surround channel output. See
+#  docs/ambdec.txt for a description of the file format. Note: This can be used
+#  to enable 3D7.1 with the appropriate configuration and speaker placement,
+#  see docs/3D7.1.txt.
+surround71 =
+
 ##
 ## Reverb effect stuff (includes EAX reverb)
 ##

+ 19 - 0
libs/openal-soft/appveyor.yml

@@ -0,0 +1,19 @@
+version: 1.18.2.{build}
+
+environment:
+    matrix:
+      - GEN: "Visual Studio 14 2015"
+        CFG: Release
+      - GEN: "Visual Studio 14 2015 Win64"
+        CFG: Release
+
+install:
+    # Remove the VS Xamarin targets to reduce AppVeyor specific noise in build
+    # logs.  See also http://help.appveyor.com/discussions/problems/4569
+    - del "C:\Program Files (x86)\MSBuild\14.0\Microsoft.Common.targets\ImportAfter\Xamarin.Common.targets"
+
+build_script:
+    - cd build
+    - cmake -G"%GEN%" -DALSOFT_REQUIRE_WINMM=ON -DALSOFT_REQUIRE_DSOUND=ON -DALSOFT_REQUIRE_MMDEVAPI=ON -DALSOFT_EMBED_HRTF_DATA=YES ..
+    - cmake --build . --config %CFG% --clean-first
+

+ 2 - 2
libs/openal-soft/cmake/CheckSharedFunctionExists.cmake

@@ -34,7 +34,7 @@
 #  License text for the above reference.)
 
 MACRO(CHECK_SHARED_FUNCTION_EXISTS SYMBOL FILES LIBRARY LOCATION VARIABLE)
-  IF("${VARIABLE}" MATCHES "^${VARIABLE}$")
+  IF(NOT DEFINED "${VARIABLE}" OR "x${${VARIABLE}}" STREQUAL "x${VARIABLE}")
     SET(CMAKE_CONFIGURABLE_FILE_CONTENT "/* */\n")
     SET(MACRO_CHECK_SYMBOL_EXISTS_FLAGS ${CMAKE_REQUIRED_FLAGS})
     IF(CMAKE_REQUIRED_LIBRARIES)
@@ -88,5 +88,5 @@ MACRO(CHECK_SHARED_FUNCTION_EXISTS SYMBOL FILES LIBRARY LOCATION VARIABLE)
         "${OUTPUT}\nFile ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/CheckSymbolExists.c:\n"
         "${CMAKE_CONFIGURABLE_FILE_CONTENT}\n")
     ENDIF(${VARIABLE})
-  ENDIF("${VARIABLE}" MATCHES "^${VARIABLE}$")
+  ENDIF(NOT DEFINED "${VARIABLE}" OR "x${${VARIABLE}}" STREQUAL "x${VARIABLE}")
 ENDMACRO(CHECK_SHARED_FUNCTION_EXISTS)

+ 16 - 10
libs/openal-soft/cmake/FindDSound.cmake

@@ -8,24 +8,30 @@
 #   DSOUND_LIBRARY     - the dsound library
 #
 
+if (WIN32)
+  include(FindWindowsSDK)
+  if (WINDOWSSDK_FOUND)
+    get_windowssdk_library_dirs(${WINDOWSSDK_PREFERRED_DIR} WINSDK_LIB_DIRS)
+    get_windowssdk_include_dirs(${WINDOWSSDK_PREFERRED_DIR} WINSDK_INCLUDE_DIRS)
+  endif()
+endif()
+
+# DSOUND_INCLUDE_DIR
 find_path(DSOUND_INCLUDE_DIR
-          NAMES dsound.h
-          PATHS "${DXSDK_DIR}"
+          NAMES "dsound.h" 
+          PATHS "${DXSDK_DIR}" ${WINSDK_INCLUDE_DIRS} 
           PATH_SUFFIXES include
-          DOC "The DirectSound include directory"
-)
+          DOC "The DirectSound include directory")
 
+# DSOUND_LIBRARY
 find_library(DSOUND_LIBRARY
              NAMES dsound
-             PATHS "${DXSDK_DIR}"
+             PATHS "${DXSDK_DIR}" ${WINSDK_LIB_DIRS}
              PATH_SUFFIXES lib lib/x86 lib/x64
-             DOC "The DirectSound library"
-)
+             DOC "The DirectSound library")
 
 include(FindPackageHandleStandardArgs)
-find_package_handle_standard_args(DSound
-    REQUIRED_VARS DSOUND_LIBRARY DSOUND_INCLUDE_DIR
-)
+find_package_handle_standard_args(DSound REQUIRED_VARS DSOUND_LIBRARY DSOUND_INCLUDE_DIR)
 
 if(DSOUND_FOUND)
     set(DSOUND_LIBRARIES ${DSOUND_LIBRARY})

+ 6 - 0
libs/openal-soft/cmake/FindFFmpeg.cmake

@@ -142,6 +142,12 @@ foreach(_component ${FFmpeg_FIND_COMPONENTS})
     endif()
 endforeach()
 
+# Add libz if it exists (needed for static ffmpeg builds)
+find_library(_FFmpeg_HAVE_LIBZ NAMES z)
+if(_FFmpeg_HAVE_LIBZ)
+    set(FFMPEG_LIBRARIES ${FFMPEG_LIBRARIES} ${_FFmpeg_HAVE_LIBZ})
+endif()
+
 # Build the include path and library list with duplicates removed.
 if(FFMPEG_INCLUDE_DIRS)
     list(REMOVE_DUPLICATES FFMPEG_INCLUDE_DIRS)

+ 13 - 1
libs/openal-soft/cmake/FindOSS.cmake

@@ -2,8 +2,10 @@
 #
 #   OSS_FOUND        - True if OSS_INCLUDE_DIR is found
 #   OSS_INCLUDE_DIRS - Set when OSS_INCLUDE_DIR is found
+#   OSS_LIBRARIES    - Set when OSS_LIBRARY is found
 #
 #   OSS_INCLUDE_DIR - where to find sys/soundcard.h, etc.
+#   OSS_LIBRARY     - where to find libossaudio (optional).
 #
 
 find_path(OSS_INCLUDE_DIR
@@ -11,11 +13,21 @@ find_path(OSS_INCLUDE_DIR
           DOC "The OSS include directory"
 )
 
+find_library(OSS_LIBRARY
+             NAMES ossaudio
+             DOC "Optional OSS library"
+)
+
 include(FindPackageHandleStandardArgs)
 find_package_handle_standard_args(OSS  REQUIRED_VARS OSS_INCLUDE_DIR)
 
 if(OSS_FOUND)
     set(OSS_INCLUDE_DIRS ${OSS_INCLUDE_DIR})
+    if(OSS_LIBRARY)
+        set(OSS_LIBRARIES ${OSS_LIBRARY})
+    else()
+        unset(OSS_LIBRARIES)
+    endif()
 endif()
 
-mark_as_advanced(OSS_INCLUDE_DIR)
+mark_as_advanced(OSS_INCLUDE_DIR OSS_LIBRARY)

+ 626 - 0
libs/openal-soft/cmake/FindWindowsSDK.cmake

@@ -0,0 +1,626 @@
+# - Find the Windows SDK aka Platform SDK
+#
+# Relevant Wikipedia article: http://en.wikipedia.org/wiki/Microsoft_Windows_SDK
+#
+# Pass "COMPONENTS tools" to ignore Visual Studio version checks: in case
+# you just want the tool binaries to run, rather than the libraries and headers
+# for compiling.
+#
+# Variables:
+#  WINDOWSSDK_FOUND - if any version of the windows or platform SDK was found that is usable with the current version of visual studio
+#  WINDOWSSDK_LATEST_DIR
+#  WINDOWSSDK_LATEST_NAME
+#  WINDOWSSDK_FOUND_PREFERENCE - if we found an entry indicating a "preferred" SDK listed for this visual studio version
+#  WINDOWSSDK_PREFERRED_DIR
+#  WINDOWSSDK_PREFERRED_NAME
+#
+#  WINDOWSSDK_DIRS - contains no duplicates, ordered most recent first.
+#  WINDOWSSDK_PREFERRED_FIRST_DIRS - contains no duplicates, ordered with preferred first, followed by the rest in descending recency
+#
+# Functions:
+#  windowssdk_name_lookup(<directory> <output variable>) - Find the name corresponding with the SDK directory you pass in, or
+#     NOTFOUND if not recognized. Your directory must be one of WINDOWSSDK_DIRS for this to work.
+#
+#  windowssdk_build_lookup(<directory> <output variable>) - Find the build version number corresponding with the SDK directory you pass in, or
+#     NOTFOUND if not recognized. Your directory must be one of WINDOWSSDK_DIRS for this to work.
+#
+#  get_windowssdk_from_component(<file or dir> <output variable>) - Given a library or include dir,
+#     find the Windows SDK root dir corresponding to it, or NOTFOUND if unrecognized.
+#
+#  get_windowssdk_library_dirs(<directory> <output variable>) - Find the architecture-appropriate
+#     library directories corresponding to the SDK directory you pass in (or NOTFOUND if none)
+#
+#  get_windowssdk_library_dirs_multiple(<output variable> <directory> ...) - Find the architecture-appropriate
+#     library directories corresponding to the SDK directories you pass in, in order, skipping those not found. NOTFOUND if none at all.
+#     Good for passing WINDOWSSDK_DIRS or WINDOWSSDK_DIRS to if you really just want a file and don't care where from.
+#
+#  get_windowssdk_include_dirs(<directory> <output variable>) - Find the
+#     include directories corresponding to the SDK directory you pass in (or NOTFOUND if none)
+#
+#  get_windowssdk_include_dirs_multiple(<output variable> <directory> ...) - Find the
+#     include directories corresponding to the SDK directories you pass in, in order, skipping those not found. NOTFOUND if none at all.
+#     Good for passing WINDOWSSDK_DIRS or WINDOWSSDK_DIRS to if you really just want a file and don't care where from.
+#
+# Requires these CMake modules:
+#  FindPackageHandleStandardArgs (known included with CMake >=2.6.2)
+#
+# Original Author:
+# 2012 Ryan Pavlik <[email protected]> <[email protected]>
+# http://academic.cleardefinition.com
+# Iowa State University HCI Graduate Program/VRAC
+#
+# Copyright Iowa State University 2012.
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE_1_0.txt or copy at
+# http://www.boost.org/LICENSE_1_0.txt)
+
+set(_preferred_sdk_dirs) # pre-output
+set(_win_sdk_dirs) # pre-output
+set(_win_sdk_versanddirs) # pre-output
+set(_win_sdk_buildsanddirs) # pre-output
+set(_winsdk_vistaonly) # search parameters
+set(_winsdk_kits) # search parameters
+
+
+set(_WINDOWSSDK_ANNOUNCE OFF)
+if(NOT WINDOWSSDK_FOUND AND (NOT WindowsSDK_FIND_QUIETLY))
+	set(_WINDOWSSDK_ANNOUNCE ON)
+endif()
+macro(_winsdk_announce)
+	if(_WINSDK_ANNOUNCE)
+		message(STATUS ${ARGN})
+	endif()
+endmacro()
+
+set(_winsdk_win10vers
+	10.0.14393.0 # Redstone aka Win10 1607 "Anniversary Update"
+	10.0.10586.0 # TH2 aka Win10 1511
+	10.0.10240.0 # Win10 RTM
+	10.0.10150.0 # just ucrt
+	10.0.10056.0
+)
+
+if(WindowsSDK_FIND_COMPONENTS MATCHES "tools")
+	set(_WINDOWSSDK_IGNOREMSVC ON)
+	_winsdk_announce("Checking for tools from Windows/Platform SDKs...")
+else()
+	set(_WINDOWSSDK_IGNOREMSVC OFF)
+	_winsdk_announce("Checking for Windows/Platform SDKs...")
+endif()
+
+# Appends to the three main pre-output lists used only if the path exists
+# and is not already in the list.
+function(_winsdk_conditional_append _vername _build _path)
+	if(("${_path}" MATCHES "registry") OR (NOT EXISTS "${_path}"))
+		# Path invalid - do not add
+		return()
+	endif()
+	list(FIND _win_sdk_dirs "${_path}" _win_sdk_idx)
+	if(_win_sdk_idx GREATER -1)
+		# Path already in list - do not add
+		return()
+	endif()
+	_winsdk_announce( " - ${_vername}, Build ${_build} @ ${_path}")
+	# Not yet in the list, so we'll add it
+	list(APPEND _win_sdk_dirs "${_path}")
+	set(_win_sdk_dirs "${_win_sdk_dirs}" CACHE INTERNAL "" FORCE)
+	list(APPEND
+		_win_sdk_versanddirs
+		"${_vername}"
+		"${_path}")
+	set(_win_sdk_versanddirs "${_win_sdk_versanddirs}" CACHE INTERNAL "" FORCE)
+	list(APPEND
+		_win_sdk_buildsanddirs
+		"${_build}"
+		"${_path}")
+	set(_win_sdk_buildsanddirs "${_win_sdk_buildsanddirs}" CACHE INTERNAL "" FORCE)
+endfunction()
+
+# Appends to the "preferred SDK" lists only if the path exists
+function(_winsdk_conditional_append_preferred _info _path)
+	if(("${_path}" MATCHES "registry") OR (NOT EXISTS "${_path}"))
+		# Path invalid - do not add
+		return()
+	endif()
+
+	get_filename_component(_path "${_path}" ABSOLUTE)
+
+	list(FIND _win_sdk_preferred_sdk_dirs "${_path}" _win_sdk_idx)
+	if(_win_sdk_idx GREATER -1)
+		# Path already in list - do not add
+		return()
+	endif()
+	_winsdk_announce( " - Found \"preferred\" SDK ${_info} @ ${_path}")
+	# Not yet in the list, so we'll add it
+	list(APPEND _win_sdk_preferred_sdk_dirs "${_path}")
+	set(_win_sdk_preferred_sdk_dirs "${_win_sdk_dirs}" CACHE INTERNAL "" FORCE)
+
+	# Just in case we somehow missed it:
+	_winsdk_conditional_append("${_info}" "" "${_path}")
+endfunction()
+
+# Given a version like v7.0A, looks for an SDK in the registry under "Microsoft SDKs".
+# If the given version might be in both HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows
+# and HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots aka "Windows Kits",
+# use this macro first, since these registry keys usually have more information.
+#
+# Pass a "default" build number as an extra argument in case we can't find it.
+function(_winsdk_check_microsoft_sdks_registry _winsdkver)
+	set(SDKKEY "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows\\${_winsdkver}")
+	get_filename_component(_sdkdir
+		"[${SDKKEY};InstallationFolder]"
+		ABSOLUTE)
+
+	set(_sdkname "Windows SDK ${_winsdkver}")
+
+	# Default build number passed as extra argument
+	set(_build ${ARGN})
+	# See if the registry holds a Microsoft-mutilated, err, designated, product name
+	# (just using get_filename_component to execute the registry lookup)
+	get_filename_component(_sdkproductname
+		"[${SDKKEY};ProductName]"
+		NAME)
+	if(NOT "${_sdkproductname}" MATCHES "registry")
+		# Got a product name
+		set(_sdkname "${_sdkname} (${_sdkproductname})")
+	endif()
+
+	# try for a version to augment our name
+	# (just using get_filename_component to execute the registry lookup)
+	get_filename_component(_sdkver
+		"[${SDKKEY};ProductVersion]"
+		NAME)
+	if(NOT "${_sdkver}" MATCHES "registry" AND NOT MATCHES)
+		# Got a version
+		if(NOT "${_sdkver}" MATCHES "\\.\\.")
+			# and it's not an invalid one with two dots in it:
+			# use to override the default build
+			set(_build ${_sdkver})
+			if(NOT "${_sdkname}" MATCHES "${_sdkver}")
+				# Got a version that's not already in the name, let's use it to improve our name.
+				set(_sdkname "${_sdkname} (${_sdkver})")
+			endif()
+		endif()
+	endif()
+	_winsdk_conditional_append("${_sdkname}" "${_build}" "${_sdkdir}")
+endfunction()
+
+# Given a name for identification purposes, the build number, and a key (technically a "value name")
+# corresponding to a Windows SDK packaged as a "Windows Kit", look for it
+# in HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots
+# Note that the key or "value name" tends to be something weird like KitsRoot81 -
+# no easy way to predict, just have to observe them in the wild.
+# Doesn't hurt to also try _winsdk_check_microsoft_sdks_registry for these:
+# sometimes you get keys in both parts of the registry (in the wow64 portion especially),
+# and the non-"Windows Kits" location is often more descriptive.
+function(_winsdk_check_windows_kits_registry _winkit_name _winkit_build _winkit_key)
+	get_filename_component(_sdkdir
+		"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots;${_winkit_key}]"
+		ABSOLUTE)
+	_winsdk_conditional_append("${_winkit_name}" "${_winkit_build}" "${_sdkdir}")
+endfunction()
+
+# Given a name for identification purposes and the build number
+# corresponding to a Windows 10 SDK packaged as a "Windows Kit", look for it
+# in HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots
+# Doesn't hurt to also try _winsdk_check_microsoft_sdks_registry for these:
+# sometimes you get keys in both parts of the registry (in the wow64 portion especially),
+# and the non-"Windows Kits" location is often more descriptive.
+function(_winsdk_check_win10_kits _winkit_build)
+	get_filename_component(_sdkdir
+		"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots;KitsRoot10]"
+		ABSOLUTE)
+	if(("${_sdkdir}" MATCHES "registry") OR (NOT EXISTS "${_sdkdir}"))
+		return() # not found
+	endif()
+	if(EXISTS "${_sdkdir}/Include/${_winkit_build}/um")
+		_winsdk_conditional_append("Windows Kits 10 (Build ${_winkit_build})" "${_winkit_build}" "${_sdkdir}")
+	endif()
+endfunction()
+
+# Given a name for indentification purposes, the build number, and the associated package GUID,
+# look in the registry under both HKLM and HKCU in \\SOFTWARE\\Microsoft\\MicrosoftSDK\\InstalledSDKs\\
+# for that guid and the SDK it points to.
+function(_winsdk_check_platformsdk_registry _platformsdkname _build _platformsdkguid)
+	foreach(_winsdk_hive HKEY_LOCAL_MACHINE HKEY_CURRENT_USER)
+		get_filename_component(_sdkdir
+			"[${_winsdk_hive}\\SOFTWARE\\Microsoft\\MicrosoftSDK\\InstalledSDKs\\${_platformsdkguid};Install Dir]"
+			ABSOLUTE)
+		_winsdk_conditional_append("${_platformsdkname} (${_build})" "${_build}" "${_sdkdir}")
+	endforeach()
+endfunction()
+
+###
+# Detect toolchain information: to know whether it's OK to use Vista+ only SDKs
+###
+set(_winsdk_vistaonly_ok OFF)
+if(MSVC AND NOT _WINDOWSSDK_IGNOREMSVC)
+	# VC 10 and older has broad target support
+	if(MSVC_VERSION LESS 1700)
+		# VC 11 by default targets Vista and later only, so we can add a few more SDKs that (might?) only work on vista+
+	elseif("${CMAKE_VS_PLATFORM_TOOLSET}" MATCHES "_xp")
+		# This is the XP-compatible v110+ toolset
+	elseif("${CMAKE_VS_PLATFORM_TOOLSET}" STREQUAL "v100" OR "${CMAKE_VS_PLATFORM_TOOLSET}" STREQUAL "v90")
+		# This is the VS2010/VS2008 toolset
+	else()
+		# OK, we're VC11 or newer and not using a backlevel or XP-compatible toolset.
+		# These versions have no XP (and possibly Vista pre-SP1) support
+		set(_winsdk_vistaonly_ok ON)
+		if(_WINDOWSSDK_ANNOUNCE AND NOT _WINDOWSSDK_VISTAONLY_PESTERED)
+			set(_WINDOWSSDK_VISTAONLY_PESTERED ON CACHE INTERNAL "" FORCE)
+			message(STATUS "FindWindowsSDK: Detected Visual Studio 2012 or newer, not using the _xp toolset variant: including SDK versions that drop XP support in search!")
+		endif()
+	endif()
+endif()
+if(_WINDOWSSDK_IGNOREMSVC)
+	set(_winsdk_vistaonly_ok ON)
+endif()
+
+###
+# MSVC version checks - keeps messy conditionals in one place
+# (messy because of _WINDOWSSDK_IGNOREMSVC)
+###
+set(_winsdk_msvc_greater_1200 OFF)
+if(_WINDOWSSDK_IGNOREMSVC OR (MSVC AND (MSVC_VERSION GREATER 1200)))
+	set(_winsdk_msvc_greater_1200 ON)
+endif()
+# Newer than VS .NET/VS Toolkit 2003
+set(_winsdk_msvc_greater_1310 OFF)
+if(_WINDOWSSDK_IGNOREMSVC OR (MSVC AND (MSVC_VERSION GREATER 1310)))
+	set(_winsdk_msvc_greater_1310 ON)
+endif()
+
+# VS2005/2008
+set(_winsdk_msvc_less_1600 OFF)
+if(_WINDOWSSDK_IGNOREMSVC OR (MSVC AND (MSVC_VERSION LESS 1600)))
+	set(_winsdk_msvc_less_1600 ON)
+endif()
+
+# VS2013+
+set(_winsdk_msvc_not_less_1800 OFF)
+if(_WINDOWSSDK_IGNOREMSVC OR (MSVC AND (NOT MSVC_VERSION LESS 1800)))
+	set(_winsdk_msvc_not_less_1800 ON)
+endif()
+
+###
+# START body of find module
+###
+if(_winsdk_msvc_greater_1310) # Newer than VS .NET/VS Toolkit 2003
+	###
+	# Look for "preferred" SDKs
+	###
+
+	# Environment variable for SDK dir
+	if(EXISTS "$ENV{WindowsSDKDir}" AND (NOT "$ENV{WindowsSDKDir}" STREQUAL ""))
+		_winsdk_conditional_append_preferred("WindowsSDKDir environment variable" "$ENV{WindowsSDKDir}")
+	endif()
+
+	if(_winsdk_msvc_less_1600)
+		# Per-user current Windows SDK for VS2005/2008
+		get_filename_component(_sdkdir
+			"[HKEY_CURRENT_USER\\Software\\Microsoft\\Microsoft SDKs\\Windows;CurrentInstallFolder]"
+			ABSOLUTE)
+		_winsdk_conditional_append_preferred("Per-user current Windows SDK" "${_sdkdir}")
+
+		# System-wide current Windows SDK for VS2005/2008
+		get_filename_component(_sdkdir
+			"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows;CurrentInstallFolder]"
+			ABSOLUTE)
+		_winsdk_conditional_append_preferred("System-wide current Windows SDK" "${_sdkdir}")
+	endif()
+
+	###
+	# Begin the massive list of SDK searching!
+	###
+	if(_winsdk_vistaonly_ok AND _winsdk_msvc_not_less_1800)
+		# These require at least Visual Studio 2013 (VC12)
+
+		_winsdk_check_microsoft_sdks_registry(v10.0A)
+
+		# Windows Software Development Kit (SDK) for Windows 10
+		# Several different versions living in the same directory - if nothing else we can assume RTM (10240)
+		_winsdk_check_microsoft_sdks_registry(v10.0 10.0.10240.0)
+		foreach(_win10build ${_winsdk_win10vers})
+			_winsdk_check_win10_kits(${_win10build})
+		endforeach()
+	endif() # vista-only and 2013+
+
+	# Included in Visual Studio 2013
+	# Includes the v120_xp toolset
+	_winsdk_check_microsoft_sdks_registry(v8.1A 8.1.51636)
+
+	if(_winsdk_vistaonly_ok AND _winsdk_msvc_not_less_1800)
+		# Windows Software Development Kit (SDK) for Windows 8.1
+		# http://msdn.microsoft.com/en-gb/windows/desktop/bg162891
+		_winsdk_check_microsoft_sdks_registry(v8.1 8.1.25984.0)
+		_winsdk_check_windows_kits_registry("Windows Kits 8.1" 8.1.25984.0 KitsRoot81)
+	endif() # vista-only and 2013+
+
+	if(_winsdk_vistaonly_ok)
+		# Included in Visual Studio 2012
+		_winsdk_check_microsoft_sdks_registry(v8.0A 8.0.50727)
+
+		# Microsoft Windows SDK for Windows 8 and .NET Framework 4.5
+		# This is the first version to also include the DirectX SDK
+		# http://msdn.microsoft.com/en-US/windows/desktop/hh852363.aspx
+		_winsdk_check_microsoft_sdks_registry(v8.0 6.2.9200.16384)
+		_winsdk_check_windows_kits_registry("Windows Kits 8.0" 6.2.9200.16384 KitsRoot)
+	endif() # vista-only
+
+	# Included with VS 2012 Update 1 or later
+	# Introduces v110_xp toolset
+	_winsdk_check_microsoft_sdks_registry(v7.1A 7.1.51106)
+	if(_winsdk_vistaonly_ok)
+		# Microsoft Windows SDK for Windows 7 and .NET Framework 4
+		# http://www.microsoft.com/downloads/en/details.aspx?FamilyID=6b6c21d2-2006-4afa-9702-529fa782d63b
+		_winsdk_check_microsoft_sdks_registry(v7.1 7.1.7600.0.30514)
+	endif() # vista-only
+
+	# Included with VS 2010
+	_winsdk_check_microsoft_sdks_registry(v7.0A 6.1.7600.16385)
+
+	# Windows SDK for Windows 7 and .NET Framework 3.5 SP1
+	# Works with VC9
+	# http://www.microsoft.com/en-us/download/details.aspx?id=18950
+	_winsdk_check_microsoft_sdks_registry(v7.0 6.1.7600.16385)
+
+	# Two versions call themselves "v6.1":
+	# Older:
+	# Windows Vista Update & .NET 3.0 SDK
+	# http://www.microsoft.com/en-us/download/details.aspx?id=14477
+
+	# Newer:
+	# Windows Server 2008 & .NET 3.5 SDK
+	# may have broken VS9SP1? they recommend v7.0 instead, or a KB...
+	# http://www.microsoft.com/en-us/download/details.aspx?id=24826
+	_winsdk_check_microsoft_sdks_registry(v6.1 6.1.6000.16384.10)
+
+	# Included in VS 2008
+	_winsdk_check_microsoft_sdks_registry(v6.0A 6.1.6723.1)
+
+	# Microsoft Windows Software Development Kit for Windows Vista and .NET Framework 3.0 Runtime Components
+	# http://blogs.msdn.com/b/stanley/archive/2006/11/08/microsoft-windows-software-development-kit-for-windows-vista-and-net-framework-3-0-runtime-components.aspx
+	_winsdk_check_microsoft_sdks_registry(v6.0 6.0.6000.16384)
+endif()
+
+# Let's not forget the Platform SDKs, which sometimes are useful!
+if(_winsdk_msvc_greater_1200)
+	_winsdk_check_platformsdk_registry("Microsoft Platform SDK for Windows Server 2003 R2" "5.2.3790.2075.51" "D2FF9F89-8AA2-4373-8A31-C838BF4DBBE1")
+	_winsdk_check_platformsdk_registry("Microsoft Platform SDK for Windows Server 2003 SP1" "5.2.3790.1830.15" "8F9E5EF3-A9A5-491B-A889-C58EFFECE8B3")
+endif()
+###
+# Finally, look for "preferred" SDKs
+###
+if(_winsdk_msvc_greater_1310) # Newer than VS .NET/VS Toolkit 2003
+
+
+	# Environment variable for SDK dir
+	if(EXISTS "$ENV{WindowsSDKDir}" AND (NOT "$ENV{WindowsSDKDir}" STREQUAL ""))
+		_winsdk_conditional_append_preferred("WindowsSDKDir environment variable" "$ENV{WindowsSDKDir}")
+	endif()
+
+	if(_winsdk_msvc_less_1600)
+		# Per-user current Windows SDK for VS2005/2008
+		get_filename_component(_sdkdir
+			"[HKEY_CURRENT_USER\\Software\\Microsoft\\Microsoft SDKs\\Windows;CurrentInstallFolder]"
+			ABSOLUTE)
+		_winsdk_conditional_append_preferred("Per-user current Windows SDK" "${_sdkdir}")
+
+		# System-wide current Windows SDK for VS2005/2008
+		get_filename_component(_sdkdir
+			"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows;CurrentInstallFolder]"
+			ABSOLUTE)
+		_winsdk_conditional_append_preferred("System-wide current Windows SDK" "${_sdkdir}")
+	endif()
+endif()
+
+
+function(windowssdk_name_lookup _dir _outvar)
+	list(FIND _win_sdk_versanddirs "${_dir}" _diridx)
+	math(EXPR _idx "${_diridx} - 1")
+	if(${_idx} GREATER -1)
+		list(GET _win_sdk_versanddirs ${_idx} _ret)
+	else()
+		set(_ret "NOTFOUND")
+	endif()
+	set(${_outvar} "${_ret}" PARENT_SCOPE)
+endfunction()
+
+function(windowssdk_build_lookup _dir _outvar)
+	list(FIND _win_sdk_buildsanddirs "${_dir}" _diridx)
+	math(EXPR _idx "${_diridx} - 1")
+	if(${_idx} GREATER -1)
+		list(GET _win_sdk_buildsanddirs ${_idx} _ret)
+	else()
+		set(_ret "NOTFOUND")
+	endif()
+	set(${_outvar} "${_ret}" PARENT_SCOPE)
+endfunction()
+
+# If we found something...
+if(_win_sdk_dirs)
+	list(GET _win_sdk_dirs 0 WINDOWSSDK_LATEST_DIR)
+	windowssdk_name_lookup("${WINDOWSSDK_LATEST_DIR}"
+		WINDOWSSDK_LATEST_NAME)
+	set(WINDOWSSDK_DIRS ${_win_sdk_dirs})
+
+	# Fallback, in case no preference found.
+	set(WINDOWSSDK_PREFERRED_DIR "${WINDOWSSDK_LATEST_DIR}")
+	set(WINDOWSSDK_PREFERRED_NAME "${WINDOWSSDK_LATEST_NAME}")
+	set(WINDOWSSDK_PREFERRED_FIRST_DIRS ${WINDOWSSDK_DIRS})
+	set(WINDOWSSDK_FOUND_PREFERENCE OFF)
+endif()
+
+# If we found indications of a user preference...
+if(_win_sdk_preferred_sdk_dirs)
+	list(GET _win_sdk_preferred_sdk_dirs 0 WINDOWSSDK_PREFERRED_DIR)
+	windowssdk_name_lookup("${WINDOWSSDK_PREFERRED_DIR}"
+		WINDOWSSDK_PREFERRED_NAME)
+	set(WINDOWSSDK_PREFERRED_FIRST_DIRS
+		${_win_sdk_preferred_sdk_dirs}
+		${_win_sdk_dirs})
+	list(REMOVE_DUPLICATES WINDOWSSDK_PREFERRED_FIRST_DIRS)
+	set(WINDOWSSDK_FOUND_PREFERENCE ON)
+endif()
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(WindowsSDK
+	"No compatible version of the Windows SDK or Platform SDK found."
+	WINDOWSSDK_DIRS)
+
+if(WINDOWSSDK_FOUND)
+	# Internal: Architecture-appropriate library directory names.
+	if("${CMAKE_VS_PLATFORM_NAME}" STREQUAL "ARM")
+		if(CMAKE_SIZEOF_VOID_P MATCHES "8")
+			# Only supported in Win10 SDK and up.
+			set(_winsdk_arch8 arm64) # what the WDK for Win8+ calls this architecture
+		else()
+			set(_winsdk_archbare /arm) # what the architecture used to be called in oldest SDKs
+			set(_winsdk_arch arm) # what the architecture used to be called
+			set(_winsdk_arch8 arm) # what the WDK for Win8+ calls this architecture
+		endif()
+	else()
+		if(CMAKE_SIZEOF_VOID_P MATCHES "8")
+			set(_winsdk_archbare /x64) # what the architecture used to be called in oldest SDKs
+			set(_winsdk_arch amd64) # what the architecture used to be called
+			set(_winsdk_arch8 x64) # what the WDK for Win8+ calls this architecture
+		else()
+			set(_winsdk_archbare ) # what the architecture used to be called in oldest SDKs
+			set(_winsdk_arch i386) # what the architecture used to be called
+			set(_winsdk_arch8 x86) # what the WDK for Win8+ calls this architecture
+		endif()
+	endif()
+
+	function(get_windowssdk_from_component _component _var)
+		get_filename_component(_component "${_component}" ABSOLUTE)
+		file(TO_CMAKE_PATH "${_component}" _component)
+		foreach(_sdkdir ${WINDOWSSDK_DIRS})
+			get_filename_component(_sdkdir "${_sdkdir}" ABSOLUTE)
+			string(LENGTH "${_sdkdir}" _sdklen)
+			file(RELATIVE_PATH _rel "${_sdkdir}" "${_component}")
+			# If we don't have any "parent directory" items...
+			if(NOT "${_rel}" MATCHES "[.][.]")
+				set(${_var} "${_sdkdir}" PARENT_SCOPE)
+				return()
+			endif()
+		endforeach()
+		# Fail.
+		set(${_var} "NOTFOUND" PARENT_SCOPE)
+	endfunction()
+	function(get_windowssdk_library_dirs _winsdk_dir _var)
+		set(_dirs)
+		set(_suffixes
+			"lib${_winsdk_archbare}" # SDKs like 7.1A
+			"lib/${_winsdk_arch}" # just because some SDKs have x86 dir and root dir
+			"lib/w2k/${_winsdk_arch}" # Win2k min requirement
+			"lib/wxp/${_winsdk_arch}" # WinXP min requirement
+			"lib/wnet/${_winsdk_arch}" # Win Server 2003 min requirement
+			"lib/wlh/${_winsdk_arch}"
+			"lib/wlh/um/${_winsdk_arch8}" # Win Vista ("Long Horn") min requirement
+			"lib/win7/${_winsdk_arch}"
+			"lib/win7/um/${_winsdk_arch8}" # Win 7 min requirement
+		)
+		foreach(_ver
+			wlh # Win Vista ("Long Horn") min requirement
+			win7 # Win 7 min requirement
+			win8 # Win 8 min requirement
+			winv6.3 # Win 8.1 min requirement
+		)
+
+			list(APPEND _suffixes
+				"lib/${_ver}/${_winsdk_arch}"
+				"lib/${_ver}/um/${_winsdk_arch8}"
+				"lib/${_ver}/km/${_winsdk_arch8}"
+			)
+		endforeach()
+
+		# Look for WDF libraries in Win10+ SDK
+		foreach(_mode umdf kmdf)
+			file(GLOB _wdfdirs RELATIVE "${_winsdk_dir}" "${_winsdk_dir}/lib/wdf/${_mode}/${_winsdk_arch8}/*")
+			if(_wdfdirs)
+				list(APPEND _suffixes ${_wdfdirs})
+			endif()
+		endforeach()
+
+		# Look in each Win10+ SDK version for the components
+		foreach(_win10ver ${_winsdk_win10vers})
+			foreach(_component um km ucrt mmos)
+				list(APPEND _suffixes "lib/${_win10ver}/${_component}/${_winsdk_arch8}")
+			endforeach()
+		endforeach()
+
+		foreach(_suffix ${_suffixes})
+			# Check to see if a library actually exists here.
+			file(GLOB _libs "${_winsdk_dir}/${_suffix}/*.lib")
+			if(_libs)
+				list(APPEND _dirs "${_winsdk_dir}/${_suffix}")
+			endif()
+		endforeach()
+		if("${_dirs}" STREQUAL "")
+			set(_dirs NOTFOUND)
+		else()
+			list(REMOVE_DUPLICATES _dirs)
+		endif()
+		set(${_var} ${_dirs} PARENT_SCOPE)
+	endfunction()
+	function(get_windowssdk_include_dirs _winsdk_dir _var)
+		set(_dirs)
+
+		set(_subdirs shared um winrt km wdf mmos ucrt)
+		set(_suffixes Include)
+
+		foreach(_dir ${_subdirs})
+			list(APPEND _suffixes "Include/${_dir}")
+		endforeach()
+
+		foreach(_ver ${_winsdk_win10vers})
+			foreach(_dir ${_subdirs})
+				list(APPEND _suffixes "Include/${_ver}/${_dir}")
+			endforeach()
+		endforeach()
+
+		foreach(_suffix ${_suffixes})
+			# Check to see if a header file actually exists here.
+			file(GLOB _headers "${_winsdk_dir}/${_suffix}/*.h")
+			if(_headers)
+				list(APPEND _dirs "${_winsdk_dir}/${_suffix}")
+			endif()
+		endforeach()
+		if("${_dirs}" STREQUAL "")
+			set(_dirs NOTFOUND)
+		else()
+			list(REMOVE_DUPLICATES _dirs)
+		endif()
+		set(${_var} ${_dirs} PARENT_SCOPE)
+	endfunction()
+	function(get_windowssdk_library_dirs_multiple _var)
+		set(_dirs)
+		foreach(_sdkdir ${ARGN})
+			get_windowssdk_library_dirs("${_sdkdir}" _current_sdk_libdirs)
+			if(_current_sdk_libdirs)
+				list(APPEND _dirs ${_current_sdk_libdirs})
+			endif()
+		endforeach()
+		if("${_dirs}" STREQUAL "")
+			set(_dirs NOTFOUND)
+		else()
+			list(REMOVE_DUPLICATES _dirs)
+		endif()
+		set(${_var} ${_dirs} PARENT_SCOPE)
+	endfunction()
+	function(get_windowssdk_include_dirs_multiple _var)
+		set(_dirs)
+		foreach(_sdkdir ${ARGN})
+			get_windowssdk_include_dirs("${_sdkdir}" _current_sdk_incdirs)
+			if(_current_sdk_libdirs)
+				list(APPEND _dirs ${_current_sdk_incdirs})
+			endif()
+		endforeach()
+		if("${_dirs}" STREQUAL "")
+			set(_dirs NOTFOUND)
+		else()
+			list(REMOVE_DUPLICATES _dirs)
+		endif()
+		set(${_var} ${_dirs} PARENT_SCOPE)
+	endfunction()
+endif()

+ 0 - 0
libs/openal-soft/include/align.h → libs/openal-soft/common/align.h


+ 62 - 0
libs/openal-soft/common/almalloc.c

@@ -0,0 +1,62 @@
+
+#include "config.h"
+
+#include "almalloc.h"
+
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_MALLOC_H
+#include <malloc.h>
+#endif
+#ifdef HAVE_WINDOWS_H
+#include <windows.h>
+#endif
+
+void *al_malloc(size_t alignment, size_t size)
+{
+#if defined(HAVE_ALIGNED_ALLOC)
+    size = (size+(alignment-1))&~(alignment-1);
+    return aligned_alloc(alignment, size);
+#elif defined(HAVE_POSIX_MEMALIGN)
+    void *ret;
+    if(posix_memalign(&ret, alignment, size) == 0)
+        return ret;
+    return NULL;
+#elif defined(HAVE__ALIGNED_MALLOC)
+    return _aligned_malloc(size, alignment);
+#else
+    char *ret = malloc(size+alignment);
+    if(ret != NULL)
+    {
+        *(ret++) = 0x00;
+        while(((ptrdiff_t)ret&(alignment-1)) != 0)
+            *(ret++) = 0x55;
+    }
+    return ret;
+#endif
+}
+
+void *al_calloc(size_t alignment, size_t size)
+{
+    void *ret = al_malloc(alignment, size);
+    if(ret) memset(ret, 0, size);
+    return ret;
+}
+
+void al_free(void *ptr)
+{
+#if defined(HAVE_ALIGNED_ALLOC) || defined(HAVE_POSIX_MEMALIGN)
+    free(ptr);
+#elif defined(HAVE__ALIGNED_MALLOC)
+    _aligned_free(ptr);
+#else
+    if(ptr != NULL)
+    {
+        char *finder = ptr;
+        do {
+            --finder;
+        } while(*finder == 0x55);
+        free(finder);
+    }
+#endif
+}

+ 21 - 0
libs/openal-soft/common/almalloc.h

@@ -0,0 +1,21 @@
+#ifndef AL_MALLOC_H
+#define AL_MALLOC_H
+
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Minimum alignment required by posix_memalign. */
+#define DEF_ALIGN sizeof(void*)
+
+void *al_malloc(size_t alignment, size_t size);
+void *al_calloc(size_t alignment, size_t size);
+void al_free(void *ptr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AL_MALLOC_H */

+ 0 - 3
libs/openal-soft/common/atomic.c

@@ -8,6 +8,3 @@ extern inline void InitRef(RefCount *ptr, uint value);
 extern inline uint ReadRef(RefCount *ptr);
 extern inline uint IncrementRef(RefCount *ptr);
 extern inline uint DecrementRef(RefCount *ptr);
-
-extern inline int ExchangeInt(volatile int *ptr, int newval);
-extern inline void *ExchangePtr(XchgPtr *ptr, void *newval);

+ 425 - 0
libs/openal-soft/common/atomic.h

@@ -0,0 +1,425 @@
+#ifndef AL_ATOMIC_H
+#define AL_ATOMIC_H
+
+#include "static_assert.h"
+#include "bool.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Atomics using C11 */
+#ifdef HAVE_C11_ATOMIC
+
+#include <stdatomic.h>
+
+#define almemory_order memory_order
+#define almemory_order_relaxed memory_order_relaxed
+#define almemory_order_consume memory_order_consume
+#define almemory_order_acquire memory_order_acquire
+#define almemory_order_release memory_order_release
+#define almemory_order_acq_rel memory_order_acq_rel
+#define almemory_order_seq_cst memory_order_seq_cst
+
+#define ATOMIC(T)  T _Atomic
+#define ATOMIC_FLAG atomic_flag
+
+#define ATOMIC_INIT atomic_init
+#define ATOMIC_INIT_STATIC ATOMIC_VAR_INIT
+/*#define ATOMIC_FLAG_INIT ATOMIC_FLAG_INIT*/
+
+#define ATOMIC_LOAD atomic_load_explicit
+#define ATOMIC_STORE atomic_store_explicit
+
+#define ATOMIC_ADD atomic_fetch_add_explicit
+#define ATOMIC_SUB atomic_fetch_sub_explicit
+
+#define ATOMIC_EXCHANGE atomic_exchange_explicit
+#define ATOMIC_COMPARE_EXCHANGE_STRONG atomic_compare_exchange_strong_explicit
+#define ATOMIC_COMPARE_EXCHANGE_WEAK atomic_compare_exchange_weak_explicit
+
+#define ATOMIC_FLAG_TEST_AND_SET atomic_flag_test_and_set_explicit
+#define ATOMIC_FLAG_CLEAR atomic_flag_clear_explicit
+
+#define ATOMIC_THREAD_FENCE atomic_thread_fence
+
+/* Atomics using GCC intrinsics */
+#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)) && !defined(__QNXNTO__)
+
+enum almemory_order {
+    almemory_order_relaxed,
+    almemory_order_consume,
+    almemory_order_acquire,
+    almemory_order_release,
+    almemory_order_acq_rel,
+    almemory_order_seq_cst
+};
+
+#define ATOMIC(T)  struct { T volatile value; }
+#define ATOMIC_FLAG  ATOMIC(int)
+
+#define ATOMIC_INIT(_val, _newval)  do { (_val)->value = (_newval); } while(0)
+#define ATOMIC_INIT_STATIC(_newval) {(_newval)}
+#define ATOMIC_FLAG_INIT            ATOMIC_INIT_STATIC(0)
+
+#define ATOMIC_LOAD(_val, _MO)  __extension__({ \
+    __typeof((_val)->value) _r = (_val)->value; \
+    __asm__ __volatile__("" ::: "memory");      \
+    _r;                                         \
+})
+#define ATOMIC_STORE(_val, _newval, _MO)  do { \
+    __asm__ __volatile__("" ::: "memory");     \
+    (_val)->value = (_newval);                 \
+} while(0)
+
+#define ATOMIC_ADD(_val, _incr, _MO) __sync_fetch_and_add(&(_val)->value, (_incr))
+#define ATOMIC_SUB(_val, _decr, _MO) __sync_fetch_and_sub(&(_val)->value, (_decr))
+
+#define ATOMIC_EXCHANGE(_val, _newval, _MO)  __extension__({                  \
+    __asm__ __volatile__("" ::: "memory");                                    \
+    __sync_lock_test_and_set(&(_val)->value, (_newval));                      \
+})
+#define ATOMIC_COMPARE_EXCHANGE_STRONG(_val, _oldval, _newval, _MO1, _MO2) __extension__({ \
+    __typeof(*(_oldval)) _o = *(_oldval);                                     \
+    *(_oldval) = __sync_val_compare_and_swap(&(_val)->value, _o, (_newval));  \
+    *(_oldval) == _o;                                                         \
+})
+
+#define ATOMIC_FLAG_TEST_AND_SET(_val, _MO)  __extension__({                  \
+    __asm__ __volatile__("" ::: "memory");                                    \
+    __sync_lock_test_and_set(&(_val)->value, 1);                              \
+})
+#define ATOMIC_FLAG_CLEAR(_val, _MO)  __extension__({                         \
+    __sync_lock_release(&(_val)->value);                                      \
+    __asm__ __volatile__("" ::: "memory");                                    \
+})
+
+
+#define ATOMIC_THREAD_FENCE(order) do {        \
+    enum { must_be_constant = (order) };       \
+    const int _o = must_be_constant;           \
+    if(_o > almemory_order_relaxed)            \
+        __asm__ __volatile__("" ::: "memory"); \
+} while(0)
+
+/* Atomics using x86/x86-64 GCC inline assembly */
+#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
+
+#define WRAP_ADD(S, ret, dest, incr) __asm__ __volatile__(                    \
+    "lock; xadd"S" %0,(%1)"                                                   \
+    : "=r" (ret)                                                              \
+    : "r" (dest), "0" (incr)                                                  \
+    : "memory"                                                                \
+)
+#define WRAP_SUB(S, ret, dest, decr) __asm__ __volatile__(                    \
+    "lock; xadd"S" %0,(%1)"                                                   \
+    : "=r" (ret)                                                              \
+    : "r" (dest), "0" (-(decr))                                               \
+    : "memory"                                                                \
+)
+
+#define WRAP_XCHG(S, ret, dest, newval) __asm__ __volatile__(                 \
+    "lock; xchg"S" %0,(%1)"                                                   \
+    : "=r" (ret)                                                              \
+    : "r" (dest), "0" (newval)                                                \
+    : "memory"                                                                \
+)
+#define WRAP_CMPXCHG(S, ret, dest, oldval, newval) __asm__ __volatile__(      \
+    "lock; cmpxchg"S" %2,(%1)"                                                \
+    : "=a" (ret)                                                              \
+    : "r" (dest), "r" (newval), "0" (oldval)                                  \
+    : "memory"                                                                \
+)
+
+
+enum almemory_order {
+    almemory_order_relaxed,
+    almemory_order_consume,
+    almemory_order_acquire,
+    almemory_order_release,
+    almemory_order_acq_rel,
+    almemory_order_seq_cst
+};
+
+#define ATOMIC(T)  struct { T volatile value; }
+
+#define ATOMIC_INIT(_val, _newval)  do { (_val)->value = (_newval); } while(0)
+#define ATOMIC_INIT_STATIC(_newval) {(_newval)}
+
+#define ATOMIC_LOAD(_val, _MO)  __extension__({ \
+    __typeof((_val)->value) _r = (_val)->value; \
+    __asm__ __volatile__("" ::: "memory");      \
+    _r;                                         \
+})
+#define ATOMIC_STORE(_val, _newval, _MO)  do { \
+    __asm__ __volatile__("" ::: "memory");     \
+    (_val)->value = (_newval);                 \
+} while(0)
+
+#define ATOMIC_ADD(_val, _incr, _MO) __extension__({                          \
+    static_assert(sizeof((_val)->value)==4 || sizeof((_val)->value)==8, "Unsupported size!"); \
+    __typeof((_val)->value) _r;                                               \
+    if(sizeof((_val)->value) == 4) WRAP_ADD("l", _r, &(_val)->value, _incr);  \
+    else if(sizeof((_val)->value) == 8) WRAP_ADD("q", _r, &(_val)->value, _incr); \
+    _r;                                                                       \
+})
+#define ATOMIC_SUB(_val, _decr, _MO) __extension__({                          \
+    static_assert(sizeof((_val)->value)==4 || sizeof((_val)->value)==8, "Unsupported size!"); \
+    __typeof((_val)->value) _r;                                               \
+    if(sizeof((_val)->value) == 4) WRAP_SUB("l", _r, &(_val)->value, _decr);  \
+    else if(sizeof((_val)->value) == 8) WRAP_SUB("q", _r, &(_val)->value, _decr); \
+    _r;                                                                       \
+})
+
+#define ATOMIC_EXCHANGE(_val, _newval, _MO)  __extension__({                  \
+    __typeof((_val)->value) _r;                                               \
+    if(sizeof((_val)->value) == 4) WRAP_XCHG("l", _r, &(_val)->value, (_newval)); \
+    else if(sizeof((_val)->value) == 8) WRAP_XCHG("q", _r, &(_val)->value, (_newval)); \
+    _r;                                                                       \
+})
+#define ATOMIC_COMPARE_EXCHANGE_STRONG(_val, _oldval, _newval, _MO1, _MO2) __extension__({ \
+    __typeof(*(_oldval)) _old = *(_oldval);                                   \
+    if(sizeof((_val)->value) == 4) WRAP_CMPXCHG("l", *(_oldval), &(_val)->value, _old, (_newval)); \
+    else if(sizeof((_val)->value) == 8) WRAP_CMPXCHG("q", *(_oldval), &(_val)->value, _old, (_newval)); \
+    *(_oldval) == _old;                                                       \
+})
+
+#define ATOMIC_EXCHANGE_PTR(_val, _newval, _MO)  __extension__({              \
+    void *_r;                                                                 \
+    if(sizeof(void*) == 4) WRAP_XCHG("l", _r, &(_val)->value, (_newval));     \
+    else if(sizeof(void*) == 8) WRAP_XCHG("q", _r, &(_val)->value, (_newval));\
+    _r;                                                                       \
+})
+#define ATOMIC_COMPARE_EXCHANGE_PTR_STRONG(_val, _oldval, _newval, _MO1, _MO2) __extension__({ \
+    void *_old = *(_oldval);                                                  \
+    if(sizeof(void*) == 4) WRAP_CMPXCHG("l", *(_oldval), &(_val)->value, _old, (_newval)); \
+    else if(sizeof(void*) == 8) WRAP_CMPXCHG("q", *(_oldval), &(_val)->value, _old, (_newval)); \
+    *(_oldval) == _old;                                                       \
+})
+
+#define ATOMIC_THREAD_FENCE(order) do {        \
+    enum { must_be_constant = (order) };       \
+    const int _o = must_be_constant;           \
+    if(_o > almemory_order_relaxed)            \
+        __asm__ __volatile__("" ::: "memory"); \
+} while(0)
+
+/* Atomics using Windows methods */
+#elif defined(_WIN32)
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+/* NOTE: This mess is *extremely* touchy. It lacks quite a bit of safety
+ * checking due to the lack of multi-statement expressions, typeof(), and C99
+ * compound literals. It is incapable of properly exchanging floats, which get
+ * casted to LONG/int, and could cast away potential warnings.
+ *
+ * Unfortunately, it's the only semi-safe way that doesn't rely on C99 (because
+ * MSVC).
+ */
+
+inline LONG AtomicAdd32(volatile LONG *dest, LONG incr)
+{
+    return InterlockedExchangeAdd(dest, incr);
+}
+inline LONGLONG AtomicAdd64(volatile LONGLONG *dest, LONGLONG incr)
+{
+    return InterlockedExchangeAdd64(dest, incr);
+}
+inline LONG AtomicSub32(volatile LONG *dest, LONG decr)
+{
+    return InterlockedExchangeAdd(dest, -decr);
+}
+inline LONGLONG AtomicSub64(volatile LONGLONG *dest, LONGLONG decr)
+{
+    return InterlockedExchangeAdd64(dest, -decr);
+}
+
+inline LONG AtomicSwap32(volatile LONG *dest, LONG newval)
+{
+    return InterlockedExchange(dest, newval);
+}
+inline LONGLONG AtomicSwap64(volatile LONGLONG *dest, LONGLONG newval)
+{
+    return InterlockedExchange64(dest, newval);
+}
+inline void *AtomicSwapPtr(void *volatile *dest, void *newval)
+{
+    return InterlockedExchangePointer(dest, newval);
+}
+
+inline bool CompareAndSwap32(volatile LONG *dest, LONG newval, LONG *oldval)
+{
+    LONG old = *oldval;
+    *oldval = InterlockedCompareExchange(dest, newval, *oldval);
+    return old == *oldval;
+}
+inline bool CompareAndSwap64(volatile LONGLONG *dest, LONGLONG newval, LONGLONG *oldval)
+{
+    LONGLONG old = *oldval;
+    *oldval = InterlockedCompareExchange64(dest, newval, *oldval);
+    return old == *oldval;
+}
+inline bool CompareAndSwapPtr(void *volatile *dest, void *newval, void **oldval)
+{
+    void *old = *oldval;
+    *oldval = InterlockedCompareExchangePointer(dest, newval, *oldval);
+    return old == *oldval;
+}
+
+#define WRAP_ADDSUB(T, _func, _ptr, _amnt)  _func((T volatile*)(_ptr), (_amnt))
+#define WRAP_XCHG(T, _func, _ptr, _newval)  _func((T volatile*)(_ptr), (_newval))
+#define WRAP_CMPXCHG(T, _func, _ptr, _newval, _oldval)  _func((T volatile*)(_ptr), (_newval), (T*)(_oldval))
+
+
+enum almemory_order {
+    almemory_order_relaxed,
+    almemory_order_consume,
+    almemory_order_acquire,
+    almemory_order_release,
+    almemory_order_acq_rel,
+    almemory_order_seq_cst
+};
+
+#define ATOMIC(T)  struct { T volatile value; }
+
+#define ATOMIC_INIT(_val, _newval)  do { (_val)->value = (_newval); } while(0)
+#define ATOMIC_INIT_STATIC(_newval) {(_newval)}
+
+#define ATOMIC_LOAD(_val, _MO)  ((_val)->value)
+#define ATOMIC_STORE(_val, _newval, _MO)  do {  \
+    (_val)->value = (_newval);                  \
+} while(0)
+
+int _al_invalid_atomic_size(); /* not defined */
+
+#define ATOMIC_ADD(_val, _incr, _MO)                                          \
+    ((sizeof((_val)->value)==4) ? WRAP_ADDSUB(LONG, AtomicAdd32, &(_val)->value, (_incr)) : \
+     (sizeof((_val)->value)==8) ? WRAP_ADDSUB(LONGLONG, AtomicAdd64, &(_val)->value, (_incr)) : \
+     _al_invalid_atomic_size())
+#define ATOMIC_SUB(_val, _decr, _MO)                                          \
+    ((sizeof((_val)->value)==4) ? WRAP_ADDSUB(LONG, AtomicSub32, &(_val)->value, (_decr)) : \
+     (sizeof((_val)->value)==8) ? WRAP_ADDSUB(LONGLONG, AtomicSub64, &(_val)->value, (_decr)) : \
+     _al_invalid_atomic_size())
+
+#define ATOMIC_EXCHANGE(_val, _newval, _MO)                                   \
+    ((sizeof((_val)->value)==4) ? WRAP_XCHG(LONG, AtomicSwap32, &(_val)->value, (_newval)) : \
+     (sizeof((_val)->value)==8) ? WRAP_XCHG(LONGLONG, AtomicSwap64, &(_val)->value, (_newval)) :   \
+     (LONG)_al_invalid_atomic_size())
+#define ATOMIC_COMPARE_EXCHANGE_STRONG(_val, _oldval, _newval, _MO1, _MO2)    \
+    ((sizeof((_val)->value)==4) ? WRAP_CMPXCHG(LONG, CompareAndSwap32, &(_val)->value, (_newval), (_oldval)) : \
+     (sizeof((_val)->value)==8) ? WRAP_CMPXCHG(LONGLONG, CompareAndSwap64, &(_val)->value, (_newval), (_oldval)) : \
+     (bool)_al_invalid_atomic_size())
+
+#define ATOMIC_EXCHANGE_PTR(_val, _newval, _MO) \
+    ((sizeof((_val)->value)==sizeof(void*)) ? AtomicSwapPtr((void*volatile*)&(_val)->value, (_newval)) : \
+     (void*)_al_invalid_atomic_size())
+#define ATOMIC_COMPARE_EXCHANGE_PTR_STRONG(_val, _oldval, _newval, _MO1, _MO2)\
+    ((sizeof((_val)->value)==sizeof(void*)) ? CompareAndSwapPtr((void*volatile*)&(_val)->value, (_newval), (void**)(_oldval)) : \
+     (bool)_al_invalid_atomic_size())
+
+#define ATOMIC_THREAD_FENCE(order) do {        \
+    enum { must_be_constant = (order) };       \
+    const int _o = must_be_constant;           \
+    if(_o > almemory_order_relaxed)            \
+        _ReadWriteBarrier();                   \
+} while(0)
+
+#else
+
+#error "No atomic functions available on this platform!"
+
+#define ATOMIC(T)  T
+
+#define ATOMIC_INIT(_val, _newval)  ((void)0)
+#define ATOMIC_INIT_STATIC(_newval) (0)
+
+#define ATOMIC_LOAD(...)   (0)
+#define ATOMIC_STORE(...)  ((void)0)
+
+#define ATOMIC_ADD(...) (0)
+#define ATOMIC_SUB(...) (0)
+
+#define ATOMIC_EXCHANGE(...) (0)
+#define ATOMIC_COMPARE_EXCHANGE_STRONG(...) (0)
+
+#define ATOMIC_THREAD_FENCE(...) ((void)0)
+#endif
+
+/* If no PTR xchg variants are provided, the normal ones can handle it. */
+#ifndef ATOMIC_EXCHANGE_PTR
+#define ATOMIC_EXCHANGE_PTR ATOMIC_EXCHANGE
+#define ATOMIC_COMPARE_EXCHANGE_PTR_STRONG ATOMIC_COMPARE_EXCHANGE_STRONG
+#define ATOMIC_COMPARE_EXCHANGE_PTR_WEAK ATOMIC_COMPARE_EXCHANGE_WEAK
+#endif
+
+/* If no weak cmpxchg is provided (not all systems will have one), substitute a
+ * strong cmpxchg. */
+#ifndef ATOMIC_COMPARE_EXCHANGE_WEAK
+#define ATOMIC_COMPARE_EXCHANGE_WEAK ATOMIC_COMPARE_EXCHANGE_STRONG
+#endif
+#ifndef ATOMIC_COMPARE_EXCHANGE_PTR_WEAK
+#define ATOMIC_COMPARE_EXCHANGE_PTR_WEAK ATOMIC_COMPARE_EXCHANGE_PTR_STRONG
+#endif
+
+/* If no ATOMIC_FLAG is defined, simulate one with an atomic int using exchange
+ * and store ops.
+ */
+#ifndef ATOMIC_FLAG
+#define ATOMIC_FLAG      ATOMIC(int)
+#define ATOMIC_FLAG_INIT ATOMIC_INIT_STATIC(0)
+#define ATOMIC_FLAG_TEST_AND_SET(_val, _MO) ATOMIC_EXCHANGE(_val, 1, _MO)
+#define ATOMIC_FLAG_CLEAR(_val, _MO)        ATOMIC_STORE(_val, 0, _MO)
+#endif
+
+
+#define ATOMIC_LOAD_SEQ(_val) ATOMIC_LOAD(_val, almemory_order_seq_cst)
+#define ATOMIC_STORE_SEQ(_val, _newval) ATOMIC_STORE(_val, _newval, almemory_order_seq_cst)
+
+#define ATOMIC_ADD_SEQ(_val, _incr) ATOMIC_ADD(_val, _incr, almemory_order_seq_cst)
+#define ATOMIC_SUB_SEQ(_val, _decr) ATOMIC_SUB(_val, _decr, almemory_order_seq_cst)
+
+#define ATOMIC_EXCHANGE_SEQ(_val, _newval) ATOMIC_EXCHANGE(_val, _newval, almemory_order_seq_cst)
+#define ATOMIC_COMPARE_EXCHANGE_STRONG_SEQ(_val, _oldval, _newval) \
+    ATOMIC_COMPARE_EXCHANGE_STRONG(_val, _oldval, _newval, almemory_order_seq_cst, almemory_order_seq_cst)
+#define ATOMIC_COMPARE_EXCHANGE_WEAK_SEQ(_val, _oldval, _newval) \
+    ATOMIC_COMPARE_EXCHANGE_WEAK(_val, _oldval, _newval, almemory_order_seq_cst, almemory_order_seq_cst)
+
+#define ATOMIC_EXCHANGE_PTR_SEQ(_val, _newval) ATOMIC_EXCHANGE_PTR(_val, _newval, almemory_order_seq_cst)
+#define ATOMIC_COMPARE_EXCHANGE_PTR_STRONG_SEQ(_val, _oldval, _newval) \
+    ATOMIC_COMPARE_EXCHANGE_PTR_STRONG(_val, _oldval, _newval, almemory_order_seq_cst, almemory_order_seq_cst)
+#define ATOMIC_COMPARE_EXCHANGE_PTR_WEAK_SEQ(_val, _oldval, _newval) \
+    ATOMIC_COMPARE_EXCHANGE_PTR_WEAK(_val, _oldval, _newval, almemory_order_seq_cst, almemory_order_seq_cst)
+
+
+typedef unsigned int uint;
+typedef ATOMIC(uint) RefCount;
+
+inline void InitRef(RefCount *ptr, uint value)
+{ ATOMIC_INIT(ptr, value); }
+inline uint ReadRef(RefCount *ptr)
+{ return ATOMIC_LOAD_SEQ(ptr); }
+inline uint IncrementRef(RefCount *ptr)
+{ return ATOMIC_ADD_SEQ(ptr, 1)+1; }
+inline uint DecrementRef(RefCount *ptr)
+{ return ATOMIC_SUB_SEQ(ptr, 1)-1; }
+
+
+/* WARNING: A livelock is theoretically possible if another thread keeps
+ * changing the head without giving this a chance to actually swap in the new
+ * one (practically impossible with this little code, but...).
+ */
+#define ATOMIC_REPLACE_HEAD(T, _head, _entry) do {                            \
+    T _first = ATOMIC_LOAD(_head, almemory_order_acquire);                    \
+    do {                                                                      \
+        ATOMIC_STORE(&(_entry)->next, _first, almemory_order_relaxed);        \
+    } while(ATOMIC_COMPARE_EXCHANGE_PTR_WEAK(_head, &_first, _entry,          \
+            almemory_order_acq_rel, almemory_order_acquire) == 0);            \
+} while(0)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AL_ATOMIC_H */

+ 0 - 0
libs/openal-soft/include/bool.h → libs/openal-soft/common/bool.h


+ 35 - 0
libs/openal-soft/common/math_defs.h

@@ -0,0 +1,35 @@
+#ifndef AL_MATH_DEFS_H
+#define AL_MATH_DEFS_H
+
+#include <math.h>
+#ifdef HAVE_FLOAT_H
+#include <float.h>
+#endif
+
+#define F_PI    (3.14159265358979323846f)
+#define F_PI_2  (1.57079632679489661923f)
+#define F_TAU   (6.28318530717958647692f)
+
+#ifndef FLT_EPSILON
+#define FLT_EPSILON (1.19209290e-07f)
+#endif
+
+#ifndef HUGE_VALF
+static const union msvc_inf_hack {
+    unsigned char b[4];
+    float f;
+} msvc_inf_union = {{ 0x00, 0x00, 0x80, 0x7F }};
+#define HUGE_VALF (msvc_inf_union.f)
+#endif
+
+#ifndef HAVE_LOG2F
+static inline float log2f(float f)
+{
+    return logf(f) / logf(2.0f);
+}
+#endif
+
+#define DEG2RAD(x)  ((float)(x) * (F_PI/180.0f))
+#define RAD2DEG(x)  ((float)(x) * (180.0f/F_PI))
+
+#endif /* AL_MATH_DEFS_H */

+ 11 - 9
libs/openal-soft/common/rwlock.c

@@ -11,26 +11,27 @@
 /* A simple spinlock. Yield the thread while the given integer is set by
  * another. Could probably be improved... */
 #define LOCK(l) do {                                                          \
-    while(ATOMIC_EXCHANGE(int, &(l), true) == true)                            \
+    while(ATOMIC_FLAG_TEST_AND_SET(&(l), almemory_order_acq_rel) == true)     \
         althrd_yield();                                                       \
 } while(0)
-#define UNLOCK(l) ATOMIC_STORE(&(l), false)
+#define UNLOCK(l) ATOMIC_FLAG_CLEAR(&(l), almemory_order_release)
 
 
 void RWLockInit(RWLock *lock)
 {
     InitRef(&lock->read_count, 0);
     InitRef(&lock->write_count, 0);
-    ATOMIC_INIT(&lock->read_lock, false);
-    ATOMIC_INIT(&lock->read_entry_lock, false);
-    ATOMIC_INIT(&lock->write_lock, false);
+    ATOMIC_FLAG_CLEAR(&lock->read_lock, almemory_order_relaxed);
+    ATOMIC_FLAG_CLEAR(&lock->read_entry_lock, almemory_order_relaxed);
+    ATOMIC_FLAG_CLEAR(&lock->write_lock, almemory_order_relaxed);
 }
 
 void ReadLock(RWLock *lock)
 {
     LOCK(lock->read_entry_lock);
     LOCK(lock->read_lock);
-    if(IncrementRef(&lock->read_count) == 1)
+    /* NOTE: ATOMIC_ADD returns the *old* value! */
+    if(ATOMIC_ADD(&lock->read_count, 1, almemory_order_acq_rel) == 0)
         LOCK(lock->write_lock);
     UNLOCK(lock->read_lock);
     UNLOCK(lock->read_entry_lock);
@@ -38,13 +39,14 @@ void ReadLock(RWLock *lock)
 
 void ReadUnlock(RWLock *lock)
 {
-    if(DecrementRef(&lock->read_count) == 0)
+    /* NOTE: ATOMIC_SUB returns the *old* value! */
+    if(ATOMIC_SUB(&lock->read_count, 1, almemory_order_acq_rel) == 1)
         UNLOCK(lock->write_lock);
 }
 
 void WriteLock(RWLock *lock)
 {
-    if(IncrementRef(&lock->write_count) == 1)
+    if(ATOMIC_ADD(&lock->write_count, 1, almemory_order_acq_rel) == 0)
         LOCK(lock->read_lock);
     LOCK(lock->write_lock);
 }
@@ -52,6 +54,6 @@ void WriteLock(RWLock *lock)
 void WriteUnlock(RWLock *lock)
 {
     UNLOCK(lock->write_lock);
-    if(DecrementRef(&lock->write_count) == 0)
+    if(ATOMIC_SUB(&lock->write_count, 1, almemory_order_acq_rel) == 1)
         UNLOCK(lock->read_lock);
 }

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