1 #ifndef CORE_UHJFILTER_H
2 #define CORE_UHJFILTER_H
8 #include "bufferline.h"
11 static constexpr size_t UhjLength256{256};
12 static constexpr size_t UhjLength512{512};
14 enum class UhjQualityType : uint8_t {
21 extern UhjQualityType UhjDecodeQuality;
22 extern UhjQualityType UhjEncodeQuality;
25 struct UhjAllPassFilter {
27 /* Last two delayed components for direct form II. */
30 std::array<AllPassState,4> mState;
32 void process(const al::span<const float,4> coeffs, const al::span<const float> src,
33 const bool update, float *RESTRICT dst);
37 struct UhjEncoderBase {
38 virtual ~UhjEncoderBase() = default;
40 virtual size_t getDelay() noexcept = 0;
43 * Encodes a 2-channel UHJ (stereo-compatible) signal from a B-Format input
44 * signal. The input must use FuMa channel ordering and UHJ scaling (FuMa
45 * with an additional +3dB boost).
47 virtual void encode(float *LeftOut, float *RightOut,
48 const al::span<const float*const,3> InSamples, const size_t SamplesToDo) = 0;
52 struct UhjEncoder final : public UhjEncoderBase {
53 static constexpr size_t sFilterDelay{N/2};
55 /* Delays and processing storage for the input signal. */
56 alignas(16) std::array<float,BufferLineSize+sFilterDelay> mW{};
57 alignas(16) std::array<float,BufferLineSize+sFilterDelay> mX{};
58 alignas(16) std::array<float,BufferLineSize+sFilterDelay> mY{};
60 alignas(16) std::array<float,BufferLineSize> mS{};
61 alignas(16) std::array<float,BufferLineSize> mD{};
63 /* History and temp storage for the FIR filter. New samples should be
64 * written to index sFilterDelay*2 - 1.
66 static constexpr size_t sWXInOffset{sFilterDelay*2 - 1};
67 alignas(16) std::array<float,BufferLineSize + sFilterDelay*2> mWX{};
69 alignas(16) std::array<std::array<float,sFilterDelay>,2> mDirectDelay{};
71 size_t getDelay() noexcept override { return sFilterDelay; }
74 * Encodes a 2-channel UHJ (stereo-compatible) signal from a B-Format input
75 * signal. The input must use FuMa channel ordering and UHJ scaling (FuMa
76 * with an additional +3dB boost).
78 void encode(float *LeftOut, float *RightOut, const al::span<const float*const,3> InSamples,
79 const size_t SamplesToDo) override;
81 DEF_NEWDEL(UhjEncoder)
84 struct UhjEncoderIIR final : public UhjEncoderBase {
85 static constexpr size_t sFilterDelay{1};
87 /* Processing storage for the input signal. */
88 alignas(16) std::array<float,BufferLineSize+1> mS{};
89 alignas(16) std::array<float,BufferLineSize+1> mD{};
90 alignas(16) std::array<float,BufferLineSize+sFilterDelay> mWX{};
91 alignas(16) std::array<float,BufferLineSize+sFilterDelay> mTemp{};
92 float mDelayWX{}, mDelayY{};
94 UhjAllPassFilter mFilter1WX;
95 UhjAllPassFilter mFilter2WX;
96 UhjAllPassFilter mFilter1Y;
98 std::array<UhjAllPassFilter,2> mFilter1Direct;
99 std::array<float,2> mDirectDelay{};
101 size_t getDelay() noexcept override { return sFilterDelay; }
104 * Encodes a 2-channel UHJ (stereo-compatible) signal from a B-Format input
105 * signal. The input must use FuMa channel ordering and UHJ scaling (FuMa
106 * with an additional +3dB boost).
108 void encode(float *LeftOut, float *RightOut, const al::span<const float*const,3> InSamples,
109 const size_t SamplesToDo) override;
111 DEF_NEWDEL(UhjEncoderIIR)
116 static constexpr size_t sMaxPadding{256};
118 /* For 2-channel UHJ, shelf filters should use these LF responses. */
119 static constexpr float sWLFScale{0.661f};
120 static constexpr float sXYLFScale{1.293f};
122 virtual ~DecoderBase() = default;
124 virtual void decode(const al::span<float*> samples, const size_t samplesToDo,
125 const bool updateState) = 0;
128 * The width factor for Super Stereo processing. Can be changed in between
129 * calls to decode, with valid values being between 0...0.7.
131 float mWidthControl{0.593f};
135 struct UhjDecoder final : public DecoderBase {
136 /* The number of extra sample frames needed for input. */
137 static constexpr size_t sInputPadding{N/2};
139 alignas(16) std::array<float,BufferLineSize+sInputPadding> mS{};
140 alignas(16) std::array<float,BufferLineSize+sInputPadding> mD{};
141 alignas(16) std::array<float,BufferLineSize+sInputPadding> mT{};
143 alignas(16) std::array<float,sInputPadding-1> mDTHistory{};
144 alignas(16) std::array<float,sInputPadding-1> mSHistory{};
146 alignas(16) std::array<float,BufferLineSize + sInputPadding*2> mTemp{};
149 * Decodes a 3- or 4-channel UHJ signal into a B-Format signal with FuMa
150 * channel ordering and UHJ scaling. For 3-channel, the 3rd channel may be
151 * attenuated by 'n', where 0 <= n <= 1. So to decode 2-channel UHJ, supply
152 * 3 channels with the 3rd channel silent (n=0). The B-Format signal
153 * reconstructed from 2-channel UHJ should not be run through a normal
154 * B-Format decoder, as it needs different shelf filters.
156 void decode(const al::span<float*> samples, const size_t samplesToDo,
157 const bool updateState) override;
159 DEF_NEWDEL(UhjDecoder)
162 struct UhjDecoderIIR final : public DecoderBase {
163 /* FIXME: These IIR decoder filters actually have a 1-sample delay on the
164 * non-filtered components, which is not reflected in the source latency
165 * value. sInputPadding is 0, however, because it doesn't need any extra
168 static constexpr size_t sInputPadding{0};
170 alignas(16) std::array<float,BufferLineSize> mS{};
171 alignas(16) std::array<float,BufferLineSize> mD{};
172 alignas(16) std::array<float,BufferLineSize+1> mTemp{};
173 float mDelayS{}, mDelayDT{}, mDelayQ{};
175 UhjAllPassFilter mFilter1S;
176 UhjAllPassFilter mFilter2DT;
177 UhjAllPassFilter mFilter1DT;
178 UhjAllPassFilter mFilter2S;
179 UhjAllPassFilter mFilter1Q;
181 void decode(const al::span<float*> samples, const size_t samplesToDo,
182 const bool updateState) override;
184 DEF_NEWDEL(UhjDecoderIIR)
188 struct UhjStereoDecoder final : public DecoderBase {
189 static constexpr size_t sInputPadding{N/2};
191 float mCurrentWidth{-1.0f};
193 alignas(16) std::array<float,BufferLineSize+sInputPadding> mS{};
194 alignas(16) std::array<float,BufferLineSize+sInputPadding> mD{};
196 alignas(16) std::array<float,sInputPadding-1> mDTHistory{};
197 alignas(16) std::array<float,sInputPadding-1> mSHistory{};
199 alignas(16) std::array<float,BufferLineSize + sInputPadding*2> mTemp{};
202 * Applies Super Stereo processing on a stereo signal to create a B-Format
203 * signal with FuMa channel ordering and UHJ scaling. The samples span
204 * should contain 3 channels, the first two being the left and right stereo
205 * channels, and the third left empty.
207 void decode(const al::span<float*> samples, const size_t samplesToDo,
208 const bool updateState) override;
210 DEF_NEWDEL(UhjStereoDecoder)
213 struct UhjStereoDecoderIIR final : public DecoderBase {
214 static constexpr size_t sInputPadding{0};
216 float mCurrentWidth{-1.0f};
218 alignas(16) std::array<float,BufferLineSize> mS{};
219 alignas(16) std::array<float,BufferLineSize> mD{};
220 alignas(16) std::array<float,BufferLineSize+1> mTemp{};
221 float mDelayS{}, mDelayD{};
223 UhjAllPassFilter mFilter1S;
224 UhjAllPassFilter mFilter2D;
225 UhjAllPassFilter mFilter1D;
226 UhjAllPassFilter mFilter2S;
228 void decode(const al::span<float*> samples, const size_t samplesToDo,
229 const bool updateState) override;
231 DEF_NEWDEL(UhjStereoDecoderIIR)
234 #endif /* CORE_UHJFILTER_H */