Restructure and clean up alu.cpp a bit

This commit is contained in:
Chris Robinson
2018-11-23 13:12:48 -08:00
parent 438e626993
commit fb94cdcfd3
+167 -152
View File
@@ -230,7 +230,7 @@ namespace {
* and starting with a seed value of 22222, is suitable for generating
* whitenoise.
*/
inline ALuint dither_rng(ALuint *seed)
inline ALuint dither_rng(ALuint *seed) noexcept
{
*seed = (*seed * 96314165) + 907633515;
return *seed;
@@ -291,7 +291,7 @@ void SendSourceStoppedEvent(ALCcontext *context, ALuint id)
size_t strpos;
ALuint scale;
enabledevt = ATOMIC_LOAD(&context->EnabledEvts, almemory_order_acquire);
enabledevt = context->EnabledEvts.load(std::memory_order_acquire);
if(!(enabledevt&EventType_SourceStateChange)) return;
evt.u.user.type = AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT;
@@ -1456,10 +1456,7 @@ void CalcAttnSourceParams(ALvoice *voice, const struct ALvoiceProps *props, cons
void CalcSourceParams(ALvoice *voice, ALCcontext *context, bool force)
{
ALbufferlistitem *BufferListItem;
struct ALvoiceProps *props;
props = voice->Update.exchange(nullptr, std::memory_order_acq_rel);
ALvoiceProps *props{voice->Update.exchange(nullptr, std::memory_order_acq_rel)};
if(!props && !force) return;
if(props)
@@ -1472,52 +1469,98 @@ void CalcSourceParams(ALvoice *voice, ALCcontext *context, bool force)
}
props = voice->Props;
BufferListItem = ATOMIC_LOAD(&voice->current_buffer, almemory_order_relaxed);
while(BufferListItem != NULL)
ALbufferlistitem *BufferListItem{voice->current_buffer.load(std::memory_order_relaxed)};
while(BufferListItem)
{
const ALbuffer *buffer = NULL;
ALsizei i = 0;
while(!buffer && i < BufferListItem->num_buffers)
buffer = BufferListItem->buffers[i];
if(LIKELY(buffer))
auto buffers_end = BufferListItem->buffers+BufferListItem->num_buffers;
auto buffer = std::find_if(BufferListItem->buffers, buffers_end,
[](const ALbuffer *buffer) noexcept -> bool
{ return buffer != nullptr; }
);
if(LIKELY(buffer != buffers_end))
{
if(props->SpatializeMode == SpatializeOn ||
(props->SpatializeMode == SpatializeAuto && buffer->FmtChannels == FmtMono))
CalcAttnSourceParams(voice, props, buffer, context);
(props->SpatializeMode == SpatializeAuto && (*buffer)->FmtChannels == FmtMono))
CalcAttnSourceParams(voice, props, *buffer, context);
else
CalcNonAttnSourceParams(voice, props, buffer, context);
CalcNonAttnSourceParams(voice, props, *buffer, context);
break;
}
BufferListItem = ATOMIC_LOAD(&BufferListItem->next, almemory_order_acquire);
BufferListItem = BufferListItem->next.load(std::memory_order_acquire);
}
}
void ProcessParamUpdates(ALCcontext *ctx, const struct ALeffectslotArray *slots)
void ProcessParamUpdates(ALCcontext *ctx, const ALeffectslotArray *slots)
{
ALvoice **voice, **voice_end;
ALsource *source;
ALsizei i;
IncrementRef(&ctx->UpdateCount);
if(!ATOMIC_LOAD(&ctx->HoldUpdates, almemory_order_acquire))
if(LIKELY(!ctx->HoldUpdates.load(std::memory_order_acquire)))
{
bool cforce = CalcContextParams(ctx);
bool force = CalcListenerParams(ctx) | cforce;
for(i = 0;i < slots->count;i++)
force |= CalcEffectSlotParams(slots->slot[i], ctx, cforce);
std::for_each(slots->slot, slots->slot+slots->count,
[ctx,cforce,&force](ALeffectslot *slot) -> void
{ force |= CalcEffectSlotParams(slot, ctx, cforce); }
);
voice = ctx->Voices;
voice_end = voice + ctx->VoiceCount;
for(;voice != voice_end;++voice)
{
source = ATOMIC_LOAD(&(*voice)->Source, almemory_order_acquire);
if(source) CalcSourceParams(*voice, ctx, force);
}
std::for_each(ctx->Voices, ctx->Voices+ctx->VoiceCount,
[ctx,force](ALvoice *voice) -> void
{
ALsource *source{voice->Source.load(std::memory_order_acquire)};
if(source) CalcSourceParams(voice, ctx, force);
}
);
}
IncrementRef(&ctx->UpdateCount);
}
void ProcessContext(ALCcontext *ctx, ALsizei SamplesToDo)
{
const ALeffectslotArray *auxslots{ctx->ActiveAuxSlots.load(std::memory_order_acquire)};
/* Process pending propery updates for objects on the context. */
ProcessParamUpdates(ctx, auxslots);
/* Clear auxiliary effect slot mixing buffers. */
std::for_each(auxslots->slot, auxslots->slot+auxslots->count,
[SamplesToDo](ALeffectslot *slot) -> void
{
std::for_each(slot->WetBuffer, slot->WetBuffer+slot->NumChannels,
[SamplesToDo](ALfloat *buffer) -> void
{ std::fill_n(buffer, SamplesToDo, 0.0f); }
);
}
);
/* Process voices that have a playing source. */
std::for_each(ctx->Voices, ctx->Voices+ctx->VoiceCount,
[SamplesToDo,ctx](ALvoice *voice) -> void
{
ALsource *source{voice->Source.load(std::memory_order_acquire)};
if(!source) return;
if(!voice->Playing.load(std::memory_order_relaxed) || voice->Step < 1)
return;
if(!MixSource(voice, source->id, ctx, SamplesToDo))
{
voice->Source.store(nullptr, std::memory_order_relaxed);
voice->Playing.store(false, std::memory_order_release);
SendSourceStoppedEvent(ctx, source->id);
}
}
);
/* Process effects. */
std::for_each(auxslots->slot, auxslots->slot+auxslots->count,
[SamplesToDo](const ALeffectslot *slot) -> void
{
EffectState *state{slot->Params.mEffectState};
state->process(SamplesToDo, slot->WetBuffer, state->mOutBuffer,
state->mOutChannels);
}
);
}
void ApplyStablizer(FrontStablizer *Stablizer, ALfloat (*RESTRICT Buffer)[BUFFERSIZE],
int lidx, int ridx, int cidx, ALsizei SamplesToDo, ALsizei NumChannels)
@@ -1598,7 +1641,7 @@ void ApplyDistanceComp(ALfloat (*RESTRICT Samples)[BUFFERSIZE], const DistanceCo
auto out = std::copy(distbuf+SamplesToDo, distbuf+base, distbuf);
std::copy_n(inout, SamplesToDo, out);
}
std::transform(Values, Values+SamplesToDo, inout,
std::transform<ALfloat*RESTRICT>(Values, Values+SamplesToDo, inout,
[gain](ALfloat in) noexcept -> ALfloat
{ return in * gain; }
);
@@ -1608,29 +1651,29 @@ void ApplyDistanceComp(ALfloat (*RESTRICT Samples)[BUFFERSIZE], const DistanceCo
void ApplyDither(ALfloat (*RESTRICT Samples)[BUFFERSIZE], ALuint *dither_seed,
const ALfloat quant_scale, const ALsizei SamplesToDo, const ALsizei numchans)
{
ASSUME(numchans > 0);
/* Dithering. Generate whitenoise (uniform distribution of random values
* between -1 and +1) and add it to the sample values, after scaling up to
* the desired quantization depth amd before rounding.
*/
const ALfloat invscale = 1.0f / quant_scale;
ALuint seed = *dither_seed;
ALsizei c, i;
ASSUME(numchans > 0);
ASSUME(SamplesToDo > 0);
/* Dithering. Step 1, generate whitenoise (uniform distribution of random
* values between -1 and +1). Step 2 is to add the noise to the samples,
* before rounding and after scaling up to the desired quantization depth.
*/
for(c = 0;c < numchans;c++)
auto dither_channel = [&seed,invscale,quant_scale,SamplesToDo](ALfloat *buffer) -> void
{
ALfloat *RESTRICT samples = Samples[c];
for(i = 0;i < SamplesToDo;i++)
{
ALfloat val = samples[i] * quant_scale;
ALuint rng0 = dither_rng(&seed);
ALuint rng1 = dither_rng(&seed);
val += (ALfloat)(rng0*(1.0/UINT_MAX) - rng1*(1.0/UINT_MAX));
samples[i] = fast_roundf(val) * invscale;
}
}
ASSUME(SamplesToDo > 0);
std::transform(buffer, buffer+SamplesToDo, buffer,
[&seed,invscale,quant_scale](ALfloat sample) noexcept -> ALfloat
{
ALfloat val = sample * quant_scale;
ALuint rng0 = dither_rng(&seed);
ALuint rng1 = dither_rng(&seed);
val += (ALfloat)(rng0*(1.0/UINT_MAX) - rng1*(1.0/UINT_MAX));
return fast_roundf(val) * invscale;
}
);
};
std::for_each(Samples, Samples+numchans, dither_channel);
*dither_seed = seed;
}
@@ -1639,11 +1682,11 @@ void ApplyDither(ALfloat (*RESTRICT Samples)[BUFFERSIZE], ALuint *dither_seed,
* chokes on that given the inline specializations.
*/
template<typename T>
inline T SampleConv(ALfloat);
inline T SampleConv(ALfloat) noexcept;
template<> inline ALfloat SampleConv(ALfloat val)
template<> inline ALfloat SampleConv(ALfloat val) noexcept
{ return val; }
template<> inline ALint SampleConv(ALfloat val)
template<> inline ALint SampleConv(ALfloat val) noexcept
{
/* 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]
@@ -1652,17 +1695,17 @@ template<> inline ALint SampleConv(ALfloat val)
*/
return fastf2i(clampf(val*16777216.0f, -16777216.0f, 16777215.0f))<<7;
}
template<> inline ALshort SampleConv(ALfloat val)
template<> inline ALshort SampleConv(ALfloat val) noexcept
{ return fastf2i(clampf(val*32768.0f, -32768.0f, 32767.0f)); }
template<> inline ALbyte SampleConv(ALfloat val)
template<> inline ALbyte SampleConv(ALfloat val) noexcept
{ return fastf2i(clampf(val*128.0f, -128.0f, 127.0f)); }
/* Define unsigned output variations. */
template<> inline ALuint SampleConv(ALfloat val)
template<> inline ALuint SampleConv(ALfloat val) noexcept
{ return SampleConv<ALint>(val) + 2147483648u; }
template<> inline ALushort SampleConv(ALfloat val)
template<> inline ALushort SampleConv(ALfloat val) noexcept
{ return SampleConv<ALshort>(val) + 32768; }
template<> inline ALubyte SampleConv(ALfloat val)
template<> inline ALubyte SampleConv(ALfloat val) noexcept
{ return SampleConv<ALbyte>(val) + 128; }
template<DevFmtType T>
@@ -1672,102 +1715,70 @@ void Write(const ALfloat (*RESTRICT InBuffer)[BUFFERSIZE], ALvoid *OutBuffer,
using SampleType = typename DevFmtTypeTraits<T>::Type;
ASSUME(numchans > 0);
ASSUME(SamplesToDo > 0);
for(ALsizei j{0};j < numchans;j++)
SampleType *outbase = static_cast<SampleType*>(OutBuffer) + Offset*numchans;
auto conv_channel = [&outbase,SamplesToDo,numchans](const ALfloat *inbuf) -> void
{
const ALfloat *RESTRICT in = InBuffer[j];
SampleType *RESTRICT out = static_cast<SampleType*>(OutBuffer) + Offset*numchans + j;
for(ALsizei i{0};i < SamplesToDo;i++)
out[i*numchans] = SampleConv<SampleType>(in[i]);
}
ASSUME(SamplesToDo > 0);
SampleType *out{outbase++};
std::for_each<const ALfloat*RESTRICT>(inbuf, inbuf+SamplesToDo,
[numchans,&out](const ALfloat s) noexcept -> void
{
*out = SampleConv<SampleType>(s);
out += numchans;
}
);
};
std::for_each(InBuffer, InBuffer+numchans, conv_channel);
}
} // namespace
void aluMixData(ALCdevice *device, ALvoid *OutBuffer, ALsizei NumSamples)
{
ALsizei SamplesToDo;
ALsizei SamplesDone;
ALCcontext *ctx;
ALsizei i, c;
FPUCtl mixer_mode{};
for(SamplesDone = 0;SamplesDone < NumSamples;)
for(ALsizei SamplesDone{0};SamplesDone < NumSamples;)
{
SamplesToDo = mini(NumSamples-SamplesDone, BUFFERSIZE);
for(c = 0;c < device->Dry.NumChannels;c++)
memset(device->Dry.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
if(device->Dry.Buffer != device->FOAOut.Buffer)
for(c = 0;c < device->FOAOut.NumChannels;c++)
memset(device->FOAOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
if(device->Dry.Buffer != device->RealOut.Buffer)
for(c = 0;c < device->RealOut.NumChannels;c++)
memset(device->RealOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
const ALsizei SamplesToDo{mini(NumSamples-SamplesDone, BUFFERSIZE)};
/* Clear main mixing buffers. */
std::for_each(device->MixBuffer.begin(), device->MixBuffer.end(),
[SamplesToDo](std::array<ALfloat,BUFFERSIZE> &buffer) -> void
{ std::fill_n(buffer.begin(), SamplesToDo, 0.0f); }
);
/* Increment the mix count at the start (lsb should now be 1). */
IncrementRef(&device->MixCount);
ctx = ATOMIC_LOAD(&device->ContextList, almemory_order_acquire);
/* For each context on this device, process and mix its sources and
* effects.
*/
ALCcontext *ctx{device->ContextList.load(std::memory_order_acquire)};
while(ctx)
{
const struct ALeffectslotArray *auxslots;
ProcessContext(ctx, SamplesToDo);
auxslots = ATOMIC_LOAD(&ctx->ActiveAuxSlots, almemory_order_acquire);
ProcessParamUpdates(ctx, auxslots);
for(i = 0;i < auxslots->count;i++)
{
ALeffectslot *slot = auxslots->slot[i];
for(c = 0;c < slot->NumChannels;c++)
memset(slot->WetBuffer[c], 0, SamplesToDo*sizeof(ALfloat));
}
/* source processing */
for(i = 0;i < ctx->VoiceCount;i++)
{
ALvoice *voice = ctx->Voices[i];
ALsource *source = ATOMIC_LOAD(&voice->Source, almemory_order_acquire);
if(source && ATOMIC_LOAD(&voice->Playing, almemory_order_relaxed) &&
voice->Step > 0)
{
if(!MixSource(voice, source->id, ctx, SamplesToDo))
{
ATOMIC_STORE(&voice->Source, static_cast<ALsource*>(nullptr),
almemory_order_relaxed);
ATOMIC_STORE(&voice->Playing, false, almemory_order_release);
SendSourceStoppedEvent(ctx, source->id);
}
}
}
/* effect slot processing */
for(i = 0;i < auxslots->count;i++)
{
const ALeffectslot *slot = auxslots->slot[i];
EffectState *state = slot->Params.mEffectState;
state->process(SamplesToDo, slot->WetBuffer, state->mOutBuffer,
state->mOutChannels);
}
ctx = ATOMIC_LOAD(&ctx->next, almemory_order_relaxed);
ctx = ctx->next.load(std::memory_order_relaxed);
}
/* Increment the clock time. Every second's worth of samples is
* converted and added to clock base so that large sample counts don't
* overflow during conversion. This also guarantees an exact, stable
* conversion. */
* overflow during conversion. This also guarantees a stable
* conversion.
*/
device->SamplesDone += SamplesToDo;
device->ClockBase += std::chrono::seconds{device->SamplesDone / device->Frequency};
device->SamplesDone %= device->Frequency;
/* Increment the mix count at the end (lsb should now be 0). */
IncrementRef(&device->MixCount);
/* Apply post-process for finalizing the Dry mix to the RealOut
* (Ambisonic decode, UHJ encode, etc).
/* Apply any needed post-process for finalizing the Dry mix to the
* RealOut (Ambisonic decode, UHJ encode, etc).
*/
if(LIKELY(device->PostProcess))
device->PostProcess(device, SamplesToDo);
/* Apply front image stablization for surround sound, if applicable. */
if(device->Stablizer)
{
int lidx = GetChannelIdxByName(&device->RealOut, FrontLeft);
@@ -1779,12 +1790,17 @@ void aluMixData(ALCdevice *device, ALvoid *OutBuffer, ALsizei NumSamples)
SamplesToDo, device->RealOut.NumChannels);
}
/* Apply delays and attenuation for mismatched speaker distances. */
ApplyDistanceComp(device->RealOut.Buffer, device->ChannelDelay, device->TempBuffer[0],
SamplesToDo, device->RealOut.NumChannels);
/* Apply compression, limiting final sample amplitude, if desired. */
if(device->Limiter)
ApplyCompression(device->Limiter.get(), SamplesToDo, device->RealOut.Buffer);
/* Apply dithering. The compressor should have left enough headroom for
* the dither noise to not saturate.
*/
if(device->DitherDepth > 0.0f)
ApplyDither(device->RealOut.Buffer, &device->DitherSeed, device->DitherDepth,
SamplesToDo, device->RealOut.NumChannels);
@@ -1794,6 +1810,9 @@ void aluMixData(ALCdevice *device, ALvoid *OutBuffer, ALsizei NumSamples)
ALfloat (*Buffer)[BUFFERSIZE] = device->RealOut.Buffer;
ALsizei Channels = device->RealOut.NumChannels;
/* Finally, interleave and convert samples, writing to the device's
* output buffer.
*/
switch(device->FmtType)
{
#define HANDLE_WRITE(T) case T: \
@@ -1816,50 +1835,46 @@ void aluMixData(ALCdevice *device, ALvoid *OutBuffer, ALsizei NumSamples)
void aluHandleDisconnect(ALCdevice *device, const char *msg, ...)
{
AsyncEvent evt = ASYNC_EVENT(EventType_Disconnected);
ALCcontext *ctx;
va_list args;
int msglen;
if(!device->Connected.exchange(AL_FALSE, std::memory_order_acq_rel))
return;
AsyncEvent evt = ASYNC_EVENT(EventType_Disconnected);
evt.u.user.type = AL_EVENT_TYPE_DISCONNECTED_SOFT;
evt.u.user.id = 0;
evt.u.user.param = 0;
va_list args;
va_start(args, msg);
msglen = vsnprintf(evt.u.user.msg, sizeof(evt.u.user.msg), msg, args);
int msglen{vsnprintf(evt.u.user.msg, sizeof(evt.u.user.msg), 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;
ctx = ATOMIC_LOAD_SEQ(&device->ContextList);
ALCcontext *ctx{device->ContextList.load()};
while(ctx)
{
ALbitfieldSOFT enabledevt = ATOMIC_LOAD(&ctx->EnabledEvts, almemory_order_acquire);
ALsizei i;
ALbitfieldSOFT enabledevt = ctx->EnabledEvts.load(std::memory_order_acquire);
if((enabledevt&EventType_Disconnected) &&
ll_ringbuffer_write(ctx->AsyncEvents, &evt, 1) == 1)
alsem_post(&ctx->EventSem);
for(i = 0;i < ctx->VoiceCount;i++)
{
ALvoice *voice = ctx->Voices[i];
ALsource *source = voice->Source.exchange(nullptr, std::memory_order_relaxed);
if(source && voice->Playing.load(std::memory_order_relaxed))
std::for_each(ctx->Voices, ctx->Voices+ctx->VoiceCount,
[ctx](ALvoice *voice) -> void
{
/* If the source's voice was playing, it's now effectively
* stopped (the source state will be updated the next time it's
* checked).
*/
SendSourceStoppedEvent(ctx, source->id);
ALsource *source{voice->Source.exchange(nullptr, std::memory_order_relaxed)};
if(source && voice->Playing.load(std::memory_order_relaxed))
{
/* If the source's voice was playing, it's now effectively
* stopped (the source state will be updated the next time
* it's checked).
*/
SendSourceStoppedEvent(ctx, source->id);
}
voice->Playing.store(false, std::memory_order_release);
}
voice->Playing.store(false, std::memory_order_release);
}
);
ctx = ATOMIC_LOAD(&ctx->next, almemory_order_relaxed);
ctx = ctx->next.load(std::memory_order_relaxed);
}
}