Compare commits

..

11 Commits

Author SHA1 Message Date
Chris Robinson ce6076091b Release 1.18.2 2017-09-24 06:41:50 -07:00
Chris Robinson dba8166b67 Update ChangeLog with JACK fix 2017-09-23 15:12:50 -07:00
Chris Robinson 4fa9c8e4b4 Restore the original JACK message callback when possible 2017-09-23 15:12:38 -07:00
Chris Robinson 912c0cc9eb Update ChangeLog 2017-09-22 07:18:31 -07:00
Daniel Scharrer 9a67aa8530 Fix build on Gentoo FreeBSD with freebsd-lib 9.1 2017-09-22 07:18:14 -07:00
Chris Robinson 45e757ea12 Update ChangeLog 2017-09-19 10:16:53 -07:00
Chris Robinson 026f96c2a3 Manually save and restore the FPU rounding mode on Windows
Apparently there is a bug with at least MinGW-W64 where fegetenv and fesetenv
do not properly save and restore the FPU rounding mode, resulting in the
rounding mode remaining as round-to-zero after certain function calls. I do not
know if this also affects MSVC, but better safe than sorry for now.
2017-09-19 10:05:41 -07:00
Chris Robinson 631624fa58 Always link to ossaudio when found 2017-09-19 10:05:41 -07:00
Chris Robinson 51e11fdb9c Handle libossaudio as an optional OSS library 2017-09-19 10:05:41 -07:00
Chris Robinson eaf3b94143 Add a check for pthread_setname_np with three parameters
As found in NetBSD.
2017-09-19 10:05:41 -07:00
Chris Robinson 84eca96dad Don't hide -msse and -mfpu=neon checks behind a not-msvc check
Apparently Clang gets reported as being MSVC on Windows, but still needs the
GCC switches to enable SSE code generation.
2017-09-19 10:05:41 -07:00
179 changed files with 80421 additions and 1575 deletions
+5 -7
View File
@@ -1,9 +1,7 @@
build*/
build
winbuild
win64build
## kdevelop
*.kdev4
## qt-creator
CMakeLists.txt.user*
include/SLES
include/sndio.h
include/sys
openal-soft.kdev4
+70
View File
@@ -0,0 +1,70 @@
language: c
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
+4649
View File
File diff suppressed because it is too large Load Diff
+1830
View File
File diff suppressed because it is too large Load Diff
+652
View File
@@ -0,0 +1,652 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 1999-2007 by authors.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#ifdef _WIN32
#ifdef __MINGW32__
#define _WIN32_IE 0x501
#else
#define _WIN32_IE 0x400
#endif
#endif
#include "config.h"
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#ifdef _WIN32_IE
#include <windows.h>
#include <shlobj.h>
#endif
#include "alMain.h"
#include "compat.h"
#include "bool.h"
typedef struct ConfigEntry {
char *key;
char *value;
} ConfigEntry;
typedef struct ConfigBlock {
ConfigEntry *entries;
unsigned int entryCount;
} ConfigBlock;
static ConfigBlock cfgBlock;
static char *lstrip(char *line)
{
while(isspace(line[0]))
line++;
return line;
}
static char *rstrip(char *line)
{
size_t len = strlen(line);
while(len > 0 && isspace(line[len-1]))
len--;
line[len] = 0;
return line;
}
static int readline(FILE *f, char **output, size_t *maxlen)
{
size_t len = 0;
int c;
while((c=fgetc(f)) != EOF && (c == '\r' || c == '\n'))
;
if(c == EOF)
return 0;
do {
if(len+1 >= *maxlen)
{
void *temp = NULL;
size_t newmax;
newmax = (*maxlen ? (*maxlen)<<1 : 32);
if(newmax > *maxlen)
temp = realloc(*output, newmax);
if(!temp)
{
ERR("Failed to realloc "SZFMT" bytes from "SZFMT"!\n", newmax, *maxlen);
return 0;
}
*output = temp;
*maxlen = newmax;
}
(*output)[len++] = c;
(*output)[len] = '\0';
} while((c=fgetc(f)) != EOF && c != '\r' && c != '\n');
return 1;
}
static char *expdup(const char *str)
{
char *output = NULL;
size_t maxlen = 0;
size_t len = 0;
while(*str != '\0')
{
const char *addstr;
size_t addstrlen;
size_t i;
if(str[0] != '$')
{
const char *next = strchr(str, '$');
addstr = str;
addstrlen = next ? (size_t)(next-str) : strlen(str);
str += addstrlen;
}
else
{
str++;
if(*str == '$')
{
const char *next = strchr(str+1, '$');
addstr = str;
addstrlen = next ? (size_t)(next-str) : strlen(str);
str += addstrlen;
}
else
{
bool hasbraces;
char envname[1024];
size_t k = 0;
hasbraces = (*str == '{');
if(hasbraces) str++;
while((isalnum(*str) || *str == '_') && k < sizeof(envname)-1)
envname[k++] = *(str++);
envname[k++] = '\0';
if(hasbraces && *str != '}')
continue;
if(hasbraces) str++;
if((addstr=getenv(envname)) == NULL)
continue;
addstrlen = strlen(addstr);
}
}
if(addstrlen == 0)
continue;
if(addstrlen >= maxlen-len)
{
void *temp = NULL;
size_t newmax;
newmax = len+addstrlen+1;
if(newmax > maxlen)
temp = realloc(output, newmax);
if(!temp)
{
ERR("Failed to realloc "SZFMT" bytes from "SZFMT"!\n", newmax, maxlen);
return output;
}
output = temp;
maxlen = newmax;
}
for(i = 0;i < addstrlen;i++)
output[len++] = addstr[i];
output[len] = '\0';
}
return output ? output : calloc(1, 1);
}
static void LoadConfigFromFile(FILE *f)
{
char curSection[128] = "";
char *buffer = NULL;
size_t maxlen = 0;
ConfigEntry *ent;
while(readline(f, &buffer, &maxlen))
{
char *line, *comment;
char key[256] = "";
char value[256] = "";
line = rstrip(lstrip(buffer));
if(!line[0]) continue;
if(line[0] == '[')
{
char *section = line+1;
char *endsection;
endsection = strchr(section, ']');
if(!endsection || section == endsection)
{
ERR("config parse error: bad line \"%s\"\n", line);
continue;
}
if(endsection[1] != 0)
{
char *end = endsection+1;
while(isspace(*end))
++end;
if(*end != 0 && *end != '#')
{
ERR("config parse error: bad line \"%s\"\n", line);
continue;
}
}
*endsection = 0;
if(strcasecmp(section, "general") == 0)
curSection[0] = 0;
else
{
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;
}
continue;
}
comment = strchr(line, '#');
if(comment) *(comment++) = 0;
if(!line[0]) continue;
if(sscanf(line, "%255[^=] = \"%255[^\"]\"", key, value) == 2 ||
sscanf(line, "%255[^=] = '%255[^\']'", key, value) == 2 ||
sscanf(line, "%255[^=] = %255[^\n]", key, value) == 2)
{
/* sscanf doesn't handle '' or "" as empty values, so clip it
* manually. */
if(strcmp(value, "\"\"") == 0 || strcmp(value, "''") == 0)
value[0] = 0;
}
else if(sscanf(line, "%255[^=] %255[=]", key, value) == 2)
{
/* Special case for 'key =' */
value[0] = 0;
}
else
{
ERR("config parse error: malformed option line: \"%s\"\n\n", line);
continue;
}
rstrip(key);
if(curSection[0] != 0)
{
size_t len = strlen(curSection);
memmove(&key[len+1], key, sizeof(key)-1-len);
key[len] = '/';
memcpy(key, curSection, len);
}
/* Check if we already have this option set */
ent = cfgBlock.entries;
while((unsigned int)(ent-cfgBlock.entries) < cfgBlock.entryCount)
{
if(strcasecmp(ent->key, key) == 0)
break;
ent++;
}
if((unsigned int)(ent-cfgBlock.entries) >= cfgBlock.entryCount)
{
/* Allocate a new option entry */
ent = realloc(cfgBlock.entries, (cfgBlock.entryCount+1)*sizeof(ConfigEntry));
if(!ent)
{
ERR("config parse error: error reallocating config entries\n");
continue;
}
cfgBlock.entries = ent;
ent = cfgBlock.entries + cfgBlock.entryCount;
cfgBlock.entryCount++;
ent->key = strdup(key);
ent->value = NULL;
}
free(ent->value);
ent->value = expdup(value);
TRACE("found '%s' = '%s'\n", ent->key, ent->value);
}
free(buffer);
}
#ifdef _WIN32
void ReadALConfig(void)
{
WCHAR buffer[PATH_MAX];
const WCHAR *str;
al_string ppath;
FILE *f;
if(SHGetSpecialFolderPathW(NULL, buffer, CSIDL_APPDATA, FALSE) != FALSE)
{
al_string filepath = AL_STRING_INIT_STATIC();
alstr_copy_wcstr(&filepath, buffer);
alstr_append_cstr(&filepath, "\\alsoft.ini");
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);
}
}
if((str=_wgetenv(L"ALSOFT_CONF")) != NULL && *str)
{
al_string filepath = AL_STRING_INIT_STATIC();
alstr_copy_wcstr(&filepath, str);
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);
}
alstr_reset(&ppath);
}
#else
void ReadALConfig(void)
{
char buffer[PATH_MAX];
const char *str;
al_string ppath;
FILE *f;
str = "/etc/openal/alsoft.conf";
TRACE("Loading config %s...\n", str);
f = al_fopen(str, "r");
if(f)
{
LoadConfigFromFile(f);
fclose(f);
}
if(!(str=getenv("XDG_CONFIG_DIRS")) || str[0] == 0)
str = "/etc/xdg";
strncpy(buffer, str, sizeof(buffer)-1);
buffer[sizeof(buffer)-1] = 0;
/* Go through the list in reverse, since "the order of base directories
* denotes their importance; the first directory listed is the most
* important". Ergo, we need to load the settings from the later dirs
* first so that the settings in the earlier dirs override them.
*/
while(1)
{
char *next = strrchr(buffer, ':');
if(next) *(next++) = 0;
else next = buffer;
if(next[0] != '/')
WARN("Ignoring XDG config dir: %s\n", next);
else
{
size_t len = strlen(next);
strncpy(next+len, "/alsoft.conf", buffer+sizeof(buffer)-next-len);
buffer[sizeof(buffer)-1] = 0;
TRACE("Loading config %s...\n", next);
f = al_fopen(next, "r");
if(f)
{
LoadConfigFromFile(f);
fclose(f);
}
}
if(next == buffer)
break;
}
if((str=getenv("HOME")) != NULL && *str)
{
snprintf(buffer, sizeof(buffer), "%s/.alsoftrc", str);
TRACE("Loading config %s...\n", buffer);
f = al_fopen(buffer, "r");
if(f)
{
LoadConfigFromFile(f);
fclose(f);
}
}
if((str=getenv("XDG_CONFIG_HOME")) != NULL && str[0] != 0)
snprintf(buffer, sizeof(buffer), "%s/%s", str, "alsoft.conf");
else
{
buffer[0] = 0;
if((str=getenv("HOME")) != NULL && str[0] != 0)
snprintf(buffer, sizeof(buffer), "%s/.config/%s", str, "alsoft.conf");
}
if(buffer[0] != 0)
{
TRACE("Loading config %s...\n", buffer);
f = al_fopen(buffer, "r");
if(f)
{
LoadConfigFromFile(f);
fclose(f);
}
}
ppath = GetProcPath();
if(!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);
f = al_fopen(str, "r");
if(f)
{
LoadConfigFromFile(f);
fclose(f);
}
}
alstr_reset(&ppath);
}
#endif
void FreeALConfig(void)
{
unsigned int i;
for(i = 0;i < cfgBlock.entryCount;i++)
{
free(cfgBlock.entries[i].key);
free(cfgBlock.entries[i].value);
}
free(cfgBlock.entries);
}
const char *GetConfigValue(const char *devName, const char *blockName, const char *keyName, const char *def)
{
unsigned int i;
char key[256];
if(!keyName)
return def;
if(blockName && strcasecmp(blockName, "general") != 0)
{
if(devName)
snprintf(key, sizeof(key), "%s/%s/%s", blockName, devName, keyName);
else
snprintf(key, sizeof(key), "%s/%s", blockName, keyName);
}
else
{
if(devName)
snprintf(key, sizeof(key), "%s/%s", devName, keyName);
else
{
strncpy(key, keyName, sizeof(key)-1);
key[sizeof(key)-1] = 0;
}
}
for(i = 0;i < cfgBlock.entryCount;i++)
{
if(strcmp(cfgBlock.entries[i].key, key) == 0)
{
TRACE("Found %s = \"%s\"\n", key, cfgBlock.entries[i].value);
if(cfgBlock.entries[i].value[0])
return cfgBlock.entries[i].value;
return def;
}
}
if(!devName)
{
TRACE("Key %s not found\n", key);
return def;
}
return GetConfigValue(NULL, blockName, keyName, def);
}
int ConfigValueExists(const char *devName, const char *blockName, const char *keyName)
{
const char *val = GetConfigValue(devName, blockName, keyName, "");
return !!val[0];
}
int ConfigValueStr(const char *devName, const char *blockName, const char *keyName, const char **ret)
{
const char *val = GetConfigValue(devName, blockName, keyName, "");
if(!val[0]) return 0;
*ret = val;
return 1;
}
int ConfigValueInt(const char *devName, const char *blockName, const char *keyName, int *ret)
{
const char *val = GetConfigValue(devName, blockName, keyName, "");
if(!val[0]) return 0;
*ret = strtol(val, NULL, 0);
return 1;
}
int ConfigValueUInt(const char *devName, const char *blockName, const char *keyName, unsigned int *ret)
{
const char *val = GetConfigValue(devName, blockName, keyName, "");
if(!val[0]) return 0;
*ret = strtoul(val, NULL, 0);
return 1;
}
int ConfigValueFloat(const char *devName, const char *blockName, const char *keyName, float *ret)
{
const char *val = GetConfigValue(devName, blockName, keyName, "");
if(!val[0]) return 0;
#ifdef HAVE_STRTOF
*ret = strtof(val, NULL);
#else
*ret = (float)strtod(val, NULL);
#endif
return 1;
}
int ConfigValueBool(const char *devName, const char *blockName, const char *keyName, int *ret)
{
const char *val = GetConfigValue(devName, blockName, keyName, "");
if(!val[0]) return 0;
*ret = (strcasecmp(val, "true") == 0 || strcasecmp(val, "yes") == 0 ||
strcasecmp(val, "on") == 0 || atoi(val) != 0);
return 1;
}
int GetConfigValueBool(const char *devName, const char *blockName, const char *keyName, int def)
{
const char *val = GetConfigValue(devName, blockName, keyName, "");
if(!val[0]) return !!def;
return (strcasecmp(val, "true") == 0 || strcasecmp(val, "yes") == 0 ||
strcasecmp(val, "on") == 0 || atoi(val) != 0);
}
+317
View File
@@ -0,0 +1,317 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 1999-2007 by authors.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include <string.h>
#include <stdlib.h>
#include "alMain.h"
#include "threads.h"
#include "almalloc.h"
#include "compat.h"
/* NOTE: This lockless ringbuffer implementation is copied from JACK, extended
* to include an element size. Consequently, parameters and return values for a
* size or count is in 'elements', not bytes. Additionally, it only supports
* single-consumer/single-provider operation. */
struct ll_ringbuffer {
ATOMIC(size_t) write_ptr;
ATOMIC(size_t) read_ptr;
size_t size;
size_t size_mask;
size_t elem_size;
int mlocked;
alignas(16) char buf[];
};
/* Create a new ringbuffer to hold at least `sz' elements of `elem_sz' bytes.
* The number of elements is rounded up to the next power of two. */
ll_ringbuffer_t *ll_ringbuffer_create(size_t sz, size_t elem_sz)
{
ll_ringbuffer_t *rb;
ALuint power_of_two;
power_of_two = NextPowerOf2(sz);
if(power_of_two < sz)
return NULL;
rb = al_malloc(16, sizeof(*rb) + power_of_two*elem_sz);
if(!rb) return NULL;
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->mlocked = 0;
return rb;
}
/* Free all data associated with the ringbuffer `rb'. */
void ll_ringbuffer_free(ll_ringbuffer_t *rb)
{
if(rb)
{
#ifdef USE_MLOCK
if(rb->mlocked)
munlock(rb, sizeof(*rb) + rb->size*rb->elem_size);
#endif /* USE_MLOCK */
al_free(rb);
}
}
/* Lock the data block of `rb' using the system call 'mlock'. */
int ll_ringbuffer_mlock(ll_ringbuffer_t *rb)
{
#ifdef USE_MLOCK
if(!rb->mlocked && mlock(rb, sizeof(*rb) + rb->size*rb->elem_size))
return -1;
#endif /* USE_MLOCK */
rb->mlocked = 1;
return 0;
}
/* Reset the read and write pointers to zero. This is not thread safe. */
void ll_ringbuffer_reset(ll_ringbuffer_t *rb)
{
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);
}
/* Return the number of elements available for reading. This is the number of
* elements in front of the read pointer and behind the write pointer. */
size_t ll_ringbuffer_read_space(const ll_ringbuffer_t *rb)
{
size_t w = 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 = 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;
size_t n1, n2;
free_cnt = ll_ringbuffer_read_space(rb);
if(free_cnt == 0) return 0;
to_read = (cnt > free_cnt) ? free_cnt : cnt;
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 - read_ptr;
n2 = cnt2 & rb->size_mask;
}
else
{
n1 = to_read;
n2 = 0;
}
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[(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;
}
/* The copying data reader w/o read pointer advance. Copy at most `cnt'
* elements from `rb' to `dest'. Returns the actual number of elements copied.
*/
size_t ll_ringbuffer_peek(ll_ringbuffer_t *rb, char *dest, size_t cnt)
{
size_t free_cnt;
size_t cnt2;
size_t to_read;
size_t n1, n2;
size_t read_ptr;
free_cnt = ll_ringbuffer_read_space(rb);
if(free_cnt == 0) return 0;
to_read = (cnt > free_cnt) ? free_cnt : cnt;
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 - read_ptr;
n2 = cnt2 & rb->size_mask;
}
else
{
n1 = to_read;
n2 = 0;
}
memcpy(dest, &rb->buf[read_ptr*rb->elem_size], n1*rb->elem_size);
if(n2)
{
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;
}
/* The copying data writer. Copy at most `cnt' elements to `rb' from `src'.
* Returns the actual number of elements copied. */
size_t ll_ringbuffer_write(ll_ringbuffer_t *rb, const char *src, size_t cnt)
{
size_t write_ptr;
size_t free_cnt;
size_t cnt2;
size_t to_write;
size_t n1, n2;
free_cnt = ll_ringbuffer_write_space(rb);
if(free_cnt == 0) return 0;
to_write = (cnt > free_cnt) ? free_cnt : cnt;
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 - write_ptr;
n2 = cnt2 & rb->size_mask;
}
else
{
n1 = to_write;
n2 = 0;
}
memcpy(&rb->buf[write_ptr*rb->elem_size], src, n1*rb->elem_size);
write_ptr += n1;
if(n2)
{
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)
{
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)
{
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
* at `vec' to hold the current readable data at `rb'. If the readable data is
* in one segment the second segment has zero length. */
void ll_ringbuffer_get_read_vector(const ll_ringbuffer_t *rb, ll_ringbuffer_data_t * vec)
{
size_t free_cnt;
size_t cnt2;
size_t w, r;
w = 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].len = rb->size - r;
vec[1].buf = (char*)rb->buf;
vec[1].len = cnt2 & rb->size_mask;
}
else
{
/* Single part vector: just the rest of the buffer */
vec[0].buf = (char*)&rb->buf[r*rb->elem_size];
vec[0].len = free_cnt;
vec[1].buf = NULL;
vec[1].len = 0;
}
}
/* The non-copying data writer. `vec' is an array of two places. Set the values
* at `vec' to hold the current writeable data at `rb'. If the writeable data
* is in one segment the second segment has zero length. */
void ll_ringbuffer_get_write_vector(const ll_ringbuffer_t *rb, ll_ringbuffer_data_t *vec)
{
size_t free_cnt;
size_t cnt2;
size_t w, r;
w = 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].len = rb->size - w;
vec[1].buf = (char*)rb->buf;
vec[1].len = cnt2 & rb->size_mask;
}
else
{
vec[0].buf = (char*)&rb->buf[w*rb->elem_size];
vec[0].len = free_cnt;
vec[1].buf = NULL;
vec[1].len = 0;
}
}
+49
View File
@@ -0,0 +1,49 @@
#ifndef ALSTRING_H
#define ALSTRING_H
#include <string.h>
#include "vector.h"
typedef char al_string_char_type;
TYPEDEF_VECTOR(al_string_char_type, al_string)
TYPEDEF_VECTOR(al_string, vector_al_string)
inline void 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) alstr_reset(&(_x))
inline size_t alstr_length(const_al_string str)
{ return VECTOR_SIZE(str); }
inline ALboolean alstr_empty(const_al_string str)
{ return alstr_length(str) == 0; }
inline const al_string_char_type *alstr_get_cstr(const_al_string str)
{ return str ? &VECTOR_FRONT(str) : ""; }
void alstr_clear(al_string *str);
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 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 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 alstr_copy_wcstr(al_string *str, const wchar_t *from);
void alstr_append_wcstr(al_string *str, const wchar_t *from);
void alstr_append_wrange(al_string *str, const wchar_t *from, const wchar_t *to);
#endif
#endif /* ALSTRING_H */
+566
View File
@@ -0,0 +1,566 @@
#include "config.h"
#include "ambdec.h"
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "compat.h"
static char *lstrip(char *line)
{
while(isspace(line[0]))
line++;
return line;
}
static char *rstrip(char *line)
{
size_t len = strlen(line);
while(len > 0 && isspace(line[len-1]))
len--;
line[len] = 0;
return line;
}
static int readline(FILE *f, char **output, size_t *maxlen)
{
size_t len = 0;
int c;
while((c=fgetc(f)) != EOF && (c == '\r' || c == '\n'))
;
if(c == EOF)
return 0;
do {
if(len+1 >= *maxlen)
{
void *temp = NULL;
size_t newmax;
newmax = (*maxlen ? (*maxlen)<<1 : 32);
if(newmax > *maxlen)
temp = realloc(*output, newmax);
if(!temp)
{
ERR("Failed to realloc "SZFMT" bytes from "SZFMT"!\n", newmax, *maxlen);
return 0;
}
*output = temp;
*maxlen = newmax;
}
(*output)[len++] = c;
(*output)[len] = '\0';
} while((c=fgetc(f)) != EOF && c != '\r' && c != '\n');
return 1;
}
/* Custom strtok_r, since we can't rely on it existing. */
static char *my_strtok_r(char *str, const char *delim, char **saveptr)
{
/* Sanity check and update internal pointer. */
if(!saveptr || !delim) return NULL;
if(str) *saveptr = str;
str = *saveptr;
/* Nothing more to do with this string. */
if(!str) return NULL;
/* Find the first non-delimiter character. */
while(*str != '\0' && strchr(delim, *str) != NULL)
str++;
if(*str == '\0')
{
/* End of string. */
*saveptr = NULL;
return NULL;
}
/* Find the next delimiter character. */
*saveptr = strpbrk(str, delim);
if(*saveptr) *((*saveptr)++) = '\0';
return str;
}
static char *read_int(ALint *num, const char *line, int base)
{
char *end;
*num = strtol(line, &end, base);
if(end && *end != '\0')
end = lstrip(end);
return end;
}
static char *read_uint(ALuint *num, const char *line, int base)
{
char *end;
*num = strtoul(line, &end, base);
if(end && *end != '\0')
end = lstrip(end);
return end;
}
static char *read_float(ALfloat *num, const char *line)
{
char *end;
#ifdef HAVE_STRTOF
*num = strtof(line, &end);
#else
*num = (ALfloat)strtod(line, &end);
#endif
if(end && *end != '\0')
end = lstrip(end);
return end;
}
char *read_clipped_line(FILE *f, char **buffer, size_t *maxlen)
{
while(readline(f, buffer, maxlen))
{
char *line, *comment;
line = lstrip(*buffer);
comment = strchr(line, '#');
if(comment) *(comment++) = 0;
line = rstrip(line);
if(line[0]) return line;
}
return NULL;
}
static int load_ambdec_speakers(AmbDecConf *conf, FILE *f, char **buffer, size_t *maxlen, char **saveptr)
{
ALsizei cur = 0;
while(cur < conf->NumSpeakers)
{
const char *cmd = my_strtok_r(NULL, " \t", saveptr);
if(!cmd)
{
char *line = read_clipped_line(f, buffer, maxlen);
if(!line)
{
ERR("Unexpected end of file\n");
return 0;
}
cmd = my_strtok_r(line, " \t", saveptr);
}
if(strcmp(cmd, "add_spkr") == 0)
{
const char *name = my_strtok_r(NULL, " \t", saveptr);
const char *dist = my_strtok_r(NULL, " \t", saveptr);
const char *az = my_strtok_r(NULL, " \t", saveptr);
const char *elev = my_strtok_r(NULL, " \t", saveptr);
const char *conn = my_strtok_r(NULL, " \t", saveptr);
if(!name) WARN("Name not specified for speaker %u\n", cur+1);
else alstr_copy_cstr(&conf->Speakers[cur].Name, name);
if(!dist) WARN("Distance not specified for speaker %u\n", cur+1);
else read_float(&conf->Speakers[cur].Distance, dist);
if(!az) WARN("Azimuth not specified for speaker %u\n", cur+1);
else read_float(&conf->Speakers[cur].Azimuth, az);
if(!elev) WARN("Elevation not specified for speaker %u\n", cur+1);
else read_float(&conf->Speakers[cur].Elevation, elev);
if(!conn) TRACE("Connection not specified for speaker %u\n", cur+1);
else alstr_copy_cstr(&conf->Speakers[cur].Connection, conn);
cur++;
}
else
{
ERR("Unexpected speakers command: %s\n", cmd);
return 0;
}
cmd = my_strtok_r(NULL, " \t", saveptr);
if(cmd)
{
ERR("Unexpected junk on line: %s\n", cmd);
return 0;
}
}
return 1;
}
static int load_ambdec_matrix(ALfloat *gains, ALfloat (*matrix)[MAX_AMBI_COEFFS], ALsizei maxrow, FILE *f, char **buffer, size_t *maxlen, char **saveptr)
{
int gotgains = 0;
ALsizei cur = 0;
while(cur < maxrow)
{
const char *cmd = my_strtok_r(NULL, " \t", saveptr);
if(!cmd)
{
char *line = read_clipped_line(f, buffer, maxlen);
if(!line)
{
ERR("Unexpected end of file\n");
return 0;
}
cmd = my_strtok_r(line, " \t", saveptr);
}
if(strcmp(cmd, "order_gain") == 0)
{
ALuint curgain = 0;
char *line;
while((line=my_strtok_r(NULL, " \t", saveptr)) != NULL)
{
ALfloat value;
line = read_float(&value, line);
if(line && *line != '\0')
{
ERR("Extra junk on gain %u: %s\n", curgain+1, line);
return 0;
}
if(curgain < MAX_AMBI_ORDER+1)
gains[curgain] = value;
curgain++;
}
while(curgain < MAX_AMBI_ORDER+1)
gains[curgain++] = 0.0f;
gotgains = 1;
}
else if(strcmp(cmd, "add_row") == 0)
{
ALuint curidx = 0;
char *line;
while((line=my_strtok_r(NULL, " \t", saveptr)) != NULL)
{
ALfloat value;
line = read_float(&value, line);
if(line && *line != '\0')
{
ERR("Extra junk on matrix element %ux%u: %s\n", cur, curidx, line);
return 0;
}
if(curidx < MAX_AMBI_COEFFS)
matrix[cur][curidx] = value;
curidx++;
}
while(curidx < MAX_AMBI_COEFFS)
matrix[cur][curidx++] = 0.0f;
cur++;
}
else
{
ERR("Unexpected speakers command: %s\n", cmd);
return 0;
}
cmd = my_strtok_r(NULL, " \t", saveptr);
if(cmd)
{
ERR("Unexpected junk on line: %s\n", cmd);
return 0;
}
}
if(!gotgains)
{
ERR("Matrix order_gain not specified\n");
return 0;
}
return 1;
}
void ambdec_init(AmbDecConf *conf)
{
ALsizei i;
memset(conf, 0, sizeof(*conf));
AL_STRING_INIT(conf->Description);
for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
{
AL_STRING_INIT(conf->Speakers[i].Name);
AL_STRING_INIT(conf->Speakers[i].Connection);
}
}
void ambdec_deinit(AmbDecConf *conf)
{
ALsizei i;
alstr_reset(&conf->Description);
for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
{
alstr_reset(&conf->Speakers[i].Name);
alstr_reset(&conf->Speakers[i].Connection);
}
memset(conf, 0, sizeof(*conf));
}
int ambdec_load(AmbDecConf *conf, const char *fname)
{
char *buffer = NULL;
size_t maxlen = 0;
char *line;
FILE *f;
f = al_fopen(fname, "r");
if(!f)
{
ERR("Failed to open: %s\n", fname);
return 0;
}
while((line=read_clipped_line(f, &buffer, &maxlen)) != NULL)
{
char *saveptr;
char *command;
command = my_strtok_r(line, "/ \t", &saveptr);
if(!command)
{
ERR("Malformed line: %s\n", line);
goto fail;
}
if(strcmp(command, "description") == 0)
{
char *value = my_strtok_r(NULL, "", &saveptr);
alstr_copy_cstr(&conf->Description, lstrip(value));
}
else if(strcmp(command, "version") == 0)
{
line = my_strtok_r(NULL, "", &saveptr);
line = read_uint(&conf->Version, line, 10);
if(line && *line != '\0')
{
ERR("Extra junk after version: %s\n", line);
goto fail;
}
if(conf->Version != 3)
{
ERR("Unsupported version: %u\n", conf->Version);
goto fail;
}
}
else if(strcmp(command, "dec") == 0)
{
const char *dec = my_strtok_r(NULL, "/ \t", &saveptr);
if(strcmp(dec, "chan_mask") == 0)
{
line = my_strtok_r(NULL, "", &saveptr);
line = read_uint(&conf->ChanMask, line, 16);
if(line && *line != '\0')
{
ERR("Extra junk after mask: %s\n", line);
goto fail;
}
}
else if(strcmp(dec, "freq_bands") == 0)
{
line = my_strtok_r(NULL, "", &saveptr);
line = read_uint(&conf->FreqBands, line, 10);
if(line && *line != '\0')
{
ERR("Extra junk after freq_bands: %s\n", line);
goto fail;
}
if(conf->FreqBands != 1 && conf->FreqBands != 2)
{
ERR("Invalid freq_bands value: %u\n", conf->FreqBands);
goto fail;
}
}
else if(strcmp(dec, "speakers") == 0)
{
line = my_strtok_r(NULL, "", &saveptr);
line = read_int(&conf->NumSpeakers, line, 10);
if(line && *line != '\0')
{
ERR("Extra junk after speakers: %s\n", line);
goto fail;
}
if(conf->NumSpeakers > MAX_OUTPUT_CHANNELS)
{
ERR("Unsupported speaker count: %u\n", conf->NumSpeakers);
goto fail;
}
}
else if(strcmp(dec, "coeff_scale") == 0)
{
line = my_strtok_r(NULL, " \t", &saveptr);
if(strcmp(line, "n3d") == 0)
conf->CoeffScale = ADS_N3D;
else if(strcmp(line, "sn3d") == 0)
conf->CoeffScale = ADS_SN3D;
else if(strcmp(line, "fuma") == 0)
conf->CoeffScale = ADS_FuMa;
else
{
ERR("Unsupported coeff scale: %s\n", line);
goto fail;
}
}
else
{
ERR("Unexpected /dec option: %s\n", dec);
goto fail;
}
}
else if(strcmp(command, "opt") == 0)
{
const char *opt = my_strtok_r(NULL, "/ \t", &saveptr);
if(strcmp(opt, "xover_freq") == 0)
{
line = my_strtok_r(NULL, "", &saveptr);
line = read_float(&conf->XOverFreq, line);
if(line && *line != '\0')
{
ERR("Extra junk after xover_freq: %s\n", line);
goto fail;
}
}
else if(strcmp(opt, "xover_ratio") == 0)
{
line = my_strtok_r(NULL, "", &saveptr);
line = read_float(&conf->XOverRatio, line);
if(line && *line != '\0')
{
ERR("Extra junk after xover_ratio: %s\n", line);
goto fail;
}
}
else if(strcmp(opt, "input_scale") == 0 || strcmp(opt, "nfeff_comp") == 0 ||
strcmp(opt, "delay_comp") == 0 || strcmp(opt, "level_comp") == 0)
{
/* Unused */
my_strtok_r(NULL, " \t", &saveptr);
}
else
{
ERR("Unexpected /opt option: %s\n", opt);
goto fail;
}
}
else if(strcmp(command, "speakers") == 0)
{
const char *value = my_strtok_r(NULL, "/ \t", &saveptr);
if(strcmp(value, "{") != 0)
{
ERR("Expected { after %s command, got %s\n", command, value);
goto fail;
}
if(!load_ambdec_speakers(conf, f, &buffer, &maxlen, &saveptr))
goto fail;
value = my_strtok_r(NULL, "/ \t", &saveptr);
if(!value)
{
line = read_clipped_line(f, &buffer, &maxlen);
if(!line)
{
ERR("Unexpected end of file\n");
goto fail;
}
value = my_strtok_r(line, "/ \t", &saveptr);
}
if(strcmp(value, "}") != 0)
{
ERR("Expected } after speaker definitions, got %s\n", value);
goto fail;
}
}
else if(strcmp(command, "lfmatrix") == 0 || strcmp(command, "hfmatrix") == 0 ||
strcmp(command, "matrix") == 0)
{
const char *value = my_strtok_r(NULL, "/ \t", &saveptr);
if(strcmp(value, "{") != 0)
{
ERR("Expected { after %s command, got %s\n", command, value);
goto fail;
}
if(conf->FreqBands == 1)
{
if(strcmp(command, "matrix") != 0)
{
ERR("Unexpected \"%s\" type for a single-band decoder\n", command);
goto fail;
}
if(!load_ambdec_matrix(conf->HFOrderGain, conf->HFMatrix, conf->NumSpeakers,
f, &buffer, &maxlen, &saveptr))
goto fail;
}
else
{
if(strcmp(command, "lfmatrix") == 0)
{
if(!load_ambdec_matrix(conf->LFOrderGain, conf->LFMatrix, conf->NumSpeakers,
f, &buffer, &maxlen, &saveptr))
goto fail;
}
else if(strcmp(command, "hfmatrix") == 0)
{
if(!load_ambdec_matrix(conf->HFOrderGain, conf->HFMatrix, conf->NumSpeakers,
f, &buffer, &maxlen, &saveptr))
goto fail;
}
else
{
ERR("Unexpected \"%s\" type for a dual-band decoder\n", command);
goto fail;
}
}
value = my_strtok_r(NULL, "/ \t", &saveptr);
if(!value)
{
line = read_clipped_line(f, &buffer, &maxlen);
if(!line)
{
ERR("Unexpected end of file\n");
goto fail;
}
value = my_strtok_r(line, "/ \t", &saveptr);
}
if(strcmp(value, "}") != 0)
{
ERR("Expected } after matrix definitions, got %s\n", value);
goto fail;
}
}
else if(strcmp(command, "end") == 0)
{
line = my_strtok_r(NULL, "/ \t", &saveptr);
if(line)
{
ERR("Unexpected junk on end: %s\n", line);
goto fail;
}
fclose(f);
free(buffer);
return 1;
}
else
{
ERR("Unexpected command: %s\n", command);
goto fail;
}
line = my_strtok_r(NULL, "/ \t", &saveptr);
if(line)
{
ERR("Unexpected junk on line: %s\n", line);
goto fail;
}
}
ERR("Unexpected end of file\n");
fail:
fclose(f);
free(buffer);
return 0;
}
+46
View File
@@ -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 */
+1437
View File
File diff suppressed because it is too large Load Diff
+81
View File
@@ -0,0 +1,81 @@
#include "config.h"
#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 = almtx_init(&self->mMutex, almtx_recursive);
assert(ret == althrd_success);
self->mDevice = device;
}
void ALCbackend_Destruct(ALCbackend *self)
{
almtx_destroy(&self->mMutex);
}
ALCboolean ALCbackend_reset(ALCbackend* UNUSED(self))
{
return ALC_FALSE;
}
ALCenum ALCbackend_captureSamples(ALCbackend* UNUSED(self), void* UNUSED(buffer), ALCuint UNUSED(samples))
{
return ALC_INVALID_DEVICE;
}
ALCuint ALCbackend_availableSamples(ALCbackend* UNUSED(self))
{
return 0;
}
ClockLatency ALCbackend_getClockLatency(ALCbackend *self)
{
ALCdevice *device = self->mDevice;
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)
{
int ret = almtx_lock(&self->mMutex);
assert(ret == althrd_success);
}
void ALCbackend_unlock(ALCbackend *self)
{
int ret = almtx_unlock(&self->mMutex);
assert(ret == althrd_success);
}
/* Base ALCbackendFactory method implementations. */
void ALCbackendFactory_deinit(ALCbackendFactory* UNUSED(self))
{
}
+155
View File
@@ -0,0 +1,155 @@
#ifndef AL_BACKENDS_BASE_H
#define AL_BACKENDS_BASE_H
#include "alMain.h"
#include "threads.h"
typedef struct ClockLatency {
ALint64 ClockTime;
ALint64 Latency;
} ClockLatency;
/* Helper to get the current clock time from the device's ClockBase, and
* SamplesDone converted from the sample rate.
*/
inline ALuint64 GetDeviceClockTime(ALCdevice *device)
{
return device->ClockBase + (device->SamplesDone * DEVICE_CLOCK_RES /
device->Frequency);
}
struct ALCbackendVtable;
typedef struct ALCbackend {
const struct ALCbackendVtable *vtbl;
ALCdevice *mDevice;
almtx_t mMutex;
} ALCbackend;
void ALCbackend_Construct(ALCbackend *self, ALCdevice *device);
void ALCbackend_Destruct(ALCbackend *self);
ALCboolean ALCbackend_reset(ALCbackend *self);
ALCenum ALCbackend_captureSamples(ALCbackend *self, void *buffer, ALCuint samples);
ALCuint ALCbackend_availableSamples(ALCbackend *self);
ClockLatency ALCbackend_getClockLatency(ALCbackend *self);
void ALCbackend_lock(ALCbackend *self);
void ALCbackend_unlock(ALCbackend *self);
struct ALCbackendVtable {
void (*const Destruct)(ALCbackend*);
ALCenum (*const open)(ALCbackend*, const ALCchar*);
void (*const close)(ALCbackend*);
ALCboolean (*const reset)(ALCbackend*);
ALCboolean (*const start)(ALCbackend*);
void (*const stop)(ALCbackend*);
ALCenum (*const captureSamples)(ALCbackend*, void*, ALCuint);
ALCuint (*const availableSamples)(ALCbackend*);
ClockLatency (*const getClockLatency)(ALCbackend*);
void (*const lock)(ALCbackend*);
void (*const unlock)(ALCbackend*);
void (*const Delete)(void*);
};
#define DEFINE_ALCBACKEND_VTABLE(T) \
DECLARE_THUNK(T, ALCbackend, void, Destruct) \
DECLARE_THUNK1(T, ALCbackend, ALCenum, open, const ALCchar*) \
DECLARE_THUNK(T, ALCbackend, void, close) \
DECLARE_THUNK(T, ALCbackend, ALCboolean, reset) \
DECLARE_THUNK(T, ALCbackend, ALCboolean, start) \
DECLARE_THUNK(T, ALCbackend, void, stop) \
DECLARE_THUNK2(T, ALCbackend, ALCenum, captureSamples, void*, ALCuint) \
DECLARE_THUNK(T, ALCbackend, ALCuint, availableSamples) \
DECLARE_THUNK(T, ALCbackend, ClockLatency, getClockLatency) \
DECLARE_THUNK(T, ALCbackend, void, lock) \
DECLARE_THUNK(T, ALCbackend, void, unlock) \
static void T##_ALCbackend_Delete(void *ptr) \
{ T##_Delete(STATIC_UPCAST(T, ALCbackend, (ALCbackend*)ptr)); } \
\
static const struct ALCbackendVtable T##_ALCbackend_vtable = { \
T##_ALCbackend_Destruct, \
\
T##_ALCbackend_open, \
T##_ALCbackend_close, \
T##_ALCbackend_reset, \
T##_ALCbackend_start, \
T##_ALCbackend_stop, \
T##_ALCbackend_captureSamples, \
T##_ALCbackend_availableSamples, \
T##_ALCbackend_getClockLatency, \
T##_ALCbackend_lock, \
T##_ALCbackend_unlock, \
\
T##_ALCbackend_Delete, \
}
typedef enum ALCbackend_Type {
ALCbackend_Playback,
ALCbackend_Capture,
ALCbackend_Loopback
} ALCbackend_Type;
struct ALCbackendFactoryVtable;
typedef struct ALCbackendFactory {
const struct ALCbackendFactoryVtable *vtbl;
} ALCbackendFactory;
void ALCbackendFactory_deinit(ALCbackendFactory *self);
struct ALCbackendFactoryVtable {
ALCboolean (*const init)(ALCbackendFactory *self);
void (*const deinit)(ALCbackendFactory *self);
ALCboolean (*const querySupport)(ALCbackendFactory *self, ALCbackend_Type type);
void (*const probe)(ALCbackendFactory *self, enum DevProbe type);
ALCbackend* (*const createBackend)(ALCbackendFactory *self, ALCdevice *device, ALCbackend_Type type);
};
#define DEFINE_ALCBACKENDFACTORY_VTABLE(T) \
DECLARE_THUNK(T, ALCbackendFactory, ALCboolean, init) \
DECLARE_THUNK(T, ALCbackendFactory, void, deinit) \
DECLARE_THUNK1(T, ALCbackendFactory, ALCboolean, querySupport, ALCbackend_Type) \
DECLARE_THUNK1(T, ALCbackendFactory, void, probe, enum DevProbe) \
DECLARE_THUNK2(T, ALCbackendFactory, ALCbackend*, createBackend, ALCdevice*, ALCbackend_Type) \
\
static const struct ALCbackendFactoryVtable T##_ALCbackendFactory_vtable = { \
T##_ALCbackendFactory_init, \
T##_ALCbackendFactory_deinit, \
T##_ALCbackendFactory_querySupport, \
T##_ALCbackendFactory_probe, \
T##_ALCbackendFactory_createBackend, \
}
ALCbackendFactory *ALCpulseBackendFactory_getFactory(void);
ALCbackendFactory *ALCalsaBackendFactory_getFactory(void);
ALCbackendFactory *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);
#endif /* AL_BACKENDS_BASE_H */
+828
View File
@@ -0,0 +1,828 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 1999-2007 by authors.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <alloca.h>
#include "alMain.h"
#include "alu.h"
#include <CoreServices/CoreServices.h>
#include <unistd.h>
#include <AudioUnit/AudioUnit.h>
#include <AudioToolbox/AudioToolbox.h>
#include "backends/base.h"
typedef struct {
AudioUnit audioUnit;
ALuint frameSize;
ALdouble sampleRateRatio; // Ratio of hardware sample rate / requested sample rate
AudioStreamBasicDescription format; // This is the OpenAL format as a CoreAudio ASBD
AudioConverterRef audioConverter; // Sample rate converter if needed
AudioBufferList *bufferList; // Buffer for data coming from the input device
ALCvoid *resampleBuffer; // Buffer for returned RingBuffer data when resampling
ll_ringbuffer_t *ring;
} ca_data;
static const ALCchar ca_device[] = "CoreAudio Default";
static AudioBufferList* allocate_buffer_list(UInt32 channelCount, UInt32 byteSize)
{
AudioBufferList *list;
list = calloc(1, sizeof(AudioBufferList) + sizeof(AudioBuffer));
if(list)
{
list->mNumberBuffers = 1;
list->mBuffers[0].mNumberChannels = channelCount;
list->mBuffers[0].mDataByteSize = byteSize;
list->mBuffers[0].mData = malloc(byteSize);
if(list->mBuffers[0].mData == NULL)
{
free(list);
list = NULL;
}
}
return list;
}
static void destroy_buffer_list(AudioBufferList* list)
{
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 / self->frameSize);
ALCdevice_Unlock(device);
return noErr;
}
static ALCenum ALCcoreAudioPlayback_open(ALCcoreAudioPlayback *self, const ALCchar *name)
{
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
AudioComponentDescription desc;
AudioComponent comp;
OSStatus err;
if(!name)
name = ca_device;
else if(strcmp(name, ca_device) != 0)
return ALC_INVALID_VALUE;
/* open the default output unit */
desc.componentType = kAudioUnitType_Output;
desc.componentSubType = kAudioUnitSubType_DefaultOutput;
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
desc.componentFlags = 0;
desc.componentFlagsMask = 0;
comp = AudioComponentFindNext(NULL, &desc);
if(comp == NULL)
{
ERR("AudioComponentFindNext failed\n");
return ALC_INVALID_VALUE;
}
err = AudioComponentInstanceNew(comp, &self->audioUnit);
if(err != noErr)
{
ERR("AudioComponentInstanceNew failed\n");
return ALC_INVALID_VALUE;
}
/* init and start the default audio unit... */
err = AudioUnitInitialize(self->audioUnit);
if(err != noErr)
{
ERR("AudioUnitInitialize failed\n");
AudioComponentInstanceDispose(self->audioUnit);
return ALC_INVALID_VALUE;
}
alstr_copy_cstr(&device->DeviceName, name);
return ALC_NO_ERROR;
}
static void ALCcoreAudioPlayback_close(ALCcoreAudioPlayback *self)
{
AudioUnitUninitialize(self->audioUnit);
AudioComponentInstanceDispose(self->audioUnit);
}
static ALCboolean ALCcoreAudioPlayback_reset(ALCcoreAudioPlayback *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
AudioStreamBasicDescription streamFormat;
AURenderCallbackStruct input;
OSStatus err;
UInt32 size;
err = AudioUnitUninitialize(self->audioUnit);
if(err != noErr)
ERR("-- AudioUnitUninitialize failed.\n");
/* retrieve default output unit's properties (output side) */
size = sizeof(AudioStreamBasicDescription);
err = AudioUnitGetProperty(self->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &streamFormat, &size);
if(err != noErr || size != sizeof(AudioStreamBasicDescription))
{
ERR("AudioUnitGetProperty failed\n");
return ALC_FALSE;
}
#if 0
TRACE("Output streamFormat of default output unit -\n");
TRACE(" streamFormat.mFramesPerPacket = %d\n", streamFormat.mFramesPerPacket);
TRACE(" streamFormat.mChannelsPerFrame = %d\n", streamFormat.mChannelsPerFrame);
TRACE(" streamFormat.mBitsPerChannel = %d\n", streamFormat.mBitsPerChannel);
TRACE(" streamFormat.mBytesPerPacket = %d\n", streamFormat.mBytesPerPacket);
TRACE(" streamFormat.mBytesPerFrame = %d\n", streamFormat.mBytesPerFrame);
TRACE(" streamFormat.mSampleRate = %5.0f\n", streamFormat.mSampleRate);
#endif
/* set default output unit's input side to match output side */
err = AudioUnitSetProperty(self->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &streamFormat, size);
if(err != noErr)
{
ERR("AudioUnitSetProperty failed\n");
return ALC_FALSE;
}
if(device->Frequency != streamFormat.mSampleRate)
{
device->NumUpdates = (ALuint)((ALuint64)device->NumUpdates *
streamFormat.mSampleRate /
device->Frequency);
device->Frequency = streamFormat.mSampleRate;
}
/* FIXME: How to tell what channels are what in the output device, and how
* to specify what we're giving? eg, 6.0 vs 5.1 */
switch(streamFormat.mChannelsPerFrame)
{
case 1:
device->FmtChans = DevFmtMono;
break;
case 2:
device->FmtChans = DevFmtStereo;
break;
case 4:
device->FmtChans = DevFmtQuad;
break;
case 6:
device->FmtChans = DevFmtX51;
break;
case 7:
device->FmtChans = DevFmtX61;
break;
case 8:
device->FmtChans = DevFmtX71;
break;
default:
ERR("Unhandled channel count (%d), using Stereo\n", streamFormat.mChannelsPerFrame);
device->FmtChans = DevFmtStereo;
streamFormat.mChannelsPerFrame = 2;
break;
}
SetDefaultWFXChannelOrder(device);
/* use channel count and sample rate from the default output unit's current
* parameters, but reset everything else */
streamFormat.mFramesPerPacket = 1;
streamFormat.mFormatFlags = 0;
switch(device->FmtType)
{
case DevFmtUByte:
device->FmtType = DevFmtByte;
/* fall-through */
case DevFmtByte:
streamFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
streamFormat.mBitsPerChannel = 8;
break;
case DevFmtUShort:
device->FmtType = DevFmtShort;
/* fall-through */
case DevFmtShort:
streamFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
streamFormat.mBitsPerChannel = 16;
break;
case DevFmtUInt:
device->FmtType = DevFmtInt;
/* fall-through */
case DevFmtInt:
streamFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
streamFormat.mBitsPerChannel = 32;
break;
case DevFmtFloat:
streamFormat.mFormatFlags = kLinearPCMFormatFlagIsFloat;
streamFormat.mBitsPerChannel = 32;
break;
}
streamFormat.mBytesPerFrame = streamFormat.mChannelsPerFrame *
streamFormat.mBitsPerChannel / 8;
streamFormat.mBytesPerPacket = streamFormat.mBytesPerFrame;
streamFormat.mFormatID = kAudioFormatLinearPCM;
streamFormat.mFormatFlags |= kAudioFormatFlagsNativeEndian |
kLinearPCMFormatFlagIsPacked;
err = AudioUnitSetProperty(self->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &streamFormat, sizeof(AudioStreamBasicDescription));
if(err != noErr)
{
ERR("AudioUnitSetProperty failed\n");
return ALC_FALSE;
}
/* setup callback */
self->frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
input.inputProc = ALCcoreAudioPlayback_MixerProc;
input.inputProcRefCon = self;
err = AudioUnitSetProperty(self->audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &input, sizeof(AURenderCallbackStruct));
if(err != noErr)
{
ERR("AudioUnitSetProperty failed\n");
return ALC_FALSE;
}
/* init the default audio unit... */
err = AudioUnitInitialize(self->audioUnit);
if(err != noErr)
{
ERR("AudioUnitInitialize failed\n");
return ALC_FALSE;
}
return ALC_TRUE;
}
static ALCboolean ALCcoreAudioPlayback_start(ALCcoreAudioPlayback *self)
{
OSStatus err = AudioOutputUnitStart(self->audioUnit);
if(err != noErr)
{
ERR("AudioOutputUnitStart failed\n");
return ALC_FALSE;
}
return ALC_TRUE;
}
static void ALCcoreAudioPlayback_stop(ALCcoreAudioPlayback *self)
{
OSStatus err = AudioOutputUnitStop(self->audioUnit);
if(err != noErr)
ERR("AudioOutputUnitStop failed\n");
}
typedef struct ALCcoreAudioCapture {
DERIVE_FROM_TYPE(ALCbackend);
AudioUnit audioUnit;
ALuint frameSize;
ALdouble sampleRateRatio; // Ratio of hardware sample rate / requested sample rate
AudioStreamBasicDescription format; // This is the OpenAL format as a CoreAudio ASBD
AudioConverterRef audioConverter; // Sample rate converter if needed
AudioBufferList *bufferList; // Buffer for data coming from the input device
ALCvoid *resampleBuffer; // Buffer for returned RingBuffer data when resampling
ll_ringbuffer_t *ring;
} ALCcoreAudioCapture;
static void ALCcoreAudioCapture_Construct(ALCcoreAudioCapture *self, ALCdevice *device);
static void ALCcoreAudioCapture_Destruct(ALCcoreAudioCapture *self);
static ALCenum ALCcoreAudioCapture_open(ALCcoreAudioCapture *self, const ALCchar *name);
static void ALCcoreAudioCapture_close(ALCcoreAudioCapture *self);
static DECLARE_FORWARD(ALCcoreAudioCapture, ALCbackend, ALCboolean, reset)
static ALCboolean ALCcoreAudioCapture_start(ALCcoreAudioCapture *self);
static void ALCcoreAudioCapture_stop(ALCcoreAudioCapture *self);
static ALCenum ALCcoreAudioCapture_captureSamples(ALCcoreAudioCapture *self, ALCvoid *buffer, ALCuint samples);
static ALCuint ALCcoreAudioCapture_availableSamples(ALCcoreAudioCapture *self);
static DECLARE_FORWARD(ALCcoreAudioCapture, ALCbackend, ClockLatency, getClockLatency)
static DECLARE_FORWARD(ALCcoreAudioCapture, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCcoreAudioCapture, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCcoreAudioCapture)
DEFINE_ALCBACKEND_VTABLE(ALCcoreAudioCapture);
static void ALCcoreAudioCapture_Construct(ALCcoreAudioCapture *self, ALCdevice *device)
{
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(ALCcoreAudioCapture, ALCbackend, self);
}
static void ALCcoreAudioCapture_Destruct(ALCcoreAudioCapture *self)
{
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
}
static OSStatus ALCcoreAudioCapture_RecordProc(void *inRefCon,
AudioUnitRenderActionFlags* UNUSED(ioActionFlags),
const AudioTimeStamp *inTimeStamp, UInt32 UNUSED(inBusNumber),
UInt32 inNumberFrames, AudioBufferList* UNUSED(ioData))
{
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
AURenderCallbackStruct input;
AudioComponentDescription desc;
AudioDeviceID inputDevice;
UInt32 outputFrameCount;
UInt32 propertySize;
AudioObjectPropertyAddress propertyAddress;
UInt32 enableIO;
AudioComponent comp;
OSStatus err;
if(!name)
name = ca_device;
else if(strcmp(name, ca_device) != 0)
return ALC_INVALID_VALUE;
desc.componentType = kAudioUnitType_Output;
desc.componentSubType = kAudioUnitSubType_HALOutput;
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
desc.componentFlags = 0;
desc.componentFlagsMask = 0;
// Search for component with given description
comp = AudioComponentFindNext(NULL, &desc);
if(comp == NULL)
{
ERR("AudioComponentFindNext failed\n");
return ALC_INVALID_VALUE;
}
// Open the component
err = AudioComponentInstanceNew(comp, &self->audioUnit);
if(err != noErr)
{
ERR("AudioComponentInstanceNew failed\n");
goto error;
}
// Turn off AudioUnit output
enableIO = 0;
err = AudioUnitSetProperty(self->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, &enableIO, sizeof(ALuint));
if(err != noErr)
{
ERR("AudioUnitSetProperty failed\n");
goto error;
}
// Turn on AudioUnit input
enableIO = 1;
err = AudioUnitSetProperty(self->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &enableIO, sizeof(ALuint));
if(err != noErr)
{
ERR("AudioUnitSetProperty failed\n");
goto error;
}
// Get the default input device
propertySize = sizeof(AudioDeviceID);
propertyAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice;
propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
propertyAddress.mElement = kAudioObjectPropertyElementMaster;
err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &propertySize, &inputDevice);
if(err != noErr)
{
ERR("AudioObjectGetPropertyData failed\n");
goto error;
}
if(inputDevice == kAudioDeviceUnknown)
{
ERR("No input device found\n");
goto error;
}
// Track the input device
err = AudioUnitSetProperty(self->audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &inputDevice, sizeof(AudioDeviceID));
if(err != noErr)
{
ERR("AudioUnitSetProperty failed\n");
goto error;
}
// set capture callback
input.inputProc = ALCcoreAudioCapture_RecordProc;
input.inputProcRefCon = self;
err = AudioUnitSetProperty(self->audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &input, sizeof(AURenderCallbackStruct));
if(err != noErr)
{
ERR("AudioUnitSetProperty failed\n");
goto error;
}
// Initialize the device
err = AudioUnitInitialize(self->audioUnit);
if(err != noErr)
{
ERR("AudioUnitInitialize failed\n");
goto error;
}
// Get the hardware format
propertySize = sizeof(AudioStreamBasicDescription);
err = AudioUnitGetProperty(self->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 1, &hardwareFormat, &propertySize);
if(err != noErr || propertySize != sizeof(AudioStreamBasicDescription))
{
ERR("AudioUnitGetProperty failed\n");
goto error;
}
// Set up the requested format description
switch(device->FmtType)
{
case DevFmtUByte:
requestedFormat.mBitsPerChannel = 8;
requestedFormat.mFormatFlags = kAudioFormatFlagIsPacked;
break;
case DevFmtShort:
requestedFormat.mBitsPerChannel = 16;
requestedFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked;
break;
case DevFmtInt:
requestedFormat.mBitsPerChannel = 32;
requestedFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked;
break;
case DevFmtFloat:
requestedFormat.mBitsPerChannel = 32;
requestedFormat.mFormatFlags = kAudioFormatFlagIsPacked;
break;
case DevFmtByte:
case DevFmtUShort:
case DevFmtUInt:
ERR("%s samples not supported\n", DevFmtTypeString(device->FmtType));
goto error;
}
switch(device->FmtChans)
{
case DevFmtMono:
requestedFormat.mChannelsPerFrame = 1;
break;
case DevFmtStereo:
requestedFormat.mChannelsPerFrame = 2;
break;
case DevFmtQuad:
case DevFmtX51:
case DevFmtX51Rear:
case DevFmtX61:
case DevFmtX71:
case DevFmtAmbi3D:
ERR("%s not supported\n", DevFmtChannelsString(device->FmtChans));
goto error;
}
requestedFormat.mBytesPerFrame = requestedFormat.mChannelsPerFrame * requestedFormat.mBitsPerChannel / 8;
requestedFormat.mBytesPerPacket = requestedFormat.mBytesPerFrame;
requestedFormat.mSampleRate = device->Frequency;
requestedFormat.mFormatID = kAudioFormatLinearPCM;
requestedFormat.mReserved = 0;
requestedFormat.mFramesPerPacket = 1;
// save requested format description for later use
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
outputFormat = requestedFormat;
outputFormat.mSampleRate = hardwareFormat.mSampleRate;
// Determine sample rate ratio for resampling
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(self->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, (void *)&outputFormat, sizeof(outputFormat));
if(err != noErr)
{
ERR("AudioUnitSetProperty failed\n");
goto error;
}
// Set the AudioUnit output format frame count
outputFrameCount = device->UpdateSize * self->sampleRateRatio;
err = AudioUnitSetProperty(self->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Output, 0, &outputFrameCount, sizeof(outputFrameCount));
if(err != noErr)
{
ERR("AudioUnitSetProperty failed: %d\n", err);
goto error;
}
// Set up sample converter
err = AudioConverterNew(&outputFormat, &requestedFormat, &self->audioConverter);
if(err != noErr)
{
ERR("AudioConverterNew failed: %d\n", err);
goto error;
}
// Create a buffer for use in the resample callback
self->resampleBuffer = malloc(device->UpdateSize * self->frameSize * self->sampleRateRatio);
// Allocate buffer for the AudioUnit output
self->bufferList = allocate_buffer_list(outputFormat.mChannelsPerFrame, device->UpdateSize * self->frameSize * self->sampleRateRatio);
if(self->bufferList == NULL)
goto error;
self->ring = ll_ringbuffer_create(
device->UpdateSize*self->sampleRateRatio*device->NumUpdates + 1,
self->frameSize
);
if(!self->ring) goto error;
alstr_copy_cstr(&device->DeviceName, name);
return ALC_NO_ERROR;
error:
ll_ringbuffer_free(self->ring);
self->ring = NULL;
free(self->resampleBuffer);
destroy_buffer_list(self->bufferList);
if(self->audioConverter)
AudioConverterDispose(self->audioConverter);
if(self->audioUnit)
AudioComponentInstanceDispose(self->audioUnit);
return ALC_INVALID_VALUE;
}
static void ALCcoreAudioCapture_close(ALCcoreAudioCapture *self)
{
ll_ringbuffer_free(self->ring);
self->ring = NULL;
free(self->resampleBuffer);
destroy_buffer_list(self->bufferList);
AudioConverterDispose(self->audioConverter);
AudioComponentInstanceDispose(self->audioUnit);
}
static ALCboolean ALCcoreAudioCapture_start(ALCcoreAudioCapture *self)
{
OSStatus err = AudioOutputUnitStart(self->audioUnit);
if(err != noErr)
{
ERR("AudioOutputUnitStart failed\n");
return ALC_FALSE;
}
return ALC_TRUE;
}
static void ALCcoreAudioCapture_stop(ALCcoreAudioCapture *self)
{
OSStatus err = AudioOutputUnitStop(self->audioUnit);
if(err != noErr)
ERR("AudioOutputUnitStop failed\n");
}
static ALCenum ALCcoreAudioCapture_captureSamples(ALCcoreAudioCapture *self, ALCvoid *buffer, ALCuint samples)
{
AudioBufferList *list;
UInt32 frameCount;
OSStatus err;
// If no samples are requested, just return
if(samples == 0)
return ALC_NO_ERROR;
// Allocate a temporary AudioBufferList to use as the return resamples data
list = alloca(sizeof(AudioBufferList) + sizeof(AudioBuffer));
// Point the resampling buffer to the capture buffer
list->mNumberBuffers = 1;
list->mBuffers[0].mNumberChannels = self->format.mChannelsPerFrame;
list->mBuffers[0].mDataByteSize = samples * self->frameSize;
list->mBuffers[0].mData = buffer;
// Resample into another AudioBufferList
frameCount = samples;
err = AudioConverterFillComplexBuffer(self->audioConverter,
ALCcoreAudioCapture_ConvertCallback, self, &frameCount, list, NULL
);
if(err != noErr)
{
ERR("AudioConverterFillComplexBuffer error: %d\n", err);
return ALC_INVALID_VALUE;
}
return ALC_NO_ERROR;
}
static ALCuint ALCcoreAudioCapture_availableSamples(ALCcoreAudioCapture *self)
{
return ll_ringbuffer_read_space(self->ring) / self->sampleRateRatio;
}
typedef struct ALCcoreAudioBackendFactory {
DERIVE_FROM_TYPE(ALCbackendFactory);
} ALCcoreAudioBackendFactory;
#define ALCCOREAUDIOBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCcoreAudioBackendFactory, ALCbackendFactory) } }
ALCbackendFactory *ALCcoreAudioBackendFactory_getFactory(void);
static ALCboolean ALCcoreAudioBackendFactory_init(ALCcoreAudioBackendFactory *self);
static DECLARE_FORWARD(ALCcoreAudioBackendFactory, ALCbackendFactory, void, deinit)
static ALCboolean ALCcoreAudioBackendFactory_querySupport(ALCcoreAudioBackendFactory *self, ALCbackend_Type type);
static void ALCcoreAudioBackendFactory_probe(ALCcoreAudioBackendFactory *self, enum DevProbe type);
static ALCbackend* ALCcoreAudioBackendFactory_createBackend(ALCcoreAudioBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCcoreAudioBackendFactory);
ALCbackendFactory *ALCcoreAudioBackendFactory_getFactory(void)
{
static ALCcoreAudioBackendFactory factory = ALCCOREAUDIOBACKENDFACTORY_INITIALIZER;
return STATIC_CAST(ALCbackendFactory, &factory);
}
static ALCboolean ALCcoreAudioBackendFactory_init(ALCcoreAudioBackendFactory* UNUSED(self))
{
return ALC_TRUE;
}
static ALCboolean ALCcoreAudioBackendFactory_querySupport(ALCcoreAudioBackendFactory* UNUSED(self), ALCbackend_Type type)
{
if(type == ALCbackend_Playback || ALCbackend_Capture)
return ALC_TRUE;
return ALC_FALSE;
}
static void ALCcoreAudioBackendFactory_probe(ALCcoreAudioBackendFactory* UNUSED(self), enum DevProbe type)
{
switch(type)
{
case ALL_DEVICE_PROBE:
AppendAllDevicesList(ca_device);
break;
case CAPTURE_DEVICE_PROBE:
AppendCaptureDeviceList(ca_device);
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;
}
File diff suppressed because it is too large Load Diff
+640
View File
@@ -0,0 +1,640 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 1999-2007 by authors.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include <stdlib.h>
#include <stdio.h>
#include <memory.h>
#include "alMain.h"
#include "alu.h"
#include "threads.h"
#include "compat.h"
#include "backends/base.h"
#include <jack/jack.h>
#include <jack/ringbuffer.h>
static const ALCchar jackDevice[] = "JACK Default";
#ifdef HAVE_DYNLOAD
#define JACK_FUNCS(MAGIC) \
MAGIC(jack_client_open); \
MAGIC(jack_client_close); \
MAGIC(jack_client_name_size); \
MAGIC(jack_get_client_name); \
MAGIC(jack_connect); \
MAGIC(jack_activate); \
MAGIC(jack_deactivate); \
MAGIC(jack_port_register); \
MAGIC(jack_port_unregister); \
MAGIC(jack_port_get_buffer); \
MAGIC(jack_port_name); \
MAGIC(jack_get_ports); \
MAGIC(jack_free); \
MAGIC(jack_get_sample_rate); \
MAGIC(jack_set_error_function); \
MAGIC(jack_set_process_callback); \
MAGIC(jack_set_buffer_size_callback); \
MAGIC(jack_set_buffer_size); \
MAGIC(jack_get_buffer_size);
static void *jack_handle;
#define MAKE_FUNC(f) static __typeof(f) * p##f
JACK_FUNCS(MAKE_FUNC);
static __typeof(jack_error_callback) * pjack_error_callback;
#undef MAKE_FUNC
#define jack_client_open pjack_client_open
#define jack_client_close pjack_client_close
#define jack_client_name_size pjack_client_name_size
#define jack_get_client_name pjack_get_client_name
#define jack_connect pjack_connect
#define jack_activate pjack_activate
#define jack_deactivate pjack_deactivate
#define jack_port_register pjack_port_register
#define jack_port_unregister pjack_port_unregister
#define jack_port_get_buffer pjack_port_get_buffer
#define jack_port_name pjack_port_name
#define jack_get_ports pjack_get_ports
#define jack_free pjack_free
#define jack_get_sample_rate pjack_get_sample_rate
#define jack_set_error_function pjack_set_error_function
#define jack_set_process_callback pjack_set_process_callback
#define jack_set_buffer_size_callback pjack_set_buffer_size_callback
#define jack_set_buffer_size pjack_set_buffer_size
#define jack_get_buffer_size pjack_get_buffer_size
#define jack_error_callback (*pjack_error_callback)
#endif
static jack_options_t ClientOptions = JackNullOption;
static ALCboolean jack_load(void)
{
ALCboolean error = ALC_FALSE;
#ifdef HAVE_DYNLOAD
if(!jack_handle)
{
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;
}
alstr_reset(&missing_funcs);
}
#endif
return !error;
}
typedef struct ALCjackPlayback {
DERIVE_FROM_TYPE(ALCbackend);
jack_client_t *Client;
jack_port_t *Port[MAX_OUTPUT_CHANNELS];
ll_ringbuffer_t *Ring;
alcnd_t Cond;
volatile int killNow;
althrd_t thread;
} ALCjackPlayback;
static int ALCjackPlayback_bufferSizeNotify(jack_nframes_t numframes, void *arg);
static int ALCjackPlayback_process(jack_nframes_t numframes, void *arg);
static int ALCjackPlayback_mixerProc(void *arg);
static void ALCjackPlayback_Construct(ALCjackPlayback *self, ALCdevice *device);
static void ALCjackPlayback_Destruct(ALCjackPlayback *self);
static ALCenum ALCjackPlayback_open(ALCjackPlayback *self, const ALCchar *name);
static void ALCjackPlayback_close(ALCjackPlayback *self);
static ALCboolean ALCjackPlayback_reset(ALCjackPlayback *self);
static ALCboolean ALCjackPlayback_start(ALCjackPlayback *self);
static void ALCjackPlayback_stop(ALCjackPlayback *self);
static DECLARE_FORWARD2(ALCjackPlayback, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
static DECLARE_FORWARD(ALCjackPlayback, ALCbackend, ALCuint, availableSamples)
static ClockLatency ALCjackPlayback_getClockLatency(ALCjackPlayback *self);
static DECLARE_FORWARD(ALCjackPlayback, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCjackPlayback, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCjackPlayback)
DEFINE_ALCBACKEND_VTABLE(ALCjackPlayback);
static void ALCjackPlayback_Construct(ALCjackPlayback *self, ALCdevice *device)
{
ALuint i;
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(ALCjackPlayback, ALCbackend, self);
alcnd_init(&self->Cond);
self->Client = NULL;
for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
self->Port[i] = NULL;
self->Ring = NULL;
self->killNow = 1;
}
static void ALCjackPlayback_Destruct(ALCjackPlayback *self)
{
ALuint i;
if(self->Client)
{
for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
{
if(self->Port[i])
jack_port_unregister(self->Client, self->Port[i]);
self->Port[i] = NULL;
}
jack_client_close(self->Client);
self->Client = NULL;
}
alcnd_destroy(&self->Cond);
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
}
static int ALCjackPlayback_bufferSizeNotify(jack_nframes_t numframes, void *arg)
{
ALCjackPlayback *self = arg;
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
ALuint bufsize;
ALCjackPlayback_lock(self);
device->UpdateSize = numframes;
device->NumUpdates = 2;
bufsize = device->UpdateSize;
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, device->AmbiOrder)
);
if(!self->Ring)
{
ERR("Failed to reallocate ringbuffer\n");
aluHandleDisconnect(device);
}
ALCjackPlayback_unlock(self);
return 0;
}
static int ALCjackPlayback_process(jack_nframes_t numframes, void *arg)
{
ALCjackPlayback *self = arg;
jack_default_audio_sample_t *out[MAX_OUTPUT_CHANNELS];
ll_ringbuffer_data_t data[2];
jack_nframes_t total = 0;
jack_nframes_t todo;
ALsizei i, c, numchans;
ll_ringbuffer_get_read_vector(self->Ring, data);
for(c = 0;c < MAX_OUTPUT_CHANNELS && self->Port[c];c++)
out[c] = jack_port_get_buffer(self->Port[c], numframes);
numchans = c;
todo = minu(numframes, data[0].len);
for(c = 0;c < numchans;c++)
{
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;
todo = minu(numframes-total, data[1].len);
if(todo > 0)
{
for(c = 0;c < 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;
}
ll_ringbuffer_read_advance(self->Ring, total);
alcnd_signal(&self->Cond);
if(numframes > total)
{
todo = numframes-total;
for(c = 0;c < numchans;c++)
{
for(i = 0;(jack_nframes_t)i < todo;i++)
out[c][i] = 0.0f;
}
}
return 0;
}
static int ALCjackPlayback_mixerProc(void *arg)
{
ALCjackPlayback *self = arg;
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
ll_ringbuffer_data_t data[2];
SetRTPriority();
althrd_setname(althrd_current(), MIXER_THREAD_NAME);
ALCjackPlayback_lock(self);
while(!self->killNow && device->Connected)
{
ALuint todo, len1, len2;
/* NOTE: Unfortunately, there is an unavoidable race condition here.
* It's possible for the process() method to run, updating the read
* pointer and signaling the condition variable, in between the mixer
* loop checking the write size and waiting for the condition variable.
* This will cause the mixer loop to wait until the *next* process()
* invocation, most likely writing silence for it.
*
* However, this should only happen if the mixer is running behind
* anyway (as ideally we'll be asleep in alcnd_wait by the time the
* process() method is invoked), so this behavior is not unwarranted.
* It's unfortunate since it'll be wasting time sleeping that could be
* used to catch up, but there's no way around it without blocking in
* the process() method.
*/
if(ll_ringbuffer_write_space(self->Ring) < device->UpdateSize)
{
alcnd_wait(&self->Cond, &STATIC_CAST(ALCbackend,self)->mMutex);
continue;
}
ll_ringbuffer_get_write_vector(self->Ring, data);
todo = data[0].len + data[1].len;
todo -= todo%device->UpdateSize;
len1 = minu(data[0].len, todo);
len2 = minu(data[1].len, todo-len1);
aluMixData(device, data[0].buf, len1);
if(len2 > 0)
aluMixData(device, data[1].buf, len2);
ll_ringbuffer_write_advance(self->Ring, todo);
}
ALCjackPlayback_unlock(self);
return 0;
}
static ALCenum ALCjackPlayback_open(ALCjackPlayback *self, const ALCchar *name)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
const char *client_name = "alsoft";
jack_status_t status;
if(!name)
name = jackDevice;
else if(strcmp(name, jackDevice) != 0)
return ALC_INVALID_VALUE;
self->Client = jack_client_open(client_name, ClientOptions, &status, NULL);
if(self->Client == NULL)
{
ERR("jack_client_open() failed, status = 0x%02x\n", status);
return ALC_INVALID_VALUE;
}
if((status&JackServerStarted))
TRACE("JACK server started\n");
if((status&JackNameNotUnique))
{
client_name = jack_get_client_name(self->Client);
TRACE("Client name not unique, got `%s' instead\n", client_name);
}
jack_set_process_callback(self->Client, ALCjackPlayback_process, self);
jack_set_buffer_size_callback(self->Client, ALCjackPlayback_bufferSizeNotify, self);
alstr_copy_cstr(&device->DeviceName, name);
return ALC_NO_ERROR;
}
static void ALCjackPlayback_close(ALCjackPlayback *self)
{
ALuint i;
for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
{
if(self->Port[i])
jack_port_unregister(self->Client, self->Port[i]);
self->Port[i] = NULL;
}
jack_client_close(self->Client);
self->Client = NULL;
}
static ALCboolean ALCjackPlayback_reset(ALCjackPlayback *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
ALsizei numchans, i;
ALuint bufsize;
for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
{
if(self->Port[i])
jack_port_unregister(self->Client, self->Port[i]);
self->Port[i] = NULL;
}
/* Ignore the requested buffer metrics and just keep one JACK-sized buffer
* ready for when requested. Note that one period's worth of audio in the
* ring buffer will always be left unfilled because one element of the ring
* buffer will not be writeable, and we only write in period-sized chunks.
*/
device->Frequency = jack_get_sample_rate(self->Client);
device->UpdateSize = jack_get_buffer_size(self->Client);
device->NumUpdates = 2;
bufsize = device->UpdateSize;
if(ConfigValueUInt(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, device->AmbiOrder);
for(i = 0;i < numchans;i++)
{
char name[64];
snprintf(name, sizeof(name), "channel_%d", i+1);
self->Port[i] = jack_port_register(self->Client, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
if(self->Port[i] == NULL)
{
ERR("Not enough JACK ports available for %s output\n", DevFmtChannelsString(device->FmtChans));
if(i == 0) return ALC_FALSE;
break;
}
}
if(i < numchans)
{
if(i == 1)
device->FmtChans = DevFmtMono;
else
{
for(--i;i >= 2;i--)
{
jack_port_unregister(self->Client, self->Port[i]);
self->Port[i] = NULL;
}
device->FmtChans = DevFmtStereo;
}
}
ll_ringbuffer_free(self->Ring);
self->Ring = ll_ringbuffer_create(bufsize,
FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder)
);
if(!self->Ring)
{
ERR("Failed to allocate ringbuffer\n");
return ALC_FALSE;
}
SetDefaultChannelOrder(device);
return ALC_TRUE;
}
static ALCboolean ALCjackPlayback_start(ALCjackPlayback *self)
{
const char **ports;
ALsizei i;
if(jack_activate(self->Client))
{
ERR("Failed to activate client\n");
return ALC_FALSE;
}
ports = jack_get_ports(self->Client, NULL, NULL, JackPortIsPhysical|JackPortIsInput);
if(ports == NULL)
{
ERR("No physical playback ports found\n");
jack_deactivate(self->Client);
return ALC_FALSE;
}
for(i = 0;i < MAX_OUTPUT_CHANNELS && self->Port[i];i++)
{
if(!ports[i])
{
ERR("No physical playback port for \"%s\"\n", jack_port_name(self->Port[i]));
break;
}
if(jack_connect(self->Client, jack_port_name(self->Port[i]), ports[i]))
ERR("Failed to connect output port \"%s\" to \"%s\"\n", jack_port_name(self->Port[i]), ports[i]);
}
jack_free(ports);
self->killNow = 0;
if(althrd_create(&self->thread, ALCjackPlayback_mixerProc, self) != althrd_success)
{
jack_deactivate(self->Client);
return ALC_FALSE;
}
return ALC_TRUE;
}
static void ALCjackPlayback_stop(ALCjackPlayback *self)
{
int res;
if(self->killNow)
return;
self->killNow = 1;
/* Lock the backend to ensure we don't flag the mixer to die and signal the
* mixer to wake up in between it checking the flag and going to sleep and
* wait for a wakeup (potentially leading to it never waking back up to see
* the flag). */
ALCjackPlayback_lock(self);
ALCjackPlayback_unlock(self);
alcnd_signal(&self->Cond);
althrd_join(self->thread, &res);
jack_deactivate(self->Client);
}
static ClockLatency ALCjackPlayback_getClockLatency(ALCjackPlayback *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
ClockLatency ret;
ALCjackPlayback_lock(self);
ret.ClockTime = GetDeviceClockTime(device);
ret.Latency = ll_ringbuffer_read_space(self->Ring) * DEVICE_CLOCK_RES /
device->Frequency;
ALCjackPlayback_unlock(self);
return ret;
}
static void jack_msg_handler(const char *message)
{
WARN("%s\n", message);
}
typedef struct ALCjackBackendFactory {
DERIVE_FROM_TYPE(ALCbackendFactory);
} ALCjackBackendFactory;
#define ALCJACKBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCjackBackendFactory, ALCbackendFactory) } }
static ALCboolean ALCjackBackendFactory_init(ALCjackBackendFactory* UNUSED(self))
{
void (*old_error_cb)(const char*);
jack_client_t *client;
jack_status_t status;
if(!jack_load())
return ALC_FALSE;
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);
if((status&JackServerFailed) && !(ClientOptions&JackNoStartServer))
ERR("Unable to connect to JACK server\n");
return ALC_FALSE;
}
jack_client_close(client);
return ALC_TRUE;
}
static void ALCjackBackendFactory_deinit(ALCjackBackendFactory* UNUSED(self))
{
#ifdef HAVE_DYNLOAD
if(jack_handle)
CloseLib(jack_handle);
jack_handle = NULL;
#endif
}
static ALCboolean ALCjackBackendFactory_querySupport(ALCjackBackendFactory* UNUSED(self), ALCbackend_Type type)
{
if(type == ALCbackend_Playback)
return ALC_TRUE;
return ALC_FALSE;
}
static void ALCjackBackendFactory_probe(ALCjackBackendFactory* UNUSED(self), enum DevProbe type)
{
switch(type)
{
case ALL_DEVICE_PROBE:
AppendAllDevicesList(jackDevice);
break;
case CAPTURE_DEVICE_PROBE:
break;
}
}
static ALCbackend* ALCjackBackendFactory_createBackend(ALCjackBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
{
if(type == ALCbackend_Playback)
{
ALCjackPlayback *backend;
NEW_OBJ(backend, ALCjackPlayback)(device);
if(!backend) return NULL;
return STATIC_CAST(ALCbackend, backend);
}
return NULL;
}
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCjackBackendFactory);
ALCbackendFactory *ALCjackBackendFactory_getFactory(void)
{
static ALCjackBackendFactory factory = ALCJACKBACKENDFACTORY_INITIALIZER;
return STATIC_CAST(ALCbackendFactory, &factory);
}
+133
View File
@@ -0,0 +1,133 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 2011 by Chris Robinson
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include <stdlib.h>
#include "alMain.h"
#include "alu.h"
#include "backends/base.h"
typedef struct ALCloopback {
DERIVE_FROM_TYPE(ALCbackend);
} ALCloopback;
static void ALCloopback_Construct(ALCloopback *self, ALCdevice *device);
static DECLARE_FORWARD(ALCloopback, ALCbackend, void, Destruct)
static ALCenum ALCloopback_open(ALCloopback *self, const ALCchar *name);
static void ALCloopback_close(ALCloopback *self);
static ALCboolean ALCloopback_reset(ALCloopback *self);
static ALCboolean ALCloopback_start(ALCloopback *self);
static void ALCloopback_stop(ALCloopback *self);
static DECLARE_FORWARD2(ALCloopback, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
static DECLARE_FORWARD(ALCloopback, ALCbackend, ALCuint, availableSamples)
static DECLARE_FORWARD(ALCloopback, ALCbackend, ClockLatency, getClockLatency)
static DECLARE_FORWARD(ALCloopback, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCloopback, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCloopback)
DEFINE_ALCBACKEND_VTABLE(ALCloopback);
static void ALCloopback_Construct(ALCloopback *self, ALCdevice *device)
{
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(ALCloopback, ALCbackend, self);
}
static ALCenum ALCloopback_open(ALCloopback *self, const ALCchar *name)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
alstr_copy_cstr(&device->DeviceName, name);
return ALC_NO_ERROR;
}
static void ALCloopback_close(ALCloopback* UNUSED(self))
{
}
static ALCboolean ALCloopback_reset(ALCloopback *self)
{
SetDefaultWFXChannelOrder(STATIC_CAST(ALCbackend, self)->mDevice);
return ALC_TRUE;
}
static ALCboolean ALCloopback_start(ALCloopback* UNUSED(self))
{
return ALC_TRUE;
}
static void ALCloopback_stop(ALCloopback* UNUSED(self))
{
}
typedef struct ALCloopbackFactory {
DERIVE_FROM_TYPE(ALCbackendFactory);
} ALCloopbackFactory;
#define ALCNULLBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCloopbackFactory, ALCbackendFactory) } }
ALCbackendFactory *ALCloopbackFactory_getFactory(void);
static ALCboolean ALCloopbackFactory_init(ALCloopbackFactory *self);
static DECLARE_FORWARD(ALCloopbackFactory, ALCbackendFactory, void, deinit)
static ALCboolean ALCloopbackFactory_querySupport(ALCloopbackFactory *self, ALCbackend_Type type);
static void ALCloopbackFactory_probe(ALCloopbackFactory *self, enum DevProbe type);
static ALCbackend* ALCloopbackFactory_createBackend(ALCloopbackFactory *self, ALCdevice *device, ALCbackend_Type type);
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCloopbackFactory);
ALCbackendFactory *ALCloopbackFactory_getFactory(void)
{
static ALCloopbackFactory factory = ALCNULLBACKENDFACTORY_INITIALIZER;
return STATIC_CAST(ALCbackendFactory, &factory);
}
static ALCboolean ALCloopbackFactory_init(ALCloopbackFactory* UNUSED(self))
{
return ALC_TRUE;
}
static ALCboolean ALCloopbackFactory_querySupport(ALCloopbackFactory* UNUSED(self), ALCbackend_Type type)
{
if(type == ALCbackend_Loopback)
return ALC_TRUE;
return ALC_FALSE;
}
static void ALCloopbackFactory_probe(ALCloopbackFactory* UNUSED(self), enum DevProbe UNUSED(type))
{
}
static ALCbackend* ALCloopbackFactory_createBackend(ALCloopbackFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
{
if(type == ALCbackend_Loopback)
{
ALCloopback *backend;
NEW_OBJ(backend, ALCloopback)(device);
if(!backend) return NULL;
return STATIC_CAST(ALCbackend, backend);
}
return NULL;
}
File diff suppressed because it is too large Load Diff
+225
View File
@@ -0,0 +1,225 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 2010 by Chris Robinson
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include <stdlib.h>
#ifdef HAVE_WINDOWS_H
#include <windows.h>
#endif
#include "alMain.h"
#include "alu.h"
#include "threads.h"
#include "compat.h"
#include "backends/base.h"
typedef struct ALCnullBackend {
DERIVE_FROM_TYPE(ALCbackend);
volatile int killNow;
althrd_t thread;
} ALCnullBackend;
static int ALCnullBackend_mixerProc(void *ptr);
static void ALCnullBackend_Construct(ALCnullBackend *self, ALCdevice *device);
static DECLARE_FORWARD(ALCnullBackend, ALCbackend, void, Destruct)
static ALCenum ALCnullBackend_open(ALCnullBackend *self, const ALCchar *name);
static void ALCnullBackend_close(ALCnullBackend *self);
static ALCboolean ALCnullBackend_reset(ALCnullBackend *self);
static ALCboolean ALCnullBackend_start(ALCnullBackend *self);
static void ALCnullBackend_stop(ALCnullBackend *self);
static DECLARE_FORWARD2(ALCnullBackend, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
static DECLARE_FORWARD(ALCnullBackend, ALCbackend, ALCuint, availableSamples)
static DECLARE_FORWARD(ALCnullBackend, ALCbackend, ClockLatency, getClockLatency)
static DECLARE_FORWARD(ALCnullBackend, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCnullBackend, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCnullBackend)
DEFINE_ALCBACKEND_VTABLE(ALCnullBackend);
static const ALCchar nullDevice[] = "No Output";
static void ALCnullBackend_Construct(ALCnullBackend *self, ALCdevice *device)
{
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(ALCnullBackend, ALCbackend, self);
}
static int ALCnullBackend_mixerProc(void *ptr)
{
ALCnullBackend *self = (ALCnullBackend*)ptr;
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
struct timespec now, start;
ALuint64 avail, done;
const long restTime = (long)((ALuint64)device->UpdateSize * 1000000000 /
device->Frequency / 2);
SetRTPriority();
althrd_setname(althrd_current(), MIXER_THREAD_NAME);
done = 0;
if(altimespec_get(&start, AL_TIME_UTC) != AL_TIME_UTC)
{
ERR("Failed to get starting time\n");
return 1;
}
while(!self->killNow && device->Connected)
{
if(altimespec_get(&now, AL_TIME_UTC) != AL_TIME_UTC)
{
ERR("Failed to get current time\n");
return 1;
}
avail = (now.tv_sec - start.tv_sec) * device->Frequency;
avail += (ALint64)(now.tv_nsec - start.tv_nsec) * device->Frequency / 1000000000;
if(avail < done)
{
/* Oops, time skipped backwards. Reset the number of samples done
* with one update available since we (likely) just came back from
* sleeping. */
done = avail - device->UpdateSize;
}
if(avail-done < device->UpdateSize)
al_nssleep(restTime);
else while(avail-done >= device->UpdateSize)
{
ALCnullBackend_lock(self);
aluMixData(device, NULL, device->UpdateSize);
ALCnullBackend_unlock(self);
done += device->UpdateSize;
}
}
return 0;
}
static ALCenum ALCnullBackend_open(ALCnullBackend *self, const ALCchar *name)
{
ALCdevice *device;
if(!name)
name = nullDevice;
else if(strcmp(name, nullDevice) != 0)
return ALC_INVALID_VALUE;
device = STATIC_CAST(ALCbackend, self)->mDevice;
alstr_copy_cstr(&device->DeviceName, name);
return ALC_NO_ERROR;
}
static void ALCnullBackend_close(ALCnullBackend* UNUSED(self))
{
}
static ALCboolean ALCnullBackend_reset(ALCnullBackend *self)
{
SetDefaultWFXChannelOrder(STATIC_CAST(ALCbackend, self)->mDevice);
return ALC_TRUE;
}
static ALCboolean ALCnullBackend_start(ALCnullBackend *self)
{
self->killNow = 0;
if(althrd_create(&self->thread, ALCnullBackend_mixerProc, self) != althrd_success)
return ALC_FALSE;
return ALC_TRUE;
}
static void ALCnullBackend_stop(ALCnullBackend *self)
{
int res;
if(self->killNow)
return;
self->killNow = 1;
althrd_join(self->thread, &res);
}
typedef struct ALCnullBackendFactory {
DERIVE_FROM_TYPE(ALCbackendFactory);
} ALCnullBackendFactory;
#define ALCNULLBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCnullBackendFactory, ALCbackendFactory) } }
ALCbackendFactory *ALCnullBackendFactory_getFactory(void);
static ALCboolean ALCnullBackendFactory_init(ALCnullBackendFactory *self);
static DECLARE_FORWARD(ALCnullBackendFactory, ALCbackendFactory, void, deinit)
static ALCboolean ALCnullBackendFactory_querySupport(ALCnullBackendFactory *self, ALCbackend_Type type);
static void ALCnullBackendFactory_probe(ALCnullBackendFactory *self, enum DevProbe type);
static ALCbackend* ALCnullBackendFactory_createBackend(ALCnullBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCnullBackendFactory);
ALCbackendFactory *ALCnullBackendFactory_getFactory(void)
{
static ALCnullBackendFactory factory = ALCNULLBACKENDFACTORY_INITIALIZER;
return STATIC_CAST(ALCbackendFactory, &factory);
}
static ALCboolean ALCnullBackendFactory_init(ALCnullBackendFactory* UNUSED(self))
{
return ALC_TRUE;
}
static ALCboolean ALCnullBackendFactory_querySupport(ALCnullBackendFactory* UNUSED(self), ALCbackend_Type type)
{
if(type == ALCbackend_Playback)
return ALC_TRUE;
return ALC_FALSE;
}
static void ALCnullBackendFactory_probe(ALCnullBackendFactory* UNUSED(self), enum DevProbe type)
{
switch(type)
{
case ALL_DEVICE_PROBE:
AppendAllDevicesList(nullDevice);
break;
case CAPTURE_DEVICE_PROBE:
break;
}
}
static ALCbackend* ALCnullBackendFactory_createBackend(ALCnullBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
{
if(type == ALCbackend_Playback)
{
ALCnullBackend *backend;
NEW_OBJ(backend, ALCnullBackend)(device);
if(!backend) return NULL;
return STATIC_CAST(ALCbackend, backend);
}
return NULL;
}
File diff suppressed because it is too large Load Diff
+868
View File
@@ -0,0 +1,868 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 1999-2007 by authors.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <memory.h>
#include <unistd.h>
#include <errno.h>
#include <math.h>
#include "alMain.h"
#include "alu.h"
#include "threads.h"
#include "compat.h"
#include "backends/base.h"
#include <sys/soundcard.h>
/*
* The OSS documentation talks about SOUND_MIXER_READ, but the header
* only contains MIXER_READ. Play safe. Same for WRITE.
*/
#ifndef SOUND_MIXER_READ
#define SOUND_MIXER_READ MIXER_READ
#endif
#ifndef SOUND_MIXER_WRITE
#define SOUND_MIXER_WRITE MIXER_WRITE
#endif
#if defined(SOUND_VERSION) && (SOUND_VERSION < 0x040000)
#define ALC_OSS_COMPAT
#endif
#ifndef SNDCTL_AUDIOINFO
#define ALC_OSS_COMPAT
#endif
/*
* FreeBSD strongly discourages the use of specific devices,
* such as those returned in oss_audioinfo.devnode
*/
#ifdef __FreeBSD__
#define ALC_OSS_DEVNODE_TRUC
#endif
struct oss_device {
const ALCchar *handle;
const char *path;
struct oss_device *next;
};
static struct oss_device oss_playback = {
"OSS Default",
"/dev/dsp",
NULL
};
static struct oss_device oss_capture = {
"OSS Default",
"/dev/dsp",
NULL
};
#ifdef ALC_OSS_COMPAT
#define DSP_CAP_OUTPUT 0x00020000
#define DSP_CAP_INPUT 0x00010000
static void ALCossListPopulate(struct oss_device *UNUSED(devlist), int UNUSED(type_flag))
{
}
#else
#ifndef HAVE_STRNLEN
static size_t strnlen(const char *str, size_t maxlen)
{
const char *end = memchr(str, 0, maxlen);
if(!end) return maxlen;
return end - str;
}
#endif
static void ALCossListAppend(struct oss_device *list, const char *handle, size_t hlen, const char *path, size_t plen)
{
struct oss_device *next;
struct oss_device *last;
size_t i;
/* skip the first item "OSS Default" */
last = list;
next = list->next;
#ifdef ALC_OSS_DEVNODE_TRUC
for(i = 0;i < plen;i++)
{
if(path[i] == '.')
{
if(strncmp(path + i, handle + hlen + i - plen, plen - i) == 0)
hlen = hlen + i - plen;
plen = i;
}
}
#else
(void)i;
#endif
if(handle[0] == '\0')
{
handle = path;
hlen = plen;
}
while(next != NULL)
{
if(strncmp(next->path, path, plen) == 0)
return;
last = next;
next = next->next;
}
next = (struct oss_device*)malloc(sizeof(struct oss_device) + hlen + plen + 2);
next->handle = (char*)(next + 1);
next->path = next->handle + hlen + 1;
next->next = NULL;
last->next = next;
strncpy((char*)next->handle, handle, hlen);
((char*)next->handle)[hlen] = '\0';
strncpy((char*)next->path, path, plen);
((char*)next->path)[plen] = '\0';
TRACE("Got device \"%s\", \"%s\"\n", next->handle, next->path);
}
static void ALCossListPopulate(struct oss_device *devlist, int type_flag)
{
struct oss_sysinfo si;
struct oss_audioinfo ai;
int fd, i;
if((fd=open("/dev/mixer", O_RDONLY)) < 0)
{
TRACE("Could not open /dev/mixer: %s\n", strerror(errno));
return;
}
if(ioctl(fd, SNDCTL_SYSINFO, &si) == -1)
{
TRACE("SNDCTL_SYSINFO failed: %s\n", strerror(errno));
goto done;
}
for(i = 0;i < si.numaudios;i++)
{
const char *handle;
size_t len;
ai.dev = i;
if(ioctl(fd, SNDCTL_AUDIOINFO, &ai) == -1)
{
ERR("SNDCTL_AUDIOINFO (%d) failed: %s\n", i, strerror(errno));
continue;
}
if(ai.devnode[0] == '\0')
continue;
if(ai.handle[0] != '\0')
{
len = strnlen(ai.handle, sizeof(ai.handle));
handle = ai.handle;
}
else
{
len = strnlen(ai.name, sizeof(ai.name));
handle = ai.name;
}
if((ai.caps&type_flag))
ALCossListAppend(devlist, handle, len, ai.devnode,
strnlen(ai.devnode, sizeof(ai.devnode)));
}
done:
close(fd);
}
#endif
static void ALCossListFree(struct oss_device *list)
{
struct oss_device *cur;
if(list == NULL)
return;
/* skip the first item "OSS Default" */
cur = list->next;
list->next = NULL;
while(cur != NULL)
{
struct oss_device *next = cur->next;
free(cur);
cur = next;
}
}
static int log2i(ALCuint x)
{
int y = 0;
while (x > 1)
{
x >>= 1;
y++;
}
return y;
}
typedef struct ALCplaybackOSS {
DERIVE_FROM_TYPE(ALCbackend);
int fd;
ALubyte *mix_data;
int data_size;
ATOMIC(ALenum) killNow;
althrd_t thread;
} ALCplaybackOSS;
static int ALCplaybackOSS_mixerProc(void *ptr);
static void ALCplaybackOSS_Construct(ALCplaybackOSS *self, ALCdevice *device);
static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, void, Destruct)
static ALCenum ALCplaybackOSS_open(ALCplaybackOSS *self, const ALCchar *name);
static void ALCplaybackOSS_close(ALCplaybackOSS *self);
static ALCboolean ALCplaybackOSS_reset(ALCplaybackOSS *self);
static ALCboolean ALCplaybackOSS_start(ALCplaybackOSS *self);
static void ALCplaybackOSS_stop(ALCplaybackOSS *self);
static DECLARE_FORWARD2(ALCplaybackOSS, ALCbackend, ALCenum, captureSamples, ALCvoid*, ALCuint)
static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, ALCuint, availableSamples)
static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, ClockLatency, getClockLatency)
static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCplaybackOSS)
DEFINE_ALCBACKEND_VTABLE(ALCplaybackOSS);
static int ALCplaybackOSS_mixerProc(void *ptr)
{
ALCplaybackOSS *self = (ALCplaybackOSS*)ptr;
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
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);
frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
ALCplaybackOSS_lock(self);
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;
}
else if(sret == 0)
{
WARN("select timeout\n");
continue;
}
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;
}
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)
{
struct oss_device *dev = &oss_playback;
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
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)
break;
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_WRONLY);
if(self->fd == -1)
{
ERR("Could not open %s: %s\n", dev->path, strerror(errno));
return ALC_INVALID_VALUE;
}
alstr_copy_cstr(&device->DeviceName, name);
return ALC_NO_ERROR;
}
static void ALCplaybackOSS_close(ALCplaybackOSS *self)
{
close(self->fd);
self->fd = -1;
}
static ALCboolean ALCplaybackOSS_reset(ALCplaybackOSS *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
int numFragmentsLogSize;
int log2FragmentSize;
unsigned int periods;
audio_buf_info info;
ALuint frameSize;
int numChannels;
int ossFormat;
int ossSpeed;
char *err;
switch(device->FmtType)
{
case DevFmtByte:
ossFormat = AFMT_S8;
break;
case DevFmtUByte:
ossFormat = AFMT_U8;
break;
case DevFmtUShort:
case DevFmtInt:
case DevFmtUInt:
case DevFmtFloat:
device->FmtType = DevFmtShort;
/* fall-through */
case DevFmtShort:
ossFormat = AFMT_S16_NE;
break;
}
periods = device->NumUpdates;
numChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
ossSpeed = device->Frequency;
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) { \
err = #func; \
goto err; \
}
/* Don't fail if SETFRAGMENT fails. We can handle just about anything
* that's reported back via GETOSPACE */
ioctl(self->fd, SNDCTL_DSP_SETFRAGMENT, &numFragmentsLogSize);
CHECKERR(ioctl(self->fd, SNDCTL_DSP_SETFMT, &ossFormat));
CHECKERR(ioctl(self->fd, SNDCTL_DSP_CHANNELS, &numChannels));
CHECKERR(ioctl(self->fd, SNDCTL_DSP_SPEED, &ossSpeed));
CHECKERR(ioctl(self->fd, SNDCTL_DSP_GETOSPACE, &info));
if(0)
{
err:
ERR("%s failed: %s\n", err, strerror(errno));
return ALC_FALSE;
}
#undef CHECKERR
if((int)ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder) != numChannels)
{
ERR("Failed to set %s, got %d channels instead\n", DevFmtChannelsString(device->FmtChans), numChannels);
return ALC_FALSE;
}
if(!((ossFormat == AFMT_S8 && device->FmtType == DevFmtByte) ||
(ossFormat == AFMT_U8 && device->FmtType == DevFmtUByte) ||
(ossFormat == AFMT_S16_NE && device->FmtType == DevFmtShort)))
{
ERR("Failed to set %s samples, got OSS format %#x\n", DevFmtTypeString(device->FmtType), ossFormat);
return ALC_FALSE;
}
device->Frequency = ossSpeed;
device->UpdateSize = info.fragsize / frameSize;
device->NumUpdates = info.fragments;
SetDefaultChannelOrder(device);
return ALC_TRUE;
}
static ALCboolean ALCplaybackOSS_start(ALCplaybackOSS *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
self->data_size = device->UpdateSize * FrameSizeFromDevFmt(
device->FmtChans, device->FmtType, device->AmbiOrder
);
self->mix_data = calloc(1, self->data_size);
ATOMIC_STORE_SEQ(&self->killNow, AL_FALSE);
if(althrd_create(&self->thread, ALCplaybackOSS_mixerProc, self) != althrd_success)
{
free(self->mix_data);
self->mix_data = NULL;
return ALC_FALSE;
}
return ALC_TRUE;
}
static void ALCplaybackOSS_stop(ALCplaybackOSS *self)
{
int res;
if(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));
free(self->mix_data);
self->mix_data = NULL;
}
typedef struct ALCcaptureOSS {
DERIVE_FROM_TYPE(ALCbackend);
int fd;
ll_ringbuffer_t *ring;
ATOMIC(ALenum) killNow;
althrd_t thread;
} ALCcaptureOSS;
static int ALCcaptureOSS_recordProc(void *ptr);
static void ALCcaptureOSS_Construct(ALCcaptureOSS *self, ALCdevice *device);
static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, void, Destruct)
static ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name);
static void ALCcaptureOSS_close(ALCcaptureOSS *self);
static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, ALCboolean, reset)
static ALCboolean ALCcaptureOSS_start(ALCcaptureOSS *self);
static void ALCcaptureOSS_stop(ALCcaptureOSS *self);
static ALCenum ALCcaptureOSS_captureSamples(ALCcaptureOSS *self, ALCvoid *buffer, ALCuint samples);
static ALCuint ALCcaptureOSS_availableSamples(ALCcaptureOSS *self);
static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, ClockLatency, getClockLatency)
static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCcaptureOSS)
DEFINE_ALCBACKEND_VTABLE(ALCcaptureOSS);
static int ALCcaptureOSS_recordProc(void *ptr)
{
ALCcaptureOSS *self = (ALCcaptureOSS*)ptr;
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
struct timeval timeout;
int frame_size;
fd_set rfds;
ssize_t amt;
int sret;
SetRTPriority();
althrd_setname(althrd_current(), RECORD_THREAD_NAME);
frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
while(!ATOMIC_LOAD_SEQ(&self->killNow))
{
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));
ALCcaptureOSS_lock(self);
aluHandleDisconnect(device);
ALCcaptureOSS_unlock(self);
break;
}
ll_ringbuffer_write_advance(self->ring, amt/frame_size);
}
}
return 0;
}
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)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
struct oss_device *dev = &oss_capture;
int numFragmentsLogSize;
int log2FragmentSize;
unsigned int periods;
audio_buf_info info;
ALuint frameSize;
int numChannels;
int ossFormat;
int ossSpeed;
char *err;
if(!name || 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)
break;
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)
{
ERR("Could not open %s: %s\n", dev->path, strerror(errno));
return ALC_INVALID_VALUE;
}
switch(device->FmtType)
{
case DevFmtByte:
ossFormat = AFMT_S8;
break;
case DevFmtUByte:
ossFormat = AFMT_U8;
break;
case DevFmtShort:
ossFormat = AFMT_S16_NE;
break;
case DevFmtUShort:
case DevFmtInt:
case DevFmtUInt:
case DevFmtFloat:
ERR("%s capture samples not supported\n", DevFmtTypeString(device->FmtType));
return ALC_INVALID_VALUE;
}
periods = 4;
numChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
frameSize = numChannels * BytesFromDevFmt(device->FmtType);
ossSpeed = device->Frequency;
log2FragmentSize = log2i(device->UpdateSize * device->NumUpdates *
frameSize / periods);
/* according to the OSS spec, 16 bytes are the minimum */
if (log2FragmentSize < 4)
log2FragmentSize = 4;
numFragmentsLogSize = (periods << 16) | log2FragmentSize;
#define CHECKERR(func) if((func) < 0) { \
err = #func; \
goto err; \
}
CHECKERR(ioctl(self->fd, SNDCTL_DSP_SETFRAGMENT, &numFragmentsLogSize));
CHECKERR(ioctl(self->fd, SNDCTL_DSP_SETFMT, &ossFormat));
CHECKERR(ioctl(self->fd, SNDCTL_DSP_CHANNELS, &numChannels));
CHECKERR(ioctl(self->fd, SNDCTL_DSP_SPEED, &ossSpeed));
CHECKERR(ioctl(self->fd, SNDCTL_DSP_GETISPACE, &info));
if(0)
{
err:
ERR("%s failed: %s\n", err, strerror(errno));
close(self->fd);
self->fd = -1;
return ALC_INVALID_VALUE;
}
#undef CHECKERR
if((int)ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder) != numChannels)
{
ERR("Failed to set %s, got %d channels instead\n", DevFmtChannelsString(device->FmtChans), numChannels);
close(self->fd);
self->fd = -1;
return ALC_INVALID_VALUE;
}
if(!((ossFormat == AFMT_S8 && device->FmtType == DevFmtByte) ||
(ossFormat == AFMT_U8 && device->FmtType == DevFmtUByte) ||
(ossFormat == AFMT_S16_NE && device->FmtType == DevFmtShort)))
{
ERR("Failed to set %s samples, got OSS format %#x\n", DevFmtTypeString(device->FmtType), ossFormat);
close(self->fd);
self->fd = -1;
return ALC_INVALID_VALUE;
}
self->ring = ll_ringbuffer_create(device->UpdateSize*device->NumUpdates + 1, frameSize);
if(!self->ring)
{
ERR("Ring buffer create failed\n");
close(self->fd);
self->fd = -1;
return ALC_OUT_OF_MEMORY;
}
alstr_copy_cstr(&device->DeviceName, name);
return ALC_NO_ERROR;
}
static void ALCcaptureOSS_close(ALCcaptureOSS *self)
{
close(self->fd);
self->fd = -1;
ll_ringbuffer_free(self->ring);
self->ring = NULL;
}
static ALCboolean ALCcaptureOSS_start(ALCcaptureOSS *self)
{
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)
{
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)
{
ll_ringbuffer_read(self->ring, buffer, samples);
return ALC_NO_ERROR;
}
static ALCuint ALCcaptureOSS_availableSamples(ALCcaptureOSS *self)
{
return ll_ringbuffer_read_space(self->ring);
}
typedef struct ALCossBackendFactory {
DERIVE_FROM_TYPE(ALCbackendFactory);
} ALCossBackendFactory;
#define ALCOSSBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCossBackendFactory, ALCbackendFactory) } }
ALCbackendFactory *ALCossBackendFactory_getFactory(void);
static ALCboolean ALCossBackendFactory_init(ALCossBackendFactory *self);
static void ALCossBackendFactory_deinit(ALCossBackendFactory *self);
static ALCboolean ALCossBackendFactory_querySupport(ALCossBackendFactory *self, ALCbackend_Type type);
static void ALCossBackendFactory_probe(ALCossBackendFactory *self, enum DevProbe type);
static ALCbackend* ALCossBackendFactory_createBackend(ALCossBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCossBackendFactory);
ALCbackendFactory *ALCossBackendFactory_getFactory(void)
{
static ALCossBackendFactory factory = ALCOSSBACKENDFACTORY_INITIALIZER;
return STATIC_CAST(ALCbackendFactory, &factory);
}
ALCboolean ALCossBackendFactory_init(ALCossBackendFactory* UNUSED(self))
{
ConfigValueStr(NULL, "oss", "device", &oss_playback.path);
ConfigValueStr(NULL, "oss", "capture", &oss_capture.path);
return ALC_TRUE;
}
void ALCossBackendFactory_deinit(ALCossBackendFactory* UNUSED(self))
{
ALCossListFree(&oss_playback);
ALCossListFree(&oss_capture);
}
ALCboolean ALCossBackendFactory_querySupport(ALCossBackendFactory* UNUSED(self), ALCbackend_Type type)
{
if(type == ALCbackend_Playback || type == ALCbackend_Capture)
return ALC_TRUE;
return ALC_FALSE;
}
void ALCossBackendFactory_probe(ALCossBackendFactory* UNUSED(self), enum DevProbe type)
{
struct oss_device *cur;
switch(type)
{
case ALL_DEVICE_PROBE:
ALCossListFree(&oss_playback);
ALCossListPopulate(&oss_playback, DSP_CAP_OUTPUT);
cur = &oss_playback;
while(cur != NULL)
{
#ifdef HAVE_STAT
struct stat buf;
if(stat(cur->path, &buf) == 0)
#endif
AppendAllDevicesList(cur->handle);
cur = cur->next;
}
break;
case CAPTURE_DEVICE_PROBE:
ALCossListFree(&oss_capture);
ALCossListPopulate(&oss_capture, DSP_CAP_INPUT);
cur = &oss_capture;
while(cur != NULL)
{
#ifdef HAVE_STAT
struct stat buf;
if(stat(cur->path, &buf) == 0)
#endif
AppendCaptureDeviceList(cur->handle);
cur = cur->next;
}
break;
}
}
ALCbackend* ALCossBackendFactory_createBackend(ALCossBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
{
if(type == ALCbackend_Playback)
{
ALCplaybackOSS *backend;
NEW_OBJ(backend, ALCplaybackOSS)(device);
if(!backend) return NULL;
return STATIC_CAST(ALCbackend, backend);
}
if(type == ALCbackend_Capture)
{
ALCcaptureOSS *backend;
NEW_OBJ(backend, ALCcaptureOSS)(device);
if(!backend) return NULL;
return STATIC_CAST(ALCbackend, backend);
}
return NULL;
}
+575
View File
@@ -0,0 +1,575 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 1999-2007 by authors.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "alMain.h"
#include "alu.h"
#include "compat.h"
#include "backends/base.h"
#include <portaudio.h>
static const ALCchar pa_device[] = "PortAudio Default";
#ifdef HAVE_DYNLOAD
static void *pa_handle;
#define MAKE_FUNC(x) static __typeof(x) * p##x
MAKE_FUNC(Pa_Initialize);
MAKE_FUNC(Pa_Terminate);
MAKE_FUNC(Pa_GetErrorText);
MAKE_FUNC(Pa_StartStream);
MAKE_FUNC(Pa_StopStream);
MAKE_FUNC(Pa_OpenStream);
MAKE_FUNC(Pa_CloseStream);
MAKE_FUNC(Pa_GetDefaultOutputDevice);
MAKE_FUNC(Pa_GetDefaultInputDevice);
MAKE_FUNC(Pa_GetStreamInfo);
#undef MAKE_FUNC
#define Pa_Initialize pPa_Initialize
#define Pa_Terminate pPa_Terminate
#define Pa_GetErrorText pPa_GetErrorText
#define Pa_StartStream pPa_StartStream
#define Pa_StopStream pPa_StopStream
#define Pa_OpenStream pPa_OpenStream
#define Pa_CloseStream pPa_CloseStream
#define Pa_GetDefaultOutputDevice pPa_GetDefaultOutputDevice
#define Pa_GetDefaultInputDevice pPa_GetDefaultInputDevice
#define Pa_GetStreamInfo pPa_GetStreamInfo
#endif
static ALCboolean pa_load(void)
{
PaError err;
#ifdef HAVE_DYNLOAD
if(!pa_handle)
{
#ifdef _WIN32
# define PALIB "portaudio.dll"
#elif defined(__APPLE__) && defined(__MACH__)
# define PALIB "libportaudio.2.dylib"
#elif defined(__OpenBSD__)
# define PALIB "libportaudio.so"
#else
# define PALIB "libportaudio.so.2"
#endif
pa_handle = LoadLib(PALIB);
if(!pa_handle)
return ALC_FALSE;
#define LOAD_FUNC(f) do { \
p##f = GetSymbol(pa_handle, #f); \
if(p##f == NULL) \
{ \
CloseLib(pa_handle); \
pa_handle = NULL; \
return ALC_FALSE; \
} \
} while(0)
LOAD_FUNC(Pa_Initialize);
LOAD_FUNC(Pa_Terminate);
LOAD_FUNC(Pa_GetErrorText);
LOAD_FUNC(Pa_StartStream);
LOAD_FUNC(Pa_StopStream);
LOAD_FUNC(Pa_OpenStream);
LOAD_FUNC(Pa_CloseStream);
LOAD_FUNC(Pa_GetDefaultOutputDevice);
LOAD_FUNC(Pa_GetDefaultInputDevice);
LOAD_FUNC(Pa_GetStreamInfo);
#undef LOAD_FUNC
if((err=Pa_Initialize()) != paNoError)
{
ERR("Pa_Initialize() returned an error: %s\n", Pa_GetErrorText(err));
CloseLib(pa_handle);
pa_handle = NULL;
return ALC_FALSE;
}
}
#else
if((err=Pa_Initialize()) != paNoError)
{
ERR("Pa_Initialize() returned an error: %s\n", Pa_GetErrorText(err));
return ALC_FALSE;
}
#endif
return ALC_TRUE;
}
typedef struct ALCportPlayback {
DERIVE_FROM_TYPE(ALCbackend);
PaStream *stream;
PaStreamParameters params;
ALuint update_size;
} ALCportPlayback;
static int ALCportPlayback_WriteCallback(const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo,
const PaStreamCallbackFlags statusFlags, void *userData);
static void ALCportPlayback_Construct(ALCportPlayback *self, ALCdevice *device);
static void ALCportPlayback_Destruct(ALCportPlayback *self);
static ALCenum ALCportPlayback_open(ALCportPlayback *self, const ALCchar *name);
static void ALCportPlayback_close(ALCportPlayback *self);
static ALCboolean ALCportPlayback_reset(ALCportPlayback *self);
static ALCboolean ALCportPlayback_start(ALCportPlayback *self);
static void ALCportPlayback_stop(ALCportPlayback *self);
static DECLARE_FORWARD2(ALCportPlayback, ALCbackend, ALCenum, captureSamples, ALCvoid*, ALCuint)
static DECLARE_FORWARD(ALCportPlayback, ALCbackend, ALCuint, availableSamples)
static DECLARE_FORWARD(ALCportPlayback, ALCbackend, ClockLatency, getClockLatency)
static DECLARE_FORWARD(ALCportPlayback, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCportPlayback, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCportPlayback)
DEFINE_ALCBACKEND_VTABLE(ALCportPlayback);
static void ALCportPlayback_Construct(ALCportPlayback *self, ALCdevice *device)
{
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(ALCportPlayback, ALCbackend, self);
self->stream = NULL;
}
static void ALCportPlayback_Destruct(ALCportPlayback *self)
{
if(self->stream)
Pa_CloseStream(self->stream);
self->stream = NULL;
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
}
static int ALCportPlayback_WriteCallback(const void *UNUSED(inputBuffer), void *outputBuffer,
unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *UNUSED(timeInfo),
const PaStreamCallbackFlags UNUSED(statusFlags), void *userData)
{
ALCportPlayback *self = userData;
ALCportPlayback_lock(self);
aluMixData(STATIC_CAST(ALCbackend, self)->mDevice, outputBuffer, framesPerBuffer);
ALCportPlayback_unlock(self);
return 0;
}
static ALCenum ALCportPlayback_open(ALCportPlayback *self, const ALCchar *name)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
PaError err;
if(!name)
name = pa_device;
else if(strcmp(name, pa_device) != 0)
return ALC_INVALID_VALUE;
self->update_size = device->UpdateSize;
self->params.device = -1;
if(!ConfigValueInt(NULL, "port", "device", &self->params.device) ||
self->params.device < 0)
self->params.device = Pa_GetDefaultOutputDevice();
self->params.suggestedLatency = (device->UpdateSize*device->NumUpdates) /
(float)device->Frequency;
self->params.hostApiSpecificStreamInfo = NULL;
self->params.channelCount = ((device->FmtChans == DevFmtMono) ? 1 : 2);
switch(device->FmtType)
{
case DevFmtByte:
self->params.sampleFormat = paInt8;
break;
case DevFmtUByte:
self->params.sampleFormat = paUInt8;
break;
case DevFmtUShort:
/* fall-through */
case DevFmtShort:
self->params.sampleFormat = paInt16;
break;
case DevFmtUInt:
/* fall-through */
case DevFmtInt:
self->params.sampleFormat = paInt32;
break;
case DevFmtFloat:
self->params.sampleFormat = paFloat32;
break;
}
retry_open:
err = Pa_OpenStream(&self->stream, NULL, &self->params,
device->Frequency, device->UpdateSize, paNoFlag,
ALCportPlayback_WriteCallback, self
);
if(err != paNoError)
{
if(self->params.sampleFormat == paFloat32)
{
self->params.sampleFormat = paInt16;
goto retry_open;
}
ERR("Pa_OpenStream() returned an error: %s\n", Pa_GetErrorText(err));
return ALC_INVALID_VALUE;
}
alstr_copy_cstr(&device->DeviceName, name);
return ALC_NO_ERROR;
}
static void ALCportPlayback_close(ALCportPlayback *self)
{
PaError err = Pa_CloseStream(self->stream);
if(err != paNoError)
ERR("Error closing stream: %s\n", Pa_GetErrorText(err));
self->stream = NULL;
}
static ALCboolean ALCportPlayback_reset(ALCportPlayback *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
const PaStreamInfo *streamInfo;
streamInfo = Pa_GetStreamInfo(self->stream);
device->Frequency = streamInfo->sampleRate;
device->UpdateSize = self->update_size;
if(self->params.sampleFormat == paInt8)
device->FmtType = DevFmtByte;
else if(self->params.sampleFormat == paUInt8)
device->FmtType = DevFmtUByte;
else if(self->params.sampleFormat == paInt16)
device->FmtType = DevFmtShort;
else if(self->params.sampleFormat == paInt32)
device->FmtType = DevFmtInt;
else if(self->params.sampleFormat == paFloat32)
device->FmtType = DevFmtFloat;
else
{
ERR("Unexpected sample format: 0x%lx\n", self->params.sampleFormat);
return ALC_FALSE;
}
if(self->params.channelCount == 2)
device->FmtChans = DevFmtStereo;
else if(self->params.channelCount == 1)
device->FmtChans = DevFmtMono;
else
{
ERR("Unexpected channel count: %u\n", self->params.channelCount);
return ALC_FALSE;
}
SetDefaultChannelOrder(device);
return ALC_TRUE;
}
static ALCboolean ALCportPlayback_start(ALCportPlayback *self)
{
PaError err;
err = Pa_StartStream(self->stream);
if(err != paNoError)
{
ERR("Pa_StartStream() returned an error: %s\n", Pa_GetErrorText(err));
return ALC_FALSE;
}
return ALC_TRUE;
}
static void ALCportPlayback_stop(ALCportPlayback *self)
{
PaError err = Pa_StopStream(self->stream);
if(err != paNoError)
ERR("Error stopping stream: %s\n", Pa_GetErrorText(err));
}
typedef struct ALCportCapture {
DERIVE_FROM_TYPE(ALCbackend);
PaStream *stream;
PaStreamParameters params;
ll_ringbuffer_t *ring;
} ALCportCapture;
static int ALCportCapture_ReadCallback(const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo,
const PaStreamCallbackFlags statusFlags, void *userData);
static void ALCportCapture_Construct(ALCportCapture *self, ALCdevice *device);
static void ALCportCapture_Destruct(ALCportCapture *self);
static ALCenum ALCportCapture_open(ALCportCapture *self, const ALCchar *name);
static void ALCportCapture_close(ALCportCapture *self);
static DECLARE_FORWARD(ALCportCapture, ALCbackend, ALCboolean, reset)
static ALCboolean ALCportCapture_start(ALCportCapture *self);
static void ALCportCapture_stop(ALCportCapture *self);
static ALCenum ALCportCapture_captureSamples(ALCportCapture *self, ALCvoid *buffer, ALCuint samples);
static ALCuint ALCportCapture_availableSamples(ALCportCapture *self);
static DECLARE_FORWARD(ALCportCapture, ALCbackend, ClockLatency, getClockLatency)
static DECLARE_FORWARD(ALCportCapture, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCportCapture, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCportCapture)
DEFINE_ALCBACKEND_VTABLE(ALCportCapture);
static void ALCportCapture_Construct(ALCportCapture *self, ALCdevice *device)
{
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(ALCportCapture, ALCbackend, self);
self->stream = NULL;
}
static void ALCportCapture_Destruct(ALCportCapture *self)
{
if(self->stream)
Pa_CloseStream(self->stream);
self->stream = NULL;
if(self->ring)
ll_ringbuffer_free(self->ring);
self->ring = NULL;
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
}
static int ALCportCapture_ReadCallback(const void *inputBuffer, void *UNUSED(outputBuffer),
unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *UNUSED(timeInfo),
const PaStreamCallbackFlags UNUSED(statusFlags), void *userData)
{
ALCportCapture *self = userData;
size_t writable = ll_ringbuffer_write_space(self->ring);
if(framesPerBuffer > writable)
framesPerBuffer = writable;
ll_ringbuffer_write(self->ring, inputBuffer, framesPerBuffer);
return 0;
}
static ALCenum ALCportCapture_open(ALCportCapture *self, const ALCchar *name)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
ALuint samples, frame_size;
PaError err;
if(!name)
name = pa_device;
else if(strcmp(name, pa_device) != 0)
return ALC_INVALID_VALUE;
samples = device->UpdateSize * device->NumUpdates;
samples = maxu(samples, 100 * device->Frequency / 1000);
frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
self->ring = ll_ringbuffer_create(samples, frame_size);
if(self->ring == NULL) return ALC_INVALID_VALUE;
self->params.device = -1;
if(!ConfigValueInt(NULL, "port", "capture", &self->params.device) ||
self->params.device < 0)
self->params.device = Pa_GetDefaultInputDevice();
self->params.suggestedLatency = 0.0f;
self->params.hostApiSpecificStreamInfo = NULL;
switch(device->FmtType)
{
case DevFmtByte:
self->params.sampleFormat = paInt8;
break;
case DevFmtUByte:
self->params.sampleFormat = paUInt8;
break;
case DevFmtShort:
self->params.sampleFormat = paInt16;
break;
case DevFmtInt:
self->params.sampleFormat = paInt32;
break;
case DevFmtFloat:
self->params.sampleFormat = paFloat32;
break;
case DevFmtUInt:
case DevFmtUShort:
ERR("%s samples not supported\n", DevFmtTypeString(device->FmtType));
return ALC_INVALID_VALUE;
}
self->params.channelCount = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
err = Pa_OpenStream(&self->stream, &self->params, NULL,
device->Frequency, paFramesPerBufferUnspecified, paNoFlag,
ALCportCapture_ReadCallback, self
);
if(err != paNoError)
{
ERR("Pa_OpenStream() returned an error: %s\n", Pa_GetErrorText(err));
return ALC_INVALID_VALUE;
}
alstr_copy_cstr(&device->DeviceName, name);
return ALC_NO_ERROR;
}
static void ALCportCapture_close(ALCportCapture *self)
{
PaError err = Pa_CloseStream(self->stream);
if(err != paNoError)
ERR("Error closing stream: %s\n", Pa_GetErrorText(err));
self->stream = NULL;
ll_ringbuffer_free(self->ring);
self->ring = NULL;
}
static ALCboolean ALCportCapture_start(ALCportCapture *self)
{
PaError err = Pa_StartStream(self->stream);
if(err != paNoError)
{
ERR("Error starting stream: %s\n", Pa_GetErrorText(err));
return ALC_FALSE;
}
return ALC_TRUE;
}
static void ALCportCapture_stop(ALCportCapture *self)
{
PaError err = Pa_StopStream(self->stream);
if(err != paNoError)
ERR("Error stopping stream: %s\n", Pa_GetErrorText(err));
}
static ALCuint ALCportCapture_availableSamples(ALCportCapture *self)
{
return ll_ringbuffer_read_space(self->ring);
}
static ALCenum ALCportCapture_captureSamples(ALCportCapture *self, ALCvoid *buffer, ALCuint samples)
{
ll_ringbuffer_read(self->ring, buffer, samples);
return ALC_NO_ERROR;
}
typedef struct ALCportBackendFactory {
DERIVE_FROM_TYPE(ALCbackendFactory);
} ALCportBackendFactory;
#define ALCPORTBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCportBackendFactory, ALCbackendFactory) } }
static ALCboolean ALCportBackendFactory_init(ALCportBackendFactory *self);
static void ALCportBackendFactory_deinit(ALCportBackendFactory *self);
static ALCboolean ALCportBackendFactory_querySupport(ALCportBackendFactory *self, ALCbackend_Type type);
static void ALCportBackendFactory_probe(ALCportBackendFactory *self, enum DevProbe type);
static ALCbackend* ALCportBackendFactory_createBackend(ALCportBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCportBackendFactory);
static ALCboolean ALCportBackendFactory_init(ALCportBackendFactory* UNUSED(self))
{
if(!pa_load())
return ALC_FALSE;
return ALC_TRUE;
}
static void ALCportBackendFactory_deinit(ALCportBackendFactory* UNUSED(self))
{
#ifdef HAVE_DYNLOAD
if(pa_handle)
{
Pa_Terminate();
CloseLib(pa_handle);
pa_handle = NULL;
}
#else
Pa_Terminate();
#endif
}
static ALCboolean ALCportBackendFactory_querySupport(ALCportBackendFactory* UNUSED(self), ALCbackend_Type type)
{
if(type == ALCbackend_Playback || type == ALCbackend_Capture)
return ALC_TRUE;
return ALC_FALSE;
}
static void ALCportBackendFactory_probe(ALCportBackendFactory* UNUSED(self), enum DevProbe type)
{
switch(type)
{
case ALL_DEVICE_PROBE:
AppendAllDevicesList(pa_device);
break;
case CAPTURE_DEVICE_PROBE:
AppendCaptureDeviceList(pa_device);
break;
}
}
static ALCbackend* ALCportBackendFactory_createBackend(ALCportBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
{
if(type == ALCbackend_Playback)
{
ALCportPlayback *backend;
NEW_OBJ(backend, ALCportPlayback)(device);
if(!backend) return NULL;
return STATIC_CAST(ALCbackend, backend);
}
if(type == ALCbackend_Capture)
{
ALCportCapture *backend;
NEW_OBJ(backend, ALCportCapture)(device);
if(!backend) return NULL;
return STATIC_CAST(ALCbackend, backend);
}
return NULL;
}
ALCbackendFactory *ALCportBackendFactory_getFactory(void)
{
static ALCportBackendFactory factory = ALCPORTBACKENDFACTORY_INITIALIZER;
return STATIC_CAST(ALCbackendFactory, &factory);
}
File diff suppressed because it is too large Load Diff
+1069
View File
File diff suppressed because it is too large Load Diff
+346
View File
@@ -0,0 +1,346 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 1999-2007 by authors.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "alMain.h"
#include "alu.h"
#include "threads.h"
#include "backends/base.h"
#include <sndio.h>
typedef struct ALCsndioBackend {
DERIVE_FROM_TYPE(ALCbackend);
struct sio_hdl *sndHandle;
ALvoid *mix_data;
ALsizei data_size;
volatile int killNow;
althrd_t thread;
} ALCsndioBackend;
static int ALCsndioBackend_mixerProc(void *ptr);
static void ALCsndioBackend_Construct(ALCsndioBackend *self, ALCdevice *device);
static void ALCsndioBackend_Destruct(ALCsndioBackend *self);
static ALCenum ALCsndioBackend_open(ALCsndioBackend *self, const ALCchar *name);
static void ALCsndioBackend_close(ALCsndioBackend *self);
static ALCboolean ALCsndioBackend_reset(ALCsndioBackend *self);
static ALCboolean ALCsndioBackend_start(ALCsndioBackend *self);
static void ALCsndioBackend_stop(ALCsndioBackend *self);
static DECLARE_FORWARD2(ALCsndioBackend, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
static DECLARE_FORWARD(ALCsndioBackend, ALCbackend, ALCuint, availableSamples)
static DECLARE_FORWARD(ALCsndioBackend, ALCbackend, ClockLatency, getClockLatency)
static DECLARE_FORWARD(ALCsndioBackend, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCsndioBackend, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCsndioBackend)
DEFINE_ALCBACKEND_VTABLE(ALCsndioBackend);
static const ALCchar sndio_device[] = "SndIO Default";
static void ALCsndioBackend_Construct(ALCsndioBackend *self, ALCdevice *device)
{
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(ALCsndioBackend, ALCbackend, self);
}
static void ALCsndioBackend_Destruct(ALCsndioBackend *self)
{
if(self->sndHandle)
sio_close(self->sndHandle);
self->sndHandle = NULL;
al_free(self->mix_data);
self->mix_data = NULL;
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
}
static int 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, device->AmbiOrder);
while(!self->killNow && device->Connected)
{
ALsizei len = self->data_size;
ALubyte *WritePtr = self->mix_data;
ALCsndioBackend_lock(self);
aluMixData(device, WritePtr, len/frameSize);
ALCsndioBackend_unlock(self);
while(len > 0 && !self->killNow)
{
wrote = sio_write(self->sndHandle, WritePtr, len);
if(wrote == 0)
{
ERR("sio_write failed\n");
ALCdevice_Lock(device);
aluHandleDisconnect(device);
ALCdevice_Unlock(device);
break;
}
len -= wrote;
WritePtr += wrote;
}
}
return 0;
}
static ALCenum ALCsndioBackend_open(ALCsndioBackend *self, const ALCchar *name)
{
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
if(!name)
name = sndio_device;
else if(strcmp(name, sndio_device) != 0)
return ALC_INVALID_VALUE;
self->sndHandle = sio_open(NULL, SIO_PLAY, 0);
if(self->sndHandle == NULL)
{
ERR("Could not open device\n");
return ALC_INVALID_VALUE;
}
alstr_copy_cstr(&device->DeviceName, name);
return ALC_NO_ERROR;
}
static void ALCsndioBackend_close(ALCsndioBackend *self)
{
sio_close(self->sndHandle);
self->sndHandle = NULL;
}
static ALCboolean ALCsndioBackend_reset(ALCsndioBackend *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
struct sio_par par;
sio_initpar(&par);
par.rate = device->Frequency;
par.pchan = ((device->FmtChans != DevFmtMono) ? 2 : 1);
switch(device->FmtType)
{
case DevFmtByte:
par.bits = 8;
par.sig = 1;
break;
case DevFmtUByte:
par.bits = 8;
par.sig = 0;
break;
case DevFmtFloat:
case DevFmtShort:
par.bits = 16;
par.sig = 1;
break;
case DevFmtUShort:
par.bits = 16;
par.sig = 0;
break;
case DevFmtInt:
par.bits = 32;
par.sig = 1;
break;
case DevFmtUInt:
par.bits = 32;
par.sig = 0;
break;
}
par.le = SIO_LE_NATIVE;
par.round = device->UpdateSize;
par.appbufsz = device->UpdateSize * (device->NumUpdates-1);
if(!par.appbufsz) par.appbufsz = device->UpdateSize;
if(!sio_setpar(self->sndHandle, &par) || !sio_getpar(self->sndHandle, &par))
{
ERR("Failed to set device parameters\n");
return ALC_FALSE;
}
if(par.bits != par.bps*8)
{
ERR("Padded samples not supported (%u of %u bits)\n", par.bits, par.bps*8);
return ALC_FALSE;
}
device->Frequency = par.rate;
device->FmtChans = ((par.pchan==1) ? DevFmtMono : DevFmtStereo);
if(par.bits == 8 && par.sig == 1)
device->FmtType = DevFmtByte;
else if(par.bits == 8 && par.sig == 0)
device->FmtType = DevFmtUByte;
else if(par.bits == 16 && par.sig == 1)
device->FmtType = DevFmtShort;
else if(par.bits == 16 && par.sig == 0)
device->FmtType = DevFmtUShort;
else if(par.bits == 32 && par.sig == 1)
device->FmtType = DevFmtInt;
else if(par.bits == 32 && par.sig == 0)
device->FmtType = DevFmtUInt;
else
{
ERR("Unhandled sample format: %s %u-bit\n", (par.sig?"signed":"unsigned"), par.bits);
return ALC_FALSE;
}
device->UpdateSize = par.round;
device->NumUpdates = (par.bufsz/par.round) + 1;
SetDefaultChannelOrder(device);
return ALC_TRUE;
}
static ALCboolean ALCsndioBackend_start(ALCsndioBackend *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
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;
}
self->killNow = 0;
if(althrd_create(&self->thread, ALCsndioBackend_mixerProc, self) != althrd_success)
{
sio_stop(self->sndHandle);
return ALC_FALSE;
}
return ALC_TRUE;
}
static void ALCsndioBackend_stop(ALCsndioBackend *self)
{
int res;
if(self->killNow)
return;
self->killNow = 1;
althrd_join(self->thread, &res);
if(!sio_stop(self->sndHandle))
ERR("Error stopping device\n");
al_free(self->mix_data);
self->mix_data = NULL;
}
typedef struct ALCsndioBackendFactory {
DERIVE_FROM_TYPE(ALCbackendFactory);
} ALCsndioBackendFactory;
#define ALCSNDIOBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCsndioBackendFactory, ALCbackendFactory) } }
ALCbackendFactory *ALCsndioBackendFactory_getFactory(void);
static ALCboolean ALCsndioBackendFactory_init(ALCsndioBackendFactory *self);
static DECLARE_FORWARD(ALCsndioBackendFactory, ALCbackendFactory, void, deinit)
static ALCboolean ALCsndioBackendFactory_querySupport(ALCsndioBackendFactory *self, ALCbackend_Type type);
static void ALCsndioBackendFactory_probe(ALCsndioBackendFactory *self, enum DevProbe type);
static ALCbackend* ALCsndioBackendFactory_createBackend(ALCsndioBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCsndioBackendFactory);
ALCbackendFactory *ALCsndioBackendFactory_getFactory(void)
{
static ALCsndioBackendFactory factory = ALCSNDIOBACKENDFACTORY_INITIALIZER;
return STATIC_CAST(ALCbackendFactory, &factory);
}
static ALCboolean ALCsndioBackendFactory_init(ALCsndioBackendFactory* UNUSED(self))
{
/* No dynamic loading */
return ALC_TRUE;
}
static ALCboolean ALCsndioBackendFactory_querySupport(ALCsndioBackendFactory* UNUSED(self), ALCbackend_Type type)
{
if(type == ALCbackend_Playback)
return ALC_TRUE;
return ALC_FALSE;
}
static void ALCsndioBackendFactory_probe(ALCsndioBackendFactory* UNUSED(self), enum DevProbe type)
{
switch(type)
{
case ALL_DEVICE_PROBE:
AppendAllDevicesList(sndio_device);
break;
case CAPTURE_DEVICE_PROBE:
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;
}
+363
View File
@@ -0,0 +1,363 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 1999-2007 by authors.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <memory.h>
#include <unistd.h>
#include <errno.h>
#include <math.h>
#include "alMain.h"
#include "alu.h"
#include "threads.h"
#include "compat.h"
#include "backends/base.h"
#include <sys/audioio.h>
typedef struct ALCsolarisBackend {
DERIVE_FROM_TYPE(ALCbackend);
int fd;
ALubyte *mix_data;
int data_size;
ATOMIC(ALenum) killNow;
althrd_t thread;
} ALCsolarisBackend;
static int ALCsolarisBackend_mixerProc(void *ptr);
static void ALCsolarisBackend_Construct(ALCsolarisBackend *self, ALCdevice *device);
static void ALCsolarisBackend_Destruct(ALCsolarisBackend *self);
static ALCenum ALCsolarisBackend_open(ALCsolarisBackend *self, const ALCchar *name);
static void ALCsolarisBackend_close(ALCsolarisBackend *self);
static ALCboolean ALCsolarisBackend_reset(ALCsolarisBackend *self);
static ALCboolean ALCsolarisBackend_start(ALCsolarisBackend *self);
static void ALCsolarisBackend_stop(ALCsolarisBackend *self);
static DECLARE_FORWARD2(ALCsolarisBackend, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
static DECLARE_FORWARD(ALCsolarisBackend, ALCbackend, ALCuint, availableSamples)
static DECLARE_FORWARD(ALCsolarisBackend, ALCbackend, ClockLatency, getClockLatency)
static DECLARE_FORWARD(ALCsolarisBackend, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCsolarisBackend, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCsolarisBackend)
DEFINE_ALCBACKEND_VTABLE(ALCsolarisBackend);
static const ALCchar solaris_device[] = "Solaris Default";
static const char *solaris_driver = "/dev/audio";
static void ALCsolarisBackend_Construct(ALCsolarisBackend *self, ALCdevice *device)
{
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(ALCsolarisBackend, ALCbackend, self);
self->fd = -1;
ATOMIC_INIT(&self->killNow, AL_FALSE);
}
static void ALCsolarisBackend_Destruct(ALCsolarisBackend *self)
{
if(self->fd != -1)
close(self->fd);
self->fd = -1;
free(self->mix_data);
self->mix_data = NULL;
self->data_size = 0;
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
}
static int ALCsolarisBackend_mixerProc(void *ptr)
{
ALCsolarisBackend *self = ptr;
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
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);
frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
ALCsolarisBackend_lock(self);
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;
}
else if(sret == 0)
{
WARN("select timeout\n");
continue;
}
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;
}
static ALCenum ALCsolarisBackend_open(ALCsolarisBackend *self, const ALCchar *name)
{
ALCdevice *device;
if(!name)
name = solaris_device;
else if(strcmp(name, solaris_device) != 0)
return ALC_INVALID_VALUE;
self->fd = open(solaris_driver, O_WRONLY);
if(self->fd == -1)
{
ERR("Could not open %s: %s\n", solaris_driver, strerror(errno));
return ALC_INVALID_VALUE;
}
device = STATIC_CAST(ALCbackend,self)->mDevice;
alstr_copy_cstr(&device->DeviceName, name);
return ALC_NO_ERROR;
}
static void ALCsolarisBackend_close(ALCsolarisBackend *self)
{
close(self->fd);
self->fd = -1;
}
static ALCboolean ALCsolarisBackend_reset(ALCsolarisBackend *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
audio_info_t info;
ALsizei frameSize;
ALsizei numChannels;
AUDIO_INITINFO(&info);
info.play.sample_rate = device->Frequency;
if(device->FmtChans != DevFmtMono)
device->FmtChans = DevFmtStereo;
numChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
info.play.channels = numChannels;
switch(device->FmtType)
{
case DevFmtByte:
info.play.precision = 8;
info.play.encoding = AUDIO_ENCODING_LINEAR;
break;
case DevFmtUByte:
info.play.precision = 8;
info.play.encoding = AUDIO_ENCODING_LINEAR8;
break;
case DevFmtUShort:
case DevFmtInt:
case DevFmtUInt:
case DevFmtFloat:
device->FmtType = DevFmtShort;
/* fall-through */
case DevFmtShort:
info.play.precision = 16;
info.play.encoding = AUDIO_ENCODING_LINEAR;
break;
}
frameSize = numChannels * BytesFromDevFmt(device->FmtType);
info.play.buffer_size = device->UpdateSize*device->NumUpdates * frameSize;
if(ioctl(self->fd, AUDIO_SETINFO, &info) < 0)
{
ERR("ioctl failed: %s\n", strerror(errno));
return ALC_FALSE;
}
if(ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder) != (ALsizei)info.play.channels)
{
ERR("Failed to set %s, got %u channels instead\n", DevFmtChannelsString(device->FmtChans), info.play.channels);
return ALC_FALSE;
}
if(!((info.play.precision == 8 && info.play.encoding == AUDIO_ENCODING_LINEAR8 && device->FmtType == DevFmtUByte) ||
(info.play.precision == 8 && info.play.encoding == AUDIO_ENCODING_LINEAR && device->FmtType == DevFmtByte) ||
(info.play.precision == 16 && info.play.encoding == AUDIO_ENCODING_LINEAR && device->FmtType == DevFmtShort) ||
(info.play.precision == 32 && info.play.encoding == AUDIO_ENCODING_LINEAR && device->FmtType == DevFmtInt)))
{
ERR("Could not set %s samples, got %d (0x%x)\n", DevFmtTypeString(device->FmtType),
info.play.precision, info.play.encoding);
return ALC_FALSE;
}
device->Frequency = info.play.sample_rate;
device->UpdateSize = (info.play.buffer_size/device->NumUpdates) + 1;
SetDefaultChannelOrder(device);
free(self->mix_data);
self->data_size = device->UpdateSize * FrameSizeFromDevFmt(
device->FmtChans, device->FmtType, device->AmbiOrder
);
self->mix_data = calloc(1, self->data_size);
return ALC_TRUE;
}
static ALCboolean ALCsolarisBackend_start(ALCsolarisBackend *self)
{
ATOMIC_STORE_SEQ(&self->killNow, AL_FALSE);
if(althrd_create(&self->thread, ALCsolarisBackend_mixerProc, self) != althrd_success)
return ALC_FALSE;
return ALC_TRUE;
}
static void ALCsolarisBackend_stop(ALCsolarisBackend *self)
{
int res;
if(ATOMIC_EXCHANGE_SEQ(&self->killNow, AL_TRUE))
return;
althrd_join(self->thread, &res);
if(ioctl(self->fd, AUDIO_DRAIN) < 0)
ERR("Error draining device: %s\n", strerror(errno));
}
typedef struct ALCsolarisBackendFactory {
DERIVE_FROM_TYPE(ALCbackendFactory);
} ALCsolarisBackendFactory;
#define ALCSOLARISBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCsolarisBackendFactory, ALCbackendFactory) } }
ALCbackendFactory *ALCsolarisBackendFactory_getFactory(void);
static ALCboolean ALCsolarisBackendFactory_init(ALCsolarisBackendFactory *self);
static DECLARE_FORWARD(ALCsolarisBackendFactory, ALCbackendFactory, void, deinit)
static ALCboolean ALCsolarisBackendFactory_querySupport(ALCsolarisBackendFactory *self, ALCbackend_Type type);
static void ALCsolarisBackendFactory_probe(ALCsolarisBackendFactory *self, enum DevProbe type);
static ALCbackend* ALCsolarisBackendFactory_createBackend(ALCsolarisBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCsolarisBackendFactory);
ALCbackendFactory *ALCsolarisBackendFactory_getFactory(void)
{
static ALCsolarisBackendFactory factory = ALCSOLARISBACKENDFACTORY_INITIALIZER;
return STATIC_CAST(ALCbackendFactory, &factory);
}
static ALCboolean ALCsolarisBackendFactory_init(ALCsolarisBackendFactory* UNUSED(self))
{
ConfigValueStr(NULL, "solaris", "device", &solaris_driver);
return ALC_TRUE;
}
static ALCboolean ALCsolarisBackendFactory_querySupport(ALCsolarisBackendFactory* UNUSED(self), ALCbackend_Type type)
{
if(type == ALCbackend_Playback)
return ALC_TRUE;
return ALC_FALSE;
}
static void ALCsolarisBackendFactory_probe(ALCsolarisBackendFactory* UNUSED(self), enum DevProbe type)
{
switch(type)
{
case ALL_DEVICE_PROBE:
{
#ifdef HAVE_STAT
struct stat buf;
if(stat(solaris_driver, &buf) == 0)
#endif
AppendAllDevicesList(solaris_device);
}
break;
case CAPTURE_DEVICE_PROBE:
break;
}
}
ALCbackend* ALCsolarisBackendFactory_createBackend(ALCsolarisBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
{
if(type == ALCbackend_Playback)
{
ALCsolarisBackend *backend;
NEW_OBJ(backend, ALCsolarisBackend)(device);
if(!backend) return NULL;
return STATIC_CAST(ALCbackend, backend);
}
return NULL;
}
+453
View File
@@ -0,0 +1,453 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 1999-2007 by authors.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include <stdlib.h>
#include <stdio.h>
#include <memory.h>
#include <errno.h>
#include "alMain.h"
#include "alu.h"
#include "threads.h"
#include "compat.h"
#include "backends/base.h"
static const ALCchar waveDevice[] = "Wave File Writer";
static const ALubyte SUBTYPE_PCM[] = {
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa,
0x00, 0x38, 0x9b, 0x71
};
static const ALubyte SUBTYPE_FLOAT[] = {
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa,
0x00, 0x38, 0x9b, 0x71
};
static const ALubyte SUBTYPE_BFORMAT_PCM[] = {
0x01, 0x00, 0x00, 0x00, 0x21, 0x07, 0xd3, 0x11, 0x86, 0x44, 0xc8, 0xc1,
0xca, 0x00, 0x00, 0x00
};
static const ALubyte SUBTYPE_BFORMAT_FLOAT[] = {
0x03, 0x00, 0x00, 0x00, 0x21, 0x07, 0xd3, 0x11, 0x86, 0x44, 0xc8, 0xc1,
0xca, 0x00, 0x00, 0x00
};
static void fwrite16le(ALushort val, FILE *f)
{
ALubyte data[2] = { val&0xff, (val>>8)&0xff };
fwrite(data, 1, 2, f);
}
static void fwrite32le(ALuint val, FILE *f)
{
ALubyte data[4] = { val&0xff, (val>>8)&0xff, (val>>16)&0xff, (val>>24)&0xff };
fwrite(data, 1, 4, f);
}
typedef struct ALCwaveBackend {
DERIVE_FROM_TYPE(ALCbackend);
FILE *mFile;
long mDataStart;
ALvoid *mBuffer;
ALuint mSize;
volatile int killNow;
althrd_t thread;
} ALCwaveBackend;
static int ALCwaveBackend_mixerProc(void *ptr);
static void ALCwaveBackend_Construct(ALCwaveBackend *self, ALCdevice *device);
static DECLARE_FORWARD(ALCwaveBackend, ALCbackend, void, Destruct)
static ALCenum ALCwaveBackend_open(ALCwaveBackend *self, const ALCchar *name);
static void ALCwaveBackend_close(ALCwaveBackend *self);
static ALCboolean ALCwaveBackend_reset(ALCwaveBackend *self);
static ALCboolean ALCwaveBackend_start(ALCwaveBackend *self);
static void ALCwaveBackend_stop(ALCwaveBackend *self);
static DECLARE_FORWARD2(ALCwaveBackend, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
static DECLARE_FORWARD(ALCwaveBackend, ALCbackend, ALCuint, availableSamples)
static DECLARE_FORWARD(ALCwaveBackend, ALCbackend, ClockLatency, getClockLatency)
static DECLARE_FORWARD(ALCwaveBackend, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCwaveBackend, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCwaveBackend)
DEFINE_ALCBACKEND_VTABLE(ALCwaveBackend);
static void ALCwaveBackend_Construct(ALCwaveBackend *self, ALCdevice *device)
{
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(ALCwaveBackend, ALCbackend, self);
self->mFile = NULL;
self->mDataStart = -1;
self->mBuffer = NULL;
self->mSize = 0;
self->killNow = 1;
}
static int ALCwaveBackend_mixerProc(void *ptr)
{
ALCwaveBackend *self = (ALCwaveBackend*)ptr;
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
struct timespec now, start;
ALint64 avail, done;
ALuint frameSize;
size_t fs;
const long restTime = (long)((ALuint64)device->UpdateSize * 1000000000 /
device->Frequency / 2);
althrd_setname(althrd_current(), MIXER_THREAD_NAME);
frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
done = 0;
if(altimespec_get(&start, AL_TIME_UTC) != AL_TIME_UTC)
{
ERR("Failed to get starting time\n");
return 1;
}
while(!self->killNow && device->Connected)
{
if(altimespec_get(&now, AL_TIME_UTC) != AL_TIME_UTC)
{
ERR("Failed to get current time\n");
return 1;
}
avail = (now.tv_sec - start.tv_sec) * device->Frequency;
avail += (ALint64)(now.tv_nsec - start.tv_nsec) * device->Frequency / 1000000000;
if(avail < done)
{
/* Oops, time skipped backwards. Reset the number of samples done
* with one update available since we (likely) just came back from
* sleeping. */
done = avail - device->UpdateSize;
}
if(avail-done < device->UpdateSize)
al_nssleep(restTime);
else while(avail-done >= device->UpdateSize)
{
ALCwaveBackend_lock(self);
aluMixData(device, self->mBuffer, device->UpdateSize);
ALCwaveBackend_unlock(self);
done += device->UpdateSize;
if(!IS_LITTLE_ENDIAN)
{
ALuint bytesize = BytesFromDevFmt(device->FmtType);
ALuint i;
if(bytesize == 2)
{
ALushort *samples = self->mBuffer;
ALuint len = self->mSize / 2;
for(i = 0;i < len;i++)
{
ALushort samp = samples[i];
samples[i] = (samp>>8) | (samp<<8);
}
}
else if(bytesize == 4)
{
ALuint *samples = self->mBuffer;
ALuint len = self->mSize / 4;
for(i = 0;i < len;i++)
{
ALuint samp = samples[i];
samples[i] = (samp>>24) | ((samp>>8)&0x0000ff00) |
((samp<<8)&0x00ff0000) | (samp<<24);
}
}
}
fs = fwrite(self->mBuffer, frameSize, device->UpdateSize, self->mFile);
(void)fs;
if(ferror(self->mFile))
{
ERR("Error writing to file\n");
ALCdevice_Lock(device);
aluHandleDisconnect(device);
ALCdevice_Unlock(device);
break;
}
}
}
return 0;
}
static ALCenum ALCwaveBackend_open(ALCwaveBackend *self, const ALCchar *name)
{
ALCdevice *device;
const char *fname;
fname = GetConfigValue(NULL, "wave", "file", "");
if(!fname[0]) return ALC_INVALID_VALUE;
if(!name)
name = waveDevice;
else if(strcmp(name, waveDevice) != 0)
return ALC_INVALID_VALUE;
self->mFile = al_fopen(fname, "wb");
if(!self->mFile)
{
ERR("Could not open file '%s': %s\n", fname, strerror(errno));
return ALC_INVALID_VALUE;
}
device = STATIC_CAST(ALCbackend, self)->mDevice;
alstr_copy_cstr(&device->DeviceName, name);
return ALC_NO_ERROR;
}
static void ALCwaveBackend_close(ALCwaveBackend *self)
{
if(self->mFile)
fclose(self->mFile);
self->mFile = NULL;
}
static ALCboolean ALCwaveBackend_reset(ALCwaveBackend *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
ALuint channels=0, bits=0, chanmask=0;
int isbformat = 0;
size_t val;
fseek(self->mFile, 0, SEEK_SET);
clearerr(self->mFile);
if(GetConfigValueBool(NULL, "wave", "bformat", 0))
{
device->FmtChans = DevFmtAmbi3D;
device->AmbiOrder = 1;
}
switch(device->FmtType)
{
case DevFmtByte:
device->FmtType = DevFmtUByte;
break;
case DevFmtUShort:
device->FmtType = DevFmtShort;
break;
case DevFmtUInt:
device->FmtType = DevFmtInt;
break;
case DevFmtUByte:
case DevFmtShort:
case DevFmtInt:
case DevFmtFloat:
break;
}
switch(device->FmtChans)
{
case DevFmtMono: chanmask = 0x04; break;
case DevFmtStereo: chanmask = 0x01 | 0x02; break;
case DevFmtQuad: chanmask = 0x01 | 0x02 | 0x10 | 0x20; break;
case DevFmtX51: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x200 | 0x400; break;
case DevFmtX51Rear: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x010 | 0x020; break;
case DevFmtX61: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x100 | 0x200 | 0x400; break;
case DevFmtX71: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x010 | 0x020 | 0x200 | 0x400; break;
case 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, device->AmbiOrder);
fputs("RIFF", self->mFile);
fwrite32le(0xFFFFFFFF, self->mFile); // 'RIFF' header len; filled in at close
fputs("WAVE", self->mFile);
fputs("fmt ", self->mFile);
fwrite32le(40, self->mFile); // 'fmt ' header len; 40 bytes for EXTENSIBLE
// 16-bit val, format type id (extensible: 0xFFFE)
fwrite16le(0xFFFE, self->mFile);
// 16-bit val, channel count
fwrite16le(channels, self->mFile);
// 32-bit val, frequency
fwrite32le(device->Frequency, self->mFile);
// 32-bit val, bytes per second
fwrite32le(device->Frequency * channels * bits / 8, self->mFile);
// 16-bit val, frame size
fwrite16le(channels * bits / 8, self->mFile);
// 16-bit val, bits per sample
fwrite16le(bits, self->mFile);
// 16-bit val, extra byte count
fwrite16le(22, self->mFile);
// 16-bit val, valid bits per sample
fwrite16le(bits, self->mFile);
// 32-bit val, channel mask
fwrite32le(chanmask, self->mFile);
// 16 byte GUID, sub-type format
val = fwrite((device->FmtType == DevFmtFloat) ?
(isbformat ? SUBTYPE_BFORMAT_FLOAT : SUBTYPE_FLOAT) :
(isbformat ? SUBTYPE_BFORMAT_PCM : SUBTYPE_PCM), 1, 16, self->mFile);
(void)val;
fputs("data", self->mFile);
fwrite32le(0xFFFFFFFF, self->mFile); // 'data' header len; filled in at close
if(ferror(self->mFile))
{
ERR("Error writing header: %s\n", strerror(errno));
return ALC_FALSE;
}
self->mDataStart = ftell(self->mFile);
SetDefaultWFXChannelOrder(device);
return ALC_TRUE;
}
static ALCboolean ALCwaveBackend_start(ALCwaveBackend *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
self->mSize = device->UpdateSize * FrameSizeFromDevFmt(
device->FmtChans, device->FmtType, device->AmbiOrder
);
self->mBuffer = malloc(self->mSize);
if(!self->mBuffer)
{
ERR("Buffer malloc failed\n");
return ALC_FALSE;
}
self->killNow = 0;
if(althrd_create(&self->thread, ALCwaveBackend_mixerProc, self) != althrd_success)
{
free(self->mBuffer);
self->mBuffer = NULL;
self->mSize = 0;
return ALC_FALSE;
}
return ALC_TRUE;
}
static void ALCwaveBackend_stop(ALCwaveBackend *self)
{
ALuint dataLen;
long size;
int res;
if(self->killNow)
return;
self->killNow = 1;
althrd_join(self->thread, &res);
free(self->mBuffer);
self->mBuffer = NULL;
size = ftell(self->mFile);
if(size > 0)
{
dataLen = size - self->mDataStart;
if(fseek(self->mFile, self->mDataStart-4, SEEK_SET) == 0)
fwrite32le(dataLen, self->mFile); // 'data' header len
if(fseek(self->mFile, 4, SEEK_SET) == 0)
fwrite32le(size-8, self->mFile); // 'WAVE' header len
}
}
typedef struct ALCwaveBackendFactory {
DERIVE_FROM_TYPE(ALCbackendFactory);
} ALCwaveBackendFactory;
#define ALCWAVEBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCwaveBackendFactory, ALCbackendFactory) } }
ALCbackendFactory *ALCwaveBackendFactory_getFactory(void);
static ALCboolean ALCwaveBackendFactory_init(ALCwaveBackendFactory *self);
static DECLARE_FORWARD(ALCwaveBackendFactory, ALCbackendFactory, void, deinit)
static ALCboolean ALCwaveBackendFactory_querySupport(ALCwaveBackendFactory *self, ALCbackend_Type type);
static void ALCwaveBackendFactory_probe(ALCwaveBackendFactory *self, enum DevProbe type);
static ALCbackend* ALCwaveBackendFactory_createBackend(ALCwaveBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCwaveBackendFactory);
ALCbackendFactory *ALCwaveBackendFactory_getFactory(void)
{
static ALCwaveBackendFactory factory = ALCWAVEBACKENDFACTORY_INITIALIZER;
return STATIC_CAST(ALCbackendFactory, &factory);
}
static ALCboolean ALCwaveBackendFactory_init(ALCwaveBackendFactory* UNUSED(self))
{
return ALC_TRUE;
}
static ALCboolean ALCwaveBackendFactory_querySupport(ALCwaveBackendFactory* UNUSED(self), ALCbackend_Type type)
{
if(type == ALCbackend_Playback)
return !!ConfigValueExists(NULL, "wave", "file");
return ALC_FALSE;
}
static void ALCwaveBackendFactory_probe(ALCwaveBackendFactory* UNUSED(self), enum DevProbe type)
{
switch(type)
{
case ALL_DEVICE_PROBE:
AppendAllDevicesList(waveDevice);
break;
case CAPTURE_DEVICE_PROBE:
break;
}
}
static ALCbackend* ALCwaveBackendFactory_createBackend(ALCwaveBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
{
if(type == ALCbackend_Playback)
{
ALCwaveBackend *backend;
NEW_OBJ(backend, ALCwaveBackend)(device);
if(!backend) return NULL;
return STATIC_CAST(ALCbackend, backend);
}
return NULL;
}
+803
View File
@@ -0,0 +1,803 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 1999-2007 by authors.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include <stdlib.h>
#include <stdio.h>
#include <memory.h>
#include <windows.h>
#include <mmsystem.h>
#include "alMain.h"
#include "alu.h"
#include "threads.h"
#include "backends/base.h"
#ifndef WAVE_FORMAT_IEEE_FLOAT
#define WAVE_FORMAT_IEEE_FLOAT 0x0003
#endif
#define DEVNAME_HEAD "OpenAL Soft on "
static vector_al_string PlaybackDevices;
static vector_al_string CaptureDevices;
static void clear_devlist(vector_al_string *list)
{
VECTOR_FOR_EACH(al_string, *list, alstr_reset);
VECTOR_RESIZE(*list, 0, 0);
}
static void ProbePlaybackDevices(void)
{
ALuint numdevs;
ALuint i;
clear_devlist(&PlaybackDevices);
numdevs = waveOutGetNumDevs();
VECTOR_RESIZE(PlaybackDevices, 0, numdevs);
for(i = 0;i < numdevs;i++)
{
WAVEOUTCAPSW WaveCaps;
const al_string *iter;
al_string dname;
AL_STRING_INIT(dname);
if(waveOutGetDevCapsW(i, &WaveCaps, sizeof(WaveCaps)) == MMSYSERR_NOERROR)
{
ALuint count = 0;
while(1)
{
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);
alstr_append_cstr(&dname, str);
}
count++;
#define MATCH_ENTRY(i) (alstr_cmp(dname, *(i)) == 0)
VECTOR_FIND_IF(iter, const al_string, PlaybackDevices, MATCH_ENTRY);
if(iter == VECTOR_END(PlaybackDevices)) break;
#undef MATCH_ENTRY
}
TRACE("Got device \"%s\", ID %u\n", alstr_get_cstr(dname), i);
}
VECTOR_PUSH_BACK(PlaybackDevices, dname);
}
}
static void ProbeCaptureDevices(void)
{
ALuint numdevs;
ALuint i;
clear_devlist(&CaptureDevices);
numdevs = waveInGetNumDevs();
VECTOR_RESIZE(CaptureDevices, 0, numdevs);
for(i = 0;i < numdevs;i++)
{
WAVEINCAPSW WaveCaps;
const al_string *iter;
al_string dname;
AL_STRING_INIT(dname);
if(waveInGetDevCapsW(i, &WaveCaps, sizeof(WaveCaps)) == MMSYSERR_NOERROR)
{
ALuint count = 0;
while(1)
{
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);
alstr_append_cstr(&dname, str);
}
count++;
#define MATCH_ENTRY(i) (alstr_cmp(dname, *(i)) == 0)
VECTOR_FIND_IF(iter, const al_string, CaptureDevices, MATCH_ENTRY);
if(iter == VECTOR_END(CaptureDevices)) break;
#undef MATCH_ENTRY
}
TRACE("Got device \"%s\", ID %u\n", alstr_get_cstr(dname), i);
}
VECTOR_PUSH_BACK(CaptureDevices, dname);
}
}
typedef struct ALCwinmmPlayback {
DERIVE_FROM_TYPE(ALCbackend);
RefCount WaveBuffersCommitted;
WAVEHDR WaveBuffer[4];
HWAVEOUT OutHdl;
WAVEFORMATEX Format;
volatile ALboolean killNow;
althrd_t thread;
} ALCwinmmPlayback;
static void ALCwinmmPlayback_Construct(ALCwinmmPlayback *self, ALCdevice *device);
static void ALCwinmmPlayback_Destruct(ALCwinmmPlayback *self);
static void CALLBACK ALCwinmmPlayback_waveOutProc(HWAVEOUT device, UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR param2);
static int ALCwinmmPlayback_mixerProc(void *arg);
static ALCenum ALCwinmmPlayback_open(ALCwinmmPlayback *self, const ALCchar *name);
static void ALCwinmmPlayback_close(ALCwinmmPlayback *self);
static ALCboolean ALCwinmmPlayback_reset(ALCwinmmPlayback *self);
static ALCboolean ALCwinmmPlayback_start(ALCwinmmPlayback *self);
static void ALCwinmmPlayback_stop(ALCwinmmPlayback *self);
static DECLARE_FORWARD2(ALCwinmmPlayback, ALCbackend, ALCenum, captureSamples, ALCvoid*, ALCuint)
static DECLARE_FORWARD(ALCwinmmPlayback, ALCbackend, ALCuint, availableSamples)
static DECLARE_FORWARD(ALCwinmmPlayback, ALCbackend, ClockLatency, getClockLatency)
static DECLARE_FORWARD(ALCwinmmPlayback, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCwinmmPlayback, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCwinmmPlayback)
DEFINE_ALCBACKEND_VTABLE(ALCwinmmPlayback);
static void ALCwinmmPlayback_Construct(ALCwinmmPlayback *self, ALCdevice *device)
{
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(ALCwinmmPlayback, ALCbackend, self);
InitRef(&self->WaveBuffersCommitted, 0);
self->OutHdl = NULL;
self->killNow = AL_TRUE;
}
static void ALCwinmmPlayback_Destruct(ALCwinmmPlayback *self)
{
if(self->OutHdl)
waveOutClose(self->OutHdl);
self->OutHdl = 0;
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
}
/* ALCwinmmPlayback_waveOutProc
*
* Posts a message to 'ALCwinmmPlayback_mixerProc' everytime a WaveOut Buffer
* is completed and returns to the application (for more data)
*/
static void CALLBACK ALCwinmmPlayback_waveOutProc(HWAVEOUT UNUSED(device), UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR UNUSED(param2))
{
ALCwinmmPlayback *self = (ALCwinmmPlayback*)instance;
if(msg != WOM_DONE)
return;
DecrementRef(&self->WaveBuffersCommitted);
PostThreadMessage(self->thread, msg, 0, param1);
}
FORCE_ALIGN static int ALCwinmmPlayback_mixerProc(void *arg)
{
ALCwinmmPlayback *self = arg;
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
WAVEHDR *WaveHdr;
MSG msg;
SetRTPriority();
althrd_setname(althrd_current(), MIXER_THREAD_NAME);
while(GetMessage(&msg, NULL, 0, 0))
{
if(msg.message != WOM_DONE)
continue;
if(self->killNow)
{
if(ReadRef(&self->WaveBuffersCommitted) == 0)
break;
continue;
}
WaveHdr = ((WAVEHDR*)msg.lParam);
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));
IncrementRef(&self->WaveBuffersCommitted);
}
return 0;
}
static ALCenum ALCwinmmPlayback_open(ALCwinmmPlayback *self, const ALCchar *deviceName)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
const al_string *iter;
UINT DeviceID;
MMRESULT res;
if(VECTOR_SIZE(PlaybackDevices) == 0)
ProbePlaybackDevices();
// Find the Device ID matching the deviceName if valid
#define MATCH_DEVNAME(iter) (!alstr_empty(*(iter)) && \
(!deviceName || alstr_cmp_cstr(*(iter), deviceName) == 0))
VECTOR_FIND_IF(iter, const al_string, PlaybackDevices, MATCH_DEVNAME);
if(iter == VECTOR_END(PlaybackDevices))
return ALC_INVALID_VALUE;
#undef MATCH_DEVNAME
DeviceID = (UINT)(iter - VECTOR_BEGIN(PlaybackDevices));
retry_open:
memset(&self->Format, 0, sizeof(WAVEFORMATEX));
if(device->FmtType == DevFmtFloat)
{
self->Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
self->Format.wBitsPerSample = 32;
}
else
{
self->Format.wFormatTag = WAVE_FORMAT_PCM;
if(device->FmtType == DevFmtUByte || device->FmtType == DevFmtByte)
self->Format.wBitsPerSample = 8;
else
self->Format.wBitsPerSample = 16;
}
self->Format.nChannels = ((device->FmtChans == DevFmtMono) ? 1 : 2);
self->Format.nBlockAlign = self->Format.wBitsPerSample *
self->Format.nChannels / 8;
self->Format.nSamplesPerSec = device->Frequency;
self->Format.nAvgBytesPerSec = self->Format.nSamplesPerSec *
self->Format.nBlockAlign;
self->Format.cbSize = 0;
if((res=waveOutOpen(&self->OutHdl, DeviceID, &self->Format, (DWORD_PTR)&ALCwinmmPlayback_waveOutProc, (DWORD_PTR)self, CALLBACK_FUNCTION)) != MMSYSERR_NOERROR)
{
if(device->FmtType == DevFmtFloat)
{
device->FmtType = DevFmtShort;
goto retry_open;
}
ERR("waveOutOpen failed: %u\n", res);
goto failure;
}
alstr_copy(&device->DeviceName, VECTOR_ELEM(PlaybackDevices, DeviceID));
return ALC_NO_ERROR;
failure:
if(self->OutHdl)
waveOutClose(self->OutHdl);
self->OutHdl = NULL;
return ALC_INVALID_VALUE;
}
static void ALCwinmmPlayback_close(ALCwinmmPlayback* UNUSED(self))
{ }
static ALCboolean ALCwinmmPlayback_reset(ALCwinmmPlayback *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
device->UpdateSize = (ALuint)((ALuint64)device->UpdateSize *
self->Format.nSamplesPerSec /
device->Frequency);
device->UpdateSize = (device->UpdateSize*device->NumUpdates + 3) / 4;
device->NumUpdates = 4;
device->Frequency = self->Format.nSamplesPerSec;
if(self->Format.wFormatTag == WAVE_FORMAT_IEEE_FLOAT)
{
if(self->Format.wBitsPerSample == 32)
device->FmtType = DevFmtFloat;
else
{
ERR("Unhandled IEEE float sample depth: %d\n", self->Format.wBitsPerSample);
return ALC_FALSE;
}
}
else if(self->Format.wFormatTag == WAVE_FORMAT_PCM)
{
if(self->Format.wBitsPerSample == 16)
device->FmtType = DevFmtShort;
else if(self->Format.wBitsPerSample == 8)
device->FmtType = DevFmtUByte;
else
{
ERR("Unhandled PCM sample depth: %d\n", self->Format.wBitsPerSample);
return ALC_FALSE;
}
}
else
{
ERR("Unhandled format tag: 0x%04x\n", self->Format.wFormatTag);
return ALC_FALSE;
}
if(self->Format.nChannels == 2)
device->FmtChans = DevFmtStereo;
else if(self->Format.nChannels == 1)
device->FmtChans = DevFmtMono;
else
{
ERR("Unhandled channel count: %d\n", self->Format.nChannels);
return ALC_FALSE;
}
SetDefaultWFXChannelOrder(device);
return ALC_TRUE;
}
static ALCboolean ALCwinmmPlayback_start(ALCwinmmPlayback *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
ALbyte *BufferData;
ALint BufferSize;
ALuint i;
self->killNow = AL_FALSE;
if(althrd_create(&self->thread, ALCwinmmPlayback_mixerProc, self) != althrd_success)
return ALC_FALSE;
InitRef(&self->WaveBuffersCommitted, 0);
// Create 4 Buffers
BufferSize = device->UpdateSize*device->NumUpdates / 4;
BufferSize *= FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
BufferData = calloc(4, BufferSize);
for(i = 0;i < 4;i++)
{
memset(&self->WaveBuffer[i], 0, sizeof(WAVEHDR));
self->WaveBuffer[i].dwBufferLength = BufferSize;
self->WaveBuffer[i].lpData = ((i==0) ? (CHAR*)BufferData :
(self->WaveBuffer[i-1].lpData +
self->WaveBuffer[i-1].dwBufferLength));
waveOutPrepareHeader(self->OutHdl, &self->WaveBuffer[i], sizeof(WAVEHDR));
waveOutWrite(self->OutHdl, &self->WaveBuffer[i], sizeof(WAVEHDR));
IncrementRef(&self->WaveBuffersCommitted);
}
return ALC_TRUE;
}
static void ALCwinmmPlayback_stop(ALCwinmmPlayback *self)
{
void *buffer = NULL;
int i;
if(self->killNow)
return;
// Set flag to stop processing headers
self->killNow = AL_TRUE;
althrd_join(self->thread, &i);
// Release the wave buffers
for(i = 0;i < 4;i++)
{
waveOutUnprepareHeader(self->OutHdl, &self->WaveBuffer[i], sizeof(WAVEHDR));
if(i == 0) buffer = self->WaveBuffer[i].lpData;
self->WaveBuffer[i].lpData = NULL;
}
free(buffer);
}
typedef struct ALCwinmmCapture {
DERIVE_FROM_TYPE(ALCbackend);
RefCount WaveBuffersCommitted;
WAVEHDR WaveBuffer[4];
HWAVEIN InHdl;
ll_ringbuffer_t *Ring;
WAVEFORMATEX Format;
volatile ALboolean killNow;
althrd_t thread;
} ALCwinmmCapture;
static void ALCwinmmCapture_Construct(ALCwinmmCapture *self, ALCdevice *device);
static void ALCwinmmCapture_Destruct(ALCwinmmCapture *self);
static void CALLBACK ALCwinmmCapture_waveInProc(HWAVEIN device, UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR param2);
static int ALCwinmmCapture_captureProc(void *arg);
static ALCenum ALCwinmmCapture_open(ALCwinmmCapture *self, const ALCchar *name);
static void ALCwinmmCapture_close(ALCwinmmCapture *self);
static DECLARE_FORWARD(ALCwinmmCapture, ALCbackend, ALCboolean, reset)
static ALCboolean ALCwinmmCapture_start(ALCwinmmCapture *self);
static void ALCwinmmCapture_stop(ALCwinmmCapture *self);
static ALCenum ALCwinmmCapture_captureSamples(ALCwinmmCapture *self, ALCvoid *buffer, ALCuint samples);
static ALCuint ALCwinmmCapture_availableSamples(ALCwinmmCapture *self);
static DECLARE_FORWARD(ALCwinmmCapture, ALCbackend, ClockLatency, getClockLatency)
static DECLARE_FORWARD(ALCwinmmCapture, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCwinmmCapture, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCwinmmCapture)
DEFINE_ALCBACKEND_VTABLE(ALCwinmmCapture);
static void ALCwinmmCapture_Construct(ALCwinmmCapture *self, ALCdevice *device)
{
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(ALCwinmmCapture, ALCbackend, self);
InitRef(&self->WaveBuffersCommitted, 0);
self->InHdl = NULL;
self->killNow = AL_TRUE;
}
static void ALCwinmmCapture_Destruct(ALCwinmmCapture *self)
{
if(self->InHdl)
waveInClose(self->InHdl);
self->InHdl = 0;
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
}
/* ALCwinmmCapture_waveInProc
*
* Posts a message to 'ALCwinmmCapture_captureProc' everytime a WaveIn Buffer
* is completed and returns to the application (with more data).
*/
static void CALLBACK ALCwinmmCapture_waveInProc(HWAVEIN UNUSED(device), UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR UNUSED(param2))
{
ALCwinmmCapture *self = (ALCwinmmCapture*)instance;
if(msg != WIM_DATA)
return;
DecrementRef(&self->WaveBuffersCommitted);
PostThreadMessage(self->thread, msg, 0, param1);
}
static int ALCwinmmCapture_captureProc(void *arg)
{
ALCwinmmCapture *self = arg;
WAVEHDR *WaveHdr;
MSG msg;
althrd_setname(althrd_current(), RECORD_THREAD_NAME);
while(GetMessage(&msg, NULL, 0, 0))
{
if(msg.message != WIM_DATA)
continue;
/* Don't wait for other buffers to finish before quitting. We're
* closing so we don't need them. */
if(self->killNow)
break;
WaveHdr = ((WAVEHDR*)msg.lParam);
ll_ringbuffer_write(self->Ring, WaveHdr->lpData,
WaveHdr->dwBytesRecorded / self->Format.nBlockAlign
);
// Send buffer back to capture more data
waveInAddBuffer(self->InHdl, WaveHdr, sizeof(WAVEHDR));
IncrementRef(&self->WaveBuffersCommitted);
}
return 0;
}
static ALCenum ALCwinmmCapture_open(ALCwinmmCapture *self, const ALCchar *name)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
const al_string *iter;
ALbyte *BufferData = NULL;
DWORD CapturedDataSize;
ALint BufferSize;
UINT DeviceID;
MMRESULT res;
ALuint i;
if(VECTOR_SIZE(CaptureDevices) == 0)
ProbeCaptureDevices();
// Find the Device ID matching the deviceName if valid
#define MATCH_DEVNAME(iter) (!alstr_empty(*(iter)) && (!name || alstr_cmp_cstr(*iter, name) == 0))
VECTOR_FIND_IF(iter, const al_string, CaptureDevices, MATCH_DEVNAME);
if(iter == VECTOR_END(CaptureDevices))
return ALC_INVALID_VALUE;
#undef MATCH_DEVNAME
DeviceID = (UINT)(iter - VECTOR_BEGIN(CaptureDevices));
switch(device->FmtChans)
{
case DevFmtMono:
case DevFmtStereo:
break;
case DevFmtQuad:
case DevFmtX51:
case DevFmtX51Rear:
case DevFmtX61:
case DevFmtX71:
case DevFmtAmbi3D:
return ALC_INVALID_ENUM;
}
switch(device->FmtType)
{
case DevFmtUByte:
case DevFmtShort:
case DevFmtInt:
case DevFmtFloat:
break;
case DevFmtByte:
case DevFmtUShort:
case DevFmtUInt:
return ALC_INVALID_ENUM;
}
memset(&self->Format, 0, sizeof(WAVEFORMATEX));
self->Format.wFormatTag = ((device->FmtType == DevFmtFloat) ?
WAVE_FORMAT_IEEE_FLOAT : WAVE_FORMAT_PCM);
self->Format.nChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
self->Format.wBitsPerSample = BytesFromDevFmt(device->FmtType) * 8;
self->Format.nBlockAlign = self->Format.wBitsPerSample *
self->Format.nChannels / 8;
self->Format.nSamplesPerSec = device->Frequency;
self->Format.nAvgBytesPerSec = self->Format.nSamplesPerSec *
self->Format.nBlockAlign;
self->Format.cbSize = 0;
if((res=waveInOpen(&self->InHdl, DeviceID, &self->Format, (DWORD_PTR)&ALCwinmmCapture_waveInProc, (DWORD_PTR)self, CALLBACK_FUNCTION)) != MMSYSERR_NOERROR)
{
ERR("waveInOpen failed: %u\n", res);
goto failure;
}
// Allocate circular memory buffer for the captured audio
CapturedDataSize = device->UpdateSize*device->NumUpdates;
// Make sure circular buffer is at least 100ms in size
if(CapturedDataSize < (self->Format.nSamplesPerSec / 10))
CapturedDataSize = self->Format.nSamplesPerSec / 10;
self->Ring = ll_ringbuffer_create(CapturedDataSize+1, self->Format.nBlockAlign);
if(!self->Ring) goto failure;
InitRef(&self->WaveBuffersCommitted, 0);
// Create 4 Buffers of 50ms each
BufferSize = self->Format.nAvgBytesPerSec / 20;
BufferSize -= (BufferSize % self->Format.nBlockAlign);
BufferData = calloc(4, BufferSize);
if(!BufferData) goto failure;
for(i = 0;i < 4;i++)
{
memset(&self->WaveBuffer[i], 0, sizeof(WAVEHDR));
self->WaveBuffer[i].dwBufferLength = BufferSize;
self->WaveBuffer[i].lpData = ((i==0) ? (CHAR*)BufferData :
(self->WaveBuffer[i-1].lpData +
self->WaveBuffer[i-1].dwBufferLength));
self->WaveBuffer[i].dwFlags = 0;
self->WaveBuffer[i].dwLoops = 0;
waveInPrepareHeader(self->InHdl, &self->WaveBuffer[i], sizeof(WAVEHDR));
waveInAddBuffer(self->InHdl, &self->WaveBuffer[i], sizeof(WAVEHDR));
IncrementRef(&self->WaveBuffersCommitted);
}
self->killNow = AL_FALSE;
if(althrd_create(&self->thread, ALCwinmmCapture_captureProc, self) != althrd_success)
goto failure;
alstr_copy(&device->DeviceName, VECTOR_ELEM(CaptureDevices, DeviceID));
return ALC_NO_ERROR;
failure:
if(BufferData)
{
for(i = 0;i < 4;i++)
waveInUnprepareHeader(self->InHdl, &self->WaveBuffer[i], sizeof(WAVEHDR));
free(BufferData);
}
ll_ringbuffer_free(self->Ring);
self->Ring = NULL;
if(self->InHdl)
waveInClose(self->InHdl);
self->InHdl = NULL;
return ALC_INVALID_VALUE;
}
static void ALCwinmmCapture_close(ALCwinmmCapture *self)
{
void *buffer = NULL;
int i;
/* Tell the processing thread to quit and wait for it to do so. */
self->killNow = AL_TRUE;
PostThreadMessage(self->thread, WM_QUIT, 0, 0);
althrd_join(self->thread, &i);
/* Make sure capture is stopped and all pending buffers are flushed. */
waveInReset(self->InHdl);
// Release the wave buffers
for(i = 0;i < 4;i++)
{
waveInUnprepareHeader(self->InHdl, &self->WaveBuffer[i], sizeof(WAVEHDR));
if(i == 0) buffer = self->WaveBuffer[i].lpData;
self->WaveBuffer[i].lpData = NULL;
}
free(buffer);
ll_ringbuffer_free(self->Ring);
self->Ring = NULL;
// Close the Wave device
waveInClose(self->InHdl);
self->InHdl = NULL;
}
static ALCboolean ALCwinmmCapture_start(ALCwinmmCapture *self)
{
waveInStart(self->InHdl);
return ALC_TRUE;
}
static void ALCwinmmCapture_stop(ALCwinmmCapture *self)
{
waveInStop(self->InHdl);
}
static ALCenum ALCwinmmCapture_captureSamples(ALCwinmmCapture *self, ALCvoid *buffer, ALCuint samples)
{
ll_ringbuffer_read(self->Ring, buffer, samples);
return ALC_NO_ERROR;
}
static ALCuint ALCwinmmCapture_availableSamples(ALCwinmmCapture *self)
{
return ll_ringbuffer_read_space(self->Ring);
}
static inline void AppendAllDevicesList2(const al_string *name)
{
if(!alstr_empty(*name))
AppendAllDevicesList(alstr_get_cstr(*name));
}
static inline void AppendCaptureDeviceList2(const al_string *name)
{
if(!alstr_empty(*name))
AppendCaptureDeviceList(alstr_get_cstr(*name));
}
typedef struct ALCwinmmBackendFactory {
DERIVE_FROM_TYPE(ALCbackendFactory);
} ALCwinmmBackendFactory;
#define ALCWINMMBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCwinmmBackendFactory, ALCbackendFactory) } }
static ALCboolean ALCwinmmBackendFactory_init(ALCwinmmBackendFactory *self);
static void ALCwinmmBackendFactory_deinit(ALCwinmmBackendFactory *self);
static ALCboolean ALCwinmmBackendFactory_querySupport(ALCwinmmBackendFactory *self, ALCbackend_Type type);
static void ALCwinmmBackendFactory_probe(ALCwinmmBackendFactory *self, enum DevProbe type);
static ALCbackend* ALCwinmmBackendFactory_createBackend(ALCwinmmBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCwinmmBackendFactory);
static ALCboolean ALCwinmmBackendFactory_init(ALCwinmmBackendFactory* UNUSED(self))
{
VECTOR_INIT(PlaybackDevices);
VECTOR_INIT(CaptureDevices);
return ALC_TRUE;
}
static void ALCwinmmBackendFactory_deinit(ALCwinmmBackendFactory* UNUSED(self))
{
clear_devlist(&PlaybackDevices);
VECTOR_DEINIT(PlaybackDevices);
clear_devlist(&CaptureDevices);
VECTOR_DEINIT(CaptureDevices);
}
static ALCboolean ALCwinmmBackendFactory_querySupport(ALCwinmmBackendFactory* UNUSED(self), ALCbackend_Type type)
{
if(type == ALCbackend_Playback || type == ALCbackend_Capture)
return ALC_TRUE;
return ALC_FALSE;
}
static void ALCwinmmBackendFactory_probe(ALCwinmmBackendFactory* UNUSED(self), enum DevProbe type)
{
switch(type)
{
case ALL_DEVICE_PROBE:
ProbePlaybackDevices();
VECTOR_FOR_EACH(const al_string, PlaybackDevices, AppendAllDevicesList2);
break;
case CAPTURE_DEVICE_PROBE:
ProbeCaptureDevices();
VECTOR_FOR_EACH(const al_string, CaptureDevices, AppendCaptureDeviceList2);
break;
}
}
static ALCbackend* ALCwinmmBackendFactory_createBackend(ALCwinmmBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
{
if(type == ALCbackend_Playback)
{
ALCwinmmPlayback *backend;
NEW_OBJ(backend, ALCwinmmPlayback)(device);
if(!backend) return NULL;
return STATIC_CAST(ALCbackend, backend);
}
if(type == ALCbackend_Capture)
{
ALCwinmmCapture *backend;
NEW_OBJ(backend, ALCwinmmCapture)(device);
if(!backend) return NULL;
return STATIC_CAST(ALCbackend, backend);
}
return NULL;
}
ALCbackendFactory *ALCwinmmBackendFactory_getFactory(void)
{
static ALCwinmmBackendFactory factory = ALCWINMMBACKENDFACTORY_INITIALIZER;
return STATIC_CAST(ALCbackendFactory, &factory);
}
+612
View File
@@ -0,0 +1,612 @@
#include "config.h"
#include "bformatdec.h"
#include "ambdec.h"
#include "mixer_defs.h"
#include "alu.h"
#include "bool.h"
#include "threads.h"
#include "almalloc.h"
void bandsplit_init(BandSplitter *splitter, ALfloat freq_mult)
{
ALfloat w = freq_mult * F_TAU;
ALfloat cw = cosf(w);
if(cw > FLT_EPSILON)
splitter->coeff = (sinf(w) - 1.0f) / cw;
else
splitter->coeff = cw * -0.5f;
splitter->lp_z1 = 0.0f;
splitter->lp_z2 = 0.0f;
splitter->hp_z1 = 0.0f;
}
void bandsplit_clear(BandSplitter *splitter)
{
splitter->lp_z1 = 0.0f;
splitter->lp_z2 = 0.0f;
splitter->hp_z1 = 0.0f;
}
void bandsplit_process(BandSplitter *splitter, ALfloat *restrict hpout, ALfloat *restrict lpout,
const ALfloat *input, ALsizei count)
{
ALfloat coeff, d, x;
ALfloat z1, z2;
ALsizei i;
coeff = splitter->coeff*0.5f + 0.5f;
z1 = splitter->lp_z1;
z2 = splitter->lp_z2;
for(i = 0;i < count;i++)
{
x = input[i];
d = (x - z1) * coeff;
x = z1 + d;
z1 = x + d;
d = (x - z2) * coeff;
x = z2 + d;
z2 = x + d;
lpout[i] = x;
}
splitter->lp_z1 = z1;
splitter->lp_z2 = z2;
coeff = splitter->coeff;
z1 = splitter->hp_z1;
for(i = 0;i < count;i++)
{
x = input[i];
d = x - coeff*z1;
x = z1 + coeff*d;
z1 = d;
hpout[i] = x - lpout[i];
}
splitter->hp_z1 = z1;
}
void splitterap_init(SplitterAllpass *splitter, ALfloat freq_mult)
{
ALfloat w = freq_mult * F_TAU;
ALfloat cw = cosf(w);
if(cw > FLT_EPSILON)
splitter->coeff = (sinf(w) - 1.0f) / cw;
else
splitter->coeff = cw * -0.5f;
splitter->z1 = 0.0f;
}
void splitterap_clear(SplitterAllpass *splitter)
{
splitter->z1 = 0.0f;
}
void splitterap_process(SplitterAllpass *splitter, ALfloat *restrict samples, ALsizei count)
{
ALfloat coeff, d, x;
ALfloat z1;
ALsizei i;
coeff = splitter->coeff;
z1 = splitter->z1;
for(i = 0;i < count;i++)
{
x = samples[i];
d = x - coeff*z1;
x = z1 + coeff*d;
z1 = d;
samples[i] = x;
}
splitter->z1 = z1;
}
static const ALfloat UnitScale[MAX_AMBI_COEFFS] = {
1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f
};
static const ALfloat SN3D2N3DScale[MAX_AMBI_COEFFS] = {
1.000000000f, /* ACN 0 (W), sqrt(1) */
1.732050808f, /* ACN 1 (Y), sqrt(3) */
1.732050808f, /* ACN 2 (Z), sqrt(3) */
1.732050808f, /* ACN 3 (X), sqrt(3) */
2.236067978f, /* ACN 4 (V), sqrt(5) */
2.236067978f, /* ACN 5 (T), sqrt(5) */
2.236067978f, /* ACN 6 (R), sqrt(5) */
2.236067978f, /* ACN 7 (S), sqrt(5) */
2.236067978f, /* ACN 8 (U), sqrt(5) */
2.645751311f, /* ACN 9 (Q), sqrt(7) */
2.645751311f, /* ACN 10 (O), sqrt(7) */
2.645751311f, /* ACN 11 (M), sqrt(7) */
2.645751311f, /* ACN 12 (K), sqrt(7) */
2.645751311f, /* ACN 13 (L), sqrt(7) */
2.645751311f, /* ACN 14 (N), sqrt(7) */
2.645751311f, /* ACN 15 (P), sqrt(7) */
};
static const ALfloat FuMa2N3DScale[MAX_AMBI_COEFFS] = {
1.414213562f, /* ACN 0 (W), sqrt(2) */
1.732050808f, /* ACN 1 (Y), sqrt(3) */
1.732050808f, /* ACN 2 (Z), sqrt(3) */
1.732050808f, /* ACN 3 (X), sqrt(3) */
1.936491673f, /* ACN 4 (V), sqrt(15)/2 */
1.936491673f, /* ACN 5 (T), sqrt(15)/2 */
2.236067978f, /* ACN 6 (R), sqrt(5) */
1.936491673f, /* ACN 7 (S), sqrt(15)/2 */
1.936491673f, /* ACN 8 (U), sqrt(15)/2 */
2.091650066f, /* ACN 9 (Q), sqrt(35/8) */
1.972026594f, /* ACN 10 (O), sqrt(35)/3 */
2.231093404f, /* ACN 11 (M), sqrt(224/45) */
2.645751311f, /* ACN 12 (K), sqrt(7) */
2.231093404f, /* ACN 13 (L), sqrt(224/45) */
1.972026594f, /* ACN 14 (N), sqrt(35)/3 */
2.091650066f, /* ACN 15 (P), sqrt(35/8) */
};
enum FreqBand {
FB_HighFreq,
FB_LowFreq,
FB_Max
};
/* These points are in AL coordinates! */
static const ALfloat Ambi3DPoints[8][3] = {
{ -0.577350269f, 0.577350269f, -0.577350269f },
{ 0.577350269f, 0.577350269f, -0.577350269f },
{ -0.577350269f, 0.577350269f, 0.577350269f },
{ 0.577350269f, 0.577350269f, 0.577350269f },
{ -0.577350269f, -0.577350269f, -0.577350269f },
{ 0.577350269f, -0.577350269f, -0.577350269f },
{ -0.577350269f, -0.577350269f, 0.577350269f },
{ 0.577350269f, -0.577350269f, 0.577350269f },
};
static const ALfloat Ambi3DDecoder[8][FB_Max][MAX_AMBI_COEFFS] = {
{ { 0.25f, 0.1443375672f, 0.1443375672f, 0.1443375672f }, { 0.125f, 0.125f, 0.125f, 0.125f } },
{ { 0.25f, -0.1443375672f, 0.1443375672f, 0.1443375672f }, { 0.125f, -0.125f, 0.125f, 0.125f } },
{ { 0.25f, 0.1443375672f, 0.1443375672f, -0.1443375672f }, { 0.125f, 0.125f, 0.125f, -0.125f } },
{ { 0.25f, -0.1443375672f, 0.1443375672f, -0.1443375672f }, { 0.125f, -0.125f, 0.125f, -0.125f } },
{ { 0.25f, 0.1443375672f, -0.1443375672f, 0.1443375672f }, { 0.125f, 0.125f, -0.125f, 0.125f } },
{ { 0.25f, -0.1443375672f, -0.1443375672f, 0.1443375672f }, { 0.125f, -0.125f, -0.125f, 0.125f } },
{ { 0.25f, 0.1443375672f, -0.1443375672f, -0.1443375672f }, { 0.125f, 0.125f, -0.125f, -0.125f } },
{ { 0.25f, -0.1443375672f, -0.1443375672f, -0.1443375672f }, { 0.125f, -0.125f, -0.125f, -0.125f } },
};
static RowMixerFunc MixMatrixRow = MixRow_C;
static alonce_flag bformatdec_inited = AL_ONCE_FLAG_INIT;
static void init_bformatdec(void)
{
MixMatrixRow = SelectRowMixer();
}
/* NOTE: BandSplitter filters are unused with single-band decoding */
typedef struct BFormatDec {
ALboolean Enabled[MAX_OUTPUT_CHANNELS];
union {
alignas(16) ALfloat Dual[MAX_OUTPUT_CHANNELS][FB_Max][MAX_AMBI_COEFFS];
alignas(16) ALfloat Single[MAX_OUTPUT_CHANNELS][MAX_AMBI_COEFFS];
} Matrix;
BandSplitter XOver[MAX_AMBI_COEFFS];
ALfloat (*Samples)[BUFFERSIZE];
/* These two alias into Samples */
ALfloat (*SamplesHF)[BUFFERSIZE];
ALfloat (*SamplesLF)[BUFFERSIZE];
alignas(16) ALfloat ChannelMix[BUFFERSIZE];
struct {
BandSplitter XOver;
ALfloat Gains[FB_Max];
} UpSampler[4];
ALsizei NumChannels;
ALboolean DualBand;
} BFormatDec;
BFormatDec *bformatdec_alloc()
{
alcall_once(&bformatdec_inited, init_bformatdec);
return al_calloc(16, sizeof(BFormatDec));
}
void bformatdec_free(BFormatDec *dec)
{
if(dec)
{
al_free(dec->Samples);
dec->Samples = NULL;
dec->SamplesHF = NULL;
dec->SamplesLF = NULL;
memset(dec, 0, sizeof(*dec));
al_free(dec);
}
}
void bformatdec_reset(BFormatDec *dec, const AmbDecConf *conf, ALsizei chancount, ALuint srate, const ALsizei chanmap[MAX_OUTPUT_CHANNELS])
{
static const ALsizei map2DTo3D[MAX_AMBI2D_COEFFS] = {
0, 1, 3, 4, 8, 9, 15
};
const ALfloat *coeff_scale = UnitScale;
bool periphonic;
ALfloat ratio;
ALsizei i;
al_free(dec->Samples);
dec->Samples = NULL;
dec->SamplesHF = NULL;
dec->SamplesLF = NULL;
dec->NumChannels = chancount;
dec->Samples = al_calloc(16, dec->NumChannels*2 * sizeof(dec->Samples[0]));
dec->SamplesHF = dec->Samples;
dec->SamplesLF = dec->SamplesHF + dec->NumChannels;
for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
dec->Enabled[i] = AL_FALSE;
for(i = 0;i < conf->NumSpeakers;i++)
dec->Enabled[chanmap[i]] = AL_TRUE;
if(conf->CoeffScale == ADS_SN3D)
coeff_scale = SN3D2N3DScale;
else if(conf->CoeffScale == ADS_FuMa)
coeff_scale = FuMa2N3DScale;
memset(dec->UpSampler, 0, sizeof(dec->UpSampler));
ratio = 400.0f / (ALfloat)srate;
for(i = 0;i < 4;i++)
bandsplit_init(&dec->UpSampler[i].XOver, ratio);
if((conf->ChanMask&AMBI_PERIPHONIC_MASK))
{
periphonic = true;
dec->UpSampler[0].Gains[FB_HighFreq] = (dec->NumChannels > 9) ? W_SCALE3D_THIRD :
(dec->NumChannels > 4) ? W_SCALE3D_SECOND : 1.0f;
dec->UpSampler[0].Gains[FB_LowFreq] = 1.0f;
for(i = 1;i < 4;i++)
{
dec->UpSampler[i].Gains[FB_HighFreq] = (dec->NumChannels > 9) ? XYZ_SCALE3D_THIRD :
(dec->NumChannels > 4) ? XYZ_SCALE3D_SECOND : 1.0f;
dec->UpSampler[i].Gains[FB_LowFreq] = 1.0f;
}
}
else
{
periphonic = false;
dec->UpSampler[0].Gains[FB_HighFreq] = (dec->NumChannels > 5) ? W_SCALE2D_THIRD :
(dec->NumChannels > 3) ? W_SCALE2D_SECOND : 1.0f;
dec->UpSampler[0].Gains[FB_LowFreq] = 1.0f;
for(i = 1;i < 3;i++)
{
dec->UpSampler[i].Gains[FB_HighFreq] = (dec->NumChannels > 5) ? XYZ_SCALE2D_THIRD :
(dec->NumChannels > 3) ? XYZ_SCALE2D_SECOND : 1.0f;
dec->UpSampler[i].Gains[FB_LowFreq] = 1.0f;
}
dec->UpSampler[3].Gains[FB_HighFreq] = 0.0f;
dec->UpSampler[3].Gains[FB_LowFreq] = 0.0f;
}
memset(&dec->Matrix, 0, sizeof(dec->Matrix));
if(conf->FreqBands == 1)
{
dec->DualBand = AL_FALSE;
for(i = 0;i < conf->NumSpeakers;i++)
{
ALsizei chan = chanmap[i];
ALfloat gain;
ALsizei j, k;
if(!periphonic)
{
for(j = 0,k = 0;j < MAX_AMBI2D_COEFFS;j++)
{
ALsizei l = map2DTo3D[j];
if(j == 0) gain = conf->HFOrderGain[0];
else if(j == 1) gain = conf->HFOrderGain[1];
else if(j == 3) gain = conf->HFOrderGain[2];
else if(j == 5) gain = conf->HFOrderGain[3];
if((conf->ChanMask&(1<<l)))
dec->Matrix.Single[chan][j] = conf->HFMatrix[i][k++] / coeff_scale[l] *
gain;
}
}
else
{
for(j = 0,k = 0;j < MAX_AMBI_COEFFS;j++)
{
if(j == 0) gain = conf->HFOrderGain[0];
else if(j == 1) gain = conf->HFOrderGain[1];
else if(j == 4) gain = conf->HFOrderGain[2];
else if(j == 9) gain = conf->HFOrderGain[3];
if((conf->ChanMask&(1<<j)))
dec->Matrix.Single[chan][j] = conf->HFMatrix[i][k++] / coeff_scale[j] *
gain;
}
}
}
}
else
{
dec->DualBand = AL_TRUE;
ratio = conf->XOverFreq / (ALfloat)srate;
for(i = 0;i < MAX_AMBI_COEFFS;i++)
bandsplit_init(&dec->XOver[i], ratio);
ratio = powf(10.0f, conf->XOverRatio / 40.0f);
for(i = 0;i < conf->NumSpeakers;i++)
{
ALsizei chan = chanmap[i];
ALfloat gain;
ALsizei j, k;
if(!periphonic)
{
for(j = 0,k = 0;j < MAX_AMBI2D_COEFFS;j++)
{
ALsizei l = map2DTo3D[j];
if(j == 0) gain = conf->HFOrderGain[0] * ratio;
else if(j == 1) gain = conf->HFOrderGain[1] * ratio;
else if(j == 3) gain = conf->HFOrderGain[2] * ratio;
else if(j == 5) gain = conf->HFOrderGain[3] * ratio;
if((conf->ChanMask&(1<<l)))
dec->Matrix.Dual[chan][FB_HighFreq][j] = conf->HFMatrix[i][k++] /
coeff_scale[l] * gain;
}
for(j = 0,k = 0;j < MAX_AMBI2D_COEFFS;j++)
{
ALsizei l = map2DTo3D[j];
if(j == 0) gain = conf->LFOrderGain[0] / ratio;
else if(j == 1) gain = conf->LFOrderGain[1] / ratio;
else if(j == 3) gain = conf->LFOrderGain[2] / ratio;
else if(j == 5) gain = conf->LFOrderGain[3] / ratio;
if((conf->ChanMask&(1<<l)))
dec->Matrix.Dual[chan][FB_LowFreq][j] = conf->LFMatrix[i][k++] /
coeff_scale[l] * gain;
}
}
else
{
for(j = 0,k = 0;j < MAX_AMBI_COEFFS;j++)
{
if(j == 0) gain = conf->HFOrderGain[0] * ratio;
else if(j == 1) gain = conf->HFOrderGain[1] * ratio;
else if(j == 4) gain = conf->HFOrderGain[2] * ratio;
else if(j == 9) gain = conf->HFOrderGain[3] * ratio;
if((conf->ChanMask&(1<<j)))
dec->Matrix.Dual[chan][FB_HighFreq][j] = conf->HFMatrix[i][k++] /
coeff_scale[j] * gain;
}
for(j = 0,k = 0;j < MAX_AMBI_COEFFS;j++)
{
if(j == 0) gain = conf->LFOrderGain[0] / ratio;
else if(j == 1) gain = conf->LFOrderGain[1] / ratio;
else if(j == 4) gain = conf->LFOrderGain[2] / ratio;
else if(j == 9) gain = conf->LFOrderGain[3] / ratio;
if((conf->ChanMask&(1<<j)))
dec->Matrix.Dual[chan][FB_LowFreq][j] = conf->LFMatrix[i][k++] /
coeff_scale[j] * gain;
}
}
}
}
}
void bformatdec_process(struct BFormatDec *dec, ALfloat (*restrict OutBuffer)[BUFFERSIZE], ALsizei OutChannels, const ALfloat (*restrict InSamples)[BUFFERSIZE], ALsizei SamplesToDo)
{
ALsizei chan, i;
OutBuffer = ASSUME_ALIGNED(OutBuffer, 16);
if(dec->DualBand)
{
for(i = 0;i < dec->NumChannels;i++)
bandsplit_process(&dec->XOver[i], dec->SamplesHF[i], dec->SamplesLF[i],
InSamples[i], SamplesToDo);
for(chan = 0;chan < OutChannels;chan++)
{
if(!dec->Enabled[chan])
continue;
memset(dec->ChannelMix, 0, SamplesToDo*sizeof(ALfloat));
MixMatrixRow(dec->ChannelMix, dec->Matrix.Dual[chan][FB_HighFreq],
SAFE_CONST(ALfloatBUFFERSIZE*,dec->SamplesHF), dec->NumChannels, 0,
SamplesToDo
);
MixMatrixRow(dec->ChannelMix, dec->Matrix.Dual[chan][FB_LowFreq],
SAFE_CONST(ALfloatBUFFERSIZE*,dec->SamplesLF), dec->NumChannels, 0,
SamplesToDo
);
for(i = 0;i < SamplesToDo;i++)
OutBuffer[chan][i] += dec->ChannelMix[i];
}
}
else
{
for(chan = 0;chan < OutChannels;chan++)
{
if(!dec->Enabled[chan])
continue;
memset(dec->ChannelMix, 0, SamplesToDo*sizeof(ALfloat));
MixMatrixRow(dec->ChannelMix, dec->Matrix.Single[chan], InSamples,
dec->NumChannels, 0, SamplesToDo);
for(i = 0;i < SamplesToDo;i++)
OutBuffer[chan][i] += dec->ChannelMix[i];
}
}
}
void bformatdec_upSample(struct BFormatDec *dec, ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat (*restrict InSamples)[BUFFERSIZE], ALsizei InChannels, ALsizei SamplesToDo)
{
ALsizei i;
/* This up-sampler leverages the differences observed in dual-band second-
* and third-order decoder matrices compared to first-order. For the same
* output channel configuration, the low-frequency matrix has identical
* coefficients in the shared input channels, while the high-frequency
* matrix has extra scalars applied to the W channel and X/Y/Z channels.
* Mixing the first-order content into the higher-order stream with the
* appropriate counter-scales applied to the HF response results in the
* subsequent higher-order decode generating the same response as a first-
* order decode.
*/
for(i = 0;i < InChannels;i++)
{
/* First, split the first-order components into low and high frequency
* bands.
*/
bandsplit_process(&dec->UpSampler[i].XOver,
dec->Samples[FB_HighFreq], dec->Samples[FB_LowFreq],
InSamples[i], SamplesToDo
);
/* Now write each band to the output. */
MixMatrixRow(OutBuffer[i], dec->UpSampler[i].Gains,
SAFE_CONST(ALfloatBUFFERSIZE*,dec->Samples), FB_Max, 0,
SamplesToDo
);
}
}
#define INVALID_UPSAMPLE_INDEX INT_MAX
static ALsizei GetACNIndex(const BFChannelConfig *chans, ALsizei numchans, ALsizei acn)
{
ALsizei i;
for(i = 0;i < numchans;i++)
{
if(chans[i].Index == acn)
return i;
}
return INVALID_UPSAMPLE_INDEX;
}
#define GetChannelForACN(b, a) GetACNIndex((b).Ambi.Map, (b).NumChannels, (a))
typedef struct AmbiUpsampler {
alignas(16) ALfloat Samples[FB_Max][BUFFERSIZE];
BandSplitter XOver[4];
ALfloat Gains[4][MAX_OUTPUT_CHANNELS][FB_Max];
} AmbiUpsampler;
AmbiUpsampler *ambiup_alloc()
{
alcall_once(&bformatdec_inited, init_bformatdec);
return al_calloc(16, sizeof(AmbiUpsampler));
}
void ambiup_free(struct AmbiUpsampler *ambiup)
{
al_free(ambiup);
}
void ambiup_reset(struct AmbiUpsampler *ambiup, const ALCdevice *device)
{
ALfloat ratio;
size_t i;
ratio = 400.0f / (ALfloat)device->Frequency;
for(i = 0;i < 4;i++)
bandsplit_init(&ambiup->XOver[i], ratio);
memset(ambiup->Gains, 0, sizeof(ambiup->Gains));
if(device->Dry.CoeffCount > 0)
{
ALfloat encgains[8][MAX_OUTPUT_CHANNELS];
ALsizei j;
size_t k;
for(i = 0;i < COUNTOF(Ambi3DPoints);i++)
{
ALfloat coeffs[MAX_AMBI_COEFFS] = { 0.0f };
CalcDirectionCoeffs(Ambi3DPoints[i], 0.0f, coeffs);
ComputePanningGains(device->Dry, coeffs, 1.0f, encgains[i]);
}
/* Combine the matrices that do the in->virt and virt->out conversions
* so we get a single in->out conversion. NOTE: the Encoder matrix
* (encgains) and output are transposed, so the input channels line up
* with the rows and the output channels line up with the columns.
*/
for(i = 0;i < 4;i++)
{
for(j = 0;j < device->Dry.NumChannels;j++)
{
ALfloat hfgain=0.0f, lfgain=0.0f;
for(k = 0;k < COUNTOF(Ambi3DDecoder);k++)
{
hfgain += Ambi3DDecoder[k][FB_HighFreq][i]*encgains[k][j];
lfgain += Ambi3DDecoder[k][FB_LowFreq][i]*encgains[k][j];
}
ambiup->Gains[i][j][FB_HighFreq] = hfgain;
ambiup->Gains[i][j][FB_LowFreq] = lfgain;
}
}
}
else
{
/* Assumes full 3D/periphonic on the input and output mixes! */
ALfloat w_scale = (device->Dry.NumChannels > 9) ? W_SCALE3D_THIRD :
(device->Dry.NumChannels > 4) ? W_SCALE3D_SECOND : 1.0f;
ALfloat xyz_scale = (device->Dry.NumChannels > 9) ? XYZ_SCALE3D_THIRD :
(device->Dry.NumChannels > 4) ? XYZ_SCALE3D_SECOND : 1.0f;
for(i = 0;i < 4;i++)
{
ALsizei index = GetChannelForACN(device->Dry, i);
if(index != INVALID_UPSAMPLE_INDEX)
{
ALfloat scale = device->Dry.Ambi.Map[index].Scale;
ambiup->Gains[i][index][FB_HighFreq] = scale * ((i==0) ? w_scale : xyz_scale);
ambiup->Gains[i][index][FB_LowFreq] = scale;
}
}
}
}
void ambiup_process(struct AmbiUpsampler *ambiup, ALfloat (*restrict OutBuffer)[BUFFERSIZE], ALsizei OutChannels, const ALfloat (*restrict InSamples)[BUFFERSIZE], ALsizei SamplesToDo)
{
ALsizei i, j;
for(i = 0;i < 4;i++)
{
bandsplit_process(&ambiup->XOver[i],
ambiup->Samples[FB_HighFreq], ambiup->Samples[FB_LowFreq],
InSamples[i], SamplesToDo
);
for(j = 0;j < OutChannels;j++)
MixMatrixRow(OutBuffer[j], ambiup->Gains[i][j],
SAFE_CONST(ALfloatBUFFERSIZE*,ambiup->Samples), FB_Max, 0,
SamplesToDo
);
}
}
+75
View File
@@ -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 */
+187
View File
@@ -0,0 +1,187 @@
/*-
* Copyright (c) 2005 Boris Mikhaylov
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "config.h"
#include <math.h>
#include <string.h>
#include "bs2b.h"
#include "alu.h"
/* Set up all data. */
static void init(struct bs2b *bs2b)
{
float Fc_lo, Fc_hi;
float G_lo, G_hi;
float x, g;
switch(bs2b->level)
{
case BS2B_LOW_CLEVEL: /* Low crossfeed level */
Fc_lo = 360.0f;
Fc_hi = 501.0f;
G_lo = 0.398107170553497f;
G_hi = 0.205671765275719f;
break;
case BS2B_MIDDLE_CLEVEL: /* Middle crossfeed level */
Fc_lo = 500.0f;
Fc_hi = 711.0f;
G_lo = 0.459726988530872f;
G_hi = 0.228208484414988f;
break;
case BS2B_HIGH_CLEVEL: /* High crossfeed level (virtual speakers are closer to itself) */
Fc_lo = 700.0f;
Fc_hi = 1021.0f;
G_lo = 0.530884444230988f;
G_hi = 0.250105790667544f;
break;
case BS2B_LOW_ECLEVEL: /* Low easy crossfeed level */
Fc_lo = 360.0f;
Fc_hi = 494.0f;
G_lo = 0.316227766016838f;
G_hi = 0.168236228897329f;
break;
case BS2B_MIDDLE_ECLEVEL: /* Middle easy crossfeed level */
Fc_lo = 500.0f;
Fc_hi = 689.0f;
G_lo = 0.354813389233575f;
G_hi = 0.187169483835901f;
break;
default: /* High easy crossfeed level */
bs2b->level = BS2B_HIGH_ECLEVEL;
Fc_lo = 700.0f;
Fc_hi = 975.0f;
G_lo = 0.398107170553497f;
G_hi = 0.205671765275719f;
break;
} /* switch */
g = 1.0f / (1.0f - G_hi + G_lo);
/* $fc = $Fc / $s;
* $d = 1 / 2 / pi / $fc;
* $x = exp(-1 / $d);
*/
x = expf(-2.0f * F_PI * Fc_lo / bs2b->srate);
bs2b->b1_lo = x;
bs2b->a0_lo = G_lo * (1.0f - x) * g;
x = expf(-2.0f * F_PI * Fc_hi / bs2b->srate);
bs2b->b1_hi = x;
bs2b->a0_hi = (1.0f - G_hi * (1.0f - x)) * g;
bs2b->a1_hi = -x * g;
} /* init */
/* Exported functions.
* See descriptions in "bs2b.h"
*/
void bs2b_set_params(struct bs2b *bs2b, int level, int srate)
{
if(srate <= 0) srate = 1;
bs2b->level = level;
bs2b->srate = srate;
init(bs2b);
} /* bs2b_set_params */
int bs2b_get_level(struct bs2b *bs2b)
{
return bs2b->level;
} /* bs2b_get_level */
int bs2b_get_srate(struct bs2b *bs2b)
{
return bs2b->srate;
} /* bs2b_get_srate */
void bs2b_clear(struct bs2b *bs2b)
{
memset(&bs2b->last_sample, 0, sizeof(bs2b->last_sample));
} /* bs2b_clear */
void bs2b_cross_feed(struct bs2b *bs2b, float *restrict Left, float *restrict Right, 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 */
+5081
View File
File diff suppressed because it is too large Load Diff
+57
View File
@@ -0,0 +1,57 @@
#ifndef AL_COMPAT_H
#define AL_COMPAT_H
#include "alstring.h"
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
WCHAR *strdupW(const WCHAR *str);
/* Opens a file with standard I/O. The filename is expected to be UTF-8. */
FILE *al_fopen(const char *fname, const char *mode);
#define HAVE_DYNLOAD 1
#else
#define al_fopen fopen
#if defined(HAVE_DLFCN_H) && !defined(IN_IDE_PARSER)
#define HAVE_DYNLOAD 1
#endif
#endif
struct FileMapping {
#ifdef _WIN32
HANDLE file;
HANDLE fmap;
#else
int fd;
#endif
void *ptr;
size_t len;
};
struct FileMapping MapFileToMem(const char *fname);
void UnmapFileMem(const struct FileMapping *mapping);
al_string GetProcPath(void);
#ifdef HAVE_DYNLOAD
void *LoadLib(const char *name);
void CloseLib(void *handle);
void *GetSymbol(void *handle, const char *name);
#endif
#ifdef __ANDROID__
#define JCALL(obj, func) ((*(obj))->func((obj), EXTRACT_VCALL_ARGS
#define JCALL0(obj, func) ((*(obj))->func((obj) EXTRACT_VCALL_ARGS
/** Returns a JNIEnv*. */
void *Android_GetJNIEnv(void);
#endif
#endif /* AL_COMPAT_H */
+466
View File
@@ -0,0 +1,466 @@
#include "config.h"
#include "converter.h"
#include "mixer_defs.h"
SampleConverter *CreateSampleConverter(enum DevFmtType srcType, enum DevFmtType dstType, ALsizei numchans, ALsizei srcRate, ALsizei dstRate)
{
SampleConverter *converter;
ALsizei step;
if(numchans <= 0 || srcRate <= 0 || dstRate <= 0)
return NULL;
converter = al_calloc(16, FAM_SIZE(SampleConverter, Chan, numchans));
converter->mSrcType = srcType;
converter->mDstType = dstType;
converter->mNumChannels = numchans;
converter->mSrcTypeSize = BytesFromDevFmt(srcType);
converter->mDstTypeSize = BytesFromDevFmt(dstType);
converter->mSrcPrepCount = 0;
converter->mFracOffset = 0;
/* Have to set the mixer FPU mode since that's what the resampler code expects. */
START_MIXER_MODE();
step = fastf2i(minf((ALdouble)srcRate / dstRate, MAX_PITCH)*FRACTIONONE + 0.5f);
converter->mIncrement = maxi(step, 1);
if(converter->mIncrement == FRACTIONONE)
converter->mResample = Resample_copy32_C;
else
{
/* TODO: Allow other resamplers. */
BsincPrepare(converter->mIncrement, &converter->mState.bsinc);
converter->mResample = SelectResampler(BSincResampler);
}
END_MIXER_MODE();
return converter;
}
void DestroySampleConverter(SampleConverter **converter)
{
if(converter)
{
al_free(*converter);
*converter = NULL;
}
}
static inline ALfloat Sample_ALbyte(ALbyte val)
{ return val * (1.0f/128.0f); }
static inline ALfloat Sample_ALubyte(ALubyte val)
{ return Sample_ALbyte((ALint)val - 128); }
static inline ALfloat Sample_ALshort(ALshort val)
{ return val * (1.0f/32768.0f); }
static inline ALfloat Sample_ALushort(ALushort val)
{ return Sample_ALshort((ALint)val - 32768); }
static inline ALfloat Sample_ALint(ALint val)
{ return (val>>7) * (1.0f/16777216.0f); }
static inline ALfloat Sample_ALuint(ALuint val)
{ return Sample_ALint(val - INT_MAX - 1); }
static inline ALfloat Sample_ALfloat(ALfloat val)
{ return val; }
#define DECL_TEMPLATE(T) \
static inline void Load_##T(ALfloat *restrict dst, const T *restrict src, \
ALint srcstep, ALsizei samples) \
{ \
ALsizei i; \
for(i = 0;i < samples;i++) \
dst[i] = Sample_##T(src[i*srcstep]); \
}
DECL_TEMPLATE(ALbyte)
DECL_TEMPLATE(ALubyte)
DECL_TEMPLATE(ALshort)
DECL_TEMPLATE(ALushort)
DECL_TEMPLATE(ALint)
DECL_TEMPLATE(ALuint)
DECL_TEMPLATE(ALfloat)
#undef DECL_TEMPLATE
static void LoadSamples(ALfloat *dst, const ALvoid *src, ALint srcstep, enum DevFmtType srctype, ALsizei samples)
{
switch(srctype)
{
case DevFmtByte:
Load_ALbyte(dst, src, srcstep, samples);
break;
case DevFmtUByte:
Load_ALubyte(dst, src, srcstep, samples);
break;
case DevFmtShort:
Load_ALshort(dst, src, srcstep, samples);
break;
case DevFmtUShort:
Load_ALushort(dst, src, srcstep, samples);
break;
case DevFmtInt:
Load_ALint(dst, src, srcstep, samples);
break;
case DevFmtUInt:
Load_ALuint(dst, src, srcstep, samples);
break;
case DevFmtFloat:
Load_ALfloat(dst, src, srcstep, samples);
break;
}
}
static inline ALbyte ALbyte_Sample(ALfloat val)
{ return fastf2i(clampf(val*128.0f, -128.0f, 127.0f)); }
static inline ALubyte ALubyte_Sample(ALfloat val)
{ return ALbyte_Sample(val)+128; }
static inline ALshort ALshort_Sample(ALfloat val)
{ return fastf2i(clampf(val*32768.0f, -32768.0f, 32767.0f)); }
static inline ALushort ALushort_Sample(ALfloat val)
{ return ALshort_Sample(val)+32768; }
static inline ALint ALint_Sample(ALfloat val)
{ return fastf2i(clampf(val*16777216.0f, -16777216.0f, 16777215.0f)) << 7; }
static inline ALuint ALuint_Sample(ALfloat val)
{ return ALint_Sample(val)+INT_MAX+1; }
static inline ALfloat ALfloat_Sample(ALfloat val)
{ return val; }
#define DECL_TEMPLATE(T) \
static inline void Store_##T(T *restrict dst, const ALfloat *restrict src, \
ALint dststep, ALsizei samples) \
{ \
ALsizei i; \
for(i = 0;i < samples;i++) \
dst[i*dststep] = T##_Sample(src[i]); \
}
DECL_TEMPLATE(ALbyte)
DECL_TEMPLATE(ALubyte)
DECL_TEMPLATE(ALshort)
DECL_TEMPLATE(ALushort)
DECL_TEMPLATE(ALint)
DECL_TEMPLATE(ALuint)
DECL_TEMPLATE(ALfloat)
#undef DECL_TEMPLATE
static void StoreSamples(ALvoid *dst, const ALfloat *src, ALint dststep, enum DevFmtType dsttype, ALsizei samples)
{
switch(dsttype)
{
case DevFmtByte:
Store_ALbyte(dst, src, dststep, samples);
break;
case DevFmtUByte:
Store_ALubyte(dst, src, dststep, samples);
break;
case DevFmtShort:
Store_ALshort(dst, src, dststep, samples);
break;
case DevFmtUShort:
Store_ALushort(dst, src, dststep, samples);
break;
case DevFmtInt:
Store_ALint(dst, src, dststep, samples);
break;
case DevFmtUInt:
Store_ALuint(dst, src, dststep, samples);
break;
case DevFmtFloat:
Store_ALfloat(dst, src, dststep, samples);
break;
}
}
ALsizei SampleConverterAvailableOut(SampleConverter *converter, ALsizei srcframes)
{
ALint prepcount = converter->mSrcPrepCount;
ALsizei increment = converter->mIncrement;
ALsizei DataPosFrac = converter->mFracOffset;
ALuint64 DataSize64;
if(prepcount < 0)
{
/* Negative prepcount means we need to skip that many input samples. */
if(-prepcount >= srcframes)
return 0;
srcframes += prepcount;
prepcount = 0;
}
if(srcframes < 1)
{
/* No output samples if there's no input samples. */
return 0;
}
if(prepcount < MAX_POST_SAMPLES+MAX_PRE_SAMPLES &&
MAX_POST_SAMPLES+MAX_PRE_SAMPLES-prepcount >= srcframes)
{
/* Not enough input samples to generate an output sample. */
return 0;
}
DataSize64 = prepcount;
DataSize64 += srcframes;
DataSize64 -= MAX_POST_SAMPLES+MAX_PRE_SAMPLES;
DataSize64 <<= FRACTIONBITS;
DataSize64 -= DataPosFrac;
/* If we have a full prep, we can generate at least one sample. */
return (ALsizei)clampu64((DataSize64 + increment-1)/increment, 1, BUFFERSIZE);
}
ALsizei SampleConverterInput(SampleConverter *converter, const ALvoid **src, ALsizei *srcframes, ALvoid *dst, ALsizei dstframes)
{
const ALsizei SrcFrameSize = converter->mNumChannels * converter->mSrcTypeSize;
const ALsizei DstFrameSize = converter->mNumChannels * converter->mDstTypeSize;
const ALsizei increment = converter->mIncrement;
ALsizei pos = 0;
START_MIXER_MODE();
while(pos < dstframes && *srcframes > 0)
{
ALfloat *restrict SrcData = ASSUME_ALIGNED(converter->mSrcSamples, 16);
ALfloat *restrict DstData = ASSUME_ALIGNED(converter->mDstSamples, 16);
ALint prepcount = converter->mSrcPrepCount;
ALsizei DataPosFrac = converter->mFracOffset;
ALuint64 DataSize64;
ALsizei DstSize;
ALint toread;
ALsizei chan;
if(prepcount < 0)
{
/* Negative prepcount means we need to skip that many input samples. */
if(-prepcount >= *srcframes)
{
converter->mSrcPrepCount = prepcount + *srcframes;
*srcframes = 0;
break;
}
*src = (const ALbyte*)*src + SrcFrameSize*-prepcount;
*srcframes += prepcount;
converter->mSrcPrepCount = 0;
continue;
}
toread = mini(*srcframes, BUFFERSIZE-(MAX_POST_SAMPLES+MAX_PRE_SAMPLES));
if(prepcount < MAX_POST_SAMPLES+MAX_PRE_SAMPLES &&
MAX_POST_SAMPLES+MAX_PRE_SAMPLES-prepcount >= toread)
{
/* Not enough input samples to generate an output sample. Store
* what we're given for later.
*/
for(chan = 0;chan < converter->mNumChannels;chan++)
LoadSamples(&converter->Chan[chan].mPrevSamples[prepcount],
(const ALbyte*)*src + converter->mSrcTypeSize*chan,
converter->mNumChannels, converter->mSrcType, toread
);
converter->mSrcPrepCount = prepcount + toread;
*srcframes = 0;
break;
}
DataSize64 = prepcount;
DataSize64 += toread;
DataSize64 -= MAX_POST_SAMPLES+MAX_PRE_SAMPLES;
DataSize64 <<= FRACTIONBITS;
DataSize64 -= DataPosFrac;
/* If we have a full prep, we can generate at least one sample. */
DstSize = (ALsizei)clampu64((DataSize64 + increment-1)/increment, 1, BUFFERSIZE);
DstSize = mini(DstSize, dstframes-pos);
for(chan = 0;chan < converter->mNumChannels;chan++)
{
const ALbyte *SrcSamples = (const ALbyte*)*src + converter->mSrcTypeSize*chan;
ALbyte *DstSamples = (ALbyte*)dst + converter->mDstTypeSize*chan;
const ALfloat *ResampledData;
ALsizei SrcDataEnd;
/* Load the previous samples into the source data first, then the
* new samples from the input buffer.
*/
memcpy(SrcData, converter->Chan[chan].mPrevSamples,
prepcount*sizeof(ALfloat));
LoadSamples(SrcData + prepcount, SrcSamples,
converter->mNumChannels, converter->mSrcType, toread
);
/* Store as many prep samples for next time as possible, given the
* number of output samples being generated.
*/
SrcDataEnd = (DataPosFrac + increment*DstSize)>>FRACTIONBITS;
if(SrcDataEnd >= prepcount+toread)
memset(converter->Chan[chan].mPrevSamples, 0,
sizeof(converter->Chan[chan].mPrevSamples));
else
{
size_t len = mini(MAX_PRE_SAMPLES+MAX_POST_SAMPLES, prepcount+toread-SrcDataEnd);
memcpy(converter->Chan[chan].mPrevSamples, &SrcData[SrcDataEnd],
len*sizeof(ALfloat));
memset(converter->Chan[chan].mPrevSamples+len, 0,
sizeof(converter->Chan[chan].mPrevSamples) - len*sizeof(ALfloat));
}
/* Now resample, and store the result in the output buffer. */
ResampledData = converter->mResample(&converter->mState,
SrcData+MAX_PRE_SAMPLES, DataPosFrac, increment,
DstData, DstSize
);
StoreSamples(DstSamples, ResampledData, converter->mNumChannels,
converter->mDstType, DstSize);
}
/* Update the number of prep samples still available, as well as the
* fractional offset.
*/
DataPosFrac += increment*DstSize;
converter->mSrcPrepCount = mini(MAX_PRE_SAMPLES+MAX_POST_SAMPLES,
prepcount+toread-(DataPosFrac>>FRACTIONBITS));
converter->mFracOffset = DataPosFrac & FRACTIONMASK;
/* Update the src and dst pointers in case there's still more to do. */
*src = (const ALbyte*)*src + SrcFrameSize*(DataPosFrac>>FRACTIONBITS);
*srcframes -= mini(*srcframes, (DataPosFrac>>FRACTIONBITS));
dst = (ALbyte*)dst + DstFrameSize*DstSize;
pos += DstSize;
}
END_MIXER_MODE();
return pos;
}
ChannelConverter *CreateChannelConverter(enum DevFmtType srcType, enum DevFmtChannels srcChans, enum DevFmtChannels dstChans)
{
ChannelConverter *converter;
if(srcChans != dstChans && !((srcChans == DevFmtMono && dstChans == DevFmtStereo) ||
(srcChans == DevFmtStereo && dstChans == DevFmtMono)))
return NULL;
converter = al_calloc(DEF_ALIGN, sizeof(*converter));
converter->mSrcType = srcType;
converter->mSrcChans = srcChans;
converter->mDstChans = dstChans;
return converter;
}
void DestroyChannelConverter(ChannelConverter **converter)
{
if(converter)
{
al_free(*converter);
*converter = NULL;
}
}
#define DECL_TEMPLATE(T) \
static void Mono2Stereo##T(ALfloat *restrict dst, const T *src, ALsizei frames)\
{ \
ALsizei i; \
for(i = 0;i < frames;i++) \
dst[i*2 + 1] = dst[i*2 + 0] = Sample_##T(src[i]) * 0.707106781187f; \
} \
\
static void Stereo2Mono##T(ALfloat *restrict dst, const T *src, ALsizei frames)\
{ \
ALsizei i; \
for(i = 0;i < frames;i++) \
dst[i] = (Sample_##T(src[i*2 + 0])+Sample_##T(src[i*2 + 1])) * \
0.707106781187f; \
}
DECL_TEMPLATE(ALbyte)
DECL_TEMPLATE(ALubyte)
DECL_TEMPLATE(ALshort)
DECL_TEMPLATE(ALushort)
DECL_TEMPLATE(ALint)
DECL_TEMPLATE(ALuint)
DECL_TEMPLATE(ALfloat)
#undef DECL_TEMPLATE
void ChannelConverterInput(ChannelConverter *converter, const ALvoid *src, ALfloat *dst, ALsizei frames)
{
if(converter->mSrcChans == converter->mDstChans)
{
LoadSamples(dst, src, 1, converter->mSrcType,
frames*ChannelsFromDevFmt(converter->mSrcChans, 0));
return;
}
if(converter->mSrcChans == DevFmtStereo && converter->mDstChans == DevFmtMono)
{
switch(converter->mSrcType)
{
case DevFmtByte:
Stereo2MonoALbyte(dst, src, frames);
break;
case DevFmtUByte:
Stereo2MonoALubyte(dst, src, frames);
break;
case DevFmtShort:
Stereo2MonoALshort(dst, src, frames);
break;
case DevFmtUShort:
Stereo2MonoALushort(dst, src, frames);
break;
case DevFmtInt:
Stereo2MonoALint(dst, src, frames);
break;
case DevFmtUInt:
Stereo2MonoALuint(dst, src, frames);
break;
case DevFmtFloat:
Stereo2MonoALfloat(dst, src, frames);
break;
}
}
else /*if(converter->mSrcChans == DevFmtMono && converter->mDstChans == DevFmtStereo)*/
{
switch(converter->mSrcType)
{
case DevFmtByte:
Mono2StereoALbyte(dst, src, frames);
break;
case DevFmtUByte:
Mono2StereoALubyte(dst, src, frames);
break;
case DevFmtShort:
Mono2StereoALshort(dst, src, frames);
break;
case DevFmtUShort:
Mono2StereoALushort(dst, src, frames);
break;
case DevFmtInt:
Mono2StereoALint(dst, src, frames);
break;
case DevFmtUInt:
Mono2StereoALuint(dst, src, frames);
break;
case DevFmtFloat:
Mono2StereoALfloat(dst, src, frames);
break;
}
}
}
+55
View File
@@ -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 */
+410
View File
@@ -0,0 +1,410 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 2013 by Mike Gorchak
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include <math.h>
#include <stdlib.h>
#include "alMain.h"
#include "alFilter.h"
#include "alAuxEffectSlot.h"
#include "alError.h"
#include "alu.h"
enum ChorusWaveForm {
CWF_Triangle = AL_CHORUS_WAVEFORM_TRIANGLE,
CWF_Sinusoid = AL_CHORUS_WAVEFORM_SINUSOID
};
typedef struct ALchorusState {
DERIVE_FROM_TYPE(ALeffectState);
ALfloat *SampleBuffer[2];
ALsizei BufferLength;
ALsizei offset;
ALsizei lfo_range;
ALfloat lfo_scale;
ALint lfo_disp;
/* Gains for left and right sides */
ALfloat Gain[2][MAX_OUTPUT_CHANNELS];
/* effect parameters */
enum ChorusWaveForm waveform;
ALint delay;
ALfloat depth;
ALfloat feedback;
} ALchorusState;
static ALvoid ALchorusState_Destruct(ALchorusState *state);
static ALboolean ALchorusState_deviceUpdate(ALchorusState *state, ALCdevice *Device);
static ALvoid ALchorusState_update(ALchorusState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props);
static ALvoid ALchorusState_process(ALchorusState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
DECLARE_DEFAULT_ALLOCATORS(ALchorusState)
DEFINE_ALEFFECTSTATE_VTABLE(ALchorusState);
static void ALchorusState_Construct(ALchorusState *state)
{
ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
SET_VTABLE2(ALchorusState, ALeffectState, state);
state->BufferLength = 0;
state->SampleBuffer[0] = NULL;
state->SampleBuffer[1] = NULL;
state->offset = 0;
state->lfo_range = 1;
state->waveform = CWF_Triangle;
}
static ALvoid ALchorusState_Destruct(ALchorusState *state)
{
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)
{
ALsizei maxlen;
ALsizei it;
maxlen = fastf2i(AL_CHORUS_MAX_DELAY * 2.0f * Device->Frequency) + 1;
maxlen = NextPowerOf2(maxlen);
if(maxlen != state->BufferLength)
{
void *temp = al_calloc(16, maxlen * sizeof(ALfloat) * 2);
if(!temp) return AL_FALSE;
al_free(state->SampleBuffer[0]);
state->SampleBuffer[0] = temp;
state->SampleBuffer[1] = state->SampleBuffer[0] + maxlen;
state->BufferLength = maxlen;
}
for(it = 0;it < state->BufferLength;it++)
{
state->SampleBuffer[0][it] = 0.0f;
state->SampleBuffer[1][it] = 0.0f;
}
return AL_TRUE;
}
static ALvoid ALchorusState_update(ALchorusState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props)
{
ALfloat frequency = (ALfloat)Device->Frequency;
ALfloat coeffs[MAX_AMBI_COEFFS];
ALfloat rate;
ALint phase;
switch(props->Chorus.Waveform)
{
case AL_CHORUS_WAVEFORM_TRIANGLE:
state->waveform = CWF_Triangle;
break;
case AL_CHORUS_WAVEFORM_SINUSOID:
state->waveform = CWF_Sinusoid;
break;
}
state->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 */
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 = props->Chorus.Phase;
rate = props->Chorus.Rate;
if(!(rate > 0.0f))
{
state->lfo_scale = 0.0f;
state->lfo_range = 1;
state->lfo_disp = 0;
}
else
{
/* Calculate LFO coefficient */
state->lfo_range = fastf2i(frequency/rate + 0.5f);
switch(state->waveform)
{
case CWF_Triangle:
state->lfo_scale = 4.0f / state->lfo_range;
break;
case CWF_Sinusoid:
state->lfo_scale = F_TAU / state->lfo_range;
break;
}
/* Calculate lfo phase displacement */
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 void GetTriangleDelays(ALint *restrict delays, ALsizei offset, const ALsizei lfo_range,
const ALfloat lfo_scale, const ALfloat depth, const ALsizei delay,
const ALsizei todo)
{
ALsizei i;
for(i = 0;i < todo;i++)
{
delays[i] = fastf2i((1.0f - fabsf(2.0f - lfo_scale*offset)) * depth) + delay;
offset = (offset+1)%lfo_range;
}
}
static void GetSinusoidDelays(ALint *restrict delays, ALsizei offset, const ALsizei lfo_range,
const ALfloat lfo_scale, const ALfloat depth, const ALsizei delay,
const ALsizei todo)
{
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];
ALint moddelays[2][128];
switch(state->waveform)
{
case CWF_Triangle:
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:
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(i = 0;i < todo;i++)
{
leftbuf[offset&bufmask] = SamplesIn[0][base+i];
temps[i][0] = leftbuf[(offset-moddelays[0][i])&bufmask] * feedback;
leftbuf[offset&bufmask] += temps[i][0];
rightbuf[offset&bufmask] = SamplesIn[0][base+i];
temps[i][1] = rightbuf[(offset-moddelays[1][i])&bufmask] * feedback;
rightbuf[offset&bufmask] += temps[i][1];
offset++;
}
for(c = 0;c < NumChannels;c++)
{
ALfloat gain = state->Gain[0][c];
if(fabsf(gain) > GAIN_SILENCE_THRESHOLD)
{
for(i = 0;i < todo;i++)
SamplesOut[c][i+base] += temps[i][0] * gain;
}
gain = state->Gain[1][c];
if(fabsf(gain) > GAIN_SILENCE_THRESHOLD)
{
for(i = 0;i < todo;i++)
SamplesOut[c][i+base] += temps[i][1] * gain;
}
}
base += todo;
}
state->offset = offset;
}
typedef struct ALchorusStateFactory {
DERIVE_FROM_TYPE(ALeffectStateFactory);
} ALchorusStateFactory;
static ALeffectState *ALchorusStateFactory_create(ALchorusStateFactory *UNUSED(factory))
{
ALchorusState *state;
NEW_OBJ0(state, ALchorusState)();
if(!state) return NULL;
return STATIC_CAST(ALeffectState, state);
}
DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALchorusStateFactory);
ALeffectStateFactory *ALchorusStateFactory_getFactory(void)
{
static ALchorusStateFactory ChorusFactory = { { GET_VTABLE2(ALchorusStateFactory, ALeffectStateFactory) } };
return STATIC_CAST(ALeffectStateFactory, &ChorusFactory);
}
void ALchorus_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
{
ALeffectProps *props = &effect->Props;
switch(param)
{
case AL_CHORUS_WAVEFORM:
if(!(val >= AL_CHORUS_MIN_WAVEFORM && val <= AL_CHORUS_MAX_WAVEFORM))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Chorus.Waveform = val;
break;
case AL_CHORUS_PHASE:
if(!(val >= AL_CHORUS_MIN_PHASE && val <= AL_CHORUS_MAX_PHASE))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Chorus.Phase = val;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
void ALchorus_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
{
ALchorus_setParami(effect, context, param, vals[0]);
}
void ALchorus_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
{
ALeffectProps *props = &effect->Props;
switch(param)
{
case AL_CHORUS_RATE:
if(!(val >= AL_CHORUS_MIN_RATE && val <= AL_CHORUS_MAX_RATE))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Chorus.Rate = val;
break;
case AL_CHORUS_DEPTH:
if(!(val >= AL_CHORUS_MIN_DEPTH && val <= AL_CHORUS_MAX_DEPTH))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Chorus.Depth = val;
break;
case AL_CHORUS_FEEDBACK:
if(!(val >= AL_CHORUS_MIN_FEEDBACK && val <= AL_CHORUS_MAX_FEEDBACK))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Chorus.Feedback = val;
break;
case AL_CHORUS_DELAY:
if(!(val >= AL_CHORUS_MIN_DELAY && val <= AL_CHORUS_MAX_DELAY))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Chorus.Delay = val;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
void ALchorus_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
{
ALchorus_setParamf(effect, context, param, vals[0]);
}
void ALchorus_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
{
const ALeffectProps *props = &effect->Props;
switch(param)
{
case AL_CHORUS_WAVEFORM:
*val = props->Chorus.Waveform;
break;
case AL_CHORUS_PHASE:
*val = props->Chorus.Phase;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
void ALchorus_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
{
ALchorus_getParami(effect, context, param, vals);
}
void ALchorus_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
{
const ALeffectProps *props = &effect->Props;
switch(param)
{
case AL_CHORUS_RATE:
*val = props->Chorus.Rate;
break;
case AL_CHORUS_DEPTH:
*val = props->Chorus.Depth;
break;
case AL_CHORUS_FEEDBACK:
*val = props->Chorus.Feedback;
break;
case AL_CHORUS_DELAY:
*val = props->Chorus.Delay;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
void ALchorus_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
{
ALchorus_getParamf(effect, context, param, vals);
}
DEFINE_ALEFFECT_VTABLE(ALchorus);
+254
View File
@@ -0,0 +1,254 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 2013 by Anis A. Hireche
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include <stdlib.h>
#include "config.h"
#include "alError.h"
#include "alMain.h"
#include "alAuxEffectSlot.h"
#include "alu.h"
typedef struct ALcompressorState {
DERIVE_FROM_TYPE(ALeffectState);
/* Effect gains for each channel */
ALfloat Gain[MAX_EFFECT_CHANNELS][MAX_OUTPUT_CHANNELS];
/* Effect parameters */
ALboolean Enabled;
ALfloat AttackRate;
ALfloat ReleaseRate;
ALfloat GainCtrl;
} ALcompressorState;
static ALvoid ALcompressorState_Destruct(ALcompressorState *state);
static ALboolean ALcompressorState_deviceUpdate(ALcompressorState *state, ALCdevice *device);
static ALvoid ALcompressorState_update(ALcompressorState *state, const ALCdevice *device, const ALeffectslot *slot, const ALeffectProps *props);
static ALvoid ALcompressorState_process(ALcompressorState *state, 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)
{
const ALfloat attackTime = device->Frequency * 0.2f; /* 200ms Attack */
const ALfloat releaseTime = device->Frequency * 0.4f; /* 400ms Release */
state->AttackRate = 1.0f / attackTime;
state->ReleaseRate = 1.0f / releaseTime;
return AL_TRUE;
}
static ALvoid ALcompressorState_update(ALcompressorState *state, const ALCdevice *device, const ALeffectslot *slot, const ALeffectProps *props)
{
ALuint i;
state->Enabled = props->Compressor.OnOff;
STATIC_CAST(ALeffectState,state)->OutBuffer = device->FOAOut.Buffer;
STATIC_CAST(ALeffectState,state)->OutChannels = device->FOAOut.NumChannels;
for(i = 0;i < 4;i++)
ComputeFirstOrderGains(device->FOAOut, IdentityMatrixf.m[i],
slot->Params.Gain, state->Gain[i]);
}
static ALvoid ALcompressorState_process(ALcompressorState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
{
ALsizei i, j, k;
ALsizei base;
for(base = 0;base < SamplesToDo;)
{
ALfloat temps[64][4];
ALsizei td = mini(64, SamplesToDo-base);
/* Load samples into the temp buffer first. */
for(j = 0;j < 4;j++)
{
for(i = 0;i < td;i++)
temps[i][j] = SamplesIn[j][i+base];
}
if(state->Enabled)
{
ALfloat gain = state->GainCtrl;
ALfloat output, amplitude;
for(i = 0;i < td;i++)
{
/* Roughly calculate the maximum amplitude from the 4-channel
* signal, and attack or release the gain control to reach it.
*/
amplitude = fabsf(temps[i][0]);
amplitude = maxf(amplitude + fabsf(temps[i][1]),
maxf(amplitude + fabsf(temps[i][2]),
amplitude + fabsf(temps[i][3])));
if(amplitude > gain)
gain = minf(gain+state->AttackRate, amplitude);
else if(amplitude < gain)
gain = maxf(gain-state->ReleaseRate, amplitude);
/* Apply the inverse of the gain control to normalize/compress
* the volume. */
output = 1.0f / clampf(gain, 0.5f, 2.0f);
for(j = 0;j < 4;j++)
temps[i][j] *= output;
}
state->GainCtrl = gain;
}
else
{
ALfloat gain = state->GainCtrl;
ALfloat output, amplitude;
for(i = 0;i < td;i++)
{
/* Same as above, except the amplitude is forced to 1. This
* helps ensure smooth gain changes when the compressor is
* turned on and off.
*/
amplitude = 1.0f;
if(amplitude > gain)
gain = minf(gain+state->AttackRate, amplitude);
else if(amplitude < gain)
gain = maxf(gain-state->ReleaseRate, amplitude);
output = 1.0f / clampf(gain, 0.5f, 2.0f);
for(j = 0;j < 4;j++)
temps[i][j] *= output;
}
state->GainCtrl = gain;
}
/* Now mix to the output. */
for(j = 0;j < 4;j++)
{
for(k = 0;k < NumChannels;k++)
{
ALfloat gain = state->Gain[j][k];
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
continue;
for(i = 0;i < td;i++)
SamplesOut[k][base+i] += gain * temps[i][j];
}
}
base += td;
}
}
typedef struct ALcompressorStateFactory {
DERIVE_FROM_TYPE(ALeffectStateFactory);
} ALcompressorStateFactory;
static ALeffectState *ALcompressorStateFactory_create(ALcompressorStateFactory *UNUSED(factory))
{
ALcompressorState *state;
NEW_OBJ0(state, ALcompressorState)();
if(!state) return NULL;
return STATIC_CAST(ALeffectState, state);
}
DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALcompressorStateFactory);
ALeffectStateFactory *ALcompressorStateFactory_getFactory(void)
{
static ALcompressorStateFactory CompressorFactory = { { GET_VTABLE2(ALcompressorStateFactory, ALeffectStateFactory) } };
return STATIC_CAST(ALeffectStateFactory, &CompressorFactory);
}
void ALcompressor_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
{
ALeffectProps *props = &effect->Props;
switch(param)
{
case AL_COMPRESSOR_ONOFF:
if(!(val >= AL_COMPRESSOR_MIN_ONOFF && val <= AL_COMPRESSOR_MAX_ONOFF))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Compressor.OnOff = val;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
void ALcompressor_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
{
ALcompressor_setParami(effect, context, param, vals[0]);
}
void ALcompressor_setParamf(ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALfloat UNUSED(val))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
void ALcompressor_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
{
ALcompressor_setParamf(effect, context, param, vals[0]);
}
void ALcompressor_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
{
const ALeffectProps *props = &effect->Props;
switch(param)
{
case AL_COMPRESSOR_ONOFF:
*val = props->Compressor.OnOff;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
void ALcompressor_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
{
ALcompressor_getParami(effect, context, param, vals);
}
void ALcompressor_getParamf(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALfloat *UNUSED(val))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
void ALcompressor_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
{
ALcompressor_getParamf(effect, context, param, vals);
}
DEFINE_ALEFFECT_VTABLE(ALcompressor);
+203
View File
@@ -0,0 +1,203 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 2011 by Chris Robinson.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include <stdlib.h>
#include "alMain.h"
#include "alFilter.h"
#include "alAuxEffectSlot.h"
#include "alError.h"
#include "alu.h"
typedef struct ALdedicatedState {
DERIVE_FROM_TYPE(ALeffectState);
ALfloat gains[MAX_OUTPUT_CHANNELS];
} ALdedicatedState;
static ALvoid ALdedicatedState_Destruct(ALdedicatedState *state);
static ALboolean ALdedicatedState_deviceUpdate(ALdedicatedState *state, ALCdevice *device);
static ALvoid ALdedicatedState_update(ALdedicatedState *state, const ALCdevice *device, const ALeffectslot *Slot, const ALeffectProps *props);
static ALvoid ALdedicatedState_process(ALdedicatedState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
DECLARE_DEFAULT_ALLOCATORS(ALdedicatedState)
DEFINE_ALEFFECTSTATE_VTABLE(ALdedicatedState);
static void ALdedicatedState_Construct(ALdedicatedState *state)
{
ALsizei s;
ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
SET_VTABLE2(ALdedicatedState, ALeffectState, state);
for(s = 0;s < MAX_OUTPUT_CHANNELS;s++)
state->gains[s] = 0.0f;
}
static ALvoid ALdedicatedState_Destruct(ALdedicatedState *state)
{
ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
}
static ALboolean ALdedicatedState_deviceUpdate(ALdedicatedState *UNUSED(state), ALCdevice *UNUSED(device))
{
return AL_TRUE;
}
static ALvoid ALdedicatedState_update(ALdedicatedState *state, const ALCdevice *device, const ALeffectslot *Slot, const ALeffectProps *props)
{
ALfloat Gain;
ALuint i;
for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
state->gains[i] = 0.0f;
Gain = Slot->Params.Gain * props->Dedicated.Gain;
if(Slot->Params.EffectType == AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT)
{
int idx;
if((idx=GetChannelIdxByName(device->RealOut, LFE)) != -1)
{
STATIC_CAST(ALeffectState,state)->OutBuffer = device->RealOut.Buffer;
STATIC_CAST(ALeffectState,state)->OutChannels = device->RealOut.NumChannels;
state->gains[idx] = Gain;
}
}
else if(Slot->Params.EffectType == AL_EFFECT_DEDICATED_DIALOGUE)
{
int idx;
/* Dialog goes to the front-center speaker if it exists, otherwise it
* plays from the front-center location. */
if((idx=GetChannelIdxByName(device->RealOut, FrontCenter)) != -1)
{
STATIC_CAST(ALeffectState,state)->OutBuffer = device->RealOut.Buffer;
STATIC_CAST(ALeffectState,state)->OutChannels = device->RealOut.NumChannels;
state->gains[idx] = Gain;
}
else
{
ALfloat coeffs[MAX_AMBI_COEFFS];
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, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
{
ALsizei i, c;
SamplesIn = ASSUME_ALIGNED(SamplesIn, 16);
SamplesOut = ASSUME_ALIGNED(SamplesOut, 16);
for(c = 0;c < NumChannels;c++)
{
const ALfloat gain = state->gains[c];
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
continue;
for(i = 0;i < SamplesToDo;i++)
SamplesOut[c][i] += SamplesIn[0][i] * gain;
}
}
typedef struct ALdedicatedStateFactory {
DERIVE_FROM_TYPE(ALeffectStateFactory);
} ALdedicatedStateFactory;
ALeffectState *ALdedicatedStateFactory_create(ALdedicatedStateFactory *UNUSED(factory))
{
ALdedicatedState *state;
NEW_OBJ0(state, ALdedicatedState)();
if(!state) return NULL;
return STATIC_CAST(ALeffectState, state);
}
DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALdedicatedStateFactory);
ALeffectStateFactory *ALdedicatedStateFactory_getFactory(void)
{
static ALdedicatedStateFactory DedicatedFactory = { { GET_VTABLE2(ALdedicatedStateFactory, ALeffectStateFactory) } };
return STATIC_CAST(ALeffectStateFactory, &DedicatedFactory);
}
void ALdedicated_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
void ALdedicated_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
{
ALdedicated_setParami(effect, context, param, vals[0]);
}
void ALdedicated_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
{
ALeffectProps *props = &effect->Props;
switch(param)
{
case AL_DEDICATED_GAIN:
if(!(val >= 0.0f && isfinite(val)))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Dedicated.Gain = val;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
void ALdedicated_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
{
ALdedicated_setParamf(effect, context, param, vals[0]);
}
void ALdedicated_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
void ALdedicated_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
{
ALdedicated_getParami(effect, context, param, vals);
}
void ALdedicated_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
{
const ALeffectProps *props = &effect->Props;
switch(param)
{
case AL_DEDICATED_GAIN:
*val = props->Dedicated.Gain;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
void ALdedicated_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
{
ALdedicated_getParamf(effect, context, param, vals);
}
DEFINE_ALEFFECT_VTABLE(ALdedicated);
+297
View File
@@ -0,0 +1,297 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 2013 by Mike Gorchak
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include <math.h>
#include <stdlib.h>
#include "alMain.h"
#include "alFilter.h"
#include "alAuxEffectSlot.h"
#include "alError.h"
#include "alu.h"
typedef struct ALdistortionState {
DERIVE_FROM_TYPE(ALeffectState);
/* Effect gains for each channel */
ALfloat Gain[MAX_OUTPUT_CHANNELS];
/* Effect parameters */
ALfilterState lowpass;
ALfilterState bandpass;
ALfloat attenuation;
ALfloat edge_coeff;
} ALdistortionState;
static ALvoid ALdistortionState_Destruct(ALdistortionState *state);
static ALboolean ALdistortionState_deviceUpdate(ALdistortionState *state, ALCdevice *device);
static ALvoid ALdistortionState_update(ALdistortionState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props);
static ALvoid ALdistortionState_process(ALdistortionState *state, 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))
{
return AL_TRUE;
}
static ALvoid ALdistortionState_update(ALdistortionState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props)
{
ALfloat frequency = (ALfloat)Device->Frequency;
ALfloat bandwidth;
ALfloat cutoff;
ALfloat edge;
/* Store distorted signal attenuation settings. */
state->attenuation = props->Distortion.Gain;
/* Store waveshaper edge settings. */
edge = sinf(props->Distortion.Edge * (F_PI_2));
edge = minf(edge, 0.99f);
state->edge_coeff = 2.0f * edge / (1.0f-edge);
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)
);
cutoff = props->Distortion.EQCenter;
/* Convert bandwidth in Hz to octaves. */
bandwidth = props->Distortion.EQBandwidth / (cutoff * 0.67f);
ALfilterState_setParams(&state->bandpass, ALfilterType_BandPass, 1.0f,
cutoff / (frequency*4.0f), calc_rcpQ_from_bandwidth(cutoff / (frequency*4.0f), bandwidth)
);
ComputeAmbientGains(Device->Dry, Slot->Params.Gain, state->Gain);
}
static ALvoid ALdistortionState_process(ALdistortionState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
{
const ALfloat fc = state->edge_coeff;
ALsizei it, kt;
ALsizei base;
for(base = 0;base < SamplesToDo;)
{
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.
*/
/* Fill oversample buffer using zero stuffing. */
for(it = 0;it < td;it++)
{
/* 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.
*/
ALfilterState_process(&state->lowpass, buffer[1], buffer[0], td*4);
/* Second step, do distortion using waveshaper function to emulate
* signal processing during tube overdriving. Three steps of
* waveshaping are intended to modify waveform without boost/clipping/
* attenuation process.
*/
for(it = 0;it < td*4;it++)
{
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));
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,
* store only one sample out of 4.
*/
ALfloat gain = state->Gain[kt] * state->attenuation;
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
continue;
for(it = 0;it < td;it++)
SamplesOut[kt][base+it] += gain * buffer[1][it*4];
}
base += td;
}
}
typedef struct ALdistortionStateFactory {
DERIVE_FROM_TYPE(ALeffectStateFactory);
} ALdistortionStateFactory;
static ALeffectState *ALdistortionStateFactory_create(ALdistortionStateFactory *UNUSED(factory))
{
ALdistortionState *state;
NEW_OBJ0(state, ALdistortionState)();
if(!state) return NULL;
return STATIC_CAST(ALeffectState, state);
}
DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALdistortionStateFactory);
ALeffectStateFactory *ALdistortionStateFactory_getFactory(void)
{
static ALdistortionStateFactory DistortionFactory = { { GET_VTABLE2(ALdistortionStateFactory, ALeffectStateFactory) } };
return STATIC_CAST(ALeffectStateFactory, &DistortionFactory);
}
void ALdistortion_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
void ALdistortion_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
{
ALdistortion_setParami(effect, context, param, vals[0]);
}
void ALdistortion_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
{
ALeffectProps *props = &effect->Props;
switch(param)
{
case AL_DISTORTION_EDGE:
if(!(val >= AL_DISTORTION_MIN_EDGE && val <= AL_DISTORTION_MAX_EDGE))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Distortion.Edge = val;
break;
case AL_DISTORTION_GAIN:
if(!(val >= AL_DISTORTION_MIN_GAIN && val <= AL_DISTORTION_MAX_GAIN))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Distortion.Gain = val;
break;
case AL_DISTORTION_LOWPASS_CUTOFF:
if(!(val >= AL_DISTORTION_MIN_LOWPASS_CUTOFF && val <= AL_DISTORTION_MAX_LOWPASS_CUTOFF))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Distortion.LowpassCutoff = val;
break;
case AL_DISTORTION_EQCENTER:
if(!(val >= AL_DISTORTION_MIN_EQCENTER && val <= AL_DISTORTION_MAX_EQCENTER))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Distortion.EQCenter = val;
break;
case AL_DISTORTION_EQBANDWIDTH:
if(!(val >= AL_DISTORTION_MIN_EQBANDWIDTH && val <= AL_DISTORTION_MAX_EQBANDWIDTH))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Distortion.EQBandwidth = val;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
void ALdistortion_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
{
ALdistortion_setParamf(effect, context, param, vals[0]);
}
void ALdistortion_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
void ALdistortion_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
{
ALdistortion_getParami(effect, context, param, vals);
}
void ALdistortion_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
{
const ALeffectProps *props = &effect->Props;
switch(param)
{
case AL_DISTORTION_EDGE:
*val = props->Distortion.Edge;
break;
case AL_DISTORTION_GAIN:
*val = props->Distortion.Gain;
break;
case AL_DISTORTION_LOWPASS_CUTOFF:
*val = props->Distortion.LowpassCutoff;
break;
case AL_DISTORTION_EQCENTER:
*val = props->Distortion.EQCenter;
break;
case AL_DISTORTION_EQBANDWIDTH:
*val = props->Distortion.EQBandwidth;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
void ALdistortion_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
{
ALdistortion_getParamf(effect, context, param, vals);
}
DEFINE_ALEFFECT_VTABLE(ALdistortion);
+326
View File
@@ -0,0 +1,326 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 2009 by Chris Robinson.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include <math.h>
#include <stdlib.h>
#include "alMain.h"
#include "alFilter.h"
#include "alAuxEffectSlot.h"
#include "alError.h"
#include "alu.h"
typedef struct ALechoState {
DERIVE_FROM_TYPE(ALeffectState);
ALfloat *SampleBuffer;
ALsizei BufferLength;
// The echo is two tap. The delay is the number of samples from before the
// current offset
struct {
ALsizei delay;
} Tap[2];
ALsizei Offset;
/* The panning gains for the two taps */
ALfloat Gain[2][MAX_OUTPUT_CHANNELS];
ALfloat FeedGain;
ALfilterState Filter;
} ALechoState;
static ALvoid ALechoState_Destruct(ALechoState *state);
static ALboolean ALechoState_deviceUpdate(ALechoState *state, ALCdevice *Device);
static ALvoid ALechoState_update(ALechoState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props);
static ALvoid ALechoState_process(ALechoState *state, 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)
{
al_free(state->SampleBuffer);
state->SampleBuffer = NULL;
ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
}
static ALboolean ALechoState_deviceUpdate(ALechoState *state, ALCdevice *Device)
{
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 = 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 = al_calloc(16, maxlen * sizeof(ALfloat));
if(!temp) return AL_FALSE;
al_free(state->SampleBuffer);
state->SampleBuffer = temp;
state->BufferLength = maxlen;
}
for(i = 0;i < state->BufferLength;i++)
state->SampleBuffer[i] = 0.0f;
return AL_TRUE;
}
static ALvoid ALechoState_update(ALechoState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props)
{
ALuint frequency = Device->Frequency;
ALfloat coeffs[MAX_AMBI_COEFFS];
ALfloat gain, lrpan, spread;
state->Tap[0].delay = fastf2i(props->Echo.Delay * frequency) + 1;
state->Tap[1].delay = fastf2i(props->Echo.LRDelay * frequency);
state->Tap[1].delay += state->Tap[0].delay;
spread = props->Echo.Spread;
if(spread < 0.0f) lrpan = -1.0f;
else lrpan = 1.0f;
/* Convert echo spread (where 0 = omni, +/-1 = directional) to coverage
* spread (where 0 = point, tau = omni).
*/
spread = asinf(1.0f - fabsf(spread))*4.0f;
state->FeedGain = props->Echo.Feedback;
gain = maxf(1.0f - props->Echo.Damping, 0.0625f); /* Limit -24dB */
ALfilterState_setParams(&state->Filter, ALfilterType_HighShelf,
gain, LOWPASSFREQREF/frequency,
calc_rcpQ_from_slope(gain, 1.0f));
gain = Slot->Params.Gain;
/* First tap panning */
CalcAngleCoeffs(-F_PI_2*lrpan, 0.0f, spread, coeffs);
ComputePanningGains(Device->Dry, coeffs, gain, state->Gain[0]);
/* Second tap panning */
CalcAngleCoeffs( F_PI_2*lrpan, 0.0f, spread, coeffs);
ComputePanningGains(Device->Dry, coeffs, gain, state->Gain[1]);
}
static ALvoid ALechoState_process(ALechoState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
{
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];
ALsizei td = mini(128, SamplesToDo-base);
for(i = 0;i < td;i++)
{
/* First tap */
temps[i][0] = state->SampleBuffer[(offset-tap1) & mask];
/* Second tap */
temps[i][1] = state->SampleBuffer[(offset-tap2) & mask];
// Apply damping and feedback gain to the second tap, and mix in the
// new sample
in = temps[i][1] + SamplesIn[0][i+base];
out = in*state->Filter.b0 +
x[0]*state->Filter.b1 + x[1]*state->Filter.b2 -
y[0]*state->Filter.a1 - y[1]*state->Filter.a2;
x[1] = x[0]; x[0] = in;
y[1] = y[0]; y[0] = out;
state->SampleBuffer[offset&mask] = out * state->FeedGain;
offset++;
}
for(k = 0;k < NumChannels;k++)
{
ALfloat gain = state->Gain[0][k];
if(fabsf(gain) > GAIN_SILENCE_THRESHOLD)
{
for(i = 0;i < td;i++)
SamplesOut[k][i+base] += temps[i][0] * gain;
}
gain = state->Gain[1][k];
if(fabsf(gain) > GAIN_SILENCE_THRESHOLD)
{
for(i = 0;i < td;i++)
SamplesOut[k][i+base] += temps[i][1] * gain;
}
}
base += td;
}
state->Filter.x[0] = x[0];
state->Filter.x[1] = x[1];
state->Filter.y[0] = y[0];
state->Filter.y[1] = y[1];
state->Offset = offset;
}
typedef struct ALechoStateFactory {
DERIVE_FROM_TYPE(ALeffectStateFactory);
} ALechoStateFactory;
ALeffectState *ALechoStateFactory_create(ALechoStateFactory *UNUSED(factory))
{
ALechoState *state;
NEW_OBJ0(state, ALechoState)();
if(!state) return NULL;
return STATIC_CAST(ALeffectState, state);
}
DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALechoStateFactory);
ALeffectStateFactory *ALechoStateFactory_getFactory(void)
{
static ALechoStateFactory EchoFactory = { { GET_VTABLE2(ALechoStateFactory, ALeffectStateFactory) } };
return STATIC_CAST(ALeffectStateFactory, &EchoFactory);
}
void ALecho_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
void ALecho_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
{
ALecho_setParami(effect, context, param, vals[0]);
}
void ALecho_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
{
ALeffectProps *props = &effect->Props;
switch(param)
{
case AL_ECHO_DELAY:
if(!(val >= AL_ECHO_MIN_DELAY && val <= AL_ECHO_MAX_DELAY))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Echo.Delay = val;
break;
case AL_ECHO_LRDELAY:
if(!(val >= AL_ECHO_MIN_LRDELAY && val <= AL_ECHO_MAX_LRDELAY))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Echo.LRDelay = val;
break;
case AL_ECHO_DAMPING:
if(!(val >= AL_ECHO_MIN_DAMPING && val <= AL_ECHO_MAX_DAMPING))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Echo.Damping = val;
break;
case AL_ECHO_FEEDBACK:
if(!(val >= AL_ECHO_MIN_FEEDBACK && val <= AL_ECHO_MAX_FEEDBACK))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Echo.Feedback = val;
break;
case AL_ECHO_SPREAD:
if(!(val >= AL_ECHO_MIN_SPREAD && val <= AL_ECHO_MAX_SPREAD))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Echo.Spread = val;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
void ALecho_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
{
ALecho_setParamf(effect, context, param, vals[0]);
}
void ALecho_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
void ALecho_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
{
ALecho_getParami(effect, context, param, vals);
}
void ALecho_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
{
const ALeffectProps *props = &effect->Props;
switch(param)
{
case AL_ECHO_DELAY:
*val = props->Echo.Delay;
break;
case AL_ECHO_LRDELAY:
*val = props->Echo.LRDelay;
break;
case AL_ECHO_DAMPING:
*val = props->Echo.Damping;
break;
case AL_ECHO_FEEDBACK:
*val = props->Echo.Feedback;
break;
case AL_ECHO_SPREAD:
*val = props->Echo.Spread;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
void ALecho_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
{
ALecho_getParamf(effect, context, param, vals);
}
DEFINE_ALEFFECT_VTABLE(ALecho);
+380
View File
@@ -0,0 +1,380 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 2013 by Mike Gorchak
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include <math.h>
#include <stdlib.h>
#include "alMain.h"
#include "alFilter.h"
#include "alAuxEffectSlot.h"
#include "alError.h"
#include "alu.h"
/* The document "Effects Extension Guide.pdf" says that low and high *
* frequencies are cutoff frequencies. This is not fully correct, they *
* are corner frequencies for low and high shelf filters. If they were *
* just cutoff frequencies, there would be no need in cutoff frequency *
* gains, which are present. Documentation for "Creative Proteus X2" *
* software describes 4-band equalizer functionality in a much better *
* way. This equalizer seems to be a predecessor of OpenAL 4-band *
* equalizer. With low and high shelf filters we are able to cutoff *
* frequencies below and/or above corner frequencies using attenuation *
* gains (below 1.0) and amplify all low and/or high frequencies using *
* gains above 1.0. *
* *
* Low-shelf Low Mid Band High Mid Band High-shelf *
* corner center center corner *
* frequency frequency frequency frequency *
* 50Hz..800Hz 200Hz..3000Hz 1000Hz..8000Hz 4000Hz..16000Hz *
* *
* | | | | *
* | | | | *
* B -----+ /--+--\ /--+--\ +----- *
* O |\ | | | | | | /| *
* O | \ - | - - | - / | *
* S + | \ | | | | | | / | *
* T | | | | | | | | | | *
* ---------+---------------+------------------+---------------+-------- *
* C | | | | | | | | | | *
* U - | / | | | | | | \ | *
* T | / - | - - | - \ | *
* O |/ | | | | | | \| *
* F -----+ \--+--/ \--+--/ +----- *
* F | | | | *
* | | | | *
* *
* Gains vary from 0.126 up to 7.943, which means from -18dB attenuation *
* up to +18dB amplification. Band width varies from 0.01 up to 1.0 in *
* octaves for two mid bands. *
* *
* Implementation is based on the "Cookbook formulae for audio EQ biquad *
* filter coefficients" by Robert Bristow-Johnson *
* http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt */
/* The maximum number of sample frames per update. */
#define MAX_UPDATE_SAMPLES 256
typedef struct ALequalizerState {
DERIVE_FROM_TYPE(ALeffectState);
/* Effect gains for each channel */
ALfloat Gain[MAX_EFFECT_CHANNELS][MAX_OUTPUT_CHANNELS];
/* Effect parameters */
ALfilterState filter[4][MAX_EFFECT_CHANNELS];
ALfloat SampleBuffer[4][MAX_EFFECT_CHANNELS][MAX_UPDATE_SAMPLES];
} ALequalizerState;
static ALvoid ALequalizerState_Destruct(ALequalizerState *state);
static ALboolean ALequalizerState_deviceUpdate(ALequalizerState *state, ALCdevice *device);
static ALvoid ALequalizerState_update(ALequalizerState *state, const ALCdevice *device, const ALeffectslot *slot, const ALeffectProps *props);
static ALvoid ALequalizerState_process(ALequalizerState *state, 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))
{
return AL_TRUE;
}
static ALvoid ALequalizerState_update(ALequalizerState *state, const ALCdevice *device, const ALeffectslot *slot, const ALeffectProps *props)
{
ALfloat frequency = (ALfloat)device->Frequency;
ALfloat gain, freq_mult;
ALuint i;
STATIC_CAST(ALeffectState,state)->OutBuffer = device->FOAOut.Buffer;
STATIC_CAST(ALeffectState,state)->OutChannels = device->FOAOut.NumChannels;
for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
ComputeFirstOrderGains(device->FOAOut, IdentityMatrixf.m[i],
slot->Params.Gain, state->Gain[i]);
/* Calculate coefficients for the each type of filter. Note that the shelf
* filters' gain is for the reference frequency, which is the centerpoint
* of the transition band.
*/
gain = 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 = 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 = 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 = 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, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
{
ALfloat (*Samples)[MAX_EFFECT_CHANNELS][MAX_UPDATE_SAMPLES] = state->SampleBuffer;
ALsizei it, kt, ft;
ALsizei base;
for(base = 0;base < SamplesToDo;)
{
ALsizei td = mini(MAX_UPDATE_SAMPLES, SamplesToDo-base);
for(ft = 0;ft < MAX_EFFECT_CHANNELS;ft++)
ALfilterState_process(&state->filter[0][ft], Samples[0][ft], &SamplesIn[ft][base], td);
for(ft = 0;ft < MAX_EFFECT_CHANNELS;ft++)
ALfilterState_process(&state->filter[1][ft], Samples[1][ft], Samples[0][ft], td);
for(ft = 0;ft < MAX_EFFECT_CHANNELS;ft++)
ALfilterState_process(&state->filter[2][ft], Samples[2][ft], Samples[1][ft], td);
for(ft = 0;ft < MAX_EFFECT_CHANNELS;ft++)
ALfilterState_process(&state->filter[3][ft], Samples[3][ft], Samples[2][ft], td);
for(ft = 0;ft < MAX_EFFECT_CHANNELS;ft++)
{
for(kt = 0;kt < NumChannels;kt++)
{
ALfloat gain = state->Gain[ft][kt];
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
continue;
for(it = 0;it < td;it++)
SamplesOut[kt][base+it] += gain * Samples[3][ft][it];
}
}
base += td;
}
}
typedef struct ALequalizerStateFactory {
DERIVE_FROM_TYPE(ALeffectStateFactory);
} ALequalizerStateFactory;
ALeffectState *ALequalizerStateFactory_create(ALequalizerStateFactory *UNUSED(factory))
{
ALequalizerState *state;
NEW_OBJ0(state, ALequalizerState)();
if(!state) return NULL;
return STATIC_CAST(ALeffectState, state);
}
DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALequalizerStateFactory);
ALeffectStateFactory *ALequalizerStateFactory_getFactory(void)
{
static ALequalizerStateFactory EqualizerFactory = { { GET_VTABLE2(ALequalizerStateFactory, ALeffectStateFactory) } };
return STATIC_CAST(ALeffectStateFactory, &EqualizerFactory);
}
void ALequalizer_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
void ALequalizer_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
{
ALequalizer_setParami(effect, context, param, vals[0]);
}
void ALequalizer_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
{
ALeffectProps *props = &effect->Props;
switch(param)
{
case AL_EQUALIZER_LOW_GAIN:
if(!(val >= AL_EQUALIZER_MIN_LOW_GAIN && val <= AL_EQUALIZER_MAX_LOW_GAIN))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Equalizer.LowGain = val;
break;
case AL_EQUALIZER_LOW_CUTOFF:
if(!(val >= AL_EQUALIZER_MIN_LOW_CUTOFF && val <= AL_EQUALIZER_MAX_LOW_CUTOFF))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Equalizer.LowCutoff = val;
break;
case AL_EQUALIZER_MID1_GAIN:
if(!(val >= AL_EQUALIZER_MIN_MID1_GAIN && val <= AL_EQUALIZER_MAX_MID1_GAIN))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Equalizer.Mid1Gain = val;
break;
case AL_EQUALIZER_MID1_CENTER:
if(!(val >= AL_EQUALIZER_MIN_MID1_CENTER && val <= AL_EQUALIZER_MAX_MID1_CENTER))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Equalizer.Mid1Center = val;
break;
case AL_EQUALIZER_MID1_WIDTH:
if(!(val >= AL_EQUALIZER_MIN_MID1_WIDTH && val <= AL_EQUALIZER_MAX_MID1_WIDTH))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Equalizer.Mid1Width = val;
break;
case AL_EQUALIZER_MID2_GAIN:
if(!(val >= AL_EQUALIZER_MIN_MID2_GAIN && val <= AL_EQUALIZER_MAX_MID2_GAIN))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Equalizer.Mid2Gain = val;
break;
case AL_EQUALIZER_MID2_CENTER:
if(!(val >= AL_EQUALIZER_MIN_MID2_CENTER && val <= AL_EQUALIZER_MAX_MID2_CENTER))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Equalizer.Mid2Center = val;
break;
case AL_EQUALIZER_MID2_WIDTH:
if(!(val >= AL_EQUALIZER_MIN_MID2_WIDTH && val <= AL_EQUALIZER_MAX_MID2_WIDTH))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Equalizer.Mid2Width = val;
break;
case AL_EQUALIZER_HIGH_GAIN:
if(!(val >= AL_EQUALIZER_MIN_HIGH_GAIN && val <= AL_EQUALIZER_MAX_HIGH_GAIN))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Equalizer.HighGain = val;
break;
case AL_EQUALIZER_HIGH_CUTOFF:
if(!(val >= AL_EQUALIZER_MIN_HIGH_CUTOFF && val <= AL_EQUALIZER_MAX_HIGH_CUTOFF))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Equalizer.HighCutoff = val;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
void ALequalizer_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
{
ALequalizer_setParamf(effect, context, param, vals[0]);
}
void ALequalizer_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
void ALequalizer_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
{
ALequalizer_getParami(effect, context, param, vals);
}
void ALequalizer_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
{
const ALeffectProps *props = &effect->Props;
switch(param)
{
case AL_EQUALIZER_LOW_GAIN:
*val = props->Equalizer.LowGain;
break;
case AL_EQUALIZER_LOW_CUTOFF:
*val = props->Equalizer.LowCutoff;
break;
case AL_EQUALIZER_MID1_GAIN:
*val = props->Equalizer.Mid1Gain;
break;
case AL_EQUALIZER_MID1_CENTER:
*val = props->Equalizer.Mid1Center;
break;
case AL_EQUALIZER_MID1_WIDTH:
*val = props->Equalizer.Mid1Width;
break;
case AL_EQUALIZER_MID2_GAIN:
*val = props->Equalizer.Mid2Gain;
break;
case AL_EQUALIZER_MID2_CENTER:
*val = props->Equalizer.Mid2Center;
break;
case AL_EQUALIZER_MID2_WIDTH:
*val = props->Equalizer.Mid2Width;
break;
case AL_EQUALIZER_HIGH_GAIN:
*val = props->Equalizer.HighGain;
break;
case AL_EQUALIZER_HIGH_CUTOFF:
*val = props->Equalizer.HighCutoff;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
void ALequalizer_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
{
ALequalizer_getParamf(effect, context, param, vals);
}
DEFINE_ALEFFECT_VTABLE(ALequalizer);
+408
View File
@@ -0,0 +1,408 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 2013 by Mike Gorchak
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include <math.h>
#include <stdlib.h>
#include "alMain.h"
#include "alFilter.h"
#include "alAuxEffectSlot.h"
#include "alError.h"
#include "alu.h"
enum FlangerWaveForm {
FWF_Triangle = AL_FLANGER_WAVEFORM_TRIANGLE,
FWF_Sinusoid = AL_FLANGER_WAVEFORM_SINUSOID
};
typedef struct ALflangerState {
DERIVE_FROM_TYPE(ALeffectState);
ALfloat *SampleBuffer[2];
ALsizei BufferLength;
ALsizei offset;
ALsizei lfo_range;
ALfloat lfo_scale;
ALint lfo_disp;
/* Gains for left and right sides */
ALfloat Gain[2][MAX_OUTPUT_CHANNELS];
/* effect parameters */
enum FlangerWaveForm waveform;
ALint delay;
ALfloat depth;
ALfloat feedback;
} ALflangerState;
static ALvoid ALflangerState_Destruct(ALflangerState *state);
static ALboolean ALflangerState_deviceUpdate(ALflangerState *state, ALCdevice *Device);
static ALvoid ALflangerState_update(ALflangerState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props);
static ALvoid ALflangerState_process(ALflangerState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
DECLARE_DEFAULT_ALLOCATORS(ALflangerState)
DEFINE_ALEFFECTSTATE_VTABLE(ALflangerState);
static void ALflangerState_Construct(ALflangerState *state)
{
ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
SET_VTABLE2(ALflangerState, ALeffectState, state);
state->BufferLength = 0;
state->SampleBuffer[0] = NULL;
state->SampleBuffer[1] = NULL;
state->offset = 0;
state->lfo_range = 1;
state->waveform = FWF_Triangle;
}
static ALvoid ALflangerState_Destruct(ALflangerState *state)
{
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)
{
ALsizei maxlen;
ALsizei it;
maxlen = fastf2i(AL_FLANGER_MAX_DELAY * 2.0f * Device->Frequency) + 1;
maxlen = NextPowerOf2(maxlen);
if(maxlen != state->BufferLength)
{
void *temp = al_calloc(16, maxlen * sizeof(ALfloat) * 2);
if(!temp) return AL_FALSE;
al_free(state->SampleBuffer[0]);
state->SampleBuffer[0] = temp;
state->SampleBuffer[1] = state->SampleBuffer[0] + maxlen;
state->BufferLength = maxlen;
}
for(it = 0;it < state->BufferLength;it++)
{
state->SampleBuffer[0][it] = 0.0f;
state->SampleBuffer[1][it] = 0.0f;
}
return AL_TRUE;
}
static ALvoid ALflangerState_update(ALflangerState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props)
{
ALfloat frequency = (ALfloat)Device->Frequency;
ALfloat coeffs[MAX_AMBI_COEFFS];
ALfloat rate;
ALint phase;
switch(props->Flanger.Waveform)
{
case AL_FLANGER_WAVEFORM_TRIANGLE:
state->waveform = FWF_Triangle;
break;
case AL_FLANGER_WAVEFORM_SINUSOID:
state->waveform = FWF_Sinusoid;
break;
}
state->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 */
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 = props->Flanger.Phase;
rate = props->Flanger.Rate;
if(!(rate > 0.0f))
{
state->lfo_scale = 0.0f;
state->lfo_range = 1;
state->lfo_disp = 0;
}
else
{
/* Calculate LFO coefficient */
state->lfo_range = fastf2i(frequency/rate + 0.5f);
switch(state->waveform)
{
case FWF_Triangle:
state->lfo_scale = 4.0f / state->lfo_range;
break;
case FWF_Sinusoid:
state->lfo_scale = F_TAU / state->lfo_range;
break;
}
/* Calculate lfo phase displacement */
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 void GetTriangleDelays(ALint *restrict delays, ALsizei offset, const ALsizei lfo_range,
const ALfloat lfo_scale, const ALfloat depth, const ALsizei delay,
const ALsizei todo)
{
ALsizei i;
for(i = 0;i < todo;i++)
{
delays[i] = fastf2i((1.0f - fabsf(2.0f - lfo_scale*offset)) * depth) + delay;
offset = (offset+1)%lfo_range;
}
}
static void GetSinusoidDelays(ALint *restrict delays, ALsizei offset, const ALsizei lfo_range,
const ALfloat lfo_scale, const ALfloat depth, const ALsizei delay,
const ALsizei todo)
{
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];
ALint moddelays[2][128];
switch(state->waveform)
{
case FWF_Triangle:
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:
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(i = 0;i < todo;i++)
{
leftbuf[offset&bufmask] = SamplesIn[0][base+i];
temps[i][0] = leftbuf[(offset-moddelays[0][i])&bufmask] * feedback;
leftbuf[offset&bufmask] += temps[i][0];
rightbuf[offset&bufmask] = SamplesIn[0][base+i];
temps[i][1] = rightbuf[(offset-moddelays[1][i])&bufmask] * feedback;
rightbuf[offset&bufmask] += temps[i][1];
offset++;
}
for(c = 0;c < NumChannels;c++)
{
ALfloat gain = state->Gain[0][c];
if(fabsf(gain) > GAIN_SILENCE_THRESHOLD)
{
for(i = 0;i < todo;i++)
SamplesOut[c][i+base] += temps[i][0] * gain;
}
gain = state->Gain[1][c];
if(fabsf(gain) > GAIN_SILENCE_THRESHOLD)
{
for(i = 0;i < todo;i++)
SamplesOut[c][i+base] += temps[i][1] * gain;
}
}
base += todo;
}
state->offset = offset;
}
typedef struct ALflangerStateFactory {
DERIVE_FROM_TYPE(ALeffectStateFactory);
} ALflangerStateFactory;
ALeffectState *ALflangerStateFactory_create(ALflangerStateFactory *UNUSED(factory))
{
ALflangerState *state;
NEW_OBJ0(state, ALflangerState)();
if(!state) return NULL;
return STATIC_CAST(ALeffectState, state);
}
DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALflangerStateFactory);
ALeffectStateFactory *ALflangerStateFactory_getFactory(void)
{
static ALflangerStateFactory FlangerFactory = { { GET_VTABLE2(ALflangerStateFactory, ALeffectStateFactory) } };
return STATIC_CAST(ALeffectStateFactory, &FlangerFactory);
}
void ALflanger_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
{
ALeffectProps *props = &effect->Props;
switch(param)
{
case AL_FLANGER_WAVEFORM:
if(!(val >= AL_FLANGER_MIN_WAVEFORM && val <= AL_FLANGER_MAX_WAVEFORM))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Flanger.Waveform = val;
break;
case AL_FLANGER_PHASE:
if(!(val >= AL_FLANGER_MIN_PHASE && val <= AL_FLANGER_MAX_PHASE))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Flanger.Phase = val;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
void ALflanger_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
{
ALflanger_setParami(effect, context, param, vals[0]);
}
void ALflanger_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
{
ALeffectProps *props = &effect->Props;
switch(param)
{
case AL_FLANGER_RATE:
if(!(val >= AL_FLANGER_MIN_RATE && val <= AL_FLANGER_MAX_RATE))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Flanger.Rate = val;
break;
case AL_FLANGER_DEPTH:
if(!(val >= AL_FLANGER_MIN_DEPTH && val <= AL_FLANGER_MAX_DEPTH))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Flanger.Depth = val;
break;
case AL_FLANGER_FEEDBACK:
if(!(val >= AL_FLANGER_MIN_FEEDBACK && val <= AL_FLANGER_MAX_FEEDBACK))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Flanger.Feedback = val;
break;
case AL_FLANGER_DELAY:
if(!(val >= AL_FLANGER_MIN_DELAY && val <= AL_FLANGER_MAX_DELAY))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Flanger.Delay = val;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
void ALflanger_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
{
ALflanger_setParamf(effect, context, param, vals[0]);
}
void ALflanger_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
{
const ALeffectProps *props = &effect->Props;
switch(param)
{
case AL_FLANGER_WAVEFORM:
*val = props->Flanger.Waveform;
break;
case AL_FLANGER_PHASE:
*val = props->Flanger.Phase;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
void ALflanger_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
{
ALflanger_getParami(effect, context, param, vals);
}
void ALflanger_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
{
const ALeffectProps *props = &effect->Props;
switch(param)
{
case AL_FLANGER_RATE:
*val = props->Flanger.Rate;
break;
case AL_FLANGER_DEPTH:
*val = props->Flanger.Depth;
break;
case AL_FLANGER_FEEDBACK:
*val = props->Flanger.Feedback;
break;
case AL_FLANGER_DELAY:
*val = props->Flanger.Delay;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
void ALflanger_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
{
ALflanger_getParamf(effect, context, param, vals);
}
DEFINE_ALEFFECT_VTABLE(ALflanger);
+311
View File
@@ -0,0 +1,311 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 2009 by Chris Robinson.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include <math.h>
#include <stdlib.h>
#include "alMain.h"
#include "alFilter.h"
#include "alAuxEffectSlot.h"
#include "alError.h"
#include "alu.h"
typedef struct ALmodulatorState {
DERIVE_FROM_TYPE(ALeffectState);
void (*Process)(ALfloat*, const ALfloat*, ALsizei, const ALsizei, ALsizei);
ALsizei index;
ALsizei step;
ALfloat Gain[MAX_EFFECT_CHANNELS][MAX_OUTPUT_CHANNELS];
ALfilterState Filter[MAX_EFFECT_CHANNELS];
} ALmodulatorState;
static ALvoid ALmodulatorState_Destruct(ALmodulatorState *state);
static ALboolean ALmodulatorState_deviceUpdate(ALmodulatorState *state, ALCdevice *device);
static ALvoid ALmodulatorState_update(ALmodulatorState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props);
static ALvoid ALmodulatorState_process(ALmodulatorState *state, 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(ALsizei index)
{
return sinf(index*(F_TAU/WAVEFORM_FRACONE) - F_PI)*0.5f + 0.5f;
}
static inline ALfloat Saw(ALsizei index)
{
return (ALfloat)index / WAVEFORM_FRACONE;
}
static inline ALfloat Square(ALsizei index)
{
return (ALfloat)((index >> (WAVEFORM_FRACBITS - 1)) & 1);
}
#define DECL_TEMPLATE(func) \
static void Modulate##func(ALfloat *restrict dst, const ALfloat *restrict src,\
ALsizei index, const ALsizei step, ALsizei todo) \
{ \
ALsizei i; \
for(i = 0;i < todo;i++) \
{ \
index += step; \
index &= WAVEFORM_FRACMASK; \
dst[i] = src[i] * func(index); \
} \
}
DECL_TEMPLATE(Sin)
DECL_TEMPLATE(Saw)
DECL_TEMPLATE(Square)
#undef DECL_TEMPLATE
static void ALmodulatorState_Construct(ALmodulatorState *state)
{
ALuint i;
ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
SET_VTABLE2(ALmodulatorState, ALeffectState, state);
state->index = 0;
state->step = 1;
for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
ALfilterState_clear(&state->Filter[i]);
}
static ALvoid ALmodulatorState_Destruct(ALmodulatorState *state)
{
ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
}
static ALboolean ALmodulatorState_deviceUpdate(ALmodulatorState *UNUSED(state), ALCdevice *UNUSED(device))
{
return AL_TRUE;
}
static ALvoid ALmodulatorState_update(ALmodulatorState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props)
{
ALfloat cw, a;
ALsizei i;
if(props->Modulator.Waveform == AL_RING_MODULATOR_SINUSOID)
state->Process = ModulateSin;
else if(props->Modulator.Waveform == AL_RING_MODULATOR_SAWTOOTH)
state->Process = ModulateSaw;
else /*if(Slot->Params.EffectProps.Modulator.Waveform == AL_RING_MODULATOR_SQUARE)*/
state->Process = ModulateSquare;
state->step = 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 * props->Modulator.HighPassCutoff / Device->Frequency);
a = (2.0f-cw) - sqrtf(powf(2.0f-cw, 2.0f) - 1.0f);
for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
{
state->Filter[i].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)
{
const ALsizei step = state->step;
ALsizei index = state->index;
ALsizei base;
for(base = 0;base < SamplesToDo;)
{
ALfloat temps[2][128];
ALsizei td = mini(128, SamplesToDo-base);
ALsizei i, j, k;
for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
{
ALfilterState_process(&state->Filter[j], temps[0], &SamplesIn[j][base], td);
state->Process(temps[1], temps[0], index, step, td);
for(k = 0;k < NumChannels;k++)
{
ALfloat gain = state->Gain[j][k];
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
continue;
for(i = 0;i < td;i++)
SamplesOut[k][base+i] += gain * temps[1][i];
}
}
for(i = 0;i < td;i++)
{
index += step;
index &= WAVEFORM_FRACMASK;
}
base += td;
}
state->index = index;
}
typedef struct ALmodulatorStateFactory {
DERIVE_FROM_TYPE(ALeffectStateFactory);
} ALmodulatorStateFactory;
static ALeffectState *ALmodulatorStateFactory_create(ALmodulatorStateFactory *UNUSED(factory))
{
ALmodulatorState *state;
NEW_OBJ0(state, ALmodulatorState)();
if(!state) return NULL;
return STATIC_CAST(ALeffectState, state);
}
DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALmodulatorStateFactory);
ALeffectStateFactory *ALmodulatorStateFactory_getFactory(void)
{
static ALmodulatorStateFactory ModulatorFactory = { { GET_VTABLE2(ALmodulatorStateFactory, ALeffectStateFactory) } };
return STATIC_CAST(ALeffectStateFactory, &ModulatorFactory);
}
void ALmodulator_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
{
ALeffectProps *props = &effect->Props;
switch(param)
{
case AL_RING_MODULATOR_FREQUENCY:
if(!(val >= AL_RING_MODULATOR_MIN_FREQUENCY && val <= AL_RING_MODULATOR_MAX_FREQUENCY))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Modulator.Frequency = val;
break;
case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
if(!(val >= AL_RING_MODULATOR_MIN_HIGHPASS_CUTOFF && val <= AL_RING_MODULATOR_MAX_HIGHPASS_CUTOFF))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Modulator.HighPassCutoff = val;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
void ALmodulator_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
{
ALmodulator_setParamf(effect, context, param, vals[0]);
}
void ALmodulator_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
{
ALeffectProps *props = &effect->Props;
switch(param)
{
case AL_RING_MODULATOR_FREQUENCY:
case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
ALmodulator_setParamf(effect, context, param, (ALfloat)val);
break;
case AL_RING_MODULATOR_WAVEFORM:
if(!(val >= AL_RING_MODULATOR_MIN_WAVEFORM && val <= AL_RING_MODULATOR_MAX_WAVEFORM))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
props->Modulator.Waveform = val;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
void ALmodulator_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
{
ALmodulator_setParami(effect, context, param, vals[0]);
}
void ALmodulator_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
{
const ALeffectProps *props = &effect->Props;
switch(param)
{
case AL_RING_MODULATOR_FREQUENCY:
*val = (ALint)props->Modulator.Frequency;
break;
case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
*val = (ALint)props->Modulator.HighPassCutoff;
break;
case AL_RING_MODULATOR_WAVEFORM:
*val = props->Modulator.Waveform;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
void ALmodulator_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
{
ALmodulator_getParami(effect, context, param, vals);
}
void ALmodulator_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
{
const ALeffectProps *props = &effect->Props;
switch(param)
{
case AL_RING_MODULATOR_FREQUENCY:
*val = props->Modulator.Frequency;
break;
case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
*val = props->Modulator.HighPassCutoff;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
void ALmodulator_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
{
ALmodulator_getParamf(effect, context, param, vals);
}
DEFINE_ALEFFECT_VTABLE(ALmodulator);
+179
View File
@@ -0,0 +1,179 @@
#include "config.h"
#include <stdlib.h>
#include "AL/al.h"
#include "AL/alc.h"
#include "alMain.h"
#include "alAuxEffectSlot.h"
#include "alError.h"
typedef struct ALnullState {
DERIVE_FROM_TYPE(ALeffectState);
} ALnullState;
/* Forward-declare "virtual" functions to define the vtable with. */
static ALvoid ALnullState_Destruct(ALnullState *state);
static ALboolean ALnullState_deviceUpdate(ALnullState *state, ALCdevice *device);
static ALvoid ALnullState_update(ALnullState *state, const ALCdevice *device, const ALeffectslot *slot, const ALeffectProps *props);
static ALvoid ALnullState_process(ALnullState *state, 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. Make sure to call the parent Destruct
* function before returning!
*/
static ALvoid ALnullState_Destruct(ALnullState *state)
{
ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
}
/* This updates the device-dependant effect state. This is called on
* initialization and any time the device parameters (eg. playback frequency,
* format) have been changed.
*/
static ALboolean ALnullState_deviceUpdate(ALnullState* UNUSED(state), ALCdevice* UNUSED(device))
{
return AL_TRUE;
}
/* This updates the effect state. This is called any time the effect is
* (re)loaded into a slot.
*/
static ALvoid ALnullState_update(ALnullState* UNUSED(state), const ALCdevice* UNUSED(device), const ALeffectslot* UNUSED(slot), const ALeffectProps* UNUSED(props))
{
}
/* This processes the effect state, for the given number of samples from the
* input to the output buffer. The result should be added to the output buffer,
* not replace it.
*/
static ALvoid ALnullState_process(ALnullState* UNUSED(state), 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 declare a default method.
*/
static void *ALnullState_New(size_t size)
{
return al_malloc(16, size);
}
/* This frees the memory used by the object, after it has been destructed.
* DECLARE_DEFAULT_ALLOCATORS can be used to declare a default method.
*/
static void ALnullState_Delete(void *ptr)
{
al_free(ptr);
}
typedef struct ALnullStateFactory {
DERIVE_FROM_TYPE(ALeffectStateFactory);
} ALnullStateFactory;
/* Creates ALeffectState objects of the appropriate type. */
ALeffectState *ALnullStateFactory_create(ALnullStateFactory *UNUSED(factory))
{
ALnullState *state;
NEW_OBJ0(state, ALnullState)();
if(!state) return NULL;
return STATIC_CAST(ALeffectState, state);
}
/* Define the ALeffectStateFactory vtable for this type. */
DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALnullStateFactory);
ALeffectStateFactory *ALnullStateFactory_getFactory(void)
{
static ALnullStateFactory NullFactory = { { GET_VTABLE2(ALnullStateFactory, ALeffectStateFactory) } };
return STATIC_CAST(ALeffectStateFactory, &NullFactory);
}
void ALnull_setParami(ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, ALint UNUSED(val))
{
switch(param)
{
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
void ALnull_setParamiv(ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, const ALint* UNUSED(vals))
{
switch(param)
{
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
void ALnull_setParamf(ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, ALfloat UNUSED(val))
{
switch(param)
{
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
void ALnull_setParamfv(ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, const ALfloat* UNUSED(vals))
{
switch(param)
{
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
void ALnull_getParami(const ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, ALint* UNUSED(val))
{
switch(param)
{
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
void ALnull_getParamiv(const ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, ALint* UNUSED(vals))
{
switch(param)
{
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
void ALnull_getParamf(const ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, ALfloat* UNUSED(val))
{
switch(param)
{
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
void ALnull_getParamfv(const ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, ALfloat* UNUSED(vals))
{
switch(param)
{
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
DEFINE_ALEFFECT_VTABLE(ALnull);
+2407
View File
File diff suppressed because it is too large Load Diff
+1165
View File
File diff suppressed because it is too large Load Diff
+1154
View File
File diff suppressed because it is too large Load Diff
+52
View File
@@ -0,0 +1,52 @@
#ifndef ALC_HRTF_H
#define ALC_HRTF_H
#include "AL/al.h"
#include "AL/alc.h"
#include "alMain.h"
#include "alstring.h"
#include "atomic.h"
/* The maximum number of virtual speakers used to generate HRTF coefficients
* for decoding B-Format.
*/
#define HRTF_AMBI_MAX_CHANNELS 16
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];
};
void FreeHrtfs(void);
vector_EnumeratedHrtf EnumerateHrtf(const_al_string devname);
void FreeHrtfList(vector_EnumeratedHrtf *list);
struct Hrtf *GetLoadedHrtf(struct HrtfEntry *entry);
void Hrtf_IncRef(struct Hrtf *hrtf);
void Hrtf_DecRef(struct Hrtf *hrtf);
void GetHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azimuth, ALfloat spread, ALfloat (*coeffs)[2], ALsizei *delays);
/**
* Produces HRTF filter coefficients for decoding B-Format, given a set of
* virtual speaker positions and HF/LF matrices for decoding to them. The
* returned coefficients are ordered and scaled according to the matrices.
* Returns the maximum impulse-response length of the generated coefficients.
*/
ALsizei BuildBFormatHrtf(const struct Hrtf *Hrtf, DirectHrtfState *state, ALsizei NumChannels, const ALfloat (*restrict AmbiPoints)[2], const ALfloat (*restrict AmbiMatrix)[2][MAX_AMBI_COEFFS], ALsizei AmbiCount);
#endif /* ALC_HRTF_H */
+255
View File
@@ -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];
}
}
+671
View File
@@ -0,0 +1,671 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 1999-2007 by authors.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>
#include "alMain.h"
#include "AL/al.h"
#include "AL/alc.h"
#include "alSource.h"
#include "alBuffer.h"
#include "alListener.h"
#include "alAuxEffectSlot.h"
#include "alu.h"
#include "mixer_defs.h"
static_assert((INT_MAX>>FRACTIONBITS)/MAX_PITCH > BUFFERSIZE,
"MAX_PITCH and/or BUFFERSIZE are too large for FRACTIONBITS!");
extern inline void InitiatePositionArrays(ALsizei frac, ALint increment, ALsizei *restrict frac_arr, ALint *restrict pos_arr, ALsizei size);
/* BSinc requires up to 11 extra samples before the current position, and 12 after. */
static_assert(MAX_PRE_SAMPLES >= 11, "MAX_PRE_SAMPLES must be at least 11!");
static_assert(MAX_POST_SAMPLES >= 12, "MAX_POST_SAMPLES must be at least 12!");
enum Resampler ResamplerDefault = LinearResampler;
static MixerFunc MixSamples = Mix_C;
static HrtfMixerFunc MixHrtfSamples = MixHrtf_C;
HrtfMixerBlendFunc MixHrtfBlendSamples = MixHrtfBlend_C;
MixerFunc SelectMixer(void)
{
#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;
}
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;
#endif
#ifdef HAVE_SSE2
if((CPUCapFlags&CPU_CAP_SSE2))
return Resample_lerp32_SSE2;
#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;
#endif
#ifdef HAVE_SSE3
if((CPUCapFlags&CPU_CAP_SSE3))
return Resample_fir4_32_SSE3;
#endif
return Resample_fir4_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;
#endif
return Resample_bsinc32_C;
}
return Resample_point32_C;
}
void aluInitMixer(void)
{
const char *str;
if(ConfigValueStr(NULL, NULL, "resampler", &str))
{
if(strcasecmp(str, "point") == 0 || strcasecmp(str, "none") == 0)
ResamplerDefault = PointResampler;
else if(strcasecmp(str, "linear") == 0)
ResamplerDefault = LinearResampler;
else if(strcasecmp(str, "sinc4") == 0)
ResamplerDefault = FIR4Resampler;
else if(strcasecmp(str, "bsinc") == 0)
ResamplerDefault = BSincResampler;
else if(strcasecmp(str, "cubic") == 0 || strcasecmp(str, "sinc8") == 0)
{
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))
ResamplerDefault = n;
else
WARN("Invalid resampler: %s\n", str);
}
}
MixHrtfBlendSamples = SelectHrtfBlendMixer();
MixHrtfSamples = SelectHrtfMixer();
MixSamples = SelectMixer();
}
static inline ALfloat Sample_ALbyte(ALbyte val)
{ return val * (1.0f/128.0f); }
static inline ALfloat Sample_ALshort(ALshort val)
{ 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, ALint srcstep, ALsizei samples)\
{ \
ALsizei i; \
for(i = 0;i < samples;i++) \
dst[i] = Sample_##T(src[i*srcstep]); \
}
DECL_TEMPLATE(ALbyte)
DECL_TEMPLATE(ALshort)
DECL_TEMPLATE(ALfloat)
#undef DECL_TEMPLATE
static void LoadSamples(ALfloat *dst, const ALvoid *src, ALint srcstep, enum FmtType srctype, ALsizei samples)
{
switch(srctype)
{
case FmtByte:
Load_ALbyte(dst, src, srcstep, samples);
break;
case FmtShort:
Load_ALshort(dst, src, srcstep, samples);
break;
case FmtFloat:
Load_ALfloat(dst, src, srcstep, samples);
break;
}
}
static inline void SilenceSamples(ALfloat *dst, ALsizei samples)
{
ALsizei i;
for(i = 0;i < samples;i++)
dst[i] = 0.0f;
}
static const ALfloat *DoFilters(ALfilterState *lpfilter, ALfilterState *hpfilter,
ALfloat *restrict dst, const ALfloat *restrict src,
ALsizei numsamples, enum ActiveFilters type)
{
ALsizei i;
switch(type)
{
case AF_None:
ALfilterState_processPassthru(lpfilter, src, numsamples);
ALfilterState_processPassthru(hpfilter, src, numsamples);
break;
case AF_LowPass:
ALfilterState_process(lpfilter, dst, src, numsamples);
ALfilterState_processPassthru(hpfilter, dst, numsamples);
return dst;
case AF_HighPass:
ALfilterState_processPassthru(lpfilter, src, numsamples);
ALfilterState_process(hpfilter, dst, src, numsamples);
return dst;
case AF_BandPass:
for(i = 0;i < numsamples;)
{
ALfloat temp[256];
ALsizei todo = mini(256, numsamples-i);
ALfilterState_process(lpfilter, temp, src+i, todo);
ALfilterState_process(hpfilter, dst+i, temp, todo);
i += todo;
}
return dst;
}
return src;
}
ALboolean MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALsizei SamplesToDo)
{
ALbufferlistitem *BufferListItem;
ALbufferlistitem *BufferLoopItem;
ALsizei NumChannels, SampleSize;
ResamplerFunc Resample;
ALsizei DataPosInt;
ALsizei DataPosFrac;
ALint64 DataSize64;
ALint increment;
ALsizei Counter;
ALsizei OutPos;
ALsizei IrSize;
bool isplaying;
bool firstpass;
ALsizei chan;
ALsizei send;
/* Get source info */
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->HrtfHandle ? Device->HrtfHandle->irSize : 0);
Resample = ((increment == FRACTIONONE && DataPosFrac == 0) ?
Resample_copy32_C : voice->Resampler);
Counter = (voice->Flags&VOICE_IS_FADING) ? SamplesToDo : 0;
firstpass = true;
OutPos = 0;
do {
ALsizei SrcBufferSize, DstBufferSize;
/* Figure out how many buffer samples will be needed */
DataSize64 = SamplesToDo-OutPos;
DataSize64 *= increment;
DataSize64 += DataPosFrac+FRACTIONMASK;
DataSize64 >>= FRACTIONBITS;
DataSize64 += MAX_POST_SAMPLES+MAX_PRE_SAMPLES;
SrcBufferSize = (ALsizei)mini64(DataSize64, BUFFERSIZE);
/* Figure out how many samples we can actually mix from this. */
DataSize64 = SrcBufferSize;
DataSize64 -= MAX_POST_SAMPLES+MAX_PRE_SAMPLES;
DataSize64 <<= FRACTIONBITS;
DataSize64 -= DataPosFrac;
DstBufferSize = (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. */
if(OutPos+DstBufferSize < SamplesToDo)
DstBufferSize &= ~3;
for(chan = 0;chan < NumChannels;chan++)
{
const ALfloat *ResampledData;
ALfloat *SrcData = Device->SourceData;
ALsizei SrcDataSize;
/* Load the previous samples into the source data first. */
memcpy(SrcData, voice->PrevSamples[chan], MAX_PRE_SAMPLES*sizeof(ALfloat));
SrcDataSize = MAX_PRE_SAMPLES;
if(Source->SourceType == AL_STATIC)
{
const ALbuffer *ALBuffer = BufferListItem->buffer;
const ALubyte *Data = ALBuffer->data;
ALsizei DataSize;
/* Offset buffer data to current channel */
Data += chan*SampleSize;
/* If current pos is beyond the loop range, do not loop */
if(!BufferLoopItem || DataPosInt >= ALBuffer->LoopEnd)
{
BufferLoopItem = NULL;
/* Load what's left to play from the source buffer, and
* clear the rest of the temp buffer */
DataSize = minu(SrcBufferSize - SrcDataSize,
ALBuffer->SampleLen - DataPosInt);
LoadSamples(&SrcData[SrcDataSize], &Data[DataPosInt * NumChannels*SampleSize],
NumChannels, ALBuffer->FmtType, DataSize);
SrcDataSize += DataSize;
SilenceSamples(&SrcData[SrcDataSize], SrcBufferSize - SrcDataSize);
SrcDataSize += SrcBufferSize - SrcDataSize;
}
else
{
ALsizei LoopStart = ALBuffer->LoopStart;
ALsizei LoopEnd = ALBuffer->LoopEnd;
/* Load what's left of this loop iteration, then load
* repeats of the loop section */
DataSize = minu(SrcBufferSize - SrcDataSize, LoopEnd - DataPosInt);
LoadSamples(&SrcData[SrcDataSize], &Data[DataPosInt * NumChannels*SampleSize],
NumChannels, ALBuffer->FmtType, DataSize);
SrcDataSize += DataSize;
DataSize = LoopEnd-LoopStart;
while(SrcBufferSize > SrcDataSize)
{
DataSize = mini(SrcBufferSize - SrcDataSize, DataSize);
LoadSamples(&SrcData[SrcDataSize], &Data[LoopStart * NumChannels*SampleSize],
NumChannels, ALBuffer->FmtType, DataSize);
SrcDataSize += DataSize;
}
}
}
else
{
/* Crawl the buffer queue to fill in the temp buffer */
ALbufferlistitem *tmpiter = BufferListItem;
ALsizei pos = DataPosInt;
while(tmpiter && SrcBufferSize > SrcDataSize)
{
const ALbuffer *ALBuffer;
if((ALBuffer=tmpiter->buffer) != NULL)
{
const ALubyte *Data = ALBuffer->data;
ALsizei DataSize = ALBuffer->SampleLen;
/* Skip the data already played */
if(DataSize <= pos)
pos -= DataSize;
else
{
Data += (pos*NumChannels + chan)*SampleSize;
DataSize -= pos;
pos -= pos;
DataSize = minu(SrcBufferSize - SrcDataSize, DataSize);
LoadSamples(&SrcData[SrcDataSize], Data, NumChannels,
ALBuffer->FmtType, DataSize);
SrcDataSize += DataSize;
}
}
tmpiter = ATOMIC_LOAD(&tmpiter->next, almemory_order_acquire);
if(!tmpiter && BufferLoopItem)
tmpiter = BufferLoopItem;
else if(!tmpiter)
{
SilenceSamples(&SrcData[SrcDataSize], SrcBufferSize - SrcDataSize);
SrcDataSize += SrcBufferSize - SrcDataSize;
}
}
}
/* Store the last source samples used for next time. */
memcpy(voice->PrevSamples[chan],
&SrcData[(increment*DstBufferSize + DataPosFrac)>>FRACTIONBITS],
MAX_PRE_SAMPLES*sizeof(ALfloat)
);
/* Now resample, then filter and mix to the appropriate outputs. */
ResampledData = Resample(&voice->ResampleState,
&SrcData[MAX_PRE_SAMPLES], DataPosFrac, increment,
Device->ResampledData, DstBufferSize
);
{
DirectParams *parms = &voice->Direct.Params[chan];
const ALfloat *samples;
samples = DoFilters(
&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
);
else
{
ALfloat *nfcsamples = Device->NFCtrlData;
ALsizei chanoffset = 0;
MixSamples(samples,
voice->Direct.ChannelsPerOrder[0], voice->Direct.Buffer,
parms->Gains.Current, parms->Gains.Target, Counter, OutPos,
DstBufferSize
);
chanoffset += voice->Direct.ChannelsPerOrder[0];
#define APPLY_NFC_MIX(order) \
if(voice->Direct.ChannelsPerOrder[order] > 0) \
{ \
NfcFilterUpdate##order(&parms->NFCtrlFilter[order-1], nfcsamples, \
samples, DstBufferSize); \
MixSamples(nfcsamples, voice->Direct.ChannelsPerOrder[order], \
voice->Direct.Buffer+chanoffset, parms->Gains.Current+chanoffset, \
parms->Gains.Target+chanoffset, Counter, OutPos, DstBufferSize \
); \
chanoffset += voice->Direct.ChannelsPerOrder[order]; \
}
APPLY_NFC_MIX(1)
APPLY_NFC_MIX(2)
APPLY_NFC_MIX(3)
#undef APPLY_NFC_MIX
}
}
else
{
MixHrtfParams hrtfparams;
ALsizei fademix = 0;
int lidx, ridx;
lidx = GetChannelIdxByName(Device->RealOut, FrontLeft);
ridx = GetChannelIdxByName(Device->RealOut, FrontRight);
assert(lidx != -1 && ridx != -1);
if(!Counter)
{
/* No fading, just overwrite the old HRTF params. */
parms->Hrtf.Old = parms->Hrtf.Target;
}
else if(!(parms->Hrtf.Old.Gain > GAIN_SILENCE_THRESHOLD))
{
/* The old HRTF params are silent, so overwrite the old
* coefficients with the new, and reset the old gain to
* 0. The future mix will then fade from silence.
*/
parms->Hrtf.Old = parms->Hrtf.Target;
parms->Hrtf.Old.Gain = 0.0f;
}
else if(firstpass)
{
ALfloat gain;
/* Fade between the coefficients over 128 samples. */
fademix = mini(DstBufferSize, 128);
/* The new coefficients need to fade in completely
* since they're replacing the old ones. To keep the
* gain fading consistent, interpolate between the old
* and new target gains given how much of the fade time
* this mix handles.
*/
gain = lerp(parms->Hrtf.Old.Gain, parms->Hrtf.Target.Gain,
minf(1.0f, (ALfloat)fademix/Counter));
hrtfparams.Coeffs = SAFE_CONST(ALfloat2*,parms->Hrtf.Target.Coeffs);
hrtfparams.Delay[0] = parms->Hrtf.Target.Delay[0];
hrtfparams.Delay[1] = parms->Hrtf.Target.Delay[1];
hrtfparams.Gain = 0.0f;
hrtfparams.GainStep = gain / (ALfloat)fademix;
MixHrtfBlendSamples(
voice->Direct.Buffer[lidx], voice->Direct.Buffer[ridx],
samples, voice->Offset, OutPos, IrSize, &parms->Hrtf.Old,
&hrtfparams, &parms->Hrtf.State, fademix
);
/* Update the old parameters with the result. */
parms->Hrtf.Old = parms->Hrtf.Target;
if(fademix < Counter)
parms->Hrtf.Old.Gain = hrtfparams.Gain;
}
if(fademix < DstBufferSize)
{
ALsizei todo = DstBufferSize - fademix;
ALfloat gain = parms->Hrtf.Target.Gain;
/* Interpolate the target gain if the gain fading lasts
* longer than this mix.
*/
if(Counter > DstBufferSize)
gain = lerp(parms->Hrtf.Old.Gain, gain,
(ALfloat)todo/(Counter-fademix));
hrtfparams.Coeffs = SAFE_CONST(ALfloat2*,parms->Hrtf.Target.Coeffs);
hrtfparams.Delay[0] = parms->Hrtf.Target.Delay[0];
hrtfparams.Delay[1] = parms->Hrtf.Target.Delay[1];
hrtfparams.Gain = parms->Hrtf.Old.Gain;
hrtfparams.GainStep = (gain - parms->Hrtf.Old.Gain) / (ALfloat)todo;
MixHrtfSamples(
voice->Direct.Buffer[lidx], voice->Direct.Buffer[ridx],
samples+fademix, voice->Offset+fademix, OutPos+fademix, IrSize,
&hrtfparams, &parms->Hrtf.State, todo
);
/* Store the interpolated gain or the final target gain
* depending if the fade is done.
*/
if(DstBufferSize < Counter)
parms->Hrtf.Old.Gain = gain;
else
parms->Hrtf.Old.Gain = parms->Hrtf.Target.Gain;
}
}
}
for(send = 0;send < Device->NumAuxSends;send++)
{
SendParams *parms = &voice->Send[send].Params[chan];
const ALfloat *samples;
if(!voice->Send[send].Buffer)
continue;
samples = DoFilters(
&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
);
}
}
/* Update positions */
DataPosFrac += increment*DstBufferSize;
DataPosInt += DataPosFrac>>FRACTIONBITS;
DataPosFrac &= FRACTIONMASK;
OutPos += DstBufferSize;
voice->Offset += DstBufferSize;
Counter = maxi(DstBufferSize, Counter) - DstBufferSize;
firstpass = false;
/* Handle looping sources */
while(1)
{
const ALbuffer *ALBuffer;
ALsizei DataSize = 0;
ALsizei LoopStart = 0;
ALsizei LoopEnd = 0;
if((ALBuffer=BufferListItem->buffer) != NULL)
{
DataSize = ALBuffer->SampleLen;
LoopStart = ALBuffer->LoopStart;
LoopEnd = ALBuffer->LoopEnd;
if(LoopEnd > DataPosInt)
break;
}
if(BufferLoopItem && Source->SourceType == AL_STATIC)
{
assert(LoopEnd > LoopStart);
DataPosInt = ((DataPosInt-LoopStart)%(LoopEnd-LoopStart)) + LoopStart;
break;
}
if(DataSize > DataPosInt)
break;
BufferListItem = ATOMIC_LOAD(&BufferListItem->next, almemory_order_acquire);
if(!BufferListItem)
{
BufferListItem = BufferLoopItem;
if(!BufferListItem)
{
isplaying = false;
DataPosInt = 0;
DataPosFrac = 0;
break;
}
}
DataPosInt -= DataSize;
}
} while(isplaying && OutPos < SamplesToDo);
voice->Flags |= VOICE_IS_FADING;
/* Update source info */
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;
}
+208
View File
@@ -0,0 +1,208 @@
#include "config.h"
#include <assert.h>
#include "alMain.h"
#include "alu.h"
#include "alSource.h"
#include "alAuxEffectSlot.h"
static inline ALfloat point32(const ALfloat *restrict vals, ALsizei UNUSED(frac))
{ return vals[0]; }
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 *restrict vals, ALsizei frac)
{ return resample_fir4(vals[-1], vals[0], vals[1], vals[2], frac); }
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. */
if((((intptr_t)src)&15) == (((intptr_t)dst)&15))
return src;
#endif
memcpy(dst, src, numsamples*sizeof(ALfloat));
return dst;
}
#define DECL_TEMPLATE(Sampler) \
const ALfloat *Resample_##Sampler##_C(const InterpState* UNUSED(state), \
const ALfloat *restrict src, ALsizei frac, ALint increment, \
ALfloat *restrict dst, ALsizei numsamples) \
{ \
ALsizei i; \
for(i = 0;i < numsamples;i++) \
{ \
dst[i] = Sampler(src, frac); \
\
frac += increment; \
src += frac>>FRACTIONBITS; \
frac &= FRACTIONMASK; \
} \
return dst; \
}
DECL_TEMPLATE(point32)
DECL_TEMPLATE(lerp32)
DECL_TEMPLATE(fir4_32)
#undef DECL_TEMPLATE
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->bsinc.sf;
const ALsizei m = state->bsinc.m;
ALsizei j_f, pi, i;
ALfloat pf, r;
src += state->bsinc.l;
for(i = 0;i < dstlen;i++)
{
// Calculate the phase index and factor.
#define FRAC_PHASE_BITDIFF (FRACTIONBITS-BSINC_PHASE_BITS)
pi = frac >> FRAC_PHASE_BITDIFF;
pf = (frac & ((1<<FRAC_PHASE_BITDIFF)-1)) * (1.0f/(1<<FRAC_PHASE_BITDIFF));
#undef FRAC_PHASE_BITDIFF
fil = ASSUME_ALIGNED(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_f < m;j_f++)
r += (fil[j_f] + sf*scd[j_f] + pf*(phd[j_f] + sf*spd[j_f])) * src[j_f];
dst[i] = r;
frac += increment;
src += frac>>FRACTIONBITS;
frac &= FRACTIONMASK;
}
return dst;
}
void ALfilterState_processC(ALfilterState *filter, ALfloat *restrict dst, const ALfloat *restrict src, ALsizei numsamples)
{
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];
}
else if(numsamples == 1)
{
dst[0] = filter->b0 * src[0] +
filter->b1 * filter->x[0] +
filter->b2 * filter->x[1] -
filter->a1 * filter->y[0] -
filter->a2 * filter->y[1];
filter->x[1] = filter->x[0];
filter->x[0] = src[0];
filter->y[1] = filter->y[0];
filter->y[0] = dst[0];
}
}
static inline void ApplyCoeffs(ALsizei Offset, ALfloat (*restrict Values)[2],
const ALsizei IrSize,
const ALfloat (*restrict Coeffs)[2],
ALfloat left, ALfloat right)
{
ALsizei c;
for(c = 0;c < IrSize;c++)
{
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, ALsizei OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
ALfloat *CurrentGains, const ALfloat *TargetGains, ALsizei Counter, ALsizei OutPos,
ALsizei BufferSize)
{
ALfloat gain, delta, step;
ALsizei c;
delta = (Counter > 0) ? 1.0f/(ALfloat)Counter : 0.0f;
for(c = 0;c < OutChans;c++)
{
ALsizei pos = 0;
gain = CurrentGains[c];
step = (TargetGains[c] - gain) * delta;
if(fabsf(step) > FLT_EPSILON)
{
ALsizei minsize = mini(BufferSize, Counter);
for(;pos < minsize;pos++)
{
OutBuffer[c][OutPos+pos] += data[pos]*gain;
gain += step;
}
if(pos == Counter)
gain = TargetGains[c];
CurrentGains[c] = gain;
}
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
continue;
for(;pos < BufferSize;pos++)
OutBuffer[c][OutPos+pos] += data[pos]*gain;
}
}
/* Basically the inverse of the above. Rather than one input going to multiple
* outputs (each with its own gain), it's multiple inputs (each with its own
* gain) going to one output. This applies one row (vs one column) of a matrix
* transform. And as the matrices are more or less static once set up, no
* stepping is necessary.
*/
void MixRow_C(ALfloat *OutBuffer, const ALfloat *Gains, const ALfloat (*restrict data)[BUFFERSIZE], 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;
}
}
+129
View File
@@ -0,0 +1,129 @@
#ifndef MIXER_DEFS_H
#define MIXER_DEFS_H
#include "AL/alc.h"
#include "AL/al.h"
#include "alMain.h"
#include "alu.h"
struct MixGains;
struct MixHrtfParams;
struct HrtfState;
/* C resamplers */
const ALfloat *Resample_copy32_C(const 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 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 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(ALsizei frac, ALint increment, ALsizei *restrict frac_arr, ALint *restrict pos_arr, ALsizei size)
{
ALsizei i;
pos_arr[0] = 0;
frac_arr[0] = frac;
for(i = 1;i < size;i++)
{
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_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_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_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 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 */
+114
View File
@@ -0,0 +1,114 @@
#include "config.h"
#include "alMain.h"
#include "alSource.h"
#include "hrtf.h"
#include "mixer_defs.h"
#include "align.h"
#include "alu.h"
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 LeftOut, ALfloat *restrict RightOut,
const ALfloat *data, ALsizei Offset, ALsizei OutPos,
const ALsizei IrSize, MixHrtfParams *hrtfparams, HrtfState *hrtfstate,
ALsizei BufferSize)
{
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;
ALsizei i;
LeftOut += OutPos;
RightOut += OutPos;
for(i = 0;i < BufferSize;i++)
{
hrtfstate->History[Offset&HRTF_HISTORY_MASK] = *(data++);
left = hrtfstate->History[(Offset-Delay[0])&HRTF_HISTORY_MASK]*gain;
right = hrtfstate->History[(Offset-Delay[1])&HRTF_HISTORY_MASK]*gain;
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);
*(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];
}
}
+331
View File
@@ -0,0 +1,331 @@
#include "config.h"
#include <arm_neon.h>
#include "AL/al.h"
#include "AL/alc.h"
#include "alMain.h"
#include "alu.h"
#include "hrtf.h"
#include "mixer_defs.h"
const ALfloat *Resample_lerp32_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 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)
{
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);
}
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)
{
ALsizei 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);
}
Values = ASSUME_ALIGNED(Values, 16);
Coeffs = ASSUME_ALIGNED(Coeffs, 16);
for(c = 0;c < IrSize;c += 2)
{
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]);
vals = vmlaq_f32(vals, coefs, leftright4);
vst1_f32((float32_t*)&Values[o0][0], vget_low_f32(vals));
vst1_f32((float32_t*)&Values[o1][0], vget_high_f32(vals));
}
}
#define MixHrtf MixHrtf_Neon
#define MixHrtfBlend MixHrtfBlend_Neon
#define MixDirectHrtf MixDirectHrtf_Neon
#include "mixer_inc.c"
#undef MixHrtf
void Mix_Neon(const ALfloat *data, ALsizei OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
ALfloat *CurrentGains, const ALfloat *TargetGains, ALsizei Counter, ALsizei OutPos,
ALsizei BufferSize)
{
ALfloat gain, delta, step;
float32x4_t gain4;
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++)
{
ALsizei pos = 0;
gain = CurrentGains[c];
step = (TargetGains[c] - gain) * delta;
if(fabsf(step) > FLT_EPSILON)
{
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 = TargetGains[c];
CurrentGains[c] = gain;
/* Mix until pos is aligned with 4 or the mix is done. */
minsize = mini(BufferSize, (pos+3)&~3);
for(;pos < minsize;pos++)
OutBuffer[c][OutPos+pos] += data[pos]*gain;
}
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
continue;
gain4 = vdupq_n_f32(gain);
for(;BufferSize-pos > 3;pos += 4)
{
const float32x4_t val4 = vld1q_f32(&data[pos]);
float32x4_t dry4 = vld1q_f32(&OutBuffer[c][OutPos+pos]);
dry4 = vmlaq_f32(dry4, val4, gain4);
vst1q_f32(&OutBuffer[c][OutPos+pos], dry4);
}
for(;pos < BufferSize;pos++)
OutBuffer[c][OutPos+pos] += data[pos]*gain;
}
}
void MixRow_Neon(ALfloat *OutBuffer, const ALfloat *Gains, const ALfloat (*restrict data)[BUFFERSIZE], 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;
}
}
+230
View File
@@ -0,0 +1,230 @@
#include "config.h"
#include <xmmintrin.h>
#include "AL/al.h"
#include "AL/alc.h"
#include "alMain.h"
#include "alu.h"
#include "alSource.h"
#include "alAuxEffectSlot.h"
#include "mixer_defs.h"
const ALfloat *Resample_bsinc32_SSE(const InterpState *state, const ALfloat *restrict src,
ALsizei frac, ALint increment, ALfloat *restrict dst,
ALsizei dstlen)
{
const __m128 sf4 = _mm_set1_ps(state->bsinc.sf);
const ALsizei m = state->bsinc.m;
const ALfloat *fil, *scd, *phd, *spd;
ALsizei pi, i, j;
ALfloat pf;
__m128 r4;
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 = _mm_setzero_ps();
{
const __m128 pf4 = _mm_set1_ps(pf);
#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)
{
/* 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]))
);
/* 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));
dst[i] = _mm_cvtss_f32(r4);
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)
{
const __m128 lrlr = _mm_setr_ps(left, right, left, right);
__m128 vals = _mm_setzero_ps();
__m128 coeffs;
ALsizei i;
Values = ASSUME_ALIGNED(Values, 16);
Coeffs = ASSUME_ALIGNED(Coeffs, 16);
if((Offset&1))
{
const ALsizei o0 = Offset&HRIR_MASK;
const ALsizei o1 = (Offset+IrSize-1)&HRIR_MASK;
__m128 imp0, imp1;
coeffs = _mm_load_ps(&Coeffs[0][0]);
vals = _mm_loadl_pi(vals, (__m64*)&Values[o0][0]);
imp0 = _mm_mul_ps(lrlr, coeffs);
vals = _mm_add_ps(imp0, vals);
_mm_storel_pi((__m64*)&Values[o0][0], vals);
for(i = 1;i < IrSize-1;i += 2)
{
const ALsizei o2 = (Offset+i)&HRIR_MASK;
coeffs = _mm_load_ps(&Coeffs[i+1][0]);
vals = _mm_load_ps(&Values[o2][0]);
imp1 = _mm_mul_ps(lrlr, coeffs);
imp0 = _mm_shuffle_ps(imp0, imp1, _MM_SHUFFLE(1, 0, 3, 2));
vals = _mm_add_ps(imp0, vals);
_mm_store_ps(&Values[o2][0], vals);
imp0 = imp1;
}
vals = _mm_loadl_pi(vals, (__m64*)&Values[o1][0]);
imp0 = _mm_movehl_ps(imp0, imp0);
vals = _mm_add_ps(imp0, vals);
_mm_storel_pi((__m64*)&Values[o1][0], vals);
}
else
{
for(i = 0;i < IrSize;i += 2)
{
const ALsizei o = (Offset + i)&HRIR_MASK;
coeffs = _mm_load_ps(&Coeffs[i][0]);
vals = _mm_load_ps(&Values[o][0]);
vals = _mm_add_ps(vals, _mm_mul_ps(lrlr, coeffs));
_mm_store_ps(&Values[o][0], vals);
}
}
}
#define MixHrtf MixHrtf_SSE
#define MixHrtfBlend MixHrtfBlend_SSE
#define MixDirectHrtf MixDirectHrtf_SSE
#include "mixer_inc.c"
#undef MixHrtf
void Mix_SSE(const ALfloat *data, ALsizei OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
ALfloat *CurrentGains, const ALfloat *TargetGains, ALsizei Counter, ALsizei OutPos,
ALsizei BufferSize)
{
ALfloat gain, delta, step;
__m128 gain4;
ALsizei c;
delta = (Counter > 0) ? 1.0f/(ALfloat)Counter : 0.0f;
for(c = 0;c < OutChans;c++)
{
ALsizei pos = 0;
gain = CurrentGains[c];
step = (TargetGains[c] - gain) * delta;
if(fabsf(step) > FLT_EPSILON)
{
ALsizei minsize = mini(BufferSize, Counter);
/* Mix with applying gain steps in aligned multiples of 4. */
if(minsize-pos > 3)
{
__m128 step4;
gain4 = _mm_setr_ps(
gain,
gain + step,
gain + step + step,
gain + step + step + step
);
step4 = _mm_set1_ps(step + step + step + step);
do {
const __m128 val4 = _mm_load_ps(&data[pos]);
__m128 dry4 = _mm_load_ps(&OutBuffer[c][OutPos+pos]);
dry4 = _mm_add_ps(dry4, _mm_mul_ps(val4, gain4));
gain4 = _mm_add_ps(gain4, step4);
_mm_store_ps(&OutBuffer[c][OutPos+pos], dry4);
pos += 4;
} while(minsize-pos > 3);
/* NOTE: gain4 now represents the next four gains after the
* last four mixed samples, so the lowest element represents
* the next gain to apply.
*/
gain = _mm_cvtss_f32(gain4);
}
/* Mix with applying left over gain steps that aren't aligned multiples of 4. */
for(;pos < minsize;pos++)
{
OutBuffer[c][OutPos+pos] += data[pos]*gain;
gain += step;
}
if(pos == Counter)
gain = TargetGains[c];
CurrentGains[c] = gain;
/* Mix until pos is aligned with 4 or the mix is done. */
minsize = mini(BufferSize, (pos+3)&~3);
for(;pos < minsize;pos++)
OutBuffer[c][OutPos+pos] += data[pos]*gain;
}
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
continue;
gain4 = _mm_set1_ps(gain);
for(;BufferSize-pos > 3;pos += 4)
{
const __m128 val4 = _mm_load_ps(&data[pos]);
__m128 dry4 = _mm_load_ps(&OutBuffer[c][OutPos+pos]);
dry4 = _mm_add_ps(dry4, _mm_mul_ps(val4, gain4));
_mm_store_ps(&OutBuffer[c][OutPos+pos], dry4);
}
for(;pos < BufferSize;pos++)
OutBuffer[c][OutPos+pos] += data[pos]*gain;
}
}
void MixRow_SSE(ALfloat *OutBuffer, const ALfloat *Gains, const ALfloat (*restrict data)[BUFFERSIZE], 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;
}
}
+82
View File
@@ -0,0 +1,82 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 2014 by Timothy Arceri <t_arceri@yahoo.com.au>.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include <xmmintrin.h>
#include <emmintrin.h>
#include "alu.h"
#include "mixer_defs.h"
const ALfloat *Resample_lerp32_SSE2(const 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);
union { alignas(16) ALint i[4]; float f[4]; } pos_;
union { alignas(16) ALsizei i[4]; float f[4]; } frac_;
__m128i frac4, pos4;
ALint pos;
ALsizei i;
InitiatePositionArrays(frac, increment, frac_.i, pos_.i, 4);
frac4 = _mm_castps_si128(_mm_load_ps(frac_.f));
pos4 = _mm_castps_si128(_mm_load_ps(pos_.f));
for(i = 0;numsamples-i > 3;i += 4)
{
const __m128 val1 = _mm_setr_ps(src[pos_.i[0]], src[pos_.i[1]], src[pos_.i[2]], src[pos_.i[3]]);
const __m128 val2 = _mm_setr_ps(src[pos_.i[0]+1], src[pos_.i[1]+1], src[pos_.i[2]+1], src[pos_.i[3]+1]);
/* val1 + (val2-val1)*mu */
const __m128 r0 = _mm_sub_ps(val2, val1);
const __m128 mu = _mm_mul_ps(_mm_cvtepi32_ps(frac4), fracOne4);
const __m128 out = _mm_add_ps(val1, _mm_mul_ps(mu, r0));
_mm_store_ps(&dst[i], out);
frac4 = _mm_add_epi32(frac4, increment4);
pos4 = _mm_add_epi32(pos4, _mm_srli_epi32(frac4, FRACTIONBITS));
frac4 = _mm_and_si128(frac4, fracMask4);
_mm_store_ps(pos_.f, _mm_castsi128_ps(pos4));
}
/* NOTE: These four elements represent the position *after* the last four
* samples, so the lowest element is the next position to resample.
*/
pos = pos_.i[0];
frac = _mm_cvtsi128_si32(frac4);
for(;i < numsamples;i++)
{
dst[i] = lerp(src[pos], src[pos+1], frac * (1.0f/FRACTIONONE));
frac += increment;
pos += frac>>FRACTIONBITS;
frac &= FRACTIONMASK;
}
return dst;
}
+97
View File
@@ -0,0 +1,97 @@
/**
* OpenAL cross platform audio library, SSE3 mixer functions
*
* Copyright (C) 2014 by Timothy Arceri <t_arceri@yahoo.com.au>.
* Copyright (C) 2015 by Chris Robinson <chris.kcat@gmail.com>.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include <xmmintrin.h>
#include <emmintrin.h>
#include <pmmintrin.h>
#include "alu.h"
#include "mixer_defs.h"
const ALfloat *Resample_fir4_32_SSE3(const 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);
union { alignas(16) ALint i[4]; float f[4]; } pos_;
union { alignas(16) ALsizei i[4]; float f[4]; } frac_;
__m128i frac4, pos4;
ALint pos;
ALsizei i;
InitiatePositionArrays(frac, increment, frac_.i, pos_.i, 4);
frac4 = _mm_castps_si128(_mm_load_ps(frac_.f));
pos4 = _mm_castps_si128(_mm_load_ps(pos_.f));
--src;
for(i = 0;numsamples-i > 3;i += 4)
{
const __m128 val0 = _mm_loadu_ps(&src[pos_.i[0]]);
const __m128 val1 = _mm_loadu_ps(&src[pos_.i[1]]);
const __m128 val2 = _mm_loadu_ps(&src[pos_.i[2]]);
const __m128 val3 = _mm_loadu_ps(&src[pos_.i[3]]);
__m128 k0 = _mm_load_ps(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);
k1 = _mm_mul_ps(k1, val1);
k2 = _mm_mul_ps(k2, val2);
k3 = _mm_mul_ps(k3, val3);
k0 = _mm_hadd_ps(k0, k1);
k2 = _mm_hadd_ps(k2, k3);
out = _mm_hadd_ps(k0, k2);
_mm_store_ps(&dst[i], out);
frac4 = _mm_add_epi32(frac4, increment4);
pos4 = _mm_add_epi32(pos4, _mm_srli_epi32(frac4, FRACTIONBITS));
frac4 = _mm_and_si128(frac4, fracMask4);
_mm_store_ps(pos_.f, _mm_castsi128_ps(pos4));
_mm_store_ps(frac_.f, _mm_castsi128_ps(frac4));
}
/* NOTE: These four elements represent the position *after* the last four
* samples, so the lowest element is the next position to resample.
*/
pos = pos_.i[0];
frac = frac_.i[0];
for(;i < numsamples;i++)
{
dst[i] = resample_fir4(src[pos], src[pos+1], src[pos+2], src[pos+3], frac);
frac += increment;
pos += frac>>FRACTIONBITS;
frac &= FRACTIONMASK;
}
return dst;
}
+154
View File
@@ -0,0 +1,154 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 2014 by Timothy Arceri <t_arceri@yahoo.com.au>.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include <xmmintrin.h>
#include <emmintrin.h>
#include <smmintrin.h>
#include "alu.h"
#include "mixer_defs.h"
const ALfloat *Resample_lerp32_SSE41(const 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);
union { alignas(16) ALint i[4]; float f[4]; } pos_;
union { alignas(16) ALsizei i[4]; float f[4]; } frac_;
__m128i frac4, pos4;
ALint pos;
ALsizei i;
InitiatePositionArrays(frac, increment, frac_.i, pos_.i, 4);
frac4 = _mm_castps_si128(_mm_load_ps(frac_.f));
pos4 = _mm_castps_si128(_mm_load_ps(pos_.f));
for(i = 0;numsamples-i > 3;i += 4)
{
const __m128 val1 = _mm_setr_ps(src[pos_.i[0]], src[pos_.i[1]], src[pos_.i[2]], src[pos_.i[3]]);
const __m128 val2 = _mm_setr_ps(src[pos_.i[0]+1], src[pos_.i[1]+1], src[pos_.i[2]+1], src[pos_.i[3]+1]);
/* val1 + (val2-val1)*mu */
const __m128 r0 = _mm_sub_ps(val2, val1);
const __m128 mu = _mm_mul_ps(_mm_cvtepi32_ps(frac4), fracOne4);
const __m128 out = _mm_add_ps(val1, _mm_mul_ps(mu, r0));
_mm_store_ps(&dst[i], out);
frac4 = _mm_add_epi32(frac4, increment4);
pos4 = _mm_add_epi32(pos4, _mm_srli_epi32(frac4, FRACTIONBITS));
frac4 = _mm_and_si128(frac4, fracMask4);
pos_.i[0] = _mm_extract_epi32(pos4, 0);
pos_.i[1] = _mm_extract_epi32(pos4, 1);
pos_.i[2] = _mm_extract_epi32(pos4, 2);
pos_.i[3] = _mm_extract_epi32(pos4, 3);
}
/* NOTE: These four elements represent the position *after* the last four
* samples, so the lowest element is the next position to resample.
*/
pos = pos_.i[0];
frac = _mm_cvtsi128_si32(frac4);
for(;i < numsamples;i++)
{
dst[i] = lerp(src[pos], src[pos+1], frac * (1.0f/FRACTIONONE));
frac += increment;
pos += frac>>FRACTIONBITS;
frac &= FRACTIONMASK;
}
return dst;
}
const ALfloat *Resample_fir4_32_SSE41(const 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);
union { alignas(16) ALint i[4]; float f[4]; } pos_;
union { alignas(16) ALsizei i[4]; float f[4]; } frac_;
__m128i frac4, pos4;
ALint pos;
ALsizei i;
InitiatePositionArrays(frac, increment, frac_.i, pos_.i, 4);
frac4 = _mm_castps_si128(_mm_load_ps(frac_.f));
pos4 = _mm_castps_si128(_mm_load_ps(pos_.f));
--src;
for(i = 0;numsamples-i > 3;i += 4)
{
const __m128 val0 = _mm_loadu_ps(&src[pos_.i[0]]);
const __m128 val1 = _mm_loadu_ps(&src[pos_.i[1]]);
const __m128 val2 = _mm_loadu_ps(&src[pos_.i[2]]);
const __m128 val3 = _mm_loadu_ps(&src[pos_.i[3]]);
__m128 k0 = _mm_load_ps(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);
k1 = _mm_mul_ps(k1, val1);
k2 = _mm_mul_ps(k2, val2);
k3 = _mm_mul_ps(k3, val3);
k0 = _mm_hadd_ps(k0, k1);
k2 = _mm_hadd_ps(k2, k3);
out = _mm_hadd_ps(k0, k2);
_mm_store_ps(&dst[i], out);
frac4 = _mm_add_epi32(frac4, increment4);
pos4 = _mm_add_epi32(pos4, _mm_srli_epi32(frac4, FRACTIONBITS));
frac4 = _mm_and_si128(frac4, fracMask4);
pos_.i[0] = _mm_extract_epi32(pos4, 0);
pos_.i[1] = _mm_extract_epi32(pos4, 1);
pos_.i[2] = _mm_extract_epi32(pos4, 2);
pos_.i[3] = _mm_extract_epi32(pos4, 3);
frac_.i[0] = _mm_extract_epi32(frac4, 0);
frac_.i[1] = _mm_extract_epi32(frac4, 1);
frac_.i[2] = _mm_extract_epi32(frac4, 2);
frac_.i[3] = _mm_extract_epi32(frac4, 3);
}
pos = pos_.i[0];
frac = frac_.i[0];
for(;i < numsamples;i++)
{
dst[i] = resample_fir4(src[pos], src[pos+1], src[pos+2], src[pos+3], frac);
frac += increment;
pos += frac>>FRACTIONBITS;
frac &= FRACTIONMASK;
}
return dst;
}
+418
View File
@@ -0,0 +1,418 @@
#include "config.h"
#include "nfcfilter.h"
#include "alu.h"
/* Near-field control filters are the basis for handling the near-field effect.
* The near-field effect is a bass-boost present in the directional components
* of a recorded signal, created as a result of the wavefront curvature (itself
* a function of sound distance). Proper reproduction dictates this be
* compensated for using a bass-cut given the playback speaker distance, to
* avoid excessive bass in the playback.
*
* For real-time rendered audio, emulating the near-field effect based on the
* sound source's distance, and subsequently compensating for it at output
* based on the speaker distances, can create a more realistic perception of
* sound distance beyond a simple 1/r attenuation.
*
* These filters do just that. Each one applies a low-shelf filter, created as
* the combination of a bass-boost for a given sound source distance (near-
* field emulation) along with a bass-cut for a given control/speaker distance
* (near-field compensation).
*
* Note that it is necessary to apply a cut along with the boost, since the
* boost alone is unstable in higher-order ambisonics as it causes an infinite
* DC gain (even first-order ambisonics requires there to be no DC offset for
* the boost to work). Consequently, ambisonics requires a control parameter to
* be used to avoid an unstable boost-only filter. NFC-HOA defines this control
* as a reference delay, calculated with:
*
* reference_delay = control_distance / speed_of_sound
*
* This means w0 (for input) or w1 (for output) should be set to:
*
* wN = 1 / (reference_delay * sample_rate)
*
* when dealing with NFC-HOA content. For FOA input content, which does not
* specify a reference_delay variable, w0 should be set to 0 to apply only
* near-field compensation for output. It's important that w1 be a finite,
* positive, non-0 value or else the bass-boost will become unstable again.
* Also, w0 should not be too large compared to w1, to avoid excessively loud
* low frequencies.
*/
static const float B[4][3] = {
{ 0.0f },
{ 1.0f },
{ 3.0f, 3.0f },
{ 3.6778f, 6.4595f, 2.3222f },
/*{ 4.2076f, 11.4877f, 5.7924f, 9.1401f }*/
};
void NfcFilterCreate1(NfcFilter *nfc, const float w0, const float w1)
{
float b_00, g_0;
float r;
memset(nfc, 0, sizeof(*nfc));
nfc->g = 1.0f;
nfc->coeffs[0] = 1.0f;
/* Calculate bass-boost coefficients. */
r = 0.5f * w0;
b_00 = B[1][0] * r;
g_0 = 1.0f + b_00;
nfc->coeffs[0] *= g_0;
nfc->coeffs[1] = (2.0f * b_00) / g_0;
/* Calculate bass-cut coefficients. */
r = 0.5f * w1;
b_00 = B[1][0] * r;
g_0 = 1.0f + b_00;
nfc->g /= g_0;
nfc->coeffs[0] /= g_0;
nfc->coeffs[1+1] = (2.0f * b_00) / g_0;
}
void NfcFilterAdjust1(NfcFilter *nfc, const float w0)
{
float b_00, g_0;
float r;
r = 0.5f * w0;
b_00 = B[1][0] * r;
g_0 = 1.0f + b_00;
nfc->coeffs[0] = nfc->g * g_0;
nfc->coeffs[1] = (2.0f * b_00) / g_0;
}
void NfcFilterUpdate1(NfcFilter *nfc, ALfloat *restrict dst, const float *restrict src, const int count)
{
const float b0 = nfc->coeffs[0];
const float a0 = nfc->coeffs[1];
const float a1 = nfc->coeffs[2];
float z1 = nfc->history[0];
int i;
for(i = 0;i < count;i++)
{
float out = src[i] * b0;
float y;
y = out - (a1*z1);
out = y + (a0*z1);
z1 += y;
dst[i] = out;
}
nfc->history[0] = z1;
}
void NfcFilterCreate2(NfcFilter *nfc, const float w0, const float w1)
{
float b_10, b_11, g_1;
float r;
memset(nfc, 0, sizeof(*nfc));
nfc->g = 1.0f;
nfc->coeffs[0] = 1.0f;
/* Calculate bass-boost coefficients. */
r = 0.5f * w0;
b_10 = B[2][0] * r;
b_11 = B[2][1] * r * r;
g_1 = 1.0f + b_10 + b_11;
nfc->coeffs[0] *= g_1;
nfc->coeffs[1] = ((2.0f * b_10) + (4.0f * b_11)) / g_1;
nfc->coeffs[2] = (4.0f * b_11) / g_1;
/* Calculate bass-cut coefficients. */
r = 0.5f * w1;
b_10 = B[2][0] * r;
b_11 = B[2][1] * r * r;
g_1 = 1.0f + b_10 + b_11;
nfc->g /= g_1;
nfc->coeffs[0] /= g_1;
nfc->coeffs[2+1] = ((2.0f * b_10) + (4.0f * b_11)) / g_1;
nfc->coeffs[2+2] = (4.0f * b_11) / g_1;
}
void NfcFilterAdjust2(NfcFilter *nfc, const float w0)
{
float b_10, b_11, g_1;
float r;
r = 0.5f * w0;
b_10 = B[2][0] * r;
b_11 = B[2][1] * r * r;
g_1 = 1.0f + b_10 + b_11;
nfc->coeffs[0] = nfc->g * g_1;
nfc->coeffs[1] = ((2.0f * b_10) + (4.0f * b_11)) / g_1;
nfc->coeffs[2] = (4.0f * b_11) / g_1;
}
void NfcFilterUpdate2(NfcFilter *nfc, ALfloat *restrict dst, const float *restrict src, const int count)
{
const float b0 = nfc->coeffs[0];
const float a00 = nfc->coeffs[1];
const float a01 = nfc->coeffs[2];
const float a10 = nfc->coeffs[3];
const float a11 = nfc->coeffs[4];
float z1 = nfc->history[0];
float z2 = nfc->history[1];
int i;
for(i = 0;i < count;i++)
{
float out = src[i] * b0;
float y;
y = out - (a10*z1) - (a11*z2);
out = y + (a00*z1) + (a01*z2);
z2 += z1;
z1 += y;
dst[i] = out;
}
nfc->history[0] = z1;
nfc->history[1] = z2;
}
void NfcFilterCreate3(NfcFilter *nfc, const float w0, const float w1)
{
float b_10, b_11, g_1;
float b_00, g_0;
float r;
memset(nfc, 0, sizeof(*nfc));
nfc->g = 1.0f;
nfc->coeffs[0] = 1.0f;
/* Calculate bass-boost coefficients. */
r = 0.5f * w0;
b_10 = B[3][0] * r;
b_11 = B[3][1] * r * r;
g_1 = 1.0f + b_10 + b_11;
nfc->coeffs[0] *= g_1;
nfc->coeffs[1] = ((2.0f * b_10) + (4.0f * b_11)) / g_1;
nfc->coeffs[2] = (4.0f * b_11) / g_1;
b_00 = B[3][2] * r;
g_0 = 1.0f + b_00;
nfc->coeffs[0] *= g_0;
nfc->coeffs[2+1] = (2.0f * b_00) / g_0;
/* Calculate bass-cut coefficients. */
r = 0.5f * w1;
b_10 = B[3][0] * r;
b_11 = B[3][1] * r * r;
g_1 = 1.0f + b_10 + b_11;
nfc->g /= g_1;
nfc->coeffs[0] /= g_1;
nfc->coeffs[3+1] = ((2.0f * b_10) + (4.0f * b_11)) / g_1;
nfc->coeffs[3+2] = (4.0f * b_11) / g_1;
b_00 = B[3][2] * r;
g_0 = 1.0f + b_00;
nfc->g /= g_0;
nfc->coeffs[0] /= g_0;
nfc->coeffs[3+2+1] = (2.0f * b_00) / g_0;
}
void NfcFilterAdjust3(NfcFilter *nfc, const float w0)
{
float b_10, b_11, g_1;
float b_00, g_0;
float r;
r = 0.5f * w0;
b_10 = B[3][0] * r;
b_11 = B[3][1] * r * r;
g_1 = 1.0f + b_10 + b_11;
nfc->coeffs[0] = nfc->g * g_1;
nfc->coeffs[1] = ((2.0f * b_10) + (4.0f * b_11)) / g_1;
nfc->coeffs[2] = (4.0f * b_11) / g_1;
b_00 = B[3][2] * r;
g_0 = 1.0f + b_00;
nfc->coeffs[0] *= g_0;
nfc->coeffs[2+1] = (2.0f * b_00) / g_0;
}
void NfcFilterUpdate3(NfcFilter *nfc, ALfloat *restrict dst, const float *restrict src, const int count)
{
const float b0 = nfc->coeffs[0];
const float a00 = nfc->coeffs[1];
const float a01 = nfc->coeffs[2];
const float a02 = nfc->coeffs[3];
const float a10 = nfc->coeffs[4];
const float a11 = nfc->coeffs[5];
const float a12 = nfc->coeffs[6];
float z1 = nfc->history[0];
float z2 = nfc->history[1];
float z3 = nfc->history[2];
int i;
for(i = 0;i < count;i++)
{
float out = src[i] * b0;
float y;
y = out - (a10*z1) - (a11*z2);
out = y + (a00*z1) + (a01*z2);
z2 += z1;
z1 += y;
y = out - (a12*z3);
out = y + (a02*z3);
z3 += y;
dst[i] = out;
}
nfc->history[0] = z1;
nfc->history[1] = z2;
nfc->history[2] = z3;
}
#if 0 /* Original methods the above are derived from. */
static void NfcFilterCreate(NfcFilter *nfc, const ALsizei order, const float src_dist, const float ctl_dist, const float rate)
{
static const float B[4][5] = {
{ },
{ 1.0f },
{ 3.0f, 3.0f },
{ 3.6778f, 6.4595f, 2.3222f },
{ 4.2076f, 11.4877f, 5.7924f, 9.1401f }
};
float w0 = SPEEDOFSOUNDMETRESPERSEC / (src_dist * rate);
float w1 = SPEEDOFSOUNDMETRESPERSEC / (ctl_dist * rate);
ALsizei i;
float r;
nfc->g = 1.0f;
nfc->coeffs[0] = 1.0f;
/* NOTE: Slight adjustment from the literature to raise the center
* frequency a bit (0.5 -> 1.0).
*/
r = 1.0f * w0;
for(i = 0; i < (order-1);i += 2)
{
float b_10 = B[order][i ] * r;
float b_11 = B[order][i+1] * r * r;
float g_1 = 1.0f + b_10 + b_11;
nfc->b[i] = b_10;
nfc->b[i + 1] = b_11;
nfc->coeffs[0] *= g_1;
nfc->coeffs[i+1] = ((2.0f * b_10) + (4.0f * b_11)) / g_1;
nfc->coeffs[i+2] = (4.0f * b_11) / g_1;
}
if(i < order)
{
float b_00 = B[order][i] * r;
float g_0 = 1.0f + b_00;
nfc->b[i] = b_00;
nfc->coeffs[0] *= g_0;
nfc->coeffs[i+1] = (2.0f * b_00) / g_0;
}
r = 1.0f * w1;
for(i = 0;i < (order-1);i += 2)
{
float b_10 = B[order][i ] * r;
float b_11 = B[order][i+1] * r * r;
float g_1 = 1.0f + b_10 + b_11;
nfc->g /= g_1;
nfc->coeffs[0] /= g_1;
nfc->coeffs[order+i+1] = ((2.0f * b_10) + (4.0f * b_11)) / g_1;
nfc->coeffs[order+i+2] = (4.0f * b_11) / g_1;
}
if(i < order)
{
float b_00 = B[order][i] * r;
float g_0 = 1.0f + b_00;
nfc->g /= g_0;
nfc->coeffs[0] /= g_0;
nfc->coeffs[order+i+1] = (2.0f * b_00) / g_0;
}
for(i = 0; i < MAX_AMBI_ORDER; i++)
nfc->history[i] = 0.0f;
}
static void NfcFilterAdjust(NfcFilter *nfc, const float distance)
{
int i;
nfc->coeffs[0] = nfc->g;
for(i = 0;i < (nfc->order-1);i += 2)
{
float b_10 = nfc->b[i] / distance;
float b_11 = nfc->b[i+1] / (distance * distance);
float g_1 = 1.0f + b_10 + b_11;
nfc->coeffs[0] *= g_1;
nfc->coeffs[i+1] = ((2.0f * b_10) + (4.0f * b_11)) / g_1;
nfc->coeffs[i+2] = (4.0f * b_11) / g_1;
}
if(i < nfc->order)
{
float b_00 = nfc->b[i] / distance;
float g_0 = 1.0f + b_00;
nfc->coeffs[0] *= g_0;
nfc->coeffs[i+1] = (2.0f * b_00) / g_0;
}
}
static float NfcFilterUpdate(const float in, NfcFilter *nfc)
{
int i;
float out = in * nfc->coeffs[0];
for(i = 0;i < (nfc->order-1);i += 2)
{
float y = out - (nfc->coeffs[nfc->order+i+1] * nfc->history[i]) -
(nfc->coeffs[nfc->order+i+2] * nfc->history[i+1]) + 1.0e-30f;
out = y + (nfc->coeffs[i+1]*nfc->history[i]) + (nfc->coeffs[i+2]*nfc->history[i+1]);
nfc->history[i+1] += nfc->history[i];
nfc->history[i] += y;
}
if(i < nfc->order)
{
float y = out - (nfc->coeffs[nfc->order+i+1] * nfc->history[i]) + 1.0e-30f;
out = y + (nfc->coeffs[i+1] * nfc->history[i]);
nfc->history[i] += y;
}
return out;
}
#endif
+37
View File
@@ -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 */
+1271
View File
File diff suppressed because it is too large Load Diff
+134
View File
@@ -0,0 +1,134 @@
#include "config.h"
#include "alu.h"
#include "uhjfilter.h"
/* This is the maximum number of samples processed for each inner loop
* iteration. */
#define MAX_UPDATE_SAMPLES 128
static const ALfloat Filter1Coeff[4] = {
0.6923878f, 0.9360654322959f, 0.9882295226860f, 0.9987488452737f
};
static const ALfloat Filter2Coeff[4] = {
0.4021921162426f, 0.8561710882420f, 0.9722909545651f, 0.9952884791278f
};
static void allpass_process(AllPassState *state, ALfloat *restrict dst, const ALfloat *restrict src, const ALfloat aa, ALsizei todo)
{
ALsizei i;
if(todo > 1)
{
dst[0] = aa*(src[0] + state->y[1]) - state->x[1];
dst[1] = aa*(src[1] + state->y[0]) - state->x[0];
for(i = 2;i < todo;i++)
dst[i] = aa*(src[i] + dst[i-2]) - src[i-2];
state->x[1] = src[i-2];
state->x[0] = src[i-1];
state->y[1] = dst[i-2];
state->y[0] = dst[i-1];
}
else if(todo == 1)
{
dst[0] = aa*(src[0] + state->y[1]) - state->x[1];
state->x[1] = state->x[0];
state->x[0] = src[0];
state->y[1] = state->y[0];
state->y[0] = dst[0];
}
}
/* NOTE: There seems to be a bit of an inconsistency in how this encoding is
* supposed to work. Some references, such as
*
* http://members.tripod.com/martin_leese/Ambisonic/UHJ_file_format.html
*
* specify a pre-scaling of sqrt(2) on the W channel input, while other
* references, such as
*
* https://en.wikipedia.org/wiki/Ambisonic_UHJ_format#Encoding.5B1.5D
* and
* https://wiki.xiph.org/Ambisonics#UHJ_format
*
* do not. The sqrt(2) scaling is in line with B-Format decoder coefficients
* which include such a scaling for the W channel input, however the original
* source for this equation is a 1985 paper by Michael Gerzon, which does not
* apparently include the scaling. Applying the extra scaling creates a louder
* result with a narrower stereo image compared to not scaling, and I don't
* know which is the intended result.
*/
void EncodeUhj2(Uhj2Encoder *enc, ALfloat *restrict LeftOut, ALfloat *restrict RightOut, ALfloat (*restrict InSamples)[BUFFERSIZE], ALsizei SamplesToDo)
{
ALfloat D[MAX_UPDATE_SAMPLES], S[MAX_UPDATE_SAMPLES];
ALfloat temp[2][MAX_UPDATE_SAMPLES];
ALsizei base, i;
for(base = 0;base < SamplesToDo;)
{
ALsizei todo = mini(SamplesToDo - base, MAX_UPDATE_SAMPLES);
/* D = 0.6554516*Y */
for(i = 0;i < todo;i++)
temp[0][i] = 0.6554516f*InSamples[2][base+i];
allpass_process(&enc->Filter1_Y[0], temp[1], temp[0],
Filter1Coeff[0]*Filter1Coeff[0], todo);
allpass_process(&enc->Filter1_Y[1], temp[0], temp[1],
Filter1Coeff[1]*Filter1Coeff[1], todo);
allpass_process(&enc->Filter1_Y[2], temp[1], temp[0],
Filter1Coeff[2]*Filter1Coeff[2], todo);
/* NOTE: Filter1 requires a 1 sample delay for the final output, so
* take the last processed sample from the previous run as the first
* output sample.
*/
D[0] = enc->Filter1_Y[3].y[0];
allpass_process(&enc->Filter1_Y[3], temp[0], temp[1],
Filter1Coeff[3]*Filter1Coeff[3], todo);
for(i = 1;i < todo;i++)
D[i] = temp[0][i-1];
/* D += j(-0.3420201*W + 0.5098604*X) */
for(i = 0;i < todo;i++)
temp[0][i] = -0.3420201f*InSamples[0][base+i] +
0.5098604f*InSamples[1][base+i];
allpass_process(&enc->Filter2_WX[0], temp[1], temp[0],
Filter2Coeff[0]*Filter2Coeff[0], todo);
allpass_process(&enc->Filter2_WX[1], temp[0], temp[1],
Filter2Coeff[1]*Filter2Coeff[1], todo);
allpass_process(&enc->Filter2_WX[2], temp[1], temp[0],
Filter2Coeff[2]*Filter2Coeff[2], todo);
allpass_process(&enc->Filter2_WX[3], temp[0], temp[1],
Filter2Coeff[3]*Filter2Coeff[3], todo);
for(i = 0;i < todo;i++)
D[i] += temp[0][i];
/* S = 0.9396926*W + 0.1855740*X */
for(i = 0;i < todo;i++)
temp[0][i] = 0.9396926f*InSamples[0][base+i] +
0.1855740f*InSamples[1][base+i];
allpass_process(&enc->Filter1_WX[0], temp[1], temp[0],
Filter1Coeff[0]*Filter1Coeff[0], todo);
allpass_process(&enc->Filter1_WX[1], temp[0], temp[1],
Filter1Coeff[1]*Filter1Coeff[1], todo);
allpass_process(&enc->Filter1_WX[2], temp[1], temp[0],
Filter1Coeff[2]*Filter1Coeff[2], todo);
S[0] = enc->Filter1_WX[3].y[0];
allpass_process(&enc->Filter1_WX[3], temp[0], temp[1],
Filter1Coeff[3]*Filter1Coeff[3], todo);
for(i = 1;i < todo;i++)
S[i] = temp[0][i-1];
/* Left = (S + D)/2.0 */
for(i = 0;i < todo;i++)
*(LeftOut++) += (S[i] + D[i]) * 0.5f;
/* Right = (S - D)/2.0 */
for(i = 0;i < todo;i++)
*(RightOut++) += (S[i] - D[i]) * 0.5f;
base += todo;
}
}
+49
View File
@@ -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 */
+93
View File
@@ -0,0 +1,93 @@
#ifndef AL_VECTOR_H
#define AL_VECTOR_H
#include <stdlib.h>
#include <AL/al.h>
#include "almalloc.h"
#define TYPEDEF_VECTOR(T, N) typedef struct { \
size_t Capacity; \
size_t Size; \
T Data[]; \
} _##N; \
typedef _##N* N; \
typedef const _##N* const_##N;
#define VECTOR(T) struct { \
size_t Capacity; \
size_t Size; \
T Data[]; \
}*
#define VECTOR_INIT(_x) do { (_x) = NULL; } while(0)
#define VECTOR_INIT_STATIC() NULL
#define VECTOR_DEINIT(_x) do { al_free((_x)); (_x) = NULL; } while(0)
#define VECTOR_RESIZE(_x, _s, _c) do { \
size_t _size = (_s); \
size_t _cap = (_c); \
if(_size > _cap) \
_cap = _size; \
\
if(!(_x) && _cap == 0) \
break; \
\
if(((_x) ? (_x)->Capacity : 0) < _cap) \
{ \
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_BEGIN(_x) ((_x) ? (_x)->Data + 0 : NULL)
#define VECTOR_END(_x) ((_x) ? (_x)->Data + (_x)->Size : NULL)
#define VECTOR_PUSH_BACK(_x, _obj) do { \
size_t _pbsize = VECTOR_SIZE(_x)+1; \
VECTOR_RESIZE(_x, _pbsize, _pbsize); \
(_x)->Data[(_x)->Size-1] = (_obj); \
} while(0)
#define VECTOR_POP_BACK(_x) ((void)((_x)->Size--))
#define VECTOR_BACK(_x) ((_x)->Data[(_x)->Size-1])
#define VECTOR_FRONT(_x) ((_x)->Data[0])
#define VECTOR_ELEM(_x, _o) ((_x)->Data[(_o)])
#define VECTOR_FOR_EACH(_t, _x, _f) do { \
_t *_iter = VECTOR_BEGIN((_x)); \
_t *_end = VECTOR_END((_x)); \
for(;_iter != _end;++_iter) \
_f(_iter); \
} while(0)
#define VECTOR_FIND_IF(_i, _t, _x, _f) do { \
_t *_iter = VECTOR_BEGIN((_x)); \
_t *_end = VECTOR_END((_x)); \
for(;_iter != _end;++_iter) \
{ \
if(_f(_iter)) \
break; \
} \
(_i) = _iter; \
} while(0)
#endif /* AL_VECTOR_H */
+1573
View File
File diff suppressed because it is too large Load Diff
+481
View File
@@ -0,0 +1,481 @@
GNU LIBRARY GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1991 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the library GPL. It is
numbered 2 because it goes with version 2 of the ordinary GPL.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Library General Public License, applies to some
specially designated Free Software Foundation software, and to any
other libraries whose authors decide to use it. You can use it for
your libraries, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if
you distribute copies of the library, or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link a program with the library, you must provide
complete object files to the recipients so that they can relink them
with the library, after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
Our method of protecting your rights has two steps: (1) copyright
the library, and (2) offer you this license which gives you legal
permission to copy, distribute and/or modify the library.
Also, for each distributor's protection, we want to make certain
that everyone understands that there is no warranty for this free
library. If the library is modified by someone else and passed on, we
want its recipients to know that what they have is not the original
version, so that any problems introduced by others will not reflect on
the original authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that companies distributing free
software will individually obtain patent licenses, thus in effect
transforming the program into proprietary software. To prevent this,
we have made it clear that any patent must be licensed for everyone's
free use or not licensed at all.
Most GNU software, including some libraries, is covered by the ordinary
GNU General Public License, which was designed for utility programs. This
license, the GNU Library General Public License, applies to certain
designated libraries. This license is quite different from the ordinary
one; be sure to read it in full, and don't assume that anything in it is
the same as in the ordinary license.
The reason we have a separate public license for some libraries is that
they blur the distinction we usually make between modifying or adding to a
program and simply using it. Linking a program with a library, without
changing the library, is in some sense simply using the library, and is
analogous to running a utility program or application program. However, in
a textual and legal sense, the linked executable is a combined work, a
derivative of the original library, and the ordinary General Public License
treats it as such.
Because of this blurred distinction, using the ordinary General
Public License for libraries did not effectively promote software
sharing, because most developers did not use the libraries. We
concluded that weaker conditions might promote sharing better.
However, unrestricted linking of non-free programs would deprive the
users of those programs of all benefit from the free status of the
libraries themselves. This Library General Public License is intended to
permit developers of non-free programs to use free libraries, while
preserving your freedom as a user of such programs to change the free
libraries that are incorporated in them. (We have not seen how to achieve
this as regards changes in header files, but we have achieved it as regards
changes in the actual functions of the Library.) The hope is that this
will lead to faster development of free libraries.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, while the latter only
works together with the library.
Note that it is possible for a library to be covered by the ordinary
General Public License rather than by this special one.
GNU LIBRARY GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library which
contains a notice placed by the copyright holder or other authorized
party saying it may be distributed under the terms of this Library
General Public License (also called "this License"). Each licensee is
addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also compile or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
c) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
d) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the source code distributed need not include anything that is normally
distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Library General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
<one line to give the library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!
+305
View File
@@ -0,0 +1,305 @@
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.
Fixed building on OSX.
Fixed building on non-Windows systems without POSIX-2008.
Fixed Dedicated Dialog and Dedicated LFE effect output.
Added a build option to override the share install dir.
Added a build option to static-link libgcc for MinGW.
openal-soft-1.17.1:
Fixed building with JACK and without PulseAudio.
Fixed building on FreeBSD.
Fixed the ALSA backend's allow-resampler option.
Fixed handling of inexact ALSA period counts.
Altered device naming scheme on Windows backends to better match other
drivers.
Updated the CoreAudio backend to use the AudioComponent API. This clears up
deprecation warnings for OSX 10.11, although requires OSX 10.6 or newer.
openal-soft-1.17.0:
Implemented a JACK playback backend.
Implemented the AL_EXT_BFORMAT and AL_EXT_MULAW_BFORMAT extensions.
Implemented the ALC_SOFT_HRTF extension.
Implemented C, SSE3, and SSE4.1 based 4- and 8-point Sinc resamplers.
Implemented a C and SSE based band-limited Sinc resampler. This does 12- to
24-point Sinc resampling, and performs anti-aliasing.
Implemented B-Format output support for the wave file writer. This creates
FuMa-style first-order Ambisonics wave files (AMB format).
Implemented a stereo-mode config option for treating stereo modes as either
speakers or headphones.
Implemented per-device configuration options.
Fixed handling of PulseAudio and MMDevAPI devices that have identical
descriptions.
Fixed a potential lockup when stopping playback of suspended PulseAudio devices.
Fixed logging of Unicode characters on Windows.
Fixed 5.1 surround sound channels. By default it will now use the side
channels for the surround output. A configuration using rear channels is
still available.
Fixed the QSA backend potentially altering the capture format.
Fixed detecting MMDevAPI's default device.
Fixed returning the default capture device name.
Fixed mixing property calculations when deferring context updates.
Altered the behavior of alcSuspendContext and alcProcessContext to better
match certain Windows drivers.
Altered the panning algorithm, utilizing Ambisonics for better side and
back positioning cues with surround sound output.
Improved support for certain older Windows apps.
Improved the alffplay example to support surround sound streams.
Improved support for building as a sub-project.
Added an HRTF playback example.
Added a tone generator output test.
Added a toolchain to help with cross-compiling to Android.
openal-soft-1.16.0:
Implemented EFX Chorus, Flanger, Distortion, Equalizer, and Compressor
effects.
Implemented high-pass and band-pass EFX filters.
Implemented the high-pass filter for the EAXReverb effect.
Implemented SSE2 and SSE4.1 linear resamplers.
Implemented Neon-enhanced non-HRTF mixers.
Implemented a QSA backend, for QNX.
Implemented the ALC_SOFT_pause_device, AL_SOFT_deferred_updates,
AL_SOFT_block_alignment, AL_SOFT_MSADPCM, and AL_SOFT_source_length
extensions.
Fixed resetting mmdevapi backend devices.
Fixed clamping when converting 32-bit float samples to integer.
Fixed modulation range in the Modulator effect.
Several fixes for the OpenSL playback backend.
Fixed device specifier names that have Unicode characters on Windows.
Added support for filenames and paths with Unicode (UTF-8) characters on
Windows.
Added support for alsoft.conf config files found in XDG Base Directory
Specification locations (XDG_CONFIG_DIRS and XDG_CONFIG_HOME, or their
defaults) on non-Windows systems.
Added a GUI configuration utility (requires Qt 4.8).
Added support for environment variable expansion in config options (not
keys or section names).
Added an example that uses SDL2 and ffmpeg.
Modified examples to use SDL_sound.
Modified CMake config option names for better sorting.
HRTF data sets specified in the hrtf_tables config option may now be
relative or absolute filenames.
Made the default HRTF data set an external file, and added a data set for
48khz playback in addition to 44.1khz.
Added support for C11 atomic methods.
Improved support for some non-GNU build systems.
openal-soft-1.15.1:
Fixed a regression with retrieving the source's AL_GAIN property.
openal-soft-1.15:
Fixed device enumeration with the OSS backend.
Reorganized internal mixing logic, so unneeded steps can potentially be
skipped for better performance.
Removed the lookup table for calculating the mixing pans. The panning is
now calculated directly for better precision.
Improved the panning of stereo source channels when using stereo output.
Improved source filter quality on send paths.
Added a config option to allow PulseAudio to move streams between devices.
The PulseAudio backend will now attempt to spawn a server by default.
Added a workaround for a DirectSound bug relating to float32 output.
Added SSE-based mixers, for HRTF and non-HRTF mixing.
Added support for the new AL_SOFT_source_latency extension.
Improved ALSA capture by avoiding an extra buffer when using sizes
supported by the underlying device.
Improved the makehrtf utility to support new options and input formats.
Modified the CFLAGS declared in the pkg-config file so the "AL/" portion of
the header includes can optionally be omitted.
Added a couple example code programs to show how to apply reverb, and
retrieve latency.
The configuration sample is now installed into the share/openal/ directory
instead of /etc/openal.
The configuration sample now gets installed by default.
+190
View File
@@ -0,0 +1,190 @@
#ifndef _AL_AUXEFFECTSLOT_H_
#define _AL_AUXEFFECTSLOT_H_
#include "alMain.h"
#include "alEffect.h"
#include "atomic.h"
#include "align.h"
#ifdef __cplusplus
extern "C" {
#endif
struct ALeffectStateVtable;
struct ALeffectslot;
typedef struct ALeffectState {
RefCount Ref;
const struct ALeffectStateVtable *vtbl;
ALfloat (*OutBuffer)[BUFFERSIZE];
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, 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);
};
#define DEFINE_ALEFFECTSTATE_VTABLE(T) \
DECLARE_THUNK(T, ALeffectState, void, Destruct) \
DECLARE_THUNK1(T, ALeffectState, ALboolean, deviceUpdate, ALCdevice*) \
DECLARE_THUNK3(T, ALeffectState, void, update, const ALCdevice*, const ALeffectslot*, const ALeffectProps*) \
DECLARE_THUNK4(T, ALeffectState, void, process, ALsizei, const ALfloatBUFFERSIZE*restrict, ALfloatBUFFERSIZE*restrict, ALsizei) \
static void T##_ALeffectState_Delete(void *ptr) \
{ return T##_Delete(STATIC_UPCAST(T, ALeffectState, (ALeffectState*)ptr)); } \
\
static const struct ALeffectStateVtable T##_ALeffectState_vtable = { \
T##_ALeffectState_Destruct, \
\
T##_ALeffectState_deviceUpdate, \
T##_ALeffectState_update, \
T##_ALeffectState_process, \
\
T##_ALeffectState_Delete, \
}
struct ALeffectStateFactoryVtable;
typedef struct ALeffectStateFactory {
const struct ALeffectStateFactoryVtable *vtbl;
} ALeffectStateFactory;
struct ALeffectStateFactoryVtable {
ALeffectState *(*const create)(ALeffectStateFactory *factory);
};
#define DEFINE_ALEFFECTSTATEFACTORY_VTABLE(T) \
DECLARE_THUNK(T, ALeffectStateFactory, ALeffectState*, create) \
\
static const struct ALeffectStateFactoryVtable T##_ALeffectStateFactory_vtable = { \
T##_ALeffectStateFactory_create, \
}
#define MAX_EFFECT_CHANNELS (4)
struct 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 {
ALfloat Gain;
ALboolean AuxSendAuto;
struct {
ALenum Type;
ALeffectProps Props;
ALeffectState *State;
} Effect;
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*)LookupUIntMapKeyNoLock(&context->EffectSlotMap, id); }
inline struct ALeffectslot *RemoveEffectSlot(ALCcontext *context, ALuint id)
{ return (struct ALeffectslot*)RemoveUIntMapKeyNoLock(&context->EffectSlotMap, id); }
ALenum InitEffectSlot(ALeffectslot *slot);
void DeinitEffectSlot(ALeffectslot *slot);
void UpdateEffectSlotProps(ALeffectslot *slot);
void UpdateAllEffectSlotProps(ALCcontext *context);
ALvoid ReleaseALAuxiliaryEffectSlots(ALCcontext *Context);
ALeffectStateFactory *ALnullStateFactory_getFactory(void);
ALeffectStateFactory *ALreverbStateFactory_getFactory(void);
ALeffectStateFactory *ALchorusStateFactory_getFactory(void);
ALeffectStateFactory *ALcompressorStateFactory_getFactory(void);
ALeffectStateFactory *ALdistortionStateFactory_getFactory(void);
ALeffectStateFactory *ALechoStateFactory_getFactory(void);
ALeffectStateFactory *ALequalizerStateFactory_getFactory(void);
ALeffectStateFactory *ALflangerStateFactory_getFactory(void);
ALeffectStateFactory *ALmodulatorStateFactory_getFactory(void);
ALeffectStateFactory *ALdedicatedStateFactory_getFactory(void);
ALenum InitializeEffect(ALCdevice *Device, ALeffectslot *EffectSlot, ALeffect *effect);
void InitEffectFactoryMap(void);
void DeinitEffectFactoryMap(void);
#ifdef __cplusplus
}
#endif
#endif
+128
View File
@@ -0,0 +1,128 @@
#ifndef _AL_BUFFER_H_
#define _AL_BUFFER_H_
#include "alMain.h"
#ifdef __cplusplus
extern "C" {
#endif
/* User formats */
enum UserFmtType {
UserFmtByte = AL_BYTE_SOFT,
UserFmtUByte = AL_UNSIGNED_BYTE_SOFT,
UserFmtShort = AL_SHORT_SOFT,
UserFmtUShort = AL_UNSIGNED_SHORT_SOFT,
UserFmtInt = AL_INT_SOFT,
UserFmtUInt = AL_UNSIGNED_INT_SOFT,
UserFmtFloat = AL_FLOAT_SOFT,
UserFmtDouble = AL_DOUBLE_SOFT,
UserFmtMulaw = AL_MULAW_SOFT,
UserFmtAlaw = 0x10000000,
UserFmtIMA4,
UserFmtMSADPCM,
};
enum UserFmtChannels {
UserFmtMono = AL_MONO_SOFT,
UserFmtStereo = AL_STEREO_SOFT,
UserFmtRear = AL_REAR_SOFT,
UserFmtQuad = AL_QUAD_SOFT,
UserFmtX51 = AL_5POINT1_SOFT, /* (WFX order) */
UserFmtX61 = AL_6POINT1_SOFT, /* (WFX order) */
UserFmtX71 = AL_7POINT1_SOFT, /* (WFX order) */
UserFmtBFormat2D = AL_BFORMAT2D_SOFT, /* WXY */
UserFmtBFormat3D = AL_BFORMAT3D_SOFT, /* WXYZ */
};
ALsizei BytesFromUserFmt(enum UserFmtType type);
ALsizei ChannelsFromUserFmt(enum UserFmtChannels chans);
inline ALsizei FrameSizeFromUserFmt(enum UserFmtChannels chans, enum UserFmtType type)
{
return ChannelsFromUserFmt(chans) * BytesFromUserFmt(type);
}
/* Storable formats */
enum FmtType {
FmtByte = UserFmtByte,
FmtShort = UserFmtShort,
FmtFloat = UserFmtFloat,
};
enum FmtChannels {
FmtMono = UserFmtMono,
FmtStereo = UserFmtStereo,
FmtRear = UserFmtRear,
FmtQuad = UserFmtQuad,
FmtX51 = UserFmtX51,
FmtX61 = UserFmtX61,
FmtX71 = UserFmtX71,
FmtBFormat2D = UserFmtBFormat2D,
FmtBFormat3D = UserFmtBFormat3D,
};
#define MAX_INPUT_CHANNELS (8)
ALsizei BytesFromFmt(enum FmtType type);
ALsizei ChannelsFromFmt(enum FmtChannels chans);
inline ALsizei FrameSizeFromFmt(enum FmtChannels chans, enum FmtType type)
{
return ChannelsFromFmt(chans) * BytesFromFmt(type);
}
typedef struct ALbuffer {
ALvoid *data;
ALsizei Frequency;
ALenum Format;
ALsizei SampleLen;
enum FmtChannels FmtChannels;
enum FmtType FmtType;
ALuint BytesAlloc;
enum UserFmtChannels OriginalChannels;
enum UserFmtType OriginalType;
ALsizei OriginalSize;
ALsizei OriginalAlign;
ALsizei LoopStart;
ALsizei LoopEnd;
ATOMIC(ALsizei) UnpackAlign;
ATOMIC(ALsizei) PackAlign;
/* Number of times buffer was attached to a source (deletion can only occur when 0) */
RefCount ref;
RWLock lock;
/* Self ID */
ALuint id;
} ALbuffer;
ALbuffer *NewBuffer(ALCcontext *context);
void DeleteBuffer(ALCdevice *device, ALbuffer *buffer);
ALenum LoadData(ALbuffer *buffer, ALuint freq, ALenum NewFormat, ALsizei frames, enum UserFmtChannels SrcChannels, enum UserFmtType SrcType, const ALvoid *data, ALsizei align, ALboolean storesrc);
inline void LockBuffersRead(ALCdevice *device)
{ LockUIntMapRead(&device->BufferMap); }
inline void UnlockBuffersRead(ALCdevice *device)
{ UnlockUIntMapRead(&device->BufferMap); }
inline void LockBuffersWrite(ALCdevice *device)
{ LockUIntMapWrite(&device->BufferMap); }
inline void UnlockBuffersWrite(ALCdevice *device)
{ UnlockUIntMapWrite(&device->BufferMap); }
inline struct ALbuffer *LookupBuffer(ALCdevice *device, ALuint id)
{ return (struct ALbuffer*)LookupUIntMapKeyNoLock(&device->BufferMap, id); }
inline struct ALbuffer *RemoveBuffer(ALCdevice *device, ALuint id)
{ return (struct ALbuffer*)RemoveUIntMapKeyNoLock(&device->BufferMap, id); }
ALvoid ReleaseALBuffers(ALCdevice *device);
#ifdef __cplusplus
}
#endif
#endif
+196
View File
@@ -0,0 +1,196 @@
#ifndef _AL_EFFECT_H_
#define _AL_EFFECT_H_
#include "alMain.h"
#ifdef __cplusplus
extern "C" {
#endif
struct ALeffect;
enum {
AL__EAXREVERB = 0,
AL__REVERB,
AL__CHORUS,
AL__COMPRESSOR,
AL__DISTORTION,
AL__ECHO,
AL__EQUALIZER,
AL__FLANGER,
AL__MODULATOR,
AL__DEDICATED,
MAX_EFFECTS
};
extern ALboolean DisabledEffects[MAX_EFFECTS];
extern ALfloat ReverbBoost;
extern ALboolean EmulateEAXReverb;
struct ALeffectVtable {
void (*const setParami)(struct ALeffect *effect, ALCcontext *context, ALenum param, ALint val);
void (*const setParamiv)(struct ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals);
void (*const setParamf)(struct ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val);
void (*const setParamfv)(struct ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals);
void (*const getParami)(const struct ALeffect *effect, ALCcontext *context, ALenum param, ALint *val);
void (*const getParamiv)(const struct ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals);
void (*const getParamf)(const struct ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val);
void (*const getParamfv)(const struct ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals);
};
#define DEFINE_ALEFFECT_VTABLE(T) \
const struct ALeffectVtable T##_vtable = { \
T##_setParami, T##_setParamiv, \
T##_setParamf, T##_setParamfv, \
T##_getParami, T##_getParamiv, \
T##_getParamf, T##_getParamfv, \
}
extern const struct ALeffectVtable ALeaxreverb_vtable;
extern const struct ALeffectVtable ALreverb_vtable;
extern const struct ALeffectVtable ALchorus_vtable;
extern const struct ALeffectVtable ALcompressor_vtable;
extern const struct ALeffectVtable ALdistortion_vtable;
extern const struct ALeffectVtable ALecho_vtable;
extern const struct ALeffectVtable ALequalizer_vtable;
extern const struct ALeffectVtable ALflanger_vtable;
extern const struct ALeffectVtable ALmodulator_vtable;
extern const struct ALeffectVtable ALnull_vtable;
extern const struct ALeffectVtable ALdedicated_vtable;
typedef union ALeffectProps {
struct {
// Shared Reverb Properties
ALfloat Density;
ALfloat Diffusion;
ALfloat Gain;
ALfloat GainHF;
ALfloat DecayTime;
ALfloat DecayHFRatio;
ALfloat ReflectionsGain;
ALfloat ReflectionsDelay;
ALfloat LateReverbGain;
ALfloat LateReverbDelay;
ALfloat AirAbsorptionGainHF;
ALfloat RoomRolloffFactor;
ALboolean DecayHFLimit;
// Additional EAX Reverb Properties
ALfloat GainLF;
ALfloat DecayLFRatio;
ALfloat ReflectionsPan[3];
ALfloat LateReverbPan[3];
ALfloat EchoTime;
ALfloat EchoDepth;
ALfloat ModulationTime;
ALfloat ModulationDepth;
ALfloat HFReference;
ALfloat LFReference;
} Reverb;
struct {
ALint Waveform;
ALint Phase;
ALfloat Rate;
ALfloat Depth;
ALfloat Feedback;
ALfloat Delay;
} Chorus;
struct {
ALboolean OnOff;
} Compressor;
struct {
ALfloat Edge;
ALfloat Gain;
ALfloat LowpassCutoff;
ALfloat EQCenter;
ALfloat EQBandwidth;
} Distortion;
struct {
ALfloat Delay;
ALfloat LRDelay;
ALfloat Damping;
ALfloat Feedback;
ALfloat Spread;
} Echo;
struct {
ALfloat LowCutoff;
ALfloat LowGain;
ALfloat Mid1Center;
ALfloat Mid1Gain;
ALfloat Mid1Width;
ALfloat Mid2Center;
ALfloat Mid2Gain;
ALfloat Mid2Width;
ALfloat HighCutoff;
ALfloat HighGain;
} Equalizer;
struct {
ALint Waveform;
ALint Phase;
ALfloat Rate;
ALfloat Depth;
ALfloat Feedback;
ALfloat Delay;
} Flanger;
struct {
ALfloat Frequency;
ALfloat HighPassCutoff;
ALint Waveform;
} Modulator;
struct {
ALfloat Gain;
} Dedicated;
} ALeffectProps;
typedef struct ALeffect {
// Effect type (AL_EFFECT_NULL, ...)
ALenum type;
ALeffectProps Props;
const struct ALeffectVtable *vtbl;
/* Self ID */
ALuint id;
} ALeffect;
inline void LockEffectsRead(ALCdevice *device)
{ LockUIntMapRead(&device->EffectMap); }
inline void UnlockEffectsRead(ALCdevice *device)
{ UnlockUIntMapRead(&device->EffectMap); }
inline void LockEffectsWrite(ALCdevice *device)
{ LockUIntMapWrite(&device->EffectMap); }
inline void UnlockEffectsWrite(ALCdevice *device)
{ UnlockUIntMapWrite(&device->EffectMap); }
inline struct ALeffect *LookupEffect(ALCdevice *device, ALuint id)
{ return (struct ALeffect*)LookupUIntMapKeyNoLock(&device->EffectMap, id); }
inline struct ALeffect *RemoveEffect(ALCdevice *device, ALuint id)
{ return (struct ALeffect*)RemoveUIntMapKeyNoLock(&device->EffectMap, id); }
inline ALboolean IsReverbEffect(ALenum type)
{ return type == AL_EFFECT_REVERB || type == AL_EFFECT_EAXREVERB; }
ALenum InitEffect(ALeffect *effect);
ALvoid ReleaseALEffects(ALCdevice *device);
ALvoid LoadReverbPreset(const char *name, ALeffect *effect);
#ifdef __cplusplus
}
#endif
#endif
+33
View File
@@ -0,0 +1,33 @@
#ifndef _AL_ERROR_H_
#define _AL_ERROR_H_
#include "alMain.h"
#ifdef __cplusplus
extern "C" {
#endif
extern ALboolean TrapALError;
ALvoid alSetError(ALCcontext *Context, ALenum errorCode);
#define SET_ERROR_AND_RETURN(ctx, err) do { \
alSetError((ctx), (err)); \
return; \
} while(0)
#define SET_ERROR_AND_RETURN_VALUE(ctx, err, val) do { \
alSetError((ctx), (err)); \
return (val); \
} while(0)
#define SET_ERROR_AND_GOTO(ctx, err, lbl) do { \
alSetError((ctx), (err)); \
goto lbl; \
} while(0)
#ifdef __cplusplus
}
#endif
#endif
+164
View File
@@ -0,0 +1,164 @@
#ifndef _AL_FILTER_H_
#define _AL_FILTER_H_
#include "alMain.h"
#include "math_defs.h"
#ifdef __cplusplus
extern "C" {
#endif
#define LOWPASSFREQREF (5000.0f)
#define HIGHPASSFREQREF (250.0f)
/* Filters implementation is based on the "Cookbook formulae for audio
* EQ biquad filter coefficients" by Robert Bristow-Johnson
* http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt
*/
/* Implementation note: For the shelf filters, the specified gain is for the
* reference frequency, which is the centerpoint of the transition band. This
* better matches EFX filter design. To set the gain for the shelf itself, use
* the square root of the desired linear gain (or halve the dB gain).
*/
typedef enum ALfilterType {
/** EFX-style low-pass filter, specifying a gain and reference frequency. */
ALfilterType_HighShelf,
/** EFX-style high-pass filter, specifying a gain and reference frequency. */
ALfilterType_LowShelf,
/** Peaking filter, specifying a gain and reference frequency. */
ALfilterType_Peaking,
/** Low-pass cut-off filter, specifying a cut-off frequency. */
ALfilterType_LowPass,
/** High-pass cut-off filter, specifying a cut-off frequency. */
ALfilterType_HighPass,
/** Band-pass filter, specifying a center frequency. */
ALfilterType_BandPass,
} ALfilterType;
typedef struct ALfilterState {
ALfloat x[2]; /* History of two last input samples */
ALfloat y[2]; /* History of two last output samples */
ALfloat b0, b1, b2; /* Transfer function coefficients "b" */
ALfloat a1, a2; /* Transfer function coefficients "a" (a0 is pre-applied) */
} ALfilterState;
/* Currently only a C-based filter process method is implemented. */
#define ALfilterState_process ALfilterState_processC
/* Calculates the rcpQ (i.e. 1/Q) coefficient for shelving filters, using the
* reference gain and shelf slope parameter.
* 0 < gain
* 0 < slope <= 1
*/
inline ALfloat calc_rcpQ_from_slope(ALfloat gain, ALfloat slope)
{
return sqrtf((gain + 1.0f/gain)*(1.0f/slope - 1.0f) + 2.0f);
}
/* Calculates the rcpQ (i.e. 1/Q) coefficient for filters, using the frequency
* multiple (i.e. ref_freq / sampling_freq) and bandwidth.
* 0 < freq_mult < 0.5.
*/
inline ALfloat calc_rcpQ_from_bandwidth(ALfloat freq_mult, ALfloat bandwidth)
{
ALfloat w0 = F_TAU * freq_mult;
return 2.0f*sinhf(logf(2.0f)/2.0f*bandwidth*w0/sinf(w0));
}
inline void ALfilterState_clear(ALfilterState *filter)
{
filter->x[0] = 0.0f;
filter->x[1] = 0.0f;
filter->y[0] = 0.0f;
filter->y[1] = 0.0f;
}
void ALfilterState_setParams(ALfilterState *filter, ALfilterType type, ALfloat gain, ALfloat freq_mult, ALfloat rcpQ);
inline void ALfilterState_copyParams(ALfilterState *restrict dst, const ALfilterState *restrict src)
{
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 *restrict src, ALsizei numsamples);
inline void ALfilterState_processPassthru(ALfilterState *filter, const ALfloat *restrict src, ALsizei numsamples)
{
if(numsamples >= 2)
{
filter->x[1] = src[numsamples-2];
filter->x[0] = src[numsamples-1];
filter->y[1] = src[numsamples-2];
filter->y[0] = src[numsamples-1];
}
else if(numsamples == 1)
{
filter->x[1] = filter->x[0];
filter->x[0] = src[0];
filter->y[1] = filter->y[0];
filter->y[0] = src[0];
}
}
typedef struct ALfilter {
// Filter type (AL_FILTER_NULL, ...)
ALenum type;
ALfloat Gain;
ALfloat GainHF;
ALfloat HFReference;
ALfloat GainLF;
ALfloat LFReference;
void (*SetParami)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALint val);
void (*SetParamiv)(struct ALfilter *filter, ALCcontext *context, ALenum param, const ALint *vals);
void (*SetParamf)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALfloat val);
void (*SetParamfv)(struct ALfilter *filter, ALCcontext *context, ALenum param, const ALfloat *vals);
void (*GetParami)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALint *val);
void (*GetParamiv)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALint *vals);
void (*GetParamf)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *val);
void (*GetParamfv)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *vals);
/* Self ID */
ALuint id;
} ALfilter;
#define ALfilter_SetParami(x, c, p, v) ((x)->SetParami((x),(c),(p),(v)))
#define ALfilter_SetParamiv(x, c, p, v) ((x)->SetParamiv((x),(c),(p),(v)))
#define ALfilter_SetParamf(x, c, p, v) ((x)->SetParamf((x),(c),(p),(v)))
#define ALfilter_SetParamfv(x, c, p, v) ((x)->SetParamfv((x),(c),(p),(v)))
#define ALfilter_GetParami(x, c, p, v) ((x)->GetParami((x),(c),(p),(v)))
#define ALfilter_GetParamiv(x, c, p, v) ((x)->GetParamiv((x),(c),(p),(v)))
#define ALfilter_GetParamf(x, c, p, v) ((x)->GetParamf((x),(c),(p),(v)))
#define ALfilter_GetParamfv(x, c, p, v) ((x)->GetParamfv((x),(c),(p),(v)))
inline void LockFiltersRead(ALCdevice *device)
{ LockUIntMapRead(&device->FilterMap); }
inline void UnlockFiltersRead(ALCdevice *device)
{ UnlockUIntMapRead(&device->FilterMap); }
inline void LockFiltersWrite(ALCdevice *device)
{ LockUIntMapWrite(&device->FilterMap); }
inline void UnlockFiltersWrite(ALCdevice *device)
{ UnlockUIntMapWrite(&device->FilterMap); }
inline struct ALfilter *LookupFilter(ALCdevice *device, ALuint id)
{ return (struct ALfilter*)LookupUIntMapKeyNoLock(&device->FilterMap, id); }
inline struct ALfilter *RemoveFilter(ALCdevice *device, ALuint id)
{ return (struct ALfilter*)RemoveUIntMapKeyNoLock(&device->FilterMap, id); }
ALvoid ReleaseALFilters(ALCdevice *device);
#ifdef __cplusplus
}
#endif
#endif
+66
View File
@@ -0,0 +1,66 @@
#ifndef _AL_LISTENER_H_
#define _AL_LISTENER_H_
#include "alMain.h"
#include "alu.h"
#ifdef __cplusplus
extern "C" {
#endif
struct ALlistenerProps {
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 {
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 {
aluMatrixf Matrix;
aluVector Velocity;
ALfloat Gain;
ALfloat MetersPerUnit;
ALfloat DopplerFactor;
ALfloat SpeedOfSound;
ALboolean SourceDistanceModel;
enum DistanceModel DistanceModel;
} Params;
} ALlistener;
void UpdateListenerProps(ALCcontext *context);
#ifdef __cplusplus
}
#endif
#endif
File diff suppressed because it is too large Load Diff
+128
View File
@@ -0,0 +1,128 @@
#ifndef _AL_SOURCE_H_
#define _AL_SOURCE_H_
#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" {
#endif
struct ALbuffer;
struct ALsource;
typedef struct ALbufferlistitem {
struct ALbuffer *buffer;
ATOMIC(struct ALbufferlistitem*) next;
} ALbufferlistitem;
typedef struct ALsource {
/** Source properties. */
ALfloat Pitch;
ALfloat Gain;
ALfloat OuterGain;
ALfloat MinGain;
ALfloat MaxGain;
ALfloat InnerAngle;
ALfloat OuterAngle;
ALfloat RefDistance;
ALfloat MaxDistance;
ALfloat RolloffFactor;
ALfloat Position[3];
ALfloat Velocity[3];
ALfloat Direction[3];
ALfloat Orientation[2][3];
ALboolean HeadRelative;
ALboolean Looping;
enum DistanceModel DistanceModel;
enum Resampler Resampler;
ALboolean DirectChannels;
enum SpatializeMode Spatialize;
ALboolean DryGainHFAuto;
ALboolean WetGainAuto;
ALboolean WetGainHFAuto;
ALfloat OuterGainHF;
ALfloat AirAbsorptionFactor;
ALfloat RoomRolloffFactor;
ALfloat DopplerFactor;
/* NOTE: Stereo pan angles are specified in radians, counter-clockwise
* rather than clockwise.
*/
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;
/**
* 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 void LockSourcesRead(ALCcontext *context)
{ LockUIntMapRead(&context->SourceMap); }
inline void UnlockSourcesRead(ALCcontext *context)
{ UnlockUIntMapRead(&context->SourceMap); }
inline void LockSourcesWrite(ALCcontext *context)
{ LockUIntMapWrite(&context->SourceMap); }
inline void UnlockSourcesWrite(ALCcontext *context)
{ UnlockUIntMapWrite(&context->SourceMap); }
inline struct ALsource *LookupSource(ALCcontext *context, ALuint id)
{ return (struct ALsource*)LookupUIntMapKeyNoLock(&context->SourceMap, id); }
inline struct ALsource *RemoveSource(ALCcontext *context, ALuint id)
{ return (struct ALsource*)RemoveUIntMapKeyNoLock(&context->SourceMap, id); }
void UpdateAllSourceProps(ALCcontext *context);
ALvoid ReleaseALSources(ALCcontext *Context);
#ifdef __cplusplus
}
#endif
#endif
+20
View File
@@ -0,0 +1,20 @@
#ifndef ALTHUNK_H
#define ALTHUNK_H
#include "alMain.h"
#ifdef __cplusplus
extern "C" {
#endif
void ThunkInit(void);
void ThunkExit(void);
ALenum NewThunkEntry(ALuint *index);
void FreeThunkEntry(ALuint index);
#ifdef __cplusplus
}
#endif
#endif //ALTHUNK_H
+516
View File
@@ -0,0 +1,516 @@
#ifndef _ALU_H_
#define _ALU_H_
#include <limits.h>
#include <math.h>
#ifdef HAVE_FLOAT_H
#include <float.h>
#endif
#ifdef HAVE_IEEEFP_H
#include <ieeefp.h>
#endif
#include "alMain.h"
#include "alBuffer.h"
#include "alFilter.h"
#include "alAuxEffectSlot.h"
#include "hrtf.h"
#include "align.h"
#include "nfcfilter.h"
#include "math_defs.h"
#define MAX_PITCH (255)
/* Maximum number of buffer samples before the current pos needed for resampling. */
#define MAX_PRE_SAMPLES 12
/* Maximum number of buffer samples after the current pos needed for resampling. */
#define MAX_POST_SAMPLES 12
#ifdef __cplusplus
extern "C" {
#endif
struct ALsource;
struct 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)
#define BSINC_PHASE_BITS 4
#define BSINC_PHASE_COUNT (1<<BSINC_PHASE_BITS)
/* Interpolator state. Kind of a misnomer since the interpolator itself is
* stateless. This just keeps it from having to recompute scale-related
* mappings for every sample.
*/
typedef struct BsincState {
ALfloat sf; /* Scale interpolation factor. */
ALuint m; /* Coefficient count. */
ALint l; /* Left coefficient offset. */
struct {
const ALfloat *filter; /* Filter coefficients. */
const ALfloat *scDelta; /* Scale deltas. */
const ALfloat *phDelta; /* Phase deltas. */
const ALfloat *spDelta; /* Scale-phase deltas. */
} coeffs[BSINC_PHASE_COUNT];
} BsincState;
typedef union 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];
} aluVector;
inline void aluVectorSet(aluVector *vector, ALfloat x, ALfloat y, ALfloat z, ALfloat w)
{
vector->v[0] = x;
vector->v[1] = y;
vector->v[2] = z;
vector->v[3] = w;
}
typedef union aluMatrixf {
alignas(16) ALfloat m[4][4];
} aluMatrixf;
extern const aluMatrixf IdentityMatrixf;
inline void aluMatrixfSetRow(aluMatrixf *matrix, ALuint row,
ALfloat m0, ALfloat m1, ALfloat m2, ALfloat m3)
{
matrix->m[row][0] = m0;
matrix->m[row][1] = m1;
matrix->m[row][2] = m2;
matrix->m[row][3] = m3;
}
inline void aluMatrixfSet(aluMatrixf *matrix, ALfloat m00, ALfloat m01, ALfloat m02, ALfloat m03,
ALfloat m10, ALfloat m11, ALfloat m12, ALfloat m13,
ALfloat m20, ALfloat m21, ALfloat m22, ALfloat m23,
ALfloat m30, ALfloat m31, ALfloat m32, ALfloat m33)
{
aluMatrixfSetRow(matrix, 0, m00, m01, m02, m03);
aluMatrixfSetRow(matrix, 1, m10, m11, m12, m13);
aluMatrixfSetRow(matrix, 2, m20, m21, m22, m23);
aluMatrixfSetRow(matrix, 3, m30, m31, m32, m33);
}
enum ActiveFilters {
AF_None = 0,
AF_LowPass = 1,
AF_HighPass = 2,
AF_BandPass = AF_LowPass | AF_HighPass
};
typedef struct MixHrtfParams {
const ALfloat (*Coeffs)[2];
ALsizei Delay[2];
ALfloat Gain;
ALfloat GainStep;
} MixHrtfParams;
typedef struct DirectParams {
ALfilterState LowPass;
ALfilterState HighPass;
NfcFilter NFCtrlFilter[MAX_AMBI_ORDER];
struct {
HrtfParams Old;
HrtfParams Target;
HrtfState State;
} Hrtf;
struct {
ALfloat Current[MAX_OUTPUT_CHANNELS];
ALfloat Target[MAX_OUTPUT_CHANNELS];
} Gains;
} DirectParams;
typedef struct SendParams {
ALfilterState LowPass;
ALfilterState HighPass;
struct {
ALfloat Current[MAX_OUTPUT_CHANNELS];
ALfloat Target[MAX_OUTPUT_CHANNELS];
} Gains;
} SendParams;
struct ALvoiceProps {
ATOMIC(struct ALvoiceProps*) next;
ALfloat Pitch;
ALfloat Gain;
ALfloat OuterGain;
ALfloat MinGain;
ALfloat MaxGain;
ALfloat InnerAngle;
ALfloat OuterAngle;
ALfloat RefDistance;
ALfloat MaxDistance;
ALfloat RolloffFactor;
ALfloat Position[3];
ALfloat Velocity[3];
ALfloat Direction[3];
ALfloat Orientation[2][3];
ALboolean HeadRelative;
enum DistanceModel DistanceModel;
enum Resampler Resampler;
ALboolean DirectChannels;
enum SpatializeMode SpatializeMode;
ALboolean DryGainHFAuto;
ALboolean WetGainAuto;
ALboolean WetGainHFAuto;
ALfloat OuterGainHF;
ALfloat AirAbsorptionFactor;
ALfloat RoomRolloffFactor;
ALfloat DopplerFactor;
ALfloat StereoPan[2];
ALfloat Radius;
/** Direct filter and auxiliary send info. */
struct {
ALfloat Gain;
ALfloat GainHF;
ALfloat HFReference;
ALfloat GainLF;
ALfloat LFReference;
} Direct;
struct {
struct ALeffectslot *Slot;
ALfloat Gain;
ALfloat GainHF;
ALfloat HFReference;
ALfloat GainLF;
ALfloat LFReference;
} Send[];
};
/* If not 'fading', gain targets are used directly without fading. */
#define VOICE_IS_FADING (1<<0)
#define VOICE_HAS_HRTF (1<<1)
#define VOICE_HAS_NFC (1<<2)
typedef struct ALvoice {
struct ALvoiceProps *Props;
ATOMIC(struct ALvoiceProps*) Update;
ATOMIC(struct ALvoiceProps*) FreeList;
ATOMIC(struct ALsource*) Source;
ATOMIC(bool) Playing;
/**
* 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)
inline ALfloat minf(ALfloat a, ALfloat b)
{ return ((a > b) ? b : a); }
inline ALfloat maxf(ALfloat a, ALfloat b)
{ return ((a > b) ? a : b); }
inline ALfloat clampf(ALfloat val, ALfloat min, ALfloat max)
{ return minf(max, maxf(min, val)); }
inline ALdouble mind(ALdouble a, ALdouble b)
{ return ((a > b) ? b : a); }
inline ALdouble maxd(ALdouble a, ALdouble b)
{ return ((a > b) ? a : b); }
inline ALdouble clampd(ALdouble val, ALdouble min, ALdouble max)
{ return mind(max, maxd(min, val)); }
inline ALuint minu(ALuint a, ALuint b)
{ return ((a > b) ? b : a); }
inline ALuint maxu(ALuint a, ALuint b)
{ return ((a > b) ? a : b); }
inline ALuint clampu(ALuint val, ALuint min, ALuint max)
{ return minu(max, maxu(min, val)); }
inline ALint mini(ALint a, ALint b)
{ return ((a > b) ? b : a); }
inline ALint maxi(ALint a, ALint b)
{ return ((a > b) ? a : b); }
inline ALint clampi(ALint val, ALint min, ALint max)
{ return mini(max, maxi(min, val)); }
inline ALint64 mini64(ALint64 a, ALint64 b)
{ return ((a > b) ? b : a); }
inline ALint64 maxi64(ALint64 a, ALint64 b)
{ return ((a > b) ? a : b); }
inline ALint64 clampi64(ALint64 val, ALint64 min, ALint64 max)
{ return mini64(max, maxi64(min, val)); }
inline ALuint64 minu64(ALuint64 a, ALuint64 b)
{ return ((a > b) ? b : a); }
inline ALuint64 maxu64(ALuint64 a, ALuint64 b)
{ return ((a > b) ? a : b); }
inline ALuint64 clampu64(ALuint64 val, ALuint64 min, ALuint64 max)
{ return minu64(max, maxu64(min, val)); }
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, ALsizei frac)
{
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);
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);
/**
* CalcDirectionCoeffs
*
* Calculates ambisonic coefficients based on a direction vector. The vector
* must be normalized (unit length), and the spread is the angular width of the
* sound (0...tau).
*/
void CalcDirectionCoeffs(const ALfloat dir[3], ALfloat spread, ALfloat coeffs[MAX_AMBI_COEFFS]);
/**
* CalcAngleCoeffs
*
* Calculates ambisonic coefficients based on azimuth and elevation. The
* azimuth and elevation parameters are in radians, going right and up
* respectively.
*/
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
*
* Computes channel gains for ambient, omni-directional sounds.
*/
#define ComputeAmbientGains(b, g, o) do { \
if((b).CoeffCount > 0) \
ComputeAmbientGainsMC((b).Ambi.Coeffs, (b).NumChannels, g, o); \
else \
ComputeAmbientGainsBF((b).Ambi.Map, (b).NumChannels, g, o); \
} while (0)
void ComputeAmbientGainsMC(const ChannelConfig *chancoeffs, ALsizei numchans, ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]);
void ComputeAmbientGainsBF(const BFChannelConfig *chanmap, ALsizei numchans, ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]);
/**
* ComputePanningGains
*
* Computes panning gains using the given channel decoder coefficients and the
* pre-calculated direction or angle coefficients.
*/
#define ComputePanningGains(b, c, g, o) do { \
if((b).CoeffCount > 0) \
ComputePanningGainsMC((b).Ambi.Coeffs, (b).NumChannels, (b).CoeffCount, c, g, o);\
else \
ComputePanningGainsBF((b).Ambi.Map, (b).NumChannels, c, g, o); \
} while (0)
void ComputePanningGainsMC(const ChannelConfig *chancoeffs, ALsizei numchans, ALsizei numcoeffs, const ALfloat coeffs[MAX_AMBI_COEFFS], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]);
void ComputePanningGainsBF(const BFChannelConfig *chanmap, ALsizei numchans, const ALfloat coeffs[MAX_AMBI_COEFFS], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]);
/**
* 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]);
ALboolean MixSource(struct ALvoice *voice, struct ALsource *Source, ALCdevice *Device, ALsizei SamplesToDo);
void aluMixData(ALCdevice *device, ALvoid *OutBuffer, ALsizei NumSamples);
/* Caller must lock the device. */
void aluHandleDisconnect(ALCdevice *device);
extern ALfloat ConeScale;
extern ALfloat ZScale;
#ifdef __cplusplus
}
#endif
#endif
+94
View File
@@ -0,0 +1,94 @@
/*-
* Copyright (c) 2005 Boris Mikhaylov
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef BS2B_H
#define BS2B_H
/* Number of crossfeed levels */
#define BS2B_CLEVELS 3
/* Normal crossfeed levels */
#define BS2B_HIGH_CLEVEL 3
#define BS2B_MIDDLE_CLEVEL 2
#define BS2B_LOW_CLEVEL 1
/* Easy crossfeed levels */
#define BS2B_HIGH_ECLEVEL BS2B_HIGH_CLEVEL + BS2B_CLEVELS
#define BS2B_MIDDLE_ECLEVEL BS2B_MIDDLE_CLEVEL + BS2B_CLEVELS
#define BS2B_LOW_ECLEVEL BS2B_LOW_CLEVEL + BS2B_CLEVELS
/* Default crossfeed levels */
#define BS2B_DEFAULT_CLEVEL BS2B_HIGH_ECLEVEL
/* Default sample rate (Hz) */
#define BS2B_DEFAULT_SRATE 44100
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
struct bs2b {
int level; /* Crossfeed level */
int srate; /* Sample rate (Hz) */
/* Lowpass IIR filter coefficients */
float a0_lo;
float b1_lo;
/* Highboost IIR filter coefficients */
float a0_hi;
float a1_hi;
float b1_hi;
/* Buffer of last filtered sample.
* [0] - first channel, [1] - second channel
*/
struct t_last_sample {
float asis;
float lo;
float hi;
} last_sample[2];
};
/* Clear buffers and set new coefficients with new crossfeed level and sample
* rate values.
* level - crossfeed level of *LEVEL values.
* srate - sample rate by Hz.
*/
void bs2b_set_params(struct bs2b *bs2b, int level, int srate);
/* Return current crossfeed level value */
int bs2b_get_level(struct bs2b *bs2b);
/* Return current sample rate value */
int bs2b_get_srate(struct bs2b *bs2b);
/* Clear buffer */
void bs2b_clear(struct bs2b *bs2b);
void bs2b_cross_feed(struct bs2b *bs2b, float *restrict Left, float *restrict Right, int SamplesToDo);
#ifdef __cplusplus
} /* extern "C" */
#endif /* __cplusplus */
#endif /* BS2B_H */
+9
View File
@@ -0,0 +1,9 @@
#ifndef SAMPLE_CVT_H
#define SAMPLE_CVT_H
#include "AL/al.h"
#include "alBuffer.h"
void ConvertData(ALvoid *dst, enum UserFmtType dstType, const ALvoid *src, enum UserFmtType srcType, ALsizei numchans, ALsizei len, ALsizei align);
#endif /* SAMPLE_CVT_H */
+727
View File
@@ -0,0 +1,727 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 1999-2007 by authors.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include <stdlib.h>
#include <math.h>
#include "AL/al.h"
#include "AL/alc.h"
#include "alMain.h"
#include "alAuxEffectSlot.h"
#include "alThunk.h"
#include "alError.h"
#include "alListener.h"
#include "alSource.h"
#include "almalloc.h"
extern inline void LockEffectSlotsRead(ALCcontext *context);
extern inline void UnlockEffectSlotsRead(ALCcontext *context);
extern inline void LockEffectSlotsWrite(ALCcontext *context);
extern inline void UnlockEffectSlotsWrite(ALCcontext *context);
extern inline struct ALeffectslot *LookupEffectSlot(ALCcontext *context, ALuint id);
extern inline struct ALeffectslot *RemoveEffectSlot(ALCcontext *context, ALuint id);
static UIntMap EffectStateFactoryMap;
static inline ALeffectStateFactory *getFactoryByType(ALenum type)
{
ALeffectStateFactory* (*getFactory)(void) = LookupUIntMapKey(&EffectStateFactoryMap, type);
if(getFactory != NULL)
return getFactory();
return NULL;
}
static void ALeffectState_IncRef(ALeffectState *state);
static void ALeffectState_DecRef(ALeffectState *state);
#define DO_UPDATEPROPS() do { \
if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire)) \
UpdateEffectSlotProps(slot); \
else \
ATOMIC_FLAG_CLEAR(&slot->PropsClean, almemory_order_release); \
} while(0)
AL_API ALvoid AL_APIENTRY alGenAuxiliaryEffectSlots(ALsizei n, ALuint *effectslots)
{
ALCcontext *context;
ALeffectslot **tmpslots = NULL;
ALsizei cur;
ALenum err;
context = GetContextRef();
if(!context) return;
if(!(n >= 0))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
tmpslots = al_malloc(DEF_ALIGN, sizeof(ALeffectslot*)*n);
LockEffectSlotsWrite(context);
for(cur = 0;cur < n;cur++)
{
ALeffectslot *slot = al_calloc(16, sizeof(ALeffectslot));
err = AL_OUT_OF_MEMORY;
if(!slot || (err=InitEffectSlot(slot)) != AL_NO_ERROR)
{
al_free(slot);
UnlockEffectSlotsWrite(context);
alDeleteAuxiliaryEffectSlots(cur, effectslots);
SET_ERROR_AND_GOTO(context, err, done);
}
err = NewThunkEntry(&slot->id);
if(err == AL_NO_ERROR)
err = InsertUIntMapEntryNoLock(&context->EffectSlotMap, slot->id, slot);
if(err != AL_NO_ERROR)
{
FreeThunkEntry(slot->id);
ALeffectState_DecRef(slot->Effect.State);
if(slot->Params.EffectState)
ALeffectState_DecRef(slot->Params.EffectState);
al_free(slot);
UnlockEffectSlotsWrite(context);
alDeleteAuxiliaryEffectSlots(cur, effectslots);
SET_ERROR_AND_GOTO(context, err, done);
}
aluInitEffectPanning(slot);
tmpslots[cur] = slot;
effectslots[cur] = slot->id;
}
if(n > 0)
{
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:
al_free(tmpslots);
ALCcontext_DecRef(context);
}
AL_API ALvoid AL_APIENTRY alDeleteAuxiliaryEffectSlots(ALsizei n, const ALuint *effectslots)
{
ALCcontext *context;
ALeffectslot *slot;
ALsizei i;
context = GetContextRef();
if(!context) return;
LockEffectSlotsWrite(context);
if(!(n >= 0))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
for(i = 0;i < n;i++)
{
if((slot=LookupEffectSlot(context, effectslots[i])) == NULL)
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
if(ReadRef(&slot->ref) != 0)
SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done);
}
// All effectslots are valid
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);
DeinitEffectSlot(slot);
memset(slot, 0, sizeof(*slot));
al_free(slot);
}
done:
UnlockEffectSlotsWrite(context);
ALCcontext_DecRef(context);
}
AL_API ALboolean AL_APIENTRY alIsAuxiliaryEffectSlot(ALuint effectslot)
{
ALCcontext *context;
ALboolean ret;
context = GetContextRef();
if(!context) return AL_FALSE;
LockEffectSlotsRead(context);
ret = (LookupEffectSlot(context, effectslot) ? AL_TRUE : AL_FALSE);
UnlockEffectSlotsRead(context);
ALCcontext_DecRef(context);
return ret;
}
AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint value)
{
ALCdevice *device;
ALCcontext *context;
ALeffectslot *slot;
ALeffect *effect = NULL;
ALenum err;
context = GetContextRef();
if(!context) return;
WriteLock(&context->PropLock);
LockEffectSlotsRead(context);
if((slot=LookupEffectSlot(context, effectslot)) == NULL)
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
switch(param)
{
case AL_EFFECTSLOT_EFFECT:
device = context->Device;
LockEffectsRead(device);
effect = (value ? LookupEffect(device, value) : NULL);
if(!(value == 0 || effect != NULL))
{
UnlockEffectsRead(device);
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
}
err = InitializeEffect(device, slot, effect);
UnlockEffectsRead(device);
if(err != AL_NO_ERROR)
SET_ERROR_AND_GOTO(context, err, done);
break;
case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
if(!(value == AL_TRUE || value == AL_FALSE))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
slot->AuxSendAuto = value;
break;
default:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
DO_UPDATEPROPS();
done:
UnlockEffectSlotsRead(context);
WriteUnlock(&context->PropLock);
ALCcontext_DecRef(context);
}
AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, const ALint *values)
{
ALCcontext *context;
switch(param)
{
case AL_EFFECTSLOT_EFFECT:
case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
alAuxiliaryEffectSloti(effectslot, param, values[0]);
return;
}
context = GetContextRef();
if(!context) return;
LockEffectSlotsRead(context);
if(LookupEffectSlot(context, effectslot) == NULL)
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
switch(param)
{
default:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
done:
UnlockEffectSlotsRead(context);
ALCcontext_DecRef(context);
}
AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat value)
{
ALCcontext *context;
ALeffectslot *slot;
context = GetContextRef();
if(!context) return;
WriteLock(&context->PropLock);
LockEffectSlotsRead(context);
if((slot=LookupEffectSlot(context, effectslot)) == NULL)
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
switch(param)
{
case AL_EFFECTSLOT_GAIN:
if(!(value >= 0.0f && value <= 1.0f))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
slot->Gain = value;
break;
default:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
DO_UPDATEPROPS();
done:
UnlockEffectSlotsRead(context);
WriteUnlock(&context->PropLock);
ALCcontext_DecRef(context);
}
AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, const ALfloat *values)
{
ALCcontext *context;
switch(param)
{
case AL_EFFECTSLOT_GAIN:
alAuxiliaryEffectSlotf(effectslot, param, values[0]);
return;
}
context = GetContextRef();
if(!context) return;
LockEffectSlotsRead(context);
if(LookupEffectSlot(context, effectslot) == NULL)
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
switch(param)
{
default:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
done:
UnlockEffectSlotsRead(context);
ALCcontext_DecRef(context);
}
AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint *value)
{
ALCcontext *context;
ALeffectslot *slot;
context = GetContextRef();
if(!context) return;
LockEffectSlotsRead(context);
if((slot=LookupEffectSlot(context, effectslot)) == NULL)
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
switch(param)
{
case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
*value = slot->AuxSendAuto;
break;
default:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
done:
UnlockEffectSlotsRead(context);
ALCcontext_DecRef(context);
}
AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, ALint *values)
{
ALCcontext *context;
switch(param)
{
case AL_EFFECTSLOT_EFFECT:
case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
alGetAuxiliaryEffectSloti(effectslot, param, values);
return;
}
context = GetContextRef();
if(!context) return;
LockEffectSlotsRead(context);
if(LookupEffectSlot(context, effectslot) == NULL)
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
switch(param)
{
default:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
done:
UnlockEffectSlotsRead(context);
ALCcontext_DecRef(context);
}
AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat *value)
{
ALCcontext *context;
ALeffectslot *slot;
context = GetContextRef();
if(!context) return;
LockEffectSlotsRead(context);
if((slot=LookupEffectSlot(context, effectslot)) == NULL)
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
switch(param)
{
case AL_EFFECTSLOT_GAIN:
*value = slot->Gain;
break;
default:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
done:
UnlockEffectSlotsRead(context);
ALCcontext_DecRef(context);
}
AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, ALfloat *values)
{
ALCcontext *context;
switch(param)
{
case AL_EFFECTSLOT_GAIN:
alGetAuxiliaryEffectSlotf(effectslot, param, values);
return;
}
context = GetContextRef();
if(!context) return;
LockEffectSlotsRead(context);
if(LookupEffectSlot(context, effectslot) == NULL)
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
switch(param)
{
default:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
done:
UnlockEffectSlotsRead(context);
ALCcontext_DecRef(context);
}
void InitEffectFactoryMap(void)
{
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_CHORUS, ALchorusStateFactory_getFactory);
InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_COMPRESSOR, ALcompressorStateFactory_getFactory);
InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_DISTORTION, ALdistortionStateFactory_getFactory);
InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_ECHO, ALechoStateFactory_getFactory);
InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_EQUALIZER, ALequalizerStateFactory_getFactory);
InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_FLANGER, ALflangerStateFactory_getFactory);
InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_RING_MODULATOR, ALmodulatorStateFactory_getFactory);
InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_DEDICATED_DIALOGUE, ALdedicatedStateFactory_getFactory);
InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT, ALdedicatedStateFactory_getFactory);
}
void DeinitEffectFactoryMap(void)
{
ResetUIntMap(&EffectStateFactoryMap);
}
ALenum InitializeEffect(ALCdevice *Device, ALeffectslot *EffectSlot, ALeffect *effect)
{
ALenum newtype = (effect ? effect->type : AL_EFFECT_NULL);
struct ALeffectslotProps *props;
ALeffectState *State;
if(newtype != EffectSlot->Effect.Type)
{
ALeffectStateFactory *factory;
factory = getFactoryByType(newtype);
if(!factory)
{
ERR("Failed to find factory for effect type 0x%04x\n", newtype);
return AL_INVALID_ENUM;
}
State = V0(factory,create)();
if(!State) return AL_OUT_OF_MEMORY;
START_MIXER_MODE();
almtx_lock(&Device->BackendLock);
State->OutBuffer = Device->Dry.Buffer;
State->OutChannels = Device->Dry.NumChannels;
if(V(State,deviceUpdate)(Device) == AL_FALSE)
{
almtx_unlock(&Device->BackendLock);
LEAVE_MIXER_MODE();
ALeffectState_DecRef(State);
return AL_OUT_OF_MEMORY;
}
almtx_unlock(&Device->BackendLock);
END_MIXER_MODE();
if(!effect)
{
EffectSlot->Effect.Type = AL_EFFECT_NULL;
memset(&EffectSlot->Effect.Props, 0, sizeof(EffectSlot->Effect.Props));
}
else
{
EffectSlot->Effect.Type = effect->type;
EffectSlot->Effect.Props = effect->Props;
}
ALeffectState_DecRef(EffectSlot->Effect.State);
EffectSlot->Effect.State = State;
}
else if(effect)
EffectSlot->Effect.Props = effect->Props;
/* Remove state references from old effect slot property updates. */
props = ATOMIC_LOAD_SEQ(&EffectSlot->FreeList);
while(props)
{
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;
slot->Effect.Type = AL_EFFECT_NULL;
factory = getFactoryByType(AL_EFFECT_NULL);
if(!(slot->Effect.State=V0(factory,create)()))
return AL_OUT_OF_MEMORY;
slot->Gain = 1.0;
slot->AuxSendAuto = AL_TRUE;
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.values[pos];
Context->EffectSlotMap.values[pos] = NULL;
DeinitEffectSlot(temp);
FreeThunkEntry(temp->id);
memset(temp, 0, sizeof(ALeffectslot));
al_free(temp);
}
}
+1409
View File
File diff suppressed because it is too large Load Diff
+716
View File
@@ -0,0 +1,716 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 1999-2007 by authors.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include <stdlib.h>
#include <math.h>
#include <float.h>
#include "AL/al.h"
#include "AL/alc.h"
#include "alMain.h"
#include "alEffect.h"
#include "alThunk.h"
#include "alError.h"
ALboolean DisabledEffects[MAX_EFFECTS];
extern inline void LockEffectsRead(ALCdevice *device);
extern inline void UnlockEffectsRead(ALCdevice *device);
extern inline void LockEffectsWrite(ALCdevice *device);
extern inline void UnlockEffectsWrite(ALCdevice *device);
extern inline struct ALeffect *LookupEffect(ALCdevice *device, ALuint id);
extern inline struct ALeffect *RemoveEffect(ALCdevice *device, ALuint id);
extern inline ALboolean IsReverbEffect(ALenum type);
static void InitEffectParams(ALeffect *effect, ALenum type);
AL_API ALvoid AL_APIENTRY alGenEffects(ALsizei n, ALuint *effects)
{
ALCdevice *device;
ALCcontext *context;
ALsizei cur;
context = GetContextRef();
if(!context) return;
if(!(n >= 0))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
device = context->Device;
for(cur = 0;cur < n;cur++)
{
ALeffect *effect = al_calloc(16, sizeof(ALeffect));
ALenum err = AL_OUT_OF_MEMORY;
if(!effect || (err=InitEffect(effect)) != AL_NO_ERROR)
{
al_free(effect);
alDeleteEffects(cur, effects);
SET_ERROR_AND_GOTO(context, err, done);
}
err = NewThunkEntry(&effect->id);
if(err == AL_NO_ERROR)
err = InsertUIntMapEntry(&device->EffectMap, effect->id, effect);
if(err != AL_NO_ERROR)
{
FreeThunkEntry(effect->id);
memset(effect, 0, sizeof(ALeffect));
al_free(effect);
alDeleteEffects(cur, effects);
SET_ERROR_AND_GOTO(context, err, done);
}
effects[cur] = effect->id;
}
done:
ALCcontext_DecRef(context);
}
AL_API ALvoid AL_APIENTRY alDeleteEffects(ALsizei n, const ALuint *effects)
{
ALCdevice *device;
ALCcontext *context;
ALeffect *effect;
ALsizei i;
context = GetContextRef();
if(!context) return;
device = context->Device;
LockEffectsWrite(device);
if(!(n >= 0))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
for(i = 0;i < n;i++)
{
if(effects[i] && LookupEffect(device, effects[i]) == NULL)
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
}
for(i = 0;i < n;i++)
{
if((effect=RemoveEffect(device, effects[i])) == NULL)
continue;
FreeThunkEntry(effect->id);
memset(effect, 0, sizeof(*effect));
al_free(effect);
}
done:
UnlockEffectsWrite(device);
ALCcontext_DecRef(context);
}
AL_API ALboolean AL_APIENTRY alIsEffect(ALuint effect)
{
ALCcontext *Context;
ALboolean result;
Context = GetContextRef();
if(!Context) return AL_FALSE;
LockEffectsRead(Context->Device);
result = ((!effect || LookupEffect(Context->Device, effect)) ?
AL_TRUE : AL_FALSE);
UnlockEffectsRead(Context->Device);
ALCcontext_DecRef(Context);
return result;
}
AL_API ALvoid AL_APIENTRY alEffecti(ALuint effect, ALenum param, ALint value)
{
ALCcontext *Context;
ALCdevice *Device;
ALeffect *ALEffect;
Context = GetContextRef();
if(!Context) return;
Device = Context->Device;
LockEffectsWrite(Device);
if((ALEffect=LookupEffect(Device, effect)) == NULL)
alSetError(Context, AL_INVALID_NAME);
else
{
if(param == AL_EFFECT_TYPE)
{
ALboolean isOk = (value == AL_EFFECT_NULL);
ALint i;
for(i = 0;!isOk && EffectList[i].val;i++)
{
if(value == EffectList[i].val &&
!DisabledEffects[EffectList[i].type])
isOk = AL_TRUE;
}
if(isOk)
InitEffectParams(ALEffect, value);
else
alSetError(Context, AL_INVALID_VALUE);
}
else
{
/* Call the appropriate handler */
V(ALEffect,setParami)(Context, param, value);
}
}
UnlockEffectsWrite(Device);
ALCcontext_DecRef(Context);
}
AL_API ALvoid AL_APIENTRY alEffectiv(ALuint effect, ALenum param, const ALint *values)
{
ALCcontext *Context;
ALCdevice *Device;
ALeffect *ALEffect;
switch(param)
{
case AL_EFFECT_TYPE:
alEffecti(effect, param, values[0]);
return;
}
Context = GetContextRef();
if(!Context) return;
Device = Context->Device;
LockEffectsWrite(Device);
if((ALEffect=LookupEffect(Device, effect)) == NULL)
alSetError(Context, AL_INVALID_NAME);
else
{
/* Call the appropriate handler */
V(ALEffect,setParamiv)(Context, param, values);
}
UnlockEffectsWrite(Device);
ALCcontext_DecRef(Context);
}
AL_API ALvoid AL_APIENTRY alEffectf(ALuint effect, ALenum param, ALfloat value)
{
ALCcontext *Context;
ALCdevice *Device;
ALeffect *ALEffect;
Context = GetContextRef();
if(!Context) return;
Device = Context->Device;
LockEffectsWrite(Device);
if((ALEffect=LookupEffect(Device, effect)) == NULL)
alSetError(Context, AL_INVALID_NAME);
else
{
/* Call the appropriate handler */
V(ALEffect,setParamf)(Context, param, value);
}
UnlockEffectsWrite(Device);
ALCcontext_DecRef(Context);
}
AL_API ALvoid AL_APIENTRY alEffectfv(ALuint effect, ALenum param, const ALfloat *values)
{
ALCcontext *Context;
ALCdevice *Device;
ALeffect *ALEffect;
Context = GetContextRef();
if(!Context) return;
Device = Context->Device;
LockEffectsWrite(Device);
if((ALEffect=LookupEffect(Device, effect)) == NULL)
alSetError(Context, AL_INVALID_NAME);
else
{
/* Call the appropriate handler */
V(ALEffect,setParamfv)(Context, param, values);
}
UnlockEffectsWrite(Device);
ALCcontext_DecRef(Context);
}
AL_API ALvoid AL_APIENTRY alGetEffecti(ALuint effect, ALenum param, ALint *value)
{
ALCcontext *Context;
ALCdevice *Device;
ALeffect *ALEffect;
Context = GetContextRef();
if(!Context) return;
Device = Context->Device;
LockEffectsRead(Device);
if((ALEffect=LookupEffect(Device, effect)) == NULL)
alSetError(Context, AL_INVALID_NAME);
else
{
if(param == AL_EFFECT_TYPE)
*value = ALEffect->type;
else
{
/* Call the appropriate handler */
V(ALEffect,getParami)(Context, param, value);
}
}
UnlockEffectsRead(Device);
ALCcontext_DecRef(Context);
}
AL_API ALvoid AL_APIENTRY alGetEffectiv(ALuint effect, ALenum param, ALint *values)
{
ALCcontext *Context;
ALCdevice *Device;
ALeffect *ALEffect;
switch(param)
{
case AL_EFFECT_TYPE:
alGetEffecti(effect, param, values);
return;
}
Context = GetContextRef();
if(!Context) return;
Device = Context->Device;
LockEffectsRead(Device);
if((ALEffect=LookupEffect(Device, effect)) == NULL)
alSetError(Context, AL_INVALID_NAME);
else
{
/* Call the appropriate handler */
V(ALEffect,getParamiv)(Context, param, values);
}
UnlockEffectsRead(Device);
ALCcontext_DecRef(Context);
}
AL_API ALvoid AL_APIENTRY alGetEffectf(ALuint effect, ALenum param, ALfloat *value)
{
ALCcontext *Context;
ALCdevice *Device;
ALeffect *ALEffect;
Context = GetContextRef();
if(!Context) return;
Device = Context->Device;
LockEffectsRead(Device);
if((ALEffect=LookupEffect(Device, effect)) == NULL)
alSetError(Context, AL_INVALID_NAME);
else
{
/* Call the appropriate handler */
V(ALEffect,getParamf)(Context, param, value);
}
UnlockEffectsRead(Device);
ALCcontext_DecRef(Context);
}
AL_API ALvoid AL_APIENTRY alGetEffectfv(ALuint effect, ALenum param, ALfloat *values)
{
ALCcontext *Context;
ALCdevice *Device;
ALeffect *ALEffect;
Context = GetContextRef();
if(!Context) return;
Device = Context->Device;
LockEffectsRead(Device);
if((ALEffect=LookupEffect(Device, effect)) == NULL)
alSetError(Context, AL_INVALID_NAME);
else
{
/* Call the appropriate handler */
V(ALEffect,getParamfv)(Context, param, values);
}
UnlockEffectsRead(Device);
ALCcontext_DecRef(Context);
}
ALenum InitEffect(ALeffect *effect)
{
InitEffectParams(effect, AL_EFFECT_NULL);
return AL_NO_ERROR;
}
ALvoid ReleaseALEffects(ALCdevice *device)
{
ALsizei i;
for(i = 0;i < device->EffectMap.size;i++)
{
ALeffect *temp = device->EffectMap.values[i];
device->EffectMap.values[i] = NULL;
// Release effect structure
FreeThunkEntry(temp->id);
memset(temp, 0, sizeof(ALeffect));
al_free(temp);
}
}
static void InitEffectParams(ALeffect *effect, ALenum type)
{
switch(type)
{
case AL_EFFECT_EAXREVERB:
effect->Props.Reverb.Density = AL_EAXREVERB_DEFAULT_DENSITY;
effect->Props.Reverb.Diffusion = AL_EAXREVERB_DEFAULT_DIFFUSION;
effect->Props.Reverb.Gain = AL_EAXREVERB_DEFAULT_GAIN;
effect->Props.Reverb.GainHF = AL_EAXREVERB_DEFAULT_GAINHF;
effect->Props.Reverb.GainLF = AL_EAXREVERB_DEFAULT_GAINLF;
effect->Props.Reverb.DecayTime = AL_EAXREVERB_DEFAULT_DECAY_TIME;
effect->Props.Reverb.DecayHFRatio = AL_EAXREVERB_DEFAULT_DECAY_HFRATIO;
effect->Props.Reverb.DecayLFRatio = AL_EAXREVERB_DEFAULT_DECAY_LFRATIO;
effect->Props.Reverb.ReflectionsGain = AL_EAXREVERB_DEFAULT_REFLECTIONS_GAIN;
effect->Props.Reverb.ReflectionsDelay = AL_EAXREVERB_DEFAULT_REFLECTIONS_DELAY;
effect->Props.Reverb.ReflectionsPan[0] = AL_EAXREVERB_DEFAULT_REFLECTIONS_PAN_XYZ;
effect->Props.Reverb.ReflectionsPan[1] = AL_EAXREVERB_DEFAULT_REFLECTIONS_PAN_XYZ;
effect->Props.Reverb.ReflectionsPan[2] = AL_EAXREVERB_DEFAULT_REFLECTIONS_PAN_XYZ;
effect->Props.Reverb.LateReverbGain = AL_EAXREVERB_DEFAULT_LATE_REVERB_GAIN;
effect->Props.Reverb.LateReverbDelay = AL_EAXREVERB_DEFAULT_LATE_REVERB_DELAY;
effect->Props.Reverb.LateReverbPan[0] = AL_EAXREVERB_DEFAULT_LATE_REVERB_PAN_XYZ;
effect->Props.Reverb.LateReverbPan[1] = AL_EAXREVERB_DEFAULT_LATE_REVERB_PAN_XYZ;
effect->Props.Reverb.LateReverbPan[2] = AL_EAXREVERB_DEFAULT_LATE_REVERB_PAN_XYZ;
effect->Props.Reverb.EchoTime = AL_EAXREVERB_DEFAULT_ECHO_TIME;
effect->Props.Reverb.EchoDepth = AL_EAXREVERB_DEFAULT_ECHO_DEPTH;
effect->Props.Reverb.ModulationTime = AL_EAXREVERB_DEFAULT_MODULATION_TIME;
effect->Props.Reverb.ModulationDepth = AL_EAXREVERB_DEFAULT_MODULATION_DEPTH;
effect->Props.Reverb.AirAbsorptionGainHF = AL_EAXREVERB_DEFAULT_AIR_ABSORPTION_GAINHF;
effect->Props.Reverb.HFReference = AL_EAXREVERB_DEFAULT_HFREFERENCE;
effect->Props.Reverb.LFReference = AL_EAXREVERB_DEFAULT_LFREFERENCE;
effect->Props.Reverb.RoomRolloffFactor = AL_EAXREVERB_DEFAULT_ROOM_ROLLOFF_FACTOR;
effect->Props.Reverb.DecayHFLimit = AL_EAXREVERB_DEFAULT_DECAY_HFLIMIT;
SET_VTABLE1(ALeaxreverb, effect);
break;
case AL_EFFECT_REVERB:
effect->Props.Reverb.Density = AL_REVERB_DEFAULT_DENSITY;
effect->Props.Reverb.Diffusion = AL_REVERB_DEFAULT_DIFFUSION;
effect->Props.Reverb.Gain = AL_REVERB_DEFAULT_GAIN;
effect->Props.Reverb.GainHF = AL_REVERB_DEFAULT_GAINHF;
effect->Props.Reverb.GainLF = 1.0f;
effect->Props.Reverb.DecayTime = AL_REVERB_DEFAULT_DECAY_TIME;
effect->Props.Reverb.DecayHFRatio = AL_REVERB_DEFAULT_DECAY_HFRATIO;
effect->Props.Reverb.DecayLFRatio = 1.0f;
effect->Props.Reverb.ReflectionsGain = AL_REVERB_DEFAULT_REFLECTIONS_GAIN;
effect->Props.Reverb.ReflectionsDelay = AL_REVERB_DEFAULT_REFLECTIONS_DELAY;
effect->Props.Reverb.ReflectionsPan[0] = 0.0f;
effect->Props.Reverb.ReflectionsPan[1] = 0.0f;
effect->Props.Reverb.ReflectionsPan[2] = 0.0f;
effect->Props.Reverb.LateReverbGain = AL_REVERB_DEFAULT_LATE_REVERB_GAIN;
effect->Props.Reverb.LateReverbDelay = AL_REVERB_DEFAULT_LATE_REVERB_DELAY;
effect->Props.Reverb.LateReverbPan[0] = 0.0f;
effect->Props.Reverb.LateReverbPan[1] = 0.0f;
effect->Props.Reverb.LateReverbPan[2] = 0.0f;
effect->Props.Reverb.EchoTime = 0.25f;
effect->Props.Reverb.EchoDepth = 0.0f;
effect->Props.Reverb.ModulationTime = 0.25f;
effect->Props.Reverb.ModulationDepth = 0.0f;
effect->Props.Reverb.AirAbsorptionGainHF = AL_REVERB_DEFAULT_AIR_ABSORPTION_GAINHF;
effect->Props.Reverb.HFReference = 5000.0f;
effect->Props.Reverb.LFReference = 250.0f;
effect->Props.Reverb.RoomRolloffFactor = AL_REVERB_DEFAULT_ROOM_ROLLOFF_FACTOR;
effect->Props.Reverb.DecayHFLimit = AL_REVERB_DEFAULT_DECAY_HFLIMIT;
SET_VTABLE1(ALreverb, effect);
break;
case AL_EFFECT_CHORUS:
effect->Props.Chorus.Waveform = AL_CHORUS_DEFAULT_WAVEFORM;
effect->Props.Chorus.Phase = AL_CHORUS_DEFAULT_PHASE;
effect->Props.Chorus.Rate = AL_CHORUS_DEFAULT_RATE;
effect->Props.Chorus.Depth = AL_CHORUS_DEFAULT_DEPTH;
effect->Props.Chorus.Feedback = AL_CHORUS_DEFAULT_FEEDBACK;
effect->Props.Chorus.Delay = AL_CHORUS_DEFAULT_DELAY;
SET_VTABLE1(ALchorus, effect);
break;
case AL_EFFECT_COMPRESSOR:
effect->Props.Compressor.OnOff = AL_COMPRESSOR_DEFAULT_ONOFF;
SET_VTABLE1(ALcompressor, effect);
break;
case AL_EFFECT_DISTORTION:
effect->Props.Distortion.Edge = AL_DISTORTION_DEFAULT_EDGE;
effect->Props.Distortion.Gain = AL_DISTORTION_DEFAULT_GAIN;
effect->Props.Distortion.LowpassCutoff = AL_DISTORTION_DEFAULT_LOWPASS_CUTOFF;
effect->Props.Distortion.EQCenter = AL_DISTORTION_DEFAULT_EQCENTER;
effect->Props.Distortion.EQBandwidth = AL_DISTORTION_DEFAULT_EQBANDWIDTH;
SET_VTABLE1(ALdistortion, effect);
break;
case AL_EFFECT_ECHO:
effect->Props.Echo.Delay = AL_ECHO_DEFAULT_DELAY;
effect->Props.Echo.LRDelay = AL_ECHO_DEFAULT_LRDELAY;
effect->Props.Echo.Damping = AL_ECHO_DEFAULT_DAMPING;
effect->Props.Echo.Feedback = AL_ECHO_DEFAULT_FEEDBACK;
effect->Props.Echo.Spread = AL_ECHO_DEFAULT_SPREAD;
SET_VTABLE1(ALecho, effect);
break;
case AL_EFFECT_EQUALIZER:
effect->Props.Equalizer.LowCutoff = AL_EQUALIZER_DEFAULT_LOW_CUTOFF;
effect->Props.Equalizer.LowGain = AL_EQUALIZER_DEFAULT_LOW_GAIN;
effect->Props.Equalizer.Mid1Center = AL_EQUALIZER_DEFAULT_MID1_CENTER;
effect->Props.Equalizer.Mid1Gain = AL_EQUALIZER_DEFAULT_MID1_GAIN;
effect->Props.Equalizer.Mid1Width = AL_EQUALIZER_DEFAULT_MID1_WIDTH;
effect->Props.Equalizer.Mid2Center = AL_EQUALIZER_DEFAULT_MID2_CENTER;
effect->Props.Equalizer.Mid2Gain = AL_EQUALIZER_DEFAULT_MID2_GAIN;
effect->Props.Equalizer.Mid2Width = AL_EQUALIZER_DEFAULT_MID2_WIDTH;
effect->Props.Equalizer.HighCutoff = AL_EQUALIZER_DEFAULT_HIGH_CUTOFF;
effect->Props.Equalizer.HighGain = AL_EQUALIZER_DEFAULT_HIGH_GAIN;
SET_VTABLE1(ALequalizer, effect);
break;
case AL_EFFECT_FLANGER:
effect->Props.Flanger.Waveform = AL_FLANGER_DEFAULT_WAVEFORM;
effect->Props.Flanger.Phase = AL_FLANGER_DEFAULT_PHASE;
effect->Props.Flanger.Rate = AL_FLANGER_DEFAULT_RATE;
effect->Props.Flanger.Depth = AL_FLANGER_DEFAULT_DEPTH;
effect->Props.Flanger.Feedback = AL_FLANGER_DEFAULT_FEEDBACK;
effect->Props.Flanger.Delay = AL_FLANGER_DEFAULT_DELAY;
SET_VTABLE1(ALflanger, effect);
break;
case AL_EFFECT_RING_MODULATOR:
effect->Props.Modulator.Frequency = AL_RING_MODULATOR_DEFAULT_FREQUENCY;
effect->Props.Modulator.HighPassCutoff = AL_RING_MODULATOR_DEFAULT_HIGHPASS_CUTOFF;
effect->Props.Modulator.Waveform = AL_RING_MODULATOR_DEFAULT_WAVEFORM;
SET_VTABLE1(ALmodulator, effect);
break;
case AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT:
case AL_EFFECT_DEDICATED_DIALOGUE:
effect->Props.Dedicated.Gain = 1.0f;
SET_VTABLE1(ALdedicated, effect);
break;
default:
SET_VTABLE1(ALnull, effect);
break;
}
effect->type = type;
}
#include "AL/efx-presets.h"
#define DECL(x) { #x, EFX_REVERB_PRESET_##x }
static const struct {
const char name[32];
EFXEAXREVERBPROPERTIES props;
} reverblist[] = {
DECL(GENERIC),
DECL(PADDEDCELL),
DECL(ROOM),
DECL(BATHROOM),
DECL(LIVINGROOM),
DECL(STONEROOM),
DECL(AUDITORIUM),
DECL(CONCERTHALL),
DECL(CAVE),
DECL(ARENA),
DECL(HANGAR),
DECL(CARPETEDHALLWAY),
DECL(HALLWAY),
DECL(STONECORRIDOR),
DECL(ALLEY),
DECL(FOREST),
DECL(CITY),
DECL(MOUNTAINS),
DECL(QUARRY),
DECL(PLAIN),
DECL(PARKINGLOT),
DECL(SEWERPIPE),
DECL(UNDERWATER),
DECL(DRUGGED),
DECL(DIZZY),
DECL(PSYCHOTIC),
DECL(CASTLE_SMALLROOM),
DECL(CASTLE_SHORTPASSAGE),
DECL(CASTLE_MEDIUMROOM),
DECL(CASTLE_LARGEROOM),
DECL(CASTLE_LONGPASSAGE),
DECL(CASTLE_HALL),
DECL(CASTLE_CUPBOARD),
DECL(CASTLE_COURTYARD),
DECL(CASTLE_ALCOVE),
DECL(FACTORY_SMALLROOM),
DECL(FACTORY_SHORTPASSAGE),
DECL(FACTORY_MEDIUMROOM),
DECL(FACTORY_LARGEROOM),
DECL(FACTORY_LONGPASSAGE),
DECL(FACTORY_HALL),
DECL(FACTORY_CUPBOARD),
DECL(FACTORY_COURTYARD),
DECL(FACTORY_ALCOVE),
DECL(ICEPALACE_SMALLROOM),
DECL(ICEPALACE_SHORTPASSAGE),
DECL(ICEPALACE_MEDIUMROOM),
DECL(ICEPALACE_LARGEROOM),
DECL(ICEPALACE_LONGPASSAGE),
DECL(ICEPALACE_HALL),
DECL(ICEPALACE_CUPBOARD),
DECL(ICEPALACE_COURTYARD),
DECL(ICEPALACE_ALCOVE),
DECL(SPACESTATION_SMALLROOM),
DECL(SPACESTATION_SHORTPASSAGE),
DECL(SPACESTATION_MEDIUMROOM),
DECL(SPACESTATION_LARGEROOM),
DECL(SPACESTATION_LONGPASSAGE),
DECL(SPACESTATION_HALL),
DECL(SPACESTATION_CUPBOARD),
DECL(SPACESTATION_ALCOVE),
DECL(WOODEN_SMALLROOM),
DECL(WOODEN_SHORTPASSAGE),
DECL(WOODEN_MEDIUMROOM),
DECL(WOODEN_LARGEROOM),
DECL(WOODEN_LONGPASSAGE),
DECL(WOODEN_HALL),
DECL(WOODEN_CUPBOARD),
DECL(WOODEN_COURTYARD),
DECL(WOODEN_ALCOVE),
DECL(SPORT_EMPTYSTADIUM),
DECL(SPORT_SQUASHCOURT),
DECL(SPORT_SMALLSWIMMINGPOOL),
DECL(SPORT_LARGESWIMMINGPOOL),
DECL(SPORT_GYMNASIUM),
DECL(SPORT_FULLSTADIUM),
DECL(SPORT_STADIUMTANNOY),
DECL(PREFAB_WORKSHOP),
DECL(PREFAB_SCHOOLROOM),
DECL(PREFAB_PRACTISEROOM),
DECL(PREFAB_OUTHOUSE),
DECL(PREFAB_CARAVAN),
DECL(DOME_TOMB),
DECL(PIPE_SMALL),
DECL(DOME_SAINTPAULS),
DECL(PIPE_LONGTHIN),
DECL(PIPE_LARGE),
DECL(PIPE_RESONANT),
DECL(OUTDOORS_BACKYARD),
DECL(OUTDOORS_ROLLINGPLAINS),
DECL(OUTDOORS_DEEPCANYON),
DECL(OUTDOORS_CREEK),
DECL(OUTDOORS_VALLEY),
DECL(MOOD_HEAVEN),
DECL(MOOD_HELL),
DECL(MOOD_MEMORY),
DECL(DRIVING_COMMENTATOR),
DECL(DRIVING_PITGARAGE),
DECL(DRIVING_INCAR_RACER),
DECL(DRIVING_INCAR_SPORTS),
DECL(DRIVING_INCAR_LUXURY),
DECL(DRIVING_FULLGRANDSTAND),
DECL(DRIVING_EMPTYGRANDSTAND),
DECL(DRIVING_TUNNEL),
DECL(CITY_STREETS),
DECL(CITY_SUBWAY),
DECL(CITY_MUSEUM),
DECL(CITY_LIBRARY),
DECL(CITY_UNDERPASS),
DECL(CITY_ABANDONED),
DECL(DUSTYROOM),
DECL(CHAPEL),
DECL(SMALLWATERROOM),
};
#undef DECL
ALvoid LoadReverbPreset(const char *name, ALeffect *effect)
{
size_t i;
if(strcasecmp(name, "NONE") == 0)
{
InitEffectParams(effect, AL_EFFECT_NULL);
TRACE("Loading reverb '%s'\n", "NONE");
return;
}
if(!DisabledEffects[AL__EAXREVERB])
InitEffectParams(effect, AL_EFFECT_EAXREVERB);
else if(!DisabledEffects[AL__REVERB])
InitEffectParams(effect, AL_EFFECT_REVERB);
else
InitEffectParams(effect, AL_EFFECT_NULL);
for(i = 0;i < COUNTOF(reverblist);i++)
{
const EFXEAXREVERBPROPERTIES *props;
if(strcasecmp(name, reverblist[i].name) != 0)
continue;
TRACE("Loading reverb '%s'\n", reverblist[i].name);
props = &reverblist[i].props;
effect->Props.Reverb.Density = props->flDensity;
effect->Props.Reverb.Diffusion = props->flDiffusion;
effect->Props.Reverb.Gain = props->flGain;
effect->Props.Reverb.GainHF = props->flGainHF;
effect->Props.Reverb.GainLF = props->flGainLF;
effect->Props.Reverb.DecayTime = props->flDecayTime;
effect->Props.Reverb.DecayHFRatio = props->flDecayHFRatio;
effect->Props.Reverb.DecayLFRatio = props->flDecayLFRatio;
effect->Props.Reverb.ReflectionsGain = props->flReflectionsGain;
effect->Props.Reverb.ReflectionsDelay = props->flReflectionsDelay;
effect->Props.Reverb.ReflectionsPan[0] = props->flReflectionsPan[0];
effect->Props.Reverb.ReflectionsPan[1] = props->flReflectionsPan[1];
effect->Props.Reverb.ReflectionsPan[2] = props->flReflectionsPan[2];
effect->Props.Reverb.LateReverbGain = props->flLateReverbGain;
effect->Props.Reverb.LateReverbDelay = props->flLateReverbDelay;
effect->Props.Reverb.LateReverbPan[0] = props->flLateReverbPan[0];
effect->Props.Reverb.LateReverbPan[1] = props->flLateReverbPan[1];
effect->Props.Reverb.LateReverbPan[2] = props->flLateReverbPan[2];
effect->Props.Reverb.EchoTime = props->flEchoTime;
effect->Props.Reverb.EchoDepth = props->flEchoDepth;
effect->Props.Reverb.ModulationTime = props->flModulationTime;
effect->Props.Reverb.ModulationDepth = props->flModulationDepth;
effect->Props.Reverb.AirAbsorptionGainHF = props->flAirAbsorptionGainHF;
effect->Props.Reverb.HFReference = props->flHFReference;
effect->Props.Reverb.LFReference = props->flLFReference;
effect->Props.Reverb.RoomRolloffFactor = props->flRoomRolloffFactor;
effect->Props.Reverb.DecayHFLimit = props->iDecayHFLimit;
return;
}
WARN("Reverb preset '%s' not found\n", name);
}
+82
View File
@@ -0,0 +1,82 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 1999-2000 by authors.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include <signal.h>
#ifdef HAVE_WINDOWS_H
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif
#include "alMain.h"
#include "AL/alc.h"
#include "alError.h"
ALboolean TrapALError = AL_FALSE;
ALvoid alSetError(ALCcontext *Context, ALenum errorCode)
{
ALenum curerr = AL_NO_ERROR;
WARN("Error generated on context %p, code 0x%04x\n", Context, errorCode);
if(TrapALError)
{
#ifdef _WIN32
/* DebugBreak will cause an exception if there is no debugger */
if(IsDebuggerPresent())
DebugBreak();
#elif defined(SIGTRAP)
raise(SIGTRAP);
#endif
}
(void)(ATOMIC_COMPARE_EXCHANGE_STRONG_SEQ(&Context->LastError, &curerr, errorCode));
}
AL_API ALenum AL_APIENTRY alGetError(void)
{
ALCcontext *Context;
ALenum errorCode;
Context = GetContextRef();
if(!Context)
{
WARN("Querying error state on null context (implicitly 0x%04x)\n",
AL_INVALID_OPERATION);
if(TrapALError)
{
#ifdef _WIN32
if(IsDebuggerPresent())
DebugBreak();
#elif defined(SIGTRAP)
raise(SIGTRAP);
#endif
}
return AL_INVALID_OPERATION;
}
errorCode = ATOMIC_EXCHANGE_SEQ(&Context->LastError, AL_NO_ERROR);
ALCcontext_DecRef(Context);
return errorCode;
}
+103
View File
@@ -0,0 +1,103 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 1999-2007 by authors.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "alError.h"
#include "alMain.h"
#include "alFilter.h"
#include "alEffect.h"
#include "alAuxEffectSlot.h"
#include "alSource.h"
#include "alBuffer.h"
#include "AL/al.h"
#include "AL/alc.h"
const struct EffectList EffectList[] = {
{ "eaxreverb", 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 }
};
AL_API ALboolean AL_APIENTRY alIsExtensionPresent(const ALchar *extName)
{
ALboolean ret = AL_FALSE;
ALCcontext *context;
const char *ptr;
size_t len;
context = GetContextRef();
if(!context) return AL_FALSE;
if(!(extName))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
len = strlen(extName);
ptr = context->ExtensionList;
while(ptr && *ptr)
{
if(strncasecmp(ptr, extName, len) == 0 &&
(ptr[len] == '\0' || isspace(ptr[len])))
{
ret = AL_TRUE;
break;
}
if((ptr=strchr(ptr, ' ')) != NULL)
{
do {
++ptr;
} while(isspace(*ptr));
}
}
done:
ALCcontext_DecRef(context);
return ret;
}
AL_API ALvoid* AL_APIENTRY alGetProcAddress(const ALchar *funcName)
{
if(!funcName)
return NULL;
return alcGetProcAddress(NULL, funcName);
}
AL_API ALenum AL_APIENTRY alGetEnumValue(const ALchar *enumName)
{
if(!enumName)
return (ALenum)0;
return alcGetEnumValue(NULL, enumName);
}
+719
View File
@@ -0,0 +1,719 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 1999-2007 by authors.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include <stdlib.h>
#include "alMain.h"
#include "alu.h"
#include "alFilter.h"
#include "alThunk.h"
#include "alError.h"
extern inline void LockFiltersRead(ALCdevice *device);
extern inline void UnlockFiltersRead(ALCdevice *device);
extern inline void LockFiltersWrite(ALCdevice *device);
extern inline void UnlockFiltersWrite(ALCdevice *device);
extern inline struct ALfilter *LookupFilter(ALCdevice *device, ALuint id);
extern inline struct ALfilter *RemoveFilter(ALCdevice *device, ALuint id);
extern inline void ALfilterState_clear(ALfilterState *filter);
extern inline void ALfilterState_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);
static void InitFilterParams(ALfilter *filter, ALenum type);
AL_API ALvoid AL_APIENTRY alGenFilters(ALsizei n, ALuint *filters)
{
ALCdevice *device;
ALCcontext *context;
ALsizei cur = 0;
ALenum err;
context = GetContextRef();
if(!context) return;
if(!(n >= 0))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
device = context->Device;
for(cur = 0;cur < n;cur++)
{
ALfilter *filter = al_calloc(16, sizeof(ALfilter));
if(!filter)
{
alDeleteFilters(cur, filters);
SET_ERROR_AND_GOTO(context, AL_OUT_OF_MEMORY, done);
}
InitFilterParams(filter, AL_FILTER_NULL);
err = NewThunkEntry(&filter->id);
if(err == AL_NO_ERROR)
err = InsertUIntMapEntry(&device->FilterMap, filter->id, filter);
if(err != AL_NO_ERROR)
{
FreeThunkEntry(filter->id);
memset(filter, 0, sizeof(ALfilter));
al_free(filter);
alDeleteFilters(cur, filters);
SET_ERROR_AND_GOTO(context, err, done);
}
filters[cur] = filter->id;
}
done:
ALCcontext_DecRef(context);
}
AL_API ALvoid AL_APIENTRY alDeleteFilters(ALsizei n, const ALuint *filters)
{
ALCdevice *device;
ALCcontext *context;
ALfilter *filter;
ALsizei i;
context = GetContextRef();
if(!context) return;
device = context->Device;
LockFiltersWrite(device);
if(!(n >= 0))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
for(i = 0;i < n;i++)
{
if(filters[i] && LookupFilter(device, filters[i]) == NULL)
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
}
for(i = 0;i < n;i++)
{
if((filter=RemoveFilter(device, filters[i])) == NULL)
continue;
FreeThunkEntry(filter->id);
memset(filter, 0, sizeof(*filter));
al_free(filter);
}
done:
UnlockFiltersWrite(device);
ALCcontext_DecRef(context);
}
AL_API ALboolean AL_APIENTRY alIsFilter(ALuint filter)
{
ALCcontext *Context;
ALboolean result;
Context = GetContextRef();
if(!Context) return AL_FALSE;
LockFiltersRead(Context->Device);
result = ((!filter || LookupFilter(Context->Device, filter)) ?
AL_TRUE : AL_FALSE);
UnlockFiltersRead(Context->Device);
ALCcontext_DecRef(Context);
return result;
}
AL_API ALvoid AL_APIENTRY alFilteri(ALuint filter, ALenum param, ALint value)
{
ALCcontext *Context;
ALCdevice *Device;
ALfilter *ALFilter;
Context = GetContextRef();
if(!Context) return;
Device = Context->Device;
LockFiltersWrite(Device);
if((ALFilter=LookupFilter(Device, filter)) == NULL)
alSetError(Context, AL_INVALID_NAME);
else
{
if(param == AL_FILTER_TYPE)
{
if(value == AL_FILTER_NULL || value == AL_FILTER_LOWPASS ||
value == AL_FILTER_HIGHPASS || value == AL_FILTER_BANDPASS)
InitFilterParams(ALFilter, value);
else
alSetError(Context, AL_INVALID_VALUE);
}
else
{
/* Call the appropriate handler */
ALfilter_SetParami(ALFilter, Context, param, value);
}
}
UnlockFiltersWrite(Device);
ALCcontext_DecRef(Context);
}
AL_API ALvoid AL_APIENTRY alFilteriv(ALuint filter, ALenum param, const ALint *values)
{
ALCcontext *Context;
ALCdevice *Device;
ALfilter *ALFilter;
switch(param)
{
case AL_FILTER_TYPE:
alFilteri(filter, param, values[0]);
return;
}
Context = GetContextRef();
if(!Context) return;
Device = Context->Device;
LockFiltersWrite(Device);
if((ALFilter=LookupFilter(Device, filter)) == NULL)
alSetError(Context, AL_INVALID_NAME);
else
{
/* Call the appropriate handler */
ALfilter_SetParamiv(ALFilter, Context, param, values);
}
UnlockFiltersWrite(Device);
ALCcontext_DecRef(Context);
}
AL_API ALvoid AL_APIENTRY alFilterf(ALuint filter, ALenum param, ALfloat value)
{
ALCcontext *Context;
ALCdevice *Device;
ALfilter *ALFilter;
Context = GetContextRef();
if(!Context) return;
Device = Context->Device;
LockFiltersWrite(Device);
if((ALFilter=LookupFilter(Device, filter)) == NULL)
alSetError(Context, AL_INVALID_NAME);
else
{
/* Call the appropriate handler */
ALfilter_SetParamf(ALFilter, Context, param, value);
}
UnlockFiltersWrite(Device);
ALCcontext_DecRef(Context);
}
AL_API ALvoid AL_APIENTRY alFilterfv(ALuint filter, ALenum param, const ALfloat *values)
{
ALCcontext *Context;
ALCdevice *Device;
ALfilter *ALFilter;
Context = GetContextRef();
if(!Context) return;
Device = Context->Device;
LockFiltersWrite(Device);
if((ALFilter=LookupFilter(Device, filter)) == NULL)
alSetError(Context, AL_INVALID_NAME);
else
{
/* Call the appropriate handler */
ALfilter_SetParamfv(ALFilter, Context, param, values);
}
UnlockFiltersWrite(Device);
ALCcontext_DecRef(Context);
}
AL_API ALvoid AL_APIENTRY alGetFilteri(ALuint filter, ALenum param, ALint *value)
{
ALCcontext *Context;
ALCdevice *Device;
ALfilter *ALFilter;
Context = GetContextRef();
if(!Context) return;
Device = Context->Device;
LockFiltersRead(Device);
if((ALFilter=LookupFilter(Device, filter)) == NULL)
alSetError(Context, AL_INVALID_NAME);
else
{
if(param == AL_FILTER_TYPE)
*value = ALFilter->type;
else
{
/* Call the appropriate handler */
ALfilter_GetParami(ALFilter, Context, param, value);
}
}
UnlockFiltersRead(Device);
ALCcontext_DecRef(Context);
}
AL_API ALvoid AL_APIENTRY alGetFilteriv(ALuint filter, ALenum param, ALint *values)
{
ALCcontext *Context;
ALCdevice *Device;
ALfilter *ALFilter;
switch(param)
{
case AL_FILTER_TYPE:
alGetFilteri(filter, param, values);
return;
}
Context = GetContextRef();
if(!Context) return;
Device = Context->Device;
LockFiltersRead(Device);
if((ALFilter=LookupFilter(Device, filter)) == NULL)
alSetError(Context, AL_INVALID_NAME);
else
{
/* Call the appropriate handler */
ALfilter_GetParamiv(ALFilter, Context, param, values);
}
UnlockFiltersRead(Device);
ALCcontext_DecRef(Context);
}
AL_API ALvoid AL_APIENTRY alGetFilterf(ALuint filter, ALenum param, ALfloat *value)
{
ALCcontext *Context;
ALCdevice *Device;
ALfilter *ALFilter;
Context = GetContextRef();
if(!Context) return;
Device = Context->Device;
LockFiltersRead(Device);
if((ALFilter=LookupFilter(Device, filter)) == NULL)
alSetError(Context, AL_INVALID_NAME);
else
{
/* Call the appropriate handler */
ALfilter_GetParamf(ALFilter, Context, param, value);
}
UnlockFiltersRead(Device);
ALCcontext_DecRef(Context);
}
AL_API ALvoid AL_APIENTRY alGetFilterfv(ALuint filter, ALenum param, ALfloat *values)
{
ALCcontext *Context;
ALCdevice *Device;
ALfilter *ALFilter;
Context = GetContextRef();
if(!Context) return;
Device = Context->Device;
LockFiltersRead(Device);
if((ALFilter=LookupFilter(Device, filter)) == NULL)
alSetError(Context, AL_INVALID_NAME);
else
{
/* Call the appropriate handler */
ALfilter_GetParamfv(ALFilter, Context, param, values);
}
UnlockFiltersRead(Device);
ALCcontext_DecRef(Context);
}
void ALfilterState_setParams(ALfilterState *filter, ALfilterType type, ALfloat gain, ALfloat freq_mult, ALfloat rcpQ)
{
ALfloat alpha, sqrtgain_alpha_2;
ALfloat w0, sin_w0, cos_w0;
ALfloat a[3] = { 1.0f, 0.0f, 0.0f };
ALfloat b[3] = { 1.0f, 0.0f, 0.0f };
// Limit gain to -100dB
assert(gain > 0.00001f);
w0 = F_TAU * freq_mult;
sin_w0 = sinf(w0);
cos_w0 = cosf(w0);
alpha = sin_w0/2.0f * rcpQ;
/* Calculate filter coefficients depending on filter type */
switch(type)
{
case ALfilterType_HighShelf:
sqrtgain_alpha_2 = 2.0f * sqrtf(gain) * alpha;
b[0] = gain*((gain+1.0f) + (gain-1.0f)*cos_w0 + sqrtgain_alpha_2);
b[1] = -2.0f*gain*((gain-1.0f) + (gain+1.0f)*cos_w0 );
b[2] = gain*((gain+1.0f) + (gain-1.0f)*cos_w0 - sqrtgain_alpha_2);
a[0] = (gain+1.0f) - (gain-1.0f)*cos_w0 + sqrtgain_alpha_2;
a[1] = 2.0f* ((gain-1.0f) - (gain+1.0f)*cos_w0 );
a[2] = (gain+1.0f) - (gain-1.0f)*cos_w0 - sqrtgain_alpha_2;
break;
case ALfilterType_LowShelf:
sqrtgain_alpha_2 = 2.0f * sqrtf(gain) * alpha;
b[0] = gain*((gain+1.0f) - (gain-1.0f)*cos_w0 + sqrtgain_alpha_2);
b[1] = 2.0f*gain*((gain-1.0f) - (gain+1.0f)*cos_w0 );
b[2] = gain*((gain+1.0f) - (gain-1.0f)*cos_w0 - sqrtgain_alpha_2);
a[0] = (gain+1.0f) + (gain-1.0f)*cos_w0 + sqrtgain_alpha_2;
a[1] = -2.0f* ((gain-1.0f) + (gain+1.0f)*cos_w0 );
a[2] = (gain+1.0f) + (gain-1.0f)*cos_w0 - sqrtgain_alpha_2;
break;
case ALfilterType_Peaking:
gain = sqrtf(gain);
b[0] = 1.0f + alpha * gain;
b[1] = -2.0f * cos_w0;
b[2] = 1.0f - alpha * gain;
a[0] = 1.0f + alpha / gain;
a[1] = -2.0f * cos_w0;
a[2] = 1.0f - alpha / gain;
break;
case ALfilterType_LowPass:
b[0] = (1.0f - cos_w0) / 2.0f;
b[1] = 1.0f - cos_w0;
b[2] = (1.0f - cos_w0) / 2.0f;
a[0] = 1.0f + alpha;
a[1] = -2.0f * cos_w0;
a[2] = 1.0f - alpha;
break;
case ALfilterType_HighPass:
b[0] = (1.0f + cos_w0) / 2.0f;
b[1] = -(1.0f + cos_w0);
b[2] = (1.0f + cos_w0) / 2.0f;
a[0] = 1.0f + alpha;
a[1] = -2.0f * cos_w0;
a[2] = 1.0f - alpha;
break;
case ALfilterType_BandPass:
b[0] = alpha;
b[1] = 0;
b[2] = -alpha;
a[0] = 1.0f + alpha;
a[1] = -2.0f * cos_w0;
a[2] = 1.0f - alpha;
break;
}
filter->a1 = a[1] / a[0];
filter->a2 = a[2] / a[0];
filter->b0 = b[0] / a[0];
filter->b1 = b[1] / a[0];
filter->b2 = b[2] / a[0];
}
static void lp_SetParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
static void lp_SetParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), const ALint *UNUSED(vals))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
static void lp_SetParamf(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat val)
{
switch(param)
{
case AL_LOWPASS_GAIN:
if(!(val >= AL_LOWPASS_MIN_GAIN && val <= AL_LOWPASS_MAX_GAIN))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
filter->Gain = val;
break;
case AL_LOWPASS_GAINHF:
if(!(val >= AL_LOWPASS_MIN_GAINHF && val <= AL_LOWPASS_MAX_GAINHF))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
filter->GainHF = val;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
static void lp_SetParamfv(ALfilter *filter, ALCcontext *context, ALenum param, const ALfloat *vals)
{
lp_SetParamf(filter, context, param, vals[0]);
}
static void lp_GetParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
static void lp_GetParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(vals))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
static void lp_GetParamf(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *val)
{
switch(param)
{
case AL_LOWPASS_GAIN:
*val = filter->Gain;
break;
case AL_LOWPASS_GAINHF:
*val = filter->GainHF;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
static void lp_GetParamfv(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *vals)
{
lp_GetParamf(filter, context, param, vals);
}
static void hp_SetParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
static void hp_SetParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), const ALint *UNUSED(vals))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
static void hp_SetParamf(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat val)
{
switch(param)
{
case AL_HIGHPASS_GAIN:
if(!(val >= AL_HIGHPASS_MIN_GAIN && val <= AL_HIGHPASS_MAX_GAIN))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
filter->Gain = val;
break;
case AL_HIGHPASS_GAINLF:
if(!(val >= AL_HIGHPASS_MIN_GAINLF && val <= AL_HIGHPASS_MAX_GAINLF))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
filter->GainLF = val;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
static void hp_SetParamfv(ALfilter *filter, ALCcontext *context, ALenum param, const ALfloat *vals)
{
hp_SetParamf(filter, context, param, vals[0]);
}
static void hp_GetParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
static void hp_GetParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(vals))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
static void hp_GetParamf(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *val)
{
switch(param)
{
case AL_HIGHPASS_GAIN:
*val = filter->Gain;
break;
case AL_HIGHPASS_GAINLF:
*val = filter->GainLF;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
static void hp_GetParamfv(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *vals)
{
hp_GetParamf(filter, context, param, vals);
}
static void bp_SetParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
static void bp_SetParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), const ALint *UNUSED(vals))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
static void bp_SetParamf(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat val)
{
switch(param)
{
case AL_BANDPASS_GAIN:
if(!(val >= AL_BANDPASS_MIN_GAIN && val <= AL_BANDPASS_MAX_GAIN))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
filter->Gain = val;
break;
case AL_BANDPASS_GAINHF:
if(!(val >= AL_BANDPASS_MIN_GAINHF && val <= AL_BANDPASS_MAX_GAINHF))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
filter->GainHF = val;
break;
case AL_BANDPASS_GAINLF:
if(!(val >= AL_BANDPASS_MIN_GAINLF && val <= AL_BANDPASS_MAX_GAINLF))
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
filter->GainLF = val;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
static void bp_SetParamfv(ALfilter *filter, ALCcontext *context, ALenum param, const ALfloat *vals)
{
bp_SetParamf(filter, context, param, vals[0]);
}
static void bp_GetParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
static void bp_GetParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(vals))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
static void bp_GetParamf(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *val)
{
switch(param)
{
case AL_BANDPASS_GAIN:
*val = filter->Gain;
break;
case AL_BANDPASS_GAINHF:
*val = filter->GainHF;
break;
case AL_BANDPASS_GAINLF:
*val = filter->GainLF;
break;
default:
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
}
}
static void bp_GetParamfv(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *vals)
{
bp_GetParamf(filter, context, param, vals);
}
static void null_SetParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
static void null_SetParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), const ALint *UNUSED(vals))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
static void null_SetParamf(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALfloat UNUSED(val))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
static void null_SetParamfv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), const ALfloat *UNUSED(vals))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
static void null_GetParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
static void null_GetParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(vals))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
static void null_GetParamf(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALfloat *UNUSED(val))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
static void null_GetParamfv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALfloat *UNUSED(vals))
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
ALvoid ReleaseALFilters(ALCdevice *device)
{
ALsizei i;
for(i = 0;i < device->FilterMap.size;i++)
{
ALfilter *temp = device->FilterMap.values[i];
device->FilterMap.values[i] = NULL;
// Release filter structure
FreeThunkEntry(temp->id);
memset(temp, 0, sizeof(ALfilter));
al_free(temp);
}
}
static void InitFilterParams(ALfilter *filter, ALenum type)
{
if(type == AL_FILTER_LOWPASS)
{
filter->Gain = AL_LOWPASS_DEFAULT_GAIN;
filter->GainHF = AL_LOWPASS_DEFAULT_GAINHF;
filter->HFReference = LOWPASSFREQREF;
filter->GainLF = 1.0f;
filter->LFReference = HIGHPASSFREQREF;
filter->SetParami = lp_SetParami;
filter->SetParamiv = lp_SetParamiv;
filter->SetParamf = lp_SetParamf;
filter->SetParamfv = lp_SetParamfv;
filter->GetParami = lp_GetParami;
filter->GetParamiv = lp_GetParamiv;
filter->GetParamf = lp_GetParamf;
filter->GetParamfv = lp_GetParamfv;
}
else if(type == AL_FILTER_HIGHPASS)
{
filter->Gain = AL_HIGHPASS_DEFAULT_GAIN;
filter->GainHF = 1.0f;
filter->HFReference = LOWPASSFREQREF;
filter->GainLF = AL_HIGHPASS_DEFAULT_GAINLF;
filter->LFReference = HIGHPASSFREQREF;
filter->SetParami = hp_SetParami;
filter->SetParamiv = hp_SetParamiv;
filter->SetParamf = hp_SetParamf;
filter->SetParamfv = hp_SetParamfv;
filter->GetParami = hp_GetParami;
filter->GetParamiv = hp_GetParamiv;
filter->GetParamf = hp_GetParamf;
filter->GetParamfv = hp_GetParamfv;
}
else if(type == AL_FILTER_BANDPASS)
{
filter->Gain = AL_BANDPASS_DEFAULT_GAIN;
filter->GainHF = AL_BANDPASS_DEFAULT_GAINHF;
filter->HFReference = LOWPASSFREQREF;
filter->GainLF = AL_BANDPASS_DEFAULT_GAINLF;
filter->LFReference = HIGHPASSFREQREF;
filter->SetParami = bp_SetParami;
filter->SetParamiv = bp_SetParamiv;
filter->SetParamf = bp_SetParamf;
filter->SetParamfv = bp_SetParamfv;
filter->GetParami = bp_GetParami;
filter->GetParamiv = bp_GetParamiv;
filter->GetParamf = bp_GetParamf;
filter->GetParamfv = bp_GetParamfv;
}
else
{
filter->Gain = 1.0f;
filter->GainHF = 1.0f;
filter->HFReference = LOWPASSFREQREF;
filter->GainLF = 1.0f;
filter->LFReference = HIGHPASSFREQREF;
filter->SetParami = null_SetParami;
filter->SetParamiv = null_SetParamiv;
filter->SetParamf = null_SetParamf;
filter->SetParamfv = null_SetParamfv;
filter->GetParami = null_GetParami;
filter->GetParamiv = null_GetParamiv;
filter->GetParamf = null_GetParamf;
filter->GetParamfv = null_GetParamfv;
}
filter->type = type;
}
+510
View File
@@ -0,0 +1,510 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 1999-2000 by authors.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include "alMain.h"
#include "AL/alc.h"
#include "alError.h"
#include "alListener.h"
#include "alSource.h"
AL_API ALvoid AL_APIENTRY alListenerf(ALenum param, ALfloat value)
{
ALCcontext *context;
context = GetContextRef();
if(!context) return;
WriteLock(&context->PropLock);
switch(param)
{
case AL_GAIN:
if(!(value >= 0.0f && isfinite(value)))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
context->Listener->Gain = value;
break;
case AL_METERS_PER_UNIT:
if(!(value >= 0.0f && isfinite(value)))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
context->Listener->MetersPerUnit = value;
break;
default:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
UpdateListenerProps(context);
done:
WriteUnlock(&context->PropLock);
ALCcontext_DecRef(context);
}
AL_API ALvoid AL_APIENTRY alListener3f(ALenum param, ALfloat value1, ALfloat value2, ALfloat value3)
{
ALCcontext *context;
context = GetContextRef();
if(!context) return;
WriteLock(&context->PropLock);
switch(param)
{
case AL_POSITION:
if(!(isfinite(value1) && isfinite(value2) && isfinite(value3)))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
context->Listener->Position[0] = value1;
context->Listener->Position[1] = value2;
context->Listener->Position[2] = value3;
break;
case AL_VELOCITY:
if(!(isfinite(value1) && isfinite(value2) && isfinite(value3)))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
context->Listener->Velocity[0] = value1;
context->Listener->Velocity[1] = value2;
context->Listener->Velocity[2] = value3;
break;
default:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
UpdateListenerProps(context);
done:
WriteUnlock(&context->PropLock);
ALCcontext_DecRef(context);
}
AL_API ALvoid AL_APIENTRY alListenerfv(ALenum param, const ALfloat *values)
{
ALCcontext *context;
if(values)
{
switch(param)
{
case AL_GAIN:
case AL_METERS_PER_UNIT:
alListenerf(param, values[0]);
return;
case AL_POSITION:
case AL_VELOCITY:
alListener3f(param, values[0], values[1], values[2]);
return;
}
}
context = GetContextRef();
if(!context) return;
WriteLock(&context->PropLock);
if(!(values))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
switch(param)
{
case AL_ORIENTATION:
if(!(isfinite(values[0]) && isfinite(values[1]) && isfinite(values[2]) &&
isfinite(values[3]) && isfinite(values[4]) && isfinite(values[5])))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
/* AT then UP */
context->Listener->Forward[0] = values[0];
context->Listener->Forward[1] = values[1];
context->Listener->Forward[2] = values[2];
context->Listener->Up[0] = values[3];
context->Listener->Up[1] = values[4];
context->Listener->Up[2] = values[5];
break;
default:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
UpdateListenerProps(context);
done:
WriteUnlock(&context->PropLock);
ALCcontext_DecRef(context);
}
AL_API ALvoid AL_APIENTRY alListeneri(ALenum param, ALint UNUSED(value))
{
ALCcontext *context;
context = GetContextRef();
if(!context) return;
WriteLock(&context->PropLock);
switch(param)
{
default:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
UpdateListenerProps(context);
done:
WriteUnlock(&context->PropLock);
ALCcontext_DecRef(context);
}
AL_API void AL_APIENTRY alListener3i(ALenum param, ALint value1, ALint value2, ALint value3)
{
ALCcontext *context;
switch(param)
{
case AL_POSITION:
case AL_VELOCITY:
alListener3f(param, (ALfloat)value1, (ALfloat)value2, (ALfloat)value3);
return;
}
context = GetContextRef();
if(!context) return;
WriteLock(&context->PropLock);
switch(param)
{
default:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
UpdateListenerProps(context);
done:
WriteUnlock(&context->PropLock);
ALCcontext_DecRef(context);
}
AL_API void AL_APIENTRY alListeneriv(ALenum param, const ALint *values)
{
ALCcontext *context;
if(values)
{
ALfloat fvals[6];
switch(param)
{
case AL_POSITION:
case AL_VELOCITY:
alListener3f(param, (ALfloat)values[0], (ALfloat)values[1], (ALfloat)values[2]);
return;
case AL_ORIENTATION:
fvals[0] = (ALfloat)values[0];
fvals[1] = (ALfloat)values[1];
fvals[2] = (ALfloat)values[2];
fvals[3] = (ALfloat)values[3];
fvals[4] = (ALfloat)values[4];
fvals[5] = (ALfloat)values[5];
alListenerfv(param, fvals);
return;
}
}
context = GetContextRef();
if(!context) return;
WriteLock(&context->PropLock);
if(!(values))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
switch(param)
{
default:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
UpdateListenerProps(context);
done:
WriteUnlock(&context->PropLock);
ALCcontext_DecRef(context);
}
AL_API ALvoid AL_APIENTRY alGetListenerf(ALenum param, ALfloat *value)
{
ALCcontext *context;
context = GetContextRef();
if(!context) return;
ReadLock(&context->PropLock);
if(!(value))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
switch(param)
{
case AL_GAIN:
*value = context->Listener->Gain;
break;
case AL_METERS_PER_UNIT:
*value = context->Listener->MetersPerUnit;
break;
default:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
done:
ReadUnlock(&context->PropLock);
ALCcontext_DecRef(context);
}
AL_API ALvoid AL_APIENTRY alGetListener3f(ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3)
{
ALCcontext *context;
context = GetContextRef();
if(!context) return;
ReadLock(&context->PropLock);
if(!(value1 && value2 && value3))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
switch(param)
{
case AL_POSITION:
*value1 = context->Listener->Position[0];
*value2 = context->Listener->Position[1];
*value3 = context->Listener->Position[2];
break;
case AL_VELOCITY:
*value1 = context->Listener->Velocity[0];
*value2 = context->Listener->Velocity[1];
*value3 = context->Listener->Velocity[2];
break;
default:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
done:
ReadUnlock(&context->PropLock);
ALCcontext_DecRef(context);
}
AL_API ALvoid AL_APIENTRY alGetListenerfv(ALenum param, ALfloat *values)
{
ALCcontext *context;
switch(param)
{
case AL_GAIN:
case AL_METERS_PER_UNIT:
alGetListenerf(param, values);
return;
case AL_POSITION:
case AL_VELOCITY:
alGetListener3f(param, values+0, values+1, values+2);
return;
}
context = GetContextRef();
if(!context) return;
ReadLock(&context->PropLock);
if(!(values))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
switch(param)
{
case AL_ORIENTATION:
// AT then UP
values[0] = context->Listener->Forward[0];
values[1] = context->Listener->Forward[1];
values[2] = context->Listener->Forward[2];
values[3] = context->Listener->Up[0];
values[4] = context->Listener->Up[1];
values[5] = context->Listener->Up[2];
break;
default:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
done:
ReadUnlock(&context->PropLock);
ALCcontext_DecRef(context);
}
AL_API ALvoid AL_APIENTRY alGetListeneri(ALenum param, ALint *value)
{
ALCcontext *context;
context = GetContextRef();
if(!context) return;
ReadLock(&context->PropLock);
if(!(value))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
switch(param)
{
default:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
done:
ReadUnlock(&context->PropLock);
ALCcontext_DecRef(context);
}
AL_API void AL_APIENTRY alGetListener3i(ALenum param, ALint *value1, ALint *value2, ALint *value3)
{
ALCcontext *context;
context = GetContextRef();
if(!context) return;
ReadLock(&context->PropLock);
if(!(value1 && value2 && value3))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
switch (param)
{
case AL_POSITION:
*value1 = (ALint)context->Listener->Position[0];
*value2 = (ALint)context->Listener->Position[1];
*value3 = (ALint)context->Listener->Position[2];
break;
case AL_VELOCITY:
*value1 = (ALint)context->Listener->Velocity[0];
*value2 = (ALint)context->Listener->Velocity[1];
*value3 = (ALint)context->Listener->Velocity[2];
break;
default:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
done:
ReadUnlock(&context->PropLock);
ALCcontext_DecRef(context);
}
AL_API void AL_APIENTRY alGetListeneriv(ALenum param, ALint* values)
{
ALCcontext *context;
switch(param)
{
case AL_POSITION:
case AL_VELOCITY:
alGetListener3i(param, values+0, values+1, values+2);
return;
}
context = GetContextRef();
if(!context) return;
ReadLock(&context->PropLock);
if(!(values))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
switch(param)
{
case AL_ORIENTATION:
// AT then UP
values[0] = (ALint)context->Listener->Forward[0];
values[1] = (ALint)context->Listener->Forward[1];
values[2] = (ALint)context->Listener->Forward[2];
values[3] = (ALint)context->Listener->Up[0];
values[4] = (ALint)context->Listener->Up[1];
values[5] = (ALint)context->Listener->Up[2];
break;
default:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
done:
ReadUnlock(&context->PropLock);
ALCcontext_DecRef(context);
}
void UpdateListenerProps(ALCcontext *context)
{
ALlistener *listener = context->Listener;
struct ALlistenerProps *props;
/* Get an unused proprty container, or allocate a new one as needed. */
props = ATOMIC_LOAD(&listener->FreeList, almemory_order_acquire);
if(!props)
props = al_calloc(16, sizeof(*props));
else
{
struct ALlistenerProps *next;
do {
next = ATOMIC_LOAD(&props->next, almemory_order_relaxed);
} while(ATOMIC_COMPARE_EXCHANGE_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);
}
}
+3527
View File
File diff suppressed because it is too large Load Diff
+779
View File
@@ -0,0 +1,779 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 1999-2000 by authors.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include "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"
#include "backends/base.h"
static const ALchar alVendor[] = "OpenAL Community";
static const ALchar alVersion[] = "1.1 ALSOFT "ALSOFT_VERSION;
static const ALchar alRenderer[] = "OpenAL Soft";
// Error Messages
static const ALchar alNoError[] = "No Error";
static const ALchar alErrInvalidName[] = "Invalid Name";
static const ALchar alErrInvalidEnum[] = "Invalid Enum";
static const ALchar alErrInvalidValue[] = "Invalid Value";
static const ALchar alErrInvalidOp[] = "Invalid Operation";
static const ALchar alErrOutOfMemory[] = "Out of Memory";
/* 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;
context = GetContextRef();
if(!context) return;
WriteLock(&context->PropLock);
switch(capability)
{
case AL_SOURCE_DISTANCE_MODEL:
context->SourceDistanceModel = AL_TRUE;
break;
default:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
UpdateListenerProps(context);
done:
WriteUnlock(&context->PropLock);
ALCcontext_DecRef(context);
}
AL_API ALvoid AL_APIENTRY alDisable(ALenum capability)
{
ALCcontext *context;
context = GetContextRef();
if(!context) return;
WriteLock(&context->PropLock);
switch(capability)
{
case AL_SOURCE_DISTANCE_MODEL:
context->SourceDistanceModel = AL_FALSE;
break;
default:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
UpdateListenerProps(context);
done:
WriteUnlock(&context->PropLock);
ALCcontext_DecRef(context);
}
AL_API ALboolean AL_APIENTRY alIsEnabled(ALenum capability)
{
ALCcontext *context;
ALboolean value=AL_FALSE;
context = GetContextRef();
if(!context) return AL_FALSE;
switch(capability)
{
case AL_SOURCE_DISTANCE_MODEL:
value = context->SourceDistanceModel;
break;
default:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
done:
ALCcontext_DecRef(context);
return value;
}
AL_API ALboolean AL_APIENTRY alGetBoolean(ALenum pname)
{
ALCcontext *context;
ALboolean value=AL_FALSE;
context = GetContextRef();
if(!context) return AL_FALSE;
switch(pname)
{
case AL_DOPPLER_FACTOR:
if(context->DopplerFactor != 0.0f)
value = AL_TRUE;
break;
case AL_DOPPLER_VELOCITY:
if(context->DopplerVelocity != 0.0f)
value = AL_TRUE;
break;
case AL_DISTANCE_MODEL:
if(context->DistanceModel == AL_INVERSE_DISTANCE_CLAMPED)
value = AL_TRUE;
break;
case AL_SPEED_OF_SOUND:
if(context->SpeedOfSound != 0.0f)
value = AL_TRUE;
break;
case AL_DEFERRED_UPDATES_SOFT:
if(ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
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:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
done:
ALCcontext_DecRef(context);
return value;
}
AL_API ALdouble AL_APIENTRY alGetDouble(ALenum pname)
{
ALCcontext *context;
ALdouble value = 0.0;
context = GetContextRef();
if(!context) return 0.0;
switch(pname)
{
case AL_DOPPLER_FACTOR:
value = (ALdouble)context->DopplerFactor;
break;
case AL_DOPPLER_VELOCITY:
value = (ALdouble)context->DopplerVelocity;
break;
case AL_DISTANCE_MODEL:
value = (ALdouble)context->DistanceModel;
break;
case AL_SPEED_OF_SOUND:
value = (ALdouble)context->SpeedOfSound;
break;
case AL_DEFERRED_UPDATES_SOFT:
if(ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
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:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
done:
ALCcontext_DecRef(context);
return value;
}
AL_API ALfloat AL_APIENTRY alGetFloat(ALenum pname)
{
ALCcontext *context;
ALfloat value = 0.0f;
context = GetContextRef();
if(!context) return 0.0f;
switch(pname)
{
case AL_DOPPLER_FACTOR:
value = context->DopplerFactor;
break;
case AL_DOPPLER_VELOCITY:
value = context->DopplerVelocity;
break;
case AL_DISTANCE_MODEL:
value = (ALfloat)context->DistanceModel;
break;
case AL_SPEED_OF_SOUND:
value = context->SpeedOfSound;
break;
case AL_DEFERRED_UPDATES_SOFT:
if(ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
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:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
done:
ALCcontext_DecRef(context);
return value;
}
AL_API ALint AL_APIENTRY alGetInteger(ALenum pname)
{
ALCcontext *context;
ALint value = 0;
context = GetContextRef();
if(!context) return 0;
switch(pname)
{
case AL_DOPPLER_FACTOR:
value = (ALint)context->DopplerFactor;
break;
case AL_DOPPLER_VELOCITY:
value = (ALint)context->DopplerVelocity;
break;
case AL_DISTANCE_MODEL:
value = (ALint)context->DistanceModel;
break;
case AL_SPEED_OF_SOUND:
value = (ALint)context->SpeedOfSound;
break;
case AL_DEFERRED_UPDATES_SOFT:
if(ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
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:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
done:
ALCcontext_DecRef(context);
return value;
}
AL_API ALint64SOFT AL_APIENTRY alGetInteger64SOFT(ALenum pname)
{
ALCcontext *context;
ALint64SOFT value = 0;
context = GetContextRef();
if(!context) return 0;
switch(pname)
{
case AL_DOPPLER_FACTOR:
value = (ALint64SOFT)context->DopplerFactor;
break;
case AL_DOPPLER_VELOCITY:
value = (ALint64SOFT)context->DopplerVelocity;
break;
case AL_DISTANCE_MODEL:
value = (ALint64SOFT)context->DistanceModel;
break;
case AL_SPEED_OF_SOUND:
value = (ALint64SOFT)context->SpeedOfSound;
break;
case AL_DEFERRED_UPDATES_SOFT:
if(ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
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:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
done:
ALCcontext_DecRef(context);
return value;
}
AL_API ALvoid AL_APIENTRY alGetBooleanv(ALenum pname, ALboolean *values)
{
ALCcontext *context;
if(values)
{
switch(pname)
{
case AL_DOPPLER_FACTOR:
case AL_DOPPLER_VELOCITY:
case AL_DISTANCE_MODEL:
case AL_SPEED_OF_SOUND:
case AL_DEFERRED_UPDATES_SOFT:
case AL_GAIN_LIMIT_SOFT:
case AL_NUM_RESAMPLERS_SOFT:
case AL_DEFAULT_RESAMPLER_SOFT:
values[0] = alGetBoolean(pname);
return;
}
}
context = GetContextRef();
if(!context) return;
if(!(values))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
switch(pname)
{
default:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
done:
ALCcontext_DecRef(context);
}
AL_API ALvoid AL_APIENTRY alGetDoublev(ALenum pname, ALdouble *values)
{
ALCcontext *context;
if(values)
{
switch(pname)
{
case AL_DOPPLER_FACTOR:
case AL_DOPPLER_VELOCITY:
case AL_DISTANCE_MODEL:
case AL_SPEED_OF_SOUND:
case AL_DEFERRED_UPDATES_SOFT:
case AL_GAIN_LIMIT_SOFT:
case AL_NUM_RESAMPLERS_SOFT:
case AL_DEFAULT_RESAMPLER_SOFT:
values[0] = alGetDouble(pname);
return;
}
}
context = GetContextRef();
if(!context) return;
if(!(values))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
switch(pname)
{
default:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
done:
ALCcontext_DecRef(context);
}
AL_API ALvoid AL_APIENTRY alGetFloatv(ALenum pname, ALfloat *values)
{
ALCcontext *context;
if(values)
{
switch(pname)
{
case AL_DOPPLER_FACTOR:
case AL_DOPPLER_VELOCITY:
case AL_DISTANCE_MODEL:
case AL_SPEED_OF_SOUND:
case AL_DEFERRED_UPDATES_SOFT:
case AL_GAIN_LIMIT_SOFT:
case AL_NUM_RESAMPLERS_SOFT:
case AL_DEFAULT_RESAMPLER_SOFT:
values[0] = alGetFloat(pname);
return;
}
}
context = GetContextRef();
if(!context) return;
if(!(values))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
switch(pname)
{
default:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
done:
ALCcontext_DecRef(context);
}
AL_API ALvoid AL_APIENTRY alGetIntegerv(ALenum pname, ALint *values)
{
ALCcontext *context;
if(values)
{
switch(pname)
{
case AL_DOPPLER_FACTOR:
case AL_DOPPLER_VELOCITY:
case AL_DISTANCE_MODEL:
case AL_SPEED_OF_SOUND:
case AL_DEFERRED_UPDATES_SOFT:
case AL_GAIN_LIMIT_SOFT:
case AL_NUM_RESAMPLERS_SOFT:
case AL_DEFAULT_RESAMPLER_SOFT:
values[0] = alGetInteger(pname);
return;
}
}
context = GetContextRef();
if(!context) return;
switch(pname)
{
default:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
done:
ALCcontext_DecRef(context);
}
AL_API void AL_APIENTRY alGetInteger64vSOFT(ALenum pname, ALint64SOFT *values)
{
ALCcontext *context;
if(values)
{
switch(pname)
{
case AL_DOPPLER_FACTOR:
case AL_DOPPLER_VELOCITY:
case AL_DISTANCE_MODEL:
case AL_SPEED_OF_SOUND:
case AL_DEFERRED_UPDATES_SOFT:
case AL_GAIN_LIMIT_SOFT:
case AL_NUM_RESAMPLERS_SOFT:
case AL_DEFAULT_RESAMPLER_SOFT:
values[0] = alGetInteger64SOFT(pname);
return;
}
}
context = GetContextRef();
if(!context) return;
switch(pname)
{
default:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
done:
ALCcontext_DecRef(context);
}
AL_API const ALchar* AL_APIENTRY alGetString(ALenum pname)
{
const ALchar *value = NULL;
ALCcontext *context;
context = GetContextRef();
if(!context) return NULL;
switch(pname)
{
case AL_VENDOR:
value = alVendor;
break;
case AL_VERSION:
value = alVersion;
break;
case AL_RENDERER:
value = alRenderer;
break;
case AL_EXTENSIONS:
value = context->ExtensionList;
break;
case AL_NO_ERROR:
value = alNoError;
break;
case AL_INVALID_NAME:
value = alErrInvalidName;
break;
case AL_INVALID_ENUM:
value = alErrInvalidEnum;
break;
case AL_INVALID_VALUE:
value = alErrInvalidValue;
break;
case AL_INVALID_OPERATION:
value = alErrInvalidOp;
break;
case AL_OUT_OF_MEMORY:
value = alErrOutOfMemory;
break;
default:
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
}
done:
ALCcontext_DecRef(context);
return value;
}
AL_API ALvoid AL_APIENTRY alDopplerFactor(ALfloat value)
{
ALCcontext *context;
context = GetContextRef();
if(!context) return;
if(!(value >= 0.0f && isfinite(value)))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
WriteLock(&context->PropLock);
context->DopplerFactor = value;
if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
UpdateListenerProps(context);
WriteUnlock(&context->PropLock);
done:
ALCcontext_DecRef(context);
}
AL_API ALvoid AL_APIENTRY alDopplerVelocity(ALfloat value)
{
ALCcontext *context;
context = GetContextRef();
if(!context) return;
if(!(value >= 0.0f && isfinite(value)))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
WriteLock(&context->PropLock);
context->DopplerVelocity = value;
if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
UpdateListenerProps(context);
WriteUnlock(&context->PropLock);
done:
ALCcontext_DecRef(context);
}
AL_API ALvoid AL_APIENTRY alSpeedOfSound(ALfloat value)
{
ALCcontext *context;
context = GetContextRef();
if(!context) return;
if(!(value > 0.0f && isfinite(value)))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
WriteLock(&context->PropLock);
context->SpeedOfSound = value;
if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
UpdateListenerProps(context);
WriteUnlock(&context->PropLock);
done:
ALCcontext_DecRef(context);
}
AL_API ALvoid AL_APIENTRY alDistanceModel(ALenum value)
{
ALCcontext *context;
context = GetContextRef();
if(!context) return;
if(!(value == AL_INVERSE_DISTANCE || value == AL_INVERSE_DISTANCE_CLAMPED ||
value == AL_LINEAR_DISTANCE || value == AL_LINEAR_DISTANCE_CLAMPED ||
value == AL_EXPONENT_DISTANCE || value == AL_EXPONENT_DISTANCE_CLAMPED ||
value == AL_NONE))
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
WriteLock(&context->PropLock);
context->DistanceModel = value;
if(!context->SourceDistanceModel)
{
if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
UpdateListenerProps(context);
}
WriteUnlock(&context->PropLock);
done:
ALCcontext_DecRef(context);
}
AL_API ALvoid AL_APIENTRY alDeferUpdatesSOFT(void)
{
ALCcontext *context;
context = GetContextRef();
if(!context) return;
ALCcontext_DeferUpdates(context);
ALCcontext_DecRef(context);
}
AL_API ALvoid AL_APIENTRY alProcessUpdatesSOFT(void)
{
ALCcontext *context;
context = GetContextRef();
if(!context) return;
ALCcontext_ProcessUpdates(context);
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;
}
+108
View File
@@ -0,0 +1,108 @@
/**
* OpenAL cross platform audio library
* Copyright (C) 1999-2007 by authors.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include <stdlib.h>
#include "alMain.h"
#include "alThunk.h"
#include "almalloc.h"
static ATOMIC_FLAG *ThunkArray;
static ALsizei ThunkArraySize;
static RWLock ThunkLock;
void ThunkInit(void)
{
RWLockInit(&ThunkLock);
ThunkArraySize = 1024;
ThunkArray = al_calloc(16, ThunkArraySize * sizeof(*ThunkArray));
}
void ThunkExit(void)
{
al_free(ThunkArray);
ThunkArray = NULL;
ThunkArraySize = 0;
}
ALenum NewThunkEntry(ALuint *index)
{
void *NewList;
ALsizei i;
ReadLock(&ThunkLock);
for(i = 0;i < ThunkArraySize;i++)
{
if(!ATOMIC_FLAG_TEST_AND_SET(&ThunkArray[i], almemory_order_acq_rel))
{
ReadUnlock(&ThunkLock);
*index = i+1;
return AL_NO_ERROR;
}
}
ReadUnlock(&ThunkLock);
WriteLock(&ThunkLock);
/* Double-check that there's still no free entries, in case another
* invocation just came through and increased the size of the array.
*/
for(;i < ThunkArraySize;i++)
{
if(!ATOMIC_FLAG_TEST_AND_SET(&ThunkArray[i], almemory_order_acq_rel))
{
WriteUnlock(&ThunkLock);
*index = i+1;
return AL_NO_ERROR;
}
}
NewList = al_calloc(16, ThunkArraySize*2 * sizeof(*ThunkArray));
if(!NewList)
{
WriteUnlock(&ThunkLock);
ERR("Realloc failed to increase to %u entries!\n", ThunkArraySize*2);
return AL_OUT_OF_MEMORY;
}
memcpy(NewList, ThunkArray, ThunkArraySize*sizeof(*ThunkArray));
al_free(ThunkArray);
ThunkArray = NewList;
ThunkArraySize *= 2;
ATOMIC_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);
return AL_NO_ERROR;
}
void FreeThunkEntry(ALuint index)
{
ReadLock(&ThunkLock);
if(index > 0 && (ALsizei)index <= ThunkArraySize)
ATOMIC_FLAG_CLEAR(&ThunkArray[index-1], almemory_order_release);
ReadUnlock(&ThunkLock);
}
File diff suppressed because it is too large Load Diff
+55
View File
@@ -0,0 +1,55 @@
Source Install
==============
To install OpenAL Soft, use your favorite shell to go into the build/
directory, and run:
cmake ..
Assuming configuration went well, you can then build it, typically using GNU
Make (KDevelop, MSVC, and others are possible depending on your system setup
and CMake configuration).
Please Note: Double check that the appropriate backends were detected. Often,
complaints of no sound, crashing, and missing devices can be solved by making
sure the correct backends are being used. CMake's output will identify which
backends were enabled.
For most systems, you will likely want to make sure ALSA, OSS, and PulseAudio
were detected (if your target system uses them). For Windows, make sure
DirectSound was detected.
Utilities
=========
The source package comes with an informational utility, openal-info, and is
built by default. It prints out information provided by the ALC and AL sub-
systems, including discovered devices, version information, and extensions.
Configuration
=============
OpenAL Soft can be configured on a per-user and per-system basis. This allows
users and sysadmins to control information provided to applications, as well
as application-agnostic behavior of the library. See alsoftrc.sample for
available settings.
Acknowledgements
================
Special thanks go to:
Creative Labs for the original source code this is based off of.
Christopher Fitzgerald for the current reverb effect implementation, and
helping with the low-pass and HRTF filters.
Christian Borss for the 3D panning code previous versions used as a base.
Ben Davis for the idea behind a previous version of the click-removal code.
Richard Furse for helping with my understanding of Ambisonics that is used by
the various parts of the library.
-16
View File
@@ -1,16 +0,0 @@
# openal-soft-vita
This repo contains patchsets for vita [openal-soft](https://openal-soft.org/) port.
Format is openal-soft-<openal_version>-vita-<patchset_version>.patch
For full source see vita-* branches
# building
```
mkdir build && cd build
cmake -DCMAKE_TOOLCHAIN_FILE=${VITASDK}/share/vita.toolchain.cmake -DCMAKE_BUILD_TYPE=Release ..
make install
```
+39
View File
@@ -0,0 +1,39 @@
# Cross-compiling requires CMake 2.6 or newer. Example:
# cmake .. -DCMAKE_TOOLCHAIN_FILE=../XCompile-Android.txt -DHOST=arm-linux-androideabi
# Where 'arm-linux-androideabi' is the host prefix for the cross-compiler. If
# you already have a toolchain file setup, you may use that instead of this
# file. Make sure to set CMAKE_FIND_ROOT_PATH to where the NDK toolchain was
# installed (e.g. "$ENV{HOME}/toolchains/arm-linux-androideabi-r10c-21").
# the name of the target operating system
SET(CMAKE_SYSTEM_NAME Linux)
# which compilers to use for C and C++
SET(CMAKE_C_COMPILER "${HOST}-gcc")
SET(CMAKE_CXX_COMPILER "${HOST}-g++")
SET(CMAKE_RC_COMPILER "${HOST}-windres")
# here is the target environment located
SET(CMAKE_FIND_ROOT_PATH "SET THIS TO THE NDK TOOLCHAIN'S INSTALL PATH")
# here is where stuff gets installed to
SET(CMAKE_INSTALL_PREFIX "${CMAKE_FIND_ROOT_PATH}" CACHE STRING "Install path prefix, prepended onto install directories." FORCE)
# adjust the default behaviour of the FIND_XXX() commands:
# search headers and libraries in the target environment, search
# programs in the host environment
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
# set env vars so that pkg-config will look in the appropriate directory for
# .pc files (as there seems to be no way to force using ${HOST}-pkg-config)
set(ENV{PKG_CONFIG_LIBDIR} "${CMAKE_INSTALL_PREFIX}/lib/pkgconfig")
set(ENV{PKG_CONFIG_PATH} "")
# Qt4 tools
SET(QT_QMAKE_EXECUTABLE ${HOST}-qmake)
SET(QT_MOC_EXECUTABLE ${HOST}-moc)
SET(QT_RCC_EXECUTABLE ${HOST}-rcc)
SET(QT_UIC_EXECUTABLE ${HOST}-uic)
SET(QT_LRELEASE_EXECUTABLE ${HOST}-lrelease)
+37
View File
@@ -0,0 +1,37 @@
# Cross-compiling requires CMake 2.6 or newer. Example:
# cmake .. -DCMAKE_TOOLCHAIN_FILE=../XCompile.txt -DHOST=i686-w64-mingw32
# Where 'i686-w64-mingw32' is the host prefix for your cross-compiler. If you
# already have a toolchain file setup, you may use that instead of this file.
# the name of the target operating system
SET(CMAKE_SYSTEM_NAME Windows)
# which compilers to use for C and C++
SET(CMAKE_C_COMPILER "${HOST}-gcc")
SET(CMAKE_CXX_COMPILER "${HOST}-g++")
SET(CMAKE_RC_COMPILER "${HOST}-windres")
# here is the target environment located
SET(CMAKE_FIND_ROOT_PATH "/usr/${HOST}")
# here is where stuff gets installed to
SET(CMAKE_INSTALL_PREFIX "${CMAKE_FIND_ROOT_PATH}" CACHE STRING "Install path prefix, prepended onto install directories." FORCE)
# adjust the default behaviour of the FIND_XXX() commands:
# search headers and libraries in the target environment, search
# programs in the host environment
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
# set env vars so that pkg-config will look in the appropriate directory for
# .pc files (as there seems to be no way to force using ${HOST}-pkg-config)
set(ENV{PKG_CONFIG_LIBDIR} "${CMAKE_INSTALL_PREFIX}/lib/pkgconfig")
set(ENV{PKG_CONFIG_PATH} "")
# Qt4 tools
SET(QT_QMAKE_EXECUTABLE ${HOST}-qmake)
SET(QT_MOC_EXECUTABLE ${HOST}-moc)
SET(QT_RCC_EXECUTABLE ${HOST}-rcc)
SET(QT_UIC_EXECUTABLE ${HOST}-uic)
SET(QT_LRELEASE_EXECUTABLE ${HOST}-lrelease)
+487
View File
@@ -0,0 +1,487 @@
# OpenAL config file.
#
# Option blocks may appear multiple times, and duplicated options will take the
# last value specified. Environment variables may be specified within option
# values, and are automatically substituted when the config file is loaded.
# Environment variable names may only contain alpha-numeric characters (a-z,
# A-Z, 0-9) and underscores (_), and are prefixed with $. For example,
# specifying "$HOME/file.ext" would typically result in something like
# "/home/user/file.ext". To specify an actual "$" character, use "$$".
#
# Device-specific values may be specified by including the device name in the
# block name, with "general" replaced by the device name. That is, general
# options for the device "Name of Device" would be in the [Name of Device]
# block, while ALSA options would be in the [alsa/Name of Device] block.
# Options marked as "(global)" are not influenced by the device.
#
# The system-wide settings can be put in /etc/openal/alsoft.conf and user-
# specific override settings in $HOME/.alsoftrc.
# For Windows, these settings should go into $AppData\alsoft.ini
#
# Option and block names are case-senstive. The supplied values are only hints
# and may not be honored (though generally it'll try to get as close as
# possible). Note: options that are left unset may default to app- or system-
# specified values. These are the current available settings:
##
## General stuff
##
[general]
## disable-cpu-exts: (global)
# Disables use of specialized methods that use specific CPU intrinsics.
# Certain methods may utilize CPU extensions for improved performance, and
# this option is useful for preventing some or all of those methods from being
# used. The available extensions are: sse, sse2, sse3, sse4.1, and neon.
# Specifying 'all' disables use of all such specialized methods.
#disable-cpu-exts =
## drivers: (global)
# Sets the backend driver list order, comma-seperated. Unknown backends and
# duplicated names are ignored. Unlisted backends won't be considered for use
# unless the list is ended with a comma (e.g. 'oss,' will try OSS first before
# other backends, while 'oss' will try OSS only). Backends prepended with -
# won't be considered for use (e.g. '-oss,' will try all available backends
# except OSS). An empty list means to try all backends.
#drivers =
## channels:
# Sets the output channel configuration. If left unspecified, one will try to
# be detected from the system, and defaulting to stereo. The available values
# are: mono, stereo, quad, surround51, surround51rear, surround61, surround71,
# ambi1, ambi2, ambi3. Note that the ambi* configurations provide ambisonic
# channels of the given order (using ACN ordering and SN3D normalization by
# default), which need to be decoded to play correctly on speakers.
#channels =
## sample-type:
# Sets the output sample type. Currently, all mixing is done with 32-bit float
# and converted to the output sample type as needed. Available values are:
# int8 - signed 8-bit int
# uint8 - unsigned 8-bit int
# int16 - signed 16-bit int
# uint16 - unsigned 16-bit int
# int32 - signed 32-bit int
# uint32 - unsigned 32-bit int
# float32 - 32-bit float
#sample-type = float32
## frequency:
# Sets the output frequency. If left unspecified it will try to detect a
# default from the system, otherwise it will default to 44100.
#frequency =
## period_size:
# Sets the update period size, in frames. This is the number of frames needed
# for each mixing update. Acceptable values range between 64 and 8192.
#period_size = 1024
## periods:
# Sets the number of update periods. Higher values create a larger mix ahead,
# which helps protect against skips when the CPU is under load, but increases
# the delay between a sound getting mixed and being heard. Acceptable values
# range between 2 and 16.
#periods = 3
## stereo-mode:
# Specifies if stereo output is treated as being headphones or speakers. With
# headphones, HRTF or crossfeed filters may be used for better audio quality.
# Valid settings are auto, speakers, and headphones.
#stereo-mode = auto
## stereo-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
# default filters will only work with 44100hz or 48000hz stereo output. While
# HRTF is used, the cf_level option is ignored. Setting this to auto (default)
# will allow HRTF to be used when headphones are detected or the app requests
# it, while setting true or false will forcefully enable or disable HRTF
# respectively.
#hrtf = auto
## default-hrtf:
# Specifies the default HRTF to use. When multiple HRTFs are available, this
# determines the preferred one to use if none are specifically requested. Note
# that this is the enumerated HRTF name, not necessarily the filename.
#default-hrtf =
## hrtf-paths:
# Specifies a comma-separated list of paths containing HRTF data sets. The
# format of the files are described in docs/hrtf.txt. The files within the
# directories must have the .mhr file extension to be recognized. By default,
# OS-dependent data paths will be used. They will also be used if the list
# ends with a comma. On Windows this is:
# $AppData\openal\hrtf
# And on other systems, it's (in order):
# $XDG_DATA_HOME/openal/hrtf (defaults to $HOME/.local/share/openal/hrtf)
# $XDG_DATA_DIRS/openal/hrtf (defaults to /usr/local/share/openal/hrtf and
# /usr/share/openal/hrtf)
#hrtf-paths =
## cf_level:
# Sets the crossfeed level for stereo output. Valid values are:
# 0 - No crossfeed
# 1 - Low crossfeed
# 2 - Middle crossfeed
# 3 - High crossfeed (virtual speakers are closer to itself)
# 4 - Low easy crossfeed
# 5 - Middle easy crossfeed
# 6 - High easy crossfeed
# Users of headphones may want to try various settings. Has no effect on non-
# stereo modes.
#cf_level = 0
## resampler: (global)
# Selects the resampler used when mixing sources. Valid values are:
# point - nearest sample, no interpolation
# linear - extrapolates samples using a linear slope between samples
# sinc4 - extrapolates samples using a 4-point Sinc filter
# bsinc - extrapolates samples using a band-limited Sinc filter (varying
# between 12 and 24 points, with anti-aliasing)
# Specifying other values will result in using the default (linear).
#resampler = linear
## rt-prio: (global)
# Sets real-time priority for the mixing thread. Not all drivers may use this
# (eg. PortAudio) as they already control the priority of the mixing thread.
# 0 and negative values will disable it. Note that this may constitute a
# security risk since a real-time priority thread can indefinitely block
# normal-priority threads if it fails to wait. As such, the default is
# disabled.
#rt-prio = 0
## sources:
# Sets the maximum number of allocatable sources. Lower values may help for
# systems with apps that try to play more sounds than the CPU can handle.
#sources = 256
## slots:
# Sets the maximum number of Auxiliary Effect Slots an app can create. A slot
# can use a non-negligible amount of CPU time if an effect is set on it even
# if no sources are feeding it, so this may help when apps use more than the
# system can handle.
#slots = 64
## 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
# help for apps that try to use effects which are too CPU intensive for the
# system to handle. Available effects are: eaxreverb,reverb,chorus,compressor,
# distortion,echo,equalizer,flanger,modulator,dedicated
#excludefx =
## default-reverb: (global)
# A reverb preset that applies by default to all sources on send 0
# (applications that set their own slots on send 0 will override this).
# Available presets are: None, Generic, PaddedCell, Room, Bathroom,
# Livingroom, Stoneroom, Auditorium, ConcertHall, Cave, Arena, Hangar,
# CarpetedHallway, Hallway, StoneCorridor, Alley, Forest, City, Moutains,
# Quarry, Plain, ParkingLot, SewerPipe, Underwater, Drugged, Dizzy, Psychotic.
#default-reverb =
## trap-alc-error: (global)
# Generates a SIGTRAP signal when an ALC device error is generated, on systems
# that support it. This helps when debugging, while trying to find the cause
# of a device error. On Windows, a breakpoint exception is generated.
#trap-alc-error = false
## trap-al-error: (global)
# Generates a SIGTRAP signal when an AL context error is generated, on systems
# that support it. This helps when debugging, while trying to find the cause
# of a context error. On Windows, a breakpoint exception is generated.
#trap-al-error = false
##
## Ambisonic decoder stuff
##
[decoder]
## hq-mode:
# Enables a high-quality ambisonic decoder. This mode is capable of frequency-
# dependent processing, creating a better reproduction of 3D sound rendering
# over surround sound speakers. Enabling this also requires specifying decoder
# configuration files for the appropriate speaker configuration you intend to
# use (see the quad, surround51, etc options below). Currently, up to third-
# order decoding is supported.
hq-mode = false
## distance-comp:
# Enables compensation for the speakers' relative distances to the listener.
# This applies the necessary delays and attenuation to make the speakers
# behave as though they are all equidistant, which is important for proper
# playback of 3D sound rendering. Requires the 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)
##
[reverb]
## boost: (global)
# A global amplification for reverb output, expressed in decibels. The value
# is logarithmic, so +6 will be a scale of (approximately) 2x, +12 will be a
# scale of 4x, etc. Similarly, -6 will be about half, and -12 about 1/4th. A
# value of 0 means no change.
#boost = 0
## emulate-eax: (global)
# Allows the standard reverb effect to be used in place of EAX reverb. EAX
# reverb processing is a bit more CPU intensive than standard, so this option
# allows a simpler effect to be used at the loss of some quality.
#emulate-eax = false
##
## PulseAudio backend stuff
##
[pulse]
## spawn-server: (global)
# Attempts to autospawn a PulseAudio server whenever needed (initializing the
# backend, enumerating devices, etc). Setting autospawn to false in Pulse's
# client.conf will still prevent autospawning even if this is set to true.
#spawn-server = true
## allow-moves: (global)
# Allows PulseAudio to move active streams to different devices. Note that the
# device specifier (seen by applications) will not be updated when this
# occurs, and neither will the AL device configuration (sample rate, format,
# etc).
#allow-moves = false
## fix-rate:
# Specifies whether to match the playback stream's sample rate to the device's
# sample rate. Enabling this forces OpenAL Soft to mix sources and effects
# directly to the actual output rate, avoiding a second resample pass by the
# PulseAudio server.
#fix-rate = false
##
## ALSA backend stuff
##
[alsa]
## device: (global)
# Sets the device name for the default playback device.
#device = default
## device-prefix: (global)
# Sets the prefix used by the discovered (non-default) playback devices. This
# will be appended with "CARD=c,DEV=d", where c is the card id and d is the
# device index for the requested device name.
#device-prefix = plughw:
## device-prefix-*: (global)
# Card- and device-specific prefixes may be used to override the device-prefix
# option. The option may specify the card id (eg, device-prefix-NVidia), or
# the card id and device index (eg, device-prefix-NVidia-0). The card id is
# case-sensitive.
#device-prefix- =
## capture: (global)
# Sets the device name for the default capture device.
#capture = default
## capture-prefix: (global)
# Sets the prefix used by the discovered (non-default) capture devices. This
# will be appended with "CARD=c,DEV=d", where c is the card id and d is the
# device number for the requested device name.
#capture-prefix = plughw:
## capture-prefix-*: (global)
# Card- and device-specific prefixes may be used to override the
# capture-prefix option. The option may specify the card id (eg,
# capture-prefix-NVidia), or the card id and device index (eg,
# capture-prefix-NVidia-0). The card id is case-sensitive.
#capture-prefix- =
## mmap:
# Sets whether to try using mmap mode (helps reduce latencies and CPU
# consumption). If mmap isn't available, it will automatically fall back to
# non-mmap mode. True, yes, on, and non-0 values will attempt to use mmap. 0
# and anything else will force mmap off.
#mmap = true
## allow-resampler:
# Specifies whether to allow ALSA's built-in resampler. Enabling this will
# allow the playback device to be set to a different sample rate than the
# actual output, causing ALSA to apply its own resampling pass after OpenAL
# Soft resamples and mixes the sources and effects for output.
#allow-resampler = false
##
## OSS backend stuff
##
[oss]
## device: (global)
# Sets the device name for OSS output.
#device = /dev/dsp
## capture: (global)
# Sets the device name for OSS capture.
#capture = /dev/dsp
##
## Solaris backend stuff
##
[solaris]
## device: (global)
# Sets the device name for Solaris output.
#device = /dev/audio
##
## QSA backend stuff
##
[qsa]
##
## JACK backend stuff
##
[jack]
## spawn-server: (global)
# Attempts to autospawn a JACK server whenever needed (initializing the
# backend, opening devices, etc).
#spawn-server = false
## buffer-size:
# Sets the update buffer size, in samples, that the backend will keep buffered
# to handle the server's real-time processing requests. This value must be a
# power of 2, or else it will be rounded up to the next power of 2. If it is
# less than JACK's buffer update size, it will be clamped. This option may
# be useful in case the server's update size is too small and doesn't give the
# mixer time to keep enough audio available for the processing requests.
#buffer-size = 0
##
## MMDevApi backend stuff
##
[mmdevapi]
##
## DirectSound backend stuff
##
[dsound]
##
## Windows Multimedia backend stuff
##
[winmm]
##
## PortAudio backend stuff
##
[port]
## device: (global)
# Sets the device index for output. Negative values will use the default as
# given by PortAudio itself.
#device = -1
## capture: (global)
# Sets the device index for capture. Negative values will use the default as
# given by PortAudio itself.
#capture = -1
##
## Wave File Writer stuff
##
[wave]
## file: (global)
# Sets the filename of the wave file to write to. An empty name prevents the
# backend from opening, even when explicitly requested.
# THIS WILL OVERWRITE EXISTING FILES WITHOUT QUESTION!
#file =
## bformat: (global)
# Creates AMB format files using first-order ambisonics instead of a standard
# single- or multi-channel .wav file.
#bformat = false
+19
View File
@@ -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
View File
+9
View File
@@ -0,0 +1,9 @@
#include <sys/types.h>
#define KB ((off_t)(1024))
#define MB ((off_t)(KB*1024))
#define GB ((off_t)(MB*1024))
int tb[((GB+GB+GB) > GB) ? 1 : -1];
int main()
{ return 0; }
+39
View File
@@ -0,0 +1,39 @@
# - Check if the _FILE_OFFSET_BITS macro is needed for large files
# CHECK_FILE_OFFSET_BITS()
#
# The following variables may be set before calling this macro to
# modify the way the check is run:
#
# CMAKE_REQUIRED_FLAGS = string of compile command line flags
# CMAKE_REQUIRED_DEFINITIONS = list of macros to define (-DFOO=bar)
# CMAKE_REQUIRED_INCLUDES = list of include directories
# Copyright (c) 2009, Chris Robinson
#
# Redistribution and use is allowed according to the terms of the LGPL license.
MACRO(CHECK_FILE_OFFSET_BITS)
IF(NOT DEFINED _FILE_OFFSET_BITS)
MESSAGE(STATUS "Checking _FILE_OFFSET_BITS for large files")
TRY_COMPILE(__WITHOUT_FILE_OFFSET_BITS_64
${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/cmake/CheckFileOffsetBits.c
COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS})
IF(NOT __WITHOUT_FILE_OFFSET_BITS_64)
TRY_COMPILE(__WITH_FILE_OFFSET_BITS_64
${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/cmake/CheckFileOffsetBits.c
COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} -D_FILE_OFFSET_BITS=64)
ENDIF(NOT __WITHOUT_FILE_OFFSET_BITS_64)
IF(NOT __WITHOUT_FILE_OFFSET_BITS_64 AND __WITH_FILE_OFFSET_BITS_64)
SET(_FILE_OFFSET_BITS 64 CACHE INTERNAL "_FILE_OFFSET_BITS macro needed for large files")
MESSAGE(STATUS "Checking _FILE_OFFSET_BITS for large files - 64")
ELSE(NOT __WITHOUT_FILE_OFFSET_BITS_64 AND __WITH_FILE_OFFSET_BITS_64)
SET(_FILE_OFFSET_BITS "" CACHE INTERNAL "_FILE_OFFSET_BITS macro needed for large files")
MESSAGE(STATUS "Checking _FILE_OFFSET_BITS for large files - not needed")
ENDIF(NOT __WITHOUT_FILE_OFFSET_BITS_64 AND __WITH_FILE_OFFSET_BITS_64)
ENDIF(NOT DEFINED _FILE_OFFSET_BITS)
ENDMACRO(CHECK_FILE_OFFSET_BITS)
+92
View File
@@ -0,0 +1,92 @@
# - Check if a symbol exists as a function, variable, or macro
# CHECK_SYMBOL_EXISTS(<symbol> <files> <variable>)
#
# Check that the <symbol> is available after including given header
# <files> and store the result in a <variable>. Specify the list
# of files in one argument as a semicolon-separated list.
#
# If the header files define the symbol as a macro it is considered
# available and assumed to work. If the header files declare the
# symbol as a function or variable then the symbol must also be
# available for linking. If the symbol is a type or enum value
# it will not be recognized (consider using CheckTypeSize or
# CheckCSourceCompiles).
#
# The following variables may be set before calling this macro to
# modify the way the check is run:
#
# CMAKE_REQUIRED_FLAGS = string of compile command line flags
# CMAKE_REQUIRED_DEFINITIONS = list of macros to define (-DFOO=bar)
# CMAKE_REQUIRED_INCLUDES = list of include directories
# CMAKE_REQUIRED_LIBRARIES = list of libraries to link
#=============================================================================
# Copyright 2003-2011 Kitware, Inc.
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
#=============================================================================
# (To distribute this file outside of CMake, substitute the full
# License text for the above reference.)
MACRO(CHECK_SHARED_FUNCTION_EXISTS SYMBOL FILES LIBRARY LOCATION VARIABLE)
IF(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)
SET(CHECK_SYMBOL_EXISTS_LIBS
"-DLINK_LIBRARIES:STRING=${CMAKE_REQUIRED_LIBRARIES};${LIBRARY}")
ELSE(CMAKE_REQUIRED_LIBRARIES)
SET(CHECK_SYMBOL_EXISTS_LIBS
"-DLINK_LIBRARIES:STRING=${LIBRARY}")
ENDIF(CMAKE_REQUIRED_LIBRARIES)
IF(CMAKE_REQUIRED_INCLUDES)
SET(CMAKE_SYMBOL_EXISTS_INCLUDES
"-DINCLUDE_DIRECTORIES:STRING=${CMAKE_REQUIRED_INCLUDES}")
ELSE(CMAKE_REQUIRED_INCLUDES)
SET(CMAKE_SYMBOL_EXISTS_INCLUDES)
ENDIF(CMAKE_REQUIRED_INCLUDES)
FOREACH(FILE ${FILES})
SET(CMAKE_CONFIGURABLE_FILE_CONTENT
"${CMAKE_CONFIGURABLE_FILE_CONTENT}#include <${FILE}>\n")
ENDFOREACH(FILE)
SET(CMAKE_CONFIGURABLE_FILE_CONTENT
"${CMAKE_CONFIGURABLE_FILE_CONTENT}\nvoid cmakeRequireSymbol(int dummy,...){(void)dummy;}\nint main()\n{\n cmakeRequireSymbol(0,&${SYMBOL});\n return 0;\n}\n")
CONFIGURE_FILE("${CMAKE_ROOT}/Modules/CMakeConfigurableFile.in"
"${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/CheckSymbolExists.c" @ONLY)
MESSAGE(STATUS "Looking for ${SYMBOL} in ${LIBRARY}")
TRY_COMPILE(${VARIABLE}
${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/CheckSymbolExists.c
COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS}
CMAKE_FLAGS
-DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_SYMBOL_EXISTS_FLAGS}
-DLINK_DIRECTORIES:STRING=${LOCATION}
"${CHECK_SYMBOL_EXISTS_LIBS}"
"${CMAKE_SYMBOL_EXISTS_INCLUDES}"
OUTPUT_VARIABLE OUTPUT)
IF(${VARIABLE})
MESSAGE(STATUS "Looking for ${SYMBOL} in ${LIBRARY} - found")
SET(${VARIABLE} 1 CACHE INTERNAL "Have symbol ${SYMBOL} in ${LIBRARY}")
FILE(APPEND ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log
"Determining if the ${SYMBOL} "
"exist in ${LIBRARY} passed with the following output:\n"
"${OUTPUT}\nFile ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/CheckSymbolExists.c:\n"
"${CMAKE_CONFIGURABLE_FILE_CONTENT}\n")
ELSE(${VARIABLE})
MESSAGE(STATUS "Looking for ${SYMBOL} in ${LIBRARY} - not found.")
SET(${VARIABLE} "" CACHE INTERNAL "Have symbol ${SYMBOL} in ${LIBRARY}")
FILE(APPEND ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log
"Determining if the ${SYMBOL} "
"exist in ${LIBRARY} failed with the following output:\n"
"${OUTPUT}\nFile ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/CheckSymbolExists.c:\n"
"${CMAKE_CONFIGURABLE_FILE_CONTENT}\n")
ENDIF(${VARIABLE})
ENDIF(NOT DEFINED "${VARIABLE}" OR "x${${VARIABLE}}" STREQUAL "x${VARIABLE}")
ENDMACRO(CHECK_SHARED_FUNCTION_EXISTS)
+73
View File
@@ -0,0 +1,73 @@
# - Find alsa
# Find the alsa libraries (asound)
#
# This module defines the following variables:
# ALSA_FOUND - True if ALSA_INCLUDE_DIR & ALSA_LIBRARY are found
# ALSA_LIBRARIES - Set when ALSA_LIBRARY is found
# ALSA_INCLUDE_DIRS - Set when ALSA_INCLUDE_DIR is found
#
# ALSA_INCLUDE_DIR - where to find asoundlib.h, etc.
# ALSA_LIBRARY - the asound library
# ALSA_VERSION_STRING - the version of alsa found (since CMake 2.8.8)
#
#=============================================================================
# Copyright 2009-2011 Kitware, Inc.
# Copyright 2009-2011 Philip Lowman <philip@yhbt.com>
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# * The names of Kitware, Inc., the Insight Consortium, or the names of
# any consortium members, or of any contributors, may not be used to
# endorse or promote products derived from this software without
# specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS''
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#=============================================================================
find_path(ALSA_INCLUDE_DIR NAMES alsa/asoundlib.h
DOC "The ALSA (asound) include directory"
)
find_library(ALSA_LIBRARY NAMES asound
DOC "The ALSA (asound) library"
)
if(ALSA_INCLUDE_DIR AND EXISTS "${ALSA_INCLUDE_DIR}/alsa/version.h")
file(STRINGS "${ALSA_INCLUDE_DIR}/alsa/version.h" alsa_version_str REGEX "^#define[\t ]+SND_LIB_VERSION_STR[\t ]+\".*\"")
string(REGEX REPLACE "^.*SND_LIB_VERSION_STR[\t ]+\"([^\"]*)\".*$" "\\1" ALSA_VERSION_STRING "${alsa_version_str}")
unset(alsa_version_str)
endif()
# handle the QUIETLY and REQUIRED arguments and set ALSA_FOUND to TRUE if
# all listed variables are TRUE
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(ALSA
REQUIRED_VARS ALSA_LIBRARY ALSA_INCLUDE_DIR
VERSION_VAR ALSA_VERSION_STRING)
if(ALSA_FOUND)
set( ALSA_LIBRARIES ${ALSA_LIBRARY} )
set( ALSA_INCLUDE_DIRS ${ALSA_INCLUDE_DIR} )
endif()
mark_as_advanced(ALSA_INCLUDE_DIR ALSA_LIBRARY)

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