diff options
-rw-r--r-- | alc/alc.cpp | 30 | ||||
-rw-r--r-- | alc/alcmain.h | 4 | ||||
-rw-r--r-- | alc/alu.cpp | 116 | ||||
-rw-r--r-- | alc/bformatdec.cpp | 136 | ||||
-rw-r--r-- | alc/bformatdec.h | 30 | ||||
-rw-r--r-- | alc/front_stablizer.h | 1 | ||||
-rw-r--r-- | alc/panning.cpp | 102 |
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; } |