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