diff options
author | Chris Robinson <[email protected]> | 2020-05-19 10:27:52 -0700 |
---|---|---|
committer | Chris Robinson <[email protected]> | 2020-05-19 10:27:52 -0700 |
commit | 825206bfa2b59ae124cc8fb34c5b668f8a682224 (patch) | |
tree | e0198b6590c7e22352c651e0352db2e8b374a9d9 /alc/mixer | |
parent | a512eae7bb3554723b7b290985404e7c480b9bf1 (diff) |
Apply the ambisonic HF scaling in real-time with HRTF
Rather than applying the HF scale to the IRs necessitating them to be truncated
along with increasing the IR size, it can be applied to the input signal for
the same results. Consequently, the IR size can be notably shortened while
avoiding the extra truncation. In its place, the delayed reversed all-pass
technique can still be used on the input for maintaining phase when applying
the bandsplit/hfscalar filter to the input signal.
Diffstat (limited to 'alc/mixer')
-rw-r--r-- | alc/mixer/hrtfbase.h | 64 |
1 files changed, 61 insertions, 3 deletions
diff --git a/alc/mixer/hrtfbase.h b/alc/mixer/hrtfbase.h index 5bef1ddd..eea63efa 100644 --- a/alc/mixer/hrtfbase.h +++ b/alc/mixer/hrtfbase.h @@ -87,16 +87,74 @@ inline void MixDirectHrtfBase(FloatBufferLine &LeftOut, FloatBufferLine &RightOu const uint_fast32_t IrSize{State->mIrSize}; - auto coeff_iter = State->mCoeffs.begin(); + auto chan_iter = State->mChannels.begin(); for(const FloatBufferLine &input : InSamples) { - const auto &Coeffs = *(coeff_iter++); + /* For dual-band processing, the signal needs extra scaling applied to + * the high frequency response. The band-splitter alone creates a + * frequency-dependent phase shift, which is not ideal. To counteract + * it, combine it with a backwards phase-shift. + */ + + /* Load the input signal backwards, into a temp buffer with delay + * padding. The delay serves to reduce the error caused by IIR filter's + * phase shift on a partial input. + */ + al::span<float> tempbuf{State->mTemp.data(), HRTF_DIRECT_DELAY+BufferSize}; + auto tmpiter = std::reverse_copy(input.begin(), input.begin()+BufferSize, tempbuf.begin()); + std::copy(chan_iter->mDelay.cbegin(), chan_iter->mDelay.cend(), tmpiter); + + /* Save the unfiltered newest input samples for next time. */ + std::copy_n(tempbuf.begin(), HRTF_DIRECT_DELAY, chan_iter->mDelay.begin()); + + /* Apply the all-pass on the reversed signal and reverse the resulting + * sample array. This produces the forward response with a backwards + * phase shift (+n degrees becomes -n degrees). + */ + chan_iter->mSplitter.applyAllpass(tempbuf); + tempbuf = tempbuf.subspan<HRTF_DIRECT_DELAY>(); + std::reverse(tempbuf.begin(), tempbuf.end()); + + /* Now apply the band-splitter. This applies the normal phase shift, + * which cancels out with the backwards phase shift to get the original + * phase on the split signal. + */ + chan_iter->mSplitter.applyHfScale(tempbuf, chan_iter->mHfScale); + + /* Now apply the HRIR coefficients to this channel. */ + const auto &Coeffs = chan_iter->mCoeffs; + ++chan_iter; + for(size_t i{0u};i < BufferSize;++i) { - const float insample{input[i]}; + const float insample{tempbuf[i]}; ApplyCoeffs(AccumSamples+i, IrSize, Coeffs, insample, insample); } } + + /* 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) LeftOut[i] += AccumSamples[i][0]; for(size_t i{0u};i < BufferSize;++i) |