OpenShot Audio Library | OpenShotAudio 0.4.0
juce_FIRFilter_test.cpp
1/*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
6
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
9
10 By using JUCE, you agree to the terms of both the JUCE 7 End-User License
11 Agreement and JUCE Privacy Policy.
12
13 End User License Agreement: www.juce.com/juce-7-licence
14 Privacy Policy: www.juce.com/juce-privacy-policy
15
16 Or: You may also use this code under the terms of the GPL v3 (see
17 www.gnu.org/licenses).
18
19 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
21 DISCLAIMED.
22
23 ==============================================================================
24*/
25
26namespace juce::dsp
27{
28
29class FIRFilterTest final : public UnitTest
30{
31 template <typename Type>
32 struct Helpers
33 {
34 static void fillRandom (Random& random, Type* buffer, size_t n)
35 {
36 for (size_t i = 0; i < n; ++i)
37 buffer[i] = (2.0f * random.nextFloat()) - 1.0f;
38 }
39
40 static bool checkArrayIsSimilar (Type* a, Type* b, size_t n) noexcept
41 {
42 for (size_t i = 0; i < n; ++i)
43 if (std::abs (a[i] - b[i]) > 1e-6f)
44 return false;
45
46 return true;
47 }
48 };
49
50 #if JUCE_USE_SIMD
51 template <typename Type>
52 struct Helpers<SIMDRegister<Type>>
53 {
54 static void fillRandom (Random& random, SIMDRegister<Type>* buffer, size_t n)
55 {
56 Helpers<Type>::fillRandom (random, reinterpret_cast<Type*> (buffer), n * SIMDRegister<Type>::size());
57 }
58
59 static bool checkArrayIsSimilar (SIMDRegister<Type>* a, SIMDRegister<Type>* b, size_t n) noexcept
60 {
61 return Helpers<Type>::checkArrayIsSimilar (reinterpret_cast<Type*> (a),
62 reinterpret_cast<Type*> (b),
64 }
65 };
66 #endif
67
68 template <typename Type>
69 static void fillRandom (Random& random, Type* buffer, size_t n) { Helpers<Type>::fillRandom (random, buffer, n); }
70
71 template <typename Type>
72 static bool checkArrayIsSimilar (Type* a, Type* b, size_t n) noexcept { return Helpers<Type>::checkArrayIsSimilar (a, b, n); }
73
74 //==============================================================================
75 // reference implementation of an FIR
76 template <typename SampleType, typename NumericType>
77 static void reference (const NumericType* firCoefficients, size_t numCoefficients,
78 const SampleType* input, SampleType* output, size_t n) noexcept
79 {
80 if (numCoefficients == 0)
81 {
82 zeromem (output, sizeof (SampleType) * n);
83 return;
84 }
85
86 HeapBlock<SampleType> scratchBuffer (numCoefficients
87 #if JUCE_USE_SIMD
88 + (SIMDRegister<NumericType>::SIMDRegisterSize / sizeof (SampleType))
89 #endif
90 );
91 #if JUCE_USE_SIMD
92 SampleType* buffer = reinterpret_cast<SampleType*> (SIMDRegister<NumericType>::getNextSIMDAlignedPtr (reinterpret_cast<NumericType*> (scratchBuffer.getData())));
93 #else
94 SampleType* buffer = scratchBuffer.getData();
95 #endif
96
97 zeromem (buffer, sizeof (SampleType) * numCoefficients);
98
99 for (size_t i = 0; i < n; ++i)
100 {
101 for (size_t j = (numCoefficients - 1); j >= 1; --j)
102 buffer[j] = buffer[j-1];
103
104 buffer[0] = input[i];
105
106 SampleType sum (0);
107
108 for (size_t j = 0; j < numCoefficients; ++j)
109 sum += buffer[j] * firCoefficients[j];
110
111 output[i] = sum;
112 }
113 }
114
115 //==============================================================================
116 struct LargeBlockTest
117 {
118 template <typename FloatType>
119 static void run (FIR::Filter<FloatType>& filter, FloatType* src, FloatType* dst, size_t n)
120 {
121 AudioBlock<const FloatType> input (&src, 1, n);
122 AudioBlock<FloatType> output (&dst, 1, n);
123 ProcessContextNonReplacing<FloatType> context (input, output);
124
125 filter.process (context);
126 }
127 };
128
129 struct SampleBySampleTest
130 {
131 template <typename FloatType>
132 static void run (FIR::Filter<FloatType>& filter, FloatType* src, FloatType* dst, size_t n)
133 {
134 for (size_t i = 0; i < n; ++i)
135 dst[i] = filter.processSample (src[i]);
136 }
137 };
138
139 struct SplitBlockTest
140 {
141 template <typename FloatType>
142 static void run (FIR::Filter<FloatType>& filter, FloatType* input, FloatType* output, size_t n)
143 {
144 size_t len = 0;
145 for (size_t i = 0; i < n; i += len)
146 {
147 len = jmin (n - i, n / 3);
148 auto* src = input + i;
149 auto* dst = output + i;
150
151 AudioBlock<const FloatType> inBlock (&src, 1, len);
152 AudioBlock<FloatType> outBlock (&dst, 1, len);
153 ProcessContextNonReplacing<FloatType> context (inBlock, outBlock);
154
155 filter.process (context);
156 }
157 }
158 };
159
160 //==============================================================================
161 template <typename TheTest, typename SampleType, typename NumericType>
162 void runTestForType()
163 {
164 Random random (8392829);
165
166 for (auto size : {1, 2, 4, 8, 12, 13, 25})
167 {
168 constexpr size_t n = 813;
169
170 HeapBlock<char> inputBuffer, outputBuffer, refBuffer;
171 AudioBlock<SampleType> input (inputBuffer, 1, n), output (outputBuffer, 1, n), ref (refBuffer, 1, n);
172 fillRandom (random, input.getChannelPointer (0), n);
173
174 HeapBlock<char> firBlock;
175 AudioBlock<NumericType> fir (firBlock, 1, static_cast<size_t> (size));
176 fillRandom (random, fir.getChannelPointer (0), static_cast<size_t> (size));
177
178 FIR::Filter<SampleType> filter (*new FIR::Coefficients<NumericType> (fir.getChannelPointer (0), static_cast<size_t> (size)));
179 ProcessSpec spec {0.0, n, 1};
180 filter.prepare (spec);
181
182 reference<SampleType, NumericType> (fir.getChannelPointer (0), static_cast<size_t> (size),
183 input.getChannelPointer (0), ref.getChannelPointer (0), n);
184
185 TheTest::template run<SampleType> (filter, input.getChannelPointer (0), output.getChannelPointer (0), n);
186 expect (checkArrayIsSimilar (output.getChannelPointer (0), ref.getChannelPointer (0), n));
187 }
188 }
189
190 template <typename TheTest>
191 void runTestForAllTypes (const char* unitTestName)
192 {
193 beginTest (unitTestName);
194
195 runTestForType<TheTest, float, float>();
196 runTestForType<TheTest, double, double>();
197 #if JUCE_USE_SIMD
198 runTestForType<TheTest, SIMDRegister<float>, float>();
199 runTestForType<TheTest, SIMDRegister<double>, double>();
200 #endif
201 }
202
203
204public:
205 FIRFilterTest()
206 : UnitTest ("FIR Filter", UnitTestCategories::dsp)
207 {}
208
209 void runTest() override
210 {
211 runTestForAllTypes<LargeBlockTest> ("Large Blocks");
212 runTestForAllTypes<SampleBySampleTest> ("Sample by Sample");
213 runTestForAllTypes<SplitBlockTest> ("Split Block");
214 }
215};
216
217static FIRFilterTest firFilterUnitTest;
218
219} // namespace juce::dsp
UnitTest(const String &name, const String &category=String())
void beginTest(const String &testName)
void expect(bool testResult, const String &failureMessage=String())
static ElementType * getNextSIMDAlignedPtr(ElementType *ptr) noexcept
static constexpr size_t SIMDRegisterSize
static constexpr size_t size() noexcept