aboutsummaryrefslogtreecommitdiffstats
path: root/alc/alu.cpp
diff options
context:
space:
mode:
authorSven Göthel <[email protected]>2024-01-05 13:52:12 +0100
committerSven Göthel <[email protected]>2024-01-05 13:52:12 +0100
commitec98cdacc85ff0202852472c7756586437912f22 (patch)
tree42414746a27ab35cb8cdbc95af521d74821e57f4 /alc/alu.cpp
parentfd5269bec9a5fe4815974b1786a037e6a247bfd2 (diff)
parentb82cd2e60edb8fbe5fdd3567105ae76a016a554c (diff)
Merge remote-tracking branch 'upstream/master'HEADmaster
Diffstat (limited to 'alc/alu.cpp')
-rw-r--r--alc/alu.cpp451
1 files changed, 239 insertions, 212 deletions
diff --git a/alc/alu.cpp b/alc/alu.cpp
index e0858b18..b50eaa41 100644
--- a/alc/alu.cpp
+++ b/alc/alu.cpp
@@ -29,6 +29,7 @@
#include <chrono>
#include <climits>
#include <cstdarg>
+#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <functional>
@@ -37,7 +38,6 @@
#include <memory>
#include <new>
#include <optional>
-#include <stdint.h>
#include <utility>
#include "almalloc.h"
@@ -141,7 +141,7 @@ using HrtfDirectMixerFunc = void(*)(const FloatBufferSpan LeftOut, const FloatBu
HrtfDirectMixerFunc MixDirectHrtf{MixDirectHrtf_<CTag>};
-inline HrtfDirectMixerFunc SelectHrtfMixer(void)
+inline HrtfDirectMixerFunc SelectHrtfMixer()
{
#ifdef HAVE_NEON
if((CPUCapFlags&CPU_CAP_NEON))
@@ -261,15 +261,15 @@ ResamplerFunc PrepareResampler(Resampler resampler, uint increment, InterpState
case Resampler::Linear:
break;
case Resampler::Cubic:
- state->cubic.filter = gCubicSpline.Tab.data();
+ state->emplace<CubicState>().filter = gCubicSpline.Tab.data();
break;
case Resampler::FastBSinc12:
case Resampler::BSinc12:
- BsincPrepare(increment, &state->bsinc, &gBSinc12);
+ BsincPrepare(increment, &state->emplace<BsincState>(), &gBSinc12);
break;
case Resampler::FastBSinc24:
case Resampler::BSinc24:
- BsincPrepare(increment, &state->bsinc, &gBSinc24);
+ BsincPrepare(increment, &state->emplace<BsincState>(), &gBSinc24);
break;
}
return SelectResampler(resampler, increment);
@@ -282,7 +282,7 @@ void DeviceBase::ProcessHrtf(const size_t SamplesToDo)
const size_t lidx{RealOut.ChannelIndex[FrontLeft]};
const size_t ridx{RealOut.ChannelIndex[FrontRight]};
- MixDirectHrtf(RealOut.Buffer[lidx], RealOut.Buffer[ridx], Dry.Buffer, HrtfAccumData,
+ MixDirectHrtf(RealOut.Buffer[lidx], RealOut.Buffer[ridx], Dry.Buffer, HrtfAccumData.data(),
mHrtfState->mTemp.data(), mHrtfState->mChannels.data(), mHrtfState->mIrSize, SamplesToDo);
}
@@ -323,8 +323,7 @@ void DeviceBase::ProcessBs2b(const size_t SamplesToDo)
const size_t ridx{RealOut.ChannelIndex[FrontRight]};
/* Now apply the BS2B binaural/crossfeed filter. */
- bs2b_cross_feed(Bs2b.get(), RealOut.Buffer[lidx].data(), RealOut.Buffer[ridx].data(),
- SamplesToDo);
+ Bs2b->cross_feed(RealOut.Buffer[lidx].data(), RealOut.Buffer[ridx].data(), SamplesToDo);
}
@@ -451,14 +450,14 @@ bool CalcEffectSlotParams(EffectSlot *slot, EffectSlot **sorted_slots, ContextBa
slot->Target = props->Target;
slot->EffectType = props->Type;
slot->mEffectProps = props->Props;
- if(props->Type == EffectSlotType::Reverb || props->Type == EffectSlotType::EAXReverb)
+ if(auto *reverbprops = std::get_if<ReverbProps>(&props->Props))
{
- slot->RoomRolloff = props->Props.Reverb.RoomRolloffFactor;
- slot->DecayTime = props->Props.Reverb.DecayTime;
- slot->DecayLFRatio = props->Props.Reverb.DecayLFRatio;
- slot->DecayHFRatio = props->Props.Reverb.DecayHFRatio;
- slot->DecayHFLimit = props->Props.Reverb.DecayHFLimit;
- slot->AirAbsorptionGainHF = props->Props.Reverb.AirAbsorptionGainHF;
+ slot->RoomRolloff = reverbprops->RoomRolloffFactor;
+ slot->DecayTime = reverbprops->DecayTime;
+ slot->DecayLFRatio = reverbprops->DecayLFRatio;
+ slot->DecayHFRatio = reverbprops->DecayHFRatio;
+ slot->DecayHFLimit = reverbprops->DecayHFLimit;
+ slot->AirAbsorptionGainHF = reverbprops->AirAbsorptionGainHF;
}
else
{
@@ -499,7 +498,7 @@ bool CalcEffectSlotParams(EffectSlot *slot, EffectSlot **sorted_slots, ContextBa
}
}
- AtomicReplaceHead(context->mFreeEffectslotProps, props);
+ AtomicReplaceHead(context->mFreeEffectSlotProps, props);
EffectTarget output;
if(EffectSlot *target{slot->Target})
@@ -679,16 +678,16 @@ void AmbiRotator(AmbiRotateMatrix &matrix, const int order)
auto P = [](const int i, const int l, const int a, const int n, const size_t last_band,
const AmbiRotateMatrix &R)
{
- const float ri1{ R[ 1+2][static_cast<size_t>(i+2)]};
- const float rim1{R[-1+2][static_cast<size_t>(i+2)]};
- const float ri0{ R[ 0+2][static_cast<size_t>(i+2)]};
+ const float ri1{ R[ 1+2][static_cast<size_t>(i+2_z)]};
+ const float rim1{R[-1+2][static_cast<size_t>(i+2_z)]};
+ const float ri0{ R[ 0+2][static_cast<size_t>(i+2_z)]};
const size_t y{last_band + static_cast<size_t>(a+l-1)};
if(n == -l)
- return ri1*R[last_band][y] + rim1*R[last_band + static_cast<size_t>(l-1)*2][y];
+ return ri1*R[last_band][y] + rim1*R[last_band + static_cast<size_t>(l-1_z)*2][y];
if(n == l)
- return ri1*R[last_band + static_cast<size_t>(l-1)*2][y] - rim1*R[last_band][y];
- return ri0*R[last_band + static_cast<size_t>(n+l-1)][y];
+ return ri1*R[last_band + static_cast<size_t>(l-1_z)*2][y] - rim1*R[last_band][y];
+ return ri0*R[last_band + static_cast<size_t>(l-1_z+n)][y];
};
auto U = [P](const int l, const int m, const int n, const size_t last_band,
@@ -776,48 +775,54 @@ struct GainTriplet { float Base, HF, LF; };
void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, const float zpos,
const float Distance, const float Spread, const GainTriplet &DryGain,
- const al::span<const GainTriplet,MAX_SENDS> WetGain, EffectSlot *(&SendSlots)[MAX_SENDS],
- const VoiceProps *props, const ContextParams &Context, DeviceBase *Device)
+ const al::span<const GainTriplet,MaxSendCount> WetGain,
+ const al::span<EffectSlot*,MaxSendCount> SendSlots, const VoiceProps *props,
+ const ContextParams &Context, DeviceBase *Device)
{
- static constexpr ChanPosMap MonoMap[1]{
- { FrontCenter, std::array{0.0f, 0.0f, -1.0f} }
- }, RearMap[2]{
- { BackLeft, std::array{-sin30, 0.0f, cos30} },
- { BackRight, std::array{ sin30, 0.0f, cos30} },
- }, QuadMap[4]{
- { FrontLeft, std::array{-sin45, 0.0f, -cos45} },
- { FrontRight, std::array{ sin45, 0.0f, -cos45} },
- { BackLeft, std::array{-sin45, 0.0f, cos45} },
- { BackRight, std::array{ sin45, 0.0f, cos45} },
- }, X51Map[6]{
- { FrontLeft, std::array{-sin30, 0.0f, -cos30} },
- { FrontRight, std::array{ sin30, 0.0f, -cos30} },
- { FrontCenter, std::array{ 0.0f, 0.0f, -1.0f} },
- { LFE, {} },
- { SideLeft, std::array{-sin110, 0.0f, -cos110} },
- { SideRight, std::array{ sin110, 0.0f, -cos110} },
- }, X61Map[7]{
- { FrontLeft, std::array{-sin30, 0.0f, -cos30} },
- { FrontRight, std::array{ sin30, 0.0f, -cos30} },
- { FrontCenter, std::array{ 0.0f, 0.0f, -1.0f} },
- { LFE, {} },
- { BackCenter, std::array{ 0.0f, 0.0f, 1.0f} },
- { SideLeft, std::array{-1.0f, 0.0f, 0.0f} },
- { SideRight, std::array{ 1.0f, 0.0f, 0.0f} },
- }, X71Map[8]{
- { FrontLeft, std::array{-sin30, 0.0f, -cos30} },
- { FrontRight, std::array{ sin30, 0.0f, -cos30} },
- { FrontCenter, std::array{ 0.0f, 0.0f, -1.0f} },
- { LFE, {} },
- { BackLeft, std::array{-sin30, 0.0f, cos30} },
- { BackRight, std::array{ sin30, 0.0f, cos30} },
- { SideLeft, std::array{ -1.0f, 0.0f, 0.0f} },
- { SideRight, std::array{ 1.0f, 0.0f, 0.0f} },
+ static constexpr std::array MonoMap{
+ ChanPosMap{FrontCenter, std::array{0.0f, 0.0f, -1.0f}}
+ };
+ static constexpr std::array RearMap{
+ ChanPosMap{BackLeft, std::array{-sin30, 0.0f, cos30}},
+ ChanPosMap{BackRight, std::array{ sin30, 0.0f, cos30}},
+ };
+ static constexpr std::array QuadMap{
+ ChanPosMap{FrontLeft, std::array{-sin45, 0.0f, -cos45}},
+ ChanPosMap{FrontRight, std::array{ sin45, 0.0f, -cos45}},
+ ChanPosMap{BackLeft, std::array{-sin45, 0.0f, cos45}},
+ ChanPosMap{BackRight, std::array{ sin45, 0.0f, cos45}},
+ };
+ static constexpr std::array X51Map{
+ ChanPosMap{FrontLeft, std::array{-sin30, 0.0f, -cos30}},
+ ChanPosMap{FrontRight, std::array{ sin30, 0.0f, -cos30}},
+ ChanPosMap{FrontCenter, std::array{ 0.0f, 0.0f, -1.0f}},
+ ChanPosMap{LFE, {}},
+ ChanPosMap{SideLeft, std::array{-sin110, 0.0f, -cos110}},
+ ChanPosMap{SideRight, std::array{ sin110, 0.0f, -cos110}},
+ };
+ static constexpr std::array X61Map{
+ ChanPosMap{FrontLeft, std::array{-sin30, 0.0f, -cos30}},
+ ChanPosMap{FrontRight, std::array{ sin30, 0.0f, -cos30}},
+ ChanPosMap{FrontCenter, std::array{ 0.0f, 0.0f, -1.0f}},
+ ChanPosMap{LFE, {}},
+ ChanPosMap{BackCenter, std::array{ 0.0f, 0.0f, 1.0f}},
+ ChanPosMap{SideLeft, std::array{-1.0f, 0.0f, 0.0f}},
+ ChanPosMap{SideRight, std::array{ 1.0f, 0.0f, 0.0f}},
+ };
+ static constexpr std::array X71Map{
+ ChanPosMap{FrontLeft, std::array{-sin30, 0.0f, -cos30}},
+ ChanPosMap{FrontRight, std::array{ sin30, 0.0f, -cos30}},
+ ChanPosMap{FrontCenter, std::array{ 0.0f, 0.0f, -1.0f}},
+ ChanPosMap{LFE, {}},
+ ChanPosMap{BackLeft, std::array{-sin30, 0.0f, cos30}},
+ ChanPosMap{BackRight, std::array{ sin30, 0.0f, cos30}},
+ ChanPosMap{SideLeft, std::array{ -1.0f, 0.0f, 0.0f}},
+ ChanPosMap{SideRight, std::array{ 1.0f, 0.0f, 0.0f}},
};
- ChanPosMap StereoMap[2]{
- { FrontLeft, std::array{-sin30, 0.0f, -cos30} },
- { FrontRight, std::array{ sin30, 0.0f, -cos30} },
+ std::array StereoMap{
+ ChanPosMap{FrontLeft, std::array{-sin30, 0.0f, -cos30}},
+ ChanPosMap{FrontRight, std::array{ sin30, 0.0f, -cos30}},
};
const auto Frequency = static_cast<float>(Device->Frequency);
@@ -834,45 +839,45 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con
[](SendParams &params) -> void { params.Gains.Target.fill(0.0f); });
}
- DirectMode DirectChannels{props->DirectChannels};
- const ChanPosMap *chans{nullptr};
- switch(voice->mFmtChannels)
+ const auto getChans = [props,&StereoMap](FmtChannels chanfmt) noexcept
+ -> std::pair<DirectMode,al::span<const ChanPosMap>>
{
- case FmtMono:
- chans = MonoMap;
- /* Mono buffers are never played direct. */
- DirectChannels = DirectMode::Off;
- break;
-
- case FmtStereo:
- if(DirectChannels == DirectMode::Off)
+ switch(chanfmt)
{
- for(size_t i{0};i < 2;++i)
+ case FmtMono:
+ /* Mono buffers are never played direct. */
+ return {DirectMode::Off, al::span{MonoMap}};
+
+ case FmtStereo:
+ if(props->DirectChannels == DirectMode::Off)
{
- /* StereoPan is counter-clockwise in radians. */
- const float a{props->StereoPan[i]};
- StereoMap[i].pos[0] = -std::sin(a);
- StereoMap[i].pos[2] = -std::cos(a);
+ for(size_t i{0};i < 2;++i)
+ {
+ /* StereoPan is counter-clockwise in radians. */
+ const float a{props->StereoPan[i]};
+ StereoMap[i].pos[0] = -std::sin(a);
+ StereoMap[i].pos[2] = -std::cos(a);
+ }
}
+ return {props->DirectChannels, al::span{StereoMap}};
+
+ case FmtRear: return {props->DirectChannels, al::span{RearMap}};
+ case FmtQuad: return {props->DirectChannels, al::span{QuadMap}};
+ case FmtX51: return {props->DirectChannels, al::span{X51Map}};
+ case FmtX61: return {props->DirectChannels, al::span{X61Map}};
+ case FmtX71: return {props->DirectChannels, al::span{X71Map}};
+
+ case FmtBFormat2D:
+ case FmtBFormat3D:
+ case FmtUHJ2:
+ case FmtUHJ3:
+ case FmtUHJ4:
+ case FmtSuperStereo:
+ return {DirectMode::Off, {}};
}
- chans = StereoMap;
- break;
-
- case FmtRear: chans = RearMap; break;
- case FmtQuad: chans = QuadMap; break;
- case FmtX51: chans = X51Map; break;
- case FmtX61: chans = X61Map; break;
- case FmtX71: chans = X71Map; break;
-
- case FmtBFormat2D:
- case FmtBFormat3D:
- case FmtUHJ2:
- case FmtUHJ3:
- case FmtUHJ4:
- case FmtSuperStereo:
- DirectChannels = DirectMode::Off;
- break;
- }
+ return {props->DirectChannels, {}};
+ };
+ const auto [DirectChannels,chans] = getChans(voice->mFmtChannels);
voice->mFlags.reset(VoiceHasHrtf).reset(VoiceHasNfc);
if(auto *decoder{voice->mDecoder.get()})
@@ -1065,8 +1070,8 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con
voice->mChans[c].mDryParams.Gains.Target[idx] = DryGain.Base;
else if(DirectChannels == DirectMode::RemixMismatch)
{
- auto match_channel = [chans,c](const InputRemixMap &map) noexcept -> bool
- { return chans[c].channel == map.channel; };
+ auto match_channel = [channel=chans[c].channel](const InputRemixMap &map) noexcept
+ { return channel == map.channel; };
auto remap = std::find_if(Device->RealOut.RemixMap.cbegin(),
Device->RealOut.RemixMap.cend(), match_channel);
if(remap != Device->RealOut.RemixMap.cend())
@@ -1176,7 +1181,7 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con
* where it can be 0 or full (non-mono sources are always full
* spread here).
*/
- const float spread{Spread * (voice->mFmtChannels == FmtMono)};
+ const float spread{Spread * float(voice->mFmtChannels == FmtMono)};
/* Local sources on HRTF play with each channel panned to its
* relative location around the listener, providing "virtual
@@ -1324,7 +1329,7 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con
* where it can be 0 or full (non-mono sources are always full
* spread here).
*/
- const float spread{Spread * (voice->mFmtChannels == FmtMono)};
+ const float spread{Spread * float(voice->mFmtChannels == FmtMono)};
for(size_t c{0};c < num_channels;c++)
{
/* Special-case LFE */
@@ -1396,7 +1401,7 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con
void CalcNonAttnSourceParams(Voice *voice, const VoiceProps *props, const ContextBase *context)
{
DeviceBase *Device{context->mDevice};
- EffectSlot *SendSlots[MAX_SENDS];
+ std::array<EffectSlot*,MaxSendCount> SendSlots;
voice->mDirect.Buffer = Device->Dry.Buffer;
for(uint i{0};i < Device->NumAuxSends;i++)
@@ -1426,7 +1431,8 @@ void CalcNonAttnSourceParams(Voice *voice, const VoiceProps *props, const Contex
context->mParams.Gain, GainMixMax);
DryGain.HF = props->Direct.GainHF;
DryGain.LF = props->Direct.GainLF;
- GainTriplet WetGain[MAX_SENDS];
+
+ std::array<GainTriplet,MaxSendCount> WetGain;
for(uint i{0};i < Device->NumAuxSends;i++)
{
WetGain[i].Base = minf(clampf(props->Gain, props->MinGain, props->MaxGain) *
@@ -1446,20 +1452,30 @@ void CalcAttnSourceParams(Voice *voice, const VoiceProps *props, const ContextBa
/* Set mixing buffers and get send parameters. */
voice->mDirect.Buffer = Device->Dry.Buffer;
- EffectSlot *SendSlots[MAX_SENDS];
- uint UseDryAttnForRoom{0};
+ std::array<EffectSlot*,MaxSendCount> SendSlots{};
+ std::array<float,MaxSendCount> RoomRolloff{};
+ std::bitset<MaxSendCount> UseDryAttnForRoom{0};
for(uint i{0};i < NumSends;i++)
{
SendSlots[i] = props->Send[i].Slot;
if(!SendSlots[i] || SendSlots[i]->EffectType == EffectSlotType::None)
SendSlots[i] = nullptr;
- else if(!SendSlots[i]->AuxSendAuto)
+ else if(SendSlots[i]->AuxSendAuto)
{
- /* If the slot's auxiliary send auto is off, the data sent to the
- * effect slot is the same as the dry path, sans filter effects.
+ /* NOTE: Contrary to the EFX docs, the effect's room rolloff factor
+ * applies to the selected distance model along with the source's
+ * room rolloff factor, not necessarily the inverse distance model.
+ *
+ * Generic Software also applies these rolloff factors regardless
+ * of any setting. It doesn't seem to use the effect slot's send
+ * auto for anything, though as far as I understand, it's supposed
+ * to control whether the send gets the same gain/gainhf as the
+ * direct path (excluding the filter).
*/
- UseDryAttnForRoom |= 1u<<i;
+ RoomRolloff[i] = props->RoomRolloffFactor + SendSlots[i]->RoomRolloff;
}
+ else
+ UseDryAttnForRoom.set(i);
if(!SendSlots[i])
voice->mSend[i].Buffer = {};
@@ -1491,62 +1507,77 @@ void CalcAttnSourceParams(Voice *voice, const VoiceProps *props, const ContextBa
/* Calculate distance attenuation */
float ClampedDist{Distance};
float DryGainBase{props->Gain};
- float WetGainBase{props->Gain};
+ std::array<float,MaxSendCount> WetGainBase{};
+ WetGainBase.fill(props->Gain);
+ float DryAttnBase{1.0f};
switch(context->mParams.SourceDistanceModel ? props->mDistanceModel
: context->mParams.mDistanceModel)
{
- case DistanceModel::InverseClamped:
- if(props->MaxDistance < props->RefDistance) break;
- ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
- /*fall-through*/
- case DistanceModel::Inverse:
- if(props->RefDistance > 0.0f)
+ case DistanceModel::InverseClamped:
+ if(props->MaxDistance < props->RefDistance) break;
+ ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
+ /*fall-through*/
+ case DistanceModel::Inverse:
+ if(props->RefDistance > 0.0f)
+ {
+ float dist{lerpf(props->RefDistance, ClampedDist, props->RolloffFactor)};
+ if(dist > 0.0f)
{
- float dist{lerpf(props->RefDistance, ClampedDist, props->RolloffFactor)};
- if(dist > 0.0f) DryGainBase *= props->RefDistance / dist;
-
- dist = lerpf(props->RefDistance, ClampedDist, props->RoomRolloffFactor);
- if(dist > 0.0f) WetGainBase *= props->RefDistance / dist;
+ DryAttnBase = props->RefDistance / dist;
+ DryGainBase *= DryAttnBase;
}
- break;
-
- case DistanceModel::LinearClamped:
- if(props->MaxDistance < props->RefDistance) break;
- ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
- /*fall-through*/
- case DistanceModel::Linear:
- if(props->MaxDistance != props->RefDistance)
- {
- float attn{(ClampedDist-props->RefDistance) /
- (props->MaxDistance-props->RefDistance) * props->RolloffFactor};
- DryGainBase *= maxf(1.0f - attn, 0.0f);
- attn = (ClampedDist-props->RefDistance) /
- (props->MaxDistance-props->RefDistance) * props->RoomRolloffFactor;
- WetGainBase *= maxf(1.0f - attn, 0.0f);
+ for(size_t i{0};i < NumSends;++i)
+ {
+ dist = lerpf(props->RefDistance, ClampedDist, RoomRolloff[i]);
+ if(dist > 0.0f) WetGainBase[i] *= props->RefDistance / dist;
}
- break;
-
- case DistanceModel::ExponentClamped:
- if(props->MaxDistance < props->RefDistance) break;
- ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
- /*fall-through*/
- case DistanceModel::Exponent:
- if(ClampedDist > 0.0f && props->RefDistance > 0.0f)
+ }
+ break;
+
+ case DistanceModel::LinearClamped:
+ if(props->MaxDistance < props->RefDistance) break;
+ ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
+ /*fall-through*/
+ case DistanceModel::Linear:
+ if(props->MaxDistance != props->RefDistance)
+ {
+ float attn{(ClampedDist-props->RefDistance) /
+ (props->MaxDistance-props->RefDistance) * props->RolloffFactor};
+ DryAttnBase = maxf(1.0f - attn, 0.0f);
+ DryGainBase *= DryAttnBase;
+
+ for(size_t i{0};i < NumSends;++i)
{
- const float dist_ratio{ClampedDist/props->RefDistance};
- DryGainBase *= std::pow(dist_ratio, -props->RolloffFactor);
- WetGainBase *= std::pow(dist_ratio, -props->RoomRolloffFactor);
+ attn = (ClampedDist-props->RefDistance) /
+ (props->MaxDistance-props->RefDistance) * RoomRolloff[i];
+ WetGainBase[i] *= maxf(1.0f - attn, 0.0f);
}
- break;
+ }
+ break;
+
+ case DistanceModel::ExponentClamped:
+ if(props->MaxDistance < props->RefDistance) break;
+ ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance);
+ /*fall-through*/
+ case DistanceModel::Exponent:
+ if(ClampedDist > 0.0f && props->RefDistance > 0.0f)
+ {
+ const float dist_ratio{ClampedDist/props->RefDistance};
+ DryAttnBase = std::pow(dist_ratio, -props->RolloffFactor);
+ DryGainBase *= DryAttnBase;
+ for(size_t i{0};i < NumSends;++i)
+ WetGainBase[i] *= std::pow(dist_ratio, -RoomRolloff[i]);
+ }
+ break;
- case DistanceModel::Disable:
- break;
+ case DistanceModel::Disable:
+ break;
}
/* Calculate directional soundcones */
- float ConeHF{1.0f}, WetConeHF{1.0f};
+ float ConeHF{1.0f}, WetCone{1.0f}, WetConeHF{1.0f};
if(directional && props->InnerAngle < 360.0f)
{
static constexpr float Rad2Deg{static_cast<float>(180.0 / al::numbers::pi)};
@@ -1556,39 +1587,43 @@ void CalcAttnSourceParams(Voice *voice, const VoiceProps *props, const ContextBa
if(Angle >= props->OuterAngle)
{
ConeGain = props->OuterGain;
- ConeHF = lerpf(1.0f, props->OuterGainHF, props->DryGainHFAuto);
+ if(props->DryGainHFAuto)
+ ConeHF = props->OuterGainHF;
}
else if(Angle >= props->InnerAngle)
{
const float scale{(Angle-props->InnerAngle) / (props->OuterAngle-props->InnerAngle)};
ConeGain = lerpf(1.0f, props->OuterGain, scale);
- ConeHF = lerpf(1.0f, props->OuterGainHF, scale * props->DryGainHFAuto);
+ if(props->DryGainHFAuto)
+ ConeHF = lerpf(1.0f, props->OuterGainHF, scale);
}
DryGainBase *= ConeGain;
- WetGainBase *= lerpf(1.0f, ConeGain, props->WetGainAuto);
-
- WetConeHF = lerpf(1.0f, ConeHF, props->WetGainHFAuto);
+ if(props->WetGainAuto)
+ WetCone = ConeGain;
+ if(props->WetGainHFAuto)
+ WetConeHF = ConeHF;
}
/* Apply gain and frequency filters */
- DryGainBase = clampf(DryGainBase, props->MinGain, props->MaxGain) * context->mParams.Gain;
- WetGainBase = clampf(WetGainBase, props->MinGain, props->MaxGain) * context->mParams.Gain;
-
GainTriplet DryGain{};
+ DryGainBase = clampf(DryGainBase, props->MinGain, props->MaxGain) * context->mParams.Gain;
DryGain.Base = minf(DryGainBase * props->Direct.Gain, GainMixMax);
DryGain.HF = ConeHF * props->Direct.GainHF;
DryGain.LF = props->Direct.GainLF;
- GainTriplet WetGain[MAX_SENDS]{};
+
+ std::array<GainTriplet,MaxSendCount> WetGain{};
for(uint i{0};i < NumSends;i++)
{
+ WetGainBase[i] = clampf(WetGainBase[i]*WetCone, props->MinGain, props->MaxGain) *
+ context->mParams.Gain;
/* If this effect slot's Auxiliary Send Auto is off, then use the dry
* path distance and cone attenuation, otherwise use the wet (room)
* path distance and cone attenuation. The send filter is used instead
* of the direct filter, regardless.
*/
- const bool use_room{!(UseDryAttnForRoom&(1u<<i))};
- const float gain{use_room ? WetGainBase : DryGainBase};
+ const bool use_room{!UseDryAttnForRoom.test(i)};
+ const float gain{use_room ? WetGainBase[i] : DryGainBase};
WetGain[i].Base = minf(gain * props->Send[i].Gain, GainMixMax);
WetGain[i].HF = (use_room ? WetConeHF : ConeHF) * props->Send[i].GainHF;
WetGain[i].LF = props->Send[i].GainLF;
@@ -1611,25 +1646,15 @@ void CalcAttnSourceParams(Voice *voice, const VoiceProps *props, const ContextBa
if(!SendSlots[i] || !(SendSlots[i]->DecayTime > 0.0f))
continue;
- auto calc_attenuation = [](float distance, float refdist, float rolloff) noexcept
- {
- const float dist{lerpf(refdist, distance, rolloff)};
- if(dist > refdist) return refdist / dist;
- return 1.0f;
- };
-
- /* The reverb effect's room rolloff factor always applies to an
- * inverse distance rolloff model.
- */
- WetGain[i].Base *= calc_attenuation(Distance, props->RefDistance,
- SendSlots[i]->RoomRolloff);
-
if(distance_meters > std::numeric_limits<float>::epsilon())
WetGain[i].HF *= std::pow(SendSlots[i]->AirAbsorptionGainHF, distance_meters);
/* If this effect slot's Auxiliary Send Auto is off, don't apply
- * the automatic initial reverb decay (should the reverb's room
- * rolloff still apply?).
+ * the automatic initial reverb decay.
+ *
+ * NOTE: Generic Software applies the initial decay regardless of
+ * this setting. It doesn't seem to use it for anything, only the
+ * source's send filter gain auto flag affects this.
*/
if(!SendSlots[i]->AuxSendAuto)
continue;
@@ -1640,7 +1665,7 @@ void CalcAttnSourceParams(Voice *voice, const VoiceProps *props, const ContextBa
*/
DecayDistance.Base = SendSlots[i]->DecayTime * SpeedOfSoundMetersPerSec;
DecayDistance.LF = DecayDistance.Base * SendSlots[i]->DecayLFRatio;
- DecayDistance.HF = DecayDistance.Base * SendSlots[i]->DecayHFRatio;
+ DecayDistance.HF = SendSlots[i]->DecayHFRatio;
if(SendSlots[i]->DecayHFLimit)
{
const float airAbsorption{SendSlots[i]->AirAbsorptionGainHF};
@@ -1656,14 +1681,18 @@ void CalcAttnSourceParams(Voice *voice, const VoiceProps *props, const ContextBa
DecayDistance.HF = minf(absorb_dist, DecayDistance.HF);
}
}
-
- const float baseAttn = calc_attenuation(Distance, props->RefDistance,
- props->RolloffFactor);
+ DecayDistance.HF *= DecayDistance.Base;
/* Apply a decay-time transformation to the wet path, based on the
* source distance. The initial decay of the reverb effect is
* calculated and applied to the wet path.
+ *
+ * FIXME: This is very likely not correct. It more likely should
+ * work by calculating a rolloff dynamically based on the reverb
+ * parameters (and source distance?) and add it to the room rolloff
+ * with the reverb and source rolloff parameters.
*/
+ const float baseAttn{DryAttnBase};
const float fact{distance_base / DecayDistance.Base};
const float gain{std::pow(ReverbDecayGain, fact)*(1.0f-baseAttn) + baseAttn};
WetGain[i].Base *= gain;
@@ -1743,7 +1772,7 @@ void CalcSourceParams(Voice *voice, ContextBase *context, bool force)
if(props)
{
- voice->mProps = *props;
+ voice->mProps = static_cast<VoiceProps&>(*props);
AtomicReplaceHead(context->mFreeVoiceProps, props);
}
@@ -1883,7 +1912,7 @@ void ProcessVoiceChanges(ContextBase *ctx)
ctx->mCurrentVoiceChange.store(cur, std::memory_order_release);
}
-void ProcessParamUpdates(ContextBase *ctx, const EffectSlotArray &slots,
+void ProcessParamUpdates(ContextBase *ctx, const al::span<EffectSlot*> slots,
const al::span<Voice*> voices)
{
ProcessVoiceChanges(ctx);
@@ -1892,7 +1921,7 @@ void ProcessParamUpdates(ContextBase *ctx, const EffectSlotArray &slots,
if(!ctx->mHoldUpdates.load(std::memory_order_acquire)) LIKELY
{
bool force{CalcContextParams(ctx)};
- auto sorted_slots = const_cast<EffectSlot**>(slots.data() + slots.size());
+ auto sorted_slots = al::to_address(slots.end());
for(EffectSlot *slot : slots)
force |= CalcEffectSlotParams(slot, sorted_slots, ctx);
@@ -1910,12 +1939,13 @@ void ProcessContexts(DeviceBase *device, const uint SamplesToDo)
{
ASSUME(SamplesToDo > 0);
- const nanoseconds curtime{device->ClockBase +
- nanoseconds{seconds{device->SamplesDone}}/device->Frequency};
+ const nanoseconds curtime{device->mClockBase.load(std::memory_order_relaxed) +
+ nanoseconds{seconds{device->mSamplesDone.load(std::memory_order_relaxed)}}/
+ device->Frequency};
for(ContextBase *ctx : *device->mContexts.load(std::memory_order_acquire))
{
- const EffectSlotArray &auxslots = *ctx->mActiveAuxSlots.load(std::memory_order_acquire);
+ auto auxslots = al::span{*ctx->mActiveAuxSlots.load(std::memory_order_acquire)};
const al::span<Voice*> voices{ctx->getVoicesSpanAcquired()};
/* Process pending property updates for objects on the context. */
@@ -1937,16 +1967,12 @@ void ProcessContexts(DeviceBase *device, const uint SamplesToDo)
}
/* Process effects. */
- if(const size_t num_slots{auxslots.size()})
+ if(!auxslots.empty())
{
- auto slots = auxslots.data();
- auto slots_end = slots + num_slots;
-
/* Sort the slots into extra storage, so that effect slots come
* before their effect slot target (or their targets' target).
*/
- const al::span<EffectSlot*> sorted_slots{const_cast<EffectSlot**>(slots_end),
- num_slots};
+ const al::span sorted_slots{al::to_address(auxslots.end()), auxslots.size()};
/* Skip sorting if it has already been done. */
if(!sorted_slots[0])
{
@@ -1954,7 +1980,7 @@ void ProcessContexts(DeviceBase *device, const uint SamplesToDo)
* sorted list so that all slots without a target slot go to
* the end.
*/
- std::copy(slots, slots_end, sorted_slots.begin());
+ std::copy(auxslots.begin(), auxslots.end(), sorted_slots.begin());
auto split_point = std::partition(sorted_slots.begin(), sorted_slots.end(),
[](const EffectSlot *slot) noexcept -> bool
{ return slot->Target != nullptr; });
@@ -2041,11 +2067,12 @@ void ApplyDistanceComp(const al::span<FloatBufferLine> Samples, const size_t Sam
void ApplyDither(const al::span<FloatBufferLine> Samples, uint *dither_seed,
const float quant_scale, const size_t SamplesToDo)
{
+ static constexpr double invRNGRange{1.0 / std::numeric_limits<uint>::max()};
ASSUME(SamplesToDo > 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.
+ * the desired quantization depth and before rounding.
*/
const float invscale{1.0f / quant_scale};
uint seed{*dither_seed};
@@ -2054,7 +2081,7 @@ void ApplyDither(const al::span<FloatBufferLine> Samples, uint *dither_seed,
float val{sample * quant_scale};
uint rng0{dither_rng(&seed)};
uint rng1{dither_rng(&seed)};
- val += static_cast<float>(rng0*(1.0/UINT_MAX) - rng1*(1.0/UINT_MAX));
+ val += static_cast<float>(rng0*invRNGRange - rng1*invRNGRange);
return fast_roundf(val) * invscale;
};
for(FloatBufferLine &inout : Samples)
@@ -2134,22 +2161,22 @@ uint DeviceBase::renderSamples(const uint numSamples)
for(FloatBufferLine &buffer : MixBuffer)
buffer.fill(0.0f);
- /* Increment the mix count at the start (lsb should now be 1). */
- IncrementRef(MixCount);
-
- /* Process and mix each context's sources and effects. */
- ProcessContexts(this, samplesToDo);
+ {
+ const auto mixLock = getWriteMixLock();
- /* 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 a stable conversion.
- */
- SamplesDone += samplesToDo;
- ClockBase += std::chrono::seconds{SamplesDone / Frequency};
- SamplesDone %= Frequency;
+ /* Process and mix each context's sources and effects. */
+ ProcessContexts(this, samplesToDo);
- /* Increment the mix count at the end (lsb should now be 0). */
- IncrementRef(MixCount);
+ /* 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 a stable conversion.
+ */
+ auto samplesDone = mSamplesDone.load(std::memory_order_relaxed) + samplesToDo;
+ auto clockBase = mClockBase.load(std::memory_order_relaxed) +
+ std::chrono::seconds{samplesDone/Frequency};
+ mSamplesDone.store(samplesDone%Frequency, std::memory_order_relaxed);
+ mClockBase.store(clockBase, std::memory_order_relaxed);
+ }
/* Apply any needed post-process for finalizing the Dry mix to the RealOut
* (Ambisonic decode, UHJ encode, etc).
@@ -2225,7 +2252,8 @@ void DeviceBase::renderSamples(void *outBuffer, const uint numSamples, const siz
void DeviceBase::handleDisconnect(const char *msg, ...)
{
- IncrementRef(MixCount);
+ const auto mixLock = getWriteMixLock();
+
if(Connected.exchange(false, std::memory_order_acq_rel))
{
AsyncEvent evt{std::in_place_type<AsyncDisconnectEvent>};
@@ -2233,10 +2261,10 @@ void DeviceBase::handleDisconnect(const char *msg, ...)
va_list args;
va_start(args, msg);
- int msglen{vsnprintf(disconnect.msg, sizeof(disconnect.msg), msg, args)};
+ int msglen{vsnprintf(disconnect.msg.data(), disconnect.msg.size(), msg, args)};
va_end(args);
- if(msglen < 0 || static_cast<size_t>(msglen) >= sizeof(disconnect.msg))
+ if(msglen < 0 || static_cast<size_t>(msglen) >= disconnect.msg.size())
disconnect.msg[sizeof(disconnect.msg)-1] = 0;
for(ContextBase *ctx : *mContexts.load())
@@ -2267,5 +2295,4 @@ void DeviceBase::handleDisconnect(const char *msg, ...)
std::for_each(voicelist.begin(), voicelist.end(), stop_voice);
}
}
- IncrementRef(MixCount);
}