1 #ifndef CORE_UHJFILTER_H
2 #define CORE_UHJFILTER_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 {
23 inline UhjQualityType UhjDecodeQuality
{UhjQualityType::Default
};
24 inline UhjQualityType UhjEncodeQuality
{UhjQualityType::Default
};
27 struct UhjAllPassFilter
{
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;
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
; }
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 */