diff options
-rw-r--r-- | alc/effects/convolution.cpp | 13 | ||||
-rw-r--r-- | alc/effects/reverb.cpp | 33 | ||||
-rw-r--r-- | core/ambidefs.cpp | 19 | ||||
-rw-r--r-- | core/ambidefs.h | 14 | ||||
-rw-r--r-- | core/voice.cpp | 56 |
5 files changed, 119 insertions, 16 deletions
diff --git a/alc/effects/convolution.cpp b/alc/effects/convolution.cpp index f72695bd..90220a50 100644 --- a/alc/effects/convolution.cpp +++ b/alc/effects/convolution.cpp @@ -194,6 +194,8 @@ struct ConvolutionState final : public EffectState { struct ChannelData { alignas(16) FloatBufferLine mBuffer{}; + float mHfScale{}; + BandSplitter mFilter{}; float Current[MAX_OUTPUT_CHANNELS]{}; float Target[MAX_OUTPUT_CHANNELS]{}; }; @@ -233,6 +235,7 @@ void ConvolutionState::UpsampleMix(const al::span<FloatBufferLine> samplesOut, for(auto &chan : *mChans) { const al::span<float> src{chan.mBuffer.data(), samplesToDo}; + chan.mFilter.processHfScale(src, chan.mHfScale); MixSamples(src, samplesOut, chan.Current, chan.Target, samplesToDo, 0); } } @@ -277,6 +280,10 @@ void ConvolutionState::deviceUpdate(const DeviceBase *device, const Buffer &buff (uint64_t{buffer.storage->mSampleLen}*device->Frequency+(buffer.storage->mSampleRate-1)) / buffer.storage->mSampleRate); + const BandSplitter splitter{device->mXOverFreq / static_cast<float>(device->Frequency)}; + for(auto &e : *mChans) + e.mFilter = splitter; + mFilter.resize(numChannels, {}); mOutput.resize(numChannels, {}); @@ -411,7 +418,13 @@ void ConvolutionState::update(const ContextBase *context, const EffectSlot *slot { DeviceBase *device{context->mDevice}; if(device->mAmbiOrder > mAmbiOrder) + { mMix = &ConvolutionState::UpsampleMix; + const auto scales = AmbiScale::GetHFOrderScales(mAmbiOrder, true); + (*mChans)[0].mHfScale = scales[0]; + for(size_t i{1};i < mChans->size();++i) + (*mChans)[i].mHfScale = scales[1]; + } mOutTarget = target.Main->Buffer; auto&& scales = GetAmbiScales(mAmbiScaling); diff --git a/alc/effects/reverb.cpp b/alc/effects/reverb.cpp index 72f58db5..ea3367d0 100644 --- a/alc/effects/reverb.cpp +++ b/alc/effects/reverb.cpp @@ -453,6 +453,8 @@ struct ReverbState final : public EffectState { bool mUpmixOutput{false}; + std::array<float,MaxAmbiOrder+1> mOrderScales{}; + std::array<std::array<BandSplitter,NUM_LINES>,2> mAmbiSplitter; static void DoMixRow(const al::span<float> OutBuffer, const al::span<const float,4> Gains, @@ -499,19 +501,30 @@ struct ReverbState final : public EffectState { { ASSUME(todo > 0); - /* TODO: If HF scaling isn't needed for upsampling, the A-to-B-Format - * matrix can be included with the panning gains like non-upsampled - * output. + /* When upsampling, the B-Format conversion needs to be done separately + * so the proper HF scaling can be applied to each B-Format channel. + * The panning gains then pan and upsample the B-Format channels. */ const al::span<float> tmpspan{al::assume_aligned<16>(mTempLine.data()), todo}; for(size_t c{0u};c < NUM_LINES;c++) { DoMixRow(tmpspan, EarlyA2B[c], mEarlySamples[0].data(), mEarlySamples[0].size()); + + /* Apply scaling to the B-Format's HF response to "upsample" it to + * higher-order output. + */ + const float hfscale{(c==0) ? mOrderScales[0] : mOrderScales[1]}; + mAmbiSplitter[0][c].processHfScale(tmpspan, hfscale); + MixSamples(tmpspan, samplesOut, mEarly.CurrentGain[c], mEarly.PanGain[c], todo, 0); } for(size_t c{0u};c < NUM_LINES;c++) { DoMixRow(tmpspan, LateA2B[c], mLateSamples[0].data(), mLateSamples[0].size()); + + const float hfscale{(c==0) ? mOrderScales[0] : mOrderScales[1]}; + mAmbiSplitter[1][c].processHfScale(tmpspan, hfscale); + MixSamples(tmpspan, samplesOut, mLate.CurrentGain[c], mLate.PanGain[c], todo, 0); } } @@ -670,7 +683,19 @@ void ReverbState::deviceUpdate(const DeviceBase *device, const Buffer&) mDoFading = true; mOffset = 0; - mUpmixOutput = (device->mAmbiOrder > 1); + if(device->mAmbiOrder > 1) + { + mUpmixOutput = true; + mOrderScales = AmbiScale::GetHFOrderScales(1, true); + } + else + { + mUpmixOutput = false; + mOrderScales.fill(1.0f); + } + mAmbiSplitter[0][0].init(device->mXOverFreq / frequency); + std::fill(mAmbiSplitter[0].begin()+1, mAmbiSplitter[0].end(), mAmbiSplitter[0][0]); + std::fill(mAmbiSplitter[1].begin(), mAmbiSplitter[1].end(), mAmbiSplitter[0][0]); } /************************************** diff --git a/core/ambidefs.cpp b/core/ambidefs.cpp index e34b520e..864144b8 100644 --- a/core/ambidefs.cpp +++ b/core/ambidefs.cpp @@ -366,3 +366,22 @@ const std::array<AmbiChannelFloatArray,9> AmbiScale::SecondOrder2DUp{CalcSecondO const std::array<AmbiChannelFloatArray,16> AmbiScale::ThirdOrderUp{CalcThirdOrderUp()}; const std::array<AmbiChannelFloatArray,16> AmbiScale::ThirdOrder2DUp{CalcThirdOrder2DUp()}; const std::array<AmbiChannelFloatArray,25> AmbiScale::FourthOrder2DUp{CalcFourthOrder2DUp()}; + +const std::array<float,MaxAmbiOrder+1> AmbiScale::DecoderHFScale1O{{ + 2.000000000e+00f, 1.154700538e+00f +}}; +const std::array<float,MaxAmbiOrder+1> AmbiScale::DecoderHFScale1O2D{{ + 1.414213562e+00f, 1.000000000e+00f +}}; +const std::array<float,MaxAmbiOrder+1> AmbiScale::DecoderHFScale2O{{ + 1.972026594e+00f, 1.527525232e+00f, 7.888106377e-01f +}}; +const std::array<float,MaxAmbiOrder+1> AmbiScale::DecoderHFScale2O2D{{ + 1.414213562e+00f, 1.224744871e+00f, 7.071067812e-01f +}}; +const std::array<float,MaxAmbiOrder+1> AmbiScale::DecoderHFScale3O{{ + 1.865086714e+00f, 1.606093894e+00f, 1.142055301e+00f, 5.683795528e-01f +}}; +const std::array<float,MaxAmbiOrder+1> AmbiScale::DecoderHFScale3O2D{{ + 1.414213562e+00f, 1.306562965e+00f, 1.000000000e+00f, 5.411961001e-01f +}}; diff --git a/core/ambidefs.h b/core/ambidefs.h index 70fa23c5..7f0f14ad 100644 --- a/core/ambidefs.h +++ b/core/ambidefs.h @@ -113,6 +113,20 @@ struct AmbiScale { return ret; } + /* Retrieves per-order HF scaling factors for "upsampling" ambisonic data. */ + static std::array<float,MaxAmbiOrder+1> GetHFOrderScales(const uint order, const bool is3D) noexcept + { + if(order >= 3) return is3D ? DecoderHFScale3O : DecoderHFScale3O2D; + if(order == 2) return is3D ? DecoderHFScale2O : DecoderHFScale2O2D; + return is3D ? DecoderHFScale1O : DecoderHFScale1O2D; + } + + static const std::array<float,MaxAmbiOrder+1> DecoderHFScale1O; + static const std::array<float,MaxAmbiOrder+1> DecoderHFScale1O2D; + static const std::array<float,MaxAmbiOrder+1> DecoderHFScale2O; + static const std::array<float,MaxAmbiOrder+1> DecoderHFScale2O2D; + static const std::array<float,MaxAmbiOrder+1> DecoderHFScale3O; + static const std::array<float,MaxAmbiOrder+1> DecoderHFScale3O2D; static const std::array<std::array<float,MaxAmbiChannels>,4> FirstOrderUp; static const std::array<std::array<float,MaxAmbiChannels>,4> FirstOrder2DUp; diff --git a/core/voice.cpp b/core/voice.cpp index 15330012..5263202e 100644 --- a/core/voice.cpp +++ b/core/voice.cpp @@ -892,20 +892,52 @@ void Voice::prepare(DeviceBase *device) /* Make sure the sample history is cleared. */ std::fill(mPrevSamples.begin(), mPrevSamples.end(), HistoryLine{}); - /* 2-channel UHJ needs different shelf filters. However, we can't just use - * different shelf filters after mixing it, given any old speaker setup the - * user has. To make this work, we apply the expected shelf filters for - * decoding UHJ2 to quad (only needs LF scaling), and act as if those 4 - * quad channels are encoded right back into B-Format. - * - * This isn't perfect, but without an entirely separate and limited UHJ2 - * path, it's better than nothing. - * - * Do not apply the shelf filter with UHJ output. UHJ2->B-Format->UHJ2 is - * identity, so don't mess with it. + /* Don't need to set the VoiceIsAmbisonic flag if the device is not higher + * order than the voice. No HF scaling is necessary to mix it. */ - if(mFmtChannels == FmtUHJ2 && !device->mUhjEncoder) + if(mAmbiOrder && device->mAmbiOrder > mAmbiOrder) { + const uint8_t *OrderFromChan{Is2DAmbisonic(mFmtChannels) ? + AmbiIndex::OrderFrom2DChannel().data() : AmbiIndex::OrderFromChannel().data()}; + const auto scales = AmbiScale::GetHFOrderScales(mAmbiOrder, !Is2DAmbisonic(mFmtChannels)); + + const BandSplitter splitter{device->mXOverFreq / static_cast<float>(device->Frequency)}; + for(auto &chandata : mChans) + { + chandata.mAmbiHFScale = scales[*(OrderFromChan++)]; + chandata.mAmbiLFScale = 1.0f; + chandata.mAmbiSplitter = splitter; + chandata.mDryParams = DirectParams{}; + chandata.mDryParams.NFCtrlFilter = device->mNFCtrlFilter; + std::fill_n(chandata.mWetParams.begin(), device->NumAuxSends, SendParams{}); + } + /* 2-channel UHJ needs different shelf filters. However, we can't just + * use different shelf filters after mixing it and with any old speaker + * setup the user has. To make this work, we apply the expected shelf + * filters for decoding UHJ2 to quad (only needs LF scaling), and act + * as if those 4 quad channels are encoded right back onto higher-order + * B-Format. + * + * This isn't perfect, but without an entirely separate and limited + * UHJ2 path, it's better than nothing. + */ + if(mFmtChannels == FmtUHJ2) + { + mChans[0].mAmbiHFScale = 1.0f; + mChans[0].mAmbiLFScale = UhjDecoder<UhjLengthStd>::sWLFScale; + mChans[1].mAmbiHFScale = 1.0f; + mChans[1].mAmbiLFScale = UhjDecoder<UhjLengthStd>::sXYLFScale; + mChans[2].mAmbiHFScale = 1.0f; + mChans[2].mAmbiLFScale = UhjDecoder<UhjLengthStd>::sXYLFScale; + } + mFlags.set(VoiceIsAmbisonic); + } + else if(mFmtChannels == FmtUHJ2 && !device->mUhjEncoder) + { + /* 2-channel UHJ with first-order output also needs the shelf filter + * correction applied, except with UHJ output (UHJ2->B-Format->UHJ2 is + * identity, so don't mess with it). + */ const BandSplitter splitter{device->mXOverFreq / static_cast<float>(device->Frequency)}; for(auto &chandata : mChans) { |