aboutsummaryrefslogtreecommitdiffstats
path: root/Alc
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2019-04-07 23:39:04 +0200
committerSven Gothel <[email protected]>2019-04-07 23:39:04 +0200
commit73233ce69919fc19c53ce8663c5b8cc05227f07e (patch)
treef2b6ccc1a14d7c387f33398a44ea4511d7ecb212 /Alc
parent8efa4c7ba5ee8eb399d31a9884e45f743d4625ad (diff)
parent99a55c445211fea77af6ab61cbc6a6ec4fbdc9b9 (diff)
Merge branch 'v1.19' of git://repo.or.cz/openal-soft into v1.19v1.19
Diffstat (limited to 'Alc')
-rw-r--r--Alc/ALc.c2799
-rw-r--r--Alc/ALu.c2490
-rw-r--r--Alc/alcRing.c401
-rw-r--r--Alc/alconfig.c (renamed from Alc/alcConfig.c)204
-rw-r--r--Alc/alconfig.h17
-rw-r--r--Alc/alstring.h44
-rw-r--r--Alc/ambdec.c566
-rw-r--r--Alc/ambdec.h46
-rw-r--r--Alc/backends/alsa.c306
-rw-r--r--Alc/backends/base.c187
-rw-r--r--Alc/backends/base.h62
-rw-r--r--Alc/backends/coreaudio.c586
-rw-r--r--Alc/backends/dsound.c372
-rw-r--r--Alc/backends/jack.c171
-rw-r--r--Alc/backends/loopback.c13
-rw-r--r--Alc/backends/null.c31
-rw-r--r--Alc/backends/opensl.c985
-rw-r--r--Alc/backends/oss.c541
-rw-r--r--Alc/backends/portaudio.c62
-rw-r--r--Alc/backends/pulseaudio.c414
-rw-r--r--Alc/backends/qsa.c415
-rw-r--r--Alc/backends/sdl2.c288
-rw-r--r--Alc/backends/sndio.c476
-rw-r--r--Alc/backends/solaris.c112
-rw-r--r--Alc/backends/wasapi.c (renamed from Alc/backends/mmdevapi.c)909
-rw-r--r--Alc/backends/wave.c123
-rw-r--r--Alc/backends/winmm.c199
-rw-r--r--Alc/bformatdec.c492
-rw-r--r--Alc/bformatdec.h57
-rw-r--r--Alc/bs2b.c57
-rw-r--r--Alc/bsinc.c981
-rw-r--r--Alc/compat.h25
-rw-r--r--Alc/converter.c468
-rw-r--r--Alc/converter.h55
-rw-r--r--Alc/cpu_caps.h15
-rw-r--r--Alc/effects/autowah.c321
-rw-r--r--Alc/effects/chorus.c494
-rw-r--r--Alc/effects/compressor.c220
-rw-r--r--Alc/effects/dedicated.c140
-rw-r--r--Alc/effects/distortion.c241
-rw-r--r--Alc/effects/echo.c243
-rw-r--r--Alc/effects/equalizer.c228
-rw-r--r--Alc/effects/flanger.c398
-rw-r--r--Alc/effects/fshifter.c329
-rw-r--r--Alc/effects/modulator.c261
-rw-r--r--Alc/effects/null.c113
-rw-r--r--Alc/effects/pshifter.c441
-rw-r--r--Alc/effects/reverb.c2433
-rw-r--r--Alc/filters/defs.h112
-rw-r--r--Alc/filters/filter.c129
-rw-r--r--Alc/filters/nfc.c426
-rw-r--r--Alc/filters/nfc.h49
-rw-r--r--Alc/filters/splitter.c109
-rw-r--r--Alc/filters/splitter.h40
-rw-r--r--Alc/fpu_modes.h34
-rw-r--r--Alc/helpers.c1431
-rw-r--r--Alc/hrtf.c1551
-rw-r--r--Alc/hrtf.h84
-rw-r--r--Alc/inprogext.h87
-rw-r--r--Alc/logging.h69
-rw-r--r--Alc/mastering.c543
-rw-r--r--Alc/mastering.h49
-rw-r--r--Alc/mixer.c638
-rw-r--r--Alc/mixer/defs.h119
-rw-r--r--Alc/mixer/hrtf_inc.c128
-rw-r--r--Alc/mixer/mixer_c.c169
-rw-r--r--Alc/mixer/mixer_neon.c283
-rw-r--r--Alc/mixer/mixer_sse.c250
-rw-r--r--Alc/mixer/mixer_sse2.c84
-rw-r--r--Alc/mixer/mixer_sse3.c0
-rw-r--r--Alc/mixer/mixer_sse41.c (renamed from Alc/mixer_sse2.c)38
-rw-r--r--Alc/mixer_c.c181
-rw-r--r--Alc/mixer_defs.h80
-rw-r--r--Alc/mixer_inc.c79
-rw-r--r--Alc/mixer_neon.c139
-rw-r--r--Alc/mixer_sse.c279
-rw-r--r--Alc/mixer_sse3.c162
-rw-r--r--Alc/mixer_sse41.c224
-rw-r--r--Alc/mixvoice.c762
-rw-r--r--Alc/panning.c1392
-rw-r--r--Alc/polymorphism.h105
-rw-r--r--Alc/ringbuffer.c295
-rw-r--r--Alc/ringbuffer.h77
-rw-r--r--Alc/uhjfilter.c120
-rw-r--r--Alc/uhjfilter.h49
-rw-r--r--Alc/vector.h99
86 files changed, 19628 insertions, 11668 deletions
diff --git a/Alc/ALc.c b/Alc/ALc.c
index d6d23eba..8bf2e1da 100644
--- a/Alc/ALc.c
+++ b/Alc/ALc.c
@@ -20,6 +20,8 @@
#include "config.h"
+#include "version.h"
+
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
@@ -30,17 +32,24 @@
#include "alMain.h"
#include "alSource.h"
#include "alListener.h"
-#include "alThunk.h"
#include "alSource.h"
#include "alBuffer.h"
+#include "alFilter.h"
+#include "alEffect.h"
#include "alAuxEffectSlot.h"
#include "alError.h"
-#include "bs2b.h"
+#include "mastering.h"
+#include "bformatdec.h"
#include "alu.h"
+#include "alconfig.h"
+#include "ringbuffer.h"
+#include "fpu_modes.h"
+#include "cpu_caps.h"
#include "compat.h"
#include "threads.h"
#include "alstring.h"
+#include "almalloc.h"
#include "backends/base.h"
@@ -51,61 +60,58 @@
struct BackendInfo {
const char *name;
ALCbackendFactory* (*getFactory)(void);
- ALCboolean (*Init)(BackendFuncs*);
- void (*Deinit)(void);
- void (*Probe)(enum DevProbe);
- BackendFuncs Funcs;
};
-#define EmptyFuncs { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
static struct BackendInfo BackendList[] = {
#ifdef HAVE_JACK
- { "jack", ALCjackBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs },
+ { "jack", ALCjackBackendFactory_getFactory },
#endif
#ifdef HAVE_PULSEAUDIO
- { "pulse", ALCpulseBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs },
+ { "pulse", ALCpulseBackendFactory_getFactory },
#endif
#ifdef HAVE_ALSA
- { "alsa", ALCalsaBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs },
+ { "alsa", ALCalsaBackendFactory_getFactory },
#endif
#ifdef HAVE_COREAUDIO
- { "core", NULL, alc_ca_init, alc_ca_deinit, alc_ca_probe, EmptyFuncs },
-#endif
-#ifdef HAVE_OSS
- { "oss", ALCossBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs },
+ { "core", ALCcoreAudioBackendFactory_getFactory },
#endif
#ifdef HAVE_SOLARIS
- { "solaris", ALCsolarisBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs },
+ { "solaris", ALCsolarisBackendFactory_getFactory },
#endif
#ifdef HAVE_SNDIO
- { "sndio", NULL, alc_sndio_init, alc_sndio_deinit, alc_sndio_probe, EmptyFuncs },
+ { "sndio", SndioBackendFactory_getFactory },
+#endif
+#ifdef HAVE_OSS
+ { "oss", ALCossBackendFactory_getFactory },
#endif
#ifdef HAVE_QSA
- { "qsa", NULL, alc_qsa_init, alc_qsa_deinit, alc_qsa_probe, EmptyFuncs },
+ { "qsa", ALCqsaBackendFactory_getFactory },
#endif
-#ifdef HAVE_MMDEVAPI
- { "mmdevapi", ALCmmdevBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs },
+#ifdef HAVE_WASAPI
+ { "wasapi", ALCwasapiBackendFactory_getFactory },
#endif
#ifdef HAVE_DSOUND
- { "dsound", ALCdsoundBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs },
+ { "dsound", ALCdsoundBackendFactory_getFactory },
#endif
#ifdef HAVE_WINMM
- { "winmm", ALCwinmmBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs },
+ { "winmm", ALCwinmmBackendFactory_getFactory },
#endif
#ifdef HAVE_PORTAUDIO
- { "port", ALCportBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs },
+ { "port", ALCportBackendFactory_getFactory },
#endif
#ifdef HAVE_OPENSL
- { "opensl", NULL, alc_opensl_init, alc_opensl_deinit, alc_opensl_probe, EmptyFuncs },
+ { "opensl", ALCopenslBackendFactory_getFactory },
+#endif
+#ifdef HAVE_SDL2
+ { "sdl2", ALCsdl2BackendFactory_getFactory },
#endif
- { "null", ALCnullBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs },
+ { "null", ALCnullBackendFactory_getFactory },
#ifdef HAVE_WAVE
- { "wave", ALCwaveBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs },
+ { "wave", ALCwaveBackendFactory_getFactory },
#endif
-
- { NULL, NULL, NULL, NULL, NULL, EmptyFuncs }
};
+static ALsizei BackendListSize = COUNTOF(BackendList);
#undef EmptyFuncs
static struct BackendInfo PlaybackBackend;
@@ -115,18 +121,11 @@ static struct BackendInfo CaptureBackend;
/************************************************
* Functions, enums, and errors
************************************************/
-typedef struct ALCfunction {
+#define DECL(x) { #x, (ALCvoid*)(x) }
+static const struct {
const ALCchar *funcName;
ALCvoid *address;
-} ALCfunction;
-
-typedef struct ALCenums {
- const ALCchar *enumName;
- ALCenum value;
-} ALCenums;
-
-#define DECL(x) { #x, (ALCvoid*)(x) }
-static const ALCfunction alcFunctions[] = {
+} alcFunctions[] = {
DECL(alcCreateContext),
DECL(alcMakeContextCurrent),
DECL(alcProcessContext),
@@ -271,13 +270,6 @@ static const ALCfunction alcFunctions[] = {
DECL(alGetAuxiliaryEffectSlotf),
DECL(alGetAuxiliaryEffectSlotfv),
- DECL(alBufferSubDataSOFT),
-
- DECL(alBufferSamplesSOFT),
- DECL(alBufferSubSamplesSOFT),
- DECL(alGetBufferSamplesSOFT),
- DECL(alIsBufferFormatSupportedSOFT),
-
DECL(alDeferUpdatesSOFT),
DECL(alProcessUpdatesSOFT),
@@ -294,12 +286,25 @@ static const ALCfunction alcFunctions[] = {
DECL(alGetSource3i64SOFT),
DECL(alGetSourcei64vSOFT),
- { NULL, NULL }
+ DECL(alGetStringiSOFT),
+
+ DECL(alBufferStorageSOFT),
+ DECL(alMapBufferSOFT),
+ DECL(alUnmapBufferSOFT),
+ DECL(alFlushMappedBufferSOFT),
+
+ DECL(alEventControlSOFT),
+ DECL(alEventCallbackSOFT),
+ DECL(alGetPointerSOFT),
+ DECL(alGetPointervSOFT),
};
#undef DECL
#define DECL(x) { #x, (x) }
-static const ALCenums enumeration[] = {
+static const struct {
+ const ALCchar *enumName;
+ ALCenum value;
+} alcEnumerations[] = {
DECL(ALC_INVALID),
DECL(ALC_FALSE),
DECL(ALC_TRUE),
@@ -336,6 +341,7 @@ static const ALCenums enumeration[] = {
DECL(ALC_5POINT1_SOFT),
DECL(ALC_6POINT1_SOFT),
DECL(ALC_7POINT1_SOFT),
+ DECL(ALC_BFORMAT3D_SOFT),
DECL(ALC_BYTE_SOFT),
DECL(ALC_UNSIGNED_BYTE_SOFT),
@@ -358,6 +364,16 @@ static const ALCenums enumeration[] = {
DECL(ALC_HRTF_SPECIFIER_SOFT),
DECL(ALC_HRTF_ID_SOFT),
+ DECL(ALC_AMBISONIC_LAYOUT_SOFT),
+ DECL(ALC_AMBISONIC_SCALING_SOFT),
+ DECL(ALC_AMBISONIC_ORDER_SOFT),
+ DECL(ALC_ACN_SOFT),
+ DECL(ALC_FUMA_SOFT),
+ DECL(ALC_N3D_SOFT),
+ DECL(ALC_SN3D_SOFT),
+
+ DECL(ALC_OUTPUT_LIMITER_SOFT),
+
DECL(ALC_NO_ERROR),
DECL(ALC_INVALID_DEVICE),
DECL(ALC_INVALID_CONTEXT),
@@ -390,9 +406,7 @@ static const ALCenums enumeration[] = {
DECL(AL_MAX_DISTANCE),
DECL(AL_SEC_OFFSET),
DECL(AL_SAMPLE_OFFSET),
- DECL(AL_SAMPLE_RW_OFFSETS_SOFT),
DECL(AL_BYTE_OFFSET),
- DECL(AL_BYTE_RW_OFFSETS_SOFT),
DECL(AL_SOURCE_TYPE),
DECL(AL_STATIC),
DECL(AL_STREAMING),
@@ -460,27 +474,6 @@ static const ALCenums enumeration[] = {
DECL(AL_FORMAT_MONO_ALAW_EXT),
DECL(AL_FORMAT_STEREO_ALAW_EXT),
- DECL(AL_MONO8_SOFT),
- DECL(AL_MONO16_SOFT),
- DECL(AL_MONO32F_SOFT),
- DECL(AL_STEREO8_SOFT),
- DECL(AL_STEREO16_SOFT),
- DECL(AL_STEREO32F_SOFT),
- DECL(AL_QUAD8_SOFT),
- DECL(AL_QUAD16_SOFT),
- DECL(AL_QUAD32F_SOFT),
- DECL(AL_REAR8_SOFT),
- DECL(AL_REAR16_SOFT),
- DECL(AL_REAR32F_SOFT),
- DECL(AL_5POINT1_8_SOFT),
- DECL(AL_5POINT1_16_SOFT),
- DECL(AL_5POINT1_32F_SOFT),
- DECL(AL_6POINT1_8_SOFT),
- DECL(AL_6POINT1_16_SOFT),
- DECL(AL_6POINT1_32F_SOFT),
- DECL(AL_7POINT1_8_SOFT),
- DECL(AL_7POINT1_16_SOFT),
- DECL(AL_7POINT1_32F_SOFT),
DECL(AL_FORMAT_BFORMAT2D_8),
DECL(AL_FORMAT_BFORMAT2D_16),
DECL(AL_FORMAT_BFORMAT2D_FLOAT32),
@@ -490,36 +483,17 @@ static const ALCenums enumeration[] = {
DECL(AL_FORMAT_BFORMAT3D_FLOAT32),
DECL(AL_FORMAT_BFORMAT3D_MULAW),
- DECL(AL_MONO_SOFT),
- DECL(AL_STEREO_SOFT),
- DECL(AL_QUAD_SOFT),
- DECL(AL_REAR_SOFT),
- DECL(AL_5POINT1_SOFT),
- DECL(AL_6POINT1_SOFT),
- DECL(AL_7POINT1_SOFT),
-
- DECL(AL_BYTE_SOFT),
- DECL(AL_UNSIGNED_BYTE_SOFT),
- DECL(AL_SHORT_SOFT),
- DECL(AL_UNSIGNED_SHORT_SOFT),
- DECL(AL_INT_SOFT),
- DECL(AL_UNSIGNED_INT_SOFT),
- DECL(AL_FLOAT_SOFT),
- DECL(AL_DOUBLE_SOFT),
- DECL(AL_BYTE3_SOFT),
- DECL(AL_UNSIGNED_BYTE3_SOFT),
-
DECL(AL_FREQUENCY),
DECL(AL_BITS),
DECL(AL_CHANNELS),
DECL(AL_SIZE),
- DECL(AL_INTERNAL_FORMAT_SOFT),
- DECL(AL_BYTE_LENGTH_SOFT),
- DECL(AL_SAMPLE_LENGTH_SOFT),
- DECL(AL_SEC_LENGTH_SOFT),
DECL(AL_UNPACK_BLOCK_ALIGNMENT_SOFT),
DECL(AL_PACK_BLOCK_ALIGNMENT_SOFT),
+ DECL(AL_SOURCE_RADIUS),
+
+ DECL(AL_STEREO_ANGLES),
+
DECL(AL_UNUSED),
DECL(AL_PENDING),
DECL(AL_PROCESSED),
@@ -542,6 +516,7 @@ static const ALCenums enumeration[] = {
DECL(AL_SPEED_OF_SOUND),
DECL(AL_SOURCE_DISTANCE_MODEL),
DECL(AL_DEFERRED_UPDATES_SOFT),
+ DECL(AL_GAIN_LIMIT_SOFT),
DECL(AL_INVERSE_DISTANCE),
DECL(AL_INVERSE_DISTANCE_CLAMPED),
@@ -574,20 +549,23 @@ static const ALCenums enumeration[] = {
DECL(AL_EFFECT_DISTORTION),
DECL(AL_EFFECT_ECHO),
DECL(AL_EFFECT_FLANGER),
-#if 0
+ DECL(AL_EFFECT_PITCH_SHIFTER),
DECL(AL_EFFECT_FREQUENCY_SHIFTER),
+#if 0
DECL(AL_EFFECT_VOCAL_MORPHER),
- DECL(AL_EFFECT_PITCH_SHIFTER),
#endif
DECL(AL_EFFECT_RING_MODULATOR),
-#if 0
DECL(AL_EFFECT_AUTOWAH),
-#endif
DECL(AL_EFFECT_COMPRESSOR),
DECL(AL_EFFECT_EQUALIZER),
DECL(AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT),
DECL(AL_EFFECT_DEDICATED_DIALOGUE),
+ DECL(AL_EFFECTSLOT_EFFECT),
+ DECL(AL_EFFECTSLOT_GAIN),
+ DECL(AL_EFFECTSLOT_AUXILIARY_SEND_AUTO),
+ DECL(AL_EFFECTSLOT_NULL),
+
DECL(AL_EAXREVERB_DENSITY),
DECL(AL_EAXREVERB_DIFFUSION),
DECL(AL_EAXREVERB_GAIN),
@@ -652,16 +630,16 @@ static const ALCenums enumeration[] = {
DECL(AL_FLANGER_FEEDBACK),
DECL(AL_FLANGER_DELAY),
+ DECL(AL_FREQUENCY_SHIFTER_FREQUENCY),
+ DECL(AL_FREQUENCY_SHIFTER_LEFT_DIRECTION),
+ DECL(AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION),
+
DECL(AL_RING_MODULATOR_FREQUENCY),
DECL(AL_RING_MODULATOR_HIGHPASS_CUTOFF),
DECL(AL_RING_MODULATOR_WAVEFORM),
-#if 0
- DECL(AL_AUTOWAH_ATTACK_TIME),
- DECL(AL_AUTOWAH_PEAK_GAIN),
- DECL(AL_AUTOWAH_RELEASE_TIME),
- DECL(AL_AUTOWAH_RESONANCE),
-#endif
+ DECL(AL_PITCH_SHIFTER_COARSE_TUNE),
+ DECL(AL_PITCH_SHIFTER_FINE_TUNE),
DECL(AL_COMPRESSOR_ONOFF),
@@ -678,7 +656,31 @@ static const ALCenums enumeration[] = {
DECL(AL_DEDICATED_GAIN),
- { NULL, (ALCenum)0 }
+ DECL(AL_AUTOWAH_ATTACK_TIME),
+ DECL(AL_AUTOWAH_RELEASE_TIME),
+ DECL(AL_AUTOWAH_RESONANCE),
+ DECL(AL_AUTOWAH_PEAK_GAIN),
+
+ DECL(AL_NUM_RESAMPLERS_SOFT),
+ DECL(AL_DEFAULT_RESAMPLER_SOFT),
+ DECL(AL_SOURCE_RESAMPLER_SOFT),
+ DECL(AL_RESAMPLER_NAME_SOFT),
+
+ DECL(AL_SOURCE_SPATIALIZE_SOFT),
+ DECL(AL_AUTO_SOFT),
+
+ DECL(AL_MAP_READ_BIT_SOFT),
+ DECL(AL_MAP_WRITE_BIT_SOFT),
+ DECL(AL_MAP_PERSISTENT_BIT_SOFT),
+ DECL(AL_PRESERVE_DATA_BIT_SOFT),
+
+ DECL(AL_EVENT_CALLBACK_FUNCTION_SOFT),
+ DECL(AL_EVENT_CALLBACK_USER_PARAM_SOFT),
+ DECL(AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT),
+ DECL(AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT),
+ DECL(AL_EVENT_TYPE_ERROR_SOFT),
+ DECL(AL_EVENT_TYPE_PERFORMANCE_SOFT),
+ DECL(AL_EVENT_TYPE_DEPRECATED_SOFT),
};
#undef DECL
@@ -706,13 +708,35 @@ static ALCchar *alcCaptureDefaultDeviceSpecifier;
/* Default context extensions */
static const ALchar alExtList[] =
- "AL_EXT_ALAW AL_EXT_BFORMAT AL_EXT_DOUBLE AL_EXT_EXPONENT_DISTANCE "
- "AL_EXT_FLOAT32 AL_EXT_IMA4 AL_EXT_LINEAR_DISTANCE AL_EXT_MCFORMATS "
- "AL_EXT_MULAW AL_EXT_MULAW_BFORMAT AL_EXT_MULAW_MCFORMATS AL_EXT_OFFSET "
- "AL_EXT_source_distance_model AL_LOKI_quadriphonic AL_SOFT_block_alignment "
- "AL_SOFT_buffer_samples AL_SOFT_buffer_sub_data AL_SOFT_deferred_updates "
- "AL_SOFT_direct_channels AL_SOFT_loop_points AL_SOFT_MSADPCM "
- "AL_SOFT_source_latency AL_SOFT_source_length";
+ "AL_EXT_ALAW "
+ "AL_EXT_BFORMAT "
+ "AL_EXT_DOUBLE "
+ "AL_EXT_EXPONENT_DISTANCE "
+ "AL_EXT_FLOAT32 "
+ "AL_EXT_IMA4 "
+ "AL_EXT_LINEAR_DISTANCE "
+ "AL_EXT_MCFORMATS "
+ "AL_EXT_MULAW "
+ "AL_EXT_MULAW_BFORMAT "
+ "AL_EXT_MULAW_MCFORMATS "
+ "AL_EXT_OFFSET "
+ "AL_EXT_source_distance_model "
+ "AL_EXT_SOURCE_RADIUS "
+ "AL_EXT_STEREO_ANGLES "
+ "AL_LOKI_quadriphonic "
+ "AL_SOFT_block_alignment "
+ "AL_SOFT_deferred_updates "
+ "AL_SOFT_direct_channels "
+ "AL_SOFTX_events "
+ "AL_SOFTX_filter_gain_ex "
+ "AL_SOFT_gain_clamp_ex "
+ "AL_SOFT_loop_points "
+ "AL_SOFTX_map_buffer "
+ "AL_SOFT_MSADPCM "
+ "AL_SOFT_source_latency "
+ "AL_SOFT_source_length "
+ "AL_SOFT_source_resampler "
+ "AL_SOFT_source_spatialize";
static ATOMIC(ALCenum) LastNullDeviceError = ATOMIC_INIT_STATIC(ALC_NO_ERROR);
@@ -755,8 +779,8 @@ static const ALCchar alcNoDeviceExtList[] =
static const ALCchar alcExtensionList[] =
"ALC_ENUMERATE_ALL_EXT ALC_ENUMERATION_EXT ALC_EXT_CAPTURE "
"ALC_EXT_DEDICATED ALC_EXT_disconnect ALC_EXT_EFX "
- "ALC_EXT_thread_local_context ALC_SOFTX_device_clock ALC_SOFT_HRTF "
- "ALC_SOFT_loopback ALC_SOFT_pause_device";
+ "ALC_EXT_thread_local_context ALC_SOFT_device_clock ALC_SOFT_HRTF "
+ "ALC_SOFT_loopback ALC_SOFT_output_limiter ALC_SOFT_pause_device";
static const ALCint alcMajorVersion = 1;
static const ALCint alcMinorVersion = 1;
@@ -772,13 +796,13 @@ static ATOMIC(ALCdevice*) DeviceList = ATOMIC_INIT_STATIC(NULL);
static almtx_t ListLock;
static inline void LockLists(void)
{
- int lockret = almtx_lock(&ListLock);
- assert(lockret == althrd_success);
+ int ret = almtx_lock(&ListLock);
+ assert(ret == althrd_success);
}
static inline void UnlockLists(void)
{
- int unlockret = almtx_unlock(&ListLock);
- assert(unlockret == althrd_success);
+ int ret = almtx_unlock(&ListLock);
+ assert(ret == althrd_success);
}
/************************************************
@@ -802,6 +826,7 @@ BOOL APIENTRY DllMain(HINSTANCE hModule, DWORD reason, LPVOID lpReserved)
break;
case DLL_THREAD_DETACH:
+ althrd_thread_detach();
break;
case DLL_PROCESS_DETACH:
@@ -864,19 +889,21 @@ static void alc_init(void)
if(str && (strcasecmp(str, "true") == 0 || strtol(str, NULL, 0) == 1))
ZScale *= -1.0f;
+ str = getenv("__ALSOFT_REVERB_IGNORES_SOUND_SPEED");
+ if(str && (strcasecmp(str, "true") == 0 || strtol(str, NULL, 0) == 1))
+ OverrideReverbSpeedOfSound = AL_TRUE;
+
ret = altss_create(&LocalContext, ReleaseThreadCtx);
assert(ret == althrd_success);
ret = almtx_init(&ListLock, almtx_recursive);
assert(ret == althrd_success);
-
- ThunkInit();
}
static void alc_initconfig(void)
{
const char *devs, *str;
- ALuint capfilter;
+ int capfilter;
float valf;
int i, n;
@@ -896,10 +923,15 @@ static void alc_initconfig(void)
else ERR("Failed to open log file '%s'\n", str);
}
+ TRACE("Initializing library v%s-%s %s\n", ALSOFT_VERSION,
+ ALSOFT_GIT_COMMIT_HASH, ALSOFT_GIT_BRANCH);
{
char buf[1024] = "";
- int len = snprintf(buf, sizeof(buf), "%s", BackendList[0].name);
- for(i = 1;BackendList[i].name;i++)
+ int len = 0;
+
+ if(BackendListSize > 0)
+ len += snprintf(buf, sizeof(buf), "%s", BackendList[0].name);
+ for(i = 1;i < BackendListSize;i++)
len += snprintf(buf+len, sizeof(buf)-len, ", %s", BackendList[i].name);
TRACE("Supported backends: %s\n", buf);
}
@@ -975,6 +1007,7 @@ static void alc_initconfig(void)
#endif
ConfigValueInt(NULL, NULL, "rt-prio", &RTPrioLevel);
+ aluInit();
aluInitMixer();
str = getenv("ALSOFT_TRAP_ERROR");
@@ -999,8 +1032,6 @@ static void alc_initconfig(void)
if(ConfigValueFloat(NULL, "reverb", "boost", &valf))
ReverbBoost *= powf(10.0f, valf / 20.0f);
- EmulateEAXReverb = GetConfigValueBool(NULL, "reverb", "emulate-eax", AL_FALSE);
-
if(((devs=getenv("ALSOFT_DRIVERS")) && devs[0]) ||
ConfigValueStr(NULL, NULL, "drivers", &devs))
{
@@ -1029,26 +1060,32 @@ static void alc_initconfig(void)
len = (next ? ((size_t)(next-devs)) : strlen(devs));
while(len > 0 && isspace(devs[len-1]))
len--;
- for(n = i;BackendList[n].name;n++)
+#ifdef HAVE_WASAPI
+ /* HACK: For backwards compatibility, convert backend references of
+ * mmdevapi to wasapi. This should eventually be removed.
+ */
+ if(len == 8 && strncmp(devs, "mmdevapi", len) == 0)
+ {
+ devs = "wasapi";
+ len = 6;
+ }
+#endif
+ for(n = i;n < BackendListSize;n++)
{
if(len == strlen(BackendList[n].name) &&
strncmp(BackendList[n].name, devs, len) == 0)
{
if(delitem)
{
- do {
+ for(;n+1 < BackendListSize;n++)
BackendList[n] = BackendList[n+1];
- ++n;
- } while(BackendList[n].name);
+ BackendListSize--;
}
else
{
struct BackendInfo Bkp = BackendList[n];
- while(n > i)
- {
+ for(;n > i;n--)
BackendList[n] = BackendList[n-1];
- --n;
- }
BackendList[n] = Bkp;
i++;
@@ -1059,64 +1096,46 @@ static void alc_initconfig(void)
} while(next++);
if(endlist)
- {
- BackendList[i].name = NULL;
- BackendList[i].getFactory = NULL;
- BackendList[i].Init = NULL;
- BackendList[i].Deinit = NULL;
- BackendList[i].Probe = NULL;
- }
+ BackendListSize = i;
}
- for(i = 0;(BackendList[i].Init || BackendList[i].getFactory) && (!PlaybackBackend.name || !CaptureBackend.name);i++)
+ for(n = i = 0;i < BackendListSize && (!PlaybackBackend.name || !CaptureBackend.name);i++)
{
- if(BackendList[i].getFactory)
- {
- ALCbackendFactory *factory = BackendList[i].getFactory();
- if(!V0(factory,init)())
- {
- WARN("Failed to initialize backend \"%s\"\n", BackendList[i].name);
- continue;
- }
-
- TRACE("Initialized backend \"%s\"\n", BackendList[i].name);
- if(!PlaybackBackend.name && V(factory,querySupport)(ALCbackend_Playback))
- {
- PlaybackBackend = BackendList[i];
- TRACE("Added \"%s\" for playback\n", PlaybackBackend.name);
- }
- if(!CaptureBackend.name && V(factory,querySupport)(ALCbackend_Capture))
- {
- CaptureBackend = BackendList[i];
- TRACE("Added \"%s\" for capture\n", CaptureBackend.name);
- }
+ ALCbackendFactory *factory;
+ BackendList[n] = BackendList[i];
- continue;
- }
-
- if(!BackendList[i].Init(&BackendList[i].Funcs))
+ factory = BackendList[n].getFactory();
+ if(!V0(factory,init)())
{
- WARN("Failed to initialize backend \"%s\"\n", BackendList[i].name);
+ WARN("Failed to initialize backend \"%s\"\n", BackendList[n].name);
continue;
}
- TRACE("Initialized backend \"%s\"\n", BackendList[i].name);
- if(BackendList[i].Funcs.OpenPlayback && !PlaybackBackend.name)
+ TRACE("Initialized backend \"%s\"\n", BackendList[n].name);
+ if(!PlaybackBackend.name && V(factory,querySupport)(ALCbackend_Playback))
{
- PlaybackBackend = BackendList[i];
+ PlaybackBackend = BackendList[n];
TRACE("Added \"%s\" for playback\n", PlaybackBackend.name);
}
- if(BackendList[i].Funcs.OpenCapture && !CaptureBackend.name)
+ if(!CaptureBackend.name && V(factory,querySupport)(ALCbackend_Capture))
{
- CaptureBackend = BackendList[i];
+ CaptureBackend = BackendList[n];
TRACE("Added \"%s\" for capture\n", CaptureBackend.name);
}
+ n++;
}
+ BackendListSize = n;
+
{
ALCbackendFactory *factory = ALCloopbackFactory_getFactory();
V0(factory,init)();
}
+ if(!PlaybackBackend.name)
+ WARN("No playback backend available!\n");
+ if(!CaptureBackend.name)
+ WARN("No capture backend available!\n");
+
if(ConfigValueStr(NULL, NULL, "excludefx", &str))
{
size_t len;
@@ -1130,7 +1149,7 @@ static void alc_initconfig(void)
continue;
len = (next ? ((size_t)(next-str)) : strlen(str));
- for(n = 0;EffectList[n].name;n++)
+ for(n = 0;n < EFFECTLIST_SIZE;n++)
{
if(len == strlen(EffectList[n].name) &&
strncmp(EffectList[n].name, str, len) == 0)
@@ -1139,8 +1158,6 @@ static void alc_initconfig(void)
} while(next++);
}
- InitEffectFactoryMap();
-
InitEffect(&DefaultEffect);
str = getenv("ALSOFT_DEFAULT_REVERB");
if((str && str[0]) || ConfigValueStr(NULL, NULL, "default-reverb", &str))
@@ -1164,16 +1181,15 @@ static void alc_cleanup(void)
free(alcCaptureDefaultDeviceSpecifier);
alcCaptureDefaultDeviceSpecifier = NULL;
- if((dev=ATOMIC_EXCHANGE(ALCdevice*, &DeviceList, NULL)) != NULL)
+ if((dev=ATOMIC_EXCHANGE_PTR_SEQ(&DeviceList, NULL)) != NULL)
{
ALCuint num = 0;
do {
num++;
- } while((dev=dev->next) != NULL);
+ dev = ATOMIC_LOAD(&dev->next, almemory_order_relaxed);
+ } while(dev != NULL);
ERR("%u device%s not closed\n", num, (num>1)?"s":"");
}
-
- DeinitEffectFactoryMap();
}
static void alc_deinit_safe(void)
@@ -1183,13 +1199,14 @@ static void alc_deinit_safe(void)
FreeHrtfs();
FreeALConfig();
- ThunkExit();
almtx_destroy(&ListLock);
altss_delete(LocalContext);
if(LogFile != stderr)
fclose(LogFile);
LogFile = NULL;
+
+ althrd_deinit();
}
static void alc_deinit(void)
@@ -1201,15 +1218,10 @@ static void alc_deinit(void)
memset(&PlaybackBackend, 0, sizeof(PlaybackBackend));
memset(&CaptureBackend, 0, sizeof(CaptureBackend));
- for(i = 0;BackendList[i].Deinit || BackendList[i].getFactory;i++)
+ for(i = 0;i < BackendListSize;i++)
{
- if(!BackendList[i].getFactory)
- BackendList[i].Deinit();
- else
- {
- ALCbackendFactory *factory = BackendList[i].getFactory();
- V0(factory,deinit)();
- }
+ ALCbackendFactory *factory = BackendList[i].getFactory();
+ V0(factory,deinit)();
}
{
ALCbackendFactory *factory = ALCloopbackFactory_getFactory();
@@ -1228,14 +1240,12 @@ static void ProbeDevices(al_string *list, struct BackendInfo *backendinfo, enum
DO_INITCONFIG();
LockLists();
- al_string_clear(list);
+ alstr_clear(list);
- if(!backendinfo->getFactory)
- backendinfo->Probe(type);
- else
+ if(backendinfo->getFactory)
{
ALCbackendFactory *factory = backendinfo->getFactory();
- V(factory,probe)(type);
+ V(factory,probe)(type, list);
}
UnlockLists();
@@ -1245,17 +1255,6 @@ static void ProbeAllDevicesList(void)
static void ProbeCaptureDeviceList(void)
{ ProbeDevices(&alcCaptureDeviceList, &CaptureBackend, CAPTURE_DEVICE_PROBE); }
-static void AppendDevice(const ALCchar *name, al_string *devnames)
-{
- size_t len = strlen(name);
- if(len > 0)
- al_string_append_range(devnames, name, name+len+1);
-}
-void AppendAllDevicesList(const ALCchar *name)
-{ AppendDevice(name, &alcAllDevicesList); }
-void AppendCaptureDeviceList(const ALCchar *name)
-{ AppendDevice(name, &alcCaptureDeviceList); }
-
/************************************************
* Device format information
@@ -1285,13 +1284,13 @@ const ALCchar *DevFmtChannelsString(enum DevFmtChannels chans)
case DevFmtX51Rear: return "5.1 Surround (Rear)";
case DevFmtX61: return "6.1 Surround";
case DevFmtX71: return "7.1 Surround";
- case DevFmtBFormat3D: return "B-Format 3D";
+ case DevFmtAmbi3D: return "Ambisonic 3D";
}
return "(unknown channels)";
}
-extern inline ALuint FrameSizeFromDevFmt(enum DevFmtChannels chans, enum DevFmtType type);
-ALuint BytesFromDevFmt(enum DevFmtType type)
+extern inline ALsizei FrameSizeFromDevFmt(enum DevFmtChannels chans, enum DevFmtType type, ALsizei ambiorder);
+ALsizei BytesFromDevFmt(enum DevFmtType type)
{
switch(type)
{
@@ -1305,7 +1304,7 @@ ALuint BytesFromDevFmt(enum DevFmtType type)
}
return 0;
}
-ALuint ChannelsFromDevFmt(enum DevFmtChannels chans)
+ALsizei ChannelsFromDevFmt(enum DevFmtChannels chans, ALsizei ambiorder)
{
switch(chans)
{
@@ -1316,13 +1315,15 @@ ALuint ChannelsFromDevFmt(enum DevFmtChannels chans)
case DevFmtX51Rear: return 6;
case DevFmtX61: return 7;
case DevFmtX71: return 8;
- case DevFmtBFormat3D: return 4;
+ case DevFmtAmbi3D: return (ambiorder >= 3) ? 16 :
+ (ambiorder == 2) ? 9 :
+ (ambiorder == 1) ? 4 : 1;
}
return 0;
}
-DECL_CONST static ALboolean DecomposeDevFormat(ALenum format,
- enum DevFmtChannels *chans, enum DevFmtType *type)
+static ALboolean DecomposeDevFormat(ALenum format, enum DevFmtChannels *chans,
+ enum DevFmtType *type)
{
static const struct {
ALenum format;
@@ -1368,7 +1369,7 @@ DECL_CONST static ALboolean DecomposeDevFormat(ALenum format,
return AL_FALSE;
}
-DECL_CONST static ALCboolean IsValidALCType(ALCenum type)
+static ALCboolean IsValidALCType(ALCenum type)
{
switch(type)
{
@@ -1384,7 +1385,7 @@ DECL_CONST static ALCboolean IsValidALCType(ALCenum type)
return ALC_FALSE;
}
-DECL_CONST static ALCboolean IsValidALCChannels(ALCenum channels)
+static ALCboolean IsValidALCChannels(ALCenum channels)
{
switch(channels)
{
@@ -1394,34 +1395,38 @@ DECL_CONST static ALCboolean IsValidALCChannels(ALCenum channels)
case ALC_5POINT1_SOFT:
case ALC_6POINT1_SOFT:
case ALC_7POINT1_SOFT:
+ case ALC_BFORMAT3D_SOFT:
return ALC_TRUE;
}
return ALC_FALSE;
}
-
-/************************************************
- * Miscellaneous ALC helpers
- ************************************************/
-enum HrtfRequestMode {
- Hrtf_Default = 0,
- Hrtf_Enable = 1,
- Hrtf_Disable = 2,
-};
-
-extern inline void LockContext(ALCcontext *context);
-extern inline void UnlockContext(ALCcontext *context);
-
-void ALCdevice_Lock(ALCdevice *device)
+static ALCboolean IsValidAmbiLayout(ALCenum layout)
{
- V0(device->Backend,lock)();
+ switch(layout)
+ {
+ case ALC_ACN_SOFT:
+ case ALC_FUMA_SOFT:
+ return ALC_TRUE;
+ }
+ return ALC_FALSE;
}
-void ALCdevice_Unlock(ALCdevice *device)
+static ALCboolean IsValidAmbiScaling(ALCenum scaling)
{
- V0(device->Backend,unlock)();
+ switch(scaling)
+ {
+ case ALC_N3D_SOFT:
+ case ALC_SN3D_SOFT:
+ case ALC_FUMA_SOFT:
+ return ALC_TRUE;
+ }
+ return ALC_FALSE;
}
+/************************************************
+ * Miscellaneous ALC helpers
+ ************************************************/
/* SetDefaultWFXChannelOrder
*
@@ -1429,66 +1434,87 @@ void ALCdevice_Unlock(ALCdevice *device)
*/
void SetDefaultWFXChannelOrder(ALCdevice *device)
{
- ALuint i;
+ ALsizei i;
for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
- device->ChannelName[i] = InvalidChannel;
+ device->RealOut.ChannelName[i] = InvalidChannel;
switch(device->FmtChans)
{
case DevFmtMono:
- device->ChannelName[0] = FrontCenter;
+ device->RealOut.ChannelName[0] = FrontCenter;
break;
case DevFmtStereo:
- device->ChannelName[0] = FrontLeft;
- device->ChannelName[1] = FrontRight;
+ device->RealOut.ChannelName[0] = FrontLeft;
+ device->RealOut.ChannelName[1] = FrontRight;
break;
case DevFmtQuad:
- device->ChannelName[0] = FrontLeft;
- device->ChannelName[1] = FrontRight;
- device->ChannelName[2] = BackLeft;
- device->ChannelName[3] = BackRight;
+ device->RealOut.ChannelName[0] = FrontLeft;
+ device->RealOut.ChannelName[1] = FrontRight;
+ device->RealOut.ChannelName[2] = BackLeft;
+ device->RealOut.ChannelName[3] = BackRight;
break;
case DevFmtX51:
- device->ChannelName[0] = FrontLeft;
- device->ChannelName[1] = FrontRight;
- device->ChannelName[2] = FrontCenter;
- device->ChannelName[3] = LFE;
- device->ChannelName[4] = SideLeft;
- device->ChannelName[5] = SideRight;
+ device->RealOut.ChannelName[0] = FrontLeft;
+ device->RealOut.ChannelName[1] = FrontRight;
+ device->RealOut.ChannelName[2] = FrontCenter;
+ device->RealOut.ChannelName[3] = LFE;
+ device->RealOut.ChannelName[4] = SideLeft;
+ device->RealOut.ChannelName[5] = SideRight;
break;
case DevFmtX51Rear:
- device->ChannelName[0] = FrontLeft;
- device->ChannelName[1] = FrontRight;
- device->ChannelName[2] = FrontCenter;
- device->ChannelName[3] = LFE;
- device->ChannelName[4] = BackLeft;
- device->ChannelName[5] = BackRight;
+ device->RealOut.ChannelName[0] = FrontLeft;
+ device->RealOut.ChannelName[1] = FrontRight;
+ device->RealOut.ChannelName[2] = FrontCenter;
+ device->RealOut.ChannelName[3] = LFE;
+ device->RealOut.ChannelName[4] = BackLeft;
+ device->RealOut.ChannelName[5] = BackRight;
break;
case DevFmtX61:
- device->ChannelName[0] = FrontLeft;
- device->ChannelName[1] = FrontRight;
- device->ChannelName[2] = FrontCenter;
- device->ChannelName[3] = LFE;
- device->ChannelName[4] = BackCenter;
- device->ChannelName[5] = SideLeft;
- device->ChannelName[6] = SideRight;
+ device->RealOut.ChannelName[0] = FrontLeft;
+ device->RealOut.ChannelName[1] = FrontRight;
+ device->RealOut.ChannelName[2] = FrontCenter;
+ device->RealOut.ChannelName[3] = LFE;
+ device->RealOut.ChannelName[4] = BackCenter;
+ device->RealOut.ChannelName[5] = SideLeft;
+ device->RealOut.ChannelName[6] = SideRight;
break;
case DevFmtX71:
- device->ChannelName[0] = FrontLeft;
- device->ChannelName[1] = FrontRight;
- device->ChannelName[2] = FrontCenter;
- device->ChannelName[3] = LFE;
- device->ChannelName[4] = BackLeft;
- device->ChannelName[5] = BackRight;
- device->ChannelName[6] = SideLeft;
- device->ChannelName[7] = SideRight;
+ device->RealOut.ChannelName[0] = FrontLeft;
+ device->RealOut.ChannelName[1] = FrontRight;
+ device->RealOut.ChannelName[2] = FrontCenter;
+ device->RealOut.ChannelName[3] = LFE;
+ device->RealOut.ChannelName[4] = BackLeft;
+ device->RealOut.ChannelName[5] = BackRight;
+ device->RealOut.ChannelName[6] = SideLeft;
+ device->RealOut.ChannelName[7] = SideRight;
break;
- case DevFmtBFormat3D:
- device->ChannelName[0] = BFormatW;
- device->ChannelName[1] = BFormatX;
- device->ChannelName[2] = BFormatY;
- device->ChannelName[3] = BFormatZ;
+ case DevFmtAmbi3D:
+ device->RealOut.ChannelName[0] = Aux0;
+ if(device->AmbiOrder > 0)
+ {
+ device->RealOut.ChannelName[1] = Aux1;
+ device->RealOut.ChannelName[2] = Aux2;
+ device->RealOut.ChannelName[3] = Aux3;
+ }
+ if(device->AmbiOrder > 1)
+ {
+ device->RealOut.ChannelName[4] = Aux4;
+ device->RealOut.ChannelName[5] = Aux5;
+ device->RealOut.ChannelName[6] = Aux6;
+ device->RealOut.ChannelName[7] = Aux7;
+ device->RealOut.ChannelName[8] = Aux8;
+ }
+ if(device->AmbiOrder > 2)
+ {
+ device->RealOut.ChannelName[9] = Aux9;
+ device->RealOut.ChannelName[10] = Aux10;
+ device->RealOut.ChannelName[11] = Aux11;
+ device->RealOut.ChannelName[12] = Aux12;
+ device->RealOut.ChannelName[13] = Aux13;
+ device->RealOut.ChannelName[14] = Aux14;
+ device->RealOut.ChannelName[15] = Aux15;
+ }
break;
}
}
@@ -1499,30 +1525,30 @@ void SetDefaultWFXChannelOrder(ALCdevice *device)
*/
void SetDefaultChannelOrder(ALCdevice *device)
{
- ALuint i;
+ ALsizei i;
for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
- device->ChannelName[i] = InvalidChannel;
+ device->RealOut.ChannelName[i] = InvalidChannel;
switch(device->FmtChans)
{
case DevFmtX51Rear:
- device->ChannelName[0] = FrontLeft;
- device->ChannelName[1] = FrontRight;
- device->ChannelName[2] = BackLeft;
- device->ChannelName[3] = BackRight;
- device->ChannelName[4] = FrontCenter;
- device->ChannelName[5] = LFE;
+ device->RealOut.ChannelName[0] = FrontLeft;
+ device->RealOut.ChannelName[1] = FrontRight;
+ device->RealOut.ChannelName[2] = BackLeft;
+ device->RealOut.ChannelName[3] = BackRight;
+ device->RealOut.ChannelName[4] = FrontCenter;
+ device->RealOut.ChannelName[5] = LFE;
return;
case DevFmtX71:
- device->ChannelName[0] = FrontLeft;
- device->ChannelName[1] = FrontRight;
- device->ChannelName[2] = BackLeft;
- device->ChannelName[3] = BackRight;
- device->ChannelName[4] = FrontCenter;
- device->ChannelName[5] = LFE;
- device->ChannelName[6] = SideLeft;
- device->ChannelName[7] = SideRight;
+ device->RealOut.ChannelName[0] = FrontLeft;
+ device->RealOut.ChannelName[1] = FrontRight;
+ device->RealOut.ChannelName[2] = BackLeft;
+ device->RealOut.ChannelName[3] = BackRight;
+ device->RealOut.ChannelName[4] = FrontCenter;
+ device->RealOut.ChannelName[5] = LFE;
+ device->RealOut.ChannelName[6] = SideLeft;
+ device->RealOut.ChannelName[7] = SideRight;
return;
/* Same as WFX order */
@@ -1531,13 +1557,14 @@ void SetDefaultChannelOrder(ALCdevice *device)
case DevFmtQuad:
case DevFmtX51:
case DevFmtX61:
- case DevFmtBFormat3D:
+ case DevFmtAmbi3D:
SetDefaultWFXChannelOrder(device);
break;
}
}
-extern inline ALint GetChannelIdxByName(const ALCdevice *device, enum Channel chan);
+extern inline ALint GetChannelIndex(const enum Channel names[MAX_OUTPUT_CHANNELS], enum Channel chan);
+extern inline ALint GetChannelIdxByName(const RealMixParams *real, enum Channel chan);
/* ALCcontext_DeferUpdates
@@ -1548,28 +1575,7 @@ extern inline ALint GetChannelIdxByName(const ALCdevice *device, enum Channel ch
*/
void ALCcontext_DeferUpdates(ALCcontext *context)
{
- ALCdevice *device = context->Device;
- FPUCtl oldMode;
-
- SetMixerFPUMode(&oldMode);
-
- V0(device->Backend,lock)();
- if(!context->DeferUpdates)
- {
- context->DeferUpdates = AL_TRUE;
-
- /* Make sure all pending updates are performed */
- UpdateContextSources(context);
-#define UPDATE_SLOT(iter) do { \
- if(ATOMIC_EXCHANGE(ALenum, &(*iter)->NeedsUpdate, AL_FALSE)) \
- V((*iter)->EffectState,update)(device, *iter); \
-} while(0)
- VECTOR_FOR_EACH(ALeffectslot*, context->ActiveAuxSlots, UPDATE_SLOT);
-#undef UPDATE_SLOT
- }
- V0(device->Backend,unlock)();
-
- RestoreFPUMode(&oldMode);
+ ATOMIC_STORE_SEQ(&context->DeferUpdates, AL_TRUE);
}
/* ALCcontext_ProcessUpdates
@@ -1578,37 +1584,29 @@ void ALCcontext_DeferUpdates(ALCcontext *context)
*/
void ALCcontext_ProcessUpdates(ALCcontext *context)
{
- ALCdevice *device = context->Device;
-
- V0(device->Backend,lock)();
- if(context->DeferUpdates)
+ almtx_lock(&context->PropLock);
+ if(ATOMIC_EXCHANGE_SEQ(&context->DeferUpdates, AL_FALSE))
{
- ALsizei pos;
-
- context->DeferUpdates = AL_FALSE;
-
- LockUIntMapRead(&context->SourceMap);
- for(pos = 0;pos < context->SourceMap.size;pos++)
- {
- ALsource *Source = context->SourceMap.array[pos].value;
- ALenum new_state;
-
- if((Source->state == AL_PLAYING || Source->state == AL_PAUSED) &&
- Source->Offset >= 0.0)
- {
- WriteLock(&Source->queue_lock);
- ApplyOffset(Source);
- WriteUnlock(&Source->queue_lock);
- }
-
- new_state = Source->new_state;
- Source->new_state = AL_NONE;
- if(new_state)
- SetSourceState(Source, context, new_state);
- }
- UnlockUIntMapRead(&context->SourceMap);
+ /* Tell the mixer to stop applying updates, then wait for any active
+ * updating to finish, before providing updates.
+ */
+ ATOMIC_STORE_SEQ(&context->HoldUpdates, AL_TRUE);
+ while((ATOMIC_LOAD(&context->UpdateCount, almemory_order_acquire)&1) != 0)
+ althrd_yield();
+
+ if(!ATOMIC_FLAG_TEST_AND_SET(&context->PropsClean, almemory_order_acq_rel))
+ UpdateContextProps(context);
+ if(!ATOMIC_FLAG_TEST_AND_SET(&context->Listener->PropsClean, almemory_order_acq_rel))
+ UpdateListenerProps(context);
+ UpdateAllEffectSlotProps(context);
+ UpdateAllSourceProps(context);
+
+ /* Now with all updates declared, let the mixer continue applying them
+ * so they all happen at once.
+ */
+ ATOMIC_STORE_SEQ(&context->HoldUpdates, AL_FALSE);
}
- V0(device->Backend,unlock)();
+ almtx_unlock(&context->PropLock);
}
@@ -1618,6 +1616,7 @@ void ALCcontext_ProcessUpdates(ALCcontext *context)
*/
static void alcSetError(ALCdevice *device, ALCenum errorCode)
{
+ WARN("Error generated on device %p, code 0x%04x\n", device, errorCode);
if(TrapALCError)
{
#ifdef _WIN32
@@ -1630,22 +1629,32 @@ static void alcSetError(ALCdevice *device, ALCenum errorCode)
}
if(device)
- ATOMIC_STORE(&device->LastError, errorCode);
+ ATOMIC_STORE_SEQ(&device->LastError, errorCode);
else
- ATOMIC_STORE(&LastNullDeviceError, errorCode);
+ ATOMIC_STORE_SEQ(&LastNullDeviceError, errorCode);
}
+static struct Compressor *CreateDeviceLimiter(const ALCdevice *device, const ALfloat threshold)
+{
+ return CompressorInit(device->RealOut.NumChannels, device->Frequency,
+ AL_TRUE, AL_TRUE, AL_TRUE, AL_TRUE, AL_TRUE, 0.001f, 0.002f,
+ 0.0f, 0.0f, threshold, INFINITY, 0.0f, 0.020f, 0.200f);
+}
+
/* UpdateClockBase
*
* Updates the device's base clock time with however many samples have been
* done. This is used so frequency changes on the device don't cause the time
- * to jump forward or back.
+ * to jump forward or back. Must not be called while the device is running/
+ * mixing.
*/
static inline void UpdateClockBase(ALCdevice *device)
{
+ IncrementRef(&device->MixCount);
device->ClockBase += device->SamplesDone * DEVICE_CLOCK_RES / device->Frequency;
device->SamplesDone = 0;
+ IncrementRef(&device->MixCount);
}
/* UpdateDeviceParams
@@ -1655,30 +1664,32 @@ static inline void UpdateClockBase(ALCdevice *device)
*/
static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
{
- ALCcontext *context;
- enum HrtfRequestMode hrtf_appreq = Hrtf_Default;
enum HrtfRequestMode hrtf_userreq = Hrtf_Default;
+ enum HrtfRequestMode hrtf_appreq = Hrtf_Default;
+ ALCenum gainLimiter = device->LimiterState;
+ const ALsizei old_sends = device->NumAuxSends;
+ ALsizei new_sends = device->NumAuxSends;
enum DevFmtChannels oldChans;
enum DevFmtType oldType;
- ALCuint oldFreq;
- FPUCtl oldMode;
+ ALboolean update_failed;
ALCsizei hrtf_id = -1;
+ ALCcontext *context;
+ ALCuint oldFreq;
size_t size;
+ ALCsizei i;
+ int val;
// Check for attributes
if(device->Type == Loopback)
{
- enum {
- GotFreq = 1<<0,
- GotChans = 1<<1,
- GotType = 1<<2,
- GotAll = GotFreq|GotChans|GotType
- };
- ALCuint freq, numMono, numStereo, numSends;
- enum DevFmtChannels schans;
- enum DevFmtType stype;
- ALCuint attrIdx = 0;
- ALCint gotFmt = 0;
+ ALCsizei numMono, numStereo, numSends;
+ ALCenum alayout = AL_NONE;
+ ALCenum ascale = AL_NONE;
+ ALCenum schans = AL_NONE;
+ ALCenum stype = AL_NONE;
+ ALCsizei attrIdx = 0;
+ ALCsizei aorder = 0;
+ ALCuint freq = 0;
if(!attrList)
{
@@ -1688,75 +1699,113 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
numMono = device->NumMonoSources;
numStereo = device->NumStereoSources;
- numSends = device->NumAuxSends;
- schans = device->FmtChans;
- stype = device->FmtType;
- freq = device->Frequency;
+ numSends = old_sends;
+#define TRACE_ATTR(a, v) TRACE("Loopback %s = %d\n", #a, v)
while(attrList[attrIdx])
{
- if(attrList[attrIdx] == ALC_FORMAT_CHANNELS_SOFT)
+ switch(attrList[attrIdx])
{
- ALCint val = attrList[attrIdx + 1];
- if(!IsValidALCChannels(val) || !ChannelsFromDevFmt(val))
- return ALC_INVALID_VALUE;
- schans = val;
- gotFmt |= GotChans;
- }
+ case ALC_FORMAT_CHANNELS_SOFT:
+ schans = attrList[attrIdx + 1];
+ TRACE_ATTR(ALC_FORMAT_CHANNELS_SOFT, schans);
+ if(!IsValidALCChannels(schans))
+ return ALC_INVALID_VALUE;
+ break;
- if(attrList[attrIdx] == ALC_FORMAT_TYPE_SOFT)
- {
- ALCint val = attrList[attrIdx + 1];
- if(!IsValidALCType(val) || !BytesFromDevFmt(val))
- return ALC_INVALID_VALUE;
- stype = val;
- gotFmt |= GotType;
- }
+ case ALC_FORMAT_TYPE_SOFT:
+ stype = attrList[attrIdx + 1];
+ TRACE_ATTR(ALC_FORMAT_TYPE_SOFT, stype);
+ if(!IsValidALCType(stype))
+ return ALC_INVALID_VALUE;
+ break;
- if(attrList[attrIdx] == ALC_FREQUENCY)
- {
- freq = attrList[attrIdx + 1];
- if(freq < MIN_OUTPUT_RATE)
- return ALC_INVALID_VALUE;
- gotFmt |= GotFreq;
- }
+ case ALC_FREQUENCY:
+ freq = attrList[attrIdx + 1];
+ TRACE_ATTR(ALC_FREQUENCY, freq);
+ if(freq < MIN_OUTPUT_RATE)
+ return ALC_INVALID_VALUE;
+ break;
- if(attrList[attrIdx] == ALC_STEREO_SOURCES)
- {
- numStereo = attrList[attrIdx + 1];
- if(numStereo > device->MaxNoOfSources)
- numStereo = device->MaxNoOfSources;
+ case ALC_AMBISONIC_LAYOUT_SOFT:
+ alayout = attrList[attrIdx + 1];
+ TRACE_ATTR(ALC_AMBISONIC_LAYOUT_SOFT, alayout);
+ if(!IsValidAmbiLayout(alayout))
+ return ALC_INVALID_VALUE;
+ break;
- numMono = device->MaxNoOfSources - numStereo;
- }
+ case ALC_AMBISONIC_SCALING_SOFT:
+ ascale = attrList[attrIdx + 1];
+ TRACE_ATTR(ALC_AMBISONIC_SCALING_SOFT, ascale);
+ if(!IsValidAmbiScaling(ascale))
+ return ALC_INVALID_VALUE;
+ break;
- if(attrList[attrIdx] == ALC_MAX_AUXILIARY_SENDS)
- numSends = attrList[attrIdx + 1];
+ case ALC_AMBISONIC_ORDER_SOFT:
+ aorder = attrList[attrIdx + 1];
+ TRACE_ATTR(ALC_AMBISONIC_ORDER_SOFT, aorder);
+ if(aorder < 1 || aorder > MAX_AMBI_ORDER)
+ return ALC_INVALID_VALUE;
+ break;
- if(attrList[attrIdx] == ALC_HRTF_SOFT)
- {
- if(attrList[attrIdx + 1] == ALC_FALSE)
- hrtf_appreq = Hrtf_Disable;
- else if(attrList[attrIdx + 1] == ALC_TRUE)
- hrtf_appreq = Hrtf_Enable;
- else
- hrtf_appreq = Hrtf_Default;
- }
+ case ALC_MONO_SOURCES:
+ numMono = attrList[attrIdx + 1];
+ TRACE_ATTR(ALC_MONO_SOURCES, numMono);
+ numMono = maxi(numMono, 0);
+ break;
+
+ case ALC_STEREO_SOURCES:
+ numStereo = attrList[attrIdx + 1];
+ TRACE_ATTR(ALC_STEREO_SOURCES, numStereo);
+ numStereo = maxi(numStereo, 0);
+ break;
+
+ case ALC_MAX_AUXILIARY_SENDS:
+ numSends = attrList[attrIdx + 1];
+ TRACE_ATTR(ALC_MAX_AUXILIARY_SENDS, numSends);
+ numSends = clampi(numSends, 0, MAX_SENDS);
+ break;
+
+ case ALC_HRTF_SOFT:
+ TRACE_ATTR(ALC_HRTF_SOFT, attrList[attrIdx + 1]);
+ if(attrList[attrIdx + 1] == ALC_FALSE)
+ hrtf_appreq = Hrtf_Disable;
+ else if(attrList[attrIdx + 1] == ALC_TRUE)
+ hrtf_appreq = Hrtf_Enable;
+ else
+ hrtf_appreq = Hrtf_Default;
+ break;
- if(attrList[attrIdx] == ALC_HRTF_ID_SOFT)
- hrtf_id = attrList[attrIdx + 1];
+ case ALC_HRTF_ID_SOFT:
+ hrtf_id = attrList[attrIdx + 1];
+ TRACE_ATTR(ALC_HRTF_ID_SOFT, hrtf_id);
+ break;
+
+ case ALC_OUTPUT_LIMITER_SOFT:
+ gainLimiter = attrList[attrIdx + 1];
+ TRACE_ATTR(ALC_OUTPUT_LIMITER_SOFT, gainLimiter);
+ break;
+
+ default:
+ TRACE("Loopback 0x%04X = %d (0x%x)\n", attrList[attrIdx],
+ attrList[attrIdx + 1], attrList[attrIdx + 1]);
+ break;
+ }
attrIdx += 2;
}
+#undef TRACE_ATTR
- if(gotFmt != GotAll)
+ if(!schans || !stype || !freq)
{
WARN("Missing format for loopback device\n");
return ALC_INVALID_VALUE;
}
-
- ConfigValueUInt(NULL, NULL, "sends", &numSends);
- numSends = minu(MAX_SENDS, numSends);
+ if(schans == ALC_BFORMAT3D_SOFT && (!alayout || !ascale || !aorder))
+ {
+ WARN("Missing ambisonic info for loopback device\n");
+ return ALC_INVALID_VALUE;
+ }
if((device->Flags&DEVICE_RUNNING))
V0(device->Backend,stop)();
@@ -1767,14 +1816,40 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
device->Frequency = freq;
device->FmtChans = schans;
device->FmtType = stype;
+ if(schans == ALC_BFORMAT3D_SOFT)
+ {
+ device->AmbiOrder = aorder;
+ device->AmbiLayout = alayout;
+ device->AmbiScale = ascale;
+ }
+
+ if(numMono > INT_MAX-numStereo)
+ numMono = INT_MAX-numStereo;
+ numMono += numStereo;
+ if(ConfigValueInt(NULL, NULL, "sources", &numMono))
+ {
+ if(numMono <= 0)
+ numMono = 256;
+ }
+ else
+ numMono = maxi(numMono, 256);
+ numStereo = mini(numStereo, numMono);
+ numMono -= numStereo;
+ device->SourcesMax = numMono + numStereo;
+
device->NumMonoSources = numMono;
device->NumStereoSources = numStereo;
- device->NumAuxSends = numSends;
+
+ if(ConfigValueInt(NULL, NULL, "sends", &new_sends))
+ new_sends = mini(numSends, clampi(new_sends, 0, MAX_SENDS));
+ else
+ new_sends = numSends;
}
else if(attrList && attrList[0])
{
- ALCuint freq, numMono, numStereo, numSends;
- ALCuint attrIdx = 0;
+ ALCsizei numMono, numStereo, numSends;
+ ALCsizei attrIdx = 0;
+ ALCuint freq;
/* If a context is already running on the device, stop playback so the
* device attributes can be updated. */
@@ -1782,55 +1857,75 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
V0(device->Backend,stop)();
device->Flags &= ~DEVICE_RUNNING;
+ UpdateClockBase(device);
+
freq = device->Frequency;
numMono = device->NumMonoSources;
numStereo = device->NumStereoSources;
- numSends = device->NumAuxSends;
+ numSends = old_sends;
+#define TRACE_ATTR(a, v) TRACE("%s = %d\n", #a, v)
while(attrList[attrIdx])
{
- if(attrList[attrIdx] == ALC_FREQUENCY)
+ switch(attrList[attrIdx])
{
- freq = attrList[attrIdx + 1];
- device->Flags |= DEVICE_FREQUENCY_REQUEST;
- }
+ case ALC_FREQUENCY:
+ freq = attrList[attrIdx + 1];
+ TRACE_ATTR(ALC_FREQUENCY, freq);
+ device->Flags |= DEVICE_FREQUENCY_REQUEST;
+ break;
- if(attrList[attrIdx] == ALC_STEREO_SOURCES)
- {
- numStereo = attrList[attrIdx + 1];
- if(numStereo > device->MaxNoOfSources)
- numStereo = device->MaxNoOfSources;
+ case ALC_MONO_SOURCES:
+ numMono = attrList[attrIdx + 1];
+ TRACE_ATTR(ALC_MONO_SOURCES, numMono);
+ numMono = maxi(numMono, 0);
+ break;
- numMono = device->MaxNoOfSources - numStereo;
- }
+ case ALC_STEREO_SOURCES:
+ numStereo = attrList[attrIdx + 1];
+ TRACE_ATTR(ALC_STEREO_SOURCES, numStereo);
+ numStereo = maxi(numStereo, 0);
+ break;
- if(attrList[attrIdx] == ALC_MAX_AUXILIARY_SENDS)
- numSends = attrList[attrIdx + 1];
+ case ALC_MAX_AUXILIARY_SENDS:
+ numSends = attrList[attrIdx + 1];
+ TRACE_ATTR(ALC_MAX_AUXILIARY_SENDS, numSends);
+ numSends = clampi(numSends, 0, MAX_SENDS);
+ break;
- if(attrList[attrIdx] == ALC_HRTF_SOFT)
- {
- if(attrList[attrIdx + 1] == ALC_FALSE)
- hrtf_appreq = Hrtf_Disable;
- else if(attrList[attrIdx + 1] == ALC_TRUE)
- hrtf_appreq = Hrtf_Enable;
- else
- hrtf_appreq = Hrtf_Default;
- }
+ case ALC_HRTF_SOFT:
+ TRACE_ATTR(ALC_HRTF_SOFT, attrList[attrIdx + 1]);
+ if(attrList[attrIdx + 1] == ALC_FALSE)
+ hrtf_appreq = Hrtf_Disable;
+ else if(attrList[attrIdx + 1] == ALC_TRUE)
+ hrtf_appreq = Hrtf_Enable;
+ else
+ hrtf_appreq = Hrtf_Default;
+ break;
+
+ case ALC_HRTF_ID_SOFT:
+ hrtf_id = attrList[attrIdx + 1];
+ TRACE_ATTR(ALC_HRTF_ID_SOFT, hrtf_id);
+ break;
+
+ case ALC_OUTPUT_LIMITER_SOFT:
+ gainLimiter = attrList[attrIdx + 1];
+ TRACE_ATTR(ALC_OUTPUT_LIMITER_SOFT, gainLimiter);
+ break;
- if(attrList[attrIdx] == ALC_HRTF_ID_SOFT)
- hrtf_id = attrList[attrIdx + 1];
+ default:
+ TRACE("0x%04X = %d (0x%x)\n", attrList[attrIdx],
+ attrList[attrIdx + 1], attrList[attrIdx + 1]);
+ break;
+ }
attrIdx += 2;
}
+#undef TRACE_ATTR
- ConfigValueUInt(al_string_get_cstr(device->DeviceName), NULL, "frequency", &freq);
+ ConfigValueUInt(alstr_get_cstr(device->DeviceName), NULL, "frequency", &freq);
freq = maxu(freq, MIN_OUTPUT_RATE);
- ConfigValueUInt(al_string_get_cstr(device->DeviceName), NULL, "sends", &numSends);
- numSends = minu(MAX_SENDS, numSends);
-
- UpdateClockBase(device);
-
device->UpdateSize = (ALuint64)device->UpdateSize * freq /
device->Frequency;
/* SSE and Neon do best with the update size being a multiple of 4 */
@@ -1838,24 +1933,67 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
device->UpdateSize = (device->UpdateSize+3)&~3;
device->Frequency = freq;
+
+ if(numMono > INT_MAX-numStereo)
+ numMono = INT_MAX-numStereo;
+ numMono += numStereo;
+ if(ConfigValueInt(alstr_get_cstr(device->DeviceName), NULL, "sources", &numMono))
+ {
+ if(numMono <= 0)
+ numMono = 256;
+ }
+ else
+ numMono = maxi(numMono, 256);
+ numStereo = mini(numStereo, numMono);
+ numMono -= numStereo;
+ device->SourcesMax = numMono + numStereo;
+
device->NumMonoSources = numMono;
device->NumStereoSources = numStereo;
- device->NumAuxSends = numSends;
+
+ if(ConfigValueInt(alstr_get_cstr(device->DeviceName), NULL, "sends", &new_sends))
+ new_sends = mini(numSends, clampi(new_sends, 0, MAX_SENDS));
+ else
+ new_sends = numSends;
}
if((device->Flags&DEVICE_RUNNING))
return ALC_NO_ERROR;
- al_free(device->DryBuffer);
- device->DryBuffer = NULL;
+ al_free(device->Uhj_Encoder);
+ device->Uhj_Encoder = NULL;
+
+ al_free(device->Bs2b);
+ device->Bs2b = NULL;
+
+ al_free(device->ChannelDelay[0].Buffer);
+ for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
+ {
+ device->ChannelDelay[i].Length = 0;
+ device->ChannelDelay[i].Buffer = NULL;
+ }
+
+ al_free(device->Dry.Buffer);
+ device->Dry.Buffer = NULL;
+ device->Dry.NumChannels = 0;
+ device->FOAOut.Buffer = NULL;
+ device->FOAOut.NumChannels = 0;
+ device->RealOut.Buffer = NULL;
+ device->RealOut.NumChannels = 0;
UpdateClockBase(device);
+ device->FixedLatency = 0;
+
+ device->DitherSeed = DITHER_RNG_SEED;
- device->Hrtf_Status = ALC_HRTF_DISABLED_SOFT;
+ /*************************************************************************
+ * Update device format request if HRTF is requested
+ */
+ device->HrtfStatus = ALC_HRTF_DISABLED_SOFT;
if(device->Type != Loopback)
{
const char *hrtf;
- if(ConfigValueStr(al_string_get_cstr(device->DeviceName), NULL, "hrtf", &hrtf))
+ if(ConfigValueStr(alstr_get_cstr(device->DeviceName), NULL, "hrtf", &hrtf))
{
if(strcasecmp(hrtf, "true") == 0)
hrtf_userreq = Hrtf_Enable;
@@ -1867,56 +2005,36 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
if(hrtf_userreq == Hrtf_Enable || (hrtf_userreq != Hrtf_Disable && hrtf_appreq == Hrtf_Enable))
{
- if(VECTOR_SIZE(device->Hrtf_List) == 0)
+ struct Hrtf *hrtf = NULL;
+ if(VECTOR_SIZE(device->HrtfList) == 0)
{
- VECTOR_DEINIT(device->Hrtf_List);
- device->Hrtf_List = EnumerateHrtf(device->DeviceName);
+ VECTOR_DEINIT(device->HrtfList);
+ device->HrtfList = EnumerateHrtf(device->DeviceName);
}
- if(VECTOR_SIZE(device->Hrtf_List) > 0)
+ if(VECTOR_SIZE(device->HrtfList) > 0)
{
- device->FmtChans = DevFmtStereo;
- if(hrtf_id >= 0 && (size_t)hrtf_id < VECTOR_SIZE(device->Hrtf_List))
- device->Frequency = GetHrtfSampleRate(VECTOR_ELEM(device->Hrtf_List, hrtf_id).hrtf);
+ if(hrtf_id >= 0 && (size_t)hrtf_id < VECTOR_SIZE(device->HrtfList))
+ hrtf = GetLoadedHrtf(VECTOR_ELEM(device->HrtfList, hrtf_id).hrtf);
else
- device->Frequency = GetHrtfSampleRate(VECTOR_ELEM(device->Hrtf_List, 0).hrtf);
- device->Flags |= DEVICE_CHANNELS_REQUEST | DEVICE_FREQUENCY_REQUEST;
- }
- else
- {
- hrtf_userreq = hrtf_appreq = Hrtf_Default;
- device->Hrtf_Status = ALC_HRTF_UNSUPPORTED_FORMAT_SOFT;
+ hrtf = GetLoadedHrtf(VECTOR_ELEM(device->HrtfList, 0).hrtf);
}
- }
- }
- else if(hrtf_appreq == Hrtf_Enable)
- {
- size_t i;
- /* Loopback device. We don't need to match to a specific HRTF entry
- * here. If the requested ID matches, we'll pick that later, if not,
- * we'll try to auto-select one anyway. */
- if(device->FmtChans != DevFmtStereo)
- i = VECTOR_SIZE(device->Hrtf_List);
- else
- {
- if(VECTOR_SIZE(device->Hrtf_List) == 0)
+
+ if(hrtf)
{
- VECTOR_DEINIT(device->Hrtf_List);
- device->Hrtf_List = EnumerateHrtf(device->DeviceName);
+ device->FmtChans = DevFmtStereo;
+ device->Frequency = hrtf->sampleRate;
+ device->Flags |= DEVICE_CHANNELS_REQUEST | DEVICE_FREQUENCY_REQUEST;
+ if(device->HrtfHandle)
+ Hrtf_DecRef(device->HrtfHandle);
+ device->HrtfHandle = hrtf;
}
- for(i = 0;i < VECTOR_SIZE(device->Hrtf_List);i++)
+ else
{
- const struct Hrtf *hrtf = VECTOR_ELEM(device->Hrtf_List, i).hrtf;
- if(GetHrtfSampleRate(hrtf) == device->Frequency)
- break;
+ hrtf_userreq = Hrtf_Default;
+ hrtf_appreq = Hrtf_Disable;
+ device->HrtfStatus = ALC_HRTF_UNSUPPORTED_FORMAT_SOFT;
}
}
- if(i == VECTOR_SIZE(device->Hrtf_List))
- {
- ERR("Requested format not HRTF compatible: %s, %uhz\n",
- DevFmtChannelsString(device->FmtChans), device->Frequency);
- hrtf_appreq = Hrtf_Default;
- device->Hrtf_Status = ALC_HRTF_UNSUPPORTED_FORMAT_SOFT;
- }
}
oldFreq = device->Frequency;
@@ -1951,11 +2069,6 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
device->Flags &= ~DEVICE_FREQUENCY_REQUEST;
}
- TRACE("Post-reset: %s, %s, %uhz, %u update size x%d\n",
- DevFmtChannelsString(device->FmtChans), DevFmtTypeString(device->FmtType),
- device->Frequency, device->UpdateSize, device->NumUpdates
- );
-
if((device->UpdateSize&3) != 0)
{
if((CPUCapFlags&CPU_CAP_SSE))
@@ -1964,230 +2077,278 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
WARN("NEON performs best with multiple of 4 update sizes (%u)\n", device->UpdateSize);
}
- device->Hrtf = NULL;
- device->Hrtf_Mode = DisabledHrtf;
- al_string_clear(&device->Hrtf_Name);
- if(device->FmtChans != DevFmtStereo)
+ TRACE("Post-reset: %s, %s, %uhz, %u update size x%d\n",
+ DevFmtChannelsString(device->FmtChans), DevFmtTypeString(device->FmtType),
+ device->Frequency, device->UpdateSize, device->NumUpdates
+ );
+
+ aluInitRenderer(device, hrtf_id, hrtf_appreq, hrtf_userreq);
+ TRACE("Channel config, Dry: %d, FOA: %d, Real: %d\n", device->Dry.NumChannels,
+ device->FOAOut.NumChannels, device->RealOut.NumChannels);
+
+ /* Allocate extra channels for any post-filter output. */
+ size = (device->Dry.NumChannels + device->FOAOut.NumChannels +
+ device->RealOut.NumChannels)*sizeof(device->Dry.Buffer[0]);
+
+ TRACE("Allocating "SZFMT" channels, "SZFMT" bytes\n", size/sizeof(device->Dry.Buffer[0]), size);
+ device->Dry.Buffer = al_calloc(16, size);
+ if(!device->Dry.Buffer)
{
- if(hrtf_appreq == Hrtf_Enable)
- device->Hrtf_Status = ALC_HRTF_UNSUPPORTED_FORMAT_SOFT;
+ ERR("Failed to allocate "SZFMT" bytes for mix buffer\n", size);
+ return ALC_INVALID_DEVICE;
+ }
- free(device->Bs2b);
- device->Bs2b = NULL;
+ if(device->RealOut.NumChannels != 0)
+ device->RealOut.Buffer = device->Dry.Buffer + device->Dry.NumChannels +
+ device->FOAOut.NumChannels;
+ else
+ {
+ device->RealOut.Buffer = device->Dry.Buffer;
+ device->RealOut.NumChannels = device->Dry.NumChannels;
}
+
+ if(device->FOAOut.NumChannels != 0)
+ device->FOAOut.Buffer = device->Dry.Buffer + device->Dry.NumChannels;
else
{
- bool headphones = device->IsHeadphones;
- enum HrtfMode hrtf_mode = FullHrtf;
- ALCenum hrtf_status = device->Hrtf_Status;
- const char *mode;
- int bs2blevel;
- int usehrtf;
+ device->FOAOut.Buffer = device->Dry.Buffer;
+ device->FOAOut.NumChannels = device->Dry.NumChannels;
+ }
- if(device->Type != Loopback)
- {
- if(ConfigValueStr(al_string_get_cstr(device->DeviceName), NULL, "stereo-mode", &mode))
- {
- if(strcasecmp(mode, "headphones") == 0)
- headphones = true;
- else if(strcasecmp(mode, "speakers") == 0)
- headphones = false;
- else if(strcasecmp(mode, "auto") != 0)
- ERR("Unexpected stereo-mode: %s\n", mode);
- }
+ device->NumAuxSends = new_sends;
+ TRACE("Max sources: %d (%d + %d), effect slots: %d, sends: %d\n",
+ device->SourcesMax, device->NumMonoSources, device->NumStereoSources,
+ device->AuxiliaryEffectSlotMax, device->NumAuxSends);
- if(ConfigValueStr(al_string_get_cstr(device->DeviceName), NULL, "hrtf-mode", &mode))
+ device->DitherDepth = 0.0f;
+ if(GetConfigValueBool(alstr_get_cstr(device->DeviceName), NULL, "dither", 1))
+ {
+ ALint depth = 0;
+ ConfigValueInt(alstr_get_cstr(device->DeviceName), NULL, "dither-depth", &depth);
+ if(depth <= 0)
+ {
+ switch(device->FmtType)
{
- if(strcasecmp(mode, "full") == 0)
- hrtf_mode = FullHrtf;
- else if(strcasecmp(mode, "basic") == 0)
- hrtf_mode = BasicHrtf;
- else
- ERR("Unexpected hrtf-mode: %s\n", mode);
+ case DevFmtByte:
+ case DevFmtUByte:
+ depth = 8;
+ break;
+ case DevFmtShort:
+ case DevFmtUShort:
+ depth = 16;
+ break;
+ case DevFmtInt:
+ case DevFmtUInt:
+ case DevFmtFloat:
+ break;
}
}
-
- if(hrtf_userreq == Hrtf_Default)
- {
- usehrtf = (headphones && hrtf_appreq != Hrtf_Disable) ||
- (hrtf_appreq == Hrtf_Enable);
- if(headphones && hrtf_appreq != Hrtf_Disable)
- hrtf_status = ALC_HRTF_HEADPHONES_DETECTED_SOFT;
- else if(usehrtf)
- hrtf_status = ALC_HRTF_ENABLED_SOFT;
- }
- else
+ if(depth > 0)
{
- usehrtf = (hrtf_userreq == Hrtf_Enable);
- if(!usehrtf)
- hrtf_status = ALC_HRTF_DENIED_SOFT;
- else
- hrtf_status = ALC_HRTF_REQUIRED_SOFT;
+ depth = clampi(depth, 2, 24);
+ device->DitherDepth = powf(2.0f, (ALfloat)(depth-1));
}
-
- if(!usehrtf)
- device->Hrtf_Status = hrtf_status;
- else
+ }
+ if(!(device->DitherDepth > 0.0f))
+ TRACE("Dithering disabled\n");
+ else
+ TRACE("Dithering enabled (%g-bit, %g)\n", log2f(device->DitherDepth)+1.0f,
+ device->DitherDepth);
+
+ device->LimiterState = gainLimiter;
+ if(ConfigValueBool(alstr_get_cstr(device->DeviceName), NULL, "output-limiter", &val))
+ gainLimiter = val ? ALC_TRUE : ALC_FALSE;
+
+ /* Valid values for gainLimiter are ALC_DONT_CARE_SOFT, ALC_TRUE, and
+ * ALC_FALSE. For ALC_DONT_CARE_SOFT, use the limiter for integer-based
+ * output (where samples must be clamped), and don't for floating-point
+ * (which can take unclamped samples).
+ */
+ if(gainLimiter == ALC_DONT_CARE_SOFT)
+ {
+ switch(device->FmtType)
{
- size_t i;
-
- device->Hrtf_Status = ALC_HRTF_UNSUPPORTED_FORMAT_SOFT;
- if(VECTOR_SIZE(device->Hrtf_List) == 0)
- {
- VECTOR_DEINIT(device->Hrtf_List);
- device->Hrtf_List = EnumerateHrtf(device->DeviceName);
- }
-
- if(hrtf_id >= 0 && (size_t)hrtf_id < VECTOR_SIZE(device->Hrtf_List))
- {
- const HrtfEntry *entry = &VECTOR_ELEM(device->Hrtf_List, hrtf_id);
- if(GetHrtfSampleRate(entry->hrtf) == device->Frequency)
- {
- device->Hrtf = entry->hrtf;
- al_string_copy(&device->Hrtf_Name, entry->name);
- }
- }
- if(!device->Hrtf)
- {
- for(i = 0;i < VECTOR_SIZE(device->Hrtf_List);i++)
- {
- const HrtfEntry *entry = &VECTOR_ELEM(device->Hrtf_List, i);
- if(GetHrtfSampleRate(entry->hrtf) == device->Frequency)
- {
- device->Hrtf = entry->hrtf;
- al_string_copy(&device->Hrtf_Name, entry->name);
- break;
- }
- }
- }
+ case DevFmtByte:
+ case DevFmtUByte:
+ case DevFmtShort:
+ case DevFmtUShort:
+ case DevFmtInt:
+ case DevFmtUInt:
+ gainLimiter = ALC_TRUE;
+ break;
+ case DevFmtFloat:
+ gainLimiter = ALC_FALSE;
+ break;
}
- if(device->Hrtf)
+ }
+ if(gainLimiter != ALC_FALSE)
+ {
+ ALfloat thrshld = 1.0f;
+ switch(device->FmtType)
{
- device->Hrtf_Mode = hrtf_mode;
- device->Hrtf_Status = hrtf_status;
- TRACE("HRTF enabled, \"%s\"\n", al_string_get_cstr(device->Hrtf_Name));
- free(device->Bs2b);
- device->Bs2b = NULL;
+ case DevFmtByte:
+ case DevFmtUByte:
+ thrshld = 127.0f / 128.0f;
+ break;
+ case DevFmtShort:
+ case DevFmtUShort:
+ thrshld = 32767.0f / 32768.0f;
+ break;
+ case DevFmtInt:
+ case DevFmtUInt:
+ case DevFmtFloat:
+ break;
}
- else
- {
- TRACE("HRTF disabled\n");
+ if(device->DitherDepth > 0.0f)
+ thrshld -= 1.0f / device->DitherDepth;
- bs2blevel = ((headphones && hrtf_appreq != Hrtf_Disable) ||
- (hrtf_appreq == Hrtf_Enable)) ? 5 : 0;
- if(device->Type != Loopback)
- ConfigValueInt(al_string_get_cstr(device->DeviceName), NULL, "cf_level", &bs2blevel);
- if(bs2blevel > 0 && bs2blevel <= 6)
- {
- if(!device->Bs2b)
- {
- device->Bs2b = calloc(1, sizeof(*device->Bs2b));
- bs2b_clear(device->Bs2b);
- }
- bs2b_set_params(device->Bs2b, bs2blevel, device->Frequency);
- TRACE("BS2B enabled\n");
- }
- else
- {
- free(device->Bs2b);
- device->Bs2b = NULL;
- TRACE("BS2B disabled\n");
- }
- }
+ al_free(device->Limiter);
+ device->Limiter = CreateDeviceLimiter(device, log10f(thrshld) * 20.0f);
+ device->FixedLatency += (ALuint)(GetCompressorLookAhead(device->Limiter) *
+ DEVICE_CLOCK_RES / device->Frequency);
}
-
- aluInitPanning(device);
-
- /* With HRTF, allocate two extra channels for the post-filter output. */
- size = sizeof(device->DryBuffer[0]) * (device->NumChannels + (device->Hrtf ? 2 : 0));
- device->DryBuffer = al_calloc(16, size);
- if(!device->DryBuffer)
+ else
{
- ERR("Failed to allocate "SZFMT" bytes for mix buffer\n", size);
- return ALC_INVALID_DEVICE;
+ al_free(device->Limiter);
+ device->Limiter = NULL;
}
+ TRACE("Output limiter %s\n", device->Limiter ? "enabled" : "disabled");
- SetMixerFPUMode(&oldMode);
- V0(device->Backend,lock)();
- context = ATOMIC_LOAD(&device->ContextList);
+ aluSelectPostProcess(device);
+
+ TRACE("Fixed device latency: %uns\n", device->FixedLatency);
+
+ /* Need to delay returning failure until replacement Send arrays have been
+ * allocated with the appropriate size.
+ */
+ update_failed = AL_FALSE;
+ START_MIXER_MODE();
+ context = ATOMIC_LOAD_SEQ(&device->ContextList);
while(context)
{
+ SourceSubList *sublist, *subend;
+ struct ALvoiceProps *vprops;
ALsizei pos;
- ATOMIC_STORE(&context->UpdateSources, AL_FALSE);
- LockUIntMapRead(&context->EffectSlotMap);
- for(pos = 0;pos < context->EffectSlotMap.size;pos++)
+ if(context->DefaultSlot)
{
- ALeffectslot *slot = context->EffectSlotMap.array[pos].value;
+ ALeffectslot *slot = context->DefaultSlot;
+ ALeffectState *state = slot->Effect.State;
- if(V(slot->EffectState,deviceUpdate)(device) == AL_FALSE)
- {
- UnlockUIntMapRead(&context->EffectSlotMap);
- V0(device->Backend,unlock)();
- RestoreFPUMode(&oldMode);
- return ALC_INVALID_DEVICE;
- }
- ATOMIC_STORE(&slot->NeedsUpdate, AL_FALSE);
- V(slot->EffectState,update)(device, slot);
+ state->OutBuffer = device->Dry.Buffer;
+ state->OutChannels = device->Dry.NumChannels;
+ if(V(state,deviceUpdate)(device) == AL_FALSE)
+ update_failed = AL_TRUE;
+ else
+ UpdateEffectSlotProps(slot, context);
+ }
+
+ almtx_lock(&context->PropLock);
+ almtx_lock(&context->EffectSlotLock);
+ for(pos = 0;pos < (ALsizei)VECTOR_SIZE(context->EffectSlotList);pos++)
+ {
+ ALeffectslot *slot = VECTOR_ELEM(context->EffectSlotList, pos);
+ ALeffectState *state = slot->Effect.State;
+
+ state->OutBuffer = device->Dry.Buffer;
+ state->OutChannels = device->Dry.NumChannels;
+ if(V(state,deviceUpdate)(device) == AL_FALSE)
+ update_failed = AL_TRUE;
+ else
+ UpdateEffectSlotProps(slot, context);
}
- UnlockUIntMapRead(&context->EffectSlotMap);
+ almtx_unlock(&context->EffectSlotLock);
- LockUIntMapRead(&context->SourceMap);
- for(pos = 0;pos < context->SourceMap.size;pos++)
+ almtx_lock(&context->SourceLock);
+ sublist = VECTOR_BEGIN(context->SourceList);
+ subend = VECTOR_END(context->SourceList);
+ for(;sublist != subend;++sublist)
{
- ALsource *source = context->SourceMap.array[pos].value;
- ALuint s = device->NumAuxSends;
- while(s < MAX_SENDS)
+ ALuint64 usemask = ~sublist->FreeMask;
+ while(usemask)
{
- if(source->Send[s].Slot)
- DecrementRef(&source->Send[s].Slot->ref);
- source->Send[s].Slot = NULL;
- source->Send[s].Gain = 1.0f;
- source->Send[s].GainHF = 1.0f;
- s++;
+ ALsizei idx = CTZ64(usemask);
+ ALsource *source = sublist->Sources + idx;
+
+ usemask &= ~(U64(1) << idx);
+
+ if(old_sends != device->NumAuxSends)
+ {
+ ALvoid *sends = al_calloc(16, device->NumAuxSends*sizeof(source->Send[0]));
+ ALsizei s;
+
+ memcpy(sends, source->Send,
+ mini(device->NumAuxSends, old_sends)*sizeof(source->Send[0])
+ );
+ for(s = device->NumAuxSends;s < old_sends;s++)
+ {
+ if(source->Send[s].Slot)
+ DecrementRef(&source->Send[s].Slot->ref);
+ source->Send[s].Slot = NULL;
+ }
+ al_free(source->Send);
+ source->Send = sends;
+ for(s = old_sends;s < device->NumAuxSends;s++)
+ {
+ source->Send[s].Slot = NULL;
+ source->Send[s].Gain = 1.0f;
+ source->Send[s].GainHF = 1.0f;
+ source->Send[s].HFReference = LOWPASSFREQREF;
+ source->Send[s].GainLF = 1.0f;
+ source->Send[s].LFReference = HIGHPASSFREQREF;
+ }
+ }
+
+ ATOMIC_FLAG_CLEAR(&source->PropsClean, almemory_order_release);
}
- ATOMIC_STORE(&source->NeedsUpdate, AL_TRUE);
}
- UnlockUIntMapRead(&context->SourceMap);
+ /* Clear any pre-existing voice property structs, in case the number of
+ * auxiliary sends is changing. Active sources will have updates
+ * respecified in UpdateAllSourceProps.
+ */
+ vprops = ATOMIC_EXCHANGE_PTR(&context->FreeVoiceProps, NULL, almemory_order_acq_rel);
+ while(vprops)
+ {
+ struct ALvoiceProps *next = ATOMIC_LOAD(&vprops->next, almemory_order_relaxed);
+ al_free(vprops);
+ vprops = next;
+ }
+
+ AllocateVoices(context, context->MaxVoices, old_sends);
for(pos = 0;pos < context->VoiceCount;pos++)
{
- ALvoice *voice = &context->Voices[pos];
- ALsource *source = voice->Source;
- ALuint s = device->NumAuxSends;
+ ALvoice *voice = context->Voices[pos];
- while(s < MAX_SENDS)
- {
- voice->Send[s].Moving = AL_FALSE;
- voice->Send[s].Counter = 0;
- s++;
- }
+ al_free(ATOMIC_EXCHANGE_PTR(&voice->Update, NULL, almemory_order_acq_rel));
+
+ if(ATOMIC_LOAD(&voice->Source, almemory_order_acquire) == NULL)
+ continue;
- if(source)
+ if(device->AvgSpeakerDist > 0.0f)
{
- ATOMIC_STORE(&source->NeedsUpdate, AL_FALSE);
- voice->Update(voice, source, context);
+ /* Reinitialize the NFC filters for new parameters. */
+ ALfloat w1 = SPEEDOFSOUNDMETRESPERSEC /
+ (device->AvgSpeakerDist * device->Frequency);
+ for(i = 0;i < voice->NumChannels;i++)
+ NfcFilterCreate(&voice->Direct.Params[i].NFCtrlFilter, 0.0f, w1);
}
}
+ almtx_unlock(&context->SourceLock);
- context = context->next;
- }
- if(device->DefaultSlot)
- {
- ALeffectslot *slot = device->DefaultSlot;
+ ATOMIC_FLAG_TEST_AND_SET(&context->PropsClean, almemory_order_release);
+ UpdateContextProps(context);
+ ATOMIC_FLAG_TEST_AND_SET(&context->Listener->PropsClean, almemory_order_release);
+ UpdateListenerProps(context);
+ UpdateAllSourceProps(context);
+ almtx_unlock(&context->PropLock);
- if(V(slot->EffectState,deviceUpdate)(device) == AL_FALSE)
- {
- V0(device->Backend,unlock)();
- RestoreFPUMode(&oldMode);
- return ALC_INVALID_DEVICE;
- }
- ATOMIC_STORE(&slot->NeedsUpdate, AL_FALSE);
- V(slot->EffectState,update)(device, slot);
+ context = ATOMIC_LOAD(&context->next, almemory_order_relaxed);
}
- V0(device->Backend,unlock)();
- RestoreFPUMode(&oldMode);
+ END_MIXER_MODE();
+ if(update_failed)
+ return ALC_INVALID_DEVICE;
if(!(device->Flags&DEVICE_PAUSED))
{
@@ -2199,6 +2360,73 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
return ALC_NO_ERROR;
}
+
+static void InitDevice(ALCdevice *device, enum DeviceType type)
+{
+ ALsizei i;
+
+ InitRef(&device->ref, 1);
+ ATOMIC_INIT(&device->Connected, ALC_TRUE);
+ device->Type = type;
+ ATOMIC_INIT(&device->LastError, ALC_NO_ERROR);
+
+ device->Flags = 0;
+ device->Render_Mode = NormalRender;
+ device->AvgSpeakerDist = 0.0f;
+ device->LimiterState = ALC_DONT_CARE_SOFT;
+
+ ATOMIC_INIT(&device->ContextList, NULL);
+
+ device->ClockBase = 0;
+ device->SamplesDone = 0;
+ device->FixedLatency = 0;
+
+ device->SourcesMax = 0;
+ device->AuxiliaryEffectSlotMax = 0;
+ device->NumAuxSends = 0;
+
+ device->Dry.Buffer = NULL;
+ device->Dry.NumChannels = 0;
+ device->FOAOut.Buffer = NULL;
+ device->FOAOut.NumChannels = 0;
+ device->RealOut.Buffer = NULL;
+ device->RealOut.NumChannels = 0;
+
+ AL_STRING_INIT(device->DeviceName);
+
+ for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
+ {
+ device->ChannelDelay[i].Gain = 1.0f;
+ device->ChannelDelay[i].Length = 0;
+ device->ChannelDelay[i].Buffer = NULL;
+ }
+
+ AL_STRING_INIT(device->HrtfName);
+ VECTOR_INIT(device->HrtfList);
+ device->HrtfHandle = NULL;
+ device->Hrtf = NULL;
+ device->Bs2b = NULL;
+ device->Uhj_Encoder = NULL;
+ device->AmbiDecoder = NULL;
+ device->AmbiUp = NULL;
+ device->Stablizer = NULL;
+ device->Limiter = NULL;
+
+ VECTOR_INIT(device->BufferList);
+ almtx_init(&device->BufferLock, almtx_plain);
+
+ VECTOR_INIT(device->EffectList);
+ almtx_init(&device->EffectLock, almtx_plain);
+
+ VECTOR_INIT(device->FilterList);
+ almtx_init(&device->FilterLock, almtx_plain);
+
+ almtx_init(&device->BackendLock, almtx_plain);
+ device->Backend = NULL;
+
+ ATOMIC_INIT(&device->next, NULL);
+}
+
/* FreeDevice
*
* Frees the device structure, and destroys any objects the app failed to
@@ -2206,50 +2434,77 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
*/
static ALCvoid FreeDevice(ALCdevice *device)
{
+ ALsizei i;
+
TRACE("%p\n", device);
- V0(device->Backend,close)();
- DELETE_OBJ(device->Backend);
+ if(device->Backend)
+ DELETE_OBJ(device->Backend);
device->Backend = NULL;
- if(device->DefaultSlot)
- {
- ALeffectState *state = device->DefaultSlot->EffectState;
- device->DefaultSlot = NULL;
- DELETE_OBJ(state);
- }
+ almtx_destroy(&device->BackendLock);
+
+ ReleaseALBuffers(device);
+#define FREE_BUFFERSUBLIST(x) al_free((x)->Buffers)
+ VECTOR_FOR_EACH(BufferSubList, device->BufferList, FREE_BUFFERSUBLIST);
+#undef FREE_BUFFERSUBLIST
+ VECTOR_DEINIT(device->BufferList);
+ almtx_destroy(&device->BufferLock);
+
+ ReleaseALEffects(device);
+#define FREE_EFFECTSUBLIST(x) al_free((x)->Effects)
+ VECTOR_FOR_EACH(EffectSubList, device->EffectList, FREE_EFFECTSUBLIST);
+#undef FREE_EFFECTSUBLIST
+ VECTOR_DEINIT(device->EffectList);
+ almtx_destroy(&device->EffectLock);
+
+ ReleaseALFilters(device);
+#define FREE_FILTERSUBLIST(x) al_free((x)->Filters)
+ VECTOR_FOR_EACH(FilterSubList, device->FilterList, FREE_FILTERSUBLIST);
+#undef FREE_FILTERSUBLIST
+ VECTOR_DEINIT(device->FilterList);
+ almtx_destroy(&device->FilterLock);
+
+ AL_STRING_DEINIT(device->HrtfName);
+ FreeHrtfList(&device->HrtfList);
+ if(device->HrtfHandle)
+ Hrtf_DecRef(device->HrtfHandle);
+ device->HrtfHandle = NULL;
+ al_free(device->Hrtf);
+ device->Hrtf = NULL;
- if(device->BufferMap.size > 0)
- {
- WARN("(%p) Deleting %d Buffer(s)\n", device, device->BufferMap.size);
- ReleaseALBuffers(device);
- }
- ResetUIntMap(&device->BufferMap);
+ al_free(device->Bs2b);
+ device->Bs2b = NULL;
- if(device->EffectMap.size > 0)
- {
- WARN("(%p) Deleting %d Effect(s)\n", device, device->EffectMap.size);
- ReleaseALEffects(device);
- }
- ResetUIntMap(&device->EffectMap);
+ al_free(device->Uhj_Encoder);
+ device->Uhj_Encoder = NULL;
- if(device->FilterMap.size > 0)
- {
- WARN("(%p) Deleting %d Filter(s)\n", device, device->FilterMap.size);
- ReleaseALFilters(device);
- }
- ResetUIntMap(&device->FilterMap);
+ bformatdec_free(&device->AmbiDecoder);
+ ambiup_free(&device->AmbiUp);
- AL_STRING_DEINIT(device->Hrtf_Name);
- FreeHrtfList(&device->Hrtf_List);
+ al_free(device->Stablizer);
+ device->Stablizer = NULL;
- free(device->Bs2b);
- device->Bs2b = NULL;
+ al_free(device->Limiter);
+ device->Limiter = NULL;
+
+ al_free(device->ChannelDelay[0].Buffer);
+ for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
+ {
+ device->ChannelDelay[i].Gain = 1.0f;
+ device->ChannelDelay[i].Length = 0;
+ device->ChannelDelay[i].Buffer = NULL;
+ }
AL_STRING_DEINIT(device->DeviceName);
- al_free(device->DryBuffer);
- device->DryBuffer = NULL;
+ al_free(device->Dry.Buffer);
+ device->Dry.Buffer = NULL;
+ device->Dry.NumChannels = 0;
+ device->FOAOut.Buffer = NULL;
+ device->FOAOut.NumChannels = 0;
+ device->RealOut.Buffer = NULL;
+ device->RealOut.NumChannels = 0;
al_free(device);
}
@@ -2279,7 +2534,7 @@ static ALCboolean VerifyDevice(ALCdevice **device)
ALCdevice *tmpDevice;
LockLists();
- tmpDevice = ATOMIC_LOAD(&DeviceList);
+ tmpDevice = ATOMIC_LOAD_SEQ(&DeviceList);
while(tmpDevice)
{
if(tmpDevice == *device)
@@ -2288,7 +2543,7 @@ static ALCboolean VerifyDevice(ALCdevice **device)
UnlockLists();
return ALC_TRUE;
}
- tmpDevice = tmpDevice->next;
+ tmpDevice = ATOMIC_LOAD(&tmpDevice->next, almemory_order_relaxed);
}
UnlockLists();
@@ -2304,30 +2559,50 @@ static ALCboolean VerifyDevice(ALCdevice **device)
static ALvoid InitContext(ALCcontext *Context)
{
ALlistener *listener = Context->Listener;
+ struct ALeffectslotArray *auxslots;
+
//Initialise listener
listener->Gain = 1.0f;
- listener->MetersPerUnit = 1.0f;
- aluVectorSet(&listener->Position, 0.0f, 0.0f, 0.0f, 1.0f);
- aluVectorSet(&listener->Velocity, 0.0f, 0.0f, 0.0f, 0.0f);
+ listener->Position[0] = 0.0f;
+ listener->Position[1] = 0.0f;
+ listener->Position[2] = 0.0f;
+ listener->Velocity[0] = 0.0f;
+ listener->Velocity[1] = 0.0f;
+ listener->Velocity[2] = 0.0f;
listener->Forward[0] = 0.0f;
listener->Forward[1] = 0.0f;
listener->Forward[2] = -1.0f;
listener->Up[0] = 0.0f;
listener->Up[1] = 1.0f;
listener->Up[2] = 0.0f;
- aluMatrixdSet(&listener->Params.Matrix,
- 1.0, 0.0, 0.0, 0.0,
- 0.0, 1.0, 0.0, 0.0,
- 0.0, 0.0, 1.0, 0.0,
- 0.0, 0.0, 0.0, 1.0
- );
- aluVectorSet(&listener->Params.Velocity, 0.0f, 0.0f, 0.0f, 0.0f);
+ ATOMIC_FLAG_TEST_AND_SET(&listener->PropsClean, almemory_order_relaxed);
+
+ ATOMIC_INIT(&listener->Update, NULL);
//Validate Context
+ InitRef(&Context->UpdateCount, 0);
+ ATOMIC_INIT(&Context->HoldUpdates, AL_FALSE);
+ Context->GainBoost = 1.0f;
+ almtx_init(&Context->PropLock, almtx_plain);
ATOMIC_INIT(&Context->LastError, AL_NO_ERROR);
- ATOMIC_INIT(&Context->UpdateSources, AL_FALSE);
- InitUIntMap(&Context->SourceMap, Context->Device->MaxNoOfSources);
- InitUIntMap(&Context->EffectSlotMap, Context->Device->AuxiliaryEffectSlotMax);
+ VECTOR_INIT(Context->SourceList);
+ Context->NumSources = 0;
+ almtx_init(&Context->SourceLock, almtx_plain);
+ VECTOR_INIT(Context->EffectSlotList);
+ almtx_init(&Context->EffectSlotLock, almtx_plain);
+
+ if(Context->DefaultSlot)
+ {
+ auxslots = al_calloc(DEF_ALIGN, FAM_SIZE(struct ALeffectslotArray, slot, 1));
+ auxslots->count = 1;
+ auxslots->slot[0] = Context->DefaultSlot;
+ }
+ else
+ {
+ auxslots = al_calloc(DEF_ALIGN, sizeof(struct ALeffectslotArray));
+ auxslots->count = 0;
+ }
+ ATOMIC_INIT(&Context->ActiveAuxSlots, auxslots);
//Set globals
Context->DistanceModel = DefaultDistanceModel;
@@ -2335,9 +2610,40 @@ static ALvoid InitContext(ALCcontext *Context)
Context->DopplerFactor = 1.0f;
Context->DopplerVelocity = 1.0f;
Context->SpeedOfSound = SPEEDOFSOUNDMETRESPERSEC;
- Context->DeferUpdates = AL_FALSE;
+ Context->MetersPerUnit = AL_DEFAULT_METERS_PER_UNIT;
+ ATOMIC_FLAG_TEST_AND_SET(&Context->PropsClean, almemory_order_relaxed);
+ ATOMIC_INIT(&Context->DeferUpdates, AL_FALSE);
+ alsem_init(&Context->EventSem, 0);
+ Context->AsyncEvents = NULL;
+ ATOMIC_INIT(&Context->EnabledEvts, 0);
+ almtx_init(&Context->EventCbLock, almtx_plain);
+ Context->EventCb = NULL;
+ Context->EventParam = NULL;
+
+ ATOMIC_INIT(&Context->Update, NULL);
+ ATOMIC_INIT(&Context->FreeContextProps, NULL);
+ ATOMIC_INIT(&Context->FreeListenerProps, NULL);
+ ATOMIC_INIT(&Context->FreeVoiceProps, NULL);
+ ATOMIC_INIT(&Context->FreeEffectslotProps, NULL);
Context->ExtensionList = alExtList;
+
+
+ listener->Params.Matrix = IdentityMatrixf;
+ aluVectorSet(&listener->Params.Velocity, 0.0f, 0.0f, 0.0f, 0.0f);
+ listener->Params.Gain = listener->Gain;
+ listener->Params.MetersPerUnit = Context->MetersPerUnit;
+ listener->Params.DopplerFactor = Context->DopplerFactor;
+ listener->Params.SpeedOfSound = Context->SpeedOfSound * Context->DopplerVelocity;
+ listener->Params.ReverbSpeedOfSound = listener->Params.SpeedOfSound *
+ listener->Params.MetersPerUnit;
+ listener->Params.SourceDistanceModel = Context->SourceDistanceModel;
+ listener->Params.DistanceModel = Context->DistanceModel;
+
+
+ Context->AsyncEvents = ll_ringbuffer_create(63, sizeof(AsyncEvent), false);
+ if(althrd_create(&Context->EventThread, EventThread, Context) != althrd_success)
+ ERR("Failed to start event thread! Expect problems.\n");
}
@@ -2348,28 +2654,111 @@ static ALvoid InitContext(ALCcontext *Context)
*/
static void FreeContext(ALCcontext *context)
{
+ ALlistener *listener = context->Listener;
+ struct ALeffectslotArray *auxslots;
+ struct ALeffectslotProps *eprops;
+ struct ALlistenerProps *lprops;
+ struct ALcontextProps *cprops;
+ struct ALvoiceProps *vprops;
+ size_t count;
+ ALsizei i;
+
TRACE("%p\n", context);
- if(context->SourceMap.size > 0)
+ if((cprops=ATOMIC_LOAD(&context->Update, almemory_order_acquire)) != NULL)
+ {
+ TRACE("Freed unapplied context update %p\n", cprops);
+ al_free(cprops);
+ }
+
+ count = 0;
+ cprops = ATOMIC_LOAD(&context->FreeContextProps, almemory_order_acquire);
+ while(cprops)
+ {
+ struct ALcontextProps *next = ATOMIC_LOAD(&cprops->next, almemory_order_acquire);
+ al_free(cprops);
+ cprops = next;
+ ++count;
+ }
+ TRACE("Freed "SZFMT" context property object%s\n", count, (count==1)?"":"s");
+
+ if(context->DefaultSlot)
{
- WARN("(%p) Deleting %d Source(s)\n", context, context->SourceMap.size);
- ReleaseALSources(context);
+ DeinitEffectSlot(context->DefaultSlot);
+ context->DefaultSlot = NULL;
}
- ResetUIntMap(&context->SourceMap);
- if(context->EffectSlotMap.size > 0)
+ auxslots = ATOMIC_EXCHANGE_PTR(&context->ActiveAuxSlots, NULL, almemory_order_relaxed);
+ al_free(auxslots);
+
+ ReleaseALSources(context);
+#define FREE_SOURCESUBLIST(x) al_free((x)->Sources)
+ VECTOR_FOR_EACH(SourceSubList, context->SourceList, FREE_SOURCESUBLIST);
+#undef FREE_SOURCESUBLIST
+ VECTOR_DEINIT(context->SourceList);
+ context->NumSources = 0;
+ almtx_destroy(&context->SourceLock);
+
+ count = 0;
+ eprops = ATOMIC_LOAD(&context->FreeEffectslotProps, almemory_order_relaxed);
+ while(eprops)
+ {
+ struct ALeffectslotProps *next = ATOMIC_LOAD(&eprops->next, almemory_order_relaxed);
+ if(eprops->State) ALeffectState_DecRef(eprops->State);
+ al_free(eprops);
+ eprops = next;
+ ++count;
+ }
+ TRACE("Freed "SZFMT" AuxiliaryEffectSlot property object%s\n", count, (count==1)?"":"s");
+
+ ReleaseALAuxiliaryEffectSlots(context);
+#define FREE_EFFECTSLOTPTR(x) al_free(*(x))
+ VECTOR_FOR_EACH(ALeffectslotPtr, context->EffectSlotList, FREE_EFFECTSLOTPTR);
+#undef FREE_EFFECTSLOTPTR
+ VECTOR_DEINIT(context->EffectSlotList);
+ almtx_destroy(&context->EffectSlotLock);
+
+ count = 0;
+ vprops = ATOMIC_LOAD(&context->FreeVoiceProps, almemory_order_relaxed);
+ while(vprops)
{
- WARN("(%p) Deleting %d AuxiliaryEffectSlot(s)\n", context, context->EffectSlotMap.size);
- ReleaseALAuxiliaryEffectSlots(context);
+ struct ALvoiceProps *next = ATOMIC_LOAD(&vprops->next, almemory_order_relaxed);
+ al_free(vprops);
+ vprops = next;
+ ++count;
}
- ResetUIntMap(&context->EffectSlotMap);
+ TRACE("Freed "SZFMT" voice property object%s\n", count, (count==1)?"":"s");
+ for(i = 0;i < context->VoiceCount;i++)
+ DeinitVoice(context->Voices[i]);
al_free(context->Voices);
context->Voices = NULL;
context->VoiceCount = 0;
context->MaxVoices = 0;
- VECTOR_DEINIT(context->ActiveAuxSlots);
+ if((lprops=ATOMIC_LOAD(&listener->Update, almemory_order_acquire)) != NULL)
+ {
+ TRACE("Freed unapplied listener update %p\n", lprops);
+ al_free(lprops);
+ }
+ count = 0;
+ lprops = ATOMIC_LOAD(&context->FreeListenerProps, almemory_order_acquire);
+ while(lprops)
+ {
+ struct ALlistenerProps *next = ATOMIC_LOAD(&lprops->next, almemory_order_acquire);
+ al_free(lprops);
+ lprops = next;
+ ++count;
+ }
+ TRACE("Freed "SZFMT" listener property object%s\n", count, (count==1)?"":"s");
+
+ almtx_destroy(&context->EventCbLock);
+ alsem_destroy(&context->EventSem);
+
+ ll_ringbuffer_free(context->AsyncEvents);
+ context->AsyncEvents = NULL;
+
+ almtx_destroy(&context->PropLock);
ALCdevice_DecRef(context->Device);
context->Device = NULL;
@@ -2382,12 +2771,14 @@ static void FreeContext(ALCcontext *context)
/* ReleaseContext
*
* Removes the context reference from the given device and removes it from
- * being current on the running thread or globally.
+ * being current on the running thread or globally. Returns true if other
+ * contexts still exist on the device.
*/
-static void ReleaseContext(ALCcontext *context, ALCdevice *device)
+static bool ReleaseContext(ALCcontext *context, ALCdevice *device)
{
- ALCcontext *nextctx;
- ALCcontext *origctx;
+ static const AsyncEvent kill_evt = ASYNC_EVENT(EventType_KillThread);
+ ALCcontext *origctx, *newhead;
+ bool ret = true;
if(altss_get(LocalContext) == context)
{
@@ -2397,44 +2788,60 @@ static void ReleaseContext(ALCcontext *context, ALCdevice *device)
}
origctx = context;
- if(ATOMIC_COMPARE_EXCHANGE_STRONG(ALCcontext*, &GlobalContext, &origctx, NULL))
+ if(ATOMIC_COMPARE_EXCHANGE_PTR_STRONG_SEQ(&GlobalContext, &origctx, NULL))
ALCcontext_DecRef(context);
- ALCdevice_Lock(device);
+ V0(device->Backend,lock)();
origctx = context;
- nextctx = context->next;
- if(!ATOMIC_COMPARE_EXCHANGE_STRONG(ALCcontext*, &device->ContextList, &origctx, nextctx))
+ newhead = ATOMIC_LOAD(&context->next, almemory_order_relaxed);
+ if(!ATOMIC_COMPARE_EXCHANGE_PTR_STRONG_SEQ(&device->ContextList, &origctx, newhead))
{
ALCcontext *list;
do {
+ /* origctx is what the desired context failed to match. Try
+ * swapping out the next one in the list.
+ */
list = origctx;
origctx = context;
- } while(!COMPARE_EXCHANGE(&list->next, &origctx, nextctx));
+ } while(!ATOMIC_COMPARE_EXCHANGE_PTR_STRONG_SEQ(&list->next, &origctx, newhead));
}
- ALCdevice_Unlock(device);
+ else
+ ret = !!newhead;
+ V0(device->Backend,unlock)();
+
+ /* Make sure the context is finished and no longer processing in the mixer
+ * before sending the message queue kill event. The backend's lock does
+ * this, although waiting for a non-odd mix count would work too.
+ */
+
+ while(ll_ringbuffer_write(context->AsyncEvents, (const char*)&kill_evt, 1) == 0)
+ althrd_yield();
+ alsem_post(&context->EventSem);
+ althrd_join(context->EventThread, NULL);
ALCcontext_DecRef(context);
+ return ret;
}
-void ALCcontext_IncRef(ALCcontext *context)
+static void ALCcontext_IncRef(ALCcontext *context)
{
- uint ref;
- ref = IncrementRef(&context->ref);
+ uint ref = IncrementRef(&context->ref);
TRACEREF("%p increasing refcount to %u\n", context, ref);
}
void ALCcontext_DecRef(ALCcontext *context)
{
- uint ref;
- ref = DecrementRef(&context->ref);
+ uint ref = DecrementRef(&context->ref);
TRACEREF("%p decreasing refcount to %u\n", context, ref);
if(ref == 0) FreeContext(context);
}
static void ReleaseThreadCtx(void *ptr)
{
- WARN("%p current for thread being destroyed\n", ptr);
- ALCcontext_DecRef(ptr);
+ ALCcontext *context = ptr;
+ uint ref = DecrementRef(&context->ref);
+ TRACEREF("%p decreasing refcount to %u\n", context, ref);
+ ERR("Context %p current for thread being destroyed, possible leak!\n", context);
}
/* VerifyContext
@@ -2446,10 +2853,10 @@ static ALCboolean VerifyContext(ALCcontext **context)
ALCdevice *dev;
LockLists();
- dev = ATOMIC_LOAD(&DeviceList);
+ dev = ATOMIC_LOAD_SEQ(&DeviceList);
while(dev)
{
- ALCcontext *ctx = ATOMIC_LOAD(&dev->ContextList);
+ ALCcontext *ctx = ATOMIC_LOAD(&dev->ContextList, almemory_order_acquire);
while(ctx)
{
if(ctx == *context)
@@ -2458,9 +2865,9 @@ static ALCboolean VerifyContext(ALCcontext **context)
UnlockLists();
return ALC_TRUE;
}
- ctx = ctx->next;
+ ctx = ATOMIC_LOAD(&ctx->next, almemory_order_relaxed);
}
- dev = dev->next;
+ dev = ATOMIC_LOAD(&dev->next, almemory_order_relaxed);
}
UnlockLists();
@@ -2484,7 +2891,7 @@ ALCcontext *GetContextRef(void)
else
{
LockLists();
- context = ATOMIC_LOAD(&GlobalContext);
+ context = ATOMIC_LOAD_SEQ(&GlobalContext);
if(context)
ALCcontext_IncRef(context);
UnlockLists();
@@ -2494,6 +2901,91 @@ ALCcontext *GetContextRef(void)
}
+void AllocateVoices(ALCcontext *context, ALsizei num_voices, ALsizei old_sends)
+{
+ ALCdevice *device = context->Device;
+ ALsizei num_sends = device->NumAuxSends;
+ struct ALvoiceProps *props;
+ size_t sizeof_props;
+ size_t sizeof_voice;
+ ALvoice **voices;
+ ALvoice *voice;
+ ALsizei v = 0;
+ size_t size;
+
+ if(num_voices == context->MaxVoices && num_sends == old_sends)
+ return;
+
+ /* Allocate the voice pointers, voices, and the voices' stored source
+ * property set (including the dynamically-sized Send[] array) in one
+ * chunk.
+ */
+ sizeof_voice = RoundUp(FAM_SIZE(ALvoice, Send, num_sends), 16);
+ sizeof_props = RoundUp(FAM_SIZE(struct ALvoiceProps, Send, num_sends), 16);
+ size = sizeof(ALvoice*) + sizeof_voice + sizeof_props;
+
+ voices = al_calloc(16, RoundUp(size*num_voices, 16));
+ /* The voice and property objects are stored interleaved since they're
+ * paired together.
+ */
+ voice = (ALvoice*)((char*)voices + RoundUp(num_voices*sizeof(ALvoice*), 16));
+ props = (struct ALvoiceProps*)((char*)voice + sizeof_voice);
+
+ if(context->Voices)
+ {
+ const ALsizei v_count = mini(context->VoiceCount, num_voices);
+ const ALsizei s_count = mini(old_sends, num_sends);
+
+ for(;v < v_count;v++)
+ {
+ ALvoice *old_voice = context->Voices[v];
+ ALsizei i;
+
+ /* Copy the old voice data and source property set to the new
+ * storage.
+ */
+ *voice = *old_voice;
+ for(i = 0;i < s_count;i++)
+ voice->Send[i] = old_voice->Send[i];
+ *props = *(old_voice->Props);
+ for(i = 0;i < s_count;i++)
+ props->Send[i] = old_voice->Props->Send[i];
+
+ /* Set this voice's property set pointer and voice reference. */
+ voice->Props = props;
+ voices[v] = voice;
+
+ /* Increment pointers to the next storage space. */
+ voice = (ALvoice*)((char*)props + sizeof_props);
+ props = (struct ALvoiceProps*)((char*)voice + sizeof_voice);
+ }
+ /* Deinit any left over voices that weren't copied over to the new
+ * array. NOTE: If this does anything, v equals num_voices and
+ * num_voices is less than VoiceCount, so the following loop won't do
+ * anything.
+ */
+ for(;v < context->VoiceCount;v++)
+ DeinitVoice(context->Voices[v]);
+ }
+ /* Finish setting the voices' property set pointers and references. */
+ for(;v < num_voices;v++)
+ {
+ ATOMIC_INIT(&voice->Update, NULL);
+
+ voice->Props = props;
+ voices[v] = voice;
+
+ voice = (ALvoice*)((char*)props + sizeof_props);
+ props = (struct ALvoiceProps*)((char*)voice + sizeof_voice);
+ }
+
+ al_free(context->Voices);
+ context->Voices = voices;
+ context->MaxVoices = num_voices;
+ context->VoiceCount = mini(context->VoiceCount, num_voices);
+}
+
+
/************************************************
* Standard ALC functions
************************************************/
@@ -2508,11 +3000,11 @@ ALC_API ALCenum ALC_APIENTRY alcGetError(ALCdevice *device)
if(VerifyDevice(&device))
{
- errorCode = ATOMIC_EXCHANGE(ALCenum, &device->LastError, ALC_NO_ERROR);
+ errorCode = ATOMIC_EXCHANGE_SEQ(&device->LastError, ALC_NO_ERROR);
ALCdevice_DecRef(device);
}
else
- errorCode = ATOMIC_EXCHANGE(ALCenum, &LastNullDeviceError, ALC_NO_ERROR);
+ errorCode = ATOMIC_EXCHANGE_SEQ(&LastNullDeviceError, ALC_NO_ERROR);
return errorCode;
}
@@ -2596,26 +3088,26 @@ ALC_API const ALCchar* ALC_APIENTRY alcGetString(ALCdevice *Device, ALCenum para
case ALC_ALL_DEVICES_SPECIFIER:
if(VerifyDevice(&Device))
{
- value = al_string_get_cstr(Device->DeviceName);
+ value = alstr_get_cstr(Device->DeviceName);
ALCdevice_DecRef(Device);
}
else
{
ProbeAllDevicesList();
- value = al_string_get_cstr(alcAllDevicesList);
+ value = alstr_get_cstr(alcAllDevicesList);
}
break;
case ALC_CAPTURE_DEVICE_SPECIFIER:
if(VerifyDevice(&Device))
{
- value = al_string_get_cstr(Device->DeviceName);
+ value = alstr_get_cstr(Device->DeviceName);
ALCdevice_DecRef(Device);
}
else
{
ProbeCaptureDeviceList();
- value = al_string_get_cstr(alcCaptureDeviceList);
+ value = alstr_get_cstr(alcCaptureDeviceList);
}
break;
@@ -2625,13 +3117,13 @@ ALC_API const ALCchar* ALC_APIENTRY alcGetString(ALCdevice *Device, ALCenum para
break;
case ALC_DEFAULT_ALL_DEVICES_SPECIFIER:
- if(al_string_empty(alcAllDevicesList))
+ if(alstr_empty(alcAllDevicesList))
ProbeAllDevicesList();
VerifyDevice(&Device);
free(alcDefaultAllDevicesSpecifier);
- alcDefaultAllDevicesSpecifier = strdup(al_string_get_cstr(alcAllDevicesList));
+ alcDefaultAllDevicesSpecifier = strdup(alstr_get_cstr(alcAllDevicesList));
if(!alcDefaultAllDevicesSpecifier)
alcSetError(Device, ALC_OUT_OF_MEMORY);
@@ -2640,13 +3132,13 @@ ALC_API const ALCchar* ALC_APIENTRY alcGetString(ALCdevice *Device, ALCenum para
break;
case ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER:
- if(al_string_empty(alcCaptureDeviceList))
+ if(alstr_empty(alcCaptureDeviceList))
ProbeCaptureDeviceList();
VerifyDevice(&Device);
free(alcCaptureDefaultDeviceSpecifier);
- alcCaptureDefaultDeviceSpecifier = strdup(al_string_get_cstr(alcCaptureDeviceList));
+ alcCaptureDefaultDeviceSpecifier = strdup(alstr_get_cstr(alcCaptureDeviceList));
if(!alcCaptureDefaultDeviceSpecifier)
alcSetError(Device, ALC_OUT_OF_MEMORY);
@@ -2669,9 +3161,9 @@ ALC_API const ALCchar* ALC_APIENTRY alcGetString(ALCdevice *Device, ALCenum para
alcSetError(NULL, ALC_INVALID_DEVICE);
else
{
- LockLists();
- value = (Device->Hrtf ? al_string_get_cstr(Device->Hrtf_Name) : "");
- UnlockLists();
+ almtx_lock(&Device->BackendLock);
+ value = (Device->HrtfHandle ? alstr_get_cstr(Device->HrtfName) : "");
+ almtx_unlock(&Device->BackendLock);
ALCdevice_DecRef(Device);
}
break;
@@ -2687,6 +3179,15 @@ ALC_API const ALCchar* ALC_APIENTRY alcGetString(ALCdevice *Device, ALCenum para
}
+static inline ALCsizei NumAttrsForDevice(ALCdevice *device)
+{
+ if(device->Type == Capture) return 9;
+ if(device->Type != Loopback) return 29;
+ if(device->FmtChans == DevFmtAmbi3D)
+ return 35;
+ return 29;
+}
+
static ALCsizei GetIntegerv(ALCdevice *device, ALCenum param, ALCsizei size, ALCint *values)
{
ALCsizei i;
@@ -2718,6 +3219,10 @@ static ALCsizei GetIntegerv(ALCdevice *device, ALCenum param, ALCsizei size, ALC
case ALC_CAPTURE_SAMPLES:
case ALC_FORMAT_CHANNELS_SOFT:
case ALC_FORMAT_TYPE_SOFT:
+ case ALC_AMBISONIC_LAYOUT_SOFT:
+ case ALC_AMBISONIC_SCALING_SOFT:
+ case ALC_AMBISONIC_ORDER_SOFT:
+ case ALC_MAX_AMBISONIC_ORDER_SOFT:
alcSetError(NULL, ALC_INVALID_DEVICE);
return 0;
@@ -2732,14 +3237,47 @@ static ALCsizei GetIntegerv(ALCdevice *device, ALCenum param, ALCsizei size, ALC
{
switch(param)
{
+ case ALC_ATTRIBUTES_SIZE:
+ values[0] = NumAttrsForDevice(device);
+ return 1;
+
+ case ALC_ALL_ATTRIBUTES:
+ if(size < NumAttrsForDevice(device))
+ {
+ alcSetError(device, ALC_INVALID_VALUE);
+ return 0;
+ }
+
+ i = 0;
+ almtx_lock(&device->BackendLock);
+ values[i++] = ALC_MAJOR_VERSION;
+ values[i++] = alcMajorVersion;
+ values[i++] = ALC_MINOR_VERSION;
+ values[i++] = alcMinorVersion;
+ values[i++] = ALC_CAPTURE_SAMPLES;
+ values[i++] = V0(device->Backend,availableSamples)();
+ values[i++] = ALC_CONNECTED;
+ values[i++] = ATOMIC_LOAD(&device->Connected, almemory_order_relaxed);
+ almtx_unlock(&device->BackendLock);
+
+ values[i++] = 0;
+ return i;
+
+ case ALC_MAJOR_VERSION:
+ values[0] = alcMajorVersion;
+ return 1;
+ case ALC_MINOR_VERSION:
+ values[0] = alcMinorVersion;
+ return 1;
+
case ALC_CAPTURE_SAMPLES:
- V0(device->Backend,lock)();
+ almtx_lock(&device->BackendLock);
values[0] = V0(device->Backend,availableSamples)();
- V0(device->Backend,unlock)();
+ almtx_unlock(&device->BackendLock);
return 1;
case ALC_CONNECTED:
- values[0] = device->Connected;
+ values[0] = ATOMIC_LOAD(&device->Connected, almemory_order_acquire);
return 1;
default:
@@ -2752,37 +3290,30 @@ static ALCsizei GetIntegerv(ALCdevice *device, ALCenum param, ALCsizei size, ALC
/* render device */
switch(param)
{
- case ALC_MAJOR_VERSION:
- values[0] = alcMajorVersion;
- return 1;
-
- case ALC_MINOR_VERSION:
- values[0] = alcMinorVersion;
- return 1;
-
- case ALC_EFX_MAJOR_VERSION:
- values[0] = alcEFXMajorVersion;
- return 1;
-
- case ALC_EFX_MINOR_VERSION:
- values[0] = alcEFXMinorVersion;
- return 1;
-
case ALC_ATTRIBUTES_SIZE:
- values[0] = 17;
+ values[0] = NumAttrsForDevice(device);
return 1;
case ALC_ALL_ATTRIBUTES:
- if(size < 17)
+ if(size < NumAttrsForDevice(device))
{
alcSetError(device, ALC_INVALID_VALUE);
return 0;
}
i = 0;
+ almtx_lock(&device->BackendLock);
+ values[i++] = ALC_MAJOR_VERSION;
+ values[i++] = alcMajorVersion;
+ values[i++] = ALC_MINOR_VERSION;
+ values[i++] = alcMinorVersion;
+ values[i++] = ALC_EFX_MAJOR_VERSION;
+ values[i++] = alcEFXMajorVersion;
+ values[i++] = ALC_EFX_MINOR_VERSION;
+ values[i++] = alcEFXMinorVersion;
+
values[i++] = ALC_FREQUENCY;
values[i++] = device->Frequency;
-
if(device->Type != Loopback)
{
values[i++] = ALC_REFRESH;
@@ -2793,6 +3324,18 @@ static ALCsizei GetIntegerv(ALCdevice *device, ALCenum param, ALCsizei size, ALC
}
else
{
+ if(device->FmtChans == DevFmtAmbi3D)
+ {
+ values[i++] = ALC_AMBISONIC_LAYOUT_SOFT;
+ values[i++] = device->AmbiLayout;
+
+ values[i++] = ALC_AMBISONIC_SCALING_SOFT;
+ values[i++] = device->AmbiScale;
+
+ values[i++] = ALC_AMBISONIC_ORDER_SOFT;
+ values[i++] = device->AmbiOrder;
+ }
+
values[i++] = ALC_FORMAT_CHANNELS_SOFT;
values[i++] = device->FmtChans;
@@ -2810,14 +3353,37 @@ static ALCsizei GetIntegerv(ALCdevice *device, ALCenum param, ALCsizei size, ALC
values[i++] = device->NumAuxSends;
values[i++] = ALC_HRTF_SOFT;
- values[i++] = (device->Hrtf ? ALC_TRUE : ALC_FALSE);
+ values[i++] = (device->HrtfHandle ? ALC_TRUE : ALC_FALSE);
values[i++] = ALC_HRTF_STATUS_SOFT;
- values[i++] = device->Hrtf_Status;
+ values[i++] = device->HrtfStatus;
+
+ values[i++] = ALC_OUTPUT_LIMITER_SOFT;
+ values[i++] = device->Limiter ? ALC_TRUE : ALC_FALSE;
+
+ values[i++] = ALC_MAX_AMBISONIC_ORDER_SOFT;
+ values[i++] = MAX_AMBI_ORDER;
+ almtx_unlock(&device->BackendLock);
values[i++] = 0;
return i;
+ case ALC_MAJOR_VERSION:
+ values[0] = alcMajorVersion;
+ return 1;
+
+ case ALC_MINOR_VERSION:
+ values[0] = alcMinorVersion;
+ return 1;
+
+ case ALC_EFX_MAJOR_VERSION:
+ values[0] = alcEFXMajorVersion;
+ return 1;
+
+ case ALC_EFX_MINOR_VERSION:
+ values[0] = alcEFXMinorVersion;
+ return 1;
+
case ALC_FREQUENCY:
values[0] = device->Frequency;
return 1;
@@ -2828,7 +3394,9 @@ static ALCsizei GetIntegerv(ALCdevice *device, ALCenum param, ALCsizei size, ALC
alcSetError(device, ALC_INVALID_DEVICE);
return 0;
}
+ almtx_lock(&device->BackendLock);
values[0] = device->Frequency / device->UpdateSize;
+ almtx_unlock(&device->BackendLock);
return 1;
case ALC_SYNC:
@@ -2858,6 +3426,33 @@ static ALCsizei GetIntegerv(ALCdevice *device, ALCenum param, ALCsizei size, ALC
values[0] = device->FmtType;
return 1;
+ case ALC_AMBISONIC_LAYOUT_SOFT:
+ if(device->Type != Loopback || device->FmtChans != DevFmtAmbi3D)
+ {
+ alcSetError(device, ALC_INVALID_DEVICE);
+ return 0;
+ }
+ values[0] = device->AmbiLayout;
+ return 1;
+
+ case ALC_AMBISONIC_SCALING_SOFT:
+ if(device->Type != Loopback || device->FmtChans != DevFmtAmbi3D)
+ {
+ alcSetError(device, ALC_INVALID_DEVICE);
+ return 0;
+ }
+ values[0] = device->AmbiScale;
+ return 1;
+
+ case ALC_AMBISONIC_ORDER_SOFT:
+ if(device->Type != Loopback || device->FmtChans != DevFmtAmbi3D)
+ {
+ alcSetError(device, ALC_INVALID_DEVICE);
+ return 0;
+ }
+ values[0] = device->AmbiOrder;
+ return 1;
+
case ALC_MONO_SOURCES:
values[0] = device->NumMonoSources;
return 1;
@@ -2871,21 +3466,31 @@ static ALCsizei GetIntegerv(ALCdevice *device, ALCenum param, ALCsizei size, ALC
return 1;
case ALC_CONNECTED:
- values[0] = device->Connected;
+ values[0] = ATOMIC_LOAD(&device->Connected, almemory_order_acquire);
return 1;
case ALC_HRTF_SOFT:
- values[0] = (device->Hrtf ? ALC_TRUE : ALC_FALSE);
+ values[0] = (device->HrtfHandle ? ALC_TRUE : ALC_FALSE);
return 1;
case ALC_HRTF_STATUS_SOFT:
- values[0] = device->Hrtf_Status;
+ values[0] = device->HrtfStatus;
return 1;
case ALC_NUM_HRTF_SPECIFIERS_SOFT:
- FreeHrtfList(&device->Hrtf_List);
- device->Hrtf_List = EnumerateHrtf(device->DeviceName);
- values[0] = (ALCint)VECTOR_SIZE(device->Hrtf_List);
+ almtx_lock(&device->BackendLock);
+ FreeHrtfList(&device->HrtfList);
+ device->HrtfList = EnumerateHrtf(device->DeviceName);
+ values[0] = (ALCint)VECTOR_SIZE(device->HrtfList);
+ almtx_unlock(&device->BackendLock);
+ return 1;
+
+ case ALC_OUTPUT_LIMITER_SOFT:
+ values[0] = device->Limiter ? ALC_TRUE : ALC_FALSE;
+ return 1;
+
+ case ALC_MAX_AMBISONIC_ORDER_SOFT:
+ values[0] = MAX_AMBI_ORDER;
return 1;
default:
@@ -2927,20 +3532,24 @@ ALC_API void ALC_APIENTRY alcGetInteger64vSOFT(ALCdevice *device, ALCenum pname,
}
else /* render device */
{
+ ClockLatency clock;
+ ALuint64 basecount;
+ ALuint samplecount;
+ ALuint refcount;
+
switch(pname)
{
case ALC_ATTRIBUTES_SIZE:
- *values = 19;
+ *values = NumAttrsForDevice(device)+4;
break;
case ALC_ALL_ATTRIBUTES:
- if(size < 19)
+ if(size < NumAttrsForDevice(device)+4)
alcSetError(device, ALC_INVALID_VALUE);
else
{
- int i = 0;
-
- V0(device->Backend,lock)();
+ i = 0;
+ almtx_lock(&device->BackendLock);
values[i++] = ALC_FREQUENCY;
values[i++] = device->Frequency;
@@ -2954,6 +3563,18 @@ ALC_API void ALC_APIENTRY alcGetInteger64vSOFT(ALCdevice *device, ALCenum pname,
}
else
{
+ if(device->FmtChans == DevFmtAmbi3D)
+ {
+ values[i++] = ALC_AMBISONIC_LAYOUT_SOFT;
+ values[i++] = device->AmbiLayout;
+
+ values[i++] = ALC_AMBISONIC_SCALING_SOFT;
+ values[i++] = device->AmbiScale;
+
+ values[i++] = ALC_AMBISONIC_ORDER_SOFT;
+ values[i++] = device->AmbiOrder;
+ }
+
values[i++] = ALC_FORMAT_CHANNELS_SOFT;
values[i++] = device->FmtChans;
@@ -2971,25 +3592,56 @@ ALC_API void ALC_APIENTRY alcGetInteger64vSOFT(ALCdevice *device, ALCenum pname,
values[i++] = device->NumAuxSends;
values[i++] = ALC_HRTF_SOFT;
- values[i++] = (device->Hrtf ? ALC_TRUE : ALC_FALSE);
+ values[i++] = (device->HrtfHandle ? ALC_TRUE : ALC_FALSE);
values[i++] = ALC_HRTF_STATUS_SOFT;
- values[i++] = device->Hrtf_Status;
+ values[i++] = device->HrtfStatus;
+
+ values[i++] = ALC_OUTPUT_LIMITER_SOFT;
+ values[i++] = device->Limiter ? ALC_TRUE : ALC_FALSE;
+ clock = GetClockLatency(device);
values[i++] = ALC_DEVICE_CLOCK_SOFT;
- values[i++] = device->ClockBase +
- (device->SamplesDone * DEVICE_CLOCK_RES / device->Frequency);
+ values[i++] = clock.ClockTime;
+
+ values[i++] = ALC_DEVICE_LATENCY_SOFT;
+ values[i++] = clock.Latency;
+ almtx_unlock(&device->BackendLock);
values[i++] = 0;
- V0(device->Backend,unlock)();
}
break;
case ALC_DEVICE_CLOCK_SOFT:
- V0(device->Backend,lock)();
- *values = device->ClockBase +
- (device->SamplesDone * DEVICE_CLOCK_RES / device->Frequency);
- V0(device->Backend,unlock)();
+ almtx_lock(&device->BackendLock);
+ do {
+ while(((refcount=ReadRef(&device->MixCount))&1) != 0)
+ althrd_yield();
+ basecount = device->ClockBase;
+ samplecount = device->SamplesDone;
+ } while(refcount != ReadRef(&device->MixCount));
+ *values = basecount + (samplecount*DEVICE_CLOCK_RES/device->Frequency);
+ almtx_unlock(&device->BackendLock);
+ break;
+
+ case ALC_DEVICE_LATENCY_SOFT:
+ almtx_lock(&device->BackendLock);
+ clock = GetClockLatency(device);
+ almtx_unlock(&device->BackendLock);
+ *values = clock.Latency;
+ break;
+
+ case ALC_DEVICE_CLOCK_LATENCY_SOFT:
+ if(size < 2)
+ alcSetError(device, ALC_INVALID_VALUE);
+ else
+ {
+ almtx_lock(&device->BackendLock);
+ clock = GetClockLatency(device);
+ almtx_unlock(&device->BackendLock);
+ values[0] = clock.ClockTime;
+ values[1] = clock.Latency;
+ }
break;
default:
@@ -3060,10 +3712,15 @@ ALC_API ALCvoid* ALC_APIENTRY alcGetProcAddress(ALCdevice *device, const ALCchar
}
else
{
- ALsizei i = 0;
- while(alcFunctions[i].funcName && strcmp(alcFunctions[i].funcName, funcName) != 0)
- i++;
- ptr = alcFunctions[i].address;
+ size_t i = 0;
+ for(i = 0;i < COUNTOF(alcFunctions);i++)
+ {
+ if(strcmp(alcFunctions[i].funcName, funcName) == 0)
+ {
+ ptr = alcFunctions[i].address;
+ break;
+ }
+ }
}
return ptr;
@@ -3086,10 +3743,15 @@ ALC_API ALCenum ALC_APIENTRY alcGetEnumValue(ALCdevice *device, const ALCchar *e
}
else
{
- ALsizei i = 0;
- while(enumeration[i].enumName && strcmp(enumeration[i].enumName, enumName) != 0)
- i++;
- val = enumeration[i].value;
+ size_t i = 0;
+ for(i = 0;i < COUNTOF(alcEnumerations);i++)
+ {
+ if(strcmp(alcEnumerations[i].enumName, enumName) == 0)
+ {
+ val = alcEnumerations[i].value;
+ break;
+ }
+ }
}
return val;
@@ -3103,81 +3765,116 @@ ALC_API ALCenum ALC_APIENTRY alcGetEnumValue(ALCdevice *device, const ALCchar *e
ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCint *attrList)
{
ALCcontext *ALContext;
+ ALfloat valf;
ALCenum err;
+ /* Explicitly hold the list lock while taking the BackendLock in case the
+ * device is asynchronously destropyed, to ensure this new context is
+ * properly cleaned up after being made.
+ */
LockLists();
- if(!VerifyDevice(&device) || device->Type == Capture || !device->Connected)
+ if(!VerifyDevice(&device) || device->Type == Capture ||
+ !ATOMIC_LOAD(&device->Connected, almemory_order_relaxed))
{
UnlockLists();
alcSetError(device, ALC_INVALID_DEVICE);
if(device) ALCdevice_DecRef(device);
return NULL;
}
+ almtx_lock(&device->BackendLock);
+ UnlockLists();
+
+ ATOMIC_STORE_SEQ(&device->LastError, ALC_NO_ERROR);
+
+ if(device->Type == Playback && DefaultEffect.type != AL_EFFECT_NULL)
+ ALContext = al_calloc(16, sizeof(ALCcontext)+sizeof(ALlistener)+sizeof(ALeffectslot));
+ else
+ ALContext = al_calloc(16, sizeof(ALCcontext)+sizeof(ALlistener));
+ if(!ALContext)
+ {
+ almtx_unlock(&device->BackendLock);
+
+ alcSetError(device, ALC_OUT_OF_MEMORY);
+ ALCdevice_DecRef(device);
+ return NULL;
+ }
+
+ InitRef(&ALContext->ref, 1);
+ ALContext->Listener = (ALlistener*)ALContext->_listener_mem;
+ ALContext->DefaultSlot = NULL;
- ATOMIC_STORE(&device->LastError, ALC_NO_ERROR);
+ ALContext->Voices = NULL;
+ ALContext->VoiceCount = 0;
+ ALContext->MaxVoices = 0;
+ ATOMIC_INIT(&ALContext->ActiveAuxSlots, NULL);
+ ALContext->Device = device;
+ ATOMIC_INIT(&ALContext->next, NULL);
if((err=UpdateDeviceParams(device, attrList)) != ALC_NO_ERROR)
{
- UnlockLists();
+ almtx_unlock(&device->BackendLock);
+
+ al_free(ALContext);
+ ALContext = NULL;
+
alcSetError(device, err);
if(err == ALC_INVALID_DEVICE)
{
V0(device->Backend,lock)();
- aluHandleDisconnect(device);
+ aluHandleDisconnect(device, "Device update failure");
V0(device->Backend,unlock)();
}
ALCdevice_DecRef(device);
return NULL;
}
+ AllocateVoices(ALContext, 256, device->NumAuxSends);
- ALContext = al_calloc(16, sizeof(ALCcontext)+sizeof(ALlistener));
- if(ALContext)
- {
- InitRef(&ALContext->ref, 1);
- ALContext->Listener = (ALlistener*)ALContext->_listener_mem;
-
- VECTOR_INIT(ALContext->ActiveAuxSlots);
-
- ALContext->VoiceCount = 0;
- ALContext->MaxVoices = 256;
- ALContext->Voices = al_calloc(16, ALContext->MaxVoices * sizeof(ALContext->Voices[0]));
- }
- if(!ALContext || !ALContext->Voices)
+ if(DefaultEffect.type != AL_EFFECT_NULL && device->Type == Playback)
{
- if(!ATOMIC_LOAD(&device->ContextList))
+ ALContext->DefaultSlot = (ALeffectslot*)(ALContext->_listener_mem + sizeof(ALlistener));
+ if(InitEffectSlot(ALContext->DefaultSlot) == AL_NO_ERROR)
+ aluInitEffectPanning(ALContext->DefaultSlot);
+ else
{
- V0(device->Backend,stop)();
- device->Flags &= ~DEVICE_RUNNING;
+ ALContext->DefaultSlot = NULL;
+ ERR("Failed to initialize the default effect slot\n");
}
- UnlockLists();
-
- if(ALContext)
- {
- al_free(ALContext->Voices);
- ALContext->Voices = NULL;
+ }
- VECTOR_DEINIT(ALContext->ActiveAuxSlots);
+ ALCdevice_IncRef(ALContext->Device);
+ InitContext(ALContext);
- al_free(ALContext);
- ALContext = NULL;
+ if(ConfigValueFloat(alstr_get_cstr(device->DeviceName), NULL, "volume-adjust", &valf))
+ {
+ if(!isfinite(valf))
+ ERR("volume-adjust must be finite: %f\n", valf);
+ else
+ {
+ ALfloat db = clampf(valf, -24.0f, 24.0f);
+ if(db != valf)
+ WARN("volume-adjust clamped: %f, range: +/-%f\n", valf, 24.0f);
+ ALContext->GainBoost = powf(10.0f, db/20.0f);
+ TRACE("volume-adjust gain: %f\n", ALContext->GainBoost);
}
-
- alcSetError(device, ALC_OUT_OF_MEMORY);
- ALCdevice_DecRef(device);
- return NULL;
}
-
- ALContext->Device = device;
- ALCdevice_IncRef(device);
- InitContext(ALContext);
+ UpdateListenerProps(ALContext);
{
- ALCcontext *head = ATOMIC_LOAD(&device->ContextList);
+ ALCcontext *head = ATOMIC_LOAD_SEQ(&device->ContextList);
do {
- ALContext->next = head;
- } while(!ATOMIC_COMPARE_EXCHANGE_WEAK(ALCcontext*, &device->ContextList, &head, ALContext));
+ ATOMIC_STORE(&ALContext->next, head, almemory_order_relaxed);
+ } while(ATOMIC_COMPARE_EXCHANGE_PTR_WEAK_SEQ(&device->ContextList, &head,
+ ALContext) == 0);
+ }
+ almtx_unlock(&device->BackendLock);
+
+ if(ALContext->DefaultSlot)
+ {
+ if(InitializeEffect(ALContext, ALContext->DefaultSlot, &DefaultEffect) == AL_NO_ERROR)
+ UpdateEffectSlotProps(ALContext->DefaultSlot, ALContext);
+ else
+ ERR("Failed to initialize the default effect\n");
}
- UnlockLists();
ALCdevice_DecRef(device);
@@ -3194,18 +3891,27 @@ ALC_API ALCvoid ALC_APIENTRY alcDestroyContext(ALCcontext *context)
ALCdevice *Device;
LockLists();
- /* alcGetContextsDevice sets an error for invalid contexts */
- Device = alcGetContextsDevice(context);
+ if(!VerifyContext(&context))
+ {
+ UnlockLists();
+ alcSetError(NULL, ALC_INVALID_CONTEXT);
+ return;
+ }
+
+ Device = context->Device;
if(Device)
{
- ReleaseContext(context, Device);
- if(!ATOMIC_LOAD(&Device->ContextList))
+ almtx_lock(&Device->BackendLock);
+ if(!ReleaseContext(context, Device))
{
V0(Device->Backend,stop)();
Device->Flags &= ~DEVICE_RUNNING;
}
+ almtx_unlock(&Device->BackendLock);
}
UnlockLists();
+
+ ALCcontext_DecRef(context);
}
@@ -3216,7 +3922,7 @@ ALC_API ALCvoid ALC_APIENTRY alcDestroyContext(ALCcontext *context)
ALC_API ALCcontext* ALC_APIENTRY alcGetCurrentContext(void)
{
ALCcontext *Context = altss_get(LocalContext);
- if(!Context) Context = ATOMIC_LOAD(&GlobalContext);
+ if(!Context) Context = ATOMIC_LOAD_SEQ(&GlobalContext);
return Context;
}
@@ -3244,7 +3950,7 @@ ALC_API ALCboolean ALC_APIENTRY alcMakeContextCurrent(ALCcontext *context)
return ALC_FALSE;
}
/* context's reference count is already incremented */
- context = ATOMIC_EXCHANGE(ALCcontext*, &GlobalContext, context);
+ context = ATOMIC_EXCHANGE_PTR_SEQ(&GlobalContext, context);
if(context) ALCcontext_DecRef(context);
if((context=altss_get(LocalContext)) != NULL)
@@ -3305,6 +4011,7 @@ ALC_API ALCdevice* ALC_APIENTRY alcGetContextsDevice(ALCcontext *Context)
*/
ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *deviceName)
{
+ ALCbackendFactory *factory;
const ALCchar *fmt;
ALCdevice *device;
ALCenum err;
@@ -3329,7 +4036,7 @@ ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *deviceName)
))
deviceName = NULL;
- device = al_calloc(16, sizeof(ALCdevice)+sizeof(ALeffectslot));
+ device = al_calloc(16, sizeof(ALCdevice));
if(!device)
{
alcSetError(NULL, ALC_OUT_OF_MEMORY);
@@ -3337,69 +4044,40 @@ ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *deviceName)
}
//Validate device
- InitRef(&device->ref, 1);
- device->Connected = ALC_TRUE;
- device->Type = Playback;
- ATOMIC_INIT(&device->LastError, ALC_NO_ERROR);
-
- device->Flags = 0;
- device->Bs2b = NULL;
- VECTOR_INIT(device->Hrtf_List);
- AL_STRING_INIT(device->Hrtf_Name);
- device->Hrtf_Mode = DisabledHrtf;
- AL_STRING_INIT(device->DeviceName);
- device->DryBuffer = NULL;
-
- ATOMIC_INIT(&device->ContextList, NULL);
-
- device->ClockBase = 0;
- device->SamplesDone = 0;
-
- device->MaxNoOfSources = 256;
- device->AuxiliaryEffectSlotMax = 4;
- device->NumAuxSends = MAX_SENDS;
-
- InitUIntMap(&device->BufferMap, ~0);
- InitUIntMap(&device->EffectMap, ~0);
- InitUIntMap(&device->FilterMap, ~0);
+ InitDevice(device, Playback);
//Set output format
device->FmtChans = DevFmtChannelsDefault;
device->FmtType = DevFmtTypeDefault;
device->Frequency = DEFAULT_OUTPUT_RATE;
device->IsHeadphones = AL_FALSE;
- device->NumUpdates = 4;
+ device->AmbiLayout = AmbiLayout_Default;
+ device->AmbiScale = AmbiNorm_Default;
+ device->LimiterState = ALC_TRUE;
+ device->NumUpdates = 3;
device->UpdateSize = 1024;
- if(!PlaybackBackend.getFactory)
- device->Backend = create_backend_wrapper(device, &PlaybackBackend.Funcs,
- ALCbackend_Playback);
- else
- {
- ALCbackendFactory *factory = PlaybackBackend.getFactory();
- device->Backend = V(factory,createBackend)(device, ALCbackend_Playback);
- }
- if(!device->Backend)
- {
- al_free(device);
- alcSetError(NULL, ALC_OUT_OF_MEMORY);
- return NULL;
- }
-
+ device->SourcesMax = 256;
+ device->AuxiliaryEffectSlotMax = 64;
+ device->NumAuxSends = DEFAULT_SENDS;
if(ConfigValueStr(deviceName, NULL, "channels", &fmt))
{
static const struct {
const char name[16];
enum DevFmtChannels chans;
+ ALsizei order;
} chanlist[] = {
- { "mono", DevFmtMono },
- { "stereo", DevFmtStereo },
- { "quad", DevFmtQuad },
- { "surround51", DevFmtX51 },
- { "surround61", DevFmtX61 },
- { "surround71", DevFmtX71 },
- { "surround51rear", DevFmtX51Rear },
+ { "mono", DevFmtMono, 0 },
+ { "stereo", DevFmtStereo, 0 },
+ { "quad", DevFmtQuad, 0 },
+ { "surround51", DevFmtX51, 0 },
+ { "surround61", DevFmtX61, 0 },
+ { "surround71", DevFmtX71, 0 },
+ { "surround51rear", DevFmtX51Rear, 0 },
+ { "ambi1", DevFmtAmbi3D, 1 },
+ { "ambi2", DevFmtAmbi3D, 2 },
+ { "ambi3", DevFmtAmbi3D, 3 },
};
size_t i;
@@ -3408,6 +4086,7 @@ ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *deviceName)
if(strcasecmp(chanlist[i].name, fmt) == 0)
{
device->FmtChans = chanlist[i].chans;
+ device->AmbiOrder = chanlist[i].order;
device->Flags |= DEVICE_CHANNELS_REQUEST;
break;
}
@@ -3460,52 +4139,67 @@ ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *deviceName)
if((CPUCapFlags&(CPU_CAP_SSE|CPU_CAP_NEON)) != 0)
device->UpdateSize = (device->UpdateSize+3)&~3;
- ConfigValueUInt(deviceName, NULL, "sources", &device->MaxNoOfSources);
- if(device->MaxNoOfSources == 0) device->MaxNoOfSources = 256;
+ ConfigValueUInt(deviceName, NULL, "sources", &device->SourcesMax);
+ if(device->SourcesMax == 0) device->SourcesMax = 256;
ConfigValueUInt(deviceName, NULL, "slots", &device->AuxiliaryEffectSlotMax);
- if(device->AuxiliaryEffectSlotMax == 0) device->AuxiliaryEffectSlotMax = 4;
+ if(device->AuxiliaryEffectSlotMax == 0) device->AuxiliaryEffectSlotMax = 64;
+ else device->AuxiliaryEffectSlotMax = minu(device->AuxiliaryEffectSlotMax, INT_MAX);
- ConfigValueUInt(deviceName, NULL, "sends", &device->NumAuxSends);
- if(device->NumAuxSends > MAX_SENDS) device->NumAuxSends = MAX_SENDS;
+ if(ConfigValueInt(deviceName, NULL, "sends", &device->NumAuxSends))
+ device->NumAuxSends = clampi(
+ DEFAULT_SENDS, 0, clampi(device->NumAuxSends, 0, MAX_SENDS)
+ );
device->NumStereoSources = 1;
- device->NumMonoSources = device->MaxNoOfSources - device->NumStereoSources;
+ device->NumMonoSources = device->SourcesMax - device->NumStereoSources;
+
+ factory = PlaybackBackend.getFactory();
+ device->Backend = V(factory,createBackend)(device, ALCbackend_Playback);
+ if(!device->Backend)
+ {
+ FreeDevice(device);
+ alcSetError(NULL, ALC_OUT_OF_MEMORY);
+ return NULL;
+ }
// Find a playback device to open
if((err=V(device->Backend,open)(deviceName)) != ALC_NO_ERROR)
{
- DELETE_OBJ(device->Backend);
- al_free(device);
+ FreeDevice(device);
alcSetError(NULL, err);
return NULL;
}
- if(DefaultEffect.type != AL_EFFECT_NULL)
+ if(ConfigValueStr(alstr_get_cstr(device->DeviceName), NULL, "ambi-format", &fmt))
{
- device->DefaultSlot = (ALeffectslot*)device->_slot_mem;
- if(InitEffectSlot(device->DefaultSlot) != AL_NO_ERROR)
+ if(strcasecmp(fmt, "fuma") == 0)
{
- device->DefaultSlot = NULL;
- ERR("Failed to initialize the default effect slot\n");
+ device->AmbiLayout = AmbiLayout_FuMa;
+ device->AmbiScale = AmbiNorm_FuMa;
}
- else if(InitializeEffect(device, device->DefaultSlot, &DefaultEffect) != AL_NO_ERROR)
+ else if(strcasecmp(fmt, "acn+sn3d") == 0)
{
- ALeffectState *state = device->DefaultSlot->EffectState;
- device->DefaultSlot = NULL;
- DELETE_OBJ(state);
- ERR("Failed to initialize the default effect\n");
+ device->AmbiLayout = AmbiLayout_ACN;
+ device->AmbiScale = AmbiNorm_SN3D;
+ }
+ else if(strcasecmp(fmt, "acn+n3d") == 0)
+ {
+ device->AmbiLayout = AmbiLayout_ACN;
+ device->AmbiScale = AmbiNorm_N3D;
}
+ else
+ ERR("Unsupported ambi-format: %s\n", fmt);
}
{
- ALCdevice *head = ATOMIC_LOAD(&DeviceList);
+ ALCdevice *head = ATOMIC_LOAD_SEQ(&DeviceList);
do {
- device->next = head;
- } while(!ATOMIC_COMPARE_EXCHANGE_WEAK(ALCdevice*, &DeviceList, &head, device));
+ ATOMIC_STORE(&device->next, head, almemory_order_relaxed);
+ } while(!ATOMIC_COMPARE_EXCHANGE_PTR_WEAK_SEQ(&DeviceList, &head, device));
}
- TRACE("Created device %p, \"%s\"\n", device, al_string_get_cstr(device->DeviceName));
+ TRACE("Created device %p, \"%s\"\n", device, alstr_get_cstr(device->DeviceName));
return device;
}
@@ -3515,37 +4209,40 @@ ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *deviceName)
*/
ALC_API ALCboolean ALC_APIENTRY alcCloseDevice(ALCdevice *device)
{
- ALCdevice *list, *origdev, *nextdev;
+ ALCdevice *iter, *origdev, *nextdev;
ALCcontext *ctx;
LockLists();
- list = ATOMIC_LOAD(&DeviceList);
+ iter = ATOMIC_LOAD_SEQ(&DeviceList);
do {
- if(list == device)
+ if(iter == device)
break;
- } while((list=list->next) != NULL);
- if(!list || list->Type == Capture)
+ iter = ATOMIC_LOAD(&iter->next, almemory_order_relaxed);
+ } while(iter != NULL);
+ if(!iter || iter->Type == Capture)
{
- alcSetError(list, ALC_INVALID_DEVICE);
+ alcSetError(iter, ALC_INVALID_DEVICE);
UnlockLists();
return ALC_FALSE;
}
+ almtx_lock(&device->BackendLock);
origdev = device;
- nextdev = device->next;
- if(!ATOMIC_COMPARE_EXCHANGE_STRONG(ALCdevice*, &DeviceList, &origdev, nextdev))
+ nextdev = ATOMIC_LOAD(&device->next, almemory_order_relaxed);
+ if(!ATOMIC_COMPARE_EXCHANGE_PTR_STRONG_SEQ(&DeviceList, &origdev, nextdev))
{
+ ALCdevice *list;
do {
list = origdev;
origdev = device;
- } while(!COMPARE_EXCHANGE(&list->next, &origdev, nextdev));
+ } while(!ATOMIC_COMPARE_EXCHANGE_PTR_STRONG_SEQ(&list->next, &origdev, nextdev));
}
UnlockLists();
- ctx = ATOMIC_LOAD(&device->ContextList);
+ ctx = ATOMIC_LOAD_SEQ(&device->ContextList);
while(ctx != NULL)
{
- ALCcontext *next = ctx->next;
+ ALCcontext *next = ATOMIC_LOAD(&ctx->next, almemory_order_relaxed);
WARN("Releasing context %p\n", ctx);
ReleaseContext(ctx, device);
ctx = next;
@@ -3553,6 +4250,7 @@ ALC_API ALCboolean ALC_APIENTRY alcCloseDevice(ALCdevice *device)
if((device->Flags&DEVICE_RUNNING))
V0(device->Backend,stop)();
device->Flags &= ~DEVICE_RUNNING;
+ almtx_unlock(&device->BackendLock);
ALCdevice_DecRef(device);
@@ -3565,6 +4263,7 @@ ALC_API ALCboolean ALC_APIENTRY alcCloseDevice(ALCdevice *device)
************************************************/
ALC_API ALCdevice* ALC_APIENTRY alcCaptureOpenDevice(const ALCchar *deviceName, ALCuint frequency, ALCenum format, ALCsizei samples)
{
+ ALCbackendFactory *factory;
ALCdevice *device = NULL;
ALCenum err;
@@ -3593,96 +4292,93 @@ ALC_API ALCdevice* ALC_APIENTRY alcCaptureOpenDevice(const ALCchar *deviceName,
}
//Validate device
- InitRef(&device->ref, 1);
- device->Connected = ALC_TRUE;
- device->Type = Capture;
-
- VECTOR_INIT(device->Hrtf_List);
- AL_STRING_INIT(device->Hrtf_Name);
-
- AL_STRING_INIT(device->DeviceName);
- device->DryBuffer = NULL;
+ InitDevice(device, Capture);
- InitUIntMap(&device->BufferMap, ~0);
- InitUIntMap(&device->EffectMap, ~0);
- InitUIntMap(&device->FilterMap, ~0);
-
- if(!CaptureBackend.getFactory)
- device->Backend = create_backend_wrapper(device, &CaptureBackend.Funcs,
- ALCbackend_Capture);
- else
- {
- ALCbackendFactory *factory = CaptureBackend.getFactory();
- device->Backend = V(factory,createBackend)(device, ALCbackend_Capture);
- }
- if(!device->Backend)
- {
- al_free(device);
- alcSetError(NULL, ALC_OUT_OF_MEMORY);
- return NULL;
- }
-
- device->Flags |= DEVICE_FREQUENCY_REQUEST;
device->Frequency = frequency;
+ device->Flags |= DEVICE_FREQUENCY_REQUEST;
- device->Flags |= DEVICE_CHANNELS_REQUEST | DEVICE_SAMPLE_TYPE_REQUEST;
if(DecomposeDevFormat(format, &device->FmtChans, &device->FmtType) == AL_FALSE)
{
- al_free(device);
+ FreeDevice(device);
alcSetError(NULL, ALC_INVALID_ENUM);
return NULL;
}
+ device->Flags |= DEVICE_CHANNELS_REQUEST | DEVICE_SAMPLE_TYPE_REQUEST;
device->IsHeadphones = AL_FALSE;
+ device->AmbiOrder = 0;
+ device->AmbiLayout = AmbiLayout_Default;
+ device->AmbiScale = AmbiNorm_Default;
device->UpdateSize = samples;
device->NumUpdates = 1;
+ factory = CaptureBackend.getFactory();
+ device->Backend = V(factory,createBackend)(device, ALCbackend_Capture);
+ if(!device->Backend)
+ {
+ FreeDevice(device);
+ alcSetError(NULL, ALC_OUT_OF_MEMORY);
+ return NULL;
+ }
+
+ TRACE("Capture format: %s, %s, %uhz, %u update size x%d\n",
+ DevFmtChannelsString(device->FmtChans), DevFmtTypeString(device->FmtType),
+ device->Frequency, device->UpdateSize, device->NumUpdates
+ );
if((err=V(device->Backend,open)(deviceName)) != ALC_NO_ERROR)
{
- al_free(device);
+ FreeDevice(device);
alcSetError(NULL, err);
return NULL;
}
{
- ALCdevice *head = ATOMIC_LOAD(&DeviceList);
+ ALCdevice *head = ATOMIC_LOAD_SEQ(&DeviceList);
do {
- device->next = head;
- } while(!ATOMIC_COMPARE_EXCHANGE_WEAK(ALCdevice*, &DeviceList, &head, device));
+ ATOMIC_STORE(&device->next, head, almemory_order_relaxed);
+ } while(!ATOMIC_COMPARE_EXCHANGE_PTR_WEAK_SEQ(&DeviceList, &head, device));
}
- TRACE("Created device %p, \"%s\"\n", device, al_string_get_cstr(device->DeviceName));
+ TRACE("Created device %p, \"%s\"\n", device, alstr_get_cstr(device->DeviceName));
return device;
}
ALC_API ALCboolean ALC_APIENTRY alcCaptureCloseDevice(ALCdevice *device)
{
- ALCdevice *list, *next, *nextdev;
+ ALCdevice *iter, *origdev, *nextdev;
LockLists();
- list = ATOMIC_LOAD(&DeviceList);
+ iter = ATOMIC_LOAD_SEQ(&DeviceList);
do {
- if(list == device)
+ if(iter == device)
break;
- } while((list=list->next) != NULL);
- if(!list || list->Type != Capture)
+ iter = ATOMIC_LOAD(&iter->next, almemory_order_relaxed);
+ } while(iter != NULL);
+ if(!iter || iter->Type != Capture)
{
- alcSetError(list, ALC_INVALID_DEVICE);
+ alcSetError(iter, ALC_INVALID_DEVICE);
UnlockLists();
return ALC_FALSE;
}
- next = device;
- nextdev = device->next;
- if(!ATOMIC_COMPARE_EXCHANGE_STRONG(ALCdevice*, &DeviceList, &next, nextdev))
+ origdev = device;
+ nextdev = ATOMIC_LOAD(&device->next, almemory_order_relaxed);
+ if(!ATOMIC_COMPARE_EXCHANGE_PTR_STRONG_SEQ(&DeviceList, &origdev, nextdev))
{
+ ALCdevice *list;
do {
- list = next;
- next = device;
- } while(!COMPARE_EXCHANGE(&list->next, &next, nextdev));
+ list = origdev;
+ origdev = device;
+ } while(!ATOMIC_COMPARE_EXCHANGE_PTR_STRONG_SEQ(&list->next, &origdev, nextdev));
}
UnlockLists();
+ almtx_lock(&device->BackendLock);
+ if((device->Flags&DEVICE_RUNNING))
+ V0(device->Backend,stop)();
+ device->Flags &= ~DEVICE_RUNNING;
+ almtx_unlock(&device->BackendLock);
+
ALCdevice_DecRef(device);
return ALC_TRUE;
@@ -3694,8 +4390,8 @@ ALC_API void ALC_APIENTRY alcCaptureStart(ALCdevice *device)
alcSetError(device, ALC_INVALID_DEVICE);
else
{
- V0(device->Backend,lock)();
- if(!device->Connected)
+ almtx_lock(&device->BackendLock);
+ if(!ATOMIC_LOAD(&device->Connected, almemory_order_acquire))
alcSetError(device, ALC_INVALID_DEVICE);
else if(!(device->Flags&DEVICE_RUNNING))
{
@@ -3703,11 +4399,11 @@ ALC_API void ALC_APIENTRY alcCaptureStart(ALCdevice *device)
device->Flags |= DEVICE_RUNNING;
else
{
- aluHandleDisconnect(device);
+ aluHandleDisconnect(device, "Device start failure");
alcSetError(device, ALC_INVALID_DEVICE);
}
}
- V0(device->Backend,unlock)();
+ almtx_unlock(&device->BackendLock);
}
if(device) ALCdevice_DecRef(device);
@@ -3719,11 +4415,11 @@ ALC_API void ALC_APIENTRY alcCaptureStop(ALCdevice *device)
alcSetError(device, ALC_INVALID_DEVICE);
else
{
- V0(device->Backend,lock)();
+ almtx_lock(&device->BackendLock);
if((device->Flags&DEVICE_RUNNING))
V0(device->Backend,stop)();
device->Flags &= ~DEVICE_RUNNING;
- V0(device->Backend,unlock)();
+ almtx_unlock(&device->BackendLock);
}
if(device) ALCdevice_DecRef(device);
@@ -3737,10 +4433,10 @@ ALC_API void ALC_APIENTRY alcCaptureSamples(ALCdevice *device, ALCvoid *buffer,
{
ALCenum err = ALC_INVALID_VALUE;
- V0(device->Backend,lock)();
+ almtx_lock(&device->BackendLock);
if(samples >= 0 && V0(device->Backend,availableSamples)() >= (ALCuint)samples)
err = V(device->Backend,captureSamples)(buffer, samples);
- V0(device->Backend,unlock)();
+ almtx_unlock(&device->BackendLock);
if(err != ALC_NO_ERROR)
alcSetError(device, err);
@@ -3779,40 +4475,11 @@ ALC_API ALCdevice* ALC_APIENTRY alcLoopbackOpenDeviceSOFT(const ALCchar *deviceN
}
//Validate device
- InitRef(&device->ref, 1);
- device->Connected = ALC_TRUE;
- device->Type = Loopback;
- ATOMIC_INIT(&device->LastError, ALC_NO_ERROR);
-
- device->Flags = 0;
- VECTOR_INIT(device->Hrtf_List);
- AL_STRING_INIT(device->Hrtf_Name);
- device->Bs2b = NULL;
- device->Hrtf_Mode = DisabledHrtf;
- AL_STRING_INIT(device->DeviceName);
- device->DryBuffer = NULL;
-
- ATOMIC_INIT(&device->ContextList, NULL);
-
- device->ClockBase = 0;
- device->SamplesDone = 0;
-
- device->MaxNoOfSources = 256;
- device->AuxiliaryEffectSlotMax = 4;
- device->NumAuxSends = MAX_SENDS;
+ InitDevice(device, Loopback);
- InitUIntMap(&device->BufferMap, ~0);
- InitUIntMap(&device->EffectMap, ~0);
- InitUIntMap(&device->FilterMap, ~0);
-
- factory = ALCloopbackFactory_getFactory();
- device->Backend = V(factory,createBackend)(device, ALCbackend_Loopback);
- if(!device->Backend)
- {
- al_free(device);
- alcSetError(NULL, ALC_OUT_OF_MEMORY);
- return NULL;
- }
+ device->SourcesMax = 256;
+ device->AuxiliaryEffectSlotMax = 64;
+ device->NumAuxSends = DEFAULT_SENDS;
//Set output format
device->NumUpdates = 0;
@@ -3822,27 +4489,41 @@ ALC_API ALCdevice* ALC_APIENTRY alcLoopbackOpenDeviceSOFT(const ALCchar *deviceN
device->FmtChans = DevFmtChannelsDefault;
device->FmtType = DevFmtTypeDefault;
device->IsHeadphones = AL_FALSE;
+ device->AmbiLayout = AmbiLayout_Default;
+ device->AmbiScale = AmbiNorm_Default;
- ConfigValueUInt(NULL, NULL, "sources", &device->MaxNoOfSources);
- if(device->MaxNoOfSources == 0) device->MaxNoOfSources = 256;
+ ConfigValueUInt(NULL, NULL, "sources", &device->SourcesMax);
+ if(device->SourcesMax == 0) device->SourcesMax = 256;
ConfigValueUInt(NULL, NULL, "slots", &device->AuxiliaryEffectSlotMax);
- if(device->AuxiliaryEffectSlotMax == 0) device->AuxiliaryEffectSlotMax = 4;
+ if(device->AuxiliaryEffectSlotMax == 0) device->AuxiliaryEffectSlotMax = 64;
+ else device->AuxiliaryEffectSlotMax = minu(device->AuxiliaryEffectSlotMax, INT_MAX);
- ConfigValueUInt(NULL, NULL, "sends", &device->NumAuxSends);
- if(device->NumAuxSends > MAX_SENDS) device->NumAuxSends = MAX_SENDS;
+ if(ConfigValueInt(NULL, NULL, "sends", &device->NumAuxSends))
+ device->NumAuxSends = clampi(
+ DEFAULT_SENDS, 0, clampi(device->NumAuxSends, 0, MAX_SENDS)
+ );
device->NumStereoSources = 1;
- device->NumMonoSources = device->MaxNoOfSources - device->NumStereoSources;
+ device->NumMonoSources = device->SourcesMax - device->NumStereoSources;
+
+ factory = ALCloopbackFactory_getFactory();
+ device->Backend = V(factory,createBackend)(device, ALCbackend_Loopback);
+ if(!device->Backend)
+ {
+ al_free(device);
+ alcSetError(NULL, ALC_OUT_OF_MEMORY);
+ return NULL;
+ }
// Open the "backend"
V(device->Backend,open)("Loopback");
{
- ALCdevice *head = ATOMIC_LOAD(&DeviceList);
+ ALCdevice *head = ATOMIC_LOAD_SEQ(&DeviceList);
do {
- device->next = head;
- } while(!ATOMIC_COMPARE_EXCHANGE_WEAK(ALCdevice*, &DeviceList, &head, device));
+ ATOMIC_STORE(&device->next, head, almemory_order_relaxed);
+ } while(!ATOMIC_COMPARE_EXCHANGE_PTR_WEAK_SEQ(&DeviceList, &head, device));
}
TRACE("Created device %p\n", device);
@@ -3863,9 +4544,7 @@ ALC_API ALCboolean ALC_APIENTRY alcIsRenderFormatSupportedSOFT(ALCdevice *device
alcSetError(device, ALC_INVALID_VALUE);
else
{
- if(IsValidALCType(type) && BytesFromDevFmt(type) > 0 &&
- IsValidALCChannels(channels) && ChannelsFromDevFmt(channels) > 0 &&
- freq >= MIN_OUTPUT_RATE)
+ if(IsValidALCType(type) && IsValidALCChannels(channels) && freq >= MIN_OUTPUT_RATE)
ret = ALC_TRUE;
}
if(device) ALCdevice_DecRef(device);
@@ -3885,7 +4564,11 @@ FORCE_ALIGN ALC_API void ALC_APIENTRY alcRenderSamplesSOFT(ALCdevice *device, AL
else if(samples < 0 || (samples > 0 && buffer == NULL))
alcSetError(device, ALC_INVALID_VALUE);
else
+ {
+ V0(device->Backend,lock)();
aluMixData(device, buffer, samples);
+ V0(device->Backend,unlock)();
+ }
if(device) ALCdevice_DecRef(device);
}
@@ -3904,12 +4587,12 @@ ALC_API void ALC_APIENTRY alcDevicePauseSOFT(ALCdevice *device)
alcSetError(device, ALC_INVALID_DEVICE);
else
{
- LockLists();
+ almtx_lock(&device->BackendLock);
if((device->Flags&DEVICE_RUNNING))
V0(device->Backend,stop)();
device->Flags &= ~DEVICE_RUNNING;
device->Flags |= DEVICE_PAUSED;
- UnlockLists();
+ almtx_unlock(&device->BackendLock);
}
if(device) ALCdevice_DecRef(device);
}
@@ -3924,24 +4607,24 @@ ALC_API void ALC_APIENTRY alcDeviceResumeSOFT(ALCdevice *device)
alcSetError(device, ALC_INVALID_DEVICE);
else
{
- LockLists();
+ almtx_lock(&device->BackendLock);
if((device->Flags&DEVICE_PAUSED))
{
device->Flags &= ~DEVICE_PAUSED;
- if(ATOMIC_LOAD(&device->ContextList) != NULL)
+ if(ATOMIC_LOAD_SEQ(&device->ContextList) != NULL)
{
if(V0(device->Backend,start)() != ALC_FALSE)
device->Flags |= DEVICE_RUNNING;
else
{
- alcSetError(device, ALC_INVALID_DEVICE);
V0(device->Backend,lock)();
- aluHandleDisconnect(device);
+ aluHandleDisconnect(device, "Device start failure");
V0(device->Backend,unlock)();
+ alcSetError(device, ALC_INVALID_DEVICE);
}
}
}
- UnlockLists();
+ almtx_unlock(&device->BackendLock);
}
if(device) ALCdevice_DecRef(device);
}
@@ -3964,8 +4647,8 @@ ALC_API const ALCchar* ALC_APIENTRY alcGetStringiSOFT(ALCdevice *device, ALCenum
else switch(paramName)
{
case ALC_HRTF_SPECIFIER_SOFT:
- if(index >= 0 && (size_t)index < VECTOR_SIZE(device->Hrtf_List))
- str = al_string_get_cstr(VECTOR_ELEM(device->Hrtf_List, index).name);
+ if(index >= 0 && (size_t)index < VECTOR_SIZE(device->HrtfList))
+ str = alstr_get_cstr(VECTOR_ELEM(device->HrtfList, index).name);
else
alcSetError(device, ALC_INVALID_VALUE);
break;
@@ -3988,28 +4671,32 @@ ALC_API ALCboolean ALC_APIENTRY alcResetDeviceSOFT(ALCdevice *device, const ALCi
ALCenum err;
LockLists();
- if(!VerifyDevice(&device) || device->Type == Capture || !device->Connected)
+ if(!VerifyDevice(&device) || device->Type == Capture ||
+ !ATOMIC_LOAD(&device->Connected, almemory_order_relaxed))
{
UnlockLists();
alcSetError(device, ALC_INVALID_DEVICE);
if(device) ALCdevice_DecRef(device);
return ALC_FALSE;
}
+ almtx_lock(&device->BackendLock);
+ UnlockLists();
+
+ err = UpdateDeviceParams(device, attribs);
+ almtx_unlock(&device->BackendLock);
- if((err=UpdateDeviceParams(device, attribs)) != ALC_NO_ERROR)
+ if(err != ALC_NO_ERROR)
{
- UnlockLists();
alcSetError(device, err);
if(err == ALC_INVALID_DEVICE)
{
V0(device->Backend,lock)();
- aluHandleDisconnect(device);
+ aluHandleDisconnect(device, "Device start failure");
V0(device->Backend,unlock)();
}
ALCdevice_DecRef(device);
return ALC_FALSE;
}
- UnlockLists();
ALCdevice_DecRef(device);
return ALC_TRUE;
diff --git a/Alc/ALu.c b/Alc/ALu.c
index 91c2aa7f..03abb116 100644
--- a/Alc/ALu.c
+++ b/Alc/ALu.c
@@ -34,25 +34,21 @@
#include "alu.h"
#include "bs2b.h"
#include "hrtf.h"
+#include "mastering.h"
+#include "uhjfilter.h"
+#include "bformatdec.h"
#include "static_assert.h"
+#include "ringbuffer.h"
+#include "filters/splitter.h"
-#include "mixer_defs.h"
+#include "mixer/defs.h"
+#include "fpu_modes.h"
+#include "cpu_caps.h"
+#include "bsinc_inc.h"
#include "backends/base.h"
-struct ChanMap {
- enum Channel channel;
- ALfloat angle;
- ALfloat elevation;
-};
-
-/* Cone scalar */
-ALfloat ConeScale = 1.0f;
-
-/* Localized Z scalar for mono sources */
-ALfloat ZScale = 1.0f;
-
extern inline ALfloat minf(ALfloat a, ALfloat b);
extern inline ALfloat maxf(ALfloat a, ALfloat b);
extern inline ALfloat clampf(ALfloat val, ALfloat min, ALfloat max);
@@ -77,9 +73,12 @@ extern inline ALuint64 minu64(ALuint64 a, ALuint64 b);
extern inline ALuint64 maxu64(ALuint64 a, ALuint64 b);
extern inline ALuint64 clampu64(ALuint64 val, ALuint64 min, ALuint64 max);
+extern inline size_t minz(size_t a, size_t b);
+extern inline size_t maxz(size_t a, size_t b);
+extern inline size_t clampz(size_t val, size_t min, size_t max);
+
extern inline ALfloat lerp(ALfloat val1, ALfloat val2, ALfloat mu);
-extern inline ALfloat resample_fir4(ALfloat val0, ALfloat val1, ALfloat val2, ALfloat val3, ALuint frac);
-extern inline ALfloat resample_fir8(ALfloat val0, ALfloat val1, ALfloat val2, ALfloat val3, ALfloat val4, ALfloat val5, ALfloat val6, ALfloat val7, ALuint frac);
+extern inline ALfloat cubic(ALfloat val1, ALfloat val2, ALfloat val3, ALfloat val4, ALfloat mu);
extern inline void aluVectorSet(aluVector *restrict vector, ALfloat x, ALfloat y, ALfloat z, ALfloat w);
@@ -91,50 +90,69 @@ extern inline void aluMatrixfSet(aluMatrixf *matrix,
ALfloat m20, ALfloat m21, ALfloat m22, ALfloat m23,
ALfloat m30, ALfloat m31, ALfloat m32, ALfloat m33);
-extern inline void aluMatrixdSetRow(aluMatrixd *matrix, ALuint row,
- ALdouble m0, ALdouble m1, ALdouble m2, ALdouble m3);
-extern inline void aluMatrixdSet(aluMatrixd *matrix,
- ALdouble m00, ALdouble m01, ALdouble m02, ALdouble m03,
- ALdouble m10, ALdouble m11, ALdouble m12, ALdouble m13,
- ALdouble m20, ALdouble m21, ALdouble m22, ALdouble m23,
- ALdouble m30, ALdouble m31, ALdouble m32, ALdouble m33);
+/* Cone scalar */
+ALfloat ConeScale = 1.0f;
+
+/* Localized Z scalar for mono sources */
+ALfloat ZScale = 1.0f;
+
+/* Force default speed of sound for distance-related reverb decay. */
+ALboolean OverrideReverbSpeedOfSound = AL_FALSE;
+
+const aluMatrixf IdentityMatrixf = {{
+ { 1.0f, 0.0f, 0.0f, 0.0f },
+ { 0.0f, 1.0f, 0.0f, 0.0f },
+ { 0.0f, 0.0f, 1.0f, 0.0f },
+ { 0.0f, 0.0f, 0.0f, 1.0f },
+}};
-/* NOTE: HRTF is set up a bit special in the device. By default, the device's
- * DryBuffer, NumChannels, ChannelName, and Channel fields correspond to the
- * output mixing format, and the DryBuffer is then converted and written to the
- * backend's audio buffer.
- *
- * With HRTF, these fields correspond to a virtual format (typically B-Format),
- * and the actual output is stored in DryBuffer[NumChannels] for the left
- * channel and DryBuffer[NumChannels+1] for the right. As a final output step,
- * the virtual channels will have HRTF applied and written to the actual
- * output. Things like effects and B-Format decoding will want to write to the
- * virtual channels so that they can be mixed with HRTF in full 3D.
- *
- * Sources that get mixed using HRTF directly (or that want to skip HRTF
- * completely) will need to offset the output buffer so that they skip the
- * virtual output and write to the actual output channels. This is the reason
- * you'll see
- *
- * voice->Direct.OutBuffer += voice->Direct.OutChannels;
- * voice->Direct.OutChannels = 2;
- *
- * at various points in the code where HRTF is explicitly used or bypassed.
- */
-static inline HrtfMixerFunc SelectHrtfMixer(void)
+static void ClearArray(ALfloat f[MAX_OUTPUT_CHANNELS])
+{
+ size_t i;
+ for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
+ f[i] = 0.0f;
+}
+
+struct ChanMap {
+ enum Channel channel;
+ ALfloat angle;
+ ALfloat elevation;
+};
+
+static HrtfDirectMixerFunc MixDirectHrtf = MixDirectHrtf_C;
+
+
+void DeinitVoice(ALvoice *voice)
+{
+ al_free(ATOMIC_EXCHANGE_PTR_SEQ(&voice->Update, NULL));
+}
+
+
+static inline HrtfDirectMixerFunc SelectHrtfMixer(void)
{
-#ifdef HAVE_SSE
- if((CPUCapFlags&CPU_CAP_SSE))
- return MixHrtf_SSE;
-#endif
#ifdef HAVE_NEON
if((CPUCapFlags&CPU_CAP_NEON))
- return MixHrtf_Neon;
+ return MixDirectHrtf_Neon;
+#endif
+#ifdef HAVE_SSE
+ if((CPUCapFlags&CPU_CAP_SSE))
+ return MixDirectHrtf_SSE;
#endif
- return MixHrtf_C;
+ return MixDirectHrtf_C;
+}
+
+
+/* This RNG method was created based on the math found in opusdec. It's quick,
+ * and starting with a seed value of 22222, is suitable for generating
+ * whitenoise.
+ */
+static inline ALuint dither_rng(ALuint *seed)
+{
+ *seed = (*seed * 96314165) + 907633515;
+ return *seed;
}
@@ -150,1337 +168,1646 @@ static inline ALfloat aluDotproduct(const aluVector *vec1, const aluVector *vec2
return vec1->v[0]*vec2->v[0] + vec1->v[1]*vec2->v[1] + vec1->v[2]*vec2->v[2];
}
-static inline ALfloat aluNormalize(ALfloat *vec)
+static ALfloat aluNormalize(ALfloat *vec)
{
ALfloat length = sqrtf(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]);
- if(length > 0.0f)
+ if(length > FLT_EPSILON)
{
ALfloat inv_length = 1.0f/length;
vec[0] *= inv_length;
vec[1] *= inv_length;
vec[2] *= inv_length;
+ return length;
}
- return length;
+ vec[0] = vec[1] = vec[2] = 0.0f;
+ return 0.0f;
}
+static void aluMatrixfFloat3(ALfloat *vec, ALfloat w, const aluMatrixf *mtx)
+{
+ ALfloat v[4] = { vec[0], vec[1], vec[2], w };
-static inline void aluCrossproductd(const ALdouble *inVector1, const ALdouble *inVector2, ALdouble *outVector)
+ vec[0] = v[0]*mtx->m[0][0] + v[1]*mtx->m[1][0] + v[2]*mtx->m[2][0] + v[3]*mtx->m[3][0];
+ vec[1] = v[0]*mtx->m[0][1] + v[1]*mtx->m[1][1] + v[2]*mtx->m[2][1] + v[3]*mtx->m[3][1];
+ vec[2] = v[0]*mtx->m[0][2] + v[1]*mtx->m[1][2] + v[2]*mtx->m[2][2] + v[3]*mtx->m[3][2];
+}
+
+static aluVector aluMatrixfVector(const aluMatrixf *mtx, const aluVector *vec)
{
- outVector[0] = inVector1[1]*inVector2[2] - inVector1[2]*inVector2[1];
- outVector[1] = inVector1[2]*inVector2[0] - inVector1[0]*inVector2[2];
- outVector[2] = inVector1[0]*inVector2[1] - inVector1[1]*inVector2[0];
+ aluVector v;
+ v.v[0] = vec->v[0]*mtx->m[0][0] + vec->v[1]*mtx->m[1][0] + vec->v[2]*mtx->m[2][0] + vec->v[3]*mtx->m[3][0];
+ v.v[1] = vec->v[0]*mtx->m[0][1] + vec->v[1]*mtx->m[1][1] + vec->v[2]*mtx->m[2][1] + vec->v[3]*mtx->m[3][1];
+ v.v[2] = vec->v[0]*mtx->m[0][2] + vec->v[1]*mtx->m[1][2] + vec->v[2]*mtx->m[2][2] + vec->v[3]*mtx->m[3][2];
+ v.v[3] = vec->v[0]*mtx->m[0][3] + vec->v[1]*mtx->m[1][3] + vec->v[2]*mtx->m[2][3] + vec->v[3]*mtx->m[3][3];
+ return v;
+}
+
+
+void aluInit(void)
+{
+ MixDirectHrtf = SelectHrtfMixer();
}
-static inline ALdouble aluNormalized(ALdouble *vec)
+
+static void SendSourceStoppedEvent(ALCcontext *context, ALuint id)
{
- ALdouble length = sqrt(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]);
- if(length > 0.0)
+ AsyncEvent evt = ASYNC_EVENT(EventType_SourceStateChange);
+ ALbitfieldSOFT enabledevt;
+ size_t strpos;
+ ALuint scale;
+
+ enabledevt = ATOMIC_LOAD(&context->EnabledEvts, almemory_order_acquire);
+ if(!(enabledevt&EventType_SourceStateChange)) return;
+
+ evt.u.user.type = AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT;
+ evt.u.user.id = id;
+ evt.u.user.param = AL_STOPPED;
+
+ /* Normally snprintf would be used, but this is called from the mixer and
+ * that function's not real-time safe, so we have to construct it manually.
+ */
+ strcpy(evt.u.user.msg, "Source ID "); strpos = 10;
+ scale = 1000000000;
+ while(scale > 0 && scale > id)
+ scale /= 10;
+ while(scale > 0)
{
- ALdouble inv_length = 1.0/length;
- vec[0] *= inv_length;
- vec[1] *= inv_length;
- vec[2] *= inv_length;
+ evt.u.user.msg[strpos++] = '0' + ((id/scale)%10);
+ scale /= 10;
}
- return length;
+ strcpy(evt.u.user.msg+strpos, " state changed to AL_STOPPED");
+
+ if(ll_ringbuffer_write(context->AsyncEvents, (const char*)&evt, 1) == 1)
+ alsem_post(&context->EventSem);
}
-static inline ALvoid aluMatrixdFloat3(ALfloat *vec, ALfloat w, const aluMatrixd *mtx)
+
+static void ProcessHrtf(ALCdevice *device, ALsizei SamplesToDo)
{
- ALdouble v[4] = { vec[0], vec[1], vec[2], w };
+ DirectHrtfState *state;
+ int lidx, ridx;
+ ALsizei c;
+
+ if(device->AmbiUp)
+ ambiup_process(device->AmbiUp,
+ device->Dry.Buffer, device->Dry.NumChannels, device->FOAOut.Buffer,
+ SamplesToDo
+ );
- vec[0] = (ALfloat)(v[0]*mtx->m[0][0] + v[1]*mtx->m[1][0] + v[2]*mtx->m[2][0] + v[3]*mtx->m[3][0]);
- vec[1] = (ALfloat)(v[0]*mtx->m[0][1] + v[1]*mtx->m[1][1] + v[2]*mtx->m[2][1] + v[3]*mtx->m[3][1]);
- vec[2] = (ALfloat)(v[0]*mtx->m[0][2] + v[1]*mtx->m[1][2] + v[2]*mtx->m[2][2] + v[3]*mtx->m[3][2]);
+ lidx = GetChannelIdxByName(&device->RealOut, FrontLeft);
+ ridx = GetChannelIdxByName(&device->RealOut, FrontRight);
+ assert(lidx != -1 && ridx != -1);
+
+ state = device->Hrtf;
+ for(c = 0;c < device->Dry.NumChannels;c++)
+ {
+ MixDirectHrtf(device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
+ device->Dry.Buffer[c], state->Offset, state->IrSize,
+ state->Chan[c].Coeffs, state->Chan[c].Values, SamplesToDo
+ );
+ }
+ state->Offset += SamplesToDo;
}
-static inline ALvoid aluMatrixdDouble3(ALdouble *vec, ALdouble w, const aluMatrixd *mtx)
+static void ProcessAmbiDec(ALCdevice *device, ALsizei SamplesToDo)
{
- ALdouble v[4] = { vec[0], vec[1], vec[2], w };
+ if(device->Dry.Buffer != device->FOAOut.Buffer)
+ bformatdec_upSample(device->AmbiDecoder,
+ device->Dry.Buffer, device->FOAOut.Buffer, device->FOAOut.NumChannels,
+ SamplesToDo
+ );
+ bformatdec_process(device->AmbiDecoder,
+ device->RealOut.Buffer, device->RealOut.NumChannels, device->Dry.Buffer,
+ SamplesToDo
+ );
+}
- vec[0] = v[0]*mtx->m[0][0] + v[1]*mtx->m[1][0] + v[2]*mtx->m[2][0] + v[3]*mtx->m[3][0];
- vec[1] = v[0]*mtx->m[0][1] + v[1]*mtx->m[1][1] + v[2]*mtx->m[2][1] + v[3]*mtx->m[3][1];
- vec[2] = v[0]*mtx->m[0][2] + v[1]*mtx->m[1][2] + v[2]*mtx->m[2][2] + v[3]*mtx->m[3][2];
+static void ProcessAmbiUp(ALCdevice *device, ALsizei SamplesToDo)
+{
+ ambiup_process(device->AmbiUp,
+ device->RealOut.Buffer, device->RealOut.NumChannels, device->FOAOut.Buffer,
+ SamplesToDo
+ );
}
-static inline aluVector aluMatrixdVector(const aluMatrixd *mtx, const aluVector *vec)
+static void ProcessUhj(ALCdevice *device, ALsizei SamplesToDo)
{
- aluVector v;
- v.v[0] = (ALfloat)(vec->v[0]*mtx->m[0][0] + vec->v[1]*mtx->m[1][0] + vec->v[2]*mtx->m[2][0] + vec->v[3]*mtx->m[3][0]);
- v.v[1] = (ALfloat)(vec->v[0]*mtx->m[0][1] + vec->v[1]*mtx->m[1][1] + vec->v[2]*mtx->m[2][1] + vec->v[3]*mtx->m[3][1]);
- v.v[2] = (ALfloat)(vec->v[0]*mtx->m[0][2] + vec->v[1]*mtx->m[1][2] + vec->v[2]*mtx->m[2][2] + vec->v[3]*mtx->m[3][2]);
- v.v[3] = (ALfloat)(vec->v[0]*mtx->m[0][3] + vec->v[1]*mtx->m[1][3] + vec->v[2]*mtx->m[2][3] + vec->v[3]*mtx->m[3][3]);
- return v;
+ int lidx = GetChannelIdxByName(&device->RealOut, FrontLeft);
+ int ridx = GetChannelIdxByName(&device->RealOut, FrontRight);
+ assert(lidx != -1 && ridx != -1);
+
+ /* Encode to stereo-compatible 2-channel UHJ output. */
+ EncodeUhj2(device->Uhj_Encoder,
+ device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
+ device->Dry.Buffer, SamplesToDo
+ );
+}
+
+static void ProcessBs2b(ALCdevice *device, ALsizei SamplesToDo)
+{
+ int lidx = GetChannelIdxByName(&device->RealOut, FrontLeft);
+ int ridx = GetChannelIdxByName(&device->RealOut, FrontRight);
+ assert(lidx != -1 && ridx != -1);
+
+ /* Apply binaural/crossfeed filter */
+ bs2b_cross_feed(device->Bs2b, device->RealOut.Buffer[lidx],
+ device->RealOut.Buffer[ridx], SamplesToDo);
}
+void aluSelectPostProcess(ALCdevice *device)
+{
+ if(device->HrtfHandle)
+ device->PostProcess = ProcessHrtf;
+ else if(device->AmbiDecoder)
+ device->PostProcess = ProcessAmbiDec;
+ else if(device->AmbiUp)
+ device->PostProcess = ProcessAmbiUp;
+ else if(device->Uhj_Encoder)
+ device->PostProcess = ProcessUhj;
+ else if(device->Bs2b)
+ device->PostProcess = ProcessBs2b;
+ else
+ device->PostProcess = NULL;
+}
-/* Prepares the interpolator for a given rate (determined by increment). A
- * result of AL_FALSE indicates that the filter output will completely cut
- * the input signal.
+
+/* Prepares the interpolator for a given rate (determined by increment).
*
* With a bit of work, and a trade of memory for CPU cost, this could be
* modified for use with an interpolated increment for buttery-smooth pitch
* changes.
*/
-static ALboolean BsincPrepare(const ALuint increment, BsincState *state)
+void BsincPrepare(const ALuint increment, BsincState *state, const BSincTable *table)
{
- static const ALfloat scaleBase = 1.510578918e-01f, scaleRange = 1.177936623e+00f;
- static const ALuint m[BSINC_SCALE_COUNT] = { 24, 24, 24, 24, 24, 24, 24, 20, 20, 20, 16, 16, 16, 12, 12, 12 };
- static const ALuint to[4][BSINC_SCALE_COUNT] =
- {
- { 0, 24, 408, 792, 1176, 1560, 1944, 2328, 2648, 2968, 3288, 3544, 3800, 4056, 4248, 4440 },
- { 4632, 5016, 5400, 5784, 6168, 6552, 6936, 7320, 7640, 7960, 8280, 8536, 8792, 9048, 9240, 0 },
- { 0, 9432, 9816, 10200, 10584, 10968, 11352, 11736, 12056, 12376, 12696, 12952, 13208, 13464, 13656, 13848 },
- { 14040, 14424, 14808, 15192, 15576, 15960, 16344, 16728, 17048, 17368, 17688, 17944, 18200, 18456, 18648, 0 }
- };
- static const ALuint tm[2][BSINC_SCALE_COUNT] =
- {
- { 0, 24, 24, 24, 24, 24, 24, 20, 20, 20, 16, 16, 16, 12, 12, 12 },
- { 24, 24, 24, 24, 24, 24, 24, 20, 20, 20, 16, 16, 16, 12, 12, 0 }
- };
- ALfloat sf;
- ALuint si, pi;
- ALboolean uncut = AL_TRUE;
+ ALfloat sf = 0.0f;
+ ALsizei si = BSINC_SCALE_COUNT-1;
if(increment > FRACTIONONE)
{
sf = (ALfloat)FRACTIONONE / increment;
- if(sf < scaleBase)
- {
- /* Signal has been completely cut. The return result can be used
- * to skip the filter (and output zeros) as an optimization.
- */
- sf = 0.0f;
- si = 0;
- uncut = AL_FALSE;
- }
- else
- {
- sf = (BSINC_SCALE_COUNT - 1) * (sf - scaleBase) * scaleRange;
- si = fastf2u(sf);
- /* The interpolation factor is fit to this diagonally-symmetric
- * curve to reduce the transition ripple caused by interpolating
- * different scales of the sinc function.
- */
- sf = 1.0f - cosf(asinf(sf - si));
- }
- }
- else
- {
- sf = 0.0f;
- si = BSINC_SCALE_COUNT - 1;
+ sf = maxf(0.0f, (BSINC_SCALE_COUNT-1) * (sf-table->scaleBase) * table->scaleRange);
+ si = float2int(sf);
+ /* The interpolation factor is fit to this diagonally-symmetric curve
+ * to reduce the transition ripple caused by interpolating different
+ * scales of the sinc function.
+ */
+ sf = 1.0f - cosf(asinf(sf - si));
}
state->sf = sf;
- state->m = m[si];
- state->l = -(ALint)((m[si] / 2) - 1);
- /* The CPU cost of this table re-mapping could be traded for the memory
- * cost of a complete table map (1024 elements large).
- */
- for(pi = 0;pi < BSINC_PHASE_COUNT;pi++)
- {
- state->coeffs[pi].filter = &bsincTab[to[0][si] + tm[0][si]*pi];
- state->coeffs[pi].scDelta = &bsincTab[to[1][si] + tm[1][si]*pi];
- state->coeffs[pi].phDelta = &bsincTab[to[2][si] + tm[0][si]*pi];
- state->coeffs[pi].spDelta = &bsincTab[to[3][si] + tm[1][si]*pi];
- }
- return uncut;
+ state->m = table->m[si];
+ state->l = (state->m/2) - 1;
+ state->filter = table->Tab + table->filterOffset[si];
}
-/* Calculates the fade time from the changes in gain and listener to source
- * angle between updates. The result is a the time, in seconds, for the
- * transition to complete.
- */
-static ALfloat CalcFadeTime(ALfloat oldGain, ALfloat newGain, const aluVector *olddir, const aluVector *newdir)
+static bool CalcContextParams(ALCcontext *Context)
{
- ALfloat gainChange, angleChange, change;
+ ALlistener *Listener = Context->Listener;
+ struct ALcontextProps *props;
- /* Calculate the normalized dB gain change. */
- newGain = maxf(newGain, 0.0001f);
- oldGain = maxf(oldGain, 0.0001f);
- gainChange = fabsf(log10f(newGain / oldGain) / log10f(0.0001f));
+ props = ATOMIC_EXCHANGE_PTR(&Context->Update, NULL, almemory_order_acq_rel);
+ if(!props) return false;
- /* 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->v[0] != olddir->v[0] || newdir->v[1] != olddir->v[1] || newdir->v[2] != olddir->v[2])
- {
- ALfloat dotp = aluDotproduct(olddir, newdir);
- angleChange = acosf(clampf(dotp, -1.0f, 1.0f)) / F_PI;
- }
- }
-
- /* Use the largest of the two changes, and apply a significance shaping
- * function to it. The result is then scaled to cover a 15ms transition
- * range.
- */
- change = maxf(angleChange * 25.0f, gainChange) * 2.0f;
- return minf(change, 1.0f) * 0.015f;
-}
+ Listener->Params.MetersPerUnit = props->MetersPerUnit;
+ Listener->Params.DopplerFactor = props->DopplerFactor;
+ Listener->Params.SpeedOfSound = props->SpeedOfSound * props->DopplerVelocity;
+ if(!OverrideReverbSpeedOfSound)
+ Listener->Params.ReverbSpeedOfSound = Listener->Params.SpeedOfSound *
+ Listener->Params.MetersPerUnit;
-static void UpdateDryStepping(DirectParams *params, ALuint num_chans, ALuint steps)
-{
- ALfloat delta;
- ALuint i, j;
-
- if(steps < 2)
- {
- for(i = 0;i < num_chans;i++)
- {
- MixGains *gains = params->Gains[i];
- for(j = 0;j < params->OutChannels;j++)
- {
- gains[j].Current = gains[j].Target;
- gains[j].Step = 0.0f;
- }
- }
- params->Counter = 0;
- return;
- }
+ Listener->Params.SourceDistanceModel = props->SourceDistanceModel;
+ Listener->Params.DistanceModel = props->DistanceModel;
- delta = 1.0f / (ALfloat)steps;
- for(i = 0;i < num_chans;i++)
- {
- MixGains *gains = params->Gains[i];
- for(j = 0;j < params->OutChannels;j++)
- {
- ALfloat diff = gains[j].Target - gains[j].Current;
- if(fabsf(diff) >= GAIN_SILENCE_THRESHOLD)
- gains[j].Step = diff * delta;
- else
- {
- gains[j].Current = gains[j].Target;
- gains[j].Step = 0.0f;
- }
- }
- }
- params->Counter = steps;
+ ATOMIC_REPLACE_HEAD(struct ALcontextProps*, &Context->FreeContextProps, props);
+ return true;
}
-static void UpdateWetStepping(SendParams *params, ALuint num_chans, ALuint steps)
+static bool CalcListenerParams(ALCcontext *Context)
{
- ALfloat delta;
- ALuint i;
+ ALlistener *Listener = Context->Listener;
+ ALfloat N[3], V[3], U[3], P[3];
+ struct ALlistenerProps *props;
+ aluVector vel;
- if(steps < 2)
- {
- for(i = 0;i < num_chans;i++)
- {
- params->Gains[i].Current = params->Gains[i].Target;
- params->Gains[i].Step = 0.0f;
- }
- params->Counter = 0;
- return;
- }
-
- delta = 1.0f / (ALfloat)steps;
- for(i = 0;i < num_chans;i++)
- {
- ALfloat diff = params->Gains[i].Target - params->Gains[i].Current;
- if(fabsf(diff) >= GAIN_SILENCE_THRESHOLD)
- params->Gains[i].Step = diff * delta;
- else
- {
- params->Gains[i].Current = params->Gains[i].Target;
- params->Gains[i].Step = 0.0f;
- }
- }
- params->Counter = steps;
-}
-
-
-static ALvoid CalcListenerParams(ALlistener *Listener)
-{
- ALdouble N[3], V[3], U[3], P[3];
+ props = ATOMIC_EXCHANGE_PTR(&Listener->Update, NULL, almemory_order_acq_rel);
+ if(!props) return false;
/* AT then UP */
- N[0] = Listener->Forward[0];
- N[1] = Listener->Forward[1];
- N[2] = Listener->Forward[2];
- aluNormalized(N);
- V[0] = Listener->Up[0];
- V[1] = Listener->Up[1];
- V[2] = Listener->Up[2];
- aluNormalized(V);
+ N[0] = props->Forward[0];
+ N[1] = props->Forward[1];
+ N[2] = props->Forward[2];
+ aluNormalize(N);
+ V[0] = props->Up[0];
+ V[1] = props->Up[1];
+ V[2] = props->Up[2];
+ aluNormalize(V);
/* Build and normalize right-vector */
- aluCrossproductd(N, V, U);
- aluNormalized(U);
+ aluCrossproduct(N, V, U);
+ aluNormalize(U);
- aluMatrixdSet(&Listener->Params.Matrix,
+ aluMatrixfSet(&Listener->Params.Matrix,
U[0], V[0], -N[0], 0.0,
U[1], V[1], -N[1], 0.0,
U[2], V[2], -N[2], 0.0,
0.0, 0.0, 0.0, 1.0
);
- P[0] = Listener->Position.v[0];
- P[1] = Listener->Position.v[1];
- P[2] = Listener->Position.v[2];
- aluMatrixdDouble3(P, 1.0, &Listener->Params.Matrix);
- aluMatrixdSetRow(&Listener->Params.Matrix, 3, -P[0], -P[1], -P[2], 1.0f);
+ P[0] = props->Position[0];
+ P[1] = props->Position[1];
+ P[2] = props->Position[2];
+ aluMatrixfFloat3(P, 1.0, &Listener->Params.Matrix);
+ aluMatrixfSetRow(&Listener->Params.Matrix, 3, -P[0], -P[1], -P[2], 1.0f);
- Listener->Params.Velocity = aluMatrixdVector(&Listener->Params.Matrix, &Listener->Velocity);
-}
+ aluVectorSet(&vel, props->Velocity[0], props->Velocity[1], props->Velocity[2], 0.0f);
+ Listener->Params.Velocity = aluMatrixfVector(&Listener->Params.Matrix, &vel);
-ALvoid CalcNonAttnSourceParams(ALvoice *voice, const ALsource *ALSource, const ALCcontext *ALContext)
-{
- static const struct ChanMap MonoMap[1] = {
- { FrontCenter, 0.0f, 0.0f }
- }, StereoMap[2] = {
- { FrontLeft, DEG2RAD(-30.0f), DEG2RAD(0.0f) },
- { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) }
- }, StereoWideMap[2] = {
- { FrontLeft, DEG2RAD(-90.0f), DEG2RAD(0.0f) },
- { FrontRight, DEG2RAD( 90.0f), DEG2RAD(0.0f) }
- }, RearMap[2] = {
- { BackLeft, DEG2RAD(-150.0f), DEG2RAD(0.0f) },
- { BackRight, DEG2RAD( 150.0f), DEG2RAD(0.0f) }
- }, QuadMap[4] = {
- { FrontLeft, DEG2RAD( -45.0f), DEG2RAD(0.0f) },
- { FrontRight, DEG2RAD( 45.0f), DEG2RAD(0.0f) },
- { BackLeft, DEG2RAD(-135.0f), DEG2RAD(0.0f) },
- { BackRight, DEG2RAD( 135.0f), DEG2RAD(0.0f) }
- }, X51Map[6] = {
- { FrontLeft, DEG2RAD( -30.0f), DEG2RAD(0.0f) },
- { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
- { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
- { LFE, 0.0f, 0.0f },
- { SideLeft, DEG2RAD(-110.0f), DEG2RAD(0.0f) },
- { SideRight, DEG2RAD( 110.0f), DEG2RAD(0.0f) }
- }, X61Map[7] = {
- { FrontLeft, DEG2RAD(-30.0f), DEG2RAD(0.0f) },
- { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
- { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
- { LFE, 0.0f, 0.0f },
- { BackCenter, DEG2RAD(180.0f), DEG2RAD(0.0f) },
- { SideLeft, DEG2RAD(-90.0f), DEG2RAD(0.0f) },
- { SideRight, DEG2RAD( 90.0f), DEG2RAD(0.0f) }
- }, X71Map[8] = {
- { FrontLeft, DEG2RAD( -30.0f), DEG2RAD(0.0f) },
- { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
- { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
- { LFE, 0.0f, 0.0f },
- { BackLeft, DEG2RAD(-150.0f), DEG2RAD(0.0f) },
- { BackRight, DEG2RAD( 150.0f), DEG2RAD(0.0f) },
- { SideLeft, DEG2RAD( -90.0f), DEG2RAD(0.0f) },
- { SideRight, DEG2RAD( 90.0f), DEG2RAD(0.0f) }
- };
+ Listener->Params.Gain = props->Gain * Context->GainBoost;
- ALCdevice *Device = ALContext->Device;
- ALfloat SourceVolume,ListenerGain,MinVolume,MaxVolume;
- ALbufferlistitem *BufferListItem;
- enum FmtChannels Channels;
- ALfloat DryGain, DryGainHF, DryGainLF;
- ALfloat WetGain[MAX_SENDS];
- ALfloat WetGainHF[MAX_SENDS];
- ALfloat WetGainLF[MAX_SENDS];
- ALuint NumSends, Frequency;
- ALboolean Relative;
- const struct ChanMap *chans = NULL;
- ALuint num_channels = 0;
- ALboolean DirectChannels;
- ALboolean isbformat = AL_FALSE;
- ALfloat Pitch;
- ALuint i, j, c;
-
- /* Get device properties */
- NumSends = Device->NumAuxSends;
- Frequency = Device->Frequency;
+ ATOMIC_REPLACE_HEAD(struct ALlistenerProps*, &Context->FreeListenerProps, props);
+ return true;
+}
- /* Get listener properties */
- ListenerGain = ALContext->Listener->Gain;
+static bool CalcEffectSlotParams(ALeffectslot *slot, ALCcontext *context, bool force)
+{
+ struct ALeffectslotProps *props;
+ ALeffectState *state;
- /* Get source properties */
- SourceVolume = ALSource->Gain;
- MinVolume = ALSource->MinGain;
- MaxVolume = ALSource->MaxGain;
- Pitch = ALSource->Pitch;
- Relative = ALSource->HeadRelative;
- DirectChannels = ALSource->DirectChannels;
+ props = ATOMIC_EXCHANGE_PTR(&slot->Update, NULL, almemory_order_acq_rel);
+ if(!props && !force) return false;
- voice->Direct.OutBuffer = Device->DryBuffer;
- voice->Direct.OutChannels = Device->NumChannels;
- for(i = 0;i < NumSends;i++)
+ if(props)
{
- ALeffectslot *Slot = ALSource->Send[i].Slot;
- if(!Slot && i == 0)
- Slot = Device->DefaultSlot;
- if(!Slot || Slot->EffectType == AL_EFFECT_NULL)
- voice->Send[i].OutBuffer = NULL;
+ slot->Params.Gain = props->Gain;
+ slot->Params.AuxSendAuto = props->AuxSendAuto;
+ slot->Params.EffectType = props->Type;
+ slot->Params.EffectProps = props->Props;
+ if(IsReverbEffect(props->Type))
+ {
+ slot->Params.RoomRolloff = props->Props.Reverb.RoomRolloffFactor;
+ slot->Params.DecayTime = props->Props.Reverb.DecayTime;
+ slot->Params.DecayLFRatio = props->Props.Reverb.DecayLFRatio;
+ slot->Params.DecayHFRatio = props->Props.Reverb.DecayHFRatio;
+ slot->Params.DecayHFLimit = props->Props.Reverb.DecayHFLimit;
+ slot->Params.AirAbsorptionGainHF = props->Props.Reverb.AirAbsorptionGainHF;
+ }
else
- voice->Send[i].OutBuffer = Slot->WetBuffer;
- }
+ {
+ slot->Params.RoomRolloff = 0.0f;
+ slot->Params.DecayTime = 0.0f;
+ slot->Params.DecayLFRatio = 0.0f;
+ slot->Params.DecayHFRatio = 0.0f;
+ slot->Params.DecayHFLimit = AL_FALSE;
+ slot->Params.AirAbsorptionGainHF = 1.0f;
+ }
- /* Calculate the stepping value */
- Channels = FmtMono;
- BufferListItem = ATOMIC_LOAD(&ALSource->queue);
- while(BufferListItem != NULL)
- {
- ALbuffer *ALBuffer;
- if((ALBuffer=BufferListItem->buffer) != NULL)
+ state = props->State;
+
+ if(state == slot->Params.EffectState)
{
- Pitch = Pitch * ALBuffer->Frequency / Frequency;
- if(Pitch > (ALfloat)MAX_PITCH)
- voice->Step = MAX_PITCH<<FRACTIONBITS;
- else
- voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
- BsincPrepare(voice->Step, &voice->SincState);
+ /* If the effect state is the same as current, we can decrement its
+ * count safely to remove it from the update object (it can't reach
+ * 0 refs since the current params also hold a reference).
+ */
+ DecrementRef(&state->Ref);
+ props->State = NULL;
+ }
+ else
+ {
+ /* Otherwise, replace it and send off the old one with a release
+ * event.
+ */
+ AsyncEvent evt = ASYNC_EVENT(EventType_ReleaseEffectState);
+ evt.u.EffectState = slot->Params.EffectState;
- Channels = ALBuffer->FmtChannels;
- break;
+ slot->Params.EffectState = state;
+ props->State = NULL;
+
+ if(LIKELY(ll_ringbuffer_write(context->AsyncEvents, (const char*)&evt, 1) != 0))
+ alsem_post(&context->EventSem);
+ else
+ {
+ /* If writing the event failed, the queue was probably full.
+ * Store the old state in the property object where it can
+ * eventually be cleaned up sometime later (not ideal, but
+ * better than blocking or leaking).
+ */
+ props->State = evt.u.EffectState;
+ }
}
- BufferListItem = BufferListItem->next;
- }
- /* Calculate gains */
- DryGain = clampf(SourceVolume, MinVolume, MaxVolume);
- DryGain *= ALSource->Direct.Gain * ListenerGain;
- DryGainHF = ALSource->Direct.GainHF;
- DryGainLF = ALSource->Direct.GainLF;
- for(i = 0;i < NumSends;i++)
- {
- WetGain[i] = clampf(SourceVolume, MinVolume, MaxVolume);
- WetGain[i] *= ALSource->Send[i].Gain * ListenerGain;
- WetGainHF[i] = ALSource->Send[i].GainHF;
- WetGainLF[i] = ALSource->Send[i].GainLF;
+ ATOMIC_REPLACE_HEAD(struct ALeffectslotProps*, &context->FreeEffectslotProps, props);
}
+ else
+ state = slot->Params.EffectState;
+
+ V(state,update)(context, slot, &slot->Params.EffectProps);
+ return true;
+}
+
+
+static const struct ChanMap MonoMap[1] = {
+ { FrontCenter, 0.0f, 0.0f }
+}, RearMap[2] = {
+ { BackLeft, DEG2RAD(-150.0f), DEG2RAD(0.0f) },
+ { BackRight, DEG2RAD( 150.0f), DEG2RAD(0.0f) }
+}, QuadMap[4] = {
+ { FrontLeft, DEG2RAD( -45.0f), DEG2RAD(0.0f) },
+ { FrontRight, DEG2RAD( 45.0f), DEG2RAD(0.0f) },
+ { BackLeft, DEG2RAD(-135.0f), DEG2RAD(0.0f) },
+ { BackRight, DEG2RAD( 135.0f), DEG2RAD(0.0f) }
+}, X51Map[6] = {
+ { FrontLeft, DEG2RAD( -30.0f), DEG2RAD(0.0f) },
+ { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
+ { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
+ { LFE, 0.0f, 0.0f },
+ { SideLeft, DEG2RAD(-110.0f), DEG2RAD(0.0f) },
+ { SideRight, DEG2RAD( 110.0f), DEG2RAD(0.0f) }
+}, X61Map[7] = {
+ { FrontLeft, DEG2RAD(-30.0f), DEG2RAD(0.0f) },
+ { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
+ { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
+ { LFE, 0.0f, 0.0f },
+ { BackCenter, DEG2RAD(180.0f), DEG2RAD(0.0f) },
+ { SideLeft, DEG2RAD(-90.0f), DEG2RAD(0.0f) },
+ { SideRight, DEG2RAD( 90.0f), DEG2RAD(0.0f) }
+}, X71Map[8] = {
+ { FrontLeft, DEG2RAD( -30.0f), DEG2RAD(0.0f) },
+ { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) },
+ { FrontCenter, DEG2RAD( 0.0f), DEG2RAD(0.0f) },
+ { LFE, 0.0f, 0.0f },
+ { BackLeft, DEG2RAD(-150.0f), DEG2RAD(0.0f) },
+ { BackRight, DEG2RAD( 150.0f), DEG2RAD(0.0f) },
+ { SideLeft, DEG2RAD( -90.0f), DEG2RAD(0.0f) },
+ { SideRight, DEG2RAD( 90.0f), DEG2RAD(0.0f) }
+};
+
+static void CalcPanningAndFilters(ALvoice *voice, const ALfloat Azi, const ALfloat Elev,
+ const ALfloat Distance, const ALfloat Spread,
+ const ALfloat DryGain, const ALfloat DryGainHF,
+ const ALfloat DryGainLF, const ALfloat *WetGain,
+ const ALfloat *WetGainLF, const ALfloat *WetGainHF,
+ ALeffectslot **SendSlots, const ALbuffer *Buffer,
+ const struct ALvoiceProps *props, const ALlistener *Listener,
+ const ALCdevice *Device)
+{
+ struct ChanMap StereoMap[2] = {
+ { FrontLeft, DEG2RAD(-30.0f), DEG2RAD(0.0f) },
+ { FrontRight, DEG2RAD( 30.0f), DEG2RAD(0.0f) }
+ };
+ bool DirectChannels = props->DirectChannels;
+ const ALsizei NumSends = Device->NumAuxSends;
+ const ALuint Frequency = Device->Frequency;
+ const struct ChanMap *chans = NULL;
+ ALsizei num_channels = 0;
+ bool isbformat = false;
+ ALfloat downmix_gain = 1.0f;
+ ALsizei c, i;
- switch(Channels)
+ switch(Buffer->FmtChannels)
{
case FmtMono:
chans = MonoMap;
num_channels = 1;
+ /* Mono buffers are never played direct. */
+ DirectChannels = false;
break;
case FmtStereo:
- /* HACK: Place the stereo channels at +/-90 degrees when using non-
- * HRTF stereo output. This helps reduce the "monoization" caused
- * by them panning towards the center. */
- if(Device->FmtChans == DevFmtStereo && !Device->Hrtf)
- chans = StereoWideMap;
- else
- chans = StereoMap;
+ /* Convert counter-clockwise to clockwise. */
+ StereoMap[0].angle = -props->StereoPan[0];
+ StereoMap[1].angle = -props->StereoPan[1];
+
+ chans = StereoMap;
num_channels = 2;
+ downmix_gain = 1.0f / 2.0f;
break;
case FmtRear:
chans = RearMap;
num_channels = 2;
+ downmix_gain = 1.0f / 2.0f;
break;
case FmtQuad:
chans = QuadMap;
num_channels = 4;
+ downmix_gain = 1.0f / 4.0f;
break;
case FmtX51:
chans = X51Map;
num_channels = 6;
+ /* NOTE: Excludes LFE. */
+ downmix_gain = 1.0f / 5.0f;
break;
case FmtX61:
chans = X61Map;
num_channels = 7;
+ /* NOTE: Excludes LFE. */
+ downmix_gain = 1.0f / 6.0f;
break;
case FmtX71:
chans = X71Map;
num_channels = 8;
+ /* NOTE: Excludes LFE. */
+ downmix_gain = 1.0f / 7.0f;
break;
case FmtBFormat2D:
num_channels = 3;
- isbformat = AL_TRUE;
- DirectChannels = AL_FALSE;
+ isbformat = true;
+ DirectChannels = false;
break;
case FmtBFormat3D:
num_channels = 4;
- isbformat = AL_TRUE;
- DirectChannels = AL_FALSE;
+ isbformat = true;
+ DirectChannels = false;
break;
}
+ for(c = 0;c < num_channels;c++)
+ {
+ memset(&voice->Direct.Params[c].Hrtf.Target, 0,
+ sizeof(voice->Direct.Params[c].Hrtf.Target));
+ ClearArray(voice->Direct.Params[c].Gains.Target);
+ }
+ for(i = 0;i < NumSends;i++)
+ {
+ for(c = 0;c < num_channels;c++)
+ ClearArray(voice->Send[i].Params[c].Gains.Target);
+ }
+
+ voice->Flags &= ~(VOICE_HAS_HRTF | VOICE_HAS_NFC);
if(isbformat)
{
- ALfloat N[3], V[3], U[3];
- aluMatrixf matrix;
- ALfloat scale;
-
- /* AT then UP */
- N[0] = ALSource->Orientation[0][0];
- N[1] = ALSource->Orientation[0][1];
- N[2] = ALSource->Orientation[0][2];
- aluNormalize(N);
- V[0] = ALSource->Orientation[1][0];
- V[1] = ALSource->Orientation[1][1];
- V[2] = ALSource->Orientation[1][2];
- aluNormalize(V);
- if(!Relative)
+ /* Special handling for B-Format sources. */
+
+ if(Distance > FLT_EPSILON)
+ {
+ /* Panning a B-Format sound toward some direction is easy. Just pan
+ * the first (W) channel as a normal mono sound and silence the
+ * others.
+ */
+ ALfloat coeffs[MAX_AMBI_COEFFS];
+
+ if(Device->AvgSpeakerDist > 0.0f)
+ {
+ ALfloat mdist = Distance * Listener->Params.MetersPerUnit;
+ ALfloat w0 = SPEEDOFSOUNDMETRESPERSEC /
+ (mdist * (ALfloat)Device->Frequency);
+ ALfloat w1 = SPEEDOFSOUNDMETRESPERSEC /
+ (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
+ /* Clamp w0 for really close distances, to prevent excessive
+ * bass.
+ */
+ w0 = minf(w0, w1*4.0f);
+
+ /* Only need to adjust the first channel of a B-Format source. */
+ NfcFilterAdjust(&voice->Direct.Params[0].NFCtrlFilter, w0);
+
+ for(i = 0;i < MAX_AMBI_ORDER+1;i++)
+ voice->Direct.ChannelsPerOrder[i] = Device->NumChannelsPerOrder[i];
+ voice->Flags |= VOICE_HAS_NFC;
+ }
+
+ /* A scalar of 1.5 for plain stereo results in +/-60 degrees being
+ * moved to +/-90 degrees for direct right and left speaker
+ * responses.
+ */
+ CalcAngleCoeffs((Device->Render_Mode==StereoPair) ? ScaleAzimuthFront(Azi, 1.5f) : Azi,
+ Elev, Spread, coeffs);
+
+ /* NOTE: W needs to be scaled by sqrt(2) due to FuMa normalization. */
+ ComputePanGains(&Device->Dry, coeffs, DryGain*SQRTF_2,
+ voice->Direct.Params[0].Gains.Target);
+ for(i = 0;i < NumSends;i++)
+ {
+ const ALeffectslot *Slot = SendSlots[i];
+ if(Slot)
+ ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs,
+ WetGain[i]*SQRTF_2, voice->Send[i].Params[0].Gains.Target
+ );
+ }
+ }
+ else
+ {
+ /* Local B-Format sources have their XYZ channels rotated according
+ * to the orientation.
+ */
+ ALfloat N[3], V[3], U[3];
+ aluMatrixf matrix;
+
+ if(Device->AvgSpeakerDist > 0.0f)
+ {
+ /* NOTE: The NFCtrlFilters were created with a w0 of 0, which
+ * is what we want for FOA input. The first channel may have
+ * been previously re-adjusted if panned, so reset it.
+ */
+ NfcFilterAdjust(&voice->Direct.Params[0].NFCtrlFilter, 0.0f);
+
+ voice->Direct.ChannelsPerOrder[0] = 1;
+ voice->Direct.ChannelsPerOrder[1] = mini(voice->Direct.Channels-1, 3);
+ for(i = 2;i < MAX_AMBI_ORDER+1;i++)
+ voice->Direct.ChannelsPerOrder[i] = 0;
+ voice->Flags |= VOICE_HAS_NFC;
+ }
+
+ /* AT then UP */
+ N[0] = props->Orientation[0][0];
+ N[1] = props->Orientation[0][1];
+ N[2] = props->Orientation[0][2];
+ aluNormalize(N);
+ V[0] = props->Orientation[1][0];
+ V[1] = props->Orientation[1][1];
+ V[2] = props->Orientation[1][2];
+ aluNormalize(V);
+ if(!props->HeadRelative)
+ {
+ const aluMatrixf *lmatrix = &Listener->Params.Matrix;
+ aluMatrixfFloat3(N, 0.0f, lmatrix);
+ aluMatrixfFloat3(V, 0.0f, lmatrix);
+ }
+ /* Build and normalize right-vector */
+ aluCrossproduct(N, V, U);
+ aluNormalize(U);
+
+ /* Build a rotate + conversion matrix (FuMa -> ACN+N3D). NOTE: This
+ * matrix is transposed, for the inputs to align on the rows and
+ * outputs on the columns.
+ */
+ aluMatrixfSet(&matrix,
+ // ACN0 ACN1 ACN2 ACN3
+ SQRTF_2, 0.0f, 0.0f, 0.0f, // Ambi W
+ 0.0f, -N[0]*SQRTF_3, N[1]*SQRTF_3, -N[2]*SQRTF_3, // Ambi X
+ 0.0f, U[0]*SQRTF_3, -U[1]*SQRTF_3, U[2]*SQRTF_3, // Ambi Y
+ 0.0f, -V[0]*SQRTF_3, V[1]*SQRTF_3, -V[2]*SQRTF_3 // Ambi Z
+ );
+
+ voice->Direct.Buffer = Device->FOAOut.Buffer;
+ voice->Direct.Channels = Device->FOAOut.NumChannels;
+ for(c = 0;c < num_channels;c++)
+ ComputePanGains(&Device->FOAOut, matrix.m[c], DryGain,
+ voice->Direct.Params[c].Gains.Target);
+ for(i = 0;i < NumSends;i++)
+ {
+ const ALeffectslot *Slot = SendSlots[i];
+ if(Slot)
+ {
+ for(c = 0;c < num_channels;c++)
+ ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
+ matrix.m[c], WetGain[i], voice->Send[i].Params[c].Gains.Target
+ );
+ }
+ }
+ }
+ }
+ else if(DirectChannels)
+ {
+ /* Direct source channels always play local. Skip the virtual channels
+ * and write inputs to the matching real outputs.
+ */
+ voice->Direct.Buffer = Device->RealOut.Buffer;
+ voice->Direct.Channels = Device->RealOut.NumChannels;
+
+ for(c = 0;c < num_channels;c++)
{
- const aluMatrixd *lmatrix = &ALContext->Listener->Params.Matrix;
- aluMatrixdFloat3(N, 0.0f, lmatrix);
- aluMatrixdFloat3(V, 0.0f, lmatrix);
+ int idx = GetChannelIdxByName(&Device->RealOut, chans[c].channel);
+ if(idx != -1) voice->Direct.Params[c].Gains.Target[idx] = DryGain;
}
- /* Build and normalize right-vector */
- aluCrossproduct(N, V, U);
- aluNormalize(U);
-
- /* Build a rotate + conversion matrix (B-Format -> N3D), and include
- * scaling for first-order content. */
- scale = Device->AmbiScale * 1.732050808f;
- aluMatrixfSet(&matrix,
- 1.414213562f, 0.0f, 0.0f, 0.0f,
- 0.0f, -N[0]*scale, N[1]*scale, -N[2]*scale,
- 0.0f, U[0]*scale, -U[1]*scale, U[2]*scale,
- 0.0f, -V[0]*scale, V[1]*scale, -V[2]*scale
- );
+ /* Auxiliary sends still use normal channel panning since they mix to
+ * B-Format, which can't channel-match.
+ */
for(c = 0;c < num_channels;c++)
{
- MixGains *gains = voice->Direct.Gains[c];
- ALfloat Target[MAX_OUTPUT_CHANNELS];
+ ALfloat coeffs[MAX_AMBI_COEFFS];
+ CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f, coeffs);
- ComputeBFormatGains(Device, matrix.m[c], DryGain, Target);
- for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
- gains[i].Target = Target[i];
+ for(i = 0;i < NumSends;i++)
+ {
+ const ALeffectslot *Slot = SendSlots[i];
+ if(Slot)
+ ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
+ coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
+ );
+ }
}
- UpdateDryStepping(&voice->Direct, num_channels, (voice->Direct.Moving ? 64 : 0));
- voice->Direct.Moving = AL_TRUE;
-
- voice->IsHrtf = AL_FALSE;
+ }
+ else if(Device->Render_Mode == HrtfRender)
+ {
+ /* Full HRTF rendering. Skip the virtual channels and render to the
+ * real outputs.
+ */
+ voice->Direct.Buffer = Device->RealOut.Buffer;
+ voice->Direct.Channels = Device->RealOut.NumChannels;
- for(i = 0;i < NumSends;i++)
+ if(Distance > FLT_EPSILON)
{
- /* Only the first channel of B-Format buffers (W) goes to auxiliary
- * sends. It also needs to be scaled by sqrt(2) to account for the
- * signal being scaled by sqrt(1/2).
+ ALfloat coeffs[MAX_AMBI_COEFFS];
+
+ /* Get the HRIR coefficients and delays just once, for the given
+ * source direction.
*/
- voice->Send[i].Gains[0].Target = WetGain[i] * 1.414213562f;
+ GetHrtfCoeffs(Device->HrtfHandle, Elev, Azi, Spread,
+ voice->Direct.Params[0].Hrtf.Target.Coeffs,
+ voice->Direct.Params[0].Hrtf.Target.Delay);
+ voice->Direct.Params[0].Hrtf.Target.Gain = DryGain * downmix_gain;
+
+ /* Remaining channels use the same results as the first. */
for(c = 1;c < num_channels;c++)
- voice->Send[i].Gains[c].Target = 0.0f;
- UpdateWetStepping(&voice->Send[i], num_channels, (voice->Send[i].Moving ? 64 : 0));
- voice->Send[i].Moving = AL_TRUE;
+ {
+ /* Skip LFE */
+ if(chans[c].channel != LFE)
+ voice->Direct.Params[c].Hrtf.Target = voice->Direct.Params[0].Hrtf.Target;
+ }
+
+ /* Calculate the directional coefficients once, which apply to all
+ * input channels of the source sends.
+ */
+ CalcAngleCoeffs(Azi, Elev, Spread, coeffs);
+
+ for(i = 0;i < NumSends;i++)
+ {
+ const ALeffectslot *Slot = SendSlots[i];
+ if(Slot)
+ for(c = 0;c < num_channels;c++)
+ {
+ /* Skip LFE */
+ if(chans[c].channel != LFE)
+ ComputePanningGainsBF(Slot->ChanMap,
+ Slot->NumChannels, coeffs, WetGain[i] * downmix_gain,
+ voice->Send[i].Params[c].Gains.Target
+ );
+ }
+ }
}
- }
- else
- {
- if(DirectChannels)
+ else
{
- if(Device->Hrtf)
+ /* Local sources on HRTF play with each channel panned to its
+ * relative location around the listener, providing "virtual
+ * speaker" responses.
+ */
+ for(c = 0;c < num_channels;c++)
{
- /* DirectChannels with HRTF enabled. Skip the virtual channels
- * and write FrontLeft and FrontRight inputs to the first and
- * second outputs.
- */
- voice->Direct.OutBuffer += voice->Direct.OutChannels;
- voice->Direct.OutChannels = 2;
- for(c = 0;c < num_channels;c++)
+ ALfloat coeffs[MAX_AMBI_COEFFS];
+
+ if(chans[c].channel == LFE)
{
- MixGains *gains = voice->Direct.Gains[c];
+ /* Skip LFE */
+ continue;
+ }
- for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
- gains[j].Target = 0.0f;
+ /* Get the HRIR coefficients and delays for this channel
+ * position.
+ */
+ GetHrtfCoeffs(Device->HrtfHandle,
+ chans[c].elevation, chans[c].angle, Spread,
+ voice->Direct.Params[c].Hrtf.Target.Coeffs,
+ voice->Direct.Params[c].Hrtf.Target.Delay
+ );
+ voice->Direct.Params[c].Hrtf.Target.Gain = DryGain;
- if(chans[c].channel == FrontLeft)
- gains[0].Target = DryGain;
- else if(chans[c].channel == FrontRight)
- gains[1].Target = DryGain;
+ /* Normal panning for auxiliary sends. */
+ CalcAngleCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs);
+
+ for(i = 0;i < NumSends;i++)
+ {
+ const ALeffectslot *Slot = SendSlots[i];
+ if(Slot)
+ ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
+ coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
+ );
}
}
- else for(c = 0;c < num_channels;c++)
+ }
+
+ voice->Flags |= VOICE_HAS_HRTF;
+ }
+ else
+ {
+ /* Non-HRTF rendering. Use normal panning to the output. */
+
+ if(Distance > FLT_EPSILON)
+ {
+ ALfloat coeffs[MAX_AMBI_COEFFS];
+ ALfloat w0 = 0.0f;
+
+ /* Calculate NFC filter coefficient if needed. */
+ if(Device->AvgSpeakerDist > 0.0f)
{
- MixGains *gains = voice->Direct.Gains[c];
- int idx;
+ ALfloat mdist = Distance * Listener->Params.MetersPerUnit;
+ ALfloat w1 = SPEEDOFSOUNDMETRESPERSEC /
+ (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
+ w0 = SPEEDOFSOUNDMETRESPERSEC /
+ (mdist * (ALfloat)Device->Frequency);
+ /* Clamp w0 for really close distances, to prevent excessive
+ * bass.
+ */
+ w0 = minf(w0, w1*4.0f);
- for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
- gains[j].Target = 0.0f;
- if((idx=GetChannelIdxByName(Device, chans[c].channel)) != -1)
- gains[idx].Target = DryGain;
+ /* Adjust NFC filters. */
+ for(c = 0;c < num_channels;c++)
+ NfcFilterAdjust(&voice->Direct.Params[c].NFCtrlFilter, w0);
+
+ for(i = 0;i < MAX_AMBI_ORDER+1;i++)
+ voice->Direct.ChannelsPerOrder[i] = Device->NumChannelsPerOrder[i];
+ voice->Flags |= VOICE_HAS_NFC;
}
- UpdateDryStepping(&voice->Direct, num_channels, (voice->Direct.Moving ? 64 : 0));
- voice->Direct.Moving = AL_TRUE;
- voice->IsHrtf = AL_FALSE;
- }
- else if(Device->Hrtf_Mode == FullHrtf)
- {
- /* Full HRTF rendering. Skip the virtual channels and render each
- * input channel to the real outputs.
+ /* Calculate the directional coefficients once, which apply to all
+ * input channels.
*/
- voice->Direct.OutBuffer += voice->Direct.OutChannels;
- voice->Direct.OutChannels = 2;
+ CalcAngleCoeffs((Device->Render_Mode==StereoPair) ? ScaleAzimuthFront(Azi, 1.5f) : Azi,
+ Elev, Spread, coeffs);
+
for(c = 0;c < num_channels;c++)
{
+ /* Special-case LFE */
if(chans[c].channel == LFE)
{
- /* Skip LFE */
- voice->Direct.Hrtf[c].Params.Delay[0] = 0;
- voice->Direct.Hrtf[c].Params.Delay[1] = 0;
- for(i = 0;i < HRIR_LENGTH;i++)
+ if(Device->Dry.Buffer == Device->RealOut.Buffer)
{
- voice->Direct.Hrtf[c].Params.Coeffs[i][0] = 0.0f;
- voice->Direct.Hrtf[c].Params.Coeffs[i][1] = 0.0f;
+ int idx = GetChannelIdxByName(&Device->RealOut, chans[c].channel);
+ if(idx != -1) voice->Direct.Params[c].Gains.Target[idx] = DryGain;
}
+ continue;
}
- else
- {
- /* Get the static HRIR coefficients and delays for this
- * channel. */
- GetLerpedHrtfCoeffs(Device->Hrtf,
- chans[c].elevation, chans[c].angle, 1.0f, DryGain,
- voice->Direct.Hrtf[c].Params.Coeffs,
- voice->Direct.Hrtf[c].Params.Delay
- );
- }
+
+ ComputePanGains(&Device->Dry, coeffs, DryGain * downmix_gain,
+ voice->Direct.Params[c].Gains.Target);
}
- voice->Direct.Counter = 0;
- voice->Direct.Moving = AL_TRUE;
- voice->IsHrtf = AL_TRUE;
+ for(i = 0;i < NumSends;i++)
+ {
+ const ALeffectslot *Slot = SendSlots[i];
+ if(Slot)
+ for(c = 0;c < num_channels;c++)
+ {
+ /* Skip LFE */
+ if(chans[c].channel != LFE)
+ ComputePanningGainsBF(Slot->ChanMap,
+ Slot->NumChannels, coeffs, WetGain[i] * downmix_gain,
+ voice->Send[i].Params[c].Gains.Target
+ );
+ }
+ }
}
else
{
- /* Basic or no HRTF rendering. Use normal panning to the output. */
+ ALfloat w0 = 0.0f;
+
+ if(Device->AvgSpeakerDist > 0.0f)
+ {
+ /* If the source distance is 0, set w0 to w1 to act as a pass-
+ * through. We still want to pass the signal through the
+ * filters so they keep an appropriate history, in case the
+ * source moves away from the listener.
+ */
+ w0 = SPEEDOFSOUNDMETRESPERSEC /
+ (Device->AvgSpeakerDist * (ALfloat)Device->Frequency);
+
+ for(c = 0;c < num_channels;c++)
+ NfcFilterAdjust(&voice->Direct.Params[c].NFCtrlFilter, w0);
+
+ for(i = 0;i < MAX_AMBI_ORDER+1;i++)
+ voice->Direct.ChannelsPerOrder[i] = Device->NumChannelsPerOrder[i];
+ voice->Flags |= VOICE_HAS_NFC;
+ }
+
for(c = 0;c < num_channels;c++)
{
- MixGains *gains = voice->Direct.Gains[c];
- ALfloat Target[MAX_OUTPUT_CHANNELS];
+ ALfloat coeffs[MAX_AMBI_COEFFS];
/* Special-case LFE */
if(chans[c].channel == LFE)
{
- int idx;
- for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
- gains[i].Target = 0.0f;
- if((idx=GetChannelIdxByName(Device, chans[c].channel)) != -1)
- gains[idx].Target = DryGain;
+ if(Device->Dry.Buffer == Device->RealOut.Buffer)
+ {
+ int idx = GetChannelIdxByName(&Device->RealOut, chans[c].channel);
+ if(idx != -1) voice->Direct.Params[c].Gains.Target[idx] = DryGain;
+ }
continue;
}
- ComputeAngleGains(Device, chans[c].angle, chans[c].elevation, DryGain, Target);
- for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
- gains[i].Target = Target[i];
- }
- UpdateDryStepping(&voice->Direct, num_channels, (voice->Direct.Moving ? 64 : 0));
- voice->Direct.Moving = AL_TRUE;
+ CalcAngleCoeffs(
+ (Device->Render_Mode==StereoPair) ? ScaleAzimuthFront(chans[c].angle, 3.0f)
+ : chans[c].angle,
+ chans[c].elevation, Spread, coeffs
+ );
- voice->IsHrtf = AL_FALSE;
- }
- for(i = 0;i < NumSends;i++)
- {
- for(c = 0;c < num_channels;c++)
- voice->Send[i].Gains[c].Target = WetGain[i];
- UpdateWetStepping(&voice->Send[i], num_channels, (voice->Send[i].Moving ? 64 : 0));
- voice->Send[i].Moving = AL_TRUE;
+ ComputePanGains(&Device->Dry, coeffs, DryGain,
+ voice->Direct.Params[c].Gains.Target);
+ for(i = 0;i < NumSends;i++)
+ {
+ const ALeffectslot *Slot = SendSlots[i];
+ if(Slot)
+ ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
+ coeffs, WetGain[i], voice->Send[i].Params[c].Gains.Target
+ );
+ }
+ }
}
}
{
- ALfloat hfscale = ALSource->Direct.HFReference / Frequency;
- ALfloat lfscale = ALSource->Direct.LFReference / Frequency;
- DryGainHF = maxf(DryGainHF, 0.0001f);
- DryGainLF = maxf(DryGainLF, 0.0001f);
- for(c = 0;c < num_channels;c++)
+ ALfloat hfScale = props->Direct.HFReference / Frequency;
+ ALfloat lfScale = props->Direct.LFReference / Frequency;
+ ALfloat gainHF = maxf(DryGainHF, 0.001f); /* Limit -60dB */
+ ALfloat gainLF = maxf(DryGainLF, 0.001f);
+
+ voice->Direct.FilterType = AF_None;
+ if(gainHF != 1.0f) voice->Direct.FilterType |= AF_LowPass;
+ if(gainLF != 1.0f) voice->Direct.FilterType |= AF_HighPass;
+ BiquadFilter_setParams(
+ &voice->Direct.Params[0].LowPass, BiquadType_HighShelf,
+ gainHF, hfScale, calc_rcpQ_from_slope(gainHF, 1.0f)
+ );
+ BiquadFilter_setParams(
+ &voice->Direct.Params[0].HighPass, BiquadType_LowShelf,
+ gainLF, lfScale, calc_rcpQ_from_slope(gainLF, 1.0f)
+ );
+ for(c = 1;c < num_channels;c++)
{
- voice->Direct.Filters[c].ActiveType = AF_None;
- if(DryGainHF != 1.0f) voice->Direct.Filters[c].ActiveType |= AF_LowPass;
- if(DryGainLF != 1.0f) voice->Direct.Filters[c].ActiveType |= AF_HighPass;
- ALfilterState_setParams(
- &voice->Direct.Filters[c].LowPass, ALfilterType_HighShelf,
- DryGainHF, hfscale, calc_rcpQ_from_slope(DryGainHF, 0.75f)
- );
- ALfilterState_setParams(
- &voice->Direct.Filters[c].HighPass, ALfilterType_LowShelf,
- DryGainLF, lfscale, calc_rcpQ_from_slope(DryGainLF, 0.75f)
- );
+ BiquadFilter_copyParams(&voice->Direct.Params[c].LowPass,
+ &voice->Direct.Params[0].LowPass);
+ BiquadFilter_copyParams(&voice->Direct.Params[c].HighPass,
+ &voice->Direct.Params[0].HighPass);
}
}
for(i = 0;i < NumSends;i++)
{
- ALfloat hfscale = ALSource->Send[i].HFReference / Frequency;
- ALfloat lfscale = ALSource->Send[i].LFReference / Frequency;
- WetGainHF[i] = maxf(WetGainHF[i], 0.0001f);
- WetGainLF[i] = maxf(WetGainLF[i], 0.0001f);
- for(c = 0;c < num_channels;c++)
+ ALfloat hfScale = props->Send[i].HFReference / Frequency;
+ ALfloat lfScale = props->Send[i].LFReference / Frequency;
+ ALfloat gainHF = maxf(WetGainHF[i], 0.001f);
+ ALfloat gainLF = maxf(WetGainLF[i], 0.001f);
+
+ voice->Send[i].FilterType = AF_None;
+ if(gainHF != 1.0f) voice->Send[i].FilterType |= AF_LowPass;
+ if(gainLF != 1.0f) voice->Send[i].FilterType |= AF_HighPass;
+ BiquadFilter_setParams(
+ &voice->Send[i].Params[0].LowPass, BiquadType_HighShelf,
+ gainHF, hfScale, calc_rcpQ_from_slope(gainHF, 1.0f)
+ );
+ BiquadFilter_setParams(
+ &voice->Send[i].Params[0].HighPass, BiquadType_LowShelf,
+ gainLF, lfScale, calc_rcpQ_from_slope(gainLF, 1.0f)
+ );
+ for(c = 1;c < num_channels;c++)
{
- voice->Send[i].Filters[c].ActiveType = AF_None;
- if(WetGainHF[i] != 1.0f) voice->Send[i].Filters[c].ActiveType |= AF_LowPass;
- if(WetGainLF[i] != 1.0f) voice->Send[i].Filters[c].ActiveType |= AF_HighPass;
- ALfilterState_setParams(
- &voice->Send[i].Filters[c].LowPass, ALfilterType_HighShelf,
- WetGainHF[i], hfscale, calc_rcpQ_from_slope(WetGainHF[i], 0.75f)
- );
- ALfilterState_setParams(
- &voice->Send[i].Filters[c].HighPass, ALfilterType_LowShelf,
- WetGainLF[i], lfscale, calc_rcpQ_from_slope(WetGainLF[i], 0.75f)
- );
+ BiquadFilter_copyParams(&voice->Send[i].Params[c].LowPass,
+ &voice->Send[i].Params[0].LowPass);
+ BiquadFilter_copyParams(&voice->Send[i].Params[c].HighPass,
+ &voice->Send[i].Params[0].HighPass);
}
}
}
-ALvoid CalcSourceParams(ALvoice *voice, const ALsource *ALSource, const ALCcontext *ALContext)
+static void CalcNonAttnSourceParams(ALvoice *voice, const struct ALvoiceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
{
- ALCdevice *Device = ALContext->Device;
- aluVector Position, Velocity, Direction, SourceToListener;
- ALfloat InnerAngle,OuterAngle,Angle,Distance,ClampedDist;
- ALfloat MinVolume,MaxVolume,MinDist,MaxDist,Rolloff;
- ALfloat ConeVolume,ConeHF,SourceVolume,ListenerGain;
- ALfloat DopplerFactor, SpeedOfSound;
- ALfloat AirAbsorptionFactor;
- ALfloat RoomAirAbsorption[MAX_SENDS];
- ALbufferlistitem *BufferListItem;
- ALfloat Attenuation;
- ALfloat RoomAttenuation[MAX_SENDS];
- ALfloat MetersPerUnit;
- ALfloat RoomRolloffBase;
- ALfloat RoomRolloff[MAX_SENDS];
- ALfloat DecayDistance[MAX_SENDS];
- ALfloat DryGain;
- ALfloat DryGainHF;
- ALfloat DryGainLF;
- ALboolean DryGainHFAuto;
+ const ALCdevice *Device = ALContext->Device;
+ const ALlistener *Listener = ALContext->Listener;
+ ALfloat DryGain, DryGainHF, DryGainLF;
ALfloat WetGain[MAX_SENDS];
ALfloat WetGainHF[MAX_SENDS];
ALfloat WetGainLF[MAX_SENDS];
- ALboolean WetGainAuto;
- ALboolean WetGainHFAuto;
+ ALeffectslot *SendSlots[MAX_SENDS];
ALfloat Pitch;
- ALuint Frequency;
- ALint NumSends;
- ALint i, j;
+ ALsizei i;
- DryGainHF = 1.0f;
- DryGainLF = 1.0f;
- for(i = 0;i < MAX_SENDS;i++)
+ voice->Direct.Buffer = Device->Dry.Buffer;
+ voice->Direct.Channels = Device->Dry.NumChannels;
+ for(i = 0;i < Device->NumAuxSends;i++)
{
- WetGainHF[i] = 1.0f;
- WetGainLF[i] = 1.0f;
+ SendSlots[i] = props->Send[i].Slot;
+ if(!SendSlots[i] && i == 0)
+ SendSlots[i] = ALContext->DefaultSlot;
+ if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
+ {
+ SendSlots[i] = NULL;
+ voice->Send[i].Buffer = NULL;
+ voice->Send[i].Channels = 0;
+ }
+ else
+ {
+ voice->Send[i].Buffer = SendSlots[i]->WetBuffer;
+ voice->Send[i].Channels = SendSlots[i]->NumChannels;
+ }
}
- /* Get context/device properties */
- DopplerFactor = ALContext->DopplerFactor * ALSource->DopplerFactor;
- SpeedOfSound = ALContext->SpeedOfSound * ALContext->DopplerVelocity;
- NumSends = Device->NumAuxSends;
- Frequency = Device->Frequency;
-
- /* Get listener properties */
- ListenerGain = ALContext->Listener->Gain;
- MetersPerUnit = ALContext->Listener->MetersPerUnit;
-
- /* Get source properties */
- SourceVolume = ALSource->Gain;
- MinVolume = ALSource->MinGain;
- MaxVolume = ALSource->MaxGain;
- Pitch = ALSource->Pitch;
- Position = ALSource->Position;
- Direction = ALSource->Direction;
- Velocity = ALSource->Velocity;
- MinDist = ALSource->RefDistance;
- MaxDist = ALSource->MaxDistance;
- Rolloff = ALSource->RollOffFactor;
- InnerAngle = ALSource->InnerAngle;
- OuterAngle = ALSource->OuterAngle;
- AirAbsorptionFactor = ALSource->AirAbsorptionFactor;
- DryGainHFAuto = ALSource->DryGainHFAuto;
- WetGainAuto = ALSource->WetGainAuto;
- WetGainHFAuto = ALSource->WetGainHFAuto;
- RoomRolloffBase = ALSource->RoomRolloffFactor;
-
- voice->Direct.OutBuffer = Device->DryBuffer;
- voice->Direct.OutChannels = Device->NumChannels;
- for(i = 0;i < NumSends;i++)
+ /* Calculate the stepping value */
+ Pitch = (ALfloat)ALBuffer->Frequency/(ALfloat)Device->Frequency * props->Pitch;
+ if(Pitch > (ALfloat)MAX_PITCH)
+ voice->Step = MAX_PITCH<<FRACTIONBITS;
+ else
+ voice->Step = maxi(fastf2i(Pitch * FRACTIONONE), 1);
+ if(props->Resampler == BSinc24Resampler)
+ BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc24);
+ else if(props->Resampler == BSinc12Resampler)
+ BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc12);
+ voice->Resampler = SelectResampler(props->Resampler);
+
+ /* Calculate gains */
+ DryGain = clampf(props->Gain, props->MinGain, props->MaxGain);
+ DryGain *= props->Direct.Gain * Listener->Params.Gain;
+ DryGain = minf(DryGain, GAIN_MIX_MAX);
+ DryGainHF = props->Direct.GainHF;
+ DryGainLF = props->Direct.GainLF;
+ for(i = 0;i < Device->NumAuxSends;i++)
{
- ALeffectslot *Slot = ALSource->Send[i].Slot;
+ WetGain[i] = clampf(props->Gain, props->MinGain, props->MaxGain);
+ WetGain[i] *= props->Send[i].Gain * Listener->Params.Gain;
+ WetGain[i] = minf(WetGain[i], GAIN_MIX_MAX);
+ WetGainHF[i] = props->Send[i].GainHF;
+ WetGainLF[i] = props->Send[i].GainLF;
+ }
+
+ CalcPanningAndFilters(voice, 0.0f, 0.0f, 0.0f, 0.0f, DryGain, DryGainHF, DryGainLF, WetGain,
+ WetGainLF, WetGainHF, SendSlots, ALBuffer, props, Listener, Device);
+}
- if(!Slot && i == 0)
- Slot = Device->DefaultSlot;
- if(!Slot || Slot->EffectType == AL_EFFECT_NULL)
+static void CalcAttnSourceParams(ALvoice *voice, const struct ALvoiceProps *props, const ALbuffer *ALBuffer, const ALCcontext *ALContext)
+{
+ const ALCdevice *Device = ALContext->Device;
+ const ALlistener *Listener = ALContext->Listener;
+ const ALsizei NumSends = Device->NumAuxSends;
+ aluVector Position, Velocity, Direction, SourceToListener;
+ ALfloat Distance, ClampedDist, DopplerFactor;
+ ALeffectslot *SendSlots[MAX_SENDS];
+ ALfloat RoomRolloff[MAX_SENDS];
+ ALfloat DecayDistance[MAX_SENDS];
+ ALfloat DecayLFDistance[MAX_SENDS];
+ ALfloat DecayHFDistance[MAX_SENDS];
+ ALfloat DryGain, DryGainHF, DryGainLF;
+ ALfloat WetGain[MAX_SENDS];
+ ALfloat WetGainHF[MAX_SENDS];
+ ALfloat WetGainLF[MAX_SENDS];
+ bool directional;
+ ALfloat ev, az;
+ ALfloat spread;
+ ALfloat Pitch;
+ ALint i;
+
+ /* Set mixing buffers and get send parameters. */
+ voice->Direct.Buffer = Device->Dry.Buffer;
+ voice->Direct.Channels = Device->Dry.NumChannels;
+ for(i = 0;i < NumSends;i++)
+ {
+ SendSlots[i] = props->Send[i].Slot;
+ if(!SendSlots[i] && i == 0)
+ SendSlots[i] = ALContext->DefaultSlot;
+ if(!SendSlots[i] || SendSlots[i]->Params.EffectType == AL_EFFECT_NULL)
{
- Slot = NULL;
+ SendSlots[i] = NULL;
RoomRolloff[i] = 0.0f;
DecayDistance[i] = 0.0f;
- RoomAirAbsorption[i] = 1.0f;
+ DecayLFDistance[i] = 0.0f;
+ DecayHFDistance[i] = 0.0f;
}
- else if(Slot->AuxSendAuto)
+ else if(SendSlots[i]->Params.AuxSendAuto)
{
- RoomRolloff[i] = RoomRolloffBase;
- if(IsReverbEffect(Slot->EffectType))
- {
- RoomRolloff[i] += Slot->EffectProps.Reverb.RoomRolloffFactor;
- DecayDistance[i] = Slot->EffectProps.Reverb.DecayTime *
- SPEEDOFSOUNDMETRESPERSEC;
- RoomAirAbsorption[i] = Slot->EffectProps.Reverb.AirAbsorptionGainHF;
- }
- else
+ RoomRolloff[i] = SendSlots[i]->Params.RoomRolloff + props->RoomRolloffFactor;
+ /* Calculate the distances to where this effect's decay reaches
+ * -60dB.
+ */
+ DecayDistance[i] = SendSlots[i]->Params.DecayTime *
+ Listener->Params.ReverbSpeedOfSound;
+ DecayLFDistance[i] = DecayDistance[i] * SendSlots[i]->Params.DecayLFRatio;
+ DecayHFDistance[i] = DecayDistance[i] * SendSlots[i]->Params.DecayHFRatio;
+ if(SendSlots[i]->Params.DecayHFLimit)
{
- DecayDistance[i] = 0.0f;
- RoomAirAbsorption[i] = 1.0f;
+ ALfloat airAbsorption = SendSlots[i]->Params.AirAbsorptionGainHF;
+ if(airAbsorption < 1.0f)
+ {
+ /* Calculate the distance to where this effect's air
+ * absorption reaches -60dB, and limit the effect's HF
+ * decay distance (so it doesn't take any longer to decay
+ * than the air would allow).
+ */
+ ALfloat absorb_dist = log10f(REVERB_DECAY_GAIN) / log10f(airAbsorption);
+ DecayHFDistance[i] = minf(absorb_dist, DecayHFDistance[i]);
+ }
}
}
else
{
/* If the slot's auxiliary send auto is off, the data sent to the
* effect slot is the same as the dry path, sans filter effects */
- RoomRolloff[i] = Rolloff;
+ RoomRolloff[i] = props->RolloffFactor;
DecayDistance[i] = 0.0f;
- RoomAirAbsorption[i] = AIRABSORBGAINHF;
+ DecayLFDistance[i] = 0.0f;
+ DecayHFDistance[i] = 0.0f;
}
- if(!Slot || Slot->EffectType == AL_EFFECT_NULL)
- voice->Send[i].OutBuffer = NULL;
+ if(!SendSlots[i])
+ {
+ voice->Send[i].Buffer = NULL;
+ voice->Send[i].Channels = 0;
+ }
else
- voice->Send[i].OutBuffer = Slot->WetBuffer;
+ {
+ voice->Send[i].Buffer = SendSlots[i]->WetBuffer;
+ voice->Send[i].Channels = SendSlots[i]->NumChannels;
+ }
}
/* Transform source to listener space (convert to head relative) */
- if(ALSource->HeadRelative == AL_FALSE)
+ aluVectorSet(&Position, props->Position[0], props->Position[1], props->Position[2], 1.0f);
+ aluVectorSet(&Direction, props->Direction[0], props->Direction[1], props->Direction[2], 0.0f);
+ aluVectorSet(&Velocity, props->Velocity[0], props->Velocity[1], props->Velocity[2], 0.0f);
+ if(props->HeadRelative == AL_FALSE)
{
- const aluMatrixd *Matrix = &ALContext->Listener->Params.Matrix;
+ const aluMatrixf *Matrix = &Listener->Params.Matrix;
/* Transform source vectors */
- Position = aluMatrixdVector(Matrix, &Position);
- Velocity = aluMatrixdVector(Matrix, &Velocity);
- Direction = aluMatrixdVector(Matrix, &Direction);
+ Position = aluMatrixfVector(Matrix, &Position);
+ Velocity = aluMatrixfVector(Matrix, &Velocity);
+ Direction = aluMatrixfVector(Matrix, &Direction);
}
else
{
- const aluVector *lvelocity = &ALContext->Listener->Params.Velocity;
+ const aluVector *lvelocity = &Listener->Params.Velocity;
/* Offset the source velocity to be relative of the listener velocity */
Velocity.v[0] += lvelocity->v[0];
Velocity.v[1] += lvelocity->v[1];
Velocity.v[2] += lvelocity->v[2];
}
- aluNormalize(Direction.v);
+ directional = aluNormalize(Direction.v) > 0.0f;
SourceToListener.v[0] = -Position.v[0];
SourceToListener.v[1] = -Position.v[1];
SourceToListener.v[2] = -Position.v[2];
SourceToListener.v[3] = 0.0f;
Distance = aluNormalize(SourceToListener.v);
+ /* Initial source gain */
+ DryGain = props->Gain;
+ DryGainHF = 1.0f;
+ DryGainLF = 1.0f;
+ for(i = 0;i < NumSends;i++)
+ {
+ WetGain[i] = props->Gain;
+ WetGainHF[i] = 1.0f;
+ WetGainLF[i] = 1.0f;
+ }
+
/* Calculate distance attenuation */
ClampedDist = Distance;
- Attenuation = 1.0f;
- for(i = 0;i < NumSends;i++)
- RoomAttenuation[i] = 1.0f;
- switch(ALContext->SourceDistanceModel ? ALSource->DistanceModel :
- ALContext->DistanceModel)
+ switch(Listener->Params.SourceDistanceModel ?
+ props->DistanceModel : Listener->Params.DistanceModel)
{
case InverseDistanceClamped:
- ClampedDist = clampf(ClampedDist, MinDist, MaxDist);
- if(MaxDist < MinDist)
+ ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
+ if(props->MaxDistance < props->RefDistance)
break;
/*fall-through*/
case InverseDistance:
- if(MinDist > 0.0f)
+ if(!(props->RefDistance > 0.0f))
+ ClampedDist = props->RefDistance;
+ else
{
- ALfloat dist = lerp(MinDist, ClampedDist, Rolloff);
- if(dist > 0.0f) Attenuation = MinDist / dist;
+ ALfloat dist = lerp(props->RefDistance, ClampedDist, props->RolloffFactor);
+ if(dist > 0.0f) DryGain *= props->RefDistance / dist;
for(i = 0;i < NumSends;i++)
{
- dist = lerp(MinDist, ClampedDist, RoomRolloff[i]);
- if(dist > 0.0f) RoomAttenuation[i] = MinDist / dist;
+ dist = lerp(props->RefDistance, ClampedDist, RoomRolloff[i]);
+ if(dist > 0.0f) WetGain[i] *= props->RefDistance / dist;
}
}
break;
case LinearDistanceClamped:
- ClampedDist = clampf(ClampedDist, MinDist, MaxDist);
- if(MaxDist < MinDist)
+ ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
+ if(props->MaxDistance < props->RefDistance)
break;
/*fall-through*/
case LinearDistance:
- if(MaxDist != MinDist)
+ if(!(props->MaxDistance != props->RefDistance))
+ ClampedDist = props->RefDistance;
+ else
{
- Attenuation = 1.0f - (Rolloff*(ClampedDist-MinDist)/(MaxDist - MinDist));
- Attenuation = maxf(Attenuation, 0.0f);
+ ALfloat attn = props->RolloffFactor * (ClampedDist-props->RefDistance) /
+ (props->MaxDistance-props->RefDistance);
+ DryGain *= maxf(1.0f - attn, 0.0f);
for(i = 0;i < NumSends;i++)
{
- RoomAttenuation[i] = 1.0f - (RoomRolloff[i]*(ClampedDist-MinDist)/(MaxDist - MinDist));
- RoomAttenuation[i] = maxf(RoomAttenuation[i], 0.0f);
+ attn = RoomRolloff[i] * (ClampedDist-props->RefDistance) /
+ (props->MaxDistance-props->RefDistance);
+ WetGain[i] *= maxf(1.0f - attn, 0.0f);
}
}
break;
case ExponentDistanceClamped:
- ClampedDist = clampf(ClampedDist, MinDist, MaxDist);
- if(MaxDist < MinDist)
+ ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
+ if(props->MaxDistance < props->RefDistance)
break;
/*fall-through*/
case ExponentDistance:
- if(ClampedDist > 0.0f && MinDist > 0.0f)
+ if(!(ClampedDist > 0.0f && props->RefDistance > 0.0f))
+ ClampedDist = props->RefDistance;
+ else
{
- Attenuation = powf(ClampedDist/MinDist, -Rolloff);
+ DryGain *= powf(ClampedDist/props->RefDistance, -props->RolloffFactor);
for(i = 0;i < NumSends;i++)
- RoomAttenuation[i] = powf(ClampedDist/MinDist, -RoomRolloff[i]);
+ WetGain[i] *= powf(ClampedDist/props->RefDistance, -RoomRolloff[i]);
}
break;
case DisableDistance:
- ClampedDist = MinDist;
+ ClampedDist = props->RefDistance;
break;
}
- /* Source Gain + Attenuation */
- DryGain = SourceVolume * Attenuation;
- for(i = 0;i < NumSends;i++)
- WetGain[i] = SourceVolume * RoomAttenuation[i];
-
- /* Distance-based air absorption */
- if(AirAbsorptionFactor > 0.0f && ClampedDist > MinDist)
+ /* Calculate directional soundcones */
+ if(directional && props->InnerAngle < 360.0f)
{
- ALfloat meters = (ClampedDist-MinDist) * MetersPerUnit;
- DryGainHF *= powf(AIRABSORBGAINHF, AirAbsorptionFactor*meters);
- for(i = 0;i < NumSends;i++)
- WetGainHF[i] *= powf(RoomAirAbsorption[i], AirAbsorptionFactor*meters);
- }
+ ALfloat ConeVolume;
+ ALfloat ConeHF;
+ ALfloat Angle;
- if(WetGainAuto)
- {
- ALfloat ApparentDist = 1.0f/maxf(Attenuation, 0.00001f) - 1.0f;
-
- /* Apply a decay-time transformation to the wet path, based on the
- * attenuation of the dry path.
- *
- * Using the apparent distance, based on the distance attenuation, the
- * initial decay of the reverb effect is calculated and applied to the
- * wet path.
- */
- for(i = 0;i < NumSends;i++)
+ Angle = acosf(aluDotproduct(&Direction, &SourceToListener));
+ Angle = RAD2DEG(Angle * ConeScale * 2.0f);
+ if(!(Angle > props->InnerAngle))
{
- if(DecayDistance[i] > 0.0f)
- WetGain[i] *= powf(0.001f/*-60dB*/, ApparentDist/DecayDistance[i]);
+ ConeVolume = 1.0f;
+ ConeHF = 1.0f;
+ }
+ else if(Angle < props->OuterAngle)
+ {
+ ALfloat scale = ( Angle-props->InnerAngle) /
+ (props->OuterAngle-props->InnerAngle);
+ ConeVolume = lerp(1.0f, props->OuterGain, scale);
+ ConeHF = lerp(1.0f, props->OuterGainHF, scale);
+ }
+ else
+ {
+ ConeVolume = props->OuterGain;
+ ConeHF = props->OuterGainHF;
}
- }
-
- /* Calculate directional soundcones */
- Angle = RAD2DEG(acosf(aluDotproduct(&Direction, &SourceToListener)) * ConeScale) * 2.0f;
- if(Angle > InnerAngle && Angle <= OuterAngle)
- {
- ALfloat scale = (Angle-InnerAngle) / (OuterAngle-InnerAngle);
- ConeVolume = lerp(1.0f, ALSource->OuterGain, scale);
- ConeHF = lerp(1.0f, ALSource->OuterGainHF, scale);
- }
- else if(Angle > OuterAngle)
- {
- ConeVolume = ALSource->OuterGain;
- ConeHF = ALSource->OuterGainHF;
- }
- else
- {
- ConeVolume = 1.0f;
- ConeHF = 1.0f;
- }
- DryGain *= ConeVolume;
- if(WetGainAuto)
- {
- for(i = 0;i < NumSends;i++)
- WetGain[i] *= ConeVolume;
- }
- if(DryGainHFAuto)
- DryGainHF *= ConeHF;
- if(WetGainHFAuto)
- {
- for(i = 0;i < NumSends;i++)
- WetGainHF[i] *= ConeHF;
+ DryGain *= ConeVolume;
+ if(props->DryGainHFAuto)
+ DryGainHF *= ConeHF;
+ if(props->WetGainAuto)
+ {
+ for(i = 0;i < NumSends;i++)
+ WetGain[i] *= ConeVolume;
+ }
+ if(props->WetGainHFAuto)
+ {
+ for(i = 0;i < NumSends;i++)
+ WetGainHF[i] *= ConeHF;
+ }
}
- /* Clamp to Min/Max Gain */
- DryGain = clampf(DryGain, MinVolume, MaxVolume);
- for(i = 0;i < NumSends;i++)
- WetGain[i] = clampf(WetGain[i], MinVolume, MaxVolume);
-
/* Apply gain and frequency filters */
- DryGain *= ALSource->Direct.Gain * ListenerGain;
- DryGainHF *= ALSource->Direct.GainHF;
- DryGainLF *= ALSource->Direct.GainLF;
+ DryGain = clampf(DryGain, props->MinGain, props->MaxGain);
+ DryGain = minf(DryGain*props->Direct.Gain*Listener->Params.Gain, GAIN_MIX_MAX);
+ DryGainHF *= props->Direct.GainHF;
+ DryGainLF *= props->Direct.GainLF;
for(i = 0;i < NumSends;i++)
{
- WetGain[i] *= ALSource->Send[i].Gain * ListenerGain;
- WetGainHF[i] *= ALSource->Send[i].GainHF;
- WetGainLF[i] *= ALSource->Send[i].GainLF;
+ WetGain[i] = clampf(WetGain[i], props->MinGain, props->MaxGain);
+ WetGain[i] = minf(WetGain[i]*props->Send[i].Gain*Listener->Params.Gain, GAIN_MIX_MAX);
+ WetGainHF[i] *= props->Send[i].GainHF;
+ WetGainLF[i] *= props->Send[i].GainLF;
}
- /* Calculate velocity-based doppler effect */
- if(DopplerFactor > 0.0f)
+ /* Distance-based air absorption and initial send decay. */
+ if(ClampedDist > props->RefDistance && props->RolloffFactor > 0.0f)
{
- const aluVector *lvelocity = &ALContext->Listener->Params.Velocity;
- ALfloat VSS, VLS;
-
- if(SpeedOfSound < 1.0f)
+ ALfloat meters_base = (ClampedDist-props->RefDistance) * props->RolloffFactor *
+ Listener->Params.MetersPerUnit;
+ if(props->AirAbsorptionFactor > 0.0f)
{
- DopplerFactor *= 1.0f/SpeedOfSound;
- SpeedOfSound = 1.0f;
+ ALfloat hfattn = powf(AIRABSORBGAINHF, meters_base * props->AirAbsorptionFactor);
+ DryGainHF *= hfattn;
+ for(i = 0;i < NumSends;i++)
+ WetGainHF[i] *= hfattn;
}
- VSS = aluDotproduct(&Velocity, &SourceToListener) * DopplerFactor;
- VLS = aluDotproduct(lvelocity, &SourceToListener) * DopplerFactor;
-
- Pitch *= clampf(SpeedOfSound-VLS, 1.0f, SpeedOfSound*2.0f - 1.0f) /
- clampf(SpeedOfSound-VSS, 1.0f, SpeedOfSound*2.0f - 1.0f);
- }
-
- BufferListItem = ATOMIC_LOAD(&ALSource->queue);
- while(BufferListItem != NULL)
- {
- ALbuffer *ALBuffer;
- if((ALBuffer=BufferListItem->buffer) != NULL)
+ if(props->WetGainAuto)
{
- /* Calculate fixed-point stepping value, based on the pitch, buffer
- * frequency, and output frequency. */
- Pitch = Pitch * ALBuffer->Frequency / Frequency;
- if(Pitch > (ALfloat)MAX_PITCH)
- voice->Step = MAX_PITCH<<FRACTIONBITS;
- else
- voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
- BsincPrepare(voice->Step, &voice->SincState);
+ /* Apply a decay-time transformation to the wet path, based on the
+ * source distance in meters. The initial decay of the reverb
+ * effect is calculated and applied to the wet path.
+ */
+ for(i = 0;i < NumSends;i++)
+ {
+ ALfloat gain, gainhf, gainlf;
- break;
+ if(!(DecayDistance[i] > 0.0f))
+ continue;
+
+ gain = powf(REVERB_DECAY_GAIN, meters_base/DecayDistance[i]);
+ WetGain[i] *= gain;
+ /* Yes, the wet path's air absorption is applied with
+ * WetGainAuto on, rather than WetGainHFAuto.
+ */
+ if(gain > 0.0f)
+ {
+ gainhf = powf(REVERB_DECAY_GAIN, meters_base/DecayHFDistance[i]);
+ WetGainHF[i] *= minf(gainhf / gain, 1.0f);
+ gainlf = powf(REVERB_DECAY_GAIN, meters_base/DecayLFDistance[i]);
+ WetGainLF[i] *= minf(gainlf / gain, 1.0f);
+ }
+ }
}
- BufferListItem = BufferListItem->next;
}
- if(Device->Hrtf_Mode == FullHrtf)
+
+ /* Initial source pitch */
+ Pitch = props->Pitch;
+
+ /* Calculate velocity-based doppler effect */
+ DopplerFactor = props->DopplerFactor * Listener->Params.DopplerFactor;
+ if(DopplerFactor > 0.0f)
{
- /* Full HRTF rendering. Skip the virtual channels and render to the
- * real outputs.
- */
- aluVector dir = {{ 0.0f, 0.0f, -1.0f, 0.0f }};
- ALfloat ev = 0.0f, az = 0.0f;
- ALfloat radius = ALSource->Radius;
- ALfloat dirfact = 1.0f;
+ const aluVector *lvelocity = &Listener->Params.Velocity;
+ const ALfloat SpeedOfSound = Listener->Params.SpeedOfSound;
+ ALfloat vss, vls;
- voice->Direct.OutBuffer += voice->Direct.OutChannels;
- voice->Direct.OutChannels = 2;
+ vss = aluDotproduct(&Velocity, &SourceToListener) * DopplerFactor;
+ vls = aluDotproduct(lvelocity, &SourceToListener) * DopplerFactor;
- if(Distance > FLT_EPSILON)
+ if(!(vls < SpeedOfSound))
{
- dir.v[0] = -SourceToListener.v[0];
- dir.v[1] = -SourceToListener.v[1];
- dir.v[2] = -SourceToListener.v[2] * ZScale;
-
- /* Calculate elevation and azimuth only when the source is not at
- * the listener. This prevents +0 and -0 Z from producing
- * inconsistent panning. Also, clamp Y in case FP precision errors
- * cause it to land outside of -1..+1. */
- ev = asinf(clampf(dir.v[1], -1.0f, 1.0f));
- az = atan2f(dir.v[0], -dir.v[2]);
- }
- if(radius > 0.0f)
- {
- if(radius >= Distance)
- dirfact *= Distance / radius * 0.5f;
- else
- dirfact *= 1.0f - (asinf(radius / Distance) / F_PI);
+ /* Listener moving away from the source at the speed of sound.
+ * Sound waves can't catch it.
+ */
+ Pitch = 0.0f;
}
-
- /* Check to see if the HRIR is already moving. */
- if(voice->Direct.Moving)
+ else if(!(vss < SpeedOfSound))
{
- ALfloat delta;
- delta = CalcFadeTime(voice->Direct.LastGain, DryGain,
- &voice->Direct.LastDir, &dir);
- /* If the delta is large enough, get the moving HRIR target
- * coefficients, target delays, steppping values, and counter.
+ /* Source moving toward the listener at the speed of sound. Sound
+ * waves bunch up to extreme frequencies.
*/
- if(delta > 0.000015f)
- {
- ALuint counter = GetMovingHrtfCoeffs(Device->Hrtf,
- ev, az, dirfact, DryGain, delta, voice->Direct.Counter,
- voice->Direct.Hrtf[0].Params.Coeffs, voice->Direct.Hrtf[0].Params.Delay,
- voice->Direct.Hrtf[0].Params.CoeffStep, voice->Direct.Hrtf[0].Params.DelayStep
- );
- voice->Direct.Counter = counter;
- voice->Direct.LastGain = DryGain;
- voice->Direct.LastDir = dir;
- }
+ Pitch = HUGE_VALF;
}
else
{
- /* Get the initial (static) HRIR coefficients and delays. */
- GetLerpedHrtfCoeffs(Device->Hrtf, ev, az, dirfact, DryGain,
- voice->Direct.Hrtf[0].Params.Coeffs,
- voice->Direct.Hrtf[0].Params.Delay);
- voice->Direct.Counter = 0;
- voice->Direct.Moving = AL_TRUE;
- voice->Direct.LastGain = DryGain;
- voice->Direct.LastDir = dir;
+ /* Source and listener movement is nominal. Calculate the proper
+ * doppler shift.
+ */
+ Pitch *= (SpeedOfSound-vls) / (SpeedOfSound-vss);
}
-
- voice->IsHrtf = AL_TRUE;
}
+
+ /* Adjust pitch based on the buffer and output frequencies, and calculate
+ * fixed-point stepping value.
+ */
+ Pitch *= (ALfloat)ALBuffer->Frequency/(ALfloat)Device->Frequency;
+ if(Pitch > (ALfloat)MAX_PITCH)
+ voice->Step = MAX_PITCH<<FRACTIONBITS;
else
+ voice->Step = maxi(fastf2i(Pitch * FRACTIONONE), 1);
+ if(props->Resampler == BSinc24Resampler)
+ BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc24);
+ else if(props->Resampler == BSinc12Resampler)
+ BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc12);
+ voice->Resampler = SelectResampler(props->Resampler);
+
+ if(Distance > 0.0f)
{
- /* Basic or no HRTF rendering. Use normal panning to the output. */
- MixGains *gains = voice->Direct.Gains[0];
- ALfloat dir[3] = { 0.0f, 0.0f, -1.0f };
- ALfloat radius = ALSource->Radius;
- ALfloat Target[MAX_OUTPUT_CHANNELS];
+ /* Clamp Y, in case rounding errors caused it to end up outside of
+ * -1...+1.
+ */
+ ev = asinf(clampf(-SourceToListener.v[1], -1.0f, 1.0f));
+ /* Double negation on Z cancels out; negate once for changing source-
+ * to-listener to listener-to-source, and again for right-handed coords
+ * with -Z in front.
+ */
+ az = atan2f(-SourceToListener.v[0], SourceToListener.v[2]*ZScale);
+ }
+ else
+ ev = az = 0.0f;
- /* Get the localized direction, and compute panned gains. */
- if(Distance > FLT_EPSILON)
- {
- dir[0] = -SourceToListener.v[0];
- dir[1] = -SourceToListener.v[1];
- dir[2] = -SourceToListener.v[2] * ZScale;
- }
- if(radius > 0.0f)
- {
- ALfloat dirfact;
- if(radius >= Distance)
- dirfact = Distance / radius * 0.5f;
- else
- dirfact = 1.0f - (asinf(radius / Distance) / F_PI);
- dir[0] *= dirfact;
- dir[1] *= dirfact;
- dir[2] *= dirfact;
- }
- ComputeDirectionalGains(Device, dir, DryGain, Target);
+ if(props->Radius > Distance)
+ spread = F_TAU - Distance/props->Radius*F_PI;
+ else if(Distance > 0.0f)
+ spread = asinf(props->Radius / Distance) * 2.0f;
+ else
+ spread = 0.0f;
+
+ CalcPanningAndFilters(voice, az, ev, Distance, spread, DryGain, DryGainHF, DryGainLF, WetGain,
+ WetGainLF, WetGainHF, SendSlots, ALBuffer, props, Listener, Device);
+}
- for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
- gains[j].Target = Target[j];
- UpdateDryStepping(&voice->Direct, 1, (voice->Direct.Moving ? 64 : 0));
- voice->Direct.Moving = AL_TRUE;
+static void CalcSourceParams(ALvoice *voice, ALCcontext *context, bool force)
+{
+ ALbufferlistitem *BufferListItem;
+ struct ALvoiceProps *props;
- voice->IsHrtf = AL_FALSE;
- }
- for(i = 0;i < NumSends;i++)
- {
- voice->Send[i].Gains[0].Target = WetGain[i];
- UpdateWetStepping(&voice->Send[i], 1, (voice->Send[i].Moving ? 64 : 0));
- voice->Send[i].Moving = AL_TRUE;
- }
+ props = ATOMIC_EXCHANGE_PTR(&voice->Update, NULL, almemory_order_acq_rel);
+ if(!props && !force) return;
+ if(props)
{
- ALfloat hfscale = ALSource->Direct.HFReference / Frequency;
- ALfloat lfscale = ALSource->Direct.LFReference / Frequency;
- DryGainHF = maxf(DryGainHF, 0.0001f);
- DryGainLF = maxf(DryGainLF, 0.0001f);
- voice->Direct.Filters[0].ActiveType = AF_None;
- if(DryGainHF != 1.0f) voice->Direct.Filters[0].ActiveType |= AF_LowPass;
- if(DryGainLF != 1.0f) voice->Direct.Filters[0].ActiveType |= AF_HighPass;
- ALfilterState_setParams(
- &voice->Direct.Filters[0].LowPass, ALfilterType_HighShelf,
- DryGainHF, hfscale, calc_rcpQ_from_slope(DryGainHF, 0.75f)
- );
- ALfilterState_setParams(
- &voice->Direct.Filters[0].HighPass, ALfilterType_LowShelf,
- DryGainLF, lfscale, calc_rcpQ_from_slope(DryGainLF, 0.75f)
+ memcpy(voice->Props, props,
+ FAM_SIZE(struct ALvoiceProps, Send, context->Device->NumAuxSends)
);
+
+ ATOMIC_REPLACE_HEAD(struct ALvoiceProps*, &context->FreeVoiceProps, props);
}
- for(i = 0;i < NumSends;i++)
+ props = voice->Props;
+
+ BufferListItem = ATOMIC_LOAD(&voice->current_buffer, almemory_order_relaxed);
+ while(BufferListItem != NULL)
{
- ALfloat hfscale = ALSource->Send[i].HFReference / Frequency;
- ALfloat lfscale = ALSource->Send[i].LFReference / Frequency;
- WetGainHF[i] = maxf(WetGainHF[i], 0.0001f);
- WetGainLF[i] = maxf(WetGainLF[i], 0.0001f);
- voice->Send[i].Filters[0].ActiveType = AF_None;
- if(WetGainHF[i] != 1.0f) voice->Send[i].Filters[0].ActiveType |= AF_LowPass;
- if(WetGainLF[i] != 1.0f) voice->Send[i].Filters[0].ActiveType |= AF_HighPass;
- ALfilterState_setParams(
- &voice->Send[i].Filters[0].LowPass, ALfilterType_HighShelf,
- WetGainHF[i], hfscale, calc_rcpQ_from_slope(WetGainHF[i], 0.75f)
- );
- ALfilterState_setParams(
- &voice->Send[i].Filters[0].HighPass, ALfilterType_LowShelf,
- WetGainLF[i], lfscale, calc_rcpQ_from_slope(WetGainLF[i], 0.75f)
- );
+ const ALbuffer *buffer = NULL;
+ ALsizei i = 0;
+ while(!buffer && i < BufferListItem->num_buffers)
+ buffer = BufferListItem->buffers[i];
+ if(LIKELY(buffer))
+ {
+ if(props->SpatializeMode == SpatializeOn ||
+ (props->SpatializeMode == SpatializeAuto && buffer->FmtChannels == FmtMono))
+ CalcAttnSourceParams(voice, props, buffer, context);
+ else
+ CalcNonAttnSourceParams(voice, props, buffer, context);
+ break;
+ }
+ BufferListItem = ATOMIC_LOAD(&BufferListItem->next, almemory_order_acquire);
}
}
-void UpdateContextSources(ALCcontext *ctx)
+static void ProcessParamUpdates(ALCcontext *ctx, const struct ALeffectslotArray *slots)
{
- ALvoice *voice, *voice_end;
+ ALvoice **voice, **voice_end;
ALsource *source;
+ ALsizei i;
- if(ATOMIC_EXCHANGE(ALenum, &ctx->UpdateSources, AL_FALSE))
+ IncrementRef(&ctx->UpdateCount);
+ if(!ATOMIC_LOAD(&ctx->HoldUpdates, almemory_order_acquire))
{
- CalcListenerParams(ctx->Listener);
+ bool cforce = CalcContextParams(ctx);
+ bool force = CalcListenerParams(ctx) | cforce;
+ for(i = 0;i < slots->count;i++)
+ force |= CalcEffectSlotParams(slots->slot[i], ctx, cforce);
voice = ctx->Voices;
voice_end = voice + ctx->VoiceCount;
for(;voice != voice_end;++voice)
{
- if(!(source=voice->Source)) continue;
- if(source->state != AL_PLAYING && source->state != AL_PAUSED)
- voice->Source = NULL;
- else
- {
- ATOMIC_STORE(&source->NeedsUpdate, AL_FALSE);
- voice->Update(voice, source, ctx);
- }
+ source = ATOMIC_LOAD(&(*voice)->Source, almemory_order_acquire);
+ if(source) CalcSourceParams(*voice, ctx, force);
}
}
- else
+ IncrementRef(&ctx->UpdateCount);
+}
+
+
+static void ApplyStablizer(FrontStablizer *Stablizer, ALfloat (*restrict Buffer)[BUFFERSIZE],
+ int lidx, int ridx, int cidx, ALsizei SamplesToDo,
+ ALsizei NumChannels)
+{
+ ALfloat (*restrict lsplit)[BUFFERSIZE] = ASSUME_ALIGNED(Stablizer->LSplit, 16);
+ ALfloat (*restrict rsplit)[BUFFERSIZE] = ASSUME_ALIGNED(Stablizer->RSplit, 16);
+ ALsizei i;
+
+ /* Apply an all-pass to all channels, except the front-left and front-
+ * right, so they maintain the same relative phase.
+ */
+ for(i = 0;i < NumChannels;i++)
{
- voice = ctx->Voices;
- voice_end = voice + ctx->VoiceCount;
- for(;voice != voice_end;++voice)
+ if(i == lidx || i == ridx)
+ continue;
+ splitterap_process(&Stablizer->APFilter[i], Buffer[i], SamplesToDo);
+ }
+
+ bandsplit_process(&Stablizer->LFilter, lsplit[1], lsplit[0], Buffer[lidx], SamplesToDo);
+ bandsplit_process(&Stablizer->RFilter, rsplit[1], rsplit[0], Buffer[ridx], SamplesToDo);
+
+ for(i = 0;i < SamplesToDo;i++)
+ {
+ ALfloat lfsum, hfsum;
+ ALfloat m, s, c;
+
+ lfsum = lsplit[0][i] + rsplit[0][i];
+ hfsum = lsplit[1][i] + rsplit[1][i];
+ s = lsplit[0][i] + lsplit[1][i] - rsplit[0][i] - rsplit[1][i];
+
+ /* This pans the separate low- and high-frequency sums between being on
+ * the center channel and the left/right channels. The low-frequency
+ * sum is 1/3rd toward center (2/3rds on left/right) and the high-
+ * frequency sum is 1/4th toward center (3/4ths on left/right). These
+ * values can be tweaked.
+ */
+ m = lfsum*cosf(1.0f/3.0f * F_PI_2) + hfsum*cosf(1.0f/4.0f * F_PI_2);
+ c = lfsum*sinf(1.0f/3.0f * F_PI_2) + hfsum*sinf(1.0f/4.0f * F_PI_2);
+
+ /* The generated center channel signal adds to the existing signal,
+ * while the modified left and right channels replace.
+ */
+ Buffer[lidx][i] = (m + s) * 0.5f;
+ Buffer[ridx][i] = (m - s) * 0.5f;
+ Buffer[cidx][i] += c * 0.5f;
+ }
+}
+
+static void ApplyDistanceComp(ALfloat (*restrict Samples)[BUFFERSIZE], DistanceComp *distcomp,
+ ALfloat *restrict Values, ALsizei SamplesToDo, ALsizei numchans)
+{
+ ALsizei i, c;
+
+ Values = ASSUME_ALIGNED(Values, 16);
+ for(c = 0;c < numchans;c++)
+ {
+ ALfloat *restrict inout = ASSUME_ALIGNED(Samples[c], 16);
+ const ALfloat gain = distcomp[c].Gain;
+ const ALsizei base = distcomp[c].Length;
+ ALfloat *restrict distbuf = ASSUME_ALIGNED(distcomp[c].Buffer, 16);
+
+ if(base == 0)
{
- if(!(source=voice->Source)) continue;
- if(source->state != AL_PLAYING && source->state != AL_PAUSED)
- voice->Source = NULL;
- else if(ATOMIC_EXCHANGE(ALenum, &source->NeedsUpdate, AL_FALSE))
- voice->Update(voice, source, ctx);
+ if(gain < 1.0f)
+ {
+ for(i = 0;i < SamplesToDo;i++)
+ inout[i] *= gain;
+ }
+ continue;
}
+
+ if(LIKELY(SamplesToDo >= base))
+ {
+ for(i = 0;i < base;i++)
+ Values[i] = distbuf[i];
+ for(;i < SamplesToDo;i++)
+ Values[i] = inout[i-base];
+ memcpy(distbuf, &inout[SamplesToDo-base], base*sizeof(ALfloat));
+ }
+ else
+ {
+ for(i = 0;i < SamplesToDo;i++)
+ Values[i] = distbuf[i];
+ memmove(distbuf, distbuf+SamplesToDo, (base-SamplesToDo)*sizeof(ALfloat));
+ memcpy(distbuf+base-SamplesToDo, inout, SamplesToDo*sizeof(ALfloat));
+ }
+ for(i = 0;i < SamplesToDo;i++)
+ inout[i] = Values[i]*gain;
}
}
-
-/* Specialized function to clamp to [-1, +1] with only one branch. This also
- * converts NaN to 0. */
-static inline ALfloat aluClampf(ALfloat val)
+static void ApplyDither(ALfloat (*restrict Samples)[BUFFERSIZE], ALuint *dither_seed,
+ const ALfloat quant_scale, const ALsizei SamplesToDo,
+ const ALsizei numchans)
{
- if(fabsf(val) <= 1.0f) return val;
- return (ALfloat)((0.0f < val) - (val < 0.0f));
+ const ALfloat invscale = 1.0f / quant_scale;
+ ALuint seed = *dither_seed;
+ ALsizei c, i;
+
+ ASSUME(numchans > 0);
+ ASSUME(SamplesToDo > 0);
+
+ /* Dithering. Step 1, generate whitenoise (uniform distribution of random
+ * values between -1 and +1). Step 2 is to add the noise to the samples,
+ * before rounding and after scaling up to the desired quantization depth.
+ */
+ for(c = 0;c < numchans;c++)
+ {
+ ALfloat *restrict samples = Samples[c];
+ for(i = 0;i < SamplesToDo;i++)
+ {
+ ALfloat val = samples[i] * quant_scale;
+ ALuint rng0 = dither_rng(&seed);
+ ALuint rng1 = dither_rng(&seed);
+ val += (ALfloat)(rng0*(1.0/UINT_MAX) - rng1*(1.0/UINT_MAX));
+ samples[i] = fast_roundf(val) * invscale;
+ }
+ }
+ *dither_seed = seed;
}
-static inline ALfloat aluF2F(ALfloat val)
-{ return val; }
-static inline ALint aluF2I(ALfloat val)
+static inline ALfloat Conv_ALfloat(ALfloat val)
+{ return val; }
+static inline ALint Conv_ALint(ALfloat val)
{
- /* Floats only have a 24-bit mantissa, so [-16777215, +16777215] is the max
- * integer range normalized floats can be safely converted to.
+ /* Floats have a 23-bit mantissa. There is an implied 1 bit in the mantissa
+ * along with the sign bit, giving 25 bits total, so [-16777216, +16777216]
+ * is the max value a normalized float can be scaled to before losing
+ * precision.
*/
- return fastf2i(aluClampf(val)*16777215.0f)<<7;
+ return fastf2i(clampf(val*16777216.0f, -16777216.0f, 16777215.0f))<<7;
}
-static inline ALuint aluF2UI(ALfloat val)
-{ return aluF2I(val)+2147483648u; }
-
-static inline ALshort aluF2S(ALfloat val)
-{ return fastf2i(aluClampf(val)*32767.0f); }
-static inline ALushort aluF2US(ALfloat val)
-{ return aluF2S(val)+32768; }
-
-static inline ALbyte aluF2B(ALfloat val)
-{ return fastf2i(aluClampf(val)*127.0f); }
-static inline ALubyte aluF2UB(ALfloat val)
-{ return aluF2B(val)+128; }
-
-#define DECL_TEMPLATE(T, func) \
-static void Write_##T(ALfloatBUFFERSIZE *InBuffer, ALvoid *OutBuffer, \
- ALuint SamplesToDo, ALuint numchans) \
+static inline ALshort Conv_ALshort(ALfloat val)
+{ return fastf2i(clampf(val*32768.0f, -32768.0f, 32767.0f)); }
+static inline ALbyte Conv_ALbyte(ALfloat val)
+{ return fastf2i(clampf(val*128.0f, -128.0f, 127.0f)); }
+
+/* Define unsigned output variations. */
+#define DECL_TEMPLATE(T, func, O) \
+static inline T Conv_##T(ALfloat val) { return func(val)+O; }
+
+DECL_TEMPLATE(ALubyte, Conv_ALbyte, 128)
+DECL_TEMPLATE(ALushort, Conv_ALshort, 32768)
+DECL_TEMPLATE(ALuint, Conv_ALint, 2147483648u)
+
+#undef DECL_TEMPLATE
+
+#define DECL_TEMPLATE(T, A) \
+static void Write##A(const ALfloat (*restrict InBuffer)[BUFFERSIZE], \
+ ALvoid *OutBuffer, ALsizei Offset, ALsizei SamplesToDo, \
+ ALsizei numchans) \
{ \
- ALuint i, j; \
+ ALsizei i, j; \
+ \
+ ASSUME(numchans > 0); \
+ ASSUME(SamplesToDo > 0); \
+ \
for(j = 0;j < numchans;j++) \
{ \
- const ALfloat *in = InBuffer[j]; \
- T *restrict out = (T*)OutBuffer + j; \
+ const ALfloat *restrict in = ASSUME_ALIGNED(InBuffer[j], 16); \
+ T *restrict out = (T*)OutBuffer + Offset*numchans + j; \
+ \
for(i = 0;i < SamplesToDo;i++) \
- out[i*numchans] = func(in[i]); \
+ out[i*numchans] = Conv_##T(in[i]); \
} \
}
-DECL_TEMPLATE(ALfloat, aluF2F)
-DECL_TEMPLATE(ALuint, aluF2UI)
-DECL_TEMPLATE(ALint, aluF2I)
-DECL_TEMPLATE(ALushort, aluF2US)
-DECL_TEMPLATE(ALshort, aluF2S)
-DECL_TEMPLATE(ALubyte, aluF2UB)
-DECL_TEMPLATE(ALbyte, aluF2B)
+DECL_TEMPLATE(ALfloat, F32)
+DECL_TEMPLATE(ALuint, UI32)
+DECL_TEMPLATE(ALint, I32)
+DECL_TEMPLATE(ALushort, UI16)
+DECL_TEMPLATE(ALshort, I16)
+DECL_TEMPLATE(ALubyte, UI8)
+DECL_TEMPLATE(ALbyte, I8)
#undef DECL_TEMPLATE
-ALvoid aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size)
+void aluMixData(ALCdevice *device, ALvoid *OutBuffer, ALsizei NumSamples)
{
- ALuint SamplesToDo;
- ALvoice *voice, *voice_end;
- ALeffectslot *slot;
- ALsource *source;
+ ALsizei SamplesToDo;
+ ALsizei SamplesDone;
ALCcontext *ctx;
- FPUCtl oldMode;
- ALuint i, c;
+ ALsizei i, c;
- SetMixerFPUMode(&oldMode);
-
- while(size > 0)
+ START_MIXER_MODE();
+ for(SamplesDone = 0;SamplesDone < NumSamples;)
{
- ALfloat (*OutBuffer)[BUFFERSIZE];
- ALuint OutChannels;
+ SamplesToDo = mini(NumSamples-SamplesDone, BUFFERSIZE);
+ for(c = 0;c < device->Dry.NumChannels;c++)
+ memset(device->Dry.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
+ if(device->Dry.Buffer != device->FOAOut.Buffer)
+ for(c = 0;c < device->FOAOut.NumChannels;c++)
+ memset(device->FOAOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
+ if(device->Dry.Buffer != device->RealOut.Buffer)
+ for(c = 0;c < device->RealOut.NumChannels;c++)
+ memset(device->RealOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
IncrementRef(&device->MixCount);
- OutBuffer = device->DryBuffer;
- OutChannels = device->NumChannels;
-
- SamplesToDo = minu(size, BUFFERSIZE);
- for(c = 0;c < OutChannels;c++)
- memset(OutBuffer[c], 0, SamplesToDo*sizeof(ALfloat));
- if(device->Hrtf)
+ ctx = ATOMIC_LOAD(&device->ContextList, almemory_order_acquire);
+ while(ctx)
{
- /* Set OutBuffer/OutChannels to correspond to the actual output
- * with HRTF. Make sure to clear them too. */
- OutBuffer += OutChannels;
- OutChannels = 2;
- for(c = 0;c < OutChannels;c++)
- memset(OutBuffer[c], 0, SamplesToDo*sizeof(ALfloat));
- }
+ const struct ALeffectslotArray *auxslots;
- V0(device->Backend,lock)();
-
- if((slot=device->DefaultSlot) != NULL)
- {
- if(ATOMIC_EXCHANGE(ALenum, &slot->NeedsUpdate, AL_FALSE))
- V(slot->EffectState,update)(device, slot);
- memset(slot->WetBuffer[0], 0, SamplesToDo*sizeof(ALfloat));
- }
+ auxslots = ATOMIC_LOAD(&ctx->ActiveAuxSlots, almemory_order_acquire);
+ ProcessParamUpdates(ctx, auxslots);
- ctx = ATOMIC_LOAD(&device->ContextList);
- while(ctx)
- {
- if(!ctx->DeferUpdates)
- {
- UpdateContextSources(ctx);
-#define UPDATE_SLOT(iter) do { \
- if(ATOMIC_EXCHANGE(ALenum, &(*iter)->NeedsUpdate, AL_FALSE)) \
- V((*iter)->EffectState,update)(device, *iter); \
- memset((*iter)->WetBuffer[0], 0, SamplesToDo*sizeof(ALfloat)); \
-} while(0)
- VECTOR_FOR_EACH(ALeffectslot*, ctx->ActiveAuxSlots, UPDATE_SLOT);
-#undef UPDATE_SLOT
- }
- else
+ for(i = 0;i < auxslots->count;i++)
{
-#define CLEAR_WET_BUFFER(iter) memset((*iter)->WetBuffer[0], 0, SamplesToDo*sizeof(ALfloat))
- VECTOR_FOR_EACH(ALeffectslot*, ctx->ActiveAuxSlots, CLEAR_WET_BUFFER);
-#undef CLEAR_WET_BUFFER
+ ALeffectslot *slot = auxslots->slot[i];
+ for(c = 0;c < slot->NumChannels;c++)
+ memset(slot->WetBuffer[c], 0, SamplesToDo*sizeof(ALfloat));
}
/* source processing */
- voice = ctx->Voices;
- voice_end = voice + ctx->VoiceCount;
- for(;voice != voice_end;++voice)
+ for(i = 0;i < ctx->VoiceCount;i++)
{
- source = voice->Source;
- if(source && source->state == AL_PLAYING)
- MixSource(voice, source, device, SamplesToDo);
+ ALvoice *voice = ctx->Voices[i];
+ ALsource *source = ATOMIC_LOAD(&voice->Source, almemory_order_acquire);
+ if(source && ATOMIC_LOAD(&voice->Playing, almemory_order_relaxed) &&
+ voice->Step > 0)
+ {
+ if(!MixSource(voice, source->id, ctx, SamplesToDo))
+ {
+ ATOMIC_STORE(&voice->Source, NULL, almemory_order_relaxed);
+ ATOMIC_STORE(&voice->Playing, false, almemory_order_release);
+ SendSourceStoppedEvent(ctx, source->id);
+ }
+ }
}
/* effect slot processing */
-#define PROCESS_SLOT(iter) V((*iter)->EffectState,process)( \
- SamplesToDo, (*iter)->WetBuffer[0], device->DryBuffer, device->NumChannels \
-);
- VECTOR_FOR_EACH(ALeffectslot*, ctx->ActiveAuxSlots, PROCESS_SLOT);
-#undef PROCESS_SLOT
+ for(i = 0;i < auxslots->count;i++)
+ {
+ const ALeffectslot *slot = auxslots->slot[i];
+ ALeffectState *state = slot->Params.EffectState;
+ V(state,process)(SamplesToDo, slot->WetBuffer, state->OutBuffer,
+ state->OutChannels);
+ }
- ctx = ctx->next;
+ ctx = ATOMIC_LOAD(&ctx->next, almemory_order_relaxed);
}
- if((slot=device->DefaultSlot) != NULL)
- V(slot->EffectState,process)(
- SamplesToDo, slot->WetBuffer[0], device->DryBuffer, device->NumChannels
- );
-
/* Increment the clock time. Every second's worth of samples is
* converted and added to clock base so that large sample counts don't
* overflow during conversion. This also guarantees an exact, stable
@@ -1488,104 +1815,109 @@ ALvoid aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size)
device->SamplesDone += SamplesToDo;
device->ClockBase += (device->SamplesDone/device->Frequency) * DEVICE_CLOCK_RES;
device->SamplesDone %= device->Frequency;
- V0(device->Backend,unlock)();
+ IncrementRef(&device->MixCount);
- if(device->Hrtf)
- {
- HrtfMixerFunc HrtfMix = SelectHrtfMixer();
- ALuint irsize = GetHrtfIrSize(device->Hrtf);
- for(c = 0;c < device->NumChannels;c++)
- HrtfMix(OutBuffer, device->DryBuffer[c], 0, device->Hrtf_Offset,
- 0, irsize, &device->Hrtf_Params[c], &device->Hrtf_State[c],
- SamplesToDo
- );
- device->Hrtf_Offset += SamplesToDo;
- }
- else if(device->Bs2b)
+ /* Apply post-process for finalizing the Dry mix to the RealOut
+ * (Ambisonic decode, UHJ encode, etc).
+ */
+ if(LIKELY(device->PostProcess))
+ device->PostProcess(device, SamplesToDo);
+
+ if(device->Stablizer)
{
- /* Apply binaural/crossfeed filter */
- for(i = 0;i < SamplesToDo;i++)
- {
- float samples[2];
- samples[0] = device->DryBuffer[0][i];
- samples[1] = device->DryBuffer[1][i];
- bs2b_cross_feed(device->Bs2b, samples);
- device->DryBuffer[0][i] = samples[0];
- device->DryBuffer[1][i] = samples[1];
- }
+ int lidx = GetChannelIdxByName(&device->RealOut, FrontLeft);
+ int ridx = GetChannelIdxByName(&device->RealOut, FrontRight);
+ int cidx = GetChannelIdxByName(&device->RealOut, FrontCenter);
+ assert(lidx >= 0 && ridx >= 0 && cidx >= 0);
+
+ ApplyStablizer(device->Stablizer, device->RealOut.Buffer, lidx, ridx, cidx,
+ SamplesToDo, device->RealOut.NumChannels);
}
- if(buffer)
+ ApplyDistanceComp(device->RealOut.Buffer, device->ChannelDelay, device->TempBuffer[0],
+ SamplesToDo, device->RealOut.NumChannels);
+
+ if(device->Limiter)
+ ApplyCompression(device->Limiter, SamplesToDo, device->RealOut.Buffer);
+
+ if(device->DitherDepth > 0.0f)
+ ApplyDither(device->RealOut.Buffer, &device->DitherSeed, device->DitherDepth,
+ SamplesToDo, device->RealOut.NumChannels);
+
+ if(LIKELY(OutBuffer))
{
-#define WRITE(T, a, b, c, d) do { \
- Write_##T((a), (b), (c), (d)); \
- buffer = (T*)buffer + (c)*(d); \
-} while(0)
+ ALfloat (*Buffer)[BUFFERSIZE] = device->RealOut.Buffer;
+ ALsizei Channels = device->RealOut.NumChannels;
+
switch(device->FmtType)
{
- case DevFmtByte:
- WRITE(ALbyte, OutBuffer, buffer, SamplesToDo, OutChannels);
- break;
- case DevFmtUByte:
- WRITE(ALubyte, OutBuffer, buffer, SamplesToDo, OutChannels);
- break;
- case DevFmtShort:
- WRITE(ALshort, OutBuffer, buffer, SamplesToDo, OutChannels);
- break;
- case DevFmtUShort:
- WRITE(ALushort, OutBuffer, buffer, SamplesToDo, OutChannels);
- break;
- case DevFmtInt:
- WRITE(ALint, OutBuffer, buffer, SamplesToDo, OutChannels);
- break;
- case DevFmtUInt:
- WRITE(ALuint, OutBuffer, buffer, SamplesToDo, OutChannels);
- break;
- case DevFmtFloat:
- WRITE(ALfloat, OutBuffer, buffer, SamplesToDo, OutChannels);
- break;
+#define HANDLE_WRITE(T, S) case T: \
+ Write##S(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels); break;
+ HANDLE_WRITE(DevFmtByte, I8)
+ HANDLE_WRITE(DevFmtUByte, UI8)
+ HANDLE_WRITE(DevFmtShort, I16)
+ HANDLE_WRITE(DevFmtUShort, UI16)
+ HANDLE_WRITE(DevFmtInt, I32)
+ HANDLE_WRITE(DevFmtUInt, UI32)
+ HANDLE_WRITE(DevFmtFloat, F32)
+#undef HANDLE_WRITE
}
-#undef WRITE
}
- size -= SamplesToDo;
- IncrementRef(&device->MixCount);
+ SamplesDone += SamplesToDo;
}
-
- RestoreFPUMode(&oldMode);
+ END_MIXER_MODE();
}
-ALvoid aluHandleDisconnect(ALCdevice *device)
+void aluHandleDisconnect(ALCdevice *device, const char *msg, ...)
{
- ALCcontext *Context;
+ AsyncEvent evt = ASYNC_EVENT(EventType_Disconnected);
+ ALCcontext *ctx;
+ va_list args;
+ int msglen;
+
+ if(!ATOMIC_EXCHANGE(&device->Connected, AL_FALSE, almemory_order_acq_rel))
+ return;
+
+ evt.u.user.type = AL_EVENT_TYPE_DISCONNECTED_SOFT;
+ evt.u.user.id = 0;
+ evt.u.user.param = 0;
- device->Connected = ALC_FALSE;
+ va_start(args, msg);
+ msglen = vsnprintf(evt.u.user.msg, sizeof(evt.u.user.msg), msg, args);
+ va_end(args);
- Context = ATOMIC_LOAD(&device->ContextList);
- while(Context)
+ if(msglen < 0 || (size_t)msglen >= sizeof(evt.u.user.msg))
+ evt.u.user.msg[sizeof(evt.u.user.msg)-1] = 0;
+
+ ctx = ATOMIC_LOAD_SEQ(&device->ContextList);
+ while(ctx)
{
- ALvoice *voice, *voice_end;
+ ALbitfieldSOFT enabledevt = ATOMIC_LOAD(&ctx->EnabledEvts, almemory_order_acquire);
+ ALsizei i;
+
+ if((enabledevt&EventType_Disconnected) &&
+ ll_ringbuffer_write(ctx->AsyncEvents, (const char*)&evt, 1) == 1)
+ alsem_post(&ctx->EventSem);
- voice = Context->Voices;
- voice_end = voice + Context->VoiceCount;
- while(voice != voice_end)
+ for(i = 0;i < ctx->VoiceCount;i++)
{
- ALsource *source = voice->Source;
- voice->Source = NULL;
+ ALvoice *voice = ctx->Voices[i];
+ ALsource *source;
- if(source && source->state == AL_PLAYING)
+ source = ATOMIC_EXCHANGE_PTR(&voice->Source, NULL, almemory_order_relaxed);
+ if(source && ATOMIC_LOAD(&voice->Playing, almemory_order_relaxed))
{
- source->state = AL_STOPPED;
- ATOMIC_STORE(&source->current_buffer, NULL);
- source->position = 0;
- source->position_fraction = 0;
+ /* If the source's voice was playing, it's now effectively
+ * stopped (the source state will be updated the next time it's
+ * checked).
+ */
+ SendSourceStoppedEvent(ctx, source->id);
}
-
- voice++;
+ ATOMIC_STORE(&voice->Playing, false, almemory_order_release);
}
- Context->VoiceCount = 0;
- Context = Context->next;
+ ctx = ATOMIC_LOAD(&ctx->next, almemory_order_relaxed);
}
}
diff --git a/Alc/alcRing.c b/Alc/alcRing.c
deleted file mode 100644
index e9a40a12..00000000
--- a/Alc/alcRing.c
+++ /dev/null
@@ -1,401 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 1999-2007 by authors.
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
-#include "config.h"
-
-#include <string.h>
-#include <stdlib.h>
-
-#include "alMain.h"
-#include "threads.h"
-#include "compat.h"
-
-
-struct RingBuffer {
- ALubyte *mem;
-
- ALsizei frame_size;
- ALsizei length;
- ALint read_pos;
- ALint write_pos;
-
- almtx_t mtx;
-};
-
-
-RingBuffer *CreateRingBuffer(ALsizei frame_size, ALsizei length)
-{
- RingBuffer *ring = calloc(1, sizeof(*ring) + ((length+1) * frame_size));
- if(ring)
- {
- ring->mem = (ALubyte*)(ring+1);
-
- ring->frame_size = frame_size;
- ring->length = length+1;
- ring->read_pos = 0;
- ring->write_pos = 0;
-
- almtx_init(&ring->mtx, almtx_plain);
- }
- return ring;
-}
-
-void DestroyRingBuffer(RingBuffer *ring)
-{
- if(ring)
- {
- almtx_destroy(&ring->mtx);
- free(ring);
- }
-}
-
-ALsizei RingBufferSize(RingBuffer *ring)
-{
- ALsizei s;
-
- almtx_lock(&ring->mtx);
- s = (ring->write_pos-ring->read_pos+ring->length) % ring->length;
- almtx_unlock(&ring->mtx);
-
- return s;
-}
-
-void WriteRingBuffer(RingBuffer *ring, const ALubyte *data, ALsizei len)
-{
- int remain;
-
- almtx_lock(&ring->mtx);
-
- remain = (ring->read_pos-ring->write_pos-1+ring->length) % ring->length;
- if(remain < len) len = remain;
-
- if(len > 0)
- {
- remain = ring->length - ring->write_pos;
- if(remain < len)
- {
- memcpy(ring->mem+(ring->write_pos*ring->frame_size), data,
- remain*ring->frame_size);
- memcpy(ring->mem, data+(remain*ring->frame_size),
- (len-remain)*ring->frame_size);
- }
- else
- memcpy(ring->mem+(ring->write_pos*ring->frame_size), data,
- len*ring->frame_size);
-
- ring->write_pos += len;
- ring->write_pos %= ring->length;
- }
-
- almtx_unlock(&ring->mtx);
-}
-
-void ReadRingBuffer(RingBuffer *ring, ALubyte *data, ALsizei len)
-{
- int remain;
-
- almtx_lock(&ring->mtx);
-
- remain = ring->length - ring->read_pos;
- if(remain < len)
- {
- memcpy(data, ring->mem+(ring->read_pos*ring->frame_size), remain*ring->frame_size);
- memcpy(data+(remain*ring->frame_size), ring->mem, (len-remain)*ring->frame_size);
- }
- else
- memcpy(data, ring->mem+(ring->read_pos*ring->frame_size), len*ring->frame_size);
-
- ring->read_pos += len;
- ring->read_pos %= ring->length;
-
- almtx_unlock(&ring->mtx);
-}
-
-
-/* NOTE: This lockless ringbuffer implementation is copied from JACK, extended
- * to include an element size. Consequently, parameters and return values for a
- * size or count is in 'elements', not bytes. Additionally, it only supports
- * single-consumer/single-provider operation. */
-struct ll_ringbuffer {
- volatile size_t write_ptr;
- volatile size_t read_ptr;
- 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;
- }
-}
diff --git a/Alc/alcConfig.c b/Alc/alconfig.c
index 6fc9db33..3d0ed140 100644
--- a/Alc/alcConfig.c
+++ b/Alc/alconfig.c
@@ -36,8 +36,12 @@
#include <windows.h>
#include <shlobj.h>
#endif
+#ifdef __APPLE__
+#include <CoreFoundation/CoreFoundation.h>
+#endif
#include "alMain.h"
+#include "alconfig.h"
#include "compat.h"
#include "bool.h"
@@ -233,7 +237,61 @@ static void LoadConfigFromFile(FILE *f)
curSection[0] = 0;
else
{
- strncpy(curSection, section, sizeof(curSection)-1);
+ size_t len, p = 0;
+ do {
+ char *nextp = strchr(section, '%');
+ if(!nextp)
+ {
+ strncpy(curSection+p, section, sizeof(curSection)-1-p);
+ break;
+ }
+
+ len = nextp - section;
+ if(len > sizeof(curSection)-1-p)
+ len = sizeof(curSection)-1-p;
+ strncpy(curSection+p, section, len);
+ p += len;
+ section = nextp;
+
+ if(((section[1] >= '0' && section[1] <= '9') ||
+ (section[1] >= 'a' && section[1] <= 'f') ||
+ (section[1] >= 'A' && section[1] <= 'F')) &&
+ ((section[2] >= '0' && section[2] <= '9') ||
+ (section[2] >= 'a' && section[2] <= 'f') ||
+ (section[2] >= 'A' && section[2] <= 'F')))
+ {
+ unsigned char b = 0;
+ if(section[1] >= '0' && section[1] <= '9')
+ b = (section[1]-'0') << 4;
+ else if(section[1] >= 'a' && section[1] <= 'f')
+ b = (section[1]-'a'+0xa) << 4;
+ else if(section[1] >= 'A' && section[1] <= 'F')
+ b = (section[1]-'A'+0x0a) << 4;
+ if(section[2] >= '0' && section[2] <= '9')
+ b |= (section[2]-'0');
+ else if(section[2] >= 'a' && section[2] <= 'f')
+ b |= (section[2]-'a'+0xa);
+ else if(section[2] >= 'A' && section[2] <= 'F')
+ b |= (section[2]-'A'+0x0a);
+ if(p < sizeof(curSection)-1)
+ curSection[p++] = b;
+ section += 3;
+ }
+ else if(section[1] == '%')
+ {
+ if(p < sizeof(curSection)-1)
+ curSection[p++] = '%';
+ section += 2;
+ }
+ else
+ {
+ if(p < sizeof(curSection)-1)
+ curSection[p++] = '%';
+ section += 1;
+ }
+ if(p < sizeof(curSection)-1)
+ curSection[p] = 0;
+ } while(p < sizeof(curSection)-1 && *section != 0);
curSection[sizeof(curSection)-1] = 0;
}
@@ -311,45 +369,62 @@ static void LoadConfigFromFile(FILE *f)
#ifdef _WIN32
void ReadALConfig(void)
{
- WCHAR buffer[PATH_MAX];
+ al_string ppath = AL_STRING_INIT_STATIC();
+ WCHAR buffer[MAX_PATH];
const WCHAR *str;
FILE *f;
if(SHGetSpecialFolderPathW(NULL, buffer, CSIDL_APPDATA, FALSE) != FALSE)
{
al_string filepath = AL_STRING_INIT_STATIC();
- al_string_copy_wcstr(&filepath, buffer);
- al_string_append_cstr(&filepath, "\\alsoft.ini");
+ alstr_copy_wcstr(&filepath, buffer);
+ alstr_append_cstr(&filepath, "\\alsoft.ini");
- TRACE("Loading config %s...\n", al_string_get_cstr(filepath));
- f = al_fopen(al_string_get_cstr(filepath), "rt");
+ TRACE("Loading config %s...\n", alstr_get_cstr(filepath));
+ f = al_fopen(alstr_get_cstr(filepath), "rt");
+ if(f)
+ {
+ LoadConfigFromFile(f);
+ fclose(f);
+ }
+ alstr_reset(&filepath);
+ }
+
+ GetProcBinary(&ppath, NULL);
+ if(!alstr_empty(ppath))
+ {
+ alstr_append_cstr(&ppath, "\\alsoft.ini");
+ TRACE("Loading config %s...\n", alstr_get_cstr(ppath));
+ f = al_fopen(alstr_get_cstr(ppath), "r");
if(f)
{
LoadConfigFromFile(f);
fclose(f);
}
- al_string_deinit(&filepath);
}
if((str=_wgetenv(L"ALSOFT_CONF")) != NULL && *str)
{
al_string filepath = AL_STRING_INIT_STATIC();
- al_string_copy_wcstr(&filepath, str);
+ alstr_copy_wcstr(&filepath, str);
- TRACE("Loading config %s...\n", al_string_get_cstr(filepath));
- f = al_fopen(al_string_get_cstr(filepath), "rt");
+ TRACE("Loading config %s...\n", alstr_get_cstr(filepath));
+ f = al_fopen(alstr_get_cstr(filepath), "rt");
if(f)
{
LoadConfigFromFile(f);
fclose(f);
}
- al_string_deinit(&filepath);
+ alstr_reset(&filepath);
}
+
+ alstr_reset(&ppath);
}
#else
void ReadALConfig(void)
{
- char buffer[PATH_MAX];
+ al_string confpaths = AL_STRING_INIT_STATIC();
+ al_string fname = AL_STRING_INIT_STATIC();
const char *str;
FILE *f;
@@ -365,45 +440,75 @@ void ReadALConfig(void)
if(!(str=getenv("XDG_CONFIG_DIRS")) || str[0] == 0)
str = "/etc/xdg";
- strncpy(buffer, str, sizeof(buffer)-1);
- buffer[sizeof(buffer)-1] = 0;
+ alstr_copy_cstr(&confpaths, str);
/* Go through the list in reverse, since "the order of base directories
* denotes their importance; the first directory listed is the most
* important". Ergo, we need to load the settings from the later dirs
* first so that the settings in the earlier dirs override them.
*/
- while(1)
+ while(!alstr_empty(confpaths))
{
- char *next = strrchr(buffer, ':');
- if(next) *(next++) = 0;
- else next = buffer;
+ char *next = strrchr(alstr_get_cstr(confpaths), ':');
+ if(next)
+ {
+ size_t len = next - alstr_get_cstr(confpaths);
+ alstr_copy_cstr(&fname, next+1);
+ VECTOR_RESIZE(confpaths, len, len+1);
+ VECTOR_ELEM(confpaths, len) = 0;
+ }
+ else
+ {
+ alstr_reset(&fname);
+ fname = confpaths;
+ AL_STRING_INIT(confpaths);
+ }
- if(next[0] != '/')
- WARN("Ignoring XDG config dir: %s\n", next);
+ if(alstr_empty(fname) || VECTOR_FRONT(fname) != '/')
+ WARN("Ignoring XDG config dir: %s\n", alstr_get_cstr(fname));
else
{
- size_t len = strlen(next);
- strncpy(next+len, "/alsoft.conf", buffer+sizeof(buffer)-next-len);
- buffer[sizeof(buffer)-1] = 0;
+ if(VECTOR_BACK(fname) != '/') alstr_append_cstr(&fname, "/alsoft.conf");
+ else alstr_append_cstr(&fname, "alsoft.conf");
- TRACE("Loading config %s...\n", next);
- f = al_fopen(next, "r");
+ TRACE("Loading config %s...\n", alstr_get_cstr(fname));
+ f = al_fopen(alstr_get_cstr(fname), "r");
if(f)
{
LoadConfigFromFile(f);
fclose(f);
}
}
- if(next == buffer)
- break;
+ alstr_clear(&fname);
}
+#ifdef __APPLE__
+ CFBundleRef mainBundle = CFBundleGetMainBundle();
+ if(mainBundle)
+ {
+ unsigned char fileName[PATH_MAX];
+ CFURLRef configURL;
+
+ if((configURL=CFBundleCopyResourceURL(mainBundle, CFSTR(".alsoftrc"), CFSTR(""), NULL)) &&
+ CFURLGetFileSystemRepresentation(configURL, true, fileName, sizeof(fileName)))
+ {
+ f = al_fopen((const char*)fileName, "r");
+ if(f)
+ {
+ LoadConfigFromFile(f);
+ fclose(f);
+ }
+ }
+ }
+#endif
+
if((str=getenv("HOME")) != NULL && *str)
{
- snprintf(buffer, sizeof(buffer), "%s/.alsoftrc", str);
+ alstr_copy_cstr(&fname, str);
+ if(VECTOR_BACK(fname) != '/') alstr_append_cstr(&fname, "/.alsoftrc");
+ else alstr_append_cstr(&fname, ".alsoftrc");
- TRACE("Loading config %s...\n", buffer);
- f = al_fopen(buffer, "r");
+ TRACE("Loading config %s...\n", alstr_get_cstr(fname));
+ f = al_fopen(alstr_get_cstr(fname), "r");
if(f)
{
LoadConfigFromFile(f);
@@ -412,17 +517,41 @@ void ReadALConfig(void)
}
if((str=getenv("XDG_CONFIG_HOME")) != NULL && str[0] != 0)
- snprintf(buffer, sizeof(buffer), "%s/%s", str, "alsoft.conf");
+ {
+ alstr_copy_cstr(&fname, str);
+ if(VECTOR_BACK(fname) != '/') alstr_append_cstr(&fname, "/alsoft.conf");
+ else alstr_append_cstr(&fname, "alsoft.conf");
+ }
else
{
- buffer[0] = 0;
+ alstr_clear(&fname);
if((str=getenv("HOME")) != NULL && str[0] != 0)
- snprintf(buffer, sizeof(buffer), "%s/.config/%s", str, "alsoft.conf");
+ {
+ alstr_copy_cstr(&fname, str);
+ if(VECTOR_BACK(fname) != '/') alstr_append_cstr(&fname, "/.config/alsoft.conf");
+ else alstr_append_cstr(&fname, ".config/alsoft.conf");
+ }
}
- if(buffer[0] != 0)
+ if(!alstr_empty(fname))
{
- TRACE("Loading config %s...\n", buffer);
- f = al_fopen(buffer, "r");
+ TRACE("Loading config %s...\n", alstr_get_cstr(fname));
+ f = al_fopen(alstr_get_cstr(fname), "r");
+ if(f)
+ {
+ LoadConfigFromFile(f);
+ fclose(f);
+ }
+ }
+
+ alstr_clear(&fname);
+ GetProcBinary(&fname, NULL);
+ if(!alstr_empty(fname))
+ {
+ if(VECTOR_BACK(fname) != '/') alstr_append_cstr(&fname, "/alsoft.conf");
+ else alstr_append_cstr(&fname, "alsoft.conf");
+
+ TRACE("Loading config %s...\n", alstr_get_cstr(fname));
+ f = al_fopen(alstr_get_cstr(fname), "r");
if(f)
{
LoadConfigFromFile(f);
@@ -440,6 +569,9 @@ void ReadALConfig(void)
fclose(f);
}
}
+
+ alstr_reset(&fname);
+ alstr_reset(&confpaths);
}
#endif
diff --git a/Alc/alconfig.h b/Alc/alconfig.h
new file mode 100644
index 00000000..1e493e2e
--- /dev/null
+++ b/Alc/alconfig.h
@@ -0,0 +1,17 @@
+#ifndef ALCONFIG_H
+#define ALCONFIG_H
+
+void ReadALConfig(void);
+void FreeALConfig(void);
+
+int ConfigValueExists(const char *devName, const char *blockName, const char *keyName);
+const char *GetConfigValue(const char *devName, const char *blockName, const char *keyName, const char *def);
+int GetConfigValueBool(const char *devName, const char *blockName, const char *keyName, int def);
+
+int ConfigValueStr(const char *devName, const char *blockName, const char *keyName, const char **ret);
+int ConfigValueInt(const char *devName, const char *blockName, const char *keyName, int *ret);
+int ConfigValueUInt(const char *devName, const char *blockName, const char *keyName, unsigned int *ret);
+int ConfigValueFloat(const char *devName, const char *blockName, const char *keyName, float *ret);
+int ConfigValueBool(const char *devName, const char *blockName, const char *keyName, int *ret);
+
+#endif /* ALCONFIG_H */
diff --git a/Alc/alstring.h b/Alc/alstring.h
index f53d2c57..923a5ea2 100644
--- a/Alc/alstring.h
+++ b/Alc/alstring.h
@@ -6,43 +6,53 @@
#include "vector.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
typedef char al_string_char_type;
TYPEDEF_VECTOR(al_string_char_type, al_string)
TYPEDEF_VECTOR(al_string, vector_al_string)
-inline void al_string_deinit(al_string *str)
+inline void alstr_reset(al_string *str)
{ VECTOR_DEINIT(*str); }
#define AL_STRING_INIT(_x) do { (_x) = (al_string)NULL; } while(0)
#define AL_STRING_INIT_STATIC() ((al_string)NULL)
-#define AL_STRING_DEINIT(_x) al_string_deinit(&(_x))
+#define AL_STRING_DEINIT(_x) alstr_reset(&(_x))
-inline size_t al_string_length(const_al_string str)
+inline size_t alstr_length(const_al_string str)
{ return VECTOR_SIZE(str); }
-inline ALboolean al_string_empty(const_al_string str)
-{ return al_string_length(str) == 0; }
+inline ALboolean alstr_empty(const_al_string str)
+{ return alstr_length(str) == 0; }
-inline const al_string_char_type *al_string_get_cstr(const_al_string str)
+inline const al_string_char_type *alstr_get_cstr(const_al_string str)
{ return str ? &VECTOR_FRONT(str) : ""; }
-void al_string_clear(al_string *str);
+void alstr_clear(al_string *str);
-int al_string_cmp(const_al_string str1, const_al_string str2);
-int al_string_cmp_cstr(const_al_string str1, const al_string_char_type *str2);
+int alstr_cmp(const_al_string str1, const_al_string str2);
+int alstr_cmp_cstr(const_al_string str1, const al_string_char_type *str2);
-void al_string_copy(al_string *str, const_al_string from);
-void al_string_copy_cstr(al_string *str, const al_string_char_type *from);
+void alstr_copy(al_string *str, const_al_string from);
+void alstr_copy_cstr(al_string *str, const al_string_char_type *from);
+void alstr_copy_range(al_string *str, const al_string_char_type *from, const al_string_char_type *to);
-void al_string_append_char(al_string *str, const al_string_char_type c);
-void al_string_append_cstr(al_string *str, const al_string_char_type *from);
-void al_string_append_range(al_string *str, const al_string_char_type *from, const al_string_char_type *to);
+void alstr_append_char(al_string *str, const al_string_char_type c);
+void alstr_append_cstr(al_string *str, const al_string_char_type *from);
+void alstr_append_range(al_string *str, const al_string_char_type *from, const al_string_char_type *to);
#ifdef _WIN32
#include <wchar.h>
/* Windows-only methods to deal with WideChar strings. */
-void al_string_copy_wcstr(al_string *str, const wchar_t *from);
-void al_string_append_wcstr(al_string *str, const wchar_t *from);
-void al_string_append_wrange(al_string *str, const wchar_t *from, const wchar_t *to);
+void alstr_copy_wcstr(al_string *str, const wchar_t *from);
+void alstr_append_wcstr(al_string *str, const wchar_t *from);
+void alstr_copy_wrange(al_string *str, const wchar_t *from, const wchar_t *to);
+void alstr_append_wrange(al_string *str, const wchar_t *from, const wchar_t *to);
+#endif
+
+#ifdef __cplusplus
+} /* extern "C" */
#endif
#endif /* ALSTRING_H */
diff --git a/Alc/ambdec.c b/Alc/ambdec.c
new file mode 100644
index 00000000..da114335
--- /dev/null
+++ b/Alc/ambdec.c
@@ -0,0 +1,566 @@
+
+#include "config.h"
+
+#include "ambdec.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "compat.h"
+
+
+static char *lstrip(char *line)
+{
+ while(isspace(line[0]))
+ line++;
+ return line;
+}
+
+static char *rstrip(char *line)
+{
+ size_t len = strlen(line);
+ while(len > 0 && isspace(line[len-1]))
+ len--;
+ line[len] = 0;
+ return line;
+}
+
+static int readline(FILE *f, char **output, size_t *maxlen)
+{
+ size_t len = 0;
+ int c;
+
+ while((c=fgetc(f)) != EOF && (c == '\r' || c == '\n'))
+ ;
+ if(c == EOF)
+ return 0;
+
+ do {
+ if(len+1 >= *maxlen)
+ {
+ void *temp = NULL;
+ size_t newmax;
+
+ newmax = (*maxlen ? (*maxlen)<<1 : 32);
+ if(newmax > *maxlen)
+ temp = realloc(*output, newmax);
+ if(!temp)
+ {
+ ERR("Failed to realloc "SZFMT" bytes from "SZFMT"!\n", newmax, *maxlen);
+ return 0;
+ }
+
+ *output = temp;
+ *maxlen = newmax;
+ }
+ (*output)[len++] = c;
+ (*output)[len] = '\0';
+ } while((c=fgetc(f)) != EOF && c != '\r' && c != '\n');
+
+ return 1;
+}
+
+
+/* Custom strtok_r, since we can't rely on it existing. */
+static char *my_strtok_r(char *str, const char *delim, char **saveptr)
+{
+ /* Sanity check and update internal pointer. */
+ if(!saveptr || !delim) return NULL;
+ if(str) *saveptr = str;
+ str = *saveptr;
+
+ /* Nothing more to do with this string. */
+ if(!str) return NULL;
+
+ /* Find the first non-delimiter character. */
+ while(*str != '\0' && strchr(delim, *str) != NULL)
+ str++;
+ if(*str == '\0')
+ {
+ /* End of string. */
+ *saveptr = NULL;
+ return NULL;
+ }
+
+ /* Find the next delimiter character. */
+ *saveptr = strpbrk(str, delim);
+ if(*saveptr) *((*saveptr)++) = '\0';
+
+ return str;
+}
+
+static char *read_int(ALint *num, const char *line, int base)
+{
+ char *end;
+ *num = strtol(line, &end, base);
+ if(end && *end != '\0')
+ end = lstrip(end);
+ return end;
+}
+
+static char *read_uint(ALuint *num, const char *line, int base)
+{
+ char *end;
+ *num = strtoul(line, &end, base);
+ if(end && *end != '\0')
+ end = lstrip(end);
+ return end;
+}
+
+static char *read_float(ALfloat *num, const char *line)
+{
+ char *end;
+#ifdef HAVE_STRTOF
+ *num = strtof(line, &end);
+#else
+ *num = (ALfloat)strtod(line, &end);
+#endif
+ if(end && *end != '\0')
+ end = lstrip(end);
+ return end;
+}
+
+
+char *read_clipped_line(FILE *f, char **buffer, size_t *maxlen)
+{
+ while(readline(f, buffer, maxlen))
+ {
+ char *line, *comment;
+
+ line = lstrip(*buffer);
+ comment = strchr(line, '#');
+ if(comment) *(comment++) = 0;
+
+ line = rstrip(line);
+ if(line[0]) return line;
+ }
+ return NULL;
+}
+
+static int load_ambdec_speakers(AmbDecConf *conf, FILE *f, char **buffer, size_t *maxlen, char **saveptr)
+{
+ ALsizei cur = 0;
+ while(cur < conf->NumSpeakers)
+ {
+ const char *cmd = my_strtok_r(NULL, " \t", saveptr);
+ if(!cmd)
+ {
+ char *line = read_clipped_line(f, buffer, maxlen);
+ if(!line)
+ {
+ ERR("Unexpected end of file\n");
+ return 0;
+ }
+ cmd = my_strtok_r(line, " \t", saveptr);
+ }
+
+ if(strcmp(cmd, "add_spkr") == 0)
+ {
+ const char *name = my_strtok_r(NULL, " \t", saveptr);
+ const char *dist = my_strtok_r(NULL, " \t", saveptr);
+ const char *az = my_strtok_r(NULL, " \t", saveptr);
+ const char *elev = my_strtok_r(NULL, " \t", saveptr);
+ const char *conn = my_strtok_r(NULL, " \t", saveptr);
+
+ if(!name) WARN("Name not specified for speaker %u\n", cur+1);
+ else alstr_copy_cstr(&conf->Speakers[cur].Name, name);
+ if(!dist) WARN("Distance not specified for speaker %u\n", cur+1);
+ else read_float(&conf->Speakers[cur].Distance, dist);
+ if(!az) WARN("Azimuth not specified for speaker %u\n", cur+1);
+ else read_float(&conf->Speakers[cur].Azimuth, az);
+ if(!elev) WARN("Elevation not specified for speaker %u\n", cur+1);
+ else read_float(&conf->Speakers[cur].Elevation, elev);
+ if(!conn) TRACE("Connection not specified for speaker %u\n", cur+1);
+ else alstr_copy_cstr(&conf->Speakers[cur].Connection, conn);
+
+ cur++;
+ }
+ else
+ {
+ ERR("Unexpected speakers command: %s\n", cmd);
+ return 0;
+ }
+
+ cmd = my_strtok_r(NULL, " \t", saveptr);
+ if(cmd)
+ {
+ ERR("Unexpected junk on line: %s\n", cmd);
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static int load_ambdec_matrix(ALfloat *gains, ALfloat (*matrix)[MAX_AMBI_COEFFS], ALsizei maxrow, FILE *f, char **buffer, size_t *maxlen, char **saveptr)
+{
+ int gotgains = 0;
+ ALsizei cur = 0;
+ while(cur < maxrow)
+ {
+ const char *cmd = my_strtok_r(NULL, " \t", saveptr);
+ if(!cmd)
+ {
+ char *line = read_clipped_line(f, buffer, maxlen);
+ if(!line)
+ {
+ ERR("Unexpected end of file\n");
+ return 0;
+ }
+ cmd = my_strtok_r(line, " \t", saveptr);
+ }
+
+ if(strcmp(cmd, "order_gain") == 0)
+ {
+ ALuint curgain = 0;
+ char *line;
+ while((line=my_strtok_r(NULL, " \t", saveptr)) != NULL)
+ {
+ ALfloat value;
+ line = read_float(&value, line);
+ if(line && *line != '\0')
+ {
+ ERR("Extra junk on gain %u: %s\n", curgain+1, line);
+ return 0;
+ }
+ if(curgain < MAX_AMBI_ORDER+1)
+ gains[curgain] = value;
+ curgain++;
+ }
+ while(curgain < MAX_AMBI_ORDER+1)
+ gains[curgain++] = 0.0f;
+ gotgains = 1;
+ }
+ else if(strcmp(cmd, "add_row") == 0)
+ {
+ ALuint curidx = 0;
+ char *line;
+ while((line=my_strtok_r(NULL, " \t", saveptr)) != NULL)
+ {
+ ALfloat value;
+ line = read_float(&value, line);
+ if(line && *line != '\0')
+ {
+ ERR("Extra junk on matrix element %ux%u: %s\n", cur, curidx, line);
+ return 0;
+ }
+ if(curidx < MAX_AMBI_COEFFS)
+ matrix[cur][curidx] = value;
+ curidx++;
+ }
+ while(curidx < MAX_AMBI_COEFFS)
+ matrix[cur][curidx++] = 0.0f;
+ cur++;
+ }
+ else
+ {
+ ERR("Unexpected speakers command: %s\n", cmd);
+ return 0;
+ }
+
+ cmd = my_strtok_r(NULL, " \t", saveptr);
+ if(cmd)
+ {
+ ERR("Unexpected junk on line: %s\n", cmd);
+ return 0;
+ }
+ }
+
+ if(!gotgains)
+ {
+ ERR("Matrix order_gain not specified\n");
+ return 0;
+ }
+
+ return 1;
+}
+
+void ambdec_init(AmbDecConf *conf)
+{
+ ALsizei i;
+
+ memset(conf, 0, sizeof(*conf));
+ AL_STRING_INIT(conf->Description);
+ for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
+ {
+ AL_STRING_INIT(conf->Speakers[i].Name);
+ AL_STRING_INIT(conf->Speakers[i].Connection);
+ }
+}
+
+void ambdec_deinit(AmbDecConf *conf)
+{
+ ALsizei i;
+
+ alstr_reset(&conf->Description);
+ for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
+ {
+ alstr_reset(&conf->Speakers[i].Name);
+ alstr_reset(&conf->Speakers[i].Connection);
+ }
+ memset(conf, 0, sizeof(*conf));
+}
+
+int ambdec_load(AmbDecConf *conf, const char *fname)
+{
+ char *buffer = NULL;
+ size_t maxlen = 0;
+ char *line;
+ FILE *f;
+
+ f = al_fopen(fname, "r");
+ if(!f)
+ {
+ ERR("Failed to open: %s\n", fname);
+ return 0;
+ }
+
+ while((line=read_clipped_line(f, &buffer, &maxlen)) != NULL)
+ {
+ char *saveptr;
+ char *command;
+
+ command = my_strtok_r(line, "/ \t", &saveptr);
+ if(!command)
+ {
+ ERR("Malformed line: %s\n", line);
+ goto fail;
+ }
+
+ if(strcmp(command, "description") == 0)
+ {
+ char *value = my_strtok_r(NULL, "", &saveptr);
+ alstr_copy_cstr(&conf->Description, lstrip(value));
+ }
+ else if(strcmp(command, "version") == 0)
+ {
+ line = my_strtok_r(NULL, "", &saveptr);
+ line = read_uint(&conf->Version, line, 10);
+ if(line && *line != '\0')
+ {
+ ERR("Extra junk after version: %s\n", line);
+ goto fail;
+ }
+ if(conf->Version != 3)
+ {
+ ERR("Unsupported version: %u\n", conf->Version);
+ goto fail;
+ }
+ }
+ else if(strcmp(command, "dec") == 0)
+ {
+ const char *dec = my_strtok_r(NULL, "/ \t", &saveptr);
+ if(strcmp(dec, "chan_mask") == 0)
+ {
+ line = my_strtok_r(NULL, "", &saveptr);
+ line = read_uint(&conf->ChanMask, line, 16);
+ if(line && *line != '\0')
+ {
+ ERR("Extra junk after mask: %s\n", line);
+ goto fail;
+ }
+ }
+ else if(strcmp(dec, "freq_bands") == 0)
+ {
+ line = my_strtok_r(NULL, "", &saveptr);
+ line = read_uint(&conf->FreqBands, line, 10);
+ if(line && *line != '\0')
+ {
+ ERR("Extra junk after freq_bands: %s\n", line);
+ goto fail;
+ }
+ if(conf->FreqBands != 1 && conf->FreqBands != 2)
+ {
+ ERR("Invalid freq_bands value: %u\n", conf->FreqBands);
+ goto fail;
+ }
+ }
+ else if(strcmp(dec, "speakers") == 0)
+ {
+ line = my_strtok_r(NULL, "", &saveptr);
+ line = read_int(&conf->NumSpeakers, line, 10);
+ if(line && *line != '\0')
+ {
+ ERR("Extra junk after speakers: %s\n", line);
+ goto fail;
+ }
+ if(conf->NumSpeakers > MAX_OUTPUT_CHANNELS)
+ {
+ ERR("Unsupported speaker count: %u\n", conf->NumSpeakers);
+ goto fail;
+ }
+ }
+ else if(strcmp(dec, "coeff_scale") == 0)
+ {
+ line = my_strtok_r(NULL, " \t", &saveptr);
+ if(strcmp(line, "n3d") == 0)
+ conf->CoeffScale = ADS_N3D;
+ else if(strcmp(line, "sn3d") == 0)
+ conf->CoeffScale = ADS_SN3D;
+ else if(strcmp(line, "fuma") == 0)
+ conf->CoeffScale = ADS_FuMa;
+ else
+ {
+ ERR("Unsupported coeff scale: %s\n", line);
+ goto fail;
+ }
+ }
+ else
+ {
+ ERR("Unexpected /dec option: %s\n", dec);
+ goto fail;
+ }
+ }
+ else if(strcmp(command, "opt") == 0)
+ {
+ const char *opt = my_strtok_r(NULL, "/ \t", &saveptr);
+ if(strcmp(opt, "xover_freq") == 0)
+ {
+ line = my_strtok_r(NULL, "", &saveptr);
+ line = read_float(&conf->XOverFreq, line);
+ if(line && *line != '\0')
+ {
+ ERR("Extra junk after xover_freq: %s\n", line);
+ goto fail;
+ }
+ }
+ else if(strcmp(opt, "xover_ratio") == 0)
+ {
+ line = my_strtok_r(NULL, "", &saveptr);
+ line = read_float(&conf->XOverRatio, line);
+ if(line && *line != '\0')
+ {
+ ERR("Extra junk after xover_ratio: %s\n", line);
+ goto fail;
+ }
+ }
+ else if(strcmp(opt, "input_scale") == 0 || strcmp(opt, "nfeff_comp") == 0 ||
+ strcmp(opt, "delay_comp") == 0 || strcmp(opt, "level_comp") == 0)
+ {
+ /* Unused */
+ my_strtok_r(NULL, " \t", &saveptr);
+ }
+ else
+ {
+ ERR("Unexpected /opt option: %s\n", opt);
+ goto fail;
+ }
+ }
+ else if(strcmp(command, "speakers") == 0)
+ {
+ const char *value = my_strtok_r(NULL, "/ \t", &saveptr);
+ if(strcmp(value, "{") != 0)
+ {
+ ERR("Expected { after %s command, got %s\n", command, value);
+ goto fail;
+ }
+ if(!load_ambdec_speakers(conf, f, &buffer, &maxlen, &saveptr))
+ goto fail;
+ value = my_strtok_r(NULL, "/ \t", &saveptr);
+ if(!value)
+ {
+ line = read_clipped_line(f, &buffer, &maxlen);
+ if(!line)
+ {
+ ERR("Unexpected end of file\n");
+ goto fail;
+ }
+ value = my_strtok_r(line, "/ \t", &saveptr);
+ }
+ if(strcmp(value, "}") != 0)
+ {
+ ERR("Expected } after speaker definitions, got %s\n", value);
+ goto fail;
+ }
+ }
+ else if(strcmp(command, "lfmatrix") == 0 || strcmp(command, "hfmatrix") == 0 ||
+ strcmp(command, "matrix") == 0)
+ {
+ const char *value = my_strtok_r(NULL, "/ \t", &saveptr);
+ if(strcmp(value, "{") != 0)
+ {
+ ERR("Expected { after %s command, got %s\n", command, value);
+ goto fail;
+ }
+ if(conf->FreqBands == 1)
+ {
+ if(strcmp(command, "matrix") != 0)
+ {
+ ERR("Unexpected \"%s\" type for a single-band decoder\n", command);
+ goto fail;
+ }
+ if(!load_ambdec_matrix(conf->HFOrderGain, conf->HFMatrix, conf->NumSpeakers,
+ f, &buffer, &maxlen, &saveptr))
+ goto fail;
+ }
+ else
+ {
+ if(strcmp(command, "lfmatrix") == 0)
+ {
+ if(!load_ambdec_matrix(conf->LFOrderGain, conf->LFMatrix, conf->NumSpeakers,
+ f, &buffer, &maxlen, &saveptr))
+ goto fail;
+ }
+ else if(strcmp(command, "hfmatrix") == 0)
+ {
+ if(!load_ambdec_matrix(conf->HFOrderGain, conf->HFMatrix, conf->NumSpeakers,
+ f, &buffer, &maxlen, &saveptr))
+ goto fail;
+ }
+ else
+ {
+ ERR("Unexpected \"%s\" type for a dual-band decoder\n", command);
+ goto fail;
+ }
+ }
+ value = my_strtok_r(NULL, "/ \t", &saveptr);
+ if(!value)
+ {
+ line = read_clipped_line(f, &buffer, &maxlen);
+ if(!line)
+ {
+ ERR("Unexpected end of file\n");
+ goto fail;
+ }
+ value = my_strtok_r(line, "/ \t", &saveptr);
+ }
+ if(strcmp(value, "}") != 0)
+ {
+ ERR("Expected } after matrix definitions, got %s\n", value);
+ goto fail;
+ }
+ }
+ else if(strcmp(command, "end") == 0)
+ {
+ line = my_strtok_r(NULL, "/ \t", &saveptr);
+ if(line)
+ {
+ ERR("Unexpected junk on end: %s\n", line);
+ goto fail;
+ }
+
+ fclose(f);
+ free(buffer);
+ return 1;
+ }
+ else
+ {
+ ERR("Unexpected command: %s\n", command);
+ goto fail;
+ }
+
+ line = my_strtok_r(NULL, "/ \t", &saveptr);
+ if(line)
+ {
+ ERR("Unexpected junk on line: %s\n", line);
+ goto fail;
+ }
+ }
+ ERR("Unexpected end of file\n");
+
+fail:
+ fclose(f);
+ free(buffer);
+ return 0;
+}
diff --git a/Alc/ambdec.h b/Alc/ambdec.h
new file mode 100644
index 00000000..0bb84072
--- /dev/null
+++ b/Alc/ambdec.h
@@ -0,0 +1,46 @@
+#ifndef AMBDEC_H
+#define AMBDEC_H
+
+#include "alstring.h"
+#include "alMain.h"
+
+/* Helpers to read .ambdec configuration files. */
+
+enum AmbDecScaleType {
+ ADS_N3D,
+ ADS_SN3D,
+ ADS_FuMa,
+};
+typedef struct AmbDecConf {
+ al_string Description;
+ ALuint Version; /* Must be 3 */
+
+ ALuint ChanMask;
+ ALuint FreqBands; /* Must be 1 or 2 */
+ ALsizei NumSpeakers;
+ enum AmbDecScaleType CoeffScale;
+
+ ALfloat XOverFreq;
+ ALfloat XOverRatio;
+
+ struct {
+ al_string Name;
+ ALfloat Distance;
+ ALfloat Azimuth;
+ ALfloat Elevation;
+ al_string Connection;
+ } Speakers[MAX_OUTPUT_CHANNELS];
+
+ /* Unused when FreqBands == 1 */
+ ALfloat LFOrderGain[MAX_AMBI_ORDER+1];
+ ALfloat LFMatrix[MAX_OUTPUT_CHANNELS][MAX_AMBI_COEFFS];
+
+ ALfloat HFOrderGain[MAX_AMBI_ORDER+1];
+ ALfloat HFMatrix[MAX_OUTPUT_CHANNELS][MAX_AMBI_COEFFS];
+} AmbDecConf;
+
+void ambdec_init(AmbDecConf *conf);
+void ambdec_deinit(AmbDecConf *conf);
+int ambdec_load(AmbDecConf *conf, const char *fname);
+
+#endif /* AMBDEC_H */
diff --git a/Alc/backends/alsa.c b/Alc/backends/alsa.c
index 9a443c09..a967fff0 100644
--- a/Alc/backends/alsa.c
+++ b/Alc/backends/alsa.c
@@ -26,6 +26,8 @@
#include "alMain.h"
#include "alu.h"
+#include "alconfig.h"
+#include "ringbuffer.h"
#include "threads.h"
#include "compat.h"
@@ -199,15 +201,21 @@ static ALCboolean alsa_load(void)
#ifdef HAVE_DYNLOAD
if(!alsa_handle)
{
+ al_string missing_funcs = AL_STRING_INIT_STATIC();
+
alsa_handle = LoadLib("libasound.so.2");
if(!alsa_handle)
+ {
+ WARN("Failed to load %s\n", "libasound.so.2");
return ALC_FALSE;
+ }
error = ALC_FALSE;
#define LOAD_FUNC(f) do { \
p##f = GetSymbol(alsa_handle, #f); \
if(p##f == NULL) { \
error = ALC_TRUE; \
+ alstr_append_cstr(&missing_funcs, "\n" #f); \
} \
} while(0)
ALSA_FUNCS(LOAD_FUNC);
@@ -215,10 +223,11 @@ static ALCboolean alsa_load(void)
if(error)
{
+ WARN("Missing expected functions:%s\n", alstr_get_cstr(missing_funcs));
CloseLib(alsa_handle);
alsa_handle = NULL;
- return ALC_FALSE;
}
+ alstr_reset(&missing_funcs);
}
#endif
@@ -237,16 +246,13 @@ static vector_DevMap CaptureDevices;
static void clear_devlist(vector_DevMap *devlist)
{
- DevMap *iter, *end;
-
- iter = VECTOR_ITER_BEGIN(*devlist);
- end = VECTOR_ITER_END(*devlist);
- for(;iter != end;iter++)
- {
- AL_STRING_DEINIT(iter->name);
- AL_STRING_DEINIT(iter->device_name);
- }
- VECTOR_RESIZE(*devlist, 0);
+#define FREE_DEV(i) do { \
+ AL_STRING_DEINIT((i)->name); \
+ AL_STRING_DEINIT((i)->device_name); \
+} while(0)
+ VECTOR_FOR_EACH(DevMap, *devlist, FREE_DEV);
+ VECTOR_RESIZE(*devlist, 0, 0);
+#undef FREE_DEV
}
@@ -272,11 +278,45 @@ static void probe_devices(snd_pcm_stream_t stream, vector_DevMap *DeviceList)
AL_STRING_INIT(entry.name);
AL_STRING_INIT(entry.device_name);
- al_string_copy_cstr(&entry.name, alsaDevice);
- al_string_copy_cstr(&entry.device_name, GetConfigValue(NULL, "alsa", (stream==SND_PCM_STREAM_PLAYBACK) ?
- "device" : "capture", "default"));
+ alstr_copy_cstr(&entry.name, alsaDevice);
+ alstr_copy_cstr(&entry.device_name, GetConfigValue(
+ NULL, "alsa", (stream==SND_PCM_STREAM_PLAYBACK) ? "device" : "capture", "default"
+ ));
VECTOR_PUSH_BACK(*DeviceList, entry);
+ if(stream == SND_PCM_STREAM_PLAYBACK)
+ {
+ const char *customdevs, *sep, *next;
+ next = GetConfigValue(NULL, "alsa", "custom-devices", "");
+ while((customdevs=next) != NULL && customdevs[0])
+ {
+ next = strchr(customdevs, ';');
+ sep = strchr(customdevs, '=');
+ if(!sep)
+ {
+ al_string spec = AL_STRING_INIT_STATIC();
+ if(next)
+ alstr_copy_range(&spec, customdevs, next++);
+ else
+ alstr_copy_cstr(&spec, customdevs);
+ ERR("Invalid ALSA device specification \"%s\"\n", alstr_get_cstr(spec));
+ alstr_reset(&spec);
+ continue;
+ }
+
+ AL_STRING_INIT(entry.name);
+ AL_STRING_INIT(entry.device_name);
+ alstr_copy_range(&entry.name, customdevs, sep++);
+ if(next)
+ alstr_copy_range(&entry.device_name, sep, next++);
+ else
+ alstr_copy_cstr(&entry.device_name, sep);
+ TRACE("Got device \"%s\", \"%s\"\n", alstr_get_cstr(entry.name),
+ alstr_get_cstr(entry.device_name));
+ VECTOR_PUSH_BACK(*DeviceList, entry);
+ }
+ }
+
card = -1;
if((err=snd_card_next(&card)) < 0)
ERR("Failed to find a card: %s\n", snd_strerror(err));
@@ -321,7 +361,8 @@ static void probe_devices(snd_pcm_stream_t stream, vector_DevMap *DeviceList)
snd_pcm_info_set_device(pcminfo, dev);
snd_pcm_info_set_subdevice(pcminfo, 0);
snd_pcm_info_set_stream(pcminfo, stream);
- if((err = snd_ctl_pcm_info(handle, pcminfo)) < 0) {
+ if((err = snd_ctl_pcm_info(handle, pcminfo)) < 0)
+ {
if(err != -ENOENT)
ERR("control digital audio info (hw:%d): %s\n", card, snd_strerror(err));
continue;
@@ -333,15 +374,15 @@ static void probe_devices(snd_pcm_stream_t stream, vector_DevMap *DeviceList)
ConfigValueStr(NULL, "alsa", name, &device_prefix);
snprintf(name, sizeof(name), "%s, %s (CARD=%s,DEV=%d)",
- cardname, devname, cardid, dev);
+ cardname, devname, cardid, dev);
snprintf(device, sizeof(device), "%sCARD=%s,DEV=%d",
- device_prefix, cardid, dev);
+ device_prefix, cardid, dev);
TRACE("Got device \"%s\", \"%s\"\n", name, device);
AL_STRING_INIT(entry.name);
AL_STRING_INIT(entry.device_name);
- al_string_copy_cstr(&entry.name, name);
- al_string_copy_cstr(&entry.device_name, device);
+ alstr_copy_cstr(&entry.name, name);
+ alstr_copy_cstr(&entry.device_name, device);
VECTOR_PUSH_BACK(*DeviceList, entry);
}
snd_ctl_close(handle);
@@ -397,7 +438,7 @@ typedef struct ALCplaybackAlsa {
ALvoid *buffer;
ALsizei size;
- volatile int killNow;
+ ATOMIC(ALenum) killNow;
althrd_t thread;
} ALCplaybackAlsa;
@@ -405,15 +446,14 @@ static int ALCplaybackAlsa_mixerProc(void *ptr);
static int ALCplaybackAlsa_mixerNoMMapProc(void *ptr);
static void ALCplaybackAlsa_Construct(ALCplaybackAlsa *self, ALCdevice *device);
-static DECLARE_FORWARD(ALCplaybackAlsa, ALCbackend, void, Destruct)
+static void ALCplaybackAlsa_Destruct(ALCplaybackAlsa *self);
static ALCenum ALCplaybackAlsa_open(ALCplaybackAlsa *self, const ALCchar *name);
-static void ALCplaybackAlsa_close(ALCplaybackAlsa *self);
static ALCboolean ALCplaybackAlsa_reset(ALCplaybackAlsa *self);
static ALCboolean ALCplaybackAlsa_start(ALCplaybackAlsa *self);
static void ALCplaybackAlsa_stop(ALCplaybackAlsa *self);
static DECLARE_FORWARD2(ALCplaybackAlsa, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
static DECLARE_FORWARD(ALCplaybackAlsa, ALCbackend, ALCuint, availableSamples)
-static ALint64 ALCplaybackAlsa_getLatency(ALCplaybackAlsa *self);
+static ClockLatency ALCplaybackAlsa_getClockLatency(ALCplaybackAlsa *self);
static DECLARE_FORWARD(ALCplaybackAlsa, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCplaybackAlsa, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCplaybackAlsa)
@@ -425,6 +465,19 @@ static void ALCplaybackAlsa_Construct(ALCplaybackAlsa *self, ALCdevice *device)
{
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(ALCplaybackAlsa, ALCbackend, self);
+
+ self->pcmHandle = NULL;
+ self->buffer = NULL;
+
+ ATOMIC_INIT(&self->killNow, AL_TRUE);
+}
+
+void ALCplaybackAlsa_Destruct(ALCplaybackAlsa *self)
+{
+ if(self->pcmHandle)
+ snd_pcm_close(self->pcmHandle);
+ self->pcmHandle = NULL;
+ ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
}
@@ -444,14 +497,14 @@ static int ALCplaybackAlsa_mixerProc(void *ptr)
update_size = device->UpdateSize;
num_updates = device->NumUpdates;
- while(!self->killNow)
+ while(!ATOMIC_LOAD(&self->killNow, almemory_order_acquire))
{
int state = verify_state(self->pcmHandle);
if(state < 0)
{
ERR("Invalid state detected: %s\n", snd_strerror(state));
ALCplaybackAlsa_lock(self);
- aluHandleDisconnect(device);
+ aluHandleDisconnect(device, "Bad state: %s", snd_strerror(state));
ALCplaybackAlsa_unlock(self);
break;
}
@@ -534,14 +587,14 @@ static int ALCplaybackAlsa_mixerNoMMapProc(void *ptr)
update_size = device->UpdateSize;
num_updates = device->NumUpdates;
- while(!self->killNow)
+ while(!ATOMIC_LOAD(&self->killNow, almemory_order_acquire))
{
int state = verify_state(self->pcmHandle);
if(state < 0)
{
ERR("Invalid state detected: %s\n", snd_strerror(state));
ALCplaybackAlsa_lock(self);
- aluHandleDisconnect(device);
+ aluHandleDisconnect(device, "Bad state: %s", snd_strerror(state));
ALCplaybackAlsa_unlock(self);
break;
}
@@ -588,7 +641,9 @@ static int ALCplaybackAlsa_mixerNoMMapProc(void *ptr)
{
case -EAGAIN:
continue;
+#if ESTRPIPE != EPIPE
case -ESTRPIPE:
+#endif
case -EPIPE:
case -EINTR:
ret = snd_pcm_recover(self->pcmHandle, ret, 1);
@@ -630,12 +685,12 @@ static ALCenum ALCplaybackAlsa_open(ALCplaybackAlsa *self, const ALCchar *name)
if(VECTOR_SIZE(PlaybackDevices) == 0)
probe_devices(SND_PCM_STREAM_PLAYBACK, &PlaybackDevices);
-#define MATCH_NAME(i) (al_string_cmp_cstr((i)->name, name) == 0)
+#define MATCH_NAME(i) (alstr_cmp_cstr((i)->name, name) == 0)
VECTOR_FIND_IF(iter, const DevMap, PlaybackDevices, MATCH_NAME);
#undef MATCH_NAME
- if(iter == VECTOR_ITER_END(PlaybackDevices))
+ if(iter == VECTOR_END(PlaybackDevices))
return ALC_INVALID_VALUE;
- driver = al_string_get_cstr(iter->device_name);
+ driver = alstr_get_cstr(iter->device_name);
}
else
{
@@ -654,16 +709,11 @@ static ALCenum ALCplaybackAlsa_open(ALCplaybackAlsa *self, const ALCchar *name)
/* Free alsa's global config tree. Otherwise valgrind reports a ton of leaks. */
snd_config_update_free_global();
- al_string_copy_cstr(&device->DeviceName, name);
+ alstr_copy_cstr(&device->DeviceName, name);
return ALC_NO_ERROR;
}
-static void ALCplaybackAlsa_close(ALCplaybackAlsa *self)
-{
- snd_pcm_close(self->pcmHandle);
-}
-
static ALCboolean ALCplaybackAlsa_reset(ALCplaybackAlsa *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
@@ -677,6 +727,7 @@ static ALCboolean ALCplaybackAlsa_reset(ALCplaybackAlsa *self)
unsigned int rate;
const char *funcerr;
int allowmmap;
+ int dir;
int err;
switch(device->FmtType)
@@ -704,7 +755,7 @@ static ALCboolean ALCplaybackAlsa_reset(ALCplaybackAlsa *self)
break;
}
- allowmmap = GetConfigValueBool(al_string_get_cstr(device->DeviceName), "alsa", "mmap", 1);
+ allowmmap = GetConfigValueBool(alstr_get_cstr(device->DeviceName), "alsa", "mmap", 1);
periods = device->NumUpdates;
periodLen = (ALuint64)device->UpdateSize * 1000000 / device->Frequency;
bufferLen = periodLen * periods;
@@ -748,7 +799,7 @@ static ALCboolean ALCplaybackAlsa_reset(ALCplaybackAlsa *self)
}
CHECK(snd_pcm_hw_params_set_format(self->pcmHandle, hp, format));
/* test and set channels (implicitly sets frame bits) */
- if(snd_pcm_hw_params_test_channels(self->pcmHandle, hp, ChannelsFromDevFmt(device->FmtChans)) < 0)
+ if(snd_pcm_hw_params_test_channels(self->pcmHandle, hp, ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder)) < 0)
{
static const enum DevFmtChannels channellist[] = {
DevFmtStereo,
@@ -761,20 +812,24 @@ static ALCboolean ALCplaybackAlsa_reset(ALCplaybackAlsa *self)
for(k = 0;k < COUNTOF(channellist);k++)
{
- if(snd_pcm_hw_params_test_channels(self->pcmHandle, hp, ChannelsFromDevFmt(channellist[k])) >= 0)
+ if(snd_pcm_hw_params_test_channels(self->pcmHandle, hp, ChannelsFromDevFmt(channellist[k], 0)) >= 0)
{
device->FmtChans = channellist[k];
+ device->AmbiOrder = 0;
break;
}
}
}
- CHECK(snd_pcm_hw_params_set_channels(self->pcmHandle, hp, ChannelsFromDevFmt(device->FmtChans)));
+ CHECK(snd_pcm_hw_params_set_channels(self->pcmHandle, hp, ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder)));
/* set rate (implicitly constrains period/buffer parameters) */
- if(GetConfigValueBool(al_string_get_cstr(device->DeviceName), "alsa", "allow-resampler", 0))
+ if(!GetConfigValueBool(alstr_get_cstr(device->DeviceName), "alsa", "allow-resampler", 0) ||
+ !(device->Flags&DEVICE_FREQUENCY_REQUEST))
{
if(snd_pcm_hw_params_set_rate_resample(self->pcmHandle, hp, 0) < 0)
ERR("Failed to disable ALSA resampler\n");
}
+ else if(snd_pcm_hw_params_set_rate_resample(self->pcmHandle, hp, 1) < 0)
+ ERR("Failed to enable ALSA resampler\n");
CHECK(snd_pcm_hw_params_set_rate_near(self->pcmHandle, hp, &rate, NULL));
/* set buffer time (implicitly constrains period/buffer parameters) */
if((err=snd_pcm_hw_params_set_buffer_time_near(self->pcmHandle, hp, &bufferLen, NULL)) < 0)
@@ -787,7 +842,9 @@ static ALCboolean ALCplaybackAlsa_reset(ALCplaybackAlsa *self)
/* retrieve configuration info */
CHECK(snd_pcm_hw_params_get_access(hp, &access));
CHECK(snd_pcm_hw_params_get_period_size(hp, &periodSizeInFrames, NULL));
- CHECK(snd_pcm_hw_params_get_periods(hp, &periods, NULL));
+ CHECK(snd_pcm_hw_params_get_periods(hp, &periods, &dir));
+ if(dir != 0)
+ WARN("Inexact period count: %u (%d)\n", periods, dir);
snd_pcm_hw_params_free(hp);
hp = NULL;
@@ -837,7 +894,7 @@ static ALCboolean ALCplaybackAlsa_start(ALCplaybackAlsa *self)
self->size = snd_pcm_frames_to_bytes(self->pcmHandle, device->UpdateSize);
if(access == SND_PCM_ACCESS_RW_INTERLEAVED)
{
- self->buffer = malloc(self->size);
+ self->buffer = al_malloc(16, self->size);
if(!self->buffer)
{
ERR("buffer malloc failed\n");
@@ -855,11 +912,11 @@ static ALCboolean ALCplaybackAlsa_start(ALCplaybackAlsa *self)
}
thread_func = ALCplaybackAlsa_mixerProc;
}
- self->killNow = 0;
+ ATOMIC_STORE(&self->killNow, AL_FALSE, almemory_order_release);
if(althrd_create(&self->thread, thread_func, self) != althrd_success)
{
ERR("Could not create playback thread\n");
- free(self->buffer);
+ al_free(self->buffer);
self->buffer = NULL;
return ALC_FALSE;
}
@@ -876,28 +933,33 @@ static void ALCplaybackAlsa_stop(ALCplaybackAlsa *self)
{
int res;
- if(self->killNow)
+ if(ATOMIC_EXCHANGE(&self->killNow, AL_TRUE, almemory_order_acq_rel))
return;
-
- self->killNow = 1;
althrd_join(self->thread, &res);
- free(self->buffer);
+ al_free(self->buffer);
self->buffer = NULL;
}
-static ALint64 ALCplaybackAlsa_getLatency(ALCplaybackAlsa *self)
+static ClockLatency ALCplaybackAlsa_getClockLatency(ALCplaybackAlsa *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
snd_pcm_sframes_t delay = 0;
+ ClockLatency ret;
int err;
+ ALCplaybackAlsa_lock(self);
+ ret.ClockTime = GetDeviceClockTime(device);
if((err=snd_pcm_delay(self->pcmHandle, &delay)) < 0)
{
ERR("Failed to get pcm delay: %s\n", snd_strerror(err));
- return 0;
+ delay = 0;
}
- return maxi64((ALint64)delay*1000000000/device->Frequency, 0);
+ if(delay < 0) delay = 0;
+ ret.Latency = delay * DEVICE_CLOCK_RES / device->Frequency;
+ ALCplaybackAlsa_unlock(self);
+
+ return ret;
}
@@ -910,21 +972,20 @@ typedef struct ALCcaptureAlsa {
ALsizei size;
ALboolean doCapture;
- RingBuffer *ring;
+ ll_ringbuffer_t *ring;
snd_pcm_sframes_t last_avail;
} ALCcaptureAlsa;
static void ALCcaptureAlsa_Construct(ALCcaptureAlsa *self, ALCdevice *device);
-static DECLARE_FORWARD(ALCcaptureAlsa, ALCbackend, void, Destruct)
+static void ALCcaptureAlsa_Destruct(ALCcaptureAlsa *self);
static ALCenum ALCcaptureAlsa_open(ALCcaptureAlsa *self, const ALCchar *name);
-static void ALCcaptureAlsa_close(ALCcaptureAlsa *self);
static DECLARE_FORWARD(ALCcaptureAlsa, ALCbackend, ALCboolean, reset)
static ALCboolean ALCcaptureAlsa_start(ALCcaptureAlsa *self);
static void ALCcaptureAlsa_stop(ALCcaptureAlsa *self);
static ALCenum ALCcaptureAlsa_captureSamples(ALCcaptureAlsa *self, ALCvoid *buffer, ALCuint samples);
static ALCuint ALCcaptureAlsa_availableSamples(ALCcaptureAlsa *self);
-static ALint64 ALCcaptureAlsa_getLatency(ALCcaptureAlsa *self);
+static ClockLatency ALCcaptureAlsa_getClockLatency(ALCcaptureAlsa *self);
static DECLARE_FORWARD(ALCcaptureAlsa, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCcaptureAlsa, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCcaptureAlsa)
@@ -936,6 +997,25 @@ static void ALCcaptureAlsa_Construct(ALCcaptureAlsa *self, ALCdevice *device)
{
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(ALCcaptureAlsa, ALCbackend, self);
+
+ self->pcmHandle = NULL;
+ self->buffer = NULL;
+ self->ring = NULL;
+}
+
+void ALCcaptureAlsa_Destruct(ALCcaptureAlsa *self)
+{
+ if(self->pcmHandle)
+ snd_pcm_close(self->pcmHandle);
+ self->pcmHandle = NULL;
+
+ al_free(self->buffer);
+ self->buffer = NULL;
+
+ ll_ringbuffer_free(self->ring);
+ self->ring = NULL;
+
+ ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
}
@@ -958,12 +1038,12 @@ static ALCenum ALCcaptureAlsa_open(ALCcaptureAlsa *self, const ALCchar *name)
if(VECTOR_SIZE(CaptureDevices) == 0)
probe_devices(SND_PCM_STREAM_CAPTURE, &CaptureDevices);
-#define MATCH_NAME(i) (al_string_cmp_cstr((i)->name, name) == 0)
+#define MATCH_NAME(i) (alstr_cmp_cstr((i)->name, name) == 0)
VECTOR_FIND_IF(iter, const DevMap, CaptureDevices, MATCH_NAME);
#undef MATCH_NAME
- if(iter == VECTOR_ITER_END(CaptureDevices))
+ if(iter == VECTOR_END(CaptureDevices))
return ALC_INVALID_VALUE;
- driver = al_string_get_cstr(iter->device_name);
+ driver = alstr_get_cstr(iter->device_name);
}
else
{
@@ -1020,7 +1100,7 @@ static ALCenum ALCcaptureAlsa_open(ALCcaptureAlsa *self, const ALCchar *name)
/* set format (implicitly sets sample bits) */
CHECK(snd_pcm_hw_params_set_format(self->pcmHandle, hp, format));
/* set channels (implicitly sets frame bits) */
- CHECK(snd_pcm_hw_params_set_channels(self->pcmHandle, hp, ChannelsFromDevFmt(device->FmtChans)));
+ CHECK(snd_pcm_hw_params_set_channels(self->pcmHandle, hp, ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder)));
/* set rate (implicitly constrains period/buffer parameters) */
CHECK(snd_pcm_hw_params_set_rate(self->pcmHandle, hp, device->Frequency, 0));
/* set buffer size in frame units (implicitly sets period size/bytes/time and buffer time/bytes) */
@@ -1042,24 +1122,19 @@ static ALCenum ALCcaptureAlsa_open(ALCcaptureAlsa *self, const ALCchar *name)
if(needring)
{
- self->ring = CreateRingBuffer(FrameSizeFromDevFmt(device->FmtChans, device->FmtType),
- device->UpdateSize*device->NumUpdates);
+ self->ring = ll_ringbuffer_create(
+ device->UpdateSize*device->NumUpdates,
+ FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder),
+ false
+ );
if(!self->ring)
{
ERR("ring buffer create failed\n");
goto error2;
}
-
- self->size = snd_pcm_frames_to_bytes(self->pcmHandle, periodSizeInFrames);
- self->buffer = malloc(self->size);
- if(!self->buffer)
- {
- ERR("buffer malloc failed\n");
- goto error2;
- }
}
- al_string_copy_cstr(&device->DeviceName, name);
+ alstr_copy_cstr(&device->DeviceName, name);
return ALC_NO_ERROR;
@@ -1068,31 +1143,29 @@ error:
if(hp) snd_pcm_hw_params_free(hp);
error2:
- free(self->buffer);
- self->buffer = NULL;
- DestroyRingBuffer(self->ring);
+ ll_ringbuffer_free(self->ring);
self->ring = NULL;
snd_pcm_close(self->pcmHandle);
+ self->pcmHandle = NULL;
return ALC_INVALID_VALUE;
}
-static void ALCcaptureAlsa_close(ALCcaptureAlsa *self)
-{
- snd_pcm_close(self->pcmHandle);
- DestroyRingBuffer(self->ring);
-
- free(self->buffer);
- self->buffer = NULL;
-}
-
static ALCboolean ALCcaptureAlsa_start(ALCcaptureAlsa *self)
{
- int err = snd_pcm_start(self->pcmHandle);
+ int err = snd_pcm_prepare(self->pcmHandle);
+ if(err < 0)
+ ERR("prepare failed: %s\n", snd_strerror(err));
+ else
+ {
+ err = snd_pcm_start(self->pcmHandle);
+ if(err < 0)
+ ERR("start failed: %s\n", snd_strerror(err));
+ }
if(err < 0)
{
- ERR("start failed: %s\n", snd_strerror(err));
- aluHandleDisconnect(STATIC_CAST(ALCbackend, self)->mDevice);
+ aluHandleDisconnect(STATIC_CAST(ALCbackend, self)->mDevice, "Capture state failure: %s",
+ snd_strerror(err));
return ALC_FALSE;
}
@@ -1117,11 +1190,11 @@ static void ALCcaptureAlsa_stop(ALCcaptureAlsa *self)
void *ptr;
size = snd_pcm_frames_to_bytes(self->pcmHandle, avail);
- ptr = malloc(size);
+ ptr = al_malloc(16, size);
if(ptr)
{
ALCcaptureAlsa_captureSamples(self, ptr, avail);
- free(self->buffer);
+ al_free(self->buffer);
self->buffer = ptr;
self->size = size;
}
@@ -1138,12 +1211,12 @@ static ALCenum ALCcaptureAlsa_captureSamples(ALCcaptureAlsa *self, ALCvoid *buff
if(self->ring)
{
- ReadRingBuffer(self->ring, buffer, samples);
+ ll_ringbuffer_read(self->ring, buffer, samples);
return ALC_NO_ERROR;
}
self->last_avail -= samples;
- while(device->Connected && samples > 0)
+ while(ATOMIC_LOAD(&device->Connected, almemory_order_acquire) && samples > 0)
{
snd_pcm_sframes_t amt = 0;
@@ -1163,7 +1236,7 @@ static ALCenum ALCcaptureAlsa_captureSamples(ALCcaptureAlsa *self, ALCvoid *buff
}
else
{
- free(self->buffer);
+ al_free(self->buffer);
self->buffer = NULL;
self->size = 0;
}
@@ -1186,7 +1259,7 @@ static ALCenum ALCcaptureAlsa_captureSamples(ALCcaptureAlsa *self, ALCvoid *buff
if(amt < 0)
{
ERR("restore error: %s\n", snd_strerror(amt));
- aluHandleDisconnect(device);
+ aluHandleDisconnect(device, "Capture recovery failure: %s", snd_strerror(amt));
break;
}
/* If the amount available is less than what's asked, we lost it
@@ -1211,7 +1284,7 @@ static ALCuint ALCcaptureAlsa_availableSamples(ALCcaptureAlsa *self)
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
snd_pcm_sframes_t avail = 0;
- if(device->Connected && self->doCapture)
+ if(ATOMIC_LOAD(&device->Connected, almemory_order_acquire) && self->doCapture)
avail = snd_pcm_avail_update(self->pcmHandle);
if(avail < 0)
{
@@ -1227,7 +1300,7 @@ static ALCuint ALCcaptureAlsa_availableSamples(ALCcaptureAlsa *self)
if(avail < 0)
{
ERR("restore error: %s\n", snd_strerror(avail));
- aluHandleDisconnect(device);
+ aluHandleDisconnect(device, "Capture recovery failure: %s", snd_strerror(avail));
}
}
@@ -1241,12 +1314,15 @@ static ALCuint ALCcaptureAlsa_availableSamples(ALCcaptureAlsa *self)
while(avail > 0)
{
+ ll_ringbuffer_data_t vec[2];
snd_pcm_sframes_t amt;
- amt = snd_pcm_bytes_to_frames(self->pcmHandle, self->size);
- if(avail < amt) amt = avail;
+ ll_ringbuffer_get_write_vector(self->ring, vec);
+ if(vec[0].len == 0) break;
- amt = snd_pcm_readi(self->pcmHandle, self->buffer, amt);
+ amt = (vec[0].len < (snd_pcm_uframes_t)avail) ?
+ vec[0].len : (snd_pcm_uframes_t)avail;
+ amt = snd_pcm_readi(self->pcmHandle, vec[0].buf, amt);
if(amt < 0)
{
ERR("read error: %s\n", snd_strerror(amt));
@@ -1263,39 +1339,41 @@ static ALCuint ALCcaptureAlsa_availableSamples(ALCcaptureAlsa *self)
if(amt < 0)
{
ERR("restore error: %s\n", snd_strerror(amt));
- aluHandleDisconnect(device);
+ aluHandleDisconnect(device, "Capture recovery failure: %s", snd_strerror(amt));
break;
}
avail = amt;
continue;
}
- WriteRingBuffer(self->ring, self->buffer, amt);
+ ll_ringbuffer_write_advance(self->ring, amt);
avail -= amt;
}
- return RingBufferSize(self->ring);
+ return ll_ringbuffer_read_space(self->ring);
}
-static ALint64 ALCcaptureAlsa_getLatency(ALCcaptureAlsa *self)
+static ClockLatency ALCcaptureAlsa_getClockLatency(ALCcaptureAlsa *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
snd_pcm_sframes_t delay = 0;
+ ClockLatency ret;
int err;
+ ALCcaptureAlsa_lock(self);
+ ret.ClockTime = GetDeviceClockTime(device);
if((err=snd_pcm_delay(self->pcmHandle, &delay)) < 0)
{
ERR("Failed to get pcm delay: %s\n", snd_strerror(err));
- return 0;
+ delay = 0;
}
- return maxi64((ALint64)delay*1000000000/device->Frequency, 0);
-}
+ if(delay < 0) delay = 0;
+ ret.Latency = delay * DEVICE_CLOCK_RES / device->Frequency;
+ ALCcaptureAlsa_unlock(self);
+ return ret;
+}
-static inline void AppendAllDevicesList2(const DevMap *entry)
-{ AppendAllDevicesList(al_string_get_cstr(entry->name)); }
-static inline void AppendCaptureDeviceList2(const DevMap *entry)
-{ AppendCaptureDeviceList(al_string_get_cstr(entry->name)); }
typedef struct ALCalsaBackendFactory {
DERIVE_FROM_TYPE(ALCbackendFactory);
@@ -1334,19 +1412,25 @@ static ALCboolean ALCalsaBackendFactory_querySupport(ALCalsaBackendFactory* UNUS
return ALC_FALSE;
}
-static void ALCalsaBackendFactory_probe(ALCalsaBackendFactory* UNUSED(self), enum DevProbe type)
+static void ALCalsaBackendFactory_probe(ALCalsaBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
{
switch(type)
{
+#define APPEND_OUTNAME(i) do { \
+ if(!alstr_empty((i)->name)) \
+ alstr_append_range(outnames, VECTOR_BEGIN((i)->name), \
+ VECTOR_END((i)->name)+1); \
+} while(0)
case ALL_DEVICE_PROBE:
probe_devices(SND_PCM_STREAM_PLAYBACK, &PlaybackDevices);
- VECTOR_FOR_EACH(const DevMap, PlaybackDevices, AppendAllDevicesList2);
+ VECTOR_FOR_EACH(const DevMap, PlaybackDevices, APPEND_OUTNAME);
break;
case CAPTURE_DEVICE_PROBE:
probe_devices(SND_PCM_STREAM_CAPTURE, &CaptureDevices);
- VECTOR_FOR_EACH(const DevMap, CaptureDevices, AppendCaptureDeviceList2);
+ VECTOR_FOR_EACH(const DevMap, CaptureDevices, APPEND_OUTNAME);
break;
+#undef APPEND_OUTNAME
}
}
diff --git a/Alc/backends/base.c b/Alc/backends/base.c
index ebeb31bf..9d8614b1 100644
--- a/Alc/backends/base.c
+++ b/Alc/backends/base.c
@@ -4,17 +4,22 @@
#include <stdlib.h>
#include "alMain.h"
+#include "alu.h"
#include "backends/base.h"
+extern inline ALuint64 GetDeviceClockTime(ALCdevice *device);
+extern inline void ALCdevice_Lock(ALCdevice *device);
+extern inline void ALCdevice_Unlock(ALCdevice *device);
+extern inline ClockLatency GetClockLatency(ALCdevice *device);
+
/* Base ALCbackend method implementations. */
void ALCbackend_Construct(ALCbackend *self, ALCdevice *device)
{
- int ret;
- self->mDevice = device;
- ret = almtx_init(&self->mMutex, almtx_recursive);
+ int ret = almtx_init(&self->mMutex, almtx_recursive);
assert(ret == althrd_success);
+ self->mDevice = device;
}
void ALCbackend_Destruct(ALCbackend *self)
@@ -37,9 +42,27 @@ ALCuint ALCbackend_availableSamples(ALCbackend* UNUSED(self))
return 0;
}
-ALint64 ALCbackend_getLatency(ALCbackend* UNUSED(self))
+ClockLatency ALCbackend_getClockLatency(ALCbackend *self)
{
- return 0;
+ ALCdevice *device = self->mDevice;
+ ALuint refcount;
+ ClockLatency ret;
+
+ do {
+ while(((refcount=ATOMIC_LOAD(&device->MixCount, almemory_order_acquire))&1))
+ althrd_yield();
+ ret.ClockTime = GetDeviceClockTime(device);
+ ATOMIC_THREAD_FENCE(almemory_order_acquire);
+ } while(refcount != ATOMIC_LOAD(&device->MixCount, almemory_order_relaxed));
+
+ /* NOTE: The device will generally have about all but one periods filled at
+ * any given time during playback. Without a more accurate measurement from
+ * the output, this is an okay approximation.
+ */
+ ret.Latency = device->UpdateSize * DEVICE_CLOCK_RES / device->Frequency *
+ maxu(device->NumUpdates-1, 1);
+
+ return ret;
}
void ALCbackend_lock(ALCbackend *self)
@@ -59,157 +82,3 @@ void ALCbackend_unlock(ALCbackend *self)
void ALCbackendFactory_deinit(ALCbackendFactory* UNUSED(self))
{
}
-
-
-/* Wrappers to use an old-style backend with the new interface. */
-typedef struct PlaybackWrapper {
- DERIVE_FROM_TYPE(ALCbackend);
-
- const BackendFuncs *Funcs;
-} PlaybackWrapper;
-
-static void PlaybackWrapper_Construct(PlaybackWrapper *self, ALCdevice *device, const BackendFuncs *funcs);
-static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, void, Destruct)
-static ALCenum PlaybackWrapper_open(PlaybackWrapper *self, const ALCchar *name);
-static void PlaybackWrapper_close(PlaybackWrapper *self);
-static ALCboolean PlaybackWrapper_reset(PlaybackWrapper *self);
-static ALCboolean PlaybackWrapper_start(PlaybackWrapper *self);
-static void PlaybackWrapper_stop(PlaybackWrapper *self);
-static DECLARE_FORWARD2(PlaybackWrapper, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
-static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, ALCuint, availableSamples)
-static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, ALint64, getLatency)
-static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, void, lock)
-static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, void, unlock)
-DECLARE_DEFAULT_ALLOCATORS(PlaybackWrapper)
-DEFINE_ALCBACKEND_VTABLE(PlaybackWrapper);
-
-static void PlaybackWrapper_Construct(PlaybackWrapper *self, ALCdevice *device, const BackendFuncs *funcs)
-{
- ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
- SET_VTABLE2(PlaybackWrapper, ALCbackend, self);
-
- self->Funcs = funcs;
-}
-
-static ALCenum PlaybackWrapper_open(PlaybackWrapper *self, const ALCchar *name)
-{
- ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
- return self->Funcs->OpenPlayback(device, name);
-}
-
-static void PlaybackWrapper_close(PlaybackWrapper *self)
-{
- ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
- self->Funcs->ClosePlayback(device);
-}
-
-static ALCboolean PlaybackWrapper_reset(PlaybackWrapper *self)
-{
- ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
- return self->Funcs->ResetPlayback(device);
-}
-
-static ALCboolean PlaybackWrapper_start(PlaybackWrapper *self)
-{
- ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
- return self->Funcs->StartPlayback(device);
-}
-
-static void PlaybackWrapper_stop(PlaybackWrapper *self)
-{
- ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
- self->Funcs->StopPlayback(device);
-}
-
-
-typedef struct CaptureWrapper {
- DERIVE_FROM_TYPE(ALCbackend);
-
- const BackendFuncs *Funcs;
-} CaptureWrapper;
-
-static void CaptureWrapper_Construct(CaptureWrapper *self, ALCdevice *device, const BackendFuncs *funcs);
-static DECLARE_FORWARD(CaptureWrapper, ALCbackend, void, Destruct)
-static ALCenum CaptureWrapper_open(CaptureWrapper *self, const ALCchar *name);
-static void CaptureWrapper_close(CaptureWrapper *self);
-static DECLARE_FORWARD(CaptureWrapper, ALCbackend, ALCboolean, reset)
-static ALCboolean CaptureWrapper_start(CaptureWrapper *self);
-static void CaptureWrapper_stop(CaptureWrapper *self);
-static ALCenum CaptureWrapper_captureSamples(CaptureWrapper *self, void *buffer, ALCuint samples);
-static ALCuint CaptureWrapper_availableSamples(CaptureWrapper *self);
-static DECLARE_FORWARD(CaptureWrapper, ALCbackend, ALint64, getLatency)
-static DECLARE_FORWARD(CaptureWrapper, ALCbackend, void, lock)
-static DECLARE_FORWARD(CaptureWrapper, ALCbackend, void, unlock)
-DECLARE_DEFAULT_ALLOCATORS(CaptureWrapper)
-DEFINE_ALCBACKEND_VTABLE(CaptureWrapper);
-
-static void CaptureWrapper_Construct(CaptureWrapper *self, ALCdevice *device, const BackendFuncs *funcs)
-{
- ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
- SET_VTABLE2(CaptureWrapper, ALCbackend, self);
-
- self->Funcs = funcs;
-}
-
-static ALCenum CaptureWrapper_open(CaptureWrapper *self, const ALCchar *name)
-{
- ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
- return self->Funcs->OpenCapture(device, name);
-}
-
-static void CaptureWrapper_close(CaptureWrapper *self)
-{
- ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
- self->Funcs->CloseCapture(device);
-}
-
-static ALCboolean CaptureWrapper_start(CaptureWrapper *self)
-{
- ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
- self->Funcs->StartCapture(device);
- return ALC_TRUE;
-}
-
-static void CaptureWrapper_stop(CaptureWrapper *self)
-{
- ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
- self->Funcs->StopCapture(device);
-}
-
-static ALCenum CaptureWrapper_captureSamples(CaptureWrapper *self, void *buffer, ALCuint samples)
-{
- ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
- return self->Funcs->CaptureSamples(device, buffer, samples);
-}
-
-static ALCuint CaptureWrapper_availableSamples(CaptureWrapper *self)
-{
- ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
- return self->Funcs->AvailableSamples(device);
-}
-
-
-ALCbackend *create_backend_wrapper(ALCdevice *device, const BackendFuncs *funcs, ALCbackend_Type type)
-{
- if(type == ALCbackend_Playback)
- {
- PlaybackWrapper *backend;
-
- NEW_OBJ(backend, PlaybackWrapper)(device, funcs);
- if(!backend) return NULL;
-
- return STATIC_CAST(ALCbackend, backend);
- }
-
- if(type == ALCbackend_Capture)
- {
- CaptureWrapper *backend;
-
- NEW_OBJ(backend, CaptureWrapper)(device, funcs);
- if(!backend) return NULL;
-
- return STATIC_CAST(ALCbackend, backend);
- }
-
- return NULL;
-}
diff --git a/Alc/backends/base.h b/Alc/backends/base.h
index f6b4b80a..03db56e9 100644
--- a/Alc/backends/base.h
+++ b/Alc/backends/base.h
@@ -3,6 +3,26 @@
#include "alMain.h"
#include "threads.h"
+#include "alstring.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct ClockLatency {
+ ALint64 ClockTime;
+ ALint64 Latency;
+} ClockLatency;
+
+/* Helper to get the current clock time from the device's ClockBase, and
+ * SamplesDone converted from the sample rate.
+ */
+inline ALuint64 GetDeviceClockTime(ALCdevice *device)
+{
+ return device->ClockBase + (device->SamplesDone * DEVICE_CLOCK_RES /
+ device->Frequency);
+}
struct ALCbackendVtable;
@@ -20,7 +40,7 @@ void ALCbackend_Destruct(ALCbackend *self);
ALCboolean ALCbackend_reset(ALCbackend *self);
ALCenum ALCbackend_captureSamples(ALCbackend *self, void *buffer, ALCuint samples);
ALCuint ALCbackend_availableSamples(ALCbackend *self);
-ALint64 ALCbackend_getLatency(ALCbackend *self);
+ClockLatency ALCbackend_getClockLatency(ALCbackend *self);
void ALCbackend_lock(ALCbackend *self);
void ALCbackend_unlock(ALCbackend *self);
@@ -28,7 +48,6 @@ struct ALCbackendVtable {
void (*const Destruct)(ALCbackend*);
ALCenum (*const open)(ALCbackend*, const ALCchar*);
- void (*const close)(ALCbackend*);
ALCboolean (*const reset)(ALCbackend*);
ALCboolean (*const start)(ALCbackend*);
@@ -37,7 +56,7 @@ struct ALCbackendVtable {
ALCenum (*const captureSamples)(ALCbackend*, void*, ALCuint);
ALCuint (*const availableSamples)(ALCbackend*);
- ALint64 (*const getLatency)(ALCbackend*);
+ ClockLatency (*const getClockLatency)(ALCbackend*);
void (*const lock)(ALCbackend*);
void (*const unlock)(ALCbackend*);
@@ -48,13 +67,12 @@ struct ALCbackendVtable {
#define DEFINE_ALCBACKEND_VTABLE(T) \
DECLARE_THUNK(T, ALCbackend, void, Destruct) \
DECLARE_THUNK1(T, ALCbackend, ALCenum, open, const ALCchar*) \
-DECLARE_THUNK(T, ALCbackend, void, close) \
DECLARE_THUNK(T, ALCbackend, ALCboolean, reset) \
DECLARE_THUNK(T, ALCbackend, ALCboolean, start) \
DECLARE_THUNK(T, ALCbackend, void, stop) \
DECLARE_THUNK2(T, ALCbackend, ALCenum, captureSamples, void*, ALCuint) \
DECLARE_THUNK(T, ALCbackend, ALCuint, availableSamples) \
-DECLARE_THUNK(T, ALCbackend, ALint64, getLatency) \
+DECLARE_THUNK(T, ALCbackend, ClockLatency, getClockLatency) \
DECLARE_THUNK(T, ALCbackend, void, lock) \
DECLARE_THUNK(T, ALCbackend, void, unlock) \
static void T##_ALCbackend_Delete(void *ptr) \
@@ -64,13 +82,12 @@ static const struct ALCbackendVtable T##_ALCbackend_vtable = { \
T##_ALCbackend_Destruct, \
\
T##_ALCbackend_open, \
- T##_ALCbackend_close, \
T##_ALCbackend_reset, \
T##_ALCbackend_start, \
T##_ALCbackend_stop, \
T##_ALCbackend_captureSamples, \
T##_ALCbackend_availableSamples, \
- T##_ALCbackend_getLatency, \
+ T##_ALCbackend_getClockLatency, \
T##_ALCbackend_lock, \
T##_ALCbackend_unlock, \
\
@@ -99,7 +116,7 @@ struct ALCbackendFactoryVtable {
ALCboolean (*const querySupport)(ALCbackendFactory *self, ALCbackend_Type type);
- void (*const probe)(ALCbackendFactory *self, enum DevProbe type);
+ void (*const probe)(ALCbackendFactory *self, enum DevProbe type, al_string *outnames);
ALCbackend* (*const createBackend)(ALCbackendFactory *self, ALCdevice *device, ALCbackend_Type type);
};
@@ -108,7 +125,7 @@ struct ALCbackendFactoryVtable {
DECLARE_THUNK(T, ALCbackendFactory, ALCboolean, init) \
DECLARE_THUNK(T, ALCbackendFactory, void, deinit) \
DECLARE_THUNK1(T, ALCbackendFactory, ALCboolean, querySupport, ALCbackend_Type) \
-DECLARE_THUNK1(T, ALCbackendFactory, void, probe, enum DevProbe) \
+DECLARE_THUNK2(T, ALCbackendFactory, void, probe, enum DevProbe, al_string*) \
DECLARE_THUNK2(T, ALCbackendFactory, ALCbackend*, createBackend, ALCdevice*, ALCbackend_Type) \
\
static const struct ALCbackendFactoryVtable T##_ALCbackendFactory_vtable = { \
@@ -122,17 +139,40 @@ static const struct ALCbackendFactoryVtable T##_ALCbackendFactory_vtable = { \
ALCbackendFactory *ALCpulseBackendFactory_getFactory(void);
ALCbackendFactory *ALCalsaBackendFactory_getFactory(void);
+ALCbackendFactory *ALCcoreAudioBackendFactory_getFactory(void);
ALCbackendFactory *ALCossBackendFactory_getFactory(void);
ALCbackendFactory *ALCjackBackendFactory_getFactory(void);
ALCbackendFactory *ALCsolarisBackendFactory_getFactory(void);
-ALCbackendFactory *ALCmmdevBackendFactory_getFactory(void);
+ALCbackendFactory *SndioBackendFactory_getFactory(void);
+ALCbackendFactory *ALCqsaBackendFactory_getFactory(void);
+ALCbackendFactory *ALCwasapiBackendFactory_getFactory(void);
ALCbackendFactory *ALCdsoundBackendFactory_getFactory(void);
ALCbackendFactory *ALCwinmmBackendFactory_getFactory(void);
ALCbackendFactory *ALCportBackendFactory_getFactory(void);
+ALCbackendFactory *ALCopenslBackendFactory_getFactory(void);
ALCbackendFactory *ALCnullBackendFactory_getFactory(void);
ALCbackendFactory *ALCwaveBackendFactory_getFactory(void);
+ALCbackendFactory *ALCsdl2BackendFactory_getFactory(void);
ALCbackendFactory *ALCloopbackFactory_getFactory(void);
-ALCbackend *create_backend_wrapper(ALCdevice *device, const BackendFuncs *funcs, ALCbackend_Type type);
+
+inline void ALCdevice_Lock(ALCdevice *device)
+{ V0(device->Backend,lock)(); }
+
+inline void ALCdevice_Unlock(ALCdevice *device)
+{ V0(device->Backend,unlock)(); }
+
+
+inline ClockLatency GetClockLatency(ALCdevice *device)
+{
+ ClockLatency ret = V0(device->Backend,getClockLatency)();
+ ret.Latency += device->FixedLatency;
+ return ret;
+}
+
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
#endif /* AL_BACKENDS_BASE_H */
diff --git a/Alc/backends/coreaudio.c b/Alc/backends/coreaudio.c
index 43e881da..adb01fa6 100644
--- a/Alc/backends/coreaudio.c
+++ b/Alc/backends/coreaudio.c
@@ -23,195 +23,145 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <alloca.h>
#include "alMain.h"
#include "alu.h"
+#include "ringbuffer.h"
-#include <CoreServices/CoreServices.h>
#include <unistd.h>
#include <AudioUnit/AudioUnit.h>
#include <AudioToolbox/AudioToolbox.h>
+#include "backends/base.h"
-typedef struct {
- AudioUnit audioUnit;
-
- ALuint frameSize;
- ALdouble sampleRateRatio; // Ratio of hardware sample rate / requested sample rate
- AudioStreamBasicDescription format; // This is the OpenAL format as a CoreAudio ASBD
- AudioConverterRef audioConverter; // Sample rate converter if needed
- AudioBufferList *bufferList; // Buffer for data coming from the input device
- ALCvoid *resampleBuffer; // Buffer for returned RingBuffer data when resampling
+static const ALCchar ca_device[] = "CoreAudio Default";
- RingBuffer *ring;
-} ca_data;
-static const ALCchar ca_device[] = "CoreAudio Default";
+typedef struct ALCcoreAudioPlayback {
+ DERIVE_FROM_TYPE(ALCbackend);
+ AudioUnit audioUnit;
-static void destroy_buffer_list(AudioBufferList* list)
-{
- if(list)
- {
- UInt32 i;
- for(i = 0;i < list->mNumberBuffers;i++)
- free(list->mBuffers[i].mData);
- free(list);
- }
-}
+ ALuint frameSize;
+ AudioStreamBasicDescription format; // This is the OpenAL format as a CoreAudio ASBD
+} ALCcoreAudioPlayback;
-static AudioBufferList* allocate_buffer_list(UInt32 channelCount, UInt32 byteSize)
-{
- AudioBufferList *list;
+static void ALCcoreAudioPlayback_Construct(ALCcoreAudioPlayback *self, ALCdevice *device);
+static void ALCcoreAudioPlayback_Destruct(ALCcoreAudioPlayback *self);
+static ALCenum ALCcoreAudioPlayback_open(ALCcoreAudioPlayback *self, const ALCchar *name);
+static ALCboolean ALCcoreAudioPlayback_reset(ALCcoreAudioPlayback *self);
+static ALCboolean ALCcoreAudioPlayback_start(ALCcoreAudioPlayback *self);
+static void ALCcoreAudioPlayback_stop(ALCcoreAudioPlayback *self);
+static DECLARE_FORWARD2(ALCcoreAudioPlayback, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
+static DECLARE_FORWARD(ALCcoreAudioPlayback, ALCbackend, ALCuint, availableSamples)
+static DECLARE_FORWARD(ALCcoreAudioPlayback, ALCbackend, ClockLatency, getClockLatency)
+static DECLARE_FORWARD(ALCcoreAudioPlayback, ALCbackend, void, lock)
+static DECLARE_FORWARD(ALCcoreAudioPlayback, ALCbackend, void, unlock)
+DECLARE_DEFAULT_ALLOCATORS(ALCcoreAudioPlayback)
- list = calloc(1, sizeof(AudioBufferList) + sizeof(AudioBuffer));
- if(list)
- {
- list->mNumberBuffers = 1;
+DEFINE_ALCBACKEND_VTABLE(ALCcoreAudioPlayback);
- list->mBuffers[0].mNumberChannels = channelCount;
- list->mBuffers[0].mDataByteSize = byteSize;
- list->mBuffers[0].mData = malloc(byteSize);
- if(list->mBuffers[0].mData == NULL)
- {
- free(list);
- list = NULL;
- }
- }
- return list;
-}
-static OSStatus ca_callback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp,
- UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData)
+static void ALCcoreAudioPlayback_Construct(ALCcoreAudioPlayback *self, ALCdevice *device)
{
- ALCdevice *device = (ALCdevice*)inRefCon;
- ca_data *data = (ca_data*)device->ExtraData;
-
- aluMixData(device, ioData->mBuffers[0].mData,
- ioData->mBuffers[0].mDataByteSize / data->frameSize);
+ ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
+ SET_VTABLE2(ALCcoreAudioPlayback, ALCbackend, self);
- return noErr;
+ self->frameSize = 0;
+ memset(&self->format, 0, sizeof(self->format));
}
-static OSStatus ca_capture_conversion_callback(AudioConverterRef inAudioConverter, UInt32 *ioNumberDataPackets,
- AudioBufferList *ioData, AudioStreamPacketDescription **outDataPacketDescription, void* inUserData)
+static void ALCcoreAudioPlayback_Destruct(ALCcoreAudioPlayback *self)
{
- ALCdevice *device = (ALCdevice*)inUserData;
- ca_data *data = (ca_data*)device->ExtraData;
+ AudioUnitUninitialize(self->audioUnit);
+ AudioComponentInstanceDispose(self->audioUnit);
- // Read from the ring buffer and store temporarily in a large buffer
- ReadRingBuffer(data->ring, data->resampleBuffer, (ALsizei)(*ioNumberDataPackets));
-
- // Set the input data
- ioData->mNumberBuffers = 1;
- ioData->mBuffers[0].mNumberChannels = data->format.mChannelsPerFrame;
- ioData->mBuffers[0].mData = data->resampleBuffer;
- ioData->mBuffers[0].mDataByteSize = (*ioNumberDataPackets) * data->format.mBytesPerFrame;
-
- return noErr;
+ ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
}
-static OSStatus ca_capture_callback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags,
- const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber,
- UInt32 inNumberFrames, AudioBufferList *ioData)
-{
- ALCdevice *device = (ALCdevice*)inRefCon;
- ca_data *data = (ca_data*)device->ExtraData;
- AudioUnitRenderActionFlags flags = 0;
- OSStatus err;
- // fill the bufferList with data from the input device
- err = AudioUnitRender(data->audioUnit, &flags, inTimeStamp, 1, inNumberFrames, data->bufferList);
- if(err != noErr)
- {
- ERR("AudioUnitRender error: %d\n", err);
- return err;
- }
+static OSStatus ALCcoreAudioPlayback_MixerProc(void *inRefCon,
+ AudioUnitRenderActionFlags* UNUSED(ioActionFlags), const AudioTimeStamp* UNUSED(inTimeStamp),
+ UInt32 UNUSED(inBusNumber), UInt32 UNUSED(inNumberFrames), AudioBufferList *ioData)
+{
+ ALCcoreAudioPlayback *self = inRefCon;
+ ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
- WriteRingBuffer(data->ring, data->bufferList->mBuffers[0].mData, inNumberFrames);
+ ALCcoreAudioPlayback_lock(self);
+ aluMixData(device, ioData->mBuffers[0].mData,
+ ioData->mBuffers[0].mDataByteSize / self->frameSize);
+ ALCcoreAudioPlayback_unlock(self);
return noErr;
}
-static ALCenum ca_open_playback(ALCdevice *device, const ALCchar *deviceName)
+
+static ALCenum ALCcoreAudioPlayback_open(ALCcoreAudioPlayback *self, const ALCchar *name)
{
- ComponentDescription desc;
- Component comp;
- ca_data *data;
+ ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
+ AudioComponentDescription desc;
+ AudioComponent comp;
OSStatus err;
- if(!deviceName)
- deviceName = ca_device;
- else if(strcmp(deviceName, ca_device) != 0)
+ if(!name)
+ name = ca_device;
+ else if(strcmp(name, ca_device) != 0)
return ALC_INVALID_VALUE;
/* open the default output unit */
desc.componentType = kAudioUnitType_Output;
+#if TARGET_OS_IOS
+ desc.componentSubType = kAudioUnitSubType_RemoteIO;
+#else
desc.componentSubType = kAudioUnitSubType_DefaultOutput;
+#endif
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
desc.componentFlags = 0;
desc.componentFlagsMask = 0;
- comp = FindNextComponent(NULL, &desc);
+ comp = AudioComponentFindNext(NULL, &desc);
if(comp == NULL)
{
- ERR("FindNextComponent failed\n");
+ ERR("AudioComponentFindNext failed\n");
return ALC_INVALID_VALUE;
}
- data = calloc(1, sizeof(*data));
-
- err = OpenAComponent(comp, &data->audioUnit);
+ err = AudioComponentInstanceNew(comp, &self->audioUnit);
if(err != noErr)
{
- ERR("OpenAComponent failed\n");
- free(data);
+ ERR("AudioComponentInstanceNew failed\n");
return ALC_INVALID_VALUE;
}
/* init and start the default audio unit... */
- err = AudioUnitInitialize(data->audioUnit);
+ err = AudioUnitInitialize(self->audioUnit);
if(err != noErr)
{
ERR("AudioUnitInitialize failed\n");
- CloseComponent(data->audioUnit);
- free(data);
+ AudioComponentInstanceDispose(self->audioUnit);
return ALC_INVALID_VALUE;
}
- al_string_copy_cstr(&device->DeviceName, deviceName);
- device->ExtraData = data;
+ alstr_copy_cstr(&device->DeviceName, name);
return ALC_NO_ERROR;
}
-static void ca_close_playback(ALCdevice *device)
+static ALCboolean ALCcoreAudioPlayback_reset(ALCcoreAudioPlayback *self)
{
- ca_data *data = (ca_data*)device->ExtraData;
-
- AudioUnitUninitialize(data->audioUnit);
- CloseComponent(data->audioUnit);
-
- free(data);
- device->ExtraData = NULL;
-}
-
-static ALCboolean ca_reset_playback(ALCdevice *device)
-{
- ca_data *data = (ca_data*)device->ExtraData;
+ ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
AudioStreamBasicDescription streamFormat;
AURenderCallbackStruct input;
OSStatus err;
UInt32 size;
- err = AudioUnitUninitialize(data->audioUnit);
+ err = AudioUnitUninitialize(self->audioUnit);
if(err != noErr)
ERR("-- AudioUnitUninitialize failed.\n");
/* retrieve default output unit's properties (output side) */
size = sizeof(AudioStreamBasicDescription);
- err = AudioUnitGetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &streamFormat, &size);
+ err = AudioUnitGetProperty(self->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &streamFormat, &size);
if(err != noErr || size != sizeof(AudioStreamBasicDescription))
{
ERR("AudioUnitGetProperty failed\n");
@@ -229,7 +179,7 @@ static ALCboolean ca_reset_playback(ALCdevice *device)
#endif
/* set default output unit's input side to match output side */
- err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &streamFormat, size);
+ err = AudioUnitSetProperty(self->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &streamFormat, size);
if(err != noErr)
{
ERR("AudioUnitSetProperty failed\n");
@@ -238,7 +188,7 @@ static ALCboolean ca_reset_playback(ALCdevice *device)
if(device->Frequency != streamFormat.mSampleRate)
{
- device->UpdateSize = (ALuint)((ALuint64)device->UpdateSize *
+ device->NumUpdates = (ALuint)((ALuint64)device->NumUpdates *
streamFormat.mSampleRate /
device->Frequency);
device->Frequency = streamFormat.mSampleRate;
@@ -313,7 +263,7 @@ static ALCboolean ca_reset_playback(ALCdevice *device)
streamFormat.mFormatFlags |= kAudioFormatFlagsNativeEndian |
kLinearPCMFormatFlagIsPacked;
- err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &streamFormat, sizeof(AudioStreamBasicDescription));
+ err = AudioUnitSetProperty(self->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &streamFormat, sizeof(AudioStreamBasicDescription));
if(err != noErr)
{
ERR("AudioUnitSetProperty failed\n");
@@ -321,11 +271,11 @@ static ALCboolean ca_reset_playback(ALCdevice *device)
}
/* setup callback */
- data->frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
- input.inputProc = ca_callback;
- input.inputProcRefCon = device;
+ self->frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
+ input.inputProc = ALCcoreAudioPlayback_MixerProc;
+ input.inputProcRefCon = self;
- err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &input, sizeof(AURenderCallbackStruct));
+ err = AudioUnitSetProperty(self->audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &input, sizeof(AURenderCallbackStruct));
if(err != noErr)
{
ERR("AudioUnitSetProperty failed\n");
@@ -333,7 +283,7 @@ static ALCboolean ca_reset_playback(ALCdevice *device)
}
/* init the default audio unit... */
- err = AudioUnitInitialize(data->audioUnit);
+ err = AudioUnitInitialize(self->audioUnit);
if(err != noErr)
{
ERR("AudioUnitInitialize failed\n");
@@ -343,12 +293,9 @@ static ALCboolean ca_reset_playback(ALCdevice *device)
return ALC_TRUE;
}
-static ALCboolean ca_start_playback(ALCdevice *device)
+static ALCboolean ALCcoreAudioPlayback_start(ALCcoreAudioPlayback *self)
{
- ca_data *data = (ca_data*)device->ExtraData;
- OSStatus err;
-
- err = AudioOutputUnitStart(data->audioUnit);
+ OSStatus err = AudioOutputUnitStart(self->audioUnit);
if(err != noErr)
{
ERR("AudioOutputUnitStart failed\n");
@@ -358,64 +305,196 @@ static ALCboolean ca_start_playback(ALCdevice *device)
return ALC_TRUE;
}
-static void ca_stop_playback(ALCdevice *device)
+static void ALCcoreAudioPlayback_stop(ALCcoreAudioPlayback *self)
+{
+ OSStatus err = AudioOutputUnitStop(self->audioUnit);
+ if(err != noErr)
+ ERR("AudioOutputUnitStop failed\n");
+}
+
+
+
+
+typedef struct ALCcoreAudioCapture {
+ DERIVE_FROM_TYPE(ALCbackend);
+
+ AudioUnit audioUnit;
+
+ ALuint frameSize;
+ ALdouble sampleRateRatio; // Ratio of hardware sample rate / requested sample rate
+ AudioStreamBasicDescription format; // This is the OpenAL format as a CoreAudio ASBD
+
+ AudioConverterRef audioConverter; // Sample rate converter if needed
+ AudioBufferList *bufferList; // Buffer for data coming from the input device
+ ALCvoid *resampleBuffer; // Buffer for returned RingBuffer data when resampling
+
+ ll_ringbuffer_t *ring;
+} ALCcoreAudioCapture;
+
+static void ALCcoreAudioCapture_Construct(ALCcoreAudioCapture *self, ALCdevice *device);
+static void ALCcoreAudioCapture_Destruct(ALCcoreAudioCapture *self);
+static ALCenum ALCcoreAudioCapture_open(ALCcoreAudioCapture *self, const ALCchar *name);
+static DECLARE_FORWARD(ALCcoreAudioCapture, ALCbackend, ALCboolean, reset)
+static ALCboolean ALCcoreAudioCapture_start(ALCcoreAudioCapture *self);
+static void ALCcoreAudioCapture_stop(ALCcoreAudioCapture *self);
+static ALCenum ALCcoreAudioCapture_captureSamples(ALCcoreAudioCapture *self, ALCvoid *buffer, ALCuint samples);
+static ALCuint ALCcoreAudioCapture_availableSamples(ALCcoreAudioCapture *self);
+static DECLARE_FORWARD(ALCcoreAudioCapture, ALCbackend, ClockLatency, getClockLatency)
+static DECLARE_FORWARD(ALCcoreAudioCapture, ALCbackend, void, lock)
+static DECLARE_FORWARD(ALCcoreAudioCapture, ALCbackend, void, unlock)
+DECLARE_DEFAULT_ALLOCATORS(ALCcoreAudioCapture)
+
+DEFINE_ALCBACKEND_VTABLE(ALCcoreAudioCapture);
+
+
+static AudioBufferList *allocate_buffer_list(UInt32 channelCount, UInt32 byteSize)
+{
+ AudioBufferList *list;
+
+ list = calloc(1, FAM_SIZE(AudioBufferList, mBuffers, 1) + byteSize);
+ if(list)
+ {
+ list->mNumberBuffers = 1;
+
+ list->mBuffers[0].mNumberChannels = channelCount;
+ list->mBuffers[0].mDataByteSize = byteSize;
+ list->mBuffers[0].mData = &list->mBuffers[1];
+ }
+ return list;
+}
+
+static void destroy_buffer_list(AudioBufferList *list)
+{
+ free(list);
+}
+
+
+static void ALCcoreAudioCapture_Construct(ALCcoreAudioCapture *self, ALCdevice *device)
+{
+ ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
+ SET_VTABLE2(ALCcoreAudioCapture, ALCbackend, self);
+
+ self->audioUnit = 0;
+ self->audioConverter = NULL;
+ self->bufferList = NULL;
+ self->resampleBuffer = NULL;
+ self->ring = NULL;
+}
+
+static void ALCcoreAudioCapture_Destruct(ALCcoreAudioCapture *self)
+{
+ ll_ringbuffer_free(self->ring);
+ self->ring = NULL;
+
+ free(self->resampleBuffer);
+ self->resampleBuffer = NULL;
+
+ destroy_buffer_list(self->bufferList);
+ self->bufferList = NULL;
+
+ if(self->audioConverter)
+ AudioConverterDispose(self->audioConverter);
+ self->audioConverter = NULL;
+
+ if(self->audioUnit)
+ AudioComponentInstanceDispose(self->audioUnit);
+ self->audioUnit = 0;
+
+ ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
+}
+
+
+static OSStatus ALCcoreAudioCapture_RecordProc(void *inRefCon,
+ AudioUnitRenderActionFlags* UNUSED(ioActionFlags),
+ const AudioTimeStamp *inTimeStamp, UInt32 UNUSED(inBusNumber),
+ UInt32 inNumberFrames, AudioBufferList* UNUSED(ioData))
{
- ca_data *data = (ca_data*)device->ExtraData;
+ ALCcoreAudioCapture *self = inRefCon;
+ AudioUnitRenderActionFlags flags = 0;
OSStatus err;
- err = AudioOutputUnitStop(data->audioUnit);
+ // fill the bufferList with data from the input device
+ err = AudioUnitRender(self->audioUnit, &flags, inTimeStamp, 1, inNumberFrames, self->bufferList);
if(err != noErr)
- ERR("AudioOutputUnitStop failed\n");
+ {
+ ERR("AudioUnitRender error: %d\n", err);
+ return err;
+ }
+
+ ll_ringbuffer_write(self->ring, self->bufferList->mBuffers[0].mData, inNumberFrames);
+
+ return noErr;
+}
+
+static OSStatus ALCcoreAudioCapture_ConvertCallback(AudioConverterRef UNUSED(inAudioConverter),
+ UInt32 *ioNumberDataPackets, AudioBufferList *ioData,
+ AudioStreamPacketDescription** UNUSED(outDataPacketDescription),
+ void *inUserData)
+{
+ ALCcoreAudioCapture *self = inUserData;
+
+ // Read from the ring buffer and store temporarily in a large buffer
+ ll_ringbuffer_read(self->ring, self->resampleBuffer, *ioNumberDataPackets);
+
+ // Set the input data
+ ioData->mNumberBuffers = 1;
+ ioData->mBuffers[0].mNumberChannels = self->format.mChannelsPerFrame;
+ ioData->mBuffers[0].mData = self->resampleBuffer;
+ ioData->mBuffers[0].mDataByteSize = (*ioNumberDataPackets) * self->format.mBytesPerFrame;
+
+ return noErr;
}
-static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
+
+static ALCenum ALCcoreAudioCapture_open(ALCcoreAudioCapture *self, const ALCchar *name)
{
+ ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
AudioStreamBasicDescription requestedFormat; // The application requested format
AudioStreamBasicDescription hardwareFormat; // The hardware format
AudioStreamBasicDescription outputFormat; // The AudioUnit output format
AURenderCallbackStruct input;
- ComponentDescription desc;
- AudioDeviceID inputDevice;
+ AudioComponentDescription desc;
UInt32 outputFrameCount;
UInt32 propertySize;
+ AudioObjectPropertyAddress propertyAddress;
UInt32 enableIO;
- Component comp;
- ca_data *data;
+ AudioComponent comp;
OSStatus err;
- if(!deviceName)
- deviceName = ca_device;
- else if(strcmp(deviceName, ca_device) != 0)
+ if(!name)
+ name = ca_device;
+ else if(strcmp(name, ca_device) != 0)
return ALC_INVALID_VALUE;
desc.componentType = kAudioUnitType_Output;
+#if TARGET_OS_IOS
+ desc.componentSubType = kAudioUnitSubType_RemoteIO;
+#else
desc.componentSubType = kAudioUnitSubType_HALOutput;
+#endif
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
desc.componentFlags = 0;
desc.componentFlagsMask = 0;
// Search for component with given description
- comp = FindNextComponent(NULL, &desc);
+ comp = AudioComponentFindNext(NULL, &desc);
if(comp == NULL)
{
- ERR("FindNextComponent failed\n");
+ ERR("AudioComponentFindNext failed\n");
return ALC_INVALID_VALUE;
}
- data = calloc(1, sizeof(*data));
- device->ExtraData = data;
-
// Open the component
- err = OpenAComponent(comp, &data->audioUnit);
+ err = AudioComponentInstanceNew(comp, &self->audioUnit);
if(err != noErr)
{
- ERR("OpenAComponent failed\n");
+ ERR("AudioComponentInstanceNew failed\n");
goto error;
}
// Turn off AudioUnit output
enableIO = 0;
- err = AudioUnitSetProperty(data->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, &enableIO, sizeof(ALuint));
+ err = AudioUnitSetProperty(self->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, &enableIO, sizeof(ALuint));
if(err != noErr)
{
ERR("AudioUnitSetProperty failed\n");
@@ -424,22 +503,28 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
// Turn on AudioUnit input
enableIO = 1;
- err = AudioUnitSetProperty(data->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &enableIO, sizeof(ALuint));
+ err = AudioUnitSetProperty(self->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &enableIO, sizeof(ALuint));
if(err != noErr)
{
ERR("AudioUnitSetProperty failed\n");
goto error;
}
+#if !TARGET_OS_IOS
// Get the default input device
+ AudioDeviceID inputDevice = kAudioDeviceUnknown;
+
propertySize = sizeof(AudioDeviceID);
- err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, &propertySize, &inputDevice);
+ propertyAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice;
+ propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
+ propertyAddress.mElement = kAudioObjectPropertyElementMaster;
+
+ err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &propertySize, &inputDevice);
if(err != noErr)
{
- ERR("AudioHardwareGetProperty failed\n");
+ ERR("AudioObjectGetPropertyData failed\n");
goto error;
}
-
if(inputDevice == kAudioDeviceUnknown)
{
ERR("No input device found\n");
@@ -447,18 +532,19 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
}
// Track the input device
- err = AudioUnitSetProperty(data->audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &inputDevice, sizeof(AudioDeviceID));
+ err = AudioUnitSetProperty(self->audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &inputDevice, sizeof(AudioDeviceID));
if(err != noErr)
{
ERR("AudioUnitSetProperty failed\n");
goto error;
}
+#endif
// set capture callback
- input.inputProc = ca_capture_callback;
- input.inputProcRefCon = device;
+ input.inputProc = ALCcoreAudioCapture_RecordProc;
+ input.inputProcRefCon = self;
- err = AudioUnitSetProperty(data->audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &input, sizeof(AURenderCallbackStruct));
+ err = AudioUnitSetProperty(self->audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &input, sizeof(AURenderCallbackStruct));
if(err != noErr)
{
ERR("AudioUnitSetProperty failed\n");
@@ -466,7 +552,7 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
}
// Initialize the device
- err = AudioUnitInitialize(data->audioUnit);
+ err = AudioUnitInitialize(self->audioUnit);
if(err != noErr)
{
ERR("AudioUnitInitialize failed\n");
@@ -475,7 +561,7 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
// Get the hardware format
propertySize = sizeof(AudioStreamBasicDescription);
- err = AudioUnitGetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 1, &hardwareFormat, &propertySize);
+ err = AudioUnitGetProperty(self->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 1, &hardwareFormat, &propertySize);
if(err != noErr || propertySize != sizeof(AudioStreamBasicDescription))
{
ERR("AudioUnitGetProperty failed\n");
@@ -522,7 +608,7 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
case DevFmtX51Rear:
case DevFmtX61:
case DevFmtX71:
- case DevFmtBFormat3D:
+ case DevFmtAmbi3D:
ERR("%s not supported\n", DevFmtChannelsString(device->FmtChans));
goto error;
}
@@ -535,8 +621,8 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
requestedFormat.mFramesPerPacket = 1;
// save requested format description for later use
- data->format = requestedFormat;
- data->frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
+ self->format = requestedFormat;
+ self->frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
// Use intermediate format for sample rate conversion (outputFormat)
// Set sample rate to the same as hardware for resampling later
@@ -544,11 +630,11 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
outputFormat.mSampleRate = hardwareFormat.mSampleRate;
// Determine sample rate ratio for resampling
- data->sampleRateRatio = outputFormat.mSampleRate / device->Frequency;
+ self->sampleRateRatio = outputFormat.mSampleRate / device->Frequency;
// The output format should be the requested format, but using the hardware sample rate
// This is because the AudioUnit will automatically scale other properties, except for sample rate
- err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, (void *)&outputFormat, sizeof(outputFormat));
+ err = AudioUnitSetProperty(self->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, (void *)&outputFormat, sizeof(outputFormat));
if(err != noErr)
{
ERR("AudioUnitSetProperty failed\n");
@@ -556,8 +642,8 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
}
// Set the AudioUnit output format frame count
- outputFrameCount = device->UpdateSize * data->sampleRateRatio;
- err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Output, 0, &outputFrameCount, sizeof(outputFrameCount));
+ outputFrameCount = device->UpdateSize * self->sampleRateRatio;
+ err = AudioUnitSetProperty(self->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Output, 0, &outputFrameCount, sizeof(outputFrameCount));
if(err != noErr)
{
ERR("AudioUnitSetProperty failed: %d\n", err);
@@ -565,7 +651,7 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
}
// Set up sample converter
- err = AudioConverterNew(&outputFormat, &requestedFormat, &data->audioConverter);
+ err = AudioConverterNew(&outputFormat, &requestedFormat, &self->audioConverter);
if(err != noErr)
{
ERR("AudioConverterNew failed: %d\n", err);
@@ -573,92 +659,83 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
}
// Create a buffer for use in the resample callback
- data->resampleBuffer = malloc(device->UpdateSize * data->frameSize * data->sampleRateRatio);
+ self->resampleBuffer = malloc(device->UpdateSize * self->frameSize * self->sampleRateRatio);
// Allocate buffer for the AudioUnit output
- data->bufferList = allocate_buffer_list(outputFormat.mChannelsPerFrame, device->UpdateSize * data->frameSize * data->sampleRateRatio);
- if(data->bufferList == NULL)
+ self->bufferList = allocate_buffer_list(outputFormat.mChannelsPerFrame, device->UpdateSize * self->frameSize * self->sampleRateRatio);
+ if(self->bufferList == NULL)
goto error;
- data->ring = CreateRingBuffer(data->frameSize, (device->UpdateSize * data->sampleRateRatio) * device->NumUpdates);
- if(data->ring == NULL)
- goto error;
+ self->ring = ll_ringbuffer_create(
+ (size_t)ceil(device->UpdateSize*self->sampleRateRatio*device->NumUpdates),
+ self->frameSize, false
+ );
+ if(!self->ring) goto error;
- al_string_copy_cstr(&device->DeviceName, deviceName);
+ alstr_copy_cstr(&device->DeviceName, name);
return ALC_NO_ERROR;
error:
- DestroyRingBuffer(data->ring);
- free(data->resampleBuffer);
- destroy_buffer_list(data->bufferList);
-
- if(data->audioConverter)
- AudioConverterDispose(data->audioConverter);
- if(data->audioUnit)
- CloseComponent(data->audioUnit);
-
- free(data);
- device->ExtraData = NULL;
+ ll_ringbuffer_free(self->ring);
+ self->ring = NULL;
+ free(self->resampleBuffer);
+ self->resampleBuffer = NULL;
+ destroy_buffer_list(self->bufferList);
+ self->bufferList = NULL;
+
+ if(self->audioConverter)
+ AudioConverterDispose(self->audioConverter);
+ self->audioConverter = NULL;
+ if(self->audioUnit)
+ AudioComponentInstanceDispose(self->audioUnit);
+ self->audioUnit = 0;
return ALC_INVALID_VALUE;
}
-static void ca_close_capture(ALCdevice *device)
-{
- ca_data *data = (ca_data*)device->ExtraData;
-
- DestroyRingBuffer(data->ring);
- free(data->resampleBuffer);
- destroy_buffer_list(data->bufferList);
- AudioConverterDispose(data->audioConverter);
- CloseComponent(data->audioUnit);
-
- free(data);
- device->ExtraData = NULL;
-}
-
-static void ca_start_capture(ALCdevice *device)
+static ALCboolean ALCcoreAudioCapture_start(ALCcoreAudioCapture *self)
{
- ca_data *data = (ca_data*)device->ExtraData;
- OSStatus err = AudioOutputUnitStart(data->audioUnit);
+ OSStatus err = AudioOutputUnitStart(self->audioUnit);
if(err != noErr)
+ {
ERR("AudioOutputUnitStart failed\n");
+ return ALC_FALSE;
+ }
+ return ALC_TRUE;
}
-static void ca_stop_capture(ALCdevice *device)
+static void ALCcoreAudioCapture_stop(ALCcoreAudioCapture *self)
{
- ca_data *data = (ca_data*)device->ExtraData;
- OSStatus err = AudioOutputUnitStop(data->audioUnit);
+ OSStatus err = AudioOutputUnitStop(self->audioUnit);
if(err != noErr)
ERR("AudioOutputUnitStop failed\n");
}
-static ALCenum ca_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint samples)
+static ALCenum ALCcoreAudioCapture_captureSamples(ALCcoreAudioCapture *self, ALCvoid *buffer, ALCuint samples)
{
- ca_data *data = (ca_data*)device->ExtraData;
- AudioBufferList *list;
+ union {
+ ALbyte _[sizeof(AudioBufferList) + sizeof(AudioBuffer)];
+ AudioBufferList list;
+ } audiobuf = { { 0 } };
UInt32 frameCount;
OSStatus err;
// If no samples are requested, just return
- if(samples == 0)
- return ALC_NO_ERROR;
-
- // Allocate a temporary AudioBufferList to use as the return resamples data
- list = alloca(sizeof(AudioBufferList) + sizeof(AudioBuffer));
+ if(samples == 0) return ALC_NO_ERROR;
// Point the resampling buffer to the capture buffer
- list->mNumberBuffers = 1;
- list->mBuffers[0].mNumberChannels = data->format.mChannelsPerFrame;
- list->mBuffers[0].mDataByteSize = samples * data->frameSize;
- list->mBuffers[0].mData = buffer;
+ audiobuf.list.mNumberBuffers = 1;
+ audiobuf.list.mBuffers[0].mNumberChannels = self->format.mChannelsPerFrame;
+ audiobuf.list.mBuffers[0].mDataByteSize = samples * self->frameSize;
+ audiobuf.list.mBuffers[0].mData = buffer;
// Resample into another AudioBufferList
frameCount = samples;
- err = AudioConverterFillComplexBuffer(data->audioConverter, ca_capture_conversion_callback,
- device, &frameCount, list, NULL);
+ err = AudioConverterFillComplexBuffer(self->audioConverter,
+ ALCcoreAudioCapture_ConvertCallback, self, &frameCount, &audiobuf.list, NULL
+ );
if(err != noErr)
{
ERR("AudioConverterFillComplexBuffer error: %d\n", err);
@@ -667,46 +744,73 @@ static ALCenum ca_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint sa
return ALC_NO_ERROR;
}
-static ALCuint ca_available_samples(ALCdevice *device)
+static ALCuint ALCcoreAudioCapture_availableSamples(ALCcoreAudioCapture *self)
{
- ca_data *data = device->ExtraData;
- return RingBufferSize(data->ring) / data->sampleRateRatio;
+ return ll_ringbuffer_read_space(self->ring) / self->sampleRateRatio;
}
-static const BackendFuncs ca_funcs = {
- ca_open_playback,
- ca_close_playback,
- ca_reset_playback,
- ca_start_playback,
- ca_stop_playback,
- ca_open_capture,
- ca_close_capture,
- ca_start_capture,
- ca_stop_capture,
- ca_capture_samples,
- ca_available_samples
-};
-
-ALCboolean alc_ca_init(BackendFuncs *func_list)
+typedef struct ALCcoreAudioBackendFactory {
+ DERIVE_FROM_TYPE(ALCbackendFactory);
+} ALCcoreAudioBackendFactory;
+#define ALCCOREAUDIOBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCcoreAudioBackendFactory, ALCbackendFactory) } }
+
+ALCbackendFactory *ALCcoreAudioBackendFactory_getFactory(void);
+
+static ALCboolean ALCcoreAudioBackendFactory_init(ALCcoreAudioBackendFactory *self);
+static DECLARE_FORWARD(ALCcoreAudioBackendFactory, ALCbackendFactory, void, deinit)
+static ALCboolean ALCcoreAudioBackendFactory_querySupport(ALCcoreAudioBackendFactory *self, ALCbackend_Type type);
+static void ALCcoreAudioBackendFactory_probe(ALCcoreAudioBackendFactory *self, enum DevProbe type, al_string *outnames);
+static ALCbackend* ALCcoreAudioBackendFactory_createBackend(ALCcoreAudioBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
+DEFINE_ALCBACKENDFACTORY_VTABLE(ALCcoreAudioBackendFactory);
+
+
+ALCbackendFactory *ALCcoreAudioBackendFactory_getFactory(void)
+{
+ static ALCcoreAudioBackendFactory factory = ALCCOREAUDIOBACKENDFACTORY_INITIALIZER;
+ return STATIC_CAST(ALCbackendFactory, &factory);
+}
+
+
+static ALCboolean ALCcoreAudioBackendFactory_init(ALCcoreAudioBackendFactory* UNUSED(self))
{
- *func_list = ca_funcs;
return ALC_TRUE;
}
-void alc_ca_deinit(void)
+static ALCboolean ALCcoreAudioBackendFactory_querySupport(ALCcoreAudioBackendFactory* UNUSED(self), ALCbackend_Type type)
{
+ if(type == ALCbackend_Playback || ALCbackend_Capture)
+ return ALC_TRUE;
+ return ALC_FALSE;
}
-void alc_ca_probe(enum DevProbe type)
+static void ALCcoreAudioBackendFactory_probe(ALCcoreAudioBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
{
switch(type)
{
case ALL_DEVICE_PROBE:
- AppendAllDevicesList(ca_device);
- break;
case CAPTURE_DEVICE_PROBE:
- AppendCaptureDeviceList(ca_device);
+ alstr_append_range(outnames, ca_device, ca_device+sizeof(ca_device));
break;
}
}
+
+static ALCbackend* ALCcoreAudioBackendFactory_createBackend(ALCcoreAudioBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
+{
+ if(type == ALCbackend_Playback)
+ {
+ ALCcoreAudioPlayback *backend;
+ NEW_OBJ(backend, ALCcoreAudioPlayback)(device);
+ if(!backend) return NULL;
+ return STATIC_CAST(ALCbackend, backend);
+ }
+ if(type == ALCbackend_Capture)
+ {
+ ALCcoreAudioCapture *backend;
+ NEW_OBJ(backend, ALCcoreAudioCapture)(device);
+ if(!backend) return NULL;
+ return STATIC_CAST(ALCbackend, backend);
+ }
+
+ return NULL;
+}
diff --git a/Alc/backends/dsound.c b/Alc/backends/dsound.c
index 4db4b557..c368cffb 100644
--- a/Alc/backends/dsound.c
+++ b/Alc/backends/dsound.c
@@ -34,6 +34,7 @@
#include "alMain.h"
#include "alu.h"
+#include "ringbuffer.h"
#include "threads.h"
#include "compat.h"
#include "alstring.h"
@@ -60,7 +61,7 @@
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"
+#define DEVNAME_HEAD "OpenAL Soft on "
#ifdef HAVE_DYNLOAD
@@ -123,7 +124,7 @@ static void clear_devlist(vector_DevMap *list)
{
#define DEINIT_STR(i) AL_STRING_DEINIT((i)->name)
VECTOR_FOR_EACH(DevMap, *list, DEINIT_STR);
- VECTOR_RESIZE(*list, 0);
+ VECTOR_RESIZE(*list, 0, 0);
#undef DEINIT_STR
}
@@ -145,19 +146,18 @@ static BOOL CALLBACK DSoundEnumDevices(GUID *guid, const WCHAR *desc, const WCHA
{
const DevMap *iter;
- al_string_copy_wcstr(&entry.name, desc);
- if(count == 0)
- al_string_append_cstr(&entry.name, DEVNAME_TAIL);
- else
+ alstr_copy_cstr(&entry.name, DEVNAME_HEAD);
+ alstr_append_wcstr(&entry.name, desc);
+ if(count != 0)
{
char str[64];
- snprintf(str, sizeof(str), " #%d"DEVNAME_TAIL, count+1);
- al_string_append_cstr(&entry.name, str);
+ snprintf(str, sizeof(str), " #%d", count+1);
+ alstr_append_cstr(&entry.name, str);
}
-#define MATCH_ENTRY(i) (al_string_cmp(entry.name, (i)->name) == 0)
+#define MATCH_ENTRY(i) (alstr_cmp(entry.name, (i)->name) == 0)
VECTOR_FIND_IF(iter, const DevMap, *devices, MATCH_ENTRY);
- if(iter == VECTOR_ITER_END(*devices)) break;
+ if(iter == VECTOR_END(*devices)) break;
#undef MATCH_ENTRY
count++;
}
@@ -166,7 +166,7 @@ static BOOL CALLBACK DSoundEnumDevices(GUID *guid, const WCHAR *desc, const WCHA
hr = StringFromCLSID(guid, &guidstr);
if(SUCCEEDED(hr))
{
- TRACE("Got device \"%s\", GUID \"%ls\"\n", al_string_get_cstr(entry.name), guidstr);
+ TRACE("Got device \"%s\", GUID \"%ls\"\n", alstr_get_cstr(entry.name), guidstr);
CoTaskMemFree(guidstr);
}
@@ -185,22 +185,21 @@ typedef struct ALCdsoundPlayback {
IDirectSoundNotify *Notifies;
HANDLE NotifyEvent;
- volatile int killNow;
+ ATOMIC(ALenum) killNow;
althrd_t thread;
} ALCdsoundPlayback;
static int ALCdsoundPlayback_mixerProc(void *ptr);
static void ALCdsoundPlayback_Construct(ALCdsoundPlayback *self, ALCdevice *device);
-static DECLARE_FORWARD(ALCdsoundPlayback, ALCbackend, void, Destruct)
+static void ALCdsoundPlayback_Destruct(ALCdsoundPlayback *self);
static ALCenum ALCdsoundPlayback_open(ALCdsoundPlayback *self, const ALCchar *name);
-static void ALCdsoundPlayback_close(ALCdsoundPlayback *self);
static ALCboolean ALCdsoundPlayback_reset(ALCdsoundPlayback *self);
static ALCboolean ALCdsoundPlayback_start(ALCdsoundPlayback *self);
static void ALCdsoundPlayback_stop(ALCdsoundPlayback *self);
static DECLARE_FORWARD2(ALCdsoundPlayback, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
static DECLARE_FORWARD(ALCdsoundPlayback, ALCbackend, ALCuint, availableSamples)
-static DECLARE_FORWARD(ALCdsoundPlayback, ALCbackend, ALint64, getLatency)
+static DECLARE_FORWARD(ALCdsoundPlayback, ALCbackend, ClockLatency, getClockLatency)
static DECLARE_FORWARD(ALCdsoundPlayback, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCdsoundPlayback, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCdsoundPlayback)
@@ -212,6 +211,35 @@ static void ALCdsoundPlayback_Construct(ALCdsoundPlayback *self, ALCdevice *devi
{
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(ALCdsoundPlayback, ALCbackend, self);
+
+ self->DS = NULL;
+ self->PrimaryBuffer = NULL;
+ self->Buffer = NULL;
+ self->Notifies = NULL;
+ self->NotifyEvent = NULL;
+ ATOMIC_INIT(&self->killNow, AL_TRUE);
+}
+
+static void ALCdsoundPlayback_Destruct(ALCdsoundPlayback *self)
+{
+ if(self->Notifies)
+ IDirectSoundNotify_Release(self->Notifies);
+ self->Notifies = NULL;
+ if(self->Buffer)
+ IDirectSoundBuffer_Release(self->Buffer);
+ self->Buffer = NULL;
+ if(self->PrimaryBuffer != NULL)
+ IDirectSoundBuffer_Release(self->PrimaryBuffer);
+ self->PrimaryBuffer = NULL;
+
+ if(self->DS)
+ IDirectSound_Release(self->DS);
+ self->DS = NULL;
+ if(self->NotifyEvent)
+ CloseHandle(self->NotifyEvent);
+ self->NotifyEvent = NULL;
+
+ ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
}
@@ -240,16 +268,17 @@ FORCE_ALIGN static int ALCdsoundPlayback_mixerProc(void *ptr)
{
ERR("Failed to get buffer caps: 0x%lx\n", err);
ALCdevice_Lock(device);
- aluHandleDisconnect(device);
+ aluHandleDisconnect(device, "Failure retrieving playback buffer info: 0x%lx", err);
ALCdevice_Unlock(device);
return 1;
}
- FrameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
+ FrameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
FragSize = device->UpdateSize * FrameSize;
IDirectSoundBuffer_GetCurrentPosition(self->Buffer, &LastCursor, NULL);
- while(!self->killNow)
+ while(!ATOMIC_LOAD(&self->killNow, almemory_order_acquire) &&
+ ATOMIC_LOAD(&device->Connected, almemory_order_acquire))
{
// Get current play cursor
IDirectSoundBuffer_GetCurrentPosition(self->Buffer, &PlayCursor, NULL);
@@ -264,7 +293,7 @@ FORCE_ALIGN static int ALCdsoundPlayback_mixerProc(void *ptr)
{
ERR("Failed to play buffer: 0x%lx\n", err);
ALCdevice_Lock(device);
- aluHandleDisconnect(device);
+ aluHandleDisconnect(device, "Failure starting playback: 0x%lx", err);
ALCdevice_Unlock(device);
return 1;
}
@@ -300,8 +329,10 @@ FORCE_ALIGN static int ALCdsoundPlayback_mixerProc(void *ptr)
if(SUCCEEDED(err))
{
// If we have an active context, mix data directly into output buffer otherwise fill with silence
+ ALCdevice_Lock(device);
aluMixData(device, WritePtr1, WriteCnt1/FrameSize);
aluMixData(device, WritePtr2, WriteCnt2/FrameSize);
+ ALCdevice_Unlock(device);
// Unlock output buffer only when successfully locked
IDirectSoundBuffer_Unlock(self->Buffer, WritePtr1, WriteCnt1, WritePtr2, WriteCnt2);
@@ -310,7 +341,7 @@ FORCE_ALIGN static int ALCdsoundPlayback_mixerProc(void *ptr)
{
ERR("Buffer lock error: %#lx\n", err);
ALCdevice_Lock(device);
- aluHandleDisconnect(device);
+ aluHandleDisconnect(device, "Failed to lock output buffer: 0x%lx", err);
ALCdevice_Unlock(device);
return 1;
}
@@ -342,23 +373,23 @@ static ALCenum ALCdsoundPlayback_open(ALCdsoundPlayback *self, const ALCchar *de
if(!deviceName && VECTOR_SIZE(PlaybackDevices) > 0)
{
- deviceName = al_string_get_cstr(VECTOR_FRONT(PlaybackDevices).name);
+ deviceName = alstr_get_cstr(VECTOR_FRONT(PlaybackDevices).name);
guid = &VECTOR_FRONT(PlaybackDevices).guid;
}
else
{
const DevMap *iter;
-#define MATCH_NAME(i) (al_string_cmp_cstr((i)->name, deviceName) == 0)
+#define MATCH_NAME(i) (alstr_cmp_cstr((i)->name, deviceName) == 0)
VECTOR_FIND_IF(iter, const DevMap, PlaybackDevices, MATCH_NAME);
#undef MATCH_NAME
- if(iter == VECTOR_ITER_END(PlaybackDevices))
+ if(iter == VECTOR_END(PlaybackDevices))
return ALC_INVALID_VALUE;
guid = &iter->guid;
}
hr = DS_OK;
- self->NotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ self->NotifyEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
if(self->NotifyEvent == NULL)
hr = E_FAIL;
@@ -380,29 +411,11 @@ static ALCenum ALCdsoundPlayback_open(ALCdsoundPlayback *self, const ALCchar *de
return ALC_INVALID_VALUE;
}
- al_string_copy_cstr(&device->DeviceName, deviceName);
+ alstr_copy_cstr(&device->DeviceName, deviceName);
return ALC_NO_ERROR;
}
-static void ALCdsoundPlayback_close(ALCdsoundPlayback *self)
-{
- if(self->Notifies)
- IDirectSoundNotify_Release(self->Notifies);
- self->Notifies = NULL;
- if(self->Buffer)
- IDirectSoundBuffer_Release(self->Buffer);
- self->Buffer = NULL;
- if(self->PrimaryBuffer != NULL)
- IDirectSoundBuffer_Release(self->PrimaryBuffer);
- self->PrimaryBuffer = NULL;
-
- IDirectSound_Release(self->DS);
- self->DS = NULL;
- CloseHandle(self->NotifyEvent);
- self->NotifyEvent = NULL;
-}
-
static ALCboolean ALCdsoundPlayback_reset(ALCdsoundPlayback *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
@@ -473,7 +486,7 @@ static ALCboolean ALCdsoundPlayback_reset(ALCdsoundPlayback *self)
case DevFmtMono:
OutputType.dwChannelMask = SPEAKER_FRONT_CENTER;
break;
- case DevFmtBFormat3D:
+ case DevFmtAmbi3D:
device->FmtChans = DevFmtStereo;
/*fall-through*/
case DevFmtStereo:
@@ -526,7 +539,7 @@ static ALCboolean ALCdsoundPlayback_reset(ALCdsoundPlayback *self)
retry_open:
hr = S_OK;
OutputType.Format.wFormatTag = WAVE_FORMAT_PCM;
- OutputType.Format.nChannels = ChannelsFromDevFmt(device->FmtChans);
+ OutputType.Format.nChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
OutputType.Format.wBitsPerSample = BytesFromDevFmt(device->FmtType) * 8;
OutputType.Format.nBlockAlign = OutputType.Format.nChannels*OutputType.Format.wBitsPerSample/8;
OutputType.Format.nSamplesPerSec = device->Frequency;
@@ -625,7 +638,7 @@ retry_open:
static ALCboolean ALCdsoundPlayback_start(ALCdsoundPlayback *self)
{
- self->killNow = 0;
+ ATOMIC_STORE(&self->killNow, AL_FALSE, almemory_order_release);
if(althrd_create(&self->thread, ALCdsoundPlayback_mixerProc, self) != althrd_success)
return ALC_FALSE;
@@ -636,10 +649,8 @@ static void ALCdsoundPlayback_stop(ALCdsoundPlayback *self)
{
int res;
- if(self->killNow)
+ if(ATOMIC_EXCHANGE(&self->killNow, AL_TRUE, almemory_order_acq_rel))
return;
-
- self->killNow = 1;
althrd_join(self->thread, &res);
IDirectSoundBuffer_Stop(self->Buffer);
@@ -654,19 +665,19 @@ typedef struct ALCdsoundCapture {
IDirectSoundCaptureBuffer *DSCbuffer;
DWORD BufferBytes;
DWORD Cursor;
- RingBuffer *Ring;
+
+ ll_ringbuffer_t *Ring;
} ALCdsoundCapture;
static void ALCdsoundCapture_Construct(ALCdsoundCapture *self, ALCdevice *device);
-static DECLARE_FORWARD(ALCdsoundCapture, ALCbackend, void, Destruct)
+static void ALCdsoundCapture_Destruct(ALCdsoundCapture *self);
static ALCenum ALCdsoundCapture_open(ALCdsoundCapture *self, const ALCchar *name);
-static void ALCdsoundCapture_close(ALCdsoundCapture *self);
static DECLARE_FORWARD(ALCdsoundCapture, ALCbackend, ALCboolean, reset)
static ALCboolean ALCdsoundCapture_start(ALCdsoundCapture *self);
static void ALCdsoundCapture_stop(ALCdsoundCapture *self);
static ALCenum ALCdsoundCapture_captureSamples(ALCdsoundCapture *self, ALCvoid *buffer, ALCuint samples);
static ALCuint ALCdsoundCapture_availableSamples(ALCdsoundCapture *self);
-static DECLARE_FORWARD(ALCdsoundCapture, ALCbackend, ALint64, getLatency)
+static DECLARE_FORWARD(ALCdsoundCapture, ALCbackend, ClockLatency, getClockLatency)
static DECLARE_FORWARD(ALCdsoundCapture, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCdsoundCapture, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCdsoundCapture)
@@ -677,6 +688,29 @@ static void ALCdsoundCapture_Construct(ALCdsoundCapture *self, ALCdevice *device
{
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(ALCdsoundCapture, ALCbackend, self);
+
+ self->DSC = NULL;
+ self->DSCbuffer = NULL;
+ self->Ring = NULL;
+}
+
+static void ALCdsoundCapture_Destruct(ALCdsoundCapture *self)
+{
+ ll_ringbuffer_free(self->Ring);
+ self->Ring = NULL;
+
+ if(self->DSCbuffer != NULL)
+ {
+ IDirectSoundCaptureBuffer_Stop(self->DSCbuffer);
+ IDirectSoundCaptureBuffer_Release(self->DSCbuffer);
+ self->DSCbuffer = NULL;
+ }
+
+ if(self->DSC)
+ IDirectSoundCapture_Release(self->DSC);
+ self->DSC = NULL;
+
+ ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
}
@@ -702,17 +736,17 @@ static ALCenum ALCdsoundCapture_open(ALCdsoundCapture *self, const ALCchar *devi
if(!deviceName && VECTOR_SIZE(CaptureDevices) > 0)
{
- deviceName = al_string_get_cstr(VECTOR_FRONT(CaptureDevices).name);
+ deviceName = alstr_get_cstr(VECTOR_FRONT(CaptureDevices).name);
guid = &VECTOR_FRONT(CaptureDevices).guid;
}
else
{
const DevMap *iter;
-#define MATCH_NAME(i) (al_string_cmp_cstr((i)->name, deviceName) == 0)
+#define MATCH_NAME(i) (alstr_cmp_cstr((i)->name, deviceName) == 0)
VECTOR_FIND_IF(iter, const DevMap, CaptureDevices, MATCH_NAME);
#undef MATCH_NAME
- if(iter == VECTOR_ITER_END(CaptureDevices))
+ if(iter == VECTOR_END(CaptureDevices))
return ALC_INVALID_VALUE;
guid = &iter->guid;
}
@@ -732,99 +766,98 @@ static ALCenum ALCdsoundCapture_open(ALCdsoundCapture *self, const ALCchar *devi
break;
}
- //DirectSoundCapture Init code
- hr = DirectSoundCaptureCreate(guid, &self->DSC, NULL);
- if(SUCCEEDED(hr))
+ memset(&InputType, 0, sizeof(InputType));
+ switch(device->FmtChans)
{
- memset(&InputType, 0, sizeof(InputType));
-
- switch(device->FmtChans)
- {
- case DevFmtMono:
- InputType.dwChannelMask = SPEAKER_FRONT_CENTER;
- break;
- case DevFmtStereo:
- InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
- SPEAKER_FRONT_RIGHT;
- break;
- case DevFmtQuad:
- InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
- SPEAKER_FRONT_RIGHT |
- SPEAKER_BACK_LEFT |
- SPEAKER_BACK_RIGHT;
- break;
- case DevFmtX51:
- InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
- SPEAKER_FRONT_RIGHT |
- SPEAKER_FRONT_CENTER |
- SPEAKER_LOW_FREQUENCY |
- SPEAKER_SIDE_LEFT |
- SPEAKER_SIDE_RIGHT;
- break;
- case DevFmtX51Rear:
- InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
- SPEAKER_FRONT_RIGHT |
- SPEAKER_FRONT_CENTER |
- SPEAKER_LOW_FREQUENCY |
- SPEAKER_BACK_LEFT |
- SPEAKER_BACK_RIGHT;
- break;
- case DevFmtX61:
- InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
- SPEAKER_FRONT_RIGHT |
- SPEAKER_FRONT_CENTER |
- SPEAKER_LOW_FREQUENCY |
- SPEAKER_BACK_CENTER |
- SPEAKER_SIDE_LEFT |
- SPEAKER_SIDE_RIGHT;
- break;
- case DevFmtX71:
- InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
- SPEAKER_FRONT_RIGHT |
- SPEAKER_FRONT_CENTER |
- SPEAKER_LOW_FREQUENCY |
- SPEAKER_BACK_LEFT |
- SPEAKER_BACK_RIGHT |
- SPEAKER_SIDE_LEFT |
- SPEAKER_SIDE_RIGHT;
- break;
- case DevFmtBFormat3D:
- break;
- }
+ case DevFmtMono:
+ InputType.dwChannelMask = SPEAKER_FRONT_CENTER;
+ break;
+ case DevFmtStereo:
+ InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
+ SPEAKER_FRONT_RIGHT;
+ break;
+ case DevFmtQuad:
+ InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
+ SPEAKER_FRONT_RIGHT |
+ SPEAKER_BACK_LEFT |
+ SPEAKER_BACK_RIGHT;
+ break;
+ case DevFmtX51:
+ InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
+ SPEAKER_FRONT_RIGHT |
+ SPEAKER_FRONT_CENTER |
+ SPEAKER_LOW_FREQUENCY |
+ SPEAKER_SIDE_LEFT |
+ SPEAKER_SIDE_RIGHT;
+ break;
+ case DevFmtX51Rear:
+ InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
+ SPEAKER_FRONT_RIGHT |
+ SPEAKER_FRONT_CENTER |
+ SPEAKER_LOW_FREQUENCY |
+ SPEAKER_BACK_LEFT |
+ SPEAKER_BACK_RIGHT;
+ break;
+ case DevFmtX61:
+ InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
+ SPEAKER_FRONT_RIGHT |
+ SPEAKER_FRONT_CENTER |
+ SPEAKER_LOW_FREQUENCY |
+ SPEAKER_BACK_CENTER |
+ SPEAKER_SIDE_LEFT |
+ SPEAKER_SIDE_RIGHT;
+ break;
+ case DevFmtX71:
+ InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
+ SPEAKER_FRONT_RIGHT |
+ SPEAKER_FRONT_CENTER |
+ SPEAKER_LOW_FREQUENCY |
+ SPEAKER_BACK_LEFT |
+ SPEAKER_BACK_RIGHT |
+ SPEAKER_SIDE_LEFT |
+ SPEAKER_SIDE_RIGHT;
+ break;
+ case DevFmtAmbi3D:
+ WARN("%s capture not supported\n", DevFmtChannelsString(device->FmtChans));
+ return ALC_INVALID_ENUM;
+ }
- InputType.Format.wFormatTag = WAVE_FORMAT_PCM;
- InputType.Format.nChannels = ChannelsFromDevFmt(device->FmtChans);
- InputType.Format.wBitsPerSample = BytesFromDevFmt(device->FmtType) * 8;
- InputType.Format.nBlockAlign = InputType.Format.nChannels*InputType.Format.wBitsPerSample/8;
- InputType.Format.nSamplesPerSec = device->Frequency;
- InputType.Format.nAvgBytesPerSec = InputType.Format.nSamplesPerSec*InputType.Format.nBlockAlign;
- InputType.Format.cbSize = 0;
+ InputType.Format.wFormatTag = WAVE_FORMAT_PCM;
+ InputType.Format.nChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
+ InputType.Format.wBitsPerSample = BytesFromDevFmt(device->FmtType) * 8;
+ InputType.Format.nBlockAlign = InputType.Format.nChannels*InputType.Format.wBitsPerSample/8;
+ InputType.Format.nSamplesPerSec = device->Frequency;
+ InputType.Format.nAvgBytesPerSec = InputType.Format.nSamplesPerSec*InputType.Format.nBlockAlign;
+ InputType.Format.cbSize = 0;
+ InputType.Samples.wValidBitsPerSample = InputType.Format.wBitsPerSample;
+ if(device->FmtType == DevFmtFloat)
+ InputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
+ else
+ InputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
- if(InputType.Format.nChannels > 2 || device->FmtType == DevFmtFloat)
- {
- InputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
- InputType.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
- InputType.Samples.wValidBitsPerSample = InputType.Format.wBitsPerSample;
- if(device->FmtType == DevFmtFloat)
- InputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
- else
- InputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
- }
+ if(InputType.Format.nChannels > 2 || device->FmtType == DevFmtFloat)
+ {
+ InputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
+ InputType.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
+ }
- samples = device->UpdateSize * device->NumUpdates;
- samples = maxu(samples, 100 * device->Frequency / 1000);
+ samples = device->UpdateSize * device->NumUpdates;
+ samples = maxu(samples, 100 * device->Frequency / 1000);
- memset(&DSCBDescription, 0, sizeof(DSCBUFFERDESC));
- DSCBDescription.dwSize = sizeof(DSCBUFFERDESC);
- DSCBDescription.dwFlags = 0;
- DSCBDescription.dwBufferBytes = samples * InputType.Format.nBlockAlign;
- DSCBDescription.lpwfxFormat = &InputType.Format;
+ memset(&DSCBDescription, 0, sizeof(DSCBUFFERDESC));
+ DSCBDescription.dwSize = sizeof(DSCBUFFERDESC);
+ DSCBDescription.dwFlags = 0;
+ DSCBDescription.dwBufferBytes = samples * InputType.Format.nBlockAlign;
+ DSCBDescription.lpwfxFormat = &InputType.Format;
+ //DirectSoundCapture Init code
+ hr = DirectSoundCaptureCreate(guid, &self->DSC, NULL);
+ if(SUCCEEDED(hr))
hr = IDirectSoundCapture_CreateCaptureBuffer(self->DSC, &DSCBDescription, &self->DSCbuffer, NULL);
- }
if(SUCCEEDED(hr))
{
- self->Ring = CreateRingBuffer(InputType.Format.nBlockAlign, device->UpdateSize * device->NumUpdates);
+ self->Ring = ll_ringbuffer_create(device->UpdateSize*device->NumUpdates,
+ InputType.Format.nBlockAlign, false);
if(self->Ring == NULL)
hr = DSERR_OUTOFMEMORY;
}
@@ -833,7 +866,7 @@ static ALCenum ALCdsoundCapture_open(ALCdsoundCapture *self, const ALCchar *devi
{
ERR("Device init failed: 0x%08lx\n", hr);
- DestroyRingBuffer(self->Ring);
+ ll_ringbuffer_free(self->Ring);
self->Ring = NULL;
if(self->DSCbuffer != NULL)
IDirectSoundCaptureBuffer_Release(self->DSCbuffer);
@@ -848,27 +881,11 @@ static ALCenum ALCdsoundCapture_open(ALCdsoundCapture *self, const ALCchar *devi
self->BufferBytes = DSCBDescription.dwBufferBytes;
SetDefaultWFXChannelOrder(device);
- al_string_copy_cstr(&device->DeviceName, deviceName);
+ alstr_copy_cstr(&device->DeviceName, deviceName);
return ALC_NO_ERROR;
}
-static void ALCdsoundCapture_close(ALCdsoundCapture *self)
-{
- DestroyRingBuffer(self->Ring);
- self->Ring = NULL;
-
- if(self->DSCbuffer != NULL)
- {
- IDirectSoundCaptureBuffer_Stop(self->DSCbuffer);
- IDirectSoundCaptureBuffer_Release(self->DSCbuffer);
- self->DSCbuffer = NULL;
- }
-
- IDirectSoundCapture_Release(self->DSC);
- self->DSC = NULL;
-}
-
static ALCboolean ALCdsoundCapture_start(ALCdsoundCapture *self)
{
HRESULT hr;
@@ -877,7 +894,8 @@ static ALCboolean ALCdsoundCapture_start(ALCdsoundCapture *self)
if(FAILED(hr))
{
ERR("start failed: 0x%08lx\n", hr);
- aluHandleDisconnect(STATIC_CAST(ALCbackend, self)->mDevice);
+ aluHandleDisconnect(STATIC_CAST(ALCbackend, self)->mDevice,
+ "Failure starting capture: 0x%lx", hr);
return ALC_FALSE;
}
@@ -892,13 +910,14 @@ static void ALCdsoundCapture_stop(ALCdsoundCapture *self)
if(FAILED(hr))
{
ERR("stop failed: 0x%08lx\n", hr);
- aluHandleDisconnect(STATIC_CAST(ALCbackend, self)->mDevice);
+ aluHandleDisconnect(STATIC_CAST(ALCbackend, self)->mDevice,
+ "Failure stopping capture: 0x%lx", hr);
}
}
static ALCenum ALCdsoundCapture_captureSamples(ALCdsoundCapture *self, ALCvoid *buffer, ALCuint samples)
{
- ReadRingBuffer(self->Ring, buffer, samples);
+ ll_ringbuffer_read(self->Ring, buffer, samples);
return ALC_NO_ERROR;
}
@@ -911,10 +930,10 @@ static ALCuint ALCdsoundCapture_availableSamples(ALCdsoundCapture *self)
DWORD FrameSize;
HRESULT hr;
- if(!device->Connected)
+ if(!ATOMIC_LOAD(&device->Connected, almemory_order_acquire))
goto done;
- FrameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
+ FrameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
BufferBytes = self->BufferBytes;
LastCursor = self->Cursor;
@@ -930,9 +949,9 @@ static ALCuint ALCdsoundCapture_availableSamples(ALCdsoundCapture *self)
}
if(SUCCEEDED(hr))
{
- WriteRingBuffer(self->Ring, ReadPtr1, ReadCnt1/FrameSize);
+ ll_ringbuffer_write(self->Ring, ReadPtr1, ReadCnt1/FrameSize);
if(ReadPtr2 != NULL)
- WriteRingBuffer(self->Ring, ReadPtr2, ReadCnt2/FrameSize);
+ ll_ringbuffer_write(self->Ring, ReadPtr2, ReadCnt2/FrameSize);
hr = IDirectSoundCaptureBuffer_Unlock(self->DSCbuffer,
ReadPtr1, ReadCnt1,
ReadPtr2, ReadCnt2);
@@ -942,19 +961,14 @@ static ALCuint ALCdsoundCapture_availableSamples(ALCdsoundCapture *self)
if(FAILED(hr))
{
ERR("update failed: 0x%08lx\n", hr);
- aluHandleDisconnect(device);
+ aluHandleDisconnect(device, "Failure retrieving capture data: 0x%lx", hr);
}
done:
- return RingBufferSize(self->Ring);
+ return (ALCuint)ll_ringbuffer_read_space(self->Ring);
}
-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 ALCdsoundBackendFactory {
DERIVE_FROM_TYPE(ALCbackendFactory);
} ALCdsoundBackendFactory;
@@ -965,7 +979,7 @@ ALCbackendFactory *ALCdsoundBackendFactory_getFactory(void);
static ALCboolean ALCdsoundBackendFactory_init(ALCdsoundBackendFactory *self);
static void ALCdsoundBackendFactory_deinit(ALCdsoundBackendFactory *self);
static ALCboolean ALCdsoundBackendFactory_querySupport(ALCdsoundBackendFactory *self, ALCbackend_Type type);
-static void ALCdsoundBackendFactory_probe(ALCdsoundBackendFactory *self, enum DevProbe type);
+static void ALCdsoundBackendFactory_probe(ALCdsoundBackendFactory *self, enum DevProbe type, al_string *outnames);
static ALCbackend* ALCdsoundBackendFactory_createBackend(ALCdsoundBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCdsoundBackendFactory);
@@ -1009,7 +1023,7 @@ static ALCboolean ALCdsoundBackendFactory_querySupport(ALCdsoundBackendFactory*
return ALC_FALSE;
}
-static void ALCdsoundBackendFactory_probe(ALCdsoundBackendFactory* UNUSED(self), enum DevProbe type)
+static void ALCdsoundBackendFactory_probe(ALCdsoundBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
{
HRESULT hr, hrcom;
@@ -1017,12 +1031,17 @@ static void ALCdsoundBackendFactory_probe(ALCdsoundBackendFactory* UNUSED(self),
hrcom = CoInitialize(NULL);
switch(type)
{
+#define APPEND_OUTNAME(e) do { \
+ if(!alstr_empty((e)->name)) \
+ alstr_append_range(outnames, VECTOR_BEGIN((e)->name), \
+ VECTOR_END((e)->name)+1); \
+} while(0)
case ALL_DEVICE_PROBE:
clear_devlist(&PlaybackDevices);
hr = DirectSoundEnumerateW(DSoundEnumDevices, &PlaybackDevices);
if(FAILED(hr))
ERR("Error enumerating DirectSound playback devices (0x%lx)!\n", hr);
- VECTOR_FOR_EACH(const DevMap, PlaybackDevices, AppendAllDevicesList2);
+ VECTOR_FOR_EACH(const DevMap, PlaybackDevices, APPEND_OUTNAME);
break;
case CAPTURE_DEVICE_PROBE:
@@ -1030,8 +1049,9 @@ static void ALCdsoundBackendFactory_probe(ALCdsoundBackendFactory* UNUSED(self),
hr = DirectSoundCaptureEnumerateW(DSoundEnumDevices, &CaptureDevices);
if(FAILED(hr))
ERR("Error enumerating DirectSound capture devices (0x%lx)!\n", hr);
- VECTOR_FOR_EACH(const DevMap, CaptureDevices, AppendCaptureDeviceList2);
+ VECTOR_FOR_EACH(const DevMap, CaptureDevices, APPEND_OUTNAME);
break;
+#undef APPEND_OUTNAME
}
if(SUCCEEDED(hrcom))
CoUninitialize();
diff --git a/Alc/backends/jack.c b/Alc/backends/jack.c
index 69d1277a..fdbe93f2 100644
--- a/Alc/backends/jack.c
+++ b/Alc/backends/jack.c
@@ -26,6 +26,8 @@
#include "alMain.h"
#include "alu.h"
+#include "alconfig.h"
+#include "ringbuffer.h"
#include "threads.h"
#include "compat.h"
@@ -54,6 +56,7 @@ static const ALCchar jackDevice[] = "JACK Default";
MAGIC(jack_get_ports); \
MAGIC(jack_free); \
MAGIC(jack_get_sample_rate); \
+ MAGIC(jack_set_error_function); \
MAGIC(jack_set_process_callback); \
MAGIC(jack_set_buffer_size_callback); \
MAGIC(jack_set_buffer_size); \
@@ -62,6 +65,7 @@ static const ALCchar jackDevice[] = "JACK Default";
static void *jack_handle;
#define MAKE_FUNC(f) static __typeof(f) * p##f
JACK_FUNCS(MAKE_FUNC);
+static __typeof(jack_error_callback) * pjack_error_callback;
#undef MAKE_FUNC
#define jack_client_open pjack_client_open
@@ -78,10 +82,12 @@ JACK_FUNCS(MAKE_FUNC);
#define jack_get_ports pjack_get_ports
#define jack_free pjack_free
#define jack_get_sample_rate pjack_get_sample_rate
+#define jack_set_error_function pjack_set_error_function
#define jack_set_process_callback pjack_set_process_callback
#define jack_set_buffer_size_callback pjack_set_buffer_size_callback
#define jack_set_buffer_size pjack_set_buffer_size
#define jack_get_buffer_size pjack_get_buffer_size
+#define jack_error_callback (*pjack_error_callback)
#endif
@@ -94,26 +100,42 @@ static ALCboolean jack_load(void)
#ifdef HAVE_DYNLOAD
if(!jack_handle)
{
- jack_handle = LoadLib("libjack.so.0");
+ al_string missing_funcs = AL_STRING_INIT_STATIC();
+
+#ifdef _WIN32
+#define JACKLIB "libjack.dll"
+#else
+#define JACKLIB "libjack.so.0"
+#endif
+ jack_handle = LoadLib(JACKLIB);
if(!jack_handle)
+ {
+ WARN("Failed to load %s\n", JACKLIB);
return ALC_FALSE;
+ }
error = ALC_FALSE;
#define LOAD_FUNC(f) do { \
p##f = GetSymbol(jack_handle, #f); \
if(p##f == NULL) { \
error = ALC_TRUE; \
+ alstr_append_cstr(&missing_funcs, "\n" #f); \
} \
} while(0)
JACK_FUNCS(LOAD_FUNC);
#undef LOAD_FUNC
+ /* Optional symbols. These don't exist in all versions of JACK. */
+#define LOAD_SYM(f) p##f = GetSymbol(jack_handle, #f)
+ LOAD_SYM(jack_error_callback);
+#undef LOAD_SYM
if(error)
{
+ WARN("Missing expected functions:%s\n", alstr_get_cstr(missing_funcs));
CloseLib(jack_handle);
jack_handle = NULL;
- return ALC_FALSE;
}
+ alstr_reset(&missing_funcs);
}
#endif
@@ -128,9 +150,9 @@ typedef struct ALCjackPlayback {
jack_port_t *Port[MAX_OUTPUT_CHANNELS];
ll_ringbuffer_t *Ring;
- alcnd_t Cond;
+ alsem_t Sem;
- volatile int killNow;
+ ATOMIC(ALenum) killNow;
althrd_t thread;
} ALCjackPlayback;
@@ -142,15 +164,14 @@ static int ALCjackPlayback_mixerProc(void *arg);
static void ALCjackPlayback_Construct(ALCjackPlayback *self, ALCdevice *device);
static void ALCjackPlayback_Destruct(ALCjackPlayback *self);
static ALCenum ALCjackPlayback_open(ALCjackPlayback *self, const ALCchar *name);
-static void ALCjackPlayback_close(ALCjackPlayback *self);
static ALCboolean ALCjackPlayback_reset(ALCjackPlayback *self);
static ALCboolean ALCjackPlayback_start(ALCjackPlayback *self);
static void ALCjackPlayback_stop(ALCjackPlayback *self);
static DECLARE_FORWARD2(ALCjackPlayback, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
static DECLARE_FORWARD(ALCjackPlayback, ALCbackend, ALCuint, availableSamples)
-static ALint64 ALCjackPlayback_getLatency(ALCjackPlayback *self);
-static void ALCjackPlayback_lock(ALCjackPlayback *self);
-static void ALCjackPlayback_unlock(ALCjackPlayback *self);
+static ClockLatency ALCjackPlayback_getClockLatency(ALCjackPlayback *self);
+static DECLARE_FORWARD(ALCjackPlayback, ALCbackend, void, lock)
+static DECLARE_FORWARD(ALCjackPlayback, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCjackPlayback)
DEFINE_ALCBACKEND_VTABLE(ALCjackPlayback);
@@ -163,14 +184,14 @@ static void ALCjackPlayback_Construct(ALCjackPlayback *self, ALCdevice *device)
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(ALCjackPlayback, ALCbackend, self);
- alcnd_init(&self->Cond);
+ alsem_init(&self->Sem, 0);
self->Client = NULL;
for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
self->Port[i] = NULL;
self->Ring = NULL;
- self->killNow = 1;
+ ATOMIC_INIT(&self->killNow, AL_TRUE);
}
static void ALCjackPlayback_Destruct(ALCjackPlayback *self)
@@ -189,7 +210,7 @@ static void ALCjackPlayback_Destruct(ALCjackPlayback *self)
self->Client = NULL;
}
- alcnd_destroy(&self->Cond);
+ alsem_destroy(&self->Sem);
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
}
@@ -204,19 +225,23 @@ static int ALCjackPlayback_bufferSizeNotify(jack_nframes_t numframes, void *arg)
ALCjackPlayback_lock(self);
device->UpdateSize = numframes;
device->NumUpdates = 2;
- TRACE("%u update size x%u\n", device->UpdateSize, device->NumUpdates);
bufsize = device->UpdateSize;
- if(ConfigValueUInt(al_string_get_cstr(device->DeviceName), "jack", "buffer-size", &bufsize))
+ if(ConfigValueUInt(alstr_get_cstr(device->DeviceName), "jack", "buffer-size", &bufsize))
bufsize = maxu(NextPowerOf2(bufsize), device->UpdateSize);
- bufsize += device->UpdateSize;
+ device->NumUpdates = (bufsize+device->UpdateSize) / device->UpdateSize;
+
+ TRACE("%u update size x%u\n", device->UpdateSize, device->NumUpdates);
ll_ringbuffer_free(self->Ring);
- self->Ring = ll_ringbuffer_create(bufsize, FrameSizeFromDevFmt(device->FmtChans, device->FmtType));
+ self->Ring = ll_ringbuffer_create(bufsize,
+ FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder),
+ true
+ );
if(!self->Ring)
{
ERR("Failed to reallocate ringbuffer\n");
- aluHandleDisconnect(device);
+ aluHandleDisconnect(device, "Failed to reallocate %u-sample buffer", bufsize);
}
ALCjackPlayback_unlock(self);
return 0;
@@ -230,7 +255,7 @@ static int ALCjackPlayback_process(jack_nframes_t numframes, void *arg)
ll_ringbuffer_data_t data[2];
jack_nframes_t total = 0;
jack_nframes_t todo;
- ALuint i, c, numchans;
+ ALsizei i, c, numchans;
ll_ringbuffer_get_read_vector(self->Ring, data);
@@ -241,8 +266,9 @@ static int ALCjackPlayback_process(jack_nframes_t numframes, void *arg)
todo = minu(numframes, data[0].len);
for(c = 0;c < numchans;c++)
{
- for(i = 0;i < todo;i++)
- out[c][i] = ((ALfloat*)data[0].buf)[i*numchans + c];
+ const ALfloat *restrict in = ((ALfloat*)data[0].buf) + c;
+ for(i = 0;(jack_nframes_t)i < todo;i++)
+ out[c][i] = in[i*numchans];
out[c] += todo;
}
total += todo;
@@ -252,22 +278,23 @@ static int ALCjackPlayback_process(jack_nframes_t numframes, void *arg)
{
for(c = 0;c < numchans;c++)
{
- for(i = 0;i < todo;i++)
- out[c][i] = ((ALfloat*)data[1].buf)[i*numchans + c];
+ const ALfloat *restrict in = ((ALfloat*)data[1].buf) + c;
+ for(i = 0;(jack_nframes_t)i < todo;i++)
+ out[c][i] = in[i*numchans];
out[c] += todo;
}
total += todo;
}
ll_ringbuffer_read_advance(self->Ring, total);
- alcnd_signal(&self->Cond);
+ alsem_post(&self->Sem);
if(numframes > total)
{
todo = numframes-total;
for(c = 0;c < numchans;c++)
{
- for(i = 0;i < todo;i++)
+ for(i = 0;(jack_nframes_t)i < todo;i++)
out[c][i] = 0.0f;
}
}
@@ -285,27 +312,16 @@ static int ALCjackPlayback_mixerProc(void *arg)
althrd_setname(althrd_current(), MIXER_THREAD_NAME);
ALCjackPlayback_lock(self);
- while(!self->killNow && device->Connected)
+ while(!ATOMIC_LOAD(&self->killNow, almemory_order_acquire) &&
+ ATOMIC_LOAD(&device->Connected, almemory_order_acquire))
{
ALuint todo, len1, len2;
- /* NOTE: Unfortunately, there is an unavoidable race condition here.
- * It's possible for the process() method to run, updating the read
- * pointer and signaling the condition variable, in between the mixer
- * loop checking the write size and waiting for the condition variable.
- * This will cause the mixer loop to wait until the *next* process()
- * invocation, most likely writing silence for it.
- *
- * However, this should only happen if the mixer is running behind
- * anyway (as ideally we'll be asleep in alcnd_wait by the time the
- * process() method is invoked), so this behavior is not unwarranted.
- * It's unfortunate since it'll be wasting time sleeping that could be
- * used to catch up, but there's no way around it without blocking in
- * the process() method.
- */
if(ll_ringbuffer_write_space(self->Ring) < device->UpdateSize)
{
- alcnd_wait(&self->Cond, &STATIC_CAST(ALCbackend,self)->mMutex);
+ ALCjackPlayback_unlock(self);
+ alsem_wait(&self->Sem);
+ ALCjackPlayback_lock(self);
continue;
}
@@ -355,29 +371,15 @@ static ALCenum ALCjackPlayback_open(ALCjackPlayback *self, const ALCchar *name)
jack_set_process_callback(self->Client, ALCjackPlayback_process, self);
jack_set_buffer_size_callback(self->Client, ALCjackPlayback_bufferSizeNotify, self);
- al_string_copy_cstr(&device->DeviceName, name);
+ alstr_copy_cstr(&device->DeviceName, name);
return ALC_NO_ERROR;
}
-static void ALCjackPlayback_close(ALCjackPlayback *self)
-{
- ALuint i;
-
- for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
- {
- if(self->Port[i])
- jack_port_unregister(self->Client, self->Port[i]);
- self->Port[i] = NULL;
- }
- jack_client_close(self->Client);
- self->Client = NULL;
-}
-
static ALCboolean ALCjackPlayback_reset(ALCjackPlayback *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
- ALuint numchans, i;
+ ALsizei numchans, i;
ALuint bufsize;
for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
@@ -388,23 +390,21 @@ static ALCboolean ALCjackPlayback_reset(ALCjackPlayback *self)
}
/* Ignore the requested buffer metrics and just keep one JACK-sized buffer
- * ready for when requested. Note that one period's worth of audio in the
- * ring buffer will always be left unfilled because one element of the ring
- * buffer will not be writeable, and we only write in period-sized chunks.
+ * ready for when requested.
*/
device->Frequency = jack_get_sample_rate(self->Client);
device->UpdateSize = jack_get_buffer_size(self->Client);
device->NumUpdates = 2;
bufsize = device->UpdateSize;
- if(ConfigValueUInt(al_string_get_cstr(device->DeviceName), "jack", "buffer-size", &bufsize))
+ if(ConfigValueUInt(alstr_get_cstr(device->DeviceName), "jack", "buffer-size", &bufsize))
bufsize = maxu(NextPowerOf2(bufsize), device->UpdateSize);
- bufsize += device->UpdateSize;
+ device->NumUpdates = (bufsize+device->UpdateSize) / device->UpdateSize;
/* Force 32-bit float output. */
device->FmtType = DevFmtFloat;
- numchans = ChannelsFromDevFmt(device->FmtChans);
+ numchans = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
for(i = 0;i < numchans;i++)
{
char name[64];
@@ -433,7 +433,10 @@ static ALCboolean ALCjackPlayback_reset(ALCjackPlayback *self)
}
ll_ringbuffer_free(self->Ring);
- self->Ring = ll_ringbuffer_create(bufsize, FrameSizeFromDevFmt(device->FmtChans, device->FmtType));
+ self->Ring = ll_ringbuffer_create(bufsize,
+ FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder),
+ true
+ );
if(!self->Ring)
{
ERR("Failed to allocate ringbuffer\n");
@@ -448,7 +451,7 @@ static ALCboolean ALCjackPlayback_reset(ALCjackPlayback *self)
static ALCboolean ALCjackPlayback_start(ALCjackPlayback *self)
{
const char **ports;
- ALuint i;
+ ALsizei i;
if(jack_activate(self->Client))
{
@@ -475,7 +478,7 @@ static ALCboolean ALCjackPlayback_start(ALCjackPlayback *self)
}
jack_free(ports);
- self->killNow = 0;
+ ATOMIC_STORE(&self->killNow, AL_FALSE, almemory_order_release);
if(althrd_create(&self->thread, ALCjackPlayback_mixerProc, self) != althrd_success)
{
jack_deactivate(self->Client);
@@ -489,47 +492,36 @@ static void ALCjackPlayback_stop(ALCjackPlayback *self)
{
int res;
- if(self->killNow)
+ if(ATOMIC_EXCHANGE(&self->killNow, AL_TRUE, almemory_order_acq_rel))
return;
- self->killNow = 1;
- /* Lock the backend to ensure we don't flag the mixer to die and signal the
- * mixer to wake up in between it checking the flag and going to sleep and
- * wait for a wakeup (potentially leading to it never waking back up to see
- * the flag). */
- ALCjackPlayback_lock(self);
- ALCjackPlayback_unlock(self);
- alcnd_signal(&self->Cond);
+ alsem_post(&self->Sem);
althrd_join(self->thread, &res);
jack_deactivate(self->Client);
}
-static ALint64 ALCjackPlayback_getLatency(ALCjackPlayback *self)
+static ClockLatency ALCjackPlayback_getClockLatency(ALCjackPlayback *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
- ALint64 latency;
+ ClockLatency ret;
ALCjackPlayback_lock(self);
- latency = ll_ringbuffer_read_space(self->Ring);
+ ret.ClockTime = GetDeviceClockTime(device);
+ ret.Latency = ll_ringbuffer_read_space(self->Ring) * DEVICE_CLOCK_RES /
+ device->Frequency;
ALCjackPlayback_unlock(self);
- return latency * 1000000000 / device->Frequency;
+ return ret;
}
-static void ALCjackPlayback_lock(ALCjackPlayback *self)
+static void jack_msg_handler(const char *message)
{
- almtx_lock(&STATIC_CAST(ALCbackend,self)->mMutex);
+ WARN("%s\n", message);
}
-static void ALCjackPlayback_unlock(ALCjackPlayback *self)
-{
- almtx_unlock(&STATIC_CAST(ALCbackend,self)->mMutex);
-}
-
-
typedef struct ALCjackBackendFactory {
DERIVE_FROM_TYPE(ALCbackendFactory);
} ALCjackBackendFactory;
@@ -537,6 +529,7 @@ typedef struct ALCjackBackendFactory {
static ALCboolean ALCjackBackendFactory_init(ALCjackBackendFactory* UNUSED(self))
{
+ void (*old_error_cb)(const char*);
jack_client_t *client;
jack_status_t status;
@@ -545,7 +538,11 @@ static ALCboolean ALCjackBackendFactory_init(ALCjackBackendFactory* UNUSED(self)
if(!GetConfigValueBool(NULL, "jack", "spawn-server", 0))
ClientOptions |= JackNoStartServer;
+
+ old_error_cb = (&jack_error_callback ? jack_error_callback : NULL);
+ jack_set_error_function(jack_msg_handler);
client = jack_client_open("alsoft", ClientOptions, &status, NULL);
+ jack_set_error_function(old_error_cb);
if(client == NULL)
{
WARN("jack_client_open() failed, 0x%02x\n", status);
@@ -574,12 +571,12 @@ static ALCboolean ALCjackBackendFactory_querySupport(ALCjackBackendFactory* UNUS
return ALC_FALSE;
}
-static void ALCjackBackendFactory_probe(ALCjackBackendFactory* UNUSED(self), enum DevProbe type)
+static void ALCjackBackendFactory_probe(ALCjackBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
{
switch(type)
{
case ALL_DEVICE_PROBE:
- AppendAllDevicesList(jackDevice);
+ alstr_append_range(outnames, jackDevice, jackDevice+sizeof(jackDevice));
break;
case CAPTURE_DEVICE_PROBE:
diff --git a/Alc/backends/loopback.c b/Alc/backends/loopback.c
index 3e577f78..e9940086 100644
--- a/Alc/backends/loopback.c
+++ b/Alc/backends/loopback.c
@@ -35,13 +35,12 @@ typedef struct ALCloopback {
static void ALCloopback_Construct(ALCloopback *self, ALCdevice *device);
static DECLARE_FORWARD(ALCloopback, ALCbackend, void, Destruct)
static ALCenum ALCloopback_open(ALCloopback *self, const ALCchar *name);
-static void ALCloopback_close(ALCloopback *self);
static ALCboolean ALCloopback_reset(ALCloopback *self);
static ALCboolean ALCloopback_start(ALCloopback *self);
static void ALCloopback_stop(ALCloopback *self);
static DECLARE_FORWARD2(ALCloopback, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
static DECLARE_FORWARD(ALCloopback, ALCbackend, ALCuint, availableSamples)
-static DECLARE_FORWARD(ALCloopback, ALCbackend, ALint64, getLatency)
+static DECLARE_FORWARD(ALCloopback, ALCbackend, ClockLatency, getClockLatency)
static DECLARE_FORWARD(ALCloopback, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCloopback, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCloopback)
@@ -59,14 +58,10 @@ static ALCenum ALCloopback_open(ALCloopback *self, const ALCchar *name)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
- al_string_copy_cstr(&device->DeviceName, name);
+ alstr_copy_cstr(&device->DeviceName, name);
return ALC_NO_ERROR;
}
-static void ALCloopback_close(ALCloopback* UNUSED(self))
-{
-}
-
static ALCboolean ALCloopback_reset(ALCloopback *self)
{
SetDefaultWFXChannelOrder(STATIC_CAST(ALCbackend, self)->mDevice);
@@ -92,7 +87,7 @@ ALCbackendFactory *ALCloopbackFactory_getFactory(void);
static ALCboolean ALCloopbackFactory_init(ALCloopbackFactory *self);
static DECLARE_FORWARD(ALCloopbackFactory, ALCbackendFactory, void, deinit)
static ALCboolean ALCloopbackFactory_querySupport(ALCloopbackFactory *self, ALCbackend_Type type);
-static void ALCloopbackFactory_probe(ALCloopbackFactory *self, enum DevProbe type);
+static void ALCloopbackFactory_probe(ALCloopbackFactory *self, enum DevProbe type, al_string *outnames);
static ALCbackend* ALCloopbackFactory_createBackend(ALCloopbackFactory *self, ALCdevice *device, ALCbackend_Type type);
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCloopbackFactory);
@@ -115,7 +110,7 @@ static ALCboolean ALCloopbackFactory_querySupport(ALCloopbackFactory* UNUSED(sel
return ALC_FALSE;
}
-static void ALCloopbackFactory_probe(ALCloopbackFactory* UNUSED(self), enum DevProbe UNUSED(type))
+static void ALCloopbackFactory_probe(ALCloopbackFactory* UNUSED(self), enum DevProbe UNUSED(type), al_string* UNUSED(outnames))
{
}
diff --git a/Alc/backends/null.c b/Alc/backends/null.c
index 99729c0a..d1c110e8 100644
--- a/Alc/backends/null.c
+++ b/Alc/backends/null.c
@@ -36,7 +36,7 @@
typedef struct ALCnullBackend {
DERIVE_FROM_TYPE(ALCbackend);
- volatile int killNow;
+ ATOMIC(int) killNow;
althrd_t thread;
} ALCnullBackend;
@@ -45,13 +45,12 @@ static int ALCnullBackend_mixerProc(void *ptr);
static void ALCnullBackend_Construct(ALCnullBackend *self, ALCdevice *device);
static DECLARE_FORWARD(ALCnullBackend, ALCbackend, void, Destruct)
static ALCenum ALCnullBackend_open(ALCnullBackend *self, const ALCchar *name);
-static void ALCnullBackend_close(ALCnullBackend *self);
static ALCboolean ALCnullBackend_reset(ALCnullBackend *self);
static ALCboolean ALCnullBackend_start(ALCnullBackend *self);
static void ALCnullBackend_stop(ALCnullBackend *self);
static DECLARE_FORWARD2(ALCnullBackend, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
static DECLARE_FORWARD(ALCnullBackend, ALCbackend, ALCuint, availableSamples)
-static DECLARE_FORWARD(ALCnullBackend, ALCbackend, ALint64, getLatency)
+static DECLARE_FORWARD(ALCnullBackend, ALCbackend, ClockLatency, getClockLatency)
static DECLARE_FORWARD(ALCnullBackend, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCnullBackend, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCnullBackend)
@@ -66,6 +65,8 @@ static void ALCnullBackend_Construct(ALCnullBackend *self, ALCdevice *device)
{
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(ALCnullBackend, ALCbackend, self);
+
+ ATOMIC_INIT(&self->killNow, AL_TRUE);
}
@@ -87,7 +88,8 @@ static int ALCnullBackend_mixerProc(void *ptr)
ERR("Failed to get starting time\n");
return 1;
}
- while(!self->killNow && device->Connected)
+ while(!ATOMIC_LOAD(&self->killNow, almemory_order_acquire) &&
+ ATOMIC_LOAD(&device->Connected, almemory_order_acquire))
{
if(altimespec_get(&now, AL_TIME_UTC) != AL_TIME_UTC)
{
@@ -109,7 +111,9 @@ static int ALCnullBackend_mixerProc(void *ptr)
al_nssleep(restTime);
else while(avail-done >= device->UpdateSize)
{
+ ALCnullBackend_lock(self);
aluMixData(device, NULL, device->UpdateSize);
+ ALCnullBackend_unlock(self);
done += device->UpdateSize;
}
}
@@ -128,15 +132,11 @@ static ALCenum ALCnullBackend_open(ALCnullBackend *self, const ALCchar *name)
return ALC_INVALID_VALUE;
device = STATIC_CAST(ALCbackend, self)->mDevice;
- al_string_copy_cstr(&device->DeviceName, name);
+ alstr_copy_cstr(&device->DeviceName, name);
return ALC_NO_ERROR;
}
-static void ALCnullBackend_close(ALCnullBackend* UNUSED(self))
-{
-}
-
static ALCboolean ALCnullBackend_reset(ALCnullBackend *self)
{
SetDefaultWFXChannelOrder(STATIC_CAST(ALCbackend, self)->mDevice);
@@ -145,7 +145,7 @@ static ALCboolean ALCnullBackend_reset(ALCnullBackend *self)
static ALCboolean ALCnullBackend_start(ALCnullBackend *self)
{
- self->killNow = 0;
+ ATOMIC_STORE(&self->killNow, AL_FALSE, almemory_order_release);
if(althrd_create(&self->thread, ALCnullBackend_mixerProc, self) != althrd_success)
return ALC_FALSE;
return ALC_TRUE;
@@ -155,10 +155,8 @@ static void ALCnullBackend_stop(ALCnullBackend *self)
{
int res;
- if(self->killNow)
+ if(ATOMIC_EXCHANGE(&self->killNow, AL_TRUE, almemory_order_acq_rel))
return;
-
- self->killNow = 1;
althrd_join(self->thread, &res);
}
@@ -173,7 +171,7 @@ ALCbackendFactory *ALCnullBackendFactory_getFactory(void);
static ALCboolean ALCnullBackendFactory_init(ALCnullBackendFactory *self);
static DECLARE_FORWARD(ALCnullBackendFactory, ALCbackendFactory, void, deinit)
static ALCboolean ALCnullBackendFactory_querySupport(ALCnullBackendFactory *self, ALCbackend_Type type);
-static void ALCnullBackendFactory_probe(ALCnullBackendFactory *self, enum DevProbe type);
+static void ALCnullBackendFactory_probe(ALCnullBackendFactory *self, enum DevProbe type, al_string *outnames);
static ALCbackend* ALCnullBackendFactory_createBackend(ALCnullBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCnullBackendFactory);
@@ -197,14 +195,13 @@ static ALCboolean ALCnullBackendFactory_querySupport(ALCnullBackendFactory* UNUS
return ALC_FALSE;
}
-static void ALCnullBackendFactory_probe(ALCnullBackendFactory* UNUSED(self), enum DevProbe type)
+static void ALCnullBackendFactory_probe(ALCnullBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
{
switch(type)
{
case ALL_DEVICE_PROBE:
- AppendAllDevicesList(nullDevice);
- break;
case CAPTURE_DEVICE_PROBE:
+ alstr_append_range(outnames, nullDevice, nullDevice+sizeof(nullDevice));
break;
}
}
diff --git a/Alc/backends/opensl.c b/Alc/backends/opensl.c
index 7b8fdb25..d8ae001b 100644
--- a/Alc/backends/opensl.c
+++ b/Alc/backends/opensl.c
@@ -22,38 +22,25 @@
#include "config.h"
#include <stdlib.h>
+#include <jni.h>
#include "alMain.h"
#include "alu.h"
+#include "ringbuffer.h"
#include "threads.h"
+#include "compat.h"
+
+#include "backends/base.h"
#include <SLES/OpenSLES.h>
#include <SLES/OpenSLES_Android.h>
+#include <SLES/OpenSLES_AndroidConfiguration.h>
/* Helper macros */
#define VCALL(obj, func) ((*(obj))->func((obj), EXTRACT_VCALL_ARGS
#define VCALL0(obj, func) ((*(obj))->func((obj) EXTRACT_VCALL_ARGS
-typedef struct {
- /* engine interfaces */
- SLObjectItf engineObject;
- SLEngineItf engine;
-
- /* output mix interfaces */
- SLObjectItf outputMix;
-
- /* buffer queue player interfaces */
- SLObjectItf bufferQueueObject;
-
- void *buffer;
- ALuint bufferSize;
- ALuint curBuffer;
-
- ALuint frameSize;
-} osl_data;
-
-
static const ALCchar opensl_device[] = "OpenSL";
@@ -79,10 +66,31 @@ static SLuint32 GetChannelMask(enum DevFmtChannels chans)
SL_SPEAKER_FRONT_CENTER|SL_SPEAKER_LOW_FREQUENCY|
SL_SPEAKER_BACK_LEFT|SL_SPEAKER_BACK_RIGHT|
SL_SPEAKER_SIDE_LEFT|SL_SPEAKER_SIDE_RIGHT;
- case DevFmtBFormat3D: break;
+ case DevFmtAmbi3D:
+ break;
+ }
+ return 0;
+}
+
+#ifdef SL_DATAFORMAT_PCM_EX
+static SLuint32 GetTypeRepresentation(enum DevFmtType type)
+{
+ switch(type)
+ {
+ case DevFmtUByte:
+ case DevFmtUShort:
+ case DevFmtUInt:
+ return SL_PCM_REPRESENTATION_UNSIGNED_INT;
+ case DevFmtByte:
+ case DevFmtShort:
+ case DevFmtInt:
+ return SL_PCM_REPRESENTATION_SIGNED_INT;
+ case DevFmtFloat:
+ return SL_PCM_REPRESENTATION_FLOAT;
}
return 0;
}
+#endif
static const char *res_str(SLresult result)
{
@@ -123,311 +131,940 @@ static const char *res_str(SLresult result)
ERR("%s: %s\n", (s), res_str((x))); \
} while(0)
+
+typedef struct ALCopenslPlayback {
+ DERIVE_FROM_TYPE(ALCbackend);
+
+ /* engine interfaces */
+ SLObjectItf mEngineObj;
+ SLEngineItf mEngine;
+
+ /* output mix interfaces */
+ SLObjectItf mOutputMix;
+
+ /* buffer queue player interfaces */
+ SLObjectItf mBufferQueueObj;
+
+ ll_ringbuffer_t *mRing;
+ alsem_t mSem;
+
+ ALsizei mFrameSize;
+
+ ATOMIC(ALenum) mKillNow;
+ althrd_t mThread;
+} ALCopenslPlayback;
+
+static void ALCopenslPlayback_process(SLAndroidSimpleBufferQueueItf bq, void *context);
+static int ALCopenslPlayback_mixerProc(void *arg);
+
+static void ALCopenslPlayback_Construct(ALCopenslPlayback *self, ALCdevice *device);
+static void ALCopenslPlayback_Destruct(ALCopenslPlayback *self);
+static ALCenum ALCopenslPlayback_open(ALCopenslPlayback *self, const ALCchar *name);
+static ALCboolean ALCopenslPlayback_reset(ALCopenslPlayback *self);
+static ALCboolean ALCopenslPlayback_start(ALCopenslPlayback *self);
+static void ALCopenslPlayback_stop(ALCopenslPlayback *self);
+static DECLARE_FORWARD2(ALCopenslPlayback, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
+static DECLARE_FORWARD(ALCopenslPlayback, ALCbackend, ALCuint, availableSamples)
+static ClockLatency ALCopenslPlayback_getClockLatency(ALCopenslPlayback *self);
+static DECLARE_FORWARD(ALCopenslPlayback, ALCbackend, void, lock)
+static DECLARE_FORWARD(ALCopenslPlayback, ALCbackend, void, unlock)
+DECLARE_DEFAULT_ALLOCATORS(ALCopenslPlayback)
+
+DEFINE_ALCBACKEND_VTABLE(ALCopenslPlayback);
+
+
+static void ALCopenslPlayback_Construct(ALCopenslPlayback *self, ALCdevice *device)
+{
+ ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
+ SET_VTABLE2(ALCopenslPlayback, ALCbackend, self);
+
+ self->mEngineObj = NULL;
+ self->mEngine = NULL;
+ self->mOutputMix = NULL;
+ self->mBufferQueueObj = NULL;
+
+ self->mRing = NULL;
+ alsem_init(&self->mSem, 0);
+
+ self->mFrameSize = 0;
+
+ ATOMIC_INIT(&self->mKillNow, AL_FALSE);
+}
+
+static void ALCopenslPlayback_Destruct(ALCopenslPlayback* self)
+{
+ if(self->mBufferQueueObj != NULL)
+ VCALL0(self->mBufferQueueObj,Destroy)();
+ self->mBufferQueueObj = NULL;
+
+ if(self->mOutputMix)
+ VCALL0(self->mOutputMix,Destroy)();
+ self->mOutputMix = NULL;
+
+ if(self->mEngineObj)
+ VCALL0(self->mEngineObj,Destroy)();
+ self->mEngineObj = NULL;
+ self->mEngine = NULL;
+
+ ll_ringbuffer_free(self->mRing);
+ self->mRing = NULL;
+
+ alsem_destroy(&self->mSem);
+
+ ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
+}
+
+
/* this callback handler is called every time a buffer finishes playing */
-static void opensl_callback(SLAndroidSimpleBufferQueueItf bq, void *context)
+static void ALCopenslPlayback_process(SLAndroidSimpleBufferQueueItf UNUSED(bq), void *context)
+{
+ ALCopenslPlayback *self = context;
+
+ /* A note on the ringbuffer usage: The buffer queue seems to hold on to the
+ * pointer passed to the Enqueue method, rather than copying the audio.
+ * Consequently, the ringbuffer contains the audio that is currently queued
+ * and waiting to play. This process() callback is called when a buffer is
+ * finished, so we simply move the read pointer up to indicate the space is
+ * available for writing again, and wake up the mixer thread to mix and
+ * queue more audio.
+ */
+ ll_ringbuffer_read_advance(self->mRing, 1);
+
+ alsem_post(&self->mSem);
+}
+
+
+static int ALCopenslPlayback_mixerProc(void *arg)
{
- ALCdevice *Device = context;
- osl_data *data = Device->ExtraData;
- ALvoid *buf;
+ ALCopenslPlayback *self = arg;
+ ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
+ SLAndroidSimpleBufferQueueItf bufferQueue;
+ ll_ringbuffer_data_t data[2];
+ SLPlayItf player;
SLresult result;
- buf = (ALbyte*)data->buffer + data->curBuffer*data->bufferSize;
- aluMixData(Device, buf, data->bufferSize/data->frameSize);
+ SetRTPriority();
+ althrd_setname(althrd_current(), MIXER_THREAD_NAME);
- result = VCALL(bq,Enqueue)(buf, data->bufferSize);
- PRINTERR(result, "bq->Enqueue");
+ result = VCALL(self->mBufferQueueObj,GetInterface)(SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
+ &bufferQueue);
+ PRINTERR(result, "bufferQueue->GetInterface SL_IID_ANDROIDSIMPLEBUFFERQUEUE");
+ if(SL_RESULT_SUCCESS == result)
+ {
+ result = VCALL(self->mBufferQueueObj,GetInterface)(SL_IID_PLAY, &player);
+ PRINTERR(result, "bufferQueue->GetInterface SL_IID_PLAY");
+ }
- data->curBuffer = (data->curBuffer+1) % Device->NumUpdates;
+ ALCopenslPlayback_lock(self);
+ if(SL_RESULT_SUCCESS != result)
+ aluHandleDisconnect(device, "Failed to get playback buffer: 0x%08x", result);
+
+ while(SL_RESULT_SUCCESS == result &&
+ !ATOMIC_LOAD(&self->mKillNow, almemory_order_acquire) &&
+ ATOMIC_LOAD(&device->Connected, almemory_order_acquire))
+ {
+ size_t todo;
+
+ if(ll_ringbuffer_write_space(self->mRing) == 0)
+ {
+ SLuint32 state = 0;
+
+ result = VCALL(player,GetPlayState)(&state);
+ PRINTERR(result, "player->GetPlayState");
+ if(SL_RESULT_SUCCESS == result && state != SL_PLAYSTATE_PLAYING)
+ {
+ result = VCALL(player,SetPlayState)(SL_PLAYSTATE_PLAYING);
+ PRINTERR(result, "player->SetPlayState");
+ }
+ if(SL_RESULT_SUCCESS != result)
+ {
+ aluHandleDisconnect(device, "Failed to start platback: 0x%08x", result);
+ break;
+ }
+
+ if(ll_ringbuffer_write_space(self->mRing) == 0)
+ {
+ ALCopenslPlayback_unlock(self);
+ alsem_wait(&self->mSem);
+ ALCopenslPlayback_lock(self);
+ continue;
+ }
+ }
+
+ ll_ringbuffer_get_write_vector(self->mRing, data);
+
+ aluMixData(device, data[0].buf, data[0].len*device->UpdateSize);
+ if(data[1].len > 0)
+ aluMixData(device, data[1].buf, data[1].len*device->UpdateSize);
+
+ todo = data[0].len+data[1].len;
+ ll_ringbuffer_write_advance(self->mRing, todo);
+
+ for(size_t i = 0;i < todo;i++)
+ {
+ if(!data[0].len)
+ {
+ data[0] = data[1];
+ data[1].buf = NULL;
+ data[1].len = 0;
+ }
+
+ result = VCALL(bufferQueue,Enqueue)(data[0].buf, device->UpdateSize*self->mFrameSize);
+ PRINTERR(result, "bufferQueue->Enqueue");
+ if(SL_RESULT_SUCCESS != result)
+ {
+ aluHandleDisconnect(device, "Failed to queue audio: 0x%08x", result);
+ break;
+ }
+
+ data[0].len--;
+ data[0].buf += device->UpdateSize*self->mFrameSize;
+ }
+ }
+ ALCopenslPlayback_unlock(self);
+
+ return 0;
}
-static ALCenum opensl_open_playback(ALCdevice *Device, const ALCchar *deviceName)
+static ALCenum ALCopenslPlayback_open(ALCopenslPlayback *self, const ALCchar *name)
{
- osl_data *data = NULL;
+ ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
SLresult result;
- if(!deviceName)
- deviceName = opensl_device;
- else if(strcmp(deviceName, opensl_device) != 0)
+ if(!name)
+ name = opensl_device;
+ else if(strcmp(name, opensl_device) != 0)
return ALC_INVALID_VALUE;
- data = calloc(1, sizeof(*data));
- if(!data)
- return ALC_OUT_OF_MEMORY;
-
// create engine
- result = slCreateEngine(&data->engineObject, 0, NULL, 0, NULL, NULL);
+ result = slCreateEngine(&self->mEngineObj, 0, NULL, 0, NULL, NULL);
PRINTERR(result, "slCreateEngine");
if(SL_RESULT_SUCCESS == result)
{
- result = VCALL(data->engineObject,Realize)(SL_BOOLEAN_FALSE);
+ result = VCALL(self->mEngineObj,Realize)(SL_BOOLEAN_FALSE);
PRINTERR(result, "engine->Realize");
}
if(SL_RESULT_SUCCESS == result)
{
- result = VCALL(data->engineObject,GetInterface)(SL_IID_ENGINE, &data->engine);
+ result = VCALL(self->mEngineObj,GetInterface)(SL_IID_ENGINE, &self->mEngine);
PRINTERR(result, "engine->GetInterface");
}
if(SL_RESULT_SUCCESS == result)
{
- result = VCALL(data->engine,CreateOutputMix)(&data->outputMix, 0, NULL, NULL);
+ result = VCALL(self->mEngine,CreateOutputMix)(&self->mOutputMix, 0, NULL, NULL);
PRINTERR(result, "engine->CreateOutputMix");
}
if(SL_RESULT_SUCCESS == result)
{
- result = VCALL(data->outputMix,Realize)(SL_BOOLEAN_FALSE);
+ result = VCALL(self->mOutputMix,Realize)(SL_BOOLEAN_FALSE);
PRINTERR(result, "outputMix->Realize");
}
if(SL_RESULT_SUCCESS != result)
{
- if(data->outputMix != NULL)
- VCALL0(data->outputMix,Destroy)();
- data->outputMix = NULL;
+ if(self->mOutputMix != NULL)
+ VCALL0(self->mOutputMix,Destroy)();
+ self->mOutputMix = NULL;
- if(data->engineObject != NULL)
- VCALL0(data->engineObject,Destroy)();
- data->engineObject = NULL;
- data->engine = NULL;
+ if(self->mEngineObj != NULL)
+ VCALL0(self->mEngineObj,Destroy)();
+ self->mEngineObj = NULL;
+ self->mEngine = NULL;
- free(data);
return ALC_INVALID_VALUE;
}
- al_string_copy_cstr(&Device->DeviceName, deviceName);
- Device->ExtraData = data;
+ alstr_copy_cstr(&device->DeviceName, name);
return ALC_NO_ERROR;
}
-
-static void opensl_close_playback(ALCdevice *Device)
-{
- osl_data *data = Device->ExtraData;
-
- if(data->bufferQueueObject != NULL)
- VCALL0(data->bufferQueueObject,Destroy)();
- data->bufferQueueObject = NULL;
-
- VCALL0(data->outputMix,Destroy)();
- data->outputMix = NULL;
-
- VCALL0(data->engineObject,Destroy)();
- data->engineObject = NULL;
- data->engine = NULL;
-
- free(data);
- Device->ExtraData = NULL;
-}
-
-static ALCboolean opensl_reset_playback(ALCdevice *Device)
+static ALCboolean ALCopenslPlayback_reset(ALCopenslPlayback *self)
{
- osl_data *data = Device->ExtraData;
+ ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
SLDataLocator_AndroidSimpleBufferQueue loc_bufq;
SLDataLocator_OutputMix loc_outmix;
- SLDataFormat_PCM format_pcm;
SLDataSource audioSrc;
SLDataSink audioSnk;
- SLInterfaceID id;
- SLboolean req;
+ ALuint sampleRate;
+ SLInterfaceID ids[2];
+ SLboolean reqs[2];
SLresult result;
+ if(self->mBufferQueueObj != NULL)
+ VCALL0(self->mBufferQueueObj,Destroy)();
+ self->mBufferQueueObj = NULL;
+
+ ll_ringbuffer_free(self->mRing);
+ self->mRing = NULL;
- Device->UpdateSize = (ALuint64)Device->UpdateSize * 44100 / Device->Frequency;
- Device->UpdateSize = Device->UpdateSize * Device->NumUpdates / 2;
- Device->NumUpdates = 2;
+ sampleRate = device->Frequency;
+#if 0
+ if(!(device->Flags&DEVICE_FREQUENCY_REQUEST))
+ {
+ /* FIXME: Disabled until I figure out how to get the Context needed for
+ * the getSystemService call.
+ */
+ JNIEnv *env = Android_GetJNIEnv();
+ jobject jctx = Android_GetContext();
+
+ /* Get necessary stuff for using java.lang.Integer,
+ * android.content.Context, and android.media.AudioManager.
+ */
+ jclass int_cls = JCALL(env,FindClass)("java/lang/Integer");
+ jmethodID int_parseint = JCALL(env,GetStaticMethodID)(int_cls,
+ "parseInt", "(Ljava/lang/String;)I"
+ );
+ TRACE("Integer: %p, parseInt: %p\n", int_cls, int_parseint);
+
+ jclass ctx_cls = JCALL(env,FindClass)("android/content/Context");
+ jfieldID ctx_audsvc = JCALL(env,GetStaticFieldID)(ctx_cls,
+ "AUDIO_SERVICE", "Ljava/lang/String;"
+ );
+ jmethodID ctx_getSysSvc = JCALL(env,GetMethodID)(ctx_cls,
+ "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;"
+ );
+ TRACE("Context: %p, AUDIO_SERVICE: %p, getSystemService: %p\n",
+ ctx_cls, ctx_audsvc, ctx_getSysSvc);
+
+ jclass audmgr_cls = JCALL(env,FindClass)("android/media/AudioManager");
+ jfieldID audmgr_prop_out_srate = JCALL(env,GetStaticFieldID)(audmgr_cls,
+ "PROPERTY_OUTPUT_SAMPLE_RATE", "Ljava/lang/String;"
+ );
+ jmethodID audmgr_getproperty = JCALL(env,GetMethodID)(audmgr_cls,
+ "getProperty", "(Ljava/lang/String;)Ljava/lang/String;"
+ );
+ TRACE("AudioManager: %p, PROPERTY_OUTPUT_SAMPLE_RATE: %p, getProperty: %p\n",
+ audmgr_cls, audmgr_prop_out_srate, audmgr_getproperty);
+
+ const char *strchars;
+ jstring strobj;
+
+ /* Now make the calls. */
+ //AudioManager audMgr = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
+ strobj = JCALL(env,GetStaticObjectField)(ctx_cls, ctx_audsvc);
+ jobject audMgr = JCALL(env,CallObjectMethod)(jctx, ctx_getSysSvc, strobj);
+ strchars = JCALL(env,GetStringUTFChars)(strobj, NULL);
+ TRACE("Context.getSystemService(%s) = %p\n", strchars, audMgr);
+ JCALL(env,ReleaseStringUTFChars)(strobj, strchars);
+
+ //String srateStr = audMgr.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE);
+ strobj = JCALL(env,GetStaticObjectField)(audmgr_cls, audmgr_prop_out_srate);
+ jstring srateStr = JCALL(env,CallObjectMethod)(audMgr, audmgr_getproperty, strobj);
+ strchars = JCALL(env,GetStringUTFChars)(strobj, NULL);
+ TRACE("audMgr.getProperty(%s) = %p\n", strchars, srateStr);
+ JCALL(env,ReleaseStringUTFChars)(strobj, strchars);
+
+ //int sampleRate = Integer.parseInt(srateStr);
+ sampleRate = JCALL(env,CallStaticIntMethod)(int_cls, int_parseint, srateStr);
+
+ strchars = JCALL(env,GetStringUTFChars)(srateStr, NULL);
+ TRACE("Got system sample rate %uhz (%s)\n", sampleRate, strchars);
+ JCALL(env,ReleaseStringUTFChars)(srateStr, strchars);
+
+ if(!sampleRate) sampleRate = device->Frequency;
+ else sampleRate = maxu(sampleRate, MIN_OUTPUT_RATE);
+ }
+#endif
- Device->Frequency = 44100;
- Device->FmtChans = DevFmtStereo;
- Device->FmtType = DevFmtShort;
+ if(sampleRate != device->Frequency)
+ {
+ device->NumUpdates = (device->NumUpdates*sampleRate + (device->Frequency>>1)) /
+ device->Frequency;
+ device->NumUpdates = maxu(device->NumUpdates, 2);
+ device->Frequency = sampleRate;
+ }
- SetDefaultWFXChannelOrder(Device);
+ device->FmtChans = DevFmtStereo;
+ device->FmtType = DevFmtShort;
+ SetDefaultWFXChannelOrder(device);
+ self->mFrameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
- id = SL_IID_ANDROIDSIMPLEBUFFERQUEUE;
- req = SL_BOOLEAN_TRUE;
loc_bufq.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
- loc_bufq.numBuffers = Device->NumUpdates;
-
+ loc_bufq.numBuffers = device->NumUpdates;
+
+#ifdef SL_DATAFORMAT_PCM_EX
+ SLDataFormat_PCM_EX format_pcm;
+ format_pcm.formatType = SL_DATAFORMAT_PCM_EX;
+ format_pcm.numChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
+ format_pcm.sampleRate = device->Frequency * 1000;
+ format_pcm.bitsPerSample = BytesFromDevFmt(device->FmtType) * 8;
+ format_pcm.containerSize = format_pcm.bitsPerSample;
+ format_pcm.channelMask = GetChannelMask(device->FmtChans);
+ format_pcm.endianness = IS_LITTLE_ENDIAN ? SL_BYTEORDER_LITTLEENDIAN :
+ SL_BYTEORDER_BIGENDIAN;
+ format_pcm.representation = GetTypeRepresentation(device->FmtType);
+#else
+ SLDataFormat_PCM format_pcm;
format_pcm.formatType = SL_DATAFORMAT_PCM;
- format_pcm.numChannels = ChannelsFromDevFmt(Device->FmtChans);
- format_pcm.samplesPerSec = Device->Frequency * 1000;
- format_pcm.bitsPerSample = BytesFromDevFmt(Device->FmtType) * 8;
+ format_pcm.numChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
+ format_pcm.samplesPerSec = device->Frequency * 1000;
+ format_pcm.bitsPerSample = BytesFromDevFmt(device->FmtType) * 8;
format_pcm.containerSize = format_pcm.bitsPerSample;
- format_pcm.channelMask = GetChannelMask(Device->FmtChans);
+ format_pcm.channelMask = GetChannelMask(device->FmtChans);
format_pcm.endianness = IS_LITTLE_ENDIAN ? SL_BYTEORDER_LITTLEENDIAN :
SL_BYTEORDER_BIGENDIAN;
+#endif
audioSrc.pLocator = &loc_bufq;
audioSrc.pFormat = &format_pcm;
loc_outmix.locatorType = SL_DATALOCATOR_OUTPUTMIX;
- loc_outmix.outputMix = data->outputMix;
+ loc_outmix.outputMix = self->mOutputMix;
audioSnk.pLocator = &loc_outmix;
audioSnk.pFormat = NULL;
- if(data->bufferQueueObject != NULL)
- VCALL0(data->bufferQueueObject,Destroy)();
- data->bufferQueueObject = NULL;
+ ids[0] = SL_IID_ANDROIDSIMPLEBUFFERQUEUE;
+ reqs[0] = SL_BOOLEAN_TRUE;
+ ids[1] = SL_IID_ANDROIDCONFIGURATION;
+ reqs[1] = SL_BOOLEAN_FALSE;
- result = VCALL(data->engine,CreateAudioPlayer)(&data->bufferQueueObject, &audioSrc, &audioSnk, 1, &id, &req);
+ result = VCALL(self->mEngine,CreateAudioPlayer)(&self->mBufferQueueObj,
+ &audioSrc, &audioSnk, COUNTOF(ids), ids, reqs
+ );
PRINTERR(result, "engine->CreateAudioPlayer");
if(SL_RESULT_SUCCESS == result)
{
- result = VCALL(data->bufferQueueObject,Realize)(SL_BOOLEAN_FALSE);
+ /* Set the stream type to "media" (games, music, etc), if possible. */
+ SLAndroidConfigurationItf config;
+ result = VCALL(self->mBufferQueueObj,GetInterface)(SL_IID_ANDROIDCONFIGURATION, &config);
+ PRINTERR(result, "bufferQueue->GetInterface SL_IID_ANDROIDCONFIGURATION");
+ if(SL_RESULT_SUCCESS == result)
+ {
+ SLint32 streamType = SL_ANDROID_STREAM_MEDIA;
+ result = VCALL(config,SetConfiguration)(SL_ANDROID_KEY_STREAM_TYPE,
+ &streamType, sizeof(streamType)
+ );
+ PRINTERR(result, "config->SetConfiguration");
+ }
+
+ /* Clear any error since this was optional. */
+ result = SL_RESULT_SUCCESS;
+ }
+ if(SL_RESULT_SUCCESS == result)
+ {
+ result = VCALL(self->mBufferQueueObj,Realize)(SL_BOOLEAN_FALSE);
PRINTERR(result, "bufferQueue->Realize");
}
+ if(SL_RESULT_SUCCESS == result)
+ {
+ self->mRing = ll_ringbuffer_create(device->NumUpdates,
+ self->mFrameSize*device->UpdateSize, true
+ );
+ if(!self->mRing)
+ {
+ ERR("Out of memory allocating ring buffer %ux%u %u\n", device->UpdateSize,
+ device->NumUpdates, self->mFrameSize);
+ result = SL_RESULT_MEMORY_FAILURE;
+ }
+ }
if(SL_RESULT_SUCCESS != result)
{
- if(data->bufferQueueObject != NULL)
- VCALL0(data->bufferQueueObject,Destroy)();
- data->bufferQueueObject = NULL;
+ if(self->mBufferQueueObj != NULL)
+ VCALL0(self->mBufferQueueObj,Destroy)();
+ self->mBufferQueueObj = NULL;
+
+ return ALC_FALSE;
+ }
+
+ return ALC_TRUE;
+}
+
+static ALCboolean ALCopenslPlayback_start(ALCopenslPlayback *self)
+{
+ SLAndroidSimpleBufferQueueItf bufferQueue;
+ SLresult result;
+
+ ll_ringbuffer_reset(self->mRing);
+
+ result = VCALL(self->mBufferQueueObj,GetInterface)(SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
+ &bufferQueue);
+ PRINTERR(result, "bufferQueue->GetInterface");
+ if(SL_RESULT_SUCCESS != result)
+ return ALC_FALSE;
+
+ result = VCALL(bufferQueue,RegisterCallback)(ALCopenslPlayback_process, self);
+ PRINTERR(result, "bufferQueue->RegisterCallback");
+ if(SL_RESULT_SUCCESS != result)
+ return ALC_FALSE;
+ ATOMIC_STORE_SEQ(&self->mKillNow, AL_FALSE);
+ if(althrd_create(&self->mThread, ALCopenslPlayback_mixerProc, self) != althrd_success)
+ {
+ ERR("Failed to start mixer thread\n");
return ALC_FALSE;
}
return ALC_TRUE;
}
-static ALCboolean opensl_start_playback(ALCdevice *Device)
+
+static void ALCopenslPlayback_stop(ALCopenslPlayback *self)
{
- osl_data *data = Device->ExtraData;
SLAndroidSimpleBufferQueueItf bufferQueue;
SLPlayItf player;
SLresult result;
- ALuint i;
+ int res;
+
+ if(ATOMIC_EXCHANGE_SEQ(&self->mKillNow, AL_TRUE))
+ return;
+
+ alsem_post(&self->mSem);
+ althrd_join(self->mThread, &res);
- result = VCALL(data->bufferQueueObject,GetInterface)(SL_IID_BUFFERQUEUE, &bufferQueue);
+ result = VCALL(self->mBufferQueueObj,GetInterface)(SL_IID_PLAY, &player);
PRINTERR(result, "bufferQueue->GetInterface");
if(SL_RESULT_SUCCESS == result)
{
- result = VCALL(bufferQueue,RegisterCallback)(opensl_callback, Device);
+ result = VCALL(player,SetPlayState)(SL_PLAYSTATE_STOPPED);
+ PRINTERR(result, "player->SetPlayState");
+ }
+
+ result = VCALL(self->mBufferQueueObj,GetInterface)(SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
+ &bufferQueue);
+ PRINTERR(result, "bufferQueue->GetInterface");
+ if(SL_RESULT_SUCCESS == result)
+ {
+ result = VCALL0(bufferQueue,Clear)();
+ PRINTERR(result, "bufferQueue->Clear");
+ }
+ if(SL_RESULT_SUCCESS == result)
+ {
+ result = VCALL(bufferQueue,RegisterCallback)(NULL, NULL);
PRINTERR(result, "bufferQueue->RegisterCallback");
}
if(SL_RESULT_SUCCESS == result)
{
- data->frameSize = FrameSizeFromDevFmt(Device->FmtChans, Device->FmtType);
- data->bufferSize = Device->UpdateSize * data->frameSize;
- data->buffer = calloc(Device->NumUpdates, data->bufferSize);
- if(!data->buffer)
- {
- result = SL_RESULT_MEMORY_FAILURE;
- PRINTERR(result, "calloc");
- }
+ SLAndroidSimpleBufferQueueState state;
+ do {
+ althrd_yield();
+ result = VCALL(bufferQueue,GetState)(&state);
+ } while(SL_RESULT_SUCCESS == result && state.count > 0);
+ PRINTERR(result, "bufferQueue->GetState");
+ }
+}
+
+static ClockLatency ALCopenslPlayback_getClockLatency(ALCopenslPlayback *self)
+{
+ ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+ ClockLatency ret;
+
+ ALCopenslPlayback_lock(self);
+ ret.ClockTime = GetDeviceClockTime(device);
+ ret.Latency = ll_ringbuffer_read_space(self->mRing)*device->UpdateSize *
+ DEVICE_CLOCK_RES / device->Frequency;
+ ALCopenslPlayback_unlock(self);
+
+ return ret;
+}
+
+
+typedef struct ALCopenslCapture {
+ DERIVE_FROM_TYPE(ALCbackend);
+
+ /* engine interfaces */
+ SLObjectItf mEngineObj;
+ SLEngineItf mEngine;
+
+ /* recording interfaces */
+ SLObjectItf mRecordObj;
+
+ ll_ringbuffer_t *mRing;
+ ALCuint mSplOffset;
+
+ ALsizei mFrameSize;
+} ALCopenslCapture;
+
+static void ALCopenslCapture_process(SLAndroidSimpleBufferQueueItf bq, void *context);
+
+static void ALCopenslCapture_Construct(ALCopenslCapture *self, ALCdevice *device);
+static void ALCopenslCapture_Destruct(ALCopenslCapture *self);
+static ALCenum ALCopenslCapture_open(ALCopenslCapture *self, const ALCchar *name);
+static DECLARE_FORWARD(ALCopenslCapture, ALCbackend, ALCboolean, reset)
+static ALCboolean ALCopenslCapture_start(ALCopenslCapture *self);
+static void ALCopenslCapture_stop(ALCopenslCapture *self);
+static ALCenum ALCopenslCapture_captureSamples(ALCopenslCapture *self, ALCvoid *buffer, ALCuint samples);
+static ALCuint ALCopenslCapture_availableSamples(ALCopenslCapture *self);
+static DECLARE_FORWARD(ALCopenslCapture, ALCbackend, ClockLatency, getClockLatency)
+static DECLARE_FORWARD(ALCopenslCapture, ALCbackend, void, lock)
+static DECLARE_FORWARD(ALCopenslCapture, ALCbackend, void, unlock)
+DECLARE_DEFAULT_ALLOCATORS(ALCopenslCapture)
+DEFINE_ALCBACKEND_VTABLE(ALCopenslCapture);
+
+
+static void ALCopenslCapture_process(SLAndroidSimpleBufferQueueItf UNUSED(bq), void *context)
+{
+ ALCopenslCapture *self = context;
+ /* A new chunk has been written into the ring buffer, advance it. */
+ ll_ringbuffer_write_advance(self->mRing, 1);
+}
+
+
+static void ALCopenslCapture_Construct(ALCopenslCapture *self, ALCdevice *device)
+{
+ ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
+ SET_VTABLE2(ALCopenslCapture, ALCbackend, self);
+
+ self->mEngineObj = NULL;
+ self->mEngine = NULL;
+
+ self->mRecordObj = NULL;
+
+ self->mRing = NULL;
+ self->mSplOffset = 0;
+
+ self->mFrameSize = 0;
+}
+
+static void ALCopenslCapture_Destruct(ALCopenslCapture *self)
+{
+ if(self->mRecordObj != NULL)
+ VCALL0(self->mRecordObj,Destroy)();
+ self->mRecordObj = NULL;
+
+ if(self->mEngineObj != NULL)
+ VCALL0(self->mEngineObj,Destroy)();
+ self->mEngineObj = NULL;
+ self->mEngine = NULL;
+
+ ll_ringbuffer_free(self->mRing);
+ self->mRing = NULL;
+
+ ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
+}
+
+static ALCenum ALCopenslCapture_open(ALCopenslCapture *self, const ALCchar *name)
+{
+ ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+ SLDataLocator_AndroidSimpleBufferQueue loc_bq;
+ SLAndroidSimpleBufferQueueItf bufferQueue;
+ SLDataLocator_IODevice loc_dev;
+ SLDataSource audioSrc;
+ SLDataSink audioSnk;
+ SLresult result;
+
+ if(!name)
+ name = opensl_device;
+ else if(strcmp(name, opensl_device) != 0)
+ return ALC_INVALID_VALUE;
+
+ result = slCreateEngine(&self->mEngineObj, 0, NULL, 0, NULL, NULL);
+ PRINTERR(result, "slCreateEngine");
+ if(SL_RESULT_SUCCESS == result)
+ {
+ result = VCALL(self->mEngineObj,Realize)(SL_BOOLEAN_FALSE);
+ PRINTERR(result, "engine->Realize");
}
- /* enqueue the first buffer to kick off the callbacks */
- for(i = 0;i < Device->NumUpdates;i++)
+ if(SL_RESULT_SUCCESS == result)
+ {
+ result = VCALL(self->mEngineObj,GetInterface)(SL_IID_ENGINE, &self->mEngine);
+ PRINTERR(result, "engine->GetInterface");
+ }
+ if(SL_RESULT_SUCCESS == result)
{
+ /* Ensure the total length is at least 100ms */
+ ALsizei length = maxi(device->NumUpdates * device->UpdateSize,
+ device->Frequency / 10);
+ /* Ensure the per-chunk length is at least 10ms, and no more than 50ms. */
+ ALsizei update_len = clampi(device->NumUpdates*device->UpdateSize / 3,
+ device->Frequency / 100,
+ device->Frequency / 100 * 5);
+
+ device->UpdateSize = update_len;
+ device->NumUpdates = (length+update_len-1) / update_len;
+
+ self->mFrameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
+ }
+ loc_dev.locatorType = SL_DATALOCATOR_IODEVICE;
+ loc_dev.deviceType = SL_IODEVICE_AUDIOINPUT;
+ loc_dev.deviceID = SL_DEFAULTDEVICEID_AUDIOINPUT;
+ loc_dev.device = NULL;
+
+ audioSrc.pLocator = &loc_dev;
+ audioSrc.pFormat = NULL;
+
+ loc_bq.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
+ loc_bq.numBuffers = device->NumUpdates;
+
+#ifdef SL_DATAFORMAT_PCM_EX
+ SLDataFormat_PCM_EX format_pcm;
+ format_pcm.formatType = SL_DATAFORMAT_PCM_EX;
+ format_pcm.numChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
+ format_pcm.sampleRate = device->Frequency * 1000;
+ format_pcm.bitsPerSample = BytesFromDevFmt(device->FmtType) * 8;
+ format_pcm.containerSize = format_pcm.bitsPerSample;
+ format_pcm.channelMask = GetChannelMask(device->FmtChans);
+ format_pcm.endianness = IS_LITTLE_ENDIAN ? SL_BYTEORDER_LITTLEENDIAN :
+ SL_BYTEORDER_BIGENDIAN;
+ format_pcm.representation = GetTypeRepresentation(device->FmtType);
+#else
+ SLDataFormat_PCM format_pcm;
+ format_pcm.formatType = SL_DATAFORMAT_PCM;
+ format_pcm.numChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
+ format_pcm.samplesPerSec = device->Frequency * 1000;
+ format_pcm.bitsPerSample = BytesFromDevFmt(device->FmtType) * 8;
+ format_pcm.containerSize = format_pcm.bitsPerSample;
+ format_pcm.channelMask = GetChannelMask(device->FmtChans);
+ format_pcm.endianness = IS_LITTLE_ENDIAN ? SL_BYTEORDER_LITTLEENDIAN :
+ SL_BYTEORDER_BIGENDIAN;
+#endif
+
+ audioSnk.pLocator = &loc_bq;
+ audioSnk.pFormat = &format_pcm;
+
+ if(SL_RESULT_SUCCESS == result)
+ {
+ const SLInterfaceID ids[2] = { SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_ANDROIDCONFIGURATION };
+ const SLboolean reqs[2] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_FALSE };
+
+ result = VCALL(self->mEngine,CreateAudioRecorder)(&self->mRecordObj,
+ &audioSrc, &audioSnk, COUNTOF(ids), ids, reqs
+ );
+ PRINTERR(result, "engine->CreateAudioRecorder");
+ }
+ if(SL_RESULT_SUCCESS == result)
+ {
+ /* Set the record preset to "generic", if possible. */
+ SLAndroidConfigurationItf config;
+ result = VCALL(self->mRecordObj,GetInterface)(SL_IID_ANDROIDCONFIGURATION, &config);
+ PRINTERR(result, "recordObj->GetInterface SL_IID_ANDROIDCONFIGURATION");
if(SL_RESULT_SUCCESS == result)
{
- ALvoid *buf = (ALbyte*)data->buffer + i*data->bufferSize;
- result = VCALL(bufferQueue,Enqueue)(buf, data->bufferSize);
- PRINTERR(result, "bufferQueue->Enqueue");
+ SLuint32 preset = SL_ANDROID_RECORDING_PRESET_GENERIC;
+ result = VCALL(config,SetConfiguration)(SL_ANDROID_KEY_RECORDING_PRESET,
+ &preset, sizeof(preset)
+ );
+ PRINTERR(result, "config->SetConfiguration");
}
+
+ /* Clear any error since this was optional. */
+ result = SL_RESULT_SUCCESS;
}
- data->curBuffer = 0;
if(SL_RESULT_SUCCESS == result)
{
- result = VCALL(data->bufferQueueObject,GetInterface)(SL_IID_PLAY, &player);
- PRINTERR(result, "bufferQueue->GetInterface");
+ result = VCALL(self->mRecordObj,Realize)(SL_BOOLEAN_FALSE);
+ PRINTERR(result, "recordObj->Realize");
}
+
if(SL_RESULT_SUCCESS == result)
{
- result = VCALL(player,SetPlayState)(SL_PLAYSTATE_PLAYING);
- PRINTERR(result, "player->SetPlayState");
+ self->mRing = ll_ringbuffer_create(device->NumUpdates,
+ device->UpdateSize*self->mFrameSize, false
+ );
+
+ result = VCALL(self->mRecordObj,GetInterface)(SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
+ &bufferQueue);
+ PRINTERR(result, "recordObj->GetInterface");
+ }
+ if(SL_RESULT_SUCCESS == result)
+ {
+ result = VCALL(bufferQueue,RegisterCallback)(ALCopenslCapture_process, self);
+ PRINTERR(result, "bufferQueue->RegisterCallback");
+ }
+ if(SL_RESULT_SUCCESS == result)
+ {
+ ALsizei chunk_size = device->UpdateSize * self->mFrameSize;
+ ll_ringbuffer_data_t data[2];
+ size_t i;
+
+ ll_ringbuffer_get_write_vector(self->mRing, data);
+ for(i = 0;i < data[0].len && SL_RESULT_SUCCESS == result;i++)
+ {
+ result = VCALL(bufferQueue,Enqueue)(data[0].buf + chunk_size*i, chunk_size);
+ PRINTERR(result, "bufferQueue->Enqueue");
+ }
+ for(i = 0;i < data[1].len && SL_RESULT_SUCCESS == result;i++)
+ {
+ result = VCALL(bufferQueue,Enqueue)(data[1].buf + chunk_size*i, chunk_size);
+ PRINTERR(result, "bufferQueue->Enqueue");
+ }
}
if(SL_RESULT_SUCCESS != result)
{
- if(data->bufferQueueObject != NULL)
- VCALL0(data->bufferQueueObject,Destroy)();
- data->bufferQueueObject = NULL;
+ if(self->mRecordObj != NULL)
+ VCALL0(self->mRecordObj,Destroy)();
+ self->mRecordObj = NULL;
+
+ if(self->mEngineObj != NULL)
+ VCALL0(self->mEngineObj,Destroy)();
+ self->mEngineObj = NULL;
+ self->mEngine = NULL;
+
+ return ALC_INVALID_VALUE;
+ }
+
+ alstr_copy_cstr(&device->DeviceName, name);
+
+ return ALC_NO_ERROR;
+}
- free(data->buffer);
- data->buffer = NULL;
- data->bufferSize = 0;
+static ALCboolean ALCopenslCapture_start(ALCopenslCapture *self)
+{
+ SLRecordItf record;
+ SLresult result;
+ result = VCALL(self->mRecordObj,GetInterface)(SL_IID_RECORD, &record);
+ PRINTERR(result, "recordObj->GetInterface");
+
+ if(SL_RESULT_SUCCESS == result)
+ {
+ result = VCALL(record,SetRecordState)(SL_RECORDSTATE_RECORDING);
+ PRINTERR(result, "record->SetRecordState");
+ }
+
+ if(SL_RESULT_SUCCESS != result)
+ {
+ ALCopenslCapture_lock(self);
+ aluHandleDisconnect(STATIC_CAST(ALCbackend, self)->mDevice,
+ "Failed to start capture: 0x%08x", result);
+ ALCopenslCapture_unlock(self);
return ALC_FALSE;
}
return ALC_TRUE;
}
-
-static void opensl_stop_playback(ALCdevice *Device)
+static void ALCopenslCapture_stop(ALCopenslCapture *self)
{
- osl_data *data = Device->ExtraData;
- SLPlayItf player;
- SLAndroidSimpleBufferQueueItf bufferQueue;
+ SLRecordItf record;
SLresult result;
- result = VCALL(data->bufferQueueObject,GetInterface)(SL_IID_PLAY, &player);
- PRINTERR(result, "bufferQueue->GetInterface");
+ result = VCALL(self->mRecordObj,GetInterface)(SL_IID_RECORD, &record);
+ PRINTERR(result, "recordObj->GetInterface");
+
if(SL_RESULT_SUCCESS == result)
{
- result = VCALL(player,SetPlayState)(SL_PLAYSTATE_STOPPED);
- PRINTERR(result, "player->SetPlayState");
+ result = VCALL(record,SetRecordState)(SL_RECORDSTATE_PAUSED);
+ PRINTERR(result, "record->SetRecordState");
}
+}
- result = VCALL(data->bufferQueueObject,GetInterface)(SL_IID_BUFFERQUEUE, &bufferQueue);
- PRINTERR(result, "bufferQueue->GetInterface");
- if(SL_RESULT_SUCCESS == result)
+static ALCenum ALCopenslCapture_captureSamples(ALCopenslCapture *self, ALCvoid *buffer, ALCuint samples)
+{
+ ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+ ALsizei chunk_size = device->UpdateSize * self->mFrameSize;
+ SLAndroidSimpleBufferQueueItf bufferQueue;
+ ll_ringbuffer_data_t data[2];
+ SLresult result;
+ ALCuint i;
+
+ result = VCALL(self->mRecordObj,GetInterface)(SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
+ &bufferQueue);
+ PRINTERR(result, "recordObj->GetInterface");
+
+ /* Read the desired samples from the ring buffer then advance its read
+ * pointer.
+ */
+ ll_ringbuffer_get_read_vector(self->mRing, data);
+ for(i = 0;i < samples;)
{
- result = VCALL0(bufferQueue,Clear)();
- PRINTERR(result, "bufferQueue->Clear");
+ ALCuint rem = minu(samples - i, device->UpdateSize - self->mSplOffset);
+ memcpy((ALCbyte*)buffer + i*self->mFrameSize,
+ data[0].buf + self->mSplOffset*self->mFrameSize,
+ rem * self->mFrameSize);
+
+ self->mSplOffset += rem;
+ if(self->mSplOffset == device->UpdateSize)
+ {
+ /* Finished a chunk, reset the offset and advance the read pointer. */
+ self->mSplOffset = 0;
+
+ ll_ringbuffer_read_advance(self->mRing, 1);
+ result = VCALL(bufferQueue,Enqueue)(data[0].buf, chunk_size);
+ PRINTERR(result, "bufferQueue->Enqueue");
+ if(SL_RESULT_SUCCESS != result) break;
+
+ data[0].len--;
+ if(!data[0].len)
+ data[0] = data[1];
+ else
+ data[0].buf += chunk_size;
+ }
+
+ i += rem;
}
- if(SL_RESULT_SUCCESS == result)
+
+ 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");
+ ALCopenslCapture_lock(self);
+ aluHandleDisconnect(device, "Failed to update capture buffer: 0x%08x", result);
+ ALCopenslCapture_unlock(self);
+ return ALC_INVALID_DEVICE;
}
- free(data->buffer);
- data->buffer = NULL;
- data->bufferSize = 0;
+ return ALC_NO_ERROR;
}
+static ALCuint ALCopenslCapture_availableSamples(ALCopenslCapture *self)
+{
+ ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+ return ll_ringbuffer_read_space(self->mRing) * device->UpdateSize;
+}
-static const BackendFuncs opensl_funcs = {
- opensl_open_playback,
- opensl_close_playback,
- opensl_reset_playback,
- opensl_start_playback,
- opensl_stop_playback,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL
-};
+typedef struct ALCopenslBackendFactory {
+ DERIVE_FROM_TYPE(ALCbackendFactory);
+} ALCopenslBackendFactory;
+#define ALCOPENSLBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCopenslBackendFactory, ALCbackendFactory) } }
-ALCboolean alc_opensl_init(BackendFuncs *func_list)
+static ALCboolean ALCopenslBackendFactory_init(ALCopenslBackendFactory* UNUSED(self))
{
- *func_list = opensl_funcs;
return ALC_TRUE;
}
-void alc_opensl_deinit(void)
+static void ALCopenslBackendFactory_deinit(ALCopenslBackendFactory* UNUSED(self))
+{
+}
+
+static ALCboolean ALCopenslBackendFactory_querySupport(ALCopenslBackendFactory* UNUSED(self), ALCbackend_Type type)
{
+ if(type == ALCbackend_Playback || type == ALCbackend_Capture)
+ return ALC_TRUE;
+ return ALC_FALSE;
}
-void alc_opensl_probe(enum DevProbe type)
+static void ALCopenslBackendFactory_probe(ALCopenslBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
{
switch(type)
{
case ALL_DEVICE_PROBE:
- AppendAllDevicesList(opensl_device);
- break;
case CAPTURE_DEVICE_PROBE:
+ alstr_append_range(outnames, opensl_device, opensl_device+sizeof(opensl_device));
break;
}
}
+
+static ALCbackend* ALCopenslBackendFactory_createBackend(ALCopenslBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
+{
+ if(type == ALCbackend_Playback)
+ {
+ ALCopenslPlayback *backend;
+ NEW_OBJ(backend, ALCopenslPlayback)(device);
+ if(!backend) return NULL;
+ return STATIC_CAST(ALCbackend, backend);
+ }
+ if(type == ALCbackend_Capture)
+ {
+ ALCopenslCapture *backend;
+ NEW_OBJ(backend, ALCopenslCapture)(device);
+ if(!backend) return NULL;
+ return STATIC_CAST(ALCbackend, backend);
+ }
+
+ return NULL;
+}
+
+DEFINE_ALCBACKENDFACTORY_VTABLE(ALCopenslBackendFactory);
+
+
+ALCbackendFactory *ALCopenslBackendFactory_getFactory(void)
+{
+ static ALCopenslBackendFactory factory = ALCOPENSLBACKENDFACTORY_INITIALIZER;
+ return STATIC_CAST(ALCbackendFactory, &factory);
+}
diff --git a/Alc/backends/oss.c b/Alc/backends/oss.c
index dce42e21..71faad25 100644
--- a/Alc/backends/oss.c
+++ b/Alc/backends/oss.c
@@ -22,10 +22,12 @@
#include <sys/ioctl.h>
#include <sys/types.h>
+#include <sys/time.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
+#include <string.h>
#include <memory.h>
#include <unistd.h>
#include <errno.h>
@@ -33,6 +35,8 @@
#include "alMain.h"
#include "alu.h"
+#include "alconfig.h"
+#include "ringbuffer.h"
#include "threads.h"
#include "compat.h"
@@ -51,11 +55,176 @@
#define SOUND_MIXER_WRITE MIXER_WRITE
#endif
+#if defined(SOUND_VERSION) && (SOUND_VERSION < 0x040000)
+#define ALC_OSS_COMPAT
+#endif
+#ifndef SNDCTL_AUDIOINFO
+#define ALC_OSS_COMPAT
+#endif
+
+/*
+ * FreeBSD strongly discourages the use of specific devices,
+ * such as those returned in oss_audioinfo.devnode
+ */
+#ifdef __FreeBSD__
+#define ALC_OSS_DEVNODE_TRUC
+#endif
+
+struct oss_device {
+ const ALCchar *handle;
+ const char *path;
+ struct oss_device *next;
+};
+
+static struct oss_device oss_playback = {
+ "OSS Default",
+ "/dev/dsp",
+ NULL
+};
+
+static struct oss_device oss_capture = {
+ "OSS Default",
+ "/dev/dsp",
+ NULL
+};
+
+#ifdef ALC_OSS_COMPAT
+
+#define DSP_CAP_OUTPUT 0x00020000
+#define DSP_CAP_INPUT 0x00010000
+static void ALCossListPopulate(struct oss_device *UNUSED(devlist), int UNUSED(type_flag))
+{
+}
+
+#else
+
+#ifndef HAVE_STRNLEN
+static size_t strnlen(const char *str, size_t maxlen)
+{
+ const char *end = memchr(str, 0, maxlen);
+ if(!end) return maxlen;
+ return end - str;
+}
+#endif
+
+static void ALCossListAppend(struct oss_device *list, const char *handle, size_t hlen, const char *path, size_t plen)
+{
+ struct oss_device *next;
+ struct oss_device *last;
+ size_t i;
+
+ /* skip the first item "OSS Default" */
+ last = list;
+ next = list->next;
+#ifdef ALC_OSS_DEVNODE_TRUC
+ for(i = 0;i < plen;i++)
+ {
+ if(path[i] == '.')
+ {
+ if(strncmp(path + i, handle + hlen + i - plen, plen - i) == 0)
+ hlen = hlen + i - plen;
+ plen = i;
+ }
+ }
+#else
+ (void)i;
+#endif
+ if(handle[0] == '\0')
+ {
+ handle = path;
+ hlen = plen;
+ }
+
+ while(next != NULL)
+ {
+ if(strncmp(next->path, path, plen) == 0)
+ return;
+ last = next;
+ next = next->next;
+ }
+
+ next = (struct oss_device*)malloc(sizeof(struct oss_device) + hlen + plen + 2);
+ next->handle = (char*)(next + 1);
+ next->path = next->handle + hlen + 1;
+ next->next = NULL;
+ last->next = next;
+
+ strncpy((char*)next->handle, handle, hlen);
+ ((char*)next->handle)[hlen] = '\0';
+ strncpy((char*)next->path, path, plen);
+ ((char*)next->path)[plen] = '\0';
-static const ALCchar oss_device[] = "OSS Default";
+ TRACE("Got device \"%s\", \"%s\"\n", next->handle, next->path);
+}
+
+static void ALCossListPopulate(struct oss_device *devlist, int type_flag)
+{
+ struct oss_sysinfo si;
+ struct oss_audioinfo ai;
+ int fd, i;
+
+ if((fd=open("/dev/mixer", O_RDONLY)) < 0)
+ {
+ TRACE("Could not open /dev/mixer: %s\n", strerror(errno));
+ return;
+ }
+ if(ioctl(fd, SNDCTL_SYSINFO, &si) == -1)
+ {
+ TRACE("SNDCTL_SYSINFO failed: %s\n", strerror(errno));
+ goto done;
+ }
+ for(i = 0;i < si.numaudios;i++)
+ {
+ const char *handle;
+ size_t len;
-static const char *oss_driver = "/dev/dsp";
-static const char *oss_capture = "/dev/dsp";
+ ai.dev = i;
+ if(ioctl(fd, SNDCTL_AUDIOINFO, &ai) == -1)
+ {
+ ERR("SNDCTL_AUDIOINFO (%d) failed: %s\n", i, strerror(errno));
+ continue;
+ }
+ if(ai.devnode[0] == '\0')
+ continue;
+
+ if(ai.handle[0] != '\0')
+ {
+ len = strnlen(ai.handle, sizeof(ai.handle));
+ handle = ai.handle;
+ }
+ else
+ {
+ len = strnlen(ai.name, sizeof(ai.name));
+ handle = ai.name;
+ }
+ if((ai.caps&type_flag))
+ ALCossListAppend(devlist, handle, len, ai.devnode,
+ strnlen(ai.devnode, sizeof(ai.devnode)));
+ }
+
+done:
+ close(fd);
+}
+
+#endif
+
+static void ALCossListFree(struct oss_device *list)
+{
+ struct oss_device *cur;
+ if(list == NULL)
+ return;
+
+ /* skip the first item "OSS Default" */
+ cur = list->next;
+ list->next = NULL;
+
+ while(cur != NULL)
+ {
+ struct oss_device *next = cur->next;
+ free(cur);
+ cur = next;
+ }
+}
static int log2i(ALCuint x)
{
@@ -68,7 +237,6 @@ static int log2i(ALCuint x)
return y;
}
-
typedef struct ALCplaybackOSS {
DERIVE_FROM_TYPE(ALCbackend);
@@ -77,22 +245,21 @@ typedef struct ALCplaybackOSS {
ALubyte *mix_data;
int data_size;
- volatile int killNow;
+ ATOMIC(ALenum) killNow;
althrd_t thread;
} ALCplaybackOSS;
static int ALCplaybackOSS_mixerProc(void *ptr);
static void ALCplaybackOSS_Construct(ALCplaybackOSS *self, ALCdevice *device);
-static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, void, Destruct)
+static void ALCplaybackOSS_Destruct(ALCplaybackOSS *self);
static ALCenum ALCplaybackOSS_open(ALCplaybackOSS *self, const ALCchar *name);
-static void ALCplaybackOSS_close(ALCplaybackOSS *self);
static ALCboolean ALCplaybackOSS_reset(ALCplaybackOSS *self);
static ALCboolean ALCplaybackOSS_start(ALCplaybackOSS *self);
static void ALCplaybackOSS_stop(ALCplaybackOSS *self);
static DECLARE_FORWARD2(ALCplaybackOSS, ALCbackend, ALCenum, captureSamples, ALCvoid*, ALCuint)
static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, ALCuint, availableSamples)
-static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, ALint64, getLatency)
+static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, ClockLatency, getClockLatency)
static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCplaybackOSS)
@@ -103,42 +270,66 @@ static int ALCplaybackOSS_mixerProc(void *ptr)
{
ALCplaybackOSS *self = (ALCplaybackOSS*)ptr;
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
- ALint frameSize;
+ struct timeval timeout;
+ ALubyte *write_ptr;
+ ALint frame_size;
+ ALint to_write;
ssize_t wrote;
+ fd_set wfds;
+ int sret;
SetRTPriority();
althrd_setname(althrd_current(), MIXER_THREAD_NAME);
- frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
+ frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
- while(!self->killNow && device->Connected)
+ ALCplaybackOSS_lock(self);
+ while(!ATOMIC_LOAD(&self->killNow, almemory_order_acquire) &&
+ ATOMIC_LOAD(&device->Connected, almemory_order_acquire))
{
- ALint len = self->data_size;
- ALubyte *WritePtr = self->mix_data;
+ FD_ZERO(&wfds);
+ FD_SET(self->fd, &wfds);
+ timeout.tv_sec = 1;
+ timeout.tv_usec = 0;
+
+ ALCplaybackOSS_unlock(self);
+ sret = select(self->fd+1, NULL, &wfds, NULL, &timeout);
+ ALCplaybackOSS_lock(self);
+ if(sret < 0)
+ {
+ if(errno == EINTR)
+ continue;
+ ERR("select failed: %s\n", strerror(errno));
+ aluHandleDisconnect(device, "Failed waiting for playback buffer: %s", strerror(errno));
+ break;
+ }
+ else if(sret == 0)
+ {
+ WARN("select timeout\n");
+ continue;
+ }
- aluMixData(device, WritePtr, len/frameSize);
- while(len > 0 && !self->killNow)
+ write_ptr = self->mix_data;
+ to_write = self->data_size;
+ aluMixData(device, write_ptr, to_write/frame_size);
+ while(to_write > 0 && !ATOMIC_LOAD_SEQ(&self->killNow))
{
- wrote = write(self->fd, WritePtr, len);
+ wrote = write(self->fd, write_ptr, to_write);
if(wrote < 0)
{
- if(errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR)
- {
- ERR("write failed: %s\n", strerror(errno));
- ALCplaybackOSS_lock(self);
- aluHandleDisconnect(device);
- ALCplaybackOSS_unlock(self);
- break;
- }
-
- al_nssleep(1000000);
- continue;
+ if(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
+ continue;
+ ERR("write failed: %s\n", strerror(errno));
+ aluHandleDisconnect(device, "Failed writing playback samples: %s",
+ strerror(errno));
+ break;
}
- len -= wrote;
- WritePtr += wrote;
+ to_write -= wrote;
+ write_ptr += wrote;
}
}
+ ALCplaybackOSS_unlock(self);
return 0;
}
@@ -148,37 +339,59 @@ static void ALCplaybackOSS_Construct(ALCplaybackOSS *self, ALCdevice *device)
{
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(ALCplaybackOSS, ALCbackend, self);
+
+ self->fd = -1;
+ ATOMIC_INIT(&self->killNow, AL_FALSE);
+}
+
+static void ALCplaybackOSS_Destruct(ALCplaybackOSS *self)
+{
+ if(self->fd != -1)
+ close(self->fd);
+ self->fd = -1;
+
+ ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
}
static ALCenum ALCplaybackOSS_open(ALCplaybackOSS *self, const ALCchar *name)
{
+ struct oss_device *dev = &oss_playback;
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
- if(!name)
- name = oss_device;
- else if(strcmp(name, oss_device) != 0)
- return ALC_INVALID_VALUE;
-
- self->killNow = 0;
+ if(!name || strcmp(name, dev->handle) == 0)
+ name = dev->handle;
+ else
+ {
+ if(!dev->next)
+ {
+ ALCossListPopulate(&oss_playback, DSP_CAP_OUTPUT);
+ dev = &oss_playback;
+ }
+ while(dev != NULL)
+ {
+ if (strcmp(dev->handle, name) == 0)
+ break;
+ dev = dev->next;
+ }
+ if(dev == NULL)
+ {
+ WARN("Could not find \"%s\" in device list\n", name);
+ return ALC_INVALID_VALUE;
+ }
+ }
- self->fd = open(oss_driver, O_WRONLY);
+ self->fd = open(dev->path, O_WRONLY);
if(self->fd == -1)
{
- ERR("Could not open %s: %s\n", oss_driver, strerror(errno));
+ ERR("Could not open %s: %s\n", dev->path, strerror(errno));
return ALC_INVALID_VALUE;
}
- al_string_copy_cstr(&device->DeviceName, name);
+ alstr_copy_cstr(&device->DeviceName, name);
return ALC_NO_ERROR;
}
-static void ALCplaybackOSS_close(ALCplaybackOSS *self)
-{
- close(self->fd);
- self->fd = -1;
-}
-
static ALCboolean ALCplaybackOSS_reset(ALCplaybackOSS *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
@@ -212,18 +425,11 @@ static ALCboolean ALCplaybackOSS_reset(ALCplaybackOSS *self)
}
periods = device->NumUpdates;
- numChannels = ChannelsFromDevFmt(device->FmtChans);
- frameSize = numChannels * BytesFromDevFmt(device->FmtType);
-
+ numChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
ossSpeed = device->Frequency;
- log2FragmentSize = log2i(device->UpdateSize * frameSize);
-
- /* according to the OSS spec, 16 bytes are the minimum */
- if (log2FragmentSize < 4)
- log2FragmentSize = 4;
- /* Subtract one period since the temp mixing buffer counts as one. Still
- * need at least two on the card, though. */
- if(periods > 2) periods--;
+ frameSize = numChannels * BytesFromDevFmt(device->FmtType);
+ /* According to the OSS spec, 16 bytes (log2(16)) is the minimum. */
+ log2FragmentSize = maxi(log2i(device->UpdateSize*frameSize), 4);
numFragmentsLogSize = (periods << 16) | log2FragmentSize;
#define CHECKERR(func) if((func) < 0) { \
@@ -245,7 +451,7 @@ static ALCboolean ALCplaybackOSS_reset(ALCplaybackOSS *self)
}
#undef CHECKERR
- if((int)ChannelsFromDevFmt(device->FmtChans) != numChannels)
+ if((int)ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder) != numChannels)
{
ERR("Failed to set %s, got %d channels instead\n", DevFmtChannelsString(device->FmtChans), numChannels);
return ALC_FALSE;
@@ -261,7 +467,7 @@ static ALCboolean ALCplaybackOSS_reset(ALCplaybackOSS *self)
device->Frequency = ossSpeed;
device->UpdateSize = info.fragsize / frameSize;
- device->NumUpdates = info.fragments + 1;
+ device->NumUpdates = info.fragments;
SetDefaultChannelOrder(device);
@@ -272,10 +478,12 @@ static ALCboolean ALCplaybackOSS_start(ALCplaybackOSS *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
- self->data_size = device->UpdateSize * FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
+ self->data_size = device->UpdateSize * FrameSizeFromDevFmt(
+ device->FmtChans, device->FmtType, device->AmbiOrder
+ );
self->mix_data = calloc(1, self->data_size);
- self->killNow = 0;
+ ATOMIC_STORE_SEQ(&self->killNow, AL_FALSE);
if(althrd_create(&self->thread, ALCplaybackOSS_mixerProc, self) != althrd_success)
{
free(self->mix_data);
@@ -290,10 +498,8 @@ static void ALCplaybackOSS_stop(ALCplaybackOSS *self)
{
int res;
- if(self->killNow)
+ if(ATOMIC_EXCHANGE_SEQ(&self->killNow, AL_TRUE))
return;
-
- self->killNow = 1;
althrd_join(self->thread, &res);
if(ioctl(self->fd, SNDCTL_DSP_RESET) != 0)
@@ -309,28 +515,23 @@ typedef struct ALCcaptureOSS {
int fd;
- ALubyte *read_data;
- int data_size;
-
- RingBuffer *ring;
- int doCapture;
+ ll_ringbuffer_t *ring;
- volatile int killNow;
+ ATOMIC(ALenum) killNow;
althrd_t thread;
} ALCcaptureOSS;
static int ALCcaptureOSS_recordProc(void *ptr);
static void ALCcaptureOSS_Construct(ALCcaptureOSS *self, ALCdevice *device);
-static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, void, Destruct)
+static void ALCcaptureOSS_Destruct(ALCcaptureOSS *self);
static ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name);
-static void ALCcaptureOSS_close(ALCcaptureOSS *self);
static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, ALCboolean, reset)
static ALCboolean ALCcaptureOSS_start(ALCcaptureOSS *self);
static void ALCcaptureOSS_stop(ALCcaptureOSS *self);
static ALCenum ALCcaptureOSS_captureSamples(ALCcaptureOSS *self, ALCvoid *buffer, ALCuint samples);
static ALCuint ALCcaptureOSS_availableSamples(ALCcaptureOSS *self);
-static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, ALint64, getLatency)
+static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, ClockLatency, getClockLatency)
static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCcaptureOSS)
@@ -341,32 +542,55 @@ static int ALCcaptureOSS_recordProc(void *ptr)
{
ALCcaptureOSS *self = (ALCcaptureOSS*)ptr;
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
- int frameSize;
- int amt;
+ struct timeval timeout;
+ int frame_size;
+ fd_set rfds;
+ ssize_t amt;
+ int sret;
SetRTPriority();
althrd_setname(althrd_current(), RECORD_THREAD_NAME);
- frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
+ frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
- while(!self->killNow)
+ while(!ATOMIC_LOAD_SEQ(&self->killNow))
{
- amt = read(self->fd, self->read_data, self->data_size);
- if(amt < 0)
+ ll_ringbuffer_data_t vec[2];
+
+ FD_ZERO(&rfds);
+ FD_SET(self->fd, &rfds);
+ timeout.tv_sec = 1;
+ timeout.tv_usec = 0;
+
+ sret = select(self->fd+1, &rfds, NULL, NULL, &timeout);
+ if(sret < 0)
{
- ERR("read failed: %s\n", strerror(errno));
- ALCcaptureOSS_lock(self);
- aluHandleDisconnect(device);
- ALCcaptureOSS_unlock(self);
+ if(errno == EINTR)
+ continue;
+ ERR("select failed: %s\n", strerror(errno));
+ aluHandleDisconnect(device, "Failed to check capture samples: %s", strerror(errno));
break;
}
- if(amt == 0)
+ else if(sret == 0)
{
- al_nssleep(1000000);
+ WARN("select timeout\n");
continue;
}
- if(self->doCapture)
- WriteRingBuffer(self->ring, self->read_data, amt/frameSize);
+
+ ll_ringbuffer_get_write_vector(self->ring, vec);
+ if(vec[0].len > 0)
+ {
+ amt = read(self->fd, vec[0].buf, vec[0].len*frame_size);
+ if(amt < 0)
+ {
+ ERR("read failed: %s\n", strerror(errno));
+ ALCcaptureOSS_lock(self);
+ aluHandleDisconnect(device, "Failed reading capture samples: %s", strerror(errno));
+ ALCcaptureOSS_unlock(self);
+ break;
+ }
+ ll_ringbuffer_write_advance(self->ring, amt/frame_size);
+ }
}
return 0;
@@ -377,11 +601,27 @@ static void ALCcaptureOSS_Construct(ALCcaptureOSS *self, ALCdevice *device)
{
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(ALCcaptureOSS, ALCbackend, self);
+
+ self->fd = -1;
+ self->ring = NULL;
+ ATOMIC_INIT(&self->killNow, AL_FALSE);
+}
+
+static void ALCcaptureOSS_Destruct(ALCcaptureOSS *self)
+{
+ if(self->fd != -1)
+ close(self->fd);
+ self->fd = -1;
+
+ ll_ringbuffer_free(self->ring);
+ self->ring = NULL;
+ ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
}
static ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+ struct oss_device *dev = &oss_capture;
int numFragmentsLogSize;
int log2FragmentSize;
unsigned int periods;
@@ -392,15 +632,32 @@ static ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name)
int ossSpeed;
char *err;
- if(!name)
- name = oss_device;
- else if(strcmp(name, oss_device) != 0)
- return ALC_INVALID_VALUE;
+ if(!name || strcmp(name, dev->handle) == 0)
+ name = dev->handle;
+ else
+ {
+ if(!dev->next)
+ {
+ ALCossListPopulate(&oss_capture, DSP_CAP_INPUT);
+ dev = &oss_capture;
+ }
+ while(dev != NULL)
+ {
+ if (strcmp(dev->handle, name) == 0)
+ break;
+ dev = dev->next;
+ }
+ if(dev == NULL)
+ {
+ WARN("Could not find \"%s\" in device list\n", name);
+ return ALC_INVALID_VALUE;
+ }
+ }
- self->fd = open(oss_capture, O_RDONLY);
+ self->fd = open(dev->path, O_RDONLY);
if(self->fd == -1)
{
- ERR("Could not open %s: %s\n", oss_capture, strerror(errno));
+ ERR("Could not open %s: %s\n", dev->path, strerror(errno));
return ALC_INVALID_VALUE;
}
@@ -424,7 +681,7 @@ static ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name)
}
periods = 4;
- numChannels = ChannelsFromDevFmt(device->FmtChans);
+ numChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
frameSize = numChannels * BytesFromDevFmt(device->FmtType);
ossSpeed = device->Frequency;
log2FragmentSize = log2i(device->UpdateSize * device->NumUpdates *
@@ -454,7 +711,7 @@ static ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name)
}
#undef CHECKERR
- if((int)ChannelsFromDevFmt(device->FmtChans) != numChannels)
+ if((int)ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder) != numChannels)
{
ERR("Failed to set %s, got %d channels instead\n", DevFmtChannelsString(device->FmtChans), numChannels);
close(self->fd);
@@ -472,7 +729,7 @@ static ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name)
return ALC_INVALID_VALUE;
}
- self->ring = CreateRingBuffer(frameSize, device->UpdateSize * device->NumUpdates);
+ self->ring = ll_ringbuffer_create(device->UpdateSize*device->NumUpdates, frameSize, false);
if(!self->ring)
{
ERR("Ring buffer create failed\n");
@@ -481,60 +738,41 @@ static ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name)
return ALC_OUT_OF_MEMORY;
}
- self->data_size = info.fragsize;
- self->read_data = calloc(1, self->data_size);
-
- self->killNow = 0;
- if(althrd_create(&self->thread, ALCcaptureOSS_recordProc, self) != althrd_success)
- {
- device->ExtraData = NULL;
- close(self->fd);
- self->fd = -1;
- return ALC_OUT_OF_MEMORY;
- }
-
- al_string_copy_cstr(&device->DeviceName, name);
+ alstr_copy_cstr(&device->DeviceName, name);
return ALC_NO_ERROR;
}
-static void ALCcaptureOSS_close(ALCcaptureOSS *self)
-{
- int res;
-
- self->killNow = 1;
- althrd_join(self->thread, &res);
-
- close(self->fd);
- self->fd = -1;
-
- DestroyRingBuffer(self->ring);
- self->ring = NULL;
-
- free(self->read_data);
- self->read_data = NULL;
-}
-
static ALCboolean ALCcaptureOSS_start(ALCcaptureOSS *self)
{
- self->doCapture = 1;
+ ATOMIC_STORE_SEQ(&self->killNow, AL_FALSE);
+ if(althrd_create(&self->thread, ALCcaptureOSS_recordProc, self) != althrd_success)
+ return ALC_FALSE;
return ALC_TRUE;
}
static void ALCcaptureOSS_stop(ALCcaptureOSS *self)
{
- self->doCapture = 0;
+ int res;
+
+ if(ATOMIC_EXCHANGE_SEQ(&self->killNow, AL_TRUE))
+ return;
+
+ althrd_join(self->thread, &res);
+
+ if(ioctl(self->fd, SNDCTL_DSP_RESET) != 0)
+ ERR("Error resetting device: %s\n", strerror(errno));
}
static ALCenum ALCcaptureOSS_captureSamples(ALCcaptureOSS *self, ALCvoid *buffer, ALCuint samples)
{
- ReadRingBuffer(self->ring, buffer, samples);
+ ll_ringbuffer_read(self->ring, buffer, samples);
return ALC_NO_ERROR;
}
static ALCuint ALCcaptureOSS_availableSamples(ALCcaptureOSS *self)
{
- return RingBufferSize(self->ring);
+ return ll_ringbuffer_read_space(self->ring);
}
@@ -546,9 +784,9 @@ typedef struct ALCossBackendFactory {
ALCbackendFactory *ALCossBackendFactory_getFactory(void);
static ALCboolean ALCossBackendFactory_init(ALCossBackendFactory *self);
-static DECLARE_FORWARD(ALCossBackendFactory, ALCbackendFactory, void, deinit)
+static void ALCossBackendFactory_deinit(ALCossBackendFactory *self);
static ALCboolean ALCossBackendFactory_querySupport(ALCossBackendFactory *self, ALCbackend_Type type);
-static void ALCossBackendFactory_probe(ALCossBackendFactory *self, enum DevProbe type);
+static void ALCossBackendFactory_probe(ALCossBackendFactory *self, enum DevProbe type, al_string *outnames);
static ALCbackend* ALCossBackendFactory_createBackend(ALCossBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCossBackendFactory);
@@ -562,12 +800,19 @@ ALCbackendFactory *ALCossBackendFactory_getFactory(void)
ALCboolean ALCossBackendFactory_init(ALCossBackendFactory* UNUSED(self))
{
- ConfigValueStr(NULL, "oss", "device", &oss_driver);
- ConfigValueStr(NULL, "oss", "capture", &oss_capture);
+ ConfigValueStr(NULL, "oss", "device", &oss_playback.path);
+ ConfigValueStr(NULL, "oss", "capture", &oss_capture.path);
return ALC_TRUE;
}
+void ALCossBackendFactory_deinit(ALCossBackendFactory* UNUSED(self))
+{
+ ALCossListFree(&oss_playback);
+ ALCossListFree(&oss_capture);
+}
+
+
ALCboolean ALCossBackendFactory_querySupport(ALCossBackendFactory* UNUSED(self), ALCbackend_Type type)
{
if(type == ALCbackend_Playback || type == ALCbackend_Capture)
@@ -575,29 +820,31 @@ ALCboolean ALCossBackendFactory_querySupport(ALCossBackendFactory* UNUSED(self),
return ALC_FALSE;
}
-void ALCossBackendFactory_probe(ALCossBackendFactory* UNUSED(self), enum DevProbe type)
+void ALCossBackendFactory_probe(ALCossBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
{
+ struct oss_device *cur = NULL;
switch(type)
{
case ALL_DEVICE_PROBE:
- {
-#ifdef HAVE_STAT
- struct stat buf;
- if(stat(oss_driver, &buf) == 0)
-#endif
- AppendAllDevicesList(oss_device);
- }
- break;
+ ALCossListFree(&oss_playback);
+ ALCossListPopulate(&oss_playback, DSP_CAP_OUTPUT);
+ cur = &oss_playback;
+ break;
case CAPTURE_DEVICE_PROBE:
- {
+ ALCossListFree(&oss_capture);
+ ALCossListPopulate(&oss_capture, DSP_CAP_INPUT);
+ cur = &oss_capture;
+ break;
+ }
+ while(cur != NULL)
+ {
#ifdef HAVE_STAT
- struct stat buf;
- if(stat(oss_capture, &buf) == 0)
+ struct stat buf;
+ if(stat(cur->path, &buf) == 0)
#endif
- AppendCaptureDeviceList(oss_device);
- }
- break;
+ alstr_append_range(outnames, cur->handle, cur->handle+strlen(cur->handle)+1);
+ cur = cur->next;
}
}
diff --git a/Alc/backends/portaudio.c b/Alc/backends/portaudio.c
index f45833c6..6a6cfa31 100644
--- a/Alc/backends/portaudio.c
+++ b/Alc/backends/portaudio.c
@@ -26,6 +26,8 @@
#include "alMain.h"
#include "alu.h"
+#include "alconfig.h"
+#include "ringbuffer.h"
#include "compat.h"
#include "backends/base.h"
@@ -139,13 +141,12 @@ static int ALCportPlayback_WriteCallback(const void *inputBuffer, void *outputBu
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, ClockLatency, getClockLatency)
static DECLARE_FORWARD(ALCportPlayback, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCportPlayback, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCportPlayback)
@@ -163,8 +164,9 @@ static void ALCportPlayback_Construct(ALCportPlayback *self, ALCdevice *device)
static void ALCportPlayback_Destruct(ALCportPlayback *self)
{
- if(self->stream)
- Pa_CloseStream(self->stream);
+ PaError err = self->stream ? Pa_CloseStream(self->stream) : paNoError;
+ if(err != paNoError)
+ ERR("Error closing stream: %s\n", Pa_GetErrorText(err));
self->stream = NULL;
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
@@ -177,7 +179,9 @@ static int ALCportPlayback_WriteCallback(const void *UNUSED(inputBuffer), void *
{
ALCportPlayback *self = userData;
+ ALCportPlayback_lock(self);
aluMixData(STATIC_CAST(ALCbackend, self)->mDevice, outputBuffer, framesPerBuffer);
+ ALCportPlayback_unlock(self);
return 0;
}
@@ -243,20 +247,12 @@ retry_open:
return ALC_INVALID_VALUE;
}
- al_string_copy_cstr(&device->DeviceName, name);
+ alstr_copy_cstr(&device->DeviceName, name);
return ALC_NO_ERROR;
}
-static void ALCportPlayback_close(ALCportPlayback *self)
-{
- PaError err = Pa_CloseStream(self->stream);
- if(err != paNoError)
- ERR("Error closing stream: %s\n", Pa_GetErrorText(err));
- self->stream = NULL;
-}
-
static ALCboolean ALCportPlayback_reset(ALCportPlayback *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
@@ -334,13 +330,12 @@ static int ALCportCapture_ReadCallback(const void *inputBuffer, void *outputBuff
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, ClockLatency, getClockLatency)
static DECLARE_FORWARD(ALCportCapture, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCportCapture, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCportCapture)
@@ -354,16 +349,17 @@ static void ALCportCapture_Construct(ALCportCapture *self, ALCdevice *device)
SET_VTABLE2(ALCportCapture, ALCbackend, self);
self->stream = NULL;
+ self->ring = NULL;
}
static void ALCportCapture_Destruct(ALCportCapture *self)
{
- if(self->stream)
- Pa_CloseStream(self->stream);
+ PaError err = self->stream ? Pa_CloseStream(self->stream) : paNoError;
+ if(err != paNoError)
+ ERR("Error closing stream: %s\n", Pa_GetErrorText(err));
self->stream = NULL;
- if(self->ring)
- ll_ringbuffer_free(self->ring);
+ ll_ringbuffer_free(self->ring);
self->ring = NULL;
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
@@ -397,9 +393,9 @@ static ALCenum ALCportCapture_open(ALCportCapture *self, const ALCchar *name)
samples = device->UpdateSize * device->NumUpdates;
samples = maxu(samples, 100 * device->Frequency / 1000);
- frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
+ frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
- self->ring = ll_ringbuffer_create(samples, frame_size);
+ self->ring = ll_ringbuffer_create(samples, frame_size, false);
if(self->ring == NULL) return ALC_INVALID_VALUE;
self->params.device = -1;
@@ -431,7 +427,7 @@ static ALCenum ALCportCapture_open(ALCportCapture *self, const ALCchar *name)
ERR("%s samples not supported\n", DevFmtTypeString(device->FmtType));
return ALC_INVALID_VALUE;
}
- self->params.channelCount = ChannelsFromDevFmt(device->FmtChans);
+ self->params.channelCount = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
err = Pa_OpenStream(&self->stream, &self->params, NULL,
device->Frequency, paFramesPerBufferUnspecified, paNoFlag,
@@ -443,22 +439,11 @@ static ALCenum ALCportCapture_open(ALCportCapture *self, const ALCchar *name)
return ALC_INVALID_VALUE;
}
- al_string_copy_cstr(&device->DeviceName, name);
+ alstr_copy_cstr(&device->DeviceName, name);
return ALC_NO_ERROR;
}
-static void ALCportCapture_close(ALCportCapture *self)
-{
- PaError err = Pa_CloseStream(self->stream);
- if(err != paNoError)
- ERR("Error closing stream: %s\n", Pa_GetErrorText(err));
- self->stream = NULL;
-
- ll_ringbuffer_free(self->ring);
- self->ring = NULL;
-}
-
static ALCboolean ALCportCapture_start(ALCportCapture *self)
{
@@ -499,9 +484,8 @@ typedef struct ALCportBackendFactory {
static ALCboolean ALCportBackendFactory_init(ALCportBackendFactory *self);
static void ALCportBackendFactory_deinit(ALCportBackendFactory *self);
static ALCboolean ALCportBackendFactory_querySupport(ALCportBackendFactory *self, ALCbackend_Type type);
-static void ALCportBackendFactory_probe(ALCportBackendFactory *self, enum DevProbe type);
+static void ALCportBackendFactory_probe(ALCportBackendFactory *self, enum DevProbe type, al_string *outnames);
static ALCbackend* ALCportBackendFactory_createBackend(ALCportBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
-
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCportBackendFactory);
@@ -533,15 +517,13 @@ static ALCboolean ALCportBackendFactory_querySupport(ALCportBackendFactory* UNUS
return ALC_FALSE;
}
-static void ALCportBackendFactory_probe(ALCportBackendFactory* UNUSED(self), enum DevProbe type)
+static void ALCportBackendFactory_probe(ALCportBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
{
switch(type)
{
case ALL_DEVICE_PROBE:
- AppendAllDevicesList(pa_device);
- break;
case CAPTURE_DEVICE_PROBE:
- AppendCaptureDeviceList(pa_device);
+ alstr_append_range(outnames, pa_device, pa_device+sizeof(pa_device));
break;
}
}
diff --git a/Alc/backends/pulseaudio.c b/Alc/backends/pulseaudio.c
index 9ad04a71..b34d7abc 100644
--- a/Alc/backends/pulseaudio.c
+++ b/Alc/backends/pulseaudio.c
@@ -25,6 +25,7 @@
#include "alMain.h"
#include "alu.h"
+#include "alconfig.h"
#include "threads.h"
#include "compat.h"
@@ -182,6 +183,8 @@ static ALCboolean pulse_load(void)
#ifdef HAVE_DYNLOAD
if(!pa_handle)
{
+ al_string missing_funcs = AL_STRING_INIT_STATIC();
+
#ifdef _WIN32
#define PALIB "libpulse-0.dll"
#elif defined(__APPLE__) && defined(__MACH__)
@@ -191,12 +194,16 @@ static ALCboolean pulse_load(void)
#endif
pa_handle = LoadLib(PALIB);
if(!pa_handle)
+ {
+ WARN("Failed to load %s\n", PALIB);
return ALC_FALSE;
+ }
#define LOAD_FUNC(x) do { \
p##x = GetSymbol(pa_handle, #x); \
if(!(p##x)) { \
ret = ALC_FALSE; \
+ alstr_append_cstr(&missing_funcs, "\n" #x); \
} \
} while(0)
LOAD_FUNC(pa_context_unref);
@@ -270,9 +277,11 @@ static ALCboolean pulse_load(void)
if(ret == ALC_FALSE)
{
+ WARN("Missing expected functions:%s\n", alstr_get_cstr(missing_funcs));
CloseLib(pa_handle);
pa_handle = NULL;
}
+ alstr_reset(&missing_funcs);
}
#endif /* HAVE_DYNLOAD */
return ret;
@@ -325,18 +334,20 @@ static void wait_for_operation(pa_operation *op, pa_threaded_mainloop *loop)
static pa_context *connect_context(pa_threaded_mainloop *loop, ALboolean silent)
{
const char *name = "OpenAL Soft";
- char path_name[PATH_MAX];
+ al_string binname = AL_STRING_INIT_STATIC();
pa_context_state_t state;
pa_context *context;
int err;
- if(pa_get_binary_name(path_name, sizeof(path_name)))
- name = pa_path_get_filename(path_name);
+ GetProcBinary(NULL, &binname);
+ if(!alstr_empty(binname))
+ name = alstr_get_cstr(binname);
context = pa_context_new(pa_threaded_mainloop_get_api(loop), name);
if(!context)
{
ERR("pa_context_new() failed\n");
+ alstr_reset(&binname);
return NULL;
}
@@ -363,9 +374,10 @@ static pa_context *connect_context(pa_threaded_mainloop *loop, ALboolean silent)
if(!silent)
ERR("Context did not connect: %s\n", pa_strerror(err));
pa_context_unref(context);
- return NULL;
+ context = NULL;
}
+ alstr_reset(&binname);
return context;
}
@@ -443,7 +455,7 @@ static void clear_devlist(vector_DevMap *list)
#define DEINIT_STRS(i) (AL_STRING_DEINIT((i)->name),AL_STRING_DEINIT((i)->device_name))
VECTOR_FOR_EACH(DevMap, *list, DEINIT_STRS);
#undef DEINIT_STRS
- VECTOR_RESIZE(*list, 0);
+ VECTOR_RESIZE(*list, 0, 0);
}
@@ -460,7 +472,7 @@ typedef struct ALCpulsePlayback {
pa_stream *stream;
pa_context *context;
- volatile ALboolean killNow;
+ ATOMIC(ALenum) killNow;
althrd_t thread;
} ALCpulsePlayback;
@@ -483,13 +495,12 @@ static int ALCpulsePlayback_mixerProc(void *ptr);
static void ALCpulsePlayback_Construct(ALCpulsePlayback *self, ALCdevice *device);
static void ALCpulsePlayback_Destruct(ALCpulsePlayback *self);
static ALCenum ALCpulsePlayback_open(ALCpulsePlayback *self, const ALCchar *name);
-static void ALCpulsePlayback_close(ALCpulsePlayback *self);
static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self);
static ALCboolean ALCpulsePlayback_start(ALCpulsePlayback *self);
static void ALCpulsePlayback_stop(ALCpulsePlayback *self);
static DECLARE_FORWARD2(ALCpulsePlayback, ALCbackend, ALCenum, captureSamples, ALCvoid*, ALCuint)
static DECLARE_FORWARD(ALCpulsePlayback, ALCbackend, ALCuint, availableSamples)
-static ALint64 ALCpulsePlayback_getLatency(ALCpulsePlayback *self);
+static ClockLatency ALCpulsePlayback_getClockLatency(ALCpulsePlayback *self);
static void ALCpulsePlayback_lock(ALCpulsePlayback *self);
static void ALCpulsePlayback_unlock(ALCpulsePlayback *self);
DECLARE_DEFAULT_ALLOCATORS(ALCpulsePlayback)
@@ -502,11 +513,20 @@ static void ALCpulsePlayback_Construct(ALCpulsePlayback *self, ALCdevice *device
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(ALCpulsePlayback, ALCbackend, self);
+ self->loop = NULL;
AL_STRING_INIT(self->device_name);
+ ATOMIC_INIT(&self->killNow, AL_TRUE);
}
static void ALCpulsePlayback_Destruct(ALCpulsePlayback *self)
{
+ if(self->loop)
+ {
+ pulse_close(self->loop, self->context, self->stream);
+ self->loop = NULL;
+ self->context = NULL;
+ self->stream = NULL;
+ }
AL_STRING_DEINIT(self->device_name);
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
}
@@ -525,35 +545,35 @@ static void ALCpulsePlayback_deviceCallback(pa_context *UNUSED(context), const p
return;
}
-#define MATCH_INFO_NAME(iter) (al_string_cmp_cstr((iter)->device_name, info->name) == 0)
+#define MATCH_INFO_NAME(iter) (alstr_cmp_cstr((iter)->device_name, info->name) == 0)
VECTOR_FIND_IF(iter, const DevMap, PlaybackDevices, MATCH_INFO_NAME);
- if(iter != VECTOR_ITER_END(PlaybackDevices)) return;
+ if(iter != VECTOR_END(PlaybackDevices)) return;
#undef MATCH_INFO_NAME
AL_STRING_INIT(entry.name);
AL_STRING_INIT(entry.device_name);
- al_string_copy_cstr(&entry.device_name, info->name);
+ alstr_copy_cstr(&entry.device_name, info->name);
count = 0;
while(1)
{
- al_string_copy_cstr(&entry.name, info->description);
+ alstr_copy_cstr(&entry.name, info->description);
if(count != 0)
{
char str[64];
snprintf(str, sizeof(str), " #%d", count+1);
- al_string_append_cstr(&entry.name, str);
+ alstr_append_cstr(&entry.name, str);
}
-#define MATCH_ENTRY(i) (al_string_cmp(entry.name, (i)->name) == 0)
+#define MATCH_ENTRY(i) (alstr_cmp(entry.name, (i)->name) == 0)
VECTOR_FIND_IF(iter, const DevMap, PlaybackDevices, MATCH_ENTRY);
- if(iter == VECTOR_ITER_END(PlaybackDevices)) break;
+ if(iter == VECTOR_END(PlaybackDevices)) break;
#undef MATCH_ENTRY
count++;
}
- TRACE("Got device \"%s\", \"%s\"\n", al_string_get_cstr(entry.name), al_string_get_cstr(entry.device_name));
+ TRACE("Got device \"%s\", \"%s\"\n", alstr_get_cstr(entry.name), alstr_get_cstr(entry.device_name));
VECTOR_PUSH_BACK(PlaybackDevices, entry);
}
@@ -618,6 +638,11 @@ static void ALCpulsePlayback_bufferAttrCallback(pa_stream *stream, void *pdata)
self->attr = *pa_stream_get_buffer_attr(stream);
TRACE("minreq=%d, tlength=%d, prebuf=%d\n", self->attr.minreq, self->attr.tlength, self->attr.prebuf);
+ /* FIXME: Update the device's UpdateSize (and/or NumUpdates) using the new
+ * buffer attributes? Changing UpdateSize will change the ALC_REFRESH
+ * property, which probably shouldn't change between device resets. But
+ * leaving it alone means ALC_REFRESH will be off.
+ */
}
static void ALCpulsePlayback_contextStateCallback(pa_context *context, void *pdata)
@@ -626,7 +651,7 @@ static void ALCpulsePlayback_contextStateCallback(pa_context *context, void *pda
if(pa_context_get_state(context) == PA_CONTEXT_FAILED)
{
ERR("Received context failure!\n");
- aluHandleDisconnect(STATIC_CAST(ALCbackend,self)->mDevice);
+ aluHandleDisconnect(STATIC_CAST(ALCbackend,self)->mDevice, "Playback state failure");
}
pa_threaded_mainloop_signal(self->loop, 0);
}
@@ -637,7 +662,7 @@ static void ALCpulsePlayback_streamStateCallback(pa_stream *stream, void *pdata)
if(pa_stream_get_state(stream) == PA_STREAM_FAILED)
{
ERR("Received stream failure!\n");
- aluHandleDisconnect(STATIC_CAST(ALCbackend,self)->mDevice);
+ aluHandleDisconnect(STATIC_CAST(ALCbackend,self)->mDevice, "Playback stream failure");
}
pa_threaded_mainloop_signal(self->loop, 0);
}
@@ -729,7 +754,7 @@ static void ALCpulsePlayback_sinkNameCallback(pa_context *UNUSED(context), const
return;
}
- al_string_copy_cstr(&device->DeviceName, info->description);
+ alstr_copy_cstr(&device->DeviceName, info->description);
}
@@ -737,9 +762,9 @@ static void ALCpulsePlayback_streamMovedCallback(pa_stream *stream, void *pdata)
{
ALCpulsePlayback *self = pdata;
- al_string_copy_cstr(&self->device_name, pa_stream_get_device_name(stream));
+ alstr_copy_cstr(&self->device_name, pa_stream_get_device_name(stream));
- TRACE("Stream moved to %s\n", al_string_get_cstr(self->device_name));
+ TRACE("Stream moved to %s\n", alstr_get_cstr(self->device_name));
}
@@ -751,6 +776,13 @@ static pa_stream *ALCpulsePlayback_connectStream(const char *device_name,
pa_stream_state_t state;
pa_stream *stream;
+ if(!device_name)
+ {
+ device_name = getenv("ALSOFT_PULSE_DEFAULT");
+ if(device_name && !device_name[0])
+ device_name = NULL;
+ }
+
stream = pa_stream_new_with_proplist(context, "Playback Stream", spec, chanmap, prop_filter);
if(!stream)
{
@@ -789,7 +821,6 @@ static int ALCpulsePlayback_mixerProc(void *ptr)
ALCpulsePlayback *self = ptr;
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
ALuint buffer_size;
- ALint update_size;
size_t frame_size;
ssize_t len;
@@ -798,18 +829,35 @@ static int ALCpulsePlayback_mixerProc(void *ptr)
pa_threaded_mainloop_lock(self->loop);
frame_size = pa_frame_size(&self->spec);
- update_size = device->UpdateSize * frame_size;
-
- /* Sanitize buffer metrics, in case we actually have less than what we
- * asked for. */
- buffer_size = minu(update_size*device->NumUpdates, self->attr.tlength);
- update_size = minu(update_size, buffer_size/2);
- do {
- len = pa_stream_writable_size(self->stream) - self->attr.tlength +
- buffer_size;
- if(len < update_size)
+
+ while(!ATOMIC_LOAD(&self->killNow, almemory_order_acquire) &&
+ ATOMIC_LOAD(&device->Connected, almemory_order_acquire))
+ {
+ void *buf;
+ int ret;
+
+ len = pa_stream_writable_size(self->stream);
+ if(len < 0)
+ {
+ ERR("Failed to get writable size: %ld", (long)len);
+ aluHandleDisconnect(device, "Failed to get writable size: %ld", (long)len);
+ break;
+ }
+
+ /* Make sure we're going to write at least 2 'periods' (minreqs), in
+ * case the server increased it since starting playback. Also round up
+ * the number of writable periods if it's not an integer count.
+ */
+ buffer_size = maxu((self->attr.tlength + self->attr.minreq/2) / self->attr.minreq, 2) *
+ self->attr.minreq;
+
+ /* NOTE: This assumes pa_stream_writable_size returns between 0 and
+ * tlength, else there will be more latency than intended.
+ */
+ len = mini(len - (ssize_t)self->attr.tlength, 0) + buffer_size;
+ if(len < (int32_t)self->attr.minreq)
{
- if(pa_stream_is_corked(self->stream) == 1)
+ if(pa_stream_is_corked(self->stream))
{
pa_operation *o;
o = pa_stream_cork(self->stream, 0, NULL, NULL);
@@ -818,26 +866,17 @@ static int ALCpulsePlayback_mixerProc(void *ptr)
pa_threaded_mainloop_wait(self->loop);
continue;
}
- len -= len%update_size;
- while(len > 0)
- {
- size_t newlen = len;
- void *buf;
- pa_free_cb_t free_func = NULL;
+ len -= len%self->attr.minreq;
+ len -= len%frame_size;
- if(pa_stream_begin_write(self->stream, &buf, &newlen) < 0)
- {
- buf = pa_xmalloc(newlen);
- free_func = pa_xfree;
- }
+ buf = pa_xmalloc(len);
- aluMixData(device, buf, newlen/frame_size);
+ aluMixData(device, buf, len/frame_size);
- pa_stream_write(self->stream, buf, newlen, free_func, 0, PA_SEEK_RELATIVE);
- len -= newlen;
- }
- } while(!self->killNow && device->Connected);
+ ret = pa_stream_write(self->stream, buf, len, pa_xfree, 0, PA_SEEK_RELATIVE);
+ if(ret != PA_OK) ERR("Failed to write to stream: %d, %s\n", ret, pa_strerror(ret));
+ }
pa_threaded_mainloop_unlock(self->loop);
return 0;
@@ -858,12 +897,12 @@ static ALCenum ALCpulsePlayback_open(ALCpulsePlayback *self, const ALCchar *name
if(VECTOR_SIZE(PlaybackDevices) == 0)
ALCpulsePlayback_probeDevices();
-#define MATCH_NAME(iter) (al_string_cmp_cstr((iter)->name, name) == 0)
+#define MATCH_NAME(iter) (alstr_cmp_cstr((iter)->name, name) == 0)
VECTOR_FIND_IF(iter, const DevMap, PlaybackDevices, MATCH_NAME);
#undef MATCH_NAME
- if(iter == VECTOR_ITER_END(PlaybackDevices))
+ if(iter == VECTOR_END(PlaybackDevices))
return ALC_INVALID_VALUE;
- pulse_name = al_string_get_cstr(iter->device_name);
+ pulse_name = alstr_get_cstr(iter->device_name);
dev_name = iter->name;
}
@@ -894,11 +933,11 @@ static ALCenum ALCpulsePlayback_open(ALCpulsePlayback *self, const ALCchar *name
}
pa_stream_set_moved_callback(self->stream, ALCpulsePlayback_streamMovedCallback, self);
- al_string_copy_cstr(&self->device_name, pa_stream_get_device_name(self->stream));
- if(al_string_empty(dev_name))
+ alstr_copy_cstr(&self->device_name, pa_stream_get_device_name(self->stream));
+ if(alstr_empty(dev_name))
{
pa_operation *o = pa_context_get_sink_info_by_name(
- self->context, al_string_get_cstr(self->device_name),
+ self->context, alstr_get_cstr(self->device_name),
ALCpulsePlayback_sinkNameCallback, self
);
wait_for_operation(o, self->loop);
@@ -906,7 +945,7 @@ static ALCenum ALCpulsePlayback_open(ALCpulsePlayback *self, const ALCchar *name
else
{
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
- al_string_copy(&device->DeviceName, dev_name);
+ alstr_copy(&device->DeviceName, dev_name);
}
pa_threaded_mainloop_unlock(self->loop);
@@ -914,16 +953,6 @@ static ALCenum ALCpulsePlayback_open(ALCpulsePlayback *self, const ALCchar *name
return ALC_NO_ERROR;
}
-static void ALCpulsePlayback_close(ALCpulsePlayback *self)
-{
- pulse_close(self->loop, self->context, self->stream);
- self->loop = NULL;
- self->context = NULL;
- self->stream = NULL;
-
- al_string_clear(&self->device_name);
-}
-
static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
@@ -931,7 +960,6 @@ static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self)
const char *mapname = NULL;
pa_channel_map chanmap;
pa_operation *o;
- ALuint len;
pa_threaded_mainloop_lock(self->loop);
@@ -946,11 +974,11 @@ static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self)
self->stream = NULL;
}
- o = pa_context_get_sink_info_by_name(self->context, al_string_get_cstr(self->device_name),
+ o = pa_context_get_sink_info_by_name(self->context, alstr_get_cstr(self->device_name),
ALCpulsePlayback_sinkInfoCallback, self);
wait_for_operation(o, self->loop);
- if(GetConfigValueBool(al_string_get_cstr(device->DeviceName), "pulse", "fix-rate", 0) ||
+ if(GetConfigValueBool(alstr_get_cstr(device->DeviceName), "pulse", "fix-rate", 0) ||
!(device->Flags&DEVICE_FREQUENCY_REQUEST))
flags |= PA_STREAM_FIX_RATE;
flags |= PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE;
@@ -984,7 +1012,7 @@ static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self)
break;
}
self->spec.rate = device->Frequency;
- self->spec.channels = ChannelsFromDevFmt(device->FmtChans);
+ self->spec.channels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
if(pa_sample_spec_valid(&self->spec) == 0)
{
@@ -998,7 +1026,7 @@ static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self)
case DevFmtMono:
mapname = "mono";
break;
- case DevFmtBFormat3D:
+ case DevFmtAmbi3D:
device->FmtChans = DevFmtStereo;
/*fall-through*/
case DevFmtStereo:
@@ -1034,9 +1062,9 @@ static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self)
self->attr.tlength = self->attr.minreq * maxu(device->NumUpdates, 2);
self->attr.maxlength = -1;
- self->stream = ALCpulsePlayback_connectStream(al_string_get_cstr(self->device_name),
- self->loop, self->context, flags,
- &self->attr, &self->spec, &chanmap);
+ self->stream = ALCpulsePlayback_connectStream(alstr_get_cstr(self->device_name),
+ self->loop, self->context, flags, &self->attr, &self->spec, &chanmap
+ );
if(!self->stream)
{
pa_threaded_mainloop_unlock(self->loop);
@@ -1051,10 +1079,12 @@ static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self)
{
/* Server updated our playback rate, so modify the buffer attribs
* accordingly. */
- device->NumUpdates = (ALuint)((ALdouble)device->NumUpdates / device->Frequency *
- self->spec.rate + 0.5);
+ device->NumUpdates = (ALuint)clampd(
+ (ALdouble)device->NumUpdates/device->Frequency*self->spec.rate + 0.5, 2.0, 16.0
+ );
+
self->attr.minreq = device->UpdateSize * pa_frame_size(&self->spec);
- self->attr.tlength = self->attr.minreq * clampu(device->NumUpdates, 2, 16);
+ self->attr.tlength = self->attr.minreq * device->NumUpdates;
self->attr.maxlength = -1;
self->attr.prebuf = 0;
@@ -1068,10 +1098,30 @@ static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self)
pa_stream_set_buffer_attr_callback(self->stream, ALCpulsePlayback_bufferAttrCallback, self);
ALCpulsePlayback_bufferAttrCallback(self->stream, self);
- len = self->attr.minreq / pa_frame_size(&self->spec);
- device->NumUpdates = (ALuint)((ALdouble)device->NumUpdates/len*device->UpdateSize + 0.5);
- device->NumUpdates = clampu(device->NumUpdates, 2, 16);
- device->UpdateSize = len;
+ device->NumUpdates = (ALuint)clampu64(
+ (self->attr.tlength + self->attr.minreq/2) / self->attr.minreq, 2, 16
+ );
+ device->UpdateSize = self->attr.minreq / pa_frame_size(&self->spec);
+
+ /* HACK: prebuf should be 0 as that's what we set it to. However on some
+ * systems it comes back as non-0, so we have to make sure the device will
+ * write enough audio to start playback. The lack of manual start control
+ * may have unintended consequences, but it's better than not starting at
+ * all.
+ */
+ if(self->attr.prebuf != 0)
+ {
+ ALuint len = self->attr.prebuf / pa_frame_size(&self->spec);
+ if(len <= device->UpdateSize*device->NumUpdates)
+ ERR("Non-0 prebuf, %u samples (%u bytes), device has %u samples\n",
+ len, self->attr.prebuf, device->UpdateSize*device->NumUpdates);
+ else
+ {
+ ERR("Large prebuf, %u samples (%u bytes), increasing device from %u samples",
+ len, self->attr.prebuf, device->UpdateSize*device->NumUpdates);
+ device->NumUpdates = (len+device->UpdateSize-1) / device->UpdateSize;
+ }
+ }
pa_threaded_mainloop_unlock(self->loop);
return ALC_TRUE;
@@ -1079,7 +1129,7 @@ static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self)
static ALCboolean ALCpulsePlayback_start(ALCpulsePlayback *self)
{
- self->killNow = AL_FALSE;
+ ATOMIC_STORE(&self->killNow, AL_FALSE, almemory_order_release);
if(althrd_create(&self->thread, ALCpulsePlayback_mixerProc, self) != althrd_success)
return ALC_FALSE;
return ALC_TRUE;
@@ -1090,10 +1140,9 @@ static void ALCpulsePlayback_stop(ALCpulsePlayback *self)
pa_operation *o;
int res;
- if(!self->stream || self->killNow)
+ if(!self->stream || ATOMIC_EXCHANGE(&self->killNow, AL_TRUE, almemory_order_acq_rel))
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
@@ -1113,12 +1162,18 @@ static void ALCpulsePlayback_stop(ALCpulsePlayback *self)
}
-static ALint64 ALCpulsePlayback_getLatency(ALCpulsePlayback *self)
+static ClockLatency ALCpulsePlayback_getClockLatency(ALCpulsePlayback *self)
{
- pa_usec_t latency = 0;
+ ClockLatency ret;
+ pa_usec_t latency;
int neg, err;
- if((err=pa_stream_get_latency(self->stream, &latency, &neg)) != 0)
+ pa_threaded_mainloop_lock(self->loop);
+ ret.ClockTime = GetDeviceClockTime(STATIC_CAST(ALCbackend,self)->mDevice);
+ err = pa_stream_get_latency(self->stream, &latency, &neg);
+ pa_threaded_mainloop_unlock(self->loop);
+
+ if(UNLIKELY(err != 0))
{
/* FIXME: if err = -PA_ERR_NODATA, it means we were called too soon
* after starting the stream and no timing info has been received from
@@ -1126,11 +1181,14 @@ static ALint64 ALCpulsePlayback_getLatency(ALCpulsePlayback *self)
* dummy value? Either way, it shouldn't be 0. */
if(err != -PA_ERR_NODATA)
ERR("Failed to get stream latency: 0x%x\n", err);
- return 0;
+ latency = 0;
+ neg = 0;
}
+ else if(UNLIKELY(neg))
+ latency = 0;
+ ret.Latency = (ALint64)minu64(latency, U64(0x7fffffffffffffff)/1000) * 1000;
- if(neg) latency = 0;
- return (ALint64)minu64(latency, U64(0x7fffffffffffffff)/1000) * 1000;
+ return ret;
}
@@ -1180,13 +1238,12 @@ static pa_stream *ALCpulseCapture_connectStream(const char *device_name,
static void ALCpulseCapture_Construct(ALCpulseCapture *self, ALCdevice *device);
static void ALCpulseCapture_Destruct(ALCpulseCapture *self);
static ALCenum ALCpulseCapture_open(ALCpulseCapture *self, const ALCchar *name);
-static void ALCpulseCapture_close(ALCpulseCapture *self);
static DECLARE_FORWARD(ALCpulseCapture, ALCbackend, ALCboolean, reset)
static ALCboolean ALCpulseCapture_start(ALCpulseCapture *self);
static void ALCpulseCapture_stop(ALCpulseCapture *self);
static ALCenum ALCpulseCapture_captureSamples(ALCpulseCapture *self, ALCvoid *buffer, ALCuint samples);
static ALCuint ALCpulseCapture_availableSamples(ALCpulseCapture *self);
-static ALint64 ALCpulseCapture_getLatency(ALCpulseCapture *self);
+static ClockLatency ALCpulseCapture_getClockLatency(ALCpulseCapture *self);
static void ALCpulseCapture_lock(ALCpulseCapture *self);
static void ALCpulseCapture_unlock(ALCpulseCapture *self);
DECLARE_DEFAULT_ALLOCATORS(ALCpulseCapture)
@@ -1199,11 +1256,19 @@ static void ALCpulseCapture_Construct(ALCpulseCapture *self, ALCdevice *device)
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
SET_VTABLE2(ALCpulseCapture, ALCbackend, self);
+ self->loop = NULL;
AL_STRING_INIT(self->device_name);
}
static void ALCpulseCapture_Destruct(ALCpulseCapture *self)
{
+ if(self->loop)
+ {
+ pulse_close(self->loop, self->context, self->stream);
+ self->loop = NULL;
+ self->context = NULL;
+ self->stream = NULL;
+ }
AL_STRING_DEINIT(self->device_name);
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
}
@@ -1222,35 +1287,35 @@ static void ALCpulseCapture_deviceCallback(pa_context *UNUSED(context), const pa
return;
}
-#define MATCH_INFO_NAME(iter) (al_string_cmp_cstr((iter)->device_name, info->name) == 0)
+#define MATCH_INFO_NAME(iter) (alstr_cmp_cstr((iter)->device_name, info->name) == 0)
VECTOR_FIND_IF(iter, const DevMap, CaptureDevices, MATCH_INFO_NAME);
- if(iter != VECTOR_ITER_END(CaptureDevices)) return;
+ if(iter != VECTOR_END(CaptureDevices)) return;
#undef MATCH_INFO_NAME
AL_STRING_INIT(entry.name);
AL_STRING_INIT(entry.device_name);
- al_string_copy_cstr(&entry.device_name, info->name);
+ alstr_copy_cstr(&entry.device_name, info->name);
count = 0;
while(1)
{
- al_string_copy_cstr(&entry.name, info->description);
+ alstr_copy_cstr(&entry.name, info->description);
if(count != 0)
{
char str[64];
snprintf(str, sizeof(str), " #%d", count+1);
- al_string_append_cstr(&entry.name, str);
+ alstr_append_cstr(&entry.name, str);
}
-#define MATCH_ENTRY(i) (al_string_cmp(entry.name, (i)->name) == 0)
+#define MATCH_ENTRY(i) (alstr_cmp(entry.name, (i)->name) == 0)
VECTOR_FIND_IF(iter, const DevMap, CaptureDevices, MATCH_ENTRY);
- if(iter == VECTOR_ITER_END(CaptureDevices)) break;
+ if(iter == VECTOR_END(CaptureDevices)) break;
#undef MATCH_ENTRY
count++;
}
- TRACE("Got device \"%s\", \"%s\"\n", al_string_get_cstr(entry.name), al_string_get_cstr(entry.device_name));
+ TRACE("Got device \"%s\", \"%s\"\n", alstr_get_cstr(entry.name), alstr_get_cstr(entry.device_name));
VECTOR_PUSH_BACK(CaptureDevices, entry);
}
@@ -1315,7 +1380,7 @@ static void ALCpulseCapture_contextStateCallback(pa_context *context, void *pdat
if(pa_context_get_state(context) == PA_CONTEXT_FAILED)
{
ERR("Received context failure!\n");
- aluHandleDisconnect(STATIC_CAST(ALCbackend,self)->mDevice);
+ aluHandleDisconnect(STATIC_CAST(ALCbackend,self)->mDevice, "Capture state failure");
}
pa_threaded_mainloop_signal(self->loop, 0);
}
@@ -1326,7 +1391,7 @@ static void ALCpulseCapture_streamStateCallback(pa_stream *stream, void *pdata)
if(pa_stream_get_state(stream) == PA_STREAM_FAILED)
{
ERR("Received stream failure!\n");
- aluHandleDisconnect(STATIC_CAST(ALCbackend,self)->mDevice);
+ aluHandleDisconnect(STATIC_CAST(ALCbackend,self)->mDevice, "Capture stream failure");
}
pa_threaded_mainloop_signal(self->loop, 0);
}
@@ -1343,7 +1408,7 @@ static void ALCpulseCapture_sourceNameCallback(pa_context *UNUSED(context), cons
return;
}
- al_string_copy_cstr(&device->DeviceName, info->description);
+ alstr_copy_cstr(&device->DeviceName, info->description);
}
@@ -1351,9 +1416,9 @@ static void ALCpulseCapture_streamMovedCallback(pa_stream *stream, void *pdata)
{
ALCpulseCapture *self = pdata;
- al_string_copy_cstr(&self->device_name, pa_stream_get_device_name(stream));
+ alstr_copy_cstr(&self->device_name, pa_stream_get_device_name(stream));
- TRACE("Stream moved to %s\n", al_string_get_cstr(self->device_name));
+ TRACE("Stream moved to %s\n", alstr_get_cstr(self->device_name));
}
@@ -1403,6 +1468,7 @@ static ALCenum ALCpulseCapture_open(ALCpulseCapture *self, const ALCchar *name)
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
const char *pulse_name = NULL;
pa_stream_flags_t flags = 0;
+ const char *mapname = NULL;
pa_channel_map chanmap;
ALuint samples;
@@ -1413,13 +1479,13 @@ static ALCenum ALCpulseCapture_open(ALCpulseCapture *self, const ALCchar *name)
if(VECTOR_SIZE(CaptureDevices) == 0)
ALCpulseCapture_probeDevices();
-#define MATCH_NAME(iter) (al_string_cmp_cstr((iter)->name, name) == 0)
+#define MATCH_NAME(iter) (alstr_cmp_cstr((iter)->name, name) == 0)
VECTOR_FIND_IF(iter, const DevMap, CaptureDevices, MATCH_NAME);
#undef MATCH_NAME
- if(iter == VECTOR_ITER_END(CaptureDevices))
+ if(iter == VECTOR_END(CaptureDevices))
return ALC_INVALID_VALUE;
- pulse_name = al_string_get_cstr(iter->device_name);
- al_string_copy(&device->DeviceName, iter->name);
+ pulse_name = alstr_get_cstr(iter->device_name);
+ alstr_copy(&device->DeviceName, iter->name);
}
if(!pulse_open(&self->loop, &self->context, ALCpulseCapture_contextStateCallback, self))
@@ -1427,9 +1493,6 @@ static ALCenum ALCpulseCapture_open(ALCpulseCapture *self, const ALCchar *name)
pa_threaded_mainloop_lock(self->loop);
- self->spec.rate = device->Frequency;
- self->spec.channels = ChannelsFromDevFmt(device->FmtChans);
-
switch(device->FmtType)
{
case DevFmtUByte:
@@ -1452,6 +1515,44 @@ static ALCenum ALCpulseCapture_open(ALCpulseCapture *self, const ALCchar *name)
goto fail;
}
+ switch(device->FmtChans)
+ {
+ case DevFmtMono:
+ mapname = "mono";
+ break;
+ case DevFmtStereo:
+ mapname = "front-left,front-right";
+ break;
+ case DevFmtQuad:
+ mapname = "front-left,front-right,rear-left,rear-right";
+ break;
+ case DevFmtX51:
+ mapname = "front-left,front-right,front-center,lfe,side-left,side-right";
+ break;
+ case DevFmtX51Rear:
+ mapname = "front-left,front-right,front-center,lfe,rear-left,rear-right";
+ break;
+ case DevFmtX61:
+ mapname = "front-left,front-right,front-center,lfe,rear-center,side-left,side-right";
+ break;
+ case DevFmtX71:
+ mapname = "front-left,front-right,front-center,lfe,rear-left,rear-right,side-left,side-right";
+ break;
+ case DevFmtAmbi3D:
+ ERR("%s capture samples not supported\n", DevFmtChannelsString(device->FmtChans));
+ pa_threaded_mainloop_unlock(self->loop);
+ goto fail;
+ }
+ if(!pa_channel_map_parse(&chanmap, mapname))
+ {
+ ERR("Failed to build channel map for %s\n", DevFmtChannelsString(device->FmtChans));
+ pa_threaded_mainloop_unlock(self->loop);
+ return ALC_FALSE;
+ }
+
+ self->spec.rate = device->Frequency;
+ self->spec.channels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
+
if(pa_sample_spec_valid(&self->spec) == 0)
{
ERR("Invalid sample format\n");
@@ -1481,9 +1582,9 @@ static ALCenum ALCpulseCapture_open(ALCpulseCapture *self, const ALCchar *name)
flags |= PA_STREAM_DONT_MOVE;
TRACE("Connecting to \"%s\"\n", pulse_name ? pulse_name : "(default)");
- self->stream = ALCpulseCapture_connectStream(pulse_name, self->loop, self->context,
- flags, &self->attr, &self->spec,
- &chanmap);
+ self->stream = ALCpulseCapture_connectStream(pulse_name,
+ self->loop, self->context, flags, &self->attr, &self->spec, &chanmap
+ );
if(!self->stream)
{
pa_threaded_mainloop_unlock(self->loop);
@@ -1492,11 +1593,11 @@ static ALCenum ALCpulseCapture_open(ALCpulseCapture *self, const ALCchar *name)
pa_stream_set_moved_callback(self->stream, ALCpulseCapture_streamMovedCallback, self);
pa_stream_set_state_callback(self->stream, ALCpulseCapture_streamStateCallback, self);
- al_string_copy_cstr(&self->device_name, pa_stream_get_device_name(self->stream));
- if(al_string_empty(device->DeviceName))
+ alstr_copy_cstr(&self->device_name, pa_stream_get_device_name(self->stream));
+ if(alstr_empty(device->DeviceName))
{
pa_operation *o = pa_context_get_source_info_by_name(
- self->context, al_string_get_cstr(self->device_name),
+ self->context, alstr_get_cstr(self->device_name),
ALCpulseCapture_sourceNameCallback, self
);
wait_for_operation(o, self->loop);
@@ -1514,30 +1615,23 @@ fail:
return ALC_INVALID_VALUE;
}
-static void ALCpulseCapture_close(ALCpulseCapture *self)
-{
- pulse_close(self->loop, self->context, self->stream);
- self->loop = NULL;
- self->context = NULL;
- self->stream = NULL;
-
- al_string_clear(&self->device_name);
-}
-
static ALCboolean ALCpulseCapture_start(ALCpulseCapture *self)
{
pa_operation *o;
+ pa_threaded_mainloop_lock(self->loop);
o = pa_stream_cork(self->stream, 0, stream_success_callback, self->loop);
wait_for_operation(o, self->loop);
-
+ pa_threaded_mainloop_unlock(self->loop);
return ALC_TRUE;
}
static void ALCpulseCapture_stop(ALCpulseCapture *self)
{
pa_operation *o;
+ pa_threaded_mainloop_lock(self->loop);
o = pa_stream_cork(self->stream, 1, stream_success_callback, self->loop);
wait_for_operation(o, self->loop);
+ pa_threaded_mainloop_unlock(self->loop);
}
static ALCenum ALCpulseCapture_captureSamples(ALCpulseCapture *self, ALCvoid *buffer, ALCuint samples)
@@ -1548,6 +1642,7 @@ static ALCenum ALCpulseCapture_captureSamples(ALCpulseCapture *self, ALCvoid *bu
/* Capture is done in fragment-sized chunks, so we loop until we get all
* that's available */
self->last_readable -= todo;
+ pa_threaded_mainloop_lock(self->loop);
while(todo > 0)
{
size_t rem = todo;
@@ -1559,14 +1654,15 @@ static ALCenum ALCpulseCapture_captureSamples(ALCpulseCapture *self, ALCvoid *bu
state = pa_stream_get_state(self->stream);
if(!PA_STREAM_IS_GOOD(state))
{
- aluHandleDisconnect(device);
+ aluHandleDisconnect(device, "Bad capture state: %u", state);
break;
}
if(pa_stream_peek(self->stream, &self->cap_store, &self->cap_len) < 0)
{
ERR("pa_stream_peek() failed: %s\n",
pa_strerror(pa_context_errno(self->context)));
- aluHandleDisconnect(device);
+ aluHandleDisconnect(device, "Failed retrieving capture samples: %s",
+ pa_strerror(pa_context_errno(self->context)));
break;
}
self->cap_remain = self->cap_len;
@@ -1587,6 +1683,7 @@ static ALCenum ALCpulseCapture_captureSamples(ALCpulseCapture *self, ALCvoid *bu
self->cap_len = 0;
}
}
+ pa_threaded_mainloop_unlock(self->loop);
if(todo > 0)
memset(buffer, ((device->FmtType==DevFmtUByte) ? 0x80 : 0), todo);
@@ -1598,16 +1695,19 @@ static ALCuint ALCpulseCapture_availableSamples(ALCpulseCapture *self)
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
size_t readable = self->cap_remain;
- if(device->Connected)
+ if(ATOMIC_LOAD(&device->Connected, almemory_order_acquire))
{
- ssize_t got = pa_stream_readable_size(self->stream);
+ ssize_t got;
+ pa_threaded_mainloop_lock(self->loop);
+ got = pa_stream_readable_size(self->stream);
if(got < 0)
{
ERR("pa_stream_readable_size() failed: %s\n", pa_strerror(got));
- aluHandleDisconnect(device);
+ aluHandleDisconnect(device, "Failed getting readable size: %s", pa_strerror(got));
}
else if((size_t)got > self->cap_len)
readable += got - self->cap_len;
+ pa_threaded_mainloop_unlock(self->loop);
}
if(self->last_readable < readable)
@@ -1616,19 +1716,28 @@ static ALCuint ALCpulseCapture_availableSamples(ALCpulseCapture *self)
}
-static ALint64 ALCpulseCapture_getLatency(ALCpulseCapture *self)
+static ClockLatency ALCpulseCapture_getClockLatency(ALCpulseCapture *self)
{
- pa_usec_t latency = 0;
- int neg;
+ ClockLatency ret;
+ pa_usec_t latency;
+ int neg, err;
+
+ pa_threaded_mainloop_lock(self->loop);
+ ret.ClockTime = GetDeviceClockTime(STATIC_CAST(ALCbackend,self)->mDevice);
+ err = pa_stream_get_latency(self->stream, &latency, &neg);
+ pa_threaded_mainloop_unlock(self->loop);
- if(pa_stream_get_latency(self->stream, &latency, &neg) != 0)
+ if(UNLIKELY(err != 0))
{
- ERR("Failed to get stream latency!\n");
- return 0;
+ ERR("Failed to get stream latency: 0x%x\n", err);
+ latency = 0;
+ neg = 0;
}
+ else if(UNLIKELY(neg))
+ latency = 0;
+ ret.Latency = (ALint64)minu64(latency, U64(0x7fffffffffffffff)/1000) * 1000;
- if(neg) latency = 0;
- return (ALint64)minu64(latency, U64(0x7fffffffffffffff)/1000) * 1000;
+ return ret;
}
@@ -1651,9 +1760,8 @@ typedef struct ALCpulseBackendFactory {
static ALCboolean ALCpulseBackendFactory_init(ALCpulseBackendFactory *self);
static void ALCpulseBackendFactory_deinit(ALCpulseBackendFactory *self);
static ALCboolean ALCpulseBackendFactory_querySupport(ALCpulseBackendFactory *self, ALCbackend_Type type);
-static void ALCpulseBackendFactory_probe(ALCpulseBackendFactory *self, enum DevProbe type);
+static void ALCpulseBackendFactory_probe(ALCpulseBackendFactory *self, enum DevProbe type, al_string *outnames);
static ALCbackend* ALCpulseBackendFactory_createBackend(ALCpulseBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
-
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCpulseBackendFactory);
@@ -1726,23 +1834,25 @@ static ALCboolean ALCpulseBackendFactory_querySupport(ALCpulseBackendFactory* UN
return ALC_FALSE;
}
-static void ALCpulseBackendFactory_probe(ALCpulseBackendFactory* UNUSED(self), enum DevProbe type)
+static void ALCpulseBackendFactory_probe(ALCpulseBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
{
switch(type)
{
+#define APPEND_OUTNAME(e) do { \
+ if(!alstr_empty((e)->name)) \
+ alstr_append_range(outnames, VECTOR_BEGIN((e)->name), \
+ VECTOR_END((e)->name)+1); \
+} while(0)
case ALL_DEVICE_PROBE:
ALCpulsePlayback_probeDevices();
-#define APPEND_ALL_DEVICES_LIST(e) AppendAllDevicesList(al_string_get_cstr((e)->name))
- VECTOR_FOR_EACH(const DevMap, PlaybackDevices, APPEND_ALL_DEVICES_LIST);
-#undef APPEND_ALL_DEVICES_LIST
+ VECTOR_FOR_EACH(const DevMap, PlaybackDevices, APPEND_OUTNAME);
break;
case CAPTURE_DEVICE_PROBE:
ALCpulseCapture_probeDevices();
-#define APPEND_CAPTURE_DEVICE_LIST(e) AppendCaptureDeviceList(al_string_get_cstr((e)->name))
- VECTOR_FOR_EACH(const DevMap, CaptureDevices, APPEND_CAPTURE_DEVICE_LIST);
-#undef APPEND_CAPTURE_DEVICE_LIST
+ VECTOR_FOR_EACH(const DevMap, CaptureDevices, APPEND_OUTNAME);
break;
+#undef APPEND_OUTNAME
}
}
@@ -1790,7 +1900,7 @@ static ALCboolean ALCpulseBackendFactory_querySupport(ALCpulseBackendFactory* UN
return ALC_FALSE;
}
-static void ALCpulseBackendFactory_probe(ALCpulseBackendFactory* UNUSED(self), enum DevProbe UNUSED(type))
+static void ALCpulseBackendFactory_probe(ALCpulseBackendFactory* UNUSED(self), enum DevProbe UNUSED(type), al_string* UNUSED(outnames))
{
}
diff --git a/Alc/backends/qsa.c b/Alc/backends/qsa.c
index 291e49fc..81645096 100644
--- a/Alc/backends/qsa.c
+++ b/Alc/backends/qsa.c
@@ -33,6 +33,8 @@
#include "alu.h"
#include "threads.h"
+#include "backends/base.h"
+
typedef struct {
snd_pcm_t* pcmHandle;
@@ -44,7 +46,7 @@ typedef struct {
ALvoid* buffer;
ALsizei size;
- volatile int killNow;
+ ATOMIC(ALenum) killNow;
althrd_t thread;
} qsa_data;
@@ -117,8 +119,10 @@ static void deviceList(int type, vector_DevMap *devmap)
if(max_cards < 0)
return;
- VECTOR_RESERVE(*devmap, max_cards+1);
- VECTOR_RESIZE(*devmap, 0);
+#define FREE_NAME(iter) free((iter)->name)
+ VECTOR_FOR_EACH(DevMap, *devmap, FREE_NAME);
+#undef FREE_NAME
+ VECTOR_RESIZE(*devmap, 0, max_cards+1);
entry.name = strdup(qsaDevice);
entry.card = 0;
@@ -158,17 +162,39 @@ static void deviceList(int type, vector_DevMap *devmap)
}
-FORCE_ALIGN static int qsa_proc_playback(void* ptr)
+/* Wrappers to use an old-style backend with the new interface. */
+typedef struct PlaybackWrapper {
+ DERIVE_FROM_TYPE(ALCbackend);
+ qsa_data *ExtraData;
+} PlaybackWrapper;
+
+static void PlaybackWrapper_Construct(PlaybackWrapper *self, ALCdevice *device);
+static void PlaybackWrapper_Destruct(PlaybackWrapper *self);
+static ALCenum PlaybackWrapper_open(PlaybackWrapper *self, const ALCchar *name);
+static ALCboolean PlaybackWrapper_reset(PlaybackWrapper *self);
+static ALCboolean PlaybackWrapper_start(PlaybackWrapper *self);
+static void PlaybackWrapper_stop(PlaybackWrapper *self);
+static DECLARE_FORWARD2(PlaybackWrapper, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
+static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, ALCuint, availableSamples)
+static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, ClockLatency, getClockLatency)
+static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, void, lock)
+static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, void, unlock)
+DECLARE_DEFAULT_ALLOCATORS(PlaybackWrapper)
+DEFINE_ALCBACKEND_VTABLE(PlaybackWrapper);
+
+
+FORCE_ALIGN static int qsa_proc_playback(void *ptr)
{
- ALCdevice* device=(ALCdevice*)ptr;
- qsa_data* data=(qsa_data*)device->ExtraData;
- char* write_ptr;
- int avail;
+ PlaybackWrapper *self = ptr;
+ ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
+ qsa_data *data = self->ExtraData;
snd_pcm_channel_status_t status;
struct sched_param param;
- fd_set wfds;
- int selectret;
struct timeval timeout;
+ char* write_ptr;
+ fd_set wfds;
+ ALint len;
+ int sret;
SetRTPriority();
althrd_setname(althrd_current(), MIXER_THREAD_NAME);
@@ -178,72 +204,69 @@ FORCE_ALIGN static int qsa_proc_playback(void* ptr)
param.sched_priority=param.sched_curpriority+1;
SchedSet(0, 0, SCHED_NOCHANGE, &param);
- ALint frame_size=FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
+ const ALint frame_size = FrameSizeFromDevFmt(
+ device->FmtChans, device->FmtType, device->AmbiOrder
+ );
- while (!data->killNow)
+ V0(device->Backend,lock)();
+ while(!ATOMIC_LOAD(&data->killNow, almemory_order_acquire))
{
- ALint len=data->size;
- write_ptr=data->buffer;
-
- avail=len/frame_size;
- aluMixData(device, write_ptr, avail);
+ FD_ZERO(&wfds);
+ FD_SET(data->audio_fd, &wfds);
+ timeout.tv_sec=2;
+ timeout.tv_usec=0;
- while (len>0 && !data->killNow)
+ /* Select also works like time slice to OS */
+ V0(device->Backend,unlock)();
+ sret = select(data->audio_fd+1, NULL, &wfds, NULL, &timeout);
+ V0(device->Backend,lock)();
+ if(sret == -1)
{
- FD_ZERO(&wfds);
- FD_SET(data->audio_fd, &wfds);
- timeout.tv_sec=2;
- timeout.tv_usec=0;
-
- /* Select also works like time slice to OS */
- selectret=select(data->audio_fd+1, NULL, &wfds, NULL, &timeout);
- switch (selectret)
- {
- case -1:
- aluHandleDisconnect(device);
- return 1;
- case 0:
- break;
- default:
- if (FD_ISSET(data->audio_fd, &wfds))
- {
- break;
- }
- break;
- }
-
- int wrote=snd_pcm_plugin_write(data->pcmHandle, write_ptr, len);
+ ERR("select error: %s\n", strerror(errno));
+ aluHandleDisconnect(device, "Failed waiting for playback buffer: %s", strerror(errno));
+ break;
+ }
+ if(sret == 0)
+ {
+ ERR("select timeout\n");
+ continue;
+ }
- if (wrote<=0)
+ len = data->size;
+ write_ptr = data->buffer;
+ aluMixData(device, write_ptr, len/frame_size);
+ while(len>0 && !ATOMIC_LOAD(&data->killNow, almemory_order_acquire))
+ {
+ int wrote = snd_pcm_plugin_write(data->pcmHandle, write_ptr, len);
+ if(wrote <= 0)
{
- if ((errno==EAGAIN) || (errno==EWOULDBLOCK))
- {
+ if(errno==EAGAIN || errno==EWOULDBLOCK)
continue;
- }
- memset(&status, 0, sizeof (status));
- status.channel=SND_PCM_CHANNEL_PLAYBACK;
+ memset(&status, 0, sizeof(status));
+ status.channel = SND_PCM_CHANNEL_PLAYBACK;
snd_pcm_plugin_status(data->pcmHandle, &status);
/* we need to reinitialize the sound channel if we've underrun the buffer */
- if ((status.status==SND_PCM_STATUS_UNDERRUN) ||
- (status.status==SND_PCM_STATUS_READY))
+ if(status.status == SND_PCM_STATUS_UNDERRUN ||
+ status.status == SND_PCM_STATUS_READY)
{
- if ((snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_PLAYBACK))<0)
+ if(snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_PLAYBACK) < 0)
{
- aluHandleDisconnect(device);
+ aluHandleDisconnect(device, "Playback recovery failed");
break;
}
}
}
else
{
- write_ptr+=wrote;
- len-=wrote;
+ write_ptr += wrote;
+ len -= wrote;
}
}
}
+ V0(device->Backend,unlock)();
return 0;
}
@@ -252,8 +275,9 @@ FORCE_ALIGN static int qsa_proc_playback(void* ptr)
/* Playback */
/************/
-static ALCenum qsa_open_playback(ALCdevice* device, const ALCchar* deviceName)
+static ALCenum qsa_open_playback(PlaybackWrapper *self, const ALCchar* deviceName)
{
+ ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
qsa_data *data;
int card, dev;
int status;
@@ -261,6 +285,7 @@ static ALCenum qsa_open_playback(ALCdevice* device, const ALCchar* deviceName)
data = (qsa_data*)calloc(1, sizeof(qsa_data));
if(data == NULL)
return ALC_OUT_OF_MEMORY;
+ ATOMIC_INIT(&data->killNow, AL_TRUE);
if(!deviceName)
deviceName = qsaDevice;
@@ -277,7 +302,7 @@ static ALCenum qsa_open_playback(ALCdevice* device, const ALCchar* deviceName)
#define MATCH_DEVNAME(iter) ((iter)->name && strcmp(deviceName, (iter)->name)==0)
VECTOR_FIND_IF(iter, const DevMap, DeviceNameMap, MATCH_DEVNAME);
#undef MATCH_DEVNAME
- if(iter == VECTOR_ITER_END(DeviceNameMap))
+ if(iter == VECTOR_END(DeviceNameMap))
{
free(data);
return ALC_INVALID_DEVICE;
@@ -300,15 +325,15 @@ static ALCenum qsa_open_playback(ALCdevice* device, const ALCchar* deviceName)
return ALC_INVALID_DEVICE;
}
- al_string_copy_cstr(&device->DeviceName, deviceName);
- device->ExtraData = data;
+ alstr_copy_cstr(&device->DeviceName, deviceName);
+ self->ExtraData = data;
return ALC_NO_ERROR;
}
-static void qsa_close_playback(ALCdevice* device)
+static void qsa_close_playback(PlaybackWrapper *self)
{
- qsa_data* data=(qsa_data*)device->ExtraData;
+ qsa_data *data = self->ExtraData;
if (data->buffer!=NULL)
{
@@ -319,12 +344,13 @@ static void qsa_close_playback(ALCdevice* device)
snd_pcm_close(data->pcmHandle);
free(data);
- device->ExtraData=NULL;
+ self->ExtraData = NULL;
}
-static ALCboolean qsa_reset_playback(ALCdevice* device)
+static ALCboolean qsa_reset_playback(PlaybackWrapper *self)
{
- qsa_data* data=(qsa_data*)device->ExtraData;
+ ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
+ qsa_data *data = self->ExtraData;
int32_t format=-1;
switch(device->FmtType)
@@ -365,14 +391,14 @@ static ALCboolean qsa_reset_playback(ALCdevice* device)
data->cparams.start_mode=SND_PCM_START_FULL;
data->cparams.stop_mode=SND_PCM_STOP_STOP;
- data->cparams.buf.block.frag_size=device->UpdateSize*
- ChannelsFromDevFmt(device->FmtChans)*BytesFromDevFmt(device->FmtType);
+ data->cparams.buf.block.frag_size=device->UpdateSize *
+ FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
data->cparams.buf.block.frags_max=device->NumUpdates;
data->cparams.buf.block.frags_min=device->NumUpdates;
data->cparams.format.interleave=1;
data->cparams.format.rate=device->Frequency;
- data->cparams.format.voices=ChannelsFromDevFmt(device->FmtChans);
+ data->cparams.format.voices=ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
data->cparams.format.format=format;
if ((snd_pcm_plugin_params(data->pcmHandle, &data->cparams))<0)
@@ -556,7 +582,7 @@ static ALCboolean qsa_reset_playback(ALCdevice* device)
SetDefaultChannelOrder(device);
device->UpdateSize=data->csetup.buf.block.frag_size/
- (ChannelsFromDevFmt(device->FmtChans)*BytesFromDevFmt(device->FmtType));
+ FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
device->NumUpdates=data->csetup.buf.block.frags;
data->size=data->csetup.buf.block.frag_size;
@@ -569,35 +595,93 @@ static ALCboolean qsa_reset_playback(ALCdevice* device)
return ALC_TRUE;
}
-static ALCboolean qsa_start_playback(ALCdevice* device)
+static ALCboolean qsa_start_playback(PlaybackWrapper *self)
{
- qsa_data *data = (qsa_data*)device->ExtraData;
+ qsa_data *data = self->ExtraData;
- data->killNow = 0;
- if(althrd_create(&data->thread, qsa_proc_playback, device) != althrd_success)
+ ATOMIC_STORE(&data->killNow, AL_FALSE, almemory_order_release);
+ if(althrd_create(&data->thread, qsa_proc_playback, self) != althrd_success)
return ALC_FALSE;
return ALC_TRUE;
}
-static void qsa_stop_playback(ALCdevice* device)
+static void qsa_stop_playback(PlaybackWrapper *self)
{
- qsa_data *data = (qsa_data*)device->ExtraData;
+ qsa_data *data = self->ExtraData;
int res;
- if(data->killNow)
+ if(ATOMIC_EXCHANGE(&data->killNow, AL_TRUE, almemory_order_acq_rel))
return;
-
- data->killNow = 1;
althrd_join(data->thread, &res);
}
+
+static void PlaybackWrapper_Construct(PlaybackWrapper *self, ALCdevice *device)
+{
+ ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
+ SET_VTABLE2(PlaybackWrapper, ALCbackend, self);
+
+ self->ExtraData = NULL;
+}
+
+static void PlaybackWrapper_Destruct(PlaybackWrapper *self)
+{
+ if(self->ExtraData)
+ qsa_close_playback(self);
+
+ ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
+}
+
+static ALCenum PlaybackWrapper_open(PlaybackWrapper *self, const ALCchar *name)
+{
+ return qsa_open_playback(self, name);
+}
+
+static ALCboolean PlaybackWrapper_reset(PlaybackWrapper *self)
+{
+ return qsa_reset_playback(self);
+}
+
+static ALCboolean PlaybackWrapper_start(PlaybackWrapper *self)
+{
+ return qsa_start_playback(self);
+}
+
+static void PlaybackWrapper_stop(PlaybackWrapper *self)
+{
+ qsa_stop_playback(self);
+}
+
+
+
/***********/
/* Capture */
/***********/
-static ALCenum qsa_open_capture(ALCdevice* device, const ALCchar* deviceName)
+typedef struct CaptureWrapper {
+ DERIVE_FROM_TYPE(ALCbackend);
+ qsa_data *ExtraData;
+} CaptureWrapper;
+
+static void CaptureWrapper_Construct(CaptureWrapper *self, ALCdevice *device);
+static void CaptureWrapper_Destruct(CaptureWrapper *self);
+static ALCenum CaptureWrapper_open(CaptureWrapper *self, const ALCchar *name);
+static DECLARE_FORWARD(CaptureWrapper, ALCbackend, ALCboolean, reset)
+static ALCboolean CaptureWrapper_start(CaptureWrapper *self);
+static void CaptureWrapper_stop(CaptureWrapper *self);
+static ALCenum CaptureWrapper_captureSamples(CaptureWrapper *self, void *buffer, ALCuint samples);
+static ALCuint CaptureWrapper_availableSamples(CaptureWrapper *self);
+static DECLARE_FORWARD(CaptureWrapper, ALCbackend, ClockLatency, getClockLatency)
+static DECLARE_FORWARD(CaptureWrapper, ALCbackend, void, lock)
+static DECLARE_FORWARD(CaptureWrapper, ALCbackend, void, unlock)
+DECLARE_DEFAULT_ALLOCATORS(CaptureWrapper)
+DEFINE_ALCBACKEND_VTABLE(CaptureWrapper);
+
+
+static ALCenum qsa_open_capture(CaptureWrapper *self, const ALCchar *deviceName)
{
+ ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
qsa_data *data;
int card, dev;
int format=-1;
@@ -624,7 +708,7 @@ static ALCenum qsa_open_capture(ALCdevice* device, const ALCchar* deviceName)
#define MATCH_DEVNAME(iter) ((iter)->name && strcmp(deviceName, (iter)->name)==0)
VECTOR_FIND_IF(iter, const DevMap, CaptureNameMap, MATCH_DEVNAME);
#undef MATCH_DEVNAME
- if(iter == VECTOR_ITER_END(CaptureNameMap))
+ if(iter == VECTOR_END(CaptureNameMap))
{
free(data);
return ALC_INVALID_DEVICE;
@@ -647,8 +731,8 @@ static ALCenum qsa_open_capture(ALCdevice* device, const ALCchar* deviceName)
return ALC_INVALID_DEVICE;
}
- al_string_copy_cstr(&device->DeviceName, deviceName);
- device->ExtraData = data;
+ alstr_copy_cstr(&device->DeviceName, deviceName);
+ self->ExtraData = data;
switch (device->FmtType)
{
@@ -688,20 +772,19 @@ static ALCenum qsa_open_capture(ALCdevice* device, const ALCchar* deviceName)
data->cparams.stop_mode=SND_PCM_STOP_STOP;
data->cparams.buf.block.frag_size=device->UpdateSize*
- ChannelsFromDevFmt(device->FmtChans)*BytesFromDevFmt(device->FmtType);
+ FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
data->cparams.buf.block.frags_max=device->NumUpdates;
data->cparams.buf.block.frags_min=device->NumUpdates;
data->cparams.format.interleave=1;
data->cparams.format.rate=device->Frequency;
- data->cparams.format.voices=ChannelsFromDevFmt(device->FmtChans);
+ data->cparams.format.voices=ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
data->cparams.format.format=format;
if(snd_pcm_plugin_params(data->pcmHandle, &data->cparams) < 0)
{
snd_pcm_close(data->pcmHandle);
free(data);
- device->ExtraData=NULL;
return ALC_INVALID_VALUE;
}
@@ -709,20 +792,20 @@ static ALCenum qsa_open_capture(ALCdevice* device, const ALCchar* deviceName)
return ALC_NO_ERROR;
}
-static void qsa_close_capture(ALCdevice* device)
+static void qsa_close_capture(CaptureWrapper *self)
{
- qsa_data* data=(qsa_data*)device->ExtraData;
+ qsa_data *data = self->ExtraData;
if (data->pcmHandle!=NULL)
snd_pcm_close(data->pcmHandle);
free(data);
- device->ExtraData=NULL;
+ self->ExtraData = NULL;
}
-static void qsa_start_capture(ALCdevice* device)
+static void qsa_start_capture(CaptureWrapper *self)
{
- qsa_data* data=(qsa_data*)device->ExtraData;
+ qsa_data *data = self->ExtraData;
int rstatus;
if ((rstatus=snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_CAPTURE))<0)
@@ -742,18 +825,18 @@ static void qsa_start_capture(ALCdevice* device)
snd_pcm_capture_go(data->pcmHandle);
}
-static void qsa_stop_capture(ALCdevice* device)
+static void qsa_stop_capture(CaptureWrapper *self)
{
- qsa_data* data=(qsa_data*)device->ExtraData;
-
+ qsa_data *data = self->ExtraData;
snd_pcm_capture_flush(data->pcmHandle);
}
-static ALCuint qsa_available_samples(ALCdevice* device)
+static ALCuint qsa_available_samples(CaptureWrapper *self)
{
- qsa_data* data=(qsa_data*)device->ExtraData;
+ ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
+ qsa_data *data = self->ExtraData;
snd_pcm_channel_status_t status;
- ALint frame_size=FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
+ ALint frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
ALint free_size;
int rstatus;
@@ -766,7 +849,7 @@ static ALCuint qsa_available_samples(ALCdevice* device)
if ((rstatus=snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_CAPTURE))<0)
{
ERR("capture prepare failed: %s\n", snd_strerror(rstatus));
- aluHandleDisconnect(device);
+ aluHandleDisconnect(device, "Failed capture recovery: %s", snd_strerror(rstatus));
return 0;
}
@@ -780,16 +863,17 @@ static ALCuint qsa_available_samples(ALCdevice* device)
return free_size/frame_size;
}
-static ALCenum qsa_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint samples)
+static ALCenum qsa_capture_samples(CaptureWrapper *self, ALCvoid *buffer, ALCuint samples)
{
- qsa_data* data=(qsa_data*)device->ExtraData;
+ ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
+ qsa_data *data = self->ExtraData;
char* read_ptr;
snd_pcm_channel_status_t status;
fd_set rfds;
int selectret;
struct timeval timeout;
int bytes_read;
- ALint frame_size=FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
+ ALint frame_size=FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
ALint len=samples*frame_size;
int rstatus;
@@ -808,7 +892,7 @@ static ALCenum qsa_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint s
switch (selectret)
{
case -1:
- aluHandleDisconnect(device);
+ aluHandleDisconnect(device, "Failed to check capture samples");
return ALC_INVALID_DEVICE;
case 0:
break;
@@ -839,7 +923,8 @@ static ALCenum qsa_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint s
if ((rstatus=snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_CAPTURE))<0)
{
ERR("capture prepare failed: %s\n", snd_strerror(rstatus));
- aluHandleDisconnect(device);
+ aluHandleDisconnect(device, "Failed capture recovery: %s",
+ snd_strerror(rstatus));
return ALC_INVALID_DEVICE;
}
snd_pcm_capture_go(data->pcmHandle);
@@ -855,27 +940,68 @@ static ALCenum qsa_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint s
return ALC_NO_ERROR;
}
-static const BackendFuncs qsa_funcs= {
- qsa_open_playback,
- qsa_close_playback,
- qsa_reset_playback,
- qsa_start_playback,
- qsa_stop_playback,
- qsa_open_capture,
- qsa_close_capture,
- qsa_start_capture,
- qsa_stop_capture,
- qsa_capture_samples,
- qsa_available_samples
-};
-ALCboolean alc_qsa_init(BackendFuncs* func_list)
+static void CaptureWrapper_Construct(CaptureWrapper *self, ALCdevice *device)
+{
+ ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
+ SET_VTABLE2(CaptureWrapper, ALCbackend, self);
+
+ self->ExtraData = NULL;
+}
+
+static void CaptureWrapper_Destruct(CaptureWrapper *self)
+{
+ if(self->ExtraData)
+ qsa_close_capture(self);
+
+ ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
+}
+
+static ALCenum CaptureWrapper_open(CaptureWrapper *self, const ALCchar *name)
+{
+ return qsa_open_capture(self, name);
+}
+
+static ALCboolean CaptureWrapper_start(CaptureWrapper *self)
{
- *func_list = qsa_funcs;
+ qsa_start_capture(self);
return ALC_TRUE;
}
-void alc_qsa_deinit(void)
+static void CaptureWrapper_stop(CaptureWrapper *self)
+{
+ qsa_stop_capture(self);
+}
+
+static ALCenum CaptureWrapper_captureSamples(CaptureWrapper *self, void *buffer, ALCuint samples)
+{
+ return qsa_capture_samples(self, buffer, samples);
+}
+
+static ALCuint CaptureWrapper_availableSamples(CaptureWrapper *self)
+{
+ return qsa_available_samples(self);
+}
+
+
+typedef struct ALCqsaBackendFactory {
+ DERIVE_FROM_TYPE(ALCbackendFactory);
+} ALCqsaBackendFactory;
+#define ALCQSABACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCqsaBackendFactory, ALCbackendFactory) } }
+
+static ALCboolean ALCqsaBackendFactory_init(ALCqsaBackendFactory* UNUSED(self));
+static void ALCqsaBackendFactory_deinit(ALCqsaBackendFactory* UNUSED(self));
+static ALCboolean ALCqsaBackendFactory_querySupport(ALCqsaBackendFactory* UNUSED(self), ALCbackend_Type type);
+static void ALCqsaBackendFactory_probe(ALCqsaBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames);
+static ALCbackend* ALCqsaBackendFactory_createBackend(ALCqsaBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type);
+DEFINE_ALCBACKENDFACTORY_VTABLE(ALCqsaBackendFactory);
+
+static ALCboolean ALCqsaBackendFactory_init(ALCqsaBackendFactory* UNUSED(self))
+{
+ return ALC_TRUE;
+}
+
+static void ALCqsaBackendFactory_deinit(ALCqsaBackendFactory* UNUSED(self))
{
#define FREE_NAME(iter) free((iter)->name)
VECTOR_FOR_EACH(DevMap, DeviceNameMap, FREE_NAME);
@@ -886,32 +1012,57 @@ void alc_qsa_deinit(void)
#undef FREE_NAME
}
-void alc_qsa_probe(enum DevProbe type)
+static ALCboolean ALCqsaBackendFactory_querySupport(ALCqsaBackendFactory* UNUSED(self), ALCbackend_Type type)
+{
+ if(type == ALCbackend_Playback || type == ALCbackend_Capture)
+ return ALC_TRUE;
+ return ALC_FALSE;
+}
+
+static void ALCqsaBackendFactory_probe(ALCqsaBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
{
switch (type)
{
+#define APPEND_OUTNAME(e) do { \
+ const char *n_ = (e)->name; \
+ if(n_ && n_[0]) \
+ alstr_append_range(outnames, n_, n_+strlen(n_)+1); \
+} while(0)
case ALL_DEVICE_PROBE:
-#define FREE_NAME(iter) free((iter)->name)
- VECTOR_FOR_EACH(DevMap, DeviceNameMap, FREE_NAME);
-#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
+ VECTOR_FOR_EACH(const DevMap, DeviceNameMap, APPEND_OUTNAME);
break;
case CAPTURE_DEVICE_PROBE:
-#define FREE_NAME(iter) free((iter)->name)
- VECTOR_FOR_EACH(DevMap, CaptureNameMap, FREE_NAME);
-#undef FREE_NAME
- VECTOR_RESIZE(CaptureNameMap, 0);
-
deviceList(SND_PCM_CHANNEL_CAPTURE, &CaptureNameMap);
-#define APPEND_DEVICE(iter) AppendCaptureDeviceList((iter)->name)
- VECTOR_FOR_EACH(const DevMap, CaptureNameMap, APPEND_DEVICE);
-#undef APPEND_DEVICE
+ VECTOR_FOR_EACH(const DevMap, CaptureNameMap, APPEND_OUTNAME);
break;
+#undef APPEND_OUTNAME
+ }
+}
+
+static ALCbackend* ALCqsaBackendFactory_createBackend(ALCqsaBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
+{
+ if(type == ALCbackend_Playback)
+ {
+ PlaybackWrapper *backend;
+ NEW_OBJ(backend, PlaybackWrapper)(device);
+ if(!backend) return NULL;
+ return STATIC_CAST(ALCbackend, backend);
}
+ if(type == ALCbackend_Capture)
+ {
+ CaptureWrapper *backend;
+ NEW_OBJ(backend, CaptureWrapper)(device);
+ if(!backend) return NULL;
+ return STATIC_CAST(ALCbackend, backend);
+ }
+
+ return NULL;
+}
+
+ALCbackendFactory *ALCqsaBackendFactory_getFactory(void)
+{
+ static ALCqsaBackendFactory factory = ALCQSABACKENDFACTORY_INITIALIZER;
+ return STATIC_CAST(ALCbackendFactory, &factory);
}
diff --git a/Alc/backends/sdl2.c b/Alc/backends/sdl2.c
new file mode 100644
index 00000000..3495e6bf
--- /dev/null
+++ b/Alc/backends/sdl2.c
@@ -0,0 +1,288 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 2018 by authors.
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <SDL2/SDL.h>
+
+#include "alMain.h"
+#include "alu.h"
+#include "threads.h"
+#include "compat.h"
+
+#include "backends/base.h"
+
+
+#ifdef _WIN32
+#define DEVNAME_PREFIX "OpenAL Soft on "
+#else
+#define DEVNAME_PREFIX ""
+#endif
+
+typedef struct ALCsdl2Backend {
+ DERIVE_FROM_TYPE(ALCbackend);
+
+ SDL_AudioDeviceID deviceID;
+ ALsizei frameSize;
+
+ ALuint Frequency;
+ enum DevFmtChannels FmtChans;
+ enum DevFmtType FmtType;
+ ALuint UpdateSize;
+} ALCsdl2Backend;
+
+static void ALCsdl2Backend_Construct(ALCsdl2Backend *self, ALCdevice *device);
+static void ALCsdl2Backend_Destruct(ALCsdl2Backend *self);
+static ALCenum ALCsdl2Backend_open(ALCsdl2Backend *self, const ALCchar *name);
+static ALCboolean ALCsdl2Backend_reset(ALCsdl2Backend *self);
+static ALCboolean ALCsdl2Backend_start(ALCsdl2Backend *self);
+static void ALCsdl2Backend_stop(ALCsdl2Backend *self);
+static DECLARE_FORWARD2(ALCsdl2Backend, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
+static DECLARE_FORWARD(ALCsdl2Backend, ALCbackend, ALCuint, availableSamples)
+static DECLARE_FORWARD(ALCsdl2Backend, ALCbackend, ClockLatency, getClockLatency)
+static void ALCsdl2Backend_lock(ALCsdl2Backend *self);
+static void ALCsdl2Backend_unlock(ALCsdl2Backend *self);
+DECLARE_DEFAULT_ALLOCATORS(ALCsdl2Backend)
+
+DEFINE_ALCBACKEND_VTABLE(ALCsdl2Backend);
+
+static const ALCchar defaultDeviceName[] = DEVNAME_PREFIX "Default Device";
+
+static void ALCsdl2Backend_Construct(ALCsdl2Backend *self, ALCdevice *device)
+{
+ ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
+ SET_VTABLE2(ALCsdl2Backend, ALCbackend, self);
+
+ self->deviceID = 0;
+ self->frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
+ self->Frequency = device->Frequency;
+ self->FmtChans = device->FmtChans;
+ self->FmtType = device->FmtType;
+ self->UpdateSize = device->UpdateSize;
+}
+
+static void ALCsdl2Backend_Destruct(ALCsdl2Backend *self)
+{
+ if(self->deviceID)
+ SDL_CloseAudioDevice(self->deviceID);
+ self->deviceID = 0;
+
+ ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
+}
+
+
+static void ALCsdl2Backend_audioCallback(void *ptr, Uint8 *stream, int len)
+{
+ ALCsdl2Backend *self = (ALCsdl2Backend*)ptr;
+ ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+
+ assert((len % self->frameSize) == 0);
+ aluMixData(device, stream, len / self->frameSize);
+}
+
+static ALCenum ALCsdl2Backend_open(ALCsdl2Backend *self, const ALCchar *name)
+{
+ ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+ SDL_AudioSpec want, have;
+
+ SDL_zero(want);
+ SDL_zero(have);
+
+ want.freq = device->Frequency;
+ switch(device->FmtType)
+ {
+ case DevFmtUByte: want.format = AUDIO_U8; break;
+ case DevFmtByte: want.format = AUDIO_S8; break;
+ case DevFmtUShort: want.format = AUDIO_U16SYS; break;
+ case DevFmtShort: want.format = AUDIO_S16SYS; break;
+ case DevFmtUInt: /* fall-through */
+ case DevFmtInt: want.format = AUDIO_S32SYS; break;
+ case DevFmtFloat: want.format = AUDIO_F32; break;
+ }
+ want.channels = (device->FmtChans == DevFmtMono) ? 1 : 2;
+ want.samples = device->UpdateSize;
+ want.callback = ALCsdl2Backend_audioCallback;
+ want.userdata = self;
+
+ /* Passing NULL to SDL_OpenAudioDevice opens a default, which isn't
+ * necessarily the first in the list.
+ */
+ if(!name || strcmp(name, defaultDeviceName) == 0)
+ self->deviceID = SDL_OpenAudioDevice(NULL, SDL_FALSE, &want, &have,
+ SDL_AUDIO_ALLOW_ANY_CHANGE);
+ else
+ {
+ const size_t prefix_len = strlen(DEVNAME_PREFIX);
+ if(strncmp(name, DEVNAME_PREFIX, prefix_len) == 0)
+ self->deviceID = SDL_OpenAudioDevice(name+prefix_len, SDL_FALSE, &want, &have,
+ SDL_AUDIO_ALLOW_ANY_CHANGE);
+ else
+ self->deviceID = SDL_OpenAudioDevice(name, SDL_FALSE, &want, &have,
+ SDL_AUDIO_ALLOW_ANY_CHANGE);
+ }
+ if(self->deviceID == 0)
+ return ALC_INVALID_VALUE;
+
+ device->Frequency = have.freq;
+ if(have.channels == 1)
+ device->FmtChans = DevFmtMono;
+ else if(have.channels == 2)
+ device->FmtChans = DevFmtStereo;
+ else
+ {
+ ERR("Got unhandled SDL channel count: %d\n", (int)have.channels);
+ return ALC_INVALID_VALUE;
+ }
+ switch(have.format)
+ {
+ case AUDIO_U8: device->FmtType = DevFmtUByte; break;
+ case AUDIO_S8: device->FmtType = DevFmtByte; break;
+ case AUDIO_U16SYS: device->FmtType = DevFmtUShort; break;
+ case AUDIO_S16SYS: device->FmtType = DevFmtShort; break;
+ case AUDIO_S32SYS: device->FmtType = DevFmtInt; break;
+ case AUDIO_F32SYS: device->FmtType = DevFmtFloat; break;
+ default:
+ ERR("Got unsupported SDL format: 0x%04x\n", have.format);
+ return ALC_INVALID_VALUE;
+ }
+ device->UpdateSize = have.samples;
+ device->NumUpdates = 2; /* SDL always (tries to) use two periods. */
+
+ self->frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
+ self->Frequency = device->Frequency;
+ self->FmtChans = device->FmtChans;
+ self->FmtType = device->FmtType;
+ self->UpdateSize = device->UpdateSize;
+
+ alstr_copy_cstr(&device->DeviceName, name ? name : defaultDeviceName);
+
+ return ALC_NO_ERROR;
+}
+
+static ALCboolean ALCsdl2Backend_reset(ALCsdl2Backend *self)
+{
+ ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+ device->Frequency = self->Frequency;
+ device->FmtChans = self->FmtChans;
+ device->FmtType = self->FmtType;
+ device->UpdateSize = self->UpdateSize;
+ device->NumUpdates = 2;
+ SetDefaultWFXChannelOrder(device);
+ return ALC_TRUE;
+}
+
+static ALCboolean ALCsdl2Backend_start(ALCsdl2Backend *self)
+{
+ SDL_PauseAudioDevice(self->deviceID, 0);
+ return ALC_TRUE;
+}
+
+static void ALCsdl2Backend_stop(ALCsdl2Backend *self)
+{
+ SDL_PauseAudioDevice(self->deviceID, 1);
+}
+
+static void ALCsdl2Backend_lock(ALCsdl2Backend *self)
+{
+ SDL_LockAudioDevice(self->deviceID);
+}
+
+static void ALCsdl2Backend_unlock(ALCsdl2Backend *self)
+{
+ SDL_UnlockAudioDevice(self->deviceID);
+}
+
+
+typedef struct ALCsdl2BackendFactory {
+ DERIVE_FROM_TYPE(ALCbackendFactory);
+} ALCsdl2BackendFactory;
+#define ALCsdl2BACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCsdl2BackendFactory, ALCbackendFactory) } }
+
+ALCbackendFactory *ALCsdl2BackendFactory_getFactory(void);
+
+static ALCboolean ALCsdl2BackendFactory_init(ALCsdl2BackendFactory *self);
+static void ALCsdl2BackendFactory_deinit(ALCsdl2BackendFactory *self);
+static ALCboolean ALCsdl2BackendFactory_querySupport(ALCsdl2BackendFactory *self, ALCbackend_Type type);
+static void ALCsdl2BackendFactory_probe(ALCsdl2BackendFactory *self, enum DevProbe type, al_string *outnames);
+static ALCbackend* ALCsdl2BackendFactory_createBackend(ALCsdl2BackendFactory *self, ALCdevice *device, ALCbackend_Type type);
+DEFINE_ALCBACKENDFACTORY_VTABLE(ALCsdl2BackendFactory);
+
+
+ALCbackendFactory *ALCsdl2BackendFactory_getFactory(void)
+{
+ static ALCsdl2BackendFactory factory = ALCsdl2BACKENDFACTORY_INITIALIZER;
+ return STATIC_CAST(ALCbackendFactory, &factory);
+}
+
+
+static ALCboolean ALCsdl2BackendFactory_init(ALCsdl2BackendFactory* UNUSED(self))
+{
+ if(SDL_InitSubSystem(SDL_INIT_AUDIO) == 0)
+ return AL_TRUE;
+ return ALC_FALSE;
+}
+
+static void ALCsdl2BackendFactory_deinit(ALCsdl2BackendFactory* UNUSED(self))
+{
+ SDL_QuitSubSystem(SDL_INIT_AUDIO);
+}
+
+static ALCboolean ALCsdl2BackendFactory_querySupport(ALCsdl2BackendFactory* UNUSED(self), ALCbackend_Type type)
+{
+ if(type == ALCbackend_Playback)
+ return ALC_TRUE;
+ return ALC_FALSE;
+}
+
+static void ALCsdl2BackendFactory_probe(ALCsdl2BackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
+{
+ int num_devices, i;
+ al_string name;
+
+ if(type != ALL_DEVICE_PROBE)
+ return;
+
+ AL_STRING_INIT(name);
+ num_devices = SDL_GetNumAudioDevices(SDL_FALSE);
+
+ alstr_append_range(outnames, defaultDeviceName, defaultDeviceName+sizeof(defaultDeviceName));
+ for(i = 0;i < num_devices;++i)
+ {
+ alstr_copy_cstr(&name, DEVNAME_PREFIX);
+ alstr_append_cstr(&name, SDL_GetAudioDeviceName(i, SDL_FALSE));
+ if(!alstr_empty(name))
+ alstr_append_range(outnames, VECTOR_BEGIN(name), VECTOR_END(name)+1);
+ }
+ alstr_reset(&name);
+}
+
+static ALCbackend* ALCsdl2BackendFactory_createBackend(ALCsdl2BackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
+{
+ if(type == ALCbackend_Playback)
+ {
+ ALCsdl2Backend *backend;
+ NEW_OBJ(backend, ALCsdl2Backend)(device);
+ if(!backend) return NULL;
+ return STATIC_CAST(ALCbackend, backend);
+ }
+
+ return NULL;
+}
diff --git a/Alc/backends/sndio.c b/Alc/backends/sndio.c
index 52bff13a..dd174cba 100644
--- a/Alc/backends/sndio.c
+++ b/Alc/backends/sndio.c
@@ -27,6 +27,9 @@
#include "alMain.h"
#include "alu.h"
#include "threads.h"
+#include "ringbuffer.h"
+
+#include "backends/base.h"
#include <sndio.h>
@@ -34,49 +37,88 @@
static const ALCchar sndio_device[] = "SndIO Default";
-static ALCboolean sndio_load(void)
-{
- return ALC_TRUE;
-}
-
+typedef struct SndioPlayback {
+ DERIVE_FROM_TYPE(ALCbackend);
-typedef struct {
struct sio_hdl *sndHandle;
ALvoid *mix_data;
ALsizei data_size;
- volatile int killNow;
+ ATOMIC(int) killNow;
althrd_t thread;
-} sndio_data;
+} SndioPlayback;
+
+static int SndioPlayback_mixerProc(void *ptr);
+static void SndioPlayback_Construct(SndioPlayback *self, ALCdevice *device);
+static void SndioPlayback_Destruct(SndioPlayback *self);
+static ALCenum SndioPlayback_open(SndioPlayback *self, const ALCchar *name);
+static ALCboolean SndioPlayback_reset(SndioPlayback *self);
+static ALCboolean SndioPlayback_start(SndioPlayback *self);
+static void SndioPlayback_stop(SndioPlayback *self);
+static DECLARE_FORWARD2(SndioPlayback, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
+static DECLARE_FORWARD(SndioPlayback, ALCbackend, ALCuint, availableSamples)
+static DECLARE_FORWARD(SndioPlayback, ALCbackend, ClockLatency, getClockLatency)
+static DECLARE_FORWARD(SndioPlayback, ALCbackend, void, lock)
+static DECLARE_FORWARD(SndioPlayback, ALCbackend, void, unlock)
+DECLARE_DEFAULT_ALLOCATORS(SndioPlayback)
+
+DEFINE_ALCBACKEND_VTABLE(SndioPlayback);
+
+
+static void SndioPlayback_Construct(SndioPlayback *self, ALCdevice *device)
+{
+ ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
+ SET_VTABLE2(SndioPlayback, ALCbackend, self);
+
+ self->sndHandle = NULL;
+ self->mix_data = NULL;
+ ATOMIC_INIT(&self->killNow, AL_TRUE);
+}
-static int sndio_proc(void *ptr)
+static void SndioPlayback_Destruct(SndioPlayback *self)
{
- ALCdevice *device = ptr;
- sndio_data *data = device->ExtraData;
+ if(self->sndHandle)
+ sio_close(self->sndHandle);
+ self->sndHandle = NULL;
+
+ al_free(self->mix_data);
+ self->mix_data = NULL;
+
+ ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
+}
+
+
+static int SndioPlayback_mixerProc(void *ptr)
+{
+ SndioPlayback *self = (SndioPlayback*)ptr;
+ ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
ALsizei frameSize;
size_t wrote;
SetRTPriority();
althrd_setname(althrd_current(), MIXER_THREAD_NAME);
- frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
+ frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
- while(!data->killNow && device->Connected)
+ while(!ATOMIC_LOAD(&self->killNow, almemory_order_acquire) &&
+ ATOMIC_LOAD(&device->Connected, almemory_order_acquire))
{
- ALsizei len = data->data_size;
- ALubyte *WritePtr = data->mix_data;
+ ALsizei len = self->data_size;
+ ALubyte *WritePtr = self->mix_data;
+ SndioPlayback_lock(self);
aluMixData(device, WritePtr, len/frameSize);
- while(len > 0 && !data->killNow)
+ SndioPlayback_unlock(self);
+ while(len > 0 && !ATOMIC_LOAD(&self->killNow, almemory_order_acquire))
{
- wrote = sio_write(data->sndHandle, WritePtr, len);
+ wrote = sio_write(self->sndHandle, WritePtr, len);
if(wrote == 0)
{
ERR("sio_write failed\n");
ALCdevice_Lock(device);
- aluHandleDisconnect(device);
+ aluHandleDisconnect(device, "Failed to write playback samples");
ALCdevice_Unlock(device);
break;
}
@@ -90,45 +132,30 @@ static int sndio_proc(void *ptr)
}
-
-static ALCenum sndio_open_playback(ALCdevice *device, const ALCchar *deviceName)
+static ALCenum SndioPlayback_open(SndioPlayback *self, const ALCchar *name)
{
- sndio_data *data;
+ ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
- if(!deviceName)
- deviceName = sndio_device;
- else if(strcmp(deviceName, sndio_device) != 0)
+ if(!name)
+ name = sndio_device;
+ else if(strcmp(name, sndio_device) != 0)
return ALC_INVALID_VALUE;
- data = calloc(1, sizeof(*data));
- data->killNow = 0;
-
- data->sndHandle = sio_open(NULL, SIO_PLAY, 0);
- if(data->sndHandle == NULL)
+ self->sndHandle = sio_open(NULL, SIO_PLAY, 0);
+ if(self->sndHandle == NULL)
{
- free(data);
ERR("Could not open device\n");
return ALC_INVALID_VALUE;
}
- al_string_copy_cstr(&device->DeviceName, deviceName);
- device->ExtraData = data;
+ alstr_copy_cstr(&device->DeviceName, name);
return ALC_NO_ERROR;
}
-static void sndio_close_playback(ALCdevice *device)
+static ALCboolean SndioPlayback_reset(SndioPlayback *self)
{
- sndio_data *data = device->ExtraData;
-
- sio_close(data->sndHandle);
- free(data);
- device->ExtraData = NULL;
-}
-
-static ALCboolean sndio_reset_playback(ALCdevice *device)
-{
- sndio_data *data = device->ExtraData;
+ ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
struct sio_par par;
sio_initpar(&par);
@@ -170,7 +197,7 @@ static ALCboolean sndio_reset_playback(ALCdevice *device)
par.appbufsz = device->UpdateSize * (device->NumUpdates-1);
if(!par.appbufsz) par.appbufsz = device->UpdateSize;
- if(!sio_setpar(data->sndHandle, &par) || !sio_getpar(data->sndHandle, &par))
+ if(!sio_setpar(self->sndHandle, &par) || !sio_getpar(self->sndHandle, &par))
{
ERR("Failed to set device parameters\n");
return ALC_FALSE;
@@ -211,84 +238,363 @@ static ALCboolean sndio_reset_playback(ALCdevice *device)
return ALC_TRUE;
}
-static ALCboolean sndio_start_playback(ALCdevice *device)
+static ALCboolean SndioPlayback_start(SndioPlayback *self)
{
- sndio_data *data = device->ExtraData;
+ ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
+
+ self->data_size = device->UpdateSize * FrameSizeFromDevFmt(
+ device->FmtChans, device->FmtType, device->AmbiOrder
+ );
+ al_free(self->mix_data);
+ self->mix_data = al_calloc(16, self->data_size);
- if(!sio_start(data->sndHandle))
+ if(!sio_start(self->sndHandle))
{
ERR("Error starting playback\n");
return ALC_FALSE;
}
- data->data_size = device->UpdateSize * FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
- data->mix_data = calloc(1, data->data_size);
-
- data->killNow = 0;
- if(althrd_create(&data->thread, sndio_proc, device) != althrd_success)
+ ATOMIC_STORE(&self->killNow, AL_FALSE, almemory_order_release);
+ if(althrd_create(&self->thread, SndioPlayback_mixerProc, self) != althrd_success)
{
- sio_stop(data->sndHandle);
- free(data->mix_data);
- data->mix_data = NULL;
+ sio_stop(self->sndHandle);
return ALC_FALSE;
}
return ALC_TRUE;
}
-static void sndio_stop_playback(ALCdevice *device)
+static void SndioPlayback_stop(SndioPlayback *self)
{
- sndio_data *data = device->ExtraData;
int res;
- if(data->killNow)
+ if(ATOMIC_EXCHANGE(&self->killNow, AL_TRUE, almemory_order_acq_rel))
return;
+ althrd_join(self->thread, &res);
- data->killNow = 1;
- althrd_join(data->thread, &res);
-
- if(!sio_stop(data->sndHandle))
+ if(!sio_stop(self->sndHandle))
ERR("Error stopping device\n");
- free(data->mix_data);
- data->mix_data = NULL;
+ al_free(self->mix_data);
+ self->mix_data = NULL;
+}
+
+
+typedef struct SndioCapture {
+ DERIVE_FROM_TYPE(ALCbackend);
+
+ struct sio_hdl *sndHandle;
+
+ ll_ringbuffer_t *ring;
+
+ ATOMIC(int) killNow;
+ althrd_t thread;
+} SndioCapture;
+
+static int SndioCapture_recordProc(void *ptr);
+
+static void SndioCapture_Construct(SndioCapture *self, ALCdevice *device);
+static void SndioCapture_Destruct(SndioCapture *self);
+static ALCenum SndioCapture_open(SndioCapture *self, const ALCchar *name);
+static DECLARE_FORWARD(SndioCapture, ALCbackend, ALCboolean, reset)
+static ALCboolean SndioCapture_start(SndioCapture *self);
+static void SndioCapture_stop(SndioCapture *self);
+static ALCenum SndioCapture_captureSamples(SndioCapture *self, void *buffer, ALCuint samples);
+static ALCuint SndioCapture_availableSamples(SndioCapture *self);
+static DECLARE_FORWARD(SndioCapture, ALCbackend, ClockLatency, getClockLatency)
+static DECLARE_FORWARD(SndioCapture, ALCbackend, void, lock)
+static DECLARE_FORWARD(SndioCapture, ALCbackend, void, unlock)
+DECLARE_DEFAULT_ALLOCATORS(SndioCapture)
+
+DEFINE_ALCBACKEND_VTABLE(SndioCapture);
+
+
+static void SndioCapture_Construct(SndioCapture *self, ALCdevice *device)
+{
+ ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
+ SET_VTABLE2(SndioCapture, ALCbackend, self);
+
+ self->sndHandle = NULL;
+ self->ring = NULL;
+ ATOMIC_INIT(&self->killNow, AL_TRUE);
+}
+
+static void SndioCapture_Destruct(SndioCapture *self)
+{
+ if(self->sndHandle)
+ sio_close(self->sndHandle);
+ self->sndHandle = NULL;
+
+ ll_ringbuffer_free(self->ring);
+ self->ring = NULL;
+
+ ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
}
-static const BackendFuncs sndio_funcs = {
- sndio_open_playback,
- sndio_close_playback,
- sndio_reset_playback,
- sndio_start_playback,
- sndio_stop_playback,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL
-};
-
-ALCboolean alc_sndio_init(BackendFuncs *func_list)
+static int SndioCapture_recordProc(void* ptr)
{
- if(!sndio_load())
+ SndioCapture *self = (SndioCapture*)ptr;
+ ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+ ALsizei frameSize;
+
+ SetRTPriority();
+ althrd_setname(althrd_current(), RECORD_THREAD_NAME);
+
+ frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
+
+ while(!ATOMIC_LOAD(&self->killNow, almemory_order_acquire) &&
+ ATOMIC_LOAD(&device->Connected, almemory_order_acquire))
+ {
+ ll_ringbuffer_data_t data[2];
+ size_t total, todo;
+
+ ll_ringbuffer_get_write_vector(self->ring, data);
+ todo = data[0].len + data[1].len;
+ if(todo == 0)
+ {
+ static char junk[4096];
+ sio_read(self->sndHandle, junk, minz(sizeof(junk)/frameSize, device->UpdateSize)*frameSize);
+ continue;
+ }
+
+ total = 0;
+ data[0].len *= frameSize;
+ data[1].len *= frameSize;
+ todo = minz(todo, device->UpdateSize) * frameSize;
+ while(total < todo)
+ {
+ size_t got;
+
+ if(!data[0].len)
+ data[0] = data[1];
+
+ got = sio_read(self->sndHandle, data[0].buf, minz(todo-total, data[0].len));
+ if(!got)
+ {
+ SndioCapture_lock(self);
+ aluHandleDisconnect(device, "Failed to read capture samples");
+ SndioCapture_unlock(self);
+ break;
+ }
+
+ data[0].buf += got;
+ data[0].len -= got;
+ total += got;
+ }
+ ll_ringbuffer_write_advance(self->ring, total / frameSize);
+ }
+
+ return 0;
+}
+
+
+static ALCenum SndioCapture_open(SndioCapture *self, const ALCchar *name)
+{
+ ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
+ struct sio_par par;
+
+ if(!name)
+ name = sndio_device;
+ else if(strcmp(name, sndio_device) != 0)
+ return ALC_INVALID_VALUE;
+
+ self->sndHandle = sio_open(NULL, SIO_REC, 0);
+ if(self->sndHandle == NULL)
+ {
+ ERR("Could not open device\n");
+ return ALC_INVALID_VALUE;
+ }
+
+ sio_initpar(&par);
+
+ switch(device->FmtType)
+ {
+ case DevFmtByte:
+ par.bps = 1;
+ par.sig = 1;
+ break;
+ case DevFmtUByte:
+ par.bps = 1;
+ par.sig = 0;
+ break;
+ case DevFmtShort:
+ par.bps = 2;
+ par.sig = 1;
+ break;
+ case DevFmtUShort:
+ par.bps = 2;
+ par.sig = 0;
+ break;
+ case DevFmtInt:
+ par.bps = 4;
+ par.sig = 1;
+ break;
+ case DevFmtUInt:
+ par.bps = 4;
+ par.sig = 0;
+ break;
+ case DevFmtFloat:
+ ERR("%s capture samples not supported\n", DevFmtTypeString(device->FmtType));
+ return ALC_INVALID_VALUE;
+ }
+ par.bits = par.bps * 8;
+ par.le = SIO_LE_NATIVE;
+ par.msb = SIO_LE_NATIVE ? 0 : 1;
+ par.rchan = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
+ par.rate = device->Frequency;
+
+ par.appbufsz = maxu(device->UpdateSize*device->NumUpdates, (device->Frequency+9)/10);
+ par.round = clampu(par.appbufsz/device->NumUpdates, (device->Frequency+99)/100,
+ (device->Frequency+19)/20);
+
+ device->UpdateSize = par.round;
+ device->NumUpdates = maxu(par.appbufsz/par.round, 1);
+
+ if(!sio_setpar(self->sndHandle, &par) || !sio_getpar(self->sndHandle, &par))
+ {
+ ERR("Failed to set device parameters\n");
+ return ALC_INVALID_VALUE;
+ }
+
+ if(par.bits != par.bps*8)
+ {
+ ERR("Padded samples not supported (%u of %u bits)\n", par.bits, par.bps*8);
+ return ALC_INVALID_VALUE;
+ }
+
+ if(!((device->FmtType == DevFmtByte && par.bits == 8 && par.sig != 0) ||
+ (device->FmtType == DevFmtUByte && par.bits == 8 && par.sig == 0) ||
+ (device->FmtType == DevFmtShort && par.bits == 16 && par.sig != 0) ||
+ (device->FmtType == DevFmtUShort && par.bits == 16 && par.sig == 0) ||
+ (device->FmtType == DevFmtInt && par.bits == 32 && par.sig != 0) ||
+ (device->FmtType == DevFmtUInt && par.bits == 32 && par.sig == 0)) ||
+ ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder) != (ALsizei)par.rchan ||
+ device->Frequency != par.rate)
+ {
+ ERR("Failed to set format %s %s %uhz, got %c%u %u-channel %uhz instead\n",
+ DevFmtTypeString(device->FmtType), DevFmtChannelsString(device->FmtChans),
+ device->Frequency, par.sig?'s':'u', par.bits, par.rchan, par.rate);
+ return ALC_INVALID_VALUE;
+ }
+
+ self->ring = ll_ringbuffer_create(device->UpdateSize*device->NumUpdates, par.bps*par.rchan, 0);
+ if(!self->ring)
+ {
+ ERR("Failed to allocate %u-byte ringbuffer\n",
+ device->UpdateSize*device->NumUpdates*par.bps*par.rchan);
+ return ALC_OUT_OF_MEMORY;
+ }
+
+ SetDefaultChannelOrder(device);
+
+ alstr_copy_cstr(&device->DeviceName, name);
+
+ return ALC_NO_ERROR;
+}
+
+static ALCboolean SndioCapture_start(SndioCapture *self)
+{
+ if(!sio_start(self->sndHandle))
+ {
+ ERR("Error starting playback\n");
return ALC_FALSE;
- *func_list = sndio_funcs;
+ }
+
+ ATOMIC_STORE(&self->killNow, AL_FALSE, almemory_order_release);
+ if(althrd_create(&self->thread, SndioCapture_recordProc, self) != althrd_success)
+ {
+ sio_stop(self->sndHandle);
+ return ALC_FALSE;
+ }
+
+ return ALC_TRUE;
+}
+
+static void SndioCapture_stop(SndioCapture *self)
+{
+ int res;
+
+ if(ATOMIC_EXCHANGE(&self->killNow, AL_TRUE, almemory_order_acq_rel))
+ return;
+ althrd_join(self->thread, &res);
+
+ if(!sio_stop(self->sndHandle))
+ ERR("Error stopping device\n");
+}
+
+static ALCenum SndioCapture_captureSamples(SndioCapture *self, void *buffer, ALCuint samples)
+{
+ ll_ringbuffer_read(self->ring, buffer, samples);
+ return ALC_NO_ERROR;
+}
+
+static ALCuint SndioCapture_availableSamples(SndioCapture *self)
+{
+ return ll_ringbuffer_read_space(self->ring);
+}
+
+
+typedef struct SndioBackendFactory {
+ DERIVE_FROM_TYPE(ALCbackendFactory);
+} SndioBackendFactory;
+#define SNDIOBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(SndioBackendFactory, ALCbackendFactory) } }
+
+ALCbackendFactory *SndioBackendFactory_getFactory(void);
+
+static ALCboolean SndioBackendFactory_init(SndioBackendFactory *self);
+static DECLARE_FORWARD(SndioBackendFactory, ALCbackendFactory, void, deinit)
+static ALCboolean SndioBackendFactory_querySupport(SndioBackendFactory *self, ALCbackend_Type type);
+static void SndioBackendFactory_probe(SndioBackendFactory *self, enum DevProbe type, al_string *outnames);
+static ALCbackend* SndioBackendFactory_createBackend(SndioBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
+DEFINE_ALCBACKENDFACTORY_VTABLE(SndioBackendFactory);
+
+ALCbackendFactory *SndioBackendFactory_getFactory(void)
+{
+ static SndioBackendFactory factory = SNDIOBACKENDFACTORY_INITIALIZER;
+ return STATIC_CAST(ALCbackendFactory, &factory);
+}
+
+static ALCboolean SndioBackendFactory_init(SndioBackendFactory* UNUSED(self))
+{
+ /* No dynamic loading */
return ALC_TRUE;
}
-void alc_sndio_deinit(void)
+static ALCboolean SndioBackendFactory_querySupport(SndioBackendFactory* UNUSED(self), ALCbackend_Type type)
{
+ if(type == ALCbackend_Playback || type == ALCbackend_Capture)
+ return ALC_TRUE;
+ return ALC_FALSE;
}
-void alc_sndio_probe(enum DevProbe type)
+static void SndioBackendFactory_probe(SndioBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
{
switch(type)
{
case ALL_DEVICE_PROBE:
- AppendAllDevicesList(sndio_device);
- break;
case CAPTURE_DEVICE_PROBE:
+ alstr_append_range(outnames, sndio_device, sndio_device+sizeof(sndio_device));
break;
}
}
+
+static ALCbackend* SndioBackendFactory_createBackend(SndioBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
+{
+ if(type == ALCbackend_Playback)
+ {
+ SndioPlayback *backend;
+ NEW_OBJ(backend, SndioPlayback)(device);
+ if(!backend) return NULL;
+ return STATIC_CAST(ALCbackend, backend);
+ }
+ if(type == ALCbackend_Capture)
+ {
+ SndioCapture *backend;
+ NEW_OBJ(backend, SndioCapture)(device);
+ if(!backend) return NULL;
+ return STATIC_CAST(ALCbackend, backend);
+ }
+
+ return NULL;
+}
diff --git a/Alc/backends/solaris.c b/Alc/backends/solaris.c
index 52ca9090..71282204 100644
--- a/Alc/backends/solaris.c
+++ b/Alc/backends/solaris.c
@@ -22,6 +22,7 @@
#include <sys/ioctl.h>
#include <sys/types.h>
+#include <sys/time.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
@@ -33,6 +34,7 @@
#include "alMain.h"
#include "alu.h"
+#include "alconfig.h"
#include "threads.h"
#include "compat.h"
@@ -49,7 +51,7 @@ typedef struct ALCsolarisBackend {
ALubyte *mix_data;
int data_size;
- volatile int killNow;
+ ATOMIC(ALenum) killNow;
althrd_t thread;
} ALCsolarisBackend;
@@ -58,13 +60,12 @@ 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, ClockLatency, getClockLatency)
static DECLARE_FORWARD(ALCsolarisBackend, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCsolarisBackend, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCsolarisBackend)
@@ -83,6 +84,8 @@ static void ALCsolarisBackend_Construct(ALCsolarisBackend *self, ALCdevice *devi
SET_VTABLE2(ALCsolarisBackend, ALCbackend, self);
self->fd = -1;
+ self->mix_data = NULL;
+ ATOMIC_INIT(&self->killNow, AL_FALSE);
}
static void ALCsolarisBackend_Destruct(ALCsolarisBackend *self)
@@ -102,43 +105,67 @@ static void ALCsolarisBackend_Destruct(ALCsolarisBackend *self)
static int ALCsolarisBackend_mixerProc(void *ptr)
{
ALCsolarisBackend *self = ptr;
- ALCdevice *Device = STATIC_CAST(ALCbackend,self)->mDevice;
- ALint frameSize;
- int wrote;
+ ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+ struct timeval timeout;
+ ALubyte *write_ptr;
+ ALint frame_size;
+ ALint to_write;
+ ssize_t wrote;
+ fd_set wfds;
+ int sret;
SetRTPriority();
althrd_setname(althrd_current(), MIXER_THREAD_NAME);
- frameSize = FrameSizeFromDevFmt(Device->FmtChans, Device->FmtType);
+ frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
- while(!self->killNow && Device->Connected)
+ ALCsolarisBackend_lock(self);
+ while(!ATOMIC_LOAD(&self->killNow, almemory_order_acquire) &&
+ ATOMIC_LOAD(&device->Connected, almemory_order_acquire))
{
- ALint len = self->data_size;
- ALubyte *WritePtr = self->mix_data;
+ FD_ZERO(&wfds);
+ FD_SET(self->fd, &wfds);
+ timeout.tv_sec = 1;
+ timeout.tv_usec = 0;
+
+ ALCsolarisBackend_unlock(self);
+ sret = select(self->fd+1, NULL, &wfds, NULL, &timeout);
+ ALCsolarisBackend_lock(self);
+ if(sret < 0)
+ {
+ if(errno == EINTR)
+ continue;
+ ERR("select failed: %s\n", strerror(errno));
+ aluHandleDisconnect(device, "Failed to wait for playback buffer: %s", strerror(errno));
+ break;
+ }
+ else if(sret == 0)
+ {
+ WARN("select timeout\n");
+ continue;
+ }
- aluMixData(Device, WritePtr, len/frameSize);
- while(len > 0 && !self->killNow)
+ write_ptr = self->mix_data;
+ to_write = self->data_size;
+ aluMixData(device, write_ptr, to_write/frame_size);
+ while(to_write > 0 && !ATOMIC_LOAD_SEQ(&self->killNow))
{
- wrote = write(self->fd, WritePtr, len);
+ wrote = write(self->fd, write_ptr, to_write);
if(wrote < 0)
{
- if(errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR)
- {
- ERR("write failed: %s\n", strerror(errno));
- ALCsolarisBackend_lock(self);
- aluHandleDisconnect(Device);
- ALCsolarisBackend_unlock(self);
- break;
- }
-
- al_nssleep(1000000);
- continue;
+ if(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
+ continue;
+ ERR("write failed: %s\n", strerror(errno));
+ aluHandleDisconnect(device, "Failed to write playback samples: %s",
+ strerror(errno));
+ break;
}
- len -= wrote;
- WritePtr += wrote;
+ to_write -= wrote;
+ write_ptr += wrote;
}
}
+ ALCsolarisBackend_unlock(self);
return 0;
}
@@ -161,23 +188,17 @@ static ALCenum ALCsolarisBackend_open(ALCsolarisBackend *self, const ALCchar *na
}
device = STATIC_CAST(ALCbackend,self)->mDevice;
- al_string_copy_cstr(&device->DeviceName, name);
+ alstr_copy_cstr(&device->DeviceName, name);
return ALC_NO_ERROR;
}
-static void ALCsolarisBackend_close(ALCsolarisBackend *self)
-{
- close(self->fd);
- self->fd = -1;
-}
-
static ALCboolean ALCsolarisBackend_reset(ALCsolarisBackend *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
audio_info_t info;
- ALuint frameSize;
- int numChannels;
+ ALsizei frameSize;
+ ALsizei numChannels;
AUDIO_INITINFO(&info);
@@ -185,7 +206,7 @@ static ALCboolean ALCsolarisBackend_reset(ALCsolarisBackend *self)
if(device->FmtChans != DevFmtMono)
device->FmtChans = DevFmtStereo;
- numChannels = ChannelsFromDevFmt(device->FmtChans);
+ numChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
info.play.channels = numChannels;
switch(device->FmtType)
@@ -219,9 +240,9 @@ static ALCboolean ALCsolarisBackend_reset(ALCsolarisBackend *self)
return ALC_FALSE;
}
- if(ChannelsFromDevFmt(device->FmtChans) != info.play.channels)
+ if(ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder) != (ALsizei)info.play.channels)
{
- ERR("Could not set %d channels, got %d instead\n", ChannelsFromDevFmt(device->FmtChans), info.play.channels);
+ ERR("Failed to set %s, got %u channels instead\n", DevFmtChannelsString(device->FmtChans), info.play.channels);
return ALC_FALSE;
}
@@ -241,7 +262,9 @@ static ALCboolean ALCsolarisBackend_reset(ALCsolarisBackend *self)
SetDefaultChannelOrder(device);
free(self->mix_data);
- self->data_size = device->UpdateSize * FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
+ self->data_size = device->UpdateSize * FrameSizeFromDevFmt(
+ device->FmtChans, device->FmtType, device->AmbiOrder
+ );
self->mix_data = calloc(1, self->data_size);
return ALC_TRUE;
@@ -249,7 +272,7 @@ static ALCboolean ALCsolarisBackend_reset(ALCsolarisBackend *self)
static ALCboolean ALCsolarisBackend_start(ALCsolarisBackend *self)
{
- self->killNow = 0;
+ ATOMIC_STORE_SEQ(&self->killNow, AL_FALSE);
if(althrd_create(&self->thread, ALCsolarisBackend_mixerProc, self) != althrd_success)
return ALC_FALSE;
return ALC_TRUE;
@@ -259,10 +282,9 @@ static void ALCsolarisBackend_stop(ALCsolarisBackend *self)
{
int res;
- if(self->killNow)
+ if(ATOMIC_EXCHANGE_SEQ(&self->killNow, AL_TRUE))
return;
- self->killNow = 1;
althrd_join(self->thread, &res);
if(ioctl(self->fd, AUDIO_DRAIN) < 0)
@@ -280,7 +302,7 @@ ALCbackendFactory *ALCsolarisBackendFactory_getFactory(void);
static ALCboolean ALCsolarisBackendFactory_init(ALCsolarisBackendFactory *self);
static DECLARE_FORWARD(ALCsolarisBackendFactory, ALCbackendFactory, void, deinit)
static ALCboolean ALCsolarisBackendFactory_querySupport(ALCsolarisBackendFactory *self, ALCbackend_Type type);
-static void ALCsolarisBackendFactory_probe(ALCsolarisBackendFactory *self, enum DevProbe type);
+static void ALCsolarisBackendFactory_probe(ALCsolarisBackendFactory *self, enum DevProbe type, al_string *outnames);
static ALCbackend* ALCsolarisBackendFactory_createBackend(ALCsolarisBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCsolarisBackendFactory);
@@ -305,7 +327,7 @@ static ALCboolean ALCsolarisBackendFactory_querySupport(ALCsolarisBackendFactory
return ALC_FALSE;
}
-static void ALCsolarisBackendFactory_probe(ALCsolarisBackendFactory* UNUSED(self), enum DevProbe type)
+static void ALCsolarisBackendFactory_probe(ALCsolarisBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
{
switch(type)
{
@@ -315,7 +337,7 @@ static void ALCsolarisBackendFactory_probe(ALCsolarisBackendFactory* UNUSED(self
struct stat buf;
if(stat(solaris_driver, &buf) == 0)
#endif
- AppendAllDevicesList(solaris_device);
+ alstr_append_range(outnames, solaris_device, solaris_device+sizeof(solaris_device));
}
break;
diff --git a/Alc/backends/mmdevapi.c b/Alc/backends/wasapi.c
index e8563d33..b974321b 100644
--- a/Alc/backends/mmdevapi.c
+++ b/Alc/backends/wasapi.c
@@ -25,6 +25,7 @@
#include <stdio.h>
#include <memory.h>
+#include <wtypes.h>
#include <mmdeviceapi.h>
#include <audioclient.h>
#include <cguid.h>
@@ -40,9 +41,11 @@
#include "alMain.h"
#include "alu.h"
+#include "ringbuffer.h"
#include "threads.h"
#include "compat.h"
#include "alstring.h"
+#include "converter.h"
#include "backends/base.h"
@@ -52,6 +55,7 @@ DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0
DEFINE_DEVPROPKEY(DEVPKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80,0x20, 0x67,0xd1,0x46,0xa8,0x50,0xe0, 14);
DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_FormFactor, 0x1da5d803, 0xd492, 0x4edd, 0x8c,0x23, 0xe0,0xc0,0xff,0xee,0x7f,0x0e, 0);
+DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_GUID, 0x1da5d803, 0xd492, 0x4edd, 0x8c, 0x23,0xe0, 0xc0,0xff,0xee,0x7f,0x0e, 4 );
#define MONO SPEAKER_FRONT_CENTER
#define STEREO (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT)
@@ -62,11 +66,21 @@ DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_FormFactor, 0x1da5d803, 0xd492, 0x4edd, 0x
#define X7DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
#define X7DOT1_WIDE (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT|SPEAKER_FRONT_LEFT_OF_CENTER|SPEAKER_FRONT_RIGHT_OF_CENTER)
-#define DEVNAME_TAIL " on OpenAL Soft"
+#define REFTIME_PER_SEC ((REFERENCE_TIME)10000000)
+
+#define DEVNAME_HEAD "OpenAL Soft on "
+
+
+/* Scales the given value using 64-bit integer math, ceiling the result. */
+static inline ALuint64 ScaleCeil(ALuint64 val, ALuint64 new_scale, ALuint64 old_scale)
+{
+ return (val*new_scale + old_scale-1) / old_scale;
+}
typedef struct {
al_string name;
+ al_string endpoint_guid; // obtained from PKEY_AudioEndpoint_GUID , set to "Unknown device GUID" if absent.
WCHAR *devid;
} DevMap;
TYPEDEF_VECTOR(DevMap, vector_DevMap)
@@ -75,11 +89,12 @@ static void clear_devlist(vector_DevMap *list)
{
#define CLEAR_DEVMAP(i) do { \
AL_STRING_DEINIT((i)->name); \
+ AL_STRING_DEINIT((i)->endpoint_guid); \
free((i)->devid); \
(i)->devid = NULL; \
} while(0)
VECTOR_FOR_EACH(DevMap, *list, CLEAR_DEVMAP);
- VECTOR_RESIZE(*list, 0);
+ VECTOR_RESIZE(*list, 0, 0);
#undef CLEAR_DEVMAP
}
@@ -104,6 +119,15 @@ typedef struct {
#define WM_USER_Enumerate (WM_USER+5)
#define WM_USER_Last (WM_USER+5)
+static const char MessageStr[WM_USER_Last+1-WM_USER][20] = {
+ "Open Device",
+ "Reset Device",
+ "Start Device",
+ "Stop Device",
+ "Close Device",
+ "Enumerate Devices",
+};
+
static inline void ReturnMsgResponse(ThreadRequest *req, HRESULT res)
{
req->result = res;
@@ -119,16 +143,21 @@ static HRESULT WaitForResponse(ThreadRequest *req)
}
-static void get_device_name(IMMDevice *device, al_string *name)
+static void get_device_name_and_guid(IMMDevice *device, al_string *name, al_string *guid)
{
IPropertyStore *ps;
PROPVARIANT pvname;
+ PROPVARIANT pvguid;
HRESULT hr;
+ alstr_copy_cstr(name, DEVNAME_HEAD);
+
hr = IMMDevice_OpenPropertyStore(device, STGM_READ, &ps);
if(FAILED(hr))
{
WARN("OpenPropertyStore failed: 0x%08lx\n", hr);
+ alstr_append_cstr(name, "Unknown Device Name");
+ if(guid!=NULL)alstr_copy_cstr(guid, "Unknown Device GUID");
return;
}
@@ -136,13 +165,39 @@ 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 Device_FriendlyName failed: 0x%08lx\n", hr);
+ alstr_append_cstr(name, "Unknown Device Name");
+ }
else if(pvname.vt == VT_LPWSTR)
- al_string_copy_wcstr(name, pvname.pwszVal);
+ alstr_append_wcstr(name, pvname.pwszVal);
else
+ {
WARN("Unexpected PROPVARIANT type: 0x%04x\n", pvname.vt);
-
+ alstr_append_cstr(name, "Unknown Device Name");
+ }
PropVariantClear(&pvname);
+
+ if(guid!=NULL){
+ PropVariantInit(&pvguid);
+
+ hr = IPropertyStore_GetValue(ps, (const PROPERTYKEY*)&PKEY_AudioEndpoint_GUID, &pvguid);
+ if(FAILED(hr))
+ {
+ WARN("GetValue AudioEndpoint_GUID failed: 0x%08lx\n", hr);
+ alstr_copy_cstr(guid, "Unknown Device GUID");
+ }
+ else if(pvguid.vt == VT_LPWSTR)
+ alstr_copy_wcstr(guid, pvguid.pwszVal);
+ else
+ {
+ WARN("Unexpected PROPVARIANT type: 0x%04x\n", pvguid.vt);
+ alstr_copy_cstr(guid, "Unknown Device GUID");
+ }
+
+ PropVariantClear(&pvguid);
+ }
+
IPropertyStore_Release(ps);
}
@@ -176,7 +231,7 @@ static void get_device_formfactor(IMMDevice *device, EndpointFormFactor *formfac
}
-static void add_device(IMMDevice *device, LPCWSTR devid, vector_DevMap *list)
+static void add_device(IMMDevice *device, const WCHAR *devid, vector_DevMap *list)
{
int count = 0;
al_string tmpname;
@@ -184,40 +239,39 @@ static void add_device(IMMDevice *device, LPCWSTR devid, vector_DevMap *list)
AL_STRING_INIT(tmpname);
AL_STRING_INIT(entry.name);
+ AL_STRING_INIT(entry.endpoint_guid);
entry.devid = strdupW(devid);
- get_device_name(device, &tmpname);
+ get_device_name_and_guid(device, &tmpname, &entry.endpoint_guid);
while(1)
{
const DevMap *iter;
- al_string_copy(&entry.name, tmpname);
- if(count == 0)
- al_string_append_cstr(&entry.name, DEVNAME_TAIL);
- else
+ alstr_copy(&entry.name, tmpname);
+ if(count != 0)
{
char str[64];
- snprintf(str, sizeof(str), " #%d"DEVNAME_TAIL, count+1);
- al_string_append_cstr(&entry.name, str);
+ snprintf(str, sizeof(str), " #%d", count+1);
+ alstr_append_cstr(&entry.name, str);
}
-#define MATCH_ENTRY(i) (al_string_cmp(entry.name, (i)->name) == 0)
+#define MATCH_ENTRY(i) (alstr_cmp(entry.name, (i)->name) == 0)
VECTOR_FIND_IF(iter, const DevMap, *list, MATCH_ENTRY);
- if(iter == VECTOR_ITER_END(*list)) break;
+ if(iter == VECTOR_END(*list)) break;
#undef MATCH_ENTRY
count++;
}
- TRACE("Got device \"%s\", \"%ls\"\n", al_string_get_cstr(entry.name), entry.devid);
+ TRACE("Got device \"%s\", \"%s\", \"%ls\"\n", alstr_get_cstr(entry.name), alstr_get_cstr(entry.endpoint_guid), entry.devid);
VECTOR_PUSH_BACK(*list, entry);
AL_STRING_DEINIT(tmpname);
}
-static LPWSTR get_device_id(IMMDevice *device)
+static WCHAR *get_device_id(IMMDevice *device)
{
- LPWSTR devid;
+ WCHAR *devid;
HRESULT hr;
hr = IMMDevice_GetId(device, &devid);
@@ -234,7 +288,7 @@ static HRESULT probe_devices(IMMDeviceEnumerator *devenum, EDataFlow flowdir, ve
{
IMMDeviceCollection *coll;
IMMDevice *defdev = NULL;
- LPWSTR defdevid = NULL;
+ WCHAR *defdevid = NULL;
HRESULT hr;
UINT count;
UINT i;
@@ -251,11 +305,7 @@ static HRESULT probe_devices(IMMDeviceEnumerator *devenum, EDataFlow flowdir, ve
if(SUCCEEDED(hr) && count > 0)
{
clear_devlist(list);
- if(!VECTOR_RESERVE(*list, count))
- {
- IMMDeviceCollection_Release(coll);
- return E_OUTOFMEMORY;
- }
+ VECTOR_RESIZE(*list, 0, count);
hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(devenum, flowdir,
eMultimedia, &defdev);
@@ -270,7 +320,7 @@ static HRESULT probe_devices(IMMDeviceEnumerator *devenum, EDataFlow flowdir, ve
for(i = 0;i < count;++i)
{
IMMDevice *device;
- LPWSTR devid;
+ WCHAR *devid;
hr = IMMDeviceCollection_Item(coll, i, &device);
if(FAILED(hr)) continue;
@@ -278,7 +328,7 @@ static HRESULT probe_devices(IMMDeviceEnumerator *devenum, EDataFlow flowdir, ve
devid = get_device_id(device);
if(devid)
{
- if(wcscmp(devid, defdevid) != 0)
+ if(!defdevid || wcscmp(devid, defdevid) != 0)
add_device(device, devid, list);
CoTaskMemFree(devid);
}
@@ -294,51 +344,51 @@ static HRESULT probe_devices(IMMDeviceEnumerator *devenum, EDataFlow flowdir, ve
/* Proxy interface used by the message handler. */
-struct ALCmmdevProxyVtable;
+struct ALCwasapiProxyVtable;
-typedef struct ALCmmdevProxy {
- const struct ALCmmdevProxyVtable *vtbl;
-} ALCmmdevProxy;
+typedef struct ALCwasapiProxy {
+ const struct ALCwasapiProxyVtable *vtbl;
+} ALCwasapiProxy;
-struct ALCmmdevProxyVtable {
- HRESULT (*const openProxy)(ALCmmdevProxy*);
- void (*const closeProxy)(ALCmmdevProxy*);
+struct ALCwasapiProxyVtable {
+ HRESULT (*const openProxy)(ALCwasapiProxy*);
+ void (*const closeProxy)(ALCwasapiProxy*);
- HRESULT (*const resetProxy)(ALCmmdevProxy*);
- HRESULT (*const startProxy)(ALCmmdevProxy*);
- void (*const stopProxy)(ALCmmdevProxy*);
+ HRESULT (*const resetProxy)(ALCwasapiProxy*);
+ HRESULT (*const startProxy)(ALCwasapiProxy*);
+ void (*const stopProxy)(ALCwasapiProxy*);
};
-#define DEFINE_ALCMMDEVPROXY_VTABLE(T) \
-DECLARE_THUNK(T, ALCmmdevProxy, HRESULT, openProxy) \
-DECLARE_THUNK(T, ALCmmdevProxy, void, closeProxy) \
-DECLARE_THUNK(T, ALCmmdevProxy, HRESULT, resetProxy) \
-DECLARE_THUNK(T, ALCmmdevProxy, HRESULT, startProxy) \
-DECLARE_THUNK(T, ALCmmdevProxy, void, stopProxy) \
+#define DEFINE_ALCWASAPIPROXY_VTABLE(T) \
+DECLARE_THUNK(T, ALCwasapiProxy, HRESULT, openProxy) \
+DECLARE_THUNK(T, ALCwasapiProxy, void, closeProxy) \
+DECLARE_THUNK(T, ALCwasapiProxy, HRESULT, resetProxy) \
+DECLARE_THUNK(T, ALCwasapiProxy, HRESULT, startProxy) \
+DECLARE_THUNK(T, ALCwasapiProxy, void, stopProxy) \
\
-static const struct ALCmmdevProxyVtable T##_ALCmmdevProxy_vtable = { \
- T##_ALCmmdevProxy_openProxy, \
- T##_ALCmmdevProxy_closeProxy, \
- T##_ALCmmdevProxy_resetProxy, \
- T##_ALCmmdevProxy_startProxy, \
- T##_ALCmmdevProxy_stopProxy, \
+static const struct ALCwasapiProxyVtable T##_ALCwasapiProxy_vtable = { \
+ T##_ALCwasapiProxy_openProxy, \
+ T##_ALCwasapiProxy_closeProxy, \
+ T##_ALCwasapiProxy_resetProxy, \
+ T##_ALCwasapiProxy_startProxy, \
+ T##_ALCwasapiProxy_stopProxy, \
}
-static void ALCmmdevProxy_Construct(ALCmmdevProxy* UNUSED(self)) { }
-static void ALCmmdevProxy_Destruct(ALCmmdevProxy* UNUSED(self)) { }
+static void ALCwasapiProxy_Construct(ALCwasapiProxy* UNUSED(self)) { }
+static void ALCwasapiProxy_Destruct(ALCwasapiProxy* UNUSED(self)) { }
-static DWORD CALLBACK ALCmmdevProxy_messageHandler(void *ptr)
+static DWORD CALLBACK ALCwasapiProxy_messageHandler(void *ptr)
{
ThreadRequest *req = ptr;
IMMDeviceEnumerator *Enumerator;
ALuint deviceCount = 0;
- ALCmmdevProxy *proxy;
+ ALCwasapiProxy *proxy;
HRESULT hr, cohr;
MSG msg;
TRACE("Starting message thread\n");
- cohr = CoInitialize(NULL);
+ cohr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
if(FAILED(cohr))
{
WARN("Failed to initialize COM: 0x%08lx\n", cohr);
@@ -372,16 +422,20 @@ static DWORD CALLBACK ALCmmdevProxy_messageHandler(void *ptr)
TRACE("Starting message loop\n");
while(GetMessage(&msg, NULL, WM_USER_First, WM_USER_Last))
{
- TRACE("Got message %u (lparam=%p, wparam=%p)\n", msg.message, (void*)msg.lParam, (void*)msg.wParam);
+ TRACE("Got message \"%s\" (0x%04x, lparam=%p, wparam=%p)\n",
+ (msg.message >= WM_USER && msg.message <= WM_USER_Last) ?
+ MessageStr[msg.message-WM_USER] : "Unknown",
+ msg.message, (void*)msg.lParam, (void*)msg.wParam
+ );
switch(msg.message)
{
case WM_USER_OpenDevice:
req = (ThreadRequest*)msg.wParam;
- proxy = (ALCmmdevProxy*)msg.lParam;
+ proxy = (ALCwasapiProxy*)msg.lParam;
hr = cohr = S_OK;
if(++deviceCount == 1)
- hr = cohr = CoInitialize(NULL);
+ hr = cohr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
if(SUCCEEDED(hr))
hr = V0(proxy,openProxy)();
if(FAILED(hr))
@@ -395,7 +449,7 @@ static DWORD CALLBACK ALCmmdevProxy_messageHandler(void *ptr)
case WM_USER_ResetDevice:
req = (ThreadRequest*)msg.wParam;
- proxy = (ALCmmdevProxy*)msg.lParam;
+ proxy = (ALCwasapiProxy*)msg.lParam;
hr = V0(proxy,resetProxy)();
ReturnMsgResponse(req, hr);
@@ -403,7 +457,7 @@ static DWORD CALLBACK ALCmmdevProxy_messageHandler(void *ptr)
case WM_USER_StartDevice:
req = (ThreadRequest*)msg.wParam;
- proxy = (ALCmmdevProxy*)msg.lParam;
+ proxy = (ALCwasapiProxy*)msg.lParam;
hr = V0(proxy,startProxy)();
ReturnMsgResponse(req, hr);
@@ -411,7 +465,7 @@ static DWORD CALLBACK ALCmmdevProxy_messageHandler(void *ptr)
case WM_USER_StopDevice:
req = (ThreadRequest*)msg.wParam;
- proxy = (ALCmmdevProxy*)msg.lParam;
+ proxy = (ALCwasapiProxy*)msg.lParam;
V0(proxy,stopProxy)();
ReturnMsgResponse(req, S_OK);
@@ -419,7 +473,7 @@ static DWORD CALLBACK ALCmmdevProxy_messageHandler(void *ptr)
case WM_USER_CloseDevice:
req = (ThreadRequest*)msg.wParam;
- proxy = (ALCmmdevProxy*)msg.lParam;
+ proxy = (ALCwasapiProxy*)msg.lParam;
V0(proxy,closeProxy)();
if(--deviceCount == 0)
@@ -433,7 +487,7 @@ static DWORD CALLBACK ALCmmdevProxy_messageHandler(void *ptr)
hr = cohr = S_OK;
if(++deviceCount == 1)
- hr = cohr = CoInitialize(NULL);
+ hr = cohr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
if(SUCCEEDED(hr))
hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
if(SUCCEEDED(hr))
@@ -466,9 +520,9 @@ static DWORD CALLBACK ALCmmdevProxy_messageHandler(void *ptr)
}
-typedef struct ALCmmdevPlayback {
+typedef struct ALCwasapiPlayback {
DERIVE_FROM_TYPE(ALCbackend);
- DERIVE_FROM_TYPE(ALCmmdevProxy);
+ DERIVE_FROM_TYPE(ALCwasapiProxy);
WCHAR *devid;
@@ -479,43 +533,42 @@ typedef struct ALCmmdevPlayback {
HANDLE MsgEvent;
- volatile UINT32 Padding;
+ ATOMIC(UINT32) Padding;
- volatile int killNow;
+ ATOMIC(int) killNow;
althrd_t thread;
-} ALCmmdevPlayback;
-
-static int ALCmmdevPlayback_mixerProc(void *arg);
-
-static void ALCmmdevPlayback_Construct(ALCmmdevPlayback *self, ALCdevice *device);
-static void ALCmmdevPlayback_Destruct(ALCmmdevPlayback *self);
-static ALCenum ALCmmdevPlayback_open(ALCmmdevPlayback *self, const ALCchar *name);
-static HRESULT ALCmmdevPlayback_openProxy(ALCmmdevPlayback *self);
-static void ALCmmdevPlayback_close(ALCmmdevPlayback *self);
-static void ALCmmdevPlayback_closeProxy(ALCmmdevPlayback *self);
-static ALCboolean ALCmmdevPlayback_reset(ALCmmdevPlayback *self);
-static HRESULT ALCmmdevPlayback_resetProxy(ALCmmdevPlayback *self);
-static ALCboolean ALCmmdevPlayback_start(ALCmmdevPlayback *self);
-static HRESULT ALCmmdevPlayback_startProxy(ALCmmdevPlayback *self);
-static void ALCmmdevPlayback_stop(ALCmmdevPlayback *self);
-static void ALCmmdevPlayback_stopProxy(ALCmmdevPlayback *self);
-static DECLARE_FORWARD2(ALCmmdevPlayback, ALCbackend, ALCenum, captureSamples, ALCvoid*, ALCuint)
-static DECLARE_FORWARD(ALCmmdevPlayback, ALCbackend, ALCuint, availableSamples)
-static ALint64 ALCmmdevPlayback_getLatency(ALCmmdevPlayback *self);
-static DECLARE_FORWARD(ALCmmdevPlayback, ALCbackend, void, lock)
-static DECLARE_FORWARD(ALCmmdevPlayback, ALCbackend, void, unlock)
-DECLARE_DEFAULT_ALLOCATORS(ALCmmdevPlayback)
-
-DEFINE_ALCMMDEVPROXY_VTABLE(ALCmmdevPlayback);
-DEFINE_ALCBACKEND_VTABLE(ALCmmdevPlayback);
-
-
-static void ALCmmdevPlayback_Construct(ALCmmdevPlayback *self, ALCdevice *device)
+} ALCwasapiPlayback;
+
+static int ALCwasapiPlayback_mixerProc(void *arg);
+
+static void ALCwasapiPlayback_Construct(ALCwasapiPlayback *self, ALCdevice *device);
+static void ALCwasapiPlayback_Destruct(ALCwasapiPlayback *self);
+static ALCenum ALCwasapiPlayback_open(ALCwasapiPlayback *self, const ALCchar *name);
+static HRESULT ALCwasapiPlayback_openProxy(ALCwasapiPlayback *self);
+static void ALCwasapiPlayback_closeProxy(ALCwasapiPlayback *self);
+static ALCboolean ALCwasapiPlayback_reset(ALCwasapiPlayback *self);
+static HRESULT ALCwasapiPlayback_resetProxy(ALCwasapiPlayback *self);
+static ALCboolean ALCwasapiPlayback_start(ALCwasapiPlayback *self);
+static HRESULT ALCwasapiPlayback_startProxy(ALCwasapiPlayback *self);
+static void ALCwasapiPlayback_stop(ALCwasapiPlayback *self);
+static void ALCwasapiPlayback_stopProxy(ALCwasapiPlayback *self);
+static DECLARE_FORWARD2(ALCwasapiPlayback, ALCbackend, ALCenum, captureSamples, ALCvoid*, ALCuint)
+static DECLARE_FORWARD(ALCwasapiPlayback, ALCbackend, ALCuint, availableSamples)
+static ClockLatency ALCwasapiPlayback_getClockLatency(ALCwasapiPlayback *self);
+static DECLARE_FORWARD(ALCwasapiPlayback, ALCbackend, void, lock)
+static DECLARE_FORWARD(ALCwasapiPlayback, ALCbackend, void, unlock)
+DECLARE_DEFAULT_ALLOCATORS(ALCwasapiPlayback)
+
+DEFINE_ALCWASAPIPROXY_VTABLE(ALCwasapiPlayback);
+DEFINE_ALCBACKEND_VTABLE(ALCwasapiPlayback);
+
+
+static void ALCwasapiPlayback_Construct(ALCwasapiPlayback *self, ALCdevice *device)
{
- SET_VTABLE2(ALCmmdevPlayback, ALCbackend, self);
- SET_VTABLE2(ALCmmdevPlayback, ALCmmdevProxy, self);
+ SET_VTABLE2(ALCwasapiPlayback, ALCbackend, self);
+ SET_VTABLE2(ALCwasapiPlayback, ALCwasapiProxy, self);
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
- ALCmmdevProxy_Construct(STATIC_CAST(ALCmmdevProxy, self));
+ ALCwasapiProxy_Construct(STATIC_CAST(ALCwasapiProxy, self));
self->devid = NULL;
@@ -526,13 +579,30 @@ static void ALCmmdevPlayback_Construct(ALCmmdevPlayback *self, ALCdevice *device
self->MsgEvent = NULL;
- self->Padding = 0;
+ ATOMIC_INIT(&self->Padding, 0);
- self->killNow = 0;
+ ATOMIC_INIT(&self->killNow, 0);
}
-static void ALCmmdevPlayback_Destruct(ALCmmdevPlayback *self)
+static void ALCwasapiPlayback_Destruct(ALCwasapiPlayback *self)
{
+ if(self->MsgEvent)
+ {
+ ThreadRequest req = { self->MsgEvent, 0 };
+ if(PostThreadMessage(ThreadID, WM_USER_CloseDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCwasapiProxy, self)))
+ (void)WaitForResponse(&req);
+
+ CloseHandle(self->MsgEvent);
+ self->MsgEvent = NULL;
+ }
+
+ if(self->NotifyEvent)
+ CloseHandle(self->NotifyEvent);
+ self->NotifyEvent = NULL;
+
+ free(self->devid);
+ self->devid = NULL;
+
if(self->NotifyEvent != NULL)
CloseHandle(self->NotifyEvent);
self->NotifyEvent = NULL;
@@ -543,26 +613,26 @@ static void ALCmmdevPlayback_Destruct(ALCmmdevPlayback *self)
free(self->devid);
self->devid = NULL;
- ALCmmdevProxy_Destruct(STATIC_CAST(ALCmmdevProxy, self));
+ ALCwasapiProxy_Destruct(STATIC_CAST(ALCwasapiProxy, self));
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
}
-FORCE_ALIGN static int ALCmmdevPlayback_mixerProc(void *arg)
+FORCE_ALIGN static int ALCwasapiPlayback_mixerProc(void *arg)
{
- ALCmmdevPlayback *self = arg;
+ ALCwasapiPlayback *self = arg;
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
UINT32 buffer_len, written;
ALuint update_size, len;
BYTE *buffer;
HRESULT hr;
- hr = CoInitialize(NULL);
+ hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
if(FAILED(hr))
{
- ERR("CoInitialize(NULL) failed: 0x%08lx\n", hr);
+ ERR("CoInitializeEx(NULL, COINIT_MULTITHREADED) failed: 0x%08lx\n", hr);
V0(device->Backend,lock)();
- aluHandleDisconnect(device);
+ aluHandleDisconnect(device, "COM init failed: 0x%08lx", hr);
V0(device->Backend,unlock)();
return 1;
}
@@ -572,18 +642,18 @@ FORCE_ALIGN static int ALCmmdevPlayback_mixerProc(void *arg)
update_size = device->UpdateSize;
buffer_len = update_size * device->NumUpdates;
- while(!self->killNow)
+ while(!ATOMIC_LOAD(&self->killNow, almemory_order_relaxed))
{
hr = IAudioClient_GetCurrentPadding(self->client, &written);
if(FAILED(hr))
{
ERR("Failed to get padding: 0x%08lx\n", hr);
V0(device->Backend,lock)();
- aluHandleDisconnect(device);
+ aluHandleDisconnect(device, "Failed to retrieve buffer padding: 0x%08lx", hr);
V0(device->Backend,unlock)();
break;
}
- self->Padding = written;
+ ATOMIC_STORE(&self->Padding, written, almemory_order_relaxed);
len = buffer_len - written;
if(len < update_size)
@@ -599,22 +669,22 @@ FORCE_ALIGN static int ALCmmdevPlayback_mixerProc(void *arg)
hr = IAudioRenderClient_GetBuffer(self->render, len, &buffer);
if(SUCCEEDED(hr))
{
- V0(device->Backend,lock)();
+ ALCwasapiPlayback_lock(self);
aluMixData(device, buffer, len);
- self->Padding = written + len;
- V0(device->Backend,unlock)();
+ ATOMIC_STORE(&self->Padding, written + len, almemory_order_relaxed);
+ ALCwasapiPlayback_unlock(self);
hr = IAudioRenderClient_ReleaseBuffer(self->render, len, 0);
}
if(FAILED(hr))
{
ERR("Failed to buffer data: 0x%08lx\n", hr);
V0(device->Backend,lock)();
- aluHandleDisconnect(device);
+ aluHandleDisconnect(device, "Failed to send playback samples: 0x%08lx", hr);
V0(device->Backend,unlock)();
break;
}
}
- self->Padding = 0;
+ ATOMIC_STORE(&self->Padding, 0, almemory_order_release);
CoUninitialize();
return 0;
@@ -660,13 +730,12 @@ static ALCboolean MakeExtensible(WAVEFORMATEXTENSIBLE *out, const WAVEFORMATEX *
return ALC_TRUE;
}
-
-static ALCenum ALCmmdevPlayback_open(ALCmmdevPlayback *self, const ALCchar *deviceName)
+static ALCenum ALCwasapiPlayback_open(ALCwasapiPlayback *self, const ALCchar *deviceName)
{
HRESULT hr = S_OK;
- self->NotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
- self->MsgEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ self->NotifyEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
+ self->MsgEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
if(self->NotifyEvent == NULL || self->MsgEvent == NULL)
{
ERR("Failed to create message events: %lu\n", GetLastError());
@@ -687,18 +756,32 @@ static ALCenum ALCmmdevPlayback_open(ALCmmdevPlayback *self, const ALCchar *devi
}
hr = E_FAIL;
-#define MATCH_NAME(i) (al_string_cmp_cstr((i)->name, deviceName) == 0)
+#define MATCH_NAME(i) (alstr_cmp_cstr((i)->name, deviceName) == 0 || \
+ alstr_cmp_cstr((i)->endpoint_guid, deviceName) == 0)
VECTOR_FIND_IF(iter, const DevMap, PlaybackDevices, MATCH_NAME);
- if(iter == VECTOR_ITER_END(PlaybackDevices))
+#undef MATCH_NAME
+ if(iter == VECTOR_END(PlaybackDevices))
+ {
+ int len;
+ if((len=MultiByteToWideChar(CP_UTF8, 0, deviceName, -1, NULL, 0)) > 0)
+ {
+ WCHAR *wname = calloc(sizeof(WCHAR), len);
+ MultiByteToWideChar(CP_UTF8, 0, deviceName, -1, wname, len);
+#define MATCH_NAME(i) (wcscmp((i)->devid, wname) == 0)
+ VECTOR_FIND_IF(iter, const DevMap, PlaybackDevices, MATCH_NAME);
+#undef MATCH_NAME
+ free(wname);
+ }
+ }
+ if(iter == VECTOR_END(PlaybackDevices))
WARN("Failed to find device name matching \"%s\"\n", deviceName);
else
{
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
self->devid = strdupW(iter->devid);
- al_string_copy(&device->DeviceName, iter->name);
+ alstr_copy(&device->DeviceName, iter->name);
hr = S_OK;
}
-#undef MATCH_NAME
}
}
@@ -707,7 +790,7 @@ static ALCenum ALCmmdevPlayback_open(ALCmmdevPlayback *self, const ALCchar *devi
ThreadRequest req = { self->MsgEvent, 0 };
hr = E_FAIL;
- if(PostThreadMessage(ThreadID, WM_USER_OpenDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
+ if(PostThreadMessage(ThreadID, WM_USER_OpenDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCwasapiProxy, self)))
hr = WaitForResponse(&req);
else
ERR("Failed to post thread message: %lu\n", GetLastError());
@@ -732,7 +815,7 @@ static ALCenum ALCmmdevPlayback_open(ALCmmdevPlayback *self, const ALCchar *devi
return ALC_NO_ERROR;
}
-static HRESULT ALCmmdevPlayback_openProxy(ALCmmdevPlayback *self)
+static HRESULT ALCwasapiPlayback_openProxy(ALCwasapiPlayback *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
void *ptr;
@@ -754,11 +837,8 @@ static HRESULT ALCmmdevPlayback_openProxy(ALCmmdevPlayback *self)
if(SUCCEEDED(hr))
{
self->client = ptr;
- if(al_string_empty(device->DeviceName))
- {
- get_device_name(self->mmdev, &device->DeviceName);
- al_string_append_cstr(&device->DeviceName, DEVNAME_TAIL);
- }
+ if(alstr_empty(device->DeviceName))
+ get_device_name_and_guid(self->mmdev, &device->DeviceName, NULL);
}
if(FAILED(hr))
@@ -772,24 +852,7 @@ static HRESULT ALCmmdevPlayback_openProxy(ALCmmdevPlayback *self)
}
-static void ALCmmdevPlayback_close(ALCmmdevPlayback *self)
-{
- ThreadRequest req = { self->MsgEvent, 0 };
-
- if(PostThreadMessage(ThreadID, WM_USER_CloseDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
- (void)WaitForResponse(&req);
-
- CloseHandle(self->MsgEvent);
- self->MsgEvent = NULL;
-
- CloseHandle(self->NotifyEvent);
- self->NotifyEvent = NULL;
-
- free(self->devid);
- self->devid = NULL;
-}
-
-static void ALCmmdevPlayback_closeProxy(ALCmmdevPlayback *self)
+static void ALCwasapiPlayback_closeProxy(ALCwasapiPlayback *self)
{
if(self->client)
IAudioClient_Release(self->client);
@@ -801,18 +864,18 @@ static void ALCmmdevPlayback_closeProxy(ALCmmdevPlayback *self)
}
-static ALCboolean ALCmmdevPlayback_reset(ALCmmdevPlayback *self)
+static ALCboolean ALCwasapiPlayback_reset(ALCwasapiPlayback *self)
{
ThreadRequest req = { self->MsgEvent, 0 };
HRESULT hr = E_FAIL;
- if(PostThreadMessage(ThreadID, WM_USER_ResetDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
+ if(PostThreadMessage(ThreadID, WM_USER_ResetDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCwasapiProxy, self)))
hr = WaitForResponse(&req);
return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE;
}
-static HRESULT ALCmmdevPlayback_resetProxy(ALCmmdevPlayback *self)
+static HRESULT ALCwasapiPlayback_resetProxy(ALCwasapiPlayback *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
EndpointFormFactor formfactor = UnknownFormFactor;
@@ -850,8 +913,8 @@ static HRESULT ALCmmdevPlayback_resetProxy(ALCmmdevPlayback *self)
CoTaskMemFree(wfx);
wfx = NULL;
- buf_time = ((REFERENCE_TIME)device->UpdateSize*device->NumUpdates*10000000 +
- device->Frequency-1) / device->Frequency;
+ buf_time = ScaleCeil(device->UpdateSize*device->NumUpdates, REFTIME_PER_SEC,
+ device->Frequency);
if(!(device->Flags&DEVICE_FREQUENCY_REQUEST))
device->Frequency = OutputType.Format.nSamplesPerSec;
@@ -881,7 +944,7 @@ static HRESULT ALCmmdevPlayback_resetProxy(ALCmmdevPlayback *self)
OutputType.Format.nChannels = 1;
OutputType.dwChannelMask = MONO;
break;
- case DevFmtBFormat3D:
+ case DevFmtAmbi3D:
device->FmtChans = DevFmtStereo;
/*fall-through*/
case DevFmtStereo:
@@ -1022,7 +1085,9 @@ static HRESULT ALCmmdevPlayback_resetProxy(ALCmmdevPlayback *self)
OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
}
get_device_formfactor(self->mmdev, &formfactor);
- device->IsHeadphones = (device->FmtChans == DevFmtStereo && formfactor == Headphones);
+ device->IsHeadphones = (device->FmtChans == DevFmtStereo &&
+ (formfactor == Headphones || formfactor == Headset)
+ );
SetDefaultWFXChannelOrder(device);
@@ -1038,7 +1103,7 @@ static HRESULT ALCmmdevPlayback_resetProxy(ALCmmdevPlayback *self)
hr = IAudioClient_GetDevicePeriod(self->client, &min_per, NULL);
if(SUCCEEDED(hr))
{
- min_len = (UINT32)((min_per*device->Frequency + 10000000-1) / 10000000);
+ min_len = (UINT32)ScaleCeil(min_per, device->Frequency, REFTIME_PER_SEC);
/* Find the nearest multiple of the period size to the update size */
if(min_len < device->UpdateSize)
min_len *= (device->UpdateSize + min_len/2)/min_len;
@@ -1070,18 +1135,18 @@ static HRESULT ALCmmdevPlayback_resetProxy(ALCmmdevPlayback *self)
}
-static ALCboolean ALCmmdevPlayback_start(ALCmmdevPlayback *self)
+static ALCboolean ALCwasapiPlayback_start(ALCwasapiPlayback *self)
{
ThreadRequest req = { self->MsgEvent, 0 };
HRESULT hr = E_FAIL;
- if(PostThreadMessage(ThreadID, WM_USER_StartDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
+ if(PostThreadMessage(ThreadID, WM_USER_StartDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCwasapiProxy, self)))
hr = WaitForResponse(&req);
return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE;
}
-static HRESULT ALCmmdevPlayback_startProxy(ALCmmdevPlayback *self)
+static HRESULT ALCwasapiPlayback_startProxy(ALCwasapiPlayback *self)
{
HRESULT hr;
void *ptr;
@@ -1096,8 +1161,8 @@ static HRESULT ALCmmdevPlayback_startProxy(ALCmmdevPlayback *self)
if(SUCCEEDED(hr))
{
self->render = ptr;
- self->killNow = 0;
- if(althrd_create(&self->thread, ALCmmdevPlayback_mixerProc, self) != althrd_success)
+ ATOMIC_STORE(&self->killNow, 0, almemory_order_release);
+ if(althrd_create(&self->thread, ALCwasapiPlayback_mixerProc, self) != althrd_success)
{
if(self->render)
IAudioRenderClient_Release(self->render);
@@ -1112,21 +1177,21 @@ static HRESULT ALCmmdevPlayback_startProxy(ALCmmdevPlayback *self)
}
-static void ALCmmdevPlayback_stop(ALCmmdevPlayback *self)
+static void ALCwasapiPlayback_stop(ALCwasapiPlayback *self)
{
ThreadRequest req = { self->MsgEvent, 0 };
- if(PostThreadMessage(ThreadID, WM_USER_StopDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
+ if(PostThreadMessage(ThreadID, WM_USER_StopDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCwasapiProxy, self)))
(void)WaitForResponse(&req);
}
-static void ALCmmdevPlayback_stopProxy(ALCmmdevPlayback *self)
+static void ALCwasapiPlayback_stopProxy(ALCwasapiPlayback *self)
{
int res;
if(!self->render)
return;
- self->killNow = 1;
+ ATOMIC_STORE_SEQ(&self->killNow, 1);
althrd_join(self->thread, &res);
IAudioRenderClient_Release(self->render);
@@ -1135,16 +1200,24 @@ static void ALCmmdevPlayback_stopProxy(ALCmmdevPlayback *self)
}
-static ALint64 ALCmmdevPlayback_getLatency(ALCmmdevPlayback *self)
+static ClockLatency ALCwasapiPlayback_getClockLatency(ALCwasapiPlayback *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
- return (ALint64)self->Padding * 1000000000 / device->Frequency;
+ ClockLatency ret;
+
+ ALCwasapiPlayback_lock(self);
+ ret.ClockTime = GetDeviceClockTime(device);
+ ret.Latency = ATOMIC_LOAD(&self->Padding, almemory_order_relaxed) * DEVICE_CLOCK_RES /
+ device->Frequency;
+ ALCwasapiPlayback_unlock(self);
+
+ return ret;
}
-typedef struct ALCmmdevCapture {
+typedef struct ALCwasapiCapture {
DERIVE_FROM_TYPE(ALCbackend);
- DERIVE_FROM_TYPE(ALCmmdevProxy);
+ DERIVE_FROM_TYPE(ALCwasapiProxy);
WCHAR *devid;
@@ -1155,43 +1228,44 @@ typedef struct ALCmmdevCapture {
HANDLE MsgEvent;
+ ChannelConverter *ChannelConv;
+ SampleConverter *SampleConv;
ll_ringbuffer_t *Ring;
- volatile int killNow;
+ ATOMIC(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)
+} ALCwasapiCapture;
+
+static int ALCwasapiCapture_recordProc(void *arg);
+
+static void ALCwasapiCapture_Construct(ALCwasapiCapture *self, ALCdevice *device);
+static void ALCwasapiCapture_Destruct(ALCwasapiCapture *self);
+static ALCenum ALCwasapiCapture_open(ALCwasapiCapture *self, const ALCchar *name);
+static HRESULT ALCwasapiCapture_openProxy(ALCwasapiCapture *self);
+static void ALCwasapiCapture_closeProxy(ALCwasapiCapture *self);
+static DECLARE_FORWARD(ALCwasapiCapture, ALCbackend, ALCboolean, reset)
+static HRESULT ALCwasapiCapture_resetProxy(ALCwasapiCapture *self);
+static ALCboolean ALCwasapiCapture_start(ALCwasapiCapture *self);
+static HRESULT ALCwasapiCapture_startProxy(ALCwasapiCapture *self);
+static void ALCwasapiCapture_stop(ALCwasapiCapture *self);
+static void ALCwasapiCapture_stopProxy(ALCwasapiCapture *self);
+static ALCenum ALCwasapiCapture_captureSamples(ALCwasapiCapture *self, ALCvoid *buffer, ALCuint samples);
+static ALuint ALCwasapiCapture_availableSamples(ALCwasapiCapture *self);
+static DECLARE_FORWARD(ALCwasapiCapture, ALCbackend, ClockLatency, getClockLatency)
+static DECLARE_FORWARD(ALCwasapiCapture, ALCbackend, void, lock)
+static DECLARE_FORWARD(ALCwasapiCapture, ALCbackend, void, unlock)
+DECLARE_DEFAULT_ALLOCATORS(ALCwasapiCapture)
+
+DEFINE_ALCWASAPIPROXY_VTABLE(ALCwasapiCapture);
+DEFINE_ALCBACKEND_VTABLE(ALCwasapiCapture);
+
+
+static void ALCwasapiCapture_Construct(ALCwasapiCapture *self, ALCdevice *device)
{
- SET_VTABLE2(ALCmmdevCapture, ALCbackend, self);
- SET_VTABLE2(ALCmmdevCapture, ALCmmdevProxy, self);
+ SET_VTABLE2(ALCwasapiCapture, ALCbackend, self);
+ SET_VTABLE2(ALCwasapiCapture, ALCwasapiProxy, self);
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
- ALCmmdevProxy_Construct(STATIC_CAST(ALCmmdevProxy, self));
+ ALCwasapiProxy_Construct(STATIC_CAST(ALCwasapiProxy, self));
self->devid = NULL;
@@ -1202,50 +1276,64 @@ static void ALCmmdevCapture_Construct(ALCmmdevCapture *self, ALCdevice *device)
self->MsgEvent = NULL;
+ self->ChannelConv = NULL;
+ self->SampleConv = NULL;
self->Ring = NULL;
- self->killNow = 0;
+ ATOMIC_INIT(&self->killNow, 0);
}
-static void ALCmmdevCapture_Destruct(ALCmmdevCapture *self)
+static void ALCwasapiCapture_Destruct(ALCwasapiCapture *self)
{
- ll_ringbuffer_free(self->Ring);
- self->Ring = NULL;
+ if(self->MsgEvent)
+ {
+ ThreadRequest req = { self->MsgEvent, 0 };
+ if(PostThreadMessage(ThreadID, WM_USER_CloseDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCwasapiProxy, self)))
+ (void)WaitForResponse(&req);
+
+ CloseHandle(self->MsgEvent);
+ self->MsgEvent = NULL;
+ }
if(self->NotifyEvent != NULL)
CloseHandle(self->NotifyEvent);
self->NotifyEvent = NULL;
- if(self->MsgEvent != NULL)
- CloseHandle(self->MsgEvent);
- self->MsgEvent = NULL;
+
+ ll_ringbuffer_free(self->Ring);
+ self->Ring = NULL;
+
+ DestroySampleConverter(&self->SampleConv);
+ DestroyChannelConverter(&self->ChannelConv);
free(self->devid);
self->devid = NULL;
- ALCmmdevProxy_Destruct(STATIC_CAST(ALCmmdevProxy, self));
+ ALCwasapiProxy_Destruct(STATIC_CAST(ALCwasapiProxy, self));
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
}
-FORCE_ALIGN int ALCmmdevCapture_recordProc(void *arg)
+FORCE_ALIGN int ALCwasapiCapture_recordProc(void *arg)
{
- ALCmmdevCapture *self = arg;
+ ALCwasapiCapture *self = arg;
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+ ALfloat *samples = NULL;
+ size_t samplesmax = 0;
HRESULT hr;
- hr = CoInitialize(NULL);
+ hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
if(FAILED(hr))
{
- ERR("CoInitialize(NULL) failed: 0x%08lx\n", hr);
+ ERR("CoInitializeEx(NULL, COINIT_MULTITHREADED) failed: 0x%08lx\n", hr);
V0(device->Backend,lock)();
- aluHandleDisconnect(device);
+ aluHandleDisconnect(device, "COM init failed: 0x%08lx", hr);
V0(device->Backend,unlock)();
return 1;
}
althrd_setname(althrd_current(), RECORD_THREAD_NAME);
- while(!self->killNow)
+ while(!ATOMIC_LOAD(&self->killNow, almemory_order_relaxed))
{
UINT32 avail;
DWORD res;
@@ -1253,39 +1341,81 @@ FORCE_ALIGN int ALCmmdevCapture_recordProc(void *arg)
hr = IAudioCaptureClient_GetNextPacketSize(self->capture, &avail);
if(FAILED(hr))
ERR("Failed to get next packet size: 0x%08lx\n", hr);
- else while(avail > 0 && SUCCEEDED(hr))
+ else if(avail > 0)
{
UINT32 numsamples;
DWORD flags;
- BYTE *data;
+ BYTE *rdata;
hr = IAudioCaptureClient_GetBuffer(self->capture,
- &data, &numsamples, &flags, NULL, NULL
+ &rdata, &numsamples, &flags, NULL, NULL
);
if(FAILED(hr))
- {
ERR("Failed to get capture buffer: 0x%08lx\n", hr);
- break;
- }
-
- ll_ringbuffer_write(self->Ring, (char*)data, numsamples);
-
- hr = IAudioCaptureClient_ReleaseBuffer(self->capture, numsamples);
- if(FAILED(hr))
+ else
{
- ERR("Failed to release capture buffer: 0x%08lx\n", hr);
- break;
+ ll_ringbuffer_data_t data[2];
+ size_t dstframes = 0;
+
+ if(self->ChannelConv)
+ {
+ if(samplesmax < numsamples)
+ {
+ size_t newmax = RoundUp(numsamples, 4096);
+ ALfloat *tmp = al_calloc(DEF_ALIGN, newmax*2*sizeof(ALfloat));
+ al_free(samples);
+ samples = tmp;
+ samplesmax = newmax;
+ }
+ ChannelConverterInput(self->ChannelConv, rdata, samples, numsamples);
+ rdata = (BYTE*)samples;
+ }
+
+ ll_ringbuffer_get_write_vector(self->Ring, data);
+
+ if(self->SampleConv)
+ {
+ const ALvoid *srcdata = rdata;
+ ALsizei srcframes = numsamples;
+
+ dstframes = SampleConverterInput(self->SampleConv,
+ &srcdata, &srcframes, data[0].buf, (ALsizei)minz(data[0].len, INT_MAX)
+ );
+ if(srcframes > 0 && dstframes == data[0].len && data[1].len > 0)
+ {
+ /* If some source samples remain, all of the first dest
+ * block was filled, and there's space in the second
+ * dest block, do another run for the second block.
+ */
+ dstframes += SampleConverterInput(self->SampleConv,
+ &srcdata, &srcframes, data[1].buf, (ALsizei)minz(data[1].len, INT_MAX)
+ );
+ }
+ }
+ else
+ {
+ ALuint framesize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType,
+ device->AmbiOrder);
+ size_t len1 = minz(data[0].len, numsamples);
+ size_t len2 = minz(data[1].len, numsamples-len1);
+
+ memcpy(data[0].buf, rdata, len1*framesize);
+ if(len2 > 0)
+ memcpy(data[1].buf, rdata+len1*framesize, len2*framesize);
+ dstframes = len1 + len2;
+ }
+
+ ll_ringbuffer_write_advance(self->Ring, dstframes);
+
+ hr = IAudioCaptureClient_ReleaseBuffer(self->capture, numsamples);
+ if(FAILED(hr)) ERR("Failed to release capture buffer: 0x%08lx\n", hr);
}
-
- hr = IAudioCaptureClient_GetNextPacketSize(self->capture, &avail);
- if(FAILED(hr))
- ERR("Failed to get next packet size: 0x%08lx\n", hr);
}
if(FAILED(hr))
{
V0(device->Backend,lock)();
- aluHandleDisconnect(device);
+ aluHandleDisconnect(device, "Failed to capture samples: 0x%08lx", hr);
V0(device->Backend,unlock)();
break;
}
@@ -1295,17 +1425,21 @@ FORCE_ALIGN int ALCmmdevCapture_recordProc(void *arg)
ERR("WaitForSingleObjectEx error: 0x%lx\n", res);
}
+ al_free(samples);
+ samples = NULL;
+ samplesmax = 0;
+
CoUninitialize();
return 0;
}
-static ALCenum ALCmmdevCapture_open(ALCmmdevCapture *self, const ALCchar *deviceName)
+static ALCenum ALCwasapiCapture_open(ALCwasapiCapture *self, const ALCchar *deviceName)
{
HRESULT hr = S_OK;
- self->NotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
- self->MsgEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ self->NotifyEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
+ self->MsgEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
if(self->NotifyEvent == NULL || self->MsgEvent == NULL)
{
ERR("Failed to create message events: %lu\n", GetLastError());
@@ -1326,18 +1460,32 @@ static ALCenum ALCmmdevCapture_open(ALCmmdevCapture *self, const ALCchar *device
}
hr = E_FAIL;
-#define MATCH_NAME(i) (al_string_cmp_cstr((i)->name, deviceName) == 0)
+#define MATCH_NAME(i) (alstr_cmp_cstr((i)->name, deviceName) == 0 || \
+ alstr_cmp_cstr((i)->endpoint_guid, deviceName) == 0)
VECTOR_FIND_IF(iter, const DevMap, CaptureDevices, MATCH_NAME);
- if(iter == VECTOR_ITER_END(CaptureDevices))
+#undef MATCH_NAME
+ if(iter == VECTOR_END(CaptureDevices))
+ {
+ int len;
+ if((len=MultiByteToWideChar(CP_UTF8, 0, deviceName, -1, NULL, 0)) > 0)
+ {
+ WCHAR *wname = calloc(sizeof(WCHAR), len);
+ MultiByteToWideChar(CP_UTF8, 0, deviceName, -1, wname, len);
+#define MATCH_NAME(i) (wcscmp((i)->devid, wname) == 0)
+ VECTOR_FIND_IF(iter, const DevMap, CaptureDevices, MATCH_NAME);
+#undef MATCH_NAME
+ free(wname);
+ }
+ }
+ if(iter == VECTOR_END(CaptureDevices))
WARN("Failed to find device name matching \"%s\"\n", deviceName);
else
{
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
self->devid = strdupW(iter->devid);
- al_string_copy(&device->DeviceName, iter->name);
+ alstr_copy(&device->DeviceName, iter->name);
hr = S_OK;
}
-#undef MATCH_NAME
}
}
@@ -1346,7 +1494,7 @@ static ALCenum ALCmmdevCapture_open(ALCmmdevCapture *self, const ALCchar *device
ThreadRequest req = { self->MsgEvent, 0 };
hr = E_FAIL;
- if(PostThreadMessage(ThreadID, WM_USER_OpenDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
+ if(PostThreadMessage(ThreadID, WM_USER_OpenDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCwasapiProxy, self)))
hr = WaitForResponse(&req);
else
ERR("Failed to post thread message: %lu\n", GetLastError());
@@ -1372,14 +1520,13 @@ static ALCenum ALCmmdevCapture_open(ALCmmdevCapture *self, const ALCchar *device
ThreadRequest req = { self->MsgEvent, 0 };
hr = E_FAIL;
- if(PostThreadMessage(ThreadID, WM_USER_ResetDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
+ if(PostThreadMessage(ThreadID, WM_USER_ResetDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCwasapiProxy, 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;
@@ -1389,7 +1536,7 @@ static ALCenum ALCmmdevCapture_open(ALCmmdevCapture *self, const ALCchar *device
return ALC_NO_ERROR;
}
-static HRESULT ALCmmdevCapture_openProxy(ALCmmdevCapture *self)
+static HRESULT ALCwasapiCapture_openProxy(ALCwasapiCapture *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
void *ptr;
@@ -1411,11 +1558,8 @@ static HRESULT ALCmmdevCapture_openProxy(ALCmmdevCapture *self)
if(SUCCEEDED(hr))
{
self->client = ptr;
- if(al_string_empty(device->DeviceName))
- {
- get_device_name(self->mmdev, &device->DeviceName);
- al_string_append_cstr(&device->DeviceName, DEVNAME_TAIL);
- }
+ if(alstr_empty(device->DeviceName))
+ get_device_name_and_guid(self->mmdev, &device->DeviceName, NULL);
}
if(FAILED(hr))
@@ -1429,27 +1573,7 @@ static HRESULT ALCmmdevCapture_openProxy(ALCmmdevCapture *self)
}
-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)
+static void ALCwasapiCapture_closeProxy(ALCwasapiCapture *self)
{
if(self->client)
IAudioClient_Release(self->client);
@@ -1461,11 +1585,12 @@ static void ALCmmdevCapture_closeProxy(ALCmmdevCapture *self)
}
-static HRESULT ALCmmdevCapture_resetProxy(ALCmmdevCapture *self)
+static HRESULT ALCwasapiCapture_resetProxy(ALCwasapiCapture *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
WAVEFORMATEXTENSIBLE OutputType;
WAVEFORMATEX *wfx = NULL;
+ enum DevFmtType srcType;
REFERENCE_TIME buf_time;
UINT32 buffer_len;
void *ptr = NULL;
@@ -1483,8 +1608,12 @@ static HRESULT ALCmmdevCapture_resetProxy(ALCmmdevCapture *self)
}
self->client = ptr;
- buf_time = ((REFERENCE_TIME)device->UpdateSize*device->NumUpdates*10000000 +
- device->Frequency-1) / device->Frequency;
+ buf_time = ScaleCeil(device->UpdateSize*device->NumUpdates, REFTIME_PER_SEC,
+ device->Frequency);
+ // Make sure buffer is at least 100ms in size
+ buf_time = maxu64(buf_time, REFTIME_PER_SEC/10);
+ device->UpdateSize = (ALuint)ScaleCeil(buf_time, device->Frequency, REFTIME_PER_SEC) /
+ device->NumUpdates;
OutputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
switch(device->FmtChans)
@@ -1518,38 +1647,33 @@ static HRESULT ALCmmdevCapture_resetProxy(ALCmmdevCapture *self)
OutputType.dwChannelMask = X7DOT1;
break;
- case DevFmtBFormat3D:
+ case DevFmtAmbi3D:
return E_FAIL;
}
switch(device->FmtType)
{
+ /* NOTE: Signedness doesn't matter, the converter will handle it. */
+ case DevFmtByte:
case DevFmtUByte:
OutputType.Format.wBitsPerSample = 8;
- OutputType.Samples.wValidBitsPerSample = 8;
OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
break;
case DevFmtShort:
+ case DevFmtUShort:
OutputType.Format.wBitsPerSample = 16;
- OutputType.Samples.wValidBitsPerSample = 16;
OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
break;
case DevFmtInt:
+ case DevFmtUInt:
OutputType.Format.wBitsPerSample = 32;
- OutputType.Samples.wValidBitsPerSample = 32;
OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
break;
case DevFmtFloat:
OutputType.Format.wBitsPerSample = 32;
- OutputType.Samples.wValidBitsPerSample = 32;
OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
break;
-
- case DevFmtByte:
- case DevFmtUShort:
- case DevFmtUInt:
- WARN("%s capture samples not supported\n", DevFmtTypeString(device->FmtType));
- return E_FAIL;
}
+ OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
OutputType.Format.nSamplesPerSec = device->Frequency;
OutputType.Format.nBlockAlign = OutputType.Format.nChannels *
@@ -1567,26 +1691,107 @@ static HRESULT ALCmmdevCapture_resetProxy(ALCmmdevCapture *self)
return hr;
}
- /* FIXME: We should do conversion/resampling if we didn't get a matching format. */
- if(wfx->nSamplesPerSec != OutputType.Format.nSamplesPerSec ||
- wfx->wBitsPerSample != OutputType.Format.wBitsPerSample ||
- wfx->nChannels != OutputType.Format.nChannels ||
- wfx->nBlockAlign != OutputType.Format.nBlockAlign)
+ DestroySampleConverter(&self->SampleConv);
+ DestroyChannelConverter(&self->ChannelConv);
+
+ if(wfx != NULL)
{
- ERR("Did not get matching format, wanted: %s %s %uhz, got: %d channel(s) %d-bit %luhz\n",
- DevFmtChannelsString(device->FmtChans), DevFmtTypeString(device->FmtType), device->Frequency,
- wfx->nChannels, wfx->wBitsPerSample, wfx->nSamplesPerSec);
+ if(!(wfx->nChannels == OutputType.Format.nChannels ||
+ (wfx->nChannels == 1 && OutputType.Format.nChannels == 2) ||
+ (wfx->nChannels == 2 && OutputType.Format.nChannels == 1)))
+ {
+ ERR("Failed to get matching format, wanted: %s %s %uhz, got: %d channel%s %d-bit %luhz\n",
+ DevFmtChannelsString(device->FmtChans), DevFmtTypeString(device->FmtType),
+ device->Frequency, wfx->nChannels, (wfx->nChannels==1)?"":"s", wfx->wBitsPerSample,
+ wfx->nSamplesPerSec);
+ CoTaskMemFree(wfx);
+ return E_FAIL;
+ }
+
+ if(!MakeExtensible(&OutputType, wfx))
+ {
+ CoTaskMemFree(wfx);
+ return E_FAIL;
+ }
CoTaskMemFree(wfx);
- return E_FAIL;
+ wfx = NULL;
}
- if(!MakeExtensible(&OutputType, wfx))
+ if(IsEqualGUID(&OutputType.SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))
{
- CoTaskMemFree(wfx);
+ if(OutputType.Format.wBitsPerSample == 8)
+ srcType = DevFmtUByte;
+ else if(OutputType.Format.wBitsPerSample == 16)
+ srcType = DevFmtShort;
+ else if(OutputType.Format.wBitsPerSample == 32)
+ srcType = DevFmtInt;
+ else
+ {
+ ERR("Unhandled integer bit depth: %d\n", OutputType.Format.wBitsPerSample);
+ return E_FAIL;
+ }
+ }
+ else if(IsEqualGUID(&OutputType.SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))
+ {
+ if(OutputType.Format.wBitsPerSample == 32)
+ srcType = DevFmtFloat;
+ else
+ {
+ ERR("Unhandled float bit depth: %d\n", OutputType.Format.wBitsPerSample);
+ return E_FAIL;
+ }
+ }
+ else
+ {
+ ERR("Unhandled format sub-type\n");
return E_FAIL;
}
- CoTaskMemFree(wfx);
- wfx = NULL;
+
+ if(device->FmtChans == DevFmtMono && OutputType.Format.nChannels == 2)
+ {
+ self->ChannelConv = CreateChannelConverter(srcType, DevFmtStereo,
+ device->FmtChans);
+ if(!self->ChannelConv)
+ {
+ ERR("Failed to create %s stereo-to-mono converter\n", DevFmtTypeString(srcType));
+ return E_FAIL;
+ }
+ TRACE("Created %s stereo-to-mono converter\n", DevFmtTypeString(srcType));
+ /* The channel converter always outputs float, so change the input type
+ * for the resampler/type-converter.
+ */
+ srcType = DevFmtFloat;
+ }
+ else if(device->FmtChans == DevFmtStereo && OutputType.Format.nChannels == 1)
+ {
+ self->ChannelConv = CreateChannelConverter(srcType, DevFmtMono,
+ device->FmtChans);
+ if(!self->ChannelConv)
+ {
+ ERR("Failed to create %s mono-to-stereo converter\n", DevFmtTypeString(srcType));
+ return E_FAIL;
+ }
+ TRACE("Created %s mono-to-stereo converter\n", DevFmtTypeString(srcType));
+ srcType = DevFmtFloat;
+ }
+
+ if(device->Frequency != OutputType.Format.nSamplesPerSec || device->FmtType != srcType)
+ {
+ self->SampleConv = CreateSampleConverter(
+ srcType, device->FmtType, ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder),
+ OutputType.Format.nSamplesPerSec, device->Frequency
+ );
+ if(!self->SampleConv)
+ {
+ ERR("Failed to create converter for %s format, dst: %s %uhz, src: %s %luhz\n",
+ DevFmtChannelsString(device->FmtChans), DevFmtTypeString(device->FmtType),
+ device->Frequency, DevFmtTypeString(srcType), OutputType.Format.nSamplesPerSec);
+ return E_FAIL;
+ }
+ TRACE("Created converter for %s format, dst: %s %uhz, src: %s %luhz\n",
+ DevFmtChannelsString(device->FmtChans), DevFmtTypeString(device->FmtType),
+ device->Frequency, DevFmtTypeString(srcType), OutputType.Format.nSamplesPerSec);
+ }
hr = IAudioClient_Initialize(self->client,
AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
@@ -1605,9 +1810,12 @@ static HRESULT ALCmmdevCapture_resetProxy(ALCmmdevCapture *self)
return hr;
}
- buffer_len = maxu(device->UpdateSize*device->NumUpdates + 1, buffer_len);
+ buffer_len = maxu(device->UpdateSize*device->NumUpdates, buffer_len);
ll_ringbuffer_free(self->Ring);
- self->Ring = ll_ringbuffer_create(buffer_len, OutputType.Format.nBlockAlign);
+ self->Ring = ll_ringbuffer_create(buffer_len,
+ FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder),
+ false
+ );
if(!self->Ring)
{
ERR("Failed to allocate capture ring buffer\n");
@@ -1625,18 +1833,18 @@ static HRESULT ALCmmdevCapture_resetProxy(ALCmmdevCapture *self)
}
-static ALCboolean ALCmmdevCapture_start(ALCmmdevCapture *self)
+static ALCboolean ALCwasapiCapture_start(ALCwasapiCapture *self)
{
ThreadRequest req = { self->MsgEvent, 0 };
HRESULT hr = E_FAIL;
- if(PostThreadMessage(ThreadID, WM_USER_StartDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
+ if(PostThreadMessage(ThreadID, WM_USER_StartDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCwasapiProxy, self)))
hr = WaitForResponse(&req);
return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE;
}
-static HRESULT ALCmmdevCapture_startProxy(ALCmmdevCapture *self)
+static HRESULT ALCwasapiCapture_startProxy(ALCwasapiCapture *self)
{
HRESULT hr;
void *ptr;
@@ -1653,8 +1861,8 @@ static HRESULT ALCmmdevCapture_startProxy(ALCmmdevCapture *self)
if(SUCCEEDED(hr))
{
self->capture = ptr;
- self->killNow = 0;
- if(althrd_create(&self->thread, ALCmmdevCapture_recordProc, self) != althrd_success)
+ ATOMIC_STORE(&self->killNow, 0, almemory_order_release);
+ if(althrd_create(&self->thread, ALCwasapiCapture_recordProc, self) != althrd_success)
{
ERR("Failed to start thread\n");
IAudioCaptureClient_Release(self->capture);
@@ -1673,21 +1881,21 @@ static HRESULT ALCmmdevCapture_startProxy(ALCmmdevCapture *self)
}
-static void ALCmmdevCapture_stop(ALCmmdevCapture *self)
+static void ALCwasapiCapture_stop(ALCwasapiCapture *self)
{
ThreadRequest req = { self->MsgEvent, 0 };
- if(PostThreadMessage(ThreadID, WM_USER_StopDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
+ if(PostThreadMessage(ThreadID, WM_USER_StopDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCwasapiProxy, self)))
(void)WaitForResponse(&req);
}
-static void ALCmmdevCapture_stopProxy(ALCmmdevCapture *self)
+static void ALCwasapiCapture_stopProxy(ALCwasapiCapture *self)
{
int res;
if(!self->capture)
return;
- self->killNow = 1;
+ ATOMIC_STORE_SEQ(&self->killNow, 1);
althrd_join(self->thread, &res);
IAudioCaptureClient_Release(self->capture);
@@ -1697,72 +1905,62 @@ static void ALCmmdevCapture_stopProxy(ALCmmdevCapture *self)
}
-ALuint ALCmmdevCapture_availableSamples(ALCmmdevCapture *self)
+ALuint ALCwasapiCapture_availableSamples(ALCwasapiCapture *self)
{
return (ALuint)ll_ringbuffer_read_space(self->Ring);
}
-ALCenum ALCmmdevCapture_captureSamples(ALCmmdevCapture *self, ALCvoid *buffer, ALCuint samples)
+ALCenum ALCwasapiCapture_captureSamples(ALCwasapiCapture *self, ALCvoid *buffer, ALCuint samples)
{
- if(ALCmmdevCapture_availableSamples(self) < samples)
+ if(ALCwasapiCapture_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)
-{ AppendCaptureDeviceList(al_string_get_cstr(entry->name)); }
-
-typedef struct ALCmmdevBackendFactory {
+typedef struct ALCwasapiBackendFactory {
DERIVE_FROM_TYPE(ALCbackendFactory);
-} ALCmmdevBackendFactory;
-#define ALCMMDEVBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCmmdevBackendFactory, ALCbackendFactory) } }
+} ALCwasapiBackendFactory;
+#define ALCWASAPIBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCwasapiBackendFactory, ALCbackendFactory) } }
-static ALCboolean ALCmmdevBackendFactory_init(ALCmmdevBackendFactory *self);
-static void ALCmmdevBackendFactory_deinit(ALCmmdevBackendFactory *self);
-static ALCboolean ALCmmdevBackendFactory_querySupport(ALCmmdevBackendFactory *self, ALCbackend_Type type);
-static void ALCmmdevBackendFactory_probe(ALCmmdevBackendFactory *self, enum DevProbe type);
-static ALCbackend* ALCmmdevBackendFactory_createBackend(ALCmmdevBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
+static ALCboolean ALCwasapiBackendFactory_init(ALCwasapiBackendFactory *self);
+static void ALCwasapiBackendFactory_deinit(ALCwasapiBackendFactory *self);
+static ALCboolean ALCwasapiBackendFactory_querySupport(ALCwasapiBackendFactory *self, ALCbackend_Type type);
+static void ALCwasapiBackendFactory_probe(ALCwasapiBackendFactory *self, enum DevProbe type, al_string *outnames);
+static ALCbackend* ALCwasapiBackendFactory_createBackend(ALCwasapiBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
-DEFINE_ALCBACKENDFACTORY_VTABLE(ALCmmdevBackendFactory);
+DEFINE_ALCBACKENDFACTORY_VTABLE(ALCwasapiBackendFactory);
-static BOOL MMDevApiLoad(void)
+static ALCboolean ALCwasapiBackendFactory_init(ALCwasapiBackendFactory* UNUSED(self))
{
static HRESULT InitResult;
+
+ VECTOR_INIT(PlaybackDevices);
+ VECTOR_INIT(CaptureDevices);
+
if(!ThreadHdl)
{
ThreadRequest req;
InitResult = E_FAIL;
- req.FinishedEvt = CreateEvent(NULL, FALSE, FALSE, NULL);
+ req.FinishedEvt = CreateEventW(NULL, FALSE, FALSE, NULL);
if(req.FinishedEvt == NULL)
ERR("Failed to create event: %lu\n", GetLastError());
else
{
- ThreadHdl = CreateThread(NULL, 0, ALCmmdevProxy_messageHandler, &req, 0, &ThreadID);
+ ThreadHdl = CreateThread(NULL, 0, ALCwasapiProxy_messageHandler, &req, 0, &ThreadID);
if(ThreadHdl != NULL)
InitResult = WaitForResponse(&req);
CloseHandle(req.FinishedEvt);
}
}
- return SUCCEEDED(InitResult);
-}
-static ALCboolean ALCmmdevBackendFactory_init(ALCmmdevBackendFactory* UNUSED(self))
-{
- VECTOR_INIT(PlaybackDevices);
- VECTOR_INIT(CaptureDevices);
-
- if(!MMDevApiLoad())
- return ALC_FALSE;
- return ALC_TRUE;
+ return SUCCEEDED(InitResult) ? ALC_TRUE : ALC_FALSE;
}
-static void ALCmmdevBackendFactory_deinit(ALCmmdevBackendFactory* UNUSED(self))
+static void ALCwasapiBackendFactory_deinit(ALCwasapiBackendFactory* UNUSED(self))
{
clear_devlist(&PlaybackDevices);
VECTOR_DEINIT(PlaybackDevices);
@@ -1779,23 +1977,18 @@ static void ALCmmdevBackendFactory_deinit(ALCmmdevBackendFactory* UNUSED(self))
}
}
-static ALCboolean ALCmmdevBackendFactory_querySupport(ALCmmdevBackendFactory* UNUSED(self), ALCbackend_Type type)
+static ALCboolean ALCwasapiBackendFactory_querySupport(ALCwasapiBackendFactory* UNUSED(self), ALCbackend_Type type)
{
- /* 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*/)
+ if(type == ALCbackend_Playback || type == ALCbackend_Capture)
return ALC_TRUE;
return ALC_FALSE;
}
-static void ALCmmdevBackendFactory_probe(ALCmmdevBackendFactory* UNUSED(self), enum DevProbe type)
+static void ALCwasapiBackendFactory_probe(ALCwasapiBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
{
ThreadRequest req = { NULL, 0 };
- req.FinishedEvt = CreateEvent(NULL, FALSE, FALSE, NULL);
+ req.FinishedEvt = CreateEventW(NULL, FALSE, FALSE, NULL);
if(req.FinishedEvt == NULL)
ERR("Failed to create event: %lu\n", GetLastError());
else
@@ -1805,32 +1998,38 @@ static void ALCmmdevBackendFactory_probe(ALCmmdevBackendFactory* UNUSED(self), e
hr = WaitForResponse(&req);
if(SUCCEEDED(hr)) switch(type)
{
+#define APPEND_OUTNAME(e) do { \
+ if(!alstr_empty((e)->name)) \
+ alstr_append_range(outnames, VECTOR_BEGIN((e)->name), \
+ VECTOR_END((e)->name)+1); \
+} while(0)
case ALL_DEVICE_PROBE:
- VECTOR_FOR_EACH(const DevMap, PlaybackDevices, AppendAllDevicesList2);
+ VECTOR_FOR_EACH(const DevMap, PlaybackDevices, APPEND_OUTNAME);
break;
case CAPTURE_DEVICE_PROBE:
- VECTOR_FOR_EACH(const DevMap, CaptureDevices, AppendCaptureDeviceList2);
+ VECTOR_FOR_EACH(const DevMap, CaptureDevices, APPEND_OUTNAME);
break;
+#undef APPEND_OUTNAME
}
CloseHandle(req.FinishedEvt);
req.FinishedEvt = NULL;
}
}
-static ALCbackend* ALCmmdevBackendFactory_createBackend(ALCmmdevBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
+static ALCbackend* ALCwasapiBackendFactory_createBackend(ALCwasapiBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
{
if(type == ALCbackend_Playback)
{
- ALCmmdevPlayback *backend;
- NEW_OBJ(backend, ALCmmdevPlayback)(device);
+ ALCwasapiPlayback *backend;
+ NEW_OBJ(backend, ALCwasapiPlayback)(device);
if(!backend) return NULL;
return STATIC_CAST(ALCbackend, backend);
}
if(type == ALCbackend_Capture)
{
- ALCmmdevCapture *backend;
- NEW_OBJ(backend, ALCmmdevCapture)(device);
+ ALCwasapiCapture *backend;
+ NEW_OBJ(backend, ALCwasapiCapture)(device);
if(!backend) return NULL;
return STATIC_CAST(ALCbackend, backend);
}
@@ -1839,8 +2038,8 @@ static ALCbackend* ALCmmdevBackendFactory_createBackend(ALCmmdevBackendFactory*
}
-ALCbackendFactory *ALCmmdevBackendFactory_getFactory(void)
+ALCbackendFactory *ALCwasapiBackendFactory_getFactory(void)
{
- static ALCmmdevBackendFactory factory = ALCMMDEVBACKENDFACTORY_INITIALIZER;
+ static ALCwasapiBackendFactory factory = ALCWASAPIBACKENDFACTORY_INITIALIZER;
return STATIC_CAST(ALCbackendFactory, &factory);
}
diff --git a/Alc/backends/wave.c b/Alc/backends/wave.c
index 6b47c611..390b2a5f 100644
--- a/Alc/backends/wave.c
+++ b/Alc/backends/wave.c
@@ -27,6 +27,7 @@
#include "alMain.h"
#include "alu.h"
+#include "alconfig.h"
#include "threads.h"
#include "compat.h"
@@ -56,16 +57,14 @@ static const ALubyte SUBTYPE_BFORMAT_FLOAT[] = {
static void fwrite16le(ALushort val, FILE *f)
{
- fputc(val&0xff, f);
- fputc((val>>8)&0xff, f);
+ ALubyte data[2] = { val&0xff, (val>>8)&0xff };
+ fwrite(data, 1, 2, f);
}
static void fwrite32le(ALuint val, FILE *f)
{
- fputc(val&0xff, f);
- fputc((val>>8)&0xff, f);
- fputc((val>>16)&0xff, f);
- fputc((val>>24)&0xff, f);
+ ALubyte data[4] = { val&0xff, (val>>8)&0xff, (val>>16)&0xff, (val>>24)&0xff };
+ fwrite(data, 1, 4, f);
}
@@ -78,22 +77,21 @@ typedef struct ALCwaveBackend {
ALvoid *mBuffer;
ALuint mSize;
- volatile int killNow;
+ ATOMIC(ALenum) 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 void ALCwaveBackend_Destruct(ALCwaveBackend *self);
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, ClockLatency, getClockLatency)
static DECLARE_FORWARD(ALCwaveBackend, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCwaveBackend, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCwaveBackend)
@@ -112,9 +110,17 @@ static void ALCwaveBackend_Construct(ALCwaveBackend *self, ALCdevice *device)
self->mBuffer = NULL;
self->mSize = 0;
- self->killNow = 1;
+ ATOMIC_INIT(&self->killNow, AL_TRUE);
}
+static void ALCwaveBackend_Destruct(ALCwaveBackend *self)
+{
+ if(self->mFile)
+ fclose(self->mFile);
+ self->mFile = NULL;
+
+ ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
+}
static int ALCwaveBackend_mixerProc(void *ptr)
{
@@ -129,7 +135,7 @@ static int ALCwaveBackend_mixerProc(void *ptr)
althrd_setname(althrd_current(), MIXER_THREAD_NAME);
- frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
+ frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
done = 0;
if(altimespec_get(&start, AL_TIME_UTC) != AL_TIME_UTC)
@@ -137,7 +143,8 @@ static int ALCwaveBackend_mixerProc(void *ptr)
ERR("Failed to get starting time\n");
return 1;
}
- while(!self->killNow && device->Connected)
+ while(!ATOMIC_LOAD(&self->killNow, almemory_order_acquire) &&
+ ATOMIC_LOAD(&device->Connected, almemory_order_acquire))
{
if(altimespec_get(&now, AL_TIME_UTC) != AL_TIME_UTC)
{
@@ -159,42 +166,46 @@ static int ALCwaveBackend_mixerProc(void *ptr)
al_nssleep(restTime);
else while(avail-done >= device->UpdateSize)
{
+ ALCwaveBackend_lock(self);
aluMixData(device, self->mBuffer, device->UpdateSize);
+ ALCwaveBackend_unlock(self);
done += device->UpdateSize;
if(!IS_LITTLE_ENDIAN)
{
ALuint bytesize = BytesFromDevFmt(device->FmtType);
- ALubyte *bytes = self->mBuffer;
ALuint i;
- if(bytesize == 1)
- {
- for(i = 0;i < self->mSize;i++)
- fputc(bytes[i], self->mFile);
- }
- else if(bytesize == 2)
+ if(bytesize == 2)
{
- for(i = 0;i < self->mSize;i++)
- fputc(bytes[i^1], self->mFile);
+ ALushort *samples = self->mBuffer;
+ ALuint len = self->mSize / 2;
+ for(i = 0;i < len;i++)
+ {
+ ALushort samp = samples[i];
+ samples[i] = (samp>>8) | (samp<<8);
+ }
}
else if(bytesize == 4)
{
- for(i = 0;i < self->mSize;i++)
- fputc(bytes[i^3], self->mFile);
+ ALuint *samples = self->mBuffer;
+ ALuint len = self->mSize / 4;
+ for(i = 0;i < len;i++)
+ {
+ ALuint samp = samples[i];
+ samples[i] = (samp>>24) | ((samp>>8)&0x0000ff00) |
+ ((samp<<8)&0x00ff0000) | (samp<<24);
+ }
}
}
- else
- {
- fs = fwrite(self->mBuffer, frameSize, device->UpdateSize,
- self->mFile);
- (void)fs;
- }
+
+ fs = fwrite(self->mBuffer, frameSize, device->UpdateSize, self->mFile);
+ (void)fs;
if(ferror(self->mFile))
{
ERR("Error writing to file\n");
ALCdevice_Lock(device);
- aluHandleDisconnect(device);
+ aluHandleDisconnect(device, "Failed to write playback samples");
ALCdevice_Unlock(device);
break;
}
@@ -226,18 +237,11 @@ static ALCenum ALCwaveBackend_open(ALCwaveBackend *self, const ALCchar *name)
}
device = STATIC_CAST(ALCbackend, self)->mDevice;
- al_string_copy_cstr(&device->DeviceName, name);
+ alstr_copy_cstr(&device->DeviceName, name);
return ALC_NO_ERROR;
}
-static void ALCwaveBackend_close(ALCwaveBackend *self)
-{
- if(self->mFile)
- fclose(self->mFile);
- self->mFile = NULL;
-}
-
static ALCboolean ALCwaveBackend_reset(ALCwaveBackend *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
@@ -249,7 +253,10 @@ static ALCboolean ALCwaveBackend_reset(ALCwaveBackend *self)
clearerr(self->mFile);
if(GetConfigValueBool(NULL, "wave", "bformat", 0))
- device->FmtChans = DevFmtBFormat3D;
+ {
+ device->FmtChans = DevFmtAmbi3D;
+ device->AmbiOrder = 1;
+ }
switch(device->FmtType)
{
@@ -277,20 +284,23 @@ static ALCboolean ALCwaveBackend_reset(ALCwaveBackend *self)
case DevFmtX51Rear: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x010 | 0x020; break;
case DevFmtX61: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x100 | 0x200 | 0x400; break;
case DevFmtX71: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x010 | 0x020 | 0x200 | 0x400; break;
- case DevFmtBFormat3D:
+ case DevFmtAmbi3D:
+ /* .amb output requires FuMa */
+ device->AmbiLayout = AmbiLayout_FuMa;
+ device->AmbiScale = AmbiNorm_FuMa;
isbformat = 1;
chanmask = 0;
break;
}
bits = BytesFromDevFmt(device->FmtType) * 8;
- channels = ChannelsFromDevFmt(device->FmtChans);
+ channels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
- fprintf(self->mFile, "RIFF");
+ fputs("RIFF", self->mFile);
fwrite32le(0xFFFFFFFF, self->mFile); // 'RIFF' header len; filled in at close
- fprintf(self->mFile, "WAVE");
+ fputs("WAVE", self->mFile);
- fprintf(self->mFile, "fmt ");
+ fputs("fmt ", self->mFile);
fwrite32le(40, self->mFile); // 'fmt ' header len; 40 bytes for EXTENSIBLE
// 16-bit val, format type id (extensible: 0xFFFE)
@@ -312,11 +322,12 @@ static ALCboolean ALCwaveBackend_reset(ALCwaveBackend *self)
// 32-bit val, channel mask
fwrite32le(chanmask, self->mFile);
// 16 byte GUID, sub-type format
- val = fwrite(((bits==32) ? (isbformat ? SUBTYPE_BFORMAT_FLOAT : SUBTYPE_FLOAT) :
- (isbformat ? SUBTYPE_BFORMAT_PCM : SUBTYPE_PCM)), 1, 16, self->mFile);
+ val = fwrite((device->FmtType == DevFmtFloat) ?
+ (isbformat ? SUBTYPE_BFORMAT_FLOAT : SUBTYPE_FLOAT) :
+ (isbformat ? SUBTYPE_BFORMAT_PCM : SUBTYPE_PCM), 1, 16, self->mFile);
(void)val;
- fprintf(self->mFile, "data");
+ fputs("data", self->mFile);
fwrite32le(0xFFFFFFFF, self->mFile); // 'data' header len; filled in at close
if(ferror(self->mFile))
@@ -335,7 +346,9 @@ static ALCboolean ALCwaveBackend_start(ALCwaveBackend *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
- self->mSize = device->UpdateSize * FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
+ self->mSize = device->UpdateSize * FrameSizeFromDevFmt(
+ device->FmtChans, device->FmtType, device->AmbiOrder
+ );
self->mBuffer = malloc(self->mSize);
if(!self->mBuffer)
{
@@ -343,7 +356,7 @@ static ALCboolean ALCwaveBackend_start(ALCwaveBackend *self)
return ALC_FALSE;
}
- self->killNow = 0;
+ ATOMIC_STORE(&self->killNow, AL_FALSE, almemory_order_release);
if(althrd_create(&self->thread, ALCwaveBackend_mixerProc, self) != althrd_success)
{
free(self->mBuffer);
@@ -361,10 +374,8 @@ static void ALCwaveBackend_stop(ALCwaveBackend *self)
long size;
int res;
- if(self->killNow)
+ if(ATOMIC_EXCHANGE(&self->killNow, AL_TRUE, almemory_order_acq_rel))
return;
-
- self->killNow = 1;
althrd_join(self->thread, &res);
free(self->mBuffer);
@@ -392,7 +403,7 @@ ALCbackendFactory *ALCwaveBackendFactory_getFactory(void);
static ALCboolean ALCwaveBackendFactory_init(ALCwaveBackendFactory *self);
static DECLARE_FORWARD(ALCwaveBackendFactory, ALCbackendFactory, void, deinit)
static ALCboolean ALCwaveBackendFactory_querySupport(ALCwaveBackendFactory *self, ALCbackend_Type type);
-static void ALCwaveBackendFactory_probe(ALCwaveBackendFactory *self, enum DevProbe type);
+static void ALCwaveBackendFactory_probe(ALCwaveBackendFactory *self, enum DevProbe type, al_string *outnames);
static ALCbackend* ALCwaveBackendFactory_createBackend(ALCwaveBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCwaveBackendFactory);
@@ -416,12 +427,12 @@ static ALCboolean ALCwaveBackendFactory_querySupport(ALCwaveBackendFactory* UNUS
return ALC_FALSE;
}
-static void ALCwaveBackendFactory_probe(ALCwaveBackendFactory* UNUSED(self), enum DevProbe type)
+static void ALCwaveBackendFactory_probe(ALCwaveBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
{
switch(type)
{
case ALL_DEVICE_PROBE:
- AppendAllDevicesList(waveDevice);
+ alstr_append_range(outnames, waveDevice, waveDevice+sizeof(waveDevice));
break;
case CAPTURE_DEVICE_PROBE:
break;
diff --git a/Alc/backends/winmm.c b/Alc/backends/winmm.c
index bf97ef2e..0d4a02b8 100644
--- a/Alc/backends/winmm.c
+++ b/Alc/backends/winmm.c
@@ -29,6 +29,7 @@
#include "alMain.h"
#include "alu.h"
+#include "ringbuffer.h"
#include "threads.h"
#include "backends/base.h"
@@ -37,7 +38,7 @@
#define WAVE_FORMAT_IEEE_FLOAT 0x0003
#endif
-#define DEVNAME_TAIL " on OpenAL Soft"
+#define DEVNAME_HEAD "OpenAL Soft on "
static vector_al_string PlaybackDevices;
@@ -45,8 +46,8 @@ static vector_al_string CaptureDevices;
static void clear_devlist(vector_al_string *list)
{
- VECTOR_FOR_EACH(al_string, *list, al_string_deinit);
- VECTOR_RESIZE(*list, 0);
+ VECTOR_FOR_EACH(al_string, *list, alstr_reset);
+ VECTOR_RESIZE(*list, 0, 0);
}
@@ -58,7 +59,7 @@ static void ProbePlaybackDevices(void)
clear_devlist(&PlaybackDevices);
numdevs = waveOutGetNumDevs();
- VECTOR_RESERVE(PlaybackDevices, numdevs);
+ VECTOR_RESIZE(PlaybackDevices, 0, numdevs);
for(i = 0;i < numdevs;i++)
{
WAVEOUTCAPSW WaveCaps;
@@ -71,24 +72,23 @@ static void ProbePlaybackDevices(void)
ALuint count = 0;
while(1)
{
- al_string_copy_wcstr(&dname, WaveCaps.szPname);
- if(count == 0)
- al_string_append_cstr(&dname, DEVNAME_TAIL);
- else
+ alstr_copy_cstr(&dname, DEVNAME_HEAD);
+ alstr_append_wcstr(&dname, WaveCaps.szPname);
+ if(count != 0)
{
char str[64];
- snprintf(str, sizeof(str), " #%d"DEVNAME_TAIL, count+1);
- al_string_append_cstr(&dname, str);
+ snprintf(str, sizeof(str), " #%d", count+1);
+ alstr_append_cstr(&dname, str);
}
count++;
-#define MATCH_ENTRY(i) (al_string_cmp(dname, *(i)) == 0)
+#define MATCH_ENTRY(i) (alstr_cmp(dname, *(i)) == 0)
VECTOR_FIND_IF(iter, const al_string, PlaybackDevices, MATCH_ENTRY);
- if(iter == VECTOR_ITER_END(PlaybackDevices)) break;
+ if(iter == VECTOR_END(PlaybackDevices)) break;
#undef MATCH_ENTRY
}
- TRACE("Got device \"%s\", ID %u\n", al_string_get_cstr(dname), i);
+ TRACE("Got device \"%s\", ID %u\n", alstr_get_cstr(dname), i);
}
VECTOR_PUSH_BACK(PlaybackDevices, dname);
}
@@ -102,7 +102,7 @@ static void ProbeCaptureDevices(void)
clear_devlist(&CaptureDevices);
numdevs = waveInGetNumDevs();
- VECTOR_RESERVE(CaptureDevices, numdevs);
+ VECTOR_RESIZE(CaptureDevices, 0, numdevs);
for(i = 0;i < numdevs;i++)
{
WAVEINCAPSW WaveCaps;
@@ -115,24 +115,23 @@ static void ProbeCaptureDevices(void)
ALuint count = 0;
while(1)
{
- al_string_copy_wcstr(&dname, WaveCaps.szPname);
- if(count == 0)
- al_string_append_cstr(&dname, DEVNAME_TAIL);
- else
+ alstr_copy_cstr(&dname, DEVNAME_HEAD);
+ alstr_append_wcstr(&dname, WaveCaps.szPname);
+ if(count != 0)
{
char str[64];
- snprintf(str, sizeof(str), " #%d"DEVNAME_TAIL, count+1);
- al_string_append_cstr(&dname, str);
+ snprintf(str, sizeof(str), " #%d", count+1);
+ alstr_append_cstr(&dname, str);
}
count++;
-#define MATCH_ENTRY(i) (al_string_cmp(dname, *(i)) == 0)
+#define MATCH_ENTRY(i) (alstr_cmp(dname, *(i)) == 0)
VECTOR_FIND_IF(iter, const al_string, CaptureDevices, MATCH_ENTRY);
- if(iter == VECTOR_ITER_END(CaptureDevices)) break;
+ if(iter == VECTOR_END(CaptureDevices)) break;
#undef MATCH_ENTRY
}
- TRACE("Got device \"%s\", ID %u\n", al_string_get_cstr(dname), i);
+ TRACE("Got device \"%s\", ID %u\n", alstr_get_cstr(dname), i);
}
VECTOR_PUSH_BACK(CaptureDevices, dname);
}
@@ -149,7 +148,7 @@ typedef struct ALCwinmmPlayback {
WAVEFORMATEX Format;
- volatile ALboolean killNow;
+ ATOMIC(ALenum) killNow;
althrd_t thread;
} ALCwinmmPlayback;
@@ -160,13 +159,12 @@ static void CALLBACK ALCwinmmPlayback_waveOutProc(HWAVEOUT device, UINT msg, DWO
static int ALCwinmmPlayback_mixerProc(void *arg);
static ALCenum ALCwinmmPlayback_open(ALCwinmmPlayback *self, const ALCchar *name);
-static void ALCwinmmPlayback_close(ALCwinmmPlayback *self);
static ALCboolean ALCwinmmPlayback_reset(ALCwinmmPlayback *self);
static ALCboolean ALCwinmmPlayback_start(ALCwinmmPlayback *self);
static void ALCwinmmPlayback_stop(ALCwinmmPlayback *self);
static DECLARE_FORWARD2(ALCwinmmPlayback, ALCbackend, ALCenum, captureSamples, ALCvoid*, ALCuint)
static DECLARE_FORWARD(ALCwinmmPlayback, ALCbackend, ALCuint, availableSamples)
-static DECLARE_FORWARD(ALCwinmmPlayback, ALCbackend, ALint64, getLatency)
+static DECLARE_FORWARD(ALCwinmmPlayback, ALCbackend, ClockLatency, getClockLatency)
static DECLARE_FORWARD(ALCwinmmPlayback, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCwinmmPlayback, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCwinmmPlayback)
@@ -182,7 +180,7 @@ static void ALCwinmmPlayback_Construct(ALCwinmmPlayback *self, ALCdevice *device
InitRef(&self->WaveBuffersCommitted, 0);
self->OutHdl = NULL;
- self->killNow = AL_TRUE;
+ ATOMIC_INIT(&self->killNow, AL_TRUE);
}
static void ALCwinmmPlayback_Destruct(ALCwinmmPlayback *self)
@@ -226,7 +224,7 @@ FORCE_ALIGN static int ALCwinmmPlayback_mixerProc(void *arg)
if(msg.message != WOM_DONE)
continue;
- if(self->killNow)
+ if(ATOMIC_LOAD(&self->killNow, almemory_order_acquire))
{
if(ReadRef(&self->WaveBuffersCommitted) == 0)
break;
@@ -234,8 +232,10 @@ FORCE_ALIGN static int ALCwinmmPlayback_mixerProc(void *arg)
}
WaveHdr = ((WAVEHDR*)msg.lParam);
+ ALCwinmmPlayback_lock(self);
aluMixData(device, WaveHdr->lpData, WaveHdr->dwBufferLength /
self->Format.nBlockAlign);
+ ALCwinmmPlayback_unlock(self);
// Send buffer back to play more data
waveOutWrite(self->OutHdl, WaveHdr, sizeof(WAVEHDR));
@@ -257,14 +257,14 @@ static ALCenum ALCwinmmPlayback_open(ALCwinmmPlayback *self, const ALCchar *devi
ProbePlaybackDevices();
// Find the Device ID matching the deviceName if valid
-#define MATCH_DEVNAME(iter) (!al_string_empty(*(iter)) && \
- (!deviceName || al_string_cmp_cstr(*(iter), deviceName) == 0))
+#define MATCH_DEVNAME(iter) (!alstr_empty(*(iter)) && \
+ (!deviceName || alstr_cmp_cstr(*(iter), deviceName) == 0))
VECTOR_FIND_IF(iter, const al_string, PlaybackDevices, MATCH_DEVNAME);
- if(iter == VECTOR_ITER_END(PlaybackDevices))
+ if(iter == VECTOR_END(PlaybackDevices))
return ALC_INVALID_VALUE;
#undef MATCH_DEVNAME
- DeviceID = (UINT)(iter - VECTOR_ITER_BEGIN(PlaybackDevices));
+ DeviceID = (UINT)(iter - VECTOR_BEGIN(PlaybackDevices));
retry_open:
memset(&self->Format, 0, sizeof(WAVEFORMATEX));
@@ -300,7 +300,7 @@ retry_open:
goto failure;
}
- al_string_copy(&device->DeviceName, VECTOR_ELEM(PlaybackDevices, DeviceID));
+ alstr_copy(&device->DeviceName, VECTOR_ELEM(PlaybackDevices, DeviceID));
return ALC_NO_ERROR;
failure:
@@ -311,9 +311,6 @@ failure:
return ALC_INVALID_VALUE;
}
-static void ALCwinmmPlayback_close(ALCwinmmPlayback* UNUSED(self))
-{ }
-
static ALCboolean ALCwinmmPlayback_reset(ALCwinmmPlayback *self)
{
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
@@ -374,7 +371,7 @@ static ALCboolean ALCwinmmPlayback_start(ALCwinmmPlayback *self)
ALint BufferSize;
ALuint i;
- self->killNow = AL_FALSE;
+ ATOMIC_STORE(&self->killNow, AL_FALSE, almemory_order_release);
if(althrd_create(&self->thread, ALCwinmmPlayback_mixerProc, self) != althrd_success)
return ALC_FALSE;
@@ -382,7 +379,7 @@ static ALCboolean ALCwinmmPlayback_start(ALCwinmmPlayback *self)
// Create 4 Buffers
BufferSize = device->UpdateSize*device->NumUpdates / 4;
- BufferSize *= FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
+ BufferSize *= FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
BufferData = calloc(4, BufferSize);
for(i = 0;i < 4;i++)
@@ -405,11 +402,8 @@ static void ALCwinmmPlayback_stop(ALCwinmmPlayback *self)
void *buffer = NULL;
int i;
- if(self->killNow)
+ if(ATOMIC_EXCHANGE(&self->killNow, AL_TRUE, almemory_order_acq_rel))
return;
-
- // Set flag to stop processing headers
- self->killNow = AL_TRUE;
althrd_join(self->thread, &i);
// Release the wave buffers
@@ -432,11 +426,11 @@ typedef struct ALCwinmmCapture {
HWAVEIN InHdl;
- RingBuffer *Ring;
+ ll_ringbuffer_t *Ring;
WAVEFORMATEX Format;
- volatile ALboolean killNow;
+ ATOMIC(ALenum) killNow;
althrd_t thread;
} ALCwinmmCapture;
@@ -447,13 +441,12 @@ static void CALLBACK ALCwinmmCapture_waveInProc(HWAVEIN device, UINT msg, DWORD_
static int ALCwinmmCapture_captureProc(void *arg);
static ALCenum ALCwinmmCapture_open(ALCwinmmCapture *self, const ALCchar *name);
-static void ALCwinmmCapture_close(ALCwinmmCapture *self);
static DECLARE_FORWARD(ALCwinmmCapture, ALCbackend, ALCboolean, reset)
static ALCboolean ALCwinmmCapture_start(ALCwinmmCapture *self);
static void ALCwinmmCapture_stop(ALCwinmmCapture *self);
static ALCenum ALCwinmmCapture_captureSamples(ALCwinmmCapture *self, ALCvoid *buffer, ALCuint samples);
static ALCuint ALCwinmmCapture_availableSamples(ALCwinmmCapture *self);
-static DECLARE_FORWARD(ALCwinmmCapture, ALCbackend, ALint64, getLatency)
+static DECLARE_FORWARD(ALCwinmmCapture, ALCbackend, ClockLatency, getClockLatency)
static DECLARE_FORWARD(ALCwinmmCapture, ALCbackend, void, lock)
static DECLARE_FORWARD(ALCwinmmCapture, ALCbackend, void, unlock)
DECLARE_DEFAULT_ALLOCATORS(ALCwinmmCapture)
@@ -469,11 +462,38 @@ static void ALCwinmmCapture_Construct(ALCwinmmCapture *self, ALCdevice *device)
InitRef(&self->WaveBuffersCommitted, 0);
self->InHdl = NULL;
- self->killNow = AL_TRUE;
+ ATOMIC_INIT(&self->killNow, AL_TRUE);
}
static void ALCwinmmCapture_Destruct(ALCwinmmCapture *self)
{
+ void *buffer = NULL;
+ int i;
+
+ /* Tell the processing thread to quit and wait for it to do so. */
+ if(!ATOMIC_EXCHANGE(&self->killNow, AL_TRUE, almemory_order_acq_rel))
+ {
+ PostThreadMessage(self->thread, WM_QUIT, 0, 0);
+
+ althrd_join(self->thread, &i);
+
+ /* Make sure capture is stopped and all pending buffers are flushed. */
+ waveInReset(self->InHdl);
+
+ // Release the wave buffers
+ for(i = 0;i < 4;i++)
+ {
+ waveInUnprepareHeader(self->InHdl, &self->WaveBuffer[i], sizeof(WAVEHDR));
+ if(i == 0) buffer = self->WaveBuffer[i].lpData;
+ self->WaveBuffer[i].lpData = NULL;
+ }
+ free(buffer);
+ }
+
+ ll_ringbuffer_free(self->Ring);
+ self->Ring = NULL;
+
+ // Close the Wave device
if(self->InHdl)
waveInClose(self->InHdl);
self->InHdl = 0;
@@ -512,12 +532,13 @@ static int ALCwinmmCapture_captureProc(void *arg)
continue;
/* Don't wait for other buffers to finish before quitting. We're
* closing so we don't need them. */
- if(self->killNow)
+ if(ATOMIC_LOAD(&self->killNow, almemory_order_acquire))
break;
WaveHdr = ((WAVEHDR*)msg.lParam);
- WriteRingBuffer(self->Ring, (ALubyte*)WaveHdr->lpData,
- WaveHdr->dwBytesRecorded/self->Format.nBlockAlign);
+ ll_ringbuffer_write(self->Ring, WaveHdr->lpData,
+ WaveHdr->dwBytesRecorded / self->Format.nBlockAlign
+ );
// Send buffer back to capture more data
waveInAddBuffer(self->InHdl, WaveHdr, sizeof(WAVEHDR));
@@ -543,13 +564,13 @@ static ALCenum ALCwinmmCapture_open(ALCwinmmCapture *self, const ALCchar *name)
ProbeCaptureDevices();
// Find the Device ID matching the deviceName if valid
-#define MATCH_DEVNAME(iter) (!al_string_empty(*(iter)) && (!name || al_string_cmp_cstr(*iter, name) == 0))
+#define MATCH_DEVNAME(iter) (!alstr_empty(*(iter)) && (!name || alstr_cmp_cstr(*iter, name) == 0))
VECTOR_FIND_IF(iter, const al_string, CaptureDevices, MATCH_DEVNAME);
- if(iter == VECTOR_ITER_END(CaptureDevices))
+ if(iter == VECTOR_END(CaptureDevices))
return ALC_INVALID_VALUE;
#undef MATCH_DEVNAME
- DeviceID = (UINT)(iter - VECTOR_ITER_BEGIN(CaptureDevices));
+ DeviceID = (UINT)(iter - VECTOR_BEGIN(CaptureDevices));
switch(device->FmtChans)
{
@@ -562,7 +583,7 @@ static ALCenum ALCwinmmCapture_open(ALCwinmmCapture *self, const ALCchar *name)
case DevFmtX51Rear:
case DevFmtX61:
case DevFmtX71:
- case DevFmtBFormat3D:
+ case DevFmtAmbi3D:
return ALC_INVALID_ENUM;
}
@@ -583,7 +604,7 @@ static ALCenum ALCwinmmCapture_open(ALCwinmmCapture *self, const ALCchar *name)
memset(&self->Format, 0, sizeof(WAVEFORMATEX));
self->Format.wFormatTag = ((device->FmtType == DevFmtFloat) ?
WAVE_FORMAT_IEEE_FLOAT : WAVE_FORMAT_PCM);
- self->Format.nChannels = ChannelsFromDevFmt(device->FmtChans);
+ self->Format.nChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
self->Format.wBitsPerSample = BytesFromDevFmt(device->FmtType) * 8;
self->Format.nBlockAlign = self->Format.wBitsPerSample *
self->Format.nChannels / 8;
@@ -605,7 +626,7 @@ static ALCenum ALCwinmmCapture_open(ALCwinmmCapture *self, const ALCchar *name)
if(CapturedDataSize < (self->Format.nSamplesPerSec / 10))
CapturedDataSize = self->Format.nSamplesPerSec / 10;
- self->Ring = CreateRingBuffer(self->Format.nBlockAlign, CapturedDataSize);
+ self->Ring = ll_ringbuffer_create(CapturedDataSize, self->Format.nBlockAlign, false);
if(!self->Ring) goto failure;
InitRef(&self->WaveBuffersCommitted, 0);
@@ -631,11 +652,11 @@ static ALCenum ALCwinmmCapture_open(ALCwinmmCapture *self, const ALCchar *name)
IncrementRef(&self->WaveBuffersCommitted);
}
- self->killNow = AL_FALSE;
+ ATOMIC_STORE(&self->killNow, AL_FALSE, almemory_order_release);
if(althrd_create(&self->thread, ALCwinmmCapture_captureProc, self) != althrd_success)
goto failure;
- al_string_copy(&device->DeviceName, VECTOR_ELEM(CaptureDevices, DeviceID));
+ alstr_copy(&device->DeviceName, VECTOR_ELEM(CaptureDevices, DeviceID));
return ALC_NO_ERROR;
failure:
@@ -646,8 +667,7 @@ failure:
free(BufferData);
}
- if(self->Ring)
- DestroyRingBuffer(self->Ring);
+ ll_ringbuffer_free(self->Ring);
self->Ring = NULL;
if(self->InHdl)
@@ -657,37 +677,6 @@ failure:
return ALC_INVALID_VALUE;
}
-static void ALCwinmmCapture_close(ALCwinmmCapture *self)
-{
- void *buffer = NULL;
- int i;
-
- /* Tell the processing thread to quit and wait for it to do so. */
- self->killNow = AL_TRUE;
- PostThreadMessage(self->thread, WM_QUIT, 0, 0);
-
- althrd_join(self->thread, &i);
-
- /* Make sure capture is stopped and all pending buffers are flushed. */
- waveInReset(self->InHdl);
-
- // Release the wave buffers
- for(i = 0;i < 4;i++)
- {
- waveInUnprepareHeader(self->InHdl, &self->WaveBuffer[i], sizeof(WAVEHDR));
- if(i == 0) buffer = self->WaveBuffer[i].lpData;
- self->WaveBuffer[i].lpData = NULL;
- }
- free(buffer);
-
- DestroyRingBuffer(self->Ring);
- self->Ring = NULL;
-
- // Close the Wave device
- waveInClose(self->InHdl);
- self->InHdl = NULL;
-}
-
static ALCboolean ALCwinmmCapture_start(ALCwinmmCapture *self)
{
waveInStart(self->InHdl);
@@ -701,27 +690,16 @@ static void ALCwinmmCapture_stop(ALCwinmmCapture *self)
static ALCenum ALCwinmmCapture_captureSamples(ALCwinmmCapture *self, ALCvoid *buffer, ALCuint samples)
{
- ReadRingBuffer(self->Ring, buffer, samples);
+ ll_ringbuffer_read(self->Ring, buffer, samples);
return ALC_NO_ERROR;
}
static ALCuint ALCwinmmCapture_availableSamples(ALCwinmmCapture *self)
{
- return RingBufferSize(self->Ring);
+ return (ALCuint)ll_ringbuffer_read_space(self->Ring);
}
-static inline void AppendAllDevicesList2(const al_string *name)
-{
- if(!al_string_empty(*name))
- AppendAllDevicesList(al_string_get_cstr(*name));
-}
-static inline void AppendCaptureDeviceList2(const al_string *name)
-{
- if(!al_string_empty(*name))
- AppendCaptureDeviceList(al_string_get_cstr(*name));
-}
-
typedef struct ALCwinmmBackendFactory {
DERIVE_FROM_TYPE(ALCbackendFactory);
} ALCwinmmBackendFactory;
@@ -730,7 +708,7 @@ typedef struct ALCwinmmBackendFactory {
static ALCboolean ALCwinmmBackendFactory_init(ALCwinmmBackendFactory *self);
static void ALCwinmmBackendFactory_deinit(ALCwinmmBackendFactory *self);
static ALCboolean ALCwinmmBackendFactory_querySupport(ALCwinmmBackendFactory *self, ALCbackend_Type type);
-static void ALCwinmmBackendFactory_probe(ALCwinmmBackendFactory *self, enum DevProbe type);
+static void ALCwinmmBackendFactory_probe(ALCwinmmBackendFactory *self, enum DevProbe type, al_string *outnames);
static ALCbackend* ALCwinmmBackendFactory_createBackend(ALCwinmmBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCwinmmBackendFactory);
@@ -760,19 +738,24 @@ static ALCboolean ALCwinmmBackendFactory_querySupport(ALCwinmmBackendFactory* UN
return ALC_FALSE;
}
-static void ALCwinmmBackendFactory_probe(ALCwinmmBackendFactory* UNUSED(self), enum DevProbe type)
+static void ALCwinmmBackendFactory_probe(ALCwinmmBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
{
switch(type)
{
+#define APPEND_OUTNAME(n) do { \
+ if(!alstr_empty(*(n))) \
+ alstr_append_range(outnames, VECTOR_BEGIN(*(n)), VECTOR_END(*(n))+1); \
+} while(0)
case ALL_DEVICE_PROBE:
ProbePlaybackDevices();
- VECTOR_FOR_EACH(const al_string, PlaybackDevices, AppendAllDevicesList2);
+ VECTOR_FOR_EACH(const al_string, PlaybackDevices, APPEND_OUTNAME);
break;
case CAPTURE_DEVICE_PROBE:
ProbeCaptureDevices();
- VECTOR_FOR_EACH(const al_string, CaptureDevices, AppendCaptureDeviceList2);
+ VECTOR_FOR_EACH(const al_string, CaptureDevices, APPEND_OUTNAME);
break;
+#undef APPEND_OUTNAME
}
}
diff --git a/Alc/bformatdec.c b/Alc/bformatdec.c
new file mode 100644
index 00000000..5233d06f
--- /dev/null
+++ b/Alc/bformatdec.c
@@ -0,0 +1,492 @@
+
+#include "config.h"
+
+#include "bformatdec.h"
+#include "ambdec.h"
+#include "filters/splitter.h"
+#include "alu.h"
+
+#include "bool.h"
+#include "threads.h"
+#include "almalloc.h"
+
+
+/* NOTE: These are scale factors as applied to Ambisonics content. Decoder
+ * coefficients should be divided by these values to get proper N3D scalings.
+ */
+const ALfloat N3D2N3DScale[MAX_AMBI_COEFFS] = {
+ 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
+ 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f
+};
+const ALfloat SN3D2N3DScale[MAX_AMBI_COEFFS] = {
+ 1.000000000f, /* ACN 0 (W), sqrt(1) */
+ 1.732050808f, /* ACN 1 (Y), sqrt(3) */
+ 1.732050808f, /* ACN 2 (Z), sqrt(3) */
+ 1.732050808f, /* ACN 3 (X), sqrt(3) */
+ 2.236067978f, /* ACN 4 (V), sqrt(5) */
+ 2.236067978f, /* ACN 5 (T), sqrt(5) */
+ 2.236067978f, /* ACN 6 (R), sqrt(5) */
+ 2.236067978f, /* ACN 7 (S), sqrt(5) */
+ 2.236067978f, /* ACN 8 (U), sqrt(5) */
+ 2.645751311f, /* ACN 9 (Q), sqrt(7) */
+ 2.645751311f, /* ACN 10 (O), sqrt(7) */
+ 2.645751311f, /* ACN 11 (M), sqrt(7) */
+ 2.645751311f, /* ACN 12 (K), sqrt(7) */
+ 2.645751311f, /* ACN 13 (L), sqrt(7) */
+ 2.645751311f, /* ACN 14 (N), sqrt(7) */
+ 2.645751311f, /* ACN 15 (P), sqrt(7) */
+};
+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) */
+};
+
+
+#define HF_BAND 0
+#define LF_BAND 1
+#define NUM_BANDS 2
+
+/* These points are in AL coordinates! */
+static const ALfloat Ambi3DPoints[8][3] = {
+ { -0.577350269f, 0.577350269f, -0.577350269f },
+ { 0.577350269f, 0.577350269f, -0.577350269f },
+ { -0.577350269f, 0.577350269f, 0.577350269f },
+ { 0.577350269f, 0.577350269f, 0.577350269f },
+ { -0.577350269f, -0.577350269f, -0.577350269f },
+ { 0.577350269f, -0.577350269f, -0.577350269f },
+ { -0.577350269f, -0.577350269f, 0.577350269f },
+ { 0.577350269f, -0.577350269f, 0.577350269f },
+};
+static const ALfloat Ambi3DDecoder[8][MAX_AMBI_COEFFS] = {
+ { 0.125f, 0.125f, 0.125f, 0.125f },
+ { 0.125f, -0.125f, 0.125f, 0.125f },
+ { 0.125f, 0.125f, 0.125f, -0.125f },
+ { 0.125f, -0.125f, 0.125f, -0.125f },
+ { 0.125f, 0.125f, -0.125f, 0.125f },
+ { 0.125f, -0.125f, -0.125f, 0.125f },
+ { 0.125f, 0.125f, -0.125f, -0.125f },
+ { 0.125f, -0.125f, -0.125f, -0.125f },
+};
+static const ALfloat Ambi3DDecoderHFScale[MAX_AMBI_COEFFS] = {
+ 2.0f,
+ 1.15470054f, 1.15470054f, 1.15470054f
+};
+
+
+/* NOTE: BandSplitter filters are unused with single-band decoding */
+typedef struct BFormatDec {
+ ALuint Enabled; /* Bitfield of enabled channels. */
+
+ union {
+ alignas(16) ALfloat Dual[MAX_OUTPUT_CHANNELS][NUM_BANDS][MAX_AMBI_COEFFS];
+ alignas(16) ALfloat Single[MAX_OUTPUT_CHANNELS][MAX_AMBI_COEFFS];
+ } Matrix;
+
+ BandSplitter XOver[MAX_AMBI_COEFFS];
+
+ ALfloat (*Samples)[BUFFERSIZE];
+ /* These two alias into Samples */
+ ALfloat (*SamplesHF)[BUFFERSIZE];
+ ALfloat (*SamplesLF)[BUFFERSIZE];
+
+ alignas(16) ALfloat ChannelMix[BUFFERSIZE];
+
+ struct {
+ BandSplitter XOver;
+ ALfloat Gains[NUM_BANDS];
+ } UpSampler[4];
+
+ ALsizei NumChannels;
+ ALboolean DualBand;
+} BFormatDec;
+
+BFormatDec *bformatdec_alloc()
+{
+ return al_calloc(16, sizeof(BFormatDec));
+}
+
+void bformatdec_free(BFormatDec **dec)
+{
+ if(dec && *dec)
+ {
+ al_free((*dec)->Samples);
+ (*dec)->Samples = NULL;
+ (*dec)->SamplesHF = NULL;
+ (*dec)->SamplesLF = NULL;
+
+ al_free(*dec);
+ *dec = NULL;
+ }
+}
+
+void bformatdec_reset(BFormatDec *dec, const AmbDecConf *conf, ALsizei chancount, ALuint srate, const ALsizei chanmap[MAX_OUTPUT_CHANNELS])
+{
+ static const ALsizei map2DTo3D[MAX_AMBI2D_COEFFS] = {
+ 0, 1, 3, 4, 8, 9, 15
+ };
+ const ALfloat *coeff_scale = N3D2N3DScale;
+ bool periphonic;
+ ALfloat ratio;
+ ALsizei i;
+
+ al_free(dec->Samples);
+ dec->Samples = NULL;
+ dec->SamplesHF = NULL;
+ dec->SamplesLF = NULL;
+
+ dec->NumChannels = chancount;
+ dec->Samples = al_calloc(16, dec->NumChannels*2 * sizeof(dec->Samples[0]));
+ dec->SamplesHF = dec->Samples;
+ dec->SamplesLF = dec->SamplesHF + dec->NumChannels;
+
+ dec->Enabled = 0;
+ for(i = 0;i < conf->NumSpeakers;i++)
+ dec->Enabled |= 1 << chanmap[i];
+
+ if(conf->CoeffScale == ADS_SN3D)
+ coeff_scale = SN3D2N3DScale;
+ else if(conf->CoeffScale == ADS_FuMa)
+ coeff_scale = FuMa2N3DScale;
+
+ memset(dec->UpSampler, 0, sizeof(dec->UpSampler));
+ ratio = 400.0f / (ALfloat)srate;
+ for(i = 0;i < 4;i++)
+ bandsplit_init(&dec->UpSampler[i].XOver, ratio);
+ if((conf->ChanMask&AMBI_PERIPHONIC_MASK))
+ {
+ periphonic = true;
+
+ dec->UpSampler[0].Gains[HF_BAND] = (conf->ChanMask > 0x1ff) ? W_SCALE_3H3P :
+ (conf->ChanMask > 0xf) ? W_SCALE_2H2P : 1.0f;
+ dec->UpSampler[0].Gains[LF_BAND] = 1.0f;
+ for(i = 1;i < 4;i++)
+ {
+ dec->UpSampler[i].Gains[HF_BAND] = (conf->ChanMask > 0x1ff) ? XYZ_SCALE_3H3P :
+ (conf->ChanMask > 0xf) ? XYZ_SCALE_2H2P : 1.0f;
+ dec->UpSampler[i].Gains[LF_BAND] = 1.0f;
+ }
+ }
+ else
+ {
+ periphonic = false;
+
+ dec->UpSampler[0].Gains[HF_BAND] = (conf->ChanMask > 0x1ff) ? W_SCALE_3H0P :
+ (conf->ChanMask > 0xf) ? W_SCALE_2H0P : 1.0f;
+ dec->UpSampler[0].Gains[LF_BAND] = 1.0f;
+ for(i = 1;i < 3;i++)
+ {
+ dec->UpSampler[i].Gains[HF_BAND] = (conf->ChanMask > 0x1ff) ? XYZ_SCALE_3H0P :
+ (conf->ChanMask > 0xf) ? XYZ_SCALE_2H0P : 1.0f;
+ dec->UpSampler[i].Gains[LF_BAND] = 1.0f;
+ }
+ dec->UpSampler[3].Gains[HF_BAND] = 0.0f;
+ dec->UpSampler[3].Gains[LF_BAND] = 0.0f;
+ }
+
+ memset(&dec->Matrix, 0, sizeof(dec->Matrix));
+ if(conf->FreqBands == 1)
+ {
+ dec->DualBand = AL_FALSE;
+ for(i = 0;i < conf->NumSpeakers;i++)
+ {
+ ALsizei chan = chanmap[i];
+ ALfloat gain;
+ ALsizei j, k;
+
+ if(!periphonic)
+ {
+ for(j = 0,k = 0;j < MAX_AMBI2D_COEFFS;j++)
+ {
+ ALsizei l = map2DTo3D[j];
+ if(j == 0) gain = conf->HFOrderGain[0];
+ else if(j == 1) gain = conf->HFOrderGain[1];
+ else if(j == 3) gain = conf->HFOrderGain[2];
+ else if(j == 5) gain = conf->HFOrderGain[3];
+ if((conf->ChanMask&(1<<l)))
+ dec->Matrix.Single[chan][j] = conf->HFMatrix[i][k++] / coeff_scale[l] *
+ gain;
+ }
+ }
+ else
+ {
+ for(j = 0,k = 0;j < MAX_AMBI_COEFFS;j++)
+ {
+ if(j == 0) gain = conf->HFOrderGain[0];
+ else if(j == 1) gain = conf->HFOrderGain[1];
+ else if(j == 4) gain = conf->HFOrderGain[2];
+ else if(j == 9) gain = conf->HFOrderGain[3];
+ if((conf->ChanMask&(1<<j)))
+ dec->Matrix.Single[chan][j] = conf->HFMatrix[i][k++] / coeff_scale[j] *
+ gain;
+ }
+ }
+ }
+ }
+ else
+ {
+ dec->DualBand = AL_TRUE;
+
+ ratio = conf->XOverFreq / (ALfloat)srate;
+ for(i = 0;i < MAX_AMBI_COEFFS;i++)
+ bandsplit_init(&dec->XOver[i], ratio);
+
+ ratio = powf(10.0f, conf->XOverRatio / 40.0f);
+ for(i = 0;i < conf->NumSpeakers;i++)
+ {
+ ALsizei chan = chanmap[i];
+ ALfloat gain;
+ ALsizei j, k;
+
+ if(!periphonic)
+ {
+ for(j = 0,k = 0;j < MAX_AMBI2D_COEFFS;j++)
+ {
+ ALsizei l = map2DTo3D[j];
+ if(j == 0) gain = conf->HFOrderGain[0] * ratio;
+ else if(j == 1) gain = conf->HFOrderGain[1] * ratio;
+ else if(j == 3) gain = conf->HFOrderGain[2] * ratio;
+ else if(j == 5) gain = conf->HFOrderGain[3] * ratio;
+ if((conf->ChanMask&(1<<l)))
+ dec->Matrix.Dual[chan][HF_BAND][j] = conf->HFMatrix[i][k++] /
+ coeff_scale[l] * gain;
+ }
+ for(j = 0,k = 0;j < MAX_AMBI2D_COEFFS;j++)
+ {
+ ALsizei l = map2DTo3D[j];
+ if(j == 0) gain = conf->LFOrderGain[0] / ratio;
+ else if(j == 1) gain = conf->LFOrderGain[1] / ratio;
+ else if(j == 3) gain = conf->LFOrderGain[2] / ratio;
+ else if(j == 5) gain = conf->LFOrderGain[3] / ratio;
+ if((conf->ChanMask&(1<<l)))
+ dec->Matrix.Dual[chan][LF_BAND][j] = conf->LFMatrix[i][k++] /
+ coeff_scale[l] * gain;
+ }
+ }
+ else
+ {
+ for(j = 0,k = 0;j < MAX_AMBI_COEFFS;j++)
+ {
+ if(j == 0) gain = conf->HFOrderGain[0] * ratio;
+ else if(j == 1) gain = conf->HFOrderGain[1] * ratio;
+ else if(j == 4) gain = conf->HFOrderGain[2] * ratio;
+ else if(j == 9) gain = conf->HFOrderGain[3] * ratio;
+ if((conf->ChanMask&(1<<j)))
+ dec->Matrix.Dual[chan][HF_BAND][j] = conf->HFMatrix[i][k++] /
+ coeff_scale[j] * gain;
+ }
+ for(j = 0,k = 0;j < MAX_AMBI_COEFFS;j++)
+ {
+ if(j == 0) gain = conf->LFOrderGain[0] / ratio;
+ else if(j == 1) gain = conf->LFOrderGain[1] / ratio;
+ else if(j == 4) gain = conf->LFOrderGain[2] / ratio;
+ else if(j == 9) gain = conf->LFOrderGain[3] / ratio;
+ if((conf->ChanMask&(1<<j)))
+ dec->Matrix.Dual[chan][LF_BAND][j] = conf->LFMatrix[i][k++] /
+ coeff_scale[j] * gain;
+ }
+ }
+ }
+ }
+}
+
+
+void bformatdec_process(struct BFormatDec *dec, ALfloat (*restrict OutBuffer)[BUFFERSIZE], ALsizei OutChannels, const ALfloat (*restrict InSamples)[BUFFERSIZE], ALsizei SamplesToDo)
+{
+ ALsizei chan, i;
+
+ OutBuffer = ASSUME_ALIGNED(OutBuffer, 16);
+ if(dec->DualBand)
+ {
+ for(i = 0;i < dec->NumChannels;i++)
+ bandsplit_process(&dec->XOver[i], dec->SamplesHF[i], dec->SamplesLF[i],
+ InSamples[i], SamplesToDo);
+
+ for(chan = 0;chan < OutChannels;chan++)
+ {
+ if(!(dec->Enabled&(1<<chan)))
+ continue;
+
+ memset(dec->ChannelMix, 0, SamplesToDo*sizeof(ALfloat));
+ MixRowSamples(dec->ChannelMix, dec->Matrix.Dual[chan][HF_BAND],
+ dec->SamplesHF, dec->NumChannels, 0, SamplesToDo
+ );
+ MixRowSamples(dec->ChannelMix, dec->Matrix.Dual[chan][LF_BAND],
+ dec->SamplesLF, dec->NumChannels, 0, SamplesToDo
+ );
+
+ for(i = 0;i < SamplesToDo;i++)
+ OutBuffer[chan][i] += dec->ChannelMix[i];
+ }
+ }
+ else
+ {
+ for(chan = 0;chan < OutChannels;chan++)
+ {
+ if(!(dec->Enabled&(1<<chan)))
+ continue;
+
+ memset(dec->ChannelMix, 0, SamplesToDo*sizeof(ALfloat));
+ MixRowSamples(dec->ChannelMix, dec->Matrix.Single[chan], InSamples,
+ dec->NumChannels, 0, SamplesToDo);
+
+ for(i = 0;i < SamplesToDo;i++)
+ OutBuffer[chan][i] += dec->ChannelMix[i];
+ }
+ }
+}
+
+
+void bformatdec_upSample(struct BFormatDec *dec, ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat (*restrict InSamples)[BUFFERSIZE], ALsizei InChannels, ALsizei SamplesToDo)
+{
+ ALsizei i;
+
+ /* This up-sampler leverages the differences observed in dual-band second-
+ * and third-order decoder matrices compared to first-order. For the same
+ * output channel configuration, the low-frequency matrix has identical
+ * coefficients in the shared input channels, while the high-frequency
+ * matrix has extra scalars applied to the W channel and X/Y/Z channels.
+ * Mixing the first-order content into the higher-order stream with the
+ * appropriate counter-scales applied to the HF response results in the
+ * subsequent higher-order decode generating the same response as a first-
+ * order decode.
+ */
+ for(i = 0;i < InChannels;i++)
+ {
+ /* First, split the first-order components into low and high frequency
+ * bands.
+ */
+ bandsplit_process(&dec->UpSampler[i].XOver,
+ dec->Samples[HF_BAND], dec->Samples[LF_BAND],
+ InSamples[i], SamplesToDo
+ );
+
+ /* Now write each band to the output. */
+ MixRowSamples(OutBuffer[i], dec->UpSampler[i].Gains,
+ dec->Samples, NUM_BANDS, 0, SamplesToDo
+ );
+ }
+}
+
+
+#define INVALID_UPSAMPLE_INDEX INT_MAX
+
+static ALsizei GetACNIndex(const BFChannelConfig *chans, ALsizei numchans, ALsizei acn)
+{
+ ALsizei i;
+ for(i = 0;i < numchans;i++)
+ {
+ if(chans[i].Index == acn)
+ return i;
+ }
+ return INVALID_UPSAMPLE_INDEX;
+}
+#define GetChannelForACN(b, a) GetACNIndex((b).Ambi.Map, (b).NumChannels, (a))
+
+typedef struct AmbiUpsampler {
+ alignas(16) ALfloat Samples[NUM_BANDS][BUFFERSIZE];
+
+ BandSplitter XOver[4];
+
+ ALfloat Gains[4][MAX_OUTPUT_CHANNELS][NUM_BANDS];
+} AmbiUpsampler;
+
+AmbiUpsampler *ambiup_alloc()
+{
+ return al_calloc(16, sizeof(AmbiUpsampler));
+}
+
+void ambiup_free(struct AmbiUpsampler **ambiup)
+{
+ if(ambiup)
+ {
+ al_free(*ambiup);
+ *ambiup = NULL;
+ }
+}
+
+void ambiup_reset(struct AmbiUpsampler *ambiup, const ALCdevice *device, ALfloat w_scale, ALfloat xyz_scale)
+{
+ ALfloat ratio;
+ ALsizei i;
+
+ ratio = 400.0f / (ALfloat)device->Frequency;
+ for(i = 0;i < 4;i++)
+ bandsplit_init(&ambiup->XOver[i], ratio);
+
+ memset(ambiup->Gains, 0, sizeof(ambiup->Gains));
+ if(device->Dry.CoeffCount > 0)
+ {
+ ALfloat encgains[8][MAX_OUTPUT_CHANNELS];
+ ALsizei j;
+ size_t k;
+
+ for(k = 0;k < COUNTOF(Ambi3DPoints);k++)
+ {
+ ALfloat coeffs[MAX_AMBI_COEFFS] = { 0.0f };
+ CalcDirectionCoeffs(Ambi3DPoints[k], 0.0f, coeffs);
+ ComputePanGains(&device->Dry, coeffs, 1.0f, encgains[k]);
+ }
+
+ /* Combine the matrices that do the in->virt and virt->out conversions
+ * so we get a single in->out conversion. NOTE: the Encoder matrix
+ * (encgains) and output are transposed, so the input channels line up
+ * with the rows and the output channels line up with the columns.
+ */
+ for(i = 0;i < 4;i++)
+ {
+ for(j = 0;j < device->Dry.NumChannels;j++)
+ {
+ ALdouble gain = 0.0;
+ for(k = 0;k < COUNTOF(Ambi3DDecoder);k++)
+ gain += (ALdouble)Ambi3DDecoder[k][i] * encgains[k][j];
+ ambiup->Gains[i][j][HF_BAND] = (ALfloat)(gain * Ambi3DDecoderHFScale[i]);
+ ambiup->Gains[i][j][LF_BAND] = (ALfloat)gain;
+ }
+ }
+ }
+ else
+ {
+ for(i = 0;i < 4;i++)
+ {
+ ALsizei index = GetChannelForACN(device->Dry, i);
+ if(index != INVALID_UPSAMPLE_INDEX)
+ {
+ ALfloat scale = device->Dry.Ambi.Map[index].Scale;
+ ambiup->Gains[i][index][HF_BAND] = scale * ((i==0) ? w_scale : xyz_scale);
+ ambiup->Gains[i][index][LF_BAND] = scale;
+ }
+ }
+ }
+}
+
+void ambiup_process(struct AmbiUpsampler *ambiup, ALfloat (*restrict OutBuffer)[BUFFERSIZE], ALsizei OutChannels, const ALfloat (*restrict InSamples)[BUFFERSIZE], ALsizei SamplesToDo)
+{
+ ALsizei i, j;
+
+ for(i = 0;i < 4;i++)
+ {
+ bandsplit_process(&ambiup->XOver[i],
+ ambiup->Samples[HF_BAND], ambiup->Samples[LF_BAND],
+ InSamples[i], SamplesToDo
+ );
+
+ for(j = 0;j < OutChannels;j++)
+ MixRowSamples(OutBuffer[j], ambiup->Gains[i][j],
+ ambiup->Samples, NUM_BANDS, 0, SamplesToDo
+ );
+ }
+}
diff --git a/Alc/bformatdec.h b/Alc/bformatdec.h
new file mode 100644
index 00000000..2d7d1d62
--- /dev/null
+++ b/Alc/bformatdec.h
@@ -0,0 +1,57 @@
+#ifndef BFORMATDEC_H
+#define BFORMATDEC_H
+
+#include "alMain.h"
+
+
+/* These are the necessary scales for first-order HF responses to play over
+ * higher-order 2D (non-periphonic) decoders.
+ */
+#define W_SCALE_2H0P 1.224744871f /* sqrt(1.5) */
+#define XYZ_SCALE_2H0P 1.0f
+#define W_SCALE_3H0P 1.414213562f /* sqrt(2) */
+#define XYZ_SCALE_3H0P 1.082392196f
+
+/* These are the necessary scales for first-order HF responses to play over
+ * higher-order 3D (periphonic) decoders.
+ */
+#define W_SCALE_2H2P 1.341640787f /* sqrt(1.8) */
+#define XYZ_SCALE_2H2P 1.0f
+#define W_SCALE_3H3P 1.695486018f
+#define XYZ_SCALE_3H3P 1.136697713f
+
+
+/* NOTE: These are scale factors as applied to Ambisonics content. Decoder
+ * coefficients should be divided by these values to get proper N3D scalings.
+ */
+const ALfloat N3D2N3DScale[MAX_AMBI_COEFFS];
+const ALfloat SN3D2N3DScale[MAX_AMBI_COEFFS];
+const ALfloat FuMa2N3DScale[MAX_AMBI_COEFFS];
+
+
+struct AmbDecConf;
+struct BFormatDec;
+struct AmbiUpsampler;
+
+
+struct BFormatDec *bformatdec_alloc();
+void bformatdec_free(struct BFormatDec **dec);
+void bformatdec_reset(struct BFormatDec *dec, const struct AmbDecConf *conf, ALsizei chancount, ALuint srate, const ALsizei chanmap[MAX_OUTPUT_CHANNELS]);
+
+/* Decodes the ambisonic input to the given output channels. */
+void bformatdec_process(struct BFormatDec *dec, ALfloat (*restrict OutBuffer)[BUFFERSIZE], ALsizei OutChannels, const ALfloat (*restrict InSamples)[BUFFERSIZE], ALsizei SamplesToDo);
+
+/* Up-samples a first-order input to the decoder's configuration. */
+void bformatdec_upSample(struct BFormatDec *dec, ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat (*restrict InSamples)[BUFFERSIZE], ALsizei InChannels, ALsizei SamplesToDo);
+
+
+/* Stand-alone first-order upsampler. Kept here because it shares some stuff
+ * with bformatdec. Assumes a periphonic (4-channel) input mix!
+ */
+struct AmbiUpsampler *ambiup_alloc();
+void ambiup_free(struct AmbiUpsampler **ambiup);
+void ambiup_reset(struct AmbiUpsampler *ambiup, const ALCdevice *device, ALfloat w_scale, ALfloat xyz_scale);
+
+void ambiup_process(struct AmbiUpsampler *ambiup, ALfloat (*restrict OutBuffer)[BUFFERSIZE], ALsizei OutChannels, const ALfloat (*restrict InSamples)[BUFFERSIZE], ALsizei SamplesToDo);
+
+#endif /* BFORMATDEC_H */
diff --git a/Alc/bs2b.c b/Alc/bs2b.c
index 6c3f052b..e235e547 100644
--- a/Alc/bs2b.c
+++ b/Alc/bs2b.c
@@ -129,4 +129,59 @@ void bs2b_clear(struct bs2b *bs2b)
memset(&bs2b->last_sample, 0, sizeof(bs2b->last_sample));
} /* bs2b_clear */
-extern inline void bs2b_cross_feed(struct bs2b *bs2b, float *restrict samples);
+void bs2b_cross_feed(struct bs2b *bs2b, float *restrict Left, float *restrict Right, int SamplesToDo)
+{
+ float lsamples[128][2];
+ float rsamples[128][2];
+ int base;
+
+ for(base = 0;base < SamplesToDo;)
+ {
+ int todo = mini(128, SamplesToDo-base);
+ int i;
+
+ /* Process left input */
+ lsamples[0][0] = bs2b->a0_lo*Left[0] +
+ bs2b->b1_lo*bs2b->last_sample[0].lo;
+ lsamples[0][1] = bs2b->a0_hi*Left[0] +
+ bs2b->a1_hi*bs2b->last_sample[0].asis +
+ bs2b->b1_hi*bs2b->last_sample[0].hi;
+ for(i = 1;i < todo;i++)
+ {
+ lsamples[i][0] = bs2b->a0_lo*Left[i] +
+ bs2b->b1_lo*lsamples[i-1][0];
+ lsamples[i][1] = bs2b->a0_hi*Left[i] +
+ bs2b->a1_hi*Left[i-1] +
+ bs2b->b1_hi*lsamples[i-1][1];
+ }
+ bs2b->last_sample[0].asis = Left[i-1];
+ bs2b->last_sample[0].lo = lsamples[i-1][0];
+ bs2b->last_sample[0].hi = lsamples[i-1][1];
+
+ /* Process right input */
+ rsamples[0][0] = bs2b->a0_lo*Right[0] +
+ bs2b->b1_lo*bs2b->last_sample[1].lo;
+ rsamples[0][1] = bs2b->a0_hi*Right[0] +
+ bs2b->a1_hi*bs2b->last_sample[1].asis +
+ bs2b->b1_hi*bs2b->last_sample[1].hi;
+ for(i = 1;i < todo;i++)
+ {
+ rsamples[i][0] = bs2b->a0_lo*Right[i] +
+ bs2b->b1_lo*rsamples[i-1][0];
+ rsamples[i][1] = bs2b->a0_hi*Right[i] +
+ bs2b->a1_hi*Right[i-1] +
+ bs2b->b1_hi*rsamples[i-1][1];
+ }
+ bs2b->last_sample[1].asis = Right[i-1];
+ bs2b->last_sample[1].lo = rsamples[i-1][0];
+ bs2b->last_sample[1].hi = rsamples[i-1][1];
+
+ /* Crossfeed */
+ for(i = 0;i < todo;i++)
+ *(Left++) = lsamples[i][1] + rsamples[i][0];
+ for(i = 0;i < todo;i++)
+ *(Right++) = rsamples[i][1] + lsamples[i][0];
+
+ base += todo;
+ }
+} /* bs2b_cross_feed */
diff --git a/Alc/bsinc.c b/Alc/bsinc.c
deleted file mode 100644
index f795120f..00000000
--- a/Alc/bsinc.c
+++ /dev/null
@@ -1,981 +0,0 @@
-
-#include "config.h"
-
-#include "AL/al.h"
-#include "align.h"
-
-/* Table of windowed sinc coefficients and deltas. This 11th order filter
- * has a rejection of -60 dB, yielding a transition width of ~0.302
- * (normalized frequency). Order increases when downsampling to a limit of
- * one octave, after which the quality of the filter (transition width)
- * suffers to reduce the CPU cost. The bandlimiting will cut all sound after
- * downsampling by ~2.73 octaves.
- */
-alignas(16) const ALfloat bsincTab[18840] =
-{
- /* 24, 0 */ +0.000000000e+00f, +0.000000000e+00f, +0.000000000e+00f, +0.000000000e+00f, +0.000000000e+00f, +0.000000000e+00f, +0.000000000e+00f, +0.000000000e+00f, +0.000000000e+00f, +0.000000000e+00f, +0.000000000e+00f, +0.000000000e+00f, +0.000000000e+00f, +0.000000000e+00f, +0.000000000e+00f, +0.000000000e+00f, +0.000000000e+00f, +0.000000000e+00f, +0.000000000e+00f, +0.000000000e+00f, +0.000000000e+00f, +0.000000000e+00f, +0.000000000e+00f, +0.000000000e+00f,
-
- /* 24, 0 */ +1.501390780e-03f, +3.431804419e-03f, +6.512803185e-03f, +1.091425387e-02f, +1.664594540e-02f, +2.351091132e-02f, +3.109255671e-02f, +3.878419288e-02f, +4.586050701e-02f, +5.158058002e-02f, +5.530384985e-02f, +5.659614054e-02f, +5.530384985e-02f, +5.158058002e-02f, +4.586050701e-02f, +3.878419288e-02f, +3.109255671e-02f, +2.351091132e-02f, +1.664594540e-02f, +1.091425387e-02f, +6.512803185e-03f, +3.431804419e-03f, +1.501390780e-03f, +4.573885647e-04f,
- /* 24, 1 */ +1.413186400e-03f, +3.279858311e-03f, +6.282638036e-03f, +1.059932179e-02f, +1.625135142e-02f, +2.305547031e-02f, +3.060840342e-02f, +3.831365198e-02f, +4.545054680e-02f, +5.127577001e-02f, +5.513916011e-02f, +5.659104154e-02f, +5.545895049e-02f, +5.187752167e-02f, +4.626513642e-02f, +3.925233583e-02f, +3.157717954e-02f, +2.396921539e-02f, +1.704503934e-02f, +1.123445076e-02f, +6.748179094e-03f, +3.588275667e-03f, +1.593065611e-03f, +5.022154476e-04f,
- /* 24, 2 */ +1.328380648e-03f, +3.132379333e-03f, +6.057656813e-03f, +1.028967374e-02f, +1.586133102e-02f, +2.260301890e-02f, +3.012488684e-02f, +3.784089895e-02f, +4.503543229e-02f, +5.096323022e-02f, +5.496495842e-02f, +5.657574693e-02f, +5.560438923e-02f, +5.216645963e-02f, +4.666426010e-02f, +3.971789474e-02f, +3.206210284e-02f, +2.443025293e-02f, +1.744855617e-02f, +1.155988996e-02f, +6.988790100e-03f, +3.749328623e-03f, +1.688282347e-03f, +5.494305796e-04f,
- /* 24, 3 */ +1.246901403e-03f, +2.989308098e-03f, +5.837830254e-03f, +9.985325752e-03f, +1.547595434e-02f, +2.215368059e-02f, +2.964217216e-02f, +3.736611920e-02f, +4.461534144e-02f, +5.064310236e-02f, +5.478132634e-02f, +5.655026396e-02f, +5.574009777e-02f, +5.244726189e-02f, +4.705770477e-02f, +4.018068337e-02f, +3.254715574e-02f, +2.489389144e-02f, +1.785641537e-02f, +1.189054572e-02f, +7.234657995e-03f, +3.915018340e-03f, +1.787112015e-03f, +5.991047395e-04f,
- /* 24, 4 */ +1.168676301e-03f, +2.850583915e-03f, +5.623126723e-03f, +9.686290690e-03f, +1.509528803e-02f, +2.170757578e-02f, +2.916042250e-02f, +3.688949768e-02f, +4.419045351e-02f, +5.031553118e-02f, +5.458834968e-02f, +5.651460469e-02f, +5.586601230e-02f, +5.271979985e-02f, +4.744529894e-02f, +4.064051541e-02f, +3.303216567e-02f, +2.535999546e-02f, +1.826853297e-02f, +1.222638897e-02f, +7.485801959e-03f, +4.085398290e-03f, +1.889625146e-03f, +6.513091287e-04f,
- /* 24, 5 */ +1.093632798e-03f, +2.716144855e-03f, +5.413512274e-03f, +9.392578266e-03f, +1.471939531e-02f, +2.126482169e-02f, +2.867979883e-02f, +3.641121873e-02f, +4.376094899e-02f, +4.998066438e-02f, +5.438611851e-02f, +5.646878599e-02f, +5.598207354e-02f, +5.298394839e-02f, +4.782687301e-02f, +4.109720465e-02f, +3.351695842e-02f, +2.582842673e-02f, +1.868482156e-02f, +1.256738733e-02f, +7.742238512e-03f, +4.260520294e-03f, +1.995891717e-03f, +7.061153220e-04f,
- /* 24, 6 */ +1.021698233e-03f, +2.585927824e-03f, +5.208950715e-03f, +9.104195104e-03f, +1.434833590e-02f, +2.082553239e-02f, +2.820045990e-02f, +3.593146595e-02f, +4.332700946e-02f, +4.963865252e-02f, +5.417472708e-02f, +5.641282954e-02f, +5.608822683e-02f, +5.323958602e-02f, +4.820225940e-02f, +4.155056502e-02f, +3.400135826e-02f, +2.629904416e-02f, +1.910519032e-02f, +1.291350505e-02f, +8.003981455e-03f, +4.440434453e-03f, +2.105981077e-03f, +7.635952183e-04f,
- /* 24, 7 */ +9.527998831e-04f, +2.459868628e-03f, +5.009403670e-03f, +8.821144768e-03f, +1.398216608e-02f, +2.038981869e-02f, +2.772256216e-02f, +3.545042216e-02f, +4.288881749e-02f, +4.928964888e-02f, +5.395427373e-02f, +5.634676181e-02f, +5.618442211e-02f, +5.348659488e-02f, +4.857129262e-02f, +4.200041076e-02f, +3.448518802e-02f, +2.677170395e-02f, +1.952954505e-02f, +1.326470299e-02f, +8.271041819e-03f, +4.625189083e-03f, +2.219961884e-03f, +8.238209888e-04f,
- /* 24, 8 */ +8.868650246e-04f, +2.337902042e-03f, +4.814830642e-03f, +8.543427812e-03f, +1.362093865e-02f, +1.995778816e-02f, +2.724625964e-02f, +3.496826923e-02f, +4.244655653e-02f, +4.893380942e-02f, +5.372486088e-02f, +5.627061400e-02f, +5.627061400e-02f, +5.372486088e-02f, +4.893380942e-02f, +4.244655653e-02f, +3.496826923e-02f, +2.724625964e-02f, +1.995778816e-02f, +1.362093865e-02f, +8.543427812e-03f, +4.814830642e-03f, +2.337902042e-03f, +8.868650246e-04f,
- /* 24, 9 */ +8.238209888e-04f, +2.219961884e-03f, +4.625189083e-03f, +8.271041819e-03f, +1.326470299e-02f, +1.952954505e-02f, +2.677170395e-02f, +3.448518802e-02f, +4.200041076e-02f, +4.857129262e-02f, +5.348659488e-02f, +5.618442211e-02f, +5.634676181e-02f, +5.395427373e-02f, +4.928964888e-02f, +4.288881749e-02f, +3.545042216e-02f, +2.772256216e-02f, +2.038981869e-02f, +1.398216608e-02f, +8.821144768e-03f, +5.009403670e-03f, +2.459868628e-03f, +9.527998831e-04f,
- /* 24,10 */ +7.635952183e-04f, +2.105981077e-03f, +4.440434453e-03f, +8.003981455e-03f, +1.291350505e-02f, +1.910519032e-02f, +2.629904416e-02f, +3.400135826e-02f, +4.155056502e-02f, +4.820225940e-02f, +5.323958602e-02f, +5.608822683e-02f, +5.641282954e-02f, +5.417472708e-02f, +4.963865252e-02f, +4.332700946e-02f, +3.593146595e-02f, +2.820045990e-02f, +2.082553239e-02f, +1.434833590e-02f, +9.104195104e-03f, +5.208950715e-03f, +2.585927824e-03f, +1.021698233e-03f,
- /* 24,11 */ +7.061153220e-04f, +1.995891717e-03f, +4.260520294e-03f, +7.742238512e-03f, +1.256738733e-02f, +1.868482156e-02f, +2.582842673e-02f, +3.351695842e-02f, +4.109720465e-02f, +4.782687301e-02f, +5.298394839e-02f, +5.598207354e-02f, +5.646878599e-02f, +5.438611851e-02f, +4.998066438e-02f, +4.376094899e-02f, +3.641121873e-02f, +2.867979883e-02f, +2.126482169e-02f, +1.471939531e-02f, +9.392578266e-03f, +5.413512274e-03f, +2.716144855e-03f, +1.093632798e-03f,
- /* 24,12 */ +6.513091287e-04f, +1.889625146e-03f, +4.085398290e-03f, +7.485801959e-03f, +1.222638897e-02f, +1.826853297e-02f, +2.535999546e-02f, +3.303216567e-02f, +4.064051541e-02f, +4.744529894e-02f, +5.271979985e-02f, +5.586601230e-02f, +5.651460469e-02f, +5.458834968e-02f, +5.031553118e-02f, +4.419045351e-02f, +3.688949768e-02f, +2.916042250e-02f, +2.170757578e-02f, +1.509528803e-02f, +9.686290690e-03f, +5.623126723e-03f, +2.850583915e-03f, +1.168676301e-03f,
- /* 24,13 */ +5.991047395e-04f, +1.787112015e-03f, +3.915018340e-03f, +7.234657995e-03f, +1.189054572e-02f, +1.785641537e-02f, +2.489389144e-02f, +3.254715574e-02f, +4.018068337e-02f, +4.705770477e-02f, +5.244726189e-02f, +5.574009777e-02f, +5.655026396e-02f, +5.478132634e-02f, +5.064310236e-02f, +4.461534144e-02f, +3.736611920e-02f, +2.964217216e-02f, +2.215368059e-02f, +1.547595434e-02f, +9.985325752e-03f, +5.837830254e-03f, +2.989308098e-03f, +1.246901403e-03f,
- /* 24,14 */ +5.494305796e-04f, +1.688282347e-03f, +3.749328623e-03f, +6.988790100e-03f, +1.155988996e-02f, +1.744855617e-02f, +2.443025293e-02f, +3.206210284e-02f, +3.971789474e-02f, +4.666426010e-02f, +5.216645963e-02f, +5.560438923e-02f, +5.657574693e-02f, +5.496495842e-02f, +5.096323022e-02f, +4.503543229e-02f, +3.784089895e-02f, +3.012488684e-02f, +2.260301890e-02f, +1.586133102e-02f, +1.028967374e-02f, +6.057656813e-03f, +3.132379333e-03f, +1.328380648e-03f,
- /* 24,15 */ +5.022154476e-04f, +1.593065611e-03f, +3.588275667e-03f, +6.748179094e-03f, +1.123445076e-02f, +1.704503934e-02f, +2.396921539e-02f, +3.157717954e-02f, +3.925233583e-02f, +4.626513642e-02f, +5.187752167e-02f, +5.545895049e-02f, +5.659104154e-02f, +5.513916011e-02f, +5.127577001e-02f, +4.545054680e-02f, +3.831365198e-02f, +3.060840342e-02f, +2.305547031e-02f, +1.625135142e-02f, +1.059932179e-02f, +6.282638036e-03f, +3.279858311e-03f, +1.413186400e-03f,
- /* 24, 0 */ -1.127794091e-03f, -1.412146034e-03f, -3.831821143e-04f, +3.227045776e-03f, +1.066768284e-02f, +2.270769386e-02f, +3.918787347e-02f, +5.876378120e-02f, +7.897914846e-02f, +9.670702233e-02f, +1.088639494e-01f, +1.131922811e-01f, +1.088639494e-01f, +9.670702233e-02f, +7.897914846e-02f, +5.876378120e-02f, +3.918787347e-02f, +2.270769386e-02f, +1.066768284e-02f, +3.227045776e-03f, -3.831821143e-04f, -1.412146034e-03f, -1.127794091e-03f, -4.881068065e-04f,
- /* 24, 1 */ -1.090580766e-03f, -1.420873386e-03f, -5.091873886e-04f, +2.900756187e-03f, +1.007202248e-02f, +2.181774373e-02f, +3.804709217e-02f, +5.749143322e-02f, +7.775467878e-02f, +9.573284944e-02f, +1.083163184e-01f, +1.131750947e-01f, +1.093805191e-01f, +9.765915788e-02f, +8.019389052e-02f, +6.003885715e-02f, +4.034112484e-02f, +2.361538773e-02f, +1.128161899e-02f, +3.568453927e-03f, -2.470940015e-04f, -1.398398357e-03f, -1.163769773e-03f, -5.264712252e-04f,
- /* 24, 2 */ -1.052308046e-03f, -1.424863695e-03f, -6.254426725e-04f, +2.589304991e-03f, +9.494532203e-03f, +2.094570441e-02f, +3.691925193e-02f, +5.622252667e-02f, +7.652128881e-02f, +9.473734332e-02f, +1.077380418e-01f, +1.131235487e-01f, +1.098656350e-01f, +9.858856505e-02f, +8.139809812e-02f, +6.131593665e-02f, +4.150635732e-02f, +2.454063933e-02f, +1.191392194e-02f, +3.925253463e-03f, -1.005901154e-04f, -1.379341793e-03f, -1.198322390e-03f, -5.655319713e-04f,
- /* 24, 3 */ -1.013146991e-03f, -1.424394748e-03f, -7.322803183e-04f, +2.292405169e-03f, +8.935092066e-03f, +2.009172411e-02f, +3.580480556e-02f, +5.495776346e-02f, +7.527978553e-02f, +9.372122042e-02f, +1.071295572e-01f, +1.130376830e-01f, +1.103189277e-01f, +9.949456662e-02f, +8.259096568e-02f, +6.259428489e-02f, +4.268306423e-02f, +2.548324354e-02f, +1.256466766e-02f, +4.297709369e-03f, +5.666214823e-05f, -1.354682733e-03f, -1.231259367e-03f, -6.052075404e-04f,
- /* 24, 4 */ -9.732614753e-04f, -1.419738627e-03f, -8.300317840e-04f, +2.009763334e-03f, +8.393568260e-03f, +1.925593236e-02f, +3.470418745e-02f, +5.369783330e-02f, +7.403097485e-02f, +9.268520876e-02f, +1.064913245e-01f, +1.129175635e-01f, +1.107400515e-01f, +1.003764999e-01f, +8.377168968e-02f, +6.387315732e-02f, +4.387072153e-02f, +2.644297610e-02f, +1.323391671e-02f, +4.686078310e-03f, +2.249946366e-04f, -1.324122762e-03f, -1.262381011e-03f, -6.454100415e-04f,
- /* 24, 5 */ -9.328082015e-04f, -1.411161525e-03f, -9.190272443e-04f, +1.741080225e-03f, +7.869813543e-03f, +1.843844016e-02f, +3.361781336e-02f, +5.244341325e-02f, +7.277566076e-02f, +9.163004717e-02f, +1.058238246e-01f, +1.127632829e-01f, +1.111286847e-01f, +1.012337173e-01f, +8.493946948e-02f, +6.515180031e-02f, +4.506878807e-02f, +2.741959349e-02f, +1.392171386e-02f, +5.090608136e-03f, +4.047380047e-04f, -1.287358924e-03f, -1.291480556e-03f, -6.860450823e-04f,
- /* 24, 6 */ -8.919367204e-04f, -1.398923562e-03f, -9.995952120e-04f, +1.486051192e-03f, +7.363667669e-03f, +1.763934022e-02f, +3.254608027e-02f, +5.119516710e-02f, +7.151464464e-02f, +9.055648452e-02f, +1.051275597e-01f, +1.125749599e-01f, +1.114845301e-01f, +1.020655875e-01f, +8.609350809e-02f, +6.642945179e-02f, +4.627670593e-02f, +2.841283293e-02f, +1.462808772e-02f, +5.511537402e-03f, +5.962212734e-04f, -1.244083985e-03f, -1.318344226e-03f, -7.270116618e-04f,
- /* 24, 7 */ -8.507894667e-04f, -1.383278624e-03f, -1.072062171e-03f, +1.244366682e-03f, +6.874957829e-03f, +1.685870710e-02f, +3.148936626e-02f, +4.995374490e-02f, +7.024872443e-02f, +8.946527894e-02f, +1.044030520e-01f, +1.123527394e-01f, +1.118073151e-01f, +1.028714955e-01f, +8.723301301e-02f, +6.770534195e-02f, +4.749390083e-02f, +2.942241233e-02f, +1.535305047e-02f, +5.949094883e-03f, +7.997713791e-04f, -1.193986722e-03f, -1.342751302e-03f, -7.682020711e-04f,
- /* 24, 8 */ -8.095018024e-04f, -1.364474212e-03f, -1.136752219e-03f, +1.015712718e-03f, +6.403499096e-03f, +1.609659749e-02f, +3.044803032e-02f, +4.871978245e-02f, +6.897869391e-02f, +8.835719701e-02f, +1.036508436e-01f, +1.120967923e-01f, +1.120967923e-01f, +1.036508436e-01f, +8.835719701e-02f, +6.897869391e-02f, +4.871978245e-02f, +3.044803032e-02f, +1.609659749e-02f, +6.403499096e-03f, +1.015712718e-03f, -1.136752219e-03f, -1.364474212e-03f, -8.095018024e-04f,
- /* 24, 9 */ -7.682020711e-04f, -1.342751302e-03f, -1.193986722e-03f, +7.997713791e-04f, +5.949094883e-03f, +1.535305047e-02f, +2.942241233e-02f, +4.749390083e-02f, +6.770534195e-02f, +8.723301301e-02f, +1.028714955e-01f, +1.118073151e-01f, +1.123527394e-01f, +1.044030520e-01f, +8.946527894e-02f, +7.024872443e-02f, +4.995374490e-02f, +3.148936626e-02f, +1.685870710e-02f, +6.874957829e-03f, +1.244366682e-03f, -1.072062171e-03f, -1.383278624e-03f, -8.507894667e-04f,
- /* 24,10 */ -7.270116618e-04f, -1.318344226e-03f, -1.244083985e-03f, +5.962212734e-04f, +5.511537402e-03f, +1.462808772e-02f, +2.841283293e-02f, +4.627670593e-02f, +6.642945179e-02f, +8.609350809e-02f, +1.020655875e-01f, +1.114845301e-01f, +1.125749599e-01f, +1.051275597e-01f, +9.055648452e-02f, +7.151464464e-02f, +5.119516710e-02f, +3.254608027e-02f, +1.763934022e-02f, +7.363667669e-03f, +1.486051192e-03f, -9.995952120e-04f, -1.398923562e-03f, -8.919367204e-04f,
- /* 24,11 */ -6.860450823e-04f, -1.291480556e-03f, -1.287358924e-03f, +4.047380047e-04f, +5.090608136e-03f, +1.392171386e-02f, +2.741959349e-02f, +4.506878807e-02f, +6.515180031e-02f, +8.493946948e-02f, +1.012337173e-01f, +1.111286847e-01f, +1.127632829e-01f, +1.058238246e-01f, +9.163004717e-02f, +7.277566076e-02f, +5.244341325e-02f, +3.361781336e-02f, +1.843844016e-02f, +7.869813543e-03f, +1.741080225e-03f, -9.190272443e-04f, -1.411161525e-03f, -9.328082015e-04f,
- /* 24,12 */ -6.454100415e-04f, -1.262381011e-03f, -1.324122762e-03f, +2.249946366e-04f, +4.686078310e-03f, +1.323391671e-02f, +2.644297610e-02f, +4.387072153e-02f, +6.387315732e-02f, +8.377168968e-02f, +1.003764999e-01f, +1.107400515e-01f, +1.129175635e-01f, +1.064913245e-01f, +9.268520876e-02f, +7.403097485e-02f, +5.369783330e-02f, +3.470418745e-02f, +1.925593236e-02f, +8.393568260e-03f, +2.009763334e-03f, -8.300317840e-04f, -1.419738627e-03f, -9.732614753e-04f,
- /* 24,13 */ -6.052075404e-04f, -1.231259367e-03f, -1.354682733e-03f, +5.666214823e-05f, +4.297709369e-03f, +1.256466766e-02f, +2.548324354e-02f, +4.268306423e-02f, +6.259428489e-02f, +8.259096568e-02f, +9.949456662e-02f, +1.103189277e-01f, +1.130376830e-01f, +1.071295572e-01f, +9.372122042e-02f, +7.527978553e-02f, +5.495776346e-02f, +3.580480556e-02f, +2.009172411e-02f, +8.935092066e-03f, +2.292405169e-03f, -7.322803183e-04f, -1.424394748e-03f, -1.013146991e-03f,
- /* 24,14 */ -5.655319713e-04f, -1.198322390e-03f, -1.379341793e-03f, -1.005901154e-04f, +3.925253463e-03f, +1.191392194e-02f, +2.454063933e-02f, +4.150635732e-02f, +6.131593665e-02f, +8.139809812e-02f, +9.858856505e-02f, +1.098656350e-01f, +1.131235487e-01f, +1.077380418e-01f, +9.473734332e-02f, +7.652128881e-02f, +5.622252667e-02f, +3.691925193e-02f, +2.094570441e-02f, +9.494532203e-03f, +2.589304991e-03f, -6.254426725e-04f, -1.424863695e-03f, -1.052308046e-03f,
- /* 24,15 */ -5.264712252e-04f, -1.163769773e-03f, -1.398398357e-03f, -2.470940015e-04f, +3.568453927e-03f, +1.128161899e-02f, +2.361538773e-02f, +4.034112484e-02f, +6.003885715e-02f, +8.019389052e-02f, +9.765915788e-02f, +1.093805191e-01f, +1.131750947e-01f, +1.083163184e-01f, +9.573284944e-02f, +7.775467878e-02f, +5.749143322e-02f, +3.804709217e-02f, +2.181774373e-02f, +1.007202248e-02f, +2.900756187e-03f, -5.091873886e-04f, -1.420873386e-03f, -1.090580766e-03f,
- /* 24, 0 */ -6.542299160e-04f, -2.850723396e-03f, -6.490258587e-03f, -9.960104872e-03f, -9.809478345e-03f, -1.578994128e-03f, +1.829834548e-02f, +5.025161588e-02f, +9.015425381e-02f, +1.297327779e-01f, +1.589915292e-01f, +1.697884216e-01f, +1.589915292e-01f, +1.297327779e-01f, +9.015425381e-02f, +5.025161588e-02f, +1.829834548e-02f, -1.578994128e-03f, -9.809478345e-03f, -9.960104872e-03f, -6.490258587e-03f, -2.850723396e-03f, -6.542299160e-04f, +6.349952235e-05f,
- /* 24, 1 */ -5.715660670e-04f, -2.664319167e-03f, -6.241370053e-03f, -9.805460951e-03f, -1.000906214e-02f, -2.409006146e-03f, +1.668518463e-02f, +4.795494216e-02f, +8.756853655e-02f, +1.274593023e-01f, +1.576392865e-01f, +1.697451719e-01f, +1.602699418e-01f, +1.319653222e-01f, +9.273931864e-02f, +5.258078166e-02f, +1.996024013e-02f, -7.024321916e-04f, -9.578061730e-03f, -1.010098516e-02f, -6.739131402e-03f, -3.043301383e-03f, -7.429059724e-04f, +4.968305000e-05f,
- /* 24, 2 */ -4.947700224e-04f, -2.484234158e-03f, -5.993080931e-03f, -9.638098137e-03f, -1.017794029e-02f, -3.193110201e-03f, +1.512113085e-02f, +4.569233080e-02f, +8.498458400e-02f, +1.251473534e-01f, +1.562147818e-01f, +1.696154741e-01f, +1.614730379e-01f, +1.341545065e-01f, +9.532128436e-02f, +5.494080034e-02f, +2.167042077e-02f, +2.212715669e-04f, -9.313697641e-03f, -1.022703863e-02f, -6.987342300e-03f, -3.241882098e-03f, -8.377276082e-04f, +3.267464436e-05f,
- /* 24, 3 */ -4.236872966e-04f, -2.310589033e-03f, -5.745975157e-03f, -9.459041324e-03f, -1.031725016e-02f, -3.931996122e-03f, +1.360648366e-02f, +4.346528177e-02f, +8.240478048e-02f, +1.227994149e-01f, +1.547196623e-01f, +1.693994819e-01f, +1.625994153e-01f, +1.362979353e-01f, +9.789767810e-02f, +5.732996534e-02f, +2.342836441e-02f, +1.192656872e-03f, -9.015286272e-03f, -1.033718507e-02f, -7.234214214e-03f, -3.446268223e-03f, -9.388162067e-04f, +1.226776811e-05f,
- /* 24, 4 */ -3.581542611e-04f, -2.143480447e-03f, -5.500605429e-03f, -9.269294257e-03f, -1.042813706e-02f, -4.626399385e-03f, +1.214146970e-02f, +4.127522351e-02f, +7.983147433e-02f, +1.204179923e-01f, +1.531556516e-01f, +1.690974512e-01f, +1.636477581e-01f, +1.383932498e-01f, +1.004660042e-01f, +5.974650439e-02f, +2.523347234e-02f, +2.212209196e-03f, -8.681745020e-03f, -1.043032883e-02f, -7.479039479e-03f, -3.656235461e-03f, -1.046280200e-03f, -1.174474466e-05f,
- /* 24, 5 */ -2.979990698e-04f, -1.982981877e-03f, -5.257493218e-03f, -9.069838305e-03f, -1.051175200e-02f, -5.277098842e-03f, +1.072624378e-02f, +3.912351177e-02f, +7.726697481e-02f, +1.180056089e-01f, +1.515245471e-01f, +1.687097397e-01f, +1.646168392e-01f, +1.404381320e-01f, +1.030237476e-01f, +6.218858132e-02f, +2.708506973e-02f, +3.280357753e-03f, -8.312010872e-03f, -1.050536039e-02f, -7.721080180e-03f, -3.871531888e-03f, -1.160214103e-03f, -3.957001380e-05f,
- /* 24, 6 */ -2.430425717e-04f, -1.829144469e-03f, -5.017128860e-03f, -8.861631306e-03f, -1.056924947e-02f, -5.884914422e-03f, +9.360889972e-03f, +3.701142868e-02f, +7.471354913e-02f, +1.155648023e-01f, +1.498282170e-01f, +1.682368061e-01f, +1.655055219e-01f, +1.424303080e-01f, +1.055683777e-01f, +6.465429798e-02f, +2.898240538e-02f, +4.397473555e-03f, -7.905042791e-03f, -1.056115807e-02f, -7.959568583e-03f, -4.091877352e-03f, -1.280697546e-03f, -7.141440876e-05f,
- /* 24, 7 */ -1.930992056e-04f, -1.681997919e-03f, -4.779971710e-03f, -8.645606504e-03f, -1.060178532e-02f, -6.450704795e-03f, +8.045422842e-03f, +3.494018190e-02f, +7.217341954e-02f, +1.130981205e-01f, +1.480685972e-01f, +1.676792097e-01f, +1.663127619e-01f, +1.443675516e-01f, +1.080973515e-01f, +6.714169632e-02f, +3.092465153e-02f, +5.563867546e-03f, -7.459824124e-03f, -1.059658974e-02f, -8.193707637e-03f, -4.316962918e-03f, -1.407794310e-03f, -1.074828157e-04f,
- /* 24, 8 */ -1.479778772e-04f, -1.541551365e-03f, -4.546450360e-03f, -8.422671559e-03f, -1.061051465e-02f, -6.975365011e-03f, +6.779788805e-03f, +3.291090392e-02f, +6.964876056e-02f, +1.106081178e-01f, +1.462476880e-01f, +1.670376092e-01f, +1.670376092e-01f, +1.462476880e-01f, +1.106081178e-01f, +6.964876056e-02f, +3.291090392e-02f, +6.779788805e-03f, -6.975365011e-03f, -1.061051465e-02f, -8.422671559e-03f, -4.546450360e-03f, -1.541551365e-03f, -1.479778772e-04f,
- /* 24, 9 */ -1.074828157e-04f, -1.407794310e-03f, -4.316962918e-03f, -8.193707637e-03f, -1.059658974e-02f, -7.459824124e-03f, +5.563867546e-03f, +3.092465153e-02f, +6.714169632e-02f, +1.080973515e-01f, +1.443675516e-01f, +1.663127619e-01f, +1.676792097e-01f, +1.480685972e-01f, +1.130981205e-01f, +7.217341954e-02f, +3.494018190e-02f, +8.045422842e-03f, -6.450704795e-03f, -1.060178532e-02f, -8.645606504e-03f, -4.779971710e-03f, -1.681997919e-03f, -1.930992056e-04f,
- /* 24,10 */ -7.141440876e-05f, -1.280697546e-03f, -4.091877352e-03f, -7.959568583e-03f, -1.056115807e-02f, -7.905042791e-03f, +4.397473555e-03f, +2.898240538e-02f, +6.465429798e-02f, +1.055683777e-01f, +1.424303080e-01f, +1.655055219e-01f, +1.682368061e-01f, +1.498282170e-01f, +1.155648023e-01f, +7.471354913e-02f, +3.701142868e-02f, +9.360889972e-03f, -5.884914422e-03f, -1.056924947e-02f, -8.861631306e-03f, -5.017128860e-03f, -1.829144469e-03f, -2.430425717e-04f,
- /* 24,11 */ -3.957001380e-05f, -1.160214103e-03f, -3.871531888e-03f, -7.721080180e-03f, -1.050536039e-02f, -8.312010872e-03f, +3.280357753e-03f, +2.708506973e-02f, +6.218858132e-02f, +1.030237476e-01f, +1.404381320e-01f, +1.646168392e-01f, +1.687097397e-01f, +1.515245471e-01f, +1.180056089e-01f, +7.726697481e-02f, +3.912351177e-02f, +1.072624378e-02f, -5.277098842e-03f, -1.051175200e-02f, -9.069838305e-03f, -5.257493218e-03f, -1.982981877e-03f, -2.979990698e-04f,
- /* 24,12 */ -1.174474466e-05f, -1.046280200e-03f, -3.656235461e-03f, -7.479039479e-03f, -1.043032883e-02f, -8.681745020e-03f, +2.212209196e-03f, +2.523347234e-02f, +5.974650439e-02f, +1.004660042e-01f, +1.383932498e-01f, +1.636477581e-01f, +1.690974512e-01f, +1.531556516e-01f, +1.204179923e-01f, +7.983147433e-02f, +4.127522351e-02f, +1.214146970e-02f, -4.626399385e-03f, -1.042813706e-02f, -9.269294257e-03f, -5.500605429e-03f, -2.143480447e-03f, -3.581542611e-04f,
- /* 24,13 */ +1.226776811e-05f, -9.388162067e-04f, -3.446268223e-03f, -7.234214214e-03f, -1.033718507e-02f, -9.015286272e-03f, +1.192656872e-03f, +2.342836441e-02f, +5.732996534e-02f, +9.789767810e-02f, +1.362979353e-01f, +1.625994153e-01f, +1.693994819e-01f, +1.547196623e-01f, +1.227994149e-01f, +8.240478048e-02f, +4.346528177e-02f, +1.360648366e-02f, -3.931996122e-03f, -1.031725016e-02f, -9.459041324e-03f, -5.745975157e-03f, -2.310589033e-03f, -4.236872966e-04f,
- /* 24,14 */ +3.267464436e-05f, -8.377276082e-04f, -3.241882098e-03f, -6.987342300e-03f, -1.022703863e-02f, -9.313697641e-03f, +2.212715669e-04f, +2.167042077e-02f, +5.494080034e-02f, +9.532128436e-02f, +1.341545065e-01f, +1.614730379e-01f, +1.696154741e-01f, +1.562147818e-01f, +1.251473534e-01f, +8.498458400e-02f, +4.569233080e-02f, +1.512113085e-02f, -3.193110201e-03f, -1.017794029e-02f, -9.638098137e-03f, -5.993080931e-03f, -2.484234158e-03f, -4.947700224e-04f,
- /* 24,15 */ +4.968305000e-05f, -7.429059724e-04f, -3.043301383e-03f, -6.739131402e-03f, -1.010098516e-02f, -9.578061730e-03f, -7.024321916e-04f, +1.996024013e-02f, +5.258078166e-02f, +9.273931864e-02f, +1.319653222e-01f, +1.602699418e-01f, +1.697451719e-01f, +1.576392865e-01f, +1.274593023e-01f, +8.756853655e-02f, +4.795494216e-02f, +1.668518463e-02f, -2.409006146e-03f, -1.000906214e-02f, -9.805460951e-03f, -6.241370053e-03f, -2.664319167e-03f, -5.715660670e-04f,
- /* 24, 0 */ +1.619229527e-03f, +2.585184252e-03f, +7.650378125e-04f, -6.171975840e-03f, -1.695416291e-02f, -2.423274385e-02f, -1.612533623e-02f, +1.737483974e-02f, +7.628093610e-02f, +1.465254238e-01f, +2.041060488e-01f, +2.263845622e-01f, +2.041060488e-01f, +1.465254238e-01f, +7.628093610e-02f, +1.737483974e-02f, -1.612533623e-02f, -2.423274385e-02f, -1.695416291e-02f, -6.171975840e-03f, +7.650378125e-04f, +2.585184252e-03f, +1.619229527e-03f, +4.203426526e-04f,
- /* 24, 1 */ +1.531668339e-03f, +2.575087939e-03f, +1.015030141e-03f, -5.584253498e-03f, -1.627529113e-02f, -2.409742304e-02f, -1.730694612e-02f, +1.446720847e-02f, +7.205349539e-02f, +1.422361210e-01f, +2.013530187e-01f, +2.262942875e-01f, +2.067165090e-01f, +1.507648574e-01f, +8.055624103e-02f, +2.038667606e-02f, -1.484111026e-02f, -2.430745079e-02f, -1.762106127e-02f, -6.776879595e-03f, +4.938567092e-04f, +2.584413048e-03f, +1.706479068e-03f, +4.743886054e-04f,
- /* 24, 2 */ +1.444251783e-03f, +2.554897668e-03f, +1.244217996e-03f, -5.014646771e-03f, -1.558700841e-02f, -2.390468713e-02f, -1.838770218e-02f, +1.166535016e-02f, +6.787901036e-02f, +1.379034790e-01f, +1.984620386e-01f, +2.260236194e-01f, +2.091800031e-01f, +1.549479100e-01f, +8.487414623e-02f, +2.350091114e-02f, -1.345266938e-02f, -2.431836796e-02f, -1.827334019e-02f, -7.397926552e-03f, +2.011593926e-04f, +2.571998910e-03f, +1.792931314e-03f, +5.318997770e-04f,
- /* 24, 3 */ +1.357406375e-03f, +2.525382259e-03f, +1.453038602e-03f, -4.463987325e-03f, -1.489178967e-02f, -2.365774922e-02f, -1.936952211e-02f, +8.970595311e-03f, +6.376239146e-02f, +1.335340325e-01f, +1.954379419e-01f, +2.255730254e-01f, +2.114923689e-01f, +1.590681020e-01f, +8.922922426e-02f, +2.671549986e-02f, -1.195858585e-02f, -2.426235104e-02f, -1.890827435e-02f, -8.033973302e-03f, -1.133208208e-04f, +2.547167581e-03f, +1.878071789e-03f, +5.928148062e-04f,
- /* 24, 4 */ +1.271528621e-03f, +2.487303056e-03f, +1.641978155e-03f, -3.933006021e-03f, -1.419201875e-02f, -2.335982837e-02f, -2.025447087e-02f, +6.384038515e-03f, +5.970836021e-02f, +1.291343068e-01f, +1.922857641e-01f, +2.249432832e-01f, +2.136496877e-01f, +1.631190001e-01f, +9.361589375e-02f, +3.002815853e-02f, -1.035761042e-02f, -2.413629608e-02f, -1.952306378e-02f, -8.683770335e-03f, -4.497860188e-04f, +2.509149105e-03f, +1.961357874e-03f, +6.570484107e-04f,
- /* 24, 5 */ +1.186984902e-03f, +2.441411339e-03f, +1.811567846e-03f, -3.422334900e-03f, -1.348998519e-02f, -2.301414142e-02f, -2.104475231e-02f, +3.906540614e-03f, +5.572144173e-02f, +1.247108046e-01f, +1.890107314e-01f, +2.241354793e-01f, +2.156482934e-01f, +1.670942309e-01f, +9.802842938e-02f, +3.343636564e-02f, -8.648679404e-03f, -2.393714847e-02f, -2.011483893e-02f, -9.345961431e-03f, -8.083699235e-04f, +2.457181100e-03f, +2.042219659e-03f, +7.244903794e-04f,
- /* 24, 6 */ +1.104111497e-03f, +2.388445882e-03f, +1.962379900e-03f, -2.932509404e-03f, -1.278788140e-02f, -2.262389503e-02f, -2.174270056e-02f, +1.538731332e-03f, +5.180595798e-02f, +1.202699924e-01f, +1.856182490e-01f, +2.231510062e-01f, +2.174847808e-01f, +1.709874949e-01f, +1.024609723e-01f, +3.693736330e-02f, -6.830921421e-03f, -2.366191192e-02f, -2.068066597e-02f, -1.001908338e-02f, -1.189134206e-03f, +2.390512141e-03f, +2.120060933e-03f, +7.950046334e-04f,
- /* 24, 7 */ +1.023214729e-03f, +2.329130668e-03f, +2.095023622e-03f, -2.463970822e-03f, -1.208780014e-02f, -2.219227795e-02f, -2.235077131e-02f, -7.189875350e-04f, +4.796602143e-02f, +1.158182872e-01f, +1.821138888e-01f, +2.219915591e-01f, +2.191560137e-01f, +1.747925803e-01f, +1.069075413e-01f, +4.052815924e-02f, -4.903663772e-03f, -2.330765754e-02f, -2.121755248e-02f, -1.070156599e-02f, -1.592064902e-03f, +2.308405249e-03f, +2.194260355e-03f, +8.684283615e-04f,
- /* 24, 8 */ +9.445712380e-04f, +2.264172764e-03f, +2.210141486e-03f, -2.017068943e-03f, -1.139173248e-02f, -2.172245353e-02f, -2.287153293e-02f, -2.866438416e-03f, +4.420552946e-02f, +1.113620436e-01f, +1.785033768e-01f, +2.206591322e-01f, +2.206591322e-01f, +1.785033768e-01f, +1.113620436e-01f, +4.420552946e-02f, -2.866438416e-03f, -2.287153293e-02f, -2.172245353e-02f, -1.139173248e-02f, -2.017068943e-03f, +2.210141486e-03f, +2.264172764e-03f, +9.445712380e-04f,
- /* 24, 9 */ +8.684283615e-04f, +2.194260355e-03f, +2.308405249e-03f, -1.592064902e-03f, -1.070156599e-02f, -2.121755248e-02f, -2.330765754e-02f, -4.903663772e-03f, +4.052815924e-02f, +1.069075413e-01f, +1.747925803e-01f, +2.191560137e-01f, +2.219915591e-01f, +1.821138888e-01f, +1.158182872e-01f, +4.796602143e-02f, -7.189875350e-04f, -2.235077131e-02f, -2.219227795e-02f, -1.208780014e-02f, -2.463970822e-03f, +2.095023622e-03f, +2.329130668e-03f, +1.023214729e-03f,
- /* 24,10 */ +7.950046334e-04f, +2.120060933e-03f, +2.390512141e-03f, -1.189134206e-03f, -1.001908338e-02f, -2.068066597e-02f, -2.366191192e-02f, -6.830921421e-03f, +3.693736330e-02f, +1.024609723e-01f, +1.709874949e-01f, +2.174847808e-01f, +2.231510062e-01f, +1.856182490e-01f, +1.202699924e-01f, +5.180595798e-02f, +1.538731332e-03f, -2.174270056e-02f, -2.262389503e-02f, -1.278788140e-02f, -2.932509404e-03f, +1.962379900e-03f, +2.388445882e-03f, +1.104111497e-03f,
- /* 24,11 */ +7.244903794e-04f, +2.042219659e-03f, +2.457181100e-03f, -8.083699235e-04f, -9.345961431e-03f, -2.011483893e-02f, -2.393714847e-02f, -8.648679404e-03f, +3.343636564e-02f, +9.802842938e-02f, +1.670942309e-01f, +2.156482934e-01f, +2.241354793e-01f, +1.890107314e-01f, +1.247108046e-01f, +5.572144173e-02f, +3.906540614e-03f, -2.104475231e-02f, -2.301414142e-02f, -1.348998519e-02f, -3.422334900e-03f, +1.811567846e-03f, +2.441411339e-03f, +1.186984902e-03f,
- /* 24,12 */ +6.570484107e-04f, +1.961357874e-03f, +2.509149105e-03f, -4.497860188e-04f, -8.683770335e-03f, -1.952306378e-02f, -2.413629608e-02f, -1.035761042e-02f, +3.002815853e-02f, +9.361589375e-02f, +1.631190001e-01f, +2.136496877e-01f, +2.249432832e-01f, +1.922857641e-01f, +1.291343068e-01f, +5.970836021e-02f, +6.384038515e-03f, -2.025447087e-02f, -2.335982837e-02f, -1.419201875e-02f, -3.933006021e-03f, +1.641978155e-03f, +2.487303056e-03f, +1.271528621e-03f,
- /* 24,13 */ +5.928148062e-04f, +1.878071789e-03f, +2.547167581e-03f, -1.133208208e-04f, -8.033973302e-03f, -1.890827435e-02f, -2.426235104e-02f, -1.195858585e-02f, +2.671549986e-02f, +8.922922426e-02f, +1.590681020e-01f, +2.114923689e-01f, +2.255730254e-01f, +1.954379419e-01f, +1.335340325e-01f, +6.376239146e-02f, +8.970595311e-03f, -1.936952211e-02f, -2.365774922e-02f, -1.489178967e-02f, -4.463987325e-03f, +1.453038602e-03f, +2.525382259e-03f, +1.357406375e-03f,
- /* 24,14 */ +5.318997770e-04f, +1.792931314e-03f, +2.571998910e-03f, +2.011593926e-04f, -7.397926552e-03f, -1.827334019e-02f, -2.431836796e-02f, -1.345266938e-02f, +2.350091114e-02f, +8.487414623e-02f, +1.549479100e-01f, +2.091800031e-01f, +2.260236194e-01f, +1.984620386e-01f, +1.379034790e-01f, +6.787901036e-02f, +1.166535016e-02f, -1.838770218e-02f, -2.390468713e-02f, -1.558700841e-02f, -5.014646771e-03f, +1.244217996e-03f, +2.554897668e-03f, +1.444251783e-03f,
- /* 24,15 */ +4.743886054e-04f, +1.706479068e-03f, +2.584413048e-03f, +4.938567092e-04f, -6.776879595e-03f, -1.762106127e-02f, -2.430745079e-02f, -1.484111026e-02f, +2.038667606e-02f, +8.055624103e-02f, +1.507648574e-01f, +2.067165090e-01f, +2.262942875e-01f, +2.013530187e-01f, +1.422361210e-01f, +7.205349539e-02f, +1.446720847e-02f, -1.730694612e-02f, -2.409742304e-02f, -1.627529113e-02f, -5.584253498e-03f, +1.015030141e-03f, +2.575087939e-03f, +1.531668339e-03f,
- /* 24, 0 */ -5.620806651e-04f, +1.786951327e-03f, +6.445247430e-03f, +8.135220753e-03f, -1.055728075e-03f, -2.182587186e-02f, -3.862210468e-02f, -2.392616717e-02f, +4.121375257e-02f, +1.449837419e-01f, +2.427850309e-01f, +2.829807027e-01f, +2.427850309e-01f, +1.449837419e-01f, +4.121375257e-02f, -2.392616717e-02f, -3.862210468e-02f, -2.182587186e-02f, -1.055728075e-03f, +8.135220753e-03f, +6.445247430e-03f, +1.786951327e-03f, -5.620806651e-04f, -5.120724112e-04f,
- /* 24, 1 */ -6.104492932e-04f, +1.548760636e-03f, +6.159105160e-03f, +8.277197332e-03f, -7.779733613e-05f, -2.039475337e-02f, -3.819819741e-02f, -2.624621678e-02f, +3.569722776e-02f, +1.380982730e-01f, +2.379020609e-01f, +2.828154582e-01f, +2.474326717e-01f, +1.518487179e-01f, +4.689321837e-02f, -2.139810919e-02f, -3.892035913e-02f, -2.324619800e-02f, -2.084809535e-03f, +7.948411519e-03f, +6.721048149e-03f, +2.036121529e-03f, -5.037148469e-04f, -5.469834647e-04f,
- /* 24, 2 */ -6.493280747e-04f, +1.322056610e-03f, +5.864617557e-03f, +8.376206823e-03f, +8.476165560e-04f, -1.895882060e-02f, -3.765599422e-02f, -2.836041007e-02f, +3.035103267e-02f, +1.312062799e-01f, +2.327950895e-01f, +2.823201223e-01f, +2.518341522e-01f, +1.586790922e-01f, +5.272765217e-02f, -1.866041870e-02f, -3.908572584e-02f, -2.464952038e-02f, -3.163389088e-03f, +7.715013258e-03f, +6.984447000e-03f, +2.295668536e-03f, -4.348733531e-04f, -5.801620624e-04f,
- /* 24, 3 */ -6.792484933e-04f, +1.107253309e-03f, +5.563710253e-03f, +8.434210700e-03f, +1.719427448e-03f, -1.752380524e-02f, -3.700294628e-02f, -3.027140813e-02f, +2.518195985e-02f, +1.243215533e-01f, +2.274759065e-01f, +2.814958870e-01f, +2.559791715e-01f, +1.654606562e-01f, +5.870851072e-02f, -1.571201688e-02f, -3.911111998e-02f, -2.602940857e-02f, -4.289522020e-03f, +7.433392157e-03f, +7.233326681e-03f, +2.564892035e-03f, -3.551113499e-04f, -6.111213026e-04f,
- /* 24, 4 */ -7.007615573e-04f, +9.046745282e-04f, +5.258232435e-03f, +8.453253159e-03f, +2.536821717e-03f, -1.609518093e-02f, -3.624657153e-02f, -3.198236083e-02f, +2.019619593e-02f, +1.174576676e-01f, +2.219567270e-01f, +2.803447347e-01f, +2.598579915e-01f, +1.721791413e-01f, +6.482669661e-02f, -1.255238605e-02f, -3.898963496e-02f, -2.737922870e-02f, -5.460966964e-03f, +7.102050305e-03f, +7.465520628e-03f, +2.842992490e-03f, -2.640225027e-04f, -6.393525967e-04f,
- /* 24, 5 */ -7.144333056e-04f, +7.145569834e-04f, +4.949951622e-03f, +8.435448103e-03f, +3.299249997e-03f, -1.467815287e-02f, -3.539442781e-02f, -3.349688539e-02f, +1.539931407e-02f, +1.106279448e-01f, +2.162501543e-01f, +2.788694321e-01f, +2.634614666e-01f, +1.788202595e-01f, +7.107257652e-02f, -9.181583996e-03f, -3.871457066e-02f, -2.869216031e-02f, -6.675182397e-03f, +6.719638981e-03f, +7.678821339e-03f, +3.129069982e-03f, -1.612438490e-04f, -6.643278432e-04f,
- /* 24, 6 */ -7.208404573e-04f, +5.370537968e-04f, +4.640549073e-03f, +8.382966356e-03f, +4.006418111e-03f, -1.327764882e-02f, -3.445408633e-02f, -3.481904366e-02f, +1.079626845e-02f, +1.038454185e-01f, +2.103691410e-01f, +2.770735210e-01f, +2.667810728e-01f, +1.853697450e-01f, +7.743600141e-02f, -5.600256400e-03f, -3.827946167e-02f, -2.996121443e-02f, -7.929324241e-03f, +6.284971816e-03f, +7.870989279e-03f, +3.422123547e-03f, -4.646067064e-05f, -6.855018549e-04f,
- /* 24, 7 */ -7.205662235e-04f, +3.722382714e-04f, +4.331615834e-03f, +8.298023138e-03f, +4.658277300e-03f, -1.189831143e-02f, -3.343310598e-02f, -3.595331849e-02f, +6.391391026e-03f, +9.712280024e-02f, +2.043269499e-01f, +2.749613076e-01f, +2.698089343e-01f, +1.918133956e-01f, +8.390632879e-02f, -1.809647645e-03f, -3.767810524e-02f, -3.117925279e-02f, -9.220244622e-03f, +5.797037759e-03f, +8.039762345e-03f, +3.721051017e-03f, +8.058866307e-05f, -7.023150347e-04f,
- /* 24, 8 */ -7.141962964e-04f, +2.201079121e-04f, +4.024649403e-03f, +8.182865872e-03f, +5.255013788e-03f, -1.054449181e-02f, -3.233900821e-02f, -3.690458903e-02f, +2.188390323e-03f, +9.047244679e-02f, +1.981371140e-01f, +2.725378483e-01f, +2.725378483e-01f, +1.981371140e-01f, +9.047244679e-02f, +2.188390323e-03f, -3.690458903e-02f, -3.233900821e-02f, -1.054449181e-02f, +5.255013788e-03f, +8.182865872e-03f, +4.024649403e-03f, +2.201079121e-04f, -7.141962964e-04f,
- /* 24, 9 */ -7.023150347e-04f, +8.058866307e-05f, +3.721051017e-03f, +8.039762345e-03f, +5.797037759e-03f, -9.220244622e-03f, -3.117925279e-02f, -3.767810524e-02f, -1.809647645e-03f, +8.390632879e-02f, +1.918133956e-01f, +2.698089343e-01f, +2.749613076e-01f, +2.043269499e-01f, +9.712280024e-02f, +6.391391026e-03f, -3.595331849e-02f, -3.343310598e-02f, -1.189831143e-02f, +4.658277300e-03f, +8.298023138e-03f, +4.331615834e-03f, +3.722382714e-04f, -7.205662235e-04f,
- /* 24,10 */ -6.855018549e-04f, -4.646067064e-05f, +3.422123547e-03f, +7.870989279e-03f, +6.284971816e-03f, -7.929324241e-03f, -2.996121443e-02f, -3.827946167e-02f, -5.600256400e-03f, +7.743600141e-02f, +1.853697450e-01f, +2.667810728e-01f, +2.770735210e-01f, +2.103691410e-01f, +1.038454185e-01f, +1.079626845e-02f, -3.481904366e-02f, -3.445408633e-02f, -1.327764882e-02f, +4.006418111e-03f, +8.382966356e-03f, +4.640549073e-03f, +5.370537968e-04f, -7.208404573e-04f,
- /* 24,11 */ -6.643278432e-04f, -1.612438490e-04f, +3.129069982e-03f, +7.678821339e-03f, +6.719638981e-03f, -6.675182397e-03f, -2.869216031e-02f, -3.871457066e-02f, -9.181583996e-03f, +7.107257652e-02f, +1.788202595e-01f, +2.634614666e-01f, +2.788694321e-01f, +2.162501543e-01f, +1.106279448e-01f, +1.539931407e-02f, -3.349688539e-02f, -3.539442781e-02f, -1.467815287e-02f, +3.299249997e-03f, +8.435448103e-03f, +4.949951622e-03f, +7.145569834e-04f, -7.144333056e-04f,
- /* 24,12 */ -6.393525967e-04f, -2.640225027e-04f, +2.842992490e-03f, +7.465520628e-03f, +7.102050305e-03f, -5.460966964e-03f, -2.737922870e-02f, -3.898963496e-02f, -1.255238605e-02f, +6.482669661e-02f, +1.721791413e-01f, +2.598579915e-01f, +2.803447347e-01f, +2.219567270e-01f, +1.174576676e-01f, +2.019619593e-02f, -3.198236083e-02f, -3.624657153e-02f, -1.609518093e-02f, +2.536821717e-03f, +8.453253159e-03f, +5.258232435e-03f, +9.046745282e-04f, -7.007615573e-04f,
- /* 24,13 */ -6.111213026e-04f, -3.551113499e-04f, +2.564892035e-03f, +7.233326681e-03f, +7.433392157e-03f, -4.289522020e-03f, -2.602940857e-02f, -3.911111998e-02f, -1.571201688e-02f, +5.870851072e-02f, +1.654606562e-01f, +2.559791715e-01f, +2.814958870e-01f, +2.274759065e-01f, +1.243215533e-01f, +2.518195985e-02f, -3.027140813e-02f, -3.700294628e-02f, -1.752380524e-02f, +1.719427448e-03f, +8.434210700e-03f, +5.563710253e-03f, +1.107253309e-03f, -6.792484933e-04f,
- /* 24,14 */ -5.801620624e-04f, -4.348733531e-04f, +2.295668536e-03f, +6.984447000e-03f, +7.715013258e-03f, -3.163389088e-03f, -2.464952038e-02f, -3.908572584e-02f, -1.866041870e-02f, +5.272765217e-02f, +1.586790922e-01f, +2.518341522e-01f, +2.823201223e-01f, +2.327950895e-01f, +1.312062799e-01f, +3.035103267e-02f, -2.836041007e-02f, -3.765599422e-02f, -1.895882060e-02f, +8.476165560e-04f, +8.376206823e-03f, +5.864617557e-03f, +1.322056610e-03f, -6.493280747e-04f,
- /* 24,15 */ -5.469834647e-04f, -5.037148469e-04f, +2.036121529e-03f, +6.721048149e-03f, +7.948411519e-03f, -2.084809535e-03f, -2.324619800e-02f, -3.892035913e-02f, -2.139810919e-02f, +4.689321837e-02f, +1.518487179e-01f, +2.474326717e-01f, +2.828154582e-01f, +2.379020609e-01f, +1.380982730e-01f, +3.569722776e-02f, -2.624621678e-02f, -3.819819741e-02f, -2.039475337e-02f, -7.779733613e-05f, +8.277197332e-03f, +6.159105160e-03f, +1.548760636e-03f, -6.104492932e-04f,
- /* 24, 0 */ -1.197013499e-03f, -3.320493122e-03f, -1.144245270e-03f, +8.577337679e-03f, +1.627759141e-02f, +3.152522439e-03f, -3.255249254e-02f, -5.362651723e-02f, -5.304244056e-03f, +1.253006387e-01f, +2.738089134e-01f, +3.395768433e-01f, +2.738089134e-01f, +1.253006387e-01f, -5.304244056e-03f, -5.362651723e-02f, -3.255249254e-02f, +3.152522439e-03f, +1.627759141e-02f, +8.577337679e-03f, -1.144245270e-03f, -3.320493122e-03f, -1.197013499e-03f, +1.261205705e-04f,
- /* 24, 1 */ -1.060573898e-03f, -3.246029352e-03f, -1.514205592e-03f, +7.849505167e-03f, +1.622707505e-02f, +4.797556391e-03f, -3.017446993e-02f, -5.385088872e-02f, -1.098434059e-02f, +1.155960124e-01f, +2.659858985e-01f, +3.393017042e-01f, +2.812897341e-01f, +1.350895441e-01f, +7.263382766e-04f, -5.311639759e-02f, -3.488122370e-02f, +1.404407443e-03f, +1.624118609e-02f, +9.301572693e-03f, -7.399572733e-04f, -3.377916464e-03f, -1.338504197e-03f, +9.901282259e-05f,
- /* 24, 2 */ -9.298712414e-04f, -3.156277727e-03f, -1.849729696e-03f, +7.122444811e-03f, +1.609438844e-02f, +6.335978542e-03f, -2.776122262e-02f, -5.380213751e-02f, -1.630850202e-02f, +1.060004951e-01f, +2.578448120e-01f, +3.384771755e-01f, +2.884051592e-01f, +1.449371908e-01f, +7.100538384e-03f, -5.230860749e-02f, -3.714619841e-02f, -4.425295608e-04f, +1.611336946e-02f, +1.001762126e-02f, -3.016869975e-04f, -3.416553196e-03f, -1.484263468e-03f, +6.526428163e-05f,
- /* 24, 3 */ -8.054954021e-04f, -3.052984548e-03f, -2.150934111e-03f, +6.400291527e-03f, +1.588450664e-02f, +7.764974256e-03f, -2.532636892e-02f, -5.349351937e-02f, -2.127269005e-02f, +9.653812261e-02f, +2.494105998e-01f, +3.371059190e-01f, +2.951330020e-01f, +1.548174220e-01f, +1.381006797e-02f, -5.119199897e-02f, -3.933260713e-02f, -2.383292518e-03f, +1.588995194e-02f, +1.072069264e-02f, +1.699725421e-04f, -3.434676821e-03f, -1.633412152e-03f, +2.453170435e-05f,
- /* 24, 4 */ -6.879416803e-04f, -2.937877889e-03f, -2.418147761e-03f, +5.686932142e-03f, +1.560259046e-02f, +9.082429619e-03f, -2.288303213e-02f, -5.293884648e-02f, -2.587426360e-02f, +8.723205545e-02f, +2.407089312e-01f, +3.351923590e-01f, +3.014521813e-01f, +1.647035561e-01f, +2.084522321e-02f, -4.975626781e-02f, -4.142535273e-02f, -4.412143180e-03f, +1.556708209e-02f, +1.140581394e-02f, +6.741710759e-04f, -3.430594409e-03f, -1.784975276e-03f, -2.348660763e-05f,
- /* 24, 5 */ -5.776128643e-04f, -2.812656385e-03f, -2.651898517e-03f, +4.985994150e-03f, +1.525394912e-02f, +1.028691298e-02f, -2.044379769e-02f, -5.215241275e-02f, -3.011195925e-02f, +7.810450253e-02f, +2.317660961e-01f, +3.327426635e-01f, +3.073428069e-01f, +1.745684830e-01f, +2.819489579e-02f, -4.799202051e-02f, -4.340911052e-02f, -6.522599631e-03f, +1.514128438e-02f, +1.206785167e-02f, +1.209792693e-03f, -3.402660968e-03f, -1.937883690e-03f, -7.904503123e-05f,
- /* 24, 6 */ -4.748218959e-04f, -2.678978820e-03f, -2.852899102e-03f, +4.300836533e-03f, +1.484400357e-02f, +1.137765361e-02f, -1.802067434e-02f, -5.114891871e-02f, -3.398586582e-02f, +6.917664952e-02f, +2.226089010e-01f, +3.297647184e-01f, +3.127862620e-01f, +1.843847637e-01f, +3.584659036e-02f, -4.589083871e-02f, -4.526839113e-02f, -8.707438641e-03f, +1.460949637e-02f, +1.270153536e-02f, +1.775448814e-03f, -3.349294247e-03f, -2.090976552e-03f, -1.423449116e-04f,
- /* 24, 7 */ -3.797950945e-04f, -2.538454547e-03f, -3.022032460e-03f, +3.634542645e-03f, +1.437825069e-02f, +1.235451773e-02f, -1.562505912e-02f, -4.994339647e-02f, -3.749739364e-02f, +6.046859273e-02f, +2.132645624e-01f, +3.262680950e-01f, +3.177652787e-01f, +1.941247325e-01f, +4.378644843e-02f, -4.344534054e-02f, -4.698760595e-02f, -1.095870195e-02f, +1.396910503e-02f, +1.330148306e-02f, +2.369472628e-03f, -3.268989872e-03f, -2.243004675e-03f, -2.135289700e-04f,
- /* 24, 8 */ -2.926758839e-04f, -2.392634763e-03f, -3.160336722e-03f, +2.989915102e-03f, +1.386222860e-02f, +1.321798203e-02f, -1.326770657e-02f, -4.855113496e-02f, -4.064923848e-02f, +5.199927852e-02f, +2.037606007e-01f, +3.222640100e-01f, +3.222640100e-01f, +2.037606007e-01f, +5.199927852e-02f, -4.064923848e-02f, -4.855113496e-02f, -1.326770657e-02f, +1.321798203e-02f, +1.386222860e-02f, +2.989915102e-03f, -3.160336722e-03f, -2.392634763e-03f, -2.926758839e-04f,
- /* 24, 9 */ -2.135289700e-04f, -2.243004675e-03f, -3.268989872e-03f, +2.369472628e-03f, +1.330148306e-02f, +1.396910503e-02f, -1.095870195e-02f, -4.698760595e-02f, -4.344534054e-02f, +4.378644843e-02f, +1.941247325e-01f, +3.177652787e-01f, +3.262680950e-01f, +2.132645624e-01f, +6.046859273e-02f, -3.749739364e-02f, -4.994339647e-02f, -1.562505912e-02f, +1.235451773e-02f, +1.437825069e-02f, +3.634542645e-03f, -3.022032460e-03f, -2.538454547e-03f, -3.797950945e-04f,
- /* 24,10 */ -1.423449116e-04f, -2.090976552e-03f, -3.349294247e-03f, +1.775448814e-03f, +1.270153536e-02f, +1.460949637e-02f, -8.707438641e-03f, -4.526839113e-02f, -4.589083871e-02f, +3.584659036e-02f, +1.843847637e-01f, +3.127862620e-01f, +3.297647184e-01f, +2.226089010e-01f, +6.917664952e-02f, -3.398586582e-02f, -5.114891871e-02f, -1.802067434e-02f, +1.137765361e-02f, +1.484400357e-02f, +4.300836533e-03f, -2.852899102e-03f, -2.678978820e-03f, -4.748218959e-04f,
- /* 24,11 */ -7.904503123e-05f, -1.937883690e-03f, -3.402660968e-03f, +1.209792693e-03f, +1.206785167e-02f, +1.514128438e-02f, -6.522599631e-03f, -4.340911052e-02f, -4.799202051e-02f, +2.819489579e-02f, +1.745684830e-01f, +3.073428069e-01f, +3.327426635e-01f, +2.317660961e-01f, +7.810450253e-02f, -3.011195925e-02f, -5.215241275e-02f, -2.044379769e-02f, +1.028691298e-02f, +1.525394912e-02f, +4.985994150e-03f, -2.651898517e-03f, -2.812656385e-03f, -5.776128643e-04f,
- /* 24,12 */ -2.348660763e-05f, -1.784975276e-03f, -3.430594409e-03f, +6.741710759e-04f, +1.140581394e-02f, +1.556708209e-02f, -4.412143180e-03f, -4.142535273e-02f, -4.975626781e-02f, +2.084522321e-02f, +1.647035561e-01f, +3.014521813e-01f, +3.351923590e-01f, +2.407089312e-01f, +8.723205545e-02f, -2.587426360e-02f, -5.293884648e-02f, -2.288303213e-02f, +9.082429619e-03f, +1.560259046e-02f, +5.686932142e-03f, -2.418147761e-03f, -2.937877889e-03f, -6.879416803e-04f,
- /* 24,13 */ +2.453170435e-05f, -1.633412152e-03f, -3.434676821e-03f, +1.699725421e-04f, +1.072069264e-02f, +1.588995194e-02f, -2.383292518e-03f, -3.933260713e-02f, -5.119199897e-02f, +1.381006797e-02f, +1.548174220e-01f, +2.951330020e-01f, +3.371059190e-01f, +2.494105998e-01f, +9.653812261e-02f, -2.127269005e-02f, -5.349351937e-02f, -2.532636892e-02f, +7.764974256e-03f, +1.588450664e-02f, +6.400291527e-03f, -2.150934111e-03f, -3.052984548e-03f, -8.054954021e-04f,
- /* 24,14 */ +6.526428163e-05f, -1.484263468e-03f, -3.416553196e-03f, -3.016869975e-04f, +1.001762126e-02f, +1.611336946e-02f, -4.425295608e-04f, -3.714619841e-02f, -5.230860749e-02f, +7.100538384e-03f, +1.449371908e-01f, +2.884051592e-01f, +3.384771755e-01f, +2.578448120e-01f, +1.060004951e-01f, -1.630850202e-02f, -5.380213751e-02f, -2.776122262e-02f, +6.335978542e-03f, +1.609438844e-02f, +7.122444811e-03f, -1.849729696e-03f, -3.156277727e-03f, -9.298712414e-04f,
- /* 24,15 */ +9.901282259e-05f, -1.338504197e-03f, -3.377916464e-03f, -7.399572733e-04f, +9.301572693e-03f, +1.624118609e-02f, +1.404407443e-03f, -3.488122370e-02f, -5.311639759e-02f, +7.263382766e-04f, +1.350895441e-01f, +2.812897341e-01f, +3.393017042e-01f, +2.659858985e-01f, +1.155960124e-01f, -1.098434059e-02f, -5.385088872e-02f, -3.017446993e-02f, +4.797556391e-03f, +1.622707505e-02f, +7.849505167e-03f, -1.514205592e-03f, -3.246029352e-03f, -1.060573898e-03f,
- /* 20, 0 */ -4.161478318e-03f, -1.410215661e-03f, +1.216462436e-02f, +1.839753508e-02f, -1.019572218e-02f, -5.576407638e-02f, -3.857794503e-02f, +9.869941459e-02f, +2.903842315e-01f, +3.819037908e-01f, +2.903842315e-01f, +9.869941459e-02f, -3.857794503e-02f, -5.576407638e-02f, -1.019572218e-02f, +1.839753508e-02f, +1.216462436e-02f, -1.410215661e-03f, -4.161478318e-03f, -1.002136091e-03f,
- /* 20, 1 */ -4.024812873e-03f, -1.935046598e-03f, +1.120868183e-02f, +1.884704309e-02f, -7.349314558e-03f, -5.377462232e-02f, -4.306909662e-02f, +8.713841011e-02f, +2.797272456e-01f, +3.815142140e-01f, +3.006231905e-01f, +1.105090175e-01f, -3.357053763e-02f, -5.750258783e-02f, -1.313424017e-02f, +1.779561475e-02f, +1.309754391e-02f, -8.338854378e-04f, -4.274968522e-03f, -1.184613130e-03f,
- /* 20, 2 */ -3.867923854e-03f, -2.407896178e-03f, +1.023778220e-02f, +1.914947854e-02f, -4.608279480e-03f, -5.155911253e-02f, -4.704785126e-02f, +7.585987841e-02f, +2.686929243e-01f, +3.803470604e-01f, +3.104047352e-01f, +1.225315522e-01f, -2.804532708e-02f, -5.896552129e-02f, -1.615031726e-02f, +1.703673197e-02f, +1.399907244e-02f, -2.069933478e-04f, -4.362323551e-03f, -1.376799150e-03f,
- /* 20, 3 */ -3.693729756e-03f, -2.828715617e-03f, +9.259646102e-03f, +1.931089692e-02f, -1.984565051e-03f, -4.914254142e-02f, -5.052030051e-02f, +6.489576800e-02f, +2.573230589e-01f, +3.784070525e-01f, +3.196909791e-01f, +1.347297046e-01f, -2.200314505e-02f, -6.012863981e-02f, -1.922814732e-02f, +1.611719780e-02f, +1.486058724e-02f, +4.690483654e-04f, -4.420601658e-03f, -1.577606548e-03f,
- /* 20, 4 */ -3.505092707e-03f, -3.197865053e-03f, +8.281610454e-03f, +1.933799402e-02f, +5.112106557e-04f, -4.654987033e-02f, -5.349467921e-02f, +5.427598051e-02f, +2.456603330e-01f, +3.757020336e-01f, +3.284457292e-01f, +1.470646693e-01f, -1.544725782e-02f, -6.096825350e-02f, -2.235071271e-02f, +1.503425320e-02f, +1.567326271e-02f, +1.192336051e-03f, -4.446907145e-03f, -1.785766437e-03f,
- /* 20, 5 */ -3.304797071e-03f, -3.516087186e-03f, +7.310594668e-03f, +1.923802927e-02f, +2.869766685e-03f, -4.380589142e-02f, -5.598126618e-02f, +4.402826232e-02f, +2.337481127e-01f, +3.722429273e-01f, +3.366346688e-01f, +1.594963158e-01f, -8.383409345e-03f, -6.146136934e-02f, -2.549983702e-02f, +1.378613431e-02f, +1.642812632e-02f, +1.960461229e-03f, -4.438419451e-03f, -1.999828319e-03f,
- /* 20, 6 */ -3.095529963e-03f, -3.784479230e-03f, +6.353071566e-03f, +1.901874863e-02f, +5.083157654e-03f, -4.093509653e-02f, -5.799227582e-02f, +3.417810958e-02f, +2.216302330e-01f, +3.680436800e-01f, +3.442255330e-01f, +1.719833640e-01f, -8.198514564e-04f, -6.158584092e-02f, -2.865624686e-02f, +1.237213363e-02f, +1.711611824e-02f, +2.770500592e-03f, -4.392423280e-03f, -2.218161540e-03f,
- /* 20, 7 */ -2.879863731e-03f, -4.004463491e-03f, +5.415043050e-03f, +1.868830751e-02f, +7.144763866e-03f, -3.796155187e-02f, -5.954174144e-02f, +2.474868675e-02f, +2.093507858e-01f, +3.631211887e-01f, +3.511882735e-01f, +1.844835679e-01f, +7.232639407e-03f, -6.132051750e-02f, -3.179964276e-02f, +1.079265669e-02f, +1.772815465e-02f, +3.619009684e-03f, -4.306339536e-03f, -2.438958613e-03f,
- /* 20, 8 */ -2.660240473e-03f, -4.177756859e-03f, +4.502020476e-03f, +1.825519424e-02f, +9.049273418e-03f, -3.490877898e-02f, -6.064539119e-02f, +1.576075912e-02f, +1.969539074e-01f, +3.574952133e-01f, +3.574952133e-01f, +1.969539074e-01f, +1.576075912e-02f, -6.064539119e-02f, -3.490877898e-02f, +9.049273418e-03f, +1.825519424e-02f, +4.502020476e-03f, -4.177756859e-03f, -2.660240473e-03f,
- /* 20, 9 */ -2.438958613e-03f, -4.306339536e-03f, +3.619009684e-03f, +1.772815465e-02f, +1.079265669e-02f, -3.179964276e-02f, -6.132051750e-02f, +7.232639407e-03f, +1.844835679e-01f, +3.511882735e-01f, +3.631211887e-01f, +2.093507858e-01f, +2.474868675e-02f, -5.954174144e-02f, -3.796155187e-02f, +7.144763866e-03f, +1.868830751e-02f, +5.415043050e-03f, -4.004463491e-03f, -2.879863731e-03f,
- /* 20,10 */ -2.218161540e-03f, -4.392423280e-03f, +2.770500592e-03f, +1.711611824e-02f, +1.237213363e-02f, -2.865624686e-02f, -6.158584092e-02f, -8.198514564e-04f, +1.719833640e-01f, +3.442255330e-01f, +3.680436800e-01f, +2.216302330e-01f, +3.417810958e-02f, -5.799227582e-02f, -4.093509653e-02f, +5.083157654e-03f, +1.901874863e-02f, +6.353071566e-03f, -3.784479230e-03f, -3.095529963e-03f,
- /* 20,11 */ -1.999828319e-03f, -4.438419451e-03f, +1.960461229e-03f, +1.642812632e-02f, +1.378613431e-02f, -2.549983702e-02f, -6.146136934e-02f, -8.383409345e-03f, +1.594963158e-01f, +3.366346688e-01f, +3.722429273e-01f, +2.337481127e-01f, +4.402826232e-02f, -5.598126618e-02f, -4.380589142e-02f, +2.869766685e-03f, +1.923802927e-02f, +7.310594668e-03f, -3.516087186e-03f, -3.304797071e-03f,
- /* 20,12 */ -1.785766437e-03f, -4.446907145e-03f, +1.192336051e-03f, +1.567326271e-02f, +1.503425320e-02f, -2.235071271e-02f, -6.096825350e-02f, -1.544725782e-02f, +1.470646693e-01f, +3.284457292e-01f, +3.757020336e-01f, +2.456603330e-01f, +5.427598051e-02f, -5.349467921e-02f, -4.654987033e-02f, +5.112106557e-04f, +1.933799402e-02f, +8.281610454e-03f, -3.197865053e-03f, -3.505092707e-03f,
- /* 20,13 */ -1.577606548e-03f, -4.420601658e-03f, +4.690483654e-04f, +1.486058724e-02f, +1.611719780e-02f, -1.922814732e-02f, -6.012863981e-02f, -2.200314505e-02f, +1.347297046e-01f, +3.196909791e-01f, +3.784070525e-01f, +2.573230589e-01f, +6.489576800e-02f, -5.052030051e-02f, -4.914254142e-02f, -1.984565051e-03f, +1.931089692e-02f, +9.259646102e-03f, -2.828715617e-03f, -3.693729756e-03f,
- /* 20,14 */ -1.376799150e-03f, -4.362323551e-03f, -2.069933478e-04f, +1.399907244e-02f, +1.703673197e-02f, -1.615031726e-02f, -5.896552129e-02f, -2.804532708e-02f, +1.225315522e-01f, +3.104047352e-01f, +3.803470604e-01f, +2.686929243e-01f, +7.585987841e-02f, -4.704785126e-02f, -5.155911253e-02f, -4.608279480e-03f, +1.914947854e-02f, +1.023778220e-02f, -2.407896178e-03f, -3.867923854e-03f,
- /* 20,15 */ -1.184613130e-03f, -4.274968522e-03f, -8.338854378e-04f, +1.309754391e-02f, +1.779561475e-02f, -1.313424017e-02f, -5.750258783e-02f, -3.357053763e-02f, +1.105090175e-01f, +3.006231905e-01f, +3.815142140e-01f, +2.797272456e-01f, +8.713841011e-02f, -4.306909662e-02f, -5.377462232e-02f, -7.349314558e-03f, +1.884704309e-02f, +1.120868183e-02f, -1.935046598e-03f, -4.024812873e-03f,
- /* 20, 0 */ -1.329352252e-03f, -4.865562069e-03f, +1.662947600e-03f, +1.893743982e-02f, +1.052975469e-02f, -4.314924294e-02f, -6.168215525e-02f, +6.793829558e-02f, +3.007295231e-01f, +4.214013440e-01f, +3.007295231e-01f, +6.793829558e-02f, -6.168215525e-02f, -4.314924294e-02f, +1.052975469e-02f, +1.893743982e-02f, +1.662947600e-03f, -4.865562069e-03f, -1.329352252e-03f, +0.000000000e+00f,
- /* 20, 1 */ -1.106503038e-03f, -4.784011640e-03f, +7.620481209e-04f, +1.810159639e-02f, +1.258770510e-02f, -3.946126222e-02f, -6.412166469e-02f, +5.516844650e-02f, +2.870012762e-01f, +4.208780002e-01f, +3.139874692e-01f, +8.118253044e-02f, -5.860314053e-02f, -4.671882663e-02f, +8.254179692e-03f, +1.967037055e-02f, +2.622520317e-03f, -4.905078775e-03f, -1.565095816e-03f, +0.000000000e+00f,
- /* 20, 2 */ -8.979094201e-04f, -4.664743082e-03f, -7.633034189e-05f, +1.717630323e-02f, +1.442449893e-02f, -3.568911340e-02f, -6.594250862e-02f, +4.291292121e-02f, +2.728656387e-01f, +4.193105477e-01f, +3.267137817e-01f, +9.485763802e-02f, -5.486676259e-02f, -5.013477905e-02f, +5.766539049e-03f, +2.028700325e-02f, +3.636071544e-03f, -4.898357639e-03f, -1.812078842e-03f, +3.513221827e-04f,
- /* 20, 3 */ -7.046452899e-04f, -4.512131585e-03f, -8.491713087e-04f, +1.617498084e-02f, +1.603855621e-02f, -3.186582692e-02f, -6.716828458e-02f, +3.120792005e-02f, +2.583867198e-01f, +4.167067067e-01f, +3.388490774e-01f, +1.089167196e-01f, -5.045835457e-02f, -5.336106841e-02f, +3.074480429e-03f, +2.077415592e-02f, +4.698051453e-03f, -4.841360048e-03f, -2.068349661e-03f, +3.288882594e-04f,
- /* 20, 4 */ -5.275063595e-04f, -4.330562617e-03f, -1.554266965e-03f, +1.511089362e-02f, +1.743016199e-02f, -2.802305698e-02f, -6.782511100e-02f, +2.008577030e-02f, +2.436294448e-01f, +4.130792899e-01f, +3.503362849e-01f, +1.233097059e-01f, -4.536663323e-02f, -5.636108634e-02f, +1.877878266e-04f, +2.111898038e-02f, +5.802054958e-03f, -4.730268616e-03f, -2.331660076e-03f, +2.941732022e-04f,
- /* 20, 5 */ -3.670219514e-04f, -4.124385552e-03f, -2.190189120e-03f, +1.399704337e-02f, +1.860136846e-02f, -2.419092016e-02f, -6.794137953e-02f, +9.574818877e-03f, +2.286591711e-01f, +4.084461208e-01f, +3.611209950e-01f, +1.379835966e-01f, -3.958387309e-02f, -5.909788086e-02f, -2.881585959e-03f, +2.130909659e-02f, +6.940829951e-03f, -4.561544087e-03f, -2.599469210e-03f, +2.461206998e-04f,
- /* 20, 6 */ -2.234691091e-04f, -3.897870552e-03f, -2.756254356e-03f, +1.284607053e-02f, +1.955588746e-02f, -2.039785121e-02f, -6.754749865e-02f, -3.006469464e-04f, +2.135413047e-01f, +4.028299211e-01f, +3.711517965e-01f, +1.528827232e-01f, -3.310606021e-02f, -6.153439984e-02f, -6.119502817e-03f, +2.133272965e-02f, +8.106294302e-03f, -4.331982887e-03f, -2.868951124e-03f, +1.837671888e-04f,
- /* 20, 7 */ -9.688872179e-05f, -3.655168976e-03f, -3.252484018e-03f, +1.167016373e-02f, +2.029897446e-02f, -1.667047641e-02f, -6.667563061e-02f, -9.520450398e-03f, +1.983409219e-01f, +3.962581664e-01f, +3.803805952e-01f, +1.679490334e-01f, -2.593302438e-02f, -6.363374348e-02f, -9.509639499e-03f, +2.117884859e-02f, +9.289561904e-03f, -4.038774728e-03f, -3.137006363e-03f, +1.062635081e-04f,
- /* 20, 8 */ +1.289664191e-05f, -3.400277535e-03f, -3.679559671e-03f, +1.048097797e-02f, +2.083730557e-02f, -1.303350512e-02f, -6.535942396e-02f, -1.806854797e-02f, +1.831223958e-01f, +3.887629129e-01f, +3.887629129e-01f, +1.831223958e-01f, -1.806854797e-02f, -6.535942396e-02f, -1.303350512e-02f, +2.083730557e-02f, +1.048097797e-02f, -3.679559671e-03f, -3.400277535e-03f, +1.289664191e-05f,
- /* 20, 9 */ +1.062635081e-04f, -3.137006363e-03f, -4.038774728e-03f, +9.289561904e-03f, +2.117884859e-02f, -9.509639499e-03f, -6.363374348e-02f, -2.593302438e-02f, +1.679490334e-01f, +3.803805952e-01f, +3.962581664e-01f, +1.983409219e-01f, -9.520450398e-03f, -6.667563061e-02f, -1.667047641e-02f, +2.029897446e-02f, +1.167016373e-02f, -3.252484018e-03f, -3.655168976e-03f, -9.688872179e-05f,
- /* 20,10 */ +1.837671888e-04f, -2.868951124e-03f, -4.331982887e-03f, +8.106294302e-03f, +2.133272965e-02f, -6.119502817e-03f, -6.153439984e-02f, -3.310606021e-02f, +1.528827232e-01f, +3.711517965e-01f, +4.028299211e-01f, +2.135413047e-01f, -3.006469464e-04f, -6.754749865e-02f, -2.039785121e-02f, +1.955588746e-02f, +1.284607053e-02f, -2.756254356e-03f, -3.897870552e-03f, -2.234691091e-04f,
- /* 20,11 */ +2.461206998e-04f, -2.599469210e-03f, -4.561544087e-03f, +6.940829951e-03f, +2.130909659e-02f, -2.881585959e-03f, -5.909788086e-02f, -3.958387309e-02f, +1.379835966e-01f, +3.611209950e-01f, +4.084461208e-01f, +2.286591711e-01f, +9.574818877e-03f, -6.794137953e-02f, -2.419092016e-02f, +1.860136846e-02f, +1.399704337e-02f, -2.190189120e-03f, -4.124385552e-03f, -3.670219514e-04f,
- /* 20,12 */ +2.941732022e-04f, -2.331660076e-03f, -4.730268616e-03f, +5.802054958e-03f, +2.111898038e-02f, +1.877878266e-04f, -5.636108634e-02f, -4.536663323e-02f, +1.233097059e-01f, +3.503362849e-01f, +4.130792899e-01f, +2.436294448e-01f, +2.008577030e-02f, -6.782511100e-02f, -2.802305698e-02f, +1.743016199e-02f, +1.511089362e-02f, -1.554266965e-03f, -4.330562617e-03f, -5.275063595e-04f,
- /* 20,13 */ +3.288882594e-04f, -2.068349661e-03f, -4.841360048e-03f, +4.698051453e-03f, +2.077415592e-02f, +3.074480429e-03f, -5.336106841e-02f, -5.045835457e-02f, +1.089167196e-01f, +3.388490774e-01f, +4.167067067e-01f, +2.583867198e-01f, +3.120792005e-02f, -6.716828458e-02f, -3.186582692e-02f, +1.603855621e-02f, +1.617498084e-02f, -8.491713087e-04f, -4.512131585e-03f, -7.046452899e-04f,
- /* 20,14 */ +3.513221827e-04f, -1.812078842e-03f, -4.898357639e-03f, +3.636071544e-03f, +2.028700325e-02f, +5.766539049e-03f, -5.013477905e-02f, -5.486676259e-02f, +9.485763802e-02f, +3.267137817e-01f, +4.193105477e-01f, +2.728656387e-01f, +4.291292121e-02f, -6.594250862e-02f, -3.568911340e-02f, +1.442449893e-02f, +1.717630323e-02f, -7.633034189e-05f, -4.664743082e-03f, -8.979094201e-04f,
- /* 20,15 */ +0.000000000e+00f, -1.565095816e-03f, -4.905078775e-03f, +2.622520317e-03f, +1.967037055e-02f, +8.254179692e-03f, -4.671882663e-02f, -5.860314053e-02f, +8.118253044e-02f, +3.139874692e-01f, +4.208780002e-01f, +2.870012762e-01f, +5.516844650e-02f, -6.412166469e-02f, -3.946126222e-02f, +1.258770510e-02f, +1.810159639e-02f, +7.620481209e-04f, -4.784011640e-03f, -1.106503038e-03f,
- /* 20, 0 */ +3.735125865e-04f, -2.550984103e-03f, -4.871486096e-03f, +1.016287769e-02f, +2.252246682e-02f, -2.231523982e-02f, -7.431762424e-02f, +3.414137659e-02f, +3.062278786e-01f, +4.608988972e-01f, +3.062278786e-01f, +3.414137659e-02f, -7.431762424e-02f, -2.231523982e-02f, +2.252246682e-02f, +1.016287769e-02f, -4.871486096e-03f, -2.550984103e-03f, +3.735125865e-04f, +0.000000000e+00f,
- /* 20, 1 */ +3.929324583e-04f, -2.236335973e-03f, -5.106050653e-03f, +8.748210493e-03f, +2.303691111e-02f, -1.786093260e-02f, -7.411924916e-02f, +2.086992015e-02f, +2.890848421e-01f, +4.602142272e-01f, +3.228796668e-01f, +4.817530421e-02f, -7.382833631e-02f, -2.685541297e-02f, +2.174514756e-02f, +1.158816420e-02f, -4.555417854e-03f, -2.871502680e-03f, +3.387491377e-04f, +0.000000000e+00f,
- /* 20, 2 */ +0.000000000e+00f, -1.931175707e-03f, -5.263447672e-03f, +7.357928950e-03f, +2.330080531e-02f, -1.352771902e-02f, -7.327813327e-02f, +8.401230311e-03f, +2.715429904e-01f, +4.581642525e-01f, +3.389493512e-01f, +6.292517925e-02f, -7.260941515e-02f, -3.144322519e-02f, +2.069454672e-02f, +1.300917115e-02f, -4.154170312e-03f, -3.193828376e-03f, +2.870815261e-04f, +0.000000000e+00f,
- /* 20, 3 */ +0.000000000e+00f, -1.638662038e-03f, -5.348575554e-03f, +6.004591146e-03f, +2.332816092e-02f, -9.347674729e-03f, -7.184169597e-02f, -3.230759097e-03f, +2.536956771e-01f, +4.547610488e-01f, +3.543483057e-01f, +7.833843581e-02f, -7.062228683e-02f, -3.603763775e-02f, +1.936240016e-02f, +1.440996064e-02f, -3.664825055e-03f, -3.513472060e-03f, +2.170808154e-04f, +0.000000000e+00f,
- /* 20, 4 */ +0.000000000e+00f, -1.361489293e-03f, -5.366796329e-03f, +4.699488665e-03f, +2.313444000e-02f, -5.349604768e-03f, -6.985939290e-02f, -1.399855627e-02f, +2.356365195e-01f, +4.500246402e-01f, +3.689907742e-01f, +9.435670405e-02f, -6.783220328e-02f, -4.059502103e-02f, +1.774280068e-02f, +1.577366666e-02f, -3.085314600e-03f, -3.825546457e-03f, +1.274866066e-04f, +0.000000000e+00f,
- /* 20, 5 */ +0.000000000e+00f, -1.101888322e-03f, -5.323837897e-03f, +3.452605627e-03f, +2.273631696e-02f, -1.558959414e-03f, -6.738225311e-02f, -2.388113922e-02f, +2.174587480e-01f, +4.439828472e-01f, +3.827944964e-01f, +1.109160995e-01f, -6.420865295e-02f, -4.506940842e-02f, +1.583239979e-02f, +1.708262332e-02f, -2.414511840e-03f, -4.124801502e-03f, +1.724424543e-05f, +0.000000000e+00f,
- /* 20, 6 */ +0.000000000e+00f, -8.616336525e-04f, -5.225698259e-03f, +2.272594520e-03f, +2.215144089e-02f, +2.002216238e-03f, -6.446241843e-02f, -3.286393171e-02f, +1.992545658e-01f, +4.366710759e-01f, +3.956813102e-01f, +1.279475618e-01f, -5.972574809e-02f, -4.941278189e-02f, +1.363059437e-02f, +1.831850983e-02f, -1.652313816e-03f, -4.405667401e-03f, -1.144582079e-04f, +0.000000000e+00f,
- /* 20, 7 */ +0.000000000e+00f, -6.420563626e-04f, -5.078552799e-03f, +1.166768183e-03f, +2.139820068e-02f, +5.315299677e-03f, -6.115268907e-02f, -4.093872873e-02f, +1.811145256e-01f, +4.281320500e-01f, +4.075777294e-01f, +1.453772407e-01f, -5.436258502e-02f, -5.357538755e-02f, +1.113969540e-02f, +1.946251142e-02f, -7.997184460e-04f, -4.662305323e-03f, -2.681538305e-04f, +0.000000000e+00f,
- /* 20, 8 */ +0.000000000e+00f, -4.440621234e-04f, -4.888665552e-03f, +1.411071672e-04f, +2.049549532e-02f, +8.365076494e-03f, -5.750607943e-02f, -4.810357305e-02f, +1.631269271e-01f, +4.184154881e-01f, +4.184154881e-01f, +1.631269271e-01f, -4.810357305e-02f, -5.750607943e-02f, +8.365076494e-03f, +2.049549532e-02f, +1.411071672e-04f, -4.888665552e-03f, -4.440621234e-04f, +0.000000000e+00f,
- /* 20, 9 */ +0.000000000e+00f, -2.681538305e-04f, -4.662305323e-03f, -7.997184460e-04f, +1.946251142e-02f, +1.113969540e-02f, -5.357538755e-02f, -5.436258502e-02f, +1.453772407e-01f, +4.075777294e-01f, +4.281320500e-01f, +1.811145256e-01f, -4.093872873e-02f, -6.115268907e-02f, +5.315299677e-03f, +2.139820068e-02f, +1.166768183e-03f, -5.078552799e-03f, -6.420563626e-04f, +0.000000000e+00f,
- /* 20,10 */ +0.000000000e+00f, -1.144582079e-04f, -4.405667401e-03f, -1.652313816e-03f, +1.831850983e-02f, +1.363059437e-02f, -4.941278189e-02f, -5.972574809e-02f, +1.279475618e-01f, +3.956813102e-01f, +4.366710759e-01f, +1.992545658e-01f, -3.286393171e-02f, -6.446241843e-02f, +2.002216238e-03f, +2.215144089e-02f, +2.272594520e-03f, -5.225698259e-03f, -8.616336525e-04f, +0.000000000e+00f,
- /* 20,11 */ +0.000000000e+00f, +1.724424543e-05f, -4.124801502e-03f, -2.414511840e-03f, +1.708262332e-02f, +1.583239979e-02f, -4.506940842e-02f, -6.420865295e-02f, +1.109160995e-01f, +3.827944964e-01f, +4.439828472e-01f, +2.174587480e-01f, -2.388113922e-02f, -6.738225311e-02f, -1.558959414e-03f, +2.273631696e-02f, +3.452605627e-03f, -5.323837897e-03f, -1.101888322e-03f, +0.000000000e+00f,
- /* 20,12 */ +0.000000000e+00f, +1.274866066e-04f, -3.825546457e-03f, -3.085314600e-03f, +1.577366666e-02f, +1.774280068e-02f, -4.059502103e-02f, -6.783220328e-02f, +9.435670405e-02f, +3.689907742e-01f, +4.500246402e-01f, +2.356365195e-01f, -1.399855627e-02f, -6.985939290e-02f, -5.349604768e-03f, +2.313444000e-02f, +4.699488665e-03f, -5.366796329e-03f, -1.361489293e-03f, +0.000000000e+00f,
- /* 20,13 */ +0.000000000e+00f, +2.170808154e-04f, -3.513472060e-03f, -3.664825055e-03f, +1.440996064e-02f, +1.936240016e-02f, -3.603763775e-02f, -7.062228683e-02f, +7.833843581e-02f, +3.543483057e-01f, +4.547610488e-01f, +2.536956771e-01f, -3.230759097e-03f, -7.184169597e-02f, -9.347674729e-03f, +2.332816092e-02f, +6.004591146e-03f, -5.348575554e-03f, -1.638662038e-03f, +0.000000000e+00f,
- /* 20,14 */ +0.000000000e+00f, +2.870815261e-04f, -3.193828376e-03f, -4.154170312e-03f, +1.300917115e-02f, +2.069454672e-02f, -3.144322519e-02f, -7.260941515e-02f, +6.292517925e-02f, +3.389493512e-01f, +4.581642525e-01f, +2.715429904e-01f, +8.401230311e-03f, -7.327813327e-02f, -1.352771902e-02f, +2.330080531e-02f, +7.357928950e-03f, -5.263447672e-03f, -1.931175707e-03f, +0.000000000e+00f,
- /* 20,15 */ +0.000000000e+00f, +3.387491377e-04f, -2.871502680e-03f, -4.555417854e-03f, +1.158816420e-02f, +2.174514756e-02f, -2.685541297e-02f, -7.382833631e-02f, +4.817530421e-02f, +3.228796668e-01f, +4.602142272e-01f, +2.890848421e-01f, +2.086992015e-02f, -7.411924916e-02f, -1.786093260e-02f, +2.303691111e-02f, +8.748210493e-03f, -5.106050653e-03f, -2.236335973e-03f, +3.929324583e-04f,
- /* 16, 0 */ -4.898743621e-03f, -8.679086087e-05f, +2.336043359e-02f, +2.135055302e-04f, -7.556698393e-02f, -3.418085064e-04f, +3.068350485e-01f, +5.003964504e-01f, +3.068350485e-01f, -3.418085064e-04f, -7.556698393e-02f, +2.135055302e-04f, +2.336043359e-02f, -8.679086087e-05f, -4.898743621e-03f, +1.466795211e-05f,
- /* 16, 1 */ -4.577177643e-03f, -1.168030162e-03f, +2.231338881e-02f, +4.259286102e-03f, -7.256190983e-02f, -1.325523148e-02f, +2.859961851e-01f, +4.995203198e-01f, +3.272077887e-01f, +1.366916740e-02f, -7.794631959e-02f, -4.137969722e-03f, +2.421268789e-02f, +1.104119466e-03f, -5.186216174e-03f, -1.424716020e-04f,
- /* 16, 2 */ -4.229744004e-03f, -2.135347302e-03f, +2.109817837e-02f, +7.975999347e-03f, -6.900371451e-02f, -2.503996167e-02f, +2.648211478e-01f, +4.968980143e-01f, +3.469854770e-01f, +2.873680235e-02f, -7.962890015e-02f, -8.766610174e-03f, +2.484400873e-02f, +2.398541233e-03f, -5.431090074e-03f, -3.278051009e-04f,
- /* 16, 3 */ -3.864289504e-03f, -2.986316790e-03f, +1.974155805e-02f, +1.134537917e-02f, -6.496587406e-02f, -3.567459207e-02f, +2.434398342e-01f, +4.925477372e-01f, +3.660412816e-01f, +4.481051979e-02f, -8.054606315e-02f, -1.363873477e-02f, +2.522910176e-02f, +3.788344934e-03f, -5.624692566e-03f, -5.415628140e-04f,
- /* 16, 4 */ -3.488195595e-03f, -3.720237037e-03f, +1.827008292e-02f, +1.435416313e-02f, -6.052200094e-02f, -4.514733704e-02f, +2.219810322e-01f, +4.864996469e-01f, +3.842515379e-01f, +6.183022559e-02f, -8.063225815e-02f, -1.871557408e-02f, +2.534390000e-02f, +5.263393235e-03f, -5.758306879e-03f, -7.834433658e-04f,
- /* 16, 5 */ -3.108305495e-03f, -4.338012209e-03f, +1.670979794e-02f, +1.699394035e-02f, -5.574514781e-02f, -5.345583367e-02f, +2.005713869e-01f, +4.787955863e-01f, +4.014968013e-01f, +7.972656727e-02f, -7.982580067e-02f, -2.395339311e-02f, +2.516595041e-02f, +6.811528665e-03f, -5.823306183e-03f, -1.052565974e-03f,
- /* 16, 6 */ -2.730865011e-03f, -4.842020290e-03f, +1.508595356e-02f, +1.926095418e-02f, -5.070714412e-02f, -6.060686010e-02f, +1.793344024e-01f, +4.694887096e-01f, +4.176628724e-01f, +9.842128660e-02f, -7.806961406e-02f, -2.930367505e-02f, +2.467480385e-02f, +8.418588685e-03f, -5.811296621e-03f, -1.347428958e-03f,
- /* 16, 7 */ -2.361477017e-03f, -5.235970004e-03f, +1.342274837e-02f, +2.115586371e-02f, -4.547797122e-02f, -6.661597542e-02f, +1.583894867e-01f, +4.586430086e-01f, +4.326417845e-01f, +1.178276637e-01f, -7.531195111e-02f, -3.471336612e-02f, +2.385240383e-02f, +1.006844961e-02f, -5.714267850e-03f, -1.665875716e-03f,
- /* 16, 8 */ -2.005069351e-03f, -5.524749278e-03f, +1.174310057e-02f, +2.268346885e-02f, -4.012518151e-02f, -7.150708699e-02f, +1.378510501e-01f, +4.463327434e-01f, +4.463327434e-01f, +1.378510501e-01f, -7.150708699e-02f, -4.012518151e-02f, +2.268346885e-02f, +1.174310057e-02f, -5.524749278e-03f, -2.005069351e-03f,
- /* 16, 9 */ -1.665875716e-03f, -5.714267850e-03f, +1.006844961e-02f, +2.385240383e-02f, -3.471336612e-02f, -7.531195111e-02f, +1.178276637e-01f, +4.326417845e-01f, +4.586430086e-01f, +1.583894867e-01f, -6.661597542e-02f, -4.547797122e-02f, +2.115586371e-02f, +1.342274837e-02f, -5.235970004e-03f, -2.361477017e-03f,
- /* 16,10 */ -1.347428958e-03f, -5.811296621e-03f, +8.418588685e-03f, +2.467480385e-02f, -2.930367505e-02f, -7.806961406e-02f, +9.842128660e-02f, +4.176628724e-01f, +4.694887096e-01f, +1.793344024e-01f, -6.060686010e-02f, -5.070714412e-02f, +1.926095418e-02f, +1.508595356e-02f, -4.842020290e-03f, -2.730865011e-03f,
- /* 16,11 */ -1.052565974e-03f, -5.823306183e-03f, +6.811528665e-03f, +2.516595041e-02f, -2.395339311e-02f, -7.982580067e-02f, +7.972656727e-02f, +4.014968013e-01f, +4.787955863e-01f, +2.005713869e-01f, -5.345583367e-02f, -5.574514781e-02f, +1.699394035e-02f, +1.670979794e-02f, -4.338012209e-03f, -3.108305495e-03f,
- /* 16,12 */ -7.834433658e-04f, -5.758306879e-03f, +5.263393235e-03f, +2.534390000e-02f, -1.871557408e-02f, -8.063225815e-02f, +6.183022559e-02f, +3.842515379e-01f, +4.864996469e-01f, +2.219810322e-01f, -4.514733704e-02f, -6.052200094e-02f, +1.435416313e-02f, +1.827008292e-02f, -3.720237037e-03f, -3.488195595e-03f,
- /* 16,13 */ -5.415628140e-04f, -5.624692566e-03f, +3.788344934e-03f, +2.522910176e-02f, -1.363873477e-02f, -8.054606315e-02f, +4.481051979e-02f, +3.660412816e-01f, +4.925477372e-01f, +2.434398342e-01f, -3.567459207e-02f, -6.496587406e-02f, +1.134537917e-02f, +1.974155805e-02f, -2.986316790e-03f, -3.864289504e-03f,
- /* 16,14 */ -3.278051009e-04f, -5.431090074e-03f, +2.398541233e-03f, +2.484400873e-02f, -8.766610174e-03f, -7.962890015e-02f, +2.873680235e-02f, +3.469854770e-01f, +4.968980143e-01f, +2.648211478e-01f, -2.503996167e-02f, -6.900371451e-02f, +7.975999347e-03f, +2.109817837e-02f, -2.135347302e-03f, -4.229744004e-03f,
- /* 16,15 */ -1.424716020e-04f, -5.186216174e-03f, +1.104119466e-03f, +2.421268789e-02f, -4.137969722e-03f, -7.794631959e-02f, +1.366916740e-02f, +3.272077887e-01f, +4.995203198e-01f, +2.859961851e-01f, -1.325523148e-02f, -7.256190983e-02f, +4.259286102e-03f, +2.231338881e-02f, -1.168030162e-03f, -4.577177643e-03f,
- /* 16, 0 */ -1.854349243e-03f, -5.842655877e-03f, +1.571555836e-02f, +1.847159410e-02f, -6.634453543e-02f, -3.320569278e-02f, +3.025932104e-01f, +5.398940036e-01f, +3.025932104e-01f, -3.320569278e-02f, -6.634453543e-02f, +1.847159410e-02f, +1.571555836e-02f, -5.842655877e-03f, -1.854349243e-03f, +0.000000000e+00f,
- /* 16, 1 */ -1.480579358e-03f, -6.106700866e-03f, +1.376986381e-02f, +2.107103425e-02f, -6.084498265e-02f, -4.482173865e-02f, +2.778559558e-01f, +5.387937054e-01f, +3.269511876e-01f, -2.013603096e-02f, -7.140657205e-02f, +1.540395578e-02f, +1.762103499e-02f, -5.450677830e-03f, -2.253515747e-03f, +0.000000000e+00f,
- /* 16, 2 */ -1.136163241e-03f, -6.251562574e-03f, +1.181432209e-02f, +2.320368920e-02f, -5.500558000e-02f, -5.497544958e-02f, +2.529151163e-01f, +5.355017071e-01f, +3.507537104e-01f, -5.635398845e-03f, -7.593183970e-02f, +1.187314151e-02f, +1.945395611e-02f, -4.923375547e-03f, -2.673102395e-03f, +0.000000000e+00f,
- /* 16, 3 */ -8.240613879e-04f, -6.287094834e-03f, +9.877041313e-03f, +2.487696888e-02f, -4.892141083e-02f, -6.367164518e-02f, +2.279442418e-01f, +5.300446041e-01f, +3.738258052e-01f, +1.025938806e-02f, -7.982055919e-02f, +7.890985370e-03f, +2.118036474e-02f, -4.254967852e-03f, -3.107109230e-03f, +0.000000000e+00f,
- /* 16, 4 */ -5.462770218e-04f, -6.224002243e-03f, +7.983624178e-03f, +2.610378910e-02f, -4.268405269e-02f, -7.092815895e-02f, +2.031131300e-01f, +5.224664143e-01f, +3.959953988e-01f, +2.749725254e-02f, -8.297353764e-02f, +3.476439880e-03f, +2.276508169e-02f, -3.441527233e-03f, -3.548528769e-03f, +4.634120047e-04f,
- /* 16, 5 */ -3.039101756e-04f, -6.073592210e-03f, +6.156978545e-03f, +2.690203687e-02f, -3.638073666e-02f, -7.677518685e-02f, +1.785862812e-01f, +5.128281199e-01f, +4.170950072e-01f, +4.601292009e-02f, -8.529332152e-02f, -1.344195268e-03f, +2.417214928e-02f, -2.481210963e-03f, -3.989383394e-03f, +4.400286560e-04f,
- /* 16, 6 */ -9.722433303e-05f, -5.847536081e-03f, +4.417180930e-03f, +2.729400173e-02f, -3.009359622e-02f, -8.125451993e-02f, +1.545214364e-01f, +5.012070337e-01f, +4.369633957e-01f, +6.572714234e-02f, -8.668537697e-02f, -6.537129252e-03f, +2.536531778e-02f, -1.374474827e-03f, -4.420785166e-03f, +3.921992729e-04f,
- /* 16, 7 */ +7.427658644e-05f, -5.557642708e-03f, +2.781391675e-03f, +2.730578215e-02f, -2.389901141e-02f, -8.441867356e-02f, +1.310682124e-01f, +4.876959980e-01f, +4.554471907e-01f, +8.654708074e-02f, -8.705928349e-02f, -1.206102709e-02f, +2.630856978e-02f, -1.242645535e-04f, -4.833018707e-03f, +3.169896142e-04f,
- /* 16, 8 */ +2.117631665e-04f, -5.215647395e-03f, +1.263819875e-03f, +2.696667686e-02f, -1.786705263e-02f, -8.632992647e-02f, +1.083668491e-01f, +4.724024249e-01f, +4.724024249e-01f, +1.083668491e-01f, -8.632992647e-02f, -1.786705263e-02f, +2.696667686e-02f, +1.263819875e-03f, -5.215647395e-03f, +2.117631665e-04f,
- /* 16, 9 */ +3.169896142e-04f, -4.833018707e-03f, -1.242645535e-04f, +2.630856978e-02f, -1.206102709e-02f, -8.705928349e-02f, +8.654708074e-02f, +4.554471907e-01f, +4.876959980e-01f, +1.310682124e-01f, -8.441867356e-02f, -2.389901141e-02f, +2.730578215e-02f, +2.781391675e-03f, -5.557642708e-03f, +7.427658644e-05f,
- /* 16,10 */ +3.921992729e-04f, -4.420785166e-03f, -1.374474827e-03f, +2.536531778e-02f, -6.537129252e-03f, -8.668537697e-02f, +6.572714234e-02f, +4.369633957e-01f, +5.012070337e-01f, +1.545214364e-01f, -8.125451993e-02f, -3.009359622e-02f, +2.729400173e-02f, +4.417180930e-03f, -5.847536081e-03f, -9.722433303e-05f,
- /* 16,11 */ +4.400286560e-04f, -3.989383394e-03f, -2.481210963e-03f, +2.417214928e-02f, -1.344195268e-03f, -8.529332152e-02f, +4.601292009e-02f, +4.170950072e-01f, +5.128281199e-01f, +1.785862812e-01f, -7.677518685e-02f, -3.638073666e-02f, +2.690203687e-02f, +6.156978545e-03f, -6.073592210e-03f, -3.039101756e-04f,
- /* 16,12 */ +4.634120047e-04f, -3.548528769e-03f, -3.441527233e-03f, +2.276508169e-02f, +3.476439880e-03f, -8.297353764e-02f, +2.749725254e-02f, +3.959953988e-01f, +5.224664143e-01f, +2.031131300e-01f, -7.092815895e-02f, -4.268405269e-02f, +2.610378910e-02f, +7.983624178e-03f, -6.224002243e-03f, -5.462770218e-04f,
- /* 16,13 */ +0.000000000e+00f, -3.107109230e-03f, -4.254967852e-03f, +2.118036474e-02f, +7.890985370e-03f, -7.982055919e-02f, +1.025938806e-02f, +3.738258052e-01f, +5.300446041e-01f, +2.279442418e-01f, -6.367164518e-02f, -4.892141083e-02f, +2.487696888e-02f, +9.877041313e-03f, -6.287094834e-03f, -8.240613879e-04f,
- /* 16,14 */ +0.000000000e+00f, -2.673102395e-03f, -4.923375547e-03f, +1.945395611e-02f, +1.187314151e-02f, -7.593183970e-02f, -5.635398845e-03f, +3.507537104e-01f, +5.355017071e-01f, +2.529151163e-01f, -5.497544958e-02f, -5.500558000e-02f, +2.320368920e-02f, +1.181432209e-02f, -6.251562574e-03f, -1.136163241e-03f,
- /* 16,15 */ +0.000000000e+00f, -2.253515747e-03f, -5.450677830e-03f, +1.762103499e-02f, +1.540395578e-02f, -7.140657205e-02f, -2.013603096e-02f, +3.269511876e-01f, +5.387937054e-01f, +2.778559558e-01f, -4.482173865e-02f, -6.084498265e-02f, +2.107103425e-02f, +1.376986381e-02f, -6.106700866e-03f, -1.480579358e-03f,
- /* 16, 0 */ +2.517634455e-04f, -5.956310854e-03f, +5.008864062e-03f, +2.864631470e-02f, -4.909056125e-02f, -6.235528720e-02f, +2.936293584e-01f, +5.793915568e-01f, +2.936293584e-01f, -6.235528720e-02f, -4.909056125e-02f, +2.864631470e-02f, +5.008864062e-03f, -5.956310854e-03f, +2.517634455e-04f, +0.000000000e+00f,
- /* 16, 1 */ +3.647589216e-04f, -5.559366521e-03f, +3.110945653e-03f, +2.922667528e-02f, -4.185408685e-02f, -7.174125192e-02f, +2.648835910e-01f, +5.780318135e-01f, +3.221602930e-01f, -5.117389488e-02f, -5.619351824e-02f, +2.755457966e-02f, +7.032385926e-03f, -6.289189967e-03f, +9.939782505e-05f, +0.000000000e+00f,
- /* 16, 2 */ +4.414886472e-04f, -5.113535158e-03f, +1.357910925e-03f, +2.932892142e-02f, -3.459618474e-02f, -7.936172697e-02f, +2.361522790e-01f, +5.739652427e-01f, +3.502436629e-01f, -3.818535953e-02f, -6.304412145e-02f, +2.592390265e-02f, +9.158035052e-03f, -6.542440674e-03f, -9.477253367e-05f, +0.000000000e+00f,
- /* 16, 3 */ +4.855802427e-04f, -4.633347213e-03f, -2.351453324e-04f, +2.899108127e-02f, -2.742112952e-02f, -8.526380919e-02f, +2.076588264e-01f, +5.672296697e-01f, +3.776458156e-01f, -2.339704980e-02f, -6.951800781e-02f, +2.373330296e-02f, +1.135820669e-02f, -6.700410184e-03f, -3.323676319e-04f, +0.000000000e+00f,
- /* 16, 4 */ +0.000000000e+00f, -4.132457962e-03f, -1.657230762e-03f, +2.825501306e-02f, -2.042447313e-02f, -8.951050933e-02f, +1.796184274e-01f, +5.578876325e-01f, +4.041347532e-01f, -6.836060229e-03f, -7.548664793e-02f, +2.096923455e-02f, +1.360131724e-02f, -6.747680557e-03f, -6.140535745e-04f, +0.000000000e+00f,
- /* 16, 5 */ +0.000000000e+00f, -3.623457200e-03f, -2.901306411e-03f, +2.716546883e-02f, -1.369230379e-02f, -9.217934767e-02f, +1.522358833e-01f, +5.460256322e-01f, +4.294827240e-01f, +1.145038182e-02f, -8.081883229e-02f, +1.762637069e-02f, +1.585203938e-02f, -6.669417793e-03f, -9.394126333e-04f, +0.000000000e+00f,
- /* 16, 6 */ +0.000000000e+00f, -3.117716971e-03f, -3.964078879e-03f, +2.576917487e-02f, -7.300675124e-03f, -9.336081470e-02f, +1.257035893e-01f, +5.317530991e-01f, +4.534687988e-01f, +3.139475616e-02f, -8.538226676e-02f, +1.370830904e-02f, +1.807162423e-02f, -6.451740247e-03f, -1.306826648e-03f, +0.000000000e+00f,
- /* 16, 7 */ +0.000000000e+00f, -2.625277441e-03f, -4.845741482e-03f, +2.411394259e-02f, -1.315205805e-03f, -9.315672206e-02f, +1.001997139e-01f, +5.152010878e-01f, +4.758813956e-01f, +5.290934542e-02f, -8.904525833e-02f, +9.228181275e-03f, +2.021831098e-02f, -6.082100328e-03f, -1.713377737e-03f, +0.000000000e+00f,
- /* 16, 8 */ +0.000000000e+00f, -2.154770219e-03f, -5.549672684e-03f, +2.224782226e-02f, +4.209152176e-03f, -9.167847004e-02f, +7.588659143e-02f, +4.965207199e-01f, +4.965207199e-01f, +7.588659143e-02f, -9.167847004e-02f, +4.209152176e-03f, +2.224782226e-02f, -5.549672684e-03f, -2.154770219e-03f, +0.000000000e+00f,
- /* 16, 9 */ +0.000000000e+00f, -1.713377737e-03f, -6.082100328e-03f, +2.021831098e-02f, +9.228181275e-03f, -8.904525833e-02f, +5.290934542e-02f, +4.758813956e-01f, +5.152010878e-01f, +1.001997139e-01f, -9.315672206e-02f, -1.315205805e-03f, +2.411394259e-02f, -4.845741482e-03f, -2.625277441e-03f, +0.000000000e+00f,
- /* 16,10 */ +0.000000000e+00f, -1.306826648e-03f, -6.451740247e-03f, +1.807162423e-02f, +1.370830904e-02f, -8.538226676e-02f, +3.139475616e-02f, +4.534687988e-01f, +5.317530991e-01f, +1.257035893e-01f, -9.336081470e-02f, -7.300675124e-03f, +2.576917487e-02f, -3.964078879e-03f, -3.117716971e-03f, +0.000000000e+00f,
- /* 16,11 */ +0.000000000e+00f, -9.394126333e-04f, -6.669417793e-03f, +1.585203938e-02f, +1.762637069e-02f, -8.081883229e-02f, +1.145038182e-02f, +4.294827240e-01f, +5.460256322e-01f, +1.522358833e-01f, -9.217934767e-02f, -1.369230379e-02f, +2.716546883e-02f, -2.901306411e-03f, -3.623457200e-03f, +0.000000000e+00f,
- /* 16,12 */ +0.000000000e+00f, -6.140535745e-04f, -6.747680557e-03f, +1.360131724e-02f, +2.096923455e-02f, -7.548664793e-02f, -6.836060229e-03f, +4.041347532e-01f, +5.578876325e-01f, +1.796184274e-01f, -8.951050933e-02f, -2.042447313e-02f, +2.825501306e-02f, -1.657230762e-03f, -4.132457962e-03f, +0.000000000e+00f,
- /* 16,13 */ +0.000000000e+00f, -3.323676319e-04f, -6.700410184e-03f, +1.135820669e-02f, +2.373330296e-02f, -6.951800781e-02f, -2.339704980e-02f, +3.776458156e-01f, +5.672296697e-01f, +2.076588264e-01f, -8.526380919e-02f, -2.742112952e-02f, +2.899108127e-02f, -2.351453324e-04f, -4.633347213e-03f, +4.855802427e-04f,
- /* 16,14 */ +0.000000000e+00f, -9.477253367e-05f, -6.542440674e-03f, +9.158035052e-03f, +2.592390265e-02f, -6.304412145e-02f, -3.818535953e-02f, +3.502436629e-01f, +5.739652427e-01f, +2.361522790e-01f, -7.936172697e-02f, -3.459618474e-02f, +2.932892142e-02f, +1.357910925e-03f, -5.113535158e-03f, +4.414886472e-04f,
- /* 16,15 */ +0.000000000e+00f, +9.939782505e-05f, -6.289189967e-03f, +7.032385926e-03f, +2.755457966e-02f, -5.619351824e-02f, -5.117389488e-02f, +3.221602930e-01f, +5.780318135e-01f, +2.648835910e-01f, -7.174125192e-02f, -4.185408685e-02f, +2.922667528e-02f, +3.110945653e-03f, -5.559366521e-03f, +3.647589216e-04f,
- /* 12, 0 */ -3.638165547e-03f, +2.979985982e-02f, -2.723323293e-02f, -8.605047059e-02f, +2.801520768e-01f, +6.188891100e-01f, +2.801520768e-01f, -8.605047059e-02f, -2.723323293e-02f, +2.979985982e-02f, -3.638165547e-03f, -3.041512814e-03f,
- /* 12, 1 */ -4.749738186e-03f, +2.841159300e-02f, -1.933319589e-02f, -9.237133076e-02f, +2.473915856e-01f, +6.172320760e-01f, +3.129551421e-01f, -7.764805088e-02f, -3.538029377e-02f, +3.077779188e-02f, -2.305275216e-03f, -3.612081594e-03f,
- /* 12, 2 */ -5.642312008e-03f, +2.667449885e-02f, -1.178831271e-02f, -9.669686191e-02f, +2.149632830e-01f, +6.122785731e-01f, +3.455026876e-01f, -6.709962705e-02f, -4.365285408e-02f, +3.128617370e-02f, -7.534120996e-04f, -4.192641091e-03f,
- /* 12, 3 */ -6.322395719e-03f, +2.465107974e-02f, -4.692548813e-03f, -9.913294928e-02f, +1.831448749e-01f, +6.040811565e-01f, +3.774917553e-01f, -5.436442589e-02f, -5.191703591e-02f, +3.126943445e-02f, +1.010002855e-03f, -4.769140004e-03f,
- /* 12, 4 */ -6.800166749e-03f, +2.240361196e-02f, +1.874795505e-03f, -9.980279721e-02f, +1.521989634e-01f, +5.927266195e-01f, +4.086182709e-01f, -3.942694703e-02f, -6.002791415e-02f, +3.067703358e-02f, +2.972136287e-03f, -5.325763732e-03f,
- /* 12, 5 */ -7.088917510e-03f, +1.999301835e-02f, +7.849320649e-03f, -9.884443272e-02f, +1.223701278e-01f, +5.783348068e-01f, +4.385808709e-01f, -2.229824534e-02f, -6.783107929e-02f, +2.946485483e-02f, +5.114495497e-03f, -5.845139328e-03f,
- /* 12, 6 */ -7.204483224e-03f, +1.747784955e-02f, +1.318150805e-02f, -9.640809295e-02f, +9.388232321e-02f, +5.610569814e-01f, +4.670847512e-01f, -3.016864363e-03f, -7.516444191e-02f, +2.759658236e-02f, +7.412783505e-03f, -6.308600966e-03f,
- /* 12, 7 */ -7.164664930e-03f, +1.491338589e-02f, +1.783645872e-02f, -9.265354184e-02f, +6.693662660e-02f, +5.410737692e-01f, +4.938454794e-01f, +1.835060492e-02f, -8.186025948e-02f, +2.504503167e-02f, +9.836867291e-03f, -6.696514785e-03f,
- /* 12, 8 */ -6.988660420e-03f, +1.235086875e-02f, +2.179340724e-02f, -8.774736114e-02f, +4.170935934e-02f, +5.185927134e-01f, +5.185927134e-01f, +4.170935934e-02f, -8.774736114e-02f, +2.179340724e-02f, +1.235086875e-02f, -6.988660420e-03f,
- /* 12, 9 */ -6.696514785e-03f, +9.836867291e-03f, +2.504503167e-02f, -8.186025948e-02f, +1.835060492e-02f, +4.938454794e-01f, +5.410737692e-01f, +6.693662660e-02f, -9.265354184e-02f, +1.783645872e-02f, +1.491338589e-02f, -7.164664930e-03f,
- /* 12,10 */ -6.308600966e-03f, +7.412783505e-03f, +2.759658236e-02f, -7.516444191e-02f, -3.016864363e-03f, +4.670847512e-01f, +5.610569814e-01f, +9.388232321e-02f, -9.640809295e-02f, +1.318150805e-02f, +1.747784955e-02f, -7.204483224e-03f,
- /* 12,11 */ -5.845139328e-03f, +5.114495497e-03f, +2.946485483e-02f, -6.783107929e-02f, -2.229824534e-02f, +4.385808709e-01f, +5.783348068e-01f, +1.223701278e-01f, -9.884443272e-02f, +7.849320649e-03f, +1.999301835e-02f, -7.088917510e-03f,
- /* 12,12 */ -5.325763732e-03f, +2.972136287e-03f, +3.067703358e-02f, -6.002791415e-02f, -3.942694703e-02f, +4.086182709e-01f, +5.927266195e-01f, +1.521989634e-01f, -9.980279721e-02f, +1.874795505e-03f, +2.240361196e-02f, -6.800166749e-03f,
- /* 12,13 */ -4.769140004e-03f, +1.010002855e-03f, +3.126943445e-02f, -5.191703591e-02f, -5.436442589e-02f, +3.774917553e-01f, +6.040811565e-01f, +1.831448749e-01f, -9.913294928e-02f, -4.692548813e-03f, +2.465107974e-02f, -6.322395719e-03f,
- /* 12,14 */ -4.192641091e-03f, -7.534120996e-04f, +3.128617370e-02f, -4.365285408e-02f, -6.709962705e-02f, +3.455026876e-01f, +6.122785731e-01f, +2.149632830e-01f, -9.669686191e-02f, -1.178831271e-02f, +2.667449885e-02f, -5.642312008e-03f,
- /* 12,15 */ -3.612081594e-03f, -2.305275216e-03f, +3.077779188e-02f, -3.538029377e-02f, -7.764805088e-02f, +3.129551421e-01f, +6.172320760e-01f, +2.473915856e-01f, -9.237133076e-02f, -1.933319589e-02f, +2.841159300e-02f, -4.749738186e-03f,
- /* 12, 0 */ -7.562702671e-03f, +2.362257603e-02f, -4.531854693e-03f, -1.030173373e-01f, +2.624467795e-01f, +6.583866631e-01f, +2.624467795e-01f, -1.030173373e-01f, -4.531854693e-03f, +2.362257603e-02f, -7.562702671e-03f, -3.516889901e-04f,
- /* 12, 1 */ -7.668183010e-03f, +2.087771707e-02f, +2.839059860e-03f, -1.056218320e-01f, +2.257778124e-01f, +6.563919279e-01f, +2.995227467e-01f, -9.814415944e-02f, -1.254420342e-02f, +2.617709089e-02f, -7.250906804e-03f, -7.142430143e-04f,
- /* 12, 2 */ -7.590423774e-03f, +1.801705410e-02f, +9.487879886e-03f, -1.061169074e-01f, +1.898705313e-01f, +6.504316937e-01f, +3.366347146e-01f, -9.086648783e-02f, -2.109702270e-02f, +2.846338313e-02f, -6.712315500e-03f, -1.140764971e-03f,
- /* 12, 3 */ -7.354275530e-03f, +1.511050856e-02f, +1.535418598e-02f, -1.046816488e-01f, +1.550586973e-01f, +6.405775033e-01f, +3.734003416e-01f, -8.107535131e-02f, -3.006937567e-02f, +3.040170317e-02f, -5.929880498e-03f, -1.628307909e-03f,
- /* 12, 4 */ -6.985575046e-03f, +1.222230420e-02f, +2.039736787e-02f, -1.015111601e-01f, +1.216509697e-01f, +6.269473635e-01f, +4.094311989e-01f, -6.869168809e-02f, -3.932109040e-02f, +3.191206547e-02f, -4.890814148e-03f, -2.171433859e-03f,
- /* 12, 5 */ -6.510406524e-03f, +9.410098002e-03f, +2.459585213e-02f, -9.681266365e-02f, +8.992722338e-02f, +6.097039216e-01f, +4.443382217e-01f, -5.366903171e-02f, -4.869391429e-02f, +3.291602689e-02f, -3.587389454e-03f, -2.762058981e-03f,
- /* 12, 6 */ -5.954426605e-03f, +6.724324555e-03f, +2.794602403e-02f, -9.080157573e-02f, +6.013541337e-02f, +5.890519583e-01f, +4.777372670e-01f, -3.599575069e-02f, -5.801308453e-02f, +3.333857894e-02f, -2.017687876e-03f, -3.389362400e-03f,
- /* 12, 7 */ -5.342264546e-03f, +4.207752570e-03f, +3.046088231e-02f, -8.369763050e-02f, +3.248902822e-02f, +5.652352421e-01f, +5.092546840e-01f, -1.569678608e-02f, -6.708930595e-02f, +3.311011989e-02f, -1.862710466e-04f, -4.039767889e-03f,
- /* 12, 8 */ -4.697006242e-03f, +1.895247343e-03f, +3.216846911e-02f, -7.572111885e-02f, +7.165160645e-03f, +5.385328020e-01f, +5.385328020e-01f, +7.165160645e-03f, -7.572111885e-02f, +3.216846911e-02f, +1.895247343e-03f, -4.697006242e-03f,
- /* 12, 9 */ -4.039767889e-03f, -1.862710466e-04f, +3.311011989e-02f, -6.708930595e-02f, -1.569678608e-02f, +5.092546840e-01f, +5.652352421e-01f, +3.248902822e-02f, -8.369763050e-02f, +3.046088231e-02f, +4.207752570e-03f, -5.342264546e-03f,
- /* 12,10 */ -3.389362400e-03f, -2.017687876e-03f, +3.333857894e-02f, -5.801308453e-02f, -3.599575069e-02f, +4.777372670e-01f, +5.890519583e-01f, +6.013541337e-02f, -9.080157573e-02f, +2.794602403e-02f, +6.724324555e-03f, -5.954426605e-03f,
- /* 12,11 */ -2.762058981e-03f, -3.587389454e-03f, +3.291602689e-02f, -4.869391429e-02f, -5.366903171e-02f, +4.443382217e-01f, +6.097039216e-01f, +8.992722338e-02f, -9.681266365e-02f, +2.459585213e-02f, +9.410098002e-03f, -6.510406524e-03f,
- /* 12,12 */ -2.171433859e-03f, -4.890814148e-03f, +3.191206547e-02f, -3.932109040e-02f, -6.869168809e-02f, +4.094311989e-01f, +6.269473635e-01f, +1.216509697e-01f, -1.015111601e-01f, +2.039736787e-02f, +1.222230420e-02f, -6.985575046e-03f,
- /* 12,13 */ -1.628307909e-03f, -5.929880498e-03f, +3.040170317e-02f, -3.006937567e-02f, -8.107535131e-02f, +3.734003416e-01f, +6.405775033e-01f, +1.550586973e-01f, -1.046816488e-01f, +1.535418598e-02f, +1.511050856e-02f, -7.354275530e-03f,
- /* 12,14 */ -1.140764971e-03f, -6.712315500e-03f, +2.846338313e-02f, -2.109702270e-02f, -9.086648783e-02f, +3.366347146e-01f, +6.504316937e-01f, +1.898705313e-01f, -1.061169074e-01f, +9.487879886e-03f, +1.801705410e-02f, -7.590423774e-03f,
- /* 12,15 */ -7.142430143e-04f, -7.250906804e-03f, +2.617709089e-02f, -1.254420342e-02f, -9.814415944e-02f, +2.995227467e-01f, +6.563919279e-01f, +2.257778124e-01f, -1.056218320e-01f, +2.839059860e-03f, +2.087771707e-02f, -7.668183010e-03f,
- /* 12, 0 */ -7.009786996e-03f, +1.344312953e-02f, +1.557210222e-02f, -1.125190619e-01f, +2.408695221e-01f, +6.978842163e-01f, +2.408695221e-01f, -1.125190619e-01f, +1.557210222e-02f, +1.344312953e-02f, -7.009786996e-03f, +6.003640016e-04f,
- /* 12, 1 */ -6.398742119e-03f, +1.026913982e-02f, +2.132332546e-02f, -1.110115061e-01f, +2.005160832e-01f, +6.955088069e-01f, +2.821133540e-01f, -1.117099281e-01f, +8.845385329e-03f, +1.669708199e-02f, -7.518519523e-03f, +5.590854556e-04f,
- /* 12, 2 */ -5.716737920e-03f, +7.240001966e-03f, +2.606967700e-02f, -1.074325911e-01f, +1.614746033e-01f, +6.884146462e-01f, +3.237982615e-01f, -1.083606220e-01f, +1.198165974e-03f, +1.995657080e-02f, -7.892366537e-03f, +4.637249154e-04f,
- /* 12, 3 */ -4.993154633e-03f, +4.410399512e-03f, +2.980590100e-02f, -1.020439692e-01f, +1.241329667e-01f, +6.766973789e-01f, +3.654537522e-01f, -1.022748781e-01f, -7.287927063e-03f, +2.313861998e-02f, -8.098411048e-03f, +3.063703263e-04f,
- /* 12, 4 */ -4.254780730e-03f, +1.824453005e-03f, +3.254896791e-02f, -9.511805181e-02f, +8.884019172e-02f, +6.605145641e-01f, +4.065952670e-01f, -9.328874484e-02f, -1.650412301e-02f, +2.615301189e-02f, -8.104377377e-03f, +8.035370630e-05f,
- /* 12, 5 */ -3.525333045e-03f, -4.843426812e-04f, +3.433584064e-02f, -8.693252112e-02f, +5.590207404e-02f, +6.400829406e-01f, +4.467316931e-01f, -8.127529114e-02f, -2.631456917e-02f, +2.890390773e-02f, -7.879705669e-03f, -2.193022854e-04f,
- /* 12, 6 */ -2.825116890e-03f, -2.492908449e-03f, +3.522097401e-02f, -7.776502604e-02f, +2.557772128e-02f, +6.156746770e-01f, +4.853731430e-01f, -6.614880956e-02f, -3.655698883e-02f, +3.129176395e-02f, -7.396691856e-03f, -5.954441701e-04f,
- /* 12, 7 */ -2.170822930e-03f, -4.188126176e-03f, +3.527362061e-02f, -6.788815999e-02f, -1.922977207e-03f, +5.876126808e-01f, +5.220388545e-01f, -4.786841211e-02f, -4.704395826e-02f, +3.321551909e-02f, -6.631665064e-03f, -1.048370326e-03f,
- /* 12, 8 */ -1.575453811e-03f, -5.566170902e-03f, +3.457501660e-02f, -5.756481055e-02f, -2.644092188e-02f, +5.562650609e-01f, +5.562650609e-01f, -2.644092188e-02f, -5.756481055e-02f, +3.457501660e-02f, -5.566170902e-03f, -1.575453811e-03f,
- /* 12, 9 */ -1.048370326e-03f, -6.631665064e-03f, +3.321551909e-02f, -4.704395826e-02f, -4.786841211e-02f, +5.220388545e-01f, +5.876126808e-01f, -1.922977207e-03f, -6.788815999e-02f, +3.527362061e-02f, -4.188126176e-03f, -2.170822930e-03f,
- /* 12,10 */ -5.954441701e-04f, -7.396691856e-03f, +3.129176395e-02f, -3.655698883e-02f, -6.614880956e-02f, +4.853731430e-01f, +6.156746770e-01f, +2.557772128e-02f, -7.776502604e-02f, +3.522097401e-02f, -2.492908449e-03f, -2.825116890e-03f,
- /* 12,11 */ -2.193022854e-04f, -7.879705669e-03f, +2.890390773e-02f, -2.631456917e-02f, -8.127529114e-02f, +4.467316931e-01f, +6.400829406e-01f, +5.590207404e-02f, -8.693252112e-02f, +3.433584064e-02f, -4.843426812e-04f, -3.525333045e-03f,
- /* 12,12 */ +8.035370630e-05f, -8.104377377e-03f, +2.615301189e-02f, -1.650412301e-02f, -9.328874484e-02f, +4.065952670e-01f, +6.605145641e-01f, +8.884019172e-02f, -9.511805181e-02f, +3.254896791e-02f, +1.824453005e-03f, -4.254780730e-03f,
- /* 12,13 */ +3.063703263e-04f, -8.098411048e-03f, +2.313861998e-02f, -7.287927063e-03f, -1.022748781e-01f, +3.654537522e-01f, +6.766973789e-01f, +1.241329667e-01f, -1.020439692e-01f, +2.980590100e-02f, +4.410399512e-03f, -4.993154633e-03f,
- /* 12,14 */ +4.637249154e-04f, -7.892366537e-03f, +1.995657080e-02f, +1.198165974e-03f, -1.083606220e-01f, +3.237982615e-01f, +6.884146462e-01f, +1.614746033e-01f, -1.074325911e-01f, +2.606967700e-02f, +7.240001966e-03f, -5.716737920e-03f,
- /* 12,15 */ +5.590854556e-04f, -7.518519523e-03f, +1.669708199e-02f, +8.845385329e-03f, -1.117099281e-01f, +2.821133540e-01f, +6.955088069e-01f, +2.005160832e-01f, -1.110115061e-01f, +2.132332546e-02f, +1.026913982e-02f, -6.398742119e-03f,
-
- /* 24, 0 */ +1.501390780e-03f, +3.431804419e-03f, +6.512803185e-03f, +1.091425387e-02f, +1.664594540e-02f, +2.351091132e-02f, +3.109255671e-02f, +3.878419288e-02f, +4.586050701e-02f, +5.158058002e-02f, +5.530384985e-02f, +5.659614054e-02f, +5.530384985e-02f, +5.158058002e-02f, +4.586050701e-02f, +3.878419288e-02f, +3.109255671e-02f, +2.351091132e-02f, +1.664594540e-02f, +1.091425387e-02f, +6.512803185e-03f, +3.431804419e-03f, +1.501390780e-03f, +4.573885647e-04f,
- /* 24, 1 */ +1.413186400e-03f, +3.279858311e-03f, +6.282638036e-03f, +1.059932179e-02f, +1.625135142e-02f, +2.305547031e-02f, +3.060840342e-02f, +3.831365198e-02f, +4.545054680e-02f, +5.127577001e-02f, +5.513916011e-02f, +5.659104154e-02f, +5.545895049e-02f, +5.187752167e-02f, +4.626513642e-02f, +3.925233583e-02f, +3.157717954e-02f, +2.396921539e-02f, +1.704503934e-02f, +1.123445076e-02f, +6.748179094e-03f, +3.588275667e-03f, +1.593065611e-03f, +5.022154476e-04f,
- /* 24, 2 */ +1.328380648e-03f, +3.132379333e-03f, +6.057656813e-03f, +1.028967374e-02f, +1.586133102e-02f, +2.260301890e-02f, +3.012488684e-02f, +3.784089895e-02f, +4.503543229e-02f, +5.096323022e-02f, +5.496495842e-02f, +5.657574693e-02f, +5.560438923e-02f, +5.216645963e-02f, +4.666426010e-02f, +3.971789474e-02f, +3.206210284e-02f, +2.443025293e-02f, +1.744855617e-02f, +1.155988996e-02f, +6.988790100e-03f, +3.749328623e-03f, +1.688282347e-03f, +5.494305796e-04f,
- /* 24, 3 */ +1.246901403e-03f, +2.989308098e-03f, +5.837830254e-03f, +9.985325752e-03f, +1.547595434e-02f, +2.215368059e-02f, +2.964217216e-02f, +3.736611920e-02f, +4.461534144e-02f, +5.064310236e-02f, +5.478132634e-02f, +5.655026396e-02f, +5.574009777e-02f, +5.244726189e-02f, +4.705770477e-02f, +4.018068337e-02f, +3.254715574e-02f, +2.489389144e-02f, +1.785641537e-02f, +1.189054572e-02f, +7.234657995e-03f, +3.915018340e-03f, +1.787112015e-03f, +5.991047395e-04f,
- /* 24, 4 */ +1.168676301e-03f, +2.850583915e-03f, +5.623126723e-03f, +9.686290690e-03f, +1.509528803e-02f, +2.170757578e-02f, +2.916042250e-02f, +3.688949768e-02f, +4.419045351e-02f, +5.031553118e-02f, +5.458834968e-02f, +5.651460469e-02f, +5.586601230e-02f, +5.271979985e-02f, +4.744529894e-02f, +4.064051541e-02f, +3.303216567e-02f, +2.535999546e-02f, +1.826853297e-02f, +1.222638897e-02f, +7.485801959e-03f, +4.085398290e-03f, +1.889625146e-03f, +6.513091287e-04f,
- /* 24, 5 */ +1.093632798e-03f, +2.716144855e-03f, +5.413512274e-03f, +9.392578266e-03f, +1.471939531e-02f, +2.126482169e-02f, +2.867979883e-02f, +3.641121873e-02f, +4.376094899e-02f, +4.998066438e-02f, +5.438611851e-02f, +5.646878599e-02f, +5.598207354e-02f, +5.298394839e-02f, +4.782687301e-02f, +4.109720465e-02f, +3.351695842e-02f, +2.582842673e-02f, +1.868482156e-02f, +1.256738733e-02f, +7.742238512e-03f, +4.260520294e-03f, +1.995891717e-03f, +7.061153220e-04f,
- /* 24, 6 */ +1.021698233e-03f, +2.585927824e-03f, +5.208950715e-03f, +9.104195104e-03f, +1.434833590e-02f, +2.082553239e-02f, +2.820045990e-02f, +3.593146595e-02f, +4.332700946e-02f, +4.963865252e-02f, +5.417472708e-02f, +5.641282954e-02f, +5.608822683e-02f, +5.323958602e-02f, +4.820225940e-02f, +4.155056502e-02f, +3.400135826e-02f, +2.629904416e-02f, +1.910519032e-02f, +1.291350505e-02f, +8.003981455e-03f, +4.440434453e-03f, +2.105981077e-03f, +7.635952183e-04f,
- /* 24, 7 */ +9.527998831e-04f, +2.459868628e-03f, +5.009403670e-03f, +8.821144768e-03f, +1.398216608e-02f, +2.038981869e-02f, +2.772256216e-02f, +3.545042216e-02f, +4.288881749e-02f, +4.928964888e-02f, +5.395427373e-02f, +5.634676181e-02f, +5.618442211e-02f, +5.348659488e-02f, +4.857129262e-02f, +4.200041076e-02f, +3.448518802e-02f, +2.677170395e-02f, +1.952954505e-02f, +1.326470299e-02f, +8.271041819e-03f, +4.625189083e-03f, +2.219961884e-03f, +8.238209888e-04f,
- /* 24, 8 */ +8.868650246e-04f, +2.337902042e-03f, +4.814830642e-03f, +8.543427812e-03f, +1.362093865e-02f, +1.995778816e-02f, +2.724625964e-02f, +3.496826923e-02f, +4.244655653e-02f, +4.893380942e-02f, +5.372486088e-02f, +5.627061400e-02f, +5.627061400e-02f, +5.372486088e-02f, +4.893380942e-02f, +4.244655653e-02f, +3.496826923e-02f, +2.724625964e-02f, +1.995778816e-02f, +1.362093865e-02f, +8.543427812e-03f, +4.814830642e-03f, +2.337902042e-03f, +8.868650246e-04f,
- /* 24, 9 */ +8.238209888e-04f, +2.219961884e-03f, +4.625189083e-03f, +8.271041819e-03f, +1.326470299e-02f, +1.952954505e-02f, +2.677170395e-02f, +3.448518802e-02f, +4.200041076e-02f, +4.857129262e-02f, +5.348659488e-02f, +5.618442211e-02f, +5.634676181e-02f, +5.395427373e-02f, +4.928964888e-02f, +4.288881749e-02f, +3.545042216e-02f, +2.772256216e-02f, +2.038981869e-02f, +1.398216608e-02f, +8.821144768e-03f, +5.009403670e-03f, +2.459868628e-03f, +9.527998831e-04f,
- /* 24,10 */ +7.635952183e-04f, +2.105981077e-03f, +4.440434453e-03f, +8.003981455e-03f, +1.291350505e-02f, +1.910519032e-02f, +2.629904416e-02f, +3.400135826e-02f, +4.155056502e-02f, +4.820225940e-02f, +5.323958602e-02f, +5.608822683e-02f, +5.641282954e-02f, +5.417472708e-02f, +4.963865252e-02f, +4.332700946e-02f, +3.593146595e-02f, +2.820045990e-02f, +2.082553239e-02f, +1.434833590e-02f, +9.104195104e-03f, +5.208950715e-03f, +2.585927824e-03f, +1.021698233e-03f,
- /* 24,11 */ +7.061153220e-04f, +1.995891717e-03f, +4.260520294e-03f, +7.742238512e-03f, +1.256738733e-02f, +1.868482156e-02f, +2.582842673e-02f, +3.351695842e-02f, +4.109720465e-02f, +4.782687301e-02f, +5.298394839e-02f, +5.598207354e-02f, +5.646878599e-02f, +5.438611851e-02f, +4.998066438e-02f, +4.376094899e-02f, +3.641121873e-02f, +2.867979883e-02f, +2.126482169e-02f, +1.471939531e-02f, +9.392578266e-03f, +5.413512274e-03f, +2.716144855e-03f, +1.093632798e-03f,
- /* 24,12 */ +6.513091287e-04f, +1.889625146e-03f, +4.085398290e-03f, +7.485801959e-03f, +1.222638897e-02f, +1.826853297e-02f, +2.535999546e-02f, +3.303216567e-02f, +4.064051541e-02f, +4.744529894e-02f, +5.271979985e-02f, +5.586601230e-02f, +5.651460469e-02f, +5.458834968e-02f, +5.031553118e-02f, +4.419045351e-02f, +3.688949768e-02f, +2.916042250e-02f, +2.170757578e-02f, +1.509528803e-02f, +9.686290690e-03f, +5.623126723e-03f, +2.850583915e-03f, +1.168676301e-03f,
- /* 24,13 */ +5.991047395e-04f, +1.787112015e-03f, +3.915018340e-03f, +7.234657995e-03f, +1.189054572e-02f, +1.785641537e-02f, +2.489389144e-02f, +3.254715574e-02f, +4.018068337e-02f, +4.705770477e-02f, +5.244726189e-02f, +5.574009777e-02f, +5.655026396e-02f, +5.478132634e-02f, +5.064310236e-02f, +4.461534144e-02f, +3.736611920e-02f, +2.964217216e-02f, +2.215368059e-02f, +1.547595434e-02f, +9.985325752e-03f, +5.837830254e-03f, +2.989308098e-03f, +1.246901403e-03f,
- /* 24,14 */ +5.494305796e-04f, +1.688282347e-03f, +3.749328623e-03f, +6.988790100e-03f, +1.155988996e-02f, +1.744855617e-02f, +2.443025293e-02f, +3.206210284e-02f, +3.971789474e-02f, +4.666426010e-02f, +5.216645963e-02f, +5.560438923e-02f, +5.657574693e-02f, +5.496495842e-02f, +5.096323022e-02f, +4.503543229e-02f, +3.784089895e-02f, +3.012488684e-02f, +2.260301890e-02f, +1.586133102e-02f, +1.028967374e-02f, +6.057656813e-03f, +3.132379333e-03f, +1.328380648e-03f,
- /* 24,15 */ +5.022154476e-04f, +1.593065611e-03f, +3.588275667e-03f, +6.748179094e-03f, +1.123445076e-02f, +1.704503934e-02f, +2.396921539e-02f, +3.157717954e-02f, +3.925233583e-02f, +4.626513642e-02f, +5.187752167e-02f, +5.545895049e-02f, +5.659104154e-02f, +5.513916011e-02f, +5.127577001e-02f, +4.545054680e-02f, +3.831365198e-02f, +3.060840342e-02f, +2.305547031e-02f, +1.625135142e-02f, +1.059932179e-02f, +6.282638036e-03f, +3.279858311e-03f, +1.413186400e-03f,
- /* 24, 0 */ -2.629184871e-03f, -4.843950453e-03f, -6.895985300e-03f, -7.687208098e-03f, -5.978262553e-03f, -8.032174656e-04f, +8.095316761e-03f, +1.997958831e-02f, +3.311864145e-02f, +4.512644231e-02f, +5.356009950e-02f, +5.659614054e-02f, +5.356009950e-02f, +4.512644231e-02f, +3.311864145e-02f, +1.997958831e-02f, +8.095316761e-03f, -8.032174656e-04f, -5.978262553e-03f, -7.687208098e-03f, -6.895985300e-03f, -4.843950453e-03f, -2.629184871e-03f, -9.454953712e-04f,
- /* 24, 1 */ -2.503767166e-03f, -4.700731697e-03f, -6.791825424e-03f, -7.698565601e-03f, -6.179328945e-03f, -1.237726578e-03f, +7.438688744e-03f, +1.917778123e-02f, +3.230413198e-02f, +4.445707943e-02f, +5.317715832e-02f, +5.658405316e-02f, +5.392156860e-02f, +4.578163621e-02f, +3.392875410e-02f, +2.078652132e-02f, +8.763945305e-03f, -3.538276542e-04f, -5.763420347e-03f, -7.665996832e-03f, -6.995273095e-03f, -4.986674025e-03f, -2.756835384e-03f, -1.028686673e-03f,
- /* 24, 2 */ -2.380688695e-03f, -4.557243028e-03f, -6.683099486e-03f, -7.700368745e-03f, -6.366798820e-03f, -1.657314491e-03f, +6.794365087e-03f, +1.838162773e-02f, +3.148585651e-02f, +4.377411309e-02f, +5.277308334e-02f, +5.654780182e-02f, +5.426124576e-02f, +4.642210542e-02f, +3.473383802e-02f, +2.159804191e-02f, +9.444254477e-03f, +1.103863968e-04f, -5.534634231e-03f, -7.634636496e-03f, -7.089380216e-03f, -5.128670417e-03f, -2.886604737e-03f, -1.114962551e-03f,
- /* 24, 3 */ -2.260048394e-03f, -4.413702845e-03f, -6.570110572e-03f, -7.692920583e-03f, -6.540862270e-03f, -2.061956485e-03f, +6.162633403e-03f, +1.759164425e-02f, +3.066444409e-02f, +4.307811806e-02f, +5.234823086e-02f, +5.648741902e-02f, +5.457882991e-02f, +4.704730472e-02f, +3.553326091e-02f, +2.241360152e-02f, +1.013590849e-02f, +5.893521078e-04f, -5.291747706e-03f, -7.592836347e-03f, -7.177995846e-03f, -5.269701073e-03f, -3.018371382e-03f, -1.204312280e-03f,
- /* 24, 4 */ -2.141937776e-03f, -4.270322542e-03f, -6.453158507e-03f, -7.676527355e-03f, -6.701719772e-03f, -2.451643421e-03f, +5.543764951e-03f, +1.680833562e-02f, +2.984052134e-02f, +4.236967758e-02f, +5.190297478e-02f, +5.640295884e-02f, +5.487403917e-02f, +4.765670002e-02f, +3.632639074e-02f, +2.323264190e-02f, +1.083855586e-02f, +1.082980638e-03f, -5.034616251e-03f, -7.540310660e-03f, -7.260807322e-03f, -5.409521052e-03f, -3.152006158e-03f, -1.296719170e-03f,
- /* 24, 5 */ -2.026441000e-03f, -4.127306381e-03f, -6.332539518e-03f, -7.651498041e-03f, -6.849581767e-03f, -2.826381528e-03f, +4.938014526e-03f, +1.603219452e-02f, +2.901471178e-02f, +4.164938279e-02f, +5.143770614e-02f, +5.629449693e-02f, +5.514661113e-02f, +4.824976895e-02f, +3.711259647e-02f, +2.405459566e-02f, +1.155182965e-02f, +1.591166761e-03f, -4.763107701e-03f, -7.476779193e-03f, -7.337500507e-03f, -5.547879217e-03f, -3.287372274e-03f, -1.392160404e-03f,
- /* 24, 6 */ -1.913634953e-03f, -3.984851387e-03f, -6.208545927e-03f, -7.618143912e-03f, -6.984668233e-03f, -3.186192169e-03f, +4.345620369e-03f, +1.526370115e-02f, +2.818763519e-02f, +4.091783200e-02f, +5.095283267e-02f, +5.616213037e-02f, +5.539630322e-02f, +4.882600150e-02f, +3.789124869e-02f, +2.487888677e-02f, +1.227534767e-02f, +2.113788767e-03f, -4.477102606e-03f, -7.401967644e-03f, -7.407760182e-03f, -5.684518438e-03f, -3.424325302e-03f, -1.490606880e-03f,
- /* 24, 7 */ -1.803589350e-03f, -3.843147252e-03f, -6.081465840e-03f, -7.576778087e-03f, -7.107208249e-03f, -3.531111592e-03f, +3.766804102e-03f, +1.450332275e-02f, +2.735990694e-02f, +4.017563005e-02f, +5.044877831e-02f, +5.600597761e-02f, +5.562289296e-02f, +4.938490059e-02f, +3.866172039e-02f, +2.570493119e-02f, +1.300871280e-02f, +2.650708377e-03f, -4.176494585e-03f, -7.315608112e-03f, -7.471270440e-03f, -5.819175805e-03f, -3.562713186e-03f, -1.592023060e-03f,
- /* 24, 8 */ -1.696366827e-03f, -3.702376254e-03f, -5.951582861e-03f, -7.527715094e-03f, -7.217439556e-03f, -3.861190662e-03f, +3.201770681e-03f, +1.375151322e-02f, +2.653213738e-02f, +3.942338759e-02f, +4.992598268e-02f, +5.582617825e-02f, +5.582617825e-02f, +4.992598268e-02f, +3.942338759e-02f, +2.653213738e-02f, +1.375151322e-02f, +3.201770681e-03f, -3.861190662e-03f, -7.217439556e-03f, -7.527715094e-03f, -5.951582861e-03f, -3.702376254e-03f, -1.696366827e-03f,
- /* 24, 9 */ -1.592023060e-03f, -3.562713186e-03f, -5.819175805e-03f, -7.471270440e-03f, -7.315608112e-03f, -4.176494585e-03f, +2.650708377e-03f, +1.300871280e-02f, +2.570493119e-02f, +3.866172039e-02f, +4.938490059e-02f, +5.562289296e-02f, +5.600597761e-02f, +5.044877831e-02f, +4.017563005e-02f, +2.735990694e-02f, +1.450332275e-02f, +3.766804102e-03f, -3.531111592e-03f, -7.107208249e-03f, -7.576778087e-03f, -6.081465840e-03f, -3.843147252e-03f, -1.803589350e-03f,
- /* 24,10 */ -1.490606880e-03f, -3.424325302e-03f, -5.684518438e-03f, -7.407760182e-03f, -7.401967644e-03f, -4.477102606e-03f, +2.113788767e-03f, +1.227534767e-02f, +2.487888677e-02f, +3.789124869e-02f, +4.882600150e-02f, +5.539630322e-02f, +5.616213037e-02f, +5.095283267e-02f, +4.091783200e-02f, +2.818763519e-02f, +1.526370115e-02f, +4.345620369e-03f, -3.186192169e-03f, -6.984668233e-03f, -7.618143912e-03f, -6.208545927e-03f, -3.984851387e-03f, -1.913634953e-03f,
- /* 24,11 */ -1.392160404e-03f, -3.287372274e-03f, -5.547879217e-03f, -7.337500507e-03f, -7.476779193e-03f, -4.763107701e-03f, +1.591166761e-03f, +1.155182965e-02f, +2.405459566e-02f, +3.711259647e-02f, +4.824976895e-02f, +5.514661113e-02f, +5.629449693e-02f, +5.143770614e-02f, +4.164938279e-02f, +2.901471178e-02f, +1.603219452e-02f, +4.938014526e-03f, -2.826381528e-03f, -6.849581767e-03f, -7.651498041e-03f, -6.332539518e-03f, -4.127306381e-03f, -2.026441000e-03f,
- /* 24,12 */ -1.296719170e-03f, -3.152006158e-03f, -5.409521052e-03f, -7.260807322e-03f, -7.540310660e-03f, -5.034616251e-03f, +1.082980638e-03f, +1.083855586e-02f, +2.323264190e-02f, +3.632639074e-02f, +4.765670002e-02f, +5.487403917e-02f, +5.640295884e-02f, +5.190297478e-02f, +4.236967758e-02f, +2.984052134e-02f, +1.680833562e-02f, +5.543764951e-03f, -2.451643421e-03f, -6.701719772e-03f, -7.676527355e-03f, -6.453158507e-03f, -4.270322542e-03f, -2.141937776e-03f,
- /* 24,13 */ -1.204312280e-03f, -3.018371382e-03f, -5.269701073e-03f, -7.177995846e-03f, -7.592836347e-03f, -5.291747706e-03f, +5.893521078e-04f, +1.013590849e-02f, +2.241360152e-02f, +3.553326091e-02f, +4.704730472e-02f, +5.457882991e-02f, +5.648741902e-02f, +5.234823086e-02f, +4.307811806e-02f, +3.066444409e-02f, +1.759164425e-02f, +6.162633403e-03f, -2.061956485e-03f, -6.540862270e-03f, -7.692920583e-03f, -6.570110572e-03f, -4.413702845e-03f, -2.260048394e-03f,
- /* 24,14 */ -1.114962551e-03f, -2.886604737e-03f, -5.128670417e-03f, -7.089380216e-03f, -7.634636496e-03f, -5.534634231e-03f, +1.103863968e-04f, +9.444254477e-03f, +2.159804191e-02f, +3.473383802e-02f, +4.642210542e-02f, +5.426124576e-02f, +5.654780182e-02f, +5.277308334e-02f, +4.377411309e-02f, +3.148585651e-02f, +1.838162773e-02f, +6.794365087e-03f, -1.657314491e-03f, -6.366798820e-03f, -7.700368745e-03f, -6.683099486e-03f, -4.557243028e-03f, -2.380688695e-03f,
- /* 24,15 */ -1.028686673e-03f, -2.756835384e-03f, -4.986674025e-03f, -6.995273095e-03f, -7.665996832e-03f, -5.763420347e-03f, -3.538276542e-04f, +8.763945305e-03f, +2.078652132e-02f, +3.392875410e-02f, +4.578163621e-02f, +5.392156860e-02f, +5.658405316e-02f, +5.317715832e-02f, +4.445707943e-02f, +3.230413198e-02f, +1.917778123e-02f, +7.438688744e-03f, -1.237726578e-03f, -6.179328945e-03f, -7.698565601e-03f, -6.791825424e-03f, -4.700731697e-03f, -2.503767166e-03f,
- /* 24, 0 */ +4.735641749e-04f, -1.438577362e-03f, -6.107076473e-03f, -1.318715065e-02f, -2.047716119e-02f, -2.428668798e-02f, -2.088952800e-02f, -8.512165320e-03f, +1.117510535e-02f, +3.302575560e-02f, +5.012757987e-02f, +5.659614054e-02f, +5.012757987e-02f, +3.302575560e-02f, +1.117510535e-02f, -8.512165320e-03f, -2.088952800e-02f, -2.428668798e-02f, -2.047716119e-02f, -1.318715065e-02f, -6.107076473e-03f, -1.438577362e-03f, +4.735641749e-04f, +5.516063288e-04f,
- /* 24, 1 */ +5.190146993e-04f, -1.243445781e-03f, -5.732182665e-03f, -1.270621714e-02f, -2.008108462e-02f, -2.422674988e-02f, -2.136190754e-02f, -9.536491055e-03f, +9.813857768e-03f, +3.172645287e-02f, +4.932296812e-02f, +5.657007725e-02f, +5.088942272e-02f, +3.430616434e-02f, +1.254542812e-02f, -7.458075491e-03f, -2.038088472e-02f, -2.431781992e-02f, -2.085968072e-02f, -1.366943909e-02f, -6.492037400e-03f, -1.644903025e-03f, +4.208638005e-04f, +5.761542752e-04f,
- /* 24, 2 */ +5.575380238e-04f, -1.059370463e-03f, -5.367638258e-03f, -1.222740313e-02f, -1.967247249e-02f, -2.413881461e-02f, -2.179812108e-02f, -1.053019587e-02f, +8.463295193e-03f, +3.041001010e-02f, +4.847674006e-02f, +5.649192540e-02f, +5.160740292e-02f, +3.556594146e-02f, +1.392318625e-02f, -6.375136316e-03f, -1.983593655e-02f, -2.431936776e-02f, -2.122761958e-02f, -1.415229209e-02f, -6.886752185e-03f, -1.862540304e-03f, +3.605947820e-04f, +5.982066157e-04f,
- /* 24, 3 */ +5.894596941e-04f, -8.861942853e-04f, -5.013694839e-03f, -1.175144649e-02f, -1.925234223e-02f, -2.402372023e-02f, -2.219832191e-02f, -1.149248169e-02f, +7.124994951e-03f, +2.907819451e-02f, +4.759010507e-02f, +5.636179897e-02f, +5.228048761e-02f, +3.680336864e-02f, +1.530671242e-02f, -5.264319552e-03f, -1.925469982e-02f, -2.429058667e-02f, -2.157995394e-02f, -1.463489443e-02f, -7.290876362e-03f, -2.091585491e-03f, +2.924431607e-04f, +6.174753085e-04f,
- /* 24, 4 */ +6.151072142e-04f, -7.237418200e-04f, -4.670573645e-03f, -1.127905759e-02f, -1.882170532e-02f, -2.388233174e-02f, -2.256271775e-02f, -1.242260980e-02f, +5.800499486e-03f, +2.773278351e-02f, +4.666432713e-02f, +5.617988770e-02f, +5.290770668e-02f, +3.801674993e-02f, +1.669431448e-02f, -4.126652928e-03f, -1.863724919e-02f, -2.423076690e-02f, -2.191566174e-02f, -1.511640714e-02f, -7.704034115e-03f, -2.332112699e-03f, +2.161008111e-04f, +6.336652968e-04f,
- /* 24, 5 */ +6.348091316e-04f, -5.718203517e-04f, -4.338465973e-03f, -1.081091853e-02f, -1.838156554e-02f, -2.371553901e-02f, -2.289156957e-02f, -1.331990148e-02f, +4.491314051e-03f, +2.637556170e-02f, +4.570072248e-02f, +5.594645674e-02f, +5.348815454e-02f, +3.920441468e-02f, +1.808427811e-02f, -2.963218986e-03f, -1.798371833e-02f, -2.413923574e-02f, -2.223372473e-02f, -1.559596853e-02f, -8.125818185e-03f, -2.584172964e-03f, +1.312664533e-04f, +6.464750685e-04f,
- /* 24, 6 */ +6.488941487e-04f, -4.302209069e-04f, -4.017533648e-03f, -1.034768250e-02f, -1.793291714e-02f, -2.352425464e-02f, -2.318519030e-02f, -1.418373842e-02f, +3.198904487e-03f, +2.500831779e-02f, +4.470065727e-02f, +5.566184614e-02f, +5.402099181e-02f, +4.036472049e-02f, +1.947486957e-02f, -1.775153809e-03f, -1.729430055e-02f, -2.401535937e-02f, -2.253313051e-02f, -1.607269547e-02f, -8.555789856e-03f, -2.847793367e-03f, +3.764667972e-05f, +6.555972530e-04f,
- /* 24, 7 */ +6.576902611e-04f, -2.987192943e-04f, -3.707909539e-03f, -9.889973186e-03f, -1.747674315e-02f, -2.330941189e-02f, -2.344394342e-02f, -1.501356300e-02f, +1.924695104e-03f, +2.363284155e-02f, +4.366554511e-02f, +5.532647026e-02f, +5.450544680e-02f, +4.149605616e-02f, +2.086433852e-02f, -5.636456344e-04f, -1.656924930e-02f, -2.385854478e-02f, -2.281287459e-02f, -1.654568462e-02f, -8.993479016e-03f, -3.122976195e-03f, -6.504300803e-05f, +6.607192554e-04f,
- /* 24, 8 */ +6.615239252e-04f, -1.770771536e-04f, -3.409698141e-03f, -9.438384277e-03f, -1.701401374e-02f, -2.307196251e-02f, -2.366824152e-02f, -1.580887853e-02f, +6.700666468e-04f, +2.225092083e-02f, +4.259684448e-02f, +5.494081698e-02f, +5.494081698e-02f, +4.259684448e-02f, +2.225092083e-02f, +6.700666468e-04f, -1.580887853e-02f, -2.366824152e-02f, -2.307196251e-02f, -1.701401374e-02f, -9.438384277e-03f, -3.409698141e-03f, -1.770771536e-04f, +6.615239252e-04f,
- /* 24, 9 */ +6.607192554e-04f, -6.504300803e-05f, -3.122976195e-03f, -8.993479016e-03f, -1.654568462e-02f, -2.281287459e-02f, -2.385854478e-02f, -1.656924930e-02f, -5.636456344e-04f, +2.086433852e-02f, +4.149605616e-02f, +5.450544680e-02f, +5.532647026e-02f, +4.366554511e-02f, +2.363284155e-02f, +1.924695104e-03f, -1.501356300e-02f, -2.344394342e-02f, -2.330941189e-02f, -1.747674315e-02f, -9.889973186e-03f, -3.707909539e-03f, -2.987192943e-04f, +6.576902611e-04f,
- /* 24,10 */ +6.555972530e-04f, +3.764667972e-05f, -2.847793367e-03f, -8.555789856e-03f, -1.607269547e-02f, -2.253313051e-02f, -2.401535937e-02f, -1.729430055e-02f, -1.775153809e-03f, +1.947486957e-02f, +4.036472049e-02f, +5.402099181e-02f, +5.566184614e-02f, +4.470065727e-02f, +2.500831779e-02f, +3.198904487e-03f, -1.418373842e-02f, -2.318519030e-02f, -2.352425464e-02f, -1.793291714e-02f, -1.034768250e-02f, -4.017533648e-03f, -4.302209069e-04f, +6.488941487e-04f,
- /* 24,11 */ +6.464750685e-04f, +1.312664533e-04f, -2.584172964e-03f, -8.125818185e-03f, -1.559596853e-02f, -2.223372473e-02f, -2.413923574e-02f, -1.798371833e-02f, -2.963218986e-03f, +1.808427811e-02f, +3.920441468e-02f, +5.348815454e-02f, +5.594645674e-02f, +4.570072248e-02f, +2.637556170e-02f, +4.491314051e-03f, -1.331990148e-02f, -2.289156957e-02f, -2.371553901e-02f, -1.838156554e-02f, -1.081091853e-02f, -4.338465973e-03f, -5.718203517e-04f, +6.348091316e-04f,
- /* 24,12 */ +6.336652968e-04f, +2.161008111e-04f, -2.332112699e-03f, -7.704034115e-03f, -1.511640714e-02f, -2.191566174e-02f, -2.423076690e-02f, -1.863724919e-02f, -4.126652928e-03f, +1.669431448e-02f, +3.801674993e-02f, +5.290770668e-02f, +5.617988770e-02f, +4.666432713e-02f, +2.773278351e-02f, +5.800499486e-03f, -1.242260980e-02f, -2.256271775e-02f, -2.388233174e-02f, -1.882170532e-02f, -1.127905759e-02f, -4.670573645e-03f, -7.237418200e-04f, +6.151072142e-04f,
- /* 24,13 */ +6.174753085e-04f, +2.924431607e-04f, -2.091585491e-03f, -7.290876362e-03f, -1.463489443e-02f, -2.157995394e-02f, -2.429058667e-02f, -1.925469982e-02f, -5.264319552e-03f, +1.530671242e-02f, +3.680336864e-02f, +5.228048761e-02f, +5.636179897e-02f, +4.759010507e-02f, +2.907819451e-02f, +7.124994951e-03f, -1.149248169e-02f, -2.219832191e-02f, -2.402372023e-02f, -1.925234223e-02f, -1.175144649e-02f, -5.013694839e-03f, -8.861942853e-04f, +5.894596941e-04f,
- /* 24,14 */ +5.982066157e-04f, +3.605947820e-04f, -1.862540304e-03f, -6.886752185e-03f, -1.415229209e-02f, -2.122761958e-02f, -2.431936776e-02f, -1.983593655e-02f, -6.375136316e-03f, +1.392318625e-02f, +3.556594146e-02f, +5.160740292e-02f, +5.649192540e-02f, +4.847674006e-02f, +3.041001010e-02f, +8.463295193e-03f, -1.053019587e-02f, -2.179812108e-02f, -2.413881461e-02f, -1.967247249e-02f, -1.222740313e-02f, -5.367638258e-03f, -1.059370463e-03f, +5.575380238e-04f,
- /* 24,15 */ +5.761542752e-04f, +4.208638005e-04f, -1.644903025e-03f, -6.492037400e-03f, -1.366943909e-02f, -2.085968072e-02f, -2.431781992e-02f, -2.038088472e-02f, -7.458075491e-03f, +1.254542812e-02f, +3.430616434e-02f, +5.088942272e-02f, +5.657007725e-02f, +4.932296812e-02f, +3.172645287e-02f, +9.813857768e-03f, -9.536491055e-03f, -2.136190754e-02f, -2.422674988e-02f, -2.008108462e-02f, -1.270621714e-02f, -5.732182665e-03f, -1.243445781e-03f, +5.190146993e-04f,
- /* 24, 0 */ +2.273459443e-03f, +5.435907648e-03f, +7.255296399e-03f, +3.788129032e-03f, -7.144684562e-03f, -2.265374973e-02f, -3.442368170e-02f, -3.287677614e-02f, -1.387331771e-02f, +1.679264590e-02f, +4.511451955e-02f, +5.659614054e-02f, +4.511451955e-02f, +1.679264590e-02f, -1.387331771e-02f, -3.287677614e-02f, -3.442368170e-02f, -2.265374973e-02f, -7.144684562e-03f, +3.788129032e-03f, +7.255296399e-03f, +5.435907648e-03f, +2.273459443e-03f, +3.568431303e-04f,
- /* 24, 1 */ +2.103234406e-03f, +5.239407106e-03f, +7.256400195e-03f, +4.221207454e-03f, -6.266228991e-03f, -2.168841690e-02f, -3.399213075e-02f, -3.348773370e-02f, -1.551504116e-02f, +1.477681868e-02f, +4.371373211e-02f, +5.654911556e-02f, +4.644656718e-02f, +1.879953521e-02f, -1.218307761e-02f, -3.219410560e-02f, -3.480135038e-02f, -2.360501860e-02f, -8.042999544e-03f, +3.324105568e-03f, +7.232988111e-03f, +5.627714430e-03f, +2.449385040e-03f, +4.247055554e-04f,
- /* 24, 2 */ +1.939021806e-03f, +5.039131826e-03f, +7.237298926e-03f, +4.623451366e-03f, -5.409068119e-03f, -2.071157693e-02f, -3.350883303e-02f, -3.402698064e-02f, -1.710557364e-02f, +1.275612557e-02f, +4.224725679e-02f, +5.640814528e-02f, +4.770696520e-02f, +2.079340350e-02f, -1.044713814e-02f, -3.143988919e-02f, -3.512309015e-02f, -2.453963953e-02f, -8.959642554e-03f, +2.829112073e-03f, +7.188501693e-03f, +5.813881008e-03f, +2.630658922e-03f, +4.992251326e-04f,
- /* 24, 3 */ +1.781093672e-03f, +4.835971292e-03f, +7.199013760e-03f, +4.995053998e-03f, -4.574539506e-03f, -1.972575310e-02f, -3.297600576e-02f, -3.449468646e-02f, -1.864238902e-02f, +1.073461753e-02f, +4.071827965e-02f, +5.617354343e-02f, +4.889295364e-02f, +2.277016679e-02f, -8.668453837e-03f, -3.061446547e-02f, -3.538695026e-02f, -2.545500791e-02f, -9.892988076e-03f, +2.303211764e-03f, +7.120893393e-03f, +5.993435804e-03f, +2.816887995e-03f, +5.805470381e-04f,
- /* 24, 4 */ +1.629682882e-03f, +4.630783503e-03f, +7.142583584e-03f, +5.336288236e-03f, -3.763881685e-03f, -1.873342898e-02f, -3.239594057e-02f, -3.489118499e-02f, -2.012311412e-02f, +8.716314532e-03f, +3.913011251e-02f, +5.584583195e-02f, +5.000192959e-02f, +2.472575033e-02f, -6.850110412e-03f, -2.971834586e-02f, -3.559108276e-02f, -2.634850527e-02f, -1.084131876e-02f, +1.746558492e-03f, +7.029253460e-03f, +6.165384566e-03f, +3.007638074e-03f, +6.687931554e-04f,
- /* 24, 5 */ +1.484983972e-03f, +4.424393216e-03f, +7.069061064e-03f, +5.647503405e-03f, -2.978233194e-03f, -1.773704257e-02f, -3.177099609e-02f, -3.521697115e-02f, -2.154553308e-02f, +6.705195776e-03f, +3.748618427e-02f, +5.542573963e-02f, +5.103145416e-02f, +2.665609886e-02f, -4.995318215e-03f, -2.875221569e-02f, -3.573374914e-02f, -2.721750622e-02f, -1.180282806e-02f, +1.159398961e-03f, +6.912710257e-03f, +6.328712988e-03f, +3.202433762e-03f, +7.640603932e-04f,
- /* 24, 6 */ +1.347154069e-03f, +4.217590351e-03f, +6.979508760e-03f, +5.929121902e-03f, -2.218631929e-03f, -1.673898061e-02f, -3.110359053e-02f, -3.547269735e-02f, -2.290759116e-02f, +4.705190128e-03f, +3.579003198e-02f, +5.491420012e-02f, +5.197925890e-02f, +2.855718684e-02f, -3.107405323e-03f, -2.771693469e-02f, -3.581332680e-02f, -2.805938547e-02f, -1.277562317e-02f, +5.420746921e-04f, +6.770434377e-03f, +6.482389493e-03f, +3.400758480e-03f, +8.664190421e-04f,
- /* 24, 7 */ +1.216313935e-03f, +4.011128586e-03f, +6.874995333e-03f, +6.181635683e-03f, -1.486014823e-03f, -1.574157316e-02f, -3.039619415e-02f, -3.565916944e-02f, -2.420739811e-02f, +2.720166717e-03f, +3.404529163e-02f, +5.431234943e-02f, +5.284325182e-02f, +3.042502866e-02f, -1.189810237e-03f, -2.661353708e-02f, -3.582831530e-02f, -2.887152508e-02f, -1.375772836e-02f, -1.049762569e-04f, +6.601642735e-03f, +6.625368167e-03f, +3.602054665e-03f, +9.759111772e-04f,
- /* 24, 8 */ +1.092549115e-03f, +3.805724130e-03f, +6.756591845e-03f, +6.405602617e-03f, -7.812178358e-04f, -1.474708852e-02f, -2.965132174e-02f, -3.577734234e-02f, -2.544323110e-02f, +7.539257812e-04f, +3.225568873e-02f, +5.362152294e-02f, +5.362152294e-02f, +3.225568873e-02f, +7.539257812e-04f, -2.544323110e-02f, -3.577734234e-02f, -2.965132174e-02f, -1.474708852e-02f, -7.812178358e-04f, +6.405602617e-03f, +6.756591845e-03f, +3.805724130e-03f, +1.092549115e-03f,
- /* 24, 9 */ +9.759111772e-04f, +3.602054665e-03f, +6.625368167e-03f, +6.601642735e-03f, -1.049762569e-04f, -1.375772836e-02f, -2.887152508e-02f, -3.582831530e-02f, -2.661353708e-02f, -1.189810237e-03f, +3.042502866e-02f, +5.284325182e-02f, +5.431234943e-02f, +3.404529163e-02f, +2.720166717e-03f, -2.420739811e-02f, -3.565916944e-02f, -3.039619415e-02f, -1.574157316e-02f, -1.486014823e-03f, +6.181635683e-03f, +6.874995333e-03f, +4.011128586e-03f, +1.216313935e-03f,
- /* 24,10 */ +8.664190421e-04f, +3.400758480e-03f, +6.482389493e-03f, +6.770434377e-03f, +5.420746921e-04f, -1.277562317e-02f, -2.805938547e-02f, -3.581332680e-02f, -2.771693469e-02f, -3.107405323e-03f, +2.855718684e-02f, +5.197925890e-02f, +5.491420012e-02f, +3.579003198e-02f, +4.705190128e-03f, -2.290759116e-02f, -3.547269735e-02f, -3.110359053e-02f, -1.673898061e-02f, -2.218631929e-03f, +5.929121902e-03f, +6.979508760e-03f, +4.217590351e-03f, +1.347154069e-03f,
- /* 24,11 */ +7.640603932e-04f, +3.202433762e-03f, +6.328712988e-03f, +6.912710257e-03f, +1.159398961e-03f, -1.180282806e-02f, -2.721750622e-02f, -3.573374914e-02f, -2.875221569e-02f, -4.995318215e-03f, +2.665609886e-02f, +5.103145416e-02f, +5.542573963e-02f, +3.748618427e-02f, +6.705195776e-03f, -2.154553308e-02f, -3.521697115e-02f, -3.177099609e-02f, -1.773704257e-02f, -2.978233194e-03f, +5.647503405e-03f, +7.069061064e-03f, +4.424393216e-03f, +1.484983972e-03f,
- /* 24,12 */ +6.687931554e-04f, +3.007638074e-03f, +6.165384566e-03f, +7.029253460e-03f, +1.746558492e-03f, -1.084131876e-02f, -2.634850527e-02f, -3.559108276e-02f, -2.971834586e-02f, -6.850110412e-03f, +2.472575033e-02f, +5.000192959e-02f, +5.584583195e-02f, +3.913011251e-02f, +8.716314532e-03f, -2.012311412e-02f, -3.489118499e-02f, -3.239594057e-02f, -1.873342898e-02f, -3.763881685e-03f, +5.336288236e-03f, +7.142583584e-03f, +4.630783503e-03f, +1.629682882e-03f,
- /* 24,13 */ +5.805470381e-04f, +2.816887995e-03f, +5.993435804e-03f, +7.120893393e-03f, +2.303211764e-03f, -9.892988076e-03f, -2.545500791e-02f, -3.538695026e-02f, -3.061446547e-02f, -8.668453837e-03f, +2.277016679e-02f, +4.889295364e-02f, +5.617354343e-02f, +4.071827965e-02f, +1.073461753e-02f, -1.864238902e-02f, -3.449468646e-02f, -3.297600576e-02f, -1.972575310e-02f, -4.574539506e-03f, +4.995053998e-03f, +7.199013760e-03f, +4.835971292e-03f, +1.781093672e-03f,
- /* 24,14 */ +4.992251326e-04f, +2.630658922e-03f, +5.813881008e-03f, +7.188501693e-03f, +2.829112073e-03f, -8.959642554e-03f, -2.453963953e-02f, -3.512309015e-02f, -3.143988919e-02f, -1.044713814e-02f, +2.079340350e-02f, +4.770696520e-02f, +5.640814528e-02f, +4.224725679e-02f, +1.275612557e-02f, -1.710557364e-02f, -3.402698064e-02f, -3.350883303e-02f, -2.071157693e-02f, -5.409068119e-03f, +4.623451366e-03f, +7.237298926e-03f, +5.039131826e-03f, +1.939021806e-03f,
- /* 24,15 */ +4.247055554e-04f, +2.449385040e-03f, +5.627714430e-03f, +7.232988111e-03f, +3.324105568e-03f, -8.042999544e-03f, -2.360501860e-02f, -3.480135038e-02f, -3.219410560e-02f, -1.218307761e-02f, +1.879953521e-02f, +4.644656718e-02f, +5.654911556e-02f, +4.371373211e-02f, +1.477681868e-02f, -1.551504116e-02f, -3.348773370e-02f, -3.399213075e-02f, -2.168841690e-02f, -6.266228991e-03f, +4.221207454e-03f, +7.256400195e-03f, +5.239407106e-03f, +2.103234406e-03f,
- /* 24, 0 */ -2.181310192e-03f, -7.982329251e-04f, +5.680209618e-03f, +1.430719659e-02f, +1.589843483e-02f, +2.406871994e-03f, -2.249676846e-02f, -4.130100690e-02f, -3.506718353e-02f, -1.541681908e-03f, +3.867898214e-02f, +5.659614054e-02f, +3.867898214e-02f, -1.541681908e-03f, -3.506718353e-02f, -4.130100690e-02f, -2.249676846e-02f, +2.406871994e-03f, +1.589843483e-02f, +1.430719659e-02f, +5.680209618e-03f, -7.982329251e-04f, -2.181310192e-03f, -9.324150638e-04f,
- /* 24, 1 */ -2.142117633e-03f, -1.026327303e-03f, +5.144075019e-03f, +1.386145083e-02f, +1.619749380e-02f, +3.702669669e-03f, -2.089125129e-02f, -4.071342524e-02f, -3.635626763e-02f, -4.137848013e-03f, +3.654904222e-02f, +5.652117066e-02f, +4.071616274e-02f, +1.083860427e-03f, -3.366302266e-02f, -4.178478525e-02f, -2.407924887e-02f, +1.061252794e-03f, +1.553625174e-02f, +1.472529111e-02f, +6.227191439e-03f, -5.482915187e-04f, -2.210193915e-03f, -1.021372070e-03f,
- /* 24, 2 */ -2.093579858e-03f, -1.232841058e-03f, +4.620399561e-03f, +1.339085359e-02f, +1.643462496e-02f, +4.945866528e-03f, -1.926829204e-02f, -4.002576024e-02f, -3.752797769e-02f, -6.697199080e-03f, +3.433305089e-02f, +5.629650283e-02f, +4.265414906e-02f, +3.731182183e-03f, -3.214649405e-02f, -4.216132985e-02f, -2.563305646e-02f, -3.311524193e-04f, +1.510995111e-02f, +1.511293981e-02f, +6.783287607e-03f, -2.763303743e-04f, -2.227804667e-03f, -1.112061839e-03f,
- /* 24, 3 */ -2.036654869e-03f, -1.418128950e-03f, +4.110671651e-03f, +1.289819803e-02f, +1.661121711e-02f, +6.133943976e-03f, -1.763342417e-02f, -3.924200344e-02f, -3.858043162e-02f, -9.212479159e-03f, +3.203796457e-02f, +5.592286159e-02f, +4.448680259e-02f, +6.392554173e-03f, -3.052071354e-02f, -4.242751674e-02f, -2.715253413e-02f, -1.767057534e-03f, +1.461875233e-02f, +1.546736546e-02f, +7.346647501e-03f, +1.772445414e-05f, -2.233183138e-03f, -1.203936109e-03f,
- /* 24, 4 */ -1.972290178e-03f, -1.582628528e-03f, +3.616254280e-03f, +1.238625918e-02f, +1.672884047e-02f, +7.264647438e-03f, -1.599210066e-02f, -3.836639935e-02f, -3.951216427e-02f, -1.167663915e-02f, +2.967096294e-02f, +5.540145153e-02f, +4.620830373e-02f, +9.060141131e-03f, -2.878919714e-02f, -4.258054458e-02f, -2.863202454e-02f, -3.242932618e-03f, +1.406209682e-02f, +1.578582064e-02f, +7.915306646e-03f, +3.338433846e-04f, -2.225380377e-03f, -1.296401007e-03f,
- /* 24, 5 */ -1.901418208e-03f, -1.726854356e-03f, +3.138383775e-03f, +1.185778300e-02f, +1.678923519e-02f, +8.335988548e-03f, -1.434967550e-02f, -3.740342600e-02f, -4.032212766e-02f, -1.408285985e-02f, +2.723942290e-02f, +5.473395280e-02f, +4.781317317e-02f, +1.172602857e-02f, -2.695585286e-02f, -4.261794963e-02f, -3.006589126e-02f, -4.755011838e-03f, +1.343965653e-02f, +1.606560041e-02f, +8.487191262e-03f, +6.718888821e-04f, -2.203463508e-03f, -1.388818223e-03f,
- /* 24, 6 */ -1.824951954e-03f, -1.851392085e-03f, +2.678169173e-03f, +1.131547576e-02f, +1.679429951e-02f, +9.346246206e-03f, -1.271138577e-02f, -3.635777499e-02f, -4.100968953e-02f, -1.642457396e-02f, +2.475089197e-02f, +5.392251484e-02f, +4.929629202e-02f, +1.438225015e-02f, -2.502497093e-02f, -4.253761970e-02f, -3.144854025e-02f, -6.299302508e-03f, +1.275134172e-02f, +1.630405519e-02f, +9.060123484e-03f, +1.031611407e-03f, -2.166521604e-03f, -1.480506488e-03f,
- /* 24, 7 */ -1.743780953e-03f, -1.956892396e-03f, +2.236592212e-03f, +1.076199396e-02f, +1.674607744e-02f, +1.029396653e-02f, -1.108233467e-02f, -3.523433096e-02f, -4.157463041e-02f, -1.869548696e-02f, +2.221306113e-02f, +5.296974848e-02f, +5.065292065e-02f, +1.702081530e-02f, -2.300121251e-02f, -4.233780689e-02f, -3.277444146e-02f, -7.871595256e-03f, +1.199730786e-02f, +1.649860375e-02f, +9.631827247e-03f, +1.412645767e-03f, -2.113671692e-03f, -1.570743396e-03f,
- /* 24, 8 */ -1.658767534e-03f, -2.044064852e-03f, +1.814507917e-03f, +1.019993481e-02f, +1.664674627e-02f, +1.117796172e-02f, -9.467475281e-03f, -3.403815061e-02f, -4.201713914e-02f, -2.088959684e-02f, +1.963373727e-02f, +5.187871616e-02f, +5.187871616e-02f, +1.963373727e-02f, -2.088959684e-02f, -4.201713914e-02f, -3.403815061e-02f, -9.467475281e-03f, +1.117796172e-02f, +1.664674627e-02f, +1.019993481e-02f, +1.814507917e-03f, -2.044064852e-03f, -1.658767534e-03f,
- /* 24, 9 */ -1.570743396e-03f, -2.113671692e-03f, +1.412645767e-03f, +9.631827247e-03f, +1.649860375e-02f, +1.199730786e-02f, -7.871595256e-03f, -3.277444146e-02f, -4.233780689e-02f, -2.300121251e-02f, +1.702081530e-02f, +5.065292065e-02f, +5.296974848e-02f, +2.221306113e-02f, -1.869548696e-02f, -4.157463041e-02f, -3.523433096e-02f, -1.108233467e-02f, +1.029396653e-02f, +1.674607744e-02f, +1.076199396e-02f, +2.236592212e-03f, -1.956892396e-03f, -1.743780953e-03f,
- /* 24,10 */ -1.480506488e-03f, -2.166521604e-03f, +1.031611407e-03f, +9.060123484e-03f, +1.630405519e-02f, +1.275134172e-02f, -6.299302508e-03f, -3.144854025e-02f, -4.253761970e-02f, -2.502497093e-02f, +1.438225015e-02f, +4.929629202e-02f, +5.392251484e-02f, +2.475089197e-02f, -1.642457396e-02f, -4.100968953e-02f, -3.635777499e-02f, -1.271138577e-02f, +9.346246206e-03f, +1.679429951e-02f, +1.131547576e-02f, +2.678169173e-03f, -1.851392085e-03f, -1.824951954e-03f,
- /* 24,11 */ -1.388818223e-03f, -2.203463508e-03f, +6.718888821e-04f, +8.487191262e-03f, +1.606560041e-02f, +1.343965653e-02f, -4.755011838e-03f, -3.006589126e-02f, -4.261794963e-02f, -2.695585286e-02f, +1.172602857e-02f, +4.781317317e-02f, +5.473395280e-02f, +2.723942290e-02f, -1.408285985e-02f, -4.032212766e-02f, -3.740342600e-02f, -1.434967550e-02f, +8.335988548e-03f, +1.678923519e-02f, +1.185778300e-02f, +3.138383775e-03f, -1.726854356e-03f, -1.901418208e-03f,
- /* 24,12 */ -1.296401007e-03f, -2.225380377e-03f, +3.338433846e-04f, +7.915306646e-03f, +1.578582064e-02f, +1.406209682e-02f, -3.242932618e-03f, -2.863202454e-02f, -4.258054458e-02f, -2.878919714e-02f, +9.060141131e-03f, +4.620830373e-02f, +5.540145153e-02f, +2.967096294e-02f, -1.167663915e-02f, -3.951216427e-02f, -3.836639935e-02f, -1.599210066e-02f, +7.264647438e-03f, +1.672884047e-02f, +1.238625918e-02f, +3.616254280e-03f, -1.582628528e-03f, -1.972290178e-03f,
- /* 24,13 */ -1.203936109e-03f, -2.233183138e-03f, +1.772445414e-05f, +7.346647501e-03f, +1.546736546e-02f, +1.461875233e-02f, -1.767057534e-03f, -2.715253413e-02f, -4.242751674e-02f, -3.052071354e-02f, +6.392554173e-03f, +4.448680259e-02f, +5.592286159e-02f, +3.203796457e-02f, -9.212479159e-03f, -3.858043162e-02f, -3.924200344e-02f, -1.763342417e-02f, +6.133943976e-03f, +1.661121711e-02f, +1.289819803e-02f, +4.110671651e-03f, -1.418128950e-03f, -2.036654869e-03f,
- /* 24,14 */ -1.112061839e-03f, -2.227804667e-03f, -2.763303743e-04f, +6.783287607e-03f, +1.511293981e-02f, +1.510995111e-02f, -3.311524193e-04f, -2.563305646e-02f, -4.216132985e-02f, -3.214649405e-02f, +3.731182183e-03f, +4.265414906e-02f, +5.629650283e-02f, +3.433305089e-02f, -6.697199080e-03f, -3.752797769e-02f, -4.002576024e-02f, -1.926829204e-02f, +4.945866528e-03f, +1.643462496e-02f, +1.339085359e-02f, +4.620399561e-03f, -1.232841058e-03f, -2.093579858e-03f,
- /* 24,15 */ -1.021372070e-03f, -2.210193915e-03f, -5.482915187e-04f, +6.227191439e-03f, +1.472529111e-02f, +1.553625174e-02f, +1.061252794e-03f, -2.407924887e-02f, -4.178478525e-02f, -3.366302266e-02f, +1.083860427e-03f, +4.071616274e-02f, +5.652117066e-02f, +3.654904222e-02f, -4.137848013e-03f, -3.635626763e-02f, -4.071342524e-02f, -2.089125129e-02f, +3.702669669e-03f, +1.619749380e-02f, +1.386145083e-02f, +5.144075019e-03f, -1.026327303e-03f, -2.142117633e-03f,
- /* 24, 0 */ -6.349328336e-04f, -5.107444449e-03f, -7.589492700e-03f, +4.421169254e-04f, +1.733331948e-02f, +2.497839430e-02f, +6.069612140e-03f, -2.970035006e-02f, -4.651799663e-02f, -1.968310326e-02f, +3.102388246e-02f, +5.659614054e-02f, +3.102388246e-02f, -1.968310326e-02f, -4.651799663e-02f, -2.970035006e-02f, +6.069612140e-03f, +2.497839430e-02f, +1.733331948e-02f, +4.421169254e-04f, -7.589492700e-03f, -5.107444449e-03f, -6.349328336e-04f, +6.381929817e-04f,
- /* 24, 1 */ -4.501246045e-04f, -4.794789988e-03f, -7.673310752e-03f, -4.276921651e-04f, +1.630487239e-02f, +2.519230977e-02f, +8.023727481e-03f, -2.760467194e-02f, -4.668156835e-02f, -2.250226055e-02f, +2.808383767e-02f, +5.648624601e-02f, +3.385706239e-02f, -1.675917373e-02f, -4.616688010e-02f, -3.171828840e-02f, +4.039135426e-03f, +2.465060544e-02f, +1.832599562e-02f, +1.353161175e-03f, -7.461005422e-03f, -5.414037993e-03f, -8.347893499e-04f, +6.459962873e-04f,
- /* 24, 2 */ -2.805431667e-04f, -4.478334337e-03f, -7.714347254e-03f, -1.253762011e-03f, +1.524677189e-02f, +2.529479915e-02f, +9.894771606e-03f, -2.544172743e-02f, -4.665953469e-02f, -2.520578478e-02f, +2.504972253e-02f, +5.615705320e-02f, +3.657100703e-02f, -1.374190145e-02f, -4.562711379e-02f, -3.364818878e-02f, +1.939527429e-03f, +2.420699082e-02f, +1.927675855e-02f, +2.302607998e-03f, -7.286133997e-03f, -5.712221732e-03f, -1.049390115e-03f, +6.454263440e-04f,
- /* 24, 3 */ -1.262469087e-04f, -4.160237857e-03f, -7.714644364e-03f, -2.033919173e-03f, +1.416507919e-02f, +2.528877950e-02f, +1.167657735e-02f, -2.322211124e-02f, -4.645464989e-02f, -2.778343069e-02f, +2.193469332e-02f, +5.561003206e-02f, +3.915383052e-02f, -1.064323425e-02f, -4.489844274e-02f, -3.547998209e-02f, -2.214871506e-04f, +2.364611605e-02f, +2.017947396e-02f, +3.287300483e-03f, -7.063354139e-03f, -5.999568857e-03f, -1.278300802e-03f, +6.356530069e-04f,
- /* 24, 4 */ +1.281987696e-05f, -3.842552418e-03f, -7.676380196e-03f, -2.766321017e-03f, +1.306576874e-02f, +2.517761055e-02f, +1.336353941e-02f, -2.095648565e-02f, -4.607045954e-02f, -3.022561220e-02f, +1.875220414e-02f, +5.484762432e-02f, +4.159418981e-02f, -7.475585158e-03f, -4.398147340e-02f, -3.720388175e-02f, -2.435717775e-03f, +2.296708552e-02f, +2.102804905e-02f, +4.303763633e-03f, -6.791349552e-03f, -6.273586899e-03f, -1.520952773e-03f, +6.158659890e-04f,
- /* 24, 5 */ +1.368204413e-04f, -3.527213369e-03f, -7.601850138e-03f, -3.449453954e-03f, +1.195469912e-02f, +2.496506585e-02f, +1.495063011e-02f, -1.865552736e-02f, -4.551127331e-02f, -3.252344226e-02f, +1.551594186e-02f, +5.387323140e-02f, +4.388134039e-02f, -4.251776447e-03f, -4.287768073e-02f, -3.881043651e-02f, -4.694539858e-03f, +2.216956067e-02f, +2.181646678e-02f, +5.348212687e-03f, -6.469028645e-03f, -6.531730950e-03f, -1.776639841e-03f, +5.852828120e-04f,
- /* 24, 6 */ +2.460185614e-04f, -3.216032617e-03f, -7.493448175e-03f, -4.082129823e-03f, +1.083758546e-02f, +2.465530244e-02f, +1.643341199e-02f, -1.632987505e-02f, -4.478213426e-02f, -3.466876896e-02f, +1.223976005e-02f, +5.269119738e-02f, +4.600518917e-02f, -9.849812576e-04f, -4.158941105e-02f, -4.029058231e-02f, -6.988929457e-03f, +2.125377578e-02f, +2.253882061e-02f, +6.416563547e-03f, -6.095540465e-03f, -6.771417794e-03f, -2.044515881e-03f, +5.431569434e-04f,
- /* 24, 7 */ +3.407711290e-04f, -2.910692818e-03f, -7.353648294e-03f, -4.663480492e-03f, +9.719973395e-03f, +2.425282916e-02f, +1.780804686e-02f, -1.399007798e-02f, -4.388878466e-02f, -3.665420751e-02f, +8.937612534e-03f, +5.130678745e-02f, +4.795634432e-02f, +2.311336934e-03f, -4.011988036e-02f, -4.163569289e-02f, -9.309500719e-03f, +2.022055084e-02f, +2.318934965e-02f, +7.504445299e-03f, -5.670289717e-03f, -6.990040888e-03f, -2.323593338e-03f, +4.887860648e-04f,
- /* 24, 8 */ +4.215204125e-04f, -2.612742676e-03f, -7.184986124e-03f, -5.192950770e-03f, +8.607214812e-03f, +2.376247385e-02f, +1.907130164e-02f, -1.164654593e-02f, -4.283762880e-02f, -3.847316827e-02f, +5.623486691e-03f, +4.972616165e-02f, +4.972616165e-02f, +5.623486691e-03f, -3.847316827e-02f, -4.283762880e-02f, -1.164654593e-02f, +1.907130164e-02f, +2.376247385e-02f, +8.607214812e-03f, -5.192950770e-03f, -7.184986124e-03f, -2.612742676e-03f, +4.215204125e-04f,
- /* 24, 9 */ +4.887860648e-04f, -2.323593338e-03f, -6.990040888e-03f, -5.670289717e-03f, +7.504445299e-03f, +2.318934965e-02f, +2.022055084e-02f, -9.309500719e-03f, -4.163569289e-02f, -4.011988036e-02f, +2.311336934e-03f, +4.795634432e-02f, +5.130678745e-02f, +8.937612534e-03f, -3.665420751e-02f, -4.388878466e-02f, -1.399007798e-02f, +1.780804686e-02f, +2.425282916e-02f, +9.719973395e-03f, -4.663480492e-03f, -7.353648294e-03f, -2.910692818e-03f, +3.407711290e-04f,
- /* 24,10 */ +5.431569434e-04f, -2.044515881e-03f, -6.771417794e-03f, -6.095540465e-03f, +6.416563547e-03f, +2.253882061e-02f, +2.125377578e-02f, -6.988929457e-03f, -4.029058231e-02f, -4.158941105e-02f, -9.849812576e-04f, +4.600518917e-02f, +5.269119738e-02f, +1.223976005e-02f, -3.466876896e-02f, -4.478213426e-02f, -1.632987505e-02f, +1.643341199e-02f, +2.465530244e-02f, +1.083758546e-02f, -4.082129823e-03f, -7.493448175e-03f, -3.216032617e-03f, +2.460185614e-04f,
- /* 24,11 */ +5.852828120e-04f, -1.776639841e-03f, -6.531730950e-03f, -6.469028645e-03f, +5.348212687e-03f, +2.181646678e-02f, +2.216956067e-02f, -4.694539858e-03f, -3.881043651e-02f, -4.287768073e-02f, -4.251776447e-03f, +4.388134039e-02f, +5.387323140e-02f, +1.551594186e-02f, -3.252344226e-02f, -4.551127331e-02f, -1.865552736e-02f, +1.495063011e-02f, +2.496506585e-02f, +1.195469912e-02f, -3.449453954e-03f, -7.601850138e-03f, -3.527213369e-03f, +1.368204413e-04f,
- /* 24,12 */ +6.158659890e-04f, -1.520952773e-03f, -6.273586899e-03f, -6.791349552e-03f, +4.303763633e-03f, +2.102804905e-02f, +2.296708552e-02f, -2.435717775e-03f, -3.720388175e-02f, -4.398147340e-02f, -7.475585158e-03f, +4.159418981e-02f, +5.484762432e-02f, +1.875220414e-02f, -3.022561220e-02f, -4.607045954e-02f, -2.095648565e-02f, +1.336353941e-02f, +2.517761055e-02f, +1.306576874e-02f, -2.766321017e-03f, -7.676380196e-03f, -3.842552418e-03f, +1.281987696e-05f,
- /* 24,13 */ +6.356530069e-04f, -1.278300802e-03f, -5.999568857e-03f, -7.063354139e-03f, +3.287300483e-03f, +2.017947396e-02f, +2.364611605e-02f, -2.214871506e-04f, -3.547998209e-02f, -4.489844274e-02f, -1.064323425e-02f, +3.915383052e-02f, +5.561003206e-02f, +2.193469332e-02f, -2.778343069e-02f, -4.645464989e-02f, -2.322211124e-02f, +1.167657735e-02f, +2.528877950e-02f, +1.416507919e-02f, -2.033919173e-03f, -7.714644364e-03f, -4.160237857e-03f, -1.262469087e-04f,
- /* 24,14 */ +6.454263440e-04f, -1.049390115e-03f, -5.712221732e-03f, -7.286133997e-03f, +2.302607998e-03f, +1.927675855e-02f, +2.420699082e-02f, +1.939527429e-03f, -3.364818878e-02f, -4.562711379e-02f, -1.374190145e-02f, +3.657100703e-02f, +5.615705320e-02f, +2.504972253e-02f, -2.520578478e-02f, -4.665953469e-02f, -2.544172743e-02f, +9.894771606e-03f, +2.529479915e-02f, +1.524677189e-02f, -1.253762011e-03f, -7.714347254e-03f, -4.478334337e-03f, -2.805431667e-04f,
- /* 24,15 */ +6.459962873e-04f, -8.347893499e-04f, -5.414037993e-03f, -7.461005422e-03f, +1.353161175e-03f, +1.832599562e-02f, +2.465060544e-02f, +4.039135426e-03f, -3.171828840e-02f, -4.616688010e-02f, -1.675917373e-02f, +3.385706239e-02f, +5.648624601e-02f, +2.808383767e-02f, -2.250226055e-02f, -4.668156835e-02f, -2.760467194e-02f, +8.023727481e-03f, +2.519230977e-02f, +1.630487239e-02f, -4.276921651e-04f, -7.673310752e-03f, -4.794789988e-03f, -4.501246045e-04f,
- /* 24, 0 */ +1.197013499e-03f, +3.320493122e-03f, -3.017233048e-03f, -9.987553340e-03f, -4.112967049e-03f, +1.524501264e-02f, +2.235677036e-02f, -2.137559158e-03f, -3.327370097e-02f, -2.660122408e-02f, +1.657531807e-02f, +4.232694754e-02f, +1.657531807e-02f, -2.660122408e-02f, -3.327370097e-02f, -2.137559158e-03f, +2.235677036e-02f, +1.524501264e-02f, -4.112967049e-03f, -9.987553340e-03f, -3.017233048e-03f, +2.318357031e-03f, +1.197013499e-03f, -1.261205705e-04f,
- /* 24, 1 */ +1.060573898e-03f, +3.246029352e-03f, -2.510607281e-03f, -9.784551764e-03f, -5.018393221e-03f, +1.404948670e-02f, +2.282515537e-02f, +7.626640355e-05f, -3.208475603e-02f, -2.845760231e-02f, +1.374134711e-02f, +4.221250979e-02f, +1.933345641e-02f, -2.458052660e-02f, -3.429687591e-02f, -4.386190237e-03f, +2.174698353e-02f, +1.639120730e-02f, -3.143642175e-03f, -1.013545813e-02f, -3.535011248e-03f, +2.193303334e-03f, +1.338504197e-03f, -9.901282259e-05f,
- /* 24, 2 */ +9.298712414e-04f, +3.156277727e-03f, -2.018194158e-03f, -9.530340989e-03f, -5.856606243e-03f, +1.281349999e-02f, +2.315294314e-02f, +2.243024978e-03f, -3.073934924e-02f, -3.014061672e-02f, +1.084811230e-02f, +4.186988491e-02f, +2.199957595e-02f, -2.240563854e-02f, -3.514586546e-02f, -6.656913804e-03f, +2.099588115e-02f, +1.747926153e-02f, -2.114297018e-03f, -1.022461460e-02f, -4.060636553e-03f, +2.039754046e-03f, +1.484263468e-03f, -6.526428163e-05f,
- /* 24, 3 */ +8.054954021e-04f, +3.052984548e-03f, -1.542795645e-03f, -9.229007144e-03f, -6.624860539e-03f, +1.154592266e-02f, +2.334180387e-02f, +4.350977952e-03f, -2.924761047e-02f, -3.164235460e-02f, +7.912459038e-03f, +4.130113344e-02f, +2.455797710e-02f, -2.008771737e-02f, -3.581321303e-02f, -8.936640836e-03f, +2.010445982e-02f, +1.850049032e-02f, -1.029364704e-03f, -1.025164428e-02f, -4.590574200e-03f, +1.857070273e-03f, +1.633412152e-03f, -2.453170435e-05f,
- /* 24, 4 */ +6.879416803e-04f, +2.937877889e-03f, -1.086944946e-03f, -8.884797195e-03f, -7.320980005e-03f, +1.025556440e-02f, +2.339424278e-02f, +6.388976156e-03f, -2.762041561e-02f, -3.295607494e-02f, +4.951401864e-03f, +4.050967459e-02f, +2.699354793e-02f, -1.763888677e-02f, -3.629248103e-02f, -1.121198570e-02f, +1.907464002e-02f, +1.944639638e-02f, +1.061806254e-04f, -1.021347789e-02f, -5.121078221e-03f, +1.644827972e-03f, +1.784975276e-03f, +2.348660763e-05f,
- /* 24, 5 */ +5.776128643e-04f, +2.812656385e-03f, -6.528985547e-04f, -8.502081335e-03f, -7.943354450e-03f, +8.951116293e-03f, +2.331356438e-02f, +8.346521334e-03f, -2.586930694e-02f, -3.407624020e-02f, +1.982016505e-03f, +3.950026382e-02f, +2.929186185e-02f, -1.507216716e-02f, -3.657830513e-02f, -1.346934883e-02f, +1.790927350e-02f, +2.030873394e-02f, +1.286841938e-03f, -1.010739044e-02f, -5.648212144e-03f, +1.402832649e-03f, +1.937883690e-03f, +7.904503123e-05f,
- /* 24, 6 */ +4.748218959e-04f, +2.678978820e-03f, -2.426308611e-04f, -8.085315763e-03f, -8.490932000e-03f, +7.641095022e-03f, +2.310383199e-02f, +1.021382218e-02f, -2.400641001e-02f, -3.499853994e-02f, -9.786680537e-04f, +3.827896159e-02f, +3.143927100e-02f, -1.240139974e-02f, -3.666644182e-02f, -1.569500220e-02f, +1.661214427e-02f, +2.107957227e-02f, +2.506621864e-03f, -9.931034771e-03f, -6.167872094e-03f, +1.131132707e-03f, +2.090976552e-03f, +1.423449116e-04f,
- /* 24, 7 */ +3.797950945e-04f, +2.538454547e-03f, +1.421687298e-04f, -7.639006136e-03f, -8.963207645e-03f, +6.333789781e-03f, +2.276982298e-02f, +1.198184460e-02f, -2.204434780e-02f, -3.571990598e-02f, -3.913776625e-03f, +3.685309369e-02f, +3.342299483e-02f, -9.641164594e-03f, -3.655380902e-02f, -1.787517696e-02f, +1.518796320e-02f, +2.175135864e-02f, +3.759049618e-03f, -9.682473374e-03f, -6.675812165e-03f, +8.300312585e-04f, +2.243004675e-03f, +2.135289700e-04f,
- /* 24, 8 */ +2.926758839e-04f, +2.392634763e-03f, +5.000962484e-04f, -7.167671961e-03f, -9.360208124e-03f, +5.037212205e-03f, +2.231697999e-02f, +1.364235598e-02f, -1.999615271e-02f, -3.623851940e-02f, -6.806693291e-03f, +3.523120326e-02f, +3.523120326e-02f, -6.806693291e-03f, -3.623851940e-02f, -1.999615271e-02f, +1.364235598e-02f, +2.231697999e-02f, +5.037212205e-03f, -9.360208124e-03f, -7.167671961e-03f, +5.000962484e-04f, +2.392634763e-03f, +2.926758839e-04f,
- /* 24, 9 */ +2.135289700e-04f, +2.243004675e-03f, +8.300312585e-04f, -6.675812165e-03f, -9.682473374e-03f, +3.759049618e-03f, +2.175135864e-02f, +1.518796320e-02f, -1.787517696e-02f, -3.655380902e-02f, -9.641164594e-03f, +3.342299483e-02f, +3.685309369e-02f, -3.913776625e-03f, -3.571990598e-02f, -2.204434780e-02f, +1.198184460e-02f, +2.276982298e-02f, +6.333789781e-03f, -8.963207645e-03f, -7.639006136e-03f, +1.421687298e-04f, +2.538454547e-03f, +3.797950945e-04f,
- /* 24,10 */ +1.423449116e-04f, +2.090976552e-03f, +1.131132707e-03f, -6.167872094e-03f, -9.931034771e-03f, +2.506621864e-03f, +2.107957227e-02f, +1.661214427e-02f, -1.569500220e-02f, -3.666644182e-02f, -1.240139974e-02f, +3.143927100e-02f, +3.827896159e-02f, -9.786680537e-04f, -3.499853994e-02f, -2.400641001e-02f, +1.021382218e-02f, +2.310383199e-02f, +7.641095022e-03f, -8.490932000e-03f, -8.085315763e-03f, -2.426308611e-04f, +2.678978820e-03f, +4.748218959e-04f,
- /* 24,11 */ +7.904503123e-05f, +1.937883690e-03f, +1.402832649e-03f, -5.648212144e-03f, -1.010739044e-02f, +1.286841938e-03f, +2.030873394e-02f, +1.790927350e-02f, -1.346934883e-02f, -3.657830513e-02f, -1.507216716e-02f, +2.929186185e-02f, +3.950026382e-02f, +1.982016505e-03f, -3.407624020e-02f, -2.586930694e-02f, +8.346521334e-03f, +2.331356438e-02f, +8.951116293e-03f, -7.943354450e-03f, -8.502081335e-03f, -6.528985547e-04f, +2.812656385e-03f, +5.776128643e-04f,
- /* 24,12 */ +2.348660763e-05f, +1.784975276e-03f, +1.644827972e-03f, -5.121078221e-03f, -1.021347789e-02f, +1.061806254e-04f, +1.944639638e-02f, +1.907464002e-02f, -1.121198570e-02f, -3.629248103e-02f, -1.763888677e-02f, +2.699354793e-02f, +4.050967459e-02f, +4.951401864e-03f, -3.295607494e-02f, -2.762041561e-02f, +6.388976156e-03f, +2.339424278e-02f, +1.025556440e-02f, -7.320980005e-03f, -8.884797195e-03f, -1.086944946e-03f, +2.937877889e-03f, +6.879416803e-04f,
- /* 24,13 */ -2.453170435e-05f, +1.633412152e-03f, +1.857070273e-03f, -4.590574200e-03f, -1.025164428e-02f, -1.029364704e-03f, +1.850049032e-02f, +2.010445982e-02f, -8.936640836e-03f, -3.581321303e-02f, -2.008771737e-02f, +2.455797710e-02f, +4.130113344e-02f, +7.912459038e-03f, -3.164235460e-02f, -2.924761047e-02f, +4.350977952e-03f, +2.334180387e-02f, +1.154592266e-02f, -6.624860539e-03f, -9.229007144e-03f, -1.542795645e-03f, +3.052984548e-03f, +8.054954021e-04f,
- /* 24,14 */ -6.526428163e-05f, +1.484263468e-03f, +2.039754046e-03f, -4.060636553e-03f, -1.022461460e-02f, -2.114297018e-03f, +1.747926153e-02f, +2.099588115e-02f, -6.656913804e-03f, -3.514586546e-02f, -2.240563854e-02f, +2.199957595e-02f, +4.186988491e-02f, +1.084811230e-02f, -3.014061672e-02f, -3.073934924e-02f, +2.243024978e-03f, +2.315294314e-02f, +1.281349999e-02f, -5.856606243e-03f, -9.530340989e-03f, -2.018194158e-03f, +3.156277727e-03f, +9.298712414e-04f,
- /* 24,15 */ -9.901282259e-05f, +1.338504197e-03f, +2.193303334e-03f, -3.535011248e-03f, -1.013545813e-02f, -3.143642175e-03f, +1.639120730e-02f, +2.174698353e-02f, -4.386190237e-03f, -3.429687591e-02f, -2.458052660e-02f, +1.933345641e-02f, +4.221250979e-02f, +1.374134711e-02f, -2.845760231e-02f, -3.208475603e-02f, +7.626640355e-05f, +2.282515537e-02f, +1.404948670e-02f, -5.018393221e-03f, -9.784551764e-03f, -2.510607281e-03f, +3.246029352e-03f, +1.060573898e-03f,
- /* 20, 0 */ +2.832126065e-03f, -3.455346407e-03f, -1.050167676e-02f, +5.399047405e-04f, +2.072547687e-02f, +1.261483344e-02f, -2.310421022e-02f, -3.076111900e-02f, +1.034529168e-02f, +3.949755319e-02f, +1.034529168e-02f, -3.076111900e-02f, -2.310421022e-02f, +1.261483344e-02f, +2.072547687e-02f, +5.399047405e-04f, -1.050167676e-02f, -3.455346407e-03f, +2.832126065e-03f, +1.002136091e-03f,
- /* 20, 1 */ +2.918309836e-03f, -2.848965042e-03f, -1.044663371e-02f, -7.454467031e-04f, +1.993701966e-02f, +1.431336010e-02f, -2.105256806e-02f, -3.196996361e-02f, +7.274030562e-03f, +3.936378623e-02f, +1.336427867e-02f, -2.932648708e-02f, -2.503260290e-02f, +1.078376120e-02f, +2.138841986e-02f, +1.874755806e-03f, -1.047502359e-02f, -4.071193337e-03f, +2.709872705e-03f, +1.184613130e-03f,
- /* 20, 2 */ +2.970014434e-03f, -2.256846905e-03f, -1.031411254e-02f, -1.973175306e-03f, +1.903277841e-02f, +1.586999913e-02f, -1.889465735e-02f, -3.294695719e-02f, +4.172714360e-03f, +3.896348732e-02f, +1.630904655e-02f, -2.767391419e-02f, -2.682143551e-02f, +8.830742245e-03f, +2.191685631e-02f, +3.250271284e-03f, -1.036300090e-02f, -4.691364292e-03f, +2.550244709e-03f, +1.728121332e-03f,
- /* 20, 3 */ +2.989084466e-03f, -1.683415968e-03f, -1.010881741e-02f, -3.135916081e-03f, +1.802312126e-02f, +1.727671449e-02f, -1.664798407e-02f, -3.368784795e-02f, +1.063660935e-03f, +3.829965427e-02f, +1.915809828e-02f, -2.581298498e-02f, -2.845520951e-02f, +6.767571398e-03f, +2.230262774e-02f, +4.656958119e-03f, -1.016253578e-02f, -5.310408414e-03f, +2.352251997e-03f, +1.906494808e-03f,
- /* 20, 4 */ +2.977586348e-03f, -1.132697564e-03f, -9.835877420e-03f, -4.227100403e-03f, +1.691895133e-02f, +1.852681335e-02f, -1.433043179e-02f, -3.419021020e-02f, -2.030888217e-03f, +3.737725626e-02f, +2.189055571e-02f, -2.375496340e-02f, -2.991937541e-02f, +4.607167163e-03f, +2.253850054e-02f, +6.084727180e-03f, -9.871207756e-03f, -5.922604667e-03f, +2.115247068e-03f, +2.079939639e-03f,
- /* 20, 5 */ +2.937775120e-03f, -6.082983663e-04f, -9.500783787e-03f, -5.240985904e-03f, +1.573160178e-02f, +1.961497125e-02f, -1.196011335e-02f, -3.445344345e-02f, -5.088941581e-03f, +3.620319350e-02f, +2.448632622e-02f, -2.151271924e-02f, -3.120046374e-02f, +2.363488482e-03f, +2.261825107e-02f, +7.522962281e-03f, -9.487296369e-03f, -6.522005316e-03f, +1.838950241e-03f, +2.245949018e-03f,
- /* 20, 6 */ +2.872060854e-03f, -1.133913219e-04f, -9.109325922e-03f, -6.172678101e-03f, +1.447272980e-02f, +2.053724533e-02f, -9.555222824e-03f, -3.447875653e-02f, -8.088928223e-03f, +3.478624106e-02f, +2.692626358e-02f, -1.910064083e-02f, -3.228620876e-02f, +5.144107936e-05f, +2.253674404e-02f, +8.960596019e-03f, -9.009823935e-03f, -7.102483479e-03f, +1.523472156e-03f, +2.401928729e-03f,
- /* 20, 7 */ +2.782975009e-03f, +3.492945151e-04f, -8.667527067e-03f, -7.018143782e-03f, +1.315421060e-02f, +2.129107545e-02f, -7.133889173e-03f, -3.426913715e-02f, -1.100986395e-02f, +3.313697764e-02f, +2.919232167e-02f, -1.653453452e-02f, -3.316566379e-02f, -2.313225982e-03f, +2.229000326e-02f, +1.038619190e-02f, -8.438592747e-03f, -7.657784411e-03f, +1.169333173e-03f, +2.545222121e-03f,
- /* 20, 8 */ +2.673137115e-03f, +7.774793244e-04f, -8.181580147e-03f, -7.774216267e-03f, +1.178803215e-02f, +2.187527386e-02f, -4.714032773e-03f, -3.382930709e-02f, -1.383151166e-02f, +3.126769968e-02f, +3.126769968e-02f, -1.383151166e-02f, -3.382930709e-02f, -4.714032773e-03f, +2.187527386e-02f, +1.178803215e-02f, -7.774216267e-03f, -8.181580147e-03f, +7.774793244e-04f, +2.673137115e-03f,
- /* 20, 9 */ +2.545222121e-03f, +1.169333173e-03f, -7.657784411e-03f, -8.438592747e-03f, +1.038619190e-02f, +2.229000326e-02f, -2.313225982e-03f, -3.316566379e-02f, -1.653453452e-02f, +2.919232167e-02f, +3.313697764e-02f, -1.100986395e-02f, -3.426913715e-02f, -7.133889173e-03f, +2.129107545e-02f, +1.315421060e-02f, -7.018143782e-03f, -8.667527067e-03f, +3.492945151e-04f, +2.782975009e-03f,
- /* 20,10 */ +2.401928729e-03f, +1.523472156e-03f, -7.102483479e-03f, -9.009823935e-03f, +8.960596019e-03f, +2.253674404e-02f, +5.144107936e-05f, -3.228620876e-02f, -1.910064083e-02f, +2.692626358e-02f, +3.478624106e-02f, -8.088928223e-03f, -3.447875653e-02f, -9.555222824e-03f, +2.053724533e-02f, +1.447272980e-02f, -6.172678101e-03f, -9.109325922e-03f, -1.133913219e-04f, +2.872060854e-03f,
- /* 20,11 */ +2.245949018e-03f, +1.838950241e-03f, -6.522005316e-03f, -9.487296369e-03f, +7.522962281e-03f, +2.261825107e-02f, +2.363488482e-03f, -3.120046374e-02f, -2.151271924e-02f, +2.448632622e-02f, +3.620319350e-02f, -5.088941581e-03f, -3.445344345e-02f, -1.196011335e-02f, +1.961497125e-02f, +1.573160178e-02f, -5.240985904e-03f, -9.500783787e-03f, -6.082983663e-04f, +2.937775120e-03f,
- /* 20,12 */ +2.079939639e-03f, +2.115247068e-03f, -5.922604667e-03f, -9.871207756e-03f, +6.084727180e-03f, +2.253850054e-02f, +4.607167163e-03f, -2.991937541e-02f, -2.375496340e-02f, +2.189055571e-02f, +3.737725626e-02f, -2.030888217e-03f, -3.419021020e-02f, -1.433043179e-02f, +1.852681335e-02f, +1.691895133e-02f, -4.227100403e-03f, -9.835877420e-03f, -1.132697564e-03f, +2.977586348e-03f,
- /* 20,13 */ +1.906494808e-03f, +2.352251997e-03f, -5.310408414e-03f, -1.016253578e-02f, +4.656958119e-03f, +2.230262774e-02f, +6.767571398e-03f, -2.845520951e-02f, -2.581298498e-02f, +1.915809828e-02f, +3.829965427e-02f, +1.063660935e-03f, -3.368784795e-02f, -1.664798407e-02f, +1.727671449e-02f, +1.802312126e-02f, -3.135916081e-03f, -1.010881741e-02f, -1.683415968e-03f, +2.989084466e-03f,
- /* 20,14 */ +1.728121332e-03f, +2.550244709e-03f, -4.691364292e-03f, -1.036300090e-02f, +3.250271284e-03f, +2.191685631e-02f, +8.830742245e-03f, -2.682143551e-02f, -2.767391419e-02f, +1.630904655e-02f, +3.896348732e-02f, +4.172714360e-03f, -3.294695719e-02f, -1.889465735e-02f, +1.586999913e-02f, +1.903277841e-02f, -1.973175306e-03f, -1.031411254e-02f, -2.256846905e-03f, +2.970014434e-03f,
- /* 20,15 */ +1.184613130e-03f, +2.709872705e-03f, -4.071193337e-03f, -1.047502359e-02f, +1.874755806e-03f, +2.138841986e-02f, +1.078376120e-02f, -2.503260290e-02f, -2.932648708e-02f, +1.336427867e-02f, +3.936378623e-02f, +7.274030562e-03f, -3.196996361e-02f, -2.105256806e-02f, +1.431336010e-02f, +1.993701966e-02f, -7.454467031e-04f, -1.044663371e-02f, -2.848965042e-03f, +2.918309836e-03f,
- /* 20, 0 */ +1.702864838e-03f, +2.314577966e-03f, -6.534433696e-03f, -8.774562126e-03f, +1.199271213e-02f, +2.083400312e-02f, -1.263546899e-02f, -3.379691899e-02f, +5.498355442e-03f, +3.949755319e-02f, +5.498355442e-03f, -3.379691899e-02f, -1.263546899e-02f, +2.083400312e-02f, +1.199271213e-02f, -8.774562126e-03f, -6.534433696e-03f, +2.314577966e-03f, +1.702864838e-03f, +0.000000000e+00f,
- /* 20, 1 */ +1.499435496e-03f, +2.547675666e-03f, -5.868098774e-03f, -9.353385898e-03f, +1.044920601e-02f, +2.160032962e-02f, -9.997584470e-03f, -3.429852636e-02f, +2.083565903e-03f, +3.933622696e-02f, +8.892197588e-03f, -3.300722623e-02f, -1.522519578e-02f, +1.986341365e-02f, +1.349096787e-02f, -8.082206357e-03f, -7.177938171e-03f, +2.033576095e-03f, +1.903844954e-03f, +0.000000000e+00f,
- /* 20, 2 */ +8.979094201e-04f, +2.733567376e-03f, -5.187117330e-03f, -9.818374279e-03f, +8.876306372e-03f, +2.216139438e-02f, -7.335624654e-03f, -3.451169090e-02f, -1.322648265e-03f, +3.885370480e-02f, +1.223556951e-02f, -3.193245877e-02f, -1.774265256e-02f, +1.869155385e-02f, +1.492800767e-02f, -7.277832099e-03f, -7.790241855e-03f, +1.704529263e-03f, +2.099160368e-03f, -3.513221827e-04f,
- /* 20, 3 */ +7.046452899e-04f, +2.873469547e-03f, -4.499404245e-03f, -1.017038969e-02f, +7.289604703e-03f, +2.251815219e-02f, -4.673411391e-03f, -3.443867914e-02f, -4.691042688e-03f, +3.805434209e-02f, +1.549922824e-02f, -3.057828381e-02f, -2.016393226e-02f, +1.732343066e-02f, +1.628791973e-02f, -6.364195274e-03f, -8.362876507e-03f, +1.327887989e-03f, +2.285430476e-03f, -3.288882594e-04f,
- /* 20, 4 */ +5.275063595e-04f, +2.969073324e-03f, -3.812529364e-03f, -1.041140495e-02f, +5.704278009e-03f, +2.267345221e-02f, -2.034281900e-03f, -3.408432658e-02f, -7.992925296e-03f, +3.694535030e-02f, +1.865448932e-02f, -2.895300188e-02f, -2.246557005e-02f, +1.576606531e-02f, +1.755501285e-02f, -5.345313722e-03f, -8.887369558e-03f, +9.047221591e-04f, +2.459146683e-03f, -2.941732022e-04f,
- /* 20, 5 */ +3.670219514e-04f, +3.022497230e-03f, -3.133648777e-03f, -1.054443774e-02f, +4.134948499e-03f, +2.263196075e-02f, +5.591264205e-04f, -3.345595810e-02f, -1.120042307e-02f, +3.553672638e-02f, +2.167350137e-02f, -2.706749712e-02f, -2.462477986e-02f, +1.402847243e-02f, +1.871398575e-02f, -4.226473266e-03f, -9.355341791e-03f, +4.367425848e-04f, +2.616713456e-03f, -2.461206998e-04f,
- /* 20, 6 */ +2.234691091e-04f, +3.036236900e-03f, -2.469443903e-03f, -1.057347601e-02f, +2.595553432e-03f, +2.240006745e-02f, +3.085080219e-03f, -3.256328476e-02f, -1.428673893e-02f, +3.384115481e-02f, +2.452951370e-02f, -2.493516141e-02f, -2.661968788e-02f, +1.212161795e-02f, +1.975009719e-02f, -3.014219819e-03f, -9.758608118e-03f, -7.368451392e-05f, +2.754492916e-03f, -1.837671888e-04f,
- /* 20, 7 */ +9.688872179e-05f, +3.013112613e-03f, -1.826068782e-03f, -1.050339555e-02f, +1.099226216e-03f, +2.198577609e-02f, +5.522941542e-03f, -3.141827834e-02f, -1.722639627e-02f, +3.187388359e-02f, +2.719713425e-02f, -2.257179274e-02f, -2.842956063e-02f, +1.005835593e-02f, +2.064933490e-02f, -1.716337171e-03f, -1.008928035e-02f, -6.235305950e-04f, +2.868852533e-03f, -1.062635081e-04f,
- /* 20, 8 */ -1.289664191e-05f, +2.956215411e-03f, -1.209105881e-03f, -1.033987080e-02f, -3.418102460e-04f, +2.139858161e-02f, +7.853344535e-03f, -3.003502508e-02f, -1.999546871e-02f, +2.965257519e-02f, +2.965257519e-02f, -1.999546871e-02f, -3.003502508e-02f, +7.853344535e-03f, +2.139858161e-02f, -3.418102460e-04f, -1.033987080e-02f, -1.209105881e-03f, +2.956215411e-03f, -1.289664191e-05f,
- /* 20, 9 */ -1.062635081e-04f, +2.868852533e-03f, -6.235305950e-04f, -1.008928035e-02f, -1.716337171e-03f, +2.064933490e-02f, +1.005835593e-02f, -2.842956063e-02f, -2.257179274e-02f, +2.719713425e-02f, +3.187388359e-02f, -1.722639627e-02f, -3.141827834e-02f, +5.522941542e-03f, +2.198577609e-02f, +1.099226216e-03f, -1.050339555e-02f, -1.826068782e-03f, +3.013112613e-03f, +9.688872179e-05f,
- /* 20,10 */ -1.837671888e-04f, +2.754492916e-03f, -7.368451392e-05f, -9.758608118e-03f, -3.014219819e-03f, +1.975009719e-02f, +1.212161795e-02f, -2.661968788e-02f, -2.493516141e-02f, +2.452951370e-02f, +3.384115481e-02f, -1.428673893e-02f, -3.256328476e-02f, +3.085080219e-03f, +2.240006745e-02f, +2.595553432e-03f, -1.057347601e-02f, -2.469443903e-03f, +3.036236900e-03f, +2.234691091e-04f,
- /* 20,11 */ -2.461206998e-04f, +2.616713456e-03f, +4.367425848e-04f, -9.355341791e-03f, -4.226473266e-03f, +1.871398575e-02f, +1.402847243e-02f, -2.462477986e-02f, -2.706749712e-02f, +2.167350137e-02f, +3.553672638e-02f, -1.120042307e-02f, -3.345595810e-02f, +5.591264205e-04f, +2.263196075e-02f, +4.134948499e-03f, -1.054443774e-02f, -3.133648777e-03f, +3.022497230e-03f, +3.670219514e-04f,
- /* 20,12 */ -2.941732022e-04f, +2.459146683e-03f, +9.047221591e-04f, -8.887369558e-03f, -5.345313722e-03f, +1.755501285e-02f, +1.576606531e-02f, -2.246557005e-02f, -2.895300188e-02f, +1.865448932e-02f, +3.694535030e-02f, -7.992925296e-03f, -3.408432658e-02f, -2.034281900e-03f, +2.267345221e-02f, +5.704278009e-03f, -1.041140495e-02f, -3.812529364e-03f, +2.969073324e-03f, +5.275063595e-04f,
- /* 20,13 */ -3.288882594e-04f, +2.285430476e-03f, +1.327887989e-03f, -8.362876507e-03f, -6.364195274e-03f, +1.628791973e-02f, +1.732343066e-02f, -2.016393226e-02f, -3.057828381e-02f, +1.549922824e-02f, +3.805434209e-02f, -4.691042688e-03f, -3.443867914e-02f, -4.673411391e-03f, +2.251815219e-02f, +7.289604703e-03f, -1.017038969e-02f, -4.499404245e-03f, +2.873469547e-03f, +7.046452899e-04f,
- /* 20,14 */ -3.513221827e-04f, +2.099160368e-03f, +1.704529263e-03f, -7.790241855e-03f, -7.277832099e-03f, +1.492800767e-02f, +1.869155385e-02f, -1.774265256e-02f, -3.193245877e-02f, +1.223556951e-02f, +3.885370480e-02f, -1.322648265e-03f, -3.451169090e-02f, -7.335624654e-03f, +2.216139438e-02f, +8.876306372e-03f, -9.818374279e-03f, -5.187117330e-03f, +2.733567376e-03f, +8.979094201e-04f,
- /* 20,15 */ +0.000000000e+00f, +1.903844954e-03f, +2.033576095e-03f, -7.177938171e-03f, -8.082206357e-03f, +1.349096787e-02f, +1.986341365e-02f, -1.522519578e-02f, -3.300722623e-02f, +8.892197588e-03f, +3.933622696e-02f, +2.083565903e-03f, -3.429852636e-02f, -9.997584470e-03f, +2.160032962e-02f, +1.044920601e-02f, -9.353385898e-03f, -5.868098774e-03f, +2.547675666e-03f, +1.499435496e-03f,
- /* 20, 0 */ -3.735125865e-04f, +2.550984103e-03f, -2.725752467e-05f, -1.024966855e-02f, +8.379667777e-04f, +2.252874535e-02f, -1.249359695e-03f, -3.448318510e-02f, +6.071698875e-04f, +3.949755319e-02f, +6.071698875e-04f, -3.448318510e-02f, -1.249359695e-03f, +2.252874535e-02f, +8.379667777e-04f, -1.024966855e-02f, -2.725752467e-05f, +2.565652055e-03f, -3.735125865e-04f, +0.000000000e+00f,
- /* 20, 1 */ -3.929324583e-04f, +2.236335973e-03f, +5.288730096e-04f, -9.916240655e-03f, -7.235223044e-04f, +2.212021870e-02f, +1.557339330e-03f, -3.412515163e-02f, -3.088657053e-03f, +3.930609269e-02f, +4.328121901e-03f, -3.450613681e-02f, -4.117983282e-03f, +2.271744325e-02f, +2.467540325e-03f, -1.048404473e-02f, -6.307983207e-04f, +2.729031078e-03f, -3.387491377e-04f, +0.000000000e+00f,
- /* 20, 2 */ +0.000000000e+00f, +1.931175707e-03f, +1.033703668e-03f, -9.493276252e-03f, -2.202626937e-03f, +2.150371837e-02f, +4.274418757e-03f, -3.344119198e-02f, -6.721842627e-03f, +3.873376177e-02f, +8.036125742e-03f, -3.418837690e-02f, -7.019484999e-03f, +2.267661502e-02f, +4.149462009e-03f, -1.061062992e-02f, -1.276919763e-03f, +2.866023275e-03f, -2.870815261e-04f, +0.000000000e+00f,
- /* 20, 3 */ +0.000000000e+00f, +1.638662038e-03f, +1.484286050e-03f, -8.990907936e-03f, -3.586602863e-03f, +2.069305390e-02f, +6.875821908e-03f, -3.244383298e-02f, -1.025584288e-02f, +3.778668841e-02f, +1.169297592e-02f, -3.352791602e-02f, -9.923776326e-03f, +2.239890298e-02f, +5.866701604e-03f, -1.062161571e-02f, -1.959867511e-03f, +2.971909246e-03f, -2.170808154e-04f, +0.000000000e+00f,
- /* 20, 4 */ +0.000000000e+00f, +1.361489293e-03f, +1.878600735e-03f, -8.419725702e-03f, -4.864357078e-03f, +1.970376789e-02f, +9.337391966e-03f, -3.114878076e-02f, -1.365548733e-02f, +3.647500669e-02f, +1.526076366e-02f, -3.252647846e-02f, -1.280005487e-02f, +2.187944695e-02f, +7.601099328e-03f, -1.051027343e-02f, -2.672992279e-03f, +3.042103091e-03f, -1.274866066e-04f, +0.000000000e+00f,
- /* 20, 5 */ +0.000000000e+00f, +1.101888322e-03f, +2.215532402e-03f, -7.790617836e-03f, -6.026519023e-03f, +1.855289977e-02f, +1.163710530e-02f, -2.957469445e-02f, -1.688736106e-02f, +3.481273909e-02f, +1.870230489e-02f, -3.118953221e-02f, -1.561714772e-02f, +2.111601531e-02f, +9.333550613e-03f, -1.027109466e-02f, -3.408794343e-03f, +3.072235528e-03f, -1.724424543e-05f, +0.000000000e+00f,
- /* 20, 6 */ +0.000000000e+00f, +8.616336525e-04f, +2.494833248e-03f, -7.114614810e-03f, -7.065487327e-03f, +1.725873795e-02f, +1.375527431e-02f, -2.774292839e-02f, -1.992016339e-02f, +3.281763375e-02f, +2.198156213e-02f, -2.952627516e-02f, -1.834386597e-02f, +2.010910684e-02f, +1.104420948e-02f, -9.899921148e-03f, -4.158982805e-03f, +3.058238443e-03f, +1.144582079e-04f, +0.000000000e+00f,
- /* 20, 7 */ +0.000000000e+00f, +6.420563626e-04f, +2.717075783e-03f, -6.402738187e-03f, -7.975452307e-03f, +1.584056403e-02f, +1.567471786e-02f, -2.567724669e-02f, -2.272503888e-02f, +3.051095862e-02f, +2.506405514e-02f, -2.754957697e-02f, -2.094936609e-02f, +1.886202143e-02f, +1.271270843e-02f, -9.394061811e-03f, -4.914549404e-03f, +2.996429606e-03f, +2.681538305e-04f, +0.000000000e+00f,
- /* 20, 8 */ +0.000000000e+00f, +4.440621234e-04f, +2.883596201e-03f, -5.665856445e-03f, -8.752394750e-03f, +1.431839236e-02f, +1.738089791e-02f, -2.340351394e-02f, -2.527587699e-02f, +2.791725525e-02f, +2.791725525e-02f, -2.527587699e-02f, -2.340351394e-02f, +1.738089791e-02f, +1.431839236e-02f, -8.752394750e-03f, -5.665856445e-03f, +2.883596201e-03f, +4.440621234e-04f, +0.000000000e+00f,
- /* 20, 9 */ +0.000000000e+00f, +2.681538305e-04f, +2.996429606e-03f, -4.914549404e-03f, -9.394061811e-03f, +1.271270843e-02f, +1.886202143e-02f, -2.094936609e-02f, -2.754957697e-02f, +2.506405514e-02f, +3.051095862e-02f, -2.272503888e-02f, -2.567724669e-02f, +1.567471786e-02f, +1.584056403e-02f, -7.975452307e-03f, -6.402738187e-03f, +2.717075783e-03f, +6.420563626e-04f, +0.000000000e+00f,
- /* 20,10 */ +0.000000000e+00f, +1.144582079e-04f, +3.058238443e-03f, -4.158982805e-03f, -9.899921148e-03f, +1.104420948e-02f, +2.010910684e-02f, -1.834386597e-02f, -2.952627516e-02f, +2.198156213e-02f, +3.281763375e-02f, -1.992016339e-02f, -2.774292839e-02f, +1.375527431e-02f, +1.725873795e-02f, -7.065487327e-03f, -7.114614810e-03f, +2.494833248e-03f, +8.616336525e-04f, +0.000000000e+00f,
- /* 20,11 */ +0.000000000e+00f, -1.724424543e-05f, +3.072235528e-03f, -3.408794343e-03f, -1.027109466e-02f, +9.333550613e-03f, +2.111601531e-02f, -1.561714772e-02f, -3.118953221e-02f, +1.870230489e-02f, +3.481273909e-02f, -1.688736106e-02f, -2.957469445e-02f, +1.163710530e-02f, +1.855289977e-02f, -6.026519023e-03f, -7.790617836e-03f, +2.215532402e-03f, +1.101888322e-03f, +0.000000000e+00f,
- /* 20,12 */ +0.000000000e+00f, -1.274866066e-04f, +3.042103091e-03f, -2.672992279e-03f, -1.051027343e-02f, +7.601099328e-03f, +2.187944695e-02f, -1.280005487e-02f, -3.252647846e-02f, +1.526076366e-02f, +3.647500669e-02f, -1.365548733e-02f, -3.114878076e-02f, +9.337391966e-03f, +1.970376789e-02f, -4.864357078e-03f, -8.419725702e-03f, +1.878600735e-03f, +1.361489293e-03f, +0.000000000e+00f,
- /* 20,13 */ +0.000000000e+00f, -2.170808154e-04f, +2.971909246e-03f, -1.959867511e-03f, -1.062161571e-02f, +5.866701604e-03f, +2.239890298e-02f, -9.923776326e-03f, -3.352791602e-02f, +1.169297592e-02f, +3.778668841e-02f, -1.025584288e-02f, -3.244383298e-02f, +6.875821908e-03f, +2.069305390e-02f, -3.586602863e-03f, -8.990907936e-03f, +1.484286050e-03f, +1.638662038e-03f, +0.000000000e+00f,
- /* 20,14 */ +0.000000000e+00f, -2.870815261e-04f, +2.866023275e-03f, -1.276919763e-03f, -1.061062992e-02f, +4.149462009e-03f, +2.267661502e-02f, -7.019484999e-03f, -3.418837690e-02f, +8.036125742e-03f, +3.873376177e-02f, -6.721842627e-03f, -3.344119198e-02f, +4.274418757e-03f, +2.150371837e-02f, -2.202626937e-03f, -9.493276252e-03f, +1.033703668e-03f, +1.931175707e-03f, +0.000000000e+00f,
- /* 20,15 */ +0.000000000e+00f, -3.387491377e-04f, +2.729031078e-03f, -6.307983207e-04f, -1.048404473e-02f, +2.467540325e-03f, +2.271744325e-02f, -4.117983282e-03f, -3.450613681e-02f, +4.328121901e-03f, +3.930609269e-02f, -3.088657053e-03f, -3.412515163e-02f, +1.557339330e-03f, +2.212021870e-02f, -7.235223044e-04f, -9.916240655e-03f, +5.288730096e-04f, +2.236335973e-03f, -3.929324583e-04f,
- /* 16, 0 */ +3.044394378e-03f, -5.755865016e-03f, -7.644875237e-03f, +1.825808857e-02f, +9.222448502e-03f, -3.286388427e-02f, -4.241838036e-03f, +3.949755319e-02f, -4.241838036e-03f, -3.286388427e-02f, +9.222448502e-03f, +1.825808857e-02f, -7.644875237e-03f, -5.755865016e-03f, +3.044394378e-03f, -1.466795211e-05f,
- /* 16, 1 */ +3.096598285e-03f, -4.938670705e-03f, -8.543525001e-03f, +1.681174815e-02f, +1.171692718e-02f, -3.156650717e-02f, -8.140229219e-03f, +3.927338559e-02f, -2.566011090e-04f, -3.380519836e-02f, +6.539747546e-03f, +1.954192550e-02f, -6.591652894e-03f, -6.554797296e-03f, +2.932700428e-03f, +1.424716020e-04f,
- /* 16, 2 */ +3.093580763e-03f, -4.116215273e-03f, -9.283856280e-03f, +1.522768985e-02f, +1.399813452e-02f, -2.993548791e-02f, -1.190603152e-02f, +3.860369285e-02f, +3.768233493e-03f, -3.437220120e-02f, +3.697060454e-03f, +2.063975168e-02f, -5.390052618e-03f, -7.321916780e-03f, +2.757987679e-03f, +3.278051009e-04f,
- /* 16, 3 */ +3.040228116e-03f, -3.300778044e-03f, -9.864516741e-03f, +1.353158972e-02f, +1.604446323e-02f, -2.799705310e-02f, -1.549559239e-02f, +3.749686691e-02f, +7.784523662e-03f, -3.455113173e-02f, +7.255039591e-04f, +2.152972014e-02f, -4.048737024e-03f, -8.043312786e-03f, +2.517583336e-03f, +5.415628140e-04f,
- /* 16, 4 */ +2.941918573e-03f, -2.503765206e-03f, -1.028645874e-02f, +1.174962597e-02f, +1.783794825e-02f, -2.578082191e-02f, -1.886790220e-02f, +3.596676747e-02f, +1.174386090e-02f, -3.433297304e-02f, -2.341279491e-03f, +2.219201396e-02f, -2.578818314e-03f, -8.704920467e-03f, +2.209778110e-03f, +1.246855370e-03f,
- /* 16, 5 */ +2.804395319e-03f, -1.735580001e-03f, -1.055281940e-02f, +9.908096520e-03f, +1.936441115e-02f, -2.331935318e-02f, -2.198510572e-02f, +3.403253365e-02f, +1.559820593e-02f, -3.371364718e-02f, -5.467520855e-03f, +2.260919785e-02f, -9.938011251e-04f, -9.292739628e-03f, +1.833922789e-03f, +1.492594630e-03f,
- /* 16, 6 */ +2.633640678e-03f, -1.005515791e-03f, -1.066877263e-02f, +8.033047542e-03f, +2.061354789e-02f, -2.064765983e-02f, -2.481296600e-02f, +3.171832409e-02f, +1.930052332e-02f, -3.269414425e-02f, -8.615762914e-03f, +2.276654580e-02f, +6.905139302e-04f, -9.793063512e-03f, +1.390511455e-03f, +1.739628231e-03f,
- /* 16, 7 */ +2.435753603e-03f, -3.216727033e-04f, -1.064135670e-02f, +6.149918447e-03f, +2.157895980e-02f, -1.780269814e-02f, -2.732127429e-02f, +2.905298940e-02f, +2.280540611e-02f, -3.128058296e-02f, -1.174733238e-02f, +2.265233903e-02f, +2.456165958e-03f, -1.019271416e-02f, +8.812491435e-04f, +1.982865330e-03f,
- /* 16, 8 */ +2.216832517e-03f, +3.091018830e-04f, -1.047928070e-02f, +4.283208005e-03f, +2.225812888e-02f, -1.482283947e-02f, -2.948420097e-02f, +2.606968157e-02f, +2.606968157e-02f, -2.948420097e-02f, -1.482283947e-02f, +2.225812888e-02f, +4.283208005e-03f, -1.047928070e-02f, +3.091018830e-04f, +2.216832517e-03f,
- /* 16, 9 */ +1.982865330e-03f, +8.812491435e-04f, -1.019271416e-02f, +2.456165958e-03f, +2.265233903e-02f, -1.174733238e-02f, -3.128058296e-02f, +2.280540611e-02f, +2.905298940e-02f, -2.732127429e-02f, -1.780269814e-02f, +2.157895980e-02f, +6.149918447e-03f, -1.064135670e-02f, -3.216727033e-04f, +2.435753603e-03f,
- /* 16,10 */ +1.739628231e-03f, +1.390511455e-03f, -9.793063512e-03f, +6.905139302e-04f, +2.276654580e-02f, -8.615762914e-03f, -3.269414425e-02f, +1.930052332e-02f, +3.171832409e-02f, -2.481296600e-02f, -2.064765983e-02f, +2.061354789e-02f, +8.033047542e-03f, -1.066877263e-02f, -1.005515791e-03f, +2.633640678e-03f,
- /* 16,11 */ +1.492594630e-03f, +1.833922789e-03f, -9.292739628e-03f, -9.938011251e-04f, +2.260919785e-02f, -5.467520855e-03f, -3.371364718e-02f, +1.559820593e-02f, +3.403253365e-02f, -2.198510572e-02f, -2.331935318e-02f, +1.936441115e-02f, +9.908096520e-03f, -1.055281940e-02f, -1.735580001e-03f, +2.804395319e-03f,
- /* 16,12 */ +1.246855370e-03f, +2.209778110e-03f, -8.704920467e-03f, -2.578818314e-03f, +2.219201396e-02f, -2.341279491e-03f, -3.433297304e-02f, +1.174386090e-02f, +3.596676747e-02f, -1.886790220e-02f, -2.578082191e-02f, +1.783794825e-02f, +1.174962597e-02f, -1.028645874e-02f, -2.503765206e-03f, +2.941918573e-03f,
- /* 16,13 */ +5.415628140e-04f, +2.517583336e-03f, -8.043312786e-03f, -4.048737024e-03f, +2.152972014e-02f, +7.255039591e-04f, -3.455113173e-02f, +7.784523662e-03f, +3.749686691e-02f, -1.549559239e-02f, -2.799705310e-02f, +1.604446323e-02f, +1.353158972e-02f, -9.864516741e-03f, -3.300778044e-03f, +3.040228116e-03f,
- /* 16,14 */ +3.278051009e-04f, +2.757987679e-03f, -7.321916780e-03f, -5.390052618e-03f, +2.063975168e-02f, +3.697060454e-03f, -3.437220120e-02f, +3.768233493e-03f, +3.860369285e-02f, -1.190603152e-02f, -2.993548791e-02f, +1.399813452e-02f, +1.522768985e-02f, -9.283856280e-03f, -4.116215273e-03f, +3.093580763e-03f,
- /* 16,15 */ +1.424716020e-04f, +2.932700428e-03f, -6.554797296e-03f, -6.591652894e-03f, +1.954192550e-02f, +6.539747546e-03f, -3.380519836e-02f, -2.566011090e-04f, +3.927338559e-02f, -8.140229219e-03f, -3.156650717e-02f, +1.171692718e-02f, +1.681174815e-02f, -8.543525001e-03f, -4.938670705e-03f, +3.096598285e-03f,
- /* 16, 0 */ +2.106112688e-03f, -1.136549770e-04f, -1.070669430e-02f, +1.017472059e-02f, +1.725397418e-02f, -2.914959442e-02f, -8.963852042e-03f, +3.949755319e-02f, -8.963852042e-03f, -2.914959442e-02f, +1.725397418e-02f, +1.017472059e-02f, -1.070669430e-02f, -1.136549770e-04f, +2.106112688e-03f, +0.000000000e+00f,
- /* 16, 1 */ +1.845338280e-03f, +5.473343456e-04f, -1.065891816e-02f, +8.155641030e-03f, +1.899089579e-02f, -2.691951328e-02f, -1.297236480e-02f, +3.923810803e-02f, -4.790894575e-03f, -3.103786392e-02f, +1.521305380e-02f, +1.215062389e-02f, -1.058864907e-02f, -8.385121370e-04f, +2.352913572e-03f, +0.000000000e+00f,
- /* 16, 2 */ +1.577651889e-03f, +1.138027416e-03f, -1.045641117e-02f, +6.125232216e-03f, +2.040939526e-02f, -2.438627738e-02f, -1.676283724e-02f, +3.846353557e-02f, -5.100475811e-04f, -3.254996068e-02f, +1.288771825e-02f, +1.405076114e-02f, -1.029592106e-02f, -1.619065127e-03f, +2.578329862e-03f, +0.000000000e+00f,
- /* 16, 3 */ +1.309641631e-03f, +1.653747621e-03f, -1.011218665e-02f, +4.114112388e-03f, +2.150028131e-02f, -2.159216402e-02f, -2.028541539e-02f, +3.718506562e-02f, +3.820010384e-03f, -3.365643786e-02f, +1.030255138e-02f, +1.584231759e-02f, -9.822158049e-03f, -2.445442331e-03f, +2.774741598e-03f, +0.000000000e+00f,
- /* 16, 4 */ +5.462770218e-04f, +2.091544281e-03f, -9.640854940e-03f, +2.151223968e-03f, +2.225957955e-02f, -1.858235038e-02f, -2.349470263e-02f, +3.542121816e-02f, +8.139354376e-03f, -3.433331277e-02f, +7.486889709e-03f, +1.749279467e-02f, -9.163764446e-03f, -3.306153325e-03f, +2.934475195e-03f, -4.634120047e-04f,
- /* 16, 5 */ +3.039101756e-04f, +2.450135010e-03f, -9.058284956e-03f, +2.634319572e-04f, +2.268843286e-02f, -1.540416082e-02f, -2.635039795e-02f, +3.319751225e-02f, +1.238771678e-02f, -3.456253827e-02f, +4.474489227e-03f, +1.897056596e-02f, -8.320109905e-03f, -4.188206830e-03f, +3.049970761e-03f, -4.400286560e-04f,
- /* 16, 6 */ +9.722433303e-05f, +2.729819110e-03f, -8.381259809e-03f, -1.524826854e-03f, +2.279292110e-02f, -1.210629477e-02f, -2.881784707e-02f, +3.054606534e-02f, +1.650540309e-02f, -3.433238619e-02f, +1.303110212e-03f, +2.024543829e-02f, -7.293693549e-03f, -5.077265419e-03f, +3.113958519e-03f, -3.921992729e-04f,
- /* 16, 7 */ -7.427658644e-05f, +2.932365266e-03f, -7.627133157e-03f, -3.191839567e-03f, +2.258380561e-02f, -8.738048497e-03f, -3.086849848e-02f, +2.750508980e-02f, +2.043420490e-02f, -3.363773532e-02f, -1.985974837e-03f, +2.128920837e-02f, -6.090258802e-03f, -5.957835775e-03f, +3.119640969e-03f, -3.169896142e-04f,
- /* 16, 8 */ -2.117631665e-04f, +3.060877176e-03f, -6.813492559e-03f, -4.718854594e-03f, +2.207620481e-02f, -5.348543574e-03f, -3.248025769e-02f, +2.411829496e-02f, +2.411829496e-02f, -3.248025769e-02f, -5.348543574e-03f, +2.207620481e-02f, -4.718854594e-03f, -6.813492559e-03f, +3.060877176e-03f, -2.117631665e-04f,
- /* 16, 9 */ -3.169896142e-04f, +3.119640969e-03f, -5.957835775e-03f, -6.090258802e-03f, +2.128920837e-02f, -1.985974837e-03f, -3.363773532e-02f, +2.043420490e-02f, +2.750508980e-02f, -3.086849848e-02f, -8.738048497e-03f, +2.258380561e-02f, -3.191839567e-03f, -7.627133157e-03f, +2.932365266e-03f, -7.427658644e-05f,
- /* 16,10 */ -3.921992729e-04f, +3.113958519e-03f, -5.077265419e-03f, -7.293693549e-03f, +2.024543829e-02f, +1.303110212e-03f, -3.433238619e-02f, +1.650540309e-02f, +3.054606534e-02f, -2.881784707e-02f, -1.210629477e-02f, +2.279292110e-02f, -1.524826854e-03f, -8.381259809e-03f, +2.729819110e-03f, +9.722433303e-05f,
- /* 16,11 */ -4.400286560e-04f, +3.049970761e-03f, -4.188206830e-03f, -8.320109905e-03f, +1.897056596e-02f, +4.474489227e-03f, -3.456253827e-02f, +1.238771678e-02f, +3.319751225e-02f, -2.635039795e-02f, -1.540416082e-02f, +2.268843286e-02f, +2.634319572e-04f, -9.058284956e-03f, +2.450135010e-03f, +3.039101756e-04f,
- /* 16,12 */ -4.634120047e-04f, +2.934475195e-03f, -3.306153325e-03f, -9.163764446e-03f, +1.749279467e-02f, +7.486889709e-03f, -3.433331277e-02f, +8.139354376e-03f, +3.542121816e-02f, -2.349470263e-02f, -1.858235038e-02f, +2.225957955e-02f, +2.151223968e-03f, -9.640854940e-03f, +2.091544281e-03f, +5.462770218e-04f,
- /* 16,13 */ +0.000000000e+00f, +2.774741598e-03f, -2.445442331e-03f, -9.822158049e-03f, +1.584231759e-02f, +1.030255138e-02f, -3.365643786e-02f, +3.820010384e-03f, +3.718506562e-02f, -2.028541539e-02f, -2.159216402e-02f, +2.150028131e-02f, +4.114112388e-03f, -1.011218665e-02f, +1.653747621e-03f, +1.309641631e-03f,
- /* 16,14 */ +0.000000000e+00f, +2.578329862e-03f, -1.619065127e-03f, -1.029592106e-02f, +1.405076114e-02f, +1.288771825e-02f, -3.254996068e-02f, -5.100475811e-04f, +3.846353557e-02f, -1.676283724e-02f, -2.438627738e-02f, +2.040939526e-02f, +6.125232216e-03f, -1.045641117e-02f, +1.138027416e-03f, +1.577651889e-03f,
- /* 16,15 */ +0.000000000e+00f, +2.352913572e-03f, -8.385121370e-04f, -1.058864907e-02f, +1.215062389e-02f, +1.521305380e-02f, -3.103786392e-02f, -4.790894575e-03f, +3.923810803e-02f, -1.297236480e-02f, -2.691951328e-02f, +1.899089579e-02f, +8.155641030e-03f, -1.065891816e-02f, +5.473343456e-04f, +1.845338280e-03f,
- /* 16, 0 */ -2.517634455e-04f, +5.956310854e-03f, -8.647029609e-03f, +1.153545125e-03f, +2.185732832e-02f, -2.369518339e-02f, -1.347728153e-02f, +3.949755319e-02f, -1.347728153e-02f, -2.369518339e-02f, +2.185732832e-02f, +1.153545125e-03f, -8.647029609e-03f, +2.914798040e-03f, -2.517634455e-04f, +0.000000000e+00f,
- /* 16, 1 */ -3.647589216e-04f, +5.559366521e-03f, -7.860683839e-03f, -8.150822761e-04f, +2.252089097e-02f, -2.063007883e-02f, -1.749200540e-02f, +3.920026256e-02f, -9.205150933e-03f, -2.647415600e-02f, +2.081322447e-02f, +3.223212212e-03f, -9.337661142e-03f, +2.677108373e-03f, -9.939782505e-05f, +0.000000000e+00f,
- /* 16, 2 */ -4.414886472e-04f, +5.113535158e-03f, -7.000222932e-03f, -2.654422569e-03f, +2.280787202e-02f, -1.733513495e-02f, -2.118899605e-02f, +3.831333038e-02f, -4.740975303e-03f, -2.891426752e-02f, +1.939126736e-02f, +5.362271050e-03f, -9.911447152e-03f, +2.349799583e-03f, +9.477253367e-05f, +0.000000000e+00f,
- /* 16, 3 */ -4.855802427e-04f, +4.633347213e-03f, -6.087250386e-03f, -4.340001532e-03f, +2.272858071e-02f, -1.386914009e-02f, -2.451395150e-02f, +3.685148678e-02f, -1.540603716e-04f, -3.096737610e-02f, +1.760097190e-02f, +7.536131493e-03f, -1.034820384e-02f, +1.931270179e-03f, +3.323676319e-04f, +0.000000000e+00f,
- /* 16, 4 */ +0.000000000e+00f, +4.132457962e-03f, -5.142935987e-03f, -5.851401101e-03f, +2.229926864e-02f, -1.029228788e-02f, -2.741946400e-02f, +3.483898696e-02f, +4.483517704e-03f, -3.259088680e-02f, +1.545873378e-02f, +9.707799030e-03f, -1.062918096e-02f, +1.421916825e-03f, +6.140535745e-04f, +0.000000000e+00f,
- /* 16, 5 */ +0.000000000e+00f, +3.623457200e-03f, -4.187611099e-03f, -7.172450485e-03f, +2.154162444e-02f, -6.665085054e-03f, -2.986575546e-02f, +3.230917458e-02f, +9.098146940e-03f, -3.374862716e-02f, +1.298775300e-02f, +1.183848413e-02f, -1.073754388e-02f, +8.242784646e-04f, +9.394126333e-04f, +0.000000000e+00f,
- /* 16, 6 */ +0.000000000e+00f, +3.117716971e-03f, -3.240404345e-03f, -8.291325326e-03f, +2.048218318e-02f, -3.047278250e-03f, -3.182126614e-02f, +2.930388237e-02f, +1.361595241e-02f, -3.441162052e-02f, +1.021782484e-02f, +1.388827332e-02f, -1.065884073e-02f, +1.431392807e-04f, +1.306826648e-03f, +0.000000000e+00f,
- /* 16, 7 */ +0.000000000e+00f, +2.625277441e-03f, -2.318923448e-03f, -9.200556695e-03f, +1.915166452e-02f, +5.031802154e-04f, -3.326308735e-02f, +2.587268140e-02f, +1.796408380e-02f, -3.455874049e-02f, +7.184998851e-03f, +1.581685040e-02f, -1.038144369e-02f, -6.144144569e-04f, +1.713377737e-03f, +0.000000000e+00f,
- /* 16, 8 */ +0.000000000e+00f, +2.154770219e-03f, -1.438987736e-03f, -9.896953513e-03f, +1.758425506e-02f, +3.931108902e-03f, -3.417723209e-02f, +2.207199352e-02f, +2.207199352e-02f, -3.417723209e-02f, +3.931108902e-03f, +1.758425506e-02f, -9.896953513e-03f, -1.438987736e-03f, +2.154770219e-03f, +0.000000000e+00f,
- /* 16, 9 */ +0.000000000e+00f, +1.713377737e-03f, -6.144144569e-04f, -1.038144369e-02f, +1.581685040e-02f, +7.184998851e-03f, -3.455874049e-02f, +1.796408380e-02f, +2.587268140e-02f, -3.326308735e-02f, +5.031802154e-04f, +1.915166452e-02f, -9.200556695e-03f, -2.318923448e-03f, +2.625277441e-03f, +0.000000000e+00f,
- /* 16,10 */ +0.000000000e+00f, +1.306826648e-03f, +1.431392807e-04f, -1.065884073e-02f, +1.388827332e-02f, +1.021782484e-02f, -3.441162052e-02f, +1.361595241e-02f, +2.930388237e-02f, -3.182126614e-02f, -3.047278250e-03f, +2.048218318e-02f, -8.291325326e-03f, -3.240404345e-03f, +3.117716971e-03f, +0.000000000e+00f,
- /* 16,11 */ +0.000000000e+00f, +9.394126333e-04f, +8.242784646e-04f, -1.073754388e-02f, +1.183848413e-02f, +1.298775300e-02f, -3.374862716e-02f, +9.098146940e-03f, +3.230917458e-02f, -2.986575546e-02f, -6.665085054e-03f, +2.154162444e-02f, -7.172450485e-03f, -4.187611099e-03f, +3.623457200e-03f, +0.000000000e+00f,
- /* 16,12 */ +0.000000000e+00f, +6.140535745e-04f, +1.421916825e-03f, -1.062918096e-02f, +9.707799030e-03f, +1.545873378e-02f, -3.259088680e-02f, +4.483517704e-03f, +3.483898696e-02f, -2.741946400e-02f, -1.029228788e-02f, +2.229926864e-02f, -5.851401101e-03f, -5.142935987e-03f, +4.132457962e-03f, +0.000000000e+00f,
- /* 16,13 */ +0.000000000e+00f, +3.323676319e-04f, +1.931270179e-03f, -1.034820384e-02f, +7.536131493e-03f, +1.760097190e-02f, -3.096737610e-02f, -1.540603716e-04f, +3.685148678e-02f, -2.451395150e-02f, -1.386914009e-02f, +2.272858071e-02f, -4.340001532e-03f, -6.087250386e-03f, +4.633347213e-03f, -4.855802427e-04f,
- /* 16,14 */ +0.000000000e+00f, +9.477253367e-05f, +2.349799583e-03f, -9.911447152e-03f, +5.362271050e-03f, +1.939126736e-02f, -2.891426752e-02f, -4.740975303e-03f, +3.831333038e-02f, -2.118899605e-02f, -1.733513495e-02f, +2.280787202e-02f, -2.654422569e-03f, -7.000222932e-03f, +5.113535158e-03f, -4.414886472e-04f,
- /* 16,15 */ +0.000000000e+00f, -9.939782505e-05f, +2.677108373e-03f, -9.337661142e-03f, +3.223212212e-03f, +2.081322447e-02f, -2.647415600e-02f, -9.205150933e-03f, +3.920026256e-02f, -1.749200540e-02f, -2.063007883e-02f, +2.252089097e-02f, -8.150822761e-04f, -7.860683839e-03f, +5.559366521e-03f, -3.647589216e-04f,
- /* 12, 0 */ -3.924537125e-03f, -6.177283790e-03f, +2.270137823e-02f, -1.696686670e-02f, -1.770529738e-02f, +3.949755319e-02f, -1.770529738e-02f, -1.696686670e-02f, +2.270137823e-02f, -6.177283790e-03f, -3.924537125e-03f, +2.689823824e-03f,
- /* 12, 1 */ -2.918444824e-03f, -7.533875928e-03f, +2.217225575e-02f, -1.325050127e-02f, -2.161377319e-02f, +3.915985190e-02f, -1.343239533e-02f, -2.049610855e-02f, +2.283609035e-02f, -4.600700986e-03f, -4.945631587e-03f, +2.897838580e-03f,
- /* 12, 2 */ -1.948111766e-03f, -8.657444743e-03f, +2.127619260e-02f, -9.420045488e-03f, -2.509275167e-02f, +3.815312062e-02f, -8.867972963e-03f, -2.376686078e-02f, +2.255583139e-02f, -2.822790564e-03f, -5.958903400e-03f, +3.051876120e-03f,
- /* 12, 3 */ -1.031879811e-03f, -9.540571177e-03f, +2.004673480e-02f, -5.548699503e-03f, -2.808617760e-02f, +3.649634673e-02f, -4.091413649e-03f, -2.671092541e-02f, +2.184766025e-02f, -8.677312765e-04f, -6.939883353e-03f, +3.140832095e-03f,
- /* 12, 4 */ -1.854082964e-04f, -1.018130776e-02f, +1.852257236e-02f, -1.708362919e-03f, -3.054799363e-02f, +3.422074403e-02f, +8.129280323e-04f, -2.926474106e-02f, +2.070682375e-02f, +1.235031889e-03f, -7.862950435e-03f, +3.154329873e-03f,
- /* 12, 5 */ +5.785109863e-04f, -1.058292035e-02f, +1.674653148e-02f, +2.031769072e-03f, -3.244290444e-02f, +3.136911490e-02f, +5.757350815e-03f, -3.137078637e-02f, +1.913716500e-02f, +3.451172065e-03f, -8.701884951e-03f, +3.083080347e-03f,
- /* 12, 6 */ +1.250056620e-03f, -1.075352499e-02f, +1.476451598e-02f, +5.606517218e-03f, -3.374690984e-02f, +2.799497691e-02f, +1.065251585e-02f, -3.297888633e-02f, +1.715135739e-02f, +5.741996578e-03f, -9.430471381e-03f, +2.919238566e-03f,
- /* 12, 7 */ +1.822400383e-03f, -1.070563332e-02f, +1.262442359e-02f, +8.955911342e-03f, -3.444759838e-02f, +2.416147295e-02f, +1.540920462e-02f, -3.404739100e-02f, +1.477095353e-02f, +8.065088216e-03f, -1.002313834e-02f, +2.656746896e-03f,
- /* 12, 8 */ +2.291654178e-03f, -1.045562141e-02f, +1.037506188e-02f, +1.202624229e-02f, -3.454419870e-02f, +1.994008861e-02f, +1.994008861e-02f, -3.454419870e-02f, +1.202624229e-02f, +1.037506188e-02f, -1.045562141e-02f, +2.291654178e-03f,
- /* 12, 9 */ +2.656746896e-03f, -1.002313834e-02f, +8.065088216e-03f, +1.477095353e-02f, -3.404739100e-02f, +1.540920462e-02f, +2.416147295e-02f, -3.444759838e-02f, +8.955911342e-03f, +1.262442359e-02f, -1.070563332e-02f, +1.822400383e-03f,
- /* 12,10 */ +2.919238566e-03f, -9.430471381e-03f, +5.741996578e-03f, +1.715135739e-02f, -3.297888633e-02f, +1.065251585e-02f, +2.799497691e-02f, -3.374690984e-02f, +5.606517218e-03f, +1.476451598e-02f, -1.075352499e-02f, +1.250056620e-03f,
- /* 12,11 */ +3.083080347e-03f, -8.701884951e-03f, +3.451172065e-03f, +1.913716500e-02f, -3.137078637e-02f, +5.757350815e-03f, +3.136911490e-02f, -3.244290444e-02f, +2.031769072e-03f, +1.674653148e-02f, -1.058292035e-02f, +5.785109863e-04f,
- /* 12,12 */ +3.154329873e-03f, -7.862950435e-03f, +1.235031889e-03f, +2.070682375e-02f, -2.926474106e-02f, +8.129280323e-04f, +3.422074403e-02f, -3.054799363e-02f, -1.708362919e-03f, +1.852257236e-02f, -1.018130776e-02f, -1.854082964e-04f,
- /* 12,13 */ +3.140832095e-03f, -6.939883353e-03f, -8.677312765e-04f, +2.184766025e-02f, -2.671092541e-02f, -4.091413649e-03f, +3.649634673e-02f, -2.808617760e-02f, -5.548699503e-03f, +2.004673480e-02f, -9.540571177e-03f, -1.031879811e-03f,
- /* 12,14 */ +3.051876120e-03f, -5.958903400e-03f, -2.822790564e-03f, +2.255583139e-02f, -2.376686078e-02f, -8.867972963e-03f, +3.815312062e-02f, -2.509275167e-02f, -9.420045488e-03f, +2.127619260e-02f, -8.657444743e-03f, -1.948111766e-03f,
- /* 12,15 */ +2.897838580e-03f, -4.945631587e-03f, -4.600700986e-03f, +2.283609035e-02f, -2.049610855e-02f, -1.343239533e-02f, +3.915985190e-02f, -2.161377319e-02f, -1.325050127e-02f, +2.217225575e-02f, -7.533875928e-03f, -2.918444824e-03f,
- /* 12, 0 */ +5.529156756e-04f, -1.017944650e-02f, +2.010395691e-02f, -9.501724583e-03f, -2.157725737e-02f, +3.949755319e-02f, -2.157725737e-02f, -9.501724583e-03f, +2.010395691e-02f, -1.017944650e-02f, +5.529156756e-04f, +9.520529918e-04f,
- /* 12, 1 */ +1.269440891e-03f, -1.060857725e-02f, +1.848426560e-02f, -5.389674050e-03f, -2.526172923e-02f, +3.911687897e-02f, -1.740939271e-02f, -1.356576871e-02f, +2.138958875e-02f, -9.480008902e-03f, -2.676127190e-04f, +1.273328470e-03f,
- /* 12, 2 */ +1.873685854e-03f, -1.077705214e-02f, +1.658179711e-02f, -1.315683663e-03f, -2.839592801e-02f, +3.798295250e-02f, -1.283645305e-02f, -1.749413418e-02f, +2.229518867e-02f, -8.506812328e-03f, -1.180051037e-03f, +1.604489886e-03f,
- /* 12, 3 */ +2.361120897e-03f, -1.070010905e-02f, +1.445171501e-02f, +2.637679549e-03f, -3.092573063e-02f, +3.611987567e-02f, -7.946589383e-03f, -2.119952675e-02f, +2.278144860e-02f, -7.263083188e-03f, -2.168530550e-03f, +1.934678236e-03f,
- /* 12, 4 */ +2.730794315e-03f, -1.039785120e-02f, +1.215160004e-02f, +6.393108320e-03f, -3.281077803e-02f, +3.356720057e-02f, -2.835931904e-03f, -2.459705675e-02f, +2.281696739e-02f, -5.759053577e-03f, -3.213563229e-03f, +2.251787566e-03f,
- /* 12, 5 */ +2.985073479e-03f, -9.894440683e-03f, +9.739988515e-03f, +9.880142532e-03f, -3.402514934e-02f, +3.037901891e-02f, +2.393471366e-03f, -2.760625942e-02f, +2.237934513e-02f, -4.012119167e-03f, -4.292316215e-03f, +2.542756696e-03f,
- /* 12, 6 */ +3.129309714e-03f, -9.217233004e-03f, +7.274949975e-03f, +1.303654969e-02f, -3.455769209e-02f, +2.662271867e-02f, +7.635875993e-03f, -3.015305887e-02f, +2.145609570e-02f, -2.046814992e-03f, -5.379003980e-03f, +2.793918230e-03f,
- /* 12, 7 */ +3.171441616e-03f, -8.395878746e-03f, +4.812738303e-03f, +1.580947051e-02f, -3.441200543e-02f, +2.237743869e-02f, +1.278417054e-02f, -3.217162603e-02f, +2.004534769e-02f, +1.053992035e-04f, -6.445394017e-03f, +2.991397563e-03f,
- /* 12, 8 */ +3.121552430e-03f, -7.461418245e-03f, +2.406547485e-03f, +1.815630830e-02f, -3.360608252e-02f, +1.773225889e-02f, +1.773225889e-02f, -3.360608252e-02f, +1.815630830e-02f, +2.406547485e-03f, -7.461418245e-03f, +3.121552430e-03f,
- /* 12, 9 */ +2.991397563e-03f, -6.445394017e-03f, +1.053992035e-04f, +2.004534769e-02f, -3.217162603e-02f, +1.278417054e-02f, +2.237743869e-02f, -3.441200543e-02f, +1.580947051e-02f, +4.812738303e-03f, -8.395878746e-03f, +3.171441616e-03f,
- /* 12,10 */ +2.793918230e-03f, -5.379003980e-03f, -2.046814992e-03f, +2.145609570e-02f, -3.015305887e-02f, +7.635875993e-03f, +2.662271867e-02f, -3.455769209e-02f, +1.303654969e-02f, +7.274949975e-03f, -9.217233004e-03f, +3.129309714e-03f,
- /* 12,11 */ +2.542756696e-03f, -4.292316215e-03f, -4.012119167e-03f, +2.237934513e-02f, -2.760625942e-02f, +2.393471366e-03f, +3.037901891e-02f, -3.402514934e-02f, +9.880142532e-03f, +9.739988515e-03f, -9.894440683e-03f, +2.985073479e-03f,
- /* 12,12 */ +2.251787566e-03f, -3.213563229e-03f, -5.759053577e-03f, +2.281696739e-02f, -2.459705675e-02f, -2.835931904e-03f, +3.356720057e-02f, -3.281077803e-02f, +6.393108320e-03f, +1.215160004e-02f, -1.039785120e-02f, +2.730794315e-03f,
- /* 12,13 */ +1.934678236e-03f, -2.168530550e-03f, -7.263083188e-03f, +2.278144860e-02f, -2.119952675e-02f, -7.946589383e-03f, +3.611987567e-02f, -3.092573063e-02f, +2.637679549e-03f, +1.445171501e-02f, -1.070010905e-02f, +2.361120897e-03f,
- /* 12,14 */ +1.604489886e-03f, -1.180051037e-03f, -8.506812328e-03f, +2.229518867e-02f, -1.749413418e-02f, -1.283645305e-02f, +3.798295250e-02f, -2.839592801e-02f, -1.315683663e-03f, +1.658179711e-02f, -1.077705214e-02f, +1.873685854e-03f,
- /* 12,15 */ +1.273328470e-03f, -2.676127190e-04f, -9.480008902e-03f, +2.138958875e-02f, -1.356576871e-02f, -1.740939271e-02f, +3.911687897e-02f, -2.526172923e-02f, -5.389674050e-03f, +1.848426560e-02f, -1.060857725e-02f, +1.269440891e-03f,
-
- /* 24, 0 */ -8.820438069e-05f, -1.519461079e-04f, -2.301651496e-04f, -3.149320871e-04f, -3.945939739e-04f, -4.554410135e-04f, -4.841532882e-04f, -4.705408991e-04f, -4.099602091e-04f, -3.048100066e-04f, -1.646897470e-04f, -5.099007530e-06f, +1.551006323e-04f, +2.969416536e-04f, +4.046294158e-04f, +4.681429482e-04f, +4.846228261e-04f, +4.583040637e-04f, +3.990939388e-04f, +3.201968846e-04f, +2.353759082e-04f, +1.564712483e-04f, +9.167483068e-05f, +4.482688286e-05f,
- /* 24, 1 */ -8.480575132e-05f, -1.474789784e-04f, -2.249812225e-04f, -3.096480504e-04f, -3.900204007e-04f, -4.524514078e-04f, -4.835165803e-04f, -4.727530367e-04f, -4.151145025e-04f, -3.125397891e-04f, -1.742016828e-04f, -1.529460870e-05f, +1.454387449e-04f, +2.889379628e-04f, +3.991236794e-04f, +4.655589110e-04f, +4.849233000e-04f, +4.610375470e-04f, +4.035168325e-04f, +3.254391996e-04f, +2.406110065e-04f, +1.610529558e-04f, +9.521673594e-05f, +4.721513201e-05f,
- /* 24, 2 */ -8.147924507e-05f, -1.430712350e-04f, -2.198265592e-04f, -3.043479843e-04f, -3.853766873e-04f, -4.493383067e-04f, -4.827146831e-04f, -4.747797448e-04f, -4.200908527e-04f, -3.201278616e-04f, -1.836320864e-04f, -2.548296987e-05f, +1.357085413e-04f, +2.808022583e-04f, +3.934446700e-04f, +4.627886263e-04f, +4.850529052e-04f, +4.636385032e-04f, +4.078592000e-04f, +3.306557574e-04f, +2.458678944e-04f, +1.656897170e-04f, +9.882966748e-05f, +4.967415993e-05f,
- /* 24, 3 */ -7.822510242e-05f, -1.387241832e-04f, -2.147035314e-04f, -2.990350629e-04f, -3.806663042e-04f, -4.461048161e-04f, -4.817496625e-04f, -4.766215175e-04f, -4.248879309e-04f, -3.275711800e-04f, -1.929766610e-04f, -3.565926997e-05f, +1.259145254e-04f, +2.725379532e-04f, +3.875941701e-04f, +4.598320457e-04f, +4.850099279e-04f, +4.661040260e-04f, +4.121175966e-04f, +3.358432542e-04f, +2.511439643e-04f, +1.703799499e-04f, +1.025131319e-04f, +5.220438912e-05f,
- /* 24, 4 */ -7.504350274e-05f, -1.344390595e-04f, -2.096144489e-04f, -2.937124231e-04f, -3.758927218e-04f, -4.427540852e-04f, -4.806236671e-04f, -4.782789577e-04f, -4.295045226e-04f, -3.348667971e-04f, -2.022311686e-04f, -4.581869630e-05f, +1.160612449e-04f, +2.641485480e-04f, +3.815740737e-04f, +4.566892339e-04f, +4.847927474e-04f, +4.684312656e-04f, +4.162885910e-04f, +3.409983591e-04f, +2.564365532e-04f, +1.751220037e-04f, +1.062665706e-04f, +5.480619333e-05f,
- /* 24, 5 */ -7.193456522e-05f, -1.302170312e-04f, -2.045615590e-04f, -2.883831622e-04f, -3.710594077e-04f, -4.392893036e-04f, -4.793389263e-04f, -4.797527765e-04f, -4.339395286e-04f, -3.420118645e-04f, -2.113914331e-04f, -5.595644787e-05f, +1.061532886e-04f, +2.556376279e-04f, +3.753863858e-04f, +4.533603695e-04f, +4.843998374e-04f, +4.706174312e-04f, +4.203687678e-04f, +3.461177167e-04f, +2.617429433e-04f, +1.799141593e-04f, +1.100893595e-04f, +5.747989630e-05f,
- /* 24, 6 */ -6.889834987e-05f, -1.260591965e-04f, -1.995470454e-04f, -2.830503358e-04f, -3.661698243e-04f, -4.357136989e-04f, -4.778977479e-04f, -4.810437916e-04f, -4.381919648e-04f, -3.490036345e-04f, -2.204533432e-04f, -6.606773875e-05f, +9.619528314e-05f, +2.470088608e-04f, +3.690332209e-04f, +4.498457452e-04f, +4.838297683e-04f, +4.726597937e-04f, +4.243547301e-04f, +3.511979487e-04f, +2.670603639e-04f, +1.847546294e-04f, +1.139808078e-04f, +6.022577049e-05f,
- /* 24, 7 */ -6.593485851e-05f, -1.219665852e-04f, -1.945730275e-04f, -2.777169567e-04f, -3.612274261e-04f, -4.320305335e-04f, -4.763025159e-04f, -4.821529264e-04f, -4.422609626e-04f, -3.558394612e-04f, -2.294128549e-04f, -7.614780136e-05f, +8.619188981e-05f, +2.382659945e-04f, +3.625168024e-04f, +4.461457687e-04f, +4.830812085e-04f, +4.745556880e-04f, +4.282431024e-04f, +3.562356572e-04f, +2.723859925e-04f, +1.896415594e-04f, +1.179401580e-04f, +6.304403582e-05f,
- /* 24, 8 */ -6.304403582e-05f, -1.179401580e-04f, -1.896415594e-04f, -2.723859925e-04f, -3.562356572e-04f, -4.282431024e-04f, -4.745556880e-04f, -4.830812085e-04f, -4.461457687e-04f, -3.625168024e-04f, -2.382659945e-04f, -8.619188981e-05f, +7.614780136e-05f, +2.294128549e-04f, +3.558394612e-04f, +4.422609626e-04f, +4.821529264e-04f, +4.763025159e-04f, +4.320305335e-04f, +3.612274261e-04f, +2.777169567e-04f, +1.945730275e-04f, +1.219665852e-04f, +6.593485851e-05f,
- /* 24, 9 */ -6.022577049e-05f, -1.139808078e-04f, -1.847546294e-04f, -2.670603639e-04f, -3.511979487e-04f, -4.243547301e-04f, -4.726597937e-04f, -4.838297683e-04f, -4.498457452e-04f, -3.690332209e-04f, -2.470088608e-04f, -9.619528314e-05f, +6.606773875e-05f, +2.204533432e-04f, +3.490036345e-04f, +4.381919648e-04f, +4.810437916e-04f, +4.778977479e-04f, +4.357136989e-04f, +3.661698243e-04f, +2.830503358e-04f, +1.995470454e-04f, +1.260591965e-04f, +6.889834987e-05f,
- /* 24,10 */ -5.747989630e-05f, -1.100893595e-04f, -1.799141593e-04f, -2.617429433e-04f, -3.461177167e-04f, -4.203687678e-04f, -4.706174312e-04f, -4.843998374e-04f, -4.533603695e-04f, -3.753863858e-04f, -2.556376279e-04f, -1.061532886e-04f, +5.595644787e-05f, +2.113914331e-04f, +3.420118645e-04f, +4.339395286e-04f, +4.797527765e-04f, +4.793389263e-04f, +4.392893036e-04f, +3.710594077e-04f, +2.883831622e-04f, +2.045615590e-04f, +1.302170312e-04f, +7.193456522e-05f,
- /* 24,11 */ -5.480619333e-05f, -1.062665706e-04f, -1.751220037e-04f, -2.564365532e-04f, -3.409983591e-04f, -4.162885910e-04f, -4.684312656e-04f, -4.847927474e-04f, -4.566892339e-04f, -3.815740737e-04f, -2.641485480e-04f, -1.160612449e-04f, +4.581869630e-05f, +2.022311686e-04f, +3.348667971e-04f, +4.295045226e-04f, +4.782789577e-04f, +4.806236671e-04f, +4.427540852e-04f, +3.758927218e-04f, +2.937124231e-04f, +2.096144489e-04f, +1.344390595e-04f, +7.504350274e-05f,
- /* 24,12 */ -5.220438912e-05f, -1.025131319e-04f, -1.703799499e-04f, -2.511439643e-04f, -3.358432542e-04f, -4.121175966e-04f, -4.661040260e-04f, -4.850099279e-04f, -4.598320457e-04f, -3.875941701e-04f, -2.725379532e-04f, -1.259145254e-04f, +3.565926997e-05f, +1.929766610e-04f, +3.275711800e-04f, +4.248879309e-04f, +4.766215175e-04f, +4.817496625e-04f, +4.461048161e-04f, +3.806663042e-04f, +2.990350629e-04f, +2.147035314e-04f, +1.387241832e-04f, +7.822510242e-05f,
- /* 24,13 */ -4.967415993e-05f, -9.882966748e-05f, -1.656897170e-04f, -2.458678944e-04f, -3.306557574e-04f, -4.078592000e-04f, -4.636385032e-04f, -4.850529052e-04f, -4.627886263e-04f, -3.934446700e-04f, -2.808022583e-04f, -1.357085413e-04f, +2.548296987e-05f, +1.836320864e-04f, +3.201278616e-04f, +4.200908527e-04f, +4.747797448e-04f, +4.827146831e-04f, +4.493383067e-04f, +3.853766873e-04f, +3.043479843e-04f, +2.198265592e-04f, +1.430712350e-04f, +8.147924507e-05f,
- /* 24,14 */ -4.721513201e-05f, -9.521673594e-05f, -1.610529558e-04f, -2.406110065e-04f, -3.254391996e-04f, -4.035168325e-04f, -4.610375470e-04f, -4.849233000e-04f, -4.655589110e-04f, -3.991236794e-04f, -2.889379628e-04f, -1.454387449e-04f, +1.529460870e-05f, +1.742016828e-04f, +3.125397891e-04f, +4.151145025e-04f, +4.727530367e-04f, +4.835165803e-04f, +4.524514078e-04f, +3.900204007e-04f, +3.096480504e-04f, +2.249812225e-04f, +1.474789784e-04f, +8.480575132e-05f,
- /* 24,15 */ -4.482688286e-05f, -9.167483068e-05f, -1.564712483e-04f, -2.353759082e-04f, -3.201968846e-04f, -3.990939388e-04f, -4.583040637e-04f, -4.846228261e-04f, -4.681429482e-04f, -4.046294158e-04f, -2.969416536e-04f, -1.551006323e-04f, +5.099007530e-06f, +1.646897470e-04f, +3.048100066e-04f, +4.099602091e-04f, +4.705408991e-04f, +4.841532882e-04f, +4.554410135e-04f, +3.945939739e-04f, +3.149320871e-04f, +2.301651496e-04f, +1.519461079e-04f, +8.820438069e-05f,
- /* 24, 0 */ +3.721332452e-05f, -8.727351622e-06f, -1.260052743e-04f, -3.262895896e-04f, -5.956603662e-04f, -8.899501259e-04f, -1.140781305e-03f, -1.272347980e-03f, -1.224469676e-03f, -9.741728935e-04f, -5.476309302e-04f, -1.718639697e-05f, +5.165697336e-04f, +9.521355524e-04f, +1.214742061e-03f, +1.275075958e-03f, +1.153251370e-03f, +9.076938752e-04f, +6.139361451e-04f, +3.414081512e-04f, +1.360881127e-04f, +1.374767685e-05f, -3.597568203e-05f, -3.836441874e-05f,
- /* 24, 1 */ +3.827272022e-05f, -3.990309212e-06f, -1.162552839e-04f, -3.114511951e-04f, -5.774902753e-04f, -8.720393210e-04f, -1.127840237e-03f, -1.268906542e-03f, -1.233389975e-03f, -9.955061195e-04f, -5.782766642e-04f, -5.154594549e-05f, +4.851159022e-04f, +9.294071718e-04f, +1.204207601e-03f, +1.277079499e-03f, +1.165232472e-03f, +9.252515980e-04f, +6.323029482e-04f, +3.567995352e-04f, +1.465038861e-04f, +1.905656402e-05f, -3.455261727e-05f, -3.906074609e-05f,
- /* 24, 2 */ +3.916105537e-05f, +4.689475139e-07f, -1.068376458e-04f, -2.968998221e-04f, -5.594401371e-04f, -8.539803004e-04f, -1.114446367e-03f, -1.264763218e-03f, -1.241503276e-03f, -1.016122900e-03f, -6.084845647e-04f, -8.586577249e-05f, +4.532926958e-04f, +9.060015608e-04f, +1.192867560e-03f, +1.278348235e-03f, +1.176706916e-03f, +9.426042142e-04f, +6.507457252e-04f, +3.724559064e-04f, +1.572522637e-04f, +2.465906089e-05f, -3.293697730e-05f, -3.967556907e-05f,
- /* 24, 3 */ +3.988551544e-05f, +4.656120273e-06f, -9.775146571e-05f, -2.826418349e-04f, -5.415238064e-04f, -8.357917522e-04f, -1.100618114e-03f, -1.259930153e-03f, -1.248810685e-03f, -1.036011659e-03f, -6.382327409e-04f, -1.201194461e-04f, +4.211237814e-04f, +8.819332498e-04f, +1.180724004e-03f, +1.278872430e-03f, +1.187657300e-03f, +9.597325558e-04f, +6.692490513e-04f, +3.883689407e-04f, +1.683324883e-04f, +3.055997058e-05f, -3.112164378e-05f, -4.020250110e-05f,
- /* 24, 4 */ +4.045327387e-05f, +8.577101915e-06f, -8.899546026e-05f, -2.686831091e-04f, -5.237547173e-04f, -8.174921923e-04f, -1.086374092e-03f, -1.254420050e-03f, -1.255314082e-03f, -1.055161588e-03f, -6.674998060e-04f, -1.542806079e-04f, +3.886332072e-04f, +8.572174771e-04f, +1.167779798e-03f, +1.278642989e-03f, +1.198066533e-03f, +9.766173891e-04f, +6.877971412e-04f, +4.045298263e-04f, +1.797433681e-04f, +3.676383839e-05f, -2.909954521e-05f, -4.063504078e-05f,
- /* 24, 5 */ +4.087148106e-05f, +1.223796294e-05f, -8.056796775e-05f, -2.550290329e-04f, -5.061458738e-04f, -7.990999447e-04f, -1.071733083e-03f, -1.248246148e-03f, -1.261016119e-03f, -1.073562648e-03f, -6.962648992e-04f, -1.883230024e-04f, +3.558453770e-04f, +8.318701747e-04f, +1.154038613e-03f, +1.277651484e-03f, +1.207917863e-03f, +9.932394374e-04f, +7.063738625e-04f, +4.209292660e-04f, +1.914832687e-04f, +4.327493877e-05f, -2.686366939e-05f, -4.096657950e-05f,
- /* 24, 6 */ +4.114725367e-05f, +1.564493806e-05f, -7.246695886e-05f, -2.416845105e-04f, -4.887098400e-04f, -7.806331214e-04f, -1.056714015e-03f, -1.241422199e-03f, -1.265920211e-03f, -1.091205584e-03f, -7.245077077e-04f, -2.222205061e-04f, +3.227850234e-04f, +8.059079530e-04f, +1.139504920e-03f, +1.275890161e-03f, +1.217194897e-03f, +1.009579403e-03f, +7.249627509e-04f, +4.375574807e-04f, +2.035501057e-04f, +5.009726244e-05f, -2.440707607e-05f, -4.119040933e-05f,
- /* 24, 7 */ +4.128766430e-05f, +1.880441272e-05f, -6.469004778e-05f, -2.286539641e-04f, -4.714587328e-04f, -7.621096041e-04f, -1.041335936e-03f, -1.233962452e-03f, -1.270030522e-03f, -1.108081926e-03f, -7.522084876e-04f, -2.559471563e-04f, +2.894771803e-04f, +7.793480846e-04f, +1.124183998e-03f, +1.273351959e-03f, +1.225881627e-03f, +1.025617992e-03f, +7.435470253e-04f, +4.544042133e-04f, +2.159413387e-04f, +5.723450367e-05f, -2.172290976e-05f, -4.129973134e-05f,
- /* 24, 8 */ +4.129973134e-05f, +2.172290976e-05f, -5.723450367e-05f, -2.159413387e-04f, -4.544042133e-04f, -7.435470253e-04f, -1.025617992e-03f, -1.225881627e-03f, -1.273351959e-03f, -1.124183998e-03f, -7.793480846e-04f, -2.894771803e-04f, +2.559471563e-04f, +7.522084876e-04f, +1.108081926e-03f, +1.270030522e-03f, +1.233962452e-03f, +1.041335936e-03f, +7.621096041e-04f, +4.714587328e-04f, +2.286539641e-04f, +6.469004778e-05f, -1.880441272e-05f, -4.128766430e-05f,
- /* 24, 9 */ +4.119040933e-05f, +2.440707607e-05f, -5.009726244e-05f, -2.035501057e-04f, -4.375574807e-04f, -7.249627509e-04f, -1.009579403e-03f, -1.217194897e-03f, -1.275890161e-03f, -1.139504920e-03f, -8.059079530e-04f, -3.227850234e-04f, +2.222205061e-04f, +7.245077077e-04f, +1.091205584e-03f, +1.265920211e-03f, +1.241422199e-03f, +1.056714015e-03f, +7.806331214e-04f, +4.887098400e-04f, +2.416845105e-04f, +7.246695886e-05f, -1.564493806e-05f, -4.114725367e-05f,
- /* 24,10 */ +4.096657950e-05f, +2.686366939e-05f, -4.327493877e-05f, -1.914832687e-04f, -4.209292660e-04f, -7.063738625e-04f, -9.932394374e-04f, -1.207917863e-03f, -1.277651484e-03f, -1.154038613e-03f, -8.318701747e-04f, -3.558453770e-04f, +1.883230024e-04f, +6.962648992e-04f, +1.073562648e-03f, +1.261016119e-03f, +1.248246148e-03f, +1.071733083e-03f, +7.990999447e-04f, +5.061458738e-04f, +2.550290329e-04f, +8.056796775e-05f, -1.223796294e-05f, -4.087148106e-05f,
- /* 24,11 */ +4.063504078e-05f, +2.909954521e-05f, -3.676383839e-05f, -1.797433681e-04f, -4.045298263e-04f, -6.877971412e-04f, -9.766173891e-04f, -1.198066533e-03f, -1.278642989e-03f, -1.167779798e-03f, -8.572174771e-04f, -3.886332072e-04f, +1.542806079e-04f, +6.674998060e-04f, +1.055161588e-03f, +1.255314082e-03f, +1.254420050e-03f, +1.086374092e-03f, +8.174921923e-04f, +5.237547173e-04f, +2.686831091e-04f, +8.899546026e-05f, -8.577101915e-06f, -4.045327387e-05f,
- /* 24,12 */ +4.020250110e-05f, +3.112164378e-05f, -3.055997058e-05f, -1.683324883e-04f, -3.883689407e-04f, -6.692490513e-04f, -9.597325558e-04f, -1.187657300e-03f, -1.278872430e-03f, -1.180724004e-03f, -8.819332498e-04f, -4.211237814e-04f, +1.201194461e-04f, +6.382327409e-04f, +1.036011659e-03f, +1.248810685e-03f, +1.259930153e-03f, +1.100618114e-03f, +8.357917522e-04f, +5.415238064e-04f, +2.826418349e-04f, +9.775146571e-05f, -4.656120273e-06f, -3.988551544e-05f,
- /* 24,13 */ +3.967556907e-05f, +3.293697730e-05f, -2.465906089e-05f, -1.572522637e-04f, -3.724559064e-04f, -6.507457252e-04f, -9.426042142e-04f, -1.176706916e-03f, -1.278348235e-03f, -1.192867560e-03f, -9.060015608e-04f, -4.532926958e-04f, +8.586577249e-05f, +6.084845647e-04f, +1.016122900e-03f, +1.241503276e-03f, +1.264763218e-03f, +1.114446367e-03f, +8.539803004e-04f, +5.594401371e-04f, +2.968998221e-04f, +1.068376458e-04f, -4.689475139e-07f, -3.916105537e-05f,
- /* 24,14 */ +3.906074609e-05f, +3.455261727e-05f, -1.905656402e-05f, -1.465038861e-04f, -3.567995352e-04f, -6.323029482e-04f, -9.252515980e-04f, -1.165232472e-03f, -1.277079499e-03f, -1.204207601e-03f, -9.294071718e-04f, -4.851159022e-04f, +5.154594549e-05f, +5.782766642e-04f, +9.955061195e-04f, +1.233389975e-03f, +1.268906542e-03f, +1.127840237e-03f, +8.720393210e-04f, +5.774902753e-04f, +3.114511951e-04f, +1.162552839e-04f, +3.990309212e-06f, -3.827272022e-05f,
- /* 24,15 */ +3.836441874e-05f, +3.597568203e-05f, -1.374767685e-05f, -1.360881127e-04f, -3.414081512e-04f, -6.139361451e-04f, -9.076938752e-04f, -1.153251370e-03f, -1.275075958e-03f, -1.214742061e-03f, -9.521355524e-04f, -5.165697336e-04f, +1.718639697e-05f, +5.476309302e-04f, +9.741728935e-04f, +1.224469676e-03f, +1.272347980e-03f, +1.140781305e-03f, +8.899501259e-04f, +5.956603662e-04f, +3.262895896e-04f, +1.260052743e-04f, +8.727351622e-06f, -3.721332452e-05f,
- /* 24, 0 */ +8.266384897e-05f, +1.864042294e-04f, +2.488885336e-04f, +1.546439211e-04f, -1.995837972e-04f, -8.300120177e-04f, -1.613160849e-03f, -2.296673715e-03f, -2.585717258e-03f, -2.273475621e-03f, -1.352242686e-03f, -4.324968723e-05f, +1.278412578e-03f, +2.232544293e-03f, +2.585064833e-03f, +2.329165788e-03f, +1.661894649e-03f, +8.765619362e-04f, +2.314166150e-04f, -1.408802900e-04f, -2.488728147e-04f, -1.925779863e-04f, -8.867605644e-05f, -1.381647235e-05f,
- /* 24, 1 */ +7.679604466e-05f, +1.800850086e-04f, +2.482891228e-04f, +1.673628145e-04f, -1.688781476e-04f, -7.841040553e-04f, -1.564053778e-03f, -2.262611362e-03f, -2.583952550e-03f, -2.311948888e-03f, -1.424504725e-03f, -1.296977982e-04f, +1.203096102e-03f, +2.189184288e-03f, +2.581965725e-03f, +2.360018674e-03f, +1.710180642e-03f, +9.237037585e-04f, +2.643640884e-04f, -1.260534627e-04f, -2.482108983e-04f, -1.985807152e-04f, -9.482163577e-05f, -1.700840565e-05f,
- /* 24, 2 */ +7.108272573e-05f, +1.736451253e-04f, +2.471057735e-04f, +1.790568131e-04f, -1.393098721e-04f, -7.388859215e-04f, -1.514647192e-03f, -2.227049028e-03f, -2.579803518e-03f, -2.347938498e-03f, -1.495119547e-03f, -2.159922036e-04f, +1.126377386e-03f, +2.143428746e-03f, +2.576393731e-03f, +2.389164999e-03f, +1.757943642e-03f, +9.713853048e-04f, +2.984113695e-04f, -1.101464409e-04f, -2.468719141e-04f, -2.043861254e-04f, -1.010885985e-04f, -2.040687624e-05f,
- /* 24, 3 */ +6.553303557e-05f, +1.671085856e-04f, +2.453697283e-04f, +1.897470664e-04f, -1.108869031e-04f, -6.944032625e-04f, -1.465013955e-03f, -2.190058265e-03f, -2.573306150e-03f, -2.381422658e-03f, -1.564010684e-03f, -3.020307159e-04f, +1.048342851e-03f, +2.095314540e-03f, +2.568326065e-03f, +2.416539054e-03f, +1.805107932e-03f, +1.019552324e-03f, +3.335412514e-04f, -9.314376077e-05f, -2.448252646e-04f, -2.099672379e-04f, -1.074639934e-04f, -2.401251277e-05f,
- /* 24, 4 */ +6.015519126e-05f, +1.604985702e-04f, +2.431122111e-04f, +1.994559526e-04f, -8.361493575e-05f, -6.506994570e-04f, -1.415225919e-03f, -2.151711738e-03f, -2.564499518e-03f, -2.412383397e-03f, -1.631104459e-03f, -3.877115714e-04f, +9.690810727e-04f, +2.044882222e-03f, +2.557743429e-03f, +2.442076932e-03f, +1.851597394e-03f, +1.068148557e-03f, +3.697341485e-04f, -7.503156587e-05f, -2.420407013e-04f, -2.152964269e-04f, -1.139339030e-04f, -2.782526914e-05f,
- /* 24, 5 */ +5.495649810e-05f, +1.538374078e-04f, +2.403643576e-04f, +2.082069988e-04f, -5.749746926e-05f, -6.078155802e-04f, -1.365353811e-03f, -2.112083086e-03f, -2.553425683e-03f, -2.440806561e-03f, -1.696330106e-03f, -4.729335981e-04f, +8.886826408e-04f, +1.992175989e-03f, +2.544630075e-03f, +2.465716661e-03f, +1.897335642e-03f, +1.117115802e-03f, +4.069680813e-04f, -5.579767803e-05f, -2.384884029e-04f, -2.203454641e-04f, -1.204834430e-04f, -3.184439496e-05f,
- /* 24, 6 */ +4.994336610e-05f, +1.471465506e-04f, +2.371571498e-04f, +2.160248014e-04f, -3.253585139e-05f, -5.657903730e-04f, -1.315467130e-03f, -2.071246779e-03f, -2.540129593e-03f, -2.466681818e-03f, -1.759619871e-03f, -5.575963834e-04f, +8.072400133e-04f, +1.937243618e-03f, +2.528973864e-03f, +2.487398336e-03f, +1.942246152e-03f, +1.166393990e-03f, +4.452186667e-04f, -3.543166589e-05f, -2.341390536e-04f, -2.250855657e-04f, -1.270967638e-04f, -3.606840695e-05f,
- /* 24, 7 */ +4.512132841e-05f, +1.404465535e-04f, +2.335213506e-04f, +2.229349451e-04f, -8.729326764e-06f, -5.246602166e-04f, -1.265634037e-03f, -2.029277978e-03f, -2.524658979e-03f, -2.490002646e-03f, -1.820909115e-03f, -6.416004392e-04f, +7.248473657e-04f, +1.880136408e-03f, +2.510766314e-03f, +2.507064240e-03f, +1.986252395e-03f, +1.215921259e-03f, +4.844591124e-04f, -1.392491133e-05f, -2.289639228e-04f, -2.294874421e-04f, -1.337570553e-04f, -4.049506149e-05f,
- /* 24, 8 */ +4.049506149e-05f, +1.337570553e-04f, +2.294874421e-04f, +2.289639228e-04f, +1.392491133e-05f, -4.844591124e-04f, -1.215921259e-03f, -1.986252395e-03f, -2.507064240e-03f, -2.510766314e-03f, -1.880136408e-03f, -7.248473657e-04f, +6.416004392e-04f, +1.820909115e-03f, +2.490002646e-03f, +2.524658979e-03f, +2.029277978e-03f, +1.265634037e-03f, +5.246602166e-04f, +8.729326764e-06f, -2.229349451e-04f, -2.335213506e-04f, -1.404465535e-04f, -4.512132841e-05f,
- /* 24, 9 */ +3.606840695e-05f, +1.270967638e-04f, +2.250855657e-04f, +2.341390536e-04f, +3.543166589e-05f, -4.452186667e-04f, -1.166393990e-03f, -1.942246152e-03f, -2.487398336e-03f, -2.528973864e-03f, -1.937243618e-03f, -8.072400133e-04f, +5.575963834e-04f, +1.759619871e-03f, +2.466681818e-03f, +2.540129593e-03f, +2.071246779e-03f, +1.315467130e-03f, +5.657903730e-04f, +3.253585139e-05f, -2.160248014e-04f, -2.371571498e-04f, -1.471465506e-04f, -4.994336610e-05f,
- /* 24,10 */ +3.184439496e-05f, +1.204834430e-04f, +2.203454641e-04f, +2.384884029e-04f, +5.579767803e-05f, -4.069680813e-04f, -1.117115802e-03f, -1.897335642e-03f, -2.465716661e-03f, -2.544630075e-03f, -1.992175989e-03f, -8.886826408e-04f, +4.729335981e-04f, +1.696330106e-03f, +2.440806561e-03f, +2.553425683e-03f, +2.112083086e-03f, +1.365353811e-03f, +6.078155802e-04f, +5.749746926e-05f, -2.082069988e-04f, -2.403643576e-04f, -1.538374078e-04f, -5.495649810e-05f,
- /* 24,11 */ +2.782526914e-05f, +1.139339030e-04f, +2.152964269e-04f, +2.420407013e-04f, +7.503156587e-05f, -3.697341485e-04f, -1.068148557e-03f, -1.851597394e-03f, -2.442076932e-03f, -2.557743429e-03f, -2.044882222e-03f, -9.690810727e-04f, +3.877115714e-04f, +1.631104459e-03f, +2.412383397e-03f, +2.564499518e-03f, +2.151711738e-03f, +1.415225919e-03f, +6.506994570e-04f, +8.361493575e-05f, -1.994559526e-04f, -2.431122111e-04f, -1.604985702e-04f, -6.015519126e-05f,
- /* 24,12 */ +2.401251277e-05f, +1.074639934e-04f, +2.099672379e-04f, +2.448252646e-04f, +9.314376077e-05f, -3.335412514e-04f, -1.019552324e-03f, -1.805107932e-03f, -2.416539054e-03f, -2.568326065e-03f, -2.095314540e-03f, -1.048342851e-03f, +3.020307159e-04f, +1.564010684e-03f, +2.381422658e-03f, +2.573306150e-03f, +2.190058265e-03f, +1.465013955e-03f, +6.944032625e-04f, +1.108869031e-04f, -1.897470664e-04f, -2.453697283e-04f, -1.671085856e-04f, -6.553303557e-05f,
- /* 24,13 */ +2.040687624e-05f, +1.010885985e-04f, +2.043861254e-04f, +2.468719141e-04f, +1.101464409e-04f, -2.984113695e-04f, -9.713853048e-04f, -1.757943642e-03f, -2.389164999e-03f, -2.576393731e-03f, -2.143428746e-03f, -1.126377386e-03f, +2.159922036e-04f, +1.495119547e-03f, +2.347938498e-03f, +2.579803518e-03f, +2.227049028e-03f, +1.514647192e-03f, +7.388859215e-04f, +1.393098721e-04f, -1.790568131e-04f, -2.471057735e-04f, -1.736451253e-04f, -7.108272573e-05f,
- /* 24,14 */ +1.700840565e-05f, +9.482163577e-05f, +1.985807152e-04f, +2.482108983e-04f, +1.260534627e-04f, -2.643640884e-04f, -9.237037585e-04f, -1.710180642e-03f, -2.360018674e-03f, -2.581965725e-03f, -2.189184288e-03f, -1.203096102e-03f, +1.296977982e-04f, +1.424504725e-03f, +2.311948888e-03f, +2.583952550e-03f, +2.262611362e-03f, +1.564053778e-03f, +7.841040553e-04f, +1.688781476e-04f, -1.673628145e-04f, -2.482891228e-04f, -1.800850086e-04f, -7.679604466e-05f,
- /* 24,15 */ +1.381647235e-05f, +8.867605644e-05f, +1.925779863e-04f, +2.488728147e-04f, +1.408802900e-04f, -2.314166150e-04f, -8.765619362e-04f, -1.661894649e-03f, -2.329165788e-03f, -2.585064833e-03f, -2.232544293e-03f, -1.278412578e-03f, +4.324968723e-05f, +1.352242686e-03f, +2.273475621e-03f, +2.585717258e-03f, +2.296673715e-03f, +1.613160849e-03f, +8.300120177e-04f, +1.995837972e-04f, -1.546439211e-04f, -2.488885336e-04f, -1.864042294e-04f, -8.266384897e-05f,
- /* 24, 0 */ -8.756118778e-05f, -1.009631262e-05f, +2.499923290e-04f, +5.877223422e-04f, +6.788717735e-04f, +1.353208099e-04f, -1.181609893e-03f, -2.907631270e-03f, -4.227440709e-03f, -4.289302846e-03f, -2.753030129e-03f, -9.027467135e-05f, +2.610460208e-03f, +4.239433597e-03f, +4.275304929e-03f, +3.011836329e-03f, +1.284225967e-03f, -7.470693818e-05f, -6.668983668e-04f, -6.049037547e-04f, -2.711811033e-04f, -7.712041122e-07f, +8.724954076e-05f, +5.404595280e-05f,
- /* 24, 1 */ -8.741655630e-05f, -2.019027119e-05f, +2.291878545e-04f, +5.696067266e-04f, +6.882827247e-04f, +1.927359117e-04f, -1.080756061e-03f, -2.801858302e-03f, -4.174485032e-03f, -4.332641998e-03f, -2.890980043e-03f, -2.706680808e-04f, +2.463494124e-03f, +4.183052585e-03f, +4.317905196e-03f, +3.114235078e-03f, +1.388440877e-03f, -1.091717027e-05f, -6.522789212e-04f, -6.210469572e-04f, -2.926973166e-04f, -1.241413728e-05f, +8.645224618e-05f, +5.751117153e-05f,
- /* 24, 2 */ -8.684540791e-05f, -2.951540942e-05f, +2.088206066e-04f, +5.506594457e-04f, +6.952187414e-04f, +2.469379129e-04f, -9.818199274e-04f, -2.694754852e-03f, -4.116618896e-03f, -4.369446535e-03f, -3.024096686e-03f, -4.505940556e-04f, +2.312365817e-03f, +4.120192033e-03f, +4.355078033e-03f, +3.214588722e-03f, +1.494083528e-03f, +5.601692583e-05f, -6.349341530e-04f, -6.360467505e-04f, -3.144802134e-04f, -2.483132916e-05f, +8.514047439e-05f, +6.091502927e-05f,
- /* 24, 3 */ -8.587775422e-05f, -3.807920270e-05f, +1.889395528e-04f, +5.309813040e-04f, +6.997709178e-04f, +2.979208532e-04f, -8.849487633e-04f, -2.586556795e-03f, -4.054031257e-03f, -4.399725659e-03f, -3.152177828e-03f, -6.297421881e-04f, +2.157318805e-03f, +4.050898074e-03f, +4.386669491e-03f, +3.312658663e-03f, +1.600975431e-03f, +1.260549593e-04f, -6.147894349e-04f, -6.497970322e-04f, -3.364651980e-04f, -3.801847602e-05f, +8.328608571e-05f, +6.423360450e-05f,
- /* 24, 4 */ -8.454371894e-05f, -4.589171696e-05f, +1.695896910e-04f, +5.106711213e-04f, +7.020335552e-04f, +3.456869501e-04f, -7.902814402e-04f, -2.477497901e-03f, -3.986918477e-03f, -4.423502152e-03f, -3.275032702e-03f, -8.078038906e-04f, +1.998605647e-03f, +3.975230752e-03f, +4.412535626e-03f, +3.408207107e-03f, +1.708931018e-03f, +1.991476106e-04f, -5.917751493e-04f, -6.621910968e-04f, -3.585839047e-04f, -5.196800512e-05f, +8.086178430e-05f, +6.744196867e-05f,
- /* 24, 5 */ -8.287340509e-05f, -5.296545744e-05f, +1.508120534e-04f, +4.898254962e-04f, +7.021037953e-04f, +3.902463858e-04f, -6.979482496e-04f, -2.367809282e-03f, -3.915483754e-03f, -4.440812209e-03f, -3.392482395e-03f, -9.844731162e-04f, +1.836487379e-03f, +3.893263974e-03f, +4.432542967e-03f, +3.500997660e-03f, +1.817757983e-03f, +2.752365501e-04f, -5.658270331e-04f, -6.731219472e-04f, -3.807642824e-04f, -6.666895953e-05f, +7.784127492e-05f, +7.051425394e-05f,
- /* 24, 6 */ -8.089676755e-05f, -5.931521393e-05f, +1.326437227e-04f, +4.685385820e-04f, +7.000812546e-04f, +4.316170765e-04f, -6.080707472e-04f, -2.257718867e-03f, -3.839936544e-03f, -4.451705229e-03f, -3.504360214e-03f, -1.159447072e-03f, +1.671232927e-03f, +3.805085432e-03f, +4.446568950e-03f, +3.590795947e-03f, +1.927257648e-03f, +3.542543807e-04f, -5.368865161e-04f, -6.824826149e-04f, -4.029306956e-04f, -8.210689127e-05f, +7.419942176e-05f, +7.342372810e-05f,
- /* 24, 7 */ -7.864349144e-05f, -6.495790319e-05f, +1.151178633e-04f, +4.469018792e-04f, +6.960676605e-04f, +4.698244236e-04f, -5.207616239e-04f, -2.147450881e-03f, -3.760491970e-03f, -4.456243581e-03f, -3.610512013e-03f, -1.332426925e-03f, +1.503118492e-03f, +3.710796487e-03f, +4.454502333e-03f, +3.677370219e-03f, +2.037225356e-03f, +4.361246060e-04f, -5.049010493e-04f, -6.901664903e-04f, -4.250040411e-04f, -9.826376366e-05f, +6.991240915e-05f, +7.614287656e-05f,
- /* 24, 8 */ -7.614287656e-05f, -6.991240915e-05f, +9.826376366e-05f, +4.250040411e-04f, +6.901664903e-04f, +5.049010493e-04f, -4.361246060e-04f, -2.037225356e-03f, -3.677370219e-03f, -4.454502333e-03f, -3.710796487e-03f, -1.503118492e-03f, +1.332426925e-03f, +3.610512013e-03f, +4.456243581e-03f, +3.760491970e-03f, +2.147450881e-03f, +5.207616239e-04f, -4.698244236e-04f, -6.960676605e-04f, -4.469018792e-04f, -1.151178633e-04f, +6.495790319e-05f, +7.864349144e-05f,
- /* 24, 9 */ -7.342372810e-05f, -7.419942176e-05f, +8.210689127e-05f, +4.029306956e-04f, +6.824826149e-04f, +5.368865161e-04f, -3.542543807e-04f, -1.927257648e-03f, -3.590795947e-03f, -4.446568950e-03f, -3.805085432e-03f, -1.671232927e-03f, +1.159447072e-03f, +3.504360214e-03f, +4.451705229e-03f, +3.839936544e-03f, +2.257718867e-03f, +6.080707472e-04f, -4.316170765e-04f, -7.000812546e-04f, -4.685385820e-04f, -1.326437227e-04f, +5.931521393e-05f, +8.089676755e-05f,
- /* 24,10 */ -7.051425394e-05f, -7.784127492e-05f, +6.666895953e-05f, +3.807642824e-04f, +6.731219472e-04f, +5.658270331e-04f, -2.752365501e-04f, -1.817757983e-03f, -3.500997660e-03f, -4.432542967e-03f, -3.893263974e-03f, -1.836487379e-03f, +9.844731162e-04f, +3.392482395e-03f, +4.440812209e-03f, +3.915483754e-03f, +2.367809282e-03f, +6.979482496e-04f, -3.902463858e-04f, -7.021037953e-04f, -4.898254962e-04f, -1.508120534e-04f, +5.296545744e-05f, +8.287340509e-05f,
- /* 24,11 */ -6.744196867e-05f, -8.086178430e-05f, +5.196800512e-05f, +3.585839047e-04f, +6.621910968e-04f, +5.917751493e-04f, -1.991476106e-04f, -1.708931018e-03f, -3.408207107e-03f, -4.412535626e-03f, -3.975230752e-03f, -1.998605647e-03f, +8.078038906e-04f, +3.275032702e-03f, +4.423502152e-03f, +3.986918477e-03f, +2.477497901e-03f, +7.902814402e-04f, -3.456869501e-04f, -7.020335552e-04f, -5.106711213e-04f, -1.695896910e-04f, +4.589171696e-05f, +8.454371894e-05f,
- /* 24,12 */ -6.423360450e-05f, -8.328608571e-05f, +3.801847602e-05f, +3.364651980e-04f, +6.497970322e-04f, +6.147894349e-04f, -1.260549593e-04f, -1.600975431e-03f, -3.312658663e-03f, -4.386669491e-03f, -4.050898074e-03f, -2.157318805e-03f, +6.297421881e-04f, +3.152177828e-03f, +4.399725659e-03f, +4.054031257e-03f, +2.586556795e-03f, +8.849487633e-04f, -2.979208532e-04f, -6.997709178e-04f, -5.309813040e-04f, -1.889395528e-04f, +3.807920270e-05f, +8.587775422e-05f,
- /* 24,13 */ -6.091502927e-05f, -8.514047439e-05f, +2.483132916e-05f, +3.144802134e-04f, +6.360467505e-04f, +6.349341530e-04f, -5.601692583e-05f, -1.494083528e-03f, -3.214588722e-03f, -4.355078033e-03f, -4.120192033e-03f, -2.312365817e-03f, +4.505940556e-04f, +3.024096686e-03f, +4.369446535e-03f, +4.116618896e-03f, +2.694754852e-03f, +9.818199274e-04f, -2.469379129e-04f, -6.952187414e-04f, -5.506594457e-04f, -2.088206066e-04f, +2.951540942e-05f, +8.684540791e-05f,
- /* 24,14 */ -5.751117153e-05f, -8.645224618e-05f, +1.241413728e-05f, +2.926973166e-04f, +6.210469572e-04f, +6.522789212e-04f, +1.091717027e-05f, -1.388440877e-03f, -3.114235078e-03f, -4.317905196e-03f, -4.183052585e-03f, -2.463494124e-03f, +2.706680808e-04f, +2.890980043e-03f, +4.332641998e-03f, +4.174485032e-03f, +2.801858302e-03f, +1.080756061e-03f, -1.927359117e-04f, -6.882827247e-04f, -5.696067266e-04f, -2.291878545e-04f, +2.019027119e-05f, +8.741655630e-05f,
- /* 24,15 */ -5.404595280e-05f, -8.724954076e-05f, +7.712041122e-07f, +2.711811033e-04f, +6.049037547e-04f, +6.668983668e-04f, +7.470693818e-05f, -1.284225967e-03f, -3.011836329e-03f, -4.275304929e-03f, -4.239433597e-03f, -2.610460208e-03f, +9.027467135e-05f, +2.753030129e-03f, +4.289302846e-03f, +4.227440709e-03f, +2.907631270e-03f, +1.181609893e-03f, -1.353208099e-04f, -6.788717735e-04f, -5.877223422e-04f, -2.499923290e-04f, +1.009631262e-05f, +8.756118778e-05f,
- /* 24, 0 */ -4.836862817e-05f, -2.381906908e-04f, -2.861422699e-04f, +1.419765781e-04f, +9.779307384e-04f, +1.431118485e-03f, +4.239072727e-04f, -2.320049614e-03f, -5.516524807e-03f, -6.885468951e-03f, -4.882970050e-03f, -1.652445539e-04f, +4.647640808e-03f, +6.864975932e-03f, +5.679465803e-03f, +2.528057977e-03f, -2.982544427e-04f, -1.420326139e-03f, -1.029081461e-03f, -1.868092348e-04f, +2.758007186e-04f, +2.491702023e-04f, +5.836581816e-05f, -3.491105347e-05f,
- /* 24, 1 */ -3.887878147e-05f, -2.267040256e-04f, -2.944876029e-04f, +9.900949096e-05f, +9.254138922e-04f, +1.435932770e-03f, +5.422031866e-04f, -2.114193296e-03f, -5.346195092e-03f, -6.891993065e-03f, -5.106971374e-03f, -4.953359135e-04f, +4.401480442e-03f, +6.830374341e-03f, +5.834433801e-03f, +2.737690485e-03f, -1.653667139e-04f, -1.403322383e-03f, -1.078579553e-03f, -2.333982604e-04f, +2.633988510e-04f, +2.595470071e-04f, +6.884149383e-05f, -3.317859772e-05f,
- /* 24, 2 */ -2.992041863e-05f, -2.148033017e-04f, -3.009073041e-04f, +5.800387728e-05f, +8.718108917e-04f, +1.435015360e-03f, +6.530479478e-04f, -1.910998057e-03f, -5.169072824e-03f, -6.884726614e-03f, -5.319183002e-03f, -8.242352929e-04f, +4.145019341e-03f, +6.781564023e-03f, +5.980858542e-03f, +2.948401826e-03f, -2.539414214e-05f, -1.379888189e-03f, -1.126132932e-03f, -2.816211011e-04f, +2.488796810e-04f, +2.692234993e-04f, +7.976200316e-05f, -3.095924021e-05f,
- /* 24, 3 */ -2.151306397e-05f, -2.025787804e-04f, -3.054778181e-04f, +1.904245883e-05f, +8.173942695e-04f, +1.428624316e-03f, +7.563747429e-04f, -1.710952703e-03f, -4.985763915e-03f, -6.863885651e-03f, -5.519179462e-03f, -1.151152247e-03f, +3.878819947e-03f, +6.718485032e-03f, +6.118185890e-03f, +3.159630822e-03f, +1.214850236e-04f, -1.349820125e-03f, -1.171444944e-03f, -3.313418525e-04f, +2.321939470e-04f, +2.781004545e-04f, +9.108884721e-05f, -2.823129406e-05f,
- /* 24, 4 */ -1.367174826e-05f, -1.901175448e-04f, -3.082808136e-04f, -1.780505549e-05f, +7.624282793e-04f, +1.417028061e-03f, +8.521437270e-04f, -1.514524553e-03f, -4.796881865e-03f, -6.829722854e-03f, -5.706572744e-03f, -1.475302628e-03f, +3.603475092e-03f, +6.641118197e-03f, +6.245879909e-03f, +3.370802059e-03f, +2.750642929e-04f, -1.312931609e-03f, -1.214215433e-03f, -3.824113238e-04f, +2.133007109e-04f, +2.860774924e-04f, +1.027786538e-04f, -2.497524656e-05f,
- /* 24, 5 */ -6.407151783e-06f, -1.775031866e-04f, -3.094025487e-04f, -5.248174754e-05f, +7.071681142e-04f, +1.400504044e-03f, +9.403414758e-04f, -1.322158275e-03f, -4.603045619e-03f, -6.782526316e-03f, -5.881013326e-03f, -1.795911068e-03f, +3.319606230e-03f, +6.549485546e-03f, +6.363424898e-03f, +3.581327596e-03f, +4.351089927e-04f, -1.269054120e-03f, -1.254141844e-03f, -4.346671648e-04f, +1.921679399e-04f, +2.930535649e-04f, +1.147831783e-04f, -2.117401174e-05f,
- /* 24, 6 */ +2.742338831e-07f, -1.648155254e-04f, -3.089332390e-04f, -8.494321776e-05f, +6.518591895e-04f, +1.379337399e-03f, +1.020980349e-03f, -1.134274832e-03f, -4.404877423e-03f, -6.722618231e-03f, -6.042191052e-03f, -2.112213435e-03f, +3.027861551e-03f, +6.443650588e-03f, +6.470327373e-03f, +3.790608755e-03f, +6.013564365e-04f, -1.218038367e-03f, -1.290920380e-03f, -4.879340571e-04f, +1.687730668e-04f, +2.989274696e-04f, +1.270493337e-04f, -1.681317980e-05f,
- /* 24, 7 */ +6.369927035e-06f, -1.521303593e-04f, -3.069664313e-04f, -1.151572658e-04f, +5.967364879e-04f, +1.353819611e-03f, +1.094097769e-03f, -9.512705360e-04f, -4.203000702e-03f, -6.650353458e-03f, -6.189835876e-03f, -2.423459243e-03f, +2.728914008e-03f, +6.323718452e-03f, +6.566118000e-03f, +3.998037969e-03f, +7.735162047e-04f, -1.159755419e-03f, -1.324247193e-03f, -5.420239710e-04f, +1.431035268e-04f, +3.035983857e-04f, +1.395192491e-04f, -1.188126168e-05f,
- /* 24, 8 */ +1.188126168e-05f, -1.395192491e-04f, -3.035983857e-04f, -1.431035268e-04f, +5.420239710e-04f, +1.324247193e-03f, +1.159755419e-03f, -7.735162047e-04f, -3.998037969e-03f, -6.566118000e-03f, -6.323718452e-03f, -2.728914008e-03f, +2.423459243e-03f, +6.189835876e-03f, +6.650353458e-03f, +4.203000702e-03f, +9.512705360e-04f, -1.094097769e-03f, -1.353819611e-03f, -5.967364879e-04f, +1.151572658e-04f, +3.069664313e-04f, +1.521303593e-04f, -6.369927035e-06f,
- /* 24, 9 */ +1.681317980e-05f, -1.270493337e-04f, -2.989274696e-04f, -1.687730668e-04f, +4.879340571e-04f, +1.290920380e-03f, +1.218038367e-03f, -6.013564365e-04f, -3.790608755e-03f, -6.470327373e-03f, -6.443650588e-03f, -3.027861551e-03f, +2.112213435e-03f, +6.042191052e-03f, +6.722618231e-03f, +4.404877423e-03f, +1.134274832e-03f, -1.020980349e-03f, -1.379337399e-03f, -6.518591895e-04f, +8.494321776e-05f, +3.089332390e-04f, +1.648155254e-04f, -2.742338831e-07f,
- /* 24,10 */ +2.117401174e-05f, -1.147831783e-04f, -2.930535649e-04f, -1.921679399e-04f, +4.346671648e-04f, +1.254141844e-03f, +1.269054120e-03f, -4.351089927e-04f, -3.581327596e-03f, -6.363424898e-03f, -6.549485546e-03f, -3.319606230e-03f, +1.795911068e-03f, +5.881013326e-03f, +6.782526316e-03f, +4.603045619e-03f, +1.322158275e-03f, -9.403414758e-04f, -1.400504044e-03f, -7.071681142e-04f, +5.248174754e-05f, +3.094025487e-04f, +1.775031866e-04f, +6.407151783e-06f,
- /* 24,11 */ +2.497524656e-05f, -1.027786538e-04f, -2.860774924e-04f, -2.133007109e-04f, +3.824113238e-04f, +1.214215433e-03f, +1.312931609e-03f, -2.750642929e-04f, -3.370802059e-03f, -6.245879909e-03f, -6.641118197e-03f, -3.603475092e-03f, +1.475302628e-03f, +5.706572744e-03f, +6.829722854e-03f, +4.796881865e-03f, +1.514524553e-03f, -8.521437270e-04f, -1.417028061e-03f, -7.624282793e-04f, +1.780505549e-05f, +3.082808136e-04f, +1.901175448e-04f, +1.367174826e-05f,
- /* 24,12 */ +2.823129406e-05f, -9.108884721e-05f, -2.781004545e-04f, -2.321939470e-04f, +3.313418525e-04f, +1.171444944e-03f, +1.349820125e-03f, -1.214850236e-04f, -3.159630822e-03f, -6.118185890e-03f, -6.718485032e-03f, -3.878819947e-03f, +1.151152247e-03f, +5.519179462e-03f, +6.863885651e-03f, +4.985763915e-03f, +1.710952703e-03f, -7.563747429e-04f, -1.428624316e-03f, -8.173942695e-04f, -1.904245883e-05f, +3.054778181e-04f, +2.025787804e-04f, +2.151306397e-05f,
- /* 24,13 */ +3.095924021e-05f, -7.976200316e-05f, -2.692234993e-04f, -2.488796810e-04f, +2.816211011e-04f, +1.126132932e-03f, +1.379888189e-03f, +2.539414214e-05f, -2.948401826e-03f, -5.980858542e-03f, -6.781564023e-03f, -4.145019341e-03f, +8.242352929e-04f, +5.319183002e-03f, +6.884726614e-03f, +5.169072824e-03f, +1.910998057e-03f, -6.530479478e-04f, -1.435015360e-03f, -8.718108917e-04f, -5.800387728e-05f, +3.009073041e-04f, +2.148033017e-04f, +2.992041863e-05f,
- /* 24,14 */ +3.317859772e-05f, -6.884149383e-05f, -2.595470071e-04f, -2.633988510e-04f, +2.333982604e-04f, +1.078579553e-03f, +1.403322383e-03f, +1.653667139e-04f, -2.737690485e-03f, -5.834433801e-03f, -6.830374341e-03f, -4.401480442e-03f, +4.953359135e-04f, +5.106971374e-03f, +6.891993065e-03f, +5.346195092e-03f, +2.114193296e-03f, -5.422031866e-04f, -1.435932770e-03f, -9.254138922e-04f, -9.900949096e-05f, +2.944876029e-04f, +2.267040256e-04f, +3.887878147e-05f,
- /* 24,15 */ +3.491105347e-05f, -5.836581816e-05f, -2.491702023e-04f, -2.758007186e-04f, +1.868092348e-04f, +1.029081461e-03f, +1.420326139e-03f, +2.982544427e-04f, -2.528057977e-03f, -5.679465803e-03f, -6.864975932e-03f, -4.647640808e-03f, +1.652445539e-04f, +4.882970050e-03f, +6.885468951e-03f, +5.516524807e-03f, +2.320049614e-03f, -4.239072727e-04f, -1.431118485e-03f, -9.779307384e-04f, -1.419765781e-04f, +2.861422699e-04f, +2.381906908e-04f, +4.836862817e-05f,
- /* 24, 0 */ +1.364396009e-04f, +7.446376994e-05f, -3.699603221e-04f, -7.278325124e-04f, -5.051635567e-05f, +1.645033952e-03f, +2.378022613e-03f, -2.243714932e-04f, -5.680096534e-03f, -9.704626250e-03f, -7.823014841e-03f, -2.751390883e-04f, +7.480820734e-03f, +9.788905453e-03f, +6.030582333e-03f, +5.101196376e-04f, -2.328731157e-03f, -1.748114996e-03f, -3.640531891e-05f, +7.242350145e-04f, +4.042879967e-04f, -5.742334247e-05f, -1.414906982e-04f, -2.710774794e-05f,
- /* 24, 1 */ +1.307026563e-04f, +8.975162518e-05f, -3.355241044e-04f, -7.270603554e-04f, -1.326866063e-04f, +1.538422151e-03f, +2.413247311e-03f, +4.875121157e-05f, -5.324161431e-03f, -9.595517291e-03f, -8.141086512e-03f, -8.245287223e-04f, +7.115425083e-03f, +9.847646625e-03f, +6.374200107e-03f, +8.077901021e-04f, -2.264974711e-03f, -1.846937004e-03f, -1.278166248e-04f, +7.160485626e-04f, +4.382702758e-04f, -3.863673145e-05f, -1.457592711e-04f, -3.374854096e-05f,
- /* 24, 2 */ +1.243758393e-04f, +1.032931784e-04f, -3.012044148e-04f, -7.221532843e-04f, -2.098818030e-04f, +1.428995714e-03f, +2.434853697e-03f, +3.086181402e-04f, -4.964188024e-03f, -9.462372525e-03f, -8.434212217e-03f, -1.371256439e-03f, +6.727842835e-03f, +9.880231223e-03f, +6.709529590e-03f, +1.116608515e-03f, -2.186408722e-03f, -1.940762958e-03f, -2.234175210e-04f, +7.030713845e-04f, +4.716595396e-04f, -1.812362539e-05f, -1.491486840e-04f, -4.073257728e-05f,
- /* 24, 3 */ +1.175537217e-04f, +1.151066589e-04f, -2.672136493e-04f, -7.133593846e-04f, -2.819161814e-04f, +1.317455363e-03f, +2.443336794e-03f, +5.546728855e-04f, -4.601573558e-03f, -9.306067159e-03f, -8.701668636e-03f, -1.913559980e-03f, +6.319179241e-03f, +9.886134123e-03f, +7.035155238e-03f, +1.435731166e-03f, -2.092745601e-03f, -2.028850662e-03f, -3.228698520e-04f, +6.851212972e-04f, +5.041985339e-04f, +4.082412249e-06f, -1.515631236e-04f, -4.801831197e-05f,
- /* 24, 4 */ +1.103288160e-04f, +1.252215041e-04f, -2.337507561e-04f, -7.009379926e-04f, -3.486413414e-04f, +1.204483360e-03f, +2.439234434e-03f, +7.864337317e-04f, -4.237695644e-03f, -9.127552920e-03f, -8.942835031e-03f, -2.449695551e-03f, +5.890625670e-03f, +9.864926907e-03f, +7.349672574e-03f, +1.764247300e-03f, -1.983757790e-03f, -2.110456450e-03f, -4.257977068e-04f, +6.620377298e-04f, +5.356216172e-04f, +2.793344097e-05f, -1.529084143e-04f, -5.555842361e-05f,
- /* 24, 5 */ +1.027909684e-04f, +1.336775649e-04f, -2.010005851e-04f, -6.851576168e-04f, -4.099455518e-04f, +1.090740633e-03f, +2.423123355e-03f, +1.003494037e-03f, -3.873906570e-03f, -8.927853008e-03f, -9.157195135e-03f, -2.977945083e-03f, +5.443455012e-03f, +9.816280735e-03f, +7.651694576e-03f, +2.101181791e-03f, -1.859280606e-03f, -2.184839011e-03f, -5.317880090e-04f, +6.336836952e-04f, +5.656561208e-04f, +5.336672075e-05f, -1.530928622e-04f, -6.329988035e-05f,
- /* 24, 6 */ +9.502680145e-05f, +1.405242734e-04f, -1.691333585e-04f, -6.662938873e-04f, -4.657528710e-04f, +9.768641187e-04f, +2.395615219e-03f, +1.205522243e-03f, -3.511527821e-03f, -8.708056786e-03f, -9.344338564e-03f, -3.496623369e-03f, +4.979016698e-03f, +9.739968779e-03f, +7.939858067e-03f, +2.445498177e-03f, -1.719214825e-03f, -2.251263312e-03f, -6.403913399e-04f, +5.999476948e-04f, +5.940238143e-04f, +8.030437567e-05f, -1.520281231e-04f, -7.118405837e-05f,
- /* 24, 7 */ +8.711921055e-05f, +1.458197836e-04f, -1.383042613e-04f, -6.446275435e-04f, -5.160220948e-04f, +8.634643034e-04f, +2.357352548e-03f, +1.392261511e-03f, -3.151844842e-03f, -8.469314215e-03f, -9.503961719e-03f, -4.004085039e-03f, +4.498731341e-03f, +9.635868210e-03f, +8.212830089e-03f, +2.796102059e-03f, -1.563529005e-03f, -2.309004616e-03f, -7.511229995e-04f, +5.607455425e-04f, +6.204424737e-04f, +1.086531499e-04f, -1.496300883e-04f, -7.914691395e-05f,
- /* 24, 8 */ +7.914691395e-05f, +1.496300883e-04f, -1.086531499e-04f, -6.204424737e-04f, -5.607455425e-04f, +7.511229995e-04f, +2.309004616e-03f, +1.563529005e-03f, -2.796102059e-03f, -8.212830089e-03f, -9.635868210e-03f, -4.498731341e-03f, +4.004085039e-03f, +9.503961719e-03f, +8.469314215e-03f, +3.151844842e-03f, -1.392261511e-03f, -2.357352548e-03f, -8.634643034e-04f, +5.160220948e-04f, +6.446275435e-04f, +1.383042613e-04f, -1.458197836e-04f, -8.711921055e-05f,
- /* 24, 9 */ +7.118405837e-05f, +1.520281231e-04f, -8.030437567e-05f, -5.940238143e-04f, -5.999476948e-04f, +6.403913399e-04f, +2.251263312e-03f, +1.719214825e-03f, -2.445498177e-03f, -7.939858067e-03f, -9.739968779e-03f, -4.979016698e-03f, +3.496623369e-03f, +9.344338564e-03f, +8.708056786e-03f, +3.511527821e-03f, -1.205522243e-03f, -2.395615219e-03f, -9.768641187e-04f, +4.657528710e-04f, +6.662938873e-04f, +1.691333585e-04f, -1.405242734e-04f, -9.502680145e-05f,
- /* 24,10 */ +6.329988035e-05f, +1.530928622e-04f, -5.336672075e-05f, -5.656561208e-04f, -6.336836952e-04f, +5.317880090e-04f, +2.184839011e-03f, +1.859280606e-03f, -2.101181791e-03f, -7.651694576e-03f, -9.816280735e-03f, -5.443455012e-03f, +2.977945083e-03f, +9.157195135e-03f, +8.927853008e-03f, +3.873906570e-03f, -1.003494037e-03f, -2.423123355e-03f, -1.090740633e-03f, +4.099455518e-04f, +6.851576168e-04f, +2.010005851e-04f, -1.336775649e-04f, -1.027909684e-04f,
- /* 24,11 */ +5.555842361e-05f, +1.529084143e-04f, -2.793344097e-05f, -5.356216172e-04f, -6.620377298e-04f, +4.257977068e-04f, +2.110456450e-03f, +1.983757790e-03f, -1.764247300e-03f, -7.349672574e-03f, -9.864926907e-03f, -5.890625670e-03f, +2.449695551e-03f, +8.942835031e-03f, +9.127552920e-03f, +4.237695644e-03f, -7.864337317e-04f, -2.439234434e-03f, -1.204483360e-03f, +3.486413414e-04f, +7.009379926e-04f, +2.337507561e-04f, -1.252215041e-04f, -1.103288160e-04f,
- /* 24,12 */ +4.801831197e-05f, +1.515631236e-04f, -4.082412249e-06f, -5.041985339e-04f, -6.851212972e-04f, +3.228698520e-04f, +2.028850662e-03f, +2.092745601e-03f, -1.435731166e-03f, -7.035155238e-03f, -9.886134123e-03f, -6.319179241e-03f, +1.913559980e-03f, +8.701668636e-03f, +9.306067159e-03f, +4.601573558e-03f, -5.546728855e-04f, -2.443336794e-03f, -1.317455363e-03f, +2.819161814e-04f, +7.133593846e-04f, +2.672136493e-04f, -1.151066589e-04f, -1.175537217e-04f,
- /* 24,13 */ +4.073257728e-05f, +1.491486840e-04f, +1.812362539e-05f, -4.716595396e-04f, -7.030713845e-04f, +2.234175210e-04f, +1.940762958e-03f, +2.186408722e-03f, -1.116608515e-03f, -6.709529590e-03f, -9.880231223e-03f, -6.727842835e-03f, +1.371256439e-03f, +8.434212217e-03f, +9.462372525e-03f, +4.964188024e-03f, -3.086181402e-04f, -2.434853697e-03f, -1.428995714e-03f, +2.098818030e-04f, +7.221532843e-04f, +3.012044148e-04f, -1.032931784e-04f, -1.243758393e-04f,
- /* 24,14 */ +3.374854096e-05f, +1.457592711e-04f, +3.863673145e-05f, -4.382702758e-04f, -7.160485626e-04f, +1.278166248e-04f, +1.846937004e-03f, +2.264974711e-03f, -8.077901021e-04f, -6.374200107e-03f, -9.847646625e-03f, -7.115425083e-03f, +8.245287223e-04f, +8.141086512e-03f, +9.595517291e-03f, +5.324161431e-03f, -4.875121157e-05f, -2.413247311e-03f, -1.538422151e-03f, +1.326866063e-04f, +7.270603554e-04f, +3.355241044e-04f, -8.975162518e-05f, -1.307026563e-04f,
- /* 24,15 */ +2.710774794e-05f, +1.414906982e-04f, +5.742334247e-05f, -4.042879967e-04f, -7.242350145e-04f, +3.640531891e-05f, +1.748114996e-03f, +2.328731157e-03f, -5.101196376e-04f, -6.030582333e-03f, -9.788905453e-03f, -7.480820734e-03f, +2.751390883e-04f, +7.823014841e-03f, +9.704626250e-03f, +5.680096534e-03f, +2.243714932e-04f, -2.378022613e-03f, -1.645033952e-03f, +5.051635567e-05f, +7.278325124e-04f, +3.699603221e-04f, -7.446376994e-05f, -1.364396009e-04f,
- /* 20, 0 */ +1.366654441e-04f, -5.248309364e-04f, -9.559425272e-04f, +4.495080153e-04f, +2.846407623e-03f, +1.989454068e-03f, -4.491151594e-03f, -1.156100448e-02f, -1.065698581e-02f, -3.895768346e-04f, +1.023895907e-02f, +1.180960294e-02f, +5.007407400e-03f, -1.738511442e-03f, -2.938517986e-03f, -6.019203323e-04f, +9.329195550e-04f, +5.763302237e-04f, -1.134902041e-04f, -1.824770389e-04f,
- /* 20, 1 */ +1.568890194e-04f, -4.728495798e-04f, -9.708996289e-04f, +3.024354413e-04f, +2.741035078e-03f, +2.215509786e-03f, -3.978754642e-03f, -1.127853170e-02f, -1.103432131e-02f, -1.167153605e-03f, +9.781544627e-03f, +1.202253469e-02f, +5.525210549e-03f, -1.462933465e-03f, -3.016077094e-03f, -7.588827792e-04f, +9.015285321e-04f, +6.268920900e-04f, -8.735502929e-05f, -1.921860197e-04f,
- /* 20, 2 */ +1.741940978e-04f, -4.208194393e-04f, -9.781360984e-04f, +1.614183836e-04f, +2.623714429e-03f, +2.416571115e-03f, -3.472449249e-03f, -1.096411041e-02f, -1.136986548e-02f, -1.940007910e-03f, +9.286243980e-03f, +1.219815240e-02f, +6.042182027e-03f, -1.163118517e-03f, -3.077830056e-03f, -9.195341683e-04f, +8.615147935e-04f, +6.760417132e-04f, -5.827810709e-05f, -2.008073985e-04f,
- /* 20, 3 */ +1.886370492e-04f, -3.691494362e-04f, -9.780356474e-04f, +2.709710175e-05f, +2.495775707e-03f, +2.592671089e-03f, -2.974378699e-03f, -1.061978749e-02f, -1.166272581e-02f, -2.705018824e-03f, +8.754750074e-03f, +1.233496472e-02f, +6.555887236e-03f, -8.396136973e-04f, -3.122565398e-03f, -1.082944596e-03f, +8.126754773e-04f, +7.232876858e-04f, -2.630548656e-05f, -2.081598890e-04f,
- /* 20, 4 */ +2.002956356e-04f, -3.182221327e-04f, -9.710157868e-04f, -9.996474913e-05f, +2.358556029e-03f, +2.743978910e-03f, -2.486586970e-03f, -1.024771819e-02f, -1.191222039e-02f, -3.459106327e-03f, +8.188939591e-03f, +1.243164652e-02f, +7.063848473e-03f, -4.931158341e-04f, -3.149124311e-03f, -1.248118895e-03f, +7.548636055e-04f, +7.681251779e-04f, +8.487693398e-06f, -2.140618814e-04f,
- /* 20, 5 */ +2.092671085e-04f, -2.683920446e-04f, -9.575231021e-04f, -2.192806382e-04f, +2.213390969e-03f, +2.870794883e-03f, -2.011009640e-03f, -9.850152742e-03f, -1.211787969e-02f, -4.199247312e-03f, +7.590864160e-03f, +1.248704815e-02f, +7.563557889e-03f, -1.244715801e-04f, -3.156409832e-03f, -1.414000676e-03f, +6.879919170e-04f, +8.100393630e-04f, +4.599617124e-05f, -2.183332212e-04f,
- /* 20, 6 */ +2.156662323e-04f, -2.199842607e-04f, -9.380285157e-04f, -3.304411223e-04f, +2.061606212e-03f, +2.973544666e-03f, -1.549465619e-03f, -9.429422833e-03f, -1.227944713e-02f, -4.922491262e-03f, +6.962740532e-03f, +1.250020393e-02f, +8.052490864e-03f, +2.653234199e-04f, -3.143395899e-03f, -1.579476941e-03f, +6.120364145e-04f, +8.485090918e-04f, +8.608374363e-05f, -2.207970734e-04f,
- /* 20, 7 */ +2.196232573e-04f, -1.732933680e-04f, -9.130225739e-04f, -4.331132727e-04f, +1.904509553e-03f, +3.052772891e-03f, -1.103649750e-03f, -8.987927630e-03f, -1.239687839e-02f, -5.625975475e-03f, +6.306939763e-03f, +1.247033951e-02f, +8.528119711e-03f, +6.751263085e-04f, -3.109136222e-03f, -1.743383273e-03f, +5.270395872e-04f, +8.830107920e-04f, +1.285826773e-04f, -2.212818602e-04f,
- /* 20, 8 */ +2.212818602e-04f, -1.285826773e-04f, -8.830107920e-04f, -5.270395872e-04f, +1.743383273e-03f, +3.109136222e-03f, -6.751263085e-04f, -8.528119711e-03f, -1.247033951e-02f, -6.306939763e-03f, +5.625975475e-03f, +1.239687839e-02f, +8.987927630e-03f, +1.103649750e-03f, -3.052772891e-03f, -1.904509553e-03f, +4.331132727e-04f, +9.130225739e-04f, +1.732933680e-04f, -2.196232573e-04f,
- /* 20, 9 */ +2.207970734e-04f, -8.608374363e-05f, -8.485090918e-04f, -6.120364145e-04f, +1.579476941e-03f, +3.143395899e-03f, -2.653234199e-04f, -8.052490864e-03f, -1.250020393e-02f, -6.962740532e-03f, +4.922491262e-03f, +1.227944713e-02f, +9.429422833e-03f, +1.549465619e-03f, -2.973544666e-03f, -2.061606212e-03f, +3.304411223e-04f, +9.380285157e-04f, +2.199842607e-04f, -2.156662323e-04f,
- /* 20,10 */ +2.183332212e-04f, -4.599617124e-05f, -8.100393630e-04f, -6.879919170e-04f, +1.414000676e-03f, +3.156409832e-03f, +1.244715801e-04f, -7.563557889e-03f, -1.248704815e-02f, -7.590864160e-03f, +4.199247312e-03f, +1.211787969e-02f, +9.850152742e-03f, +2.011009640e-03f, -2.870794883e-03f, -2.213390969e-03f, +2.192806382e-04f, +9.575231021e-04f, +2.683920446e-04f, -2.092671085e-04f,
- /* 20,11 */ +2.140618814e-04f, -8.487693398e-06f, -7.681251779e-04f, -7.548636055e-04f, +1.248118895e-03f, +3.149124311e-03f, +4.931158341e-04f, -7.063848473e-03f, -1.243164652e-02f, -8.188939591e-03f, +3.459106327e-03f, +1.191222039e-02f, +1.024771819e-02f, +2.486586970e-03f, -2.743978910e-03f, -2.358556029e-03f, +9.996474913e-05f, +9.710157868e-04f, +3.182221327e-04f, -2.002956356e-04f,
- /* 20,12 */ +2.081598890e-04f, +2.630548656e-05f, -7.232876858e-04f, -8.126754773e-04f, +1.082944596e-03f, +3.122565398e-03f, +8.396136973e-04f, -6.555887236e-03f, -1.233496472e-02f, -8.754750074e-03f, +2.705018824e-03f, +1.166272581e-02f, +1.061978749e-02f, +2.974378699e-03f, -2.592671089e-03f, -2.495775707e-03f, -2.709710175e-05f, +9.780356474e-04f, +3.691494362e-04f, -1.886370492e-04f,
- /* 20,13 */ +2.008073985e-04f, +5.827810709e-05f, -6.760417132e-04f, -8.615147935e-04f, +9.195341683e-04f, +3.077830056e-03f, +1.163118517e-03f, -6.042182027e-03f, -1.219815240e-02f, -9.286243980e-03f, +1.940007910e-03f, +1.136986548e-02f, +1.096411041e-02f, +3.472449249e-03f, -2.416571115e-03f, -2.623714429e-03f, -1.614183836e-04f, +9.781360984e-04f, +4.208194393e-04f, -1.741940978e-04f,
- /* 20,14 */ +1.921860197e-04f, +8.735502929e-05f, -6.268920900e-04f, -9.015285321e-04f, +7.588827792e-04f, +3.016077094e-03f, +1.462933465e-03f, -5.525210549e-03f, -1.202253469e-02f, -9.781544627e-03f, +1.167153605e-03f, +1.103432131e-02f, +1.127853170e-02f, +3.978754642e-03f, -2.215509786e-03f, -2.741035078e-03f, -3.024354413e-04f, +9.708996289e-04f, +4.728495798e-04f, -1.568890194e-04f,
- /* 20,15 */ +1.824770389e-04f, +1.134902041e-04f, -5.763302237e-04f, -9.329195550e-04f, +6.019203323e-04f, +2.938517986e-03f, +1.738511442e-03f, -5.007407400e-03f, -1.180960294e-02f, -1.023895907e-02f, +3.895768346e-04f, +1.065698581e-02f, +1.156100448e-02f, +4.491151594e-03f, -1.989454068e-03f, -2.846407623e-03f, -4.495080153e-04f, +9.559425272e-04f, +5.248309364e-04f, -1.366654441e-04f,
- /* 20, 0 */ +2.228492143e-04f, +8.155042897e-05f, -9.008994790e-04f, -8.358434283e-04f, +2.057950411e-03f, +3.687980724e-03f, -2.439509438e-03f, -1.276984908e-02f, -1.372824692e-02f, -5.233437973e-04f, +1.325794606e-02f, +1.324423486e-02f, +3.079014715e-03f, -3.569583683e-03f, -2.275574997e-03f, +7.329307333e-04f, +9.595727172e-04f, -3.951670647e-05f, -2.357435643e-04f, +0.000000000e+00f,
- /* 20, 1 */ +2.085936177e-04f, +1.192685572e-04f, -8.383784628e-04f, -9.252931617e-04f, +1.836793834e-03f, +3.772148819e-03f, -1.820843931e-03f, -1.225552529e-02f, -1.413563751e-02f, -1.567452511e-03f, +1.272631251e-02f, +1.367510758e-02f, +3.736377939e-03f, -3.415952420e-03f, -2.487640644e-03f, +6.166326985e-04f, +1.013551226e-03f, +6.721135679e-06f, -2.469830254e-04f, +3.513221827e-04f,
- /* 20, 2 */ +1.932641301e-04f, +1.526114972e-04f, -7.728409668e-04f, -1.001322392e-03f, +1.614057279e-03f, +3.823286477e-03f, -1.225775962e-03f, -1.170500117e-02f, -1.447891891e-02f, -2.603840968e-03f, +1.213529571e-02f, +1.405908160e-02f, +4.408408028e-03f, -3.226289363e-03f, -2.692058620e-03f, +4.871526668e-04f, +1.061979909e-03f, +5.699759108e-05f, -2.562708188e-04f, -2.243392330e-05f,
- /* 20, 3 */ +1.771389305e-04f, +1.815689683e-04f, -7.050956564e-04f, -1.064087220e-03f, +1.391605775e-03f, +3.842769943e-03f, -6.568264230e-04f, -1.112214974e-02f, -1.475727496e-02f, -3.627416831e-03f, +1.148720750e-02f, +1.439298630e-02f, +5.091721334e-03f, -3.000017933e-03f, -2.886692602e-03f, +3.448244648e-04f, +1.104003505e-03f, +1.110914327e-04f, -2.633104157e-04f, -3.471505729e-05f,
- /* 20, 4 */ +1.604844080e-04f, +2.061770649e-04f, -6.359221544e-04f, -1.113850250e-03f, +1.171206475e-03f, +3.832136817e-03f, -1.162685315e-04f, -1.051095143e-02f, -1.497027375e-02f, -4.633169088e-03f, +1.078471010e-02f, +1.467389068e-02f, +5.782760145e-03f, -2.736794515e-03f, -3.069373786e-03f, +1.901162062e-04f, +1.138774993e-03f, +1.687245284e-04f, -2.678091338e-04f, -4.805250238e-05f,
- /* 20, 5 */ +1.435528424e-04f, +2.265149998e-04f, -5.660652366e-04f, -1.150972836e-03f, +9.545189950e-04f, +3.793068954e-03f, +3.938808876e-04f, -9.875465824e-03f, -1.511786634e-02f, -5.616199746e-03f, +1.003080152e-02f, +1.489912656e-02f, +6.477812874e-03f, -2.436518983e-03f, -3.237916857e-03f, +2.363306300e-05f, +1.165464351e-03f, +2.295611999e-04f, -2.694819135e-04f, -6.235351094e-05f,
- /* 20, 6 */ +1.265803873e-04f, +2.427015763e-04f, -4.962296614e-04f, -1.175906803e-03f, +7.430870045e-04f, +3.727374795e-03f, +8.718680321e-04f, -9.219803451e-03f, -1.520038286e-02f, -6.571754682e-03f, +9.228798617e-03f, +1.506631023e-02f, +7.173035830e-03f, -2.099343642e-03f, -3.390136682e-03f, -1.538810619e-04f, +1.183267602e-03f, +2.932081598e-04f, -2.680552395e-04f, -7.750368078e-05f,
- /* 20, 7 */ +1.097853637e-04f, +2.548914413e-04f, -4.270756535e-04f, -1.189185758e-03f, +5.383311068e-04f, +3.636971298e-03f, +1.316206650e-03f, -8.548097577e-03f, -1.521852609e-02f, -7.495253444e-03f, +8.382317770e-03f, +1.517336238e-02f, +7.864476409e-03f, -1.725680482e-03f, -3.523865616e-03f, -3.415430197e-04f, +1.191416067e-03f, +3.592150564e-04f, -2.632711715e-04f, -9.336686615e-05f,
- /* 20, 8 */ +9.336686615e-05f, +2.632711715e-04f, -3.592150564e-04f, -1.191416067e-03f, +3.415430197e-04f, +3.523865616e-03f, +1.725680482e-03f, -7.864476409e-03f, -1.517336238e-02f, -8.382317770e-03f, +7.495253444e-03f, +1.521852609e-02f, +8.548097577e-03f, -1.316206650e-03f, -3.636971298e-03f, -5.383311068e-04f, +1.189185758e-03f, +4.270756535e-04f, -2.548914413e-04f, -1.097853637e-04f,
- /* 20, 9 */ +7.750368078e-05f, +2.680552395e-04f, -2.932081598e-04f, -1.183267602e-03f, +1.538810619e-04f, +3.390136682e-03f, +2.099343642e-03f, -7.173035830e-03f, -1.506631023e-02f, -9.228798617e-03f, +6.571754682e-03f, +1.520038286e-02f, +9.219803451e-03f, -8.718680321e-04f, -3.727374795e-03f, -7.430870045e-04f, +1.175906803e-03f, +4.962296614e-04f, -2.427015763e-04f, -1.265803873e-04f,
- /* 20,10 */ +6.235351094e-05f, +2.694819135e-04f, -2.295611999e-04f, -1.165464351e-03f, -2.363306300e-05f, +3.237916857e-03f, +2.436518983e-03f, -6.477812874e-03f, -1.489912656e-02f, -1.003080152e-02f, +5.616199746e-03f, +1.511786634e-02f, +9.875465824e-03f, -3.938808876e-04f, -3.793068954e-03f, -9.545189950e-04f, +1.150972836e-03f, +5.660652366e-04f, -2.265149998e-04f, -1.435528424e-04f,
- /* 20,11 */ +4.805250238e-05f, +2.678091338e-04f, -1.687245284e-04f, -1.138774993e-03f, -1.901162062e-04f, +3.069373786e-03f, +2.736794515e-03f, -5.782760145e-03f, -1.467389068e-02f, -1.078471010e-02f, +4.633169088e-03f, +1.497027375e-02f, +1.051095143e-02f, +1.162685315e-04f, -3.832136817e-03f, -1.171206475e-03f, +1.113850250e-03f, +6.359221544e-04f, -2.061770649e-04f, -1.604844080e-04f,
- /* 20,12 */ +3.471505729e-05f, +2.633104157e-04f, -1.110914327e-04f, -1.104003505e-03f, -3.448244648e-04f, +2.886692602e-03f, +3.000017933e-03f, -5.091721334e-03f, -1.439298630e-02f, -1.148720750e-02f, +3.627416831e-03f, +1.475727496e-02f, +1.112214974e-02f, +6.568264230e-04f, -3.842769943e-03f, -1.391605775e-03f, +1.064087220e-03f, +7.050956564e-04f, -1.815689683e-04f, -1.771389305e-04f,
- /* 20,13 */ +2.243392330e-05f, +2.562708188e-04f, -5.699759108e-05f, -1.061979909e-03f, -4.871526668e-04f, +2.692058620e-03f, +3.226289363e-03f, -4.408408028e-03f, -1.405908160e-02f, -1.213529571e-02f, +2.603840968e-03f, +1.447891891e-02f, +1.170500117e-02f, +1.225775962e-03f, -3.823286477e-03f, -1.614057279e-03f, +1.001322392e-03f, +7.728409668e-04f, -1.526114972e-04f, -1.932641301e-04f,
- /* 20,14 */ -3.513221827e-04f, +2.469830254e-04f, -6.721135679e-06f, -1.013551226e-03f, -6.166326985e-04f, +2.487640644e-03f, +3.415952420e-03f, -3.736377939e-03f, -1.367510758e-02f, -1.272631251e-02f, +1.567452511e-03f, +1.413563751e-02f, +1.225552529e-02f, +1.820843931e-03f, -3.772148819e-03f, -1.836793834e-03f, +9.252931617e-04f, +8.383784628e-04f, -1.192685572e-04f, -2.085936177e-04f,
- /* 20,15 */ +0.000000000e+00f, +2.357435643e-04f, +3.951670647e-05f, -9.595727172e-04f, -7.329307333e-04f, +2.275574997e-03f, +3.569583683e-03f, -3.079014715e-03f, -1.324423486e-02f, -1.325794606e-02f, +5.233437973e-04f, +1.372824692e-02f, +1.276984908e-02f, +2.439509438e-03f, -3.687980724e-03f, -2.057950411e-03f, +8.358434283e-04f, +9.008994790e-04f, -8.155042897e-05f, -2.228492143e-04f,
- /* 20, 0 */ +1.941987182e-05f, +3.146481294e-04f, -2.345645569e-04f, -1.414667200e-03f, +5.144442975e-04f, +4.454307224e-03f, +1.983750799e-04f, -1.327145644e-02f, -1.714303646e-02f, -6.846700315e-04f, +1.665178821e-02f, +1.403392762e-02f, +4.892879248e-04f, -4.540173148e-03f, -7.773192529e-04f, +1.425286503e-03f, +3.160682424e-04f, -3.205185770e-04f, -3.476344875e-05f, +0.000000000e+00f,
- /* 20, 1 */ -3.929324583e-04f, +3.051602666e-04f, -1.573970191e-04f, -1.390281543e-03f, +2.638941923e-04f, +4.333213577e-03f, +8.411158857e-04f, -1.246868983e-02f, -1.754185168e-02f, -2.049974665e-03f, +1.606968443e-02f, +1.474987505e-02f, +1.218921159e-03f, -4.587812221e-03f, -1.050600845e-03f, +1.421006956e-03f, +4.012475422e-04f, -3.223256966e-04f, -5.166761157e-05f, +0.000000000e+00f,
- /* 20, 2 */ +0.000000000e+00f, +2.925136688e-04f, -8.512788201e-05f, -1.353337804e-03f, +2.735561011e-05f, +4.180044295e-03f, +1.436437300e-03f, -1.163198941e-02f, -1.784731333e-02f, -3.403203680e-03f, +1.539895444e-02f, +1.541325656e-02f, +1.987128326e-03f, -4.594412557e-03f, -1.332146559e-03f, +1.400789491e-03f, +4.893452569e-04f, -3.196436833e-04f, -7.000071077e-05f, +0.000000000e+00f,
- /* 20, 3 */ +0.000000000e+00f, +2.771727451e-04f, -1.822077520e-05f, -1.305102482e-03f, -1.937209193e-04f, +3.998069961e-03f, +1.982303068e-03f, -1.076779718e-02f, -1.805915757e-02f, -4.736408619e-03f, +1.464246859e-02f, +1.601826823e-02f, +2.790083541e-03f, -4.557383277e-03f, -1.619599483e-03f, +1.363706016e-03f, +5.795104543e-04f, -3.120743969e-04f, -8.959420873e-05f, +0.000000000e+00f,
- /* 20, 4 */ +0.000000000e+00f, +2.596009711e-04f, +4.295843246e-05f, -1.246883037e-03f, -3.981230344e-04f, +3.790645354e-03f, +2.477139789e-03f, -9.882582946e-03f, -1.817777152e-02f, -6.041793017e-03f, +1.380372215e-02f, +1.655939544e-02f, +3.623550339e-03f, -4.474387395e-03f, -1.910400883e-03f, +1.308956662e-03f, +6.708027600e-04f, -2.992550459e-04f, -1.102423612e-04f, +0.000000000e+00f,
- /* 20, 5 */ +0.000000000e+00f, +2.402546692e-04f, +9.813963752e-05f, -1.180011107e-03f, -5.848760724e-04f, +3.561175652e-03f, +2.919834686e-03f, -8.982792490e-03f, -1.820418220e-02f, -7.311771311e-03f, +1.288681385e-02f, +1.703146227e-02f, +4.482904855e-03f, -4.343373467e-03f, -2.201805423e-03f, +1.235886510e-03f, +7.621980241e-04f, -2.808658988e-04f, -1.317024534e-04f, +0.000000000e+00f,
- /* 20, 6 */ +0.000000000e+00f, +2.195772899e-04f, +1.471454599e-04f, -1.105826337e-03f, -7.532402115e-04f, +3.313083439e-03f, +3.309729355e-03f, -8.074797022e-03f, -1.814004021e-02f, -8.539025902e-03f, +1.189641917e-02f, +1.742967891e-02f, +5.363163073e-03f, -4.162605659e-03f, -2.490898970e-03f, +1.144001586e-03f, +8.525953702e-04f, -2.566379213e-04f, -1.536956226e-04f, +0.000000000e+00f,
- /* 20, 7 */ +0.000000000e+00f, +1.979942392e-04f, +1.898872476e-04f, -1.025661016e-03f, -9.027053553e-04f, +3.049776817e-03f, +3.646609643e-03f, -7.164844319e-03f, -1.798759853e-02f, -9.716561844e-03f, +1.083775871e-02f, +1.774968640e-02f, +6.259011965e-03f, -3.930691879e-03f, -2.774618906e-03f, +1.032983905e-03f, +9.408256132e-04f, -2.263602294e-04f, -1.759082929e-04f, +0.000000000e+00f,
- /* 20, 8 */ +0.000000000e+00f, +1.759082929e-04f, +2.263602294e-04f, -9.408256132e-04f, -1.032983905e-03f, +2.774618906e-03f, +3.930691879e-03f, -6.259011965e-03f, -1.774968640e-02f, -1.083775871e-02f, +9.716561844e-03f, +1.798759853e-02f, +7.164844319e-03f, -3.646609643e-03f, -3.049776817e-03f, +9.027053553e-04f, +1.025661016e-03f, -1.898872476e-04f, -1.979942392e-04f, +0.000000000e+00f,
- /* 20, 9 */ +0.000000000e+00f, +1.536956226e-04f, +2.566379213e-04f, -8.525953702e-04f, -1.144001586e-03f, +2.490898970e-03f, +4.162605659e-03f, -5.363163073e-03f, -1.742967891e-02f, -1.189641917e-02f, +8.539025902e-03f, +1.814004021e-02f, +8.074797022e-03f, -3.309729355e-03f, -3.313083439e-03f, +7.532402115e-04f, +1.105826337e-03f, -1.471454599e-04f, -2.195772899e-04f, +0.000000000e+00f,
- /* 20,10 */ +0.000000000e+00f, +1.317024534e-04f, +2.808658988e-04f, -7.621980241e-04f, -1.235886510e-03f, +2.201805423e-03f, +4.343373467e-03f, -4.482904855e-03f, -1.703146227e-02f, -1.288681385e-02f, +7.311771311e-03f, +1.820418220e-02f, +8.982792490e-03f, -2.919834686e-03f, -3.561175652e-03f, +5.848760724e-04f, +1.180011107e-03f, -9.813963752e-05f, -2.402546692e-04f, +0.000000000e+00f,
- /* 20,11 */ +0.000000000e+00f, +1.102423612e-04f, +2.992550459e-04f, -6.708027600e-04f, -1.308956662e-03f, +1.910400883e-03f, +4.474387395e-03f, -3.623550339e-03f, -1.655939544e-02f, -1.380372215e-02f, +6.041793017e-03f, +1.817777152e-02f, +9.882582946e-03f, -2.477139789e-03f, -3.790645354e-03f, +3.981230344e-04f, +1.246883037e-03f, -4.295843246e-05f, -2.596009711e-04f, +0.000000000e+00f,
- /* 20,12 */ +0.000000000e+00f, +8.959420873e-05f, +3.120743969e-04f, -5.795104543e-04f, -1.363706016e-03f, +1.619599483e-03f, +4.557383277e-03f, -2.790083541e-03f, -1.601826823e-02f, -1.464246859e-02f, +4.736408619e-03f, +1.805915757e-02f, +1.076779718e-02f, -1.982303068e-03f, -3.998069961e-03f, +1.937209193e-04f, +1.305102482e-03f, +1.822077520e-05f, -2.771727451e-04f, +0.000000000e+00f,
- /* 20,13 */ +0.000000000e+00f, +7.000071077e-05f, +3.196436833e-04f, -4.893452569e-04f, -1.400789491e-03f, +1.332146559e-03f, +4.594412557e-03f, -1.987128326e-03f, -1.541325656e-02f, -1.539895444e-02f, +3.403203680e-03f, +1.784731333e-02f, +1.163198941e-02f, -1.436437300e-03f, -4.180044295e-03f, -2.735561011e-05f, +1.353337804e-03f, +8.512788201e-05f, -2.925136688e-04f, +0.000000000e+00f,
- /* 20,14 */ +0.000000000e+00f, +5.166761157e-05f, +3.223256966e-04f, -4.012475422e-04f, -1.421006956e-03f, +1.050600845e-03f, +4.587812221e-03f, -1.218921159e-03f, -1.474987505e-02f, -1.606968443e-02f, +2.049974665e-03f, +1.754185168e-02f, +1.246868983e-02f, -8.411158857e-04f, -4.333213577e-03f, -2.638941923e-04f, +1.390281543e-03f, +1.573970191e-04f, -3.051602666e-04f, +3.929324583e-04f,
- /* 20,15 */ +0.000000000e+00f, +3.476344875e-05f, +3.205185770e-04f, -3.160682424e-04f, -1.425286503e-03f, +7.773192529e-04f, +4.540173148e-03f, -4.892879248e-04f, -1.403392762e-02f, -1.665178821e-02f, +6.846700315e-04f, +1.714303646e-02f, +1.327145644e-02f, -1.983750799e-04f, -4.454307224e-03f, -5.144442975e-04f, +1.414667200e-03f, +2.345645569e-04f, -3.146481294e-04f, -1.941987182e-05f,
- /* 16, 0 */ +3.215659774e-04f, -1.081239301e-03f, -1.047044785e-03f, +4.045780572e-03f, +3.005074105e-03f, -1.291342297e-02f, -2.083886340e-02f, -8.761305366e-04f, +2.037274022e-02f, +1.401097590e-02f, -2.379335663e-03f, -4.351475252e-03f, +8.522542940e-04f, +1.190910327e-03f, -2.874725537e-04f, -1.571395541e-04f,
- /* 16, 1 */ +3.474336395e-04f, -9.673171402e-04f, -1.215210440e-03f, +3.716713245e-03f, +3.558195313e-03f, -1.178473019e-02f, -2.117503726e-02f, -2.622305580e-03f, +1.977768827e-02f, +1.506763496e-02f, -1.682580557e-03f, -4.628640452e-03f, +6.313208395e-04f, +1.294421768e-03f, -2.448738999e-04f, -1.853334990e-04f,
- /* 16, 2 */ +3.654544998e-04f, -8.509694882e-04f, -1.356620316e-03f, +3.369379821e-03f, +4.037840451e-03f, -1.063463040e-02f, -2.138131359e-02f, -4.350277043e-03f, +1.905580462e-02f, +1.607371744e-02f, -9.171630004e-04f, -4.872124601e-03f, +3.850930357e-04f, +1.389803701e-03f, -1.936024914e-04f, -2.137577131e-04f,
- /* 16, 3 */ +3.760939096e-04f, -7.339202470e-04f, -1.471475134e-03f, +3.008783957e-03f, +4.443873126e-03f, -9.472744965e-03f, -2.145880202e-02f, -6.048090342e-03f, +1.821025632e-02f, +1.701970579e-02f, -8.619500088e-05f, -5.076839304e-03f, +1.147982409e-04f, +1.475048300e-03f, -1.336143134e-04f, -2.418805517e-04f,
- /* 16, 4 */ +3.798900997e-04f, -6.177751723e-04f, -1.560284979e-03f, +2.639777229e-03f, +4.776853126e-03f, -8.308496634e-03f, -2.140964525e-02f, -7.704060609e-03f, +1.724526338e-02f, +1.789634168e-02f, +8.064574864e-04f, -5.237819035e-03f, -1.779495980e-04f, +1.548135431e-03f, -6.499930382e-05f, -2.691226085e-04f,
- /* 16, 5 */ +3.774404835e-04f, -5.040080805e-04f, -1.623844377e-03f, +2.267013831e-03f, +5.038003695e-03f, -7.151026424e-03f, -2.123698452e-02f, -9.306876654e-03f, +1.616607109e-02f, +1.869471933e-02f, +1.756186609e-03f, -5.350281937e-03f, -4.911465534e-04f, +1.607060019e-03f, +1.200956190e-05f, -2.948629835e-04f,
- /* 16, 6 */ +3.693879948e-04f, -3.939497146e-04f, -1.663205192e-03f, +1.894909522e-03f, +5.229172900e-03f, -6.009115326e-03f, -2.094491570e-02f, -1.084570103e-02f, +1.497891218e-02f, +1.940637710e-02f, +2.757662946e-03f, -5.409691072e-03f, -8.224000281e-04f, +1.649860923e-03f, +9.702877105e-05f, -3.184467584e-04f,
- /* 16, 7 */ +3.564076658e-04f, -2.887792732e-04f, -1.679647798e-03f, +1.527605146e-03f, +5.352789702e-03f, -4.891111570e-03f, -2.053843663e-02f, -1.231026521e-02f, +1.369095882e-02f, +2.002338639e-02f, +3.804864118e-03f, -5.411815390e-03f, -1.168934972e-03f, +1.674650966e-03f, +1.895185724e-04f, -3.391936346e-04f,
- /* 16, 8 */ +3.391936346e-04f, -1.895185724e-04f, -1.674650966e-03f, +1.168934972e-03f, +5.411815390e-03f, -3.804864118e-03f, -2.002338639e-02f, -1.369095882e-02f, +1.231026521e-02f, +2.053843663e-02f, +4.891111570e-03f, -5.352789702e-03f, -1.527605146e-03f, +1.679647798e-03f, +2.887792732e-04f, -3.564076658e-04f,
- /* 16, 9 */ +3.184467584e-04f, -9.702877105e-05f, -1.649860923e-03f, +8.224000281e-04f, +5.409691072e-03f, -2.757662946e-03f, -1.940637710e-02f, -1.497891218e-02f, +1.084570103e-02f, +2.094491570e-02f, +6.009115326e-03f, -5.229172900e-03f, -1.894909522e-03f, +1.663205192e-03f, +3.939497146e-04f, -3.693879948e-04f,
- /* 16,10 */ +2.948629835e-04f, -1.200956190e-05f, -1.607060019e-03f, +4.911465534e-04f, +5.350281937e-03f, -1.756186609e-03f, -1.869471933e-02f, -1.616607109e-02f, +9.306876654e-03f, +2.123698452e-02f, +7.151026424e-03f, -5.038003695e-03f, -2.267013831e-03f, +1.623844377e-03f, +5.040080805e-04f, -3.774404835e-04f,
- /* 16,11 */ +2.691226085e-04f, +6.499930382e-05f, -1.548135431e-03f, +1.779495980e-04f, +5.237819035e-03f, -8.064574864e-04f, -1.789634168e-02f, -1.724526338e-02f, +7.704060609e-03f, +2.140964525e-02f, +8.308496634e-03f, -4.776853126e-03f, -2.639777229e-03f, +1.560284979e-03f, +6.177751723e-04f, -3.798900997e-04f,
- /* 16,12 */ +2.418805517e-04f, +1.336143134e-04f, -1.475048300e-03f, -1.147982409e-04f, +5.076839304e-03f, +8.619500088e-05f, -1.701970579e-02f, -1.821025632e-02f, +6.048090342e-03f, +2.145880202e-02f, +9.472744965e-03f, -4.443873126e-03f, -3.008783957e-03f, +1.471475134e-03f, +7.339202470e-04f, -3.760939096e-04f,
- /* 16,13 */ +2.137577131e-04f, +1.936024914e-04f, -1.389803701e-03f, -3.850930357e-04f, +4.872124601e-03f, +9.171630004e-04f, -1.607371744e-02f, -1.905580462e-02f, +4.350277043e-03f, +2.138131359e-02f, +1.063463040e-02f, -4.037840451e-03f, -3.369379821e-03f, +1.356620316e-03f, +8.509694882e-04f, -3.654544998e-04f,
- /* 16,14 */ +1.853334990e-04f, +2.448738999e-04f, -1.294421768e-03f, -6.313208395e-04f, +4.628640452e-03f, +1.682580557e-03f, -1.506763496e-02f, -1.977768827e-02f, +2.622305580e-03f, +2.117503726e-02f, +1.178473019e-02f, -3.558195313e-03f, -3.716713245e-03f, +1.215210440e-03f, +9.673171402e-04f, -3.474336395e-04f,
- /* 16,15 */ +1.571395541e-04f, +2.874725537e-04f, -1.190910327e-03f, -8.522542940e-04f, +4.351475252e-03f, +2.379335663e-03f, -1.401097590e-02f, -2.037274022e-02f, +8.761305366e-04f, +2.083886340e-02f, +1.291342297e-02f, -3.005074105e-03f, -4.045780572e-03f, +1.047044785e-03f, +1.081239301e-03f, -3.215659774e-04f,
- /* 16, 0 */ +3.737698842e-04f, -2.640449894e-04f, -1.945694549e-03f, +2.599440145e-03f, +5.499552783e-03f, -1.161604587e-02f, -2.473725459e-02f, -1.100298137e-03f, +2.435797715e-02f, +1.306966182e-02f, -5.062036618e-03f, -3.067638325e-03f, +1.905476637e-03f, +3.919780470e-04f, -3.991665042e-04f, +0.000000000e+00f,
- /* 16, 1 */ +3.444161169e-04f, -1.448617079e-04f, -1.955541719e-03f, +2.132654952e-03f, +5.839402648e-03f, -1.015371093e-02f, -2.494083956e-02f, -3.291998323e-03f, +2.380252288e-02f, +1.450063212e-02f, -4.525267650e-03f, -3.530814272e-03f, +1.832921116e-03f, +5.273022833e-04f, -4.195866486e-04f, +0.000000000e+00f,
- /* 16, 2 */ +3.121018536e-04f, -3.553225965e-05f, -1.937280777e-03f, +1.673279684e-03f, +6.084169165e-03f, -8.696195593e-03f, -2.497087446e-02f, -5.457102987e-03f, +2.307209479e-02f, +1.589478690e-02f, -3.888719495e-03f, -3.982156136e-03f, +1.726408630e-03f, +6.684076945e-04f, -4.340068349e-04f, +0.000000000e+00f,
- /* 16, 3 */ +2.777843660e-04f, +6.309259068e-05f, -1.893417136e-03f, +1.226820211e-03f, +6.237358144e-03f, -7.256513778e-03f, -2.483111182e-02f, -7.578189778e-03f, +2.216959356e-02f, +1.723786448e-02f, -3.152978451e-03f, -4.414545490e-03f, +1.584716951e-03f, +8.134406195e-04f, -4.414195390e-04f, +4.634120047e-04f,
- /* 16, 4 */ +2.423668462e-04f, +1.504100330e-04f, -1.826645633e-03f, +7.982477790e-04f, +6.303316031e-03f, -5.847027899e-03f, -2.452684878e-02f, -9.638294429e-03f, +2.109960841e-02f, +1.851566754e-02f, -2.319783877e-03f, -4.820635148e-03f, +1.407067591e-03f, +9.603162700e-04f, -4.408546251e-04f, -2.338334874e-05f,
- /* 16, 5 */ +2.066858426e-04f, +2.260561294e-04f, -1.739797615e-03f, +3.919648528e-04f, +6.287140435e-03f, -4.479333074e-03f, -2.406484480e-02f, -1.162108621e-02f, +1.986838848e-02f, +1.971422226e-02f, -1.392055451e-03f, -5.192933984e-03f, +1.193168502e-03f, +1.106736135e-03f, -4.314017719e-04f, -4.782938304e-05f,
- /* 16, 6 */ +1.715009195e-04f, +2.898933732e-04f, -1.635789255e-03f, +1.178042715e-05f, +6.194584811e-03f, -3.164153635e-03f, -2.345322399e-02f, -1.351103572e-02f, +1.848379497e-02f, +2.081993840e-02f, -3.739065234e-04f, -5.523897843e-03f, +9.432519997e-04f, +1.250210274e-03f, -4.122335402e-04f, -7.520965874e-05f,
- /* 16, 7 */ +1.374865801e-04f, +3.419953130e-04f, -1.517571800e-03f, -3.391052954e-04f, +6.031958779e-03f, -1.911252903e-03f, -2.270136331e-02f, -1.529357304e-02f, +1.695523429e-02f, +2.181976838e-02f, +7.293570292e-04f, -5.806025538e-03f, +6.581070752e-04f, +1.388084428e-03f, -3.826286881e-04f, -1.052264476e-04f,
- /* 16, 8 */ +1.052264476e-04f, +3.826286881e-04f, -1.388084428e-03f, -6.581070752e-04f, +5.806025538e-03f, -7.293570292e-04f, -2.181976838e-02f, -1.695523429e-02f, +1.529357304e-02f, +2.270136331e-02f, +1.911252903e-03f, -6.031958779e-03f, +3.391052954e-04f, +1.517571800e-03f, -3.419953130e-04f, -1.374865801e-04f,
- /* 16, 9 */ +7.520965874e-05f, +4.122335402e-04f, -1.250210274e-03f, -9.432519997e-04f, +5.523897843e-03f, +3.739065234e-04f, -2.081993840e-02f, -1.848379497e-02f, +1.351103572e-02f, +2.345322399e-02f, +3.164153635e-03f, -6.194584811e-03f, -1.178042715e-05f, +1.635789255e-03f, -2.898933732e-04f, -1.715009195e-04f,
- /* 16,10 */ +4.782938304e-05f, +4.314017719e-04f, -1.106736135e-03f, -1.193168502e-03f, +5.192933984e-03f, +1.392055451e-03f, -1.971422226e-02f, -1.986838848e-02f, +1.162108621e-02f, +2.406484480e-02f, +4.479333074e-03f, -6.287140435e-03f, -3.919648528e-04f, +1.739797615e-03f, -2.260561294e-04f, -2.066858426e-04f,
- /* 16,11 */ +2.338334874e-05f, +4.408546251e-04f, -9.603162700e-04f, -1.407067591e-03f, +4.820635148e-03f, +2.319783877e-03f, -1.851566754e-02f, -2.109960841e-02f, +9.638294429e-03f, +2.452684878e-02f, +5.847027899e-03f, -6.303316031e-03f, -7.982477790e-04f, +1.826645633e-03f, -1.504100330e-04f, -2.423668462e-04f,
- /* 16,12 */ -4.634120047e-04f, +4.414195390e-04f, -8.134406195e-04f, -1.584716951e-03f, +4.414545490e-03f, +3.152978451e-03f, -1.723786448e-02f, -2.216959356e-02f, +7.578189778e-03f, +2.483111182e-02f, +7.256513778e-03f, -6.237358144e-03f, -1.226820211e-03f, +1.893417136e-03f, -6.309259068e-05f, -2.777843660e-04f,
- /* 16,13 */ +0.000000000e+00f, +4.340068349e-04f, -6.684076945e-04f, -1.726408630e-03f, +3.982156136e-03f, +3.888719495e-03f, -1.589478690e-02f, -2.307209479e-02f, +5.457102987e-03f, +2.497087446e-02f, +8.696195593e-03f, -6.084169165e-03f, -1.673279684e-03f, +1.937280777e-03f, +3.553225965e-05f, -3.121018536e-04f,
- /* 16,14 */ +0.000000000e+00f, +4.195866486e-04f, -5.273022833e-04f, -1.832921116e-03f, +3.530814272e-03f, +4.525267650e-03f, -1.450063212e-02f, -2.380252288e-02f, +3.291998323e-03f, +2.494083956e-02f, +1.015371093e-02f, -5.839402648e-03f, -2.132654952e-03f, +1.955541719e-03f, +1.448617079e-04f, -3.444161169e-04f,
- /* 16,15 */ +0.000000000e+00f, +3.991665042e-04f, -3.919780470e-04f, -1.905476637e-03f, +3.067638325e-03f, +5.062036618e-03f, -1.306966182e-02f, -2.435797715e-02f, +1.100298137e-03f, +2.473725459e-02f, +1.161604587e-02f, -5.499552783e-03f, -2.599440145e-03f, +1.945694549e-03f, +2.640449894e-04f, -3.737698842e-04f,
- /* 16, 0 */ +1.129954761e-04f, +3.969443331e-04f, -1.897918409e-03f, +5.803605804e-04f, +7.236474393e-03f, -9.385964725e-03f, -2.874576735e-02f, -1.359743295e-03f, +2.853093461e-02f, +1.118139232e-02f, -7.102956997e-03f, -1.091735034e-03f, +2.023521864e-03f, -3.328791130e-04f, -1.523656204e-04f, +0.000000000e+00f,
- /* 16, 1 */ +7.672972562e-05f, +4.458313625e-04f, -1.753034729e-03f, +1.022461377e-04f, +7.257902118e-03f, -7.620475041e-03f, -2.873131200e-02f, -4.066570788e-03f, +2.808336987e-02f, +1.298853535e-02f, -6.850603202e-03f, -1.630677017e-03f, +2.125649126e-03f, -2.532507071e-04f, -1.941703587e-04f, +0.000000000e+00f,
- /* 16, 2 */ +4.409159553e-05f, +4.801879450e-04f, -1.593056257e-03f, -3.378401438e-04f, +7.175055211e-03f, -5.902082228e-03f, -2.849345261e-02f, -6.735572940e-03f, +2.740215275e-02f, +1.478830973e-02f, -6.473886365e-03f, -2.190599691e-03f, +2.200171639e-03f, -1.579695096e-04f, -2.375950982e-04f, +0.000000000e+00f,
- /* 16, 3 */ -4.855802427e-04f, +5.008892512e-04f, -1.422085430e-03f, -7.360682089e-04f, +6.996656391e-03f, -4.246700138e-03f, -2.804039906e-02f, -9.342037235e-03f, +2.648893755e-02f, +1.656098957e-02f, -5.968640123e-03f, -2.764068406e-03f, +2.243110553e-03f, -4.727037370e-05f, -2.816859426e-04f, +0.000000000e+00f,
- /* 16, 4 */ +0.000000000e+00f, +5.090007623e-04f, -1.244075649e-03f, -1.089544232e-03f, +6.732169339e-03f, -2.668838337e-03f, -2.738254409e-02f, -1.186200034e-02f, +2.534797081e-02f, +1.828644204e-02f, -5.332184360e-03f, -3.342863857e-03f, +2.250722132e-03f, +7.826276448e-05f, -3.253590588e-04f, +0.000000000e+00f,
- /* 16, 5 */ +0.000000000e+00f, +5.057402285e-04f, -1.062772468e-03f, -1.396293958e-03f, +6.391628670e-03f, -1.181467029e-03f, -2.653229393e-02f, -1.427253312e-02f, +2.398607480e-02f, +1.994437434e-02f, -4.563434465e-03f, -3.918061651e-03f, +2.219584858e-03f, +2.176775461e-04f, -3.674140144e-04f, +0.000000000e+00f,
- /* 16, 6 */ +0.000000000e+00f, +4.924395299e-04f, -8.816626027e-04f, -1.655232286e-03f, +5.985469320e-03f, +2.040926394e-04f, -2.550387540e-02f, -1.655201127e-02f, +2.241259678e-02f, +2.151458926e-02f, -3.662991573e-03f, -4.480127768e-03f, +2.146686747e-03f, +3.696399185e-04f, -4.065510896e-04f, +0.000000000e+00f,
- /* 16, 7 */ +0.000000000e+00f, +4.705072223e-04f, -7.039312026e-04f, -1.866120323e-03f, +5.524357980e-03f, +1.478252020e-03f, -2.431312252e-02f, -1.868036788e-02f, +2.063932435e-02f, +2.297724601e-02f, -2.633211707e-03f, -5.019029099e-03f, +2.029511283e-03f, +5.324276437e-04f, -4.413924818e-04f, +0.000000000e+00f,
- /* 16, 8 */ +0.000000000e+00f, +4.413924818e-04f, -5.324276437e-04f, -2.029511283e-03f, +5.019029099e-03f, +2.633211707e-03f, -2.297724601e-02f, -2.063932435e-02f, +1.868036788e-02f, +2.431312252e-02f, -1.478252020e-03f, -5.524357980e-03f, +1.866120323e-03f, +7.039312026e-04f, -4.705072223e-04f, +0.000000000e+00f,
- /* 16, 9 */ +0.000000000e+00f, +4.065510896e-04f, -3.696399185e-04f, -2.146686747e-03f, +4.480127768e-03f, +3.662991573e-03f, -2.151458926e-02f, -2.241259678e-02f, +1.655201127e-02f, +2.550387540e-02f, -2.040926394e-04f, -5.985469320e-03f, +1.655232286e-03f, +8.816626027e-04f, -4.924395299e-04f, +0.000000000e+00f,
- /* 16,10 */ +0.000000000e+00f, +3.674140144e-04f, -2.176775461e-04f, -2.219584858e-03f, +3.918061651e-03f, +4.563434465e-03f, -1.994437434e-02f, -2.398607480e-02f, +1.427253312e-02f, +2.653229393e-02f, +1.181467029e-03f, -6.391628670e-03f, +1.396293958e-03f, +1.062772468e-03f, -5.057402285e-04f, +0.000000000e+00f,
- /* 16,11 */ +0.000000000e+00f, +3.253590588e-04f, -7.826276448e-05f, -2.250722132e-03f, +3.342863857e-03f, +5.332184360e-03f, -1.828644204e-02f, -2.534797081e-02f, +1.186200034e-02f, +2.738254409e-02f, +2.668838337e-03f, -6.732169339e-03f, +1.089544232e-03f, +1.244075649e-03f, -5.090007623e-04f, +0.000000000e+00f,
- /* 16,12 */ +0.000000000e+00f, +2.816859426e-04f, +4.727037370e-05f, -2.243110553e-03f, +2.764068406e-03f, +5.968640123e-03f, -1.656098957e-02f, -2.648893755e-02f, +9.342037235e-03f, +2.804039906e-02f, +4.246700138e-03f, -6.996656391e-03f, +7.360682089e-04f, +1.422085430e-03f, -5.008892512e-04f, +4.855802427e-04f,
- /* 16,13 */ +0.000000000e+00f, +2.375950982e-04f, +1.579695096e-04f, -2.200171639e-03f, +2.190599691e-03f, +6.473886365e-03f, -1.478830973e-02f, -2.740215275e-02f, +6.735572940e-03f, +2.849345261e-02f, +5.902082228e-03f, -7.175055211e-03f, +3.378401438e-04f, +1.593056257e-03f, -4.801879450e-04f, -4.409159553e-05f,
- /* 16,14 */ +0.000000000e+00f, +1.941703587e-04f, +2.532507071e-04f, -2.125649126e-03f, +1.630677017e-03f, +6.850603202e-03f, -1.298853535e-02f, -2.808336987e-02f, +4.066570788e-03f, +2.873131200e-02f, +7.620475041e-03f, -7.257902118e-03f, -1.022461377e-04f, +1.753034729e-03f, -4.458313625e-04f, -7.672972562e-05f,
- /* 16,15 */ +0.000000000e+00f, +1.523656204e-04f, +3.328791130e-04f, -2.023521864e-03f, +1.091735034e-03f, +7.102956997e-03f, -1.118139232e-02f, -2.853093461e-02f, +1.359743295e-03f, +2.874576735e-02f, +9.385964725e-03f, -7.236474393e-03f, -5.803605804e-04f, +1.897918409e-03f, -3.969443331e-04f, -1.129954761e-04f,
- /* 12, 0 */ -1.111572639e-03f, -1.388266820e-03f, +7.900037037e-03f, -6.320860170e-03f, -3.276049121e-02f, -1.657033928e-03f, +3.280306521e-02f, +8.402419704e-03f, -8.147060845e-03f, +9.779320530e-04f, +1.332890330e-03f, -5.705687800e-04f,
- /* 12, 1 */ -8.925738220e-04f, -1.737094155e-03f, +7.544883174e-03f, -4.325531156e-03f, -3.242830266e-02f, -4.953502968e-03f, +3.254754550e-02f, +1.054842384e-02f, -8.272560314e-03f, +5.083818205e-04f, +1.551863117e-03f, -5.805594968e-04f,
- /* 12, 2 */ -6.800837112e-04f, -2.023419107e-03f, +7.095763901e-03f, -2.436087367e-03f, -3.181840805e-02f, -8.197416535e-03f, +3.198906769e-02f, +1.273520115e-02f, -8.264181830e-03f, -1.673924732e-05f, +1.763414955e-03f, -5.764989135e-04f,
- /* 12, 3 */ -4.777710304e-04f, -2.247467778e-03f, +6.567344318e-03f, -6.698479312e-04f, -3.094591156e-02f, -1.135453705e-02f, +3.112651563e-02f, +1.493747887e-02f, -8.110878239e-03f, -5.924008684e-04f, +1.962133432e-03f, -5.566237283e-04f,
- /* 12, 4 */ -2.887507612e-04f, -2.410593616e-03f, +5.974525144e-03f, +9.583644900e-04f, -2.982883555e-02f, -1.439181272e-02f, +2.996260004e-02f, +1.712870168e-02f, -7.803165138e-03f, -1.212178752e-03f, +2.142359210e-03f, -5.193755958e-04f,
- /* 12, 5 */ -1.155657138e-04f, -2.515168800e-03f, +5.332187403e-03f, +2.436339775e-03f, -2.848780461e-02f, -1.727782533e-02f, +2.850388027e-02f, +1.928138098e-02f, -7.333362623e-03f, -1.868272468e-03f, +2.298288008e-03f, -4.634616378e-04f,
- /* 12, 6 */ +3.981829456e-05f, -2.564463655e-03f, +4.654950666e-03f, +3.754551105e-03f, -2.694569661e-02f, -1.998321224e-02f, +2.676072816e-02f, +2.136746929e-02f, -6.695817566e-03f, -2.551550686e-03f, +2.424083786e-03f, -3.879138191e-04f,
- /* 12, 7 */ +1.760045094e-04f, -2.562517141e-03f, +3.956948521e-03f, +4.906180706e-03f, -2.522726725e-02f, -2.248105576e-02f, +2.474723407e-02f, +2.335875442e-02f, -5.887101657e-03f, -3.251624436e-03f, +2.514001460e-03f, -2.921456350e-04f,
- /* 12, 8 */ +2.921456350e-04f, -2.514001460e-03f, +3.251624436e-03f, +5.887101657e-03f, -2.335875442e-02f, -2.474723407e-02f, +2.248105576e-02f, +2.522726725e-02f, -4.906180706e-03f, -3.956948521e-03f, +2.562517141e-03f, -1.760045094e-04f,
- /* 12, 9 */ +3.879138191e-04f, -2.424083786e-03f, +2.551550686e-03f, +6.695817566e-03f, -2.136746929e-02f, -2.676072816e-02f, +1.998321224e-02f, +2.694569661e-02f, -3.754551105e-03f, -4.654950666e-03f, +2.564463655e-03f, -3.981829456e-05f,
- /* 12,10 */ +4.634616378e-04f, -2.298288008e-03f, +1.868272468e-03f, +7.333362623e-03f, -1.928138098e-02f, -2.850388027e-02f, +1.727782533e-02f, +2.848780461e-02f, -2.436339775e-03f, -5.332187403e-03f, +2.515168800e-03f, +1.155657138e-04f,
- /* 12,11 */ +5.193755958e-04f, -2.142359210e-03f, +1.212178752e-03f, +7.803165138e-03f, -1.712870168e-02f, -2.996260004e-02f, +1.439181272e-02f, +2.982883555e-02f, -9.583644900e-04f, -5.974525144e-03f, +2.410593616e-03f, +2.887507612e-04f,
- /* 12,12 */ +5.566237283e-04f, -1.962133432e-03f, +5.924008684e-04f, +8.110878239e-03f, -1.493747887e-02f, -3.112651563e-02f, +1.135453705e-02f, +3.094591156e-02f, +6.698479312e-04f, -6.567344318e-03f, +2.247467778e-03f, +4.777710304e-04f,
- /* 12,13 */ +5.764989135e-04f, -1.763414955e-03f, +1.673924732e-05f, +8.264181830e-03f, -1.273520115e-02f, -3.198906769e-02f, +8.197416535e-03f, +3.181840805e-02f, +2.436087367e-03f, -7.095763901e-03f, +2.023419107e-03f, +6.800837112e-04f,
- /* 12,14 */ +5.805594968e-04f, -1.551863117e-03f, -5.083818205e-04f, +8.272560314e-03f, -1.054842384e-02f, -3.254754550e-02f, +4.953502968e-03f, +3.242830266e-02f, +4.325531156e-03f, -7.544883174e-03f, +1.737094155e-03f, +8.925738220e-04f,
- /* 12,15 */ +5.705687800e-04f, -1.332890330e-03f, -9.779320530e-04f, +8.147060845e-03f, -8.402419704e-03f, -3.280306521e-02f, +1.657033928e-03f, +3.276049121e-02f, +6.320860170e-03f, -7.900037037e-03f, +1.388266820e-03f, +1.111572639e-03f,
- /* 12, 0 */ -1.054803383e-04f, -2.744858958e-03f, +7.370914553e-03f, -2.604494739e-03f, -3.666896703e-02f, -1.994735221e-03f, +3.707596726e-02f, +4.873177855e-03f, -8.012348726e-03f, +2.554514858e-03f, +3.117958677e-04f, -3.625540242e-04f,
- /* 12, 1 */ +7.775923585e-05f, -2.860662969e-03f, +6.648820026e-03f, -4.950753709e-04f, -3.590728113e-02f, -5.960234251e-03f, +3.711196787e-02f, +7.277671607e-03f, -8.552819277e-03f, +2.286292242e-03f, +5.385913036e-04f, -4.265219564e-04f,
- /* 12, 2 */ +2.361482435e-04f, -2.906545541e-03f, +5.866306097e-03f, +1.435258618e-03f, -3.481183398e-02f, -9.854190425e-03f, +3.676562700e-02f, +9.791136525e-03f, -8.972352970e-03f, +1.938320040e-03f, +7.824350015e-04f, -4.875429386e-04f,
- /* 12, 3 */ +3.687004846e-04f, -2.888204359e-03f, +5.043181884e-03f, +3.170488652e-03f, -3.340772759e-02f, -1.363013975e-02f, +3.603085731e-02f, +1.238366322e-02f, -9.251714734e-03f, +1.510362297e-03f, +1.039066351e-03f, -5.431259501e-04f,
- /* 12, 4 */ +4.751685216e-04f, -2.812206203e-03f, +4.198484259e-03f, +4.698496481e-03f, -3.172374637e-02f, -1.724344185e-02f, +3.490702283e-02f, +1.502265637e-02f, -9.372823891e-03f, +1.003961424e-03f, +1.303424694e-03f, -5.906251216e-04f,
- /* 12, 5 */ +5.559799195e-04f, -2.685773447e-03f, +3.350171906e-03f, +6.011087921e-03f, -2.979181001e-02f, -2.065196332e-02f, +3.339904530e-02f, +1.767328103e-02f, -9.319170237e-03f, +4.225520445e-04f, +1.569701578e-03f, -6.273034189e-04f,
- /* 12, 6 */ +6.121620582e-04f, -2.516571985e-03f, +2.514858275e-03f, +7.103945228e-03f, -2.764638515e-02f, -2.381671619e-02f, +3.151741694e-02f, +2.029896461e-02f, -9.076221424e-03f, -2.284590483e-04f, +1.831416829e-03f, -6.504054889e-04f,
- /* 12, 7 */ +6.452583046e-04f, -2.312505227e-03f, +1.707586806e-03f, +7.976511649e-03f, -2.532386758e-02f, -2.670244010e-02f, +2.927811806e-02f, +2.286194672e-02f, -8.631812899e-03f, -9.416507761e-04f, +2.081518390e-03f, -6.572383529e-04f,
- /* 12, 8 */ +6.572383529e-04f, -2.081518390e-03f, +9.416507761e-04f, +8.631812899e-03f, -2.286194672e-02f, -2.927811806e-02f, +2.670244010e-02f, +2.532386758e-02f, -7.976511649e-03f, -1.707586806e-03f, +2.312505227e-03f, -6.452583046e-04f,
- /* 12, 9 */ +6.504054889e-04f, -1.831416829e-03f, +2.284590483e-04f, +9.076221424e-03f, -2.029896461e-02f, -3.151741694e-02f, +2.381671619e-02f, +2.764638515e-02f, -7.103945228e-03f, -2.514858275e-03f, +2.516571985e-03f, -6.121620582e-04f,
- /* 12,10 */ +6.273034189e-04f, -1.569701578e-03f, -4.225520445e-04f, +9.319170237e-03f, -1.767328103e-02f, -3.339904530e-02f, +2.065196332e-02f, +2.979181001e-02f, -6.011087921e-03f, -3.350171906e-03f, +2.685773447e-03f, -5.559799195e-04f,
- /* 12,11 */ +5.906251216e-04f, -1.303424694e-03f, -1.003961424e-03f, +9.372823891e-03f, -1.502265637e-02f, -3.490702283e-02f, +1.724344185e-02f, +3.172374637e-02f, -4.698496481e-03f, -4.198484259e-03f, +2.812206203e-03f, -4.751685216e-04f,
- /* 12,12 */ +5.431259501e-04f, -1.039066351e-03f, -1.510362297e-03f, +9.251714734e-03f, -1.238366322e-02f, -3.603085731e-02f, +1.363013975e-02f, +3.340772759e-02f, -3.170488652e-03f, -5.043181884e-03f, +2.888204359e-03f, -3.687004846e-04f,
- /* 12,13 */ +4.875429386e-04f, -7.824350015e-04f, -1.938320040e-03f, +8.972352970e-03f, -9.791136525e-03f, -3.676562700e-02f, +9.854190425e-03f, +3.481183398e-02f, -1.435258618e-03f, -5.866306097e-03f, +2.906545541e-03f, -2.361482435e-04f,
- /* 12,14 */ +4.265219564e-04f, -5.385913036e-04f, -2.286292242e-03f, +8.552819277e-03f, -7.277671607e-03f, -3.711196787e-02f, +5.960234251e-03f, +3.590728113e-02f, +4.950753709e-04f, -6.648820026e-03f, +2.860662969e-03f, -7.775923585e-05f,
- /* 12,15 */ +3.625540242e-04f, -3.117958677e-04f, -2.554514858e-03f, +8.012348726e-03f, -4.873177855e-03f, -3.707596726e-02f, +1.994735221e-03f, +3.666896703e-02f, +2.604494739e-03f, -7.370914553e-03f, +2.744858958e-03f, +1.054803383e-04f,
- /* 12, 0 */ +6.110448771e-04f, -3.173989705e-03f, +5.751223243e-03f, +1.507555794e-03f, -4.035343888e-02f, -2.375409442e-03f, +4.124383193e-02f, +8.091337269e-04f, -6.726716888e-03f, +3.253952459e-03f, -5.087325269e-04f, -4.127854608e-05f,
- /* 12, 1 */ +6.820041984e-04f, -3.029137857e-03f, +4.746351538e-03f, +3.578915017e-03f, -3.904147991e-02f, -7.094160720e-03f, +4.168490752e-02f, +3.349306133e-03f, -7.647219355e-03f, +3.259488816e-03f, -3.738470149e-04f, -9.536054020e-05f,
- /* 12, 2 */ +7.235832870e-04f, -2.829602454e-03f, +3.736224000e-03f, +5.388621830e-03f, -3.734163661e-02f, -1.171726725e-02f, +4.165549067e-02f, +6.085743956e-03f, -8.486093037e-03f, +3.182049180e-03f, -2.060445111e-04f, -1.573545891e-04f,
- /* 12, 3 */ +7.383739029e-04f, -2.585946508e-03f, +2.743066911e-03f, +6.925917423e-03f, -3.529277498e-02f, -1.618281485e-02f, +4.114151479e-02f, +8.986133221e-03f, -9.216195947e-03f, +3.014391908e-03f, -5.966328360e-06f, -2.260166200e-04f,
- /* 12, 4 */ +7.294476853e-04f, -2.308795686e-03f, +1.786872733e-03f, +8.185530694e-03f, -3.293811768e-02f, -2.043162352e-02f, +4.013642610e-02f, +1.201345370e-02f, -9.810446156e-03f, +2.750895834e-03f, +2.246717082e-04f, -2.996559917e-04f,
- /* 12, 5 */ +7.002161546e-04f, -2.008565767e-03f, +8.851333656e-04f, +9.167495079e-03f, -3.032435275e-02f, -2.440826356e-02f, +3.864144993e-02f, +1.512648157e-02f, -1.024241966e-02f, +2.387856219e-03f, +4.830138128e-04f, -3.761418846e-04f,
- /* 12, 6 */ +6.542939604e-04f, -1.695217727e-03f, +5.264660367e-05f, +9.876866054e-03f, -2.750069849e-02f, -2.806199617e-02f, +3.666571148e-02f, +1.828039745e-02f, -1.048696943e-02f, +1.923755148e-03f, +7.650267923e-04f, -4.529261561e-04f,
- /* 12, 7 */ +5.953691186e-04f, -1.378044726e-03f, -6.986040124e-04f, +1.032334944e-02f, -2.451794467e-02f, -3.134761990e-02f, +3.422620641e-02f, +2.142749023e-02f, -1.052085229e-02f, +1.359497506e-03f, +1.065494162e-03f, -5.270834853e-04f,
- /* 12, 8 */ +5.270834853e-04f, -1.065494162e-03f, -1.359497506e-03f, +1.052085229e-02f, -2.142749023e-02f, -3.422620641e-02f, +3.134761990e-02f, +2.451794467e-02f, -1.032334944e-02f, +6.986040124e-04f, +1.378044726e-03f, -5.953691186e-04f,
- /* 12, 9 */ +4.529261561e-04f, -7.650267923e-04f, -1.923755148e-03f, +1.048696943e-02f, -1.828039745e-02f, -3.666571148e-02f, +2.806199617e-02f, +2.750069849e-02f, -9.876866054e-03f, -5.264660367e-05f, +1.695217727e-03f, -6.542939604e-04f,
- /* 12,10 */ +3.761418846e-04f, -4.830138128e-04f, -2.387856219e-03f, +1.024241966e-02f, -1.512648157e-02f, -3.864144993e-02f, +2.440826356e-02f, +3.032435275e-02f, -9.167495079e-03f, -8.851333656e-04f, +2.008565767e-03f, -7.002161546e-04f,
- /* 12,11 */ +2.996559917e-04f, -2.246717082e-04f, -2.750895834e-03f, +9.810446156e-03f, -1.201345370e-02f, -4.013642610e-02f, +2.043162352e-02f, +3.293811768e-02f, -8.185530694e-03f, -1.786872733e-03f, +2.308795686e-03f, -7.294476853e-04f,
- /* 12,12 */ +2.260166200e-04f, +5.966328360e-06f, -3.014391908e-03f, +9.216195947e-03f, -8.986133221e-03f, -4.114151479e-02f, +1.618281485e-02f, +3.529277498e-02f, -6.925917423e-03f, -2.743066911e-03f, +2.585946508e-03f, -7.383739029e-04f,
- /* 12,13 */ +1.573545891e-04f, +2.060445111e-04f, -3.182049180e-03f, +8.486093037e-03f, -6.085743956e-03f, -4.165549067e-02f, +1.171726725e-02f, +3.734163661e-02f, -5.388621830e-03f, -3.736224000e-03f, +2.829602454e-03f, -7.235832870e-04f,
- /* 12,14 */ +9.536054020e-05f, +3.738470149e-04f, -3.259488816e-03f, +7.647219355e-03f, -3.349306133e-03f, -4.168490752e-02f, +7.094160720e-03f, +3.904147991e-02f, -3.578915017e-03f, -4.746351538e-03f, +3.029137857e-03f, -6.820041984e-04f,
- /* 12,15 */ +4.127854608e-05f, +5.087325269e-04f, -3.253952459e-03f, +6.726716888e-03f, -8.091337269e-04f, -4.124383193e-02f, +2.375409442e-03f, +4.035343888e-02f, -1.507555794e-03f, -5.751223243e-03f, +3.173989705e-03f, -6.110448771e-04f,
-
- /* 24, 0 */ -8.820438069e-05f, -1.519461079e-04f, -2.301651496e-04f, -3.149320871e-04f, -3.945939739e-04f, -4.554410135e-04f, -4.841532882e-04f, -4.705408991e-04f, -4.099602091e-04f, -3.048100066e-04f, -1.646897470e-04f, -5.099007530e-06f, +1.551006323e-04f, +2.969416536e-04f, +4.046294158e-04f, +4.681429482e-04f, +4.846228261e-04f, +4.583040637e-04f, +3.990939388e-04f, +3.201968846e-04f, +2.353759082e-04f, +1.564712483e-04f, +9.167483068e-05f, +4.482688286e-05f,
- /* 24, 1 */ -8.480575132e-05f, -1.474789784e-04f, -2.249812225e-04f, -3.096480504e-04f, -3.900204007e-04f, -4.524514078e-04f, -4.835165803e-04f, -4.727530367e-04f, -4.151145025e-04f, -3.125397891e-04f, -1.742016828e-04f, -1.529460870e-05f, +1.454387449e-04f, +2.889379628e-04f, +3.991236794e-04f, +4.655589110e-04f, +4.849233000e-04f, +4.610375470e-04f, +4.035168325e-04f, +3.254391996e-04f, +2.406110065e-04f, +1.610529558e-04f, +9.521673594e-05f, +4.721513201e-05f,
- /* 24, 2 */ -8.147924507e-05f, -1.430712350e-04f, -2.198265592e-04f, -3.043479843e-04f, -3.853766873e-04f, -4.493383067e-04f, -4.827146831e-04f, -4.747797448e-04f, -4.200908527e-04f, -3.201278616e-04f, -1.836320864e-04f, -2.548296987e-05f, +1.357085413e-04f, +2.808022583e-04f, +3.934446700e-04f, +4.627886263e-04f, +4.850529052e-04f, +4.636385032e-04f, +4.078592000e-04f, +3.306557574e-04f, +2.458678944e-04f, +1.656897170e-04f, +9.882966748e-05f, +4.967415993e-05f,
- /* 24, 3 */ -7.822510242e-05f, -1.387241832e-04f, -2.147035314e-04f, -2.990350629e-04f, -3.806663042e-04f, -4.461048161e-04f, -4.817496625e-04f, -4.766215175e-04f, -4.248879309e-04f, -3.275711800e-04f, -1.929766610e-04f, -3.565926997e-05f, +1.259145254e-04f, +2.725379532e-04f, +3.875941701e-04f, +4.598320457e-04f, +4.850099279e-04f, +4.661040260e-04f, +4.121175966e-04f, +3.358432542e-04f, +2.511439643e-04f, +1.703799499e-04f, +1.025131319e-04f, +5.220438912e-05f,
- /* 24, 4 */ -7.504350274e-05f, -1.344390595e-04f, -2.096144489e-04f, -2.937124231e-04f, -3.758927218e-04f, -4.427540852e-04f, -4.806236671e-04f, -4.782789577e-04f, -4.295045226e-04f, -3.348667971e-04f, -2.022311686e-04f, -4.581869630e-05f, +1.160612449e-04f, +2.641485480e-04f, +3.815740737e-04f, +4.566892339e-04f, +4.847927474e-04f, +4.684312656e-04f, +4.162885910e-04f, +3.409983591e-04f, +2.564365532e-04f, +1.751220037e-04f, +1.062665706e-04f, +5.480619333e-05f,
- /* 24, 5 */ -7.193456522e-05f, -1.302170312e-04f, -2.045615590e-04f, -2.883831622e-04f, -3.710594077e-04f, -4.392893036e-04f, -4.793389263e-04f, -4.797527765e-04f, -4.339395286e-04f, -3.420118645e-04f, -2.113914331e-04f, -5.595644787e-05f, +1.061532886e-04f, +2.556376279e-04f, +3.753863858e-04f, +4.533603695e-04f, +4.843998374e-04f, +4.706174312e-04f, +4.203687678e-04f, +3.461177167e-04f, +2.617429433e-04f, +1.799141593e-04f, +1.100893595e-04f, +5.747989630e-05f,
- /* 24, 6 */ -6.889834987e-05f, -1.260591965e-04f, -1.995470454e-04f, -2.830503358e-04f, -3.661698243e-04f, -4.357136989e-04f, -4.778977479e-04f, -4.810437916e-04f, -4.381919648e-04f, -3.490036345e-04f, -2.204533432e-04f, -6.606773875e-05f, +9.619528314e-05f, +2.470088608e-04f, +3.690332209e-04f, +4.498457452e-04f, +4.838297683e-04f, +4.726597937e-04f, +4.243547301e-04f, +3.511979487e-04f, +2.670603639e-04f, +1.847546294e-04f, +1.139808078e-04f, +6.022577049e-05f,
- /* 24, 7 */ -6.593485851e-05f, -1.219665852e-04f, -1.945730275e-04f, -2.777169567e-04f, -3.612274261e-04f, -4.320305335e-04f, -4.763025159e-04f, -4.821529264e-04f, -4.422609626e-04f, -3.558394612e-04f, -2.294128549e-04f, -7.614780136e-05f, +8.619188981e-05f, +2.382659945e-04f, +3.625168024e-04f, +4.461457687e-04f, +4.830812085e-04f, +4.745556880e-04f, +4.282431024e-04f, +3.562356572e-04f, +2.723859925e-04f, +1.896415594e-04f, +1.179401580e-04f, +6.304403582e-05f,
- /* 24, 8 */ -6.304403582e-05f, -1.179401580e-04f, -1.896415594e-04f, -2.723859925e-04f, -3.562356572e-04f, -4.282431024e-04f, -4.745556880e-04f, -4.830812085e-04f, -4.461457687e-04f, -3.625168024e-04f, -2.382659945e-04f, -8.619188981e-05f, +7.614780136e-05f, +2.294128549e-04f, +3.558394612e-04f, +4.422609626e-04f, +4.821529264e-04f, +4.763025159e-04f, +4.320305335e-04f, +3.612274261e-04f, +2.777169567e-04f, +1.945730275e-04f, +1.219665852e-04f, +6.593485851e-05f,
- /* 24, 9 */ -6.022577049e-05f, -1.139808078e-04f, -1.847546294e-04f, -2.670603639e-04f, -3.511979487e-04f, -4.243547301e-04f, -4.726597937e-04f, -4.838297683e-04f, -4.498457452e-04f, -3.690332209e-04f, -2.470088608e-04f, -9.619528314e-05f, +6.606773875e-05f, +2.204533432e-04f, +3.490036345e-04f, +4.381919648e-04f, +4.810437916e-04f, +4.778977479e-04f, +4.357136989e-04f, +3.661698243e-04f, +2.830503358e-04f, +1.995470454e-04f, +1.260591965e-04f, +6.889834987e-05f,
- /* 24,10 */ -5.747989630e-05f, -1.100893595e-04f, -1.799141593e-04f, -2.617429433e-04f, -3.461177167e-04f, -4.203687678e-04f, -4.706174312e-04f, -4.843998374e-04f, -4.533603695e-04f, -3.753863858e-04f, -2.556376279e-04f, -1.061532886e-04f, +5.595644787e-05f, +2.113914331e-04f, +3.420118645e-04f, +4.339395286e-04f, +4.797527765e-04f, +4.793389263e-04f, +4.392893036e-04f, +3.710594077e-04f, +2.883831622e-04f, +2.045615590e-04f, +1.302170312e-04f, +7.193456522e-05f,
- /* 24,11 */ -5.480619333e-05f, -1.062665706e-04f, -1.751220037e-04f, -2.564365532e-04f, -3.409983591e-04f, -4.162885910e-04f, -4.684312656e-04f, -4.847927474e-04f, -4.566892339e-04f, -3.815740737e-04f, -2.641485480e-04f, -1.160612449e-04f, +4.581869630e-05f, +2.022311686e-04f, +3.348667971e-04f, +4.295045226e-04f, +4.782789577e-04f, +4.806236671e-04f, +4.427540852e-04f, +3.758927218e-04f, +2.937124231e-04f, +2.096144489e-04f, +1.344390595e-04f, +7.504350274e-05f,
- /* 24,12 */ -5.220438912e-05f, -1.025131319e-04f, -1.703799499e-04f, -2.511439643e-04f, -3.358432542e-04f, -4.121175966e-04f, -4.661040260e-04f, -4.850099279e-04f, -4.598320457e-04f, -3.875941701e-04f, -2.725379532e-04f, -1.259145254e-04f, +3.565926997e-05f, +1.929766610e-04f, +3.275711800e-04f, +4.248879309e-04f, +4.766215175e-04f, +4.817496625e-04f, +4.461048161e-04f, +3.806663042e-04f, +2.990350629e-04f, +2.147035314e-04f, +1.387241832e-04f, +7.822510242e-05f,
- /* 24,13 */ -4.967415993e-05f, -9.882966748e-05f, -1.656897170e-04f, -2.458678944e-04f, -3.306557574e-04f, -4.078592000e-04f, -4.636385032e-04f, -4.850529052e-04f, -4.627886263e-04f, -3.934446700e-04f, -2.808022583e-04f, -1.357085413e-04f, +2.548296987e-05f, +1.836320864e-04f, +3.201278616e-04f, +4.200908527e-04f, +4.747797448e-04f, +4.827146831e-04f, +4.493383067e-04f, +3.853766873e-04f, +3.043479843e-04f, +2.198265592e-04f, +1.430712350e-04f, +8.147924507e-05f,
- /* 24,14 */ -4.721513201e-05f, -9.521673594e-05f, -1.610529558e-04f, -2.406110065e-04f, -3.254391996e-04f, -4.035168325e-04f, -4.610375470e-04f, -4.849233000e-04f, -4.655589110e-04f, -3.991236794e-04f, -2.889379628e-04f, -1.454387449e-04f, +1.529460870e-05f, +1.742016828e-04f, +3.125397891e-04f, +4.151145025e-04f, +4.727530367e-04f, +4.835165803e-04f, +4.524514078e-04f, +3.900204007e-04f, +3.096480504e-04f, +2.249812225e-04f, +1.474789784e-04f, +8.480575132e-05f,
- /* 24,15 */ -4.482688286e-05f, -9.167483068e-05f, -1.564712483e-04f, -2.353759082e-04f, -3.201968846e-04f, -3.990939388e-04f, -4.583040637e-04f, -4.846228261e-04f, -4.681429482e-04f, -4.046294158e-04f, -2.969416536e-04f, -1.551006323e-04f, +5.099007530e-06f, +1.646897470e-04f, +3.048100066e-04f, +4.099602091e-04f, +4.705408991e-04f, +4.841532882e-04f, +4.554410135e-04f, +3.945939739e-04f, +3.149320871e-04f, +2.301651496e-04f, +1.519461079e-04f, +8.820438069e-05f,
- /* 24, 0 */ +1.254177052e-04f, +1.432187562e-04f, +1.041598752e-04f, -1.135750248e-05f, -2.010663923e-04f, -4.345091125e-04f, -6.566280172e-04f, -8.018070806e-04f, -8.145094672e-04f, -6.693628869e-04f, -3.829411831e-04f, -1.208738944e-05f, +3.614691013e-04f, +6.551938988e-04f, +8.101126455e-04f, +8.069330100e-04f, +6.686285441e-04f, +4.493898115e-04f, +2.148422063e-04f, +2.121126661e-05f, -9.928779545e-05f, -1.427235715e-04f, -1.276505127e-04f, -8.319130160e-05f,
- /* 24, 1 */ +1.230784715e-04f, +1.434886692e-04f, +1.087259386e-04f, -1.803144714e-06f, -1.874698746e-04f, -4.195879132e-04f, -6.443236569e-04f, -7.961535056e-04f, -8.182754723e-04f, -6.829663304e-04f, -4.040749814e-04f, -3.625133679e-05f, +3.396771574e-04f, +6.404692089e-04f, +8.050839212e-04f, +8.115205881e-04f, +6.803091718e-04f, +4.642140510e-04f, +2.287861157e-04f, +3.136033560e-05f, -9.410712043e-05f, -1.419963918e-04f, -1.297693532e-04f, -8.627587811e-05f,
- /* 24, 2 */ +1.206403004e-04f, +1.435401825e-04f, +1.129889134e-04f, +7.448162127e-06f, -1.740634498e-04f, -4.046419937e-04f, -6.317316839e-04f, -7.899834729e-04f, -8.214124236e-04f, -6.959950382e-04f, -4.248524782e-04f, -6.038280262e-05f, +3.175841545e-04f, +6.251993025e-04f, +7.994228899e-04f, +8.155596091e-04f, +6.916540113e-04f, +4.789657110e-04f, +2.428865252e-04f, +4.180014906e-05f, -8.861563067e-05f, -1.410306561e-04f, -1.317666448e-04f, -8.934972901e-05f,
- /* 24, 3 */ +1.181106179e-04f, +1.433803035e-04f, +1.169520657e-04f, +1.639322797e-05f, -1.608575022e-04f, -3.896869361e-04f, -6.188684520e-04f, -7.833086355e-04f, -8.239227539e-04f, -7.084404793e-04f, -4.452560799e-04f, -8.446017611e-05f, +2.952092559e-04f, +6.093952965e-04f, +7.931298338e-04f, +8.190403838e-04f, +7.026473724e-04f, +4.936285297e-04f, +2.571314547e-04f, +5.252568657e-05f, -8.281147599e-05f, -1.398199793e-04f, -1.336347757e-04f, -9.240689021e-05f,
- /* 24, 4 */ +1.154967766e-04f, +1.430161614e-04f, +1.206189886e-04f, +2.502931407e-05f, -1.478619956e-04f, -3.747381071e-04f, -6.057504254e-04f, -7.761410928e-04f, -8.258095592e-04f, -7.202947906e-04f, -4.652686374e-04f, -1.084619116e-04f, +2.725719623e-04f, +5.930689291e-04f, +7.862057246e-04f, +8.219537553e-04f, +7.132737861e-04f, +5.081861235e-04f, +2.715085502e-04f, +6.353146713e-05f, -7.669318508e-05f, -1.383581653e-04f, -1.353661159e-04f, -9.544123411e-05f,
- /* 24, 5 */ +1.128060463e-04f, +1.424549941e-04f, +1.239935912e-04f, +3.335412922e-05f, -1.350864661e-04f, -3.598106411e-04f, -5.923941571e-04f, -7.684933716e-04f, -8.270765907e-04f, -7.315507834e-04f, -4.848734661e-04f, -1.323665545e-04f, +2.496920884e-04f, +5.762325468e-04f, +7.786522269e-04f, +8.242911148e-04f, +7.235180256e-04f, +5.226220062e-04f, +2.860050947e-04f, +7.481154929e-05f, -7.025967465e-05f, -1.366392205e-04f, -1.369530289e-04f, -9.844647580e-05f,
- /* 24, 6 */ +1.100456035e-04f, +1.417041346e-04f, +1.270800866e-04f, +4.136582536e-05f, -1.225400157e-04f, -3.449194225e-04f, -5.788162671e-04f, -7.603784078e-04f, -8.277282458e-04f, -7.422019493e-04f, -5.040543645e-04f, -1.561527673e-04f, +2.265897402e-04f, +5.588990922e-04f, +7.704716994e-04f, +8.260444163e-04f, +7.333651287e-04f, +5.369196097e-04f, +3.006080208e-04f, +8.635953200e-05f, -6.351025813e-05f, -1.346573670e-04f, -1.383878839e-04f, -1.014161798e-04f,
- /* 24, 7 */ +1.072225228e-04f, +1.407709979e-04f, +1.298829797e-04f, +4.906299265e-05f, -1.102313067e-04f, -3.300790706e-04f, -5.650334202e-04f, -7.518095256e-04f, -8.277695590e-04f, -7.522424649e-04f, -5.227956327e-04f, -1.797993550e-04f, +2.032852905e-04f, +5.410820901e-04f, +7.616671958e-04f, +8.272061905e-04f, +7.428004186e-04f, +5.510623045e-04f, +3.153039229e-04f, +9.816855611e-05f, -5.644465382e-05f, -1.324070557e-04f, -1.396630677e-04f, -1.043437672e-04f,
- /* 24, 8 */ +1.043437672e-04f, +1.396630677e-04f, +1.324070557e-04f, +5.644465382e-05f, -9.816855611e-05f, -3.153039229e-04f, -5.510623045e-04f, -7.428004186e-04f, -8.272061905e-04f, -7.616671958e-04f, -5.410820901e-04f, -2.032852905e-04f, +1.797993550e-04f, +5.227956327e-04f, +7.522424649e-04f, +8.277695590e-04f, +7.518095256e-04f, +5.650334202e-04f, +3.300790706e-04f, +1.102313067e-04f, -4.906299265e-05f, -1.298829797e-04f, -1.407709979e-04f, -1.072225228e-04f,
- /* 24, 9 */ +1.014161798e-04f, +1.383878839e-04f, +1.346573670e-04f, +6.351025813e-05f, -8.635953200e-05f, -3.006080208e-04f, -5.369196097e-04f, -7.333651287e-04f, -8.260444163e-04f, -7.704716994e-04f, -5.588990922e-04f, -2.265897402e-04f, +1.561527673e-04f, +5.040543645e-04f, +7.422019493e-04f, +8.277282458e-04f, +7.603784078e-04f, +5.788162671e-04f, +3.449194225e-04f, +1.225400157e-04f, -4.136582536e-05f, -1.270800866e-04f, -1.417041346e-04f, -1.100456035e-04f,
- /* 24,10 */ +9.844647580e-05f, +1.369530289e-04f, +1.366392205e-04f, +7.025967465e-05f, -7.481154929e-05f, -2.860050947e-04f, -5.226220062e-04f, -7.235180256e-04f, -8.242911148e-04f, -7.786522269e-04f, -5.762325468e-04f, -2.496920884e-04f, +1.323665545e-04f, +4.848734661e-04f, +7.315507834e-04f, +8.270765907e-04f, +7.684933716e-04f, +5.923941571e-04f, +3.598106411e-04f, +1.350864661e-04f, -3.335412922e-05f, -1.239935912e-04f, -1.424549941e-04f, -1.128060463e-04f,
- /* 24,11 */ +9.544123411e-05f, +1.353661159e-04f, +1.383581653e-04f, +7.669318508e-05f, -6.353146713e-05f, -2.715085502e-04f, -5.081861235e-04f, -7.132737861e-04f, -8.219537553e-04f, -7.862057246e-04f, -5.930689291e-04f, -2.725719623e-04f, +1.084619116e-04f, +4.652686374e-04f, +7.202947906e-04f, +8.258095592e-04f, +7.761410928e-04f, +6.057504254e-04f, +3.747381071e-04f, +1.478619956e-04f, -2.502931407e-05f, -1.206189886e-04f, -1.430161614e-04f, -1.154967766e-04f,
- /* 24,12 */ +9.240689021e-05f, +1.336347757e-04f, +1.398199793e-04f, +8.281147599e-05f, -5.252568657e-05f, -2.571314547e-04f, -4.936285297e-04f, -7.026473724e-04f, -8.190403838e-04f, -7.931298338e-04f, -6.093952965e-04f, -2.952092559e-04f, +8.446017611e-05f, +4.452560799e-04f, +7.084404793e-04f, +8.239227539e-04f, +7.833086355e-04f, +6.188684520e-04f, +3.896869361e-04f, +1.608575022e-04f, -1.639322797e-05f, -1.169520657e-04f, -1.433803035e-04f, -1.181106179e-04f,
- /* 24,13 */ +8.934972901e-05f, +1.317666448e-04f, +1.410306561e-04f, +8.861563067e-05f, -4.180014906e-05f, -2.428865252e-04f, -4.789657110e-04f, -6.916540113e-04f, -8.155596091e-04f, -7.994228899e-04f, -6.251993025e-04f, -3.175841545e-04f, +6.038280262e-05f, +4.248524782e-04f, +6.959950382e-04f, +8.214124236e-04f, +7.899834729e-04f, +6.317316839e-04f, +4.046419937e-04f, +1.740634498e-04f, -7.448162127e-06f, -1.129889134e-04f, -1.435401825e-04f, -1.206403004e-04f,
- /* 24,14 */ +8.627587811e-05f, +1.297693532e-04f, +1.419963918e-04f, +9.410712043e-05f, -3.136033560e-05f, -2.287861157e-04f, -4.642140510e-04f, -6.803091718e-04f, -8.115205881e-04f, -8.050839212e-04f, -6.404692089e-04f, -3.396771574e-04f, +3.625133679e-05f, +4.040749814e-04f, +6.829663304e-04f, +8.182754723e-04f, +7.961535056e-04f, +6.443236569e-04f, +4.195879132e-04f, +1.874698746e-04f, +1.803144714e-06f, -1.087259386e-04f, -1.434886692e-04f, -1.230784715e-04f,
- /* 24,15 */ +8.319130160e-05f, +1.276505127e-04f, +1.427235715e-04f, +9.928779545e-05f, -2.121126661e-05f, -2.148422063e-04f, -4.493898115e-04f, -6.686285441e-04f, -8.069330100e-04f, -8.101126455e-04f, -6.551938988e-04f, -3.614691013e-04f, +1.208738944e-05f, +3.829411831e-04f, +6.693628869e-04f, +8.145094672e-04f, +8.018070806e-04f, +6.566280172e-04f, +4.345091125e-04f, +2.010663923e-04f, +1.135750248e-05f, -1.041598752e-04f, -1.432187562e-04f, -1.254177052e-04f,
- /* 24, 0 */ +4.545052445e-05f, +1.951315810e-04f, +3.748938080e-04f, +4.809335107e-04f, +3.960765690e-04f, +5.993810822e-05f, -4.723795438e-04f, -1.024325735e-03f, -1.361247582e-03f, -1.299302728e-03f, -8.046117557e-04f, -2.606329026e-05f, +7.618428442e-04f, +1.280408741e-03f, +1.370322771e-03f, +1.054089829e-03f, +5.086432784e-04f, -3.113193898e-05f, -3.825195300e-04f, -4.822884412e-04f, -3.849609275e-04f, -2.063256631e-04f, -5.270037440e-05f, +2.454794639e-05f,
- /* 24, 1 */ +3.852332445e-05f, +1.840753178e-04f, +3.645444067e-04f, +4.788140096e-04f, +4.086121277e-04f, +8.793526572e-05f, -4.362135407e-04f, -9.937048198e-04f, -1.350562575e-03f, -1.316442769e-03f, -8.462280606e-04f, -7.815185270e-05f, +7.179801999e-04f, +1.259777116e-03f, +1.377758125e-03f, +1.082939175e-03f, +5.449481703e-04f, -1.547839432e-06f, -3.679388598e-04f, -4.828529979e-04f, -3.947147844e-04f, -2.176372792e-04f, -6.026901850e-05f, +2.205234045e-05f,
- /* 24, 2 */ +3.192167036e-05f, +1.731761778e-04f, +3.539434193e-04f, +4.759566352e-04f, +4.201302650e-04f, +1.150943789e-04f, -4.002008253e-04f, -9.622858103e-04f, -1.338300241e-03f, -1.331815598e-03f, -8.866349827e-04f, -1.301264311e-04f, +6.730846905e-04f, +1.237427186e-03f, +1.383526171e-03f, +1.110816763e-03f, +5.812367254e-04f, +2.878109064e-05f, -3.523343557e-04f, -4.826023474e-04f, -4.041241778e-04f, -2.290451863e-04f, -6.815162122e-05f, +1.926869283e-05f,
- /* 24, 3 */ +2.564752013e-05f, +1.624524653e-04f, +3.431211940e-04f, +4.723889014e-04f, +4.306369033e-04f, +1.413884897e-04f, -3.643958409e-04f, -9.301281119e-04f, -1.324495465e-03f, -1.345410999e-03f, -9.257779427e-04f, -1.819112698e-04f, +6.272190697e-04f, +1.213381290e-03f, +1.387602061e-03f, +1.137666624e-03f, +6.174506312e-04f, +5.981976836e-05f, -3.357077999e-04f, -4.815127015e-04f, -4.131577529e-04f, -2.405272085e-04f, -7.634234963e-05f, +1.618998833e-05f,
- /* 24, 4 */ +1.970191739e-05f, +1.519214683e-04f, +3.321076713e-04f, +4.681390617e-04f, +4.401397816e-04f, +1.667927354e-04f, -3.288518263e-04f, -8.972916878e-04f, -1.309185436e-03f, -1.357221809e-03f, -9.636046528e-04f, -2.334309635e-04f, +5.804478655e-04f, +1.187664745e-03f, +1.389963631e-03f, +1.163433942e-03f, +6.535308606e-04f, +9.153116784e-05f, -3.180629927e-04f, -4.795613921e-04f, -4.217840695e-04f, -2.520602653e-04f, -8.483435780e-05f, +1.280977164e-05f,
- /* 24, 5 */ +1.408501704e-05f, +1.415994448e-04f, +3.209323254e-04f, +4.632360317e-04f, +4.486484045e-04f, +1.912843645e-04f, -2.936207276e-04f, -8.638369381e-04f, -1.292409564e-03f, -1.367243913e-03f, -1.000065207e-03f, -2.846105957e-04f, +5.328372638e-04f, +1.160305814e-03f, +1.390591463e-03f, +1.188065177e-03f, +6.894177793e-04f, +1.238763650e-04f, -2.994057812e-04f, -4.767269440e-04f, -4.299716716e-04f, -2.636204028e-04f, -9.361977359e-05f, +9.122184539e-06f,
- /* 24, 6 */ +8.796112429e-06f, +1.315016126e-04f, +3.096241086e-04f, +4.577093118e-04f, +4.561739887e-04f, +2.148427485e-04f, -2.587531149e-04f, -8.298245798e-04f, -1.274209383e-03f, -1.375476234e-03f, -1.035112163e-03f, -3.353758773e-04f, +4.844549899e-04f, +1.131335665e-03f, +1.389468944e-03f, +1.211508174e-03f, +7.250512548e-04f, +1.568145870e-04f, -2.797440842e-04f, -4.729891466e-04f, -4.376891593e-04f, -2.751828281e-04f, -1.026896878e-04f, +5.122002376e-06f,
- /* 24, 7 */ +3.833664119e-06f, +1.216421408e-04f, +2.982113984e-04f, +4.515889092e-04f, +4.627294060e-04f, +2.374493875e-04f, -2.242981012e-04f, -7.953155261e-04f, -1.254628457e-03f, -1.381920720e-03f, -1.068700627e-03f, -3.856532828e-04f, +4.353701854e-04f, +1.100788324e-03f, +1.386582316e-03f, +1.233712281e-03f, +7.603707678e-04f, +1.903032668e-04f, -2.590879129e-04f, -4.683291247e-04f, -4.449052614e-04f, -2.867219458e-04f, -1.120341455e-04f, +8.046698450e-07f,
- /* 24, 8 */ -8.046698450e-07f, +1.120341455e-04f, +2.867219458e-04f, +4.449052614e-04f, +4.683291247e-04f, +2.590879129e-04f, -1.903032668e-04f, -7.603707678e-04f, -1.233712281e-03f, -1.386582316e-03f, -1.100788324e-03f, -4.353701854e-04f, +3.856532828e-04f, +1.068700627e-03f, +1.381920720e-03f, +1.254628457e-03f, +7.953155261e-04f, +2.242981012e-04f, -2.374493875e-04f, -4.627294060e-04f, -4.515889092e-04f, -2.982113984e-04f, -1.216421408e-04f, -3.833664119e-06f,
- /* 24, 9 */ -5.122002376e-06f, +1.026896878e-04f, +2.751828281e-04f, +4.376891593e-04f, +4.729891466e-04f, +2.797440842e-04f, -1.568145870e-04f, -7.250512548e-04f, -1.211508174e-03f, -1.389468944e-03f, -1.131335665e-03f, -4.844549899e-04f, +3.353758773e-04f, +1.035112163e-03f, +1.375476234e-03f, +1.274209383e-03f, +8.298245798e-04f, +2.587531149e-04f, -2.148427485e-04f, -4.561739887e-04f, -4.577093118e-04f, -3.096241086e-04f, -1.315016126e-04f, -8.796112429e-06f,
- /* 24,10 */ -9.122184539e-06f, +9.361977359e-05f, +2.636204028e-04f, +4.299716716e-04f, +4.767269440e-04f, +2.994057812e-04f, -1.238763650e-04f, -6.894177793e-04f, -1.188065177e-03f, -1.390591463e-03f, -1.160305814e-03f, -5.328372638e-04f, +2.846105957e-04f, +1.000065207e-03f, +1.367243913e-03f, +1.292409564e-03f, +8.638369381e-04f, +2.936207276e-04f, -1.912843645e-04f, -4.486484045e-04f, -4.632360317e-04f, -3.209323254e-04f, -1.415994448e-04f, -1.408501704e-05f,
- /* 24,11 */ -1.280977164e-05f, +8.483435780e-05f, +2.520602653e-04f, +4.217840695e-04f, +4.795613921e-04f, +3.180629927e-04f, -9.153116784e-05f, -6.535308606e-04f, -1.163433942e-03f, -1.389963631e-03f, -1.187664745e-03f, -5.804478655e-04f, +2.334309635e-04f, +9.636046528e-04f, +1.357221809e-03f, +1.309185436e-03f, +8.972916878e-04f, +3.288518263e-04f, -1.667927354e-04f, -4.401397816e-04f, -4.681390617e-04f, -3.321076713e-04f, -1.519214683e-04f, -1.970191739e-05f,
- /* 24,12 */ -1.618998833e-05f, +7.634234963e-05f, +2.405272085e-04f, +4.131577529e-04f, +4.815127015e-04f, +3.357077999e-04f, -5.981976836e-05f, -6.174506312e-04f, -1.137666624e-03f, -1.387602061e-03f, -1.213381290e-03f, -6.272190697e-04f, +1.819112698e-04f, +9.257779427e-04f, +1.345410999e-03f, +1.324495465e-03f, +9.301281119e-04f, +3.643958409e-04f, -1.413884897e-04f, -4.306369033e-04f, -4.723889014e-04f, -3.431211940e-04f, -1.624524653e-04f, -2.564752013e-05f,
- /* 24,13 */ -1.926869283e-05f, +6.815162122e-05f, +2.290451863e-04f, +4.041241778e-04f, +4.826023474e-04f, +3.523343557e-04f, -2.878109064e-05f, -5.812367254e-04f, -1.110816763e-03f, -1.383526171e-03f, -1.237427186e-03f, -6.730846905e-04f, +1.301264311e-04f, +8.866349827e-04f, +1.331815598e-03f, +1.338300241e-03f, +9.622858103e-04f, +4.002008253e-04f, -1.150943789e-04f, -4.201302650e-04f, -4.759566352e-04f, -3.539434193e-04f, -1.731761778e-04f, -3.192167036e-05f,
- /* 24,14 */ -2.205234045e-05f, +6.026901850e-05f, +2.176372792e-04f, +3.947147844e-04f, +4.828529979e-04f, +3.679388598e-04f, +1.547839432e-06f, -5.449481703e-04f, -1.082939175e-03f, -1.377758125e-03f, -1.259777116e-03f, -7.179801999e-04f, +7.815185270e-05f, +8.462280606e-04f, +1.316442769e-03f, +1.350562575e-03f, +9.937048198e-04f, +4.362135407e-04f, -8.793526572e-05f, -4.086121277e-04f, -4.788140096e-04f, -3.645444067e-04f, -1.840753178e-04f, -3.852332445e-05f,
- /* 24,15 */ -2.454794639e-05f, +5.270037440e-05f, +2.063256631e-04f, +3.849609275e-04f, +4.822884412e-04f, +3.825195300e-04f, +3.113193898e-05f, -5.086432784e-04f, -1.054089829e-03f, -1.370322771e-03f, -1.280408741e-03f, -7.618428442e-04f, +2.606329026e-05f, +8.046117557e-04f, +1.299302728e-03f, +1.361247582e-03f, +1.024325735e-03f, +4.723795438e-04f, -5.993810822e-05f, -3.960765690e-04f, -4.809335107e-04f, -3.748938080e-04f, -1.951315810e-04f, -4.545052445e-05f,
- /* 24, 0 */ -1.702250368e-04f, -1.965005420e-04f, +1.103795304e-06f, +4.330784212e-04f, +8.784555707e-04f, +9.653328276e-04f, +4.315509563e-04f, -6.109575553e-04f, -1.641723450e-03f, -2.015827225e-03f, -1.400787443e-03f, -4.702498413e-05f, +1.332047630e-03f, +2.006889303e-03f, +1.690240096e-03f, +6.826705419e-04f, -3.776686811e-04f, -9.512688743e-04f, -8.983149818e-04f, -4.640234647e-04f, -2.230828855e-05f, +1.918067822e-04f, +1.759255972e-04f, +6.786242515e-05f,
- /* 24, 1 */ -1.642126010e-04f, -2.002752798e-04f, -1.910126829e-05f, +4.022439121e-04f, +8.571608722e-04f, +9.768399670e-04f, +4.832977173e-04f, -5.392469404e-04f, -1.590532482e-03f, -2.020693110e-03f, -1.466475318e-03f, -1.409702826e-04f, +1.260398022e-03f, +1.993868298e-03f, +1.735939471e-03f, +7.542164043e-04f, -3.217397652e-04f, -9.346209288e-04f, -9.166430096e-04f, -4.949934945e-04f, -4.448641826e-05f, +1.861665779e-04f, +1.812738819e-04f, +7.451957718e-05f,
- /* 24, 2 */ -1.579281336e-04f, -2.031605347e-04f, -3.828516688e-05f, +3.716026326e-04f, +8.345286135e-04f, +9.858238344e-04f, +5.328272649e-04f, -4.677058239e-04f, -1.536815378e-03f, -2.021508037e-03f, -1.528977139e-03f, -2.346018520e-04f, +1.185988431e-03f, +1.976763286e-03f, +1.778684302e-03f, +8.254237228e-04f, -2.638601140e-04f, -9.153683790e-04f, -9.333455225e-04f, -5.259003096e-04f, -6.760829922e-05f, +1.795547962e-04f, +1.862290729e-04f, +8.132190552e-05f,
- /* 24, 3 */ -1.514107898e-04f, -2.051877883e-04f, -5.643017558e-05f, +3.412342375e-04f, +8.106578209e-04f, +9.923241157e-04f, +5.800651921e-04f, -3.964985305e-04f, -1.480725107e-03f, -2.018303000e-03f, -1.588167145e-03f, -3.277114722e-04f, +1.108975953e-03f, +1.955583535e-03f, +1.818343426e-03f, +8.961196099e-04f, -2.041325004e-04f, -8.934973649e-04f, -9.483306864e-04f, -5.566532714e-04f, -9.163993343e-05f, +1.719487619e-04f, +1.907500791e-04f, +8.824611727e-05f,
- /* 24, 4 */ -1.446989102e-04f, -2.063902871e-04f, -7.352252002e-05f, +3.112151687e-04f, +7.856484910e-04f, +9.963864071e-04f, +6.249444785e-04f, -3.257861630e-04f, -1.422418959e-03f, -2.011118755e-03f, -1.643928243e-03f, -4.200923193e-04f, +1.029524574e-03f, +1.930348530e-03f, +1.854792197e-03f, +9.661301752e-04f, -1.426663759e-04f, -8.690009463e-04f, -9.615092978e-04f, -5.871595310e-04f, -1.165432034e-04f, +1.633284218e-04f, +1.947956873e-04f, +9.526723781e-05f,
- /* 24, 5 */ -1.378299032e-04f, -2.068028652e-04f, -8.955230425e-05f, +2.816184974e-04f, +7.596012646e-04f, +9.980619660e-04f, +6.674055614e-04f, -2.557261963e-04f, -1.362058071e-03f, -2.000005648e-03f, -1.696152289e-03f, -5.115395181e-04f, +9.478047386e-04f, +1.901087985e-03f, +1.887912892e-03f, +1.035280999e-03f, -7.957765930e-05f, -8.418792522e-04f, -9.727951144e-04f, -6.173242692e-04f, -1.422758795e-04f, +1.536765045e-04f, +1.983247179e-04f, +1.023586489e-04f,
- /* 24, 6 */ -1.308401336e-04f, -2.064617646e-04f, -1.045134271e-04f, +2.525137807e-04f, +7.326171060e-04f, +9.974074494e-04f, +7.073963828e-04f, -1.864720875e-04f, -1.299806951e-03f, -1.985023411e-03f, -1.744740344e-03f, -6.018506887e-04f, +8.639929140e-04f, +1.867841815e-03f, +1.917595086e-03f, +1.103397612e-03f, -1.498850339e-05f, -8.121396097e-04f, -9.821051828e-04f, -6.470509490e-04f, -1.687916421e-04f, +1.429786744e-04f, +2.012961856e-04f, +1.094921351e-04f,
- /* 24, 7 */ -1.237648199e-04f, -2.054044567e-04f, -1.184034872e-04f, +2.239669341e-04f, +7.047969872e-04f, +9.944846402e-04f, +7.448724134e-04f, -1.181729029e-04f, -1.235832990e-03f, -1.966240936e-03f, -1.789602898e-03f, -6.908264855e-04f, +7.782711267e-04f, +1.830660078e-03f, +1.943736019e-03f, +1.170305979e-03f, +5.097296116e-05f, -7.797966533e-04f, -9.893601617e-04f, -6.762415789e-04f, -1.960401183e-04f, +1.312236785e-04f, +2.036694644e-04f, +1.166379381e-04f,
- /* 24, 8 */ -1.166379381e-04f, -2.036694644e-04f, -1.312236785e-04f, +1.960401183e-04f, +6.762415789e-04f, +9.893601617e-04f, +7.797966533e-04f, -5.097296116e-05f, -1.170305979e-03f, -1.943736019e-03f, -1.830660078e-03f, -7.782711267e-04f, +6.908264855e-04f, +1.789602898e-03f, +1.966240936e-03f, +1.235832990e-03f, +1.181729029e-04f, -7.448724134e-04f, -9.944846402e-04f, -7.047969872e-04f, -2.239669341e-04f, +1.184034872e-04f, +2.054044567e-04f, +1.237648199e-04f,
- /* 24, 9 */ -1.094921351e-04f, -2.012961856e-04f, -1.429786744e-04f, +1.687916421e-04f, +6.470509490e-04f, +9.821051828e-04f, +8.121396097e-04f, +1.498850339e-05f, -1.103397612e-03f, -1.917595086e-03f, -1.867841815e-03f, -8.639929140e-04f, +6.018506887e-04f, +1.744740344e-03f, +1.985023411e-03f, +1.299806951e-03f, +1.864720875e-04f, -7.073963828e-04f, -9.974074494e-04f, -7.326171060e-04f, -2.525137807e-04f, +1.045134271e-04f, +2.064617646e-04f, +1.308401336e-04f,
- /* 24,10 */ -1.023586489e-04f, -1.983247179e-04f, -1.536765045e-04f, +1.422758795e-04f, +6.173242692e-04f, +9.727951144e-04f, +8.418792522e-04f, +7.957765930e-05f, -1.035280999e-03f, -1.887912892e-03f, -1.901087985e-03f, -9.478047386e-04f, +5.115395181e-04f, +1.696152289e-03f, +2.000005648e-03f, +1.362058071e-03f, +2.557261963e-04f, -6.674055614e-04f, -9.980619660e-04f, -7.596012646e-04f, -2.816184974e-04f, +8.955230425e-05f, +2.068028652e-04f, +1.378299032e-04f,
- /* 24,11 */ -9.526723781e-05f, -1.947956873e-04f, -1.633284218e-04f, +1.165432034e-04f, +5.871595310e-04f, +9.615092978e-04f, +8.690009463e-04f, +1.426663759e-04f, -9.661301752e-04f, -1.854792197e-03f, -1.930348530e-03f, -1.029524574e-03f, +4.200923193e-04f, +1.643928243e-03f, +2.011118755e-03f, +1.422418959e-03f, +3.257861630e-04f, -6.249444785e-04f, -9.963864071e-04f, -7.856484910e-04f, -3.112151687e-04f, +7.352252002e-05f, +2.063902871e-04f, +1.446989102e-04f,
- /* 24,12 */ -8.824611727e-05f, -1.907500791e-04f, -1.719487619e-04f, +9.163993343e-05f, +5.566532714e-04f, +9.483306864e-04f, +8.934973649e-04f, +2.041325004e-04f, -8.961196099e-04f, -1.818343426e-03f, -1.955583535e-03f, -1.108975953e-03f, +3.277114722e-04f, +1.588167145e-03f, +2.018303000e-03f, +1.480725107e-03f, +3.964985305e-04f, -5.800651921e-04f, -9.923241157e-04f, -8.106578209e-04f, -3.412342375e-04f, +5.643017558e-05f, +2.051877883e-04f, +1.514107898e-04f,
- /* 24,13 */ -8.132190552e-05f, -1.862290729e-04f, -1.795547962e-04f, +6.760829922e-05f, +5.259003096e-04f, +9.333455225e-04f, +9.153683790e-04f, +2.638601140e-04f, -8.254237228e-04f, -1.778684302e-03f, -1.976763286e-03f, -1.185988431e-03f, +2.346018520e-04f, +1.528977139e-03f, +2.021508037e-03f, +1.536815378e-03f, +4.677058239e-04f, -5.328272649e-04f, -9.858238344e-04f, -8.345286135e-04f, -3.716026326e-04f, +3.828516688e-05f, +2.031605347e-04f, +1.579281336e-04f,
- /* 24,14 */ -7.451957718e-05f, -1.812738819e-04f, -1.861665779e-04f, +4.448641826e-05f, +4.949934945e-04f, +9.166430096e-04f, +9.346209288e-04f, +3.217397652e-04f, -7.542164043e-04f, -1.735939471e-03f, -1.993868298e-03f, -1.260398022e-03f, +1.409702826e-04f, +1.466475318e-03f, +2.020693110e-03f, +1.590532482e-03f, +5.392469404e-04f, -4.832977173e-04f, -9.768399670e-04f, -8.571608722e-04f, -4.022439121e-04f, +1.910126829e-05f, +2.002752798e-04f, +1.642126010e-04f,
- /* 24,15 */ -6.786242515e-05f, -1.759255972e-04f, -1.918067822e-04f, +2.230828855e-05f, +4.640234647e-04f, +8.983149818e-04f, +9.512688743e-04f, +3.776686811e-04f, -6.826705419e-04f, -1.690240096e-03f, -2.006889303e-03f, -1.332047630e-03f, +4.702498413e-05f, +1.400787443e-03f, +2.015827225e-03f, +1.641723450e-03f, +6.109575553e-04f, -4.315509563e-04f, -9.653328276e-04f, -8.784555707e-04f, -4.330784212e-04f, -1.103795304e-06f, +1.965005420e-04f, +1.702250368e-04f,
- /* 24, 0 */ +3.919255962e-05f, -2.280943782e-04f, -5.361345988e-04f, -4.457457641e-04f, +2.990589649e-04f, +1.295797675e-03f, +1.605517166e-03f, +5.875816565e-04f, -1.289084098e-03f, -2.596166105e-03f, -2.129939921e-03f, -7.496988250e-05f, +2.037180601e-03f, +2.625542335e-03f, +1.404160874e-03f, -4.837783526e-04f, -1.582480410e-03f, -1.345619201e-03f, -3.621830939e-04f, +4.180945199e-04f, +5.469818219e-04f, +2.499414065e-04f, -2.888372260e-05f, -8.895700627e-05f,
- /* 24, 1 */ +4.853777483e-05f, -2.065137544e-04f, -5.236754574e-04f, -4.705972357e-04f, +2.371311675e-04f, +1.243196859e-03f, +1.622959247e-03f, +6.876650067e-04f, -1.171710060e-03f, -2.559351067e-03f, -2.215991331e-03f, -2.246678327e-04f, +1.937986318e-03f, +2.647321756e-03f, +1.516528605e-03f, -3.765445934e-04f, -1.553807591e-03f, -1.392405213e-03f, -4.263006320e-04f, +3.876486968e-04f, +5.560961676e-04f, +2.719611444e-04f, -1.761075235e-05f, -9.068976925e-05f,
- /* 24, 2 */ +5.692498928e-05f, -1.852878923e-04f, -5.097279107e-04f, -4.926555685e-04f, +1.765921503e-04f, +1.188077447e-03f, +1.634867875e-03f, +7.837567953e-04f, -1.052453928e-03f, -2.515280079e-03f, -2.295086316e-03f, -3.736412373e-04f, +1.832653524e-03f, +2.661371990e-03f, +1.625780509e-03f, -2.661868955e-04f, -1.519477670e-03f, -1.435905115e-03f, -4.911987788e-04f, +3.544256494e-04f, +5.633598944e-04f, +2.940548284e-04f, -5.378471232e-06f, -9.187426948e-05f,
- /* 24, 3 */ +6.436469025e-05f, -1.644995777e-04f, -4.944173708e-04f, -5.119388451e-04f, +1.176233517e-04f, +1.130703462e-03f, +1.641323506e-03f, +8.756040923e-04f, -9.317326579e-04f, -2.464159993e-03f, -2.367001633e-03f, -5.214100591e-04f, +1.721501142e-03f, +2.667586958e-03f, +1.731516399e-03f, -1.530278412e-04f, -1.479490407e-03f, -1.475875084e-03f, -5.566555092e-04f, +3.184551797e-04f, +5.686591450e-04f, +3.161189305e-04f, +7.802761507e-06f, -9.246489856e-05f,
- /* 24, 4 */ +7.087197068e-05f, -1.442258278e-04f, -4.778705046e-04f, -5.284761768e-04f, +6.039472401e-05f, +1.071341110e-03f, +1.642425167e-03f, +9.629733481e-04f, -8.099633882e-04f, -2.406220702e-03f, -2.431540042e-03f, -6.674987371e-04f, +1.604869446e-03f, +2.665887444e-03f, +1.833344283e-03f, -3.740504807e-05f, -1.433866725e-03f, -1.512079220e-03f, -6.224402836e-04f, +2.797797731e-04f, +5.718846156e-04f, +3.380454975e-04f, +2.191686946e-05f, -9.241721523e-05f,
- /* 24, 5 */ +7.646625331e-05f, -1.245377292e-04f, -4.602146020e-04f, -5.423072437e-04f, +5.064318866e-06f, +1.010257658e-03f, +1.638289725e-03f, +1.045651008e-03f, -6.875618648e-04f, -2.341714107e-03f, -2.488530931e-03f, -8.114379517e-04f, +1.483118851e-03f, +2.656221571e-03f, +1.930881931e-03f, +8.032993590e-05f, -1.382648990e-03f, -1.544290670e-03f, -6.883148110e-04f, +2.384547825e-04f, +5.729322223e-04f, +3.597225244e-04f, +3.694190340e-05f, -9.168826568e-05f,
- /* 24, 6 */ +8.117100143e-05f, -1.055003114e-04f, -4.415769617e-04f, -5.534817998e-04f, -4.822206513e-05f, +9.477203224e-04f, +1.629051096e-03f, +1.123444034e-03f, -5.649408783e-04f, -2.270913001e-03f, -2.537830838e-03f, -9.527663633e-04f, +1.356628624e-03f, +2.638565156e-03f, +2.023758423e-03f, +1.998128074e-04f, -1.325901212e-03f, -1.572292748e-03f, -7.540338642e-04f, +1.945485578e-04f, +5.717037624e-04f, +3.810343609e-04f, +5.284991195e-05f, -9.023690790e-05f,
- /* 24, 7 */ +8.501341847e-05f, -8.717245610e-05f, -4.220842946e-04f, -5.620591450e-04f, -9.933117260e-05f, +8.839951872e-04f, +1.614859393e-03f, +1.196180345e-03f, -4.425087329e-04f, -2.194109877e-03f, -2.579323863e-03f, -1.091032318e-03f, +1.225795515e-03f, +2.612921966e-03f, +2.111615667e-03f, +3.206677492e-04f, -1.263709151e-03f, -1.595880025e-03f, -8.193461436e-04f, +1.481425192e-04f, +5.681075679e-04f, +4.018621494e-04f, +6.960683992e-05f, -8.802413824e-05f,
- /* 24, 8 */ +8.802413824e-05f, -6.960683992e-05f, -4.018621494e-04f, -5.681075679e-04f, -1.481425192e-04f, +8.193461436e-04f, +1.595880025e-03f, +1.263709151e-03f, -3.206677492e-04f, -2.111615667e-03f, -2.612921966e-03f, -1.225795515e-03f, +1.091032318e-03f, +2.579323863e-03f, +2.194109877e-03f, +4.425087329e-04f, -1.196180345e-03f, -1.614859393e-03f, -8.839951872e-04f, +9.933117260e-05f, +5.620591450e-04f, +4.220842946e-04f, +8.717245610e-05f, -8.501341847e-05f,
- /* 24, 9 */ +9.023690790e-05f, -5.284991195e-05f, -3.810343609e-04f, -5.717037624e-04f, -1.945485578e-04f, +7.540338642e-04f, +1.572292748e-03f, +1.325901212e-03f, -1.998128074e-04f, -2.023758423e-03f, -2.638565156e-03f, -1.356628624e-03f, +9.527663633e-04f, +2.537830838e-03f, +2.270913001e-03f, +5.649408783e-04f, -1.123444034e-03f, -1.629051096e-03f, -9.477203224e-04f, +4.822206513e-05f, +5.534817998e-04f, +4.415769617e-04f, +1.055003114e-04f, -8.117100143e-05f,
- /* 24,10 */ +9.168826568e-05f, -3.694190340e-05f, -3.597225244e-04f, -5.729322223e-04f, -2.384547825e-04f, +6.883148110e-04f, +1.544290670e-03f, +1.382648990e-03f, -8.032993590e-05f, -1.930881931e-03f, -2.656221571e-03f, -1.483118851e-03f, +8.114379517e-04f, +2.488530931e-03f, +2.341714107e-03f, +6.875618648e-04f, -1.045651008e-03f, -1.638289725e-03f, -1.010257658e-03f, -5.064318866e-06f, +5.423072437e-04f, +4.602146020e-04f, +1.245377292e-04f, -7.646625331e-05f,
- /* 24,11 */ +9.241721523e-05f, -2.191686946e-05f, -3.380454975e-04f, -5.718846156e-04f, -2.797797731e-04f, +6.224402836e-04f, +1.512079220e-03f, +1.433866725e-03f, +3.740504807e-05f, -1.833344283e-03f, -2.665887444e-03f, -1.604869446e-03f, +6.674987371e-04f, +2.431540042e-03f, +2.406220702e-03f, +8.099633882e-04f, -9.629733481e-04f, -1.642425167e-03f, -1.071341110e-03f, -6.039472401e-05f, +5.284761768e-04f, +4.778705046e-04f, +1.442258278e-04f, -7.087197068e-05f,
- /* 24,12 */ +9.246489856e-05f, -7.802761507e-06f, -3.161189305e-04f, -5.686591450e-04f, -3.184551797e-04f, +5.566555092e-04f, +1.475875084e-03f, +1.479490407e-03f, +1.530278412e-04f, -1.731516399e-03f, -2.667586958e-03f, -1.721501142e-03f, +5.214100591e-04f, +2.367001633e-03f, +2.464159993e-03f, +9.317326579e-04f, -8.756040923e-04f, -1.641323506e-03f, -1.130703462e-03f, -1.176233517e-04f, +5.119388451e-04f, +4.944173708e-04f, +1.644995777e-04f, -6.436469025e-05f,
- /* 24,13 */ +9.187426948e-05f, +5.378471232e-06f, -2.940548284e-04f, -5.633598944e-04f, -3.544256494e-04f, +4.911987788e-04f, +1.435905115e-03f, +1.519477670e-03f, +2.661868955e-04f, -1.625780509e-03f, -2.661371990e-03f, -1.832653524e-03f, +3.736412373e-04f, +2.295086316e-03f, +2.515280079e-03f, +1.052453928e-03f, -7.837567953e-04f, -1.634867875e-03f, -1.188077447e-03f, -1.765921503e-04f, +4.926555685e-04f, +5.097279107e-04f, +1.852878923e-04f, -5.692498928e-05f,
- /* 24,14 */ +9.068976925e-05f, +1.761075235e-05f, -2.719611444e-04f, -5.560961676e-04f, -3.876486968e-04f, +4.263006320e-04f, +1.392405213e-03f, +1.553807591e-03f, +3.765445934e-04f, -1.516528605e-03f, -2.647321756e-03f, -1.937986318e-03f, +2.246678327e-04f, +2.215991331e-03f, +2.559351067e-03f, +1.171710060e-03f, -6.876650067e-04f, -1.622959247e-03f, -1.243196859e-03f, -2.371311675e-04f, +4.705972357e-04f, +5.236754574e-04f, +2.065137544e-04f, -4.853777483e-05f,
- /* 24,15 */ +8.895700627e-05f, +2.888372260e-05f, -2.499414065e-04f, -5.469818219e-04f, -4.180945199e-04f, +3.621830939e-04f, +1.345619201e-03f, +1.582480410e-03f, +4.837783526e-04f, -1.404160874e-03f, -2.625542335e-03f, -2.037180601e-03f, +7.496988250e-05f, +2.129939921e-03f, +2.596166105e-03f, +1.289084098e-03f, -5.875816565e-04f, -1.605517166e-03f, -1.295797675e-03f, -2.990589649e-04f, +4.457457641e-04f, +5.361345988e-04f, +2.280943782e-04f, -3.919255962e-05f,
- /* 24, 0 */ +1.848082291e-04f, +3.126544607e-04f, -8.381805218e-05f, -8.698090905e-04f, -1.028447094e-03f, +2.139154673e-04f, +1.954115341e-03f, +2.095678120e-03f, -1.635717275e-04f, -2.819157299e-03f, -2.940044791e-03f, -1.098945345e-04f, +2.833179926e-03f, +2.923929522e-03f, +3.511165299e-04f, -2.017938339e-03f, -2.030476715e-03f, -3.277888569e-04f, +9.926761418e-04f, +9.110442493e-04f, +1.284872781e-04f, -3.065935448e-04f, -1.998565163e-04f, +7.803305534e-06f,
- /* 24, 1 */ +1.695814378e-04f, +3.164556508e-04f, -4.103650150e-05f, -8.260698464e-04f, -1.058100498e-03f, +1.024893805e-04f, +1.871044125e-03f, +2.162944507e-03f, +2.203366063e-05f, -2.703524226e-03f, -3.034115138e-03f, -3.291928088e-04f, +2.713944640e-03f, +3.017272284e-03f, +5.397663057e-04f, -1.929900383e-03f, -2.099607997e-03f, -4.436146208e-04f, +9.507629285e-04f, +9.494468230e-04f, +1.748714247e-04f, -2.981837386e-04f, -2.146007649e-04f, -5.699432396e-07f,
- /* 24, 2 */ +1.542962580e-04f, +3.180964801e-04f, -2.971107404e-07f, -7.801571616e-04f, -1.081692695e-03f, -6.019646704e-06f, +1.781805749e-03f, +2.219616197e-03f, +2.048848004e-04f, -2.577645910e-03f, -3.115029215e-03f, -5.470211463e-04f, +2.582823494e-03f, +3.098667200e-03f, +7.286710477e-04f, -1.831793311e-03f, -2.161014579e-03f, -5.608747689e-04f, +9.027154107e-04f, +9.846924856e-04f, +2.227798586e-04f, -2.873471247e-04f, -2.289106872e-04f, -9.773337074e-06f,
- /* 24, 3 */ +1.390667857e-04f, +3.176854393e-04f, +3.826416878e-05f, -7.324018434e-04f, -1.099310451e-03f, -1.111689527e-04f, +1.686962051e-03f, +2.265625589e-03f, +3.841903571e-04f, -2.442181508e-03f, -3.182489175e-03f, -7.624077328e-04f, +2.440359294e-03f, +3.167649091e-03f, +9.169693475e-04f, -1.723899657e-03f, -2.214230624e-03f, -6.790305367e-04f, +8.485750922e-04f, +1.016463150e-03f, +2.720045869e-04f, -2.740180422e-04f, -2.426519708e-04f, -1.978701792e-05f,
- /* 24, 4 */ +1.240005643e-04f, +3.153390489e-04f, +7.453005747e-05f, -6.831329371e-04f, -1.111069621e-03f, -2.125447010e-04f, +1.587090707e-03f, +2.300958285e-03f, +5.591862212e-04f, -2.297830066e-03f, -3.236262287e-03f, -9.743929228e-04f, +2.287150577e-03f, +3.223808711e-03f, +1.103792665e-03f, -1.606554759e-03f, -2.258822083e-03f, -7.975248409e-04f, +7.884177261e-04f, +1.044449054e-03f, +3.223209063e-04f, -2.581440514e-04f, -2.556870681e-04f, -3.058317705e-05f,
- /* 24, 5 */ +1.091981202e-04f, +3.111807515e-04f, +1.084019636e-04f, -6.326758693e-04f, -1.117113666e-03f, -3.097634105e-04f, +1.482781879e-03f, +2.325652312e-03f, +7.291390495e-04f, -2.145326692e-03f, -3.276181809e-03f, -1.182034015e-03f, +2.123848782e-03f, +3.266795190e-03f, +1.288269679e-03f, -1.480145805e-03f, -2.294389599e-03f, -9.157848908e-04f, +7.223538352e-04f, +1.068350860e-03f, +3.734881809e-04f, -2.396868442e-04f, -2.678760406e-04f, -4.212586861e-05f,
- /* 24, 6 */ +9.475256757e-05f, +3.053397988e-04f, +1.397998805e-04f, -5.813506695e-04f, -1.117612060e-03f, -4.024732802e-04f, +1.374634870e-03f, +2.339797075e-03f, +8.933496022e-04f, -1.985438555e-03f, -3.302147511e-03f, -1.384409934e-03f, +1.951155148e-03f, +3.296318191e-03f, +1.469530695e-03f, -1.345110577e-03f, -2.320571262e-03f, -1.033224945e-03f, +6.505290403e-04f, +1.087881752e-03f, +4.252507475e-04f, -2.186230940e-04f, -2.790774568e-04f, -5.437087857e-05f,
- /* 24, 7 */ +8.074928352e-05f, +2.979501429e-04f, +1.686621699e-04f, -5.294702777e-04f, -1.112758583e-03f, -4.903553074e-04f, +1.263254779e-03f, +2.343532047e-03f, +1.051155860e-03f, -1.818960757e-03f, -3.314125843e-03f, -1.580625796e-03f, +1.769817333e-03f, +3.312149758e-03f, +1.646712089e-03f, -1.201935910e-03f, -2.337045209e-03f, -1.149249197e-03f, +5.731241934e-04f, +1.102769514e-03f, +4.773389469e-04f, -1.949452358e-04f, -2.891493374e-04f, -6.726565227e-05f,
- /* 24, 8 */ +6.726565227e-05f, +2.891493374e-04f, +1.949452358e-04f, -4.773389469e-04f, -1.102769514e-03f, -5.731241934e-04f, +1.149249197e-03f, +2.337045209e-03f, +1.201935910e-03f, -1.646712089e-03f, -3.312149758e-03f, -1.769817333e-03f, +1.580625796e-03f, +3.314125843e-03f, +1.818960757e-03f, -1.051155860e-03f, -2.343532047e-03f, -1.263254779e-03f, +4.903553074e-04f, +1.112758583e-03f, +5.294702777e-04f, -1.686621699e-04f, -2.979501429e-04f, -8.074928352e-05f,
- /* 24, 9 */ +5.437087857e-05f, +2.790774568e-04f, +2.186230940e-04f, -4.252507475e-04f, -1.087881752e-03f, -6.505290403e-04f, +1.033224945e-03f, +2.320571262e-03f, +1.345110577e-03f, -1.469530695e-03f, -3.296318191e-03f, -1.951155148e-03f, +1.384409934e-03f, +3.302147511e-03f, +1.985438555e-03f, -8.933496022e-04f, -2.339797075e-03f, -1.374634870e-03f, +4.024732802e-04f, +1.117612060e-03f, +5.813506695e-04f, -1.397998805e-04f, -3.053397988e-04f, -9.475256757e-05f,
- /* 24,10 */ +4.212586861e-05f, +2.678760406e-04f, +2.396868442e-04f, -3.734881809e-04f, -1.068350860e-03f, -7.223538352e-04f, +9.157848908e-04f, +2.294389599e-03f, +1.480145805e-03f, -1.288269679e-03f, -3.266795190e-03f, -2.123848782e-03f, +1.182034015e-03f, +3.276181809e-03f, +2.145326692e-03f, -7.291390495e-04f, -2.325652312e-03f, -1.482781879e-03f, +3.097634105e-04f, +1.117113666e-03f, +6.326758693e-04f, -1.084019636e-04f, -3.111807515e-04f, -1.091981202e-04f,
- /* 24,11 */ +3.058317705e-05f, +2.556870681e-04f, +2.581440514e-04f, -3.223209063e-04f, -1.044449054e-03f, -7.884177261e-04f, +7.975248409e-04f, +2.258822083e-03f, +1.606554759e-03f, -1.103792665e-03f, -3.223808711e-03f, -2.287150577e-03f, +9.743929228e-04f, +3.236262287e-03f, +2.297830066e-03f, -5.591862212e-04f, -2.300958285e-03f, -1.587090707e-03f, +2.125447010e-04f, +1.111069621e-03f, +6.831329371e-04f, -7.453005747e-05f, -3.153390489e-04f, -1.240005643e-04f,
- /* 24,12 */ +1.978701792e-05f, +2.426519708e-04f, +2.740180422e-04f, -2.720045869e-04f, -1.016463150e-03f, -8.485750922e-04f, +6.790305367e-04f, +2.214230624e-03f, +1.723899657e-03f, -9.169693475e-04f, -3.167649091e-03f, -2.440359294e-03f, +7.624077328e-04f, +3.182489175e-03f, +2.442181508e-03f, -3.841903571e-04f, -2.265625589e-03f, -1.686962051e-03f, +1.111689527e-04f, +1.099310451e-03f, +7.324018434e-04f, -3.826416878e-05f, -3.176854393e-04f, -1.390667857e-04f,
- /* 24,13 */ +9.773337074e-06f, +2.289106872e-04f, +2.873471247e-04f, -2.227798586e-04f, -9.846924856e-04f, -9.027154107e-04f, +5.608747689e-04f, +2.161014579e-03f, +1.831793311e-03f, -7.286710477e-04f, -3.098667200e-03f, -2.582823494e-03f, +5.470211463e-04f, +3.115029215e-03f, +2.577645910e-03f, -2.048848004e-04f, -2.219616197e-03f, -1.781805749e-03f, +6.019646704e-06f, +1.081692695e-03f, +7.801571616e-04f, +2.971107404e-07f, -3.180964801e-04f, -1.542962580e-04f,
- /* 24,14 */ +5.699432396e-07f, +2.146007649e-04f, +2.981837386e-04f, -1.748714247e-04f, -9.494468230e-04f, -9.507629285e-04f, +4.436146208e-04f, +2.099607997e-03f, +1.929900383e-03f, -5.397663057e-04f, -3.017272284e-03f, -2.713944640e-03f, +3.291928088e-04f, +3.034115138e-03f, +2.703524226e-03f, -2.203366063e-05f, -2.162944507e-03f, -1.871044125e-03f, -1.024893805e-04f, +1.058100498e-03f, +8.260698464e-04f, +4.103650150e-05f, -3.164556508e-04f, -1.695814378e-04f,
- /* 24,15 */ -7.803305534e-06f, +1.998565163e-04f, +3.065935448e-04f, -1.284872781e-04f, -9.110442493e-04f, -9.926761418e-04f, +3.277888569e-04f, +2.030476715e-03f, +2.017938339e-03f, -3.511165299e-04f, -2.923929522e-03f, -2.833179926e-03f, +1.098945345e-04f, +2.940044791e-03f, +2.819157299e-03f, +1.635717275e-04f, -2.095678120e-03f, -1.954115341e-03f, -2.139154673e-04f, +1.028447094e-03f, +8.698090905e-04f, +8.381805218e-05f, -3.126544607e-04f, -1.848082291e-04f,
- /* 24, 0 */ -1.364396009e-04f, -7.446376994e-05f, +5.066257662e-04f, +2.030015760e-04f, -9.054261715e-04f, -1.195525937e-03f, +4.683850093e-04f, +2.213825561e-03f, +1.188944940e-03f, -1.856378227e-03f, -2.833970964e-03f, -1.144377463e-04f, +2.758138339e-03f, +2.020697482e-03f, -1.023174933e-03f, -2.248631080e-03f, -6.097868283e-04f, +1.146194663e-03f, +9.693248739e-04f, -1.479047908e-04f, -5.177782008e-04f, -1.250536964e-04f, +1.414906982e-04f, +2.710774794e-05f,
- /* 24, 1 */ -1.307026563e-04f, -8.975162518e-05f, +4.924131238e-04f, +2.542107757e-04f, -8.382130226e-04f, -1.235986710e-03f, +3.277877666e-04f, +2.166758574e-03f, +1.345406789e-03f, -1.683014413e-03f, -2.893234801e-03f, -3.426248829e-04f, +2.666119545e-03f, +2.174888063e-03f, -8.489895579e-04f, -2.270723567e-03f, -7.511023835e-04f, +1.088054225e-03f, +1.029345157e-03f, -8.915647260e-05f, -5.256253050e-04f, -1.535492882e-04f, +1.457592711e-04f, +3.374854096e-05f,
- /* 24, 2 */ -1.243758393e-04f, -1.032931784e-04f, +4.753985127e-04f, +3.013338450e-04f, -7.682542954e-04f, -1.267577330e-03f, +1.888607322e-04f, +2.107952975e-03f, +1.491738775e-03f, -1.501737881e-03f, -2.935653267e-03f, -5.687514710e-04f, +2.558401145e-03f, +2.317921172e-03f, -6.673475630e-04f, -2.279727032e-03f, -8.914213340e-04f, +1.021228789e-03f, +1.084932315e-03f, -2.702967135e-05f, -5.299376467e-04f, -1.826837732e-04f, +1.491486840e-04f, +4.073257728e-05f,
- /* 24, 3 */ -1.175537217e-04f, -1.151066589e-04f, +4.558506985e-04f, +3.442099484e-04f, -6.961194660e-04f, -1.290358261e-03f, +5.243891240e-05f, +2.037998203e-03f, +1.627194859e-03f, -1.313720334e-03f, -2.961057174e-03f, -7.914588446e-04f, +2.435570833e-03f, +2.448830599e-03f, -4.792680017e-04f, -2.275344863e-03f, -1.029819797e-03f, +9.459060659e-04f, +1.135545329e-03f, +3.816638860e-05f, -5.305040204e-04f, -2.122423012e-04f, +1.515631236e-04f, +4.801831197e-05f,
- /* 24, 4 */ -1.103288160e-04f, -1.252215041e-04f, +4.340463917e-04f, +3.827158599e-04f, -6.223744454e-04f, -1.304448109e-03f, -8.067840470e-05f, +1.957545178e-03f, +1.751108674e-03f, -1.120165265e-03f, -2.969385358e-03f, -1.009410776e-03f, +2.298313921e-03f, +2.566719612e-03f, -2.858241016e-04f, -2.257363134e-03f, -1.165366520e-03f, +8.623375551e-04f, +1.180661312e-03f, +1.060874480e-04f, -5.271339238e-04f, -2.419953224e-04f, +1.529084143e-04f, +5.555842361e-05f,
- /* 24, 5 */ -1.027909684e-04f, -1.336775649e-04f, +4.102676936e-04f, +4.167655723e-04f, -5.475775503e-04f, -1.310021272e-03f, -2.097323863e-04f, +1.867300846e-03f, +1.862896930e-03f, -9.222997333e-04f, -2.960684559e-03f, -1.221302229e-03f, +2.147409148e-03f, +2.670767418e-03f, -8.813668768e-05f, -2.225653372e-03f, -1.297129225e-03f, +7.708383352e-04f, +1.219779926e-03f, +1.763556677e-04f, -5.196599496e-04f, -2.716999419e-04f, +1.530928622e-04f, +6.329988035e-05f,
- /* 24, 6 */ -9.502680145e-05f, -1.405242734e-04f, +3.847995909e-04f, +4.463096266e-04f, -4.722756447e-04f, -1.307305241e-03f, -3.340090076e-04f, +1.768022423e-03f, +1.962062202e-03f, -7.213660470e-04f, -2.935108571e-03f, -1.425867893e-03f, +1.983723834e-03f, +2.760235146e-03f, +1.126327965e-04f, -2.180174758e-03f, -1.424181074e-03f, +6.717863708e-04f, +1.252427754e-03f, +2.485613970e-04f, -5.079400707e-04f, -3.011014490e-04f, +1.520281231e-04f, +7.118405837e-05f,
- /* 24, 7 */ -8.711921055e-05f, -1.458197836e-04f, +3.579275186e-04f, +4.713341756e-04f, -3.970004792e-04f, -1.296577576e-03f, -4.528429958e-04f, +1.660511380e-03f, +2.048195092e-03f, -5.186134147e-04f, -2.892916666e-03f, -1.621890436e-03f, +1.808208423e-03f, +2.834471303e-03f, +3.152896222e-04f, -2.120975750e-03f, -1.545607217e-03f, +5.656213428e-04f, +1.278162587e-03f, +3.222652495e-04f, -4.918597964e-04f, -3.299350101e-04f, +1.496300883e-04f, +7.914691395e-05f,
- /* 24, 8 */ -7.914691395e-05f, -1.496300883e-04f, +3.299350101e-04f, +4.918597964e-04f, -3.222652495e-04f, -1.278162587e-03f, -5.656213428e-04f, +1.545607217e-03f, +2.120975750e-03f, -3.152896222e-04f, -2.834471303e-03f, -1.808208423e-03f, +1.621890436e-03f, +2.892916666e-03f, +5.186134147e-04f, -2.048195092e-03f, -1.660511380e-03f, +4.528429958e-04f, +1.296577576e-03f, +3.970004792e-04f, -4.713341756e-04f, -3.579275186e-04f, +1.458197836e-04f, +8.711921055e-05f,
- /* 24, 9 */ -7.118405837e-05f, -1.520281231e-04f, +3.011014490e-04f, +5.079400707e-04f, -2.485613970e-04f, -1.252427754e-03f, -6.717863708e-04f, +1.424181074e-03f, +2.180174758e-03f, -1.126327965e-04f, -2.760235146e-03f, -1.983723834e-03f, +1.425867893e-03f, +2.935108571e-03f, +7.213660470e-04f, -1.962062202e-03f, -1.768022423e-03f, +3.340090076e-04f, +1.307305241e-03f, +4.722756447e-04f, -4.463096266e-04f, -3.847995909e-04f, +1.405242734e-04f, +9.502680145e-05f,
- /* 24,10 */ -6.329988035e-05f, -1.530928622e-04f, +2.716999419e-04f, +5.196599496e-04f, -1.763556677e-04f, -1.219779926e-03f, -7.708383352e-04f, +1.297129225e-03f, +2.225653372e-03f, +8.813668768e-05f, -2.670767418e-03f, -2.147409148e-03f, +1.221302229e-03f, +2.960684559e-03f, +9.222997333e-04f, -1.862896930e-03f, -1.867300846e-03f, +2.097323863e-04f, +1.310021272e-03f, +5.475775503e-04f, -4.167655723e-04f, -4.102676936e-04f, +1.336775649e-04f, +1.027909684e-04f,
- /* 24,11 */ -5.555842361e-05f, -1.529084143e-04f, +2.419953224e-04f, +5.271339238e-04f, -1.060874480e-04f, -1.180661312e-03f, -8.623375551e-04f, +1.165366520e-03f, +2.257363134e-03f, +2.858241016e-04f, -2.566719612e-03f, -2.298313921e-03f, +1.009410776e-03f, +2.969385358e-03f, +1.120165265e-03f, -1.751108674e-03f, -1.957545178e-03f, +8.067840470e-05f, +1.304448109e-03f, +6.223744454e-04f, -3.827158599e-04f, -4.340463917e-04f, +1.252215041e-04f, +1.103288160e-04f,
- /* 24,12 */ -4.801831197e-05f, -1.515631236e-04f, +2.122423012e-04f, +5.305040204e-04f, -3.816638860e-05f, -1.135545329e-03f, -9.459060659e-04f, +1.029819797e-03f, +2.275344863e-03f, +4.792680017e-04f, -2.448830599e-03f, -2.435570833e-03f, +7.914588446e-04f, +2.961057174e-03f, +1.313720334e-03f, -1.627194859e-03f, -2.037998203e-03f, -5.243891240e-05f, +1.290358261e-03f, +6.961194660e-04f, -3.442099484e-04f, -4.558506985e-04f, +1.151066589e-04f, +1.175537217e-04f,
- /* 24,13 */ -4.073257728e-05f, -1.491486840e-04f, +1.826837732e-04f, +5.299376467e-04f, +2.702967135e-05f, -1.084932315e-03f, -1.021228789e-03f, +8.914213340e-04f, +2.279727032e-03f, +6.673475630e-04f, -2.317921172e-03f, -2.558401145e-03f, +5.687514710e-04f, +2.935653267e-03f, +1.501737881e-03f, -1.491738775e-03f, -2.107952975e-03f, -1.888607322e-04f, +1.267577330e-03f, +7.682542954e-04f, -3.013338450e-04f, -4.753985127e-04f, +1.032931784e-04f, +1.243758393e-04f,
- /* 24,14 */ -3.374854096e-05f, -1.457592711e-04f, +1.535492882e-04f, +5.256253050e-04f, +8.915647260e-05f, -1.029345157e-03f, -1.088054225e-03f, +7.511023835e-04f, +2.270723567e-03f, +8.489895579e-04f, -2.174888063e-03f, -2.666119545e-03f, +3.426248829e-04f, +2.893234801e-03f, +1.683014413e-03f, -1.345406789e-03f, -2.166758574e-03f, -3.277877666e-04f, +1.235986710e-03f, +8.382130226e-04f, -2.542107757e-04f, -4.924131238e-04f, +8.975162518e-05f, +1.307026563e-04f,
- /* 24,15 */ -2.710774794e-05f, -1.414906982e-04f, +1.250536964e-04f, +5.177782008e-04f, +1.479047908e-04f, -9.693248739e-04f, -1.146194663e-03f, +6.097868283e-04f, +2.248631080e-03f, +1.023174933e-03f, -2.020697482e-03f, -2.758138339e-03f, +1.144377463e-04f, +2.833970964e-03f, +1.856378227e-03f, -1.188944940e-03f, -2.213825561e-03f, -4.683850093e-04f, +1.195525937e-03f, +9.054261715e-04f, -2.030015760e-04f, -5.066257662e-04f, +7.446376994e-05f, +1.364396009e-04f,
- /* 20, 0 */ +8.618377023e-05f, +6.063813654e-04f, +5.504304823e-05f, -1.285351444e-03f, -7.884572117e-04f, +1.698526656e-03f, +2.051642156e-03f, -1.208844608e-03f, -3.071261118e-03f, -1.337669627e-04f, +3.018986987e-03f, +1.434631920e-03f, -1.928392685e-03f, -1.831072241e-03f, +6.629429882e-04f, +1.334851066e-03f, +2.665316224e-05f, -6.158469302e-04f, -1.222533602e-04f, +1.824770389e-04f,
- /* 20, 1 */ +5.170459821e-05f, +5.921181369e-04f, +1.325211661e-04f, -1.227728603e-03f, -9.042412445e-04f, +1.556639033e-03f, +2.157910711e-03f, -9.769935819e-04f, -3.101316201e-03f, -4.002989059e-04f, +2.944767882e-03f, +1.652572896e-03f, -1.788832610e-03f, -1.953018956e-03f, +5.284364506e-04f, +1.375515478e-03f, +1.120226944e-04f, -6.201709543e-04f, -1.596279961e-04f, +5.435082024e-04f,
- /* 20, 2 */ +1.907003227e-05f, +5.734309365e-04f, +2.052951315e-04f, -1.162740775e-03f, -1.009657150e-03f, +1.406715362e-03f, +2.246673287e-03f, -7.408907618e-04f, -3.109053425e-03f, -6.638330580e-04f, +2.849051730e-03f, +1.860929207e-03f, -1.633773999e-03f, -2.063170847e-03f, +3.857714352e-04f, +1.406686835e-03f, +2.004651158e-04f, -6.190441221e-04f, -1.979927117e-04f, +1.783734753e-04f,
- /* 20, 3 */ -1.149811868e-05f, +5.507184044e-04f, +2.729399910e-04f, -1.091184321e-03f, -1.104169932e-03f, +1.250098855e-03f, +2.317552276e-03f, -5.023622502e-04f, -3.094549152e-03f, -9.223980063e-04f, +2.732457430e-03f, +2.058021578e-03f, -1.464165902e-03f, -2.160404235e-03f, +2.358727957e-04f, +1.427769061e-03f, +2.913280278e-04f, -6.121962531e-04f, -2.370049292e-04f, +1.734448317e-04f,
- /* 20, 4 */ -3.981122763e-05f, +5.243991976e-04f, +3.350936324e-04f, -1.013885501e-03f, -1.187349554e-03f, +1.088157908e-03f, +2.370318438e-03f, -2.632332411e-04f, -3.058053364e-03f, -1.174062761e-03f, +2.595770512e-03f, +2.242244162e-03f, -1.281088328e-03f, -2.243678681e-03f, +7.975052491e-05f, +1.438235101e-03f, +3.839113875e-04f, -5.994006494e-04f, -2.762968272e-04f, +1.660093790e-04f,
- /* 20, 5 */ -6.571426612e-05f, +4.949070444e-04f, +3.914578654e-04f, -9.316921975e-04f, -1.258871974e-03f, +9.222740706e-04f, +2.404890527e-03f, -2.531308203e-05f, -2.999986642e-03f, -1.416952434e-03f, +2.439937364e-03f, +2.412078410e-03f, -1.085745014e-03f, -2.312047403e-03f, -8.150702581e-05f, +1.437633739e-03f, +4.774724341e-04f, -5.804781630e-04f, -3.154780847e-04f, +1.559797103e-04f,
- /* 20, 6 */ -8.908584503e-05f, +4.626858369e-04f, +4.417988543e-04f, -8.454656803e-04f, -1.318519207e-03f, +7.538301290e-04f, +2.421333651e-03f, +2.096193816e-04f, -2.920935727e-03f, -1.649263420e-03f, +2.266058084e-03f, +2.566106310e-03f, -8.794550343e-04f, -2.364667062e-03f, -2.467407834e-04f, +1.425595879e-03f, +5.712311871e-04f, -5.553009320e-04f, -3.541389831e-04f, +1.432933926e-04f,
- /* 20, 7 */ -1.098378936e-04f, +4.281848093e-04f, +4.859469205e-04f, -7.560724855e-04f, -1.366178446e-03f, +5.841984075e-04f, +2.419856400e-03f, +4.398300532e-04f, -2.821647705e-03f, -1.869277969e-03f, +2.075378006e-03f, +2.703022864e-03f, -6.636433018e-04f, -2.400806791e-03f, -4.147293943e-04f, +1.401840253e-03f, +6.643764801e-04f, -5.237957356e-04f, -3.918538488e-04f, +1.279149940e-04f,
- /* 20, 8 */ -1.279149940e-04f, +3.918538488e-04f, +5.237957356e-04f, -6.643764801e-04f, -1.401840253e-03f, +4.147293943e-04f, +2.400806791e-03f, +6.636433018e-04f, -2.703022864e-03f, -2.075378006e-03f, +1.869277969e-03f, +2.821647705e-03f, -4.398300532e-04f, -2.419856400e-03f, -5.841984075e-04f, +1.366178446e-03f, +7.560724855e-04f, -4.859469205e-04f, -4.281848093e-04f, +1.098378936e-04f,
- /* 20, 9 */ -1.432933926e-04f, +3.541389831e-04f, +5.553009320e-04f, -5.712311871e-04f, -1.425595879e-03f, +2.467407834e-04f, +2.364667062e-03f, +8.794550343e-04f, -2.566106310e-03f, -2.266058084e-03f, +1.649263420e-03f, +2.920935727e-03f, -2.096193816e-04f, -2.421333651e-03f, -7.538301290e-04f, +1.318519207e-03f, +8.454656803e-04f, -4.417988543e-04f, -4.626858369e-04f, +8.908584503e-05f,
- /* 20,10 */ -1.559797103e-04f, +3.154780847e-04f, +5.804781630e-04f, -4.774724341e-04f, -1.437633739e-03f, +8.150702581e-05f, +2.312047403e-03f, +1.085745014e-03f, -2.412078410e-03f, -2.439937364e-03f, +1.416952434e-03f, +2.999986642e-03f, +2.531308203e-05f, -2.404890527e-03f, -9.222740706e-04f, +1.258871974e-03f, +9.316921975e-04f, -3.914578654e-04f, -4.949070444e-04f, +6.571426612e-05f,
- /* 20,11 */ -1.660093790e-04f, +2.762968272e-04f, +5.994006494e-04f, -3.839113875e-04f, -1.438235101e-03f, -7.975052491e-05f, +2.243678681e-03f, +1.281088328e-03f, -2.242244162e-03f, -2.595770512e-03f, +1.174062761e-03f, +3.058053364e-03f, +2.632332411e-04f, -2.370318438e-03f, -1.088157908e-03f, +1.187349554e-03f, +1.013885501e-03f, -3.350936324e-04f, -5.243991976e-04f, +3.981122763e-05f,
- /* 20,12 */ -1.734448317e-04f, +2.370049292e-04f, +6.121962531e-04f, -2.913280278e-04f, -1.427769061e-03f, -2.358727957e-04f, +2.160404235e-03f, +1.464165902e-03f, -2.058021578e-03f, -2.732457430e-03f, +9.223980063e-04f, +3.094549152e-03f, +5.023622502e-04f, -2.317552276e-03f, -1.250098855e-03f, +1.104169932e-03f, +1.091184321e-03f, -2.729399910e-04f, -5.507184044e-04f, +1.149811868e-05f,
- /* 20,13 */ -1.783734753e-04f, +1.979927117e-04f, +6.190441221e-04f, -2.004651158e-04f, -1.406686835e-03f, -3.857714352e-04f, +2.063170847e-03f, +1.633773999e-03f, -1.860929207e-03f, -2.849051730e-03f, +6.638330580e-04f, +3.109053425e-03f, +7.408907618e-04f, -2.246673287e-03f, -1.406715362e-03f, +1.009657150e-03f, +1.162740775e-03f, -2.052951315e-04f, -5.734309365e-04f, -1.907003227e-05f,
- /* 20,14 */ -5.435082024e-04f, +1.596279961e-04f, +6.201709543e-04f, -1.120226944e-04f, -1.375515478e-03f, -5.284364506e-04f, +1.953018956e-03f, +1.788832610e-03f, -1.652572896e-03f, -2.944767882e-03f, +4.002989059e-04f, +3.101316201e-03f, +9.769935819e-04f, -2.157910711e-03f, -1.556639033e-03f, +9.042412445e-04f, +1.227728603e-03f, -1.325211661e-04f, -5.921181369e-04f, -5.170459821e-05f,
- /* 20,15 */ -1.824770389e-04f, +1.222533602e-04f, +6.158469302e-04f, -2.665316224e-05f, -1.334851066e-03f, -6.629429882e-04f, +1.831072241e-03f, +1.928392685e-03f, -1.434631920e-03f, -3.018986987e-03f, +1.337669627e-04f, +3.071261118e-03f, +1.208844608e-03f, -2.051642156e-03f, -1.698526656e-03f, +7.884572117e-04f, +1.285351444e-03f, -5.504304823e-05f, -6.063813654e-04f, -8.618377023e-05f,
- /* 20, 0 */ -2.034293425e-04f, +2.330977005e-04f, +6.663349221e-04f, -5.788237715e-04f, -1.543506114e-03f, +7.663264997e-04f, +2.637884518e-03f, -5.016073607e-04f, -3.414789539e-03f, -1.613262342e-04f, +3.393842146e-03f, +7.896927597e-04f, -2.589726790e-03f, -9.705894653e-04f, +1.498255744e-03f, +6.923557695e-04f, -6.435044748e-04f, -2.810018705e-04f, +2.009801156e-04f, +0.000000000e+00f,
- /* 20, 1 */ -6.015260759e-04f, +1.858917095e-04f, +6.809814437e-04f, -4.649883812e-04f, -1.572899641e-03f, +5.610647582e-04f, +2.661959816e-03f, -2.131645492e-04f, -3.406214168e-03f, -4.825221542e-04f, +3.343371924e-03f, +1.074767465e-03f, -2.517456780e-03f, -1.171859801e-03f, +1.437039798e-03f, +8.043742580e-04f, -6.123036842e-04f, -3.290468323e-04f, +1.953154138e-04f, -3.513221827e-04f,
- /* 20, 2 */ -1.932641301e-04f, +1.399021716e-04f, +6.877130848e-04f, -3.520154127e-04f, -1.586701669e-03f, +3.567578182e-04f, +2.662213263e-03f, +7.301175991e-05f, -3.368394423e-03f, -7.993627115e-04f, +3.263658730e-03f, +1.354174959e-03f, -2.421279702e-03f, -1.368123194e-03f, +1.359912061e-03f, +9.136368247e-04f, -5.726346524e-04f, -3.766412744e-04f, +1.862701081e-04f, +2.243392330e-05f,
- /* 20, 3 */ -1.771389305e-04f, +9.560377684e-05f, +6.868748812e-04f, -2.410152618e-04f, -1.585326694e-03f, +1.553000173e-04f, +2.639129491e-03f, +3.543525656e-04f, -3.301882608e-03f, -1.108991788e-03f, +3.155261081e-03f, +1.625281932e-03f, -2.301637793e-03f, -1.557365345e-03f, +1.267093119e-03f, +1.018881552e-03f, -5.244930508e-04f, -4.231658296e-04f, +1.737162070e-04f, +3.471505729e-05f,
- /* 20, 4 */ -1.604844080e-04f, +5.342390613e-05f, +6.788805869e-04f, -1.330327870e-04f, -1.569329509e-03f, -4.149146373e-05f, +2.593408320e-03f, +6.283684809e-04f, -3.207497769e-03f, -1.408623929e-03f, +3.019012048e-03f, +1.885504757e-03f, -2.159209806e-03f, -1.737592880e-03f, +1.158972903e-03f, +1.118840456e-03f, -4.679722330e-04f, -4.679795743e-04f, +1.575667726e-04f, +4.805250238e-05f,
- /* 20, 5 */ -1.435528424e-04f, +1.373966937e-05f, +6.642048742e-04f, -2.903827106e-05f, -1.539395067e-03f, -2.318933016e-04f, +2.525953799e-03f, +8.926733333e-04f, -3.086315860e-03f, -1.695571566e-03f, +2.856012327e-03f, +2.132335710e-03f, -1.994908019e-03f, -1.906854484e-03f, +1.036111434e-03f, +1.212253447e-03f, -4.032663270e-04f, -5.104270987e-04f, +1.377794601e-04f, +6.235351094e-05f,
- /* 20, 6 */ -1.265803873e-04f, -2.312428639e-05f, +6.433751213e-04f, +7.008046528e-05f, -1.496327216e-03f, -4.142913555e-04f, +2.437861323e-03f, +1.145006429e-03f, -2.939657349e-03f, -1.967271220e-03f, +2.667620552e-03f, +2.363368676e-03f, -1.809872756e-03f, -2.063262017e-03f, +8.992377119e-04f, +1.297882648e-03f, -3.306722314e-04f, -5.498460811e-04f, +1.143596169e-04f, +7.750368078e-05f,
- /* 20, 7 */ -1.097853637e-04f, -5.689720211e-05f, +6.169629011e-04f, +1.635247423e-04f, -1.441036462e-03f, -5.871944806e-04f, +2.330402993e-03f, +1.383253258e-03f, -2.769072436e-03f, -2.221308400e-03f, +2.455440941e-03f, +2.576324026e-03f, -1.605464444e-03f, -2.205011397e-03f, +7.492467105e-04f, +1.374526925e-03f, -2.505904541e-04f, -5.855752858e-04f, +8.736287854e-05f, +9.336686615e-05f,
- /* 20, 8 */ -9.336686615e-05f, -8.736287854e-05f, +5.855752858e-04f, +2.505904541e-04f, -1.374526925e-03f, -7.492467105e-04f, +2.205011397e-03f, +1.605464444e-03f, -2.576324026e-03f, -2.455440941e-03f, +2.221308400e-03f, +2.769072436e-03f, -1.383253258e-03f, -2.330402993e-03f, +5.871944806e-04f, +1.441036462e-03f, -1.635247423e-04f, -6.169629011e-04f, +5.689720211e-05f, +1.097853637e-04f,
- /* 20, 9 */ -7.750368078e-05f, -1.143596169e-04f, +5.498460811e-04f, +3.306722314e-04f, -1.297882648e-03f, -8.992377119e-04f, +2.063262017e-03f, +1.809872756e-03f, -2.363368676e-03f, -2.667620552e-03f, +1.967271220e-03f, +2.939657349e-03f, -1.145006429e-03f, -2.437861323e-03f, +4.142913555e-04f, +1.496327216e-03f, -7.008046528e-05f, -6.433751213e-04f, +2.312428639e-05f, +1.265803873e-04f,
- /* 20,10 */ -6.235351094e-05f, -1.377794601e-04f, +5.104270987e-04f, +4.032663270e-04f, -1.212253447e-03f, -1.036111434e-03f, +1.906854484e-03f, +1.994908019e-03f, -2.132335710e-03f, -2.856012327e-03f, +1.695571566e-03f, +3.086315860e-03f, -8.926733333e-04f, -2.525953799e-03f, +2.318933016e-04f, +1.539395067e-03f, +2.903827106e-05f, -6.642048742e-04f, -1.373966937e-05f, +1.435528424e-04f,
- /* 20,11 */ -4.805250238e-05f, -1.575667726e-04f, +4.679795743e-04f, +4.679722330e-04f, -1.118840456e-03f, -1.158972903e-03f, +1.737592880e-03f, +2.159209806e-03f, -1.885504757e-03f, -3.019012048e-03f, +1.408623929e-03f, +3.207497769e-03f, -6.283684809e-04f, -2.593408320e-03f, +4.149146373e-05f, +1.569329509e-03f, +1.330327870e-04f, -6.788805869e-04f, -5.342390613e-05f, +1.604844080e-04f,
- /* 20,12 */ -3.471505729e-05f, -1.737162070e-04f, +4.231658296e-04f, +5.244930508e-04f, -1.018881552e-03f, -1.267093119e-03f, +1.557365345e-03f, +2.301637793e-03f, -1.625281932e-03f, -3.155261081e-03f, +1.108991788e-03f, +3.301882608e-03f, -3.543525656e-04f, -2.639129491e-03f, -1.553000173e-04f, +1.585326694e-03f, +2.410152618e-04f, -6.868748812e-04f, -9.560377684e-05f, +1.771389305e-04f,
- /* 20,13 */ -2.243392330e-05f, -1.862701081e-04f, +3.766412744e-04f, +5.726346524e-04f, -9.136368247e-04f, -1.359912061e-03f, +1.368123194e-03f, +2.421279702e-03f, -1.354174959e-03f, -3.263658730e-03f, +7.993627115e-04f, +3.368394423e-03f, -7.301175991e-05f, -2.662213263e-03f, -3.567578182e-04f, +1.586701669e-03f, +3.520154127e-04f, -6.877130848e-04f, -1.399021716e-04f, +1.932641301e-04f,
- /* 20,14 */ +3.513221827e-04f, -1.953154138e-04f, +3.290468323e-04f, +6.123036842e-04f, -8.043742580e-04f, -1.437039798e-03f, +1.171859801e-03f, +2.517456780e-03f, -1.074767465e-03f, -3.343371924e-03f, +4.825221542e-04f, +3.406214168e-03f, +2.131645492e-04f, -2.661959816e-03f, -5.610647582e-04f, +1.572899641e-03f, +4.649883812e-04f, -6.809814437e-04f, -1.858917095e-04f, +6.015260759e-04f,
- /* 20,15 */ +0.000000000e+00f, -2.009801156e-04f, +2.810018705e-04f, +6.435044748e-04f, -6.923557695e-04f, -1.498255744e-03f, +9.705894653e-04f, +2.589726790e-03f, -7.896927597e-04f, -3.393842146e-03f, +1.613262342e-04f, +3.414789539e-03f, +5.016073607e-04f, -2.637884518e-03f, -7.663264997e-04f, +1.543506114e-03f, +5.788237715e-04f, -6.663349221e-04f, -2.330977005e-04f, +2.034293425e-04f,
- /* 20, 0 */ -1.941987182e-05f, -3.146481294e-04f, +5.561305343e-04f, +3.334278991e-04f, -1.561489082e-03f, -4.085266513e-04f, +2.806699025e-03f, +3.580334706e-04f, -3.695826941e-03f, -1.914605051e-04f, +3.720952014e-03f, -2.295171057e-05f, -2.868623587e-03f, +1.886978960e-04f, +1.629573547e-03f, -2.343761763e-04f, -6.035407960e-04f, +1.633790229e-04f, +3.476344875e-05f, +0.000000000e+00f,
- /* 20, 1 */ +3.929324583e-04f, -3.051602666e-04f, +5.048306585e-04f, +4.229644027e-04f, -1.479104632e-03f, -6.165003319e-04f, +2.717079427e-03f, +6.839596419e-04f, -3.633185574e-03f, -5.723309145e-04f, +3.708003841e-03f, +3.177599071e-04f, -2.901501717e-03f, -4.082823094e-05f, +1.681921685e-03f, -1.265851889e-04f, -6.461214422e-04f, +1.369921977e-04f, +5.166761157e-05f, +0.000000000e+00f,
- /* 20, 2 */ +0.000000000e+00f, -2.925136688e-04f, +4.505823818e-04f, +5.023683161e-04f, -1.383975926e-03f, -8.106644739e-04f, +2.601403151e-03f, +9.973590062e-04f, -3.534000256e-03f, -9.470733637e-04f, +3.656850180e-03f, +6.604608766e-04f, -2.904291326e-03f, -2.777120434e-04f, +1.717239595e-03f, -1.098579054e-05f, -6.829477483e-04f, +1.058859702e-04f, +7.000071077e-05f, +0.000000000e+00f,
- /* 20, 3 */ +0.000000000e+00f, -2.771727451e-04f, +3.943146848e-04f, +5.711822345e-04f, -1.277754215e-03f, -9.892860040e-04f, +2.461570058e-03f, +1.295052213e-03f, -3.399644449e-03f, -1.311681723e-03f, +3.567787737e-03f, +1.001437562e-03f, -2.876278542e-03f, -5.194560271e-04f, +1.734397724e-03f, +1.113422841e-04f, -7.131247677e-04f, +7.019384523e-05f, +8.959420873e-05f, +0.000000000e+00f,
- /* 20, 4 */ +0.000000000e+00f, -2.596009711e-04f, +3.369316672e-04f, +6.291078651e-04f, -1.162161945e-03f, -1.150868125e-03f, +2.299713337e-03f, +1.574086312e-03f, -3.231873732e-03f, -1.662267592e-03f, +3.441541225e-03f, +1.336946247e-03f, -2.817092852e-03f, -7.634316403e-04f, +1.732451285e-03f, +2.391787684e-04f, -7.358020638e-04f, +3.013243736e-05f, +1.102423612e-04f, +0.000000000e+00f,
- /* 20, 5 */ +0.000000000e+00f, -2.402546692e-04f, +2.793008459e-04f, +6.760030262e-04f, -1.038968304e-03f, -1.294161821e-03f, +2.118169008e-03f, +1.831766066e-03f, -3.032802328e-03f, -1.995105343e-03f, +3.279257242e-03f, +1.663257052e-03f, -2.726718246e-03f, -1.006908470e-03f, +1.710658870e-03f, +3.711735089e-04f, -7.501884622e-04f, -1.399708473e-05f, +1.317024534e-04f, +0.000000000e+00f,
- /* 20, 6 */ +0.000000000e+00f, -2.195772899e-04f, +2.222425350e-04f, +7.118766228e-04f, -9.099649801e-04f, -1.418173917e-03f, +1.919443545e-03f, +2.065681697e-03f, -2.804875489e-03f, -2.306675131e-03f, +3.082493013e-03f, +1.976698190e-03f, -2.605500127e-03f, -1.247085413e-03f, +1.668498942e-03f, +5.058593370e-04f, -7.555665992e-04f, -6.180883712e-05f, +1.536956226e-04f, +0.000000000e+00f,
- /* 20, 7 */ +0.000000000e+00f, -1.979942392e-04f, +1.665204182e-04f, +7.368817426e-04f, -7.769424426e-04f, -1.522171671e-03f, +1.706180058e-03f, +2.273732749e-03f, -2.550838108e-03f, -2.593703365e-03f, +2.853200114e-03f, +2.273699984e-03f, -2.454147846e-03f, -1.481123511e-03f, +1.605683934e-03f, +6.416670612e-04f, -7.513070408e-04f, -1.128334053e-04f, +1.759082929e-04f, +0.000000000e+00f,
- /* 20, 8 */ +0.000000000e+00f, -1.759082929e-04f, +1.128334053e-04f, +7.513070408e-04f, -6.416670612e-04f, -1.605683934e-03f, +1.481123511e-03f, +2.454147846e-03f, -2.273699984e-03f, -2.853200114e-03f, +2.593703365e-03f, +2.550838108e-03f, -2.273732749e-03f, -1.706180058e-03f, +1.522171671e-03f, +7.769424426e-04f, -7.368817426e-04f, -1.665204182e-04f, +1.979942392e-04f, +0.000000000e+00f,
- /* 20, 9 */ +0.000000000e+00f, -1.536956226e-04f, +6.180883712e-05f, +7.555665992e-04f, -5.058593370e-04f, -1.668498942e-03f, +1.247085413e-03f, +2.605500127e-03f, -1.976698190e-03f, -3.082493013e-03f, +2.306675131e-03f, +2.804875489e-03f, -2.065681697e-03f, -1.919443545e-03f, +1.418173917e-03f, +9.099649801e-04f, -7.118766228e-04f, -2.222425350e-04f, +2.195772899e-04f, +0.000000000e+00f,
- /* 20,10 */ +0.000000000e+00f, -1.317024534e-04f, +1.399708473e-05f, +7.501884622e-04f, -3.711735089e-04f, -1.710658870e-03f, +1.006908470e-03f, +2.726718246e-03f, -1.663257052e-03f, -3.279257242e-03f, +1.995105343e-03f, +3.032802328e-03f, -1.831766066e-03f, -2.118169008e-03f, +1.294161821e-03f, +1.038968304e-03f, -6.760030262e-04f, -2.793008459e-04f, +2.402546692e-04f, +0.000000000e+00f,
- /* 20,11 */ +0.000000000e+00f, -1.102423612e-04f, -3.013243736e-05f, +7.358020638e-04f, -2.391787684e-04f, -1.732451285e-03f, +7.634316403e-04f, +2.817092852e-03f, -1.336946247e-03f, -3.441541225e-03f, +1.662267592e-03f, +3.231873732e-03f, -1.574086312e-03f, -2.299713337e-03f, +1.150868125e-03f, +1.162161945e-03f, -6.291078651e-04f, -3.369316672e-04f, +2.596009711e-04f, +0.000000000e+00f,
- /* 20,12 */ +0.000000000e+00f, -8.959420873e-05f, -7.019384523e-05f, +7.131247677e-04f, -1.113422841e-04f, -1.734397724e-03f, +5.194560271e-04f, +2.876278542e-03f, -1.001437562e-03f, -3.567787737e-03f, +1.311681723e-03f, +3.399644449e-03f, -1.295052213e-03f, -2.461570058e-03f, +9.892860040e-04f, +1.277754215e-03f, -5.711822345e-04f, -3.943146848e-04f, +2.771727451e-04f, +0.000000000e+00f,
- /* 20,13 */ +0.000000000e+00f, -7.000071077e-05f, -1.058859702e-04f, +6.829477483e-04f, +1.098579054e-05f, -1.717239595e-03f, +2.777120434e-04f, +2.904291326e-03f, -6.604608766e-04f, -3.656850180e-03f, +9.470733637e-04f, +3.534000256e-03f, -9.973590062e-04f, -2.601403151e-03f, +8.106644739e-04f, +1.383975926e-03f, -5.023683161e-04f, -4.505823818e-04f, +2.925136688e-04f, +0.000000000e+00f,
- /* 20,14 */ +0.000000000e+00f, -5.166761157e-05f, -1.369921977e-04f, +6.461214422e-04f, +1.265851889e-04f, -1.681921685e-03f, +4.082823094e-05f, +2.901501717e-03f, -3.177599071e-04f, -3.708003841e-03f, +5.723309145e-04f, +3.633185574e-03f, -6.839596419e-04f, -2.717079427e-03f, +6.165003319e-04f, +1.479104632e-03f, -4.229644027e-04f, -5.048306585e-04f, +3.051602666e-04f, -3.929324583e-04f,
- /* 20,15 */ +0.000000000e+00f, -3.476344875e-05f, -1.633790229e-04f, +6.035407960e-04f, +2.343761763e-04f, -1.629573547e-03f, -1.886978960e-04f, +2.868623587e-03f, +2.295171057e-05f, -3.720952014e-03f, +1.914605051e-04f, +3.695826941e-03f, -3.580334706e-04f, -2.806699025e-03f, +4.085266513e-04f, +1.561489082e-03f, -3.334278991e-04f, -5.561305343e-04f, +3.146481294e-04f, +1.941987182e-05f,
- /* 16, 0 */ +5.220390682e-05f, +8.171943113e-04f, -8.986497643e-04f, -1.446340428e-03f, +2.494478678e-03f, +1.297377101e-03f, -3.898391184e-03f, -2.241676001e-04f, +3.985236927e-03f, -9.413140896e-04f, -2.682700956e-03f, +1.283836927e-03f, +1.053222343e-03f, -7.989322796e-04f, -1.116939505e-04f, +1.571395541e-04f,
- /* 16, 1 */ -3.017522584e-06f, +8.224554322e-04f, -7.403312787e-04f, -1.584058293e-03f, +2.281207335e-03f, +1.631019258e-03f, -3.765802305e-03f, -6.696927435e-04f, +4.024834602e-03f, -5.670028406e-04f, -2.842687093e-03f, +1.097826180e-03f, +1.201600277e-03f, -7.671194843e-04f, -1.747127487e-04f, +1.853334990e-04f,
- /* 16, 2 */ -5.335264618e-05f, +8.154372286e-04f, -5.806604605e-04f, -1.696100138e-03f, +2.046328714e-03f, +1.938434809e-03f, -3.589560871e-03f, -1.106825943e-03f, +4.016290169e-03f, -1.789305351e-04f, -2.971556495e-03f, +8.899684644e-04f, +1.341315594e-03f, -7.213960065e-04f, -2.404043435e-04f, +2.137577131e-04f,
- /* 16, 3 */ -9.830954357e-05f, +7.970128377e-04f, -4.219420013e-04f, -1.781963746e-03f, +1.793485018e-03f, +2.216231187e-03f, -3.372309802e-03f, -1.530099436e-03f, +3.959337237e-03f, +2.181586899e-04f, -3.066783450e-03f, +6.622938144e-04f, +1.469918710e-03f, -6.616076810e-04f, -3.078052257e-04f, +7.052925564e-04f,
- /* 16, 4 */ -1.375232535e-04f, +7.681852053e-04f, -2.663606532e-04f, -1.841529450e-03f, +1.526462906e-03f, +2.461468734e-03f, -3.117203525e-03f, -1.934233820e-03f, +3.854345030e-03f, +6.193258599e-04f, -3.126241364e-03f, +4.171838871e-04f, +1.585017189e-03f, -5.878191605e-04f, -3.758553213e-04f, +2.457392598e-04f,
- /* 16, 5 */ -1.707546409e-04f, +7.300642099e-04f, -1.159532388e-04f, -1.875048978e-03f, +1.249136740e-03f, +2.671693350e-03f, -2.827860277e-03f, -2.314209556e-03f, +3.702317390e-03f, +1.019502933e-03f, -3.148242059e-03f, +1.573479538e-04f, +1.684315055e-03f, -5.003238841e-04f, -4.434113338e-04f, +2.470336005e-04f,
- /* 16, 6 */ -1.978870754e-04f, +6.838430878e-04f, +2.741593655e-05f, -1.883129095e-03f, +9.654119112e-04f, +2.844961691e-03f, -2.508308289e-03f, -2.665334690e-03f, +3.504882792e-03f, +1.413561295e-03f, -3.131569469e-03f, -1.142067707e-04f, +1.765652028e-03f, -3.996506493e-04f, -5.092623113e-04f, +2.432370997e-04f,
- /* 16, 7 */ -2.189210857e-04f, +6.307745862e-04f, +1.620759979e-04f, -1.866710442e-03f, +6.791690772e-04f, +2.979858667e-03f, -2.162926680e-03f, -2.983307830e-03f, +3.264275462e-03f, +1.796381989e-03f, -3.075507089e-03f, -3.942101476e-04f, +1.827042047e-03f, -2.865665382e-04f, -5.721472605e-04f, +2.339671870e-04f,
- /* 16, 8 */ -2.339671870e-04f, +5.721472605e-04f, +2.865665382e-04f, -1.827042047e-03f, +3.942101476e-04f, +3.075507089e-03f, -1.796381989e-03f, -3.264275462e-03f, +2.983307830e-03f, +2.162926680e-03f, -2.979858667e-03f, -6.791690772e-04f, +1.866710442e-03f, -1.620759979e-04f, -6.307745862e-04f, +2.189210857e-04f,
- /* 16, 9 */ -2.432370997e-04f, +5.092623113e-04f, +3.996506493e-04f, -1.765652028e-03f, +1.142067707e-04f, +3.131569469e-03f, -1.413561295e-03f, -3.504882792e-03f, +2.665334690e-03f, +2.508308289e-03f, -2.844961691e-03f, -9.654119112e-04f, +1.883129095e-03f, -2.741593655e-05f, -6.838430878e-04f, +1.978870754e-04f,
- /* 16,10 */ -2.470336005e-04f, +4.434113338e-04f, +5.003238841e-04f, -1.684315055e-03f, -1.573479538e-04f, +3.148242059e-03f, -1.019502933e-03f, -3.702317390e-03f, +2.314209556e-03f, +2.827860277e-03f, -2.671693350e-03f, -1.249136740e-03f, +1.875048978e-03f, +1.159532388e-04f, -7.300642099e-04f, +1.707546409e-04f,
- /* 16,11 */ -2.457392598e-04f, +3.758553213e-04f, +5.878191605e-04f, -1.585017189e-03f, -4.171838871e-04f, +3.126241364e-03f, -6.193258599e-04f, -3.854345030e-03f, +1.934233820e-03f, +3.117203525e-03f, -2.461468734e-03f, -1.526462906e-03f, +1.841529450e-03f, +2.663606532e-04f, -7.681852053e-04f, +1.375232535e-04f,
- /* 16,12 */ -7.052925564e-04f, +3.078052257e-04f, +6.616076810e-04f, -1.469918710e-03f, -6.622938144e-04f, +3.066783450e-03f, -2.181586899e-04f, -3.959337237e-03f, +1.530099436e-03f, +3.372309802e-03f, -2.216231187e-03f, -1.793485018e-03f, +1.781963746e-03f, +4.219420013e-04f, -7.970128377e-04f, +9.830954357e-05f,
- /* 16,13 */ -2.137577131e-04f, +2.404043435e-04f, +7.213960065e-04f, -1.341315594e-03f, -8.899684644e-04f, +2.971556495e-03f, +1.789305351e-04f, -4.016290169e-03f, +1.106825943e-03f, +3.589560871e-03f, -1.938434809e-03f, -2.046328714e-03f, +1.696100138e-03f, +5.806604605e-04f, -8.154372286e-04f, +5.335264618e-05f,
- /* 16,14 */ -1.853334990e-04f, +1.747127487e-04f, +7.671194843e-04f, -1.201600277e-03f, -1.097826180e-03f, +2.842687093e-03f, +5.670028406e-04f, -4.024834602e-03f, +6.696927435e-04f, +3.765802305e-03f, -1.631019258e-03f, -2.281207335e-03f, +1.584058293e-03f, +7.403312787e-04f, -8.224554322e-04f, +3.017522584e-06f,
- /* 16,15 */ -1.571395541e-04f, +1.116939505e-04f, +7.989322796e-04f, -1.053222343e-03f, -1.283836927e-03f, +2.682700956e-03f, +9.413140896e-04f, -3.985236927e-03f, +2.241676001e-04f, +3.898391184e-03f, -1.297377101e-03f, -2.494478678e-03f, +1.446340428e-03f, +8.986497643e-04f, -8.171943113e-04f, -5.220390682e-05f,
- /* 16, 0 */ -2.607744081e-04f, +6.609893225e-04f, +4.777614003e-05f, -2.019079564e-03f, +1.736921610e-03f, +2.230081148e-03f, -4.008512761e-03f, -2.594451583e-04f, +4.172957467e-03f, -1.888269495e-03f, -2.040920379e-03f, +1.975903291e-03f, +1.180452271e-04f, -7.248571600e-04f, +2.468008838e-04f, +0.000000000e+00f,
- /* 16, 1 */ -2.676863913e-04f, +5.906930705e-04f, +2.025069901e-04f, -2.030408814e-03f, +1.418499470e-03f, +2.533235894e-03f, -3.790472440e-03f, -7.745724648e-04f, +4.280846994e-03f, -1.512096765e-03f, -2.325335551e-03f, +1.900137256e-03f, +2.927280105e-04f, -7.805529904e-04f, +2.254162899e-04f, +0.000000000e+00f,
- /* 16, 2 */ -2.680102581e-04f, +5.157202047e-04f, +3.442245196e-04f, -2.011119828e-03f, +1.090886046e-03f, +2.794113365e-03f, -3.522578151e-03f, -1.278469954e-03f, +4.330057965e-03f, -1.106477171e-03f, -2.585166870e-03f, +1.791556445e-03f, +4.737630093e-04f, -8.263772041e-04f, +1.964117366e-04f, +0.000000000e+00f,
- /* 16, 3 */ -7.633646088e-04f, +4.377966605e-04f, +4.713317060e-04f, -1.962888420e-03f, +7.592982470e-04f, +3.009813640e-03f, -3.209287239e-03f, -1.763847458e-03f, +4.319343992e-03f, -6.768749158e-04f, -2.815661672e-03f, +1.650477084e-03f, +6.583936022e-04f, -8.607109932e-04f, +1.597335965e-04f, -4.634120047e-04f,
- /* 16, 4 */ -2.423668462e-04f, +3.585907293e-04f, +5.825699836e-04f, -1.887792011e-03f, +4.288533077e-04f, +3.178189562e-03f, -2.855695312e-03f, -2.223705909e-03f, +4.248362401e-03f, -2.292254995e-04f, -3.012400483e-03f, +1.477771292e-03f, +8.436545409e-04f, -8.820535056e-04f, +1.154955663e-04f, +2.338334874e-05f,
- /* 16, 5 */ -2.066858426e-04f, +2.796840991e-04f, +6.770251471e-04f, -1.788258811e-03f, +1.044882353e-04f, +3.297866045e-03f, -2.467449129e-03f, -2.651446905e-03f, +4.117686315e-03f, +2.301520823e-04f, -3.171379014e-03f, +1.274872332e-03f, +1.026416356e-03f, -8.890585891e-04f, +6.398775754e-05f, +4.782938304e-05f,
- /* 16, 6 */ -1.715009195e-04f, +2.025461567e-04f, +7.541266524e-04f, -1.667012713e-03f, -2.091154912e-04f, +3.368246274e-03f, -2.050651409e-03f, -3.040975547e-03f, +3.928801804e-03f, +6.946508648e-04f, -3.289085050e-03f, +1.043770075e-03f, +1.203434747e-03f, -8.805703554e-04f, +5.682450650e-06f, +7.520965874e-05f,
- /* 16, 7 */ -1.374865801e-04f, +1.285119093e-04f, +8.136405975e-04f, -1.527015027e-03f, -5.076007987e-04f, +3.389504923e-03f, -1.611759203e-03f, -3.386794836e-03f, +3.684090065e-03f, +1.157477637e-03f, -3.362568736e-03f, +7.869964389e-04f, +1.371404208e-03f, -8.556567843e-04f, -5.876379361e-05f, +1.052264476e-04f,
- /* 16, 8 */ -1.052264476e-04f, +5.876379361e-05f, +8.556567843e-04f, -1.371404208e-03f, -7.869964389e-04f, +3.362568736e-03f, -1.157477637e-03f, -3.684090065e-03f, +3.386794836e-03f, +1.611759203e-03f, -3.389504923e-03f, +5.076007987e-04f, +1.527015027e-03f, -8.136405975e-04f, -1.285119093e-04f, +1.374865801e-04f,
- /* 16, 9 */ -7.520965874e-05f, -5.682450650e-06f, +8.805703554e-04f, -1.203434747e-03f, -1.043770075e-03f, +3.289085050e-03f, -6.946508648e-04f, -3.928801804e-03f, +3.040975547e-03f, +2.050651409e-03f, -3.368246274e-03f, +2.091154912e-04f, +1.667012713e-03f, -7.541266524e-04f, -2.025461567e-04f, +1.715009195e-04f,
- /* 16,10 */ -4.782938304e-05f, -6.398775754e-05f, +8.890585891e-04f, -1.026416356e-03f, -1.274872332e-03f, +3.171379014e-03f, -2.301520823e-04f, -4.117686315e-03f, +2.651446905e-03f, +2.467449129e-03f, -3.297866045e-03f, -1.044882353e-04f, +1.788258811e-03f, -6.770251471e-04f, -2.796840991e-04f, +2.066858426e-04f,
- /* 16,11 */ -2.338334874e-05f, -1.154955663e-04f, +8.820535056e-04f, -8.436545409e-04f, -1.477771292e-03f, +3.012400483e-03f, +2.292254995e-04f, -4.248362401e-03f, +2.223705909e-03f, +2.855695312e-03f, -3.178189562e-03f, -4.288533077e-04f, +1.887792011e-03f, -5.825699836e-04f, -3.585907293e-04f, +2.423668462e-04f,
- /* 16,12 */ +4.634120047e-04f, -1.597335965e-04f, +8.607109932e-04f, -6.583936022e-04f, -1.650477084e-03f, +2.815661672e-03f, +6.768749158e-04f, -4.319343992e-03f, +1.763847458e-03f, +3.209287239e-03f, -3.009813640e-03f, -7.592982470e-04f, +1.962888420e-03f, -4.713317060e-04f, -4.377966605e-04f, +7.633646088e-04f,
- /* 16,13 */ +0.000000000e+00f, -1.964117366e-04f, +8.263772041e-04f, -4.737630093e-04f, -1.791556445e-03f, +2.585166870e-03f, +1.106477171e-03f, -4.330057965e-03f, +1.278469954e-03f, +3.522578151e-03f, -2.794113365e-03f, -1.090886046e-03f, +2.011119828e-03f, -3.442245196e-04f, -5.157202047e-04f, +2.680102581e-04f,
- /* 16,14 */ +0.000000000e+00f, -2.254162899e-04f, +7.805529904e-04f, -2.927280105e-04f, -1.900137256e-03f, +2.325335551e-03f, +1.512096765e-03f, -4.280846994e-03f, +7.745724648e-04f, +3.790472440e-03f, -2.533235894e-03f, -1.418499470e-03f, +2.030408814e-03f, -2.025069901e-04f, -5.906930705e-04f, +2.676863913e-04f,
- /* 16,15 */ +0.000000000e+00f, -2.468008838e-04f, +7.248571600e-04f, -1.180452271e-04f, -1.975903291e-03f, +2.040920379e-03f, +1.888269495e-03f, -4.172957467e-03f, +2.594451583e-04f, +4.008512761e-03f, -2.230081148e-03f, -1.736921610e-03f, +2.019079564e-03f, -4.777614003e-05f, -6.609893225e-04f, +2.607744081e-04f,
- /* 16, 0 */ -1.129954761e-04f, -3.969443331e-04f, +7.863457700e-04f, -1.968627401e-03f, +6.635626436e-04f, +3.065104554e-03f, -4.014723865e-03f, -2.972906332e-04f, +4.272130602e-03f, -2.778972616e-03f, -1.044103847e-03f, +2.069667087e-03f, -6.906315332e-04f, -2.376896670e-04f, +1.523656204e-04f, +0.000000000e+00f,
- /* 16, 1 */ -7.672972562e-05f, -4.458313625e-04f, +8.604609066e-04f, -1.839340292e-03f, +2.869810557e-04f, +3.294943885e-03f, -3.696990655e-03f, -8.869321804e-04f, +4.464175630e-03f, -2.440111513e-03f, -1.421957113e-03f, +2.139058837e-03f, -5.737860098e-04f, -3.273087897e-04f, +1.941703587e-04f, +0.000000000e+00f,
- /* 16, 2 */ -4.409159553e-05f, -4.801879450e-04f, +9.129725459e-04f, -1.685578963e-03f, -7.929130936e-05f, +3.465994861e-03f, -3.324955442e-03f, -1.461843594e-03f, +4.586914931e-03f, -2.053108579e-03f, -1.790295465e-03f, +2.173860444e-03f, -4.367566844e-04f, -4.185294039e-04f, +2.375950982e-04f, +0.000000000e+00f,
- /* 16, 3 */ +4.855802427e-04f, -5.008892512e-04f, +9.443143993e-04f, -1.511399569e-03f, -4.293120730e-04f, +3.576852207e-03f, -2.905512498e-03f, -2.012499819e-03f, +4.637578076e-03f, -1.623510702e-03f, -2.142238116e-03f, +2.171667537e-03f, -2.809771210e-04f, -5.093533546e-04f, +2.816859426e-04f, +0.000000000e+00f,
- /* 16, 4 */ +0.000000000e+00f, -5.090007623e-04f, +9.553248878e-04f, -1.321049384e-03f, -7.576441950e-04f, +3.627202827e-03f, -2.446291464e-03f, -2.529812382e-03f, +4.614629236e-03f, -1.157740361e-03f, -2.470980778e-03f, +2.130685105e-03f, -1.083629217e-04f, -5.976383603e-04f, +3.253590588e-04f, +0.000000000e+00f,
- /* 16, 5 */ +0.000000000e+00f, -5.057402285e-04f, +9.472067544e-04f, -1.118874842e-03f, -1.059441268e-03f, +3.617806804e-03f, -1.955510679e-03f, -3.005292216e-03f, +4.517805471e-03f, -6.629933593e-04f, -2.769928158e-03f, +2.049789183e-03f, +7.870314989e-05f, -6.811391839e-04f, +3.674140144e-04f, +0.000000000e+00f,
- /* 16, 6 */ +0.000000000e+00f, -4.924395299e-04f, +9.214808972e-04f, -9.092313688e-04f, -1.330518654e-03f, +3.550458465e-03f, -1.441821213e-03f, -3.431200966e-03f, +4.348131386e-03f, -1.471199733e-04f, -3.032825993e-03f, +1.928577081e-03f, +2.773970394e-04f, -7.575537377e-04f, +4.065510896e-04f, +0.000000000e+00f,
- /* 16, 7 */ +0.000000000e+00f, -4.705072223e-04f, +8.799357120e-04f, -6.963968181e-04f, -1.567409460e-03f, +3.427928686e-03f, -9.141447359e-04f, -3.800687882e-03f, +4.107909720e-03f, +3.815084058e-04f, -3.253889950e-03f, +1.767404663e-03f, +4.844901765e-04f, -8.245732787e-04f, +4.413924818e-04f, +0.000000000e+00f,
- /* 16, 8 */ +0.000000000e+00f, -4.413924818e-04f, +8.245732787e-04f, -4.844901765e-04f, -1.767404663e-03f, +3.253889950e-03f, -3.815084058e-04f, -4.107909720e-03f, +3.800687882e-03f, +9.141447359e-04f, -3.427928686e-03f, +1.567409460e-03f, +6.963968181e-04f, -8.799357120e-04f, +4.705072223e-04f, +0.000000000e+00f,
- /* 16, 9 */ +0.000000000e+00f, -4.065510896e-04f, +7.575537377e-04f, -2.773970394e-04f, -1.928577081e-03f, +3.032825993e-03f, +1.471199733e-04f, -4.348131386e-03f, +3.431200966e-03f, +1.441821213e-03f, -3.550458465e-03f, +1.330518654e-03f, +9.092313688e-04f, -9.214808972e-04f, +4.924395299e-04f, +0.000000000e+00f,
- /* 16,10 */ +0.000000000e+00f, -3.674140144e-04f, +6.811391839e-04f, -7.870314989e-05f, -2.049789183e-03f, +2.769928158e-03f, +6.629933593e-04f, -4.517805471e-03f, +3.005292216e-03f, +1.955510679e-03f, -3.617806804e-03f, +1.059441268e-03f, +1.118874842e-03f, -9.472067544e-04f, +5.057402285e-04f, +0.000000000e+00f,
- /* 16,11 */ +0.000000000e+00f, -3.253590588e-04f, +5.976383603e-04f, +1.083629217e-04f, -2.130685105e-03f, +2.470980778e-03f, +1.157740361e-03f, -4.614629236e-03f, +2.529812382e-03f, +2.446291464e-03f, -3.627202827e-03f, +7.576441950e-04f, +1.321049384e-03f, -9.553248878e-04f, +5.090007623e-04f, +0.000000000e+00f,
- /* 16,12 */ +0.000000000e+00f, -2.816859426e-04f, +5.093533546e-04f, +2.809771210e-04f, -2.171667537e-03f, +2.142238116e-03f, +1.623510702e-03f, -4.637578076e-03f, +2.012499819e-03f, +2.905512498e-03f, -3.576852207e-03f, +4.293120730e-04f, +1.511399569e-03f, -9.443143993e-04f, +5.008892512e-04f, -4.855802427e-04f,
- /* 16,13 */ +0.000000000e+00f, -2.375950982e-04f, +4.185294039e-04f, +4.367566844e-04f, -2.173860444e-03f, +1.790295465e-03f, +2.053108579e-03f, -4.586914931e-03f, +1.461843594e-03f, +3.324955442e-03f, -3.465994861e-03f, +7.929130936e-05f, +1.685578963e-03f, -9.129725459e-04f, +4.801879450e-04f, +4.409159553e-05f,
- /* 16,14 */ +0.000000000e+00f, -1.941703587e-04f, +3.273087897e-04f, +5.737860098e-04f, -2.139058837e-03f, +1.421957113e-03f, +2.440111513e-03f, -4.464175630e-03f, +8.869321804e-04f, +3.696990655e-03f, -3.294943885e-03f, -2.869810557e-04f, +1.839340292e-03f, -8.604609066e-04f, +4.458313625e-04f, +7.672972562e-05f,
- /* 16,15 */ +0.000000000e+00f, -1.523656204e-04f, +2.376896670e-04f, +6.906315332e-04f, -2.069667087e-03f, +1.044103847e-03f, +2.778972616e-03f, -4.272130602e-03f, +2.972906332e-04f, +4.014723865e-03f, -3.065104554e-03f, -6.635626436e-04f, +1.968627401e-03f, -7.863457700e-04f, +3.969443331e-04f, +1.129954761e-04f,
- /* 12, 0 */ +1.006092301e-03f, -1.356592138e-03f, -5.291224839e-04f, +3.716365432e-03f, -3.908475818e-03f, -3.377012930e-04f, +4.272902045e-03f, -3.529241849e-03f, +1.347121185e-04f, +1.576582805e-03f, -1.021094463e-03f, +2.080147558e-04f,
- /* 12, 1 */ +9.703330579e-04f, -1.123568815e-03f, -8.960631472e-04f, +3.830455785e-03f, -3.478978472e-03f, -1.006731283e-03f, +4.564422369e-03f, -3.270752231e-03f, -2.802589631e-04f, +1.777910422e-03f, -1.013271813e-03f, +1.540375404e-04f,
- /* 12, 2 */ +9.162319547e-04f, -8.831264337e-04f, -1.229457804e-03f, +3.871345986e-03f, -2.993425932e-03f, -1.656773890e-03f, +4.776559314e-03f, -2.944064628e-03f, -7.081711396e-04f, +1.955059288e-03f, -9.809799530e-04f, +8.895597490e-05f,
- /* 12, 3 */ +8.464715149e-04f, -6.407365814e-04f, -1.524162434e-03f, +3.840336583e-03f, -2.461816027e-03f, -2.275602698e-03f, +4.904341682e-03f, -2.553815646e-03f, -1.140836495e-03f, +2.102763165e-03f, -9.230670815e-04f, +1.349777819e-05f,
- /* 12, 4 */ +7.639192828e-04f, -4.016125871e-04f, -1.776040886e-03f, +3.740131991e-03f, -1.894910812e-03f, -2.851629129e-03f, +4.944422783e-03f, -2.106045312e-03f, -1.569658753e-03f, +2.216140176e-03f, -8.389345160e-04f, -7.124952580e-05f,
- /* 12, 5 */ +6.715456333e-04f, -1.706046467e-04f, -1.982015497e-03f, +3.574748146e-03f, -1.304005398e-03f, -3.374137989e-03f, +4.895165032e-03f, -1.608099956e-03f, -1.985807614e-03f, +2.290824513e-03f, -7.285864297e-04f, -1.638417811e-04f,
- /* 12, 6 */ +5.723437636e-04f, +4.789167018e-05f, -2.140092391e-03f, +3.349394124e-03f, -7.006885404e-04f, -3.833503957e-03f, +4.756688774e-03f, -1.068504673e-03f, -2.380403858e-03f, +2.323091638e-03f, -5.926669574e-04f, -2.624916697e-04f,
- /* 12, 7 */ +4.692537952e-04f, +2.500119139e-04f, -2.249361715e-03f, +3.070330944e-03f, -9.660032268e-05f, -4.221384345e-03f, +4.530883988e-03f, -4.968076992e-04f, -2.744711242e-03f, +2.309973659e-03f, -4.324830696e-04f, -3.650927179e-04f,
- /* 12, 8 */ +3.650927179e-04f, +4.324830696e-04f, -2.309973659e-03f, +2.744711242e-03f, +4.968076992e-04f, -4.530883988e-03f, +4.221384345e-03f, +9.660032268e-05f, -3.070330944e-03f, +2.249361715e-03f, -2.500119139e-04f, -4.692537952e-04f,
- /* 12, 9 */ +2.624916697e-04f, +5.926669574e-04f, -2.323091638e-03f, +2.380403858e-03f, +1.068504673e-03f, -4.756688774e-03f, +3.833503957e-03f, +7.006885404e-04f, -3.349394124e-03f, +2.140092391e-03f, -4.789167018e-05f, -5.723437636e-04f,
- /* 12,10 */ +1.638417811e-04f, +7.285864297e-04f, -2.290824513e-03f, +1.985807614e-03f, +1.608099956e-03f, -4.895165032e-03f, +3.374137989e-03f, +1.304005398e-03f, -3.574748146e-03f, +1.982015497e-03f, +1.706046467e-04f, -6.715456333e-04f,
- /* 12,11 */ +7.124952580e-05f, +8.389345160e-04f, -2.216140176e-03f, +1.569658753e-03f, +2.106045312e-03f, -4.944422783e-03f, +2.851629129e-03f, +1.894910812e-03f, -3.740131991e-03f, +1.776040886e-03f, +4.016125871e-04f, -7.639192828e-04f,
- /* 12,12 */ -1.349777819e-05f, +9.230670815e-04f, -2.102763165e-03f, +1.140836495e-03f, +2.553815646e-03f, -4.904341682e-03f, +2.275602698e-03f, +2.461816027e-03f, -3.840336583e-03f, +1.524162434e-03f, +6.407365814e-04f, -8.464715149e-04f,
- /* 12,13 */ -8.895597490e-05f, +9.809799530e-04f, -1.955059288e-03f, +7.081711396e-04f, +2.944064628e-03f, -4.776559314e-03f, +1.656773890e-03f, +2.993425932e-03f, -3.871345986e-03f, +1.229457804e-03f, +8.831264337e-04f, -9.162319547e-04f,
- /* 12,14 */ -1.540375404e-04f, +1.013271813e-03f, -1.777910422e-03f, +2.802589631e-04f, +3.270752231e-03f, -4.564422369e-03f, +1.006731283e-03f, +3.478978472e-03f, -3.830455785e-03f, +8.960631472e-04f, +1.123568815e-03f, -9.703330579e-04f,
- /* 12,15 */ -2.080147558e-04f, +1.021094463e-03f, -1.576582805e-03f, -1.347121185e-04f, +3.529241849e-03f, -4.272902045e-03f, +3.377012930e-04f, +3.908475818e-03f, -3.716365432e-03f, +5.291224839e-04f, +1.356592138e-03f, -1.006092301e-03f,
- /* 12, 0 */ +7.165252154e-04f, -4.291307465e-04f, -1.619691310e-03f, +4.112050532e-03f, -3.684471854e-03f, -3.806742210e-04f, +4.167864669e-03f, -4.064044128e-03f, +1.285631838e-03f, +6.994376016e-04f, -8.205283947e-04f, +3.212754781e-04f,
- /* 12, 1 */ +6.042449626e-04f, -1.684748882e-04f, -1.902468489e-03f, +4.073990388e-03f, -3.134198780e-03f, -1.133926469e-03f, +4.572939653e-03f, -3.928365474e-03f, +9.055999228e-04f, +9.731965741e-04f, -9.124383184e-04f, +3.311614162e-04f,
- /* 12, 2 */ +4.874350434e-04f, +7.694308689e-05f, -2.130082097e-03f, +3.953363211e-03f, -2.529802622e-03f, -1.863076830e-03f, +4.889863669e-03f, -3.705392569e-03f, +4.862599326e-04f, +1.243729140e-03f, -9.884795125e-04f, +3.301883495e-04f,
- /* 12, 3 */ +3.696734183e-04f, +3.022578517e-04f, -2.300114973e-03f, +3.755428771e-03f, -1.885047396e-03f, -2.552675098e-03f, +5.110657479e-03f, -3.397530000e-03f, +3.551878686e-05f, +1.504029611e-03f, -1.045032679e-03f, +3.171093301e-04f,
- /* 12, 4 */ +2.542791637e-04f, +5.034105172e-04f, -2.411611526e-03f, +3.487034212e-03f, -1.214371318e-03f, -3.188181667e-03f, +5.229403271e-03f, -3.009202669e-03f, -4.376222644e-04f, +1.746934410e-03f, -1.078752986e-03f, +2.909691299e-04f,
- /* 12, 5 */ +1.442362351e-04f, +6.772076791e-04f, -2.465038541e-03f, +3.156407157e-03f, -5.325427440e-04f, -3.756300237e-03f, +5.242404627e-03f, -2.546799451e-03f, -9.232494237e-04f, +1.965304174e-03f, -1.086687765e-03f, +2.511615343e-04f,
- /* 12, 6 */ +4.213190220e-05f, +8.213542577e-04f, -2.462211671e-03f, +2.772920825e-03f, +1.456866600e-04f, -4.245279979e-03f, +5.148294544e-03f, -2.018567160e-03f, -1.410748009e-03f, +2.152214196e-03f, -1.066390037e-03f, +1.974793328e-04f,
- /* 12, 7 */ -4.988918599e-05f, +9.344605010e-04f, -2.406190818e-03f, +2.346837790e-03f, +8.059229054e-04f, -4.645179801e-03f, +4.948088353e-03f, -1.434456490e-03f, -1.889039390e-03f, +2.301148282e-03f, -1.016024228e-03f, +1.301548676e-04f,
- /* 12, 8 */ -1.301548676e-04f, +1.016024228e-03f, -2.301148282e-03f, +1.889039390e-03f, +1.434456490e-03f, -4.948088353e-03f, +4.645179801e-03f, -8.059229054e-04f, -2.346837790e-03f, +2.406190818e-03f, -9.344605010e-04f, +4.988918599e-05f,
- /* 12, 9 */ -1.974793328e-04f, +1.066390037e-03f, -2.152214196e-03f, +1.410748009e-03f, +2.018567160e-03f, -5.148294544e-03f, +4.245279979e-03f, -1.456866600e-04f, -2.772920825e-03f, +2.462211671e-03f, -8.213542577e-04f, -4.213190220e-05f,
- /* 12,10 */ -2.511615343e-04f, +1.086687765e-03f, -1.965304174e-03f, +9.232494237e-04f, +2.546799451e-03f, -5.242404627e-03f, +3.756300237e-03f, +5.325427440e-04f, -3.156407157e-03f, +2.465038541e-03f, -6.772076791e-04f, -1.442362351e-04f,
- /* 12,11 */ -2.909691299e-04f, +1.078752986e-03f, -1.746934410e-03f, +4.376222644e-04f, +3.009202669e-03f, -5.229403271e-03f, +3.188181667e-03f, +1.214371318e-03f, -3.487034212e-03f, +2.411611526e-03f, -5.034105172e-04f, -2.542791637e-04f,
- /* 12,12 */ -3.171093301e-04f, +1.045032679e-03f, -1.504029611e-03f, -3.551878686e-05f, +3.397530000e-03f, -5.110657479e-03f, +2.552675098e-03f, +1.885047396e-03f, -3.755428771e-03f, +2.300114973e-03f, -3.022578517e-04f, -3.696734183e-04f,
- /* 12,13 */ -3.301883495e-04f, +9.884795125e-04f, -1.243729140e-03f, -4.862599326e-04f, +3.705392569e-03f, -4.889863669e-03f, +1.863076830e-03f, +2.529802622e-03f, -3.953363211e-03f, +2.130082097e-03f, -7.694308689e-05f, -4.874350434e-04f,
- /* 12,14 */ -3.311614162e-04f, +9.124383184e-04f, -9.731965741e-04f, -9.055999228e-04f, +3.928365474e-03f, -4.572939653e-03f, +1.133926469e-03f, +3.134198780e-03f, -4.073990388e-03f, +1.902468489e-03f, +1.684748882e-04f, -6.042449626e-04f,
- /* 12,15 */ -3.212754781e-04f, +8.205283947e-04f, -6.994376016e-04f, -1.285631838e-03f, +4.064044128e-03f, -4.167864669e-03f, +3.806742210e-04f, +3.684471854e-03f, -4.112050532e-03f, +1.619691310e-03f, +4.291307465e-04f, -7.165252154e-04f
-};
diff --git a/Alc/compat.h b/Alc/compat.h
index f54ef9ce..495bfdf2 100644
--- a/Alc/compat.h
+++ b/Alc/compat.h
@@ -1,6 +1,12 @@
#ifndef AL_COMPAT_H
#define AL_COMPAT_H
+#include "alstring.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
@@ -23,10 +29,29 @@ FILE *al_fopen(const char *fname, const char *mode);
#endif
+struct FileMapping {
+#ifdef _WIN32
+ HANDLE file;
+ HANDLE fmap;
+#else
+ int fd;
+#endif
+ void *ptr;
+ size_t len;
+};
+struct FileMapping MapFileToMem(const char *fname);
+void UnmapFileMem(const struct FileMapping *mapping);
+
+void GetProcBinary(al_string *path, al_string *fname);
+
#ifdef HAVE_DYNLOAD
void *LoadLib(const char *name);
void CloseLib(void *handle);
void *GetSymbol(void *handle, const char *name);
#endif
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
#endif /* AL_COMPAT_H */
diff --git a/Alc/converter.c b/Alc/converter.c
new file mode 100644
index 00000000..ef2eb9af
--- /dev/null
+++ b/Alc/converter.c
@@ -0,0 +1,468 @@
+
+#include "config.h"
+
+#include "converter.h"
+
+#include "fpu_modes.h"
+#include "mixer/defs.h"
+
+
+SampleConverter *CreateSampleConverter(enum DevFmtType srcType, enum DevFmtType dstType, ALsizei numchans, ALsizei srcRate, ALsizei dstRate)
+{
+ SampleConverter *converter;
+ ALsizei step;
+
+ if(numchans <= 0 || srcRate <= 0 || dstRate <= 0)
+ return NULL;
+
+ converter = al_calloc(16, FAM_SIZE(SampleConverter, Chan, numchans));
+ converter->mSrcType = srcType;
+ converter->mDstType = dstType;
+ converter->mNumChannels = numchans;
+ converter->mSrcTypeSize = BytesFromDevFmt(srcType);
+ converter->mDstTypeSize = BytesFromDevFmt(dstType);
+
+ converter->mSrcPrepCount = 0;
+ converter->mFracOffset = 0;
+
+ /* Have to set the mixer FPU mode since that's what the resampler code expects. */
+ START_MIXER_MODE();
+ step = (ALsizei)mind(((ALdouble)srcRate/dstRate*FRACTIONONE) + 0.5,
+ MAX_PITCH * FRACTIONONE);
+ converter->mIncrement = maxi(step, 1);
+ if(converter->mIncrement == FRACTIONONE)
+ converter->mResample = Resample_copy_C;
+ else
+ {
+ /* TODO: Allow other resamplers. */
+ BsincPrepare(converter->mIncrement, &converter->mState.bsinc, &bsinc12);
+ converter->mResample = SelectResampler(BSinc12Resampler);
+ }
+ END_MIXER_MODE();
+
+ return converter;
+}
+
+void DestroySampleConverter(SampleConverter **converter)
+{
+ if(converter)
+ {
+ al_free(*converter);
+ *converter = NULL;
+ }
+}
+
+
+static inline ALfloat Sample_ALbyte(ALbyte val)
+{ return val * (1.0f/128.0f); }
+static inline ALfloat Sample_ALubyte(ALubyte val)
+{ return Sample_ALbyte((ALint)val - 128); }
+
+static inline ALfloat Sample_ALshort(ALshort val)
+{ return val * (1.0f/32768.0f); }
+static inline ALfloat Sample_ALushort(ALushort val)
+{ return Sample_ALshort((ALint)val - 32768); }
+
+static inline ALfloat Sample_ALint(ALint val)
+{ return (val>>7) * (1.0f/16777216.0f); }
+static inline ALfloat Sample_ALuint(ALuint val)
+{ return Sample_ALint(val - INT_MAX - 1); }
+
+static inline ALfloat Sample_ALfloat(ALfloat val)
+{ return val; }
+
+#define DECL_TEMPLATE(T) \
+static inline void Load_##T(ALfloat *restrict dst, const T *restrict src, \
+ ALint srcstep, ALsizei samples) \
+{ \
+ ALsizei i; \
+ for(i = 0;i < samples;i++) \
+ dst[i] = Sample_##T(src[i*srcstep]); \
+}
+
+DECL_TEMPLATE(ALbyte)
+DECL_TEMPLATE(ALubyte)
+DECL_TEMPLATE(ALshort)
+DECL_TEMPLATE(ALushort)
+DECL_TEMPLATE(ALint)
+DECL_TEMPLATE(ALuint)
+DECL_TEMPLATE(ALfloat)
+
+#undef DECL_TEMPLATE
+
+static void LoadSamples(ALfloat *dst, const ALvoid *src, ALint srcstep, enum DevFmtType srctype, ALsizei samples)
+{
+ switch(srctype)
+ {
+ case DevFmtByte:
+ Load_ALbyte(dst, src, srcstep, samples);
+ break;
+ case DevFmtUByte:
+ Load_ALubyte(dst, src, srcstep, samples);
+ break;
+ case DevFmtShort:
+ Load_ALshort(dst, src, srcstep, samples);
+ break;
+ case DevFmtUShort:
+ Load_ALushort(dst, src, srcstep, samples);
+ break;
+ case DevFmtInt:
+ Load_ALint(dst, src, srcstep, samples);
+ break;
+ case DevFmtUInt:
+ Load_ALuint(dst, src, srcstep, samples);
+ break;
+ case DevFmtFloat:
+ Load_ALfloat(dst, src, srcstep, samples);
+ break;
+ }
+}
+
+
+static inline ALbyte ALbyte_Sample(ALfloat val)
+{ return fastf2i(clampf(val*128.0f, -128.0f, 127.0f)); }
+static inline ALubyte ALubyte_Sample(ALfloat val)
+{ return ALbyte_Sample(val)+128; }
+
+static inline ALshort ALshort_Sample(ALfloat val)
+{ return fastf2i(clampf(val*32768.0f, -32768.0f, 32767.0f)); }
+static inline ALushort ALushort_Sample(ALfloat val)
+{ return ALshort_Sample(val)+32768; }
+
+static inline ALint ALint_Sample(ALfloat val)
+{ return fastf2i(clampf(val*16777216.0f, -16777216.0f, 16777215.0f)) << 7; }
+static inline ALuint ALuint_Sample(ALfloat val)
+{ return ALint_Sample(val)+INT_MAX+1; }
+
+static inline ALfloat ALfloat_Sample(ALfloat val)
+{ return val; }
+
+#define DECL_TEMPLATE(T) \
+static inline void Store_##T(T *restrict dst, const ALfloat *restrict src, \
+ ALint dststep, ALsizei samples) \
+{ \
+ ALsizei i; \
+ for(i = 0;i < samples;i++) \
+ dst[i*dststep] = T##_Sample(src[i]); \
+}
+
+DECL_TEMPLATE(ALbyte)
+DECL_TEMPLATE(ALubyte)
+DECL_TEMPLATE(ALshort)
+DECL_TEMPLATE(ALushort)
+DECL_TEMPLATE(ALint)
+DECL_TEMPLATE(ALuint)
+DECL_TEMPLATE(ALfloat)
+
+#undef DECL_TEMPLATE
+
+static void StoreSamples(ALvoid *dst, const ALfloat *src, ALint dststep, enum DevFmtType dsttype, ALsizei samples)
+{
+ switch(dsttype)
+ {
+ case DevFmtByte:
+ Store_ALbyte(dst, src, dststep, samples);
+ break;
+ case DevFmtUByte:
+ Store_ALubyte(dst, src, dststep, samples);
+ break;
+ case DevFmtShort:
+ Store_ALshort(dst, src, dststep, samples);
+ break;
+ case DevFmtUShort:
+ Store_ALushort(dst, src, dststep, samples);
+ break;
+ case DevFmtInt:
+ Store_ALint(dst, src, dststep, samples);
+ break;
+ case DevFmtUInt:
+ Store_ALuint(dst, src, dststep, samples);
+ break;
+ case DevFmtFloat:
+ Store_ALfloat(dst, src, dststep, samples);
+ break;
+ }
+}
+
+
+ALsizei SampleConverterAvailableOut(SampleConverter *converter, ALsizei srcframes)
+{
+ ALint prepcount = converter->mSrcPrepCount;
+ ALsizei increment = converter->mIncrement;
+ ALsizei DataPosFrac = converter->mFracOffset;
+ ALuint64 DataSize64;
+
+ if(prepcount < 0)
+ {
+ /* Negative prepcount means we need to skip that many input samples. */
+ if(-prepcount >= srcframes)
+ return 0;
+ srcframes += prepcount;
+ prepcount = 0;
+ }
+
+ if(srcframes < 1)
+ {
+ /* No output samples if there's no input samples. */
+ return 0;
+ }
+
+ if(prepcount < MAX_RESAMPLE_PADDING*2 &&
+ MAX_RESAMPLE_PADDING*2 - prepcount >= srcframes)
+ {
+ /* Not enough input samples to generate an output sample. */
+ return 0;
+ }
+
+ DataSize64 = prepcount;
+ DataSize64 += srcframes;
+ DataSize64 -= MAX_RESAMPLE_PADDING*2;
+ DataSize64 <<= FRACTIONBITS;
+ DataSize64 -= DataPosFrac;
+
+ /* If we have a full prep, we can generate at least one sample. */
+ return (ALsizei)clampu64((DataSize64 + increment-1)/increment, 1, BUFFERSIZE);
+}
+
+
+ALsizei SampleConverterInput(SampleConverter *converter, const ALvoid **src, ALsizei *srcframes, ALvoid *dst, ALsizei dstframes)
+{
+ const ALsizei SrcFrameSize = converter->mNumChannels * converter->mSrcTypeSize;
+ const ALsizei DstFrameSize = converter->mNumChannels * converter->mDstTypeSize;
+ const ALsizei increment = converter->mIncrement;
+ ALsizei pos = 0;
+
+ START_MIXER_MODE();
+ while(pos < dstframes && *srcframes > 0)
+ {
+ ALfloat *restrict SrcData = ASSUME_ALIGNED(converter->mSrcSamples, 16);
+ ALfloat *restrict DstData = ASSUME_ALIGNED(converter->mDstSamples, 16);
+ ALint prepcount = converter->mSrcPrepCount;
+ ALsizei DataPosFrac = converter->mFracOffset;
+ ALuint64 DataSize64;
+ ALsizei DstSize;
+ ALint toread;
+ ALsizei chan;
+
+ if(prepcount < 0)
+ {
+ /* Negative prepcount means we need to skip that many input samples. */
+ if(-prepcount >= *srcframes)
+ {
+ converter->mSrcPrepCount = prepcount + *srcframes;
+ *srcframes = 0;
+ break;
+ }
+ *src = (const ALbyte*)*src + SrcFrameSize*-prepcount;
+ *srcframes += prepcount;
+ converter->mSrcPrepCount = 0;
+ continue;
+ }
+ toread = mini(*srcframes, BUFFERSIZE - MAX_RESAMPLE_PADDING*2);
+
+ if(prepcount < MAX_RESAMPLE_PADDING*2 &&
+ MAX_RESAMPLE_PADDING*2 - prepcount >= toread)
+ {
+ /* Not enough input samples to generate an output sample. Store
+ * what we're given for later.
+ */
+ for(chan = 0;chan < converter->mNumChannels;chan++)
+ LoadSamples(&converter->Chan[chan].mPrevSamples[prepcount],
+ (const ALbyte*)*src + converter->mSrcTypeSize*chan,
+ converter->mNumChannels, converter->mSrcType, toread
+ );
+
+ converter->mSrcPrepCount = prepcount + toread;
+ *srcframes = 0;
+ break;
+ }
+
+ DataSize64 = prepcount;
+ DataSize64 += toread;
+ DataSize64 -= MAX_RESAMPLE_PADDING*2;
+ DataSize64 <<= FRACTIONBITS;
+ DataSize64 -= DataPosFrac;
+
+ /* If we have a full prep, we can generate at least one sample. */
+ DstSize = (ALsizei)clampu64((DataSize64 + increment-1)/increment, 1, BUFFERSIZE);
+ DstSize = mini(DstSize, dstframes-pos);
+
+ for(chan = 0;chan < converter->mNumChannels;chan++)
+ {
+ const ALbyte *SrcSamples = (const ALbyte*)*src + converter->mSrcTypeSize*chan;
+ ALbyte *DstSamples = (ALbyte*)dst + converter->mDstTypeSize*chan;
+ const ALfloat *ResampledData;
+ ALsizei SrcDataEnd;
+
+ /* Load the previous samples into the source data first, then the
+ * new samples from the input buffer.
+ */
+ memcpy(SrcData, converter->Chan[chan].mPrevSamples,
+ prepcount*sizeof(ALfloat));
+ LoadSamples(SrcData + prepcount, SrcSamples,
+ converter->mNumChannels, converter->mSrcType, toread
+ );
+
+ /* Store as many prep samples for next time as possible, given the
+ * number of output samples being generated.
+ */
+ SrcDataEnd = (DataPosFrac + increment*DstSize)>>FRACTIONBITS;
+ if(SrcDataEnd >= prepcount+toread)
+ memset(converter->Chan[chan].mPrevSamples, 0,
+ sizeof(converter->Chan[chan].mPrevSamples));
+ else
+ {
+ size_t len = mini(MAX_RESAMPLE_PADDING*2, prepcount+toread-SrcDataEnd);
+ memcpy(converter->Chan[chan].mPrevSamples, &SrcData[SrcDataEnd],
+ len*sizeof(ALfloat));
+ memset(converter->Chan[chan].mPrevSamples+len, 0,
+ sizeof(converter->Chan[chan].mPrevSamples) - len*sizeof(ALfloat));
+ }
+
+ /* Now resample, and store the result in the output buffer. */
+ ResampledData = converter->mResample(&converter->mState,
+ SrcData+MAX_RESAMPLE_PADDING, DataPosFrac, increment,
+ DstData, DstSize
+ );
+
+ StoreSamples(DstSamples, ResampledData, converter->mNumChannels,
+ converter->mDstType, DstSize);
+ }
+
+ /* Update the number of prep samples still available, as well as the
+ * fractional offset.
+ */
+ DataPosFrac += increment*DstSize;
+ converter->mSrcPrepCount = mini(prepcount + toread - (DataPosFrac>>FRACTIONBITS),
+ MAX_RESAMPLE_PADDING*2);
+ converter->mFracOffset = DataPosFrac & FRACTIONMASK;
+
+ /* Update the src and dst pointers in case there's still more to do. */
+ *src = (const ALbyte*)*src + SrcFrameSize*(DataPosFrac>>FRACTIONBITS);
+ *srcframes -= mini(*srcframes, (DataPosFrac>>FRACTIONBITS));
+
+ dst = (ALbyte*)dst + DstFrameSize*DstSize;
+ pos += DstSize;
+ }
+ END_MIXER_MODE();
+
+ return pos;
+}
+
+
+ChannelConverter *CreateChannelConverter(enum DevFmtType srcType, enum DevFmtChannels srcChans, enum DevFmtChannels dstChans)
+{
+ ChannelConverter *converter;
+
+ if(srcChans != dstChans && !((srcChans == DevFmtMono && dstChans == DevFmtStereo) ||
+ (srcChans == DevFmtStereo && dstChans == DevFmtMono)))
+ return NULL;
+
+ converter = al_calloc(DEF_ALIGN, sizeof(*converter));
+ converter->mSrcType = srcType;
+ converter->mSrcChans = srcChans;
+ converter->mDstChans = dstChans;
+
+ return converter;
+}
+
+void DestroyChannelConverter(ChannelConverter **converter)
+{
+ if(converter)
+ {
+ al_free(*converter);
+ *converter = NULL;
+ }
+}
+
+
+#define DECL_TEMPLATE(T) \
+static void Mono2Stereo##T(ALfloat *restrict dst, const T *src, ALsizei frames)\
+{ \
+ ALsizei i; \
+ for(i = 0;i < frames;i++) \
+ dst[i*2 + 1] = dst[i*2 + 0] = Sample_##T(src[i]) * 0.707106781187f; \
+} \
+ \
+static void Stereo2Mono##T(ALfloat *restrict dst, const T *src, ALsizei frames)\
+{ \
+ ALsizei i; \
+ for(i = 0;i < frames;i++) \
+ dst[i] = (Sample_##T(src[i*2 + 0])+Sample_##T(src[i*2 + 1])) * \
+ 0.707106781187f; \
+}
+
+DECL_TEMPLATE(ALbyte)
+DECL_TEMPLATE(ALubyte)
+DECL_TEMPLATE(ALshort)
+DECL_TEMPLATE(ALushort)
+DECL_TEMPLATE(ALint)
+DECL_TEMPLATE(ALuint)
+DECL_TEMPLATE(ALfloat)
+
+#undef DECL_TEMPLATE
+
+void ChannelConverterInput(ChannelConverter *converter, const ALvoid *src, ALfloat *dst, ALsizei frames)
+{
+ if(converter->mSrcChans == converter->mDstChans)
+ {
+ LoadSamples(dst, src, 1, converter->mSrcType,
+ frames*ChannelsFromDevFmt(converter->mSrcChans, 0));
+ return;
+ }
+
+ if(converter->mSrcChans == DevFmtStereo && converter->mDstChans == DevFmtMono)
+ {
+ switch(converter->mSrcType)
+ {
+ case DevFmtByte:
+ Stereo2MonoALbyte(dst, src, frames);
+ break;
+ case DevFmtUByte:
+ Stereo2MonoALubyte(dst, src, frames);
+ break;
+ case DevFmtShort:
+ Stereo2MonoALshort(dst, src, frames);
+ break;
+ case DevFmtUShort:
+ Stereo2MonoALushort(dst, src, frames);
+ break;
+ case DevFmtInt:
+ Stereo2MonoALint(dst, src, frames);
+ break;
+ case DevFmtUInt:
+ Stereo2MonoALuint(dst, src, frames);
+ break;
+ case DevFmtFloat:
+ Stereo2MonoALfloat(dst, src, frames);
+ break;
+ }
+ }
+ else /*if(converter->mSrcChans == DevFmtMono && converter->mDstChans == DevFmtStereo)*/
+ {
+ switch(converter->mSrcType)
+ {
+ case DevFmtByte:
+ Mono2StereoALbyte(dst, src, frames);
+ break;
+ case DevFmtUByte:
+ Mono2StereoALubyte(dst, src, frames);
+ break;
+ case DevFmtShort:
+ Mono2StereoALshort(dst, src, frames);
+ break;
+ case DevFmtUShort:
+ Mono2StereoALushort(dst, src, frames);
+ break;
+ case DevFmtInt:
+ Mono2StereoALint(dst, src, frames);
+ break;
+ case DevFmtUInt:
+ Mono2StereoALuint(dst, src, frames);
+ break;
+ case DevFmtFloat:
+ Mono2StereoALfloat(dst, src, frames);
+ break;
+ }
+ }
+}
diff --git a/Alc/converter.h b/Alc/converter.h
new file mode 100644
index 00000000..b58fd831
--- /dev/null
+++ b/Alc/converter.h
@@ -0,0 +1,55 @@
+#ifndef CONVERTER_H
+#define CONVERTER_H
+
+#include "alMain.h"
+#include "alu.h"
+
+#ifdef __cpluspluc
+extern "C" {
+#endif
+
+typedef struct SampleConverter {
+ enum DevFmtType mSrcType;
+ enum DevFmtType mDstType;
+ ALsizei mNumChannels;
+ ALsizei mSrcTypeSize;
+ ALsizei mDstTypeSize;
+
+ ALint mSrcPrepCount;
+
+ ALsizei mFracOffset;
+ ALsizei mIncrement;
+ InterpState mState;
+ ResamplerFunc mResample;
+
+ alignas(16) ALfloat mSrcSamples[BUFFERSIZE];
+ alignas(16) ALfloat mDstSamples[BUFFERSIZE];
+
+ struct {
+ alignas(16) ALfloat mPrevSamples[MAX_RESAMPLE_PADDING*2];
+ } Chan[];
+} SampleConverter;
+
+SampleConverter *CreateSampleConverter(enum DevFmtType srcType, enum DevFmtType dstType, ALsizei numchans, ALsizei srcRate, ALsizei dstRate);
+void DestroySampleConverter(SampleConverter **converter);
+
+ALsizei SampleConverterInput(SampleConverter *converter, const ALvoid **src, ALsizei *srcframes, ALvoid *dst, ALsizei dstframes);
+ALsizei SampleConverterAvailableOut(SampleConverter *converter, ALsizei srcframes);
+
+
+typedef struct ChannelConverter {
+ enum DevFmtType mSrcType;
+ enum DevFmtChannels mSrcChans;
+ enum DevFmtChannels mDstChans;
+} ChannelConverter;
+
+ChannelConverter *CreateChannelConverter(enum DevFmtType srcType, enum DevFmtChannels srcChans, enum DevFmtChannels dstChans);
+void DestroyChannelConverter(ChannelConverter **converter);
+
+void ChannelConverterInput(ChannelConverter *converter, const ALvoid *src, ALfloat *dst, ALsizei frames);
+
+#ifdef __cpluspluc
+}
+#endif
+
+#endif /* CONVERTER_H */
diff --git a/Alc/cpu_caps.h b/Alc/cpu_caps.h
new file mode 100644
index 00000000..328d470e
--- /dev/null
+++ b/Alc/cpu_caps.h
@@ -0,0 +1,15 @@
+#ifndef CPU_CAPS_H
+#define CPU_CAPS_H
+
+extern int CPUCapFlags;
+enum {
+ CPU_CAP_SSE = 1<<0,
+ CPU_CAP_SSE2 = 1<<1,
+ CPU_CAP_SSE3 = 1<<2,
+ CPU_CAP_SSE4_1 = 1<<3,
+ CPU_CAP_NEON = 1<<4,
+};
+
+void FillCPUCaps(int capfilter);
+
+#endif /* CPU_CAPS_H */
diff --git a/Alc/effects/autowah.c b/Alc/effects/autowah.c
index 6770f719..ba1180ef 100644
--- a/Alc/effects/autowah.c
+++ b/Alc/effects/autowah.c
@@ -1,6 +1,6 @@
/**
* OpenAL cross platform audio library
- * Copyright (C) 2013 by Anis A. Hireche, Nasca Octavian Paul
+ * Copyright (C) 2018 by Raul Herraiz.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
@@ -18,181 +18,215 @@
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
+#include "config.h"
+
+#include <math.h>
#include <stdlib.h>
-#include "config.h"
-#include "alu.h"
-#include "alFilter.h"
-#include "alError.h"
#include "alMain.h"
#include "alAuxEffectSlot.h"
+#include "alError.h"
+#include "alu.h"
+#include "filters/defs.h"
-
-/* Auto-wah is simply a low-pass filter with a cutoff frequency that shifts up
- * or down depending on the input signal, and a resonant peak at the cutoff.
- *
- * Currently, we assume a cutoff frequency range of 20hz (no amplitude) to
- * 20khz (peak gain). Peak gain is assumed to be in normalized scale.
- */
+#define MIN_FREQ 20.0f
+#define MAX_FREQ 2500.0f
+#define Q_FACTOR 5.0f
typedef struct ALautowahState {
DERIVE_FROM_TYPE(ALeffectState);
- /* Effect gains for each channel */
- ALfloat Gain[MAX_OUTPUT_CHANNELS];
-
/* Effect parameters */
ALfloat AttackRate;
ALfloat ReleaseRate;
- ALfloat Resonance;
+ ALfloat ResonanceGain;
ALfloat PeakGain;
- ALfloat GainCtrl;
- ALfloat Frequency;
-
- /* Samples processing */
- ALfilterState LowPass;
+ ALfloat FreqMinNorm;
+ ALfloat BandwidthNorm;
+ ALfloat env_delay;
+
+ /* Filter components derived from the envelope. */
+ struct {
+ ALfloat cos_w0;
+ ALfloat alpha;
+ } Env[BUFFERSIZE];
+
+ struct {
+ /* Effect filters' history. */
+ struct {
+ ALfloat z1, z2;
+ } Filter;
+
+ /* Effect gains for each output channel */
+ ALfloat CurrentGains[MAX_OUTPUT_CHANNELS];
+ ALfloat TargetGains[MAX_OUTPUT_CHANNELS];
+ } Chans[MAX_EFFECT_CHANNELS];
+
+ /* Effects buffers */
+ alignas(16) ALfloat BufferOut[BUFFERSIZE];
} ALautowahState;
-static ALvoid ALautowahState_Destruct(ALautowahState *UNUSED(state))
+static ALvoid ALautowahState_Destruct(ALautowahState *state);
+static ALboolean ALautowahState_deviceUpdate(ALautowahState *state, ALCdevice *device);
+static ALvoid ALautowahState_update(ALautowahState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props);
+static ALvoid ALautowahState_process(ALautowahState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
+DECLARE_DEFAULT_ALLOCATORS(ALautowahState)
+
+DEFINE_ALEFFECTSTATE_VTABLE(ALautowahState);
+
+static void ALautowahState_Construct(ALautowahState *state)
{
+ ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
+ SET_VTABLE2(ALautowahState, ALeffectState, state);
}
-static ALboolean ALautowahState_deviceUpdate(ALautowahState *state, ALCdevice *device)
+static ALvoid ALautowahState_Destruct(ALautowahState *state)
{
- state->Frequency = (ALfloat)device->Frequency;
- return AL_TRUE;
+ ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
}
-static ALvoid ALautowahState_update(ALautowahState *state, ALCdevice *device, const ALeffectslot *slot)
+static ALboolean ALautowahState_deviceUpdate(ALautowahState *state, ALCdevice *UNUSED(device))
{
- ALfloat attackTime, releaseTime;
+ /* (Re-)initializing parameters and clear the buffers. */
+ ALsizei i, j;
- attackTime = slot->EffectProps.Autowah.AttackTime * state->Frequency;
- releaseTime = slot->EffectProps.Autowah.ReleaseTime * state->Frequency;
+ state->AttackRate = 1.0f;
+ state->ReleaseRate = 1.0f;
+ state->ResonanceGain = 10.0f;
+ state->PeakGain = 4.5f;
+ state->FreqMinNorm = 4.5e-4f;
+ state->BandwidthNorm = 0.05f;
+ state->env_delay = 0.0f;
- state->AttackRate = powf(1.0f/GAIN_SILENCE_THRESHOLD, 1.0f/attackTime);
- state->ReleaseRate = powf(GAIN_SILENCE_THRESHOLD/1.0f, 1.0f/releaseTime);
- state->PeakGain = slot->EffectProps.Autowah.PeakGain;
- state->Resonance = slot->EffectProps.Autowah.Resonance;
+ memset(state->Env, 0, sizeof(state->Env));
+
+ for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
+ {
+ for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
+ state->Chans[i].CurrentGains[j] = 0.0f;
+ state->Chans[i].Filter.z1 = 0.0f;
+ state->Chans[i].Filter.z2 = 0.0f;
+ }
- ComputeAmbientGains(device, slot->Gain, state->Gain);
+ return AL_TRUE;
}
-static ALvoid ALautowahState_process(ALautowahState *state, ALuint SamplesToDo, const ALfloat *SamplesIn, ALfloat (*SamplesOut)[BUFFERSIZE], ALuint NumChannels)
+static ALvoid ALautowahState_update(ALautowahState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props)
{
- ALuint it, kt;
- ALuint base;
+ const ALCdevice *device = context->Device;
+ ALfloat ReleaseTime;
+ ALsizei i;
+
+ ReleaseTime = clampf(props->Autowah.ReleaseTime, 0.001f, 1.0f);
+
+ state->AttackRate = expf(-1.0f / (props->Autowah.AttackTime*device->Frequency));
+ state->ReleaseRate = expf(-1.0f / (ReleaseTime*device->Frequency));
+ /* 0-20dB Resonance Peak gain */
+ state->ResonanceGain = sqrtf(log10f(props->Autowah.Resonance)*10.0f / 3.0f);
+ state->PeakGain = 1.0f - log10f(props->Autowah.PeakGain/AL_AUTOWAH_MAX_PEAK_GAIN);
+ state->FreqMinNorm = MIN_FREQ / device->Frequency;
+ state->BandwidthNorm = (MAX_FREQ-MIN_FREQ) / device->Frequency;
+
+ STATIC_CAST(ALeffectState,state)->OutBuffer = device->FOAOut.Buffer;
+ STATIC_CAST(ALeffectState,state)->OutChannels = device->FOAOut.NumChannels;
+ for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
+ ComputePanGains(&device->FOAOut, IdentityMatrixf.m[i], slot->Params.Gain,
+ state->Chans[i].TargetGains);
+}
- for(base = 0;base < SamplesToDo;)
+static ALvoid ALautowahState_process(ALautowahState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
+{
+ const ALfloat attack_rate = state->AttackRate;
+ const ALfloat release_rate = state->ReleaseRate;
+ const ALfloat res_gain = state->ResonanceGain;
+ const ALfloat peak_gain = state->PeakGain;
+ const ALfloat freq_min = state->FreqMinNorm;
+ const ALfloat bandwidth = state->BandwidthNorm;
+ ALfloat env_delay;
+ ALsizei c, i;
+
+ env_delay = state->env_delay;
+ for(i = 0;i < SamplesToDo;i++)
{
- ALfloat temps[256];
- ALuint td = minu(256, SamplesToDo-base);
- ALfloat gain = state->GainCtrl;
-
- for(it = 0;it < td;it++)
- {
- ALfloat smp = SamplesIn[it+base];
- ALfloat alpha, w0;
- ALfloat amplitude;
- ALfloat cutoff;
-
- /* Similar to compressor, we get the current amplitude of the
- * incoming signal, and attack or release to reach it. */
- amplitude = fabsf(smp);
- if(amplitude > gain)
- gain = minf(gain*state->AttackRate, amplitude);
- else if(amplitude < gain)
- gain = maxf(gain*state->ReleaseRate, amplitude);
- gain = maxf(gain, GAIN_SILENCE_THRESHOLD);
-
- /* FIXME: What range does the filter cover? */
- cutoff = lerp(20.0f, 20000.0f, minf(gain/state->PeakGain, 1.0f));
-
- /* The code below is like calling ALfilterState_setParams with
- * ALfilterType_LowPass. However, instead of passing a bandwidth,
- * we use the resonance property for Q. This also inlines the call.
- */
- w0 = F_TAU * cutoff / state->Frequency;
-
- /* FIXME: Resonance controls the resonant peak, or Q. How? Not sure
- * that Q = resonance*0.1. */
- alpha = sinf(w0) / (2.0f * state->Resonance*0.1f);
- state->LowPass.b[0] = (1.0f - cosf(w0)) / 2.0f;
- state->LowPass.b[1] = 1.0f - cosf(w0);
- state->LowPass.b[2] = (1.0f - cosf(w0)) / 2.0f;
- state->LowPass.a[0] = 1.0f + alpha;
- state->LowPass.a[1] = -2.0f * cosf(w0);
- state->LowPass.a[2] = 1.0f - alpha;
-
- state->LowPass.b[2] /= state->LowPass.a[0];
- state->LowPass.b[1] /= state->LowPass.a[0];
- state->LowPass.b[0] /= state->LowPass.a[0];
- state->LowPass.a[2] /= state->LowPass.a[0];
- state->LowPass.a[1] /= state->LowPass.a[0];
- state->LowPass.a[0] /= state->LowPass.a[0];
-
- temps[it] = ALfilterState_processSingle(&state->LowPass, smp);
- }
- state->GainCtrl = gain;
+ ALfloat w0, sample, a;
+
+ /* Envelope follower described on the book: Audio Effects, Theory,
+ * Implementation and Application.
+ */
+ sample = peak_gain * fabsf(SamplesIn[0][i]);
+ a = (sample > env_delay) ? attack_rate : release_rate;
+ env_delay = lerp(sample, env_delay, a);
+
+ /* Calculate the cos and alpha components for this sample's filter. */
+ w0 = minf((bandwidth*env_delay + freq_min), 0.46f) * F_TAU;
+ state->Env[i].cos_w0 = cosf(w0);
+ state->Env[i].alpha = sinf(w0)/(2.0f * Q_FACTOR);
+ }
+ state->env_delay = env_delay;
- for(kt = 0;kt < NumChannels;kt++)
+ for(c = 0;c < MAX_EFFECT_CHANNELS; c++)
+ {
+ /* This effectively inlines BiquadFilter_setParams for a peaking
+ * filter and BiquadFilter_processC. The alpha and cosine components
+ * for the filter coefficients were previously calculated with the
+ * envelope. Because the filter changes for each sample, the
+ * coefficients are transient and don't need to be held.
+ */
+ ALfloat z1 = state->Chans[c].Filter.z1;
+ ALfloat z2 = state->Chans[c].Filter.z2;
+
+ for(i = 0;i < SamplesToDo;i++)
{
- ALfloat gain = state->Gain[kt];
- if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
- continue;
-
- for(it = 0;it < td;it++)
- SamplesOut[kt][base+it] += gain * temps[it];
+ const ALfloat alpha = state->Env[i].alpha;
+ const ALfloat cos_w0 = state->Env[i].cos_w0;
+ ALfloat input, output;
+ ALfloat a[3], b[3];
+
+ b[0] = 1.0f + alpha*res_gain;
+ b[1] = -2.0f * cos_w0;
+ b[2] = 1.0f - alpha*res_gain;
+ a[0] = 1.0f + alpha/res_gain;
+ a[1] = -2.0f * cos_w0;
+ a[2] = 1.0f - alpha/res_gain;
+
+ input = SamplesIn[c][i];
+ output = input*(b[0]/a[0]) + z1;
+ z1 = input*(b[1]/a[0]) - output*(a[1]/a[0]) + z2;
+ z2 = input*(b[2]/a[0]) - output*(a[2]/a[0]);
+ state->BufferOut[i] = output;
}
+ state->Chans[c].Filter.z1 = z1;
+ state->Chans[c].Filter.z2 = z2;
- base += td;
+ /* Now, mix the processed sound data to the output. */
+ MixSamples(state->BufferOut, NumChannels, SamplesOut, state->Chans[c].CurrentGains,
+ state->Chans[c].TargetGains, SamplesToDo, 0, SamplesToDo);
}
}
-DECLARE_DEFAULT_ALLOCATORS(ALautowahState)
-
-DEFINE_ALEFFECTSTATE_VTABLE(ALautowahState);
-
+typedef struct AutowahStateFactory {
+ DERIVE_FROM_TYPE(EffectStateFactory);
+} AutowahStateFactory;
-typedef struct ALautowahStateFactory {
- DERIVE_FROM_TYPE(ALeffectStateFactory);
-} ALautowahStateFactory;
-
-static ALeffectState *ALautowahStateFactory_create(ALautowahStateFactory *UNUSED(factory))
+static ALeffectState *AutowahStateFactory_create(AutowahStateFactory *UNUSED(factory))
{
ALautowahState *state;
- state = ALautowahState_New(sizeof(*state));
+ NEW_OBJ0(state, ALautowahState)();
if(!state) return NULL;
- SET_VTABLE2(ALautowahState, ALeffectState, state);
-
- state->AttackRate = 1.0f;
- state->ReleaseRate = 1.0f;
- state->Resonance = 2.0f;
- state->PeakGain = 1.0f;
- state->GainCtrl = 1.0f;
-
- ALfilterState_clear(&state->LowPass);
return STATIC_CAST(ALeffectState, state);
}
-DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALautowahStateFactory);
+DEFINE_EFFECTSTATEFACTORY_VTABLE(AutowahStateFactory);
-ALeffectStateFactory *ALautowahStateFactory_getFactory(void)
+EffectStateFactory *AutowahStateFactory_getFactory(void)
{
- static ALautowahStateFactory AutowahFactory = { { GET_VTABLE2(ALautowahStateFactory, ALeffectStateFactory) } };
+ static AutowahStateFactory AutowahFactory = { { GET_VTABLE2(AutowahStateFactory, EffectStateFactory) } };
- return STATIC_CAST(ALeffectStateFactory, &AutowahFactory);
+ return STATIC_CAST(EffectStateFactory, &AutowahFactory);
}
-
-void ALautowah_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val))
-{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
-void ALautowah_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
-{
- ALautowah_setParami(effect, context, param, vals[0]);
-}
void ALautowah_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
{
ALeffectProps *props = &effect->Props;
@@ -200,45 +234,60 @@ void ALautowah_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, AL
{
case AL_AUTOWAH_ATTACK_TIME:
if(!(val >= AL_AUTOWAH_MIN_ATTACK_TIME && val <= AL_AUTOWAH_MAX_ATTACK_TIME))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ SETERR_RETURN(context, AL_INVALID_VALUE,,"Autowah attack time out of range");
props->Autowah.AttackTime = val;
break;
case AL_AUTOWAH_RELEASE_TIME:
if(!(val >= AL_AUTOWAH_MIN_RELEASE_TIME && val <= AL_AUTOWAH_MAX_RELEASE_TIME))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ SETERR_RETURN(context, AL_INVALID_VALUE,,"Autowah release time out of range");
props->Autowah.ReleaseTime = val;
break;
case AL_AUTOWAH_RESONANCE:
if(!(val >= AL_AUTOWAH_MIN_RESONANCE && val <= AL_AUTOWAH_MAX_RESONANCE))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ SETERR_RETURN(context, AL_INVALID_VALUE,,"Autowah resonance out of range");
props->Autowah.Resonance = val;
break;
case AL_AUTOWAH_PEAK_GAIN:
if(!(val >= AL_AUTOWAH_MIN_PEAK_GAIN && val <= AL_AUTOWAH_MAX_PEAK_GAIN))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ SETERR_RETURN(context, AL_INVALID_VALUE,,"Autowah peak gain out of range");
props->Autowah.PeakGain = val;
break;
default:
- SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ alSetError(context, AL_INVALID_ENUM, "Invalid autowah float property 0x%04x", param);
}
}
+
void ALautowah_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
{
ALautowah_setParamf(effect, context, param, vals[0]);
}
-void ALautowah_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val))
-{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
-void ALautowah_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
+void ALautowah_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint UNUSED(val))
+{
+ alSetError(context, AL_INVALID_ENUM, "Invalid autowah integer property 0x%04x", param);
+}
+
+void ALautowah_setParamiv(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, const ALint *UNUSED(vals))
+{
+ alSetError(context, AL_INVALID_ENUM, "Invalid autowah integer vector property 0x%04x", param);
+}
+
+void ALautowah_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(val))
{
- ALautowah_getParami(effect, context, param, vals);
+ alSetError(context, AL_INVALID_ENUM, "Invalid autowah integer property 0x%04x", param);
}
+void ALautowah_getParamiv(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(vals))
+{
+ alSetError(context, AL_INVALID_ENUM, "Invalid autowah integer vector property 0x%04x", param);
+}
+
void ALautowah_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
{
+
const ALeffectProps *props = &effect->Props;
switch(param)
{
@@ -259,9 +308,11 @@ void ALautowah_getParamf(const ALeffect *effect, ALCcontext *context, ALenum par
break;
default:
- SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ alSetError(context, AL_INVALID_ENUM, "Invalid autowah float property 0x%04x", param);
}
+
}
+
void ALautowah_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
{
ALautowah_getParamf(effect, context, param, vals);
diff --git a/Alc/effects/chorus.c b/Alc/effects/chorus.c
index 7aa5898b..f2861cf5 100644
--- a/Alc/effects/chorus.c
+++ b/Alc/effects/chorus.c
@@ -24,261 +24,289 @@
#include <stdlib.h>
#include "alMain.h"
-#include "alFilter.h"
#include "alAuxEffectSlot.h"
#include "alError.h"
#include "alu.h"
+#include "filters/defs.h"
-enum ChorusWaveForm {
- CWF_Triangle = AL_CHORUS_WAVEFORM_TRIANGLE,
- CWF_Sinusoid = AL_CHORUS_WAVEFORM_SINUSOID
+static_assert(AL_CHORUS_WAVEFORM_SINUSOID == AL_FLANGER_WAVEFORM_SINUSOID, "Chorus/Flanger waveform value mismatch");
+static_assert(AL_CHORUS_WAVEFORM_TRIANGLE == AL_FLANGER_WAVEFORM_TRIANGLE, "Chorus/Flanger waveform value mismatch");
+
+enum WaveForm {
+ WF_Sinusoid,
+ WF_Triangle
};
typedef struct ALchorusState {
DERIVE_FROM_TYPE(ALeffectState);
- ALfloat *SampleBuffer[2];
- ALuint BufferLength;
- ALuint offset;
- ALuint lfo_range;
+ ALfloat *SampleBuffer;
+ ALsizei BufferLength;
+ ALsizei offset;
+
+ ALsizei lfo_offset;
+ ALsizei lfo_range;
ALfloat lfo_scale;
ALint lfo_disp;
/* Gains for left and right sides */
- ALfloat Gain[2][MAX_OUTPUT_CHANNELS];
+ struct {
+ ALfloat Current[MAX_OUTPUT_CHANNELS];
+ ALfloat Target[MAX_OUTPUT_CHANNELS];
+ } Gains[2];
/* effect parameters */
- enum ChorusWaveForm waveform;
+ enum WaveForm waveform;
ALint delay;
ALfloat depth;
ALfloat feedback;
} ALchorusState;
+static ALvoid ALchorusState_Destruct(ALchorusState *state);
+static ALboolean ALchorusState_deviceUpdate(ALchorusState *state, ALCdevice *Device);
+static ALvoid ALchorusState_update(ALchorusState *state, const ALCcontext *Context, const ALeffectslot *Slot, const ALeffectProps *props);
+static ALvoid ALchorusState_process(ALchorusState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
+DECLARE_DEFAULT_ALLOCATORS(ALchorusState)
+
+DEFINE_ALEFFECTSTATE_VTABLE(ALchorusState);
+
+
+static void ALchorusState_Construct(ALchorusState *state)
+{
+ ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
+ SET_VTABLE2(ALchorusState, ALeffectState, state);
+
+ state->BufferLength = 0;
+ state->SampleBuffer = NULL;
+ state->offset = 0;
+ state->lfo_offset = 0;
+ state->lfo_range = 1;
+ state->waveform = WF_Triangle;
+}
+
static ALvoid ALchorusState_Destruct(ALchorusState *state)
{
- free(state->SampleBuffer[0]);
- state->SampleBuffer[0] = NULL;
- state->SampleBuffer[1] = NULL;
+ al_free(state->SampleBuffer);
+ state->SampleBuffer = NULL;
+
+ ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
}
static ALboolean ALchorusState_deviceUpdate(ALchorusState *state, ALCdevice *Device)
{
- ALuint maxlen;
- ALuint it;
+ const ALfloat max_delay = maxf(AL_CHORUS_MAX_DELAY, AL_FLANGER_MAX_DELAY);
+ ALsizei maxlen;
- maxlen = fastf2u(AL_CHORUS_MAX_DELAY * 3.0f * Device->Frequency) + 1;
- maxlen = NextPowerOf2(maxlen);
+ maxlen = NextPowerOf2(float2int(max_delay*2.0f*Device->Frequency) + 1u);
+ if(maxlen <= 0) return AL_FALSE;
if(maxlen != state->BufferLength)
{
- void *temp;
-
- temp = realloc(state->SampleBuffer[0], maxlen * sizeof(ALfloat) * 2);
+ void *temp = al_calloc(16, maxlen * sizeof(ALfloat));
if(!temp) return AL_FALSE;
- state->SampleBuffer[0] = temp;
- state->SampleBuffer[1] = state->SampleBuffer[0] + maxlen;
+
+ al_free(state->SampleBuffer);
+ state->SampleBuffer = temp;
state->BufferLength = maxlen;
}
- for(it = 0;it < state->BufferLength;it++)
- {
- state->SampleBuffer[0][it] = 0.0f;
- state->SampleBuffer[1][it] = 0.0f;
- }
+ memset(state->SampleBuffer, 0, state->BufferLength*sizeof(ALfloat));
+ memset(state->Gains, 0, sizeof(state->Gains));
return AL_TRUE;
}
-static ALvoid ALchorusState_update(ALchorusState *state, ALCdevice *Device, const ALeffectslot *Slot)
+static ALvoid ALchorusState_update(ALchorusState *state, const ALCcontext *Context, const ALeffectslot *Slot, const ALeffectProps *props)
{
- static const ALfloat left_dir[3] = { -1.0f, 0.0f, 0.0f };
- static const ALfloat right_dir[3] = { 1.0f, 0.0f, 0.0f };
- ALfloat frequency = (ALfloat)Device->Frequency;
+ const ALsizei mindelay = MAX_RESAMPLE_PADDING << FRACTIONBITS;
+ const ALCdevice *device = Context->Device;
+ ALfloat frequency = (ALfloat)device->Frequency;
+ ALfloat coeffs[MAX_AMBI_COEFFS];
ALfloat rate;
ALint phase;
- switch(Slot->EffectProps.Chorus.Waveform)
+ switch(props->Chorus.Waveform)
{
case AL_CHORUS_WAVEFORM_TRIANGLE:
- state->waveform = CWF_Triangle;
+ state->waveform = WF_Triangle;
break;
case AL_CHORUS_WAVEFORM_SINUSOID:
- state->waveform = CWF_Sinusoid;
+ state->waveform = WF_Sinusoid;
break;
}
- state->depth = Slot->EffectProps.Chorus.Depth;
- state->feedback = Slot->EffectProps.Chorus.Feedback;
- state->delay = fastf2i(Slot->EffectProps.Chorus.Delay * frequency);
+
+ /* The LFO depth is scaled to be relative to the sample delay. Clamp the
+ * delay and depth to allow enough padding for resampling.
+ */
+ state->delay = maxi(float2int(props->Chorus.Delay*frequency*FRACTIONONE + 0.5f),
+ mindelay);
+ state->depth = minf(props->Chorus.Depth * state->delay,
+ (ALfloat)(state->delay - mindelay));
+
+ state->feedback = props->Chorus.Feedback;
/* Gains for left and right sides */
- ComputeDirectionalGains(Device, left_dir, Slot->Gain, state->Gain[0]);
- ComputeDirectionalGains(Device, right_dir, Slot->Gain, state->Gain[1]);
+ CalcAngleCoeffs(-F_PI_2, 0.0f, 0.0f, coeffs);
+ ComputePanGains(&device->Dry, coeffs, Slot->Params.Gain, state->Gains[0].Target);
+ CalcAngleCoeffs( F_PI_2, 0.0f, 0.0f, coeffs);
+ ComputePanGains(&device->Dry, coeffs, Slot->Params.Gain, state->Gains[1].Target);
- phase = Slot->EffectProps.Chorus.Phase;
- rate = Slot->EffectProps.Chorus.Rate;
+ phase = props->Chorus.Phase;
+ rate = props->Chorus.Rate;
if(!(rate > 0.0f))
{
- state->lfo_scale = 0.0f;
+ state->lfo_offset = 0;
state->lfo_range = 1;
+ state->lfo_scale = 0.0f;
state->lfo_disp = 0;
}
else
{
- /* Calculate LFO coefficient */
- state->lfo_range = fastf2u(frequency/rate + 0.5f);
+ /* Calculate LFO coefficient (number of samples per cycle). Limit the
+ * max range to avoid overflow when calculating the displacement.
+ */
+ ALsizei lfo_range = float2int(minf(frequency/rate + 0.5f, (ALfloat)(INT_MAX/360 - 180)));
+
+ state->lfo_offset = float2int((ALfloat)state->lfo_offset/state->lfo_range*
+ lfo_range + 0.5f) % lfo_range;
+ state->lfo_range = lfo_range;
switch(state->waveform)
{
- case CWF_Triangle:
+ case WF_Triangle:
state->lfo_scale = 4.0f / state->lfo_range;
break;
- case CWF_Sinusoid:
+ case WF_Sinusoid:
state->lfo_scale = F_TAU / state->lfo_range;
break;
}
/* Calculate lfo phase displacement */
- state->lfo_disp = fastf2i(state->lfo_range * (phase/360.0f));
+ if(phase < 0) phase = 360 + phase;
+ state->lfo_disp = (state->lfo_range*phase + 180) / 360;
}
}
-static inline void Triangle(ALint *delay_left, ALint *delay_right, ALuint offset, const ALchorusState *state)
+static void GetTriangleDelays(ALint *restrict delays, ALsizei offset, const ALsizei lfo_range,
+ const ALfloat lfo_scale, const ALfloat depth, const ALsizei delay,
+ const ALsizei todo)
{
- ALfloat lfo_value;
-
- lfo_value = 2.0f - fabsf(2.0f - state->lfo_scale*(offset%state->lfo_range));
- lfo_value *= state->depth * state->delay;
- *delay_left = fastf2i(lfo_value) + state->delay;
-
- offset += state->lfo_disp;
- lfo_value = 2.0f - fabsf(2.0f - state->lfo_scale*(offset%state->lfo_range));
- lfo_value *= state->depth * state->delay;
- *delay_right = fastf2i(lfo_value) + state->delay;
+ ALsizei i;
+ for(i = 0;i < todo;i++)
+ {
+ delays[i] = fastf2i((1.0f - fabsf(2.0f - lfo_scale*offset)) * depth) + delay;
+ offset = (offset+1)%lfo_range;
+ }
}
-static inline void Sinusoid(ALint *delay_left, ALint *delay_right, ALuint offset, const ALchorusState *state)
+static void GetSinusoidDelays(ALint *restrict delays, ALsizei offset, const ALsizei lfo_range,
+ const ALfloat lfo_scale, const ALfloat depth, const ALsizei delay,
+ const ALsizei todo)
{
- ALfloat lfo_value;
-
- lfo_value = 1.0f + sinf(state->lfo_scale*(offset%state->lfo_range));
- lfo_value *= state->depth * state->delay;
- *delay_left = fastf2i(lfo_value) + state->delay;
-
- offset += state->lfo_disp;
- lfo_value = 1.0f + sinf(state->lfo_scale*(offset%state->lfo_range));
- lfo_value *= state->depth * state->delay;
- *delay_right = fastf2i(lfo_value) + state->delay;
-}
-
-#define DECL_TEMPLATE(Func) \
-static void Process##Func(ALchorusState *state, const ALuint SamplesToDo, \
- const ALfloat *restrict SamplesIn, ALfloat (*restrict out)[2]) \
-{ \
- const ALuint bufmask = state->BufferLength-1; \
- ALfloat *restrict leftbuf = state->SampleBuffer[0]; \
- ALfloat *restrict rightbuf = state->SampleBuffer[1]; \
- ALuint offset = state->offset; \
- const ALfloat feedback = state->feedback; \
- ALuint it; \
- \
- for(it = 0;it < SamplesToDo;it++) \
- { \
- ALint delay_left, delay_right; \
- Func(&delay_left, &delay_right, offset, state); \
- \
- out[it][0] = leftbuf[(offset-delay_left)&bufmask]; \
- leftbuf[offset&bufmask] = (out[it][0]+SamplesIn[it]) * feedback; \
- \
- out[it][1] = rightbuf[(offset-delay_right)&bufmask]; \
- rightbuf[offset&bufmask] = (out[it][1]+SamplesIn[it]) * feedback; \
- \
- offset++; \
- } \
- state->offset = offset; \
+ ALsizei i;
+ for(i = 0;i < todo;i++)
+ {
+ delays[i] = fastf2i(sinf(lfo_scale*offset) * depth) + delay;
+ offset = (offset+1)%lfo_range;
+ }
}
-DECL_TEMPLATE(Triangle)
-DECL_TEMPLATE(Sinusoid)
-#undef DECL_TEMPLATE
-
-static ALvoid ALchorusState_process(ALchorusState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels)
+static ALvoid ALchorusState_process(ALchorusState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
{
- ALuint it, kt;
- ALuint base;
+ const ALsizei bufmask = state->BufferLength-1;
+ const ALfloat feedback = state->feedback;
+ const ALsizei avgdelay = (state->delay + (FRACTIONONE>>1)) >> FRACTIONBITS;
+ ALfloat *restrict delaybuf = state->SampleBuffer;
+ ALsizei offset = state->offset;
+ ALsizei i, c;
+ ALsizei base;
for(base = 0;base < SamplesToDo;)
{
- ALfloat temps[128][2];
- ALuint td = minu(128, SamplesToDo-base);
+ const ALsizei todo = mini(256, SamplesToDo-base);
+ ALint moddelays[2][256];
+ alignas(16) ALfloat temps[2][256];
- switch(state->waveform)
+ if(state->waveform == WF_Sinusoid)
{
- case CWF_Triangle:
- ProcessTriangle(state, td, SamplesIn+base, temps);
- break;
- case CWF_Sinusoid:
- ProcessSinusoid(state, td, SamplesIn+base, temps);
- break;
+ GetSinusoidDelays(moddelays[0], state->lfo_offset, state->lfo_range, state->lfo_scale,
+ state->depth, state->delay, todo);
+ GetSinusoidDelays(moddelays[1], (state->lfo_offset+state->lfo_disp)%state->lfo_range,
+ state->lfo_range, state->lfo_scale, state->depth, state->delay,
+ todo);
}
+ else /*if(state->waveform == WF_Triangle)*/
+ {
+ GetTriangleDelays(moddelays[0], state->lfo_offset, state->lfo_range, state->lfo_scale,
+ state->depth, state->delay, todo);
+ GetTriangleDelays(moddelays[1], (state->lfo_offset+state->lfo_disp)%state->lfo_range,
+ state->lfo_range, state->lfo_scale, state->depth, state->delay,
+ todo);
+ }
+ state->lfo_offset = (state->lfo_offset+todo) % state->lfo_range;
- for(kt = 0;kt < NumChannels;kt++)
+ for(i = 0;i < todo;i++)
{
- ALfloat gain = state->Gain[0][kt];
- 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(fabsf(gain) > GAIN_SILENCE_THRESHOLD)
- {
- for(it = 0;it < td;it++)
- SamplesOut[kt][it+base] += temps[it][1] * gain;
- }
+ ALint delay;
+ ALfloat mu;
+
+ // Feed the buffer's input first (necessary for delays < 1).
+ delaybuf[offset&bufmask] = SamplesIn[0][base+i];
+
+ // Tap for the left output.
+ delay = offset - (moddelays[0][i]>>FRACTIONBITS);
+ mu = (moddelays[0][i]&FRACTIONMASK) * (1.0f/FRACTIONONE);
+ temps[0][i] = cubic(delaybuf[(delay+1) & bufmask], delaybuf[(delay ) & bufmask],
+ delaybuf[(delay-1) & bufmask], delaybuf[(delay-2) & bufmask],
+ mu);
+
+ // Tap for the right output.
+ delay = offset - (moddelays[1][i]>>FRACTIONBITS);
+ mu = (moddelays[1][i]&FRACTIONMASK) * (1.0f/FRACTIONONE);
+ temps[1][i] = cubic(delaybuf[(delay+1) & bufmask], delaybuf[(delay ) & bufmask],
+ delaybuf[(delay-1) & bufmask], delaybuf[(delay-2) & bufmask],
+ mu);
+
+ // Accumulate feedback from the average delay of the taps.
+ delaybuf[offset&bufmask] += delaybuf[(offset-avgdelay) & bufmask] * feedback;
+ offset++;
}
- base += td;
- }
-}
+ for(c = 0;c < 2;c++)
+ MixSamples(temps[c], NumChannels, SamplesOut, state->Gains[c].Current,
+ state->Gains[c].Target, SamplesToDo-base, base, todo);
-DECLARE_DEFAULT_ALLOCATORS(ALchorusState)
+ base += todo;
+ }
-DEFINE_ALEFFECTSTATE_VTABLE(ALchorusState);
+ state->offset = offset;
+}
-typedef struct ALchorusStateFactory {
- DERIVE_FROM_TYPE(ALeffectStateFactory);
-} ALchorusStateFactory;
+typedef struct ChorusStateFactory {
+ DERIVE_FROM_TYPE(EffectStateFactory);
+} ChorusStateFactory;
-static ALeffectState *ALchorusStateFactory_create(ALchorusStateFactory *UNUSED(factory))
+static ALeffectState *ChorusStateFactory_create(ChorusStateFactory *UNUSED(factory))
{
ALchorusState *state;
- state = ALchorusState_New(sizeof(*state));
+ NEW_OBJ0(state, ALchorusState)();
if(!state) return NULL;
- SET_VTABLE2(ALchorusState, ALeffectState, state);
-
- state->BufferLength = 0;
- state->SampleBuffer[0] = NULL;
- state->SampleBuffer[1] = NULL;
- state->offset = 0;
- state->lfo_range = 1;
- state->waveform = CWF_Triangle;
return STATIC_CAST(ALeffectState, state);
}
-DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALchorusStateFactory);
+DEFINE_EFFECTSTATEFACTORY_VTABLE(ChorusStateFactory);
-ALeffectStateFactory *ALchorusStateFactory_getFactory(void)
+EffectStateFactory *ChorusStateFactory_getFactory(void)
{
- static ALchorusStateFactory ChorusFactory = { { GET_VTABLE2(ALchorusStateFactory, ALeffectStateFactory) } };
+ static ChorusStateFactory ChorusFactory = { { GET_VTABLE2(ChorusStateFactory, EffectStateFactory) } };
- return STATIC_CAST(ALeffectStateFactory, &ChorusFactory);
+ return STATIC_CAST(EffectStateFactory, &ChorusFactory);
}
@@ -289,24 +317,22 @@ void ALchorus_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALi
{
case AL_CHORUS_WAVEFORM:
if(!(val >= AL_CHORUS_MIN_WAVEFORM && val <= AL_CHORUS_MAX_WAVEFORM))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "Invalid chorus waveform");
props->Chorus.Waveform = val;
break;
case AL_CHORUS_PHASE:
if(!(val >= AL_CHORUS_MIN_PHASE && val <= AL_CHORUS_MAX_PHASE))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "Chorus phase out of range");
props->Chorus.Phase = val;
break;
default:
- SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ alSetError(context, AL_INVALID_ENUM, "Invalid chorus integer property 0x%04x", param);
}
}
void ALchorus_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
-{
- ALchorus_setParami(effect, context, param, vals[0]);
-}
+{ ALchorus_setParami(effect, context, param, vals[0]); }
void ALchorus_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
{
ALeffectProps *props = &effect->Props;
@@ -314,36 +340,34 @@ void ALchorus_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALf
{
case AL_CHORUS_RATE:
if(!(val >= AL_CHORUS_MIN_RATE && val <= AL_CHORUS_MAX_RATE))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "Chorus rate out of range");
props->Chorus.Rate = val;
break;
case AL_CHORUS_DEPTH:
if(!(val >= AL_CHORUS_MIN_DEPTH && val <= AL_CHORUS_MAX_DEPTH))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "Chorus depth out of range");
props->Chorus.Depth = val;
break;
case AL_CHORUS_FEEDBACK:
if(!(val >= AL_CHORUS_MIN_FEEDBACK && val <= AL_CHORUS_MAX_FEEDBACK))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "Chorus feedback out of range");
props->Chorus.Feedback = val;
break;
case AL_CHORUS_DELAY:
if(!(val >= AL_CHORUS_MIN_DELAY && val <= AL_CHORUS_MAX_DELAY))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "Chorus delay out of range");
props->Chorus.Delay = val;
break;
default:
- SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ alSetError(context, AL_INVALID_ENUM, "Invalid chorus float property 0x%04x", param);
}
}
void ALchorus_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
-{
- ALchorus_setParamf(effect, context, param, vals[0]);
-}
+{ ALchorus_setParamf(effect, context, param, vals[0]); }
void ALchorus_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
{
@@ -359,13 +383,11 @@ void ALchorus_getParami(const ALeffect *effect, ALCcontext *context, ALenum para
break;
default:
- SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ alSetError(context, AL_INVALID_ENUM, "Invalid chorus integer property 0x%04x", param);
}
}
void ALchorus_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
-{
- ALchorus_getParami(effect, context, param, vals);
-}
+{ ALchorus_getParami(effect, context, param, vals); }
void ALchorus_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
{
const ALeffectProps *props = &effect->Props;
@@ -388,12 +410,146 @@ void ALchorus_getParamf(const ALeffect *effect, ALCcontext *context, ALenum para
break;
default:
- SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ alSetError(context, AL_INVALID_ENUM, "Invalid chorus float property 0x%04x", param);
}
}
void ALchorus_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
+{ ALchorus_getParamf(effect, context, param, vals); }
+
+DEFINE_ALEFFECT_VTABLE(ALchorus);
+
+
+/* Flanger is basically a chorus with a really short delay. They can both use
+ * the same processing functions, so piggyback flanger on the chorus functions.
+ */
+typedef struct FlangerStateFactory {
+ DERIVE_FROM_TYPE(EffectStateFactory);
+} FlangerStateFactory;
+
+ALeffectState *FlangerStateFactory_create(FlangerStateFactory *UNUSED(factory))
{
- ALchorus_getParamf(effect, context, param, vals);
+ ALchorusState *state;
+
+ NEW_OBJ0(state, ALchorusState)();
+ if(!state) return NULL;
+
+ return STATIC_CAST(ALeffectState, state);
}
-DEFINE_ALEFFECT_VTABLE(ALchorus);
+DEFINE_EFFECTSTATEFACTORY_VTABLE(FlangerStateFactory);
+
+EffectStateFactory *FlangerStateFactory_getFactory(void)
+{
+ static FlangerStateFactory FlangerFactory = { { GET_VTABLE2(FlangerStateFactory, EffectStateFactory) } };
+
+ return STATIC_CAST(EffectStateFactory, &FlangerFactory);
+}
+
+
+void ALflanger_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
+{
+ ALeffectProps *props = &effect->Props;
+ switch(param)
+ {
+ case AL_FLANGER_WAVEFORM:
+ if(!(val >= AL_FLANGER_MIN_WAVEFORM && val <= AL_FLANGER_MAX_WAVEFORM))
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "Invalid flanger waveform");
+ props->Chorus.Waveform = val;
+ break;
+
+ case AL_FLANGER_PHASE:
+ if(!(val >= AL_FLANGER_MIN_PHASE && val <= AL_FLANGER_MAX_PHASE))
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "Flanger phase out of range");
+ props->Chorus.Phase = val;
+ break;
+
+ default:
+ alSetError(context, AL_INVALID_ENUM, "Invalid flanger integer property 0x%04x", param);
+ }
+}
+void ALflanger_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
+{ ALflanger_setParami(effect, context, param, vals[0]); }
+void ALflanger_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
+{
+ ALeffectProps *props = &effect->Props;
+ switch(param)
+ {
+ case AL_FLANGER_RATE:
+ if(!(val >= AL_FLANGER_MIN_RATE && val <= AL_FLANGER_MAX_RATE))
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "Flanger rate out of range");
+ props->Chorus.Rate = val;
+ break;
+
+ case AL_FLANGER_DEPTH:
+ if(!(val >= AL_FLANGER_MIN_DEPTH && val <= AL_FLANGER_MAX_DEPTH))
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "Flanger depth out of range");
+ props->Chorus.Depth = val;
+ break;
+
+ case AL_FLANGER_FEEDBACK:
+ if(!(val >= AL_FLANGER_MIN_FEEDBACK && val <= AL_FLANGER_MAX_FEEDBACK))
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "Flanger feedback out of range");
+ props->Chorus.Feedback = val;
+ break;
+
+ case AL_FLANGER_DELAY:
+ if(!(val >= AL_FLANGER_MIN_DELAY && val <= AL_FLANGER_MAX_DELAY))
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "Flanger delay out of range");
+ props->Chorus.Delay = val;
+ break;
+
+ default:
+ alSetError(context, AL_INVALID_ENUM, "Invalid flanger float property 0x%04x", param);
+ }
+}
+void ALflanger_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
+{ ALflanger_setParamf(effect, context, param, vals[0]); }
+
+void ALflanger_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
+{
+ const ALeffectProps *props = &effect->Props;
+ switch(param)
+ {
+ case AL_FLANGER_WAVEFORM:
+ *val = props->Chorus.Waveform;
+ break;
+
+ case AL_FLANGER_PHASE:
+ *val = props->Chorus.Phase;
+ break;
+
+ default:
+ alSetError(context, AL_INVALID_ENUM, "Invalid flanger integer property 0x%04x", param);
+ }
+}
+void ALflanger_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
+{ ALflanger_getParami(effect, context, param, vals); }
+void ALflanger_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
+{
+ const ALeffectProps *props = &effect->Props;
+ switch(param)
+ {
+ case AL_FLANGER_RATE:
+ *val = props->Chorus.Rate;
+ break;
+
+ case AL_FLANGER_DEPTH:
+ *val = props->Chorus.Depth;
+ break;
+
+ case AL_FLANGER_FEEDBACK:
+ *val = props->Chorus.Feedback;
+ break;
+
+ case AL_FLANGER_DELAY:
+ *val = props->Chorus.Delay;
+ break;
+
+ default:
+ alSetError(context, AL_INVALID_ENUM, "Invalid flanger float property 0x%04x", param);
+ }
+}
+void ALflanger_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
+{ ALflanger_getParamf(effect, context, param, vals); }
+
+DEFINE_ALEFFECT_VTABLE(ALflanger);
diff --git a/Alc/effects/compressor.c b/Alc/effects/compressor.c
index 9859a085..2b4a76b0 100644
--- a/Alc/effects/compressor.c
+++ b/Alc/effects/compressor.c
@@ -27,141 +27,172 @@
#include "alu.h"
+#define AMP_ENVELOPE_MIN 0.5f
+#define AMP_ENVELOPE_MAX 2.0f
+
+#define ATTACK_TIME 0.1f /* 100ms to rise from min to max */
+#define RELEASE_TIME 0.2f /* 200ms to drop from max to min */
+
+
typedef struct ALcompressorState {
DERIVE_FROM_TYPE(ALeffectState);
/* Effect gains for each channel */
- ALfloat Gain[MAX_OUTPUT_CHANNELS];
+ ALfloat Gain[MAX_EFFECT_CHANNELS][MAX_OUTPUT_CHANNELS];
/* Effect parameters */
ALboolean Enabled;
- ALfloat AttackRate;
- ALfloat ReleaseRate;
- ALfloat GainCtrl;
+ ALfloat AttackMult;
+ ALfloat ReleaseMult;
+ ALfloat EnvFollower;
} ALcompressorState;
-static ALvoid ALcompressorState_Destruct(ALcompressorState *UNUSED(state))
+static ALvoid ALcompressorState_Destruct(ALcompressorState *state);
+static ALboolean ALcompressorState_deviceUpdate(ALcompressorState *state, ALCdevice *device);
+static ALvoid ALcompressorState_update(ALcompressorState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props);
+static ALvoid ALcompressorState_process(ALcompressorState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
+DECLARE_DEFAULT_ALLOCATORS(ALcompressorState)
+
+DEFINE_ALEFFECTSTATE_VTABLE(ALcompressorState);
+
+
+static void ALcompressorState_Construct(ALcompressorState *state)
+{
+ ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
+ SET_VTABLE2(ALcompressorState, ALeffectState, state);
+
+ state->Enabled = AL_TRUE;
+ state->AttackMult = 1.0f;
+ state->ReleaseMult = 1.0f;
+ state->EnvFollower = 1.0f;
+}
+
+static ALvoid ALcompressorState_Destruct(ALcompressorState *state)
{
+ ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
}
static ALboolean ALcompressorState_deviceUpdate(ALcompressorState *state, ALCdevice *device)
{
- const ALfloat attackTime = device->Frequency * 0.2f; /* 200ms Attack */
- const ALfloat releaseTime = device->Frequency * 0.4f; /* 400ms Release */
-
- state->AttackRate = 1.0f / attackTime;
- state->ReleaseRate = 1.0f / releaseTime;
+ /* Number of samples to do a full attack and release (non-integer sample
+ * counts are okay).
+ */
+ const ALfloat attackCount = (ALfloat)device->Frequency * ATTACK_TIME;
+ const ALfloat releaseCount = (ALfloat)device->Frequency * RELEASE_TIME;
+
+ /* Calculate per-sample multipliers to attack and release at the desired
+ * rates.
+ */
+ state->AttackMult = powf(AMP_ENVELOPE_MAX/AMP_ENVELOPE_MIN, 1.0f/attackCount);
+ state->ReleaseMult = powf(AMP_ENVELOPE_MIN/AMP_ENVELOPE_MAX, 1.0f/releaseCount);
return AL_TRUE;
}
-static ALvoid ALcompressorState_update(ALcompressorState *state, ALCdevice *device, const ALeffectslot *slot)
+static ALvoid ALcompressorState_update(ALcompressorState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props)
{
- state->Enabled = slot->EffectProps.Compressor.OnOff;
+ const ALCdevice *device = context->Device;
+ ALuint i;
+
+ state->Enabled = props->Compressor.OnOff;
- ComputeAmbientGains(device, slot->Gain, state->Gain);
+ STATIC_CAST(ALeffectState,state)->OutBuffer = device->FOAOut.Buffer;
+ STATIC_CAST(ALeffectState,state)->OutChannels = device->FOAOut.NumChannels;
+ for(i = 0;i < 4;i++)
+ ComputePanGains(&device->FOAOut, IdentityMatrixf.m[i], slot->Params.Gain, state->Gain[i]);
}
-static ALvoid ALcompressorState_process(ALcompressorState *state, ALuint SamplesToDo, const ALfloat *SamplesIn, ALfloat (*SamplesOut)[BUFFERSIZE], ALuint NumChannels)
+static ALvoid ALcompressorState_process(ALcompressorState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
{
- ALuint it, kt;
- ALuint base;
+ ALsizei i, j, k;
+ ALsizei base;
for(base = 0;base < SamplesToDo;)
{
- ALfloat temps[256];
- ALuint td = minu(256, SamplesToDo-base);
+ ALfloat gains[256];
+ ALsizei td = mini(256, SamplesToDo-base);
+ ALfloat env = state->EnvFollower;
+ /* Generate the per-sample gains from the signal envelope. */
if(state->Enabled)
{
- ALfloat output, smp, amplitude;
- ALfloat gain = state->GainCtrl;
-
- for(it = 0;it < td;it++)
+ for(i = 0;i < td;++i)
{
- smp = SamplesIn[it+base];
-
- amplitude = fabsf(smp);
- if(amplitude > gain)
- gain = minf(gain+state->AttackRate, amplitude);
- else if(amplitude < gain)
- gain = maxf(gain-state->ReleaseRate, amplitude);
- output = 1.0f / clampf(gain, 0.5f, 2.0f);
-
- temps[it] = smp * output;
+ /* Clamp the absolute amplitude to the defined envelope limits,
+ * then attack or release the envelope to reach it.
+ */
+ ALfloat amplitude = clampf(fabsf(SamplesIn[0][base+i]),
+ AMP_ENVELOPE_MIN, AMP_ENVELOPE_MAX);
+ if(amplitude > env)
+ env = minf(env*state->AttackMult, amplitude);
+ else if(amplitude < env)
+ env = maxf(env*state->ReleaseMult, amplitude);
+
+ /* Apply the reciprocal of the envelope to normalize the volume
+ * (compress the dynamic range).
+ */
+ gains[i] = 1.0f / env;
}
-
- state->GainCtrl = gain;
}
else
{
- ALfloat output, smp, amplitude;
- ALfloat gain = state->GainCtrl;
-
- for(it = 0;it < td;it++)
+ /* Same as above, except the amplitude is forced to 1. This helps
+ * ensure smooth gain changes when the compressor is turned on and
+ * off.
+ */
+ for(i = 0;i < td;++i)
{
- smp = SamplesIn[it+base];
-
- amplitude = 1.0f;
- if(amplitude > gain)
- gain = minf(gain+state->AttackRate, amplitude);
- else if(amplitude < gain)
- gain = maxf(gain-state->ReleaseRate, amplitude);
- output = 1.0f / clampf(gain, 0.5f, 2.0f);
+ ALfloat amplitude = 1.0f;
+ if(amplitude > env)
+ env = minf(env*state->AttackMult, amplitude);
+ else if(amplitude < env)
+ env = maxf(env*state->ReleaseMult, amplitude);
- temps[it] = smp * output;
+ gains[i] = 1.0f / env;
}
-
- state->GainCtrl = gain;
}
+ state->EnvFollower = env;
-
- for(kt = 0;kt < NumChannels;kt++)
+ /* Now compress the signal amplitude to output. */
+ for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
{
- ALfloat gain = state->Gain[kt];
- if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
- continue;
+ for(k = 0;k < NumChannels;k++)
+ {
+ ALfloat gain = state->Gain[j][k];
+ if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
+ continue;
- for(it = 0;it < td;it++)
- SamplesOut[kt][base+it] += gain * temps[it];
+ for(i = 0;i < td;i++)
+ SamplesOut[k][base+i] += SamplesIn[j][base+i] * gains[i] * gain;
+ }
}
base += td;
}
}
-DECLARE_DEFAULT_ALLOCATORS(ALcompressorState)
-DEFINE_ALEFFECTSTATE_VTABLE(ALcompressorState);
+typedef struct CompressorStateFactory {
+ DERIVE_FROM_TYPE(EffectStateFactory);
+} CompressorStateFactory;
-
-typedef struct ALcompressorStateFactory {
- DERIVE_FROM_TYPE(ALeffectStateFactory);
-} ALcompressorStateFactory;
-
-static ALeffectState *ALcompressorStateFactory_create(ALcompressorStateFactory *UNUSED(factory))
+static ALeffectState *CompressorStateFactory_create(CompressorStateFactory *UNUSED(factory))
{
ALcompressorState *state;
- state = ALcompressorState_New(sizeof(*state));
+ NEW_OBJ0(state, ALcompressorState)();
if(!state) return NULL;
- SET_VTABLE2(ALcompressorState, ALeffectState, state);
-
- state->Enabled = AL_TRUE;
- state->AttackRate = 0.0f;
- state->ReleaseRate = 0.0f;
- state->GainCtrl = 1.0f;
return STATIC_CAST(ALeffectState, state);
}
-DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALcompressorStateFactory);
+DEFINE_EFFECTSTATEFACTORY_VTABLE(CompressorStateFactory);
-ALeffectStateFactory *ALcompressorStateFactory_getFactory(void)
+EffectStateFactory *CompressorStateFactory_getFactory(void)
{
- static ALcompressorStateFactory CompressorFactory = { { GET_VTABLE2(ALcompressorStateFactory, ALeffectStateFactory) } };
+ static CompressorStateFactory CompressorFactory = { { GET_VTABLE2(CompressorStateFactory, EffectStateFactory) } };
- return STATIC_CAST(ALeffectStateFactory, &CompressorFactory);
+ return STATIC_CAST(EffectStateFactory, &CompressorFactory);
}
@@ -172,24 +203,21 @@ void ALcompressor_setParami(ALeffect *effect, ALCcontext *context, ALenum param,
{
case AL_COMPRESSOR_ONOFF:
if(!(val >= AL_COMPRESSOR_MIN_ONOFF && val <= AL_COMPRESSOR_MAX_ONOFF))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "Compressor state out of range");
props->Compressor.OnOff = val;
break;
- default:
- SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ default:
+ alSetError(context, AL_INVALID_ENUM, "Invalid compressor integer property 0x%04x",
+ param);
}
}
void ALcompressor_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
-{
- ALcompressor_setParami(effect, context, param, vals[0]);
-}
-void ALcompressor_setParamf(ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALfloat UNUSED(val))
-{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
-void ALcompressor_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
-{
- ALcompressor_setParamf(effect, context, param, vals[0]);
-}
+{ ALcompressor_setParami(effect, context, param, vals[0]); }
+void ALcompressor_setParamf(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALfloat UNUSED(val))
+{ alSetError(context, AL_INVALID_ENUM, "Invalid compressor float property 0x%04x", param); }
+void ALcompressor_setParamfv(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, const ALfloat *UNUSED(vals))
+{ alSetError(context, AL_INVALID_ENUM, "Invalid compressor float-vector property 0x%04x", param); }
void ALcompressor_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
{
@@ -199,19 +227,17 @@ void ALcompressor_getParami(const ALeffect *effect, ALCcontext *context, ALenum
case AL_COMPRESSOR_ONOFF:
*val = props->Compressor.OnOff;
break;
+
default:
- SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ alSetError(context, AL_INVALID_ENUM, "Invalid compressor integer property 0x%04x",
+ param);
}
}
void ALcompressor_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
-{
- ALcompressor_getParami(effect, context, param, vals);
-}
-void ALcompressor_getParamf(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALfloat *UNUSED(val))
-{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
-void ALcompressor_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
-{
- ALcompressor_getParamf(effect, context, param, vals);
-}
+{ ALcompressor_getParami(effect, context, param, vals); }
+void ALcompressor_getParamf(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALfloat *UNUSED(val))
+{ alSetError(context, AL_INVALID_ENUM, "Invalid compressor float property 0x%04x", param); }
+void ALcompressor_getParamfv(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALfloat *UNUSED(vals))
+{ alSetError(context, AL_INVALID_ENUM, "Invalid compressor float-vector property 0x%04x", param); }
DEFINE_ALEFFECT_VTABLE(ALcompressor);
diff --git a/Alc/effects/dedicated.c b/Alc/effects/dedicated.c
index e09cc682..0e1fd389 100644
--- a/Alc/effects/dedicated.c
+++ b/Alc/effects/dedicated.c
@@ -23,114 +23,126 @@
#include <stdlib.h>
#include "alMain.h"
-#include "alFilter.h"
#include "alAuxEffectSlot.h"
#include "alError.h"
#include "alu.h"
+#include "filters/defs.h"
typedef struct ALdedicatedState {
DERIVE_FROM_TYPE(ALeffectState);
- ALfloat gains[MAX_OUTPUT_CHANNELS];
+ ALfloat CurrentGains[MAX_OUTPUT_CHANNELS];
+ ALfloat TargetGains[MAX_OUTPUT_CHANNELS];
} ALdedicatedState;
+static ALvoid ALdedicatedState_Destruct(ALdedicatedState *state);
+static ALboolean ALdedicatedState_deviceUpdate(ALdedicatedState *state, ALCdevice *device);
+static ALvoid ALdedicatedState_update(ALdedicatedState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props);
+static ALvoid ALdedicatedState_process(ALdedicatedState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
+DECLARE_DEFAULT_ALLOCATORS(ALdedicatedState)
+
+DEFINE_ALEFFECTSTATE_VTABLE(ALdedicatedState);
+
+
+static void ALdedicatedState_Construct(ALdedicatedState *state)
+{
+ ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
+ SET_VTABLE2(ALdedicatedState, ALeffectState, state);
+}
-static ALvoid ALdedicatedState_Destruct(ALdedicatedState *UNUSED(state))
+static ALvoid ALdedicatedState_Destruct(ALdedicatedState *state)
{
+ ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
}
-static ALboolean ALdedicatedState_deviceUpdate(ALdedicatedState *UNUSED(state), ALCdevice *UNUSED(device))
+static ALboolean ALdedicatedState_deviceUpdate(ALdedicatedState *state, ALCdevice *UNUSED(device))
{
+ ALsizei i;
+ for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
+ state->CurrentGains[i] = 0.0f;
return AL_TRUE;
}
-static ALvoid ALdedicatedState_update(ALdedicatedState *state, ALCdevice *device, const ALeffectslot *Slot)
+static ALvoid ALdedicatedState_update(ALdedicatedState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props)
{
+ const ALCdevice *device = context->Device;
ALfloat Gain;
- ALuint i;
+ ALsizei i;
for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
- state->gains[i] = 0.0f;
+ state->TargetGains[i] = 0.0f;
- Gain = Slot->Gain * Slot->EffectProps.Dedicated.Gain;
- if(Slot->EffectType == AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT)
+ Gain = slot->Params.Gain * props->Dedicated.Gain;
+ if(slot->Params.EffectType == AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT)
{
int idx;
- if((idx=GetChannelIdxByName(device, LFE)) != -1)
- state->gains[idx] = Gain;
+ if((idx=GetChannelIdxByName(&device->RealOut, LFE)) != -1)
+ {
+ STATIC_CAST(ALeffectState,state)->OutBuffer = device->RealOut.Buffer;
+ STATIC_CAST(ALeffectState,state)->OutChannels = device->RealOut.NumChannels;
+ state->TargetGains[idx] = Gain;
+ }
}
- else if(Slot->EffectType == AL_EFFECT_DEDICATED_DIALOGUE)
+ else if(slot->Params.EffectType == AL_EFFECT_DEDICATED_DIALOGUE)
{
int idx;
/* Dialog goes to the front-center speaker if it exists, otherwise it
* plays from the front-center location. */
- if((idx=GetChannelIdxByName(device, FrontCenter)) != -1)
- state->gains[idx] = Gain;
+ if((idx=GetChannelIdxByName(&device->RealOut, FrontCenter)) != -1)
+ {
+ STATIC_CAST(ALeffectState,state)->OutBuffer = device->RealOut.Buffer;
+ STATIC_CAST(ALeffectState,state)->OutChannels = device->RealOut.NumChannels;
+ state->TargetGains[idx] = Gain;
+ }
else
{
- static const ALfloat front_dir[3] = { 0.0f, 0.0f, -1.0f };
- ComputeDirectionalGains(device, front_dir, Gain, state->gains);
+ ALfloat coeffs[MAX_AMBI_COEFFS];
+ CalcAngleCoeffs(0.0f, 0.0f, 0.0f, coeffs);
+
+ STATIC_CAST(ALeffectState,state)->OutBuffer = device->Dry.Buffer;
+ STATIC_CAST(ALeffectState,state)->OutChannels = device->Dry.NumChannels;
+ ComputePanGains(&device->Dry, coeffs, Gain, state->TargetGains);
}
}
}
-static ALvoid ALdedicatedState_process(ALdedicatedState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels)
+static ALvoid ALdedicatedState_process(ALdedicatedState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
{
- const ALfloat *gains = state->gains;
- ALuint i, c;
-
- for(c = 0;c < NumChannels;c++)
- {
- if(!(fabsf(gains[c]) > GAIN_SILENCE_THRESHOLD))
- continue;
-
- for(i = 0;i < SamplesToDo;i++)
- SamplesOut[c][i] = SamplesIn[i] * gains[c];
- }
+ MixSamples(SamplesIn[0], NumChannels, SamplesOut, state->CurrentGains,
+ state->TargetGains, SamplesToDo, 0, SamplesToDo);
}
-DECLARE_DEFAULT_ALLOCATORS(ALdedicatedState)
-
-DEFINE_ALEFFECTSTATE_VTABLE(ALdedicatedState);
-
-typedef struct ALdedicatedStateFactory {
- DERIVE_FROM_TYPE(ALeffectStateFactory);
-} ALdedicatedStateFactory;
+typedef struct DedicatedStateFactory {
+ DERIVE_FROM_TYPE(EffectStateFactory);
+} DedicatedStateFactory;
-ALeffectState *ALdedicatedStateFactory_create(ALdedicatedStateFactory *UNUSED(factory))
+ALeffectState *DedicatedStateFactory_create(DedicatedStateFactory *UNUSED(factory))
{
ALdedicatedState *state;
- ALsizei s;
- state = ALdedicatedState_New(sizeof(*state));
+ NEW_OBJ0(state, ALdedicatedState)();
if(!state) return NULL;
- SET_VTABLE2(ALdedicatedState, ALeffectState, state);
-
- for(s = 0;s < MAX_OUTPUT_CHANNELS;s++)
- state->gains[s] = 0.0f;
return STATIC_CAST(ALeffectState, state);
}
-DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALdedicatedStateFactory);
+DEFINE_EFFECTSTATEFACTORY_VTABLE(DedicatedStateFactory);
-ALeffectStateFactory *ALdedicatedStateFactory_getFactory(void)
+EffectStateFactory *DedicatedStateFactory_getFactory(void)
{
- static ALdedicatedStateFactory DedicatedFactory = { { GET_VTABLE2(ALdedicatedStateFactory, ALeffectStateFactory) } };
+ static DedicatedStateFactory DedicatedFactory = { { GET_VTABLE2(DedicatedStateFactory, EffectStateFactory) } };
- return STATIC_CAST(ALeffectStateFactory, &DedicatedFactory);
+ return STATIC_CAST(EffectStateFactory, &DedicatedFactory);
}
-void ALdedicated_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val))
-{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
-void ALdedicated_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
-{
- ALdedicated_setParami(effect, context, param, vals[0]);
-}
+void ALdedicated_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint UNUSED(val))
+{ alSetError(context, AL_INVALID_ENUM, "Invalid dedicated integer property 0x%04x", param); }
+void ALdedicated_setParamiv(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, const ALint *UNUSED(vals))
+{ alSetError(context, AL_INVALID_ENUM, "Invalid dedicated integer-vector property 0x%04x", param); }
void ALdedicated_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
{
ALeffectProps *props = &effect->Props;
@@ -138,25 +150,21 @@ void ALdedicated_setParamf(ALeffect *effect, ALCcontext *context, ALenum param,
{
case AL_DEDICATED_GAIN:
if(!(val >= 0.0f && isfinite(val)))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "Dedicated gain out of range");
props->Dedicated.Gain = val;
break;
default:
- SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ alSetError(context, AL_INVALID_ENUM, "Invalid dedicated float property 0x%04x", param);
}
}
void ALdedicated_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
-{
- ALdedicated_setParamf(effect, context, param, vals[0]);
-}
+{ ALdedicated_setParamf(effect, context, param, vals[0]); }
-void ALdedicated_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val))
-{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
-void ALdedicated_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
-{
- ALdedicated_getParami(effect, context, param, vals);
-}
+void ALdedicated_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(val))
+{ alSetError(context, AL_INVALID_ENUM, "Invalid dedicated integer property 0x%04x", param); }
+void ALdedicated_getParamiv(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(vals))
+{ alSetError(context, AL_INVALID_ENUM, "Invalid dedicated integer-vector property 0x%04x", param); }
void ALdedicated_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
{
const ALeffectProps *props = &effect->Props;
@@ -167,12 +175,10 @@ void ALdedicated_getParamf(const ALeffect *effect, ALCcontext *context, ALenum p
break;
default:
- SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ alSetError(context, AL_INVALID_ENUM, "Invalid dedicated float property 0x%04x", param);
}
}
void ALdedicated_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
-{
- ALdedicated_getParamf(effect, context, param, vals);
-}
+{ ALdedicated_getParamf(effect, context, param, vals); }
DEFINE_ALEFFECT_VTABLE(ALdedicated);
diff --git a/Alc/effects/distortion.c b/Alc/effects/distortion.c
index 221cec39..de8da4fe 100644
--- a/Alc/effects/distortion.c
+++ b/Alc/effects/distortion.c
@@ -24,10 +24,10 @@
#include <stdlib.h>
#include "alMain.h"
-#include "alFilter.h"
#include "alAuxEffectSlot.h"
#include "alError.h"
#include "alu.h"
+#include "filters/defs.h"
typedef struct ALdistortionState {
@@ -37,177 +37,172 @@ typedef struct ALdistortionState {
ALfloat Gain[MAX_OUTPUT_CHANNELS];
/* Effect parameters */
- ALfilterState lowpass;
- ALfilterState bandpass;
+ BiquadFilter lowpass;
+ BiquadFilter bandpass;
ALfloat attenuation;
ALfloat edge_coeff;
+
+ ALfloat Buffer[2][BUFFERSIZE];
} ALdistortionState;
-static ALvoid ALdistortionState_Destruct(ALdistortionState *UNUSED(state))
+static ALvoid ALdistortionState_Destruct(ALdistortionState *state);
+static ALboolean ALdistortionState_deviceUpdate(ALdistortionState *state, ALCdevice *device);
+static ALvoid ALdistortionState_update(ALdistortionState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props);
+static ALvoid ALdistortionState_process(ALdistortionState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
+DECLARE_DEFAULT_ALLOCATORS(ALdistortionState)
+
+DEFINE_ALEFFECTSTATE_VTABLE(ALdistortionState);
+
+
+static void ALdistortionState_Construct(ALdistortionState *state)
+{
+ ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
+ SET_VTABLE2(ALdistortionState, ALeffectState, state);
+}
+
+static ALvoid ALdistortionState_Destruct(ALdistortionState *state)
{
+ ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
}
-static ALboolean ALdistortionState_deviceUpdate(ALdistortionState *UNUSED(state), ALCdevice *UNUSED(device))
+static ALboolean ALdistortionState_deviceUpdate(ALdistortionState *state, ALCdevice *UNUSED(device))
{
+ BiquadFilter_clear(&state->lowpass);
+ BiquadFilter_clear(&state->bandpass);
return AL_TRUE;
}
-static ALvoid ALdistortionState_update(ALdistortionState *state, ALCdevice *Device, const ALeffectslot *Slot)
+static ALvoid ALdistortionState_update(ALdistortionState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props)
{
- ALfloat frequency = (ALfloat)Device->Frequency;
+ const ALCdevice *device = context->Device;
+ ALfloat frequency = (ALfloat)device->Frequency;
+ ALfloat coeffs[MAX_AMBI_COEFFS];
ALfloat bandwidth;
ALfloat cutoff;
ALfloat edge;
- /* Store distorted signal attenuation settings */
- state->attenuation = Slot->EffectProps.Distortion.Gain;
-
- /* Store waveshaper edge settings */
- edge = sinf(Slot->EffectProps.Distortion.Edge * (F_PI_2));
+ /* Store waveshaper edge settings. */
+ edge = sinf(props->Distortion.Edge * (F_PI_2));
edge = minf(edge, 0.99f);
state->edge_coeff = 2.0f * edge / (1.0f-edge);
- /* Lowpass filter */
- cutoff = Slot->EffectProps.Distortion.LowpassCutoff;
- /* Bandwidth value is constant in octaves */
+ cutoff = props->Distortion.LowpassCutoff;
+ /* Bandwidth value is constant in octaves. */
bandwidth = (cutoff / 2.0f) / (cutoff * 0.67f);
- ALfilterState_setParams(&state->lowpass, ALfilterType_LowPass, 1.0f,
+ /* Multiply sampling frequency by the amount of oversampling done during
+ * processing.
+ */
+ BiquadFilter_setParams(&state->lowpass, BiquadType_LowPass, 1.0f,
cutoff / (frequency*4.0f), calc_rcpQ_from_bandwidth(cutoff / (frequency*4.0f), bandwidth)
);
- /* Bandpass filter */
- cutoff = Slot->EffectProps.Distortion.EQCenter;
- /* Convert bandwidth in Hz to octaves */
- bandwidth = Slot->EffectProps.Distortion.EQBandwidth / (cutoff * 0.67f);
- ALfilterState_setParams(&state->bandpass, ALfilterType_BandPass, 1.0f,
+ cutoff = props->Distortion.EQCenter;
+ /* Convert bandwidth in Hz to octaves. */
+ bandwidth = props->Distortion.EQBandwidth / (cutoff * 0.67f);
+ BiquadFilter_setParams(&state->bandpass, BiquadType_BandPass, 1.0f,
cutoff / (frequency*4.0f), calc_rcpQ_from_bandwidth(cutoff / (frequency*4.0f), bandwidth)
);
- ComputeAmbientGains(Device, Slot->Gain, state->Gain);
+ CalcAngleCoeffs(0.0f, 0.0f, 0.0f, coeffs);
+ ComputePanGains(&device->Dry, coeffs, slot->Params.Gain*props->Distortion.Gain, state->Gain);
}
-static ALvoid ALdistortionState_process(ALdistortionState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels)
+static ALvoid ALdistortionState_process(ALdistortionState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
{
+ ALfloat (*restrict buffer)[BUFFERSIZE] = state->Buffer;
const ALfloat fc = state->edge_coeff;
- ALuint base;
- ALuint it;
- ALuint ot;
- ALuint kt;
+ ALsizei base;
+ ALsizei i, k;
for(base = 0;base < SamplesToDo;)
{
- float oversample_buffer[64][4];
- ALuint td = minu(64, SamplesToDo-base);
-
- /* Perform 4x oversampling to avoid aliasing. */
- /* Oversampling greatly improves distortion */
- /* quality and allows to implement lowpass and */
- /* bandpass filters using high frequencies, at */
- /* which classic IIR filters became unstable. */
-
- /* Fill oversample buffer using zero stuffing */
- for(it = 0;it < td;it++)
+ /* Perform 4x oversampling to avoid aliasing. Oversampling greatly
+ * improves distortion quality and allows to implement lowpass and
+ * bandpass filters using high frequencies, at which classic IIR
+ * filters became unstable.
+ */
+ ALsizei todo = mini(BUFFERSIZE, (SamplesToDo-base) * 4);
+
+ /* Fill oversample buffer using zero stuffing. Multiply the sample by
+ * the amount of oversampling to maintain the signal's power.
+ */
+ for(i = 0;i < todo;i++)
+ buffer[0][i] = !(i&3) ? SamplesIn[0][(i>>2)+base] * 4.0f : 0.0f;
+
+ /* First step, do lowpass filtering of original signal. Additionally
+ * perform buffer interpolation and lowpass cutoff for oversampling
+ * (which is fortunately first step of distortion). So combine three
+ * operations into the one.
+ */
+ BiquadFilter_process(&state->lowpass, buffer[1], buffer[0], todo);
+
+ /* Second step, do distortion using waveshaper function to emulate
+ * signal processing during tube overdriving. Three steps of
+ * waveshaping are intended to modify waveform without boost/clipping/
+ * attenuation process.
+ */
+ for(i = 0;i < todo;i++)
{
- oversample_buffer[it][0] = SamplesIn[it+base];
- oversample_buffer[it][1] = 0.0f;
- oversample_buffer[it][2] = 0.0f;
- oversample_buffer[it][3] = 0.0f;
- }
+ ALfloat smp = buffer[1][i];
- /* First step, do lowpass filtering of original signal, */
- /* additionally perform buffer interpolation and lowpass */
- /* cutoff for oversampling (which is fortunately first */
- /* step of distortion). So combine three operations into */
- /* the one. */
- for(it = 0;it < td;it++)
- {
- for(ot = 0;ot < 4;ot++)
- {
- ALfloat smp;
- smp = ALfilterState_processSingle(&state->lowpass, oversample_buffer[it][ot]);
-
- /* Restore signal power by multiplying sample by amount of oversampling */
- oversample_buffer[it][ot] = smp * 4.0f;
- }
- }
+ smp = (1.0f + fc) * smp/(1.0f + fc*fabsf(smp));
+ smp = (1.0f + fc) * smp/(1.0f + fc*fabsf(smp)) * -1.0f;
+ smp = (1.0f + fc) * smp/(1.0f + fc*fabsf(smp));
- for(it = 0;it < td;it++)
- {
- /* Second step, do distortion using waveshaper function */
- /* to emulate signal processing during tube overdriving. */
- /* Three steps of waveshaping are intended to modify */
- /* waveform without boost/clipping/attenuation process. */
- for(ot = 0;ot < 4;ot++)
- {
- ALfloat smp = oversample_buffer[it][ot];
-
- smp = (1.0f + fc) * smp/(1.0f + fc*fabsf(smp));
- smp = (1.0f + fc) * smp/(1.0f + fc*fabsf(smp)) * -1.0f;
- smp = (1.0f + fc) * smp/(1.0f + fc*fabsf(smp));
-
- /* Third step, do bandpass filtering of distorted signal */
- smp = ALfilterState_processSingle(&state->bandpass, smp);
- oversample_buffer[it][ot] = smp;
- }
+ buffer[0][i] = smp;
}
- for(kt = 0;kt < NumChannels;kt++)
+ /* Third step, do bandpass filtering of distorted signal. */
+ BiquadFilter_process(&state->bandpass, buffer[1], buffer[0], todo);
+
+ todo >>= 2;
+ for(k = 0;k < NumChannels;k++)
{
/* Fourth step, final, do attenuation and perform decimation,
- * store only one sample out of 4.
+ * storing only one sample out of four.
*/
- ALfloat gain = state->Gain[kt] * state->attenuation;
+ ALfloat gain = state->Gain[k];
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
continue;
- for(it = 0;it < td;it++)
- SamplesOut[kt][base+it] += gain * oversample_buffer[it][0];
+ for(i = 0;i < todo;i++)
+ SamplesOut[k][base+i] += gain * buffer[1][i*4];
}
- base += td;
+ base += todo;
}
}
-DECLARE_DEFAULT_ALLOCATORS(ALdistortionState)
-
-DEFINE_ALEFFECTSTATE_VTABLE(ALdistortionState);
-
-typedef struct ALdistortionStateFactory {
- DERIVE_FROM_TYPE(ALeffectStateFactory);
-} ALdistortionStateFactory;
+typedef struct DistortionStateFactory {
+ DERIVE_FROM_TYPE(EffectStateFactory);
+} DistortionStateFactory;
-static ALeffectState *ALdistortionStateFactory_create(ALdistortionStateFactory *UNUSED(factory))
+static ALeffectState *DistortionStateFactory_create(DistortionStateFactory *UNUSED(factory))
{
ALdistortionState *state;
- state = ALdistortionState_New(sizeof(*state));
+ NEW_OBJ0(state, ALdistortionState)();
if(!state) return NULL;
- SET_VTABLE2(ALdistortionState, ALeffectState, state);
-
- ALfilterState_clear(&state->lowpass);
- ALfilterState_clear(&state->bandpass);
return STATIC_CAST(ALeffectState, state);
}
-DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALdistortionStateFactory);
+DEFINE_EFFECTSTATEFACTORY_VTABLE(DistortionStateFactory);
-ALeffectStateFactory *ALdistortionStateFactory_getFactory(void)
+EffectStateFactory *DistortionStateFactory_getFactory(void)
{
- static ALdistortionStateFactory DistortionFactory = { { GET_VTABLE2(ALdistortionStateFactory, ALeffectStateFactory) } };
+ static DistortionStateFactory DistortionFactory = { { GET_VTABLE2(DistortionStateFactory, EffectStateFactory) } };
- return STATIC_CAST(ALeffectStateFactory, &DistortionFactory);
+ return STATIC_CAST(EffectStateFactory, &DistortionFactory);
}
-void ALdistortion_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val))
-{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
-void ALdistortion_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
-{
- ALdistortion_setParami(effect, context, param, vals[0]);
-}
+void ALdistortion_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint UNUSED(val))
+{ alSetError(context, AL_INVALID_ENUM, "Invalid distortion integer property 0x%04x", param); }
+void ALdistortion_setParamiv(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, const ALint *UNUSED(vals))
+{ alSetError(context, AL_INVALID_ENUM, "Invalid distortion integer-vector property 0x%04x", param); }
void ALdistortion_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
{
ALeffectProps *props = &effect->Props;
@@ -215,49 +210,46 @@ void ALdistortion_setParamf(ALeffect *effect, ALCcontext *context, ALenum param,
{
case AL_DISTORTION_EDGE:
if(!(val >= AL_DISTORTION_MIN_EDGE && val <= AL_DISTORTION_MAX_EDGE))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "Distortion edge out of range");
props->Distortion.Edge = val;
break;
case AL_DISTORTION_GAIN:
if(!(val >= AL_DISTORTION_MIN_GAIN && val <= AL_DISTORTION_MAX_GAIN))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "Distortion gain out of range");
props->Distortion.Gain = val;
break;
case AL_DISTORTION_LOWPASS_CUTOFF:
if(!(val >= AL_DISTORTION_MIN_LOWPASS_CUTOFF && val <= AL_DISTORTION_MAX_LOWPASS_CUTOFF))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "Distortion low-pass cutoff out of range");
props->Distortion.LowpassCutoff = val;
break;
case AL_DISTORTION_EQCENTER:
if(!(val >= AL_DISTORTION_MIN_EQCENTER && val <= AL_DISTORTION_MAX_EQCENTER))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "Distortion EQ center out of range");
props->Distortion.EQCenter = val;
break;
case AL_DISTORTION_EQBANDWIDTH:
if(!(val >= AL_DISTORTION_MIN_EQBANDWIDTH && val <= AL_DISTORTION_MAX_EQBANDWIDTH))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "Distortion EQ bandwidth out of range");
props->Distortion.EQBandwidth = val;
break;
default:
- SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ alSetError(context, AL_INVALID_ENUM, "Invalid distortion float property 0x%04x",
+ param);
}
}
void ALdistortion_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
-{
- ALdistortion_setParamf(effect, context, param, vals[0]);
-}
+{ ALdistortion_setParamf(effect, context, param, vals[0]); }
-void ALdistortion_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val))
-{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
-void ALdistortion_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
-{
- ALdistortion_getParami(effect, context, param, vals);
-}
+void ALdistortion_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(val))
+{ alSetError(context, AL_INVALID_ENUM, "Invalid distortion integer property 0x%04x", param); }
+void ALdistortion_getParamiv(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(vals))
+{ alSetError(context, AL_INVALID_ENUM, "Invalid distortion integer-vector property 0x%04x", param); }
void ALdistortion_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
{
const ALeffectProps *props = &effect->Props;
@@ -284,12 +276,11 @@ void ALdistortion_getParamf(const ALeffect *effect, ALCcontext *context, ALenum
break;
default:
- SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ alSetError(context, AL_INVALID_ENUM, "Invalid distortion float property 0x%04x",
+ param);
}
}
void ALdistortion_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
-{
- ALdistortion_getParamf(effect, context, param, vals);
-}
+{ ALdistortion_getParamf(effect, context, param, vals); }
DEFINE_ALEFFECT_VTABLE(ALdistortion);
diff --git a/Alc/effects/echo.c b/Alc/effects/echo.c
index f5a53c36..4570fcb1 100644
--- a/Alc/effects/echo.c
+++ b/Alc/effects/echo.c
@@ -28,186 +28,207 @@
#include "alAuxEffectSlot.h"
#include "alError.h"
#include "alu.h"
+#include "filters/defs.h"
typedef struct ALechoState {
DERIVE_FROM_TYPE(ALeffectState);
ALfloat *SampleBuffer;
- ALuint BufferLength;
+ ALsizei BufferLength;
// The echo is two tap. The delay is the number of samples from before the
// current offset
struct {
- ALuint delay;
+ ALsizei delay;
} Tap[2];
- ALuint Offset;
+ ALsizei Offset;
+
/* The panning gains for the two taps */
- ALfloat Gain[2][MAX_OUTPUT_CHANNELS];
+ struct {
+ ALfloat Current[MAX_OUTPUT_CHANNELS];
+ ALfloat Target[MAX_OUTPUT_CHANNELS];
+ } Gains[2];
ALfloat FeedGain;
- ALfilterState Filter;
+ BiquadFilter Filter;
} ALechoState;
+static ALvoid ALechoState_Destruct(ALechoState *state);
+static ALboolean ALechoState_deviceUpdate(ALechoState *state, ALCdevice *Device);
+static ALvoid ALechoState_update(ALechoState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props);
+static ALvoid ALechoState_process(ALechoState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
+DECLARE_DEFAULT_ALLOCATORS(ALechoState)
+
+DEFINE_ALEFFECTSTATE_VTABLE(ALechoState);
+
+
+static void ALechoState_Construct(ALechoState *state)
+{
+ ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
+ SET_VTABLE2(ALechoState, ALeffectState, state);
+
+ state->BufferLength = 0;
+ state->SampleBuffer = NULL;
+
+ state->Tap[0].delay = 0;
+ state->Tap[1].delay = 0;
+ state->Offset = 0;
+
+ BiquadFilter_clear(&state->Filter);
+}
+
static ALvoid ALechoState_Destruct(ALechoState *state)
{
- free(state->SampleBuffer);
+ al_free(state->SampleBuffer);
state->SampleBuffer = NULL;
+ ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
}
static ALboolean ALechoState_deviceUpdate(ALechoState *state, ALCdevice *Device)
{
- ALuint maxlen, i;
+ ALsizei maxlen;
// Use the next power of 2 for the buffer length, so the tap offsets can be
// wrapped using a mask instead of a modulo
- maxlen = fastf2u(AL_ECHO_MAX_DELAY * Device->Frequency) + 1;
- maxlen += fastf2u(AL_ECHO_MAX_LRDELAY * Device->Frequency) + 1;
- maxlen = NextPowerOf2(maxlen);
+ maxlen = float2int(AL_ECHO_MAX_DELAY*Device->Frequency + 0.5f) +
+ float2int(AL_ECHO_MAX_LRDELAY*Device->Frequency + 0.5f);
+ maxlen = NextPowerOf2(maxlen);
+ if(maxlen <= 0) return AL_FALSE;
if(maxlen != state->BufferLength)
{
- void *temp;
-
- temp = realloc(state->SampleBuffer, maxlen * sizeof(ALfloat));
+ void *temp = al_calloc(16, maxlen * sizeof(ALfloat));
if(!temp) return AL_FALSE;
+
+ al_free(state->SampleBuffer);
state->SampleBuffer = temp;
state->BufferLength = maxlen;
}
- for(i = 0;i < state->BufferLength;i++)
- state->SampleBuffer[i] = 0.0f;
+
+ memset(state->SampleBuffer, 0, state->BufferLength*sizeof(ALfloat));
+ memset(state->Gains, 0, sizeof(state->Gains));
return AL_TRUE;
}
-static ALvoid ALechoState_update(ALechoState *state, ALCdevice *Device, const ALeffectslot *Slot)
+static ALvoid ALechoState_update(ALechoState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props)
{
- ALfloat pandir[3] = { 0.0f, 0.0f, 0.0f };
- ALuint frequency = Device->Frequency;
- ALfloat gain, lrpan;
+ const ALCdevice *device = context->Device;
+ ALuint frequency = device->Frequency;
+ ALfloat coeffs[MAX_AMBI_COEFFS];
+ ALfloat gainhf, lrpan, spread;
- state->Tap[0].delay = fastf2u(Slot->EffectProps.Echo.Delay * frequency) + 1;
- state->Tap[1].delay = fastf2u(Slot->EffectProps.Echo.LRDelay * frequency);
+ state->Tap[0].delay = maxi(float2int(props->Echo.Delay*frequency + 0.5f), 1);
+ state->Tap[1].delay = float2int(props->Echo.LRDelay*frequency + 0.5f);
state->Tap[1].delay += state->Tap[0].delay;
- lrpan = Slot->EffectProps.Echo.Spread;
+ spread = props->Echo.Spread;
+ if(spread < 0.0f) lrpan = -1.0f;
+ else lrpan = 1.0f;
+ /* Convert echo spread (where 0 = omni, +/-1 = directional) to coverage
+ * spread (where 0 = point, tau = omni).
+ */
+ spread = asinf(1.0f - fabsf(spread))*4.0f;
- state->FeedGain = Slot->EffectProps.Echo.Feedback;
+ state->FeedGain = props->Echo.Feedback;
- gain = minf(1.0f - Slot->EffectProps.Echo.Damping, 0.01f);
- ALfilterState_setParams(&state->Filter, ALfilterType_HighShelf,
- gain, LOWPASSFREQREF/frequency,
- calc_rcpQ_from_slope(gain, 0.75f));
-
- gain = Slot->Gain;
+ gainhf = maxf(1.0f - props->Echo.Damping, 0.0625f); /* Limit -24dB */
+ BiquadFilter_setParams(&state->Filter, BiquadType_HighShelf,
+ gainhf, LOWPASSFREQREF/frequency, calc_rcpQ_from_slope(gainhf, 1.0f)
+ );
/* First tap panning */
- pandir[0] = -lrpan;
- ComputeDirectionalGains(Device, pandir, gain, state->Gain[0]);
+ CalcAngleCoeffs(-F_PI_2*lrpan, 0.0f, spread, coeffs);
+ ComputePanGains(&device->Dry, coeffs, slot->Params.Gain, state->Gains[0].Target);
/* Second tap panning */
- pandir[0] = +lrpan;
- ComputeDirectionalGains(Device, pandir, gain, state->Gain[1]);
+ CalcAngleCoeffs( F_PI_2*lrpan, 0.0f, spread, coeffs);
+ ComputePanGains(&device->Dry, coeffs, slot->Params.Gain, state->Gains[1].Target);
}
-static ALvoid ALechoState_process(ALechoState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels)
+static ALvoid ALechoState_process(ALechoState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
{
- const ALuint mask = state->BufferLength-1;
- const ALuint tap1 = state->Tap[0].delay;
- const ALuint tap2 = state->Tap[1].delay;
- ALuint offset = state->Offset;
- ALfloat smp;
- ALuint base;
- ALuint i, k;
-
+ const ALsizei mask = state->BufferLength-1;
+ const ALsizei tap1 = state->Tap[0].delay;
+ const ALsizei tap2 = state->Tap[1].delay;
+ ALfloat *restrict delaybuf = state->SampleBuffer;
+ ALsizei offset = state->Offset;
+ ALfloat z1, z2, in, out;
+ ALsizei base;
+ ALsizei c, i;
+
+ z1 = state->Filter.z1;
+ z2 = state->Filter.z2;
for(base = 0;base < SamplesToDo;)
{
- ALfloat temps[128][2];
- ALuint td = minu(128, SamplesToDo-base);
+ alignas(16) ALfloat temps[2][128];
+ ALsizei td = mini(128, SamplesToDo-base);
for(i = 0;i < td;i++)
{
+ /* Feed the delay buffer's input first. */
+ delaybuf[offset&mask] = SamplesIn[0][i+base];
+
/* First tap */
- temps[i][0] = state->SampleBuffer[(offset-tap1) & mask];
+ temps[0][i] = delaybuf[(offset-tap1) & mask];
/* Second tap */
- temps[i][1] = state->SampleBuffer[(offset-tap2) & mask];
+ temps[1][i] = delaybuf[(offset-tap2) & mask];
- // Apply damping and feedback gain to the second tap, and mix in the
- // new sample
- smp = ALfilterState_processSingle(&state->Filter, temps[i][1]+SamplesIn[i+base]);
- state->SampleBuffer[offset&mask] = smp * state->FeedGain;
+ /* Apply damping to the second tap, then add it to the buffer with
+ * feedback attenuation.
+ */
+ in = temps[1][i];
+ out = in*state->Filter.b0 + z1;
+ z1 = in*state->Filter.b1 - out*state->Filter.a1 + z2;
+ z2 = in*state->Filter.b2 - out*state->Filter.a2;
+
+ delaybuf[offset&mask] += out * state->FeedGain;
offset++;
}
- for(k = 0;k < NumChannels;k++)
- {
- ALfloat gain = state->Gain[0][k];
- if(fabsf(gain) > GAIN_SILENCE_THRESHOLD)
- {
- for(i = 0;i < td;i++)
- SamplesOut[k][i+base] += temps[i][0] * gain;
- }
-
- gain = state->Gain[1][k];
- if(fabsf(gain) > GAIN_SILENCE_THRESHOLD)
- {
- for(i = 0;i < td;i++)
- SamplesOut[k][i+base] += temps[i][1] * gain;
- }
- }
+ for(c = 0;c < 2;c++)
+ MixSamples(temps[c], NumChannels, SamplesOut, state->Gains[c].Current,
+ state->Gains[c].Target, SamplesToDo-base, base, td);
base += td;
}
+ state->Filter.z1 = z1;
+ state->Filter.z2 = z2;
state->Offset = offset;
}
-DECLARE_DEFAULT_ALLOCATORS(ALechoState)
-
-DEFINE_ALEFFECTSTATE_VTABLE(ALechoState);
-
-typedef struct ALechoStateFactory {
- DERIVE_FROM_TYPE(ALeffectStateFactory);
-} ALechoStateFactory;
+typedef struct EchoStateFactory {
+ DERIVE_FROM_TYPE(EffectStateFactory);
+} EchoStateFactory;
-ALeffectState *ALechoStateFactory_create(ALechoStateFactory *UNUSED(factory))
+ALeffectState *EchoStateFactory_create(EchoStateFactory *UNUSED(factory))
{
ALechoState *state;
- state = ALechoState_New(sizeof(*state));
+ NEW_OBJ0(state, ALechoState)();
if(!state) return NULL;
- SET_VTABLE2(ALechoState, ALeffectState, state);
-
- state->BufferLength = 0;
- state->SampleBuffer = NULL;
-
- state->Tap[0].delay = 0;
- state->Tap[1].delay = 0;
- state->Offset = 0;
-
- ALfilterState_clear(&state->Filter);
return STATIC_CAST(ALeffectState, state);
}
-DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALechoStateFactory);
+DEFINE_EFFECTSTATEFACTORY_VTABLE(EchoStateFactory);
-ALeffectStateFactory *ALechoStateFactory_getFactory(void)
+EffectStateFactory *EchoStateFactory_getFactory(void)
{
- static ALechoStateFactory EchoFactory = { { GET_VTABLE2(ALechoStateFactory, ALeffectStateFactory) } };
+ static EchoStateFactory EchoFactory = { { GET_VTABLE2(EchoStateFactory, EffectStateFactory) } };
- return STATIC_CAST(ALeffectStateFactory, &EchoFactory);
+ return STATIC_CAST(EffectStateFactory, &EchoFactory);
}
-void ALecho_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val))
-{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
-void ALecho_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
-{
- ALecho_setParami(effect, context, param, vals[0]);
-}
+void ALecho_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint UNUSED(val))
+{ alSetError(context, AL_INVALID_ENUM, "Invalid echo integer property 0x%04x", param); }
+void ALecho_setParamiv(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, const ALint *UNUSED(vals))
+{ alSetError(context, AL_INVALID_ENUM, "Invalid echo integer-vector property 0x%04x", param); }
void ALecho_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
{
ALeffectProps *props = &effect->Props;
@@ -215,49 +236,45 @@ void ALecho_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALflo
{
case AL_ECHO_DELAY:
if(!(val >= AL_ECHO_MIN_DELAY && val <= AL_ECHO_MAX_DELAY))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "Echo delay out of range");
props->Echo.Delay = val;
break;
case AL_ECHO_LRDELAY:
if(!(val >= AL_ECHO_MIN_LRDELAY && val <= AL_ECHO_MAX_LRDELAY))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "Echo LR delay out of range");
props->Echo.LRDelay = val;
break;
case AL_ECHO_DAMPING:
if(!(val >= AL_ECHO_MIN_DAMPING && val <= AL_ECHO_MAX_DAMPING))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "Echo damping out of range");
props->Echo.Damping = val;
break;
case AL_ECHO_FEEDBACK:
if(!(val >= AL_ECHO_MIN_FEEDBACK && val <= AL_ECHO_MAX_FEEDBACK))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "Echo feedback out of range");
props->Echo.Feedback = val;
break;
case AL_ECHO_SPREAD:
if(!(val >= AL_ECHO_MIN_SPREAD && val <= AL_ECHO_MAX_SPREAD))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "Echo spread out of range");
props->Echo.Spread = val;
break;
default:
- SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ alSetError(context, AL_INVALID_ENUM, "Invalid echo float property 0x%04x", param);
}
}
void ALecho_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
-{
- ALecho_setParamf(effect, context, param, vals[0]);
-}
+{ ALecho_setParamf(effect, context, param, vals[0]); }
-void ALecho_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val))
-{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
-void ALecho_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
-{
- ALecho_getParami(effect, context, param, vals);
-}
+void ALecho_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(val))
+{ alSetError(context, AL_INVALID_ENUM, "Invalid echo integer property 0x%04x", param); }
+void ALecho_getParamiv(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(vals))
+{ alSetError(context, AL_INVALID_ENUM, "Invalid echo integer-vector property 0x%04x", param); }
void ALecho_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
{
const ALeffectProps *props = &effect->Props;
@@ -284,12 +301,10 @@ void ALecho_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param,
break;
default:
- SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ alSetError(context, AL_INVALID_ENUM, "Invalid echo float property 0x%04x", param);
}
}
void ALecho_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
-{
- ALecho_getParamf(effect, context, param, vals);
-}
+{ ALecho_getParamf(effect, context, param, vals); }
DEFINE_ALEFFECT_VTABLE(ALecho);
diff --git a/Alc/effects/equalizer.c b/Alc/effects/equalizer.c
index 244667ab..17106127 100644
--- a/Alc/effects/equalizer.c
+++ b/Alc/effects/equalizer.c
@@ -24,10 +24,10 @@
#include <stdlib.h>
#include "alMain.h"
-#include "alFilter.h"
#include "alAuxEffectSlot.h"
#include "alError.h"
#include "alu.h"
+#include "filters/defs.h"
/* The document "Effects Extension Guide.pdf" says that low and high *
@@ -71,139 +71,159 @@
* filter coefficients" by Robert Bristow-Johnson *
* http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt */
+
typedef struct ALequalizerState {
DERIVE_FROM_TYPE(ALeffectState);
- /* Effect gains for each channel */
- ALfloat Gain[MAX_OUTPUT_CHANNELS];
+ struct {
+ /* Effect parameters */
+ BiquadFilter filter[4];
+
+ /* Effect gains for each channel */
+ ALfloat CurrentGains[MAX_OUTPUT_CHANNELS];
+ ALfloat TargetGains[MAX_OUTPUT_CHANNELS];
+ } Chans[MAX_EFFECT_CHANNELS];
- /* Effect parameters */
- ALfilterState filter[4];
+ ALfloat SampleBuffer[MAX_EFFECT_CHANNELS][BUFFERSIZE];
} ALequalizerState;
-static ALvoid ALequalizerState_Destruct(ALequalizerState *UNUSED(state))
+static ALvoid ALequalizerState_Destruct(ALequalizerState *state);
+static ALboolean ALequalizerState_deviceUpdate(ALequalizerState *state, ALCdevice *device);
+static ALvoid ALequalizerState_update(ALequalizerState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props);
+static ALvoid ALequalizerState_process(ALequalizerState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
+DECLARE_DEFAULT_ALLOCATORS(ALequalizerState)
+
+DEFINE_ALEFFECTSTATE_VTABLE(ALequalizerState);
+
+
+static void ALequalizerState_Construct(ALequalizerState *state)
+{
+ ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
+ SET_VTABLE2(ALequalizerState, ALeffectState, state);
+}
+
+static ALvoid ALequalizerState_Destruct(ALequalizerState *state)
{
+ ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
}
-static ALboolean ALequalizerState_deviceUpdate(ALequalizerState *UNUSED(state), ALCdevice *UNUSED(device))
+static ALboolean ALequalizerState_deviceUpdate(ALequalizerState *state, ALCdevice *UNUSED(device))
{
+ ALsizei i, j;
+
+ for(i = 0; i < MAX_EFFECT_CHANNELS;i++)
+ {
+ for(j = 0;j < 4;j++)
+ BiquadFilter_clear(&state->Chans[i].filter[j]);
+ for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
+ state->Chans[i].CurrentGains[j] = 0.0f;
+ }
return AL_TRUE;
}
-static ALvoid ALequalizerState_update(ALequalizerState *state, ALCdevice *device, const ALeffectslot *slot)
+static ALvoid ALequalizerState_update(ALequalizerState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props)
{
+ const ALCdevice *device = context->Device;
ALfloat frequency = (ALfloat)device->Frequency;
- ALfloat gain, freq_mult;
-
- ComputeAmbientGains(device, slot->Gain, state->Gain);
+ ALfloat gain, f0norm;
+ ALuint i;
/* Calculate coefficients for the each type of filter. Note that the shelf
* filters' gain is for the reference frequency, which is the centerpoint
* of the transition band.
*/
- gain = sqrtf(slot->EffectProps.Equalizer.LowGain);
- freq_mult = slot->EffectProps.Equalizer.LowCutoff/frequency;
- ALfilterState_setParams(&state->filter[0], ALfilterType_LowShelf,
- gain, freq_mult, calc_rcpQ_from_slope(gain, 0.75f)
+ gain = maxf(sqrtf(props->Equalizer.LowGain), 0.0625f); /* Limit -24dB */
+ f0norm = props->Equalizer.LowCutoff/frequency;
+ BiquadFilter_setParams(&state->Chans[0].filter[0], BiquadType_LowShelf,
+ gain, f0norm, calc_rcpQ_from_slope(gain, 0.75f)
);
- gain = slot->EffectProps.Equalizer.Mid1Gain;
- freq_mult = slot->EffectProps.Equalizer.Mid1Center/frequency;
- ALfilterState_setParams(&state->filter[1], ALfilterType_Peaking,
- gain, freq_mult, calc_rcpQ_from_bandwidth(freq_mult, slot->EffectProps.Equalizer.Mid1Width)
+ gain = maxf(props->Equalizer.Mid1Gain, 0.0625f);
+ f0norm = props->Equalizer.Mid1Center/frequency;
+ BiquadFilter_setParams(&state->Chans[0].filter[1], BiquadType_Peaking,
+ gain, f0norm, calc_rcpQ_from_bandwidth(
+ f0norm, props->Equalizer.Mid1Width
+ )
);
- gain = slot->EffectProps.Equalizer.Mid2Gain;
- freq_mult = slot->EffectProps.Equalizer.Mid2Center/frequency;
- ALfilterState_setParams(&state->filter[2], ALfilterType_Peaking,
- gain, freq_mult, calc_rcpQ_from_bandwidth(freq_mult, slot->EffectProps.Equalizer.Mid2Width)
+ gain = maxf(props->Equalizer.Mid2Gain, 0.0625f);
+ f0norm = props->Equalizer.Mid2Center/frequency;
+ BiquadFilter_setParams(&state->Chans[0].filter[2], BiquadType_Peaking,
+ gain, f0norm, calc_rcpQ_from_bandwidth(
+ f0norm, props->Equalizer.Mid2Width
+ )
);
- gain = sqrtf(slot->EffectProps.Equalizer.HighGain);
- freq_mult = slot->EffectProps.Equalizer.HighCutoff/frequency;
- ALfilterState_setParams(&state->filter[3], ALfilterType_HighShelf,
- gain, freq_mult, calc_rcpQ_from_slope(gain, 0.75f)
+ gain = maxf(sqrtf(props->Equalizer.HighGain), 0.0625f);
+ f0norm = props->Equalizer.HighCutoff/frequency;
+ BiquadFilter_setParams(&state->Chans[0].filter[3], BiquadType_HighShelf,
+ gain, f0norm, calc_rcpQ_from_slope(gain, 0.75f)
);
-}
-
-static ALvoid ALequalizerState_process(ALequalizerState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels)
-{
- ALuint base;
- ALuint it;
- ALuint kt;
- ALuint ft;
- for(base = 0;base < SamplesToDo;)
+ /* Copy the filter coefficients for the other input channels. */
+ for(i = 1;i < MAX_EFFECT_CHANNELS;i++)
{
- ALfloat temps[256];
- ALuint td = minu(256, SamplesToDo-base);
-
- for(it = 0;it < td;it++)
- {
- ALfloat smp = SamplesIn[base+it];
-
- for(ft = 0;ft < 4;ft++)
- smp = ALfilterState_processSingle(&state->filter[ft], smp);
-
- temps[it] = smp;
- }
+ BiquadFilter_copyParams(&state->Chans[i].filter[0], &state->Chans[0].filter[0]);
+ BiquadFilter_copyParams(&state->Chans[i].filter[1], &state->Chans[0].filter[1]);
+ BiquadFilter_copyParams(&state->Chans[i].filter[2], &state->Chans[0].filter[2]);
+ BiquadFilter_copyParams(&state->Chans[i].filter[3], &state->Chans[0].filter[3]);
+ }
- for(kt = 0;kt < NumChannels;kt++)
- {
- ALfloat gain = state->Gain[kt];
- if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
- continue;
+ STATIC_CAST(ALeffectState,state)->OutBuffer = device->FOAOut.Buffer;
+ STATIC_CAST(ALeffectState,state)->OutChannels = device->FOAOut.NumChannels;
+ for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
+ ComputePanGains(&device->FOAOut, IdentityMatrixf.m[i], slot->Params.Gain,
+ state->Chans[i].TargetGains);
+}
- for(it = 0;it < td;it++)
- SamplesOut[kt][base+it] += gain * temps[it];
- }
+static ALvoid ALequalizerState_process(ALequalizerState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
+{
+ ALfloat (*restrict temps)[BUFFERSIZE] = state->SampleBuffer;
+ ALsizei c;
- base += td;
+ for(c = 0;c < MAX_EFFECT_CHANNELS;c++)
+ {
+ BiquadFilter_process(&state->Chans[c].filter[0], temps[0], SamplesIn[c], SamplesToDo);
+ BiquadFilter_process(&state->Chans[c].filter[1], temps[1], temps[0], SamplesToDo);
+ BiquadFilter_process(&state->Chans[c].filter[2], temps[2], temps[1], SamplesToDo);
+ BiquadFilter_process(&state->Chans[c].filter[3], temps[3], temps[2], SamplesToDo);
+
+ MixSamples(temps[3], NumChannels, SamplesOut,
+ state->Chans[c].CurrentGains, state->Chans[c].TargetGains,
+ SamplesToDo, 0, SamplesToDo
+ );
}
}
-DECLARE_DEFAULT_ALLOCATORS(ALequalizerState)
-
-DEFINE_ALEFFECTSTATE_VTABLE(ALequalizerState);
+typedef struct EqualizerStateFactory {
+ DERIVE_FROM_TYPE(EffectStateFactory);
+} EqualizerStateFactory;
-typedef struct ALequalizerStateFactory {
- DERIVE_FROM_TYPE(ALeffectStateFactory);
-} ALequalizerStateFactory;
-
-ALeffectState *ALequalizerStateFactory_create(ALequalizerStateFactory *UNUSED(factory))
+ALeffectState *EqualizerStateFactory_create(EqualizerStateFactory *UNUSED(factory))
{
ALequalizerState *state;
- int it;
- state = ALequalizerState_New(sizeof(*state));
+ NEW_OBJ0(state, ALequalizerState)();
if(!state) return NULL;
- SET_VTABLE2(ALequalizerState, ALeffectState, state);
-
- /* Initialize sample history only on filter creation to avoid */
- /* sound clicks if filter settings were changed in runtime. */
- for(it = 0; it < 4; it++)
- ALfilterState_clear(&state->filter[it]);
return STATIC_CAST(ALeffectState, state);
}
-DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALequalizerStateFactory);
+DEFINE_EFFECTSTATEFACTORY_VTABLE(EqualizerStateFactory);
-ALeffectStateFactory *ALequalizerStateFactory_getFactory(void)
+EffectStateFactory *EqualizerStateFactory_getFactory(void)
{
- static ALequalizerStateFactory EqualizerFactory = { { GET_VTABLE2(ALequalizerStateFactory, ALeffectStateFactory) } };
+ static EqualizerStateFactory EqualizerFactory = { { GET_VTABLE2(EqualizerStateFactory, EffectStateFactory) } };
- return STATIC_CAST(ALeffectStateFactory, &EqualizerFactory);
+ return STATIC_CAST(EffectStateFactory, &EqualizerFactory);
}
-void ALequalizer_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val))
-{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
-void ALequalizer_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
-{
- ALequalizer_setParami(effect, context, param, vals[0]);
-}
+void ALequalizer_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint UNUSED(val))
+{ alSetError(context, AL_INVALID_ENUM, "Invalid equalizer integer property 0x%04x", param); }
+void ALequalizer_setParamiv(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, const ALint *UNUSED(vals))
+{ alSetError(context, AL_INVALID_ENUM, "Invalid equalizer integer-vector property 0x%04x", param); }
void ALequalizer_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
{
ALeffectProps *props = &effect->Props;
@@ -211,79 +231,75 @@ void ALequalizer_setParamf(ALeffect *effect, ALCcontext *context, ALenum param,
{
case AL_EQUALIZER_LOW_GAIN:
if(!(val >= AL_EQUALIZER_MIN_LOW_GAIN && val <= AL_EQUALIZER_MAX_LOW_GAIN))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer low-band gain out of range");
props->Equalizer.LowGain = val;
break;
case AL_EQUALIZER_LOW_CUTOFF:
if(!(val >= AL_EQUALIZER_MIN_LOW_CUTOFF && val <= AL_EQUALIZER_MAX_LOW_CUTOFF))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer low-band cutoff out of range");
props->Equalizer.LowCutoff = val;
break;
case AL_EQUALIZER_MID1_GAIN:
if(!(val >= AL_EQUALIZER_MIN_MID1_GAIN && val <= AL_EQUALIZER_MAX_MID1_GAIN))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer mid1-band gain out of range");
props->Equalizer.Mid1Gain = val;
break;
case AL_EQUALIZER_MID1_CENTER:
if(!(val >= AL_EQUALIZER_MIN_MID1_CENTER && val <= AL_EQUALIZER_MAX_MID1_CENTER))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer mid1-band center out of range");
props->Equalizer.Mid1Center = val;
break;
case AL_EQUALIZER_MID1_WIDTH:
if(!(val >= AL_EQUALIZER_MIN_MID1_WIDTH && val <= AL_EQUALIZER_MAX_MID1_WIDTH))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer mid1-band width out of range");
props->Equalizer.Mid1Width = val;
break;
case AL_EQUALIZER_MID2_GAIN:
if(!(val >= AL_EQUALIZER_MIN_MID2_GAIN && val <= AL_EQUALIZER_MAX_MID2_GAIN))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer mid2-band gain out of range");
props->Equalizer.Mid2Gain = val;
break;
case AL_EQUALIZER_MID2_CENTER:
if(!(val >= AL_EQUALIZER_MIN_MID2_CENTER && val <= AL_EQUALIZER_MAX_MID2_CENTER))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer mid2-band center out of range");
props->Equalizer.Mid2Center = val;
break;
case AL_EQUALIZER_MID2_WIDTH:
if(!(val >= AL_EQUALIZER_MIN_MID2_WIDTH && val <= AL_EQUALIZER_MAX_MID2_WIDTH))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer mid2-band width out of range");
props->Equalizer.Mid2Width = val;
break;
case AL_EQUALIZER_HIGH_GAIN:
if(!(val >= AL_EQUALIZER_MIN_HIGH_GAIN && val <= AL_EQUALIZER_MAX_HIGH_GAIN))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer high-band gain out of range");
props->Equalizer.HighGain = val;
break;
case AL_EQUALIZER_HIGH_CUTOFF:
if(!(val >= AL_EQUALIZER_MIN_HIGH_CUTOFF && val <= AL_EQUALIZER_MAX_HIGH_CUTOFF))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer high-band cutoff out of range");
props->Equalizer.HighCutoff = val;
break;
default:
- SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ alSetError(context, AL_INVALID_ENUM, "Invalid equalizer float property 0x%04x", param);
}
}
void ALequalizer_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
-{
- ALequalizer_setParamf(effect, context, param, vals[0]);
-}
+{ ALequalizer_setParamf(effect, context, param, vals[0]); }
-void ALequalizer_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val))
-{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
-void ALequalizer_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
-{
- ALequalizer_getParami(effect, context, param, vals);
-}
+void ALequalizer_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(val))
+{ alSetError(context, AL_INVALID_ENUM, "Invalid equalizer integer property 0x%04x", param); }
+void ALequalizer_getParamiv(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(vals))
+{ alSetError(context, AL_INVALID_ENUM, "Invalid equalizer integer-vector property 0x%04x", param); }
void ALequalizer_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
{
const ALeffectProps *props = &effect->Props;
@@ -330,12 +346,10 @@ void ALequalizer_getParamf(const ALeffect *effect, ALCcontext *context, ALenum p
break;
default:
- SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ alSetError(context, AL_INVALID_ENUM, "Invalid equalizer float property 0x%04x", param);
}
}
void ALequalizer_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
-{
- ALequalizer_getParamf(effect, context, param, vals);
-}
+{ ALequalizer_getParamf(effect, context, param, vals); }
DEFINE_ALEFFECT_VTABLE(ALequalizer);
diff --git a/Alc/effects/flanger.c b/Alc/effects/flanger.c
deleted file mode 100644
index f6191abd..00000000
--- a/Alc/effects/flanger.c
+++ /dev/null
@@ -1,398 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 2013 by Mike Gorchak
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
-#include "config.h"
-
-#include <math.h>
-#include <stdlib.h>
-
-#include "alMain.h"
-#include "alFilter.h"
-#include "alAuxEffectSlot.h"
-#include "alError.h"
-#include "alu.h"
-
-
-enum FlangerWaveForm {
- FWF_Triangle = AL_FLANGER_WAVEFORM_TRIANGLE,
- FWF_Sinusoid = AL_FLANGER_WAVEFORM_SINUSOID
-};
-
-typedef struct ALflangerState {
- DERIVE_FROM_TYPE(ALeffectState);
-
- ALfloat *SampleBuffer[2];
- ALuint BufferLength;
- ALuint offset;
- ALuint lfo_range;
- ALfloat lfo_scale;
- ALint lfo_disp;
-
- /* Gains for left and right sides */
- ALfloat Gain[2][MAX_OUTPUT_CHANNELS];
-
- /* effect parameters */
- enum FlangerWaveForm waveform;
- ALint delay;
- ALfloat depth;
- ALfloat feedback;
-} ALflangerState;
-
-static ALvoid ALflangerState_Destruct(ALflangerState *state)
-{
- free(state->SampleBuffer[0]);
- state->SampleBuffer[0] = NULL;
- state->SampleBuffer[1] = NULL;
-}
-
-static ALboolean ALflangerState_deviceUpdate(ALflangerState *state, ALCdevice *Device)
-{
- ALuint maxlen;
- ALuint it;
-
- maxlen = fastf2u(AL_FLANGER_MAX_DELAY * 3.0f * Device->Frequency) + 1;
- maxlen = NextPowerOf2(maxlen);
-
- if(maxlen != state->BufferLength)
- {
- void *temp;
-
- temp = realloc(state->SampleBuffer[0], maxlen * sizeof(ALfloat) * 2);
- if(!temp) return AL_FALSE;
- state->SampleBuffer[0] = temp;
- state->SampleBuffer[1] = state->SampleBuffer[0] + maxlen;
-
- state->BufferLength = maxlen;
- }
-
- for(it = 0;it < state->BufferLength;it++)
- {
- state->SampleBuffer[0][it] = 0.0f;
- state->SampleBuffer[1][it] = 0.0f;
- }
-
- return AL_TRUE;
-}
-
-static ALvoid ALflangerState_update(ALflangerState *state, 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;
-
- switch(Slot->EffectProps.Flanger.Waveform)
- {
- case AL_FLANGER_WAVEFORM_TRIANGLE:
- state->waveform = FWF_Triangle;
- break;
- case AL_FLANGER_WAVEFORM_SINUSOID:
- state->waveform = FWF_Sinusoid;
- break;
- }
- state->depth = Slot->EffectProps.Flanger.Depth;
- state->feedback = Slot->EffectProps.Flanger.Feedback;
- state->delay = fastf2i(Slot->EffectProps.Flanger.Delay * frequency);
-
- /* Gains for left and right sides */
- 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;
- if(!(rate > 0.0f))
- {
- state->lfo_scale = 0.0f;
- state->lfo_range = 1;
- state->lfo_disp = 0;
- }
- else
- {
- /* Calculate LFO coefficient */
- state->lfo_range = fastf2u(frequency/rate + 0.5f);
- switch(state->waveform)
- {
- case FWF_Triangle:
- state->lfo_scale = 4.0f / state->lfo_range;
- break;
- case FWF_Sinusoid:
- state->lfo_scale = F_TAU / state->lfo_range;
- break;
- }
-
- /* Calculate lfo phase displacement */
- state->lfo_disp = fastf2i(state->lfo_range * (phase/360.0f));
- }
-}
-
-static inline void Triangle(ALint *delay_left, ALint *delay_right, ALuint offset, const ALflangerState *state)
-{
- ALfloat lfo_value;
-
- lfo_value = 2.0f - fabsf(2.0f - state->lfo_scale*(offset%state->lfo_range));
- lfo_value *= state->depth * state->delay;
- *delay_left = fastf2i(lfo_value) + state->delay;
-
- offset += state->lfo_disp;
- lfo_value = 2.0f - fabsf(2.0f - state->lfo_scale*(offset%state->lfo_range));
- lfo_value *= state->depth * state->delay;
- *delay_right = fastf2i(lfo_value) + state->delay;
-}
-
-static inline void Sinusoid(ALint *delay_left, ALint *delay_right, ALuint offset, const ALflangerState *state)
-{
- ALfloat lfo_value;
-
- lfo_value = 1.0f + sinf(state->lfo_scale*(offset%state->lfo_range));
- lfo_value *= state->depth * state->delay;
- *delay_left = fastf2i(lfo_value) + state->delay;
-
- offset += state->lfo_disp;
- lfo_value = 1.0f + sinf(state->lfo_scale*(offset%state->lfo_range));
- lfo_value *= state->depth * state->delay;
- *delay_right = fastf2i(lfo_value) + state->delay;
-}
-
-#define DECL_TEMPLATE(Func) \
-static void Process##Func(ALflangerState *state, const ALuint SamplesToDo, \
- const ALfloat *restrict SamplesIn, ALfloat (*restrict out)[2]) \
-{ \
- const ALuint bufmask = state->BufferLength-1; \
- ALfloat *restrict leftbuf = state->SampleBuffer[0]; \
- ALfloat *restrict rightbuf = state->SampleBuffer[1]; \
- ALuint offset = state->offset; \
- const ALfloat feedback = state->feedback; \
- ALuint it; \
- \
- for(it = 0;it < SamplesToDo;it++) \
- { \
- ALint delay_left, delay_right; \
- Func(&delay_left, &delay_right, offset, state); \
- \
- out[it][0] = leftbuf[(offset-delay_left)&bufmask]; \
- leftbuf[offset&bufmask] = (out[it][0]+SamplesIn[it]) * feedback; \
- \
- out[it][1] = rightbuf[(offset-delay_right)&bufmask]; \
- rightbuf[offset&bufmask] = (out[it][1]+SamplesIn[it]) * feedback; \
- \
- offset++; \
- } \
- state->offset = offset; \
-}
-
-DECL_TEMPLATE(Triangle)
-DECL_TEMPLATE(Sinusoid)
-
-#undef DECL_TEMPLATE
-
-static ALvoid ALflangerState_process(ALflangerState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels)
-{
- ALuint it, kt;
- ALuint base;
-
- for(base = 0;base < SamplesToDo;)
- {
- ALfloat temps[128][2];
- ALuint td = minu(128, SamplesToDo-base);
-
- switch(state->waveform)
- {
- case FWF_Triangle:
- ProcessTriangle(state, td, SamplesIn+base, temps);
- break;
- case FWF_Sinusoid:
- ProcessSinusoid(state, td, SamplesIn+base, temps);
- break;
- }
-
- for(kt = 0;kt < NumChannels;kt++)
- {
- ALfloat gain = state->Gain[0][kt];
- 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(fabsf(gain) > GAIN_SILENCE_THRESHOLD)
- {
- for(it = 0;it < td;it++)
- SamplesOut[kt][it+base] += temps[it][1] * gain;
- }
- }
-
- base += td;
- }
-}
-
-DECLARE_DEFAULT_ALLOCATORS(ALflangerState)
-
-DEFINE_ALEFFECTSTATE_VTABLE(ALflangerState);
-
-
-typedef struct ALflangerStateFactory {
- DERIVE_FROM_TYPE(ALeffectStateFactory);
-} ALflangerStateFactory;
-
-ALeffectState *ALflangerStateFactory_create(ALflangerStateFactory *UNUSED(factory))
-{
- ALflangerState *state;
-
- state = ALflangerState_New(sizeof(*state));
- if(!state) return NULL;
- SET_VTABLE2(ALflangerState, ALeffectState, state);
-
- state->BufferLength = 0;
- state->SampleBuffer[0] = NULL;
- state->SampleBuffer[1] = NULL;
- state->offset = 0;
- state->lfo_range = 1;
- state->waveform = FWF_Triangle;
-
- return STATIC_CAST(ALeffectState, state);
-}
-
-DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALflangerStateFactory);
-
-ALeffectStateFactory *ALflangerStateFactory_getFactory(void)
-{
- static ALflangerStateFactory FlangerFactory = { { GET_VTABLE2(ALflangerStateFactory, ALeffectStateFactory) } };
-
- return STATIC_CAST(ALeffectStateFactory, &FlangerFactory);
-}
-
-
-void ALflanger_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
-{
- ALeffectProps *props = &effect->Props;
- switch(param)
- {
- case AL_FLANGER_WAVEFORM:
- if(!(val >= AL_FLANGER_MIN_WAVEFORM && val <= AL_FLANGER_MAX_WAVEFORM))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
- props->Flanger.Waveform = val;
- break;
-
- case AL_FLANGER_PHASE:
- if(!(val >= AL_FLANGER_MIN_PHASE && val <= AL_FLANGER_MAX_PHASE))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
- props->Flanger.Phase = val;
- break;
-
- default:
- SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
- }
-}
-void ALflanger_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
-{
- ALflanger_setParami(effect, context, param, vals[0]);
-}
-void ALflanger_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
-{
- ALeffectProps *props = &effect->Props;
- switch(param)
- {
- case AL_FLANGER_RATE:
- if(!(val >= AL_FLANGER_MIN_RATE && val <= AL_FLANGER_MAX_RATE))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
- props->Flanger.Rate = val;
- break;
-
- case AL_FLANGER_DEPTH:
- if(!(val >= AL_FLANGER_MIN_DEPTH && val <= AL_FLANGER_MAX_DEPTH))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
- props->Flanger.Depth = val;
- break;
-
- case AL_FLANGER_FEEDBACK:
- if(!(val >= AL_FLANGER_MIN_FEEDBACK && val <= AL_FLANGER_MAX_FEEDBACK))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
- props->Flanger.Feedback = val;
- break;
-
- case AL_FLANGER_DELAY:
- if(!(val >= AL_FLANGER_MIN_DELAY && val <= AL_FLANGER_MAX_DELAY))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
- props->Flanger.Delay = val;
- break;
-
- default:
- SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
- }
-}
-void ALflanger_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
-{
- ALflanger_setParamf(effect, context, param, vals[0]);
-}
-
-void ALflanger_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
-{
- const ALeffectProps *props = &effect->Props;
- switch(param)
- {
- case AL_FLANGER_WAVEFORM:
- *val = props->Flanger.Waveform;
- break;
-
- case AL_FLANGER_PHASE:
- *val = props->Flanger.Phase;
- break;
-
- default:
- SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
- }
-}
-void ALflanger_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
-{
- ALflanger_getParami(effect, context, param, vals);
-}
-void ALflanger_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
-{
- const ALeffectProps *props = &effect->Props;
- switch(param)
- {
- case AL_FLANGER_RATE:
- *val = props->Flanger.Rate;
- break;
-
- case AL_FLANGER_DEPTH:
- *val = props->Flanger.Depth;
- break;
-
- case AL_FLANGER_FEEDBACK:
- *val = props->Flanger.Feedback;
- break;
-
- case AL_FLANGER_DELAY:
- *val = props->Flanger.Delay;
- break;
-
- default:
- SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
- }
-}
-void ALflanger_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
-{
- ALflanger_getParamf(effect, context, param, vals);
-}
-
-DEFINE_ALEFFECT_VTABLE(ALflanger);
diff --git a/Alc/effects/fshifter.c b/Alc/effects/fshifter.c
new file mode 100644
index 00000000..7d72472a
--- /dev/null
+++ b/Alc/effects/fshifter.c
@@ -0,0 +1,329 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 2018 by Raul Herraiz.
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#include "config.h"
+
+#include <math.h>
+#include <stdlib.h>
+
+#include "alMain.h"
+#include "alAuxEffectSlot.h"
+#include "alError.h"
+#include "alu.h"
+#include "filters/defs.h"
+
+#include "alcomplex.h"
+
+#define HIL_SIZE 1024
+#define OVERSAMP (1<<2)
+
+#define HIL_STEP (HIL_SIZE / OVERSAMP)
+#define FIFO_LATENCY (HIL_STEP * (OVERSAMP-1))
+
+
+typedef struct ALfshifterState {
+ DERIVE_FROM_TYPE(ALeffectState);
+
+ /* Effect parameters */
+ ALsizei count;
+ ALsizei PhaseStep;
+ ALsizei Phase;
+ ALdouble ld_sign;
+
+ /*Effects buffers*/
+ ALfloat InFIFO[HIL_SIZE];
+ ALcomplex OutFIFO[HIL_SIZE];
+ ALcomplex OutputAccum[HIL_SIZE];
+ ALcomplex Analytic[HIL_SIZE];
+ ALcomplex Outdata[BUFFERSIZE];
+
+ alignas(16) ALfloat BufferOut[BUFFERSIZE];
+
+ /* Effect gains for each output channel */
+ ALfloat CurrentGains[MAX_OUTPUT_CHANNELS];
+ ALfloat TargetGains[MAX_OUTPUT_CHANNELS];
+} ALfshifterState;
+
+static ALvoid ALfshifterState_Destruct(ALfshifterState *state);
+static ALboolean ALfshifterState_deviceUpdate(ALfshifterState *state, ALCdevice *device);
+static ALvoid ALfshifterState_update(ALfshifterState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props);
+static ALvoid ALfshifterState_process(ALfshifterState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
+DECLARE_DEFAULT_ALLOCATORS(ALfshifterState)
+
+DEFINE_ALEFFECTSTATE_VTABLE(ALfshifterState);
+
+/* Define a Hann window, used to filter the HIL input and output. */
+alignas(16) static ALdouble HannWindow[HIL_SIZE];
+
+static void InitHannWindow(void)
+{
+ ALsizei i;
+
+ /* Create lookup table of the Hann window for the desired size, i.e. HIL_SIZE */
+ for(i = 0;i < HIL_SIZE>>1;i++)
+ {
+ ALdouble val = sin(M_PI * (ALdouble)i / (ALdouble)(HIL_SIZE-1));
+ HannWindow[i] = HannWindow[HIL_SIZE-1-i] = val * val;
+ }
+}
+
+static alonce_flag HannInitOnce = AL_ONCE_FLAG_INIT;
+
+static void ALfshifterState_Construct(ALfshifterState *state)
+{
+ ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
+ SET_VTABLE2(ALfshifterState, ALeffectState, state);
+
+ alcall_once(&HannInitOnce, InitHannWindow);
+}
+
+static ALvoid ALfshifterState_Destruct(ALfshifterState *state)
+{
+ ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
+}
+
+static ALboolean ALfshifterState_deviceUpdate(ALfshifterState *state, ALCdevice *UNUSED(device))
+{
+ /* (Re-)initializing parameters and clear the buffers. */
+ state->count = FIFO_LATENCY;
+ state->PhaseStep = 0;
+ state->Phase = 0;
+ state->ld_sign = 1.0;
+
+ memset(state->InFIFO, 0, sizeof(state->InFIFO));
+ memset(state->OutFIFO, 0, sizeof(state->OutFIFO));
+ memset(state->OutputAccum, 0, sizeof(state->OutputAccum));
+ memset(state->Analytic, 0, sizeof(state->Analytic));
+
+ memset(state->CurrentGains, 0, sizeof(state->CurrentGains));
+ memset(state->TargetGains, 0, sizeof(state->TargetGains));
+
+ return AL_TRUE;
+}
+
+static ALvoid ALfshifterState_update(ALfshifterState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props)
+{
+ const ALCdevice *device = context->Device;
+ ALfloat coeffs[MAX_AMBI_COEFFS];
+ ALfloat step;
+
+ step = props->Fshifter.Frequency / (ALfloat)device->Frequency;
+ state->PhaseStep = fastf2i(minf(step, 0.5f) * FRACTIONONE);
+
+ switch(props->Fshifter.LeftDirection)
+ {
+ case AL_FREQUENCY_SHIFTER_DIRECTION_DOWN:
+ state->ld_sign = -1.0;
+ break;
+
+ case AL_FREQUENCY_SHIFTER_DIRECTION_UP:
+ state->ld_sign = 1.0;
+ break;
+
+ case AL_FREQUENCY_SHIFTER_DIRECTION_OFF:
+ state->Phase = 0;
+ state->PhaseStep = 0;
+ break;
+ }
+
+ CalcAngleCoeffs(0.0f, 0.0f, 0.0f, coeffs);
+ ComputePanGains(&device->Dry, coeffs, slot->Params.Gain, state->TargetGains);
+}
+
+static ALvoid ALfshifterState_process(ALfshifterState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
+{
+ static const ALcomplex complex_zero = { 0.0, 0.0 };
+ ALfloat *restrict BufferOut = state->BufferOut;
+ ALsizei j, k, base;
+
+ for(base = 0;base < SamplesToDo;)
+ {
+ ALsizei todo = mini(HIL_SIZE-state->count, SamplesToDo-base);
+
+ ASSUME(todo > 0);
+
+ /* Fill FIFO buffer with samples data */
+ k = state->count;
+ for(j = 0;j < todo;j++,k++)
+ {
+ state->InFIFO[k] = SamplesIn[0][base+j];
+ state->Outdata[base+j] = state->OutFIFO[k-FIFO_LATENCY];
+ }
+ state->count += todo;
+ base += todo;
+
+ /* Check whether FIFO buffer is filled */
+ if(state->count < HIL_SIZE) continue;
+
+ state->count = FIFO_LATENCY;
+
+ /* Real signal windowing and store in Analytic buffer */
+ for(k = 0;k < HIL_SIZE;k++)
+ {
+ state->Analytic[k].Real = state->InFIFO[k] * HannWindow[k];
+ state->Analytic[k].Imag = 0.0;
+ }
+
+ /* Processing signal by Discrete Hilbert Transform (analytical signal). */
+ complex_hilbert(state->Analytic, HIL_SIZE);
+
+ /* Windowing and add to output accumulator */
+ for(k = 0;k < HIL_SIZE;k++)
+ {
+ state->OutputAccum[k].Real += 2.0/OVERSAMP*HannWindow[k]*state->Analytic[k].Real;
+ state->OutputAccum[k].Imag += 2.0/OVERSAMP*HannWindow[k]*state->Analytic[k].Imag;
+ }
+
+ /* Shift accumulator, input & output FIFO */
+ for(k = 0;k < HIL_STEP;k++) state->OutFIFO[k] = state->OutputAccum[k];
+ for(j = 0;k < HIL_SIZE;k++,j++) state->OutputAccum[j] = state->OutputAccum[k];
+ for(;j < HIL_SIZE;j++) state->OutputAccum[j] = complex_zero;
+ for(k = 0;k < FIFO_LATENCY;k++)
+ state->InFIFO[k] = state->InFIFO[k+HIL_STEP];
+ }
+
+ /* Process frequency shifter using the analytic signal obtained. */
+ for(k = 0;k < SamplesToDo;k++)
+ {
+ ALdouble phase = state->Phase * ((1.0/FRACTIONONE) * 2.0*M_PI);
+ BufferOut[k] = (ALfloat)(state->Outdata[k].Real*cos(phase) +
+ state->Outdata[k].Imag*sin(phase)*state->ld_sign);
+
+ state->Phase += state->PhaseStep;
+ state->Phase &= FRACTIONMASK;
+ }
+
+ /* Now, mix the processed sound data to the output. */
+ MixSamples(BufferOut, NumChannels, SamplesOut, state->CurrentGains, state->TargetGains,
+ maxi(SamplesToDo, 512), 0, SamplesToDo);
+}
+
+typedef struct FshifterStateFactory {
+ DERIVE_FROM_TYPE(EffectStateFactory);
+} FshifterStateFactory;
+
+static ALeffectState *FshifterStateFactory_create(FshifterStateFactory *UNUSED(factory))
+{
+ ALfshifterState *state;
+
+ NEW_OBJ0(state, ALfshifterState)();
+ if(!state) return NULL;
+
+ return STATIC_CAST(ALeffectState, state);
+}
+
+DEFINE_EFFECTSTATEFACTORY_VTABLE(FshifterStateFactory);
+
+EffectStateFactory *FshifterStateFactory_getFactory(void)
+{
+ static FshifterStateFactory FshifterFactory = { { GET_VTABLE2(FshifterStateFactory, EffectStateFactory) } };
+
+ return STATIC_CAST(EffectStateFactory, &FshifterFactory);
+}
+
+void ALfshifter_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
+{
+ ALeffectProps *props = &effect->Props;
+ switch(param)
+ {
+ case AL_FREQUENCY_SHIFTER_FREQUENCY:
+ if(!(val >= AL_FREQUENCY_SHIFTER_MIN_FREQUENCY && val <= AL_FREQUENCY_SHIFTER_MAX_FREQUENCY))
+ SETERR_RETURN(context, AL_INVALID_VALUE,,"Frequency shifter frequency out of range");
+ props->Fshifter.Frequency = val;
+ break;
+
+ default:
+ alSetError(context, AL_INVALID_ENUM, "Invalid frequency shifter float property 0x%04x", param);
+ }
+}
+
+void ALfshifter_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
+{
+ ALfshifter_setParamf(effect, context, param, vals[0]);
+}
+
+void ALfshifter_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
+{
+ ALeffectProps *props = &effect->Props;
+ switch(param)
+ {
+ case AL_FREQUENCY_SHIFTER_LEFT_DIRECTION:
+ if(!(val >= AL_FREQUENCY_SHIFTER_MIN_LEFT_DIRECTION && val <= AL_FREQUENCY_SHIFTER_MAX_LEFT_DIRECTION))
+ SETERR_RETURN(context, AL_INVALID_VALUE,,"Frequency shifter left direction out of range");
+ props->Fshifter.LeftDirection = val;
+ break;
+
+ case AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION:
+ if(!(val >= AL_FREQUENCY_SHIFTER_MIN_RIGHT_DIRECTION && val <= AL_FREQUENCY_SHIFTER_MAX_RIGHT_DIRECTION))
+ SETERR_RETURN(context, AL_INVALID_VALUE,,"Frequency shifter right direction out of range");
+ props->Fshifter.RightDirection = val;
+ break;
+
+ default:
+ alSetError(context, AL_INVALID_ENUM, "Invalid frequency shifter integer property 0x%04x", param);
+ }
+}
+void ALfshifter_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
+{
+ ALfshifter_setParami(effect, context, param, vals[0]);
+}
+
+void ALfshifter_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
+{
+ const ALeffectProps *props = &effect->Props;
+ switch(param)
+ {
+ case AL_FREQUENCY_SHIFTER_LEFT_DIRECTION:
+ *val = props->Fshifter.LeftDirection;
+ break;
+ case AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION:
+ *val = props->Fshifter.RightDirection;
+ break;
+ default:
+ alSetError(context, AL_INVALID_ENUM, "Invalid frequency shifter integer property 0x%04x", param);
+ }
+}
+void ALfshifter_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
+{
+ ALfshifter_getParami(effect, context, param, vals);
+}
+
+void ALfshifter_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
+{
+
+ const ALeffectProps *props = &effect->Props;
+ switch(param)
+ {
+ case AL_FREQUENCY_SHIFTER_FREQUENCY:
+ *val = props->Fshifter.Frequency;
+ break;
+
+ default:
+ alSetError(context, AL_INVALID_ENUM, "Invalid frequency shifter float property 0x%04x", param);
+ }
+
+}
+
+void ALfshifter_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
+{
+ ALfshifter_getParamf(effect, context, param, vals);
+}
+
+DEFINE_ALEFFECT_VTABLE(ALfshifter);
diff --git a/Alc/effects/modulator.c b/Alc/effects/modulator.c
index dceb408e..e368adb8 100644
--- a/Alc/effects/modulator.c
+++ b/Alc/effects/modulator.c
@@ -24,184 +24,197 @@
#include <stdlib.h>
#include "alMain.h"
-#include "alFilter.h"
#include "alAuxEffectSlot.h"
#include "alError.h"
#include "alu.h"
+#include "filters/defs.h"
+#define MAX_UPDATE_SAMPLES 128
+
typedef struct ALmodulatorState {
DERIVE_FROM_TYPE(ALeffectState);
- enum {
- SINUSOID,
- SAWTOOTH,
- SQUARE
- } Waveform;
+ void (*GetSamples)(ALfloat*, ALsizei, const ALsizei, ALsizei);
- ALuint index;
- ALuint step;
+ ALsizei index;
+ ALsizei step;
- ALfloat Gain[MAX_OUTPUT_CHANNELS];
+ struct {
+ BiquadFilter Filter;
- ALfilterState Filter;
+ ALfloat CurrentGains[MAX_OUTPUT_CHANNELS];
+ ALfloat TargetGains[MAX_OUTPUT_CHANNELS];
+ } Chans[MAX_EFFECT_CHANNELS];
} ALmodulatorState;
+static ALvoid ALmodulatorState_Destruct(ALmodulatorState *state);
+static ALboolean ALmodulatorState_deviceUpdate(ALmodulatorState *state, ALCdevice *device);
+static ALvoid ALmodulatorState_update(ALmodulatorState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props);
+static ALvoid ALmodulatorState_process(ALmodulatorState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
+DECLARE_DEFAULT_ALLOCATORS(ALmodulatorState)
+
+DEFINE_ALEFFECTSTATE_VTABLE(ALmodulatorState);
+
+
#define WAVEFORM_FRACBITS 24
#define WAVEFORM_FRACONE (1<<WAVEFORM_FRACBITS)
#define WAVEFORM_FRACMASK (WAVEFORM_FRACONE-1)
-static inline ALfloat Sin(ALuint index)
+static inline ALfloat Sin(ALsizei index)
{
- return sinf(index*(F_TAU/WAVEFORM_FRACONE) - F_PI)*0.5f + 0.5f;
+ return sinf((ALfloat)index * (F_TAU / WAVEFORM_FRACONE));
}
-static inline ALfloat Saw(ALuint index)
+static inline ALfloat Saw(ALsizei index)
{
- return (ALfloat)index / WAVEFORM_FRACONE;
+ return (ALfloat)index*(2.0f/WAVEFORM_FRACONE) - 1.0f;
}
-static inline ALfloat Square(ALuint index)
+static inline ALfloat Square(ALsizei index)
{
- return (ALfloat)((index >> (WAVEFORM_FRACBITS - 1)) & 1);
+ return (ALfloat)(((index>>(WAVEFORM_FRACBITS-2))&2) - 1);
+}
+
+static inline ALfloat One(ALsizei UNUSED(index))
+{
+ return 1.0f;
}
#define DECL_TEMPLATE(func) \
-static void Process##func(ALmodulatorState *state, ALuint SamplesToDo, \
- const ALfloat *restrict SamplesIn, \
- ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels) \
+static void Modulate##func(ALfloat *restrict dst, ALsizei index, \
+ const ALsizei step, ALsizei todo) \
{ \
- const ALuint step = state->step; \
- ALuint index = state->index; \
- ALuint base; \
- \
- for(base = 0;base < SamplesToDo;) \
+ ALsizei i; \
+ for(i = 0;i < todo;i++) \
{ \
- ALfloat temps[256]; \
- ALuint td = minu(256, SamplesToDo-base); \
- ALuint i, k; \
- \
- for(i = 0;i < td;i++) \
- { \
- ALfloat samp; \
- samp = SamplesIn[base+i]; \
- samp = ALfilterState_processSingle(&state->Filter, samp); \
- \
- index += step; \
- index &= WAVEFORM_FRACMASK; \
- temps[i] = samp * func(index); \
- } \
- \
- for(k = 0;k < NumChannels;k++) \
- { \
- ALfloat gain = state->Gain[k]; \
- if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD)) \
- continue; \
- \
- for(i = 0;i < td;i++) \
- SamplesOut[k][base+i] += gain * temps[i]; \
- } \
- \
- base += td; \
+ index += step; \
+ index &= WAVEFORM_FRACMASK; \
+ dst[i] = func(index); \
} \
- state->index = index; \
}
DECL_TEMPLATE(Sin)
DECL_TEMPLATE(Saw)
DECL_TEMPLATE(Square)
+DECL_TEMPLATE(One)
#undef DECL_TEMPLATE
-static ALvoid ALmodulatorState_Destruct(ALmodulatorState *UNUSED(state))
+static void ALmodulatorState_Construct(ALmodulatorState *state)
{
+ ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
+ SET_VTABLE2(ALmodulatorState, ALeffectState, state);
+
+ state->index = 0;
+ state->step = 1;
+}
+
+static ALvoid ALmodulatorState_Destruct(ALmodulatorState *state)
+{
+ ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
}
-static ALboolean ALmodulatorState_deviceUpdate(ALmodulatorState *UNUSED(state), ALCdevice *UNUSED(device))
+static ALboolean ALmodulatorState_deviceUpdate(ALmodulatorState *state, ALCdevice *UNUSED(device))
{
+ ALsizei i, j;
+ for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
+ {
+ BiquadFilter_clear(&state->Chans[i].Filter);
+ for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
+ state->Chans[i].CurrentGains[j] = 0.0f;
+ }
return AL_TRUE;
}
-static ALvoid ALmodulatorState_update(ALmodulatorState *state, ALCdevice *Device, const ALeffectslot *Slot)
+static ALvoid ALmodulatorState_update(ALmodulatorState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props)
{
- ALfloat cw, a;
-
- if(Slot->EffectProps.Modulator.Waveform == AL_RING_MODULATOR_SINUSOID)
- state->Waveform = SINUSOID;
- else if(Slot->EffectProps.Modulator.Waveform == AL_RING_MODULATOR_SAWTOOTH)
- state->Waveform = SAWTOOTH;
- else if(Slot->EffectProps.Modulator.Waveform == AL_RING_MODULATOR_SQUARE)
- state->Waveform = SQUARE;
-
- state->step = fastf2u(Slot->EffectProps.Modulator.Frequency*WAVEFORM_FRACONE /
- Device->Frequency);
- if(state->step == 0) state->step = 1;
-
- /* Custom filter coeffs, which match the old version instead of a low-shelf. */
- cw = cosf(F_TAU * Slot->EffectProps.Modulator.HighPassCutoff / Device->Frequency);
- a = (2.0f-cw) - sqrtf(powf(2.0f-cw, 2.0f) - 1.0f);
-
- state->Filter.b[0] = a;
- state->Filter.b[1] = -a;
- state->Filter.b[2] = 0.0f;
- state->Filter.a[0] = 1.0f;
- state->Filter.a[1] = -a;
- state->Filter.a[2] = 0.0f;
-
- ComputeAmbientGains(Device, Slot->Gain, state->Gain);
+ const ALCdevice *device = context->Device;
+ ALfloat f0norm;
+ ALsizei i;
+
+ state->step = fastf2i(props->Modulator.Frequency / (ALfloat)device->Frequency *
+ WAVEFORM_FRACONE);
+ state->step = clampi(state->step, 0, WAVEFORM_FRACONE-1);
+
+ if(state->step == 0)
+ state->GetSamples = ModulateOne;
+ else if(props->Modulator.Waveform == AL_RING_MODULATOR_SINUSOID)
+ state->GetSamples = ModulateSin;
+ else if(props->Modulator.Waveform == AL_RING_MODULATOR_SAWTOOTH)
+ state->GetSamples = ModulateSaw;
+ else /*if(Slot->Params.EffectProps.Modulator.Waveform == AL_RING_MODULATOR_SQUARE)*/
+ state->GetSamples = ModulateSquare;
+
+ f0norm = props->Modulator.HighPassCutoff / (ALfloat)device->Frequency;
+ f0norm = clampf(f0norm, 1.0f/512.0f, 0.49f);
+ /* Bandwidth value is constant in octaves. */
+ BiquadFilter_setParams(&state->Chans[0].Filter, BiquadType_HighPass, 1.0f,
+ f0norm, calc_rcpQ_from_bandwidth(f0norm, 0.75f));
+ for(i = 1;i < MAX_EFFECT_CHANNELS;i++)
+ BiquadFilter_copyParams(&state->Chans[i].Filter, &state->Chans[0].Filter);
+
+ STATIC_CAST(ALeffectState,state)->OutBuffer = device->FOAOut.Buffer;
+ STATIC_CAST(ALeffectState,state)->OutChannels = device->FOAOut.NumChannels;
+ for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
+ ComputePanGains(&device->FOAOut, IdentityMatrixf.m[i], slot->Params.Gain,
+ state->Chans[i].TargetGains);
}
-static ALvoid ALmodulatorState_process(ALmodulatorState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels)
+static ALvoid ALmodulatorState_process(ALmodulatorState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
{
- switch(state->Waveform)
+ const ALsizei step = state->step;
+ ALsizei base;
+
+ for(base = 0;base < SamplesToDo;)
{
- case SINUSOID:
- ProcessSin(state, SamplesToDo, SamplesIn, SamplesOut, NumChannels);
- break;
+ alignas(16) ALfloat modsamples[MAX_UPDATE_SAMPLES];
+ ALsizei td = mini(MAX_UPDATE_SAMPLES, SamplesToDo-base);
+ ALsizei c, i;
- case SAWTOOTH:
- ProcessSaw(state, SamplesToDo, SamplesIn, SamplesOut, NumChannels);
- break;
+ state->GetSamples(modsamples, state->index, step, td);
+ state->index += (step*td) & WAVEFORM_FRACMASK;
+ state->index &= WAVEFORM_FRACMASK;
- case SQUARE:
- ProcessSquare(state, SamplesToDo, SamplesIn, SamplesOut, NumChannels);
- break;
- }
-}
+ for(c = 0;c < MAX_EFFECT_CHANNELS;c++)
+ {
+ alignas(16) ALfloat temps[MAX_UPDATE_SAMPLES];
-DECLARE_DEFAULT_ALLOCATORS(ALmodulatorState)
+ BiquadFilter_process(&state->Chans[c].Filter, temps, &SamplesIn[c][base], td);
+ for(i = 0;i < td;i++)
+ temps[i] *= modsamples[i];
-DEFINE_ALEFFECTSTATE_VTABLE(ALmodulatorState);
+ MixSamples(temps, NumChannels, SamplesOut, state->Chans[c].CurrentGains,
+ state->Chans[c].TargetGains, SamplesToDo-base, base, td);
+ }
+
+ base += td;
+ }
+}
-typedef struct ALmodulatorStateFactory {
- DERIVE_FROM_TYPE(ALeffectStateFactory);
-} ALmodulatorStateFactory;
+typedef struct ModulatorStateFactory {
+ DERIVE_FROM_TYPE(EffectStateFactory);
+} ModulatorStateFactory;
-static ALeffectState *ALmodulatorStateFactory_create(ALmodulatorStateFactory *UNUSED(factory))
+static ALeffectState *ModulatorStateFactory_create(ModulatorStateFactory *UNUSED(factory))
{
ALmodulatorState *state;
- state = ALmodulatorState_New(sizeof(*state));
+ NEW_OBJ0(state, ALmodulatorState)();
if(!state) return NULL;
- SET_VTABLE2(ALmodulatorState, ALeffectState, state);
-
- state->index = 0;
- state->step = 1;
-
- ALfilterState_clear(&state->Filter);
return STATIC_CAST(ALeffectState, state);
}
-DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALmodulatorStateFactory);
+DEFINE_EFFECTSTATEFACTORY_VTABLE(ModulatorStateFactory);
-ALeffectStateFactory *ALmodulatorStateFactory_getFactory(void)
+EffectStateFactory *ModulatorStateFactory_getFactory(void)
{
- static ALmodulatorStateFactory ModulatorFactory = { { GET_VTABLE2(ALmodulatorStateFactory, ALeffectStateFactory) } };
+ static ModulatorStateFactory ModulatorFactory = { { GET_VTABLE2(ModulatorStateFactory, EffectStateFactory) } };
- return STATIC_CAST(ALeffectStateFactory, &ModulatorFactory);
+ return STATIC_CAST(EffectStateFactory, &ModulatorFactory);
}
@@ -212,24 +225,22 @@ void ALmodulator_setParamf(ALeffect *effect, ALCcontext *context, ALenum param,
{
case AL_RING_MODULATOR_FREQUENCY:
if(!(val >= AL_RING_MODULATOR_MIN_FREQUENCY && val <= AL_RING_MODULATOR_MAX_FREQUENCY))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "Modulator frequency out of range");
props->Modulator.Frequency = val;
break;
case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
if(!(val >= AL_RING_MODULATOR_MIN_HIGHPASS_CUTOFF && val <= AL_RING_MODULATOR_MAX_HIGHPASS_CUTOFF))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "Modulator high-pass cutoff out of range");
props->Modulator.HighPassCutoff = val;
break;
default:
- SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ alSetError(context, AL_INVALID_ENUM, "Invalid modulator float property 0x%04x", param);
}
}
void ALmodulator_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
-{
- ALmodulator_setParamf(effect, context, param, vals[0]);
-}
+{ ALmodulator_setParamf(effect, context, param, vals[0]); }
void ALmodulator_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
{
ALeffectProps *props = &effect->Props;
@@ -242,18 +253,16 @@ void ALmodulator_setParami(ALeffect *effect, ALCcontext *context, ALenum param,
case AL_RING_MODULATOR_WAVEFORM:
if(!(val >= AL_RING_MODULATOR_MIN_WAVEFORM && val <= AL_RING_MODULATOR_MAX_WAVEFORM))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "Invalid modulator waveform");
props->Modulator.Waveform = val;
break;
default:
- SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ alSetError(context, AL_INVALID_ENUM, "Invalid modulator integer property 0x%04x", param);
}
}
void ALmodulator_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
-{
- ALmodulator_setParami(effect, context, param, vals[0]);
-}
+{ ALmodulator_setParami(effect, context, param, vals[0]); }
void ALmodulator_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
{
@@ -271,13 +280,11 @@ void ALmodulator_getParami(const ALeffect *effect, ALCcontext *context, ALenum p
break;
default:
- SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ alSetError(context, AL_INVALID_ENUM, "Invalid modulator integer property 0x%04x", param);
}
}
void ALmodulator_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
-{
- ALmodulator_getParami(effect, context, param, vals);
-}
+{ ALmodulator_getParami(effect, context, param, vals); }
void ALmodulator_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
{
const ALeffectProps *props = &effect->Props;
@@ -291,12 +298,10 @@ void ALmodulator_getParamf(const ALeffect *effect, ALCcontext *context, ALenum p
break;
default:
- SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ alSetError(context, AL_INVALID_ENUM, "Invalid modulator float property 0x%04x", param);
}
}
void ALmodulator_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
-{
- ALmodulator_getParamf(effect, context, param, vals);
-}
+{ ALmodulator_getParamf(effect, context, param, vals); }
DEFINE_ALEFFECT_VTABLE(ALmodulator);
diff --git a/Alc/effects/null.c b/Alc/effects/null.c
index adc4ca81..e57359e3 100644
--- a/Alc/effects/null.c
+++ b/Alc/effects/null.c
@@ -13,12 +13,35 @@ typedef struct ALnullState {
DERIVE_FROM_TYPE(ALeffectState);
} ALnullState;
+/* Forward-declare "virtual" functions to define the vtable with. */
+static ALvoid ALnullState_Destruct(ALnullState *state);
+static ALboolean ALnullState_deviceUpdate(ALnullState *state, ALCdevice *device);
+static ALvoid ALnullState_update(ALnullState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props);
+static ALvoid ALnullState_process(ALnullState *state, ALsizei samplesToDo, const ALfloat (*restrict samplesIn)[BUFFERSIZE], ALfloat (*restrict samplesOut)[BUFFERSIZE], ALsizei mumChannels);
+static void *ALnullState_New(size_t size);
+static void ALnullState_Delete(void *ptr);
+
+/* Define the ALeffectState vtable for this type. */
+DEFINE_ALEFFECTSTATE_VTABLE(ALnullState);
+
+
+/* This constructs the effect state. It's called when the object is first
+ * created. Make sure to call the parent Construct function first, and set the
+ * vtable!
+ */
+static void ALnullState_Construct(ALnullState *state)
+{
+ ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
+ SET_VTABLE2(ALnullState, ALeffectState, state);
+}
/* This destructs (not free!) the effect state. It's called only when the
- * effect slot is no longer used.
+ * effect slot is no longer used. Make sure to call the parent Destruct
+ * function before returning!
*/
-static ALvoid ALnullState_Destruct(ALnullState* UNUSED(state))
+static ALvoid ALnullState_Destruct(ALnullState *state)
{
+ ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
}
/* This updates the device-dependant effect state. This is called on
@@ -33,7 +56,7 @@ static ALboolean ALnullState_deviceUpdate(ALnullState* UNUSED(state), ALCdevice*
/* This updates the effect state. This is called any time the effect is
* (re)loaded into a slot.
*/
-static ALvoid ALnullState_update(ALnullState* UNUSED(state), ALCdevice* UNUSED(device), const ALeffectslot* UNUSED(slot))
+static ALvoid ALnullState_update(ALnullState* UNUSED(state), const ALCcontext* UNUSED(context), const ALeffectslot* UNUSED(slot), const ALeffectProps* UNUSED(props))
{
}
@@ -41,121 +64,115 @@ static ALvoid ALnullState_update(ALnullState* UNUSED(state), ALCdevice* UNUSED(d
* input to the output buffer. The result should be added to the output buffer,
* not replace it.
*/
-static ALvoid ALnullState_process(ALnullState* UNUSED(state), ALuint UNUSED(samplesToDo), const ALfloat *restrict UNUSED(samplesIn), ALfloatBUFFERSIZE*restrict UNUSED(samplesOut), ALuint UNUSED(NumChannels))
+static ALvoid ALnullState_process(ALnullState* UNUSED(state), ALsizei UNUSED(samplesToDo), const ALfloatBUFFERSIZE*restrict UNUSED(samplesIn), ALfloatBUFFERSIZE*restrict UNUSED(samplesOut), ALsizei UNUSED(numChannels))
{
}
/* This allocates memory to store the object, before it gets constructed.
- * DECLARE_DEFAULT_ALLOCATORS can be used to declate a default method.
+ * DECLARE_DEFAULT_ALLOCATORS can be used to declare a default method.
*/
static void *ALnullState_New(size_t size)
{
- return malloc(size);
+ return al_malloc(16, size);
}
/* This frees the memory used by the object, after it has been destructed.
- * DECLARE_DEFAULT_ALLOCATORS can be used to declate a default method.
+ * DECLARE_DEFAULT_ALLOCATORS can be used to declare a default method.
*/
static void ALnullState_Delete(void *ptr)
{
- free(ptr);
+ al_free(ptr);
}
-/* Define the forwards and the ALeffectState vtable for this type. */
-DEFINE_ALEFFECTSTATE_VTABLE(ALnullState);
-
-typedef struct ALnullStateFactory {
- DERIVE_FROM_TYPE(ALeffectStateFactory);
-} ALnullStateFactory;
+typedef struct NullStateFactory {
+ DERIVE_FROM_TYPE(EffectStateFactory);
+} NullStateFactory;
/* Creates ALeffectState objects of the appropriate type. */
-ALeffectState *ALnullStateFactory_create(ALnullStateFactory *UNUSED(factory))
+ALeffectState *NullStateFactory_create(NullStateFactory *UNUSED(factory))
{
ALnullState *state;
- state = ALnullState_New(sizeof(*state));
+ NEW_OBJ0(state, ALnullState)();
if(!state) return NULL;
- /* Set vtables for inherited types. */
- SET_VTABLE2(ALnullState, ALeffectState, state);
return STATIC_CAST(ALeffectState, state);
}
-/* Define the ALeffectStateFactory vtable for this type. */
-DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALnullStateFactory);
+/* Define the EffectStateFactory vtable for this type. */
+DEFINE_EFFECTSTATEFACTORY_VTABLE(NullStateFactory);
-ALeffectStateFactory *ALnullStateFactory_getFactory(void)
+EffectStateFactory *NullStateFactory_getFactory(void)
{
- static ALnullStateFactory NullFactory = { { GET_VTABLE2(ALnullStateFactory, ALeffectStateFactory) } };
-
- return STATIC_CAST(ALeffectStateFactory, &NullFactory);
+ static NullStateFactory NullFactory = { { GET_VTABLE2(NullStateFactory, EffectStateFactory) } };
+ return STATIC_CAST(EffectStateFactory, &NullFactory);
}
-void ALnull_setParami(ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, ALint UNUSED(val))
+void ALnull_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint UNUSED(val))
{
switch(param)
{
- default:
- SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ default:
+ alSetError(context, AL_INVALID_ENUM, "Invalid null effect integer property 0x%04x", param);
}
}
-void ALnull_setParamiv(ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, const ALint* UNUSED(vals))
+void ALnull_setParamiv(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, const ALint* UNUSED(vals))
{
switch(param)
{
- default:
- SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ default:
+ alSetError(context, AL_INVALID_ENUM, "Invalid null effect integer-vector property 0x%04x", param);
}
}
-void ALnull_setParamf(ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, ALfloat UNUSED(val))
+void ALnull_setParamf(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALfloat UNUSED(val))
{
switch(param)
{
- default:
- SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ default:
+ alSetError(context, AL_INVALID_ENUM, "Invalid null effect float property 0x%04x", param);
}
}
-void ALnull_setParamfv(ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, const ALfloat* UNUSED(vals))
+void ALnull_setParamfv(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, const ALfloat* UNUSED(vals))
{
switch(param)
{
- default:
- SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ default:
+ alSetError(context, AL_INVALID_ENUM, "Invalid null effect float-vector property 0x%04x", param);
}
}
-void ALnull_getParami(const ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, ALint* UNUSED(val))
+void ALnull_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint* UNUSED(val))
{
switch(param)
{
- default:
- SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ default:
+ alSetError(context, AL_INVALID_ENUM, "Invalid null effect integer property 0x%04x", param);
}
}
-void ALnull_getParamiv(const ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, ALint* UNUSED(vals))
+void ALnull_getParamiv(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint* UNUSED(vals))
{
switch(param)
{
- default:
- SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ default:
+ alSetError(context, AL_INVALID_ENUM, "Invalid null effect integer-vector property 0x%04x", param);
}
}
-void ALnull_getParamf(const ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, ALfloat* UNUSED(val))
+void ALnull_getParamf(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALfloat* UNUSED(val))
{
switch(param)
{
- default:
- SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ default:
+ alSetError(context, AL_INVALID_ENUM, "Invalid null effect float property 0x%04x", param);
}
}
-void ALnull_getParamfv(const ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, ALfloat* UNUSED(vals))
+void ALnull_getParamfv(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALfloat* UNUSED(vals))
{
switch(param)
{
- default:
- SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ default:
+ alSetError(context, AL_INVALID_ENUM, "Invalid null effect float-vector property 0x%04x", param);
}
}
diff --git a/Alc/effects/pshifter.c b/Alc/effects/pshifter.c
new file mode 100644
index 00000000..ed18e9a8
--- /dev/null
+++ b/Alc/effects/pshifter.c
@@ -0,0 +1,441 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 2018 by Raul Herraiz.
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#include "config.h"
+
+#include <math.h>
+#include <stdlib.h>
+
+#include "alMain.h"
+#include "alAuxEffectSlot.h"
+#include "alError.h"
+#include "alu.h"
+#include "filters/defs.h"
+
+#include "alcomplex.h"
+
+
+#define STFT_SIZE 1024
+#define STFT_HALF_SIZE (STFT_SIZE>>1)
+#define OVERSAMP (1<<2)
+
+#define STFT_STEP (STFT_SIZE / OVERSAMP)
+#define FIFO_LATENCY (STFT_STEP * (OVERSAMP-1))
+
+
+typedef struct ALphasor {
+ ALdouble Amplitude;
+ ALdouble Phase;
+} ALphasor;
+
+typedef struct ALFrequencyDomain {
+ ALdouble Amplitude;
+ ALdouble Frequency;
+} ALfrequencyDomain;
+
+
+typedef struct ALpshifterState {
+ DERIVE_FROM_TYPE(ALeffectState);
+
+ /* Effect parameters */
+ ALsizei count;
+ ALsizei PitchShiftI;
+ ALfloat PitchShift;
+ ALfloat FreqPerBin;
+
+ /*Effects buffers*/
+ ALfloat InFIFO[STFT_SIZE];
+ ALfloat OutFIFO[STFT_STEP];
+ ALdouble LastPhase[STFT_HALF_SIZE+1];
+ ALdouble SumPhase[STFT_HALF_SIZE+1];
+ ALdouble OutputAccum[STFT_SIZE];
+
+ ALcomplex FFTbuffer[STFT_SIZE];
+
+ ALfrequencyDomain Analysis_buffer[STFT_HALF_SIZE+1];
+ ALfrequencyDomain Syntesis_buffer[STFT_HALF_SIZE+1];
+
+ alignas(16) ALfloat BufferOut[BUFFERSIZE];
+
+ /* Effect gains for each output channel */
+ ALfloat CurrentGains[MAX_OUTPUT_CHANNELS];
+ ALfloat TargetGains[MAX_OUTPUT_CHANNELS];
+} ALpshifterState;
+
+static ALvoid ALpshifterState_Destruct(ALpshifterState *state);
+static ALboolean ALpshifterState_deviceUpdate(ALpshifterState *state, ALCdevice *device);
+static ALvoid ALpshifterState_update(ALpshifterState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props);
+static ALvoid ALpshifterState_process(ALpshifterState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
+DECLARE_DEFAULT_ALLOCATORS(ALpshifterState)
+
+DEFINE_ALEFFECTSTATE_VTABLE(ALpshifterState);
+
+
+/* Define a Hann window, used to filter the STFT input and output. */
+alignas(16) static ALdouble HannWindow[STFT_SIZE];
+
+static void InitHannWindow(void)
+{
+ ALsizei i;
+
+ /* Create lookup table of the Hann window for the desired size, i.e. STFT_SIZE */
+ for(i = 0;i < STFT_SIZE>>1;i++)
+ {
+ ALdouble val = sin(M_PI * (ALdouble)i / (ALdouble)(STFT_SIZE-1));
+ HannWindow[i] = HannWindow[STFT_SIZE-1-i] = val * val;
+ }
+}
+static alonce_flag HannInitOnce = AL_ONCE_FLAG_INIT;
+
+
+static inline ALint double2int(ALdouble d)
+{
+#if ((defined(__GNUC__) || defined(__clang__)) && (defined(__i386__) || defined(__x86_64__)) && \
+ !defined(__SSE2_MATH__)) || (defined(_MSC_VER) && defined(_M_IX86_FP) && _M_IX86_FP < 2)
+ ALint sign, shift;
+ ALint64 mant;
+ union {
+ ALdouble d;
+ ALint64 i64;
+ } conv;
+
+ conv.d = d;
+ sign = (conv.i64>>63) | 1;
+ shift = ((conv.i64>>52)&0x7ff) - (1023+52);
+
+ /* Over/underflow */
+ if(UNLIKELY(shift >= 63 || shift < -52))
+ return 0;
+
+ mant = (conv.i64&I64(0xfffffffffffff)) | I64(0x10000000000000);
+ if(LIKELY(shift < 0))
+ return (ALint)(mant >> -shift) * sign;
+ return (ALint)(mant << shift) * sign;
+
+#else
+
+ return (ALint)d;
+#endif
+}
+
+
+/* Converts ALcomplex to ALphasor */
+static inline ALphasor rect2polar(ALcomplex number)
+{
+ ALphasor polar;
+
+ polar.Amplitude = sqrt(number.Real*number.Real + number.Imag*number.Imag);
+ polar.Phase = atan2(number.Imag, number.Real);
+
+ return polar;
+}
+
+/* Converts ALphasor to ALcomplex */
+static inline ALcomplex polar2rect(ALphasor number)
+{
+ ALcomplex cartesian;
+
+ cartesian.Real = number.Amplitude * cos(number.Phase);
+ cartesian.Imag = number.Amplitude * sin(number.Phase);
+
+ return cartesian;
+}
+
+
+static void ALpshifterState_Construct(ALpshifterState *state)
+{
+ ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
+ SET_VTABLE2(ALpshifterState, ALeffectState, state);
+
+ alcall_once(&HannInitOnce, InitHannWindow);
+}
+
+static ALvoid ALpshifterState_Destruct(ALpshifterState *state)
+{
+ ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
+}
+
+static ALboolean ALpshifterState_deviceUpdate(ALpshifterState *state, ALCdevice *device)
+{
+ /* (Re-)initializing parameters and clear the buffers. */
+ state->count = FIFO_LATENCY;
+ state->PitchShiftI = FRACTIONONE;
+ state->PitchShift = 1.0f;
+ state->FreqPerBin = device->Frequency / (ALfloat)STFT_SIZE;
+
+ memset(state->InFIFO, 0, sizeof(state->InFIFO));
+ memset(state->OutFIFO, 0, sizeof(state->OutFIFO));
+ memset(state->FFTbuffer, 0, sizeof(state->FFTbuffer));
+ memset(state->LastPhase, 0, sizeof(state->LastPhase));
+ memset(state->SumPhase, 0, sizeof(state->SumPhase));
+ memset(state->OutputAccum, 0, sizeof(state->OutputAccum));
+ memset(state->Analysis_buffer, 0, sizeof(state->Analysis_buffer));
+ memset(state->Syntesis_buffer, 0, sizeof(state->Syntesis_buffer));
+
+ memset(state->CurrentGains, 0, sizeof(state->CurrentGains));
+ memset(state->TargetGains, 0, sizeof(state->TargetGains));
+
+ return AL_TRUE;
+}
+
+static ALvoid ALpshifterState_update(ALpshifterState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props)
+{
+ const ALCdevice *device = context->Device;
+ ALfloat coeffs[MAX_AMBI_COEFFS];
+ float pitch;
+
+ pitch = powf(2.0f,
+ (ALfloat)(props->Pshifter.CoarseTune*100 + props->Pshifter.FineTune) / 1200.0f
+ );
+ state->PitchShiftI = fastf2i(pitch*FRACTIONONE);
+ state->PitchShift = state->PitchShiftI * (1.0f/FRACTIONONE);
+
+ CalcAngleCoeffs(0.0f, 0.0f, 0.0f, coeffs);
+ ComputePanGains(&device->Dry, coeffs, slot->Params.Gain, state->TargetGains);
+}
+
+static ALvoid ALpshifterState_process(ALpshifterState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
+{
+ /* Pitch shifter engine based on the work of Stephan Bernsee.
+ * http://blogs.zynaptiq.com/bernsee/pitch-shifting-using-the-ft/
+ */
+
+ static const ALdouble expected = M_PI*2.0 / OVERSAMP;
+ const ALdouble freq_per_bin = state->FreqPerBin;
+ ALfloat *restrict bufferOut = state->BufferOut;
+ ALsizei count = state->count;
+ ALsizei i, j, k;
+
+ for(i = 0;i < SamplesToDo;)
+ {
+ do {
+ /* Fill FIFO buffer with samples data */
+ state->InFIFO[count] = SamplesIn[0][i];
+ bufferOut[i] = state->OutFIFO[count - FIFO_LATENCY];
+
+ count++;
+ } while(++i < SamplesToDo && count < STFT_SIZE);
+
+ /* Check whether FIFO buffer is filled */
+ if(count < STFT_SIZE) break;
+ count = FIFO_LATENCY;
+
+ /* Real signal windowing and store in FFTbuffer */
+ for(k = 0;k < STFT_SIZE;k++)
+ {
+ state->FFTbuffer[k].Real = state->InFIFO[k] * HannWindow[k];
+ state->FFTbuffer[k].Imag = 0.0;
+ }
+
+ /* ANALYSIS */
+ /* Apply FFT to FFTbuffer data */
+ complex_fft(state->FFTbuffer, STFT_SIZE, -1.0);
+
+ /* Analyze the obtained data. Since the real FFT is symmetric, only
+ * STFT_HALF_SIZE+1 samples are needed.
+ */
+ for(k = 0;k < STFT_HALF_SIZE+1;k++)
+ {
+ ALphasor component;
+ ALdouble tmp;
+ ALint qpd;
+
+ /* Compute amplitude and phase */
+ component = rect2polar(state->FFTbuffer[k]);
+
+ /* Compute phase difference and subtract expected phase difference */
+ tmp = (component.Phase - state->LastPhase[k]) - k*expected;
+
+ /* Map delta phase into +/- Pi interval */
+ qpd = double2int(tmp / M_PI);
+ tmp -= M_PI * (qpd + (qpd%2));
+
+ /* Get deviation from bin frequency from the +/- Pi interval */
+ tmp /= expected;
+
+ /* Compute the k-th partials' true frequency, twice the amplitude
+ * for maintain the gain (because half of bins are used) and store
+ * amplitude and true frequency in analysis buffer.
+ */
+ state->Analysis_buffer[k].Amplitude = 2.0 * component.Amplitude;
+ state->Analysis_buffer[k].Frequency = (k + tmp) * freq_per_bin;
+
+ /* Store actual phase[k] for the calculations in the next frame*/
+ state->LastPhase[k] = component.Phase;
+ }
+
+ /* PROCESSING */
+ /* pitch shifting */
+ for(k = 0;k < STFT_HALF_SIZE+1;k++)
+ {
+ state->Syntesis_buffer[k].Amplitude = 0.0;
+ state->Syntesis_buffer[k].Frequency = 0.0;
+ }
+
+ for(k = 0;k < STFT_HALF_SIZE+1;k++)
+ {
+ j = (k*state->PitchShiftI) >> FRACTIONBITS;
+ if(j >= STFT_HALF_SIZE+1) break;
+
+ state->Syntesis_buffer[j].Amplitude += state->Analysis_buffer[k].Amplitude;
+ state->Syntesis_buffer[j].Frequency = state->Analysis_buffer[k].Frequency *
+ state->PitchShift;
+ }
+
+ /* SYNTHESIS */
+ /* Synthesis the processing data */
+ for(k = 0;k < STFT_HALF_SIZE+1;k++)
+ {
+ ALphasor component;
+ ALdouble tmp;
+
+ /* Compute bin deviation from scaled freq */
+ tmp = state->Syntesis_buffer[k].Frequency/freq_per_bin - k;
+
+ /* Calculate actual delta phase and accumulate it to get bin phase */
+ state->SumPhase[k] += (k + tmp) * expected;
+
+ component.Amplitude = state->Syntesis_buffer[k].Amplitude;
+ component.Phase = state->SumPhase[k];
+
+ /* Compute phasor component to cartesian complex number and storage it into FFTbuffer*/
+ state->FFTbuffer[k] = polar2rect(component);
+ }
+ /* zero negative frequencies for recontruct a real signal */
+ for(k = STFT_HALF_SIZE+1;k < STFT_SIZE;k++)
+ {
+ state->FFTbuffer[k].Real = 0.0;
+ state->FFTbuffer[k].Imag = 0.0;
+ }
+
+ /* Apply iFFT to buffer data */
+ complex_fft(state->FFTbuffer, STFT_SIZE, 1.0);
+
+ /* Windowing and add to output */
+ for(k = 0;k < STFT_SIZE;k++)
+ state->OutputAccum[k] += HannWindow[k] * state->FFTbuffer[k].Real /
+ (0.5 * STFT_HALF_SIZE * OVERSAMP);
+
+ /* Shift accumulator, input & output FIFO */
+ for(k = 0;k < STFT_STEP;k++) state->OutFIFO[k] = (ALfloat)state->OutputAccum[k];
+ for(j = 0;k < STFT_SIZE;k++,j++) state->OutputAccum[j] = state->OutputAccum[k];
+ for(;j < STFT_SIZE;j++) state->OutputAccum[j] = 0.0;
+ for(k = 0;k < FIFO_LATENCY;k++)
+ state->InFIFO[k] = state->InFIFO[k+STFT_STEP];
+ }
+ state->count = count;
+
+ /* Now, mix the processed sound data to the output. */
+ MixSamples(bufferOut, NumChannels, SamplesOut, state->CurrentGains, state->TargetGains,
+ maxi(SamplesToDo, 512), 0, SamplesToDo);
+}
+
+typedef struct PshifterStateFactory {
+ DERIVE_FROM_TYPE(EffectStateFactory);
+} PshifterStateFactory;
+
+static ALeffectState *PshifterStateFactory_create(PshifterStateFactory *UNUSED(factory))
+{
+ ALpshifterState *state;
+
+ NEW_OBJ0(state, ALpshifterState)();
+ if(!state) return NULL;
+
+ return STATIC_CAST(ALeffectState, state);
+}
+
+DEFINE_EFFECTSTATEFACTORY_VTABLE(PshifterStateFactory);
+
+EffectStateFactory *PshifterStateFactory_getFactory(void)
+{
+ static PshifterStateFactory PshifterFactory = { { GET_VTABLE2(PshifterStateFactory, EffectStateFactory) } };
+
+ return STATIC_CAST(EffectStateFactory, &PshifterFactory);
+}
+
+
+void ALpshifter_setParamf(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALfloat UNUSED(val))
+{
+ alSetError( context, AL_INVALID_ENUM, "Invalid pitch shifter float property 0x%04x", param );
+}
+
+void ALpshifter_setParamfv(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, const ALfloat *UNUSED(vals))
+{
+ alSetError( context, AL_INVALID_ENUM, "Invalid pitch shifter float-vector property 0x%04x", param );
+}
+
+void ALpshifter_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
+{
+ ALeffectProps *props = &effect->Props;
+ switch(param)
+ {
+ case AL_PITCH_SHIFTER_COARSE_TUNE:
+ if(!(val >= AL_PITCH_SHIFTER_MIN_COARSE_TUNE && val <= AL_PITCH_SHIFTER_MAX_COARSE_TUNE))
+ SETERR_RETURN(context, AL_INVALID_VALUE,,"Pitch shifter coarse tune out of range");
+ props->Pshifter.CoarseTune = val;
+ break;
+
+ case AL_PITCH_SHIFTER_FINE_TUNE:
+ if(!(val >= AL_PITCH_SHIFTER_MIN_FINE_TUNE && val <= AL_PITCH_SHIFTER_MAX_FINE_TUNE))
+ SETERR_RETURN(context, AL_INVALID_VALUE,,"Pitch shifter fine tune out of range");
+ props->Pshifter.FineTune = val;
+ break;
+
+ default:
+ alSetError(context, AL_INVALID_ENUM, "Invalid pitch shifter integer property 0x%04x", param);
+ }
+}
+void ALpshifter_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
+{
+ ALpshifter_setParami(effect, context, param, vals[0]);
+}
+
+void ALpshifter_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
+{
+ const ALeffectProps *props = &effect->Props;
+ switch(param)
+ {
+ case AL_PITCH_SHIFTER_COARSE_TUNE:
+ *val = (ALint)props->Pshifter.CoarseTune;
+ break;
+ case AL_PITCH_SHIFTER_FINE_TUNE:
+ *val = (ALint)props->Pshifter.FineTune;
+ break;
+
+ default:
+ alSetError(context, AL_INVALID_ENUM, "Invalid pitch shifter integer property 0x%04x", param);
+ }
+}
+void ALpshifter_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
+{
+ ALpshifter_getParami(effect, context, param, vals);
+}
+
+void ALpshifter_getParamf(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALfloat *UNUSED(val))
+{
+ alSetError(context, AL_INVALID_ENUM, "Invalid pitch shifter float property 0x%04x", param);
+}
+
+void ALpshifter_getParamfv(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALfloat *UNUSED(vals))
+{
+ alSetError(context, AL_INVALID_ENUM, "Invalid pitch shifter float vector-property 0x%04x", param);
+}
+
+DEFINE_ALEFFECT_VTABLE(ALpshifter);
diff --git a/Alc/effects/reverb.c b/Alc/effects/reverb.c
index e1013309..8ebc089e 100644
--- a/Alc/effects/reverb.c
+++ b/Alc/effects/reverb.c
@@ -1,6 +1,6 @@
/**
- * Reverb for the OpenAL cross platform audio library
- * Copyright (C) 2008-2009 by Christopher Fitzgerald.
+ * Ambisonic reverb engine for the OpenAL cross platform audio library
+ * Copyright (C) 2008-2017 by Chris Robinson and Christopher Fitzgerald.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
@@ -27,634 +27,464 @@
#include "alMain.h"
#include "alu.h"
#include "alAuxEffectSlot.h"
-#include "alEffect.h"
-#include "alFilter.h"
+#include "alListener.h"
#include "alError.h"
+#include "filters/defs.h"
+/* This is a user config option for modifying the overall output of the reverb
+ * effect.
+ */
+ALfloat ReverbBoost = 1.0f;
/* This is the maximum number of samples processed for each inner loop
* iteration. */
#define MAX_UPDATE_SAMPLES 256
-typedef struct DelayLine
-{
- // The delay lines use sample lengths that are powers of 2 to allow the
- // use of bit-masking instead of a modulus for wrapping.
- ALuint Mask;
- ALfloat *Line;
-} DelayLine;
+/* The number of samples used for cross-faded delay lines. This can be used
+ * to balance the compensation for abrupt line changes and attenuation due to
+ * minimally lengthed recursive lines. Try to keep this below the device
+ * update size.
+ */
+#define FADE_SAMPLES 128
-typedef struct ALreverbState {
- DERIVE_FROM_TYPE(ALeffectState);
+/* The number of spatialized lines or channels to process. Four channels allows
+ * for a 3D A-Format response. NOTE: This can't be changed without taking care
+ * of the conversion matrices, and a few places where the length arrays are
+ * assumed to have 4 elements.
+ */
+#define NUM_LINES 4
- ALboolean IsEax;
- // All delay lines are allocated as a single buffer to reduce memory
- // fragmentation and management code.
- ALfloat *SampleBuffer;
- ALuint TotalSamples;
+/* The B-Format to A-Format conversion matrix. The arrangement of rows is
+ * deliberately chosen to align the resulting lines to their spatial opposites
+ * (0:above front left <-> 3:above back right, 1:below front right <-> 2:below
+ * back left). It's not quite opposite, since the A-Format results in a
+ * tetrahedron, but it's close enough. Should the model be extended to 8-lines
+ * in the future, true opposites can be used.
+ */
+static const aluMatrixf B2A = {{
+ { 0.288675134595f, 0.288675134595f, 0.288675134595f, 0.288675134595f },
+ { 0.288675134595f, -0.288675134595f, -0.288675134595f, 0.288675134595f },
+ { 0.288675134595f, 0.288675134595f, -0.288675134595f, -0.288675134595f },
+ { 0.288675134595f, -0.288675134595f, 0.288675134595f, -0.288675134595f }
+}};
+
+/* Converts A-Format to B-Format. */
+static const aluMatrixf A2B = {{
+ { 0.866025403785f, 0.866025403785f, 0.866025403785f, 0.866025403785f },
+ { 0.866025403785f, -0.866025403785f, 0.866025403785f, -0.866025403785f },
+ { 0.866025403785f, -0.866025403785f, -0.866025403785f, 0.866025403785f },
+ { 0.866025403785f, 0.866025403785f, -0.866025403785f, -0.866025403785f }
+}};
+
+static const ALfloat FadeStep = 1.0f / FADE_SAMPLES;
+
+/* The all-pass and delay lines have a variable length dependent on the
+ * effect's density parameter, which helps alter the perceived environment
+ * size. The size-to-density conversion is a cubed scale:
+ *
+ * density = min(1.0, pow(size, 3.0) / DENSITY_SCALE);
+ *
+ * The line lengths scale linearly with room size, so the inverse density
+ * conversion is needed, taking the cube root of the re-scaled density to
+ * calculate the line length multiplier:
+ *
+ * length_mult = max(5.0, cbrtf(density*DENSITY_SCALE));
+ *
+ * The density scale below will result in a max line multiplier of 50, for an
+ * effective size range of 5m to 50m.
+ */
+static const ALfloat DENSITY_SCALE = 125000.0f;
- // Master effect filters
- ALfilterState LpFilter;
- ALfilterState HpFilter; // EAX only
+/* All delay line lengths are specified in seconds.
+ *
+ * To approximate early reflections, we break them up into primary (those
+ * arriving from the same direction as the source) and secondary (those
+ * arriving from the opposite direction).
+ *
+ * The early taps decorrelate the 4-channel signal to approximate an average
+ * room response for the primary reflections after the initial early delay.
+ *
+ * Given an average room dimension (d_a) and the speed of sound (c) we can
+ * calculate the average reflection delay (r_a) regardless of listener and
+ * source positions as:
+ *
+ * r_a = d_a / c
+ * c = 343.3
+ *
+ * This can extended to finding the average difference (r_d) between the
+ * maximum (r_1) and minimum (r_0) reflection delays:
+ *
+ * r_0 = 2 / 3 r_a
+ * = r_a - r_d / 2
+ * = r_d
+ * r_1 = 4 / 3 r_a
+ * = r_a + r_d / 2
+ * = 2 r_d
+ * r_d = 2 / 3 r_a
+ * = r_1 - r_0
+ *
+ * As can be determined by integrating the 1D model with a source (s) and
+ * listener (l) positioned across the dimension of length (d_a):
+ *
+ * r_d = int_(l=0)^d_a (int_(s=0)^d_a |2 d_a - 2 (l + s)| ds) dl / c
+ *
+ * The initial taps (T_(i=0)^N) are then specified by taking a power series
+ * that ranges between r_0 and half of r_1 less r_0:
+ *
+ * R_i = 2^(i / (2 N - 1)) r_d
+ * = r_0 + (2^(i / (2 N - 1)) - 1) r_d
+ * = r_0 + T_i
+ * T_i = R_i - r_0
+ * = (2^(i / (2 N - 1)) - 1) r_d
+ *
+ * Assuming an average of 1m, we get the following taps:
+ */
+static const ALfloat EARLY_TAP_LENGTHS[NUM_LINES] =
+{
+ 0.0000000e+0f, 2.0213520e-4f, 4.2531060e-4f, 6.7171600e-4f
+};
- struct {
- // Modulator delay line.
- DelayLine Delay;
-
- // The vibrato time is tracked with an index over a modulus-wrapped
- // range (in samples).
- ALuint Index;
- ALuint Range;
-
- // The depth of frequency change (also in samples) and its filter.
- ALfloat Depth;
- ALfloat Coeff;
- ALfloat Filter;
- } Mod;
-
- // Initial effect delay.
- DelayLine Delay;
- // The tap points for the initial delay. First tap goes to early
- // reflections, the last to late reverb.
- ALuint DelayTap[2];
+/* The early all-pass filter lengths are based on the early tap lengths:
+ *
+ * A_i = R_i / a
+ *
+ * Where a is the approximate maximum all-pass cycle limit (20).
+ */
+static const ALfloat EARLY_ALLPASS_LENGTHS[NUM_LINES] =
+{
+ 9.7096800e-5f, 1.0720356e-4f, 1.1836234e-4f, 1.3068260e-4f
+};
- struct {
- // Output gain for early reflections.
- ALfloat Gain;
+/* The early delay lines are used to transform the primary reflections into
+ * the secondary reflections. The A-format is arranged in such a way that
+ * the channels/lines are spatially opposite:
+ *
+ * C_i is opposite C_(N-i-1)
+ *
+ * The delays of the two opposing reflections (R_i and O_i) from a source
+ * anywhere along a particular dimension always sum to twice its full delay:
+ *
+ * 2 r_a = R_i + O_i
+ *
+ * With that in mind we can determine the delay between the two reflections
+ * and thus specify our early line lengths (L_(i=0)^N) using:
+ *
+ * O_i = 2 r_a - R_(N-i-1)
+ * L_i = O_i - R_(N-i-1)
+ * = 2 (r_a - R_(N-i-1))
+ * = 2 (r_a - T_(N-i-1) - r_0)
+ * = 2 r_a (1 - (2 / 3) 2^((N - i - 1) / (2 N - 1)))
+ *
+ * Using an average dimension of 1m, we get:
+ */
+static const ALfloat EARLY_LINE_LENGTHS[NUM_LINES] =
+{
+ 5.9850400e-4f, 1.0913150e-3f, 1.5376658e-3f, 1.9419362e-3f
+};
- // Early reflections are done with 4 delay lines.
- ALfloat Coeff[4];
- DelayLine Delay[4];
- ALuint Offset[4];
+/* The late all-pass filter lengths are based on the late line lengths:
+ *
+ * A_i = (5 / 3) L_i / r_1
+ */
+static const ALfloat LATE_ALLPASS_LENGTHS[NUM_LINES] =
+{
+ 1.6182800e-4f, 2.0389060e-4f, 2.8159360e-4f, 3.2365600e-4f
+};
- // The gain for each output channel based on 3D panning (only for the
- // EAX path).
- ALfloat PanGain[4][MAX_OUTPUT_CHANNELS];
- } Early;
+/* The late lines are used to approximate the decaying cycle of recursive
+ * late reflections.
+ *
+ * Splitting the lines in half, we start with the shortest reflection paths
+ * (L_(i=0)^(N/2)):
+ *
+ * L_i = 2^(i / (N - 1)) r_d
+ *
+ * Then for the opposite (longest) reflection paths (L_(i=N/2)^N):
+ *
+ * L_i = 2 r_a - L_(i-N/2)
+ * = 2 r_a - 2^((i - N / 2) / (N - 1)) r_d
+ *
+ * For our 1m average room, we get:
+ */
+static const ALfloat LATE_LINE_LENGTHS[NUM_LINES] =
+{
+ 1.9419362e-3f, 2.4466860e-3f, 3.3791220e-3f, 3.8838720e-3f
+};
- // Decorrelator delay line.
- DelayLine Decorrelator;
- // There are actually 4 decorrelator taps, but the first occurs at the
- // initial sample.
- ALuint DecoTap[3];
- struct {
- // Output gain for late reverb.
- ALfloat Gain;
+typedef struct DelayLineI {
+ /* The delay lines use interleaved samples, with the lengths being powers
+ * of 2 to allow the use of bit-masking instead of a modulus for wrapping.
+ */
+ ALsizei Mask;
+ ALfloat (*Line)[NUM_LINES];
+} DelayLineI;
+
+typedef struct VecAllpass {
+ DelayLineI Delay;
+ ALfloat Coeff;
+ ALsizei Offset[NUM_LINES][2];
+} VecAllpass;
+
+typedef struct T60Filter {
+ /* Two filters are used to adjust the signal. One to control the low
+ * frequencies, and one to control the high frequencies.
+ */
+ ALfloat MidGain[2];
+ BiquadFilter HFFilter, LFFilter;
+} T60Filter;
- // Attenuation to compensate for the modal density and decay rate of
- // the late lines.
- ALfloat DensityGain;
+typedef struct EarlyReflections {
+ /* A Gerzon vector all-pass filter is used to simulate initial diffusion.
+ * The spread from this filter also helps smooth out the reverb tail.
+ */
+ VecAllpass VecAp;
- // The feed-back and feed-forward all-pass coefficient.
- ALfloat ApFeedCoeff;
+ /* An echo line is used to complete the second half of the early
+ * reflections.
+ */
+ DelayLineI Delay;
+ ALsizei Offset[NUM_LINES][2];
+ ALfloat Coeff[NUM_LINES][2];
+
+ /* The gain for each output channel based on 3D panning. */
+ ALfloat CurrentGain[NUM_LINES][MAX_OUTPUT_CHANNELS];
+ ALfloat PanGain[NUM_LINES][MAX_OUTPUT_CHANNELS];
+} EarlyReflections;
+
+typedef struct LateReverb {
+ /* A recursive delay line is used fill in the reverb tail. */
+ DelayLineI Delay;
+ ALsizei Offset[NUM_LINES][2];
+
+ /* Attenuation to compensate for the modal density and decay rate of the
+ * late lines.
+ */
+ ALfloat DensityGain[2];
- // Mixing matrix coefficient.
- ALfloat MixCoeff;
+ /* T60 decay filters are used to simulate absorption. */
+ T60Filter T60[NUM_LINES];
- // Late reverb has 4 parallel all-pass filters.
- ALfloat ApCoeff[4];
- DelayLine ApDelay[4];
- ALuint ApOffset[4];
+ /* A Gerzon vector all-pass filter is used to simulate diffusion. */
+ VecAllpass VecAp;
- // In addition to 4 cyclical delay lines.
- ALfloat Coeff[4];
- DelayLine Delay[4];
- ALuint Offset[4];
+ /* The gain for each output channel based on 3D panning. */
+ ALfloat CurrentGain[NUM_LINES][MAX_OUTPUT_CHANNELS];
+ ALfloat PanGain[NUM_LINES][MAX_OUTPUT_CHANNELS];
+} LateReverb;
- // The cyclical delay lines are 1-pole low-pass filtered.
- ALfloat LpCoeff[4];
- ALfloat LpSample[4];
+typedef struct ReverbState {
+ DERIVE_FROM_TYPE(ALeffectState);
- // The gain for each output channel based on 3D panning (only for the
- // EAX path).
- ALfloat PanGain[4][MAX_OUTPUT_CHANNELS];
- } Late;
+ /* All delay lines are allocated as a single buffer to reduce memory
+ * fragmentation and management code.
+ */
+ ALfloat *SampleBuffer;
+ ALuint TotalSamples;
struct {
- // Attenuation to compensate for the modal density and decay rate of
- // the echo line.
- ALfloat DensityGain;
-
- // Echo delay and all-pass lines.
- DelayLine Delay;
- DelayLine ApDelay;
-
- ALfloat Coeff;
- ALfloat ApFeedCoeff;
- ALfloat ApCoeff;
-
- ALuint Offset;
- ALuint ApOffset;
-
- // The echo line is 1-pole low-pass filtered.
- ALfloat LpCoeff;
- ALfloat LpSample;
-
- // Echo mixing coefficient.
- ALfloat MixCoeff;
- } Echo;
-
- // The current read offset for all delay lines.
- ALuint Offset;
-
- // The gain for each output channel (non-EAX path only; aliased from
- // Late.PanGain)
- ALfloat (*Gain)[MAX_OUTPUT_CHANNELS];
+ /* Calculated parameters which indicate if cross-fading is needed after
+ * an update.
+ */
+ ALfloat Density, Diffusion;
+ ALfloat DecayTime, HFDecayTime, LFDecayTime;
+ ALfloat HFReference, LFReference;
+ } Params;
- /* Temporary storage used when processing. */
- ALfloat ReverbSamples[MAX_UPDATE_SAMPLES][4];
- ALfloat EarlySamples[MAX_UPDATE_SAMPLES][4];
-} ALreverbState;
+ /* Master effect filters */
+ struct {
+ BiquadFilter Lp;
+ BiquadFilter Hp;
+ } Filter[NUM_LINES];
-/* This is a user config option for modifying the overall output of the reverb
- * effect.
- */
-ALfloat ReverbBoost = 1.0f;
+ /* Core delay line (early reflections and late reverb tap from this). */
+ DelayLineI Delay;
-/* Specifies whether to use a standard reverb effect in place of EAX reverb */
-ALboolean EmulateEAXReverb = AL_FALSE;
+ /* Tap points for early reflection delay. */
+ ALsizei EarlyDelayTap[NUM_LINES][2];
+ ALfloat EarlyDelayCoeff[NUM_LINES][2];
-/* This coefficient is used to define the maximum frequency range controlled
- * by the modulation depth. The current value of 0.1 will allow it to swing
- * from 0.9x to 1.1x. This value must be below 1. At 1 it will cause the
- * sampler to stall on the downswing, and above 1 it will cause it to sample
- * backwards.
- */
-static const ALfloat MODULATION_DEPTH_COEFF = 0.1f;
+ /* Tap points for late reverb feed and delay. */
+ ALsizei LateFeedTap;
+ ALsizei LateDelayTap[NUM_LINES][2];
-/* A filter is used to avoid the terrible distortion caused by changing
- * modulation time and/or depth. To be consistent across different sample
- * rates, the coefficient must be raised to a constant divided by the sample
- * rate: coeff^(constant / rate).
- */
-static const ALfloat MODULATION_FILTER_COEFF = 0.048f;
-static const ALfloat MODULATION_FILTER_CONST = 100000.0f;
+ /* Coefficients for the all-pass and line scattering matrices. */
+ ALfloat MixX;
+ ALfloat MixY;
-// When diffusion is above 0, an all-pass filter is used to take the edge off
-// the echo effect. It uses the following line length (in seconds).
-static const ALfloat ECHO_ALLPASS_LENGTH = 0.0133f;
+ EarlyReflections Early;
-// Input into the late reverb is decorrelated between four channels. Their
-// timings are dependent on a fraction and multiplier. See the
-// UpdateDecorrelator() routine for the calculations involved.
-static const ALfloat DECO_FRACTION = 0.15f;
-static const ALfloat DECO_MULTIPLIER = 2.0f;
+ LateReverb Late;
-// All delay line lengths are specified in seconds.
+ /* Indicates the cross-fade point for delay line reads [0,FADE_SAMPLES]. */
+ ALsizei FadeCount;
-// The lengths of the early delay lines.
-static const ALfloat EARLY_LINE_LENGTH[4] =
-{
- 0.0015f, 0.0045f, 0.0135f, 0.0405f
-};
+ /* Maximum number of samples to process at once. */
+ ALsizei MaxUpdate[2];
-// The lengths of the late all-pass delay lines.
-static const ALfloat ALLPASS_LINE_LENGTH[4] =
-{
- 0.0151f, 0.0167f, 0.0183f, 0.0200f,
-};
+ /* The current write offset for all delay lines. */
+ ALsizei Offset;
-// The lengths of the late cyclical delay lines.
-static const ALfloat LATE_LINE_LENGTH[4] =
-{
- 0.0211f, 0.0311f, 0.0461f, 0.0680f
-};
+ /* Temporary storage used when processing. */
+ alignas(16) ALfloat TempSamples[NUM_LINES][MAX_UPDATE_SAMPLES];
+ alignas(16) ALfloat MixSamples[NUM_LINES][MAX_UPDATE_SAMPLES];
+} ReverbState;
-// The late cyclical delay lines have a variable length dependent on the
-// effect's density parameter (inverted for some reason) and this multiplier.
-static const ALfloat LATE_LINE_MULTIPLIER = 4.0f;
+static ALvoid ReverbState_Destruct(ReverbState *State);
+static ALboolean ReverbState_deviceUpdate(ReverbState *State, ALCdevice *Device);
+static ALvoid ReverbState_update(ReverbState *State, const ALCcontext *Context, const ALeffectslot *Slot, const ALeffectProps *props);
+static ALvoid ReverbState_process(ReverbState *State, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
+DECLARE_DEFAULT_ALLOCATORS(ReverbState)
+DEFINE_ALEFFECTSTATE_VTABLE(ReverbState);
-// Basic delay line input/output routines.
-static inline ALfloat DelayLineOut(DelayLine *Delay, ALuint offset)
+static void ReverbState_Construct(ReverbState *state)
{
- return Delay->Line[offset&Delay->Mask];
-}
+ ALsizei i, j;
-static inline ALvoid DelayLineIn(DelayLine *Delay, ALuint offset, ALfloat in)
-{
- Delay->Line[offset&Delay->Mask] = in;
-}
+ ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
+ SET_VTABLE2(ReverbState, ALeffectState, state);
-// Given an input sample, this function produces modulation for the late
-// reverb.
-static inline ALfloat EAXModulation(ALreverbState *State, ALuint offset, ALfloat in)
-{
- ALfloat sinus, frac, fdelay;
- ALfloat out0, out1;
- ALuint delay;
-
- // Calculate the sinus rythm (dependent on modulation time and the
- // sampling rate). The center of the sinus is moved to reduce the delay
- // of the effect when the time or depth are low.
- sinus = 1.0f - cosf(F_TAU * State->Mod.Index / State->Mod.Range);
-
- // Step the modulation index forward, keeping it bound to its range.
- State->Mod.Index = (State->Mod.Index + 1) % State->Mod.Range;
-
- // The depth determines the range over which to read the input samples
- // from, so it must be filtered to reduce the distortion caused by even
- // small parameter changes.
- State->Mod.Filter = lerp(State->Mod.Filter, State->Mod.Depth,
- State->Mod.Coeff);
-
- // Calculate the read offset and fraction between it and the next sample.
- frac = modff(State->Mod.Filter*sinus + 1.0f, &fdelay);
- delay = fastf2u(fdelay);
-
- // Get the two samples crossed by the offset, and feed the delay line
- // with the next input sample.
- out0 = DelayLineOut(&State->Mod.Delay, offset - delay);
- out1 = DelayLineOut(&State->Mod.Delay, offset - delay - 1);
- DelayLineIn(&State->Mod.Delay, offset, in);
-
- // The output is obtained by linearly interpolating the two samples that
- // were acquired above.
- return lerp(out0, out1, frac);
-}
+ state->TotalSamples = 0;
+ state->SampleBuffer = NULL;
-// Given some input sample, this function produces four-channel outputs for the
-// early reflections.
-static inline ALvoid EarlyReflection(ALreverbState *State, ALuint todo, ALfloat (*restrict out)[4])
-{
- ALfloat d[4], v, f[4];
- ALuint i;
+ state->Params.Density = AL_EAXREVERB_DEFAULT_DENSITY;
+ state->Params.Diffusion = AL_EAXREVERB_DEFAULT_DIFFUSION;
+ state->Params.DecayTime = AL_EAXREVERB_DEFAULT_DECAY_TIME;
+ state->Params.HFDecayTime = AL_EAXREVERB_DEFAULT_DECAY_TIME*AL_EAXREVERB_DEFAULT_DECAY_HFRATIO;
+ state->Params.LFDecayTime = AL_EAXREVERB_DEFAULT_DECAY_TIME*AL_EAXREVERB_DEFAULT_DECAY_LFRATIO;
+ state->Params.HFReference = AL_EAXREVERB_DEFAULT_HFREFERENCE;
+ state->Params.LFReference = AL_EAXREVERB_DEFAULT_LFREFERENCE;
- for(i = 0;i < todo;i++)
+ for(i = 0;i < NUM_LINES;i++)
{
- ALuint offset = State->Offset+i;
-
- // Obtain the decayed results of each early delay line.
- d[0] = DelayLineOut(&State->Early.Delay[0], offset-State->Early.Offset[0]) * State->Early.Coeff[0];
- d[1] = DelayLineOut(&State->Early.Delay[1], offset-State->Early.Offset[1]) * State->Early.Coeff[1];
- d[2] = DelayLineOut(&State->Early.Delay[2], offset-State->Early.Offset[2]) * State->Early.Coeff[2];
- d[3] = DelayLineOut(&State->Early.Delay[3], offset-State->Early.Offset[3]) * State->Early.Coeff[3];
-
- /* The following uses a lossless scattering junction from waveguide
- * theory. It actually amounts to a householder mixing matrix, which
- * will produce a maximally diffuse response, and means this can
- * probably be considered a simple feed-back delay network (FDN).
- * N
- * ---
- * \
- * v = 2/N / d_i
- * ---
- * i=1
- */
- v = (d[0] + d[1] + d[2] + d[3]) * 0.5f;
- // The junction is loaded with the input here.
- v += DelayLineOut(&State->Delay, offset-State->DelayTap[0]);
-
- // Calculate the feed values for the delay lines.
- f[0] = v - d[0];
- f[1] = v - d[1];
- f[2] = v - d[2];
- f[3] = v - d[3];
-
- // Re-feed the delay lines.
- DelayLineIn(&State->Early.Delay[0], offset, f[0]);
- DelayLineIn(&State->Early.Delay[1], offset, f[1]);
- DelayLineIn(&State->Early.Delay[2], offset, f[2]);
- DelayLineIn(&State->Early.Delay[3], offset, f[3]);
-
- // Output the results of the junction for all four channels.
- out[i][0] = State->Early.Gain * f[0];
- out[i][1] = State->Early.Gain * f[1];
- out[i][2] = State->Early.Gain * f[2];
- out[i][3] = State->Early.Gain * f[3];
+ BiquadFilter_clear(&state->Filter[i].Lp);
+ BiquadFilter_clear(&state->Filter[i].Hp);
}
-}
-
-// Basic attenuated all-pass input/output routine.
-static inline ALfloat AllpassInOut(DelayLine *Delay, ALuint outOffset, ALuint inOffset, ALfloat in, ALfloat feedCoeff, ALfloat coeff)
-{
- ALfloat out, feed;
-
- out = DelayLineOut(Delay, outOffset);
- feed = feedCoeff * in;
- DelayLineIn(Delay, inOffset, (feedCoeff * (out - feed)) + in);
-
- // The time-based attenuation is only applied to the delay output to
- // keep it from affecting the feed-back path (which is already controlled
- // by the all-pass feed coefficient).
- return (coeff * out) - feed;
-}
-// All-pass input/output routine for late reverb.
-static inline ALfloat LateAllPassInOut(ALreverbState *State, ALuint offset, ALuint index, ALfloat in)
-{
- return AllpassInOut(&State->Late.ApDelay[index],
- offset - State->Late.ApOffset[index],
- offset, in, State->Late.ApFeedCoeff,
- State->Late.ApCoeff[index]);
-}
-
-// Low-pass filter input/output routine for late reverb.
-static inline ALfloat LateLowPassInOut(ALreverbState *State, ALuint index, ALfloat in)
-{
- in = lerp(in, State->Late.LpSample[index], State->Late.LpCoeff[index]);
- State->Late.LpSample[index] = in;
- return in;
-}
-
-// Given four decorrelated input samples, this function produces four-channel
-// output for the late reverb.
-static inline ALvoid LateReverb(ALreverbState *State, ALuint todo, ALfloat (*restrict out)[4])
-{
- ALfloat d[4], f[4];
- ALuint i;
+ state->Delay.Mask = 0;
+ state->Delay.Line = NULL;
- for(i = 0;i < todo;i++)
+ for(i = 0;i < NUM_LINES;i++)
{
- ALuint offset = State->Offset+i;
-
- f[0] = DelayLineOut(&State->Decorrelator, offset);
- f[1] = DelayLineOut(&State->Decorrelator, offset-State->DecoTap[0]);
- f[2] = DelayLineOut(&State->Decorrelator, offset-State->DecoTap[1]);
- f[3] = DelayLineOut(&State->Decorrelator, offset-State->DecoTap[2]);
-
- // Obtain the decayed results of the cyclical delay lines, and add the
- // corresponding input channels. Then pass the results through the
- // low-pass filters.
- f[0] += DelayLineOut(&State->Late.Delay[0], offset-State->Late.Offset[0]) * State->Late.Coeff[0];
- f[1] += DelayLineOut(&State->Late.Delay[1], offset-State->Late.Offset[1]) * State->Late.Coeff[1];
- f[2] += DelayLineOut(&State->Late.Delay[2], offset-State->Late.Offset[2]) * State->Late.Coeff[2];
- f[3] += DelayLineOut(&State->Late.Delay[3], offset-State->Late.Offset[3]) * State->Late.Coeff[3];
-
- // This is where the feed-back cycles from line 0 to 1 to 3 to 2 and
- // back to 0.
- d[0] = LateLowPassInOut(State, 2, f[2]);
- d[1] = LateLowPassInOut(State, 0, f[0]);
- d[2] = LateLowPassInOut(State, 3, f[3]);
- d[3] = LateLowPassInOut(State, 1, f[1]);
-
- // To help increase diffusion, run each line through an all-pass filter.
- // When there is no diffusion, the shortest all-pass filter will feed
- // the shortest delay line.
- d[0] = LateAllPassInOut(State, offset, 0, d[0]);
- d[1] = LateAllPassInOut(State, offset, 1, d[1]);
- d[2] = LateAllPassInOut(State, offset, 2, d[2]);
- d[3] = LateAllPassInOut(State, offset, 3, d[3]);
-
- /* Late reverb is done with a modified feed-back delay network (FDN)
- * topology. Four input lines are each fed through their own all-pass
- * filter and then into the mixing matrix. The four outputs of the
- * mixing matrix are then cycled back to the inputs. Each output feeds
- * a different input to form a circlular feed cycle.
- *
- * The mixing matrix used is a 4D skew-symmetric rotation matrix
- * derived using a single unitary rotational parameter:
- *
- * [ d, a, b, c ] 1 = a^2 + b^2 + c^2 + d^2
- * [ -a, d, c, -b ]
- * [ -b, -c, d, a ]
- * [ -c, b, -a, d ]
- *
- * The rotation is constructed from the effect's diffusion parameter,
- * yielding: 1 = x^2 + 3 y^2; where a, b, and c are the coefficient y
- * with differing signs, and d is the coefficient x. The matrix is
- * thus:
- *
- * [ x, y, -y, y ] n = sqrt(matrix_order - 1)
- * [ -y, x, y, y ] t = diffusion_parameter * atan(n)
- * [ y, -y, x, y ] x = cos(t)
- * [ -y, -y, -y, x ] y = sin(t) / n
- *
- * To reduce the number of multiplies, the x coefficient is applied
- * with the cyclical delay line coefficients. Thus only the y
- * coefficient is applied when mixing, and is modified to be: y / x.
- */
- f[0] = d[0] + (State->Late.MixCoeff * ( d[1] + -d[2] + d[3]));
- f[1] = d[1] + (State->Late.MixCoeff * (-d[0] + d[2] + d[3]));
- f[2] = d[2] + (State->Late.MixCoeff * ( d[0] + -d[1] + d[3]));
- f[3] = d[3] + (State->Late.MixCoeff * (-d[0] + -d[1] + -d[2] ));
-
- // Output the results of the matrix for all four channels, attenuated by
- // the late reverb gain (which is attenuated by the 'x' mix coefficient).
- // Mix early reflections and late reverb.
- out[i][0] += State->Late.Gain * f[0];
- out[i][1] += State->Late.Gain * f[1];
- out[i][2] += State->Late.Gain * f[2];
- out[i][3] += State->Late.Gain * f[3];
-
- // Re-feed the cyclical delay lines.
- DelayLineIn(&State->Late.Delay[0], offset, f[0]);
- DelayLineIn(&State->Late.Delay[1], offset, f[1]);
- DelayLineIn(&State->Late.Delay[2], offset, f[2]);
- DelayLineIn(&State->Late.Delay[3], offset, f[3]);
+ state->EarlyDelayTap[i][0] = 0;
+ state->EarlyDelayTap[i][1] = 0;
+ state->EarlyDelayCoeff[i][0] = 0.0f;
+ state->EarlyDelayCoeff[i][1] = 0.0f;
}
-}
-// Given an input sample, this function mixes echo into the four-channel late
-// reverb.
-static inline ALvoid EAXEcho(ALreverbState *State, ALuint todo, ALfloat (*restrict late)[4])
-{
- ALfloat out, feed;
- ALuint i;
+ state->LateFeedTap = 0;
- for(i = 0;i < todo;i++)
+ for(i = 0;i < NUM_LINES;i++)
{
- ALuint offset = State->Offset+i;
-
- // Get the latest attenuated echo sample for output.
- feed = DelayLineOut(&State->Echo.Delay, offset-State->Echo.Offset) *
- State->Echo.Coeff;
-
- // Mix the output into the late reverb channels.
- out = State->Echo.MixCoeff * feed;
- late[i][0] += out;
- late[i][1] += out;
- late[i][2] += out;
- late[i][3] += out;
-
- // Mix the energy-attenuated input with the output and pass it through
- // the echo low-pass filter.
- feed += DelayLineOut(&State->Delay, offset-State->DelayTap[1]) *
- State->Echo.DensityGain;
- feed = lerp(feed, State->Echo.LpSample, State->Echo.LpCoeff);
- State->Echo.LpSample = feed;
-
- // Then the echo all-pass filter.
- feed = AllpassInOut(&State->Echo.ApDelay, offset-State->Echo.ApOffset,
- offset, feed, State->Echo.ApFeedCoeff,
- State->Echo.ApCoeff);
-
- // Feed the delay with the mixed and filtered sample.
- DelayLineIn(&State->Echo.Delay, offset, feed);
+ state->LateDelayTap[i][0] = 0;
+ state->LateDelayTap[i][1] = 0;
}
-}
-// Perform the non-EAX reverb pass on a given input sample, resulting in
-// four-channel output.
-static inline ALvoid VerbPass(ALreverbState *State, ALuint todo, const ALfloat *in, ALfloat (*restrict out)[4])
-{
- ALuint i;
+ state->MixX = 0.0f;
+ state->MixY = 0.0f;
- // Low-pass filter the incoming samples.
- for(i = 0;i < todo;i++)
- DelayLineIn(&State->Delay, State->Offset+i,
- ALfilterState_processSingle(&State->LpFilter, in[i])
- );
-
- // Calculate the early reflection from the first delay tap.
- EarlyReflection(State, todo, out);
-
- // Feed the decorrelator from the energy-attenuated output of the second
- // delay tap.
- for(i = 0;i < todo;i++)
+ state->Early.VecAp.Delay.Mask = 0;
+ state->Early.VecAp.Delay.Line = NULL;
+ state->Early.VecAp.Coeff = 0.0f;
+ state->Early.Delay.Mask = 0;
+ state->Early.Delay.Line = NULL;
+ for(i = 0;i < NUM_LINES;i++)
{
- ALuint offset = State->Offset+i;
- ALfloat sample = DelayLineOut(&State->Delay, offset - State->DelayTap[1]) *
- State->Late.DensityGain;
- DelayLineIn(&State->Decorrelator, offset, sample);
+ state->Early.VecAp.Offset[i][0] = 0;
+ state->Early.VecAp.Offset[i][1] = 0;
+ state->Early.Offset[i][0] = 0;
+ state->Early.Offset[i][1] = 0;
+ state->Early.Coeff[i][0] = 0.0f;
+ state->Early.Coeff[i][1] = 0.0f;
}
- // Calculate the late reverb from the decorrelator taps.
- LateReverb(State, todo, out);
-
- // Step all delays forward one sample.
- State->Offset += todo;
-}
-
-// Perform the EAX reverb pass on a given input sample, resulting in four-
-// channel output.
-static inline ALvoid EAXVerbPass(ALreverbState *State, ALuint todo, const ALfloat *input, ALfloat (*restrict early)[4], ALfloat (*restrict late)[4])
-{
- ALuint i;
-
- // Band-pass and modulate the incoming samples.
- for(i = 0;i < todo;i++)
+ state->Late.DensityGain[0] = 0.0f;
+ state->Late.DensityGain[1] = 0.0f;
+ state->Late.Delay.Mask = 0;
+ state->Late.Delay.Line = NULL;
+ state->Late.VecAp.Delay.Mask = 0;
+ state->Late.VecAp.Delay.Line = NULL;
+ state->Late.VecAp.Coeff = 0.0f;
+ for(i = 0;i < NUM_LINES;i++)
{
- ALfloat sample = input[i];
- sample = ALfilterState_processSingle(&State->LpFilter, sample);
- sample = ALfilterState_processSingle(&State->HpFilter, sample);
+ state->Late.Offset[i][0] = 0;
+ state->Late.Offset[i][1] = 0;
- // Perform any modulation on the input.
- sample = EAXModulation(State, State->Offset+i, sample);
+ state->Late.VecAp.Offset[i][0] = 0;
+ state->Late.VecAp.Offset[i][1] = 0;
- // Feed the initial delay line.
- DelayLineIn(&State->Delay, State->Offset+i, sample);
+ state->Late.T60[i].MidGain[0] = 0.0f;
+ state->Late.T60[i].MidGain[1] = 0.0f;
+ BiquadFilter_clear(&state->Late.T60[i].HFFilter);
+ BiquadFilter_clear(&state->Late.T60[i].LFFilter);
}
- // Calculate the early reflection from the first delay tap.
- EarlyReflection(State, todo, early);
-
- // Feed the decorrelator from the energy-attenuated output of the second
- // delay tap.
- for(i = 0;i < todo;i++)
- {
- ALuint offset = State->Offset+i;
- ALfloat sample = DelayLineOut(&State->Delay, offset - State->DelayTap[1]) *
- State->Late.DensityGain;
- DelayLineIn(&State->Decorrelator, offset, sample);
- }
-
- // Calculate the late reverb from the decorrelator taps.
- memset(late, 0, sizeof(*late)*todo);
- LateReverb(State, todo, late);
-
- // Calculate and mix in any echo.
- EAXEcho(State, todo, late);
-
- // Step all delays forward.
- State->Offset += todo;
-}
-
-static ALvoid ALreverbState_processStandard(ALreverbState *State, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels)
-{
- ALfloat (*restrict out)[4] = State->ReverbSamples;
- ALuint index, c, i, l;
-
- /* Process reverb for these samples. */
- for(index = 0;index < SamplesToDo;)
+ for(i = 0;i < NUM_LINES;i++)
{
- ALuint todo = minu(SamplesToDo-index, MAX_UPDATE_SAMPLES);
-
- VerbPass(State, todo, &SamplesIn[index], out);
-
- for(l = 0;l < 4;l++)
+ for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
{
- for(c = 0;c < NumChannels;c++)
- {
- ALfloat gain = State->Gain[l][c];
- if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
- continue;
- for(i = 0;i < todo;i++)
- SamplesOut[c][index+i] += gain*out[i][l];
- }
+ state->Early.CurrentGain[i][j] = 0.0f;
+ state->Early.PanGain[i][j] = 0.0f;
+ state->Late.CurrentGain[i][j] = 0.0f;
+ state->Late.PanGain[i][j] = 0.0f;
}
-
- index += todo;
}
+
+ state->FadeCount = 0;
+ state->MaxUpdate[0] = MAX_UPDATE_SAMPLES;
+ state->MaxUpdate[1] = MAX_UPDATE_SAMPLES;
+ state->Offset = 0;
}
-static ALvoid ALreverbState_processEax(ALreverbState *State, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels)
+static ALvoid ReverbState_Destruct(ReverbState *State)
{
- ALfloat (*restrict early)[4] = State->EarlySamples;
- ALfloat (*restrict late)[4] = State->ReverbSamples;
- ALuint index, c, i, l;
- ALfloat gain;
-
- /* Process reverb for these samples. */
- for(index = 0;index < SamplesToDo;)
- {
- ALuint todo = minu(SamplesToDo-index, MAX_UPDATE_SAMPLES);
-
- EAXVerbPass(State, todo, &SamplesIn[index], early, late);
-
- for(l = 0;l < 4;l++)
- {
- for(c = 0;c < NumChannels;c++)
- {
- gain = State->Early.PanGain[l][c];
- if(fabsf(gain) > GAIN_SILENCE_THRESHOLD)
- {
- for(i = 0;i < todo;i++)
- SamplesOut[c][index+i] += gain*early[i][l];
- }
- gain = State->Late.PanGain[l][c];
- if(fabsf(gain) > GAIN_SILENCE_THRESHOLD)
- {
- for(i = 0;i < todo;i++)
- SamplesOut[c][index+i] += gain*late[i][l];
- }
- }
- }
+ al_free(State->SampleBuffer);
+ State->SampleBuffer = NULL;
- index += todo;
- }
+ ALeffectState_Destruct(STATIC_CAST(ALeffectState,State));
}
-static ALvoid ALreverbState_process(ALreverbState *State, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels)
+/**************************************
+ * Device Update *
+ **************************************/
+
+static inline ALfloat CalcDelayLengthMult(ALfloat density)
{
- if(State->IsEax)
- ALreverbState_processEax(State, SamplesToDo, SamplesIn, SamplesOut, NumChannels);
- else
- ALreverbState_processStandard(State, SamplesToDo, SamplesIn, SamplesOut, NumChannels);
+ return maxf(5.0f, cbrtf(density*DENSITY_SCALE));
}
-// Given the allocated sample buffer, this function updates each delay line
-// offset.
-static inline ALvoid RealizeLineOffset(ALfloat *sampleBuffer, DelayLine *Delay)
+/* Given the allocated sample buffer, this function updates each delay line
+ * offset.
+ */
+static inline ALvoid RealizeLineOffset(ALfloat *sampleBuffer, DelayLineI *Delay)
{
- Delay->Line = &sampleBuffer[(ptrdiff_t)Delay->Line];
+ union {
+ ALfloat *f;
+ ALfloat (*f4)[NUM_LINES];
+ } u;
+ u.f = &sampleBuffer[(ptrdiff_t)Delay->Line * NUM_LINES];
+ Delay->Line = u.f4;
}
-// Calculate the length of a delay line and store its mask and offset.
-static ALuint CalcLineLength(ALfloat length, ptrdiff_t offset, ALuint frequency, ALuint extra, DelayLine *Delay)
+/* Calculate the length of a delay line and store its mask and offset. */
+static ALuint CalcLineLength(const ALfloat length, const ptrdiff_t offset, const ALuint frequency,
+ const ALuint extra, DelayLineI *Delay)
{
ALuint samples;
- // All line lengths are powers of 2, calculated from their lengths, with
- // an additional sample in case of rounding errors.
- samples = fastf2u(length*frequency) + extra;
- samples = NextPowerOf2(samples + 1);
- // All lines share a single sample buffer.
+ /* All line lengths are powers of 2, calculated from their lengths in
+ * seconds, rounded up.
+ */
+ samples = float2int(ceilf(length*frequency));
+ samples = NextPowerOf2(samples + extra);
+
+ /* All lines share a single sample buffer. */
Delay->Mask = samples - 1;
- Delay->Line = (ALfloat*)offset;
- // Return the sample count for accumulation.
+ Delay->Line = (ALfloat(*)[NUM_LINES])offset;
+
+ /* Return the sample count for accumulation. */
return samples;
}
@@ -662,144 +492,173 @@ static ALuint CalcLineLength(ALfloat length, ptrdiff_t offset, ALuint frequency,
* for all lines given the sample rate (frequency). If an allocation failure
* occurs, it returns AL_FALSE.
*/
-static ALboolean AllocLines(ALuint frequency, ALreverbState *State)
+static ALboolean AllocLines(const ALuint frequency, ReverbState *State)
{
- ALuint totalSamples, index;
- ALfloat length;
- ALfloat *newBuffer = NULL;
+ ALuint totalSamples, i;
+ ALfloat multiplier, length;
- // All delay line lengths are calculated to accomodate the full range of
- // lengths given their respective paramters.
+ /* All delay line lengths are calculated to accomodate the full range of
+ * lengths given their respective paramters.
+ */
totalSamples = 0;
- /* The modulator's line length is calculated from the maximum modulation
- * time and depth coefficient, and halfed for the low-to-high frequency
- * swing. An additional sample is added to keep it stable when there is no
- * modulation.
+ /* Multiplier for the maximum density value, i.e. density=1, which is
+ * actually the least density...
*/
- length = (AL_EAXREVERB_MAX_MODULATION_TIME*MODULATION_DEPTH_COEFF/2.0f);
- totalSamples += CalcLineLength(length, totalSamples, frequency, 1,
- &State->Mod.Delay);
-
- // The initial delay is the sum of the reflections and late reverb
- // delays. This must include space for storing a loop update to feed the
- // early reflections, decorrelator, and echo.
- length = AL_EAXREVERB_MAX_REFLECTIONS_DELAY +
- AL_EAXREVERB_MAX_LATE_REVERB_DELAY;
- totalSamples += CalcLineLength(length, totalSamples, frequency,
- MAX_UPDATE_SAMPLES, &State->Delay);
-
- // The early reflection lines.
- for(index = 0;index < 4;index++)
- totalSamples += CalcLineLength(EARLY_LINE_LENGTH[index], totalSamples,
- frequency, 0, &State->Early.Delay[index]);
-
- // The decorrelator line is calculated from the lowest reverb density (a
- // parameter value of 1). This must include space for storing a loop update
- // to feed the late reverb.
- length = (DECO_FRACTION * DECO_MULTIPLIER * DECO_MULTIPLIER) *
- LATE_LINE_LENGTH[0] * (1.0f + LATE_LINE_MULTIPLIER);
+ multiplier = CalcDelayLengthMult(AL_EAXREVERB_MAX_DENSITY);
+
+ /* The main delay length includes the maximum early reflection delay, the
+ * largest early tap width, the maximum late reverb delay, and the
+ * largest late tap width. Finally, it must also be extended by the
+ * update size (MAX_UPDATE_SAMPLES) for block processing.
+ */
+ length = AL_EAXREVERB_MAX_REFLECTIONS_DELAY + EARLY_TAP_LENGTHS[NUM_LINES-1]*multiplier +
+ AL_EAXREVERB_MAX_LATE_REVERB_DELAY +
+ (LATE_LINE_LENGTHS[NUM_LINES-1] - LATE_LINE_LENGTHS[0])*0.25f*multiplier;
totalSamples += CalcLineLength(length, totalSamples, frequency, MAX_UPDATE_SAMPLES,
- &State->Decorrelator);
+ &State->Delay);
- // The late all-pass lines.
- for(index = 0;index < 4;index++)
- totalSamples += CalcLineLength(ALLPASS_LINE_LENGTH[index], totalSamples,
- frequency, 0, &State->Late.ApDelay[index]);
+ /* The early vector all-pass line. */
+ length = EARLY_ALLPASS_LENGTHS[NUM_LINES-1] * multiplier;
+ totalSamples += CalcLineLength(length, totalSamples, frequency, 0,
+ &State->Early.VecAp.Delay);
- // The late delay lines are calculated from the lowest reverb density.
- for(index = 0;index < 4;index++)
- {
- length = LATE_LINE_LENGTH[index] * (1.0f + LATE_LINE_MULTIPLIER);
- totalSamples += CalcLineLength(length, totalSamples, frequency, 0,
- &State->Late.Delay[index]);
- }
+ /* The early reflection line. */
+ length = EARLY_LINE_LENGTHS[NUM_LINES-1] * multiplier;
+ totalSamples += CalcLineLength(length, totalSamples, frequency, 0,
+ &State->Early.Delay);
+
+ /* The late vector all-pass line. */
+ length = LATE_ALLPASS_LENGTHS[NUM_LINES-1] * multiplier;
+ totalSamples += CalcLineLength(length, totalSamples, frequency, 0,
+ &State->Late.VecAp.Delay);
- // The echo all-pass and delay lines.
- totalSamples += CalcLineLength(ECHO_ALLPASS_LENGTH, totalSamples,
- frequency, 0, &State->Echo.ApDelay);
- totalSamples += CalcLineLength(AL_EAXREVERB_MAX_ECHO_TIME, totalSamples,
- frequency, 0, &State->Echo.Delay);
+ /* The late delay lines are calculated from the largest maximum density
+ * line length.
+ */
+ length = LATE_LINE_LENGTHS[NUM_LINES-1] * multiplier;
+ totalSamples += CalcLineLength(length, totalSamples, frequency, 0,
+ &State->Late.Delay);
if(totalSamples != State->TotalSamples)
{
- TRACE("New reverb buffer length: %u samples (%f sec)\n", totalSamples, totalSamples/(float)frequency);
- newBuffer = realloc(State->SampleBuffer, sizeof(ALfloat) * totalSamples);
- if(newBuffer == NULL)
- return AL_FALSE;
+ ALfloat *newBuffer;
+
+ TRACE("New reverb buffer length: %ux4 samples\n", totalSamples);
+ newBuffer = al_calloc(16, sizeof(ALfloat[NUM_LINES]) * totalSamples);
+ if(!newBuffer) return AL_FALSE;
+
+ al_free(State->SampleBuffer);
State->SampleBuffer = newBuffer;
State->TotalSamples = totalSamples;
}
- // Update all delays to reflect the new sample buffer.
+ /* Update all delays to reflect the new sample buffer. */
RealizeLineOffset(State->SampleBuffer, &State->Delay);
- RealizeLineOffset(State->SampleBuffer, &State->Decorrelator);
- for(index = 0;index < 4;index++)
- {
- RealizeLineOffset(State->SampleBuffer, &State->Early.Delay[index]);
- RealizeLineOffset(State->SampleBuffer, &State->Late.ApDelay[index]);
- RealizeLineOffset(State->SampleBuffer, &State->Late.Delay[index]);
- }
- RealizeLineOffset(State->SampleBuffer, &State->Mod.Delay);
- RealizeLineOffset(State->SampleBuffer, &State->Echo.ApDelay);
- RealizeLineOffset(State->SampleBuffer, &State->Echo.Delay);
+ RealizeLineOffset(State->SampleBuffer, &State->Early.VecAp.Delay);
+ RealizeLineOffset(State->SampleBuffer, &State->Early.Delay);
+ RealizeLineOffset(State->SampleBuffer, &State->Late.VecAp.Delay);
+ RealizeLineOffset(State->SampleBuffer, &State->Late.Delay);
- // Clear the sample buffer.
- for(index = 0;index < State->TotalSamples;index++)
- State->SampleBuffer[index] = 0.0f;
+ /* Clear the sample buffer. */
+ for(i = 0;i < State->TotalSamples;i++)
+ State->SampleBuffer[i] = 0.0f;
return AL_TRUE;
}
-static ALboolean ALreverbState_deviceUpdate(ALreverbState *State, ALCdevice *Device)
+static ALboolean ReverbState_deviceUpdate(ReverbState *State, ALCdevice *Device)
{
- ALuint frequency = Device->Frequency, index;
+ ALuint frequency = Device->Frequency;
+ ALfloat multiplier;
+ ALsizei i, j;
- // Allocate the delay lines.
+ /* Allocate the delay lines. */
if(!AllocLines(frequency, State))
return AL_FALSE;
- // Calculate the modulation filter coefficient. Notice that the exponent
- // is calculated given the current sample rate. This ensures that the
- // resulting filter response over time is consistent across all sample
- // rates.
- State->Mod.Coeff = powf(MODULATION_FILTER_COEFF,
- MODULATION_FILTER_CONST / frequency);
+ multiplier = CalcDelayLengthMult(AL_EAXREVERB_MAX_DENSITY);
+
+ /* The late feed taps are set a fixed position past the latest delay tap. */
+ State->LateFeedTap = float2int((AL_EAXREVERB_MAX_REFLECTIONS_DELAY +
+ EARLY_TAP_LENGTHS[NUM_LINES-1]*multiplier) *
+ frequency);
- // The early reflection and late all-pass filter line lengths are static,
- // so their offsets only need to be calculated once.
- for(index = 0;index < 4;index++)
+ /* Clear filters and gain coefficients since the delay lines were all just
+ * cleared (if not reallocated).
+ */
+ for(i = 0;i < NUM_LINES;i++)
{
- State->Early.Offset[index] = fastf2u(EARLY_LINE_LENGTH[index] *
- frequency);
- State->Late.ApOffset[index] = fastf2u(ALLPASS_LINE_LENGTH[index] *
- frequency);
+ BiquadFilter_clear(&State->Filter[i].Lp);
+ BiquadFilter_clear(&State->Filter[i].Hp);
}
- // The echo all-pass filter line length is static, so its offset only
- // needs to be calculated once.
- State->Echo.ApOffset = fastf2u(ECHO_ALLPASS_LENGTH * frequency);
+ for(i = 0;i < NUM_LINES;i++)
+ {
+ State->EarlyDelayCoeff[i][0] = 0.0f;
+ State->EarlyDelayCoeff[i][1] = 0.0f;
+ }
+
+ for(i = 0;i < NUM_LINES;i++)
+ {
+ State->Early.Coeff[i][0] = 0.0f;
+ State->Early.Coeff[i][1] = 0.0f;
+ }
+
+ State->Late.DensityGain[0] = 0.0f;
+ State->Late.DensityGain[1] = 0.0f;
+ for(i = 0;i < NUM_LINES;i++)
+ {
+ State->Late.T60[i].MidGain[0] = 0.0f;
+ State->Late.T60[i].MidGain[1] = 0.0f;
+ BiquadFilter_clear(&State->Late.T60[i].HFFilter);
+ BiquadFilter_clear(&State->Late.T60[i].LFFilter);
+ }
+
+ for(i = 0;i < NUM_LINES;i++)
+ {
+ for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
+ {
+ State->Early.CurrentGain[i][j] = 0.0f;
+ State->Early.PanGain[i][j] = 0.0f;
+ State->Late.CurrentGain[i][j] = 0.0f;
+ State->Late.PanGain[i][j] = 0.0f;
+ }
+ }
+
+ /* Reset counters and offset base. */
+ State->FadeCount = 0;
+ State->MaxUpdate[0] = MAX_UPDATE_SAMPLES;
+ State->MaxUpdate[1] = MAX_UPDATE_SAMPLES;
+ State->Offset = 0;
return AL_TRUE;
}
-// Calculate a decay coefficient given the length of each cycle and the time
-// until the decay reaches -60 dB.
-static inline ALfloat CalcDecayCoeff(ALfloat length, ALfloat decayTime)
+/**************************************
+ * Effect Update *
+ **************************************/
+
+/* Calculate a decay coefficient given the length of each cycle and the time
+ * until the decay reaches -60 dB.
+ */
+static inline ALfloat CalcDecayCoeff(const ALfloat length, const ALfloat decayTime)
{
- return powf(0.001f/*-60 dB*/, length/decayTime);
+ return powf(REVERB_DECAY_GAIN, length/decayTime);
}
-// Calculate a decay length from a coefficient and the time until the decay
-// reaches -60 dB.
-static inline ALfloat CalcDecayLength(ALfloat coeff, ALfloat decayTime)
+/* Calculate a decay length from a coefficient and the time until the decay
+ * reaches -60 dB.
+ */
+static inline ALfloat CalcDecayLength(const ALfloat coeff, const ALfloat decayTime)
{
- return log10f(coeff) * decayTime / log10f(0.001f)/*-60 dB*/;
+ return log10f(coeff) * decayTime / log10f(REVERB_DECAY_GAIN);
}
-// Calculate an attenuation to be applied to the input of any echo models to
-// compensate for modal density and decay time.
-static inline ALfloat CalcDensityGain(ALfloat a)
+/* Calculate an attenuation to be applied to the input of any echo models to
+ * compensate for modal density and decay time.
+ */
+static inline ALfloat CalcDensityGain(const ALfloat a)
{
/* The energy of a signal can be obtained by finding the area under the
* squared signal. This takes the form of Sum(x_n^2), where x is the
@@ -809,32 +668,34 @@ static inline ALfloat CalcDensityGain(ALfloat a)
* where a is the attenuation coefficient, and n is the sample. The area
* under this decay curve can be calculated as: 1 / (1 - a).
*
- * Modifying the above equation to find the squared area under the curve
+ * Modifying the above equation to find the area under the squared curve
* (for energy) yields: 1 / (1 - a^2). Input attenuation can then be
* calculated by inverting the square root of this approximation,
* yielding: 1 / sqrt(1 / (1 - a^2)), simplified to: sqrt(1 - a^2).
*/
- return sqrtf(1.0f - (a * a));
+ return sqrtf(1.0f - a*a);
}
-// Calculate the mixing matrix coefficients given a diffusion factor.
-static inline ALvoid CalcMatrixCoeffs(ALfloat diffusion, ALfloat *x, ALfloat *y)
+/* Calculate the scattering matrix coefficients given a diffusion factor. */
+static inline ALvoid CalcMatrixCoeffs(const ALfloat diffusion, ALfloat *x, ALfloat *y)
{
ALfloat n, t;
- // The matrix is of order 4, so n is sqrt (4 - 1).
+ /* The matrix is of order 4, so n is sqrt(4 - 1). */
n = sqrtf(3.0f);
t = diffusion * atanf(n);
- // Calculate the first mixing matrix coefficient.
+ /* Calculate the first mixing matrix coefficient. */
*x = cosf(t);
- // Calculate the second mixing matrix coefficient.
+ /* Calculate the second mixing matrix coefficient. */
*y = sinf(t) / n;
}
-// Calculate the limited HF ratio for use with the late reverb low-pass
-// filters.
-static ALfloat CalcLimitedHfRatio(ALfloat hfRatio, ALfloat airAbsorptionGainHF, ALfloat decayTime)
+/* Calculate the limited HF ratio for use with the late reverb low-pass
+ * filters.
+ */
+static ALfloat CalcLimitedHfRatio(const ALfloat hfRatio, const ALfloat airAbsorptionGainHF,
+ const ALfloat decayTime, const ALfloat SpeedOfSound)
{
ALfloat limitRatio;
@@ -843,464 +704,906 @@ static ALfloat CalcLimitedHfRatio(ALfloat hfRatio, ALfloat airAbsorptionGainHF,
* equation, solve for HF ratio. The delay length is cancelled out of
* the equation, so it can be calculated once for all lines.
*/
- limitRatio = 1.0f / (CalcDecayLength(airAbsorptionGainHF, decayTime) *
- SPEEDOFSOUNDMETRESPERSEC);
- /* Using the limit calculated above, apply the upper bound to the HF
- * ratio. Also need to limit the result to a minimum of 0.1, just like the
- * HF ratio parameter. */
- return clampf(limitRatio, 0.1f, hfRatio);
-}
+ limitRatio = 1.0f / (CalcDecayLength(airAbsorptionGainHF, decayTime) * SpeedOfSound);
-// Calculate the coefficient for a HF (and eventually LF) decay damping
-// filter.
-static inline ALfloat CalcDampingCoeff(ALfloat hfRatio, ALfloat length, ALfloat decayTime, ALfloat decayCoeff, ALfloat cw)
-{
- ALfloat coeff, g;
+ /* Using the limit calculated above, apply the upper bound to the HF ratio.
+ */
+ return minf(limitRatio, hfRatio);
+}
- // Eventually this should boost the high frequencies when the ratio
- // exceeds 1.
- coeff = 0.0f;
- if (hfRatio < 1.0f)
- {
- // Calculate the low-pass coefficient by dividing the HF decay
- // coefficient by the full decay coefficient.
- g = CalcDecayCoeff(length, decayTime * hfRatio) / decayCoeff;
- // Damping is done with a 1-pole filter, so g needs to be squared.
- g *= g;
- if(g < 0.9999f) /* 1-epsilon */
- {
- /* Be careful with gains < 0.001, as that causes the coefficient
- * head towards 1, which will flatten the signal. */
- g = maxf(g, 0.001f);
- coeff = (1 - g*cw - sqrtf(2*g*(1-cw) - g*g*(1 - cw*cw))) /
- (1 - g);
- }
-
- // Very low decay times will produce minimal output, so apply an
- // upper bound to the coefficient.
- coeff = minf(coeff, 0.98f);
- }
- return coeff;
+/* Calculates the 3-band T60 damping coefficients for a particular delay line
+ * of specified length, using a combination of two shelf filter sections given
+ * decay times for each band split at two reference frequencies.
+ */
+static void CalcT60DampingCoeffs(const ALfloat length, const ALfloat lfDecayTime,
+ const ALfloat mfDecayTime, const ALfloat hfDecayTime,
+ const ALfloat lf0norm, const ALfloat hf0norm,
+ T60Filter *filter)
+{
+ ALfloat lfGain = CalcDecayCoeff(length, lfDecayTime);
+ ALfloat mfGain = CalcDecayCoeff(length, mfDecayTime);
+ ALfloat hfGain = CalcDecayCoeff(length, hfDecayTime);
+
+ filter->MidGain[1] = mfGain;
+ BiquadFilter_setParams(&filter->LFFilter, BiquadType_LowShelf, lfGain/mfGain, lf0norm,
+ calc_rcpQ_from_slope(lfGain/mfGain, 1.0f));
+ BiquadFilter_setParams(&filter->HFFilter, BiquadType_HighShelf, hfGain/mfGain, hf0norm,
+ calc_rcpQ_from_slope(hfGain/mfGain, 1.0f));
}
-// Update the EAX modulation index, range, and depth. Keep in mind that this
-// kind of vibrato is additive and not multiplicative as one may expect. The
-// downswing will sound stronger than the upswing.
-static ALvoid UpdateModulator(ALfloat modTime, ALfloat modDepth, ALuint frequency, ALreverbState *State)
+/* Update the offsets for the main effect delay line. */
+static ALvoid UpdateDelayLine(const ALfloat earlyDelay, const ALfloat lateDelay, const ALfloat density, const ALfloat decayTime, const ALuint frequency, ReverbState *State)
{
- ALuint range;
+ ALfloat multiplier, length;
+ ALuint i;
- /* Modulation is calculated in two parts.
+ multiplier = CalcDelayLengthMult(density);
+
+ /* Early reflection taps are decorrelated by means of an average room
+ * reflection approximation described above the definition of the taps.
+ * This approximation is linear and so the above density multiplier can
+ * be applied to adjust the width of the taps. A single-band decay
+ * coefficient is applied to simulate initial attenuation and absorption.
*
- * The modulation time effects the sinus applied to the change in
- * frequency. An index out of the current time range (both in samples)
- * is incremented each sample. The range is bound to a reasonable
- * minimum (1 sample) and when the timing changes, the index is rescaled
- * to the new range (to keep the sinus consistent).
- */
- range = maxu(fastf2u(modTime*frequency), 1);
- State->Mod.Index = (ALuint)(State->Mod.Index * (ALuint64)range /
- State->Mod.Range);
- State->Mod.Range = range;
-
- /* The modulation depth effects the amount of frequency change over the
- * range of the sinus. It needs to be scaled by the modulation time so
- * that a given depth produces a consistent change in frequency over all
- * ranges of time. Since the depth is applied to a sinus value, it needs
- * to be halfed once for the sinus range and again for the sinus swing
- * in time (half of it is spent decreasing the frequency, half is spent
- * increasing it).
+ * Late reverb taps are based on the late line lengths to allow a zero-
+ * delay path and offsets that would continue the propagation naturally
+ * into the late lines.
*/
- State->Mod.Depth = modDepth * MODULATION_DEPTH_COEFF * modTime / 2.0f /
- 2.0f * frequency;
-}
+ for(i = 0;i < NUM_LINES;i++)
+ {
+ length = earlyDelay + EARLY_TAP_LENGTHS[i]*multiplier;
+ State->EarlyDelayTap[i][1] = float2int(length * frequency);
-// Update the offsets for the initial effect delay line.
-static ALvoid UpdateDelayLine(ALfloat earlyDelay, ALfloat lateDelay, ALuint frequency, ALreverbState *State)
-{
- // Calculate the initial delay taps.
- State->DelayTap[0] = fastf2u(earlyDelay * frequency);
- State->DelayTap[1] = fastf2u((earlyDelay + lateDelay) * frequency);
-}
+ length = EARLY_TAP_LENGTHS[i]*multiplier;
+ State->EarlyDelayCoeff[i][1] = CalcDecayCoeff(length, decayTime);
-// Update the early reflections gain and line coefficients.
-static ALvoid UpdateEarlyLines(ALfloat reverbGain, ALfloat earlyGain, ALfloat lateDelay, ALreverbState *State)
-{
- ALuint index;
-
- // Calculate the early reflections gain (from the master effect gain, and
- // reflections gain parameters) with a constant attenuation of 0.5.
- State->Early.Gain = 0.5f * reverbGain * earlyGain;
-
- // Calculate the gain (coefficient) for each early delay line using the
- // late delay time. This expands the early reflections to the start of
- // the late reverb.
- for(index = 0;index < 4;index++)
- State->Early.Coeff[index] = CalcDecayCoeff(EARLY_LINE_LENGTH[index],
- lateDelay);
+ length = lateDelay + (LATE_LINE_LENGTHS[i] - LATE_LINE_LENGTHS[0])*0.25f*multiplier;
+ State->LateDelayTap[i][1] = State->LateFeedTap + float2int(length * frequency);
+ }
}
-// Update the offsets for the decorrelator line.
-static ALvoid UpdateDecorrelator(ALfloat density, ALuint frequency, ALreverbState *State)
+/* Update the early reflection line lengths and gain coefficients. */
+static ALvoid UpdateEarlyLines(const ALfloat density, const ALfloat diffusion, const ALfloat decayTime, const ALuint frequency, EarlyReflections *Early)
{
- ALuint index;
- ALfloat length;
+ ALfloat multiplier, length;
+ ALsizei i;
- /* The late reverb inputs are decorrelated to smooth the reverb tail and
- * reduce harsh echos. The first tap occurs immediately, while the
- * remaining taps are delayed by multiples of a fraction of the smallest
- * cyclical delay time.
- *
- * offset[index] = (FRACTION (MULTIPLIER^index)) smallest_delay
- */
- for(index = 0;index < 3;index++)
+ multiplier = CalcDelayLengthMult(density);
+
+ /* Calculate the all-pass feed-back/forward coefficient. */
+ Early->VecAp.Coeff = sqrtf(0.5f) * powf(diffusion, 2.0f);
+
+ for(i = 0;i < NUM_LINES;i++)
{
- length = (DECO_FRACTION * powf(DECO_MULTIPLIER, (ALfloat)index)) *
- LATE_LINE_LENGTH[0] * (1.0f + (density * LATE_LINE_MULTIPLIER));
- State->DecoTap[index] = fastf2u(length * frequency);
+ /* Calculate the length (in seconds) of each all-pass line. */
+ length = EARLY_ALLPASS_LENGTHS[i] * multiplier;
+
+ /* Calculate the delay offset for each all-pass line. */
+ Early->VecAp.Offset[i][1] = float2int(length * frequency);
+
+ /* Calculate the length (in seconds) of each delay line. */
+ length = EARLY_LINE_LENGTHS[i] * multiplier;
+
+ /* Calculate the delay offset for each delay line. */
+ Early->Offset[i][1] = float2int(length * frequency);
+
+ /* Calculate the gain (coefficient) for each line. */
+ Early->Coeff[i][1] = CalcDecayCoeff(length, decayTime);
}
}
-// Update the late reverb gains, line lengths, and line coefficients.
-static ALvoid UpdateLateLines(ALfloat reverbGain, ALfloat lateGain, ALfloat xMix, ALfloat density, ALfloat decayTime, ALfloat diffusion, ALfloat echoDepth, ALfloat hfRatio, ALfloat cw, ALuint frequency, ALreverbState *State)
+/* Update the late reverb line lengths and T60 coefficients. */
+static ALvoid UpdateLateLines(const ALfloat density, const ALfloat diffusion, const ALfloat lfDecayTime, const ALfloat mfDecayTime, const ALfloat hfDecayTime, const ALfloat lf0norm, const ALfloat hf0norm, const ALuint frequency, LateReverb *Late)
{
- ALfloat length;
- ALuint index;
-
- /* Calculate the late reverb gain (from the master effect gain, and late
- * reverb gain parameters). Since the output is tapped prior to the
- * application of the next delay line coefficients, this gain needs to be
- * attenuated by the 'x' mixing matrix coefficient as well. Also attenuate
- * the late reverb when echo depth is high and diffusion is low, so the
- * echo is slightly stronger than the decorrelated echos in the reverb
- * tail.
+ /* Scaling factor to convert the normalized reference frequencies from
+ * representing 0...freq to 0...max_reference.
*/
- State->Late.Gain = reverbGain * lateGain * xMix *
- (1.0f - (echoDepth*0.5f*(1.0f - diffusion)));
+ const ALfloat norm_weight_factor = (ALfloat)frequency / AL_EAXREVERB_MAX_HFREFERENCE;
+ ALfloat multiplier, length, bandWeights[3];
+ ALsizei i;
/* To compensate for changes in modal density and decay time of the late
* reverb signal, the input is attenuated based on the maximal energy of
* the outgoing signal. This approximation is used to keep the apparent
* energy of the signal equal for all ranges of density and decay time.
*
- * The average length of the cyclcical delay lines is used to calculate
- * the attenuation coefficient.
+ * The average length of the delay lines is used to calculate the
+ * attenuation coefficient.
+ */
+ multiplier = CalcDelayLengthMult(density);
+ length = (LATE_LINE_LENGTHS[0] + LATE_LINE_LENGTHS[1] +
+ LATE_LINE_LENGTHS[2] + LATE_LINE_LENGTHS[3]) / 4.0f * multiplier;
+ length += (LATE_ALLPASS_LENGTHS[0] + LATE_ALLPASS_LENGTHS[1] +
+ LATE_ALLPASS_LENGTHS[2] + LATE_ALLPASS_LENGTHS[3]) / 4.0f * multiplier;
+ /* The density gain calculation uses an average decay time weighted by
+ * approximate bandwidth. This attempts to compensate for losses of energy
+ * that reduce decay time due to scattering into highly attenuated bands.
*/
- length = (LATE_LINE_LENGTH[0] + LATE_LINE_LENGTH[1] +
- LATE_LINE_LENGTH[2] + LATE_LINE_LENGTH[3]) / 4.0f;
- length *= 1.0f + (density * LATE_LINE_MULTIPLIER);
- State->Late.DensityGain = CalcDensityGain(
- CalcDecayCoeff(length, decayTime)
+ bandWeights[0] = lf0norm*norm_weight_factor;
+ bandWeights[1] = hf0norm*norm_weight_factor - lf0norm*norm_weight_factor;
+ bandWeights[2] = 1.0f - hf0norm*norm_weight_factor;
+ Late->DensityGain[1] = CalcDensityGain(
+ CalcDecayCoeff(length,
+ bandWeights[0]*lfDecayTime + bandWeights[1]*mfDecayTime + bandWeights[2]*hfDecayTime
+ )
);
- // Calculate the all-pass feed-back and feed-forward coefficient.
- State->Late.ApFeedCoeff = 0.5f * powf(diffusion, 2.0f);
+ /* Calculate the all-pass feed-back/forward coefficient. */
+ Late->VecAp.Coeff = sqrtf(0.5f) * powf(diffusion, 2.0f);
- for(index = 0;index < 4;index++)
+ for(i = 0;i < NUM_LINES;i++)
{
- // Calculate the gain (coefficient) for each all-pass line.
- State->Late.ApCoeff[index] = CalcDecayCoeff(
- ALLPASS_LINE_LENGTH[index], decayTime
- );
+ /* Calculate the length (in seconds) of each all-pass line. */
+ length = LATE_ALLPASS_LENGTHS[i] * multiplier;
- // Calculate the length (in seconds) of each cyclical delay line.
- length = LATE_LINE_LENGTH[index] *
- (1.0f + (density * LATE_LINE_MULTIPLIER));
+ /* Calculate the delay offset for each all-pass line. */
+ Late->VecAp.Offset[i][1] = float2int(length * frequency);
- // Calculate the delay offset for each cyclical delay line.
- State->Late.Offset[index] = fastf2u(length * frequency);
+ /* Calculate the length (in seconds) of each delay line. */
+ length = LATE_LINE_LENGTHS[i] * multiplier;
- // Calculate the gain (coefficient) for each cyclical line.
- State->Late.Coeff[index] = CalcDecayCoeff(length, decayTime);
+ /* Calculate the delay offset for each delay line. */
+ Late->Offset[i][1] = float2int(length*frequency + 0.5f);
- // Calculate the damping coefficient for each low-pass filter.
- State->Late.LpCoeff[index] = CalcDampingCoeff(
- hfRatio, length, decayTime, State->Late.Coeff[index], cw
- );
+ /* Approximate the absorption that the vector all-pass would exhibit
+ * given the current diffusion so we don't have to process a full T60
+ * filter for each of its four lines.
+ */
+ length += lerp(LATE_ALLPASS_LENGTHS[i],
+ (LATE_ALLPASS_LENGTHS[0] + LATE_ALLPASS_LENGTHS[1] +
+ LATE_ALLPASS_LENGTHS[2] + LATE_ALLPASS_LENGTHS[3]) / 4.0f,
+ diffusion) * multiplier;
+
+ /* Calculate the T60 damping coefficients for each line. */
+ CalcT60DampingCoeffs(length, lfDecayTime, mfDecayTime, hfDecayTime,
+ lf0norm, hf0norm, &Late->T60[i]);
+ }
+}
- // Attenuate the cyclical line coefficients by the mixing coefficient
- // (x).
- State->Late.Coeff[index] *= xMix;
+/* Creates a transform matrix given a reverb vector. The vector pans the reverb
+ * reflections toward the given direction, using its magnitude (up to 1) as a
+ * focal strength. This function results in a B-Format transformation matrix
+ * that spatially focuses the signal in the desired direction.
+ */
+static aluMatrixf GetTransformFromVector(const ALfloat *vec)
+{
+ aluMatrixf focus;
+ ALfloat norm[3];
+ ALfloat mag;
+
+ /* Normalize the panning vector according to the N3D scale, which has an
+ * extra sqrt(3) term on the directional components. Converting from OpenAL
+ * to B-Format also requires negating X (ACN 1) and Z (ACN 3). Note however
+ * that the reverb panning vectors use left-handed coordinates, unlike the
+ * rest of OpenAL which use right-handed. This is fixed by negating Z,
+ * which cancels out with the B-Format Z negation.
+ */
+ mag = sqrtf(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]);
+ if(mag > 1.0f)
+ {
+ norm[0] = vec[0] / mag * -SQRTF_3;
+ norm[1] = vec[1] / mag * SQRTF_3;
+ norm[2] = vec[2] / mag * SQRTF_3;
+ mag = 1.0f;
}
+ else
+ {
+ /* If the magnitude is less than or equal to 1, just apply the sqrt(3)
+ * term. There's no need to renormalize the magnitude since it would
+ * just be reapplied in the matrix.
+ */
+ norm[0] = vec[0] * -SQRTF_3;
+ norm[1] = vec[1] * SQRTF_3;
+ norm[2] = vec[2] * SQRTF_3;
+ }
+
+ aluMatrixfSet(&focus,
+ 1.0f, 0.0f, 0.0f, 0.0f,
+ norm[0], 1.0f-mag, 0.0f, 0.0f,
+ norm[1], 0.0f, 1.0f-mag, 0.0f,
+ norm[2], 0.0f, 0.0f, 1.0f-mag
+ );
+
+ return focus;
}
-// Update the echo gain, line offset, line coefficients, and mixing
-// coefficients.
-static ALvoid UpdateEchoLine(ALfloat reverbGain, ALfloat lateGain, ALfloat echoTime, ALfloat decayTime, ALfloat diffusion, ALfloat echoDepth, ALfloat hfRatio, ALfloat cw, ALuint frequency, ALreverbState *State)
+/* Update the early and late 3D panning gains. */
+static ALvoid Update3DPanning(const ALCdevice *Device, const ALfloat *ReflectionsPan, const ALfloat *LateReverbPan, const ALfloat earlyGain, const ALfloat lateGain, ReverbState *State)
{
- // Update the offset and coefficient for the echo delay line.
- State->Echo.Offset = fastf2u(echoTime * frequency);
+ aluMatrixf transform, rot;
+ ALsizei i;
+
+ STATIC_CAST(ALeffectState,State)->OutBuffer = Device->FOAOut.Buffer;
+ STATIC_CAST(ALeffectState,State)->OutChannels = Device->FOAOut.NumChannels;
+
+ /* Note: _res is transposed. */
+#define MATRIX_MULT(_res, _m1, _m2) do { \
+ int row, col; \
+ for(col = 0;col < 4;col++) \
+ { \
+ for(row = 0;row < 4;row++) \
+ _res.m[col][row] = _m1.m[row][0]*_m2.m[0][col] + _m1.m[row][1]*_m2.m[1][col] + \
+ _m1.m[row][2]*_m2.m[2][col] + _m1.m[row][3]*_m2.m[3][col]; \
+ } \
+} while(0)
+ /* Create a matrix that first converts A-Format to B-Format, then
+ * transforms the B-Format signal according to the panning vector.
+ */
+ rot = GetTransformFromVector(ReflectionsPan);
+ MATRIX_MULT(transform, rot, A2B);
+ memset(&State->Early.PanGain, 0, sizeof(State->Early.PanGain));
+ for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
+ ComputePanGains(&Device->FOAOut, transform.m[i], earlyGain,
+ State->Early.PanGain[i]);
+
+ rot = GetTransformFromVector(LateReverbPan);
+ MATRIX_MULT(transform, rot, A2B);
+ memset(&State->Late.PanGain, 0, sizeof(State->Late.PanGain));
+ for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
+ ComputePanGains(&Device->FOAOut, transform.m[i], lateGain,
+ State->Late.PanGain[i]);
+#undef MATRIX_MULT
+}
- // Calculate the decay coefficient for the echo line.
- State->Echo.Coeff = CalcDecayCoeff(echoTime, decayTime);
+static void ReverbState_update(ReverbState *State, const ALCcontext *Context, const ALeffectslot *Slot, const ALeffectProps *props)
+{
+ const ALCdevice *Device = Context->Device;
+ const ALlistener *Listener = Context->Listener;
+ ALuint frequency = Device->Frequency;
+ ALfloat lf0norm, hf0norm, hfRatio;
+ ALfloat lfDecayTime, hfDecayTime;
+ ALfloat gain, gainlf, gainhf;
+ ALsizei i;
+
+ /* Calculate the master filters */
+ hf0norm = minf(props->Reverb.HFReference / frequency, 0.49f);
+ /* Restrict the filter gains from going below -60dB to keep the filter from
+ * killing most of the signal.
+ */
+ gainhf = maxf(props->Reverb.GainHF, 0.001f);
+ BiquadFilter_setParams(&State->Filter[0].Lp, BiquadType_HighShelf, gainhf, hf0norm,
+ calc_rcpQ_from_slope(gainhf, 1.0f));
+ lf0norm = minf(props->Reverb.LFReference / frequency, 0.49f);
+ gainlf = maxf(props->Reverb.GainLF, 0.001f);
+ BiquadFilter_setParams(&State->Filter[0].Hp, BiquadType_LowShelf, gainlf, lf0norm,
+ calc_rcpQ_from_slope(gainlf, 1.0f));
+ for(i = 1;i < NUM_LINES;i++)
+ {
+ BiquadFilter_copyParams(&State->Filter[i].Lp, &State->Filter[0].Lp);
+ BiquadFilter_copyParams(&State->Filter[i].Hp, &State->Filter[0].Hp);
+ }
- // Calculate the energy-based attenuation coefficient for the echo delay
- // line.
- State->Echo.DensityGain = CalcDensityGain(State->Echo.Coeff);
+ /* Update the main effect delay and associated taps. */
+ UpdateDelayLine(props->Reverb.ReflectionsDelay, props->Reverb.LateReverbDelay,
+ props->Reverb.Density, props->Reverb.DecayTime, frequency,
+ State);
- // Calculate the echo all-pass feed coefficient.
- State->Echo.ApFeedCoeff = 0.5f * powf(diffusion, 2.0f);
+ /* Update the early lines. */
+ UpdateEarlyLines(props->Reverb.Density, props->Reverb.Diffusion,
+ props->Reverb.DecayTime, frequency, &State->Early);
- // Calculate the echo all-pass attenuation coefficient.
- State->Echo.ApCoeff = CalcDecayCoeff(ECHO_ALLPASS_LENGTH, decayTime);
+ /* Get the mixing matrix coefficients. */
+ CalcMatrixCoeffs(props->Reverb.Diffusion, &State->MixX, &State->MixY);
- // Calculate the damping coefficient for each low-pass filter.
- State->Echo.LpCoeff = CalcDampingCoeff(hfRatio, echoTime, decayTime,
- State->Echo.Coeff, cw);
+ /* If the HF limit parameter is flagged, calculate an appropriate limit
+ * based on the air absorption parameter.
+ */
+ hfRatio = props->Reverb.DecayHFRatio;
+ if(props->Reverb.DecayHFLimit && props->Reverb.AirAbsorptionGainHF < 1.0f)
+ hfRatio = CalcLimitedHfRatio(hfRatio, props->Reverb.AirAbsorptionGainHF,
+ props->Reverb.DecayTime, Listener->Params.ReverbSpeedOfSound
+ );
+
+ /* Calculate the LF/HF decay times. */
+ lfDecayTime = clampf(props->Reverb.DecayTime * props->Reverb.DecayLFRatio,
+ AL_EAXREVERB_MIN_DECAY_TIME, AL_EAXREVERB_MAX_DECAY_TIME);
+ hfDecayTime = clampf(props->Reverb.DecayTime * hfRatio,
+ AL_EAXREVERB_MIN_DECAY_TIME, AL_EAXREVERB_MAX_DECAY_TIME);
+
+ /* Update the late lines. */
+ UpdateLateLines(props->Reverb.Density, props->Reverb.Diffusion,
+ lfDecayTime, props->Reverb.DecayTime, hfDecayTime, lf0norm, hf0norm,
+ frequency, &State->Late
+ );
+
+ /* Update early and late 3D panning. */
+ gain = props->Reverb.Gain * Slot->Params.Gain * ReverbBoost;
+ Update3DPanning(Device, props->Reverb.ReflectionsPan, props->Reverb.LateReverbPan,
+ props->Reverb.ReflectionsGain*gain, props->Reverb.LateReverbGain*gain,
+ State);
+
+ /* Calculate the max update size from the smallest relevant delay. */
+ State->MaxUpdate[1] = mini(MAX_UPDATE_SAMPLES,
+ mini(State->Early.Offset[0][1], State->Late.Offset[0][1])
+ );
- /* Calculate the echo mixing coefficients. The first is applied to the
- * echo itself. The second is used to attenuate the late reverb when
- * echo depth is high and diffusion is low, so the echo is slightly
- * stronger than the decorrelated echos in the reverb tail.
+ /* Determine if delay-line cross-fading is required. Density is essentially
+ * a master control for the feedback delays, so changes the offsets of many
+ * delay lines.
*/
- State->Echo.MixCoeff = reverbGain * lateGain * echoDepth;
+ if(State->Params.Density != props->Reverb.Density ||
+ /* Diffusion and decay times influences the decay rate (gain) of the
+ * late reverb T60 filter.
+ */
+ State->Params.Diffusion != props->Reverb.Diffusion ||
+ State->Params.DecayTime != props->Reverb.DecayTime ||
+ State->Params.HFDecayTime != hfDecayTime ||
+ State->Params.LFDecayTime != lfDecayTime ||
+ /* HF/LF References control the weighting used to calculate the density
+ * gain.
+ */
+ State->Params.HFReference != props->Reverb.HFReference ||
+ State->Params.LFReference != props->Reverb.LFReference)
+ State->FadeCount = 0;
+ State->Params.Density = props->Reverb.Density;
+ State->Params.Diffusion = props->Reverb.Diffusion;
+ State->Params.DecayTime = props->Reverb.DecayTime;
+ State->Params.HFDecayTime = hfDecayTime;
+ State->Params.LFDecayTime = lfDecayTime;
+ State->Params.HFReference = props->Reverb.HFReference;
+ State->Params.LFReference = props->Reverb.LFReference;
}
-// Update the early and late 3D panning gains.
-static ALvoid Update3DPanning(const ALCdevice *Device, const ALfloat *ReflectionsPan, const ALfloat *LateReverbPan, ALfloat Gain, ALreverbState *State)
+
+/**************************************
+ * Effect Processing *
+ **************************************/
+
+/* Basic delay line input/output routines. */
+static inline ALfloat DelayLineOut(const DelayLineI *Delay, const ALsizei offset, const ALsizei c)
{
- static const ALfloat EarlyPanAngles[4] = {
- DEG2RAD(0.0f), DEG2RAD(-90.0f), DEG2RAD(90.0f), DEG2RAD(180.0f)
- }, LatePanAngles[4] = {
- DEG2RAD(45.0f), DEG2RAD(-45.0f), DEG2RAD(135.0f), DEG2RAD(-135.0f)
- };
- ALfloat length, ev, az;
- ALuint i;
+ return Delay->Line[offset&Delay->Mask][c];
+}
+
+/* Cross-faded delay line output routine. Instead of interpolating the
+ * offsets, this interpolates (cross-fades) the outputs at each offset.
+ */
+static inline ALfloat FadedDelayLineOut(const DelayLineI *Delay, const ALsizei off0,
+ const ALsizei off1, const ALsizei c,
+ const ALfloat sc0, const ALfloat sc1)
+{
+ return Delay->Line[off0&Delay->Mask][c]*sc0 +
+ Delay->Line[off1&Delay->Mask][c]*sc1;
+}
+
+
+static inline void DelayLineIn(const DelayLineI *Delay, ALsizei offset, const ALsizei c,
+ const ALfloat *restrict in, ALsizei count)
+{
+ ALsizei i;
+ for(i = 0;i < count;i++)
+ Delay->Line[(offset++)&Delay->Mask][c] = *(in++);
+}
- length = sqrtf(ReflectionsPan[0]*ReflectionsPan[0] + ReflectionsPan[1]*ReflectionsPan[1] + ReflectionsPan[2]*ReflectionsPan[2]);
- if(!(length > FLT_EPSILON))
+/* Applies a scattering matrix to the 4-line (vector) input. This is used
+ * for both the below vector all-pass model and to perform modal feed-back
+ * delay network (FDN) mixing.
+ *
+ * The matrix is derived from a skew-symmetric matrix to form a 4D rotation
+ * matrix with a single unitary rotational parameter:
+ *
+ * [ d, a, b, c ] 1 = a^2 + b^2 + c^2 + d^2
+ * [ -a, d, c, -b ]
+ * [ -b, -c, d, a ]
+ * [ -c, b, -a, d ]
+ *
+ * The rotation is constructed from the effect's diffusion parameter,
+ * yielding:
+ *
+ * 1 = x^2 + 3 y^2
+ *
+ * Where a, b, and c are the coefficient y with differing signs, and d is the
+ * coefficient x. The final matrix is thus:
+ *
+ * [ x, y, -y, y ] n = sqrt(matrix_order - 1)
+ * [ -y, x, y, y ] t = diffusion_parameter * atan(n)
+ * [ y, -y, x, y ] x = cos(t)
+ * [ -y, -y, -y, x ] y = sin(t) / n
+ *
+ * Any square orthogonal matrix with an order that is a power of two will
+ * work (where ^T is transpose, ^-1 is inverse):
+ *
+ * M^T = M^-1
+ *
+ * Using that knowledge, finding an appropriate matrix can be accomplished
+ * naively by searching all combinations of:
+ *
+ * M = D + S - S^T
+ *
+ * Where D is a diagonal matrix (of x), and S is a triangular matrix (of y)
+ * whose combination of signs are being iterated.
+ */
+static inline void VectorPartialScatter(ALfloat *restrict out, const ALfloat *restrict in,
+ const ALfloat xCoeff, const ALfloat yCoeff)
+{
+ out[0] = xCoeff*in[0] + yCoeff*( in[1] + -in[2] + in[3]);
+ out[1] = xCoeff*in[1] + yCoeff*(-in[0] + in[2] + in[3]);
+ out[2] = xCoeff*in[2] + yCoeff*( in[0] + -in[1] + in[3]);
+ out[3] = xCoeff*in[3] + yCoeff*(-in[0] + -in[1] + -in[2] );
+}
+#define VectorScatterDelayIn(delay, o, in, xcoeff, ycoeff) \
+ VectorPartialScatter((delay)->Line[(o)&(delay)->Mask], in, xcoeff, ycoeff)
+
+/* Utilizes the above, but reverses the input channels. */
+static inline void VectorScatterRevDelayIn(const DelayLineI *Delay, ALint offset,
+ const ALfloat xCoeff, const ALfloat yCoeff,
+ const ALfloat (*restrict in)[MAX_UPDATE_SAMPLES],
+ const ALsizei count)
+{
+ const DelayLineI delay = *Delay;
+ ALsizei i, j;
+
+ for(i = 0;i < count;++i)
{
- for(i = 0;i < 4;i++)
- ComputeAngleGains(Device, EarlyPanAngles[i], 0.0f, Gain, State->Early.PanGain[i]);
+ ALfloat f[NUM_LINES];
+ for(j = 0;j < NUM_LINES;j++)
+ f[NUM_LINES-1-j] = in[j][i];
+
+ VectorScatterDelayIn(&delay, offset++, f, xCoeff, yCoeff);
}
- else
+}
+
+/* This applies a Gerzon multiple-in/multiple-out (MIMO) vector all-pass
+ * filter to the 4-line input.
+ *
+ * It works by vectorizing a regular all-pass filter and replacing the delay
+ * element with a scattering matrix (like the one above) and a diagonal
+ * matrix of delay elements.
+ *
+ * Two static specializations are used for transitional (cross-faded) delay
+ * line processing and non-transitional processing.
+ */
+static void VectorAllpass_Unfaded(ALfloat (*restrict samples)[MAX_UPDATE_SAMPLES], ALsizei offset,
+ const ALfloat xCoeff, const ALfloat yCoeff, ALsizei todo,
+ VecAllpass *Vap)
+{
+ const DelayLineI delay = Vap->Delay;
+ const ALfloat feedCoeff = Vap->Coeff;
+ ALsizei vap_offset[NUM_LINES];
+ ALsizei i, j;
+
+ ASSUME(todo > 0);
+
+ for(j = 0;j < NUM_LINES;j++)
+ vap_offset[j] = offset-Vap->Offset[j][0];
+ for(i = 0;i < todo;i++)
{
- ev = asinf(clampf(ReflectionsPan[1]/length, -1.0f, 1.0f));
- az = atan2f(ReflectionsPan[0], ReflectionsPan[2]);
+ ALfloat f[NUM_LINES];
- length = minf(length, 1.0f);
- for(i = 0;i < 4;i++)
+ for(j = 0;j < NUM_LINES;j++)
{
- /* This is essentially just a lerp, but takes the shortest path
- * with respect to circular wrapping. e.g.
- * -135 -> +/-180 -> +135
- * instead of
- * -135 -> 0 -> +135 */
- float offset, naz, nev;
- naz = EarlyPanAngles[i] + (modff((az-EarlyPanAngles[i])*length/F_TAU + 1.5f, &offset)-0.5f)*F_TAU;
- nev = (modff((ev )*length/F_TAU + 1.5f, &offset)-0.5f)*F_TAU;
- ComputeAngleGains(Device, naz, nev, Gain, State->Early.PanGain[i]);
+ ALfloat input = samples[j][i];
+ ALfloat out = DelayLineOut(&delay, vap_offset[j]++, j) - feedCoeff*input;
+ f[j] = input + feedCoeff*out;
+
+ samples[j][i] = out;
}
+
+ VectorScatterDelayIn(&delay, offset, f, xCoeff, yCoeff);
+ ++offset;
}
+}
+static void VectorAllpass_Faded(ALfloat (*restrict samples)[MAX_UPDATE_SAMPLES], ALsizei offset,
+ const ALfloat xCoeff, const ALfloat yCoeff, ALfloat fade,
+ ALsizei todo, VecAllpass *Vap)
+{
+ const DelayLineI delay = Vap->Delay;
+ const ALfloat feedCoeff = Vap->Coeff;
+ ALsizei vap_offset[NUM_LINES][2];
+ ALsizei i, j;
+
+ ASSUME(todo > 0);
- length = sqrtf(LateReverbPan[0]*LateReverbPan[0] + LateReverbPan[1]*LateReverbPan[1] + LateReverbPan[2]*LateReverbPan[2]);
- if(!(length > FLT_EPSILON))
+ fade *= 1.0f/FADE_SAMPLES;
+ for(j = 0;j < NUM_LINES;j++)
{
- for(i = 0;i < 4;i++)
- ComputeAngleGains(Device, LatePanAngles[i], 0.0f, Gain, State->Late.PanGain[i]);
+ vap_offset[j][0] = offset-Vap->Offset[j][0];
+ vap_offset[j][1] = offset-Vap->Offset[j][1];
}
- else
+ for(i = 0;i < todo;i++)
{
- ev = asinf(clampf(LateReverbPan[1]/length, -1.0f, 1.0f));
- az = atan2f(LateReverbPan[0], LateReverbPan[2]);
+ ALfloat f[NUM_LINES];
- length = minf(length, 1.0f);
- for(i = 0;i < 4;i++)
+ for(j = 0;j < NUM_LINES;j++)
{
- float offset, naz, nev;
- naz = LatePanAngles[i] + (modff((az-LatePanAngles[i])*length/F_TAU + 1.5f, &offset)-0.5f)*F_TAU;
- nev = (modff((ev )*length/F_TAU + 1.5f, &offset)-0.5f)*F_TAU;
- ComputeAngleGains(Device, naz, nev, Gain, State->Late.PanGain[i]);
+ ALfloat input = samples[j][i];
+ ALfloat out =
+ FadedDelayLineOut(&delay, vap_offset[j][0]++, vap_offset[j][1]++, j,
+ 1.0f-fade, fade
+ ) - feedCoeff*input;
+ f[j] = input + feedCoeff*out;
+
+ samples[j][i] = out;
}
+ fade += FadeStep;
+
+ VectorScatterDelayIn(&delay, offset, f, xCoeff, yCoeff);
+ ++offset;
}
}
-static ALvoid ALreverbState_update(ALreverbState *State, ALCdevice *Device, const ALeffectslot *Slot)
+/* This generates early reflections.
+ *
+ * This is done by obtaining the primary reflections (those arriving from the
+ * same direction as the source) from the main delay line. These are
+ * attenuated and all-pass filtered (based on the diffusion parameter).
+ *
+ * The early lines are then fed in reverse (according to the approximately
+ * opposite spatial location of the A-Format lines) to create the secondary
+ * reflections (those arriving from the opposite direction as the source).
+ *
+ * The early response is then completed by combining the primary reflections
+ * with the delayed and attenuated output from the early lines.
+ *
+ * Finally, the early response is reversed, scattered (based on diffusion),
+ * and fed into the late reverb section of the main delay line.
+ *
+ * Two static specializations are used for transitional (cross-faded) delay
+ * line processing and non-transitional processing.
+ */
+static void EarlyReflection_Unfaded(ReverbState *State, ALsizei offset, const ALsizei todo,
+ ALfloat (*restrict out)[MAX_UPDATE_SAMPLES])
{
- const ALeffectProps *props = &Slot->EffectProps;
- ALuint frequency = Device->Frequency;
- ALfloat lfscale, hfscale, hfRatio;
- ALfloat gainlf, gainhf;
- ALfloat cw, x, y;
-
- if(Slot->EffectType == AL_EFFECT_EAXREVERB && !EmulateEAXReverb)
- State->IsEax = AL_TRUE;
- else if(Slot->EffectType == AL_EFFECT_REVERB || EmulateEAXReverb)
- State->IsEax = AL_FALSE;
-
- // Calculate the master filters
- hfscale = props->Reverb.HFReference / frequency;
- gainhf = maxf(props->Reverb.GainHF, 0.0001f);
- ALfilterState_setParams(&State->LpFilter, ALfilterType_HighShelf,
- gainhf, hfscale, calc_rcpQ_from_slope(gainhf, 0.75f));
- lfscale = props->Reverb.LFReference / frequency;
- gainlf = maxf(props->Reverb.GainLF, 0.0001f);
- ALfilterState_setParams(&State->HpFilter, ALfilterType_LowShelf,
- gainlf, lfscale, calc_rcpQ_from_slope(gainlf, 0.75f));
-
- // Update the modulator line.
- UpdateModulator(props->Reverb.ModulationTime, props->Reverb.ModulationDepth,
- frequency, State);
-
- // Update the initial effect delay.
- UpdateDelayLine(props->Reverb.ReflectionsDelay, props->Reverb.LateReverbDelay,
- frequency, State);
+ ALfloat (*restrict temps)[MAX_UPDATE_SAMPLES] = State->TempSamples;
+ const DelayLineI early_delay = State->Early.Delay;
+ const DelayLineI main_delay = State->Delay;
+ const ALfloat mixX = State->MixX;
+ const ALfloat mixY = State->MixY;
+ ALsizei late_feed_tap;
+ ALsizei i, j;
+
+ ASSUME(todo > 0);
+
+ /* First, load decorrelated samples from the main delay line as the primary
+ * reflections.
+ */
+ for(j = 0;j < NUM_LINES;j++)
+ {
+ ALsizei early_delay_tap = offset - State->EarlyDelayTap[j][0];
+ ALfloat coeff = State->EarlyDelayCoeff[j][0];
+ for(i = 0;i < todo;i++)
+ temps[j][i] = DelayLineOut(&main_delay, early_delay_tap++, j) * coeff;
+ }
- // Update the early lines.
- UpdateEarlyLines(props->Reverb.Gain, props->Reverb.ReflectionsGain,
- props->Reverb.LateReverbDelay, State);
+ /* Apply a vector all-pass, to help color the initial reflections based on
+ * the diffusion strength.
+ */
+ VectorAllpass_Unfaded(temps, offset, mixX, mixY, todo, &State->Early.VecAp);
- // Update the decorrelator.
- UpdateDecorrelator(props->Reverb.Density, frequency, State);
+ /* Apply a delay and bounce to generate secondary reflections, combine with
+ * the primary reflections and write out the result for mixing.
+ */
+ for(j = 0;j < NUM_LINES;j++)
+ {
+ ALint early_feedb_tap = offset - State->Early.Offset[j][0];
+ ALfloat early_feedb_coeff = State->Early.Coeff[j][0];
- // Get the mixing matrix coefficients (x and y).
- CalcMatrixCoeffs(props->Reverb.Diffusion, &x, &y);
- // Then divide x into y to simplify the matrix calculation.
- State->Late.MixCoeff = y / x;
+ for(i = 0;i < todo;i++)
+ out[j][i] = DelayLineOut(&early_delay, early_feedb_tap++, j)*early_feedb_coeff +
+ temps[j][i];
+ }
+ for(j = 0;j < NUM_LINES;j++)
+ DelayLineIn(&early_delay, offset, NUM_LINES-1-j, temps[j], todo);
- // If the HF limit parameter is flagged, calculate an appropriate limit
- // based on the air absorption parameter.
- hfRatio = props->Reverb.DecayHFRatio;
- if(props->Reverb.DecayHFLimit && props->Reverb.AirAbsorptionGainHF < 1.0f)
- hfRatio = CalcLimitedHfRatio(hfRatio, props->Reverb.AirAbsorptionGainHF,
- props->Reverb.DecayTime);
-
- cw = cosf(F_TAU * hfscale);
- // Update the late lines.
- UpdateLateLines(props->Reverb.Gain, props->Reverb.LateReverbGain, x,
- props->Reverb.Density, props->Reverb.DecayTime,
- props->Reverb.Diffusion, props->Reverb.EchoDepth,
- hfRatio, cw, frequency, State);
-
- // Update the echo line.
- UpdateEchoLine(props->Reverb.Gain, props->Reverb.LateReverbGain,
- props->Reverb.EchoTime, props->Reverb.DecayTime,
- props->Reverb.Diffusion, props->Reverb.EchoDepth,
- hfRatio, cw, frequency, State);
-
- // Update early and late 3D panning.
- Update3DPanning(Device, props->Reverb.ReflectionsPan,
- props->Reverb.LateReverbPan,
- Slot->Gain * ReverbBoost, State);
+ /* Also write the result back to the main delay line for the late reverb
+ * stage to pick up at the appropriate time, appplying a scatter and
+ * bounce to improve the initial diffusion in the late reverb.
+ */
+ late_feed_tap = offset - State->LateFeedTap;
+ VectorScatterRevDelayIn(&main_delay, late_feed_tap, mixX, mixY, out, todo);
}
+static void EarlyReflection_Faded(ReverbState *State, ALsizei offset, const ALsizei todo,
+ const ALfloat fade, ALfloat (*restrict out)[MAX_UPDATE_SAMPLES])
+{
+ ALfloat (*restrict temps)[MAX_UPDATE_SAMPLES] = State->TempSamples;
+ const DelayLineI early_delay = State->Early.Delay;
+ const DelayLineI main_delay = State->Delay;
+ const ALfloat mixX = State->MixX;
+ const ALfloat mixY = State->MixY;
+ ALsizei late_feed_tap;
+ ALsizei i, j;
+ ASSUME(todo > 0);
-static ALvoid ALreverbState_Destruct(ALreverbState *State)
-{
- free(State->SampleBuffer);
- State->SampleBuffer = NULL;
-}
+ for(j = 0;j < NUM_LINES;j++)
+ {
+ ALsizei early_delay_tap0 = offset - State->EarlyDelayTap[j][0];
+ ALsizei early_delay_tap1 = offset - State->EarlyDelayTap[j][1];
+ ALfloat oldCoeff = State->EarlyDelayCoeff[j][0];
+ ALfloat oldCoeffStep = -oldCoeff / FADE_SAMPLES;
+ ALfloat newCoeffStep = State->EarlyDelayCoeff[j][1] / FADE_SAMPLES;
+ ALfloat fadeCount = fade;
+
+ for(i = 0;i < todo;i++)
+ {
+ const ALfloat fade0 = oldCoeff + oldCoeffStep*fadeCount;
+ const ALfloat fade1 = newCoeffStep*fadeCount;
+ temps[j][i] = FadedDelayLineOut(&main_delay,
+ early_delay_tap0++, early_delay_tap1++, j, fade0, fade1
+ );
+ fadeCount += 1.0f;
+ }
+ }
-DECLARE_DEFAULT_ALLOCATORS(ALreverbState)
+ VectorAllpass_Faded(temps, offset, mixX, mixY, fade, todo, &State->Early.VecAp);
-DEFINE_ALEFFECTSTATE_VTABLE(ALreverbState);
+ for(j = 0;j < NUM_LINES;j++)
+ {
+ ALint feedb_tap0 = offset - State->Early.Offset[j][0];
+ ALint feedb_tap1 = offset - State->Early.Offset[j][1];
+ ALfloat feedb_oldCoeff = State->Early.Coeff[j][0];
+ ALfloat feedb_oldCoeffStep = -feedb_oldCoeff / FADE_SAMPLES;
+ ALfloat feedb_newCoeffStep = State->Early.Coeff[j][1] / FADE_SAMPLES;
+ ALfloat fadeCount = fade;
+
+ for(i = 0;i < todo;i++)
+ {
+ const ALfloat fade0 = feedb_oldCoeff + feedb_oldCoeffStep*fadeCount;
+ const ALfloat fade1 = feedb_newCoeffStep*fadeCount;
+ out[j][i] = FadedDelayLineOut(&early_delay,
+ feedb_tap0++, feedb_tap1++, j, fade0, fade1
+ ) + temps[j][i];
+ fadeCount += 1.0f;
+ }
+ }
+ for(j = 0;j < NUM_LINES;j++)
+ DelayLineIn(&early_delay, offset, NUM_LINES-1-j, temps[j], todo);
+ late_feed_tap = offset - State->LateFeedTap;
+ VectorScatterRevDelayIn(&main_delay, late_feed_tap, mixX, mixY, out, todo);
+}
-typedef struct ALreverbStateFactory {
- DERIVE_FROM_TYPE(ALeffectStateFactory);
-} ALreverbStateFactory;
+/* Applies the two T60 damping filter sections. */
+static inline void LateT60Filter(ALfloat *restrict samples, const ALsizei todo, T60Filter *filter)
+{
+ ALfloat temp[MAX_UPDATE_SAMPLES];
+ BiquadFilter_process(&filter->HFFilter, temp, samples, todo);
+ BiquadFilter_process(&filter->LFFilter, samples, temp, todo);
+}
-static ALeffectState *ALreverbStateFactory_create(ALreverbStateFactory* UNUSED(factory))
+/* This generates the reverb tail using a modified feed-back delay network
+ * (FDN).
+ *
+ * Results from the early reflections are mixed with the output from the late
+ * delay lines.
+ *
+ * The late response is then completed by T60 and all-pass filtering the mix.
+ *
+ * Finally, the lines are reversed (so they feed their opposite directions)
+ * and scattered with the FDN matrix before re-feeding the delay lines.
+ *
+ * Two variations are made, one for for transitional (cross-faded) delay line
+ * processing and one for non-transitional processing.
+ */
+static void LateReverb_Unfaded(ReverbState *State, ALsizei offset, const ALsizei todo,
+ ALfloat (*restrict out)[MAX_UPDATE_SAMPLES])
{
- ALreverbState *state;
- ALuint index, l;
+ ALfloat (*restrict temps)[MAX_UPDATE_SAMPLES] = State->TempSamples;
+ const DelayLineI late_delay = State->Late.Delay;
+ const DelayLineI main_delay = State->Delay;
+ const ALfloat mixX = State->MixX;
+ const ALfloat mixY = State->MixY;
+ ALsizei i, j;
- state = ALreverbState_New(sizeof(*state));
- if(!state) return NULL;
- SET_VTABLE2(ALreverbState, ALeffectState, state);
+ ASSUME(todo > 0);
- state->TotalSamples = 0;
- state->SampleBuffer = NULL;
+ /* First, load decorrelated samples from the main and feedback delay lines.
+ * Filter the signal to apply its frequency-dependent decay.
+ */
+ for(j = 0;j < NUM_LINES;j++)
+ {
+ ALsizei late_delay_tap = offset - State->LateDelayTap[j][0];
+ ALsizei late_feedb_tap = offset - State->Late.Offset[j][0];
+ ALfloat midGain = State->Late.T60[j].MidGain[0];
+ const ALfloat densityGain = State->Late.DensityGain[0] * midGain;
+ for(i = 0;i < todo;i++)
+ temps[j][i] = DelayLineOut(&main_delay, late_delay_tap++, j)*densityGain +
+ DelayLineOut(&late_delay, late_feedb_tap++, j)*midGain;
+ LateT60Filter(temps[j], todo, &State->Late.T60[j]);
+ }
- ALfilterState_clear(&state->LpFilter);
- ALfilterState_clear(&state->HpFilter);
+ /* Apply a vector all-pass to improve micro-surface diffusion, and write
+ * out the results for mixing.
+ */
+ VectorAllpass_Unfaded(temps, offset, mixX, mixY, todo, &State->Late.VecAp);
- state->Mod.Delay.Mask = 0;
- state->Mod.Delay.Line = NULL;
- state->Mod.Index = 0;
- state->Mod.Range = 1;
- state->Mod.Depth = 0.0f;
- state->Mod.Coeff = 0.0f;
- state->Mod.Filter = 0.0f;
+ for(j = 0;j < NUM_LINES;j++)
+ memcpy(out[j], temps[j], todo*sizeof(ALfloat));
- state->Delay.Mask = 0;
- state->Delay.Line = NULL;
- state->DelayTap[0] = 0;
- state->DelayTap[1] = 0;
+ /* Finally, scatter and bounce the results to refeed the feedback buffer. */
+ VectorScatterRevDelayIn(&late_delay, offset, mixX, mixY, out, todo);
+}
+static void LateReverb_Faded(ReverbState *State, ALsizei offset, const ALsizei todo,
+ const ALfloat fade, ALfloat (*restrict out)[MAX_UPDATE_SAMPLES])
+{
+ ALfloat (*restrict temps)[MAX_UPDATE_SAMPLES] = State->TempSamples;
+ const DelayLineI late_delay = State->Late.Delay;
+ const DelayLineI main_delay = State->Delay;
+ const ALfloat mixX = State->MixX;
+ const ALfloat mixY = State->MixY;
+ ALsizei i, j;
- state->Early.Gain = 0.0f;
- for(index = 0;index < 4;index++)
- {
- state->Early.Coeff[index] = 0.0f;
- state->Early.Delay[index].Mask = 0;
- state->Early.Delay[index].Line = NULL;
- state->Early.Offset[index] = 0;
- }
+ ASSUME(todo > 0);
- state->Decorrelator.Mask = 0;
- state->Decorrelator.Line = NULL;
- state->DecoTap[0] = 0;
- state->DecoTap[1] = 0;
- state->DecoTap[2] = 0;
-
- state->Late.Gain = 0.0f;
- state->Late.DensityGain = 0.0f;
- state->Late.ApFeedCoeff = 0.0f;
- state->Late.MixCoeff = 0.0f;
- for(index = 0;index < 4;index++)
+ for(j = 0;j < NUM_LINES;j++)
{
- state->Late.ApCoeff[index] = 0.0f;
- state->Late.ApDelay[index].Mask = 0;
- state->Late.ApDelay[index].Line = NULL;
- state->Late.ApOffset[index] = 0;
-
- state->Late.Coeff[index] = 0.0f;
- state->Late.Delay[index].Mask = 0;
- state->Late.Delay[index].Line = NULL;
- state->Late.Offset[index] = 0;
-
- state->Late.LpCoeff[index] = 0.0f;
- state->Late.LpSample[index] = 0.0f;
+ const ALfloat oldMidGain = State->Late.T60[j].MidGain[0];
+ const ALfloat midGain = State->Late.T60[j].MidGain[1];
+ const ALfloat oldMidStep = -oldMidGain / FADE_SAMPLES;
+ const ALfloat midStep = midGain / FADE_SAMPLES;
+ const ALfloat oldDensityGain = State->Late.DensityGain[0] * oldMidGain;
+ const ALfloat densityGain = State->Late.DensityGain[1] * midGain;
+ const ALfloat oldDensityStep = -oldDensityGain / FADE_SAMPLES;
+ const ALfloat densityStep = densityGain / FADE_SAMPLES;
+ ALsizei late_delay_tap0 = offset - State->LateDelayTap[j][0];
+ ALsizei late_delay_tap1 = offset - State->LateDelayTap[j][1];
+ ALsizei late_feedb_tap0 = offset - State->Late.Offset[j][0];
+ ALsizei late_feedb_tap1 = offset - State->Late.Offset[j][1];
+ ALfloat fadeCount = fade;
+
+ for(i = 0;i < todo;i++)
+ {
+ const ALfloat fade0 = oldDensityGain + oldDensityStep*fadeCount;
+ const ALfloat fade1 = densityStep*fadeCount;
+ const ALfloat gfade0 = oldMidGain + oldMidStep*fadeCount;
+ const ALfloat gfade1 = midStep*fadeCount;
+ temps[j][i] =
+ FadedDelayLineOut(&main_delay, late_delay_tap0++, late_delay_tap1++, j,
+ fade0, fade1) +
+ FadedDelayLineOut(&late_delay, late_feedb_tap0++, late_feedb_tap1++, j,
+ gfade0, gfade1);
+ fadeCount += 1.0f;
+ }
+ LateT60Filter(temps[j], todo, &State->Late.T60[j]);
}
- for(l = 0;l < 4;l++)
+ VectorAllpass_Faded(temps, offset, mixX, mixY, fade, todo, &State->Late.VecAp);
+
+ for(j = 0;j < NUM_LINES;j++)
+ memcpy(out[j], temps[j], todo*sizeof(ALfloat));
+
+ VectorScatterRevDelayIn(&late_delay, offset, mixX, mixY, temps, todo);
+}
+
+static ALvoid ReverbState_process(ReverbState *State, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
+{
+ ALfloat (*restrict afmt)[MAX_UPDATE_SAMPLES] = State->TempSamples;
+ ALfloat (*restrict samples)[MAX_UPDATE_SAMPLES] = State->MixSamples;
+ ALsizei fadeCount = State->FadeCount;
+ ALsizei offset = State->Offset;
+ ALsizei base, c;
+
+ /* Process reverb for these samples. */
+ for(base = 0;base < SamplesToDo;)
{
- for(index = 0;index < MAX_OUTPUT_CHANNELS;index++)
+ ALsizei todo = SamplesToDo - base;
+ /* If cross-fading, don't do more samples than there are to fade. */
+ if(FADE_SAMPLES-fadeCount > 0)
{
- state->Early.PanGain[l][index] = 0.0f;
- state->Late.PanGain[l][index] = 0.0f;
+ todo = mini(todo, FADE_SAMPLES-fadeCount);
+ todo = mini(todo, State->MaxUpdate[0]);
}
+ todo = mini(todo, State->MaxUpdate[1]);
+ /* If this is not the final update, ensure the update size is a
+ * multiple of 4 for the SIMD mixers.
+ */
+ if(todo < SamplesToDo-base)
+ todo &= ~3;
+
+ /* Convert B-Format to A-Format for processing. */
+ memset(afmt, 0, sizeof(*afmt)*NUM_LINES);
+ for(c = 0;c < NUM_LINES;c++)
+ MixRowSamples(afmt[c], B2A.m[c],
+ SamplesIn, MAX_EFFECT_CHANNELS, base, todo
+ );
+
+ /* Process the samples for reverb. */
+ for(c = 0;c < NUM_LINES;c++)
+ {
+ /* Band-pass the incoming samples. */
+ BiquadFilter_process(&State->Filter[c].Lp, samples[0], afmt[c], todo);
+ BiquadFilter_process(&State->Filter[c].Hp, samples[1], samples[0], todo);
+
+ /* Feed the initial delay line. */
+ DelayLineIn(&State->Delay, offset, c, samples[1], todo);
+ }
+
+ if(UNLIKELY(fadeCount < FADE_SAMPLES))
+ {
+ ALfloat fade = (ALfloat)fadeCount;
+
+ /* Generate early reflections. */
+ EarlyReflection_Faded(State, offset, todo, fade, samples);
+ /* Mix the A-Format results to output, implicitly converting back
+ * to B-Format.
+ */
+ for(c = 0;c < NUM_LINES;c++)
+ MixSamples(samples[c], NumChannels, SamplesOut,
+ State->Early.CurrentGain[c], State->Early.PanGain[c],
+ SamplesToDo-base, base, todo
+ );
+
+ /* Generate and mix late reverb. */
+ LateReverb_Faded(State, offset, todo, fade, samples);
+ for(c = 0;c < NUM_LINES;c++)
+ MixSamples(samples[c], NumChannels, SamplesOut,
+ State->Late.CurrentGain[c], State->Late.PanGain[c],
+ SamplesToDo-base, base, todo
+ );
+
+ /* Step fading forward. */
+ fadeCount += todo;
+ if(LIKELY(fadeCount >= FADE_SAMPLES))
+ {
+ /* Update the cross-fading delay line taps. */
+ fadeCount = FADE_SAMPLES;
+ for(c = 0;c < NUM_LINES;c++)
+ {
+ State->EarlyDelayTap[c][0] = State->EarlyDelayTap[c][1];
+ State->EarlyDelayCoeff[c][0] = State->EarlyDelayCoeff[c][1];
+ State->Early.VecAp.Offset[c][0] = State->Early.VecAp.Offset[c][1];
+ State->Early.Offset[c][0] = State->Early.Offset[c][1];
+ State->Early.Coeff[c][0] = State->Early.Coeff[c][1];
+ State->LateDelayTap[c][0] = State->LateDelayTap[c][1];
+ State->Late.VecAp.Offset[c][0] = State->Late.VecAp.Offset[c][1];
+ State->Late.Offset[c][0] = State->Late.Offset[c][1];
+ State->Late.T60[c].MidGain[0] = State->Late.T60[c].MidGain[1];
+ }
+ State->Late.DensityGain[0] = State->Late.DensityGain[1];
+ State->MaxUpdate[0] = State->MaxUpdate[1];
+ }
+ }
+ else
+ {
+ /* Generate and mix early reflections. */
+ EarlyReflection_Unfaded(State, offset, todo, samples);
+ for(c = 0;c < NUM_LINES;c++)
+ MixSamples(samples[c], NumChannels, SamplesOut,
+ State->Early.CurrentGain[c], State->Early.PanGain[c],
+ SamplesToDo-base, base, todo
+ );
+
+ /* Generate and mix late reverb. */
+ LateReverb_Unfaded(State, offset, todo, samples);
+ for(c = 0;c < NUM_LINES;c++)
+ MixSamples(samples[c], NumChannels, SamplesOut,
+ State->Late.CurrentGain[c], State->Late.PanGain[c],
+ SamplesToDo-base, base, todo
+ );
+ }
+
+ /* Step all delays forward. */
+ offset += todo;
+
+ base += todo;
}
+ State->Offset = offset;
+ State->FadeCount = fadeCount;
+}
- state->Echo.DensityGain = 0.0f;
- state->Echo.Delay.Mask = 0;
- state->Echo.Delay.Line = NULL;
- state->Echo.ApDelay.Mask = 0;
- state->Echo.ApDelay.Line = NULL;
- state->Echo.Coeff = 0.0f;
- state->Echo.ApFeedCoeff = 0.0f;
- state->Echo.ApCoeff = 0.0f;
- state->Echo.Offset = 0;
- state->Echo.ApOffset = 0;
- state->Echo.LpCoeff = 0.0f;
- state->Echo.LpSample = 0.0f;
- state->Echo.MixCoeff = 0.0f;
- state->Offset = 0;
+typedef struct ReverbStateFactory {
+ DERIVE_FROM_TYPE(EffectStateFactory);
+} ReverbStateFactory;
- state->Gain = state->Late.PanGain;
+static ALeffectState *ReverbStateFactory_create(ReverbStateFactory* UNUSED(factory))
+{
+ ReverbState *state;
+
+ NEW_OBJ0(state, ReverbState)();
+ if(!state) return NULL;
return STATIC_CAST(ALeffectState, state);
}
-DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALreverbStateFactory);
+DEFINE_EFFECTSTATEFACTORY_VTABLE(ReverbStateFactory);
-ALeffectStateFactory *ALreverbStateFactory_getFactory(void)
+EffectStateFactory *ReverbStateFactory_getFactory(void)
{
- static ALreverbStateFactory ReverbFactory = { { GET_VTABLE2(ALreverbStateFactory, ALeffectStateFactory) } };
+ static ReverbStateFactory ReverbFactory = { { GET_VTABLE2(ReverbStateFactory, EffectStateFactory) } };
- return STATIC_CAST(ALeffectStateFactory, &ReverbFactory);
+ return STATIC_CAST(EffectStateFactory, &ReverbFactory);
}
@@ -1311,18 +1614,17 @@ void ALeaxreverb_setParami(ALeffect *effect, ALCcontext *context, ALenum param,
{
case AL_EAXREVERB_DECAY_HFLIMIT:
if(!(val >= AL_EAXREVERB_MIN_DECAY_HFLIMIT && val <= AL_EAXREVERB_MAX_DECAY_HFLIMIT))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb decay hflimit out of range");
props->Reverb.DecayHFLimit = val;
break;
default:
- SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ alSetError(context, AL_INVALID_ENUM, "Invalid EAX reverb integer property 0x%04x",
+ param);
}
}
void ALeaxreverb_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
-{
- ALeaxreverb_setParami(effect, context, param, vals[0]);
-}
+{ ALeaxreverb_setParami(effect, context, param, vals[0]); }
void ALeaxreverb_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
{
ALeffectProps *props = &effect->Props;
@@ -1330,126 +1632,127 @@ void ALeaxreverb_setParamf(ALeffect *effect, ALCcontext *context, ALenum param,
{
case AL_EAXREVERB_DENSITY:
if(!(val >= AL_EAXREVERB_MIN_DENSITY && val <= AL_EAXREVERB_MAX_DENSITY))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb density out of range");
props->Reverb.Density = val;
break;
case AL_EAXREVERB_DIFFUSION:
if(!(val >= AL_EAXREVERB_MIN_DIFFUSION && val <= AL_EAXREVERB_MAX_DIFFUSION))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb diffusion out of range");
props->Reverb.Diffusion = val;
break;
case AL_EAXREVERB_GAIN:
if(!(val >= AL_EAXREVERB_MIN_GAIN && val <= AL_EAXREVERB_MAX_GAIN))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb gain out of range");
props->Reverb.Gain = val;
break;
case AL_EAXREVERB_GAINHF:
if(!(val >= AL_EAXREVERB_MIN_GAINHF && val <= AL_EAXREVERB_MAX_GAINHF))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb gainhf out of range");
props->Reverb.GainHF = val;
break;
case AL_EAXREVERB_GAINLF:
if(!(val >= AL_EAXREVERB_MIN_GAINLF && val <= AL_EAXREVERB_MAX_GAINLF))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb gainlf out of range");
props->Reverb.GainLF = val;
break;
case AL_EAXREVERB_DECAY_TIME:
if(!(val >= AL_EAXREVERB_MIN_DECAY_TIME && val <= AL_EAXREVERB_MAX_DECAY_TIME))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb decay time out of range");
props->Reverb.DecayTime = val;
break;
case AL_EAXREVERB_DECAY_HFRATIO:
if(!(val >= AL_EAXREVERB_MIN_DECAY_HFRATIO && val <= AL_EAXREVERB_MAX_DECAY_HFRATIO))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb decay hfratio out of range");
props->Reverb.DecayHFRatio = val;
break;
case AL_EAXREVERB_DECAY_LFRATIO:
if(!(val >= AL_EAXREVERB_MIN_DECAY_LFRATIO && val <= AL_EAXREVERB_MAX_DECAY_LFRATIO))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb decay lfratio out of range");
props->Reverb.DecayLFRatio = val;
break;
case AL_EAXREVERB_REFLECTIONS_GAIN:
if(!(val >= AL_EAXREVERB_MIN_REFLECTIONS_GAIN && val <= AL_EAXREVERB_MAX_REFLECTIONS_GAIN))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb reflections gain out of range");
props->Reverb.ReflectionsGain = val;
break;
case AL_EAXREVERB_REFLECTIONS_DELAY:
if(!(val >= AL_EAXREVERB_MIN_REFLECTIONS_DELAY && val <= AL_EAXREVERB_MAX_REFLECTIONS_DELAY))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb reflections delay out of range");
props->Reverb.ReflectionsDelay = val;
break;
case AL_EAXREVERB_LATE_REVERB_GAIN:
if(!(val >= AL_EAXREVERB_MIN_LATE_REVERB_GAIN && val <= AL_EAXREVERB_MAX_LATE_REVERB_GAIN))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb late reverb gain out of range");
props->Reverb.LateReverbGain = val;
break;
case AL_EAXREVERB_LATE_REVERB_DELAY:
if(!(val >= AL_EAXREVERB_MIN_LATE_REVERB_DELAY && val <= AL_EAXREVERB_MAX_LATE_REVERB_DELAY))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb late reverb delay out of range");
props->Reverb.LateReverbDelay = val;
break;
case AL_EAXREVERB_AIR_ABSORPTION_GAINHF:
if(!(val >= AL_EAXREVERB_MIN_AIR_ABSORPTION_GAINHF && val <= AL_EAXREVERB_MAX_AIR_ABSORPTION_GAINHF))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb air absorption gainhf out of range");
props->Reverb.AirAbsorptionGainHF = val;
break;
case AL_EAXREVERB_ECHO_TIME:
if(!(val >= AL_EAXREVERB_MIN_ECHO_TIME && val <= AL_EAXREVERB_MAX_ECHO_TIME))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb echo time out of range");
props->Reverb.EchoTime = val;
break;
case AL_EAXREVERB_ECHO_DEPTH:
if(!(val >= AL_EAXREVERB_MIN_ECHO_DEPTH && val <= AL_EAXREVERB_MAX_ECHO_DEPTH))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb echo depth out of range");
props->Reverb.EchoDepth = val;
break;
case AL_EAXREVERB_MODULATION_TIME:
if(!(val >= AL_EAXREVERB_MIN_MODULATION_TIME && val <= AL_EAXREVERB_MAX_MODULATION_TIME))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb modulation time out of range");
props->Reverb.ModulationTime = val;
break;
case AL_EAXREVERB_MODULATION_DEPTH:
if(!(val >= AL_EAXREVERB_MIN_MODULATION_DEPTH && val <= AL_EAXREVERB_MAX_MODULATION_DEPTH))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb modulation depth out of range");
props->Reverb.ModulationDepth = val;
break;
case AL_EAXREVERB_HFREFERENCE:
if(!(val >= AL_EAXREVERB_MIN_HFREFERENCE && val <= AL_EAXREVERB_MAX_HFREFERENCE))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb hfreference out of range");
props->Reverb.HFReference = val;
break;
case AL_EAXREVERB_LFREFERENCE:
if(!(val >= AL_EAXREVERB_MIN_LFREFERENCE && val <= AL_EAXREVERB_MAX_LFREFERENCE))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb lfreference out of range");
props->Reverb.LFReference = val;
break;
case AL_EAXREVERB_ROOM_ROLLOFF_FACTOR:
if(!(val >= AL_EAXREVERB_MIN_ROOM_ROLLOFF_FACTOR && val <= AL_EAXREVERB_MAX_ROOM_ROLLOFF_FACTOR))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb room rolloff factor out of range");
props->Reverb.RoomRolloffFactor = val;
break;
default:
- SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ alSetError(context, AL_INVALID_ENUM, "Invalid EAX reverb float property 0x%04x",
+ param);
}
}
void ALeaxreverb_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
@@ -1459,21 +1762,17 @@ void ALeaxreverb_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param,
{
case AL_EAXREVERB_REFLECTIONS_PAN:
if(!(isfinite(vals[0]) && isfinite(vals[1]) && isfinite(vals[2])))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
- LockContext(context);
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb reflections pan out of range");
props->Reverb.ReflectionsPan[0] = vals[0];
props->Reverb.ReflectionsPan[1] = vals[1];
props->Reverb.ReflectionsPan[2] = vals[2];
- UnlockContext(context);
break;
case AL_EAXREVERB_LATE_REVERB_PAN:
if(!(isfinite(vals[0]) && isfinite(vals[1]) && isfinite(vals[2])))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
- LockContext(context);
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "EAX Reverb late reverb pan out of range");
props->Reverb.LateReverbPan[0] = vals[0];
props->Reverb.LateReverbPan[1] = vals[1];
props->Reverb.LateReverbPan[2] = vals[2];
- UnlockContext(context);
break;
default:
@@ -1492,13 +1791,12 @@ void ALeaxreverb_getParami(const ALeffect *effect, ALCcontext *context, ALenum p
break;
default:
- SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ alSetError(context, AL_INVALID_ENUM, "Invalid EAX reverb integer property 0x%04x",
+ param);
}
}
void ALeaxreverb_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
-{
- ALeaxreverb_getParami(effect, context, param, vals);
-}
+{ ALeaxreverb_getParami(effect, context, param, vals); }
void ALeaxreverb_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
{
const ALeffectProps *props = &effect->Props;
@@ -1585,7 +1883,8 @@ void ALeaxreverb_getParamf(const ALeffect *effect, ALCcontext *context, ALenum p
break;
default:
- SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ alSetError(context, AL_INVALID_ENUM, "Invalid EAX reverb float property 0x%04x",
+ param);
}
}
void ALeaxreverb_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
@@ -1594,18 +1893,14 @@ void ALeaxreverb_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum
switch(param)
{
case AL_EAXREVERB_REFLECTIONS_PAN:
- LockContext(context);
vals[0] = props->Reverb.ReflectionsPan[0];
vals[1] = props->Reverb.ReflectionsPan[1];
vals[2] = props->Reverb.ReflectionsPan[2];
- UnlockContext(context);
break;
case AL_EAXREVERB_LATE_REVERB_PAN:
- LockContext(context);
vals[0] = props->Reverb.LateReverbPan[0];
vals[1] = props->Reverb.LateReverbPan[1];
vals[2] = props->Reverb.LateReverbPan[2];
- UnlockContext(context);
break;
default:
@@ -1623,18 +1918,16 @@ void ALreverb_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALi
{
case AL_REVERB_DECAY_HFLIMIT:
if(!(val >= AL_REVERB_MIN_DECAY_HFLIMIT && val <= AL_REVERB_MAX_DECAY_HFLIMIT))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb decay hflimit out of range");
props->Reverb.DecayHFLimit = val;
break;
default:
- SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ alSetError(context, AL_INVALID_ENUM, "Invalid reverb integer property 0x%04x", param);
}
}
void ALreverb_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
-{
- ALreverb_setParami(effect, context, param, vals[0]);
-}
+{ ALreverb_setParami(effect, context, param, vals[0]); }
void ALreverb_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
{
ALeffectProps *props = &effect->Props;
@@ -1642,84 +1935,82 @@ void ALreverb_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALf
{
case AL_REVERB_DENSITY:
if(!(val >= AL_REVERB_MIN_DENSITY && val <= AL_REVERB_MAX_DENSITY))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb density out of range");
props->Reverb.Density = val;
break;
case AL_REVERB_DIFFUSION:
if(!(val >= AL_REVERB_MIN_DIFFUSION && val <= AL_REVERB_MAX_DIFFUSION))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb diffusion out of range");
props->Reverb.Diffusion = val;
break;
case AL_REVERB_GAIN:
if(!(val >= AL_REVERB_MIN_GAIN && val <= AL_REVERB_MAX_GAIN))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb gain out of range");
props->Reverb.Gain = val;
break;
case AL_REVERB_GAINHF:
if(!(val >= AL_REVERB_MIN_GAINHF && val <= AL_REVERB_MAX_GAINHF))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb gainhf out of range");
props->Reverb.GainHF = val;
break;
case AL_REVERB_DECAY_TIME:
if(!(val >= AL_REVERB_MIN_DECAY_TIME && val <= AL_REVERB_MAX_DECAY_TIME))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb decay time out of range");
props->Reverb.DecayTime = val;
break;
case AL_REVERB_DECAY_HFRATIO:
if(!(val >= AL_REVERB_MIN_DECAY_HFRATIO && val <= AL_REVERB_MAX_DECAY_HFRATIO))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb decay hfratio out of range");
props->Reverb.DecayHFRatio = val;
break;
case AL_REVERB_REFLECTIONS_GAIN:
if(!(val >= AL_REVERB_MIN_REFLECTIONS_GAIN && val <= AL_REVERB_MAX_REFLECTIONS_GAIN))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb reflections gain out of range");
props->Reverb.ReflectionsGain = val;
break;
case AL_REVERB_REFLECTIONS_DELAY:
if(!(val >= AL_REVERB_MIN_REFLECTIONS_DELAY && val <= AL_REVERB_MAX_REFLECTIONS_DELAY))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb reflections delay out of range");
props->Reverb.ReflectionsDelay = val;
break;
case AL_REVERB_LATE_REVERB_GAIN:
if(!(val >= AL_REVERB_MIN_LATE_REVERB_GAIN && val <= AL_REVERB_MAX_LATE_REVERB_GAIN))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb late reverb gain out of range");
props->Reverb.LateReverbGain = val;
break;
case AL_REVERB_LATE_REVERB_DELAY:
if(!(val >= AL_REVERB_MIN_LATE_REVERB_DELAY && val <= AL_REVERB_MAX_LATE_REVERB_DELAY))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb late reverb delay out of range");
props->Reverb.LateReverbDelay = val;
break;
case AL_REVERB_AIR_ABSORPTION_GAINHF:
if(!(val >= AL_REVERB_MIN_AIR_ABSORPTION_GAINHF && val <= AL_REVERB_MAX_AIR_ABSORPTION_GAINHF))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb air absorption gainhf out of range");
props->Reverb.AirAbsorptionGainHF = val;
break;
case AL_REVERB_ROOM_ROLLOFF_FACTOR:
if(!(val >= AL_REVERB_MIN_ROOM_ROLLOFF_FACTOR && val <= AL_REVERB_MAX_ROOM_ROLLOFF_FACTOR))
- SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ SETERR_RETURN(context, AL_INVALID_VALUE,, "Reverb room rolloff factor out of range");
props->Reverb.RoomRolloffFactor = val;
break;
default:
- SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ alSetError(context, AL_INVALID_ENUM, "Invalid reverb float property 0x%04x", param);
}
}
void ALreverb_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
-{
- ALreverb_setParamf(effect, context, param, vals[0]);
-}
+{ ALreverb_setParamf(effect, context, param, vals[0]); }
void ALreverb_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
{
@@ -1731,13 +2022,11 @@ void ALreverb_getParami(const ALeffect *effect, ALCcontext *context, ALenum para
break;
default:
- SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ alSetError(context, AL_INVALID_ENUM, "Invalid reverb integer property 0x%04x", param);
}
}
void ALreverb_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
-{
- ALreverb_getParami(effect, context, param, vals);
-}
+{ ALreverb_getParami(effect, context, param, vals); }
void ALreverb_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
{
const ALeffectProps *props = &effect->Props;
@@ -1792,12 +2081,10 @@ void ALreverb_getParamf(const ALeffect *effect, ALCcontext *context, ALenum para
break;
default:
- SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ alSetError(context, AL_INVALID_ENUM, "Invalid reverb float property 0x%04x", param);
}
}
void ALreverb_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
-{
- ALreverb_getParamf(effect, context, param, vals);
-}
+{ ALreverb_getParamf(effect, context, param, vals); }
DEFINE_ALEFFECT_VTABLE(ALreverb);
diff --git a/Alc/filters/defs.h b/Alc/filters/defs.h
new file mode 100644
index 00000000..133a85eb
--- /dev/null
+++ b/Alc/filters/defs.h
@@ -0,0 +1,112 @@
+#ifndef ALC_FILTER_H
+#define ALC_FILTER_H
+
+#include "AL/al.h"
+#include "math_defs.h"
+
+/* Filters implementation is based on the "Cookbook formulae for audio
+ * EQ biquad filter coefficients" by Robert Bristow-Johnson
+ * http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt
+ */
+/* Implementation note: For the shelf filters, the specified gain is for the
+ * reference frequency, which is the centerpoint of the transition band. This
+ * better matches EFX filter design. To set the gain for the shelf itself, use
+ * the square root of the desired linear gain (or halve the dB gain).
+ */
+
+typedef enum BiquadType {
+ /** EFX-style low-pass filter, specifying a gain and reference frequency. */
+ BiquadType_HighShelf,
+ /** EFX-style high-pass filter, specifying a gain and reference frequency. */
+ BiquadType_LowShelf,
+ /** Peaking filter, specifying a gain and reference frequency. */
+ BiquadType_Peaking,
+
+ /** Low-pass cut-off filter, specifying a cut-off frequency. */
+ BiquadType_LowPass,
+ /** High-pass cut-off filter, specifying a cut-off frequency. */
+ BiquadType_HighPass,
+ /** Band-pass filter, specifying a center frequency. */
+ BiquadType_BandPass,
+} BiquadType;
+
+typedef struct BiquadFilter {
+ ALfloat z1, z2; /* Last two delayed components for direct form II. */
+ ALfloat b0, b1, b2; /* Transfer function coefficients "b" (numerator) */
+ ALfloat a1, a2; /* Transfer function coefficients "a" (denominator; a0 is
+ * pre-applied). */
+} BiquadFilter;
+/* Currently only a C-based filter process method is implemented. */
+#define BiquadFilter_process BiquadFilter_processC
+
+/**
+ * Calculates the rcpQ (i.e. 1/Q) coefficient for shelving filters, using the
+ * reference gain and shelf slope parameter.
+ * \param gain 0 < gain
+ * \param slope 0 < slope <= 1
+ */
+inline ALfloat calc_rcpQ_from_slope(ALfloat gain, ALfloat slope)
+{
+ return sqrtf((gain + 1.0f/gain)*(1.0f/slope - 1.0f) + 2.0f);
+}
+/**
+ * Calculates the rcpQ (i.e. 1/Q) coefficient for filters, using the normalized
+ * reference frequency and bandwidth.
+ * \param f0norm 0 < f0norm < 0.5.
+ * \param bandwidth 0 < bandwidth
+ */
+inline ALfloat calc_rcpQ_from_bandwidth(ALfloat f0norm, ALfloat bandwidth)
+{
+ ALfloat w0 = F_TAU * f0norm;
+ return 2.0f*sinhf(logf(2.0f)/2.0f*bandwidth*w0/sinf(w0));
+}
+
+inline void BiquadFilter_clear(BiquadFilter *filter)
+{
+ filter->z1 = 0.0f;
+ filter->z2 = 0.0f;
+}
+
+/**
+ * Sets up the filter state for the specified filter type and its parameters.
+ *
+ * \param filter The filter object to prepare.
+ * \param type The type of filter for the object to apply.
+ * \param gain The gain for the reference frequency response. Only used by the
+ * Shelf and Peaking filter types.
+ * \param f0norm The normalized reference frequency (ref_freq / sample_rate).
+ * This is the center point for the Shelf, Peaking, and BandPass
+ * filter types, or the cutoff frequency for the LowPass and
+ * HighPass filter types.
+ * \param rcpQ The reciprocal of the Q coefficient for the filter's transition
+ * band. Can be generated from calc_rcpQ_from_slope or
+ * calc_rcpQ_from_bandwidth depending on the available data.
+ */
+void BiquadFilter_setParams(BiquadFilter *filter, BiquadType type, ALfloat gain, ALfloat f0norm, ALfloat rcpQ);
+
+inline void BiquadFilter_copyParams(BiquadFilter *restrict dst, const BiquadFilter *restrict src)
+{
+ dst->b0 = src->b0;
+ dst->b1 = src->b1;
+ dst->b2 = src->b2;
+ dst->a1 = src->a1;
+ dst->a2 = src->a2;
+}
+
+void BiquadFilter_processC(BiquadFilter *filter, ALfloat *restrict dst, const ALfloat *restrict src, ALsizei numsamples);
+
+inline void BiquadFilter_passthru(BiquadFilter *filter, ALsizei numsamples)
+{
+ if(LIKELY(numsamples >= 2))
+ {
+ filter->z1 = 0.0f;
+ filter->z2 = 0.0f;
+ }
+ else if(numsamples == 1)
+ {
+ filter->z1 = filter->z2;
+ filter->z2 = 0.0f;
+ }
+}
+
+#endif /* ALC_FILTER_H */
diff --git a/Alc/filters/filter.c b/Alc/filters/filter.c
new file mode 100644
index 00000000..2b370f89
--- /dev/null
+++ b/Alc/filters/filter.c
@@ -0,0 +1,129 @@
+
+#include "config.h"
+
+#include "AL/alc.h"
+#include "AL/al.h"
+
+#include "alMain.h"
+#include "defs.h"
+
+extern inline void BiquadFilter_clear(BiquadFilter *filter);
+extern inline void BiquadFilter_copyParams(BiquadFilter *restrict dst, const BiquadFilter *restrict src);
+extern inline void BiquadFilter_passthru(BiquadFilter *filter, ALsizei numsamples);
+extern inline ALfloat calc_rcpQ_from_slope(ALfloat gain, ALfloat slope);
+extern inline ALfloat calc_rcpQ_from_bandwidth(ALfloat f0norm, ALfloat bandwidth);
+
+
+void BiquadFilter_setParams(BiquadFilter *filter, BiquadType type, ALfloat gain, ALfloat f0norm, ALfloat rcpQ)
+{
+ ALfloat alpha, sqrtgain_alpha_2;
+ ALfloat w0, sin_w0, cos_w0;
+ ALfloat a[3] = { 1.0f, 0.0f, 0.0f };
+ ALfloat b[3] = { 1.0f, 0.0f, 0.0f };
+
+ // Limit gain to -100dB
+ assert(gain > 0.00001f);
+
+ w0 = F_TAU * f0norm;
+ sin_w0 = sinf(w0);
+ cos_w0 = cosf(w0);
+ alpha = sin_w0/2.0f * rcpQ;
+
+ /* Calculate filter coefficients depending on filter type */
+ switch(type)
+ {
+ case BiquadType_HighShelf:
+ sqrtgain_alpha_2 = 2.0f * sqrtf(gain) * alpha;
+ b[0] = gain*((gain+1.0f) + (gain-1.0f)*cos_w0 + sqrtgain_alpha_2);
+ b[1] = -2.0f*gain*((gain-1.0f) + (gain+1.0f)*cos_w0 );
+ b[2] = gain*((gain+1.0f) + (gain-1.0f)*cos_w0 - sqrtgain_alpha_2);
+ a[0] = (gain+1.0f) - (gain-1.0f)*cos_w0 + sqrtgain_alpha_2;
+ a[1] = 2.0f* ((gain-1.0f) - (gain+1.0f)*cos_w0 );
+ a[2] = (gain+1.0f) - (gain-1.0f)*cos_w0 - sqrtgain_alpha_2;
+ break;
+ case BiquadType_LowShelf:
+ sqrtgain_alpha_2 = 2.0f * sqrtf(gain) * alpha;
+ b[0] = gain*((gain+1.0f) - (gain-1.0f)*cos_w0 + sqrtgain_alpha_2);
+ b[1] = 2.0f*gain*((gain-1.0f) - (gain+1.0f)*cos_w0 );
+ b[2] = gain*((gain+1.0f) - (gain-1.0f)*cos_w0 - sqrtgain_alpha_2);
+ a[0] = (gain+1.0f) + (gain-1.0f)*cos_w0 + sqrtgain_alpha_2;
+ a[1] = -2.0f* ((gain-1.0f) + (gain+1.0f)*cos_w0 );
+ a[2] = (gain+1.0f) + (gain-1.0f)*cos_w0 - sqrtgain_alpha_2;
+ break;
+ case BiquadType_Peaking:
+ gain = sqrtf(gain);
+ b[0] = 1.0f + alpha * gain;
+ b[1] = -2.0f * cos_w0;
+ b[2] = 1.0f - alpha * gain;
+ a[0] = 1.0f + alpha / gain;
+ a[1] = -2.0f * cos_w0;
+ a[2] = 1.0f - alpha / gain;
+ break;
+
+ case BiquadType_LowPass:
+ b[0] = (1.0f - cos_w0) / 2.0f;
+ b[1] = 1.0f - cos_w0;
+ b[2] = (1.0f - cos_w0) / 2.0f;
+ a[0] = 1.0f + alpha;
+ a[1] = -2.0f * cos_w0;
+ a[2] = 1.0f - alpha;
+ break;
+ case BiquadType_HighPass:
+ b[0] = (1.0f + cos_w0) / 2.0f;
+ b[1] = -(1.0f + cos_w0);
+ b[2] = (1.0f + cos_w0) / 2.0f;
+ a[0] = 1.0f + alpha;
+ a[1] = -2.0f * cos_w0;
+ a[2] = 1.0f - alpha;
+ break;
+ case BiquadType_BandPass:
+ b[0] = alpha;
+ b[1] = 0;
+ b[2] = -alpha;
+ a[0] = 1.0f + alpha;
+ a[1] = -2.0f * cos_w0;
+ a[2] = 1.0f - alpha;
+ break;
+ }
+
+ filter->a1 = a[1] / a[0];
+ filter->a2 = a[2] / a[0];
+ filter->b0 = b[0] / a[0];
+ filter->b1 = b[1] / a[0];
+ filter->b2 = b[2] / a[0];
+}
+
+
+void BiquadFilter_processC(BiquadFilter *filter, ALfloat *restrict dst, const ALfloat *restrict src, ALsizei numsamples)
+{
+ const ALfloat a1 = filter->a1;
+ const ALfloat a2 = filter->a2;
+ const ALfloat b0 = filter->b0;
+ const ALfloat b1 = filter->b1;
+ const ALfloat b2 = filter->b2;
+ ALfloat z1 = filter->z1;
+ ALfloat z2 = filter->z2;
+ ALsizei i;
+
+ ASSUME(numsamples > 0);
+
+ /* Processing loop is Transposed Direct Form II. This requires less storage
+ * compared to Direct Form I (only two delay components, instead of a four-
+ * sample history; the last two inputs and outputs), and works better for
+ * floating-point which favors summing similarly-sized values while being
+ * less bothered by overflow.
+ *
+ * See: http://www.earlevel.com/main/2003/02/28/biquads/
+ */
+ for(i = 0;i < numsamples;i++)
+ {
+ ALfloat input = src[i];
+ ALfloat output = input*b0 + z1;
+ z1 = input*b1 - output*a1 + z2;
+ z2 = input*b2 - output*a2;
+ dst[i] = output;
+ }
+
+ filter->z1 = z1;
+ filter->z2 = z2;
+}
diff --git a/Alc/filters/nfc.c b/Alc/filters/nfc.c
new file mode 100644
index 00000000..8869d1d0
--- /dev/null
+++ b/Alc/filters/nfc.c
@@ -0,0 +1,426 @@
+
+#include "config.h"
+
+#include "nfc.h"
+#include "alMain.h"
+
+#include <string.h>
+
+
+/* Near-field control filters are the basis for handling the near-field effect.
+ * The near-field effect is a bass-boost present in the directional components
+ * of a recorded signal, created as a result of the wavefront curvature (itself
+ * a function of sound distance). Proper reproduction dictates this be
+ * compensated for using a bass-cut given the playback speaker distance, to
+ * avoid excessive bass in the playback.
+ *
+ * For real-time rendered audio, emulating the near-field effect based on the
+ * sound source's distance, and subsequently compensating for it at output
+ * based on the speaker distances, can create a more realistic perception of
+ * sound distance beyond a simple 1/r attenuation.
+ *
+ * These filters do just that. Each one applies a low-shelf filter, created as
+ * the combination of a bass-boost for a given sound source distance (near-
+ * field emulation) along with a bass-cut for a given control/speaker distance
+ * (near-field compensation).
+ *
+ * Note that it is necessary to apply a cut along with the boost, since the
+ * boost alone is unstable in higher-order ambisonics as it causes an infinite
+ * DC gain (even first-order ambisonics requires there to be no DC offset for
+ * the boost to work). Consequently, ambisonics requires a control parameter to
+ * be used to avoid an unstable boost-only filter. NFC-HOA defines this control
+ * as a reference delay, calculated with:
+ *
+ * reference_delay = control_distance / speed_of_sound
+ *
+ * This means w0 (for input) or w1 (for output) should be set to:
+ *
+ * wN = 1 / (reference_delay * sample_rate)
+ *
+ * when dealing with NFC-HOA content. For FOA input content, which does not
+ * specify a reference_delay variable, w0 should be set to 0 to apply only
+ * near-field compensation for output. It's important that w1 be a finite,
+ * positive, non-0 value or else the bass-boost will become unstable again.
+ * Also, w0 should not be too large compared to w1, to avoid excessively loud
+ * low frequencies.
+ */
+
+static const float B[4][3] = {
+ { 0.0f },
+ { 1.0f },
+ { 3.0f, 3.0f },
+ { 3.6778f, 6.4595f, 2.3222f },
+ /*{ 4.2076f, 11.4877f, 5.7924f, 9.1401f }*/
+};
+
+static void NfcFilterCreate1(struct NfcFilter1 *nfc, const float w0, const float w1)
+{
+ float b_00, g_0;
+ float r;
+
+ nfc->base_gain = 1.0f;
+ nfc->gain = 1.0f;
+
+ /* Calculate bass-boost coefficients. */
+ r = 0.5f * w0;
+ b_00 = B[1][0] * r;
+ g_0 = 1.0f + b_00;
+
+ nfc->gain *= g_0;
+ nfc->b1 = 2.0f * b_00 / g_0;
+
+ /* Calculate bass-cut coefficients. */
+ r = 0.5f * w1;
+ b_00 = B[1][0] * r;
+ g_0 = 1.0f + b_00;
+
+ nfc->base_gain /= g_0;
+ nfc->gain /= g_0;
+ nfc->a1 = 2.0f * b_00 / g_0;
+}
+
+static void NfcFilterAdjust1(struct NfcFilter1 *nfc, const float w0)
+{
+ float b_00, g_0;
+ float r;
+
+ r = 0.5f * w0;
+ b_00 = B[1][0] * r;
+ g_0 = 1.0f + b_00;
+
+ nfc->gain = nfc->base_gain * g_0;
+ nfc->b1 = 2.0f * b_00 / g_0;
+}
+
+
+static void NfcFilterCreate2(struct NfcFilter2 *nfc, const float w0, const float w1)
+{
+ float b_10, b_11, g_1;
+ float r;
+
+ nfc->base_gain = 1.0f;
+ nfc->gain = 1.0f;
+
+ /* Calculate bass-boost coefficients. */
+ r = 0.5f * w0;
+ b_10 = B[2][0] * r;
+ b_11 = B[2][1] * r * r;
+ g_1 = 1.0f + b_10 + b_11;
+
+ nfc->gain *= g_1;
+ nfc->b1 = (2.0f*b_10 + 4.0f*b_11) / g_1;
+ nfc->b2 = 4.0f * b_11 / g_1;
+
+ /* Calculate bass-cut coefficients. */
+ r = 0.5f * w1;
+ b_10 = B[2][0] * r;
+ b_11 = B[2][1] * r * r;
+ g_1 = 1.0f + b_10 + b_11;
+
+ nfc->base_gain /= g_1;
+ nfc->gain /= g_1;
+ nfc->a1 = (2.0f*b_10 + 4.0f*b_11) / g_1;
+ nfc->a2 = 4.0f * b_11 / g_1;
+}
+
+static void NfcFilterAdjust2(struct NfcFilter2 *nfc, const float w0)
+{
+ float b_10, b_11, g_1;
+ float r;
+
+ r = 0.5f * w0;
+ b_10 = B[2][0] * r;
+ b_11 = B[2][1] * r * r;
+ g_1 = 1.0f + b_10 + b_11;
+
+ nfc->gain = nfc->base_gain * g_1;
+ nfc->b1 = (2.0f*b_10 + 4.0f*b_11) / g_1;
+ nfc->b2 = 4.0f * b_11 / g_1;
+}
+
+
+static void NfcFilterCreate3(struct NfcFilter3 *nfc, const float w0, const float w1)
+{
+ float b_10, b_11, g_1;
+ float b_00, g_0;
+ float r;
+
+ nfc->base_gain = 1.0f;
+ nfc->gain = 1.0f;
+
+ /* Calculate bass-boost coefficients. */
+ r = 0.5f * w0;
+ b_10 = B[3][0] * r;
+ b_11 = B[3][1] * r * r;
+ g_1 = 1.0f + b_10 + b_11;
+
+ nfc->gain *= g_1;
+ nfc->b1 = (2.0f*b_10 + 4.0f*b_11) / g_1;
+ nfc->b2 = 4.0f * b_11 / g_1;
+
+ b_00 = B[3][2] * r;
+ g_0 = 1.0f + b_00;
+
+ nfc->gain *= g_0;
+ nfc->b3 = 2.0f * b_00 / g_0;
+
+ /* Calculate bass-cut coefficients. */
+ r = 0.5f * w1;
+ b_10 = B[3][0] * r;
+ b_11 = B[3][1] * r * r;
+ g_1 = 1.0f + b_10 + b_11;
+
+ nfc->base_gain /= g_1;
+ nfc->gain /= g_1;
+ nfc->a1 = (2.0f*b_10 + 4.0f*b_11) / g_1;
+ nfc->a2 = 4.0f * b_11 / g_1;
+
+ b_00 = B[3][2] * r;
+ g_0 = 1.0f + b_00;
+
+ nfc->base_gain /= g_0;
+ nfc->gain /= g_0;
+ nfc->a3 = 2.0f * b_00 / g_0;
+}
+
+static void NfcFilterAdjust3(struct NfcFilter3 *nfc, const float w0)
+{
+ float b_10, b_11, g_1;
+ float b_00, g_0;
+ float r;
+
+ r = 0.5f * w0;
+ b_10 = B[3][0] * r;
+ b_11 = B[3][1] * r * r;
+ g_1 = 1.0f + b_10 + b_11;
+
+ nfc->gain = nfc->base_gain * g_1;
+ nfc->b1 = (2.0f*b_10 + 4.0f*b_11) / g_1;
+ nfc->b2 = 4.0f * b_11 / g_1;
+
+ b_00 = B[3][2] * r;
+ g_0 = 1.0f + b_00;
+
+ nfc->gain *= g_0;
+ nfc->b3 = 2.0f * b_00 / g_0;
+}
+
+
+void NfcFilterCreate(NfcFilter *nfc, const float w0, const float w1)
+{
+ memset(nfc, 0, sizeof(*nfc));
+ NfcFilterCreate1(&nfc->first, w0, w1);
+ NfcFilterCreate2(&nfc->second, w0, w1);
+ NfcFilterCreate3(&nfc->third, w0, w1);
+}
+
+void NfcFilterAdjust(NfcFilter *nfc, const float w0)
+{
+ NfcFilterAdjust1(&nfc->first, w0);
+ NfcFilterAdjust2(&nfc->second, w0);
+ NfcFilterAdjust3(&nfc->third, w0);
+}
+
+
+void NfcFilterProcess1(NfcFilter *nfc, float *restrict dst, const float *restrict src, const int count)
+{
+ const float gain = nfc->first.gain;
+ const float b1 = nfc->first.b1;
+ const float a1 = nfc->first.a1;
+ float z1 = nfc->first.z[0];
+ int i;
+
+ ASSUME(count > 0);
+
+ for(i = 0;i < count;i++)
+ {
+ float y = src[i]*gain - a1*z1;
+ float out = y + b1*z1;
+ z1 += y;
+
+ dst[i] = out;
+ }
+ nfc->first.z[0] = z1;
+}
+
+void NfcFilterProcess2(NfcFilter *nfc, float *restrict dst, const float *restrict src, const int count)
+{
+ const float gain = nfc->second.gain;
+ const float b1 = nfc->second.b1;
+ const float b2 = nfc->second.b2;
+ const float a1 = nfc->second.a1;
+ const float a2 = nfc->second.a2;
+ float z1 = nfc->second.z[0];
+ float z2 = nfc->second.z[1];
+ int i;
+
+ ASSUME(count > 0);
+
+ for(i = 0;i < count;i++)
+ {
+ float y = src[i]*gain - a1*z1 - a2*z2;
+ float out = y + b1*z1 + b2*z2;
+ z2 += z1;
+ z1 += y;
+
+ dst[i] = out;
+ }
+ nfc->second.z[0] = z1;
+ nfc->second.z[1] = z2;
+}
+
+void NfcFilterProcess3(NfcFilter *nfc, float *restrict dst, const float *restrict src, const int count)
+{
+ const float gain = nfc->third.gain;
+ const float b1 = nfc->third.b1;
+ const float b2 = nfc->third.b2;
+ const float b3 = nfc->third.b3;
+ const float a1 = nfc->third.a1;
+ const float a2 = nfc->third.a2;
+ const float a3 = nfc->third.a3;
+ float z1 = nfc->third.z[0];
+ float z2 = nfc->third.z[1];
+ float z3 = nfc->third.z[2];
+ int i;
+
+ ASSUME(count > 0);
+
+ for(i = 0;i < count;i++)
+ {
+ float y = src[i]*gain - a1*z1 - a2*z2;
+ float out = y + b1*z1 + b2*z2;
+ z2 += z1;
+ z1 += y;
+
+ y = out - a3*z3;
+ out = y + b3*z3;
+ z3 += y;
+
+ dst[i] = out;
+ }
+ nfc->third.z[0] = z1;
+ nfc->third.z[1] = z2;
+ nfc->third.z[2] = z3;
+}
+
+#if 0 /* Original methods the above are derived from. */
+static void NfcFilterCreate(NfcFilter *nfc, const ALsizei order, const float src_dist, const float ctl_dist, const float rate)
+{
+ static const float B[4][5] = {
+ { },
+ { 1.0f },
+ { 3.0f, 3.0f },
+ { 3.6778f, 6.4595f, 2.3222f },
+ { 4.2076f, 11.4877f, 5.7924f, 9.1401f }
+ };
+ float w0 = SPEEDOFSOUNDMETRESPERSEC / (src_dist * rate);
+ float w1 = SPEEDOFSOUNDMETRESPERSEC / (ctl_dist * rate);
+ ALsizei i;
+ float r;
+
+ nfc->g = 1.0f;
+ nfc->coeffs[0] = 1.0f;
+
+ /* NOTE: Slight adjustment from the literature to raise the center
+ * frequency a bit (0.5 -> 1.0).
+ */
+ r = 1.0f * w0;
+ for(i = 0; i < (order-1);i += 2)
+ {
+ float b_10 = B[order][i ] * r;
+ float b_11 = B[order][i+1] * r * r;
+ float g_1 = 1.0f + b_10 + b_11;
+
+ nfc->b[i] = b_10;
+ nfc->b[i + 1] = b_11;
+ nfc->coeffs[0] *= g_1;
+ nfc->coeffs[i+1] = ((2.0f * b_10) + (4.0f * b_11)) / g_1;
+ nfc->coeffs[i+2] = (4.0f * b_11) / g_1;
+ }
+ if(i < order)
+ {
+ float b_00 = B[order][i] * r;
+ float g_0 = 1.0f + b_00;
+
+ nfc->b[i] = b_00;
+ nfc->coeffs[0] *= g_0;
+ nfc->coeffs[i+1] = (2.0f * b_00) / g_0;
+ }
+
+ r = 1.0f * w1;
+ for(i = 0;i < (order-1);i += 2)
+ {
+ float b_10 = B[order][i ] * r;
+ float b_11 = B[order][i+1] * r * r;
+ float g_1 = 1.0f + b_10 + b_11;
+
+ nfc->g /= g_1;
+ nfc->coeffs[0] /= g_1;
+ nfc->coeffs[order+i+1] = ((2.0f * b_10) + (4.0f * b_11)) / g_1;
+ nfc->coeffs[order+i+2] = (4.0f * b_11) / g_1;
+ }
+ if(i < order)
+ {
+ float b_00 = B[order][i] * r;
+ float g_0 = 1.0f + b_00;
+
+ nfc->g /= g_0;
+ nfc->coeffs[0] /= g_0;
+ nfc->coeffs[order+i+1] = (2.0f * b_00) / g_0;
+ }
+
+ for(i = 0; i < MAX_AMBI_ORDER; i++)
+ nfc->history[i] = 0.0f;
+}
+
+static void NfcFilterAdjust(NfcFilter *nfc, const float distance)
+{
+ int i;
+
+ nfc->coeffs[0] = nfc->g;
+
+ for(i = 0;i < (nfc->order-1);i += 2)
+ {
+ float b_10 = nfc->b[i] / distance;
+ float b_11 = nfc->b[i+1] / (distance * distance);
+ float g_1 = 1.0f + b_10 + b_11;
+
+ nfc->coeffs[0] *= g_1;
+ nfc->coeffs[i+1] = ((2.0f * b_10) + (4.0f * b_11)) / g_1;
+ nfc->coeffs[i+2] = (4.0f * b_11) / g_1;
+ }
+ if(i < nfc->order)
+ {
+ float b_00 = nfc->b[i] / distance;
+ float g_0 = 1.0f + b_00;
+
+ nfc->coeffs[0] *= g_0;
+ nfc->coeffs[i+1] = (2.0f * b_00) / g_0;
+ }
+}
+
+static float NfcFilterProcess(const float in, NfcFilter *nfc)
+{
+ int i;
+ float out = in * nfc->coeffs[0];
+
+ for(i = 0;i < (nfc->order-1);i += 2)
+ {
+ float y = out - (nfc->coeffs[nfc->order+i+1] * nfc->history[i]) -
+ (nfc->coeffs[nfc->order+i+2] * nfc->history[i+1]) + 1.0e-30f;
+ out = y + (nfc->coeffs[i+1]*nfc->history[i]) + (nfc->coeffs[i+2]*nfc->history[i+1]);
+
+ nfc->history[i+1] += nfc->history[i];
+ nfc->history[i] += y;
+ }
+ if(i < nfc->order)
+ {
+ float y = out - (nfc->coeffs[nfc->order+i+1] * nfc->history[i]) + 1.0e-30f;
+
+ out = y + (nfc->coeffs[i+1] * nfc->history[i]);
+ nfc->history[i] += y;
+ }
+
+ return out;
+}
+#endif
diff --git a/Alc/filters/nfc.h b/Alc/filters/nfc.h
new file mode 100644
index 00000000..12a5a18f
--- /dev/null
+++ b/Alc/filters/nfc.h
@@ -0,0 +1,49 @@
+#ifndef FILTER_NFC_H
+#define FILTER_NFC_H
+
+struct NfcFilter1 {
+ float base_gain, gain;
+ float b1, a1;
+ float z[1];
+};
+struct NfcFilter2 {
+ float base_gain, gain;
+ float b1, b2, a1, a2;
+ float z[2];
+};
+struct NfcFilter3 {
+ float base_gain, gain;
+ float b1, b2, b3, a1, a2, a3;
+ float z[3];
+};
+
+typedef struct NfcFilter {
+ struct NfcFilter1 first;
+ struct NfcFilter2 second;
+ struct NfcFilter3 third;
+} NfcFilter;
+
+
+/* NOTE:
+ * w0 = speed_of_sound / (source_distance * sample_rate);
+ * w1 = speed_of_sound / (control_distance * sample_rate);
+ *
+ * Generally speaking, the control distance should be approximately the average
+ * speaker distance, or based on the reference delay if outputing NFC-HOA. It
+ * must not be negative, 0, or infinite. The source distance should not be too
+ * small relative to the control distance.
+ */
+
+void NfcFilterCreate(NfcFilter *nfc, const float w0, const float w1);
+void NfcFilterAdjust(NfcFilter *nfc, const float w0);
+
+/* Near-field control filter for first-order ambisonic channels (1-3). */
+void NfcFilterProcess1(NfcFilter *nfc, float *restrict dst, const float *restrict src, const int count);
+
+/* Near-field control filter for second-order ambisonic channels (4-8). */
+void NfcFilterProcess2(NfcFilter *nfc, float *restrict dst, const float *restrict src, const int count);
+
+/* Near-field control filter for third-order ambisonic channels (9-15). */
+void NfcFilterProcess3(NfcFilter *nfc, float *restrict dst, const float *restrict src, const int count);
+
+#endif /* FILTER_NFC_H */
diff --git a/Alc/filters/splitter.c b/Alc/filters/splitter.c
new file mode 100644
index 00000000..e99f4b95
--- /dev/null
+++ b/Alc/filters/splitter.c
@@ -0,0 +1,109 @@
+
+#include "config.h"
+
+#include "splitter.h"
+
+#include "math_defs.h"
+
+
+void bandsplit_init(BandSplitter *splitter, ALfloat f0norm)
+{
+ ALfloat w = f0norm * F_TAU;
+ ALfloat cw = cosf(w);
+ if(cw > FLT_EPSILON)
+ splitter->coeff = (sinf(w) - 1.0f) / cw;
+ else
+ splitter->coeff = cw * -0.5f;
+
+ splitter->lp_z1 = 0.0f;
+ splitter->lp_z2 = 0.0f;
+ splitter->hp_z1 = 0.0f;
+}
+
+void bandsplit_clear(BandSplitter *splitter)
+{
+ splitter->lp_z1 = 0.0f;
+ splitter->lp_z2 = 0.0f;
+ splitter->hp_z1 = 0.0f;
+}
+
+void bandsplit_process(BandSplitter *splitter, ALfloat *restrict hpout, ALfloat *restrict lpout,
+ const ALfloat *input, ALsizei count)
+{
+ ALfloat lp_coeff, hp_coeff, lp_y, hp_y, d;
+ ALfloat lp_z1, lp_z2, hp_z1;
+ ALsizei i;
+
+ ASSUME(count > 0);
+
+ hp_coeff = splitter->coeff;
+ lp_coeff = splitter->coeff*0.5f + 0.5f;
+ lp_z1 = splitter->lp_z1;
+ lp_z2 = splitter->lp_z2;
+ hp_z1 = splitter->hp_z1;
+ for(i = 0;i < count;i++)
+ {
+ ALfloat in = input[i];
+
+ /* Low-pass sample processing. */
+ d = (in - lp_z1) * lp_coeff;
+ lp_y = lp_z1 + d;
+ lp_z1 = lp_y + d;
+
+ d = (lp_y - lp_z2) * lp_coeff;
+ lp_y = lp_z2 + d;
+ lp_z2 = lp_y + d;
+
+ lpout[i] = lp_y;
+
+ /* All-pass sample processing. */
+ hp_y = in*hp_coeff + hp_z1;
+ hp_z1 = in - hp_y*hp_coeff;
+
+ /* High-pass generated from removing low-passed output. */
+ hpout[i] = hp_y - lp_y;
+ }
+ splitter->lp_z1 = lp_z1;
+ splitter->lp_z2 = lp_z2;
+ splitter->hp_z1 = hp_z1;
+}
+
+
+void splitterap_init(SplitterAllpass *splitter, ALfloat f0norm)
+{
+ ALfloat w = f0norm * F_TAU;
+ ALfloat cw = cosf(w);
+ if(cw > FLT_EPSILON)
+ splitter->coeff = (sinf(w) - 1.0f) / cw;
+ else
+ splitter->coeff = cw * -0.5f;
+
+ splitter->z1 = 0.0f;
+}
+
+void splitterap_clear(SplitterAllpass *splitter)
+{
+ splitter->z1 = 0.0f;
+}
+
+void splitterap_process(SplitterAllpass *splitter, ALfloat *restrict samples, ALsizei count)
+{
+ ALfloat coeff, in, out;
+ ALfloat z1;
+ ALsizei i;
+
+ ASSUME(count > 0);
+
+ coeff = splitter->coeff;
+ z1 = splitter->z1;
+ for(i = 0;i < count;i++)
+ {
+ in = samples[i];
+
+ out = in*coeff + z1;
+ z1 = in - out*coeff;
+
+ samples[i] = out;
+ }
+ splitter->z1 = z1;
+}
diff --git a/Alc/filters/splitter.h b/Alc/filters/splitter.h
new file mode 100644
index 00000000..a788bc3e
--- /dev/null
+++ b/Alc/filters/splitter.h
@@ -0,0 +1,40 @@
+#ifndef FILTER_SPLITTER_H
+#define FILTER_SPLITTER_H
+
+#include "alMain.h"
+
+
+/* Band splitter. Splits a signal into two phase-matching frequency bands. */
+typedef struct BandSplitter {
+ ALfloat coeff;
+ ALfloat lp_z1;
+ ALfloat lp_z2;
+ ALfloat hp_z1;
+} BandSplitter;
+
+void bandsplit_init(BandSplitter *splitter, ALfloat f0norm);
+void bandsplit_clear(BandSplitter *splitter);
+void bandsplit_process(BandSplitter *splitter, ALfloat *restrict hpout, ALfloat *restrict lpout,
+ const ALfloat *input, ALsizei count);
+
+/* The all-pass portion of the band splitter. Applies the same phase shift
+ * without splitting the signal.
+ */
+typedef struct SplitterAllpass {
+ ALfloat coeff;
+ ALfloat z1;
+} SplitterAllpass;
+
+void splitterap_init(SplitterAllpass *splitter, ALfloat f0norm);
+void splitterap_clear(SplitterAllpass *splitter);
+void splitterap_process(SplitterAllpass *splitter, ALfloat *restrict samples, ALsizei count);
+
+
+typedef struct FrontStablizer {
+ SplitterAllpass APFilter[MAX_OUTPUT_CHANNELS];
+ BandSplitter LFilter, RFilter;
+ alignas(16) ALfloat LSplit[2][BUFFERSIZE];
+ alignas(16) ALfloat RSplit[2][BUFFERSIZE];
+} FrontStablizer;
+
+#endif /* FILTER_SPLITTER_H */
diff --git a/Alc/fpu_modes.h b/Alc/fpu_modes.h
new file mode 100644
index 00000000..eb305967
--- /dev/null
+++ b/Alc/fpu_modes.h
@@ -0,0 +1,34 @@
+#ifndef FPU_MODES_H
+#define FPU_MODES_H
+
+#ifdef HAVE_FENV_H
+#include <fenv.h>
+#endif
+
+
+typedef struct FPUCtl {
+#if defined(__GNUC__) && defined(HAVE_SSE)
+ unsigned int sse_state;
+#elif defined(HAVE___CONTROL87_2)
+ unsigned int state;
+ unsigned int sse_state;
+#elif defined(HAVE__CONTROLFP)
+ unsigned int state;
+#endif
+} FPUCtl;
+void SetMixerFPUMode(FPUCtl *ctl);
+void RestoreFPUMode(const FPUCtl *ctl);
+
+#ifdef __GNUC__
+/* Use an alternate macro set with GCC to avoid accidental continue or break
+ * statements within the mixer mode.
+ */
+#define START_MIXER_MODE() __extension__({ FPUCtl _oldMode; SetMixerFPUMode(&_oldMode)
+#define END_MIXER_MODE() RestoreFPUMode(&_oldMode); })
+#else
+#define START_MIXER_MODE() do { FPUCtl _oldMode; SetMixerFPUMode(&_oldMode)
+#define END_MIXER_MODE() RestoreFPUMode(&_oldMode); } while(0)
+#endif
+#define LEAVE_MIXER_MODE() RestoreFPUMode(&_oldMode)
+
+#endif /* FPU_MODES_H */
diff --git a/Alc/helpers.c b/Alc/helpers.c
index 7b9e58e0..d2cb6253 100644
--- a/Alc/helpers.c
+++ b/Alc/helpers.c
@@ -32,12 +32,21 @@
#include <time.h>
#include <errno.h>
#include <stdarg.h>
+#include <ctype.h>
#ifdef HAVE_MALLOC_H
#include <malloc.h>
#endif
#ifdef HAVE_DIRENT_H
#include <dirent.h>
#endif
+#ifdef HAVE_PROC_PIDPATH
+#include <libproc.h>
+#endif
+
+#ifdef __FreeBSD__
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#endif
#ifndef AL_NO_UID_DEFS
#if defined(HAVE_GUIDDEF_H) || defined(HAVE_INITGUID_H)
@@ -60,11 +69,13 @@ DEFINE_GUID(IID_IAudioClient, 0x1cb9ad4c, 0xdbfa, 0x4c32, 0xb1,0x78, 0xc
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
+#ifdef HAVE_WASAPI
+#include <wtypes.h>
#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);
+DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_GUID, 0x1da5d803, 0xd492, 0x4edd, 0x8c, 0x23,0xe0, 0xc0,0xff,0xee,0x7f,0x0e, 4 );
#endif
#endif
#endif /* AL_NO_UID_DEFS */
@@ -89,6 +100,10 @@ DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_FormFactor, 0x1da5d803, 0xd492, 0x4edd, 0x
#endif
#ifndef _WIN32
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <fcntl.h>
#include <unistd.h>
#elif defined(_WIN32_IE)
#include <shlobj.h>
@@ -96,6 +111,8 @@ DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_FormFactor, 0x1da5d803, 0xd492, 0x4edd, 0x
#include "alMain.h"
#include "alu.h"
+#include "cpu_caps.h"
+#include "fpu_modes.h"
#include "atomic.h"
#include "uintmap.h"
#include "vector.h"
@@ -105,71 +122,51 @@ DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_FormFactor, 0x1da5d803, 0xd492, 0x4edd, 0x
extern inline ALuint NextPowerOf2(ALuint value);
+extern inline size_t RoundUp(size_t value, size_t r);
extern inline ALint fastf2i(ALfloat f);
-extern inline ALuint fastf2u(ALfloat f);
+extern inline int float2int(float f);
+extern inline float fast_roundf(float f);
+#ifndef __GNUC__
+#if defined(HAVE_BITSCANFORWARD64_INTRINSIC)
+extern inline int msvc64_ctz64(ALuint64 v);
+#elif defined(HAVE_BITSCANFORWARD_INTRINSIC)
+extern inline int msvc_ctz64(ALuint64 v);
+#else
+extern inline int fallback_popcnt64(ALuint64 v);
+extern inline int fallback_ctz64(ALuint64 value);
+#endif
+#endif
-ALuint CPUCapFlags = 0;
+#if defined(HAVE_GCC_GET_CPUID) && (defined(__i386__) || defined(__x86_64__) || \
+ defined(_M_IX86) || defined(_M_X64))
+typedef unsigned int reg_type;
+static inline void get_cpuid(int f, reg_type *regs)
+{ __get_cpuid(f, &regs[0], &regs[1], &regs[2], &regs[3]); }
+#define CAN_GET_CPUID
+#elif defined(HAVE_CPUID_INTRINSIC) && (defined(__i386__) || defined(__x86_64__) || \
+ defined(_M_IX86) || defined(_M_X64))
+typedef int reg_type;
+static inline void get_cpuid(int f, reg_type *regs)
+{ (__cpuid)(regs, f); }
+#define CAN_GET_CPUID
+#endif
+int CPUCapFlags = 0;
-void FillCPUCaps(ALuint capfilter)
+void FillCPUCaps(int capfilter)
{
- ALuint caps = 0;
+ int caps = 0;
/* FIXME: We really should get this for all available CPUs in case different
* CPUs have different caps (is that possible on one machine?). */
-#if defined(HAVE_GCC_GET_CPUID) && (defined(__i386__) || defined(__x86_64__) || \
- defined(_M_IX86) || defined(_M_X64))
+#ifdef CAN_GET_CPUID
union {
- unsigned int regs[4];
- char str[sizeof(unsigned int[4])];
- } cpuinf[3];
+ reg_type regs[4];
+ char str[sizeof(reg_type[4])];
+ } cpuinf[3] = {{ { 0, 0, 0, 0 } }};
- if(!__get_cpuid(0, &cpuinf[0].regs[0], &cpuinf[0].regs[1], &cpuinf[0].regs[2], &cpuinf[0].regs[3]))
- ERR("Failed to get CPUID\n");
- else
- {
- unsigned int maxfunc = cpuinf[0].regs[0];
- unsigned int maxextfunc = 0;
-
- if(__get_cpuid(0x80000000, &cpuinf[0].regs[0], &cpuinf[0].regs[1], &cpuinf[0].regs[2], &cpuinf[0].regs[3]))
- maxextfunc = cpuinf[0].regs[0];
- TRACE("Detected max CPUID function: 0x%x (ext. 0x%x)\n", maxfunc, maxextfunc);
-
- TRACE("Vendor ID: \"%.4s%.4s%.4s\"\n", cpuinf[0].str+4, cpuinf[0].str+12, cpuinf[0].str+8);
- if(maxextfunc >= 0x80000004 &&
- __get_cpuid(0x80000002, &cpuinf[0].regs[0], &cpuinf[0].regs[1], &cpuinf[0].regs[2], &cpuinf[0].regs[3]) &&
- __get_cpuid(0x80000003, &cpuinf[1].regs[0], &cpuinf[1].regs[1], &cpuinf[1].regs[2], &cpuinf[1].regs[3]) &&
- __get_cpuid(0x80000004, &cpuinf[2].regs[0], &cpuinf[2].regs[1], &cpuinf[2].regs[2], &cpuinf[2].regs[3]))
- TRACE("Name: \"%.16s%.16s%.16s\"\n", cpuinf[0].str, cpuinf[1].str, cpuinf[2].str);
-
- if(maxfunc >= 1 &&
- __get_cpuid(1, &cpuinf[0].regs[0], &cpuinf[0].regs[1], &cpuinf[0].regs[2], &cpuinf[0].regs[3]))
- {
- if((cpuinf[0].regs[3]&(1<<25)))
- {
- caps |= CPU_CAP_SSE;
- if((cpuinf[0].regs[3]&(1<<26)))
- {
- caps |= CPU_CAP_SSE2;
- if((cpuinf[0].regs[2]&(1<<0)))
- {
- caps |= CPU_CAP_SSE3;
- if((cpuinf[0].regs[2]&(1<<19)))
- caps |= CPU_CAP_SSE4_1;
- }
- }
- }
- }
- }
-#elif defined(HAVE_CPUID_INTRINSIC) && (defined(__i386__) || defined(__x86_64__) || \
- defined(_M_IX86) || defined(_M_X64))
- union {
- int regs[4];
- char str[sizeof(int[4])];
- } cpuinf[3];
-
- (__cpuid)(cpuinf[0].regs, 0);
+ get_cpuid(0, cpuinf[0].regs);
if(cpuinf[0].regs[0] == 0)
ERR("Failed to get CPUID\n");
else
@@ -177,7 +174,7 @@ void FillCPUCaps(ALuint capfilter)
unsigned int maxfunc = cpuinf[0].regs[0];
unsigned int maxextfunc;
- (__cpuid)(cpuinf[0].regs, 0x80000000);
+ get_cpuid(0x80000000, cpuinf[0].regs);
maxextfunc = cpuinf[0].regs[0];
TRACE("Detected max CPUID function: 0x%x (ext. 0x%x)\n", maxfunc, maxextfunc);
@@ -185,29 +182,23 @@ void FillCPUCaps(ALuint capfilter)
TRACE("Vendor ID: \"%.4s%.4s%.4s\"\n", cpuinf[0].str+4, cpuinf[0].str+12, cpuinf[0].str+8);
if(maxextfunc >= 0x80000004)
{
- (__cpuid)(cpuinf[0].regs, 0x80000002);
- (__cpuid)(cpuinf[1].regs, 0x80000003);
- (__cpuid)(cpuinf[2].regs, 0x80000004);
+ get_cpuid(0x80000002, cpuinf[0].regs);
+ get_cpuid(0x80000003, cpuinf[1].regs);
+ get_cpuid(0x80000004, cpuinf[2].regs);
TRACE("Name: \"%.16s%.16s%.16s\"\n", cpuinf[0].str, cpuinf[1].str, cpuinf[2].str);
}
if(maxfunc >= 1)
{
- (__cpuid)(cpuinf[0].regs, 1);
+ get_cpuid(1, cpuinf[0].regs);
if((cpuinf[0].regs[3]&(1<<25)))
- {
caps |= CPU_CAP_SSE;
- if((cpuinf[0].regs[3]&(1<<26)))
- {
- caps |= CPU_CAP_SSE2;
- if((cpuinf[0].regs[2]&(1<<0)))
- {
- caps |= CPU_CAP_SSE3;
- if((cpuinf[0].regs[2]&(1<<19)))
- caps |= CPU_CAP_SSE4_1;
- }
- }
- }
+ if((caps&CPU_CAP_SSE) && (cpuinf[0].regs[3]&(1<<26)))
+ caps |= CPU_CAP_SSE2;
+ if((caps&CPU_CAP_SSE2) && (cpuinf[0].regs[2]&(1<<0)))
+ caps |= CPU_CAP_SSE3;
+ if((caps&CPU_CAP_SSE3) && (cpuinf[0].regs[2]&(1<<19)))
+ caps |= CPU_CAP_SSE4_1;
}
}
#else
@@ -227,8 +218,50 @@ void FillCPUCaps(ALuint capfilter)
#endif
#endif
#ifdef HAVE_NEON
- /* Assume Neon support if compiled with it */
- caps |= CPU_CAP_NEON;
+ FILE *file = fopen("/proc/cpuinfo", "rt");
+ if(!file)
+ ERR("Failed to open /proc/cpuinfo, cannot check for NEON support\n");
+ else
+ {
+ al_string features = AL_STRING_INIT_STATIC();
+ char buf[256];
+
+ while(fgets(buf, sizeof(buf), file) != NULL)
+ {
+ if(strncmp(buf, "Features\t:", 10) != 0)
+ continue;
+
+ alstr_copy_cstr(&features, buf+10);
+ while(VECTOR_BACK(features) != '\n')
+ {
+ if(fgets(buf, sizeof(buf), file) == NULL)
+ break;
+ alstr_append_cstr(&features, buf);
+ }
+ break;
+ }
+ fclose(file);
+ file = NULL;
+
+ if(!alstr_empty(features))
+ {
+ const char *str = alstr_get_cstr(features);
+ while(isspace(str[0])) ++str;
+
+ TRACE("Got features string:%s\n", str);
+ while((str=strstr(str, "neon")) != NULL)
+ {
+ if(isspace(*(str-1)) && (str[4] == 0 || isspace(str[4])))
+ {
+ caps |= CPU_CAP_NEON;
+ break;
+ }
+ ++str;
+ }
+ }
+
+ alstr_reset(&features);
+ }
#endif
TRACE("Extensions:%s%s%s%s%s%s\n",
@@ -236,136 +269,125 @@ void FillCPUCaps(ALuint capfilter)
((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&CPU_CAP_NEON) ? ((caps&CPU_CAP_NEON) ? " +NEON" : " -NEON") : ""),
((!capfilter) ? " -none-" : "")
);
CPUCapFlags = caps & capfilter;
}
-void *al_malloc(size_t alignment, size_t size)
-{
-#if defined(HAVE_ALIGNED_ALLOC)
- size = (size+(alignment-1))&~(alignment-1);
- return aligned_alloc(alignment, size);
-#elif defined(HAVE_POSIX_MEMALIGN)
- void *ret;
- if(posix_memalign(&ret, alignment, size) == 0)
- return ret;
- return NULL;
-#elif defined(HAVE__ALIGNED_MALLOC)
- return _aligned_malloc(size, alignment);
-#else
- char *ret = malloc(size+alignment);
- if(ret != NULL)
- {
- *(ret++) = 0x00;
- while(((ptrdiff_t)ret&(alignment-1)) != 0)
- *(ret++) = 0x55;
- }
- return ret;
-#endif
-}
-
-void *al_calloc(size_t alignment, size_t size)
-{
- void *ret = al_malloc(alignment, size);
- if(ret) memset(ret, 0, size);
- return ret;
-}
-
-void al_free(void *ptr)
-{
-#if defined(HAVE_ALIGNED_ALLOC) || defined(HAVE_POSIX_MEMALIGN)
- free(ptr);
-#elif defined(HAVE__ALIGNED_MALLOC)
- _aligned_free(ptr);
-#else
- if(ptr != NULL)
- {
- char *finder = ptr;
- do {
- --finder;
- } while(*finder == 0x55);
- free(finder);
- }
-#endif
-}
-
-
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
-
-#ifdef FE_TOWARDZERO
- fesetround(FE_TOWARDZERO);
-#endif
#if defined(__GNUC__) && defined(HAVE_SSE)
if((CPUCapFlags&CPU_CAP_SSE))
{
- int sseState = ctl->sse_state;
- sseState |= 0x6000; /* set round-to-zero */
+ __asm__ __volatile__("stmxcsr %0" : "=m" (*&ctl->sse_state));
+ unsigned int sseState = ctl->sse_state;
sseState |= 0x8000; /* set flush-to-zero */
if((CPUCapFlags&CPU_CAP_SSE2))
sseState |= 0x0040; /* set denormals-are-zero */
__asm__ __volatile__("ldmxcsr %0" : : "m" (*&sseState));
}
-#endif
#elif defined(HAVE___CONTROL87_2)
- int mode;
- __control87_2(0, 0, &ctl->state, NULL);
- __control87_2(_RC_CHOP, _MCW_RC, &mode, NULL);
-#ifdef HAVE_SSE
- if((CPUCapFlags&CPU_CAP_SSE))
- {
- __control87_2(0, 0, NULL, &ctl->sse_state);
- __control87_2(_RC_CHOP|_DN_FLUSH, _MCW_RC|_MCW_DN, NULL, &mode);
- }
-#endif
+ __control87_2(0, 0, &ctl->state, &ctl->sse_state);
+ _control87(_DN_FLUSH, _MCW_DN);
#elif defined(HAVE__CONTROLFP)
ctl->state = _controlfp(0, 0);
- (void)_controlfp(_RC_CHOP, _MCW_RC);
+ _controlfp(_DN_FLUSH, _MCW_DN);
#endif
}
void RestoreFPUMode(const FPUCtl *ctl)
{
-#ifdef HAVE_FENV_H
- fesetenv(STATIC_CAST(fenv_t, ctl));
#if defined(__GNUC__) && defined(HAVE_SSE)
if((CPUCapFlags&CPU_CAP_SSE))
__asm__ __volatile__("ldmxcsr %0" : : "m" (*&ctl->sse_state));
-#endif
#elif defined(HAVE___CONTROL87_2)
int mode;
- __control87_2(ctl->state, _MCW_RC, &mode, NULL);
-#ifdef HAVE_SSE
- if((CPUCapFlags&CPU_CAP_SSE))
- __control87_2(ctl->sse_state, _MCW_RC|_MCW_DN, NULL, &mode);
-#endif
+ __control87_2(ctl->state, _MCW_DN, &mode, NULL);
+ __control87_2(ctl->sse_state, _MCW_DN, NULL, &mode);
#elif defined(HAVE__CONTROLFP)
- _controlfp(ctl->state, _MCW_RC);
+ _controlfp(ctl->state, _MCW_DN);
#endif
}
+static int StringSortCompare(const void *str1, const void *str2)
+{
+ return alstr_cmp(*(const_al_string*)str1, *(const_al_string*)str2);
+}
+
#ifdef _WIN32
+static WCHAR *strrchrW(WCHAR *str, WCHAR ch)
+{
+ WCHAR *ret = NULL;
+ while(*str)
+ {
+ if(*str == ch)
+ ret = str;
+ ++str;
+ }
+ return ret;
+}
+
+void GetProcBinary(al_string *path, al_string *fname)
+{
+ WCHAR *pathname, *sep;
+ DWORD pathlen;
+ DWORD len;
+
+ pathlen = 256;
+ pathname = malloc(pathlen * sizeof(pathname[0]));
+ while(pathlen > 0 && (len=GetModuleFileNameW(NULL, pathname, pathlen)) == pathlen)
+ {
+ free(pathname);
+ pathlen <<= 1;
+ pathname = malloc(pathlen * sizeof(pathname[0]));
+ }
+ if(len == 0)
+ {
+ free(pathname);
+ ERR("Failed to get process name: error %lu\n", GetLastError());
+ return;
+ }
+
+ pathname[len] = 0;
+ if((sep=strrchrW(pathname, '\\')) != NULL)
+ {
+ WCHAR *sep2 = strrchrW(sep+1, '/');
+ if(sep2) sep = sep2;
+ }
+ else
+ sep = strrchrW(pathname, '/');
+
+ if(sep)
+ {
+ if(path) alstr_copy_wrange(path, pathname, sep);
+ if(fname) alstr_copy_wcstr(fname, sep+1);
+ }
+ else
+ {
+ if(path) alstr_clear(path);
+ if(fname) alstr_copy_wcstr(fname, pathname);
+ }
+ free(pathname);
+
+ if(path && fname)
+ TRACE("Got: %s, %s\n", alstr_get_cstr(*path), alstr_get_cstr(*fname));
+ else if(path) TRACE("Got path: %s\n", alstr_get_cstr(*path));
+ else if(fname) TRACE("Got filename: %s\n", alstr_get_cstr(*fname));
+}
+
+
static WCHAR *FromUTF8(const char *str)
{
WCHAR *out = NULL;
@@ -471,363 +493,290 @@ void al_print(const char *type, const char *func, const char *fmt, ...)
static inline int is_slash(int c)
{ return (c == '\\' || c == '/'); }
-FILE *OpenDataFile(const char *fname, const char *subdir)
+static void DirectorySearch(const char *path, const char *ext, vector_al_string *results)
{
- static const int ids[2] = { CSIDL_APPDATA, CSIDL_COMMON_APPDATA };
- WCHAR *wname=NULL, *wsubdir=NULL;
- FILE *f;
- size_t i;
+ al_string pathstr = AL_STRING_INIT_STATIC();
+ WIN32_FIND_DATAW fdata;
+ WCHAR *wpath;
+ HANDLE hdl;
- wname = FromUTF8(fname);
- if(!wname)
- {
- ERR("Failed to convert UTF-8 filename: \"%s\"\n", fname);
- return NULL;
- }
+ alstr_copy_cstr(&pathstr, path);
+ alstr_append_cstr(&pathstr, "\\*");
+ alstr_append_cstr(&pathstr, ext);
- /* 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;
- }
+ TRACE("Searching %s\n", alstr_get_cstr(pathstr));
- /* 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;
- }
+ wpath = FromUTF8(alstr_get_cstr(pathstr));
- for(i = 0;i < COUNTOF(ids);i++)
+ hdl = FindFirstFileW(wpath, &fdata);
+ if(hdl != INVALID_HANDLE_VALUE)
{
- 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;
- }
+ size_t base = VECTOR_SIZE(*results);
+ do {
+ al_string str = AL_STRING_INIT_STATIC();
+ alstr_copy_cstr(&str, path);
+ alstr_append_char(&str, '\\');
+ alstr_append_wcstr(&str, fdata.cFileName);
+ TRACE("Got result %s\n", alstr_get_cstr(str));
+ VECTOR_PUSH_BACK(*results, str);
+ } while(FindNextFileW(hdl, &fdata));
+ FindClose(hdl);
+
+ if(VECTOR_SIZE(*results) > base)
+ qsort(VECTOR_BEGIN(*results)+base, VECTOR_SIZE(*results)-base,
+ sizeof(VECTOR_FRONT(*results)), StringSortCompare);
}
- free(wname);
- free(wsubdir);
- if(f == NULL)
- WARN("Could not open %s\\%s\n", subdir, fname);
- return f;
+ free(wpath);
+ alstr_reset(&pathstr);
}
-
-static const WCHAR *strchrW(const WCHAR *str, WCHAR ch)
+vector_al_string SearchDataFiles(const char *ext, const char *subdir)
{
- for(;*str != 0;++str)
- {
- if(*str == ch)
- return str;
- }
- return NULL;
-}
+ static const int ids[2] = { CSIDL_APPDATA, CSIDL_COMMON_APPDATA };
+ static RefCount search_lock;
+ vector_al_string results = VECTOR_INIT_STATIC();
+ size_t i;
-static const WCHAR *strrchrW(const WCHAR *str, WCHAR ch)
-{
- const WCHAR *ret = NULL;
- for(;*str != 0;++str)
+ while(ATOMIC_EXCHANGE_SEQ(&search_lock, 1) == 1)
+ althrd_yield();
+
+ /* If the path is absolute, use it directly. */
+ if(isalpha(subdir[0]) && subdir[1] == ':' && is_slash(subdir[2]))
{
- if(*str == ch)
- ret = str;
+ al_string path = AL_STRING_INIT_STATIC();
+ alstr_copy_cstr(&path, subdir);
+#define FIX_SLASH(i) do { if(*(i) == '/') *(i) = '\\'; } while(0)
+ VECTOR_FOR_EACH(char, path, FIX_SLASH);
+#undef FIX_SLASH
+
+ DirectorySearch(alstr_get_cstr(path), ext, &results);
+
+ alstr_reset(&path);
}
- return ret;
-}
+ else if(subdir[0] == '\\' && subdir[1] == '\\' && subdir[2] == '?' && subdir[3] == '\\')
+ DirectorySearch(subdir, ext, &results);
+ else
+ {
+ al_string path = AL_STRING_INIT_STATIC();
+ WCHAR *cwdbuf;
-/* 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;
+ /* Search the app-local directory. */
+ if((cwdbuf=_wgetenv(L"ALSOFT_LOCAL_PATH")) && *cwdbuf != '\0')
+ {
+ alstr_copy_wcstr(&path, cwdbuf);
+ if(is_slash(VECTOR_BACK(path)))
+ {
+ VECTOR_POP_BACK(path);
+ *VECTOR_END(path) = 0;
+ }
+ }
+ else if(!(cwdbuf=_wgetcwd(NULL, 0)))
+ alstr_copy_cstr(&path, ".");
else
{
- int len = p-match;
- ret = lstrlenW(name) >= len;
- if(ret)
- ret = CompareStringW(GetThreadLocale(), NORM_IGNORECASE,
- match, len, name, len) == CSTR_EQUAL;
- if(ret)
+ alstr_copy_wcstr(&path, cwdbuf);
+ if(is_slash(VECTOR_BACK(path)))
{
- 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;
- }
+ VECTOR_POP_BACK(path);
+ *VECTOR_END(path) = 0;
}
+ free(cwdbuf);
}
+#define FIX_SLASH(i) do { if(*(i) == '/') *(i) = '\\'; } while(0)
+ VECTOR_FOR_EACH(char, path, FIX_SLASH);
+#undef FIX_SLASH
+ DirectorySearch(alstr_get_cstr(path), ext, &results);
- 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;
+ /* Search the local and global data dirs. */
+ for(i = 0;i < COUNTOF(ids);i++)
+ {
+ WCHAR buffer[MAX_PATH];
+ if(SHGetSpecialFolderPathW(NULL, buffer, ids[i], FALSE) != FALSE)
+ {
+ alstr_copy_wcstr(&path, buffer);
+ if(!is_slash(VECTOR_BACK(path)))
+ alstr_append_char(&path, '\\');
+ alstr_append_cstr(&path, subdir);
+#define FIX_SLASH(i) do { if(*(i) == '/') *(i) = '\\'; } while(0)
+ VECTOR_FOR_EACH(char, path, FIX_SLASH);
+#undef FIX_SLASH
- if(!match[0])
- return;
+ DirectorySearch(alstr_get_cstr(path), ext, &results);
+ }
+ }
- /* Find the last directory separator and the next '%' marker in the match
- * string. */
- sep = strrchrW(match, '\\');
- p = strchrW(match, '%');
+ alstr_reset(&path);
+ }
- /* 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;
+ ATOMIC_STORE_SEQ(&search_lock, 0);
- TRACE("Searching %s for %ls\n", path, match);
+ return results;
+}
- 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);
- }
+struct FileMapping MapFileToMem(const char *fname)
+{
+ struct FileMapping ret = { NULL, NULL, NULL, 0 };
+ MEMORY_BASIC_INFORMATION meminfo;
+ HANDLE file, fmap;
+ WCHAR *wname;
+ void *ptr;
- free(wpath);
- al_string_deinit(&pathstr);
+ wname = FromUTF8(fname);
- return;
+ file = CreateFileW(wname, GENERIC_READ, FILE_SHARE_READ, NULL,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if(file == INVALID_HANDLE_VALUE)
+ {
+ ERR("Failed to open %s: %lu\n", fname, GetLastError());
+ free(wname);
+ return ret;
}
+ free(wname);
+ wname = NULL;
- /* 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)
+ fmap = CreateFileMappingW(file, NULL, PAGE_READONLY, 0, 0, NULL);
+ if(!fmap)
{
- 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;
+ ERR("Failed to create map for %s: %lu\n", fname, GetLastError());
+ CloseHandle(file);
+ return ret;
}
- /* Look for the last separator before the '%' marker, and the first
- * separator after it. */
- sep = strchrW(match, '\\');
- if(sep-p >= 0) sep = NULL;
- for(;;)
+ ptr = MapViewOfFile(fmap, FILE_MAP_READ, 0, 0, 0);
+ if(!ptr)
{
- 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);
- }
+ ERR("Failed to map %s: %lu\n", fname, GetLastError());
+ CloseHandle(fmap);
+ CloseHandle(file);
+ return ret;
+ }
- free(nwmatch);
- free(nwpath);
- al_string_deinit(&npath);
- break;
- }
- sep = next;
+ if(VirtualQuery(ptr, &meminfo, sizeof(meminfo)) != sizeof(meminfo))
+ {
+ ERR("Failed to get map size for %s: %lu\n", fname, GetLastError());
+ UnmapViewOfFile(ptr);
+ CloseHandle(fmap);
+ CloseHandle(file);
+ return ret;
}
+
+ ret.file = file;
+ ret.fmap = fmap;
+ ret.ptr = ptr;
+ ret.len = meminfo.RegionSize;
+ return ret;
}
-vector_al_string SearchDataFiles(const char *match, const char *subdir)
+void UnmapFileMem(const struct FileMapping *mapping)
{
- 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;
+ UnmapViewOfFile(mapping->ptr);
+ CloseHandle(mapping->fmap);
+ CloseHandle(mapping->file);
+}
- while(ATOMIC_EXCHANGE(uint, &search_lock, 1) == 1)
- althrd_yield();
+#else
- 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] = '\\';
- }
+void GetProcBinary(al_string *path, al_string *fname)
+{
+ char *pathname = NULL;
+ size_t pathlen;
- /* If the path is absolute, use it directly. */
- if(isalpha(wmatch[0]) && wmatch[1] == ':' && is_slash(wmatch[2]))
+#ifdef __FreeBSD__
+ int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };
+ if(sysctl(mib, 4, NULL, &pathlen, NULL, 0) == -1)
+ WARN("Failed to sysctl kern.proc.pathname: %s\n", strerror(errno));
+ else
{
- char drv[3] = { (char)wmatch[0], ':', 0 };
- RecurseDirectorySearch(drv, wmatch+3, &results);
+ pathname = malloc(pathlen + 1);
+ sysctl(mib, 4, (void*)pathname, &pathlen, NULL, 0);
+ pathname[pathlen] = 0;
}
- else if(wmatch[0] == '\\' && wmatch[1] == '\\' && wmatch[2] == '?' && wmatch[3] == '\\')
- RecurseDirectorySearch("\\\\?", wmatch+4, &results);
- else
+#endif
+#ifdef HAVE_PROC_PIDPATH
+ if(!pathname)
{
- al_string path = AL_STRING_INIT_STATIC();
- WCHAR *cwdbuf;
+ const pid_t pid = getpid();
+ char procpath[PROC_PIDPATHINFO_MAXSIZE];
+ int ret;
- /* Search the CWD. */
- if(!(cwdbuf=_wgetcwd(NULL, 0)))
- al_string_copy_cstr(&path, ".");
+ ret = proc_pidpath(pid, procpath, sizeof(procpath));
+ if(ret < 1)
+ {
+ WARN("proc_pidpath(%d, ...) failed: %s\n", pid, strerror(errno));
+ free(pathname);
+ pathname = NULL;
+ }
else
{
- al_string_copy_wcstr(&path, cwdbuf);
- if(is_slash(VECTOR_BACK(path)))
- {
- VECTOR_POP_BACK(path);
- *VECTOR_ITER_END(path) = 0;
- }
- free(cwdbuf);
+ pathlen = strlen(procpath);
+ pathname = strdup(procpath);
}
- RecurseDirectorySearch(al_string_get_cstr(path), wmatch, &results);
+ }
+#endif
+ if(!pathname)
+ {
+ const char *selfname;
+ ssize_t len;
- /* Search the local and global data dirs. */
- for(i = 0;i < COUNTOF(ids);i++)
+ pathlen = 256;
+ pathname = malloc(pathlen);
+
+ selfname = "/proc/self/exe";
+ len = readlink(selfname, pathname, pathlen);
+ if(len == -1 && errno == ENOENT)
{
- 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
+ selfname = "/proc/self/file";
+ len = readlink(selfname, pathname, pathlen);
+ }
+ if(len == -1 && errno == ENOENT)
+ {
+ selfname = "/proc/curproc/exe";
+ len = readlink(selfname, pathname, pathlen);
+ }
+ if(len == -1 && errno == ENOENT)
+ {
+ selfname = "/proc/curproc/file";
+ len = readlink(selfname, pathname, pathlen);
+ }
- RecurseDirectorySearch(al_string_get_cstr(path), wmatch, &results);
- }
+ while(len > 0 && (size_t)len == pathlen)
+ {
+ free(pathname);
+ pathlen <<= 1;
+ pathname = malloc(pathlen);
+ len = readlink(selfname, pathname, pathlen);
+ }
+ if(len <= 0)
+ {
+ free(pathname);
+ WARN("Failed to readlink %s: %s\n", selfname, strerror(errno));
+ return;
}
- al_string_deinit(&path);
+ pathname[len] = 0;
}
- free(wmatch);
- ATOMIC_STORE(&search_lock, 0);
+ char *sep = strrchr(pathname, '/');
+ if(sep)
+ {
+ if(path) alstr_copy_range(path, pathname, sep);
+ if(fname) alstr_copy_cstr(fname, sep+1);
+ }
+ else
+ {
+ if(path) alstr_clear(path);
+ if(fname) alstr_copy_cstr(fname, pathname);
+ }
+ free(pathname);
- return results;
+ if(path && fname)
+ TRACE("Got: %s, %s\n", alstr_get_cstr(*path), alstr_get_cstr(*fname));
+ else if(path) TRACE("Got path: %s\n", alstr_get_cstr(*path));
+ else if(fname) TRACE("Got filename: %s\n", alstr_get_cstr(*fname));
}
-#else
#ifdef HAVE_DLFCN_H
@@ -874,251 +823,108 @@ void al_print(const char *type, const char *func, const char *fmt, ...)
}
-FILE *OpenDataFile(const char *fname, const char *subdir)
+static void DirectorySearch(const char *path, const char *ext, vector_al_string *results)
{
- char buffer[PATH_MAX] = "";
- const char *str, *next;
- FILE *f;
-
- if(fname[0] == '/')
- {
- if((f=al_fopen(fname, "rb")) != NULL)
- {
- TRACE("Opened %s\n", fname);
- return f;
- }
- WARN("Could not open %s\n", fname);
- return NULL;
- }
-
- if((f=al_fopen(fname, "rb")) != NULL)
- {
- TRACE("Opened %s\n", fname);
- return f;
- }
-
- if((str=getenv("XDG_DATA_HOME")) != NULL && str[0] != '\0')
- snprintf(buffer, sizeof(buffer), "%s/%s/%s", str, subdir, fname);
- else if((str=getenv("HOME")) != NULL && str[0] != '\0')
- snprintf(buffer, sizeof(buffer), "%s/.local/share/%s/%s", str, subdir, fname);
- if(buffer[0])
- {
- if((f=al_fopen(buffer, "rb")) != NULL)
- {
- TRACE("Opened %s\n", buffer);
- return f;
- }
- }
+ size_t extlen = strlen(ext);
+ DIR *dir;
- 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')
+ TRACE("Searching %s for *%s\n", path, ext);
+ dir = opendir(path);
+ if(dir != NULL)
{
- size_t len;
- next = strchr(str, ':');
-
- if(!next)
- len = strlen(str);
- else
+ size_t base = VECTOR_SIZE(*results);
+ struct dirent *dirent;
+ while((dirent=readdir(dir)) != NULL)
{
- len = next - str;
- next++;
+ al_string str;
+ size_t len;
+ if(strcmp(dirent->d_name, ".") == 0 || strcmp(dirent->d_name, "..") == 0)
+ continue;
+
+ len = strlen(dirent->d_name);
+ if(!(len > extlen))
+ continue;
+ if(strcasecmp(dirent->d_name+len-extlen, ext) != 0)
+ continue;
+
+ AL_STRING_INIT(str);
+ alstr_copy_cstr(&str, path);
+ if(VECTOR_BACK(str) != '/')
+ alstr_append_char(&str, '/');
+ alstr_append_cstr(&str, dirent->d_name);
+ TRACE("Got result %s\n", alstr_get_cstr(str));
+ VECTOR_PUSH_BACK(*results, str);
}
+ closedir(dir);
- if(len > sizeof(buffer)-1)
- len = sizeof(buffer)-1;
- strncpy(buffer, str, len);
- buffer[len] = '\0';
- snprintf(buffer+len, sizeof(buffer)-len, "/%s/%s", subdir, fname);
-
- if((f=al_fopen(buffer, "rb")) != NULL)
- {
- TRACE("Opened %s\n", buffer);
- return f;
- }
+ if(VECTOR_SIZE(*results) > base)
+ qsort(VECTOR_BEGIN(*results)+base, VECTOR_SIZE(*results)-base,
+ sizeof(VECTOR_FRONT(*results)), StringSortCompare);
}
- 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)
+vector_al_string SearchDataFiles(const char *ext, const char *subdir)
{
- 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);
- }
+ static RefCount search_lock;
+ vector_al_string results = VECTOR_INIT_STATIC();
- return;
- }
+ while(ATOMIC_EXCHANGE_SEQ(&search_lock, 1) == 1)
+ althrd_yield();
- if(!p || p-sep >= 0)
+ if(subdir[0] == '/')
+ DirectorySearch(subdir, ext, &results);
+ else
{
- 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;
- }
+ al_string path = AL_STRING_INIT_STATIC();
+ const char *str, *next;
- sep = strchr(match, '/');
- if(sep-p >= 0) sep = NULL;
- for(;;)
- {
- char *next = strchr(sep?sep+1:match, '/');
- if(next-p < 0)
+ /* Search the app-local directory. */
+ if((str=getenv("ALSOFT_LOCAL_PATH")) && *str != '\0')
+ DirectorySearch(str, ext, &results);
+ else
{
- al_string npath = AL_STRING_INIT_STATIC();
- al_string nmatch = AL_STRING_INIT_STATIC();
-
- if(!sep)
+ size_t cwdlen = 256;
+ char *cwdbuf = malloc(cwdlen);
+ while(!getcwd(cwdbuf, cwdlen))
{
- al_string_append_cstr(&npath, path?path:"/.");
- MatchString = match;
+ free(cwdbuf);
+ cwdbuf = NULL;
+ if(errno != ERANGE)
+ break;
+ cwdlen <<= 1;
+ cwdbuf = malloc(cwdlen);
}
+ if(!cwdbuf)
+ DirectorySearch(".", ext, &results);
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);
+ DirectorySearch(cwdbuf, ext, &results);
+ free(cwdbuf);
+ cwdbuf = NULL;
}
-
- 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);
+ alstr_copy_cstr(&path, str);
+ if(VECTOR_BACK(path) != '/')
+ alstr_append_char(&path, '/');
+ alstr_append_cstr(&path, subdir);
+ DirectorySearch(alstr_get_cstr(path), ext, &results);
}
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);
+ alstr_copy_cstr(&path, str);
+ if(VECTOR_BACK(path) == '/')
+ {
+ VECTOR_POP_BACK(path);
+ *VECTOR_END(path) = 0;
+ }
+ alstr_append_cstr(&path, "/.local/share/");
+ alstr_append_cstr(&path, subdir);
+ DirectorySearch(alstr_get_cstr(path), ext, &results);
}
- 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')
@@ -1129,30 +935,71 @@ vector_al_string SearchDataFiles(const char *match, const char *subdir)
{
next = strchr(str, ':');
if(!next)
- al_string_copy_cstr(&path, str);
+ alstr_copy_cstr(&path, str);
else
{
- al_string_clear(&path);
- al_string_append_range(&path, str, next);
+ alstr_copy_range(&path, str, next);
++next;
}
- if(!al_string_empty(path))
+ if(!alstr_empty(path))
{
- al_string_append_char(&path, '/');
- al_string_append_cstr(&path, subdir);
+ if(VECTOR_BACK(path) != '/')
+ alstr_append_char(&path, '/');
+ alstr_append_cstr(&path, subdir);
- RecurseDirectorySearch(al_string_get_cstr(path), match, &results);
+ DirectorySearch(alstr_get_cstr(path), ext, &results);
}
}
- al_string_deinit(&path);
+ alstr_reset(&path);
}
- ATOMIC_STORE(&search_lock, 0);
+ ATOMIC_STORE_SEQ(&search_lock, 0);
return results;
}
+
+struct FileMapping MapFileToMem(const char *fname)
+{
+ struct FileMapping ret = { -1, NULL, 0 };
+ struct stat sbuf;
+ void *ptr;
+ int fd;
+
+ fd = open(fname, O_RDONLY, 0);
+ if(fd == -1)
+ {
+ ERR("Failed to open %s: (%d) %s\n", fname, errno, strerror(errno));
+ return ret;
+ }
+ if(fstat(fd, &sbuf) == -1)
+ {
+ ERR("Failed to stat %s: (%d) %s\n", fname, errno, strerror(errno));
+ close(fd);
+ return ret;
+ }
+
+ ptr = mmap(NULL, sbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if(ptr == MAP_FAILED)
+ {
+ ERR("Failed to map %s: (%d) %s\n", fname, errno, strerror(errno));
+ close(fd);
+ return ret;
+ }
+
+ ret.fd = fd;
+ ret.ptr = ptr;
+ ret.len = sbuf.st_size;
+ return ret;
+}
+
+void UnmapFileMem(const struct FileMapping *mapping)
+{
+ munmap(mapping->ptr, mapping->len);
+ close(mapping->fd);
+}
+
#endif
@@ -1181,93 +1028,26 @@ void SetRTPriority(void)
}
-ALboolean vector_reserve(char *ptr, size_t base_size, size_t obj_size, size_t obj_count, ALboolean exact)
-{
- vector_ *vecptr = (vector_*)ptr;
- if((*vecptr ? (*vecptr)->Capacity : 0) < obj_count)
- {
- 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 && obj_count < INT_MAX)
- obj_count = NextPowerOf2((ALuint)obj_count);
-
- /* 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,
- * sizeof(*vector_) may not equal base_size). */
- temp = realloc(*vecptr, base_size + obj_size*obj_count);
- if(temp == NULL) return AL_FALSE;
-
- *vecptr = temp;
- (*vecptr)->Capacity = obj_count;
- (*vecptr)->Size = old_size;
- }
- return AL_TRUE;
-}
-
-ALboolean vector_resize(char *ptr, size_t base_size, size_t obj_size, size_t obj_count)
-{
- vector_ *vecptr = (vector_*)ptr;
- if(*vecptr || obj_count > 0)
- {
- if(!vector_reserve((char*)vecptr, base_size, obj_size, obj_count, AL_TRUE))
- return AL_FALSE;
- (*vecptr)->Size = obj_count;
- }
- return AL_TRUE;
-}
+extern inline void alstr_reset(al_string *str);
+extern inline size_t alstr_length(const_al_string str);
+extern inline ALboolean alstr_empty(const_al_string str);
+extern inline const al_string_char_type *alstr_get_cstr(const_al_string str);
-ALboolean vector_insert(char *ptr, size_t base_size, size_t obj_size, void *ins_pos, const void *datstart, const void *datend)
+void alstr_clear(al_string *str)
{
- vector_ *vecptr = (vector_*)ptr;
- if(datstart != datend)
+ if(!alstr_empty(*str))
{
- ptrdiff_t ins_elem = (*vecptr ? ((char*)ins_pos - ((char*)(*vecptr) + base_size)) :
- ((char*)ins_pos - (char*)NULL)) /
- obj_size;
- ptrdiff_t numins = ((const char*)datend - (const char*)datstart) / obj_size;
-
- assert(numins > 0);
- 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((size_t)ins_elem < (*vecptr)->Size)
- {
- memmove((char*)(*vecptr) + base_size + ((ins_elem+numins)*obj_size),
- (char*)(*vecptr) + base_size + ((ins_elem )*obj_size),
- ((*vecptr)->Size-ins_elem)*obj_size);
- }
- memcpy((char*)(*vecptr) + base_size + (ins_elem*obj_size),
- datstart, numins*obj_size);
- (*vecptr)->Size += numins;
+ /* Reserve one more character than the total size of the string. This
+ * is to ensure we have space to add a null terminator in the string
+ * data so it can be used as a C-style string.
+ */
+ VECTOR_RESIZE(*str, 0, 1);
+ VECTOR_ELEM(*str, 0) = 0;
}
- return AL_TRUE;
-}
-
-
-extern inline void al_string_deinit(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);
-
-void al_string_clear(al_string *str)
-{
- /* Reserve one more character than the total size of the string. This is to
- * ensure we have space to add a null terminator in the string data so it
- * can be used as a C-style string. */
- VECTOR_RESERVE(*str, 1);
- VECTOR_RESIZE(*str, 0);
- *VECTOR_ITER_END(*str) = 0;
}
-static inline int al_string_compare(const al_string_char_type *str1, size_t str1len,
- const al_string_char_type *str2, size_t str2len)
+static inline int alstr_compare(const al_string_char_type *str1, size_t str1len,
+ const al_string_char_type *str2, size_t str2len)
{
size_t complen = (str1len < str2len) ? str1len : str2len;
int ret = memcmp(str1, str2, complen);
@@ -1278,99 +1058,132 @@ static inline int al_string_compare(const al_string_char_type *str1, size_t str1
}
return ret;
}
-int al_string_cmp(const_al_string str1, const_al_string str2)
+int alstr_cmp(const_al_string str1, const_al_string str2)
{
- return al_string_compare(&VECTOR_FRONT(str1), al_string_length(str1),
- &VECTOR_FRONT(str2), al_string_length(str2));
+ return alstr_compare(&VECTOR_FRONT(str1), alstr_length(str1),
+ &VECTOR_FRONT(str2), alstr_length(str2));
}
-int al_string_cmp_cstr(const_al_string str1, const al_string_char_type *str2)
+int alstr_cmp_cstr(const_al_string str1, const al_string_char_type *str2)
{
- return al_string_compare(&VECTOR_FRONT(str1), al_string_length(str1),
- str2, strlen(str2));
+ return alstr_compare(&VECTOR_FRONT(str1), alstr_length(str1),
+ str2, strlen(str2));
}
-void al_string_copy(al_string *str, const_al_string from)
+void alstr_copy(al_string *str, const_al_string 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);
- *VECTOR_ITER_END(*str) = 0;
+ size_t len = alstr_length(from);
+ size_t i;
+
+ VECTOR_RESIZE(*str, len, len+1);
+ for(i = 0;i < len;i++)
+ VECTOR_ELEM(*str, i) = VECTOR_ELEM(from, i);
+ VECTOR_ELEM(*str, i) = 0;
}
-void al_string_copy_cstr(al_string *str, const al_string_char_type *from)
+void alstr_copy_cstr(al_string *str, const al_string_char_type *from)
{
size_t len = strlen(from);
- VECTOR_RESERVE(*str, len+1);
- VECTOR_RESIZE(*str, 0);
- VECTOR_INSERT(*str, VECTOR_ITER_END(*str), from, from+len);
- *VECTOR_ITER_END(*str) = 0;
+ size_t i;
+
+ VECTOR_RESIZE(*str, len, len+1);
+ for(i = 0;i < len;i++)
+ VECTOR_ELEM(*str, i) = from[i];
+ VECTOR_ELEM(*str, i) = 0;
+}
+
+void alstr_copy_range(al_string *str, const al_string_char_type *from, const al_string_char_type *to)
+{
+ size_t len = to - from;
+ size_t i;
+
+ VECTOR_RESIZE(*str, len, len+1);
+ for(i = 0;i < len;i++)
+ VECTOR_ELEM(*str, i) = from[i];
+ VECTOR_ELEM(*str, i) = 0;
}
-void al_string_append_char(al_string *str, const al_string_char_type c)
+void alstr_append_char(al_string *str, const al_string_char_type c)
{
- VECTOR_RESERVE(*str, al_string_length(*str)+2);
- VECTOR_PUSH_BACK(*str, c);
- *VECTOR_ITER_END(*str) = 0;
+ size_t len = alstr_length(*str);
+ VECTOR_RESIZE(*str, len+1, len+2);
+ VECTOR_BACK(*str) = c;
+ VECTOR_ELEM(*str, len+1) = 0;
}
-void al_string_append_cstr(al_string *str, const al_string_char_type *from)
+void alstr_append_cstr(al_string *str, const al_string_char_type *from)
{
size_t len = strlen(from);
if(len != 0)
{
- VECTOR_RESERVE(*str, al_string_length(*str)+len+1);
- VECTOR_INSERT(*str, VECTOR_ITER_END(*str), from, from+len);
- *VECTOR_ITER_END(*str) = 0;
+ size_t base = alstr_length(*str);
+ size_t i;
+
+ VECTOR_RESIZE(*str, base+len, base+len+1);
+ for(i = 0;i < len;i++)
+ VECTOR_ELEM(*str, base+i) = from[i];
+ VECTOR_ELEM(*str, base+i) = 0;
}
}
-void al_string_append_range(al_string *str, const al_string_char_type *from, const al_string_char_type *to)
+void alstr_append_range(al_string *str, const al_string_char_type *from, const al_string_char_type *to)
{
- if(to != from)
+ size_t len = to - from;
+ if(len != 0)
{
- VECTOR_RESERVE(*str, al_string_length(*str)+(to-from)+1);
- VECTOR_INSERT(*str, VECTOR_ITER_END(*str), from, to);
- *VECTOR_ITER_END(*str) = 0;
+ size_t base = alstr_length(*str);
+ size_t i;
+
+ VECTOR_RESIZE(*str, base+len, base+len+1);
+ for(i = 0;i < len;i++)
+ VECTOR_ELEM(*str, base+i) = from[i];
+ VECTOR_ELEM(*str, base+i) = 0;
}
}
#ifdef _WIN32
-void al_string_copy_wcstr(al_string *str, const wchar_t *from)
+void alstr_copy_wcstr(al_string *str, const wchar_t *from)
{
int len;
if((len=WideCharToMultiByte(CP_UTF8, 0, from, -1, NULL, 0, NULL, NULL)) > 0)
{
- VECTOR_RESERVE(*str, len);
- VECTOR_RESIZE(*str, len-1);
+ VECTOR_RESIZE(*str, len-1, len);
WideCharToMultiByte(CP_UTF8, 0, from, -1, &VECTOR_FRONT(*str), len, NULL, NULL);
- *VECTOR_ITER_END(*str) = 0;
+ VECTOR_ELEM(*str, len-1) = 0;
}
}
-void al_string_append_wcstr(al_string *str, const wchar_t *from)
+void alstr_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;
+ size_t base = alstr_length(*str);
+ VECTOR_RESIZE(*str, base+len-1, base+len);
+ WideCharToMultiByte(CP_UTF8, 0, from, -1, &VECTOR_ELEM(*str, base), len, NULL, NULL);
+ VECTOR_ELEM(*str, base+len-1) = 0;
+ }
+}
+
+void alstr_copy_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)
+ {
+ VECTOR_RESIZE(*str, len, len+1);
+ WideCharToMultiByte(CP_UTF8, 0, from, (int)(to-from), &VECTOR_FRONT(*str), len+1, NULL, NULL);
+ VECTOR_ELEM(*str, len) = 0;
}
}
-void al_string_append_wrange(al_string *str, const wchar_t *from, const wchar_t *to)
+void alstr_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;
+ size_t base = alstr_length(*str);
+ VECTOR_RESIZE(*str, base+len, base+len+1);
+ WideCharToMultiByte(CP_UTF8, 0, from, (int)(to-from), &VECTOR_ELEM(*str, base), len+1, NULL, NULL);
+ VECTOR_ELEM(*str, base+len) = 0;
}
}
#endif
diff --git a/Alc/hrtf.c b/Alc/hrtf.c
index 71dd9d79..ddbd3a28 100644
--- a/Alc/hrtf.c
+++ b/Alc/hrtf.c
@@ -29,423 +29,460 @@
#include "alSource.h"
#include "alu.h"
#include "hrtf.h"
+#include "alconfig.h"
+#include "filters/splitter.h"
#include "compat.h"
+#include "almalloc.h"
/* Current data set limits defined by the makehrtf utility. */
#define MIN_IR_SIZE (8)
-#define MAX_IR_SIZE (128)
+#define MAX_IR_SIZE (512)
#define MOD_IR_SIZE (8)
+#define MIN_FD_COUNT (1)
+#define MAX_FD_COUNT (16)
+
+#define MIN_FD_DISTANCE (50)
+#define MAX_FD_DISTANCE (2500)
+
#define MIN_EV_COUNT (5)
#define MAX_EV_COUNT (128)
#define MIN_AZ_COUNT (1)
#define MAX_AZ_COUNT (128)
-struct Hrtf {
- ALuint sampleRate;
- ALuint irSize;
- ALubyte evCount;
+#define MAX_HRIR_DELAY (HRTF_HISTORY_LENGTH-1)
- const ALubyte *azCount;
- const ALushort *evOffset;
- const ALshort *coeffs;
- const ALubyte *delays;
-
- al_string filename;
- struct Hrtf *next;
+struct HrtfEntry {
+ struct HrtfEntry *next;
+ struct Hrtf *handle;
+ char filename[];
};
static const ALchar magicMarker00[8] = "MinPHR00";
static const ALchar magicMarker01[8] = "MinPHR01";
+static const ALchar magicMarker02[8] = "MinPHR02";
/* First value for pass-through coefficients (remaining are 0), used for omni-
* directional sounds. */
-static const ALfloat PassthruCoeff = 32767.0f * 0.707106781187f/*sqrt(0.5)*/;
+static const ALfloat PassthruCoeff = 0.707106781187f/*sqrt(0.5)*/;
+
+static ATOMIC_FLAG LoadedHrtfLock = ATOMIC_FLAG_INIT;
+static struct HrtfEntry *LoadedHrtfs = NULL;
-static struct Hrtf *LoadedHrtfs = NULL;
-/* Calculate the elevation indices given the polar elevation in radians.
- * This will return two indices between 0 and (evcount - 1) and an
- * interpolation factor between 0.0 and 1.0.
+/* Calculate the elevation index given the polar elevation in radians. This
+ * will return an index between 0 and (evcount - 1).
*/
-static void CalcEvIndices(ALuint evcount, ALfloat ev, ALuint *evidx, ALfloat *evmu)
+static ALsizei CalcEvIndex(ALsizei evcount, ALfloat ev, ALfloat *mu)
{
- ev = (F_PI_2 + ev) * (evcount-1) / F_PI;
- evidx[0] = fastf2u(ev);
- evidx[1] = minu(evidx[0] + 1, evcount-1);
- *evmu = ev - evidx[0];
+ ALsizei idx;
+ ev = (F_PI_2+ev) * (evcount-1) / F_PI;
+ idx = float2int(ev);
+
+ *mu = ev - idx;
+ return mini(idx, evcount-1);
}
-/* Calculate the azimuth indices given the polar azimuth in radians. This
- * will return two indices between 0 and (azcount - 1) and an interpolation
- * factor between 0.0 and 1.0.
+/* Calculate the azimuth index given the polar azimuth in radians. This will
+ * return an index between 0 and (azcount - 1).
*/
-static void CalcAzIndices(ALuint azcount, ALfloat az, ALuint *azidx, ALfloat *azmu)
+static ALsizei CalcAzIndex(ALsizei azcount, ALfloat az, ALfloat *mu)
{
- az = (F_TAU + az) * azcount / F_TAU;
- azidx[0] = fastf2u(az) % azcount;
- azidx[1] = (azidx[0] + 1) % azcount;
- *azmu = az - floorf(az);
+ ALsizei idx;
+ az = (F_TAU+az) * azcount / F_TAU;
+
+ idx = float2int(az);
+ *mu = az - idx;
+ return idx % azcount;
}
-/* Calculates static HRIR coefficients and delays for the given polar
- * elevation and azimuth in radians. Linear interpolation is used to
- * increase the apparent resolution of the HRIR data set. The coefficients
- * are also normalized and attenuated by the specified gain.
+/* Calculates static HRIR coefficients and delays for the given polar elevation
+ * and azimuth in radians. The coefficients are normalized.
*/
-void GetLerpedHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azimuth, ALfloat dirfact, ALfloat gain, ALfloat (*coeffs)[2], ALuint *delays)
+void GetHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azimuth, ALfloat spread,
+ ALfloat (*restrict coeffs)[2], ALsizei *delays)
{
- ALuint evidx[2], lidx[4], ridx[4];
- ALfloat mu[3], blend[4];
- ALuint i;
-
- /* Claculate elevation indices and interpolation factor. */
- CalcEvIndices(Hrtf->evCount, elevation, evidx, &mu[2]);
-
- for(i = 0;i < 2;i++)
+ ALsizei evidx, azidx, idx[4];
+ ALsizei evoffset;
+ ALfloat emu, amu[2];
+ ALfloat blend[4];
+ ALfloat dirfact;
+ ALsizei i, c;
+
+ dirfact = 1.0f - (spread / F_TAU);
+
+ /* Claculate the lower elevation index. */
+ evidx = CalcEvIndex(Hrtf->evCount, elevation, &emu);
+ evoffset = Hrtf->evOffset[evidx];
+
+ /* Calculate lower azimuth index. */
+ azidx= CalcAzIndex(Hrtf->azCount[evidx], azimuth, &amu[0]);
+
+ /* Calculate the lower HRIR indices. */
+ idx[0] = evoffset + azidx;
+ idx[1] = evoffset + ((azidx+1) % Hrtf->azCount[evidx]);
+ if(evidx < Hrtf->evCount-1)
{
- ALuint azcount = Hrtf->azCount[evidx[i]];
- ALuint evoffset = Hrtf->evOffset[evidx[i]];
- ALuint azidx[2];
+ /* Increment elevation to the next (upper) index. */
+ evidx++;
+ evoffset = Hrtf->evOffset[evidx];
- /* Calculate azimuth indices and interpolation factor for this elevation. */
- CalcAzIndices(azcount, azimuth, azidx, &mu[i]);
+ /* Calculate upper azimuth index. */
+ azidx = CalcAzIndex(Hrtf->azCount[evidx], azimuth, &amu[1]);
- /* Calculate a set of linear HRIR indices for left and right channels. */
- lidx[i*2 + 0] = evoffset + azidx[0];
- lidx[i*2 + 1] = evoffset + azidx[1];
- ridx[i*2 + 0] = evoffset + ((azcount-azidx[0]) % azcount);
- ridx[i*2 + 1] = evoffset + ((azcount-azidx[1]) % azcount);
+ /* Calculate the upper HRIR indices. */
+ idx[2] = evoffset + azidx;
+ idx[3] = evoffset + ((azidx+1) % Hrtf->azCount[evidx]);
+ }
+ else
+ {
+ /* If the lower elevation is the top index, the upper elevation is the
+ * same as the lower.
+ */
+ amu[1] = amu[0];
+ idx[2] = idx[0];
+ idx[3] = idx[1];
}
- /* Calculate 4 blending weights for 2D bilinear interpolation. */
- blend[0] = (1.0f-mu[0]) * (1.0f-mu[2]);
- blend[1] = ( mu[0]) * (1.0f-mu[2]);
- blend[2] = (1.0f-mu[1]) * ( mu[2]);
- blend[3] = ( mu[1]) * ( mu[2]);
-
- /* Calculate the HRIR delays using linear interpolation. */
- delays[0] = fastf2u((Hrtf->delays[lidx[0]]*blend[0] + Hrtf->delays[lidx[1]]*blend[1] +
- Hrtf->delays[lidx[2]]*blend[2] + Hrtf->delays[lidx[3]]*blend[3]) *
- dirfact + 0.5f) << HRTFDELAY_BITS;
- delays[1] = fastf2u((Hrtf->delays[ridx[0]]*blend[0] + Hrtf->delays[ridx[1]]*blend[1] +
- Hrtf->delays[ridx[2]]*blend[2] + Hrtf->delays[ridx[3]]*blend[3]) *
- dirfact + 0.5f) << HRTFDELAY_BITS;
+ /* Calculate bilinear blending weights, attenuated according to the
+ * directional panning factor.
+ */
+ blend[0] = (1.0f-emu) * (1.0f-amu[0]) * dirfact;
+ blend[1] = (1.0f-emu) * ( amu[0]) * dirfact;
+ blend[2] = ( emu) * (1.0f-amu[1]) * dirfact;
+ blend[3] = ( emu) * ( amu[1]) * dirfact;
+
+ /* Calculate the blended HRIR delays. */
+ delays[0] = fastf2i(
+ Hrtf->delays[idx[0]][0]*blend[0] + Hrtf->delays[idx[1]][0]*blend[1] +
+ Hrtf->delays[idx[2]][0]*blend[2] + Hrtf->delays[idx[3]][0]*blend[3]
+ );
+ delays[1] = fastf2i(
+ Hrtf->delays[idx[0]][1]*blend[0] + Hrtf->delays[idx[1]][1]*blend[1] +
+ Hrtf->delays[idx[2]][1]*blend[2] + Hrtf->delays[idx[3]][1]*blend[3]
+ );
/* Calculate the sample offsets for the HRIR indices. */
- lidx[0] *= Hrtf->irSize;
- lidx[1] *= Hrtf->irSize;
- lidx[2] *= Hrtf->irSize;
- lidx[3] *= Hrtf->irSize;
- ridx[0] *= Hrtf->irSize;
- ridx[1] *= Hrtf->irSize;
- ridx[2] *= Hrtf->irSize;
- ridx[3] *= Hrtf->irSize;
-
- /* Calculate the normalized and attenuated HRIR coefficients using linear
- * interpolation when there is enough gain to warrant it. Zero the
- * coefficients if gain is too low.
- */
- if(gain > 0.0001f)
+ idx[0] *= Hrtf->irSize;
+ idx[1] *= Hrtf->irSize;
+ idx[2] *= Hrtf->irSize;
+ idx[3] *= Hrtf->irSize;
+
+ ASSUME(Hrtf->irSize >= MIN_IR_SIZE && (Hrtf->irSize%MOD_IR_SIZE) == 0);
+ coeffs = ASSUME_ALIGNED(coeffs, 16);
+ /* Calculate the blended HRIR coefficients. */
+ coeffs[0][0] = PassthruCoeff * (1.0f-dirfact);
+ coeffs[0][1] = PassthruCoeff * (1.0f-dirfact);
+ for(i = 1;i < Hrtf->irSize;i++)
{
- ALfloat c;
-
- i = 0;
- c = (Hrtf->coeffs[lidx[0]+i]*blend[0] + Hrtf->coeffs[lidx[1]+i]*blend[1] +
- Hrtf->coeffs[lidx[2]+i]*blend[2] + Hrtf->coeffs[lidx[3]+i]*blend[3]);
- coeffs[i][0] = lerp(PassthruCoeff, c, dirfact) * gain * (1.0f/32767.0f);
- c = (Hrtf->coeffs[ridx[0]+i]*blend[0] + Hrtf->coeffs[ridx[1]+i]*blend[1] +
- Hrtf->coeffs[ridx[2]+i]*blend[2] + Hrtf->coeffs[ridx[3]+i]*blend[3]);
- coeffs[i][1] = lerp(PassthruCoeff, c, dirfact) * gain * (1.0f/32767.0f);
-
- for(i = 1;i < Hrtf->irSize;i++)
- {
- c = (Hrtf->coeffs[lidx[0]+i]*blend[0] + Hrtf->coeffs[lidx[1]+i]*blend[1] +
- Hrtf->coeffs[lidx[2]+i]*blend[2] + Hrtf->coeffs[lidx[3]+i]*blend[3]);
- coeffs[i][0] = lerp(0.0f, c, dirfact) * gain * (1.0f/32767.0f);
- c = (Hrtf->coeffs[ridx[0]+i]*blend[0] + Hrtf->coeffs[ridx[1]+i]*blend[1] +
- Hrtf->coeffs[ridx[2]+i]*blend[2] + Hrtf->coeffs[ridx[3]+i]*blend[3]);
- coeffs[i][1] = lerp(0.0f, c, dirfact) * gain * (1.0f/32767.0f);
- }
+ coeffs[i][0] = 0.0f;
+ coeffs[i][1] = 0.0f;
}
- else
+ for(c = 0;c < 4;c++)
{
+ const ALfloat (*restrict srccoeffs)[2] = ASSUME_ALIGNED(Hrtf->coeffs+idx[c], 16);
for(i = 0;i < Hrtf->irSize;i++)
{
- coeffs[i][0] = 0.0f;
- coeffs[i][1] = 0.0f;
+ coeffs[i][0] += srccoeffs[i][0] * blend[c];
+ coeffs[i][1] += srccoeffs[i][1] * blend[c];
}
}
}
-/* Calculates the moving HRIR target coefficients, target delays, and
- * stepping values for the given polar elevation and azimuth in radians.
- * Linear interpolation is used to increase the apparent resolution of the
- * HRIR data set. The coefficients are also normalized and attenuated by the
- * specified gain. Stepping resolution and count is determined using the
- * given delta factor between 0.0 and 1.0.
- */
-ALuint GetMovingHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azimuth, ALfloat dirfact, ALfloat gain, ALfloat delta, ALint counter, ALfloat (*coeffs)[2], ALuint *delays, ALfloat (*coeffStep)[2], ALint *delayStep)
-{
- ALuint evidx[2], lidx[4], ridx[4];
- ALfloat mu[3], blend[4];
- ALfloat left, right;
- ALfloat steps;
- ALuint i;
- /* Claculate elevation indices and interpolation factor. */
- CalcEvIndices(Hrtf->evCount, elevation, evidx, &mu[2]);
-
- for(i = 0;i < 2;i++)
+void BuildBFormatHrtf(const struct Hrtf *Hrtf, DirectHrtfState *state, ALsizei NumChannels, const struct AngularPoint *AmbiPoints, const ALfloat (*restrict AmbiMatrix)[MAX_AMBI_COEFFS], ALsizei AmbiCount, const ALfloat *restrict AmbiOrderHFGain)
+{
+/* Set this to 2 for dual-band HRTF processing. May require a higher quality
+ * band-splitter, or better calculation of the new IR length to deal with the
+ * tail generated by the filter.
+ */
+#define NUM_BANDS 2
+ BandSplitter splitter;
+ ALdouble (*tmpres)[HRIR_LENGTH][2];
+ ALsizei *restrict idx;
+ ALsizei min_delay = HRTF_HISTORY_LENGTH;
+ ALsizei max_delay = 0;
+ ALfloat temps[3][HRIR_LENGTH];
+ ALsizei max_length;
+ ALsizei i, c, b;
+
+ idx = al_calloc(DEF_ALIGN, AmbiCount*sizeof(*idx));
+
+ for(c = 0;c < AmbiCount;c++)
{
- ALuint azcount = Hrtf->azCount[evidx[i]];
- ALuint evoffset = Hrtf->evOffset[evidx[i]];
- ALuint azidx[2];
+ ALuint evidx, azidx;
+ ALuint evoffset;
+ ALuint azcount;
- /* Calculate azimuth indices and interpolation factor for this elevation. */
- CalcAzIndices(azcount, azimuth, azidx, &mu[i]);
+ /* Calculate elevation index. */
+ evidx = (ALsizei)((F_PI_2+AmbiPoints[c].Elev) * (Hrtf->evCount-1) / F_PI + 0.5f);
+ evidx = clampi(evidx, 0, Hrtf->evCount-1);
- /* Calculate a set of linear HRIR indices for left and right channels. */
- lidx[i*2 + 0] = evoffset + azidx[0];
- lidx[i*2 + 1] = evoffset + azidx[1];
- ridx[i*2 + 0] = evoffset + ((azcount-azidx[0]) % azcount);
- ridx[i*2 + 1] = evoffset + ((azcount-azidx[1]) % azcount);
- }
-
- // Calculate the stepping parameters.
- steps = maxf(floorf(delta*Hrtf->sampleRate + 0.5f), 1.0f);
- delta = 1.0f / steps;
+ azcount = Hrtf->azCount[evidx];
+ evoffset = Hrtf->evOffset[evidx];
- /* Calculate 4 blending weights for 2D bilinear interpolation. */
- blend[0] = (1.0f-mu[0]) * (1.0f-mu[2]);
- blend[1] = ( mu[0]) * (1.0f-mu[2]);
- blend[2] = (1.0f-mu[1]) * ( mu[2]);
- blend[3] = ( mu[1]) * ( mu[2]);
+ /* Calculate azimuth index for this elevation. */
+ azidx = (ALsizei)((F_TAU+AmbiPoints[c].Azim) * azcount / F_TAU + 0.5f) % azcount;
- /* Calculate the HRIR delays using linear interpolation. Then calculate
- * the delay stepping values using the target and previous running
- * delays.
- */
- left = (ALfloat)(delays[0] - (delayStep[0] * counter));
- right = (ALfloat)(delays[1] - (delayStep[1] * counter));
+ /* Calculate indices for left and right channels. */
+ idx[c] = evoffset + azidx;
- delays[0] = fastf2u((Hrtf->delays[lidx[0]]*blend[0] + Hrtf->delays[lidx[1]]*blend[1] +
- Hrtf->delays[lidx[2]]*blend[2] + Hrtf->delays[lidx[3]]*blend[3]) *
- dirfact + 0.5f) << HRTFDELAY_BITS;
- delays[1] = fastf2u((Hrtf->delays[ridx[0]]*blend[0] + Hrtf->delays[ridx[1]]*blend[1] +
- Hrtf->delays[ridx[2]]*blend[2] + Hrtf->delays[ridx[3]]*blend[3]) *
- dirfact + 0.5f) << HRTFDELAY_BITS;
+ min_delay = mini(min_delay, mini(Hrtf->delays[idx[c]][0], Hrtf->delays[idx[c]][1]));
+ max_delay = maxi(max_delay, maxi(Hrtf->delays[idx[c]][0], Hrtf->delays[idx[c]][1]));
+ }
- delayStep[0] = fastf2i(delta * (delays[0] - left));
- delayStep[1] = fastf2i(delta * (delays[1] - right));
+ tmpres = al_calloc(16, NumChannels * sizeof(*tmpres));
- /* Calculate the sample offsets for the HRIR indices. */
- lidx[0] *= Hrtf->irSize;
- lidx[1] *= Hrtf->irSize;
- lidx[2] *= Hrtf->irSize;
- lidx[3] *= Hrtf->irSize;
- ridx[0] *= Hrtf->irSize;
- ridx[1] *= Hrtf->irSize;
- ridx[2] *= Hrtf->irSize;
- ridx[3] *= Hrtf->irSize;
-
- /* Calculate the normalized and attenuated target HRIR coefficients using
- * linear interpolation when there is enough gain to warrant it. Zero
- * the target coefficients if gain is too low. Then calculate the
- * coefficient stepping values using the target and previous running
- * coefficients.
- */
- if(gain > 0.0001f)
+ memset(temps, 0, sizeof(temps));
+ bandsplit_init(&splitter, 400.0f / (ALfloat)Hrtf->sampleRate);
+ for(c = 0;c < AmbiCount;c++)
{
- ALfloat c;
-
- i = 0;
- left = coeffs[i][0] - (coeffStep[i][0] * counter);
- right = coeffs[i][1] - (coeffStep[i][1] * counter);
+ const ALfloat (*fir)[2] = &Hrtf->coeffs[idx[c] * Hrtf->irSize];
+ ALsizei ldelay = Hrtf->delays[idx[c]][0] - min_delay;
+ ALsizei rdelay = Hrtf->delays[idx[c]][1] - min_delay;
- c = (Hrtf->coeffs[lidx[0]+i]*blend[0] + Hrtf->coeffs[lidx[1]+i]*blend[1] +
- Hrtf->coeffs[lidx[2]+i]*blend[2] + Hrtf->coeffs[lidx[3]+i]*blend[3]);
- coeffs[i][0] = lerp(PassthruCoeff, c, dirfact) * gain * (1.0f/32767.0f);
- c = (Hrtf->coeffs[ridx[0]+i]*blend[0] + Hrtf->coeffs[ridx[1]+i]*blend[1] +
- Hrtf->coeffs[ridx[2]+i]*blend[2] + Hrtf->coeffs[ridx[3]+i]*blend[3]);
- coeffs[i][1] = lerp(PassthruCoeff, c, dirfact) * gain * (1.0f/32767.0f);
-
- coeffStep[i][0] = delta * (coeffs[i][0] - left);
- coeffStep[i][1] = delta * (coeffs[i][1] - right);
-
- for(i = 1;i < Hrtf->irSize;i++)
+ if(NUM_BANDS == 1)
+ {
+ for(i = 0;i < NumChannels;++i)
+ {
+ ALdouble mult = (ALdouble)AmbiOrderHFGain[(ALsizei)sqrt(i)] * AmbiMatrix[c][i];
+ ALsizei lidx = ldelay, ridx = rdelay;
+ ALsizei j = 0;
+ while(lidx < HRIR_LENGTH && ridx < HRIR_LENGTH && j < Hrtf->irSize)
+ {
+ tmpres[i][lidx++][0] += fir[j][0] * mult;
+ tmpres[i][ridx++][1] += fir[j][1] * mult;
+ j++;
+ }
+ }
+ }
+ else
{
- left = coeffs[i][0] - (coeffStep[i][0] * counter);
- right = coeffs[i][1] - (coeffStep[i][1] * counter);
+ /* Band-split left HRIR into low and high frequency responses. */
+ bandsplit_clear(&splitter);
+ for(i = 0;i < Hrtf->irSize;i++)
+ temps[2][i] = fir[i][0];
+ bandsplit_process(&splitter, temps[0], temps[1], temps[2], HRIR_LENGTH);
+
+ /* Apply left ear response with delay. */
+ for(i = 0;i < NumChannels;++i)
+ {
+ ALfloat hfgain = AmbiOrderHFGain[(ALsizei)sqrt(i)];
+ for(b = 0;b < NUM_BANDS;b++)
+ {
+ ALdouble mult = AmbiMatrix[c][i] * (ALdouble)((b==0) ? hfgain : 1.0);
+ ALsizei lidx = ldelay;
+ ALsizei j = 0;
+ while(lidx < HRIR_LENGTH)
+ tmpres[i][lidx++][0] += temps[b][j++] * mult;
+ }
+ }
- c = (Hrtf->coeffs[lidx[0]+i]*blend[0] + Hrtf->coeffs[lidx[1]+i]*blend[1] +
- Hrtf->coeffs[lidx[2]+i]*blend[2] + Hrtf->coeffs[lidx[3]+i]*blend[3]);
- coeffs[i][0] = lerp(0.0f, c, dirfact) * gain * (1.0f/32767.0f);
- c = (Hrtf->coeffs[ridx[0]+i]*blend[0] + Hrtf->coeffs[ridx[1]+i]*blend[1] +
- Hrtf->coeffs[ridx[2]+i]*blend[2] + Hrtf->coeffs[ridx[3]+i]*blend[3]);
- coeffs[i][1] = lerp(0.0f, c, dirfact) * gain * (1.0f/32767.0f);
+ /* Band-split right HRIR into low and high frequency responses. */
+ bandsplit_clear(&splitter);
+ for(i = 0;i < Hrtf->irSize;i++)
+ temps[2][i] = fir[i][1];
+ bandsplit_process(&splitter, temps[0], temps[1], temps[2], HRIR_LENGTH);
- coeffStep[i][0] = delta * (coeffs[i][0] - left);
- coeffStep[i][1] = delta * (coeffs[i][1] - right);
+ /* Apply right ear response with delay. */
+ for(i = 0;i < NumChannels;++i)
+ {
+ ALfloat hfgain = AmbiOrderHFGain[(ALsizei)sqrt(i)];
+ for(b = 0;b < NUM_BANDS;b++)
+ {
+ ALdouble mult = AmbiMatrix[c][i] * (ALdouble)((b==0) ? hfgain : 1.0);
+ ALsizei ridx = rdelay;
+ ALsizei j = 0;
+ while(ridx < HRIR_LENGTH)
+ tmpres[i][ridx++][1] += temps[b][j++] * mult;
+ }
+ }
}
}
- else
+
+ for(i = 0;i < NumChannels;++i)
{
- for(i = 0;i < Hrtf->irSize;i++)
+ int idx;
+ for(idx = 0;idx < HRIR_LENGTH;idx++)
{
- left = coeffs[i][0] - (coeffStep[i][0] * counter);
- right = coeffs[i][1] - (coeffStep[i][1] * counter);
-
- coeffs[i][0] = 0.0f;
- coeffs[i][1] = 0.0f;
-
- coeffStep[i][0] = delta * -left;
- coeffStep[i][1] = delta * -right;
+ state->Chan[i].Coeffs[idx][0] = (ALfloat)tmpres[i][idx][0];
+ state->Chan[i].Coeffs[idx][1] = (ALfloat)tmpres[i][idx][1];
}
}
+ al_free(tmpres);
+ tmpres = NULL;
+ al_free(idx);
+ idx = NULL;
- /* The stepping count is the number of samples necessary for the HRIR to
- * complete its transition. The mixer will only apply stepping for this
- * many samples.
- */
- return fastf2u(steps);
+ if(NUM_BANDS == 1)
+ max_length = mini(max_delay-min_delay + Hrtf->irSize, HRIR_LENGTH);
+ else
+ {
+ /* Increase the IR size by 2/3rds to account for the tail generated by
+ * the band-split filter.
+ */
+ const ALsizei irsize = mini(Hrtf->irSize*5/3, HRIR_LENGTH);
+ max_length = mini(max_delay-min_delay + irsize, HRIR_LENGTH);
+ }
+ /* Round up to the next IR size multiple. */
+ max_length += MOD_IR_SIZE-1;
+ max_length -= max_length%MOD_IR_SIZE;
+
+ TRACE("Skipped delay: %d, max delay: %d, new FIR length: %d\n",
+ min_delay, max_delay-min_delay, max_length);
+ state->IrSize = max_length;
+#undef NUM_BANDS
}
-/* Calculates HRTF coefficients for B-Format channels (only up to first-order).
- * Note that these will decode a B-Format output mix, which uses FuMa ordering
- * and scaling, not N3D!
- */
-void GetBFormatHrtfCoeffs(const struct Hrtf *Hrtf, const ALuint num_chans, ALfloat (**coeffs_list)[2], ALuint **delay_list)
+static struct Hrtf *CreateHrtfStore(ALuint rate, ALsizei irSize,
+ ALfloat distance, ALsizei evCount, ALsizei irCount, const ALubyte *azCount,
+ const ALushort *evOffset, const ALfloat (*coeffs)[2], const ALubyte (*delays)[2],
+ const char *filename)
{
- ALuint elev_idx, azi_idx;
- ALfloat scale;
- ALuint i, c;
-
- assert(num_chans <= 4);
-
- for(c = 0;c < num_chans;c++)
- {
- ALfloat (*coeffs)[2] = coeffs_list[c];
- ALuint *delay = delay_list[c];
-
- for(i = 0;i < Hrtf->irSize;i++)
- {
- coeffs[i][0] = 0.0f;
- coeffs[i][1] = 0.0f;
- }
- delay[0] = 0;
- delay[1] = 0;
- }
-
- /* NOTE: HRTF coefficients are generated by combining all the HRIRs in the
- * dataset, with each entry scaled according to how much it contributes to
- * the given B-Format channel based on its direction (including negative
- * contributions!).
- */
- scale = 0.0f;
- for(elev_idx = 0;elev_idx < Hrtf->evCount;elev_idx++)
+ struct Hrtf *Hrtf;
+ size_t total;
+
+ total = sizeof(struct Hrtf);
+ total += sizeof(Hrtf->azCount[0])*evCount;
+ total = RoundUp(total, sizeof(ALushort)); /* Align for ushort fields */
+ total += sizeof(Hrtf->evOffset[0])*evCount;
+ total = RoundUp(total, 16); /* Align for coefficients using SIMD */
+ total += sizeof(Hrtf->coeffs[0])*irSize*irCount;
+ total += sizeof(Hrtf->delays[0])*irCount;
+
+ Hrtf = al_calloc(16, total);
+ if(Hrtf == NULL)
+ ERR("Out of memory allocating storage for %s.\n", filename);
+ else
{
- ALfloat elev = (ALfloat)elev_idx/(ALfloat)(Hrtf->evCount-1)*F_PI - F_PI_2;
- ALuint evoffset = Hrtf->evOffset[elev_idx];
- ALuint azcount = Hrtf->azCount[elev_idx];
+ uintptr_t offset = sizeof(struct Hrtf);
+ char *base = (char*)Hrtf;
+ ALushort *_evOffset;
+ ALubyte *_azCount;
+ ALubyte (*_delays)[2];
+ ALfloat (*_coeffs)[2];
+ ALsizei i;
+
+ InitRef(&Hrtf->ref, 0);
+ Hrtf->sampleRate = rate;
+ Hrtf->irSize = irSize;
+ Hrtf->distance = distance;
+ Hrtf->evCount = evCount;
- scale += (ALfloat)azcount;
+ /* Set up pointers to storage following the main HRTF struct. */
+ _azCount = (ALubyte*)(base + offset);
+ offset += sizeof(_azCount[0])*evCount;
- for(azi_idx = 0;azi_idx < azcount;azi_idx++)
- {
- ALuint lidx, ridx;
- ALfloat ambi_coeffs[4];
- ALfloat az, gain;
- ALfloat x, y, z;
+ offset = RoundUp(offset, sizeof(ALushort)); /* Align for ushort fields */
+ _evOffset = (ALushort*)(base + offset);
+ offset += sizeof(_evOffset[0])*evCount;
- lidx = evoffset + azi_idx;
- ridx = evoffset + ((azcount-azi_idx) % azcount);
+ offset = RoundUp(offset, 16); /* Align for coefficients using SIMD */
+ _coeffs = (ALfloat(*)[2])(base + offset);
+ offset += sizeof(_coeffs[0])*irSize*irCount;
- az = (ALfloat)azi_idx / (ALfloat)azcount * F_TAU;
- if(az > F_PI) az -= F_TAU;
+ _delays = (ALubyte(*)[2])(base + offset);
+ offset += sizeof(_delays[0])*irCount;
- x = cosf(-az) * cosf(elev);
- y = sinf(-az) * cosf(elev);
- z = sinf(elev);
+ assert(offset == total);
- ambi_coeffs[0] = 1.414213562f;
- ambi_coeffs[1] = x;
- ambi_coeffs[2] = y;
- ambi_coeffs[3] = z;
+ /* Copy input data to storage. */
+ for(i = 0;i < evCount;i++) _azCount[i] = azCount[i];
+ for(i = 0;i < evCount;i++) _evOffset[i] = evOffset[i];
+ for(i = 0;i < irSize*irCount;i++)
+ {
+ _coeffs[i][0] = coeffs[i][0];
+ _coeffs[i][1] = coeffs[i][1];
+ }
+ for(i = 0;i < irCount;i++)
+ {
+ _delays[i][0] = delays[i][0];
+ _delays[i][1] = delays[i][1];
+ }
- for(c = 0;c < num_chans;c++)
- {
- ALfloat (*coeffs)[2] = coeffs_list[c];
- ALuint *delay = delay_list[c];
+ /* Finally, assign the storage pointers. */
+ Hrtf->azCount = _azCount;
+ Hrtf->evOffset = _evOffset;
+ Hrtf->coeffs = _coeffs;
+ Hrtf->delays = _delays;
+ }
- /* 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];
+ return Hrtf;
+}
- gain = ambi_coeffs[c];
- if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
- continue;
+static ALubyte GetLE_ALubyte(const ALubyte **data, size_t *len)
+{
+ ALubyte ret = (*data)[0];
+ *data += 1; *len -= 1;
+ return ret;
+}
- for(i = 0;i < Hrtf->irSize;i++)
- {
- coeffs[i][0] += Hrtf->coeffs[lidx*Hrtf->irSize + i]*(1.0f/32767.0f) * gain;
- coeffs[i][1] += Hrtf->coeffs[ridx*Hrtf->irSize + i]*(1.0f/32767.0f) * gain;
- }
- }
- }
- }
+static ALshort GetLE_ALshort(const ALubyte **data, size_t *len)
+{
+ ALshort ret = (*data)[0] | ((*data)[1]<<8);
+ *data += 2; *len -= 2;
+ return ret;
+}
- scale = 1.0f/scale;
+static ALushort GetLE_ALushort(const ALubyte **data, size_t *len)
+{
+ ALushort ret = (*data)[0] | ((*data)[1]<<8);
+ *data += 2; *len -= 2;
+ return ret;
+}
- for(c = 0;c < num_chans;c++)
- {
- ALfloat (*coeffs)[2] = coeffs_list[c];
- ALuint *delay = delay_list[c];
+static ALint GetLE_ALint24(const ALubyte **data, size_t *len)
+{
+ ALint ret = (*data)[0] | ((*data)[1]<<8) | ((*data)[2]<<16);
+ *data += 3; *len -= 3;
+ return (ret^0x800000) - 0x800000;
+}
- 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 ALuint GetLE_ALuint(const ALubyte **data, size_t *len)
+{
+ ALuint ret = (*data)[0] | ((*data)[1]<<8) | ((*data)[2]<<16) | ((*data)[3]<<24);
+ *data += 4; *len -= 4;
+ return ret;
}
+static const ALubyte *Get_ALubytePtr(const ALubyte **data, size_t *len, size_t size)
+{
+ const ALubyte *ret = *data;
+ *data += size; *len -= size;
+ return ret;
+}
-static struct Hrtf *LoadHrtf00(FILE *f)
+static struct Hrtf *LoadHrtf00(const ALubyte *data, size_t datalen, const char *filename)
{
- const ALubyte maxDelay = HRTF_HISTORY_LENGTH-1;
struct Hrtf *Hrtf = NULL;
ALboolean failed = AL_FALSE;
- ALuint rate = 0, irCount = 0;
+ ALuint rate = 0;
+ ALushort irCount = 0;
ALushort irSize = 0;
ALubyte evCount = 0;
ALubyte *azCount = NULL;
ALushort *evOffset = NULL;
- ALshort *coeffs = NULL;
- ALubyte *delays = NULL;
- ALuint i, j;
+ ALfloat (*coeffs)[2] = NULL;
+ ALubyte (*delays)[2] = NULL;
+ ALsizei i, j;
+
+ if(datalen < 9)
+ {
+ ERR("Unexpected end of %s data (req %d, rem "SZFMT")\n", filename, 9, datalen);
+ return NULL;
+ }
- rate = fgetc(f);
- rate |= fgetc(f)<<8;
- rate |= fgetc(f)<<16;
- rate |= fgetc(f)<<24;
+ rate = GetLE_ALuint(&data, &datalen);
- irCount = fgetc(f);
- irCount |= fgetc(f)<<8;
+ irCount = GetLE_ALushort(&data, &datalen);
- irSize = fgetc(f);
- irSize |= fgetc(f)<<8;
+ irSize = GetLE_ALushort(&data, &datalen);
- evCount = fgetc(f);
+ evCount = GetLE_ALubyte(&data, &datalen);
if(irSize < MIN_IR_SIZE || irSize > MAX_IR_SIZE || (irSize%MOD_IR_SIZE))
{
@@ -459,10 +496,15 @@ static struct Hrtf *LoadHrtf00(FILE *f)
evCount, MIN_EV_COUNT, MAX_EV_COUNT);
failed = AL_TRUE;
}
-
if(failed)
return NULL;
+ if(datalen < evCount*2u)
+ {
+ ERR("Unexpected end of %s data (req %d, rem "SZFMT")\n", filename, evCount*2, datalen);
+ return NULL;
+ }
+
azCount = malloc(sizeof(azCount[0])*evCount);
evOffset = malloc(sizeof(evOffset[0])*evCount);
if(azCount == NULL || evOffset == NULL)
@@ -473,12 +515,10 @@ static struct Hrtf *LoadHrtf00(FILE *f)
if(!failed)
{
- evOffset[0] = fgetc(f);
- evOffset[0] |= fgetc(f)<<8;
+ evOffset[0] = GetLE_ALushort(&data, &datalen);
for(i = 1;i < evCount;i++)
{
- evOffset[i] = fgetc(f);
- evOffset[i] |= fgetc(f)<<8;
+ evOffset[i] = GetLE_ALushort(&data, &datalen);
if(evOffset[i] <= evOffset[i-1])
{
ERR("Invalid evOffset: evOffset[%d]=%d (last=%d)\n",
@@ -523,86 +563,89 @@ static struct Hrtf *LoadHrtf00(FILE *f)
if(!failed)
{
- for(i = 0;i < irCount*irSize;i+=irSize)
+ size_t reqsize = 2*irSize*irCount + irCount;
+ if(datalen < reqsize)
+ {
+ ERR("Unexpected end of %s data (req "SZFMT", rem "SZFMT")\n",
+ filename, reqsize, datalen);
+ failed = AL_TRUE;
+ }
+ }
+
+ if(!failed)
+ {
+ for(i = 0;i < irCount;i++)
{
for(j = 0;j < irSize;j++)
- {
- ALshort coeff;
- coeff = fgetc(f);
- coeff |= fgetc(f)<<8;
- coeffs[i+j] = coeff;
- }
+ coeffs[i*irSize + j][0] = GetLE_ALshort(&data, &datalen) / 32768.0f;
}
+
for(i = 0;i < irCount;i++)
{
- delays[i] = fgetc(f);
- if(delays[i] > maxDelay)
+ delays[i][0] = GetLE_ALubyte(&data, &datalen);
+ if(delays[i][0] > MAX_HRIR_DELAY)
{
- ERR("Invalid delays[%d]: %d (%d)\n", i, delays[i], maxDelay);
+ ERR("Invalid delays[%d]: %d (%d)\n", i, delays[i][0], MAX_HRIR_DELAY);
failed = AL_TRUE;
}
}
-
- if(feof(f))
- {
- ERR("Premature end of data\n");
- failed = AL_TRUE;
- }
}
if(!failed)
{
- Hrtf = malloc(sizeof(struct Hrtf));
- if(Hrtf == NULL)
+ /* Mirror the left ear responses to the right ear. */
+ for(i = 0;i < evCount;i++)
{
- ERR("Out of memory.\n");
- failed = AL_TRUE;
+ ALushort evoffset = evOffset[i];
+ ALubyte azcount = azCount[i];
+ for(j = 0;j < azcount;j++)
+ {
+ ALsizei lidx = evoffset + j;
+ ALsizei ridx = evoffset + ((azcount-j) % azcount);
+ ALsizei k;
+
+ for(k = 0;k < irSize;k++)
+ coeffs[ridx*irSize + k][1] = coeffs[lidx*irSize + k][0];
+ delays[ridx][1] = delays[lidx][0];
+ }
}
- }
- if(!failed)
- {
- Hrtf->sampleRate = rate;
- Hrtf->irSize = irSize;
- Hrtf->evCount = evCount;
- Hrtf->azCount = azCount;
- Hrtf->evOffset = evOffset;
- Hrtf->coeffs = coeffs;
- Hrtf->delays = delays;
- AL_STRING_INIT(Hrtf->filename);
- Hrtf->next = NULL;
- return Hrtf;
+ Hrtf = CreateHrtfStore(rate, irSize, 0.0f, evCount, irCount, azCount,
+ evOffset, coeffs, delays, filename);
}
free(azCount);
free(evOffset);
free(coeffs);
free(delays);
- return NULL;
+ return Hrtf;
}
-
-static struct Hrtf *LoadHrtf01(FILE *f)
+static struct Hrtf *LoadHrtf01(const ALubyte *data, size_t datalen, const char *filename)
{
- const ALubyte maxDelay = HRTF_HISTORY_LENGTH-1;
struct Hrtf *Hrtf = NULL;
ALboolean failed = AL_FALSE;
- ALuint rate = 0, irCount = 0;
- ALubyte irSize = 0, evCount = 0;
- ALubyte *azCount = NULL;
+ ALuint rate = 0;
+ ALushort irCount = 0;
+ ALushort irSize = 0;
+ ALubyte evCount = 0;
+ const ALubyte *azCount = NULL;
ALushort *evOffset = NULL;
- ALshort *coeffs = NULL;
- ALubyte *delays = NULL;
- ALuint i, j;
+ ALfloat (*coeffs)[2] = NULL;
+ ALubyte (*delays)[2] = NULL;
+ ALsizei i, j;
+
+ if(datalen < 6)
+ {
+ ERR("Unexpected end of %s data (req %d, rem "SZFMT"\n", filename, 6, datalen);
+ return NULL;
+ }
- rate = fgetc(f);
- rate |= fgetc(f)<<8;
- rate |= fgetc(f)<<16;
- rate |= fgetc(f)<<24;
+ rate = GetLE_ALuint(&data, &datalen);
- irSize = fgetc(f);
+ irSize = GetLE_ALubyte(&data, &datalen);
- evCount = fgetc(f);
+ evCount = GetLE_ALubyte(&data, &datalen);
if(irSize < MIN_IR_SIZE || irSize > MAX_IR_SIZE || (irSize%MOD_IR_SIZE))
{
@@ -616,11 +659,17 @@ static struct Hrtf *LoadHrtf01(FILE *f)
evCount, MIN_EV_COUNT, MAX_EV_COUNT);
failed = AL_TRUE;
}
-
if(failed)
return NULL;
- azCount = malloc(sizeof(azCount[0])*evCount);
+ if(datalen < evCount)
+ {
+ ERR("Unexpected end of %s data (req %d, rem "SZFMT"\n", filename, evCount, datalen);
+ return NULL;
+ }
+
+ azCount = Get_ALubytePtr(&data, &datalen, evCount);
+
evOffset = malloc(sizeof(evOffset[0])*evCount);
if(azCount == NULL || evOffset == NULL)
{
@@ -632,7 +681,6 @@ static struct Hrtf *LoadHrtf01(FILE *f)
{
for(i = 0;i < evCount;i++)
{
- azCount[i] = fgetc(f);
if(azCount[i] < MIN_AZ_COUNT || azCount[i] > MAX_AZ_COUNT)
{
ERR("Unsupported azimuth count: azCount[%d]=%d (%d to %d)\n",
@@ -663,37 +711,193 @@ static struct Hrtf *LoadHrtf01(FILE *f)
if(!failed)
{
- for(i = 0;i < irCount*irSize;i+=irSize)
+ size_t reqsize = 2*irSize*irCount + irCount;
+ if(datalen < reqsize)
+ {
+ ERR("Unexpected end of %s data (req "SZFMT", rem "SZFMT"\n",
+ filename, reqsize, datalen);
+ failed = AL_TRUE;
+ }
+ }
+
+ if(!failed)
+ {
+ for(i = 0;i < irCount;i++)
{
for(j = 0;j < irSize;j++)
- {
- ALshort coeff;
- coeff = fgetc(f);
- coeff |= fgetc(f)<<8;
- coeffs[i+j] = coeff;
- }
+ coeffs[i*irSize + j][0] = GetLE_ALshort(&data, &datalen) / 32768.0f;
}
+
for(i = 0;i < irCount;i++)
{
- delays[i] = fgetc(f);
- if(delays[i] > maxDelay)
+ delays[i][0] = GetLE_ALubyte(&data, &datalen);
+ if(delays[i][0] > MAX_HRIR_DELAY)
{
- ERR("Invalid delays[%d]: %d (%d)\n", i, delays[i], maxDelay);
+ ERR("Invalid delays[%d]: %d (%d)\n", i, delays[i][0], MAX_HRIR_DELAY);
failed = AL_TRUE;
}
}
+ }
- if(feof(f))
+ if(!failed)
+ {
+ /* Mirror the left ear responses to the right ear. */
+ for(i = 0;i < evCount;i++)
{
- ERR("Premature end of data\n");
+ ALushort evoffset = evOffset[i];
+ ALubyte azcount = azCount[i];
+ for(j = 0;j < azcount;j++)
+ {
+ ALsizei lidx = evoffset + j;
+ ALsizei ridx = evoffset + ((azcount-j) % azcount);
+ ALsizei k;
+
+ for(k = 0;k < irSize;k++)
+ coeffs[ridx*irSize + k][1] = coeffs[lidx*irSize + k][0];
+ delays[ridx][1] = delays[lidx][0];
+ }
+ }
+
+ Hrtf = CreateHrtfStore(rate, irSize, 0.0f, evCount, irCount, azCount,
+ evOffset, coeffs, delays, filename);
+ }
+
+ free(evOffset);
+ free(coeffs);
+ free(delays);
+ return Hrtf;
+}
+
+#define SAMPLETYPE_S16 0
+#define SAMPLETYPE_S24 1
+
+#define CHANTYPE_LEFTONLY 0
+#define CHANTYPE_LEFTRIGHT 1
+
+static struct Hrtf *LoadHrtf02(const ALubyte *data, size_t datalen, const char *filename)
+{
+ struct Hrtf *Hrtf = NULL;
+ ALboolean failed = AL_FALSE;
+ ALuint rate = 0;
+ ALubyte sampleType;
+ ALubyte channelType;
+ ALushort irCount = 0;
+ ALushort irSize = 0;
+ ALubyte fdCount = 0;
+ ALushort distance = 0;
+ ALubyte evCount = 0;
+ const ALubyte *azCount = NULL;
+ ALushort *evOffset = NULL;
+ ALfloat (*coeffs)[2] = NULL;
+ ALubyte (*delays)[2] = NULL;
+ ALsizei i, j;
+
+ if(datalen < 8)
+ {
+ ERR("Unexpected end of %s data (req %d, rem "SZFMT"\n", filename, 8, datalen);
+ return NULL;
+ }
+
+ rate = GetLE_ALuint(&data, &datalen);
+ sampleType = GetLE_ALubyte(&data, &datalen);
+ channelType = GetLE_ALubyte(&data, &datalen);
+
+ irSize = GetLE_ALubyte(&data, &datalen);
+
+ fdCount = GetLE_ALubyte(&data, &datalen);
+
+ if(sampleType > SAMPLETYPE_S24)
+ {
+ ERR("Unsupported sample type: %d\n", sampleType);
+ failed = AL_TRUE;
+ }
+ if(channelType > CHANTYPE_LEFTRIGHT)
+ {
+ ERR("Unsupported channel type: %d\n", channelType);
+ 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",
+ irSize, MIN_IR_SIZE, MAX_IR_SIZE, MOD_IR_SIZE);
+ failed = AL_TRUE;
+ }
+ if(fdCount != 1)
+ {
+ ERR("Multiple field-depths not supported: fdCount=%d (%d to %d)\n",
+ evCount, MIN_FD_COUNT, MAX_FD_COUNT);
+ failed = AL_TRUE;
+ }
+ if(failed)
+ return NULL;
+
+ for(i = 0;i < fdCount;i++)
+ {
+ if(datalen < 3)
+ {
+ ERR("Unexpected end of %s data (req %d, rem "SZFMT"\n", filename, 3, datalen);
+ return NULL;
+ }
+
+ distance = GetLE_ALushort(&data, &datalen);
+ if(distance < MIN_FD_DISTANCE || distance > MAX_FD_DISTANCE)
+ {
+ ERR("Unsupported field distance: distance=%d (%dmm to %dmm)\n",
+ distance, MIN_FD_DISTANCE, MAX_FD_DISTANCE);
failed = AL_TRUE;
}
+
+ evCount = GetLE_ALubyte(&data, &datalen);
+ if(evCount < MIN_EV_COUNT || evCount > MAX_EV_COUNT)
+ {
+ ERR("Unsupported elevation count: evCount=%d (%d to %d)\n",
+ evCount, MIN_EV_COUNT, MAX_EV_COUNT);
+ failed = AL_TRUE;
+ }
+ if(failed)
+ return NULL;
+
+ if(datalen < evCount)
+ {
+ ERR("Unexpected end of %s data (req %d, rem "SZFMT"\n", filename, evCount, datalen);
+ return NULL;
+ }
+
+ azCount = Get_ALubytePtr(&data, &datalen, evCount);
+ for(j = 0;j < evCount;j++)
+ {
+ if(azCount[j] < MIN_AZ_COUNT || azCount[j] > MAX_AZ_COUNT)
+ {
+ ERR("Unsupported azimuth count: azCount[%d]=%d (%d to %d)\n",
+ j, azCount[j], MIN_AZ_COUNT, MAX_AZ_COUNT);
+ failed = AL_TRUE;
+ }
+ }
+ }
+ if(failed)
+ return NULL;
+
+ evOffset = malloc(sizeof(evOffset[0])*evCount);
+ if(azCount == NULL || evOffset == NULL)
+ {
+ ERR("Out of memory.\n");
+ failed = AL_TRUE;
}
if(!failed)
{
- Hrtf = malloc(sizeof(struct Hrtf));
- if(Hrtf == NULL)
+ evOffset[0] = 0;
+ irCount = azCount[0];
+ for(i = 1;i < evCount;i++)
+ {
+ evOffset[i] = evOffset[i-1] + azCount[i-1];
+ irCount += azCount[i];
+ }
+
+ coeffs = malloc(sizeof(coeffs[0])*irSize*irCount);
+ delays = malloc(sizeof(delays[0])*irCount);
+ if(coeffs == NULL || delays == NULL)
{
ERR("Out of memory.\n");
failed = AL_TRUE;
@@ -702,199 +906,560 @@ static struct Hrtf *LoadHrtf01(FILE *f)
if(!failed)
{
- Hrtf->sampleRate = rate;
- Hrtf->irSize = irSize;
- Hrtf->evCount = evCount;
- Hrtf->azCount = azCount;
- Hrtf->evOffset = evOffset;
- Hrtf->coeffs = coeffs;
- Hrtf->delays = delays;
- AL_STRING_INIT(Hrtf->filename);
- Hrtf->next = NULL;
- return Hrtf;
+ size_t reqsize = 2*irSize*irCount + irCount;
+ if(datalen < reqsize)
+ {
+ ERR("Unexpected end of %s data (req "SZFMT", rem "SZFMT"\n",
+ filename, reqsize, datalen);
+ failed = AL_TRUE;
+ }
+ }
+
+ if(!failed)
+ {
+ if(channelType == CHANTYPE_LEFTONLY)
+ {
+ if(sampleType == SAMPLETYPE_S16)
+ for(i = 0;i < irCount;i++)
+ {
+ for(j = 0;j < irSize;j++)
+ coeffs[i*irSize + j][0] = GetLE_ALshort(&data, &datalen) / 32768.0f;
+ }
+ else if(sampleType == SAMPLETYPE_S24)
+ for(i = 0;i < irCount;i++)
+ {
+ for(j = 0;j < irSize;j++)
+ coeffs[i*irSize + j][0] = GetLE_ALint24(&data, &datalen) / 8388608.0f;
+ }
+
+ for(i = 0;i < irCount;i++)
+ {
+ delays[i][0] = GetLE_ALubyte(&data, &datalen);
+ if(delays[i][0] > MAX_HRIR_DELAY)
+ {
+ ERR("Invalid delays[%d][0]: %d (%d)\n", i, delays[i][0], MAX_HRIR_DELAY);
+ failed = AL_TRUE;
+ }
+ }
+ }
+ else if(channelType == CHANTYPE_LEFTRIGHT)
+ {
+ if(sampleType == SAMPLETYPE_S16)
+ for(i = 0;i < irCount;i++)
+ {
+ for(j = 0;j < irSize;j++)
+ {
+ coeffs[i*irSize + j][0] = GetLE_ALshort(&data, &datalen) / 32768.0f;
+ coeffs[i*irSize + j][1] = GetLE_ALshort(&data, &datalen) / 32768.0f;
+ }
+ }
+ else if(sampleType == SAMPLETYPE_S24)
+ for(i = 0;i < irCount;i++)
+ {
+ for(j = 0;j < irSize;j++)
+ {
+ coeffs[i*irSize + j][0] = GetLE_ALint24(&data, &datalen) / 8388608.0f;
+ coeffs[i*irSize + j][1] = GetLE_ALint24(&data, &datalen) / 8388608.0f;
+ }
+ }
+
+ for(i = 0;i < irCount;i++)
+ {
+ delays[i][0] = GetLE_ALubyte(&data, &datalen);
+ if(delays[i][0] > MAX_HRIR_DELAY)
+ {
+ ERR("Invalid delays[%d][0]: %d (%d)\n", i, delays[i][0], MAX_HRIR_DELAY);
+ failed = AL_TRUE;
+ }
+ delays[i][1] = GetLE_ALubyte(&data, &datalen);
+ if(delays[i][1] > MAX_HRIR_DELAY)
+ {
+ ERR("Invalid delays[%d][1]: %d (%d)\n", i, delays[i][1], MAX_HRIR_DELAY);
+ failed = AL_TRUE;
+ }
+ }
+ }
+ }
+
+ if(!failed)
+ {
+ if(channelType == CHANTYPE_LEFTONLY)
+ {
+ /* Mirror the left ear responses to the right ear. */
+ for(i = 0;i < evCount;i++)
+ {
+ ALushort evoffset = evOffset[i];
+ ALubyte azcount = azCount[i];
+ for(j = 0;j < azcount;j++)
+ {
+ ALsizei lidx = evoffset + j;
+ ALsizei ridx = evoffset + ((azcount-j) % azcount);
+ ALsizei k;
+
+ for(k = 0;k < irSize;k++)
+ coeffs[ridx*irSize + k][1] = coeffs[lidx*irSize + k][0];
+ delays[ridx][1] = delays[lidx][0];
+ }
+ }
+ }
+
+ Hrtf = CreateHrtfStore(rate, irSize,
+ (ALfloat)distance / 1000.0f, evCount, irCount, azCount, evOffset,
+ coeffs, delays, filename
+ );
}
- free(azCount);
free(evOffset);
free(coeffs);
free(delays);
- return NULL;
+ return Hrtf;
}
-static void AddFileEntry(vector_HrtfEntry *list, al_string *filename)
+static void AddFileEntry(vector_EnumeratedHrtf *list, const_al_string filename)
{
- HrtfEntry entry = { AL_STRING_INIT_STATIC(), *filename, NULL };
- HrtfEntry *iter;
+ EnumeratedHrtf entry = { AL_STRING_INIT_STATIC(), NULL };
+ struct HrtfEntry *loaded_entry;
+ const EnumeratedHrtf *iter;
const char *name;
+ const char *ext;
int i;
- name = strrchr(al_string_get_cstr(entry.filename), '/');
- if(!name) name = strrchr(al_string_get_cstr(entry.filename), '\\');
- if(!name) name = al_string_get_cstr(entry.filename);
- else ++name;
-
- entry.hrtf = LoadedHrtfs;
- while(entry.hrtf)
+ /* Check if this file has already been loaded globally. */
+ loaded_entry = LoadedHrtfs;
+ while(loaded_entry)
{
- if(al_string_cmp(entry.filename, entry.hrtf->filename) == 0)
+ if(alstr_cmp_cstr(filename, loaded_entry->filename) == 0)
+ {
+ /* Check if this entry has already been added to the list. */
+#define MATCH_ENTRY(i) (loaded_entry == (i)->hrtf)
+ VECTOR_FIND_IF(iter, const EnumeratedHrtf, *list, MATCH_ENTRY);
+#undef MATCH_ENTRY
+ if(iter != VECTOR_END(*list))
+ {
+ TRACE("Skipping duplicate file entry %s\n", alstr_get_cstr(filename));
+ return;
+ }
+
break;
- entry.hrtf = entry.hrtf->next;
+ }
+ loaded_entry = loaded_entry->next;
}
- if(!entry.hrtf)
+ if(!loaded_entry)
{
- struct Hrtf *hrtf = NULL;
- ALchar magic[8];
- FILE *f;
+ TRACE("Got new file \"%s\"\n", alstr_get_cstr(filename));
+
+ loaded_entry = al_calloc(DEF_ALIGN,
+ FAM_SIZE(struct HrtfEntry, filename, alstr_length(filename)+1)
+ );
+ loaded_entry->next = LoadedHrtfs;
+ loaded_entry->handle = NULL;
+ strcpy(loaded_entry->filename, alstr_get_cstr(filename));
+ LoadedHrtfs = loaded_entry;
+ }
- TRACE("Loading %s...\n", al_string_get_cstr(entry.filename));
- f = al_fopen(al_string_get_cstr(entry.filename), "rb");
- if(f == NULL)
+ /* TODO: Get a human-readable name from the HRTF data (possibly coming in a
+ * format update). */
+ name = strrchr(alstr_get_cstr(filename), '/');
+ if(!name) name = strrchr(alstr_get_cstr(filename), '\\');
+ if(!name) name = alstr_get_cstr(filename);
+ else ++name;
+
+ ext = strrchr(name, '.');
+
+ i = 0;
+ do {
+ if(!ext)
+ alstr_copy_cstr(&entry.name, name);
+ else
+ alstr_copy_range(&entry.name, name, ext);
+ if(i != 0)
{
- ERR("Could not open %s\n", al_string_get_cstr(entry.filename));
- goto error;
+ char str[64];
+ snprintf(str, sizeof(str), " #%d", i+1);
+ alstr_append_cstr(&entry.name, str);
}
+ ++i;
- if(fread(magic, 1, sizeof(magic), f) != sizeof(magic))
- ERR("Failed to read header from %s\n", al_string_get_cstr(entry.filename));
- else
+#define MATCH_NAME(i) (alstr_cmp(entry.name, (i)->name) == 0)
+ VECTOR_FIND_IF(iter, const EnumeratedHrtf, *list, MATCH_NAME);
+#undef MATCH_NAME
+ } while(iter != VECTOR_END(*list));
+ entry.hrtf = loaded_entry;
+
+ TRACE("Adding entry \"%s\" from file \"%s\"\n", alstr_get_cstr(entry.name),
+ alstr_get_cstr(filename));
+ VECTOR_PUSH_BACK(*list, entry);
+}
+
+/* Unfortunate that we have to duplicate AddFileEntry to take a memory buffer
+ * for input instead of opening the given filename.
+ */
+static void AddBuiltInEntry(vector_EnumeratedHrtf *list, const_al_string filename, ALuint residx)
+{
+ EnumeratedHrtf entry = { AL_STRING_INIT_STATIC(), NULL };
+ struct HrtfEntry *loaded_entry;
+ struct Hrtf *hrtf = NULL;
+ const EnumeratedHrtf *iter;
+ const char *name;
+ const char *ext;
+ int i;
+
+ loaded_entry = LoadedHrtfs;
+ while(loaded_entry)
+ {
+ if(alstr_cmp_cstr(filename, loaded_entry->filename) == 0)
{
- if(memcmp(magic, magicMarker00, sizeof(magicMarker00)) == 0)
- {
- TRACE("Detected data set format v0\n");
- hrtf = LoadHrtf00(f);
- }
- else if(memcmp(magic, magicMarker01, sizeof(magicMarker01)) == 0)
+#define MATCH_ENTRY(i) (loaded_entry == (i)->hrtf)
+ VECTOR_FIND_IF(iter, const EnumeratedHrtf, *list, MATCH_ENTRY);
+#undef MATCH_ENTRY
+ if(iter != VECTOR_END(*list))
{
- TRACE("Detected data set format v1\n");
- hrtf = LoadHrtf01(f);
+ TRACE("Skipping duplicate file entry %s\n", alstr_get_cstr(filename));
+ return;
}
- else
- ERR("Invalid header in %s: \"%.8s\"\n", al_string_get_cstr(entry.filename), magic);
- }
- fclose(f);
- if(!hrtf)
- {
- ERR("Failed to load %s\n", al_string_get_cstr(entry.filename));
- goto error;
+ break;
}
+ loaded_entry = loaded_entry->next;
+ }
- al_string_copy(&hrtf->filename, entry.filename);
- hrtf->next = LoadedHrtfs;
- LoadedHrtfs = hrtf;
- TRACE("Loaded HRTF support for format: %s %uhz\n",
- DevFmtChannelsString(DevFmtStereo), hrtf->sampleRate);
- entry.hrtf = hrtf;
+ if(!loaded_entry)
+ {
+ size_t namelen = alstr_length(filename)+32;
+
+ TRACE("Got new file \"%s\"\n", alstr_get_cstr(filename));
+
+ loaded_entry = al_calloc(DEF_ALIGN,
+ FAM_SIZE(struct HrtfEntry, filename, namelen)
+ );
+ loaded_entry->next = LoadedHrtfs;
+ loaded_entry->handle = hrtf;
+ snprintf(loaded_entry->filename, namelen, "!%u_%s",
+ residx, alstr_get_cstr(filename));
+ LoadedHrtfs = loaded_entry;
}
/* TODO: Get a human-readable name from the HRTF data (possibly coming in a
* format update). */
+ name = strrchr(alstr_get_cstr(filename), '/');
+ if(!name) name = strrchr(alstr_get_cstr(filename), '\\');
+ if(!name) name = alstr_get_cstr(filename);
+ else ++name;
+
+ ext = strrchr(name, '.');
i = 0;
do {
- al_string_copy_cstr(&entry.name, name);
+ if(!ext)
+ alstr_copy_cstr(&entry.name, name);
+ else
+ alstr_copy_range(&entry.name, name, ext);
if(i != 0)
{
char str[64];
snprintf(str, sizeof(str), " #%d", i+1);
- al_string_append_cstr(&entry.name, str);
+ alstr_append_cstr(&entry.name, str);
}
++i;
-#define MATCH_NAME(i) (al_string_cmp(entry.name, (i)->name) == 0)
- VECTOR_FIND_IF(iter, HrtfEntry, *list, MATCH_NAME);
+#define MATCH_NAME(i) (alstr_cmp(entry.name, (i)->name) == 0)
+ VECTOR_FIND_IF(iter, const EnumeratedHrtf, *list, MATCH_NAME);
#undef MATCH_NAME
- } while(iter != VECTOR_ITER_END(*list));
+ } while(iter != VECTOR_END(*list));
+ entry.hrtf = loaded_entry;
- TRACE("Adding entry \"%s\" from file \"%s\"\n", al_string_get_cstr(entry.name),
- al_string_get_cstr(entry.filename));
+ TRACE("Adding built-in entry \"%s\"\n", alstr_get_cstr(entry.name));
VECTOR_PUSH_BACK(*list, entry);
- return;
+}
+
+
+#define IDR_DEFAULT_44100_MHR 1
+#define IDR_DEFAULT_48000_MHR 2
+
+#ifndef ALSOFT_EMBED_HRTF_DATA
-error:
- al_string_deinit(&entry.filename);
+static const ALubyte *GetResource(int UNUSED(name), size_t *size)
+{
+ *size = 0;
+ return NULL;
+}
+
+#else
+
+#include "default-44100.mhr.h"
+#include "default-48000.mhr.h"
+
+static const ALubyte *GetResource(int name, size_t *size)
+{
+ if(name == IDR_DEFAULT_44100_MHR)
+ {
+ *size = sizeof(hrtf_default_44100);
+ return hrtf_default_44100;
+ }
+ if(name == IDR_DEFAULT_48000_MHR)
+ {
+ *size = sizeof(hrtf_default_48000);
+ return hrtf_default_48000;
+ }
+ *size = 0;
+ return NULL;
}
+#endif
-vector_HrtfEntry EnumerateHrtf(const_al_string devname)
+vector_EnumeratedHrtf EnumerateHrtf(const_al_string devname)
{
- vector_HrtfEntry list = VECTOR_INIT_STATIC();
- const char *fnamelist = "default-%r.mhr";
+ vector_EnumeratedHrtf list = VECTOR_INIT_STATIC();
+ const char *defaulthrtf = "";
+ const char *pathlist = "";
+ bool usedefaults = true;
- ConfigValueStr(al_string_get_cstr(devname), NULL, "hrtf_tables", &fnamelist);
- while(fnamelist && *fnamelist)
+ if(ConfigValueStr(alstr_get_cstr(devname), NULL, "hrtf-paths", &pathlist))
{
- while(isspace(*fnamelist) || *fnamelist == ',')
- fnamelist++;
- if(*fnamelist != '\0')
+ al_string pname = AL_STRING_INIT_STATIC();
+ while(pathlist && *pathlist)
{
const char *next, *end;
- next = strchr(fnamelist, ',');
- if(!next)
- end = fnamelist + strlen(fnamelist);
- else
+ while(isspace(*pathlist) || *pathlist == ',')
+ pathlist++;
+ if(*pathlist == '\0')
+ continue;
+
+ next = strchr(pathlist, ',');
+ if(next)
end = next++;
+ else
+ {
+ end = pathlist + strlen(pathlist);
+ usedefaults = false;
+ }
- while(end != fnamelist && isspace(*(end-1)))
+ while(end != pathlist && isspace(*(end-1)))
--end;
- if(end != fnamelist)
+ if(end != pathlist)
{
- al_string fname = AL_STRING_INIT_STATIC();
vector_al_string flist;
+ size_t i;
- al_string_append_range(&fname, fnamelist, end);
+ alstr_copy_range(&pname, pathlist, end);
- flist = SearchDataFiles(al_string_get_cstr(fname), "openal/hrtf");
- VECTOR_FOR_EACH_PARAMS(al_string, flist, AddFileEntry, &list);
+ flist = SearchDataFiles(".mhr", alstr_get_cstr(pname));
+ for(i = 0;i < VECTOR_SIZE(flist);i++)
+ AddFileEntry(&list, VECTOR_ELEM(flist, i));
+ VECTOR_FOR_EACH(al_string, flist, alstr_reset);
VECTOR_DEINIT(flist);
-
- al_string_deinit(&fname);
}
- fnamelist = next;
+ pathlist = next;
+ }
+
+ alstr_reset(&pname);
+ }
+ else if(ConfigValueExists(alstr_get_cstr(devname), NULL, "hrtf_tables"))
+ ERR("The hrtf_tables option is deprecated, please use hrtf-paths instead.\n");
+
+ if(usedefaults)
+ {
+ al_string ename = AL_STRING_INIT_STATIC();
+ vector_al_string flist;
+ const ALubyte *rdata;
+ size_t rsize, i;
+
+ flist = SearchDataFiles(".mhr", "openal/hrtf");
+ for(i = 0;i < VECTOR_SIZE(flist);i++)
+ AddFileEntry(&list, VECTOR_ELEM(flist, i));
+ VECTOR_FOR_EACH(al_string, flist, alstr_reset);
+ VECTOR_DEINIT(flist);
+
+ rdata = GetResource(IDR_DEFAULT_44100_MHR, &rsize);
+ if(rdata != NULL && rsize > 0)
+ {
+ alstr_copy_cstr(&ename, "Built-In 44100hz");
+ AddBuiltInEntry(&list, ename, IDR_DEFAULT_44100_MHR);
+ }
+
+ rdata = GetResource(IDR_DEFAULT_48000_MHR, &rsize);
+ if(rdata != NULL && rsize > 0)
+ {
+ alstr_copy_cstr(&ename, "Built-In 48000hz");
+ AddBuiltInEntry(&list, ename, IDR_DEFAULT_48000_MHR);
+ }
+ alstr_reset(&ename);
+ }
+
+ if(VECTOR_SIZE(list) > 1 && ConfigValueStr(alstr_get_cstr(devname), NULL, "default-hrtf", &defaulthrtf))
+ {
+ const EnumeratedHrtf *iter;
+ /* Find the preferred HRTF and move it to the front of the list. */
+#define FIND_ENTRY(i) (alstr_cmp_cstr((i)->name, defaulthrtf) == 0)
+ VECTOR_FIND_IF(iter, const EnumeratedHrtf, list, FIND_ENTRY);
+#undef FIND_ENTRY
+ if(iter == VECTOR_END(list))
+ WARN("Failed to find default HRTF \"%s\"\n", defaulthrtf);
+ else if(iter != VECTOR_BEGIN(list))
+ {
+ EnumeratedHrtf entry = *iter;
+ memmove(&VECTOR_ELEM(list,1), &VECTOR_ELEM(list,0),
+ (iter-VECTOR_BEGIN(list))*sizeof(EnumeratedHrtf));
+ VECTOR_ELEM(list,0) = entry;
}
}
return list;
}
-void FreeHrtfList(vector_HrtfEntry *list)
+void FreeHrtfList(vector_EnumeratedHrtf *list)
{
-#define CLEAR_ENTRY(i) do { \
- al_string_deinit(&(i)->name); \
- al_string_deinit(&(i)->filename); \
-} while(0)
- VECTOR_FOR_EACH(HrtfEntry, *list, CLEAR_ENTRY);
+#define CLEAR_ENTRY(i) alstr_reset(&(i)->name)
+ VECTOR_FOR_EACH(EnumeratedHrtf, *list, CLEAR_ENTRY);
VECTOR_DEINIT(*list);
#undef CLEAR_ENTRY
}
+struct Hrtf *GetLoadedHrtf(struct HrtfEntry *entry)
+{
+ struct Hrtf *hrtf = NULL;
+ struct FileMapping fmap;
+ const ALubyte *rdata;
+ const char *name;
+ ALuint residx;
+ size_t rsize;
+ char ch;
+
+ while(ATOMIC_FLAG_TEST_AND_SET(&LoadedHrtfLock, almemory_order_seq_cst))
+ althrd_yield();
+
+ if(entry->handle)
+ {
+ hrtf = entry->handle;
+ Hrtf_IncRef(hrtf);
+ goto done;
+ }
+
+ fmap.ptr = NULL;
+ fmap.len = 0;
+ if(sscanf(entry->filename, "!%u%c", &residx, &ch) == 2 && ch == '_')
+ {
+ name = strchr(entry->filename, ch)+1;
+
+ TRACE("Loading %s...\n", name);
+ rdata = GetResource(residx, &rsize);
+ if(rdata == NULL || rsize == 0)
+ {
+ ERR("Could not get resource %u, %s\n", residx, name);
+ goto done;
+ }
+ }
+ else
+ {
+ name = entry->filename;
+
+ TRACE("Loading %s...\n", entry->filename);
+ fmap = MapFileToMem(entry->filename);
+ if(fmap.ptr == NULL)
+ {
+ ERR("Could not open %s\n", entry->filename);
+ goto done;
+ }
-ALuint GetHrtfSampleRate(const struct Hrtf *Hrtf)
+ rdata = fmap.ptr;
+ rsize = fmap.len;
+ }
+
+ if(rsize < sizeof(magicMarker02))
+ ERR("%s data is too short ("SZFMT" bytes)\n", name, rsize);
+ else if(memcmp(rdata, magicMarker02, sizeof(magicMarker02)) == 0)
+ {
+ TRACE("Detected data set format v2\n");
+ hrtf = LoadHrtf02(rdata+sizeof(magicMarker02),
+ rsize-sizeof(magicMarker02), name
+ );
+ }
+ else if(memcmp(rdata, magicMarker01, sizeof(magicMarker01)) == 0)
+ {
+ TRACE("Detected data set format v1\n");
+ hrtf = LoadHrtf01(rdata+sizeof(magicMarker01),
+ rsize-sizeof(magicMarker01), name
+ );
+ }
+ else if(memcmp(rdata, magicMarker00, sizeof(magicMarker00)) == 0)
+ {
+ TRACE("Detected data set format v0\n");
+ hrtf = LoadHrtf00(rdata+sizeof(magicMarker00),
+ rsize-sizeof(magicMarker00), name
+ );
+ }
+ else
+ ERR("Invalid header in %s: \"%.8s\"\n", name, (const char*)rdata);
+ if(fmap.ptr)
+ UnmapFileMem(&fmap);
+
+ if(!hrtf)
+ {
+ ERR("Failed to load %s\n", name);
+ goto done;
+ }
+ entry->handle = hrtf;
+ Hrtf_IncRef(hrtf);
+
+ TRACE("Loaded HRTF support for format: %s %uhz\n",
+ DevFmtChannelsString(DevFmtStereo), hrtf->sampleRate);
+
+done:
+ ATOMIC_FLAG_CLEAR(&LoadedHrtfLock, almemory_order_seq_cst);
+ return hrtf;
+}
+
+
+void Hrtf_IncRef(struct Hrtf *hrtf)
{
- return Hrtf->sampleRate;
+ uint ref = IncrementRef(&hrtf->ref);
+ TRACEREF("%p increasing refcount to %u\n", hrtf, ref);
}
-ALuint GetHrtfIrSize(const struct Hrtf *Hrtf)
+void Hrtf_DecRef(struct Hrtf *hrtf)
{
- return Hrtf->irSize;
+ struct HrtfEntry *Hrtf;
+ uint ref = DecrementRef(&hrtf->ref);
+ TRACEREF("%p decreasing refcount to %u\n", hrtf, ref);
+ if(ref == 0)
+ {
+ while(ATOMIC_FLAG_TEST_AND_SET(&LoadedHrtfLock, almemory_order_seq_cst))
+ althrd_yield();
+
+ Hrtf = LoadedHrtfs;
+ while(Hrtf != NULL)
+ {
+ /* Need to double-check that it's still unused, as another device
+ * could've reacquired this HRTF after its reference went to 0 and
+ * before the lock was taken.
+ */
+ if(hrtf == Hrtf->handle && ReadRef(&hrtf->ref) == 0)
+ {
+ al_free(Hrtf->handle);
+ Hrtf->handle = NULL;
+ TRACE("Unloaded unused HRTF %s\n", Hrtf->filename);
+ }
+ Hrtf = Hrtf->next;
+ }
+
+ ATOMIC_FLAG_CLEAR(&LoadedHrtfLock, almemory_order_seq_cst);
+ }
}
void FreeHrtfs(void)
{
- struct Hrtf *Hrtf = NULL;
+ struct HrtfEntry *Hrtf = LoadedHrtfs;
+ LoadedHrtfs = NULL;
- while((Hrtf=LoadedHrtfs) != NULL)
+ while(Hrtf != NULL)
{
- LoadedHrtfs = Hrtf->next;
- free((void*)Hrtf->azCount);
- free((void*)Hrtf->evOffset);
- free((void*)Hrtf->coeffs);
- free((void*)Hrtf->delays);
- al_string_deinit(&Hrtf->filename);
- free(Hrtf);
+ struct HrtfEntry *next = Hrtf->next;
+ al_free(Hrtf->handle);
+ al_free(Hrtf);
+ Hrtf = next;
}
}
diff --git a/Alc/hrtf.h b/Alc/hrtf.h
index 6dcd6948..ab68929b 100644
--- a/Alc/hrtf.h
+++ b/Alc/hrtf.h
@@ -4,37 +4,81 @@
#include "AL/al.h"
#include "AL/alc.h"
+#include "alMain.h"
#include "alstring.h"
+#include "atomic.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 HRTF_HISTORY_BITS (6)
+#define HRTF_HISTORY_LENGTH (1<<HRTF_HISTORY_BITS)
+#define HRTF_HISTORY_MASK (HRTF_HISTORY_LENGTH-1)
#define HRIR_BITS (7)
#define HRIR_LENGTH (1<<HRIR_BITS)
#define HRIR_MASK (HRIR_LENGTH-1)
-#define HRTFDELAY_BITS (20)
-#define HRTFDELAY_FRACONE (1<<HRTFDELAY_BITS)
-#define HRTFDELAY_MASK (HRTFDELAY_FRACONE-1)
+
+
+struct HrtfEntry;
+
+struct Hrtf {
+ RefCount ref;
+
+ ALuint sampleRate;
+ ALsizei irSize;
+
+ ALfloat distance;
+ ALubyte evCount;
+
+ const ALubyte *azCount;
+ const ALushort *evOffset;
+ const ALfloat (*coeffs)[2];
+ const ALubyte (*delays)[2];
+};
+
+
+typedef struct HrtfState {
+ alignas(16) ALfloat History[HRTF_HISTORY_LENGTH];
+ alignas(16) ALfloat Values[HRIR_LENGTH][2];
+} HrtfState;
+
+typedef struct HrtfParams {
+ alignas(16) ALfloat Coeffs[HRIR_LENGTH][2];
+ ALsizei Delay[2];
+ ALfloat Gain;
+} HrtfParams;
+
+typedef struct DirectHrtfState {
+ /* HRTF filter state for dry buffer content */
+ ALsizei Offset;
+ ALsizei IrSize;
+ struct {
+ alignas(16) ALfloat Values[HRIR_LENGTH][2];
+ alignas(16) ALfloat Coeffs[HRIR_LENGTH][2];
+ } Chan[];
+} DirectHrtfState;
+
+struct AngularPoint {
+ ALfloat Elev;
+ ALfloat Azim;
+};
+
void FreeHrtfs(void);
-vector_HrtfEntry EnumerateHrtf(const_al_string devname);
-void FreeHrtfList(vector_HrtfEntry *list);
+vector_EnumeratedHrtf EnumerateHrtf(const_al_string devname);
+void FreeHrtfList(vector_EnumeratedHrtf *list);
+struct Hrtf *GetLoadedHrtf(struct HrtfEntry *entry);
+void Hrtf_IncRef(struct Hrtf *hrtf);
+void Hrtf_DecRef(struct Hrtf *hrtf);
-ALuint GetHrtfSampleRate(const struct Hrtf *Hrtf);
-ALuint GetHrtfIrSize(const struct Hrtf *Hrtf);
+void GetHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azimuth, ALfloat spread, ALfloat (*coeffs)[2], ALsizei *delays);
-void GetLerpedHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azimuth, ALfloat dirfact, ALfloat gain, ALfloat (*coeffs)[2], ALuint *delays);
-ALuint GetMovingHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azimuth, ALfloat dirfact, ALfloat gain, ALfloat delta, ALint counter, ALfloat (*coeffs)[2], ALuint *delays, ALfloat (*coeffStep)[2], ALint *delayStep);
-void GetBFormatHrtfCoeffs(const struct Hrtf *Hrtf, const ALuint num_chans, ALfloat (**coeffs_list)[2], ALuint **delay_list);
+/**
+ * Produces HRTF filter coefficients for decoding B-Format, given a set of
+ * virtual speaker positions, a matching decoding matrix, and per-order high-
+ * frequency gains for the decoder. The calculated impulse responses are
+ * ordered and scaled according to the matrix input.
+ */
+void BuildBFormatHrtf(const struct Hrtf *Hrtf, DirectHrtfState *state, ALsizei NumChannels, const struct AngularPoint *AmbiPoints, const ALfloat (*restrict AmbiMatrix)[MAX_AMBI_COEFFS], ALsizei AmbiCount, const ALfloat *restrict AmbiOrderHFGain);
#endif /* ALC_HRTF_H */
diff --git a/Alc/inprogext.h b/Alc/inprogext.h
new file mode 100644
index 00000000..3025abe2
--- /dev/null
+++ b/Alc/inprogext.h
@@ -0,0 +1,87 @@
+#ifndef INPROGEXT_H
+#define INPROGEXT_H
+
+#include "AL/al.h"
+#include "AL/alc.h"
+#include "AL/alext.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef ALC_SOFT_loopback2
+#define ALC_SOFT_loopback2 1
+#define ALC_AMBISONIC_LAYOUT_SOFT 0xfff0
+#define ALC_AMBISONIC_SCALING_SOFT 0xfff1
+#define ALC_AMBISONIC_ORDER_SOFT 0xfff2
+#define ALC_MAX_AMBISONIC_ORDER_SOFT 0xfff3
+
+#define ALC_BFORMAT3D_SOFT 0x1508
+
+/* Ambisonic layouts */
+#define ALC_ACN_SOFT 0xfff4
+#define ALC_FUMA_SOFT 0xfff5
+
+/* Ambisonic scalings (normalization) */
+/*#define ALC_FUMA_SOFT*/
+#define ALC_SN3D_SOFT 0xfff6
+#define ALC_N3D_SOFT 0xfff7
+#endif
+
+#ifndef AL_SOFT_map_buffer
+#define AL_SOFT_map_buffer 1
+typedef unsigned int ALbitfieldSOFT;
+#define AL_MAP_READ_BIT_SOFT 0x00000001
+#define AL_MAP_WRITE_BIT_SOFT 0x00000002
+#define AL_MAP_PERSISTENT_BIT_SOFT 0x00000004
+#define AL_PRESERVE_DATA_BIT_SOFT 0x00000008
+typedef void (AL_APIENTRY*LPALBUFFERSTORAGESOFT)(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei freq, ALbitfieldSOFT flags);
+typedef void* (AL_APIENTRY*LPALMAPBUFFERSOFT)(ALuint buffer, ALsizei offset, ALsizei length, ALbitfieldSOFT access);
+typedef void (AL_APIENTRY*LPALUNMAPBUFFERSOFT)(ALuint buffer);
+typedef void (AL_APIENTRY*LPALFLUSHMAPPEDBUFFERSOFT)(ALuint buffer, ALsizei offset, ALsizei length);
+#ifdef AL_ALEXT_PROTOTYPES
+AL_API void AL_APIENTRY alBufferStorageSOFT(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei freq, ALbitfieldSOFT flags);
+AL_API void* AL_APIENTRY alMapBufferSOFT(ALuint buffer, ALsizei offset, ALsizei length, ALbitfieldSOFT access);
+AL_API void AL_APIENTRY alUnmapBufferSOFT(ALuint buffer);
+AL_API void AL_APIENTRY alFlushMappedBufferSOFT(ALuint buffer, ALsizei offset, ALsizei length);
+#endif
+#endif
+
+#ifndef AL_SOFT_events
+#define AL_SOFT_events 1
+#define AL_EVENT_CALLBACK_FUNCTION_SOFT 0x1220
+#define AL_EVENT_CALLBACK_USER_PARAM_SOFT 0x1221
+#define AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT 0x1222
+#define AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT 0x1223
+#define AL_EVENT_TYPE_ERROR_SOFT 0x1224
+#define AL_EVENT_TYPE_PERFORMANCE_SOFT 0x1225
+#define AL_EVENT_TYPE_DEPRECATED_SOFT 0x1226
+#define AL_EVENT_TYPE_DISCONNECTED_SOFT 0x1227
+typedef void (AL_APIENTRY*ALEVENTPROCSOFT)(ALenum eventType, ALuint object, ALuint param,
+ ALsizei length, const ALchar *message,
+ void *userParam);
+typedef void (AL_APIENTRY*LPALEVENTCONTROLSOFT)(ALsizei count, const ALenum *types, ALboolean enable);
+typedef void (AL_APIENTRY*LPALEVENTCALLBACKSOFT)(ALEVENTPROCSOFT callback, void *userParam);
+typedef void* (AL_APIENTRY*LPALGETPOINTERSOFT)(ALenum pname);
+typedef void (AL_APIENTRY*LPALGETPOINTERVSOFT)(ALenum pname, void **values);
+#ifdef AL_ALEXT_PROTOTYPES
+AL_API void AL_APIENTRY alEventControlSOFT(ALsizei count, const ALenum *types, ALboolean enable);
+AL_API void AL_APIENTRY alEventCallbackSOFT(ALEVENTPROCSOFT callback, void *userParam);
+AL_API void* AL_APIENTRY alGetPointerSOFT(ALenum pname);
+AL_API void AL_APIENTRY alGetPointervSOFT(ALenum pname, void **values);
+#endif
+#endif
+
+#ifndef AL_SOFT_buffer_layers
+#define AL_SOFT_buffer_layers
+typedef void (AL_APIENTRY*LPALSOURCEQUEUEBUFFERLAYERSSOFT)(ALuint src, ALsizei nb, const ALuint *buffers);
+#ifdef AL_ALEXT_PROTOTYPES
+AL_API void AL_APIENTRY alSourceQueueBufferLayersSOFT(ALuint src, ALsizei nb, const ALuint *buffers);
+#endif
+#endif
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* INPROGEXT_H */
diff --git a/Alc/logging.h b/Alc/logging.h
new file mode 100644
index 00000000..785771c8
--- /dev/null
+++ b/Alc/logging.h
@@ -0,0 +1,69 @@
+#ifndef LOGGING_H
+#define LOGGING_H
+
+#include <stdio.h>
+
+
+#ifdef __GNUC__
+#define DECL_FORMAT(x, y, z) __attribute__((format(x, (y), (z))))
+#else
+#define DECL_FORMAT(x, y, z)
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern FILE *LogFile;
+
+#if defined(__GNUC__) && !defined(_WIN32)
+#define AL_PRINT(T, MSG, ...) fprintf(LogFile, "AL lib: %s %s: "MSG, T, __FUNCTION__ , ## __VA_ARGS__)
+#else
+void al_print(const char *type, const char *func, const char *fmt, ...) DECL_FORMAT(printf, 3,4);
+#define AL_PRINT(T, ...) al_print((T), __FUNCTION__, __VA_ARGS__)
+#endif
+
+#ifdef __ANDROID__
+#include <android/log.h>
+#define LOG_ANDROID(T, MSG, ...) __android_log_print(T, "openal", "AL lib: %s: "MSG, __FUNCTION__ , ## __VA_ARGS__)
+#else
+#define LOG_ANDROID(T, MSG, ...) ((void)0)
+#endif
+
+enum LogLevel {
+ NoLog,
+ LogError,
+ LogWarning,
+ LogTrace,
+ LogRef
+};
+extern enum LogLevel LogLevel;
+
+#define TRACEREF(...) do { \
+ if(LogLevel >= LogRef) \
+ AL_PRINT("(--)", __VA_ARGS__); \
+} while(0)
+
+#define TRACE(...) do { \
+ if(LogLevel >= LogTrace) \
+ AL_PRINT("(II)", __VA_ARGS__); \
+ LOG_ANDROID(ANDROID_LOG_DEBUG, __VA_ARGS__); \
+} while(0)
+
+#define WARN(...) do { \
+ if(LogLevel >= LogWarning) \
+ AL_PRINT("(WW)", __VA_ARGS__); \
+ LOG_ANDROID(ANDROID_LOG_WARN, __VA_ARGS__); \
+} while(0)
+
+#define ERR(...) do { \
+ if(LogLevel >= LogError) \
+ AL_PRINT("(EE)", __VA_ARGS__); \
+ LOG_ANDROID(ANDROID_LOG_ERROR, __VA_ARGS__); \
+} while(0)
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* LOGGING_H */
diff --git a/Alc/mastering.c b/Alc/mastering.c
new file mode 100644
index 00000000..6745c1c7
--- /dev/null
+++ b/Alc/mastering.c
@@ -0,0 +1,543 @@
+#include "config.h"
+
+#include <math.h>
+
+#include "mastering.h"
+#include "alu.h"
+#include "almalloc.h"
+#include "static_assert.h"
+#include "math_defs.h"
+
+
+/* Early MSVC lacks round/roundf */
+#if defined(_MSC_VER) && _MSC_VER < 1800
+static double round(double val)
+{
+ if(val < 0.0)
+ return ceil(val-0.5);
+ return floor(val+0.5);
+}
+#define roundf(f) ((float)round((float)(f)))
+#endif
+
+
+/* These structures assume BUFFERSIZE is a power of 2. */
+static_assert((BUFFERSIZE & (BUFFERSIZE-1)) == 0, "BUFFERSIZE is not a power of 2");
+
+typedef struct SlidingHold {
+ ALfloat Values[BUFFERSIZE];
+ ALsizei Expiries[BUFFERSIZE];
+ ALsizei LowerIndex;
+ ALsizei UpperIndex;
+ ALsizei Length;
+} SlidingHold;
+
+/* General topology and basic automation was based on the following paper:
+ *
+ * D. Giannoulis, M. Massberg and J. D. Reiss,
+ * "Parameter Automation in a Dynamic Range Compressor,"
+ * Journal of the Audio Engineering Society, v61 (10), Oct. 2013
+ *
+ * Available (along with supplemental reading) at:
+ *
+ * http://c4dm.eecs.qmul.ac.uk/audioengineering/compressors/
+ */
+typedef struct Compressor {
+ ALsizei NumChans;
+ ALuint SampleRate;
+
+ struct {
+ ALuint Knee : 1;
+ ALuint Attack : 1;
+ ALuint Release : 1;
+ ALuint PostGain : 1;
+ ALuint Declip : 1;
+ } Auto;
+
+ ALsizei LookAhead;
+
+ ALfloat PreGain;
+ ALfloat PostGain;
+
+ ALfloat Threshold;
+ ALfloat Slope;
+ ALfloat Knee;
+
+ ALfloat Attack;
+ ALfloat Release;
+
+ alignas(16) ALfloat SideChain[2*BUFFERSIZE];
+ alignas(16) ALfloat CrestFactor[BUFFERSIZE];
+
+ SlidingHold *Hold;
+ ALfloat (*Delay)[BUFFERSIZE];
+ ALsizei DelayIndex;
+
+ ALfloat CrestCoeff;
+ ALfloat GainEstimate;
+ ALfloat AdaptCoeff;
+
+ ALfloat LastPeakSq;
+ ALfloat LastRmsSq;
+ ALfloat LastRelease;
+ ALfloat LastAttack;
+ ALfloat LastGainDev;
+} Compressor;
+
+
+/* This sliding hold follows the input level with an instant attack and a
+ * fixed duration hold before an instant release to the next highest level.
+ * It is a sliding window maximum (descending maxima) implementation based on
+ * Richard Harter's ascending minima algorithm available at:
+ *
+ * http://www.richardhartersworld.com/cri/2001/slidingmin.html
+ */
+static ALfloat UpdateSlidingHold(SlidingHold *Hold, const ALsizei i, const ALfloat in)
+{
+ const ALsizei mask = BUFFERSIZE - 1;
+ const ALsizei length = Hold->Length;
+ ALfloat *restrict values = Hold->Values;
+ ALsizei *restrict expiries = Hold->Expiries;
+ ALsizei lowerIndex = Hold->LowerIndex;
+ ALsizei upperIndex = Hold->UpperIndex;
+
+ if(i >= expiries[upperIndex])
+ upperIndex = (upperIndex + 1) & mask;
+
+ if(in >= values[upperIndex])
+ {
+ values[upperIndex] = in;
+ expiries[upperIndex] = i + length;
+ lowerIndex = upperIndex;
+ }
+ else
+ {
+ do {
+ do {
+ if(!(in >= values[lowerIndex]))
+ goto found_place;
+ } while(lowerIndex--);
+ lowerIndex = mask;
+ } while(1);
+ found_place:
+
+ lowerIndex = (lowerIndex + 1) & mask;
+ values[lowerIndex] = in;
+ expiries[lowerIndex] = i + length;
+ }
+
+ Hold->LowerIndex = lowerIndex;
+ Hold->UpperIndex = upperIndex;
+
+ return values[upperIndex];
+}
+
+static void ShiftSlidingHold(SlidingHold *Hold, const ALsizei n)
+{
+ const ALsizei lowerIndex = Hold->LowerIndex;
+ ALsizei *restrict expiries = Hold->Expiries;
+ ALsizei i = Hold->UpperIndex;
+
+ if(lowerIndex < i)
+ {
+ for(;i < BUFFERSIZE;i++)
+ expiries[i] -= n;
+ i = 0;
+ }
+ for(;i < lowerIndex;i++)
+ expiries[i] -= n;
+
+ expiries[i] -= n;
+}
+
+/* Multichannel compression is linked via the absolute maximum of all
+ * channels.
+ */
+static void LinkChannels(Compressor *Comp, const ALsizei SamplesToDo, ALfloat (*restrict OutBuffer)[BUFFERSIZE])
+{
+ const ALsizei index = Comp->LookAhead;
+ const ALsizei numChans = Comp->NumChans;
+ ALfloat *restrict sideChain = Comp->SideChain;
+ ALsizei c, i;
+
+ ASSUME(SamplesToDo > 0);
+ ASSUME(numChans > 0);
+
+ for(i = 0;i < SamplesToDo;i++)
+ sideChain[index + i] = 0.0f;
+
+ for(c = 0;c < numChans;c++)
+ {
+ ALsizei offset = index;
+ for(i = 0;i < SamplesToDo;i++)
+ {
+ sideChain[offset] = maxf(sideChain[offset], fabsf(OutBuffer[c][i]));
+ ++offset;
+ }
+ }
+}
+
+/* This calculates the squared crest factor of the control signal for the
+ * basic automation of the attack/release times. As suggested by the paper,
+ * it uses an instantaneous squared peak detector and a squared RMS detector
+ * both with 200ms release times.
+ */
+static void CrestDetector(Compressor *Comp, const ALsizei SamplesToDo)
+{
+ const ALfloat a_crest = Comp->CrestCoeff;
+ const ALsizei index = Comp->LookAhead;
+ const ALfloat *restrict sideChain = Comp->SideChain;
+ ALfloat *restrict crestFactor = Comp->CrestFactor;
+ ALfloat y2_peak = Comp->LastPeakSq;
+ ALfloat y2_rms = Comp->LastRmsSq;
+ ALsizei i;
+
+ ASSUME(SamplesToDo > 0);
+
+ for(i = 0;i < SamplesToDo;i++)
+ {
+ ALfloat x_abs = sideChain[index + i];
+ ALfloat x2 = maxf(0.000001f, x_abs * x_abs);
+
+ y2_peak = maxf(x2, lerp(x2, y2_peak, a_crest));
+ y2_rms = lerp(x2, y2_rms, a_crest);
+ crestFactor[i] = y2_peak / y2_rms;
+ }
+
+ Comp->LastPeakSq = y2_peak;
+ Comp->LastRmsSq = y2_rms;
+}
+
+/* The side-chain starts with a simple peak detector (based on the absolute
+ * value of the incoming signal) and performs most of its operations in the
+ * log domain.
+ */
+static void PeakDetector(Compressor *Comp, const ALsizei SamplesToDo)
+{
+ const ALsizei index = Comp->LookAhead;
+ ALfloat *restrict sideChain = Comp->SideChain;
+ ALsizei i;
+
+ ASSUME(SamplesToDo > 0);
+
+ for(i = 0;i < SamplesToDo;i++)
+ {
+ const ALuint offset = index + i;
+ const ALfloat x_abs = sideChain[offset];
+
+ sideChain[offset] = logf(maxf(0.000001f, x_abs));
+ }
+}
+
+/* An optional hold can be used to extend the peak detector so it can more
+ * solidly detect fast transients. This is best used when operating as a
+ * limiter.
+ */
+static void PeakHoldDetector(Compressor *Comp, const ALsizei SamplesToDo)
+{
+ const ALsizei index = Comp->LookAhead;
+ ALfloat *restrict sideChain = Comp->SideChain;
+ SlidingHold *hold = Comp->Hold;
+ ALsizei i;
+
+ ASSUME(SamplesToDo > 0);
+
+ for(i = 0;i < SamplesToDo;i++)
+ {
+ const ALsizei offset = index + i;
+ const ALfloat x_abs = sideChain[offset];
+ const ALfloat x_G = logf(maxf(0.000001f, x_abs));
+
+ sideChain[offset] = UpdateSlidingHold(hold, i, x_G);
+ }
+
+ ShiftSlidingHold(hold, SamplesToDo);
+}
+
+/* This is the heart of the feed-forward compressor. It operates in the log
+ * domain (to better match human hearing) and can apply some basic automation
+ * to knee width, attack/release times, make-up/post gain, and clipping
+ * reduction.
+ */
+static void GainCompressor(Compressor *Comp, const ALsizei SamplesToDo)
+{
+ const bool autoKnee = Comp->Auto.Knee;
+ const bool autoAttack = Comp->Auto.Attack;
+ const bool autoRelease = Comp->Auto.Release;
+ const bool autoPostGain = Comp->Auto.PostGain;
+ const bool autoDeclip = Comp->Auto.Declip;
+ const ALsizei lookAhead = Comp->LookAhead;
+ const ALfloat threshold = Comp->Threshold;
+ const ALfloat slope = Comp->Slope;
+ const ALfloat attack = Comp->Attack;
+ const ALfloat release = Comp->Release;
+ const ALfloat c_est = Comp->GainEstimate;
+ const ALfloat a_adp = Comp->AdaptCoeff;
+ const ALfloat *restrict crestFactor = Comp->CrestFactor;
+ ALfloat *restrict sideChain = Comp->SideChain;
+ ALfloat postGain = Comp->PostGain;
+ ALfloat knee = Comp->Knee;
+ ALfloat t_att = attack;
+ ALfloat t_rel = release - attack;
+ ALfloat a_att = expf(-1.0f / t_att);
+ ALfloat a_rel = expf(-1.0f / t_rel);
+ ALfloat y_1 = Comp->LastRelease;
+ ALfloat y_L = Comp->LastAttack;
+ ALfloat c_dev = Comp->LastGainDev;
+ ALsizei i;
+
+ ASSUME(SamplesToDo > 0);
+
+ for(i = 0;i < SamplesToDo;i++)
+ {
+ const ALfloat y2_crest = crestFactor[i];
+ const ALfloat x_G = sideChain[lookAhead + i];
+ const ALfloat x_over = x_G - threshold;
+ ALfloat knee_h;
+ ALfloat y_G;
+ ALfloat x_L;
+
+ if(autoKnee)
+ knee = maxf(0.0f, 2.5f * (c_dev + c_est));
+ knee_h = 0.5f * knee;
+
+ /* This is the gain computer. It applies a static compression curve
+ * to the control signal.
+ */
+ if(x_over <= -knee_h)
+ y_G = 0.0f;
+ else if(fabsf(x_over) < knee_h)
+ y_G = (x_over + knee_h) * (x_over + knee_h) / (2.0f * knee);
+ else
+ y_G = x_over;
+
+ x_L = -slope * y_G;
+
+ if(autoAttack)
+ {
+ t_att = 2.0f * attack / y2_crest;
+ a_att = expf(-1.0f / t_att);
+ }
+
+ if(autoRelease)
+ {
+ t_rel = 2.0f * release / y2_crest - t_att;
+ a_rel = expf(-1.0f / t_rel);
+ }
+
+ /* Gain smoothing (ballistics) is done via a smooth decoupled peak
+ * detector. The attack time is subtracted from the release time
+ * above to compensate for the chained operating mode.
+ */
+ y_1 = maxf(x_L, lerp(x_L, y_1, a_rel));
+ y_L = lerp(y_1, y_L, a_att);
+
+ /* Knee width and make-up gain automation make use of a smoothed
+ * measurement of deviation between the control signal and estimate.
+ * The estimate is also used to bias the measurement to hot-start its
+ * average.
+ */
+ c_dev = lerp(-y_L - c_est, c_dev, a_adp);
+
+ if(autoPostGain)
+ {
+ /* Clipping reduction is only viable when make-up gain is being
+ * automated. It modifies the deviation to further attenuate the
+ * control signal when clipping is detected. The adaptation
+ * time is sufficiently long enough to suppress further clipping
+ * at the same output level.
+ */
+ if(autoDeclip)
+ c_dev = maxf(c_dev, sideChain[i] - y_L - threshold - c_est);
+
+ postGain = -(c_dev + c_est);
+ }
+
+ sideChain[i] = expf(postGain - y_L);
+ }
+
+ Comp->LastRelease = y_1;
+ Comp->LastAttack = y_L;
+ Comp->LastGainDev = c_dev;
+}
+
+/* Combined with the hold time, a look-ahead delay can improve handling of
+ * fast transients by allowing the envelope time to converge prior to
+ * reaching the offending impulse. This is best used when operating as a
+ * limiter.
+ */
+static void SignalDelay(Compressor *Comp, const ALsizei SamplesToDo, ALfloat (*restrict OutBuffer)[BUFFERSIZE])
+{
+ const ALsizei mask = BUFFERSIZE - 1;
+ const ALsizei numChans = Comp->NumChans;
+ const ALsizei indexIn = Comp->DelayIndex;
+ const ALsizei indexOut = Comp->DelayIndex - Comp->LookAhead;
+ ALfloat (*restrict delay)[BUFFERSIZE] = Comp->Delay;
+ ALsizei c, i;
+
+ ASSUME(SamplesToDo > 0);
+ ASSUME(numChans > 0);
+
+ for(c = 0;c < numChans;c++)
+ {
+ for(i = 0;i < SamplesToDo;i++)
+ {
+ ALfloat sig = OutBuffer[c][i];
+
+ OutBuffer[c][i] = delay[c][(indexOut + i) & mask];
+ delay[c][(indexIn + i) & mask] = sig;
+ }
+ }
+
+ Comp->DelayIndex = (indexIn + SamplesToDo) & mask;
+}
+
+/* The compressor is initialized with the following settings:
+ *
+ * NumChans - Number of channels to process.
+ * SampleRate - Sample rate to process.
+ * AutoKnee - Whether to automate the knee width parameter.
+ * AutoAttack - Whether to automate the attack time parameter.
+ * AutoRelease - Whether to automate the release time parameter.
+ * AutoPostGain - Whether to automate the make-up (post) gain parameter.
+ * AutoDeclip - Whether to automate clipping reduction. Ignored when
+ * not automating make-up gain.
+ * LookAheadTime - Look-ahead time (in seconds).
+ * HoldTime - Peak hold-time (in seconds).
+ * PreGainDb - Gain applied before detection (in dB).
+ * PostGainDb - Make-up gain applied after compression (in dB).
+ * ThresholdDb - Triggering threshold (in dB).
+ * Ratio - Compression ratio (x:1). Set to INFINITY for true
+ * limiting. Ignored when automating knee width.
+ * KneeDb - Knee width (in dB). Ignored when automating knee
+ * width.
+ * AttackTimeMin - Attack time (in seconds). Acts as a maximum when
+ * automating attack time.
+ * ReleaseTimeMin - Release time (in seconds). Acts as a maximum when
+ * automating release time.
+ */
+Compressor* CompressorInit(const ALsizei NumChans, const ALuint SampleRate,
+ const ALboolean AutoKnee, const ALboolean AutoAttack,
+ const ALboolean AutoRelease, const ALboolean AutoPostGain,
+ const ALboolean AutoDeclip, const ALfloat LookAheadTime,
+ const ALfloat HoldTime, const ALfloat PreGainDb,
+ const ALfloat PostGainDb, const ALfloat ThresholdDb,
+ const ALfloat Ratio, const ALfloat KneeDb,
+ const ALfloat AttackTime, const ALfloat ReleaseTime)
+{
+ Compressor *Comp;
+ ALsizei lookAhead;
+ ALsizei hold;
+ size_t size;
+
+ lookAhead = (ALsizei)clampf(roundf(LookAheadTime*SampleRate), 0.0f, BUFFERSIZE-1);
+ hold = (ALsizei)clampf(roundf(HoldTime*SampleRate), 0.0f, BUFFERSIZE-1);
+ /* The sliding hold implementation doesn't handle a length of 1. A 1-sample
+ * hold is useless anyway, it would only ever give back what was just given
+ * to it.
+ */
+ if(hold == 1)
+ hold = 0;
+
+ size = sizeof(*Comp);
+ if(lookAhead > 0)
+ {
+ size += sizeof(*Comp->Delay) * NumChans;
+ if(hold > 0)
+ size += sizeof(*Comp->Hold);
+ }
+
+ Comp = al_calloc(16, size);
+ Comp->NumChans = NumChans;
+ Comp->SampleRate = SampleRate;
+ Comp->Auto.Knee = AutoKnee;
+ Comp->Auto.Attack = AutoAttack;
+ Comp->Auto.Release = AutoRelease;
+ Comp->Auto.PostGain = AutoPostGain;
+ Comp->Auto.Declip = AutoPostGain && AutoDeclip;
+ Comp->LookAhead = lookAhead;
+ Comp->PreGain = powf(10.0f, PreGainDb / 20.0f);
+ Comp->PostGain = PostGainDb * logf(10.0f) / 20.0f;
+ Comp->Threshold = ThresholdDb * logf(10.0f) / 20.0f;
+ Comp->Slope = 1.0f / maxf(1.0f, Ratio) - 1.0f;
+ Comp->Knee = maxf(0.0f, KneeDb * logf(10.0f) / 20.0f);
+ Comp->Attack = maxf(1.0f, AttackTime * SampleRate);
+ Comp->Release = maxf(1.0f, ReleaseTime * SampleRate);
+
+ /* Knee width automation actually treats the compressor as a limiter. By
+ * varying the knee width, it can effectively be seen as applying
+ * compression over a wide range of ratios.
+ */
+ if(AutoKnee)
+ Comp->Slope = -1.0f;
+
+ if(lookAhead > 0)
+ {
+ if(hold > 0)
+ {
+ Comp->Hold = (SlidingHold*)(Comp + 1);
+ Comp->Hold->Values[0] = -HUGE_VALF;
+ Comp->Hold->Expiries[0] = hold;
+ Comp->Hold->Length = hold;
+ Comp->Delay = (ALfloat(*)[BUFFERSIZE])(Comp->Hold + 1);
+ }
+ else
+ {
+ Comp->Delay = (ALfloat(*)[BUFFERSIZE])(Comp + 1);
+ }
+ }
+
+ Comp->CrestCoeff = expf(-1.0f / (0.200f * SampleRate)); // 200ms
+ Comp->GainEstimate = Comp->Threshold * -0.5f * Comp->Slope;
+ Comp->AdaptCoeff = expf(-1.0f / (2.0f * SampleRate)); // 2s
+
+ return Comp;
+}
+
+void ApplyCompression(Compressor *Comp, const ALsizei SamplesToDo, ALfloat (*restrict OutBuffer)[BUFFERSIZE])
+{
+ const ALsizei numChans = Comp->NumChans;
+ const ALfloat preGain = Comp->PreGain;
+ ALfloat *restrict sideChain;
+ ALsizei c, i;
+
+ ASSUME(SamplesToDo > 0);
+ ASSUME(numChans > 0);
+
+ if(preGain != 1.0f)
+ {
+ for(c = 0;c < numChans;c++)
+ {
+ for(i = 0;i < SamplesToDo;i++)
+ OutBuffer[c][i] *= preGain;
+ }
+ }
+
+ LinkChannels(Comp, SamplesToDo, OutBuffer);
+
+ if(Comp->Auto.Attack || Comp->Auto.Release)
+ CrestDetector(Comp, SamplesToDo);
+
+ if(Comp->Hold)
+ PeakHoldDetector(Comp, SamplesToDo);
+ else
+ PeakDetector(Comp, SamplesToDo);
+
+ GainCompressor(Comp, SamplesToDo);
+
+ if(Comp->Delay)
+ SignalDelay(Comp, SamplesToDo, OutBuffer);
+
+ sideChain = Comp->SideChain;
+ for(c = 0;c < numChans;c++)
+ {
+ for(i = 0;i < SamplesToDo;i++)
+ OutBuffer[c][i] *= sideChain[i];
+ }
+
+ memmove(sideChain, sideChain+SamplesToDo, Comp->LookAhead*sizeof(ALfloat));
+}
+
+
+ALsizei GetCompressorLookAhead(const Compressor *Comp)
+{ return Comp->LookAhead; }
diff --git a/Alc/mastering.h b/Alc/mastering.h
new file mode 100644
index 00000000..b68b0de1
--- /dev/null
+++ b/Alc/mastering.h
@@ -0,0 +1,49 @@
+#ifndef MASTERING_H
+#define MASTERING_H
+
+#include "AL/al.h"
+
+/* For BUFFERSIZE. */
+#include "alMain.h"
+
+struct Compressor;
+
+/* The compressor is initialized with the following settings:
+ *
+ * NumChans - Number of channels to process.
+ * SampleRate - Sample rate to process.
+ * AutoKnee - Whether to automate the knee width parameter.
+ * AutoAttack - Whether to automate the attack time parameter.
+ * AutoRelease - Whether to automate the release time parameter.
+ * AutoPostGain - Whether to automate the make-up (post) gain parameter.
+ * AutoDeclip - Whether to automate clipping reduction. Ignored when
+ * not automating make-up gain.
+ * LookAheadTime - Look-ahead time (in seconds).
+ * HoldTime - Peak hold-time (in seconds).
+ * PreGainDb - Gain applied before detection (in dB).
+ * PostGainDb - Make-up gain applied after compression (in dB).
+ * ThresholdDb - Triggering threshold (in dB).
+ * Ratio - Compression ratio (x:1). Set to INFINIFTY for true
+ * limiting. Ignored when automating knee width.
+ * KneeDb - Knee width (in dB). Ignored when automating knee
+ * width.
+ * AttackTimeMin - Attack time (in seconds). Acts as a maximum when
+ * automating attack time.
+ * ReleaseTimeMin - Release time (in seconds). Acts as a maximum when
+ * automating release time.
+ */
+struct Compressor* CompressorInit(const ALsizei NumChans, const ALuint SampleRate,
+ const ALboolean AutoKnee, const ALboolean AutoAttack,
+ const ALboolean AutoRelease, const ALboolean AutoPostGain,
+ const ALboolean AutoDeclip, const ALfloat LookAheadTime,
+ const ALfloat HoldTime, const ALfloat PreGainDb,
+ const ALfloat PostGainDb, const ALfloat ThresholdDb,
+ const ALfloat Ratio, const ALfloat KneeDb,
+ const ALfloat AttackTime, const ALfloat ReleaseTime);
+
+void ApplyCompression(struct Compressor *Comp, const ALsizei SamplesToDo,
+ ALfloat (*restrict OutBuffer)[BUFFERSIZE]);
+
+ALsizei GetCompressorLookAhead(const struct Compressor *Comp);
+
+#endif /* MASTERING_H */
diff --git a/Alc/mixer.c b/Alc/mixer.c
deleted file mode 100644
index 712075f1..00000000
--- a/Alc/mixer.c
+++ /dev/null
@@ -1,638 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 1999-2007 by authors.
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
-#include "config.h"
-
-#include <math.h>
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-#include <assert.h>
-
-#include "alMain.h"
-#include "AL/al.h"
-#include "AL/alc.h"
-#include "alSource.h"
-#include "alBuffer.h"
-#include "alListener.h"
-#include "alAuxEffectSlot.h"
-#include "alu.h"
-
-#include "mixer_defs.h"
-
-
-static_assert((INT_MAX>>FRACTIONBITS)/MAX_PITCH > BUFFERSIZE,
- "MAX_PITCH and/or BUFFERSIZE are too large for FRACTIONBITS!");
-
-extern inline void InitiatePositionArrays(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)
-{
-#ifdef HAVE_SSE
- if((CPUCapFlags&CPU_CAP_SSE))
- return MixHrtf_SSE;
-#endif
-#ifdef HAVE_NEON
- if((CPUCapFlags&CPU_CAP_NEON))
- return MixHrtf_Neon;
-#endif
-
- return MixHrtf_C;
-}
-
-static inline MixerFunc SelectMixer(void)
-{
-#ifdef HAVE_SSE
- if((CPUCapFlags&CPU_CAP_SSE))
- return Mix_SSE;
-#endif
-#ifdef HAVE_NEON
- if((CPUCapFlags&CPU_CAP_NEON))
- return Mix_Neon;
-#endif
-
- return Mix_C;
-}
-
-static inline ResamplerFunc SelectResampler(enum Resampler resampler)
-{
- switch(resampler)
- {
- case PointResampler:
- return Resample_point32_C;
- case LinearResampler:
-#ifdef HAVE_SSE4_1
- if((CPUCapFlags&CPU_CAP_SSE4_1))
- return Resample_lerp32_SSE41;
-#endif
-#ifdef HAVE_SSE2
- if((CPUCapFlags&CPU_CAP_SSE2))
- return Resample_lerp32_SSE2;
-#endif
- return Resample_lerp32_C;
- case FIR4Resampler:
-#ifdef HAVE_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); }
-
-static inline ALfloat Sample_ALshort(ALshort val)
-{ return val * (1.0f/32767.0f); }
-
-static inline ALfloat Sample_ALfloat(ALfloat val)
-{ return val; }
-
-#define DECL_TEMPLATE(T) \
-static inline void Load_##T(ALfloat *dst, const T *src, ALuint srcstep, ALuint samples)\
-{ \
- ALuint i; \
- for(i = 0;i < samples;i++) \
- dst[i] = Sample_##T(src[i*srcstep]); \
-}
-
-DECL_TEMPLATE(ALbyte)
-DECL_TEMPLATE(ALshort)
-DECL_TEMPLATE(ALfloat)
-
-#undef DECL_TEMPLATE
-
-static void LoadSamples(ALfloat *dst, const ALvoid *src, ALuint srcstep, enum FmtType srctype, ALuint samples)
-{
- switch(srctype)
- {
- case FmtByte:
- Load_ALbyte(dst, src, srcstep, samples);
- break;
- case FmtShort:
- Load_ALshort(dst, src, srcstep, samples);
- break;
- case FmtFloat:
- Load_ALfloat(dst, src, srcstep, samples);
- break;
- }
-}
-
-static inline void SilenceSamples(ALfloat *dst, ALuint samples)
-{
- ALuint i;
- for(i = 0;i < samples;i++)
- dst[i] = 0.0f;
-}
-
-
-static const ALfloat *DoFilters(ALfilterState *lpfilter, ALfilterState *hpfilter,
- ALfloat *restrict dst, const ALfloat *restrict src,
- ALuint numsamples, enum ActiveFilters type)
-{
- ALuint i;
- switch(type)
- {
- case AF_None:
- ALfilterState_processPassthru(lpfilter, src, numsamples);
- ALfilterState_processPassthru(hpfilter, src, numsamples);
- break;
-
- case AF_LowPass:
- ALfilterState_process(lpfilter, dst, src, numsamples);
- ALfilterState_processPassthru(hpfilter, dst, numsamples);
- return dst;
- case AF_HighPass:
- ALfilterState_processPassthru(lpfilter, src, numsamples);
- ALfilterState_process(hpfilter, dst, src, numsamples);
- return dst;
-
- case AF_BandPass:
- for(i = 0;i < numsamples;)
- {
- ALfloat temp[256];
- ALuint todo = minu(256, numsamples-i);
-
- ALfilterState_process(lpfilter, temp, src+i, todo);
- ALfilterState_process(hpfilter, dst+i, temp, todo);
- i += todo;
- }
- return dst;
- }
- return src;
-}
-
-
-ALvoid MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALuint SamplesToDo)
-{
- ResamplerFunc Resample;
- ALbufferlistitem *BufferListItem;
- ALuint DataPosInt, DataPosFrac;
- ALboolean Looping;
- ALuint increment;
- ALenum State;
- ALuint OutPos;
- ALuint NumChannels;
- ALuint SampleSize;
- ALint64 DataSize64;
- ALuint IrSize;
- ALuint chan, j;
-
- /* Get source info */
- State = Source->state;
- BufferListItem = ATOMIC_LOAD(&Source->current_buffer);
- DataPosInt = Source->position;
- DataPosFrac = Source->position_fraction;
- Looping = Source->Looping;
- NumChannels = Source->NumChannels;
- SampleSize = Source->SampleSize;
- increment = voice->Step;
-
- IrSize = (Device->Hrtf ? GetHrtfIrSize(Device->Hrtf) : 0);
-
- Resample = ((increment == FRACTIONONE && DataPosFrac == 0) ?
- Resample_copy32_C : ResampleSamples);
-
- OutPos = 0;
- do {
- ALuint SrcBufferSize, DstBufferSize;
-
- /* Figure out how many buffer samples will be needed */
- DataSize64 = SamplesToDo-OutPos;
- DataSize64 *= increment;
- DataSize64 += DataPosFrac+FRACTIONMASK;
- DataSize64 >>= FRACTIONBITS;
- DataSize64 += MAX_POST_SAMPLES+MAX_PRE_SAMPLES;
-
- SrcBufferSize = (ALuint)mini64(DataSize64, BUFFERSIZE);
-
- /* Figure out how many samples we can actually mix from this. */
- DataSize64 = SrcBufferSize;
- DataSize64 -= MAX_POST_SAMPLES+MAX_PRE_SAMPLES;
- DataSize64 <<= FRACTIONBITS;
- DataSize64 -= DataPosFrac;
-
- DstBufferSize = (ALuint)((DataSize64+(increment-1)) / increment);
- DstBufferSize = minu(DstBufferSize, (SamplesToDo-OutPos));
-
- /* Some mixers like having a multiple of 4, so try to give that unless
- * this is the last update. */
- if(OutPos+DstBufferSize < SamplesToDo)
- DstBufferSize &= ~3;
-
- for(chan = 0;chan < NumChannels;chan++)
- {
- const ALfloat *ResampledData;
- ALfloat *SrcData = Device->SourceData;
- 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)
- {
- const ALbuffer *ALBuffer = BufferListItem->buffer;
- const ALubyte *Data = ALBuffer->data;
- 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;
-
- /* 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*SampleSize],
- NumChannels, ALBuffer->FmtType, DataSize);
- SrcDataSize += DataSize;
-
- SilenceSamples(&SrcData[SrcDataSize], SrcBufferSize - SrcDataSize);
- SrcDataSize += SrcBufferSize - SrcDataSize;
- }
- else
- {
- ALuint LoopStart = ALBuffer->LoopStart;
- ALuint LoopEnd = ALBuffer->LoopEnd;
-
- /* Load what's left of this loop iteration, then load
- * repeats of the loop section */
- pos = DataPosInt;
- DataSize = LoopEnd - pos;
- DataSize = minu(SrcBufferSize - SrcDataSize, DataSize);
-
- LoadSamples(&SrcData[SrcDataSize], &Data[pos * NumChannels*SampleSize],
- NumChannels, ALBuffer->FmtType, DataSize);
- SrcDataSize += DataSize;
-
- DataSize = LoopEnd-LoopStart;
- while(SrcBufferSize > SrcDataSize)
- {
- DataSize = minu(SrcBufferSize - SrcDataSize, DataSize);
-
- LoadSamples(&SrcData[SrcDataSize], &Data[LoopStart * NumChannels*SampleSize],
- NumChannels, ALBuffer->FmtType, DataSize);
- SrcDataSize += DataSize;
- }
- }
- }
- else
- {
- /* Crawl the buffer queue to fill in the temp buffer */
- ALbufferlistitem *tmpiter = BufferListItem;
- ALuint pos = DataPosInt;
-
- while(tmpiter && SrcBufferSize > SrcDataSize)
- {
- const ALbuffer *ALBuffer;
- if((ALBuffer=tmpiter->buffer) != NULL)
- {
- const ALubyte *Data = ALBuffer->data;
- ALuint DataSize = ALBuffer->SampleLen;
-
- /* Skip the data already played */
- if(DataSize <= pos)
- pos -= DataSize;
- else
- {
- Data += (pos*NumChannels + chan)*SampleSize;
- DataSize -= pos;
- pos -= pos;
-
- DataSize = minu(SrcBufferSize - SrcDataSize, DataSize);
- LoadSamples(&SrcData[SrcDataSize], Data, NumChannels,
- ALBuffer->FmtType, DataSize);
- SrcDataSize += DataSize;
- }
- }
- tmpiter = tmpiter->next;
- if(!tmpiter && Looping)
- tmpiter = ATOMIC_LOAD(&Source->queue);
- else if(!tmpiter)
- {
- SilenceSamples(&SrcData[SrcDataSize], SrcBufferSize - SrcDataSize);
- SrcDataSize += SrcBufferSize - SrcDataSize;
- }
- }
- }
-
- /* Store the last source samples used for next time. */
- memcpy(voice->PrevSamples[chan],
- &SrcData[(increment*DstBufferSize + DataPosFrac)>>FRACTIONBITS],
- MAX_PRE_SAMPLES*sizeof(ALfloat)
- );
-
- /* Now resample, then filter and mix to the appropriate outputs. */
- ResampledData = Resample(&voice->SincState,
- &SrcData[MAX_PRE_SAMPLES], DataPosFrac, increment,
- Device->ResampledData, DstBufferSize
- );
- {
- DirectParams *parms = &voice->Direct;
- const ALfloat *samples;
-
- samples = DoFilters(
- &parms->Filters[chan].LowPass, &parms->Filters[chan].HighPass,
- Device->FilteredData, ResampledData, DstBufferSize,
- parms->Filters[chan].ActiveType
- );
- if(!voice->IsHrtf)
- MixSamples(samples, parms->OutChannels, parms->OutBuffer, parms->Gains[chan],
- parms->Counter, OutPos, DstBufferSize);
- else
- MixHrtfSamples(parms->OutBuffer, samples, parms->Counter, voice->Offset,
- OutPos, IrSize, &parms->Hrtf[chan].Params,
- &parms->Hrtf[chan].State, DstBufferSize);
- }
-
- for(j = 0;j < Device->NumAuxSends;j++)
- {
- SendParams *parms = &voice->Send[j];
- const ALfloat *samples;
-
- if(!parms->OutBuffer)
- continue;
-
- samples = DoFilters(
- &parms->Filters[chan].LowPass, &parms->Filters[chan].HighPass,
- Device->FilteredData, ResampledData, DstBufferSize,
- parms->Filters[chan].ActiveType
- );
- MixSamples(samples, 1, parms->OutBuffer, &parms->Gains[chan],
- parms->Counter, OutPos, DstBufferSize);
- }
- }
- /* Update positions */
- DataPosFrac += increment*DstBufferSize;
- DataPosInt += DataPosFrac>>FRACTIONBITS;
- DataPosFrac &= FRACTIONMASK;
-
- OutPos += DstBufferSize;
- voice->Offset += DstBufferSize;
- voice->Direct.Counter = maxu(voice->Direct.Counter, DstBufferSize) - DstBufferSize;
- for(j = 0;j < Device->NumAuxSends;j++)
- voice->Send[j].Counter = maxu(voice->Send[j].Counter, DstBufferSize) - DstBufferSize;
-
- /* Handle looping sources */
- while(1)
- {
- const ALbuffer *ALBuffer;
- ALuint DataSize = 0;
- ALuint LoopStart = 0;
- ALuint LoopEnd = 0;
-
- if((ALBuffer=BufferListItem->buffer) != NULL)
- {
- DataSize = ALBuffer->SampleLen;
- LoopStart = ALBuffer->LoopStart;
- LoopEnd = ALBuffer->LoopEnd;
- if(LoopEnd > DataPosInt)
- break;
- }
-
- if(Looping && Source->SourceType == AL_STATIC)
- {
- assert(LoopEnd > LoopStart);
- DataPosInt = ((DataPosInt-LoopStart)%(LoopEnd-LoopStart)) + LoopStart;
- break;
- }
-
- if(DataSize > DataPosInt)
- break;
-
- if(!(BufferListItem=BufferListItem->next))
- {
- if(Looping)
- BufferListItem = ATOMIC_LOAD(&Source->queue);
- else
- {
- State = AL_STOPPED;
- BufferListItem = NULL;
- DataPosInt = 0;
- DataPosFrac = 0;
- break;
- }
- }
-
- DataPosInt -= DataSize;
- }
- } while(State == AL_PLAYING && OutPos < SamplesToDo);
-
- /* Update source info */
- Source->state = State;
- ATOMIC_STORE(&Source->current_buffer, BufferListItem);
- Source->position = DataPosInt;
- Source->position_fraction = DataPosFrac;
-}
diff --git a/Alc/mixer/defs.h b/Alc/mixer/defs.h
new file mode 100644
index 00000000..8f6e3755
--- /dev/null
+++ b/Alc/mixer/defs.h
@@ -0,0 +1,119 @@
+#ifndef MIXER_DEFS_H
+#define MIXER_DEFS_H
+
+#include "AL/alc.h"
+#include "AL/al.h"
+#include "alMain.h"
+#include "alu.h"
+
+struct MixGains;
+
+struct MixHrtfParams;
+struct HrtfState;
+
+/* C resamplers */
+const ALfloat *Resample_copy_C(const InterpState *state, const ALfloat *restrict src, ALsizei frac, ALint increment, ALfloat *restrict dst, ALsizei dstlen);
+const ALfloat *Resample_point_C(const InterpState *state, const ALfloat *restrict src, ALsizei frac, ALint increment, ALfloat *restrict dst, ALsizei dstlen);
+const ALfloat *Resample_lerp_C(const InterpState *state, const ALfloat *restrict src, ALsizei frac, ALint increment, ALfloat *restrict dst, ALsizei dstlen);
+const ALfloat *Resample_cubic_C(const InterpState *state, const ALfloat *restrict src, ALsizei frac, ALint increment, ALfloat *restrict dst, ALsizei dstlen);
+const ALfloat *Resample_bsinc_C(const InterpState *state, const ALfloat *restrict src, ALsizei frac, ALint increment, ALfloat *restrict dst, ALsizei dstlen);
+
+
+/* C mixers */
+void MixHrtf_C(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
+ const ALfloat *data, ALsizei Offset, ALsizei OutPos,
+ const ALsizei IrSize, struct MixHrtfParams *hrtfparams,
+ struct HrtfState *hrtfstate, ALsizei BufferSize);
+void MixHrtfBlend_C(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
+ const ALfloat *data, ALsizei Offset, ALsizei OutPos,
+ const ALsizei IrSize, const HrtfParams *oldparams,
+ MixHrtfParams *newparams, HrtfState *hrtfstate,
+ ALsizei BufferSize);
+void MixDirectHrtf_C(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
+ const ALfloat *data, ALsizei Offset, const ALsizei IrSize,
+ const ALfloat (*restrict Coeffs)[2], ALfloat (*restrict Values)[2],
+ ALsizei BufferSize);
+void Mix_C(const ALfloat *data, ALsizei OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
+ ALfloat *CurrentGains, const ALfloat *TargetGains, ALsizei Counter, ALsizei OutPos,
+ ALsizei BufferSize);
+void MixRow_C(ALfloat *OutBuffer, const ALfloat *Gains,
+ const ALfloat (*restrict data)[BUFFERSIZE], ALsizei InChans,
+ ALsizei InPos, ALsizei BufferSize);
+
+/* SSE mixers */
+void MixHrtf_SSE(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
+ const ALfloat *data, ALsizei Offset, ALsizei OutPos,
+ const ALsizei IrSize, struct MixHrtfParams *hrtfparams,
+ struct HrtfState *hrtfstate, ALsizei BufferSize);
+void MixHrtfBlend_SSE(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
+ const ALfloat *data, ALsizei Offset, ALsizei OutPos,
+ const ALsizei IrSize, const HrtfParams *oldparams,
+ MixHrtfParams *newparams, HrtfState *hrtfstate,
+ ALsizei BufferSize);
+void MixDirectHrtf_SSE(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
+ const ALfloat *data, ALsizei Offset, const ALsizei IrSize,
+ const ALfloat (*restrict Coeffs)[2], ALfloat (*restrict Values)[2],
+ ALsizei BufferSize);
+void Mix_SSE(const ALfloat *data, ALsizei OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
+ ALfloat *CurrentGains, const ALfloat *TargetGains, ALsizei Counter, ALsizei OutPos,
+ ALsizei BufferSize);
+void MixRow_SSE(ALfloat *OutBuffer, const ALfloat *Gains,
+ const ALfloat (*restrict data)[BUFFERSIZE], ALsizei InChans,
+ ALsizei InPos, ALsizei BufferSize);
+
+/* SSE resamplers */
+inline void InitiatePositionArrays(ALsizei frac, ALint increment, ALsizei *restrict frac_arr, ALsizei *restrict pos_arr, ALsizei size)
+{
+ ALsizei i;
+
+ pos_arr[0] = 0;
+ frac_arr[0] = frac;
+ for(i = 1;i < size;i++)
+ {
+ ALint frac_tmp = frac_arr[i-1] + increment;
+ pos_arr[i] = pos_arr[i-1] + (frac_tmp>>FRACTIONBITS);
+ frac_arr[i] = frac_tmp&FRACTIONMASK;
+ }
+}
+
+const ALfloat *Resample_lerp_SSE2(const InterpState *state, const ALfloat *restrict src,
+ ALsizei frac, ALint increment, ALfloat *restrict dst,
+ ALsizei numsamples);
+const ALfloat *Resample_lerp_SSE41(const InterpState *state, const ALfloat *restrict src,
+ ALsizei frac, ALint increment, ALfloat *restrict dst,
+ ALsizei numsamples);
+
+const ALfloat *Resample_bsinc_SSE(const InterpState *state, const ALfloat *restrict src,
+ ALsizei frac, ALint increment, ALfloat *restrict dst,
+ ALsizei dstlen);
+
+/* Neon mixers */
+void MixHrtf_Neon(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
+ const ALfloat *data, ALsizei Offset, ALsizei OutPos,
+ const ALsizei IrSize, struct MixHrtfParams *hrtfparams,
+ struct HrtfState *hrtfstate, ALsizei BufferSize);
+void MixHrtfBlend_Neon(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
+ const ALfloat *data, ALsizei Offset, ALsizei OutPos,
+ const ALsizei IrSize, const HrtfParams *oldparams,
+ MixHrtfParams *newparams, HrtfState *hrtfstate,
+ ALsizei BufferSize);
+void MixDirectHrtf_Neon(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
+ const ALfloat *data, ALsizei Offset, const ALsizei IrSize,
+ const ALfloat (*restrict Coeffs)[2], ALfloat (*restrict Values)[2],
+ ALsizei BufferSize);
+void Mix_Neon(const ALfloat *data, ALsizei OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
+ ALfloat *CurrentGains, const ALfloat *TargetGains, ALsizei Counter, ALsizei OutPos,
+ ALsizei BufferSize);
+void MixRow_Neon(ALfloat *OutBuffer, const ALfloat *Gains,
+ const ALfloat (*restrict data)[BUFFERSIZE], ALsizei InChans,
+ ALsizei InPos, ALsizei BufferSize);
+
+/* Neon resamplers */
+const ALfloat *Resample_lerp_Neon(const InterpState *state, const ALfloat *restrict src,
+ ALsizei frac, ALint increment, ALfloat *restrict dst,
+ ALsizei numsamples);
+const ALfloat *Resample_bsinc_Neon(const InterpState *state, const ALfloat *restrict src,
+ ALsizei frac, ALint increment, ALfloat *restrict dst,
+ ALsizei dstlen);
+
+#endif /* MIXER_DEFS_H */
diff --git a/Alc/mixer/hrtf_inc.c b/Alc/mixer/hrtf_inc.c
new file mode 100644
index 00000000..3ef22f24
--- /dev/null
+++ b/Alc/mixer/hrtf_inc.c
@@ -0,0 +1,128 @@
+#include "config.h"
+
+#include "alMain.h"
+#include "alSource.h"
+
+#include "hrtf.h"
+#include "align.h"
+#include "alu.h"
+#include "defs.h"
+
+
+static inline void ApplyCoeffs(ALsizei Offset, ALfloat (*restrict Values)[2],
+ const ALsizei irSize,
+ const ALfloat (*restrict Coeffs)[2],
+ ALfloat left, ALfloat right);
+
+
+void MixHrtf(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
+ const ALfloat *data, ALsizei Offset, ALsizei OutPos,
+ const ALsizei IrSize, MixHrtfParams *hrtfparams, HrtfState *hrtfstate,
+ ALsizei BufferSize)
+{
+ const ALfloat (*Coeffs)[2] = ASSUME_ALIGNED(hrtfparams->Coeffs, 16);
+ const ALsizei Delay[2] = { hrtfparams->Delay[0], hrtfparams->Delay[1] };
+ const ALfloat gainstep = hrtfparams->GainStep;
+ const ALfloat gain = hrtfparams->Gain;
+ ALfloat g, stepcount = 0.0f;
+ ALfloat left, right;
+ ALsizei i;
+
+ ASSUME(IrSize >= 4);
+ ASSUME(BufferSize > 0);
+
+ LeftOut += OutPos;
+ RightOut += OutPos;
+ for(i = 0;i < BufferSize;i++)
+ {
+ hrtfstate->History[Offset&HRTF_HISTORY_MASK] = *(data++);
+
+ g = gain + gainstep*stepcount;
+ left = hrtfstate->History[(Offset-Delay[0])&HRTF_HISTORY_MASK]*g;
+ right = hrtfstate->History[(Offset-Delay[1])&HRTF_HISTORY_MASK]*g;
+
+ hrtfstate->Values[(Offset+IrSize-1)&HRIR_MASK][0] = 0.0f;
+ hrtfstate->Values[(Offset+IrSize-1)&HRIR_MASK][1] = 0.0f;
+
+ ApplyCoeffs(Offset, hrtfstate->Values, IrSize, Coeffs, left, right);
+ *(LeftOut++) += hrtfstate->Values[Offset&HRIR_MASK][0];
+ *(RightOut++) += hrtfstate->Values[Offset&HRIR_MASK][1];
+
+ stepcount += 1.0f;
+ Offset++;
+ }
+ hrtfparams->Gain = gain + gainstep*stepcount;
+}
+
+void MixHrtfBlend(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
+ const ALfloat *data, ALsizei Offset, ALsizei OutPos,
+ const ALsizei IrSize, const HrtfParams *oldparams,
+ MixHrtfParams *newparams, HrtfState *hrtfstate,
+ ALsizei BufferSize)
+{
+ const ALfloat (*OldCoeffs)[2] = ASSUME_ALIGNED(oldparams->Coeffs, 16);
+ const ALsizei OldDelay[2] = { oldparams->Delay[0], oldparams->Delay[1] };
+ const ALfloat oldGain = oldparams->Gain;
+ const ALfloat oldGainStep = -oldGain / (ALfloat)BufferSize;
+ const ALfloat (*NewCoeffs)[2] = ASSUME_ALIGNED(newparams->Coeffs, 16);
+ const ALsizei NewDelay[2] = { newparams->Delay[0], newparams->Delay[1] };
+ const ALfloat newGain = newparams->Gain;
+ const ALfloat newGainStep = newparams->GainStep;
+ ALfloat g, stepcount = 0.0f;
+ ALfloat left, right;
+ ALsizei i;
+
+ ASSUME(IrSize >= 4);
+ ASSUME(BufferSize > 0);
+
+ LeftOut += OutPos;
+ RightOut += OutPos;
+ for(i = 0;i < BufferSize;i++)
+ {
+ hrtfstate->Values[(Offset+IrSize-1)&HRIR_MASK][0] = 0.0f;
+ hrtfstate->Values[(Offset+IrSize-1)&HRIR_MASK][1] = 0.0f;
+
+ hrtfstate->History[Offset&HRTF_HISTORY_MASK] = *(data++);
+
+ g = oldGain + oldGainStep*stepcount;
+ left = hrtfstate->History[(Offset-OldDelay[0])&HRTF_HISTORY_MASK]*g;
+ right = hrtfstate->History[(Offset-OldDelay[1])&HRTF_HISTORY_MASK]*g;
+ ApplyCoeffs(Offset, hrtfstate->Values, IrSize, OldCoeffs, left, right);
+
+ g = newGain + newGainStep*stepcount;
+ left = hrtfstate->History[(Offset-NewDelay[0])&HRTF_HISTORY_MASK]*g;
+ right = hrtfstate->History[(Offset-NewDelay[1])&HRTF_HISTORY_MASK]*g;
+ ApplyCoeffs(Offset, hrtfstate->Values, IrSize, NewCoeffs, left, right);
+
+ *(LeftOut++) += hrtfstate->Values[Offset&HRIR_MASK][0];
+ *(RightOut++) += hrtfstate->Values[Offset&HRIR_MASK][1];
+
+ stepcount += 1.0f;
+ Offset++;
+ }
+ newparams->Gain = newGain + newGainStep*stepcount;
+}
+
+void MixDirectHrtf(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
+ const ALfloat *data, ALsizei Offset, const ALsizei IrSize,
+ const ALfloat (*restrict Coeffs)[2], ALfloat (*restrict Values)[2],
+ ALsizei BufferSize)
+{
+ ALfloat insample;
+ ALsizei i;
+
+ ASSUME(IrSize >= 4);
+ ASSUME(BufferSize > 0);
+
+ for(i = 0;i < BufferSize;i++)
+ {
+ Values[(Offset+IrSize)&HRIR_MASK][0] = 0.0f;
+ Values[(Offset+IrSize)&HRIR_MASK][1] = 0.0f;
+ Offset++;
+
+ insample = *(data++);
+ ApplyCoeffs(Offset, Values, IrSize, Coeffs, insample, insample);
+ *(LeftOut++) += Values[Offset&HRIR_MASK][0];
+ *(RightOut++) += Values[Offset&HRIR_MASK][1];
+ }
+}
diff --git a/Alc/mixer/mixer_c.c b/Alc/mixer/mixer_c.c
new file mode 100644
index 00000000..14d7c669
--- /dev/null
+++ b/Alc/mixer/mixer_c.c
@@ -0,0 +1,169 @@
+#include "config.h"
+
+#include <assert.h>
+
+#include "alMain.h"
+#include "alu.h"
+#include "alSource.h"
+#include "alAuxEffectSlot.h"
+#include "defs.h"
+
+
+static inline ALfloat do_point(const InterpState* UNUSED(state), const ALfloat *restrict vals, ALsizei UNUSED(frac))
+{ return vals[0]; }
+static inline ALfloat do_lerp(const InterpState* UNUSED(state), const ALfloat *restrict vals, ALsizei frac)
+{ return lerp(vals[0], vals[1], frac * (1.0f/FRACTIONONE)); }
+static inline ALfloat do_cubic(const InterpState* UNUSED(state), const ALfloat *restrict vals, ALsizei frac)
+{ return cubic(vals[0], vals[1], vals[2], vals[3], frac * (1.0f/FRACTIONONE)); }
+static inline ALfloat do_bsinc(const InterpState *state, const ALfloat *restrict vals, ALsizei frac)
+{
+ const ALfloat *fil, *scd, *phd, *spd;
+ ALsizei j_f, pi;
+ ALfloat pf, r;
+
+ ASSUME(state->bsinc.m > 0);
+
+ // Calculate the phase index and factor.
+#define FRAC_PHASE_BITDIFF (FRACTIONBITS-BSINC_PHASE_BITS)
+ pi = frac >> FRAC_PHASE_BITDIFF;
+ pf = (frac & ((1<<FRAC_PHASE_BITDIFF)-1)) * (1.0f/(1<<FRAC_PHASE_BITDIFF));
+#undef FRAC_PHASE_BITDIFF
+
+ fil = ASSUME_ALIGNED(state->bsinc.filter + state->bsinc.m*pi*4, 16);
+ scd = ASSUME_ALIGNED(fil + state->bsinc.m, 16);
+ phd = ASSUME_ALIGNED(scd + state->bsinc.m, 16);
+ spd = ASSUME_ALIGNED(phd + state->bsinc.m, 16);
+
+ // Apply the scale and phase interpolated filter.
+ r = 0.0f;
+ for(j_f = 0;j_f < state->bsinc.m;j_f++)
+ r += (fil[j_f] + state->bsinc.sf*scd[j_f] + pf*(phd[j_f] + state->bsinc.sf*spd[j_f])) * vals[j_f];
+ return r;
+}
+
+const ALfloat *Resample_copy_C(const InterpState* UNUSED(state),
+ const ALfloat *restrict src, ALsizei UNUSED(frac), ALint UNUSED(increment),
+ ALfloat *restrict dst, ALsizei numsamples)
+{
+#if defined(HAVE_SSE) || defined(HAVE_NEON)
+ /* Avoid copying the source data if it's aligned like the destination. */
+ if((((intptr_t)src)&15) == (((intptr_t)dst)&15))
+ return src;
+#endif
+ memcpy(dst, src, numsamples*sizeof(ALfloat));
+ return dst;
+}
+
+#define DECL_TEMPLATE(Tag, Sampler, O) \
+const ALfloat *Resample_##Tag##_C(const InterpState *state, \
+ const ALfloat *restrict src, ALsizei frac, ALint increment, \
+ ALfloat *restrict dst, ALsizei numsamples) \
+{ \
+ const InterpState istate = *state; \
+ ALsizei i; \
+ \
+ ASSUME(numsamples > 0); \
+ \
+ src -= O; \
+ for(i = 0;i < numsamples;i++) \
+ { \
+ dst[i] = Sampler(&istate, src, frac); \
+ \
+ frac += increment; \
+ src += frac>>FRACTIONBITS; \
+ frac &= FRACTIONMASK; \
+ } \
+ return dst; \
+}
+
+DECL_TEMPLATE(point, do_point, 0)
+DECL_TEMPLATE(lerp, do_lerp, 0)
+DECL_TEMPLATE(cubic, do_cubic, 1)
+DECL_TEMPLATE(bsinc, do_bsinc, istate.bsinc.l)
+
+#undef DECL_TEMPLATE
+
+
+static inline void ApplyCoeffs(ALsizei Offset, ALfloat (*restrict Values)[2],
+ const ALsizei IrSize,
+ const ALfloat (*restrict Coeffs)[2],
+ ALfloat left, ALfloat right)
+{
+ ALsizei c;
+ for(c = 0;c < IrSize;c++)
+ {
+ const ALsizei off = (Offset+c)&HRIR_MASK;
+ Values[off][0] += Coeffs[c][0] * left;
+ Values[off][1] += Coeffs[c][1] * right;
+ }
+}
+
+#define MixHrtf MixHrtf_C
+#define MixHrtfBlend MixHrtfBlend_C
+#define MixDirectHrtf MixDirectHrtf_C
+#include "hrtf_inc.c"
+
+
+void Mix_C(const ALfloat *data, ALsizei OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
+ ALfloat *CurrentGains, const ALfloat *TargetGains, ALsizei Counter, ALsizei OutPos,
+ ALsizei BufferSize)
+{
+ const ALfloat delta = (Counter > 0) ? 1.0f/(ALfloat)Counter : 0.0f;
+ ALsizei c;
+
+ ASSUME(OutChans > 0);
+ ASSUME(BufferSize > 0);
+
+ for(c = 0;c < OutChans;c++)
+ {
+ ALsizei pos = 0;
+ ALfloat gain = CurrentGains[c];
+ const ALfloat diff = TargetGains[c] - gain;
+
+ if(fabsf(diff) > FLT_EPSILON)
+ {
+ ALsizei minsize = mini(BufferSize, Counter);
+ const ALfloat step = diff * delta;
+ ALfloat step_count = 0.0f;
+ for(;pos < minsize;pos++)
+ {
+ OutBuffer[c][OutPos+pos] += data[pos] * (gain + step*step_count);
+ step_count += 1.0f;
+ }
+ if(pos == Counter)
+ gain = TargetGains[c];
+ else
+ gain += step*step_count;
+ CurrentGains[c] = gain;
+ }
+
+ if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
+ continue;
+ for(;pos < BufferSize;pos++)
+ OutBuffer[c][OutPos+pos] += data[pos]*gain;
+ }
+}
+
+/* Basically the inverse of the above. Rather than one input going to multiple
+ * outputs (each with its own gain), it's multiple inputs (each with its own
+ * gain) going to one output. This applies one row (vs one column) of a matrix
+ * transform. And as the matrices are more or less static once set up, no
+ * stepping is necessary.
+ */
+void MixRow_C(ALfloat *OutBuffer, const ALfloat *Gains, const ALfloat (*restrict data)[BUFFERSIZE], ALsizei InChans, ALsizei InPos, ALsizei BufferSize)
+{
+ ALsizei c, i;
+
+ ASSUME(InChans > 0);
+ ASSUME(BufferSize > 0);
+
+ for(c = 0;c < InChans;c++)
+ {
+ const ALfloat gain = Gains[c];
+ if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
+ continue;
+
+ for(i = 0;i < BufferSize;i++)
+ OutBuffer[i] += data[c][InPos+i] * gain;
+ }
+}
diff --git a/Alc/mixer/mixer_neon.c b/Alc/mixer/mixer_neon.c
new file mode 100644
index 00000000..9bf5521a
--- /dev/null
+++ b/Alc/mixer/mixer_neon.c
@@ -0,0 +1,283 @@
+#include "config.h"
+
+#include <arm_neon.h>
+
+#include "AL/al.h"
+#include "AL/alc.h"
+#include "alMain.h"
+#include "alu.h"
+#include "hrtf.h"
+#include "defs.h"
+
+
+const ALfloat *Resample_lerp_Neon(const InterpState* UNUSED(state),
+ const ALfloat *restrict src, ALsizei frac, ALint increment,
+ ALfloat *restrict dst, ALsizei numsamples)
+{
+ const int32x4_t increment4 = vdupq_n_s32(increment*4);
+ const float32x4_t fracOne4 = vdupq_n_f32(1.0f/FRACTIONONE);
+ const int32x4_t fracMask4 = vdupq_n_s32(FRACTIONMASK);
+ alignas(16) ALsizei pos_[4], frac_[4];
+ int32x4_t pos4, frac4;
+ ALsizei todo, pos, i;
+
+ ASSUME(numsamples > 0);
+
+ InitiatePositionArrays(frac, increment, frac_, pos_, 4);
+ frac4 = vld1q_s32(frac_);
+ pos4 = vld1q_s32(pos_);
+
+ todo = numsamples & ~3;
+ for(i = 0;i < todo;i += 4)
+ {
+ const int pos0 = vgetq_lane_s32(pos4, 0);
+ const int pos1 = vgetq_lane_s32(pos4, 1);
+ const int pos2 = vgetq_lane_s32(pos4, 2);
+ const int pos3 = vgetq_lane_s32(pos4, 3);
+ const float32x4_t val1 = (float32x4_t){src[pos0], src[pos1], src[pos2], src[pos3]};
+ const float32x4_t val2 = (float32x4_t){src[pos0+1], src[pos1+1], src[pos2+1], src[pos3+1]};
+
+ /* val1 + (val2-val1)*mu */
+ const float32x4_t r0 = vsubq_f32(val2, val1);
+ const float32x4_t mu = vmulq_f32(vcvtq_f32_s32(frac4), fracOne4);
+ const float32x4_t out = vmlaq_f32(val1, mu, r0);
+
+ vst1q_f32(&dst[i], out);
+
+ frac4 = vaddq_s32(frac4, increment4);
+ pos4 = vaddq_s32(pos4, vshrq_n_s32(frac4, FRACTIONBITS));
+ frac4 = vandq_s32(frac4, fracMask4);
+ }
+
+ /* NOTE: These four elements represent the position *after* the last four
+ * samples, so the lowest element is the next position to resample.
+ */
+ pos = vgetq_lane_s32(pos4, 0);
+ frac = vgetq_lane_s32(frac4, 0);
+
+ for(;i < numsamples;++i)
+ {
+ dst[i] = lerp(src[pos], src[pos+1], frac * (1.0f/FRACTIONONE));
+
+ frac += increment;
+ pos += frac>>FRACTIONBITS;
+ frac &= FRACTIONMASK;
+ }
+ return dst;
+}
+
+const ALfloat *Resample_bsinc_Neon(const InterpState *state,
+ const ALfloat *restrict src, ALsizei frac, ALint increment,
+ ALfloat *restrict dst, ALsizei dstlen)
+{
+ const ALfloat *const filter = state->bsinc.filter;
+ const float32x4_t sf4 = vdupq_n_f32(state->bsinc.sf);
+ const ALsizei m = state->bsinc.m;
+ const float32x4_t *fil, *scd, *phd, *spd;
+ ALsizei pi, i, j, offset;
+ float32x4_t r4;
+ ALfloat pf;
+
+ ASSUME(m > 0);
+ ASSUME(dstlen > 0);
+
+ src -= state->bsinc.l;
+ for(i = 0;i < dstlen;i++)
+ {
+ // Calculate the phase index and factor.
+#define FRAC_PHASE_BITDIFF (FRACTIONBITS-BSINC_PHASE_BITS)
+ pi = frac >> FRAC_PHASE_BITDIFF;
+ pf = (frac & ((1<<FRAC_PHASE_BITDIFF)-1)) * (1.0f/(1<<FRAC_PHASE_BITDIFF));
+#undef FRAC_PHASE_BITDIFF
+
+ offset = m*pi*4;
+ fil = ASSUME_ALIGNED(filter + offset, 16); offset += m;
+ scd = ASSUME_ALIGNED(filter + offset, 16); offset += m;
+ phd = ASSUME_ALIGNED(filter + offset, 16); offset += m;
+ spd = ASSUME_ALIGNED(filter + offset, 16);
+
+ // Apply the scale and phase interpolated filter.
+ r4 = vdupq_n_f32(0.0f);
+ {
+ const ALsizei count = m >> 2;
+ const float32x4_t pf4 = vdupq_n_f32(pf);
+
+ ASSUME(count > 0);
+
+ for(j = 0;j < count;j++)
+ {
+ /* f = ((fil + sf*scd) + pf*(phd + sf*spd)) */
+ const float32x4_t f4 = vmlaq_f32(
+ vmlaq_f32(fil[j], sf4, scd[j]),
+ pf4, vmlaq_f32(phd[j], sf4, spd[j])
+ );
+ /* r += f*src */
+ r4 = vmlaq_f32(r4, f4, vld1q_f32(&src[j*4]));
+ }
+ }
+ r4 = vaddq_f32(r4, vcombine_f32(vrev64_f32(vget_high_f32(r4)),
+ vrev64_f32(vget_low_f32(r4))));
+ dst[i] = vget_lane_f32(vadd_f32(vget_low_f32(r4), vget_high_f32(r4)), 0);
+
+ frac += increment;
+ src += frac>>FRACTIONBITS;
+ frac &= FRACTIONMASK;
+ }
+ return dst;
+}
+
+
+static inline void ApplyCoeffs(ALsizei Offset, ALfloat (*restrict Values)[2],
+ const ALsizei IrSize,
+ const ALfloat (*restrict Coeffs)[2],
+ ALfloat left, ALfloat right)
+{
+ ALsizei c;
+ float32x4_t leftright4;
+ {
+ float32x2_t leftright2 = vdup_n_f32(0.0);
+ leftright2 = vset_lane_f32(left, leftright2, 0);
+ leftright2 = vset_lane_f32(right, leftright2, 1);
+ leftright4 = vcombine_f32(leftright2, leftright2);
+ }
+ Values = ASSUME_ALIGNED(Values, 16);
+ Coeffs = ASSUME_ALIGNED(Coeffs, 16);
+ for(c = 0;c < IrSize;c += 2)
+ {
+ const ALsizei o0 = (Offset+c)&HRIR_MASK;
+ const ALsizei o1 = (o0+1)&HRIR_MASK;
+ float32x4_t vals = vcombine_f32(vld1_f32((float32_t*)&Values[o0][0]),
+ vld1_f32((float32_t*)&Values[o1][0]));
+ float32x4_t coefs = vld1q_f32((float32_t*)&Coeffs[c][0]);
+
+ vals = vmlaq_f32(vals, coefs, leftright4);
+
+ vst1_f32((float32_t*)&Values[o0][0], vget_low_f32(vals));
+ vst1_f32((float32_t*)&Values[o1][0], vget_high_f32(vals));
+ }
+}
+
+#define MixHrtf MixHrtf_Neon
+#define MixHrtfBlend MixHrtfBlend_Neon
+#define MixDirectHrtf MixDirectHrtf_Neon
+#include "hrtf_inc.c"
+
+
+void Mix_Neon(const ALfloat *data, ALsizei OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
+ ALfloat *CurrentGains, const ALfloat *TargetGains, ALsizei Counter, ALsizei OutPos,
+ ALsizei BufferSize)
+{
+ const ALfloat delta = (Counter > 0) ? 1.0f/(ALfloat)Counter : 0.0f;
+ ALsizei c;
+
+ ASSUME(OutChans > 0);
+ ASSUME(BufferSize > 0);
+ data = ASSUME_ALIGNED(data, 16);
+ OutBuffer = ASSUME_ALIGNED(OutBuffer, 16);
+
+ for(c = 0;c < OutChans;c++)
+ {
+ ALsizei pos = 0;
+ ALfloat gain = CurrentGains[c];
+ const ALfloat diff = TargetGains[c] - gain;
+
+ if(fabsf(diff) > FLT_EPSILON)
+ {
+ ALsizei minsize = mini(BufferSize, Counter);
+ const ALfloat step = diff * delta;
+ ALfloat step_count = 0.0f;
+ /* Mix with applying gain steps in aligned multiples of 4. */
+ if(LIKELY(minsize > 3))
+ {
+ const float32x4_t four4 = vdupq_n_f32(4.0f);
+ const float32x4_t step4 = vdupq_n_f32(step);
+ const float32x4_t gain4 = vdupq_n_f32(gain);
+ float32x4_t step_count4 = vsetq_lane_f32(0.0f,
+ vsetq_lane_f32(1.0f,
+ vsetq_lane_f32(2.0f,
+ vsetq_lane_f32(3.0f, vdupq_n_f32(0.0f), 3),
+ 2), 1), 0
+ );
+ ALsizei todo = minsize >> 2;
+
+ do {
+ const float32x4_t val4 = vld1q_f32(&data[pos]);
+ float32x4_t dry4 = vld1q_f32(&OutBuffer[c][OutPos+pos]);
+ dry4 = vmlaq_f32(dry4, val4, vmlaq_f32(gain4, step4, step_count4));
+ step_count4 = vaddq_f32(step_count4, four4);
+ vst1q_f32(&OutBuffer[c][OutPos+pos], dry4);
+ pos += 4;
+ } while(--todo);
+ /* NOTE: step_count4 now represents the next four counts after
+ * the last four mixed samples, so the lowest element
+ * represents the next step count to apply.
+ */
+ step_count = vgetq_lane_f32(step_count4, 0);
+ }
+ /* Mix with applying left over gain steps that aren't aligned multiples of 4. */
+ for(;pos < minsize;pos++)
+ {
+ OutBuffer[c][OutPos+pos] += data[pos]*(gain + step*step_count);
+ step_count += 1.0f;
+ }
+ if(pos == Counter)
+ gain = TargetGains[c];
+ else
+ gain += step*step_count;
+ CurrentGains[c] = gain;
+
+ /* Mix until pos is aligned with 4 or the mix is done. */
+ minsize = mini(BufferSize, (pos+3)&~3);
+ for(;pos < minsize;pos++)
+ OutBuffer[c][OutPos+pos] += data[pos]*gain;
+ }
+
+ if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
+ continue;
+ if(LIKELY(BufferSize-pos > 3))
+ {
+ ALsizei todo = (BufferSize-pos) >> 2;
+ const float32x4_t gain4 = vdupq_n_f32(gain);
+ do {
+ const float32x4_t val4 = vld1q_f32(&data[pos]);
+ float32x4_t dry4 = vld1q_f32(&OutBuffer[c][OutPos+pos]);
+ dry4 = vmlaq_f32(dry4, val4, gain4);
+ vst1q_f32(&OutBuffer[c][OutPos+pos], dry4);
+ pos += 4;
+ } while(--todo);
+ }
+ for(;pos < BufferSize;pos++)
+ OutBuffer[c][OutPos+pos] += data[pos]*gain;
+ }
+}
+
+void MixRow_Neon(ALfloat *OutBuffer, const ALfloat *Gains, const ALfloat (*restrict data)[BUFFERSIZE], ALsizei InChans, ALsizei InPos, ALsizei BufferSize)
+{
+ ALsizei c;
+
+ ASSUME(InChans > 0);
+ ASSUME(BufferSize > 0);
+
+ for(c = 0;c < InChans;c++)
+ {
+ ALsizei pos = 0;
+ const ALfloat gain = Gains[c];
+ if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
+ continue;
+
+ if(LIKELY(BufferSize > 3))
+ {
+ ALsizei todo = BufferSize >> 2;
+ float32x4_t gain4 = vdupq_n_f32(gain);
+ do {
+ const float32x4_t val4 = vld1q_f32(&data[c][InPos+pos]);
+ float32x4_t dry4 = vld1q_f32(&OutBuffer[pos]);
+ dry4 = vmlaq_f32(dry4, val4, gain4);
+ vst1q_f32(&OutBuffer[pos], dry4);
+ pos += 4;
+ } while(--todo);
+ }
+ for(;pos < BufferSize;pos++)
+ OutBuffer[pos] += data[c][InPos+pos]*gain;
+ }
+}
diff --git a/Alc/mixer/mixer_sse.c b/Alc/mixer/mixer_sse.c
new file mode 100644
index 00000000..725a5ebc
--- /dev/null
+++ b/Alc/mixer/mixer_sse.c
@@ -0,0 +1,250 @@
+#include "config.h"
+
+#include <xmmintrin.h>
+
+#include "AL/al.h"
+#include "AL/alc.h"
+#include "alMain.h"
+#include "alu.h"
+
+#include "alSource.h"
+#include "alAuxEffectSlot.h"
+#include "defs.h"
+
+
+const ALfloat *Resample_bsinc_SSE(const InterpState *state, const ALfloat *restrict src,
+ ALsizei frac, ALint increment, ALfloat *restrict dst,
+ ALsizei dstlen)
+{
+ const ALfloat *const filter = state->bsinc.filter;
+ const __m128 sf4 = _mm_set1_ps(state->bsinc.sf);
+ const ALsizei m = state->bsinc.m;
+ const __m128 *fil, *scd, *phd, *spd;
+ ALsizei pi, i, j, offset;
+ ALfloat pf;
+ __m128 r4;
+
+ ASSUME(m > 0);
+ ASSUME(dstlen > 0);
+
+ src -= state->bsinc.l;
+ for(i = 0;i < dstlen;i++)
+ {
+ // Calculate the phase index and factor.
+#define FRAC_PHASE_BITDIFF (FRACTIONBITS-BSINC_PHASE_BITS)
+ pi = frac >> FRAC_PHASE_BITDIFF;
+ pf = (frac & ((1<<FRAC_PHASE_BITDIFF)-1)) * (1.0f/(1<<FRAC_PHASE_BITDIFF));
+#undef FRAC_PHASE_BITDIFF
+
+ offset = m*pi*4;
+ fil = (const __m128*)ASSUME_ALIGNED(filter + offset, 16); offset += m;
+ scd = (const __m128*)ASSUME_ALIGNED(filter + offset, 16); offset += m;
+ phd = (const __m128*)ASSUME_ALIGNED(filter + offset, 16); offset += m;
+ spd = (const __m128*)ASSUME_ALIGNED(filter + offset, 16);
+
+ // Apply the scale and phase interpolated filter.
+ r4 = _mm_setzero_ps();
+ {
+ const ALsizei count = m >> 2;
+ const __m128 pf4 = _mm_set1_ps(pf);
+
+ ASSUME(count > 0);
+
+#define MLA4(x, y, z) _mm_add_ps(x, _mm_mul_ps(y, z))
+ for(j = 0;j < count;j++)
+ {
+ /* f = ((fil + sf*scd) + pf*(phd + sf*spd)) */
+ const __m128 f4 = MLA4(
+ MLA4(fil[j], sf4, scd[j]),
+ pf4, MLA4(phd[j], sf4, spd[j])
+ );
+ /* r += f*src */
+ r4 = MLA4(r4, f4, _mm_loadu_ps(&src[j*4]));
+ }
+#undef MLA4
+ }
+ r4 = _mm_add_ps(r4, _mm_shuffle_ps(r4, r4, _MM_SHUFFLE(0, 1, 2, 3)));
+ r4 = _mm_add_ps(r4, _mm_movehl_ps(r4, r4));
+ dst[i] = _mm_cvtss_f32(r4);
+
+ frac += increment;
+ src += frac>>FRACTIONBITS;
+ frac &= FRACTIONMASK;
+ }
+ return dst;
+}
+
+
+static inline void ApplyCoeffs(ALsizei Offset, ALfloat (*restrict Values)[2],
+ const ALsizei IrSize,
+ const ALfloat (*restrict Coeffs)[2],
+ ALfloat left, ALfloat right)
+{
+ const __m128 lrlr = _mm_setr_ps(left, right, left, right);
+ __m128 vals = _mm_setzero_ps();
+ __m128 coeffs;
+ ALsizei i;
+
+ Values = ASSUME_ALIGNED(Values, 16);
+ Coeffs = ASSUME_ALIGNED(Coeffs, 16);
+ if((Offset&1))
+ {
+ const ALsizei o0 = Offset&HRIR_MASK;
+ const ALsizei o1 = (Offset+IrSize-1)&HRIR_MASK;
+ __m128 imp0, imp1;
+
+ coeffs = _mm_load_ps(&Coeffs[0][0]);
+ vals = _mm_loadl_pi(vals, (__m64*)&Values[o0][0]);
+ imp0 = _mm_mul_ps(lrlr, coeffs);
+ vals = _mm_add_ps(imp0, vals);
+ _mm_storel_pi((__m64*)&Values[o0][0], vals);
+ for(i = 1;i < IrSize-1;i += 2)
+ {
+ const ALsizei o2 = (Offset+i)&HRIR_MASK;
+
+ coeffs = _mm_load_ps(&Coeffs[i+1][0]);
+ vals = _mm_load_ps(&Values[o2][0]);
+ imp1 = _mm_mul_ps(lrlr, coeffs);
+ imp0 = _mm_shuffle_ps(imp0, imp1, _MM_SHUFFLE(1, 0, 3, 2));
+ vals = _mm_add_ps(imp0, vals);
+ _mm_store_ps(&Values[o2][0], vals);
+ imp0 = imp1;
+ }
+ vals = _mm_loadl_pi(vals, (__m64*)&Values[o1][0]);
+ imp0 = _mm_movehl_ps(imp0, imp0);
+ vals = _mm_add_ps(imp0, vals);
+ _mm_storel_pi((__m64*)&Values[o1][0], vals);
+ }
+ else
+ {
+ for(i = 0;i < IrSize;i += 2)
+ {
+ const ALsizei o = (Offset + i)&HRIR_MASK;
+
+ coeffs = _mm_load_ps(&Coeffs[i][0]);
+ vals = _mm_load_ps(&Values[o][0]);
+ vals = _mm_add_ps(vals, _mm_mul_ps(lrlr, coeffs));
+ _mm_store_ps(&Values[o][0], vals);
+ }
+ }
+}
+
+#define MixHrtf MixHrtf_SSE
+#define MixHrtfBlend MixHrtfBlend_SSE
+#define MixDirectHrtf MixDirectHrtf_SSE
+#include "hrtf_inc.c"
+
+
+void Mix_SSE(const ALfloat *data, ALsizei OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
+ ALfloat *CurrentGains, const ALfloat *TargetGains, ALsizei Counter, ALsizei OutPos,
+ ALsizei BufferSize)
+{
+ const ALfloat delta = (Counter > 0) ? 1.0f/(ALfloat)Counter : 0.0f;
+ ALsizei c;
+
+ ASSUME(OutChans > 0);
+ ASSUME(BufferSize > 0);
+
+ for(c = 0;c < OutChans;c++)
+ {
+ ALsizei pos = 0;
+ ALfloat gain = CurrentGains[c];
+ const ALfloat diff = TargetGains[c] - gain;
+
+ if(fabsf(diff) > FLT_EPSILON)
+ {
+ ALsizei minsize = mini(BufferSize, Counter);
+ const ALfloat step = diff * delta;
+ ALfloat step_count = 0.0f;
+ /* Mix with applying gain steps in aligned multiples of 4. */
+ if(LIKELY(minsize > 3))
+ {
+ const __m128 four4 = _mm_set1_ps(4.0f);
+ const __m128 step4 = _mm_set1_ps(step);
+ const __m128 gain4 = _mm_set1_ps(gain);
+ __m128 step_count4 = _mm_setr_ps(0.0f, 1.0f, 2.0f, 3.0f);
+ ALsizei todo = minsize >> 2;
+ do {
+ const __m128 val4 = _mm_load_ps(&data[pos]);
+ __m128 dry4 = _mm_load_ps(&OutBuffer[c][OutPos+pos]);
+#define MLA4(x, y, z) _mm_add_ps(x, _mm_mul_ps(y, z))
+ /* dry += val * (gain + step*step_count) */
+ dry4 = MLA4(dry4, val4, MLA4(gain4, step4, step_count4));
+#undef MLA4
+ _mm_store_ps(&OutBuffer[c][OutPos+pos], dry4);
+ step_count4 = _mm_add_ps(step_count4, four4);
+ pos += 4;
+ } while(--todo);
+ /* NOTE: step_count4 now represents the next four counts after
+ * the last four mixed samples, so the lowest element
+ * represents the next step count to apply.
+ */
+ step_count = _mm_cvtss_f32(step_count4);
+ }
+ /* Mix with applying left over gain steps that aren't aligned multiples of 4. */
+ for(;pos < minsize;pos++)
+ {
+ OutBuffer[c][OutPos+pos] += data[pos]*(gain + step*step_count);
+ step_count += 1.0f;
+ }
+ if(pos == Counter)
+ gain = TargetGains[c];
+ else
+ gain += step*step_count;
+ CurrentGains[c] = gain;
+
+ /* Mix until pos is aligned with 4 or the mix is done. */
+ minsize = mini(BufferSize, (pos+3)&~3);
+ for(;pos < minsize;pos++)
+ OutBuffer[c][OutPos+pos] += data[pos]*gain;
+ }
+
+ if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
+ continue;
+ if(LIKELY(BufferSize-pos > 3))
+ {
+ ALsizei todo = (BufferSize-pos) >> 2;
+ const __m128 gain4 = _mm_set1_ps(gain);
+ do {
+ const __m128 val4 = _mm_load_ps(&data[pos]);
+ __m128 dry4 = _mm_load_ps(&OutBuffer[c][OutPos+pos]);
+ dry4 = _mm_add_ps(dry4, _mm_mul_ps(val4, gain4));
+ _mm_store_ps(&OutBuffer[c][OutPos+pos], dry4);
+ pos += 4;
+ } while(--todo);
+ }
+ for(;pos < BufferSize;pos++)
+ OutBuffer[c][OutPos+pos] += data[pos]*gain;
+ }
+}
+
+void MixRow_SSE(ALfloat *OutBuffer, const ALfloat *Gains, const ALfloat (*restrict data)[BUFFERSIZE], ALsizei InChans, ALsizei InPos, ALsizei BufferSize)
+{
+ ALsizei c;
+
+ ASSUME(InChans > 0);
+ ASSUME(BufferSize > 0);
+
+ for(c = 0;c < InChans;c++)
+ {
+ ALsizei pos = 0;
+ const ALfloat gain = Gains[c];
+ if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
+ continue;
+
+ if(LIKELY(BufferSize > 3))
+ {
+ ALsizei todo = BufferSize >> 2;
+ const __m128 gain4 = _mm_set1_ps(gain);
+ do {
+ const __m128 val4 = _mm_load_ps(&data[c][InPos+pos]);
+ __m128 dry4 = _mm_load_ps(&OutBuffer[pos]);
+ dry4 = _mm_add_ps(dry4, _mm_mul_ps(val4, gain4));
+ _mm_store_ps(&OutBuffer[pos], dry4);
+ pos += 4;
+ } while(--todo);
+ }
+ for(;pos < BufferSize;pos++)
+ OutBuffer[pos] += data[c][InPos+pos]*gain;
+ }
+}
diff --git a/Alc/mixer/mixer_sse2.c b/Alc/mixer/mixer_sse2.c
new file mode 100644
index 00000000..9cbaeb0a
--- /dev/null
+++ b/Alc/mixer/mixer_sse2.c
@@ -0,0 +1,84 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 2014 by Timothy Arceri <[email protected]>.
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#include "config.h"
+
+#include <xmmintrin.h>
+#include <emmintrin.h>
+
+#include "alu.h"
+#include "defs.h"
+
+
+const ALfloat *Resample_lerp_SSE2(const InterpState* UNUSED(state),
+ const ALfloat *restrict src, ALsizei frac, ALint increment,
+ ALfloat *restrict dst, ALsizei numsamples)
+{
+ const __m128i increment4 = _mm_set1_epi32(increment*4);
+ const __m128 fracOne4 = _mm_set1_ps(1.0f/FRACTIONONE);
+ const __m128i fracMask4 = _mm_set1_epi32(FRACTIONMASK);
+ alignas(16) ALsizei pos_[4], frac_[4];
+ __m128i frac4, pos4;
+ ALsizei todo, pos, i;
+
+ ASSUME(numsamples > 0);
+
+ InitiatePositionArrays(frac, increment, frac_, pos_, 4);
+ frac4 = _mm_setr_epi32(frac_[0], frac_[1], frac_[2], frac_[3]);
+ pos4 = _mm_setr_epi32(pos_[0], pos_[1], pos_[2], pos_[3]);
+
+ todo = numsamples & ~3;
+ for(i = 0;i < todo;i += 4)
+ {
+ const int pos0 = _mm_cvtsi128_si32(_mm_shuffle_epi32(pos4, _MM_SHUFFLE(0, 0, 0, 0)));
+ const int pos1 = _mm_cvtsi128_si32(_mm_shuffle_epi32(pos4, _MM_SHUFFLE(1, 1, 1, 1)));
+ const int pos2 = _mm_cvtsi128_si32(_mm_shuffle_epi32(pos4, _MM_SHUFFLE(2, 2, 2, 2)));
+ const int pos3 = _mm_cvtsi128_si32(_mm_shuffle_epi32(pos4, _MM_SHUFFLE(3, 3, 3, 3)));
+ const __m128 val1 = _mm_setr_ps(src[pos0 ], src[pos1 ], src[pos2 ], src[pos3 ]);
+ const __m128 val2 = _mm_setr_ps(src[pos0+1], src[pos1+1], src[pos2+1], src[pos3+1]);
+
+ /* val1 + (val2-val1)*mu */
+ const __m128 r0 = _mm_sub_ps(val2, val1);
+ const __m128 mu = _mm_mul_ps(_mm_cvtepi32_ps(frac4), fracOne4);
+ const __m128 out = _mm_add_ps(val1, _mm_mul_ps(mu, r0));
+
+ _mm_store_ps(&dst[i], out);
+
+ frac4 = _mm_add_epi32(frac4, increment4);
+ pos4 = _mm_add_epi32(pos4, _mm_srli_epi32(frac4, FRACTIONBITS));
+ frac4 = _mm_and_si128(frac4, fracMask4);
+ }
+
+ /* NOTE: These four elements represent the position *after* the last four
+ * samples, so the lowest element is the next position to resample.
+ */
+ pos = _mm_cvtsi128_si32(pos4);
+ frac = _mm_cvtsi128_si32(frac4);
+
+ for(;i < numsamples;++i)
+ {
+ dst[i] = lerp(src[pos], src[pos+1], frac * (1.0f/FRACTIONONE));
+
+ frac += increment;
+ pos += frac>>FRACTIONBITS;
+ frac &= FRACTIONMASK;
+ }
+ return dst;
+}
diff --git a/Alc/mixer/mixer_sse3.c b/Alc/mixer/mixer_sse3.c
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/Alc/mixer/mixer_sse3.c
diff --git a/Alc/mixer_sse2.c b/Alc/mixer/mixer_sse41.c
index 32f29227..e92a3dd0 100644
--- a/Alc/mixer_sse2.c
+++ b/Alc/mixer/mixer_sse41.c
@@ -22,32 +22,38 @@
#include <xmmintrin.h>
#include <emmintrin.h>
+#include <smmintrin.h>
#include "alu.h"
-#include "mixer_defs.h"
+#include "defs.h"
-const ALfloat *Resample_lerp32_SSE2(const BsincState* UNUSED(state), const ALfloat *src, ALuint frac, ALuint increment,
- ALfloat *restrict dst, ALuint numsamples)
+const ALfloat *Resample_lerp_SSE41(const InterpState* UNUSED(state),
+ const ALfloat *restrict src, ALsizei frac, ALint increment,
+ ALfloat *restrict dst, ALsizei numsamples)
{
const __m128i increment4 = _mm_set1_epi32(increment*4);
const __m128 fracOne4 = _mm_set1_ps(1.0f/FRACTIONONE);
const __m128i fracMask4 = _mm_set1_epi32(FRACTIONMASK);
- alignas(16) union { ALuint i[4]; float f[4]; } pos_;
- alignas(16) union { ALuint i[4]; float f[4]; } frac_;
+ alignas(16) ALsizei pos_[4], frac_[4];
__m128i frac4, pos4;
- ALuint pos;
- ALuint i;
+ ALsizei todo, pos, i;
- InitiatePositionArrays(frac, increment, frac_.i, pos_.i, 4);
+ ASSUME(numsamples > 0);
- frac4 = _mm_castps_si128(_mm_load_ps(frac_.f));
- pos4 = _mm_castps_si128(_mm_load_ps(pos_.f));
+ InitiatePositionArrays(frac, increment, frac_, pos_, 4);
+ frac4 = _mm_setr_epi32(frac_[0], frac_[1], frac_[2], frac_[3]);
+ pos4 = _mm_setr_epi32(pos_[0], pos_[1], pos_[2], pos_[3]);
- for(i = 0;numsamples-i > 3;i += 4)
+ todo = numsamples & ~3;
+ for(i = 0;i < todo;i += 4)
{
- const __m128 val1 = _mm_setr_ps(src[pos_.i[0]], src[pos_.i[1]], src[pos_.i[2]], src[pos_.i[3]]);
- const __m128 val2 = _mm_setr_ps(src[pos_.i[0]+1], src[pos_.i[1]+1], src[pos_.i[2]+1], src[pos_.i[3]+1]);
+ const int pos0 = _mm_extract_epi32(pos4, 0);
+ const int pos1 = _mm_extract_epi32(pos4, 1);
+ const int pos2 = _mm_extract_epi32(pos4, 2);
+ const int pos3 = _mm_extract_epi32(pos4, 3);
+ const __m128 val1 = _mm_setr_ps(src[pos0 ], src[pos1 ], src[pos2 ], src[pos3 ]);
+ const __m128 val2 = _mm_setr_ps(src[pos0+1], src[pos1+1], src[pos2+1], src[pos3+1]);
/* val1 + (val2-val1)*mu */
const __m128 r0 = _mm_sub_ps(val2, val1);
@@ -59,17 +65,15 @@ const ALfloat *Resample_lerp32_SSE2(const BsincState* UNUSED(state), const ALflo
frac4 = _mm_add_epi32(frac4, increment4);
pos4 = _mm_add_epi32(pos4, _mm_srli_epi32(frac4, FRACTIONBITS));
frac4 = _mm_and_si128(frac4, fracMask4);
-
- _mm_store_ps(pos_.f, _mm_castsi128_ps(pos4));
}
/* NOTE: These four elements represent the position *after* the last four
* samples, so the lowest element is the next position to resample.
*/
- pos = pos_.i[0];
+ pos = _mm_cvtsi128_si32(pos4);
frac = _mm_cvtsi128_si32(frac4);
- for(;i < numsamples;i++)
+ for(;i < numsamples;++i)
{
dst[i] = lerp(src[pos], src[pos+1], frac * (1.0f/FRACTIONONE));
diff --git a/Alc/mixer_c.c b/Alc/mixer_c.c
deleted file mode 100644
index ef37b730..00000000
--- a/Alc/mixer_c.c
+++ /dev/null
@@ -1,181 +0,0 @@
-#include "config.h"
-
-#include <assert.h>
-
-#include "alMain.h"
-#include "alu.h"
-#include "alSource.h"
-#include "alAuxEffectSlot.h"
-
-
-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 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 BsincState* UNUSED(state), const ALfloat *src, ALuint UNUSED(frac),
- ALuint UNUSED(increment), ALfloat *restrict dst, ALuint numsamples)
-{
-#if defined(HAVE_SSE) || defined(HAVE_NEON)
- /* Avoid copying the source data if it's aligned like the destination. */
- if((((intptr_t)src)&15) == (((intptr_t)dst)&15))
- return src;
-#endif
- memcpy(dst, src, numsamples*sizeof(ALfloat));
- return dst;
-}
-
-#define DECL_TEMPLATE(Sampler) \
-const ALfloat *Resample_##Sampler##_C(const BsincState* UNUSED(state), \
- const ALfloat *src, ALuint frac, ALuint increment, \
- ALfloat *restrict dst, ALuint numsamples) \
-{ \
- ALuint i; \
- for(i = 0;i < numsamples;i++) \
- { \
- dst[i] = Sampler(src, frac); \
- \
- frac += increment; \
- src += frac>>FRACTIONBITS; \
- frac &= FRACTIONMASK; \
- } \
- return dst; \
-}
-
-DECL_TEMPLATE(point32)
-DECL_TEMPLATE(lerp32)
-DECL_TEMPLATE(fir4_32)
-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)
-{
- ALuint i;
- for(i = 0;i < numsamples;i++)
- *(dst++) = ALfilterState_processSingle(filter, *(src++));
-}
-
-
-static inline void SetupCoeffs(ALfloat (*restrict OutCoeffs)[2],
- const HrtfParams *hrtfparams,
- ALuint IrSize, ALuint Counter)
-{
- ALuint c;
- for(c = 0;c < IrSize;c++)
- {
- 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],
- const ALfloat (*restrict CoeffStep)[2],
- ALfloat left, ALfloat right)
-{
- ALuint c;
- for(c = 0;c < IrSize;c++)
- {
- const ALuint off = (Offset+c)&HRIR_MASK;
- Values[off][0] += Coeffs[c][0] * left;
- Values[off][1] += Coeffs[c][1] * right;
- Coeffs[c][0] += CoeffStep[c][0];
- Coeffs[c][1] += CoeffStep[c][1];
- }
-}
-
-static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2],
- const ALuint IrSize,
- ALfloat (*restrict Coeffs)[2],
- ALfloat left, ALfloat right)
-{
- ALuint c;
- for(c = 0;c < IrSize;c++)
- {
- const ALuint off = (Offset+c)&HRIR_MASK;
- Values[off][0] += Coeffs[c][0] * left;
- Values[off][1] += Coeffs[c][1] * right;
- }
-}
-
-#define MixHrtf MixHrtf_C
-#include "mixer_inc.c"
-#undef MixHrtf
-
-
-void Mix_C(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
- MixGains *Gains, ALuint Counter, ALuint OutPos, ALuint BufferSize)
-{
- ALfloat gain, step;
- ALuint c;
-
- for(c = 0;c < OutChans;c++)
- {
- ALuint pos = 0;
- gain = Gains[c].Current;
- step = Gains[c].Step;
- if(step != 0.0f && Counter > 0)
- {
- ALuint minsize = minu(BufferSize, Counter);
- for(;pos < minsize;pos++)
- {
- OutBuffer[c][OutPos+pos] += data[pos]*gain;
- gain += step;
- }
- if(pos == Counter)
- gain = Gains[c].Target;
- Gains[c].Current = gain;
- }
-
- if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
- continue;
- for(;pos < BufferSize;pos++)
- OutBuffer[c][OutPos+pos] += data[pos]*gain;
- }
-}
diff --git a/Alc/mixer_defs.h b/Alc/mixer_defs.h
deleted file mode 100644
index 3c32278b..00000000
--- a/Alc/mixer_defs.h
+++ /dev/null
@@ -1,80 +0,0 @@
-#ifndef MIXER_DEFS_H
-#define MIXER_DEFS_H
-
-#include "AL/alc.h"
-#include "AL/al.h"
-#include "alMain.h"
-#include "alu.h"
-
-struct MixGains;
-
-struct HrtfParams;
-struct HrtfState;
-
-/* C resamplers */
-const ALfloat *Resample_copy32_C(const BsincState *state, const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen);
-const ALfloat *Resample_point32_C(const BsincState *state, const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen);
-const ALfloat *Resample_lerp32_C(const BsincState *state, const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen);
-const ALfloat *Resample_fir4_32_C(const BsincState *state, const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen);
-const ALfloat *Resample_fir8_32_C(const BsincState *state, const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen);
-const ALfloat *Resample_bsinc32_C(const BsincState *state, const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen);
-
-
-/* C mixers */
-void MixHrtf_C(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat *data,
- ALuint Counter, ALuint Offset, ALuint OutPos, const ALuint IrSize,
- const struct HrtfParams *hrtfparams, struct HrtfState *hrtfstate,
- ALuint BufferSize);
-void Mix_C(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
- struct MixGains *Gains, ALuint Counter, ALuint OutPos, ALuint BufferSize);
-
-/* SSE mixers */
-void MixHrtf_SSE(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat *data,
- ALuint Counter, ALuint Offset, ALuint OutPos, const ALuint IrSize,
- const struct HrtfParams *hrtfparams, struct HrtfState *hrtfstate,
- ALuint BufferSize);
-void Mix_SSE(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
- struct MixGains *Gains, ALuint Counter, ALuint OutPos, ALuint BufferSize);
-
-/* SSE resamplers */
-inline void InitiatePositionArrays(ALuint frac, ALuint increment, ALuint *frac_arr, ALuint *pos_arr, ALuint size)
-{
- ALuint i;
-
- pos_arr[0] = 0;
- frac_arr[0] = frac;
- for(i = 1;i < size;i++)
- {
- ALuint frac_tmp = frac_arr[i-1] + increment;
- pos_arr[i] = pos_arr[i-1] + (frac_tmp>>FRACTIONBITS);
- frac_arr[i] = frac_tmp&FRACTIONMASK;
- }
-}
-
-const ALfloat *Resample_bsinc32_SSE(const BsincState *state, const ALfloat *src, ALuint frac,
- ALuint increment, ALfloat *restrict dst, ALuint dstlen);
-
-const ALfloat *Resample_lerp32_SSE2(const BsincState *state, const ALfloat *src, ALuint frac, ALuint increment,
- ALfloat *restrict dst, ALuint numsamples);
-const ALfloat *Resample_lerp32_SSE41(const BsincState *state, const ALfloat *src, ALuint frac, ALuint increment,
- ALfloat *restrict dst, ALuint numsamples);
-
-const ALfloat *Resample_fir4_32_SSE3(const 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,
- const struct HrtfParams *hrtfparams, struct HrtfState *hrtfstate,
- ALuint BufferSize);
-void Mix_Neon(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
- struct MixGains *Gains, ALuint Counter, ALuint OutPos, ALuint BufferSize);
-
-#endif /* MIXER_DEFS_H */
diff --git a/Alc/mixer_inc.c b/Alc/mixer_inc.c
deleted file mode 100644
index a82930cc..00000000
--- a/Alc/mixer_inc.c
+++ /dev/null
@@ -1,79 +0,0 @@
-#include "config.h"
-
-#include "alMain.h"
-#include "alSource.h"
-
-#include "hrtf.h"
-#include "mixer_defs.h"
-#include "align.h"
-
-
-static inline void SetupCoeffs(ALfloat (*restrict OutCoeffs)[2],
- const HrtfParams *hrtfparams,
- ALuint IrSize, ALuint Counter);
-static inline void ApplyCoeffsStep(ALuint Offset, ALfloat (*restrict Values)[2],
- const ALuint irSize,
- ALfloat (*restrict Coeffs)[2],
- const ALfloat (*restrict CoeffStep)[2],
- ALfloat left, ALfloat right);
-static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2],
- const ALuint irSize,
- ALfloat (*restrict Coeffs)[2],
- ALfloat left, ALfloat right);
-
-
-void MixHrtf(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat *data,
- ALuint Counter, ALuint Offset, ALuint OutPos, const ALuint IrSize,
- const HrtfParams *hrtfparams, HrtfState *hrtfstate, ALuint BufferSize)
-{
- alignas(16) ALfloat Coeffs[HRIR_LENGTH][2];
- ALuint Delay[2];
- ALfloat left, right;
- ALuint pos;
-
- SetupCoeffs(Coeffs, hrtfparams, IrSize, Counter);
- Delay[0] = hrtfparams->Delay[0] - (hrtfparams->DelayStep[0]*Counter);
- Delay[1] = hrtfparams->Delay[1] - (hrtfparams->DelayStep[1]*Counter);
-
- pos = 0;
- for(;pos < BufferSize && pos < Counter;pos++)
- {
- hrtfstate->History[Offset&HRTF_HISTORY_MASK] = data[pos];
- left = lerp(hrtfstate->History[(Offset-(Delay[0]>>HRTFDELAY_BITS))&HRTF_HISTORY_MASK],
- hrtfstate->History[(Offset-(Delay[0]>>HRTFDELAY_BITS)-1)&HRTF_HISTORY_MASK],
- (Delay[0]&HRTFDELAY_MASK)*(1.0f/HRTFDELAY_FRACONE));
- right = lerp(hrtfstate->History[(Offset-(Delay[1]>>HRTFDELAY_BITS))&HRTF_HISTORY_MASK],
- hrtfstate->History[(Offset-(Delay[1]>>HRTFDELAY_BITS)-1)&HRTF_HISTORY_MASK],
- (Delay[1]&HRTFDELAY_MASK)*(1.0f/HRTFDELAY_FRACONE));
-
- Delay[0] += hrtfparams->DelayStep[0];
- Delay[1] += hrtfparams->DelayStep[1];
-
- hrtfstate->Values[(Offset+IrSize)&HRIR_MASK][0] = 0.0f;
- hrtfstate->Values[(Offset+IrSize)&HRIR_MASK][1] = 0.0f;
- Offset++;
-
- ApplyCoeffsStep(Offset, hrtfstate->Values, IrSize, Coeffs, hrtfparams->CoeffStep, left, right);
- OutBuffer[0][OutPos] += hrtfstate->Values[Offset&HRIR_MASK][0];
- OutBuffer[1][OutPos] += hrtfstate->Values[Offset&HRIR_MASK][1];
- OutPos++;
- }
-
- Delay[0] >>= HRTFDELAY_BITS;
- Delay[1] >>= HRTFDELAY_BITS;
- for(;pos < BufferSize;pos++)
- {
- hrtfstate->History[Offset&HRTF_HISTORY_MASK] = data[pos];
- left = hrtfstate->History[(Offset-Delay[0])&HRTF_HISTORY_MASK];
- right = hrtfstate->History[(Offset-Delay[1])&HRTF_HISTORY_MASK];
-
- hrtfstate->Values[(Offset+IrSize)&HRIR_MASK][0] = 0.0f;
- hrtfstate->Values[(Offset+IrSize)&HRIR_MASK][1] = 0.0f;
- Offset++;
-
- ApplyCoeffs(Offset, hrtfstate->Values, IrSize, Coeffs, left, right);
- OutBuffer[0][OutPos] += hrtfstate->Values[Offset&HRIR_MASK][0];
- OutBuffer[1][OutPos] += hrtfstate->Values[Offset&HRIR_MASK][1];
- OutPos++;
- }
-}
diff --git a/Alc/mixer_neon.c b/Alc/mixer_neon.c
deleted file mode 100644
index a89caeae..00000000
--- a/Alc/mixer_neon.c
+++ /dev/null
@@ -1,139 +0,0 @@
-#include "config.h"
-
-#include <arm_neon.h>
-
-#include "AL/al.h"
-#include "AL/alc.h"
-#include "alMain.h"
-#include "alu.h"
-#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],
- const ALfloat (*restrict CoeffStep)[2],
- ALfloat left, ALfloat right)
-{
- ALuint c;
- float32x4_t leftright4;
- {
- float32x2_t leftright2 = vdup_n_f32(0.0);
- leftright2 = vset_lane_f32(left, leftright2, 0);
- leftright2 = vset_lane_f32(right, leftright2, 1);
- leftright4 = vcombine_f32(leftright2, leftright2);
- }
- for(c = 0;c < IrSize;c += 2)
- {
- const ALuint o0 = (Offset+c)&HRIR_MASK;
- const ALuint o1 = (o0+1)&HRIR_MASK;
- float32x4_t vals = vcombine_f32(vld1_f32((float32_t*)&Values[o0][0]),
- vld1_f32((float32_t*)&Values[o1][0]));
- float32x4_t coefs = vld1q_f32((float32_t*)&Coeffs[c][0]);
- float32x4_t deltas = vld1q_f32(&CoeffStep[c][0]);
-
- vals = vmlaq_f32(vals, coefs, leftright4);
- coefs = vaddq_f32(coefs, deltas);
-
- vst1_f32((float32_t*)&Values[o0][0], vget_low_f32(vals));
- vst1_f32((float32_t*)&Values[o1][0], vget_high_f32(vals));
- vst1q_f32(&Coeffs[c][0], coefs);
- }
-}
-
-static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2],
- const ALuint IrSize,
- ALfloat (*restrict Coeffs)[2],
- ALfloat left, ALfloat right)
-{
- ALuint c;
- float32x4_t leftright4;
- {
- float32x2_t leftright2 = vdup_n_f32(0.0);
- leftright2 = vset_lane_f32(left, leftright2, 0);
- leftright2 = vset_lane_f32(right, leftright2, 1);
- leftright4 = vcombine_f32(leftright2, leftright2);
- }
- for(c = 0;c < IrSize;c += 2)
- {
- const ALuint o0 = (Offset+c)&HRIR_MASK;
- const ALuint o1 = (o0+1)&HRIR_MASK;
- float32x4_t vals = vcombine_f32(vld1_f32((float32_t*)&Values[o0][0]),
- vld1_f32((float32_t*)&Values[o1][0]));
- float32x4_t coefs = vld1q_f32((float32_t*)&Coeffs[c][0]);
-
- vals = vmlaq_f32(vals, coefs, leftright4);
-
- vst1_f32((float32_t*)&Values[o0][0], vget_low_f32(vals));
- vst1_f32((float32_t*)&Values[o1][0], vget_high_f32(vals));
- }
-}
-
-#define MixHrtf MixHrtf_Neon
-#include "mixer_inc.c"
-#undef MixHrtf
-
-
-void Mix_Neon(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
- MixGains *Gains, ALuint Counter, ALuint OutPos, ALuint BufferSize)
-{
- ALfloat gain, step;
- float32x4_t gain4;
- ALuint c;
-
- for(c = 0;c < OutChans;c++)
- {
- ALuint pos = 0;
- gain = Gains[c].Current;
- step = Gains[c].Step;
- if(step != 0.0f && Counter > 0)
- {
- ALuint minsize = minu(BufferSize, Counter);
- for(;pos < minsize;pos++)
- {
- OutBuffer[c][OutPos+pos] += data[pos]*gain;
- gain += step;
- }
- if(pos == Counter)
- gain = Gains[c].Target;
- Gains[c].Current = gain;
-
- /* Mix until pos is aligned with 4 or the mix is done. */
- minsize = minu(BufferSize, (pos+3)&~3);
- for(;pos < minsize;pos++)
- OutBuffer[c][OutPos+pos] += data[pos]*gain;
- }
-
- if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
- continue;
- gain4 = vdupq_n_f32(gain);
- for(;BufferSize-pos > 3;pos += 4)
- {
- const float32x4_t val4 = vld1q_f32(&data[pos]);
- float32x4_t dry4 = vld1q_f32(&OutBuffer[c][OutPos+pos]);
- dry4 = vmlaq_f32(dry4, val4, gain4);
- vst1q_f32(&OutBuffer[c][OutPos+pos], dry4);
- }
- for(;pos < BufferSize;pos++)
- OutBuffer[c][OutPos+pos] += data[pos]*gain;
- }
-}
diff --git a/Alc/mixer_sse.c b/Alc/mixer_sse.c
deleted file mode 100644
index 090b7a5a..00000000
--- a/Alc/mixer_sse.c
+++ /dev/null
@@ -1,279 +0,0 @@
-#include "config.h"
-
-#include <xmmintrin.h>
-
-#include "AL/al.h"
-#include "AL/alc.h"
-#include "alMain.h"
-#include "alu.h"
-
-#include "alSource.h"
-#include "alAuxEffectSlot.h"
-#include "mixer_defs.h"
-
-
-const ALfloat *Resample_bsinc32_SSE(const 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],
- const ALfloat (*restrict CoeffStep)[2],
- ALfloat left, ALfloat right)
-{
- const __m128 lrlr = _mm_setr_ps(left, right, left, right);
- __m128 coeffs, deltas, imp0, imp1;
- __m128 vals = _mm_setzero_ps();
- ALuint i;
-
- if((Offset&1))
- {
- const ALuint o0 = Offset&HRIR_MASK;
- const ALuint o1 = (Offset+IrSize-1)&HRIR_MASK;
-
- coeffs = _mm_load_ps(&Coeffs[0][0]);
- deltas = _mm_load_ps(&CoeffStep[0][0]);
- vals = _mm_loadl_pi(vals, (__m64*)&Values[o0][0]);
- imp0 = _mm_mul_ps(lrlr, coeffs);
- coeffs = _mm_add_ps(coeffs, deltas);
- vals = _mm_add_ps(imp0, vals);
- _mm_store_ps(&Coeffs[0][0], coeffs);
- _mm_storel_pi((__m64*)&Values[o0][0], vals);
- for(i = 1;i < IrSize-1;i += 2)
- {
- const ALuint o2 = (Offset+i)&HRIR_MASK;
-
- coeffs = _mm_load_ps(&Coeffs[i+1][0]);
- deltas = _mm_load_ps(&CoeffStep[i+1][0]);
- vals = _mm_load_ps(&Values[o2][0]);
- imp1 = _mm_mul_ps(lrlr, coeffs);
- coeffs = _mm_add_ps(coeffs, deltas);
- imp0 = _mm_shuffle_ps(imp0, imp1, _MM_SHUFFLE(1, 0, 3, 2));
- vals = _mm_add_ps(imp0, vals);
- _mm_store_ps(&Coeffs[i+1][0], coeffs);
- _mm_store_ps(&Values[o2][0], vals);
- imp0 = imp1;
- }
- vals = _mm_loadl_pi(vals, (__m64*)&Values[o1][0]);
- imp0 = _mm_movehl_ps(imp0, imp0);
- vals = _mm_add_ps(imp0, vals);
- _mm_storel_pi((__m64*)&Values[o1][0], vals);
- }
- else
- {
- for(i = 0;i < IrSize;i += 2)
- {
- const ALuint o = (Offset + i)&HRIR_MASK;
-
- coeffs = _mm_load_ps(&Coeffs[i][0]);
- deltas = _mm_load_ps(&CoeffStep[i][0]);
- vals = _mm_load_ps(&Values[o][0]);
- imp0 = _mm_mul_ps(lrlr, coeffs);
- coeffs = _mm_add_ps(coeffs, deltas);
- vals = _mm_add_ps(imp0, vals);
- _mm_store_ps(&Coeffs[i][0], coeffs);
- _mm_store_ps(&Values[o][0], vals);
- }
- }
-}
-
-static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2],
- const ALuint IrSize,
- ALfloat (*restrict Coeffs)[2],
- ALfloat left, ALfloat right)
-{
- const __m128 lrlr = _mm_setr_ps(left, right, left, right);
- __m128 vals = _mm_setzero_ps();
- __m128 coeffs;
- ALuint i;
-
- if((Offset&1))
- {
- const ALuint o0 = Offset&HRIR_MASK;
- const ALuint o1 = (Offset+IrSize-1)&HRIR_MASK;
- __m128 imp0, imp1;
-
- coeffs = _mm_load_ps(&Coeffs[0][0]);
- vals = _mm_loadl_pi(vals, (__m64*)&Values[o0][0]);
- imp0 = _mm_mul_ps(lrlr, coeffs);
- vals = _mm_add_ps(imp0, vals);
- _mm_storel_pi((__m64*)&Values[o0][0], vals);
- for(i = 1;i < IrSize-1;i += 2)
- {
- const ALuint o2 = (Offset+i)&HRIR_MASK;
-
- coeffs = _mm_load_ps(&Coeffs[i+1][0]);
- vals = _mm_load_ps(&Values[o2][0]);
- imp1 = _mm_mul_ps(lrlr, coeffs);
- imp0 = _mm_shuffle_ps(imp0, imp1, _MM_SHUFFLE(1, 0, 3, 2));
- vals = _mm_add_ps(imp0, vals);
- _mm_store_ps(&Values[o2][0], vals);
- imp0 = imp1;
- }
- vals = _mm_loadl_pi(vals, (__m64*)&Values[o1][0]);
- imp0 = _mm_movehl_ps(imp0, imp0);
- vals = _mm_add_ps(imp0, vals);
- _mm_storel_pi((__m64*)&Values[o1][0], vals);
- }
- else
- {
- for(i = 0;i < IrSize;i += 2)
- {
- const ALuint o = (Offset + i)&HRIR_MASK;
-
- coeffs = _mm_load_ps(&Coeffs[i][0]);
- vals = _mm_load_ps(&Values[o][0]);
- vals = _mm_add_ps(vals, _mm_mul_ps(lrlr, coeffs));
- _mm_store_ps(&Values[o][0], vals);
- }
- }
-}
-
-#define MixHrtf MixHrtf_SSE
-#include "mixer_inc.c"
-#undef MixHrtf
-
-
-void Mix_SSE(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
- MixGains *Gains, ALuint Counter, ALuint OutPos, ALuint BufferSize)
-{
- ALfloat gain, step;
- __m128 gain4;
- ALuint c;
-
- for(c = 0;c < OutChans;c++)
- {
- ALuint pos = 0;
- gain = Gains[c].Current;
- step = Gains[c].Step;
- if(step != 0.0f && Counter > 0)
- {
- ALuint minsize = minu(BufferSize, Counter);
- /* Mix with applying gain steps in aligned multiples of 4. */
- if(minsize-pos > 3)
- {
- __m128 step4;
- gain4 = _mm_setr_ps(
- gain,
- gain + step,
- gain + step + step,
- gain + step + step + step
- );
- step4 = _mm_set1_ps(step + step + step + step);
- do {
- const __m128 val4 = _mm_load_ps(&data[pos]);
- __m128 dry4 = _mm_load_ps(&OutBuffer[c][OutPos+pos]);
- dry4 = _mm_add_ps(dry4, _mm_mul_ps(val4, gain4));
- gain4 = _mm_add_ps(gain4, step4);
- _mm_store_ps(&OutBuffer[c][OutPos+pos], dry4);
- pos += 4;
- } while(minsize-pos > 3);
- /* NOTE: gain4 now represents the next four gains after the
- * last four mixed samples, so the lowest element represents
- * the next gain to apply.
- */
- gain = _mm_cvtss_f32(gain4);
- }
- /* Mix with applying left over gain steps that aren't aligned multiples of 4. */
- for(;pos < minsize;pos++)
- {
- OutBuffer[c][OutPos+pos] += data[pos]*gain;
- gain += step;
- }
- if(pos == Counter)
- gain = Gains[c].Target;
- Gains[c].Current = gain;
-
- /* Mix until pos is aligned with 4 or the mix is done. */
- minsize = minu(BufferSize, (pos+3)&~3);
- for(;pos < minsize;pos++)
- OutBuffer[c][OutPos+pos] += data[pos]*gain;
- }
-
- if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
- continue;
- gain4 = _mm_set1_ps(gain);
- for(;BufferSize-pos > 3;pos += 4)
- {
- const __m128 val4 = _mm_load_ps(&data[pos]);
- __m128 dry4 = _mm_load_ps(&OutBuffer[c][OutPos+pos]);
- dry4 = _mm_add_ps(dry4, _mm_mul_ps(val4, gain4));
- _mm_store_ps(&OutBuffer[c][OutPos+pos], dry4);
- }
- for(;pos < BufferSize;pos++)
- OutBuffer[c][OutPos+pos] += data[pos]*gain;
- }
-}
diff --git a/Alc/mixer_sse3.c b/Alc/mixer_sse3.c
deleted file mode 100644
index 7085c537..00000000
--- a/Alc/mixer_sse3.c
+++ /dev/null
@@ -1,162 +0,0 @@
-/**
- * OpenAL cross platform audio library, SSE3 mixer functions
- *
- * Copyright (C) 2014 by Timothy Arceri <[email protected]>.
- * Copyright (C) 2015 by Chris Robinson <[email protected]>.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
-#include "config.h"
-
-#include <xmmintrin.h>
-#include <emmintrin.h>
-#include <pmmintrin.h>
-
-#include "alu.h"
-#include "mixer_defs.h"
-
-
-const ALfloat *Resample_fir4_32_SSE3(const 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);
-
- _mm_store_ps(pos_.f, _mm_castsi128_ps(pos4));
- _mm_store_ps(frac_.f, _mm_castsi128_ps(frac4));
- }
-
- /* NOTE: These four elements represent the position *after* the last four
- * samples, so the lowest element is the next position to resample.
- */
- pos = pos_.i[0];
- frac = frac_.i[0];
-
- for(;i < numsamples;i++)
- {
- dst[i] = resample_fir4(src[pos], src[pos+1], src[pos+2], src[pos+3], frac);
-
- frac += increment;
- pos += frac>>FRACTIONBITS;
- frac &= FRACTIONMASK;
- }
- return dst;
-}
-
-const ALfloat *Resample_fir8_32_SSE3(const BsincState* UNUSED(state), const ALfloat *src, ALuint frac, ALuint increment,
- ALfloat *restrict dst, ALuint numsamples)
-{
- const __m128i increment4 = _mm_set1_epi32(increment*4);
- const __m128i fracMask4 = _mm_set1_epi32(FRACTIONMASK);
- alignas(16) union { ALuint i[4]; float f[4]; } pos_;
- alignas(16) union { ALuint i[4]; float f[4]; } frac_;
- __m128i frac4, pos4;
- ALuint pos;
- ALuint i, j;
-
- InitiatePositionArrays(frac, increment, frac_.i, pos_.i, 4);
-
- frac4 = _mm_castps_si128(_mm_load_ps(frac_.f));
- pos4 = _mm_castps_si128(_mm_load_ps(pos_.f));
-
- src -= 3;
- for(i = 0;numsamples-i > 3;i += 4)
- {
- __m128 out[2];
- for(j = 0;j < 8;j+=4)
- {
- const __m128 val0 = _mm_loadu_ps(&src[pos_.i[0]+j]);
- const __m128 val1 = _mm_loadu_ps(&src[pos_.i[1]+j]);
- const __m128 val2 = _mm_loadu_ps(&src[pos_.i[2]+j]);
- const __m128 val3 = _mm_loadu_ps(&src[pos_.i[3]+j]);
- __m128 k0 = _mm_load_ps(&ResampleCoeffs.FIR8[frac_.i[0]][j]);
- __m128 k1 = _mm_load_ps(&ResampleCoeffs.FIR8[frac_.i[1]][j]);
- __m128 k2 = _mm_load_ps(&ResampleCoeffs.FIR8[frac_.i[2]][j]);
- __m128 k3 = _mm_load_ps(&ResampleCoeffs.FIR8[frac_.i[3]][j]);
-
- k0 = _mm_mul_ps(k0, val0);
- k1 = _mm_mul_ps(k1, val1);
- k2 = _mm_mul_ps(k2, val2);
- k3 = _mm_mul_ps(k3, val3);
- k0 = _mm_hadd_ps(k0, k1);
- k2 = _mm_hadd_ps(k2, k3);
- out[j>>2] = _mm_hadd_ps(k0, k2);
- }
-
- out[0] = _mm_add_ps(out[0], out[1]);
- _mm_store_ps(&dst[i], out[0]);
-
- frac4 = _mm_add_epi32(frac4, increment4);
- pos4 = _mm_add_epi32(pos4, _mm_srli_epi32(frac4, FRACTIONBITS));
- frac4 = _mm_and_si128(frac4, fracMask4);
-
- _mm_store_ps(pos_.f, _mm_castsi128_ps(pos4));
- _mm_store_ps(frac_.f, _mm_castsi128_ps(frac4));
- }
-
- pos = pos_.i[0];
- frac = frac_.i[0];
-
- for(;i < numsamples;i++)
- {
- dst[i] = resample_fir8(src[pos ], src[pos+1], src[pos+2], src[pos+3],
- src[pos+4], src[pos+5], src[pos+6], src[pos+7], frac);
-
- frac += increment;
- pos += frac>>FRACTIONBITS;
- frac &= FRACTIONMASK;
- }
- return dst;
-}
diff --git a/Alc/mixer_sse41.c b/Alc/mixer_sse41.c
deleted file mode 100644
index e832e5df..00000000
--- a/Alc/mixer_sse41.c
+++ /dev/null
@@ -1,224 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 2014 by Timothy Arceri <[email protected]>.
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
-#include "config.h"
-
-#include <xmmintrin.h>
-#include <emmintrin.h>
-#include <smmintrin.h>
-
-#include "alu.h"
-#include "mixer_defs.h"
-
-
-const ALfloat *Resample_lerp32_SSE41(const BsincState* UNUSED(state), const ALfloat *src, ALuint frac, ALuint increment,
- ALfloat *restrict dst, ALuint numsamples)
-{
- const __m128i increment4 = _mm_set1_epi32(increment*4);
- const __m128 fracOne4 = _mm_set1_ps(1.0f/FRACTIONONE);
- const __m128i fracMask4 = _mm_set1_epi32(FRACTIONMASK);
- alignas(16) union { ALuint i[4]; float f[4]; } pos_;
- alignas(16) union { ALuint i[4]; float f[4]; } frac_;
- __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));
-
- for(i = 0;numsamples-i > 3;i += 4)
- {
- const __m128 val1 = _mm_setr_ps(src[pos_.i[0]], src[pos_.i[1]], src[pos_.i[2]], src[pos_.i[3]]);
- const __m128 val2 = _mm_setr_ps(src[pos_.i[0]+1], src[pos_.i[1]+1], src[pos_.i[2]+1], src[pos_.i[3]+1]);
-
- /* val1 + (val2-val1)*mu */
- const __m128 r0 = _mm_sub_ps(val2, val1);
- const __m128 mu = _mm_mul_ps(_mm_cvtepi32_ps(frac4), fracOne4);
- const __m128 out = _mm_add_ps(val1, _mm_mul_ps(mu, r0));
-
- _mm_store_ps(&dst[i], out);
-
- frac4 = _mm_add_epi32(frac4, increment4);
- pos4 = _mm_add_epi32(pos4, _mm_srli_epi32(frac4, FRACTIONBITS));
- frac4 = _mm_and_si128(frac4, fracMask4);
-
- pos_.i[0] = _mm_extract_epi32(pos4, 0);
- pos_.i[1] = _mm_extract_epi32(pos4, 1);
- pos_.i[2] = _mm_extract_epi32(pos4, 2);
- pos_.i[3] = _mm_extract_epi32(pos4, 3);
- }
-
- /* NOTE: These four elements represent the position *after* the last four
- * samples, so the lowest element is the next position to resample.
- */
- pos = pos_.i[0];
- frac = _mm_cvtsi128_si32(frac4);
-
- for(;i < numsamples;i++)
- {
- dst[i] = lerp(src[pos], src[pos+1], frac * (1.0f/FRACTIONONE));
-
- frac += increment;
- pos += frac>>FRACTIONBITS;
- frac &= FRACTIONMASK;
- }
- return dst;
-}
-
-const ALfloat *Resample_fir4_32_SSE41(const 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;
-}
diff --git a/Alc/mixvoice.c b/Alc/mixvoice.c
new file mode 100644
index 00000000..d019b898
--- /dev/null
+++ b/Alc/mixvoice.c
@@ -0,0 +1,762 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 1999-2007 by authors.
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#include "config.h"
+
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+
+#include "alMain.h"
+#include "AL/al.h"
+#include "AL/alc.h"
+#include "alSource.h"
+#include "alBuffer.h"
+#include "alListener.h"
+#include "alAuxEffectSlot.h"
+#include "sample_cvt.h"
+#include "alu.h"
+#include "alconfig.h"
+#include "ringbuffer.h"
+
+#include "cpu_caps.h"
+#include "mixer/defs.h"
+
+
+static_assert((INT_MAX>>FRACTIONBITS)/MAX_PITCH > BUFFERSIZE,
+ "MAX_PITCH and/or BUFFERSIZE are too large for FRACTIONBITS!");
+
+extern inline void InitiatePositionArrays(ALsizei frac, ALint increment, ALsizei *restrict frac_arr, ALsizei *restrict pos_arr, ALsizei size);
+
+
+/* BSinc24 requires up to 23 extra samples before the current position, and 24 after. */
+static_assert(MAX_RESAMPLE_PADDING >= 24, "MAX_RESAMPLE_PADDING must be at least 24!");
+
+
+enum Resampler ResamplerDefault = LinearResampler;
+
+MixerFunc MixSamples = Mix_C;
+RowMixerFunc MixRowSamples = MixRow_C;
+static HrtfMixerFunc MixHrtfSamples = MixHrtf_C;
+static HrtfMixerBlendFunc MixHrtfBlendSamples = MixHrtfBlend_C;
+
+static MixerFunc SelectMixer(void)
+{
+#ifdef HAVE_NEON
+ if((CPUCapFlags&CPU_CAP_NEON))
+ return Mix_Neon;
+#endif
+#ifdef HAVE_SSE
+ if((CPUCapFlags&CPU_CAP_SSE))
+ return Mix_SSE;
+#endif
+ return Mix_C;
+}
+
+static RowMixerFunc SelectRowMixer(void)
+{
+#ifdef HAVE_NEON
+ if((CPUCapFlags&CPU_CAP_NEON))
+ return MixRow_Neon;
+#endif
+#ifdef HAVE_SSE
+ if((CPUCapFlags&CPU_CAP_SSE))
+ return MixRow_SSE;
+#endif
+ return MixRow_C;
+}
+
+static inline HrtfMixerFunc SelectHrtfMixer(void)
+{
+#ifdef HAVE_NEON
+ if((CPUCapFlags&CPU_CAP_NEON))
+ return MixHrtf_Neon;
+#endif
+#ifdef HAVE_SSE
+ if((CPUCapFlags&CPU_CAP_SSE))
+ return MixHrtf_SSE;
+#endif
+ return MixHrtf_C;
+}
+
+static inline HrtfMixerBlendFunc SelectHrtfBlendMixer(void)
+{
+#ifdef HAVE_NEON
+ if((CPUCapFlags&CPU_CAP_NEON))
+ return MixHrtfBlend_Neon;
+#endif
+#ifdef HAVE_SSE
+ if((CPUCapFlags&CPU_CAP_SSE))
+ return MixHrtfBlend_SSE;
+#endif
+ return MixHrtfBlend_C;
+}
+
+ResamplerFunc SelectResampler(enum Resampler resampler)
+{
+ switch(resampler)
+ {
+ case PointResampler:
+ return Resample_point_C;
+ case LinearResampler:
+#ifdef HAVE_NEON
+ if((CPUCapFlags&CPU_CAP_NEON))
+ return Resample_lerp_Neon;
+#endif
+#ifdef HAVE_SSE4_1
+ if((CPUCapFlags&CPU_CAP_SSE4_1))
+ return Resample_lerp_SSE41;
+#endif
+#ifdef HAVE_SSE2
+ if((CPUCapFlags&CPU_CAP_SSE2))
+ return Resample_lerp_SSE2;
+#endif
+ return Resample_lerp_C;
+ case FIR4Resampler:
+ return Resample_cubic_C;
+ case BSinc12Resampler:
+ case BSinc24Resampler:
+#ifdef HAVE_NEON
+ if((CPUCapFlags&CPU_CAP_NEON))
+ return Resample_bsinc_Neon;
+#endif
+#ifdef HAVE_SSE
+ if((CPUCapFlags&CPU_CAP_SSE))
+ return Resample_bsinc_SSE;
+#endif
+ return Resample_bsinc_C;
+ }
+
+ return Resample_point_C;
+}
+
+
+void aluInitMixer(void)
+{
+ const char *str;
+
+ if(ConfigValueStr(NULL, NULL, "resampler", &str))
+ {
+ if(strcasecmp(str, "point") == 0 || strcasecmp(str, "none") == 0)
+ ResamplerDefault = PointResampler;
+ else if(strcasecmp(str, "linear") == 0)
+ ResamplerDefault = LinearResampler;
+ else if(strcasecmp(str, "cubic") == 0)
+ ResamplerDefault = FIR4Resampler;
+ else if(strcasecmp(str, "bsinc12") == 0)
+ ResamplerDefault = BSinc12Resampler;
+ else if(strcasecmp(str, "bsinc24") == 0)
+ ResamplerDefault = BSinc24Resampler;
+ else if(strcasecmp(str, "bsinc") == 0)
+ {
+ WARN("Resampler option \"%s\" is deprecated, using bsinc12\n", str);
+ ResamplerDefault = BSinc12Resampler;
+ }
+ else if(strcasecmp(str, "sinc4") == 0 || strcasecmp(str, "sinc8") == 0)
+ {
+ WARN("Resampler option \"%s\" is deprecated, using cubic\n", str);
+ ResamplerDefault = FIR4Resampler;
+ }
+ else
+ {
+ char *end;
+ long n = strtol(str, &end, 0);
+ if(*end == '\0' && (n == PointResampler || n == LinearResampler || n == FIR4Resampler))
+ ResamplerDefault = n;
+ else
+ WARN("Invalid resampler: %s\n", str);
+ }
+ }
+
+ MixHrtfBlendSamples = SelectHrtfBlendMixer();
+ MixHrtfSamples = SelectHrtfMixer();
+ MixSamples = SelectMixer();
+ MixRowSamples = SelectRowMixer();
+}
+
+
+static void SendAsyncEvent(ALCcontext *context, ALuint enumtype, ALenum type,
+ ALuint objid, ALuint param, const char *msg)
+{
+ AsyncEvent evt = ASYNC_EVENT(enumtype);
+ evt.u.user.type = type;
+ evt.u.user.id = objid;
+ evt.u.user.param = param;
+ strcpy(evt.u.user.msg, msg);
+ if(ll_ringbuffer_write(context->AsyncEvents, (const char*)&evt, 1) == 1)
+ alsem_post(&context->EventSem);
+}
+
+
+static inline ALfloat Sample_ALubyte(ALubyte val)
+{ return (val-128) * (1.0f/128.0f); }
+
+static inline ALfloat Sample_ALshort(ALshort val)
+{ return val * (1.0f/32768.0f); }
+
+static inline ALfloat Sample_ALfloat(ALfloat val)
+{ return val; }
+
+static inline ALfloat Sample_ALdouble(ALdouble val)
+{ return (ALfloat)val; }
+
+typedef ALubyte ALmulaw;
+static inline ALfloat Sample_ALmulaw(ALmulaw val)
+{ return muLawDecompressionTable[val] * (1.0f/32768.0f); }
+
+typedef ALubyte ALalaw;
+static inline ALfloat Sample_ALalaw(ALalaw val)
+{ return aLawDecompressionTable[val] * (1.0f/32768.0f); }
+
+#define DECL_TEMPLATE(T) \
+static inline void Load_##T(ALfloat *restrict dst, const T *restrict src, \
+ ALint srcstep, ALsizei samples) \
+{ \
+ ALsizei i; \
+ for(i = 0;i < samples;i++) \
+ dst[i] += Sample_##T(src[i*srcstep]); \
+}
+
+DECL_TEMPLATE(ALubyte)
+DECL_TEMPLATE(ALshort)
+DECL_TEMPLATE(ALfloat)
+DECL_TEMPLATE(ALdouble)
+DECL_TEMPLATE(ALmulaw)
+DECL_TEMPLATE(ALalaw)
+
+#undef DECL_TEMPLATE
+
+static void LoadSamples(ALfloat *restrict dst, const ALvoid *restrict src, ALint srcstep,
+ enum FmtType srctype, ALsizei samples)
+{
+#define HANDLE_FMT(ET, ST) case ET: Load_##ST(dst, src, srcstep, samples); break
+ switch(srctype)
+ {
+ HANDLE_FMT(FmtUByte, ALubyte);
+ HANDLE_FMT(FmtShort, ALshort);
+ HANDLE_FMT(FmtFloat, ALfloat);
+ HANDLE_FMT(FmtDouble, ALdouble);
+ HANDLE_FMT(FmtMulaw, ALmulaw);
+ HANDLE_FMT(FmtAlaw, ALalaw);
+ }
+#undef HANDLE_FMT
+}
+
+
+static const ALfloat *DoFilters(BiquadFilter *lpfilter, BiquadFilter *hpfilter,
+ ALfloat *restrict dst, const ALfloat *restrict src,
+ ALsizei numsamples, enum ActiveFilters type)
+{
+ ALsizei i;
+ switch(type)
+ {
+ case AF_None:
+ BiquadFilter_passthru(lpfilter, numsamples);
+ BiquadFilter_passthru(hpfilter, numsamples);
+ break;
+
+ case AF_LowPass:
+ BiquadFilter_process(lpfilter, dst, src, numsamples);
+ BiquadFilter_passthru(hpfilter, numsamples);
+ return dst;
+ case AF_HighPass:
+ BiquadFilter_passthru(lpfilter, numsamples);
+ BiquadFilter_process(hpfilter, dst, src, numsamples);
+ return dst;
+
+ case AF_BandPass:
+ for(i = 0;i < numsamples;)
+ {
+ ALfloat temp[256];
+ ALsizei todo = mini(256, numsamples-i);
+
+ BiquadFilter_process(lpfilter, temp, src+i, todo);
+ BiquadFilter_process(hpfilter, dst+i, temp, todo);
+ i += todo;
+ }
+ return dst;
+ }
+ return src;
+}
+
+
+/* This function uses these device temp buffers. */
+#define SOURCE_DATA_BUF 0
+#define RESAMPLED_BUF 1
+#define FILTERED_BUF 2
+#define NFC_DATA_BUF 3
+ALboolean MixSource(ALvoice *voice, ALuint SourceID, ALCcontext *Context, ALsizei SamplesToDo)
+{
+ ALCdevice *Device = Context->Device;
+ ALbufferlistitem *BufferListItem;
+ ALbufferlistitem *BufferLoopItem;
+ ALsizei NumChannels, SampleSize;
+ ALbitfieldSOFT enabledevt;
+ ALsizei buffers_done = 0;
+ ResamplerFunc Resample;
+ ALsizei DataPosInt;
+ ALsizei DataPosFrac;
+ ALint64 DataSize64;
+ ALint increment;
+ ALsizei Counter;
+ ALsizei OutPos;
+ ALsizei IrSize;
+ bool isplaying;
+ bool firstpass;
+ bool isstatic;
+ ALsizei chan;
+ ALsizei send;
+
+ /* Get source info */
+ isplaying = true; /* Will only be called while playing. */
+ isstatic = !!(voice->Flags&VOICE_IS_STATIC);
+ DataPosInt = ATOMIC_LOAD(&voice->position, almemory_order_acquire);
+ DataPosFrac = ATOMIC_LOAD(&voice->position_fraction, almemory_order_relaxed);
+ BufferListItem = ATOMIC_LOAD(&voice->current_buffer, almemory_order_relaxed);
+ BufferLoopItem = ATOMIC_LOAD(&voice->loop_buffer, almemory_order_relaxed);
+ NumChannels = voice->NumChannels;
+ SampleSize = voice->SampleSize;
+ increment = voice->Step;
+
+ IrSize = (Device->HrtfHandle ? Device->HrtfHandle->irSize : 0);
+
+ Resample = ((increment == FRACTIONONE && DataPosFrac == 0) ?
+ Resample_copy_C : voice->Resampler);
+
+ Counter = (voice->Flags&VOICE_IS_FADING) ? SamplesToDo : 0;
+ firstpass = true;
+ OutPos = 0;
+
+ do {
+ ALsizei SrcBufferSize, DstBufferSize;
+
+ /* Figure out how many buffer samples will be needed */
+ DataSize64 = SamplesToDo-OutPos;
+ DataSize64 *= increment;
+ DataSize64 += DataPosFrac+FRACTIONMASK;
+ DataSize64 >>= FRACTIONBITS;
+ DataSize64 += MAX_RESAMPLE_PADDING*2;
+ SrcBufferSize = (ALsizei)mini64(DataSize64, BUFFERSIZE);
+
+ /* Figure out how many samples we can actually mix from this. */
+ DataSize64 = SrcBufferSize;
+ DataSize64 -= MAX_RESAMPLE_PADDING*2;
+ DataSize64 <<= FRACTIONBITS;
+ DataSize64 -= DataPosFrac;
+ DstBufferSize = (ALsizei)mini64((DataSize64+(increment-1)) / increment,
+ SamplesToDo - OutPos);
+
+ /* Some mixers like having a multiple of 4, so try to give that unless
+ * this is the last update. */
+ if(DstBufferSize < SamplesToDo-OutPos)
+ DstBufferSize &= ~3;
+
+ /* It's impossible to have a buffer list item with no entries. */
+ assert(BufferListItem->num_buffers > 0);
+
+ for(chan = 0;chan < NumChannels;chan++)
+ {
+ const ALfloat *ResampledData;
+ ALfloat *SrcData = Device->TempBuffer[SOURCE_DATA_BUF];
+ ALsizei FilledAmt;
+
+ /* Load the previous samples into the source data first, and clear the rest. */
+ memcpy(SrcData, voice->PrevSamples[chan], MAX_RESAMPLE_PADDING*sizeof(ALfloat));
+ memset(SrcData+MAX_RESAMPLE_PADDING, 0, (BUFFERSIZE-MAX_RESAMPLE_PADDING)*
+ sizeof(ALfloat));
+ FilledAmt = MAX_RESAMPLE_PADDING;
+
+ if(isstatic)
+ {
+ /* TODO: For static sources, loop points are taken from the
+ * first buffer (should be adjusted by any buffer offset, to
+ * possibly be added later).
+ */
+ const ALbuffer *Buffer0 = BufferListItem->buffers[0];
+ const ALsizei LoopStart = Buffer0->LoopStart;
+ const ALsizei LoopEnd = Buffer0->LoopEnd;
+ const ALsizei LoopSize = LoopEnd - LoopStart;
+
+ /* If current pos is beyond the loop range, do not loop */
+ if(!BufferLoopItem || DataPosInt >= LoopEnd)
+ {
+ ALsizei SizeToDo = SrcBufferSize - FilledAmt;
+ ALsizei CompLen = 0;
+ ALsizei i;
+
+ BufferLoopItem = NULL;
+
+ for(i = 0;i < BufferListItem->num_buffers;i++)
+ {
+ const ALbuffer *buffer = BufferListItem->buffers[i];
+ const ALubyte *Data = buffer->data;
+ ALsizei DataSize;
+
+ if(DataPosInt >= buffer->SampleLen)
+ continue;
+
+ /* Load what's left to play from the buffer */
+ DataSize = mini(SizeToDo, buffer->SampleLen - DataPosInt);
+ CompLen = maxi(CompLen, DataSize);
+
+ LoadSamples(&SrcData[FilledAmt],
+ &Data[(DataPosInt*NumChannels + chan)*SampleSize],
+ NumChannels, buffer->FmtType, DataSize
+ );
+ }
+ FilledAmt += CompLen;
+ }
+ else
+ {
+ ALsizei SizeToDo = mini(SrcBufferSize - FilledAmt, LoopEnd - DataPosInt);
+ ALsizei CompLen = 0;
+ ALsizei i;
+
+ for(i = 0;i < BufferListItem->num_buffers;i++)
+ {
+ const ALbuffer *buffer = BufferListItem->buffers[i];
+ const ALubyte *Data = buffer->data;
+ ALsizei DataSize;
+
+ if(DataPosInt >= buffer->SampleLen)
+ continue;
+
+ /* Load what's left of this loop iteration */
+ DataSize = mini(SizeToDo, buffer->SampleLen - DataPosInt);
+ CompLen = maxi(CompLen, DataSize);
+
+ LoadSamples(&SrcData[FilledAmt],
+ &Data[(DataPosInt*NumChannels + chan)*SampleSize],
+ NumChannels, buffer->FmtType, DataSize
+ );
+ }
+ FilledAmt += CompLen;
+
+ while(SrcBufferSize > FilledAmt)
+ {
+ const ALsizei SizeToDo = mini(SrcBufferSize - FilledAmt, LoopSize);
+
+ CompLen = 0;
+ for(i = 0;i < BufferListItem->num_buffers;i++)
+ {
+ const ALbuffer *buffer = BufferListItem->buffers[i];
+ const ALubyte *Data = buffer->data;
+ ALsizei DataSize;
+
+ if(LoopStart >= buffer->SampleLen)
+ continue;
+
+ DataSize = mini(SizeToDo, buffer->SampleLen - LoopStart);
+ CompLen = maxi(CompLen, DataSize);
+
+ LoadSamples(&SrcData[FilledAmt],
+ &Data[(LoopStart*NumChannels + chan)*SampleSize],
+ NumChannels, buffer->FmtType, DataSize
+ );
+ }
+ FilledAmt += CompLen;
+ }
+ }
+ }
+ else
+ {
+ /* Crawl the buffer queue to fill in the temp buffer */
+ ALbufferlistitem *tmpiter = BufferListItem;
+ ALsizei pos = DataPosInt;
+
+ while(tmpiter && SrcBufferSize > FilledAmt)
+ {
+ ALsizei SizeToDo = SrcBufferSize - FilledAmt;
+ ALsizei CompLen = 0;
+ ALsizei i;
+
+ for(i = 0;i < tmpiter->num_buffers;i++)
+ {
+ const ALbuffer *ALBuffer = tmpiter->buffers[i];
+ ALsizei DataSize = ALBuffer ? ALBuffer->SampleLen : 0;
+
+ if(DataSize > pos)
+ {
+ const ALubyte *Data = ALBuffer->data;
+ Data += (pos*NumChannels + chan)*SampleSize;
+
+ DataSize = mini(SizeToDo, DataSize - pos);
+ CompLen = maxi(CompLen, DataSize);
+
+ LoadSamples(&SrcData[FilledAmt], Data, NumChannels,
+ ALBuffer->FmtType, DataSize);
+ }
+ }
+ if(UNLIKELY(!CompLen))
+ pos -= tmpiter->max_samples;
+ else
+ {
+ FilledAmt += CompLen;
+ if(SrcBufferSize <= FilledAmt)
+ break;
+ pos = 0;
+ }
+ tmpiter = ATOMIC_LOAD(&tmpiter->next, almemory_order_acquire);
+ if(!tmpiter) tmpiter = BufferLoopItem;
+ }
+ }
+
+ /* Store the last source samples used for next time. */
+ memcpy(voice->PrevSamples[chan],
+ &SrcData[(increment*DstBufferSize + DataPosFrac)>>FRACTIONBITS],
+ MAX_RESAMPLE_PADDING*sizeof(ALfloat)
+ );
+
+ /* Now resample, then filter and mix to the appropriate outputs. */
+ ResampledData = Resample(&voice->ResampleState,
+ &SrcData[MAX_RESAMPLE_PADDING], DataPosFrac, increment,
+ Device->TempBuffer[RESAMPLED_BUF], DstBufferSize
+ );
+ {
+ DirectParams *parms = &voice->Direct.Params[chan];
+ const ALfloat *samples;
+
+ samples = DoFilters(
+ &parms->LowPass, &parms->HighPass, Device->TempBuffer[FILTERED_BUF],
+ ResampledData, DstBufferSize, voice->Direct.FilterType
+ );
+ if(!(voice->Flags&VOICE_HAS_HRTF))
+ {
+ if(!Counter)
+ memcpy(parms->Gains.Current, parms->Gains.Target,
+ sizeof(parms->Gains.Current));
+ if(!(voice->Flags&VOICE_HAS_NFC))
+ MixSamples(samples, voice->Direct.Channels, voice->Direct.Buffer,
+ parms->Gains.Current, parms->Gains.Target, Counter, OutPos,
+ DstBufferSize
+ );
+ else
+ {
+ ALfloat *nfcsamples = Device->TempBuffer[NFC_DATA_BUF];
+ ALsizei chanoffset = 0;
+
+ MixSamples(samples,
+ voice->Direct.ChannelsPerOrder[0], voice->Direct.Buffer,
+ parms->Gains.Current, parms->Gains.Target, Counter, OutPos,
+ DstBufferSize
+ );
+ chanoffset += voice->Direct.ChannelsPerOrder[0];
+#define APPLY_NFC_MIX(order) \
+ if(voice->Direct.ChannelsPerOrder[order] > 0) \
+ { \
+ NfcFilterProcess##order(&parms->NFCtrlFilter, nfcsamples, samples, \
+ DstBufferSize); \
+ MixSamples(nfcsamples, voice->Direct.ChannelsPerOrder[order], \
+ voice->Direct.Buffer+chanoffset, parms->Gains.Current+chanoffset, \
+ parms->Gains.Target+chanoffset, Counter, OutPos, DstBufferSize \
+ ); \
+ chanoffset += voice->Direct.ChannelsPerOrder[order]; \
+ }
+ APPLY_NFC_MIX(1)
+ APPLY_NFC_MIX(2)
+ APPLY_NFC_MIX(3)
+#undef APPLY_NFC_MIX
+ }
+ }
+ else
+ {
+ MixHrtfParams hrtfparams;
+ ALsizei fademix = 0;
+ int lidx, ridx;
+
+ lidx = GetChannelIdxByName(&Device->RealOut, FrontLeft);
+ ridx = GetChannelIdxByName(&Device->RealOut, FrontRight);
+ assert(lidx != -1 && ridx != -1);
+
+ if(!Counter)
+ {
+ /* No fading, just overwrite the old HRTF params. */
+ parms->Hrtf.Old = parms->Hrtf.Target;
+ }
+ else if(!(parms->Hrtf.Old.Gain > GAIN_SILENCE_THRESHOLD))
+ {
+ /* The old HRTF params are silent, so overwrite the old
+ * coefficients with the new, and reset the old gain to
+ * 0. The future mix will then fade from silence.
+ */
+ parms->Hrtf.Old = parms->Hrtf.Target;
+ parms->Hrtf.Old.Gain = 0.0f;
+ }
+ else if(firstpass)
+ {
+ ALfloat gain;
+
+ /* Fade between the coefficients over 128 samples. */
+ fademix = mini(DstBufferSize, 128);
+
+ /* The new coefficients need to fade in completely
+ * since they're replacing the old ones. To keep the
+ * gain fading consistent, interpolate between the old
+ * and new target gains given how much of the fade time
+ * this mix handles.
+ */
+ gain = lerp(parms->Hrtf.Old.Gain, parms->Hrtf.Target.Gain,
+ minf(1.0f, (ALfloat)fademix/Counter));
+ hrtfparams.Coeffs = parms->Hrtf.Target.Coeffs;
+ hrtfparams.Delay[0] = parms->Hrtf.Target.Delay[0];
+ hrtfparams.Delay[1] = parms->Hrtf.Target.Delay[1];
+ hrtfparams.Gain = 0.0f;
+ hrtfparams.GainStep = gain / (ALfloat)fademix;
+
+ MixHrtfBlendSamples(
+ voice->Direct.Buffer[lidx], voice->Direct.Buffer[ridx],
+ samples, voice->Offset, OutPos, IrSize, &parms->Hrtf.Old,
+ &hrtfparams, &parms->Hrtf.State, fademix
+ );
+ /* Update the old parameters with the result. */
+ parms->Hrtf.Old = parms->Hrtf.Target;
+ if(fademix < Counter)
+ parms->Hrtf.Old.Gain = hrtfparams.Gain;
+ }
+
+ if(fademix < DstBufferSize)
+ {
+ ALsizei todo = DstBufferSize - fademix;
+ ALfloat gain = parms->Hrtf.Target.Gain;
+
+ /* Interpolate the target gain if the gain fading lasts
+ * longer than this mix.
+ */
+ if(Counter > DstBufferSize)
+ gain = lerp(parms->Hrtf.Old.Gain, gain,
+ (ALfloat)todo/(Counter-fademix));
+
+ hrtfparams.Coeffs = parms->Hrtf.Target.Coeffs;
+ hrtfparams.Delay[0] = parms->Hrtf.Target.Delay[0];
+ hrtfparams.Delay[1] = parms->Hrtf.Target.Delay[1];
+ hrtfparams.Gain = parms->Hrtf.Old.Gain;
+ hrtfparams.GainStep = (gain - parms->Hrtf.Old.Gain) / (ALfloat)todo;
+ MixHrtfSamples(
+ voice->Direct.Buffer[lidx], voice->Direct.Buffer[ridx],
+ samples+fademix, voice->Offset+fademix, OutPos+fademix, IrSize,
+ &hrtfparams, &parms->Hrtf.State, todo
+ );
+ /* Store the interpolated gain or the final target gain
+ * depending if the fade is done.
+ */
+ if(DstBufferSize < Counter)
+ parms->Hrtf.Old.Gain = gain;
+ else
+ parms->Hrtf.Old.Gain = parms->Hrtf.Target.Gain;
+ }
+ }
+ }
+
+ for(send = 0;send < Device->NumAuxSends;send++)
+ {
+ SendParams *parms = &voice->Send[send].Params[chan];
+ const ALfloat *samples;
+
+ if(!voice->Send[send].Buffer)
+ continue;
+
+ samples = DoFilters(
+ &parms->LowPass, &parms->HighPass, Device->TempBuffer[FILTERED_BUF],
+ ResampledData, DstBufferSize, voice->Send[send].FilterType
+ );
+
+ if(!Counter)
+ memcpy(parms->Gains.Current, parms->Gains.Target,
+ sizeof(parms->Gains.Current));
+ MixSamples(samples, voice->Send[send].Channels, voice->Send[send].Buffer,
+ parms->Gains.Current, parms->Gains.Target, Counter, OutPos, DstBufferSize
+ );
+ }
+ }
+ /* Update positions */
+ DataPosFrac += increment*DstBufferSize;
+ DataPosInt += DataPosFrac>>FRACTIONBITS;
+ DataPosFrac &= FRACTIONMASK;
+
+ OutPos += DstBufferSize;
+ voice->Offset += DstBufferSize;
+ Counter = maxi(DstBufferSize, Counter) - DstBufferSize;
+ firstpass = false;
+
+ if(isstatic)
+ {
+ if(BufferLoopItem)
+ {
+ /* Handle looping static source */
+ const ALbuffer *Buffer = BufferListItem->buffers[0];
+ ALsizei LoopStart = Buffer->LoopStart;
+ ALsizei LoopEnd = Buffer->LoopEnd;
+ if(DataPosInt >= LoopEnd)
+ {
+ assert(LoopEnd > LoopStart);
+ DataPosInt = ((DataPosInt-LoopStart)%(LoopEnd-LoopStart)) + LoopStart;
+ }
+ }
+ else
+ {
+ /* Handle non-looping static source */
+ if(DataPosInt >= BufferListItem->max_samples)
+ {
+ isplaying = false;
+ BufferListItem = NULL;
+ DataPosInt = 0;
+ DataPosFrac = 0;
+ break;
+ }
+ }
+ }
+ else while(1)
+ {
+ /* Handle streaming source */
+ if(BufferListItem->max_samples > DataPosInt)
+ break;
+
+ DataPosInt -= BufferListItem->max_samples;
+
+ buffers_done += BufferListItem->num_buffers;
+ BufferListItem = ATOMIC_LOAD(&BufferListItem->next, almemory_order_relaxed);
+ if(!BufferListItem && !(BufferListItem=BufferLoopItem))
+ {
+ isplaying = false;
+ DataPosInt = 0;
+ DataPosFrac = 0;
+ break;
+ }
+ }
+ } while(isplaying && OutPos < SamplesToDo);
+
+ voice->Flags |= VOICE_IS_FADING;
+
+ /* Update source info */
+ ATOMIC_STORE(&voice->position, DataPosInt, almemory_order_relaxed);
+ ATOMIC_STORE(&voice->position_fraction, DataPosFrac, almemory_order_relaxed);
+ ATOMIC_STORE(&voice->current_buffer, BufferListItem, almemory_order_release);
+
+ /* Send any events now, after the position/buffer info was updated. */
+ enabledevt = ATOMIC_LOAD(&Context->EnabledEvts, almemory_order_acquire);
+ if(buffers_done > 0 && (enabledevt&EventType_BufferCompleted))
+ SendAsyncEvent(Context, EventType_BufferCompleted,
+ AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT, SourceID, buffers_done, "Buffer completed"
+ );
+
+ return isplaying;
+}
diff --git a/Alc/panning.c b/Alc/panning.c
index 6305bff7..2c0f3bf2 100644
--- a/Alc/panning.c
+++ b/Alc/panning.c
@@ -27,19 +27,24 @@
#include <assert.h>
#include "alMain.h"
-#include "AL/al.h"
-#include "AL/alc.h"
+#include "alAuxEffectSlot.h"
#include "alu.h"
+#include "alconfig.h"
#include "bool.h"
+#include "ambdec.h"
+#include "bformatdec.h"
+#include "filters/splitter.h"
+#include "uhjfilter.h"
+#include "bs2b.h"
-#define ZERO_ORDER_SCALE 0.0f
-#define FIRST_ORDER_SCALE 1.0f
-#define SECOND_ORDER_SCALE (1.0f / 1.22474f)
-#define THIRD_ORDER_SCALE (1.0f / 1.30657f)
+extern inline void CalcDirectionCoeffs(const ALfloat dir[3], ALfloat spread, ALfloat coeffs[MAX_AMBI_COEFFS]);
+extern inline void CalcAngleCoeffs(ALfloat azimuth, ALfloat elevation, ALfloat spread, ALfloat coeffs[MAX_AMBI_COEFFS]);
+extern inline float ScaleAzimuthFront(float azimuth, float scale);
+extern inline void ComputePanGains(const MixParams *dry, const ALfloat*restrict coeffs, ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]);
-static const ALuint FuMa2ACN[MAX_AMBI_COEFFS] = {
+static const ALsizei FuMa2ACN[MAX_AMBI_COEFFS] = {
0, /* W */
3, /* X */
1, /* Y */
@@ -57,71 +62,21 @@ static const ALuint FuMa2ACN[MAX_AMBI_COEFFS] = {
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) */
+static const ALsizei ACN2ACN[MAX_AMBI_COEFFS] = {
+ 0, 1, 2, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, 13, 14, 15
};
-void ComputeAmbientGains(const ALCdevice *device, ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS])
-{
- ALuint i;
-
- for(i = 0;i < device->NumChannels;i++)
- {
- // The W coefficients are based on a mathematical average of the
- // output. The square root of the base average provides for a more
- // perceptual average volume, better suited to non-directional gains.
- gains[i] = sqrtf(device->AmbiCoeffs[i][0]) * ingain;
- }
- for(;i < MAX_OUTPUT_CHANNELS;i++)
- gains[i] = 0.0f;
-}
-
-void ComputeAngleGains(const ALCdevice *device, ALfloat angle, ALfloat elevation, ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS])
+void CalcAmbiCoeffs(const ALfloat y, const ALfloat z, const ALfloat x, const ALfloat spread,
+ ALfloat coeffs[MAX_AMBI_COEFFS])
{
- 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 */
+ coeffs[1] = SQRTF_3 * y; /* ACN 1 = sqrt(3) * Y */
+ coeffs[2] = SQRTF_3 * z; /* ACN 2 = sqrt(3) * Z */
+ coeffs[3] = SQRTF_3 * x; /* ACN 3 = sqrt(3) * X */
/* Second-order */
coeffs[4] = 3.872983346f * x * y; /* ACN 4 = sqrt(15) * X * Y */
coeffs[5] = 3.872983346f * y * z; /* ACN 5 = sqrt(15) * Y * Z */
@@ -137,34 +92,92 @@ void ComputeDirectionalGains(const ALCdevice *device, const ALfloat dir[3], ALfl
coeffs[14] = 5.123475383f * z * (x*x - y*y); /* ACN 14 = sqrt(105)/2 * Z * (X*X - Y*Y) */
coeffs[15] = 2.091650066f * x * (x*x - 3.0f*y*y); /* ACN 15 = sqrt(35/8) * X * (X*X - 3*Y*Y) */
- for(i = 0;i < device->NumChannels;i++)
+ if(spread > 0.0f)
{
- float gain = 0.0f;
- for(j = 0;j < MAX_AMBI_COEFFS;j++)
- gain += device->AmbiCoeffs[i][j]*coeffs[j];
- gains[i] = gain * ingain;
+ /* Implement the spread by using a spherical source that subtends the
+ * angle spread. See:
+ * http://www.ppsloan.org/publications/StupidSH36.pdf - Appendix A3
+ *
+ * When adjusted for N3D normalization instead of SN3D, these
+ * calculations are:
+ *
+ * ZH0 = -sqrt(pi) * (-1+ca);
+ * ZH1 = 0.5*sqrt(pi) * sa*sa;
+ * ZH2 = -0.5*sqrt(pi) * ca*(-1+ca)*(ca+1);
+ * ZH3 = -0.125*sqrt(pi) * (-1+ca)*(ca+1)*(5*ca*ca - 1);
+ * ZH4 = -0.125*sqrt(pi) * ca*(-1+ca)*(ca+1)*(7*ca*ca - 3);
+ * ZH5 = -0.0625*sqrt(pi) * (-1+ca)*(ca+1)*(21*ca*ca*ca*ca - 14*ca*ca + 1);
+ *
+ * The gain of the source is compensated for size, so that the
+ * loundness doesn't depend on the spread. Thus:
+ *
+ * ZH0 = 1.0f;
+ * ZH1 = 0.5f * (ca+1.0f);
+ * ZH2 = 0.5f * (ca+1.0f)*ca;
+ * ZH3 = 0.125f * (ca+1.0f)*(5.0f*ca*ca - 1.0f);
+ * ZH4 = 0.125f * (ca+1.0f)*(7.0f*ca*ca - 3.0f)*ca;
+ * ZH5 = 0.0625f * (ca+1.0f)*(21.0f*ca*ca*ca*ca - 14.0f*ca*ca + 1.0f);
+ */
+ ALfloat ca = cosf(spread * 0.5f);
+ /* Increase the source volume by up to +3dB for a full spread. */
+ ALfloat scale = sqrtf(1.0f + spread/F_TAU);
+
+ ALfloat ZH0_norm = scale;
+ ALfloat ZH1_norm = 0.5f * (ca+1.f) * scale;
+ ALfloat ZH2_norm = 0.5f * (ca+1.f)*ca * scale;
+ ALfloat ZH3_norm = 0.125f * (ca+1.f)*(5.f*ca*ca-1.f) * scale;
+
+ /* Zeroth-order */
+ coeffs[0] *= ZH0_norm;
+ /* First-order */
+ coeffs[1] *= ZH1_norm;
+ coeffs[2] *= ZH1_norm;
+ coeffs[3] *= ZH1_norm;
+ /* Second-order */
+ coeffs[4] *= ZH2_norm;
+ coeffs[5] *= ZH2_norm;
+ coeffs[6] *= ZH2_norm;
+ coeffs[7] *= ZH2_norm;
+ coeffs[8] *= ZH2_norm;
+ /* Third-order */
+ coeffs[9] *= ZH3_norm;
+ coeffs[10] *= ZH3_norm;
+ coeffs[11] *= ZH3_norm;
+ coeffs[12] *= ZH3_norm;
+ coeffs[13] *= ZH3_norm;
+ coeffs[14] *= ZH3_norm;
+ coeffs[15] *= ZH3_norm;
}
- for(;i < MAX_OUTPUT_CHANNELS;i++)
- gains[i] = 0.0f;
}
-void ComputeBFormatGains(const ALCdevice *device, const ALfloat mtx[4], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS])
+
+void ComputePanningGainsMC(const ChannelConfig *chancoeffs, ALsizei numchans, ALsizei numcoeffs, const ALfloat*restrict coeffs, ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS])
{
- ALuint i, j;
+ ALsizei i, j;
- for(i = 0;i < device->NumChannels;i++)
+ for(i = 0;i < numchans;i++)
{
float gain = 0.0f;
- for(j = 0;j < 4;j++)
- gain += device->AmbiCoeffs[i][j] * mtx[j];
- gains[i] = gain * ingain;
+ for(j = 0;j < numcoeffs;j++)
+ gain += chancoeffs[i][j]*coeffs[j];
+ gains[i] = clampf(gain, 0.0f, 1.0f) * ingain;
}
for(;i < MAX_OUTPUT_CHANNELS;i++)
gains[i] = 0.0f;
}
+void ComputePanningGainsBF(const BFChannelConfig *chanmap, ALsizei numchans, const ALfloat*restrict coeffs, ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS])
+{
+ ALsizei i;
+
+ for(i = 0;i < numchans;i++)
+ gains[i] = chanmap[i].Scale * coeffs[chanmap[i].Index] * ingain;
+ for(;i < MAX_OUTPUT_CHANNELS;i++)
+ gains[i] = 0.0f;
+}
+
-DECL_CONST static inline const char *GetLabelFromChannel(enum Channel channel)
+static inline const char *GetLabelFromChannel(enum Channel channel)
{
switch(channel)
{
@@ -178,10 +191,31 @@ DECL_CONST static inline const char *GetLabelFromChannel(enum Channel channel)
case SideLeft: return "side-left";
case SideRight: return "side-right";
- case BFormatW: return "bformat-w";
- case BFormatX: return "bformat-x";
- case BFormatY: return "bformat-y";
- case BFormatZ: return "bformat-z";
+ case UpperFrontLeft: return "upper-front-left";
+ case UpperFrontRight: return "upper-front-right";
+ case UpperBackLeft: return "upper-back-left";
+ case UpperBackRight: return "upper-back-right";
+ case LowerFrontLeft: return "lower-front-left";
+ case LowerFrontRight: return "lower-front-right";
+ case LowerBackLeft: return "lower-back-left";
+ case LowerBackRight: return "lower-back-right";
+
+ case Aux0: return "aux-0";
+ case Aux1: return "aux-1";
+ case Aux2: return "aux-2";
+ case Aux3: return "aux-3";
+ case Aux4: return "aux-4";
+ case Aux5: return "aux-5";
+ case Aux6: return "aux-6";
+ case Aux7: return "aux-7";
+ case Aux8: return "aux-8";
+ case Aux9: return "aux-9";
+ case Aux10: return "aux-10";
+ case Aux11: return "aux-11";
+ case Aux12: return "aux-12";
+ case Aux13: return "aux-13";
+ case Aux14: return "aux-14";
+ case Aux15: return "aux-15";
case InvalidChannel: break;
}
@@ -194,364 +228,1012 @@ typedef struct ChannelMap {
ChannelConfig Config;
} ChannelMap;
-static void SetChannelMap(ALCdevice *device, const ChannelMap *chanmap, size_t count, ALfloat ambiscale, ALboolean isfuma)
+static void SetChannelMap(const enum Channel devchans[MAX_OUTPUT_CHANNELS],
+ ChannelConfig *ambicoeffs, const ChannelMap *chanmap,
+ ALsizei count, ALsizei *outcount)
{
- size_t j, k;
- ALuint i;
+ ALsizei maxchans = 0;
+ ALsizei i, j;
- device->AmbiScale = ambiscale;
- for(i = 0;i < MAX_OUTPUT_CHANNELS && device->ChannelName[i] != InvalidChannel;i++)
+ for(i = 0;i < count;i++)
{
- if(device->ChannelName[i] == LFE)
+ ALint idx = GetChannelIndex(devchans, chanmap[i].ChanName);
+ if(idx < 0)
{
- for(j = 0;j < MAX_AMBI_COEFFS;j++)
- device->AmbiCoeffs[i][j] = 0.0f;
+ ERR("Failed to find %s channel in device\n",
+ GetLabelFromChannel(chanmap[i].ChanName));
continue;
}
- for(j = 0;j < count;j++)
+ maxchans = maxi(maxchans, idx+1);
+ for(j = 0;j < MAX_AMBI_COEFFS;j++)
+ ambicoeffs[idx][j] = chanmap[i].Config[j];
+ }
+ *outcount = mini(maxchans, MAX_OUTPUT_CHANNELS);
+}
+
+static bool MakeSpeakerMap(ALCdevice *device, const AmbDecConf *conf, ALsizei speakermap[MAX_OUTPUT_CHANNELS])
+{
+ ALsizei i;
+
+ for(i = 0;i < conf->NumSpeakers;i++)
+ {
+ enum Channel ch;
+ int chidx = -1;
+
+ /* NOTE: AmbDec does not define any standard speaker names, however
+ * for this to work we have to by able to find the output channel
+ * the speaker definition corresponds to. Therefore, OpenAL Soft
+ * requires these channel labels to be recognized:
+ *
+ * LF = Front left
+ * RF = Front right
+ * LS = Side left
+ * RS = Side right
+ * LB = Back left
+ * RB = Back right
+ * CE = Front center
+ * CB = Back center
+ *
+ * Additionally, surround51 will acknowledge back speakers for side
+ * channels, and surround51rear will acknowledge side speakers for
+ * back channels, to avoid issues with an ambdec expecting 5.1 to
+ * use the side channels when the device is configured for back,
+ * and vice-versa.
+ */
+ if(alstr_cmp_cstr(conf->Speakers[i].Name, "LF") == 0)
+ ch = FrontLeft;
+ else if(alstr_cmp_cstr(conf->Speakers[i].Name, "RF") == 0)
+ ch = FrontRight;
+ else if(alstr_cmp_cstr(conf->Speakers[i].Name, "CE") == 0)
+ ch = FrontCenter;
+ else if(alstr_cmp_cstr(conf->Speakers[i].Name, "LS") == 0)
+ {
+ if(device->FmtChans == DevFmtX51Rear)
+ ch = BackLeft;
+ else
+ ch = SideLeft;
+ }
+ else if(alstr_cmp_cstr(conf->Speakers[i].Name, "RS") == 0)
+ {
+ if(device->FmtChans == DevFmtX51Rear)
+ ch = BackRight;
+ else
+ ch = SideRight;
+ }
+ else if(alstr_cmp_cstr(conf->Speakers[i].Name, "LB") == 0)
+ {
+ if(device->FmtChans == DevFmtX51)
+ ch = SideLeft;
+ else
+ ch = BackLeft;
+ }
+ else if(alstr_cmp_cstr(conf->Speakers[i].Name, "RB") == 0)
+ {
+ if(device->FmtChans == DevFmtX51)
+ ch = SideRight;
+ else
+ ch = BackRight;
+ }
+ else if(alstr_cmp_cstr(conf->Speakers[i].Name, "CB") == 0)
+ ch = BackCenter;
+ else
{
- if(device->ChannelName[i] == chanmap[j].ChanName)
+ const char *name = alstr_get_cstr(conf->Speakers[i].Name);
+ unsigned int n;
+ char c;
+
+ if(sscanf(name, "AUX%u%c", &n, &c) == 1 && n < 16)
+ ch = Aux0+n;
+ else
{
- if(isfuma)
- {
- /* Reformat FuMa -> ACN/N3D */
- for(k = 0;k < MAX_AMBI_COEFFS;++k)
- {
- ALuint acn = FuMa2ACN[k];
- device->AmbiCoeffs[i][acn] = chanmap[j].Config[k] / FuMa2N3DScale[acn];
- }
- }
- else
- {
- for(k = 0;k < MAX_AMBI_COEFFS;++k)
- device->AmbiCoeffs[i][k] = chanmap[j].Config[k];
- }
- break;
+ ERR("AmbDec speaker label \"%s\" not recognized\n", name);
+ return false;
}
}
- if(j == count)
- ERR("Failed to match %s channel (%u) in config\n", GetLabelFromChannel(device->ChannelName[i]), i);
+ chidx = GetChannelIdxByName(&device->RealOut, ch);
+ if(chidx == -1)
+ {
+ ERR("Failed to lookup AmbDec speaker label %s\n",
+ alstr_get_cstr(conf->Speakers[i].Name));
+ return false;
+ }
+ speakermap[i] = chidx;
}
- device->NumChannels = i;
+
+ return true;
}
-static bool LoadChannelSetup(ALCdevice *device)
+
+static const ChannelMap MonoCfg[1] = {
+ { FrontCenter, { 1.0f } },
+}, StereoCfg[2] = {
+ { FrontLeft, { 5.00000000e-1f, 2.88675135e-1f, 0.0f, 5.52305643e-2f } },
+ { FrontRight, { 5.00000000e-1f, -2.88675135e-1f, 0.0f, 5.52305643e-2f } },
+}, QuadCfg[4] = {
+ { BackLeft, { 3.53553391e-1f, 2.04124145e-1f, 0.0f, -2.04124145e-1f } },
+ { FrontLeft, { 3.53553391e-1f, 2.04124145e-1f, 0.0f, 2.04124145e-1f } },
+ { FrontRight, { 3.53553391e-1f, -2.04124145e-1f, 0.0f, 2.04124145e-1f } },
+ { BackRight, { 3.53553391e-1f, -2.04124145e-1f, 0.0f, -2.04124145e-1f } },
+}, X51SideCfg[4] = {
+ { SideLeft, { 3.33000782e-1f, 1.89084803e-1f, 0.0f, -2.00042375e-1f, -2.12307769e-2f, 0.0f, 0.0f, 0.0f, -1.14579885e-2f } },
+ { FrontLeft, { 1.88542860e-1f, 1.27709292e-1f, 0.0f, 1.66295695e-1f, 7.30571517e-2f, 0.0f, 0.0f, 0.0f, 2.10901184e-2f } },
+ { FrontRight, { 1.88542860e-1f, -1.27709292e-1f, 0.0f, 1.66295695e-1f, -7.30571517e-2f, 0.0f, 0.0f, 0.0f, 2.10901184e-2f } },
+ { SideRight, { 3.33000782e-1f, -1.89084803e-1f, 0.0f, -2.00042375e-1f, 2.12307769e-2f, 0.0f, 0.0f, 0.0f, -1.14579885e-2f } },
+}, X51RearCfg[4] = {
+ { BackLeft, { 3.33000782e-1f, 1.89084803e-1f, 0.0f, -2.00042375e-1f, -2.12307769e-2f, 0.0f, 0.0f, 0.0f, -1.14579885e-2f } },
+ { FrontLeft, { 1.88542860e-1f, 1.27709292e-1f, 0.0f, 1.66295695e-1f, 7.30571517e-2f, 0.0f, 0.0f, 0.0f, 2.10901184e-2f } },
+ { FrontRight, { 1.88542860e-1f, -1.27709292e-1f, 0.0f, 1.66295695e-1f, -7.30571517e-2f, 0.0f, 0.0f, 0.0f, 2.10901184e-2f } },
+ { BackRight, { 3.33000782e-1f, -1.89084803e-1f, 0.0f, -2.00042375e-1f, 2.12307769e-2f, 0.0f, 0.0f, 0.0f, -1.14579885e-2f } },
+}, X61Cfg[6] = {
+ { SideLeft, { 2.04460341e-1f, 2.17177926e-1f, 0.0f, -4.39996780e-2f, -2.60790269e-2f, 0.0f, 0.0f, 0.0f, -6.87239792e-2f } },
+ { FrontLeft, { 1.58923161e-1f, 9.21772680e-2f, 0.0f, 1.59658796e-1f, 6.66278083e-2f, 0.0f, 0.0f, 0.0f, 3.84686854e-2f } },
+ { FrontRight, { 1.58923161e-1f, -9.21772680e-2f, 0.0f, 1.59658796e-1f, -6.66278083e-2f, 0.0f, 0.0f, 0.0f, 3.84686854e-2f } },
+ { SideRight, { 2.04460341e-1f, -2.17177926e-1f, 0.0f, -4.39996780e-2f, 2.60790269e-2f, 0.0f, 0.0f, 0.0f, -6.87239792e-2f } },
+ { BackCenter, { 2.50001688e-1f, 0.00000000e+0f, 0.0f, -2.50000094e-1f, 0.00000000e+0f, 0.0f, 0.0f, 0.0f, 6.05133395e-2f } },
+}, X71Cfg[6] = {
+ { BackLeft, { 2.04124145e-1f, 1.08880247e-1f, 0.0f, -1.88586120e-1f, -1.29099444e-1f, 0.0f, 0.0f, 0.0f, 7.45355993e-2f, 3.73460789e-2f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.00000000e+0f } },
+ { SideLeft, { 2.04124145e-1f, 2.17760495e-1f, 0.0f, 0.00000000e+0f, 0.00000000e+0f, 0.0f, 0.0f, 0.0f, -1.49071198e-1f, -3.73460789e-2f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.00000000e+0f } },
+ { FrontLeft, { 2.04124145e-1f, 1.08880247e-1f, 0.0f, 1.88586120e-1f, 1.29099444e-1f, 0.0f, 0.0f, 0.0f, 7.45355993e-2f, 3.73460789e-2f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.00000000e+0f } },
+ { FrontRight, { 2.04124145e-1f, -1.08880247e-1f, 0.0f, 1.88586120e-1f, -1.29099444e-1f, 0.0f, 0.0f, 0.0f, 7.45355993e-2f, -3.73460789e-2f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.00000000e+0f } },
+ { SideRight, { 2.04124145e-1f, -2.17760495e-1f, 0.0f, 0.00000000e+0f, 0.00000000e+0f, 0.0f, 0.0f, 0.0f, -1.49071198e-1f, 3.73460789e-2f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.00000000e+0f } },
+ { BackRight, { 2.04124145e-1f, -1.08880247e-1f, 0.0f, -1.88586120e-1f, 1.29099444e-1f, 0.0f, 0.0f, 0.0f, 7.45355993e-2f, -3.73460789e-2f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.00000000e+0f } },
+};
+
+static void InitNearFieldCtrl(ALCdevice *device, ALfloat ctrl_dist, ALsizei order,
+ const ALsizei *restrict chans_per_order)
{
- static const enum Channel mono_chans[1] = {
- FrontCenter
- }, stereo_chans[2] = {
- FrontLeft, FrontRight
- }, quad_chans[4] = {
- FrontLeft, FrontRight,
- BackLeft, BackRight
- }, surround51_chans[5] = {
- FrontLeft, FrontRight, FrontCenter,
- SideLeft, SideRight
- }, surround51rear_chans[5] = {
- FrontLeft, FrontRight, FrontCenter,
- BackLeft, BackRight
- }, surround61_chans[6] = {
- FrontLeft, FrontRight,
- FrontCenter, BackCenter,
- SideLeft, SideRight
- }, surround71_chans[7] = {
- FrontLeft, FrontRight, FrontCenter,
- BackLeft, BackRight,
- SideLeft, SideRight
- };
- ChannelMap chanmap[MAX_OUTPUT_CHANNELS];
- const enum Channel *channels = NULL;
- const char *layout = NULL;
- ALfloat ambiscale = 1.0f;
- size_t count = 0;
- int isfuma;
- int order;
- size_t i;
+ const char *devname = alstr_get_cstr(device->DeviceName);
+ ALsizei i;
+
+ if(GetConfigValueBool(devname, "decoder", "nfc", 1) && ctrl_dist > 0.0f)
+ {
+ /* NFC is only used when AvgSpeakerDist is greater than 0, and can only
+ * be used when rendering to an ambisonic buffer.
+ */
+ device->AvgSpeakerDist = minf(ctrl_dist, 10.0f);
+ TRACE("Using near-field reference distance: %.2f meters\n", device->AvgSpeakerDist);
+
+ for(i = 0;i < order+1;i++)
+ device->NumChannelsPerOrder[i] = chans_per_order[i];
+ for(;i < MAX_AMBI_ORDER+1;i++)
+ device->NumChannelsPerOrder[i] = 0;
+ }
+}
+
+static void InitDistanceComp(ALCdevice *device, const AmbDecConf *conf, const ALsizei speakermap[MAX_OUTPUT_CHANNELS])
+{
+ const char *devname = alstr_get_cstr(device->DeviceName);
+ ALfloat maxdist = 0.0f;
+ size_t total = 0;
+ ALsizei i;
+
+ for(i = 0;i < conf->NumSpeakers;i++)
+ maxdist = maxf(maxdist, conf->Speakers[i].Distance);
+
+ if(GetConfigValueBool(devname, "decoder", "distance-comp", 1) && maxdist > 0.0f)
+ {
+ ALfloat srate = (ALfloat)device->Frequency;
+ for(i = 0;i < conf->NumSpeakers;i++)
+ {
+ ALsizei chan = speakermap[i];
+ ALfloat delay;
+
+ /* Distance compensation only delays in steps of the sample rate.
+ * This is a bit less accurate since the delay time falls to the
+ * nearest sample time, but it's far simpler as it doesn't have to
+ * deal with phase offsets. This means at 48khz, for instance, the
+ * distance delay will be in steps of about 7 millimeters.
+ */
+ delay = floorf((maxdist-conf->Speakers[i].Distance) / SPEEDOFSOUNDMETRESPERSEC *
+ srate + 0.5f);
+ if(delay >= (ALfloat)MAX_DELAY_LENGTH)
+ ERR("Delay for speaker \"%s\" exceeds buffer length (%f >= %u)\n",
+ alstr_get_cstr(conf->Speakers[i].Name), delay, MAX_DELAY_LENGTH);
+
+ device->ChannelDelay[chan].Length = (ALsizei)clampf(
+ delay, 0.0f, (ALfloat)(MAX_DELAY_LENGTH-1)
+ );
+ device->ChannelDelay[chan].Gain = conf->Speakers[i].Distance / maxdist;
+ TRACE("Channel %u \"%s\" distance compensation: %d samples, %f gain\n", chan,
+ alstr_get_cstr(conf->Speakers[i].Name), device->ChannelDelay[chan].Length,
+ device->ChannelDelay[chan].Gain
+ );
+
+ /* Round up to the next 4th sample, so each channel buffer starts
+ * 16-byte aligned.
+ */
+ total += RoundUp(device->ChannelDelay[chan].Length, 4);
+ }
+ }
+
+ if(total > 0)
+ {
+ device->ChannelDelay[0].Buffer = al_calloc(16, total * sizeof(ALfloat));
+ for(i = 1;i < MAX_OUTPUT_CHANNELS;i++)
+ {
+ size_t len = RoundUp(device->ChannelDelay[i-1].Length, 4);
+ device->ChannelDelay[i].Buffer = device->ChannelDelay[i-1].Buffer + len;
+ }
+ }
+}
+
+static void InitPanning(ALCdevice *device)
+{
+ const ChannelMap *chanmap = NULL;
+ ALsizei coeffcount = 0;
+ ALsizei count = 0;
+ ALsizei i, j;
switch(device->FmtChans)
{
case DevFmtMono:
- layout = "mono";
- channels = mono_chans;
- count = COUNTOF(mono_chans);
+ count = COUNTOF(MonoCfg);
+ chanmap = MonoCfg;
+ coeffcount = 1;
break;
+
case DevFmtStereo:
- layout = "stereo";
- channels = stereo_chans;
- count = COUNTOF(stereo_chans);
+ count = COUNTOF(StereoCfg);
+ chanmap = StereoCfg;
+ coeffcount = 4;
break;
+
case DevFmtQuad:
- layout = "quad";
- channels = quad_chans;
- count = COUNTOF(quad_chans);
+ count = COUNTOF(QuadCfg);
+ chanmap = QuadCfg;
+ coeffcount = 4;
break;
+
case DevFmtX51:
- layout = "surround51";
- channels = surround51_chans;
- count = COUNTOF(surround51_chans);
+ count = COUNTOF(X51SideCfg);
+ chanmap = X51SideCfg;
+ coeffcount = 9;
break;
+
case DevFmtX51Rear:
- layout = "surround51rear";
- channels = surround51rear_chans;
- count = COUNTOF(surround51rear_chans);
+ count = COUNTOF(X51RearCfg);
+ chanmap = X51RearCfg;
+ coeffcount = 9;
break;
+
case DevFmtX61:
- layout = "surround61";
- channels = surround61_chans;
- count = COUNTOF(surround61_chans);
+ count = COUNTOF(X61Cfg);
+ chanmap = X61Cfg;
+ coeffcount = 9;
break;
+
case DevFmtX71:
- layout = "surround71";
- channels = surround71_chans;
- count = COUNTOF(surround71_chans);
+ count = COUNTOF(X71Cfg);
+ chanmap = X71Cfg;
+ coeffcount = 16;
break;
- case DevFmtBFormat3D:
+
+ case DevFmtAmbi3D:
break;
}
- if(!layout)
- return false;
- else
+ if(device->FmtChans == DevFmtAmbi3D)
{
- char name[32] = {0};
- const char *type;
- char eol;
-
- snprintf(name, sizeof(name), "%s/type", layout);
- if(!ConfigValueStr(al_string_get_cstr(device->DeviceName), "layouts", name, &type))
- return false;
+ const char *devname = alstr_get_cstr(device->DeviceName);
+ const ALsizei *acnmap = (device->AmbiLayout == AmbiLayout_FuMa) ? FuMa2ACN : ACN2ACN;
+ const ALfloat *n3dscale = (device->AmbiScale == AmbiNorm_FuMa) ? FuMa2N3DScale :
+ (device->AmbiScale == AmbiNorm_SN3D) ? SN3D2N3DScale :
+ /*(device->AmbiScale == AmbiNorm_N3D) ?*/ N3D2N3DScale;
+ ALfloat nfc_delay = 0.0f;
- if(sscanf(type, " %31[^: ] : %d%c", name, &order, &eol) != 2)
+ count = (device->AmbiOrder == 3) ? 16 :
+ (device->AmbiOrder == 2) ? 9 :
+ (device->AmbiOrder == 1) ? 4 : 1;
+ for(i = 0;i < count;i++)
{
- ERR("Invalid type value '%s' (expected name:order) for layout %s\n", type, layout);
- return false;
+ ALsizei acn = acnmap[i];
+ device->Dry.Ambi.Map[i].Scale = 1.0f/n3dscale[acn];
+ device->Dry.Ambi.Map[i].Index = acn;
}
+ device->Dry.CoeffCount = 0;
+ device->Dry.NumChannels = count;
- if(strcasecmp(name, "fuma") == 0)
- isfuma = 1;
- else if(strcasecmp(name, "n3d") == 0)
- isfuma = 0;
+ if(device->AmbiOrder < 2)
+ {
+ device->FOAOut.Ambi = device->Dry.Ambi;
+ device->FOAOut.CoeffCount = device->Dry.CoeffCount;
+ device->FOAOut.NumChannels = 0;
+ }
else
{
- ERR("Unhandled type name '%s' (expected FuMa or N3D) for layout %s\n", name, layout);
- return false;
+ ALfloat w_scale=1.0f, xyz_scale=1.0f;
+
+ /* FOA output is always ACN+N3D for higher-order ambisonic output.
+ * The upsampler expects this and will convert it for output.
+ */
+ memset(&device->FOAOut.Ambi, 0, sizeof(device->FOAOut.Ambi));
+ for(i = 0;i < 4;i++)
+ {
+ device->FOAOut.Ambi.Map[i].Scale = 1.0f;
+ device->FOAOut.Ambi.Map[i].Index = i;
+ }
+ device->FOAOut.CoeffCount = 0;
+ device->FOAOut.NumChannels = 4;
+
+ if(device->AmbiOrder >= 3)
+ {
+ w_scale = W_SCALE_3H3P;
+ xyz_scale = XYZ_SCALE_3H3P;
+ }
+ else
+ {
+ w_scale = W_SCALE_2H2P;
+ xyz_scale = XYZ_SCALE_2H2P;
+ }
+ ambiup_reset(device->AmbiUp, device, w_scale, xyz_scale);
}
- if(order == 3)
- ambiscale = THIRD_ORDER_SCALE;
- else if(order == 2)
- ambiscale = SECOND_ORDER_SCALE;
- else if(order == 1)
- ambiscale = FIRST_ORDER_SCALE;
- else if(order == 0)
- ambiscale = ZERO_ORDER_SCALE;
- else
+ if(ConfigValueFloat(devname, "decoder", "nfc-ref-delay", &nfc_delay) && nfc_delay > 0.0f)
{
- ERR("Unhandled type order %d (expected 0, 1, 2, or 3) for layout %s\n", order, layout);
- return false;
+ static const ALsizei chans_per_order[MAX_AMBI_ORDER+1] = {
+ 1, 3, 5, 7
+ };
+ nfc_delay = clampf(nfc_delay, 0.001f, 1000.0f);
+ InitNearFieldCtrl(device, nfc_delay * SPEEDOFSOUNDMETRESPERSEC,
+ device->AmbiOrder, chans_per_order);
}
}
+ else
+ {
+ ALfloat w_scale, xyz_scale;
- for(i = 0;i < count;i++)
+ SetChannelMap(device->RealOut.ChannelName, device->Dry.Ambi.Coeffs,
+ chanmap, count, &device->Dry.NumChannels);
+ device->Dry.CoeffCount = coeffcount;
+
+ w_scale = (device->Dry.CoeffCount > 9) ? W_SCALE_3H0P :
+ (device->Dry.CoeffCount > 4) ? W_SCALE_2H0P : 1.0f;
+ xyz_scale = (device->Dry.CoeffCount > 9) ? XYZ_SCALE_3H0P :
+ (device->Dry.CoeffCount > 4) ? XYZ_SCALE_2H0P : 1.0f;
+
+ memset(&device->FOAOut.Ambi, 0, sizeof(device->FOAOut.Ambi));
+ for(i = 0;i < device->Dry.NumChannels;i++)
+ {
+ device->FOAOut.Ambi.Coeffs[i][0] = device->Dry.Ambi.Coeffs[i][0] * w_scale;
+ for(j = 1;j < 4;j++)
+ device->FOAOut.Ambi.Coeffs[i][j] = device->Dry.Ambi.Coeffs[i][j] * xyz_scale;
+ }
+ device->FOAOut.CoeffCount = 4;
+ device->FOAOut.NumChannels = 0;
+ }
+ device->RealOut.NumChannels = 0;
+}
+
+static void InitCustomPanning(ALCdevice *device, const AmbDecConf *conf, const ALsizei speakermap[MAX_OUTPUT_CHANNELS])
+{
+ ChannelMap chanmap[MAX_OUTPUT_CHANNELS];
+ const ALfloat *coeff_scale = N3D2N3DScale;
+ ALfloat w_scale = 1.0f;
+ ALfloat xyz_scale = 1.0f;
+ ALsizei i, j;
+
+ if(conf->FreqBands != 1)
+ ERR("Basic renderer uses the high-frequency matrix as single-band (xover_freq = %.0fhz)\n",
+ conf->XOverFreq);
+
+ if((conf->ChanMask&AMBI_PERIPHONIC_MASK))
+ {
+ if(conf->ChanMask > 0x1ff)
+ {
+ w_scale = W_SCALE_3H3P;
+ xyz_scale = XYZ_SCALE_3H3P;
+ }
+ else if(conf->ChanMask > 0xf)
+ {
+ w_scale = W_SCALE_2H2P;
+ xyz_scale = XYZ_SCALE_2H2P;
+ }
+ }
+ else
+ {
+ if(conf->ChanMask > 0x1ff)
+ {
+ w_scale = W_SCALE_3H0P;
+ xyz_scale = XYZ_SCALE_3H0P;
+ }
+ else if(conf->ChanMask > 0xf)
+ {
+ w_scale = W_SCALE_2H0P;
+ xyz_scale = XYZ_SCALE_2H0P;
+ }
+ }
+
+ if(conf->CoeffScale == ADS_SN3D)
+ coeff_scale = SN3D2N3DScale;
+ else if(conf->CoeffScale == ADS_FuMa)
+ coeff_scale = FuMa2N3DScale;
+
+ for(i = 0;i < conf->NumSpeakers;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;
+ ALsizei chan = speakermap[i];
+ ALfloat gain;
+ ALsizei k = 0;
- chanmap[i].ChanName = channels[i];
- channame = GetLabelFromChannel(channels[i]);
+ for(j = 0;j < MAX_AMBI_COEFFS;j++)
+ chanmap[i].Config[j] = 0.0f;
- snprintf(chanlayout, sizeof(chanlayout), "%s/%s", layout, channame);
- if(!ConfigValueStr(al_string_get_cstr(device->DeviceName), "layouts", chanlayout, &value))
+ chanmap[i].ChanName = device->RealOut.ChannelName[chan];
+ for(j = 0;j < MAX_AMBI_COEFFS;j++)
{
- ERR("Missing channel %s\n", channame);
- return false;
+ if(j == 0) gain = conf->HFOrderGain[0];
+ else if(j == 1) gain = conf->HFOrderGain[1];
+ else if(j == 4) gain = conf->HFOrderGain[2];
+ else if(j == 9) gain = conf->HFOrderGain[3];
+ if((conf->ChanMask&(1<<j)))
+ chanmap[i].Config[j] = conf->HFMatrix[i][k++] / coeff_scale[j] * gain;
}
- if(order == 3)
- props = sscanf(value, " %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %c",
- &coeffs[0], &coeffs[1], &coeffs[2], &coeffs[3],
- &coeffs[4], &coeffs[5], &coeffs[6], &coeffs[7],
- &coeffs[8], &coeffs[9], &coeffs[10], &coeffs[11],
- &coeffs[12], &coeffs[13], &coeffs[14], &coeffs[15],
- &eol
- );
- else if(order == 2)
- props = sscanf(value, " %f %f %f %f %f %f %f %f %f %c",
- &coeffs[0], &coeffs[1], &coeffs[2],
- &coeffs[3], &coeffs[4], &coeffs[5],
- &coeffs[6], &coeffs[7], &coeffs[8],
- &eol
- );
- else if(order == 1)
- props = sscanf(value, " %f %f %f %f %c",
- &coeffs[0], &coeffs[1],
- &coeffs[2], &coeffs[3],
- &eol
- );
- else if(order == 0)
- props = sscanf(value, " %f %c", &coeffs[0], &eol);
- if(props == 0)
+ }
+
+ SetChannelMap(device->RealOut.ChannelName, device->Dry.Ambi.Coeffs, chanmap,
+ conf->NumSpeakers, &device->Dry.NumChannels);
+ device->Dry.CoeffCount = (conf->ChanMask > 0x1ff) ? 16 :
+ (conf->ChanMask > 0xf) ? 9 : 4;
+
+ memset(&device->FOAOut.Ambi, 0, sizeof(device->FOAOut.Ambi));
+ for(i = 0;i < device->Dry.NumChannels;i++)
+ {
+ device->FOAOut.Ambi.Coeffs[i][0] = device->Dry.Ambi.Coeffs[i][0] * w_scale;
+ for(j = 1;j < 4;j++)
+ device->FOAOut.Ambi.Coeffs[i][j] = device->Dry.Ambi.Coeffs[i][j] * xyz_scale;
+ }
+ device->FOAOut.CoeffCount = 4;
+ device->FOAOut.NumChannels = 0;
+
+ device->RealOut.NumChannels = 0;
+
+ InitDistanceComp(device, conf, speakermap);
+}
+
+static void InitHQPanning(ALCdevice *device, const AmbDecConf *conf, const ALsizei speakermap[MAX_OUTPUT_CHANNELS])
+{
+ static const ALsizei chans_per_order2d[MAX_AMBI_ORDER+1] = { 1, 2, 2, 2 };
+ static const ALsizei chans_per_order3d[MAX_AMBI_ORDER+1] = { 1, 3, 5, 7 };
+ ALfloat avg_dist;
+ ALsizei count;
+ ALsizei i;
+
+ if((conf->ChanMask&AMBI_PERIPHONIC_MASK))
+ {
+ count = (conf->ChanMask > 0x1ff) ? 16 :
+ (conf->ChanMask > 0xf) ? 9 : 4;
+ for(i = 0;i < count;i++)
{
- ERR("Failed to parse option %s properties\n", chanlayout);
- return false;
+ device->Dry.Ambi.Map[i].Scale = 1.0f;
+ device->Dry.Ambi.Map[i].Index = i;
}
+ }
+ else
+ {
+ static const int map[MAX_AMBI2D_COEFFS] = { 0, 1, 3, 4, 8, 9, 15 };
- if(props > (order+1)*(order+1))
+ count = (conf->ChanMask > 0x1ff) ? 7 :
+ (conf->ChanMask > 0xf) ? 5 : 3;
+ for(i = 0;i < count;i++)
{
- ERR("Excess elements in option %s (expected %d)\n", chanlayout, (order+1)*(order+1));
- return false;
+ device->Dry.Ambi.Map[i].Scale = 1.0f;
+ device->Dry.Ambi.Map[i].Index = map[i];
}
+ }
+ device->Dry.CoeffCount = 0;
+ device->Dry.NumChannels = count;
+
+ TRACE("Enabling %s-band %s-order%s ambisonic decoder\n",
+ (conf->FreqBands == 1) ? "single" : "dual",
+ (conf->ChanMask > 0xf) ? (conf->ChanMask > 0x1ff) ? "third" : "second" : "first",
+ (conf->ChanMask&AMBI_PERIPHONIC_MASK) ? " periphonic" : ""
+ );
+ bformatdec_reset(device->AmbiDecoder, conf, count, device->Frequency, speakermap);
- for(j = 0;j < MAX_AMBI_COEFFS;++j)
- chanmap[i].Config[j] = coeffs[j];
+ if(!(conf->ChanMask > 0xf))
+ {
+ device->FOAOut.Ambi = device->Dry.Ambi;
+ device->FOAOut.CoeffCount = device->Dry.CoeffCount;
+ device->FOAOut.NumChannels = 0;
}
- SetChannelMap(device, chanmap, count, ambiscale, isfuma);
- return true;
+ else
+ {
+ memset(&device->FOAOut.Ambi, 0, sizeof(device->FOAOut.Ambi));
+ if((conf->ChanMask&AMBI_PERIPHONIC_MASK))
+ {
+ count = 4;
+ for(i = 0;i < count;i++)
+ {
+ device->FOAOut.Ambi.Map[i].Scale = 1.0f;
+ device->FOAOut.Ambi.Map[i].Index = i;
+ }
+ }
+ else
+ {
+ static const int map[3] = { 0, 1, 3 };
+ count = 3;
+ for(i = 0;i < count;i++)
+ {
+ device->FOAOut.Ambi.Map[i].Scale = 1.0f;
+ device->FOAOut.Ambi.Map[i].Index = map[i];
+ }
+ }
+ device->FOAOut.CoeffCount = 0;
+ device->FOAOut.NumChannels = count;
+ }
+
+ device->RealOut.NumChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
+
+ avg_dist = 0.0f;
+ for(i = 0;i < conf->NumSpeakers;i++)
+ avg_dist += conf->Speakers[i].Distance;
+ avg_dist /= (ALfloat)conf->NumSpeakers;
+ InitNearFieldCtrl(device, avg_dist,
+ (conf->ChanMask > 0x1ff) ? 3 : (conf->ChanMask > 0xf) ? 2 : 1,
+ (conf->ChanMask&AMBI_PERIPHONIC_MASK) ? chans_per_order3d : chans_per_order2d
+ );
+
+ InitDistanceComp(device, conf, speakermap);
}
-ALvoid aluInitPanning(ALCdevice *device)
+static void InitHrtfPanning(ALCdevice *device)
{
- /* NOTE: These decoder coefficients are using FuMa channel ordering and
- * normalization, since that's what was produced by the Ambisonic Decoder
- * Toolbox. SetChannelMap will convert them to N3D.
- */
- static const ChannelMap MonoCfg[1] = {
- { FrontCenter, { 1.414213562f } },
- }, StereoCfg[2] = {
- { FrontLeft, { 0.707106781f, 0.0f, 0.5f, 0.0f } },
- { FrontRight, { 0.707106781f, 0.0f, -0.5f, 0.0f } },
- }, QuadCfg[4] = {
- { FrontLeft, { 0.353553f, 0.306184f, 0.306184f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000f, 0.117186f } },
- { FrontRight, { 0.353553f, 0.306184f, -0.306184f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000f, -0.117186f } },
- { BackLeft, { 0.353553f, -0.306184f, 0.306184f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000f, -0.117186f } },
- { BackRight, { 0.353553f, -0.306184f, -0.306184f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000f, 0.117186f } },
- }, X51SideCfg[5] = {
- { FrontLeft, { 0.208954f, 0.212846f, 0.238350f, 0.0f, 0.0f, 0.0f, 0.0f, -0.017738f, 0.204014f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.051023f, 0.047490f } },
- { FrontRight, { 0.208954f, 0.212846f, -0.238350f, 0.0f, 0.0f, 0.0f, 0.0f, -0.017738f, -0.204014f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.051023f, -0.047490f } },
- { FrontCenter, { 0.109403f, 0.179490f, 0.000000f, 0.0f, 0.0f, 0.0f, 0.0f, 0.142031f, 0.000000f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.072024f, 0.000000f } },
- { SideLeft, { 0.470936f, -0.369626f, 0.349386f, 0.0f, 0.0f, 0.0f, 0.0f, -0.031375f, -0.058144f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.007119f, -0.043968f } },
- { SideRight, { 0.470936f, -0.369626f, -0.349386f, 0.0f, 0.0f, 0.0f, 0.0f, -0.031375f, 0.058144f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.007119f, 0.043968f } },
- }, X51RearCfg[5] = {
- { FrontLeft, { 0.208954f, 0.212846f, 0.238350f, 0.0f, 0.0f, 0.0f, 0.0f, -0.017738f, 0.204014f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.051023f, 0.047490f } },
- { FrontRight, { 0.208954f, 0.212846f, -0.238350f, 0.0f, 0.0f, 0.0f, 0.0f, -0.017738f, -0.204014f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.051023f, -0.047490f } },
- { FrontCenter, { 0.109403f, 0.179490f, 0.000000f, 0.0f, 0.0f, 0.0f, 0.0f, 0.142031f, 0.000000f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.072024f, 0.000000f } },
- { BackLeft, { 0.470936f, -0.369626f, 0.349386f, 0.0f, 0.0f, 0.0f, 0.0f, -0.031375f, -0.058144f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.007119f, -0.043968f } },
- { BackRight, { 0.470936f, -0.369626f, -0.349386f, 0.0f, 0.0f, 0.0f, 0.0f, -0.031375f, 0.058144f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.007119f, 0.043968f } },
- }, X61Cfg[6] = {
- { FrontLeft, { 0.167065f, 0.200583f, 0.172695f, 0.0f, 0.0f, 0.0f, 0.0f, 0.029855f, 0.186407f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.039241f, 0.068910f } },
- { FrontRight, { 0.167065f, 0.200583f, -0.172695f, 0.0f, 0.0f, 0.0f, 0.0f, 0.029855f, -0.186407f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.039241f, -0.068910f } },
- { FrontCenter, { 0.109403f, 0.179490f, 0.000000f, 0.0f, 0.0f, 0.0f, 0.0f, 0.142031f, 0.000000f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.072024f, 0.000000f } },
- { BackCenter, { 0.353556f, -0.461940f, 0.000000f, 0.0f, 0.0f, 0.0f, 0.0f, 0.165723f, 0.000000f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000f, 0.000000f } },
- { SideLeft, { 0.289151f, -0.081301f, 0.401292f, 0.0f, 0.0f, 0.0f, 0.0f, -0.188208f, -0.071420f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.010099f, -0.032897f } },
- { SideRight, { 0.289151f, -0.081301f, -0.401292f, 0.0f, 0.0f, 0.0f, 0.0f, -0.188208f, 0.071420f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.010099f, 0.032897f } },
- }, X71Cfg[7] = {
- { FrontLeft, { 0.167065f, 0.200583f, 0.172695f, 0.0f, 0.0f, 0.0f, 0.0f, 0.029855f, 0.186407f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.039241f, 0.068910f } },
- { FrontRight, { 0.167065f, 0.200583f, -0.172695f, 0.0f, 0.0f, 0.0f, 0.0f, 0.029855f, -0.186407f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.039241f, -0.068910f } },
- { FrontCenter, { 0.109403f, 0.179490f, 0.000000f, 0.0f, 0.0f, 0.0f, 0.0f, 0.142031f, 0.000000f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.072024f, 0.000000f } },
- { BackLeft, { 0.224752f, -0.295009f, 0.170325f, 0.0f, 0.0f, 0.0f, 0.0f, 0.105349f, -0.182473f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000f, 0.065799f } },
- { BackRight, { 0.224752f, -0.295009f, -0.170325f, 0.0f, 0.0f, 0.0f, 0.0f, 0.105349f, 0.182473f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000f, -0.065799f } },
- { SideLeft, { 0.224739f, 0.000000f, 0.340644f, 0.0f, 0.0f, 0.0f, 0.0f, -0.210697f, 0.000000f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000f, -0.065795f } },
- { SideRight, { 0.224739f, 0.000000f, -0.340644f, 0.0f, 0.0f, 0.0f, 0.0f, -0.210697f, 0.000000f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000f, 0.065795f } },
- }, BFormat3D[4] = {
- { BFormatW, { 1.0f, 0.0f, 0.0f, 0.0f } },
- { BFormatX, { 0.0f, 1.0f, 0.0f, 0.0f } },
- { BFormatY, { 0.0f, 0.0f, 1.0f, 0.0f } },
- { BFormatZ, { 0.0f, 0.0f, 0.0f, 1.0f } },
+ /* NOTE: azimuth goes clockwise. */
+ static const struct AngularPoint AmbiPoints[] = {
+ { DEG2RAD( 90.0f), DEG2RAD( 0.0f) },
+ { DEG2RAD( 35.2643897f), DEG2RAD( 45.0f) },
+ { DEG2RAD( 35.2643897f), DEG2RAD( 135.0f) },
+ { DEG2RAD( 35.2643897f), DEG2RAD(-135.0f) },
+ { DEG2RAD( 35.2643897f), DEG2RAD( -45.0f) },
+ { DEG2RAD( 0.0f), DEG2RAD( 0.0f) },
+ { DEG2RAD( 0.0f), DEG2RAD( 45.0f) },
+ { DEG2RAD( 0.0f), DEG2RAD( 90.0f) },
+ { DEG2RAD( 0.0f), DEG2RAD( 135.0f) },
+ { DEG2RAD( 0.0f), DEG2RAD( 180.0f) },
+ { DEG2RAD( 0.0f), DEG2RAD(-135.0f) },
+ { DEG2RAD( 0.0f), DEG2RAD( -90.0f) },
+ { DEG2RAD( 0.0f), DEG2RAD( -45.0f) },
+ { DEG2RAD(-35.2643897f), DEG2RAD( 45.0f) },
+ { DEG2RAD(-35.2643897f), DEG2RAD( 135.0f) },
+ { DEG2RAD(-35.2643897f), DEG2RAD(-135.0f) },
+ { DEG2RAD(-35.2643897f), DEG2RAD( -45.0f) },
+ { DEG2RAD(-90.0f), DEG2RAD( 0.0f) },
};
- const ChannelMap *chanmap = NULL;
- ALfloat ambiscale = 1.0f;
- size_t count = 0;
+ static const ALfloat AmbiMatrixFOA[][MAX_AMBI_COEFFS] = {
+ { 5.55555556e-02f, 0.00000000e+00f, 1.23717915e-01f, 0.00000000e+00f },
+ { 5.55555556e-02f, -5.00000000e-02f, 7.14285715e-02f, 5.00000000e-02f },
+ { 5.55555556e-02f, -5.00000000e-02f, 7.14285715e-02f, -5.00000000e-02f },
+ { 5.55555556e-02f, 5.00000000e-02f, 7.14285715e-02f, -5.00000000e-02f },
+ { 5.55555556e-02f, 5.00000000e-02f, 7.14285715e-02f, 5.00000000e-02f },
+ { 5.55555556e-02f, 0.00000000e+00f, 0.00000000e+00f, 8.66025404e-02f },
+ { 5.55555556e-02f, -6.12372435e-02f, 0.00000000e+00f, 6.12372435e-02f },
+ { 5.55555556e-02f, -8.66025404e-02f, 0.00000000e+00f, 0.00000000e+00f },
+ { 5.55555556e-02f, -6.12372435e-02f, 0.00000000e+00f, -6.12372435e-02f },
+ { 5.55555556e-02f, 0.00000000e+00f, 0.00000000e+00f, -8.66025404e-02f },
+ { 5.55555556e-02f, 6.12372435e-02f, 0.00000000e+00f, -6.12372435e-02f },
+ { 5.55555556e-02f, 8.66025404e-02f, 0.00000000e+00f, 0.00000000e+00f },
+ { 5.55555556e-02f, 6.12372435e-02f, 0.00000000e+00f, 6.12372435e-02f },
+ { 5.55555556e-02f, -5.00000000e-02f, -7.14285715e-02f, 5.00000000e-02f },
+ { 5.55555556e-02f, -5.00000000e-02f, -7.14285715e-02f, -5.00000000e-02f },
+ { 5.55555556e-02f, 5.00000000e-02f, -7.14285715e-02f, -5.00000000e-02f },
+ { 5.55555556e-02f, 5.00000000e-02f, -7.14285715e-02f, 5.00000000e-02f },
+ { 5.55555556e-02f, 0.00000000e+00f, -1.23717915e-01f, 0.00000000e+00f },
+ }, AmbiMatrixHOA[][MAX_AMBI_COEFFS] = {
+ { 5.55555556e-02f, 0.00000000e+00f, 1.23717915e-01f, 0.00000000e+00f, 0.00000000e+00f, 0.00000000e+00f },
+ { 5.55555556e-02f, -5.00000000e-02f, 7.14285715e-02f, 5.00000000e-02f, -4.55645099e-02f, 0.00000000e+00f },
+ { 5.55555556e-02f, -5.00000000e-02f, 7.14285715e-02f, -5.00000000e-02f, 4.55645099e-02f, 0.00000000e+00f },
+ { 5.55555556e-02f, 5.00000000e-02f, 7.14285715e-02f, -5.00000000e-02f, -4.55645099e-02f, 0.00000000e+00f },
+ { 5.55555556e-02f, 5.00000000e-02f, 7.14285715e-02f, 5.00000000e-02f, 4.55645099e-02f, 0.00000000e+00f },
+ { 5.55555556e-02f, 0.00000000e+00f, 0.00000000e+00f, 8.66025404e-02f, 0.00000000e+00f, 1.29099445e-01f },
+ { 5.55555556e-02f, -6.12372435e-02f, 0.00000000e+00f, 6.12372435e-02f, -6.83467648e-02f, 0.00000000e+00f },
+ { 5.55555556e-02f, -8.66025404e-02f, 0.00000000e+00f, 0.00000000e+00f, 0.00000000e+00f, -1.29099445e-01f },
+ { 5.55555556e-02f, -6.12372435e-02f, 0.00000000e+00f, -6.12372435e-02f, 6.83467648e-02f, 0.00000000e+00f },
+ { 5.55555556e-02f, 0.00000000e+00f, 0.00000000e+00f, -8.66025404e-02f, 0.00000000e+00f, 1.29099445e-01f },
+ { 5.55555556e-02f, 6.12372435e-02f, 0.00000000e+00f, -6.12372435e-02f, -6.83467648e-02f, 0.00000000e+00f },
+ { 5.55555556e-02f, 8.66025404e-02f, 0.00000000e+00f, 0.00000000e+00f, 0.00000000e+00f, -1.29099445e-01f },
+ { 5.55555556e-02f, 6.12372435e-02f, 0.00000000e+00f, 6.12372435e-02f, 6.83467648e-02f, 0.00000000e+00f },
+ { 5.55555556e-02f, -5.00000000e-02f, -7.14285715e-02f, 5.00000000e-02f, -4.55645099e-02f, 0.00000000e+00f },
+ { 5.55555556e-02f, -5.00000000e-02f, -7.14285715e-02f, -5.00000000e-02f, 4.55645099e-02f, 0.00000000e+00f },
+ { 5.55555556e-02f, 5.00000000e-02f, -7.14285715e-02f, -5.00000000e-02f, -4.55645099e-02f, 0.00000000e+00f },
+ { 5.55555556e-02f, 5.00000000e-02f, -7.14285715e-02f, 5.00000000e-02f, 4.55645099e-02f, 0.00000000e+00f },
+ { 5.55555556e-02f, 0.00000000e+00f, -1.23717915e-01f, 0.00000000e+00f, 0.00000000e+00f, 0.00000000e+00f },
+ };
+ static const ALfloat AmbiOrderHFGainFOA[MAX_AMBI_ORDER+1] = {
+ 3.00000000e+00f, 1.73205081e+00f
+ }, AmbiOrderHFGainHOA[MAX_AMBI_ORDER+1] = {
+ 2.40192231e+00f, 1.86052102e+00f, 9.60768923e-01f
+ };
+ static const ALsizei IndexMap[6] = { 0, 1, 2, 3, 4, 8 };
+ static const ALsizei ChansPerOrder[MAX_AMBI_ORDER+1] = { 1, 3, 2, 0 };
+ const ALfloat (*restrict AmbiMatrix)[MAX_AMBI_COEFFS] = AmbiMatrixFOA;
+ const ALfloat *restrict AmbiOrderHFGain = AmbiOrderHFGainFOA;
+ ALsizei count = 4;
+ ALsizei i;
- device->AmbiScale = 1.0f;
- memset(device->AmbiCoeffs, 0, sizeof(device->AmbiCoeffs));
- device->NumChannels = 0;
+ static_assert(COUNTOF(AmbiPoints) == COUNTOF(AmbiMatrixFOA), "FOA Ambisonic HRTF mismatch");
+ static_assert(COUNTOF(AmbiPoints) == COUNTOF(AmbiMatrixHOA), "HOA Ambisonic HRTF mismatch");
- if(device->Hrtf)
+ if(device->AmbiUp)
{
- ALfloat (*coeffs_list[4])[2];
- ALuint *delay_list[4];
- ALuint i;
+ AmbiMatrix = AmbiMatrixHOA;
+ AmbiOrderHFGain = AmbiOrderHFGainHOA;
+ count = COUNTOF(IndexMap);
+ }
- count = COUNTOF(BFormat3D);
- chanmap = BFormat3D;
- ambiscale = 1.0f;
+ device->Hrtf = al_calloc(16, FAM_SIZE(DirectHrtfState, Chan, count));
- 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 < count;i++)
+ {
+ device->Dry.Ambi.Map[i].Scale = 1.0f;
+ device->Dry.Ambi.Map[i].Index = IndexMap[i];
+ }
+ device->Dry.CoeffCount = 0;
+ device->Dry.NumChannels = count;
- for(i = 0;i < 4;++i)
+ if(device->AmbiUp)
+ {
+ memset(&device->FOAOut.Ambi, 0, sizeof(device->FOAOut.Ambi));
+ 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;
+ device->FOAOut.Ambi.Map[i].Scale = 1.0f;
+ device->FOAOut.Ambi.Map[i].Index = i;
}
- GetBFormatHrtfCoeffs(device->Hrtf, 4, coeffs_list, delay_list);
+ device->FOAOut.CoeffCount = 0;
+ device->FOAOut.NumChannels = 4;
- return;
+ ambiup_reset(device->AmbiUp, device, AmbiOrderHFGainFOA[0] / AmbiOrderHFGain[0],
+ AmbiOrderHFGainFOA[1] / AmbiOrderHFGain[1]);
+ }
+ else
+ {
+ device->FOAOut.Ambi = device->Dry.Ambi;
+ device->FOAOut.CoeffCount = device->Dry.CoeffCount;
+ device->FOAOut.NumChannels = 0;
}
- if(LoadChannelSetup(device))
- return;
+ device->RealOut.NumChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
- switch(device->FmtChans)
+ BuildBFormatHrtf(device->HrtfHandle,
+ device->Hrtf, device->Dry.NumChannels, AmbiPoints, AmbiMatrix, COUNTOF(AmbiPoints),
+ AmbiOrderHFGain
+ );
+
+ InitNearFieldCtrl(device, device->HrtfHandle->distance, device->AmbiUp ? 2 : 1,
+ ChansPerOrder);
+}
+
+static void InitUhjPanning(ALCdevice *device)
+{
+ ALsizei count = 3;
+ ALsizei i;
+
+ for(i = 0;i < count;i++)
{
- case DevFmtMono:
- count = COUNTOF(MonoCfg);
- chanmap = MonoCfg;
- ambiscale = ZERO_ORDER_SCALE;
- break;
+ ALsizei acn = FuMa2ACN[i];
+ device->Dry.Ambi.Map[i].Scale = 1.0f/FuMa2N3DScale[acn];
+ device->Dry.Ambi.Map[i].Index = acn;
+ }
+ device->Dry.CoeffCount = 0;
+ device->Dry.NumChannels = count;
- case DevFmtStereo:
- count = COUNTOF(StereoCfg);
- chanmap = StereoCfg;
- ambiscale = FIRST_ORDER_SCALE;
- break;
+ device->FOAOut.Ambi = device->Dry.Ambi;
+ device->FOAOut.CoeffCount = device->Dry.CoeffCount;
+ device->FOAOut.NumChannels = 0;
- case DevFmtQuad:
- count = COUNTOF(QuadCfg);
- chanmap = QuadCfg;
- ambiscale = SECOND_ORDER_SCALE;
- break;
+ device->RealOut.NumChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
+}
- case DevFmtX51:
- count = COUNTOF(X51SideCfg);
- chanmap = X51SideCfg;
- ambiscale = THIRD_ORDER_SCALE;
- break;
+void aluInitRenderer(ALCdevice *device, ALint hrtf_id, enum HrtfRequestMode hrtf_appreq, enum HrtfRequestMode hrtf_userreq)
+{
+ /* Hold the HRTF the device last used, in case it's used again. */
+ struct Hrtf *old_hrtf = device->HrtfHandle;
+ const char *mode;
+ bool headphones;
+ int bs2blevel;
+ size_t i;
- case DevFmtX51Rear:
- count = COUNTOF(X51RearCfg);
- chanmap = X51RearCfg;
- ambiscale = THIRD_ORDER_SCALE;
- break;
+ al_free(device->Hrtf);
+ device->Hrtf = NULL;
+ device->HrtfHandle = NULL;
+ alstr_clear(&device->HrtfName);
+ device->Render_Mode = NormalRender;
- case DevFmtX61:
- count = COUNTOF(X61Cfg);
- chanmap = X61Cfg;
- ambiscale = THIRD_ORDER_SCALE;
- break;
+ memset(&device->Dry.Ambi, 0, sizeof(device->Dry.Ambi));
+ device->Dry.CoeffCount = 0;
+ device->Dry.NumChannels = 0;
+ for(i = 0;i < MAX_AMBI_ORDER+1;i++)
+ device->NumChannelsPerOrder[i] = 0;
+
+ device->AvgSpeakerDist = 0.0f;
+ memset(device->ChannelDelay, 0, sizeof(device->ChannelDelay));
+ for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
+ {
+ device->ChannelDelay[i].Gain = 1.0f;
+ device->ChannelDelay[i].Length = 0;
+ }
+
+ al_free(device->Stablizer);
+ device->Stablizer = NULL;
+
+ if(device->FmtChans != DevFmtStereo)
+ {
+ ALsizei speakermap[MAX_OUTPUT_CHANNELS];
+ const char *devname, *layout = NULL;
+ AmbDecConf conf, *pconf = NULL;
+
+ if(old_hrtf)
+ Hrtf_DecRef(old_hrtf);
+ old_hrtf = NULL;
+ if(hrtf_appreq == Hrtf_Enable)
+ device->HrtfStatus = ALC_HRTF_UNSUPPORTED_FORMAT_SOFT;
+
+ ambdec_init(&conf);
+
+ devname = alstr_get_cstr(device->DeviceName);
+ switch(device->FmtChans)
+ {
+ case DevFmtQuad: layout = "quad"; break;
+ case DevFmtX51: /* fall-through */
+ case DevFmtX51Rear: layout = "surround51"; break;
+ case DevFmtX61: layout = "surround61"; break;
+ case DevFmtX71: layout = "surround71"; break;
+ /* Mono, Stereo, and Ambisonics output don't use custom decoders. */
+ case DevFmtMono:
+ case DevFmtStereo:
+ case DevFmtAmbi3D:
+ break;
+ }
+ if(layout)
+ {
+ const char *fname;
+ if(ConfigValueStr(devname, "decoder", layout, &fname))
+ {
+ if(!ambdec_load(&conf, fname))
+ ERR("Failed to load layout file %s\n", fname);
+ else
+ {
+ if(conf.ChanMask > 0xffff)
+ ERR("Unsupported channel mask 0x%04x (max 0xffff)\n", conf.ChanMask);
+ else
+ {
+ if(MakeSpeakerMap(device, &conf, speakermap))
+ pconf = &conf;
+ }
+ }
+ }
+ }
+ if(pconf && GetConfigValueBool(devname, "decoder", "hq-mode", 0))
+ {
+ ambiup_free(&device->AmbiUp);
+ if(!device->AmbiDecoder)
+ device->AmbiDecoder = bformatdec_alloc();
+ }
+ else
+ {
+ bformatdec_free(&device->AmbiDecoder);
+ if(device->FmtChans != DevFmtAmbi3D || device->AmbiOrder < 2)
+ ambiup_free(&device->AmbiUp);
+ else
+ {
+ if(!device->AmbiUp)
+ device->AmbiUp = ambiup_alloc();
+ }
+ }
+
+ if(!pconf)
+ InitPanning(device);
+ else if(device->AmbiDecoder)
+ InitHQPanning(device, pconf, speakermap);
+ else
+ InitCustomPanning(device, pconf, speakermap);
+
+ /* Enable the stablizer only for formats that have front-left, front-
+ * right, and front-center outputs.
+ */
+ switch(device->FmtChans)
+ {
+ case DevFmtX51:
+ case DevFmtX51Rear:
+ case DevFmtX61:
case DevFmtX71:
- count = COUNTOF(X71Cfg);
- chanmap = X71Cfg;
- ambiscale = THIRD_ORDER_SCALE;
- break;
+ if(GetConfigValueBool(devname, NULL, "front-stablizer", 0))
+ {
+ /* Initialize band-splitting filters for the front-left and
+ * front-right channels, with a crossover at 5khz (could be
+ * higher).
+ */
+ ALfloat scale = (ALfloat)(5000.0 / device->Frequency);
+ FrontStablizer *stablizer = al_calloc(16, sizeof(*stablizer));
- case DevFmtBFormat3D:
- count = COUNTOF(BFormat3D);
- chanmap = BFormat3D;
- ambiscale = 1.0f;
+ bandsplit_init(&stablizer->LFilter, scale);
+ stablizer->RFilter = stablizer->LFilter;
+
+ /* Initialize all-pass filters for all other channels. */
+ splitterap_init(&stablizer->APFilter[0], scale);
+ for(i = 1;i < (size_t)device->RealOut.NumChannels;i++)
+ stablizer->APFilter[i] = stablizer->APFilter[0];
+
+ device->Stablizer = stablizer;
+ }
+ break;
+ case DevFmtMono:
+ case DevFmtStereo:
+ case DevFmtQuad:
+ case DevFmtAmbi3D:
break;
+ }
+ TRACE("Front stablizer %s\n", device->Stablizer ? "enabled" : "disabled");
+
+ ambdec_deinit(&conf);
+ return;
+ }
+
+ bformatdec_free(&device->AmbiDecoder);
+
+ headphones = device->IsHeadphones;
+ if(device->Type != Loopback)
+ {
+ const char *mode;
+ if(ConfigValueStr(alstr_get_cstr(device->DeviceName), NULL, "stereo-mode", &mode))
+ {
+ if(strcasecmp(mode, "headphones") == 0)
+ headphones = true;
+ else if(strcasecmp(mode, "speakers") == 0)
+ headphones = false;
+ else if(strcasecmp(mode, "auto") != 0)
+ ERR("Unexpected stereo-mode: %s\n", mode);
+ }
+ }
+
+ if(hrtf_userreq == Hrtf_Default)
+ {
+ bool usehrtf = (headphones && hrtf_appreq != Hrtf_Disable) ||
+ (hrtf_appreq == Hrtf_Enable);
+ if(!usehrtf) goto no_hrtf;
+
+ device->HrtfStatus = ALC_HRTF_ENABLED_SOFT;
+ if(headphones && hrtf_appreq != Hrtf_Disable)
+ device->HrtfStatus = ALC_HRTF_HEADPHONES_DETECTED_SOFT;
+ }
+ else
+ {
+ if(hrtf_userreq != Hrtf_Enable)
+ {
+ if(hrtf_appreq == Hrtf_Enable)
+ device->HrtfStatus = ALC_HRTF_DENIED_SOFT;
+ goto no_hrtf;
+ }
+ device->HrtfStatus = ALC_HRTF_REQUIRED_SOFT;
+ }
+
+ if(VECTOR_SIZE(device->HrtfList) == 0)
+ {
+ VECTOR_DEINIT(device->HrtfList);
+ device->HrtfList = EnumerateHrtf(device->DeviceName);
}
- SetChannelMap(device, chanmap, count, ambiscale, AL_TRUE);
+ if(hrtf_id >= 0 && (size_t)hrtf_id < VECTOR_SIZE(device->HrtfList))
+ {
+ const EnumeratedHrtf *entry = &VECTOR_ELEM(device->HrtfList, hrtf_id);
+ struct Hrtf *hrtf = GetLoadedHrtf(entry->hrtf);
+ if(hrtf && hrtf->sampleRate == device->Frequency)
+ {
+ device->HrtfHandle = hrtf;
+ alstr_copy(&device->HrtfName, entry->name);
+ }
+ else if(hrtf)
+ Hrtf_DecRef(hrtf);
+ }
+
+ for(i = 0;!device->HrtfHandle && i < VECTOR_SIZE(device->HrtfList);i++)
+ {
+ const EnumeratedHrtf *entry = &VECTOR_ELEM(device->HrtfList, i);
+ struct Hrtf *hrtf = GetLoadedHrtf(entry->hrtf);
+ if(hrtf && hrtf->sampleRate == device->Frequency)
+ {
+ device->HrtfHandle = hrtf;
+ alstr_copy(&device->HrtfName, entry->name);
+ }
+ else if(hrtf)
+ Hrtf_DecRef(hrtf);
+ }
+
+ if(device->HrtfHandle)
+ {
+ if(old_hrtf)
+ Hrtf_DecRef(old_hrtf);
+ old_hrtf = NULL;
+
+ device->Render_Mode = HrtfRender;
+ if(ConfigValueStr(alstr_get_cstr(device->DeviceName), NULL, "hrtf-mode", &mode))
+ {
+ if(strcasecmp(mode, "full") == 0)
+ device->Render_Mode = HrtfRender;
+ else if(strcasecmp(mode, "basic") == 0)
+ device->Render_Mode = NormalRender;
+ else
+ ERR("Unexpected hrtf-mode: %s\n", mode);
+ }
+
+ if(device->Render_Mode == HrtfRender)
+ {
+ /* Don't bother with HOA when using full HRTF rendering. Nothing
+ * needs it, and it eases the CPU/memory load.
+ */
+ ambiup_free(&device->AmbiUp);
+ }
+ else
+ {
+ if(!device->AmbiUp)
+ device->AmbiUp = ambiup_alloc();
+ }
+
+ TRACE("%s HRTF rendering enabled, using \"%s\"\n",
+ ((device->Render_Mode == HrtfRender) ? "Full" : "Basic"),
+ alstr_get_cstr(device->HrtfName)
+ );
+ InitHrtfPanning(device);
+ return;
+ }
+ device->HrtfStatus = ALC_HRTF_UNSUPPORTED_FORMAT_SOFT;
+
+no_hrtf:
+ if(old_hrtf)
+ Hrtf_DecRef(old_hrtf);
+ old_hrtf = NULL;
+ TRACE("HRTF disabled\n");
+
+ device->Render_Mode = StereoPair;
+
+ ambiup_free(&device->AmbiUp);
+
+ bs2blevel = ((headphones && hrtf_appreq != Hrtf_Disable) ||
+ (hrtf_appreq == Hrtf_Enable)) ? 5 : 0;
+ if(device->Type != Loopback)
+ ConfigValueInt(alstr_get_cstr(device->DeviceName), NULL, "cf_level", &bs2blevel);
+ if(bs2blevel > 0 && bs2blevel <= 6)
+ {
+ device->Bs2b = al_calloc(16, sizeof(*device->Bs2b));
+ bs2b_set_params(device->Bs2b, bs2blevel, device->Frequency);
+ TRACE("BS2B enabled\n");
+ InitPanning(device);
+ return;
+ }
+
+ TRACE("BS2B disabled\n");
+
+ if(ConfigValueStr(alstr_get_cstr(device->DeviceName), NULL, "stereo-encoding", &mode))
+ {
+ if(strcasecmp(mode, "uhj") == 0)
+ device->Render_Mode = NormalRender;
+ else if(strcasecmp(mode, "panpot") != 0)
+ ERR("Unexpected stereo-encoding: %s\n", mode);
+ }
+ if(device->Render_Mode == NormalRender)
+ {
+ device->Uhj_Encoder = al_calloc(16, sizeof(Uhj2Encoder));
+ TRACE("UHJ enabled\n");
+ InitUhjPanning(device);
+ return;
+ }
+
+ TRACE("UHJ disabled\n");
+ InitPanning(device);
+}
+
+
+void aluInitEffectPanning(ALeffectslot *slot)
+{
+ ALsizei i;
+
+ memset(slot->ChanMap, 0, sizeof(slot->ChanMap));
+ slot->NumChannels = 0;
+
+ for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
+ {
+ slot->ChanMap[i].Scale = 1.0f;
+ slot->ChanMap[i].Index = i;
+ }
+ slot->NumChannels = i;
}
diff --git a/Alc/polymorphism.h b/Alc/polymorphism.h
new file mode 100644
index 00000000..fa31fad2
--- /dev/null
+++ b/Alc/polymorphism.h
@@ -0,0 +1,105 @@
+#ifndef POLYMORPHISM_H
+#define POLYMORPHISM_H
+
+/* Macros to declare inheriting types, and to (down-)cast and up-cast. */
+#define DERIVE_FROM_TYPE(t) t t##_parent
+#define STATIC_CAST(to, obj) (&(obj)->to##_parent)
+#ifdef __GNUC__
+#define STATIC_UPCAST(to, from, obj) __extension__({ \
+ static_assert(__builtin_types_compatible_p(from, __typeof(*(obj))), \
+ "Invalid upcast object from type"); \
+ (to*)((char*)(obj) - offsetof(to, from##_parent)); \
+})
+#else
+#define STATIC_UPCAST(to, from, obj) ((to*)((char*)(obj) - offsetof(to, from##_parent)))
+#endif
+
+/* Defines method forwards, which call the given parent's (T2's) implementation. */
+#define DECLARE_FORWARD(T1, T2, rettype, func) \
+rettype T1##_##func(T1 *obj) \
+{ return T2##_##func(STATIC_CAST(T2, obj)); }
+
+#define DECLARE_FORWARD1(T1, T2, rettype, func, argtype1) \
+rettype T1##_##func(T1 *obj, argtype1 a) \
+{ return T2##_##func(STATIC_CAST(T2, obj), a); }
+
+#define DECLARE_FORWARD2(T1, T2, rettype, func, argtype1, argtype2) \
+rettype T1##_##func(T1 *obj, argtype1 a, argtype2 b) \
+{ return T2##_##func(STATIC_CAST(T2, obj), a, b); }
+
+#define DECLARE_FORWARD3(T1, T2, rettype, func, argtype1, argtype2, argtype3) \
+rettype T1##_##func(T1 *obj, argtype1 a, argtype2 b, argtype3 c) \
+{ return T2##_##func(STATIC_CAST(T2, obj), a, b, c); }
+
+/* Defines method thunks, functions that call to the child's method. */
+#define DECLARE_THUNK(T1, T2, rettype, func) \
+static rettype T1##_##T2##_##func(T2 *obj) \
+{ return T1##_##func(STATIC_UPCAST(T1, T2, obj)); }
+
+#define DECLARE_THUNK1(T1, T2, rettype, func, argtype1) \
+static rettype T1##_##T2##_##func(T2 *obj, argtype1 a) \
+{ return T1##_##func(STATIC_UPCAST(T1, T2, obj), a); }
+
+#define DECLARE_THUNK2(T1, T2, rettype, func, argtype1, argtype2) \
+static rettype T1##_##T2##_##func(T2 *obj, argtype1 a, argtype2 b) \
+{ return T1##_##func(STATIC_UPCAST(T1, T2, obj), a, b); }
+
+#define DECLARE_THUNK3(T1, T2, rettype, func, argtype1, argtype2, argtype3) \
+static rettype T1##_##T2##_##func(T2 *obj, argtype1 a, argtype2 b, argtype3 c) \
+{ return T1##_##func(STATIC_UPCAST(T1, T2, obj), a, b, c); }
+
+#define DECLARE_THUNK4(T1, T2, rettype, func, argtype1, argtype2, argtype3, argtype4) \
+static rettype T1##_##T2##_##func(T2 *obj, argtype1 a, argtype2 b, argtype3 c, argtype4 d) \
+{ return T1##_##func(STATIC_UPCAST(T1, T2, obj), a, b, c, d); }
+
+/* Defines the default functions used to (de)allocate a polymorphic object. */
+#define DECLARE_DEFAULT_ALLOCATORS(T) \
+static void* T##_New(size_t size) { return al_malloc(16, size); } \
+static void T##_Delete(void *ptr) { al_free(ptr); }
+
+
+/* Helper to extract an argument list for virtual method calls. */
+#define EXTRACT_VCALL_ARGS(...) __VA_ARGS__))
+
+/* Call a "virtual" method on an object, with arguments. */
+#define V(obj, func) ((obj)->vtbl->func((obj), EXTRACT_VCALL_ARGS
+/* Call a "virtual" method on an object, with no arguments. */
+#define V0(obj, func) ((obj)->vtbl->func((obj) EXTRACT_VCALL_ARGS
+
+
+/* Helper to extract an argument list for NEW_OBJ calls. */
+#define EXTRACT_NEW_ARGS(...) __VA_ARGS__); \
+ } \
+} while(0)
+
+/* Allocate and construct an object, with arguments. */
+#define NEW_OBJ(_res, T) do { \
+ _res = T##_New(sizeof(T)); \
+ if(_res) \
+ { \
+ memset(_res, 0, sizeof(T)); \
+ T##_Construct(_res, EXTRACT_NEW_ARGS
+/* Allocate and construct an object, with no arguments. */
+#define NEW_OBJ0(_res, T) do { \
+ _res = T##_New(sizeof(T)); \
+ if(_res) \
+ { \
+ memset(_res, 0, sizeof(T)); \
+ T##_Construct(_res EXTRACT_NEW_ARGS
+
+/* Destructs and deallocate an object. */
+#define DELETE_OBJ(obj) do { \
+ if((obj) != NULL) \
+ { \
+ V0((obj),Destruct)(); \
+ V0((obj),Delete)(); \
+ } \
+} while(0)
+
+
+/* Helper to get a type's vtable thunk for a child type. */
+#define GET_VTABLE2(T1, T2) (&(T1##_##T2##_vtable))
+/* Helper to set an object's vtable thunk for a child type. Used when constructing an object. */
+#define SET_VTABLE2(T1, T2, obj) (STATIC_CAST(T2, obj)->vtbl = GET_VTABLE2(T1, T2))
+
+#endif /* POLYMORPHISM_H */
diff --git a/Alc/ringbuffer.c b/Alc/ringbuffer.c
new file mode 100644
index 00000000..6c419cf8
--- /dev/null
+++ b/Alc/ringbuffer.c
@@ -0,0 +1,295 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 1999-2007 by authors.
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <limits.h>
+
+#include "ringbuffer.h"
+#include "align.h"
+#include "atomic.h"
+#include "threads.h"
+#include "almalloc.h"
+#include "compat.h"
+
+
+/* NOTE: This lockless ringbuffer implementation is copied from JACK, extended
+ * to include an element size. Consequently, parameters and return values for a
+ * size or count is in 'elements', not bytes. Additionally, it only supports
+ * single-consumer/single-provider operation. */
+struct ll_ringbuffer {
+ ATOMIC(size_t) write_ptr;
+ ATOMIC(size_t) read_ptr;
+ size_t size;
+ size_t size_mask;
+ size_t elem_size;
+
+ alignas(16) char buf[];
+};
+
+ll_ringbuffer_t *ll_ringbuffer_create(size_t sz, size_t elem_sz, int limit_writes)
+{
+ ll_ringbuffer_t *rb;
+ size_t power_of_two = 0;
+
+ if(sz > 0)
+ {
+ power_of_two = sz;
+ power_of_two |= power_of_two>>1;
+ power_of_two |= power_of_two>>2;
+ power_of_two |= power_of_two>>4;
+ power_of_two |= power_of_two>>8;
+ power_of_two |= power_of_two>>16;
+#if SIZE_MAX > UINT_MAX
+ power_of_two |= power_of_two>>32;
+#endif
+ }
+ power_of_two++;
+ if(power_of_two < sz) return NULL;
+
+ rb = al_malloc(16, sizeof(*rb) + power_of_two*elem_sz);
+ if(!rb) return NULL;
+
+ ATOMIC_INIT(&rb->write_ptr, 0);
+ ATOMIC_INIT(&rb->read_ptr, 0);
+ rb->size = limit_writes ? sz : power_of_two;
+ rb->size_mask = power_of_two - 1;
+ rb->elem_size = elem_sz;
+ return rb;
+}
+
+void ll_ringbuffer_free(ll_ringbuffer_t *rb)
+{
+ al_free(rb);
+}
+
+void ll_ringbuffer_reset(ll_ringbuffer_t *rb)
+{
+ ATOMIC_STORE(&rb->write_ptr, 0, almemory_order_release);
+ ATOMIC_STORE(&rb->read_ptr, 0, almemory_order_release);
+ memset(rb->buf, 0, (rb->size_mask+1)*rb->elem_size);
+}
+
+
+size_t ll_ringbuffer_read_space(const ll_ringbuffer_t *rb)
+{
+ size_t w = ATOMIC_LOAD(&CONST_CAST(ll_ringbuffer_t*,rb)->write_ptr, almemory_order_acquire);
+ size_t r = ATOMIC_LOAD(&CONST_CAST(ll_ringbuffer_t*,rb)->read_ptr, almemory_order_acquire);
+ return (w-r) & rb->size_mask;
+}
+
+size_t ll_ringbuffer_write_space(const ll_ringbuffer_t *rb)
+{
+ size_t w = ATOMIC_LOAD(&CONST_CAST(ll_ringbuffer_t*,rb)->write_ptr, almemory_order_acquire);
+ size_t r = ATOMIC_LOAD(&CONST_CAST(ll_ringbuffer_t*,rb)->read_ptr, almemory_order_acquire);
+ w = (r-w-1) & rb->size_mask;
+ return (w > rb->size) ? rb->size : w;
+}
+
+
+size_t ll_ringbuffer_read(ll_ringbuffer_t *rb, char *dest, size_t cnt)
+{
+ size_t read_ptr;
+ size_t free_cnt;
+ size_t cnt2;
+ size_t to_read;
+ size_t n1, n2;
+
+ free_cnt = ll_ringbuffer_read_space(rb);
+ if(free_cnt == 0) return 0;
+
+ to_read = (cnt > free_cnt) ? free_cnt : cnt;
+ read_ptr = ATOMIC_LOAD(&rb->read_ptr, almemory_order_relaxed) & rb->size_mask;
+
+ cnt2 = read_ptr + to_read;
+ if(cnt2 > rb->size_mask+1)
+ {
+ n1 = rb->size_mask+1 - read_ptr;
+ n2 = cnt2 & rb->size_mask;
+ }
+ else
+ {
+ n1 = to_read;
+ n2 = 0;
+ }
+
+ memcpy(dest, &rb->buf[read_ptr*rb->elem_size], n1*rb->elem_size);
+ read_ptr += n1;
+ if(n2)
+ {
+ memcpy(dest + n1*rb->elem_size, &rb->buf[(read_ptr&rb->size_mask)*rb->elem_size],
+ n2*rb->elem_size);
+ read_ptr += n2;
+ }
+ ATOMIC_STORE(&rb->read_ptr, read_ptr, almemory_order_release);
+ return to_read;
+}
+
+size_t ll_ringbuffer_peek(ll_ringbuffer_t *rb, char *dest, size_t cnt)
+{
+ size_t free_cnt;
+ size_t cnt2;
+ size_t to_read;
+ size_t n1, n2;
+ size_t read_ptr;
+
+ free_cnt = ll_ringbuffer_read_space(rb);
+ if(free_cnt == 0) return 0;
+
+ to_read = (cnt > free_cnt) ? free_cnt : cnt;
+ read_ptr = ATOMIC_LOAD(&rb->read_ptr, almemory_order_relaxed) & rb->size_mask;
+
+ cnt2 = read_ptr + to_read;
+ if(cnt2 > rb->size_mask+1)
+ {
+ n1 = rb->size_mask+1 - read_ptr;
+ n2 = cnt2 & rb->size_mask;
+ }
+ else
+ {
+ n1 = to_read;
+ n2 = 0;
+ }
+
+ memcpy(dest, &rb->buf[read_ptr*rb->elem_size], n1*rb->elem_size);
+ if(n2)
+ {
+ read_ptr += n1;
+ memcpy(dest + n1*rb->elem_size, &rb->buf[(read_ptr&rb->size_mask)*rb->elem_size],
+ n2*rb->elem_size);
+ }
+ return to_read;
+}
+
+size_t ll_ringbuffer_write(ll_ringbuffer_t *rb, const char *src, size_t cnt)
+{
+ size_t write_ptr;
+ size_t free_cnt;
+ size_t cnt2;
+ size_t to_write;
+ size_t n1, n2;
+
+ free_cnt = ll_ringbuffer_write_space(rb);
+ if(free_cnt == 0) return 0;
+
+ to_write = (cnt > free_cnt) ? free_cnt : cnt;
+ write_ptr = ATOMIC_LOAD(&rb->write_ptr, almemory_order_relaxed) & rb->size_mask;
+
+ cnt2 = write_ptr + to_write;
+ if(cnt2 > rb->size_mask+1)
+ {
+ n1 = rb->size_mask+1 - write_ptr;
+ n2 = cnt2 & rb->size_mask;
+ }
+ else
+ {
+ n1 = to_write;
+ n2 = 0;
+ }
+
+ memcpy(&rb->buf[write_ptr*rb->elem_size], src, n1*rb->elem_size);
+ write_ptr += n1;
+ if(n2)
+ {
+ memcpy(&rb->buf[(write_ptr&rb->size_mask)*rb->elem_size], src + n1*rb->elem_size,
+ n2*rb->elem_size);
+ write_ptr += n2;
+ }
+ ATOMIC_STORE(&rb->write_ptr, write_ptr, almemory_order_release);
+ return to_write;
+}
+
+
+void ll_ringbuffer_read_advance(ll_ringbuffer_t *rb, size_t cnt)
+{
+ ATOMIC_ADD(&rb->read_ptr, cnt, almemory_order_acq_rel);
+}
+
+void ll_ringbuffer_write_advance(ll_ringbuffer_t *rb, size_t cnt)
+{
+ ATOMIC_ADD(&rb->write_ptr, cnt, almemory_order_acq_rel);
+}
+
+
+void ll_ringbuffer_get_read_vector(const ll_ringbuffer_t *rb, ll_ringbuffer_data_t vec[2])
+{
+ size_t free_cnt;
+ size_t cnt2;
+ size_t w, r;
+
+ w = ATOMIC_LOAD(&CONST_CAST(ll_ringbuffer_t*,rb)->write_ptr, almemory_order_acquire);
+ r = ATOMIC_LOAD(&CONST_CAST(ll_ringbuffer_t*,rb)->read_ptr, almemory_order_acquire);
+ w &= rb->size_mask;
+ r &= rb->size_mask;
+ free_cnt = (w-r) & rb->size_mask;
+
+ cnt2 = r + free_cnt;
+ if(cnt2 > rb->size_mask+1)
+ {
+ /* 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_mask+1 - 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;
+ }
+}
+
+void ll_ringbuffer_get_write_vector(const ll_ringbuffer_t *rb, ll_ringbuffer_data_t vec[2])
+{
+ size_t free_cnt;
+ size_t cnt2;
+ size_t w, r;
+
+ w = ATOMIC_LOAD(&CONST_CAST(ll_ringbuffer_t*,rb)->write_ptr, almemory_order_acquire);
+ r = ATOMIC_LOAD(&CONST_CAST(ll_ringbuffer_t*,rb)->read_ptr, almemory_order_acquire);
+ w &= rb->size_mask;
+ r &= rb->size_mask;
+ free_cnt = (r-w-1) & rb->size_mask;
+ if(free_cnt > rb->size) free_cnt = rb->size;
+
+ cnt2 = w + free_cnt;
+ if(cnt2 > rb->size_mask+1)
+ {
+ /* 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_mask+1 - 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;
+ }
+}
diff --git a/Alc/ringbuffer.h b/Alc/ringbuffer.h
new file mode 100644
index 00000000..0d05ec84
--- /dev/null
+++ b/Alc/ringbuffer.h
@@ -0,0 +1,77 @@
+#ifndef RINGBUFFER_H
+#define RINGBUFFER_H
+
+#include <stddef.h>
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct ll_ringbuffer ll_ringbuffer_t;
+typedef struct ll_ringbuffer_data {
+ char *buf;
+ size_t len;
+} ll_ringbuffer_data_t;
+
+
+/**
+ * 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 (even if it is
+ * already a power of two, to ensure the requested amount can be written).
+ */
+ll_ringbuffer_t *ll_ringbuffer_create(size_t sz, size_t elem_sz, int limit_writes);
+/** Free all data associated with the ringbuffer `rb'. */
+void ll_ringbuffer_free(ll_ringbuffer_t *rb);
+/** Reset the read and write pointers to zero. This is not thread safe. */
+void ll_ringbuffer_reset(ll_ringbuffer_t *rb);
+
+/**
+ * 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[2]);
+/**
+ * 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[2]);
+
+/**
+ * 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);
+/**
+ * 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);
+/**
+ * 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);
+/** Advance the read pointer `cnt' places. */
+void ll_ringbuffer_read_advance(ll_ringbuffer_t *rb, size_t cnt);
+
+/**
+ * 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);
+/**
+ * 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);
+/** Advance the write pointer `cnt' places. */
+void ll_ringbuffer_write_advance(ll_ringbuffer_t *rb, size_t cnt);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* RINGBUFFER_H */
diff --git a/Alc/uhjfilter.c b/Alc/uhjfilter.c
new file mode 100644
index 00000000..42b0bc40
--- /dev/null
+++ b/Alc/uhjfilter.c
@@ -0,0 +1,120 @@
+
+#include "config.h"
+
+#include "alu.h"
+#include "uhjfilter.h"
+
+/* This is the maximum number of samples processed for each inner loop
+ * iteration. */
+#define MAX_UPDATE_SAMPLES 128
+
+
+static const ALfloat Filter1CoeffSqr[4] = {
+ 0.479400865589f, 0.876218493539f, 0.976597589508f, 0.997499255936f
+};
+static const ALfloat Filter2CoeffSqr[4] = {
+ 0.161758498368f, 0.733028932341f, 0.945349700329f, 0.990599156685f
+};
+
+static void allpass_process(AllPassState *state, ALfloat *restrict dst, const ALfloat *restrict src, const ALfloat aa, ALsizei todo)
+{
+ ALfloat z1 = state->z[0];
+ ALfloat z2 = state->z[1];
+ ALsizei i;
+
+ for(i = 0;i < todo;i++)
+ {
+ ALfloat input = src[i];
+ ALfloat output = input*aa + z1;
+ z1 = z2; z2 = output*aa - input;
+ dst[i] = output;
+ }
+
+ state->z[0] = z1;
+ state->z[1] = z2;
+}
+
+
+/* NOTE: There seems to be a bit of an inconsistency in how this encoding is
+ * supposed to work. Some references, such as
+ *
+ * http://members.tripod.com/martin_leese/Ambisonic/UHJ_file_format.html
+ *
+ * specify a pre-scaling of sqrt(2) on the W channel input, while other
+ * references, such as
+ *
+ * https://en.wikipedia.org/wiki/Ambisonic_UHJ_format#Encoding.5B1.5D
+ * and
+ * https://wiki.xiph.org/Ambisonics#UHJ_format
+ *
+ * do not. The sqrt(2) scaling is in line with B-Format decoder coefficients
+ * which include such a scaling for the W channel input, however the original
+ * source for this equation is a 1985 paper by Michael Gerzon, which does not
+ * apparently include the scaling. Applying the extra scaling creates a louder
+ * result with a narrower stereo image compared to not scaling, and I don't
+ * know which is the intended result.
+ */
+
+void EncodeUhj2(Uhj2Encoder *enc, ALfloat *restrict LeftOut, ALfloat *restrict RightOut, ALfloat (*restrict InSamples)[BUFFERSIZE], ALsizei SamplesToDo)
+{
+ ALfloat D[MAX_UPDATE_SAMPLES], S[MAX_UPDATE_SAMPLES];
+ ALfloat temp[2][MAX_UPDATE_SAMPLES];
+ ALsizei base, i;
+
+ ASSUME(SamplesToDo > 0);
+
+ for(base = 0;base < SamplesToDo;)
+ {
+ ALsizei todo = mini(SamplesToDo - base, MAX_UPDATE_SAMPLES);
+ ASSUME(todo > 0);
+
+ /* D = 0.6554516*Y */
+ for(i = 0;i < todo;i++)
+ temp[0][i] = 0.6554516f*InSamples[2][base+i];
+ allpass_process(&enc->Filter1_Y[0], temp[1], temp[0], Filter1CoeffSqr[0], todo);
+ allpass_process(&enc->Filter1_Y[1], temp[0], temp[1], Filter1CoeffSqr[1], todo);
+ allpass_process(&enc->Filter1_Y[2], temp[1], temp[0], Filter1CoeffSqr[2], todo);
+ allpass_process(&enc->Filter1_Y[3], temp[0], temp[1], Filter1CoeffSqr[3], todo);
+ /* NOTE: Filter1 requires a 1 sample delay for the final output, so
+ * take the last processed sample from the previous run as the first
+ * output sample.
+ */
+ D[0] = enc->LastY;
+ for(i = 1;i < todo;i++)
+ D[i] = temp[0][i-1];
+ enc->LastY = temp[0][i-1];
+
+ /* D += j(-0.3420201*W + 0.5098604*X) */
+ for(i = 0;i < todo;i++)
+ temp[0][i] = -0.3420201f*InSamples[0][base+i] +
+ 0.5098604f*InSamples[1][base+i];
+ allpass_process(&enc->Filter2_WX[0], temp[1], temp[0], Filter2CoeffSqr[0], todo);
+ allpass_process(&enc->Filter2_WX[1], temp[0], temp[1], Filter2CoeffSqr[1], todo);
+ allpass_process(&enc->Filter2_WX[2], temp[1], temp[0], Filter2CoeffSqr[2], todo);
+ allpass_process(&enc->Filter2_WX[3], temp[0], temp[1], Filter2CoeffSqr[3], todo);
+ for(i = 0;i < todo;i++)
+ D[i] += temp[0][i];
+
+ /* S = 0.9396926*W + 0.1855740*X */
+ for(i = 0;i < todo;i++)
+ temp[0][i] = 0.9396926f*InSamples[0][base+i] +
+ 0.1855740f*InSamples[1][base+i];
+ allpass_process(&enc->Filter1_WX[0], temp[1], temp[0], Filter1CoeffSqr[0], todo);
+ allpass_process(&enc->Filter1_WX[1], temp[0], temp[1], Filter1CoeffSqr[1], todo);
+ allpass_process(&enc->Filter1_WX[2], temp[1], temp[0], Filter1CoeffSqr[2], todo);
+ allpass_process(&enc->Filter1_WX[3], temp[0], temp[1], Filter1CoeffSqr[3], todo);
+ S[0] = enc->LastWX;
+ for(i = 1;i < todo;i++)
+ S[i] = temp[0][i-1];
+ enc->LastWX = temp[0][i-1];
+
+ /* Left = (S + D)/2.0 */
+ for(i = 0;i < todo;i++)
+ *(LeftOut++) += (S[i] + D[i]) * 0.5f;
+ /* Right = (S - D)/2.0 */
+ for(i = 0;i < todo;i++)
+ *(RightOut++) += (S[i] - D[i]) * 0.5f;
+
+ base += todo;
+ }
+}
diff --git a/Alc/uhjfilter.h b/Alc/uhjfilter.h
new file mode 100644
index 00000000..e773e0a7
--- /dev/null
+++ b/Alc/uhjfilter.h
@@ -0,0 +1,49 @@
+#ifndef UHJFILTER_H
+#define UHJFILTER_H
+
+#include "AL/al.h"
+
+#include "alMain.h"
+
+typedef struct AllPassState {
+ ALfloat z[2];
+} AllPassState;
+
+/* Encoding 2-channel UHJ from B-Format is done as:
+ *
+ * S = 0.9396926*W + 0.1855740*X
+ * D = j(-0.3420201*W + 0.5098604*X) + 0.6554516*Y
+ *
+ * Left = (S + D)/2.0
+ * Right = (S - D)/2.0
+ *
+ * where j is a wide-band +90 degree phase shift.
+ *
+ * The phase shift is done using a Hilbert transform, described here:
+ * https://web.archive.org/web/20060708031958/http://www.biochem.oulu.fi/~oniemita/dsp/hilbert/
+ * It works using 2 sets of 4 chained filters. The first filter chain produces
+ * a phase shift of varying magnitude over a wide range of frequencies, while
+ * the second filter chain produces a phase shift 90 degrees ahead of the
+ * first over the same range.
+ *
+ * Combining these two stages requires the use of three filter chains. S-
+ * channel output uses a Filter1 chain on the W and X channel mix, while the D-
+ * channel output uses a Filter1 chain on the Y channel plus a Filter2 chain on
+ * the W and X channel mix. This results in the W and X input mix on the D-
+ * channel output having the required +90 degree phase shift relative to the
+ * other inputs.
+ */
+
+typedef struct Uhj2Encoder {
+ AllPassState Filter1_Y[4];
+ AllPassState Filter2_WX[4];
+ AllPassState Filter1_WX[4];
+ ALfloat LastY, LastWX;
+} Uhj2Encoder;
+
+/* Encodes a 2-channel UHJ (stereo-compatible) signal from a B-Format input
+ * signal. The input must use FuMa channel ordering and scaling.
+ */
+void EncodeUhj2(Uhj2Encoder *enc, ALfloat *restrict LeftOut, ALfloat *restrict RightOut, ALfloat (*restrict InSamples)[BUFFERSIZE], ALsizei SamplesToDo);
+
+#endif /* UHJFILTER_H */
diff --git a/Alc/vector.h b/Alc/vector.h
index c1fc925d..ed9acfb0 100644
--- a/Alc/vector.h
+++ b/Alc/vector.h
@@ -5,11 +5,8 @@
#include <AL/al.h>
-/* "Base" vector type, designed to alias with the actual vector types. */
-typedef struct vector__s {
- size_t Capacity;
- size_t Size;
-} *vector_;
+#include "almalloc.h"
+
#define TYPEDEF_VECTOR(T, N) typedef struct { \
size_t Capacity; \
@@ -27,38 +24,48 @@ typedef const _##N* const_##N;
#define VECTOR_INIT(_x) do { (_x) = NULL; } while(0)
#define VECTOR_INIT_STATIC() NULL
-#define VECTOR_DEINIT(_x) do { free((_x)); (_x) = NULL; } while(0)
-
-/* Helper to increase a vector's reserve. Do not call directly. */
-ALboolean vector_reserve(char *ptr, size_t base_size, size_t obj_size, size_t obj_count, ALboolean exact);
-#define VECTOR_RESERVE(_x, _c) (vector_reserve((char*)&(_x), sizeof(*(_x)), sizeof((_x)->Data[0]), (_c), AL_TRUE))
-
-ALboolean vector_resize(char *ptr, size_t base_size, size_t obj_size, size_t obj_count);
-#define VECTOR_RESIZE(_x, _c) (vector_resize((char*)&(_x), sizeof(*(_x)), sizeof((_x)->Data[0]), (_c)))
+#define VECTOR_DEINIT(_x) do { al_free((_x)); (_x) = NULL; } while(0)
+
+#define VECTOR_RESIZE(_x, _s, _c) do { \
+ size_t _size = (_s); \
+ size_t _cap = (_c); \
+ if(_size > _cap) \
+ _cap = _size; \
+ \
+ if(!(_x) && _cap == 0) \
+ break; \
+ \
+ if(((_x) ? (_x)->Capacity : 0) < _cap) \
+ { \
+ ptrdiff_t data_offset = (_x) ? (char*)((_x)->Data) - (char*)(_x) : \
+ sizeof(*(_x)); \
+ size_t old_size = ((_x) ? (_x)->Size : 0); \
+ void *temp; \
+ \
+ temp = al_calloc(16, data_offset + sizeof((_x)->Data[0])*_cap); \
+ assert(temp != NULL); \
+ if((_x)) \
+ memcpy(((char*)temp)+data_offset, (_x)->Data, \
+ sizeof((_x)->Data[0])*old_size); \
+ \
+ al_free((_x)); \
+ (_x) = temp; \
+ (_x)->Capacity = _cap; \
+ } \
+ (_x)->Size = _size; \
+} while(0) \
#define VECTOR_CAPACITY(_x) ((_x) ? (_x)->Capacity : 0)
#define VECTOR_SIZE(_x) ((_x) ? (_x)->Size : 0)
-#define VECTOR_ITER_BEGIN(_x) ((_x) ? (_x)->Data + 0 : NULL)
-#define VECTOR_ITER_END(_x) ((_x) ? (_x)->Data + (_x)->Size : NULL)
-
-ALboolean vector_insert(char *ptr, size_t base_size, size_t obj_size, void *ins_pos, const void *datstart, const void *datend);
-#ifdef __GNUC__
-#define TYPE_CHECK(T1, T2) __builtin_types_compatible_p(T1, T2)
-#define VECTOR_INSERT(_x, _i, _s, _e) __extension__({ \
- ALboolean _r; \
- static_assert(TYPE_CHECK(__typeof((_x)->Data[0]), __typeof(*(_i))), "Incompatible insertion iterator"); \
- static_assert(TYPE_CHECK(__typeof((_x)->Data[0]), __typeof(*(_s))), "Incompatible insertion source type"); \
- static_assert(TYPE_CHECK(__typeof(*(_s)), __typeof(*(_e))), "Incompatible iterator sources"); \
- _r = vector_insert((char*)&(_x), sizeof(*(_x)), sizeof((_x)->Data[0]), (_i), (_s), (_e)); \
- _r; \
-})
-#else
-#define VECTOR_INSERT(_x, _i, _s, _e) (vector_insert((char*)&(_x), sizeof(*(_x)), sizeof((_x)->Data[0]), (_i), (_s), (_e)))
-#endif
-
-#define VECTOR_PUSH_BACK(_x, _obj) (vector_reserve((char*)&(_x), sizeof(*(_x)), sizeof((_x)->Data[0]), VECTOR_SIZE(_x)+1, AL_FALSE) && \
- (((_x)->Data[(_x)->Size++] = (_obj)),AL_TRUE))
+#define VECTOR_BEGIN(_x) ((_x) ? (_x)->Data + 0 : NULL)
+#define VECTOR_END(_x) ((_x) ? (_x)->Data + (_x)->Size : NULL)
+
+#define VECTOR_PUSH_BACK(_x, _obj) do { \
+ size_t _pbsize = VECTOR_SIZE(_x)+1; \
+ VECTOR_RESIZE(_x, _pbsize, _pbsize); \
+ (_x)->Data[(_x)->Size-1] = (_obj); \
+} while(0)
#define VECTOR_POP_BACK(_x) ((void)((_x)->Size--))
#define VECTOR_BACK(_x) ((_x)->Data[(_x)->Size-1])
@@ -67,22 +74,15 @@ ALboolean vector_insert(char *ptr, size_t base_size, size_t obj_size, void *ins_
#define VECTOR_ELEM(_x, _o) ((_x)->Data[(_o)])
#define VECTOR_FOR_EACH(_t, _x, _f) do { \
- _t *_iter = VECTOR_ITER_BEGIN((_x)); \
- _t *_end = VECTOR_ITER_END((_x)); \
+ _t *_iter = VECTOR_BEGIN((_x)); \
+ _t *_end = VECTOR_END((_x)); \
for(;_iter != _end;++_iter) \
_f(_iter); \
} while(0)
-#define VECTOR_FOR_EACH_PARAMS(_t, _x, _f, ...) do { \
- _t *_iter = VECTOR_ITER_BEGIN((_x)); \
- _t *_end = VECTOR_ITER_END((_x)); \
- for(;_iter != _end;++_iter) \
- _f(__VA_ARGS__, _iter); \
-} while(0)
-
#define VECTOR_FIND_IF(_i, _t, _x, _f) do { \
- _t *_iter = VECTOR_ITER_BEGIN((_x)); \
- _t *_end = VECTOR_ITER_END((_x)); \
+ _t *_iter = VECTOR_BEGIN((_x)); \
+ _t *_end = VECTOR_END((_x)); \
for(;_iter != _end;++_iter) \
{ \
if(_f(_iter)) \
@@ -91,15 +91,4 @@ ALboolean vector_insert(char *ptr, size_t base_size, size_t obj_size, void *ins_
(_i) = _iter; \
} while(0)
-#define VECTOR_FIND_IF_PARMS(_i, _t, _x, _f, ...) do { \
- _t *_iter = VECTOR_ITER_BEGIN((_x)); \
- _t *_end = VECTOR_ITER_END((_x)); \
- for(;_iter != _end;++_iter) \
- { \
- if(_f(__VA_ARGS__, _iter)) \
- break; \
- } \
- (_i) = _iter; \
-} while(0)
-
#endif /* AL_VECTOR_H */