mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-04-21 04:15:36 +00:00
Revert "Updated SDL, Bullet and OpenAL soft libs"
This reverts commit 370161cfb1.
This commit is contained in:
parent
160dc00c07
commit
e7ee94428e
1102 changed files with 62741 additions and 204988 deletions
|
|
@ -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", SndioBackendFactory_getFactory },
|
||||
#endif
|
||||
#ifdef HAVE_OSS
|
||||
{ "oss", ALCossBackendFactory_getFactory },
|
||||
{ "sndio", ALCsndioBackendFactory_getFactory },
|
||||
#endif
|
||||
#ifdef HAVE_QSA
|
||||
{ "qsa", ALCqsaBackendFactory_getFactory },
|
||||
|
|
@ -550,12 +550,14 @@ static const struct {
|
|||
DECL(AL_EFFECT_ECHO),
|
||||
DECL(AL_EFFECT_FLANGER),
|
||||
DECL(AL_EFFECT_PITCH_SHIFTER),
|
||||
DECL(AL_EFFECT_FREQUENCY_SHIFTER),
|
||||
#if 0
|
||||
DECL(AL_EFFECT_FREQUENCY_SHIFTER),
|
||||
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),
|
||||
|
|
@ -630,10 +632,6 @@ 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),
|
||||
|
|
@ -656,11 +654,6 @@ 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),
|
||||
|
|
@ -728,7 +721,6 @@ 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 "
|
||||
|
|
@ -1165,6 +1157,75 @@ 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
|
||||
|
|
@ -1245,7 +1306,7 @@ static void ProbeDevices(al_string *list, struct BackendInfo *backendinfo, enum
|
|||
if(backendinfo->getFactory)
|
||||
{
|
||||
ALCbackendFactory *factory = backendinfo->getFactory();
|
||||
V(factory,probe)(type, list);
|
||||
V(factory,probe)(type);
|
||||
}
|
||||
|
||||
UnlockLists();
|
||||
|
|
@ -1255,6 +1316,17 @@ 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
|
||||
|
|
@ -1635,11 +1707,10 @@ static void alcSetError(ALCdevice *device, ALCenum errorCode)
|
|||
}
|
||||
|
||||
|
||||
static struct Compressor *CreateDeviceLimiter(const ALCdevice *device, const ALfloat threshold)
|
||||
struct Compressor *CreateDeviceLimiter(const ALCdevice *device)
|
||||
{
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
/* UpdateClockBase
|
||||
|
|
@ -1666,7 +1737,7 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
|
|||
{
|
||||
enum HrtfRequestMode hrtf_userreq = Hrtf_Default;
|
||||
enum HrtfRequestMode hrtf_appreq = Hrtf_Default;
|
||||
ALCenum gainLimiter = device->LimiterState;
|
||||
ALCenum gainLimiter = device->Limiter ? ALC_TRUE : ALC_FALSE;
|
||||
const ALsizei old_sends = device->NumAuxSends;
|
||||
ALsizei new_sends = device->NumAuxSends;
|
||||
enum DevFmtChannels oldChans;
|
||||
|
|
@ -1982,7 +2053,6 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
|
|||
device->RealOut.NumChannels = 0;
|
||||
|
||||
UpdateClockBase(device);
|
||||
device->FixedLatency = 0;
|
||||
|
||||
device->DitherSeed = DITHER_RNG_SEED;
|
||||
|
||||
|
|
@ -2143,12 +2213,9 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(depth > 0)
|
||||
{
|
||||
depth = clampi(depth, 2, 24);
|
||||
device->DitherDepth = powf(2.0f, (ALfloat)(depth-1));
|
||||
}
|
||||
else if(depth > 24)
|
||||
depth = 24;
|
||||
device->DitherDepth = (depth > 0) ? powf(2.0f, (ALfloat)(depth-1)) : 0.0f;
|
||||
}
|
||||
if(!(device->DitherDepth > 0.0f))
|
||||
TRACE("Dithering disabled\n");
|
||||
|
|
@ -2156,57 +2223,19 @@ 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. 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).
|
||||
* ALC_FALSE. We default to on, so ALC_DONT_CARE_SOFT is the same as
|
||||
* ALC_TRUE.
|
||||
*/
|
||||
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)
|
||||
{
|
||||
ALfloat thrshld = 1.0f;
|
||||
switch(device->FmtType)
|
||||
if(!device->Limiter || device->Frequency != GetCompressorSampleRate(device->Limiter))
|
||||
{
|
||||
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;
|
||||
al_free(device->Limiter);
|
||||
device->Limiter = CreateDeviceLimiter(device);
|
||||
}
|
||||
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
|
||||
{
|
||||
|
|
@ -2217,8 +2246,6 @@ 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.
|
||||
*/
|
||||
|
|
@ -2373,13 +2400,11 @@ 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;
|
||||
|
|
@ -2613,6 +2638,7 @@ 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);
|
||||
|
|
@ -2639,11 +2665,6 @@ 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");
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -2752,7 +2773,17 @@ 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);
|
||||
|
|
@ -2776,7 +2807,6 @@ 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;
|
||||
|
||||
|
|
@ -2809,16 +2839,6 @@ 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;
|
||||
}
|
||||
|
|
@ -3600,7 +3620,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 = GetClockLatency(device);
|
||||
clock = V0(device->Backend,getClockLatency)();
|
||||
values[i++] = ALC_DEVICE_CLOCK_SOFT;
|
||||
values[i++] = clock.ClockTime;
|
||||
|
||||
|
|
@ -3626,7 +3646,7 @@ ALC_API void ALC_APIENTRY alcGetInteger64vSOFT(ALCdevice *device, ALCenum pname,
|
|||
|
||||
case ALC_DEVICE_LATENCY_SOFT:
|
||||
almtx_lock(&device->BackendLock);
|
||||
clock = GetClockLatency(device);
|
||||
clock = V0(device->Backend,getClockLatency)();
|
||||
almtx_unlock(&device->BackendLock);
|
||||
*values = clock.Latency;
|
||||
break;
|
||||
|
|
@ -3637,7 +3657,7 @@ ALC_API void ALC_APIENTRY alcGetInteger64vSOFT(ALCdevice *device, ALCenum pname,
|
|||
else
|
||||
{
|
||||
almtx_lock(&device->BackendLock);
|
||||
clock = GetClockLatency(device);
|
||||
clock = V0(device->Backend,getClockLatency)();
|
||||
almtx_unlock(&device->BackendLock);
|
||||
values[0] = clock.ClockTime;
|
||||
values[1] = clock.Latency;
|
||||
|
|
@ -4053,7 +4073,6 @@ 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;
|
||||
|
||||
|
|
@ -4192,6 +4211,8 @@ 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 {
|
||||
|
|
@ -4373,12 +4394,6 @@ 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;
|
||||
|
|
@ -4519,6 +4534,8 @@ 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 {
|
||||
|
|
|
|||
|
|
@ -145,6 +145,16 @@ 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.
|
||||
|
|
@ -211,31 +221,32 @@ 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.u.user.type = AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT;
|
||||
evt.u.user.id = id;
|
||||
evt.u.user.param = AL_STOPPED;
|
||||
evt.EnumType = EventType_SourceStateChange;
|
||||
evt.Type = AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT;
|
||||
evt.ObjectId = id;
|
||||
evt.Param = AL_STOPPED;
|
||||
|
||||
/* Normally snprintf would be used, but this is called from the mixer and
|
||||
* that function's not real-time safe, so we have to construct it manually.
|
||||
*/
|
||||
strcpy(evt.u.user.msg, "Source ID "); strpos = 10;
|
||||
strcpy(evt.Message, "Source ID "); strpos = 10;
|
||||
scale = 1000000000;
|
||||
while(scale > 0 && scale > id)
|
||||
scale /= 10;
|
||||
while(scale > 0)
|
||||
{
|
||||
evt.u.user.msg[strpos++] = '0' + ((id/scale)%10);
|
||||
evt.Message[strpos++] = '0' + ((id/scale)%10);
|
||||
scale /= 10;
|
||||
}
|
||||
strcpy(evt.u.user.msg+strpos, " state changed to AL_STOPPED");
|
||||
strcpy(evt.Message+strpos, " state changed to AL_STOPPED");
|
||||
|
||||
if(ll_ringbuffer_write(context->AsyncEvents, (const char*)&evt, 1) == 1)
|
||||
alsem_post(&context->EventSem);
|
||||
|
|
@ -331,7 +342,9 @@ void aluSelectPostProcess(ALCdevice *device)
|
|||
}
|
||||
|
||||
|
||||
/* Prepares the interpolator for a given rate (determined by increment).
|
||||
/* 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.
|
||||
*
|
||||
* 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
|
||||
|
|
@ -356,7 +369,7 @@ void BsincPrepare(const ALuint increment, BsincState *state, const BSincTable *t
|
|||
|
||||
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];
|
||||
}
|
||||
|
||||
|
|
@ -462,40 +475,12 @@ 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;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
props->State = slot->Params.EffectState;
|
||||
slot->Params.EffectState = state;
|
||||
|
||||
ATOMIC_REPLACE_HEAD(struct ALeffectslotProps*, &context->FreeEffectslotProps, props);
|
||||
}
|
||||
|
|
@ -671,26 +656,24 @@ 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->NumChannelsPerOrder[i];
|
||||
voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
|
||||
voice->Flags |= VOICE_HAS_NFC;
|
||||
}
|
||||
|
||||
/* A scalar of 1.5 for plain stereo results in +/-60 degrees being
|
||||
* moved to +/-90 degrees for direct right and left speaker
|
||||
* responses.
|
||||
*/
|
||||
CalcAngleCoeffs((Device->Render_Mode==StereoPair) ? ScaleAzimuthFront(Azi, 1.5f) : Azi,
|
||||
Elev, Spread, coeffs);
|
||||
if(Device->Render_Mode == StereoPair)
|
||||
CalcAnglePairwiseCoeffs(Azi, Elev, Spread, coeffs);
|
||||
else
|
||||
CalcAngleCoeffs(Azi, Elev, Spread, coeffs);
|
||||
|
||||
/* NOTE: W needs to be scaled by sqrt(2) due to FuMa normalization. */
|
||||
ComputePanGains(&Device->Dry, coeffs, DryGain*SQRTF_2,
|
||||
ComputeDryPanGains(&Device->Dry, coeffs, DryGain*1.414213562f,
|
||||
voice->Direct.Params[0].Gains.Target);
|
||||
for(i = 0;i < NumSends;i++)
|
||||
{
|
||||
const ALeffectslot *Slot = SendSlots[i];
|
||||
if(Slot)
|
||||
ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs,
|
||||
WetGain[i]*SQRTF_2, voice->Send[i].Params[0].Gains.Target
|
||||
ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
|
||||
coeffs, WetGain[i]*1.414213562f, voice->Send[i].Params[0].Gains.Target
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -699,6 +682,8 @@ 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;
|
||||
|
||||
|
|
@ -741,25 +726,25 @@ static void CalcPanningAndFilters(ALvoice *voice, const ALfloat Azi, const ALflo
|
|||
* outputs on the columns.
|
||||
*/
|
||||
aluMatrixfSet(&matrix,
|
||||
// ACN0 ACN1 ACN2 ACN3
|
||||
SQRTF_2, 0.0f, 0.0f, 0.0f, // Ambi W
|
||||
0.0f, -N[0]*SQRTF_3, N[1]*SQRTF_3, -N[2]*SQRTF_3, // Ambi X
|
||||
0.0f, U[0]*SQRTF_3, -U[1]*SQRTF_3, U[2]*SQRTF_3, // Ambi Y
|
||||
0.0f, -V[0]*SQRTF_3, V[1]*SQRTF_3, -V[2]*SQRTF_3 // Ambi Z
|
||||
// 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
|
||||
);
|
||||
|
||||
voice->Direct.Buffer = Device->FOAOut.Buffer;
|
||||
voice->Direct.Channels = Device->FOAOut.NumChannels;
|
||||
for(c = 0;c < num_channels;c++)
|
||||
ComputePanGains(&Device->FOAOut, matrix.m[c], DryGain,
|
||||
voice->Direct.Params[c].Gains.Target);
|
||||
ComputeFirstOrderGains(&Device->FOAOut, matrix.m[c], DryGain,
|
||||
voice->Direct.Params[c].Gains.Target);
|
||||
for(i = 0;i < NumSends;i++)
|
||||
{
|
||||
const ALeffectslot *Slot = SendSlots[i];
|
||||
if(Slot)
|
||||
{
|
||||
for(c = 0;c < num_channels;c++)
|
||||
ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
|
||||
ComputeFirstOrderGainsBF(Slot->ChanMap, Slot->NumChannels,
|
||||
matrix.m[c], WetGain[i], voice->Send[i].Params[c].Gains.Target
|
||||
);
|
||||
}
|
||||
|
|
@ -915,15 +900,17 @@ 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->NumChannelsPerOrder[i];
|
||||
voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
|
||||
voice->Flags |= VOICE_HAS_NFC;
|
||||
}
|
||||
|
||||
/* Calculate the directional coefficients once, which apply to all
|
||||
* input channels.
|
||||
*/
|
||||
CalcAngleCoeffs((Device->Render_Mode==StereoPair) ? ScaleAzimuthFront(Azi, 1.5f) : Azi,
|
||||
Elev, Spread, coeffs);
|
||||
if(Device->Render_Mode == StereoPair)
|
||||
CalcAnglePairwiseCoeffs(Azi, Elev, Spread, coeffs);
|
||||
else
|
||||
CalcAngleCoeffs(Azi, Elev, Spread, coeffs);
|
||||
|
||||
for(c = 0;c < num_channels;c++)
|
||||
{
|
||||
|
|
@ -938,8 +925,9 @@ static void CalcPanningAndFilters(ALvoice *voice, const ALfloat Azi, const ALflo
|
|||
continue;
|
||||
}
|
||||
|
||||
ComputePanGains(&Device->Dry, coeffs, DryGain * downmix_gain,
|
||||
voice->Direct.Params[c].Gains.Target);
|
||||
ComputeDryPanGains(&Device->Dry,
|
||||
coeffs, DryGain * downmix_gain, voice->Direct.Params[c].Gains.Target
|
||||
);
|
||||
}
|
||||
|
||||
for(i = 0;i < NumSends;i++)
|
||||
|
|
@ -975,7 +963,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->NumChannelsPerOrder[i];
|
||||
voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
|
||||
voice->Flags |= VOICE_HAS_NFC;
|
||||
}
|
||||
|
||||
|
|
@ -994,14 +982,14 @@ static void CalcPanningAndFilters(ALvoice *voice, const ALfloat Azi, const ALflo
|
|||
continue;
|
||||
}
|
||||
|
||||
CalcAngleCoeffs(
|
||||
(Device->Render_Mode==StereoPair) ? ScaleAzimuthFront(chans[c].angle, 3.0f)
|
||||
: chans[c].angle,
|
||||
chans[c].elevation, Spread, coeffs
|
||||
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
|
||||
);
|
||||
|
||||
ComputePanGains(&Device->Dry, coeffs, DryGain,
|
||||
voice->Direct.Params[c].Gains.Target);
|
||||
for(i = 0;i < NumSends;i++)
|
||||
{
|
||||
const ALeffectslot *Slot = SendSlots[i];
|
||||
|
|
@ -1634,7 +1622,7 @@ static void ApplyDistanceComp(ALfloat (*restrict Samples)[BUFFERSIZE], DistanceC
|
|||
continue;
|
||||
}
|
||||
|
||||
if(LIKELY(SamplesToDo >= base))
|
||||
if(SamplesToDo >= base)
|
||||
{
|
||||
for(i = 0;i < base;i++)
|
||||
Values[i] = distbuf[i];
|
||||
|
|
@ -1662,9 +1650,6 @@ 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.
|
||||
|
|
@ -1678,7 +1663,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] = fast_roundf(val) * invscale;
|
||||
samples[i] = fastf2i(val) * invscale;
|
||||
}
|
||||
}
|
||||
*dither_seed = seed;
|
||||
|
|
@ -1689,9 +1674,9 @@ static inline ALfloat Conv_ALfloat(ALfloat val)
|
|||
{ return val; }
|
||||
static inline ALint Conv_ALint(ALfloat val)
|
||||
{
|
||||
/* 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
|
||||
/* Floats have a 23-bit mantissa. A bit of the exponent helps out along
|
||||
* with the sign bit, giving 25 bits. So [-16777216, +16777216] is the max
|
||||
* integer range normalized floats can be converted to before losing
|
||||
* precision.
|
||||
*/
|
||||
return fastf2i(clampf(val*16777216.0f, -16777216.0f, 16777215.0f))<<7;
|
||||
|
|
@ -1717,10 +1702,6 @@ 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); \
|
||||
|
|
@ -1838,7 +1819,8 @@ void aluMixData(ALCdevice *device, ALvoid *OutBuffer, ALsizei NumSamples)
|
|||
SamplesToDo, device->RealOut.NumChannels);
|
||||
|
||||
if(device->Limiter)
|
||||
ApplyCompression(device->Limiter, SamplesToDo, device->RealOut.Buffer);
|
||||
ApplyCompression(device->Limiter, device->RealOut.NumChannels, SamplesToDo,
|
||||
device->RealOut.Buffer);
|
||||
|
||||
if(device->DitherDepth > 0.0f)
|
||||
ApplyDither(device->RealOut.Buffer, &device->DitherSeed, device->DitherDepth,
|
||||
|
|
@ -1851,16 +1833,27 @@ void aluMixData(ALCdevice *device, ALvoid *OutBuffer, ALsizei NumSamples)
|
|||
|
||||
switch(device->FmtType)
|
||||
{
|
||||
#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
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1872,24 +1865,35 @@ 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.u.user.type = AL_EVENT_TYPE_DISCONNECTED_SOFT;
|
||||
evt.u.user.id = 0;
|
||||
evt.u.user.param = 0;
|
||||
evt.EnumType = EventType_Disconnected;
|
||||
evt.Type = AL_EVENT_TYPE_DISCONNECTED_SOFT;
|
||||
evt.ObjectId = 0;
|
||||
evt.Param = 0;
|
||||
|
||||
va_start(args, msg);
|
||||
msglen = vsnprintf(evt.u.user.msg, sizeof(evt.u.user.msg), msg, args);
|
||||
msglen = vsnprintf(evt.Message, sizeof(evt.Message), msg, args);
|
||||
va_end(args);
|
||||
|
||||
if(msglen < 0 || (size_t)msglen >= sizeof(evt.u.user.msg))
|
||||
evt.u.user.msg[sizeof(evt.u.user.msg)-1] = 0;
|
||||
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);
|
||||
}
|
||||
|
||||
ctx = ATOMIC_LOAD_SEQ(&device->ContextList);
|
||||
while(ctx)
|
||||
|
|
|
|||
|
|
@ -36,9 +36,6 @@
|
|||
#include <windows.h>
|
||||
#include <shlobj.h>
|
||||
#endif
|
||||
#ifdef __APPLE__
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#endif
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alconfig.h"
|
||||
|
|
@ -481,26 +478,6 @@ 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);
|
||||
|
|
|
|||
|
|
@ -1153,17 +1153,10 @@ error2:
|
|||
|
||||
static ALCboolean ALCcaptureAlsa_start(ALCcaptureAlsa *self)
|
||||
{
|
||||
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));
|
||||
}
|
||||
int err = snd_pcm_start(self->pcmHandle);
|
||||
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;
|
||||
|
|
@ -1375,6 +1368,11 @@ 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;
|
||||
|
|
@ -1412,25 +1410,19 @@ static ALCboolean ALCalsaBackendFactory_querySupport(ALCalsaBackendFactory* UNUS
|
|||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
static void ALCalsaBackendFactory_probe(ALCalsaBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
|
||||
static void ALCalsaBackendFactory_probe(ALCalsaBackendFactory* UNUSED(self), enum DevProbe type)
|
||||
{
|
||||
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, APPEND_OUTNAME);
|
||||
VECTOR_FOR_EACH(const DevMap, PlaybackDevices, AppendAllDevicesList2);
|
||||
break;
|
||||
|
||||
case CAPTURE_DEVICE_PROBE:
|
||||
probe_devices(SND_PCM_STREAM_CAPTURE, &CaptureDevices);
|
||||
VECTOR_FOR_EACH(const DevMap, CaptureDevices, APPEND_OUTNAME);
|
||||
VECTOR_FOR_EACH(const DevMap, CaptureDevices, AppendCaptureDeviceList2);
|
||||
break;
|
||||
#undef APPEND_OUTNAME
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@
|
|||
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,7 +3,6 @@
|
|||
|
||||
#include "alMain.h"
|
||||
#include "threads.h"
|
||||
#include "alstring.h"
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
@ -116,7 +115,7 @@ struct ALCbackendFactoryVtable {
|
|||
|
||||
ALCboolean (*const querySupport)(ALCbackendFactory *self, ALCbackend_Type type);
|
||||
|
||||
void (*const probe)(ALCbackendFactory *self, enum DevProbe type, al_string *outnames);
|
||||
void (*const probe)(ALCbackendFactory *self, enum DevProbe type);
|
||||
|
||||
ALCbackend* (*const createBackend)(ALCbackendFactory *self, ALCdevice *device, ALCbackend_Type type);
|
||||
};
|
||||
|
|
@ -125,7 +124,7 @@ struct ALCbackendFactoryVtable {
|
|||
DECLARE_THUNK(T, ALCbackendFactory, ALCboolean, init) \
|
||||
DECLARE_THUNK(T, ALCbackendFactory, void, deinit) \
|
||||
DECLARE_THUNK1(T, ALCbackendFactory, ALCboolean, querySupport, ALCbackend_Type) \
|
||||
DECLARE_THUNK2(T, ALCbackendFactory, void, probe, enum DevProbe, al_string*) \
|
||||
DECLARE_THUNK1(T, ALCbackendFactory, void, probe, enum DevProbe) \
|
||||
DECLARE_THUNK2(T, ALCbackendFactory, ALCbackend*, createBackend, ALCdevice*, ALCbackend_Type) \
|
||||
\
|
||||
static const struct ALCbackendFactoryVtable T##_ALCbackendFactory_vtable = { \
|
||||
|
|
@ -143,7 +142,7 @@ ALCbackendFactory *ALCcoreAudioBackendFactory_getFactory(void);
|
|||
ALCbackendFactory *ALCossBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCjackBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCsolarisBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *SndioBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCsndioBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCqsaBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCwasapiBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCdsoundBackendFactory_getFactory(void);
|
||||
|
|
@ -162,15 +161,6 @@ 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,6 +28,7 @@
|
|||
#include "alu.h"
|
||||
#include "ringbuffer.h"
|
||||
|
||||
#include <CoreServices/CoreServices.h>
|
||||
#include <unistd.h>
|
||||
#include <AudioUnit/AudioUnit.h>
|
||||
#include <AudioToolbox/AudioToolbox.h>
|
||||
|
|
@ -111,11 +112,7 @@ 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;
|
||||
|
|
@ -454,6 +451,7 @@ 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;
|
||||
|
|
@ -467,11 +465,7 @@ 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;
|
||||
|
|
@ -510,9 +504,7 @@ 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;
|
||||
|
|
@ -525,6 +517,7 @@ static ALCenum ALCcoreAudioCapture_open(ALCcoreAudioCapture *self, const ALCchar
|
|||
ERR("AudioObjectGetPropertyData failed\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if(inputDevice == kAudioDeviceUnknown)
|
||||
{
|
||||
ERR("No input device found\n");
|
||||
|
|
@ -538,7 +531,6 @@ static ALCenum ALCcoreAudioCapture_open(ALCcoreAudioCapture *self, const ALCchar
|
|||
ERR("AudioUnitSetProperty failed\n");
|
||||
goto error;
|
||||
}
|
||||
#endif
|
||||
|
||||
// set capture callback
|
||||
input.inputProc = ALCcoreAudioCapture_RecordProc;
|
||||
|
|
@ -760,7 +752,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, al_string *outnames);
|
||||
static void ALCcoreAudioBackendFactory_probe(ALCcoreAudioBackendFactory *self, enum DevProbe type);
|
||||
static ALCbackend* ALCcoreAudioBackendFactory_createBackend(ALCcoreAudioBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
|
||||
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCcoreAudioBackendFactory);
|
||||
|
||||
|
|
@ -784,13 +776,15 @@ static ALCboolean ALCcoreAudioBackendFactory_querySupport(ALCcoreAudioBackendFac
|
|||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
static void ALCcoreAudioBackendFactory_probe(ALCcoreAudioBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
|
||||
static void ALCcoreAudioBackendFactory_probe(ALCcoreAudioBackendFactory* UNUSED(self), enum DevProbe type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case ALL_DEVICE_PROBE:
|
||||
AppendAllDevicesList(ca_device);
|
||||
break;
|
||||
case CAPTURE_DEVICE_PROBE:
|
||||
alstr_append_range(outnames, ca_device, ca_device+sizeof(ca_device));
|
||||
AppendCaptureDeviceList(ca_device);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -969,6 +969,11 @@ 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;
|
||||
|
|
@ -979,7 +984,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, al_string *outnames);
|
||||
static void ALCdsoundBackendFactory_probe(ALCdsoundBackendFactory *self, enum DevProbe type);
|
||||
static ALCbackend* ALCdsoundBackendFactory_createBackend(ALCdsoundBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
|
||||
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCdsoundBackendFactory);
|
||||
|
||||
|
|
@ -1023,7 +1028,7 @@ static ALCboolean ALCdsoundBackendFactory_querySupport(ALCdsoundBackendFactory*
|
|||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
static void ALCdsoundBackendFactory_probe(ALCdsoundBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
|
||||
static void ALCdsoundBackendFactory_probe(ALCdsoundBackendFactory* UNUSED(self), enum DevProbe type)
|
||||
{
|
||||
HRESULT hr, hrcom;
|
||||
|
||||
|
|
@ -1031,17 +1036,12 @@ 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, APPEND_OUTNAME);
|
||||
VECTOR_FOR_EACH(const DevMap, PlaybackDevices, AppendAllDevicesList2);
|
||||
break;
|
||||
|
||||
case CAPTURE_DEVICE_PROBE:
|
||||
|
|
@ -1049,9 +1049,8 @@ 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, APPEND_OUTNAME);
|
||||
VECTOR_FOR_EACH(const DevMap, CaptureDevices, AppendCaptureDeviceList2);
|
||||
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, al_string *outnames)
|
||||
static void ALCjackBackendFactory_probe(ALCjackBackendFactory* UNUSED(self), enum DevProbe type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case ALL_DEVICE_PROBE:
|
||||
alstr_append_range(outnames, jackDevice, jackDevice+sizeof(jackDevice));
|
||||
AppendAllDevicesList(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, al_string *outnames);
|
||||
static void ALCloopbackFactory_probe(ALCloopbackFactory *self, enum DevProbe type);
|
||||
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), al_string* UNUSED(outnames))
|
||||
static void ALCloopbackFactory_probe(ALCloopbackFactory* UNUSED(self), enum DevProbe UNUSED(type))
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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, al_string *outnames);
|
||||
static void ALCnullBackendFactory_probe(ALCnullBackendFactory *self, enum DevProbe type);
|
||||
static ALCbackend* ALCnullBackendFactory_createBackend(ALCnullBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
|
||||
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCnullBackendFactory);
|
||||
|
||||
|
|
@ -195,13 +195,14 @@ static ALCboolean ALCnullBackendFactory_querySupport(ALCnullBackendFactory* UNUS
|
|||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
static void ALCnullBackendFactory_probe(ALCnullBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
|
||||
static void ALCnullBackendFactory_probe(ALCnullBackendFactory* UNUSED(self), enum DevProbe type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case ALL_DEVICE_PROBE:
|
||||
AppendAllDevicesList(nullDevice);
|
||||
break;
|
||||
case CAPTURE_DEVICE_PROBE:
|
||||
alstr_append_range(outnames, nullDevice, nullDevice+sizeof(nullDevice));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -206,9 +206,6 @@ 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));
|
||||
|
|
@ -254,16 +251,19 @@ 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);
|
||||
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) &&
|
||||
while(!ATOMIC_LOAD(&self->mKillNow, almemory_order_acquire) &&
|
||||
ATOMIC_LOAD(&device->Connected, almemory_order_acquire))
|
||||
{
|
||||
size_t todo;
|
||||
size_t todo, len0, len1;
|
||||
|
||||
if(ll_ringbuffer_write_space(self->mRing) == 0)
|
||||
{
|
||||
|
|
@ -292,34 +292,35 @@ 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);
|
||||
|
||||
for(size_t i = 0;i < todo;i++)
|
||||
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++)
|
||||
{
|
||||
if(!data[0].len)
|
||||
{
|
||||
data[0] = data[1];
|
||||
data[1].buf = NULL;
|
||||
data[1].len = 0;
|
||||
}
|
||||
|
||||
result = VCALL(bufferQueue,Enqueue)(data[0].buf, device->UpdateSize*self->mFrameSize);
|
||||
PRINTERR(result, "bufferQueue->Enqueue");
|
||||
if(SL_RESULT_SUCCESS != result)
|
||||
{
|
||||
aluHandleDisconnect(device, "Failed to queue audio: 0x%08x", result);
|
||||
break;
|
||||
}
|
||||
if(SL_RESULT_SUCCESS == result)
|
||||
ll_ringbuffer_write_advance(self->mRing, 1);
|
||||
|
||||
data[0].len--;
|
||||
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++)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
ALCopenslPlayback_unlock(self);
|
||||
|
||||
|
|
@ -391,24 +392,19 @@ 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 0
|
||||
if(!(device->Flags&DEVICE_FREQUENCY_REQUEST))
|
||||
if(!(device->Flags&DEVICE_FREQUENCY_REQUEST) && (env=Android_GetJNIEnv()) != NULL)
|
||||
{
|
||||
/* FIXME: Disabled until I figure out how to get the Context needed for
|
||||
* the getSystemService call.
|
||||
*/
|
||||
JNIEnv *env = Android_GetJNIEnv();
|
||||
jobject jctx = Android_GetContext();
|
||||
|
||||
#if 0
|
||||
/* Get necessary stuff for using java.lang.Integer,
|
||||
* android.content.Context, and android.media.AudioManager.
|
||||
*/
|
||||
|
|
@ -444,7 +440,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)(jctx, ctx_getSysSvc, strobj);
|
||||
jobject audMgr = JCALL(env,CallObjectMethod)(ctx_cls, ctx_getSysSvc, strobj);
|
||||
strchars = JCALL(env,GetStringUTFChars)(strobj, NULL);
|
||||
TRACE("Context.getSystemService(%s) = %p\n", strchars, audMgr);
|
||||
JCALL(env,ReleaseStringUTFChars)(strobj, strchars);
|
||||
|
|
@ -465,8 +461,8 @@ static ALCboolean ALCopenslPlayback_reset(ALCopenslPlayback *self)
|
|||
|
||||
if(!sampleRate) sampleRate = device->Frequency;
|
||||
else sampleRate = maxu(sampleRate, MIN_OUTPUT_RATE);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if(sampleRate != device->Frequency)
|
||||
{
|
||||
|
|
@ -550,18 +546,6 @@ 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)
|
||||
{
|
||||
|
|
@ -577,10 +561,13 @@ 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_reset(self->mRing);
|
||||
ll_ringbuffer_free(self->mRing);
|
||||
self->mRing = ll_ringbuffer_create(device->NumUpdates, self->mFrameSize*device->UpdateSize,
|
||||
true);
|
||||
|
||||
result = VCALL(self->mBufferQueueObj,GetInterface)(SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
|
||||
&bufferQueue);
|
||||
|
|
@ -647,6 +634,9 @@ 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)
|
||||
|
|
@ -723,6 +713,9 @@ 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;
|
||||
|
|
@ -732,9 +725,6 @@ 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));
|
||||
}
|
||||
|
||||
|
|
@ -853,9 +843,8 @@ 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);
|
||||
|
|
@ -952,16 +941,14 @@ 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);
|
||||
|
|
@ -974,11 +961,7 @@ static ALCenum ALCopenslCapture_captureSamples(ALCopenslCapture *self, ALCvoid *
|
|||
{
|
||||
/* Finished a chunk, reset the offset and advance the read pointer. */
|
||||
self->mSplOffset = 0;
|
||||
|
||||
ll_ringbuffer_read_advance(self->mRing, 1);
|
||||
result = VCALL(bufferQueue,Enqueue)(data[0].buf, chunk_size);
|
||||
PRINTERR(result, "bufferQueue->Enqueue");
|
||||
if(SL_RESULT_SUCCESS != result) break;
|
||||
advance++;
|
||||
|
||||
data[0].len--;
|
||||
if(!data[0].len)
|
||||
|
|
@ -989,6 +972,24 @@ 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)
|
||||
{
|
||||
|
|
@ -1029,13 +1030,16 @@ static ALCboolean ALCopenslBackendFactory_querySupport(ALCopenslBackendFactory*
|
|||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
static void ALCopenslBackendFactory_probe(ALCopenslBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
|
||||
static void ALCopenslBackendFactory_probe(ALCopenslBackendFactory* UNUSED(self), enum DevProbe type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case ALL_DEVICE_PROBE:
|
||||
AppendAllDevicesList(opensl_device);
|
||||
break;
|
||||
|
||||
case CAPTURE_DEVICE_PROBE:
|
||||
alstr_append_range(outnames, opensl_device, opensl_device+sizeof(opensl_device));
|
||||
AppendAllDevicesList(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, al_string *outnames);
|
||||
static void ALCossBackendFactory_probe(ALCossBackendFactory *self, enum DevProbe type);
|
||||
static ALCbackend* ALCossBackendFactory_createBackend(ALCossBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
|
||||
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCossBackendFactory);
|
||||
|
||||
|
|
@ -820,31 +820,40 @@ ALCboolean ALCossBackendFactory_querySupport(ALCossBackendFactory* UNUSED(self),
|
|||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
void ALCossBackendFactory_probe(ALCossBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
|
||||
void ALCossBackendFactory_probe(ALCossBackendFactory* UNUSED(self), enum DevProbe type)
|
||||
{
|
||||
struct oss_device *cur = NULL;
|
||||
struct oss_device *cur;
|
||||
switch(type)
|
||||
{
|
||||
case ALL_DEVICE_PROBE:
|
||||
ALCossListFree(&oss_playback);
|
||||
ALCossListPopulate(&oss_playback, DSP_CAP_OUTPUT);
|
||||
cur = &oss_playback;
|
||||
while(cur != NULL)
|
||||
{
|
||||
#ifdef HAVE_STAT
|
||||
struct stat buf;
|
||||
if(stat(cur->path, &buf) == 0)
|
||||
#endif
|
||||
AppendAllDevicesList(cur->handle);
|
||||
cur = cur->next;
|
||||
}
|
||||
break;
|
||||
|
||||
case CAPTURE_DEVICE_PROBE:
|
||||
ALCossListFree(&oss_capture);
|
||||
ALCossListPopulate(&oss_capture, DSP_CAP_INPUT);
|
||||
cur = &oss_capture;
|
||||
break;
|
||||
}
|
||||
while(cur != NULL)
|
||||
{
|
||||
while(cur != NULL)
|
||||
{
|
||||
#ifdef HAVE_STAT
|
||||
struct stat buf;
|
||||
if(stat(cur->path, &buf) == 0)
|
||||
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;
|
||||
AppendCaptureDeviceList(cur->handle);
|
||||
cur = cur->next;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -484,8 +484,9 @@ 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, al_string *outnames);
|
||||
static void ALCportBackendFactory_probe(ALCportBackendFactory *self, enum DevProbe type);
|
||||
static ALCbackend* ALCportBackendFactory_createBackend(ALCportBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
|
||||
|
||||
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCportBackendFactory);
|
||||
|
||||
|
||||
|
|
@ -517,13 +518,15 @@ static ALCboolean ALCportBackendFactory_querySupport(ALCportBackendFactory* UNUS
|
|||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
static void ALCportBackendFactory_probe(ALCportBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
|
||||
static void ALCportBackendFactory_probe(ALCportBackendFactory* UNUSED(self), enum DevProbe type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case ALL_DEVICE_PROBE:
|
||||
AppendAllDevicesList(pa_device);
|
||||
break;
|
||||
case CAPTURE_DEVICE_PROBE:
|
||||
alstr_append_range(outnames, pa_device, pa_device+sizeof(pa_device));
|
||||
AppendCaptureDeviceList(pa_device);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1760,8 +1760,9 @@ 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, al_string *outnames);
|
||||
static void ALCpulseBackendFactory_probe(ALCpulseBackendFactory *self, enum DevProbe type);
|
||||
static ALCbackend* ALCpulseBackendFactory_createBackend(ALCpulseBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
|
||||
|
||||
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCpulseBackendFactory);
|
||||
|
||||
|
||||
|
|
@ -1834,25 +1835,23 @@ static ALCboolean ALCpulseBackendFactory_querySupport(ALCpulseBackendFactory* UN
|
|||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
static void ALCpulseBackendFactory_probe(ALCpulseBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
|
||||
static void ALCpulseBackendFactory_probe(ALCpulseBackendFactory* UNUSED(self), enum DevProbe type)
|
||||
{
|
||||
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();
|
||||
VECTOR_FOR_EACH(const DevMap, PlaybackDevices, APPEND_OUTNAME);
|
||||
#define APPEND_ALL_DEVICES_LIST(e) AppendAllDevicesList(alstr_get_cstr((e)->name))
|
||||
VECTOR_FOR_EACH(const DevMap, PlaybackDevices, APPEND_ALL_DEVICES_LIST);
|
||||
#undef APPEND_ALL_DEVICES_LIST
|
||||
break;
|
||||
|
||||
case CAPTURE_DEVICE_PROBE:
|
||||
ALCpulseCapture_probeDevices();
|
||||
VECTOR_FOR_EACH(const DevMap, CaptureDevices, APPEND_OUTNAME);
|
||||
#define APPEND_CAPTURE_DEVICE_LIST(e) AppendCaptureDeviceList(alstr_get_cstr((e)->name))
|
||||
VECTOR_FOR_EACH(const DevMap, CaptureDevices, APPEND_CAPTURE_DEVICE_LIST);
|
||||
#undef APPEND_CAPTURE_DEVICE_LIST
|
||||
break;
|
||||
#undef APPEND_OUTNAME
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1900,7 +1899,7 @@ static ALCboolean ALCpulseBackendFactory_querySupport(ALCpulseBackendFactory* UN
|
|||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
static void ALCpulseBackendFactory_probe(ALCpulseBackendFactory* UNUSED(self), enum DevProbe UNUSED(type), al_string* UNUSED(outnames))
|
||||
static void ALCpulseBackendFactory_probe(ALCpulseBackendFactory* UNUSED(self), enum DevProbe UNUSED(type))
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -119,9 +119,6 @@ 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);
|
||||
|
|
@ -992,7 +989,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, al_string *outnames);
|
||||
static void ALCqsaBackendFactory_probe(ALCqsaBackendFactory* UNUSED(self), enum DevProbe type);
|
||||
static ALCbackend* ALCqsaBackendFactory_createBackend(ALCqsaBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type);
|
||||
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCqsaBackendFactory);
|
||||
|
||||
|
|
@ -1019,25 +1016,33 @@ static ALCboolean ALCqsaBackendFactory_querySupport(ALCqsaBackendFactory* UNUSED
|
|||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
static void ALCqsaBackendFactory_probe(ALCqsaBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
|
||||
static void ALCqsaBackendFactory_probe(ALCqsaBackendFactory* UNUSED(self), enum DevProbe type)
|
||||
{
|
||||
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);
|
||||
VECTOR_FOR_EACH(const DevMap, DeviceNameMap, APPEND_OUTNAME);
|
||||
#define APPEND_DEVICE(iter) AppendAllDevicesList((iter)->name)
|
||||
VECTOR_FOR_EACH(const DevMap, DeviceNameMap, APPEND_DEVICE);
|
||||
#undef APPEND_DEVICE
|
||||
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);
|
||||
VECTOR_FOR_EACH(const DevMap, CaptureNameMap, APPEND_OUTNAME);
|
||||
#define APPEND_DEVICE(iter) AppendCaptureDeviceList((iter)->name)
|
||||
VECTOR_FOR_EACH(const DevMap, CaptureNameMap, APPEND_DEVICE);
|
||||
#undef APPEND_DEVICE
|
||||
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, al_string *outnames);
|
||||
static void ALCsdl2BackendFactory_probe(ALCsdl2BackendFactory *self, enum DevProbe type);
|
||||
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, al_string *outnames)
|
||||
static void ALCsdl2BackendFactory_probe(ALCsdl2BackendFactory* UNUSED(self), enum DevProbe type)
|
||||
{
|
||||
int num_devices, i;
|
||||
al_string name;
|
||||
|
|
@ -263,13 +263,12 @@ static void ALCsdl2BackendFactory_probe(ALCsdl2BackendFactory* UNUSED(self), enu
|
|||
AL_STRING_INIT(name);
|
||||
num_devices = SDL_GetNumAudioDevices(SDL_FALSE);
|
||||
|
||||
alstr_append_range(outnames, defaultDeviceName, defaultDeviceName+sizeof(defaultDeviceName));
|
||||
AppendAllDevicesList(defaultDeviceName);
|
||||
for(i = 0;i < num_devices;++i)
|
||||
{
|
||||
alstr_copy_cstr(&name, DEVNAME_PREFIX);
|
||||
alstr_append_cstr(&name, SDL_GetAudioDeviceName(i, SDL_FALSE));
|
||||
if(!alstr_empty(name))
|
||||
alstr_append_range(outnames, VECTOR_BEGIN(name), VECTOR_END(name)+1);
|
||||
AppendAllDevicesList(alstr_get_cstr(name));
|
||||
}
|
||||
alstr_reset(&name);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,17 +27,15 @@
|
|||
#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 SndioPlayback {
|
||||
typedef struct ALCsndioBackend {
|
||||
DERIVE_FROM_TYPE(ALCbackend);
|
||||
|
||||
struct sio_hdl *sndHandle;
|
||||
|
|
@ -47,37 +45,40 @@ typedef struct SndioPlayback {
|
|||
|
||||
ATOMIC(int) killNow;
|
||||
althrd_t thread;
|
||||
} SndioPlayback;
|
||||
} ALCsndioBackend;
|
||||
|
||||
static int SndioPlayback_mixerProc(void *ptr);
|
||||
static int ALCsndioBackend_mixerProc(void *ptr);
|
||||
|
||||
static void SndioPlayback_Construct(SndioPlayback *self, ALCdevice *device);
|
||||
static void SndioPlayback_Destruct(SndioPlayback *self);
|
||||
static ALCenum SndioPlayback_open(SndioPlayback *self, const ALCchar *name);
|
||||
static ALCboolean SndioPlayback_reset(SndioPlayback *self);
|
||||
static ALCboolean SndioPlayback_start(SndioPlayback *self);
|
||||
static void SndioPlayback_stop(SndioPlayback *self);
|
||||
static DECLARE_FORWARD2(SndioPlayback, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
|
||||
static DECLARE_FORWARD(SndioPlayback, ALCbackend, ALCuint, availableSamples)
|
||||
static DECLARE_FORWARD(SndioPlayback, ALCbackend, ClockLatency, getClockLatency)
|
||||
static DECLARE_FORWARD(SndioPlayback, ALCbackend, void, lock)
|
||||
static DECLARE_FORWARD(SndioPlayback, ALCbackend, void, unlock)
|
||||
DECLARE_DEFAULT_ALLOCATORS(SndioPlayback)
|
||||
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)
|
||||
|
||||
DEFINE_ALCBACKEND_VTABLE(SndioPlayback);
|
||||
DEFINE_ALCBACKEND_VTABLE(ALCsndioBackend);
|
||||
|
||||
|
||||
static void SndioPlayback_Construct(SndioPlayback *self, ALCdevice *device)
|
||||
static const ALCchar sndio_device[] = "SndIO Default";
|
||||
|
||||
|
||||
static void ALCsndioBackend_Construct(ALCsndioBackend *self, ALCdevice *device)
|
||||
{
|
||||
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
|
||||
SET_VTABLE2(SndioPlayback, ALCbackend, self);
|
||||
SET_VTABLE2(ALCsndioBackend, ALCbackend, self);
|
||||
|
||||
self->sndHandle = NULL;
|
||||
self->mix_data = NULL;
|
||||
ATOMIC_INIT(&self->killNow, AL_TRUE);
|
||||
}
|
||||
|
||||
static void SndioPlayback_Destruct(SndioPlayback *self)
|
||||
static void ALCsndioBackend_Destruct(ALCsndioBackend *self)
|
||||
{
|
||||
if(self->sndHandle)
|
||||
sio_close(self->sndHandle);
|
||||
|
|
@ -90,9 +91,9 @@ static void SndioPlayback_Destruct(SndioPlayback *self)
|
|||
}
|
||||
|
||||
|
||||
static int SndioPlayback_mixerProc(void *ptr)
|
||||
static int ALCsndioBackend_mixerProc(void *ptr)
|
||||
{
|
||||
SndioPlayback *self = (SndioPlayback*)ptr;
|
||||
ALCsndioBackend *self = (ALCsndioBackend*)ptr;
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
ALsizei frameSize;
|
||||
size_t wrote;
|
||||
|
|
@ -108,9 +109,9 @@ static int SndioPlayback_mixerProc(void *ptr)
|
|||
ALsizei len = self->data_size;
|
||||
ALubyte *WritePtr = self->mix_data;
|
||||
|
||||
SndioPlayback_lock(self);
|
||||
ALCsndioBackend_lock(self);
|
||||
aluMixData(device, WritePtr, len/frameSize);
|
||||
SndioPlayback_unlock(self);
|
||||
ALCsndioBackend_unlock(self);
|
||||
while(len > 0 && !ATOMIC_LOAD(&self->killNow, almemory_order_acquire))
|
||||
{
|
||||
wrote = sio_write(self->sndHandle, WritePtr, len);
|
||||
|
|
@ -132,7 +133,7 @@ static int SndioPlayback_mixerProc(void *ptr)
|
|||
}
|
||||
|
||||
|
||||
static ALCenum SndioPlayback_open(SndioPlayback *self, const ALCchar *name)
|
||||
static ALCenum ALCsndioBackend_open(ALCsndioBackend *self, const ALCchar *name)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
|
||||
|
||||
|
|
@ -153,7 +154,7 @@ static ALCenum SndioPlayback_open(SndioPlayback *self, const ALCchar *name)
|
|||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static ALCboolean SndioPlayback_reset(SndioPlayback *self)
|
||||
static ALCboolean ALCsndioBackend_reset(ALCsndioBackend *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
|
||||
struct sio_par par;
|
||||
|
|
@ -238,7 +239,7 @@ static ALCboolean SndioPlayback_reset(SndioPlayback *self)
|
|||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static ALCboolean SndioPlayback_start(SndioPlayback *self)
|
||||
static ALCboolean ALCsndioBackend_start(ALCsndioBackend *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
|
||||
|
||||
|
|
@ -255,7 +256,7 @@ static ALCboolean SndioPlayback_start(SndioPlayback *self)
|
|||
}
|
||||
|
||||
ATOMIC_STORE(&self->killNow, AL_FALSE, almemory_order_release);
|
||||
if(althrd_create(&self->thread, SndioPlayback_mixerProc, self) != althrd_success)
|
||||
if(althrd_create(&self->thread, ALCsndioBackend_mixerProc, self) != althrd_success)
|
||||
{
|
||||
sio_stop(self->sndHandle);
|
||||
return ALC_FALSE;
|
||||
|
|
@ -264,7 +265,7 @@ static ALCboolean SndioPlayback_start(SndioPlayback *self)
|
|||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static void SndioPlayback_stop(SndioPlayback *self)
|
||||
static void ALCsndioBackend_stop(ALCsndioBackend *self)
|
||||
{
|
||||
int res;
|
||||
|
||||
|
|
@ -280,318 +281,59 @@ static void SndioPlayback_stop(SndioPlayback *self)
|
|||
}
|
||||
|
||||
|
||||
typedef struct SndioCapture {
|
||||
DERIVE_FROM_TYPE(ALCbackend);
|
||||
|
||||
struct sio_hdl *sndHandle;
|
||||
|
||||
ll_ringbuffer_t *ring;
|
||||
|
||||
ATOMIC(int) killNow;
|
||||
althrd_t thread;
|
||||
} SndioCapture;
|
||||
|
||||
static int SndioCapture_recordProc(void *ptr);
|
||||
|
||||
static void SndioCapture_Construct(SndioCapture *self, ALCdevice *device);
|
||||
static void SndioCapture_Destruct(SndioCapture *self);
|
||||
static ALCenum SndioCapture_open(SndioCapture *self, const ALCchar *name);
|
||||
static DECLARE_FORWARD(SndioCapture, ALCbackend, ALCboolean, reset)
|
||||
static ALCboolean SndioCapture_start(SndioCapture *self);
|
||||
static void SndioCapture_stop(SndioCapture *self);
|
||||
static ALCenum SndioCapture_captureSamples(SndioCapture *self, void *buffer, ALCuint samples);
|
||||
static ALCuint SndioCapture_availableSamples(SndioCapture *self);
|
||||
static DECLARE_FORWARD(SndioCapture, ALCbackend, ClockLatency, getClockLatency)
|
||||
static DECLARE_FORWARD(SndioCapture, ALCbackend, void, lock)
|
||||
static DECLARE_FORWARD(SndioCapture, ALCbackend, void, unlock)
|
||||
DECLARE_DEFAULT_ALLOCATORS(SndioCapture)
|
||||
|
||||
DEFINE_ALCBACKEND_VTABLE(SndioCapture);
|
||||
|
||||
|
||||
static void SndioCapture_Construct(SndioCapture *self, ALCdevice *device)
|
||||
{
|
||||
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
|
||||
SET_VTABLE2(SndioCapture, ALCbackend, self);
|
||||
|
||||
self->sndHandle = NULL;
|
||||
self->ring = NULL;
|
||||
ATOMIC_INIT(&self->killNow, AL_TRUE);
|
||||
}
|
||||
|
||||
static void SndioCapture_Destruct(SndioCapture *self)
|
||||
{
|
||||
if(self->sndHandle)
|
||||
sio_close(self->sndHandle);
|
||||
self->sndHandle = NULL;
|
||||
|
||||
ll_ringbuffer_free(self->ring);
|
||||
self->ring = NULL;
|
||||
|
||||
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
|
||||
}
|
||||
|
||||
|
||||
static 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 {
|
||||
typedef struct ALCsndioBackendFactory {
|
||||
DERIVE_FROM_TYPE(ALCbackendFactory);
|
||||
} SndioBackendFactory;
|
||||
#define SNDIOBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(SndioBackendFactory, ALCbackendFactory) } }
|
||||
} ALCsndioBackendFactory;
|
||||
#define ALCSNDIOBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCsndioBackendFactory, ALCbackendFactory) } }
|
||||
|
||||
ALCbackendFactory *SndioBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCsndioBackendFactory_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);
|
||||
static ALCboolean ALCsndioBackendFactory_init(ALCsndioBackendFactory *self);
|
||||
static DECLARE_FORWARD(ALCsndioBackendFactory, ALCbackendFactory, void, deinit)
|
||||
static ALCboolean ALCsndioBackendFactory_querySupport(ALCsndioBackendFactory *self, ALCbackend_Type type);
|
||||
static void ALCsndioBackendFactory_probe(ALCsndioBackendFactory *self, enum DevProbe type);
|
||||
static ALCbackend* ALCsndioBackendFactory_createBackend(ALCsndioBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
|
||||
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCsndioBackendFactory);
|
||||
|
||||
ALCbackendFactory *SndioBackendFactory_getFactory(void)
|
||||
|
||||
ALCbackendFactory *ALCsndioBackendFactory_getFactory(void)
|
||||
{
|
||||
static SndioBackendFactory factory = SNDIOBACKENDFACTORY_INITIALIZER;
|
||||
static ALCsndioBackendFactory factory = ALCSNDIOBACKENDFACTORY_INITIALIZER;
|
||||
return STATIC_CAST(ALCbackendFactory, &factory);
|
||||
}
|
||||
|
||||
static ALCboolean SndioBackendFactory_init(SndioBackendFactory* UNUSED(self))
|
||||
|
||||
static ALCboolean ALCsndioBackendFactory_init(ALCsndioBackendFactory* UNUSED(self))
|
||||
{
|
||||
/* No dynamic loading */
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static ALCboolean SndioBackendFactory_querySupport(SndioBackendFactory* UNUSED(self), ALCbackend_Type type)
|
||||
static ALCboolean ALCsndioBackendFactory_querySupport(ALCsndioBackendFactory* UNUSED(self), ALCbackend_Type type)
|
||||
{
|
||||
if(type == ALCbackend_Playback || type == ALCbackend_Capture)
|
||||
if(type == ALCbackend_Playback)
|
||||
return ALC_TRUE;
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
static void SndioBackendFactory_probe(SndioBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
|
||||
static void ALCsndioBackendFactory_probe(ALCsndioBackendFactory* UNUSED(self), enum DevProbe type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case ALL_DEVICE_PROBE:
|
||||
AppendAllDevicesList(sndio_device);
|
||||
break;
|
||||
case CAPTURE_DEVICE_PROBE:
|
||||
alstr_append_range(outnames, sndio_device, sndio_device+sizeof(sndio_device));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static ALCbackend* SndioBackendFactory_createBackend(SndioBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
|
||||
static ALCbackend* ALCsndioBackendFactory_createBackend(ALCsndioBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
|
||||
{
|
||||
if(type == ALCbackend_Playback)
|
||||
{
|
||||
SndioPlayback *backend;
|
||||
NEW_OBJ(backend, SndioPlayback)(device);
|
||||
if(!backend) return NULL;
|
||||
return STATIC_CAST(ALCbackend, backend);
|
||||
}
|
||||
if(type == ALCbackend_Capture)
|
||||
{
|
||||
SndioCapture *backend;
|
||||
NEW_OBJ(backend, SndioCapture)(device);
|
||||
ALCsndioBackend *backend;
|
||||
NEW_OBJ(backend, ALCsndioBackend)(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, al_string *outnames);
|
||||
static void ALCsolarisBackendFactory_probe(ALCsolarisBackendFactory *self, enum DevProbe type);
|
||||
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, al_string *outnames)
|
||||
static void ALCsolarisBackendFactory_probe(ALCsolarisBackendFactory* UNUSED(self), enum DevProbe type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
|
|
@ -337,7 +337,7 @@ static void ALCsolarisBackendFactory_probe(ALCsolarisBackendFactory* UNUSED(self
|
|||
struct stat buf;
|
||||
if(stat(solaris_driver, &buf) == 0)
|
||||
#endif
|
||||
alstr_append_range(outnames, solaris_device, solaris_device+sizeof(solaris_device));
|
||||
AppendAllDevicesList(solaris_device);
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
|
|||
|
|
@ -1919,6 +1919,11 @@ 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;
|
||||
|
|
@ -1927,7 +1932,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, al_string *outnames);
|
||||
static void ALCwasapiBackendFactory_probe(ALCwasapiBackendFactory *self, enum DevProbe type);
|
||||
static ALCbackend* ALCwasapiBackendFactory_createBackend(ALCwasapiBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
|
||||
|
||||
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCwasapiBackendFactory);
|
||||
|
|
@ -1984,7 +1989,7 @@ static ALCboolean ALCwasapiBackendFactory_querySupport(ALCwasapiBackendFactory*
|
|||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
static void ALCwasapiBackendFactory_probe(ALCwasapiBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
|
||||
static void ALCwasapiBackendFactory_probe(ALCwasapiBackendFactory* UNUSED(self), enum DevProbe type)
|
||||
{
|
||||
ThreadRequest req = { NULL, 0 };
|
||||
|
||||
|
|
@ -1998,19 +2003,13 @@ 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, APPEND_OUTNAME);
|
||||
VECTOR_FOR_EACH(const DevMap, PlaybackDevices, AppendAllDevicesList2);
|
||||
break;
|
||||
|
||||
case CAPTURE_DEVICE_PROBE:
|
||||
VECTOR_FOR_EACH(const DevMap, CaptureDevices, APPEND_OUTNAME);
|
||||
VECTOR_FOR_EACH(const DevMap, CaptureDevices, AppendCaptureDeviceList2);
|
||||
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, al_string *outnames);
|
||||
static void ALCwaveBackendFactory_probe(ALCwaveBackendFactory *self, enum DevProbe type);
|
||||
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, al_string *outnames)
|
||||
static void ALCwaveBackendFactory_probe(ALCwaveBackendFactory* UNUSED(self), enum DevProbe type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case ALL_DEVICE_PROBE:
|
||||
alstr_append_range(outnames, waveDevice, waveDevice+sizeof(waveDevice));
|
||||
AppendAllDevicesList(waveDevice);
|
||||
break;
|
||||
case CAPTURE_DEVICE_PROBE:
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -700,6 +700,17 @@ 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;
|
||||
|
|
@ -708,7 +719,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, al_string *outnames);
|
||||
static void ALCwinmmBackendFactory_probe(ALCwinmmBackendFactory *self, enum DevProbe type);
|
||||
static ALCbackend* ALCwinmmBackendFactory_createBackend(ALCwinmmBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
|
||||
|
||||
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCwinmmBackendFactory);
|
||||
|
|
@ -738,24 +749,19 @@ static ALCboolean ALCwinmmBackendFactory_querySupport(ALCwinmmBackendFactory* UN
|
|||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
static void ALCwinmmBackendFactory_probe(ALCwinmmBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
|
||||
static void ALCwinmmBackendFactory_probe(ALCwinmmBackendFactory* UNUSED(self), enum DevProbe type)
|
||||
{
|
||||
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, APPEND_OUTNAME);
|
||||
VECTOR_FOR_EACH(const al_string, PlaybackDevices, AppendAllDevicesList2);
|
||||
break;
|
||||
|
||||
case CAPTURE_DEVICE_PROBE:
|
||||
ProbeCaptureDevices();
|
||||
VECTOR_FOR_EACH(const al_string, CaptureDevices, APPEND_OUTNAME);
|
||||
VECTOR_FOR_EACH(const al_string, CaptureDevices, AppendCaptureDeviceList2);
|
||||
break;
|
||||
#undef APPEND_OUTNAME
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -438,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);
|
||||
ComputePanGains(&device->Dry, coeffs, 1.0f, encgains[k]);
|
||||
ComputeDryPanGains(&device->Dry, coeffs, 1.0f, encgains[k]);
|
||||
}
|
||||
|
||||
/* Combine the matrices that do the in->virt and virt->out conversions
|
||||
|
|
@ -450,11 +450,11 @@ void ambiup_reset(struct AmbiUpsampler *ambiup, const ALCdevice *device, ALfloat
|
|||
{
|
||||
for(j = 0;j < device->Dry.NumChannels;j++)
|
||||
{
|
||||
ALdouble gain = 0.0;
|
||||
ALfloat gain=0.0f;
|
||||
for(k = 0;k < COUNTOF(Ambi3DDecoder);k++)
|
||||
gain += (ALdouble)Ambi3DDecoder[k][i] * encgains[k][j];
|
||||
ambiup->Gains[i][j][HF_BAND] = (ALfloat)(gain * Ambi3DDecoderHFScale[i]);
|
||||
ambiup->Gains[i][j][LF_BAND] = (ALfloat)gain;
|
||||
gain += Ambi3DDecoder[k][i] * encgains[k][j];
|
||||
ambiup->Gains[i][j][HF_BAND] = gain * Ambi3DDecoderHFScale[i];
|
||||
ambiup->Gains[i][j][LF_BAND] = gain;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,6 +50,14 @@ 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
|
||||
|
|
|
|||
|
|
@ -1,321 +0,0 @@
|
|||
/**
|
||||
* 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);
|
||||
|
|
@ -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);
|
||||
ComputePanGains(&device->Dry, coeffs, Slot->Params.Gain, state->Gains[0].Target);
|
||||
ComputeDryPanGains(&device->Dry, coeffs, Slot->Params.Gain, state->Gains[0].Target);
|
||||
CalcAngleCoeffs( F_PI_2, 0.0f, 0.0f, coeffs);
|
||||
ComputePanGains(&device->Dry, coeffs, Slot->Params.Gain, state->Gains[1].Target);
|
||||
ComputeDryPanGains(&device->Dry, coeffs, Slot->Params.Gain, state->Gains[1].Target);
|
||||
|
||||
phase = props->Chorus.Phase;
|
||||
rate = props->Chorus.Rate;
|
||||
|
|
|
|||
|
|
@ -27,13 +27,6 @@
|
|||
#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);
|
||||
|
||||
|
|
@ -42,9 +35,9 @@ typedef struct ALcompressorState {
|
|||
|
||||
/* Effect parameters */
|
||||
ALboolean Enabled;
|
||||
ALfloat AttackMult;
|
||||
ALfloat ReleaseMult;
|
||||
ALfloat EnvFollower;
|
||||
ALfloat AttackRate;
|
||||
ALfloat ReleaseRate;
|
||||
ALfloat GainCtrl;
|
||||
} ALcompressorState;
|
||||
|
||||
static ALvoid ALcompressorState_Destruct(ALcompressorState *state);
|
||||
|
|
@ -62,9 +55,9 @@ static void ALcompressorState_Construct(ALcompressorState *state)
|
|||
SET_VTABLE2(ALcompressorState, ALeffectState, state);
|
||||
|
||||
state->Enabled = AL_TRUE;
|
||||
state->AttackMult = 1.0f;
|
||||
state->ReleaseMult = 1.0f;
|
||||
state->EnvFollower = 1.0f;
|
||||
state->AttackRate = 0.0f;
|
||||
state->ReleaseRate = 0.0f;
|
||||
state->GainCtrl = 1.0f;
|
||||
}
|
||||
|
||||
static ALvoid ALcompressorState_Destruct(ALcompressorState *state)
|
||||
|
|
@ -74,17 +67,11 @@ static ALvoid ALcompressorState_Destruct(ALcompressorState *state)
|
|||
|
||||
static ALboolean ALcompressorState_deviceUpdate(ALcompressorState *state, ALCdevice *device)
|
||||
{
|
||||
/* 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;
|
||||
const ALfloat attackTime = device->Frequency * 0.2f; /* 200ms Attack */
|
||||
const ALfloat releaseTime = device->Frequency * 0.4f; /* 400ms Release */
|
||||
|
||||
/* 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);
|
||||
state->AttackRate = 1.0f / attackTime;
|
||||
state->ReleaseRate = 1.0f / releaseTime;
|
||||
|
||||
return AL_TRUE;
|
||||
}
|
||||
|
|
@ -99,7 +86,8 @@ 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++)
|
||||
ComputePanGains(&device->FOAOut, IdentityMatrixf.m[i], slot->Params.Gain, state->Gain[i]);
|
||||
ComputeFirstOrderGains(&device->FOAOut, IdentityMatrixf.m[i],
|
||||
slot->Params.Gain, state->Gain[i]);
|
||||
}
|
||||
|
||||
static ALvoid ALcompressorState_process(ALcompressorState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
|
||||
|
|
@ -109,52 +97,71 @@ static ALvoid ALcompressorState_process(ALcompressorState *state, ALsizei Sample
|
|||
|
||||
for(base = 0;base < SamplesToDo;)
|
||||
{
|
||||
ALfloat gains[256];
|
||||
ALsizei td = mini(256, SamplesToDo-base);
|
||||
ALfloat env = state->EnvFollower;
|
||||
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];
|
||||
}
|
||||
|
||||
/* Generate the per-sample gains from the signal envelope. */
|
||||
if(state->Enabled)
|
||||
{
|
||||
for(i = 0;i < td;++i)
|
||||
{
|
||||
/* Clamp the absolute amplitude to the defined envelope limits,
|
||||
* then attack or release the envelope to reach it.
|
||||
*/
|
||||
ALfloat amplitude = clampf(fabsf(SamplesIn[0][base+i]),
|
||||
AMP_ENVELOPE_MIN, AMP_ENVELOPE_MAX);
|
||||
if(amplitude > env)
|
||||
env = minf(env*state->AttackMult, amplitude);
|
||||
else if(amplitude < env)
|
||||
env = maxf(env*state->ReleaseMult, amplitude);
|
||||
ALfloat gain = state->GainCtrl;
|
||||
ALfloat output, amplitude;
|
||||
|
||||
/* Apply the reciprocal of the envelope to normalize the volume
|
||||
* (compress the dynamic range).
|
||||
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.
|
||||
*/
|
||||
gains[i] = 1.0f / env;
|
||||
amplitude = fabsf(temps[i][0]);
|
||||
amplitude = maxf(amplitude + fabsf(temps[i][1]),
|
||||
maxf(amplitude + fabsf(temps[i][2]),
|
||||
amplitude + fabsf(temps[i][3])));
|
||||
if(amplitude > gain)
|
||||
gain = minf(gain+state->AttackRate, amplitude);
|
||||
else if(amplitude < gain)
|
||||
gain = maxf(gain-state->ReleaseRate, amplitude);
|
||||
|
||||
/* Apply the inverse of the gain control to normalize/compress
|
||||
* the volume. */
|
||||
output = 1.0f / clampf(gain, 0.5f, 2.0f);
|
||||
for(j = 0;j < 4;j++)
|
||||
temps[i][j] *= output;
|
||||
}
|
||||
|
||||
state->GainCtrl = gain;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* 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)
|
||||
ALfloat gain = state->GainCtrl;
|
||||
ALfloat output, amplitude;
|
||||
|
||||
for(i = 0;i < td;i++)
|
||||
{
|
||||
ALfloat amplitude = 1.0f;
|
||||
if(amplitude > env)
|
||||
env = minf(env*state->AttackMult, amplitude);
|
||||
else if(amplitude < env)
|
||||
env = maxf(env*state->ReleaseMult, amplitude);
|
||||
/* 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);
|
||||
|
||||
gains[i] = 1.0f / env;
|
||||
output = 1.0f / clampf(gain, 0.5f, 2.0f);
|
||||
for(j = 0;j < 4;j++)
|
||||
temps[i][j] *= output;
|
||||
}
|
||||
}
|
||||
state->EnvFollower = env;
|
||||
|
||||
/* Now compress the signal amplitude to output. */
|
||||
for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
|
||||
state->GainCtrl = gain;
|
||||
}
|
||||
|
||||
/* Now mix to the output. */
|
||||
for(j = 0;j < 4;j++)
|
||||
{
|
||||
for(k = 0;k < NumChannels;k++)
|
||||
{
|
||||
|
|
@ -163,7 +170,7 @@ static ALvoid ALcompressorState_process(ALcompressorState *state, ALsizei Sample
|
|||
continue;
|
||||
|
||||
for(i = 0;i < td;i++)
|
||||
SamplesOut[k][base+i] += SamplesIn[j][base+i] * gains[i] * gain;
|
||||
SamplesOut[k][base+i] += gain * temps[i][j];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
ComputePanGains(&device->Dry, coeffs, Gain, state->TargetGains);
|
||||
ComputeDryPanGains(&device->Dry, coeffs, Gain, state->TargetGains);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -104,7 +104,8 @@ static ALvoid ALdistortionState_update(ALdistortionState *state, const ALCcontex
|
|||
);
|
||||
|
||||
CalcAngleCoeffs(0.0f, 0.0f, 0.0f, coeffs);
|
||||
ComputePanGains(&device->Dry, coeffs, slot->Params.Gain*props->Distortion.Gain, state->Gain);
|
||||
ComputeDryPanGains(&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)
|
||||
|
|
|
|||
|
|
@ -141,11 +141,11 @@ static ALvoid ALechoState_update(ALechoState *state, const ALCcontext *context,
|
|||
|
||||
/* First tap panning */
|
||||
CalcAngleCoeffs(-F_PI_2*lrpan, 0.0f, spread, coeffs);
|
||||
ComputePanGains(&device->Dry, coeffs, slot->Params.Gain, state->Gains[0].Target);
|
||||
ComputeDryPanGains(&device->Dry, coeffs, slot->Params.Gain, state->Gains[0].Target);
|
||||
|
||||
/* Second tap panning */
|
||||
CalcAngleCoeffs( F_PI_2*lrpan, 0.0f, spread, coeffs);
|
||||
ComputePanGains(&device->Dry, coeffs, slot->Params.Gain, state->Gains[1].Target);
|
||||
ComputeDryPanGains(&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)
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
BiquadFilter filter[4];
|
||||
} Chans[MAX_EFFECT_CHANNELS];
|
||||
|
||||
ALfloat SampleBuffer[MAX_EFFECT_CHANNELS][BUFFERSIZE];
|
||||
|
|
@ -128,6 +128,12 @@ 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.
|
||||
|
|
@ -168,12 +174,6 @@ static ALvoid ALequalizerState_update(ALequalizerState *state, const ALCcontext
|
|||
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)
|
||||
|
|
|
|||
|
|
@ -1,329 +0,0 @@
|
|||
/**
|
||||
* 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,6 +40,8 @@ typedef struct ALmodulatorState {
|
|||
ALsizei index;
|
||||
ALsizei step;
|
||||
|
||||
alignas(16) ALfloat ModSamples[MAX_UPDATE_SAMPLES];
|
||||
|
||||
struct {
|
||||
BiquadFilter Filter;
|
||||
|
||||
|
|
@ -63,22 +65,17 @@ DEFINE_ALEFFECTSTATE_VTABLE(ALmodulatorState);
|
|||
|
||||
static inline ALfloat Sin(ALsizei index)
|
||||
{
|
||||
return sinf((ALfloat)index * (F_TAU / WAVEFORM_FRACONE));
|
||||
return sinf(index*(F_TAU/WAVEFORM_FRACONE) - F_PI)*0.5f + 0.5f;
|
||||
}
|
||||
|
||||
static inline ALfloat Saw(ALsizei index)
|
||||
{
|
||||
return (ALfloat)index*(2.0f/WAVEFORM_FRACONE) - 1.0f;
|
||||
return (ALfloat)index / WAVEFORM_FRACONE;
|
||||
}
|
||||
|
||||
static inline ALfloat Square(ALsizei index)
|
||||
{
|
||||
return (ALfloat)(((index>>(WAVEFORM_FRACBITS-2))&2) - 1);
|
||||
}
|
||||
|
||||
static inline ALfloat One(ALsizei UNUSED(index))
|
||||
{
|
||||
return 1.0f;
|
||||
return (ALfloat)((index >> (WAVEFORM_FRACBITS - 1)) & 1);
|
||||
}
|
||||
|
||||
#define DECL_TEMPLATE(func) \
|
||||
|
|
@ -97,7 +94,6 @@ static void Modulate##func(ALfloat *restrict dst, ALsizei index, \
|
|||
DECL_TEMPLATE(Sin)
|
||||
DECL_TEMPLATE(Saw)
|
||||
DECL_TEMPLATE(Square)
|
||||
DECL_TEMPLATE(One)
|
||||
|
||||
#undef DECL_TEMPLATE
|
||||
|
||||
|
|
@ -131,45 +127,47 @@ 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 f0norm;
|
||||
ALfloat cw, a;
|
||||
ALsizei i;
|
||||
|
||||
state->step = fastf2i(props->Modulator.Frequency / (ALfloat)device->Frequency *
|
||||
WAVEFORM_FRACONE);
|
||||
state->step = clampi(state->step, 0, WAVEFORM_FRACONE-1);
|
||||
|
||||
if(state->step == 0)
|
||||
state->GetSamples = ModulateOne;
|
||||
else if(props->Modulator.Waveform == AL_RING_MODULATOR_SINUSOID)
|
||||
if(props->Modulator.Waveform == AL_RING_MODULATOR_SINUSOID)
|
||||
state->GetSamples = ModulateSin;
|
||||
else if(props->Modulator.Waveform == AL_RING_MODULATOR_SAWTOOTH)
|
||||
state->GetSamples = ModulateSaw;
|
||||
else /*if(Slot->Params.EffectProps.Modulator.Waveform == AL_RING_MODULATOR_SQUARE)*/
|
||||
state->GetSamples = ModulateSquare;
|
||||
|
||||
f0norm = props->Modulator.HighPassCutoff / (ALfloat)device->Frequency;
|
||||
f0norm = clampf(f0norm, 1.0f/512.0f, 0.49f);
|
||||
/* Bandwidth value is constant in octaves. */
|
||||
BiquadFilter_setParams(&state->Chans[0].Filter, BiquadType_HighPass, 1.0f,
|
||||
f0norm, calc_rcpQ_from_bandwidth(f0norm, 0.75f));
|
||||
state->step = float2int(props->Modulator.Frequency*WAVEFORM_FRACONE/device->Frequency + 0.5f);
|
||||
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;
|
||||
for(i = 1;i < MAX_EFFECT_CHANNELS;i++)
|
||||
BiquadFilter_copyParams(&state->Chans[i].Filter, &state->Chans[0].Filter);
|
||||
|
||||
STATIC_CAST(ALeffectState,state)->OutBuffer = device->FOAOut.Buffer;
|
||||
STATIC_CAST(ALeffectState,state)->OutChannels = device->FOAOut.NumChannels;
|
||||
for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
|
||||
ComputePanGains(&device->FOAOut, IdentityMatrixf.m[i], slot->Params.Gain,
|
||||
state->Chans[i].TargetGains);
|
||||
ComputeFirstOrderGains(&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 modsamples[MAX_UPDATE_SAMPLES];
|
||||
alignas(16) ALfloat temps[2][MAX_UPDATE_SAMPLES];
|
||||
ALsizei td = mini(MAX_UPDATE_SAMPLES, SamplesToDo-base);
|
||||
ALsizei c, i;
|
||||
|
||||
|
|
@ -179,13 +177,11 @@ static ALvoid ALmodulatorState_process(ALmodulatorState *state, ALsizei SamplesT
|
|||
|
||||
for(c = 0;c < MAX_EFFECT_CHANNELS;c++)
|
||||
{
|
||||
alignas(16) ALfloat temps[MAX_UPDATE_SAMPLES];
|
||||
|
||||
BiquadFilter_process(&state->Chans[c].Filter, temps, &SamplesIn[c][base], td);
|
||||
BiquadFilter_process(&state->Chans[c].Filter, temps[0], &SamplesIn[c][base], td);
|
||||
for(i = 0;i < td;i++)
|
||||
temps[i] *= modsamples[i];
|
||||
temps[1][i] = temps[0][i] * modsamples[i];
|
||||
|
||||
MixSamples(temps, NumChannels, SamplesOut, state->Chans[c].CurrentGains,
|
||||
MixSamples(temps[1], NumChannels, SamplesOut, state->Chans[c].CurrentGains,
|
||||
state->Chans[c].TargetGains, SamplesToDo-base, base, td);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,8 +29,6 @@
|
|||
#include "alu.h"
|
||||
#include "filters/defs.h"
|
||||
|
||||
#include "alcomplex.h"
|
||||
|
||||
|
||||
#define STFT_SIZE 1024
|
||||
#define STFT_HALF_SIZE (STFT_SIZE>>1)
|
||||
|
|
@ -39,6 +37,10 @@
|
|||
#define STFT_STEP (STFT_SIZE / OVERSAMP)
|
||||
#define FIFO_LATENCY (STFT_STEP * (OVERSAMP-1))
|
||||
|
||||
typedef struct ALcomplex {
|
||||
ALdouble Real;
|
||||
ALdouble Imag;
|
||||
} ALcomplex;
|
||||
|
||||
typedef struct ALphasor {
|
||||
ALdouble Amplitude;
|
||||
|
|
@ -50,7 +52,6 @@ typedef struct ALFrequencyDomain {
|
|||
ALdouble Frequency;
|
||||
} ALfrequencyDomain;
|
||||
|
||||
|
||||
typedef struct ALpshifterState {
|
||||
DERIVE_FROM_TYPE(ALeffectState);
|
||||
|
||||
|
|
@ -105,32 +106,26 @@ static void InitHannWindow(void)
|
|||
static alonce_flag HannInitOnce = AL_ONCE_FLAG_INIT;
|
||||
|
||||
|
||||
static inline ALint double2int(ALdouble d)
|
||||
/* Fast double-to-int conversion. Assumes the FPU is already in round-to-zero
|
||||
* mode. */
|
||||
static inline ALint fastd2i(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;
|
||||
|
||||
/* NOTE: SSE2 is required for the efficient double-to-int opcodes on x86.
|
||||
* Otherwise, we need to rely on x87's fistp opcode with it already in
|
||||
* round-to-zero mode. x86-64 guarantees SSE2 support.
|
||||
*/
|
||||
#if (defined(__i386__) && !defined(__SSE2_MATH__)) || (defined(_M_IX86_FP) && (_M_IX86_FP < 2))
|
||||
#ifdef HAVE_LRINTF
|
||||
return lrint(d);
|
||||
#elif defined(_MSC_VER) && defined(_M_IX86)
|
||||
ALint i;
|
||||
__asm fld d
|
||||
__asm fistp i
|
||||
return i;
|
||||
#else
|
||||
return (ALint)d;
|
||||
#endif
|
||||
#else
|
||||
|
||||
return (ALint)d;
|
||||
#endif
|
||||
}
|
||||
|
|
@ -148,7 +143,7 @@ static inline ALphasor rect2polar(ALcomplex number)
|
|||
}
|
||||
|
||||
/* Converts ALphasor to ALcomplex */
|
||||
static inline ALcomplex polar2rect(ALphasor number)
|
||||
static inline ALcomplex polar2rect(ALphasor number)
|
||||
{
|
||||
ALcomplex cartesian;
|
||||
|
||||
|
|
@ -158,6 +153,96 @@ static inline ALcomplex polar2rect(ALphasor number)
|
|||
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, 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 Danielson–Lanczos 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void ALpshifterState_Construct(ALpshifterState *state)
|
||||
{
|
||||
|
|
@ -204,11 +289,11 @@ static ALvoid ALpshifterState_update(ALpshifterState *state, const ALCcontext *c
|
|||
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);
|
||||
state->PitchShiftI = (ALsizei)(pitch*FRACTIONONE + 0.5f);
|
||||
state->PitchShift = state->PitchShiftI * (1.0f/FRACTIONONE);
|
||||
|
||||
CalcAngleCoeffs(0.0f, 0.0f, 0.0f, coeffs);
|
||||
ComputePanGains(&device->Dry, coeffs, slot->Params.Gain, state->TargetGains);
|
||||
ComputeDryPanGains(&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)
|
||||
|
|
@ -246,7 +331,7 @@ static ALvoid ALpshifterState_process(ALpshifterState *state, ALsizei SamplesToD
|
|||
|
||||
/* ANALYSIS */
|
||||
/* Apply FFT to FFTbuffer data */
|
||||
complex_fft(state->FFTbuffer, STFT_SIZE, -1.0);
|
||||
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.
|
||||
|
|
@ -264,7 +349,7 @@ static ALvoid ALpshifterState_process(ALpshifterState *state, ALsizei SamplesToD
|
|||
tmp = (component.Phase - state->LastPhase[k]) - k*expected;
|
||||
|
||||
/* Map delta phase into +/- Pi interval */
|
||||
qpd = double2int(tmp / M_PI);
|
||||
qpd = fastd2i(tmp / M_PI);
|
||||
tmp -= M_PI * (qpd + (qpd%2));
|
||||
|
||||
/* Get deviation from bin frequency from the +/- Pi interval */
|
||||
|
|
@ -326,7 +411,7 @@ static ALvoid ALpshifterState_process(ALpshifterState *state, ALsizei SamplesToD
|
|||
}
|
||||
|
||||
/* Apply iFFT to buffer data */
|
||||
complex_fft(state->FFTbuffer, STFT_SIZE, 1.0);
|
||||
FFT(state->FFTbuffer, STFT_SIZE, 1.0);
|
||||
|
||||
/* Windowing and add to output */
|
||||
for(k = 0;k < STFT_SIZE;k++)
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -125,7 +125,6 @@ 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);
|
||||
|
|
@ -223,32 +222,22 @@ 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;
|
||||
|
||||
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;
|
||||
len = strlen(buf);
|
||||
while(len > 0 && isspace(buf[len-1]))
|
||||
buf[--len] = 0;
|
||||
|
||||
if(!alstr_empty(features))
|
||||
{
|
||||
const char *str = alstr_get_cstr(features);
|
||||
while(isspace(str[0])) ++str;
|
||||
TRACE("Got features string:%s\n", buf+10);
|
||||
|
||||
TRACE("Got features string:%s\n", str);
|
||||
str = buf;
|
||||
while((str=strstr(str, "neon")) != NULL)
|
||||
{
|
||||
if(isspace(*(str-1)) && (str[4] == 0 || isspace(str[4])))
|
||||
|
|
@ -256,11 +245,13 @@ void FillCPUCaps(int capfilter)
|
|||
caps |= CPU_CAP_NEON;
|
||||
break;
|
||||
}
|
||||
++str;
|
||||
str++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
alstr_reset(&features);
|
||||
fclose(file);
|
||||
file = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
@ -684,13 +675,13 @@ void GetProcBinary(al_string *path, al_string *fname)
|
|||
size_t pathlen;
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };
|
||||
if(sysctl(mib, 4, NULL, &pathlen, NULL, 0) == -1)
|
||||
WARN("Failed to sysctl kern.proc.pathname: %s\n", strerror(errno));
|
||||
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));
|
||||
else
|
||||
{
|
||||
pathname = malloc(pathlen + 1);
|
||||
sysctl(mib, 4, (void*)pathname, &pathlen, NULL, 0);
|
||||
sysctl(mib, 3, (void*)pathname, &pathlen, NULL, 0);
|
||||
pathname[pathlen] = 0;
|
||||
}
|
||||
#endif
|
||||
|
|
@ -1105,8 +1096,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+1, len+2);
|
||||
VECTOR_BACK(*str) = c;
|
||||
VECTOR_RESIZE(*str, len, len+2);
|
||||
VECTOR_PUSH_BACK(*str, c);
|
||||
VECTOR_ELEM(*str, len+1) = 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -156,13 +156,13 @@ void GetHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azimuth,
|
|||
blend[3] = ( emu) * ( amu[1]) * dirfact;
|
||||
|
||||
/* Calculate the blended HRIR delays. */
|
||||
delays[0] = fastf2i(
|
||||
delays[0] = float2int(
|
||||
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]
|
||||
Hrtf->delays[idx[2]][0]*blend[2] + Hrtf->delays[idx[3]][0]*blend[3] + 0.5f
|
||||
);
|
||||
delays[1] = fastf2i(
|
||||
delays[1] = float2int(
|
||||
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]
|
||||
Hrtf->delays[idx[2]][1]*blend[2] + Hrtf->delays[idx[3]][1]*blend[3] + 0.5f
|
||||
);
|
||||
|
||||
/* Calculate the sample offsets for the HRIR indices. */
|
||||
|
|
@ -202,15 +202,12 @@ void BuildBFormatHrtf(const struct Hrtf *Hrtf, DirectHrtfState *state, ALsizei N
|
|||
#define NUM_BANDS 2
|
||||
BandSplitter splitter;
|
||||
ALdouble (*tmpres)[HRIR_LENGTH][2];
|
||||
ALsizei *restrict idx;
|
||||
ALsizei idx[HRTF_AMBI_MAX_CHANNELS];
|
||||
ALsizei min_delay = HRTF_HISTORY_LENGTH;
|
||||
ALsizei max_delay = 0;
|
||||
ALfloat temps[3][HRIR_LENGTH];
|
||||
ALsizei max_length;
|
||||
ALsizei max_length = 0;
|
||||
ALsizei i, c, b;
|
||||
|
||||
idx = al_calloc(DEF_ALIGN, AmbiCount*sizeof(*idx));
|
||||
|
||||
for(c = 0;c < AmbiCount;c++)
|
||||
{
|
||||
ALuint evidx, azidx;
|
||||
|
|
@ -231,7 +228,6 @@ void BuildBFormatHrtf(const struct Hrtf *Hrtf, DirectHrtfState *state, ALsizei N
|
|||
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));
|
||||
|
|
@ -246,6 +242,10 @@ void BuildBFormatHrtf(const struct Hrtf *Hrtf, DirectHrtfState *state, ALsizei N
|
|||
|
||||
if(NUM_BANDS == 1)
|
||||
{
|
||||
max_length = maxi(max_length,
|
||||
mini(maxi(ldelay, rdelay) + Hrtf->irSize, HRIR_LENGTH)
|
||||
);
|
||||
|
||||
for(i = 0;i < NumChannels;++i)
|
||||
{
|
||||
ALdouble mult = (ALdouble)AmbiOrderHFGain[(ALsizei)sqrt(i)] * AmbiMatrix[c][i];
|
||||
|
|
@ -261,6 +261,15 @@ void BuildBFormatHrtf(const struct Hrtf *Hrtf, DirectHrtfState *state, ALsizei N
|
|||
}
|
||||
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 = maxi(max_length,
|
||||
mini(maxi(ldelay, rdelay) + irsize, HRIR_LENGTH)
|
||||
);
|
||||
|
||||
/* Band-split left HRIR into low and high frequency responses. */
|
||||
bandsplit_clear(&splitter);
|
||||
for(i = 0;i < Hrtf->irSize;i++)
|
||||
|
|
@ -302,6 +311,9 @@ void BuildBFormatHrtf(const struct Hrtf *Hrtf, DirectHrtfState *state, ALsizei N
|
|||
}
|
||||
}
|
||||
}
|
||||
/* Round up to the next IR size multiple. */
|
||||
max_length += MOD_IR_SIZE-1;
|
||||
max_length -= max_length%MOD_IR_SIZE;
|
||||
|
||||
for(i = 0;i < NumChannels;++i)
|
||||
{
|
||||
|
|
@ -312,27 +324,11 @@ void BuildBFormatHrtf(const struct Hrtf *Hrtf, DirectHrtfState *state, ALsizei N
|
|||
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: %d, max delay: %d, new FIR length: %d\n",
|
||||
min_delay, max_delay-min_delay, max_length);
|
||||
TRACE("Skipped delay: %d, new FIR length: %d\n", min_delay, max_length);
|
||||
state->IrSize = max_length;
|
||||
#undef NUM_BANDS
|
||||
}
|
||||
|
|
@ -1034,12 +1030,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;
|
||||
}
|
||||
|
|
@ -1113,12 +1109,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,6 +9,12 @@
|
|||
#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)
|
||||
|
|
@ -75,9 +81,8 @@ 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, 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.
|
||||
* virtual speaker positions and HF/LF matrices for decoding to them. The
|
||||
* returned coefficients are ordered and scaled according to the matrices.
|
||||
*/
|
||||
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,14 +72,6 @@ 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
|
||||
|
|
|
|||
|
|
@ -5,526 +5,228 @@
|
|||
#include "mastering.h"
|
||||
#include "alu.h"
|
||||
#include "almalloc.h"
|
||||
#include "static_assert.h"
|
||||
|
||||
|
||||
/* These structures assume BUFFERSIZE is a power of 2. */
|
||||
static_assert((BUFFERSIZE & (BUFFERSIZE-1)) == 0, "BUFFERSIZE is not a power of 2");
|
||||
extern inline ALuint GetCompressorSampleRate(const Compressor *Comp);
|
||||
|
||||
typedef struct SlidingHold {
|
||||
ALfloat Values[BUFFERSIZE];
|
||||
ALsizei Expiries[BUFFERSIZE];
|
||||
ALsizei LowerIndex;
|
||||
ALsizei UpperIndex;
|
||||
ALsizei Length;
|
||||
} SlidingHold;
|
||||
#define RMS_WINDOW_SIZE (1<<7)
|
||||
#define RMS_WINDOW_MASK (RMS_WINDOW_SIZE-1)
|
||||
#define RMS_VALUE_MAX (1<<24)
|
||||
|
||||
/* General topology and basic automation was based on the following paper:
|
||||
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:
|
||||
*
|
||||
* D. Giannoulis, M. Massberg and J. D. Reiss,
|
||||
* "Parameter Automation in a Dynamic Range Compressor,"
|
||||
* Journal of the Audio Engineering Society, v61 (10), Oct. 2013
|
||||
*
|
||||
* Available (along with supplemental reading) at:
|
||||
*
|
||||
* http://c4dm.eecs.qmul.ac.uk/audioengineering/compressors/
|
||||
* Summed - Absolute sum of all channels.
|
||||
* Maxed - Absolute maximum of any channel.
|
||||
*/
|
||||
typedef struct Compressor {
|
||||
ALsizei NumChans;
|
||||
ALuint SampleRate;
|
||||
|
||||
struct {
|
||||
ALuint Knee : 1;
|
||||
ALuint Attack : 1;
|
||||
ALuint Release : 1;
|
||||
ALuint PostGain : 1;
|
||||
ALuint Declip : 1;
|
||||
} Auto;
|
||||
|
||||
ALsizei LookAhead;
|
||||
|
||||
ALfloat PreGain;
|
||||
ALfloat PostGain;
|
||||
|
||||
ALfloat Threshold;
|
||||
ALfloat Slope;
|
||||
ALfloat Knee;
|
||||
|
||||
ALfloat Attack;
|
||||
ALfloat Release;
|
||||
|
||||
alignas(16) ALfloat SideChain[2*BUFFERSIZE];
|
||||
alignas(16) ALfloat CrestFactor[BUFFERSIZE];
|
||||
|
||||
SlidingHold *Hold;
|
||||
ALfloat (*Delay)[BUFFERSIZE];
|
||||
ALsizei DelayIndex;
|
||||
|
||||
ALfloat CrestCoeff;
|
||||
ALfloat GainEstimate;
|
||||
ALfloat AdaptCoeff;
|
||||
|
||||
ALfloat LastPeakSq;
|
||||
ALfloat LastRmsSq;
|
||||
ALfloat LastRelease;
|
||||
ALfloat LastAttack;
|
||||
ALfloat LastGainDev;
|
||||
} Compressor;
|
||||
|
||||
|
||||
/* This sliding hold follows the input level with an instant attack and a
|
||||
* fixed duration hold before an instant release to the next highest level.
|
||||
* It is a sliding window maximum (descending maxima) implementation based on
|
||||
* Richard Harter's ascending minima algorithm available at:
|
||||
*
|
||||
* http://www.richardhartersworld.com/cri/2001/slidingmin.html
|
||||
*/
|
||||
static ALfloat UpdateSlidingHold(SlidingHold *Hold, const ALsizei i, const ALfloat in)
|
||||
static void SumChannels(Compressor *Comp, const ALsizei NumChans, const ALsizei SamplesToDo,
|
||||
ALfloat (*restrict OutBuffer)[BUFFERSIZE])
|
||||
{
|
||||
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;
|
||||
ALsizei c, i;
|
||||
|
||||
if(i >= expiries[upperIndex])
|
||||
upperIndex = (upperIndex + 1) & mask;
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
Comp->Envelope[i] = 0.0f;
|
||||
|
||||
if(in >= values[upperIndex])
|
||||
for(c = 0;c < NumChans;c++)
|
||||
{
|
||||
values[upperIndex] = in;
|
||||
expiries[upperIndex] = i + length;
|
||||
lowerIndex = upperIndex;
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
Comp->Envelope[i] += OutBuffer[c][i];
|
||||
}
|
||||
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
Comp->Envelope[i] = fabsf(Comp->Envelope[i]);
|
||||
}
|
||||
|
||||
static void MaxChannels(Compressor *Comp, const ALsizei NumChans, const ALsizei SamplesToDo,
|
||||
ALfloat (*restrict OutBuffer)[BUFFERSIZE])
|
||||
{
|
||||
ALsizei c, i;
|
||||
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
Comp->Envelope[i] = 0.0f;
|
||||
|
||||
for(c = 0;c < NumChans;c++)
|
||||
{
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
Comp->Envelope[i] = maxf(Comp->Envelope[i], fabsf(OutBuffer[c][i]));
|
||||
}
|
||||
}
|
||||
|
||||
/* Envelope detection/sensing can be done via:
|
||||
*
|
||||
* RMS - Rectangular windowed root mean square of linking stage.
|
||||
* Peak - Implicit output from linking stage.
|
||||
*/
|
||||
static void RmsDetection(Compressor *Comp, const ALsizei SamplesToDo)
|
||||
{
|
||||
ALuint sum = Comp->RmsSum;
|
||||
ALuint *window = Comp->RmsWindow;
|
||||
ALsizei index = Comp->RmsIndex;
|
||||
ALsizei i;
|
||||
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
{
|
||||
ALfloat sig = Comp->Envelope[i];
|
||||
|
||||
sum -= window[index];
|
||||
window[index] = fastf2i(minf(sig * sig * 65536.0f, RMS_VALUE_MAX));
|
||||
sum += window[index];
|
||||
index = (index + 1) & RMS_WINDOW_MASK;
|
||||
|
||||
Comp->Envelope[i] = sqrtf(sum / 65536.0f / RMS_WINDOW_SIZE);
|
||||
}
|
||||
|
||||
Comp->RmsSum = sum;
|
||||
Comp->RmsIndex = index;
|
||||
}
|
||||
|
||||
/* This isn't a very sophisticated envelope follower, but it gets the job
|
||||
* done. First, it operates at logarithmic scales to keep transitions
|
||||
* appropriate for human hearing. Second, it can apply adaptive (automated)
|
||||
* attack/release adjustments based on the signal.
|
||||
*/
|
||||
static void FollowEnvelope(Compressor *Comp, const ALsizei SamplesToDo)
|
||||
{
|
||||
ALfloat attackMin = Comp->AttackMin;
|
||||
ALfloat attackMax = Comp->AttackMax;
|
||||
ALfloat releaseMin = Comp->ReleaseMin;
|
||||
ALfloat releaseMax = Comp->ReleaseMax;
|
||||
ALfloat last = Comp->EnvLast;
|
||||
ALsizei i;
|
||||
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
{
|
||||
ALfloat env = log10f(maxf(Comp->Envelope[i], 0.000001f));
|
||||
ALfloat slope = minf(1.0f, fabsf(env - last) / 4.5f);
|
||||
|
||||
if(env > last)
|
||||
last = minf(env, last + lerp(attackMin, attackMax, 1.0f - (slope * slope)));
|
||||
else
|
||||
last = maxf(env, last + lerp(releaseMin, releaseMax, 1.0f - (slope * slope)));
|
||||
|
||||
Comp->Envelope[i] = last;
|
||||
}
|
||||
|
||||
Comp->EnvLast = last;
|
||||
}
|
||||
|
||||
/* The envelope is converted to control gain with an optional soft knee. */
|
||||
static void EnvelopeGain(Compressor *Comp, const ALsizei SamplesToDo, const ALfloat Slope)
|
||||
{
|
||||
const ALfloat threshold = Comp->Threshold;
|
||||
const ALfloat knee = Comp->Knee;
|
||||
ALsizei i;
|
||||
|
||||
if(!(knee > 0.0f))
|
||||
{
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
{
|
||||
ALfloat gain = Slope * (threshold - Comp->Envelope[i]);
|
||||
Comp->Envelope[i] = powf(10.0f, minf(0.0f, gain));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
do {
|
||||
do {
|
||||
if(!(in >= values[lowerIndex]))
|
||||
goto found_place;
|
||||
} while(lowerIndex--);
|
||||
lowerIndex = mask;
|
||||
} while(1);
|
||||
found_place:
|
||||
const ALfloat lower = threshold - (0.5f * knee);
|
||||
const ALfloat upper = threshold + (0.5f * knee);
|
||||
const ALfloat m = 0.5f * Slope / knee;
|
||||
|
||||
lowerIndex = (lowerIndex + 1) & mask;
|
||||
values[lowerIndex] = in;
|
||||
expiries[lowerIndex] = i + length;
|
||||
}
|
||||
|
||||
Hold->LowerIndex = lowerIndex;
|
||||
Hold->UpperIndex = upperIndex;
|
||||
|
||||
return values[upperIndex];
|
||||
}
|
||||
|
||||
static void ShiftSlidingHold(SlidingHold *Hold, const ALsizei n)
|
||||
{
|
||||
const ALsizei lowerIndex = Hold->LowerIndex;
|
||||
ALsizei *restrict expiries = Hold->Expiries;
|
||||
ALsizei i = Hold->UpperIndex;
|
||||
|
||||
if(lowerIndex < i)
|
||||
{
|
||||
for(;i < BUFFERSIZE;i++)
|
||||
expiries[i] -= n;
|
||||
i = 0;
|
||||
}
|
||||
for(;i < lowerIndex;i++)
|
||||
expiries[i] -= n;
|
||||
|
||||
expiries[i] -= n;
|
||||
}
|
||||
|
||||
/* Multichannel compression is linked via the absolute maximum of all
|
||||
* channels.
|
||||
*/
|
||||
static void LinkChannels(Compressor *Comp, const ALsizei SamplesToDo, ALfloat (*restrict OutBuffer)[BUFFERSIZE])
|
||||
{
|
||||
const ALsizei index = Comp->LookAhead;
|
||||
const ALsizei numChans = Comp->NumChans;
|
||||
ALfloat *restrict sideChain = Comp->SideChain;
|
||||
ALsizei c, i;
|
||||
|
||||
ASSUME(SamplesToDo > 0);
|
||||
ASSUME(numChans > 0);
|
||||
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
sideChain[index + i] = 0.0f;
|
||||
|
||||
for(c = 0;c < numChans;c++)
|
||||
{
|
||||
ALsizei offset = index;
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
{
|
||||
sideChain[offset] = maxf(sideChain[offset], fabsf(OutBuffer[c][i]));
|
||||
++offset;
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* This calculates the squared crest factor of the control signal for the
|
||||
* basic automation of the attack/release times. As suggested by the paper,
|
||||
* it uses an instantaneous squared peak detector and a squared RMS detector
|
||||
* both with 200ms release times.
|
||||
*/
|
||||
static void CrestDetector(Compressor *Comp, const ALsizei SamplesToDo)
|
||||
{
|
||||
const ALfloat a_crest = Comp->CrestCoeff;
|
||||
const ALsizei index = Comp->LookAhead;
|
||||
const ALfloat *restrict sideChain = Comp->SideChain;
|
||||
ALfloat *restrict crestFactor = Comp->CrestFactor;
|
||||
ALfloat y2_peak = Comp->LastPeakSq;
|
||||
ALfloat y2_rms = Comp->LastRmsSq;
|
||||
ALsizei i;
|
||||
|
||||
ASSUME(SamplesToDo > 0);
|
||||
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
{
|
||||
ALfloat x_abs = sideChain[index + i];
|
||||
ALfloat x2 = maxf(0.000001f, x_abs * x_abs);
|
||||
|
||||
y2_peak = maxf(x2, lerp(x2, y2_peak, a_crest));
|
||||
y2_rms = lerp(x2, y2_rms, a_crest);
|
||||
crestFactor[i] = y2_peak / y2_rms;
|
||||
}
|
||||
|
||||
Comp->LastPeakSq = y2_peak;
|
||||
Comp->LastRmsSq = y2_rms;
|
||||
}
|
||||
|
||||
/* The side-chain starts with a simple peak detector (based on the absolute
|
||||
* value of the incoming signal) and performs most of its operations in the
|
||||
* log domain.
|
||||
*/
|
||||
static void PeakDetector(Compressor *Comp, const ALsizei SamplesToDo)
|
||||
{
|
||||
const ALsizei index = Comp->LookAhead;
|
||||
ALfloat *restrict sideChain = Comp->SideChain;
|
||||
ALsizei i;
|
||||
|
||||
ASSUME(SamplesToDo > 0);
|
||||
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
{
|
||||
const ALuint offset = index + i;
|
||||
const ALfloat x_abs = sideChain[offset];
|
||||
|
||||
sideChain[offset] = logf(maxf(0.000001f, x_abs));
|
||||
}
|
||||
}
|
||||
|
||||
/* An optional hold can be used to extend the peak detector so it can more
|
||||
* solidly detect fast transients. This is best used when operating as a
|
||||
* limiter.
|
||||
*/
|
||||
static void PeakHoldDetector(Compressor *Comp, const ALsizei SamplesToDo)
|
||||
{
|
||||
const ALsizei index = Comp->LookAhead;
|
||||
ALfloat *restrict sideChain = Comp->SideChain;
|
||||
SlidingHold *hold = Comp->Hold;
|
||||
ALsizei i;
|
||||
|
||||
ASSUME(SamplesToDo > 0);
|
||||
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
{
|
||||
const ALsizei offset = index + i;
|
||||
const ALfloat x_abs = sideChain[offset];
|
||||
const ALfloat x_G = logf(maxf(0.000001f, x_abs));
|
||||
|
||||
sideChain[offset] = UpdateSlidingHold(hold, i, x_G);
|
||||
}
|
||||
|
||||
ShiftSlidingHold(hold, SamplesToDo);
|
||||
}
|
||||
|
||||
/* This is the heart of the feed-forward compressor. It operates in the log
|
||||
* domain (to better match human hearing) and can apply some basic automation
|
||||
* to knee width, attack/release times, make-up/post gain, and clipping
|
||||
* reduction.
|
||||
*/
|
||||
static void GainCompressor(Compressor *Comp, const ALsizei SamplesToDo)
|
||||
{
|
||||
const bool autoKnee = Comp->Auto.Knee;
|
||||
const bool autoAttack = Comp->Auto.Attack;
|
||||
const bool autoRelease = Comp->Auto.Release;
|
||||
const bool autoPostGain = Comp->Auto.PostGain;
|
||||
const bool autoDeclip = Comp->Auto.Declip;
|
||||
const ALsizei lookAhead = Comp->LookAhead;
|
||||
const ALfloat threshold = Comp->Threshold;
|
||||
const ALfloat slope = Comp->Slope;
|
||||
const ALfloat attack = Comp->Attack;
|
||||
const ALfloat release = Comp->Release;
|
||||
const ALfloat c_est = Comp->GainEstimate;
|
||||
const ALfloat a_adp = Comp->AdaptCoeff;
|
||||
const ALfloat *restrict crestFactor = Comp->CrestFactor;
|
||||
ALfloat *restrict sideChain = Comp->SideChain;
|
||||
ALfloat postGain = Comp->PostGain;
|
||||
ALfloat knee = Comp->Knee;
|
||||
ALfloat t_att = attack;
|
||||
ALfloat t_rel = release - attack;
|
||||
ALfloat a_att = expf(-1.0f / t_att);
|
||||
ALfloat a_rel = expf(-1.0f / t_rel);
|
||||
ALfloat y_1 = Comp->LastRelease;
|
||||
ALfloat y_L = Comp->LastAttack;
|
||||
ALfloat c_dev = Comp->LastGainDev;
|
||||
ALsizei i;
|
||||
|
||||
ASSUME(SamplesToDo > 0);
|
||||
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
{
|
||||
const ALfloat y2_crest = crestFactor[i];
|
||||
const ALfloat x_G = sideChain[lookAhead + i];
|
||||
const ALfloat x_over = x_G - threshold;
|
||||
ALfloat knee_h;
|
||||
ALfloat y_G;
|
||||
ALfloat x_L;
|
||||
|
||||
if(autoKnee)
|
||||
knee = maxf(0.0f, 2.5f * (c_dev + c_est));
|
||||
knee_h = 0.5f * knee;
|
||||
|
||||
/* This is the gain computer. It applies a static compression curve
|
||||
* to the control signal.
|
||||
*/
|
||||
if(x_over <= -knee_h)
|
||||
y_G = 0.0f;
|
||||
else if(fabsf(x_over) < knee_h)
|
||||
y_G = (x_over + knee_h) * (x_over + knee_h) / (2.0f * knee);
|
||||
else
|
||||
y_G = x_over;
|
||||
|
||||
x_L = -slope * y_G;
|
||||
|
||||
if(autoAttack)
|
||||
{
|
||||
t_att = 2.0f * attack / y2_crest;
|
||||
a_att = expf(-1.0f / t_att);
|
||||
}
|
||||
|
||||
if(autoRelease)
|
||||
{
|
||||
t_rel = 2.0f * release / y2_crest - t_att;
|
||||
a_rel = expf(-1.0f / t_rel);
|
||||
}
|
||||
|
||||
/* Gain smoothing (ballistics) is done via a smooth decoupled peak
|
||||
* detector. The attack time is subtracted from the release time
|
||||
* above to compensate for the chained operating mode.
|
||||
*/
|
||||
y_1 = maxf(x_L, lerp(x_L, y_1, a_rel));
|
||||
y_L = lerp(y_1, y_L, a_att);
|
||||
|
||||
/* Knee width and make-up gain automation make use of a smoothed
|
||||
* measurement of deviation between the control signal and estimate.
|
||||
* The estimate is also used to bias the measurement to hot-start its
|
||||
* average.
|
||||
*/
|
||||
c_dev = lerp(-y_L - c_est, c_dev, a_adp);
|
||||
|
||||
if(autoPostGain)
|
||||
{
|
||||
/* Clipping reduction is only viable when make-up gain is being
|
||||
* automated. It modifies the deviation to further attenuate the
|
||||
* control signal when clipping is detected. The adaptation
|
||||
* time is sufficiently long enough to suppress further clipping
|
||||
* at the same output level.
|
||||
*/
|
||||
if(autoDeclip)
|
||||
c_dev = maxf(c_dev, sideChain[i] - y_L - threshold - c_est);
|
||||
|
||||
postGain = -(c_dev + c_est);
|
||||
}
|
||||
|
||||
sideChain[i] = expf(postGain - y_L);
|
||||
}
|
||||
|
||||
Comp->LastRelease = y_1;
|
||||
Comp->LastAttack = y_L;
|
||||
Comp->LastGainDev = c_dev;
|
||||
}
|
||||
|
||||
/* Combined with the hold time, a look-ahead delay can improve handling of
|
||||
* fast transients by allowing the envelope time to converge prior to
|
||||
* reaching the offending impulse. This is best used when operating as a
|
||||
* limiter.
|
||||
*/
|
||||
static void SignalDelay(Compressor *Comp, const ALsizei SamplesToDo, ALfloat (*restrict OutBuffer)[BUFFERSIZE])
|
||||
{
|
||||
const ALsizei mask = BUFFERSIZE - 1;
|
||||
const ALsizei numChans = Comp->NumChans;
|
||||
const ALsizei indexIn = Comp->DelayIndex;
|
||||
const ALsizei indexOut = Comp->DelayIndex - Comp->LookAhead;
|
||||
ALfloat (*restrict delay)[BUFFERSIZE] = Comp->Delay;
|
||||
ALsizei c, i;
|
||||
|
||||
ASSUME(SamplesToDo > 0);
|
||||
ASSUME(numChans > 0);
|
||||
|
||||
for(c = 0;c < numChans;c++)
|
||||
{
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
{
|
||||
ALfloat sig = OutBuffer[c][i];
|
||||
|
||||
OutBuffer[c][i] = delay[c][(indexOut + i) & mask];
|
||||
delay[c][(indexIn + i) & mask] = sig;
|
||||
}
|
||||
}
|
||||
|
||||
Comp->DelayIndex = (indexIn + SamplesToDo) & mask;
|
||||
}
|
||||
|
||||
/* The compressor is initialized with the following settings:
|
||||
*
|
||||
* NumChans - Number of channels to process.
|
||||
* SampleRate - Sample rate to process.
|
||||
* AutoKnee - Whether to automate the knee width parameter.
|
||||
* AutoAttack - Whether to automate the attack time parameter.
|
||||
* AutoRelease - Whether to automate the release time parameter.
|
||||
* AutoPostGain - Whether to automate the make-up (post) gain parameter.
|
||||
* AutoDeclip - Whether to automate clipping reduction. Ignored when
|
||||
* not automating make-up gain.
|
||||
* LookAheadTime - Look-ahead time (in seconds).
|
||||
* HoldTime - Peak hold-time (in seconds).
|
||||
* PreGainDb - Gain applied before detection (in dB).
|
||||
* PostGainDb - Make-up gain applied after compression (in dB).
|
||||
* ThresholdDb - Triggering threshold (in dB).
|
||||
* Ratio - Compression ratio (x:1). Set to INFINITY for true
|
||||
* limiting. Ignored when automating knee width.
|
||||
* KneeDb - Knee width (in dB). Ignored when automating knee
|
||||
* width.
|
||||
* AttackTimeMin - Attack time (in seconds). Acts as a maximum when
|
||||
* automating attack time.
|
||||
* ReleaseTimeMin - Release time (in seconds). Acts as a maximum when
|
||||
* automating release time.
|
||||
*/
|
||||
Compressor* CompressorInit(const ALsizei NumChans, const ALuint SampleRate,
|
||||
const ALboolean AutoKnee, const ALboolean AutoAttack,
|
||||
const ALboolean AutoRelease, const ALboolean AutoPostGain,
|
||||
const ALboolean AutoDeclip, const ALfloat LookAheadTime,
|
||||
const ALfloat HoldTime, const ALfloat PreGainDb,
|
||||
const ALfloat PostGainDb, const ALfloat ThresholdDb,
|
||||
const ALfloat Ratio, const ALfloat KneeDb,
|
||||
const ALfloat AttackTime, const ALfloat ReleaseTime)
|
||||
Compressor *CompressorInit(const ALfloat PreGainDb, const ALfloat PostGainDb,
|
||||
const ALboolean SummedLink, const ALboolean RmsSensing,
|
||||
const ALfloat AttackTimeMin, const ALfloat AttackTimeMax,
|
||||
const ALfloat ReleaseTimeMin, const ALfloat ReleaseTimeMax,
|
||||
const ALfloat Ratio, const ALfloat ThresholdDb,
|
||||
const ALfloat KneeDb, const ALuint SampleRate)
|
||||
{
|
||||
Compressor *Comp;
|
||||
ALsizei lookAhead;
|
||||
ALsizei hold;
|
||||
size_t size;
|
||||
|
||||
lookAhead = (ALsizei)clampf(roundf(LookAheadTime*SampleRate), 0.0f, BUFFERSIZE-1);
|
||||
hold = (ALsizei)clampf(roundf(HoldTime*SampleRate), 0.0f, BUFFERSIZE-1);
|
||||
/* The sliding hold implementation doesn't handle a length of 1. A 1-sample
|
||||
* hold is useless anyway, it would only ever give back what was just given
|
||||
* to it.
|
||||
*/
|
||||
if(hold == 1)
|
||||
hold = 0;
|
||||
ALsizei i;
|
||||
|
||||
size = sizeof(*Comp);
|
||||
if(lookAhead > 0)
|
||||
{
|
||||
size += sizeof(*Comp->Delay) * NumChans;
|
||||
if(hold > 0)
|
||||
size += sizeof(*Comp->Hold);
|
||||
}
|
||||
|
||||
if(RmsSensing)
|
||||
size += sizeof(Comp->RmsWindow[0]) * RMS_WINDOW_SIZE;
|
||||
Comp = al_calloc(16, size);
|
||||
Comp->NumChans = NumChans;
|
||||
Comp->SampleRate = SampleRate;
|
||||
Comp->Auto.Knee = AutoKnee;
|
||||
Comp->Auto.Attack = AutoAttack;
|
||||
Comp->Auto.Release = AutoRelease;
|
||||
Comp->Auto.PostGain = AutoPostGain;
|
||||
Comp->Auto.Declip = AutoPostGain && AutoDeclip;
|
||||
Comp->LookAhead = lookAhead;
|
||||
|
||||
Comp->PreGain = powf(10.0f, PreGainDb / 20.0f);
|
||||
Comp->PostGain = PostGainDb * logf(10.0f) / 20.0f;
|
||||
Comp->Threshold = ThresholdDb * logf(10.0f) / 20.0f;
|
||||
Comp->Slope = 1.0f / maxf(1.0f, Ratio) - 1.0f;
|
||||
Comp->Knee = maxf(0.0f, KneeDb * logf(10.0f) / 20.0f);
|
||||
Comp->Attack = maxf(1.0f, AttackTime * SampleRate);
|
||||
Comp->Release = maxf(1.0f, ReleaseTime * SampleRate);
|
||||
Comp->PostGain = powf(10.0f, PostGainDb / 20.0f);
|
||||
Comp->SummedLink = SummedLink;
|
||||
Comp->AttackMin = 1.0f / maxf(0.000001f, AttackTimeMin * SampleRate * logf(10.0f));
|
||||
Comp->AttackMax = 1.0f / maxf(0.000001f, AttackTimeMax * SampleRate * logf(10.0f));
|
||||
Comp->ReleaseMin = -1.0f / maxf(0.000001f, ReleaseTimeMin * SampleRate * logf(10.0f));
|
||||
Comp->ReleaseMax = -1.0f / maxf(0.000001f, ReleaseTimeMax * SampleRate * logf(10.0f));
|
||||
Comp->Ratio = Ratio;
|
||||
Comp->Threshold = ThresholdDb / 20.0f;
|
||||
Comp->Knee = maxf(0.0f, KneeDb / 20.0f);
|
||||
Comp->SampleRate = SampleRate;
|
||||
|
||||
/* 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;
|
||||
Comp->RmsSum = 0;
|
||||
if(RmsSensing)
|
||||
Comp->RmsWindow = (ALuint*)(Comp+1);
|
||||
else
|
||||
Comp->RmsWindow = NULL;
|
||||
Comp->RmsIndex = 0;
|
||||
|
||||
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
|
||||
for(i = 0;i < BUFFERSIZE;i++)
|
||||
Comp->Envelope[i] = 0.0f;
|
||||
Comp->EnvLast = -6.0f;
|
||||
|
||||
return Comp;
|
||||
}
|
||||
|
||||
void ApplyCompression(Compressor *Comp, const ALsizei SamplesToDo, ALfloat (*restrict OutBuffer)[BUFFERSIZE])
|
||||
void ApplyCompression(Compressor *Comp, const ALsizei NumChans, const ALsizei SamplesToDo,
|
||||
ALfloat (*restrict OutBuffer)[BUFFERSIZE])
|
||||
{
|
||||
const ALsizei numChans = Comp->NumChans;
|
||||
const ALfloat preGain = Comp->PreGain;
|
||||
ALfloat *restrict sideChain;
|
||||
ALsizei c, i;
|
||||
|
||||
ASSUME(SamplesToDo > 0);
|
||||
ASSUME(numChans > 0);
|
||||
|
||||
if(preGain != 1.0f)
|
||||
if(Comp->PreGain != 1.0f)
|
||||
{
|
||||
for(c = 0;c < numChans;c++)
|
||||
for(c = 0;c < NumChans;c++)
|
||||
{
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
OutBuffer[c][i] *= preGain;
|
||||
OutBuffer[c][i] *= Comp->PreGain;
|
||||
}
|
||||
}
|
||||
|
||||
LinkChannels(Comp, SamplesToDo, OutBuffer);
|
||||
|
||||
if(Comp->Auto.Attack || Comp->Auto.Release)
|
||||
CrestDetector(Comp, SamplesToDo);
|
||||
|
||||
if(Comp->Hold)
|
||||
PeakHoldDetector(Comp, SamplesToDo);
|
||||
if(Comp->SummedLink)
|
||||
SumChannels(Comp, NumChans, SamplesToDo, OutBuffer);
|
||||
else
|
||||
PeakDetector(Comp, SamplesToDo);
|
||||
MaxChannels(Comp, NumChans, SamplesToDo, OutBuffer);
|
||||
|
||||
GainCompressor(Comp, SamplesToDo);
|
||||
if(Comp->RmsWindow)
|
||||
RmsDetection(Comp, SamplesToDo);
|
||||
FollowEnvelope(Comp, SamplesToDo);
|
||||
|
||||
if(Comp->Delay)
|
||||
SignalDelay(Comp, SamplesToDo, OutBuffer);
|
||||
if(Comp->Ratio > 0.0f)
|
||||
EnvelopeGain(Comp, SamplesToDo, 1.0f - (1.0f / Comp->Ratio));
|
||||
else
|
||||
EnvelopeGain(Comp, SamplesToDo, 1.0f);
|
||||
|
||||
sideChain = Comp->SideChain;
|
||||
for(c = 0;c < numChans;c++)
|
||||
if(Comp->PostGain != 1.0f)
|
||||
{
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
OutBuffer[c][i] *= sideChain[i];
|
||||
Comp->Envelope[i] *= Comp->PostGain;
|
||||
}
|
||||
for(c = 0;c < NumChans;c++)
|
||||
{
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
OutBuffer[c][i] *= Comp->Envelope[i];
|
||||
}
|
||||
|
||||
memmove(sideChain, sideChain+SamplesToDo, Comp->LookAhead*sizeof(ALfloat));
|
||||
}
|
||||
|
||||
|
||||
ALsizei GetCompressorLookAhead(const Compressor *Comp)
|
||||
{ return Comp->LookAhead; }
|
||||
|
|
|
|||
|
|
@ -6,44 +6,52 @@
|
|||
/* For BUFFERSIZE. */
|
||||
#include "alMain.h"
|
||||
|
||||
struct Compressor;
|
||||
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;
|
||||
|
||||
/* The compressor is initialized with the following settings:
|
||||
ALuint RmsSum;
|
||||
ALuint *RmsWindow;
|
||||
ALsizei RmsIndex;
|
||||
ALfloat Envelope[BUFFERSIZE];
|
||||
ALfloat EnvLast;
|
||||
} Compressor;
|
||||
|
||||
/* The compressor requires the following information for proper
|
||||
* initialization:
|
||||
*
|
||||
* 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).
|
||||
* 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).
|
||||
* 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.
|
||||
* KneeDb - Knee width (below threshold; in dB).
|
||||
* SampleRate - Sample rate to process.
|
||||
*/
|
||||
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);
|
||||
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);
|
||||
|
||||
void ApplyCompression(struct Compressor *Comp, const ALsizei SamplesToDo,
|
||||
void ApplyCompression(struct Compressor *Comp, const ALsizei NumChans, const ALsizei SamplesToDo,
|
||||
ALfloat (*restrict OutBuffer)[BUFFERSIZE]);
|
||||
|
||||
ALsizei GetCompressorLookAhead(const struct Compressor *Comp);
|
||||
inline ALuint GetCompressorSampleRate(const Compressor *Comp)
|
||||
{ return Comp->SampleRate; }
|
||||
|
||||
#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, ALsizei *restrict pos_arr, ALsizei size)
|
||||
inline void InitiatePositionArrays(ALsizei frac, ALint increment, ALsizei *restrict frac_arr, ALint *restrict pos_arr, ALsizei size)
|
||||
{
|
||||
ALsizei i;
|
||||
|
||||
|
|
|
|||
|
|
@ -22,9 +22,8 @@ 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] };
|
||||
const ALfloat gainstep = hrtfparams->GainStep;
|
||||
const ALfloat gain = hrtfparams->Gain;
|
||||
ALfloat g, stepcount = 0.0f;
|
||||
ALfloat gainstep = hrtfparams->GainStep;
|
||||
ALfloat gain = hrtfparams->Gain;
|
||||
ALfloat left, right;
|
||||
ALsizei i;
|
||||
|
||||
|
|
@ -36,10 +35,8 @@ void MixHrtf(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
|
|||
for(i = 0;i < BufferSize;i++)
|
||||
{
|
||||
hrtfstate->History[Offset&HRTF_HISTORY_MASK] = *(data++);
|
||||
|
||||
g = gain + gainstep*stepcount;
|
||||
left = hrtfstate->History[(Offset-Delay[0])&HRTF_HISTORY_MASK]*g;
|
||||
right = hrtfstate->History[(Offset-Delay[1])&HRTF_HISTORY_MASK]*g;
|
||||
left = hrtfstate->History[(Offset-Delay[0])&HRTF_HISTORY_MASK]*gain;
|
||||
right = hrtfstate->History[(Offset-Delay[1])&HRTF_HISTORY_MASK]*gain;
|
||||
|
||||
hrtfstate->Values[(Offset+IrSize-1)&HRIR_MASK][0] = 0.0f;
|
||||
hrtfstate->Values[(Offset+IrSize-1)&HRIR_MASK][1] = 0.0f;
|
||||
|
|
@ -48,10 +45,10 @@ void MixHrtf(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
|
|||
*(LeftOut++) += hrtfstate->Values[Offset&HRIR_MASK][0];
|
||||
*(RightOut++) += hrtfstate->Values[Offset&HRIR_MASK][1];
|
||||
|
||||
stepcount += 1.0f;
|
||||
gain += gainstep;
|
||||
Offset++;
|
||||
}
|
||||
hrtfparams->Gain = gain + gainstep*stepcount;
|
||||
hrtfparams->Gain = gain;
|
||||
}
|
||||
|
||||
void MixHrtfBlend(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
|
||||
|
|
@ -62,13 +59,12 @@ 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] };
|
||||
const ALfloat oldGain = oldparams->Gain;
|
||||
const ALfloat oldGainStep = -oldGain / (ALfloat)BufferSize;
|
||||
ALfloat oldGain = oldparams->Gain;
|
||||
ALfloat oldGainStep = -oldGain / (ALfloat)BufferSize;
|
||||
const ALfloat (*NewCoeffs)[2] = ASSUME_ALIGNED(newparams->Coeffs, 16);
|
||||
const ALsizei NewDelay[2] = { newparams->Delay[0], newparams->Delay[1] };
|
||||
const ALfloat newGain = newparams->Gain;
|
||||
const ALfloat newGainStep = newparams->GainStep;
|
||||
ALfloat g, stepcount = 0.0f;
|
||||
ALfloat newGain = newparams->Gain;
|
||||
ALfloat newGainStep = newparams->GainStep;
|
||||
ALfloat left, right;
|
||||
ALsizei i;
|
||||
|
||||
|
|
@ -84,23 +80,22 @@ void MixHrtfBlend(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
|
|||
|
||||
hrtfstate->History[Offset&HRTF_HISTORY_MASK] = *(data++);
|
||||
|
||||
g = oldGain + oldGainStep*stepcount;
|
||||
left = hrtfstate->History[(Offset-OldDelay[0])&HRTF_HISTORY_MASK]*g;
|
||||
right = hrtfstate->History[(Offset-OldDelay[1])&HRTF_HISTORY_MASK]*g;
|
||||
left = hrtfstate->History[(Offset-OldDelay[0])&HRTF_HISTORY_MASK]*oldGain;
|
||||
right = hrtfstate->History[(Offset-OldDelay[1])&HRTF_HISTORY_MASK]*oldGain;
|
||||
ApplyCoeffs(Offset, hrtfstate->Values, IrSize, OldCoeffs, left, right);
|
||||
|
||||
g = newGain + newGainStep*stepcount;
|
||||
left = hrtfstate->History[(Offset-NewDelay[0])&HRTF_HISTORY_MASK]*g;
|
||||
right = hrtfstate->History[(Offset-NewDelay[1])&HRTF_HISTORY_MASK]*g;
|
||||
left = hrtfstate->History[(Offset-NewDelay[0])&HRTF_HISTORY_MASK]*newGain;
|
||||
right = hrtfstate->History[(Offset-NewDelay[1])&HRTF_HISTORY_MASK]*newGain;
|
||||
ApplyCoeffs(Offset, hrtfstate->Values, IrSize, NewCoeffs, left, right);
|
||||
|
||||
*(LeftOut++) += hrtfstate->Values[Offset&HRIR_MASK][0];
|
||||
*(RightOut++) += hrtfstate->Values[Offset&HRIR_MASK][1];
|
||||
|
||||
stepcount += 1.0f;
|
||||
oldGain += oldGainStep;
|
||||
newGain += newGainStep;
|
||||
Offset++;
|
||||
}
|
||||
newparams->Gain = newGain + newGainStep*stepcount;
|
||||
newparams->Gain = newGain;
|
||||
}
|
||||
|
||||
void MixDirectHrtf(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
|
||||
|
|
|
|||
|
|
@ -9,37 +9,12 @@
|
|||
#include "defs.h"
|
||||
|
||||
|
||||
static inline ALfloat do_point(const InterpState* UNUSED(state), const ALfloat *restrict vals, ALsizei UNUSED(frac))
|
||||
static inline ALfloat do_point(const ALfloat *restrict vals, ALsizei UNUSED(frac))
|
||||
{ return vals[0]; }
|
||||
static inline ALfloat do_lerp(const InterpState* UNUSED(state), const ALfloat *restrict vals, ALsizei frac)
|
||||
static inline ALfloat do_lerp(const ALfloat *restrict vals, ALsizei frac)
|
||||
{ return lerp(vals[0], vals[1], frac * (1.0f/FRACTIONONE)); }
|
||||
static inline ALfloat do_cubic(const InterpState* UNUSED(state), const ALfloat *restrict vals, ALsizei frac)
|
||||
static inline ALfloat do_cubic(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),
|
||||
|
|
@ -55,19 +30,16 @@ const ALfloat *Resample_copy_C(const InterpState* UNUSED(state),
|
|||
}
|
||||
|
||||
#define DECL_TEMPLATE(Tag, Sampler, O) \
|
||||
const ALfloat *Resample_##Tag##_C(const InterpState *state, \
|
||||
const ALfloat *Resample_##Tag##_C(const InterpState* UNUSED(state), \
|
||||
const ALfloat *restrict src, ALsizei frac, ALint increment, \
|
||||
ALfloat *restrict dst, ALsizei numsamples) \
|
||||
{ \
|
||||
const InterpState istate = *state; \
|
||||
ALsizei i; \
|
||||
\
|
||||
ASSUME(numsamples > 0); \
|
||||
\
|
||||
src -= O; \
|
||||
for(i = 0;i < numsamples;i++) \
|
||||
{ \
|
||||
dst[i] = Sampler(&istate, src, frac); \
|
||||
dst[i] = Sampler(src, frac); \
|
||||
\
|
||||
frac += increment; \
|
||||
src += frac>>FRACTIONBITS; \
|
||||
|
|
@ -79,10 +51,49 @@ const ALfloat *Resample_##Tag##_C(const InterpState *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;
|
||||
|
||||
ASSUME(m > 0);
|
||||
|
||||
src += state->bsinc.l;
|
||||
for(i = 0;i < dstlen;i++)
|
||||
{
|
||||
// Calculate the phase index and factor.
|
||||
#define FRAC_PHASE_BITDIFF (FRACTIONBITS-BSINC_PHASE_BITS)
|
||||
pi = frac >> FRAC_PHASE_BITDIFF;
|
||||
pf = (frac & ((1<<FRAC_PHASE_BITDIFF)-1)) * (1.0f/(1<<FRAC_PHASE_BITDIFF));
|
||||
#undef FRAC_PHASE_BITDIFF
|
||||
|
||||
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,
|
||||
|
|
@ -108,32 +119,28 @@ void Mix_C(const ALfloat *data, ALsizei OutChans, ALfloat (*restrict OutBuffer)[
|
|||
ALfloat *CurrentGains, const ALfloat *TargetGains, ALsizei Counter, ALsizei OutPos,
|
||||
ALsizei BufferSize)
|
||||
{
|
||||
const ALfloat delta = (Counter > 0) ? 1.0f/(ALfloat)Counter : 0.0f;
|
||||
ALfloat gain, delta, step;
|
||||
ALsizei c;
|
||||
|
||||
ASSUME(OutChans > 0);
|
||||
ASSUME(BufferSize > 0);
|
||||
delta = (Counter > 0) ? 1.0f/(ALfloat)Counter : 0.0f;
|
||||
|
||||
for(c = 0;c < OutChans;c++)
|
||||
{
|
||||
ALsizei pos = 0;
|
||||
ALfloat gain = CurrentGains[c];
|
||||
const ALfloat diff = TargetGains[c] - gain;
|
||||
|
||||
if(fabsf(diff) > FLT_EPSILON)
|
||||
gain = CurrentGains[c];
|
||||
step = (TargetGains[c] - gain) * delta;
|
||||
if(fabsf(step) > FLT_EPSILON)
|
||||
{
|
||||
ALsizei minsize = mini(BufferSize, Counter);
|
||||
const ALfloat step = diff * delta;
|
||||
ALfloat step_count = 0.0f;
|
||||
for(;pos < minsize;pos++)
|
||||
{
|
||||
OutBuffer[c][OutPos+pos] += data[pos] * (gain + step*step_count);
|
||||
step_count += 1.0f;
|
||||
OutBuffer[c][OutPos+pos] += data[pos]*gain;
|
||||
gain += step;
|
||||
}
|
||||
if(pos == Counter)
|
||||
gain = TargetGains[c];
|
||||
else
|
||||
gain += step*step_count;
|
||||
CurrentGains[c] = gain;
|
||||
}
|
||||
|
||||
|
|
@ -159,7 +166,7 @@ void MixRow_C(ALfloat *OutBuffer, const ALfloat *Gains, const ALfloat (*restrict
|
|||
|
||||
for(c = 0;c < InChans;c++)
|
||||
{
|
||||
const ALfloat gain = Gains[c];
|
||||
ALfloat gain = Gains[c];
|
||||
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
|
||||
continue;
|
||||
|
||||
|
|
|
|||
|
|
@ -17,25 +17,23 @@ 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) ALsizei pos_[4], frac_[4];
|
||||
int32x4_t pos4, frac4;
|
||||
ALsizei todo, pos, i;
|
||||
alignas(16) ALint pos_[4];
|
||||
alignas(16) ALsizei frac_[4];
|
||||
int32x4_t pos4;
|
||||
int32x4_t frac4;
|
||||
ALsizei i;
|
||||
|
||||
ASSUME(numsamples > 0);
|
||||
|
||||
InitiatePositionArrays(frac, increment, frac_, pos_, 4);
|
||||
|
||||
frac4 = vld1q_s32(frac_);
|
||||
pos4 = vld1q_s32(pos_);
|
||||
|
||||
todo = numsamples & ~3;
|
||||
for(i = 0;i < todo;i += 4)
|
||||
for(i = 0;numsamples-i > 3;i += 4)
|
||||
{
|
||||
const int pos0 = vgetq_lane_s32(pos4, 0);
|
||||
const int pos1 = vgetq_lane_s32(pos4, 1);
|
||||
const int pos2 = vgetq_lane_s32(pos4, 2);
|
||||
const int pos3 = vgetq_lane_s32(pos4, 3);
|
||||
const float32x4_t val1 = (float32x4_t){src[pos0], src[pos1], src[pos2], src[pos3]};
|
||||
const float32x4_t val2 = (float32x4_t){src[pos0+1], src[pos1+1], src[pos2+1], src[pos3+1]};
|
||||
const float32x4_t val1 = (float32x4_t){src[pos_[0]], src[pos_[1]], src[pos_[2]], src[pos_[3]]};
|
||||
const float32x4_t val2 = (float32x4_t){src[pos_[0]+1], src[pos_[1]+1], src[pos_[2]+1], src[pos_[3]+1]};
|
||||
|
||||
/* val1 + (val2-val1)*mu */
|
||||
const float32x4_t r0 = vsubq_f32(val2, val1);
|
||||
|
|
@ -47,21 +45,25 @@ 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);
|
||||
}
|
||||
|
||||
/* NOTE: These four elements represent the position *after* the last four
|
||||
* samples, so the lowest element is the next position to resample.
|
||||
*/
|
||||
pos = vgetq_lane_s32(pos4, 0);
|
||||
frac = vgetq_lane_s32(frac4, 0);
|
||||
|
||||
for(;i < numsamples;++i)
|
||||
if(i < numsamples)
|
||||
{
|
||||
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.
|
||||
*/
|
||||
ALint pos = pos_[0];
|
||||
frac = vgetq_lane_s32(frac4, 0);
|
||||
do {
|
||||
dst[i] = lerp(src[pos], src[pos+1], frac * (1.0f/FRACTIONONE));
|
||||
|
||||
frac += increment;
|
||||
pos += frac>>FRACTIONBITS;
|
||||
frac &= FRACTIONMASK;
|
||||
frac += increment;
|
||||
pos += frac>>FRACTIONBITS;
|
||||
frac &= FRACTIONMASK;
|
||||
} while(++i < numsamples);
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
|
@ -81,7 +83,7 @@ const ALfloat *Resample_bsinc_Neon(const InterpState *state,
|
|||
ASSUME(m > 0);
|
||||
ASSUME(dstlen > 0);
|
||||
|
||||
src -= state->bsinc.l;
|
||||
src += state->bsinc.l;
|
||||
for(i = 0;i < dstlen;i++)
|
||||
{
|
||||
// Calculate the phase index and factor.
|
||||
|
|
@ -99,20 +101,16 @@ 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);
|
||||
|
||||
ASSUME(count > 0);
|
||||
|
||||
for(j = 0;j < count;j++)
|
||||
for(j = 0;j < m;j+=4,fil++,scd++,phd++,spd++)
|
||||
{
|
||||
/* f = ((fil + sf*scd) + pf*(phd + sf*spd)) */
|
||||
const float32x4_t f4 = vmlaq_f32(
|
||||
vmlaq_f32(fil[j], sf4, scd[j]),
|
||||
pf4, vmlaq_f32(phd[j], sf4, spd[j])
|
||||
vmlaq_f32(*fil, sf4, *scd),
|
||||
pf4, vmlaq_f32(*phd, sf4, *spd)
|
||||
);
|
||||
/* r += f*src */
|
||||
r4 = vmlaq_f32(r4, f4, vld1q_f32(&src[j*4]));
|
||||
r4 = vmlaq_f32(r4, f4, vld1q_f32(&src[j]));
|
||||
}
|
||||
}
|
||||
r4 = vaddq_f32(r4, vcombine_f32(vrev64_f32(vget_high_f32(r4)),
|
||||
|
|
@ -167,7 +165,8 @@ void Mix_Neon(const ALfloat *data, ALsizei OutChans, ALfloat (*restrict OutBuffe
|
|||
ALfloat *CurrentGains, const ALfloat *TargetGains, ALsizei Counter, ALsizei OutPos,
|
||||
ALsizei BufferSize)
|
||||
{
|
||||
const ALfloat delta = (Counter > 0) ? 1.0f/(ALfloat)Counter : 0.0f;
|
||||
ALfloat gain, delta, step;
|
||||
float32x4_t gain4;
|
||||
ALsizei c;
|
||||
|
||||
ASSUME(OutChans > 0);
|
||||
|
|
@ -175,55 +174,47 @@ void Mix_Neon(const ALfloat *data, ALsizei OutChans, ALfloat (*restrict OutBuffe
|
|||
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;
|
||||
ALfloat gain = CurrentGains[c];
|
||||
const ALfloat diff = TargetGains[c] - gain;
|
||||
|
||||
if(fabsf(diff) > FLT_EPSILON)
|
||||
gain = CurrentGains[c];
|
||||
step = (TargetGains[c] - gain) * delta;
|
||||
if(fabsf(step) > FLT_EPSILON)
|
||||
{
|
||||
ALsizei minsize = mini(BufferSize, Counter);
|
||||
const ALfloat step = diff * delta;
|
||||
ALfloat step_count = 0.0f;
|
||||
/* Mix with applying gain steps in aligned multiples of 4. */
|
||||
if(LIKELY(minsize > 3))
|
||||
if(minsize-pos > 3)
|
||||
{
|
||||
const float32x4_t four4 = vdupq_n_f32(4.0f);
|
||||
const float32x4_t step4 = vdupq_n_f32(step);
|
||||
const float32x4_t gain4 = vdupq_n_f32(gain);
|
||||
float32x4_t step_count4 = vsetq_lane_f32(0.0f,
|
||||
vsetq_lane_f32(1.0f,
|
||||
vsetq_lane_f32(2.0f,
|
||||
vsetq_lane_f32(3.0f, vdupq_n_f32(0.0f), 3),
|
||||
2), 1), 0
|
||||
);
|
||||
ALsizei todo = minsize >> 2;
|
||||
|
||||
float32x4_t step4;
|
||||
gain4 = vsetq_lane_f32(gain, gain4, 0);
|
||||
gain4 = vsetq_lane_f32(gain + step, gain4, 1);
|
||||
gain4 = vsetq_lane_f32(gain + step + step, gain4, 2);
|
||||
gain4 = vsetq_lane_f32(gain + step + step + step, gain4, 3);
|
||||
step4 = vdupq_n_f32(step + step + step + step);
|
||||
do {
|
||||
const float32x4_t val4 = vld1q_f32(&data[pos]);
|
||||
float32x4_t dry4 = vld1q_f32(&OutBuffer[c][OutPos+pos]);
|
||||
dry4 = vmlaq_f32(dry4, val4, vmlaq_f32(gain4, step4, step_count4));
|
||||
step_count4 = vaddq_f32(step_count4, four4);
|
||||
dry4 = vmlaq_f32(dry4, val4, gain4);
|
||||
gain4 = vaddq_f32(gain4, step4);
|
||||
vst1q_f32(&OutBuffer[c][OutPos+pos], dry4);
|
||||
pos += 4;
|
||||
} while(--todo);
|
||||
/* NOTE: step_count4 now represents the next four counts after
|
||||
* the last four mixed samples, so the lowest element
|
||||
* represents the next step count to apply.
|
||||
} 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.
|
||||
*/
|
||||
step_count = vgetq_lane_f32(step_count4, 0);
|
||||
gain = vgetq_lane_f32(gain4, 0);
|
||||
}
|
||||
/* Mix with applying left over gain steps that aren't aligned multiples of 4. */
|
||||
for(;pos < minsize;pos++)
|
||||
{
|
||||
OutBuffer[c][OutPos+pos] += data[pos]*(gain + step*step_count);
|
||||
step_count += 1.0f;
|
||||
OutBuffer[c][OutPos+pos] += data[pos]*gain;
|
||||
gain += step;
|
||||
}
|
||||
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. */
|
||||
|
|
@ -234,17 +225,13 @@ void Mix_Neon(const ALfloat *data, ALsizei OutChans, ALfloat (*restrict OutBuffe
|
|||
|
||||
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
|
||||
continue;
|
||||
if(LIKELY(BufferSize-pos > 3))
|
||||
gain4 = vdupq_n_f32(gain);
|
||||
for(;BufferSize-pos > 3;pos += 4)
|
||||
{
|
||||
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);
|
||||
const float32x4_t val4 = vld1q_f32(&data[pos]);
|
||||
float32x4_t dry4 = vld1q_f32(&OutBuffer[c][OutPos+pos]);
|
||||
dry4 = vmlaq_f32(dry4, val4, gain4);
|
||||
vst1q_f32(&OutBuffer[c][OutPos+pos], dry4);
|
||||
}
|
||||
for(;pos < BufferSize;pos++)
|
||||
OutBuffer[c][OutPos+pos] += data[pos]*gain;
|
||||
|
|
@ -253,29 +240,28 @@ 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;
|
||||
|
||||
ASSUME(InChans > 0);
|
||||
ASSUME(BufferSize > 0);
|
||||
data = ASSUME_ALIGNED(data, 16);
|
||||
OutBuffer = ASSUME_ALIGNED(OutBuffer, 16);
|
||||
|
||||
for(c = 0;c < InChans;c++)
|
||||
{
|
||||
ALsizei pos = 0;
|
||||
const ALfloat gain = Gains[c];
|
||||
ALfloat gain = Gains[c];
|
||||
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
|
||||
continue;
|
||||
|
||||
if(LIKELY(BufferSize > 3))
|
||||
gain4 = vdupq_n_f32(gain);
|
||||
for(;BufferSize-pos > 3;pos += 4)
|
||||
{
|
||||
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);
|
||||
const float32x4_t val4 = vld1q_f32(&data[c][InPos+pos]);
|
||||
float32x4_t dry4 = vld1q_f32(&OutBuffer[pos]);
|
||||
dry4 = vmlaq_f32(dry4, val4, gain4);
|
||||
vst1q_f32(&OutBuffer[pos], dry4);
|
||||
}
|
||||
for(;pos < BufferSize;pos++)
|
||||
OutBuffer[pos] += data[c][InPos+pos]*gain;
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ const ALfloat *Resample_bsinc_SSE(const InterpState *state, const ALfloat *restr
|
|||
ASSUME(m > 0);
|
||||
ASSUME(dstlen > 0);
|
||||
|
||||
src -= state->bsinc.l;
|
||||
src += state->bsinc.l;
|
||||
for(i = 0;i < dstlen;i++)
|
||||
{
|
||||
// Calculate the phase index and factor.
|
||||
|
|
@ -45,21 +45,17 @@ 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 < count;j++)
|
||||
for(j = 0;j < m;j+=4,fil++,scd++,phd++,spd++)
|
||||
{
|
||||
/* f = ((fil + sf*scd) + pf*(phd + sf*spd)) */
|
||||
const __m128 f4 = MLA4(
|
||||
MLA4(fil[j], sf4, scd[j]),
|
||||
pf4, MLA4(phd[j], sf4, spd[j])
|
||||
MLA4(*fil, sf4, *scd),
|
||||
pf4, MLA4(*phd, sf4, *spd)
|
||||
);
|
||||
/* r += f*src */
|
||||
r4 = MLA4(r4, f4, _mm_loadu_ps(&src[j*4]));
|
||||
r4 = MLA4(r4, f4, _mm_loadu_ps(&src[j]));
|
||||
}
|
||||
#undef MLA4
|
||||
}
|
||||
|
|
@ -139,58 +135,55 @@ void Mix_SSE(const ALfloat *data, ALsizei OutChans, ALfloat (*restrict OutBuffer
|
|||
ALfloat *CurrentGains, const ALfloat *TargetGains, ALsizei Counter, ALsizei OutPos,
|
||||
ALsizei BufferSize)
|
||||
{
|
||||
const ALfloat delta = (Counter > 0) ? 1.0f/(ALfloat)Counter : 0.0f;
|
||||
ALfloat gain, delta, step;
|
||||
__m128 gain4;
|
||||
ALsizei c;
|
||||
|
||||
ASSUME(OutChans > 0);
|
||||
ASSUME(BufferSize > 0);
|
||||
delta = (Counter > 0) ? 1.0f/(ALfloat)Counter : 0.0f;
|
||||
|
||||
for(c = 0;c < OutChans;c++)
|
||||
{
|
||||
ALsizei pos = 0;
|
||||
ALfloat gain = CurrentGains[c];
|
||||
const ALfloat diff = TargetGains[c] - gain;
|
||||
|
||||
if(fabsf(diff) > FLT_EPSILON)
|
||||
gain = CurrentGains[c];
|
||||
step = (TargetGains[c] - gain) * delta;
|
||||
if(fabsf(step) > FLT_EPSILON)
|
||||
{
|
||||
ALsizei minsize = mini(BufferSize, Counter);
|
||||
const ALfloat step = diff * delta;
|
||||
ALfloat step_count = 0.0f;
|
||||
/* Mix with applying gain steps in aligned multiples of 4. */
|
||||
if(LIKELY(minsize > 3))
|
||||
if(minsize-pos > 3)
|
||||
{
|
||||
const __m128 four4 = _mm_set1_ps(4.0f);
|
||||
const __m128 step4 = _mm_set1_ps(step);
|
||||
const __m128 gain4 = _mm_set1_ps(gain);
|
||||
__m128 step_count4 = _mm_setr_ps(0.0f, 1.0f, 2.0f, 3.0f);
|
||||
ALsizei todo = minsize >> 2;
|
||||
__m128 step4;
|
||||
gain4 = _mm_setr_ps(
|
||||
gain,
|
||||
gain + step,
|
||||
gain + step + step,
|
||||
gain + step + step + step
|
||||
);
|
||||
step4 = _mm_set1_ps(step + step + step + step);
|
||||
do {
|
||||
const __m128 val4 = _mm_load_ps(&data[pos]);
|
||||
__m128 dry4 = _mm_load_ps(&OutBuffer[c][OutPos+pos]);
|
||||
#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
|
||||
dry4 = _mm_add_ps(dry4, _mm_mul_ps(val4, gain4));
|
||||
gain4 = _mm_add_ps(gain4, step4);
|
||||
_mm_store_ps(&OutBuffer[c][OutPos+pos], dry4);
|
||||
step_count4 = _mm_add_ps(step_count4, four4);
|
||||
pos += 4;
|
||||
} while(--todo);
|
||||
/* NOTE: step_count4 now represents the next four counts after
|
||||
* the last four mixed samples, so the lowest element
|
||||
* represents the next step count to apply.
|
||||
} 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.
|
||||
*/
|
||||
step_count = _mm_cvtss_f32(step_count4);
|
||||
gain = _mm_cvtss_f32(gain4);
|
||||
}
|
||||
/* Mix with applying left over gain steps that aren't aligned multiples of 4. */
|
||||
for(;pos < minsize;pos++)
|
||||
{
|
||||
OutBuffer[c][OutPos+pos] += data[pos]*(gain + step*step_count);
|
||||
step_count += 1.0f;
|
||||
OutBuffer[c][OutPos+pos] += data[pos]*gain;
|
||||
gain += step;
|
||||
}
|
||||
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. */
|
||||
|
|
@ -201,17 +194,13 @@ void Mix_SSE(const ALfloat *data, ALsizei OutChans, ALfloat (*restrict OutBuffer
|
|||
|
||||
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
|
||||
continue;
|
||||
if(LIKELY(BufferSize-pos > 3))
|
||||
gain4 = _mm_set1_ps(gain);
|
||||
for(;BufferSize-pos > 3;pos += 4)
|
||||
{
|
||||
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);
|
||||
const __m128 val4 = _mm_load_ps(&data[pos]);
|
||||
__m128 dry4 = _mm_load_ps(&OutBuffer[c][OutPos+pos]);
|
||||
dry4 = _mm_add_ps(dry4, _mm_mul_ps(val4, gain4));
|
||||
_mm_store_ps(&OutBuffer[c][OutPos+pos], dry4);
|
||||
}
|
||||
for(;pos < BufferSize;pos++)
|
||||
OutBuffer[c][OutPos+pos] += data[pos]*gain;
|
||||
|
|
@ -220,6 +209,7 @@ 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);
|
||||
|
|
@ -228,21 +218,17 @@ void MixRow_SSE(ALfloat *OutBuffer, const ALfloat *Gains, const ALfloat (*restri
|
|||
for(c = 0;c < InChans;c++)
|
||||
{
|
||||
ALsizei pos = 0;
|
||||
const ALfloat gain = Gains[c];
|
||||
ALfloat gain = Gains[c];
|
||||
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
|
||||
continue;
|
||||
|
||||
if(LIKELY(BufferSize > 3))
|
||||
gain4 = _mm_set1_ps(gain);
|
||||
for(;BufferSize-pos > 3;pos += 4)
|
||||
{
|
||||
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);
|
||||
const __m128 val4 = _mm_load_ps(&data[c][InPos+pos]);
|
||||
__m128 dry4 = _mm_load_ps(&OutBuffer[pos]);
|
||||
dry4 = _mm_add_ps(dry4, _mm_mul_ps(val4, gain4));
|
||||
_mm_store_ps(&OutBuffer[pos], dry4);
|
||||
}
|
||||
for(;pos < BufferSize;pos++)
|
||||
OutBuffer[pos] += data[c][InPos+pos]*gain;
|
||||
|
|
|
|||
|
|
@ -34,25 +34,23 @@ 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);
|
||||
alignas(16) ALsizei pos_[4], frac_[4];
|
||||
union { alignas(16) ALint i[4]; float f[4]; } pos_;
|
||||
union { alignas(16) ALsizei i[4]; float f[4]; } frac_;
|
||||
__m128i frac4, pos4;
|
||||
ALsizei todo, pos, i;
|
||||
ALint pos;
|
||||
ALsizei i;
|
||||
|
||||
ASSUME(numsamples > 0);
|
||||
|
||||
InitiatePositionArrays(frac, increment, frac_, pos_, 4);
|
||||
frac4 = _mm_setr_epi32(frac_[0], frac_[1], frac_[2], frac_[3]);
|
||||
pos4 = _mm_setr_epi32(pos_[0], pos_[1], pos_[2], pos_[3]);
|
||||
InitiatePositionArrays(frac, increment, frac_.i, pos_.i, 4);
|
||||
|
||||
todo = numsamples & ~3;
|
||||
for(i = 0;i < todo;i += 4)
|
||||
frac4 = _mm_castps_si128(_mm_load_ps(frac_.f));
|
||||
pos4 = _mm_castps_si128(_mm_load_ps(pos_.f));
|
||||
|
||||
for(i = 0;numsamples-i > 3;i += 4)
|
||||
{
|
||||
const 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]);
|
||||
const __m128 val1 = _mm_setr_ps(src[pos_.i[0]], src[pos_.i[1]], src[pos_.i[2]], src[pos_.i[3]]);
|
||||
const __m128 val2 = _mm_setr_ps(src[pos_.i[0]+1], src[pos_.i[1]+1], src[pos_.i[2]+1], src[pos_.i[3]+1]);
|
||||
|
||||
/* val1 + (val2-val1)*mu */
|
||||
const __m128 r0 = _mm_sub_ps(val2, val1);
|
||||
|
|
@ -64,15 +62,17 @@ 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 = _mm_cvtsi128_si32(pos4);
|
||||
pos = pos_.i[0];
|
||||
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,25 +35,23 @@ 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);
|
||||
alignas(16) ALsizei pos_[4], frac_[4];
|
||||
union { alignas(16) ALint i[4]; float f[4]; } pos_;
|
||||
union { alignas(16) ALsizei i[4]; float f[4]; } frac_;
|
||||
__m128i frac4, pos4;
|
||||
ALsizei todo, pos, i;
|
||||
ALint pos;
|
||||
ALsizei i;
|
||||
|
||||
ASSUME(numsamples > 0);
|
||||
|
||||
InitiatePositionArrays(frac, increment, frac_, pos_, 4);
|
||||
frac4 = _mm_setr_epi32(frac_[0], frac_[1], frac_[2], frac_[3]);
|
||||
pos4 = _mm_setr_epi32(pos_[0], pos_[1], pos_[2], pos_[3]);
|
||||
InitiatePositionArrays(frac, increment, frac_.i, pos_.i, 4);
|
||||
|
||||
todo = numsamples & ~3;
|
||||
for(i = 0;i < todo;i += 4)
|
||||
frac4 = _mm_castps_si128(_mm_load_ps(frac_.f));
|
||||
pos4 = _mm_castps_si128(_mm_load_ps(pos_.f));
|
||||
|
||||
for(i = 0;numsamples-i > 3;i += 4)
|
||||
{
|
||||
const 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]);
|
||||
const __m128 val1 = _mm_setr_ps(src[pos_.i[0]], src[pos_.i[1]], src[pos_.i[2]], src[pos_.i[3]]);
|
||||
const __m128 val2 = _mm_setr_ps(src[pos_.i[0]+1], src[pos_.i[1]+1], src[pos_.i[2]+1], src[pos_.i[3]+1]);
|
||||
|
||||
/* val1 + (val2-val1)*mu */
|
||||
const __m128 r0 = _mm_sub_ps(val2, val1);
|
||||
|
|
@ -65,15 +63,20 @@ 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 = _mm_cvtsi128_si32(pos4);
|
||||
pos = pos_.i[0];
|
||||
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, ALsizei *restrict pos_arr, ALsizei size);
|
||||
extern inline void InitiatePositionArrays(ALsizei frac, ALint increment, ALsizei *restrict frac_arr, ALint *restrict pos_arr, ALsizei size);
|
||||
|
||||
|
||||
/* BSinc24 requires up to 23 extra samples before the current position, and 24 after. */
|
||||
|
|
@ -197,11 +197,12 @@ void aluInitMixer(void)
|
|||
static void SendAsyncEvent(ALCcontext *context, ALuint enumtype, ALenum type,
|
||||
ALuint objid, ALuint param, const char *msg)
|
||||
{
|
||||
AsyncEvent evt = ASYNC_EVENT(enumtype);
|
||||
evt.u.user.type = type;
|
||||
evt.u.user.id = objid;
|
||||
evt.u.user.param = param;
|
||||
strcpy(evt.u.user.msg, msg);
|
||||
AsyncEvent evt;
|
||||
evt.EnumType = enumtype;
|
||||
evt.Type = type;
|
||||
evt.ObjectId = objid;
|
||||
evt.Param = param;
|
||||
strcpy(evt.Message, msg);
|
||||
if(ll_ringbuffer_write(context->AsyncEvents, (const char*)&evt, 1) == 1)
|
||||
alsem_post(&context->EventSem);
|
||||
}
|
||||
|
|
@ -486,7 +487,6 @@ 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,24 +499,23 @@ ALboolean MixSource(ALvoice *voice, ALuint SourceID, ALCcontext *Context, ALsize
|
|||
const ALubyte *Data = ALBuffer->data;
|
||||
Data += (pos*NumChannels + chan)*SampleSize;
|
||||
|
||||
DataSize = mini(SizeToDo, DataSize - pos);
|
||||
CompLen = maxi(CompLen, DataSize);
|
||||
|
||||
DataSize = minu(SizeToDo, DataSize - pos);
|
||||
LoadSamples(&SrcData[FilledAmt], Data, NumChannels,
|
||||
ALBuffer->FmtType, DataSize);
|
||||
}
|
||||
}
|
||||
if(UNLIKELY(!CompLen))
|
||||
if(pos > tmpiter->max_samples)
|
||||
pos -= tmpiter->max_samples;
|
||||
else
|
||||
{
|
||||
FilledAmt += CompLen;
|
||||
if(SrcBufferSize <= FilledAmt)
|
||||
break;
|
||||
FilledAmt += tmpiter->max_samples - pos;
|
||||
pos = 0;
|
||||
}
|
||||
tmpiter = ATOMIC_LOAD(&tmpiter->next, almemory_order_acquire);
|
||||
if(!tmpiter) tmpiter = BufferLoopItem;
|
||||
if(SrcBufferSize > FilledAmt)
|
||||
{
|
||||
tmpiter = ATOMIC_LOAD(&tmpiter->next, almemory_order_acquire);
|
||||
if(!tmpiter) tmpiter = BufferLoopItem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -730,10 +729,8 @@ 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_relaxed);
|
||||
BufferListItem = ATOMIC_LOAD(&BufferListItem->next, almemory_order_acquire);
|
||||
if(!BufferListItem && !(BufferListItem=BufferLoopItem))
|
||||
{
|
||||
isplaying = false;
|
||||
|
|
@ -741,6 +738,8 @@ ALboolean MixSource(ALvoice *voice, ALuint SourceID, ALCcontext *Context, ALsize
|
|||
DataPosFrac = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
DataPosInt -= BufferListItem->max_samples;
|
||||
}
|
||||
} while(isplaying && OutPos < SamplesToDo);
|
||||
|
||||
|
|
|
|||
|
|
@ -38,10 +38,9 @@
|
|||
#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 float ScaleAzimuthFront(float azimuth, float scale);
|
||||
extern inline void ComputePanGains(const MixParams *dry, const ALfloat*restrict coeffs, ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]);
|
||||
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]);
|
||||
|
||||
|
||||
static const ALsizei FuMa2ACN[MAX_AMBI_COEFFS] = {
|
||||
|
|
@ -68,15 +67,19 @@ static const ALsizei ACN2ACN[MAX_AMBI_COEFFS] = {
|
|||
};
|
||||
|
||||
|
||||
void CalcAmbiCoeffs(const ALfloat y, const ALfloat z, const ALfloat x, const ALfloat spread,
|
||||
ALfloat coeffs[MAX_AMBI_COEFFS])
|
||||
void CalcDirectionCoeffs(const ALfloat dir[3], 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] = 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 */
|
||||
coeffs[1] = 1.732050808f * y; /* ACN 1 = sqrt(3) * Y */
|
||||
coeffs[2] = 1.732050808f * z; /* ACN 2 = sqrt(3) * Z */
|
||||
coeffs[3] = 1.732050808f * x; /* ACN 3 = sqrt(3) * X */
|
||||
/* Second-order */
|
||||
coeffs[4] = 3.872983346f * x * y; /* ACN 4 = sqrt(15) * X * Y */
|
||||
coeffs[5] = 3.872983346f * y * z; /* ACN 5 = sqrt(15) * Y * Z */
|
||||
|
|
@ -150,8 +153,16 @@ void CalcAmbiCoeffs(const ALfloat y, const ALfloat z, const ALfloat x, const ALf
|
|||
}
|
||||
}
|
||||
|
||||
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*restrict coeffs, ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS])
|
||||
|
||||
void ComputePanningGainsMC(const ChannelConfig *chancoeffs, ALsizei numchans, ALsizei numcoeffs, const ALfloat coeffs[MAX_AMBI_COEFFS], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS])
|
||||
{
|
||||
ALsizei i, j;
|
||||
|
||||
|
|
@ -166,7 +177,7 @@ void ComputePanningGainsMC(const ChannelConfig *chancoeffs, ALsizei numchans, AL
|
|||
gains[i] = 0.0f;
|
||||
}
|
||||
|
||||
void ComputePanningGainsBF(const BFChannelConfig *chanmap, ALsizei numchans, const ALfloat*restrict 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])
|
||||
{
|
||||
ALsizei i;
|
||||
|
||||
|
|
@ -176,6 +187,31 @@ 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)
|
||||
{
|
||||
|
|
@ -392,12 +428,11 @@ 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);
|
||||
|
||||
for(i = 0;i < order+1;i++)
|
||||
device->NumChannelsPerOrder[i] = chans_per_order[i];
|
||||
device->Dry.NumChannelsPerOrder[i] = chans_per_order[i];
|
||||
for(;i < MAX_AMBI_ORDER+1;i++)
|
||||
device->NumChannelsPerOrder[i] = 0;
|
||||
device->Dry.NumChannelsPerOrder[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -781,10 +816,10 @@ static void InitHrtfPanning(ALCdevice *device)
|
|||
/* NOTE: azimuth goes clockwise. */
|
||||
static const struct AngularPoint AmbiPoints[] = {
|
||||
{ DEG2RAD( 90.0f), DEG2RAD( 0.0f) },
|
||||
{ DEG2RAD( 35.2643897f), DEG2RAD( 45.0f) },
|
||||
{ DEG2RAD( 35.2643897f), DEG2RAD( 135.0f) },
|
||||
{ DEG2RAD( 35.2643897f), DEG2RAD(-135.0f) },
|
||||
{ DEG2RAD( 35.2643897f), DEG2RAD( -45.0f) },
|
||||
{ DEG2RAD( 35.0f), DEG2RAD( 45.0f) },
|
||||
{ DEG2RAD( 35.0f), DEG2RAD( 135.0f) },
|
||||
{ DEG2RAD( 35.0f), DEG2RAD(-135.0f) },
|
||||
{ DEG2RAD( 35.0f), DEG2RAD( -45.0f) },
|
||||
{ DEG2RAD( 0.0f), DEG2RAD( 0.0f) },
|
||||
{ DEG2RAD( 0.0f), DEG2RAD( 45.0f) },
|
||||
{ DEG2RAD( 0.0f), DEG2RAD( 90.0f) },
|
||||
|
|
@ -793,10 +828,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.2643897f), DEG2RAD( 45.0f) },
|
||||
{ DEG2RAD(-35.2643897f), DEG2RAD( 135.0f) },
|
||||
{ DEG2RAD(-35.2643897f), DEG2RAD(-135.0f) },
|
||||
{ DEG2RAD(-35.2643897f), 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(-90.0f), DEG2RAD( 0.0f) },
|
||||
};
|
||||
static const ALfloat AmbiMatrixFOA[][MAX_AMBI_COEFFS] = {
|
||||
|
|
@ -852,6 +887,7 @@ 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)
|
||||
{
|
||||
|
|
@ -942,7 +978,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->NumChannelsPerOrder[i] = 0;
|
||||
device->Dry.NumChannelsPerOrder[i] = 0;
|
||||
|
||||
device->AvgSpeakerDist = 0.0f;
|
||||
memset(device->ChannelDelay, 0, sizeof(device->ChannelDelay));
|
||||
|
|
|
|||
|
|
@ -102,8 +102,8 @@ IF(NOT LIBTYPE)
|
|||
ENDIF()
|
||||
|
||||
SET(LIB_MAJOR_VERSION "1")
|
||||
SET(LIB_MINOR_VERSION "19")
|
||||
SET(LIB_REVISION "1")
|
||||
SET(LIB_MINOR_VERSION "18")
|
||||
SET(LIB_REVISION "2")
|
||||
SET(LIB_VERSION "${LIB_MAJOR_VERSION}.${LIB_MINOR_VERSION}.${LIB_REVISION}")
|
||||
|
||||
SET(EXPORT_DECL "")
|
||||
|
|
@ -432,48 +432,6 @@ 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)
|
||||
|
||||
|
|
@ -554,7 +512,6 @@ 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)
|
||||
|
|
@ -743,8 +700,6 @@ ENDIF()
|
|||
|
||||
|
||||
SET(COMMON_OBJS
|
||||
common/alcomplex.c
|
||||
common/alcomplex.h
|
||||
common/align.h
|
||||
common/almalloc.c
|
||||
common/almalloc.h
|
||||
|
|
@ -798,14 +753,12 @@ SET(ALC_OBJS
|
|||
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
|
||||
|
|
@ -902,7 +855,7 @@ IF(ALSOFT_REQUIRE_SSE2 AND NOT HAVE_SSE2)
|
|||
MESSAGE(FATAL_ERROR "Failed to enable required SSE2 CPU extensions")
|
||||
ENDIF()
|
||||
|
||||
OPTION(ALSOFT_REQUIRE_SSE3 "Require SSE3 support" OFF)
|
||||
OPTION(ALSOFT_REQUIRE_SSE2 "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)
|
||||
|
|
@ -1302,7 +1255,7 @@ ADD_CUSTOM_TARGET(native-tools
|
|||
VERBATIM
|
||||
)
|
||||
|
||||
option(ALSOFT_EMBED_HRTF_DATA "Embed the HRTF data files (increases library footprint)" ON)
|
||||
option(ALSOFT_EMBED_HRTF_DATA "Embed the HRTF data files (increases library footprint)" OFF)
|
||||
if(ALSOFT_EMBED_HRTF_DATA)
|
||||
MACRO(make_hrtf_header FILENAME VARNAME)
|
||||
SET(infile "${OpenAL_SOURCE_DIR}/hrtf/${FILENAME}")
|
||||
|
|
@ -1515,10 +1468,6 @@ 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)
|
||||
|
|
@ -1746,8 +1695,7 @@ 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} ex-common common
|
||||
OpenAL)
|
||||
PRIVATE ${LINKER_FLAGS} ${SDL2_LIBRARY} ${FFMPEG_LIBRARIES} common OpenAL)
|
||||
|
||||
IF(ALSOFT_INSTALL)
|
||||
INSTALL(TARGETS alffplay
|
||||
|
|
|
|||
|
|
@ -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,3 +435,47 @@ 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,24 +1,8 @@
|
|||
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.
|
||||
Implemented the Pitch Shifter effect.
|
||||
|
||||
Fixed compiling on FreeBSD systems that use freebsd-lib 9.1.
|
||||
|
||||
|
|
@ -32,14 +16,11 @@ openal-soft-1.19.0:
|
|||
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
|
||||
Added support fot 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.
|
||||
|
|
|
|||
|
|
@ -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 source pan for first-order device
|
||||
* output (FOAOut).
|
||||
* ambisonics signal and make a B-Format pan (ComputeFirstOrderGains) for
|
||||
* first-order device output (FOAOut).
|
||||
*/
|
||||
alignas(16) ALfloat WetBuffer[MAX_EFFECT_CHANNELS][BUFFERSIZE];
|
||||
} ALeffectslot;
|
||||
|
|
@ -160,14 +160,12 @@ 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,14 +12,12 @@ 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,
|
||||
|
|
@ -35,7 +33,7 @@ struct EffectList {
|
|||
int type;
|
||||
ALenum val;
|
||||
};
|
||||
#define EFFECTLIST_SIZE 14
|
||||
#define EFFECTLIST_SIZE 12
|
||||
extern const struct EffectList EffectList[EFFECTLIST_SIZE];
|
||||
|
||||
|
||||
|
|
@ -61,14 +59,12 @@ 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;
|
||||
|
|
@ -105,13 +101,6 @@ typedef union ALeffectProps {
|
|||
ALfloat LFReference;
|
||||
} Reverb;
|
||||
|
||||
struct {
|
||||
ALfloat AttackTime;
|
||||
ALfloat ReleaseTime;
|
||||
ALfloat Resonance;
|
||||
ALfloat PeakGain;
|
||||
} Autowah;
|
||||
|
||||
struct {
|
||||
ALint Waveform;
|
||||
ALint Phase;
|
||||
|
|
@ -156,12 +145,6 @@ typedef union ALeffectProps {
|
|||
ALfloat HighGain;
|
||||
} Equalizer;
|
||||
|
||||
struct {
|
||||
ALfloat Frequency;
|
||||
ALint LeftDirection;
|
||||
ALint RightDirection;
|
||||
} Fshifter;
|
||||
|
||||
struct {
|
||||
ALfloat Frequency;
|
||||
ALfloat HighPassCutoff;
|
||||
|
|
|
|||
|
|
@ -115,25 +115,15 @@ 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 __builtin_ctzl
|
||||
#define CTZ64(x) __builtin_ctzl(x)
|
||||
#else
|
||||
#define CTZ64 __builtin_ctzll
|
||||
#define CTZ64(x) __builtin_ctzll(x)
|
||||
#endif
|
||||
|
||||
#elif defined(HAVE_BITSCANFORWARD64_INTRINSIC)
|
||||
|
|
@ -144,7 +134,7 @@ inline int msvc64_ctz64(ALuint64 v)
|
|||
_BitScanForward64(&idx, v);
|
||||
return (int)idx;
|
||||
}
|
||||
#define CTZ64 msvc64_ctz64
|
||||
#define CTZ64(x) msvc64_ctz64(x)
|
||||
|
||||
#elif defined(HAVE_BITSCANFORWARD_INTRINSIC)
|
||||
|
||||
|
|
@ -158,7 +148,7 @@ inline int msvc_ctz64(ALuint64 v)
|
|||
}
|
||||
return (int)idx;
|
||||
}
|
||||
#define CTZ64 msvc_ctz64
|
||||
#define CTZ64(x) msvc_ctz64(x)
|
||||
|
||||
#else
|
||||
|
||||
|
|
@ -181,18 +171,14 @@ inline int fallback_ctz64(ALuint64 value)
|
|||
{
|
||||
return fallback_popcnt64(~value & (value - 1));
|
||||
}
|
||||
#define CTZ64 fallback_ctz64
|
||||
#define CTZ64(x) fallback_ctz64(x)
|
||||
#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]))
|
||||
|
||||
|
|
@ -263,7 +249,7 @@ inline ALint fastf2i(ALfloat f)
|
|||
#ifdef __SSE_MATH__
|
||||
__asm__("cvtss2si %1, %0" : "=r"(i) : "x"(f));
|
||||
#else
|
||||
__asm__ __volatile__("fistpl %0" : "=m"(i) : "t"(f) : "st");
|
||||
__asm__("flds %1\n fistps %0" : "=m"(i) : "m"(f));
|
||||
#endif
|
||||
return i;
|
||||
|
||||
|
|
@ -285,85 +271,8 @@ inline ALint fastf2i(ALfloat f)
|
|||
/* 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
|
||||
|
||||
/* TODO: Make a more efficient method for x87. */
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -582,7 +491,7 @@ typedef struct DistanceComp {
|
|||
*/
|
||||
#define BUFFERSIZE 2048
|
||||
|
||||
typedef struct MixParams {
|
||||
typedef struct DryMixParams {
|
||||
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
|
||||
|
|
@ -592,7 +501,17 @@ typedef struct MixParams {
|
|||
|
||||
ALfloat (*Buffer)[BUFFERSIZE];
|
||||
ALsizei NumChannels;
|
||||
} MixParams;
|
||||
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;
|
||||
|
||||
typedef struct RealMixParams {
|
||||
enum Channel ChannelName[MAX_OUTPUT_CHANNELS];
|
||||
|
|
@ -622,8 +541,6 @@ struct ALCdevice_struct {
|
|||
enum AmbiLayout AmbiLayout;
|
||||
enum AmbiNorm AmbiScale;
|
||||
|
||||
ALCenum LimiterState;
|
||||
|
||||
al_string DeviceName;
|
||||
|
||||
ATOMIC(ALCenum) LastError;
|
||||
|
|
@ -678,17 +595,15 @@ 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. */
|
||||
MixParams Dry;
|
||||
ALsizei NumChannelsPerOrder[MAX_AMBI_ORDER+1];
|
||||
DryMixParams Dry;
|
||||
|
||||
/* First-order ambisonics output, to be upsampled to the dry buffer if different. */
|
||||
MixParams FOAOut;
|
||||
BFMixParams FOAOut;
|
||||
|
||||
/* "Real" output, which will be written to the device buffer. May alias the
|
||||
* dry buffer.
|
||||
|
|
@ -753,35 +668,21 @@ 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;
|
||||
union {
|
||||
char dummy;
|
||||
struct {
|
||||
ALenum type;
|
||||
ALuint id;
|
||||
ALuint param;
|
||||
ALchar msg[1008];
|
||||
} user;
|
||||
struct ALeffectState *EffectState;
|
||||
} u;
|
||||
ALenum Type;
|
||||
ALuint ObjectId;
|
||||
ALuint Param;
|
||||
ALchar Message[1008];
|
||||
} AsyncEvent;
|
||||
#define ASYNC_EVENT(t) { t, { 0 } }
|
||||
|
||||
struct ALCcontext_struct {
|
||||
RefCount ref;
|
||||
|
|
@ -834,6 +735,7 @@ struct ALCcontext_struct {
|
|||
|
||||
ATOMIC(struct ALeffectslotArray*) ActiveAuxSlots;
|
||||
|
||||
almtx_t EventThrdLock;
|
||||
althrd_t EventThread;
|
||||
alsem_t EventSem;
|
||||
struct ll_ringbuffer *AsyncEvents;
|
||||
|
|
@ -863,6 +765,9 @@ 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);
|
||||
|
|
@ -908,9 +813,6 @@ 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. */
|
||||
ALsizei l; /* Left coefficient offset. */
|
||||
ALint 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.
|
||||
|
|
@ -430,35 +430,14 @@ 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 an OpenAL 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 a direction vector. The vector
|
||||
* must be normalized (unit length), and the spread is the angular width of the
|
||||
* sound (0...tau).
|
||||
*/
|
||||
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);
|
||||
}
|
||||
void CalcDirectionCoeffs(const ALfloat dir[3], ALfloat spread, ALfloat coeffs[MAX_AMBI_COEFFS]);
|
||||
|
||||
/**
|
||||
* CalcAngleCoeffs
|
||||
|
|
@ -469,40 +448,34 @@ inline void CalcDirectionCoeffs(const ALfloat dir[3], ALfloat spread, ALfloat co
|
|||
*/
|
||||
inline void CalcAngleCoeffs(ALfloat azimuth, ALfloat elevation, ALfloat spread, ALfloat coeffs[MAX_AMBI_COEFFS])
|
||||
{
|
||||
ALfloat x = -sinf(azimuth) * cosf(elevation);
|
||||
ALfloat y = sinf(elevation);
|
||||
ALfloat z = cosf(azimuth) * cosf(elevation);
|
||||
|
||||
CalcAmbiCoeffs(x, y, z, spread, coeffs);
|
||||
ALfloat dir[3] = {
|
||||
sinf(azimuth) * cosf(elevation),
|
||||
sinf(elevation),
|
||||
-cosf(azimuth) * cosf(elevation)
|
||||
};
|
||||
CalcDirectionCoeffs(dir, spread, coeffs);
|
||||
}
|
||||
|
||||
/**
|
||||
* ScaleAzimuthFront
|
||||
* CalcAnglePairwiseCoeffs
|
||||
*
|
||||
* Scales the given azimuth toward the side (+/- pi/2 radians) for positions in
|
||||
* front.
|
||||
* 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.
|
||||
*/
|
||||
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 CalcAnglePairwiseCoeffs(ALfloat azimuth, ALfloat elevation, ALfloat spread, ALfloat coeffs[MAX_AMBI_COEFFS]);
|
||||
|
||||
|
||||
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]);
|
||||
|
||||
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]);
|
||||
/**
|
||||
* ComputePanGains
|
||||
* ComputeDryPanGains
|
||||
*
|
||||
* Computes panning gains using the given channel decoder coefficients and the
|
||||
* 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.
|
||||
* pre-calculated direction or angle coefficients.
|
||||
*/
|
||||
inline void ComputePanGains(const MixParams *dry, const ALfloat*restrict coeffs, ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS])
|
||||
inline void ComputeDryPanGains(const DryMixParams *dry, const ALfloat coeffs[MAX_AMBI_COEFFS], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS])
|
||||
{
|
||||
if(dry->CoeffCount > 0)
|
||||
ComputePanningGainsMC(dry->Ambi.Coeffs, dry->NumChannels, dry->CoeffCount,
|
||||
|
|
@ -511,6 +484,23 @@ inline void ComputePanGains(const MixParams *dry, const ALfloat*restrict coeffs,
|
|||
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,14 +48,12 @@ 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 },
|
||||
|
|
@ -122,6 +120,12 @@ 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);
|
||||
|
|
@ -136,13 +140,6 @@ 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);
|
||||
}
|
||||
|
|
@ -753,9 +750,6 @@ 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,14 +38,12 @@ 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 },
|
||||
|
|
@ -535,13 +533,6 @@ 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;
|
||||
|
|
@ -593,12 +584,6 @@ 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,9 +28,6 @@
|
|||
#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);
|
||||
|
||||
|
|
@ -350,7 +347,7 @@ static void ALlowpass_setParamf(ALfilter *filter, ALCcontext *context, ALenum pa
|
|||
switch(param)
|
||||
{
|
||||
case AL_LOWPASS_GAIN:
|
||||
if(!(val >= FILTER_MIN_GAIN && val <= FILTER_MAX_GAIN))
|
||||
if(!(val >= AL_LOWPASS_MIN_GAIN && val <= AL_LOWPASS_MAX_GAIN))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Low-pass gain %f out of range", val);
|
||||
filter->Gain = val;
|
||||
break;
|
||||
|
|
@ -403,7 +400,7 @@ static void ALhighpass_setParamf(ALfilter *filter, ALCcontext *context, ALenum p
|
|||
switch(param)
|
||||
{
|
||||
case AL_HIGHPASS_GAIN:
|
||||
if(!(val >= FILTER_MIN_GAIN && val <= FILTER_MAX_GAIN))
|
||||
if(!(val >= AL_HIGHPASS_MIN_GAIN && val <= AL_HIGHPASS_MAX_GAIN))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "High-pass gain out of range");
|
||||
filter->Gain = val;
|
||||
break;
|
||||
|
|
@ -456,7 +453,7 @@ static void ALbandpass_setParamf(ALfilter *filter, ALCcontext *context, ALenum p
|
|||
switch(param)
|
||||
{
|
||||
case AL_BANDPASS_GAIN:
|
||||
if(!(val >= FILTER_MIN_GAIN && val <= FILTER_MAX_GAIN))
|
||||
if(!(val >= AL_BANDPASS_MIN_GAIN && val <= AL_BANDPASS_MAX_GAIN))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Band-pass gain out of range");
|
||||
filter->Gain = val;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -229,16 +229,17 @@ 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.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,
|
||||
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,
|
||||
(state==AL_INITIAL) ? "AL_INITIAL" :
|
||||
(state==AL_PLAYING) ? "AL_PLAYING" :
|
||||
(state==AL_PAUSED) ? "AL_PAUSED" :
|
||||
|
|
@ -1295,7 +1296,7 @@ static ALboolean GetSourcedv(ALsource *Source, ALCcontext *Context, SourceProp p
|
|||
*/
|
||||
values[0] = GetSourceSecOffset(Source, Context, &srcclock);
|
||||
almtx_lock(&device->BackendLock);
|
||||
clocktime = GetClockLatency(device);
|
||||
clocktime = V0(device->Backend,getClockLatency)();
|
||||
almtx_unlock(&device->BackendLock);
|
||||
if(srcclock == (ALuint64)clocktime.ClockTime)
|
||||
values[1] = (ALdouble)clocktime.Latency / 1000000000.0;
|
||||
|
|
@ -1559,7 +1560,7 @@ static ALboolean GetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp
|
|||
*/
|
||||
values[0] = GetSourceSampleOffset(Source, Context, &srcclock);
|
||||
almtx_lock(&device->BackendLock);
|
||||
clocktime = GetClockLatency(device);
|
||||
clocktime = V0(device->Backend,getClockLatency)();
|
||||
almtx_unlock(&device->BackendLock);
|
||||
if(srcclock == (ALuint64)clocktime.ClockTime)
|
||||
values[1] = clocktime.Latency;
|
||||
|
|
@ -2825,121 +2826,6 @@ 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,16 +6,19 @@
|
|||
#include "AL/alext.h"
|
||||
#include "alMain.h"
|
||||
#include "alError.h"
|
||||
#include "alAuxEffectSlot.h"
|
||||
#include "ringbuffer.h"
|
||||
|
||||
|
||||
int EventThread(void *arg)
|
||||
static int EventThread(void *arg)
|
||||
{
|
||||
ALCcontext *context = arg;
|
||||
bool quitnow = false;
|
||||
|
||||
while(!quitnow)
|
||||
/* Clear all pending posts on the semaphore. */
|
||||
while(alsem_trywait(&context->EventSem) == althrd_success)
|
||||
{
|
||||
}
|
||||
|
||||
while(1)
|
||||
{
|
||||
ALbitfieldSOFT enabledevts;
|
||||
AsyncEvent evt;
|
||||
|
|
@ -25,24 +28,14 @@ int EventThread(void *arg)
|
|||
alsem_wait(&context->EventSem);
|
||||
continue;
|
||||
}
|
||||
if(!evt.EnumType)
|
||||
break;
|
||||
|
||||
almtx_lock(&context->EventCbLock);
|
||||
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);
|
||||
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);
|
||||
almtx_unlock(&context->EventCbLock);
|
||||
}
|
||||
return 0;
|
||||
|
|
@ -53,6 +46,7 @@ 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();
|
||||
|
|
@ -80,9 +74,13 @@ 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)
|
||||
{
|
||||
|
|
@ -90,20 +88,35 @@ 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)
|
||||
{
|
||||
}
|
||||
/* Wait to ensure the event handler sees the changed flags before
|
||||
* returning.
|
||||
*/
|
||||
almtx_lock(&context->EventCbLock);
|
||||
almtx_unlock(&context->EventCbLock);
|
||||
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);
|
||||
}
|
||||
}
|
||||
almtx_unlock(&context->EventThrdLock);
|
||||
|
||||
done:
|
||||
ALCcontext_DecRef(context);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
version: 1.19.0.{build}
|
||||
version: 1.18.2.{build}
|
||||
|
||||
environment:
|
||||
matrix:
|
||||
|
|
|
|||
|
|
@ -1,92 +0,0 @@
|
|||
|
||||
#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);
|
||||
}
|
||||
|
|
@ -1,71 +0,0 @@
|
|||
#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,12 +18,6 @@
|
|||
#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];
|
||||
|
|
@ -46,20 +40,7 @@ static inline float cbrtf(float f)
|
|||
}
|
||||
#endif
|
||||
|
||||
#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))
|
||||
#define DEG2RAD(x) ((float)(x) * (F_PI/180.0f))
|
||||
#define RAD2DEG(x) ((float)(x) * (180.0f/F_PI))
|
||||
|
||||
#endif /* AL_MATH_DEFS_H */
|
||||
|
|
|
|||
|
|
@ -428,11 +428,7 @@ void althrd_thread_detach(void)
|
|||
{
|
||||
void *ptr = altss_get(TlsDestructors.keys[i]);
|
||||
altss_dtor_t callback = (altss_dtor_t)TlsDestructors.values[i];
|
||||
if(ptr)
|
||||
{
|
||||
if(callback) callback(ptr);
|
||||
altss_set(TlsDestructors.keys[i], NULL);
|
||||
}
|
||||
if(ptr && callback) callback(ptr);
|
||||
}
|
||||
UnlockUIntMapRead(&TlsDestructors);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -98,9 +98,6 @@
|
|||
/* 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
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue