aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--alc/alc.cpp30
-rw-r--r--alc/alcmain.h4
-rw-r--r--alc/alu.cpp116
-rw-r--r--alc/bformatdec.cpp136
-rw-r--r--alc/bformatdec.h30
-rw-r--r--alc/front_stablizer.h1
-rw-r--r--alc/panning.cpp102
7 files changed, 254 insertions, 165 deletions
diff --git a/alc/alc.cpp b/alc/alc.cpp
index c996ecf8..e84b26e5 100644
--- a/alc/alc.cpp
+++ b/alc/alc.cpp
@@ -1870,7 +1870,6 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList)
device->Bs2b = nullptr;
device->PostProcess = nullptr;
- device->Stablizer = nullptr;
device->Limiter = nullptr;
device->ChannelDelay.clear();
@@ -1986,39 +1985,14 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList)
}
if(device->mHrtfState)
device->FixedLatency += nanoseconds{seconds{HRTF_DIRECT_DELAY}} / device->Frequency;
-
- /* Enable the stablizer only for formats that have front-left, front-right,
- * and front-center outputs.
- */
- switch(device->FmtChans)
+ if(auto *ambidec = device->AmbiDecoder.get())
{
- case DevFmtX51:
- case DevFmtX51Rear:
- case DevFmtX61:
- case DevFmtX71:
- if(GetConfigValueBool(device->DeviceName.c_str(), nullptr, "front-stablizer", 0))
+ if(ambidec->hasStablizer())
{
- auto stablizer = FrontStablizer::Create(device->channelsFromFmt());
- for(auto &buf : stablizer->DelayBuf)
- std::fill(buf.begin(), buf.end(), 0.0f);
-
- /* Initialize band-splitting filter for the mid signal, with a
- * crossover at 5khz (could be higher).
- */
- stablizer->MidFilter.init(5000.0f / static_cast<float>(device->Frequency));
-
- device->Stablizer = std::move(stablizer);
constexpr size_t StablizerDelay{FrontStablizer::DelayLength};
device->FixedLatency += nanoseconds{seconds{StablizerDelay}} / device->Frequency;
}
- break;
- case DevFmtMono:
- case DevFmtStereo:
- case DevFmtQuad:
- case DevFmtAmbi3D:
- break;
}
- TRACE("Front stablizer %s\n", device->Stablizer ? "enabled" : "disabled");
if(GetConfigValueBool(device->DeviceName.c_str(), nullptr, "dither", 1))
{
diff --git a/alc/alcmain.h b/alc/alcmain.h
index a05cc511..8d802d85 100644
--- a/alc/alcmain.h
+++ b/alc/alcmain.h
@@ -38,7 +38,6 @@ struct ALfilter;
struct BackendBase;
struct Compressor;
struct EffectState;
-struct FrontStablizer;
struct Uhj2Encoder;
struct bs2b;
@@ -299,8 +298,6 @@ struct ALCdevice : public al::intrusive_ref<ALCdevice> {
using PostProc = void(ALCdevice::*)(const size_t SamplesToDo);
PostProc PostProcess{nullptr};
- std::unique_ptr<FrontStablizer> Stablizer;
-
std::unique_ptr<Compressor> Limiter;
/* Delay buffers used to compensate for speaker distances. */
@@ -347,6 +344,7 @@ struct ALCdevice : public al::intrusive_ref<ALCdevice> {
void ProcessHrtf(const size_t SamplesToDo);
void ProcessAmbiDec(const size_t SamplesToDo);
+ void ProcessAmbiDecStablized(const size_t SamplesToDo);
void ProcessUhj(const size_t SamplesToDo);
void ProcessBs2b(const size_t SamplesToDo);
diff --git a/alc/alu.cpp b/alc/alu.cpp
index 9b077304..adc17877 100644
--- a/alc/alu.cpp
+++ b/alc/alu.cpp
@@ -292,6 +292,17 @@ void ALCdevice::ProcessAmbiDec(const size_t SamplesToDo)
AmbiDecoder->process(RealOut.Buffer, Dry.Buffer.data(), SamplesToDo);
}
+void ALCdevice::ProcessAmbiDecStablized(const size_t SamplesToDo)
+{
+ /* Decode with front image stablization. */
+ const ALuint lidx{RealOut.ChannelIndex[FrontLeft]};
+ const ALuint ridx{RealOut.ChannelIndex[FrontRight]};
+ const ALuint cidx{RealOut.ChannelIndex[FrontCenter]};
+
+ AmbiDecoder->processStablize(RealOut.Buffer, Dry.Buffer.data(), lidx, ridx, cidx,
+ SamplesToDo);
+}
+
void ALCdevice::ProcessUhj(const size_t SamplesToDo)
{
/* UHJ is stereo output only. */
@@ -1800,101 +1811,6 @@ void ProcessContexts(ALCdevice *device, const ALuint SamplesToDo)
}
-/* FIXME: This shouldn't really be applied to the final output mix like this,
- * since a source mixed with direct channels shouldn't be subjected to this
- * filtering. The only way to do that is as part of the ambisonic decode (in
- * BFormatDec::process) where it can separate the pre-mixed direct-channel feed
- * from the decoded soundfield.
- */
-void ApplyStablizer(FrontStablizer *Stablizer, const al::span<FloatBufferLine> Buffer,
- const size_t lidx, const size_t ridx, const size_t cidx, const size_t SamplesToDo)
-{
- ASSUME(SamplesToDo > 0);
-
- /* Apply a delay to all channels, except the front-left and front-right, so
- * they maintain correct timing.
- */
- const size_t NumChannels{Buffer.size()};
- for(size_t i{0u};i < NumChannels;i++)
- {
- if(i == lidx || i == ridx)
- continue;
-
- auto &DelayBuf = Stablizer->DelayBuf[i];
- auto buffer_end = Buffer[i].begin() + SamplesToDo;
- if LIKELY(SamplesToDo >= FrontStablizer::DelayLength)
- {
- auto delay_end = std::rotate(Buffer[i].begin(),
- buffer_end - FrontStablizer::DelayLength, buffer_end);
- std::swap_ranges(Buffer[i].begin(), delay_end, DelayBuf.begin());
- }
- else
- {
- auto delay_start = std::swap_ranges(Buffer[i].begin(), buffer_end,
- DelayBuf.begin());
- std::rotate(DelayBuf.begin(), delay_start, DelayBuf.end());
- }
- }
-
- /* Add a delay to the incoming side signal to keep it aligned with the mid
- * filter delay.
- */
- for(size_t i{0};i < SamplesToDo;++i)
- Stablizer->Side[FrontStablizer::DelayLength+i] = Buffer[lidx][i] - Buffer[ridx][i];
-
- /* Combine the delayed mid signal with the incoming signal. Note that the
- * samples are stored and combined in reverse, so the newest samples are at
- * the front and the oldest at the back.
- */
- al::span<float> tmpbuf{Stablizer->TempBuf};
- auto tmpiter = tmpbuf.begin() + SamplesToDo;
- std::copy(Stablizer->MidDelay.cbegin(), Stablizer->MidDelay.cend(), tmpiter);
- for(size_t i{0};i < SamplesToDo;++i)
- *--tmpiter = Buffer[lidx][i] + Buffer[ridx][i];
- /* Save the newest samples for next time. */
- std::copy_n(tmpbuf.cbegin(), Stablizer->MidDelay.size(), Stablizer->MidDelay.begin());
-
- /* Apply an all-pass on the reversed signal, then reverse the samples to
- * get the forward signal with a reversed phase shift. 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).
- */
- Stablizer->MidFilter.applyAllpass(tmpbuf);
- tmpbuf = tmpbuf.subspan<FrontStablizer::DelayLength>();
- std::reverse(tmpbuf.begin(), tmpbuf.end());
-
- /* Now apply the band-splitter, combining its phase shift with the reversed
- * phase shift, restoring the original phase on the split signal.
- */
- Stablizer->MidFilter.process(tmpbuf, Stablizer->MidHF.data(), Stablizer->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
- * 1/4th toward center. These values can be tweaked.
- */
- const float cos_lf{std::cos(1.0f/3.0f * (al::MathDefs<float>::Pi()*0.5f))};
- const float cos_hf{std::cos(1.0f/4.0f * (al::MathDefs<float>::Pi()*0.5f))};
- const float sin_lf{std::sin(1.0f/3.0f * (al::MathDefs<float>::Pi()*0.5f))};
- const float sin_hf{std::sin(1.0f/4.0f * (al::MathDefs<float>::Pi()*0.5f))};
- for(ALuint i{0};i < SamplesToDo;i++)
- {
- const float m{Stablizer->MidLF[i]*cos_lf + Stablizer->MidHF[i]*cos_hf};
- const float c{Stablizer->MidLF[i]*sin_lf + Stablizer->MidHF[i]*sin_hf};
- const float s{Stablizer->Side[i]};
-
- /* The generated center channel signal adds to the existing signal,
- * while the modified left and right channels replace.
- */
- Buffer[lidx][i] = (m + s) * 0.5f;
- Buffer[ridx][i] = (m - s) * 0.5f;
- Buffer[cidx][i] += c * 0.5f;
- }
- /* Move the delayed side samples to the front for next time. */
- auto side_end = Stablizer->Side.cbegin() + SamplesToDo;
- std::copy(side_end, side_end+FrontStablizer::DelayLength, Stablizer->Side.begin());
-}
-
void ApplyDistanceComp(const al::span<FloatBufferLine> Samples, const size_t SamplesToDo,
const DistanceComp::DistData *distcomp)
{
@@ -2042,16 +1958,6 @@ void aluMixData(ALCdevice *device, void *OutBuffer, const ALuint NumSamples,
const al::span<FloatBufferLine> RealOut{device->RealOut.Buffer};
- /* Apply front image stablization for surround sound, if applicable. */
- if(FrontStablizer *stablizer{device->Stablizer.get()})
- {
- const ALuint lidx{GetChannelIdxByName(device->RealOut, FrontLeft)};
- const ALuint ridx{GetChannelIdxByName(device->RealOut, FrontRight)};
- const ALuint cidx{GetChannelIdxByName(device->RealOut, FrontCenter)};
-
- ApplyStablizer(stablizer, RealOut, lidx, ridx, cidx, SamplesToDo);
- }
-
/* Apply compression, limiting sample amplitude if needed or desired. */
if(Compressor *comp{device->Limiter.get()})
comp->process(SamplesToDo, RealOut.data());
diff --git a/alc/bformatdec.cpp b/alc/bformatdec.cpp
index 41710332..b2a2aec9 100644
--- a/alc/bformatdec.cpp
+++ b/alc/bformatdec.cpp
@@ -16,6 +16,8 @@
#include "alu.h"
#include "ambdec.h"
#include "filters/splitter.h"
+#include "front_stablizer.h"
+#include "math_defs.h"
#include "opthelpers.h"
@@ -50,10 +52,11 @@ inline auto GetAmbiScales(AmbDecScale scaletype) noexcept
BFormatDec::BFormatDec(const AmbDecConf *conf, const bool allow_2band, const size_t inchans,
- const ALuint srate, const ALuint (&chanmap)[MAX_OUTPUT_CHANNELS]) : mChannelDec{inchans}
+ const ALuint srate, const ALuint (&chanmap)[MAX_OUTPUT_CHANNELS],
+ std::unique_ptr<FrontStablizer> stablizer)
+ : mStablizer{std::move(stablizer)}, mDualBand{allow_2band && (conf->FreqBands == 2)}
+ , mChannelDec{inchans}
{
- mDualBand = allow_2band && (conf->FreqBands == 2);
-
const bool periphonic{(conf->ChanMask&AMBI_PERIPHONIC_MASK) != 0};
const std::array<float,MAX_AMBI_CHANNELS> &coeff_scale = GetAmbiScales(conf->CoeffScale);
@@ -99,10 +102,9 @@ BFormatDec::BFormatDec(const AmbDecConf *conf, const bool allow_2band, const siz
}
BFormatDec::BFormatDec(const size_t inchans, const al::span<const ChannelDec> coeffs,
- const al::span<const ChannelDec> coeffslf) : mChannelDec{inchans}
+ const al::span<const ChannelDec> coeffslf, std::unique_ptr<FrontStablizer> stablizer)
+ : mStablizer{std::move(stablizer)}, mDualBand{!coeffslf.empty()}, mChannelDec{inchans}
{
- mDualBand = !coeffslf.empty();
-
if(!mDualBand)
{
for(size_t j{0};j < mChannelDec.size();++j)
@@ -159,6 +161,113 @@ void BFormatDec::process(const al::span<FloatBufferLine> OutBuffer,
}
}
+void BFormatDec::processStablize(const al::span<FloatBufferLine> OutBuffer,
+ const FloatBufferLine *InSamples, const size_t lidx, const size_t ridx, const size_t cidx,
+ const size_t SamplesToDo)
+{
+ 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.
+ */
+ 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];
+ }
+ std::fill_n(OutBuffer[lidx].begin(), SamplesToDo, 0.0f);
+ std::fill_n(OutBuffer[ridx].begin(), SamplesToDo, 0.0f);
+
+ /* 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.
+ */
+ 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 LIKELY(SamplesToDo >= FrontStablizer::DelayLength)
+ {
+ auto delay_end = std::rotate(OutBuffer[i].begin(),
+ buffer_end - FrontStablizer::DelayLength, buffer_end);
+ std::swap_ranges(OutBuffer[i].begin(), delay_end, DelayBuf.begin());
+ }
+ else
+ {
+ auto delay_start = std::swap_ranges(OutBuffer[i].begin(), buffer_end,
+ DelayBuf.begin());
+ std::rotate(DelayBuf.begin(), delay_start, DelayBuf.end());
+ }
+ }
+
+ /* 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. Note that
+ * the samples are stored and combined in reverse, so the newest samples
+ * are at the front and the oldest at the back.
+ */
+ al::span<float> tmpbuf{mStablizer->TempBuf.data(), SamplesToDo+FrontStablizer::DelayLength};
+ auto tmpiter = tmpbuf.begin() + SamplesToDo;
+ std::copy(mStablizer->MidDelay.cbegin(), mStablizer->MidDelay.cend(), tmpiter);
+ for(size_t i{0};i < SamplesToDo;++i)
+ *--tmpiter = OutBuffer[lidx][i] + OutBuffer[ridx][i];
+ /* Save the newest samples for next time. */
+ std::copy_n(tmpbuf.cbegin(), mStablizer->MidDelay.size(), mStablizer->MidDelay.begin());
+
+ /* Apply an all-pass on the reversed signal, then reverse the samples to
+ * get the forward signal with a reversed phase shift. 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.applyAllpass(tmpbuf);
+ tmpbuf = tmpbuf.subspan<FrontStablizer::DelayLength>();
+ std::reverse(tmpbuf.begin(), tmpbuf.end());
+
+ /* 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, 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
+ * 1/4th toward center. These values can be tweaked.
+ */
+ const float cos_lf{std::cos(1.0f/3.0f * (al::MathDefs<float>::Pi()*0.5f))};
+ const float cos_hf{std::cos(1.0f/4.0f * (al::MathDefs<float>::Pi()*0.5f))};
+ const float sin_lf{std::sin(1.0f/3.0f * (al::MathDefs<float>::Pi()*0.5f))};
+ const float sin_hf{std::sin(1.0f/4.0f * (al::MathDefs<float>::Pi()*0.5f))};
+ for(size_t i{0};i < SamplesToDo;i++)
+ {
+ 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]};
+
+ /* The generated center channel signal adds to the existing signal,
+ * while the modified left and right channels replace.
+ */
+ OutBuffer[lidx][i] = (m + s) * 0.5f;
+ 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());
+}
+
auto BFormatDec::GetHFOrderScales(const ALuint in_order, const ALuint out_order) noexcept
-> std::array<float,MAX_AMBI_ORDER+1>
@@ -175,3 +284,18 @@ auto BFormatDec::GetHFOrderScales(const ALuint in_order, const ALuint out_order)
return ret;
}
+
+std::unique_ptr<BFormatDec> BFormatDec::Create(const AmbDecConf *conf, const bool allow_2band,
+ const size_t inchans, const ALuint srate, const ALuint (&chanmap)[MAX_OUTPUT_CHANNELS],
+ std::unique_ptr<FrontStablizer> stablizer)
+{
+ return std::unique_ptr<BFormatDec>{new(FamCount(inchans))
+ BFormatDec{conf, allow_2band, inchans, srate, chanmap, std::move(stablizer)}};
+}
+std::unique_ptr<BFormatDec> BFormatDec::Create(const size_t inchans,
+ const al::span<const ChannelDec> coeffs, const al::span<const ChannelDec> coeffslf,
+ std::unique_ptr<FrontStablizer> stablizer)
+{
+ return std::unique_ptr<BFormatDec>{new(FamCount(inchans))
+ BFormatDec{inchans, coeffs, coeffslf, std::move(stablizer)}};
+}
diff --git a/alc/bformatdec.h b/alc/bformatdec.h
index 428af281..03249c55 100644
--- a/alc/bformatdec.h
+++ b/alc/bformatdec.h
@@ -15,6 +15,7 @@
#include "filters/splitter.h"
struct AmbDecConf;
+struct FrontStablizer;
using ChannelDec = std::array<float,MAX_AMBI_CHANNELS>;
@@ -36,36 +37,39 @@ class BFormatDec {
alignas(16) std::array<FloatBufferLine,2> mSamples;
- bool mDualBand{false};
+ const std::unique_ptr<FrontStablizer> mStablizer;
+ const bool mDualBand{false};
al::FlexArray<ChannelDecoder> mChannelDec;
public:
BFormatDec(const AmbDecConf *conf, const bool allow_2band, const size_t inchans,
- const ALuint srate, const ALuint (&chanmap)[MAX_OUTPUT_CHANNELS]);
+ const ALuint srate, const ALuint (&chanmap)[MAX_OUTPUT_CHANNELS],
+ std::unique_ptr<FrontStablizer> stablizer);
BFormatDec(const size_t inchans, const al::span<const ChannelDec> coeffs,
- const al::span<const ChannelDec> coeffslf);
+ const al::span<const ChannelDec> coeffslf, std::unique_ptr<FrontStablizer> stablizer);
+
+ bool hasStablizer() const noexcept { return mStablizer != nullptr; };
/* Decodes the ambisonic input to the given output channels. */
void process(const al::span<FloatBufferLine> OutBuffer, const FloatBufferLine *InSamples,
const size_t SamplesToDo);
+ /* Decodes the ambisonic input to the given output channels with stablization. */
+ void processStablize(const al::span<FloatBufferLine> OutBuffer,
+ const FloatBufferLine *InSamples, const size_t lidx, const size_t ridx, const size_t cidx,
+ const size_t SamplesToDo);
+
/* Retrieves per-order HF scaling factors for "upsampling" ambisonic data. */
static std::array<float,MAX_AMBI_ORDER+1> GetHFOrderScales(const ALuint in_order,
const ALuint out_order) noexcept;
static std::unique_ptr<BFormatDec> Create(const AmbDecConf *conf, const bool allow_2band,
- const size_t inchans, const ALuint srate, const ALuint (&chanmap)[MAX_OUTPUT_CHANNELS])
- {
- return std::unique_ptr<BFormatDec>{new(FamCount(inchans))
- BFormatDec{conf, allow_2band, inchans, srate, chanmap}};
- }
+ const size_t inchans, const ALuint srate, const ALuint (&chanmap)[MAX_OUTPUT_CHANNELS],
+ std::unique_ptr<FrontStablizer> stablizer);
static std::unique_ptr<BFormatDec> Create(const size_t inchans,
- const al::span<const ChannelDec> coeffs, const al::span<const ChannelDec> coeffslf)
- {
- return std::unique_ptr<BFormatDec>{new(FamCount(inchans))
- BFormatDec{inchans, coeffs, coeffslf}};
- }
+ const al::span<const ChannelDec> coeffs, const al::span<const ChannelDec> coeffslf,
+ std::unique_ptr<FrontStablizer> stablizer);
DEF_FAM_NEWDEL(BFormatDec, mChannelDec)
};
diff --git a/alc/front_stablizer.h b/alc/front_stablizer.h
index da39618e..5e7c267f 100644
--- a/alc/front_stablizer.h
+++ b/alc/front_stablizer.h
@@ -15,6 +15,7 @@ struct FrontStablizer {
FrontStablizer(size_t numchans) : DelayBuf{numchans} { }
alignas(16) std::array<float,BUFFERSIZE + DelayLength> Side{};
+ alignas(16) std::array<float,BUFFERSIZE + DelayLength> MidDirect{};
alignas(16) std::array<float,DelayLength> MidDelay{};
alignas(16) std::array<float,BUFFERSIZE + DelayLength> TempBuf{};
diff --git a/alc/panning.cpp b/alc/panning.cpp
index 85340d66..e62e2baf 100644
--- a/alc/panning.cpp
+++ b/alc/panning.cpp
@@ -51,6 +51,7 @@
#include "bformatdec.h"
#include "bs2b.h"
#include "devformat.h"
+#include "front_stablizer.h"
#include "hrtf.h"
#include "logging.h"
#include "math_defs.h"
@@ -121,6 +122,20 @@ inline const char *GetLabelFromChannel(Channel channel)
}
+std::unique_ptr<FrontStablizer> CreateStablizer(const size_t outchans, const ALuint srate)
+{
+ auto stablizer = FrontStablizer::Create(outchans);
+ for(auto &buf : stablizer->DelayBuf)
+ std::fill(buf.begin(), buf.end(), 0.0f);
+
+ /* Initialize band-splitting filter for the mid signal, with a crossover at
+ * 5khz (could be higher).
+ */
+ stablizer->MidFilter.init(5000.0f / static_cast<float>(srate));
+
+ return stablizer;
+}
+
void AllocChannels(ALCdevice *device, const size_t main_chans, const size_t real_chans)
{
TRACE("Channel config, Main: %zu, Real: %zu\n", main_chans, real_chans);
@@ -476,7 +491,7 @@ constexpr DecoderConfig<DualBand, 6> X71Config{
}}
};
-void InitPanning(ALCdevice *device, const bool hqdec=false)
+void InitPanning(ALCdevice *device, const bool hqdec=false, const bool stablize=false)
{
DecoderView decoder{};
switch(device->FmtChans)
@@ -577,17 +592,43 @@ void InitPanning(ALCdevice *device, const bool hqdec=false)
);
AllocChannels(device, ambicount, device->channelsFromFmt());
+ std::unique_ptr<FrontStablizer> stablizer;
+ if(stablize)
+ {
+ /* Only enable the stablizer if the decoder does not output to the
+ * front-center channel.
+ */
+ const auto cidx = device->RealOut.ChannelIndex[FrontCenter];
+ bool hasfc{false};
+ if(cidx < chancoeffs.size())
+ {
+ for(const auto &coeff : chancoeffs[cidx])
+ hasfc |= coeff != 0.0f;
+ }
+ if(!hasfc && cidx < chancoeffslf.size())
+ {
+ for(const auto &coeff : chancoeffslf[cidx])
+ hasfc |= coeff != 0.0f;
+ }
+ if(!hasfc)
+ {
+ stablizer = CreateStablizer(device->channelsFromFmt(), device->Frequency);
+ TRACE("Front stablizer enabled\n");
+ }
+ }
+
TRACE("Enabling %s-band %s-order%s ambisonic decoder\n",
!dual_band ? "single" : "dual",
(decoder.mOrder > 2) ? "third" :
(decoder.mOrder > 1) ? "second" : "first",
"");
- device->AmbiDecoder = BFormatDec::Create(ambicount, chancoeffs, chancoeffslf);
+ device->AmbiDecoder = BFormatDec::Create(ambicount, chancoeffs, chancoeffslf,
+ std::move(stablizer));
}
}
-void InitCustomPanning(ALCdevice *device, bool hqdec, const AmbDecConf *conf,
- const ALuint (&speakermap)[MAX_OUTPUT_CHANNELS])
+void InitCustomPanning(ALCdevice *device, const bool hqdec, const bool stablize,
+ const AmbDecConf *conf, const ALuint (&speakermap)[MAX_OUTPUT_CHANNELS])
{
if(!hqdec && conf->FreqBands != 1)
ERR("Basic renderer uses the high-frequency matrix as single-band (xover_freq = %.0fhz)\n",
@@ -616,13 +657,44 @@ void InitCustomPanning(ALCdevice *device, bool hqdec, const AmbDecConf *conf,
}
AllocChannels(device, count, device->channelsFromFmt());
+ std::unique_ptr<FrontStablizer> stablizer;
+ if(stablize)
+ {
+ /* Only enable the stablizer if the decoder does not output to the
+ * front-center channel.
+ */
+ size_t cidx{0};
+ for(;cidx < conf->Speakers.size();++cidx)
+ {
+ if(speakermap[cidx] == FrontCenter)
+ break;
+ }
+ bool hasfc{false};
+ if(cidx < conf->LFMatrix.size())
+ {
+ for(const auto &coeff : conf->LFMatrix[cidx])
+ hasfc |= coeff != 0.0f;
+ }
+ if(!hasfc && cidx < conf->HFMatrix.size())
+ {
+ for(const auto &coeff : conf->HFMatrix[cidx])
+ hasfc |= coeff != 0.0f;
+ }
+ if(!hasfc)
+ {
+ stablizer = CreateStablizer(device->channelsFromFmt(), device->Frequency);
+ TRACE("Front stablizer enabled\n");
+ }
+ }
+
TRACE("Enabling %s-band %s-order%s ambisonic decoder\n",
(!hqdec || conf->FreqBands == 1) ? "single" : "dual",
(conf->ChanMask > AMBI_2ORDER_MASK) ? "third" :
(conf->ChanMask > AMBI_1ORDER_MASK) ? "second" : "first",
(conf->ChanMask&AMBI_PERIPHONIC_MASK) ? " periphonic" : ""
);
- device->AmbiDecoder = BFormatDec::Create(conf, hqdec, count, device->Frequency, speakermap);
+ device->AmbiDecoder = BFormatDec::Create(conf, hqdec, count, device->Frequency, speakermap,
+ std::move(stablizer));
auto accum_spkr_dist = std::bind(std::plus<float>{}, _1,
std::bind(std::mem_fn(&AmbDecConf::SpeakerConf::Distance), _2));
@@ -852,13 +924,23 @@ void aluInitRenderer(ALCdevice *device, int hrtf_id, HrtfRequestMode hrtf_appreq
}
}
- const int hqdec{GetConfigValueBool(devname, "decoder", "hq-mode", 1)};
+ /* Enable the stablizer only for formats that have front-left, front-
+ * right, and front-center outputs.
+ */
+ const bool stablize{device->RealOut.ChannelIndex[FrontCenter] != INVALID_CHANNEL_INDEX
+ && device->RealOut.ChannelIndex[FrontLeft] != INVALID_CHANNEL_INDEX
+ && device->RealOut.ChannelIndex[FrontRight] != INVALID_CHANNEL_INDEX
+ && GetConfigValueBool(devname, nullptr, "front-stablizer", 0) != 0};
+ const bool hqdec{GetConfigValueBool(devname, "decoder", "hq-mode", 1) != 0};
if(!pconf)
- InitPanning(device, !!hqdec);
+ InitPanning(device, hqdec, stablize);
else
- InitCustomPanning(device, !!hqdec, pconf, speakermap);
- if(device->AmbiDecoder)
- device->PostProcess = &ALCdevice::ProcessAmbiDec;
+ InitCustomPanning(device, hqdec, stablize, pconf, speakermap);
+ if(auto *ambidec{device->AmbiDecoder.get()})
+ {
+ device->PostProcess = ambidec->hasStablizer() ? &ALCdevice::ProcessAmbiDecStablized
+ : &ALCdevice::ProcessAmbiDec;
+ }
return;
}