aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--alc/alu.cpp168
1 files changed, 118 insertions, 50 deletions
diff --git a/alc/alu.cpp b/alc/alu.cpp
index ef311395..ffbf87aa 100644
--- a/alc/alu.cpp
+++ b/alc/alu.cpp
@@ -962,35 +962,54 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con
if(Distance > std::numeric_limits<float>::epsilon())
{
- const float ev{std::asin(clampf(ypos, -1.0f, 1.0f))};
- const float az{std::atan2(xpos, -zpos)};
+ const float src_ev{std::asin(clampf(ypos, -1.0f, 1.0f))};
+ const float src_az{std::atan2(xpos, -zpos)};
- /* Get the HRIR coefficients and delays just once, for the given
- * source direction.
- */
- GetHrtfCoeffs(Device->mHrtf.get(), ev, az, Distance*NfcScale, Spread,
- voice->mChans[0].mDryParams.Hrtf.Target.Coeffs,
- voice->mChans[0].mDryParams.Hrtf.Target.Delay);
- voice->mChans[0].mDryParams.Hrtf.Target.Gain = DryGain.Base;
+ if(voice->mFmtChannels == FmtMono)
+ {
+ GetHrtfCoeffs(Device->mHrtf.get(), src_ev, src_az, Distance*NfcScale, Spread,
+ voice->mChans[0].mDryParams.Hrtf.Target.Coeffs,
+ voice->mChans[0].mDryParams.Hrtf.Target.Delay);
+ voice->mChans[0].mDryParams.Hrtf.Target.Gain = DryGain.Base;
- /* Remaining channels use the same results as the first. */
- for(size_t c{1};c < num_channels;c++)
+ const auto coeffs = CalcAngleCoeffs(src_az, src_ev, Spread);
+ for(uint i{0};i < NumSends;i++)
+ {
+ if(const EffectSlot *Slot{SendSlots[i]})
+ ComputePanGains(&Slot->Wet, coeffs.data(), WetGain[i].Base,
+ voice->mChans[0].mWetParams[i].Gains.Target);
+ }
+ }
+ else for(size_t c{0};c < num_channels;c++)
{
+ using namespace al::numbers;
+
/* Skip LFE */
if(chans[c].channel == LFE) continue;
- voice->mChans[c].mDryParams.Hrtf.Target = voice->mChans[0].mDryParams.Hrtf.Target;
- }
- /* Calculate the directional coefficients once, which apply to all
- * input channels of the source sends.
- */
- const auto coeffs = CalcDirectionCoeffs({xpos, ypos, zpos}, Spread);
+ /* Warp the channel position toward the source position as the
+ * source spread decreases. With no spread, all channels are at
+ * the source position, at full spread (pi*2), each channel is
+ * left unchanged.
+ */
+ const float ev{lerpf(src_ev, chans[c].elevation, inv_pi_v<float>/2.0f * Spread)};
- for(size_t c{0};c < num_channels;c++)
- {
- /* Skip LFE */
- if(chans[c].channel == LFE)
- continue;
+ float az{chans[c].angle - src_az};
+ if(az < -pi_v<float>) az += pi_v<float>*2.0f;
+ else if(az > pi_v<float>) az -= pi_v<float>*2.0f;
+
+ az *= inv_pi_v<float>/2.0f * Spread;
+
+ az += src_az;
+ if(az < -pi_v<float>) az += pi_v<float>*2.0f;
+ else if(az > pi_v<float>) az -= pi_v<float>*2.0f;
+
+ GetHrtfCoeffs(Device->mHrtf.get(), ev, az, Distance*NfcScale, 0.0f,
+ voice->mChans[c].mDryParams.Hrtf.Target.Coeffs,
+ voice->mChans[c].mDryParams.Hrtf.Target.Delay);
+ voice->mChans[c].mDryParams.Hrtf.Target.Gain = DryGain.Base;
+
+ const auto coeffs = CalcAngleCoeffs(az, ev, 0.0f);
for(uint i{0};i < NumSends;i++)
{
if(const EffectSlot *Slot{SendSlots[i]})
@@ -1001,6 +1020,12 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con
}
else
{
+ /* With no distance, spread is only meaningful for mono sources
+ * where it can be 0 or full (non-mono sources are always full
+ * spread here).
+ */
+ const float spread{Spread * (voice->mFmtChannels == FmtMono)};
+
/* Local sources on HRTF play with each channel panned to its
* relative location around the listener, providing "virtual
* speaker" responses.
@@ -1015,7 +1040,7 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con
* position.
*/
GetHrtfCoeffs(Device->mHrtf.get(), chans[c].elevation, chans[c].angle,
- std::numeric_limits<float>::infinity(), Spread,
+ std::numeric_limits<float>::infinity(), spread,
voice->mChans[c].mDryParams.Hrtf.Target.Coeffs,
voice->mChans[c].mDryParams.Hrtf.Target.Delay);
voice->mChans[c].mDryParams.Hrtf.Target.Gain = DryGain.Base;
@@ -1056,40 +1081,78 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con
voice->mFlags.set(VoiceHasNfc);
}
- /* Calculate the directional coefficients once, which apply to all
- * input channels.
- */
- auto calc_coeffs = [xpos,ypos,zpos,Spread](RenderMode mode)
- {
- if(mode != RenderMode::Pairwise)
- return CalcDirectionCoeffs({xpos, ypos, zpos}, Spread);
- const float ev{std::asin(clampf(ypos, -1.0f, 1.0f))};
- const float az{std::atan2(xpos, -zpos)};
- return CalcAngleCoeffs(ScaleAzimuthFront(az, 1.5f), ev, Spread);
- };
- const auto coeffs = calc_coeffs(Device->mRenderMode);
-
- for(size_t c{0};c < num_channels;c++)
+ if(voice->mFmtChannels == FmtMono)
{
- /* Special-case LFE */
- if(chans[c].channel == LFE)
+ auto calc_coeffs = [xpos,ypos,zpos,Spread](RenderMode mode)
{
- if(Device->Dry.Buffer.data() == Device->RealOut.Buffer.data())
- {
- const uint idx{Device->channelIdxByName(chans[c].channel)};
- if(idx != INVALID_CHANNEL_INDEX)
- voice->mChans[c].mDryParams.Gains.Target[idx] = DryGain.Base;
- }
- continue;
- }
+ if(mode != RenderMode::Pairwise)
+ return CalcDirectionCoeffs({xpos, ypos, zpos}, Spread);
+ const float ev{std::asin(clampf(ypos, -1.0f, 1.0f))};
+ const float az{std::atan2(xpos, -zpos)};
+ return CalcAngleCoeffs(ScaleAzimuthFront(az, 1.5f), ev, Spread);
+ };
+ const auto coeffs = calc_coeffs(Device->mRenderMode);
ComputePanGains(&Device->Dry, coeffs.data(), DryGain.Base,
- voice->mChans[c].mDryParams.Gains.Target);
+ voice->mChans[0].mDryParams.Gains.Target);
for(uint i{0};i < NumSends;i++)
{
if(const EffectSlot *Slot{SendSlots[i]})
ComputePanGains(&Slot->Wet, coeffs.data(), WetGain[i].Base,
- voice->mChans[c].mWetParams[i].Gains.Target);
+ voice->mChans[0].mWetParams[i].Gains.Target);
+ }
+ }
+ else
+ {
+ using namespace al::numbers;
+
+ const float src_ev{std::asin(clampf(ypos, -1.0f, 1.0f))};
+ const float src_az{std::atan2(xpos, -zpos)};
+
+ for(size_t c{0};c < num_channels;c++)
+ {
+ /* Special-case LFE */
+ if(chans[c].channel == LFE)
+ {
+ if(Device->Dry.Buffer.data() == Device->RealOut.Buffer.data())
+ {
+ const uint idx{Device->channelIdxByName(chans[c].channel)};
+ if(idx != INVALID_CHANNEL_INDEX)
+ voice->mChans[c].mDryParams.Gains.Target[idx] = DryGain.Base;
+ }
+ continue;
+ }
+
+ /* Warp the channel position toward the source position as
+ * the spread decreases. With no spread, all channels are
+ * at the source position, at full spread (pi*2), each
+ * channel position is left unchanged.
+ */
+ const float ev{lerpf(src_ev, chans[c].elevation,
+ inv_pi_v<float>/2.0f * Spread)};
+
+ float az{chans[c].angle - src_az};
+ if(az < -pi_v<float>) az += pi_v<float>*2.0f;
+ else if(az > pi_v<float>) az -= pi_v<float>*2.0f;
+
+ az *= inv_pi_v<float>/2.0f * Spread;
+
+ az += src_az;
+ if(az < -pi_v<float>) az += pi_v<float>*2.0f;
+ else if(az > pi_v<float>) az -= pi_v<float>*2.0f;
+
+ if(Device->mRenderMode == RenderMode::Pairwise)
+ az = ScaleAzimuthFront(az, 3.0f);
+ const auto coeffs = CalcAngleCoeffs(az, ev, 0.0f);
+
+ ComputePanGains(&Device->Dry, coeffs.data(), DryGain.Base,
+ voice->mChans[c].mDryParams.Gains.Target);
+ for(uint i{0};i < NumSends;i++)
+ {
+ if(const EffectSlot *Slot{SendSlots[i]})
+ ComputePanGains(&Slot->Wet, coeffs.data(), WetGain[i].Base,
+ voice->mChans[c].mWetParams[i].Gains.Target);
+ }
}
}
}
@@ -1107,6 +1170,11 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con
voice->mFlags.set(VoiceHasNfc);
}
+ /* With no distance, spread is only meaningful for mono sources
+ * where it can be 0 or full (non-mono sources are always full
+ * spread here).
+ */
+ const float spread{Spread * (voice->mFmtChannels == FmtMono)};
for(size_t c{0};c < num_channels;c++)
{
/* Special-case LFE */
@@ -1123,7 +1191,7 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con
const auto coeffs = CalcAngleCoeffs((Device->mRenderMode == RenderMode::Pairwise)
? ScaleAzimuthFront(chans[c].angle, 3.0f) : chans[c].angle,
- chans[c].elevation, Spread);
+ chans[c].elevation, spread);
ComputePanGains(&Device->Dry, coeffs.data(), DryGain.Base,
voice->mChans[c].mDryParams.Gains.Target);