OpenShot Audio Library | OpenShotAudio  0.6.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 
26 namespace juce::dsp
27 {
28 
29 class 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 
204 public:
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 
217 static 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 constexpr size_t SIMDRegisterSize
static constexpr size_t size() noexcept
static ElementType * getNextSIMDAlignedPtr(ElementType *ptr) noexcept