diff options
author | Chris Robinson <[email protected]> | 2022-03-01 15:38:04 -0800 |
---|---|---|
committer | Chris Robinson <[email protected]> | 2022-03-01 15:38:04 -0800 |
commit | fbac67a6a0f297ca69c9b61da85e90f5c13663ae (patch) | |
tree | eb8ddca54554b2b2e3df855b0cb5fb9343e0e702 /alc | |
parent | d86f1ab4768ce9fdb3480a9d2a81d8f8e9293e0e (diff) |
Rework the initial reverb decay
The idea here is that the initial reverb decay can't become less than the dry
path distance attenuation, as the dry attenuation represents the audio that has
not yet had a chance to start reflecting in the environment. As well, the
reference distance indicates where there is no distance attenuation, with any
initial attenuation set by the environment itself.
So what we do is use the dry path attenuation as the baseline for what's mixed
to the reverb, with the decay rate indicating how much of the remaining room
(non-direct) energy attenuates with distance.
This may be over-complicating it. Other sources hint at a more typical XdB per
doubling of distance, with X varying depending on environment properties (room
size, absorbancy, etc). This could be handled by applying a normal inverse
distance attenuation model, with a rolloff factor generated from the reverb
properties (density, decay rate, etc). Will need more testing and research.
Diffstat (limited to 'alc')
-rw-r--r-- | alc/alu.cpp | 73 |
1 files changed, 32 insertions, 41 deletions
diff --git a/alc/alu.cpp b/alc/alu.cpp index a263960c..f0667f73 100644 --- a/alc/alu.cpp +++ b/alc/alu.cpp @@ -1308,14 +1308,12 @@ void CalcAttnSourceParams(Voice *voice, const VoiceProps *props, const ContextBa alu::Vector ToSource{Position[0], Position[1], Position[2], 0.0f}; const float Distance{ToSource.normalize()}; - /* Initial source gain */ - GainTriplet DryGain{props->Gain, 1.0f, 1.0f}; - GainTriplet WetGain[MAX_SENDS]; - for(uint i{0};i < NumSends;i++) - WetGain[i] = DryGain; - /* Calculate distance attenuation */ float ClampedDist{Distance}; + float DryAttenuation{1.0f}; + float WetAttenuation[MAX_SENDS]; + for(uint i{0};i < NumSends;i++) + WetAttenuation[i] = DryAttenuation; switch(context->mParams.SourceDistanceModel ? props->mDistanceModel : context->mParams.mDistanceModel) @@ -1330,11 +1328,11 @@ void CalcAttnSourceParams(Voice *voice, const VoiceProps *props, const ContextBa else { float dist{lerp(props->RefDistance, ClampedDist, props->RolloffFactor)}; - if(dist > 0.0f) DryGain.Base *= props->RefDistance / dist; + 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) WetGain[i].Base *= props->RefDistance / dist; + if(dist > 0.0f) WetAttenuation[i] = props->RefDistance / dist; } } break; @@ -1350,12 +1348,12 @@ void CalcAttnSourceParams(Voice *voice, const VoiceProps *props, const ContextBa { float attn{props->RolloffFactor * (ClampedDist-props->RefDistance) / (props->MaxDistance-props->RefDistance)}; - DryGain.Base *= maxf(1.0f - attn, 0.0f); + DryAttenuation = maxf(1.0f - attn, 0.0f); for(uint i{0};i < NumSends;i++) { attn = RoomRolloff[i] * (ClampedDist-props->RefDistance) / (props->MaxDistance-props->RefDistance); - WetGain[i].Base *= maxf(1.0f - attn, 0.0f); + WetAttenuation[i] = maxf(1.0f - attn, 0.0f); } } break; @@ -1370,9 +1368,9 @@ void CalcAttnSourceParams(Voice *voice, const VoiceProps *props, const ContextBa else { const float dist_ratio{ClampedDist/props->RefDistance}; - DryGain.Base *= std::pow(dist_ratio, -props->RolloffFactor); + DryAttenuation = std::pow(dist_ratio, -props->RolloffFactor); for(uint i{0};i < NumSends;i++) - WetGain[i].Base *= std::pow(dist_ratio, -RoomRolloff[i]); + WetAttenuation[i] = std::pow(dist_ratio, -RoomRolloff[i]); } break; @@ -1381,51 +1379,44 @@ 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}; 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, ConeHF; - if(!(Angle > props->InnerAngle)) + if(Angle >= props->OuterAngle) { - ConeGain = 1.0f; - ConeHF = 1.0f; + ConeGain = props->OuterGain; + ConeHF = lerp(1.0f, props->OuterGainHF, props->DryGainHFAuto); } - else if(Angle < props->OuterAngle) + else if(Angle >= props->InnerAngle) { const float scale{(Angle-props->InnerAngle) / (props->OuterAngle-props->InnerAngle)}; ConeGain = lerp(1.0f, props->OuterGain, scale); - ConeHF = lerp(1.0f, props->OuterGainHF, scale); - } - else - { - ConeGain = props->OuterGain; - ConeHF = props->OuterGainHF; + ConeHF = lerp(1.0f, props->OuterGainHF, scale * props->DryGainHFAuto); } - DryGain.Base *= ConeGain; - if(props->DryGainHFAuto) - DryGain.HF *= ConeHF; - if(props->WetGainAuto) - std::for_each(std::begin(WetGain), std::begin(WetGain)+NumSends, - [ConeGain](GainTriplet &gain) noexcept -> void { gain.Base *= ConeGain; }); - if(props->WetGainHFAuto) - std::for_each(std::begin(WetGain), std::begin(WetGain)+NumSends, - [ConeHF](GainTriplet &gain) noexcept -> void { gain.HF *= ConeHF; }); + WetConeGain = lerp(1.0f, ConeGain, props->WetGainAuto); + WetConeHF = lerp(1.0f, ConeHF, props->WetGainHFAuto); } /* Apply gain and frequency filters */ + 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.HF *= props->Direct.GainHF; - DryGain.LF *= props->Direct.GainLF; + DryGain.HF = ConeHF * props->Direct.GainHF; + DryGain.LF = props->Direct.GainLF; + GainTriplet WetGain[MAX_SENDS]{}; for(uint i{0};i < NumSends;i++) { - WetGain[i].Base = minf(clampf(WetGain[i].Base, props->MinGain, props->MaxGain) * - props->Send[i].Gain * context->mParams.Gain, GainMixMax); - WetGain[i].HF *= props->Send[i].GainHF; - WetGain[i].LF *= props->Send[i].GainLF; + 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; + WetGain[i].LF = props->Send[i].GainLF; } /* Distance-based air absorption and initial send decay. */ @@ -1454,16 +1445,16 @@ void CalcAttnSourceParams(Voice *voice, const VoiceProps *props, const ContextBa continue; const float gain{std::pow(ReverbDecayGain, meters_base/DecayDistance[i].Base)}; - WetGain[i].Base *= gain; + WetGain[i].Base *= (1.0f-DryAttenuation)*gain + DryAttenuation; /* Yes, the wet path's air absorption is applied with * WetGainAuto on, rather than WetGainHFAuto. */ if(gain > 0.0f) { float gainhf{std::pow(ReverbDecayGain, meters_base/DecayDistance[i].HF)}; - WetGain[i].HF *= minf(gainhf / gain, 1.0f); + 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 *= minf(gainlf / gain, 1.0f); + WetGain[i].LF *= (1.0f-DryAttenuation)*minf(gainlf/gain, 1.0f) + DryAttenuation; } } } |