diff options
author | Chris Robinson <[email protected]> | 2019-02-22 21:53:45 -0800 |
---|---|---|
committer | Chris Robinson <[email protected]> | 2019-02-22 21:53:45 -0800 |
commit | 5b35e60a980815bf7b4310afcc5004bd36bd92f6 (patch) | |
tree | a10a11bb23b3b3e9654c27e97b01e57eccd5e8de /Alc | |
parent | 6a3c10d850338af8d12b88dbe3d5bedf997d3c95 (diff) |
Apply ambisonic upsampling on reverb output as needed
This isn't the greatest thing since it splits the A-to-B-Format transform from
the panning transform. The A-to-B and HF scale mixes are also not as optimal as
they could be, since they can't use the main mixer functions (wrong buffer line
length).
It does, however, get rid of the final use of the FOAOut buffer, so the
upsampling post-process is no longer needed.
Diffstat (limited to 'Alc')
-rw-r--r-- | Alc/effects/reverb.cpp | 147 |
1 files changed, 110 insertions, 37 deletions
diff --git a/Alc/effects/reverb.cpp b/Alc/effects/reverb.cpp index 5b0fd080..4e230004 100644 --- a/Alc/effects/reverb.cpp +++ b/Alc/effects/reverb.cpp @@ -26,6 +26,7 @@ #include <cmath> #include <algorithm> +#include <functional> #include "alMain.h" #include "alcontext.h" @@ -33,6 +34,7 @@ #include "alAuxEffectSlot.h" #include "alListener.h" #include "alError.h" +#include "bformatdec.h" #include "filters/biquad.h" #include "vector.h" #include "vecmat.h" @@ -44,6 +46,8 @@ ALfloat ReverbBoost = 1.0f; namespace { +using namespace std::placeholders; + /* This is the maximum number of samples processed for each inner loop * iteration. */ @@ -86,6 +90,29 @@ constexpr alu::Matrix A2B{ 0.866025403785f, 0.866025403785f, -0.866025403785f, -0.866025403785f }; +inline void ConvertA2B(ALfloat (&dst)[NUM_LINES][MAX_UPDATE_SAMPLES], + const ALfloat (&src)[NUM_LINES][MAX_UPDATE_SAMPLES], const ALsizei todo) +{ + for(ALsizei c{0};c < NUM_LINES;c++) + { + /* Not ideal to do this mixing manually, but the main mixers work on + * BUFFERSIZE-length lines and these buffers are MAX_UPDATE_SAMPLES in + * length. At least the compiler seems to vectorize it, when targeting + * capable CPUs (just no run-time detection). + */ + std::fill(std::begin(dst[c]), std::end(dst[c]), 0.0f); + for(ALsizei d{0};d < NUM_LINES;d++) + { + ASSUME(todo > 0); + std::transform(std::begin(src[d]), std::begin(src[d])+todo, dst[c], dst[c], + std::bind(std::plus<float>{}, + std::bind(std::multiplies<float>{}, _1, A2B[c][d]), + _2) + ); + } + } +} + constexpr ALfloat FadeStep{1.0f / FADE_SAMPLES}; /* The all-pass and delay lines have a variable length dependent on the @@ -335,6 +362,56 @@ struct ReverbState final : public EffectState { alignas(16) ALfloat mTempSamples[NUM_LINES][MAX_UPDATE_SAMPLES]{}; alignas(16) ALfloat mMixBuffer[NUM_LINES][MAX_UPDATE_SAMPLES]{}; + using MixOutT = void (ReverbState::*)(std::array<BandSplitter,NUM_LINES> &splitter, + const ALsizei numOutput, ALfloat (*samplesOut)[BUFFERSIZE], + ALfloat (¤tGains)[NUM_LINES][MAX_AMBI_CHANNELS], + const ALfloat (&targetGains)[NUM_LINES][MAX_AMBI_CHANNELS], const ALsizei counter, + const ALsizei base, const ALsizei todo); + + MixOutT mMixOut{&ReverbState::MixOutPlain}; + std::array<ALfloat,MAX_AMBI_ORDER+1> mOrderScales{}; + std::array<std::array<BandSplitter,NUM_LINES>,2> mAmbiSplitter; + + + void MixOutPlain(std::array<BandSplitter,NUM_LINES>& /*splitter*/, const ALsizei numOutput, + ALfloat (*samplesOut)[BUFFERSIZE], ALfloat (¤tGains)[NUM_LINES][MAX_AMBI_CHANNELS], + const ALfloat (&targetGains)[NUM_LINES][MAX_AMBI_CHANNELS], const ALsizei counter, + const ALsizei base, const ALsizei todo) + { + /* Convert back to B-Format, and mix the results to output. */ + ConvertA2B(mTempSamples, mMixBuffer, todo); + for(ALsizei c{0};c < NUM_LINES;c++) + MixSamples(mTempSamples[c], numOutput, samplesOut, currentGains[c], + targetGains[c], counter, base, todo); + } + + void MixOutAmbiUp(std::array<BandSplitter,NUM_LINES> &splitter, const ALsizei numOutput, + ALfloat (*samplesOut)[BUFFERSIZE], ALfloat (¤tGains)[NUM_LINES][MAX_AMBI_CHANNELS], + const ALfloat (&targetGains)[NUM_LINES][MAX_AMBI_CHANNELS], const ALsizei counter, + const ALsizei base, const ALsizei todo) + { + ConvertA2B(mTempSamples, mMixBuffer, todo); + for(ALsizei c{0};c < NUM_LINES;c++) + { + /* Apply scaling to the B-Format's HF response to "upsample" it to + * higher-order output. + */ + const ALfloat hfscale{(c==0) ? mOrderScales[0] : mOrderScales[1]}; + ALfloat (&hfbuf)[MAX_UPDATE_SAMPLES] = mMixBuffer[0]; + ALfloat (&lfbuf)[MAX_UPDATE_SAMPLES] = mMixBuffer[1]; + + ASSUME(todo > 0); + splitter[c].process(hfbuf, lfbuf, mTempSamples[c], todo); + std::transform(hfbuf, hfbuf+todo, lfbuf, lfbuf, + std::bind(std::plus<float>{}, + std::bind(std::multiplies<float>{}, _1, hfscale), + _2) + ); + + MixSamples(lfbuf, numOutput, samplesOut, currentGains[c], + targetGains[c], counter, base, todo); + } + } ALboolean deviceUpdate(const ALCdevice *device) override; void update(const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props, const EffectTarget target) override; @@ -452,9 +529,9 @@ ALboolean AllocLines(const ALuint frequency, ReverbState *State) return AL_TRUE; } -ALboolean ReverbState::deviceUpdate(const ALCdevice *Device) +ALboolean ReverbState::deviceUpdate(const ALCdevice *device) { - const ALuint frequency{Device->Frequency}; + const ALuint frequency{device->Frequency}; /* Allocate the delay lines. */ if(!AllocLines(frequency, this)) @@ -505,6 +582,20 @@ ALboolean ReverbState::deviceUpdate(const ALCdevice *Device) std::fill(std::begin(mMaxUpdate), std::end(mMaxUpdate), MAX_UPDATE_SAMPLES); mOffset = 0; + if(device->mAmbiOrder > 1) + { + mMixOut = &ReverbState::MixOutAmbiUp; + mOrderScales = AmbiUpsampler::GetHFOrderScales(1, device->mAmbiOrder); + } + else + { + mMixOut = &ReverbState::MixOutPlain; + mOrderScales.fill(1.0f); + } + mAmbiSplitter[0][0].init(400.0f / static_cast<ALfloat>(frequency)); + std::fill(mAmbiSplitter[0].begin()+1, mAmbiSplitter[0].end(), mAmbiSplitter[0][0]); + std::fill(mAmbiSplitter[1].begin(), mAmbiSplitter[1].end(), mAmbiSplitter[0][0]); + return AL_TRUE; } @@ -765,35 +856,24 @@ alu::Matrix GetTransformFromVector(const ALfloat *vec) /* Update the early and late 3D panning gains. */ ALvoid Update3DPanning(const ALfloat *ReflectionsPan, const ALfloat *LateReverbPan, const ALfloat earlyGain, const ALfloat lateGain, const EffectTarget &target, ReverbState *State) { - /* Multiples two matrices, producing the given column's results. */ - auto MatrixMult = [](const alu::Matrix &m1, const alu::Matrix &m2, size_t col) noexcept -> std::array<ALfloat,MAX_AMBI_CHANNELS> - { - std::array<ALfloat,MAX_AMBI_CHANNELS> ret{}; - for(int row{0};row < 4;row++) - ret[row] = m1[row][0]*m2[0][col] + m1[row][1]*m2[1][col] + - m1[row][2]*m2[2][col] + m1[row][3]*m2[3][col]; - return ret; - }; - /* Create matrices that transform a B-Format signal according to the * panning vectors. */ const alu::Matrix earlymat{GetTransformFromVector(ReflectionsPan)}; const alu::Matrix latemat{GetTransformFromVector(LateReverbPan)}; - State->mOutBuffer = target.FOAOut->Buffer; - State->mOutChannels = target.FOAOut->NumChannels; + State->mOutBuffer = target.Main->Buffer; + State->mOutChannels = target.Main->NumChannels; for(size_t i{0u};i < NUM_LINES;i++) { - /* Combine the B-Format transform matrix with one that first converts - * A-Format to B-Format. - */ - auto coeffs = MatrixMult(earlymat, A2B, i); - ComputePanGains(target.FOAOut, coeffs.data(), earlyGain, State->mEarly.PanGain[i]); + const ALfloat coeffs[MAX_AMBI_CHANNELS]{earlymat[0][i], earlymat[1][i], earlymat[2][i], + earlymat[3][i]}; + ComputePanGains(target.Main, coeffs, earlyGain, State->mEarly.PanGain[i]); } for(size_t i{0u};i < NUM_LINES;i++) { - auto coeffs = MatrixMult(latemat, A2B, i); - ComputePanGains(target.FOAOut, coeffs.data(), lateGain, State->mLate.PanGain[i]); + const ALfloat coeffs[MAX_AMBI_CHANNELS]{latemat[0][i], latemat[1][i], latemat[2][i], + latemat[3][i]}; + ComputePanGains(target.Main, coeffs, lateGain, State->mLate.PanGain[i]); } } @@ -1343,20 +1423,15 @@ void ReverbState::process(ALsizei samplesToDo, const ALfloat (*RESTRICT samplesI { auto fade = static_cast<ALfloat>(fadeCount); - /* Generate early reflections. */ + /* Generate and mix early reflections. */ EarlyReflection_Faded(this, offset, todo, fade, mMixBuffer); - /* Mix the A-Format results to output, implicitly converting back - * to B-Format. - */ - for(ALsizei c{0};c < NUM_LINES;c++) - MixSamples(mMixBuffer[c], numOutput, samplesOut, mEarly.CurrentGain[c], - mEarly.PanGain[c], samplesToDo-base, base, todo); + (this->*mMixOut)(mAmbiSplitter[0], numOutput, samplesOut, mEarly.CurrentGain, + mEarly.PanGain, samplesToDo-base, base, todo); /* Generate and mix late reverb. */ LateReverb_Faded(this, offset, todo, fade, mMixBuffer); - for(ALsizei c{0};c < NUM_LINES;c++) - MixSamples(mMixBuffer[c], numOutput, samplesOut, mLate.CurrentGain[c], - mLate.PanGain[c], samplesToDo-base, base, todo); + (this->*mMixOut)(mAmbiSplitter[1], numOutput, samplesOut, mLate.CurrentGain, + mLate.PanGain, samplesToDo-base, base, todo); /* Step fading forward. */ fadeCount += todo; @@ -1384,15 +1459,13 @@ void ReverbState::process(ALsizei samplesToDo, const ALfloat (*RESTRICT samplesI { /* Generate and mix early reflections. */ EarlyReflection_Unfaded(this, offset, todo, mMixBuffer); - for(ALsizei c{0};c < NUM_LINES;c++) - MixSamples(mMixBuffer[c], numOutput, samplesOut, mEarly.CurrentGain[c], - mEarly.PanGain[c], samplesToDo-base, base, todo); + (this->*mMixOut)(mAmbiSplitter[0], numOutput, samplesOut, mEarly.CurrentGain, + mEarly.PanGain, samplesToDo-base, base, todo); /* Generate and mix late reverb. */ LateReverb_Unfaded(this, offset, todo, mMixBuffer); - for(ALsizei c{0};c < NUM_LINES;c++) - MixSamples(mMixBuffer[c], numOutput, samplesOut, mLate.CurrentGain[c], - mLate.PanGain[c], samplesToDo-base, base, todo); + (this->*mMixOut)(mAmbiSplitter[1], numOutput, samplesOut, mLate.CurrentGain, + mLate.PanGain, samplesToDo-base, base, todo); } /* Step all delays forward. */ |