aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChris Robinson <[email protected]>2019-03-31 18:54:43 -0700
committerChris Robinson <[email protected]>2019-03-31 18:54:43 -0700
commitc88b09170cd5cebf7c64b283fa8dbec4d2f6d29e (patch)
tree3fc2b032018b7a656b17b4deefd6a92569d105ea
parentd8c76ba0c06b49ef73697390594f7359cc9e7e71 (diff)
Use phase correction for the front stablizer
-rw-r--r--Alc/alu.cpp55
-rw-r--r--Alc/filters/splitter.h8
-rw-r--r--Alc/panning.cpp15
3 files changed, 68 insertions, 10 deletions
diff --git a/Alc/alu.cpp b/Alc/alu.cpp
index dd160202..4492b1f3 100644
--- a/Alc/alu.cpp
+++ b/Alc/alu.cpp
@@ -1471,20 +1471,65 @@ void ApplyStablizer(FrontStablizer *Stablizer, ALfloat (*RESTRICT Buffer)[BUFFER
ASSUME(SamplesToDo > 0);
ASSUME(NumChannels > 0);
- /* Apply an all-pass to all channels, except the front-left and front-
- * right, so they maintain the same relative phase.
+ /* Apply a delay to all channels, except the front-left and front-right, so
+ * they maintain correct timing.
*/
for(ALsizei i{0};i < NumChannels;i++)
{
if(i == lidx || i == ridx)
continue;
- Stablizer->APFilter[i].process(Buffer[i], SamplesToDo);
+
+ auto &DelayBuf = Stablizer->DelayBuf[i];
+ auto buffer_end = Buffer[i] + SamplesToDo;
+ if(LIKELY(SamplesToDo >= ALsizei{FrontStablizer::DelayLength}))
+ {
+ auto delay_end = std::rotate(Buffer[i], buffer_end - FrontStablizer::DelayLength,
+ buffer_end);
+ std::swap_ranges(Buffer[i], delay_end, std::begin(DelayBuf));
+ }
+ else
+ {
+ auto delay_start = std::swap_ranges(Buffer[i], buffer_end, std::begin(DelayBuf));
+ std::rotate(std::begin(DelayBuf), delay_start, std::end(DelayBuf));
+ }
}
+ SplitterAllpass APFilter = Stablizer->APFilter;
ALfloat (&lsplit)[2][BUFFERSIZE] = Stablizer->LSplit;
ALfloat (&rsplit)[2][BUFFERSIZE] = Stablizer->RSplit;
- Stablizer->LFilter.process(lsplit[1], lsplit[0], Buffer[lidx], SamplesToDo);
- Stablizer->RFilter.process(rsplit[1], rsplit[0], Buffer[ridx], SamplesToDo);
+ auto &tmpbuf = Stablizer->TempBuf;
+
+ /* This applies the band-splitter, preserving phase at the cost of some
+ * delay. The shorter the delay, the more error seeps into the result.
+ */
+ auto apply_splitter = [&APFilter,&tmpbuf,SamplesToDo](const ALfloat *RESTRICT Buffer,
+ ALfloat (&DelayBuf)[FrontStablizer::DelayLength], BandSplitter &Filter,
+ ALfloat (&splitbuf)[2][BUFFERSIZE]) -> void
+ {
+ /* Combine the delayed samples and the input samples into the temp
+ * buffer, in reverse. Then copy the final samples back into the delay
+ * buffer for next time. Note that the delay buffer's samples are
+ * stored backwards here.
+ */
+ auto tmpbuf_end = std::begin(tmpbuf) + SamplesToDo;
+ std::copy_n(std::begin(DelayBuf), FrontStablizer::DelayLength, tmpbuf_end);
+ std::reverse_copy(Buffer, Buffer+SamplesToDo, tmpbuf_end);
+ std::copy_n(std::begin(tmpbuf), FrontStablizer::DelayLength, std::begin(DelayBuf));
+
+ /* Apply an all-pass on the reversed signal, then reverse the samples
+ * to get the forward signal with a reversed phase shift.
+ */
+ APFilter.process(tmpbuf, SamplesToDo+FrontStablizer::DelayLength);
+ std::reverse(std::begin(tmpbuf), tmpbuf_end+FrontStablizer::DelayLength);
+
+ /* Now apply the band-splitter, combining its phase shift with the
+ * reversed phase shift, restoring the original phase on the split
+ * signal.
+ */
+ Filter.process(splitbuf[1], splitbuf[0], tmpbuf, SamplesToDo);
+ };
+ apply_splitter(Buffer[lidx], Stablizer->DelayBuf[lidx], Stablizer->LFilter, lsplit);
+ apply_splitter(Buffer[ridx], Stablizer->DelayBuf[ridx], Stablizer->RFilter, rsplit);
for(ALsizei i{0};i < SamplesToDo;i++)
{
diff --git a/Alc/filters/splitter.h b/Alc/filters/splitter.h
index 83281366..ccb4f104 100644
--- a/Alc/filters/splitter.h
+++ b/Alc/filters/splitter.h
@@ -38,11 +38,17 @@ using SplitterAllpass = SplitterAllpassR<float>;
struct FrontStablizer {
- SplitterAllpass APFilter[MAX_OUTPUT_CHANNELS];
+ static constexpr size_t DelayLength{256u};
+
+ alignas(16) float DelayBuf[MAX_OUTPUT_CHANNELS][DelayLength];
+
+ SplitterAllpass APFilter;
BandSplitter LFilter, RFilter;
alignas(16) float LSplit[2][BUFFERSIZE];
alignas(16) float RSplit[2][BUFFERSIZE];
+ alignas(16) float TempBuf[BUFFERSIZE + DelayLength];
+
DEF_NEWDEL(FrontStablizer)
};
diff --git a/Alc/panning.cpp b/Alc/panning.cpp
index f8ca7149..34984c94 100644
--- a/Alc/panning.cpp
+++ b/Alc/panning.cpp
@@ -27,6 +27,7 @@
#include <cassert>
#include <cmath>
+#include <chrono>
#include <numeric>
#include <algorithm>
#include <functional>
@@ -54,6 +55,8 @@ constexpr std::array<int,MAX_AMBI_CHANNELS> AmbiIndex::From3D;
namespace {
using namespace std::placeholders;
+using std::chrono::seconds;
+using std::chrono::nanoseconds;
inline const char *GetLabelFromChannel(Channel channel)
{
@@ -850,12 +853,16 @@ void aluInitRenderer(ALCdevice *device, ALint hrtf_id, HrtfRequestMode hrtf_appr
stablizer->LFilter.init(scale);
stablizer->RFilter = stablizer->LFilter;
- /* Initialize all-pass filters for all other channels. */
- stablizer->APFilter[0].init(scale);
- std::fill(std::begin(stablizer->APFilter)+1, std::end(stablizer->APFilter),
- stablizer->APFilter[0]);
+ /* Initialize an all-pass filter for the phase corrector. */
+ stablizer->APFilter.init(scale);
device->Stablizer = std::move(stablizer);
+ /* NOTE: Don't know why this has to be "copied" into a local
+ * static constexpr variable to avoid a reference on
+ * FrontStablizer::DelayLength...
+ */
+ static constexpr size_t StablizerDelay{FrontStablizer::DelayLength};
+ device->FixedLatency += nanoseconds{seconds{StablizerDelay}} / device->Frequency;
}
break;
case DevFmtMono: