mirror of
https://github.com/love2d/megasource.git
synced 2026-08-25 15:01:12 +02:00
Update OpenAL Soft to 1.18.2
This commit is contained in:
@@ -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
|
||||
|
||||
+1464
-831
File diff suppressed because it is too large
Load Diff
+1211
-972
File diff suppressed because it is too large
Load Diff
@@ -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
@@ -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;
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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;
|
||||
@@ -340,8 +379,8 @@ static void probe_devices(snd_pcm_stream_t stream, vector_DevMap *DeviceList)
|
||||
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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
if(list)
|
||||
{
|
||||
UInt32 i;
|
||||
for(i = 0;i < list->mNumberBuffers;i++)
|
||||
free(list->mBuffers[i].mData);
|
||||
free(list);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
typedef struct ALCcoreAudioPlayback {
|
||||
DERIVE_FROM_TYPE(ALCbackend);
|
||||
|
||||
AudioUnit audioUnit;
|
||||
|
||||
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 void ALCcoreAudioPlayback_Destruct(ALCcoreAudioPlayback *self)
|
||||
{
|
||||
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
|
||||
}
|
||||
|
||||
|
||||
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 / data->frameSize);
|
||||
ioData->mBuffers[0].mDataByteSize / self->frameSize);
|
||||
ALCdevice_Unlock(device);
|
||||
|
||||
return noErr;
|
||||
}
|
||||
|
||||
static OSStatus ca_capture_conversion_callback(AudioConverterRef inAudioConverter, UInt32 *ioNumberDataPackets,
|
||||
AudioBufferList *ioData, AudioStreamPacketDescription **outDataPacketDescription, void* inUserData)
|
||||
{
|
||||
ALCdevice *device = (ALCdevice*)inUserData;
|
||||
ca_data *data = (ca_data*)device->ExtraData;
|
||||
|
||||
// Read from the ring buffer and store temporarily in a large buffer
|
||||
ReadRingBuffer(data->ring, data->resampleBuffer, (ALsizei)(*ioNumberDataPackets));
|
||||
|
||||
// Set the input data
|
||||
ioData->mNumberBuffers = 1;
|
||||
ioData->mBuffers[0].mNumberChannels = data->format.mChannelsPerFrame;
|
||||
ioData->mBuffers[0].mData = data->resampleBuffer;
|
||||
ioData->mBuffers[0].mDataByteSize = (*ioNumberDataPackets) * data->format.mBytesPerFrame;
|
||||
|
||||
return noErr;
|
||||
}
|
||||
|
||||
static OSStatus ca_capture_callback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags,
|
||||
const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber,
|
||||
UInt32 inNumberFrames, AudioBufferList *ioData)
|
||||
{
|
||||
ALCdevice *device = (ALCdevice*)inRefCon;
|
||||
ca_data *data = (ca_data*)device->ExtraData;
|
||||
AudioUnitRenderActionFlags flags = 0;
|
||||
OSStatus err;
|
||||
|
||||
// fill the bufferList with data from the input device
|
||||
err = AudioUnitRender(data->audioUnit, &flags, inTimeStamp, 1, inNumberFrames, data->bufferList);
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("AudioUnitRender error: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
WriteRingBuffer(data->ring, data->bufferList->mBuffers[0].mData, inNumberFrames);
|
||||
|
||||
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)
|
||||
{
|
||||
ca_data *data = (ca_data*)device->ExtraData;
|
||||
OSStatus err;
|
||||
|
||||
err = AudioOutputUnitStop(data->audioUnit);
|
||||
OSStatus err = AudioOutputUnitStop(self->audioUnit);
|
||||
if(err != noErr)
|
||||
ERR("AudioOutputUnitStop failed\n");
|
||||
}
|
||||
|
||||
static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
|
||||
|
||||
|
||||
|
||||
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))
|
||||
{
|
||||
ALCcoreAudioCapture *self = inRefCon;
|
||||
AudioUnitRenderActionFlags flags = 0;
|
||||
OSStatus err;
|
||||
|
||||
// fill the bufferList with data from the input device
|
||||
err = AudioUnitRender(self->audioUnit, &flags, inTimeStamp, 1, inNumberFrames, self->bufferList);
|
||||
if(err != noErr)
|
||||
{
|
||||
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 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
|
||||
};
|
||||
typedef struct ALCcoreAudioBackendFactory {
|
||||
DERIVE_FROM_TYPE(ALCbackendFactory);
|
||||
} ALCcoreAudioBackendFactory;
|
||||
#define ALCCOREAUDIOBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCcoreAudioBackendFactory, ALCbackendFactory) } }
|
||||
|
||||
ALCboolean alc_ca_init(BackendFuncs *func_list)
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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,12 +734,7 @@ 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)
|
||||
{
|
||||
case DevFmtMono:
|
||||
@@ -787,27 +785,28 @@ static ALCenum ALCdsoundCapture_open(ALCdsoundCapture *self, const ALCchar *devi
|
||||
SPEAKER_SIDE_LEFT |
|
||||
SPEAKER_SIDE_RIGHT;
|
||||
break;
|
||||
case DevFmtBFormat3D:
|
||||
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.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;
|
||||
|
||||
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;
|
||||
@@ -819,11 +818,14 @@ static ALCenum ALCdsoundCapture_open(ALCdsoundCapture *self, const ALCchar *devi
|
||||
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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
PropVariantClear(&pvname);
|
||||
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;
|
||||
else
|
||||
{
|
||||
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_write(self->Ring, (char*)data, numsamples);
|
||||
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);
|
||||
break;
|
||||
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,15 +1698,19 @@ 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;
|
||||
}
|
||||
@@ -1588,6 +1722,83 @@ static HRESULT ALCmmdevCapture_resetProxy(ALCmmdevCapture *self)
|
||||
}
|
||||
CoTaskMemFree(wfx);
|
||||
wfx = NULL;
|
||||
}
|
||||
|
||||
if(IsEqualGUID(&OutputType.SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+159
-111
@@ -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)
|
||||
{
|
||||
ALint len = self->data_size;
|
||||
ALubyte *WritePtr = self->mix_data;
|
||||
|
||||
aluMixData(device, WritePtr, len/frameSize);
|
||||
while(len > 0 && !self->killNow)
|
||||
{
|
||||
wrote = write(self->fd, WritePtr, len);
|
||||
if(wrote < 0)
|
||||
{
|
||||
if(errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR)
|
||||
{
|
||||
ERR("write failed: %s\n", strerror(errno));
|
||||
ALCplaybackOSS_lock(self);
|
||||
aluHandleDisconnect(device);
|
||||
while(!ATOMIC_LOAD_SEQ(&self->killNow) && device->Connected)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
al_nssleep(1000000);
|
||||
else if(sret == 0)
|
||||
{
|
||||
WARN("select timeout\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
len -= wrote;
|
||||
WritePtr += wrote;
|
||||
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, write_ptr, to_write);
|
||||
if(wrote < 0)
|
||||
{
|
||||
if(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
|
||||
continue;
|
||||
ERR("write failed: %s\n", strerror(errno));
|
||||
aluHandleDisconnect(device);
|
||||
break;
|
||||
}
|
||||
|
||||
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,10 +345,15 @@ 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
|
||||
{
|
||||
if(!dev->next)
|
||||
{
|
||||
ALCossListPopulate(&oss_playback, DSP_CAP_OUTPUT);
|
||||
dev = &oss_playback;
|
||||
}
|
||||
while(dev != NULL)
|
||||
{
|
||||
if (strcmp(dev->handle, name) == 0)
|
||||
@@ -331,10 +361,11 @@ static ALCenum ALCplaybackOSS_open(ALCplaybackOSS *self, const ALCchar *name)
|
||||
dev = dev->next;
|
||||
}
|
||||
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;
|
||||
ll_ringbuffer_t *ring;
|
||||
|
||||
RingBuffer *ring;
|
||||
int doCapture;
|
||||
|
||||
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,17 +536,45 @@ 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);
|
||||
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)
|
||||
{
|
||||
if(errno == EINTR)
|
||||
continue;
|
||||
ERR("select failed: %s\n", strerror(errno));
|
||||
aluHandleDisconnect(device);
|
||||
break;
|
||||
}
|
||||
else if(sret == 0)
|
||||
{
|
||||
WARN("select timeout\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
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));
|
||||
@@ -535,13 +583,8 @@ static int ALCcaptureOSS_recordProc(void *ptr)
|
||||
ALCcaptureOSS_unlock(self);
|
||||
break;
|
||||
}
|
||||
if(amt == 0)
|
||||
{
|
||||
al_nssleep(1000000);
|
||||
continue;
|
||||
ll_ringbuffer_write_advance(self->ring, amt/frame_size);
|
||||
}
|
||||
if(self->doCapture)
|
||||
WriteRingBuffer(self->ring, self->read_data, amt/frameSize);
|
||||
}
|
||||
|
||||
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,10 +613,15 @@ 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
|
||||
{
|
||||
if(!dev->next)
|
||||
{
|
||||
ALCossListPopulate(&oss_capture, DSP_CAP_INPUT);
|
||||
dev = &oss_capture;
|
||||
}
|
||||
while(dev != NULL)
|
||||
{
|
||||
if (strcmp(dev->handle, name) == 0)
|
||||
@@ -579,8 +629,11 @@ static ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name)
|
||||
dev = dev->next;
|
||||
}
|
||||
if(dev == NULL)
|
||||
{
|
||||
WARN("Could not find \"%s\" in device list\n", name);
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
self->fd = open(dev->path, O_RDONLY);
|
||||
if(self->fd == -1)
|
||||
@@ -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,32 +812,37 @@ 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);
|
||||
ALCossListFree(&oss_playback);
|
||||
ALCossListPopulate(&oss_playback, DSP_CAP_OUTPUT);
|
||||
cur = &oss_playback;
|
||||
while(cur != NULL)
|
||||
{
|
||||
#ifdef HAVE_STAT
|
||||
struct stat buf;
|
||||
if(stat(cur->path, &buf) == 0)
|
||||
#endif
|
||||
AppendAllDevicesList(cur->handle);
|
||||
cur = cur->next;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case CAPTURE_DEVICE_PROBE:
|
||||
{
|
||||
struct oss_device *cur = &oss_capture;
|
||||
ALCossListFree(cur);
|
||||
ALCossListPopulate(NULL, cur);
|
||||
ALCossListFree(&oss_capture);
|
||||
ALCossListPopulate(&oss_capture, DSP_CAP_INPUT);
|
||||
cur = &oss_capture;
|
||||
while(cur != NULL)
|
||||
{
|
||||
#ifdef HAVE_STAT
|
||||
struct stat buf;
|
||||
if(stat(cur->path, &buf) == 0)
|
||||
#endif
|
||||
AppendCaptureDeviceList(cur->handle);
|
||||
cur = cur->next;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
if(pa_stream_is_corked(self->stream) == 1)
|
||||
len = pa_stream_writable_size(self->stream);
|
||||
if(len < 0)
|
||||
{
|
||||
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;
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
/* 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,17 +202,12 @@ FORCE_ALIGN static int qsa_proc_playback(void* ptr)
|
||||
param.sched_priority=param.sched_curpriority+1;
|
||||
SchedSet(0, 0, SCHED_NOCHANGE, ¶m);
|
||||
|
||||
ALint frame_size=FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
|
||||
const ALint frame_size = FrameSizeFromDevFmt(
|
||||
device->FmtChans, device->FmtType, device->AmbiOrder
|
||||
);
|
||||
|
||||
V0(device->Backend,lock)();
|
||||
while(!data->killNow)
|
||||
{
|
||||
ALint len=data->size;
|
||||
write_ptr=data->buffer;
|
||||
|
||||
avail=len/frame_size;
|
||||
aluMixData(device, write_ptr, avail);
|
||||
|
||||
while (len>0 && !data->killNow)
|
||||
{
|
||||
FD_ZERO(&wfds);
|
||||
FD_SET(data->audio_fd, &wfds);
|
||||
@@ -196,41 +215,42 @@ FORCE_ALIGN static int qsa_proc_playback(void* ptr)
|
||||
timeout.tv_usec=0;
|
||||
|
||||
/* Select also works like time slice to OS */
|
||||
selectret=select(data->audio_fd+1, NULL, &wfds, NULL, &timeout);
|
||||
switch (selectret)
|
||||
V0(device->Backend,unlock)();
|
||||
sret = select(data->audio_fd+1, NULL, &wfds, NULL, &timeout);
|
||||
V0(device->Backend,lock)();
|
||||
if(sret == -1)
|
||||
{
|
||||
case -1:
|
||||
ERR("select error: %s\n", strerror(errno));
|
||||
aluHandleDisconnect(device);
|
||||
return 1;
|
||||
case 0:
|
||||
break;
|
||||
default:
|
||||
if (FD_ISSET(data->audio_fd, &wfds))
|
||||
{
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
int wrote=snd_pcm_plugin_write(data->pcmHandle, write_ptr, len);
|
||||
|
||||
if (wrote<=0)
|
||||
{
|
||||
if ((errno==EAGAIN) || (errno==EWOULDBLOCK))
|
||||
if(sret == 0)
|
||||
{
|
||||
ERR("select timeout\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
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)
|
||||
continue;
|
||||
|
||||
memset(&status, 0, sizeof(status));
|
||||
status.channel = SND_PCM_CHANNEL_PLAYBACK;
|
||||
|
||||
snd_pcm_plugin_status(data->pcmHandle, &status);
|
||||
|
||||
/* we need to reinitialize the sound channel if we've underrun the buffer */
|
||||
if ((status.status==SND_PCM_STATUS_UNDERRUN) ||
|
||||
(status.status==SND_PCM_STATUS_READY))
|
||||
if(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;
|
||||
@@ -244,6 +264,7 @@ FORCE_ALIGN static int qsa_proc_playback(void* ptr)
|
||||
}
|
||||
}
|
||||
}
|
||||
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)
|
||||
@@ -366,13 +389,13 @@ static ALCboolean qsa_reset_playback(ALCdevice* device)
|
||||
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)
|
||||
@@ -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)
|
||||
{
|
||||
*func_list = qsa_funcs;
|
||||
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;
|
||||
}
|
||||
|
||||
void alc_qsa_deinit(void)
|
||||
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))
|
||||
{
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -28,19 +28,16 @@
|
||||
#include "alu.h"
|
||||
#include "threads.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 int sndio_proc(void *ptr)
|
||||
static const ALCchar sndio_device[] = "SndIO Default";
|
||||
|
||||
|
||||
static void ALCsndioBackend_Construct(ALCsndioBackend *self, ALCdevice *device)
|
||||
{
|
||||
ALCdevice *device = ptr;
|
||||
sndio_data *data = device->ExtraData;
|
||||
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 ALCsndioBackend_mixerProc(void *ptr)
|
||||
{
|
||||
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
|
||||
};
|
||||
typedef struct ALCsndioBackendFactory {
|
||||
DERIVE_FROM_TYPE(ALCbackendFactory);
|
||||
} ALCsndioBackendFactory;
|
||||
#define ALCSNDIOBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCsndioBackendFactory, ALCbackendFactory) } }
|
||||
|
||||
ALCboolean alc_sndio_init(BackendFuncs *func_list)
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
ALint len = self->data_size;
|
||||
ALubyte *WritePtr = self->mix_data;
|
||||
|
||||
aluMixData(Device, WritePtr, len/frameSize);
|
||||
while(len > 0 && !self->killNow)
|
||||
{
|
||||
wrote = write(self->fd, WritePtr, len);
|
||||
if(wrote < 0)
|
||||
{
|
||||
if(errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR)
|
||||
{
|
||||
ERR("write failed: %s\n", strerror(errno));
|
||||
ALCsolarisBackend_lock(self);
|
||||
aluHandleDisconnect(Device);
|
||||
while(!ATOMIC_LOAD_SEQ(&self->killNow) && device->Connected)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
al_nssleep(1000000);
|
||||
else if(sret == 0)
|
||||
{
|
||||
WARN("select timeout\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
len -= wrote;
|
||||
WritePtr += wrote;
|
||||
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, write_ptr, to_write);
|
||||
if(wrote < 0)
|
||||
{
|
||||
if(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
|
||||
continue;
|
||||
ERR("write failed: %s\n", strerror(errno));
|
||||
aluHandleDisconnect(device);
|
||||
break;
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
@@ -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);
|
||||
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 == 2)
|
||||
{
|
||||
for(i = 0;i < self->mSize;i++)
|
||||
fputc(bytes[i^1], self->mFile);
|
||||
}
|
||||
else if(bytesize == 4)
|
||||
{
|
||||
for(i = 0;i < self->mSize;i++)
|
||||
fputc(bytes[i^3], self->mFile);
|
||||
}
|
||||
}
|
||||
else
|
||||
ALuint *samples = self->mBuffer;
|
||||
ALuint len = self->mSize / 4;
|
||||
for(i = 0;i < len;i++)
|
||||
{
|
||||
fs = fwrite(self->mBuffer, frameSize, device->UpdateSize,
|
||||
self->mFile);
|
||||
(void)fs;
|
||||
ALuint samp = samples[i];
|
||||
samples[i] = (samp>>24) | ((samp>>8)&0x0000ff00) |
|
||||
((samp<<8)&0x00ff0000) | (samp<<24);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fs = fwrite(self->mBuffer, frameSize, device->UpdateSize, self->mFile);
|
||||
(void)fs;
|
||||
if(ferror(self->mFile))
|
||||
{
|
||||
ERR("Error writing to file\n");
|
||||
@@ -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,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 {
|
||||
|
||||
@@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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);
|
||||
@@ -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 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)
|
||||
{
|
||||
free(state->SampleBuffer[0]);
|
||||
ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
|
||||
SET_VTABLE2(ALchorusState, ALeffectState, state);
|
||||
|
||||
state->BufferLength = 0;
|
||||
state->SampleBuffer[0] = NULL;
|
||||
state->SampleBuffer[1] = NULL;
|
||||
state->offset = 0;
|
||||
state->lfo_range = 1;
|
||||
state->waveform = CWF_Triangle;
|
||||
}
|
||||
|
||||
static ALvoid ALchorusState_Destruct(ALchorusState *state)
|
||||
{
|
||||
al_free(state->SampleBuffer[0]);
|
||||
state->SampleBuffer[0] = NULL;
|
||||
state->SampleBuffer[1] = NULL;
|
||||
|
||||
ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
|
||||
}
|
||||
|
||||
static ALboolean ALchorusState_deviceUpdate(ALchorusState *state, ALCdevice *Device)
|
||||
{
|
||||
ALuint maxlen;
|
||||
ALuint it;
|
||||
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 */
|
||||
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;
|
||||
}
|
||||
|
||||
static inline void Sinusoid(ALint *delay_left, ALint *delay_right, ALuint offset, const ALchorusState *state)
|
||||
ALsizei i;
|
||||
for(i = 0;i < todo;i++)
|
||||
{
|
||||
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;
|
||||
delays[i] = fastf2i((1.0f - fabsf(2.0f - lfo_scale*offset)) * depth) + delay;
|
||||
offset = (offset+1)%lfo_range;
|
||||
}
|
||||
}
|
||||
|
||||
#define DECL_TEMPLATE(Func) \
|
||||
static void Process##Func(ALchorusState *state, const ALuint SamplesToDo, \
|
||||
const ALfloat *restrict SamplesIn, ALfloat (*restrict out)[2]) \
|
||||
{ \
|
||||
const ALuint bufmask = state->BufferLength-1; \
|
||||
ALfloat *restrict leftbuf = state->SampleBuffer[0]; \
|
||||
ALfloat *restrict rightbuf = state->SampleBuffer[1]; \
|
||||
ALuint offset = state->offset; \
|
||||
const ALfloat feedback = state->feedback; \
|
||||
ALuint it; \
|
||||
\
|
||||
for(it = 0;it < SamplesToDo;it++) \
|
||||
{ \
|
||||
ALint delay_left, delay_right; \
|
||||
Func(&delay_left, &delay_right, offset, state); \
|
||||
\
|
||||
out[it][0] = leftbuf[(offset-delay_left)&bufmask]; \
|
||||
leftbuf[offset&bufmask] = (out[it][0]+SamplesIn[it]) * feedback; \
|
||||
\
|
||||
out[it][1] = rightbuf[(offset-delay_right)&bufmask]; \
|
||||
rightbuf[offset&bufmask] = (out[it][1]+SamplesIn[it]) * feedback; \
|
||||
\
|
||||
offset++; \
|
||||
} \
|
||||
state->offset = offset; \
|
||||
}
|
||||
|
||||
DECL_TEMPLATE(Triangle)
|
||||
DECL_TEMPLATE(Sinusoid)
|
||||
|
||||
#undef DECL_TEMPLATE
|
||||
|
||||
static ALvoid ALchorusState_process(ALchorusState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels)
|
||||
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)
|
||||
{
|
||||
ALuint it, kt;
|
||||
ALuint base;
|
||||
ALsizei i;
|
||||
for(i = 0;i < todo;i++)
|
||||
{
|
||||
delays[i] = fastf2i(sinf(lfo_scale*offset) * depth) + delay;
|
||||
offset = (offset+1)%lfo_range;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static ALvoid ALchorusState_process(ALchorusState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
ComputeAmbientGains(device, slot->Gain, state->Gain);
|
||||
state->Enabled = props->Compressor.OnOff;
|
||||
|
||||
STATIC_CAST(ALeffectState,state)->OutBuffer = device->FOAOut.Buffer;
|
||||
STATIC_CAST(ALeffectState,state)->OutChannels = device->FOAOut.NumChannels;
|
||||
for(i = 0;i < 4;i++)
|
||||
ComputeFirstOrderGains(device->FOAOut, IdentityMatrixf.m[i],
|
||||
slot->Params.Gain, state->Gain[i]);
|
||||
}
|
||||
|
||||
static ALvoid ALcompressorState_process(ALcompressorState *state, ALuint SamplesToDo, const ALfloat *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];
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
static ALvoid ALdedicatedState_Destruct(ALdedicatedState *UNUSED(state))
|
||||
DEFINE_ALEFFECTSTATE_VTABLE(ALdedicatedState);
|
||||
|
||||
|
||||
static void ALdedicatedState_Construct(ALdedicatedState *state)
|
||||
{
|
||||
ALsizei s;
|
||||
|
||||
ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
|
||||
SET_VTABLE2(ALdedicatedState, ALeffectState, state);
|
||||
|
||||
for(s = 0;s < MAX_OUTPUT_CHANNELS;s++)
|
||||
state->gains[s] = 0.0f;
|
||||
}
|
||||
|
||||
static ALvoid ALdedicatedState_Destruct(ALdedicatedState *state)
|
||||
{
|
||||
ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
|
||||
}
|
||||
|
||||
static ALboolean ALdedicatedState_deviceUpdate(ALdedicatedState *UNUSED(state), ALCdevice *UNUSED(device))
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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++)
|
||||
{
|
||||
for(ot = 0;ot < 4;ot++)
|
||||
{
|
||||
ALfloat smp;
|
||||
smp = ALfilterState_processSingle(&state->lowpass, oversample_buffer[it][ot]);
|
||||
/* 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);
|
||||
|
||||
/* Restore signal power by multiplying sample by amount of oversampling */
|
||||
oversample_buffer[it][ot] = smp * 4.0f;
|
||||
}
|
||||
}
|
||||
|
||||
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(it = 0;it < td*4;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];
|
||||
ALfloat smp = buffer[1][it];
|
||||
|
||||
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;
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
);
|
||||
/* 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 = 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)
|
||||
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
|
||||
)
|
||||
);
|
||||
for(i = 1;i < MAX_EFFECT_CHANNELS;i++)
|
||||
ALfilterState_copyParams(&state->filter[1][i], &state->filter[1][0]);
|
||||
|
||||
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)
|
||||
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);
|
||||
ALsizei td = mini(MAX_UPDATE_SAMPLES, SamplesToDo-base);
|
||||
|
||||
for(it = 0;it < td;it++)
|
||||
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];
|
||||
ALfloat gain = state->Gain[ft][kt];
|
||||
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
|
||||
continue;
|
||||
|
||||
for(it = 0;it < td;it++)
|
||||
SamplesOut[kt][base+it] += gain * temps[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);
|
||||
}
|
||||
|
||||
@@ -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 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)
|
||||
{
|
||||
free(state->SampleBuffer[0]);
|
||||
ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
|
||||
SET_VTABLE2(ALflangerState, ALeffectState, state);
|
||||
|
||||
state->BufferLength = 0;
|
||||
state->SampleBuffer[0] = NULL;
|
||||
state->SampleBuffer[1] = NULL;
|
||||
state->offset = 0;
|
||||
state->lfo_range = 1;
|
||||
state->waveform = FWF_Triangle;
|
||||
}
|
||||
|
||||
static ALvoid ALflangerState_Destruct(ALflangerState *state)
|
||||
{
|
||||
al_free(state->SampleBuffer[0]);
|
||||
state->SampleBuffer[0] = NULL;
|
||||
state->SampleBuffer[1] = NULL;
|
||||
|
||||
ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
|
||||
}
|
||||
|
||||
static ALboolean ALflangerState_deviceUpdate(ALflangerState *state, ALCdevice *Device)
|
||||
{
|
||||
ALuint maxlen;
|
||||
ALuint it;
|
||||
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 */
|
||||
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;
|
||||
}
|
||||
|
||||
static inline void Sinusoid(ALint *delay_left, ALint *delay_right, ALuint offset, const ALflangerState *state)
|
||||
ALsizei i;
|
||||
for(i = 0;i < todo;i++)
|
||||
{
|
||||
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;
|
||||
delays[i] = fastf2i((1.0f - fabsf(2.0f - lfo_scale*offset)) * depth) + delay;
|
||||
offset = (offset+1)%lfo_range;
|
||||
}
|
||||
}
|
||||
|
||||
#define DECL_TEMPLATE(Func) \
|
||||
static void Process##Func(ALflangerState *state, const ALuint SamplesToDo, \
|
||||
const ALfloat *restrict SamplesIn, ALfloat (*restrict out)[2]) \
|
||||
{ \
|
||||
const ALuint bufmask = state->BufferLength-1; \
|
||||
ALfloat *restrict leftbuf = state->SampleBuffer[0]; \
|
||||
ALfloat *restrict rightbuf = state->SampleBuffer[1]; \
|
||||
ALuint offset = state->offset; \
|
||||
const ALfloat feedback = state->feedback; \
|
||||
ALuint it; \
|
||||
\
|
||||
for(it = 0;it < SamplesToDo;it++) \
|
||||
{ \
|
||||
ALint delay_left, delay_right; \
|
||||
Func(&delay_left, &delay_right, offset, state); \
|
||||
\
|
||||
out[it][0] = leftbuf[(offset-delay_left)&bufmask]; \
|
||||
leftbuf[offset&bufmask] = (out[it][0]+SamplesIn[it]) * feedback; \
|
||||
\
|
||||
out[it][1] = rightbuf[(offset-delay_right)&bufmask]; \
|
||||
rightbuf[offset&bufmask] = (out[it][1]+SamplesIn[it]) * feedback; \
|
||||
\
|
||||
offset++; \
|
||||
} \
|
||||
state->offset = offset; \
|
||||
}
|
||||
|
||||
DECL_TEMPLATE(Triangle)
|
||||
DECL_TEMPLATE(Sinusoid)
|
||||
|
||||
#undef DECL_TEMPLATE
|
||||
|
||||
static ALvoid ALflangerState_process(ALflangerState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels)
|
||||
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)
|
||||
{
|
||||
ALuint it, kt;
|
||||
ALuint base;
|
||||
ALsizei i;
|
||||
for(i = 0;i < todo;i++)
|
||||
{
|
||||
delays[i] = fastf2i(sinf(lfo_scale*offset) * depth) + delay;
|
||||
offset = (offset+1)%lfo_range;
|
||||
}
|
||||
}
|
||||
|
||||
static ALvoid ALflangerState_process(ALflangerState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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); \
|
||||
dst[i] = src[i] * 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; \
|
||||
} \
|
||||
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,55 +115,79 @@ 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;
|
||||
|
||||
ComputeAmbientGains(Device, Slot->Gain, state->Gain);
|
||||
}
|
||||
|
||||
static ALvoid ALmodulatorState_process(ALmodulatorState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels)
|
||||
for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
|
||||
{
|
||||
switch(state->Waveform)
|
||||
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;
|
||||
}
|
||||
|
||||
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, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
|
||||
{
|
||||
case SINUSOID:
|
||||
ProcessSin(state, SamplesToDo, SamplesIn, SamplesOut, NumChannels);
|
||||
break;
|
||||
const ALsizei step = state->step;
|
||||
ALsizei index = state->index;
|
||||
ALsizei base;
|
||||
|
||||
case SAWTOOTH:
|
||||
ProcessSaw(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;
|
||||
|
||||
case SQUARE:
|
||||
ProcessSquare(state, SamplesToDo, SamplesIn, SamplesOut, NumChannels);
|
||||
break;
|
||||
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];
|
||||
}
|
||||
}
|
||||
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALmodulatorState)
|
||||
|
||||
DEFINE_ALEFFECTSTATE_VTABLE(ALmodulatorState);
|
||||
for(i = 0;i < td;i++)
|
||||
{
|
||||
index += step;
|
||||
index &= WAVEFORM_FRACMASK;
|
||||
}
|
||||
base += td;
|
||||
}
|
||||
state->index = index;
|
||||
}
|
||||
|
||||
|
||||
typedef struct ALmodulatorStateFactory {
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
+1620
-1016
File diff suppressed because it is too large
Load Diff
+450
-794
File diff suppressed because it is too large
Load Diff
+783
-529
File diff suppressed because it is too large
Load Diff
+33
-21
@@ -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;
|
||||
|
||||
struct Hrtf {
|
||||
RefCount ref;
|
||||
|
||||
ALuint sampleRate;
|
||||
ALsizei irSize;
|
||||
ALubyte evCount;
|
||||
|
||||
const ALubyte *azCount;
|
||||
const ALushort *evOffset;
|
||||
const ALfloat (*coeffs)[2];
|
||||
const ALubyte (*delays)[2];
|
||||
};
|
||||
|
||||
#define HRIR_BITS (7)
|
||||
#define HRIR_LENGTH (1<<HRIR_BITS)
|
||||
#define HRIR_MASK (HRIR_LENGTH-1)
|
||||
#define HRTFDELAY_BITS (20)
|
||||
#define HRTFDELAY_FRACONE (1<<HRTFDELAY_BITS)
|
||||
#define HRTFDELAY_MASK (HRTFDELAY_FRACONE-1)
|
||||
|
||||
void FreeHrtfs(void);
|
||||
|
||||
vector_HrtfEntry EnumerateHrtf(const_al_string devname);
|
||||
void FreeHrtfList(vector_HrtfEntry *list);
|
||||
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);
|
||||
|
||||
ALuint GetHrtfSampleRate(const struct Hrtf *Hrtf);
|
||||
ALuint GetHrtfIrSize(const struct Hrtf *Hrtf);
|
||||
void GetHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azimuth, ALfloat spread, ALfloat (*coeffs)[2], ALsizei *delays);
|
||||
|
||||
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);
|
||||
/**
|
||||
* 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 */
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
}
|
||||
+289
-256
@@ -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);
|
||||
|
||||
alignas(16) union ResamplerCoeffs ResampleCoeffs;
|
||||
extern inline void InitiatePositionArrays(ALsizei frac, ALint increment, ALsizei *restrict frac_arr, ALint *restrict pos_arr, ALsizei size);
|
||||
|
||||
|
||||
enum Resampler {
|
||||
PointResampler,
|
||||
LinearResampler,
|
||||
FIR4Resampler,
|
||||
FIR8Resampler,
|
||||
BSincResampler,
|
||||
|
||||
ResamplerDefault = LinearResampler
|
||||
};
|
||||
|
||||
/* FIR8 requires 3 extra samples before the current position, and 4 after. */
|
||||
static_assert(MAX_PRE_SAMPLES >= 3, "MAX_PRE_SAMPLES must be at least 3!");
|
||||
static_assert(MAX_POST_SAMPLES >= 4, "MAX_POST_SAMPLES must be at least 4!");
|
||||
/* 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!");
|
||||
|
||||
|
||||
static HrtfMixerFunc MixHrtfSamples = MixHrtf_C;
|
||||
enum Resampler ResamplerDefault = LinearResampler;
|
||||
|
||||
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_SSE
|
||||
if((CPUCapFlags&CPU_CAP_SSE))
|
||||
return MixHrtf_SSE;
|
||||
#endif
|
||||
#ifdef HAVE_NEON
|
||||
if((CPUCapFlags&CPU_CAP_NEON))
|
||||
return MixHrtf_Neon;
|
||||
#endif
|
||||
|
||||
return MixHrtf_C;
|
||||
}
|
||||
|
||||
static inline MixerFunc SelectMixer(void)
|
||||
{
|
||||
#ifdef HAVE_SSE
|
||||
if((CPUCapFlags&CPU_CAP_SSE))
|
||||
return Mix_SSE;
|
||||
#endif
|
||||
#ifdef HAVE_NEON
|
||||
if((CPUCapFlags&CPU_CAP_NEON))
|
||||
return Mix_Neon;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SSE
|
||||
if((CPUCapFlags&CPU_CAP_SSE))
|
||||
return Mix_SSE;
|
||||
#endif
|
||||
return Mix_C;
|
||||
}
|
||||
|
||||
static inline ResamplerFunc SelectResampler(enum Resampler resampler)
|
||||
RowMixerFunc SelectRowMixer(void)
|
||||
{
|
||||
#ifdef HAVE_NEON
|
||||
if((CPUCapFlags&CPU_CAP_NEON))
|
||||
return MixRow_Neon;
|
||||
#endif
|
||||
#ifdef HAVE_SSE
|
||||
if((CPUCapFlags&CPU_CAP_SSE))
|
||||
return MixRow_SSE;
|
||||
#endif
|
||||
return MixRow_C;
|
||||
}
|
||||
|
||||
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 MixHrtf_SSE;
|
||||
#endif
|
||||
return MixHrtf_C;
|
||||
}
|
||||
|
||||
static inline HrtfMixerBlendFunc SelectHrtfBlendMixer(void)
|
||||
{
|
||||
#ifdef HAVE_NEON
|
||||
if((CPUCapFlags&CPU_CAP_NEON))
|
||||
return MixHrtfBlend_Neon;
|
||||
#endif
|
||||
#ifdef HAVE_SSE
|
||||
if((CPUCapFlags&CPU_CAP_SSE))
|
||||
return MixHrtfBlend_SSE;
|
||||
#endif
|
||||
return MixHrtfBlend_C;
|
||||
}
|
||||
|
||||
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->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
|
||||
);
|
||||
if(!voice->IsHrtf)
|
||||
MixSamples(samples, parms->OutChannels, parms->OutBuffer, parms->Gains[chan],
|
||||
parms->Counter, OutPos, DstBufferSize);
|
||||
else
|
||||
MixHrtfSamples(parms->OutBuffer, samples, parms->Counter, voice->Offset,
|
||||
OutPos, IrSize, &parms->Hrtf[chan].Params,
|
||||
&parms->Hrtf[chan].State, DstBufferSize);
|
||||
{
|
||||
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
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
for(j = 0;j < Device->NumAuxSends;j++)
|
||||
if(fademix < DstBufferSize)
|
||||
{
|
||||
SendParams *parms = &voice->Send[j];
|
||||
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(send = 0;send < Device->NumAuxSends;send++)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
+100
-73
@@ -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++));
|
||||
ALsizei i;
|
||||
if(numsamples > 1)
|
||||
{
|
||||
dst[0] = filter->b0 * src[0] +
|
||||
filter->b1 * filter->x[0] +
|
||||
filter->b2 * filter->x[1] -
|
||||
filter->a1 * filter->y[0] -
|
||||
filter->a2 * filter->y[1];
|
||||
dst[1] = filter->b0 * src[1] +
|
||||
filter->b1 * src[0] +
|
||||
filter->b2 * filter->x[0] -
|
||||
filter->a1 * dst[0] -
|
||||
filter->a2 * filter->y[0];
|
||||
for(i = 2;i < numsamples;i++)
|
||||
dst[i] = filter->b0 * src[i] +
|
||||
filter->b1 * src[i-1] +
|
||||
filter->b2 * src[i-2] -
|
||||
filter->a1 * dst[i-1] -
|
||||
filter->a2 * dst[i-2];
|
||||
filter->x[0] = src[i-1];
|
||||
filter->x[1] = src[i-2];
|
||||
filter->y[0] = dst[i-1];
|
||||
filter->y[1] = dst[i-2];
|
||||
}
|
||||
|
||||
|
||||
static inline void SetupCoeffs(ALfloat (*restrict OutCoeffs)[2],
|
||||
const HrtfParams *hrtfparams,
|
||||
ALuint IrSize, ALuint Counter)
|
||||
else if(numsamples == 1)
|
||||
{
|
||||
ALuint c;
|
||||
for(c = 0;c < IrSize;c++)
|
||||
{
|
||||
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];
|
||||
filter->x[1] = filter->x[0];
|
||||
filter->x[0] = src[0];
|
||||
filter->y[1] = filter->y[0];
|
||||
filter->y[0] = dst[0];
|
||||
}
|
||||
}
|
||||
|
||||
static inline void ApplyCoeffsStep(ALuint Offset, ALfloat (*restrict Values)[2],
|
||||
const ALuint IrSize,
|
||||
ALfloat (*restrict Coeffs)[2],
|
||||
const ALfloat (*restrict CoeffStep)[2],
|
||||
|
||||
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;
|
||||
Values[off][0] += Coeffs[c][0] * left;
|
||||
Values[off][1] += Coeffs[c][1] * right;
|
||||
Coeffs[c][0] += CoeffStep[c][0];
|
||||
Coeffs[c][1] += CoeffStep[c][1];
|
||||
}
|
||||
}
|
||||
|
||||
static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2],
|
||||
const ALuint IrSize,
|
||||
ALfloat (*restrict Coeffs)[2],
|
||||
ALfloat left, ALfloat right)
|
||||
{
|
||||
ALuint c;
|
||||
for(c = 0;c < IrSize;c++)
|
||||
{
|
||||
const ALuint off = (Offset+c)&HRIR_MASK;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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));
|
||||
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;
|
||||
|
||||
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++;
|
||||
|
||||
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++;
|
||||
}
|
||||
|
||||
Delay[0] >>= HRTFDELAY_BITS;
|
||||
Delay[1] >>= HRTFDELAY_BITS;
|
||||
for(;pos < BufferSize;pos++)
|
||||
{
|
||||
hrtfstate->History[Offset&HRTF_HISTORY_MASK] = data[pos];
|
||||
left = hrtfstate->History[(Offset-Delay[0])&HRTF_HISTORY_MASK];
|
||||
right = hrtfstate->History[(Offset-Delay[1])&HRTF_HISTORY_MASK];
|
||||
|
||||
hrtfstate->Values[(Offset+IrSize)&HRIR_MASK][0] = 0.0f;
|
||||
hrtfstate->Values[(Offset+IrSize)&HRIR_MASK][1] = 0.0f;
|
||||
Offset++;
|
||||
hrtfstate->Values[(Offset+IrSize-1)&HRIR_MASK][0] = 0.0f;
|
||||
hrtfstate->Values[(Offset+IrSize-1)&HRIR_MASK][1] = 0.0f;
|
||||
|
||||
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++;
|
||||
*(LeftOut++) += hrtfstate->Values[Offset&HRIR_MASK][0];
|
||||
*(RightOut++) += hrtfstate->Values[Offset&HRIR_MASK][1];
|
||||
|
||||
gain += gainstep;
|
||||
Offset++;
|
||||
}
|
||||
hrtfparams->Gain = gain;
|
||||
}
|
||||
|
||||
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->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++);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,34 +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);
|
||||
}
|
||||
for(c = 0;c < IrSize;c += 2)
|
||||
{
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
static inline void ApplyCoeffsStep(ALuint Offset, ALfloat (*restrict Values)[2],
|
||||
const ALuint IrSize,
|
||||
ALfloat (*restrict Coeffs)[2],
|
||||
const ALfloat (*restrict CoeffStep)[2],
|
||||
if(i < numsamples)
|
||||
{
|
||||
/* NOTE: These four elements represent the position *after* the last
|
||||
* four samples, so the lowest element is the next position to
|
||||
* resample.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
const ALfloat *Resample_fir4_32_Neon(const InterpState* UNUSED(state),
|
||||
const ALfloat *restrict src, ALsizei frac, ALint increment,
|
||||
ALfloat *restrict dst, ALsizei numsamples)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
if(i < numsamples)
|
||||
{
|
||||
/* NOTE: These four elements represent the position *after* the last
|
||||
* four samples, so the lowest element is the next position to
|
||||
* resample.
|
||||
*/
|
||||
ALint pos = pos_[0];
|
||||
frac = frac_[0];
|
||||
do {
|
||||
dst[i] = resample_fir4(src[pos], src[pos+1], src[pos+2], src[pos+3], frac);
|
||||
|
||||
frac += increment;
|
||||
pos += frac>>FRACTIONBITS;
|
||||
frac &= FRACTIONMASK;
|
||||
} while(++i < numsamples);
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
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(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);
|
||||
@@ -42,41 +203,12 @@ static inline void ApplyCoeffsStep(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;
|
||||
float32x4_t vals = vcombine_f32(vld1_f32((float32_t*)&Values[o0][0]),
|
||||
vld1_f32((float32_t*)&Values[o1][0]));
|
||||
float32x4_t coefs = vld1q_f32((float32_t*)&Coeffs[c][0]);
|
||||
float32x4_t deltas = vld1q_f32(&CoeffStep[c][0]);
|
||||
|
||||
vals = vmlaq_f32(vals, coefs, leftright4);
|
||||
coefs = vaddq_f32(coefs, deltas);
|
||||
|
||||
vst1_f32((float32_t*)&Values[o0][0], vget_low_f32(vals));
|
||||
vst1_f32((float32_t*)&Values[o1][0], vget_high_f32(vals));
|
||||
vst1q_f32(&Coeffs[c][0], coefs);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2],
|
||||
const ALuint IrSize,
|
||||
ALfloat (*restrict Coeffs)[2],
|
||||
ALfloat left, ALfloat right)
|
||||
{
|
||||
ALuint c;
|
||||
float32x4_t leftright4;
|
||||
{
|
||||
float32x2_t leftright2 = vdup_n_f32(0.0);
|
||||
leftright2 = vset_lane_f32(left, leftright2, 0);
|
||||
leftright2 = vset_lane_f32(right, leftright2, 1);
|
||||
leftright4 = vcombine_f32(leftright2, leftright2);
|
||||
}
|
||||
for(c = 0;c < IrSize;c += 2)
|
||||
{
|
||||
const ALuint o0 = (Offset+c)&HRIR_MASK;
|
||||
const ALuint o1 = (o0+1)&HRIR_MASK;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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 */
|
||||
+1039
-325
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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)
|
||||
#define VECTOR_DEINIT(_x) do { al_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_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)
|
||||
#define VECTOR_BEGIN(_x) ((_x) ? (_x)->Data + 0 : NULL)
|
||||
#define VECTOR_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_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 */
|
||||
|
||||
+418
-213
File diff suppressed because it is too large
Load Diff
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -17,10 +17,8 @@ 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,
|
||||
};
|
||||
@@ -32,13 +30,13 @@ enum UserFmtChannels {
|
||||
UserFmtX51 = AL_5POINT1_SOFT, /* (WFX order) */
|
||||
UserFmtX61 = AL_6POINT1_SOFT, /* (WFX order) */
|
||||
UserFmtX71 = AL_7POINT1_SOFT, /* (WFX order) */
|
||||
UserFmtBFormat2D = 0x10000000, /* WXY */
|
||||
UserFmtBFormat3D, /* WXYZ */
|
||||
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,6 +78,7 @@ typedef struct ALbuffer {
|
||||
|
||||
enum FmtChannels FmtChannels;
|
||||
enum FmtType FmtType;
|
||||
ALuint BytesAlloc;
|
||||
|
||||
enum UserFmtChannels OriginalChannels;
|
||||
enum UserFmtType OriginalType;
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stddef.h>
|
||||
#include <stdarg.h>
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
@@ -20,6 +21,124 @@
|
||||
#include "AL/alc.h"
|
||||
#include "AL/alext.h"
|
||||
|
||||
#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
|
||||
|
||||
/* 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"
|
||||
@@ -30,24 +149,59 @@
|
||||
#endif
|
||||
|
||||
|
||||
#include "static_assert.h"
|
||||
#include "align.h"
|
||||
#include "atomic.h"
|
||||
#include "uintmap.h"
|
||||
#include "vector.h"
|
||||
#include "alstring.h"
|
||||
|
||||
#include "hrtf.h"
|
||||
|
||||
#ifndef ALC_SOFT_device_clock
|
||||
#define ALC_SOFT_device_clock 1
|
||||
typedef int64_t ALCint64SOFT;
|
||||
typedef uint64_t ALCuint64SOFT;
|
||||
#define ALC_DEVICE_CLOCK_SOFT 0x1600
|
||||
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);
|
||||
#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
|
||||
|
||||
|
||||
@@ -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
|
||||
{
|
||||
@@ -430,19 +680,25 @@ struct ALCdevice_struct
|
||||
enum DevFmtChannels FmtChans;
|
||||
enum DevFmtType FmtType;
|
||||
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;
|
||||
|
||||
ALCuint NumMonoSources;
|
||||
ALCuint NumStereoSources;
|
||||
ALuint NumAuxSends;
|
||||
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;
|
||||
/* HRTF state and info */
|
||||
DirectHrtfState *Hrtf;
|
||||
al_string HrtfName;
|
||||
struct Hrtf *HrtfHandle;
|
||||
vector_EnumeratedHrtf HrtfList;
|
||||
ALCenum HrtfStatus;
|
||||
|
||||
// Stereo-to-binaural filter
|
||||
/* 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];
|
||||
|
||||
/* Dry path buffer mix. */
|
||||
alignas(16) ALfloat (*DryBuffer)[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];
|
||||
|
||||
/* 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;
|
||||
|
||||
volatile enum DistanceModel DistanceModel;
|
||||
volatile ALboolean SourceDistanceModel;
|
||||
ALfloat DopplerFactor;
|
||||
ALfloat DopplerVelocity;
|
||||
ALfloat SpeedOfSound;
|
||||
ATOMIC(ALenum) DeferUpdates;
|
||||
|
||||
volatile ALfloat DopplerFactor;
|
||||
volatile ALfloat DopplerVelocity;
|
||||
volatile ALfloat SpeedOfSound;
|
||||
volatile ALenum DeferUpdates;
|
||||
RWLock PropLock;
|
||||
|
||||
struct ALvoice *Voices;
|
||||
/* Counter for the pre-mixing updates, in 31.1 fixed point (lowest bit
|
||||
* indicates if updates are currently happening).
|
||||
*/
|
||||
RefCount UpdateCount;
|
||||
ATOMIC(ALenum) HoldUpdates;
|
||||
|
||||
ALfloat GainBoost;
|
||||
|
||||
struct ALvoice **Voices;
|
||||
ALsizei VoiceCount;
|
||||
ALsizei MaxVoices;
|
||||
|
||||
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,15 +1042,42 @@ 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
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -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;
|
||||
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;
|
||||
|
||||
volatile ALboolean DryGainHFAuto;
|
||||
volatile ALboolean WetGainAuto;
|
||||
volatile ALboolean WetGainHFAuto;
|
||||
volatile ALfloat OuterGainHF;
|
||||
ALboolean DryGainHFAuto;
|
||||
ALboolean WetGainAuto;
|
||||
ALboolean WetGainHFAuto;
|
||||
ALfloat OuterGainHF;
|
||||
|
||||
volatile ALfloat AirAbsorptionFactor;
|
||||
volatile ALfloat RoomRolloffFactor;
|
||||
volatile ALfloat DopplerFactor;
|
||||
ALfloat AirAbsorptionFactor;
|
||||
ALfloat RoomRolloffFactor;
|
||||
ALfloat DopplerFactor;
|
||||
|
||||
volatile ALfloat Radius;
|
||||
|
||||
/**
|
||||
* Last user-specified offset, and the offset type (bytes, samples, or
|
||||
* seconds).
|
||||
/* NOTE: Stereo pan angles are specified in radians, counter-clockwise
|
||||
* rather than clockwise.
|
||||
*/
|
||||
ALdouble Offset;
|
||||
ALenum OffsetType;
|
||||
ALfloat StereoPan[2];
|
||||
|
||||
/** 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.
|
||||
*/
|
||||
ALuint position;
|
||||
ALuint position_fraction;
|
||||
|
||||
/** Source Buffer Queue info. */
|
||||
ATOMIC(ALbufferlistitem*) queue;
|
||||
ATOMIC(ALbufferlistitem*) current_buffer;
|
||||
RWLock queue_lock;
|
||||
|
||||
/** 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;
|
||||
|
||||
/** Source needs to update its mixing parameters. */
|
||||
ATOMIC(ALenum) NeedsUpdate;
|
||||
/**
|
||||
* Last user-specified offset, and the offset type (bytes, samples, or
|
||||
* seconds).
|
||||
*/
|
||||
ALdouble Offset;
|
||||
ALenum OffsetType;
|
||||
|
||||
/** Source type (static, streaming, or undetermined) */
|
||||
ALint SourceType;
|
||||
|
||||
/** Source state (initial, playing, paused, or stopped) */
|
||||
ATOMIC(ALenum) state;
|
||||
|
||||
/** Source Buffer Queue head. */
|
||||
RWLock queue_lock;
|
||||
ALbufferlistitem *queue;
|
||||
|
||||
ATOMIC_FLAG PropsClean;
|
||||
|
||||
/** Self ID */
|
||||
ALuint id;
|
||||
} ALsource;
|
||||
|
||||
inline struct ALsource *LookupSource(ALCcontext *context, ALuint id)
|
||||
{ return (struct ALsource*)LookupUIntMapKey(&context->SourceMap, id); }
|
||||
inline struct ALsource *RemoveSource(ALCcontext *context, ALuint id)
|
||||
{ return (struct ALsource*)RemoveUIntMapKey(&context->SourceMap, id); }
|
||||
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); }
|
||||
|
||||
ALvoid SetSourceState(ALsource *Source, ALCcontext *Context, ALenum state);
|
||||
ALboolean ApplyOffset(ALsource *Source);
|
||||
inline struct ALsource *LookupSource(ALCcontext *context, ALuint id)
|
||||
{ return (struct ALsource*)LookupUIntMapKeyNoLock(&context->SourceMap, id); }
|
||||
inline struct ALsource *RemoveSource(ALCcontext *context, ALuint id)
|
||||
{ return (struct ALsource*)RemoveUIntMapKeyNoLock(&context->SourceMap, id); }
|
||||
|
||||
void UpdateAllSourceProps(ALCcontext *context);
|
||||
|
||||
ALvoid ReleaseALSources(ALCcontext *Context);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
/* 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;
|
||||
|
||||
struct {
|
||||
enum ActiveFilters ActiveType;
|
||||
ALfilterState LowPass;
|
||||
ALfilterState HighPass;
|
||||
} Filters[MAX_INPUT_CHANNELS];
|
||||
|
||||
NfcFilter NFCtrlFilter[MAX_AMBI_ORDER];
|
||||
|
||||
struct {
|
||||
HrtfParams Params;
|
||||
HrtfParams Old;
|
||||
HrtfParams Target;
|
||||
HrtfState State;
|
||||
} Hrtf[MAX_INPUT_CHANNELS];
|
||||
MixGains Gains[MAX_INPUT_CHANNELS][MAX_OUTPUT_CHANNELS];
|
||||
} Hrtf;
|
||||
|
||||
struct {
|
||||
ALfloat Current[MAX_OUTPUT_CHANNELS];
|
||||
ALfloat Target[MAX_OUTPUT_CHANNELS];
|
||||
} Gains;
|
||||
} DirectParams;
|
||||
|
||||
typedef struct SendParams {
|
||||
ALfloat (*OutBuffer)[BUFFERSIZE];
|
||||
|
||||
ALboolean Moving;
|
||||
ALuint Counter;
|
||||
|
||||
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];
|
||||
struct {
|
||||
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;
|
||||
|
||||
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);
|
||||
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;
|
||||
|
||||
/**
|
||||
* 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)
|
||||
inline ALfloat resample_fir4(ALfloat val0, ALfloat val1, ALfloat val2, ALfloat val3, ALsizei frac)
|
||||
{
|
||||
const ALfloat *k = ResampleCoeffs.FIR4[frac];
|
||||
return k[0]*val0 + k[1]*val1 + k[2]*val2 + k[3]*val3;
|
||||
}
|
||||
inline ALfloat resample_fir8(ALfloat val0, ALfloat val1, ALfloat val2, ALfloat val3, ALfloat val4, ALfloat val5, ALfloat val6, ALfloat val7, ALuint frac)
|
||||
{
|
||||
const ALfloat *k = ResampleCoeffs.FIR8[frac];
|
||||
return k[0]*val0 + k[1]*val1 + k[2]*val2 + k[3]*val3 +
|
||||
k[4]*val4 + k[5]*val5 + k[6]*val6 + k[7]*val7;
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* CalcAnglePairwiseCoeffs
|
||||
*
|
||||
* 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 CalcAnglePairwiseCoeffs(ALfloat azimuth, ALfloat elevation, ALfloat spread, ALfloat coeffs[MAX_AMBI_COEFFS]);
|
||||
|
||||
/**
|
||||
* ComputeAmbientGains
|
||||
*
|
||||
* Sets channel gains for ambient, omni-directional sounds.
|
||||
* Computes channel gains for ambient, omni-directional sounds.
|
||||
*/
|
||||
void ComputeAmbientGains(const ALCdevice *device, 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]);
|
||||
|
||||
/**
|
||||
* ComputeBFormatGains
|
||||
* ComputePanningGains
|
||||
*
|
||||
* 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 panning gains using the given channel decoder coefficients and the
|
||||
* pre-calculated direction or angle coefficients.
|
||||
*/
|
||||
void ComputeBFormatGains(const ALCdevice *device, const ALfloat mtx[4], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]);
|
||||
#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]);
|
||||
|
||||
/**
|
||||
* 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 UpdateContextSources(ALCcontext *context);
|
||||
ALboolean MixSource(struct ALvoice *voice, struct ALsource *Source, ALCdevice *Device, ALsizei SamplesToDo);
|
||||
|
||||
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);
|
||||
|
||||
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;
|
||||
|
||||
@@ -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" */
|
||||
|
||||
@@ -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;
|
||||
|
||||
if(newtype != EffectSlot->EffectType)
|
||||
{
|
||||
struct ALeffectslotProps *props;
|
||||
ALeffectState *State;
|
||||
FPUCtl oldMode;
|
||||
|
||||
if(newtype != EffectSlot->Effect.Type)
|
||||
{
|
||||
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;
|
||||
if(!State) return AL_OUT_OF_MEMORY;
|
||||
|
||||
SetMixerFPUMode(&oldMode);
|
||||
|
||||
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));
|
||||
|
||||
@@ -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);
|
||||
/* Round up to the next 16-byte multiple. This could reallocate only when
|
||||
* increasing or the new size is less than half the current, but then the
|
||||
* buffer's AL_SIZE would not be very reliable for accounting buffer memory
|
||||
* usage, and reporting the real size could cause problems for apps that
|
||||
* use AL_SIZE to try to get the buffer's play length.
|
||||
*/
|
||||
newsize = (newsize+15) & ~0xf;
|
||||
if(newsize != ALBuf->BytesAlloc)
|
||||
{
|
||||
void *temp = al_calloc(16, (size_t)newsize);
|
||||
if(!temp && newsize)
|
||||
{
|
||||
WriteUnlock(&ALBuf->lock);
|
||||
return AL_OUT_OF_MEMORY;
|
||||
}
|
||||
al_free(ALBuf->data);
|
||||
ALBuf->data = temp;
|
||||
ALBuf->BytesAlloc = (ALuint)newsize;
|
||||
}
|
||||
|
||||
if(data != NULL)
|
||||
ConvertData(ALBuf->data, (enum UserFmtType)DstType, data, SrcType, NewChannels, frames, align);
|
||||
@@ -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,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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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 }
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+1066
-599
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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,320 +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]);
|
||||
}
|
||||
/* 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; }
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
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 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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); }
|
||||
|
||||
#undef DECL_TEMPLATE
|
||||
|
||||
/* Define alternate-sign functions. */
|
||||
#define DECL_TEMPLATE(T1, T2, O) \
|
||||
static inline T1 Conv_##T1##_##T2(T2 val) { return (T1)val - O; } \
|
||||
static inline T2 Conv_##T2##_##T1(T1 val) { return (T2)val + O; }
|
||||
|
||||
DECL_TEMPLATE(ALbyte, ALubyte, 128);
|
||||
DECL_TEMPLATE(ALshort, ALushort, 32768);
|
||||
DECL_TEMPLATE(ALint, ALuint, 2147483648u);
|
||||
|
||||
#undef DECL_TEMPLATE
|
||||
|
||||
/* Define int-type to int-type functions */
|
||||
#define DECL_TEMPLATE(T, ST, UT, SH) \
|
||||
static inline T Conv_##T##_##ST(ST val){ return val >> SH; } \
|
||||
static inline T Conv_##T##_##UT(UT val){ return Conv_##ST##_##UT(val) >> SH; }\
|
||||
static inline ST Conv_##ST##_##T(T val){ return val << SH; } \
|
||||
static inline UT Conv_##UT##_##T(T val){ return Conv_##UT##_##ST(val << SH); }
|
||||
|
||||
#define DECL_TEMPLATE2(T1, T2, SH) \
|
||||
DECL_TEMPLATE(AL##T1, AL##T2, ALu##T2, SH) \
|
||||
DECL_TEMPLATE(ALu##T1, ALu##T2, AL##T2, SH)
|
||||
|
||||
DECL_TEMPLATE2(byte, short, 8)
|
||||
DECL_TEMPLATE2(short, int, 16)
|
||||
DECL_TEMPLATE2(byte, int, 24)
|
||||
|
||||
#undef DECL_TEMPLATE2
|
||||
#undef DECL_TEMPLATE
|
||||
|
||||
/* Define int-type to fp functions */
|
||||
#define DECL_TEMPLATE(T, ST, UT, OP) \
|
||||
static inline T Conv_##T##_##ST(ST val) { return (T)val * OP; } \
|
||||
static inline T Conv_##T##_##UT(UT val) { return (T)Conv_##ST##_##UT(val) * OP; }
|
||||
|
||||
#define DECL_TEMPLATE2(T1, T2, OP) \
|
||||
DECL_TEMPLATE(T1, AL##T2, ALu##T2, OP)
|
||||
|
||||
DECL_TEMPLATE2(ALfloat, byte, (1.0f/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 ALmulaw Conv_ALmulaw_##T(T val) \
|
||||
{ return EncodeMuLaw(Conv_ALshort_##T(val)); }
|
||||
|
||||
DECL_TEMPLATE(ALbyte)
|
||||
DECL_TEMPLATE(ALubyte)
|
||||
DECL_TEMPLATE(ALshort)
|
||||
DECL_TEMPLATE(ALushort)
|
||||
DECL_TEMPLATE(ALint)
|
||||
DECL_TEMPLATE(ALuint)
|
||||
DECL_TEMPLATE(ALfloat)
|
||||
DECL_TEMPLATE(ALdouble)
|
||||
static inline ALmulaw Conv_ALmulaw_ALmulaw(ALmulaw val)
|
||||
{ return val; }
|
||||
DECL_TEMPLATE(ALalaw)
|
||||
DECL_TEMPLATE(ALbyte3)
|
||||
DECL_TEMPLATE(ALubyte3)
|
||||
|
||||
#undef DECL_TEMPLATE
|
||||
|
||||
#define DECL_TEMPLATE(T) \
|
||||
{ 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)); }
|
||||
{ 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)
|
||||
@@ -821,53 +621,14 @@ 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)
|
||||
|
||||
#undef DECL_TEMPLATE
|
||||
|
||||
#define DECL_TEMPLATE(T) \
|
||||
static inline ALbyte3 Conv_ALbyte3_##T(T val) \
|
||||
{ return EncodeByte3(Conv_ALint_##T(val)>>8); }
|
||||
|
||||
DECL_TEMPLATE(ALbyte)
|
||||
DECL_TEMPLATE(ALubyte)
|
||||
DECL_TEMPLATE(ALshort)
|
||||
DECL_TEMPLATE(ALushort)
|
||||
DECL_TEMPLATE(ALint)
|
||||
DECL_TEMPLATE(ALuint)
|
||||
DECL_TEMPLATE(ALfloat)
|
||||
DECL_TEMPLATE(ALdouble)
|
||||
DECL_TEMPLATE(ALmulaw)
|
||||
DECL_TEMPLATE(ALalaw)
|
||||
static inline ALbyte3 Conv_ALbyte3_ALbyte3(ALbyte3 val)
|
||||
{ return val; }
|
||||
DECL_TEMPLATE(ALubyte3)
|
||||
|
||||
#undef DECL_TEMPLATE
|
||||
|
||||
#define DECL_TEMPLATE(T) \
|
||||
static inline ALubyte3 Conv_ALubyte3_##T(T val) \
|
||||
{ return EncodeUByte3(Conv_ALuint_##T(val)>>8); }
|
||||
|
||||
DECL_TEMPLATE(ALbyte)
|
||||
DECL_TEMPLATE(ALubyte)
|
||||
DECL_TEMPLATE(ALshort)
|
||||
DECL_TEMPLATE(ALushort)
|
||||
DECL_TEMPLATE(ALint)
|
||||
DECL_TEMPLATE(ALuint)
|
||||
DECL_TEMPLATE(ALfloat)
|
||||
DECL_TEMPLATE(ALdouble)
|
||||
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) \
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
##
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -8,24 +8,30 @@
|
||||
# DSOUND_LIBRARY - the dsound library
|
||||
#
|
||||
|
||||
find_path(DSOUND_INCLUDE_DIR
|
||||
NAMES dsound.h
|
||||
PATHS "${DXSDK_DIR}"
|
||||
PATH_SUFFIXES include
|
||||
DOC "The DirectSound include directory"
|
||||
)
|
||||
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}" ${WINSDK_INCLUDE_DIRS}
|
||||
PATH_SUFFIXES include
|
||||
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})
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 <rpavlik@iastate.edu> <abiryan@ryand.net>
|
||||
# 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 +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
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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);
|
||||
|
||||
@@ -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 +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,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 have changed in this diff Show More
Reference in New Issue
Block a user