mirror of
https://github.com/love2d/megasource.git
synced 2026-08-12 00:20:55 +02:00
Update OpenAL Soft to 1.19.1.
This commit is contained in:
+1
-1
@@ -160,7 +160,7 @@ set(MEGA_LIBTHEORA_VER "1.1.1")
|
||||
set(MEGA_MPG123_VER "1.25.6")
|
||||
set(MEGA_FREETYPE_VER "2.8.1")
|
||||
set(MEGA_SDL2_VER "2.0.7-2088cd828335")
|
||||
set(MEGA_OPENAL_VER "1.18.2")
|
||||
set(MEGA_OPENAL_VER "1.19.1")
|
||||
set(MEGA_MODPLUG_VER "0.8.8.4")
|
||||
|
||||
set(SKIP_INSTALL_ALL TRUE)
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
build
|
||||
winbuild
|
||||
win64build
|
||||
include/SLES
|
||||
include/sndio.h
|
||||
include/sys
|
||||
build*/
|
||||
winbuild/
|
||||
win64build/
|
||||
openal-soft.kdev4
|
||||
.kdev4/
|
||||
|
||||
+101
-118
@@ -75,14 +75,14 @@ static struct BackendInfo BackendList[] = {
|
||||
#ifdef HAVE_COREAUDIO
|
||||
{ "core", ALCcoreAudioBackendFactory_getFactory },
|
||||
#endif
|
||||
#ifdef HAVE_OSS
|
||||
{ "oss", ALCossBackendFactory_getFactory },
|
||||
#endif
|
||||
#ifdef HAVE_SOLARIS
|
||||
{ "solaris", ALCsolarisBackendFactory_getFactory },
|
||||
#endif
|
||||
#ifdef HAVE_SNDIO
|
||||
{ "sndio", ALCsndioBackendFactory_getFactory },
|
||||
{ "sndio", SndioBackendFactory_getFactory },
|
||||
#endif
|
||||
#ifdef HAVE_OSS
|
||||
{ "oss", ALCossBackendFactory_getFactory },
|
||||
#endif
|
||||
#ifdef HAVE_QSA
|
||||
{ "qsa", ALCqsaBackendFactory_getFactory },
|
||||
@@ -550,14 +550,12 @@ static const struct {
|
||||
DECL(AL_EFFECT_ECHO),
|
||||
DECL(AL_EFFECT_FLANGER),
|
||||
DECL(AL_EFFECT_PITCH_SHIFTER),
|
||||
#if 0
|
||||
DECL(AL_EFFECT_FREQUENCY_SHIFTER),
|
||||
#if 0
|
||||
DECL(AL_EFFECT_VOCAL_MORPHER),
|
||||
#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),
|
||||
@@ -632,6 +630,10 @@ static const struct {
|
||||
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),
|
||||
@@ -654,6 +656,11 @@ static const struct {
|
||||
|
||||
DECL(AL_DEDICATED_GAIN),
|
||||
|
||||
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),
|
||||
@@ -721,6 +728,7 @@ static const ALchar alExtList[] =
|
||||
"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 "
|
||||
@@ -1157,75 +1165,6 @@ static void alc_initconfig(void)
|
||||
}
|
||||
#define DO_INITCONFIG() alcall_once(&alc_config_once, alc_initconfig)
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#include <jni.h>
|
||||
|
||||
static JavaVM *gJavaVM;
|
||||
static pthread_key_t gJVMThreadKey;
|
||||
|
||||
static void CleanupJNIEnv(void* UNUSED(ptr))
|
||||
{
|
||||
JCALL0(gJavaVM,DetachCurrentThread)();
|
||||
}
|
||||
|
||||
void *Android_GetJNIEnv(void)
|
||||
{
|
||||
if(!gJavaVM)
|
||||
{
|
||||
WARN("gJavaVM is NULL!\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* http://developer.android.com/guide/practices/jni.html
|
||||
*
|
||||
* All threads are Linux threads, scheduled by the kernel. They're usually
|
||||
* started from managed code (using Thread.start), but they can also be
|
||||
* created elsewhere and then attached to the JavaVM. For example, a thread
|
||||
* started with pthread_create can be attached with the JNI
|
||||
* AttachCurrentThread or AttachCurrentThreadAsDaemon functions. Until a
|
||||
* thread is attached, it has no JNIEnv, and cannot make JNI calls.
|
||||
* Attaching a natively-created thread causes a java.lang.Thread object to
|
||||
* be constructed and added to the "main" ThreadGroup, making it visible to
|
||||
* the debugger. Calling AttachCurrentThread on an already-attached thread
|
||||
* is a no-op.
|
||||
*/
|
||||
JNIEnv *env = pthread_getspecific(gJVMThreadKey);
|
||||
if(!env)
|
||||
{
|
||||
int status = JCALL(gJavaVM,AttachCurrentThread)(&env, NULL);
|
||||
if(status < 0)
|
||||
{
|
||||
ERR("Failed to attach current thread\n");
|
||||
return NULL;
|
||||
}
|
||||
pthread_setspecific(gJVMThreadKey, env);
|
||||
}
|
||||
return env;
|
||||
}
|
||||
|
||||
/* Automatically called by JNI. */
|
||||
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void* UNUSED(reserved))
|
||||
{
|
||||
void *env;
|
||||
int err;
|
||||
|
||||
gJavaVM = jvm;
|
||||
if(JCALL(gJavaVM,GetEnv)(&env, JNI_VERSION_1_4) != JNI_OK)
|
||||
{
|
||||
ERR("Failed to get JNIEnv with JNI_VERSION_1_4\n");
|
||||
return JNI_ERR;
|
||||
}
|
||||
|
||||
/* Create gJVMThreadKey so we can keep track of the JNIEnv assigned to each
|
||||
* thread. The JNIEnv *must* be detached before the thread is destroyed.
|
||||
*/
|
||||
if((err=pthread_key_create(&gJVMThreadKey, CleanupJNIEnv)) != 0)
|
||||
ERR("pthread_key_create failed: %d\n", err);
|
||||
pthread_setspecific(gJVMThreadKey, env);
|
||||
return JNI_VERSION_1_4;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/************************************************
|
||||
* Library deinitialization
|
||||
@@ -1306,7 +1245,7 @@ static void ProbeDevices(al_string *list, struct BackendInfo *backendinfo, enum
|
||||
if(backendinfo->getFactory)
|
||||
{
|
||||
ALCbackendFactory *factory = backendinfo->getFactory();
|
||||
V(factory,probe)(type);
|
||||
V(factory,probe)(type, list);
|
||||
}
|
||||
|
||||
UnlockLists();
|
||||
@@ -1316,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)
|
||||
alstr_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
|
||||
@@ -1707,10 +1635,11 @@ static void alcSetError(ALCdevice *device, ALCenum errorCode)
|
||||
}
|
||||
|
||||
|
||||
struct Compressor *CreateDeviceLimiter(const ALCdevice *device)
|
||||
static struct Compressor *CreateDeviceLimiter(const ALCdevice *device, const ALfloat threshold)
|
||||
{
|
||||
return CompressorInit(0.0f, 0.0f, AL_FALSE, AL_TRUE, 0.0f, 0.0f, 0.5f, 2.0f,
|
||||
0.0f, -3.0f, 3.0f, device->Frequency);
|
||||
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
|
||||
@@ -1737,7 +1666,7 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
|
||||
{
|
||||
enum HrtfRequestMode hrtf_userreq = Hrtf_Default;
|
||||
enum HrtfRequestMode hrtf_appreq = Hrtf_Default;
|
||||
ALCenum gainLimiter = device->Limiter ? ALC_TRUE : ALC_FALSE;
|
||||
ALCenum gainLimiter = device->LimiterState;
|
||||
const ALsizei old_sends = device->NumAuxSends;
|
||||
ALsizei new_sends = device->NumAuxSends;
|
||||
enum DevFmtChannels oldChans;
|
||||
@@ -2053,6 +1982,7 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
|
||||
device->RealOut.NumChannels = 0;
|
||||
|
||||
UpdateClockBase(device);
|
||||
device->FixedLatency = 0;
|
||||
|
||||
device->DitherSeed = DITHER_RNG_SEED;
|
||||
|
||||
@@ -2213,9 +2143,12 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if(depth > 24)
|
||||
depth = 24;
|
||||
device->DitherDepth = (depth > 0) ? powf(2.0f, (ALfloat)(depth-1)) : 0.0f;
|
||||
|
||||
if(depth > 0)
|
||||
{
|
||||
depth = clampi(depth, 2, 24);
|
||||
device->DitherDepth = powf(2.0f, (ALfloat)(depth-1));
|
||||
}
|
||||
}
|
||||
if(!(device->DitherDepth > 0.0f))
|
||||
TRACE("Dithering disabled\n");
|
||||
@@ -2223,19 +2156,57 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
|
||||
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. We default to on, so ALC_DONT_CARE_SOFT is the same as
|
||||
* ALC_TRUE.
|
||||
* 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)
|
||||
{
|
||||
case DevFmtByte:
|
||||
case DevFmtUByte:
|
||||
case DevFmtShort:
|
||||
case DevFmtUShort:
|
||||
case DevFmtInt:
|
||||
case DevFmtUInt:
|
||||
gainLimiter = ALC_TRUE;
|
||||
break;
|
||||
case DevFmtFloat:
|
||||
gainLimiter = ALC_FALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(gainLimiter != ALC_FALSE)
|
||||
{
|
||||
if(!device->Limiter || device->Frequency != GetCompressorSampleRate(device->Limiter))
|
||||
ALfloat thrshld = 1.0f;
|
||||
switch(device->FmtType)
|
||||
{
|
||||
al_free(device->Limiter);
|
||||
device->Limiter = CreateDeviceLimiter(device);
|
||||
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;
|
||||
}
|
||||
if(device->DitherDepth > 0.0f)
|
||||
thrshld -= 1.0f / device->DitherDepth;
|
||||
|
||||
al_free(device->Limiter);
|
||||
device->Limiter = CreateDeviceLimiter(device, log10f(thrshld) * 20.0f);
|
||||
device->FixedLatency += (ALuint)(GetCompressorLookAhead(device->Limiter) *
|
||||
DEVICE_CLOCK_RES / device->Frequency);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2246,6 +2217,8 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
|
||||
|
||||
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.
|
||||
*/
|
||||
@@ -2400,11 +2373,13 @@ static void InitDevice(ALCdevice *device, enum DeviceType type)
|
||||
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;
|
||||
@@ -2638,7 +2613,6 @@ static ALvoid InitContext(ALCcontext *Context)
|
||||
Context->MetersPerUnit = AL_DEFAULT_METERS_PER_UNIT;
|
||||
ATOMIC_FLAG_TEST_AND_SET(&Context->PropsClean, almemory_order_relaxed);
|
||||
ATOMIC_INIT(&Context->DeferUpdates, AL_FALSE);
|
||||
almtx_init(&Context->EventThrdLock, almtx_plain);
|
||||
alsem_init(&Context->EventSem, 0);
|
||||
Context->AsyncEvents = NULL;
|
||||
ATOMIC_INIT(&Context->EnabledEvts, 0);
|
||||
@@ -2665,6 +2639,11 @@ static ALvoid InitContext(ALCcontext *Context)
|
||||
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");
|
||||
}
|
||||
|
||||
|
||||
@@ -2773,17 +2752,7 @@ static void FreeContext(ALCcontext *context)
|
||||
}
|
||||
TRACE("Freed "SZFMT" listener property object%s\n", count, (count==1)?"":"s");
|
||||
|
||||
if(ATOMIC_EXCHANGE(&context->EnabledEvts, 0, almemory_order_acq_rel))
|
||||
{
|
||||
static const AsyncEvent kill_evt = { 0 };
|
||||
while(ll_ringbuffer_write(context->AsyncEvents, (const char*)&kill_evt, 1) == 0)
|
||||
althrd_yield();
|
||||
alsem_post(&context->EventSem);
|
||||
althrd_join(context->EventThread, NULL);
|
||||
}
|
||||
|
||||
almtx_destroy(&context->EventCbLock);
|
||||
almtx_destroy(&context->EventThrdLock);
|
||||
alsem_destroy(&context->EventSem);
|
||||
|
||||
ll_ringbuffer_free(context->AsyncEvents);
|
||||
@@ -2807,6 +2776,7 @@ static void FreeContext(ALCcontext *context)
|
||||
*/
|
||||
static bool ReleaseContext(ALCcontext *context, ALCdevice *device)
|
||||
{
|
||||
static const AsyncEvent kill_evt = ASYNC_EVENT(EventType_KillThread);
|
||||
ALCcontext *origctx, *newhead;
|
||||
bool ret = true;
|
||||
|
||||
@@ -2839,6 +2809,16 @@ static bool ReleaseContext(ALCcontext *context, ALCdevice *device)
|
||||
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;
|
||||
}
|
||||
@@ -3620,7 +3600,7 @@ ALC_API void ALC_APIENTRY alcGetInteger64vSOFT(ALCdevice *device, ALCenum pname,
|
||||
values[i++] = ALC_OUTPUT_LIMITER_SOFT;
|
||||
values[i++] = device->Limiter ? ALC_TRUE : ALC_FALSE;
|
||||
|
||||
clock = V0(device->Backend,getClockLatency)();
|
||||
clock = GetClockLatency(device);
|
||||
values[i++] = ALC_DEVICE_CLOCK_SOFT;
|
||||
values[i++] = clock.ClockTime;
|
||||
|
||||
@@ -3646,7 +3626,7 @@ ALC_API void ALC_APIENTRY alcGetInteger64vSOFT(ALCdevice *device, ALCenum pname,
|
||||
|
||||
case ALC_DEVICE_LATENCY_SOFT:
|
||||
almtx_lock(&device->BackendLock);
|
||||
clock = V0(device->Backend,getClockLatency)();
|
||||
clock = GetClockLatency(device);
|
||||
almtx_unlock(&device->BackendLock);
|
||||
*values = clock.Latency;
|
||||
break;
|
||||
@@ -3657,7 +3637,7 @@ ALC_API void ALC_APIENTRY alcGetInteger64vSOFT(ALCdevice *device, ALCenum pname,
|
||||
else
|
||||
{
|
||||
almtx_lock(&device->BackendLock);
|
||||
clock = V0(device->Backend,getClockLatency)();
|
||||
clock = GetClockLatency(device);
|
||||
almtx_unlock(&device->BackendLock);
|
||||
values[0] = clock.ClockTime;
|
||||
values[1] = clock.Latency;
|
||||
@@ -4073,6 +4053,7 @@ ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *deviceName)
|
||||
device->IsHeadphones = AL_FALSE;
|
||||
device->AmbiLayout = AmbiLayout_Default;
|
||||
device->AmbiScale = AmbiNorm_Default;
|
||||
device->LimiterState = ALC_TRUE;
|
||||
device->NumUpdates = 3;
|
||||
device->UpdateSize = 1024;
|
||||
|
||||
@@ -4211,8 +4192,6 @@ ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *deviceName)
|
||||
ERR("Unsupported ambi-format: %s\n", fmt);
|
||||
}
|
||||
|
||||
device->Limiter = CreateDeviceLimiter(device);
|
||||
|
||||
{
|
||||
ALCdevice *head = ATOMIC_LOAD_SEQ(&DeviceList);
|
||||
do {
|
||||
@@ -4394,6 +4373,12 @@ ALC_API ALCboolean ALC_APIENTRY alcCaptureCloseDevice(ALCdevice *device)
|
||||
}
|
||||
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;
|
||||
@@ -4534,8 +4519,6 @@ ALC_API ALCdevice* ALC_APIENTRY alcLoopbackOpenDeviceSOFT(const ALCchar *deviceN
|
||||
// Open the "backend"
|
||||
V(device->Backend,open)("Loopback");
|
||||
|
||||
device->Limiter = CreateDeviceLimiter(device);
|
||||
|
||||
{
|
||||
ALCdevice *head = ATOMIC_LOAD_SEQ(&DeviceList);
|
||||
do {
|
||||
|
||||
+134
-143
@@ -39,6 +39,7 @@
|
||||
#include "bformatdec.h"
|
||||
#include "static_assert.h"
|
||||
#include "ringbuffer.h"
|
||||
#include "filters/splitter.h"
|
||||
|
||||
#include "mixer/defs.h"
|
||||
#include "fpu_modes.h"
|
||||
@@ -144,16 +145,6 @@ static inline HrtfDirectMixerFunc SelectHrtfMixer(void)
|
||||
}
|
||||
|
||||
|
||||
/* Prior to VS2013, MSVC lacks the round() family of functions. */
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1800
|
||||
static float roundf(float val)
|
||||
{
|
||||
if(val < 0.0f)
|
||||
return ceilf(val-0.5f);
|
||||
return floorf(val+0.5f);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* 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.
|
||||
@@ -220,32 +211,31 @@ void aluInit(void)
|
||||
|
||||
static void SendSourceStoppedEvent(ALCcontext *context, ALuint id)
|
||||
{
|
||||
AsyncEvent evt = ASYNC_EVENT(EventType_SourceStateChange);
|
||||
ALbitfieldSOFT enabledevt;
|
||||
AsyncEvent evt;
|
||||
size_t strpos;
|
||||
ALuint scale;
|
||||
|
||||
enabledevt = ATOMIC_LOAD(&context->EnabledEvts, almemory_order_acquire);
|
||||
if(!(enabledevt&EventType_SourceStateChange)) return;
|
||||
|
||||
evt.EnumType = EventType_SourceStateChange;
|
||||
evt.Type = AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT;
|
||||
evt.ObjectId = id;
|
||||
evt.Param = AL_STOPPED;
|
||||
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.Message, "Source ID "); strpos = 10;
|
||||
strcpy(evt.u.user.msg, "Source ID "); strpos = 10;
|
||||
scale = 1000000000;
|
||||
while(scale > 0 && scale > id)
|
||||
scale /= 10;
|
||||
while(scale > 0)
|
||||
{
|
||||
evt.Message[strpos++] = '0' + ((id/scale)%10);
|
||||
evt.u.user.msg[strpos++] = '0' + ((id/scale)%10);
|
||||
scale /= 10;
|
||||
}
|
||||
strcpy(evt.Message+strpos, " state changed to AL_STOPPED");
|
||||
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);
|
||||
@@ -304,26 +294,24 @@ static void ProcessUhj(ALCdevice *device, ALsizei SamplesToDo)
|
||||
{
|
||||
int lidx = GetChannelIdxByName(&device->RealOut, FrontLeft);
|
||||
int ridx = GetChannelIdxByName(&device->RealOut, FrontRight);
|
||||
if(LIKELY(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
|
||||
);
|
||||
}
|
||||
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);
|
||||
if(LIKELY(lidx != -1 && ridx != -1))
|
||||
{
|
||||
/* Apply binaural/crossfeed filter */
|
||||
bs2b_cross_feed(device->Bs2b, device->RealOut.Buffer[lidx],
|
||||
device->RealOut.Buffer[ridx], SamplesToDo);
|
||||
}
|
||||
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)
|
||||
@@ -343,9 +331,7 @@ void aluSelectPostProcess(ALCdevice *device)
|
||||
}
|
||||
|
||||
|
||||
/* 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
|
||||
@@ -353,29 +339,24 @@ void aluSelectPostProcess(ALCdevice *device)
|
||||
*/
|
||||
void BsincPrepare(const ALuint increment, BsincState *state, const BSincTable *table)
|
||||
{
|
||||
ALfloat sf;
|
||||
ALsizei si;
|
||||
ALfloat sf = 0.0f;
|
||||
ALsizei si = BSINC_SCALE_COUNT-1;
|
||||
|
||||
if(increment > FRACTIONONE)
|
||||
{
|
||||
sf = (ALfloat)FRACTIONONE / increment;
|
||||
sf = maxf(0.0f, (BSINC_SCALE_COUNT-1) * (sf-table->scaleBase) * table->scaleRange);
|
||||
si = fastf2i(sf);
|
||||
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));
|
||||
}
|
||||
else
|
||||
{
|
||||
sf = 0.0f;
|
||||
si = BSINC_SCALE_COUNT - 1;
|
||||
}
|
||||
|
||||
state->sf = sf;
|
||||
state->m = table->m[si];
|
||||
state->l = -((state->m/2) - 1);
|
||||
state->l = (state->m/2) - 1;
|
||||
state->filter = table->Tab + table->filterOffset[si];
|
||||
}
|
||||
|
||||
@@ -481,12 +462,40 @@ static bool CalcEffectSlotParams(ALeffectslot *slot, ALCcontext *context, bool f
|
||||
slot->Params.AirAbsorptionGainHF = 1.0f;
|
||||
}
|
||||
|
||||
/* Swap effect states. No need to play with the ref counts since they
|
||||
* keep the same number of refs.
|
||||
*/
|
||||
state = props->State;
|
||||
props->State = slot->Params.EffectState;
|
||||
slot->Params.EffectState = state;
|
||||
|
||||
if(state == slot->Params.EffectState)
|
||||
{
|
||||
/* 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;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
ATOMIC_REPLACE_HEAD(struct ALeffectslotProps*, &context->FreeEffectslotProps, props);
|
||||
}
|
||||
@@ -662,24 +671,26 @@ static void CalcPanningAndFilters(ALvoice *voice, const ALfloat Azi, const ALflo
|
||||
NfcFilterAdjust(&voice->Direct.Params[0].NFCtrlFilter, w0);
|
||||
|
||||
for(i = 0;i < MAX_AMBI_ORDER+1;i++)
|
||||
voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
|
||||
voice->Direct.ChannelsPerOrder[i] = Device->NumChannelsPerOrder[i];
|
||||
voice->Flags |= VOICE_HAS_NFC;
|
||||
}
|
||||
|
||||
if(Device->Render_Mode == StereoPair)
|
||||
CalcAnglePairwiseCoeffs(Azi, Elev, Spread, coeffs);
|
||||
else
|
||||
CalcAngleCoeffs(Azi, Elev, Spread, coeffs);
|
||||
/* 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. */
|
||||
ComputeDryPanGains(&Device->Dry, coeffs, DryGain*1.414213562f,
|
||||
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]*1.414213562f, voice->Send[i].Params[0].Gains.Target
|
||||
ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs,
|
||||
WetGain[i]*SQRTF_2, voice->Send[i].Params[0].Gains.Target
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -688,8 +699,6 @@ static void CalcPanningAndFilters(ALvoice *voice, const ALfloat Azi, const ALflo
|
||||
/* Local B-Format sources have their XYZ channels rotated according
|
||||
* to the orientation.
|
||||
*/
|
||||
const ALfloat sqrt_2 = sqrtf(2.0f);
|
||||
const ALfloat sqrt_3 = sqrtf(3.0f);
|
||||
ALfloat N[3], V[3], U[3];
|
||||
aluMatrixf matrix;
|
||||
|
||||
@@ -732,25 +741,25 @@ static void CalcPanningAndFilters(ALvoice *voice, const ALfloat Azi, const ALflo
|
||||
* outputs on the columns.
|
||||
*/
|
||||
aluMatrixfSet(&matrix,
|
||||
// ACN0 ACN1 ACN2 ACN3
|
||||
sqrt_2, 0.0f, 0.0f, 0.0f, // Ambi W
|
||||
0.0f, -N[0]*sqrt_3, N[1]*sqrt_3, -N[2]*sqrt_3, // Ambi X
|
||||
0.0f, U[0]*sqrt_3, -U[1]*sqrt_3, U[2]*sqrt_3, // Ambi Y
|
||||
0.0f, -V[0]*sqrt_3, V[1]*sqrt_3, -V[2]*sqrt_3 // Ambi Z
|
||||
// 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++)
|
||||
ComputeFirstOrderGains(&Device->FOAOut, matrix.m[c], DryGain,
|
||||
voice->Direct.Params[c].Gains.Target);
|
||||
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++)
|
||||
ComputeFirstOrderGainsBF(Slot->ChanMap, Slot->NumChannels,
|
||||
ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
|
||||
matrix.m[c], WetGain[i], voice->Send[i].Params[c].Gains.Target
|
||||
);
|
||||
}
|
||||
@@ -906,17 +915,15 @@ static void CalcPanningAndFilters(ALvoice *voice, const ALfloat Azi, const ALflo
|
||||
NfcFilterAdjust(&voice->Direct.Params[c].NFCtrlFilter, w0);
|
||||
|
||||
for(i = 0;i < MAX_AMBI_ORDER+1;i++)
|
||||
voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
|
||||
voice->Direct.ChannelsPerOrder[i] = Device->NumChannelsPerOrder[i];
|
||||
voice->Flags |= VOICE_HAS_NFC;
|
||||
}
|
||||
|
||||
/* Calculate the directional coefficients once, which apply to all
|
||||
* input channels.
|
||||
*/
|
||||
if(Device->Render_Mode == StereoPair)
|
||||
CalcAnglePairwiseCoeffs(Azi, Elev, Spread, coeffs);
|
||||
else
|
||||
CalcAngleCoeffs(Azi, Elev, Spread, coeffs);
|
||||
CalcAngleCoeffs((Device->Render_Mode==StereoPair) ? ScaleAzimuthFront(Azi, 1.5f) : Azi,
|
||||
Elev, Spread, coeffs);
|
||||
|
||||
for(c = 0;c < num_channels;c++)
|
||||
{
|
||||
@@ -931,9 +938,8 @@ static void CalcPanningAndFilters(ALvoice *voice, const ALfloat Azi, const ALflo
|
||||
continue;
|
||||
}
|
||||
|
||||
ComputeDryPanGains(&Device->Dry,
|
||||
coeffs, DryGain * downmix_gain, voice->Direct.Params[c].Gains.Target
|
||||
);
|
||||
ComputePanGains(&Device->Dry, coeffs, DryGain * downmix_gain,
|
||||
voice->Direct.Params[c].Gains.Target);
|
||||
}
|
||||
|
||||
for(i = 0;i < NumSends;i++)
|
||||
@@ -969,7 +975,7 @@ static void CalcPanningAndFilters(ALvoice *voice, const ALfloat Azi, const ALflo
|
||||
NfcFilterAdjust(&voice->Direct.Params[c].NFCtrlFilter, w0);
|
||||
|
||||
for(i = 0;i < MAX_AMBI_ORDER+1;i++)
|
||||
voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
|
||||
voice->Direct.ChannelsPerOrder[i] = Device->NumChannelsPerOrder[i];
|
||||
voice->Flags |= VOICE_HAS_NFC;
|
||||
}
|
||||
|
||||
@@ -988,14 +994,14 @@ static void CalcPanningAndFilters(ALvoice *voice, const ALfloat Azi, const ALflo
|
||||
continue;
|
||||
}
|
||||
|
||||
if(Device->Render_Mode == StereoPair)
|
||||
CalcAnglePairwiseCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs);
|
||||
else
|
||||
CalcAngleCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs);
|
||||
ComputeDryPanGains(&Device->Dry,
|
||||
coeffs, DryGain, voice->Direct.Params[c].Gains.Target
|
||||
CalcAngleCoeffs(
|
||||
(Device->Render_Mode==StereoPair) ? ScaleAzimuthFront(chans[c].angle, 3.0f)
|
||||
: chans[c].angle,
|
||||
chans[c].elevation, Spread, coeffs
|
||||
);
|
||||
|
||||
ComputePanGains(&Device->Dry, coeffs, DryGain,
|
||||
voice->Direct.Params[c].Gains.Target);
|
||||
for(i = 0;i < NumSends;i++)
|
||||
{
|
||||
const ALeffectslot *Slot = SendSlots[i];
|
||||
@@ -1017,20 +1023,20 @@ static void CalcPanningAndFilters(ALvoice *voice, const ALfloat Azi, const ALflo
|
||||
voice->Direct.FilterType = AF_None;
|
||||
if(gainHF != 1.0f) voice->Direct.FilterType |= AF_LowPass;
|
||||
if(gainLF != 1.0f) voice->Direct.FilterType |= AF_HighPass;
|
||||
BiquadState_setParams(
|
||||
BiquadFilter_setParams(
|
||||
&voice->Direct.Params[0].LowPass, BiquadType_HighShelf,
|
||||
gainHF, hfScale, calc_rcpQ_from_slope(gainHF, 1.0f)
|
||||
);
|
||||
BiquadState_setParams(
|
||||
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++)
|
||||
{
|
||||
BiquadState_copyParams(&voice->Direct.Params[c].LowPass,
|
||||
&voice->Direct.Params[0].LowPass);
|
||||
BiquadState_copyParams(&voice->Direct.Params[c].HighPass,
|
||||
&voice->Direct.Params[0].HighPass);
|
||||
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++)
|
||||
@@ -1043,20 +1049,20 @@ static void CalcPanningAndFilters(ALvoice *voice, const ALfloat Azi, const ALflo
|
||||
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;
|
||||
BiquadState_setParams(
|
||||
BiquadFilter_setParams(
|
||||
&voice->Send[i].Params[0].LowPass, BiquadType_HighShelf,
|
||||
gainHF, hfScale, calc_rcpQ_from_slope(gainHF, 1.0f)
|
||||
);
|
||||
BiquadState_setParams(
|
||||
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++)
|
||||
{
|
||||
BiquadState_copyParams(&voice->Send[i].Params[c].LowPass,
|
||||
&voice->Send[i].Params[0].LowPass);
|
||||
BiquadState_copyParams(&voice->Send[i].Params[c].HighPass,
|
||||
&voice->Send[i].Params[0].HighPass);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1098,7 +1104,7 @@ static void CalcNonAttnSourceParams(ALvoice *voice, const struct ALvoiceProps *p
|
||||
if(Pitch > (ALfloat)MAX_PITCH)
|
||||
voice->Step = MAX_PITCH<<FRACTIONBITS;
|
||||
else
|
||||
voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
|
||||
voice->Step = maxi(fastf2i(Pitch * FRACTIONONE), 1);
|
||||
if(props->Resampler == BSinc24Resampler)
|
||||
BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc24);
|
||||
else if(props->Resampler == BSinc12Resampler)
|
||||
@@ -1459,7 +1465,7 @@ static void CalcAttnSourceParams(ALvoice *voice, const struct ALvoiceProps *prop
|
||||
if(Pitch > (ALfloat)MAX_PITCH)
|
||||
voice->Step = MAX_PITCH<<FRACTIONBITS;
|
||||
else
|
||||
voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
|
||||
voice->Step = maxi(fastf2i(Pitch * FRACTIONONE), 1);
|
||||
if(props->Resampler == BSinc24Resampler)
|
||||
BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc24);
|
||||
else if(props->Resampler == BSinc12Resampler)
|
||||
@@ -1628,7 +1634,7 @@ static void ApplyDistanceComp(ALfloat (*restrict Samples)[BUFFERSIZE], DistanceC
|
||||
continue;
|
||||
}
|
||||
|
||||
if(SamplesToDo >= base)
|
||||
if(LIKELY(SamplesToDo >= base))
|
||||
{
|
||||
for(i = 0;i < base;i++)
|
||||
Values[i] = distbuf[i];
|
||||
@@ -1656,6 +1662,9 @@ static void ApplyDither(ALfloat (*restrict Samples)[BUFFERSIZE], ALuint *dither_
|
||||
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.
|
||||
@@ -1669,7 +1678,7 @@ static void ApplyDither(ALfloat (*restrict Samples)[BUFFERSIZE], ALuint *dither_
|
||||
ALuint rng0 = dither_rng(&seed);
|
||||
ALuint rng1 = dither_rng(&seed);
|
||||
val += (ALfloat)(rng0*(1.0/UINT_MAX) - rng1*(1.0/UINT_MAX));
|
||||
samples[i] = roundf(val) * invscale;
|
||||
samples[i] = fast_roundf(val) * invscale;
|
||||
}
|
||||
}
|
||||
*dither_seed = seed;
|
||||
@@ -1680,9 +1689,10 @@ static inline ALfloat Conv_ALfloat(ALfloat val)
|
||||
{ return val; }
|
||||
static inline ALint Conv_ALint(ALfloat val)
|
||||
{
|
||||
/* Floats only have a 24-bit mantissa, so [-16777216, +16777216] is the max
|
||||
* integer range normalized floats can be safely converted to (a bit of the
|
||||
* exponent helps out, effectively giving 25 bits).
|
||||
/* 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(clampf(val*16777216.0f, -16777216.0f, 16777215.0f))<<7;
|
||||
}
|
||||
@@ -1707,6 +1717,10 @@ static void Write##A(const ALfloat (*restrict InBuffer)[BUFFERSIZE], \
|
||||
ALsizei numchans) \
|
||||
{ \
|
||||
ALsizei i, j; \
|
||||
\
|
||||
ASSUME(numchans > 0); \
|
||||
ASSUME(SamplesToDo > 0); \
|
||||
\
|
||||
for(j = 0;j < numchans;j++) \
|
||||
{ \
|
||||
const ALfloat *restrict in = ASSUME_ALIGNED(InBuffer[j], 16); \
|
||||
@@ -1824,41 +1838,29 @@ void aluMixData(ALCdevice *device, ALvoid *OutBuffer, ALsizei NumSamples)
|
||||
SamplesToDo, device->RealOut.NumChannels);
|
||||
|
||||
if(device->Limiter)
|
||||
ApplyCompression(device->Limiter, device->RealOut.NumChannels, SamplesToDo,
|
||||
device->RealOut.Buffer);
|
||||
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(OutBuffer)
|
||||
if(LIKELY(OutBuffer))
|
||||
{
|
||||
ALfloat (*Buffer)[BUFFERSIZE] = device->RealOut.Buffer;
|
||||
ALsizei Channels = device->RealOut.NumChannels;
|
||||
|
||||
switch(device->FmtType)
|
||||
{
|
||||
case DevFmtByte:
|
||||
WriteI8(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
|
||||
break;
|
||||
case DevFmtUByte:
|
||||
WriteUI8(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
|
||||
break;
|
||||
case DevFmtShort:
|
||||
WriteI16(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
|
||||
break;
|
||||
case DevFmtUShort:
|
||||
WriteUI16(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
|
||||
break;
|
||||
case DevFmtInt:
|
||||
WriteI32(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
|
||||
break;
|
||||
case DevFmtUInt:
|
||||
WriteUI32(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
|
||||
break;
|
||||
case DevFmtFloat:
|
||||
WriteF32(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1870,35 +1872,24 @@ void aluMixData(ALCdevice *device, ALvoid *OutBuffer, ALsizei NumSamples)
|
||||
|
||||
void aluHandleDisconnect(ALCdevice *device, const char *msg, ...)
|
||||
{
|
||||
AsyncEvent evt = ASYNC_EVENT(EventType_Disconnected);
|
||||
ALCcontext *ctx;
|
||||
AsyncEvent evt;
|
||||
va_list args;
|
||||
int msglen;
|
||||
|
||||
if(!ATOMIC_EXCHANGE(&device->Connected, AL_FALSE, almemory_order_acq_rel))
|
||||
return;
|
||||
|
||||
evt.EnumType = EventType_Disconnected;
|
||||
evt.Type = AL_EVENT_TYPE_DISCONNECTED_SOFT;
|
||||
evt.ObjectId = 0;
|
||||
evt.Param = 0;
|
||||
evt.u.user.type = AL_EVENT_TYPE_DISCONNECTED_SOFT;
|
||||
evt.u.user.id = 0;
|
||||
evt.u.user.param = 0;
|
||||
|
||||
va_start(args, msg);
|
||||
msglen = vsnprintf(evt.Message, sizeof(evt.Message), msg, args);
|
||||
msglen = vsnprintf(evt.u.user.msg, sizeof(evt.u.user.msg), msg, args);
|
||||
va_end(args);
|
||||
|
||||
if(msglen < 0 || (size_t)msglen >= sizeof(evt.Message))
|
||||
{
|
||||
evt.Message[sizeof(evt.Message)-1] = 0;
|
||||
msglen = (int)strlen(evt.Message);
|
||||
}
|
||||
if(msglen > 0)
|
||||
msg = evt.Message;
|
||||
else
|
||||
{
|
||||
msg = "<internal error constructing message>";
|
||||
msglen = (int)strlen(msg);
|
||||
}
|
||||
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)
|
||||
|
||||
@@ -36,6 +36,9 @@
|
||||
#include <windows.h>
|
||||
#include <shlobj.h>
|
||||
#endif
|
||||
#ifdef __APPLE__
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#endif
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alconfig.h"
|
||||
@@ -478,6 +481,26 @@ void ReadALConfig(void)
|
||||
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)
|
||||
{
|
||||
alstr_copy_cstr(&fname, str);
|
||||
|
||||
@@ -1146,16 +1146,24 @@ error2:
|
||||
ll_ringbuffer_free(self->ring);
|
||||
self->ring = NULL;
|
||||
snd_pcm_close(self->pcmHandle);
|
||||
self->pcmHandle = NULL;
|
||||
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
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, "Capture state failure: %s",
|
||||
snd_strerror(err));
|
||||
return ALC_FALSE;
|
||||
@@ -1367,11 +1375,6 @@ static ClockLatency ALCcaptureAlsa_getClockLatency(ALCcaptureAlsa *self)
|
||||
}
|
||||
|
||||
|
||||
static inline void AppendAllDevicesList2(const DevMap *entry)
|
||||
{ AppendAllDevicesList(alstr_get_cstr(entry->name)); }
|
||||
static inline void AppendCaptureDeviceList2(const DevMap *entry)
|
||||
{ AppendCaptureDeviceList(alstr_get_cstr(entry->name)); }
|
||||
|
||||
typedef struct ALCalsaBackendFactory {
|
||||
DERIVE_FROM_TYPE(ALCbackendFactory);
|
||||
} ALCalsaBackendFactory;
|
||||
@@ -1409,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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
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)
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "alMain.h"
|
||||
#include "threads.h"
|
||||
#include "alstring.h"
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
@@ -115,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);
|
||||
};
|
||||
@@ -124,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 = { \
|
||||
@@ -142,7 +143,7 @@ ALCbackendFactory *ALCcoreAudioBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCossBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCjackBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCsolarisBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCsndioBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *SndioBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCqsaBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCwasapiBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCdsoundBackendFactory_getFactory(void);
|
||||
@@ -161,6 +162,15 @@ inline void ALCdevice_Lock(ALCdevice *device)
|
||||
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
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
#include "alu.h"
|
||||
#include "ringbuffer.h"
|
||||
|
||||
#include <CoreServices/CoreServices.h>
|
||||
#include <unistd.h>
|
||||
#include <AudioUnit/AudioUnit.h>
|
||||
#include <AudioToolbox/AudioToolbox.h>
|
||||
@@ -112,7 +111,11 @@ static ALCenum ALCcoreAudioPlayback_open(ALCcoreAudioPlayback *self, const ALCch
|
||||
|
||||
/* 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;
|
||||
@@ -451,7 +454,6 @@ static ALCenum ALCcoreAudioCapture_open(ALCcoreAudioCapture *self, const ALCchar
|
||||
AudioStreamBasicDescription outputFormat; // The AudioUnit output format
|
||||
AURenderCallbackStruct input;
|
||||
AudioComponentDescription desc;
|
||||
AudioDeviceID inputDevice;
|
||||
UInt32 outputFrameCount;
|
||||
UInt32 propertySize;
|
||||
AudioObjectPropertyAddress propertyAddress;
|
||||
@@ -465,7 +467,11 @@ static ALCenum ALCcoreAudioCapture_open(ALCcoreAudioCapture *self, const ALCchar
|
||||
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;
|
||||
@@ -504,7 +510,9 @@ static ALCenum ALCcoreAudioCapture_open(ALCcoreAudioCapture *self, const ALCchar
|
||||
goto error;
|
||||
}
|
||||
|
||||
#if !TARGET_OS_IOS
|
||||
// Get the default input device
|
||||
AudioDeviceID inputDevice = kAudioDeviceUnknown;
|
||||
|
||||
propertySize = sizeof(AudioDeviceID);
|
||||
propertyAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice;
|
||||
@@ -517,7 +525,6 @@ static ALCenum ALCcoreAudioCapture_open(ALCcoreAudioCapture *self, const ALCchar
|
||||
ERR("AudioObjectGetPropertyData failed\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if(inputDevice == kAudioDeviceUnknown)
|
||||
{
|
||||
ERR("No input device found\n");
|
||||
@@ -531,6 +538,7 @@ static ALCenum ALCcoreAudioCapture_open(ALCcoreAudioCapture *self, const ALCchar
|
||||
ERR("AudioUnitSetProperty failed\n");
|
||||
goto error;
|
||||
}
|
||||
#endif
|
||||
|
||||
// set capture callback
|
||||
input.inputProc = ALCcoreAudioCapture_RecordProc;
|
||||
@@ -752,7 +760,7 @@ ALCbackendFactory *ALCcoreAudioBackendFactory_getFactory(void);
|
||||
static ALCboolean ALCcoreAudioBackendFactory_init(ALCcoreAudioBackendFactory *self);
|
||||
static DECLARE_FORWARD(ALCcoreAudioBackendFactory, ALCbackendFactory, void, deinit)
|
||||
static ALCboolean ALCcoreAudioBackendFactory_querySupport(ALCcoreAudioBackendFactory *self, ALCbackend_Type type);
|
||||
static void ALCcoreAudioBackendFactory_probe(ALCcoreAudioBackendFactory *self, enum DevProbe type);
|
||||
static 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);
|
||||
|
||||
@@ -776,15 +784,13 @@ static ALCboolean ALCcoreAudioBackendFactory_querySupport(ALCcoreAudioBackendFac
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
static void ALCcoreAudioBackendFactory_probe(ALCcoreAudioBackendFactory* UNUSED(self), 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -969,11 +969,6 @@ done:
|
||||
}
|
||||
|
||||
|
||||
static inline void AppendAllDevicesList2(const DevMap *entry)
|
||||
{ AppendAllDevicesList(alstr_get_cstr(entry->name)); }
|
||||
static inline void AppendCaptureDeviceList2(const DevMap *entry)
|
||||
{ AppendCaptureDeviceList(alstr_get_cstr(entry->name)); }
|
||||
|
||||
typedef struct ALCdsoundBackendFactory {
|
||||
DERIVE_FROM_TYPE(ALCbackendFactory);
|
||||
} ALCdsoundBackendFactory;
|
||||
@@ -984,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);
|
||||
|
||||
@@ -1028,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;
|
||||
|
||||
@@ -1036,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:
|
||||
@@ -1049,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();
|
||||
|
||||
@@ -571,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:
|
||||
|
||||
@@ -87,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);
|
||||
|
||||
@@ -110,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))
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -171,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);
|
||||
|
||||
@@ -195,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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,6 +206,9 @@ static void ALCopenslPlayback_Destruct(ALCopenslPlayback* self)
|
||||
self->mEngineObj = NULL;
|
||||
self->mEngine = NULL;
|
||||
|
||||
ll_ringbuffer_free(self->mRing);
|
||||
self->mRing = NULL;
|
||||
|
||||
alsem_destroy(&self->mSem);
|
||||
|
||||
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
|
||||
@@ -251,19 +254,16 @@ static int ALCopenslPlayback_mixerProc(void *arg)
|
||||
result = VCALL(self->mBufferQueueObj,GetInterface)(SL_IID_PLAY, &player);
|
||||
PRINTERR(result, "bufferQueue->GetInterface SL_IID_PLAY");
|
||||
}
|
||||
if(SL_RESULT_SUCCESS != result)
|
||||
{
|
||||
ALCopenslPlayback_lock(self);
|
||||
aluHandleDisconnect(device, "Failed to get playback buffer: 0x%08x", result);
|
||||
ALCopenslPlayback_unlock(self);
|
||||
return 1;
|
||||
}
|
||||
|
||||
ALCopenslPlayback_lock(self);
|
||||
while(!ATOMIC_LOAD(&self->mKillNow, almemory_order_acquire) &&
|
||||
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, len0, len1;
|
||||
size_t todo;
|
||||
|
||||
if(ll_ringbuffer_write_space(self->mRing) == 0)
|
||||
{
|
||||
@@ -292,34 +292,33 @@ static int ALCopenslPlayback_mixerProc(void *arg)
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
len0 = minu(todo, data[0].len);
|
||||
len1 = minu(todo-len0, data[1].len);
|
||||
|
||||
aluMixData(device, data[0].buf, len0*device->UpdateSize);
|
||||
for(size_t i = 0;i < len0;i++)
|
||||
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)
|
||||
ll_ringbuffer_write_advance(self->mRing, 1);
|
||||
|
||||
data[0].buf += device->UpdateSize*self->mFrameSize;
|
||||
}
|
||||
|
||||
if(len1 > 0)
|
||||
{
|
||||
aluMixData(device, data[1].buf, len1*device->UpdateSize);
|
||||
for(size_t i = 0;i < len1;i++)
|
||||
if(SL_RESULT_SUCCESS != result)
|
||||
{
|
||||
result = VCALL(bufferQueue,Enqueue)(data[1].buf, device->UpdateSize*self->mFrameSize);
|
||||
PRINTERR(result, "bufferQueue->Enqueue");
|
||||
if(SL_RESULT_SUCCESS == result)
|
||||
ll_ringbuffer_write_advance(self->mRing, 1);
|
||||
|
||||
data[1].buf += device->UpdateSize*self->mFrameSize;
|
||||
aluHandleDisconnect(device, "Failed to queue audio: 0x%08x", result);
|
||||
break;
|
||||
}
|
||||
|
||||
data[0].len--;
|
||||
data[0].buf += device->UpdateSize*self->mFrameSize;
|
||||
}
|
||||
}
|
||||
ALCopenslPlayback_unlock(self);
|
||||
@@ -392,19 +391,24 @@ static ALCboolean ALCopenslPlayback_reset(ALCopenslPlayback *self)
|
||||
SLInterfaceID ids[2];
|
||||
SLboolean reqs[2];
|
||||
SLresult result;
|
||||
JNIEnv *env;
|
||||
|
||||
if(self->mBufferQueueObj != NULL)
|
||||
VCALL0(self->mBufferQueueObj,Destroy)();
|
||||
self->mBufferQueueObj = NULL;
|
||||
|
||||
ll_ringbuffer_free(self->mRing);
|
||||
self->mRing = NULL;
|
||||
|
||||
sampleRate = device->Frequency;
|
||||
if(!(device->Flags&DEVICE_FREQUENCY_REQUEST) && (env=Android_GetJNIEnv()) != NULL)
|
||||
#if 0
|
||||
if(!(device->Flags&DEVICE_FREQUENCY_REQUEST))
|
||||
{
|
||||
/* FIXME: Disabled until I figure out how to get the Context needed for
|
||||
* the getSystemService call.
|
||||
*/
|
||||
#if 0
|
||||
JNIEnv *env = Android_GetJNIEnv();
|
||||
jobject jctx = Android_GetContext();
|
||||
|
||||
/* Get necessary stuff for using java.lang.Integer,
|
||||
* android.content.Context, and android.media.AudioManager.
|
||||
*/
|
||||
@@ -440,7 +444,7 @@ static ALCboolean ALCopenslPlayback_reset(ALCopenslPlayback *self)
|
||||
/* Now make the calls. */
|
||||
//AudioManager audMgr = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
|
||||
strobj = JCALL(env,GetStaticObjectField)(ctx_cls, ctx_audsvc);
|
||||
jobject audMgr = JCALL(env,CallObjectMethod)(ctx_cls, ctx_getSysSvc, strobj);
|
||||
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);
|
||||
@@ -461,8 +465,8 @@ static ALCboolean ALCopenslPlayback_reset(ALCopenslPlayback *self)
|
||||
|
||||
if(!sampleRate) sampleRate = device->Frequency;
|
||||
else sampleRate = maxu(sampleRate, MIN_OUTPUT_RATE);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
if(sampleRate != device->Frequency)
|
||||
{
|
||||
@@ -546,6 +550,18 @@ static ALCboolean ALCopenslPlayback_reset(ALCopenslPlayback *self)
|
||||
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)
|
||||
{
|
||||
@@ -561,13 +577,10 @@ static ALCboolean ALCopenslPlayback_reset(ALCopenslPlayback *self)
|
||||
|
||||
static ALCboolean ALCopenslPlayback_start(ALCopenslPlayback *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
|
||||
SLAndroidSimpleBufferQueueItf bufferQueue;
|
||||
SLresult result;
|
||||
|
||||
ll_ringbuffer_free(self->mRing);
|
||||
self->mRing = ll_ringbuffer_create(device->NumUpdates, self->mFrameSize*device->UpdateSize,
|
||||
true);
|
||||
ll_ringbuffer_reset(self->mRing);
|
||||
|
||||
result = VCALL(self->mBufferQueueObj,GetInterface)(SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
|
||||
&bufferQueue);
|
||||
@@ -634,9 +647,6 @@ static void ALCopenslPlayback_stop(ALCopenslPlayback *self)
|
||||
} while(SL_RESULT_SUCCESS == result && state.count > 0);
|
||||
PRINTERR(result, "bufferQueue->GetState");
|
||||
}
|
||||
|
||||
ll_ringbuffer_free(self->mRing);
|
||||
self->mRing = NULL;
|
||||
}
|
||||
|
||||
static ClockLatency ALCopenslPlayback_getClockLatency(ALCopenslPlayback *self)
|
||||
@@ -713,9 +723,6 @@ static void ALCopenslCapture_Construct(ALCopenslCapture *self, ALCdevice *device
|
||||
|
||||
static void ALCopenslCapture_Destruct(ALCopenslCapture *self)
|
||||
{
|
||||
ll_ringbuffer_free(self->mRing);
|
||||
self->mRing = NULL;
|
||||
|
||||
if(self->mRecordObj != NULL)
|
||||
VCALL0(self->mRecordObj,Destroy)();
|
||||
self->mRecordObj = NULL;
|
||||
@@ -725,6 +732,9 @@ static void ALCopenslCapture_Destruct(ALCopenslCapture *self)
|
||||
self->mEngineObj = NULL;
|
||||
self->mEngine = NULL;
|
||||
|
||||
ll_ringbuffer_free(self->mRing);
|
||||
self->mRing = NULL;
|
||||
|
||||
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
|
||||
}
|
||||
|
||||
@@ -843,8 +853,9 @@ static ALCenum ALCopenslCapture_open(ALCopenslCapture *self, const ALCchar *name
|
||||
|
||||
if(SL_RESULT_SUCCESS == result)
|
||||
{
|
||||
self->mRing = ll_ringbuffer_create(device->NumUpdates, device->UpdateSize*self->mFrameSize,
|
||||
false);
|
||||
self->mRing = ll_ringbuffer_create(device->NumUpdates,
|
||||
device->UpdateSize*self->mFrameSize, false
|
||||
);
|
||||
|
||||
result = VCALL(self->mRecordObj,GetInterface)(SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
|
||||
&bufferQueue);
|
||||
@@ -941,14 +952,16 @@ static ALCenum ALCopenslCapture_captureSamples(ALCopenslCapture *self, ALCvoid *
|
||||
SLAndroidSimpleBufferQueueItf bufferQueue;
|
||||
ll_ringbuffer_data_t data[2];
|
||||
SLresult result;
|
||||
size_t advance;
|
||||
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);
|
||||
advance = 0;
|
||||
for(i = 0;i < samples;)
|
||||
{
|
||||
ALCuint rem = minu(samples - i, device->UpdateSize - self->mSplOffset);
|
||||
@@ -961,7 +974,11 @@ static ALCenum ALCopenslCapture_captureSamples(ALCopenslCapture *self, ALCvoid *
|
||||
{
|
||||
/* Finished a chunk, reset the offset and advance the read pointer. */
|
||||
self->mSplOffset = 0;
|
||||
advance++;
|
||||
|
||||
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)
|
||||
@@ -972,24 +989,6 @@ static ALCenum ALCopenslCapture_captureSamples(ALCopenslCapture *self, ALCvoid *
|
||||
|
||||
i += rem;
|
||||
}
|
||||
ll_ringbuffer_read_advance(self->mRing, advance);
|
||||
|
||||
result = VCALL(self->mRecordObj,GetInterface)(SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
|
||||
&bufferQueue);
|
||||
PRINTERR(result, "recordObj->GetInterface");
|
||||
|
||||
/* Enqueue any newly-writable chunks in the ring buffer. */
|
||||
ll_ringbuffer_get_write_vector(self->mRing, data);
|
||||
for(i = 0;i < data[0].len && SL_RESULT_SUCCESS == result;i++)
|
||||
{
|
||||
result = VCALL(bufferQueue,Enqueue)(data[0].buf + chunk_size*i, chunk_size);
|
||||
PRINTERR(result, "bufferQueue->Enqueue");
|
||||
}
|
||||
for(i = 0;i < data[1].len && SL_RESULT_SUCCESS == result;i++)
|
||||
{
|
||||
result = VCALL(bufferQueue,Enqueue)(data[1].buf + chunk_size*i, chunk_size);
|
||||
PRINTERR(result, "bufferQueue->Enqueue");
|
||||
}
|
||||
|
||||
if(SL_RESULT_SUCCESS != result)
|
||||
{
|
||||
@@ -1030,16 +1029,13 @@ static ALCboolean ALCopenslBackendFactory_querySupport(ALCopenslBackendFactory*
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
static void ALCopenslBackendFactory_probe(ALCopenslBackendFactory* UNUSED(self), 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:
|
||||
AppendAllDevicesList(opensl_device);
|
||||
alstr_append_range(outnames, opensl_device, opensl_device+sizeof(opensl_device));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -786,7 +786,7 @@ ALCbackendFactory *ALCossBackendFactory_getFactory(void);
|
||||
static ALCboolean ALCossBackendFactory_init(ALCossBackendFactory *self);
|
||||
static void ALCossBackendFactory_deinit(ALCossBackendFactory *self);
|
||||
static ALCboolean ALCossBackendFactory_querySupport(ALCossBackendFactory *self, ALCbackend_Type type);
|
||||
static void ALCossBackendFactory_probe(ALCossBackendFactory *self, enum DevProbe type);
|
||||
static 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);
|
||||
|
||||
@@ -820,41 +820,32 @@ 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;
|
||||
struct oss_device *cur = NULL;
|
||||
switch(type)
|
||||
{
|
||||
case ALL_DEVICE_PROBE:
|
||||
ALCossListFree(&oss_playback);
|
||||
ALCossListPopulate(&oss_playback, DSP_CAP_OUTPUT);
|
||||
cur = &oss_playback;
|
||||
while(cur != NULL)
|
||||
{
|
||||
#ifdef HAVE_STAT
|
||||
struct stat buf;
|
||||
if(stat(cur->path, &buf) == 0)
|
||||
#endif
|
||||
AppendAllDevicesList(cur->handle);
|
||||
cur = cur->next;
|
||||
}
|
||||
break;
|
||||
|
||||
case CAPTURE_DEVICE_PROBE:
|
||||
ALCossListFree(&oss_capture);
|
||||
ALCossListPopulate(&oss_capture, DSP_CAP_INPUT);
|
||||
cur = &oss_capture;
|
||||
while(cur != NULL)
|
||||
{
|
||||
#ifdef HAVE_STAT
|
||||
struct stat buf;
|
||||
if(stat(cur->path, &buf) == 0)
|
||||
#endif
|
||||
AppendCaptureDeviceList(cur->handle);
|
||||
cur = cur->next;
|
||||
}
|
||||
break;
|
||||
}
|
||||
while(cur != NULL)
|
||||
{
|
||||
#ifdef HAVE_STAT
|
||||
struct stat buf;
|
||||
if(stat(cur->path, &buf) == 0)
|
||||
#endif
|
||||
alstr_append_range(outnames, cur->handle, cur->handle+strlen(cur->handle)+1);
|
||||
cur = cur->next;
|
||||
}
|
||||
}
|
||||
|
||||
ALCbackend* ALCossBackendFactory_createBackend(ALCossBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
|
||||
|
||||
@@ -484,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);
|
||||
|
||||
|
||||
@@ -518,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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1760,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);
|
||||
|
||||
|
||||
@@ -1835,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(alstr_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(alstr_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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1899,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))
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -119,6 +119,9 @@ static void deviceList(int type, vector_DevMap *devmap)
|
||||
if(max_cards < 0)
|
||||
return;
|
||||
|
||||
#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);
|
||||
@@ -989,7 +992,7 @@ typedef struct ALCqsaBackendFactory {
|
||||
static ALCboolean ALCqsaBackendFactory_init(ALCqsaBackendFactory* UNUSED(self));
|
||||
static void ALCqsaBackendFactory_deinit(ALCqsaBackendFactory* UNUSED(self));
|
||||
static ALCboolean ALCqsaBackendFactory_querySupport(ALCqsaBackendFactory* UNUSED(self), ALCbackend_Type type);
|
||||
static void ALCqsaBackendFactory_probe(ALCqsaBackendFactory* UNUSED(self), enum DevProbe type);
|
||||
static 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);
|
||||
|
||||
@@ -1016,33 +1019,25 @@ static ALCboolean ALCqsaBackendFactory_querySupport(ALCqsaBackendFactory* UNUSED
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
static void ALCqsaBackendFactory_probe(ALCqsaBackendFactory* UNUSED(self), enum DevProbe type)
|
||||
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);
|
||||
VECTOR_RESIZE(DeviceNameMap, 0, 0);
|
||||
#undef FREE_NAME
|
||||
|
||||
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);
|
||||
VECTOR_RESIZE(CaptureNameMap, 0, 0);
|
||||
#undef FREE_NAME
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -221,7 +221,7 @@ 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);
|
||||
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);
|
||||
|
||||
@@ -252,7 +252,7 @@ static ALCboolean ALCsdl2BackendFactory_querySupport(ALCsdl2BackendFactory* UNUS
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
static void ALCsdl2BackendFactory_probe(ALCsdl2BackendFactory* UNUSED(self), enum DevProbe type)
|
||||
static void ALCsdl2BackendFactory_probe(ALCsdl2BackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
|
||||
{
|
||||
int num_devices, i;
|
||||
al_string name;
|
||||
@@ -263,12 +263,13 @@ static void ALCsdl2BackendFactory_probe(ALCsdl2BackendFactory* UNUSED(self), enu
|
||||
AL_STRING_INIT(name);
|
||||
num_devices = SDL_GetNumAudioDevices(SDL_FALSE);
|
||||
|
||||
AppendAllDevicesList(defaultDeviceName);
|
||||
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));
|
||||
AppendAllDevicesList(alstr_get_cstr(name));
|
||||
if(!alstr_empty(name))
|
||||
alstr_append_range(outnames, VECTOR_BEGIN(name), VECTOR_END(name)+1);
|
||||
}
|
||||
alstr_reset(&name);
|
||||
}
|
||||
|
||||
@@ -27,15 +27,17 @@
|
||||
#include "alMain.h"
|
||||
#include "alu.h"
|
||||
#include "threads.h"
|
||||
#include "ringbuffer.h"
|
||||
|
||||
#include "backends/base.h"
|
||||
|
||||
#include <sndio.h>
|
||||
|
||||
|
||||
static const ALCchar sndio_device[] = "SndIO Default";
|
||||
|
||||
|
||||
typedef struct ALCsndioBackend {
|
||||
typedef struct SndioPlayback {
|
||||
DERIVE_FROM_TYPE(ALCbackend);
|
||||
|
||||
struct sio_hdl *sndHandle;
|
||||
@@ -45,40 +47,37 @@ typedef struct ALCsndioBackend {
|
||||
|
||||
ATOMIC(int) killNow;
|
||||
althrd_t thread;
|
||||
} ALCsndioBackend;
|
||||
} SndioPlayback;
|
||||
|
||||
static int ALCsndioBackend_mixerProc(void *ptr);
|
||||
static int SndioPlayback_mixerProc(void *ptr);
|
||||
|
||||
static void ALCsndioBackend_Construct(ALCsndioBackend *self, ALCdevice *device);
|
||||
static void ALCsndioBackend_Destruct(ALCsndioBackend *self);
|
||||
static ALCenum ALCsndioBackend_open(ALCsndioBackend *self, const ALCchar *name);
|
||||
static ALCboolean ALCsndioBackend_reset(ALCsndioBackend *self);
|
||||
static ALCboolean ALCsndioBackend_start(ALCsndioBackend *self);
|
||||
static void ALCsndioBackend_stop(ALCsndioBackend *self);
|
||||
static DECLARE_FORWARD2(ALCsndioBackend, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
|
||||
static DECLARE_FORWARD(ALCsndioBackend, ALCbackend, ALCuint, availableSamples)
|
||||
static DECLARE_FORWARD(ALCsndioBackend, ALCbackend, ClockLatency, getClockLatency)
|
||||
static DECLARE_FORWARD(ALCsndioBackend, ALCbackend, void, lock)
|
||||
static DECLARE_FORWARD(ALCsndioBackend, ALCbackend, void, unlock)
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALCsndioBackend)
|
||||
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(ALCsndioBackend);
|
||||
DEFINE_ALCBACKEND_VTABLE(SndioPlayback);
|
||||
|
||||
|
||||
static const ALCchar sndio_device[] = "SndIO Default";
|
||||
|
||||
|
||||
static void ALCsndioBackend_Construct(ALCsndioBackend *self, ALCdevice *device)
|
||||
static void SndioPlayback_Construct(SndioPlayback *self, ALCdevice *device)
|
||||
{
|
||||
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
|
||||
SET_VTABLE2(ALCsndioBackend, ALCbackend, self);
|
||||
SET_VTABLE2(SndioPlayback, ALCbackend, self);
|
||||
|
||||
self->sndHandle = NULL;
|
||||
self->mix_data = NULL;
|
||||
ATOMIC_INIT(&self->killNow, AL_TRUE);
|
||||
}
|
||||
|
||||
static void ALCsndioBackend_Destruct(ALCsndioBackend *self)
|
||||
static void SndioPlayback_Destruct(SndioPlayback *self)
|
||||
{
|
||||
if(self->sndHandle)
|
||||
sio_close(self->sndHandle);
|
||||
@@ -91,9 +90,9 @@ static void ALCsndioBackend_Destruct(ALCsndioBackend *self)
|
||||
}
|
||||
|
||||
|
||||
static int ALCsndioBackend_mixerProc(void *ptr)
|
||||
static int SndioPlayback_mixerProc(void *ptr)
|
||||
{
|
||||
ALCsndioBackend *self = (ALCsndioBackend*)ptr;
|
||||
SndioPlayback *self = (SndioPlayback*)ptr;
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
ALsizei frameSize;
|
||||
size_t wrote;
|
||||
@@ -109,9 +108,9 @@ static int ALCsndioBackend_mixerProc(void *ptr)
|
||||
ALsizei len = self->data_size;
|
||||
ALubyte *WritePtr = self->mix_data;
|
||||
|
||||
ALCsndioBackend_lock(self);
|
||||
SndioPlayback_lock(self);
|
||||
aluMixData(device, WritePtr, len/frameSize);
|
||||
ALCsndioBackend_unlock(self);
|
||||
SndioPlayback_unlock(self);
|
||||
while(len > 0 && !ATOMIC_LOAD(&self->killNow, almemory_order_acquire))
|
||||
{
|
||||
wrote = sio_write(self->sndHandle, WritePtr, len);
|
||||
@@ -133,7 +132,7 @@ static int ALCsndioBackend_mixerProc(void *ptr)
|
||||
}
|
||||
|
||||
|
||||
static ALCenum ALCsndioBackend_open(ALCsndioBackend *self, const ALCchar *name)
|
||||
static ALCenum SndioPlayback_open(SndioPlayback *self, const ALCchar *name)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
|
||||
|
||||
@@ -154,7 +153,7 @@ static ALCenum ALCsndioBackend_open(ALCsndioBackend *self, const ALCchar *name)
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static ALCboolean ALCsndioBackend_reset(ALCsndioBackend *self)
|
||||
static ALCboolean SndioPlayback_reset(SndioPlayback *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
|
||||
struct sio_par par;
|
||||
@@ -239,7 +238,7 @@ static ALCboolean ALCsndioBackend_reset(ALCsndioBackend *self)
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static ALCboolean ALCsndioBackend_start(ALCsndioBackend *self)
|
||||
static ALCboolean SndioPlayback_start(SndioPlayback *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
|
||||
|
||||
@@ -256,7 +255,7 @@ static ALCboolean ALCsndioBackend_start(ALCsndioBackend *self)
|
||||
}
|
||||
|
||||
ATOMIC_STORE(&self->killNow, AL_FALSE, almemory_order_release);
|
||||
if(althrd_create(&self->thread, ALCsndioBackend_mixerProc, self) != althrd_success)
|
||||
if(althrd_create(&self->thread, SndioPlayback_mixerProc, self) != althrd_success)
|
||||
{
|
||||
sio_stop(self->sndHandle);
|
||||
return ALC_FALSE;
|
||||
@@ -265,7 +264,7 @@ static ALCboolean ALCsndioBackend_start(ALCsndioBackend *self)
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static void ALCsndioBackend_stop(ALCsndioBackend *self)
|
||||
static void SndioPlayback_stop(SndioPlayback *self)
|
||||
{
|
||||
int res;
|
||||
|
||||
@@ -281,59 +280,318 @@ static void ALCsndioBackend_stop(ALCsndioBackend *self)
|
||||
}
|
||||
|
||||
|
||||
typedef struct ALCsndioBackendFactory {
|
||||
DERIVE_FROM_TYPE(ALCbackendFactory);
|
||||
} ALCsndioBackendFactory;
|
||||
#define ALCSNDIOBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCsndioBackendFactory, ALCbackendFactory) } }
|
||||
typedef struct SndioCapture {
|
||||
DERIVE_FROM_TYPE(ALCbackend);
|
||||
|
||||
ALCbackendFactory *ALCsndioBackendFactory_getFactory(void);
|
||||
struct sio_hdl *sndHandle;
|
||||
|
||||
static ALCboolean ALCsndioBackendFactory_init(ALCsndioBackendFactory *self);
|
||||
static DECLARE_FORWARD(ALCsndioBackendFactory, ALCbackendFactory, void, deinit)
|
||||
static ALCboolean ALCsndioBackendFactory_querySupport(ALCsndioBackendFactory *self, ALCbackend_Type type);
|
||||
static void ALCsndioBackendFactory_probe(ALCsndioBackendFactory *self, enum DevProbe type);
|
||||
static ALCbackend* ALCsndioBackendFactory_createBackend(ALCsndioBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
|
||||
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCsndioBackendFactory);
|
||||
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);
|
||||
|
||||
|
||||
ALCbackendFactory *ALCsndioBackendFactory_getFactory(void)
|
||||
static void SndioCapture_Construct(SndioCapture *self, ALCdevice *device)
|
||||
{
|
||||
static ALCsndioBackendFactory factory = ALCSNDIOBACKENDFACTORY_INITIALIZER;
|
||||
return STATIC_CAST(ALCbackendFactory, &factory);
|
||||
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 ALCboolean ALCsndioBackendFactory_init(ALCsndioBackendFactory* UNUSED(self))
|
||||
static int SndioCapture_recordProc(void* ptr)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static ALCboolean ALCsndioBackendFactory_querySupport(ALCsndioBackendFactory* UNUSED(self), ALCbackend_Type type)
|
||||
static ALCboolean SndioBackendFactory_querySupport(SndioBackendFactory* UNUSED(self), ALCbackend_Type type)
|
||||
{
|
||||
if(type == ALCbackend_Playback)
|
||||
if(type == ALCbackend_Playback || type == ALCbackend_Capture)
|
||||
return ALC_TRUE;
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
static void ALCsndioBackendFactory_probe(ALCsndioBackendFactory* UNUSED(self), 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* ALCsndioBackendFactory_createBackend(ALCsndioBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
|
||||
static ALCbackend* SndioBackendFactory_createBackend(SndioBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
|
||||
{
|
||||
if(type == ALCbackend_Playback)
|
||||
{
|
||||
ALCsndioBackend *backend;
|
||||
NEW_OBJ(backend, ALCsndioBackend)(device);
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -302,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);
|
||||
|
||||
@@ -327,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)
|
||||
{
|
||||
@@ -337,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;
|
||||
|
||||
|
||||
@@ -1919,11 +1919,6 @@ ALCenum ALCwasapiCapture_captureSamples(ALCwasapiCapture *self, ALCvoid *buffer,
|
||||
}
|
||||
|
||||
|
||||
static inline void AppendAllDevicesList2(const DevMap *entry)
|
||||
{ AppendAllDevicesList(alstr_get_cstr(entry->name)); }
|
||||
static inline void AppendCaptureDeviceList2(const DevMap *entry)
|
||||
{ AppendCaptureDeviceList(alstr_get_cstr(entry->name)); }
|
||||
|
||||
typedef struct ALCwasapiBackendFactory {
|
||||
DERIVE_FROM_TYPE(ALCbackendFactory);
|
||||
} ALCwasapiBackendFactory;
|
||||
@@ -1932,7 +1927,7 @@ typedef struct ALCwasapiBackendFactory {
|
||||
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);
|
||||
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(ALCwasapiBackendFactory);
|
||||
@@ -1989,7 +1984,7 @@ static ALCboolean ALCwasapiBackendFactory_querySupport(ALCwasapiBackendFactory*
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
static void ALCwasapiBackendFactory_probe(ALCwasapiBackendFactory* UNUSED(self), enum DevProbe type)
|
||||
static void ALCwasapiBackendFactory_probe(ALCwasapiBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
|
||||
{
|
||||
ThreadRequest req = { NULL, 0 };
|
||||
|
||||
@@ -2003,13 +1998,19 @@ static void ALCwasapiBackendFactory_probe(ALCwasapiBackendFactory* UNUSED(self),
|
||||
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;
|
||||
|
||||
@@ -403,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);
|
||||
|
||||
@@ -427,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;
|
||||
|
||||
@@ -700,17 +700,6 @@ static ALCuint ALCwinmmCapture_availableSamples(ALCwinmmCapture *self)
|
||||
}
|
||||
|
||||
|
||||
static inline void AppendAllDevicesList2(const al_string *name)
|
||||
{
|
||||
if(!alstr_empty(*name))
|
||||
AppendAllDevicesList(alstr_get_cstr(*name));
|
||||
}
|
||||
static inline void AppendCaptureDeviceList2(const al_string *name)
|
||||
{
|
||||
if(!alstr_empty(*name))
|
||||
AppendCaptureDeviceList(alstr_get_cstr(*name));
|
||||
}
|
||||
|
||||
typedef struct ALCwinmmBackendFactory {
|
||||
DERIVE_FROM_TYPE(ALCbackendFactory);
|
||||
} ALCwinmmBackendFactory;
|
||||
@@ -719,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);
|
||||
@@ -749,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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "bformatdec.h"
|
||||
#include "ambdec.h"
|
||||
#include "filters/splitter.h"
|
||||
#include "alu.h"
|
||||
|
||||
#include "bool.h"
|
||||
@@ -10,107 +11,6 @@
|
||||
#include "almalloc.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;
|
||||
|
||||
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. */
|
||||
d = in - hp_coeff*hp_z1;
|
||||
hp_y = hp_z1 + hp_coeff*d;
|
||||
hp_z1 = d;
|
||||
|
||||
/* 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, d, x;
|
||||
ALfloat z1;
|
||||
ALsizei i;
|
||||
|
||||
coeff = splitter->coeff;
|
||||
z1 = splitter->z1;
|
||||
for(i = 0;i < count;i++)
|
||||
{
|
||||
x = samples[i];
|
||||
|
||||
d = x - coeff*z1;
|
||||
x = z1 + coeff*d;
|
||||
z1 = d;
|
||||
|
||||
samples[i] = x;
|
||||
}
|
||||
splitter->z1 = z1;
|
||||
}
|
||||
|
||||
|
||||
/* NOTE: These are scale factors as applied to Ambisonics content. Decoder
|
||||
* coefficients should be divided by these values to get proper N3D scalings.
|
||||
*/
|
||||
@@ -538,7 +438,7 @@ void ambiup_reset(struct AmbiUpsampler *ambiup, const ALCdevice *device, ALfloat
|
||||
{
|
||||
ALfloat coeffs[MAX_AMBI_COEFFS] = { 0.0f };
|
||||
CalcDirectionCoeffs(Ambi3DPoints[k], 0.0f, coeffs);
|
||||
ComputeDryPanGains(&device->Dry, coeffs, 1.0f, encgains[k]);
|
||||
ComputePanGains(&device->Dry, coeffs, 1.0f, encgains[k]);
|
||||
}
|
||||
|
||||
/* Combine the matrices that do the in->virt and virt->out conversions
|
||||
@@ -550,11 +450,11 @@ void ambiup_reset(struct AmbiUpsampler *ambiup, const ALCdevice *device, ALfloat
|
||||
{
|
||||
for(j = 0;j < device->Dry.NumChannels;j++)
|
||||
{
|
||||
ALfloat gain=0.0f;
|
||||
ALdouble gain = 0.0;
|
||||
for(k = 0;k < COUNTOF(Ambi3DDecoder);k++)
|
||||
gain += Ambi3DDecoder[k][i] * encgains[k][j];
|
||||
ambiup->Gains[i][j][HF_BAND] = gain * Ambi3DDecoderHFScale[i];
|
||||
ambiup->Gains[i][j][LF_BAND] = gain;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,38 +54,4 @@ void ambiup_reset(struct AmbiUpsampler *ambiup, const ALCdevice *device, ALfloat
|
||||
|
||||
void ambiup_process(struct AmbiUpsampler *ambiup, ALfloat (*restrict OutBuffer)[BUFFERSIZE], ALsizei OutChannels, const ALfloat (*restrict InSamples)[BUFFERSIZE], ALsizei SamplesToDo);
|
||||
|
||||
|
||||
/* Band splitter. Splits a signal into two phase-matching frequency bands. */
|
||||
typedef struct BandSplitter {
|
||||
ALfloat coeff;
|
||||
ALfloat lp_z1;
|
||||
ALfloat lp_z2;
|
||||
ALfloat hp_z1;
|
||||
} BandSplitter;
|
||||
|
||||
void bandsplit_init(BandSplitter *splitter, ALfloat 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 /* BFORMATDEC_H */
|
||||
|
||||
@@ -50,14 +50,6 @@ void CloseLib(void *handle);
|
||||
void *GetSymbol(void *handle, const char *name);
|
||||
#endif
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#define JCALL(obj, func) ((*(obj))->func((obj), EXTRACT_VCALL_ARGS
|
||||
#define JCALL0(obj, func) ((*(obj))->func((obj) EXTRACT_VCALL_ARGS
|
||||
|
||||
/** Returns a JNIEnv*. */
|
||||
void *Android_GetJNIEnv(void);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
@@ -27,7 +27,8 @@ SampleConverter *CreateSampleConverter(enum DevFmtType srcType, enum DevFmtType
|
||||
|
||||
/* Have to set the mixer FPU mode since that's what the resampler code expects. */
|
||||
START_MIXER_MODE();
|
||||
step = fastf2i((ALfloat)mind((ALdouble)srcRate / dstRate, MAX_PITCH)*FRACTIONONE + 0.5f);
|
||||
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;
|
||||
|
||||
@@ -0,0 +1,321 @@
|
||||
/**
|
||||
* 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"
|
||||
|
||||
#define MIN_FREQ 20.0f
|
||||
#define MAX_FREQ 2500.0f
|
||||
#define Q_FACTOR 5.0f
|
||||
|
||||
typedef struct ALautowahState {
|
||||
DERIVE_FROM_TYPE(ALeffectState);
|
||||
|
||||
/* Effect parameters */
|
||||
ALfloat AttackRate;
|
||||
ALfloat ReleaseRate;
|
||||
ALfloat ResonanceGain;
|
||||
ALfloat PeakGain;
|
||||
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 *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 ALvoid ALautowahState_Destruct(ALautowahState *state)
|
||||
{
|
||||
ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
|
||||
}
|
||||
|
||||
static ALboolean ALautowahState_deviceUpdate(ALautowahState *state, ALCdevice *UNUSED(device))
|
||||
{
|
||||
/* (Re-)initializing parameters and clear the buffers. */
|
||||
ALsizei i, j;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
return AL_TRUE;
|
||||
}
|
||||
|
||||
static ALvoid ALautowahState_update(ALautowahState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
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 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(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++)
|
||||
{
|
||||
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;
|
||||
|
||||
/* 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);
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct AutowahStateFactory {
|
||||
DERIVE_FROM_TYPE(EffectStateFactory);
|
||||
} AutowahStateFactory;
|
||||
|
||||
static ALeffectState *AutowahStateFactory_create(AutowahStateFactory *UNUSED(factory))
|
||||
{
|
||||
ALautowahState *state;
|
||||
|
||||
NEW_OBJ0(state, ALautowahState)();
|
||||
if(!state) return NULL;
|
||||
|
||||
return STATIC_CAST(ALeffectState, state);
|
||||
}
|
||||
|
||||
DEFINE_EFFECTSTATEFACTORY_VTABLE(AutowahStateFactory);
|
||||
|
||||
EffectStateFactory *AutowahStateFactory_getFactory(void)
|
||||
{
|
||||
static AutowahStateFactory AutowahFactory = { { GET_VTABLE2(AutowahStateFactory, EffectStateFactory) } };
|
||||
|
||||
return STATIC_CAST(EffectStateFactory, &AutowahFactory);
|
||||
}
|
||||
|
||||
void ALautowah_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
|
||||
{
|
||||
ALeffectProps *props = &effect->Props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_AUTOWAH_ATTACK_TIME:
|
||||
if(!(val >= AL_AUTOWAH_MIN_ATTACK_TIME && val <= AL_AUTOWAH_MAX_ATTACK_TIME))
|
||||
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))
|
||||
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))
|
||||
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))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,,"Autowah peak gain out of range");
|
||||
props->Autowah.PeakGain = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
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_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))
|
||||
{
|
||||
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)
|
||||
{
|
||||
case AL_AUTOWAH_ATTACK_TIME:
|
||||
*val = props->Autowah.AttackTime;
|
||||
break;
|
||||
|
||||
case AL_AUTOWAH_RELEASE_TIME:
|
||||
*val = props->Autowah.ReleaseTime;
|
||||
break;
|
||||
|
||||
case AL_AUTOWAH_RESONANCE:
|
||||
*val = props->Autowah.Resonance;
|
||||
break;
|
||||
|
||||
case AL_AUTOWAH_PEAK_GAIN:
|
||||
*val = props->Autowah.PeakGain;
|
||||
break;
|
||||
|
||||
default:
|
||||
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);
|
||||
}
|
||||
|
||||
DEFINE_ALEFFECT_VTABLE(ALautowah);
|
||||
@@ -98,7 +98,7 @@ static ALboolean ALchorusState_deviceUpdate(ALchorusState *state, ALCdevice *Dev
|
||||
const ALfloat max_delay = maxf(AL_CHORUS_MAX_DELAY, AL_FLANGER_MAX_DELAY);
|
||||
ALsizei maxlen;
|
||||
|
||||
maxlen = NextPowerOf2(fastf2i(max_delay*2.0f*Device->Frequency) + 1);
|
||||
maxlen = NextPowerOf2(float2int(max_delay*2.0f*Device->Frequency) + 1u);
|
||||
if(maxlen <= 0) return AL_FALSE;
|
||||
|
||||
if(maxlen != state->BufferLength)
|
||||
@@ -140,7 +140,7 @@ static ALvoid ALchorusState_update(ALchorusState *state, const ALCcontext *Conte
|
||||
/* 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(fastf2i(props->Chorus.Delay*frequency*FRACTIONONE + 0.5f),
|
||||
state->delay = maxi(float2int(props->Chorus.Delay*frequency*FRACTIONONE + 0.5f),
|
||||
mindelay);
|
||||
state->depth = minf(props->Chorus.Depth * state->delay,
|
||||
(ALfloat)(state->delay - mindelay));
|
||||
@@ -149,9 +149,9 @@ static ALvoid ALchorusState_update(ALchorusState *state, const ALCcontext *Conte
|
||||
|
||||
/* Gains for left and right sides */
|
||||
CalcAngleCoeffs(-F_PI_2, 0.0f, 0.0f, coeffs);
|
||||
ComputeDryPanGains(&device->Dry, coeffs, Slot->Params.Gain, state->Gains[0].Target);
|
||||
ComputePanGains(&device->Dry, coeffs, Slot->Params.Gain, state->Gains[0].Target);
|
||||
CalcAngleCoeffs( F_PI_2, 0.0f, 0.0f, coeffs);
|
||||
ComputeDryPanGains(&device->Dry, coeffs, Slot->Params.Gain, state->Gains[1].Target);
|
||||
ComputePanGains(&device->Dry, coeffs, Slot->Params.Gain, state->Gains[1].Target);
|
||||
|
||||
phase = props->Chorus.Phase;
|
||||
rate = props->Chorus.Rate;
|
||||
@@ -167,10 +167,10 @@ static ALvoid ALchorusState_update(ALchorusState *state, const ALCcontext *Conte
|
||||
/* Calculate LFO coefficient (number of samples per cycle). Limit the
|
||||
* max range to avoid overflow when calculating the displacement.
|
||||
*/
|
||||
ALsizei lfo_range = mini(fastf2i(frequency/rate + 0.5f), INT_MAX/360 - 180);
|
||||
ALsizei lfo_range = float2int(minf(frequency/rate + 0.5f, (ALfloat)(INT_MAX/360 - 180)));
|
||||
|
||||
state->lfo_offset = fastf2i((ALfloat)state->lfo_offset/state->lfo_range*
|
||||
lfo_range + 0.5f) % lfo_range;
|
||||
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)
|
||||
{
|
||||
|
||||
@@ -27,6 +27,13 @@
|
||||
#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);
|
||||
|
||||
@@ -35,9 +42,9 @@ typedef struct ALcompressorState {
|
||||
|
||||
/* Effect parameters */
|
||||
ALboolean Enabled;
|
||||
ALfloat AttackRate;
|
||||
ALfloat ReleaseRate;
|
||||
ALfloat GainCtrl;
|
||||
ALfloat AttackMult;
|
||||
ALfloat ReleaseMult;
|
||||
ALfloat EnvFollower;
|
||||
} ALcompressorState;
|
||||
|
||||
static ALvoid ALcompressorState_Destruct(ALcompressorState *state);
|
||||
@@ -55,9 +62,9 @@ static void ALcompressorState_Construct(ALcompressorState *state)
|
||||
SET_VTABLE2(ALcompressorState, ALeffectState, state);
|
||||
|
||||
state->Enabled = AL_TRUE;
|
||||
state->AttackRate = 0.0f;
|
||||
state->ReleaseRate = 0.0f;
|
||||
state->GainCtrl = 1.0f;
|
||||
state->AttackMult = 1.0f;
|
||||
state->ReleaseMult = 1.0f;
|
||||
state->EnvFollower = 1.0f;
|
||||
}
|
||||
|
||||
static ALvoid ALcompressorState_Destruct(ALcompressorState *state)
|
||||
@@ -67,11 +74,17 @@ static ALvoid ALcompressorState_Destruct(ALcompressorState *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 */
|
||||
/* 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;
|
||||
|
||||
state->AttackRate = 1.0f / attackTime;
|
||||
state->ReleaseRate = 1.0f / releaseTime;
|
||||
/* 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;
|
||||
}
|
||||
@@ -86,8 +99,7 @@ static ALvoid ALcompressorState_update(ALcompressorState *state, const ALCcontex
|
||||
STATIC_CAST(ALeffectState,state)->OutBuffer = device->FOAOut.Buffer;
|
||||
STATIC_CAST(ALeffectState,state)->OutChannels = device->FOAOut.NumChannels;
|
||||
for(i = 0;i < 4;i++)
|
||||
ComputeFirstOrderGains(&device->FOAOut, IdentityMatrixf.m[i],
|
||||
slot->Params.Gain, state->Gain[i]);
|
||||
ComputePanGains(&device->FOAOut, IdentityMatrixf.m[i], slot->Params.Gain, state->Gain[i]);
|
||||
}
|
||||
|
||||
static ALvoid ALcompressorState_process(ALcompressorState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
|
||||
@@ -97,71 +109,52 @@ static ALvoid ALcompressorState_process(ALcompressorState *state, ALsizei Sample
|
||||
|
||||
for(base = 0;base < SamplesToDo;)
|
||||
{
|
||||
ALfloat temps[64][4];
|
||||
ALsizei td = mini(64, SamplesToDo-base);
|
||||
|
||||
/* Load samples into the temp buffer first. */
|
||||
for(j = 0;j < 4;j++)
|
||||
{
|
||||
for(i = 0;i < td;i++)
|
||||
temps[i][j] = SamplesIn[j][i+base];
|
||||
}
|
||||
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 gain = state->GainCtrl;
|
||||
ALfloat output, amplitude;
|
||||
|
||||
for(i = 0;i < td;i++)
|
||||
for(i = 0;i < td;++i)
|
||||
{
|
||||
/* Roughly calculate the maximum amplitude from the 4-channel
|
||||
* signal, and attack or release the gain control to reach it.
|
||||
/* Clamp the absolute amplitude to the defined envelope limits,
|
||||
* then attack or release the envelope to reach it.
|
||||
*/
|
||||
amplitude = fabsf(temps[i][0]);
|
||||
amplitude = maxf(amplitude + fabsf(temps[i][1]),
|
||||
maxf(amplitude + fabsf(temps[i][2]),
|
||||
amplitude + fabsf(temps[i][3])));
|
||||
if(amplitude > gain)
|
||||
gain = minf(gain+state->AttackRate, amplitude);
|
||||
else if(amplitude < gain)
|
||||
gain = maxf(gain-state->ReleaseRate, amplitude);
|
||||
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 inverse of the gain control to normalize/compress
|
||||
* the volume. */
|
||||
output = 1.0f / clampf(gain, 0.5f, 2.0f);
|
||||
for(j = 0;j < 4;j++)
|
||||
temps[i][j] *= output;
|
||||
/* Apply the reciprocal of the envelope to normalize the volume
|
||||
* (compress the dynamic range).
|
||||
*/
|
||||
gains[i] = 1.0f / env;
|
||||
}
|
||||
|
||||
state->GainCtrl = gain;
|
||||
}
|
||||
else
|
||||
{
|
||||
ALfloat gain = state->GainCtrl;
|
||||
ALfloat output, amplitude;
|
||||
|
||||
for(i = 0;i < td;i++)
|
||||
/* Same as above, except the amplitude is forced to 1. This helps
|
||||
* ensure smooth gain changes when the compressor is turned on and
|
||||
* off.
|
||||
*/
|
||||
for(i = 0;i < td;++i)
|
||||
{
|
||||
/* Same as above, except the amplitude is forced to 1. This
|
||||
* helps ensure smooth gain changes when the compressor is
|
||||
* turned on and off.
|
||||
*/
|
||||
amplitude = 1.0f;
|
||||
if(amplitude > gain)
|
||||
gain = minf(gain+state->AttackRate, amplitude);
|
||||
else if(amplitude < gain)
|
||||
gain = maxf(gain-state->ReleaseRate, amplitude);
|
||||
ALfloat amplitude = 1.0f;
|
||||
if(amplitude > env)
|
||||
env = minf(env*state->AttackMult, amplitude);
|
||||
else if(amplitude < env)
|
||||
env = maxf(env*state->ReleaseMult, amplitude);
|
||||
|
||||
output = 1.0f / clampf(gain, 0.5f, 2.0f);
|
||||
for(j = 0;j < 4;j++)
|
||||
temps[i][j] *= output;
|
||||
gains[i] = 1.0f / env;
|
||||
}
|
||||
|
||||
state->GainCtrl = gain;
|
||||
}
|
||||
state->EnvFollower = env;
|
||||
|
||||
/* Now mix to the output. */
|
||||
for(j = 0;j < 4;j++)
|
||||
/* Now compress the signal amplitude to output. */
|
||||
for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
|
||||
{
|
||||
for(k = 0;k < NumChannels;k++)
|
||||
{
|
||||
@@ -170,7 +163,7 @@ static ALvoid ALcompressorState_process(ALcompressorState *state, ALsizei Sample
|
||||
continue;
|
||||
|
||||
for(i = 0;i < td;i++)
|
||||
SamplesOut[k][base+i] += gain * temps[i][j];
|
||||
SamplesOut[k][base+i] += SamplesIn[j][base+i] * gains[i] * gain;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -102,7 +102,7 @@ static ALvoid ALdedicatedState_update(ALdedicatedState *state, const ALCcontext
|
||||
|
||||
STATIC_CAST(ALeffectState,state)->OutBuffer = device->Dry.Buffer;
|
||||
STATIC_CAST(ALeffectState,state)->OutChannels = device->Dry.NumChannels;
|
||||
ComputeDryPanGains(&device->Dry, coeffs, Gain, state->TargetGains);
|
||||
ComputePanGains(&device->Dry, coeffs, Gain, state->TargetGains);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,8 +37,8 @@ typedef struct ALdistortionState {
|
||||
ALfloat Gain[MAX_OUTPUT_CHANNELS];
|
||||
|
||||
/* Effect parameters */
|
||||
BiquadState lowpass;
|
||||
BiquadState bandpass;
|
||||
BiquadFilter lowpass;
|
||||
BiquadFilter bandpass;
|
||||
ALfloat attenuation;
|
||||
ALfloat edge_coeff;
|
||||
|
||||
@@ -58,9 +58,6 @@ static void ALdistortionState_Construct(ALdistortionState *state)
|
||||
{
|
||||
ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
|
||||
SET_VTABLE2(ALdistortionState, ALeffectState, state);
|
||||
|
||||
BiquadState_clear(&state->lowpass);
|
||||
BiquadState_clear(&state->bandpass);
|
||||
}
|
||||
|
||||
static ALvoid ALdistortionState_Destruct(ALdistortionState *state)
|
||||
@@ -68,8 +65,10 @@ 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;
|
||||
}
|
||||
|
||||
@@ -93,20 +92,19 @@ static ALvoid ALdistortionState_update(ALdistortionState *state, const ALCcontex
|
||||
/* Multiply sampling frequency by the amount of oversampling done during
|
||||
* processing.
|
||||
*/
|
||||
BiquadState_setParams(&state->lowpass, BiquadType_LowPass, 1.0f,
|
||||
BiquadFilter_setParams(&state->lowpass, BiquadType_LowPass, 1.0f,
|
||||
cutoff / (frequency*4.0f), calc_rcpQ_from_bandwidth(cutoff / (frequency*4.0f), bandwidth)
|
||||
);
|
||||
|
||||
cutoff = props->Distortion.EQCenter;
|
||||
/* Convert bandwidth in Hz to octaves. */
|
||||
bandwidth = props->Distortion.EQBandwidth / (cutoff * 0.67f);
|
||||
BiquadState_setParams(&state->bandpass, BiquadType_BandPass, 1.0f,
|
||||
BiquadFilter_setParams(&state->bandpass, BiquadType_BandPass, 1.0f,
|
||||
cutoff / (frequency*4.0f), calc_rcpQ_from_bandwidth(cutoff / (frequency*4.0f), bandwidth)
|
||||
);
|
||||
|
||||
CalcAngleCoeffs(0.0f, 0.0f, 0.0f, coeffs);
|
||||
ComputeDryPanGains(&device->Dry, coeffs, slot->Params.Gain * props->Distortion.Gain,
|
||||
state->Gain);
|
||||
ComputePanGains(&device->Dry, coeffs, slot->Params.Gain*props->Distortion.Gain, state->Gain);
|
||||
}
|
||||
|
||||
static ALvoid ALdistortionState_process(ALdistortionState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
|
||||
@@ -136,7 +134,7 @@ static ALvoid ALdistortionState_process(ALdistortionState *state, ALsizei Sample
|
||||
* (which is fortunately first step of distortion). So combine three
|
||||
* operations into the one.
|
||||
*/
|
||||
BiquadState_process(&state->lowpass, buffer[1], buffer[0], todo);
|
||||
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
|
||||
@@ -155,7 +153,7 @@ static ALvoid ALdistortionState_process(ALdistortionState *state, ALsizei Sample
|
||||
}
|
||||
|
||||
/* Third step, do bandpass filtering of distorted signal. */
|
||||
BiquadState_process(&state->bandpass, buffer[1], buffer[0], todo);
|
||||
BiquadFilter_process(&state->bandpass, buffer[1], buffer[0], todo);
|
||||
|
||||
todo >>= 2;
|
||||
for(k = 0;k < NumChannels;k++)
|
||||
|
||||
@@ -52,7 +52,7 @@ typedef struct ALechoState {
|
||||
|
||||
ALfloat FeedGain;
|
||||
|
||||
BiquadState Filter;
|
||||
BiquadFilter Filter;
|
||||
} ALechoState;
|
||||
|
||||
static ALvoid ALechoState_Destruct(ALechoState *state);
|
||||
@@ -76,7 +76,7 @@ static void ALechoState_Construct(ALechoState *state)
|
||||
state->Tap[1].delay = 0;
|
||||
state->Offset = 0;
|
||||
|
||||
BiquadState_clear(&state->Filter);
|
||||
BiquadFilter_clear(&state->Filter);
|
||||
}
|
||||
|
||||
static ALvoid ALechoState_Destruct(ALechoState *state)
|
||||
@@ -92,8 +92,8 @@ static ALboolean ALechoState_deviceUpdate(ALechoState *state, ALCdevice *Device)
|
||||
|
||||
// Use the next power of 2 for the buffer length, so the tap offsets can be
|
||||
// wrapped using a mask instead of a modulo
|
||||
maxlen = fastf2i(AL_ECHO_MAX_DELAY*Device->Frequency + 0.5f) +
|
||||
fastf2i(AL_ECHO_MAX_LRDELAY*Device->Frequency + 0.5f);
|
||||
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;
|
||||
|
||||
@@ -120,8 +120,8 @@ static ALvoid ALechoState_update(ALechoState *state, const ALCcontext *context,
|
||||
ALfloat coeffs[MAX_AMBI_COEFFS];
|
||||
ALfloat gainhf, lrpan, spread;
|
||||
|
||||
state->Tap[0].delay = maxi(fastf2i(props->Echo.Delay*frequency + 0.5f), 1);
|
||||
state->Tap[1].delay = fastf2i(props->Echo.LRDelay*frequency + 0.5f);
|
||||
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;
|
||||
|
||||
spread = props->Echo.Spread;
|
||||
@@ -135,17 +135,17 @@ static ALvoid ALechoState_update(ALechoState *state, const ALCcontext *context,
|
||||
state->FeedGain = props->Echo.Feedback;
|
||||
|
||||
gainhf = maxf(1.0f - props->Echo.Damping, 0.0625f); /* Limit -24dB */
|
||||
BiquadState_setParams(&state->Filter, BiquadType_HighShelf,
|
||||
gainhf, LOWPASSFREQREF/frequency,
|
||||
calc_rcpQ_from_slope(gainhf, 1.0f));
|
||||
BiquadFilter_setParams(&state->Filter, BiquadType_HighShelf,
|
||||
gainhf, LOWPASSFREQREF/frequency, calc_rcpQ_from_slope(gainhf, 1.0f)
|
||||
);
|
||||
|
||||
/* First tap panning */
|
||||
CalcAngleCoeffs(-F_PI_2*lrpan, 0.0f, spread, coeffs);
|
||||
ComputeDryPanGains(&device->Dry, coeffs, slot->Params.Gain, state->Gains[0].Target);
|
||||
ComputePanGains(&device->Dry, coeffs, slot->Params.Gain, state->Gains[0].Target);
|
||||
|
||||
/* Second tap panning */
|
||||
CalcAngleCoeffs( F_PI_2*lrpan, 0.0f, spread, coeffs);
|
||||
ComputeDryPanGains(&device->Dry, coeffs, slot->Params.Gain, state->Gains[1].Target);
|
||||
ComputePanGains(&device->Dry, coeffs, slot->Params.Gain, state->Gains[1].Target);
|
||||
}
|
||||
|
||||
static ALvoid ALechoState_process(ALechoState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
|
||||
@@ -155,14 +155,12 @@ static ALvoid ALechoState_process(ALechoState *state, ALsizei SamplesToDo, const
|
||||
const ALsizei tap2 = state->Tap[1].delay;
|
||||
ALfloat *restrict delaybuf = state->SampleBuffer;
|
||||
ALsizei offset = state->Offset;
|
||||
ALfloat x[2], y[2], in, out;
|
||||
ALfloat z1, z2, in, out;
|
||||
ALsizei base;
|
||||
ALsizei c, i;
|
||||
|
||||
x[0] = state->Filter.x[0];
|
||||
x[1] = state->Filter.x[1];
|
||||
y[0] = state->Filter.y[0];
|
||||
y[1] = state->Filter.y[1];
|
||||
z1 = state->Filter.z1;
|
||||
z2 = state->Filter.z2;
|
||||
for(base = 0;base < SamplesToDo;)
|
||||
{
|
||||
alignas(16) ALfloat temps[2][128];
|
||||
@@ -182,11 +180,9 @@ static ALvoid ALechoState_process(ALechoState *state, ALsizei SamplesToDo, const
|
||||
* feedback attenuation.
|
||||
*/
|
||||
in = temps[1][i];
|
||||
out = in*state->Filter.b0 +
|
||||
x[0]*state->Filter.b1 + x[1]*state->Filter.b2 -
|
||||
y[0]*state->Filter.a1 - y[1]*state->Filter.a2;
|
||||
x[1] = x[0]; x[0] = in;
|
||||
y[1] = y[0]; y[0] = out;
|
||||
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++;
|
||||
@@ -198,10 +194,8 @@ static ALvoid ALechoState_process(ALechoState *state, ALsizei SamplesToDo, const
|
||||
|
||||
base += td;
|
||||
}
|
||||
state->Filter.x[0] = x[0];
|
||||
state->Filter.x[1] = x[1];
|
||||
state->Filter.y[0] = y[0];
|
||||
state->Filter.y[1] = y[1];
|
||||
state->Filter.z1 = z1;
|
||||
state->Filter.z2 = z2;
|
||||
|
||||
state->Offset = offset;
|
||||
}
|
||||
|
||||
@@ -76,12 +76,12 @@ typedef struct ALequalizerState {
|
||||
DERIVE_FROM_TYPE(ALeffectState);
|
||||
|
||||
struct {
|
||||
/* Effect parameters */
|
||||
BiquadFilter filter[4];
|
||||
|
||||
/* Effect gains for each channel */
|
||||
ALfloat CurrentGains[MAX_OUTPUT_CHANNELS];
|
||||
ALfloat TargetGains[MAX_OUTPUT_CHANNELS];
|
||||
|
||||
/* Effect parameters */
|
||||
BiquadState filter[4];
|
||||
} Chans[MAX_EFFECT_CHANNELS];
|
||||
|
||||
ALfloat SampleBuffer[MAX_EFFECT_CHANNELS][BUFFERSIZE];
|
||||
@@ -114,7 +114,7 @@ static ALboolean ALequalizerState_deviceUpdate(ALequalizerState *state, ALCdevic
|
||||
for(i = 0; i < MAX_EFFECT_CHANNELS;i++)
|
||||
{
|
||||
for(j = 0;j < 4;j++)
|
||||
BiquadState_clear(&state->Chans[i].filter[j]);
|
||||
BiquadFilter_clear(&state->Chans[i].filter[j]);
|
||||
for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
|
||||
state->Chans[i].CurrentGains[j] = 0.0f;
|
||||
}
|
||||
@@ -128,25 +128,19 @@ static ALvoid ALequalizerState_update(ALequalizerState *state, const ALCcontext
|
||||
ALfloat gain, f0norm;
|
||||
ALuint i;
|
||||
|
||||
STATIC_CAST(ALeffectState,state)->OutBuffer = device->FOAOut.Buffer;
|
||||
STATIC_CAST(ALeffectState,state)->OutChannels = device->FOAOut.NumChannels;
|
||||
for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
|
||||
ComputeFirstOrderGains(&device->FOAOut, IdentityMatrixf.m[i],
|
||||
slot->Params.Gain, state->Chans[i].TargetGains);
|
||||
|
||||
/* Calculate coefficients for the each type of filter. Note that the shelf
|
||||
* filters' gain is for the reference frequency, which is the centerpoint
|
||||
* of the transition band.
|
||||
*/
|
||||
gain = maxf(sqrtf(props->Equalizer.LowGain), 0.0625f); /* Limit -24dB */
|
||||
f0norm = props->Equalizer.LowCutoff/frequency;
|
||||
BiquadState_setParams(&state->Chans[0].filter[0], BiquadType_LowShelf,
|
||||
BiquadFilter_setParams(&state->Chans[0].filter[0], BiquadType_LowShelf,
|
||||
gain, f0norm, calc_rcpQ_from_slope(gain, 0.75f)
|
||||
);
|
||||
|
||||
gain = maxf(props->Equalizer.Mid1Gain, 0.0625f);
|
||||
f0norm = props->Equalizer.Mid1Center/frequency;
|
||||
BiquadState_setParams(&state->Chans[0].filter[1], BiquadType_Peaking,
|
||||
BiquadFilter_setParams(&state->Chans[0].filter[1], BiquadType_Peaking,
|
||||
gain, f0norm, calc_rcpQ_from_bandwidth(
|
||||
f0norm, props->Equalizer.Mid1Width
|
||||
)
|
||||
@@ -154,7 +148,7 @@ static ALvoid ALequalizerState_update(ALequalizerState *state, const ALCcontext
|
||||
|
||||
gain = maxf(props->Equalizer.Mid2Gain, 0.0625f);
|
||||
f0norm = props->Equalizer.Mid2Center/frequency;
|
||||
BiquadState_setParams(&state->Chans[0].filter[2], BiquadType_Peaking,
|
||||
BiquadFilter_setParams(&state->Chans[0].filter[2], BiquadType_Peaking,
|
||||
gain, f0norm, calc_rcpQ_from_bandwidth(
|
||||
f0norm, props->Equalizer.Mid2Width
|
||||
)
|
||||
@@ -162,18 +156,24 @@ static ALvoid ALequalizerState_update(ALequalizerState *state, const ALCcontext
|
||||
|
||||
gain = maxf(sqrtf(props->Equalizer.HighGain), 0.0625f);
|
||||
f0norm = props->Equalizer.HighCutoff/frequency;
|
||||
BiquadState_setParams(&state->Chans[0].filter[3], BiquadType_HighShelf,
|
||||
BiquadFilter_setParams(&state->Chans[0].filter[3], BiquadType_HighShelf,
|
||||
gain, f0norm, calc_rcpQ_from_slope(gain, 0.75f)
|
||||
);
|
||||
|
||||
/* Copy the filter coefficients for the other input channels. */
|
||||
for(i = 1;i < MAX_EFFECT_CHANNELS;i++)
|
||||
{
|
||||
BiquadState_copyParams(&state->Chans[i].filter[0], &state->Chans[0].filter[0]);
|
||||
BiquadState_copyParams(&state->Chans[i].filter[1], &state->Chans[0].filter[1]);
|
||||
BiquadState_copyParams(&state->Chans[i].filter[2], &state->Chans[0].filter[2]);
|
||||
BiquadState_copyParams(&state->Chans[i].filter[3], &state->Chans[0].filter[3]);
|
||||
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]);
|
||||
}
|
||||
|
||||
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 ALequalizerState_process(ALequalizerState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
|
||||
@@ -183,10 +183,10 @@ static ALvoid ALequalizerState_process(ALequalizerState *state, ALsizei SamplesT
|
||||
|
||||
for(c = 0;c < MAX_EFFECT_CHANNELS;c++)
|
||||
{
|
||||
BiquadState_process(&state->Chans[c].filter[0], temps[0], SamplesIn[c], SamplesToDo);
|
||||
BiquadState_process(&state->Chans[c].filter[1], temps[1], temps[0], SamplesToDo);
|
||||
BiquadState_process(&state->Chans[c].filter[2], temps[2], temps[1], SamplesToDo);
|
||||
BiquadState_process(&state->Chans[c].filter[3], temps[3], temps[2], SamplesToDo);
|
||||
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,
|
||||
|
||||
@@ -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);
|
||||
@@ -40,10 +40,8 @@ typedef struct ALmodulatorState {
|
||||
ALsizei index;
|
||||
ALsizei step;
|
||||
|
||||
alignas(16) ALfloat ModSamples[MAX_UPDATE_SAMPLES];
|
||||
|
||||
struct {
|
||||
BiquadState Filter;
|
||||
BiquadFilter Filter;
|
||||
|
||||
ALfloat CurrentGains[MAX_OUTPUT_CHANNELS];
|
||||
ALfloat TargetGains[MAX_OUTPUT_CHANNELS];
|
||||
@@ -65,17 +63,22 @@ DEFINE_ALEFFECTSTATE_VTABLE(ALmodulatorState);
|
||||
|
||||
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(ALsizei index)
|
||||
{
|
||||
return (ALfloat)index / WAVEFORM_FRACONE;
|
||||
return (ALfloat)index*(2.0f/WAVEFORM_FRACONE) - 1.0f;
|
||||
}
|
||||
|
||||
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) \
|
||||
@@ -94,6 +97,7 @@ static void Modulate##func(ALfloat *restrict dst, ALsizei index, \
|
||||
DECL_TEMPLATE(Sin)
|
||||
DECL_TEMPLATE(Saw)
|
||||
DECL_TEMPLATE(Square)
|
||||
DECL_TEMPLATE(One)
|
||||
|
||||
#undef DECL_TEMPLATE
|
||||
|
||||
@@ -117,7 +121,7 @@ static ALboolean ALmodulatorState_deviceUpdate(ALmodulatorState *state, ALCdevic
|
||||
ALsizei i, j;
|
||||
for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
|
||||
{
|
||||
BiquadState_clear(&state->Chans[i].Filter);
|
||||
BiquadFilter_clear(&state->Chans[i].Filter);
|
||||
for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
|
||||
state->Chans[i].CurrentGains[j] = 0.0f;
|
||||
}
|
||||
@@ -127,48 +131,45 @@ static ALboolean ALmodulatorState_deviceUpdate(ALmodulatorState *state, ALCdevic
|
||||
static ALvoid ALmodulatorState_update(ALmodulatorState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props)
|
||||
{
|
||||
const ALCdevice *device = context->Device;
|
||||
ALfloat cw, a;
|
||||
ALfloat f0norm;
|
||||
ALsizei i;
|
||||
|
||||
if(props->Modulator.Waveform == AL_RING_MODULATOR_SINUSOID)
|
||||
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;
|
||||
|
||||
state->step = fastf2i(props->Modulator.Frequency*WAVEFORM_FRACONE /
|
||||
device->Frequency);
|
||||
state->step = clampi(state->step, 1, WAVEFORM_FRACONE-1);
|
||||
|
||||
/* Custom filter coeffs, which match the old version instead of a low-shelf. */
|
||||
cw = cosf(F_TAU * props->Modulator.HighPassCutoff / device->Frequency);
|
||||
a = (2.0f-cw) - sqrtf(powf(2.0f-cw, 2.0f) - 1.0f);
|
||||
|
||||
state->Chans[0].Filter.b0 = a;
|
||||
state->Chans[0].Filter.b1 = -a;
|
||||
state->Chans[0].Filter.b2 = 0.0f;
|
||||
state->Chans[0].Filter.a1 = -a;
|
||||
state->Chans[0].Filter.a2 = 0.0f;
|
||||
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++)
|
||||
BiquadState_copyParams(&state->Chans[i].Filter, &state->Chans[0].Filter);
|
||||
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++)
|
||||
ComputeFirstOrderGains(&device->FOAOut, IdentityMatrixf.m[i],
|
||||
slot->Params.Gain, state->Chans[i].TargetGains);
|
||||
ComputePanGains(&device->FOAOut, IdentityMatrixf.m[i], slot->Params.Gain,
|
||||
state->Chans[i].TargetGains);
|
||||
}
|
||||
|
||||
static ALvoid ALmodulatorState_process(ALmodulatorState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
|
||||
{
|
||||
ALfloat *restrict modsamples = ASSUME_ALIGNED(state->ModSamples, 16);
|
||||
const ALsizei step = state->step;
|
||||
ALsizei base;
|
||||
|
||||
for(base = 0;base < SamplesToDo;)
|
||||
{
|
||||
alignas(16) ALfloat temps[2][MAX_UPDATE_SAMPLES];
|
||||
alignas(16) ALfloat modsamples[MAX_UPDATE_SAMPLES];
|
||||
ALsizei td = mini(MAX_UPDATE_SAMPLES, SamplesToDo-base);
|
||||
ALsizei c, i;
|
||||
|
||||
@@ -178,11 +179,13 @@ static ALvoid ALmodulatorState_process(ALmodulatorState *state, ALsizei SamplesT
|
||||
|
||||
for(c = 0;c < MAX_EFFECT_CHANNELS;c++)
|
||||
{
|
||||
BiquadState_process(&state->Chans[c].Filter, temps[0], &SamplesIn[c][base], td);
|
||||
for(i = 0;i < td;i++)
|
||||
temps[1][i] = temps[0][i] * modsamples[i];
|
||||
alignas(16) ALfloat temps[MAX_UPDATE_SAMPLES];
|
||||
|
||||
MixSamples(temps[1], NumChannels, SamplesOut, state->Chans[c].CurrentGains,
|
||||
BiquadFilter_process(&state->Chans[c].Filter, temps, &SamplesIn[c][base], td);
|
||||
for(i = 0;i < td;i++)
|
||||
temps[i] *= modsamples[i];
|
||||
|
||||
MixSamples(temps, NumChannels, SamplesOut, state->Chans[c].CurrentGains,
|
||||
state->Chans[c].TargetGains, SamplesToDo-base, base, td);
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,8 @@
|
||||
#include "alu.h"
|
||||
#include "filters/defs.h"
|
||||
|
||||
#include "alcomplex.h"
|
||||
|
||||
|
||||
#define STFT_SIZE 1024
|
||||
#define STFT_HALF_SIZE (STFT_SIZE>>1)
|
||||
@@ -37,35 +39,33 @@
|
||||
#define STFT_STEP (STFT_SIZE / OVERSAMP)
|
||||
#define FIFO_LATENCY (STFT_STEP * (OVERSAMP-1))
|
||||
|
||||
typedef struct ALcomplex {
|
||||
ALfloat Real;
|
||||
ALfloat Imag;
|
||||
} ALcomplex;
|
||||
|
||||
typedef struct ALphasor {
|
||||
ALfloat Amplitude;
|
||||
ALfloat Phase;
|
||||
ALdouble Amplitude;
|
||||
ALdouble Phase;
|
||||
} ALphasor;
|
||||
|
||||
typedef struct ALFrequencyDomain {
|
||||
ALfloat Amplitude;
|
||||
ALfloat Frequency;
|
||||
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];
|
||||
ALfloat LastPhase[STFT_HALF_SIZE+1];
|
||||
ALfloat SumPhase[STFT_HALF_SIZE+1];
|
||||
ALfloat OutputAccum[STFT_SIZE];
|
||||
ALdouble LastPhase[STFT_HALF_SIZE+1];
|
||||
ALdouble SumPhase[STFT_HALF_SIZE+1];
|
||||
ALdouble OutputAccum[STFT_SIZE];
|
||||
|
||||
ALcomplex FFTbuffer[STFT_SIZE];
|
||||
|
||||
@@ -89,7 +89,7 @@ DEFINE_ALEFFECTSTATE_VTABLE(ALpshifterState);
|
||||
|
||||
|
||||
/* Define a Hann window, used to filter the STFT input and output. */
|
||||
alignas(16) static ALfloat HannWindow[STFT_SIZE];
|
||||
alignas(16) static ALdouble HannWindow[STFT_SIZE];
|
||||
|
||||
static void InitHannWindow(void)
|
||||
{
|
||||
@@ -99,124 +99,65 @@ static void InitHannWindow(void)
|
||||
for(i = 0;i < STFT_SIZE>>1;i++)
|
||||
{
|
||||
ALdouble val = sin(M_PI * (ALdouble)i / (ALdouble)(STFT_SIZE-1));
|
||||
HannWindow[i] = HannWindow[STFT_SIZE-(i+1)] = (ALfloat)(val * val);
|
||||
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 = sqrtf(number.Real*number.Real + number.Imag*number.Imag);
|
||||
polar.Phase = atan2f(number.Imag , number.Real);
|
||||
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)
|
||||
static inline ALcomplex polar2rect(ALphasor number)
|
||||
{
|
||||
ALcomplex cartesian;
|
||||
|
||||
cartesian.Real = number.Amplitude * cosf(number.Phase);
|
||||
cartesian.Imag = number.Amplitude * sinf(number.Phase);
|
||||
cartesian.Real = number.Amplitude * cos(number.Phase);
|
||||
cartesian.Imag = number.Amplitude * sin(number.Phase);
|
||||
|
||||
return cartesian;
|
||||
}
|
||||
|
||||
/* Addition of two complex numbers (ALcomplex format) */
|
||||
static inline ALcomplex complex_add(ALcomplex a, ALcomplex b)
|
||||
{
|
||||
ALcomplex result;
|
||||
|
||||
result.Real = a.Real + b.Real;
|
||||
result.Imag = a.Imag + b.Imag;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Subtraction of two complex numbers (ALcomplex format) */
|
||||
static inline ALcomplex complex_sub(ALcomplex a, ALcomplex b)
|
||||
{
|
||||
ALcomplex result;
|
||||
|
||||
result.Real = a.Real - b.Real;
|
||||
result.Imag = a.Imag - b.Imag;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Multiplication of two complex numbers (ALcomplex format) */
|
||||
static inline ALcomplex complex_mult(ALcomplex a, ALcomplex b)
|
||||
{
|
||||
ALcomplex result;
|
||||
|
||||
result.Real = a.Real*b.Real - a.Imag*b.Imag;
|
||||
result.Imag = a.Imag*b.Real + a.Real*b.Imag;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Iterative implementation of 2-radix FFT (In-place algorithm). Sign = -1 is
|
||||
* FFT and 1 is iFFT (inverse). Fills FFTBuffer[0...FFTSize-1] with the
|
||||
* Discrete Fourier Transform (DFT) of the time domain data stored in
|
||||
* FFTBuffer[0...FFTSize-1]. FFTBuffer is an array of complex numbers
|
||||
* (ALcomplex), FFTSize MUST BE power of two.
|
||||
*/
|
||||
static inline ALvoid FFT(ALcomplex *FFTBuffer, ALsizei FFTSize, ALfloat Sign)
|
||||
{
|
||||
ALsizei i, j, k, mask, step, step2;
|
||||
ALcomplex temp, u, w;
|
||||
ALfloat arg;
|
||||
|
||||
/* Bit-reversal permutation applied to a sequence of FFTSize items */
|
||||
for(i = 1;i < FFTSize-1;i++)
|
||||
{
|
||||
for(mask = 0x1, j = 0;mask < FFTSize;mask <<= 1)
|
||||
{
|
||||
if((i&mask) != 0)
|
||||
j++;
|
||||
j <<= 1;
|
||||
}
|
||||
j >>= 1;
|
||||
|
||||
if(i < j)
|
||||
{
|
||||
temp = FFTBuffer[i];
|
||||
FFTBuffer[i] = FFTBuffer[j];
|
||||
FFTBuffer[j] = temp;
|
||||
}
|
||||
}
|
||||
|
||||
/* Iterative form of Danielson–Lanczos lemma */
|
||||
for(i = 1, step = 2;i < FFTSize;i<<=1, step<<=1)
|
||||
{
|
||||
step2 = step >> 1;
|
||||
arg = F_PI / step2;
|
||||
|
||||
w.Real = cosf(arg);
|
||||
w.Imag = sinf(arg) * Sign;
|
||||
|
||||
u.Real = 1.0f;
|
||||
u.Imag = 0.0f;
|
||||
|
||||
for(j = 0;j < step2;j++)
|
||||
{
|
||||
for(k = j;k < FFTSize;k+=step)
|
||||
{
|
||||
temp = complex_mult(FFTBuffer[k+step2], u);
|
||||
FFTBuffer[k+step2] = complex_sub(FFTBuffer[k], temp);
|
||||
FFTBuffer[k] = complex_add(FFTBuffer[k], temp);
|
||||
}
|
||||
|
||||
u = complex_mult(u, w);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void ALpshifterState_Construct(ALpshifterState *state)
|
||||
{
|
||||
@@ -234,9 +175,10 @@ static ALvoid ALpshifterState_Destruct(ALpshifterState *state)
|
||||
static ALboolean ALpshifterState_deviceUpdate(ALpshifterState *state, ALCdevice *device)
|
||||
{
|
||||
/* (Re-)initializing parameters and clear the buffers. */
|
||||
state->count = FIFO_LATENCY;
|
||||
state->PitchShift = 1.0f;
|
||||
state->FreqPerBin = device->Frequency / (ALfloat)STFT_SIZE;
|
||||
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));
|
||||
@@ -257,13 +199,16 @@ static ALvoid ALpshifterState_update(ALpshifterState *state, const ALCcontext *c
|
||||
{
|
||||
const ALCdevice *device = context->Device;
|
||||
ALfloat coeffs[MAX_AMBI_COEFFS];
|
||||
float pitch;
|
||||
|
||||
state->PitchShift = powf(2.0f,
|
||||
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);
|
||||
ComputeDryPanGains(&device->Dry, coeffs, slot->Params.Gain, state->TargetGains);
|
||||
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)
|
||||
@@ -272,8 +217,8 @@ static ALvoid ALpshifterState_process(ALpshifterState *state, ALsizei SamplesToD
|
||||
* http://blogs.zynaptiq.com/bernsee/pitch-shifting-using-the-ft/
|
||||
*/
|
||||
|
||||
static const ALfloat expected = F_TAU / (ALfloat)OVERSAMP;
|
||||
const ALfloat freq_per_bin = state->FreqPerBin;
|
||||
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;
|
||||
@@ -296,12 +241,12 @@ static ALvoid ALpshifterState_process(ALpshifterState *state, ALsizei SamplesToD
|
||||
for(k = 0;k < STFT_SIZE;k++)
|
||||
{
|
||||
state->FFTbuffer[k].Real = state->InFIFO[k] * HannWindow[k];
|
||||
state->FFTbuffer[k].Imag = 0.0f;
|
||||
state->FFTbuffer[k].Imag = 0.0;
|
||||
}
|
||||
|
||||
/* ANALYSIS */
|
||||
/* Apply FFT to FFTbuffer data */
|
||||
FFT(state->FFTbuffer, STFT_SIZE, -1.0f);
|
||||
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.
|
||||
@@ -309,18 +254,18 @@ static ALvoid ALpshifterState_process(ALpshifterState *state, ALsizei SamplesToD
|
||||
for(k = 0;k < STFT_HALF_SIZE+1;k++)
|
||||
{
|
||||
ALphasor component;
|
||||
ALfloat tmp;
|
||||
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]) - (ALfloat)k*expected;
|
||||
tmp = (component.Phase - state->LastPhase[k]) - k*expected;
|
||||
|
||||
/* Map delta phase into +/- Pi interval */
|
||||
qpd = fastf2i(tmp / F_PI);
|
||||
tmp -= F_PI * (ALfloat)(qpd + (qpd%2));
|
||||
qpd = double2int(tmp / M_PI);
|
||||
tmp -= M_PI * (qpd + (qpd%2));
|
||||
|
||||
/* Get deviation from bin frequency from the +/- Pi interval */
|
||||
tmp /= expected;
|
||||
@@ -329,8 +274,8 @@ static ALvoid ALpshifterState_process(ALpshifterState *state, ALsizei SamplesToD
|
||||
* 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.0f * component.Amplitude;
|
||||
state->Analysis_buffer[k].Frequency = ((ALfloat)k + tmp) * freq_per_bin;
|
||||
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;
|
||||
@@ -340,13 +285,13 @@ static ALvoid ALpshifterState_process(ALpshifterState *state, ALsizei SamplesToD
|
||||
/* pitch shifting */
|
||||
for(k = 0;k < STFT_HALF_SIZE+1;k++)
|
||||
{
|
||||
state->Syntesis_buffer[k].Amplitude = 0.0f;
|
||||
state->Syntesis_buffer[k].Frequency = 0.0f;
|
||||
state->Syntesis_buffer[k].Amplitude = 0.0;
|
||||
state->Syntesis_buffer[k].Frequency = 0.0;
|
||||
}
|
||||
|
||||
for(k = 0;k < STFT_HALF_SIZE+1;k++)
|
||||
{
|
||||
j = fastf2i((ALfloat)k * state->PitchShift);
|
||||
j = (k*state->PitchShiftI) >> FRACTIONBITS;
|
||||
if(j >= STFT_HALF_SIZE+1) break;
|
||||
|
||||
state->Syntesis_buffer[j].Amplitude += state->Analysis_buffer[k].Amplitude;
|
||||
@@ -359,13 +304,13 @@ static ALvoid ALpshifterState_process(ALpshifterState *state, ALsizei SamplesToD
|
||||
for(k = 0;k < STFT_HALF_SIZE+1;k++)
|
||||
{
|
||||
ALphasor component;
|
||||
ALfloat tmp;
|
||||
ALdouble tmp;
|
||||
|
||||
/* Compute bin deviation from scaled freq */
|
||||
tmp = state->Syntesis_buffer[k].Frequency/freq_per_bin - (ALfloat)k;
|
||||
tmp = state->Syntesis_buffer[k].Frequency/freq_per_bin - k;
|
||||
|
||||
/* Calculate actual delta phase and accumulate it to get bin phase */
|
||||
state->SumPhase[k] += ((ALfloat)k + tmp) * expected;
|
||||
state->SumPhase[k] += (k + tmp) * expected;
|
||||
|
||||
component.Amplitude = state->Syntesis_buffer[k].Amplitude;
|
||||
component.Phase = state->SumPhase[k];
|
||||
@@ -376,22 +321,22 @@ static ALvoid ALpshifterState_process(ALpshifterState *state, ALsizei SamplesToD
|
||||
/* zero negative frequencies for recontruct a real signal */
|
||||
for(k = STFT_HALF_SIZE+1;k < STFT_SIZE;k++)
|
||||
{
|
||||
state->FFTbuffer[k].Real = 0.0f;
|
||||
state->FFTbuffer[k].Imag = 0.0f;
|
||||
state->FFTbuffer[k].Real = 0.0;
|
||||
state->FFTbuffer[k].Imag = 0.0;
|
||||
}
|
||||
|
||||
/* Apply iFFT to buffer data */
|
||||
FFT(state->FFTbuffer, STFT_SIZE, 1.0f);
|
||||
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.5f * STFT_HALF_SIZE * OVERSAMP);
|
||||
(0.5 * STFT_HALF_SIZE * OVERSAMP);
|
||||
|
||||
/* Shift accumulator, input & output FIFO */
|
||||
for(k = 0;k < STFT_STEP;k++) state->OutFIFO[k] = state->OutputAccum[k];
|
||||
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.0f;
|
||||
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];
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -30,14 +30,14 @@ typedef enum BiquadType {
|
||||
BiquadType_BandPass,
|
||||
} BiquadType;
|
||||
|
||||
typedef struct BiquadState {
|
||||
ALfloat x[2]; /* History of two last input samples */
|
||||
ALfloat y[2]; /* History of two last output samples */
|
||||
ALfloat b0, b1, b2; /* Transfer function coefficients "b" */
|
||||
ALfloat a1, a2; /* Transfer function coefficients "a" (a0 is pre-applied) */
|
||||
} BiquadState;
|
||||
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 BiquadState_process BiquadState_processC
|
||||
#define BiquadFilter_process BiquadFilter_processC
|
||||
|
||||
/**
|
||||
* Calculates the rcpQ (i.e. 1/Q) coefficient for shelving filters, using the
|
||||
@@ -61,12 +61,10 @@ inline ALfloat calc_rcpQ_from_bandwidth(ALfloat f0norm, ALfloat bandwidth)
|
||||
return 2.0f*sinhf(logf(2.0f)/2.0f*bandwidth*w0/sinf(w0));
|
||||
}
|
||||
|
||||
inline void BiquadState_clear(BiquadState *filter)
|
||||
inline void BiquadFilter_clear(BiquadFilter *filter)
|
||||
{
|
||||
filter->x[0] = 0.0f;
|
||||
filter->x[1] = 0.0f;
|
||||
filter->y[0] = 0.0f;
|
||||
filter->y[1] = 0.0f;
|
||||
filter->z1 = 0.0f;
|
||||
filter->z2 = 0.0f;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -84,9 +82,9 @@ inline void BiquadState_clear(BiquadState *filter)
|
||||
* band. Can be generated from calc_rcpQ_from_slope or
|
||||
* calc_rcpQ_from_bandwidth depending on the available data.
|
||||
*/
|
||||
void BiquadState_setParams(BiquadState *filter, BiquadType type, ALfloat gain, ALfloat f0norm, ALfloat rcpQ);
|
||||
void BiquadFilter_setParams(BiquadFilter *filter, BiquadType type, ALfloat gain, ALfloat f0norm, ALfloat rcpQ);
|
||||
|
||||
inline void BiquadState_copyParams(BiquadState *restrict dst, const BiquadState *restrict src)
|
||||
inline void BiquadFilter_copyParams(BiquadFilter *restrict dst, const BiquadFilter *restrict src)
|
||||
{
|
||||
dst->b0 = src->b0;
|
||||
dst->b1 = src->b1;
|
||||
@@ -95,23 +93,19 @@ inline void BiquadState_copyParams(BiquadState *restrict dst, const BiquadState
|
||||
dst->a2 = src->a2;
|
||||
}
|
||||
|
||||
void BiquadState_processC(BiquadState *filter, ALfloat *restrict dst, const ALfloat *restrict src, ALsizei numsamples);
|
||||
void BiquadFilter_processC(BiquadFilter *filter, ALfloat *restrict dst, const ALfloat *restrict src, ALsizei numsamples);
|
||||
|
||||
inline void BiquadState_processPassthru(BiquadState *filter, const ALfloat *restrict src, ALsizei numsamples)
|
||||
inline void BiquadFilter_passthru(BiquadFilter *filter, ALsizei numsamples)
|
||||
{
|
||||
if(numsamples >= 2)
|
||||
if(LIKELY(numsamples >= 2))
|
||||
{
|
||||
filter->x[1] = src[numsamples-2];
|
||||
filter->x[0] = src[numsamples-1];
|
||||
filter->y[1] = src[numsamples-2];
|
||||
filter->y[0] = src[numsamples-1];
|
||||
filter->z1 = 0.0f;
|
||||
filter->z2 = 0.0f;
|
||||
}
|
||||
else if(numsamples == 1)
|
||||
{
|
||||
filter->x[1] = filter->x[0];
|
||||
filter->x[0] = src[0];
|
||||
filter->y[1] = filter->y[0];
|
||||
filter->y[0] = src[0];
|
||||
filter->z1 = filter->z2;
|
||||
filter->z2 = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,14 +7,14 @@
|
||||
#include "alMain.h"
|
||||
#include "defs.h"
|
||||
|
||||
extern inline void BiquadState_clear(BiquadState *filter);
|
||||
extern inline void BiquadState_copyParams(BiquadState *restrict dst, const BiquadState *restrict src);
|
||||
extern inline void BiquadState_processPassthru(BiquadState *filter, const ALfloat *restrict src, ALsizei numsamples);
|
||||
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 BiquadState_setParams(BiquadState *filter, BiquadType type, ALfloat gain, ALfloat f0norm, ALfloat rcpQ)
|
||||
void BiquadFilter_setParams(BiquadFilter *filter, BiquadType type, ALfloat gain, ALfloat f0norm, ALfloat rcpQ)
|
||||
{
|
||||
ALfloat alpha, sqrtgain_alpha_2;
|
||||
ALfloat w0, sin_w0, cos_w0;
|
||||
@@ -94,40 +94,36 @@ void BiquadState_setParams(BiquadState *filter, BiquadType type, ALfloat gain, A
|
||||
}
|
||||
|
||||
|
||||
void BiquadState_processC(BiquadState *filter, ALfloat *restrict dst, const ALfloat *restrict src, ALsizei numsamples)
|
||||
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;
|
||||
if(LIKELY(numsamples > 1))
|
||||
{
|
||||
ALfloat x0 = filter->x[0];
|
||||
ALfloat x1 = filter->x[1];
|
||||
ALfloat y0 = filter->y[0];
|
||||
ALfloat y1 = filter->y[1];
|
||||
|
||||
for(i = 0;i < numsamples;i++)
|
||||
{
|
||||
dst[i] = filter->b0* src[i] +
|
||||
filter->b1*x0 + filter->b2*x1 -
|
||||
filter->a1*y0 - filter->a2*y1;
|
||||
y1 = y0; y0 = dst[i];
|
||||
x1 = x0; x0 = src[i];
|
||||
}
|
||||
ASSUME(numsamples > 0);
|
||||
|
||||
filter->x[0] = x0;
|
||||
filter->x[1] = x1;
|
||||
filter->y[0] = y0;
|
||||
filter->y[1] = y1;
|
||||
}
|
||||
else if(numsamples == 1)
|
||||
/* 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++)
|
||||
{
|
||||
dst[0] = filter->b0 * src[0] +
|
||||
filter->b1 * filter->x[0] +
|
||||
filter->b2 * filter->x[1] -
|
||||
filter->a1 * filter->y[0] -
|
||||
filter->a2 * filter->y[1];
|
||||
filter->x[1] = filter->x[0];
|
||||
filter->x[0] = src[0];
|
||||
filter->y[1] = filter->y[0];
|
||||
filter->y[0] = dst[0];
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "nfc.h"
|
||||
#include "alMain.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
@@ -57,25 +58,25 @@ static void NfcFilterCreate1(struct NfcFilter1 *nfc, const float w0, const float
|
||||
float b_00, g_0;
|
||||
float r;
|
||||
|
||||
nfc->g = 1.0f;
|
||||
nfc->coeffs[0] = 1.0f;
|
||||
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->coeffs[0] *= g_0;
|
||||
nfc->coeffs[1] = (2.0f * b_00) / g_0;
|
||||
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->g /= g_0;
|
||||
nfc->coeffs[0] /= g_0;
|
||||
nfc->coeffs[1+1] = (2.0f * b_00) / g_0;
|
||||
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)
|
||||
@@ -87,8 +88,8 @@ static void NfcFilterAdjust1(struct NfcFilter1 *nfc, const float w0)
|
||||
b_00 = B[1][0] * r;
|
||||
g_0 = 1.0f + b_00;
|
||||
|
||||
nfc->coeffs[0] = nfc->g * g_0;
|
||||
nfc->coeffs[1] = (2.0f * b_00) / g_0;
|
||||
nfc->gain = nfc->base_gain * g_0;
|
||||
nfc->b1 = 2.0f * b_00 / g_0;
|
||||
}
|
||||
|
||||
|
||||
@@ -97,8 +98,8 @@ static void NfcFilterCreate2(struct NfcFilter2 *nfc, const float w0, const float
|
||||
float b_10, b_11, g_1;
|
||||
float r;
|
||||
|
||||
nfc->g = 1.0f;
|
||||
nfc->coeffs[0] = 1.0f;
|
||||
nfc->base_gain = 1.0f;
|
||||
nfc->gain = 1.0f;
|
||||
|
||||
/* Calculate bass-boost coefficients. */
|
||||
r = 0.5f * w0;
|
||||
@@ -106,9 +107,9 @@ static void NfcFilterCreate2(struct NfcFilter2 *nfc, const float w0, const float
|
||||
b_11 = B[2][1] * r * r;
|
||||
g_1 = 1.0f + b_10 + b_11;
|
||||
|
||||
nfc->coeffs[0] *= g_1;
|
||||
nfc->coeffs[1] = ((2.0f * b_10) + (4.0f * b_11)) / g_1;
|
||||
nfc->coeffs[2] = (4.0f * b_11) / g_1;
|
||||
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;
|
||||
@@ -116,10 +117,10 @@ static void NfcFilterCreate2(struct NfcFilter2 *nfc, const float w0, const float
|
||||
b_11 = B[2][1] * r * r;
|
||||
g_1 = 1.0f + b_10 + b_11;
|
||||
|
||||
nfc->g /= g_1;
|
||||
nfc->coeffs[0] /= g_1;
|
||||
nfc->coeffs[2+1] = ((2.0f * b_10) + (4.0f * b_11)) / g_1;
|
||||
nfc->coeffs[2+2] = (4.0f * b_11) / g_1;
|
||||
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)
|
||||
@@ -132,9 +133,9 @@ static void NfcFilterAdjust2(struct NfcFilter2 *nfc, const float w0)
|
||||
b_11 = B[2][1] * r * r;
|
||||
g_1 = 1.0f + b_10 + b_11;
|
||||
|
||||
nfc->coeffs[0] = nfc->g * g_1;
|
||||
nfc->coeffs[1] = ((2.0f * b_10) + (4.0f * b_11)) / g_1;
|
||||
nfc->coeffs[2] = (4.0f * b_11) / g_1;
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -144,8 +145,8 @@ static void NfcFilterCreate3(struct NfcFilter3 *nfc, const float w0, const float
|
||||
float b_00, g_0;
|
||||
float r;
|
||||
|
||||
nfc->g = 1.0f;
|
||||
nfc->coeffs[0] = 1.0f;
|
||||
nfc->base_gain = 1.0f;
|
||||
nfc->gain = 1.0f;
|
||||
|
||||
/* Calculate bass-boost coefficients. */
|
||||
r = 0.5f * w0;
|
||||
@@ -153,15 +154,15 @@ static void NfcFilterCreate3(struct NfcFilter3 *nfc, const float w0, const float
|
||||
b_11 = B[3][1] * r * r;
|
||||
g_1 = 1.0f + b_10 + b_11;
|
||||
|
||||
nfc->coeffs[0] *= g_1;
|
||||
nfc->coeffs[1] = ((2.0f * b_10) + (4.0f * b_11)) / g_1;
|
||||
nfc->coeffs[2] = (4.0f * b_11) / g_1;
|
||||
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->coeffs[0] *= g_0;
|
||||
nfc->coeffs[2+1] = (2.0f * b_00) / g_0;
|
||||
nfc->gain *= g_0;
|
||||
nfc->b3 = 2.0f * b_00 / g_0;
|
||||
|
||||
/* Calculate bass-cut coefficients. */
|
||||
r = 0.5f * w1;
|
||||
@@ -169,17 +170,17 @@ static void NfcFilterCreate3(struct NfcFilter3 *nfc, const float w0, const float
|
||||
b_11 = B[3][1] * r * r;
|
||||
g_1 = 1.0f + b_10 + b_11;
|
||||
|
||||
nfc->g /= g_1;
|
||||
nfc->coeffs[0] /= g_1;
|
||||
nfc->coeffs[3+1] = ((2.0f * b_10) + (4.0f * b_11)) / g_1;
|
||||
nfc->coeffs[3+2] = (4.0f * b_11) / g_1;
|
||||
|
||||
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->g /= g_0;
|
||||
nfc->coeffs[0] /= g_0;
|
||||
nfc->coeffs[3+2+1] = (2.0f * b_00) / g_0;
|
||||
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)
|
||||
@@ -193,15 +194,15 @@ static void NfcFilterAdjust3(struct NfcFilter3 *nfc, const float w0)
|
||||
b_11 = B[3][1] * r * r;
|
||||
g_1 = 1.0f + b_10 + b_11;
|
||||
|
||||
nfc->coeffs[0] = nfc->g * g_1;
|
||||
nfc->coeffs[1] = ((2.0f * b_10) + (4.0f * b_11)) / g_1;
|
||||
nfc->coeffs[2] = (4.0f * b_11) / g_1;
|
||||
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->coeffs[0] *= g_0;
|
||||
nfc->coeffs[2+1] = (2.0f * b_00) / g_0;
|
||||
nfc->gain *= g_0;
|
||||
nfc->b3 = 2.0f * b_00 / g_0;
|
||||
}
|
||||
|
||||
|
||||
@@ -223,86 +224,83 @@ void NfcFilterAdjust(NfcFilter *nfc, const float w0)
|
||||
|
||||
void NfcFilterProcess1(NfcFilter *nfc, float *restrict dst, const float *restrict src, const int count)
|
||||
{
|
||||
const float b0 = nfc->first.coeffs[0];
|
||||
const float a0 = nfc->first.coeffs[1];
|
||||
const float a1 = nfc->first.coeffs[2];
|
||||
float z1 = nfc->first.history[0];
|
||||
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 out = src[i] * b0;
|
||||
float y;
|
||||
|
||||
y = out - (a1*z1);
|
||||
out = y + (a0*z1);
|
||||
float y = src[i]*gain - a1*z1;
|
||||
float out = y + b1*z1;
|
||||
z1 += y;
|
||||
|
||||
dst[i] = out;
|
||||
}
|
||||
nfc->first.history[0] = z1;
|
||||
nfc->first.z[0] = z1;
|
||||
}
|
||||
|
||||
void NfcFilterProcess2(NfcFilter *nfc, float *restrict dst, const float *restrict src, const int count)
|
||||
{
|
||||
const float b0 = nfc->second.coeffs[0];
|
||||
const float a00 = nfc->second.coeffs[1];
|
||||
const float a01 = nfc->second.coeffs[2];
|
||||
const float a10 = nfc->second.coeffs[3];
|
||||
const float a11 = nfc->second.coeffs[4];
|
||||
float z1 = nfc->second.history[0];
|
||||
float z2 = nfc->second.history[1];
|
||||
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 out = src[i] * b0;
|
||||
float y;
|
||||
|
||||
y = out - (a10*z1) - (a11*z2);
|
||||
out = y + (a00*z1) + (a01*z2);
|
||||
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.history[0] = z1;
|
||||
nfc->second.history[1] = z2;
|
||||
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 b0 = nfc->third.coeffs[0];
|
||||
const float a00 = nfc->third.coeffs[1];
|
||||
const float a01 = nfc->third.coeffs[2];
|
||||
const float a02 = nfc->third.coeffs[3];
|
||||
const float a10 = nfc->third.coeffs[4];
|
||||
const float a11 = nfc->third.coeffs[5];
|
||||
const float a12 = nfc->third.coeffs[6];
|
||||
float z1 = nfc->third.history[0];
|
||||
float z2 = nfc->third.history[1];
|
||||
float z3 = nfc->third.history[2];
|
||||
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 out = src[i] * b0;
|
||||
float y;
|
||||
|
||||
y = out - (a10*z1) - (a11*z2);
|
||||
out = y + (a00*z1) + (a01*z2);
|
||||
float y = src[i]*gain - a1*z1 - a2*z2;
|
||||
float out = y + b1*z1 + b2*z2;
|
||||
z2 += z1;
|
||||
z1 += y;
|
||||
|
||||
y = out - (a12*z3);
|
||||
out = y + (a02*z3);
|
||||
y = out - a3*z3;
|
||||
out = y + b3*z3;
|
||||
z3 += y;
|
||||
|
||||
dst[i] = out;
|
||||
}
|
||||
nfc->third.history[0] = z1;
|
||||
nfc->third.history[1] = z2;
|
||||
nfc->third.history[2] = z3;
|
||||
nfc->third.z[0] = z1;
|
||||
nfc->third.z[1] = z2;
|
||||
nfc->third.z[2] = z3;
|
||||
}
|
||||
|
||||
#if 0 /* Original methods the above are derived from. */
|
||||
|
||||
@@ -2,19 +2,19 @@
|
||||
#define FILTER_NFC_H
|
||||
|
||||
struct NfcFilter1 {
|
||||
float g;
|
||||
float coeffs[1*2 + 1];
|
||||
float history[1];
|
||||
float base_gain, gain;
|
||||
float b1, a1;
|
||||
float z[1];
|
||||
};
|
||||
struct NfcFilter2 {
|
||||
float g;
|
||||
float coeffs[2*2 + 1];
|
||||
float history[2];
|
||||
float base_gain, gain;
|
||||
float b1, b2, a1, a2;
|
||||
float z[2];
|
||||
};
|
||||
struct NfcFilter3 {
|
||||
float g;
|
||||
float coeffs[3*2 + 1];
|
||||
float history[3];
|
||||
float base_gain, gain;
|
||||
float b1, b2, b3, a1, a2, a3;
|
||||
float z[3];
|
||||
};
|
||||
|
||||
typedef struct NfcFilter {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -7,16 +7,13 @@
|
||||
|
||||
|
||||
typedef struct FPUCtl {
|
||||
#ifdef HAVE_FENV_H
|
||||
fenv_t flt_env;
|
||||
#ifdef _WIN32
|
||||
int round_mode;
|
||||
#endif
|
||||
#else
|
||||
int state;
|
||||
#endif
|
||||
#ifdef HAVE_SSE
|
||||
int sse_state;
|
||||
#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);
|
||||
|
||||
+66
-130
@@ -124,6 +124,8 @@ DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_GUID, 0x1da5d803, 0xd492, 0x4edd, 0x8c, 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 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);
|
||||
@@ -136,6 +138,20 @@ extern inline int fallback_ctz64(ALuint64 value);
|
||||
#endif
|
||||
|
||||
|
||||
#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, ®s[0], ®s[1], ®s[2], ®s[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(int capfilter)
|
||||
@@ -144,58 +160,13 @@ void FillCPUCaps(int capfilter)
|
||||
|
||||
/* 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
|
||||
@@ -203,7 +174,7 @@ void FillCPUCaps(int 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);
|
||||
@@ -211,29 +182,23 @@ void FillCPUCaps(int 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
|
||||
@@ -258,22 +223,32 @@ void FillCPUCaps(int capfilter)
|
||||
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)
|
||||
{
|
||||
size_t len;
|
||||
char *str;
|
||||
|
||||
if(strncmp(buf, "Features\t:", 10) != 0)
|
||||
continue;
|
||||
|
||||
len = strlen(buf);
|
||||
while(len > 0 && isspace(buf[len-1]))
|
||||
buf[--len] = 0;
|
||||
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;
|
||||
|
||||
TRACE("Got features string:%s\n", buf+10);
|
||||
if(!alstr_empty(features))
|
||||
{
|
||||
const char *str = alstr_get_cstr(features);
|
||||
while(isspace(str[0])) ++str;
|
||||
|
||||
str = buf;
|
||||
TRACE("Got features string:%s\n", str);
|
||||
while((str=strstr(str, "neon")) != NULL)
|
||||
{
|
||||
if(isspace(*(str-1)) && (str[4] == 0 || isspace(str[4])))
|
||||
@@ -281,13 +256,11 @@ void FillCPUCaps(int capfilter)
|
||||
caps |= CPU_CAP_NEON;
|
||||
break;
|
||||
}
|
||||
str++;
|
||||
++str;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
file = NULL;
|
||||
alstr_reset(&features);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -305,81 +278,44 @@ void FillCPUCaps(int capfilter)
|
||||
|
||||
void SetMixerFPUMode(FPUCtl *ctl)
|
||||
{
|
||||
#ifdef HAVE_FENV_H
|
||||
fegetenv(&ctl->flt_env);
|
||||
#ifdef _WIN32
|
||||
/* HACK: A nasty bug in MinGW-W64 causes fegetenv and fesetenv to not save
|
||||
* and restore the FPU rounding mode, so we have to do it manually. Don't
|
||||
* know if this also applies to MSVC.
|
||||
*/
|
||||
ctl->round_mode = fegetround();
|
||||
#endif
|
||||
#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(&ctl->flt_env);
|
||||
#ifdef _WIN32
|
||||
fesetround(ctl->round_mode);
|
||||
#endif
|
||||
#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
|
||||
}
|
||||
|
||||
@@ -748,13 +684,13 @@ void GetProcBinary(al_string *path, al_string *fname)
|
||||
size_t pathlen;
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
int mib[4] = { CTL_KERN, KERN_PROC_ARGS, getpid() };
|
||||
if(sysctl(mib, 3, NULL, &pathlen, NULL, 0) == -1)
|
||||
WARN("Failed to sysctl kern.procargs.%d: %s\n", mib[2], strerror(errno));
|
||||
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
|
||||
{
|
||||
pathname = malloc(pathlen + 1);
|
||||
sysctl(mib, 3, (void*)pathname, &pathlen, NULL, 0);
|
||||
sysctl(mib, 4, (void*)pathname, &pathlen, NULL, 0);
|
||||
pathname[pathlen] = 0;
|
||||
}
|
||||
#endif
|
||||
@@ -1169,8 +1105,8 @@ void alstr_copy_range(al_string *str, const al_string_char_type *from, const al_
|
||||
void alstr_append_char(al_string *str, const al_string_char_type c)
|
||||
{
|
||||
size_t len = alstr_length(*str);
|
||||
VECTOR_RESIZE(*str, len, len+2);
|
||||
VECTOR_PUSH_BACK(*str, c);
|
||||
VECTOR_RESIZE(*str, len+1, len+2);
|
||||
VECTOR_BACK(*str) = c;
|
||||
VECTOR_ELEM(*str, len+1) = 0;
|
||||
}
|
||||
|
||||
|
||||
+63
-43
@@ -28,9 +28,9 @@
|
||||
#include "alMain.h"
|
||||
#include "alSource.h"
|
||||
#include "alu.h"
|
||||
#include "bformatdec.h"
|
||||
#include "hrtf.h"
|
||||
#include "alconfig.h"
|
||||
#include "filters/splitter.h"
|
||||
|
||||
#include "compat.h"
|
||||
#include "almalloc.h"
|
||||
@@ -74,31 +74,29 @@ static struct HrtfEntry *LoadedHrtfs = NULL;
|
||||
|
||||
|
||||
/* Calculate the elevation index given the polar elevation in radians. This
|
||||
* will return an index between 0 and (evcount - 1). Assumes the FPU is in
|
||||
* round-to-zero mode.
|
||||
* will return an index between 0 and (evcount - 1).
|
||||
*/
|
||||
static ALsizei CalcEvIndex(ALsizei evcount, ALfloat ev, ALfloat *mu)
|
||||
{
|
||||
ALsizei idx;
|
||||
ev = (F_PI_2+ev) * (evcount-1) / F_PI;
|
||||
idx = mini(fastf2i(ev), evcount-1);
|
||||
idx = float2int(ev);
|
||||
|
||||
*mu = ev - idx;
|
||||
return idx;
|
||||
return mini(idx, evcount-1);
|
||||
}
|
||||
|
||||
/* Calculate the azimuth index given the polar azimuth in radians. This will
|
||||
* return an index between 0 and (azcount - 1). Assumes the FPU is in round-to-
|
||||
* zero mode.
|
||||
* return an index between 0 and (azcount - 1).
|
||||
*/
|
||||
static ALsizei CalcAzIndex(ALsizei azcount, ALfloat az, ALfloat *mu)
|
||||
{
|
||||
ALsizei idx;
|
||||
az = (F_TAU+az) * azcount / F_TAU;
|
||||
|
||||
idx = fastf2i(az) % azcount;
|
||||
*mu = az - floorf(az);
|
||||
return idx;
|
||||
idx = float2int(az);
|
||||
*mu = az - idx;
|
||||
return idx % azcount;
|
||||
}
|
||||
|
||||
/* Calculates static HRIR coefficients and delays for the given polar elevation
|
||||
@@ -160,11 +158,11 @@ void GetHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azimuth,
|
||||
/* Calculate the blended HRIR delays. */
|
||||
delays[0] = fastf2i(
|
||||
Hrtf->delays[idx[0]][0]*blend[0] + Hrtf->delays[idx[1]][0]*blend[1] +
|
||||
Hrtf->delays[idx[2]][0]*blend[2] + Hrtf->delays[idx[3]][0]*blend[3] + 0.5f
|
||||
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] + 0.5f
|
||||
Hrtf->delays[idx[2]][1]*blend[2] + Hrtf->delays[idx[3]][1]*blend[3]
|
||||
);
|
||||
|
||||
/* Calculate the sample offsets for the HRIR indices. */
|
||||
@@ -173,6 +171,7 @@ void GetHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azimuth,
|
||||
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);
|
||||
@@ -202,13 +201,16 @@ void BuildBFormatHrtf(const struct Hrtf *Hrtf, DirectHrtfState *state, ALsizei N
|
||||
*/
|
||||
#define NUM_BANDS 2
|
||||
BandSplitter splitter;
|
||||
ALsizei idx[HRTF_AMBI_MAX_CHANNELS];
|
||||
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 = 0;
|
||||
ALsizei max_length;
|
||||
ALsizei i, c, b;
|
||||
|
||||
idx = al_calloc(DEF_ALIGN, AmbiCount*sizeof(*idx));
|
||||
|
||||
for(c = 0;c < AmbiCount;c++)
|
||||
{
|
||||
ALuint evidx, azidx;
|
||||
@@ -216,48 +218,43 @@ void BuildBFormatHrtf(const struct Hrtf *Hrtf, DirectHrtfState *state, ALsizei N
|
||||
ALuint azcount;
|
||||
|
||||
/* Calculate elevation index. */
|
||||
evidx = (ALsizei)floorf((F_PI_2 + AmbiPoints[c].Elev) *
|
||||
(Hrtf->evCount-1)/F_PI + 0.5f);
|
||||
evidx = (ALsizei)((F_PI_2+AmbiPoints[c].Elev) * (Hrtf->evCount-1) / F_PI + 0.5f);
|
||||
evidx = clampi(evidx, 0, Hrtf->evCount-1);
|
||||
|
||||
azcount = Hrtf->azCount[evidx];
|
||||
evoffset = Hrtf->evOffset[evidx];
|
||||
|
||||
/* Calculate azimuth index for this elevation. */
|
||||
azidx = (ALsizei)floorf((F_TAU+AmbiPoints[c].Azim) *
|
||||
azcount/F_TAU + 0.5f) % azcount;
|
||||
azidx = (ALsizei)((F_TAU+AmbiPoints[c].Azim) * azcount / F_TAU + 0.5f) % azcount;
|
||||
|
||||
/* Calculate indices for left and right channels. */
|
||||
idx[c] = evoffset + azidx;
|
||||
|
||||
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]));
|
||||
}
|
||||
|
||||
tmpres = al_calloc(16, NumChannels * sizeof(*tmpres));
|
||||
|
||||
memset(temps, 0, sizeof(temps));
|
||||
bandsplit_init(&splitter, 400.0f / (ALfloat)Hrtf->sampleRate);
|
||||
for(c = 0;c < AmbiCount;c++)
|
||||
{
|
||||
const ALfloat (*fir)[2] = &Hrtf->coeffs[idx[c] * Hrtf->irSize];
|
||||
const ALsizei res_delay = mini(Hrtf->delays[idx[c]][0], Hrtf->delays[idx[c]][1]);
|
||||
ALsizei ldelay = Hrtf->delays[idx[c]][0] - res_delay;
|
||||
ALsizei rdelay = Hrtf->delays[idx[c]][1] - res_delay;
|
||||
|
||||
min_delay = mini(min_delay, res_delay);
|
||||
max_delay = maxi(max_delay, res_delay);
|
||||
|
||||
max_length = maxi(max_length,
|
||||
mini(maxi(ldelay, rdelay) + Hrtf->irSize, HRIR_LENGTH)
|
||||
);
|
||||
ALsizei ldelay = Hrtf->delays[idx[c]][0] - min_delay;
|
||||
ALsizei rdelay = Hrtf->delays[idx[c]][1] - min_delay;
|
||||
|
||||
if(NUM_BANDS == 1)
|
||||
{
|
||||
for(i = 0;i < NumChannels;++i)
|
||||
{
|
||||
ALfloat hfgain = AmbiOrderHFGain[(ALsizei)floor(sqrt(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)
|
||||
{
|
||||
state->Chan[i].Coeffs[lidx++][0] += fir[j][0] * AmbiMatrix[c][i] * hfgain;
|
||||
state->Chan[i].Coeffs[ridx++][1] += fir[j][1] * AmbiMatrix[c][i] * hfgain;
|
||||
tmpres[i][lidx++][0] += fir[j][0] * mult;
|
||||
tmpres[i][ridx++][1] += fir[j][1] * mult;
|
||||
j++;
|
||||
}
|
||||
}
|
||||
@@ -273,15 +270,14 @@ void BuildBFormatHrtf(const struct Hrtf *Hrtf, DirectHrtfState *state, ALsizei N
|
||||
/* Apply left ear response with delay. */
|
||||
for(i = 0;i < NumChannels;++i)
|
||||
{
|
||||
ALfloat hfgain = AmbiOrderHFGain[(ALsizei)floor(sqrt(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)
|
||||
state->Chan[i].Coeffs[lidx++][0] += temps[b][j++] * AmbiMatrix[c][i] *
|
||||
hfgain;
|
||||
hfgain = 1.0f;
|
||||
tmpres[i][lidx++][0] += temps[b][j++] * mult;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -294,25 +290,49 @@ void BuildBFormatHrtf(const struct Hrtf *Hrtf, DirectHrtfState *state, ALsizei N
|
||||
/* Apply right ear response with delay. */
|
||||
for(i = 0;i < NumChannels;++i)
|
||||
{
|
||||
ALfloat hfgain = AmbiOrderHFGain[(ALsizei)floor(sqrt(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)
|
||||
state->Chan[i].Coeffs[ridx++][1] += temps[b][j++] * AmbiMatrix[c][i] *
|
||||
hfgain;
|
||||
hfgain = 1.0f;
|
||||
tmpres[i][ridx++][1] += temps[b][j++] * mult;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(i = 0;i < NumChannels;++i)
|
||||
{
|
||||
int idx;
|
||||
for(idx = 0;idx < HRIR_LENGTH;idx++)
|
||||
{
|
||||
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;
|
||||
|
||||
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 min: %d, max: %d, new FIR length: %d\n", min_delay, max_delay,
|
||||
max_length);
|
||||
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
|
||||
}
|
||||
@@ -1014,12 +1034,12 @@ static void AddFileEntry(vector_EnumeratedHrtf *list, const_al_string filename)
|
||||
/* 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;
|
||||
}
|
||||
#undef MATCH_FNAME
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -1093,12 +1113,12 @@ static void AddBuiltInEntry(vector_EnumeratedHrtf *list, const_al_string filenam
|
||||
{
|
||||
#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;
|
||||
}
|
||||
#undef MATCH_FNAME
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -9,12 +9,6 @@
|
||||
#include "atomic.h"
|
||||
|
||||
|
||||
/* The maximum number of virtual speakers used to generate HRTF coefficients
|
||||
* for decoding B-Format.
|
||||
*/
|
||||
#define HRTF_AMBI_MAX_CHANNELS 18
|
||||
|
||||
|
||||
#define HRTF_HISTORY_BITS (6)
|
||||
#define HRTF_HISTORY_LENGTH (1<<HRTF_HISTORY_BITS)
|
||||
#define HRTF_HISTORY_MASK (HRTF_HISTORY_LENGTH-1)
|
||||
@@ -81,8 +75,9 @@ void GetHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azimuth,
|
||||
|
||||
/**
|
||||
* Produces HRTF filter coefficients for decoding B-Format, given a set of
|
||||
* virtual speaker positions and HF/LF matrices for decoding to them. The
|
||||
* returned coefficients are ordered and scaled according to the matrices.
|
||||
* 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);
|
||||
|
||||
|
||||
@@ -72,6 +72,14 @@ 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
|
||||
|
||||
+466
-168
@@ -5,228 +5,526 @@
|
||||
#include "mastering.h"
|
||||
#include "alu.h"
|
||||
#include "almalloc.h"
|
||||
#include "static_assert.h"
|
||||
|
||||
|
||||
extern inline ALuint GetCompressorSampleRate(const Compressor *Comp);
|
||||
/* These structures assume BUFFERSIZE is a power of 2. */
|
||||
static_assert((BUFFERSIZE & (BUFFERSIZE-1)) == 0, "BUFFERSIZE is not a power of 2");
|
||||
|
||||
#define RMS_WINDOW_SIZE (1<<7)
|
||||
#define RMS_WINDOW_MASK (RMS_WINDOW_SIZE-1)
|
||||
#define RMS_VALUE_MAX (1<<24)
|
||||
typedef struct SlidingHold {
|
||||
ALfloat Values[BUFFERSIZE];
|
||||
ALsizei Expiries[BUFFERSIZE];
|
||||
ALsizei LowerIndex;
|
||||
ALsizei UpperIndex;
|
||||
ALsizei Length;
|
||||
} SlidingHold;
|
||||
|
||||
static_assert(RMS_VALUE_MAX < (UINT_MAX / RMS_WINDOW_SIZE), "RMS_VALUE_MAX is too big");
|
||||
|
||||
|
||||
/* Multichannel compression is linked via one of two modes:
|
||||
/* General topology and basic automation was based on the following paper:
|
||||
*
|
||||
* Summed - Absolute sum of all channels.
|
||||
* Maxed - Absolute maximum of any channel.
|
||||
*/
|
||||
static void SumChannels(Compressor *Comp, const ALsizei NumChans, const ALsizei SamplesToDo,
|
||||
ALfloat (*restrict OutBuffer)[BUFFERSIZE])
|
||||
{
|
||||
ALsizei c, i;
|
||||
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
Comp->Envelope[i] = 0.0f;
|
||||
|
||||
for(c = 0;c < NumChans;c++)
|
||||
{
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
Comp->Envelope[i] += OutBuffer[c][i];
|
||||
}
|
||||
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
Comp->Envelope[i] = fabsf(Comp->Envelope[i]);
|
||||
}
|
||||
|
||||
static void MaxChannels(Compressor *Comp, const ALsizei NumChans, const ALsizei SamplesToDo,
|
||||
ALfloat (*restrict OutBuffer)[BUFFERSIZE])
|
||||
{
|
||||
ALsizei c, i;
|
||||
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
Comp->Envelope[i] = 0.0f;
|
||||
|
||||
for(c = 0;c < NumChans;c++)
|
||||
{
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
Comp->Envelope[i] = maxf(Comp->Envelope[i], fabsf(OutBuffer[c][i]));
|
||||
}
|
||||
}
|
||||
|
||||
/* Envelope detection/sensing can be done via:
|
||||
* 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
|
||||
*
|
||||
* RMS - Rectangular windowed root mean square of linking stage.
|
||||
* Peak - Implicit output from linking stage.
|
||||
* Available (along with supplemental reading) at:
|
||||
*
|
||||
* http://c4dm.eecs.qmul.ac.uk/audioengineering/compressors/
|
||||
*/
|
||||
static void RmsDetection(Compressor *Comp, const ALsizei SamplesToDo)
|
||||
{
|
||||
ALuint sum = Comp->RmsSum;
|
||||
ALuint *window = Comp->RmsWindow;
|
||||
ALsizei index = Comp->RmsIndex;
|
||||
ALsizei i;
|
||||
typedef struct Compressor {
|
||||
ALsizei NumChans;
|
||||
ALuint SampleRate;
|
||||
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
{
|
||||
ALfloat sig = Comp->Envelope[i];
|
||||
struct {
|
||||
ALuint Knee : 1;
|
||||
ALuint Attack : 1;
|
||||
ALuint Release : 1;
|
||||
ALuint PostGain : 1;
|
||||
ALuint Declip : 1;
|
||||
} Auto;
|
||||
|
||||
sum -= window[index];
|
||||
window[index] = fastf2i(minf(sig * sig * 65536.0f, RMS_VALUE_MAX));
|
||||
sum += window[index];
|
||||
index = (index + 1) & RMS_WINDOW_MASK;
|
||||
ALsizei LookAhead;
|
||||
|
||||
Comp->Envelope[i] = sqrtf(sum / 65536.0f / RMS_WINDOW_SIZE);
|
||||
}
|
||||
ALfloat PreGain;
|
||||
ALfloat PostGain;
|
||||
|
||||
Comp->RmsSum = sum;
|
||||
Comp->RmsIndex = index;
|
||||
}
|
||||
ALfloat Threshold;
|
||||
ALfloat Slope;
|
||||
ALfloat Knee;
|
||||
|
||||
/* This isn't a very sophisticated envelope follower, but it gets the job
|
||||
* done. First, it operates at logarithmic scales to keep transitions
|
||||
* appropriate for human hearing. Second, it can apply adaptive (automated)
|
||||
* attack/release adjustments based on the signal.
|
||||
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 void FollowEnvelope(Compressor *Comp, const ALsizei SamplesToDo)
|
||||
static ALfloat UpdateSlidingHold(SlidingHold *Hold, const ALsizei i, const ALfloat in)
|
||||
{
|
||||
ALfloat attackMin = Comp->AttackMin;
|
||||
ALfloat attackMax = Comp->AttackMax;
|
||||
ALfloat releaseMin = Comp->ReleaseMin;
|
||||
ALfloat releaseMax = Comp->ReleaseMax;
|
||||
ALfloat last = Comp->EnvLast;
|
||||
ALsizei i;
|
||||
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;
|
||||
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
if(i >= expiries[upperIndex])
|
||||
upperIndex = (upperIndex + 1) & mask;
|
||||
|
||||
if(in >= values[upperIndex])
|
||||
{
|
||||
ALfloat env = maxf(-6.0f, log10f(Comp->Envelope[i]));
|
||||
ALfloat slope = minf(1.0f, fabsf(env - last) / 4.5f);
|
||||
|
||||
if(env > last)
|
||||
last = minf(env, last + lerp(attackMin, attackMax, 1.0f - (slope * slope)));
|
||||
else
|
||||
last = maxf(env, last + lerp(releaseMin, releaseMax, 1.0f - (slope * slope)));
|
||||
|
||||
Comp->Envelope[i] = last;
|
||||
}
|
||||
|
||||
Comp->EnvLast = last;
|
||||
}
|
||||
|
||||
/* The envelope is converted to control gain with an optional soft knee. */
|
||||
static void EnvelopeGain(Compressor *Comp, const ALsizei SamplesToDo, const ALfloat Slope)
|
||||
{
|
||||
const ALfloat threshold = Comp->Threshold;
|
||||
const ALfloat knee = Comp->Knee;
|
||||
ALsizei i;
|
||||
|
||||
if(!(knee > 0.0f))
|
||||
{
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
{
|
||||
ALfloat gain = Slope * (threshold - Comp->Envelope[i]);
|
||||
Comp->Envelope[i] = powf(10.0f, minf(0.0f, gain));
|
||||
}
|
||||
values[upperIndex] = in;
|
||||
expiries[upperIndex] = i + length;
|
||||
lowerIndex = upperIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
const ALfloat lower = threshold - (0.5f * knee);
|
||||
const ALfloat upper = threshold + (0.5f * knee);
|
||||
const ALfloat m = 0.5f * Slope / knee;
|
||||
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++)
|
||||
{
|
||||
ALfloat env = Comp->Envelope[i];
|
||||
ALfloat gain;
|
||||
|
||||
if(env > lower && env < upper)
|
||||
gain = m * (env - lower) * (lower - env);
|
||||
else
|
||||
gain = Slope * (threshold - env);
|
||||
|
||||
Comp->Envelope[i] = powf(10.0f, minf(0.0f, gain));
|
||||
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;
|
||||
|
||||
Compressor *CompressorInit(const ALfloat PreGainDb, const ALfloat PostGainDb,
|
||||
const ALboolean SummedLink, const ALboolean RmsSensing,
|
||||
const ALfloat AttackTimeMin, const ALfloat AttackTimeMax,
|
||||
const ALfloat ReleaseTimeMin, const ALfloat ReleaseTimeMax,
|
||||
const ALfloat Ratio, const ALfloat ThresholdDb,
|
||||
const ALfloat KneeDb, const ALuint SampleRate)
|
||||
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;
|
||||
ALsizei i;
|
||||
|
||||
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(RmsSensing)
|
||||
size += sizeof(Comp->RmsWindow[0]) * RMS_WINDOW_SIZE;
|
||||
if(lookAhead > 0)
|
||||
{
|
||||
size += sizeof(*Comp->Delay) * NumChans;
|
||||
if(hold > 0)
|
||||
size += sizeof(*Comp->Hold);
|
||||
}
|
||||
|
||||
Comp = al_calloc(16, size);
|
||||
|
||||
Comp->PreGain = powf(10.0f, PreGainDb / 20.0f);
|
||||
Comp->PostGain = powf(10.0f, PostGainDb / 20.0f);
|
||||
Comp->SummedLink = SummedLink;
|
||||
Comp->AttackMin = 1.0f / maxf(0.000001f, AttackTimeMin * SampleRate * logf(10.0f));
|
||||
Comp->AttackMax = 1.0f / maxf(0.000001f, AttackTimeMax * SampleRate * logf(10.0f));
|
||||
Comp->ReleaseMin = -1.0f / maxf(0.000001f, ReleaseTimeMin * SampleRate * logf(10.0f));
|
||||
Comp->ReleaseMax = -1.0f / maxf(0.000001f, ReleaseTimeMax * SampleRate * logf(10.0f));
|
||||
Comp->Ratio = Ratio;
|
||||
Comp->Threshold = ThresholdDb / 20.0f;
|
||||
Comp->Knee = maxf(0.0f, KneeDb / 20.0f);
|
||||
Comp->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);
|
||||
|
||||
Comp->RmsSum = 0;
|
||||
if(RmsSensing)
|
||||
Comp->RmsWindow = (ALuint*)(Comp+1);
|
||||
else
|
||||
Comp->RmsWindow = NULL;
|
||||
Comp->RmsIndex = 0;
|
||||
/* 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;
|
||||
|
||||
for(i = 0;i < BUFFERSIZE;i++)
|
||||
Comp->Envelope[i] = 0.0f;
|
||||
Comp->EnvLast = -6.0f;
|
||||
if(lookAhead > 0)
|
||||
{
|
||||
if(hold > 0)
|
||||
{
|
||||
Comp->Hold = (SlidingHold*)(Comp + 1);
|
||||
Comp->Hold->Values[0] = -INFINITY;
|
||||
Comp->Hold->Expiries[0] = hold;
|
||||
Comp->Hold->Length = hold;
|
||||
Comp->Delay = (ALfloat(*)[])(Comp->Hold + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
Comp->Delay = (ALfloat(*)[])(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 NumChans, const ALsizei SamplesToDo,
|
||||
ALfloat (*restrict OutBuffer)[BUFFERSIZE])
|
||||
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;
|
||||
|
||||
if(Comp->PreGain != 1.0f)
|
||||
ASSUME(SamplesToDo > 0);
|
||||
ASSUME(numChans > 0);
|
||||
|
||||
if(preGain != 1.0f)
|
||||
{
|
||||
for(c = 0;c < NumChans;c++)
|
||||
for(c = 0;c < numChans;c++)
|
||||
{
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
OutBuffer[c][i] *= Comp->PreGain;
|
||||
OutBuffer[c][i] *= preGain;
|
||||
}
|
||||
}
|
||||
|
||||
if(Comp->SummedLink)
|
||||
SumChannels(Comp, NumChans, SamplesToDo, OutBuffer);
|
||||
LinkChannels(Comp, SamplesToDo, OutBuffer);
|
||||
|
||||
if(Comp->Auto.Attack || Comp->Auto.Release)
|
||||
CrestDetector(Comp, SamplesToDo);
|
||||
|
||||
if(Comp->Hold)
|
||||
PeakHoldDetector(Comp, SamplesToDo);
|
||||
else
|
||||
MaxChannels(Comp, NumChans, SamplesToDo, OutBuffer);
|
||||
PeakDetector(Comp, SamplesToDo);
|
||||
|
||||
if(Comp->RmsWindow)
|
||||
RmsDetection(Comp, SamplesToDo);
|
||||
FollowEnvelope(Comp, SamplesToDo);
|
||||
GainCompressor(Comp, SamplesToDo);
|
||||
|
||||
if(Comp->Ratio > 0.0f)
|
||||
EnvelopeGain(Comp, SamplesToDo, 1.0f - (1.0f / Comp->Ratio));
|
||||
else
|
||||
EnvelopeGain(Comp, SamplesToDo, 1.0f);
|
||||
if(Comp->Delay)
|
||||
SignalDelay(Comp, SamplesToDo, OutBuffer);
|
||||
|
||||
if(Comp->PostGain != 1.0f)
|
||||
sideChain = Comp->SideChain;
|
||||
for(c = 0;c < numChans;c++)
|
||||
{
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
Comp->Envelope[i] *= Comp->PostGain;
|
||||
}
|
||||
for(c = 0;c < NumChans;c++)
|
||||
{
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
OutBuffer[c][i] *= Comp->Envelope[i];
|
||||
OutBuffer[c][i] *= sideChain[i];
|
||||
}
|
||||
|
||||
memmove(sideChain, sideChain+SamplesToDo, Comp->LookAhead*sizeof(ALfloat));
|
||||
}
|
||||
|
||||
|
||||
ALsizei GetCompressorLookAhead(const Compressor *Comp)
|
||||
{ return Comp->LookAhead; }
|
||||
|
||||
@@ -6,52 +6,44 @@
|
||||
/* For BUFFERSIZE. */
|
||||
#include "alMain.h"
|
||||
|
||||
typedef struct Compressor {
|
||||
ALfloat PreGain;
|
||||
ALfloat PostGain;
|
||||
ALboolean SummedLink;
|
||||
ALfloat AttackMin;
|
||||
ALfloat AttackMax;
|
||||
ALfloat ReleaseMin;
|
||||
ALfloat ReleaseMax;
|
||||
ALfloat Ratio;
|
||||
ALfloat Threshold;
|
||||
ALfloat Knee;
|
||||
ALuint SampleRate;
|
||||
struct Compressor;
|
||||
|
||||
ALuint RmsSum;
|
||||
ALuint *RmsWindow;
|
||||
ALsizei RmsIndex;
|
||||
ALfloat Envelope[BUFFERSIZE];
|
||||
ALfloat EnvLast;
|
||||
} Compressor;
|
||||
|
||||
/* The compressor requires the following information for proper
|
||||
* initialization:
|
||||
/* The compressor is initialized with the following settings:
|
||||
*
|
||||
* PreGainDb - Gain applied before detection (in dB).
|
||||
* PostGainDb - Gain applied after compression (in dB).
|
||||
* SummedLink - Whether to use summed (true) or maxed (false) linking.
|
||||
* RmsSensing - Whether to use RMS (true) or Peak (false) sensing.
|
||||
* AttackTimeMin - Minimum attack time (in seconds).
|
||||
* AttackTimeMax - Maximum attack time. Automates when min != max.
|
||||
* ReleaseTimeMin - Minimum release time (in seconds).
|
||||
* ReleaseTimeMax - Maximum release time. Automates when min != max.
|
||||
* Ratio - Compression ratio (x:1). Set to 0 for true limiter.
|
||||
* ThresholdDb - Triggering threshold (in dB).
|
||||
* KneeDb - Knee width (below threshold; in dB).
|
||||
* 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.
|
||||
*/
|
||||
Compressor *CompressorInit(const ALfloat PreGainDb, const ALfloat PostGainDb,
|
||||
const ALboolean SummedLink, const ALboolean RmsSensing, const ALfloat AttackTimeMin,
|
||||
const ALfloat AttackTimeMax, const ALfloat ReleaseTimeMin, const ALfloat ReleaseTimeMax,
|
||||
const ALfloat Ratio, const ALfloat ThresholdDb, const ALfloat KneeDb,
|
||||
const ALuint SampleRate);
|
||||
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 NumChans, const ALsizei SamplesToDo,
|
||||
void ApplyCompression(struct Compressor *Comp, const ALsizei SamplesToDo,
|
||||
ALfloat (*restrict OutBuffer)[BUFFERSIZE]);
|
||||
|
||||
inline ALuint GetCompressorSampleRate(const Compressor *Comp)
|
||||
{ return Comp->SampleRate; }
|
||||
ALsizei GetCompressorLookAhead(const struct Compressor *Comp);
|
||||
|
||||
#endif /* MASTERING_H */
|
||||
|
||||
@@ -62,7 +62,7 @@ void MixRow_SSE(ALfloat *OutBuffer, const ALfloat *Gains,
|
||||
ALsizei InPos, ALsizei BufferSize);
|
||||
|
||||
/* SSE resamplers */
|
||||
inline void InitiatePositionArrays(ALsizei frac, ALint increment, ALsizei *restrict frac_arr, ALint *restrict pos_arr, ALsizei size)
|
||||
inline void InitiatePositionArrays(ALsizei frac, ALint increment, ALsizei *restrict frac_arr, ALsizei *restrict pos_arr, ALsizei size)
|
||||
{
|
||||
ALsizei i;
|
||||
|
||||
|
||||
@@ -22,18 +22,24 @@ void MixHrtf(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
|
||||
{
|
||||
const ALfloat (*Coeffs)[2] = ASSUME_ALIGNED(hrtfparams->Coeffs, 16);
|
||||
const ALsizei Delay[2] = { hrtfparams->Delay[0], hrtfparams->Delay[1] };
|
||||
ALfloat gainstep = hrtfparams->GainStep;
|
||||
ALfloat gain = hrtfparams->Gain;
|
||||
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++);
|
||||
left = hrtfstate->History[(Offset-Delay[0])&HRTF_HISTORY_MASK]*gain;
|
||||
right = hrtfstate->History[(Offset-Delay[1])&HRTF_HISTORY_MASK]*gain;
|
||||
|
||||
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;
|
||||
@@ -42,10 +48,10 @@ void MixHrtf(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
|
||||
*(LeftOut++) += hrtfstate->Values[Offset&HRIR_MASK][0];
|
||||
*(RightOut++) += hrtfstate->Values[Offset&HRIR_MASK][1];
|
||||
|
||||
gain += gainstep;
|
||||
stepcount += 1.0f;
|
||||
Offset++;
|
||||
}
|
||||
hrtfparams->Gain = gain;
|
||||
hrtfparams->Gain = gain + gainstep*stepcount;
|
||||
}
|
||||
|
||||
void MixHrtfBlend(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
|
||||
@@ -56,15 +62,19 @@ void MixHrtfBlend(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
|
||||
{
|
||||
const ALfloat (*OldCoeffs)[2] = ASSUME_ALIGNED(oldparams->Coeffs, 16);
|
||||
const ALsizei OldDelay[2] = { oldparams->Delay[0], oldparams->Delay[1] };
|
||||
ALfloat oldGain = oldparams->Gain;
|
||||
ALfloat oldGainStep = -oldGain / (ALfloat)BufferSize;
|
||||
const ALfloat 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] };
|
||||
ALfloat newGain = newparams->Gain;
|
||||
ALfloat newGainStep = newparams->GainStep;
|
||||
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++)
|
||||
@@ -74,22 +84,23 @@ void MixHrtfBlend(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
|
||||
|
||||
hrtfstate->History[Offset&HRTF_HISTORY_MASK] = *(data++);
|
||||
|
||||
left = hrtfstate->History[(Offset-OldDelay[0])&HRTF_HISTORY_MASK]*oldGain;
|
||||
right = hrtfstate->History[(Offset-OldDelay[1])&HRTF_HISTORY_MASK]*oldGain;
|
||||
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);
|
||||
|
||||
left = hrtfstate->History[(Offset-NewDelay[0])&HRTF_HISTORY_MASK]*newGain;
|
||||
right = hrtfstate->History[(Offset-NewDelay[1])&HRTF_HISTORY_MASK]*newGain;
|
||||
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];
|
||||
|
||||
oldGain += oldGainStep;
|
||||
newGain += newGainStep;
|
||||
stepcount += 1.0f;
|
||||
Offset++;
|
||||
}
|
||||
newparams->Gain = newGain;
|
||||
newparams->Gain = newGain + newGainStep*stepcount;
|
||||
}
|
||||
|
||||
void MixDirectHrtf(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
|
||||
@@ -100,6 +111,9 @@ void MixDirectHrtf(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
|
||||
ALfloat insample;
|
||||
ALsizei i;
|
||||
|
||||
ASSUME(IrSize >= 4);
|
||||
ASSUME(BufferSize > 0);
|
||||
|
||||
for(i = 0;i < BufferSize;i++)
|
||||
{
|
||||
Values[(Offset+IrSize)&HRIR_MASK][0] = 0.0f;
|
||||
|
||||
@@ -9,12 +9,37 @@
|
||||
#include "defs.h"
|
||||
|
||||
|
||||
static inline ALfloat do_point(const ALfloat *restrict vals, ALsizei UNUSED(frac))
|
||||
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 ALfloat *restrict vals, ALsizei frac)
|
||||
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 ALfloat *restrict vals, ALsizei frac)
|
||||
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),
|
||||
@@ -30,16 +55,19 @@ const ALfloat *Resample_copy_C(const InterpState* UNUSED(state),
|
||||
}
|
||||
|
||||
#define DECL_TEMPLATE(Tag, Sampler, O) \
|
||||
const ALfloat *Resample_##Tag##_C(const InterpState* UNUSED(state), \
|
||||
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(src, frac); \
|
||||
dst[i] = Sampler(&istate, src, frac); \
|
||||
\
|
||||
frac += increment; \
|
||||
src += frac>>FRACTIONBITS; \
|
||||
@@ -51,47 +79,10 @@ const ALfloat *Resample_##Tag##_C(const InterpState* UNUSED(state), \
|
||||
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
|
||||
|
||||
const ALfloat *Resample_bsinc_C(const InterpState *state, const ALfloat *restrict src,
|
||||
ALsizei frac, ALint increment, ALfloat *restrict dst,
|
||||
ALsizei dstlen)
|
||||
{
|
||||
const ALfloat *fil, *scd, *phd, *spd;
|
||||
const ALfloat *const filter = state->bsinc.filter;
|
||||
const ALfloat sf = state->bsinc.sf;
|
||||
const ALsizei m = state->bsinc.m;
|
||||
ALsizei j_f, pi, i;
|
||||
ALfloat pf, r;
|
||||
|
||||
src += state->bsinc.l;
|
||||
for(i = 0;i < dstlen;i++)
|
||||
{
|
||||
// Calculate the phase index and factor.
|
||||
#define FRAC_PHASE_BITDIFF (FRACTIONBITS-BSINC_PHASE_BITS)
|
||||
pi = frac >> FRAC_PHASE_BITDIFF;
|
||||
pf = (frac & ((1<<FRAC_PHASE_BITDIFF)-1)) * (1.0f/(1<<FRAC_PHASE_BITDIFF));
|
||||
#undef FRAC_PHASE_BITDIFF
|
||||
|
||||
fil = ASSUME_ALIGNED(filter + m*pi*4, 16);
|
||||
scd = ASSUME_ALIGNED(fil + m, 16);
|
||||
phd = ASSUME_ALIGNED(scd + m, 16);
|
||||
spd = ASSUME_ALIGNED(phd + m, 16);
|
||||
|
||||
// Apply the scale and phase interpolated filter.
|
||||
r = 0.0f;
|
||||
for(j_f = 0;j_f < m;j_f++)
|
||||
r += (fil[j_f] + sf*scd[j_f] + pf*(phd[j_f] + sf*spd[j_f])) * src[j_f];
|
||||
dst[i] = r;
|
||||
|
||||
frac += increment;
|
||||
src += frac>>FRACTIONBITS;
|
||||
frac &= FRACTIONMASK;
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
|
||||
static inline void ApplyCoeffs(ALsizei Offset, ALfloat (*restrict Values)[2],
|
||||
const ALsizei IrSize,
|
||||
@@ -111,33 +102,38 @@ static inline void ApplyCoeffs(ALsizei Offset, ALfloat (*restrict Values)[2],
|
||||
#define MixHrtfBlend MixHrtfBlend_C
|
||||
#define MixDirectHrtf MixDirectHrtf_C
|
||||
#include "hrtf_inc.c"
|
||||
#undef MixHrtf
|
||||
|
||||
|
||||
void Mix_C(const ALfloat *data, ALsizei OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
|
||||
ALfloat *CurrentGains, const ALfloat *TargetGains, ALsizei Counter, ALsizei OutPos,
|
||||
ALsizei BufferSize)
|
||||
{
|
||||
ALfloat gain, delta, step;
|
||||
const ALfloat delta = (Counter > 0) ? 1.0f/(ALfloat)Counter : 0.0f;
|
||||
ALsizei c;
|
||||
|
||||
delta = (Counter > 0) ? 1.0f/(ALfloat)Counter : 0.0f;
|
||||
ASSUME(OutChans > 0);
|
||||
ASSUME(BufferSize > 0);
|
||||
|
||||
for(c = 0;c < OutChans;c++)
|
||||
{
|
||||
ALsizei pos = 0;
|
||||
gain = CurrentGains[c];
|
||||
step = (TargetGains[c] - gain) * delta;
|
||||
if(fabsf(step) > FLT_EPSILON)
|
||||
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;
|
||||
gain += step;
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -158,9 +154,12 @@ void MixRow_C(ALfloat *OutBuffer, const ALfloat *Gains, const ALfloat (*restrict
|
||||
{
|
||||
ALsizei c, i;
|
||||
|
||||
ASSUME(InChans > 0);
|
||||
ASSUME(BufferSize > 0);
|
||||
|
||||
for(c = 0;c < InChans;c++)
|
||||
{
|
||||
ALfloat gain = Gains[c];
|
||||
const ALfloat gain = Gains[c];
|
||||
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
|
||||
continue;
|
||||
|
||||
|
||||
@@ -17,21 +17,25 @@ const ALfloat *Resample_lerp_Neon(const InterpState* UNUSED(state),
|
||||
const int32x4_t increment4 = vdupq_n_s32(increment*4);
|
||||
const float32x4_t fracOne4 = vdupq_n_f32(1.0f/FRACTIONONE);
|
||||
const int32x4_t fracMask4 = vdupq_n_s32(FRACTIONMASK);
|
||||
alignas(16) ALint pos_[4];
|
||||
alignas(16) ALsizei frac_[4];
|
||||
int32x4_t pos4;
|
||||
int32x4_t frac4;
|
||||
ALsizei i;
|
||||
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_);
|
||||
|
||||
for(i = 0;numsamples-i > 3;i += 4)
|
||||
todo = numsamples & ~3;
|
||||
for(i = 0;i < todo;i += 4)
|
||||
{
|
||||
const float32x4_t val1 = (float32x4_t){src[pos_[0]], src[pos_[1]], src[pos_[2]], src[pos_[3]]};
|
||||
const float32x4_t val2 = (float32x4_t){src[pos_[0]+1], src[pos_[1]+1], src[pos_[2]+1], src[pos_[3]+1]};
|
||||
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);
|
||||
@@ -43,25 +47,21 @@ const ALfloat *Resample_lerp_Neon(const InterpState* UNUSED(state),
|
||||
frac4 = vaddq_s32(frac4, increment4);
|
||||
pos4 = vaddq_s32(pos4, vshrq_n_s32(frac4, FRACTIONBITS));
|
||||
frac4 = vandq_s32(frac4, fracMask4);
|
||||
|
||||
vst1q_s32(pos_, pos4);
|
||||
}
|
||||
|
||||
if(i < numsamples)
|
||||
{
|
||||
/* NOTE: These four elements represent the position *after* the last
|
||||
* four samples, so the lowest element is the next position to
|
||||
* resample.
|
||||
*/
|
||||
ALint pos = pos_[0];
|
||||
frac = vgetq_lane_s32(frac4, 0);
|
||||
do {
|
||||
dst[i] = lerp(src[pos], src[pos+1], frac * (1.0f/FRACTIONONE));
|
||||
/* 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);
|
||||
|
||||
frac += increment;
|
||||
pos += frac>>FRACTIONBITS;
|
||||
frac &= FRACTIONMASK;
|
||||
} while(++i < numsamples);
|
||||
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;
|
||||
}
|
||||
@@ -78,7 +78,10 @@ const ALfloat *Resample_bsinc_Neon(const InterpState *state,
|
||||
float32x4_t r4;
|
||||
ALfloat pf;
|
||||
|
||||
src += state->bsinc.l;
|
||||
ASSUME(m > 0);
|
||||
ASSUME(dstlen > 0);
|
||||
|
||||
src -= state->bsinc.l;
|
||||
for(i = 0;i < dstlen;i++)
|
||||
{
|
||||
// Calculate the phase index and factor.
|
||||
@@ -96,16 +99,20 @@ const ALfloat *Resample_bsinc_Neon(const InterpState *state,
|
||||
// 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);
|
||||
for(j = 0;j < m;j+=4,fil++,scd++,phd++,spd++)
|
||||
|
||||
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, sf4, *scd),
|
||||
pf4, vmlaq_f32(*phd, sf4, *spd)
|
||||
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]));
|
||||
r4 = vmlaq_f32(r4, f4, vld1q_f32(&src[j*4]));
|
||||
}
|
||||
}
|
||||
r4 = vaddq_f32(r4, vcombine_f32(vrev64_f32(vget_high_f32(r4)),
|
||||
@@ -154,61 +161,69 @@ static inline void ApplyCoeffs(ALsizei Offset, ALfloat (*restrict Values)[2],
|
||||
#define MixHrtfBlend MixHrtfBlend_Neon
|
||||
#define MixDirectHrtf MixDirectHrtf_Neon
|
||||
#include "hrtf_inc.c"
|
||||
#undef MixHrtf
|
||||
|
||||
|
||||
void Mix_Neon(const ALfloat *data, ALsizei OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
|
||||
ALfloat *CurrentGains, const ALfloat *TargetGains, ALsizei Counter, ALsizei OutPos,
|
||||
ALsizei BufferSize)
|
||||
{
|
||||
ALfloat gain, delta, step;
|
||||
float32x4_t gain4;
|
||||
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);
|
||||
|
||||
delta = (Counter > 0) ? 1.0f/(ALfloat)Counter : 0.0f;
|
||||
|
||||
for(c = 0;c < OutChans;c++)
|
||||
{
|
||||
ALsizei pos = 0;
|
||||
gain = CurrentGains[c];
|
||||
step = (TargetGains[c] - gain) * delta;
|
||||
if(fabsf(step) > FLT_EPSILON)
|
||||
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(minsize-pos > 3)
|
||||
if(LIKELY(minsize > 3))
|
||||
{
|
||||
float32x4_t step4;
|
||||
gain4 = vsetq_lane_f32(gain, gain4, 0);
|
||||
gain4 = vsetq_lane_f32(gain + step, gain4, 1);
|
||||
gain4 = vsetq_lane_f32(gain + step + step, gain4, 2);
|
||||
gain4 = vsetq_lane_f32(gain + step + step + step, gain4, 3);
|
||||
step4 = vdupq_n_f32(step + step + step + step);
|
||||
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, gain4);
|
||||
gain4 = vaddq_f32(gain4, step4);
|
||||
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(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.
|
||||
} 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.
|
||||
*/
|
||||
gain = vgetq_lane_f32(gain4, 0);
|
||||
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;
|
||||
gain += step;
|
||||
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. */
|
||||
@@ -219,13 +234,17 @@ void Mix_Neon(const ALfloat *data, ALsizei OutChans, ALfloat (*restrict OutBuffe
|
||||
|
||||
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
|
||||
continue;
|
||||
gain4 = vdupq_n_f32(gain);
|
||||
for(;BufferSize-pos > 3;pos += 4)
|
||||
if(LIKELY(BufferSize-pos > 3))
|
||||
{
|
||||
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);
|
||||
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;
|
||||
@@ -234,26 +253,29 @@ void Mix_Neon(const ALfloat *data, ALsizei OutChans, ALfloat (*restrict OutBuffe
|
||||
|
||||
void MixRow_Neon(ALfloat *OutBuffer, const ALfloat *Gains, const ALfloat (*restrict data)[BUFFERSIZE], ALsizei InChans, ALsizei InPos, ALsizei BufferSize)
|
||||
{
|
||||
float32x4_t gain4;
|
||||
ALsizei c;
|
||||
|
||||
data = ASSUME_ALIGNED(data, 16);
|
||||
OutBuffer = ASSUME_ALIGNED(OutBuffer, 16);
|
||||
ASSUME(InChans > 0);
|
||||
ASSUME(BufferSize > 0);
|
||||
|
||||
for(c = 0;c < InChans;c++)
|
||||
{
|
||||
ALsizei pos = 0;
|
||||
ALfloat gain = Gains[c];
|
||||
const ALfloat gain = Gains[c];
|
||||
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
|
||||
continue;
|
||||
|
||||
gain4 = vdupq_n_f32(gain);
|
||||
for(;BufferSize-pos > 3;pos += 4)
|
||||
if(LIKELY(BufferSize > 3))
|
||||
{
|
||||
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);
|
||||
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;
|
||||
|
||||
@@ -24,7 +24,10 @@ const ALfloat *Resample_bsinc_SSE(const InterpState *state, const ALfloat *restr
|
||||
ALfloat pf;
|
||||
__m128 r4;
|
||||
|
||||
src += state->bsinc.l;
|
||||
ASSUME(m > 0);
|
||||
ASSUME(dstlen > 0);
|
||||
|
||||
src -= state->bsinc.l;
|
||||
for(i = 0;i < dstlen;i++)
|
||||
{
|
||||
// Calculate the phase index and factor.
|
||||
@@ -42,17 +45,21 @@ const ALfloat *Resample_bsinc_SSE(const InterpState *state, const ALfloat *restr
|
||||
// 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 < m;j+=4,fil++,scd++,phd++,spd++)
|
||||
for(j = 0;j < count;j++)
|
||||
{
|
||||
/* f = ((fil + sf*scd) + pf*(phd + sf*spd)) */
|
||||
const __m128 f4 = MLA4(
|
||||
MLA4(*fil, sf4, *scd),
|
||||
pf4, MLA4(*phd, sf4, *spd)
|
||||
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]));
|
||||
r4 = MLA4(r4, f4, _mm_loadu_ps(&src[j*4]));
|
||||
}
|
||||
#undef MLA4
|
||||
}
|
||||
@@ -126,60 +133,64 @@ static inline void ApplyCoeffs(ALsizei Offset, ALfloat (*restrict Values)[2],
|
||||
#define MixHrtfBlend MixHrtfBlend_SSE
|
||||
#define MixDirectHrtf MixDirectHrtf_SSE
|
||||
#include "hrtf_inc.c"
|
||||
#undef MixHrtf
|
||||
|
||||
|
||||
void Mix_SSE(const ALfloat *data, ALsizei OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
|
||||
ALfloat *CurrentGains, const ALfloat *TargetGains, ALsizei Counter, ALsizei OutPos,
|
||||
ALsizei BufferSize)
|
||||
{
|
||||
ALfloat gain, delta, step;
|
||||
__m128 gain4;
|
||||
const ALfloat delta = (Counter > 0) ? 1.0f/(ALfloat)Counter : 0.0f;
|
||||
ALsizei c;
|
||||
|
||||
delta = (Counter > 0) ? 1.0f/(ALfloat)Counter : 0.0f;
|
||||
ASSUME(OutChans > 0);
|
||||
ASSUME(BufferSize > 0);
|
||||
|
||||
for(c = 0;c < OutChans;c++)
|
||||
{
|
||||
ALsizei pos = 0;
|
||||
gain = CurrentGains[c];
|
||||
step = (TargetGains[c] - gain) * delta;
|
||||
if(fabsf(step) > FLT_EPSILON)
|
||||
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(minsize-pos > 3)
|
||||
if(LIKELY(minsize > 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);
|
||||
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]);
|
||||
dry4 = _mm_add_ps(dry4, _mm_mul_ps(val4, gain4));
|
||||
gain4 = _mm_add_ps(gain4, step4);
|
||||
#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(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.
|
||||
} 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.
|
||||
*/
|
||||
gain = _mm_cvtss_f32(gain4);
|
||||
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;
|
||||
gain += step;
|
||||
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. */
|
||||
@@ -190,13 +201,17 @@ void Mix_SSE(const ALfloat *data, ALsizei OutChans, ALfloat (*restrict OutBuffer
|
||||
|
||||
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
|
||||
continue;
|
||||
gain4 = _mm_set1_ps(gain);
|
||||
for(;BufferSize-pos > 3;pos += 4)
|
||||
if(LIKELY(BufferSize-pos > 3))
|
||||
{
|
||||
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);
|
||||
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;
|
||||
@@ -205,23 +220,29 @@ void Mix_SSE(const ALfloat *data, ALsizei OutChans, ALfloat (*restrict OutBuffer
|
||||
|
||||
void MixRow_SSE(ALfloat *OutBuffer, const ALfloat *Gains, const ALfloat (*restrict data)[BUFFERSIZE], ALsizei InChans, ALsizei InPos, ALsizei BufferSize)
|
||||
{
|
||||
__m128 gain4;
|
||||
ALsizei c;
|
||||
|
||||
ASSUME(InChans > 0);
|
||||
ASSUME(BufferSize > 0);
|
||||
|
||||
for(c = 0;c < InChans;c++)
|
||||
{
|
||||
ALsizei pos = 0;
|
||||
ALfloat gain = Gains[c];
|
||||
const ALfloat gain = Gains[c];
|
||||
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
|
||||
continue;
|
||||
|
||||
gain4 = _mm_set1_ps(gain);
|
||||
for(;BufferSize-pos > 3;pos += 4)
|
||||
if(LIKELY(BufferSize > 3))
|
||||
{
|
||||
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);
|
||||
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;
|
||||
|
||||
@@ -34,21 +34,25 @@ const ALfloat *Resample_lerp_SSE2(const InterpState* UNUSED(state),
|
||||
const __m128i increment4 = _mm_set1_epi32(increment*4);
|
||||
const __m128 fracOne4 = _mm_set1_ps(1.0f/FRACTIONONE);
|
||||
const __m128i fracMask4 = _mm_set1_epi32(FRACTIONMASK);
|
||||
union { alignas(16) ALint i[4]; float f[4]; } pos_;
|
||||
union { alignas(16) ALsizei i[4]; float f[4]; } frac_;
|
||||
alignas(16) ALsizei pos_[4], frac_[4];
|
||||
__m128i frac4, pos4;
|
||||
ALint pos;
|
||||
ALsizei 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_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);
|
||||
@@ -60,17 +64,15 @@ const ALfloat *Resample_lerp_SSE2(const InterpState* UNUSED(state),
|
||||
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));
|
||||
|
||||
|
||||
@@ -35,21 +35,25 @@ const ALfloat *Resample_lerp_SSE41(const InterpState* UNUSED(state),
|
||||
const __m128i increment4 = _mm_set1_epi32(increment*4);
|
||||
const __m128 fracOne4 = _mm_set1_ps(1.0f/FRACTIONONE);
|
||||
const __m128i fracMask4 = _mm_set1_epi32(FRACTIONMASK);
|
||||
union { alignas(16) ALint i[4]; float f[4]; } pos_;
|
||||
union { alignas(16) ALsizei i[4]; float f[4]; } frac_;
|
||||
alignas(16) ALsizei pos_[4], frac_[4];
|
||||
__m128i frac4, pos4;
|
||||
ALint pos;
|
||||
ALsizei 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);
|
||||
@@ -61,20 +65,15 @@ const ALfloat *Resample_lerp_SSE41(const InterpState* UNUSED(state),
|
||||
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];
|
||||
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));
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
static_assert((INT_MAX>>FRACTIONBITS)/MAX_PITCH > BUFFERSIZE,
|
||||
"MAX_PITCH and/or BUFFERSIZE are too large for FRACTIONBITS!");
|
||||
|
||||
extern inline void InitiatePositionArrays(ALsizei frac, ALint increment, ALsizei *restrict frac_arr, ALint *restrict pos_arr, ALsizei size);
|
||||
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. */
|
||||
@@ -197,12 +197,11 @@ void aluInitMixer(void)
|
||||
static void SendAsyncEvent(ALCcontext *context, ALuint enumtype, ALenum type,
|
||||
ALuint objid, ALuint param, const char *msg)
|
||||
{
|
||||
AsyncEvent evt;
|
||||
evt.EnumType = enumtype;
|
||||
evt.Type = type;
|
||||
evt.ObjectId = objid;
|
||||
evt.Param = param;
|
||||
strcpy(evt.Message, 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);
|
||||
}
|
||||
@@ -263,7 +262,7 @@ static void LoadSamples(ALfloat *restrict dst, const ALvoid *restrict src, ALint
|
||||
}
|
||||
|
||||
|
||||
static const ALfloat *DoFilters(BiquadState *lpfilter, BiquadState *hpfilter,
|
||||
static const ALfloat *DoFilters(BiquadFilter *lpfilter, BiquadFilter *hpfilter,
|
||||
ALfloat *restrict dst, const ALfloat *restrict src,
|
||||
ALsizei numsamples, enum ActiveFilters type)
|
||||
{
|
||||
@@ -271,17 +270,17 @@ static const ALfloat *DoFilters(BiquadState *lpfilter, BiquadState *hpfilter,
|
||||
switch(type)
|
||||
{
|
||||
case AF_None:
|
||||
BiquadState_processPassthru(lpfilter, src, numsamples);
|
||||
BiquadState_processPassthru(hpfilter, src, numsamples);
|
||||
BiquadFilter_passthru(lpfilter, numsamples);
|
||||
BiquadFilter_passthru(hpfilter, numsamples);
|
||||
break;
|
||||
|
||||
case AF_LowPass:
|
||||
BiquadState_process(lpfilter, dst, src, numsamples);
|
||||
BiquadState_processPassthru(hpfilter, dst, numsamples);
|
||||
BiquadFilter_process(lpfilter, dst, src, numsamples);
|
||||
BiquadFilter_passthru(hpfilter, numsamples);
|
||||
return dst;
|
||||
case AF_HighPass:
|
||||
BiquadState_processPassthru(lpfilter, src, numsamples);
|
||||
BiquadState_process(hpfilter, dst, src, numsamples);
|
||||
BiquadFilter_passthru(lpfilter, numsamples);
|
||||
BiquadFilter_process(hpfilter, dst, src, numsamples);
|
||||
return dst;
|
||||
|
||||
case AF_BandPass:
|
||||
@@ -290,8 +289,8 @@ static const ALfloat *DoFilters(BiquadState *lpfilter, BiquadState *hpfilter,
|
||||
ALfloat temp[256];
|
||||
ALsizei todo = mini(256, numsamples-i);
|
||||
|
||||
BiquadState_process(lpfilter, temp, src+i, todo);
|
||||
BiquadState_process(hpfilter, dst+i, temp, todo);
|
||||
BiquadFilter_process(lpfilter, temp, src+i, todo);
|
||||
BiquadFilter_process(hpfilter, dst+i, temp, todo);
|
||||
i += todo;
|
||||
}
|
||||
return dst;
|
||||
@@ -487,6 +486,7 @@ ALboolean MixSource(ALvoice *voice, ALuint SourceID, ALCcontext *Context, ALsize
|
||||
while(tmpiter && SrcBufferSize > FilledAmt)
|
||||
{
|
||||
ALsizei SizeToDo = SrcBufferSize - FilledAmt;
|
||||
ALsizei CompLen = 0;
|
||||
ALsizei i;
|
||||
|
||||
for(i = 0;i < tmpiter->num_buffers;i++)
|
||||
@@ -499,23 +499,24 @@ ALboolean MixSource(ALvoice *voice, ALuint SourceID, ALCcontext *Context, ALsize
|
||||
const ALubyte *Data = ALBuffer->data;
|
||||
Data += (pos*NumChannels + chan)*SampleSize;
|
||||
|
||||
DataSize = minu(SizeToDo, DataSize - pos);
|
||||
DataSize = mini(SizeToDo, DataSize - pos);
|
||||
CompLen = maxi(CompLen, DataSize);
|
||||
|
||||
LoadSamples(&SrcData[FilledAmt], Data, NumChannels,
|
||||
ALBuffer->FmtType, DataSize);
|
||||
}
|
||||
}
|
||||
if(pos > tmpiter->max_samples)
|
||||
if(UNLIKELY(!CompLen))
|
||||
pos -= tmpiter->max_samples;
|
||||
else
|
||||
{
|
||||
FilledAmt += tmpiter->max_samples - pos;
|
||||
FilledAmt += CompLen;
|
||||
if(SrcBufferSize <= FilledAmt)
|
||||
break;
|
||||
pos = 0;
|
||||
}
|
||||
if(SrcBufferSize > FilledAmt)
|
||||
{
|
||||
tmpiter = ATOMIC_LOAD(&tmpiter->next, almemory_order_acquire);
|
||||
if(!tmpiter) tmpiter = BufferLoopItem;
|
||||
}
|
||||
tmpiter = ATOMIC_LOAD(&tmpiter->next, almemory_order_acquire);
|
||||
if(!tmpiter) tmpiter = BufferLoopItem;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -729,8 +730,10 @@ ALboolean MixSource(ALvoice *voice, ALuint SourceID, ALCcontext *Context, ALsize
|
||||
if(BufferListItem->max_samples > DataPosInt)
|
||||
break;
|
||||
|
||||
DataPosInt -= BufferListItem->max_samples;
|
||||
|
||||
buffers_done += BufferListItem->num_buffers;
|
||||
BufferListItem = ATOMIC_LOAD(&BufferListItem->next, almemory_order_acquire);
|
||||
BufferListItem = ATOMIC_LOAD(&BufferListItem->next, almemory_order_relaxed);
|
||||
if(!BufferListItem && !(BufferListItem=BufferLoopItem))
|
||||
{
|
||||
isplaying = false;
|
||||
@@ -738,8 +741,6 @@ ALboolean MixSource(ALvoice *voice, ALuint SourceID, ALCcontext *Context, ALsize
|
||||
DataPosFrac = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
DataPosInt -= BufferListItem->max_samples;
|
||||
}
|
||||
} while(isplaying && OutPos < SamplesToDo);
|
||||
|
||||
|
||||
@@ -33,13 +33,15 @@
|
||||
#include "bool.h"
|
||||
#include "ambdec.h"
|
||||
#include "bformatdec.h"
|
||||
#include "filters/splitter.h"
|
||||
#include "uhjfilter.h"
|
||||
#include "bs2b.h"
|
||||
|
||||
|
||||
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 void ComputeDryPanGains(const DryMixParams *dry, const ALfloat coeffs[MAX_AMBI_COEFFS], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]);
|
||||
extern inline void ComputeFirstOrderGains(const BFMixParams *foa, const ALfloat mtx[4], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]);
|
||||
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 ALsizei FuMa2ACN[MAX_AMBI_COEFFS] = {
|
||||
@@ -66,19 +68,15 @@ static const ALsizei ACN2ACN[MAX_AMBI_COEFFS] = {
|
||||
};
|
||||
|
||||
|
||||
void CalcDirectionCoeffs(const ALfloat dir[3], ALfloat spread, ALfloat coeffs[MAX_AMBI_COEFFS])
|
||||
void CalcAmbiCoeffs(const ALfloat y, const ALfloat z, const ALfloat x, const ALfloat spread,
|
||||
ALfloat coeffs[MAX_AMBI_COEFFS])
|
||||
{
|
||||
/* 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 */
|
||||
@@ -152,16 +150,8 @@ void CalcDirectionCoeffs(const ALfloat dir[3], ALfloat spread, ALfloat coeffs[MA
|
||||
}
|
||||
}
|
||||
|
||||
void CalcAnglePairwiseCoeffs(ALfloat azimuth, ALfloat elevation, ALfloat spread, ALfloat coeffs[MAX_AMBI_COEFFS])
|
||||
{
|
||||
ALfloat sign = (azimuth < 0.0f) ? -1.0f : 1.0f;
|
||||
if(!(fabsf(azimuth) > F_PI_2))
|
||||
azimuth = minf(fabsf(azimuth) * F_PI_2 / (F_PI/6.0f), F_PI_2) * sign;
|
||||
CalcAngleCoeffs(azimuth, elevation, spread, coeffs);
|
||||
}
|
||||
|
||||
|
||||
void ComputePanningGainsMC(const ChannelConfig *chancoeffs, ALsizei numchans, ALsizei numcoeffs, const ALfloat coeffs[MAX_AMBI_COEFFS], 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])
|
||||
{
|
||||
ALsizei i, j;
|
||||
|
||||
@@ -176,7 +166,7 @@ void ComputePanningGainsMC(const ChannelConfig *chancoeffs, ALsizei numchans, AL
|
||||
gains[i] = 0.0f;
|
||||
}
|
||||
|
||||
void ComputePanningGainsBF(const BFChannelConfig *chanmap, ALsizei numchans, const ALfloat coeffs[MAX_AMBI_COEFFS], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS])
|
||||
void ComputePanningGainsBF(const BFChannelConfig *chanmap, ALsizei numchans, const ALfloat*restrict coeffs, ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS])
|
||||
{
|
||||
ALsizei i;
|
||||
|
||||
@@ -186,31 +176,6 @@ void ComputePanningGainsBF(const BFChannelConfig *chanmap, ALsizei numchans, con
|
||||
gains[i] = 0.0f;
|
||||
}
|
||||
|
||||
void ComputeFirstOrderGainsMC(const ChannelConfig *chancoeffs, ALsizei numchans, const ALfloat mtx[4], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS])
|
||||
{
|
||||
ALsizei i, j;
|
||||
|
||||
for(i = 0;i < numchans;i++)
|
||||
{
|
||||
float gain = 0.0f;
|
||||
for(j = 0;j < 4;j++)
|
||||
gain += chancoeffs[i][j] * mtx[j];
|
||||
gains[i] = clampf(gain, 0.0f, 1.0f) * ingain;
|
||||
}
|
||||
for(;i < MAX_OUTPUT_CHANNELS;i++)
|
||||
gains[i] = 0.0f;
|
||||
}
|
||||
|
||||
void ComputeFirstOrderGainsBF(const BFChannelConfig *chanmap, ALsizei numchans, const ALfloat mtx[4], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS])
|
||||
{
|
||||
ALsizei i;
|
||||
|
||||
for(i = 0;i < numchans;i++)
|
||||
gains[i] = chanmap[i].Scale * mtx[chanmap[i].Index] * ingain;
|
||||
for(;i < MAX_OUTPUT_CHANNELS;i++)
|
||||
gains[i] = 0.0f;
|
||||
}
|
||||
|
||||
|
||||
static inline const char *GetLabelFromChannel(enum Channel channel)
|
||||
{
|
||||
@@ -383,8 +348,8 @@ static bool MakeSpeakerMap(ALCdevice *device, const AmbDecConf *conf, ALsizei sp
|
||||
static const ChannelMap MonoCfg[1] = {
|
||||
{ FrontCenter, { 1.0f } },
|
||||
}, StereoCfg[2] = {
|
||||
{ FrontLeft, { 5.00000000e-1f, 2.88675135e-1f, 0.0f, 5.77350269e-2f } },
|
||||
{ FrontRight, { 5.00000000e-1f, -2.88675135e-1f, 0.0f, 5.77350269e-2f } },
|
||||
{ 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 } },
|
||||
@@ -415,7 +380,8 @@ static const ChannelMap MonoCfg[1] = {
|
||||
{ BackRight, { 2.04124145e-1f, -1.08880247e-1f, 0.0f, -1.88586120e-1f, 1.29099444e-1f, 0.0f, 0.0f, 0.0f, 7.45355993e-2f, -3.73460789e-2f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.00000000e+0f } },
|
||||
};
|
||||
|
||||
static void InitNearFieldCtrl(ALCdevice *device, ALfloat ctrl_dist, ALsizei order, bool periphonic)
|
||||
static void InitNearFieldCtrl(ALCdevice *device, ALfloat ctrl_dist, ALsizei order,
|
||||
const ALsizei *restrict chans_per_order)
|
||||
{
|
||||
const char *devname = alstr_get_cstr(device->DeviceName);
|
||||
ALsizei i;
|
||||
@@ -426,16 +392,12 @@ static void InitNearFieldCtrl(ALCdevice *device, ALfloat ctrl_dist, ALsizei orde
|
||||
* 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);
|
||||
|
||||
device->Dry.NumChannelsPerOrder[0] = 1;
|
||||
if(periphonic)
|
||||
for(i = 1;i < order+1;i++)
|
||||
device->Dry.NumChannelsPerOrder[i] = (i+1)*(i+1) - i*i;
|
||||
else
|
||||
for(i = 1;i < order+1;i++)
|
||||
device->Dry.NumChannelsPerOrder[i] = (i*2+1) - ((i-1)*2+1);
|
||||
for(i = 0;i < order+1;i++)
|
||||
device->NumChannelsPerOrder[i] = chans_per_order[i];
|
||||
for(;i < MAX_AMBI_ORDER+1;i++)
|
||||
device->Dry.NumChannelsPerOrder[i] = 0;
|
||||
device->NumChannelsPerOrder[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -609,9 +571,12 @@ static void InitPanning(ALCdevice *device)
|
||||
|
||||
if(ConfigValueFloat(devname, "decoder", "nfc-ref-delay", &nfc_delay) && nfc_delay > 0.0f)
|
||||
{
|
||||
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, true);
|
||||
device->AmbiOrder, chans_per_order);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -727,6 +692,8 @@ static void InitCustomPanning(ALCdevice *device, const AmbDecConf *conf, const A
|
||||
|
||||
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;
|
||||
@@ -803,7 +770,7 @@ static void InitHQPanning(ALCdevice *device, const AmbDecConf *conf, const ALsiz
|
||||
avg_dist /= (ALfloat)conf->NumSpeakers;
|
||||
InitNearFieldCtrl(device, avg_dist,
|
||||
(conf->ChanMask > 0x1ff) ? 3 : (conf->ChanMask > 0xf) ? 2 : 1,
|
||||
!!(conf->ChanMask&AMBI_PERIPHONIC_MASK)
|
||||
(conf->ChanMask&AMBI_PERIPHONIC_MASK) ? chans_per_order3d : chans_per_order2d
|
||||
);
|
||||
|
||||
InitDistanceComp(device, conf, speakermap);
|
||||
@@ -814,10 +781,10 @@ static void InitHrtfPanning(ALCdevice *device)
|
||||
/* NOTE: azimuth goes clockwise. */
|
||||
static const struct AngularPoint AmbiPoints[] = {
|
||||
{ DEG2RAD( 90.0f), DEG2RAD( 0.0f) },
|
||||
{ DEG2RAD( 35.0f), DEG2RAD( 45.0f) },
|
||||
{ DEG2RAD( 35.0f), DEG2RAD( 135.0f) },
|
||||
{ DEG2RAD( 35.0f), DEG2RAD(-135.0f) },
|
||||
{ DEG2RAD( 35.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( 0.0f), DEG2RAD( 0.0f) },
|
||||
{ DEG2RAD( 0.0f), DEG2RAD( 45.0f) },
|
||||
{ DEG2RAD( 0.0f), DEG2RAD( 90.0f) },
|
||||
@@ -826,10 +793,10 @@ static void InitHrtfPanning(ALCdevice *device)
|
||||
{ DEG2RAD( 0.0f), DEG2RAD(-135.0f) },
|
||||
{ DEG2RAD( 0.0f), DEG2RAD( -90.0f) },
|
||||
{ DEG2RAD( 0.0f), DEG2RAD( -45.0f) },
|
||||
{ DEG2RAD(-35.0f), DEG2RAD( 45.0f) },
|
||||
{ DEG2RAD(-35.0f), DEG2RAD( 135.0f) },
|
||||
{ DEG2RAD(-35.0f), DEG2RAD(-135.0f) },
|
||||
{ DEG2RAD(-35.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) },
|
||||
};
|
||||
static const ALfloat AmbiMatrixFOA[][MAX_AMBI_COEFFS] = {
|
||||
@@ -872,11 +839,12 @@ static void InitHrtfPanning(ALCdevice *device)
|
||||
{ 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] = {
|
||||
1.00000000e+00f, 5.77350269e-01f
|
||||
3.00000000e+00f, 1.73205081e+00f
|
||||
}, AmbiOrderHFGainHOA[MAX_AMBI_ORDER+1] = {
|
||||
9.80580676e-01f, 7.59554525e-01f, 3.92232270e-01f
|
||||
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;
|
||||
@@ -884,7 +852,6 @@ static void InitHrtfPanning(ALCdevice *device)
|
||||
|
||||
static_assert(COUNTOF(AmbiPoints) == COUNTOF(AmbiMatrixFOA), "FOA Ambisonic HRTF mismatch");
|
||||
static_assert(COUNTOF(AmbiPoints) == COUNTOF(AmbiMatrixHOA), "HOA Ambisonic HRTF mismatch");
|
||||
static_assert(COUNTOF(AmbiPoints) <= HRTF_AMBI_MAX_CHANNELS, "HRTF_AMBI_MAX_CHANNELS is too small");
|
||||
|
||||
if(device->AmbiUp)
|
||||
{
|
||||
@@ -931,22 +898,8 @@ static void InitHrtfPanning(ALCdevice *device)
|
||||
AmbiOrderHFGain
|
||||
);
|
||||
|
||||
if(GetConfigValueBool(alstr_get_cstr(device->DeviceName), "decoder", "nfc", 1) &&
|
||||
device->HrtfHandle->distance > 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(device->HrtfHandle->distance, 10.0f);
|
||||
|
||||
i = 0;
|
||||
device->Dry.NumChannelsPerOrder[i++] = 1;
|
||||
device->Dry.NumChannelsPerOrder[i++] = 3;
|
||||
if(device->AmbiUp)
|
||||
device->Dry.NumChannelsPerOrder[i++] = 2;
|
||||
while(i < MAX_AMBI_ORDER+1)
|
||||
device->Dry.NumChannelsPerOrder[i++] = 0;
|
||||
}
|
||||
InitNearFieldCtrl(device, device->HrtfHandle->distance, device->AmbiUp ? 2 : 1,
|
||||
ChansPerOrder);
|
||||
}
|
||||
|
||||
static void InitUhjPanning(ALCdevice *device)
|
||||
@@ -989,7 +942,7 @@ void aluInitRenderer(ALCdevice *device, ALint hrtf_id, enum HrtfRequestMode hrtf
|
||||
device->Dry.CoeffCount = 0;
|
||||
device->Dry.NumChannels = 0;
|
||||
for(i = 0;i < MAX_AMBI_ORDER+1;i++)
|
||||
device->Dry.NumChannelsPerOrder[i] = 0;
|
||||
device->NumChannelsPerOrder[i] = 0;
|
||||
|
||||
device->AvgSpeakerDist = 0.0f;
|
||||
memset(device->ChannelDelay, 0, sizeof(device->ChannelDelay));
|
||||
|
||||
@@ -9,44 +9,29 @@
|
||||
#define MAX_UPDATE_SAMPLES 128
|
||||
|
||||
|
||||
static const ALfloat Filter1Coeff[4] = {
|
||||
0.6923878f, 0.9360654322959f, 0.9882295226860f, 0.9987488452737f
|
||||
static const ALfloat Filter1CoeffSqr[4] = {
|
||||
0.479400865589f, 0.876218493539f, 0.976597589508f, 0.997499255936f
|
||||
};
|
||||
static const ALfloat Filter2Coeff[4] = {
|
||||
0.4021921162426f, 0.8561710882420f, 0.9722909545651f, 0.9952884791278f
|
||||
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;
|
||||
|
||||
if(LIKELY(todo > 1))
|
||||
for(i = 0;i < todo;i++)
|
||||
{
|
||||
ALfloat x0 = state->x[0];
|
||||
ALfloat x1 = state->x[1];
|
||||
ALfloat y0 = state->y[0];
|
||||
ALfloat y1 = state->y[1];
|
||||
|
||||
for(i = 0;i < todo;i++)
|
||||
{
|
||||
dst[i] = aa*(src[i] + y1) - x1;
|
||||
y1 = y0; y0 = dst[i];
|
||||
x1 = x0; x0 = src[i];
|
||||
}
|
||||
|
||||
state->x[0] = x0;
|
||||
state->x[1] = x1;
|
||||
state->y[0] = y0;
|
||||
state->y[1] = y1;
|
||||
}
|
||||
else if(todo == 1)
|
||||
{
|
||||
dst[0] = aa*(src[0] + state->y[1]) - state->x[1];
|
||||
state->x[1] = state->x[0];
|
||||
state->x[0] = src[0];
|
||||
state->y[1] = state->y[0];
|
||||
state->y[0] = dst[0];
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -76,41 +61,37 @@ void EncodeUhj2(Uhj2Encoder *enc, ALfloat *restrict LeftOut, ALfloat *restrict R
|
||||
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],
|
||||
Filter1Coeff[0]*Filter1Coeff[0], todo);
|
||||
allpass_process(&enc->Filter1_Y[1], temp[0], temp[1],
|
||||
Filter1Coeff[1]*Filter1Coeff[1], todo);
|
||||
allpass_process(&enc->Filter1_Y[2], temp[1], temp[0],
|
||||
Filter1Coeff[2]*Filter1Coeff[2], todo);
|
||||
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->Filter1_Y[3].y[0];
|
||||
allpass_process(&enc->Filter1_Y[3], temp[0], temp[1],
|
||||
Filter1Coeff[3]*Filter1Coeff[3], todo);
|
||||
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],
|
||||
Filter2Coeff[0]*Filter2Coeff[0], todo);
|
||||
allpass_process(&enc->Filter2_WX[1], temp[0], temp[1],
|
||||
Filter2Coeff[1]*Filter2Coeff[1], todo);
|
||||
allpass_process(&enc->Filter2_WX[2], temp[1], temp[0],
|
||||
Filter2Coeff[2]*Filter2Coeff[2], todo);
|
||||
allpass_process(&enc->Filter2_WX[3], temp[0], temp[1],
|
||||
Filter2Coeff[3]*Filter2Coeff[3], todo);
|
||||
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];
|
||||
|
||||
@@ -118,17 +99,14 @@ void EncodeUhj2(Uhj2Encoder *enc, ALfloat *restrict LeftOut, ALfloat *restrict R
|
||||
for(i = 0;i < todo;i++)
|
||||
temp[0][i] = 0.9396926f*InSamples[0][base+i] +
|
||||
0.1855740f*InSamples[1][base+i];
|
||||
allpass_process(&enc->Filter1_WX[0], temp[1], temp[0],
|
||||
Filter1Coeff[0]*Filter1Coeff[0], todo);
|
||||
allpass_process(&enc->Filter1_WX[1], temp[0], temp[1],
|
||||
Filter1Coeff[1]*Filter1Coeff[1], todo);
|
||||
allpass_process(&enc->Filter1_WX[2], temp[1], temp[0],
|
||||
Filter1Coeff[2]*Filter1Coeff[2], todo);
|
||||
S[0] = enc->Filter1_WX[3].y[0];
|
||||
allpass_process(&enc->Filter1_WX[3], temp[0], temp[1],
|
||||
Filter1Coeff[3]*Filter1Coeff[3], todo);
|
||||
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++)
|
||||
|
||||
@@ -6,8 +6,7 @@
|
||||
#include "alMain.h"
|
||||
|
||||
typedef struct AllPassState {
|
||||
ALfloat x[2]; /* Last two input samples */
|
||||
ALfloat y[2]; /* Last two output samples */
|
||||
ALfloat z[2];
|
||||
} AllPassState;
|
||||
|
||||
/* Encoding 2-channel UHJ from B-Format is done as:
|
||||
@@ -36,9 +35,10 @@ typedef struct AllPassState {
|
||||
*/
|
||||
|
||||
typedef struct Uhj2Encoder {
|
||||
AllPassState Filter1_WX[4];
|
||||
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
|
||||
|
||||
+160
-69
@@ -41,16 +41,16 @@ OPTION(ALSOFT_DLOPEN "Check for the dlopen API for loading optional libs" ON)
|
||||
|
||||
OPTION(ALSOFT_WERROR "Treat compile warnings as errors" OFF)
|
||||
|
||||
OPTION(ALSOFT_UTILS "Build and install utility programs" OFF)
|
||||
OPTION(ALSOFT_NO_CONFIG_UTIL "Disable building the alsoft-config utility" ON)
|
||||
OPTION(ALSOFT_UTILS "Build and install utility programs" ON)
|
||||
OPTION(ALSOFT_NO_CONFIG_UTIL "Disable building the alsoft-config utility" OFF)
|
||||
|
||||
OPTION(ALSOFT_EXAMPLES "Build and install example programs" OFF)
|
||||
OPTION(ALSOFT_TESTS "Build and install test programs" OFF)
|
||||
OPTION(ALSOFT_EXAMPLES "Build and install example programs" ON)
|
||||
OPTION(ALSOFT_TESTS "Build and install test programs" ON)
|
||||
|
||||
OPTION(ALSOFT_CONFIG "Install alsoft.conf sample configuration file" OFF)
|
||||
OPTION(ALSOFT_HRTF_DEFS "Install HRTF definition files" OFF)
|
||||
OPTION(ALSOFT_AMBDEC_PRESETS "Install AmbDec preset files" OFF)
|
||||
OPTION(ALSOFT_INSTALL "Install headers and libraries" OFF)
|
||||
OPTION(ALSOFT_CONFIG "Install alsoft.conf sample configuration file" ON)
|
||||
OPTION(ALSOFT_HRTF_DEFS "Install HRTF definition files" ON)
|
||||
OPTION(ALSOFT_AMBDEC_PRESETS "Install AmbDec preset files" ON)
|
||||
OPTION(ALSOFT_INSTALL "Install headers and libraries" ON)
|
||||
|
||||
if(DEFINED SHARE_INSTALL_DIR)
|
||||
message(WARNING "SHARE_INSTALL_DIR is deprecated. Use the variables provided by the GNUInstallDirs module instead")
|
||||
@@ -102,8 +102,8 @@ IF(NOT LIBTYPE)
|
||||
ENDIF()
|
||||
|
||||
SET(LIB_MAJOR_VERSION "1")
|
||||
SET(LIB_MINOR_VERSION "18")
|
||||
SET(LIB_REVISION "2")
|
||||
SET(LIB_MINOR_VERSION "19")
|
||||
SET(LIB_REVISION "1")
|
||||
SET(LIB_VERSION "${LIB_MAJOR_VERSION}.${LIB_MINOR_VERSION}.${LIB_REVISION}")
|
||||
|
||||
SET(EXPORT_DECL "")
|
||||
@@ -432,6 +432,48 @@ IF(HAVE_MFPU_NEON_SWITCH)
|
||||
SET(FPU_NEON_SWITCH "-mfpu=neon")
|
||||
ENDIF()
|
||||
|
||||
SET(FPMATH_SET "0")
|
||||
IF(CMAKE_SIZEOF_VOID_P MATCHES "4")
|
||||
IF(SSE_SWITCH OR MSVC)
|
||||
OPTION(ALSOFT_ENABLE_SSE_CODEGEN "Enable SSE code generation instead of x87 for 32-bit targets." TRUE)
|
||||
ENDIF()
|
||||
IF(SSE2_SWITCH OR MSVC)
|
||||
OPTION(ALSOFT_ENABLE_SSE2_CODEGEN "Enable SSE2 code generation instead of x87 for 32-bit targets." TRUE)
|
||||
ENDIF()
|
||||
|
||||
IF(ALSOFT_ENABLE_SSE2_CODEGEN)
|
||||
IF(SSE2_SWITCH)
|
||||
CHECK_C_COMPILER_FLAG("${SSE2_SWITCH} -mfpmath=sse" HAVE_MFPMATH_SSE_2)
|
||||
IF(HAVE_MFPMATH_SSE_2)
|
||||
SET(C_FLAGS ${C_FLAGS} ${SSE2_SWITCH} -mfpmath=sse)
|
||||
SET(FPMATH_SET 2)
|
||||
ENDIF()
|
||||
ELSEIF(MSVC)
|
||||
CHECK_C_COMPILER_FLAG("/arch:SSE2" HAVE_ARCH_SSE2)
|
||||
IF(HAVE_ARCH_SSE2)
|
||||
SET(C_FLAGS ${C_FLAGS} "/arch:SSE2")
|
||||
SET(FPMATH_SET 2)
|
||||
ENDIF()
|
||||
ENDIF()
|
||||
ENDIF()
|
||||
IF(ALSOFT_ENABLE_SSE_CODEGEN AND NOT FPMATH_SET)
|
||||
IF(SSE_SWITCH)
|
||||
CHECK_C_COMPILER_FLAG("${SSE_SWITCH} -mfpmath=sse" HAVE_MFPMATH_SSE)
|
||||
IF(HAVE_MFPMATH_SSE)
|
||||
SET(C_FLAGS ${C_FLAGS} ${SSE_SWITCH} -mfpmath=sse)
|
||||
SET(FPMATH_SET 1)
|
||||
ENDIF()
|
||||
ELSEIF(MSVC)
|
||||
CHECK_C_COMPILER_FLAG("/arch:SSE" HAVE_ARCH_SSE)
|
||||
IF(HAVE_ARCH_SSE)
|
||||
SET(C_FLAGS ${C_FLAGS} "/arch:SSE")
|
||||
SET(FPMATH_SET 1)
|
||||
ENDIF()
|
||||
ENDIF()
|
||||
ENDIF()
|
||||
ENDIF()
|
||||
|
||||
|
||||
CHECK_C_SOURCE_COMPILES("int foo(const char *str, ...) __attribute__((format(printf, 1, 2)));
|
||||
int main() {return 0;}" HAVE_GCC_FORMAT)
|
||||
|
||||
@@ -512,6 +554,7 @@ CHECK_SYMBOL_EXISTS(lrintf math.h HAVE_LRINTF)
|
||||
CHECK_SYMBOL_EXISTS(modff math.h HAVE_MODFF)
|
||||
CHECK_SYMBOL_EXISTS(log2f math.h HAVE_LOG2F)
|
||||
CHECK_SYMBOL_EXISTS(cbrtf math.h HAVE_CBRTF)
|
||||
CHECK_SYMBOL_EXISTS(copysignf math.h HAVE_COPYSIGNF)
|
||||
|
||||
IF(HAVE_FLOAT_H)
|
||||
CHECK_SYMBOL_EXISTS(_controlfp float.h HAVE__CONTROLFP)
|
||||
@@ -699,51 +742,100 @@ IF(NOT HAVE_STDINT_H)
|
||||
ENDIF()
|
||||
|
||||
|
||||
SET(COMMON_OBJS common/almalloc.c
|
||||
common/atomic.c
|
||||
common/rwlock.c
|
||||
common/threads.c
|
||||
common/uintmap.c
|
||||
SET(COMMON_OBJS
|
||||
common/alcomplex.c
|
||||
common/alcomplex.h
|
||||
common/align.h
|
||||
common/almalloc.c
|
||||
common/almalloc.h
|
||||
common/atomic.c
|
||||
common/atomic.h
|
||||
common/bool.h
|
||||
common/math_defs.h
|
||||
common/rwlock.c
|
||||
common/rwlock.h
|
||||
common/static_assert.h
|
||||
common/threads.c
|
||||
common/threads.h
|
||||
common/uintmap.c
|
||||
common/uintmap.h
|
||||
)
|
||||
SET(OPENAL_OBJS OpenAL32/alAuxEffectSlot.c
|
||||
OpenAL32/alBuffer.c
|
||||
OpenAL32/alEffect.c
|
||||
OpenAL32/alError.c
|
||||
OpenAL32/alExtension.c
|
||||
OpenAL32/alFilter.c
|
||||
OpenAL32/alListener.c
|
||||
OpenAL32/alSource.c
|
||||
OpenAL32/alState.c
|
||||
OpenAL32/event.c
|
||||
OpenAL32/sample_cvt.c
|
||||
SET(OPENAL_OBJS
|
||||
OpenAL32/Include/bs2b.h
|
||||
OpenAL32/Include/alMain.h
|
||||
OpenAL32/Include/alu.h
|
||||
|
||||
OpenAL32/Include/alAuxEffectSlot.h
|
||||
OpenAL32/alAuxEffectSlot.c
|
||||
OpenAL32/Include/alBuffer.h
|
||||
OpenAL32/alBuffer.c
|
||||
OpenAL32/Include/alEffect.h
|
||||
OpenAL32/alEffect.c
|
||||
OpenAL32/Include/alError.h
|
||||
OpenAL32/alError.c
|
||||
OpenAL32/alExtension.c
|
||||
OpenAL32/Include/alFilter.h
|
||||
OpenAL32/alFilter.c
|
||||
OpenAL32/Include/alListener.h
|
||||
OpenAL32/alListener.c
|
||||
OpenAL32/Include/alSource.h
|
||||
OpenAL32/alSource.c
|
||||
OpenAL32/alState.c
|
||||
OpenAL32/event.c
|
||||
OpenAL32/Include/sample_cvt.h
|
||||
OpenAL32/sample_cvt.c
|
||||
)
|
||||
SET(ALC_OBJS Alc/ALc.c
|
||||
Alc/ALu.c
|
||||
Alc/alconfig.c
|
||||
Alc/bs2b.c
|
||||
Alc/converter.c
|
||||
Alc/mastering.c
|
||||
Alc/ringbuffer.c
|
||||
Alc/effects/chorus.c
|
||||
Alc/effects/compressor.c
|
||||
Alc/effects/dedicated.c
|
||||
Alc/effects/distortion.c
|
||||
Alc/effects/echo.c
|
||||
Alc/effects/equalizer.c
|
||||
Alc/effects/modulator.c
|
||||
Alc/effects/null.c
|
||||
Alc/effects/pshifter.c
|
||||
Alc/effects/reverb.c
|
||||
Alc/filters/filter.c
|
||||
Alc/filters/nfc.c
|
||||
Alc/helpers.c
|
||||
Alc/hrtf.c
|
||||
Alc/uhjfilter.c
|
||||
Alc/ambdec.c
|
||||
Alc/bformatdec.c
|
||||
Alc/panning.c
|
||||
Alc/mixvoice.c
|
||||
Alc/mixer/mixer_c.c
|
||||
SET(ALC_OBJS
|
||||
Alc/ALc.c
|
||||
Alc/ALu.c
|
||||
Alc/alconfig.c
|
||||
Alc/alconfig.h
|
||||
Alc/bs2b.c
|
||||
Alc/converter.c
|
||||
Alc/converter.h
|
||||
Alc/inprogext.h
|
||||
Alc/mastering.c
|
||||
Alc/mastering.h
|
||||
Alc/ringbuffer.c
|
||||
Alc/ringbuffer.h
|
||||
Alc/effects/autowah.c
|
||||
Alc/effects/chorus.c
|
||||
Alc/effects/compressor.c
|
||||
Alc/effects/dedicated.c
|
||||
Alc/effects/distortion.c
|
||||
Alc/effects/echo.c
|
||||
Alc/effects/equalizer.c
|
||||
Alc/effects/fshifter.c
|
||||
Alc/effects/modulator.c
|
||||
Alc/effects/null.c
|
||||
Alc/effects/pshifter.c
|
||||
Alc/effects/reverb.c
|
||||
Alc/filters/defs.h
|
||||
Alc/filters/filter.c
|
||||
Alc/filters/nfc.c
|
||||
Alc/filters/nfc.h
|
||||
Alc/filters/splitter.c
|
||||
Alc/filters/splitter.h
|
||||
Alc/helpers.c
|
||||
Alc/alstring.h
|
||||
Alc/compat.h
|
||||
Alc/cpu_caps.h
|
||||
Alc/fpu_modes.h
|
||||
Alc/logging.h
|
||||
Alc/vector.h
|
||||
Alc/hrtf.c
|
||||
Alc/hrtf.h
|
||||
Alc/uhjfilter.c
|
||||
Alc/uhjfilter.h
|
||||
Alc/ambdec.c
|
||||
Alc/ambdec.h
|
||||
Alc/bformatdec.c
|
||||
Alc/bformatdec.h
|
||||
Alc/panning.c
|
||||
Alc/polymorphism.h
|
||||
Alc/mixvoice.c
|
||||
Alc/mixer/defs.h
|
||||
Alc/mixer/mixer_c.c
|
||||
)
|
||||
|
||||
|
||||
@@ -810,7 +902,7 @@ IF(ALSOFT_REQUIRE_SSE2 AND NOT HAVE_SSE2)
|
||||
MESSAGE(FATAL_ERROR "Failed to enable required SSE2 CPU extensions")
|
||||
ENDIF()
|
||||
|
||||
OPTION(ALSOFT_REQUIRE_SSE2 "Require SSE3 support" OFF)
|
||||
OPTION(ALSOFT_REQUIRE_SSE3 "Require SSE3 support" OFF)
|
||||
CHECK_INCLUDE_FILE(pmmintrin.h HAVE_PMMINTRIN_H "${SSE3_SWITCH}")
|
||||
IF(HAVE_EMMINTRIN_H)
|
||||
OPTION(ALSOFT_CPUEXT_SSE3 "Enable SSE3 support" ON)
|
||||
@@ -883,10 +975,11 @@ ENDIF()
|
||||
|
||||
SET(BACKENDS "")
|
||||
SET(ALC_OBJS ${ALC_OBJS}
|
||||
Alc/backends/base.c
|
||||
# Default backends, always available
|
||||
Alc/backends/loopback.c
|
||||
Alc/backends/null.c
|
||||
Alc/backends/base.c
|
||||
Alc/backends/base.h
|
||||
# Default backends, always available
|
||||
Alc/backends/loopback.c
|
||||
Alc/backends/null.c
|
||||
)
|
||||
|
||||
# Check ALSA backend
|
||||
@@ -1209,7 +1302,7 @@ ADD_CUSTOM_TARGET(native-tools
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
option(ALSOFT_EMBED_HRTF_DATA "Embed the HRTF data files (increases library footprint)" OFF)
|
||||
option(ALSOFT_EMBED_HRTF_DATA "Embed the HRTF data files (increases library footprint)" ON)
|
||||
if(ALSOFT_EMBED_HRTF_DATA)
|
||||
MACRO(make_hrtf_header FILENAME VARNAME)
|
||||
SET(infile "${OpenAL_SOURCE_DIR}/hrtf/${FILENAME}")
|
||||
@@ -1319,7 +1412,7 @@ ELSE()
|
||||
ENDIF()
|
||||
|
||||
IF(WIN32 AND ALSOFT_BUILD_ROUTER)
|
||||
ADD_LIBRARY(OpenAL SHARED router/router.c router/alc.c router/al.c)
|
||||
ADD_LIBRARY(OpenAL SHARED router/router.c router/router.h router/alc.c router/al.c)
|
||||
TARGET_COMPILE_DEFINITIONS(OpenAL
|
||||
PRIVATE AL_BUILD_LIBRARY AL_ALEXT_PROTOTYPES ${CPP_DEFS})
|
||||
TARGET_COMPILE_OPTIONS(OpenAL PRIVATE ${C_FLAGS})
|
||||
@@ -1351,13 +1444,10 @@ TARGET_INCLUDE_DIRECTORIES(${IMPL_TARGET}
|
||||
TARGET_COMPILE_OPTIONS(${IMPL_TARGET} PRIVATE ${C_FLAGS})
|
||||
TARGET_LINK_LIBRARIES(${IMPL_TARGET}
|
||||
PRIVATE ${LINKER_FLAGS} ${COMMON_LIB} ${EXTRA_LIBS} ${MATH_LIB})
|
||||
TARGET_INCLUDE_DIRECTORIES(${IMPL_TARGET} PUBLIC include)
|
||||
IF(TARGET build_version)
|
||||
ADD_DEPENDENCIES(${IMPL_TARGET} build_version)
|
||||
ENDIF()
|
||||
|
||||
SET(OPENAL_LIB_NAME ${IMPL_TARGET} PARENT_SCOPE)
|
||||
|
||||
IF(WIN32 AND MINGW AND ALSOFT_BUILD_IMPORT_LIB AND NOT LIBTYPE STREQUAL "STATIC")
|
||||
FIND_PROGRAM(SED_EXECUTABLE NAMES sed DOC "sed executable")
|
||||
FIND_PROGRAM(DLLTOOL_EXECUTABLE NAMES "${DLLTOOL}" DOC "dlltool executable")
|
||||
@@ -1425,6 +1515,10 @@ MESSAGE(STATUS "")
|
||||
MESSAGE(STATUS "Building with support for CPU extensions:")
|
||||
MESSAGE(STATUS " ${CPU_EXTS}")
|
||||
MESSAGE(STATUS "")
|
||||
IF(FPMATH_SET)
|
||||
MESSAGE(STATUS "Building with SSE${FPMATH_SET} codegen")
|
||||
MESSAGE(STATUS "")
|
||||
ENDIF()
|
||||
|
||||
IF(WIN32)
|
||||
IF(NOT HAVE_DSOUND)
|
||||
@@ -1480,7 +1574,7 @@ IF(ALSOFT_UTILS)
|
||||
|
||||
SET(MAKEHRTF_SRCS utils/makehrtf.c)
|
||||
IF(NOT HAVE_GETOPT)
|
||||
SET(MAKEHRTF_SRCS ${MAKEHRTF_SRCS} utils/getopt.c)
|
||||
SET(MAKEHRTF_SRCS ${MAKEHRTF_SRCS} utils/getopt.c utils/getopt.h)
|
||||
ENDIF()
|
||||
ADD_EXECUTABLE(makehrtf ${MAKEHRTF_SRCS})
|
||||
TARGET_COMPILE_DEFINITIONS(makehrtf PRIVATE ${CPP_DEFS})
|
||||
@@ -1652,7 +1746,8 @@ IF(ALSOFT_EXAMPLES)
|
||||
PRIVATE ${SDL2_INCLUDE_DIR} ${FFMPEG_INCLUDE_DIRS})
|
||||
TARGET_COMPILE_OPTIONS(alffplay PRIVATE ${C_FLAGS})
|
||||
TARGET_LINK_LIBRARIES(alffplay
|
||||
PRIVATE ${LINKER_FLAGS} ${SDL2_LIBRARY} ${FFMPEG_LIBRARIES} common OpenAL)
|
||||
PRIVATE ${LINKER_FLAGS} ${SDL2_LIBRARY} ${FFMPEG_LIBRARIES} ex-common common
|
||||
OpenAL)
|
||||
|
||||
IF(ALSOFT_INSTALL)
|
||||
INSTALL(TARGETS alffplay
|
||||
@@ -1666,7 +1761,3 @@ IF(ALSOFT_EXAMPLES)
|
||||
MESSAGE(STATUS "")
|
||||
ENDIF()
|
||||
ENDIF()
|
||||
|
||||
if(MEGA)
|
||||
install(TARGETS ${IMPL_TARGET} RUNTIME DESTINATION . LIBRARY DESTINATION .)
|
||||
ENDIF()
|
||||
|
||||
@@ -51,7 +51,7 @@ library. If the library is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original
|
||||
version, so that any problems introduced by others will not reflect on
|
||||
the original authors' reputations.
|
||||
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that companies distributing free
|
||||
software will individually obtain patent licenses, thus in effect
|
||||
@@ -98,7 +98,7 @@ works together with the library.
|
||||
|
||||
Note that it is possible for a library to be covered by the ordinary
|
||||
General Public License rather than by this special one.
|
||||
|
||||
|
||||
GNU LIBRARY GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
@@ -145,7 +145,7 @@ Library.
|
||||
You may charge a fee for the physical act of transferring a copy,
|
||||
and you may at your option offer warranty protection in exchange for a
|
||||
fee.
|
||||
|
||||
|
||||
2. You may modify your copy or copies of the Library or any portion
|
||||
of it, thus forming a work based on the Library, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
@@ -203,7 +203,7 @@ instead of to this License. (If a newer version than version 2 of the
|
||||
ordinary GNU General Public License has appeared, then you can specify
|
||||
that version instead if you wish.) Do not make any other change in
|
||||
these notices.
|
||||
|
||||
|
||||
Once this change is made in a given copy, it is irreversible for
|
||||
that copy, so the ordinary GNU General Public License applies to all
|
||||
subsequent copies and derivative works made from that copy.
|
||||
@@ -254,7 +254,7 @@ Library will still fall under Section 6.)
|
||||
distribute the object code for the work under the terms of Section 6.
|
||||
Any executables containing that work also fall under Section 6,
|
||||
whether or not they are linked directly with the Library itself.
|
||||
|
||||
|
||||
6. As an exception to the Sections above, you may also compile or
|
||||
link a "work that uses the Library" with the Library to produce a
|
||||
work containing portions of the Library, and distribute that work
|
||||
@@ -308,7 +308,7 @@ restrictions of other proprietary libraries that do not normally
|
||||
accompany the operating system. Such a contradiction means you cannot
|
||||
use both them and the Library together in an executable that you
|
||||
distribute.
|
||||
|
||||
|
||||
7. You may place library facilities that are a work based on the
|
||||
Library side-by-side in a single library together with other library
|
||||
facilities not covered by this License, and distribute such a combined
|
||||
@@ -349,7 +349,7 @@ subject to these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
|
||||
11. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
@@ -401,7 +401,7 @@ conditions either of that version or of any later version published by
|
||||
the Free Software Foundation. If the Library does not specify a
|
||||
license version number, you may choose any version ever published by
|
||||
the Free Software Foundation.
|
||||
|
||||
|
||||
14. If you wish to incorporate parts of the Library into other free
|
||||
programs whose distribution conditions are incompatible with these,
|
||||
write to the author to ask for permission. For software which is
|
||||
@@ -435,47 +435,3 @@ SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Libraries
|
||||
|
||||
If you develop a new library, and you want it to be of the greatest
|
||||
possible use to the public, we recommend making it free software that
|
||||
everyone can redistribute and change. You can do so by permitting
|
||||
redistribution under these terms (or, alternatively, under the terms of the
|
||||
ordinary General Public License).
|
||||
|
||||
To apply these terms, attach the following notices to the library. It is
|
||||
safest to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least the
|
||||
"copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the library's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the library, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the
|
||||
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1990
|
||||
Ty Coon, President of Vice
|
||||
|
||||
That's all there is to it!
|
||||
|
||||
@@ -1,3 +1,70 @@
|
||||
openal-soft-1.19.1:
|
||||
|
||||
Implemented capture support for the SoundIO backend.
|
||||
|
||||
Fixed source buffer queues potentially not playing properly when a queue
|
||||
entry completes.
|
||||
|
||||
Fixed possible unexpected failures when generating auxiliary effect slots.
|
||||
|
||||
Fixed a crash with certain reverb or device settings.
|
||||
|
||||
Fixed OpenSL capture.
|
||||
|
||||
Improved output limiter response, better ensuring the sample amplitude is
|
||||
clamped for output.
|
||||
|
||||
openal-soft-1.19.0:
|
||||
|
||||
Implemented the ALC_SOFT_device_clock extension.
|
||||
|
||||
Implemented the Pitch Shifter, Frequency Shifter, and Autowah effects.
|
||||
|
||||
Fixed compiling on FreeBSD systems that use freebsd-lib 9.1.
|
||||
|
||||
Fixed compiling on NetBSD.
|
||||
|
||||
Fixed the reverb effect's density scale and panning parameters.
|
||||
|
||||
Fixed use of the WASAPI backend with certain games, which caused odd COM
|
||||
initialization errors.
|
||||
|
||||
Increased the number of virtual channels for decoding Ambisonics to HRTF
|
||||
output.
|
||||
|
||||
Changed 32-bit x86 builds to use SSE2 math by default for performance.
|
||||
Build-time options are available to use just SSE1 or x87 instead.
|
||||
|
||||
Replaced the 4-point Sinc resampler with a more efficient cubic resampler.
|
||||
|
||||
Renamed the MMDevAPI backend to WASAPI.
|
||||
|
||||
Added support for 24-bit, dual-ear HRTF data sets. The built-in data set
|
||||
has been updated to 24-bit.
|
||||
|
||||
Added a 24- to 48-point band-limited Sinc resampler.
|
||||
|
||||
Added an SDL2 playback backend. Disabled by default to avoid a dependency
|
||||
on SDL2.
|
||||
|
||||
Improved the performance and quality of the Chorus and Flanger effects.
|
||||
|
||||
Improved the efficiency of the band-limited Sinc resampler.
|
||||
|
||||
Improved the Sinc resampler's transition band to avoid over-attenuating
|
||||
higher frequencies.
|
||||
|
||||
Improved the performance of some filter operations.
|
||||
|
||||
Improved the efficiency of object ID lookups.
|
||||
|
||||
Improved the efficienty of internal voice/source synchronization.
|
||||
|
||||
Improved AL call error logging with contextualized messages.
|
||||
|
||||
Removed the reverb effect's modulation stage. Due to the lack of reference
|
||||
for its intended behavior and strength.
|
||||
|
||||
openal-soft-1.18.2:
|
||||
|
||||
Fixed resetting the FPU rounding mode after certain function calls on
|
||||
|
||||
@@ -145,8 +145,8 @@ typedef struct ALeffectslot {
|
||||
* * Channel 3 is OpenAL -Z * sqrt(3)
|
||||
* Consequently, effects that only want to work with mono input can use
|
||||
* channel 0 by itself. Effects that want multichannel can process the
|
||||
* ambisonics signal and make a B-Format pan (ComputeFirstOrderGains) for
|
||||
* first-order device output (FOAOut).
|
||||
* ambisonics signal and make a B-Format source pan for first-order device
|
||||
* output (FOAOut).
|
||||
*/
|
||||
alignas(16) ALfloat WetBuffer[MAX_EFFECT_CHANNELS][BUFFERSIZE];
|
||||
} ALeffectslot;
|
||||
@@ -160,12 +160,14 @@ ALvoid ReleaseALAuxiliaryEffectSlots(ALCcontext *Context);
|
||||
|
||||
EffectStateFactory *NullStateFactory_getFactory(void);
|
||||
EffectStateFactory *ReverbStateFactory_getFactory(void);
|
||||
EffectStateFactory *AutowahStateFactory_getFactory(void);
|
||||
EffectStateFactory *ChorusStateFactory_getFactory(void);
|
||||
EffectStateFactory *CompressorStateFactory_getFactory(void);
|
||||
EffectStateFactory *DistortionStateFactory_getFactory(void);
|
||||
EffectStateFactory *EchoStateFactory_getFactory(void);
|
||||
EffectStateFactory *EqualizerStateFactory_getFactory(void);
|
||||
EffectStateFactory *FlangerStateFactory_getFactory(void);
|
||||
EffectStateFactory *FshifterStateFactory_getFactory(void);
|
||||
EffectStateFactory *ModulatorStateFactory_getFactory(void);
|
||||
EffectStateFactory *PshifterStateFactory_getFactory(void);
|
||||
|
||||
|
||||
@@ -12,12 +12,14 @@ struct ALeffect;
|
||||
enum {
|
||||
EAXREVERB_EFFECT = 0,
|
||||
REVERB_EFFECT,
|
||||
AUTOWAH_EFFECT,
|
||||
CHORUS_EFFECT,
|
||||
COMPRESSOR_EFFECT,
|
||||
DISTORTION_EFFECT,
|
||||
ECHO_EFFECT,
|
||||
EQUALIZER_EFFECT,
|
||||
FLANGER_EFFECT,
|
||||
FSHIFTER_EFFECT,
|
||||
MODULATOR_EFFECT,
|
||||
PSHIFTER_EFFECT,
|
||||
DEDICATED_EFFECT,
|
||||
@@ -33,7 +35,7 @@ struct EffectList {
|
||||
int type;
|
||||
ALenum val;
|
||||
};
|
||||
#define EFFECTLIST_SIZE 12
|
||||
#define EFFECTLIST_SIZE 14
|
||||
extern const struct EffectList EffectList[EFFECTLIST_SIZE];
|
||||
|
||||
|
||||
@@ -59,12 +61,14 @@ const struct ALeffectVtable T##_vtable = { \
|
||||
|
||||
extern const struct ALeffectVtable ALeaxreverb_vtable;
|
||||
extern const struct ALeffectVtable ALreverb_vtable;
|
||||
extern const struct ALeffectVtable ALautowah_vtable;
|
||||
extern const struct ALeffectVtable ALchorus_vtable;
|
||||
extern const struct ALeffectVtable ALcompressor_vtable;
|
||||
extern const struct ALeffectVtable ALdistortion_vtable;
|
||||
extern const struct ALeffectVtable ALecho_vtable;
|
||||
extern const struct ALeffectVtable ALequalizer_vtable;
|
||||
extern const struct ALeffectVtable ALflanger_vtable;
|
||||
extern const struct ALeffectVtable ALfshifter_vtable;
|
||||
extern const struct ALeffectVtable ALmodulator_vtable;
|
||||
extern const struct ALeffectVtable ALnull_vtable;
|
||||
extern const struct ALeffectVtable ALpshifter_vtable;
|
||||
@@ -101,6 +105,13 @@ typedef union ALeffectProps {
|
||||
ALfloat LFReference;
|
||||
} Reverb;
|
||||
|
||||
struct {
|
||||
ALfloat AttackTime;
|
||||
ALfloat ReleaseTime;
|
||||
ALfloat Resonance;
|
||||
ALfloat PeakGain;
|
||||
} Autowah;
|
||||
|
||||
struct {
|
||||
ALint Waveform;
|
||||
ALint Phase;
|
||||
@@ -145,6 +156,12 @@ typedef union ALeffectProps {
|
||||
ALfloat HighGain;
|
||||
} Equalizer;
|
||||
|
||||
struct {
|
||||
ALfloat Frequency;
|
||||
ALint LeftDirection;
|
||||
ALint RightDirection;
|
||||
} Fshifter;
|
||||
|
||||
struct {
|
||||
ALfloat Frequency;
|
||||
ALfloat HighPassCutoff;
|
||||
|
||||
@@ -40,13 +40,39 @@
|
||||
#define SZFMT "%zu"
|
||||
#endif
|
||||
|
||||
#ifdef __has_builtin
|
||||
#define HAS_BUILTIN __has_builtin
|
||||
#else
|
||||
#define HAS_BUILTIN(x) (0)
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
/* LIKELY optimizes the case where the condition is true. The condition is not
|
||||
* required to be true, but it can result in more optimal code for the true
|
||||
* path at the expense of a less optimal false path.
|
||||
*/
|
||||
#define LIKELY(x) __builtin_expect(!!(x), !0)
|
||||
/* The opposite of LIKELY, optimizing the case where the condition is false. */
|
||||
#define UNLIKELY(x) __builtin_expect(!!(x), 0)
|
||||
/* Unlike LIKELY, ASSUME requires the condition to be true or else it invokes
|
||||
* undefined behavior. It's essentially an assert without actually checking the
|
||||
* condition at run-time, allowing for stronger optimizations than LIKELY.
|
||||
*/
|
||||
#if HAS_BUILTIN(__builtin_assume)
|
||||
#define ASSUME __builtin_assume
|
||||
#else
|
||||
#define ASSUME(x) do { if(!(x)) __builtin_unreachable(); } while(0)
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
#define LIKELY(x) (!!(x))
|
||||
#define UNLIKELY(x) (!!(x))
|
||||
#ifdef _MSC_VER
|
||||
#define ASSUME __assume
|
||||
#else
|
||||
#define ASSUME(x) ((void)0)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef UINT64_MAX
|
||||
@@ -89,15 +115,25 @@ typedef ALuint64SOFT ALuint64;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef I64
|
||||
#if defined(_MSC_VER)
|
||||
#define I64(x) ((ALint64)(x##i64))
|
||||
#elif SIZEOF_LONG == 8
|
||||
#define I64(x) ((ALint64)(x##l))
|
||||
#elif SIZEOF_LONG_LONG == 8
|
||||
#define I64(x) ((ALint64)(x##ll))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Define a CTZ64 macro (count trailing zeros, for 64-bit integers). The result
|
||||
* is *UNDEFINED* if the value is 0.
|
||||
*/
|
||||
#ifdef __GNUC__
|
||||
|
||||
#if SIZEOF_LONG == 8
|
||||
#define CTZ64(x) __builtin_ctzl(x)
|
||||
#define CTZ64 __builtin_ctzl
|
||||
#else
|
||||
#define CTZ64(x) __builtin_ctzll(x)
|
||||
#define CTZ64 __builtin_ctzll
|
||||
#endif
|
||||
|
||||
#elif defined(HAVE_BITSCANFORWARD64_INTRINSIC)
|
||||
@@ -108,7 +144,7 @@ inline int msvc64_ctz64(ALuint64 v)
|
||||
_BitScanForward64(&idx, v);
|
||||
return (int)idx;
|
||||
}
|
||||
#define CTZ64(x) msvc64_ctz64(x)
|
||||
#define CTZ64 msvc64_ctz64
|
||||
|
||||
#elif defined(HAVE_BITSCANFORWARD_INTRINSIC)
|
||||
|
||||
@@ -122,7 +158,7 @@ inline int msvc_ctz64(ALuint64 v)
|
||||
}
|
||||
return (int)idx;
|
||||
}
|
||||
#define CTZ64(x) msvc_ctz64(x)
|
||||
#define CTZ64 msvc_ctz64
|
||||
|
||||
#else
|
||||
|
||||
@@ -145,14 +181,18 @@ inline int fallback_ctz64(ALuint64 value)
|
||||
{
|
||||
return fallback_popcnt64(~value & (value - 1));
|
||||
}
|
||||
#define CTZ64(x) fallback_ctz64(x)
|
||||
#define CTZ64 fallback_ctz64
|
||||
#endif
|
||||
|
||||
#if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__)
|
||||
#define IS_LITTLE_ENDIAN (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
|
||||
#else
|
||||
static const union {
|
||||
ALuint u;
|
||||
ALubyte b[sizeof(ALuint)];
|
||||
} EndianTest = { 1 };
|
||||
#define IS_LITTLE_ENDIAN (EndianTest.b[0] == 1)
|
||||
#endif
|
||||
|
||||
#define COUNTOF(x) (sizeof(x) / sizeof(0[x]))
|
||||
|
||||
@@ -200,22 +240,132 @@ inline size_t RoundUp(size_t value, size_t r)
|
||||
return value - (value%r);
|
||||
}
|
||||
|
||||
/* Fast float-to-int conversion. Assumes the FPU is already in round-to-zero
|
||||
* mode. */
|
||||
/* Fast float-to-int conversion. No particular rounding mode is assumed; the
|
||||
* IEEE-754 default is round-to-nearest with ties-to-even, though an app could
|
||||
* change it on its own threads. On some systems, a truncating conversion may
|
||||
* always be the fastest method.
|
||||
*/
|
||||
inline ALint fastf2i(ALfloat f)
|
||||
{
|
||||
#ifdef HAVE_LRINTF
|
||||
return lrintf(f);
|
||||
#elif defined(_MSC_VER) && defined(_M_IX86)
|
||||
#if defined(HAVE_INTRIN_H) && ((defined(_M_IX86_FP) && (_M_IX86_FP > 0)) || defined(_M_X64))
|
||||
return _mm_cvt_ss2si(_mm_set1_ps(f));
|
||||
|
||||
#elif defined(_MSC_VER) && defined(_M_IX86_FP)
|
||||
|
||||
ALint i;
|
||||
__asm fld f
|
||||
__asm fistp i
|
||||
return i;
|
||||
|
||||
#elif (defined(__GNUC__) || defined(__clang__)) && (defined(__i386__) || defined(__x86_64__))
|
||||
|
||||
ALint i;
|
||||
#ifdef __SSE_MATH__
|
||||
__asm__("cvtss2si %1, %0" : "=r"(i) : "x"(f));
|
||||
#else
|
||||
__asm__ __volatile__("fistpl %0" : "=m"(i) : "t"(f) : "st");
|
||||
#endif
|
||||
return i;
|
||||
|
||||
/* On GCC when compiling with -fno-math-errno, lrintf can be inlined to
|
||||
* some simple instructions. Clang does not inline it, always generating a
|
||||
* libc call, while MSVC's implementation is horribly slow, so always fall
|
||||
* back to a normal integer conversion for them.
|
||||
*/
|
||||
#elif defined(HAVE_LRINTF) && !defined(_MSC_VER) && !defined(__clang__)
|
||||
|
||||
return lrintf(f);
|
||||
|
||||
#else
|
||||
|
||||
return (ALint)f;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Converts float-to-int using standard behavior (truncation). */
|
||||
inline int float2int(float f)
|
||||
{
|
||||
#if ((defined(__GNUC__) || defined(__clang__)) && (defined(__i386__) || defined(__x86_64__)) && \
|
||||
!defined(__SSE_MATH__)) || (defined(_MSC_VER) && defined(_M_IX86_FP) && _M_IX86_FP == 0)
|
||||
ALint sign, shift, mant;
|
||||
union {
|
||||
ALfloat f;
|
||||
ALint i;
|
||||
} conv;
|
||||
|
||||
conv.f = f;
|
||||
sign = (conv.i>>31) | 1;
|
||||
shift = ((conv.i>>23)&0xff) - (127+23);
|
||||
|
||||
/* Over/underflow */
|
||||
if(UNLIKELY(shift >= 31 || shift < -23))
|
||||
return 0;
|
||||
|
||||
mant = (conv.i&0x7fffff) | 0x800000;
|
||||
if(LIKELY(shift < 0))
|
||||
return (mant >> -shift) * sign;
|
||||
return (mant << shift) * sign;
|
||||
|
||||
#else
|
||||
|
||||
return (ALint)f;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Rounds a float to the nearest integral value, according to the current
|
||||
* rounding mode. This is essentially an inlined version of rintf, although
|
||||
* makes fewer promises (e.g. -0 or -0.25 rounded to 0 may result in +0).
|
||||
*/
|
||||
inline float fast_roundf(float f)
|
||||
{
|
||||
#if (defined(__GNUC__) || defined(__clang__)) && (defined(__i386__) || defined(__x86_64__)) && \
|
||||
!defined(__SSE_MATH__)
|
||||
|
||||
float out;
|
||||
__asm__ __volatile__("frndint" : "=t"(out) : "0"(f));
|
||||
return out;
|
||||
|
||||
#else
|
||||
|
||||
/* Integral limit, where sub-integral precision is not available for
|
||||
* floats.
|
||||
*/
|
||||
static const float ilim[2] = {
|
||||
8388608.0f /* 0x1.0p+23 */,
|
||||
-8388608.0f /* -0x1.0p+23 */
|
||||
};
|
||||
ALuint sign, expo;
|
||||
union {
|
||||
ALfloat f;
|
||||
ALuint i;
|
||||
} conv;
|
||||
|
||||
conv.f = f;
|
||||
sign = (conv.i>>31)&0x01;
|
||||
expo = (conv.i>>23)&0xff;
|
||||
|
||||
if(UNLIKELY(expo >= 150/*+23*/))
|
||||
{
|
||||
/* An exponent (base-2) of 23 or higher is incapable of sub-integral
|
||||
* precision, so it's already an integral value. We don't need to worry
|
||||
* about infinity or NaN here.
|
||||
*/
|
||||
return f;
|
||||
}
|
||||
/* Adding the integral limit to the value (with a matching sign) forces a
|
||||
* result that has no sub-integral precision, and is consequently forced to
|
||||
* round to an integral value. Removing the integral limit then restores
|
||||
* the initial value rounded to the integral. The compiler should not
|
||||
* optimize this out because of non-associative rules on floating-point
|
||||
* math (as long as you don't use -fassociative-math,
|
||||
* -funsafe-math-optimizations, -ffast-math, or -Ofast, in which case this
|
||||
* may break).
|
||||
*/
|
||||
f += ilim[sign];
|
||||
return f - ilim[sign];
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
enum DevProbe {
|
||||
ALL_DEVICE_PROBE,
|
||||
@@ -432,7 +582,7 @@ typedef struct DistanceComp {
|
||||
*/
|
||||
#define BUFFERSIZE 2048
|
||||
|
||||
typedef struct DryMixParams {
|
||||
typedef struct MixParams {
|
||||
AmbiConfig Ambi;
|
||||
/* Number of coefficients in each Ambi.Coeffs to mix together (4 for first-
|
||||
* order, 9 for second-order, etc). If the count is 0, Ambi.Map is used
|
||||
@@ -442,17 +592,7 @@ typedef struct DryMixParams {
|
||||
|
||||
ALfloat (*Buffer)[BUFFERSIZE];
|
||||
ALsizei NumChannels;
|
||||
ALsizei NumChannelsPerOrder[MAX_AMBI_ORDER+1];
|
||||
} DryMixParams;
|
||||
|
||||
typedef struct BFMixParams {
|
||||
AmbiConfig Ambi;
|
||||
/* Will only be 4 or 0. */
|
||||
ALsizei CoeffCount;
|
||||
|
||||
ALfloat (*Buffer)[BUFFERSIZE];
|
||||
ALsizei NumChannels;
|
||||
} BFMixParams;
|
||||
} MixParams;
|
||||
|
||||
typedef struct RealMixParams {
|
||||
enum Channel ChannelName[MAX_OUTPUT_CHANNELS];
|
||||
@@ -482,6 +622,8 @@ struct ALCdevice_struct {
|
||||
enum AmbiLayout AmbiLayout;
|
||||
enum AmbiNorm AmbiScale;
|
||||
|
||||
ALCenum LimiterState;
|
||||
|
||||
al_string DeviceName;
|
||||
|
||||
ATOMIC(ALCenum) LastError;
|
||||
@@ -536,15 +678,17 @@ struct ALCdevice_struct {
|
||||
|
||||
ALuint64 ClockBase;
|
||||
ALuint SamplesDone;
|
||||
ALuint FixedLatency;
|
||||
|
||||
/* Temp storage used for mixer processing. */
|
||||
alignas(16) ALfloat TempBuffer[4][BUFFERSIZE];
|
||||
|
||||
/* The "dry" path corresponds to the main output. */
|
||||
DryMixParams Dry;
|
||||
MixParams Dry;
|
||||
ALsizei NumChannelsPerOrder[MAX_AMBI_ORDER+1];
|
||||
|
||||
/* First-order ambisonics output, to be upsampled to the dry buffer if different. */
|
||||
BFMixParams FOAOut;
|
||||
MixParams FOAOut;
|
||||
|
||||
/* "Real" output, which will be written to the device buffer. May alias the
|
||||
* dry buffer.
|
||||
@@ -609,21 +753,35 @@ struct ALCdevice_struct {
|
||||
|
||||
|
||||
enum {
|
||||
/* End event thread processing. */
|
||||
EventType_KillThread = 0,
|
||||
|
||||
/* User event types. */
|
||||
EventType_SourceStateChange = 1<<0,
|
||||
EventType_BufferCompleted = 1<<1,
|
||||
EventType_Error = 1<<2,
|
||||
EventType_Performance = 1<<3,
|
||||
EventType_Deprecated = 1<<4,
|
||||
EventType_Disconnected = 1<<5,
|
||||
|
||||
/* Internal events. */
|
||||
EventType_ReleaseEffectState = 65536,
|
||||
};
|
||||
|
||||
typedef struct AsyncEvent {
|
||||
unsigned int EnumType;
|
||||
ALenum Type;
|
||||
ALuint ObjectId;
|
||||
ALuint Param;
|
||||
ALchar Message[1008];
|
||||
union {
|
||||
char dummy;
|
||||
struct {
|
||||
ALenum type;
|
||||
ALuint id;
|
||||
ALuint param;
|
||||
ALchar msg[1008];
|
||||
} user;
|
||||
struct ALeffectState *EffectState;
|
||||
} u;
|
||||
} AsyncEvent;
|
||||
#define ASYNC_EVENT(t) { t, { 0 } }
|
||||
|
||||
struct ALCcontext_struct {
|
||||
RefCount ref;
|
||||
@@ -676,7 +834,6 @@ struct ALCcontext_struct {
|
||||
|
||||
ATOMIC(struct ALeffectslotArray*) ActiveAuxSlots;
|
||||
|
||||
almtx_t EventThrdLock;
|
||||
althrd_t EventThread;
|
||||
alsem_t EventSem;
|
||||
struct ll_ringbuffer *AsyncEvents;
|
||||
@@ -706,9 +863,6 @@ void ALCcontext_ProcessUpdates(ALCcontext *context);
|
||||
|
||||
void AllocateVoices(ALCcontext *context, ALsizei num_voices, ALsizei old_sends);
|
||||
|
||||
void AppendAllDevicesList(const ALCchar *name);
|
||||
void AppendCaptureDeviceList(const ALCchar *name);
|
||||
|
||||
|
||||
extern ALint RTPrioLevel;
|
||||
void SetRTPriority(void);
|
||||
@@ -754,6 +908,9 @@ inline void UnlockEffectSlotList(ALCcontext *context)
|
||||
{ almtx_unlock(&context->EffectSlotLock); }
|
||||
|
||||
|
||||
int EventThread(void *arg);
|
||||
|
||||
|
||||
vector_al_string SearchDataFiles(const char *match, const char *subdir);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -74,7 +74,7 @@ extern enum Resampler ResamplerDefault;
|
||||
typedef struct BsincState {
|
||||
ALfloat sf; /* Scale interpolation factor. */
|
||||
ALsizei m; /* Coefficient count. */
|
||||
ALint l; /* Left coefficient offset. */
|
||||
ALsizei l; /* Left coefficient offset. */
|
||||
/* Filter coefficients, followed by the scale, phase, and scale-phase
|
||||
* delta coefficients. Starting at phase index 0, each subsequent phase
|
||||
* index follows contiguously.
|
||||
@@ -153,8 +153,8 @@ typedef struct MixHrtfParams {
|
||||
|
||||
|
||||
typedef struct DirectParams {
|
||||
BiquadState LowPass;
|
||||
BiquadState HighPass;
|
||||
BiquadFilter LowPass;
|
||||
BiquadFilter HighPass;
|
||||
|
||||
NfcFilter NFCtrlFilter;
|
||||
|
||||
@@ -171,8 +171,8 @@ typedef struct DirectParams {
|
||||
} DirectParams;
|
||||
|
||||
typedef struct SendParams {
|
||||
BiquadState LowPass;
|
||||
BiquadState HighPass;
|
||||
BiquadFilter LowPass;
|
||||
BiquadFilter HighPass;
|
||||
|
||||
struct {
|
||||
ALfloat Current[MAX_OUTPUT_CHANNELS];
|
||||
@@ -430,14 +430,35 @@ void aluInitEffectPanning(struct ALeffectslot *slot);
|
||||
|
||||
void aluSelectPostProcess(ALCdevice *device);
|
||||
|
||||
/**
|
||||
* Calculates ambisonic encoder coefficients using the X, Y, and Z direction
|
||||
* components, which must represent a normalized (unit length) vector, and the
|
||||
* spread is the angular width of the sound (0...tau).
|
||||
*
|
||||
* NOTE: The components use ambisonic coordinates. As a result:
|
||||
*
|
||||
* Ambisonic Y = OpenAL -X
|
||||
* Ambisonic Z = OpenAL Y
|
||||
* Ambisonic X = OpenAL -Z
|
||||
*
|
||||
* The components are ordered such that OpenAL's X, Y, and Z are the first,
|
||||
* second, and third parameters respectively -- simply negate X and Z.
|
||||
*/
|
||||
void CalcAmbiCoeffs(const ALfloat y, const ALfloat z, const ALfloat x, const ALfloat spread,
|
||||
ALfloat coeffs[MAX_AMBI_COEFFS]);
|
||||
|
||||
/**
|
||||
* CalcDirectionCoeffs
|
||||
*
|
||||
* Calculates ambisonic coefficients based on a direction vector. The vector
|
||||
* must be normalized (unit length), and the spread is the angular width of the
|
||||
* sound (0...tau).
|
||||
* Calculates ambisonic coefficients based on an OpenAL direction vector. The
|
||||
* vector must be normalized (unit length), and the spread is the angular width
|
||||
* of the sound (0...tau).
|
||||
*/
|
||||
void CalcDirectionCoeffs(const ALfloat dir[3], ALfloat spread, ALfloat coeffs[MAX_AMBI_COEFFS]);
|
||||
inline void CalcDirectionCoeffs(const ALfloat dir[3], ALfloat spread, ALfloat coeffs[MAX_AMBI_COEFFS])
|
||||
{
|
||||
/* Convert from OpenAL coords to Ambisonics. */
|
||||
CalcAmbiCoeffs(-dir[0], dir[1], -dir[2], spread, coeffs);
|
||||
}
|
||||
|
||||
/**
|
||||
* CalcAngleCoeffs
|
||||
@@ -448,34 +469,40 @@ void CalcDirectionCoeffs(const ALfloat dir[3], ALfloat spread, ALfloat coeffs[MA
|
||||
*/
|
||||
inline void CalcAngleCoeffs(ALfloat azimuth, ALfloat elevation, ALfloat spread, ALfloat coeffs[MAX_AMBI_COEFFS])
|
||||
{
|
||||
ALfloat dir[3] = {
|
||||
sinf(azimuth) * cosf(elevation),
|
||||
sinf(elevation),
|
||||
-cosf(azimuth) * cosf(elevation)
|
||||
};
|
||||
CalcDirectionCoeffs(dir, spread, coeffs);
|
||||
ALfloat x = -sinf(azimuth) * cosf(elevation);
|
||||
ALfloat y = sinf(elevation);
|
||||
ALfloat z = cosf(azimuth) * cosf(elevation);
|
||||
|
||||
CalcAmbiCoeffs(x, y, z, spread, coeffs);
|
||||
}
|
||||
|
||||
/**
|
||||
* CalcAnglePairwiseCoeffs
|
||||
* ScaleAzimuthFront
|
||||
*
|
||||
* Calculates ambisonic coefficients based on azimuth and elevation. The
|
||||
* azimuth and elevation parameters are in radians, going right and up
|
||||
* respectively. This pairwise variant warps the result such that +30 azimuth
|
||||
* is full right, and -30 azimuth is full left.
|
||||
* Scales the given azimuth toward the side (+/- pi/2 radians) for positions in
|
||||
* front.
|
||||
*/
|
||||
void CalcAnglePairwiseCoeffs(ALfloat azimuth, ALfloat elevation, ALfloat spread, ALfloat coeffs[MAX_AMBI_COEFFS]);
|
||||
inline float ScaleAzimuthFront(float azimuth, float scale)
|
||||
{
|
||||
ALfloat sign = copysignf(1.0f, azimuth);
|
||||
if(!(fabsf(azimuth) > F_PI_2))
|
||||
return minf(fabsf(azimuth) * scale, F_PI_2) * sign;
|
||||
return azimuth;
|
||||
}
|
||||
|
||||
|
||||
void ComputePanningGainsMC(const ChannelConfig *chancoeffs, ALsizei numchans, ALsizei numcoeffs, const ALfloat coeffs[MAX_AMBI_COEFFS], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]);
|
||||
void ComputePanningGainsBF(const BFChannelConfig *chanmap, ALsizei numchans, const ALfloat coeffs[MAX_AMBI_COEFFS], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]);
|
||||
void ComputePanningGainsMC(const ChannelConfig *chancoeffs, ALsizei numchans, ALsizei numcoeffs, const ALfloat*restrict coeffs, ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]);
|
||||
void ComputePanningGainsBF(const BFChannelConfig *chanmap, ALsizei numchans, const ALfloat*restrict coeffs, ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]);
|
||||
|
||||
/**
|
||||
* ComputeDryPanGains
|
||||
* ComputePanGains
|
||||
*
|
||||
* Computes panning gains using the given channel decoder coefficients and the
|
||||
* pre-calculated direction or angle coefficients.
|
||||
* pre-calculated direction or angle coefficients. For B-Format sources, the
|
||||
* coeffs are a 'slice' of a transform matrix for the input channel, used to
|
||||
* scale and orient the sound samples.
|
||||
*/
|
||||
inline void ComputeDryPanGains(const DryMixParams *dry, const ALfloat coeffs[MAX_AMBI_COEFFS], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS])
|
||||
inline void ComputePanGains(const MixParams *dry, const ALfloat*restrict coeffs, ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS])
|
||||
{
|
||||
if(dry->CoeffCount > 0)
|
||||
ComputePanningGainsMC(dry->Ambi.Coeffs, dry->NumChannels, dry->CoeffCount,
|
||||
@@ -484,23 +511,6 @@ inline void ComputeDryPanGains(const DryMixParams *dry, const ALfloat coeffs[MAX
|
||||
ComputePanningGainsBF(dry->Ambi.Map, dry->NumChannels, coeffs, ingain, gains);
|
||||
}
|
||||
|
||||
void ComputeFirstOrderGainsMC(const ChannelConfig *chancoeffs, ALsizei numchans, const ALfloat mtx[4], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]);
|
||||
void ComputeFirstOrderGainsBF(const BFChannelConfig *chanmap, ALsizei numchans, const ALfloat mtx[4], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]);
|
||||
/**
|
||||
* ComputeFirstOrderGains
|
||||
*
|
||||
* Sets channel gains for a first-order ambisonics input channel. The matrix is
|
||||
* a 1x4 'slice' of a transform matrix for the input channel, used to scale and
|
||||
* orient the sound samples.
|
||||
*/
|
||||
inline void ComputeFirstOrderGains(const BFMixParams *foa, const ALfloat mtx[4], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS])
|
||||
{
|
||||
if(foa->CoeffCount > 0)
|
||||
ComputeFirstOrderGainsMC(foa->Ambi.Coeffs, foa->NumChannels, mtx, ingain, gains);
|
||||
else
|
||||
ComputeFirstOrderGainsBF(foa->Ambi.Map, foa->NumChannels, mtx, ingain, gains);
|
||||
}
|
||||
|
||||
|
||||
ALboolean MixSource(struct ALvoice *voice, ALuint SourceID, ALCcontext *Context, ALsizei SamplesToDo);
|
||||
|
||||
|
||||
@@ -48,12 +48,14 @@ static const struct {
|
||||
{ AL_EFFECT_NULL, NullStateFactory_getFactory },
|
||||
{ AL_EFFECT_EAXREVERB, ReverbStateFactory_getFactory },
|
||||
{ AL_EFFECT_REVERB, ReverbStateFactory_getFactory },
|
||||
{ AL_EFFECT_AUTOWAH, AutowahStateFactory_getFactory },
|
||||
{ AL_EFFECT_CHORUS, ChorusStateFactory_getFactory },
|
||||
{ AL_EFFECT_COMPRESSOR, CompressorStateFactory_getFactory },
|
||||
{ AL_EFFECT_DISTORTION, DistortionStateFactory_getFactory },
|
||||
{ AL_EFFECT_ECHO, EchoStateFactory_getFactory },
|
||||
{ AL_EFFECT_EQUALIZER, EqualizerStateFactory_getFactory },
|
||||
{ AL_EFFECT_FLANGER, FlangerStateFactory_getFactory },
|
||||
{ AL_EFFECT_FREQUENCY_SHIFTER, FshifterStateFactory_getFactory },
|
||||
{ AL_EFFECT_RING_MODULATOR, ModulatorStateFactory_getFactory },
|
||||
{ AL_EFFECT_PITCH_SHIFTER, PshifterStateFactory_getFactory},
|
||||
{ AL_EFFECT_DEDICATED_DIALOGUE, DedicatedStateFactory_getFactory },
|
||||
@@ -120,12 +122,6 @@ AL_API ALvoid AL_APIENTRY alGenAuxiliaryEffectSlots(ALsizei n, ALuint *effectslo
|
||||
|
||||
LockEffectSlotList(context);
|
||||
device = context->Device;
|
||||
if(device->AuxiliaryEffectSlotMax - VECTOR_SIZE(context->EffectSlotList) < (ALuint)n)
|
||||
{
|
||||
UnlockEffectSlotList(context);
|
||||
SETERR_GOTO(context, AL_OUT_OF_MEMORY, done, "Exceeding %u auxiliary effect slot limit",
|
||||
device->AuxiliaryEffectSlotMax);
|
||||
}
|
||||
for(cur = 0;cur < n;cur++)
|
||||
{
|
||||
ALeffectslotPtr *iter = VECTOR_BEGIN(context->EffectSlotList);
|
||||
@@ -140,6 +136,13 @@ AL_API ALvoid AL_APIENTRY alGenAuxiliaryEffectSlots(ALsizei n, ALuint *effectslo
|
||||
}
|
||||
if(iter == end)
|
||||
{
|
||||
if(device->AuxiliaryEffectSlotMax == VECTOR_SIZE(context->EffectSlotList))
|
||||
{
|
||||
UnlockEffectSlotList(context);
|
||||
alDeleteAuxiliaryEffectSlots(cur, effectslots);
|
||||
SETERR_GOTO(context, AL_OUT_OF_MEMORY, done,
|
||||
"Exceeding %u auxiliary effect slot limit", device->AuxiliaryEffectSlotMax);
|
||||
}
|
||||
VECTOR_PUSH_BACK(context->EffectSlotList, NULL);
|
||||
iter = &VECTOR_BACK(context->EffectSlotList);
|
||||
}
|
||||
@@ -750,6 +753,9 @@ void UpdateEffectSlotProps(ALeffectslot *slot, ALCcontext *context)
|
||||
/* If there was an unused update container, put it back in the
|
||||
* freelist.
|
||||
*/
|
||||
if(props->State)
|
||||
ALeffectState_DecRef(props->State);
|
||||
props->State = NULL;
|
||||
ATOMIC_REPLACE_HEAD(struct ALeffectslotProps*, &context->FreeEffectslotProps, props);
|
||||
}
|
||||
|
||||
|
||||
@@ -38,12 +38,14 @@ extern inline ALboolean IsReverbEffect(ALenum type);
|
||||
const struct EffectList EffectList[EFFECTLIST_SIZE] = {
|
||||
{ "eaxreverb", EAXREVERB_EFFECT, AL_EFFECT_EAXREVERB },
|
||||
{ "reverb", REVERB_EFFECT, AL_EFFECT_REVERB },
|
||||
{ "autowah", AUTOWAH_EFFECT, AL_EFFECT_AUTOWAH },
|
||||
{ "chorus", CHORUS_EFFECT, AL_EFFECT_CHORUS },
|
||||
{ "compressor", COMPRESSOR_EFFECT, AL_EFFECT_COMPRESSOR },
|
||||
{ "distortion", DISTORTION_EFFECT, AL_EFFECT_DISTORTION },
|
||||
{ "echo", ECHO_EFFECT, AL_EFFECT_ECHO },
|
||||
{ "equalizer", EQUALIZER_EFFECT, AL_EFFECT_EQUALIZER },
|
||||
{ "flanger", FLANGER_EFFECT, AL_EFFECT_FLANGER },
|
||||
{ "fshifter", FSHIFTER_EFFECT, AL_EFFECT_FREQUENCY_SHIFTER },
|
||||
{ "modulator", MODULATOR_EFFECT, AL_EFFECT_RING_MODULATOR },
|
||||
{ "pshifter", PSHIFTER_EFFECT, AL_EFFECT_PITCH_SHIFTER },
|
||||
{ "dedicated", DEDICATED_EFFECT, AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT },
|
||||
@@ -533,6 +535,13 @@ static void InitEffectParams(ALeffect *effect, ALenum type)
|
||||
effect->Props.Reverb.DecayHFLimit = AL_REVERB_DEFAULT_DECAY_HFLIMIT;
|
||||
effect->vtab = &ALreverb_vtable;
|
||||
break;
|
||||
case AL_EFFECT_AUTOWAH:
|
||||
effect->Props.Autowah.AttackTime = AL_AUTOWAH_DEFAULT_ATTACK_TIME;
|
||||
effect->Props.Autowah.ReleaseTime = AL_AUTOWAH_DEFAULT_RELEASE_TIME;
|
||||
effect->Props.Autowah.Resonance = AL_AUTOWAH_DEFAULT_RESONANCE;
|
||||
effect->Props.Autowah.PeakGain = AL_AUTOWAH_DEFAULT_PEAK_GAIN;
|
||||
effect->vtab = &ALautowah_vtable;
|
||||
break;
|
||||
case AL_EFFECT_CHORUS:
|
||||
effect->Props.Chorus.Waveform = AL_CHORUS_DEFAULT_WAVEFORM;
|
||||
effect->Props.Chorus.Phase = AL_CHORUS_DEFAULT_PHASE;
|
||||
@@ -584,6 +593,12 @@ static void InitEffectParams(ALeffect *effect, ALenum type)
|
||||
effect->Props.Chorus.Delay = AL_FLANGER_DEFAULT_DELAY;
|
||||
effect->vtab = &ALflanger_vtable;
|
||||
break;
|
||||
case AL_EFFECT_FREQUENCY_SHIFTER:
|
||||
effect->Props.Fshifter.Frequency = AL_FREQUENCY_SHIFTER_DEFAULT_FREQUENCY;
|
||||
effect->Props.Fshifter.LeftDirection = AL_FREQUENCY_SHIFTER_DEFAULT_LEFT_DIRECTION;
|
||||
effect->Props.Fshifter.RightDirection = AL_FREQUENCY_SHIFTER_DEFAULT_RIGHT_DIRECTION;
|
||||
effect->vtab = &ALfshifter_vtable;
|
||||
break;
|
||||
case AL_EFFECT_RING_MODULATOR:
|
||||
effect->Props.Modulator.Frequency = AL_RING_MODULATOR_DEFAULT_FREQUENCY;
|
||||
effect->Props.Modulator.HighPassCutoff = AL_RING_MODULATOR_DEFAULT_HIGHPASS_CUTOFF;
|
||||
|
||||
@@ -28,6 +28,9 @@
|
||||
#include "alError.h"
|
||||
|
||||
|
||||
#define FILTER_MIN_GAIN 0.0f
|
||||
#define FILTER_MAX_GAIN 4.0f /* +12dB */
|
||||
|
||||
extern inline void LockFilterList(ALCdevice *device);
|
||||
extern inline void UnlockFilterList(ALCdevice *device);
|
||||
|
||||
@@ -347,7 +350,7 @@ static void ALlowpass_setParamf(ALfilter *filter, ALCcontext *context, ALenum pa
|
||||
switch(param)
|
||||
{
|
||||
case AL_LOWPASS_GAIN:
|
||||
if(!(val >= AL_LOWPASS_MIN_GAIN && val <= AL_LOWPASS_MAX_GAIN))
|
||||
if(!(val >= FILTER_MIN_GAIN && val <= FILTER_MAX_GAIN))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Low-pass gain %f out of range", val);
|
||||
filter->Gain = val;
|
||||
break;
|
||||
@@ -400,7 +403,7 @@ static void ALhighpass_setParamf(ALfilter *filter, ALCcontext *context, ALenum p
|
||||
switch(param)
|
||||
{
|
||||
case AL_HIGHPASS_GAIN:
|
||||
if(!(val >= AL_HIGHPASS_MIN_GAIN && val <= AL_HIGHPASS_MAX_GAIN))
|
||||
if(!(val >= FILTER_MIN_GAIN && val <= FILTER_MAX_GAIN))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "High-pass gain out of range");
|
||||
filter->Gain = val;
|
||||
break;
|
||||
@@ -453,7 +456,7 @@ static void ALbandpass_setParamf(ALfilter *filter, ALCcontext *context, ALenum p
|
||||
switch(param)
|
||||
{
|
||||
case AL_BANDPASS_GAIN:
|
||||
if(!(val >= AL_BANDPASS_MIN_GAIN && val <= AL_BANDPASS_MAX_GAIN))
|
||||
if(!(val >= FILTER_MIN_GAIN && val <= FILTER_MAX_GAIN))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Band-pass gain out of range");
|
||||
filter->Gain = val;
|
||||
break;
|
||||
|
||||
@@ -229,17 +229,16 @@ static inline bool SourceShouldUpdate(ALsource *source, ALCcontext *context)
|
||||
/** Can only be called while the mixer is locked! */
|
||||
static void SendStateChangeEvent(ALCcontext *context, ALuint id, ALenum state)
|
||||
{
|
||||
AsyncEvent evt = ASYNC_EVENT(EventType_SourceStateChange);
|
||||
ALbitfieldSOFT enabledevt;
|
||||
AsyncEvent evt;
|
||||
|
||||
enabledevt = ATOMIC_LOAD(&context->EnabledEvts, almemory_order_acquire);
|
||||
if(!(enabledevt&EventType_SourceStateChange)) return;
|
||||
|
||||
evt.EnumType = EventType_SourceStateChange;
|
||||
evt.Type = AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT;
|
||||
evt.ObjectId = id;
|
||||
evt.Param = state;
|
||||
snprintf(evt.Message, sizeof(evt.Message), "Source ID %u state changed to %s", id,
|
||||
evt.u.user.type = AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT;
|
||||
evt.u.user.id = id;
|
||||
evt.u.user.param = state;
|
||||
snprintf(evt.u.user.msg, sizeof(evt.u.user.msg), "Source ID %u state changed to %s", id,
|
||||
(state==AL_INITIAL) ? "AL_INITIAL" :
|
||||
(state==AL_PLAYING) ? "AL_PLAYING" :
|
||||
(state==AL_PAUSED) ? "AL_PAUSED" :
|
||||
@@ -1296,7 +1295,7 @@ static ALboolean GetSourcedv(ALsource *Source, ALCcontext *Context, SourceProp p
|
||||
*/
|
||||
values[0] = GetSourceSecOffset(Source, Context, &srcclock);
|
||||
almtx_lock(&device->BackendLock);
|
||||
clocktime = V0(device->Backend,getClockLatency)();
|
||||
clocktime = GetClockLatency(device);
|
||||
almtx_unlock(&device->BackendLock);
|
||||
if(srcclock == (ALuint64)clocktime.ClockTime)
|
||||
values[1] = (ALdouble)clocktime.Latency / 1000000000.0;
|
||||
@@ -1560,7 +1559,7 @@ static ALboolean GetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp
|
||||
*/
|
||||
values[0] = GetSourceSampleOffset(Source, Context, &srcclock);
|
||||
almtx_lock(&device->BackendLock);
|
||||
clocktime = V0(device->Backend,getClockLatency)();
|
||||
clocktime = GetClockLatency(device);
|
||||
almtx_unlock(&device->BackendLock);
|
||||
if(srcclock == (ALuint64)clocktime.ClockTime)
|
||||
values[1] = clocktime.Latency;
|
||||
@@ -2826,6 +2825,121 @@ done:
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
AL_API void AL_APIENTRY alSourceQueueBufferLayersSOFT(ALuint src, ALsizei nb, const ALuint *buffers)
|
||||
{
|
||||
ALCdevice *device;
|
||||
ALCcontext *context;
|
||||
ALbufferlistitem *BufferListStart;
|
||||
ALbufferlistitem *BufferList;
|
||||
ALbuffer *BufferFmt = NULL;
|
||||
ALsource *source;
|
||||
ALsizei i;
|
||||
|
||||
if(nb == 0)
|
||||
return;
|
||||
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
device = context->Device;
|
||||
|
||||
LockSourceList(context);
|
||||
if(!(nb >= 0 && nb < 16))
|
||||
SETERR_GOTO(context, AL_INVALID_VALUE, done, "Queueing %d buffer layers", nb);
|
||||
if((source=LookupSource(context, src)) == NULL)
|
||||
SETERR_GOTO(context, AL_INVALID_NAME, done, "Invalid source ID %u", src);
|
||||
|
||||
if(source->SourceType == AL_STATIC)
|
||||
{
|
||||
/* Can't queue on a Static Source */
|
||||
SETERR_GOTO(context, AL_INVALID_OPERATION, done, "Queueing onto static source %u", src);
|
||||
}
|
||||
|
||||
/* Check for a valid Buffer, for its frequency and format */
|
||||
BufferList = source->queue;
|
||||
while(BufferList)
|
||||
{
|
||||
for(i = 0;i < BufferList->num_buffers;i++)
|
||||
{
|
||||
if((BufferFmt=BufferList->buffers[i]) != NULL)
|
||||
break;
|
||||
}
|
||||
if(BufferFmt) break;
|
||||
BufferList = ATOMIC_LOAD(&BufferList->next, almemory_order_relaxed);
|
||||
}
|
||||
|
||||
LockBufferList(device);
|
||||
BufferListStart = al_calloc(DEF_ALIGN, FAM_SIZE(ALbufferlistitem, buffers, nb));
|
||||
BufferList = BufferListStart;
|
||||
ATOMIC_INIT(&BufferList->next, NULL);
|
||||
BufferList->max_samples = 0;
|
||||
BufferList->num_buffers = 0;
|
||||
for(i = 0;i < nb;i++)
|
||||
{
|
||||
ALbuffer *buffer = NULL;
|
||||
if(buffers[i] && (buffer=LookupBuffer(device, buffers[i])) == NULL)
|
||||
SETERR_GOTO(context, AL_INVALID_NAME, buffer_error, "Queueing invalid buffer ID %u",
|
||||
buffers[i]);
|
||||
|
||||
BufferList->buffers[BufferList->num_buffers++] = buffer;
|
||||
if(!buffer) continue;
|
||||
|
||||
IncrementRef(&buffer->ref);
|
||||
|
||||
BufferList->max_samples = maxi(BufferList->max_samples, buffer->SampleLen);
|
||||
|
||||
if(buffer->MappedAccess != 0 && !(buffer->MappedAccess&AL_MAP_PERSISTENT_BIT_SOFT))
|
||||
SETERR_GOTO(context, AL_INVALID_OPERATION, buffer_error,
|
||||
"Queueing non-persistently mapped buffer %u", buffer->id);
|
||||
|
||||
if(BufferFmt == NULL)
|
||||
BufferFmt = buffer;
|
||||
else if(BufferFmt->Frequency != buffer->Frequency ||
|
||||
BufferFmt->FmtChannels != buffer->FmtChannels ||
|
||||
BufferFmt->OriginalType != buffer->OriginalType)
|
||||
{
|
||||
alSetError(context, AL_INVALID_OPERATION, "Queueing buffer with mismatched format");
|
||||
|
||||
buffer_error:
|
||||
/* A buffer failed (invalid ID or format), so unlock and release
|
||||
* each buffer we had. */
|
||||
while(BufferListStart)
|
||||
{
|
||||
ALbufferlistitem *next = ATOMIC_LOAD(&BufferListStart->next,
|
||||
almemory_order_relaxed);
|
||||
for(i = 0;i < BufferListStart->num_buffers;i++)
|
||||
{
|
||||
if((buffer=BufferListStart->buffers[i]) != NULL)
|
||||
DecrementRef(&buffer->ref);
|
||||
}
|
||||
al_free(BufferListStart);
|
||||
BufferListStart = next;
|
||||
}
|
||||
UnlockBufferList(device);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
/* All buffers good. */
|
||||
UnlockBufferList(device);
|
||||
|
||||
/* Source is now streaming */
|
||||
source->SourceType = AL_STREAMING;
|
||||
|
||||
if(!(BufferList=source->queue))
|
||||
source->queue = BufferListStart;
|
||||
else
|
||||
{
|
||||
ALbufferlistitem *next;
|
||||
while((next=ATOMIC_LOAD(&BufferList->next, almemory_order_relaxed)) != NULL)
|
||||
BufferList = next;
|
||||
ATOMIC_STORE(&BufferList->next, BufferListStart, almemory_order_release);
|
||||
}
|
||||
|
||||
done:
|
||||
UnlockSourceList(context);
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
AL_API ALvoid AL_APIENTRY alSourceUnqueueBuffers(ALuint src, ALsizei nb, ALuint *buffers)
|
||||
{
|
||||
ALCcontext *context;
|
||||
|
||||
@@ -6,19 +6,16 @@
|
||||
#include "AL/alext.h"
|
||||
#include "alMain.h"
|
||||
#include "alError.h"
|
||||
#include "alAuxEffectSlot.h"
|
||||
#include "ringbuffer.h"
|
||||
|
||||
|
||||
static int EventThread(void *arg)
|
||||
int EventThread(void *arg)
|
||||
{
|
||||
ALCcontext *context = arg;
|
||||
bool quitnow = false;
|
||||
|
||||
/* Clear all pending posts on the semaphore. */
|
||||
while(alsem_trywait(&context->EventSem) == althrd_success)
|
||||
{
|
||||
}
|
||||
|
||||
while(1)
|
||||
while(!quitnow)
|
||||
{
|
||||
ALbitfieldSOFT enabledevts;
|
||||
AsyncEvent evt;
|
||||
@@ -28,14 +25,24 @@ static int EventThread(void *arg)
|
||||
alsem_wait(&context->EventSem);
|
||||
continue;
|
||||
}
|
||||
if(!evt.EnumType)
|
||||
break;
|
||||
|
||||
almtx_lock(&context->EventCbLock);
|
||||
enabledevts = ATOMIC_LOAD(&context->EnabledEvts, almemory_order_acquire);
|
||||
if(context->EventCb && (enabledevts&evt.EnumType) == evt.EnumType)
|
||||
context->EventCb(evt.Type, evt.ObjectId, evt.Param, (ALsizei)strlen(evt.Message),
|
||||
evt.Message, context->EventParam);
|
||||
do {
|
||||
quitnow = evt.EnumType == EventType_KillThread;
|
||||
if(quitnow) break;
|
||||
|
||||
if(evt.EnumType == EventType_ReleaseEffectState)
|
||||
{
|
||||
ALeffectState_DecRef(evt.u.EffectState);
|
||||
continue;
|
||||
}
|
||||
|
||||
enabledevts = ATOMIC_LOAD(&context->EnabledEvts, almemory_order_acquire);
|
||||
if(context->EventCb && (enabledevts&evt.EnumType) == evt.EnumType)
|
||||
context->EventCb(evt.u.user.type, evt.u.user.id, evt.u.user.param,
|
||||
(ALsizei)strlen(evt.u.user.msg), evt.u.user.msg, context->EventParam
|
||||
);
|
||||
} while(ll_ringbuffer_read(context->AsyncEvents, (char*)&evt, 1) != 0);
|
||||
almtx_unlock(&context->EventCbLock);
|
||||
}
|
||||
return 0;
|
||||
@@ -46,7 +53,6 @@ AL_API void AL_APIENTRY alEventControlSOFT(ALsizei count, const ALenum *types, A
|
||||
ALCcontext *context;
|
||||
ALbitfieldSOFT enabledevts;
|
||||
ALbitfieldSOFT flags = 0;
|
||||
bool isrunning;
|
||||
ALsizei i;
|
||||
|
||||
context = GetContextRef();
|
||||
@@ -74,13 +80,9 @@ AL_API void AL_APIENTRY alEventControlSOFT(ALsizei count, const ALenum *types, A
|
||||
SETERR_GOTO(context, AL_INVALID_ENUM, done, "Invalid event type 0x%04x", types[i]);
|
||||
}
|
||||
|
||||
almtx_lock(&context->EventThrdLock);
|
||||
if(enable)
|
||||
{
|
||||
if(!context->AsyncEvents)
|
||||
context->AsyncEvents = ll_ringbuffer_create(63, sizeof(AsyncEvent), false);
|
||||
enabledevts = ATOMIC_LOAD(&context->EnabledEvts, almemory_order_relaxed);
|
||||
isrunning = !!enabledevts;
|
||||
while(ATOMIC_COMPARE_EXCHANGE_WEAK(&context->EnabledEvts, &enabledevts, enabledevts|flags,
|
||||
almemory_order_acq_rel, almemory_order_acquire) == 0)
|
||||
{
|
||||
@@ -88,35 +90,20 @@ AL_API void AL_APIENTRY alEventControlSOFT(ALsizei count, const ALenum *types, A
|
||||
* just try again.
|
||||
*/
|
||||
}
|
||||
if(!isrunning && flags)
|
||||
althrd_create(&context->EventThread, EventThread, context);
|
||||
}
|
||||
else
|
||||
{
|
||||
enabledevts = ATOMIC_LOAD(&context->EnabledEvts, almemory_order_relaxed);
|
||||
isrunning = !!enabledevts;
|
||||
while(ATOMIC_COMPARE_EXCHANGE_WEAK(&context->EnabledEvts, &enabledevts, enabledevts&~flags,
|
||||
almemory_order_acq_rel, almemory_order_acquire) == 0)
|
||||
{
|
||||
}
|
||||
if(isrunning && !(enabledevts&~flags))
|
||||
{
|
||||
static const AsyncEvent kill_evt = { 0 };
|
||||
while(ll_ringbuffer_write(context->AsyncEvents, (const char*)&kill_evt, 1) == 0)
|
||||
althrd_yield();
|
||||
alsem_post(&context->EventSem);
|
||||
althrd_join(context->EventThread, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Wait to ensure the event handler sees the changed flags before
|
||||
* returning.
|
||||
*/
|
||||
almtx_lock(&context->EventCbLock);
|
||||
almtx_unlock(&context->EventCbLock);
|
||||
}
|
||||
/* Wait to ensure the event handler sees the changed flags before
|
||||
* returning.
|
||||
*/
|
||||
almtx_lock(&context->EventCbLock);
|
||||
almtx_unlock(&context->EventCbLock);
|
||||
}
|
||||
almtx_unlock(&context->EventThrdLock);
|
||||
|
||||
done:
|
||||
ALCcontext_DecRef(context);
|
||||
|
||||
@@ -219,8 +219,9 @@
|
||||
## excludefx: (global)
|
||||
# Sets which effects to exclude, preventing apps from using them. This can
|
||||
# help for apps that try to use effects which are too CPU intensive for the
|
||||
# system to handle. Available effects are: eaxreverb,reverb,chorus,compressor,
|
||||
# distortion,echo,equalizer,flanger,modulator,dedicated,pshifter
|
||||
# system to handle. Available effects are: eaxreverb,reverb,autowah,chorus,
|
||||
# compressor,distortion,echo,equalizer,flanger,modulator,dedicated,pshifter,
|
||||
# fshifter
|
||||
#excludefx =
|
||||
|
||||
## default-reverb: (global)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
version: 1.18.2.{build}
|
||||
version: 1.19.0.{build}
|
||||
|
||||
environment:
|
||||
matrix:
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "alcomplex.h"
|
||||
#include "math_defs.h"
|
||||
|
||||
|
||||
extern inline ALcomplex complex_add(ALcomplex a, ALcomplex b);
|
||||
extern inline ALcomplex complex_sub(ALcomplex a, ALcomplex b);
|
||||
extern inline ALcomplex complex_mult(ALcomplex a, ALcomplex b);
|
||||
|
||||
void complex_fft(ALcomplex *FFTBuffer, ALsizei FFTSize, ALdouble Sign)
|
||||
{
|
||||
ALsizei i, j, k, mask, step, step2;
|
||||
ALcomplex temp, u, w;
|
||||
ALdouble arg;
|
||||
|
||||
/* Bit-reversal permutation applied to a sequence of FFTSize items */
|
||||
for(i = 1;i < FFTSize-1;i++)
|
||||
{
|
||||
for(mask = 0x1, j = 0;mask < FFTSize;mask <<= 1)
|
||||
{
|
||||
if((i&mask) != 0)
|
||||
j++;
|
||||
j <<= 1;
|
||||
}
|
||||
j >>= 1;
|
||||
|
||||
if(i < j)
|
||||
{
|
||||
temp = FFTBuffer[i];
|
||||
FFTBuffer[i] = FFTBuffer[j];
|
||||
FFTBuffer[j] = temp;
|
||||
}
|
||||
}
|
||||
|
||||
/* Iterative form of DanielsonLanczos lemma */
|
||||
for(i = 1, step = 2;i < FFTSize;i<<=1, step<<=1)
|
||||
{
|
||||
step2 = step >> 1;
|
||||
arg = M_PI / step2;
|
||||
|
||||
w.Real = cos(arg);
|
||||
w.Imag = sin(arg) * Sign;
|
||||
|
||||
u.Real = 1.0;
|
||||
u.Imag = 0.0;
|
||||
|
||||
for(j = 0;j < step2;j++)
|
||||
{
|
||||
for(k = j;k < FFTSize;k+=step)
|
||||
{
|
||||
temp = complex_mult(FFTBuffer[k+step2], u);
|
||||
FFTBuffer[k+step2] = complex_sub(FFTBuffer[k], temp);
|
||||
FFTBuffer[k] = complex_add(FFTBuffer[k], temp);
|
||||
}
|
||||
|
||||
u = complex_mult(u, w);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void complex_hilbert(ALcomplex *Buffer, ALsizei size)
|
||||
{
|
||||
const ALdouble inverse_size = 1.0/(ALdouble)size;
|
||||
ALsizei todo, i;
|
||||
|
||||
for(i = 0;i < size;i++)
|
||||
Buffer[i].Imag = 0.0;
|
||||
|
||||
complex_fft(Buffer, size, 1.0);
|
||||
|
||||
todo = size >> 1;
|
||||
Buffer[0].Real *= inverse_size;
|
||||
Buffer[0].Imag *= inverse_size;
|
||||
for(i = 1;i < todo;i++)
|
||||
{
|
||||
Buffer[i].Real *= 2.0*inverse_size;
|
||||
Buffer[i].Imag *= 2.0*inverse_size;
|
||||
}
|
||||
Buffer[i].Real *= inverse_size;
|
||||
Buffer[i].Imag *= inverse_size;
|
||||
i++;
|
||||
|
||||
for(;i < size;i++)
|
||||
{
|
||||
Buffer[i].Real = 0.0;
|
||||
Buffer[i].Imag = 0.0;
|
||||
}
|
||||
|
||||
complex_fft(Buffer, size, -1.0);
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
#ifndef ALCOMPLEX_H
|
||||
#define ALCOMPLEX_H
|
||||
|
||||
#include "AL/al.h"
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct ALcomplex {
|
||||
ALdouble Real;
|
||||
ALdouble Imag;
|
||||
} ALcomplex;
|
||||
|
||||
/** Addition of two complex numbers. */
|
||||
inline ALcomplex complex_add(ALcomplex a, ALcomplex b)
|
||||
{
|
||||
ALcomplex result;
|
||||
|
||||
result.Real = a.Real + b.Real;
|
||||
result.Imag = a.Imag + b.Imag;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Subtraction of two complex numbers. */
|
||||
inline ALcomplex complex_sub(ALcomplex a, ALcomplex b)
|
||||
{
|
||||
ALcomplex result;
|
||||
|
||||
result.Real = a.Real - b.Real;
|
||||
result.Imag = a.Imag - b.Imag;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Multiplication of two complex numbers. */
|
||||
inline ALcomplex complex_mult(ALcomplex a, ALcomplex b)
|
||||
{
|
||||
ALcomplex result;
|
||||
|
||||
result.Real = a.Real*b.Real - a.Imag*b.Imag;
|
||||
result.Imag = a.Imag*b.Real + a.Real*b.Imag;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterative implementation of 2-radix FFT (In-place algorithm). Sign = -1 is
|
||||
* FFT and 1 is iFFT (inverse). Fills FFTBuffer[0...FFTSize-1] with the
|
||||
* Discrete Fourier Transform (DFT) of the time domain data stored in
|
||||
* FFTBuffer[0...FFTSize-1]. FFTBuffer is an array of complex numbers, FFTSize
|
||||
* MUST BE power of two.
|
||||
*/
|
||||
void complex_fft(ALcomplex *FFTBuffer, ALsizei FFTSize, ALdouble Sign);
|
||||
|
||||
/**
|
||||
* Calculate the complex helical sequence (discrete-time analytical signal) of
|
||||
* the given input using the discrete Hilbert transform (In-place algorithm).
|
||||
* Fills Buffer[0...size-1] with the discrete-time analytical signal stored in
|
||||
* Buffer[0...size-1]. Buffer is an array of complex numbers, size MUST BE
|
||||
* power of two.
|
||||
*/
|
||||
void complex_hilbert(ALcomplex *Buffer, ALsizei size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif /* ALCOMPLEX_H */
|
||||
@@ -18,6 +18,12 @@
|
||||
#define FLT_EPSILON (1.19209290e-07f)
|
||||
#endif
|
||||
|
||||
#define SQRT_2 1.41421356237309504880
|
||||
#define SQRT_3 1.73205080756887719318
|
||||
|
||||
#define SQRTF_2 1.41421356237309504880f
|
||||
#define SQRTF_3 1.73205080756887719318f
|
||||
|
||||
#ifndef HUGE_VALF
|
||||
static const union msvc_inf_hack {
|
||||
unsigned char b[4];
|
||||
@@ -40,7 +46,20 @@ static inline float cbrtf(float f)
|
||||
}
|
||||
#endif
|
||||
|
||||
#define DEG2RAD(x) ((float)(x) * (F_PI/180.0f))
|
||||
#define RAD2DEG(x) ((float)(x) * (180.0f/F_PI))
|
||||
#ifndef HAVE_COPYSIGNF
|
||||
static inline float copysignf(float x, float y)
|
||||
{
|
||||
union {
|
||||
float f;
|
||||
unsigned int u;
|
||||
} ux = { x }, uy = { y };
|
||||
ux.u &= 0x7fffffffu;
|
||||
ux.u |= (uy.u&0x80000000u);
|
||||
return ux.f;
|
||||
}
|
||||
#endif
|
||||
|
||||
#define DEG2RAD(x) ((float)(x) * (float)(M_PI/180.0))
|
||||
#define RAD2DEG(x) ((float)(x) * (float)(180.0/M_PI))
|
||||
|
||||
#endif /* AL_MATH_DEFS_H */
|
||||
|
||||
@@ -428,7 +428,11 @@ void althrd_thread_detach(void)
|
||||
{
|
||||
void *ptr = altss_get(TlsDestructors.keys[i]);
|
||||
altss_dtor_t callback = (altss_dtor_t)TlsDestructors.values[i];
|
||||
if(ptr && callback) callback(ptr);
|
||||
if(ptr)
|
||||
{
|
||||
if(callback) callback(ptr);
|
||||
altss_set(TlsDestructors.keys[i], NULL);
|
||||
}
|
||||
}
|
||||
UnlockUIntMapRead(&TlsDestructors);
|
||||
}
|
||||
|
||||
@@ -98,6 +98,9 @@
|
||||
/* Define if we have the cbrtf function */
|
||||
#cmakedefine HAVE_CBRTF
|
||||
|
||||
/* Define if we have the copysignf function */
|
||||
#cmakedefine HAVE_COPYSIGNF
|
||||
|
||||
/* Define if we have the strtof function */
|
||||
#cmakedefine HAVE_STRTOF
|
||||
|
||||
|
||||
@@ -39,7 +39,16 @@ extern "C" {
|
||||
#include "AL/al.h"
|
||||
#include "AL/alext.h"
|
||||
|
||||
#include "common/alhelpers.h"
|
||||
|
||||
extern "C" {
|
||||
/* Undefine this to disable use of experimental extensions. Don't use for
|
||||
* production code! Interfaces and behavior may change prior to being
|
||||
* finalized.
|
||||
*/
|
||||
#define ALLOW_EXPERIMENTAL_EXTS
|
||||
|
||||
#ifdef ALLOW_EXPERIMENTAL_EXTS
|
||||
#ifndef AL_SOFT_map_buffer
|
||||
#define AL_SOFT_map_buffer 1
|
||||
typedef unsigned int ALbitfieldSOFT;
|
||||
@@ -71,10 +80,15 @@ typedef void (AL_APIENTRY*LPALEVENTCALLBACKSOFT)(ALEVENTPROCSOFT callback, void
|
||||
typedef void* (AL_APIENTRY*LPALGETPOINTERSOFT)(ALenum pname);
|
||||
typedef void (AL_APIENTRY*LPALGETPOINTERVSOFT)(ALenum pname, void **values);
|
||||
#endif
|
||||
#endif /* ALLOW_EXPERIMENTAL_EXTS */
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI (3.14159265358979323846)
|
||||
#endif
|
||||
|
||||
using nanoseconds = std::chrono::nanoseconds;
|
||||
using microseconds = std::chrono::microseconds;
|
||||
using milliseconds = std::chrono::milliseconds;
|
||||
@@ -84,15 +98,20 @@ using seconds_d64 = std::chrono::duration<double>;
|
||||
const std::string AppName("alffplay");
|
||||
|
||||
bool EnableDirectOut = false;
|
||||
bool EnableWideStereo = false;
|
||||
LPALGETSOURCEI64VSOFT alGetSourcei64vSOFT;
|
||||
LPALCGETINTEGER64VSOFT alcGetInteger64vSOFT;
|
||||
|
||||
#ifdef AL_SOFT_map_buffer
|
||||
LPALBUFFERSTORAGESOFT alBufferStorageSOFT;
|
||||
LPALMAPBUFFERSOFT alMapBufferSOFT;
|
||||
LPALUNMAPBUFFERSOFT alUnmapBufferSOFT;
|
||||
#endif
|
||||
|
||||
#ifdef AL_SOFT_events
|
||||
LPALEVENTCONTROLSOFT alEventControlSOFT;
|
||||
LPALEVENTCALLBACKSOFT alEventCallbackSOFT;
|
||||
#endif
|
||||
|
||||
const seconds AVNoSyncThreshold(10);
|
||||
|
||||
@@ -258,9 +277,11 @@ struct AudioState {
|
||||
av_freep(&mSamples);
|
||||
}
|
||||
|
||||
#ifdef AL_SOFT_events
|
||||
static void AL_APIENTRY EventCallback(ALenum eventType, ALuint object, ALuint param,
|
||||
ALsizei length, const ALchar *message,
|
||||
void *userParam);
|
||||
#endif
|
||||
|
||||
nanoseconds getClockNoLock();
|
||||
nanoseconds getClock()
|
||||
@@ -683,6 +704,7 @@ bool AudioState::readAudio(uint8_t *samples, int length)
|
||||
}
|
||||
|
||||
|
||||
#ifdef AL_SOFT_events
|
||||
void AL_APIENTRY AudioState::EventCallback(ALenum eventType, ALuint object, ALuint param,
|
||||
ALsizei length, const ALchar *message,
|
||||
void *userParam)
|
||||
@@ -699,7 +721,7 @@ void AL_APIENTRY AudioState::EventCallback(ALenum eventType, ALuint object, ALui
|
||||
return;
|
||||
}
|
||||
|
||||
std::cout<< "---- AL Event on AudioState "<<self<<" ----\nEvent: ";
|
||||
std::cout<< "\n---- AL Event on AudioState "<<self<<" ----\nEvent: ";
|
||||
switch(eventType)
|
||||
{
|
||||
case AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT: std::cout<< "Buffer completed"; break;
|
||||
@@ -712,8 +734,8 @@ void AL_APIENTRY AudioState::EventCallback(ALenum eventType, ALuint object, ALui
|
||||
std::dec<<std::setw(0)<<std::setfill(' '); break;
|
||||
}
|
||||
std::cout<< "\n"
|
||||
"Object ID: "<<object<<'\n'<<
|
||||
"Parameter: "<<param<<'\n'<<
|
||||
"Object ID: "<<object<<"\n"
|
||||
"Parameter: "<<param<<"\n"
|
||||
"Message: "<<std::string(message, length)<<"\n----"<<
|
||||
std::endl;
|
||||
|
||||
@@ -726,24 +748,27 @@ void AL_APIENTRY AudioState::EventCallback(ALenum eventType, ALuint object, ALui
|
||||
self->mSrcCond.notify_one();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
int AudioState::handler()
|
||||
{
|
||||
const std::array<ALenum,6> types{{
|
||||
AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT, AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT,
|
||||
AL_EVENT_TYPE_ERROR_SOFT, AL_EVENT_TYPE_PERFORMANCE_SOFT, AL_EVENT_TYPE_DEPRECATED_SOFT,
|
||||
AL_EVENT_TYPE_DISCONNECTED_SOFT
|
||||
}};
|
||||
std::unique_lock<std::mutex> lock(mSrcMutex);
|
||||
milliseconds sleep_time = AudioBufferTime / 3;
|
||||
ALenum fmt;
|
||||
|
||||
#ifdef AL_SOFT_events
|
||||
const std::array<ALenum,6> evt_types{{
|
||||
AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT, AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT,
|
||||
AL_EVENT_TYPE_ERROR_SOFT, AL_EVENT_TYPE_PERFORMANCE_SOFT, AL_EVENT_TYPE_DEPRECATED_SOFT,
|
||||
AL_EVENT_TYPE_DISCONNECTED_SOFT
|
||||
}};
|
||||
if(alEventControlSOFT)
|
||||
{
|
||||
alEventControlSOFT(types.size(), types.data(), AL_TRUE);
|
||||
alEventControlSOFT(evt_types.size(), evt_types.data(), AL_TRUE);
|
||||
alEventCallbackSOFT(EventCallback, this);
|
||||
sleep_time = AudioBufferTotalTime;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Find a suitable format for OpenAL. */
|
||||
mDstChanLayout = 0;
|
||||
@@ -885,13 +910,17 @@ int AudioState::handler()
|
||||
|
||||
if(EnableDirectOut)
|
||||
alSourcei(mSource, AL_DIRECT_CHANNELS_SOFT, AL_TRUE);
|
||||
if(EnableWideStereo)
|
||||
{
|
||||
ALfloat angles[2] = { (ALfloat)(M_PI/3.0), (ALfloat)(-M_PI/3.0) };
|
||||
alSourcefv(mSource, AL_STEREO_ANGLES, angles);
|
||||
}
|
||||
|
||||
if(alGetError() != AL_NO_ERROR)
|
||||
goto finish;
|
||||
|
||||
if(!alBufferStorageSOFT)
|
||||
samples = av_malloc(buffer_len);
|
||||
else
|
||||
#ifdef AL_SOFT_map_buffer
|
||||
if(alBufferStorageSOFT)
|
||||
{
|
||||
for(ALuint bufid : mBuffers)
|
||||
alBufferStorageSOFT(bufid, mFormat, nullptr, buffer_len, mCodecCtx->sample_rate,
|
||||
@@ -902,6 +931,9 @@ int AudioState::handler()
|
||||
samples = av_malloc(buffer_len);
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
samples = av_malloc(buffer_len);
|
||||
|
||||
while(alGetError() == AL_NO_ERROR && !mMovie.mQuit.load(std::memory_order_relaxed) &&
|
||||
mConnected.test_and_set(std::memory_order_relaxed))
|
||||
@@ -924,15 +956,19 @@ int AudioState::handler()
|
||||
{
|
||||
ALuint bufid = mBuffers[mBufferIdx];
|
||||
|
||||
uint8_t *ptr = reinterpret_cast<uint8_t*>(
|
||||
samples ? samples : alMapBufferSOFT(bufid, 0, buffer_len, AL_MAP_WRITE_BIT_SOFT)
|
||||
uint8_t *ptr = reinterpret_cast<uint8_t*>(samples
|
||||
#ifdef AL_SOFT_map_buffer
|
||||
? samples : alMapBufferSOFT(bufid, 0, buffer_len, AL_MAP_WRITE_BIT_SOFT)
|
||||
#endif
|
||||
);
|
||||
if(!ptr) break;
|
||||
|
||||
/* Read the next chunk of data, filling the buffer, and queue it on
|
||||
* the source */
|
||||
bool got_audio = readAudio(ptr, buffer_len);
|
||||
#ifdef AL_SOFT_map_buffer
|
||||
if(!samples) alUnmapBufferSOFT(bufid);
|
||||
#endif
|
||||
if(!got_audio) break;
|
||||
|
||||
if(samples)
|
||||
@@ -973,11 +1009,13 @@ int AudioState::handler()
|
||||
finish:
|
||||
av_freep(&samples);
|
||||
|
||||
#ifdef AL_SOFT_events
|
||||
if(alEventControlSOFT)
|
||||
{
|
||||
alEventControlSOFT(types.size(), types.data(), AL_FALSE);
|
||||
alEventControlSOFT(evt_types.size(), evt_types.data(), AL_FALSE);
|
||||
alEventCallbackSOFT(nullptr, nullptr);
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1681,41 +1719,21 @@ int main(int argc, char *argv[])
|
||||
SDL_RenderPresent(renderer);
|
||||
|
||||
/* Open an audio device */
|
||||
int fileidx = 1;
|
||||
ALCdevice *device = [argc,argv,&fileidx]() -> ALCdevice*
|
||||
{
|
||||
ALCdevice *dev = NULL;
|
||||
if(argc > 3 && strcmp(argv[1], "-device") == 0)
|
||||
{
|
||||
fileidx = 3;
|
||||
dev = alcOpenDevice(argv[2]);
|
||||
if(dev) return dev;
|
||||
std::cerr<< "Failed to open \""<<argv[2]<<"\" - trying default" <<std::endl;
|
||||
}
|
||||
return alcOpenDevice(nullptr);
|
||||
}();
|
||||
ALCcontext *context = alcCreateContext(device, nullptr);
|
||||
if(!context || alcMakeContextCurrent(context) == ALC_FALSE)
|
||||
++argv; --argc;
|
||||
if(InitAL(&argv, &argc))
|
||||
{
|
||||
std::cerr<< "Failed to set up audio device" <<std::endl;
|
||||
if(context)
|
||||
alcDestroyContext(context);
|
||||
return 1;
|
||||
}
|
||||
|
||||
const ALCchar *name = nullptr;
|
||||
if(alcIsExtensionPresent(device, "ALC_ENUMERATE_ALL_EXT"))
|
||||
name = alcGetString(device, ALC_ALL_DEVICES_SPECIFIER);
|
||||
if(!name || alcGetError(device) != AL_NO_ERROR)
|
||||
name = alcGetString(device, ALC_DEVICE_SPECIFIER);
|
||||
std::cout<< "Opened \""<<name<<"\"" <<std::endl;
|
||||
|
||||
if(alcIsExtensionPresent(device, "ALC_SOFT_device_clock"))
|
||||
{
|
||||
std::cout<< "Found ALC_SOFT_device_clock" <<std::endl;
|
||||
alcGetInteger64vSOFT = reinterpret_cast<LPALCGETINTEGER64VSOFT>(
|
||||
alcGetProcAddress(device, "alcGetInteger64vSOFT")
|
||||
);
|
||||
{ auto device = alcGetContextsDevice(alcGetCurrentContext());
|
||||
if(alcIsExtensionPresent(device, "ALC_SOFT_device_clock"))
|
||||
{
|
||||
std::cout<< "Found ALC_SOFT_device_clock" <<std::endl;
|
||||
alcGetInteger64vSOFT = reinterpret_cast<LPALCGETINTEGER64VSOFT>(
|
||||
alcGetProcAddress(device, "alcGetInteger64vSOFT")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if(alIsExtensionPresent("AL_SOFT_source_latency"))
|
||||
@@ -1725,6 +1743,7 @@ int main(int argc, char *argv[])
|
||||
alGetProcAddress("alGetSourcei64vSOFT")
|
||||
);
|
||||
}
|
||||
#ifdef AL_SOFT_map_buffer
|
||||
if(alIsExtensionPresent("AL_SOFTX_map_buffer"))
|
||||
{
|
||||
std::cout<< "Found AL_SOFT_map_buffer" <<std::endl;
|
||||
@@ -1735,6 +1754,8 @@ int main(int argc, char *argv[])
|
||||
alUnmapBufferSOFT = reinterpret_cast<LPALUNMAPBUFFERSOFT>(
|
||||
alGetProcAddress("alUnmapBufferSOFT"));
|
||||
}
|
||||
#endif
|
||||
#ifdef AL_SOFT_events
|
||||
if(alIsExtensionPresent("AL_SOFTX_events"))
|
||||
{
|
||||
std::cout<< "Found AL_SOFT_events" <<std::endl;
|
||||
@@ -1743,17 +1764,33 @@ int main(int argc, char *argv[])
|
||||
alEventCallbackSOFT = reinterpret_cast<LPALEVENTCALLBACKSOFT>(
|
||||
alGetProcAddress("alEventCallbackSOFT"));
|
||||
}
|
||||
#endif
|
||||
|
||||
if(fileidx < argc && strcmp(argv[fileidx], "-direct") == 0)
|
||||
int fileidx = 0;
|
||||
for(;fileidx < argc;++fileidx)
|
||||
{
|
||||
++fileidx;
|
||||
if(!alIsExtensionPresent("AL_SOFT_direct_channels"))
|
||||
std::cerr<< "AL_SOFT_direct_channels not supported for direct output" <<std::endl;
|
||||
else
|
||||
if(strcmp(argv[fileidx], "-direct") == 0)
|
||||
{
|
||||
std::cout<< "Found AL_SOFT_direct_channels" <<std::endl;
|
||||
EnableDirectOut = true;
|
||||
if(!alIsExtensionPresent("AL_SOFT_direct_channels"))
|
||||
std::cerr<< "AL_SOFT_direct_channels not supported for direct output" <<std::endl;
|
||||
else
|
||||
{
|
||||
std::cout<< "Found AL_SOFT_direct_channels" <<std::endl;
|
||||
EnableDirectOut = true;
|
||||
}
|
||||
}
|
||||
else if(strcmp(argv[fileidx], "-wide") == 0)
|
||||
{
|
||||
if(!alIsExtensionPresent("AL_EXT_STEREO_ANGLES"))
|
||||
std::cerr<< "AL_EXT_STEREO_ANGLES not supported for wide stereo" <<std::endl;
|
||||
else
|
||||
{
|
||||
std::cout<< "Found AL_EXT_STEREO_ANGLES" <<std::endl;
|
||||
EnableWideStereo = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
while(fileidx < argc && !movState)
|
||||
@@ -1858,9 +1895,7 @@ int main(int argc, char *argv[])
|
||||
/* Nothing more to play. Shut everything down and quit. */
|
||||
movState = nullptr;
|
||||
|
||||
alcMakeContextCurrent(nullptr);
|
||||
alcDestroyContext(context);
|
||||
alcCloseDevice(device);
|
||||
CloseAL();
|
||||
|
||||
SDL_DestroyRenderer(renderer);
|
||||
renderer = nullptr;
|
||||
|
||||
@@ -4,8 +4,10 @@ option(ALSOFT_NO_QT5 "Use Qt4 instead of Qt5 for alsoft-config" FALSE)
|
||||
|
||||
include_directories("${alsoft-config_BINARY_DIR}")
|
||||
|
||||
set(alsoft-config_SRCS main.cpp
|
||||
mainwindow.cpp
|
||||
set(alsoft-config_SRCS
|
||||
main.cpp
|
||||
mainwindow.cpp
|
||||
mainwindow.h
|
||||
)
|
||||
set(alsoft-config_UIS mainwindow.ui)
|
||||
set(alsoft-config_MOCS mainwindow.h)
|
||||
|
||||
@@ -372,12 +372,14 @@ MainWindow::MainWindow(QWidget *parent) :
|
||||
connect(ui->defaultReverbComboBox, SIGNAL(currentIndexChanged(const QString&)), this, SLOT(enableApplyButton()));
|
||||
connect(ui->enableEaxReverbCheck, SIGNAL(stateChanged(int)), this, SLOT(enableApplyButton()));
|
||||
connect(ui->enableStdReverbCheck, SIGNAL(stateChanged(int)), this, SLOT(enableApplyButton()));
|
||||
connect(ui->enableAutowahCheck, SIGNAL(stateChanged(int)), this, SLOT(enableApplyButton()));
|
||||
connect(ui->enableChorusCheck, SIGNAL(stateChanged(int)), this, SLOT(enableApplyButton()));
|
||||
connect(ui->enableCompressorCheck, SIGNAL(stateChanged(int)), this, SLOT(enableApplyButton()));
|
||||
connect(ui->enableDistortionCheck, SIGNAL(stateChanged(int)), this, SLOT(enableApplyButton()));
|
||||
connect(ui->enableEchoCheck, SIGNAL(stateChanged(int)), this, SLOT(enableApplyButton()));
|
||||
connect(ui->enableEqualizerCheck, SIGNAL(stateChanged(int)), this, SLOT(enableApplyButton()));
|
||||
connect(ui->enableFlangerCheck, SIGNAL(stateChanged(int)), this, SLOT(enableApplyButton()));
|
||||
connect(ui->enableFrequencyShifterCheck, SIGNAL(stateChanged(int)), this, SLOT(enableApplyButton()));
|
||||
connect(ui->enableModulatorCheck, SIGNAL(stateChanged(int)), this, SLOT(enableApplyButton()));
|
||||
connect(ui->enableDedicatedCheck, SIGNAL(stateChanged(int)), this, SLOT(enableApplyButton()));
|
||||
connect(ui->enablePitchShifterCheck, SIGNAL(stateChanged(int)), this, SLOT(enableApplyButton()));
|
||||
@@ -838,12 +840,14 @@ void MainWindow::loadConfig(const QString &fname)
|
||||
*iter = iter->trimmed();
|
||||
ui->enableEaxReverbCheck->setChecked(!excludefx.contains("eaxreverb", Qt::CaseInsensitive));
|
||||
ui->enableStdReverbCheck->setChecked(!excludefx.contains("reverb", Qt::CaseInsensitive));
|
||||
ui->enableAutowahCheck->setChecked(!excludefx.contains("autowah", Qt::CaseInsensitive));
|
||||
ui->enableChorusCheck->setChecked(!excludefx.contains("chorus", Qt::CaseInsensitive));
|
||||
ui->enableCompressorCheck->setChecked(!excludefx.contains("compressor", Qt::CaseInsensitive));
|
||||
ui->enableDistortionCheck->setChecked(!excludefx.contains("distortion", Qt::CaseInsensitive));
|
||||
ui->enableEchoCheck->setChecked(!excludefx.contains("echo", Qt::CaseInsensitive));
|
||||
ui->enableEqualizerCheck->setChecked(!excludefx.contains("equalizer", Qt::CaseInsensitive));
|
||||
ui->enableFlangerCheck->setChecked(!excludefx.contains("flanger", Qt::CaseInsensitive));
|
||||
ui->enableFrequencyShifterCheck->setChecked(!excludefx.contains("fshifter", Qt::CaseInsensitive));
|
||||
ui->enableModulatorCheck->setChecked(!excludefx.contains("modulator", Qt::CaseInsensitive));
|
||||
ui->enableDedicatedCheck->setChecked(!excludefx.contains("dedicated", Qt::CaseInsensitive));
|
||||
ui->enablePitchShifterCheck->setChecked(!excludefx.contains("pshifter", Qt::CaseInsensitive));
|
||||
@@ -1044,6 +1048,8 @@ void MainWindow::saveConfig(const QString &fname) const
|
||||
strlist.append("eaxreverb");
|
||||
if(!ui->enableStdReverbCheck->isChecked())
|
||||
strlist.append("reverb");
|
||||
if(!ui->enableAutowahCheck->isChecked())
|
||||
strlist.append("autowah");
|
||||
if(!ui->enableChorusCheck->isChecked())
|
||||
strlist.append("chorus");
|
||||
if(!ui->enableDistortionCheck->isChecked())
|
||||
@@ -1056,6 +1062,8 @@ void MainWindow::saveConfig(const QString &fname) const
|
||||
strlist.append("equalizer");
|
||||
if(!ui->enableFlangerCheck->isChecked())
|
||||
strlist.append("flanger");
|
||||
if(!ui->enableFrequencyShifterCheck->isChecked())
|
||||
strlist.append("fshifter");
|
||||
if(!ui->enableModulatorCheck->isChecked())
|
||||
strlist.append("modulator");
|
||||
if(!ui->enableDedicatedCheck->isChecked())
|
||||
|
||||
@@ -1933,7 +1933,7 @@ be useful for preventing those extensions from being used.</string>
|
||||
<x>10</x>
|
||||
<y>100</y>
|
||||
<width>511</width>
|
||||
<height>231</height>
|
||||
<height>241</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
@@ -2011,8 +2011,8 @@ for the system to handle.</string>
|
||||
<widget class="QCheckBox" name="enableEchoCheck">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>320</x>
|
||||
<y>30</y>
|
||||
<x>70</x>
|
||||
<y>180</y>
|
||||
<width>131</width>
|
||||
<height>21</height>
|
||||
</rect>
|
||||
@@ -2028,7 +2028,7 @@ for the system to handle.</string>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>320</x>
|
||||
<y>60</y>
|
||||
<y>30</y>
|
||||
<width>131</width>
|
||||
<height>21</height>
|
||||
</rect>
|
||||
@@ -2060,7 +2060,7 @@ for the system to handle.</string>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>320</x>
|
||||
<y>120</y>
|
||||
<y>150</y>
|
||||
<width>131</width>
|
||||
<height>21</height>
|
||||
</rect>
|
||||
@@ -2076,7 +2076,7 @@ for the system to handle.</string>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>320</x>
|
||||
<y>150</y>
|
||||
<y>180</y>
|
||||
<width>131</width>
|
||||
<height>21</height>
|
||||
</rect>
|
||||
@@ -2111,8 +2111,8 @@ added by the ALC_EXT_DEDICATED extension.</string>
|
||||
<widget class="QCheckBox" name="enablePitchShifterCheck">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>70</x>
|
||||
<y>180</y>
|
||||
<x>320</x>
|
||||
<y>120</y>
|
||||
<width>131</width>
|
||||
<height>21</height>
|
||||
</rect>
|
||||
@@ -2124,6 +2124,38 @@ added by the ALC_EXT_DEDICATED extension.</string>
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QCheckBox" name="enableFrequencyShifterCheck">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>320</x>
|
||||
<y>60</y>
|
||||
<width>131</width>
|
||||
<height>21</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Frequency Shifter</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QCheckBox" name="enableAutowahCheck">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>70</x>
|
||||
<y>210</y>
|
||||
<width>131</width>
|
||||
<height>21</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Autowah</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
<widget class="QLabel" name="label_13">
|
||||
<property name="geometry">
|
||||
|
||||
@@ -988,46 +988,30 @@ static Complex *CreateComplexes(size_t n)
|
||||
return a;
|
||||
}
|
||||
|
||||
/* Fast Fourier transform routines. The number of points must be a power of
|
||||
* two. In-place operation is possible only if both the real and imaginary
|
||||
* parts are in-place together.
|
||||
/* Fast Fourier transform routines. The number of points must be a power of
|
||||
* two.
|
||||
*/
|
||||
|
||||
// Performs bit-reversal ordering.
|
||||
static void FftArrange(const uint n, const Complex *in, Complex *out)
|
||||
static void FftArrange(const uint n, Complex *inout)
|
||||
{
|
||||
uint rk, k, m;
|
||||
|
||||
if(in == out)
|
||||
// Handle in-place arrangement.
|
||||
rk = 0;
|
||||
for(k = 0;k < n;k++)
|
||||
{
|
||||
// Handle in-place arrangement.
|
||||
rk = 0;
|
||||
for(k = 0;k < n;k++)
|
||||
if(rk > k)
|
||||
{
|
||||
if(rk > k)
|
||||
{
|
||||
Complex temp = in[rk];
|
||||
out[rk] = in[k];
|
||||
out[k] = temp;
|
||||
}
|
||||
m = n;
|
||||
while(rk&(m >>= 1))
|
||||
rk &= ~m;
|
||||
rk |= m;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Handle copy arrangement.
|
||||
rk = 0;
|
||||
for(k = 0;k < n;k++)
|
||||
{
|
||||
out[rk] = in[k];
|
||||
m = n;
|
||||
while(rk&(m >>= 1))
|
||||
rk &= ~m;
|
||||
rk |= m;
|
||||
Complex temp = inout[rk];
|
||||
inout[rk] = inout[k];
|
||||
inout[k] = temp;
|
||||
}
|
||||
|
||||
m = n;
|
||||
while(rk&(m >>= 1))
|
||||
rk &= ~m;
|
||||
rk |= m;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1061,23 +1045,23 @@ static void FftSummation(const int n, const double s, Complex *cplx)
|
||||
}
|
||||
|
||||
// Performs a forward FFT.
|
||||
static void FftForward(const uint n, const Complex *in, Complex *out)
|
||||
static void FftForward(const uint n, Complex *inout)
|
||||
{
|
||||
FftArrange(n, in, out);
|
||||
FftSummation(n, 1.0, out);
|
||||
FftArrange(n, inout);
|
||||
FftSummation(n, 1.0, inout);
|
||||
}
|
||||
|
||||
// Performs an inverse FFT.
|
||||
static void FftInverse(const uint n, const Complex *in, Complex *out)
|
||||
static void FftInverse(const uint n, Complex *inout)
|
||||
{
|
||||
double f;
|
||||
uint i;
|
||||
|
||||
FftArrange(n, in, out);
|
||||
FftSummation(n, -1.0, out);
|
||||
FftArrange(n, inout);
|
||||
FftSummation(n, -1.0, inout);
|
||||
f = 1.0 / n;
|
||||
for(i = 0;i < n;i++)
|
||||
out[i] = c_muls(out[i], f);
|
||||
inout[i] = c_muls(inout[i], f);
|
||||
}
|
||||
|
||||
/* Calculate the complex helical sequence (or discrete-time analytical signal)
|
||||
@@ -1085,30 +1069,22 @@ static void FftInverse(const uint n, const Complex *in, Complex *out)
|
||||
* of a signal's magnitude response, the imaginary components can be used as
|
||||
* the angles for minimum-phase reconstruction.
|
||||
*/
|
||||
static void Hilbert(const uint n, const Complex *in, Complex *out)
|
||||
static void Hilbert(const uint n, Complex *inout)
|
||||
{
|
||||
uint i;
|
||||
|
||||
if(in == out)
|
||||
{
|
||||
// Handle in-place operation.
|
||||
for(i = 0;i < n;i++)
|
||||
out[i].Imag = 0.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Handle copy operation.
|
||||
for(i = 0;i < n;i++)
|
||||
out[i] = MakeComplex(in[i].Real, 0.0);
|
||||
}
|
||||
FftInverse(n, out, out);
|
||||
// Handle in-place operation.
|
||||
for(i = 0;i < n;i++)
|
||||
inout[i].Imag = 0.0;
|
||||
|
||||
FftInverse(n, inout);
|
||||
for(i = 1;i < (n+1)/2;i++)
|
||||
out[i] = c_muls(out[i], 2.0);
|
||||
inout[i] = c_muls(inout[i], 2.0);
|
||||
/* Increment i if n is even. */
|
||||
i += (n&1)^1;
|
||||
for(;i < n;i++)
|
||||
out[i] = MakeComplex(0.0, 0.0);
|
||||
FftForward(n, out, out);
|
||||
inout[i] = MakeComplex(0.0, 0.0);
|
||||
FftForward(n, inout);
|
||||
}
|
||||
|
||||
/* Calculate the magnitude response of the given input. This is used in
|
||||
@@ -1175,7 +1151,7 @@ static void MinimumPhase(const uint n, const double *in, Complex *out)
|
||||
mags[i] = mags[n - i];
|
||||
out[i] = out[n - i];
|
||||
}
|
||||
Hilbert(n, out, out);
|
||||
Hilbert(n, out);
|
||||
// Remove any DC offset the filter has.
|
||||
mags[0] = EPSILON;
|
||||
for(i = 0;i < n;i++)
|
||||
@@ -2073,7 +2049,7 @@ static void AverageHrirMagnitude(const uint points, const uint n, const double *
|
||||
h[i] = MakeComplex(hrir[i], 0.0);
|
||||
for(;i < n;i++)
|
||||
h[i] = MakeComplex(0.0, 0.0);
|
||||
FftForward(n, h, h);
|
||||
FftForward(n, h);
|
||||
MagnitudeResponse(n, h, r);
|
||||
for(i = 0;i < m;i++)
|
||||
mag[i] = Lerp(mag[i], r[i], f);
|
||||
@@ -2194,16 +2170,16 @@ static void DiffuseFieldEqualize(const uint channels, const uint m, const double
|
||||
{
|
||||
uint ti, fi, ei, ai, i;
|
||||
|
||||
for(ti = 0;ti < channels;ti++)
|
||||
for(fi = 0;fi < hData->mFdCount;fi++)
|
||||
{
|
||||
for(fi = 0;fi < hData->mFdCount;fi++)
|
||||
for(ei = hData->mFds[fi].mEvStart;ei < hData->mFds[fi].mEvCount;ei++)
|
||||
{
|
||||
for(ei = hData->mFds[fi].mEvStart;ei < hData->mFds[fi].mEvCount;ei++)
|
||||
for(ai = 0;ai < hData->mFds[fi].mEvs[ei].mAzCount;ai++)
|
||||
{
|
||||
for(ai = 0;ai < hData->mFds[fi].mEvs[ei].mAzCount;ai++)
|
||||
{
|
||||
HrirAzT *azd = &hData->mFds[fi].mEvs[ei].mAzs[ai];
|
||||
HrirAzT *azd = &hData->mFds[fi].mEvs[ei].mAzs[ai];
|
||||
|
||||
for(ti = 0;ti < channels;ti++)
|
||||
{
|
||||
for(i = 0;i < m;i++)
|
||||
azd->mIrs[ti][i] /= dfa[(ti * m) + i];
|
||||
}
|
||||
@@ -2232,18 +2208,18 @@ static void ReconstructHrirs(const HrirDataT *hData)
|
||||
count = pcdone = lastpc = 0;
|
||||
printf("%3d%% done.", pcdone);
|
||||
fflush(stdout);
|
||||
for(ti = 0;ti < channels;ti++)
|
||||
for(fi = 0;fi < hData->mFdCount;fi++)
|
||||
{
|
||||
for(fi = 0;fi < hData->mFdCount;fi++)
|
||||
for(ei = hData->mFds[fi].mEvStart;ei < hData->mFds[fi].mEvCount;ei++)
|
||||
{
|
||||
for(ei = hData->mFds[fi].mEvStart;ei < hData->mFds[fi].mEvCount;ei++)
|
||||
for(ai = 0;ai < hData->mFds[fi].mEvs[ei].mAzCount;ai++)
|
||||
{
|
||||
for(ai = 0;ai < hData->mFds[fi].mEvs[ei].mAzCount;ai++)
|
||||
{
|
||||
HrirAzT *azd = &hData->mFds[fi].mEvs[ei].mAzs[ai];
|
||||
HrirAzT *azd = &hData->mFds[fi].mEvs[ei].mAzs[ai];
|
||||
|
||||
for(ti = 0;ti < channels;ti++)
|
||||
{
|
||||
MinimumPhase(n, azd->mIrs[ti], h);
|
||||
FftInverse(n, h, h);
|
||||
FftInverse(n, h);
|
||||
for(i = 0;i < hData->mIrPoints;i++)
|
||||
azd->mIrs[ti][i] = h[i].Real;
|
||||
pcdone = ++count * 100 / total;
|
||||
@@ -2270,18 +2246,16 @@ static void ResampleHrirs(const uint rate, HrirDataT *hData)
|
||||
ResamplerT rs;
|
||||
|
||||
ResamplerSetup(&rs, hData->mIrRate, rate);
|
||||
for(ti = 0;ti < channels;ti++)
|
||||
for(fi = 0;fi < hData->mFdCount;fi++)
|
||||
{
|
||||
for(fi = 0;fi < hData->mFdCount;fi++)
|
||||
for(ei = hData->mFds[fi].mEvStart;ei < hData->mFds[fi].mEvCount;ei++)
|
||||
{
|
||||
for(ei = hData->mFds[fi].mEvStart;ei < hData->mFds[fi].mEvCount;ei++)
|
||||
for(ai = 0;ai < hData->mFds[fi].mEvs[ei].mAzCount;ai++)
|
||||
{
|
||||
for(ai = 0;ai < hData->mFds[fi].mEvs[ei].mAzCount;ai++)
|
||||
{
|
||||
HrirAzT *azd = &hData->mFds[fi].mEvs[ei].mAzs[ai];
|
||||
HrirAzT *azd = &hData->mFds[fi].mEvs[ei].mAzs[ai];
|
||||
|
||||
for(ti = 0;ti < channels;ti++)
|
||||
ResamplerRun(&rs, n, azd->mIrs[ti], n, azd->mIrs[ti]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2313,13 +2287,14 @@ static void SynthesizeOnsets(HrirDataT *hData)
|
||||
uint ti, fi, oi, ai, ei, a0, a1;
|
||||
double t, of, af;
|
||||
|
||||
for(ti = 0;ti < channels;ti++)
|
||||
for(fi = 0;fi < hData->mFdCount;fi++)
|
||||
{
|
||||
for(fi = 0;fi < hData->mFdCount;fi++)
|
||||
if(hData->mFds[fi].mEvStart <= 0)
|
||||
continue;
|
||||
oi = hData->mFds[fi].mEvStart;
|
||||
|
||||
for(ti = 0;ti < channels;ti++)
|
||||
{
|
||||
if(hData->mFds[fi].mEvStart <= 0)
|
||||
continue;
|
||||
oi = hData->mFds[fi].mEvStart;
|
||||
t = 0.0;
|
||||
for(ai = 0;ai < hData->mFds[fi].mEvs[oi].mAzCount;ai++)
|
||||
t += hData->mFds[fi].mEvs[oi].mAzs[ai].mDelays[ti];
|
||||
@@ -2330,7 +2305,12 @@ static void SynthesizeOnsets(HrirDataT *hData)
|
||||
for(ai = 0;ai < hData->mFds[fi].mEvs[ei].mAzCount;ai++)
|
||||
{
|
||||
CalcAzIndices(hData, fi, oi, hData->mFds[fi].mEvs[ei].mAzs[ai].mAzimuth, &a0, &a1, &af);
|
||||
hData->mFds[fi].mEvs[ei].mAzs[ai].mDelays[ti] = Lerp(hData->mFds[fi].mEvs[0].mAzs[0].mDelays[ti], Lerp(hData->mFds[fi].mEvs[oi].mAzs[a0].mDelays[ti], hData->mFds[fi].mEvs[oi].mAzs[a1].mDelays[ti], af), of);
|
||||
hData->mFds[fi].mEvs[ei].mAzs[ai].mDelays[ti] = Lerp(
|
||||
hData->mFds[fi].mEvs[0].mAzs[0].mDelays[ti],
|
||||
Lerp(hData->mFds[fi].mEvs[oi].mAzs[a0].mDelays[ti],
|
||||
hData->mFds[fi].mEvs[oi].mAzs[a1].mDelays[ti], af),
|
||||
of
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2421,16 +2401,16 @@ static void NormalizeHrirs(const HrirDataT *hData)
|
||||
uint ti, fi, ei, ai, i;
|
||||
double maxLevel = 0.0;
|
||||
|
||||
for(ti = 0;ti < channels;ti++)
|
||||
for(fi = 0;fi < hData->mFdCount;fi++)
|
||||
{
|
||||
for(fi = 0;fi < hData->mFdCount;fi++)
|
||||
for(ei = 0;ei < hData->mFds[fi].mEvCount;ei++)
|
||||
{
|
||||
for(ei = 0;ei < hData->mFds[fi].mEvCount;ei++)
|
||||
for(ai = 0;ai < hData->mFds[fi].mEvs[ei].mAzCount;ai++)
|
||||
{
|
||||
for(ai = 0;ai < hData->mFds[fi].mEvs[ei].mAzCount;ai++)
|
||||
{
|
||||
HrirAzT *azd = &hData->mFds[fi].mEvs[ei].mAzs[ai];
|
||||
HrirAzT *azd = &hData->mFds[fi].mEvs[ei].mAzs[ai];
|
||||
|
||||
for(ti = 0;ti < channels;ti++)
|
||||
{
|
||||
for(i = 0;i < n;i++)
|
||||
maxLevel = fmax(fabs(azd->mIrs[ti][i]), maxLevel);
|
||||
}
|
||||
@@ -2438,16 +2418,16 @@ static void NormalizeHrirs(const HrirDataT *hData)
|
||||
}
|
||||
}
|
||||
maxLevel = 1.01 * maxLevel;
|
||||
for(ti = 0;ti < channels;ti++)
|
||||
for(fi = 0;fi < hData->mFdCount;fi++)
|
||||
{
|
||||
for(fi = 0;fi < hData->mFdCount;fi++)
|
||||
for(ei = 0;ei < hData->mFds[fi].mEvCount;ei++)
|
||||
{
|
||||
for(ei = 0;ei < hData->mFds[fi].mEvCount;ei++)
|
||||
for(ai = 0;ai < hData->mFds[fi].mEvs[ei].mAzCount;ai++)
|
||||
{
|
||||
for(ai = 0;ai < hData->mFds[fi].mEvs[ei].mAzCount;ai++)
|
||||
{
|
||||
HrirAzT *azd = &hData->mFds[fi].mEvs[ei].mAzs[ai];
|
||||
HrirAzT *azd = &hData->mFds[fi].mEvs[ei].mAzs[ai];
|
||||
|
||||
for(ti = 0;ti < channels;ti++)
|
||||
{
|
||||
for(i = 0;i < n;i++)
|
||||
azd->mIrs[ti][i] /= maxLevel;
|
||||
}
|
||||
@@ -2481,16 +2461,16 @@ static void CalculateHrtds(const HeadModelT model, const double radius, HrirData
|
||||
|
||||
if(model == HM_DATASET)
|
||||
{
|
||||
for(ti = 0;ti < channels;ti++)
|
||||
for(fi = 0;fi < hData->mFdCount;fi++)
|
||||
{
|
||||
for(fi = 0;fi < hData->mFdCount;fi++)
|
||||
for(ei = 0;ei < hData->mFds[fi].mEvCount;ei++)
|
||||
{
|
||||
for(ei = 0;ei < hData->mFds[fi].mEvCount;ei++)
|
||||
for(ai = 0;ai < hData->mFds[fi].mEvs[ei].mAzCount;ai++)
|
||||
{
|
||||
for(ai = 0;ai < hData->mFds[fi].mEvs[ei].mAzCount;ai++)
|
||||
{
|
||||
HrirAzT *azd = &hData->mFds[fi].mEvs[ei].mAzs[ai];
|
||||
HrirAzT *azd = &hData->mFds[fi].mEvs[ei].mAzs[ai];
|
||||
|
||||
for(ti = 0;ti < channels;ti++)
|
||||
{
|
||||
t = azd->mDelays[ti] * radius / hData->mRadius;
|
||||
azd->mDelays[ti] = t;
|
||||
maxHrtd = fmax(t, maxHrtd);
|
||||
@@ -2502,18 +2482,18 @@ static void CalculateHrtds(const HeadModelT model, const double radius, HrirData
|
||||
}
|
||||
else
|
||||
{
|
||||
for(ti = 0;ti < channels;ti++)
|
||||
for(fi = 0;fi < hData->mFdCount;fi++)
|
||||
{
|
||||
for(fi = 0;fi < hData->mFdCount;fi++)
|
||||
for(ei = 0;ei < hData->mFds[fi].mEvCount;ei++)
|
||||
{
|
||||
for(ei = 0;ei < hData->mFds[fi].mEvCount;ei++)
|
||||
HrirEvT *evd = &hData->mFds[fi].mEvs[ei];
|
||||
|
||||
for(ai = 0;ai < evd->mAzCount;ai++)
|
||||
{
|
||||
HrirEvT *evd = &hData->mFds[fi].mEvs[ei];
|
||||
HrirAzT *azd = &evd->mAzs[ai];
|
||||
|
||||
for(ai = 0;ai < evd->mAzCount;ai++)
|
||||
for(ti = 0;ti < channels;ti++)
|
||||
{
|
||||
HrirAzT *azd = &evd->mAzs[ai];
|
||||
|
||||
t = CalcLTD(evd->mElevation, azd->mAzimuth, radius, hData->mFds[fi].mDistance);
|
||||
azd->mDelays[ti] = t;
|
||||
maxHrtd = fmax(t, maxHrtd);
|
||||
@@ -2523,11 +2503,11 @@ static void CalculateHrtds(const HeadModelT model, const double radius, HrirData
|
||||
}
|
||||
}
|
||||
}
|
||||
for(ti = 0;ti < channels;ti++)
|
||||
for(fi = 0;fi < hData->mFdCount;fi++)
|
||||
{
|
||||
for(fi = 0;fi < hData->mFdCount;fi++)
|
||||
for(ei = 0;ei < hData->mFds[fi].mEvCount;ei++)
|
||||
{
|
||||
for(ei = 0;ei < hData->mFds[fi].mEvCount;ei++)
|
||||
for(ti = 0;ti < channels;ti++)
|
||||
{
|
||||
for(ai = 0;ai < hData->mFds[fi].mEvs[ei].mAzCount;ai++)
|
||||
hData->mFds[fi].mEvs[ei].mAzs[ai].mDelays[ti] -= minHrtd;
|
||||
@@ -2618,8 +2598,7 @@ static void FreeHrirData(HrirDataT *hData)
|
||||
{
|
||||
if(hData->mFds[0].mEvs[0].mAzs)
|
||||
{
|
||||
if(hData->mFds[0].mEvs[0].mAzs[0].mIrs[0] != NULL)
|
||||
free(hData->mFds[0].mEvs[0].mAzs[0].mIrs[0]);
|
||||
free(hData->mFds[0].mEvs[0].mAzs[0].mIrs[0]);
|
||||
free(hData->mFds[0].mEvs[0].mAzs);
|
||||
}
|
||||
free(hData->mFds[0].mEvs);
|
||||
|
||||
Reference in New Issue
Block a user