diff options
author | Chris Robinson <[email protected]> | 2020-05-08 15:38:14 -0700 |
---|---|---|
committer | Chris Robinson <[email protected]> | 2020-05-08 15:38:14 -0700 |
commit | 1c320c353259c766b39292ed4b2a709e4469762c (patch) | |
tree | 055101254912fc6eb1c3c04a8ffefe44125db3c6 /alc/uhjfilter.h | |
parent | 12bb5a47cda0ef6ec1ced73ccf5d267a71f9e710 (diff) |
Use a FIR filter for the UHJ all-pass
Diffstat (limited to 'alc/uhjfilter.h')
-rw-r--r-- | alc/uhjfilter.h | 49 |
1 files changed, 22 insertions, 27 deletions
diff --git a/alc/uhjfilter.h b/alc/uhjfilter.h index 0593cdb9..db8e55ec 100644 --- a/alc/uhjfilter.h +++ b/alc/uhjfilter.h @@ -7,10 +7,6 @@ #include "almalloc.h" -struct AllPassState { - std::array<float,2> z{{0.0f, 0.0f}}; -}; - /* Encoding 2-channel UHJ from B-Format is done as: * * S = 0.9396926*W + 0.1855740*X @@ -21,36 +17,35 @@ struct AllPassState { * * where j is a wide-band +90 degree phase shift. * - * The phase shift is done using a Hilbert transform, described here: - * https://web.archive.org/web/20060708031958/http://www.biochem.oulu.fi/~oniemita/dsp/hilbert/ - * It works using 2 sets of 4 chained filters. The first filter chain produces - * a phase shift of varying magnitude over a wide range of frequencies, while - * the second filter chain produces a phase shift 90 degrees ahead of the - * first over the same range. - * - * Combining these two stages requires the use of three filter chains. S- - * channel output uses a Filter1 chain on the W and X channel mix, while the D- - * channel output uses a Filter1 chain on the Y channel plus a Filter2 chain on - * the W and X channel mix. This results in the W and X input mix on the D- - * channel output having the required +90 degree phase shift relative to the - * other inputs. + * The phase shift is done using a FIR filter derived from an FFT'd impulse + * with the desired shift. */ struct Uhj2Encoder { - alignas(16) std::array<float,BUFFERSIZE> mTemp; - alignas(16) std::array<float,BUFFERSIZE+1> mMid; - alignas(16) std::array<float,BUFFERSIZE+1> mSide; + /* A particular property of the filter allows it to cover nearly twice its + * length, so the filter size is also the effective delay (despite being + * center-aligned). + */ + constexpr static size_t sFilterSize{128}; + + /* Delays for the unfiltered signal. */ + alignas(16) std::array<float,sFilterSize> mMidDelay; + alignas(16) std::array<float,sFilterSize> mSideDelay; + + /* History for the FIR filter. */ + alignas(16) std::array<float,sFilterSize*2 - 1> mSideHistory; + + alignas(16) std::array<float,BUFFERSIZE + sFilterSize*2> mTemp; - AllPassState mFilter1_Y[4]; - AllPassState mFilter2_WX[4]; - AllPassState mFilter1_WX[4]; - float mLastY{0.0f}, mLastWX{0.0f}; + alignas(16) std::array<float,BUFFERSIZE> mMid; + alignas(16) std::array<float,BUFFERSIZE> mSide; - /* Encodes a 2-channel UHJ (stereo-compatible) signal from a B-Format input + /** + * Encodes a 2-channel UHJ (stereo-compatible) signal from a B-Format input * signal. The input must use FuMa channel ordering and scaling. */ - void encode(FloatBufferLine &LeftOut, FloatBufferLine &RightOut, FloatBufferLine *InSamples, - const size_t SamplesToDo); + void encode(FloatBufferLine &LeftOut, FloatBufferLine &RightOut, + const FloatBufferLine *InSamples, const size_t SamplesToDo); DEF_NEWDEL(Uhj2Encoder) }; |