Improve HRTF delay handling

Ensures source-level HRTF, the dry mix, and direct output all align properly,
and simplifies adding the delay in the direct mix output.
This commit is contained in:
Chris Robinson 2020-05-20 11:13:06 -07:00
parent f38b813ba8
commit 200c164d78
4 changed files with 20 additions and 33 deletions

View File

@ -269,7 +269,7 @@ struct ALCdevice : public al::intrusive_ref<ALCdevice> {
}; };
/* Persistent storage for HRTF mixing. */ /* Persistent storage for HRTF mixing. */
alignas(16) float2 HrtfAccumData[BUFFERSIZE + HRIR_LENGTH]; alignas(16) float2 HrtfAccumData[BUFFERSIZE + HRIR_LENGTH + HRTF_DIRECT_DELAY];
/* Mixing buffer used by the Dry mix and Real output. */ /* Mixing buffer used by the Dry mix and Real output. */
al::vector<FloatBufferLine, 16> MixBuffer; al::vector<FloatBufferLine, 16> MixBuffer;

View File

@ -88,8 +88,6 @@ struct DirectHrtfState {
alignas(16) HrirArray mCoeffs{}; alignas(16) HrirArray mCoeffs{};
}; };
std::array<float,HRTF_DIRECT_DELAY> mLeftDelay{};
std::array<float,HRTF_DIRECT_DELAY> mRightDelay{};
std::array<float,HRTF_DIRECT_DELAY+BUFFERSIZE> mTemp; std::array<float,HRTF_DIRECT_DELAY+BUFFERSIZE> mTemp;
/* HRTF filter state for dry buffer content */ /* HRTF filter state for dry buffer content */

View File

@ -85,8 +85,16 @@ inline void MixDirectHrtfBase(FloatBufferLine &LeftOut, FloatBufferLine &RightOu
{ {
ASSUME(BufferSize > 0); ASSUME(BufferSize > 0);
const uint_fast32_t IrSize{State->mIrSize}; /* Add the existing signal directly to the accumulation buffer, unfiltered,
* and with a delay to align with the input delay.
*/
for(size_t i{0};i < BufferSize;++i)
{
AccumSamples[HRTF_DIRECT_DELAY+i][0] += LeftOut[i];
AccumSamples[HRTF_DIRECT_DELAY+i][1] += RightOut[i];
}
const uint_fast32_t IrSize{State->mIrSize};
auto chan_iter = State->mChannels.begin(); auto chan_iter = State->mChannels.begin();
for(const FloatBufferLine &input : InSamples) for(const FloatBufferLine &input : InSamples)
{ {
@ -123,47 +131,25 @@ inline void MixDirectHrtfBase(FloatBufferLine &LeftOut, FloatBufferLine &RightOu
/* Now apply the HRIR coefficients to this channel. */ /* Now apply the HRIR coefficients to this channel. */
const auto &Coeffs = chan_iter->mCoeffs; const auto &Coeffs = chan_iter->mCoeffs;
++chan_iter;
for(size_t i{0u};i < BufferSize;++i) for(size_t i{0u};i < BufferSize;++i)
{ {
const float insample{tempbuf[i]}; const float insample{tempbuf[i]};
ApplyCoeffs(AccumSamples+i, IrSize, Coeffs, insample, insample); ApplyCoeffs(AccumSamples+i, IrSize, Coeffs, insample, insample);
} }
++chan_iter;
} }
/* Apply a delay to the existing signal to align with the input delay. */
auto &ldelay = State->mLeftDelay;
auto &rdelay = State->mRightDelay;
if LIKELY(BufferSize >= HRTF_DIRECT_DELAY)
{
auto buffer_end = LeftOut.begin() + BufferSize;
auto delay_end = std::rotate(LeftOut.begin(), buffer_end - HRTF_DIRECT_DELAY, buffer_end);
std::swap_ranges(LeftOut.begin(), delay_end, ldelay.begin());
buffer_end = RightOut.begin() + BufferSize;
delay_end = std::rotate(RightOut.begin(), buffer_end - HRTF_DIRECT_DELAY, buffer_end);
std::swap_ranges(RightOut.begin(), delay_end, rdelay.begin());
}
else
{
auto buffer_end = LeftOut.begin() + BufferSize;
auto delay_start = std::swap_ranges(LeftOut.begin(), buffer_end, ldelay.begin());
std::rotate(ldelay.begin(), delay_start, ldelay.end());
buffer_end = RightOut.begin() + BufferSize;
delay_start = std::swap_ranges(RightOut.begin(), buffer_end, rdelay.begin());
std::rotate(rdelay.begin(), delay_start, rdelay.end());
}
for(size_t i{0u};i < BufferSize;++i) for(size_t i{0u};i < BufferSize;++i)
LeftOut[i] += AccumSamples[i][0]; LeftOut[i] = AccumSamples[i][0];
for(size_t i{0u};i < BufferSize;++i) for(size_t i{0u};i < BufferSize;++i)
RightOut[i] += AccumSamples[i][1]; RightOut[i] = AccumSamples[i][1];
/* Copy the new in-progress accumulation values to the front and clear the /* Copy the new in-progress accumulation values to the front and clear the
* following samples for the next mix. * following samples for the next mix.
*/ */
auto accum_iter = std::copy_n(AccumSamples+BufferSize, HRIR_LENGTH, AccumSamples); auto accum_iter = std::copy_n(AccumSamples+BufferSize, HRIR_LENGTH+HRTF_DIRECT_DELAY,
AccumSamples);
std::fill_n(accum_iter, BufferSize, float2{}); std::fill_n(accum_iter, BufferSize, float2{});
} }

View File

@ -468,7 +468,10 @@ void DoHrtfMix(const float *samples, const ALuint DstBufferSize, DirectParams &p
ALCdevice *Device) ALCdevice *Device)
{ {
auto &HrtfSamples = Device->HrtfSourceData; auto &HrtfSamples = Device->HrtfSourceData;
auto &AccumSamples = Device->HrtfAccumData; /* Source HRTF mixing needs to include the direct delay so it remains
* aligned with the direct mix's HRTF filtering.
*/
float2 *AccumSamples{Device->HrtfAccumData + HRTF_DIRECT_DELAY};
/* Copy the HRTF history and new input samples into a temp buffer. */ /* Copy the HRTF history and new input samples into a temp buffer. */
auto src_iter = std::copy(parms.Hrtf.History.begin(), parms.Hrtf.History.end(), auto src_iter = std::copy(parms.Hrtf.History.begin(), parms.Hrtf.History.end(),