|
|
@@ -39,6 +39,7 @@
|
|
|
#include "bformatdec.h"
|
|
|
#include "static_assert.h"
|
|
|
#include "ringbuffer.h"
|
|
|
+#include "filters/splitter.h"
|
|
|
|
|
|
#include "mixer/defs.h"
|
|
|
#include "fpu_modes.h"
|
|
|
@@ -144,16 +145,6 @@ static inline HrtfDirectMixerFunc SelectHrtfMixer(void)
|
|
|
}
|
|
|
|
|
|
|
|
|
-/* Prior to VS2013, MSVC lacks the round() family of functions. */
|
|
|
-#if defined(_MSC_VER) && _MSC_VER < 1800
|
|
|
-static float roundf(float val)
|
|
|
-{
|
|
|
- if(val < 0.0f)
|
|
|
- return ceilf(val-0.5f);
|
|
|
- return floorf(val+0.5f);
|
|
|
-}
|
|
|
-#endif
|
|
|
-
|
|
|
/* This RNG method was created based on the math found in opusdec. It's quick,
|
|
|
* and starting with a seed value of 22222, is suitable for generating
|
|
|
* whitenoise.
|
|
|
@@ -220,32 +211,31 @@ void aluInit(void)
|
|
|
|
|
|
static void SendSourceStoppedEvent(ALCcontext *context, ALuint id)
|
|
|
{
|
|
|
+ AsyncEvent evt = ASYNC_EVENT(EventType_SourceStateChange);
|
|
|
ALbitfieldSOFT enabledevt;
|
|
|
- AsyncEvent evt;
|
|
|
size_t strpos;
|
|
|
ALuint scale;
|
|
|
|
|
|
enabledevt = ATOMIC_LOAD(&context->EnabledEvts, almemory_order_acquire);
|
|
|
if(!(enabledevt&EventType_SourceStateChange)) return;
|
|
|
|
|
|
- evt.EnumType = EventType_SourceStateChange;
|
|
|
- evt.Type = AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT;
|
|
|
- evt.ObjectId = id;
|
|
|
- evt.Param = AL_STOPPED;
|
|
|
+ evt.u.user.type = AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT;
|
|
|
+ evt.u.user.id = id;
|
|
|
+ evt.u.user.param = AL_STOPPED;
|
|
|
|
|
|
/* Normally snprintf would be used, but this is called from the mixer and
|
|
|
* that function's not real-time safe, so we have to construct it manually.
|
|
|
*/
|
|
|
- strcpy(evt.Message, "Source ID "); strpos = 10;
|
|
|
+ strcpy(evt.u.user.msg, "Source ID "); strpos = 10;
|
|
|
scale = 1000000000;
|
|
|
while(scale > 0 && scale > id)
|
|
|
scale /= 10;
|
|
|
while(scale > 0)
|
|
|
{
|
|
|
- evt.Message[strpos++] = '0' + ((id/scale)%10);
|
|
|
+ evt.u.user.msg[strpos++] = '0' + ((id/scale)%10);
|
|
|
scale /= 10;
|
|
|
}
|
|
|
- strcpy(evt.Message+strpos, " state changed to AL_STOPPED");
|
|
|
+ strcpy(evt.u.user.msg+strpos, " state changed to AL_STOPPED");
|
|
|
|
|
|
if(ll_ringbuffer_write(context->AsyncEvents, (const char*)&evt, 1) == 1)
|
|
|
alsem_post(&context->EventSem);
|
|
|
@@ -304,26 +294,24 @@ static void ProcessUhj(ALCdevice *device, ALsizei SamplesToDo)
|
|
|
{
|
|
|
int lidx = GetChannelIdxByName(&device->RealOut, FrontLeft);
|
|
|
int ridx = GetChannelIdxByName(&device->RealOut, FrontRight);
|
|
|
- if(LIKELY(lidx != -1 && ridx != -1))
|
|
|
- {
|
|
|
- /* Encode to stereo-compatible 2-channel UHJ output. */
|
|
|
- EncodeUhj2(device->Uhj_Encoder,
|
|
|
- device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
|
|
|
- device->Dry.Buffer, SamplesToDo
|
|
|
- );
|
|
|
- }
|
|
|
+ assert(lidx != -1 && ridx != -1);
|
|
|
+
|
|
|
+ /* Encode to stereo-compatible 2-channel UHJ output. */
|
|
|
+ EncodeUhj2(device->Uhj_Encoder,
|
|
|
+ device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
|
|
|
+ device->Dry.Buffer, SamplesToDo
|
|
|
+ );
|
|
|
}
|
|
|
|
|
|
static void ProcessBs2b(ALCdevice *device, ALsizei SamplesToDo)
|
|
|
{
|
|
|
int lidx = GetChannelIdxByName(&device->RealOut, FrontLeft);
|
|
|
int ridx = GetChannelIdxByName(&device->RealOut, FrontRight);
|
|
|
- if(LIKELY(lidx != -1 && ridx != -1))
|
|
|
- {
|
|
|
- /* Apply binaural/crossfeed filter */
|
|
|
- bs2b_cross_feed(device->Bs2b, device->RealOut.Buffer[lidx],
|
|
|
- device->RealOut.Buffer[ridx], SamplesToDo);
|
|
|
- }
|
|
|
+ assert(lidx != -1 && ridx != -1);
|
|
|
+
|
|
|
+ /* Apply binaural/crossfeed filter */
|
|
|
+ bs2b_cross_feed(device->Bs2b, device->RealOut.Buffer[lidx],
|
|
|
+ device->RealOut.Buffer[ridx], SamplesToDo);
|
|
|
}
|
|
|
|
|
|
void aluSelectPostProcess(ALCdevice *device)
|
|
|
@@ -343,9 +331,7 @@ void aluSelectPostProcess(ALCdevice *device)
|
|
|
}
|
|
|
|
|
|
|
|
|
-/* Prepares the interpolator for a given rate (determined by increment). A
|
|
|
- * result of AL_FALSE indicates that the filter output will completely cut
|
|
|
- * the input signal.
|
|
|
+/* Prepares the interpolator for a given rate (determined by increment).
|
|
|
*
|
|
|
* With a bit of work, and a trade of memory for CPU cost, this could be
|
|
|
* modified for use with an interpolated increment for buttery-smooth pitch
|
|
|
@@ -353,29 +339,24 @@ void aluSelectPostProcess(ALCdevice *device)
|
|
|
*/
|
|
|
void BsincPrepare(const ALuint increment, BsincState *state, const BSincTable *table)
|
|
|
{
|
|
|
- ALfloat sf;
|
|
|
- ALsizei si;
|
|
|
+ ALfloat sf = 0.0f;
|
|
|
+ ALsizei si = BSINC_SCALE_COUNT-1;
|
|
|
|
|
|
if(increment > FRACTIONONE)
|
|
|
{
|
|
|
sf = (ALfloat)FRACTIONONE / increment;
|
|
|
sf = maxf(0.0f, (BSINC_SCALE_COUNT-1) * (sf-table->scaleBase) * table->scaleRange);
|
|
|
- si = fastf2i(sf);
|
|
|
+ si = float2int(sf);
|
|
|
/* The interpolation factor is fit to this diagonally-symmetric curve
|
|
|
* to reduce the transition ripple caused by interpolating different
|
|
|
* scales of the sinc function.
|
|
|
*/
|
|
|
sf = 1.0f - cosf(asinf(sf - si));
|
|
|
}
|
|
|
- else
|
|
|
- {
|
|
|
- sf = 0.0f;
|
|
|
- si = BSINC_SCALE_COUNT - 1;
|
|
|
- }
|
|
|
|
|
|
state->sf = sf;
|
|
|
state->m = table->m[si];
|
|
|
- state->l = -((state->m/2) - 1);
|
|
|
+ state->l = (state->m/2) - 1;
|
|
|
state->filter = table->Tab + table->filterOffset[si];
|
|
|
}
|
|
|
|
|
|
@@ -481,12 +462,40 @@ static bool CalcEffectSlotParams(ALeffectslot *slot, ALCcontext *context, bool f
|
|
|
slot->Params.AirAbsorptionGainHF = 1.0f;
|
|
|
}
|
|
|
|
|
|
- /* Swap effect states. No need to play with the ref counts since they
|
|
|
- * keep the same number of refs.
|
|
|
- */
|
|
|
state = props->State;
|
|
|
- props->State = slot->Params.EffectState;
|
|
|
- slot->Params.EffectState = state;
|
|
|
+
|
|
|
+ if(state == slot->Params.EffectState)
|
|
|
+ {
|
|
|
+ /* If the effect state is the same as current, we can decrement its
|
|
|
+ * count safely to remove it from the update object (it can't reach
|
|
|
+ * 0 refs since the current params also hold a reference).
|
|
|
+ */
|
|
|
+ DecrementRef(&state->Ref);
|
|
|
+ props->State = NULL;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ /* Otherwise, replace it and send off the old one with a release
|
|
|
+ * event.
|
|
|
+ */
|
|
|
+ AsyncEvent evt = ASYNC_EVENT(EventType_ReleaseEffectState);
|
|
|
+ evt.u.EffectState = slot->Params.EffectState;
|
|
|
+
|
|
|
+ slot->Params.EffectState = state;
|
|
|
+ props->State = NULL;
|
|
|
+
|
|
|
+ if(LIKELY(ll_ringbuffer_write(context->AsyncEvents, (const char*)&evt, 1) != 0))
|
|
|
+ alsem_post(&context->EventSem);
|
|
|
+ else
|
|
|
+ {
|
|
|
+ /* If writing the event failed, the queue was probably full.
|
|
|
+ * Store the old state in the property object where it can
|
|
|
+ * eventually be cleaned up sometime later (not ideal, but
|
|
|
+ * better than blocking or leaking).
|
|
|
+ */
|
|
|
+ props->State = evt.u.EffectState;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
ATOMIC_REPLACE_HEAD(struct ALeffectslotProps*, &context->FreeEffectslotProps, props);
|
|
|
}
|
|
|
@@ -662,24 +671,26 @@ static void CalcPanningAndFilters(ALvoice *voice, const ALfloat Azi, const ALflo
|
|
|
NfcFilterAdjust(&voice->Direct.Params[0].NFCtrlFilter, w0);
|
|
|
|
|
|
for(i = 0;i < MAX_AMBI_ORDER+1;i++)
|
|
|
- voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
|
|
|
+ voice->Direct.ChannelsPerOrder[i] = Device->NumChannelsPerOrder[i];
|
|
|
voice->Flags |= VOICE_HAS_NFC;
|
|
|
}
|
|
|
|
|
|
- if(Device->Render_Mode == StereoPair)
|
|
|
- CalcAnglePairwiseCoeffs(Azi, Elev, Spread, coeffs);
|
|
|
- else
|
|
|
- CalcAngleCoeffs(Azi, Elev, Spread, coeffs);
|
|
|
+ /* A scalar of 1.5 for plain stereo results in +/-60 degrees being
|
|
|
+ * moved to +/-90 degrees for direct right and left speaker
|
|
|
+ * responses.
|
|
|
+ */
|
|
|
+ CalcAngleCoeffs((Device->Render_Mode==StereoPair) ? ScaleAzimuthFront(Azi, 1.5f) : Azi,
|
|
|
+ Elev, Spread, coeffs);
|
|
|
|
|
|
/* NOTE: W needs to be scaled by sqrt(2) due to FuMa normalization. */
|
|
|
- ComputeDryPanGains(&Device->Dry, coeffs, DryGain*1.414213562f,
|
|
|
+ ComputePanGains(&Device->Dry, coeffs, DryGain*SQRTF_2,
|
|
|
voice->Direct.Params[0].Gains.Target);
|
|
|
for(i = 0;i < NumSends;i++)
|
|
|
{
|
|
|
const ALeffectslot *Slot = SendSlots[i];
|
|
|
if(Slot)
|
|
|
- ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
|
|
|
- coeffs, WetGain[i]*1.414213562f, voice->Send[i].Params[0].Gains.Target
|
|
|
+ ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels, coeffs,
|
|
|
+ WetGain[i]*SQRTF_2, voice->Send[i].Params[0].Gains.Target
|
|
|
);
|
|
|
}
|
|
|
}
|
|
|
@@ -688,8 +699,6 @@ static void CalcPanningAndFilters(ALvoice *voice, const ALfloat Azi, const ALflo
|
|
|
/* Local B-Format sources have their XYZ channels rotated according
|
|
|
* to the orientation.
|
|
|
*/
|
|
|
- const ALfloat sqrt_2 = sqrtf(2.0f);
|
|
|
- const ALfloat sqrt_3 = sqrtf(3.0f);
|
|
|
ALfloat N[3], V[3], U[3];
|
|
|
aluMatrixf matrix;
|
|
|
|
|
|
@@ -732,25 +741,25 @@ static void CalcPanningAndFilters(ALvoice *voice, const ALfloat Azi, const ALflo
|
|
|
* outputs on the columns.
|
|
|
*/
|
|
|
aluMatrixfSet(&matrix,
|
|
|
- // ACN0 ACN1 ACN2 ACN3
|
|
|
- sqrt_2, 0.0f, 0.0f, 0.0f, // Ambi W
|
|
|
- 0.0f, -N[0]*sqrt_3, N[1]*sqrt_3, -N[2]*sqrt_3, // Ambi X
|
|
|
- 0.0f, U[0]*sqrt_3, -U[1]*sqrt_3, U[2]*sqrt_3, // Ambi Y
|
|
|
- 0.0f, -V[0]*sqrt_3, V[1]*sqrt_3, -V[2]*sqrt_3 // Ambi Z
|
|
|
+ // ACN0 ACN1 ACN2 ACN3
|
|
|
+ SQRTF_2, 0.0f, 0.0f, 0.0f, // Ambi W
|
|
|
+ 0.0f, -N[0]*SQRTF_3, N[1]*SQRTF_3, -N[2]*SQRTF_3, // Ambi X
|
|
|
+ 0.0f, U[0]*SQRTF_3, -U[1]*SQRTF_3, U[2]*SQRTF_3, // Ambi Y
|
|
|
+ 0.0f, -V[0]*SQRTF_3, V[1]*SQRTF_3, -V[2]*SQRTF_3 // Ambi Z
|
|
|
);
|
|
|
|
|
|
voice->Direct.Buffer = Device->FOAOut.Buffer;
|
|
|
voice->Direct.Channels = Device->FOAOut.NumChannels;
|
|
|
for(c = 0;c < num_channels;c++)
|
|
|
- ComputeFirstOrderGains(&Device->FOAOut, matrix.m[c], DryGain,
|
|
|
- voice->Direct.Params[c].Gains.Target);
|
|
|
+ ComputePanGains(&Device->FOAOut, matrix.m[c], DryGain,
|
|
|
+ voice->Direct.Params[c].Gains.Target);
|
|
|
for(i = 0;i < NumSends;i++)
|
|
|
{
|
|
|
const ALeffectslot *Slot = SendSlots[i];
|
|
|
if(Slot)
|
|
|
{
|
|
|
for(c = 0;c < num_channels;c++)
|
|
|
- ComputeFirstOrderGainsBF(Slot->ChanMap, Slot->NumChannels,
|
|
|
+ ComputePanningGainsBF(Slot->ChanMap, Slot->NumChannels,
|
|
|
matrix.m[c], WetGain[i], voice->Send[i].Params[c].Gains.Target
|
|
|
);
|
|
|
}
|
|
|
@@ -906,17 +915,15 @@ static void CalcPanningAndFilters(ALvoice *voice, const ALfloat Azi, const ALflo
|
|
|
NfcFilterAdjust(&voice->Direct.Params[c].NFCtrlFilter, w0);
|
|
|
|
|
|
for(i = 0;i < MAX_AMBI_ORDER+1;i++)
|
|
|
- voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
|
|
|
+ voice->Direct.ChannelsPerOrder[i] = Device->NumChannelsPerOrder[i];
|
|
|
voice->Flags |= VOICE_HAS_NFC;
|
|
|
}
|
|
|
|
|
|
/* Calculate the directional coefficients once, which apply to all
|
|
|
* input channels.
|
|
|
*/
|
|
|
- if(Device->Render_Mode == StereoPair)
|
|
|
- CalcAnglePairwiseCoeffs(Azi, Elev, Spread, coeffs);
|
|
|
- else
|
|
|
- CalcAngleCoeffs(Azi, Elev, Spread, coeffs);
|
|
|
+ CalcAngleCoeffs((Device->Render_Mode==StereoPair) ? ScaleAzimuthFront(Azi, 1.5f) : Azi,
|
|
|
+ Elev, Spread, coeffs);
|
|
|
|
|
|
for(c = 0;c < num_channels;c++)
|
|
|
{
|
|
|
@@ -931,9 +938,8 @@ static void CalcPanningAndFilters(ALvoice *voice, const ALfloat Azi, const ALflo
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
- ComputeDryPanGains(&Device->Dry,
|
|
|
- coeffs, DryGain * downmix_gain, voice->Direct.Params[c].Gains.Target
|
|
|
- );
|
|
|
+ ComputePanGains(&Device->Dry, coeffs, DryGain * downmix_gain,
|
|
|
+ voice->Direct.Params[c].Gains.Target);
|
|
|
}
|
|
|
|
|
|
for(i = 0;i < NumSends;i++)
|
|
|
@@ -969,7 +975,7 @@ static void CalcPanningAndFilters(ALvoice *voice, const ALfloat Azi, const ALflo
|
|
|
NfcFilterAdjust(&voice->Direct.Params[c].NFCtrlFilter, w0);
|
|
|
|
|
|
for(i = 0;i < MAX_AMBI_ORDER+1;i++)
|
|
|
- voice->Direct.ChannelsPerOrder[i] = Device->Dry.NumChannelsPerOrder[i];
|
|
|
+ voice->Direct.ChannelsPerOrder[i] = Device->NumChannelsPerOrder[i];
|
|
|
voice->Flags |= VOICE_HAS_NFC;
|
|
|
}
|
|
|
|
|
|
@@ -988,14 +994,14 @@ static void CalcPanningAndFilters(ALvoice *voice, const ALfloat Azi, const ALflo
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
- if(Device->Render_Mode == StereoPair)
|
|
|
- CalcAnglePairwiseCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs);
|
|
|
- else
|
|
|
- CalcAngleCoeffs(chans[c].angle, chans[c].elevation, Spread, coeffs);
|
|
|
- ComputeDryPanGains(&Device->Dry,
|
|
|
- coeffs, DryGain, voice->Direct.Params[c].Gains.Target
|
|
|
+ CalcAngleCoeffs(
|
|
|
+ (Device->Render_Mode==StereoPair) ? ScaleAzimuthFront(chans[c].angle, 3.0f)
|
|
|
+ : chans[c].angle,
|
|
|
+ chans[c].elevation, Spread, coeffs
|
|
|
);
|
|
|
|
|
|
+ ComputePanGains(&Device->Dry, coeffs, DryGain,
|
|
|
+ voice->Direct.Params[c].Gains.Target);
|
|
|
for(i = 0;i < NumSends;i++)
|
|
|
{
|
|
|
const ALeffectslot *Slot = SendSlots[i];
|
|
|
@@ -1017,20 +1023,20 @@ static void CalcPanningAndFilters(ALvoice *voice, const ALfloat Azi, const ALflo
|
|
|
voice->Direct.FilterType = AF_None;
|
|
|
if(gainHF != 1.0f) voice->Direct.FilterType |= AF_LowPass;
|
|
|
if(gainLF != 1.0f) voice->Direct.FilterType |= AF_HighPass;
|
|
|
- BiquadState_setParams(
|
|
|
+ BiquadFilter_setParams(
|
|
|
&voice->Direct.Params[0].LowPass, BiquadType_HighShelf,
|
|
|
gainHF, hfScale, calc_rcpQ_from_slope(gainHF, 1.0f)
|
|
|
);
|
|
|
- BiquadState_setParams(
|
|
|
+ BiquadFilter_setParams(
|
|
|
&voice->Direct.Params[0].HighPass, BiquadType_LowShelf,
|
|
|
gainLF, lfScale, calc_rcpQ_from_slope(gainLF, 1.0f)
|
|
|
);
|
|
|
for(c = 1;c < num_channels;c++)
|
|
|
{
|
|
|
- BiquadState_copyParams(&voice->Direct.Params[c].LowPass,
|
|
|
- &voice->Direct.Params[0].LowPass);
|
|
|
- BiquadState_copyParams(&voice->Direct.Params[c].HighPass,
|
|
|
- &voice->Direct.Params[0].HighPass);
|
|
|
+ BiquadFilter_copyParams(&voice->Direct.Params[c].LowPass,
|
|
|
+ &voice->Direct.Params[0].LowPass);
|
|
|
+ BiquadFilter_copyParams(&voice->Direct.Params[c].HighPass,
|
|
|
+ &voice->Direct.Params[0].HighPass);
|
|
|
}
|
|
|
}
|
|
|
for(i = 0;i < NumSends;i++)
|
|
|
@@ -1043,20 +1049,20 @@ static void CalcPanningAndFilters(ALvoice *voice, const ALfloat Azi, const ALflo
|
|
|
voice->Send[i].FilterType = AF_None;
|
|
|
if(gainHF != 1.0f) voice->Send[i].FilterType |= AF_LowPass;
|
|
|
if(gainLF != 1.0f) voice->Send[i].FilterType |= AF_HighPass;
|
|
|
- BiquadState_setParams(
|
|
|
+ BiquadFilter_setParams(
|
|
|
&voice->Send[i].Params[0].LowPass, BiquadType_HighShelf,
|
|
|
gainHF, hfScale, calc_rcpQ_from_slope(gainHF, 1.0f)
|
|
|
);
|
|
|
- BiquadState_setParams(
|
|
|
+ BiquadFilter_setParams(
|
|
|
&voice->Send[i].Params[0].HighPass, BiquadType_LowShelf,
|
|
|
gainLF, lfScale, calc_rcpQ_from_slope(gainLF, 1.0f)
|
|
|
);
|
|
|
for(c = 1;c < num_channels;c++)
|
|
|
{
|
|
|
- BiquadState_copyParams(&voice->Send[i].Params[c].LowPass,
|
|
|
- &voice->Send[i].Params[0].LowPass);
|
|
|
- BiquadState_copyParams(&voice->Send[i].Params[c].HighPass,
|
|
|
- &voice->Send[i].Params[0].HighPass);
|
|
|
+ BiquadFilter_copyParams(&voice->Send[i].Params[c].LowPass,
|
|
|
+ &voice->Send[i].Params[0].LowPass);
|
|
|
+ BiquadFilter_copyParams(&voice->Send[i].Params[c].HighPass,
|
|
|
+ &voice->Send[i].Params[0].HighPass);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -1098,7 +1104,7 @@ static void CalcNonAttnSourceParams(ALvoice *voice, const struct ALvoiceProps *p
|
|
|
if(Pitch > (ALfloat)MAX_PITCH)
|
|
|
voice->Step = MAX_PITCH<<FRACTIONBITS;
|
|
|
else
|
|
|
- voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
|
|
|
+ voice->Step = maxi(fastf2i(Pitch * FRACTIONONE), 1);
|
|
|
if(props->Resampler == BSinc24Resampler)
|
|
|
BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc24);
|
|
|
else if(props->Resampler == BSinc12Resampler)
|
|
|
@@ -1459,7 +1465,7 @@ static void CalcAttnSourceParams(ALvoice *voice, const struct ALvoiceProps *prop
|
|
|
if(Pitch > (ALfloat)MAX_PITCH)
|
|
|
voice->Step = MAX_PITCH<<FRACTIONBITS;
|
|
|
else
|
|
|
- voice->Step = maxi(fastf2i(Pitch*FRACTIONONE + 0.5f), 1);
|
|
|
+ voice->Step = maxi(fastf2i(Pitch * FRACTIONONE), 1);
|
|
|
if(props->Resampler == BSinc24Resampler)
|
|
|
BsincPrepare(voice->Step, &voice->ResampleState.bsinc, &bsinc24);
|
|
|
else if(props->Resampler == BSinc12Resampler)
|
|
|
@@ -1628,7 +1634,7 @@ static void ApplyDistanceComp(ALfloat (*restrict Samples)[BUFFERSIZE], DistanceC
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
- if(SamplesToDo >= base)
|
|
|
+ if(LIKELY(SamplesToDo >= base))
|
|
|
{
|
|
|
for(i = 0;i < base;i++)
|
|
|
Values[i] = distbuf[i];
|
|
|
@@ -1656,6 +1662,9 @@ static void ApplyDither(ALfloat (*restrict Samples)[BUFFERSIZE], ALuint *dither_
|
|
|
ALuint seed = *dither_seed;
|
|
|
ALsizei c, i;
|
|
|
|
|
|
+ ASSUME(numchans > 0);
|
|
|
+ ASSUME(SamplesToDo > 0);
|
|
|
+
|
|
|
/* Dithering. Step 1, generate whitenoise (uniform distribution of random
|
|
|
* values between -1 and +1). Step 2 is to add the noise to the samples,
|
|
|
* before rounding and after scaling up to the desired quantization depth.
|
|
|
@@ -1669,7 +1678,7 @@ static void ApplyDither(ALfloat (*restrict Samples)[BUFFERSIZE], ALuint *dither_
|
|
|
ALuint rng0 = dither_rng(&seed);
|
|
|
ALuint rng1 = dither_rng(&seed);
|
|
|
val += (ALfloat)(rng0*(1.0/UINT_MAX) - rng1*(1.0/UINT_MAX));
|
|
|
- samples[i] = roundf(val) * invscale;
|
|
|
+ samples[i] = fast_roundf(val) * invscale;
|
|
|
}
|
|
|
}
|
|
|
*dither_seed = seed;
|
|
|
@@ -1680,9 +1689,10 @@ static inline ALfloat Conv_ALfloat(ALfloat val)
|
|
|
{ return val; }
|
|
|
static inline ALint Conv_ALint(ALfloat val)
|
|
|
{
|
|
|
- /* Floats only have a 24-bit mantissa, so [-16777216, +16777216] is the max
|
|
|
- * integer range normalized floats can be safely converted to (a bit of the
|
|
|
- * exponent helps out, effectively giving 25 bits).
|
|
|
+ /* Floats have a 23-bit mantissa. There is an implied 1 bit in the mantissa
|
|
|
+ * along with the sign bit, giving 25 bits total, so [-16777216, +16777216]
|
|
|
+ * is the max value a normalized float can be scaled to before losing
|
|
|
+ * precision.
|
|
|
*/
|
|
|
return fastf2i(clampf(val*16777216.0f, -16777216.0f, 16777215.0f))<<7;
|
|
|
}
|
|
|
@@ -1707,6 +1717,10 @@ static void Write##A(const ALfloat (*restrict InBuffer)[BUFFERSIZE], \
|
|
|
ALsizei numchans) \
|
|
|
{ \
|
|
|
ALsizei i, j; \
|
|
|
+ \
|
|
|
+ ASSUME(numchans > 0); \
|
|
|
+ ASSUME(SamplesToDo > 0); \
|
|
|
+ \
|
|
|
for(j = 0;j < numchans;j++) \
|
|
|
{ \
|
|
|
const ALfloat *restrict in = ASSUME_ALIGNED(InBuffer[j], 16); \
|
|
|
@@ -1824,41 +1838,29 @@ void aluMixData(ALCdevice *device, ALvoid *OutBuffer, ALsizei NumSamples)
|
|
|
SamplesToDo, device->RealOut.NumChannels);
|
|
|
|
|
|
if(device->Limiter)
|
|
|
- ApplyCompression(device->Limiter, device->RealOut.NumChannels, SamplesToDo,
|
|
|
- device->RealOut.Buffer);
|
|
|
+ ApplyCompression(device->Limiter, SamplesToDo, device->RealOut.Buffer);
|
|
|
|
|
|
if(device->DitherDepth > 0.0f)
|
|
|
ApplyDither(device->RealOut.Buffer, &device->DitherSeed, device->DitherDepth,
|
|
|
SamplesToDo, device->RealOut.NumChannels);
|
|
|
|
|
|
- if(OutBuffer)
|
|
|
+ if(LIKELY(OutBuffer))
|
|
|
{
|
|
|
ALfloat (*Buffer)[BUFFERSIZE] = device->RealOut.Buffer;
|
|
|
ALsizei Channels = device->RealOut.NumChannels;
|
|
|
|
|
|
switch(device->FmtType)
|
|
|
{
|
|
|
- case DevFmtByte:
|
|
|
- WriteI8(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
|
|
|
- break;
|
|
|
- case DevFmtUByte:
|
|
|
- WriteUI8(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
|
|
|
- break;
|
|
|
- case DevFmtShort:
|
|
|
- WriteI16(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
|
|
|
- break;
|
|
|
- case DevFmtUShort:
|
|
|
- WriteUI16(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
|
|
|
- break;
|
|
|
- case DevFmtInt:
|
|
|
- WriteI32(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
|
|
|
- break;
|
|
|
- case DevFmtUInt:
|
|
|
- WriteUI32(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
|
|
|
- break;
|
|
|
- case DevFmtFloat:
|
|
|
- WriteF32(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels);
|
|
|
- break;
|
|
|
+#define HANDLE_WRITE(T, S) case T: \
|
|
|
+ Write##S(Buffer, OutBuffer, SamplesDone, SamplesToDo, Channels); break;
|
|
|
+ HANDLE_WRITE(DevFmtByte, I8)
|
|
|
+ HANDLE_WRITE(DevFmtUByte, UI8)
|
|
|
+ HANDLE_WRITE(DevFmtShort, I16)
|
|
|
+ HANDLE_WRITE(DevFmtUShort, UI16)
|
|
|
+ HANDLE_WRITE(DevFmtInt, I32)
|
|
|
+ HANDLE_WRITE(DevFmtUInt, UI32)
|
|
|
+ HANDLE_WRITE(DevFmtFloat, F32)
|
|
|
+#undef HANDLE_WRITE
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -1870,35 +1872,24 @@ void aluMixData(ALCdevice *device, ALvoid *OutBuffer, ALsizei NumSamples)
|
|
|
|
|
|
void aluHandleDisconnect(ALCdevice *device, const char *msg, ...)
|
|
|
{
|
|
|
+ AsyncEvent evt = ASYNC_EVENT(EventType_Disconnected);
|
|
|
ALCcontext *ctx;
|
|
|
- AsyncEvent evt;
|
|
|
va_list args;
|
|
|
int msglen;
|
|
|
|
|
|
if(!ATOMIC_EXCHANGE(&device->Connected, AL_FALSE, almemory_order_acq_rel))
|
|
|
return;
|
|
|
|
|
|
- evt.EnumType = EventType_Disconnected;
|
|
|
- evt.Type = AL_EVENT_TYPE_DISCONNECTED_SOFT;
|
|
|
- evt.ObjectId = 0;
|
|
|
- evt.Param = 0;
|
|
|
+ evt.u.user.type = AL_EVENT_TYPE_DISCONNECTED_SOFT;
|
|
|
+ evt.u.user.id = 0;
|
|
|
+ evt.u.user.param = 0;
|
|
|
|
|
|
va_start(args, msg);
|
|
|
- msglen = vsnprintf(evt.Message, sizeof(evt.Message), msg, args);
|
|
|
+ msglen = vsnprintf(evt.u.user.msg, sizeof(evt.u.user.msg), msg, args);
|
|
|
va_end(args);
|
|
|
|
|
|
- if(msglen < 0 || (size_t)msglen >= sizeof(evt.Message))
|
|
|
- {
|
|
|
- evt.Message[sizeof(evt.Message)-1] = 0;
|
|
|
- msglen = (int)strlen(evt.Message);
|
|
|
- }
|
|
|
- if(msglen > 0)
|
|
|
- msg = evt.Message;
|
|
|
- else
|
|
|
- {
|
|
|
- msg = "<internal error constructing message>";
|
|
|
- msglen = (int)strlen(msg);
|
|
|
- }
|
|
|
+ if(msglen < 0 || (size_t)msglen >= sizeof(evt.u.user.msg))
|
|
|
+ evt.u.user.msg[sizeof(evt.u.user.msg)-1] = 0;
|
|
|
|
|
|
ctx = ATOMIC_LOAD_SEQ(&device->ContextList);
|
|
|
while(ctx)
|