aboutsummaryrefslogtreecommitdiffstats
path: root/core/bformatdec.cpp
diff options
context:
space:
mode:
authorChris Robinson <[email protected]>2023-01-06 01:40:10 -0800
committerChris Robinson <[email protected]>2023-01-06 01:40:10 -0800
commitec30a306df6c1d2079786c9cd4ae37fd6554e229 (patch)
tree223928cf686f6f62cdcc9c51f75f3b394065ff8d /core/bformatdec.cpp
parent3f8a3af3637606dae61ba112cb5bd9d2d897e5cb (diff)
Avoid using a reversed all-pass for the front stablizer
While a neat trick, it's been shown to be too volatile and add noise as the signal gets louder. It's better to just accept the phase shift and ensure everything stays aligned.
Diffstat (limited to 'core/bformatdec.cpp')
-rw-r--r--core/bformatdec.cpp80
1 files changed, 29 insertions, 51 deletions
diff --git a/core/bformatdec.cpp b/core/bformatdec.cpp
index b93c2f44..129b9976 100644
--- a/core/bformatdec.cpp
+++ b/core/bformatdec.cpp
@@ -88,15 +88,14 @@ void BFormatDec::processStablize(const al::span<FloatBufferLine> OutBuffer,
ASSUME(SamplesToDo > 0);
/* Move the existing direct L/R signal out so it doesn't get processed by
- * the stablizer. Add a delay to it so it stays aligned with the stablizer
- * delay.
+ * the stablizer.
*/
float *RESTRICT mid{al::assume_aligned<16>(mStablizer->MidDirect.data())};
float *RESTRICT side{al::assume_aligned<16>(mStablizer->Side.data())};
for(size_t i{0};i < SamplesToDo;++i)
{
- mid[FrontStablizer::DelayLength+i] = OutBuffer[lidx][i] + OutBuffer[ridx][i];
- side[FrontStablizer::DelayLength+i] = OutBuffer[lidx][i] - OutBuffer[ridx][i];
+ mid[i] = OutBuffer[lidx][i] + OutBuffer[ridx][i];
+ side[i] = OutBuffer[lidx][i] - OutBuffer[ridx][i];
}
std::fill_n(OutBuffer[lidx].begin(), SamplesToDo, 0.0f);
std::fill_n(OutBuffer[ridx].begin(), SamplesToDo, 0.0f);
@@ -104,55 +103,36 @@ void BFormatDec::processStablize(const al::span<FloatBufferLine> OutBuffer,
/* Decode the B-Format input to OutBuffer. */
process(OutBuffer, InSamples, SamplesToDo);
- /* Apply a delay to all channels, except the front-left and front-right, so
- * they maintain correct timing.
+ /* Include the decoded side signal with the direct side signal. */
+ for(size_t i{0};i < SamplesToDo;++i)
+ side[i] += OutBuffer[lidx][i] - OutBuffer[ridx][i];
+
+ /* Get the decoded mid signal and band-split it. */
+ std::transform(OutBuffer[lidx].cbegin(), OutBuffer[lidx].cbegin()+SamplesToDo,
+ OutBuffer[ridx].cbegin(), mStablizer->Temp.begin(),
+ [](const float l, const float r) noexcept { return l + r; });
+
+ mStablizer->MidFilter.process({mStablizer->Temp.data(), SamplesToDo}, mStablizer->MidHF.data(),
+ mStablizer->MidLF.data());
+
+ /* Apply an all-pass to all channels to match the band-splitter's phase
+ * shift. This is to keep the phase synchronized between the existing
+ * signal and the split mid signal.
*/
const size_t NumChannels{OutBuffer.size()};
for(size_t i{0u};i < NumChannels;i++)
{
- if(i == lidx || i == ridx)
- continue;
-
- auto &DelayBuf = mStablizer->DelayBuf[i];
- auto buffer_end = OutBuffer[i].begin() + SamplesToDo;
- if(SamplesToDo >= FrontStablizer::DelayLength) [[likely]]
- {
- auto delay_end = std::rotate(OutBuffer[i].begin(),
- buffer_end - FrontStablizer::DelayLength, buffer_end);
- std::swap_ranges(OutBuffer[i].begin(), delay_end, DelayBuf.begin());
- }
+ /* Skip the left and right channels, which are going to get overwritten,
+ * and substitute the direct mid signal and direct+decoded side signal.
+ */
+ if(i == lidx)
+ mStablizer->ChannelFilters[i].processAllPass({mid, SamplesToDo});
+ else if(i == ridx)
+ mStablizer->ChannelFilters[i].processAllPass({side, SamplesToDo});
else
- {
- auto delay_start = std::swap_ranges(OutBuffer[i].begin(), buffer_end,
- DelayBuf.begin());
- std::rotate(DelayBuf.begin(), delay_start, DelayBuf.end());
- }
+ mStablizer->ChannelFilters[i].processAllPass({OutBuffer[i].data(), SamplesToDo});
}
- /* Include the side signal for what was just decoded. */
- for(size_t i{0};i < SamplesToDo;++i)
- side[FrontStablizer::DelayLength+i] += OutBuffer[lidx][i] - OutBuffer[ridx][i];
-
- /* Combine the delayed mid signal with the decoded mid signal. */
- float *tmpbuf{mStablizer->TempBuf.data()};
- auto tmpiter = std::copy(mStablizer->MidDelay.cbegin(), mStablizer->MidDelay.cend(), tmpbuf);
- for(size_t i{0};i < SamplesToDo;++i,++tmpiter)
- *tmpiter = OutBuffer[lidx][i] + OutBuffer[ridx][i];
- /* Save the newest samples for next time. */
- std::copy_n(tmpbuf+SamplesToDo, mStablizer->MidDelay.size(), mStablizer->MidDelay.begin());
-
- /* Apply an all-pass on the signal in reverse. The future samples are
- * included with the all-pass to reduce the error in the output samples
- * (the smaller the delay, the more error is introduced).
- */
- mStablizer->MidFilter.applyAllpassRev({tmpbuf, SamplesToDo+FrontStablizer::DelayLength});
-
- /* Now apply the band-splitter, combining its phase shift with the reversed
- * phase shift, restoring the original phase on the split signal.
- */
- mStablizer->MidFilter.process({tmpbuf, SamplesToDo}, mStablizer->MidHF.data(),
- mStablizer->MidLF.data());
-
/* This pans the separate low- and high-frequency signals between being on
* the center channel and the left+right channels. The low-frequency signal
* is panned 1/3rd toward center and the high-frequency signal is panned
@@ -164,6 +144,9 @@ void BFormatDec::processStablize(const al::span<FloatBufferLine> OutBuffer,
const float sin_hf{std::sin(1.0f/4.0f * (al::numbers::pi_v<float>*0.5f))};
for(size_t i{0};i < SamplesToDo;i++)
{
+ /* Add the direct mid signal to the processed mid signal so it can be
+ * properly combined with the direct+decoded side signal.
+ */
const float m{mStablizer->MidLF[i]*cos_lf + mStablizer->MidHF[i]*cos_hf + mid[i]};
const float c{mStablizer->MidLF[i]*sin_lf + mStablizer->MidHF[i]*sin_hf};
const float s{side[i]};
@@ -175,11 +158,6 @@ void BFormatDec::processStablize(const al::span<FloatBufferLine> OutBuffer,
OutBuffer[ridx][i] = (m - s) * 0.5f;
OutBuffer[cidx][i] += c * 0.5f;
}
- /* Move the delayed mid/side samples to the front for next time. */
- auto mid_end = mStablizer->MidDirect.cbegin() + SamplesToDo;
- std::copy(mid_end, mid_end+FrontStablizer::DelayLength, mStablizer->MidDirect.begin());
- auto side_end = mStablizer->Side.cbegin() + SamplesToDo;
- std::copy(side_end, side_end+FrontStablizer::DelayLength, mStablizer->Side.begin());
}