Revert "Updated SDL, Bullet and OpenAL soft libs"

This reverts commit 370161cfb1.
This commit is contained in:
AzaezelX 2019-07-08 09:49:44 -05:00
parent 160dc00c07
commit e7ee94428e
1102 changed files with 62741 additions and 204988 deletions

View file

@ -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 {

View file

@ -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)

View file

@ -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);

View file

@ -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
}
}

View file

@ -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)

View file

@ -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

View file

@ -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;
}
}

View file

@ -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();

View file

@ -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:

View file

@ -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))
{
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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))
{
}

View file

@ -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
}
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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
}
}

View file

@ -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;
}
}
}

View file

@ -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

View file

@ -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);

View file

@ -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;

View file

@ -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];
}
}

View file

@ -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);
}
}
}

View file

@ -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)

View file

@ -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)

View file

@ -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)

View file

@ -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);

View file

@ -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);
}

View file

@ -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 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);
}
}
}
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

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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);

View file

@ -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

View file

@ -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; }

View file

@ -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 */

View file

@ -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;

View file

@ -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,

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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));

View file

@ -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));

View file

@ -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);

View file

@ -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));

View file

@ -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

View file

@ -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!

View file

@ -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.

View file

@ -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);

View file

@ -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;

View file

@ -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

View file

@ -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);

View file

@ -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);
}

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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);

View file

@ -1,4 +1,4 @@
version: 1.19.0.{build}
version: 1.18.2.{build}
environment:
matrix:

View file

@ -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 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);
}
}
}
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);
}

View file

@ -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 */

View file

@ -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 */

View file

@ -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);
}

View file

@ -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