diff options
author | Chris Robinson <[email protected]> | 2022-03-02 11:14:59 -0800 |
---|---|---|
committer | Chris Robinson <[email protected]> | 2022-03-02 11:14:59 -0800 |
commit | 43682a8c3ece8c2b01fa552390cc64b67e4d8b1e (patch) | |
tree | 7c25660bce1477e2f33421317e26de35f3710272 | |
parent | fbac67a6a0f297ca69c9b61da85e90f5c13663ae (diff) |
Rework source send distance attenuation handling
Specifically, AL_[EAX]REVERB_ROOM_ROLLOFF_FACTOR always applies to an inverse
distance rolloff model, where the source's AL_ROOM_ROLLOFF_FACTOR applies to
the selected distance model. Consequently, the normal distance and cone
attenuation only needs to be calculated once for all sends, with the effect's
room rolloff calculated and applied separately.
-rw-r--r-- | alc/alu.cpp | 138 |
1 files changed, 77 insertions, 61 deletions
diff --git a/alc/alu.cpp b/alc/alu.cpp index f0667f73..d3cc74ca 100644 --- a/alc/alu.cpp +++ b/alc/alu.cpp @@ -1235,6 +1235,7 @@ void CalcAttnSourceParams(Voice *voice, const VoiceProps *props, const ContextBa EffectSlot *SendSlots[MAX_SENDS]; float RoomRolloff[MAX_SENDS]; GainTriplet DecayDistance[MAX_SENDS]; + uint UseDryAttnForRoom{0}; for(uint i{0};i < NumSends;i++) { SendSlots[i] = props->Send[i].Slot; @@ -1246,9 +1247,20 @@ void CalcAttnSourceParams(Voice *voice, const VoiceProps *props, const ContextBa DecayDistance[i].LF = 0.0f; DecayDistance[i].HF = 0.0f; } - else if(SendSlots[i]->AuxSendAuto) + else if(!SendSlots[i]->AuxSendAuto) { - RoomRolloff[i] = SendSlots[i]->RoomRolloff + props->RoomRolloffFactor; + /* 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. + */ + RoomRolloff[i] = 0.0f; + DecayDistance[i].Base = 0.0f; + DecayDistance[i].LF = 0.0f; + DecayDistance[i].HF = 0.0f; + UseDryAttnForRoom |= 1u<<i; + } + else + { + RoomRolloff[i] = SendSlots[i]->RoomRolloff; /* Calculate the distances to where this effect's decay reaches * -60dB. */ @@ -1271,15 +1283,6 @@ void CalcAttnSourceParams(Voice *voice, const VoiceProps *props, const ContextBa } } } - else - { - /* 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 */ - RoomRolloff[i] = props->RolloffFactor; - DecayDistance[i].Base = 0.0f; - DecayDistance[i].LF = 0.0f; - DecayDistance[i].HF = 0.0f; - } if(!SendSlots[i]) voice->mSend[i].Buffer = {}; @@ -1310,10 +1313,8 @@ void CalcAttnSourceParams(Voice *voice, const VoiceProps *props, const ContextBa /* Calculate distance attenuation */ float ClampedDist{Distance}; - float DryAttenuation{1.0f}; - float WetAttenuation[MAX_SENDS]; - for(uint i{0};i < NumSends;i++) - WetAttenuation[i] = DryAttenuation; + float DryGainBase{props->Gain}; + float WetGainBase{props->Gain}; switch(context->mParams.SourceDistanceModel ? props->mDistanceModel : context->mParams.mDistanceModel) @@ -1328,12 +1329,10 @@ void CalcAttnSourceParams(Voice *voice, const VoiceProps *props, const ContextBa else { float dist{lerp(props->RefDistance, ClampedDist, props->RolloffFactor)}; - if(dist > 0.0f) DryAttenuation = props->RefDistance / dist; - for(uint i{0};i < NumSends;i++) - { - dist = lerp(props->RefDistance, ClampedDist, RoomRolloff[i]); - if(dist > 0.0f) WetAttenuation[i] = props->RefDistance / dist; - } + if(dist > 0.0f) DryGainBase *= props->RefDistance / dist; + + dist = lerp(props->RefDistance, ClampedDist, props->RoomRolloffFactor); + if(dist > 0.0f) WetGainBase *= props->RefDistance / dist; } break; @@ -1346,15 +1345,13 @@ void CalcAttnSourceParams(Voice *voice, const VoiceProps *props, const ContextBa ClampedDist = props->RefDistance; else { - float attn{props->RolloffFactor * (ClampedDist-props->RefDistance) / - (props->MaxDistance-props->RefDistance)}; - DryAttenuation = maxf(1.0f - attn, 0.0f); - for(uint i{0};i < NumSends;i++) - { - attn = RoomRolloff[i] * (ClampedDist-props->RefDistance) / - (props->MaxDistance-props->RefDistance); - WetAttenuation[i] = maxf(1.0f - attn, 0.0f); - } + 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); } break; @@ -1368,9 +1365,8 @@ void CalcAttnSourceParams(Voice *voice, const VoiceProps *props, const ContextBa else { const float dist_ratio{ClampedDist/props->RefDistance}; - DryAttenuation = std::pow(dist_ratio, -props->RolloffFactor); - for(uint i{0};i < NumSends;i++) - WetAttenuation[i] = std::pow(dist_ratio, -RoomRolloff[i]); + DryGainBase *= std::pow(dist_ratio, -props->RolloffFactor); + WetGainBase *= std::pow(dist_ratio, -props->RoomRolloffFactor); } break; @@ -1379,13 +1375,13 @@ void CalcAttnSourceParams(Voice *voice, const VoiceProps *props, const ContextBa } /* Calculate directional soundcones */ - float ConeGain{1.0f}, ConeHF{1.0f}; - float WetConeGain{1.0f}, WetConeHF{1.0f}; + float ConeHF{1.0f}, WetConeHF{1.0f}; if(directional && props->InnerAngle < 360.0f) { static constexpr float Rad2Deg{static_cast<float>(180.0 / al::numbers::pi)}; const float Angle{Rad2Deg*2.0f * std::acos(-Direction.dot_product(ToSource)) * ConeScale}; + float ConeGain{1.0f}; if(Angle >= props->OuterAngle) { ConeGain = props->OuterGain; @@ -1398,43 +1394,62 @@ void CalcAttnSourceParams(Voice *voice, const VoiceProps *props, const ContextBa ConeHF = lerp(1.0f, props->OuterGainHF, scale * props->DryGainHFAuto); } - WetConeGain = lerp(1.0f, ConeGain, props->WetGainAuto); + DryGainBase *= ConeGain; + WetGainBase *= lerp(1.0f, ConeGain, props->WetGainAuto); + WetConeHF = lerp(1.0f, ConeHF, props->WetGainHFAuto); } /* 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{}; - DryGain.Base = props->Gain * DryAttenuation * ConeGain; - DryGain.Base = minf(clampf(DryGain.Base, props->MinGain, props->MaxGain) * props->Direct.Gain * - context->mParams.Gain, GainMixMax); + DryGain.Base = minf(DryGainBase * props->Direct.Gain, GainMixMax); DryGain.HF = ConeHF * props->Direct.GainHF; DryGain.LF = props->Direct.GainLF; GainTriplet WetGain[MAX_SENDS]{}; for(uint i{0};i < NumSends;i++) { - const float gain{props->Gain * WetConeGain * WetAttenuation[i]}; - WetGain[i].Base = minf(clampf(gain, props->MinGain, props->MaxGain) * props->Send[i].Gain * - context->mParams.Gain, GainMixMax); - WetGain[i].HF = WetConeHF * props->Send[i].GainHF; + const auto wet_switch = static_cast<float>(!(UseDryAttnForRoom&(1u<<i))); + const float gain{lerp(DryGainBase, WetGainBase, wet_switch)}; + WetGain[i].Base = minf(gain * props->Send[i].Gain, GainMixMax); + WetGain[i].HF = lerp(ConeHF, WetConeHF, wet_switch) * props->Send[i].GainHF; WetGain[i].LF = props->Send[i].GainLF; } /* Distance-based air absorption and initial send decay. */ - if(ClampedDist > props->RefDistance && props->RolloffFactor > 0.0f) + if(likely(ClampedDist > props->RefDistance)) { - const float meters_base{(ClampedDist-props->RefDistance) * props->RolloffFactor * - context->mParams.MetersPerUnit}; - if(props->AirAbsorptionFactor > 0.0f) + const float meters_base{(ClampedDist-props->RefDistance) * props->RolloffFactor}; + const float absorption{meters_base * context->mParams.MetersPerUnit * + props->AirAbsorptionFactor}; + if(absorption > std::numeric_limits<float>::epsilon()) { - const float hfattn{std::pow(context->mParams.AirAbsorptionGainHF, - meters_base*props->AirAbsorptionFactor)}; - DryGain.HF *= hfattn; - std::for_each(std::begin(WetGain), std::begin(WetGain)+NumSends, - [hfattn](GainTriplet &gain) noexcept -> void { gain.HF *= hfattn; }); + const float hfattn{std::pow(context->mParams.AirAbsorptionGainHF, absorption)}; + ConeHF *= hfattn; + WetConeHF *= hfattn; } + auto calc_attenuation = [](float distance, float refdist, float rolloff) + { + const float dist{lerp(refdist, distance, rolloff)}; + if(dist > 0.0f) return refdist / dist; + return 1.0f; + }; + + /* The reverb effect's room rolloff factor always applies to an inverse + * distance rolloff model. + */ + for(uint i{0};i < NumSends;i++) + WetGain[i].Base *= calc_attenuation(ClampedDist, props->RefDistance, + RoomRolloff[i]); + if(props->WetGainAuto) { + const float baseAttn = calc_attenuation(ClampedDist, props->RefDistance, + props->RolloffFactor); + /* Apply a decay-time transformation to the wet path, based on the * source distance in meters. The initial decay of the reverb * effect is calculated and applied to the wet path. @@ -1444,17 +1459,18 @@ void CalcAttnSourceParams(Voice *voice, const VoiceProps *props, const ContextBa if(!(DecayDistance[i].Base > 0.0f)) continue; - const float gain{std::pow(ReverbDecayGain, meters_base/DecayDistance[i].Base)}; - WetGain[i].Base *= (1.0f-DryAttenuation)*gain + DryAttenuation; - /* Yes, the wet path's air absorption is applied with - * WetGainAuto on, rather than WetGainHFAuto. - */ + const float gain{std::pow(ReverbDecayGain, meters_base/DecayDistance[i].Base)* + (1.0f-baseAttn) + baseAttn}; + WetGain[i].Base *= gain; + if(gain > 0.0f) { - float gainhf{std::pow(ReverbDecayGain, meters_base/DecayDistance[i].HF)}; - WetGain[i].HF *= (1.0f-DryAttenuation)*minf(gainhf/gain, 1.0f) + DryAttenuation; - float gainlf{std::pow(ReverbDecayGain, meters_base/DecayDistance[i].LF)}; - WetGain[i].LF *= (1.0f-DryAttenuation)*minf(gainlf/gain, 1.0f) + DryAttenuation; + float gainhf{std::pow(ReverbDecayGain, meters_base/DecayDistance[i].HF)* + (1.0f-baseAttn) + baseAttn}; + WetGain[i].HF *= minf(gainhf/gain, 1.0f); + float gainlf{std::pow(ReverbDecayGain, meters_base/DecayDistance[i].LF)* + (1.0f-baseAttn) + baseAttn}; + WetGain[i].LF *= minf(gainlf/gain, 1.0f); } } } |