Include fmt 11.0.2
[openal-soft.git] / core / uhjfilter.h
blob264a1e1aff73e1301340714fadc188ffac6e01cc
1 #ifndef CORE_UHJFILTER_H
2 #define CORE_UHJFILTER_H
4 #include <array>
5 #include <cstddef>
6 #include <cstdint>
8 #include "alspan.h"
9 #include "bufferline.h"
10 #include "opthelpers.h"
13 inline constexpr std::size_t UhjLength256{256};
14 inline constexpr std::size_t UhjLength512{512};
16 enum class UhjQualityType : std::uint8_t {
17 IIR = 0,
18 FIR256,
19 FIR512,
20 Default = IIR
23 inline UhjQualityType UhjDecodeQuality{UhjQualityType::Default};
24 inline UhjQualityType UhjEncodeQuality{UhjQualityType::Default};
27 struct UhjAllPassFilter {
28 struct AllPassState {
29 /* Last two delayed components for direct form II. */
30 std::array<float,2> z{};
32 std::array<AllPassState,4> mState;
34 void processOne(const al::span<const float,4> coeffs, float x);
35 void process(const al::span<const float,4> coeffs, const al::span<const float> src,
36 const bool update, const al::span<float> dst);
40 struct SIMDALIGN UhjEncoderBase {
41 UhjEncoderBase() = default;
42 UhjEncoderBase(const UhjEncoderBase&) = delete;
43 UhjEncoderBase(UhjEncoderBase&&) = delete;
44 virtual ~UhjEncoderBase() = default;
46 void operator=(const UhjEncoderBase&) = delete;
47 void operator=(UhjEncoderBase&&) = delete;
49 virtual std::size_t getDelay() noexcept = 0;
51 /**
52 * Encodes a 2-channel UHJ (stereo-compatible) signal from a B-Format input
53 * signal. The input must use FuMa channel ordering and UHJ scaling (FuMa
54 * with an additional +3dB boost).
56 virtual void encode(float *LeftOut, float *RightOut,
57 const al::span<const float*const,3> InSamples, const std::size_t SamplesToDo) = 0;
60 template<std::size_t N>
61 struct UhjEncoder final : public UhjEncoderBase {
62 static constexpr std::size_t sFftLength{256};
63 static constexpr std::size_t sSegmentSize{sFftLength/2};
64 static constexpr std::size_t sNumSegments{N/sSegmentSize};
65 static constexpr std::size_t sFilterDelay{N/2 + sSegmentSize};
67 /* Delays and processing storage for the input signal. */
68 alignas(16) std::array<float,BufferLineSize+sFilterDelay> mW{};
69 alignas(16) std::array<float,BufferLineSize+sFilterDelay> mX{};
70 alignas(16) std::array<float,BufferLineSize+sFilterDelay> mY{};
72 alignas(16) std::array<float,BufferLineSize> mS{};
73 alignas(16) std::array<float,BufferLineSize> mD{};
75 /* History and temp storage for the convolution filter. */
76 std::size_t mFifoPos{}, mCurrentSegment{};
77 alignas(16) std::array<float,sFftLength> mWXInOut{};
78 alignas(16) std::array<float,sFftLength> mFftBuffer{};
79 alignas(16) std::array<float,sFftLength> mWorkData{};
80 alignas(16) std::array<float,sFftLength*sNumSegments> mWXHistory{};
82 alignas(16) std::array<std::array<float,sFilterDelay>,2> mDirectDelay{};
84 std::size_t getDelay() noexcept override { return sFilterDelay; }
86 /**
87 * Encodes a 2-channel UHJ (stereo-compatible) signal from a B-Format input
88 * signal. The input must use FuMa channel ordering and UHJ scaling (FuMa
89 * with an additional +3dB boost).
91 void encode(float *LeftOut, float *RightOut, const al::span<const float*const,3> InSamples,
92 const std::size_t SamplesToDo) final;
95 struct UhjEncoderIIR final : public UhjEncoderBase {
96 static constexpr std::size_t sFilterDelay{1};
98 /* Processing storage for the input signal. */
99 alignas(16) std::array<float,BufferLineSize+1> mS{};
100 alignas(16) std::array<float,BufferLineSize+1> mD{};
101 alignas(16) std::array<float,BufferLineSize+sFilterDelay> mWX{};
102 alignas(16) std::array<float,BufferLineSize+sFilterDelay> mTemp{};
103 float mDelayWX{}, mDelayY{};
105 UhjAllPassFilter mFilter1WX;
106 UhjAllPassFilter mFilter2WX;
107 UhjAllPassFilter mFilter1Y;
109 std::array<UhjAllPassFilter,2> mFilter1Direct;
110 std::array<float,2> mDirectDelay{};
112 std::size_t getDelay() noexcept override { return sFilterDelay; }
115 * Encodes a 2-channel UHJ (stereo-compatible) signal from a B-Format input
116 * signal. The input must use FuMa channel ordering and UHJ scaling (FuMa
117 * with an additional +3dB boost).
119 void encode(float *LeftOut, float *RightOut, const al::span<const float*const,3> InSamples,
120 const std::size_t SamplesToDo) final;
124 struct SIMDALIGN DecoderBase {
125 static constexpr std::size_t sMaxPadding{256};
127 /* For 2-channel UHJ, shelf filters should use these LF responses. */
128 static constexpr float sWLFScale{0.661f};
129 static constexpr float sXYLFScale{1.293f};
131 DecoderBase() = default;
132 DecoderBase(const DecoderBase&) = delete;
133 DecoderBase(DecoderBase&&) = delete;
134 virtual ~DecoderBase() = default;
136 void operator=(const DecoderBase&) = delete;
137 void operator=(DecoderBase&&) = delete;
139 virtual void decode(const al::span<float*> samples, const std::size_t samplesToDo,
140 const bool updateState) = 0;
143 * The width factor for Super Stereo processing. Can be changed in between
144 * calls to decode, with valid values being between 0...0.7.
146 float mWidthControl{0.593f};
149 template<std::size_t N>
150 struct UhjDecoder final : public DecoderBase {
151 /* The number of extra sample frames needed for input. */
152 static constexpr std::size_t sInputPadding{N/2};
154 alignas(16) std::array<float,BufferLineSize+sInputPadding> mS{};
155 alignas(16) std::array<float,BufferLineSize+sInputPadding> mD{};
156 alignas(16) std::array<float,BufferLineSize+sInputPadding> mT{};
158 alignas(16) std::array<float,sInputPadding-1> mDTHistory{};
159 alignas(16) std::array<float,sInputPadding-1> mSHistory{};
161 alignas(16) std::array<float,BufferLineSize + sInputPadding*2> mTemp{};
164 * Decodes a 3- or 4-channel UHJ signal into a B-Format signal with FuMa
165 * channel ordering and UHJ scaling. For 3-channel, the 3rd channel may be
166 * attenuated by 'n', where 0 <= n <= 1. So to decode 2-channel UHJ, supply
167 * 3 channels with the 3rd channel silent (n=0). The B-Format signal
168 * reconstructed from 2-channel UHJ should not be run through a normal
169 * B-Format decoder, as it needs different shelf filters.
171 void decode(const al::span<float*> samples, const std::size_t samplesToDo,
172 const bool updateState) final;
175 struct UhjDecoderIIR final : public DecoderBase {
176 /* These IIR decoder filters normally have a 1-sample delay on the non-
177 * filtered components. However, the filtered components are made to skip
178 * the first output sample and take one future sample, which puts it ahead
179 * by one sample. The first filtered output sample is cut to align it with
180 * the first non-filtered sample, similar to the FIR filters.
182 static constexpr std::size_t sInputPadding{1};
184 bool mFirstRun{true};
185 alignas(16) std::array<float,BufferLineSize+sInputPadding> mS{};
186 alignas(16) std::array<float,BufferLineSize+sInputPadding> mD{};
187 alignas(16) std::array<float,BufferLineSize+sInputPadding> mTemp{};
189 UhjAllPassFilter mFilter1S;
190 UhjAllPassFilter mFilter2DT;
191 UhjAllPassFilter mFilter1DT;
192 UhjAllPassFilter mFilter2S;
193 UhjAllPassFilter mFilter1Q;
195 void decode(const al::span<float*> samples, const std::size_t samplesToDo,
196 const bool updateState) final;
199 template<std::size_t N>
200 struct UhjStereoDecoder final : public DecoderBase {
201 static constexpr std::size_t sInputPadding{N/2};
203 float mCurrentWidth{-1.0f};
205 alignas(16) std::array<float,BufferLineSize+sInputPadding> mS{};
206 alignas(16) std::array<float,BufferLineSize+sInputPadding> mD{};
208 alignas(16) std::array<float,sInputPadding-1> mDTHistory{};
209 alignas(16) std::array<float,sInputPadding-1> mSHistory{};
211 alignas(16) std::array<float,BufferLineSize + sInputPadding*2> mTemp{};
214 * Applies Super Stereo processing on a stereo signal to create a B-Format
215 * signal with FuMa channel ordering and UHJ scaling. The samples span
216 * should contain 3 channels, the first two being the left and right stereo
217 * channels, and the third left empty.
219 void decode(const al::span<float*> samples, const std::size_t samplesToDo,
220 const bool updateState) final;
223 struct UhjStereoDecoderIIR final : public DecoderBase {
224 static constexpr std::size_t sInputPadding{1};
226 bool mFirstRun{true};
227 float mCurrentWidth{-1.0f};
229 alignas(16) std::array<float,BufferLineSize+sInputPadding> mS{};
230 alignas(16) std::array<float,BufferLineSize+sInputPadding> mD{};
231 alignas(16) std::array<float,BufferLineSize> mTemp{};
233 UhjAllPassFilter mFilter1S;
234 UhjAllPassFilter mFilter2D;
235 UhjAllPassFilter mFilter1D;
236 UhjAllPassFilter mFilter2S;
238 void decode(const al::span<float*> samples, const std::size_t samplesToDo,
239 const bool updateState) final;
242 #endif /* CORE_UHJFILTER_H */