mirror of
https://github.com/love2d/megasource.git
synced 2026-08-17 11:11:35 +02:00
Added missing changes to the OpenAL-Soft update.
This commit is contained in:
+781
-511
File diff suppressed because it is too large
Load Diff
+837
-567
File diff suppressed because it is too large
Load Diff
@@ -13,8 +13,8 @@
|
||||
*
|
||||
* 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., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
@@ -33,11 +33,13 @@
|
||||
#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 {
|
||||
@@ -137,13 +139,21 @@ static char *expdup(const char *str)
|
||||
}
|
||||
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);
|
||||
@@ -192,12 +202,8 @@ static void LoadConfigFromFile(FILE *f)
|
||||
char key[256] = "";
|
||||
char value[256] = "";
|
||||
|
||||
comment = strchr(buffer, '#');
|
||||
if(comment) *(comment++) = 0;
|
||||
|
||||
line = rstrip(lstrip(buffer));
|
||||
if(!line[0])
|
||||
continue;
|
||||
if(!line[0]) continue;
|
||||
|
||||
if(line[0] == '[')
|
||||
{
|
||||
@@ -205,10 +211,21 @@ static void LoadConfigFromFile(FILE *f)
|
||||
char *endsection;
|
||||
|
||||
endsection = strchr(section, ']');
|
||||
if(!endsection || section == endsection || endsection[1] != 0)
|
||||
if(!endsection || section == endsection)
|
||||
{
|
||||
ERR("config parse error: bad line \"%s\"\n", line);
|
||||
continue;
|
||||
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;
|
||||
|
||||
@@ -223,6 +240,10 @@ static void LoadConfigFromFile(FILE *f)
|
||||
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)
|
||||
@@ -296,27 +317,33 @@ void ReadALConfig(void)
|
||||
|
||||
if(SHGetSpecialFolderPathW(NULL, buffer, CSIDL_APPDATA, FALSE) != FALSE)
|
||||
{
|
||||
size_t p = lstrlenW(buffer);
|
||||
_snwprintf(buffer+p, PATH_MAX-p, L"\\alsoft.ini");
|
||||
al_string filepath = AL_STRING_INIT_STATIC();
|
||||
al_string_copy_wcstr(&filepath, buffer);
|
||||
al_string_append_cstr(&filepath, "\\alsoft.ini");
|
||||
|
||||
TRACE("Loading config %ls...\n", buffer);
|
||||
f = _wfopen(buffer, L"rt");
|
||||
TRACE("Loading config %s...\n", al_string_get_cstr(filepath));
|
||||
f = al_fopen(al_string_get_cstr(filepath), "rt");
|
||||
if(f)
|
||||
{
|
||||
LoadConfigFromFile(f);
|
||||
fclose(f);
|
||||
}
|
||||
al_string_deinit(&filepath);
|
||||
}
|
||||
|
||||
if((str=_wgetenv(L"ALSOFT_CONF")) != NULL && *str)
|
||||
{
|
||||
TRACE("Loading config %ls...\n", str);
|
||||
f = _wfopen(str, L"rt");
|
||||
al_string filepath = AL_STRING_INIT_STATIC();
|
||||
al_string_copy_wcstr(&filepath, str);
|
||||
|
||||
TRACE("Loading config %s...\n", al_string_get_cstr(filepath));
|
||||
f = al_fopen(al_string_get_cstr(filepath), "rt");
|
||||
if(f)
|
||||
{
|
||||
LoadConfigFromFile(f);
|
||||
fclose(f);
|
||||
}
|
||||
al_string_deinit(&filepath);
|
||||
}
|
||||
}
|
||||
#else
|
||||
@@ -428,7 +455,7 @@ void FreeALConfig(void)
|
||||
free(cfgBlock.entries);
|
||||
}
|
||||
|
||||
const char *GetConfigValue(const char *blockName, const char *keyName, const char *def)
|
||||
const char *GetConfigValue(const char *devName, const char *blockName, const char *keyName, const char *def)
|
||||
{
|
||||
unsigned int i;
|
||||
char key[256];
|
||||
@@ -437,16 +464,26 @@ const char *GetConfigValue(const char *blockName, const char *keyName, const cha
|
||||
return def;
|
||||
|
||||
if(blockName && strcasecmp(blockName, "general") != 0)
|
||||
snprintf(key, sizeof(key), "%s/%s", blockName, keyName);
|
||||
{
|
||||
if(devName)
|
||||
snprintf(key, sizeof(key), "%s/%s/%s", blockName, devName, keyName);
|
||||
else
|
||||
snprintf(key, sizeof(key), "%s/%s", blockName, keyName);
|
||||
}
|
||||
else
|
||||
{
|
||||
strncpy(key, keyName, sizeof(key)-1);
|
||||
key[sizeof(key)-1] = 0;
|
||||
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(strcasecmp(cfgBlock.entries[i].key, key) == 0)
|
||||
if(strcmp(cfgBlock.entries[i].key, key) == 0)
|
||||
{
|
||||
TRACE("Found %s = \"%s\"\n", key, cfgBlock.entries[i].value);
|
||||
if(cfgBlock.entries[i].value[0])
|
||||
@@ -455,46 +492,50 @@ const char *GetConfigValue(const char *blockName, const char *keyName, const cha
|
||||
}
|
||||
}
|
||||
|
||||
TRACE("Key %s not found\n", key);
|
||||
return def;
|
||||
if(!devName)
|
||||
{
|
||||
TRACE("Key %s not found\n", key);
|
||||
return def;
|
||||
}
|
||||
return GetConfigValue(NULL, blockName, keyName, def);
|
||||
}
|
||||
|
||||
int ConfigValueExists(const char *blockName, const char *keyName)
|
||||
int ConfigValueExists(const char *devName, const char *blockName, const char *keyName)
|
||||
{
|
||||
const char *val = GetConfigValue(blockName, keyName, "");
|
||||
const char *val = GetConfigValue(devName, blockName, keyName, "");
|
||||
return !!val[0];
|
||||
}
|
||||
|
||||
int ConfigValueStr(const char *blockName, const char *keyName, const char **ret)
|
||||
int ConfigValueStr(const char *devName, const char *blockName, const char *keyName, const char **ret)
|
||||
{
|
||||
const char *val = GetConfigValue(blockName, keyName, "");
|
||||
const char *val = GetConfigValue(devName, blockName, keyName, "");
|
||||
if(!val[0]) return 0;
|
||||
|
||||
*ret = val;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int ConfigValueInt(const char *blockName, const char *keyName, int *ret)
|
||||
int ConfigValueInt(const char *devName, const char *blockName, const char *keyName, int *ret)
|
||||
{
|
||||
const char *val = GetConfigValue(blockName, keyName, "");
|
||||
const char *val = GetConfigValue(devName, blockName, keyName, "");
|
||||
if(!val[0]) return 0;
|
||||
|
||||
*ret = strtol(val, NULL, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int ConfigValueUInt(const char *blockName, const char *keyName, unsigned int *ret)
|
||||
int ConfigValueUInt(const char *devName, const char *blockName, const char *keyName, unsigned int *ret)
|
||||
{
|
||||
const char *val = GetConfigValue(blockName, keyName, "");
|
||||
const char *val = GetConfigValue(devName, blockName, keyName, "");
|
||||
if(!val[0]) return 0;
|
||||
|
||||
*ret = strtoul(val, NULL, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int ConfigValueFloat(const char *blockName, const char *keyName, float *ret)
|
||||
int ConfigValueFloat(const char *devName, const char *blockName, const char *keyName, float *ret)
|
||||
{
|
||||
const char *val = GetConfigValue(blockName, keyName, "");
|
||||
const char *val = GetConfigValue(devName, blockName, keyName, "");
|
||||
if(!val[0]) return 0;
|
||||
|
||||
#ifdef HAVE_STRTOF
|
||||
@@ -505,9 +546,19 @@ int ConfigValueFloat(const char *blockName, const char *keyName, float *ret)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int GetConfigValueBool(const char *blockName, const char *keyName, int def)
|
||||
int ConfigValueBool(const char *devName, const char *blockName, const char *keyName, int *ret)
|
||||
{
|
||||
const char *val = GetConfigValue(blockName, keyName, "");
|
||||
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 ||
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
*
|
||||
* 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., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
@@ -127,3 +127,275 @@ void ReadRingBuffer(RingBuffer *ring, ALubyte *data, ALsizei len)
|
||||
|
||||
almtx_unlock(&ring->mtx);
|
||||
}
|
||||
|
||||
|
||||
/* NOTE: This lockless ringbuffer implementation is copied from JACK, extended
|
||||
* to include an element size. Consequently, parameters and return values for a
|
||||
* size or count is in 'elements', not bytes. Additionally, it only supports
|
||||
* single-consumer/single-provider operation. */
|
||||
struct ll_ringbuffer {
|
||||
volatile size_t write_ptr;
|
||||
volatile size_t read_ptr;
|
||||
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;
|
||||
|
||||
rb->size = power_of_two;
|
||||
rb->size_mask = rb->size - 1;
|
||||
rb->elem_size = elem_sz;
|
||||
rb->write_ptr = 0;
|
||||
rb->read_ptr = 0;
|
||||
rb->mlocked = 0;
|
||||
return rb;
|
||||
}
|
||||
|
||||
/* 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->locked && 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)
|
||||
{
|
||||
rb->read_ptr = 0;
|
||||
rb->write_ptr = 0;
|
||||
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 = rb->write_ptr;
|
||||
size_t r = rb->read_ptr;
|
||||
return (rb->size+w-r) & rb->size_mask;
|
||||
}
|
||||
/* Return the number of elements available for writing. This is the number of
|
||||
* elements in front of the write pointer and behind the read pointer. */
|
||||
size_t ll_ringbuffer_write_space(const ll_ringbuffer_t *rb)
|
||||
{
|
||||
size_t w = rb->write_ptr;
|
||||
size_t r = rb->read_ptr;
|
||||
return (rb->size+r-w-1) & rb->size_mask;
|
||||
}
|
||||
|
||||
/* 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 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;
|
||||
cnt2 = rb->read_ptr + to_read;
|
||||
if(cnt2 > rb->size)
|
||||
{
|
||||
n1 = rb->size - rb->read_ptr;
|
||||
n2 = cnt2 & rb->size_mask;
|
||||
}
|
||||
else
|
||||
{
|
||||
n1 = to_read;
|
||||
n2 = 0;
|
||||
}
|
||||
|
||||
memcpy(dest, &(rb->buf[rb->read_ptr*rb->elem_size]), n1*rb->elem_size);
|
||||
rb->read_ptr = (rb->read_ptr + n1) & rb->size_mask;
|
||||
if(n2)
|
||||
{
|
||||
memcpy(dest + n1*rb->elem_size, &(rb->buf[rb->read_ptr*rb->elem_size]), n2*rb->elem_size);
|
||||
rb->read_ptr = (rb->read_ptr + n2) & rb->size_mask;
|
||||
}
|
||||
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 tmp_read_ptr;
|
||||
|
||||
tmp_read_ptr = rb->read_ptr;
|
||||
free_cnt = ll_ringbuffer_read_space(rb);
|
||||
if(free_cnt == 0) return 0;
|
||||
|
||||
to_read = (cnt > free_cnt) ? free_cnt : cnt;
|
||||
cnt2 = tmp_read_ptr + to_read;
|
||||
if(cnt2 > rb->size)
|
||||
{
|
||||
n1 = rb->size - tmp_read_ptr;
|
||||
n2 = cnt2 & rb->size_mask;
|
||||
}
|
||||
else
|
||||
{
|
||||
n1 = to_read;
|
||||
n2 = 0;
|
||||
}
|
||||
|
||||
memcpy(dest, &(rb->buf[tmp_read_ptr*rb->elem_size]), n1*rb->elem_size);
|
||||
tmp_read_ptr = (tmp_read_ptr + n1) & rb->size_mask;
|
||||
if(n2)
|
||||
memcpy(dest + n1*rb->elem_size, &(rb->buf[tmp_read_ptr*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 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;
|
||||
cnt2 = rb->write_ptr + to_write;
|
||||
if(cnt2 > rb->size)
|
||||
{
|
||||
n1 = rb->size - rb->write_ptr;
|
||||
n2 = cnt2 & rb->size_mask;
|
||||
}
|
||||
else
|
||||
{
|
||||
n1 = to_write;
|
||||
n2 = 0;
|
||||
}
|
||||
|
||||
memcpy(&(rb->buf[rb->write_ptr*rb->elem_size]), src, n1*rb->elem_size);
|
||||
rb->write_ptr = (rb->write_ptr + n1) & rb->size_mask;
|
||||
if(n2)
|
||||
{
|
||||
memcpy(&(rb->buf[rb->write_ptr*rb->elem_size]), src + n1*rb->elem_size, n2*rb->elem_size);
|
||||
rb->write_ptr = (rb->write_ptr + n2) & rb->size_mask;
|
||||
}
|
||||
return to_write;
|
||||
}
|
||||
|
||||
/* Advance the read pointer `cnt' places. */
|
||||
void ll_ringbuffer_read_advance(ll_ringbuffer_t *rb, size_t cnt)
|
||||
{
|
||||
size_t tmp = (rb->read_ptr + cnt) & rb->size_mask;
|
||||
rb->read_ptr = tmp;
|
||||
}
|
||||
|
||||
/* Advance the write pointer `cnt' places. */
|
||||
void ll_ringbuffer_write_advance(ll_ringbuffer_t *rb, size_t cnt)
|
||||
{
|
||||
size_t tmp = (rb->write_ptr + cnt) & rb->size_mask;
|
||||
rb->write_ptr = tmp;
|
||||
}
|
||||
|
||||
/* 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 = rb->write_ptr;
|
||||
r = rb->read_ptr;
|
||||
free_cnt = (rb->size+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 = rb->write_ptr;
|
||||
r = rb->read_ptr;
|
||||
free_cnt = (rb->size+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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
typedef char al_string_char_type;
|
||||
TYPEDEF_VECTOR(al_string_char_type, al_string)
|
||||
TYPEDEF_VECTOR(al_string, vector_al_string)
|
||||
|
||||
inline void al_string_deinit(al_string *str)
|
||||
{ VECTOR_DEINIT(*str); }
|
||||
@@ -15,7 +16,7 @@ inline void al_string_deinit(al_string *str)
|
||||
#define AL_STRING_INIT_STATIC() ((al_string)NULL)
|
||||
#define AL_STRING_DEINIT(_x) al_string_deinit(&(_x))
|
||||
|
||||
inline ALsizei al_string_length(const_al_string str)
|
||||
inline size_t al_string_length(const_al_string str)
|
||||
{ return VECTOR_SIZE(str); }
|
||||
|
||||
inline ALboolean al_string_empty(const_al_string str)
|
||||
@@ -40,6 +41,8 @@ void al_string_append_range(al_string *str, const al_string_char_type *from, con
|
||||
#include <wchar.h>
|
||||
/* Windows-only methods to deal with WideChar strings. */
|
||||
void al_string_copy_wcstr(al_string *str, const wchar_t *from);
|
||||
void al_string_append_wcstr(al_string *str, const wchar_t *from);
|
||||
void al_string_append_wrange(al_string *str, const wchar_t *from, const wchar_t *to);
|
||||
#endif
|
||||
|
||||
#endif /* ALSTRING_H */
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
*
|
||||
* 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., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
@@ -273,14 +273,14 @@ static void probe_devices(snd_pcm_stream_t stream, vector_DevMap *DeviceList)
|
||||
AL_STRING_INIT(entry.name);
|
||||
AL_STRING_INIT(entry.device_name);
|
||||
al_string_copy_cstr(&entry.name, alsaDevice);
|
||||
al_string_copy_cstr(&entry.device_name, GetConfigValue("alsa", (stream==SND_PCM_STREAM_PLAYBACK) ?
|
||||
al_string_copy_cstr(&entry.device_name, GetConfigValue(NULL, "alsa", (stream==SND_PCM_STREAM_PLAYBACK) ?
|
||||
"device" : "capture", "default"));
|
||||
VECTOR_PUSH_BACK(*DeviceList, entry);
|
||||
|
||||
card = -1;
|
||||
if((err=snd_card_next(&card)) < 0)
|
||||
ERR("Failed to find a card: %s\n", snd_strerror(err));
|
||||
ConfigValueStr("alsa", prefix_name(stream), &main_prefix);
|
||||
ConfigValueStr(NULL, "alsa", prefix_name(stream), &main_prefix);
|
||||
while(card >= 0)
|
||||
{
|
||||
const char *card_prefix = main_prefix;
|
||||
@@ -304,7 +304,7 @@ static void probe_devices(snd_pcm_stream_t stream, vector_DevMap *DeviceList)
|
||||
cardid = snd_ctl_card_info_get_id(info);
|
||||
|
||||
snprintf(name, sizeof(name), "%s-%s", prefix_name(stream), cardid);
|
||||
ConfigValueStr("alsa", name, &card_prefix);
|
||||
ConfigValueStr(NULL, "alsa", name, &card_prefix);
|
||||
|
||||
dev = -1;
|
||||
while(1)
|
||||
@@ -330,7 +330,7 @@ static void probe_devices(snd_pcm_stream_t stream, vector_DevMap *DeviceList)
|
||||
devname = snd_pcm_info_get_name(pcminfo);
|
||||
|
||||
snprintf(name, sizeof(name), "%s-%s-%d", prefix_name(stream), cardid, dev);
|
||||
ConfigValueStr("alsa", name, &device_prefix);
|
||||
ConfigValueStr(NULL, "alsa", name, &device_prefix);
|
||||
|
||||
snprintf(name, sizeof(name), "%s, %s (CARD=%s,DEV=%d)",
|
||||
cardname, devname, cardid, dev);
|
||||
@@ -640,7 +640,7 @@ static ALCenum ALCplaybackAlsa_open(ALCplaybackAlsa *self, const ALCchar *name)
|
||||
else
|
||||
{
|
||||
name = alsaDevice;
|
||||
driver = GetConfigValue("alsa", "device", "default");
|
||||
driver = GetConfigValue(NULL, "alsa", "device", "default");
|
||||
}
|
||||
|
||||
TRACE("Opening device \"%s\"\n", driver);
|
||||
@@ -704,7 +704,7 @@ static ALCboolean ALCplaybackAlsa_reset(ALCplaybackAlsa *self)
|
||||
break;
|
||||
}
|
||||
|
||||
allowmmap = GetConfigValueBool("alsa", "mmap", 1);
|
||||
allowmmap = GetConfigValueBool(al_string_get_cstr(device->DeviceName), "alsa", "mmap", 1);
|
||||
periods = device->NumUpdates;
|
||||
periodLen = (ALuint64)device->UpdateSize * 1000000 / device->Frequency;
|
||||
bufferLen = periodLen * periods;
|
||||
@@ -770,8 +770,11 @@ static ALCboolean ALCplaybackAlsa_reset(ALCplaybackAlsa *self)
|
||||
}
|
||||
CHECK(snd_pcm_hw_params_set_channels(self->pcmHandle, hp, ChannelsFromDevFmt(device->FmtChans)));
|
||||
/* set rate (implicitly constrains period/buffer parameters) */
|
||||
if(snd_pcm_hw_params_set_rate_resample(self->pcmHandle, hp, 0) < 0)
|
||||
ERR("Failed to disable ALSA resampler\n");
|
||||
if(GetConfigValueBool(al_string_get_cstr(device->DeviceName), "alsa", "allow-resampler", 0))
|
||||
{
|
||||
if(snd_pcm_hw_params_set_rate_resample(self->pcmHandle, hp, 0) < 0)
|
||||
ERR("Failed to disable ALSA resampler\n");
|
||||
}
|
||||
CHECK(snd_pcm_hw_params_set_rate_near(self->pcmHandle, hp, &rate, NULL));
|
||||
/* set buffer time (implicitly constrains period/buffer parameters) */
|
||||
if((err=snd_pcm_hw_params_set_buffer_time_near(self->pcmHandle, hp, &bufferLen, NULL)) < 0)
|
||||
@@ -965,7 +968,7 @@ static ALCenum ALCcaptureAlsa_open(ALCcaptureAlsa *self, const ALCchar *name)
|
||||
else
|
||||
{
|
||||
name = alsaDevice;
|
||||
driver = GetConfigValue("alsa", "capture", "default");
|
||||
driver = GetConfigValue(NULL, "alsa", "capture", "default");
|
||||
}
|
||||
|
||||
TRACE("Opening device \"%s\"\n", driver);
|
||||
@@ -1352,25 +1355,15 @@ static ALCbackend* ALCalsaBackendFactory_createBackend(ALCalsaBackendFactory* UN
|
||||
if(type == ALCbackend_Playback)
|
||||
{
|
||||
ALCplaybackAlsa *backend;
|
||||
|
||||
backend = ALCplaybackAlsa_New(sizeof(*backend));
|
||||
NEW_OBJ(backend, ALCplaybackAlsa)(device);
|
||||
if(!backend) return NULL;
|
||||
memset(backend, 0, sizeof(*backend));
|
||||
|
||||
ALCplaybackAlsa_Construct(backend, device);
|
||||
|
||||
return STATIC_CAST(ALCbackend, backend);
|
||||
}
|
||||
if(type == ALCbackend_Capture)
|
||||
{
|
||||
ALCcaptureAlsa *backend;
|
||||
|
||||
backend = ALCcaptureAlsa_New(sizeof(*backend));
|
||||
NEW_OBJ(backend, ALCcaptureAlsa)(device);
|
||||
if(!backend) return NULL;
|
||||
memset(backend, 0, sizeof(*backend));
|
||||
|
||||
ALCcaptureAlsa_Construct(backend, device);
|
||||
|
||||
return STATIC_CAST(ALCbackend, backend);
|
||||
}
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@ static ALCboolean PlaybackWrapper_start(PlaybackWrapper *self);
|
||||
static void PlaybackWrapper_stop(PlaybackWrapper *self);
|
||||
static DECLARE_FORWARD2(PlaybackWrapper, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
|
||||
static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, ALCuint, availableSamples)
|
||||
static ALint64 PlaybackWrapper_getLatency(PlaybackWrapper *self);
|
||||
static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, ALint64, getLatency)
|
||||
static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, void, lock)
|
||||
static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, void, unlock)
|
||||
DECLARE_DEFAULT_ALLOCATORS(PlaybackWrapper)
|
||||
@@ -121,12 +121,6 @@ static void PlaybackWrapper_stop(PlaybackWrapper *self)
|
||||
self->Funcs->StopPlayback(device);
|
||||
}
|
||||
|
||||
static ALint64 PlaybackWrapper_getLatency(PlaybackWrapper *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
return self->Funcs->GetLatency(device);
|
||||
}
|
||||
|
||||
|
||||
typedef struct CaptureWrapper {
|
||||
DERIVE_FROM_TYPE(ALCbackend);
|
||||
@@ -143,13 +137,12 @@ static ALCboolean CaptureWrapper_start(CaptureWrapper *self);
|
||||
static void CaptureWrapper_stop(CaptureWrapper *self);
|
||||
static ALCenum CaptureWrapper_captureSamples(CaptureWrapper *self, void *buffer, ALCuint samples);
|
||||
static ALCuint CaptureWrapper_availableSamples(CaptureWrapper *self);
|
||||
static ALint64 CaptureWrapper_getLatency(CaptureWrapper *self);
|
||||
static DECLARE_FORWARD(CaptureWrapper, ALCbackend, ALint64, getLatency)
|
||||
static DECLARE_FORWARD(CaptureWrapper, ALCbackend, void, lock)
|
||||
static DECLARE_FORWARD(CaptureWrapper, ALCbackend, void, unlock)
|
||||
DECLARE_DEFAULT_ALLOCATORS(CaptureWrapper)
|
||||
DEFINE_ALCBACKEND_VTABLE(CaptureWrapper);
|
||||
|
||||
|
||||
static void CaptureWrapper_Construct(CaptureWrapper *self, ALCdevice *device, const BackendFuncs *funcs)
|
||||
{
|
||||
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
|
||||
@@ -195,12 +188,6 @@ static ALCuint CaptureWrapper_availableSamples(CaptureWrapper *self)
|
||||
return self->Funcs->AvailableSamples(device);
|
||||
}
|
||||
|
||||
static ALint64 CaptureWrapper_getLatency(CaptureWrapper *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
return self->Funcs->GetLatency(device);
|
||||
}
|
||||
|
||||
|
||||
ALCbackend *create_backend_wrapper(ALCdevice *device, const BackendFuncs *funcs, ALCbackend_Type type)
|
||||
{
|
||||
@@ -208,11 +195,9 @@ ALCbackend *create_backend_wrapper(ALCdevice *device, const BackendFuncs *funcs,
|
||||
{
|
||||
PlaybackWrapper *backend;
|
||||
|
||||
backend = PlaybackWrapper_New(sizeof(*backend));
|
||||
NEW_OBJ(backend, PlaybackWrapper)(device, funcs);
|
||||
if(!backend) return NULL;
|
||||
|
||||
PlaybackWrapper_Construct(backend, device, funcs);
|
||||
|
||||
return STATIC_CAST(ALCbackend, backend);
|
||||
}
|
||||
|
||||
@@ -220,11 +205,9 @@ ALCbackend *create_backend_wrapper(ALCdevice *device, const BackendFuncs *funcs,
|
||||
{
|
||||
CaptureWrapper *backend;
|
||||
|
||||
backend = CaptureWrapper_New(sizeof(*backend));
|
||||
NEW_OBJ(backend, CaptureWrapper)(device, funcs);
|
||||
if(!backend) return NULL;
|
||||
|
||||
CaptureWrapper_Construct(backend, device, funcs);
|
||||
|
||||
return STATIC_CAST(ALCbackend, backend);
|
||||
}
|
||||
|
||||
|
||||
@@ -123,9 +123,14 @@ static const struct ALCbackendFactoryVtable T##_ALCbackendFactory_vtable = { \
|
||||
ALCbackendFactory *ALCpulseBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCalsaBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCossBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCjackBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCsolarisBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCmmdevBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCdsoundBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCwinmmBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCportBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCnullBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCwaveBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCloopbackFactory_getFactory(void);
|
||||
|
||||
ALCbackend *create_backend_wrapper(ALCdevice *device, const BackendFuncs *funcs, ALCbackend_Type type);
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
*
|
||||
* 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., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
@@ -383,6 +383,11 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
|
||||
ca_data *data;
|
||||
OSStatus err;
|
||||
|
||||
if(!deviceName)
|
||||
deviceName = ca_device;
|
||||
else if(strcmp(deviceName, ca_device) != 0)
|
||||
return ALC_INVALID_VALUE;
|
||||
|
||||
desc.componentType = kAudioUnitType_Output;
|
||||
desc.componentSubType = kAudioUnitSubType_HALOutput;
|
||||
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
|
||||
@@ -514,9 +519,10 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
|
||||
|
||||
case DevFmtQuad:
|
||||
case DevFmtX51:
|
||||
case DevFmtX51Side:
|
||||
case DevFmtX51Rear:
|
||||
case DevFmtX61:
|
||||
case DevFmtX71:
|
||||
case DevFmtBFormat3D:
|
||||
ERR("%s not supported\n", DevFmtChannelsString(device->FmtChans));
|
||||
goto error;
|
||||
}
|
||||
@@ -679,8 +685,7 @@ static const BackendFuncs ca_funcs = {
|
||||
ca_start_capture,
|
||||
ca_stop_capture,
|
||||
ca_capture_samples,
|
||||
ca_available_samples,
|
||||
ALCdevice_GetLatencyDefault
|
||||
ca_available_samples
|
||||
};
|
||||
|
||||
ALCboolean alc_ca_init(BackendFuncs *func_list)
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
*
|
||||
* 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., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
@@ -43,6 +43,9 @@
|
||||
#ifndef DSSPEAKER_5POINT1
|
||||
# define DSSPEAKER_5POINT1 0x00000006
|
||||
#endif
|
||||
#ifndef DSSPEAKER_5POINT1_BACK
|
||||
# define DSSPEAKER_5POINT1_BACK 0x00000006
|
||||
#endif
|
||||
#ifndef DSSPEAKER_7POINT1
|
||||
# define DSSPEAKER_7POINT1 0x00000007
|
||||
#endif
|
||||
@@ -57,6 +60,8 @@
|
||||
DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
|
||||
DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
|
||||
|
||||
#define DEVNAME_TAIL " on OpenAL Soft"
|
||||
|
||||
|
||||
#ifdef HAVE_DYNLOAD
|
||||
static void *ds_handle;
|
||||
@@ -118,15 +123,14 @@ static void clear_devlist(vector_DevMap *list)
|
||||
{
|
||||
#define DEINIT_STR(i) AL_STRING_DEINIT((i)->name)
|
||||
VECTOR_FOR_EACH(DevMap, *list, DEINIT_STR);
|
||||
#undef DEINIT_STR
|
||||
VECTOR_RESIZE(*list, 0);
|
||||
#undef DEINIT_STR
|
||||
}
|
||||
|
||||
static BOOL CALLBACK DSoundEnumDevices(GUID *guid, const WCHAR *desc, const WCHAR* UNUSED(drvname), void *data)
|
||||
{
|
||||
vector_DevMap *devices = data;
|
||||
OLECHAR *guidstr = NULL;
|
||||
DevMap *iter, *end;
|
||||
DevMap entry;
|
||||
HRESULT hr;
|
||||
int count;
|
||||
@@ -137,24 +141,26 @@ static BOOL CALLBACK DSoundEnumDevices(GUID *guid, const WCHAR *desc, const WCHA
|
||||
AL_STRING_INIT(entry.name);
|
||||
|
||||
count = 0;
|
||||
do {
|
||||
while(1)
|
||||
{
|
||||
const DevMap *iter;
|
||||
|
||||
al_string_copy_wcstr(&entry.name, desc);
|
||||
if(count != 0)
|
||||
if(count == 0)
|
||||
al_string_append_cstr(&entry.name, DEVNAME_TAIL);
|
||||
else
|
||||
{
|
||||
char str[64];
|
||||
snprintf(str, sizeof(str), " #%d", count+1);
|
||||
snprintf(str, sizeof(str), " #%d"DEVNAME_TAIL, count+1);
|
||||
al_string_append_cstr(&entry.name, str);
|
||||
}
|
||||
count++;
|
||||
|
||||
iter = VECTOR_ITER_BEGIN(*devices);
|
||||
end = VECTOR_ITER_END(*devices);
|
||||
for(;iter != end;++iter)
|
||||
{
|
||||
if(al_string_cmp(entry.name, iter->name) == 0)
|
||||
break;
|
||||
}
|
||||
} while(iter != end);
|
||||
#define MATCH_ENTRY(i) (al_string_cmp(entry.name, (i)->name) == 0)
|
||||
VECTOR_FIND_IF(iter, const DevMap, *devices, MATCH_ENTRY);
|
||||
if(iter == VECTOR_ITER_END(*devices)) break;
|
||||
#undef MATCH_ENTRY
|
||||
count++;
|
||||
}
|
||||
entry.guid = *guid;
|
||||
|
||||
hr = StringFromCLSID(guid, &guidstr);
|
||||
@@ -441,28 +447,35 @@ static ALCboolean ALCdsoundPlayback_reset(ALCdsoundPlayback *self)
|
||||
hr = IDirectSound_GetSpeakerConfig(self->DS, &speakers);
|
||||
if(SUCCEEDED(hr))
|
||||
{
|
||||
speakers = DSSPEAKER_CONFIG(speakers);
|
||||
if(!(device->Flags&DEVICE_CHANNELS_REQUEST))
|
||||
{
|
||||
speakers = DSSPEAKER_CONFIG(speakers);
|
||||
if(speakers == DSSPEAKER_MONO)
|
||||
device->FmtChans = DevFmtMono;
|
||||
else if(speakers == DSSPEAKER_STEREO || speakers == DSSPEAKER_HEADPHONE)
|
||||
device->FmtChans = DevFmtStereo;
|
||||
else if(speakers == DSSPEAKER_QUAD)
|
||||
device->FmtChans = DevFmtQuad;
|
||||
else if(speakers == DSSPEAKER_5POINT1 || speakers == DSSPEAKER_5POINT1_SURROUND)
|
||||
else if(speakers == DSSPEAKER_5POINT1_SURROUND)
|
||||
device->FmtChans = DevFmtX51;
|
||||
else if(speakers == DSSPEAKER_5POINT1_BACK)
|
||||
device->FmtChans = DevFmtX51Rear;
|
||||
else if(speakers == DSSPEAKER_7POINT1 || speakers == DSSPEAKER_7POINT1_SURROUND)
|
||||
device->FmtChans = DevFmtX71;
|
||||
else
|
||||
ERR("Unknown system speaker config: 0x%lx\n", speakers);
|
||||
}
|
||||
device->IsHeadphones = (device->FmtChans == DevFmtStereo &&
|
||||
speakers == DSSPEAKER_HEADPHONE);
|
||||
|
||||
switch(device->FmtChans)
|
||||
{
|
||||
case DevFmtMono:
|
||||
OutputType.dwChannelMask = SPEAKER_FRONT_CENTER;
|
||||
break;
|
||||
case DevFmtBFormat3D:
|
||||
device->FmtChans = DevFmtStereo;
|
||||
/*fall-through*/
|
||||
case DevFmtStereo:
|
||||
OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
|
||||
SPEAKER_FRONT_RIGHT;
|
||||
@@ -478,16 +491,16 @@ static ALCboolean ALCdsoundPlayback_reset(ALCdsoundPlayback *self)
|
||||
SPEAKER_FRONT_RIGHT |
|
||||
SPEAKER_FRONT_CENTER |
|
||||
SPEAKER_LOW_FREQUENCY |
|
||||
SPEAKER_BACK_LEFT |
|
||||
SPEAKER_BACK_RIGHT;
|
||||
SPEAKER_SIDE_LEFT |
|
||||
SPEAKER_SIDE_RIGHT;
|
||||
break;
|
||||
case DevFmtX51Side:
|
||||
case DevFmtX51Rear:
|
||||
OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
|
||||
SPEAKER_FRONT_RIGHT |
|
||||
SPEAKER_FRONT_CENTER |
|
||||
SPEAKER_LOW_FREQUENCY |
|
||||
SPEAKER_SIDE_LEFT |
|
||||
SPEAKER_SIDE_RIGHT;
|
||||
SPEAKER_BACK_LEFT |
|
||||
SPEAKER_BACK_RIGHT;
|
||||
break;
|
||||
case DevFmtX61:
|
||||
OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
|
||||
@@ -745,16 +758,16 @@ static ALCenum ALCdsoundCapture_open(ALCdsoundCapture *self, const ALCchar *devi
|
||||
SPEAKER_FRONT_RIGHT |
|
||||
SPEAKER_FRONT_CENTER |
|
||||
SPEAKER_LOW_FREQUENCY |
|
||||
SPEAKER_BACK_LEFT |
|
||||
SPEAKER_BACK_RIGHT;
|
||||
SPEAKER_SIDE_LEFT |
|
||||
SPEAKER_SIDE_RIGHT;
|
||||
break;
|
||||
case DevFmtX51Side:
|
||||
case DevFmtX51Rear:
|
||||
InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
|
||||
SPEAKER_FRONT_RIGHT |
|
||||
SPEAKER_FRONT_CENTER |
|
||||
SPEAKER_LOW_FREQUENCY |
|
||||
SPEAKER_SIDE_LEFT |
|
||||
SPEAKER_SIDE_RIGHT;
|
||||
SPEAKER_BACK_LEFT |
|
||||
SPEAKER_BACK_RIGHT;
|
||||
break;
|
||||
case DevFmtX61:
|
||||
InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
|
||||
@@ -775,6 +788,8 @@ static ALCenum ALCdsoundCapture_open(ALCdsoundCapture *self, const ALCchar *devi
|
||||
SPEAKER_SIDE_LEFT |
|
||||
SPEAKER_SIDE_RIGHT;
|
||||
break;
|
||||
case DevFmtBFormat3D:
|
||||
break;
|
||||
}
|
||||
|
||||
InputType.Format.wFormatTag = WAVE_FORMAT_PCM;
|
||||
@@ -1027,26 +1042,16 @@ static ALCbackend* ALCdsoundBackendFactory_createBackend(ALCdsoundBackendFactory
|
||||
if(type == ALCbackend_Playback)
|
||||
{
|
||||
ALCdsoundPlayback *backend;
|
||||
|
||||
backend = ALCdsoundPlayback_New(sizeof(*backend));
|
||||
NEW_OBJ(backend, ALCdsoundPlayback)(device);
|
||||
if(!backend) return NULL;
|
||||
memset(backend, 0, sizeof(*backend));
|
||||
|
||||
ALCdsoundPlayback_Construct(backend, device);
|
||||
|
||||
return STATIC_CAST(ALCbackend, backend);
|
||||
}
|
||||
|
||||
if(type == ALCbackend_Capture)
|
||||
{
|
||||
ALCdsoundCapture *backend;
|
||||
|
||||
backend = ALCdsoundCapture_New(sizeof(*backend));
|
||||
NEW_OBJ(backend, ALCdsoundCapture)(device);
|
||||
if(!backend) return NULL;
|
||||
memset(backend, 0, sizeof(*backend));
|
||||
|
||||
ALCdsoundCapture_Construct(backend, device);
|
||||
|
||||
return STATIC_CAST(ALCbackend, backend);
|
||||
}
|
||||
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
*
|
||||
* 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., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
@@ -124,13 +124,8 @@ static ALCbackend* ALCloopbackFactory_createBackend(ALCloopbackFactory* UNUSED(s
|
||||
if(type == ALCbackend_Loopback)
|
||||
{
|
||||
ALCloopback *backend;
|
||||
|
||||
backend = ALCloopback_New(sizeof(*backend));
|
||||
NEW_OBJ(backend, ALCloopback)(device);
|
||||
if(!backend) return NULL;
|
||||
memset(backend, 0, sizeof(*backend));
|
||||
|
||||
ALCloopback_Construct(backend, device);
|
||||
|
||||
return STATIC_CAST(ALCbackend, backend);
|
||||
}
|
||||
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
*
|
||||
* 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., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
@@ -51,14 +51,18 @@ DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x
|
||||
DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
|
||||
|
||||
DEFINE_DEVPROPKEY(DEVPKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80,0x20, 0x67,0xd1,0x46,0xa8,0x50,0xe0, 14);
|
||||
DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_FormFactor, 0x1da5d803, 0xd492, 0x4edd, 0x8c,0x23, 0xe0,0xc0,0xff,0xee,0x7f,0x0e, 0);
|
||||
|
||||
#define MONO SPEAKER_FRONT_CENTER
|
||||
#define STEREO (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT)
|
||||
#define QUAD (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT)
|
||||
#define X5DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT)
|
||||
#define X5DOT1SIDE (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
|
||||
#define X5DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
|
||||
#define X5DOT1REAR (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT)
|
||||
#define X6DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_CENTER|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
|
||||
#define X7DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
|
||||
#define X7DOT1_WIDE (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT|SPEAKER_FRONT_LEFT_OF_CENTER|SPEAKER_FRONT_RIGHT_OF_CENTER)
|
||||
|
||||
#define DEVNAME_TAIL " on OpenAL Soft"
|
||||
|
||||
|
||||
typedef struct {
|
||||
@@ -69,16 +73,14 @@ TYPEDEF_VECTOR(DevMap, vector_DevMap)
|
||||
|
||||
static void clear_devlist(vector_DevMap *list)
|
||||
{
|
||||
DevMap *iter, *end;
|
||||
|
||||
iter = VECTOR_ITER_BEGIN(*list);
|
||||
end = VECTOR_ITER_END(*list);
|
||||
for(;iter != end;iter++)
|
||||
{
|
||||
AL_STRING_DEINIT(iter->name);
|
||||
free(iter->devid);
|
||||
}
|
||||
#define CLEAR_DEVMAP(i) do { \
|
||||
AL_STRING_DEINIT((i)->name); \
|
||||
free((i)->devid); \
|
||||
(i)->devid = NULL; \
|
||||
} while(0)
|
||||
VECTOR_FOR_EACH(DevMap, *list, CLEAR_DEVMAP);
|
||||
VECTOR_RESIZE(*list, 0);
|
||||
#undef CLEAR_DEVMAP
|
||||
}
|
||||
|
||||
static vector_DevMap PlaybackDevices;
|
||||
@@ -134,39 +136,105 @@ static void get_device_name(IMMDevice *device, al_string *name)
|
||||
|
||||
hr = IPropertyStore_GetValue(ps, (const PROPERTYKEY*)&DEVPKEY_Device_FriendlyName, &pvname);
|
||||
if(FAILED(hr))
|
||||
WARN("GetValue failed: 0x%08lx\n", hr);
|
||||
else
|
||||
WARN("GetValue Device_FriendlyName failed: 0x%08lx\n", hr);
|
||||
else if(pvname.vt == VT_LPWSTR)
|
||||
al_string_copy_wcstr(name, pvname.pwszVal);
|
||||
else
|
||||
WARN("Unexpected PROPVARIANT type: 0x%04x\n", pvname.vt);
|
||||
|
||||
PropVariantClear(&pvname);
|
||||
IPropertyStore_Release(ps);
|
||||
}
|
||||
|
||||
static void add_device(IMMDevice *device, vector_DevMap *list)
|
||||
static void get_device_formfactor(IMMDevice *device, EndpointFormFactor *formfactor)
|
||||
{
|
||||
IPropertyStore *ps;
|
||||
PROPVARIANT pvform;
|
||||
HRESULT hr;
|
||||
|
||||
hr = IMMDevice_OpenPropertyStore(device, STGM_READ, &ps);
|
||||
if(FAILED(hr))
|
||||
{
|
||||
WARN("OpenPropertyStore failed: 0x%08lx\n", hr);
|
||||
return;
|
||||
}
|
||||
|
||||
PropVariantInit(&pvform);
|
||||
|
||||
hr = IPropertyStore_GetValue(ps, &PKEY_AudioEndpoint_FormFactor, &pvform);
|
||||
if(FAILED(hr))
|
||||
WARN("GetValue AudioEndpoint_FormFactor failed: 0x%08lx\n", hr);
|
||||
else if(pvform.vt == VT_UI4)
|
||||
*formfactor = pvform.ulVal;
|
||||
else if(pvform.vt == VT_EMPTY)
|
||||
*formfactor = UnknownFormFactor;
|
||||
else
|
||||
WARN("Unexpected PROPVARIANT type: 0x%04x\n", pvform.vt);
|
||||
|
||||
PropVariantClear(&pvform);
|
||||
IPropertyStore_Release(ps);
|
||||
}
|
||||
|
||||
|
||||
static void add_device(IMMDevice *device, LPCWSTR devid, vector_DevMap *list)
|
||||
{
|
||||
int count = 0;
|
||||
al_string tmpname;
|
||||
DevMap entry;
|
||||
|
||||
AL_STRING_INIT(tmpname);
|
||||
AL_STRING_INIT(entry.name);
|
||||
|
||||
entry.devid = strdupW(devid);
|
||||
get_device_name(device, &tmpname);
|
||||
|
||||
while(1)
|
||||
{
|
||||
const DevMap *iter;
|
||||
|
||||
al_string_copy(&entry.name, tmpname);
|
||||
if(count == 0)
|
||||
al_string_append_cstr(&entry.name, DEVNAME_TAIL);
|
||||
else
|
||||
{
|
||||
char str[64];
|
||||
snprintf(str, sizeof(str), " #%d"DEVNAME_TAIL, count+1);
|
||||
al_string_append_cstr(&entry.name, str);
|
||||
}
|
||||
|
||||
#define MATCH_ENTRY(i) (al_string_cmp(entry.name, (i)->name) == 0)
|
||||
VECTOR_FIND_IF(iter, const DevMap, *list, MATCH_ENTRY);
|
||||
if(iter == VECTOR_ITER_END(*list)) break;
|
||||
#undef MATCH_ENTRY
|
||||
count++;
|
||||
}
|
||||
|
||||
TRACE("Got device \"%s\", \"%ls\"\n", al_string_get_cstr(entry.name), entry.devid);
|
||||
VECTOR_PUSH_BACK(*list, entry);
|
||||
|
||||
AL_STRING_DEINIT(tmpname);
|
||||
}
|
||||
|
||||
static LPWSTR get_device_id(IMMDevice *device)
|
||||
{
|
||||
LPWSTR devid;
|
||||
HRESULT hr;
|
||||
|
||||
hr = IMMDevice_GetId(device, &devid);
|
||||
if(SUCCEEDED(hr))
|
||||
if(FAILED(hr))
|
||||
{
|
||||
DevMap entry;
|
||||
AL_STRING_INIT(entry.name);
|
||||
|
||||
entry.devid = strdupW(devid);
|
||||
get_device_name(device, &entry.name);
|
||||
|
||||
CoTaskMemFree(devid);
|
||||
|
||||
TRACE("Got device \"%s\", \"%ls\"\n", al_string_get_cstr(entry.name), entry.devid);
|
||||
VECTOR_PUSH_BACK(*list, entry);
|
||||
ERR("Failed to get device id: %lx\n", hr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return devid;
|
||||
}
|
||||
|
||||
static HRESULT probe_devices(IMMDeviceEnumerator *devenum, EDataFlow flowdir, vector_DevMap *list)
|
||||
{
|
||||
IMMDeviceCollection *coll;
|
||||
IMMDevice *defdev = NULL;
|
||||
LPWSTR defdevid = NULL;
|
||||
HRESULT hr;
|
||||
UINT count;
|
||||
UINT i;
|
||||
@@ -183,7 +251,7 @@ static HRESULT probe_devices(IMMDeviceEnumerator *devenum, EDataFlow flowdir, ve
|
||||
if(SUCCEEDED(hr) && count > 0)
|
||||
{
|
||||
clear_devlist(list);
|
||||
if(!VECTOR_RESERVE(*list, count+1))
|
||||
if(!VECTOR_RESERVE(*list, count))
|
||||
{
|
||||
IMMDeviceCollection_Release(coll);
|
||||
return E_OUTOFMEMORY;
|
||||
@@ -193,22 +261,32 @@ static HRESULT probe_devices(IMMDeviceEnumerator *devenum, EDataFlow flowdir, ve
|
||||
eMultimedia, &defdev);
|
||||
}
|
||||
if(SUCCEEDED(hr) && defdev != NULL)
|
||||
add_device(defdev, list);
|
||||
{
|
||||
defdevid = get_device_id(defdev);
|
||||
if(defdevid)
|
||||
add_device(defdev, defdevid, list);
|
||||
}
|
||||
|
||||
for(i = 0;i < count;++i)
|
||||
{
|
||||
IMMDevice *device;
|
||||
LPWSTR devid;
|
||||
|
||||
if(FAILED(IMMDeviceCollection_Item(coll, i, &device)))
|
||||
continue;
|
||||
|
||||
if(device != defdev)
|
||||
add_device(device, list);
|
||||
hr = IMMDeviceCollection_Item(coll, i, &device);
|
||||
if(FAILED(hr)) continue;
|
||||
|
||||
devid = get_device_id(device);
|
||||
if(devid)
|
||||
{
|
||||
if(wcscmp(devid, defdevid) != 0)
|
||||
add_device(device, devid, list);
|
||||
CoTaskMemFree(devid);
|
||||
}
|
||||
IMMDevice_Release(device);
|
||||
}
|
||||
|
||||
if(defdev) IMMDevice_Release(defdev);
|
||||
if(defdevid) CoTaskMemFree(defdevid);
|
||||
IMMDeviceCollection_Release(coll);
|
||||
|
||||
return S_OK;
|
||||
@@ -294,7 +372,7 @@ static DWORD CALLBACK ALCmmdevProxy_messageHandler(void *ptr)
|
||||
TRACE("Starting message loop\n");
|
||||
while(GetMessage(&msg, NULL, WM_USER_First, WM_USER_Last))
|
||||
{
|
||||
TRACE("Got message %u\n", msg.message);
|
||||
TRACE("Got message %u (lparam=%p, wparam=%p)\n", msg.message, (void*)msg.lParam, (void*)msg.wParam);
|
||||
switch(msg.message)
|
||||
{
|
||||
case WM_USER_OpenDevice:
|
||||
@@ -483,9 +561,9 @@ FORCE_ALIGN static int ALCmmdevPlayback_mixerProc(void *arg)
|
||||
if(FAILED(hr))
|
||||
{
|
||||
ERR("CoInitialize(NULL) failed: 0x%08lx\n", hr);
|
||||
ALCdevice_Lock(device);
|
||||
V0(device->Backend,lock)();
|
||||
aluHandleDisconnect(device);
|
||||
ALCdevice_Unlock(device);
|
||||
V0(device->Backend,unlock)();
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -500,9 +578,9 @@ FORCE_ALIGN static int ALCmmdevPlayback_mixerProc(void *arg)
|
||||
if(FAILED(hr))
|
||||
{
|
||||
ERR("Failed to get padding: 0x%08lx\n", hr);
|
||||
ALCdevice_Lock(device);
|
||||
V0(device->Backend,lock)();
|
||||
aluHandleDisconnect(device);
|
||||
ALCdevice_Unlock(device);
|
||||
V0(device->Backend,unlock)();
|
||||
break;
|
||||
}
|
||||
self->Padding = written;
|
||||
@@ -521,18 +599,18 @@ FORCE_ALIGN static int ALCmmdevPlayback_mixerProc(void *arg)
|
||||
hr = IAudioRenderClient_GetBuffer(self->render, len, &buffer);
|
||||
if(SUCCEEDED(hr))
|
||||
{
|
||||
ALCdevice_Lock(device);
|
||||
V0(device->Backend,lock)();
|
||||
aluMixData(device, buffer, len);
|
||||
self->Padding = written + len;
|
||||
ALCdevice_Unlock(device);
|
||||
V0(device->Backend,unlock)();
|
||||
hr = IAudioRenderClient_ReleaseBuffer(self->render, len, 0);
|
||||
}
|
||||
if(FAILED(hr))
|
||||
{
|
||||
ERR("Failed to buffer data: 0x%08lx\n", hr);
|
||||
ALCdevice_Lock(device);
|
||||
V0(device->Backend,lock)();
|
||||
aluHandleDisconnect(device);
|
||||
ALCdevice_Unlock(device);
|
||||
V0(device->Backend,unlock)();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -599,7 +677,7 @@ static ALCenum ALCmmdevPlayback_open(ALCmmdevPlayback *self, const ALCchar *devi
|
||||
{
|
||||
if(deviceName)
|
||||
{
|
||||
const DevMap *iter, *end;
|
||||
const DevMap *iter;
|
||||
|
||||
if(VECTOR_SIZE(PlaybackDevices) == 0)
|
||||
{
|
||||
@@ -609,19 +687,18 @@ static ALCenum ALCmmdevPlayback_open(ALCmmdevPlayback *self, const ALCchar *devi
|
||||
}
|
||||
|
||||
hr = E_FAIL;
|
||||
iter = VECTOR_ITER_BEGIN(PlaybackDevices);
|
||||
end = VECTOR_ITER_END(PlaybackDevices);
|
||||
for(;iter != end;iter++)
|
||||
{
|
||||
if(al_string_cmp_cstr(iter->name, deviceName) == 0)
|
||||
{
|
||||
self->devid = strdupW(iter->devid);
|
||||
hr = S_OK;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(FAILED(hr))
|
||||
#define MATCH_NAME(i) (al_string_cmp_cstr((i)->name, deviceName) == 0)
|
||||
VECTOR_FIND_IF(iter, const DevMap, PlaybackDevices, MATCH_NAME);
|
||||
if(iter == VECTOR_ITER_END(PlaybackDevices))
|
||||
WARN("Failed to find device name matching \"%s\"\n", deviceName);
|
||||
else
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
|
||||
self->devid = strdupW(iter->devid);
|
||||
al_string_copy(&device->DeviceName, iter->name);
|
||||
hr = S_OK;
|
||||
}
|
||||
#undef MATCH_NAME
|
||||
}
|
||||
}
|
||||
|
||||
@@ -677,7 +754,11 @@ static HRESULT ALCmmdevPlayback_openProxy(ALCmmdevPlayback *self)
|
||||
if(SUCCEEDED(hr))
|
||||
{
|
||||
self->client = ptr;
|
||||
get_device_name(self->mmdev, &device->DeviceName);
|
||||
if(al_string_empty(device->DeviceName))
|
||||
{
|
||||
get_device_name(self->mmdev, &device->DeviceName);
|
||||
al_string_append_cstr(&device->DeviceName, DEVNAME_TAIL);
|
||||
}
|
||||
}
|
||||
|
||||
if(FAILED(hr))
|
||||
@@ -734,6 +815,7 @@ static ALCboolean ALCmmdevPlayback_reset(ALCmmdevPlayback *self)
|
||||
static HRESULT ALCmmdevPlayback_resetProxy(ALCmmdevPlayback *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
EndpointFormFactor formfactor = UnknownFormFactor;
|
||||
WAVEFORMATEXTENSIBLE OutputType;
|
||||
WAVEFORMATEX *wfx = NULL;
|
||||
REFERENCE_TIME min_per, buf_time;
|
||||
@@ -783,11 +865,11 @@ static HRESULT ALCmmdevPlayback_resetProxy(ALCmmdevPlayback *self)
|
||||
device->FmtChans = DevFmtQuad;
|
||||
else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1)
|
||||
device->FmtChans = DevFmtX51;
|
||||
else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1SIDE)
|
||||
device->FmtChans = DevFmtX51Side;
|
||||
else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1REAR)
|
||||
device->FmtChans = DevFmtX51Rear;
|
||||
else if(OutputType.Format.nChannels == 7 && OutputType.dwChannelMask == X6DOT1)
|
||||
device->FmtChans = DevFmtX61;
|
||||
else if(OutputType.Format.nChannels == 8 && OutputType.dwChannelMask == X7DOT1)
|
||||
else if(OutputType.Format.nChannels == 8 && (OutputType.dwChannelMask == X7DOT1 || OutputType.dwChannelMask == X7DOT1_WIDE))
|
||||
device->FmtChans = DevFmtX71;
|
||||
else
|
||||
ERR("Unhandled channel config: %d -- 0x%08lx\n", OutputType.Format.nChannels, OutputType.dwChannelMask);
|
||||
@@ -799,6 +881,9 @@ static HRESULT ALCmmdevPlayback_resetProxy(ALCmmdevPlayback *self)
|
||||
OutputType.Format.nChannels = 1;
|
||||
OutputType.dwChannelMask = MONO;
|
||||
break;
|
||||
case DevFmtBFormat3D:
|
||||
device->FmtChans = DevFmtStereo;
|
||||
/*fall-through*/
|
||||
case DevFmtStereo:
|
||||
OutputType.Format.nChannels = 2;
|
||||
OutputType.dwChannelMask = STEREO;
|
||||
@@ -811,9 +896,9 @@ static HRESULT ALCmmdevPlayback_resetProxy(ALCmmdevPlayback *self)
|
||||
OutputType.Format.nChannels = 6;
|
||||
OutputType.dwChannelMask = X5DOT1;
|
||||
break;
|
||||
case DevFmtX51Side:
|
||||
case DevFmtX51Rear:
|
||||
OutputType.Format.nChannels = 6;
|
||||
OutputType.dwChannelMask = X5DOT1SIDE;
|
||||
OutputType.dwChannelMask = X5DOT1REAR;
|
||||
break;
|
||||
case DevFmtX61:
|
||||
OutputType.Format.nChannels = 7;
|
||||
@@ -894,11 +979,11 @@ static HRESULT ALCmmdevPlayback_resetProxy(ALCmmdevPlayback *self)
|
||||
device->FmtChans = DevFmtQuad;
|
||||
else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1)
|
||||
device->FmtChans = DevFmtX51;
|
||||
else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1SIDE)
|
||||
device->FmtChans = DevFmtX51Side;
|
||||
else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1REAR)
|
||||
device->FmtChans = DevFmtX51Rear;
|
||||
else if(OutputType.Format.nChannels == 7 && OutputType.dwChannelMask == X6DOT1)
|
||||
device->FmtChans = DevFmtX61;
|
||||
else if(OutputType.Format.nChannels == 8 && OutputType.dwChannelMask == X7DOT1)
|
||||
else if(OutputType.Format.nChannels == 8 && (OutputType.dwChannelMask == X7DOT1 || OutputType.dwChannelMask == X7DOT1_WIDE))
|
||||
device->FmtChans = DevFmtX71;
|
||||
else
|
||||
{
|
||||
@@ -936,6 +1021,8 @@ static HRESULT ALCmmdevPlayback_resetProxy(ALCmmdevPlayback *self)
|
||||
}
|
||||
OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
|
||||
}
|
||||
get_device_formfactor(self->mmdev, &formfactor);
|
||||
device->IsHeadphones = (device->FmtChans == DevFmtStereo && formfactor == Headphones);
|
||||
|
||||
SetDefaultWFXChannelOrder(device);
|
||||
|
||||
@@ -1055,6 +1142,575 @@ static ALint64 ALCmmdevPlayback_getLatency(ALCmmdevPlayback *self)
|
||||
}
|
||||
|
||||
|
||||
typedef struct ALCmmdevCapture {
|
||||
DERIVE_FROM_TYPE(ALCbackend);
|
||||
DERIVE_FROM_TYPE(ALCmmdevProxy);
|
||||
|
||||
WCHAR *devid;
|
||||
|
||||
IMMDevice *mmdev;
|
||||
IAudioClient *client;
|
||||
IAudioCaptureClient *capture;
|
||||
HANDLE NotifyEvent;
|
||||
|
||||
HANDLE MsgEvent;
|
||||
|
||||
ll_ringbuffer_t *Ring;
|
||||
|
||||
volatile int killNow;
|
||||
althrd_t thread;
|
||||
} ALCmmdevCapture;
|
||||
|
||||
static int ALCmmdevCapture_recordProc(void *arg);
|
||||
|
||||
static void ALCmmdevCapture_Construct(ALCmmdevCapture *self, ALCdevice *device);
|
||||
static void ALCmmdevCapture_Destruct(ALCmmdevCapture *self);
|
||||
static ALCenum ALCmmdevCapture_open(ALCmmdevCapture *self, const ALCchar *name);
|
||||
static HRESULT ALCmmdevCapture_openProxy(ALCmmdevCapture *self);
|
||||
static void ALCmmdevCapture_close(ALCmmdevCapture *self);
|
||||
static void ALCmmdevCapture_closeProxy(ALCmmdevCapture *self);
|
||||
static DECLARE_FORWARD(ALCmmdevCapture, ALCbackend, ALCboolean, reset)
|
||||
static HRESULT ALCmmdevCapture_resetProxy(ALCmmdevCapture *self);
|
||||
static ALCboolean ALCmmdevCapture_start(ALCmmdevCapture *self);
|
||||
static HRESULT ALCmmdevCapture_startProxy(ALCmmdevCapture *self);
|
||||
static void ALCmmdevCapture_stop(ALCmmdevCapture *self);
|
||||
static void ALCmmdevCapture_stopProxy(ALCmmdevCapture *self);
|
||||
static ALCenum ALCmmdevCapture_captureSamples(ALCmmdevCapture *self, ALCvoid *buffer, ALCuint samples);
|
||||
static ALuint ALCmmdevCapture_availableSamples(ALCmmdevCapture *self);
|
||||
static DECLARE_FORWARD(ALCmmdevCapture, ALCbackend, ALint64, getLatency)
|
||||
static DECLARE_FORWARD(ALCmmdevCapture, ALCbackend, void, lock)
|
||||
static DECLARE_FORWARD(ALCmmdevCapture, ALCbackend, void, unlock)
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALCmmdevCapture)
|
||||
|
||||
DEFINE_ALCMMDEVPROXY_VTABLE(ALCmmdevCapture);
|
||||
DEFINE_ALCBACKEND_VTABLE(ALCmmdevCapture);
|
||||
|
||||
|
||||
static void ALCmmdevCapture_Construct(ALCmmdevCapture *self, ALCdevice *device)
|
||||
{
|
||||
SET_VTABLE2(ALCmmdevCapture, ALCbackend, self);
|
||||
SET_VTABLE2(ALCmmdevCapture, ALCmmdevProxy, self);
|
||||
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
|
||||
ALCmmdevProxy_Construct(STATIC_CAST(ALCmmdevProxy, self));
|
||||
|
||||
self->devid = NULL;
|
||||
|
||||
self->mmdev = NULL;
|
||||
self->client = NULL;
|
||||
self->capture = NULL;
|
||||
self->NotifyEvent = NULL;
|
||||
|
||||
self->MsgEvent = NULL;
|
||||
|
||||
self->Ring = NULL;
|
||||
|
||||
self->killNow = 0;
|
||||
}
|
||||
|
||||
static void ALCmmdevCapture_Destruct(ALCmmdevCapture *self)
|
||||
{
|
||||
ll_ringbuffer_free(self->Ring);
|
||||
self->Ring = NULL;
|
||||
|
||||
if(self->NotifyEvent != NULL)
|
||||
CloseHandle(self->NotifyEvent);
|
||||
self->NotifyEvent = NULL;
|
||||
if(self->MsgEvent != NULL)
|
||||
CloseHandle(self->MsgEvent);
|
||||
self->MsgEvent = NULL;
|
||||
|
||||
free(self->devid);
|
||||
self->devid = NULL;
|
||||
|
||||
ALCmmdevProxy_Destruct(STATIC_CAST(ALCmmdevProxy, self));
|
||||
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
|
||||
}
|
||||
|
||||
|
||||
FORCE_ALIGN int ALCmmdevCapture_recordProc(void *arg)
|
||||
{
|
||||
ALCmmdevCapture *self = arg;
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
HRESULT hr;
|
||||
|
||||
hr = CoInitialize(NULL);
|
||||
if(FAILED(hr))
|
||||
{
|
||||
ERR("CoInitialize(NULL) failed: 0x%08lx\n", hr);
|
||||
V0(device->Backend,lock)();
|
||||
aluHandleDisconnect(device);
|
||||
V0(device->Backend,unlock)();
|
||||
return 1;
|
||||
}
|
||||
|
||||
althrd_setname(althrd_current(), RECORD_THREAD_NAME);
|
||||
|
||||
while(!self->killNow)
|
||||
{
|
||||
UINT32 avail;
|
||||
DWORD res;
|
||||
|
||||
hr = IAudioCaptureClient_GetNextPacketSize(self->capture, &avail);
|
||||
if(FAILED(hr))
|
||||
ERR("Failed to get next packet size: 0x%08lx\n", hr);
|
||||
else while(avail > 0 && SUCCEEDED(hr))
|
||||
{
|
||||
UINT32 numsamples;
|
||||
DWORD flags;
|
||||
BYTE *data;
|
||||
|
||||
hr = IAudioCaptureClient_GetBuffer(self->capture,
|
||||
&data, &numsamples, &flags, NULL, NULL
|
||||
);
|
||||
if(FAILED(hr))
|
||||
{
|
||||
ERR("Failed to get capture buffer: 0x%08lx\n", hr);
|
||||
break;
|
||||
}
|
||||
|
||||
ll_ringbuffer_write(self->Ring, (char*)data, numsamples);
|
||||
|
||||
hr = IAudioCaptureClient_ReleaseBuffer(self->capture, numsamples);
|
||||
if(FAILED(hr))
|
||||
{
|
||||
ERR("Failed to release capture buffer: 0x%08lx\n", hr);
|
||||
break;
|
||||
}
|
||||
|
||||
hr = IAudioCaptureClient_GetNextPacketSize(self->capture, &avail);
|
||||
if(FAILED(hr))
|
||||
ERR("Failed to get next packet size: 0x%08lx\n", hr);
|
||||
}
|
||||
|
||||
if(FAILED(hr))
|
||||
{
|
||||
V0(device->Backend,lock)();
|
||||
aluHandleDisconnect(device);
|
||||
V0(device->Backend,unlock)();
|
||||
break;
|
||||
}
|
||||
|
||||
res = WaitForSingleObjectEx(self->NotifyEvent, 2000, FALSE);
|
||||
if(res != WAIT_OBJECT_0)
|
||||
ERR("WaitForSingleObjectEx error: 0x%lx\n", res);
|
||||
}
|
||||
|
||||
CoUninitialize();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static ALCenum ALCmmdevCapture_open(ALCmmdevCapture *self, const ALCchar *deviceName)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
self->NotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
self->MsgEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
if(self->NotifyEvent == NULL || self->MsgEvent == NULL)
|
||||
{
|
||||
ERR("Failed to create message events: %lu\n", GetLastError());
|
||||
hr = E_FAIL;
|
||||
}
|
||||
|
||||
if(SUCCEEDED(hr))
|
||||
{
|
||||
if(deviceName)
|
||||
{
|
||||
const DevMap *iter;
|
||||
|
||||
if(VECTOR_SIZE(CaptureDevices) == 0)
|
||||
{
|
||||
ThreadRequest req = { self->MsgEvent, 0 };
|
||||
if(PostThreadMessage(ThreadID, WM_USER_Enumerate, (WPARAM)&req, CAPTURE_DEVICE_PROBE))
|
||||
(void)WaitForResponse(&req);
|
||||
}
|
||||
|
||||
hr = E_FAIL;
|
||||
#define MATCH_NAME(i) (al_string_cmp_cstr((i)->name, deviceName) == 0)
|
||||
VECTOR_FIND_IF(iter, const DevMap, CaptureDevices, MATCH_NAME);
|
||||
if(iter == VECTOR_ITER_END(CaptureDevices))
|
||||
WARN("Failed to find device name matching \"%s\"\n", deviceName);
|
||||
else
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
|
||||
self->devid = strdupW(iter->devid);
|
||||
al_string_copy(&device->DeviceName, iter->name);
|
||||
hr = S_OK;
|
||||
}
|
||||
#undef MATCH_NAME
|
||||
}
|
||||
}
|
||||
|
||||
if(SUCCEEDED(hr))
|
||||
{
|
||||
ThreadRequest req = { self->MsgEvent, 0 };
|
||||
|
||||
hr = E_FAIL;
|
||||
if(PostThreadMessage(ThreadID, WM_USER_OpenDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
|
||||
hr = WaitForResponse(&req);
|
||||
else
|
||||
ERR("Failed to post thread message: %lu\n", GetLastError());
|
||||
}
|
||||
|
||||
if(FAILED(hr))
|
||||
{
|
||||
if(self->NotifyEvent != NULL)
|
||||
CloseHandle(self->NotifyEvent);
|
||||
self->NotifyEvent = NULL;
|
||||
if(self->MsgEvent != NULL)
|
||||
CloseHandle(self->MsgEvent);
|
||||
self->MsgEvent = NULL;
|
||||
|
||||
free(self->devid);
|
||||
self->devid = NULL;
|
||||
|
||||
ERR("Device init failed: 0x%08lx\n", hr);
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
ThreadRequest req = { self->MsgEvent, 0 };
|
||||
|
||||
hr = E_FAIL;
|
||||
if(PostThreadMessage(ThreadID, WM_USER_ResetDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
|
||||
hr = WaitForResponse(&req);
|
||||
else
|
||||
ERR("Failed to post thread message: %lu\n", GetLastError());
|
||||
|
||||
if(FAILED(hr))
|
||||
{
|
||||
ALCmmdevCapture_close(self);
|
||||
if(hr == E_OUTOFMEMORY)
|
||||
return ALC_OUT_OF_MEMORY;
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static HRESULT ALCmmdevCapture_openProxy(ALCmmdevCapture *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
void *ptr;
|
||||
HRESULT hr;
|
||||
|
||||
hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
|
||||
if(SUCCEEDED(hr))
|
||||
{
|
||||
IMMDeviceEnumerator *Enumerator = ptr;
|
||||
if(!self->devid)
|
||||
hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(Enumerator, eCapture, eMultimedia, &self->mmdev);
|
||||
else
|
||||
hr = IMMDeviceEnumerator_GetDevice(Enumerator, self->devid, &self->mmdev);
|
||||
IMMDeviceEnumerator_Release(Enumerator);
|
||||
Enumerator = NULL;
|
||||
}
|
||||
if(SUCCEEDED(hr))
|
||||
hr = IMMDevice_Activate(self->mmdev, &IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, &ptr);
|
||||
if(SUCCEEDED(hr))
|
||||
{
|
||||
self->client = ptr;
|
||||
if(al_string_empty(device->DeviceName))
|
||||
{
|
||||
get_device_name(self->mmdev, &device->DeviceName);
|
||||
al_string_append_cstr(&device->DeviceName, DEVNAME_TAIL);
|
||||
}
|
||||
}
|
||||
|
||||
if(FAILED(hr))
|
||||
{
|
||||
if(self->mmdev)
|
||||
IMMDevice_Release(self->mmdev);
|
||||
self->mmdev = NULL;
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
|
||||
static void ALCmmdevCapture_close(ALCmmdevCapture *self)
|
||||
{
|
||||
ThreadRequest req = { self->MsgEvent, 0 };
|
||||
|
||||
if(PostThreadMessage(ThreadID, WM_USER_CloseDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
|
||||
(void)WaitForResponse(&req);
|
||||
|
||||
ll_ringbuffer_free(self->Ring);
|
||||
self->Ring = NULL;
|
||||
|
||||
CloseHandle(self->MsgEvent);
|
||||
self->MsgEvent = NULL;
|
||||
|
||||
CloseHandle(self->NotifyEvent);
|
||||
self->NotifyEvent = NULL;
|
||||
|
||||
free(self->devid);
|
||||
self->devid = NULL;
|
||||
}
|
||||
|
||||
static void ALCmmdevCapture_closeProxy(ALCmmdevCapture *self)
|
||||
{
|
||||
if(self->client)
|
||||
IAudioClient_Release(self->client);
|
||||
self->client = NULL;
|
||||
|
||||
if(self->mmdev)
|
||||
IMMDevice_Release(self->mmdev);
|
||||
self->mmdev = NULL;
|
||||
}
|
||||
|
||||
|
||||
static HRESULT ALCmmdevCapture_resetProxy(ALCmmdevCapture *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
WAVEFORMATEXTENSIBLE OutputType;
|
||||
WAVEFORMATEX *wfx = NULL;
|
||||
REFERENCE_TIME buf_time;
|
||||
UINT32 buffer_len;
|
||||
void *ptr = NULL;
|
||||
HRESULT hr;
|
||||
|
||||
if(self->client)
|
||||
IAudioClient_Release(self->client);
|
||||
self->client = NULL;
|
||||
|
||||
hr = IMMDevice_Activate(self->mmdev, &IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, &ptr);
|
||||
if(FAILED(hr))
|
||||
{
|
||||
ERR("Failed to reactivate audio client: 0x%08lx\n", hr);
|
||||
return hr;
|
||||
}
|
||||
self->client = ptr;
|
||||
|
||||
buf_time = ((REFERENCE_TIME)device->UpdateSize*device->NumUpdates*10000000 +
|
||||
device->Frequency-1) / device->Frequency;
|
||||
|
||||
OutputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
|
||||
switch(device->FmtChans)
|
||||
{
|
||||
case DevFmtMono:
|
||||
OutputType.Format.nChannels = 1;
|
||||
OutputType.dwChannelMask = MONO;
|
||||
break;
|
||||
case DevFmtStereo:
|
||||
OutputType.Format.nChannels = 2;
|
||||
OutputType.dwChannelMask = STEREO;
|
||||
break;
|
||||
case DevFmtQuad:
|
||||
OutputType.Format.nChannels = 4;
|
||||
OutputType.dwChannelMask = QUAD;
|
||||
break;
|
||||
case DevFmtX51:
|
||||
OutputType.Format.nChannels = 6;
|
||||
OutputType.dwChannelMask = X5DOT1;
|
||||
break;
|
||||
case DevFmtX51Rear:
|
||||
OutputType.Format.nChannels = 6;
|
||||
OutputType.dwChannelMask = X5DOT1REAR;
|
||||
break;
|
||||
case DevFmtX61:
|
||||
OutputType.Format.nChannels = 7;
|
||||
OutputType.dwChannelMask = X6DOT1;
|
||||
break;
|
||||
case DevFmtX71:
|
||||
OutputType.Format.nChannels = 8;
|
||||
OutputType.dwChannelMask = X7DOT1;
|
||||
break;
|
||||
|
||||
case DevFmtBFormat3D:
|
||||
return E_FAIL;
|
||||
}
|
||||
switch(device->FmtType)
|
||||
{
|
||||
case DevFmtUByte:
|
||||
OutputType.Format.wBitsPerSample = 8;
|
||||
OutputType.Samples.wValidBitsPerSample = 8;
|
||||
OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
|
||||
break;
|
||||
case DevFmtShort:
|
||||
OutputType.Format.wBitsPerSample = 16;
|
||||
OutputType.Samples.wValidBitsPerSample = 16;
|
||||
OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
|
||||
break;
|
||||
case DevFmtInt:
|
||||
OutputType.Format.wBitsPerSample = 32;
|
||||
OutputType.Samples.wValidBitsPerSample = 32;
|
||||
OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
|
||||
break;
|
||||
case DevFmtFloat:
|
||||
OutputType.Format.wBitsPerSample = 32;
|
||||
OutputType.Samples.wValidBitsPerSample = 32;
|
||||
OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
|
||||
break;
|
||||
|
||||
case DevFmtByte:
|
||||
case DevFmtUShort:
|
||||
case DevFmtUInt:
|
||||
WARN("%s capture samples not supported\n", DevFmtTypeString(device->FmtType));
|
||||
return E_FAIL;
|
||||
}
|
||||
OutputType.Format.nSamplesPerSec = device->Frequency;
|
||||
|
||||
OutputType.Format.nBlockAlign = OutputType.Format.nChannels *
|
||||
OutputType.Format.wBitsPerSample / 8;
|
||||
OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec *
|
||||
OutputType.Format.nBlockAlign;
|
||||
OutputType.Format.cbSize = sizeof(OutputType) - sizeof(OutputType.Format);
|
||||
|
||||
hr = IAudioClient_IsFormatSupported(self->client,
|
||||
AUDCLNT_SHAREMODE_SHARED, &OutputType.Format, &wfx
|
||||
);
|
||||
if(FAILED(hr))
|
||||
{
|
||||
ERR("Failed to check format support: 0x%08lx\n", hr);
|
||||
return hr;
|
||||
}
|
||||
|
||||
/* FIXME: We should do conversion/resampling if we didn't get a matching format. */
|
||||
if(wfx->nSamplesPerSec != OutputType.Format.nSamplesPerSec ||
|
||||
wfx->wBitsPerSample != OutputType.Format.wBitsPerSample ||
|
||||
wfx->nChannels != OutputType.Format.nChannels ||
|
||||
wfx->nBlockAlign != OutputType.Format.nBlockAlign)
|
||||
{
|
||||
ERR("Did not get matching format, wanted: %s %s %uhz, got: %d channel(s) %d-bit %luhz\n",
|
||||
DevFmtChannelsString(device->FmtChans), DevFmtTypeString(device->FmtType), device->Frequency,
|
||||
wfx->nChannels, wfx->wBitsPerSample, wfx->nSamplesPerSec);
|
||||
CoTaskMemFree(wfx);
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
if(!MakeExtensible(&OutputType, wfx))
|
||||
{
|
||||
CoTaskMemFree(wfx);
|
||||
return E_FAIL;
|
||||
}
|
||||
CoTaskMemFree(wfx);
|
||||
wfx = NULL;
|
||||
|
||||
hr = IAudioClient_Initialize(self->client,
|
||||
AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
|
||||
buf_time, 0, &OutputType.Format, NULL
|
||||
);
|
||||
if(FAILED(hr))
|
||||
{
|
||||
ERR("Failed to initialize audio client: 0x%08lx\n", hr);
|
||||
return hr;
|
||||
}
|
||||
|
||||
hr = IAudioClient_GetBufferSize(self->client, &buffer_len);
|
||||
if(FAILED(hr))
|
||||
{
|
||||
ERR("Failed to get buffer size: 0x%08lx\n", hr);
|
||||
return hr;
|
||||
}
|
||||
|
||||
buffer_len = maxu(device->UpdateSize*device->NumUpdates + 1, buffer_len);
|
||||
ll_ringbuffer_free(self->Ring);
|
||||
self->Ring = ll_ringbuffer_create(buffer_len, OutputType.Format.nBlockAlign);
|
||||
if(!self->Ring)
|
||||
{
|
||||
ERR("Failed to allocate capture ring buffer\n");
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
|
||||
hr = IAudioClient_SetEventHandle(self->client, self->NotifyEvent);
|
||||
if(FAILED(hr))
|
||||
{
|
||||
ERR("Failed to set event handle: 0x%08lx\n", hr);
|
||||
return hr;
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
|
||||
static ALCboolean ALCmmdevCapture_start(ALCmmdevCapture *self)
|
||||
{
|
||||
ThreadRequest req = { self->MsgEvent, 0 };
|
||||
HRESULT hr = E_FAIL;
|
||||
|
||||
if(PostThreadMessage(ThreadID, WM_USER_StartDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
|
||||
hr = WaitForResponse(&req);
|
||||
|
||||
return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE;
|
||||
}
|
||||
|
||||
static HRESULT ALCmmdevCapture_startProxy(ALCmmdevCapture *self)
|
||||
{
|
||||
HRESULT hr;
|
||||
void *ptr;
|
||||
|
||||
ResetEvent(self->NotifyEvent);
|
||||
hr = IAudioClient_Start(self->client);
|
||||
if(FAILED(hr))
|
||||
{
|
||||
ERR("Failed to start audio client: 0x%08lx\n", hr);
|
||||
return hr;
|
||||
}
|
||||
|
||||
hr = IAudioClient_GetService(self->client, &IID_IAudioCaptureClient, &ptr);
|
||||
if(SUCCEEDED(hr))
|
||||
{
|
||||
self->capture = ptr;
|
||||
self->killNow = 0;
|
||||
if(althrd_create(&self->thread, ALCmmdevCapture_recordProc, self) != althrd_success)
|
||||
{
|
||||
ERR("Failed to start thread\n");
|
||||
IAudioCaptureClient_Release(self->capture);
|
||||
self->capture = NULL;
|
||||
hr = E_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
if(FAILED(hr))
|
||||
{
|
||||
IAudioClient_Stop(self->client);
|
||||
IAudioClient_Reset(self->client);
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
|
||||
static void ALCmmdevCapture_stop(ALCmmdevCapture *self)
|
||||
{
|
||||
ThreadRequest req = { self->MsgEvent, 0 };
|
||||
if(PostThreadMessage(ThreadID, WM_USER_StopDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
|
||||
(void)WaitForResponse(&req);
|
||||
}
|
||||
|
||||
static void ALCmmdevCapture_stopProxy(ALCmmdevCapture *self)
|
||||
{
|
||||
int res;
|
||||
|
||||
if(!self->capture)
|
||||
return;
|
||||
|
||||
self->killNow = 1;
|
||||
althrd_join(self->thread, &res);
|
||||
|
||||
IAudioCaptureClient_Release(self->capture);
|
||||
self->capture = NULL;
|
||||
IAudioClient_Stop(self->client);
|
||||
IAudioClient_Reset(self->client);
|
||||
}
|
||||
|
||||
|
||||
ALuint ALCmmdevCapture_availableSamples(ALCmmdevCapture *self)
|
||||
{
|
||||
return (ALuint)ll_ringbuffer_read_space(self->Ring);
|
||||
}
|
||||
|
||||
ALCenum ALCmmdevCapture_captureSamples(ALCmmdevCapture *self, ALCvoid *buffer, ALCuint samples)
|
||||
{
|
||||
if(ALCmmdevCapture_availableSamples(self) < samples)
|
||||
return ALC_INVALID_VALUE;
|
||||
ll_ringbuffer_read(self->Ring, buffer, samples);
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
|
||||
static inline void AppendAllDevicesList2(const DevMap *entry)
|
||||
{ AppendAllDevicesList(al_string_get_cstr(entry->name)); }
|
||||
static inline void AppendCaptureDeviceList2(const DevMap *entry)
|
||||
@@ -1125,7 +1781,12 @@ static void ALCmmdevBackendFactory_deinit(ALCmmdevBackendFactory* UNUSED(self))
|
||||
|
||||
static ALCboolean ALCmmdevBackendFactory_querySupport(ALCmmdevBackendFactory* UNUSED(self), ALCbackend_Type type)
|
||||
{
|
||||
if(type == ALCbackend_Playback)
|
||||
/* TODO: Disable capture with mmdevapi for now, since it doesn't do any
|
||||
* rechanneling or resampling; if the device is configured for 48000hz
|
||||
* stereo input, for example, and the app asks for 22050hz mono,
|
||||
* initialization will fail.
|
||||
*/
|
||||
if(type == ALCbackend_Playback /*|| type == ALCbackend_Capture*/)
|
||||
return ALC_TRUE;
|
||||
return ALC_FALSE;
|
||||
}
|
||||
@@ -1162,13 +1823,15 @@ static ALCbackend* ALCmmdevBackendFactory_createBackend(ALCmmdevBackendFactory*
|
||||
if(type == ALCbackend_Playback)
|
||||
{
|
||||
ALCmmdevPlayback *backend;
|
||||
|
||||
backend = ALCmmdevPlayback_New(sizeof(*backend));
|
||||
NEW_OBJ(backend, ALCmmdevPlayback)(device);
|
||||
if(!backend) return NULL;
|
||||
return STATIC_CAST(ALCbackend, backend);
|
||||
}
|
||||
if(type == ALCbackend_Capture)
|
||||
{
|
||||
ALCmmdevCapture *backend;
|
||||
NEW_OBJ(backend, ALCmmdevCapture)(device);
|
||||
if(!backend) return NULL;
|
||||
memset(backend, 0, sizeof(*backend));
|
||||
|
||||
ALCmmdevPlayback_Construct(backend, device);
|
||||
|
||||
return STATIC_CAST(ALCbackend, backend);
|
||||
}
|
||||
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
*
|
||||
* 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., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
@@ -106,7 +106,7 @@ static int ALCnullBackend_mixerProc(void *ptr)
|
||||
}
|
||||
|
||||
if(avail-done < device->UpdateSize)
|
||||
al_nssleep(0, restTime);
|
||||
al_nssleep(restTime);
|
||||
else while(avail-done >= device->UpdateSize)
|
||||
{
|
||||
aluMixData(device, NULL, device->UpdateSize);
|
||||
@@ -214,13 +214,8 @@ static ALCbackend* ALCnullBackendFactory_createBackend(ALCnullBackendFactory* UN
|
||||
if(type == ALCbackend_Playback)
|
||||
{
|
||||
ALCnullBackend *backend;
|
||||
|
||||
backend = ALCnullBackend_New(sizeof(*backend));
|
||||
NEW_OBJ(backend, ALCnullBackend)(device);
|
||||
if(!backend) return NULL;
|
||||
memset(backend, 0, sizeof(*backend));
|
||||
|
||||
ALCnullBackend_Construct(backend, device);
|
||||
|
||||
return STATIC_CAST(ALCbackend, backend);
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alu.h"
|
||||
|
||||
#include "threads.h"
|
||||
|
||||
#include <SLES/OpenSLES.h>
|
||||
#include <SLES/OpenSLES_Android.h>
|
||||
@@ -67,7 +67,10 @@ static SLuint32 GetChannelMask(enum DevFmtChannels chans)
|
||||
SL_SPEAKER_BACK_LEFT|SL_SPEAKER_BACK_RIGHT;
|
||||
case DevFmtX51: return SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT|
|
||||
SL_SPEAKER_FRONT_CENTER|SL_SPEAKER_LOW_FREQUENCY|
|
||||
SL_SPEAKER_BACK_LEFT|SL_SPEAKER_BACK_RIGHT;
|
||||
SL_SPEAKER_SIDE_LEFT|SL_SPEAKER_SIDE_RIGHT;
|
||||
case DevFmtX51Rear: return SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT|
|
||||
SL_SPEAKER_FRONT_CENTER|SL_SPEAKER_LOW_FREQUENCY|
|
||||
SL_SPEAKER_BACK_LEFT|SL_SPEAKER_BACK_RIGHT;
|
||||
case DevFmtX61: return SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT|
|
||||
SL_SPEAKER_FRONT_CENTER|SL_SPEAKER_LOW_FREQUENCY|
|
||||
SL_SPEAKER_BACK_CENTER|
|
||||
@@ -76,9 +79,7 @@ static SLuint32 GetChannelMask(enum DevFmtChannels chans)
|
||||
SL_SPEAKER_FRONT_CENTER|SL_SPEAKER_LOW_FREQUENCY|
|
||||
SL_SPEAKER_BACK_LEFT|SL_SPEAKER_BACK_RIGHT|
|
||||
SL_SPEAKER_SIDE_LEFT|SL_SPEAKER_SIDE_RIGHT;
|
||||
case DevFmtX51Side: return SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT|
|
||||
SL_SPEAKER_FRONT_CENTER|SL_SPEAKER_LOW_FREQUENCY|
|
||||
SL_SPEAKER_SIDE_LEFT|SL_SPEAKER_SIDE_RIGHT;
|
||||
case DevFmtBFormat3D: break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -378,6 +379,15 @@ static void opensl_stop_playback(ALCdevice *Device)
|
||||
result = VCALL0(bufferQueue,Clear)();
|
||||
PRINTERR(result, "bufferQueue->Clear");
|
||||
}
|
||||
if(SL_RESULT_SUCCESS == result)
|
||||
{
|
||||
SLAndroidSimpleBufferQueueState state;
|
||||
do {
|
||||
althrd_yield();
|
||||
result = VCALL(bufferQueue,GetState)(&state);
|
||||
} while(SL_RESULT_SUCCESS == result && state.count > 0);
|
||||
PRINTERR(result, "bufferQueue->GetState");
|
||||
}
|
||||
|
||||
free(data->buffer);
|
||||
data->buffer = NULL;
|
||||
@@ -396,8 +406,7 @@ static const BackendFuncs opensl_funcs = {
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
ALCdevice_GetLatencyDefault
|
||||
NULL
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
*
|
||||
* 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., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
@@ -131,7 +131,7 @@ static int ALCplaybackOSS_mixerProc(void *ptr)
|
||||
break;
|
||||
}
|
||||
|
||||
al_nssleep(0, 1000000);
|
||||
al_nssleep(1000000);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -345,7 +345,7 @@ static int ALCcaptureOSS_recordProc(void *ptr)
|
||||
int amt;
|
||||
|
||||
SetRTPriority();
|
||||
althrd_setname(althrd_current(), "alsoft-record");
|
||||
althrd_setname(althrd_current(), RECORD_THREAD_NAME);
|
||||
|
||||
frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
|
||||
|
||||
@@ -362,7 +362,7 @@ static int ALCcaptureOSS_recordProc(void *ptr)
|
||||
}
|
||||
if(amt == 0)
|
||||
{
|
||||
al_nssleep(0, 1000000);
|
||||
al_nssleep(1000000);
|
||||
continue;
|
||||
}
|
||||
if(self->doCapture)
|
||||
@@ -562,8 +562,8 @@ ALCbackendFactory *ALCossBackendFactory_getFactory(void)
|
||||
|
||||
ALCboolean ALCossBackendFactory_init(ALCossBackendFactory* UNUSED(self))
|
||||
{
|
||||
ConfigValueStr("oss", "device", &oss_driver);
|
||||
ConfigValueStr("oss", "capture", &oss_capture);
|
||||
ConfigValueStr(NULL, "oss", "device", &oss_driver);
|
||||
ConfigValueStr(NULL, "oss", "capture", &oss_capture);
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
@@ -606,25 +606,15 @@ ALCbackend* ALCossBackendFactory_createBackend(ALCossBackendFactory* UNUSED(self
|
||||
if(type == ALCbackend_Playback)
|
||||
{
|
||||
ALCplaybackOSS *backend;
|
||||
|
||||
backend = ALCplaybackOSS_New(sizeof(*backend));
|
||||
NEW_OBJ(backend, ALCplaybackOSS)(device);
|
||||
if(!backend) return NULL;
|
||||
memset(backend, 0, sizeof(*backend));
|
||||
|
||||
ALCplaybackOSS_Construct(backend, device);
|
||||
|
||||
return STATIC_CAST(ALCbackend, backend);
|
||||
}
|
||||
if(type == ALCbackend_Capture)
|
||||
{
|
||||
ALCcaptureOSS *backend;
|
||||
|
||||
backend = ALCcaptureOSS_New(sizeof(*backend));
|
||||
NEW_OBJ(backend, ALCcaptureOSS)(device);
|
||||
if(!backend) return NULL;
|
||||
memset(backend, 0, sizeof(*backend));
|
||||
|
||||
ALCcaptureOSS_Construct(backend, device);
|
||||
|
||||
return STATIC_CAST(ALCbackend, backend);
|
||||
}
|
||||
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
*
|
||||
* 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., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
#include "alu.h"
|
||||
#include "compat.h"
|
||||
|
||||
#include "backends/base.h"
|
||||
|
||||
#include <portaudio.h>
|
||||
|
||||
|
||||
@@ -122,149 +124,171 @@ static ALCboolean pa_load(void)
|
||||
}
|
||||
|
||||
|
||||
typedef struct {
|
||||
typedef struct ALCportPlayback {
|
||||
DERIVE_FROM_TYPE(ALCbackend);
|
||||
|
||||
PaStream *stream;
|
||||
PaStreamParameters params;
|
||||
ALuint update_size;
|
||||
} ALCportPlayback;
|
||||
|
||||
RingBuffer *ring;
|
||||
} pa_data;
|
||||
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, ALint64, getLatency)
|
||||
static DECLARE_FORWARD(ALCportPlayback, ALCbackend, void, lock)
|
||||
static DECLARE_FORWARD(ALCportPlayback, ALCbackend, void, unlock)
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALCportPlayback)
|
||||
|
||||
DEFINE_ALCBACKEND_VTABLE(ALCportPlayback);
|
||||
|
||||
|
||||
static int pa_callback(const void *UNUSED(inputBuffer), void *outputBuffer,
|
||||
unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *UNUSED(timeInfo),
|
||||
const PaStreamCallbackFlags UNUSED(statusFlags), void *userData)
|
||||
static void ALCportPlayback_Construct(ALCportPlayback *self, ALCdevice *device)
|
||||
{
|
||||
ALCdevice *device = (ALCdevice*)userData;
|
||||
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
|
||||
SET_VTABLE2(ALCportPlayback, ALCbackend, self);
|
||||
|
||||
aluMixData(device, outputBuffer, framesPerBuffer);
|
||||
return 0;
|
||||
self->stream = NULL;
|
||||
}
|
||||
|
||||
static int pa_capture_cb(const void *inputBuffer, void *UNUSED(outputBuffer),
|
||||
unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *UNUSED(timeInfo),
|
||||
const PaStreamCallbackFlags UNUSED(statusFlags), void *userData)
|
||||
static void ALCportPlayback_Destruct(ALCportPlayback *self)
|
||||
{
|
||||
ALCdevice *device = (ALCdevice*)userData;
|
||||
pa_data *data = (pa_data*)device->ExtraData;
|
||||
if(self->stream)
|
||||
Pa_CloseStream(self->stream);
|
||||
self->stream = NULL;
|
||||
|
||||
WriteRingBuffer(data->ring, inputBuffer, framesPerBuffer);
|
||||
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;
|
||||
|
||||
aluMixData(STATIC_CAST(ALCbackend, self)->mDevice, outputBuffer, framesPerBuffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static ALCenum pa_open_playback(ALCdevice *device, const ALCchar *deviceName)
|
||||
static ALCenum ALCportPlayback_open(ALCportPlayback *self, const ALCchar *name)
|
||||
{
|
||||
pa_data *data;
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
PaError err;
|
||||
|
||||
if(!deviceName)
|
||||
deviceName = pa_device;
|
||||
else if(strcmp(deviceName, pa_device) != 0)
|
||||
if(!name)
|
||||
name = pa_device;
|
||||
else if(strcmp(name, pa_device) != 0)
|
||||
return ALC_INVALID_VALUE;
|
||||
|
||||
data = (pa_data*)calloc(1, sizeof(pa_data));
|
||||
data->update_size = device->UpdateSize;
|
||||
self->update_size = device->UpdateSize;
|
||||
|
||||
data->params.device = -1;
|
||||
if(!ConfigValueInt("port", "device", &data->params.device) ||
|
||||
data->params.device < 0)
|
||||
data->params.device = Pa_GetDefaultOutputDevice();
|
||||
data->params.suggestedLatency = (device->UpdateSize*device->NumUpdates) /
|
||||
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;
|
||||
data->params.hostApiSpecificStreamInfo = NULL;
|
||||
self->params.hostApiSpecificStreamInfo = NULL;
|
||||
|
||||
data->params.channelCount = ((device->FmtChans == DevFmtMono) ? 1 : 2);
|
||||
self->params.channelCount = ((device->FmtChans == DevFmtMono) ? 1 : 2);
|
||||
|
||||
switch(device->FmtType)
|
||||
{
|
||||
case DevFmtByte:
|
||||
data->params.sampleFormat = paInt8;
|
||||
self->params.sampleFormat = paInt8;
|
||||
break;
|
||||
case DevFmtUByte:
|
||||
data->params.sampleFormat = paUInt8;
|
||||
self->params.sampleFormat = paUInt8;
|
||||
break;
|
||||
case DevFmtUShort:
|
||||
/* fall-through */
|
||||
case DevFmtShort:
|
||||
data->params.sampleFormat = paInt16;
|
||||
self->params.sampleFormat = paInt16;
|
||||
break;
|
||||
case DevFmtUInt:
|
||||
/* fall-through */
|
||||
case DevFmtInt:
|
||||
data->params.sampleFormat = paInt32;
|
||||
self->params.sampleFormat = paInt32;
|
||||
break;
|
||||
case DevFmtFloat:
|
||||
data->params.sampleFormat = paFloat32;
|
||||
self->params.sampleFormat = paFloat32;
|
||||
break;
|
||||
}
|
||||
|
||||
retry_open:
|
||||
err = Pa_OpenStream(&data->stream, NULL, &data->params, device->Frequency,
|
||||
device->UpdateSize, paNoFlag, pa_callback, device);
|
||||
err = Pa_OpenStream(&self->stream, NULL, &self->params,
|
||||
device->Frequency, device->UpdateSize, paNoFlag,
|
||||
ALCportPlayback_WriteCallback, self
|
||||
);
|
||||
if(err != paNoError)
|
||||
{
|
||||
if(data->params.sampleFormat == paFloat32)
|
||||
if(self->params.sampleFormat == paFloat32)
|
||||
{
|
||||
data->params.sampleFormat = paInt16;
|
||||
self->params.sampleFormat = paInt16;
|
||||
goto retry_open;
|
||||
}
|
||||
ERR("Pa_OpenStream() returned an error: %s\n", Pa_GetErrorText(err));
|
||||
free(data);
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
device->ExtraData = data;
|
||||
al_string_copy_cstr(&device->DeviceName, deviceName);
|
||||
al_string_copy_cstr(&device->DeviceName, name);
|
||||
|
||||
return ALC_NO_ERROR;
|
||||
|
||||
}
|
||||
|
||||
static void pa_close_playback(ALCdevice *device)
|
||||
static void ALCportPlayback_close(ALCportPlayback *self)
|
||||
{
|
||||
pa_data *data = (pa_data*)device->ExtraData;
|
||||
PaError err;
|
||||
|
||||
err = Pa_CloseStream(data->stream);
|
||||
PaError err = Pa_CloseStream(self->stream);
|
||||
if(err != paNoError)
|
||||
ERR("Error closing stream: %s\n", Pa_GetErrorText(err));
|
||||
|
||||
free(data);
|
||||
device->ExtraData = NULL;
|
||||
self->stream = NULL;
|
||||
}
|
||||
|
||||
static ALCboolean pa_reset_playback(ALCdevice *device)
|
||||
static ALCboolean ALCportPlayback_reset(ALCportPlayback *self)
|
||||
{
|
||||
pa_data *data = (pa_data*)device->ExtraData;
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
const PaStreamInfo *streamInfo;
|
||||
|
||||
streamInfo = Pa_GetStreamInfo(data->stream);
|
||||
streamInfo = Pa_GetStreamInfo(self->stream);
|
||||
device->Frequency = streamInfo->sampleRate;
|
||||
device->UpdateSize = data->update_size;
|
||||
device->UpdateSize = self->update_size;
|
||||
|
||||
if(data->params.sampleFormat == paInt8)
|
||||
if(self->params.sampleFormat == paInt8)
|
||||
device->FmtType = DevFmtByte;
|
||||
else if(data->params.sampleFormat == paUInt8)
|
||||
else if(self->params.sampleFormat == paUInt8)
|
||||
device->FmtType = DevFmtUByte;
|
||||
else if(data->params.sampleFormat == paInt16)
|
||||
else if(self->params.sampleFormat == paInt16)
|
||||
device->FmtType = DevFmtShort;
|
||||
else if(data->params.sampleFormat == paInt32)
|
||||
else if(self->params.sampleFormat == paInt32)
|
||||
device->FmtType = DevFmtInt;
|
||||
else if(data->params.sampleFormat == paFloat32)
|
||||
else if(self->params.sampleFormat == paFloat32)
|
||||
device->FmtType = DevFmtFloat;
|
||||
else
|
||||
{
|
||||
ERR("Unexpected sample format: 0x%lx\n", data->params.sampleFormat);
|
||||
ERR("Unexpected sample format: 0x%lx\n", self->params.sampleFormat);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
if(data->params.channelCount == 2)
|
||||
if(self->params.channelCount == 2)
|
||||
device->FmtChans = DevFmtStereo;
|
||||
else if(data->params.channelCount == 1)
|
||||
else if(self->params.channelCount == 1)
|
||||
device->FmtChans = DevFmtMono;
|
||||
else
|
||||
{
|
||||
ERR("Unexpected channel count: %u\n", data->params.channelCount);
|
||||
ERR("Unexpected channel count: %u\n", self->params.channelCount);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
SetDefaultChannelOrder(device);
|
||||
@@ -272,12 +296,11 @@ static ALCboolean pa_reset_playback(ALCdevice *device)
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static ALCboolean pa_start_playback(ALCdevice *device)
|
||||
static ALCboolean ALCportPlayback_start(ALCportPlayback *self)
|
||||
{
|
||||
pa_data *data = (pa_data*)device->ExtraData;
|
||||
PaError err;
|
||||
|
||||
err = Pa_StartStream(data->stream);
|
||||
err = Pa_StartStream(self->stream);
|
||||
if(err != paNoError)
|
||||
{
|
||||
ERR("Pa_StartStream() returned an error: %s\n", Pa_GetErrorText(err));
|
||||
@@ -287,161 +310,209 @@ static ALCboolean pa_start_playback(ALCdevice *device)
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static void pa_stop_playback(ALCdevice *device)
|
||||
static void ALCportPlayback_stop(ALCportPlayback *self)
|
||||
{
|
||||
pa_data *data = (pa_data*)device->ExtraData;
|
||||
PaError err;
|
||||
|
||||
err = Pa_StopStream(data->stream);
|
||||
PaError err = Pa_StopStream(self->stream);
|
||||
if(err != paNoError)
|
||||
ERR("Error stopping stream: %s\n", Pa_GetErrorText(err));
|
||||
}
|
||||
|
||||
|
||||
static ALCenum pa_open_capture(ALCdevice *device, const ALCchar *deviceName)
|
||||
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, ALint64, getLatency)
|
||||
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)
|
||||
{
|
||||
ALuint frame_size;
|
||||
pa_data *data;
|
||||
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(!deviceName)
|
||||
deviceName = pa_device;
|
||||
else if(strcmp(deviceName, pa_device) != 0)
|
||||
if(!name)
|
||||
name = pa_device;
|
||||
else if(strcmp(name, pa_device) != 0)
|
||||
return ALC_INVALID_VALUE;
|
||||
|
||||
data = (pa_data*)calloc(1, sizeof(pa_data));
|
||||
if(data == NULL)
|
||||
return ALC_OUT_OF_MEMORY;
|
||||
|
||||
samples = device->UpdateSize * device->NumUpdates;
|
||||
samples = maxu(samples, 100 * device->Frequency / 1000);
|
||||
frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
|
||||
data->ring = CreateRingBuffer(frame_size, device->UpdateSize*device->NumUpdates);
|
||||
if(data->ring == NULL)
|
||||
goto error;
|
||||
|
||||
data->params.device = -1;
|
||||
if(!ConfigValueInt("port", "capture", &data->params.device) ||
|
||||
data->params.device < 0)
|
||||
data->params.device = Pa_GetDefaultInputDevice();
|
||||
data->params.suggestedLatency = 0.0f;
|
||||
data->params.hostApiSpecificStreamInfo = NULL;
|
||||
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:
|
||||
data->params.sampleFormat = paInt8;
|
||||
self->params.sampleFormat = paInt8;
|
||||
break;
|
||||
case DevFmtUByte:
|
||||
data->params.sampleFormat = paUInt8;
|
||||
self->params.sampleFormat = paUInt8;
|
||||
break;
|
||||
case DevFmtShort:
|
||||
data->params.sampleFormat = paInt16;
|
||||
self->params.sampleFormat = paInt16;
|
||||
break;
|
||||
case DevFmtInt:
|
||||
data->params.sampleFormat = paInt32;
|
||||
self->params.sampleFormat = paInt32;
|
||||
break;
|
||||
case DevFmtFloat:
|
||||
data->params.sampleFormat = paFloat32;
|
||||
self->params.sampleFormat = paFloat32;
|
||||
break;
|
||||
case DevFmtUInt:
|
||||
case DevFmtUShort:
|
||||
ERR("%s samples not supported\n", DevFmtTypeString(device->FmtType));
|
||||
goto error;
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
data->params.channelCount = ChannelsFromDevFmt(device->FmtChans);
|
||||
self->params.channelCount = ChannelsFromDevFmt(device->FmtChans);
|
||||
|
||||
err = Pa_OpenStream(&data->stream, &data->params, NULL, device->Frequency,
|
||||
paFramesPerBufferUnspecified, paNoFlag, pa_capture_cb, device);
|
||||
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));
|
||||
goto error;
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
al_string_copy_cstr(&device->DeviceName, deviceName);
|
||||
al_string_copy_cstr(&device->DeviceName, name);
|
||||
|
||||
device->ExtraData = data;
|
||||
return ALC_NO_ERROR;
|
||||
|
||||
error:
|
||||
DestroyRingBuffer(data->ring);
|
||||
free(data);
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
static void pa_close_capture(ALCdevice *device)
|
||||
static void ALCportCapture_close(ALCportCapture *self)
|
||||
{
|
||||
pa_data *data = (pa_data*)device->ExtraData;
|
||||
PaError err;
|
||||
|
||||
err = Pa_CloseStream(data->stream);
|
||||
PaError err = Pa_CloseStream(self->stream);
|
||||
if(err != paNoError)
|
||||
ERR("Error closing stream: %s\n", Pa_GetErrorText(err));
|
||||
self->stream = NULL;
|
||||
|
||||
DestroyRingBuffer(data->ring);
|
||||
data->ring = NULL;
|
||||
|
||||
free(data);
|
||||
device->ExtraData = NULL;
|
||||
ll_ringbuffer_free(self->ring);
|
||||
self->ring = NULL;
|
||||
}
|
||||
|
||||
static void pa_start_capture(ALCdevice *device)
|
||||
{
|
||||
pa_data *data = device->ExtraData;
|
||||
PaError err;
|
||||
|
||||
err = Pa_StartStream(data->stream);
|
||||
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 pa_stop_capture(ALCdevice *device)
|
||||
static void ALCportCapture_stop(ALCportCapture *self)
|
||||
{
|
||||
pa_data *data = (pa_data*)device->ExtraData;
|
||||
PaError err;
|
||||
|
||||
err = Pa_StopStream(data->stream);
|
||||
PaError err = Pa_StopStream(self->stream);
|
||||
if(err != paNoError)
|
||||
ERR("Error stopping stream: %s\n", Pa_GetErrorText(err));
|
||||
}
|
||||
|
||||
static ALCenum pa_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint samples)
|
||||
|
||||
static ALCuint ALCportCapture_availableSamples(ALCportCapture *self)
|
||||
{
|
||||
pa_data *data = device->ExtraData;
|
||||
ReadRingBuffer(data->ring, buffer, samples);
|
||||
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;
|
||||
}
|
||||
|
||||
static ALCuint pa_available_samples(ALCdevice *device)
|
||||
{
|
||||
pa_data *data = device->ExtraData;
|
||||
return RingBufferSize(data->ring);
|
||||
}
|
||||
|
||||
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 const BackendFuncs pa_funcs = {
|
||||
pa_open_playback,
|
||||
pa_close_playback,
|
||||
pa_reset_playback,
|
||||
pa_start_playback,
|
||||
pa_stop_playback,
|
||||
pa_open_capture,
|
||||
pa_close_capture,
|
||||
pa_start_capture,
|
||||
pa_stop_capture,
|
||||
pa_capture_samples,
|
||||
pa_available_samples,
|
||||
ALCdevice_GetLatencyDefault
|
||||
};
|
||||
|
||||
ALCboolean alc_pa_init(BackendFuncs *func_list)
|
||||
static ALCboolean ALCportBackendFactory_init(ALCportBackendFactory* UNUSED(self))
|
||||
{
|
||||
if(!pa_load())
|
||||
return ALC_FALSE;
|
||||
*func_list = pa_funcs;
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
void alc_pa_deinit(void)
|
||||
static void ALCportBackendFactory_deinit(ALCportBackendFactory* UNUSED(self))
|
||||
{
|
||||
#ifdef HAVE_DYNLOAD
|
||||
if(pa_handle)
|
||||
@@ -455,7 +526,14 @@ void alc_pa_deinit(void)
|
||||
#endif
|
||||
}
|
||||
|
||||
void alc_pa_probe(enum DevProbe type)
|
||||
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)
|
||||
{
|
||||
@@ -467,3 +545,29 @@ void alc_pa_probe(enum DevProbe type)
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
*
|
||||
* 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., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
@@ -34,13 +34,6 @@
|
||||
|
||||
#if PA_API_VERSION == 12
|
||||
|
||||
#ifndef PA_CHECK_VERSION
|
||||
#define PA_CHECK_VERSION(major,minor,micro) \
|
||||
((PA_MAJOR > (major)) || \
|
||||
(PA_MAJOR == (major) && PA_MINOR > (minor)) || \
|
||||
(PA_MAJOR == (major) && PA_MINOR == (minor) && PA_MICRO >= (micro)))
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_DYNLOAD
|
||||
static void *pa_handle;
|
||||
#define MAKE_FUNC(x) static __typeof(x) * p##x
|
||||
@@ -108,13 +101,9 @@ MAKE_FUNC(pa_operation_unref);
|
||||
MAKE_FUNC(pa_proplist_new);
|
||||
MAKE_FUNC(pa_proplist_free);
|
||||
MAKE_FUNC(pa_proplist_set);
|
||||
#if PA_CHECK_VERSION(0,9,15)
|
||||
MAKE_FUNC(pa_channel_map_superset);
|
||||
MAKE_FUNC(pa_stream_set_buffer_attr_callback);
|
||||
#endif
|
||||
#if PA_CHECK_VERSION(0,9,16)
|
||||
MAKE_FUNC(pa_stream_begin_write);
|
||||
#endif
|
||||
#undef MAKE_FUNC
|
||||
|
||||
#define pa_context_unref ppa_context_unref
|
||||
@@ -181,13 +170,9 @@ MAKE_FUNC(pa_stream_begin_write);
|
||||
#define pa_proplist_new ppa_proplist_new
|
||||
#define pa_proplist_free ppa_proplist_free
|
||||
#define pa_proplist_set ppa_proplist_set
|
||||
#if PA_CHECK_VERSION(0,9,15)
|
||||
#define pa_channel_map_superset ppa_channel_map_superset
|
||||
#define pa_stream_set_buffer_attr_callback ppa_stream_set_buffer_attr_callback
|
||||
#endif
|
||||
#if PA_CHECK_VERSION(0,9,16)
|
||||
#define pa_stream_begin_write ppa_stream_begin_write
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -278,18 +263,10 @@ static ALCboolean pulse_load(void)
|
||||
LOAD_FUNC(pa_proplist_new);
|
||||
LOAD_FUNC(pa_proplist_free);
|
||||
LOAD_FUNC(pa_proplist_set);
|
||||
LOAD_FUNC(pa_channel_map_superset);
|
||||
LOAD_FUNC(pa_stream_set_buffer_attr_callback);
|
||||
LOAD_FUNC(pa_stream_begin_write);
|
||||
#undef LOAD_FUNC
|
||||
#define LOAD_OPTIONAL_FUNC(x) do { \
|
||||
p##x = GetSymbol(pa_handle, #x); \
|
||||
} while(0)
|
||||
#if PA_CHECK_VERSION(0,9,15)
|
||||
LOAD_OPTIONAL_FUNC(pa_channel_map_superset);
|
||||
LOAD_OPTIONAL_FUNC(pa_stream_set_buffer_attr_callback);
|
||||
#endif
|
||||
#if PA_CHECK_VERSION(0,9,16)
|
||||
LOAD_OPTIONAL_FUNC(pa_stream_begin_write);
|
||||
#endif
|
||||
#undef LOAD_OPTIONAL_FUNC
|
||||
|
||||
if(ret == ALC_FALSE)
|
||||
{
|
||||
@@ -428,8 +405,7 @@ error:
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
static void pulse_close(pa_threaded_mainloop *loop, pa_context *context,
|
||||
pa_stream *stream)
|
||||
static void pulse_close(pa_threaded_mainloop *loop, pa_context *context, pa_stream *stream)
|
||||
{
|
||||
pa_threaded_mainloop_lock(loop);
|
||||
|
||||
@@ -438,10 +414,7 @@ static void pulse_close(pa_threaded_mainloop *loop, pa_context *context,
|
||||
pa_stream_set_state_callback(stream, NULL, NULL);
|
||||
pa_stream_set_moved_callback(stream, NULL, NULL);
|
||||
pa_stream_set_write_callback(stream, NULL, NULL);
|
||||
#if PA_CHECK_VERSION(0,9,15)
|
||||
if(pa_stream_set_buffer_attr_callback)
|
||||
pa_stream_set_buffer_attr_callback(stream, NULL, NULL);
|
||||
#endif
|
||||
pa_stream_set_buffer_attr_callback(stream, NULL, NULL);
|
||||
pa_stream_disconnect(stream);
|
||||
pa_stream_unref(stream);
|
||||
}
|
||||
@@ -544,6 +517,7 @@ static void ALCpulsePlayback_deviceCallback(pa_context *UNUSED(context), const p
|
||||
pa_threaded_mainloop *loop = pdata;
|
||||
const DevMap *iter;
|
||||
DevMap entry;
|
||||
int count;
|
||||
|
||||
if(eol)
|
||||
{
|
||||
@@ -553,18 +527,34 @@ static void ALCpulsePlayback_deviceCallback(pa_context *UNUSED(context), const p
|
||||
|
||||
#define MATCH_INFO_NAME(iter) (al_string_cmp_cstr((iter)->device_name, info->name) == 0)
|
||||
VECTOR_FIND_IF(iter, const DevMap, PlaybackDevices, MATCH_INFO_NAME);
|
||||
if(iter != VECTOR_ITER_END(PlaybackDevices)) return;
|
||||
#undef MATCH_INFO_NAME
|
||||
if(iter != VECTOR_ITER_END(PlaybackDevices))
|
||||
return;
|
||||
|
||||
TRACE("Got device \"%s\", \"%s\"\n", info->description, info->name);
|
||||
|
||||
AL_STRING_INIT(entry.name);
|
||||
AL_STRING_INIT(entry.device_name);
|
||||
|
||||
al_string_copy_cstr(&entry.name, info->description);
|
||||
al_string_copy_cstr(&entry.device_name, info->name);
|
||||
|
||||
count = 0;
|
||||
while(1)
|
||||
{
|
||||
al_string_copy_cstr(&entry.name, info->description);
|
||||
if(count != 0)
|
||||
{
|
||||
char str[64];
|
||||
snprintf(str, sizeof(str), " #%d", count+1);
|
||||
al_string_append_cstr(&entry.name, str);
|
||||
}
|
||||
|
||||
#define MATCH_ENTRY(i) (al_string_cmp(entry.name, (i)->name) == 0)
|
||||
VECTOR_FIND_IF(iter, const DevMap, PlaybackDevices, MATCH_ENTRY);
|
||||
if(iter == VECTOR_ITER_END(PlaybackDevices)) break;
|
||||
#undef MATCH_ENTRY
|
||||
count++;
|
||||
}
|
||||
|
||||
TRACE("Got device \"%s\", \"%s\"\n", al_string_get_cstr(entry.name), al_string_get_cstr(entry.device_name));
|
||||
|
||||
VECTOR_PUSH_BACK(PlaybackDevices, entry);
|
||||
}
|
||||
|
||||
@@ -660,27 +650,44 @@ static void ALCpulsePlayback_streamWriteCallback(pa_stream* UNUSED(p), size_t UN
|
||||
|
||||
static void ALCpulsePlayback_sinkInfoCallback(pa_context *UNUSED(context), const pa_sink_info *info, int eol, void *pdata)
|
||||
{
|
||||
static const struct {
|
||||
enum DevFmtChannels chans;
|
||||
pa_channel_map map;
|
||||
} chanmaps[] = {
|
||||
{ DevFmtX71, { 8, {
|
||||
PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
|
||||
PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE,
|
||||
PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
|
||||
PA_CHANNEL_POSITION_SIDE_LEFT, PA_CHANNEL_POSITION_SIDE_RIGHT
|
||||
} } },
|
||||
{ DevFmtX61, { 7, {
|
||||
PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
|
||||
PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE,
|
||||
PA_CHANNEL_POSITION_REAR_CENTER,
|
||||
PA_CHANNEL_POSITION_SIDE_LEFT, PA_CHANNEL_POSITION_SIDE_RIGHT
|
||||
} } },
|
||||
{ DevFmtX51, { 6, {
|
||||
PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
|
||||
PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE,
|
||||
PA_CHANNEL_POSITION_SIDE_LEFT, PA_CHANNEL_POSITION_SIDE_RIGHT
|
||||
} } },
|
||||
{ DevFmtX51Rear, { 6, {
|
||||
PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
|
||||
PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE,
|
||||
PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT
|
||||
} } },
|
||||
{ DevFmtQuad, { 4, {
|
||||
PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
|
||||
PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT
|
||||
} } },
|
||||
{ DevFmtStereo, { 2, {
|
||||
PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT
|
||||
} } },
|
||||
{ DevFmtMono, { 1, {PA_CHANNEL_POSITION_MONO} } }
|
||||
};
|
||||
ALCpulsePlayback *self = pdata;
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
|
||||
char chanmap_str[256] = "";
|
||||
const struct {
|
||||
const char *str;
|
||||
enum DevFmtChannels chans;
|
||||
} chanmaps[] = {
|
||||
{ "front-left,front-right,front-center,lfe,rear-left,rear-right,side-left,side-right",
|
||||
DevFmtX71 },
|
||||
{ "front-left,front-right,front-center,lfe,rear-center,side-left,side-right",
|
||||
DevFmtX61 },
|
||||
{ "front-left,front-right,front-center,lfe,rear-left,rear-right",
|
||||
DevFmtX51 },
|
||||
{ "front-left,front-right,front-center,lfe,side-left,side-right",
|
||||
DevFmtX51Side },
|
||||
{ "front-left,front-right,rear-left,rear-right", DevFmtQuad },
|
||||
{ "front-left,front-right", DevFmtStereo },
|
||||
{ "mono", DevFmtMono },
|
||||
{ NULL, 0 }
|
||||
};
|
||||
int i;
|
||||
size_t i;
|
||||
|
||||
if(eol)
|
||||
{
|
||||
@@ -688,26 +695,27 @@ static void ALCpulsePlayback_sinkInfoCallback(pa_context *UNUSED(context), const
|
||||
return;
|
||||
}
|
||||
|
||||
for(i = 0;chanmaps[i].str;i++)
|
||||
for(i = 0;i < COUNTOF(chanmaps);i++)
|
||||
{
|
||||
pa_channel_map map;
|
||||
if(!pa_channel_map_parse(&map, chanmaps[i].str))
|
||||
continue;
|
||||
|
||||
if(pa_channel_map_equal(&info->channel_map, &map)
|
||||
#if PA_CHECK_VERSION(0,9,15)
|
||||
|| (pa_channel_map_superset &&
|
||||
pa_channel_map_superset(&info->channel_map, &map))
|
||||
#endif
|
||||
)
|
||||
if(pa_channel_map_superset(&info->channel_map, &chanmaps[i].map))
|
||||
{
|
||||
device->FmtChans = chanmaps[i].chans;
|
||||
return;
|
||||
if(!(device->Flags&DEVICE_CHANNELS_REQUEST))
|
||||
device->FmtChans = chanmaps[i].chans;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(i == COUNTOF(chanmaps))
|
||||
{
|
||||
char chanmap_str[PA_CHANNEL_MAP_SNPRINT_MAX] = "";
|
||||
pa_channel_map_snprint(chanmap_str, sizeof(chanmap_str), &info->channel_map);
|
||||
WARN("Failed to find format for channel map:\n %s\n", chanmap_str);
|
||||
}
|
||||
|
||||
pa_channel_map_snprint(chanmap_str, sizeof(chanmap_str), &info->channel_map);
|
||||
ERR("Failed to find format for channel map:\n %s\n", chanmap_str);
|
||||
if(info->active_port)
|
||||
TRACE("Active port: %s (%s)\n", info->active_port->name, info->active_port->description);
|
||||
device->IsHeadphones = (info->active_port &&
|
||||
strcmp(info->active_port->name, "analog-output-headphones") == 0 &&
|
||||
device->FmtChans == DevFmtStereo);
|
||||
}
|
||||
|
||||
static void ALCpulsePlayback_sinkNameCallback(pa_context *UNUSED(context), const pa_sink_info *info, int eol, void *pdata)
|
||||
@@ -818,10 +826,7 @@ static int ALCpulsePlayback_mixerProc(void *ptr)
|
||||
void *buf;
|
||||
pa_free_cb_t free_func = NULL;
|
||||
|
||||
#if PA_CHECK_VERSION(0,9,16)
|
||||
if(!pa_stream_begin_write ||
|
||||
pa_stream_begin_write(self->stream, &buf, &newlen) < 0)
|
||||
#endif
|
||||
if(pa_stream_begin_write(self->stream, &buf, &newlen) < 0)
|
||||
{
|
||||
buf = pa_xmalloc(newlen);
|
||||
free_func = pa_xfree;
|
||||
@@ -841,10 +846,10 @@ static int ALCpulsePlayback_mixerProc(void *ptr)
|
||||
|
||||
static ALCenum ALCpulsePlayback_open(ALCpulsePlayback *self, const ALCchar *name)
|
||||
{
|
||||
const_al_string dev_name = AL_STRING_INIT_STATIC();
|
||||
const char *pulse_name = NULL;
|
||||
pa_stream_flags_t flags;
|
||||
pa_sample_spec spec;
|
||||
pa_operation *o;
|
||||
|
||||
if(name)
|
||||
{
|
||||
@@ -859,6 +864,7 @@ static ALCenum ALCpulsePlayback_open(ALCpulsePlayback *self, const ALCchar *name
|
||||
if(iter == VECTOR_ITER_END(PlaybackDevices))
|
||||
return ALC_INVALID_VALUE;
|
||||
pulse_name = al_string_get_cstr(iter->device_name);
|
||||
dev_name = iter->name;
|
||||
}
|
||||
|
||||
if(!pulse_open(&self->loop, &self->context, ALCpulsePlayback_contextStateCallback, self))
|
||||
@@ -868,7 +874,7 @@ static ALCenum ALCpulsePlayback_open(ALCpulsePlayback *self, const ALCchar *name
|
||||
|
||||
flags = PA_STREAM_FIX_FORMAT | PA_STREAM_FIX_RATE |
|
||||
PA_STREAM_FIX_CHANNELS;
|
||||
if(!GetConfigValueBool("pulse", "allow-moves", 0))
|
||||
if(!GetConfigValueBool(NULL, "pulse", "allow-moves", 0))
|
||||
flags |= PA_STREAM_DONT_MOVE;
|
||||
|
||||
spec.format = PA_SAMPLE_S16NE;
|
||||
@@ -889,10 +895,19 @@ static ALCenum ALCpulsePlayback_open(ALCpulsePlayback *self, const ALCchar *name
|
||||
pa_stream_set_moved_callback(self->stream, ALCpulsePlayback_streamMovedCallback, self);
|
||||
|
||||
al_string_copy_cstr(&self->device_name, pa_stream_get_device_name(self->stream));
|
||||
o = pa_context_get_sink_info_by_name(self->context,
|
||||
al_string_get_cstr(self->device_name),
|
||||
ALCpulsePlayback_sinkNameCallback, self);
|
||||
wait_for_operation(o, self->loop);
|
||||
if(al_string_empty(dev_name))
|
||||
{
|
||||
pa_operation *o = pa_context_get_sink_info_by_name(
|
||||
self->context, al_string_get_cstr(self->device_name),
|
||||
ALCpulsePlayback_sinkNameCallback, self
|
||||
);
|
||||
wait_for_operation(o, self->loop);
|
||||
}
|
||||
else
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
|
||||
al_string_copy(&device->DeviceName, dev_name);
|
||||
}
|
||||
|
||||
pa_threaded_mainloop_unlock(self->loop);
|
||||
|
||||
@@ -915,37 +930,33 @@ static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self)
|
||||
pa_stream_flags_t flags = 0;
|
||||
const char *mapname = NULL;
|
||||
pa_channel_map chanmap;
|
||||
pa_operation *o;
|
||||
ALuint len;
|
||||
|
||||
pa_threaded_mainloop_lock(self->loop);
|
||||
|
||||
if(self->stream)
|
||||
{
|
||||
pa_stream_set_state_callback(self->stream, NULL, NULL);
|
||||
pa_stream_set_moved_callback(self->stream, NULL, NULL);
|
||||
#if PA_CHECK_VERSION(0,9,15)
|
||||
if(pa_stream_set_buffer_attr_callback)
|
||||
pa_stream_set_buffer_attr_callback(self->stream, NULL, NULL);
|
||||
#endif
|
||||
pa_stream_set_write_callback(self->stream, NULL, NULL);
|
||||
pa_stream_set_buffer_attr_callback(self->stream, NULL, NULL);
|
||||
pa_stream_disconnect(self->stream);
|
||||
pa_stream_unref(self->stream);
|
||||
self->stream = NULL;
|
||||
}
|
||||
|
||||
if(!(device->Flags&DEVICE_CHANNELS_REQUEST))
|
||||
{
|
||||
pa_operation *o;
|
||||
o = pa_context_get_sink_info_by_name(self->context,
|
||||
al_string_get_cstr(self->device_name),
|
||||
ALCpulsePlayback_sinkInfoCallback, self);
|
||||
wait_for_operation(o, self->loop);
|
||||
}
|
||||
if(!(device->Flags&DEVICE_FREQUENCY_REQUEST))
|
||||
flags |= PA_STREAM_FIX_RATE;
|
||||
o = pa_context_get_sink_info_by_name(self->context, al_string_get_cstr(self->device_name),
|
||||
ALCpulsePlayback_sinkInfoCallback, self);
|
||||
wait_for_operation(o, self->loop);
|
||||
|
||||
if(GetConfigValueBool(al_string_get_cstr(device->DeviceName), "pulse", "fix-rate", 0) ||
|
||||
!(device->Flags&DEVICE_FREQUENCY_REQUEST))
|
||||
flags |= PA_STREAM_FIX_RATE;
|
||||
flags |= PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE;
|
||||
flags |= PA_STREAM_ADJUST_LATENCY;
|
||||
flags |= PA_STREAM_START_CORKED;
|
||||
if(!GetConfigValueBool("pulse", "allow-moves", 0))
|
||||
if(!GetConfigValueBool(NULL, "pulse", "allow-moves", 0))
|
||||
flags |= PA_STREAM_DONT_MOVE;
|
||||
|
||||
switch(device->FmtType)
|
||||
@@ -987,6 +998,9 @@ static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self)
|
||||
case DevFmtMono:
|
||||
mapname = "mono";
|
||||
break;
|
||||
case DevFmtBFormat3D:
|
||||
device->FmtChans = DevFmtStereo;
|
||||
/*fall-through*/
|
||||
case DevFmtStereo:
|
||||
mapname = "front-left,front-right";
|
||||
break;
|
||||
@@ -994,11 +1008,11 @@ static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self)
|
||||
mapname = "front-left,front-right,rear-left,rear-right";
|
||||
break;
|
||||
case DevFmtX51:
|
||||
mapname = "front-left,front-right,front-center,lfe,rear-left,rear-right";
|
||||
break;
|
||||
case DevFmtX51Side:
|
||||
mapname = "front-left,front-right,front-center,lfe,side-left,side-right";
|
||||
break;
|
||||
case DevFmtX51Rear:
|
||||
mapname = "front-left,front-right,front-center,lfe,rear-left,rear-right";
|
||||
break;
|
||||
case DevFmtX61:
|
||||
mapname = "front-left,front-right,front-center,lfe,rear-center,side-left,side-right";
|
||||
break;
|
||||
@@ -1035,8 +1049,6 @@ static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self)
|
||||
self->spec = *(pa_stream_get_sample_spec(self->stream));
|
||||
if(device->Frequency != self->spec.rate)
|
||||
{
|
||||
pa_operation *o;
|
||||
|
||||
/* Server updated our playback rate, so modify the buffer attribs
|
||||
* accordingly. */
|
||||
device->NumUpdates = (ALuint)((ALdouble)device->NumUpdates / device->Frequency *
|
||||
@@ -1053,15 +1065,10 @@ static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self)
|
||||
device->Frequency = self->spec.rate;
|
||||
}
|
||||
|
||||
#if PA_CHECK_VERSION(0,9,15)
|
||||
if(pa_stream_set_buffer_attr_callback)
|
||||
pa_stream_set_buffer_attr_callback(self->stream, ALCpulsePlayback_bufferAttrCallback, self);
|
||||
#endif
|
||||
pa_stream_set_buffer_attr_callback(self->stream, ALCpulsePlayback_bufferAttrCallback, self);
|
||||
ALCpulsePlayback_bufferAttrCallback(self->stream, self);
|
||||
|
||||
len = self->attr.minreq / pa_frame_size(&self->spec);
|
||||
if((CPUCapFlags&CPU_CAP_SSE))
|
||||
len = (len+3)&~3;
|
||||
device->NumUpdates = (ALuint)((ALdouble)device->NumUpdates/len*device->UpdateSize + 0.5);
|
||||
device->NumUpdates = clampu(device->NumUpdates, 2, 16);
|
||||
device->UpdateSize = len;
|
||||
@@ -1083,10 +1090,18 @@ static void ALCpulsePlayback_stop(ALCpulsePlayback *self)
|
||||
pa_operation *o;
|
||||
int res;
|
||||
|
||||
if(!self->stream)
|
||||
if(!self->stream || self->killNow)
|
||||
return;
|
||||
|
||||
self->killNow = AL_TRUE;
|
||||
/* Signal the main loop in case PulseAudio isn't sending us audio requests
|
||||
* (e.g. if the device is suspended). We need to lock the mainloop in case
|
||||
* the mixer is between checking the killNow flag but before waiting for
|
||||
* the signal.
|
||||
*/
|
||||
pa_threaded_mainloop_lock(self->loop);
|
||||
pa_threaded_mainloop_unlock(self->loop);
|
||||
pa_threaded_mainloop_signal(self->loop, 0);
|
||||
althrd_join(self->thread, &res);
|
||||
|
||||
pa_threaded_mainloop_lock(self->loop);
|
||||
@@ -1199,6 +1214,7 @@ static void ALCpulseCapture_deviceCallback(pa_context *UNUSED(context), const pa
|
||||
pa_threaded_mainloop *loop = pdata;
|
||||
const DevMap *iter;
|
||||
DevMap entry;
|
||||
int count;
|
||||
|
||||
if(eol)
|
||||
{
|
||||
@@ -1208,18 +1224,34 @@ static void ALCpulseCapture_deviceCallback(pa_context *UNUSED(context), const pa
|
||||
|
||||
#define MATCH_INFO_NAME(iter) (al_string_cmp_cstr((iter)->device_name, info->name) == 0)
|
||||
VECTOR_FIND_IF(iter, const DevMap, CaptureDevices, MATCH_INFO_NAME);
|
||||
if(iter != VECTOR_ITER_END(CaptureDevices)) return;
|
||||
#undef MATCH_INFO_NAME
|
||||
if(iter != VECTOR_ITER_END(CaptureDevices))
|
||||
return;
|
||||
|
||||
TRACE("Got device \"%s\", \"%s\"\n", info->description, info->name);
|
||||
|
||||
AL_STRING_INIT(entry.name);
|
||||
AL_STRING_INIT(entry.device_name);
|
||||
|
||||
al_string_copy_cstr(&entry.name, info->description);
|
||||
al_string_copy_cstr(&entry.device_name, info->name);
|
||||
|
||||
count = 0;
|
||||
while(1)
|
||||
{
|
||||
al_string_copy_cstr(&entry.name, info->description);
|
||||
if(count != 0)
|
||||
{
|
||||
char str[64];
|
||||
snprintf(str, sizeof(str), " #%d", count+1);
|
||||
al_string_append_cstr(&entry.name, str);
|
||||
}
|
||||
|
||||
#define MATCH_ENTRY(i) (al_string_cmp(entry.name, (i)->name) == 0)
|
||||
VECTOR_FIND_IF(iter, const DevMap, CaptureDevices, MATCH_ENTRY);
|
||||
if(iter == VECTOR_ITER_END(CaptureDevices)) break;
|
||||
#undef MATCH_ENTRY
|
||||
count++;
|
||||
}
|
||||
|
||||
TRACE("Got device \"%s\", \"%s\"\n", al_string_get_cstr(entry.name), al_string_get_cstr(entry.device_name));
|
||||
|
||||
VECTOR_PUSH_BACK(CaptureDevices, entry);
|
||||
}
|
||||
|
||||
@@ -1372,7 +1404,6 @@ static ALCenum ALCpulseCapture_open(ALCpulseCapture *self, const ALCchar *name)
|
||||
const char *pulse_name = NULL;
|
||||
pa_stream_flags_t flags = 0;
|
||||
pa_channel_map chanmap;
|
||||
pa_operation *o;
|
||||
ALuint samples;
|
||||
|
||||
if(name)
|
||||
@@ -1388,6 +1419,7 @@ static ALCenum ALCpulseCapture_open(ALCpulseCapture *self, const ALCchar *name)
|
||||
if(iter == VECTOR_ITER_END(CaptureDevices))
|
||||
return ALC_INVALID_VALUE;
|
||||
pulse_name = al_string_get_cstr(iter->device_name);
|
||||
al_string_copy(&device->DeviceName, iter->name);
|
||||
}
|
||||
|
||||
if(!pulse_open(&self->loop, &self->context, ALCpulseCapture_contextStateCallback, self))
|
||||
@@ -1445,7 +1477,7 @@ static ALCenum ALCpulseCapture_open(ALCpulseCapture *self, const ALCchar *name)
|
||||
pa_frame_size(&self->spec);
|
||||
|
||||
flags |= PA_STREAM_START_CORKED|PA_STREAM_ADJUST_LATENCY;
|
||||
if(!GetConfigValueBool("pulse", "allow-moves", 0))
|
||||
if(!GetConfigValueBool(NULL, "pulse", "allow-moves", 0))
|
||||
flags |= PA_STREAM_DONT_MOVE;
|
||||
|
||||
TRACE("Connecting to \"%s\"\n", pulse_name ? pulse_name : "(default)");
|
||||
@@ -1461,10 +1493,14 @@ static ALCenum ALCpulseCapture_open(ALCpulseCapture *self, const ALCchar *name)
|
||||
pa_stream_set_state_callback(self->stream, ALCpulseCapture_streamStateCallback, self);
|
||||
|
||||
al_string_copy_cstr(&self->device_name, pa_stream_get_device_name(self->stream));
|
||||
o = pa_context_get_source_info_by_name(self->context,
|
||||
al_string_get_cstr(self->device_name),
|
||||
ALCpulseCapture_sourceNameCallback, self);
|
||||
wait_for_operation(o, self->loop);
|
||||
if(al_string_empty(device->DeviceName))
|
||||
{
|
||||
pa_operation *o = pa_context_get_source_info_by_name(
|
||||
self->context, al_string_get_cstr(self->device_name),
|
||||
ALCpulseCapture_sourceNameCallback, self
|
||||
);
|
||||
wait_for_operation(o, self->loop);
|
||||
}
|
||||
|
||||
pa_threaded_mainloop_unlock(self->loop);
|
||||
return ALC_NO_ERROR;
|
||||
@@ -1607,11 +1643,6 @@ static void ALCpulseCapture_unlock(ALCpulseCapture *self)
|
||||
}
|
||||
|
||||
|
||||
static inline void AppendAllDevicesList2(const DevMap *entry)
|
||||
{ AppendAllDevicesList(al_string_get_cstr(entry->name)); }
|
||||
static inline void AppendCaptureDeviceList2(const DevMap *entry)
|
||||
{ AppendCaptureDeviceList(al_string_get_cstr(entry->name)); }
|
||||
|
||||
typedef struct ALCpulseBackendFactory {
|
||||
DERIVE_FROM_TYPE(ALCbackendFactory);
|
||||
} ALCpulseBackendFactory;
|
||||
@@ -1638,7 +1669,7 @@ static ALCboolean ALCpulseBackendFactory_init(ALCpulseBackendFactory* UNUSED(sel
|
||||
pa_threaded_mainloop *loop;
|
||||
|
||||
pulse_ctx_flags = 0;
|
||||
if(!GetConfigValueBool("pulse", "spawn-server", 1))
|
||||
if(!GetConfigValueBool(NULL, "pulse", "spawn-server", 1))
|
||||
pulse_ctx_flags |= PA_CONTEXT_NOAUTOSPAWN;
|
||||
|
||||
if((loop=pa_threaded_mainloop_new()) &&
|
||||
@@ -1701,12 +1732,16 @@ static void ALCpulseBackendFactory_probe(ALCpulseBackendFactory* UNUSED(self), e
|
||||
{
|
||||
case ALL_DEVICE_PROBE:
|
||||
ALCpulsePlayback_probeDevices();
|
||||
VECTOR_FOR_EACH(const DevMap, PlaybackDevices, AppendAllDevicesList2);
|
||||
#define APPEND_ALL_DEVICES_LIST(e) AppendAllDevicesList(al_string_get_cstr((e)->name))
|
||||
VECTOR_FOR_EACH(const DevMap, PlaybackDevices, APPEND_ALL_DEVICES_LIST);
|
||||
#undef APPEND_ALL_DEVICES_LIST
|
||||
break;
|
||||
|
||||
case CAPTURE_DEVICE_PROBE:
|
||||
ALCpulseCapture_probeDevices();
|
||||
VECTOR_FOR_EACH(const DevMap, CaptureDevices, AppendCaptureDeviceList2);
|
||||
#define APPEND_CAPTURE_DEVICE_LIST(e) AppendCaptureDeviceList(al_string_get_cstr((e)->name))
|
||||
VECTOR_FOR_EACH(const DevMap, CaptureDevices, APPEND_CAPTURE_DEVICE_LIST);
|
||||
#undef APPEND_CAPTURE_DEVICE_LIST
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1716,25 +1751,15 @@ static ALCbackend* ALCpulseBackendFactory_createBackend(ALCpulseBackendFactory*
|
||||
if(type == ALCbackend_Playback)
|
||||
{
|
||||
ALCpulsePlayback *backend;
|
||||
|
||||
backend = ALCpulsePlayback_New(sizeof(*backend));
|
||||
NEW_OBJ(backend, ALCpulsePlayback)(device);
|
||||
if(!backend) return NULL;
|
||||
memset(backend, 0, sizeof(*backend));
|
||||
|
||||
ALCpulsePlayback_Construct(backend, device);
|
||||
|
||||
return STATIC_CAST(ALCbackend, backend);
|
||||
}
|
||||
if(type == ALCbackend_Capture)
|
||||
{
|
||||
ALCpulseCapture *backend;
|
||||
|
||||
backend = ALCpulseCapture_New(sizeof(*backend));
|
||||
NEW_OBJ(backend, ALCpulseCapture)(device);
|
||||
if(!backend) return NULL;
|
||||
memset(backend, 0, sizeof(*backend));
|
||||
|
||||
ALCpulseCapture_Construct(backend, device);
|
||||
|
||||
return STATIC_CAST(ALCbackend, backend);
|
||||
}
|
||||
|
||||
|
||||
+108
-352
@@ -13,8 +13,8 @@
|
||||
*
|
||||
* 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., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
@@ -53,12 +53,12 @@ typedef struct {
|
||||
int card;
|
||||
int dev;
|
||||
} DevMap;
|
||||
TYPEDEF_VECTOR(DevMap, vector_DevMap)
|
||||
|
||||
static vector_DevMap DeviceNameMap;
|
||||
static vector_DevMap CaptureNameMap;
|
||||
|
||||
static const ALCchar qsaDevice[] = "QSA Default";
|
||||
static DevMap* allDevNameMap;
|
||||
static ALuint numDevNames;
|
||||
static DevMap* allCaptureDevNameMap;
|
||||
static ALuint numCaptureDevNames;
|
||||
|
||||
static const struct {
|
||||
int32_t format;
|
||||
@@ -104,69 +104,57 @@ static const struct {
|
||||
{0},
|
||||
};
|
||||
|
||||
static DevMap *deviceList(int type, ALuint *count)
|
||||
static void deviceList(int type, vector_DevMap *devmap)
|
||||
{
|
||||
snd_ctl_t* handle;
|
||||
snd_pcm_info_t pcminfo;
|
||||
int max_cards, card, err, dev, num_devices, idx;
|
||||
DevMap* dev_list;
|
||||
int max_cards, card, err, dev;
|
||||
DevMap entry;
|
||||
char name[1024];
|
||||
struct snd_ctl_hw_info info;
|
||||
void* temp;
|
||||
|
||||
idx=0;
|
||||
num_devices=0;
|
||||
max_cards=snd_cards();
|
||||
max_cards = snd_cards();
|
||||
if(max_cards < 0)
|
||||
return;
|
||||
|
||||
if (max_cards<=0)
|
||||
VECTOR_RESERVE(*devmap, max_cards+1);
|
||||
VECTOR_RESIZE(*devmap, 0);
|
||||
|
||||
entry.name = strdup(qsaDevice);
|
||||
entry.card = 0;
|
||||
entry.dev = 0;
|
||||
VECTOR_PUSH_BACK(*devmap, entry);
|
||||
|
||||
for(card = 0;card < max_cards;card++)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
dev_list=malloc(sizeof(DevMap)*1);
|
||||
dev_list[0].name=strdup(qsaDevice);
|
||||
num_devices=1;
|
||||
|
||||
for (card=0; card<max_cards; card++)
|
||||
{
|
||||
if ((err=snd_ctl_open(&handle, card))<0)
|
||||
{
|
||||
if((err=snd_ctl_open(&handle, card)) < 0)
|
||||
continue;
|
||||
}
|
||||
if ((err=snd_ctl_hw_info(handle, &info))<0)
|
||||
|
||||
if((err=snd_ctl_hw_info(handle, &info)) < 0)
|
||||
{
|
||||
snd_ctl_close(handle);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (dev=0; dev<(int)info.pcmdevs; dev++)
|
||||
for(dev = 0;dev < (int)info.pcmdevs;dev++)
|
||||
{
|
||||
if ((err=snd_ctl_pcm_info(handle, dev, &pcminfo)) < 0)
|
||||
{
|
||||
if((err=snd_ctl_pcm_info(handle, dev, &pcminfo)) < 0)
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((type==SND_PCM_CHANNEL_PLAYBACK && (pcminfo.flags&SND_PCM_INFO_PLAYBACK)) ||
|
||||
(type==SND_PCM_CHANNEL_CAPTURE && (pcminfo.flags&SND_PCM_INFO_CAPTURE)))
|
||||
if((type==SND_PCM_CHANNEL_PLAYBACK && (pcminfo.flags&SND_PCM_INFO_PLAYBACK)) ||
|
||||
(type==SND_PCM_CHANNEL_CAPTURE && (pcminfo.flags&SND_PCM_INFO_CAPTURE)))
|
||||
{
|
||||
temp=realloc(dev_list, sizeof(DevMap)*(num_devices+1));
|
||||
if (temp)
|
||||
{
|
||||
dev_list=temp;
|
||||
snprintf(name, sizeof(name), "%s [%s] (hw:%d,%d)", info.name, pcminfo.name, card, dev);
|
||||
dev_list[num_devices].name=strdup(name);
|
||||
dev_list[num_devices].card=card;
|
||||
dev_list[num_devices].dev=dev;
|
||||
num_devices++;
|
||||
}
|
||||
snprintf(name, sizeof(name), "%s [%s] (hw:%d,%d)", info.name, pcminfo.name, card, dev);
|
||||
entry.name = strdup(name);
|
||||
entry.card = card;
|
||||
entry.dev = dev;
|
||||
|
||||
VECTOR_PUSH_BACK(*devmap, entry);
|
||||
TRACE("Got device \"%s\", card %d, dev %d\n", name, card, dev);
|
||||
}
|
||||
}
|
||||
snd_ctl_close (handle);
|
||||
snd_ctl_close(handle);
|
||||
}
|
||||
|
||||
*count=num_devices;
|
||||
|
||||
return dev_list;
|
||||
}
|
||||
|
||||
|
||||
@@ -266,71 +254,48 @@ FORCE_ALIGN static int qsa_proc_playback(void* ptr)
|
||||
|
||||
static ALCenum qsa_open_playback(ALCdevice* device, const ALCchar* deviceName)
|
||||
{
|
||||
qsa_data* data;
|
||||
char driver[64];
|
||||
int status;
|
||||
qsa_data *data;
|
||||
int card, dev;
|
||||
int status;
|
||||
|
||||
strncpy(driver, GetConfigValue("qsa", "device", qsaDevice), sizeof(driver)-1);
|
||||
driver[sizeof(driver)-1]=0;
|
||||
|
||||
data=(qsa_data*)calloc(1, sizeof(qsa_data));
|
||||
if (data==NULL)
|
||||
{
|
||||
data = (qsa_data*)calloc(1, sizeof(qsa_data));
|
||||
if(data == NULL)
|
||||
return ALC_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
if (!deviceName)
|
||||
{
|
||||
deviceName=driver;
|
||||
}
|
||||
if(!deviceName)
|
||||
deviceName = qsaDevice;
|
||||
|
||||
if (strcmp(deviceName, qsaDevice)==0)
|
||||
{
|
||||
if (!deviceName)
|
||||
{
|
||||
deviceName=qsaDevice;
|
||||
}
|
||||
|
||||
status=snd_pcm_open_preferred(&data->pcmHandle, &card, &dev, SND_PCM_OPEN_PLAYBACK);
|
||||
}
|
||||
if(strcmp(deviceName, qsaDevice) == 0)
|
||||
status = snd_pcm_open_preferred(&data->pcmHandle, &card, &dev, SND_PCM_OPEN_PLAYBACK);
|
||||
else
|
||||
{
|
||||
size_t idx;
|
||||
const DevMap *iter;
|
||||
|
||||
if (!allDevNameMap)
|
||||
{
|
||||
allDevNameMap=deviceList(SND_PCM_CHANNEL_PLAYBACK, &numDevNames);
|
||||
}
|
||||
if(VECTOR_SIZE(DeviceNameMap) == 0)
|
||||
deviceList(SND_PCM_CHANNEL_PLAYBACK, &DeviceNameMap);
|
||||
|
||||
for (idx=0; idx<numDevNames; idx++)
|
||||
{
|
||||
if (allDevNameMap[idx].name && strcmp(deviceName, allDevNameMap[idx].name)==0)
|
||||
{
|
||||
if (idx>0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (idx==numDevNames)
|
||||
#define MATCH_DEVNAME(iter) ((iter)->name && strcmp(deviceName, (iter)->name)==0)
|
||||
VECTOR_FIND_IF(iter, const DevMap, DeviceNameMap, MATCH_DEVNAME);
|
||||
#undef MATCH_DEVNAME
|
||||
if(iter == VECTOR_ITER_END(DeviceNameMap))
|
||||
{
|
||||
free(data);
|
||||
return ALC_INVALID_DEVICE;
|
||||
}
|
||||
|
||||
status=snd_pcm_open(&data->pcmHandle, allDevNameMap[idx].card, allDevNameMap[idx].dev, SND_PCM_OPEN_PLAYBACK);
|
||||
status = snd_pcm_open(&data->pcmHandle, iter->card, iter->dev, SND_PCM_OPEN_PLAYBACK);
|
||||
}
|
||||
|
||||
if (status<0)
|
||||
if(status < 0)
|
||||
{
|
||||
free(data);
|
||||
return ALC_INVALID_DEVICE;
|
||||
}
|
||||
|
||||
data->audio_fd=snd_pcm_file_descriptor(data->pcmHandle, SND_PCM_CHANNEL_PLAYBACK);
|
||||
if (data->audio_fd<0)
|
||||
data->audio_fd = snd_pcm_file_descriptor(data->pcmHandle, SND_PCM_CHANNEL_PLAYBACK);
|
||||
if(data->audio_fd < 0)
|
||||
{
|
||||
snd_pcm_close(data->pcmHandle);
|
||||
free(data);
|
||||
return ALC_INVALID_DEVICE;
|
||||
}
|
||||
@@ -633,72 +598,51 @@ static void qsa_stop_playback(ALCdevice* device)
|
||||
|
||||
static ALCenum qsa_open_capture(ALCdevice* device, const ALCchar* deviceName)
|
||||
{
|
||||
qsa_data* data;
|
||||
int format=-1;
|
||||
char driver[64];
|
||||
qsa_data *data;
|
||||
int card, dev;
|
||||
int format=-1;
|
||||
int status;
|
||||
|
||||
strncpy(driver, GetConfigValue("qsa", "capture", qsaDevice), sizeof(driver)-1);
|
||||
driver[sizeof(driver)-1]=0;
|
||||
|
||||
data=(qsa_data*)calloc(1, sizeof(qsa_data));
|
||||
if (data==NULL)
|
||||
{
|
||||
return ALC_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
if (!deviceName)
|
||||
{
|
||||
deviceName=driver;
|
||||
}
|
||||
if(!deviceName)
|
||||
deviceName = qsaDevice;
|
||||
|
||||
if (strcmp(deviceName, qsaDevice)==0)
|
||||
{
|
||||
if (!deviceName)
|
||||
{
|
||||
deviceName=qsaDevice;
|
||||
}
|
||||
|
||||
status=snd_pcm_open_preferred(&data->pcmHandle, &card, &dev, SND_PCM_OPEN_CAPTURE);
|
||||
}
|
||||
if(strcmp(deviceName, qsaDevice) == 0)
|
||||
status = snd_pcm_open_preferred(&data->pcmHandle, &card, &dev, SND_PCM_OPEN_CAPTURE);
|
||||
else
|
||||
{
|
||||
size_t idx;
|
||||
const DevMap *iter;
|
||||
|
||||
if (!allCaptureDevNameMap)
|
||||
{
|
||||
allCaptureDevNameMap=deviceList(SND_PCM_CHANNEL_CAPTURE, &numDevNames);
|
||||
}
|
||||
if(VECTOR_SIZE(CaptureNameMap) == 0)
|
||||
deviceList(SND_PCM_CHANNEL_CAPTURE, &CaptureNameMap);
|
||||
|
||||
for (idx=0; idx<numDevNames; idx++)
|
||||
{
|
||||
if (allCaptureDevNameMap[idx].name && strcmp(deviceName, allCaptureDevNameMap[idx].name)==0)
|
||||
{
|
||||
if (idx>0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (idx==numDevNames)
|
||||
#define MATCH_DEVNAME(iter) ((iter)->name && strcmp(deviceName, (iter)->name)==0)
|
||||
VECTOR_FIND_IF(iter, const DevMap, CaptureNameMap, MATCH_DEVNAME);
|
||||
#undef MATCH_DEVNAME
|
||||
if(iter == VECTOR_ITER_END(CaptureNameMap))
|
||||
{
|
||||
free(data);
|
||||
return ALC_INVALID_DEVICE;
|
||||
}
|
||||
|
||||
status=snd_pcm_open(&data->pcmHandle, allCaptureDevNameMap[idx].card, allCaptureDevNameMap[idx].dev, SND_PCM_OPEN_CAPTURE);
|
||||
status = snd_pcm_open(&data->pcmHandle, iter->card, iter->dev, SND_PCM_OPEN_CAPTURE);
|
||||
}
|
||||
|
||||
if (status<0)
|
||||
if(status < 0)
|
||||
{
|
||||
free(data);
|
||||
return ALC_INVALID_DEVICE;
|
||||
}
|
||||
|
||||
data->audio_fd=snd_pcm_file_descriptor(data->pcmHandle, SND_PCM_CHANNEL_CAPTURE);
|
||||
if (data->audio_fd<0)
|
||||
data->audio_fd = snd_pcm_file_descriptor(data->pcmHandle, SND_PCM_CHANNEL_CAPTURE);
|
||||
if(data->audio_fd < 0)
|
||||
{
|
||||
snd_pcm_close(data->pcmHandle);
|
||||
free(data);
|
||||
return ALC_INVALID_DEVICE;
|
||||
}
|
||||
@@ -753,170 +697,13 @@ static ALCenum qsa_open_capture(ALCdevice* device, const ALCchar* deviceName)
|
||||
data->cparams.format.voices=ChannelsFromDevFmt(device->FmtChans);
|
||||
data->cparams.format.format=format;
|
||||
|
||||
if ((snd_pcm_plugin_params(data->pcmHandle, &data->cparams))<0)
|
||||
if(snd_pcm_plugin_params(data->pcmHandle, &data->cparams) < 0)
|
||||
{
|
||||
int original_rate=data->cparams.format.rate;
|
||||
int original_voices=data->cparams.format.voices;
|
||||
int original_format=data->cparams.format.format;
|
||||
int it;
|
||||
int jt;
|
||||
snd_pcm_close(data->pcmHandle);
|
||||
free(data);
|
||||
device->ExtraData=NULL;
|
||||
|
||||
for (it=0; it<1; it++)
|
||||
{
|
||||
/* Check for second pass */
|
||||
if (it==1)
|
||||
{
|
||||
original_rate=ratelist[0].rate;
|
||||
original_voices=channellist[0].channels;
|
||||
original_format=formatlist[0].format;
|
||||
}
|
||||
|
||||
do {
|
||||
/* At first downgrade sample format */
|
||||
jt=0;
|
||||
do {
|
||||
if (formatlist[jt].format==data->cparams.format.format)
|
||||
{
|
||||
data->cparams.format.format=formatlist[jt+1].format;
|
||||
break;
|
||||
}
|
||||
if (formatlist[jt].format==0)
|
||||
{
|
||||
data->cparams.format.format=0;
|
||||
break;
|
||||
}
|
||||
jt++;
|
||||
} while(1);
|
||||
|
||||
if (data->cparams.format.format==0)
|
||||
{
|
||||
data->cparams.format.format=original_format;
|
||||
|
||||
/* At secod downgrade sample rate */
|
||||
jt=0;
|
||||
do {
|
||||
if (ratelist[jt].rate==data->cparams.format.rate)
|
||||
{
|
||||
data->cparams.format.rate=ratelist[jt+1].rate;
|
||||
break;
|
||||
}
|
||||
if (ratelist[jt].rate==0)
|
||||
{
|
||||
data->cparams.format.rate=0;
|
||||
break;
|
||||
}
|
||||
jt++;
|
||||
} while(1);
|
||||
|
||||
if (data->cparams.format.rate==0)
|
||||
{
|
||||
data->cparams.format.rate=original_rate;
|
||||
data->cparams.format.format=original_format;
|
||||
|
||||
/* At third downgrade channels number */
|
||||
jt=0;
|
||||
do {
|
||||
if(channellist[jt].channels==data->cparams.format.voices)
|
||||
{
|
||||
data->cparams.format.voices=channellist[jt+1].channels;
|
||||
break;
|
||||
}
|
||||
if (channellist[jt].channels==0)
|
||||
{
|
||||
data->cparams.format.voices=0;
|
||||
break;
|
||||
}
|
||||
jt++;
|
||||
} while(1);
|
||||
}
|
||||
|
||||
if (data->cparams.format.voices==0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
data->cparams.buf.block.frag_size=device->UpdateSize*
|
||||
data->cparams.format.voices*
|
||||
snd_pcm_format_width(data->cparams.format.format)/8;
|
||||
data->cparams.buf.block.frags_max=device->NumUpdates;
|
||||
data->cparams.buf.block.frags_min=device->NumUpdates;
|
||||
if ((snd_pcm_plugin_params(data->pcmHandle, &data->cparams))<0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
} while(1);
|
||||
|
||||
if (data->cparams.format.voices!=0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (data->cparams.format.voices==0)
|
||||
{
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
/* now fill back to the our AL device */
|
||||
device->Frequency=data->cparams.format.rate;
|
||||
|
||||
switch (data->cparams.format.voices)
|
||||
{
|
||||
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:
|
||||
device->FmtChans=DevFmtMono;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (data->cparams.format.format)
|
||||
{
|
||||
case SND_PCM_SFMT_S8:
|
||||
device->FmtType=DevFmtByte;
|
||||
break;
|
||||
case SND_PCM_SFMT_U8:
|
||||
device->FmtType=DevFmtUByte;
|
||||
break;
|
||||
case SND_PCM_SFMT_S16_LE:
|
||||
device->FmtType=DevFmtShort;
|
||||
break;
|
||||
case SND_PCM_SFMT_U16_LE:
|
||||
device->FmtType=DevFmtUShort;
|
||||
break;
|
||||
case SND_PCM_SFMT_S32_LE:
|
||||
device->FmtType=DevFmtInt;
|
||||
break;
|
||||
case SND_PCM_SFMT_U32_LE:
|
||||
device->FmtType=DevFmtUInt;
|
||||
break;
|
||||
case SND_PCM_SFMT_FLOAT_LE:
|
||||
device->FmtType=DevFmtFloat;
|
||||
break;
|
||||
default:
|
||||
device->FmtType=DevFmtShort;
|
||||
break;
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
return ALC_NO_ERROR;
|
||||
@@ -927,9 +714,8 @@ static void qsa_close_capture(ALCdevice* device)
|
||||
qsa_data* data=(qsa_data*)device->ExtraData;
|
||||
|
||||
if (data->pcmHandle!=NULL)
|
||||
{
|
||||
snd_pcm_close(data->pcmHandle);
|
||||
}
|
||||
|
||||
free(data);
|
||||
device->ExtraData=NULL;
|
||||
}
|
||||
@@ -954,10 +740,6 @@ static void qsa_start_capture(ALCdevice* device)
|
||||
}
|
||||
|
||||
snd_pcm_capture_go(data->pcmHandle);
|
||||
|
||||
device->UpdateSize=data->csetup.buf.block.frag_size/
|
||||
(ChannelsFromDevFmt(device->FmtChans)*BytesFromDevFmt(device->FmtType));
|
||||
device->NumUpdates=data->csetup.buf.block.frags;
|
||||
}
|
||||
|
||||
static void qsa_stop_capture(ALCdevice* device)
|
||||
@@ -1073,16 +855,7 @@ static ALCenum qsa_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint s
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static ALint64 qsa_get_latency(ALCdevice* device)
|
||||
{
|
||||
ALint frame_size=FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
|
||||
|
||||
return (ALint64)(device->UpdateSize*device->NumUpdates/frame_size)*
|
||||
1000000000/device->Frequency;
|
||||
}
|
||||
|
||||
BackendFuncs qsa_funcs=
|
||||
{
|
||||
static const BackendFuncs qsa_funcs= {
|
||||
qsa_open_playback,
|
||||
qsa_close_playback,
|
||||
qsa_reset_playback,
|
||||
@@ -1093,69 +866,52 @@ BackendFuncs qsa_funcs=
|
||||
qsa_start_capture,
|
||||
qsa_stop_capture,
|
||||
qsa_capture_samples,
|
||||
qsa_available_samples,
|
||||
qsa_get_latency,
|
||||
qsa_available_samples
|
||||
};
|
||||
|
||||
ALCboolean alc_qsa_init(BackendFuncs* func_list)
|
||||
{
|
||||
*func_list=qsa_funcs;
|
||||
|
||||
*func_list = qsa_funcs;
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
void alc_qsa_deinit(void)
|
||||
{
|
||||
ALuint i;
|
||||
#define FREE_NAME(iter) free((iter)->name)
|
||||
VECTOR_FOR_EACH(DevMap, DeviceNameMap, FREE_NAME);
|
||||
VECTOR_DEINIT(DeviceNameMap);
|
||||
|
||||
for (i=0; i<numDevNames; ++i)
|
||||
{
|
||||
free(allDevNameMap[i].name);
|
||||
}
|
||||
free(allDevNameMap);
|
||||
allDevNameMap=NULL;
|
||||
numDevNames=0;
|
||||
|
||||
for (i=0; i<numCaptureDevNames; ++i)
|
||||
{
|
||||
free(allCaptureDevNameMap[i].name);
|
||||
}
|
||||
free(allCaptureDevNameMap);
|
||||
allCaptureDevNameMap=NULL;
|
||||
numCaptureDevNames=0;
|
||||
VECTOR_FOR_EACH(DevMap, CaptureNameMap, FREE_NAME);
|
||||
VECTOR_DEINIT(CaptureNameMap);
|
||||
#undef FREE_NAME
|
||||
}
|
||||
|
||||
void alc_qsa_probe(enum DevProbe type)
|
||||
{
|
||||
ALuint i;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case ALL_DEVICE_PROBE:
|
||||
for (i=0; i<numDevNames; ++i)
|
||||
{
|
||||
free(allDevNameMap[i].name);
|
||||
}
|
||||
free(allDevNameMap);
|
||||
#define FREE_NAME(iter) free((iter)->name)
|
||||
VECTOR_FOR_EACH(DevMap, DeviceNameMap, FREE_NAME);
|
||||
#undef FREE_NAME
|
||||
VECTOR_RESIZE(DeviceNameMap, 0);
|
||||
|
||||
deviceList(SND_PCM_CHANNEL_PLAYBACK, &DeviceNameMap);
|
||||
#define APPEND_DEVICE(iter) AppendAllDevicesList((iter)->name)
|
||||
VECTOR_FOR_EACH(const DevMap, DeviceNameMap, APPEND_DEVICE);
|
||||
#undef APPEND_DEVICE
|
||||
break;
|
||||
|
||||
allDevNameMap=deviceList(SND_PCM_CHANNEL_PLAYBACK, &numDevNames);
|
||||
for (i=0; i<numDevNames; ++i)
|
||||
{
|
||||
AppendAllDevicesList(allDevNameMap[i].name);
|
||||
}
|
||||
break;
|
||||
case CAPTURE_DEVICE_PROBE:
|
||||
for (i=0; i<numCaptureDevNames; ++i)
|
||||
{
|
||||
free(allCaptureDevNameMap[i].name);
|
||||
}
|
||||
free(allCaptureDevNameMap);
|
||||
#define FREE_NAME(iter) free((iter)->name)
|
||||
VECTOR_FOR_EACH(DevMap, CaptureNameMap, FREE_NAME);
|
||||
#undef FREE_NAME
|
||||
VECTOR_RESIZE(CaptureNameMap, 0);
|
||||
|
||||
allCaptureDevNameMap=deviceList(SND_PCM_CHANNEL_CAPTURE, &numCaptureDevNames);
|
||||
for (i=0; i<numCaptureDevNames; ++i)
|
||||
{
|
||||
AppendCaptureDeviceList(allCaptureDevNameMap[i].name);
|
||||
}
|
||||
break;
|
||||
deviceList(SND_PCM_CHANNEL_CAPTURE, &CaptureNameMap);
|
||||
#define APPEND_DEVICE(iter) AppendCaptureDeviceList((iter)->name)
|
||||
VECTOR_FOR_EACH(const DevMap, CaptureNameMap, APPEND_DEVICE);
|
||||
#undef APPEND_DEVICE
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
*
|
||||
* 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., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
@@ -266,8 +266,7 @@ static const BackendFuncs sndio_funcs = {
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
ALCdevice_GetLatencyDefault
|
||||
NULL
|
||||
};
|
||||
|
||||
ALCboolean alc_sndio_init(BackendFuncs *func_list)
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
*
|
||||
* 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., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
@@ -36,14 +36,14 @@
|
||||
#include "threads.h"
|
||||
#include "compat.h"
|
||||
|
||||
#include "backends/base.h"
|
||||
|
||||
#include <sys/audioio.h>
|
||||
|
||||
|
||||
static const ALCchar solaris_device[] = "Solaris Default";
|
||||
typedef struct ALCsolarisBackend {
|
||||
DERIVE_FROM_TYPE(ALCbackend);
|
||||
|
||||
static const char *solaris_driver = "/dev/audio";
|
||||
|
||||
typedef struct {
|
||||
int fd;
|
||||
|
||||
ALubyte *mix_data;
|
||||
@@ -51,13 +51,58 @@ typedef struct {
|
||||
|
||||
volatile int killNow;
|
||||
althrd_t thread;
|
||||
} solaris_data;
|
||||
} 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, ALint64, getLatency)
|
||||
static DECLARE_FORWARD(ALCsolarisBackend, ALCbackend, void, lock)
|
||||
static DECLARE_FORWARD(ALCsolarisBackend, ALCbackend, void, unlock)
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALCsolarisBackend)
|
||||
|
||||
DEFINE_ALCBACKEND_VTABLE(ALCsolarisBackend);
|
||||
|
||||
|
||||
static int SolarisProc(void *ptr)
|
||||
static const ALCchar solaris_device[] = "Solaris Default";
|
||||
|
||||
static const char *solaris_driver = "/dev/audio";
|
||||
|
||||
|
||||
static void ALCsolarisBackend_Construct(ALCsolarisBackend *self, ALCdevice *device)
|
||||
{
|
||||
ALCdevice *Device = (ALCdevice*)ptr;
|
||||
solaris_data *data = (solaris_data*)Device->ExtraData;
|
||||
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
|
||||
SET_VTABLE2(ALCsolarisBackend, ALCbackend, self);
|
||||
|
||||
self->fd = -1;
|
||||
}
|
||||
|
||||
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;
|
||||
ALint frameSize;
|
||||
int wrote;
|
||||
|
||||
@@ -66,27 +111,27 @@ static int SolarisProc(void *ptr)
|
||||
|
||||
frameSize = FrameSizeFromDevFmt(Device->FmtChans, Device->FmtType);
|
||||
|
||||
while(!data->killNow && Device->Connected)
|
||||
while(!self->killNow && Device->Connected)
|
||||
{
|
||||
ALint len = data->data_size;
|
||||
ALubyte *WritePtr = data->mix_data;
|
||||
ALint len = self->data_size;
|
||||
ALubyte *WritePtr = self->mix_data;
|
||||
|
||||
aluMixData(Device, WritePtr, len/frameSize);
|
||||
while(len > 0 && !data->killNow)
|
||||
while(len > 0 && !self->killNow)
|
||||
{
|
||||
wrote = write(data->fd, WritePtr, len);
|
||||
wrote = write(self->fd, WritePtr, len);
|
||||
if(wrote < 0)
|
||||
{
|
||||
if(errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR)
|
||||
{
|
||||
ERR("write failed: %s\n", strerror(errno));
|
||||
ALCdevice_Lock(Device);
|
||||
ALCsolarisBackend_lock(self);
|
||||
aluHandleDisconnect(Device);
|
||||
ALCdevice_Unlock(Device);
|
||||
ALCsolarisBackend_unlock(self);
|
||||
break;
|
||||
}
|
||||
|
||||
al_nssleep(0, 1000000);
|
||||
al_nssleep(1000000);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -99,43 +144,37 @@ static int SolarisProc(void *ptr)
|
||||
}
|
||||
|
||||
|
||||
static ALCenum solaris_open_playback(ALCdevice *device, const ALCchar *deviceName)
|
||||
static ALCenum ALCsolarisBackend_open(ALCsolarisBackend *self, const ALCchar *name)
|
||||
{
|
||||
solaris_data *data;
|
||||
ALCdevice *device;
|
||||
|
||||
if(!deviceName)
|
||||
deviceName = solaris_device;
|
||||
else if(strcmp(deviceName, solaris_device) != 0)
|
||||
if(!name)
|
||||
name = solaris_device;
|
||||
else if(strcmp(name, solaris_device) != 0)
|
||||
return ALC_INVALID_VALUE;
|
||||
|
||||
data = (solaris_data*)calloc(1, sizeof(solaris_data));
|
||||
data->killNow = 0;
|
||||
|
||||
data->fd = open(solaris_driver, O_WRONLY);
|
||||
if(data->fd == -1)
|
||||
self->fd = open(solaris_driver, O_WRONLY);
|
||||
if(self->fd == -1)
|
||||
{
|
||||
free(data);
|
||||
ERR("Could not open %s: %s\n", solaris_driver, strerror(errno));
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
al_string_copy_cstr(&device->DeviceName, deviceName);
|
||||
device->ExtraData = data;
|
||||
device = STATIC_CAST(ALCbackend,self)->mDevice;
|
||||
al_string_copy_cstr(&device->DeviceName, name);
|
||||
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static void solaris_close_playback(ALCdevice *device)
|
||||
static void ALCsolarisBackend_close(ALCsolarisBackend *self)
|
||||
{
|
||||
solaris_data *data = (solaris_data*)device->ExtraData;
|
||||
|
||||
close(data->fd);
|
||||
free(data);
|
||||
device->ExtraData = NULL;
|
||||
close(self->fd);
|
||||
self->fd = -1;
|
||||
}
|
||||
|
||||
static ALCboolean solaris_reset_playback(ALCdevice *device)
|
||||
static ALCboolean ALCsolarisBackend_reset(ALCsolarisBackend *self)
|
||||
{
|
||||
solaris_data *data = (solaris_data*)device->ExtraData;
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
|
||||
audio_info_t info;
|
||||
ALuint frameSize;
|
||||
int numChannels;
|
||||
@@ -174,7 +213,7 @@ static ALCboolean solaris_reset_playback(ALCdevice *device)
|
||||
frameSize = numChannels * BytesFromDevFmt(device->FmtType);
|
||||
info.play.buffer_size = device->UpdateSize*device->NumUpdates * frameSize;
|
||||
|
||||
if(ioctl(data->fd, AUDIO_SETINFO, &info) < 0)
|
||||
if(ioctl(self->fd, AUDIO_SETINFO, &info) < 0)
|
||||
{
|
||||
ERR("ioctl failed: %s\n", strerror(errno));
|
||||
return ALC_FALSE;
|
||||
@@ -201,74 +240,72 @@ static ALCboolean solaris_reset_playback(ALCdevice *device)
|
||||
|
||||
SetDefaultChannelOrder(device);
|
||||
|
||||
free(self->mix_data);
|
||||
self->data_size = device->UpdateSize * FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
|
||||
self->mix_data = calloc(1, self->data_size);
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static ALCboolean solaris_start_playback(ALCdevice *device)
|
||||
static ALCboolean ALCsolarisBackend_start(ALCsolarisBackend *self)
|
||||
{
|
||||
solaris_data *data = (solaris_data*)device->ExtraData;
|
||||
|
||||
data->data_size = device->UpdateSize * FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
|
||||
data->mix_data = calloc(1, data->data_size);
|
||||
|
||||
data->killNow = 0;
|
||||
if(althrd_create(&data->thread, SolarisProc, device) != althrd_success)
|
||||
{
|
||||
free(data->mix_data);
|
||||
data->mix_data = NULL;
|
||||
self->killNow = 0;
|
||||
if(althrd_create(&self->thread, ALCsolarisBackend_mixerProc, self) != althrd_success)
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static void solaris_stop_playback(ALCdevice *device)
|
||||
static void ALCsolarisBackend_stop(ALCsolarisBackend *self)
|
||||
{
|
||||
solaris_data *data = (solaris_data*)device->ExtraData;
|
||||
int res;
|
||||
|
||||
if(data->killNow)
|
||||
if(self->killNow)
|
||||
return;
|
||||
|
||||
data->killNow = 1;
|
||||
althrd_join(data->thread, &res);
|
||||
self->killNow = 1;
|
||||
althrd_join(self->thread, &res);
|
||||
|
||||
if(ioctl(data->fd, AUDIO_DRAIN) < 0)
|
||||
if(ioctl(self->fd, AUDIO_DRAIN) < 0)
|
||||
ERR("Error draining device: %s\n", strerror(errno));
|
||||
|
||||
free(data->mix_data);
|
||||
data->mix_data = NULL;
|
||||
}
|
||||
|
||||
|
||||
static const BackendFuncs solaris_funcs = {
|
||||
solaris_open_playback,
|
||||
solaris_close_playback,
|
||||
solaris_reset_playback,
|
||||
solaris_start_playback,
|
||||
solaris_stop_playback,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
ALCdevice_GetLatencyDefault
|
||||
};
|
||||
typedef struct ALCsolarisBackendFactory {
|
||||
DERIVE_FROM_TYPE(ALCbackendFactory);
|
||||
} ALCsolarisBackendFactory;
|
||||
#define ALCSOLARISBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCsolarisBackendFactory, ALCbackendFactory) } }
|
||||
|
||||
ALCboolean alc_solaris_init(BackendFuncs *func_list)
|
||||
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)
|
||||
{
|
||||
ConfigValueStr("solaris", "device", &solaris_driver);
|
||||
static ALCsolarisBackendFactory factory = ALCSOLARISBACKENDFACTORY_INITIALIZER;
|
||||
return STATIC_CAST(ALCbackendFactory, &factory);
|
||||
}
|
||||
|
||||
*func_list = solaris_funcs;
|
||||
|
||||
static ALCboolean ALCsolarisBackendFactory_init(ALCsolarisBackendFactory* UNUSED(self))
|
||||
{
|
||||
ConfigValueStr(NULL, "solaris", "device", &solaris_driver);
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
void alc_solaris_deinit(void)
|
||||
static ALCboolean ALCsolarisBackendFactory_querySupport(ALCsolarisBackendFactory* UNUSED(self), ALCbackend_Type type)
|
||||
{
|
||||
if(type == ALCbackend_Playback)
|
||||
return ALC_TRUE;
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
void alc_solaris_probe(enum DevProbe type)
|
||||
static void ALCsolarisBackendFactory_probe(ALCsolarisBackendFactory* UNUSED(self), enum DevProbe type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
@@ -286,3 +323,16 @@ void alc_solaris_probe(enum DevProbe type)
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
*
|
||||
* 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., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
@@ -24,26 +24,13 @@
|
||||
#include <stdio.h>
|
||||
#include <memory.h>
|
||||
#include <errno.h>
|
||||
#ifdef HAVE_WINDOWS_H
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alu.h"
|
||||
#include "threads.h"
|
||||
#include "compat.h"
|
||||
|
||||
|
||||
typedef struct {
|
||||
FILE *f;
|
||||
long DataStart;
|
||||
|
||||
ALvoid *buffer;
|
||||
ALuint size;
|
||||
|
||||
volatile int killNow;
|
||||
althrd_t thread;
|
||||
} wave_data;
|
||||
#include "backends/base.h"
|
||||
|
||||
|
||||
static const ALCchar waveDevice[] = "Wave File Writer";
|
||||
@@ -57,18 +44,15 @@ static const ALubyte SUBTYPE_FLOAT[] = {
|
||||
0x00, 0x38, 0x9b, 0x71
|
||||
};
|
||||
|
||||
static const ALuint channel_masks[] = {
|
||||
0, /* invalid */
|
||||
0x4, /* Mono */
|
||||
0x1 | 0x2, /* Stereo */
|
||||
0, /* 3 channel */
|
||||
0x1 | 0x2 | 0x10 | 0x20, /* Quad */
|
||||
0, /* 5 channel */
|
||||
0x1 | 0x2 | 0x4 | 0x8 | 0x10 | 0x20, /* 5.1 */
|
||||
0x1 | 0x2 | 0x4 | 0x8 | 0x100 | 0x200 | 0x400, /* 6.1 */
|
||||
0x1 | 0x2 | 0x4 | 0x8 | 0x10 | 0x20 | 0x200 | 0x400, /* 7.1 */
|
||||
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)
|
||||
{
|
||||
@@ -85,10 +69,57 @@ static void fwrite32le(ALuint val, FILE *f)
|
||||
}
|
||||
|
||||
|
||||
static int WaveProc(void *ptr)
|
||||
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, ALint64, getLatency)
|
||||
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)
|
||||
{
|
||||
ALCdevice *device = (ALCdevice*)ptr;
|
||||
wave_data *data = (wave_data*)device->ExtraData;
|
||||
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;
|
||||
@@ -106,7 +137,7 @@ static int WaveProc(void *ptr)
|
||||
ERR("Failed to get starting time\n");
|
||||
return 1;
|
||||
}
|
||||
while(!data->killNow && device->Connected)
|
||||
while(!self->killNow && device->Connected)
|
||||
{
|
||||
if(altimespec_get(&now, AL_TIME_UTC) != AL_TIME_UTC)
|
||||
{
|
||||
@@ -125,41 +156,41 @@ static int WaveProc(void *ptr)
|
||||
}
|
||||
|
||||
if(avail-done < device->UpdateSize)
|
||||
al_nssleep(0, restTime);
|
||||
al_nssleep(restTime);
|
||||
else while(avail-done >= device->UpdateSize)
|
||||
{
|
||||
aluMixData(device, data->buffer, device->UpdateSize);
|
||||
aluMixData(device, self->mBuffer, device->UpdateSize);
|
||||
done += device->UpdateSize;
|
||||
|
||||
if(!IS_LITTLE_ENDIAN)
|
||||
{
|
||||
ALuint bytesize = BytesFromDevFmt(device->FmtType);
|
||||
ALubyte *bytes = data->buffer;
|
||||
ALubyte *bytes = self->mBuffer;
|
||||
ALuint i;
|
||||
|
||||
if(bytesize == 1)
|
||||
{
|
||||
for(i = 0;i < data->size;i++)
|
||||
fputc(bytes[i], data->f);
|
||||
for(i = 0;i < self->mSize;i++)
|
||||
fputc(bytes[i], self->mFile);
|
||||
}
|
||||
else if(bytesize == 2)
|
||||
{
|
||||
for(i = 0;i < data->size;i++)
|
||||
fputc(bytes[i^1], data->f);
|
||||
for(i = 0;i < self->mSize;i++)
|
||||
fputc(bytes[i^1], self->mFile);
|
||||
}
|
||||
else if(bytesize == 4)
|
||||
{
|
||||
for(i = 0;i < data->size;i++)
|
||||
fputc(bytes[i^3], data->f);
|
||||
for(i = 0;i < self->mSize;i++)
|
||||
fputc(bytes[i^3], self->mFile);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fs = fwrite(data->buffer, frameSize, device->UpdateSize,
|
||||
data->f);
|
||||
fs = fwrite(self->mBuffer, frameSize, device->UpdateSize,
|
||||
self->mFile);
|
||||
(void)fs;
|
||||
}
|
||||
if(ferror(data->f))
|
||||
if(ferror(self->mFile))
|
||||
{
|
||||
ERR("Error writing to file\n");
|
||||
ALCdevice_Lock(device);
|
||||
@@ -173,52 +204,52 @@ static int WaveProc(void *ptr)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ALCenum wave_open_playback(ALCdevice *device, const ALCchar *deviceName)
|
||||
|
||||
static ALCenum ALCwaveBackend_open(ALCwaveBackend *self, const ALCchar *name)
|
||||
{
|
||||
wave_data *data;
|
||||
ALCdevice *device;
|
||||
const char *fname;
|
||||
|
||||
fname = GetConfigValue("wave", "file", "");
|
||||
if(!fname[0])
|
||||
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;
|
||||
|
||||
if(!deviceName)
|
||||
deviceName = waveDevice;
|
||||
else if(strcmp(deviceName, waveDevice) != 0)
|
||||
return ALC_INVALID_VALUE;
|
||||
|
||||
data = (wave_data*)calloc(1, sizeof(wave_data));
|
||||
|
||||
data->f = al_fopen(fname, "wb");
|
||||
if(!data->f)
|
||||
self->mFile = al_fopen(fname, "wb");
|
||||
if(!self->mFile)
|
||||
{
|
||||
free(data);
|
||||
ERR("Could not open file '%s': %s\n", fname, strerror(errno));
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
al_string_copy_cstr(&device->DeviceName, deviceName);
|
||||
device->ExtraData = data;
|
||||
device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
al_string_copy_cstr(&device->DeviceName, name);
|
||||
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static void wave_close_playback(ALCdevice *device)
|
||||
static void ALCwaveBackend_close(ALCwaveBackend *self)
|
||||
{
|
||||
wave_data *data = (wave_data*)device->ExtraData;
|
||||
|
||||
fclose(data->f);
|
||||
free(data);
|
||||
device->ExtraData = NULL;
|
||||
if(self->mFile)
|
||||
fclose(self->mFile);
|
||||
self->mFile = NULL;
|
||||
}
|
||||
|
||||
static ALCboolean wave_reset_playback(ALCdevice *device)
|
||||
static ALCboolean ALCwaveBackend_reset(ALCwaveBackend *self)
|
||||
{
|
||||
wave_data *data = (wave_data*)device->ExtraData;
|
||||
ALuint channels=0, bits=0;
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
ALuint channels=0, bits=0, chanmask=0;
|
||||
int isbformat = 0;
|
||||
size_t val;
|
||||
|
||||
fseek(data->f, 0, SEEK_SET);
|
||||
clearerr(data->f);
|
||||
fseek(self->mFile, 0, SEEK_SET);
|
||||
clearerr(self->mFile);
|
||||
|
||||
if(GetConfigValueBool(NULL, "wave", "bformat", 0))
|
||||
device->FmtChans = DevFmtBFormat3D;
|
||||
|
||||
switch(device->FmtType)
|
||||
{
|
||||
@@ -237,135 +268,156 @@ static ALCboolean wave_reset_playback(ALCdevice *device)
|
||||
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 DevFmtBFormat3D:
|
||||
isbformat = 1;
|
||||
chanmask = 0;
|
||||
break;
|
||||
}
|
||||
bits = BytesFromDevFmt(device->FmtType) * 8;
|
||||
channels = ChannelsFromDevFmt(device->FmtChans);
|
||||
|
||||
fprintf(data->f, "RIFF");
|
||||
fwrite32le(0xFFFFFFFF, data->f); // 'RIFF' header len; filled in at close
|
||||
fprintf(self->mFile, "RIFF");
|
||||
fwrite32le(0xFFFFFFFF, self->mFile); // 'RIFF' header len; filled in at close
|
||||
|
||||
fprintf(data->f, "WAVE");
|
||||
fprintf(self->mFile, "WAVE");
|
||||
|
||||
fprintf(data->f, "fmt ");
|
||||
fwrite32le(40, data->f); // 'fmt ' header len; 40 bytes for EXTENSIBLE
|
||||
fprintf(self->mFile, "fmt ");
|
||||
fwrite32le(40, self->mFile); // 'fmt ' header len; 40 bytes for EXTENSIBLE
|
||||
|
||||
// 16-bit val, format type id (extensible: 0xFFFE)
|
||||
fwrite16le(0xFFFE, data->f);
|
||||
fwrite16le(0xFFFE, self->mFile);
|
||||
// 16-bit val, channel count
|
||||
fwrite16le(channels, data->f);
|
||||
fwrite16le(channels, self->mFile);
|
||||
// 32-bit val, frequency
|
||||
fwrite32le(device->Frequency, data->f);
|
||||
fwrite32le(device->Frequency, self->mFile);
|
||||
// 32-bit val, bytes per second
|
||||
fwrite32le(device->Frequency * channels * bits / 8, data->f);
|
||||
fwrite32le(device->Frequency * channels * bits / 8, self->mFile);
|
||||
// 16-bit val, frame size
|
||||
fwrite16le(channels * bits / 8, data->f);
|
||||
fwrite16le(channels * bits / 8, self->mFile);
|
||||
// 16-bit val, bits per sample
|
||||
fwrite16le(bits, data->f);
|
||||
fwrite16le(bits, self->mFile);
|
||||
// 16-bit val, extra byte count
|
||||
fwrite16le(22, data->f);
|
||||
fwrite16le(22, self->mFile);
|
||||
// 16-bit val, valid bits per sample
|
||||
fwrite16le(bits, data->f);
|
||||
fwrite16le(bits, self->mFile);
|
||||
// 32-bit val, channel mask
|
||||
fwrite32le(channel_masks[channels], data->f);
|
||||
fwrite32le(chanmask, self->mFile);
|
||||
// 16 byte GUID, sub-type format
|
||||
val = fwrite(((bits==32) ? SUBTYPE_FLOAT : SUBTYPE_PCM), 1, 16, data->f);
|
||||
val = fwrite(((bits==32) ? (isbformat ? SUBTYPE_BFORMAT_FLOAT : SUBTYPE_FLOAT) :
|
||||
(isbformat ? SUBTYPE_BFORMAT_PCM : SUBTYPE_PCM)), 1, 16, self->mFile);
|
||||
(void)val;
|
||||
|
||||
fprintf(data->f, "data");
|
||||
fwrite32le(0xFFFFFFFF, data->f); // 'data' header len; filled in at close
|
||||
fprintf(self->mFile, "data");
|
||||
fwrite32le(0xFFFFFFFF, self->mFile); // 'data' header len; filled in at close
|
||||
|
||||
if(ferror(data->f))
|
||||
if(ferror(self->mFile))
|
||||
{
|
||||
ERR("Error writing header: %s\n", strerror(errno));
|
||||
return ALC_FALSE;
|
||||
}
|
||||
data->DataStart = ftell(data->f);
|
||||
self->mDataStart = ftell(self->mFile);
|
||||
|
||||
SetDefaultWFXChannelOrder(device);
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static ALCboolean wave_start_playback(ALCdevice *device)
|
||||
static ALCboolean ALCwaveBackend_start(ALCwaveBackend *self)
|
||||
{
|
||||
wave_data *data = (wave_data*)device->ExtraData;
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
|
||||
data->size = device->UpdateSize * FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
|
||||
data->buffer = malloc(data->size);
|
||||
if(!data->buffer)
|
||||
self->mSize = device->UpdateSize * FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
|
||||
self->mBuffer = malloc(self->mSize);
|
||||
if(!self->mBuffer)
|
||||
{
|
||||
ERR("Buffer malloc failed\n");
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
data->killNow = 0;
|
||||
if(althrd_create(&data->thread, WaveProc, device) != althrd_success)
|
||||
self->killNow = 0;
|
||||
if(althrd_create(&self->thread, ALCwaveBackend_mixerProc, self) != althrd_success)
|
||||
{
|
||||
free(data->buffer);
|
||||
data->buffer = NULL;
|
||||
free(self->mBuffer);
|
||||
self->mBuffer = NULL;
|
||||
self->mSize = 0;
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static void wave_stop_playback(ALCdevice *device)
|
||||
static void ALCwaveBackend_stop(ALCwaveBackend *self)
|
||||
{
|
||||
wave_data *data = (wave_data*)device->ExtraData;
|
||||
ALuint dataLen;
|
||||
long size;
|
||||
int res;
|
||||
|
||||
if(data->killNow)
|
||||
if(self->killNow)
|
||||
return;
|
||||
|
||||
data->killNow = 1;
|
||||
althrd_join(data->thread, &res);
|
||||
self->killNow = 1;
|
||||
althrd_join(self->thread, &res);
|
||||
|
||||
free(data->buffer);
|
||||
data->buffer = NULL;
|
||||
free(self->mBuffer);
|
||||
self->mBuffer = NULL;
|
||||
|
||||
size = ftell(data->f);
|
||||
size = ftell(self->mFile);
|
||||
if(size > 0)
|
||||
{
|
||||
dataLen = size - data->DataStart;
|
||||
if(fseek(data->f, data->DataStart-4, SEEK_SET) == 0)
|
||||
fwrite32le(dataLen, data->f); // 'data' header len
|
||||
if(fseek(data->f, 4, SEEK_SET) == 0)
|
||||
fwrite32le(size-8, data->f); // 'WAVE' header len
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static const BackendFuncs wave_funcs = {
|
||||
wave_open_playback,
|
||||
wave_close_playback,
|
||||
wave_reset_playback,
|
||||
wave_start_playback,
|
||||
wave_stop_playback,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
ALCdevice_GetLatencyDefault
|
||||
};
|
||||
typedef struct ALCwaveBackendFactory {
|
||||
DERIVE_FROM_TYPE(ALCbackendFactory);
|
||||
} ALCwaveBackendFactory;
|
||||
#define ALCWAVEBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCwaveBackendFactory, ALCbackendFactory) } }
|
||||
|
||||
ALCboolean alc_wave_init(BackendFuncs *func_list)
|
||||
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))
|
||||
{
|
||||
*func_list = wave_funcs;
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
void alc_wave_deinit(void)
|
||||
static ALCboolean ALCwaveBackendFactory_querySupport(ALCwaveBackendFactory* UNUSED(self), ALCbackend_Type type)
|
||||
{
|
||||
if(type == ALCbackend_Playback)
|
||||
return !!ConfigValueExists(NULL, "wave", "file");
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
void alc_wave_probe(enum DevProbe type)
|
||||
static void ALCwaveBackendFactory_probe(ALCwaveBackendFactory* UNUSED(self), enum DevProbe type)
|
||||
{
|
||||
if(!ConfigValueExists("wave", "file"))
|
||||
return;
|
||||
|
||||
switch(type)
|
||||
{
|
||||
case ALL_DEVICE_PROBE:
|
||||
@@ -375,3 +427,16 @@ void alc_wave_probe(enum DevProbe type)
|
||||
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;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -29,9 +29,6 @@
|
||||
#include "bs2b.h"
|
||||
#include "alu.h"
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.14159265358979323846
|
||||
#endif
|
||||
|
||||
/* Set up all data. */
|
||||
static void init(struct bs2b *bs2b)
|
||||
@@ -40,8 +37,6 @@ static void init(struct bs2b *bs2b)
|
||||
float G_lo, G_hi;
|
||||
float x, g;
|
||||
|
||||
bs2b->srate = clampi(bs2b->srate, 2000, 192000);
|
||||
|
||||
switch(bs2b->level)
|
||||
{
|
||||
case BS2B_LOW_CLEVEL: /* Low crossfeed level */
|
||||
@@ -105,31 +100,25 @@ static void init(struct bs2b *bs2b)
|
||||
bs2b->a1_hi = -x * g;
|
||||
} /* init */
|
||||
|
||||
|
||||
/* Exported functions.
|
||||
* See descriptions in "bs2b.h"
|
||||
*/
|
||||
|
||||
void bs2b_set_level(struct bs2b *bs2b, int level)
|
||||
void bs2b_set_params(struct bs2b *bs2b, int level, int srate)
|
||||
{
|
||||
if(level == bs2b->level)
|
||||
return;
|
||||
if(srate <= 0) srate = 1;
|
||||
|
||||
bs2b->level = level;
|
||||
bs2b->srate = srate;
|
||||
init(bs2b);
|
||||
} /* bs2b_set_level */
|
||||
} /* bs2b_set_params */
|
||||
|
||||
int bs2b_get_level(struct bs2b *bs2b)
|
||||
{
|
||||
return bs2b->level;
|
||||
} /* bs2b_get_level */
|
||||
|
||||
void bs2b_set_srate(struct bs2b *bs2b, int srate)
|
||||
{
|
||||
if (srate == bs2b->srate)
|
||||
return;
|
||||
bs2b->srate = srate;
|
||||
init(bs2b);
|
||||
} /* bs2b_set_srate */
|
||||
|
||||
int bs2b_get_srate(struct bs2b *bs2b)
|
||||
{
|
||||
return bs2b->srate;
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
*
|
||||
* 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., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
@@ -31,15 +31,15 @@
|
||||
/* Auto-wah is simply a low-pass filter with a cutoff frequency that shifts up
|
||||
* or down depending on the input signal, and a resonant peak at the cutoff.
|
||||
*
|
||||
* Currently, we assume a cutoff frequency range of 500hz (no amplitude) to
|
||||
* 3khz (peak gain). Peak gain is assumed to be in normalized scale.
|
||||
* Currently, we assume a cutoff frequency range of 20hz (no amplitude) to
|
||||
* 20khz (peak gain). Peak gain is assumed to be in normalized scale.
|
||||
*/
|
||||
|
||||
typedef struct ALautowahState {
|
||||
DERIVE_FROM_TYPE(ALeffectState);
|
||||
|
||||
/* Effect gains for each channel */
|
||||
ALfloat Gain[MaxChannels];
|
||||
ALfloat Gain[MAX_OUTPUT_CHANNELS];
|
||||
|
||||
/* Effect parameters */
|
||||
ALfloat AttackRate;
|
||||
@@ -66,7 +66,6 @@ static ALboolean ALautowahState_deviceUpdate(ALautowahState *state, ALCdevice *d
|
||||
static ALvoid ALautowahState_update(ALautowahState *state, ALCdevice *device, const ALeffectslot *slot)
|
||||
{
|
||||
ALfloat attackTime, releaseTime;
|
||||
ALfloat gain;
|
||||
|
||||
attackTime = slot->EffectProps.Autowah.AttackTime * state->Frequency;
|
||||
releaseTime = slot->EffectProps.Autowah.ReleaseTime * state->Frequency;
|
||||
@@ -76,19 +75,18 @@ static ALvoid ALautowahState_update(ALautowahState *state, ALCdevice *device, co
|
||||
state->PeakGain = slot->EffectProps.Autowah.PeakGain;
|
||||
state->Resonance = slot->EffectProps.Autowah.Resonance;
|
||||
|
||||
gain = sqrtf(1.0f / device->NumChan) * slot->Gain;
|
||||
SetGains(device, gain, state->Gain);
|
||||
ComputeAmbientGains(device, slot->Gain, state->Gain);
|
||||
}
|
||||
|
||||
static ALvoid ALautowahState_process(ALautowahState *state, ALuint SamplesToDo, const ALfloat *SamplesIn, ALfloat (*SamplesOut)[BUFFERSIZE])
|
||||
static ALvoid ALautowahState_process(ALautowahState *state, ALuint SamplesToDo, const ALfloat *SamplesIn, ALfloat (*SamplesOut)[BUFFERSIZE], ALuint NumChannels)
|
||||
{
|
||||
ALuint it, kt;
|
||||
ALuint base;
|
||||
|
||||
for(base = 0;base < SamplesToDo;)
|
||||
{
|
||||
ALfloat temps[64];
|
||||
ALuint td = minu(SamplesToDo-base, 64);
|
||||
ALfloat temps[256];
|
||||
ALuint td = minu(256, SamplesToDo-base);
|
||||
ALfloat gain = state->GainCtrl;
|
||||
|
||||
for(it = 0;it < td;it++)
|
||||
@@ -114,7 +112,7 @@ static ALvoid ALautowahState_process(ALautowahState *state, ALuint SamplesToDo,
|
||||
* ALfilterType_LowPass. However, instead of passing a bandwidth,
|
||||
* we use the resonance property for Q. This also inlines the call.
|
||||
*/
|
||||
w0 = F_2PI * cutoff / state->Frequency;
|
||||
w0 = F_TAU * cutoff / state->Frequency;
|
||||
|
||||
/* FIXME: Resonance controls the resonant peak, or Q. How? Not sure
|
||||
* that Q = resonance*0.1. */
|
||||
@@ -137,10 +135,10 @@ static ALvoid ALautowahState_process(ALautowahState *state, ALuint SamplesToDo,
|
||||
}
|
||||
state->GainCtrl = gain;
|
||||
|
||||
for(kt = 0;kt < MaxChannels;kt++)
|
||||
for(kt = 0;kt < NumChannels;kt++)
|
||||
{
|
||||
ALfloat gain = state->Gain[kt];
|
||||
if(!(gain > GAIN_SILENCE_THRESHOLD))
|
||||
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
|
||||
continue;
|
||||
|
||||
for(it = 0;it < td;it++)
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
*
|
||||
* 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., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
@@ -46,7 +46,7 @@ typedef struct ALchorusState {
|
||||
ALint lfo_disp;
|
||||
|
||||
/* Gains for left and right sides */
|
||||
ALfloat Gain[2][MaxChannels];
|
||||
ALfloat Gain[2][MAX_OUTPUT_CHANNELS];
|
||||
|
||||
/* effect parameters */
|
||||
enum ChorusWaveForm waveform;
|
||||
@@ -93,6 +93,8 @@ static ALboolean ALchorusState_deviceUpdate(ALchorusState *state, ALCdevice *Dev
|
||||
|
||||
static ALvoid ALchorusState_update(ALchorusState *state, ALCdevice *Device, const ALeffectslot *Slot)
|
||||
{
|
||||
static const ALfloat left_dir[3] = { -1.0f, 0.0f, 0.0f };
|
||||
static const ALfloat right_dir[3] = { 1.0f, 0.0f, 0.0f };
|
||||
ALfloat frequency = (ALfloat)Device->Frequency;
|
||||
ALfloat rate;
|
||||
ALint phase;
|
||||
@@ -111,8 +113,8 @@ static ALvoid ALchorusState_update(ALchorusState *state, ALCdevice *Device, cons
|
||||
state->delay = fastf2i(Slot->EffectProps.Chorus.Delay * frequency);
|
||||
|
||||
/* Gains for left and right sides */
|
||||
ComputeAngleGains(Device, atan2f(-1.0f, 0.0f), 0.0f, Slot->Gain, state->Gain[0]);
|
||||
ComputeAngleGains(Device, atan2f(+1.0f, 0.0f), 0.0f, Slot->Gain, state->Gain[1]);
|
||||
ComputeDirectionalGains(Device, left_dir, Slot->Gain, state->Gain[0]);
|
||||
ComputeDirectionalGains(Device, right_dir, Slot->Gain, state->Gain[1]);
|
||||
|
||||
phase = Slot->EffectProps.Chorus.Phase;
|
||||
rate = Slot->EffectProps.Chorus.Rate;
|
||||
@@ -132,7 +134,7 @@ static ALvoid ALchorusState_update(ALchorusState *state, ALCdevice *Device, cons
|
||||
state->lfo_scale = 4.0f / state->lfo_range;
|
||||
break;
|
||||
case CWF_Sinusoid:
|
||||
state->lfo_scale = F_2PI / state->lfo_range;
|
||||
state->lfo_scale = F_TAU / state->lfo_range;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -201,15 +203,15 @@ DECL_TEMPLATE(Sinusoid)
|
||||
|
||||
#undef DECL_TEMPLATE
|
||||
|
||||
static ALvoid ALchorusState_process(ALchorusState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE])
|
||||
static ALvoid ALchorusState_process(ALchorusState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels)
|
||||
{
|
||||
ALuint it, kt;
|
||||
ALuint base;
|
||||
|
||||
for(base = 0;base < SamplesToDo;)
|
||||
{
|
||||
ALfloat temps[64][2];
|
||||
ALuint td = minu(SamplesToDo-base, 64);
|
||||
ALfloat temps[128][2];
|
||||
ALuint td = minu(128, SamplesToDo-base);
|
||||
|
||||
switch(state->waveform)
|
||||
{
|
||||
@@ -221,17 +223,17 @@ static ALvoid ALchorusState_process(ALchorusState *state, ALuint SamplesToDo, co
|
||||
break;
|
||||
}
|
||||
|
||||
for(kt = 0;kt < MaxChannels;kt++)
|
||||
for(kt = 0;kt < NumChannels;kt++)
|
||||
{
|
||||
ALfloat gain = state->Gain[0][kt];
|
||||
if(gain > GAIN_SILENCE_THRESHOLD)
|
||||
if(fabsf(gain) > GAIN_SILENCE_THRESHOLD)
|
||||
{
|
||||
for(it = 0;it < td;it++)
|
||||
SamplesOut[kt][it+base] += temps[it][0] * gain;
|
||||
}
|
||||
|
||||
gain = state->Gain[1][kt];
|
||||
if(gain > GAIN_SILENCE_THRESHOLD)
|
||||
if(fabsf(gain) > GAIN_SILENCE_THRESHOLD)
|
||||
{
|
||||
for(it = 0;it < td;it++)
|
||||
SamplesOut[kt][it+base] += temps[it][1] * gain;
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
*
|
||||
* 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., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
@@ -31,7 +31,7 @@ typedef struct ALcompressorState {
|
||||
DERIVE_FROM_TYPE(ALeffectState);
|
||||
|
||||
/* Effect gains for each channel */
|
||||
ALfloat Gain[MaxChannels];
|
||||
ALfloat Gain[MAX_OUTPUT_CHANNELS];
|
||||
|
||||
/* Effect parameters */
|
||||
ALboolean Enabled;
|
||||
@@ -55,25 +55,22 @@ static ALboolean ALcompressorState_deviceUpdate(ALcompressorState *state, ALCdev
|
||||
return AL_TRUE;
|
||||
}
|
||||
|
||||
static ALvoid ALcompressorState_update(ALcompressorState *state, ALCdevice *Device, const ALeffectslot *Slot)
|
||||
static ALvoid ALcompressorState_update(ALcompressorState *state, ALCdevice *device, const ALeffectslot *slot)
|
||||
{
|
||||
ALfloat gain;
|
||||
state->Enabled = slot->EffectProps.Compressor.OnOff;
|
||||
|
||||
state->Enabled = Slot->EffectProps.Compressor.OnOff;
|
||||
|
||||
gain = sqrtf(1.0f / Device->NumChan) * Slot->Gain;
|
||||
SetGains(Device, gain, state->Gain);
|
||||
ComputeAmbientGains(device, slot->Gain, state->Gain);
|
||||
}
|
||||
|
||||
static ALvoid ALcompressorState_process(ALcompressorState *state, ALuint SamplesToDo, const ALfloat *SamplesIn, ALfloat (*SamplesOut)[BUFFERSIZE])
|
||||
static ALvoid ALcompressorState_process(ALcompressorState *state, ALuint SamplesToDo, const ALfloat *SamplesIn, ALfloat (*SamplesOut)[BUFFERSIZE], ALuint NumChannels)
|
||||
{
|
||||
ALuint it, kt;
|
||||
ALuint base;
|
||||
|
||||
for(base = 0;base < SamplesToDo;)
|
||||
{
|
||||
ALfloat temps[64];
|
||||
ALuint td = minu(SamplesToDo-base, 64);
|
||||
ALfloat temps[256];
|
||||
ALuint td = minu(256, SamplesToDo-base);
|
||||
|
||||
if(state->Enabled)
|
||||
{
|
||||
@@ -119,10 +116,10 @@ static ALvoid ALcompressorState_process(ALcompressorState *state, ALuint Samples
|
||||
}
|
||||
|
||||
|
||||
for(kt = 0;kt < MaxChannels;kt++)
|
||||
for(kt = 0;kt < NumChannels;kt++)
|
||||
{
|
||||
ALfloat gain = state->Gain[kt];
|
||||
if(!(gain > GAIN_SILENCE_THRESHOLD))
|
||||
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
|
||||
continue;
|
||||
|
||||
for(it = 0;it < td;it++)
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
*
|
||||
* 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., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
typedef struct ALdedicatedState {
|
||||
DERIVE_FROM_TYPE(ALeffectState);
|
||||
|
||||
ALfloat gains[MaxChannels];
|
||||
ALfloat gains[MAX_OUTPUT_CHANNELS];
|
||||
} ALdedicatedState;
|
||||
|
||||
|
||||
@@ -48,27 +48,41 @@ static ALboolean ALdedicatedState_deviceUpdate(ALdedicatedState *UNUSED(state),
|
||||
static ALvoid ALdedicatedState_update(ALdedicatedState *state, ALCdevice *device, const ALeffectslot *Slot)
|
||||
{
|
||||
ALfloat Gain;
|
||||
ALsizei s;
|
||||
ALuint i;
|
||||
|
||||
for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
|
||||
state->gains[i] = 0.0f;
|
||||
|
||||
Gain = Slot->Gain * Slot->EffectProps.Dedicated.Gain;
|
||||
if(Slot->EffectType == AL_EFFECT_DEDICATED_DIALOGUE)
|
||||
ComputeAngleGains(device, atan2f(0.0f, 1.0f), 0.0f, Gain, state->gains);
|
||||
else if(Slot->EffectType == AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT)
|
||||
if(Slot->EffectType == AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT)
|
||||
{
|
||||
for(s = 0;s < MaxChannels;s++)
|
||||
state->gains[s] = 0.0f;
|
||||
state->gains[LFE] = Gain;
|
||||
int idx;
|
||||
if((idx=GetChannelIdxByName(device, LFE)) != -1)
|
||||
state->gains[idx] = Gain;
|
||||
}
|
||||
else if(Slot->EffectType == AL_EFFECT_DEDICATED_DIALOGUE)
|
||||
{
|
||||
int idx;
|
||||
/* Dialog goes to the front-center speaker if it exists, otherwise it
|
||||
* plays from the front-center location. */
|
||||
if((idx=GetChannelIdxByName(device, FrontCenter)) != -1)
|
||||
state->gains[idx] = Gain;
|
||||
else
|
||||
{
|
||||
static const ALfloat front_dir[3] = { 0.0f, 0.0f, -1.0f };
|
||||
ComputeDirectionalGains(device, front_dir, Gain, state->gains);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static ALvoid ALdedicatedState_process(ALdedicatedState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE])
|
||||
static ALvoid ALdedicatedState_process(ALdedicatedState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels)
|
||||
{
|
||||
const ALfloat *gains = state->gains;
|
||||
ALuint i, c;
|
||||
|
||||
for(c = 0;c < MaxChannels;c++)
|
||||
for(c = 0;c < NumChannels;c++)
|
||||
{
|
||||
if(!(gains[c] > GAIN_SILENCE_THRESHOLD))
|
||||
if(!(fabsf(gains[c]) > GAIN_SILENCE_THRESHOLD))
|
||||
continue;
|
||||
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
@@ -94,7 +108,7 @@ ALeffectState *ALdedicatedStateFactory_create(ALdedicatedStateFactory *UNUSED(fa
|
||||
if(!state) return NULL;
|
||||
SET_VTABLE2(ALdedicatedState, ALeffectState, state);
|
||||
|
||||
for(s = 0;s < MaxChannels;s++)
|
||||
for(s = 0;s < MAX_OUTPUT_CHANNELS;s++)
|
||||
state->gains[s] = 0.0f;
|
||||
|
||||
return STATIC_CAST(ALeffectState, state);
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
*
|
||||
* 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., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
@@ -34,7 +34,7 @@ typedef struct ALdistortionState {
|
||||
DERIVE_FROM_TYPE(ALeffectState);
|
||||
|
||||
/* Effect gains for each channel */
|
||||
ALfloat Gain[MaxChannels];
|
||||
ALfloat Gain[MAX_OUTPUT_CHANNELS];
|
||||
|
||||
/* Effect parameters */
|
||||
ALfilterState lowpass;
|
||||
@@ -58,7 +58,6 @@ static ALvoid ALdistortionState_update(ALdistortionState *state, ALCdevice *Devi
|
||||
ALfloat bandwidth;
|
||||
ALfloat cutoff;
|
||||
ALfloat edge;
|
||||
ALfloat gain;
|
||||
|
||||
/* Store distorted signal attenuation settings */
|
||||
state->attenuation = Slot->EffectProps.Distortion.Gain;
|
||||
@@ -73,23 +72,23 @@ static ALvoid ALdistortionState_update(ALdistortionState *state, ALCdevice *Devi
|
||||
/* Bandwidth value is constant in octaves */
|
||||
bandwidth = (cutoff / 2.0f) / (cutoff * 0.67f);
|
||||
ALfilterState_setParams(&state->lowpass, ALfilterType_LowPass, 1.0f,
|
||||
cutoff / (frequency*4.0f), bandwidth);
|
||||
cutoff / (frequency*4.0f), calc_rcpQ_from_bandwidth(cutoff / (frequency*4.0f), bandwidth)
|
||||
);
|
||||
|
||||
/* Bandpass filter */
|
||||
cutoff = Slot->EffectProps.Distortion.EQCenter;
|
||||
/* Convert bandwidth in Hz to octaves */
|
||||
bandwidth = Slot->EffectProps.Distortion.EQBandwidth / (cutoff * 0.67f);
|
||||
ALfilterState_setParams(&state->bandpass, ALfilterType_BandPass, 1.0f,
|
||||
cutoff / (frequency*4.0f), bandwidth);
|
||||
cutoff / (frequency*4.0f), calc_rcpQ_from_bandwidth(cutoff / (frequency*4.0f), bandwidth)
|
||||
);
|
||||
|
||||
gain = sqrtf(1.0f / Device->NumChan) * Slot->Gain;
|
||||
SetGains(Device, gain, state->Gain);
|
||||
ComputeAmbientGains(Device, Slot->Gain, state->Gain);
|
||||
}
|
||||
|
||||
static ALvoid ALdistortionState_process(ALdistortionState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE])
|
||||
static ALvoid ALdistortionState_process(ALdistortionState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels)
|
||||
{
|
||||
const ALfloat fc = state->edge_coeff;
|
||||
float oversample_buffer[64][4];
|
||||
ALuint base;
|
||||
ALuint it;
|
||||
ALuint ot;
|
||||
@@ -97,8 +96,8 @@ static ALvoid ALdistortionState_process(ALdistortionState *state, ALuint Samples
|
||||
|
||||
for(base = 0;base < SamplesToDo;)
|
||||
{
|
||||
ALfloat temps[64];
|
||||
ALuint td = minu(SamplesToDo-base, 64);
|
||||
float oversample_buffer[64][4];
|
||||
ALuint td = minu(64, SamplesToDo-base);
|
||||
|
||||
/* Perform 4x oversampling to avoid aliasing. */
|
||||
/* Oversampling greatly improves distortion */
|
||||
@@ -150,20 +149,19 @@ static ALvoid ALdistortionState_process(ALdistortionState *state, ALuint Samples
|
||||
smp = ALfilterState_processSingle(&state->bandpass, smp);
|
||||
oversample_buffer[it][ot] = smp;
|
||||
}
|
||||
|
||||
/* Fourth step, final, do attenuation and perform decimation, */
|
||||
/* store only one sample out of 4. */
|
||||
temps[it] = oversample_buffer[it][0] * state->attenuation;
|
||||
}
|
||||
|
||||
for(kt = 0;kt < MaxChannels;kt++)
|
||||
for(kt = 0;kt < NumChannels;kt++)
|
||||
{
|
||||
ALfloat gain = state->Gain[kt];
|
||||
if(!(gain > GAIN_SILENCE_THRESHOLD))
|
||||
/* 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 * temps[it];
|
||||
SamplesOut[kt][base+it] += gain * oversample_buffer[it][0];
|
||||
}
|
||||
|
||||
base += td;
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
*
|
||||
* 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., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
@@ -43,7 +43,7 @@ typedef struct ALechoState {
|
||||
} Tap[2];
|
||||
ALuint Offset;
|
||||
/* The panning gains for the two taps */
|
||||
ALfloat Gain[2][MaxChannels];
|
||||
ALfloat Gain[2][MAX_OUTPUT_CHANNELS];
|
||||
|
||||
ALfloat FeedGain;
|
||||
|
||||
@@ -83,9 +83,9 @@ static ALboolean ALechoState_deviceUpdate(ALechoState *state, ALCdevice *Device)
|
||||
|
||||
static ALvoid ALechoState_update(ALechoState *state, ALCdevice *Device, const ALeffectslot *Slot)
|
||||
{
|
||||
ALfloat pandir[3] = { 0.0f, 0.0f, 0.0f };
|
||||
ALuint frequency = Device->Frequency;
|
||||
ALfloat lrpan, gain;
|
||||
ALfloat dirGain;
|
||||
ALfloat gain, lrpan;
|
||||
|
||||
state->Tap[0].delay = fastf2u(Slot->EffectProps.Echo.Delay * frequency) + 1;
|
||||
state->Tap[1].delay = fastf2u(Slot->EffectProps.Echo.LRDelay * frequency);
|
||||
@@ -95,21 +95,23 @@ static ALvoid ALechoState_update(ALechoState *state, ALCdevice *Device, const AL
|
||||
|
||||
state->FeedGain = Slot->EffectProps.Echo.Feedback;
|
||||
|
||||
gain = minf(1.0f - Slot->EffectProps.Echo.Damping, 0.01f);
|
||||
ALfilterState_setParams(&state->Filter, ALfilterType_HighShelf,
|
||||
1.0f - Slot->EffectProps.Echo.Damping,
|
||||
LOWPASSFREQREF/frequency, 0.0f);
|
||||
gain, LOWPASSFREQREF/frequency,
|
||||
calc_rcpQ_from_slope(gain, 0.75f));
|
||||
|
||||
gain = Slot->Gain;
|
||||
dirGain = fabsf(lrpan);
|
||||
|
||||
/* First tap panning */
|
||||
ComputeAngleGains(Device, atan2f(-lrpan, 0.0f), (1.0f-dirGain)*F_PI, gain, state->Gain[0]);
|
||||
pandir[0] = -lrpan;
|
||||
ComputeDirectionalGains(Device, pandir, gain, state->Gain[0]);
|
||||
|
||||
/* Second tap panning */
|
||||
ComputeAngleGains(Device, atan2f(+lrpan, 0.0f), (1.0f-dirGain)*F_PI, gain, state->Gain[1]);
|
||||
pandir[0] = +lrpan;
|
||||
ComputeDirectionalGains(Device, pandir, gain, state->Gain[1]);
|
||||
}
|
||||
|
||||
static ALvoid ALechoState_process(ALechoState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE])
|
||||
static ALvoid ALechoState_process(ALechoState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels)
|
||||
{
|
||||
const ALuint mask = state->BufferLength-1;
|
||||
const ALuint tap1 = state->Tap[0].delay;
|
||||
@@ -121,8 +123,8 @@ static ALvoid ALechoState_process(ALechoState *state, ALuint SamplesToDo, const
|
||||
|
||||
for(base = 0;base < SamplesToDo;)
|
||||
{
|
||||
ALfloat temps[64][2];
|
||||
ALuint td = minu(SamplesToDo-base, 64);
|
||||
ALfloat temps[128][2];
|
||||
ALuint td = minu(128, SamplesToDo-base);
|
||||
|
||||
for(i = 0;i < td;i++)
|
||||
{
|
||||
@@ -138,17 +140,17 @@ static ALvoid ALechoState_process(ALechoState *state, ALuint SamplesToDo, const
|
||||
offset++;
|
||||
}
|
||||
|
||||
for(k = 0;k < MaxChannels;k++)
|
||||
for(k = 0;k < NumChannels;k++)
|
||||
{
|
||||
ALfloat gain = state->Gain[0][k];
|
||||
if(gain > GAIN_SILENCE_THRESHOLD)
|
||||
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(gain > GAIN_SILENCE_THRESHOLD)
|
||||
if(fabsf(gain) > GAIN_SILENCE_THRESHOLD)
|
||||
{
|
||||
for(i = 0;i < td;i++)
|
||||
SamplesOut[k][i+base] += temps[i][1] * gain;
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
*
|
||||
* 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., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
@@ -75,7 +75,7 @@ typedef struct ALequalizerState {
|
||||
DERIVE_FROM_TYPE(ALeffectState);
|
||||
|
||||
/* Effect gains for each channel */
|
||||
ALfloat Gain[MaxChannels];
|
||||
ALfloat Gain[MAX_OUTPUT_CHANNELS];
|
||||
|
||||
/* Effect parameters */
|
||||
ALfilterState filter[4];
|
||||
@@ -93,33 +93,40 @@ static ALboolean ALequalizerState_deviceUpdate(ALequalizerState *UNUSED(state),
|
||||
static ALvoid ALequalizerState_update(ALequalizerState *state, ALCdevice *device, const ALeffectslot *slot)
|
||||
{
|
||||
ALfloat frequency = (ALfloat)device->Frequency;
|
||||
ALfloat gain = sqrtf(1.0f / device->NumChan) * slot->Gain;
|
||||
ALfloat gain, freq_mult;
|
||||
|
||||
SetGains(device, gain, state->Gain);
|
||||
ComputeAmbientGains(device, slot->Gain, state->Gain);
|
||||
|
||||
/* Calculate coefficients for the each type of filter */
|
||||
/* Calculate coefficients for the each type of filter. Note that the shelf
|
||||
* filters' gain is for the reference frequency, which is the centerpoint
|
||||
* of the transition band.
|
||||
*/
|
||||
gain = sqrtf(slot->EffectProps.Equalizer.LowGain);
|
||||
freq_mult = slot->EffectProps.Equalizer.LowCutoff/frequency;
|
||||
ALfilterState_setParams(&state->filter[0], ALfilterType_LowShelf,
|
||||
sqrtf(slot->EffectProps.Equalizer.LowGain),
|
||||
slot->EffectProps.Equalizer.LowCutoff/frequency,
|
||||
0.0f);
|
||||
gain, freq_mult, calc_rcpQ_from_slope(gain, 0.75f)
|
||||
);
|
||||
|
||||
gain = slot->EffectProps.Equalizer.Mid1Gain;
|
||||
freq_mult = slot->EffectProps.Equalizer.Mid1Center/frequency;
|
||||
ALfilterState_setParams(&state->filter[1], ALfilterType_Peaking,
|
||||
sqrtf(slot->EffectProps.Equalizer.Mid1Gain),
|
||||
slot->EffectProps.Equalizer.Mid1Center/frequency,
|
||||
slot->EffectProps.Equalizer.Mid1Width);
|
||||
gain, freq_mult, calc_rcpQ_from_bandwidth(freq_mult, slot->EffectProps.Equalizer.Mid1Width)
|
||||
);
|
||||
|
||||
gain = slot->EffectProps.Equalizer.Mid2Gain;
|
||||
freq_mult = slot->EffectProps.Equalizer.Mid2Center/frequency;
|
||||
ALfilterState_setParams(&state->filter[2], ALfilterType_Peaking,
|
||||
sqrtf(slot->EffectProps.Equalizer.Mid2Gain),
|
||||
slot->EffectProps.Equalizer.Mid2Center/frequency,
|
||||
slot->EffectProps.Equalizer.Mid2Width);
|
||||
gain, freq_mult, calc_rcpQ_from_bandwidth(freq_mult, slot->EffectProps.Equalizer.Mid2Width)
|
||||
);
|
||||
|
||||
gain = sqrtf(slot->EffectProps.Equalizer.HighGain);
|
||||
freq_mult = slot->EffectProps.Equalizer.HighCutoff/frequency;
|
||||
ALfilterState_setParams(&state->filter[3], ALfilterType_HighShelf,
|
||||
sqrtf(slot->EffectProps.Equalizer.HighGain),
|
||||
slot->EffectProps.Equalizer.HighCutoff/frequency,
|
||||
0.0f);
|
||||
gain, freq_mult, calc_rcpQ_from_slope(gain, 0.75f)
|
||||
);
|
||||
}
|
||||
|
||||
static ALvoid ALequalizerState_process(ALequalizerState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE])
|
||||
static ALvoid ALequalizerState_process(ALequalizerState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels)
|
||||
{
|
||||
ALuint base;
|
||||
ALuint it;
|
||||
@@ -128,8 +135,8 @@ static ALvoid ALequalizerState_process(ALequalizerState *state, ALuint SamplesTo
|
||||
|
||||
for(base = 0;base < SamplesToDo;)
|
||||
{
|
||||
ALfloat temps[64];
|
||||
ALuint td = minu(SamplesToDo-base, 64);
|
||||
ALfloat temps[256];
|
||||
ALuint td = minu(256, SamplesToDo-base);
|
||||
|
||||
for(it = 0;it < td;it++)
|
||||
{
|
||||
@@ -141,10 +148,10 @@ static ALvoid ALequalizerState_process(ALequalizerState *state, ALuint SamplesTo
|
||||
temps[it] = smp;
|
||||
}
|
||||
|
||||
for(kt = 0;kt < MaxChannels;kt++)
|
||||
for(kt = 0;kt < NumChannels;kt++)
|
||||
{
|
||||
ALfloat gain = state->Gain[kt];
|
||||
if(!(gain > GAIN_SILENCE_THRESHOLD))
|
||||
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
|
||||
continue;
|
||||
|
||||
for(it = 0;it < td;it++)
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
*
|
||||
* 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., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
@@ -46,7 +46,7 @@ typedef struct ALflangerState {
|
||||
ALint lfo_disp;
|
||||
|
||||
/* Gains for left and right sides */
|
||||
ALfloat Gain[2][MaxChannels];
|
||||
ALfloat Gain[2][MAX_OUTPUT_CHANNELS];
|
||||
|
||||
/* effect parameters */
|
||||
enum FlangerWaveForm waveform;
|
||||
@@ -93,6 +93,8 @@ static ALboolean ALflangerState_deviceUpdate(ALflangerState *state, ALCdevice *D
|
||||
|
||||
static ALvoid ALflangerState_update(ALflangerState *state, ALCdevice *Device, const ALeffectslot *Slot)
|
||||
{
|
||||
static const ALfloat left_dir[3] = { -1.0f, 0.0f, 0.0f };
|
||||
static const ALfloat right_dir[3] = { 1.0f, 0.0f, 0.0f };
|
||||
ALfloat frequency = (ALfloat)Device->Frequency;
|
||||
ALfloat rate;
|
||||
ALint phase;
|
||||
@@ -111,8 +113,8 @@ static ALvoid ALflangerState_update(ALflangerState *state, ALCdevice *Device, co
|
||||
state->delay = fastf2i(Slot->EffectProps.Flanger.Delay * frequency);
|
||||
|
||||
/* Gains for left and right sides */
|
||||
ComputeAngleGains(Device, atan2f(-1.0f, 0.0f), 0.0f, Slot->Gain, state->Gain[0]);
|
||||
ComputeAngleGains(Device, atan2f(+1.0f, 0.0f), 0.0f, Slot->Gain, state->Gain[1]);
|
||||
ComputeDirectionalGains(Device, left_dir, Slot->Gain, state->Gain[0]);
|
||||
ComputeDirectionalGains(Device, right_dir, Slot->Gain, state->Gain[1]);
|
||||
|
||||
phase = Slot->EffectProps.Flanger.Phase;
|
||||
rate = Slot->EffectProps.Flanger.Rate;
|
||||
@@ -132,7 +134,7 @@ static ALvoid ALflangerState_update(ALflangerState *state, ALCdevice *Device, co
|
||||
state->lfo_scale = 4.0f / state->lfo_range;
|
||||
break;
|
||||
case FWF_Sinusoid:
|
||||
state->lfo_scale = F_2PI / state->lfo_range;
|
||||
state->lfo_scale = F_TAU / state->lfo_range;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -201,15 +203,15 @@ DECL_TEMPLATE(Sinusoid)
|
||||
|
||||
#undef DECL_TEMPLATE
|
||||
|
||||
static ALvoid ALflangerState_process(ALflangerState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE])
|
||||
static ALvoid ALflangerState_process(ALflangerState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels)
|
||||
{
|
||||
ALuint it, kt;
|
||||
ALuint base;
|
||||
|
||||
for(base = 0;base < SamplesToDo;)
|
||||
{
|
||||
ALfloat temps[64][2];
|
||||
ALuint td = minu(SamplesToDo-base, 64);
|
||||
ALfloat temps[128][2];
|
||||
ALuint td = minu(128, SamplesToDo-base);
|
||||
|
||||
switch(state->waveform)
|
||||
{
|
||||
@@ -221,17 +223,17 @@ static ALvoid ALflangerState_process(ALflangerState *state, ALuint SamplesToDo,
|
||||
break;
|
||||
}
|
||||
|
||||
for(kt = 0;kt < MaxChannels;kt++)
|
||||
for(kt = 0;kt < NumChannels;kt++)
|
||||
{
|
||||
ALfloat gain = state->Gain[0][kt];
|
||||
if(gain > GAIN_SILENCE_THRESHOLD)
|
||||
if(fabsf(gain) > GAIN_SILENCE_THRESHOLD)
|
||||
{
|
||||
for(it = 0;it < td;it++)
|
||||
SamplesOut[kt][it+base] += temps[it][0] * gain;
|
||||
}
|
||||
|
||||
gain = state->Gain[1][kt];
|
||||
if(gain > GAIN_SILENCE_THRESHOLD)
|
||||
if(fabsf(gain) > GAIN_SILENCE_THRESHOLD)
|
||||
{
|
||||
for(it = 0;it < td;it++)
|
||||
SamplesOut[kt][it+base] += temps[it][1] * gain;
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
*
|
||||
* 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., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
@@ -42,7 +42,7 @@ typedef struct ALmodulatorState {
|
||||
ALuint index;
|
||||
ALuint step;
|
||||
|
||||
ALfloat Gain[MaxChannels];
|
||||
ALfloat Gain[MAX_OUTPUT_CHANNELS];
|
||||
|
||||
ALfilterState Filter;
|
||||
} ALmodulatorState;
|
||||
@@ -53,7 +53,7 @@ typedef struct ALmodulatorState {
|
||||
|
||||
static inline ALfloat Sin(ALuint index)
|
||||
{
|
||||
return sinf(index*(F_2PI/WAVEFORM_FRACONE) - F_PI)*0.5f + 0.5f;
|
||||
return sinf(index*(F_TAU/WAVEFORM_FRACONE) - F_PI)*0.5f + 0.5f;
|
||||
}
|
||||
|
||||
static inline ALfloat Saw(ALuint index)
|
||||
@@ -69,7 +69,7 @@ static inline ALfloat Square(ALuint index)
|
||||
#define DECL_TEMPLATE(func) \
|
||||
static void Process##func(ALmodulatorState *state, ALuint SamplesToDo, \
|
||||
const ALfloat *restrict SamplesIn, \
|
||||
ALfloat (*restrict SamplesOut)[BUFFERSIZE]) \
|
||||
ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels) \
|
||||
{ \
|
||||
const ALuint step = state->step; \
|
||||
ALuint index = state->index; \
|
||||
@@ -77,8 +77,8 @@ static void Process##func(ALmodulatorState *state, ALuint SamplesToDo, \
|
||||
\
|
||||
for(base = 0;base < SamplesToDo;) \
|
||||
{ \
|
||||
ALfloat temps[64]; \
|
||||
ALuint td = minu(SamplesToDo-base, 64); \
|
||||
ALfloat temps[256]; \
|
||||
ALuint td = minu(256, SamplesToDo-base); \
|
||||
ALuint i, k; \
|
||||
\
|
||||
for(i = 0;i < td;i++) \
|
||||
@@ -92,10 +92,10 @@ static void Process##func(ALmodulatorState *state, ALuint SamplesToDo, \
|
||||
temps[i] = samp * func(index); \
|
||||
} \
|
||||
\
|
||||
for(k = 0;k < MaxChannels;k++) \
|
||||
for(k = 0;k < NumChannels;k++) \
|
||||
{ \
|
||||
ALfloat gain = state->Gain[k]; \
|
||||
if(!(gain > GAIN_SILENCE_THRESHOLD)) \
|
||||
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD)) \
|
||||
continue; \
|
||||
\
|
||||
for(i = 0;i < td;i++) \
|
||||
@@ -125,7 +125,7 @@ static ALboolean ALmodulatorState_deviceUpdate(ALmodulatorState *UNUSED(state),
|
||||
|
||||
static ALvoid ALmodulatorState_update(ALmodulatorState *state, ALCdevice *Device, const ALeffectslot *Slot)
|
||||
{
|
||||
ALfloat gain, cw, a;
|
||||
ALfloat cw, a;
|
||||
|
||||
if(Slot->EffectProps.Modulator.Waveform == AL_RING_MODULATOR_SINUSOID)
|
||||
state->Waveform = SINUSOID;
|
||||
@@ -139,7 +139,7 @@ static ALvoid ALmodulatorState_update(ALmodulatorState *state, ALCdevice *Device
|
||||
if(state->step == 0) state->step = 1;
|
||||
|
||||
/* Custom filter coeffs, which match the old version instead of a low-shelf. */
|
||||
cw = cosf(F_2PI * Slot->EffectProps.Modulator.HighPassCutoff / Device->Frequency);
|
||||
cw = cosf(F_TAU * Slot->EffectProps.Modulator.HighPassCutoff / Device->Frequency);
|
||||
a = (2.0f-cw) - sqrtf(powf(2.0f-cw, 2.0f) - 1.0f);
|
||||
|
||||
state->Filter.b[0] = a;
|
||||
@@ -149,24 +149,23 @@ static ALvoid ALmodulatorState_update(ALmodulatorState *state, ALCdevice *Device
|
||||
state->Filter.a[1] = -a;
|
||||
state->Filter.a[2] = 0.0f;
|
||||
|
||||
gain = sqrtf(1.0f/Device->NumChan) * Slot->Gain;
|
||||
SetGains(Device, gain, state->Gain);
|
||||
ComputeAmbientGains(Device, Slot->Gain, state->Gain);
|
||||
}
|
||||
|
||||
static ALvoid ALmodulatorState_process(ALmodulatorState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE])
|
||||
static ALvoid ALmodulatorState_process(ALmodulatorState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels)
|
||||
{
|
||||
switch(state->Waveform)
|
||||
{
|
||||
case SINUSOID:
|
||||
ProcessSin(state, SamplesToDo, SamplesIn, SamplesOut);
|
||||
ProcessSin(state, SamplesToDo, SamplesIn, SamplesOut, NumChannels);
|
||||
break;
|
||||
|
||||
case SAWTOOTH:
|
||||
ProcessSaw(state, SamplesToDo, SamplesIn, SamplesOut);
|
||||
ProcessSaw(state, SamplesToDo, SamplesIn, SamplesOut, NumChannels);
|
||||
break;
|
||||
|
||||
case SQUARE:
|
||||
ProcessSquare(state, SamplesToDo, SamplesIn, SamplesOut);
|
||||
ProcessSquare(state, SamplesToDo, SamplesIn, SamplesOut, NumChannels);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,11 +41,8 @@ static ALvoid ALnullState_update(ALnullState* UNUSED(state), ALCdevice* UNUSED(d
|
||||
* input to the output buffer. The result should be added to the output buffer,
|
||||
* not replace it.
|
||||
*/
|
||||
static ALvoid ALnullState_process(ALnullState* UNUSED(state), ALuint UNUSED(samplesToDo), const ALfloat *restrict UNUSED(samplesIn), ALfloat (*restrict samplesOut)[BUFFERSIZE])
|
||||
static ALvoid ALnullState_process(ALnullState* UNUSED(state), ALuint UNUSED(samplesToDo), const ALfloat *restrict UNUSED(samplesIn), ALfloatBUFFERSIZE*restrict UNUSED(samplesOut), ALuint UNUSED(NumChannels))
|
||||
{
|
||||
/* NOTE: Couldn't use the UNUSED macro on samplesOut due to the way GCC's
|
||||
* __attribute__ declaration interacts with the parenthesis. */
|
||||
(void)samplesOut;
|
||||
}
|
||||
|
||||
/* This allocates memory to store the object, before it gets constructed.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+669
-107
@@ -13,8 +13,8 @@
|
||||
*
|
||||
* 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., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
@@ -35,6 +35,9 @@
|
||||
#ifdef HAVE_MALLOC_H
|
||||
#include <malloc.h>
|
||||
#endif
|
||||
#ifdef HAVE_DIRENT_H
|
||||
#include <dirent.h>
|
||||
#endif
|
||||
|
||||
#ifndef AL_NO_UID_DEFS
|
||||
#if defined(HAVE_GUIDDEF_H) || defined(HAVE_INITGUID_H)
|
||||
@@ -55,10 +58,13 @@ DEFINE_GUID(CLSID_MMDeviceEnumerator, 0xbcde0395, 0xe52f, 0x467c, 0x8e,0x3d, 0xc
|
||||
DEFINE_GUID(IID_IMMDeviceEnumerator, 0xa95664d2, 0x9614, 0x4f35, 0xa7,0x46, 0xde,0x8d,0xb6,0x36,0x17,0xe6);
|
||||
DEFINE_GUID(IID_IAudioClient, 0x1cb9ad4c, 0xdbfa, 0x4c32, 0xb1,0x78, 0xc2,0xf5,0x68,0xa7,0x03,0xb2);
|
||||
DEFINE_GUID(IID_IAudioRenderClient, 0xf294acfc, 0x3146, 0x4483, 0xa7,0xbf, 0xad,0xdc,0xa7,0xc2,0x60,0xe2);
|
||||
DEFINE_GUID(IID_IAudioCaptureClient, 0xc8adbd64, 0xe71e, 0x48a0, 0xa4,0xde, 0x18,0x5c,0x39,0x5c,0xd3,0x17);
|
||||
|
||||
#ifdef HAVE_MMDEVAPI
|
||||
#include <devpropdef.h>
|
||||
#include <propkeydef.h>
|
||||
DEFINE_DEVPROPKEY(DEVPKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80,0x20, 0x67,0xd1,0x46,0xa8,0x50,0xe0, 14);
|
||||
DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_FormFactor, 0x1da5d803, 0xd492, 0x4edd, 0x8c,0x23, 0xe0,0xc0,0xff,0xee,0x7f,0x0e, 0);
|
||||
#endif
|
||||
#endif
|
||||
#endif /* AL_NO_UID_DEFS */
|
||||
@@ -82,7 +88,9 @@ DEFINE_DEVPROPKEY(DEVPKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80,
|
||||
#include <ieeefp.h>
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32_IE
|
||||
#ifndef _WIN32
|
||||
#include <unistd.h>
|
||||
#elif defined(_WIN32_IE)
|
||||
#include <shlobj.h>
|
||||
#endif
|
||||
|
||||
@@ -144,8 +152,12 @@ void FillCPUCaps(ALuint capfilter)
|
||||
if((cpuinf[0].regs[3]&(1<<26)))
|
||||
{
|
||||
caps |= CPU_CAP_SSE2;
|
||||
if((cpuinf[0].regs[2]&(1<<19)))
|
||||
caps |= CPU_CAP_SSE4_1;
|
||||
if((cpuinf[0].regs[2]&(1<<0)))
|
||||
{
|
||||
caps |= CPU_CAP_SSE3;
|
||||
if((cpuinf[0].regs[2]&(1<<19)))
|
||||
caps |= CPU_CAP_SSE4_1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -188,8 +200,12 @@ void FillCPUCaps(ALuint capfilter)
|
||||
if((cpuinf[0].regs[3]&(1<<26)))
|
||||
{
|
||||
caps |= CPU_CAP_SSE2;
|
||||
if((cpuinf[0].regs[2]&(1<<19)))
|
||||
caps |= CPU_CAP_SSE4_1;
|
||||
if((cpuinf[0].regs[2]&(1<<0)))
|
||||
{
|
||||
caps |= CPU_CAP_SSE3;
|
||||
if((cpuinf[0].regs[2]&(1<<19)))
|
||||
caps |= CPU_CAP_SSE4_1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -198,13 +214,16 @@ void FillCPUCaps(ALuint capfilter)
|
||||
/* Assume support for whatever's supported if we can't check for it */
|
||||
#if defined(HAVE_SSE4_1)
|
||||
#warning "Assuming SSE 4.1 run-time support!"
|
||||
capfilter |= CPU_CAP_SSE | CPU_CAP_SSE2 | CPU_CAP_SSE4_1;
|
||||
caps |= CPU_CAP_SSE | CPU_CAP_SSE2 | CPU_CAP_SSE3 | CPU_CAP_SSE4_1;
|
||||
#elif defined(HAVE_SSE3)
|
||||
#warning "Assuming SSE 3 run-time support!"
|
||||
caps |= CPU_CAP_SSE | CPU_CAP_SSE2 | CPU_CAP_SSE3;
|
||||
#elif defined(HAVE_SSE2)
|
||||
#warning "Assuming SSE 2 run-time support!"
|
||||
capfilter |= CPU_CAP_SSE | CPU_CAP_SSE2;
|
||||
caps |= CPU_CAP_SSE | CPU_CAP_SSE2;
|
||||
#elif defined(HAVE_SSE)
|
||||
#warning "Assuming SSE run-time support!"
|
||||
capfilter |= CPU_CAP_SSE;
|
||||
caps |= CPU_CAP_SSE;
|
||||
#endif
|
||||
#endif
|
||||
#ifdef HAVE_NEON
|
||||
@@ -212,9 +231,10 @@ void FillCPUCaps(ALuint capfilter)
|
||||
caps |= CPU_CAP_NEON;
|
||||
#endif
|
||||
|
||||
TRACE("Extensions:%s%s%s%s%s\n",
|
||||
TRACE("Extensions:%s%s%s%s%s%s\n",
|
||||
((capfilter&CPU_CAP_SSE) ? ((caps&CPU_CAP_SSE) ? " +SSE" : " -SSE") : ""),
|
||||
((capfilter&CPU_CAP_SSE2) ? ((caps&CPU_CAP_SSE2) ? " +SSE2" : " -SSE2") : ""),
|
||||
((capfilter&CPU_CAP_SSE3) ? ((caps&CPU_CAP_SSE3) ? " +SSE3" : " -SSE3") : ""),
|
||||
((capfilter&CPU_CAP_SSE4_1) ? ((caps&CPU_CAP_SSE4_1) ? " +SSE4.1" : " -SSE4.1") : ""),
|
||||
((capfilter&CPU_CAP_NEON) ? ((caps&CPU_CAP_NEON) ? " +Neon" : " -Neon") : ""),
|
||||
((!capfilter) ? " -none-" : "")
|
||||
@@ -240,7 +260,7 @@ void *al_malloc(size_t alignment, size_t size)
|
||||
if(ret != NULL)
|
||||
{
|
||||
*(ret++) = 0x00;
|
||||
while(((ALintptrEXT)ret&(alignment-1)) != 0)
|
||||
while(((ptrdiff_t)ret&(alignment-1)) != 0)
|
||||
*(ret++) = 0x55;
|
||||
}
|
||||
return ret;
|
||||
@@ -278,6 +298,8 @@ void SetMixerFPUMode(FPUCtl *ctl)
|
||||
#ifdef HAVE_FENV_H
|
||||
fegetenv(STATIC_CAST(fenv_t, ctl));
|
||||
#if defined(__GNUC__) && defined(HAVE_SSE)
|
||||
/* FIXME: Some fegetenv implementations can get the SSE environment too?
|
||||
* How to tell when it does? */
|
||||
if((CPUCapFlags&CPU_CAP_SSE))
|
||||
__asm__ __volatile__("stmxcsr %0" : "=m" (*&ctl->sse_state));
|
||||
#endif
|
||||
@@ -421,6 +443,390 @@ FILE *al_fopen(const char *fname, const char *mode)
|
||||
return file;
|
||||
}
|
||||
|
||||
|
||||
void al_print(const char *type, const char *func, const char *fmt, ...)
|
||||
{
|
||||
char str[1024];
|
||||
WCHAR *wstr;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(str, sizeof(str), fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
str[sizeof(str)-1] = 0;
|
||||
wstr = FromUTF8(str);
|
||||
if(!wstr)
|
||||
fprintf(LogFile, "AL lib: %s %s: <UTF-8 error> %s", type, func, str);
|
||||
else
|
||||
{
|
||||
fprintf(LogFile, "AL lib: %s %s: %ls", type, func, wstr);
|
||||
free(wstr);
|
||||
wstr = NULL;
|
||||
}
|
||||
fflush(LogFile);
|
||||
}
|
||||
|
||||
|
||||
static inline int is_slash(int c)
|
||||
{ return (c == '\\' || c == '/'); }
|
||||
|
||||
FILE *OpenDataFile(const char *fname, const char *subdir)
|
||||
{
|
||||
static const int ids[2] = { CSIDL_APPDATA, CSIDL_COMMON_APPDATA };
|
||||
WCHAR *wname=NULL, *wsubdir=NULL;
|
||||
FILE *f;
|
||||
size_t i;
|
||||
|
||||
wname = FromUTF8(fname);
|
||||
if(!wname)
|
||||
{
|
||||
ERR("Failed to convert UTF-8 filename: \"%s\"\n", fname);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* If the path is absolute, open it directly. */
|
||||
if(wname[0] != '\0' && wname[1] == ':' && is_slash(wname[2]))
|
||||
{
|
||||
f = _wfopen(wname, L"rb");
|
||||
if(f) TRACE("Opened %s\n", fname);
|
||||
else WARN("Could not open %s\n", fname);
|
||||
free(wname);
|
||||
return f;
|
||||
}
|
||||
|
||||
/* Try the current directory first before the data directories. */
|
||||
if((f=_wfopen(wname, L"rb")) != NULL)
|
||||
{
|
||||
TRACE("Opened %s\n", fname);
|
||||
free(wname);
|
||||
return f;
|
||||
}
|
||||
|
||||
wsubdir = FromUTF8(subdir);
|
||||
if(!wsubdir)
|
||||
{
|
||||
ERR("Failed to convert UTF-8 subdir: \"%s\"\n", subdir);
|
||||
free(wname);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for(i = 0;i < COUNTOF(ids);i++)
|
||||
{
|
||||
WCHAR buffer[PATH_MAX];
|
||||
size_t len;
|
||||
|
||||
if(SHGetSpecialFolderPathW(NULL, buffer, ids[i], FALSE) == FALSE)
|
||||
continue;
|
||||
|
||||
len = lstrlenW(buffer);
|
||||
if(len > 0 && is_slash(buffer[len-1]))
|
||||
buffer[--len] = '\0';
|
||||
_snwprintf(buffer+len, PATH_MAX-len, L"/%ls/%ls", wsubdir, wname);
|
||||
len = lstrlenW(buffer);
|
||||
while(len > 0)
|
||||
{
|
||||
--len;
|
||||
if(buffer[len] == '/')
|
||||
buffer[len] = '\\';
|
||||
}
|
||||
|
||||
if((f=_wfopen(buffer, L"rb")) != NULL)
|
||||
{
|
||||
al_string filepath = AL_STRING_INIT_STATIC();
|
||||
al_string_copy_wcstr(&filepath, buffer);
|
||||
TRACE("Opened %s\n", al_string_get_cstr(filepath));
|
||||
al_string_deinit(&filepath);
|
||||
break;
|
||||
}
|
||||
}
|
||||
free(wname);
|
||||
free(wsubdir);
|
||||
|
||||
if(f == NULL)
|
||||
WARN("Could not open %s\\%s\n", subdir, fname);
|
||||
return f;
|
||||
}
|
||||
|
||||
|
||||
static const WCHAR *strchrW(const WCHAR *str, WCHAR ch)
|
||||
{
|
||||
for(;*str != 0;++str)
|
||||
{
|
||||
if(*str == ch)
|
||||
return str;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const WCHAR *strrchrW(const WCHAR *str, WCHAR ch)
|
||||
{
|
||||
const WCHAR *ret = NULL;
|
||||
for(;*str != 0;++str)
|
||||
{
|
||||
if(*str == ch)
|
||||
ret = str;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Compares the filename in the find data with the match string. The match
|
||||
* string may contain the "%r" marker to signifiy a sample rate (really any
|
||||
* positive integer), or "%%" to signify a single '%'.
|
||||
*/
|
||||
static int MatchFilter(const WCHAR *match, const WIN32_FIND_DATAW *fdata)
|
||||
{
|
||||
const WCHAR *name = fdata->cFileName;
|
||||
int ret = 1;
|
||||
|
||||
do {
|
||||
const WCHAR *p = strchrW(match, '%');
|
||||
if(!p)
|
||||
ret = CompareStringW(GetThreadLocale(), NORM_IGNORECASE,
|
||||
match, -1, name, -1) == CSTR_EQUAL;
|
||||
else
|
||||
{
|
||||
int len = p-match;
|
||||
ret = lstrlenW(name) >= len;
|
||||
if(ret)
|
||||
ret = CompareStringW(GetThreadLocale(), NORM_IGNORECASE,
|
||||
match, len, name, len) == CSTR_EQUAL;
|
||||
if(ret)
|
||||
{
|
||||
match += len;
|
||||
name += len;
|
||||
|
||||
++p;
|
||||
if(*p == 'r')
|
||||
{
|
||||
unsigned long l = 0;
|
||||
while(*name >= '0' && *name <= '9')
|
||||
{
|
||||
l = l*10 + (*name-'0');
|
||||
++name;
|
||||
}
|
||||
ret = l > 0;
|
||||
++p;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match = p;
|
||||
} while(ret && match && *match);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void RecurseDirectorySearch(const char *path, const WCHAR *match, vector_al_string *results)
|
||||
{
|
||||
WIN32_FIND_DATAW fdata;
|
||||
const WCHAR *sep, *p;
|
||||
HANDLE hdl;
|
||||
|
||||
if(!match[0])
|
||||
return;
|
||||
|
||||
/* Find the last directory separator and the next '%' marker in the match
|
||||
* string. */
|
||||
sep = strrchrW(match, '\\');
|
||||
p = strchrW(match, '%');
|
||||
|
||||
/* If there's no separator, test the files in the specified path against
|
||||
* the match string, and add the results. */
|
||||
if(!sep)
|
||||
{
|
||||
al_string pathstr = AL_STRING_INIT_STATIC();
|
||||
WCHAR *wpath;
|
||||
|
||||
TRACE("Searching %s for %ls\n", path, match);
|
||||
|
||||
al_string_append_cstr(&pathstr, path);
|
||||
al_string_append_cstr(&pathstr, "\\*.*");
|
||||
wpath = FromUTF8(al_string_get_cstr(pathstr));
|
||||
|
||||
hdl = FindFirstFileW(wpath, &fdata);
|
||||
if(hdl != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
do {
|
||||
if(MatchFilter(match, &fdata))
|
||||
{
|
||||
al_string str = AL_STRING_INIT_STATIC();
|
||||
al_string_copy_cstr(&str, path);
|
||||
al_string_append_char(&str, '\\');
|
||||
al_string_append_wcstr(&str, fdata.cFileName);
|
||||
TRACE("Got result %s\n", al_string_get_cstr(str));
|
||||
VECTOR_PUSH_BACK(*results, str);
|
||||
}
|
||||
} while(FindNextFileW(hdl, &fdata));
|
||||
FindClose(hdl);
|
||||
}
|
||||
|
||||
free(wpath);
|
||||
al_string_deinit(&pathstr);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* If there's no '%' marker, or it's after the final separator, append the
|
||||
* remaining directories to the path and recurse into it with the remaining
|
||||
* filename portion. */
|
||||
if(!p || p-sep >= 0)
|
||||
{
|
||||
al_string npath = AL_STRING_INIT_STATIC();
|
||||
al_string_append_cstr(&npath, path);
|
||||
al_string_append_char(&npath, '\\');
|
||||
al_string_append_wrange(&npath, match, sep);
|
||||
|
||||
TRACE("Recursing into %s with %ls\n", al_string_get_cstr(npath), sep+1);
|
||||
RecurseDirectorySearch(al_string_get_cstr(npath), sep+1, results);
|
||||
|
||||
al_string_deinit(&npath);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Look for the last separator before the '%' marker, and the first
|
||||
* separator after it. */
|
||||
sep = strchrW(match, '\\');
|
||||
if(sep-p >= 0) sep = NULL;
|
||||
for(;;)
|
||||
{
|
||||
const WCHAR *next = strchrW(sep?sep+1:match, '\\');
|
||||
if(next-p < 0)
|
||||
{
|
||||
al_string npath = AL_STRING_INIT_STATIC();
|
||||
WCHAR *nwpath, *nwmatch;
|
||||
|
||||
/* Append up to the last directory before the one with a '%'. */
|
||||
al_string_copy_cstr(&npath, path);
|
||||
if(sep)
|
||||
{
|
||||
al_string_append_char(&npath, '\\');
|
||||
al_string_append_wrange(&npath, match, sep);
|
||||
}
|
||||
al_string_append_cstr(&npath, "\\*.*");
|
||||
nwpath = FromUTF8(al_string_get_cstr(npath));
|
||||
|
||||
/* Take the directory name containing a '%' as a new string to
|
||||
* match against. */
|
||||
if(!sep)
|
||||
{
|
||||
nwmatch = calloc(2, next-match+1);
|
||||
memcpy(nwmatch, match, (next-match)*2);
|
||||
}
|
||||
else
|
||||
{
|
||||
nwmatch = calloc(2, next-(sep+1)+1);
|
||||
memcpy(nwmatch, sep+1, (next-(sep+1))*2);
|
||||
}
|
||||
|
||||
/* For each matching directory name, recurse into it with the
|
||||
* remaining string. */
|
||||
TRACE("Searching %s for %ls\n", al_string_get_cstr(npath), nwmatch);
|
||||
hdl = FindFirstFileW(nwpath, &fdata);
|
||||
if(hdl != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
do {
|
||||
if(MatchFilter(nwmatch, &fdata))
|
||||
{
|
||||
al_string ndir = AL_STRING_INIT_STATIC();
|
||||
al_string_copy(&ndir, npath);
|
||||
al_string_append_char(&ndir, '\\');
|
||||
al_string_append_wcstr(&ndir, fdata.cFileName);
|
||||
TRACE("Recursing %s with %ls\n", al_string_get_cstr(ndir), next+1);
|
||||
RecurseDirectorySearch(al_string_get_cstr(ndir), next+1, results);
|
||||
al_string_deinit(&ndir);
|
||||
}
|
||||
} while(FindNextFileW(hdl, &fdata));
|
||||
FindClose(hdl);
|
||||
}
|
||||
|
||||
free(nwmatch);
|
||||
free(nwpath);
|
||||
al_string_deinit(&npath);
|
||||
break;
|
||||
}
|
||||
sep = next;
|
||||
}
|
||||
}
|
||||
|
||||
vector_al_string SearchDataFiles(const char *match, const char *subdir)
|
||||
{
|
||||
static const int ids[2] = { CSIDL_APPDATA, CSIDL_COMMON_APPDATA };
|
||||
static RefCount search_lock;
|
||||
vector_al_string results = VECTOR_INIT_STATIC();
|
||||
WCHAR *wmatch;
|
||||
size_t i;
|
||||
|
||||
while(ATOMIC_EXCHANGE(uint, &search_lock, 1) == 1)
|
||||
althrd_yield();
|
||||
|
||||
wmatch = FromUTF8(match);
|
||||
if(!wmatch)
|
||||
{
|
||||
ERR("Failed to convert UTF-8 filename: \"%s\"\n", match);
|
||||
return results;
|
||||
}
|
||||
for(i = 0;wmatch[i];++i)
|
||||
{
|
||||
if(wmatch[i] == '/')
|
||||
wmatch[i] = '\\';
|
||||
}
|
||||
|
||||
/* If the path is absolute, use it directly. */
|
||||
if(isalpha(wmatch[0]) && wmatch[1] == ':' && is_slash(wmatch[2]))
|
||||
{
|
||||
char drv[3] = { (char)wmatch[0], ':', 0 };
|
||||
RecurseDirectorySearch(drv, wmatch+3, &results);
|
||||
}
|
||||
else if(wmatch[0] == '\\' && wmatch[1] == '\\' && wmatch[2] == '?' && wmatch[3] == '\\')
|
||||
RecurseDirectorySearch("\\\\?", wmatch+4, &results);
|
||||
else
|
||||
{
|
||||
al_string path = AL_STRING_INIT_STATIC();
|
||||
WCHAR *cwdbuf;
|
||||
|
||||
/* Search the CWD. */
|
||||
if(!(cwdbuf=_wgetcwd(NULL, 0)))
|
||||
al_string_copy_cstr(&path, ".");
|
||||
else
|
||||
{
|
||||
al_string_copy_wcstr(&path, cwdbuf);
|
||||
if(is_slash(VECTOR_BACK(path)))
|
||||
{
|
||||
VECTOR_POP_BACK(path);
|
||||
*VECTOR_ITER_END(path) = 0;
|
||||
}
|
||||
free(cwdbuf);
|
||||
}
|
||||
RecurseDirectorySearch(al_string_get_cstr(path), wmatch, &results);
|
||||
|
||||
/* Search the local and global data dirs. */
|
||||
for(i = 0;i < COUNTOF(ids);i++)
|
||||
{
|
||||
WCHAR buffer[PATH_MAX];
|
||||
if(SHGetSpecialFolderPathW(NULL, buffer, ids[i], FALSE) != FALSE)
|
||||
{
|
||||
al_string_copy_wcstr(&path, buffer);
|
||||
if(!is_slash(VECTOR_BACK(path)))
|
||||
al_string_append_char(&path, '\\');
|
||||
al_string_append_cstr(&path, subdir);
|
||||
#define FIX_SLASH(i) do { if(*(i) == '/') *(i) = '\\'; } while(0)
|
||||
VECTOR_FOR_EACH(char, path, FIX_SLASH);
|
||||
#undef FIX_SLASH
|
||||
|
||||
RecurseDirectorySearch(al_string_get_cstr(path), wmatch, &results);
|
||||
}
|
||||
}
|
||||
|
||||
al_string_deinit(&path);
|
||||
}
|
||||
|
||||
free(wmatch);
|
||||
ATOMIC_STORE(&search_lock, 0);
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#ifdef HAVE_DLFCN_H
|
||||
@@ -453,9 +859,7 @@ void *GetSymbol(void *handle, const char *name)
|
||||
return sym;
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif /* HAVE_DLFCN_H */
|
||||
|
||||
void al_print(const char *type, const char *func, const char *fmt, ...)
|
||||
{
|
||||
@@ -469,76 +873,7 @@ void al_print(const char *type, const char *func, const char *fmt, ...)
|
||||
fflush(LogFile);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
static inline int is_slash(int c)
|
||||
{ return (c == '\\' || c == '/'); }
|
||||
|
||||
FILE *OpenDataFile(const char *fname, const char *subdir)
|
||||
{
|
||||
static const int ids[2] = { CSIDL_APPDATA, CSIDL_COMMON_APPDATA };
|
||||
WCHAR *wname=NULL, *wsubdir=NULL;
|
||||
FILE *f;
|
||||
int i;
|
||||
|
||||
/* If the path is absolute, open it directly. */
|
||||
if(fname[0] != '\0' && fname[1] == ':' && is_slash(fname[2]))
|
||||
{
|
||||
if((f=al_fopen(fname, "rb")) != NULL)
|
||||
{
|
||||
TRACE("Opened %s\n", fname);
|
||||
return f;
|
||||
}
|
||||
WARN("Could not open %s\n", fname);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* If it's relative, try the current directory first before the data directories. */
|
||||
if((f=al_fopen(fname, "rb")) != NULL)
|
||||
{
|
||||
TRACE("Opened %s\n", fname);
|
||||
return f;
|
||||
}
|
||||
WARN("Could not open %s\n", fname);
|
||||
|
||||
wname = FromUTF8(fname);
|
||||
wsubdir = FromUTF8(subdir);
|
||||
if(!wname)
|
||||
ERR("Failed to convert UTF-8 filename: \"%s\"\n", fname);
|
||||
else if(!wsubdir)
|
||||
ERR("Failed to convert UTF-8 subdir: \"%s\"\n", subdir);
|
||||
else for(i = 0;i < 2;i++)
|
||||
{
|
||||
WCHAR buffer[PATH_MAX];
|
||||
size_t len;
|
||||
|
||||
if(SHGetSpecialFolderPathW(NULL, buffer, ids[i], FALSE) == FALSE)
|
||||
continue;
|
||||
|
||||
len = lstrlenW(buffer);
|
||||
if(len > 0 && is_slash(buffer[len-1]))
|
||||
buffer[--len] = '\0';
|
||||
_snwprintf(buffer+len, PATH_MAX-len, L"/%ls/%ls", wsubdir, wname);
|
||||
len = lstrlenW(buffer);
|
||||
while(len > 0)
|
||||
{
|
||||
--len;
|
||||
if(buffer[len] == '/')
|
||||
buffer[len] = '\\';
|
||||
}
|
||||
|
||||
if((f=_wfopen(buffer, L"rb")) != NULL)
|
||||
{
|
||||
TRACE("Opened %ls\n", buffer);
|
||||
return f;
|
||||
}
|
||||
WARN("Could not open %ls\n", buffer);
|
||||
}
|
||||
free(wname);
|
||||
free(wsubdir);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
#else
|
||||
FILE *OpenDataFile(const char *fname, const char *subdir)
|
||||
{
|
||||
char buffer[PATH_MAX] = "";
|
||||
@@ -561,7 +896,6 @@ FILE *OpenDataFile(const char *fname, const char *subdir)
|
||||
TRACE("Opened %s\n", fname);
|
||||
return f;
|
||||
}
|
||||
WARN("Could not open %s\n", fname);
|
||||
|
||||
if((str=getenv("XDG_DATA_HOME")) != NULL && str[0] != '\0')
|
||||
snprintf(buffer, sizeof(buffer), "%s/%s/%s", str, subdir, fname);
|
||||
@@ -574,7 +908,6 @@ FILE *OpenDataFile(const char *fname, const char *subdir)
|
||||
TRACE("Opened %s\n", buffer);
|
||||
return f;
|
||||
}
|
||||
WARN("Could not open %s\n", buffer);
|
||||
}
|
||||
|
||||
if((str=getenv("XDG_DATA_DIRS")) == NULL || str[0] == '\0')
|
||||
@@ -605,11 +938,221 @@ FILE *OpenDataFile(const char *fname, const char *subdir)
|
||||
TRACE("Opened %s\n", buffer);
|
||||
return f;
|
||||
}
|
||||
WARN("Could not open %s\n", buffer);
|
||||
}
|
||||
WARN("Could not open %s/%s\n", subdir, fname);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static const char *MatchString;
|
||||
static int MatchFilter(const struct dirent *dir)
|
||||
{
|
||||
const char *match = MatchString;
|
||||
const char *name = dir->d_name;
|
||||
int ret = 1;
|
||||
|
||||
do {
|
||||
const char *p = strchr(match, '%');
|
||||
if(!p)
|
||||
ret = strcmp(match, name) == 0;
|
||||
else
|
||||
{
|
||||
size_t len = p-match;
|
||||
ret = strncmp(match, name, len) == 0;
|
||||
if(ret)
|
||||
{
|
||||
match += len;
|
||||
name += len;
|
||||
|
||||
++p;
|
||||
if(*p == 'r')
|
||||
{
|
||||
char *end;
|
||||
ret = strtoul(name, &end, 10) > 0;
|
||||
if(ret) name = end;
|
||||
++p;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match = p;
|
||||
} while(ret && match && *match);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void RecurseDirectorySearch(const char *path, const char *match, vector_al_string *results)
|
||||
{
|
||||
struct dirent **namelist;
|
||||
char *sep, *p;
|
||||
int n, i;
|
||||
|
||||
if(!match[0])
|
||||
return;
|
||||
|
||||
sep = strrchr(match, '/');
|
||||
p = strchr(match, '%');
|
||||
|
||||
if(!sep)
|
||||
{
|
||||
MatchString = match;
|
||||
TRACE("Searching %s for %s\n", path?path:"/", match);
|
||||
n = scandir(path?path:"/", &namelist, MatchFilter, alphasort);
|
||||
if(n >= 0)
|
||||
{
|
||||
for(i = 0;i < n;++i)
|
||||
{
|
||||
al_string str = AL_STRING_INIT_STATIC();
|
||||
if(path) al_string_copy_cstr(&str, path);
|
||||
al_string_append_char(&str, '/');
|
||||
al_string_append_cstr(&str, namelist[i]->d_name);
|
||||
TRACE("Got result %s\n", al_string_get_cstr(str));
|
||||
VECTOR_PUSH_BACK(*results, str);
|
||||
free(namelist[i]);
|
||||
}
|
||||
free(namelist);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if(!p || p-sep >= 0)
|
||||
{
|
||||
al_string npath = AL_STRING_INIT_STATIC();
|
||||
if(path) al_string_append_cstr(&npath, path);
|
||||
al_string_append_char(&npath, '/');
|
||||
al_string_append_range(&npath, match, sep);
|
||||
|
||||
TRACE("Recursing into %s with %s\n", al_string_get_cstr(npath), sep+1);
|
||||
RecurseDirectorySearch(al_string_get_cstr(npath), sep+1, results);
|
||||
|
||||
al_string_deinit(&npath);
|
||||
return;
|
||||
}
|
||||
|
||||
sep = strchr(match, '/');
|
||||
if(sep-p >= 0) sep = NULL;
|
||||
for(;;)
|
||||
{
|
||||
char *next = strchr(sep?sep+1:match, '/');
|
||||
if(next-p < 0)
|
||||
{
|
||||
al_string npath = AL_STRING_INIT_STATIC();
|
||||
al_string nmatch = AL_STRING_INIT_STATIC();
|
||||
|
||||
if(!sep)
|
||||
{
|
||||
al_string_append_cstr(&npath, path?path:"/.");
|
||||
MatchString = match;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(path) al_string_append_cstr(&npath, path);
|
||||
al_string_append_char(&npath, '/');
|
||||
al_string_append_range(&npath, match, sep);
|
||||
|
||||
al_string_append_range(&nmatch, sep+1, next);
|
||||
MatchString = al_string_get_cstr(nmatch);
|
||||
}
|
||||
|
||||
TRACE("Searching %s for %s\n", al_string_get_cstr(npath), MatchString);
|
||||
n = scandir(al_string_get_cstr(npath), &namelist, MatchFilter, alphasort);
|
||||
if(n >= 0)
|
||||
{
|
||||
al_string ndir = AL_STRING_INIT_STATIC();
|
||||
for(i = 0;i < n;++i)
|
||||
{
|
||||
al_string_copy(&ndir, npath);
|
||||
al_string_append_char(&ndir, '/');
|
||||
al_string_append_cstr(&ndir, namelist[i]->d_name);
|
||||
free(namelist[i]);
|
||||
TRACE("Recursing %s with %s\n", al_string_get_cstr(ndir), next+1);
|
||||
RecurseDirectorySearch(al_string_get_cstr(ndir), next+1, results);
|
||||
}
|
||||
al_string_deinit(&ndir);
|
||||
free(namelist);
|
||||
}
|
||||
|
||||
al_string_deinit(&nmatch);
|
||||
al_string_deinit(&npath);
|
||||
break;
|
||||
}
|
||||
|
||||
sep = next;
|
||||
}
|
||||
}
|
||||
|
||||
vector_al_string SearchDataFiles(const char *match, const char *subdir)
|
||||
{
|
||||
static RefCount search_lock;
|
||||
vector_al_string results = VECTOR_INIT_STATIC();
|
||||
|
||||
while(ATOMIC_EXCHANGE(uint, &search_lock, 1) == 1)
|
||||
althrd_yield();
|
||||
|
||||
if(match[0] == '/')
|
||||
RecurseDirectorySearch(NULL, match+1, &results);
|
||||
else
|
||||
{
|
||||
al_string path = AL_STRING_INIT_STATIC();
|
||||
const char *str, *next;
|
||||
char cwdbuf[PATH_MAX];
|
||||
|
||||
// Search CWD
|
||||
if(!getcwd(cwdbuf, sizeof(cwdbuf)))
|
||||
strcpy(cwdbuf, ".");
|
||||
RecurseDirectorySearch(cwdbuf, match, &results);
|
||||
|
||||
// Search local data dir
|
||||
if((str=getenv("XDG_DATA_HOME")) != NULL && str[0] != '\0')
|
||||
{
|
||||
al_string_append_cstr(&path, str);
|
||||
al_string_append_char(&path, '/');
|
||||
al_string_append_cstr(&path, subdir);
|
||||
}
|
||||
else if((str=getenv("HOME")) != NULL && str[0] != '\0')
|
||||
{
|
||||
al_string_append_cstr(&path, str);
|
||||
al_string_append_cstr(&path, "/.local/share/");
|
||||
al_string_append_cstr(&path, subdir);
|
||||
}
|
||||
if(!al_string_empty(path))
|
||||
RecurseDirectorySearch(al_string_get_cstr(path), match, &results);
|
||||
|
||||
// Search global data dirs
|
||||
if((str=getenv("XDG_DATA_DIRS")) == NULL || str[0] == '\0')
|
||||
str = "/usr/local/share/:/usr/share/";
|
||||
|
||||
next = str;
|
||||
while((str=next) != NULL && str[0] != '\0')
|
||||
{
|
||||
next = strchr(str, ':');
|
||||
if(!next)
|
||||
al_string_copy_cstr(&path, str);
|
||||
else
|
||||
{
|
||||
al_string_clear(&path);
|
||||
al_string_append_range(&path, str, next);
|
||||
++next;
|
||||
}
|
||||
if(!al_string_empty(path))
|
||||
{
|
||||
al_string_append_char(&path, '/');
|
||||
al_string_append_cstr(&path, subdir);
|
||||
|
||||
RecurseDirectorySearch(al_string_get_cstr(path), match, &results);
|
||||
}
|
||||
}
|
||||
|
||||
al_string_deinit(&path);
|
||||
}
|
||||
|
||||
ATOMIC_STORE(&search_lock, 0);
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -638,25 +1181,20 @@ void SetRTPriority(void)
|
||||
}
|
||||
|
||||
|
||||
ALboolean vector_reserve(char *ptr, size_t base_size, size_t obj_size, ALsizei obj_count, ALboolean exact)
|
||||
ALboolean vector_reserve(char *ptr, size_t base_size, size_t obj_size, size_t obj_count, ALboolean exact)
|
||||
{
|
||||
vector_ *vecptr = (vector_*)ptr;
|
||||
if(obj_count < 0)
|
||||
return AL_FALSE;
|
||||
if((*vecptr ? (*vecptr)->Capacity : 0) < obj_count)
|
||||
{
|
||||
ALsizei old_size = (*vecptr ? (*vecptr)->Size : 0);
|
||||
size_t old_size = (*vecptr ? (*vecptr)->Size : 0);
|
||||
void *temp;
|
||||
|
||||
/* Use the next power-of-2 size if we don't need to allocate the exact
|
||||
* amount. This is preferred when regularly increasing the vector since
|
||||
* it means fewer reallocations. Though it means it also wastes some
|
||||
* memory. */
|
||||
if(exact == AL_FALSE)
|
||||
{
|
||||
if(exact == AL_FALSE && obj_count < INT_MAX)
|
||||
obj_count = NextPowerOf2((ALuint)obj_count);
|
||||
if(obj_count < 0) return AL_FALSE;
|
||||
}
|
||||
|
||||
/* Need to be explicit with the caller type's base size, because it
|
||||
* could have extra padding before the start of the array (that is,
|
||||
@@ -671,11 +1209,9 @@ ALboolean vector_reserve(char *ptr, size_t base_size, size_t obj_size, ALsizei o
|
||||
return AL_TRUE;
|
||||
}
|
||||
|
||||
ALboolean vector_resize(char *ptr, size_t base_size, size_t obj_size, ALsizei obj_count)
|
||||
ALboolean vector_resize(char *ptr, size_t base_size, size_t obj_size, size_t obj_count)
|
||||
{
|
||||
vector_ *vecptr = (vector_*)ptr;
|
||||
if(obj_count < 0)
|
||||
return AL_FALSE;
|
||||
if(*vecptr || obj_count > 0)
|
||||
{
|
||||
if(!vector_reserve((char*)vecptr, base_size, obj_size, obj_count, AL_TRUE))
|
||||
@@ -696,12 +1232,12 @@ ALboolean vector_insert(char *ptr, size_t base_size, size_t obj_size, void *ins_
|
||||
ptrdiff_t numins = ((const char*)datend - (const char*)datstart) / obj_size;
|
||||
|
||||
assert(numins > 0);
|
||||
if(INT_MAX-VECTOR_SIZE(*vecptr) <= numins ||
|
||||
if((size_t)numins + VECTOR_SIZE(*vecptr) < (size_t)numins ||
|
||||
!vector_reserve((char*)vecptr, base_size, obj_size, VECTOR_SIZE(*vecptr)+numins, AL_TRUE))
|
||||
return AL_FALSE;
|
||||
|
||||
/* NOTE: ins_pos may have been invalidated if *vecptr moved. Use ins_elem instead. */
|
||||
if(ins_elem < (*vecptr)->Size)
|
||||
if((size_t)ins_elem < (*vecptr)->Size)
|
||||
{
|
||||
memmove((char*)(*vecptr) + base_size + ((ins_elem+numins)*obj_size),
|
||||
(char*)(*vecptr) + base_size + ((ins_elem )*obj_size),
|
||||
@@ -709,14 +1245,14 @@ ALboolean vector_insert(char *ptr, size_t base_size, size_t obj_size, void *ins_
|
||||
}
|
||||
memcpy((char*)(*vecptr) + base_size + (ins_elem*obj_size),
|
||||
datstart, numins*obj_size);
|
||||
(*vecptr)->Size += (ALsizei)numins;
|
||||
(*vecptr)->Size += numins;
|
||||
}
|
||||
return AL_TRUE;
|
||||
}
|
||||
|
||||
|
||||
extern inline void al_string_deinit(al_string *str);
|
||||
extern inline ALsizei al_string_length(const_al_string str);
|
||||
extern inline size_t al_string_length(const_al_string str);
|
||||
extern inline ALboolean al_string_empty(const_al_string str);
|
||||
extern inline const al_string_char_type *al_string_get_cstr(const_al_string str);
|
||||
|
||||
@@ -730,10 +1266,10 @@ void al_string_clear(al_string *str)
|
||||
*VECTOR_ITER_END(*str) = 0;
|
||||
}
|
||||
|
||||
static inline int al_string_compare(const al_string_char_type *str1, ALsizei str1len,
|
||||
const al_string_char_type *str2, ALsizei str2len)
|
||||
static inline int al_string_compare(const al_string_char_type *str1, size_t str1len,
|
||||
const al_string_char_type *str2, size_t str2len)
|
||||
{
|
||||
ALsizei complen = mini(str1len, str2len);
|
||||
size_t complen = (str1len < str2len) ? str1len : str2len;
|
||||
int ret = memcmp(str1, str2, complen);
|
||||
if(ret == 0)
|
||||
{
|
||||
@@ -750,12 +1286,12 @@ int al_string_cmp(const_al_string str1, const_al_string str2)
|
||||
int al_string_cmp_cstr(const_al_string str1, const al_string_char_type *str2)
|
||||
{
|
||||
return al_string_compare(&VECTOR_FRONT(str1), al_string_length(str1),
|
||||
str2, (ALsizei)strlen(str2));
|
||||
str2, strlen(str2));
|
||||
}
|
||||
|
||||
void al_string_copy(al_string *str, const_al_string from)
|
||||
{
|
||||
ALsizei len = VECTOR_SIZE(from);
|
||||
size_t len = al_string_length(from);
|
||||
VECTOR_RESERVE(*str, len+1);
|
||||
VECTOR_RESIZE(*str, 0);
|
||||
VECTOR_INSERT(*str, VECTOR_ITER_END(*str), VECTOR_ITER_BEGIN(from), VECTOR_ITER_BEGIN(from)+len);
|
||||
@@ -811,4 +1347,30 @@ void al_string_copy_wcstr(al_string *str, const wchar_t *from)
|
||||
*VECTOR_ITER_END(*str) = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void al_string_append_wcstr(al_string *str, const wchar_t *from)
|
||||
{
|
||||
int len;
|
||||
if((len=WideCharToMultiByte(CP_UTF8, 0, from, -1, NULL, 0, NULL, NULL)) > 0)
|
||||
{
|
||||
size_t strlen = al_string_length(*str);
|
||||
VECTOR_RESERVE(*str, strlen+len);
|
||||
VECTOR_RESIZE(*str, strlen+len-1);
|
||||
WideCharToMultiByte(CP_UTF8, 0, from, -1, &VECTOR_FRONT(*str) + strlen, len, NULL, NULL);
|
||||
*VECTOR_ITER_END(*str) = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void al_string_append_wrange(al_string *str, const wchar_t *from, const wchar_t *to)
|
||||
{
|
||||
int len;
|
||||
if((len=WideCharToMultiByte(CP_UTF8, 0, from, (int)(to-from), NULL, 0, NULL, NULL)) > 0)
|
||||
{
|
||||
size_t strlen = al_string_length(*str);
|
||||
VECTOR_RESERVE(*str, strlen+len+1);
|
||||
VECTOR_RESIZE(*str, strlen+len);
|
||||
WideCharToMultiByte(CP_UTF8, 0, from, (int)(to-from), &VECTOR_FRONT(*str) + strlen, len+1, NULL, NULL);
|
||||
*VECTOR_ITER_END(*str) = 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
+257
-177
@@ -13,8 +13,8 @@
|
||||
*
|
||||
* 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., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
@@ -30,6 +30,8 @@
|
||||
#include "alu.h"
|
||||
#include "hrtf.h"
|
||||
|
||||
#include "compat.h"
|
||||
|
||||
|
||||
/* Current data set limits defined by the makehrtf utility. */
|
||||
#define MIN_IR_SIZE (8)
|
||||
@@ -52,6 +54,7 @@ struct Hrtf {
|
||||
const ALshort *coeffs;
|
||||
const ALubyte *delays;
|
||||
|
||||
al_string filename;
|
||||
struct Hrtf *next;
|
||||
};
|
||||
|
||||
@@ -82,45 +85,12 @@ static void CalcEvIndices(ALuint evcount, ALfloat ev, ALuint *evidx, ALfloat *ev
|
||||
*/
|
||||
static void CalcAzIndices(ALuint azcount, ALfloat az, ALuint *azidx, ALfloat *azmu)
|
||||
{
|
||||
az = (F_2PI + az) * azcount / (F_2PI);
|
||||
az = (F_TAU + az) * azcount / F_TAU;
|
||||
azidx[0] = fastf2u(az) % azcount;
|
||||
azidx[1] = (azidx[0] + 1) % azcount;
|
||||
*azmu = az - floorf(az);
|
||||
}
|
||||
|
||||
/* Calculates the normalized HRTF transition factor (delta) from the changes
|
||||
* in gain and listener to source angle between updates. The result is a
|
||||
* normalized delta factor that can be used to calculate moving HRIR stepping
|
||||
* values.
|
||||
*/
|
||||
ALfloat CalcHrtfDelta(ALfloat oldGain, ALfloat newGain, const ALfloat olddir[3], const ALfloat newdir[3])
|
||||
{
|
||||
ALfloat gainChange, angleChange, change;
|
||||
|
||||
// Calculate the normalized dB gain change.
|
||||
newGain = maxf(newGain, 0.0001f);
|
||||
oldGain = maxf(oldGain, 0.0001f);
|
||||
gainChange = fabsf(log10f(newGain / oldGain) / log10f(0.0001f));
|
||||
|
||||
// Calculate the angle change only when there is enough gain to notice it.
|
||||
angleChange = 0.0f;
|
||||
if(gainChange > 0.0001f || newGain > 0.0001f)
|
||||
{
|
||||
// No angle change when the directions are equal or degenerate (when
|
||||
// both have zero length).
|
||||
if(newdir[0] != olddir[0] || newdir[1] != olddir[1] || newdir[2] != olddir[2])
|
||||
{
|
||||
ALfloat dotp = olddir[0]*newdir[0] + olddir[1]*newdir[1] + olddir[2]*newdir[2];
|
||||
angleChange = acosf(clampf(dotp, -1.0f, 1.0f)) / F_PI;
|
||||
}
|
||||
}
|
||||
|
||||
// Use the largest of the two changes for the delta factor, and apply a
|
||||
// significance shaping function to it.
|
||||
change = maxf(angleChange * 25.0f, gainChange) * 2.0f;
|
||||
return minf(change, 1.0f);
|
||||
}
|
||||
|
||||
/* Calculates static HRIR coefficients and delays for the given polar
|
||||
* elevation and azimuth in radians. Linear interpolation is used to
|
||||
* increase the apparent resolution of the HRIR data set. The coefficients
|
||||
@@ -183,24 +153,22 @@ void GetLerpedHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azi
|
||||
{
|
||||
ALfloat c;
|
||||
|
||||
gain *= 1.0f/32767.0f;
|
||||
|
||||
i = 0;
|
||||
c = (Hrtf->coeffs[lidx[0]+i]*blend[0] + Hrtf->coeffs[lidx[1]+i]*blend[1] +
|
||||
Hrtf->coeffs[lidx[2]+i]*blend[2] + Hrtf->coeffs[lidx[3]+i]*blend[3]);
|
||||
coeffs[i][0] = lerp(PassthruCoeff, c, dirfact) * gain;
|
||||
coeffs[i][0] = lerp(PassthruCoeff, c, dirfact) * gain * (1.0f/32767.0f);
|
||||
c = (Hrtf->coeffs[ridx[0]+i]*blend[0] + Hrtf->coeffs[ridx[1]+i]*blend[1] +
|
||||
Hrtf->coeffs[ridx[2]+i]*blend[2] + Hrtf->coeffs[ridx[3]+i]*blend[3]);
|
||||
coeffs[i][1] = lerp(PassthruCoeff, c, dirfact) * gain;
|
||||
coeffs[i][1] = lerp(PassthruCoeff, c, dirfact) * gain * (1.0f/32767.0f);
|
||||
|
||||
for(i = 1;i < Hrtf->irSize;i++)
|
||||
{
|
||||
c = (Hrtf->coeffs[lidx[0]+i]*blend[0] + Hrtf->coeffs[lidx[1]+i]*blend[1] +
|
||||
Hrtf->coeffs[lidx[2]+i]*blend[2] + Hrtf->coeffs[lidx[3]+i]*blend[3]);
|
||||
coeffs[i][0] = lerp(0.0f, c, dirfact) * gain;
|
||||
coeffs[i][0] = lerp(0.0f, c, dirfact) * gain * (1.0f/32767.0f);
|
||||
c = (Hrtf->coeffs[ridx[0]+i]*blend[0] + Hrtf->coeffs[ridx[1]+i]*blend[1] +
|
||||
Hrtf->coeffs[ridx[2]+i]*blend[2] + Hrtf->coeffs[ridx[3]+i]*blend[3]);
|
||||
coeffs[i][1] = lerp(0.0f, c, dirfact) * gain;
|
||||
coeffs[i][1] = lerp(0.0f, c, dirfact) * gain * (1.0f/32767.0f);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -225,7 +193,7 @@ ALuint GetMovingHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat a
|
||||
ALuint evidx[2], lidx[4], ridx[4];
|
||||
ALfloat mu[3], blend[4];
|
||||
ALfloat left, right;
|
||||
ALfloat step;
|
||||
ALfloat steps;
|
||||
ALuint i;
|
||||
|
||||
/* Claculate elevation indices and interpolation factor. */
|
||||
@@ -248,8 +216,8 @@ ALuint GetMovingHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat a
|
||||
}
|
||||
|
||||
// Calculate the stepping parameters.
|
||||
delta = maxf(floorf(delta*(Hrtf->sampleRate*0.015f) + 0.5f), 1.0f);
|
||||
step = 1.0f / delta;
|
||||
steps = maxf(floorf(delta*Hrtf->sampleRate + 0.5f), 1.0f);
|
||||
delta = 1.0f / steps;
|
||||
|
||||
/* Calculate 4 blending weights for 2D bilinear interpolation. */
|
||||
blend[0] = (1.0f-mu[0]) * (1.0f-mu[2]);
|
||||
@@ -271,8 +239,8 @@ ALuint GetMovingHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat a
|
||||
Hrtf->delays[ridx[2]]*blend[2] + Hrtf->delays[ridx[3]]*blend[3]) *
|
||||
dirfact + 0.5f) << HRTFDELAY_BITS;
|
||||
|
||||
delayStep[0] = fastf2i(step * (delays[0] - left));
|
||||
delayStep[1] = fastf2i(step * (delays[1] - right));
|
||||
delayStep[0] = fastf2i(delta * (delays[0] - left));
|
||||
delayStep[1] = fastf2i(delta * (delays[1] - right));
|
||||
|
||||
/* Calculate the sample offsets for the HRIR indices. */
|
||||
lidx[0] *= Hrtf->irSize;
|
||||
@@ -294,21 +262,19 @@ ALuint GetMovingHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat a
|
||||
{
|
||||
ALfloat c;
|
||||
|
||||
gain *= 1.0f/32767.0f;
|
||||
|
||||
i = 0;
|
||||
left = coeffs[i][0] - (coeffStep[i][0] * counter);
|
||||
right = coeffs[i][1] - (coeffStep[i][1] * counter);
|
||||
|
||||
c = (Hrtf->coeffs[lidx[0]+i]*blend[0] + Hrtf->coeffs[lidx[1]+i]*blend[1] +
|
||||
Hrtf->coeffs[lidx[2]+i]*blend[2] + Hrtf->coeffs[lidx[3]+i]*blend[3]);
|
||||
coeffs[i][0] = lerp(PassthruCoeff, c, dirfact) * gain;
|
||||
coeffs[i][0] = lerp(PassthruCoeff, c, dirfact) * gain * (1.0f/32767.0f);
|
||||
c = (Hrtf->coeffs[ridx[0]+i]*blend[0] + Hrtf->coeffs[ridx[1]+i]*blend[1] +
|
||||
Hrtf->coeffs[ridx[2]+i]*blend[2] + Hrtf->coeffs[ridx[3]+i]*blend[3]);
|
||||
coeffs[i][1] = lerp(PassthruCoeff, c, dirfact) * gain;
|
||||
coeffs[i][1] = lerp(PassthruCoeff, c, dirfact) * gain * (1.0f/32767.0f);
|
||||
|
||||
coeffStep[i][0] = step * (coeffs[i][0] - left);
|
||||
coeffStep[i][1] = step * (coeffs[i][1] - right);
|
||||
coeffStep[i][0] = delta * (coeffs[i][0] - left);
|
||||
coeffStep[i][1] = delta * (coeffs[i][1] - right);
|
||||
|
||||
for(i = 1;i < Hrtf->irSize;i++)
|
||||
{
|
||||
@@ -317,13 +283,13 @@ ALuint GetMovingHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat a
|
||||
|
||||
c = (Hrtf->coeffs[lidx[0]+i]*blend[0] + Hrtf->coeffs[lidx[1]+i]*blend[1] +
|
||||
Hrtf->coeffs[lidx[2]+i]*blend[2] + Hrtf->coeffs[lidx[3]+i]*blend[3]);
|
||||
coeffs[i][0] = lerp(0.0f, c, dirfact) * gain;
|
||||
coeffs[i][0] = lerp(0.0f, c, dirfact) * gain * (1.0f/32767.0f);
|
||||
c = (Hrtf->coeffs[ridx[0]+i]*blend[0] + Hrtf->coeffs[ridx[1]+i]*blend[1] +
|
||||
Hrtf->coeffs[ridx[2]+i]*blend[2] + Hrtf->coeffs[ridx[3]+i]*blend[3]);
|
||||
coeffs[i][1] = lerp(0.0f, c, dirfact) * gain;
|
||||
coeffs[i][1] = lerp(0.0f, c, dirfact) * gain * (1.0f/32767.0f);
|
||||
|
||||
coeffStep[i][0] = step * (coeffs[i][0] - left);
|
||||
coeffStep[i][1] = step * (coeffs[i][1] - right);
|
||||
coeffStep[i][0] = delta * (coeffs[i][0] - left);
|
||||
coeffStep[i][1] = delta * (coeffs[i][1] - right);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -336,8 +302,8 @@ ALuint GetMovingHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat a
|
||||
coeffs[i][0] = 0.0f;
|
||||
coeffs[i][1] = 0.0f;
|
||||
|
||||
coeffStep[i][0] = step * -left;
|
||||
coeffStep[i][1] = step * -right;
|
||||
coeffStep[i][0] = delta * -left;
|
||||
coeffStep[i][1] = delta * -right;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -345,13 +311,118 @@ ALuint GetMovingHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat a
|
||||
* complete its transition. The mixer will only apply stepping for this
|
||||
* many samples.
|
||||
*/
|
||||
return fastf2u(delta);
|
||||
return fastf2u(steps);
|
||||
}
|
||||
|
||||
|
||||
static struct Hrtf *LoadHrtf00(FILE *f, ALuint deviceRate)
|
||||
/* Calculates HRTF coefficients for B-Format channels (only up to first-order).
|
||||
* Note that these will decode a B-Format output mix, which uses FuMa ordering
|
||||
* and scaling, not N3D!
|
||||
*/
|
||||
void GetBFormatHrtfCoeffs(const struct Hrtf *Hrtf, const ALuint num_chans, ALfloat (**coeffs_list)[2], ALuint **delay_list)
|
||||
{
|
||||
const ALubyte maxDelay = SRC_HISTORY_LENGTH-1;
|
||||
ALuint elev_idx, azi_idx;
|
||||
ALfloat scale;
|
||||
ALuint i, c;
|
||||
|
||||
assert(num_chans <= 4);
|
||||
|
||||
for(c = 0;c < num_chans;c++)
|
||||
{
|
||||
ALfloat (*coeffs)[2] = coeffs_list[c];
|
||||
ALuint *delay = delay_list[c];
|
||||
|
||||
for(i = 0;i < Hrtf->irSize;i++)
|
||||
{
|
||||
coeffs[i][0] = 0.0f;
|
||||
coeffs[i][1] = 0.0f;
|
||||
}
|
||||
delay[0] = 0;
|
||||
delay[1] = 0;
|
||||
}
|
||||
|
||||
/* NOTE: HRTF coefficients are generated by combining all the HRIRs in the
|
||||
* dataset, with each entry scaled according to how much it contributes to
|
||||
* the given B-Format channel based on its direction (including negative
|
||||
* contributions!).
|
||||
*/
|
||||
scale = 0.0f;
|
||||
for(elev_idx = 0;elev_idx < Hrtf->evCount;elev_idx++)
|
||||
{
|
||||
ALfloat elev = (ALfloat)elev_idx/(ALfloat)(Hrtf->evCount-1)*F_PI - F_PI_2;
|
||||
ALuint evoffset = Hrtf->evOffset[elev_idx];
|
||||
ALuint azcount = Hrtf->azCount[elev_idx];
|
||||
|
||||
scale += (ALfloat)azcount;
|
||||
|
||||
for(azi_idx = 0;azi_idx < azcount;azi_idx++)
|
||||
{
|
||||
ALuint lidx, ridx;
|
||||
ALfloat ambi_coeffs[4];
|
||||
ALfloat az, gain;
|
||||
ALfloat x, y, z;
|
||||
|
||||
lidx = evoffset + azi_idx;
|
||||
ridx = evoffset + ((azcount-azi_idx) % azcount);
|
||||
|
||||
az = (ALfloat)azi_idx / (ALfloat)azcount * F_TAU;
|
||||
if(az > F_PI) az -= F_TAU;
|
||||
|
||||
x = cosf(-az) * cosf(elev);
|
||||
y = sinf(-az) * cosf(elev);
|
||||
z = sinf(elev);
|
||||
|
||||
ambi_coeffs[0] = 1.414213562f;
|
||||
ambi_coeffs[1] = x;
|
||||
ambi_coeffs[2] = y;
|
||||
ambi_coeffs[3] = z;
|
||||
|
||||
for(c = 0;c < num_chans;c++)
|
||||
{
|
||||
ALfloat (*coeffs)[2] = coeffs_list[c];
|
||||
ALuint *delay = delay_list[c];
|
||||
|
||||
/* NOTE: Always include the total delay average since the
|
||||
* channels need to have matching delays. */
|
||||
delay[0] += Hrtf->delays[lidx];
|
||||
delay[1] += Hrtf->delays[ridx];
|
||||
|
||||
gain = ambi_coeffs[c];
|
||||
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
|
||||
continue;
|
||||
|
||||
for(i = 0;i < Hrtf->irSize;i++)
|
||||
{
|
||||
coeffs[i][0] += Hrtf->coeffs[lidx*Hrtf->irSize + i]*(1.0f/32767.0f) * gain;
|
||||
coeffs[i][1] += Hrtf->coeffs[ridx*Hrtf->irSize + i]*(1.0f/32767.0f) * gain;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scale = 1.0f/scale;
|
||||
|
||||
for(c = 0;c < num_chans;c++)
|
||||
{
|
||||
ALfloat (*coeffs)[2] = coeffs_list[c];
|
||||
ALuint *delay = delay_list[c];
|
||||
|
||||
for(i = 0;i < Hrtf->irSize;i++)
|
||||
{
|
||||
coeffs[i][0] *= scale;
|
||||
coeffs[i][1] *= scale;
|
||||
}
|
||||
delay[0] = minu((ALuint)((ALfloat)delay[0] * scale), HRTF_HISTORY_LENGTH-1);
|
||||
delay[0] <<= HRTFDELAY_BITS;
|
||||
delay[1] = minu((ALuint)((ALfloat)delay[1] * scale), HRTF_HISTORY_LENGTH-1);
|
||||
delay[1] <<= HRTFDELAY_BITS;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static struct Hrtf *LoadHrtf00(FILE *f)
|
||||
{
|
||||
const ALubyte maxDelay = HRTF_HISTORY_LENGTH-1;
|
||||
struct Hrtf *Hrtf = NULL;
|
||||
ALboolean failed = AL_FALSE;
|
||||
ALuint rate = 0, irCount = 0;
|
||||
@@ -376,12 +447,6 @@ static struct Hrtf *LoadHrtf00(FILE *f, ALuint deviceRate)
|
||||
|
||||
evCount = fgetc(f);
|
||||
|
||||
if(rate != deviceRate)
|
||||
{
|
||||
ERR("HRIR rate does not match device rate: rate=%d (%d)\n",
|
||||
rate, deviceRate);
|
||||
failed = AL_TRUE;
|
||||
}
|
||||
if(irSize < MIN_IR_SIZE || irSize > MAX_IR_SIZE || (irSize%MOD_IR_SIZE))
|
||||
{
|
||||
ERR("Unsupported HRIR size: irSize=%d (%d to %d by %d)\n",
|
||||
@@ -504,6 +569,7 @@ static struct Hrtf *LoadHrtf00(FILE *f, ALuint deviceRate)
|
||||
Hrtf->evOffset = evOffset;
|
||||
Hrtf->coeffs = coeffs;
|
||||
Hrtf->delays = delays;
|
||||
AL_STRING_INIT(Hrtf->filename);
|
||||
Hrtf->next = NULL;
|
||||
return Hrtf;
|
||||
}
|
||||
@@ -516,9 +582,9 @@ static struct Hrtf *LoadHrtf00(FILE *f, ALuint deviceRate)
|
||||
}
|
||||
|
||||
|
||||
static struct Hrtf *LoadHrtf01(FILE *f, ALuint deviceRate)
|
||||
static struct Hrtf *LoadHrtf01(FILE *f)
|
||||
{
|
||||
const ALubyte maxDelay = SRC_HISTORY_LENGTH-1;
|
||||
const ALubyte maxDelay = HRTF_HISTORY_LENGTH-1;
|
||||
struct Hrtf *Hrtf = NULL;
|
||||
ALboolean failed = AL_FALSE;
|
||||
ALuint rate = 0, irCount = 0;
|
||||
@@ -538,12 +604,6 @@ static struct Hrtf *LoadHrtf01(FILE *f, ALuint deviceRate)
|
||||
|
||||
evCount = fgetc(f);
|
||||
|
||||
if(rate != deviceRate)
|
||||
{
|
||||
ERR("HRIR rate does not match device rate: rate=%d (%d)\n",
|
||||
rate, deviceRate);
|
||||
failed = AL_TRUE;
|
||||
}
|
||||
if(irSize < MIN_IR_SIZE || irSize > MAX_IR_SIZE || (irSize%MOD_IR_SIZE))
|
||||
{
|
||||
ERR("Unsupported HRIR size: irSize=%d (%d to %d by %d)\n",
|
||||
@@ -649,6 +709,7 @@ static struct Hrtf *LoadHrtf01(FILE *f, ALuint deviceRate)
|
||||
Hrtf->evOffset = evOffset;
|
||||
Hrtf->coeffs = coeffs;
|
||||
Hrtf->delays = delays;
|
||||
AL_STRING_INIT(Hrtf->filename);
|
||||
Hrtf->next = NULL;
|
||||
return Hrtf;
|
||||
}
|
||||
@@ -661,144 +722,167 @@ static struct Hrtf *LoadHrtf01(FILE *f, ALuint deviceRate)
|
||||
}
|
||||
|
||||
|
||||
static struct Hrtf *LoadHrtf(ALuint deviceRate)
|
||||
static void AddFileEntry(vector_HrtfEntry *list, al_string *filename)
|
||||
{
|
||||
const char *fnamelist = "default-%r.mhr";
|
||||
HrtfEntry entry = { AL_STRING_INIT_STATIC(), *filename, NULL };
|
||||
HrtfEntry *iter;
|
||||
const char *name;
|
||||
int i;
|
||||
|
||||
ConfigValueStr(NULL, "hrtf_tables", &fnamelist);
|
||||
while(*fnamelist != '\0')
|
||||
name = strrchr(al_string_get_cstr(entry.filename), '/');
|
||||
if(!name) name = strrchr(al_string_get_cstr(entry.filename), '\\');
|
||||
if(!name) name = al_string_get_cstr(entry.filename);
|
||||
else ++name;
|
||||
|
||||
entry.hrtf = LoadedHrtfs;
|
||||
while(entry.hrtf)
|
||||
{
|
||||
struct Hrtf *Hrtf = NULL;
|
||||
char fname[PATH_MAX];
|
||||
const char *next;
|
||||
if(al_string_cmp(entry.filename, entry.hrtf->filename) == 0)
|
||||
break;
|
||||
entry.hrtf = entry.hrtf->next;
|
||||
}
|
||||
|
||||
if(!entry.hrtf)
|
||||
{
|
||||
struct Hrtf *hrtf = NULL;
|
||||
ALchar magic[8];
|
||||
ALuint i;
|
||||
FILE *f;
|
||||
|
||||
i = 0;
|
||||
while(isspace(*fnamelist) || *fnamelist == ',')
|
||||
fnamelist++;
|
||||
next = fnamelist;
|
||||
while(*(fnamelist=next) != '\0' && *fnamelist != ',')
|
||||
{
|
||||
next = strpbrk(fnamelist, "%,");
|
||||
while(fnamelist != next && *fnamelist && i < sizeof(fname))
|
||||
fname[i++] = *(fnamelist++);
|
||||
|
||||
if(!next || *next == ',')
|
||||
break;
|
||||
|
||||
/* *next == '%' */
|
||||
next++;
|
||||
if(*next == 'r')
|
||||
{
|
||||
int wrote = snprintf(&fname[i], sizeof(fname)-i, "%u", deviceRate);
|
||||
i += minu(wrote, sizeof(fname)-i);
|
||||
next++;
|
||||
}
|
||||
else if(*next == '%')
|
||||
{
|
||||
if(i < sizeof(fname))
|
||||
fname[i++] = '%';
|
||||
next++;
|
||||
}
|
||||
else
|
||||
ERR("Invalid marker '%%%c'\n", *next);
|
||||
}
|
||||
i = minu(i, sizeof(fname)-1);
|
||||
fname[i] = '\0';
|
||||
while(i > 0 && isspace(fname[i-1]))
|
||||
i--;
|
||||
fname[i] = '\0';
|
||||
|
||||
if(fname[0] == '\0')
|
||||
continue;
|
||||
|
||||
TRACE("Loading %s...\n", fname);
|
||||
f = OpenDataFile(fname, "openal/hrtf");
|
||||
TRACE("Loading %s...\n", al_string_get_cstr(entry.filename));
|
||||
f = al_fopen(al_string_get_cstr(entry.filename), "rb");
|
||||
if(f == NULL)
|
||||
{
|
||||
ERR("Could not open %s\n", fname);
|
||||
continue;
|
||||
ERR("Could not open %s\n", al_string_get_cstr(entry.filename));
|
||||
goto error;
|
||||
}
|
||||
|
||||
if(fread(magic, 1, sizeof(magic), f) != sizeof(magic))
|
||||
ERR("Failed to read header from %s\n", fname);
|
||||
ERR("Failed to read header from %s\n", al_string_get_cstr(entry.filename));
|
||||
else
|
||||
{
|
||||
if(memcmp(magic, magicMarker00, sizeof(magicMarker00)) == 0)
|
||||
{
|
||||
TRACE("Detected data set format v0\n");
|
||||
Hrtf = LoadHrtf00(f, deviceRate);
|
||||
hrtf = LoadHrtf00(f);
|
||||
}
|
||||
else if(memcmp(magic, magicMarker01, sizeof(magicMarker01)) == 0)
|
||||
{
|
||||
TRACE("Detected data set format v1\n");
|
||||
Hrtf = LoadHrtf01(f, deviceRate);
|
||||
hrtf = LoadHrtf01(f);
|
||||
}
|
||||
else
|
||||
ERR("Invalid header in %s: \"%.8s\"\n", fname, magic);
|
||||
ERR("Invalid header in %s: \"%.8s\"\n", al_string_get_cstr(entry.filename), magic);
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
f = NULL;
|
||||
|
||||
if(Hrtf)
|
||||
if(!hrtf)
|
||||
{
|
||||
Hrtf->next = LoadedHrtfs;
|
||||
LoadedHrtfs = Hrtf;
|
||||
TRACE("Loaded HRTF support for format: %s %uhz\n",
|
||||
DevFmtChannelsString(DevFmtStereo), Hrtf->sampleRate);
|
||||
return Hrtf;
|
||||
ERR("Failed to load %s\n", al_string_get_cstr(entry.filename));
|
||||
goto error;
|
||||
}
|
||||
|
||||
ERR("Failed to load %s\n", fname);
|
||||
al_string_copy(&hrtf->filename, entry.filename);
|
||||
hrtf->next = LoadedHrtfs;
|
||||
LoadedHrtfs = hrtf;
|
||||
TRACE("Loaded HRTF support for format: %s %uhz\n",
|
||||
DevFmtChannelsString(DevFmtStereo), hrtf->sampleRate);
|
||||
entry.hrtf = hrtf;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
/* TODO: Get a human-readable name from the HRTF data (possibly coming in a
|
||||
* format update). */
|
||||
|
||||
const struct Hrtf *GetHrtf(enum DevFmtChannels chans, ALCuint srate)
|
||||
{
|
||||
if(chans == DevFmtStereo)
|
||||
{
|
||||
struct Hrtf *Hrtf = LoadedHrtfs;
|
||||
while(Hrtf != NULL)
|
||||
i = 0;
|
||||
do {
|
||||
al_string_copy_cstr(&entry.name, name);
|
||||
if(i != 0)
|
||||
{
|
||||
if(srate == Hrtf->sampleRate)
|
||||
return Hrtf;
|
||||
Hrtf = Hrtf->next;
|
||||
char str[64];
|
||||
snprintf(str, sizeof(str), " #%d", i+1);
|
||||
al_string_append_cstr(&entry.name, str);
|
||||
}
|
||||
++i;
|
||||
|
||||
Hrtf = LoadHrtf(srate);
|
||||
if(Hrtf != NULL)
|
||||
return Hrtf;
|
||||
}
|
||||
ERR("Incompatible format: %s %uhz\n", DevFmtChannelsString(chans), srate);
|
||||
return NULL;
|
||||
#define MATCH_NAME(i) (al_string_cmp(entry.name, (i)->name) == 0)
|
||||
VECTOR_FIND_IF(iter, HrtfEntry, *list, MATCH_NAME);
|
||||
#undef MATCH_NAME
|
||||
} while(iter != VECTOR_ITER_END(*list));
|
||||
|
||||
TRACE("Adding entry \"%s\" from file \"%s\"\n", al_string_get_cstr(entry.name),
|
||||
al_string_get_cstr(entry.filename));
|
||||
VECTOR_PUSH_BACK(*list, entry);
|
||||
return;
|
||||
|
||||
error:
|
||||
al_string_deinit(&entry.filename);
|
||||
}
|
||||
|
||||
ALCboolean FindHrtfFormat(enum DevFmtChannels *chans, ALCuint *srate)
|
||||
vector_HrtfEntry EnumerateHrtf(const_al_string devname)
|
||||
{
|
||||
const struct Hrtf *hrtf = LoadedHrtfs;
|
||||
while(hrtf != NULL)
|
||||
vector_HrtfEntry list = VECTOR_INIT_STATIC();
|
||||
const char *fnamelist = "default-%r.mhr";
|
||||
|
||||
ConfigValueStr(al_string_get_cstr(devname), NULL, "hrtf_tables", &fnamelist);
|
||||
while(fnamelist && *fnamelist)
|
||||
{
|
||||
if(*srate == hrtf->sampleRate)
|
||||
break;
|
||||
hrtf = hrtf->next;
|
||||
while(isspace(*fnamelist) || *fnamelist == ',')
|
||||
fnamelist++;
|
||||
if(*fnamelist != '\0')
|
||||
{
|
||||
const char *next, *end;
|
||||
|
||||
next = strchr(fnamelist, ',');
|
||||
if(!next)
|
||||
end = fnamelist + strlen(fnamelist);
|
||||
else
|
||||
end = next++;
|
||||
|
||||
while(end != fnamelist && isspace(*(end-1)))
|
||||
--end;
|
||||
if(end != fnamelist)
|
||||
{
|
||||
al_string fname = AL_STRING_INIT_STATIC();
|
||||
vector_al_string flist;
|
||||
|
||||
al_string_append_range(&fname, fnamelist, end);
|
||||
|
||||
flist = SearchDataFiles(al_string_get_cstr(fname), "openal/hrtf");
|
||||
VECTOR_FOR_EACH_PARAMS(al_string, flist, AddFileEntry, &list);
|
||||
VECTOR_DEINIT(flist);
|
||||
|
||||
al_string_deinit(&fname);
|
||||
}
|
||||
|
||||
fnamelist = next;
|
||||
}
|
||||
}
|
||||
|
||||
if(hrtf == NULL)
|
||||
{
|
||||
hrtf = LoadHrtf(*srate);
|
||||
if(hrtf == NULL) return ALC_FALSE;
|
||||
}
|
||||
|
||||
*chans = DevFmtStereo;
|
||||
*srate = hrtf->sampleRate;
|
||||
return ALC_TRUE;
|
||||
return list;
|
||||
}
|
||||
|
||||
void FreeHrtfList(vector_HrtfEntry *list)
|
||||
{
|
||||
#define CLEAR_ENTRY(i) do { \
|
||||
al_string_deinit(&(i)->name); \
|
||||
al_string_deinit(&(i)->filename); \
|
||||
} while(0)
|
||||
VECTOR_FOR_EACH(HrtfEntry, *list, CLEAR_ENTRY);
|
||||
VECTOR_DEINIT(*list);
|
||||
#undef CLEAR_ENTRY
|
||||
}
|
||||
|
||||
|
||||
ALuint GetHrtfSampleRate(const struct Hrtf *Hrtf)
|
||||
{
|
||||
return Hrtf->sampleRate;
|
||||
}
|
||||
|
||||
ALuint GetHrtfIrSize(const struct Hrtf *Hrtf)
|
||||
{
|
||||
return Hrtf->irSize;
|
||||
}
|
||||
|
||||
|
||||
void FreeHrtfs(void)
|
||||
{
|
||||
struct Hrtf *Hrtf = NULL;
|
||||
@@ -810,11 +894,7 @@ void FreeHrtfs(void)
|
||||
free((void*)Hrtf->evOffset);
|
||||
free((void*)Hrtf->coeffs);
|
||||
free((void*)Hrtf->delays);
|
||||
al_string_deinit(&Hrtf->filename);
|
||||
free(Hrtf);
|
||||
}
|
||||
}
|
||||
|
||||
ALuint GetHrtfIrSize (const struct Hrtf *Hrtf)
|
||||
{
|
||||
return Hrtf->irSize;
|
||||
}
|
||||
|
||||
@@ -4,10 +4,20 @@
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
|
||||
#include "alstring.h"
|
||||
|
||||
enum DevFmtChannels;
|
||||
|
||||
struct Hrtf;
|
||||
|
||||
typedef struct HrtfEntry {
|
||||
al_string name;
|
||||
al_string filename;
|
||||
|
||||
const struct Hrtf *hrtf;
|
||||
} HrtfEntry;
|
||||
TYPEDEF_VECTOR(HrtfEntry, vector_HrtfEntry)
|
||||
|
||||
#define HRIR_BITS (7)
|
||||
#define HRIR_LENGTH (1<<HRIR_BITS)
|
||||
#define HRIR_MASK (HRIR_LENGTH-1)
|
||||
@@ -15,14 +25,16 @@ struct Hrtf;
|
||||
#define HRTFDELAY_FRACONE (1<<HRTFDELAY_BITS)
|
||||
#define HRTFDELAY_MASK (HRTFDELAY_FRACONE-1)
|
||||
|
||||
const struct Hrtf *GetHrtf(enum DevFmtChannels chans, ALCuint srate);
|
||||
ALCboolean FindHrtfFormat(enum DevFmtChannels *chans, ALCuint *srate);
|
||||
|
||||
void FreeHrtfs(void);
|
||||
|
||||
vector_HrtfEntry EnumerateHrtf(const_al_string devname);
|
||||
void FreeHrtfList(vector_HrtfEntry *list);
|
||||
|
||||
ALuint GetHrtfSampleRate(const struct Hrtf *Hrtf);
|
||||
ALuint GetHrtfIrSize(const struct Hrtf *Hrtf);
|
||||
ALfloat CalcHrtfDelta(ALfloat oldGain, ALfloat newGain, const ALfloat olddir[3], const ALfloat newdir[3]);
|
||||
|
||||
void GetLerpedHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azimuth, ALfloat dirfact, ALfloat gain, ALfloat (*coeffs)[2], ALuint *delays);
|
||||
ALuint GetMovingHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azimuth, ALfloat dirfact, ALfloat gain, ALfloat delta, ALint counter, ALfloat (*coeffs)[2], ALuint *delays, ALfloat (*coeffStep)[2], ALint *delayStep);
|
||||
void GetBFormatHrtfCoeffs(const struct Hrtf *Hrtf, const ALuint num_chans, ALfloat (**coeffs_list)[2], ALuint **delay_list);
|
||||
|
||||
#endif /* ALC_HRTF_H */
|
||||
|
||||
+253
-125
@@ -13,8 +13,8 @@
|
||||
*
|
||||
* 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., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
@@ -38,8 +38,32 @@
|
||||
#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(ALuint frac, ALuint increment, ALuint *frac_arr, ALuint *pos_arr, ALuint size);
|
||||
|
||||
alignas(16) union ResamplerCoeffs ResampleCoeffs;
|
||||
|
||||
|
||||
enum Resampler {
|
||||
PointResampler,
|
||||
LinearResampler,
|
||||
FIR4Resampler,
|
||||
FIR8Resampler,
|
||||
BSincResampler,
|
||||
|
||||
ResamplerDefault = LinearResampler
|
||||
};
|
||||
|
||||
/* FIR8 requires 3 extra samples before the current position, and 4 after. */
|
||||
static_assert(MAX_PRE_SAMPLES >= 3, "MAX_PRE_SAMPLES must be at least 3!");
|
||||
static_assert(MAX_POST_SAMPLES >= 4, "MAX_POST_SAMPLES must be at least 4!");
|
||||
|
||||
|
||||
static HrtfMixerFunc MixHrtfSamples = MixHrtf_C;
|
||||
static MixerFunc MixSamples = Mix_C;
|
||||
static ResamplerFunc ResampleSamples = Resample_point32_C;
|
||||
|
||||
static inline HrtfMixerFunc SelectHrtfMixer(void)
|
||||
{
|
||||
@@ -69,11 +93,9 @@ static inline MixerFunc SelectMixer(void)
|
||||
return Mix_C;
|
||||
}
|
||||
|
||||
static inline ResamplerFunc SelectResampler(enum Resampler Resampler, ALuint increment)
|
||||
static inline ResamplerFunc SelectResampler(enum Resampler resampler)
|
||||
{
|
||||
if(increment == FRACTIONONE)
|
||||
return Resample_copy32_C;
|
||||
switch(Resampler)
|
||||
switch(resampler)
|
||||
{
|
||||
case PointResampler:
|
||||
return Resample_point32_C;
|
||||
@@ -87,17 +109,181 @@ static inline ResamplerFunc SelectResampler(enum Resampler Resampler, ALuint inc
|
||||
return Resample_lerp32_SSE2;
|
||||
#endif
|
||||
return Resample_lerp32_C;
|
||||
case CubicResampler:
|
||||
return Resample_cubic32_C;
|
||||
case ResamplerMax:
|
||||
/* Shouldn't happen */
|
||||
break;
|
||||
case FIR4Resampler:
|
||||
#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 FIR8Resampler:
|
||||
#ifdef HAVE_SSE4_1
|
||||
if((CPUCapFlags&CPU_CAP_SSE4_1))
|
||||
return Resample_fir8_32_SSE41;
|
||||
#endif
|
||||
#ifdef HAVE_SSE3
|
||||
if((CPUCapFlags&CPU_CAP_SSE3))
|
||||
return Resample_fir8_32_SSE3;
|
||||
#endif
|
||||
return Resample_fir8_32_C;
|
||||
case BSincResampler:
|
||||
#ifdef HAVE_SSE
|
||||
if((CPUCapFlags&CPU_CAP_SSE))
|
||||
return Resample_bsinc32_SSE;
|
||||
#endif
|
||||
return Resample_bsinc32_C;
|
||||
}
|
||||
|
||||
return Resample_point32_C;
|
||||
}
|
||||
|
||||
|
||||
/* The sinc resampler makes use of a Kaiser window to limit the needed sample
|
||||
* points to 4 and 8, respectively.
|
||||
*/
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI (3.14159265358979323846)
|
||||
#endif
|
||||
static inline double Sinc(double x)
|
||||
{
|
||||
if(x == 0.0) return 1.0;
|
||||
return sin(x*M_PI) / (x*M_PI);
|
||||
}
|
||||
|
||||
/* The zero-order modified Bessel function of the first kind, used for the
|
||||
* Kaiser window.
|
||||
*
|
||||
* I_0(x) = sum_{k=0}^inf (1 / k!)^2 (x / 2)^(2 k)
|
||||
* = sum_{k=0}^inf ((x / 2)^k / k!)^2
|
||||
*/
|
||||
static double BesselI_0(double x)
|
||||
{
|
||||
double term, sum, x2, y, last_sum;
|
||||
int k;
|
||||
|
||||
/* Start at k=1 since k=0 is trivial. */
|
||||
term = 1.0;
|
||||
sum = 1.0;
|
||||
x2 = x / 2.0;
|
||||
k = 1;
|
||||
|
||||
/* Let the integration converge until the term of the sum is no longer
|
||||
* significant.
|
||||
*/
|
||||
do {
|
||||
y = x2 / k;
|
||||
k ++;
|
||||
last_sum = sum;
|
||||
term *= y * y;
|
||||
sum += term;
|
||||
} while(sum != last_sum);
|
||||
return sum;
|
||||
}
|
||||
|
||||
/* Calculate a Kaiser window from the given beta value and a normalized k
|
||||
* [-1, 1].
|
||||
*
|
||||
* w(k) = { I_0(B sqrt(1 - k^2)) / I_0(B), -1 <= k <= 1
|
||||
* { 0, elsewhere.
|
||||
*
|
||||
* Where k can be calculated as:
|
||||
*
|
||||
* k = i / l, where -l <= i <= l.
|
||||
*
|
||||
* or:
|
||||
*
|
||||
* k = 2 i / M - 1, where 0 <= i <= M.
|
||||
*/
|
||||
static inline double Kaiser(double b, double k)
|
||||
{
|
||||
if(k <= -1.0 || k >= 1.0) return 0.0;
|
||||
return BesselI_0(b * sqrt(1.0 - (k*k))) / BesselI_0(b);
|
||||
}
|
||||
|
||||
static inline double CalcKaiserBeta(double rejection)
|
||||
{
|
||||
if(rejection > 50.0)
|
||||
return 0.1102 * (rejection - 8.7);
|
||||
if(rejection >= 21.0)
|
||||
return (0.5842 * pow(rejection - 21.0, 0.4)) +
|
||||
(0.07886 * (rejection - 21.0));
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
static float SincKaiser(double r, double x)
|
||||
{
|
||||
/* Limit rippling to -60dB. */
|
||||
return (float)(Kaiser(CalcKaiserBeta(60.0), x / r) * Sinc(x));
|
||||
}
|
||||
|
||||
|
||||
void aluInitMixer(void)
|
||||
{
|
||||
enum Resampler resampler = ResamplerDefault;
|
||||
const char *str;
|
||||
ALuint i;
|
||||
|
||||
if(ConfigValueStr(NULL, NULL, "resampler", &str))
|
||||
{
|
||||
if(strcasecmp(str, "point") == 0 || strcasecmp(str, "none") == 0)
|
||||
resampler = PointResampler;
|
||||
else if(strcasecmp(str, "linear") == 0)
|
||||
resampler = LinearResampler;
|
||||
else if(strcasecmp(str, "sinc4") == 0)
|
||||
resampler = FIR4Resampler;
|
||||
else if(strcasecmp(str, "sinc8") == 0)
|
||||
resampler = FIR8Resampler;
|
||||
else if(strcasecmp(str, "bsinc") == 0)
|
||||
resampler = BSincResampler;
|
||||
else if(strcasecmp(str, "cubic") == 0)
|
||||
{
|
||||
WARN("Resampler option \"cubic\" is deprecated, using sinc4\n");
|
||||
resampler = FIR4Resampler;
|
||||
}
|
||||
else
|
||||
{
|
||||
char *end;
|
||||
long n = strtol(str, &end, 0);
|
||||
if(*end == '\0' && (n == PointResampler || n == LinearResampler || n == FIR4Resampler))
|
||||
resampler = n;
|
||||
else
|
||||
WARN("Invalid resampler: %s\n", str);
|
||||
}
|
||||
}
|
||||
|
||||
if(resampler == FIR8Resampler)
|
||||
for(i = 0;i < FRACTIONONE;i++)
|
||||
{
|
||||
ALdouble mu = (ALdouble)i / FRACTIONONE;
|
||||
ResampleCoeffs.FIR8[i][0] = SincKaiser(4.0, mu - -3.0);
|
||||
ResampleCoeffs.FIR8[i][1] = SincKaiser(4.0, mu - -2.0);
|
||||
ResampleCoeffs.FIR8[i][2] = SincKaiser(4.0, mu - -1.0);
|
||||
ResampleCoeffs.FIR8[i][3] = SincKaiser(4.0, mu - 0.0);
|
||||
ResampleCoeffs.FIR8[i][4] = SincKaiser(4.0, mu - 1.0);
|
||||
ResampleCoeffs.FIR8[i][5] = SincKaiser(4.0, mu - 2.0);
|
||||
ResampleCoeffs.FIR8[i][6] = SincKaiser(4.0, mu - 3.0);
|
||||
ResampleCoeffs.FIR8[i][7] = SincKaiser(4.0, mu - 4.0);
|
||||
}
|
||||
else if(resampler == FIR4Resampler)
|
||||
for(i = 0;i < FRACTIONONE;i++)
|
||||
{
|
||||
ALdouble mu = (ALdouble)i / FRACTIONONE;
|
||||
ResampleCoeffs.FIR4[i][0] = SincKaiser(2.0, mu - -1.0);
|
||||
ResampleCoeffs.FIR4[i][1] = SincKaiser(2.0, mu - 0.0);
|
||||
ResampleCoeffs.FIR4[i][2] = SincKaiser(2.0, mu - 1.0);
|
||||
ResampleCoeffs.FIR4[i][3] = SincKaiser(2.0, mu - 2.0);
|
||||
}
|
||||
|
||||
MixHrtfSamples = SelectHrtfMixer();
|
||||
MixSamples = SelectMixer();
|
||||
ResampleSamples = SelectResampler(resampler);
|
||||
}
|
||||
|
||||
|
||||
static inline ALfloat Sample_ALbyte(ALbyte val)
|
||||
{ return val * (1.0f/127.0f); }
|
||||
|
||||
@@ -108,7 +294,7 @@ static inline ALfloat Sample_ALfloat(ALfloat val)
|
||||
{ return val; }
|
||||
|
||||
#define DECL_TEMPLATE(T) \
|
||||
static void Load_##T(ALfloat *dst, const T *src, ALuint srcstep, ALuint samples)\
|
||||
static inline void Load_##T(ALfloat *dst, const T *src, ALuint srcstep, ALuint samples)\
|
||||
{ \
|
||||
ALuint i; \
|
||||
for(i = 0;i < samples;i++) \
|
||||
@@ -137,7 +323,7 @@ static void LoadSamples(ALfloat *dst, const ALvoid *src, ALuint srcstep, enum Fm
|
||||
}
|
||||
}
|
||||
|
||||
static void SilenceSamples(ALfloat *dst, ALuint samples)
|
||||
static inline void SilenceSamples(ALfloat *dst, ALuint samples)
|
||||
{
|
||||
ALuint i;
|
||||
for(i = 0;i < samples;i++)
|
||||
@@ -153,20 +339,24 @@ static const ALfloat *DoFilters(ALfilterState *lpfilter, ALfilterState *hpfilter
|
||||
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[64];
|
||||
ALuint todo = minu(64, numsamples-i);
|
||||
ALfloat temp[256];
|
||||
ALuint todo = minu(256, numsamples-i);
|
||||
|
||||
ALfilterState_process(lpfilter, temp, src+i, todo);
|
||||
ALfilterState_process(hpfilter, dst+i, temp, todo);
|
||||
@@ -178,22 +368,19 @@ static const ALfloat *DoFilters(ALfilterState *lpfilter, ALfilterState *hpfilter
|
||||
}
|
||||
|
||||
|
||||
ALvoid MixSource(ALactivesource *src, ALCdevice *Device, ALuint SamplesToDo)
|
||||
ALvoid MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALuint SamplesToDo)
|
||||
{
|
||||
MixerFunc Mix;
|
||||
HrtfMixerFunc HrtfMix;
|
||||
ResamplerFunc Resample;
|
||||
ALsource *Source = src->Source;
|
||||
ALbufferlistitem *BufferListItem;
|
||||
ALuint DataPosInt, DataPosFrac;
|
||||
ALboolean Looping;
|
||||
ALuint increment;
|
||||
enum Resampler Resampler;
|
||||
ALenum State;
|
||||
ALuint OutPos;
|
||||
ALuint NumChannels;
|
||||
ALuint SampleSize;
|
||||
ALint64 DataSize64;
|
||||
ALuint IrSize;
|
||||
ALuint chan, j;
|
||||
|
||||
/* Get source info */
|
||||
@@ -202,19 +389,17 @@ ALvoid MixSource(ALactivesource *src, ALCdevice *Device, ALuint SamplesToDo)
|
||||
DataPosInt = Source->position;
|
||||
DataPosFrac = Source->position_fraction;
|
||||
Looping = Source->Looping;
|
||||
increment = src->Step;
|
||||
Resampler = (increment==FRACTIONONE) ? PointResampler : Source->Resampler;
|
||||
NumChannels = Source->NumChannels;
|
||||
SampleSize = Source->SampleSize;
|
||||
increment = voice->Step;
|
||||
|
||||
Mix = SelectMixer();
|
||||
HrtfMix = SelectHrtfMixer();
|
||||
Resample = SelectResampler(Resampler, increment);
|
||||
IrSize = (Device->Hrtf ? GetHrtfIrSize(Device->Hrtf) : 0);
|
||||
|
||||
Resample = ((increment == FRACTIONONE && DataPosFrac == 0) ?
|
||||
Resample_copy32_C : ResampleSamples);
|
||||
|
||||
OutPos = 0;
|
||||
do {
|
||||
const ALuint BufferPrePadding = ResamplerPrePadding[Resampler];
|
||||
const ALuint BufferPadding = ResamplerPadding[Resampler];
|
||||
ALuint SrcBufferSize, DstBufferSize;
|
||||
|
||||
/* Figure out how many buffer samples will be needed */
|
||||
@@ -222,13 +407,13 @@ ALvoid MixSource(ALactivesource *src, ALCdevice *Device, ALuint SamplesToDo)
|
||||
DataSize64 *= increment;
|
||||
DataSize64 += DataPosFrac+FRACTIONMASK;
|
||||
DataSize64 >>= FRACTIONBITS;
|
||||
DataSize64 += BufferPadding+BufferPrePadding;
|
||||
DataSize64 += MAX_POST_SAMPLES+MAX_PRE_SAMPLES;
|
||||
|
||||
SrcBufferSize = (ALuint)mini64(DataSize64, BUFFERSIZE);
|
||||
|
||||
/* Figure out how many samples we can actually mix from this. */
|
||||
DataSize64 = SrcBufferSize;
|
||||
DataSize64 -= BufferPadding+BufferPrePadding;
|
||||
DataSize64 -= MAX_POST_SAMPLES+MAX_PRE_SAMPLES;
|
||||
DataSize64 <<= FRACTIONBITS;
|
||||
DataSize64 -= DataPosFrac;
|
||||
|
||||
@@ -244,7 +429,11 @@ ALvoid MixSource(ALactivesource *src, ALCdevice *Device, ALuint SamplesToDo)
|
||||
{
|
||||
const ALfloat *ResampledData;
|
||||
ALfloat *SrcData = Device->SourceData;
|
||||
ALuint SrcDataSize = 0;
|
||||
ALuint 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)
|
||||
{
|
||||
@@ -253,29 +442,20 @@ ALvoid MixSource(ALactivesource *src, ALCdevice *Device, ALuint SamplesToDo)
|
||||
ALuint DataSize;
|
||||
ALuint pos;
|
||||
|
||||
/* Offset buffer data to current channel */
|
||||
Data += chan*SampleSize;
|
||||
|
||||
/* If current pos is beyond the loop range, do not loop */
|
||||
if(Looping == AL_FALSE || DataPosInt >= (ALuint)ALBuffer->LoopEnd)
|
||||
{
|
||||
Looping = AL_FALSE;
|
||||
|
||||
if(DataPosInt >= BufferPrePadding)
|
||||
pos = DataPosInt - BufferPrePadding;
|
||||
else
|
||||
{
|
||||
DataSize = BufferPrePadding - DataPosInt;
|
||||
DataSize = minu(SrcBufferSize - SrcDataSize, DataSize);
|
||||
|
||||
SilenceSamples(&SrcData[SrcDataSize], DataSize);
|
||||
SrcDataSize += DataSize;
|
||||
|
||||
pos = 0;
|
||||
}
|
||||
|
||||
/* Copy what's left to play in the source buffer, and clear the
|
||||
* rest of the temp buffer */
|
||||
/* Load what's left to play from the source buffer, and
|
||||
* clear the rest of the temp buffer */
|
||||
pos = DataPosInt;
|
||||
DataSize = minu(SrcBufferSize - SrcDataSize, ALBuffer->SampleLen - pos);
|
||||
|
||||
LoadSamples(&SrcData[SrcDataSize], &Data[(pos*NumChannels + chan)*SampleSize],
|
||||
LoadSamples(&SrcData[SrcDataSize], &Data[pos * NumChannels*SampleSize],
|
||||
NumChannels, ALBuffer->FmtType, DataSize);
|
||||
SrcDataSize += DataSize;
|
||||
|
||||
@@ -287,33 +467,13 @@ ALvoid MixSource(ALactivesource *src, ALCdevice *Device, ALuint SamplesToDo)
|
||||
ALuint LoopStart = ALBuffer->LoopStart;
|
||||
ALuint LoopEnd = ALBuffer->LoopEnd;
|
||||
|
||||
if(DataPosInt >= LoopStart)
|
||||
{
|
||||
pos = DataPosInt-LoopStart;
|
||||
while(pos < BufferPrePadding)
|
||||
pos += LoopEnd-LoopStart;
|
||||
pos -= BufferPrePadding;
|
||||
pos += LoopStart;
|
||||
}
|
||||
else if(DataPosInt >= BufferPrePadding)
|
||||
pos = DataPosInt - BufferPrePadding;
|
||||
else
|
||||
{
|
||||
DataSize = BufferPrePadding - DataPosInt;
|
||||
DataSize = minu(SrcBufferSize - SrcDataSize, DataSize);
|
||||
|
||||
SilenceSamples(&SrcData[SrcDataSize], DataSize);
|
||||
SrcDataSize += DataSize;
|
||||
|
||||
pos = 0;
|
||||
}
|
||||
|
||||
/* Copy what's left of this loop iteration, then copy repeats
|
||||
* of the loop section */
|
||||
/* Load what's left of this loop iteration, then load
|
||||
* repeats of the loop section */
|
||||
pos = DataPosInt;
|
||||
DataSize = LoopEnd - pos;
|
||||
DataSize = minu(SrcBufferSize - SrcDataSize, DataSize);
|
||||
|
||||
LoadSamples(&SrcData[SrcDataSize], &Data[(pos*NumChannels + chan)*SampleSize],
|
||||
LoadSamples(&SrcData[SrcDataSize], &Data[pos * NumChannels*SampleSize],
|
||||
NumChannels, ALBuffer->FmtType, DataSize);
|
||||
SrcDataSize += DataSize;
|
||||
|
||||
@@ -322,7 +482,7 @@ ALvoid MixSource(ALactivesource *src, ALCdevice *Device, ALuint SamplesToDo)
|
||||
{
|
||||
DataSize = minu(SrcBufferSize - SrcDataSize, DataSize);
|
||||
|
||||
LoadSamples(&SrcData[SrcDataSize], &Data[(LoopStart*NumChannels + chan)*SampleSize],
|
||||
LoadSamples(&SrcData[SrcDataSize], &Data[LoopStart * NumChannels*SampleSize],
|
||||
NumChannels, ALBuffer->FmtType, DataSize);
|
||||
SrcDataSize += DataSize;
|
||||
}
|
||||
@@ -332,45 +492,7 @@ ALvoid MixSource(ALactivesource *src, ALCdevice *Device, ALuint SamplesToDo)
|
||||
{
|
||||
/* Crawl the buffer queue to fill in the temp buffer */
|
||||
ALbufferlistitem *tmpiter = BufferListItem;
|
||||
ALuint pos;
|
||||
|
||||
if(DataPosInt >= BufferPrePadding)
|
||||
pos = DataPosInt - BufferPrePadding;
|
||||
else
|
||||
{
|
||||
pos = BufferPrePadding - DataPosInt;
|
||||
while(pos > 0)
|
||||
{
|
||||
ALbufferlistitem *prev;
|
||||
if((prev=tmpiter->prev) != NULL)
|
||||
tmpiter = prev;
|
||||
else if(Looping)
|
||||
{
|
||||
while(tmpiter->next)
|
||||
tmpiter = tmpiter->next;
|
||||
}
|
||||
else
|
||||
{
|
||||
ALuint DataSize = minu(SrcBufferSize - SrcDataSize, pos);
|
||||
|
||||
SilenceSamples(&SrcData[SrcDataSize], DataSize);
|
||||
SrcDataSize += DataSize;
|
||||
|
||||
pos = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if(tmpiter->buffer)
|
||||
{
|
||||
if((ALuint)tmpiter->buffer->SampleLen > pos)
|
||||
{
|
||||
pos = tmpiter->buffer->SampleLen - pos;
|
||||
break;
|
||||
}
|
||||
pos -= tmpiter->buffer->SampleLen;
|
||||
}
|
||||
}
|
||||
}
|
||||
ALuint pos = DataPosInt;
|
||||
|
||||
while(tmpiter && SrcBufferSize > SrcDataSize)
|
||||
{
|
||||
@@ -406,13 +528,19 @@ ALvoid MixSource(ALactivesource *src, ALCdevice *Device, ALuint SamplesToDo)
|
||||
}
|
||||
}
|
||||
|
||||
/* 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(
|
||||
&SrcData[BufferPrePadding], DataPosFrac, increment,
|
||||
ResampledData = Resample(&voice->SincState,
|
||||
&SrcData[MAX_PRE_SAMPLES], DataPosFrac, increment,
|
||||
Device->ResampledData, DstBufferSize
|
||||
);
|
||||
{
|
||||
DirectParams *parms = &src->Direct;
|
||||
DirectParams *parms = &voice->Direct;
|
||||
const ALfloat *samples;
|
||||
|
||||
samples = DoFilters(
|
||||
@@ -420,18 +548,18 @@ ALvoid MixSource(ALactivesource *src, ALCdevice *Device, ALuint SamplesToDo)
|
||||
Device->FilteredData, ResampledData, DstBufferSize,
|
||||
parms->Filters[chan].ActiveType
|
||||
);
|
||||
if(!src->IsHrtf)
|
||||
Mix(samples, MaxChannels, parms->OutBuffer, parms->Mix.Gains[chan],
|
||||
parms->Counter, OutPos, DstBufferSize);
|
||||
if(!voice->IsHrtf)
|
||||
MixSamples(samples, parms->OutChannels, parms->OutBuffer, parms->Gains[chan],
|
||||
parms->Counter, OutPos, DstBufferSize);
|
||||
else
|
||||
HrtfMix(parms->OutBuffer, samples, parms->Counter, src->Offset,
|
||||
OutPos, parms->Mix.Hrtf.IrSize, &parms->Mix.Hrtf.Params[chan],
|
||||
&parms->Mix.Hrtf.State[chan], DstBufferSize);
|
||||
MixHrtfSamples(parms->OutBuffer, samples, parms->Counter, voice->Offset,
|
||||
OutPos, IrSize, &parms->Hrtf[chan].Params,
|
||||
&parms->Hrtf[chan].State, DstBufferSize);
|
||||
}
|
||||
|
||||
for(j = 0;j < Device->NumAuxSends;j++)
|
||||
{
|
||||
SendParams *parms = &src->Send[j];
|
||||
SendParams *parms = &voice->Send[j];
|
||||
const ALfloat *samples;
|
||||
|
||||
if(!parms->OutBuffer)
|
||||
@@ -442,8 +570,8 @@ ALvoid MixSource(ALactivesource *src, ALCdevice *Device, ALuint SamplesToDo)
|
||||
Device->FilteredData, ResampledData, DstBufferSize,
|
||||
parms->Filters[chan].ActiveType
|
||||
);
|
||||
Mix(samples, 1, parms->OutBuffer, &parms->Gain,
|
||||
parms->Counter, OutPos, DstBufferSize);
|
||||
MixSamples(samples, 1, parms->OutBuffer, &parms->Gains[chan],
|
||||
parms->Counter, OutPos, DstBufferSize);
|
||||
}
|
||||
}
|
||||
/* Update positions */
|
||||
@@ -452,10 +580,10 @@ ALvoid MixSource(ALactivesource *src, ALCdevice *Device, ALuint SamplesToDo)
|
||||
DataPosFrac &= FRACTIONMASK;
|
||||
|
||||
OutPos += DstBufferSize;
|
||||
src->Offset += DstBufferSize;
|
||||
src->Direct.Counter = maxu(src->Direct.Counter, DstBufferSize) - DstBufferSize;
|
||||
voice->Offset += DstBufferSize;
|
||||
voice->Direct.Counter = maxu(voice->Direct.Counter, DstBufferSize) - DstBufferSize;
|
||||
for(j = 0;j < Device->NumAuxSends;j++)
|
||||
src->Send[j].Counter = maxu(src->Send[j].Counter, DstBufferSize) - DstBufferSize;
|
||||
voice->Send[j].Counter = maxu(voice->Send[j].Counter, DstBufferSize) - DstBufferSize;
|
||||
|
||||
/* Handle looping sources */
|
||||
while(1)
|
||||
|
||||
@@ -12,13 +12,15 @@ static inline ALfloat point32(const ALfloat *vals, ALuint UNUSED(frac))
|
||||
{ return vals[0]; }
|
||||
static inline ALfloat lerp32(const ALfloat *vals, ALuint frac)
|
||||
{ return lerp(vals[0], vals[1], frac * (1.0f/FRACTIONONE)); }
|
||||
static inline ALfloat cubic32(const ALfloat *vals, ALuint frac)
|
||||
{ return cubic(vals[-1], vals[0], vals[1], vals[2], frac * (1.0f/FRACTIONONE)); }
|
||||
static inline ALfloat fir4_32(const ALfloat *vals, ALuint frac)
|
||||
{ return resample_fir4(vals[-1], vals[0], vals[1], vals[2], frac); }
|
||||
static inline ALfloat fir8_32(const ALfloat *vals, ALuint frac)
|
||||
{ return resample_fir8(vals[-3], vals[-2], vals[-1], vals[0], vals[1], vals[2], vals[3], vals[4], frac); }
|
||||
|
||||
const ALfloat *Resample_copy32_C(const ALfloat *src, ALuint UNUSED(frac),
|
||||
ALuint increment, ALfloat *restrict dst, ALuint numsamples)
|
||||
|
||||
const ALfloat *Resample_copy32_C(const BsincState* UNUSED(state), const ALfloat *src, ALuint UNUSED(frac),
|
||||
ALuint UNUSED(increment), ALfloat *restrict dst, ALuint numsamples)
|
||||
{
|
||||
assert(increment==FRACTIONONE);
|
||||
#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))
|
||||
@@ -29,8 +31,9 @@ const ALfloat *Resample_copy32_C(const ALfloat *src, ALuint UNUSED(frac),
|
||||
}
|
||||
|
||||
#define DECL_TEMPLATE(Sampler) \
|
||||
const ALfloat *Resample_##Sampler##_C(const ALfloat *src, ALuint frac, \
|
||||
ALuint increment, ALfloat *restrict dst, ALuint numsamples) \
|
||||
const ALfloat *Resample_##Sampler##_C(const BsincState* UNUSED(state), \
|
||||
const ALfloat *src, ALuint frac, ALuint increment, \
|
||||
ALfloat *restrict dst, ALuint numsamples) \
|
||||
{ \
|
||||
ALuint i; \
|
||||
for(i = 0;i < numsamples;i++) \
|
||||
@@ -46,10 +49,49 @@ const ALfloat *Resample_##Sampler##_C(const ALfloat *src, ALuint frac, \
|
||||
|
||||
DECL_TEMPLATE(point32)
|
||||
DECL_TEMPLATE(lerp32)
|
||||
DECL_TEMPLATE(cubic32)
|
||||
DECL_TEMPLATE(fir4_32)
|
||||
DECL_TEMPLATE(fir8_32)
|
||||
|
||||
#undef DECL_TEMPLATE
|
||||
|
||||
const ALfloat *Resample_bsinc32_C(const BsincState *state, const ALfloat *src, ALuint frac,
|
||||
ALuint increment, ALfloat *restrict dst, ALuint dstlen)
|
||||
{
|
||||
const ALfloat *fil, *scd, *phd, *spd;
|
||||
const ALfloat sf = state->sf;
|
||||
const ALuint m = state->m;
|
||||
const ALint l = state->l;
|
||||
ALuint j_f, pi, i;
|
||||
ALfloat pf, r;
|
||||
ALint j_s;
|
||||
|
||||
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 = state->coeffs[pi].filter;
|
||||
scd = state->coeffs[pi].scDelta;
|
||||
phd = state->coeffs[pi].phDelta;
|
||||
spd = state->coeffs[pi].spDelta;
|
||||
|
||||
// Apply the scale and phase interpolated filter.
|
||||
r = 0.0f;
|
||||
for(j_f = 0,j_s = l;j_f < m;j_f++,j_s++)
|
||||
r += (fil[j_f] + sf*scd[j_f] + pf*(phd[j_f] + sf*spd[j_f])) *
|
||||
src[j_s];
|
||||
dst[i] = r;
|
||||
|
||||
frac += increment;
|
||||
src += frac>>FRACTIONBITS;
|
||||
frac &= FRACTIONMASK;
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
|
||||
void ALfilterState_processC(ALfilterState *filter, ALfloat *restrict dst, const ALfloat *src, ALuint numsamples)
|
||||
{
|
||||
@@ -59,6 +101,18 @@ void ALfilterState_processC(ALfilterState *filter, ALfloat *restrict dst, const
|
||||
}
|
||||
|
||||
|
||||
static inline void SetupCoeffs(ALfloat (*restrict OutCoeffs)[2],
|
||||
const HrtfParams *hrtfparams,
|
||||
ALuint IrSize, ALuint Counter)
|
||||
{
|
||||
ALuint c;
|
||||
for(c = 0;c < IrSize;c++)
|
||||
{
|
||||
OutCoeffs[c][0] = hrtfparams->Coeffs[c][0] - (hrtfparams->CoeffStep[c][0]*Counter);
|
||||
OutCoeffs[c][1] = hrtfparams->Coeffs[c][1] - (hrtfparams->CoeffStep[c][1]*Counter);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void ApplyCoeffsStep(ALuint Offset, ALfloat (*restrict Values)[2],
|
||||
const ALuint IrSize,
|
||||
ALfloat (*restrict Coeffs)[2],
|
||||
@@ -90,9 +144,9 @@ static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2],
|
||||
}
|
||||
}
|
||||
|
||||
#define SUFFIX C
|
||||
#define MixHrtf MixHrtf_C
|
||||
#include "mixer_inc.c"
|
||||
#undef SUFFIX
|
||||
#undef MixHrtf
|
||||
|
||||
|
||||
void Mix_C(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
|
||||
@@ -106,19 +160,20 @@ void Mix_C(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[B
|
||||
ALuint pos = 0;
|
||||
gain = Gains[c].Current;
|
||||
step = Gains[c].Step;
|
||||
if(step != 1.0f && Counter > 0)
|
||||
if(step != 0.0f && Counter > 0)
|
||||
{
|
||||
for(;pos < BufferSize && pos < Counter;pos++)
|
||||
ALuint minsize = minu(BufferSize, Counter);
|
||||
for(;pos < minsize;pos++)
|
||||
{
|
||||
OutBuffer[c][OutPos+pos] += data[pos]*gain;
|
||||
gain *= step;
|
||||
gain += step;
|
||||
}
|
||||
if(pos == Counter)
|
||||
gain = Gains[c].Target;
|
||||
Gains[c].Current = gain;
|
||||
}
|
||||
|
||||
if(!(gain > GAIN_SILENCE_THRESHOLD))
|
||||
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
|
||||
continue;
|
||||
for(;pos < BufferSize;pos++)
|
||||
OutBuffer[c][OutPos+pos] += data[pos]*gain;
|
||||
|
||||
@@ -12,10 +12,12 @@ struct HrtfParams;
|
||||
struct HrtfState;
|
||||
|
||||
/* C resamplers */
|
||||
const ALfloat *Resample_copy32_C(const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen);
|
||||
const ALfloat *Resample_point32_C(const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen);
|
||||
const ALfloat *Resample_lerp32_C(const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen);
|
||||
const ALfloat *Resample_cubic32_C(const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen);
|
||||
const ALfloat *Resample_copy32_C(const BsincState *state, const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen);
|
||||
const ALfloat *Resample_point32_C(const BsincState *state, const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen);
|
||||
const ALfloat *Resample_lerp32_C(const BsincState *state, const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen);
|
||||
const ALfloat *Resample_fir4_32_C(const BsincState *state, const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen);
|
||||
const ALfloat *Resample_fir8_32_C(const BsincState *state, const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen);
|
||||
const ALfloat *Resample_bsinc32_C(const BsincState *state, const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen);
|
||||
|
||||
|
||||
/* C mixers */
|
||||
@@ -24,7 +26,7 @@ void MixHrtf_C(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat *data,
|
||||
const struct HrtfParams *hrtfparams, struct HrtfState *hrtfstate,
|
||||
ALuint BufferSize);
|
||||
void Mix_C(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
|
||||
struct MixGains *Gains, ALuint Counter, ALuint OutPos, ALuint BufferSize);
|
||||
struct MixGains *Gains, ALuint Counter, ALuint OutPos, ALuint BufferSize);
|
||||
|
||||
/* SSE mixers */
|
||||
void MixHrtf_SSE(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat *data,
|
||||
@@ -49,11 +51,24 @@ inline void InitiatePositionArrays(ALuint frac, ALuint increment, ALuint *frac_a
|
||||
}
|
||||
}
|
||||
|
||||
const ALfloat *Resample_lerp32_SSE2(const ALfloat *src, ALuint frac, ALuint increment,
|
||||
const ALfloat *Resample_bsinc32_SSE(const BsincState *state, const ALfloat *src, ALuint frac,
|
||||
ALuint increment, ALfloat *restrict dst, ALuint dstlen);
|
||||
|
||||
const ALfloat *Resample_lerp32_SSE2(const BsincState *state, const ALfloat *src, ALuint frac, ALuint increment,
|
||||
ALfloat *restrict dst, ALuint numsamples);
|
||||
const ALfloat *Resample_lerp32_SSE41(const ALfloat *src, ALuint frac, ALuint increment,
|
||||
const ALfloat *Resample_lerp32_SSE41(const BsincState *state, const ALfloat *src, ALuint frac, ALuint increment,
|
||||
ALfloat *restrict dst, ALuint numsamples);
|
||||
|
||||
const ALfloat *Resample_fir4_32_SSE3(const BsincState *state, const ALfloat *src, ALuint frac, ALuint increment,
|
||||
ALfloat *restrict dst, ALuint numsamples);
|
||||
const ALfloat *Resample_fir4_32_SSE41(const BsincState *state, const ALfloat *src, ALuint frac, ALuint increment,
|
||||
ALfloat *restrict dst, ALuint numsamples);
|
||||
|
||||
const ALfloat *Resample_fir8_32_SSE3(const BsincState *state, const ALfloat *src, ALuint frac, ALuint increment,
|
||||
ALfloat *restrict dst, ALuint numsamples);
|
||||
const ALfloat *Resample_fir8_32_SSE41(const BsincState *state, const ALfloat *src, ALuint frac, ALuint increment,
|
||||
ALfloat *restrict dst, ALuint numsamples);
|
||||
|
||||
/* Neon mixers */
|
||||
void MixHrtf_Neon(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat *data,
|
||||
ALuint Counter, ALuint Offset, ALuint OutPos, const ALuint IrSize,
|
||||
|
||||
@@ -8,12 +8,9 @@
|
||||
#include "align.h"
|
||||
|
||||
|
||||
#define REAL_MERGE(a,b) a##b
|
||||
#define MERGE(a,b) REAL_MERGE(a,b)
|
||||
|
||||
#define MixHrtf MERGE(MixHrtf_,SUFFIX)
|
||||
|
||||
|
||||
static inline void SetupCoeffs(ALfloat (*restrict OutCoeffs)[2],
|
||||
const HrtfParams *hrtfparams,
|
||||
ALuint IrSize, ALuint Counter);
|
||||
static inline void ApplyCoeffsStep(ALuint Offset, ALfloat (*restrict Values)[2],
|
||||
const ALuint irSize,
|
||||
ALfloat (*restrict Coeffs)[2],
|
||||
@@ -33,24 +30,20 @@ void MixHrtf(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat *data,
|
||||
ALuint Delay[2];
|
||||
ALfloat left, right;
|
||||
ALuint pos;
|
||||
ALuint c;
|
||||
|
||||
for(c = 0;c < IrSize;c++)
|
||||
{
|
||||
Coeffs[c][0] = hrtfparams->Coeffs[c][0] - (hrtfparams->CoeffStep[c][0]*Counter);
|
||||
Coeffs[c][1] = hrtfparams->Coeffs[c][1] - (hrtfparams->CoeffStep[c][1]*Counter);
|
||||
}
|
||||
SetupCoeffs(Coeffs, hrtfparams, IrSize, Counter);
|
||||
Delay[0] = hrtfparams->Delay[0] - (hrtfparams->DelayStep[0]*Counter);
|
||||
Delay[1] = hrtfparams->Delay[1] - (hrtfparams->DelayStep[1]*Counter);
|
||||
|
||||
for(pos = 0;pos < BufferSize && pos < Counter;pos++)
|
||||
pos = 0;
|
||||
for(;pos < BufferSize && pos < Counter;pos++)
|
||||
{
|
||||
hrtfstate->History[Offset&SRC_HISTORY_MASK] = data[pos];
|
||||
left = lerp(hrtfstate->History[(Offset-(Delay[0]>>HRTFDELAY_BITS))&SRC_HISTORY_MASK],
|
||||
hrtfstate->History[(Offset-(Delay[0]>>HRTFDELAY_BITS)-1)&SRC_HISTORY_MASK],
|
||||
hrtfstate->History[Offset&HRTF_HISTORY_MASK] = data[pos];
|
||||
left = lerp(hrtfstate->History[(Offset-(Delay[0]>>HRTFDELAY_BITS))&HRTF_HISTORY_MASK],
|
||||
hrtfstate->History[(Offset-(Delay[0]>>HRTFDELAY_BITS)-1)&HRTF_HISTORY_MASK],
|
||||
(Delay[0]&HRTFDELAY_MASK)*(1.0f/HRTFDELAY_FRACONE));
|
||||
right = lerp(hrtfstate->History[(Offset-(Delay[1]>>HRTFDELAY_BITS))&SRC_HISTORY_MASK],
|
||||
hrtfstate->History[(Offset-(Delay[1]>>HRTFDELAY_BITS)-1)&SRC_HISTORY_MASK],
|
||||
right = lerp(hrtfstate->History[(Offset-(Delay[1]>>HRTFDELAY_BITS))&HRTF_HISTORY_MASK],
|
||||
hrtfstate->History[(Offset-(Delay[1]>>HRTFDELAY_BITS)-1)&HRTF_HISTORY_MASK],
|
||||
(Delay[1]&HRTFDELAY_MASK)*(1.0f/HRTFDELAY_FRACONE));
|
||||
|
||||
Delay[0] += hrtfparams->DelayStep[0];
|
||||
@@ -61,8 +54,8 @@ void MixHrtf(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat *data,
|
||||
Offset++;
|
||||
|
||||
ApplyCoeffsStep(Offset, hrtfstate->Values, IrSize, Coeffs, hrtfparams->CoeffStep, left, right);
|
||||
OutBuffer[FrontLeft][OutPos] += hrtfstate->Values[Offset&HRIR_MASK][0];
|
||||
OutBuffer[FrontRight][OutPos] += hrtfstate->Values[Offset&HRIR_MASK][1];
|
||||
OutBuffer[0][OutPos] += hrtfstate->Values[Offset&HRIR_MASK][0];
|
||||
OutBuffer[1][OutPos] += hrtfstate->Values[Offset&HRIR_MASK][1];
|
||||
OutPos++;
|
||||
}
|
||||
|
||||
@@ -70,24 +63,17 @@ void MixHrtf(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat *data,
|
||||
Delay[1] >>= HRTFDELAY_BITS;
|
||||
for(;pos < BufferSize;pos++)
|
||||
{
|
||||
hrtfstate->History[Offset&SRC_HISTORY_MASK] = data[pos];
|
||||
left = hrtfstate->History[(Offset-Delay[0])&SRC_HISTORY_MASK];
|
||||
right = hrtfstate->History[(Offset-Delay[1])&SRC_HISTORY_MASK];
|
||||
hrtfstate->History[Offset&HRTF_HISTORY_MASK] = data[pos];
|
||||
left = hrtfstate->History[(Offset-Delay[0])&HRTF_HISTORY_MASK];
|
||||
right = hrtfstate->History[(Offset-Delay[1])&HRTF_HISTORY_MASK];
|
||||
|
||||
hrtfstate->Values[(Offset+IrSize)&HRIR_MASK][0] = 0.0f;
|
||||
hrtfstate->Values[(Offset+IrSize)&HRIR_MASK][1] = 0.0f;
|
||||
Offset++;
|
||||
|
||||
ApplyCoeffs(Offset, hrtfstate->Values, IrSize, Coeffs, left, right);
|
||||
OutBuffer[FrontLeft][OutPos] += hrtfstate->Values[Offset&HRIR_MASK][0];
|
||||
OutBuffer[FrontRight][OutPos] += hrtfstate->Values[Offset&HRIR_MASK][1];
|
||||
|
||||
OutBuffer[0][OutPos] += hrtfstate->Values[Offset&HRIR_MASK][0];
|
||||
OutBuffer[1][OutPos] += hrtfstate->Values[Offset&HRIR_MASK][1];
|
||||
OutPos++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#undef MixHrtf
|
||||
|
||||
#undef MERGE
|
||||
#undef REAL_MERGE
|
||||
|
||||
@@ -9,6 +9,25 @@
|
||||
#include "hrtf.h"
|
||||
|
||||
|
||||
static inline void SetupCoeffs(ALfloat (*restrict OutCoeffs)[2],
|
||||
const HrtfParams *hrtfparams,
|
||||
ALuint IrSize, ALuint Counter)
|
||||
{
|
||||
ALuint c;
|
||||
float32x4_t counter4;
|
||||
{
|
||||
float32x2_t counter2 = vdup_n_f32(-(float)Counter);
|
||||
counter4 = vcombine_f32(counter2, counter2);
|
||||
}
|
||||
for(c = 0;c < IrSize;c += 2)
|
||||
{
|
||||
float32x4_t step4 = vld1q_f32((float32_t*)hrtfparams->CoeffStep[c]);
|
||||
float32x4_t coeffs = vld1q_f32((float32_t*)hrtfparams->Coeffs[c]);
|
||||
coeffs = vmlaq_f32(coeffs, step4, counter4);
|
||||
vst1q_f32((float32_t*)OutCoeffs[c], coeffs);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void ApplyCoeffsStep(ALuint Offset, ALfloat (*restrict Values)[2],
|
||||
const ALuint IrSize,
|
||||
ALfloat (*restrict Coeffs)[2],
|
||||
@@ -69,14 +88,13 @@ static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2],
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#define SUFFIX Neon
|
||||
#define MixHrtf MixHrtf_Neon
|
||||
#include "mixer_inc.c"
|
||||
#undef SUFFIX
|
||||
#undef MixHrtf
|
||||
|
||||
|
||||
void MixDirect_Neon(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
|
||||
MixGains *Gains, ALuint Counter, ALuint OutPos, ALuint BufferSize)
|
||||
void Mix_Neon(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
|
||||
MixGains *Gains, ALuint Counter, ALuint OutPos, ALuint BufferSize)
|
||||
{
|
||||
ALfloat gain, step;
|
||||
float32x4_t gain4;
|
||||
@@ -87,29 +105,32 @@ void MixDirect_Neon(const ALfloat *data, ALuint OutChans, ALfloat (*restrict Out
|
||||
ALuint pos = 0;
|
||||
gain = Gains[c].Current;
|
||||
step = Gains[c].Step;
|
||||
if(step != 1.0f && Counter > 0)
|
||||
if(step != 0.0f && Counter > 0)
|
||||
{
|
||||
for(;pos < BufferSize && pos < Counter;pos++)
|
||||
ALuint minsize = minu(BufferSize, Counter);
|
||||
for(;pos < minsize;pos++)
|
||||
{
|
||||
OutBuffer[c][OutPos+pos] += data[pos]*gain;
|
||||
gain *= step;
|
||||
gain += step;
|
||||
}
|
||||
if(pos == Counter)
|
||||
gain = Gains[c].Target;
|
||||
Gains[c].Current = gain;
|
||||
|
||||
/* Mix until pos is aligned with 4 or the mix is done. */
|
||||
for(;pos < BufferSize && (pos&3) != 0;pos++)
|
||||
minsize = minu(BufferSize, (pos+3)&~3);
|
||||
for(;pos < minsize;pos++)
|
||||
OutBuffer[c][OutPos+pos] += data[pos]*gain;
|
||||
}
|
||||
|
||||
if(!(gain > GAIN_SILENCE_THRESHOLD))
|
||||
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 = vaddq_f32(dry4, vmulq_f32(val4, gain4));
|
||||
dry4 = vmlaq_f32(dry4, val4, gain4);
|
||||
vst1q_f32(&OutBuffer[c][OutPos+pos], dry4);
|
||||
}
|
||||
for(;pos < BufferSize;pos++)
|
||||
|
||||
@@ -1,12 +1,5 @@
|
||||
#include "config.h"
|
||||
|
||||
#ifdef IN_IDE_PARSER
|
||||
/* KDevelop's parser won't recognize these defines that get added by the -msse
|
||||
* switch used to compile this source. Without them, xmmintrin.h fails to
|
||||
* declare anything. */
|
||||
#define __MMX__
|
||||
#define __SSE__
|
||||
#endif
|
||||
#include <xmmintrin.h>
|
||||
|
||||
#include "AL/al.h"
|
||||
@@ -19,6 +12,82 @@
|
||||
#include "mixer_defs.h"
|
||||
|
||||
|
||||
const ALfloat *Resample_bsinc32_SSE(const BsincState *state, const ALfloat *src, ALuint frac,
|
||||
ALuint increment, ALfloat *restrict dst, ALuint dstlen)
|
||||
{
|
||||
const __m128 sf4 = _mm_set1_ps(state->sf);
|
||||
const ALuint m = state->m;
|
||||
const ALint l = state->l;
|
||||
const ALfloat *fil, *scd, *phd, *spd;
|
||||
ALuint pi, j_f, i;
|
||||
ALfloat pf;
|
||||
ALint j_s;
|
||||
__m128 r4;
|
||||
|
||||
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 = state->coeffs[pi].filter;
|
||||
scd = state->coeffs[pi].scDelta;
|
||||
phd = state->coeffs[pi].phDelta;
|
||||
spd = state->coeffs[pi].spDelta;
|
||||
|
||||
// Apply the scale and phase interpolated filter.
|
||||
r4 = _mm_setzero_ps();
|
||||
{
|
||||
const __m128 pf4 = _mm_set1_ps(pf);
|
||||
for(j_f = 0,j_s = l;j_f < m;j_f+=4,j_s+=4)
|
||||
{
|
||||
const __m128 f4 = _mm_add_ps(
|
||||
_mm_add_ps(
|
||||
_mm_load_ps(&fil[j_f]),
|
||||
_mm_mul_ps(sf4, _mm_load_ps(&scd[j_f]))
|
||||
),
|
||||
_mm_mul_ps(
|
||||
pf4,
|
||||
_mm_add_ps(
|
||||
_mm_load_ps(&phd[j_f]),
|
||||
_mm_mul_ps(sf4, _mm_load_ps(&spd[j_f]))
|
||||
)
|
||||
)
|
||||
);
|
||||
r4 = _mm_add_ps(r4, _mm_mul_ps(f4, _mm_loadu_ps(&src[j_s])));
|
||||
}
|
||||
}
|
||||
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 SetupCoeffs(ALfloat (*restrict OutCoeffs)[2],
|
||||
const HrtfParams *hrtfparams,
|
||||
ALuint IrSize, ALuint Counter)
|
||||
{
|
||||
const __m128 counter4 = _mm_set1_ps((float)Counter);
|
||||
__m128 coeffs, step4;
|
||||
ALuint i;
|
||||
|
||||
for(i = 0;i < IrSize;i += 2)
|
||||
{
|
||||
step4 = _mm_load_ps(&hrtfparams->CoeffStep[i][0]);
|
||||
coeffs = _mm_load_ps(&hrtfparams->Coeffs[i][0]);
|
||||
coeffs = _mm_sub_ps(coeffs, _mm_mul_ps(step4, counter4));
|
||||
_mm_store_ps(&OutCoeffs[i][0], coeffs);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void ApplyCoeffsStep(ALuint Offset, ALfloat (*restrict Values)[2],
|
||||
const ALuint IrSize,
|
||||
ALfloat (*restrict Coeffs)[2],
|
||||
@@ -133,16 +202,16 @@ static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2],
|
||||
}
|
||||
}
|
||||
|
||||
#define SUFFIX SSE
|
||||
#define MixHrtf MixHrtf_SSE
|
||||
#include "mixer_inc.c"
|
||||
#undef SUFFIX
|
||||
#undef MixHrtf
|
||||
|
||||
|
||||
void Mix_SSE(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
|
||||
MixGains *Gains, ALuint Counter, ALuint OutPos, ALuint BufferSize)
|
||||
{
|
||||
ALfloat gain, step;
|
||||
__m128 gain4, step4;
|
||||
__m128 gain4;
|
||||
ALuint c;
|
||||
|
||||
for(c = 0;c < OutChans;c++)
|
||||
@@ -150,43 +219,51 @@ void Mix_SSE(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)
|
||||
ALuint pos = 0;
|
||||
gain = Gains[c].Current;
|
||||
step = Gains[c].Step;
|
||||
if(step != 1.0f && Counter > 0)
|
||||
if(step != 0.0f && Counter > 0)
|
||||
{
|
||||
ALuint minsize = minu(BufferSize, Counter);
|
||||
/* Mix with applying gain steps in aligned multiples of 4. */
|
||||
if(BufferSize-pos > 3 && Counter-pos > 3)
|
||||
if(minsize-pos > 3)
|
||||
{
|
||||
__m128 step4;
|
||||
gain4 = _mm_setr_ps(
|
||||
gain,
|
||||
gain * step,
|
||||
gain * step * step,
|
||||
gain * step * step * step
|
||||
gain + step,
|
||||
gain + step + step,
|
||||
gain + step + step + step
|
||||
);
|
||||
step4 = _mm_set1_ps(step * 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_mul_ps(gain4, step4);
|
||||
gain4 = _mm_add_ps(gain4, step4);
|
||||
_mm_store_ps(&OutBuffer[c][OutPos+pos], dry4);
|
||||
pos += 4;
|
||||
} while(BufferSize-pos > 3 && Counter-pos > 3);
|
||||
} 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 < BufferSize && pos < Counter;pos++)
|
||||
for(;pos < minsize;pos++)
|
||||
{
|
||||
OutBuffer[c][OutPos+pos] += data[pos]*gain;
|
||||
gain *= step;
|
||||
gain += step;
|
||||
}
|
||||
if(pos == Counter)
|
||||
gain = Gains[c].Target;
|
||||
Gains[c].Current = gain;
|
||||
|
||||
/* Mix until pos is aligned with 4 or the mix is done. */
|
||||
for(;pos < BufferSize && (pos&3) != 0;pos++)
|
||||
minsize = minu(BufferSize, (pos+3)&~3);
|
||||
for(;pos < minsize;pos++)
|
||||
OutBuffer[c][OutPos+pos] += data[pos]*gain;
|
||||
}
|
||||
|
||||
if(!(gain > GAIN_SILENCE_THRESHOLD))
|
||||
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
|
||||
continue;
|
||||
gain4 = _mm_set1_ps(gain);
|
||||
for(;BufferSize-pos > 3;pos += 4)
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
*
|
||||
* 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., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
#include "mixer_defs.h"
|
||||
|
||||
|
||||
const ALfloat *Resample_lerp32_SSE2(const ALfloat *src, ALuint frac, ALuint increment,
|
||||
const ALfloat *Resample_lerp32_SSE2(const BsincState* UNUSED(state), const ALfloat *src, ALuint frac, ALuint increment,
|
||||
ALfloat *restrict dst, ALuint numsamples)
|
||||
{
|
||||
const __m128i increment4 = _mm_set1_epi32(increment*4);
|
||||
@@ -63,6 +63,9 @@ const ALfloat *Resample_lerp32_SSE2(const ALfloat *src, ALuint frac, ALuint incr
|
||||
_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);
|
||||
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
*
|
||||
* 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., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
#include "mixer_defs.h"
|
||||
|
||||
|
||||
const ALfloat *Resample_lerp32_SSE41(const ALfloat *src, ALuint frac, ALuint increment,
|
||||
const ALfloat *Resample_lerp32_SSE41(const BsincState* UNUSED(state), const ALfloat *src, ALuint frac, ALuint increment,
|
||||
ALfloat *restrict dst, ALuint numsamples)
|
||||
{
|
||||
const __m128i increment4 = _mm_set1_epi32(increment*4);
|
||||
@@ -67,6 +67,9 @@ const ALfloat *Resample_lerp32_SSE41(const ALfloat *src, ALuint frac, ALuint inc
|
||||
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);
|
||||
|
||||
@@ -80,3 +83,142 @@ const ALfloat *Resample_lerp32_SSE41(const ALfloat *src, ALuint frac, ALuint inc
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
const ALfloat *Resample_fir4_32_SSE41(const BsincState* UNUSED(state), const ALfloat *src, ALuint frac, ALuint increment,
|
||||
ALfloat *restrict dst, ALuint numsamples)
|
||||
{
|
||||
const __m128i increment4 = _mm_set1_epi32(increment*4);
|
||||
const __m128i fracMask4 = _mm_set1_epi32(FRACTIONMASK);
|
||||
alignas(16) union { ALuint i[4]; float f[4]; } pos_;
|
||||
alignas(16) union { ALuint i[4]; float f[4]; } frac_;
|
||||
__m128i frac4, pos4;
|
||||
ALuint pos;
|
||||
ALuint i;
|
||||
|
||||
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(ResampleCoeffs.FIR4[frac_.i[0]]);
|
||||
__m128 k1 = _mm_load_ps(ResampleCoeffs.FIR4[frac_.i[1]]);
|
||||
__m128 k2 = _mm_load_ps(ResampleCoeffs.FIR4[frac_.i[2]]);
|
||||
__m128 k3 = _mm_load_ps(ResampleCoeffs.FIR4[frac_.i[3]]);
|
||||
__m128 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;
|
||||
}
|
||||
|
||||
const ALfloat *Resample_fir8_32_SSE41(const BsincState* UNUSED(state), const ALfloat *src, ALuint frac, ALuint increment,
|
||||
ALfloat *restrict dst, ALuint numsamples)
|
||||
{
|
||||
const __m128i increment4 = _mm_set1_epi32(increment*4);
|
||||
const __m128i fracMask4 = _mm_set1_epi32(FRACTIONMASK);
|
||||
alignas(16) union { ALuint i[4]; float f[4]; } pos_;
|
||||
alignas(16) union { ALuint i[4]; float f[4]; } frac_;
|
||||
__m128i frac4, pos4;
|
||||
ALuint pos;
|
||||
ALuint i, j;
|
||||
|
||||
InitiatePositionArrays(frac, increment, frac_.i, pos_.i, 4);
|
||||
|
||||
frac4 = _mm_castps_si128(_mm_load_ps(frac_.f));
|
||||
pos4 = _mm_castps_si128(_mm_load_ps(pos_.f));
|
||||
|
||||
src -= 3;
|
||||
for(i = 0;numsamples-i > 3;i += 4)
|
||||
{
|
||||
__m128 out[2];
|
||||
for(j = 0;j < 8;j+=4)
|
||||
{
|
||||
const __m128 val0 = _mm_loadu_ps(&src[pos_.i[0]+j]);
|
||||
const __m128 val1 = _mm_loadu_ps(&src[pos_.i[1]+j]);
|
||||
const __m128 val2 = _mm_loadu_ps(&src[pos_.i[2]+j]);
|
||||
const __m128 val3 = _mm_loadu_ps(&src[pos_.i[3]+j]);
|
||||
__m128 k0 = _mm_load_ps(&ResampleCoeffs.FIR8[frac_.i[0]][j]);
|
||||
__m128 k1 = _mm_load_ps(&ResampleCoeffs.FIR8[frac_.i[1]][j]);
|
||||
__m128 k2 = _mm_load_ps(&ResampleCoeffs.FIR8[frac_.i[2]][j]);
|
||||
__m128 k3 = _mm_load_ps(&ResampleCoeffs.FIR8[frac_.i[3]][j]);
|
||||
|
||||
k0 = _mm_mul_ps(k0, val0);
|
||||
k1 = _mm_mul_ps(k1, val1);
|
||||
k2 = _mm_mul_ps(k2, val2);
|
||||
k3 = _mm_mul_ps(k3, val3);
|
||||
k0 = _mm_hadd_ps(k0, k1);
|
||||
k2 = _mm_hadd_ps(k2, k3);
|
||||
out[j>>2] = _mm_hadd_ps(k0, k2);
|
||||
}
|
||||
|
||||
out[0] = _mm_add_ps(out[0], out[1]);
|
||||
_mm_store_ps(&dst[i], out[0]);
|
||||
|
||||
frac4 = _mm_add_epi32(frac4, increment4);
|
||||
pos4 = _mm_add_epi32(pos4, _mm_srli_epi32(frac4, FRACTIONBITS));
|
||||
frac4 = _mm_and_si128(frac4, fracMask4);
|
||||
|
||||
pos_.i[0] = _mm_extract_epi32(pos4, 0);
|
||||
pos_.i[1] = _mm_extract_epi32(pos4, 1);
|
||||
pos_.i[2] = _mm_extract_epi32(pos4, 2);
|
||||
pos_.i[3] = _mm_extract_epi32(pos4, 3);
|
||||
frac_.i[0] = _mm_extract_epi32(frac4, 0);
|
||||
frac_.i[1] = _mm_extract_epi32(frac4, 1);
|
||||
frac_.i[2] = _mm_extract_epi32(frac4, 2);
|
||||
frac_.i[3] = _mm_extract_epi32(frac4, 3);
|
||||
}
|
||||
|
||||
pos = pos_.i[0];
|
||||
frac = frac_.i[0];
|
||||
|
||||
for(;i < numsamples;i++)
|
||||
{
|
||||
dst[i] = resample_fir8(src[pos ], src[pos+1], src[pos+2], src[pos+3],
|
||||
src[pos+4], src[pos+5], src[pos+6], src[pos+7], frac);
|
||||
|
||||
frac += increment;
|
||||
pos += frac>>FRACTIONBITS;
|
||||
frac &= FRACTIONMASK;
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
+484
-377
@@ -13,8 +13,8 @@
|
||||
*
|
||||
* 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., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
@@ -30,421 +30,528 @@
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
#include "alu.h"
|
||||
#include "bool.h"
|
||||
|
||||
extern inline void SetGains(const ALCdevice *device, ALfloat ingain, ALfloat gains[MaxChannels]);
|
||||
|
||||
static void SetSpeakerArrangement(const char *name, ALfloat SpeakerAngle[MaxChannels],
|
||||
enum Channel Speaker2Chan[MaxChannels], ALint chans)
|
||||
#define ZERO_ORDER_SCALE 0.0f
|
||||
#define FIRST_ORDER_SCALE 1.0f
|
||||
#define SECOND_ORDER_SCALE (1.0f / 1.22474f)
|
||||
#define THIRD_ORDER_SCALE (1.0f / 1.30657f)
|
||||
|
||||
|
||||
static const ALuint FuMa2ACN[MAX_AMBI_COEFFS] = {
|
||||
0, /* W */
|
||||
3, /* X */
|
||||
1, /* Y */
|
||||
2, /* Z */
|
||||
6, /* R */
|
||||
7, /* S */
|
||||
5, /* T */
|
||||
8, /* U */
|
||||
4, /* V */
|
||||
12, /* K */
|
||||
13, /* L */
|
||||
11, /* M */
|
||||
14, /* N */
|
||||
10, /* O */
|
||||
15, /* P */
|
||||
9, /* Q */
|
||||
};
|
||||
|
||||
/* NOTE: These are scale factors as applied to Ambisonics content. FuMa
|
||||
* decoder coefficients should be divided by these values to get N3D decoder
|
||||
* coefficients.
|
||||
*/
|
||||
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) */
|
||||
};
|
||||
|
||||
|
||||
void ComputeAmbientGains(const ALCdevice *device, ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS])
|
||||
{
|
||||
char *confkey, *next;
|
||||
char *layout_str;
|
||||
char *sep, *end;
|
||||
enum Channel val;
|
||||
const char *str;
|
||||
int i;
|
||||
ALuint i;
|
||||
|
||||
if(!ConfigValueStr(NULL, name, &str) && !ConfigValueStr(NULL, "layout", &str))
|
||||
return;
|
||||
|
||||
layout_str = strdup(str);
|
||||
next = confkey = layout_str;
|
||||
while(next && *next)
|
||||
for(i = 0;i < device->NumChannels;i++)
|
||||
{
|
||||
confkey = next;
|
||||
next = strchr(confkey, ',');
|
||||
if(next)
|
||||
{
|
||||
*next = 0;
|
||||
do {
|
||||
next++;
|
||||
} while(isspace(*next) || *next == ',');
|
||||
}
|
||||
// The W coefficients are based on a mathematical average of the
|
||||
// output. The square root of the base average provides for a more
|
||||
// perceptual average volume, better suited to non-directional gains.
|
||||
gains[i] = sqrtf(device->AmbiCoeffs[i][0]) * ingain;
|
||||
}
|
||||
for(;i < MAX_OUTPUT_CHANNELS;i++)
|
||||
gains[i] = 0.0f;
|
||||
}
|
||||
|
||||
sep = strchr(confkey, '=');
|
||||
if(!sep || confkey == sep)
|
||||
void ComputeAngleGains(const ALCdevice *device, ALfloat angle, ALfloat elevation, ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS])
|
||||
{
|
||||
ALfloat dir[3] = {
|
||||
sinf(angle) * cosf(elevation),
|
||||
sinf(elevation),
|
||||
-cosf(angle) * cosf(elevation)
|
||||
};
|
||||
ComputeDirectionalGains(device, dir, ingain, gains);
|
||||
}
|
||||
|
||||
void ComputeDirectionalGains(const ALCdevice *device, const ALfloat dir[3], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS])
|
||||
{
|
||||
ALfloat coeffs[MAX_AMBI_COEFFS];
|
||||
ALuint i, j;
|
||||
/* Convert from OpenAL coords to Ambisonics. */
|
||||
ALfloat x = -dir[2];
|
||||
ALfloat y = -dir[0];
|
||||
ALfloat z = dir[1];
|
||||
|
||||
/* Zeroth-order */
|
||||
coeffs[0] = 1.0f; /* ACN 0 = 1 */
|
||||
/* First-order */
|
||||
coeffs[1] = 1.732050808f * y; /* ACN 1 = sqrt(3) * Y */
|
||||
coeffs[2] = 1.732050808f * z; /* ACN 2 = sqrt(3) * Z */
|
||||
coeffs[3] = 1.732050808f * x; /* ACN 3 = sqrt(3) * X */
|
||||
/* Second-order */
|
||||
coeffs[4] = 3.872983346f * x * y; /* ACN 4 = sqrt(15) * X * Y */
|
||||
coeffs[5] = 3.872983346f * y * z; /* ACN 5 = sqrt(15) * Y * Z */
|
||||
coeffs[6] = 1.118033989f * (3.0f*z*z - 1.0f); /* ACN 6 = sqrt(5)/2 * (3*Z*Z - 1) */
|
||||
coeffs[7] = 3.872983346f * x * z; /* ACN 7 = sqrt(15) * X * Z */
|
||||
coeffs[8] = 1.936491673f * (x*x - y*y); /* ACN 8 = sqrt(15)/2 * (X*X - Y*Y) */
|
||||
/* Third-order */
|
||||
coeffs[9] = 2.091650066f * y * (3.0f*x*x - y*y); /* ACN 9 = sqrt(35/8) * Y * (3*X*X - Y*Y) */
|
||||
coeffs[10] = 10.246950766f * z * x * y; /* ACN 10 = sqrt(105) * Z * X * Y */
|
||||
coeffs[11] = 1.620185175f * y * (5.0f*z*z - 1.0f); /* ACN 11 = sqrt(21/8) * Y * (5*Z*Z - 1) */
|
||||
coeffs[12] = 1.322875656f * z * (5.0f*z*z - 3.0f); /* ACN 12 = sqrt(7)/2 * Z * (5*Z*Z - 3) */
|
||||
coeffs[13] = 1.620185175f * x * (5.0f*z*z - 1.0f); /* ACN 13 = sqrt(21/8) * X * (5*Z*Z - 1) */
|
||||
coeffs[14] = 5.123475383f * z * (x*x - y*y); /* ACN 14 = sqrt(105)/2 * Z * (X*X - Y*Y) */
|
||||
coeffs[15] = 2.091650066f * x * (x*x - 3.0f*y*y); /* ACN 15 = sqrt(35/8) * X * (X*X - 3*Y*Y) */
|
||||
|
||||
for(i = 0;i < device->NumChannels;i++)
|
||||
{
|
||||
float gain = 0.0f;
|
||||
for(j = 0;j < MAX_AMBI_COEFFS;j++)
|
||||
gain += device->AmbiCoeffs[i][j]*coeffs[j];
|
||||
gains[i] = gain * ingain;
|
||||
}
|
||||
for(;i < MAX_OUTPUT_CHANNELS;i++)
|
||||
gains[i] = 0.0f;
|
||||
}
|
||||
|
||||
void ComputeBFormatGains(const ALCdevice *device, const ALfloat mtx[4], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS])
|
||||
{
|
||||
ALuint i, j;
|
||||
|
||||
for(i = 0;i < device->NumChannels;i++)
|
||||
{
|
||||
float gain = 0.0f;
|
||||
for(j = 0;j < 4;j++)
|
||||
gain += device->AmbiCoeffs[i][j] * mtx[j];
|
||||
gains[i] = gain * ingain;
|
||||
}
|
||||
for(;i < MAX_OUTPUT_CHANNELS;i++)
|
||||
gains[i] = 0.0f;
|
||||
}
|
||||
|
||||
|
||||
DECL_CONST static inline const char *GetLabelFromChannel(enum Channel channel)
|
||||
{
|
||||
switch(channel)
|
||||
{
|
||||
case FrontLeft: return "front-left";
|
||||
case FrontRight: return "front-right";
|
||||
case FrontCenter: return "front-center";
|
||||
case LFE: return "lfe";
|
||||
case BackLeft: return "back-left";
|
||||
case BackRight: return "back-right";
|
||||
case BackCenter: return "back-center";
|
||||
case SideLeft: return "side-left";
|
||||
case SideRight: return "side-right";
|
||||
|
||||
case BFormatW: return "bformat-w";
|
||||
case BFormatX: return "bformat-x";
|
||||
case BFormatY: return "bformat-y";
|
||||
case BFormatZ: return "bformat-z";
|
||||
|
||||
case InvalidChannel: break;
|
||||
}
|
||||
return "(unknown)";
|
||||
}
|
||||
|
||||
|
||||
typedef struct ChannelMap {
|
||||
enum Channel ChanName;
|
||||
ChannelConfig Config;
|
||||
} ChannelMap;
|
||||
|
||||
static void SetChannelMap(ALCdevice *device, const ChannelMap *chanmap, size_t count, ALfloat ambiscale, ALboolean isfuma)
|
||||
{
|
||||
size_t j, k;
|
||||
ALuint i;
|
||||
|
||||
device->AmbiScale = ambiscale;
|
||||
for(i = 0;i < MAX_OUTPUT_CHANNELS && device->ChannelName[i] != InvalidChannel;i++)
|
||||
{
|
||||
if(device->ChannelName[i] == LFE)
|
||||
{
|
||||
ERR("Malformed speaker key: %s\n", confkey);
|
||||
for(j = 0;j < MAX_AMBI_COEFFS;j++)
|
||||
device->AmbiCoeffs[i][j] = 0.0f;
|
||||
continue;
|
||||
}
|
||||
|
||||
end = sep - 1;
|
||||
while(isspace(*end) && end != confkey)
|
||||
end--;
|
||||
*(++end) = 0;
|
||||
|
||||
if(strcmp(confkey, "fl") == 0 || strcmp(confkey, "front-left") == 0)
|
||||
val = FrontLeft;
|
||||
else if(strcmp(confkey, "fr") == 0 || strcmp(confkey, "front-right") == 0)
|
||||
val = FrontRight;
|
||||
else if(strcmp(confkey, "fc") == 0 || strcmp(confkey, "front-center") == 0)
|
||||
val = FrontCenter;
|
||||
else if(strcmp(confkey, "bl") == 0 || strcmp(confkey, "back-left") == 0)
|
||||
val = BackLeft;
|
||||
else if(strcmp(confkey, "br") == 0 || strcmp(confkey, "back-right") == 0)
|
||||
val = BackRight;
|
||||
else if(strcmp(confkey, "bc") == 0 || strcmp(confkey, "back-center") == 0)
|
||||
val = BackCenter;
|
||||
else if(strcmp(confkey, "sl") == 0 || strcmp(confkey, "side-left") == 0)
|
||||
val = SideLeft;
|
||||
else if(strcmp(confkey, "sr") == 0 || strcmp(confkey, "side-right") == 0)
|
||||
val = SideRight;
|
||||
else
|
||||
for(j = 0;j < count;j++)
|
||||
{
|
||||
ERR("Unknown speaker for %s: \"%s\"\n", name, confkey);
|
||||
continue;
|
||||
}
|
||||
|
||||
*(sep++) = 0;
|
||||
while(isspace(*sep))
|
||||
sep++;
|
||||
|
||||
for(i = 0;i < chans;i++)
|
||||
{
|
||||
if(Speaker2Chan[i] == val)
|
||||
if(device->ChannelName[i] == chanmap[j].ChanName)
|
||||
{
|
||||
long angle = strtol(sep, NULL, 10);
|
||||
if(angle >= -180 && angle <= 180)
|
||||
SpeakerAngle[i] = DEG2RAD(angle);
|
||||
if(isfuma)
|
||||
{
|
||||
/* Reformat FuMa -> ACN/N3D */
|
||||
for(k = 0;k < MAX_AMBI_COEFFS;++k)
|
||||
{
|
||||
ALuint acn = FuMa2ACN[k];
|
||||
device->AmbiCoeffs[i][acn] = chanmap[j].Config[k] / FuMa2N3DScale[acn];
|
||||
}
|
||||
}
|
||||
else
|
||||
ERR("Invalid angle for speaker \"%s\": %ld\n", confkey, angle);
|
||||
{
|
||||
for(k = 0;k < MAX_AMBI_COEFFS;++k)
|
||||
device->AmbiCoeffs[i][k] = chanmap[j].Config[k];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(j == count)
|
||||
ERR("Failed to match %s channel (%u) in config\n", GetLabelFromChannel(device->ChannelName[i]), i);
|
||||
}
|
||||
free(layout_str);
|
||||
layout_str = NULL;
|
||||
|
||||
for(i = 0;i < chans;i++)
|
||||
{
|
||||
int min = i;
|
||||
int i2;
|
||||
|
||||
for(i2 = i+1;i2 < chans;i2++)
|
||||
{
|
||||
if(SpeakerAngle[i2] < SpeakerAngle[min])
|
||||
min = i2;
|
||||
}
|
||||
|
||||
if(min != i)
|
||||
{
|
||||
ALfloat tmpf;
|
||||
enum Channel tmpc;
|
||||
|
||||
tmpf = SpeakerAngle[i];
|
||||
SpeakerAngle[i] = SpeakerAngle[min];
|
||||
SpeakerAngle[min] = tmpf;
|
||||
|
||||
tmpc = Speaker2Chan[i];
|
||||
Speaker2Chan[i] = Speaker2Chan[min];
|
||||
Speaker2Chan[min] = tmpc;
|
||||
}
|
||||
}
|
||||
device->NumChannels = i;
|
||||
}
|
||||
|
||||
|
||||
void ComputeAngleGains(const ALCdevice *device, ALfloat angle, ALfloat hwidth, ALfloat ingain, ALfloat gains[MaxChannels])
|
||||
static bool LoadChannelSetup(ALCdevice *device)
|
||||
{
|
||||
ALfloat tmpgains[MaxChannels] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
|
||||
enum Channel Speaker2Chan[MaxChannels];
|
||||
ALfloat SpeakerAngle[MaxChannels];
|
||||
ALfloat langle, rangle;
|
||||
ALfloat a;
|
||||
ALuint i;
|
||||
static const enum Channel mono_chans[1] = {
|
||||
FrontCenter
|
||||
}, stereo_chans[2] = {
|
||||
FrontLeft, FrontRight
|
||||
}, quad_chans[4] = {
|
||||
FrontLeft, FrontRight,
|
||||
BackLeft, BackRight
|
||||
}, surround51_chans[5] = {
|
||||
FrontLeft, FrontRight, FrontCenter,
|
||||
SideLeft, SideRight
|
||||
}, surround51rear_chans[5] = {
|
||||
FrontLeft, FrontRight, FrontCenter,
|
||||
BackLeft, BackRight
|
||||
}, surround61_chans[6] = {
|
||||
FrontLeft, FrontRight,
|
||||
FrontCenter, BackCenter,
|
||||
SideLeft, SideRight
|
||||
}, surround71_chans[7] = {
|
||||
FrontLeft, FrontRight, FrontCenter,
|
||||
BackLeft, BackRight,
|
||||
SideLeft, SideRight
|
||||
};
|
||||
ChannelMap chanmap[MAX_OUTPUT_CHANNELS];
|
||||
const enum Channel *channels = NULL;
|
||||
const char *layout = NULL;
|
||||
ALfloat ambiscale = 1.0f;
|
||||
size_t count = 0;
|
||||
int isfuma;
|
||||
int order;
|
||||
size_t i;
|
||||
|
||||
for(i = 0;i < device->NumChan;i++)
|
||||
Speaker2Chan[i] = device->Speaker2Chan[i];
|
||||
for(i = 0;i < device->NumChan;i++)
|
||||
SpeakerAngle[i] = device->SpeakerAngle[i];
|
||||
|
||||
/* Some easy special-cases first... */
|
||||
if(device->NumChan <= 1 || hwidth >= F_PI)
|
||||
{
|
||||
/* Full coverage for all speakers. */
|
||||
for(i = 0;i < MaxChannels;i++)
|
||||
gains[i] = 0.0f;
|
||||
for(i = 0;i < device->NumChan;i++)
|
||||
{
|
||||
enum Channel chan = Speaker2Chan[i];
|
||||
gains[chan] = ingain;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if(hwidth <= 0.0f)
|
||||
{
|
||||
/* Infinitely small sound point. */
|
||||
for(i = 0;i < MaxChannels;i++)
|
||||
gains[i] = 0.0f;
|
||||
for(i = 0;i < device->NumChan-1;i++)
|
||||
{
|
||||
if(angle >= SpeakerAngle[i] && angle < SpeakerAngle[i+1])
|
||||
{
|
||||
/* Sound is between speakers i and i+1 */
|
||||
a = (angle-SpeakerAngle[i]) /
|
||||
(SpeakerAngle[i+1]-SpeakerAngle[i]);
|
||||
gains[Speaker2Chan[i]] = sqrtf(1.0f-a) * ingain;
|
||||
gains[Speaker2Chan[i+1]] = sqrtf( a) * ingain;
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* Sound is between last and first speakers */
|
||||
if(angle < SpeakerAngle[0])
|
||||
angle += F_2PI;
|
||||
a = (angle-SpeakerAngle[i]) /
|
||||
(F_2PI + SpeakerAngle[0]-SpeakerAngle[i]);
|
||||
gains[Speaker2Chan[i]] = sqrtf(1.0f-a) * ingain;
|
||||
gains[Speaker2Chan[0]] = sqrtf( a) * ingain;
|
||||
return;
|
||||
}
|
||||
|
||||
if(fabsf(angle)+hwidth > F_PI)
|
||||
{
|
||||
/* The coverage area would go outside of -pi...+pi. Instead, rotate the
|
||||
* speaker angles so it would be as if angle=0, and keep them wrapped
|
||||
* within -pi...+pi. */
|
||||
if(angle > 0.0f)
|
||||
{
|
||||
ALuint done;
|
||||
ALuint i = 0;
|
||||
while(i < device->NumChan && device->SpeakerAngle[i]-angle < -F_PI)
|
||||
i++;
|
||||
for(done = 0;i < device->NumChan;done++)
|
||||
{
|
||||
SpeakerAngle[done] = device->SpeakerAngle[i]-angle;
|
||||
Speaker2Chan[done] = device->Speaker2Chan[i];
|
||||
i++;
|
||||
}
|
||||
for(i = 0;done < device->NumChan;i++)
|
||||
{
|
||||
SpeakerAngle[done] = device->SpeakerAngle[i]-angle + F_2PI;
|
||||
Speaker2Chan[done] = device->Speaker2Chan[i];
|
||||
done++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* NOTE: '< device->NumChan' on the iterators is correct here since
|
||||
* we need to handle index 0. Because the iterators are unsigned,
|
||||
* they'll underflow and wrap to become 0xFFFFFFFF, which will
|
||||
* break as expected. */
|
||||
ALuint done;
|
||||
ALuint i = device->NumChan-1;
|
||||
while(i < device->NumChan && device->SpeakerAngle[i]-angle > F_PI)
|
||||
i--;
|
||||
for(done = device->NumChan-1;i < device->NumChan;done--)
|
||||
{
|
||||
SpeakerAngle[done] = device->SpeakerAngle[i]-angle;
|
||||
Speaker2Chan[done] = device->Speaker2Chan[i];
|
||||
i--;
|
||||
}
|
||||
for(i = device->NumChan-1;done < device->NumChan;i--)
|
||||
{
|
||||
SpeakerAngle[done] = device->SpeakerAngle[i]-angle - F_2PI;
|
||||
Speaker2Chan[done] = device->Speaker2Chan[i];
|
||||
done--;
|
||||
}
|
||||
}
|
||||
angle = 0.0f;
|
||||
}
|
||||
langle = angle - hwidth;
|
||||
rangle = angle + hwidth;
|
||||
|
||||
/* First speaker */
|
||||
i = 0;
|
||||
do {
|
||||
ALuint last = device->NumChan-1;
|
||||
enum Channel chan = Speaker2Chan[i];
|
||||
|
||||
if(SpeakerAngle[i] >= langle && SpeakerAngle[i] <= rangle)
|
||||
{
|
||||
tmpgains[chan] = 1.0f;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(SpeakerAngle[i] < langle && SpeakerAngle[i+1] > langle)
|
||||
{
|
||||
a = (langle-SpeakerAngle[i]) /
|
||||
(SpeakerAngle[i+1]-SpeakerAngle[i]);
|
||||
tmpgains[chan] = lerp(tmpgains[chan], 1.0f, 1.0f-a);
|
||||
}
|
||||
if(SpeakerAngle[i] > rangle)
|
||||
{
|
||||
a = (F_2PI + rangle-SpeakerAngle[last]) /
|
||||
(F_2PI + SpeakerAngle[i]-SpeakerAngle[last]);
|
||||
tmpgains[chan] = lerp(tmpgains[chan], 1.0f, a);
|
||||
}
|
||||
else if(SpeakerAngle[last] < rangle)
|
||||
{
|
||||
a = (rangle-SpeakerAngle[last]) /
|
||||
(F_2PI + SpeakerAngle[i]-SpeakerAngle[last]);
|
||||
tmpgains[chan] = lerp(tmpgains[chan], 1.0f, a);
|
||||
}
|
||||
} while(0);
|
||||
|
||||
for(i = 1;i < device->NumChan-1;i++)
|
||||
{
|
||||
enum Channel chan = Speaker2Chan[i];
|
||||
if(SpeakerAngle[i] >= langle && SpeakerAngle[i] <= rangle)
|
||||
{
|
||||
tmpgains[chan] = 1.0f;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(SpeakerAngle[i] < langle && SpeakerAngle[i+1] > langle)
|
||||
{
|
||||
a = (langle-SpeakerAngle[i]) /
|
||||
(SpeakerAngle[i+1]-SpeakerAngle[i]);
|
||||
tmpgains[chan] = lerp(tmpgains[chan], 1.0f, 1.0f-a);
|
||||
}
|
||||
if(SpeakerAngle[i] > rangle && SpeakerAngle[i-1] < rangle)
|
||||
{
|
||||
a = (rangle-SpeakerAngle[i-1]) /
|
||||
(SpeakerAngle[i]-SpeakerAngle[i-1]);
|
||||
tmpgains[chan] = lerp(tmpgains[chan], 1.0f, a);
|
||||
}
|
||||
}
|
||||
|
||||
/* Last speaker */
|
||||
i = device->NumChan-1;
|
||||
do {
|
||||
enum Channel chan = Speaker2Chan[i];
|
||||
if(SpeakerAngle[i] >= langle && SpeakerAngle[i] <= rangle)
|
||||
{
|
||||
tmpgains[Speaker2Chan[i]] = 1.0f;
|
||||
continue;
|
||||
}
|
||||
if(SpeakerAngle[i] > rangle && SpeakerAngle[i-1] < rangle)
|
||||
{
|
||||
a = (rangle-SpeakerAngle[i-1]) /
|
||||
(SpeakerAngle[i]-SpeakerAngle[i-1]);
|
||||
tmpgains[chan] = lerp(tmpgains[chan], 1.0f, a);
|
||||
}
|
||||
if(SpeakerAngle[i] < langle)
|
||||
{
|
||||
a = (langle-SpeakerAngle[i]) /
|
||||
(F_2PI + SpeakerAngle[0]-SpeakerAngle[i]);
|
||||
tmpgains[chan] = lerp(tmpgains[chan], 1.0f, 1.0f-a);
|
||||
}
|
||||
else if(SpeakerAngle[0] > langle)
|
||||
{
|
||||
a = (F_2PI + langle-SpeakerAngle[i]) /
|
||||
(F_2PI + SpeakerAngle[0]-SpeakerAngle[i]);
|
||||
tmpgains[chan] = lerp(tmpgains[chan], 1.0f, 1.0f-a);
|
||||
}
|
||||
} while(0);
|
||||
|
||||
for(i = 0;i < device->NumChan;i++)
|
||||
{
|
||||
enum Channel chan = device->Speaker2Chan[i];
|
||||
gains[chan] = sqrtf(tmpgains[chan]) * ingain;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ALvoid aluInitPanning(ALCdevice *Device)
|
||||
{
|
||||
const char *layoutname = NULL;
|
||||
enum Channel *Speaker2Chan;
|
||||
ALfloat *SpeakerAngle;
|
||||
|
||||
Speaker2Chan = Device->Speaker2Chan;
|
||||
SpeakerAngle = Device->SpeakerAngle;
|
||||
switch(Device->FmtChans)
|
||||
switch(device->FmtChans)
|
||||
{
|
||||
case DevFmtMono:
|
||||
Device->NumChan = 1;
|
||||
Speaker2Chan[0] = FrontCenter;
|
||||
SpeakerAngle[0] = DEG2RAD(0.0f);
|
||||
layoutname = NULL;
|
||||
layout = "mono";
|
||||
channels = mono_chans;
|
||||
count = COUNTOF(mono_chans);
|
||||
break;
|
||||
case DevFmtStereo:
|
||||
layout = "stereo";
|
||||
channels = stereo_chans;
|
||||
count = COUNTOF(stereo_chans);
|
||||
break;
|
||||
case DevFmtQuad:
|
||||
layout = "quad";
|
||||
channels = quad_chans;
|
||||
count = COUNTOF(quad_chans);
|
||||
break;
|
||||
case DevFmtX51:
|
||||
layout = "surround51";
|
||||
channels = surround51_chans;
|
||||
count = COUNTOF(surround51_chans);
|
||||
break;
|
||||
case DevFmtX51Rear:
|
||||
layout = "surround51rear";
|
||||
channels = surround51rear_chans;
|
||||
count = COUNTOF(surround51rear_chans);
|
||||
break;
|
||||
case DevFmtX61:
|
||||
layout = "surround61";
|
||||
channels = surround61_chans;
|
||||
count = COUNTOF(surround61_chans);
|
||||
break;
|
||||
case DevFmtX71:
|
||||
layout = "surround71";
|
||||
channels = surround71_chans;
|
||||
count = COUNTOF(surround71_chans);
|
||||
break;
|
||||
case DevFmtBFormat3D:
|
||||
break;
|
||||
}
|
||||
|
||||
if(!layout)
|
||||
return false;
|
||||
else
|
||||
{
|
||||
char name[32] = {0};
|
||||
const char *type;
|
||||
char eol;
|
||||
|
||||
snprintf(name, sizeof(name), "%s/type", layout);
|
||||
if(!ConfigValueStr(al_string_get_cstr(device->DeviceName), "layouts", name, &type))
|
||||
return false;
|
||||
|
||||
if(sscanf(type, " %31[^: ] : %d%c", name, &order, &eol) != 2)
|
||||
{
|
||||
ERR("Invalid type value '%s' (expected name:order) for layout %s\n", type, layout);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(strcasecmp(name, "fuma") == 0)
|
||||
isfuma = 1;
|
||||
else if(strcasecmp(name, "n3d") == 0)
|
||||
isfuma = 0;
|
||||
else
|
||||
{
|
||||
ERR("Unhandled type name '%s' (expected FuMa or N3D) for layout %s\n", name, layout);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(order == 3)
|
||||
ambiscale = THIRD_ORDER_SCALE;
|
||||
else if(order == 2)
|
||||
ambiscale = SECOND_ORDER_SCALE;
|
||||
else if(order == 1)
|
||||
ambiscale = FIRST_ORDER_SCALE;
|
||||
else if(order == 0)
|
||||
ambiscale = ZERO_ORDER_SCALE;
|
||||
else
|
||||
{
|
||||
ERR("Unhandled type order %d (expected 0, 1, 2, or 3) for layout %s\n", order, layout);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for(i = 0;i < count;i++)
|
||||
{
|
||||
float coeffs[MAX_AMBI_COEFFS] = {0.0f};
|
||||
const char *channame;
|
||||
char chanlayout[32];
|
||||
const char *value;
|
||||
int props = 0;
|
||||
char eol = 0;
|
||||
int j;
|
||||
|
||||
chanmap[i].ChanName = channels[i];
|
||||
channame = GetLabelFromChannel(channels[i]);
|
||||
|
||||
snprintf(chanlayout, sizeof(chanlayout), "%s/%s", layout, channame);
|
||||
if(!ConfigValueStr(al_string_get_cstr(device->DeviceName), "layouts", chanlayout, &value))
|
||||
{
|
||||
ERR("Missing channel %s\n", channame);
|
||||
return false;
|
||||
}
|
||||
if(order == 3)
|
||||
props = sscanf(value, " %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %c",
|
||||
&coeffs[0], &coeffs[1], &coeffs[2], &coeffs[3],
|
||||
&coeffs[4], &coeffs[5], &coeffs[6], &coeffs[7],
|
||||
&coeffs[8], &coeffs[9], &coeffs[10], &coeffs[11],
|
||||
&coeffs[12], &coeffs[13], &coeffs[14], &coeffs[15],
|
||||
&eol
|
||||
);
|
||||
else if(order == 2)
|
||||
props = sscanf(value, " %f %f %f %f %f %f %f %f %f %c",
|
||||
&coeffs[0], &coeffs[1], &coeffs[2],
|
||||
&coeffs[3], &coeffs[4], &coeffs[5],
|
||||
&coeffs[6], &coeffs[7], &coeffs[8],
|
||||
&eol
|
||||
);
|
||||
else if(order == 1)
|
||||
props = sscanf(value, " %f %f %f %f %c",
|
||||
&coeffs[0], &coeffs[1],
|
||||
&coeffs[2], &coeffs[3],
|
||||
&eol
|
||||
);
|
||||
else if(order == 0)
|
||||
props = sscanf(value, " %f %c", &coeffs[0], &eol);
|
||||
if(props == 0)
|
||||
{
|
||||
ERR("Failed to parse option %s properties\n", chanlayout);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(props > (order+1)*(order+1))
|
||||
{
|
||||
ERR("Excess elements in option %s (expected %d)\n", chanlayout, (order+1)*(order+1));
|
||||
return false;
|
||||
}
|
||||
|
||||
for(j = 0;j < MAX_AMBI_COEFFS;++j)
|
||||
chanmap[i].Config[j] = coeffs[j];
|
||||
}
|
||||
SetChannelMap(device, chanmap, count, ambiscale, isfuma);
|
||||
return true;
|
||||
}
|
||||
|
||||
ALvoid aluInitPanning(ALCdevice *device)
|
||||
{
|
||||
/* NOTE: These decoder coefficients are using FuMa channel ordering and
|
||||
* normalization, since that's what was produced by the Ambisonic Decoder
|
||||
* Toolbox. SetChannelMap will convert them to N3D.
|
||||
*/
|
||||
static const ChannelMap MonoCfg[1] = {
|
||||
{ FrontCenter, { 1.414213562f } },
|
||||
}, StereoCfg[2] = {
|
||||
{ FrontLeft, { 0.707106781f, 0.0f, 0.5f, 0.0f } },
|
||||
{ FrontRight, { 0.707106781f, 0.0f, -0.5f, 0.0f } },
|
||||
}, QuadCfg[4] = {
|
||||
{ FrontLeft, { 0.353553f, 0.306184f, 0.306184f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000f, 0.117186f } },
|
||||
{ FrontRight, { 0.353553f, 0.306184f, -0.306184f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000f, -0.117186f } },
|
||||
{ BackLeft, { 0.353553f, -0.306184f, 0.306184f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000f, -0.117186f } },
|
||||
{ BackRight, { 0.353553f, -0.306184f, -0.306184f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000f, 0.117186f } },
|
||||
}, X51SideCfg[5] = {
|
||||
{ FrontLeft, { 0.208954f, 0.212846f, 0.238350f, 0.0f, 0.0f, 0.0f, 0.0f, -0.017738f, 0.204014f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.051023f, 0.047490f } },
|
||||
{ FrontRight, { 0.208954f, 0.212846f, -0.238350f, 0.0f, 0.0f, 0.0f, 0.0f, -0.017738f, -0.204014f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.051023f, -0.047490f } },
|
||||
{ FrontCenter, { 0.109403f, 0.179490f, 0.000000f, 0.0f, 0.0f, 0.0f, 0.0f, 0.142031f, 0.000000f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.072024f, 0.000000f } },
|
||||
{ SideLeft, { 0.470936f, -0.369626f, 0.349386f, 0.0f, 0.0f, 0.0f, 0.0f, -0.031375f, -0.058144f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.007119f, -0.043968f } },
|
||||
{ SideRight, { 0.470936f, -0.369626f, -0.349386f, 0.0f, 0.0f, 0.0f, 0.0f, -0.031375f, 0.058144f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.007119f, 0.043968f } },
|
||||
}, X51RearCfg[5] = {
|
||||
{ FrontLeft, { 0.208954f, 0.212846f, 0.238350f, 0.0f, 0.0f, 0.0f, 0.0f, -0.017738f, 0.204014f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.051023f, 0.047490f } },
|
||||
{ FrontRight, { 0.208954f, 0.212846f, -0.238350f, 0.0f, 0.0f, 0.0f, 0.0f, -0.017738f, -0.204014f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.051023f, -0.047490f } },
|
||||
{ FrontCenter, { 0.109403f, 0.179490f, 0.000000f, 0.0f, 0.0f, 0.0f, 0.0f, 0.142031f, 0.000000f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.072024f, 0.000000f } },
|
||||
{ BackLeft, { 0.470936f, -0.369626f, 0.349386f, 0.0f, 0.0f, 0.0f, 0.0f, -0.031375f, -0.058144f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.007119f, -0.043968f } },
|
||||
{ BackRight, { 0.470936f, -0.369626f, -0.349386f, 0.0f, 0.0f, 0.0f, 0.0f, -0.031375f, 0.058144f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.007119f, 0.043968f } },
|
||||
}, X61Cfg[6] = {
|
||||
{ FrontLeft, { 0.167065f, 0.200583f, 0.172695f, 0.0f, 0.0f, 0.0f, 0.0f, 0.029855f, 0.186407f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.039241f, 0.068910f } },
|
||||
{ FrontRight, { 0.167065f, 0.200583f, -0.172695f, 0.0f, 0.0f, 0.0f, 0.0f, 0.029855f, -0.186407f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.039241f, -0.068910f } },
|
||||
{ FrontCenter, { 0.109403f, 0.179490f, 0.000000f, 0.0f, 0.0f, 0.0f, 0.0f, 0.142031f, 0.000000f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.072024f, 0.000000f } },
|
||||
{ BackCenter, { 0.353556f, -0.461940f, 0.000000f, 0.0f, 0.0f, 0.0f, 0.0f, 0.165723f, 0.000000f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000f, 0.000000f } },
|
||||
{ SideLeft, { 0.289151f, -0.081301f, 0.401292f, 0.0f, 0.0f, 0.0f, 0.0f, -0.188208f, -0.071420f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.010099f, -0.032897f } },
|
||||
{ SideRight, { 0.289151f, -0.081301f, -0.401292f, 0.0f, 0.0f, 0.0f, 0.0f, -0.188208f, 0.071420f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.010099f, 0.032897f } },
|
||||
}, X71Cfg[7] = {
|
||||
{ FrontLeft, { 0.167065f, 0.200583f, 0.172695f, 0.0f, 0.0f, 0.0f, 0.0f, 0.029855f, 0.186407f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.039241f, 0.068910f } },
|
||||
{ FrontRight, { 0.167065f, 0.200583f, -0.172695f, 0.0f, 0.0f, 0.0f, 0.0f, 0.029855f, -0.186407f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.039241f, -0.068910f } },
|
||||
{ FrontCenter, { 0.109403f, 0.179490f, 0.000000f, 0.0f, 0.0f, 0.0f, 0.0f, 0.142031f, 0.000000f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.072024f, 0.000000f } },
|
||||
{ BackLeft, { 0.224752f, -0.295009f, 0.170325f, 0.0f, 0.0f, 0.0f, 0.0f, 0.105349f, -0.182473f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000f, 0.065799f } },
|
||||
{ BackRight, { 0.224752f, -0.295009f, -0.170325f, 0.0f, 0.0f, 0.0f, 0.0f, 0.105349f, 0.182473f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000f, -0.065799f } },
|
||||
{ SideLeft, { 0.224739f, 0.000000f, 0.340644f, 0.0f, 0.0f, 0.0f, 0.0f, -0.210697f, 0.000000f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000f, -0.065795f } },
|
||||
{ SideRight, { 0.224739f, 0.000000f, -0.340644f, 0.0f, 0.0f, 0.0f, 0.0f, -0.210697f, 0.000000f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000f, 0.065795f } },
|
||||
}, BFormat3D[4] = {
|
||||
{ BFormatW, { 1.0f, 0.0f, 0.0f, 0.0f } },
|
||||
{ BFormatX, { 0.0f, 1.0f, 0.0f, 0.0f } },
|
||||
{ BFormatY, { 0.0f, 0.0f, 1.0f, 0.0f } },
|
||||
{ BFormatZ, { 0.0f, 0.0f, 0.0f, 1.0f } },
|
||||
};
|
||||
const ChannelMap *chanmap = NULL;
|
||||
ALfloat ambiscale = 1.0f;
|
||||
size_t count = 0;
|
||||
|
||||
device->AmbiScale = 1.0f;
|
||||
memset(device->AmbiCoeffs, 0, sizeof(device->AmbiCoeffs));
|
||||
device->NumChannels = 0;
|
||||
|
||||
if(device->Hrtf)
|
||||
{
|
||||
ALfloat (*coeffs_list[4])[2];
|
||||
ALuint *delay_list[4];
|
||||
ALuint i;
|
||||
|
||||
count = COUNTOF(BFormat3D);
|
||||
chanmap = BFormat3D;
|
||||
ambiscale = 1.0f;
|
||||
|
||||
for(i = 0;i < count;i++)
|
||||
device->ChannelName[i] = chanmap[i].ChanName;
|
||||
for(;i < MAX_OUTPUT_CHANNELS;i++)
|
||||
device->ChannelName[i] = InvalidChannel;
|
||||
SetChannelMap(device, chanmap, count, ambiscale, AL_TRUE);
|
||||
|
||||
for(i = 0;i < 4;++i)
|
||||
{
|
||||
static const enum Channel inputs[4] = { BFormatW, BFormatX, BFormatY, BFormatZ };
|
||||
int chan = GetChannelIdxByName(device, inputs[i]);
|
||||
coeffs_list[i] = device->Hrtf_Params[chan].Coeffs;
|
||||
delay_list[i] = device->Hrtf_Params[chan].Delay;
|
||||
}
|
||||
GetBFormatHrtfCoeffs(device->Hrtf, 4, coeffs_list, delay_list);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if(LoadChannelSetup(device))
|
||||
return;
|
||||
|
||||
switch(device->FmtChans)
|
||||
{
|
||||
case DevFmtMono:
|
||||
count = COUNTOF(MonoCfg);
|
||||
chanmap = MonoCfg;
|
||||
ambiscale = ZERO_ORDER_SCALE;
|
||||
break;
|
||||
|
||||
case DevFmtStereo:
|
||||
Device->NumChan = 2;
|
||||
Speaker2Chan[0] = FrontLeft;
|
||||
Speaker2Chan[1] = FrontRight;
|
||||
SpeakerAngle[0] = DEG2RAD(-90.0f);
|
||||
SpeakerAngle[1] = DEG2RAD( 90.0f);
|
||||
layoutname = "layout_stereo";
|
||||
count = COUNTOF(StereoCfg);
|
||||
chanmap = StereoCfg;
|
||||
ambiscale = FIRST_ORDER_SCALE;
|
||||
break;
|
||||
|
||||
case DevFmtQuad:
|
||||
Device->NumChan = 4;
|
||||
Speaker2Chan[0] = BackLeft;
|
||||
Speaker2Chan[1] = FrontLeft;
|
||||
Speaker2Chan[2] = FrontRight;
|
||||
Speaker2Chan[3] = BackRight;
|
||||
SpeakerAngle[0] = DEG2RAD(-135.0f);
|
||||
SpeakerAngle[1] = DEG2RAD( -45.0f);
|
||||
SpeakerAngle[2] = DEG2RAD( 45.0f);
|
||||
SpeakerAngle[3] = DEG2RAD( 135.0f);
|
||||
layoutname = "layout_quad";
|
||||
count = COUNTOF(QuadCfg);
|
||||
chanmap = QuadCfg;
|
||||
ambiscale = SECOND_ORDER_SCALE;
|
||||
break;
|
||||
|
||||
case DevFmtX51:
|
||||
Device->NumChan = 5;
|
||||
Speaker2Chan[0] = BackLeft;
|
||||
Speaker2Chan[1] = FrontLeft;
|
||||
Speaker2Chan[2] = FrontCenter;
|
||||
Speaker2Chan[3] = FrontRight;
|
||||
Speaker2Chan[4] = BackRight;
|
||||
SpeakerAngle[0] = DEG2RAD(-110.0f);
|
||||
SpeakerAngle[1] = DEG2RAD( -30.0f);
|
||||
SpeakerAngle[2] = DEG2RAD( 0.0f);
|
||||
SpeakerAngle[3] = DEG2RAD( 30.0f);
|
||||
SpeakerAngle[4] = DEG2RAD( 110.0f);
|
||||
layoutname = "layout_surround51";
|
||||
count = COUNTOF(X51SideCfg);
|
||||
chanmap = X51SideCfg;
|
||||
ambiscale = THIRD_ORDER_SCALE;
|
||||
break;
|
||||
|
||||
case DevFmtX51Side:
|
||||
Device->NumChan = 5;
|
||||
Speaker2Chan[0] = SideLeft;
|
||||
Speaker2Chan[1] = FrontLeft;
|
||||
Speaker2Chan[2] = FrontCenter;
|
||||
Speaker2Chan[3] = FrontRight;
|
||||
Speaker2Chan[4] = SideRight;
|
||||
SpeakerAngle[0] = DEG2RAD(-90.0f);
|
||||
SpeakerAngle[1] = DEG2RAD(-30.0f);
|
||||
SpeakerAngle[2] = DEG2RAD( 0.0f);
|
||||
SpeakerAngle[3] = DEG2RAD( 30.0f);
|
||||
SpeakerAngle[4] = DEG2RAD( 90.0f);
|
||||
layoutname = "layout_side51";
|
||||
case DevFmtX51Rear:
|
||||
count = COUNTOF(X51RearCfg);
|
||||
chanmap = X51RearCfg;
|
||||
ambiscale = THIRD_ORDER_SCALE;
|
||||
break;
|
||||
|
||||
case DevFmtX61:
|
||||
Device->NumChan = 6;
|
||||
Speaker2Chan[0] = SideLeft;
|
||||
Speaker2Chan[1] = FrontLeft;
|
||||
Speaker2Chan[2] = FrontCenter;
|
||||
Speaker2Chan[3] = FrontRight;
|
||||
Speaker2Chan[4] = SideRight;
|
||||
Speaker2Chan[5] = BackCenter;
|
||||
SpeakerAngle[0] = DEG2RAD(-90.0f);
|
||||
SpeakerAngle[1] = DEG2RAD(-30.0f);
|
||||
SpeakerAngle[2] = DEG2RAD( 0.0f);
|
||||
SpeakerAngle[3] = DEG2RAD( 30.0f);
|
||||
SpeakerAngle[4] = DEG2RAD( 90.0f);
|
||||
SpeakerAngle[5] = DEG2RAD(180.0f);
|
||||
layoutname = "layout_surround61";
|
||||
count = COUNTOF(X61Cfg);
|
||||
chanmap = X61Cfg;
|
||||
ambiscale = THIRD_ORDER_SCALE;
|
||||
break;
|
||||
|
||||
case DevFmtX71:
|
||||
Device->NumChan = 7;
|
||||
Speaker2Chan[0] = BackLeft;
|
||||
Speaker2Chan[1] = SideLeft;
|
||||
Speaker2Chan[2] = FrontLeft;
|
||||
Speaker2Chan[3] = FrontCenter;
|
||||
Speaker2Chan[4] = FrontRight;
|
||||
Speaker2Chan[5] = SideRight;
|
||||
Speaker2Chan[6] = BackRight;
|
||||
SpeakerAngle[0] = DEG2RAD(-150.0f);
|
||||
SpeakerAngle[1] = DEG2RAD( -90.0f);
|
||||
SpeakerAngle[2] = DEG2RAD( -30.0f);
|
||||
SpeakerAngle[3] = DEG2RAD( 0.0f);
|
||||
SpeakerAngle[4] = DEG2RAD( 30.0f);
|
||||
SpeakerAngle[5] = DEG2RAD( 90.0f);
|
||||
SpeakerAngle[6] = DEG2RAD( 150.0f);
|
||||
layoutname = "layout_surround71";
|
||||
count = COUNTOF(X71Cfg);
|
||||
chanmap = X71Cfg;
|
||||
ambiscale = THIRD_ORDER_SCALE;
|
||||
break;
|
||||
|
||||
case DevFmtBFormat3D:
|
||||
count = COUNTOF(BFormat3D);
|
||||
chanmap = BFormat3D;
|
||||
ambiscale = 1.0f;
|
||||
break;
|
||||
}
|
||||
if(layoutname && Device->Type != Loopback)
|
||||
SetSpeakerArrangement(layoutname, SpeakerAngle, Speaker2Chan, Device->NumChan);
|
||||
|
||||
SetChannelMap(device, chanmap, count, ambiscale, AL_TRUE);
|
||||
}
|
||||
|
||||
@@ -7,21 +7,21 @@
|
||||
|
||||
/* "Base" vector type, designed to alias with the actual vector types. */
|
||||
typedef struct vector__s {
|
||||
ALsizei Capacity;
|
||||
ALsizei Size;
|
||||
size_t Capacity;
|
||||
size_t Size;
|
||||
} *vector_;
|
||||
|
||||
#define TYPEDEF_VECTOR(T, N) typedef struct { \
|
||||
ALsizei Capacity; \
|
||||
ALsizei Size; \
|
||||
size_t Capacity; \
|
||||
size_t Size; \
|
||||
T Data[]; \
|
||||
} _##N; \
|
||||
typedef _##N* N; \
|
||||
typedef const _##N* const_##N;
|
||||
|
||||
#define VECTOR(T) struct { \
|
||||
ALsizei Capacity; \
|
||||
ALsizei Size; \
|
||||
size_t Capacity; \
|
||||
size_t Size; \
|
||||
T Data[]; \
|
||||
}*
|
||||
|
||||
@@ -30,10 +30,10 @@ typedef const _##N* const_##N;
|
||||
#define VECTOR_DEINIT(_x) do { free((_x)); (_x) = NULL; } while(0)
|
||||
|
||||
/* Helper to increase a vector's reserve. Do not call directly. */
|
||||
ALboolean vector_reserve(char *ptr, size_t base_size, size_t obj_size, ALsizei obj_count, ALboolean exact);
|
||||
ALboolean vector_reserve(char *ptr, size_t base_size, size_t obj_size, size_t obj_count, ALboolean exact);
|
||||
#define VECTOR_RESERVE(_x, _c) (vector_reserve((char*)&(_x), sizeof(*(_x)), sizeof((_x)->Data[0]), (_c), AL_TRUE))
|
||||
|
||||
ALboolean vector_resize(char *ptr, size_t base_size, size_t obj_size, ALsizei obj_count);
|
||||
ALboolean vector_resize(char *ptr, size_t base_size, size_t obj_size, size_t obj_count);
|
||||
#define VECTOR_RESIZE(_x, _c) (vector_resize((char*)&(_x), sizeof(*(_x)), sizeof((_x)->Data[0]), (_c)))
|
||||
|
||||
#define VECTOR_CAPACITY(_x) ((_x) ? (_x)->Capacity : 0)
|
||||
@@ -73,6 +73,13 @@ ALboolean vector_insert(char *ptr, size_t base_size, size_t obj_size, void *ins_
|
||||
_f(_iter); \
|
||||
} while(0)
|
||||
|
||||
#define VECTOR_FOR_EACH_PARAMS(_t, _x, _f, ...) do { \
|
||||
_t *_iter = VECTOR_ITER_BEGIN((_x)); \
|
||||
_t *_end = VECTOR_ITER_END((_x)); \
|
||||
for(;_iter != _end;++_iter) \
|
||||
_f(__VA_ARGS__, _iter); \
|
||||
} while(0)
|
||||
|
||||
#define VECTOR_FIND_IF(_i, _t, _x, _f) do { \
|
||||
_t *_iter = VECTOR_ITER_BEGIN((_x)); \
|
||||
_t *_end = VECTOR_ITER_END((_x)); \
|
||||
@@ -84,4 +91,15 @@ ALboolean vector_insert(char *ptr, size_t base_size, size_t obj_size, void *ins_
|
||||
(_i) = _iter; \
|
||||
} while(0)
|
||||
|
||||
#define VECTOR_FIND_IF_PARMS(_i, _t, _x, _f, ...) do { \
|
||||
_t *_iter = VECTOR_ITER_BEGIN((_x)); \
|
||||
_t *_end = VECTOR_ITER_END((_x)); \
|
||||
for(;_iter != _end;++_iter) \
|
||||
{ \
|
||||
if(_f(__VA_ARGS__, _iter)) \
|
||||
break; \
|
||||
} \
|
||||
(_i) = _iter; \
|
||||
} while(0)
|
||||
|
||||
#endif /* AL_VECTOR_H */
|
||||
|
||||
Reference in New Issue
Block a user