OpenShot Audio Library | OpenShotAudio  0.6.0
juce_Interpolators.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  The code included in this file is provided under the terms of the ISC license
11  http://www.isc.org/downloads/software-support-policy/isc-license. Permission
12  To use, copy, modify, and/or distribute this software for any purpose with or
13  without fee is hereby granted provided that the above copyright notice and
14  this permission notice appear in all copies.
15 
16  JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
17  EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
18  DISCLAIMED.
19 
20  ==============================================================================
21 */
22 
23 namespace juce
24 {
25 
26 #if JUCE_UNIT_TESTS
27 
28 class InterpolatorTests final : public UnitTest
29 {
30 public:
31  InterpolatorTests()
32  : UnitTest ("InterpolatorTests", UnitTestCategories::audio)
33  {
34  }
35 
36 private:
37  template <typename InterpolatorType>
38  void runInterplatorTests (const String& interpolatorName)
39  {
40  auto createGaussian = [] (std::vector<float>& destination, float scale, float centreInSamples, float width)
41  {
42  for (size_t i = 0; i < destination.size(); ++i)
43  {
44  auto x = (((float) i) - centreInSamples) * width;
45  destination[i] = std::exp (-(x * x));
46  }
47 
48  FloatVectorOperations::multiply (destination.data(), scale, (int) destination.size());
49  };
50 
51  auto findGaussianPeak = [] (const std::vector<float>& input) -> float
52  {
53  auto max = std::max_element (std::begin (input), std::end (input));
54  auto maxPrev = max - 1;
55  jassert (maxPrev >= std::begin (input));
56  auto maxNext = max + 1;
57  jassert (maxNext < std::end (input));
58  auto quadraticMaxLoc = (*maxPrev - *maxNext) / (2.0f * ((*maxNext + *maxPrev) - (2.0f * *max)));
59  return quadraticMaxLoc + (float) std::distance (std::begin (input), max);
60  };
61 
62  auto expectAllElementsWithin = [this] (const std::vector<float>& v1, const std::vector<float>& v2, float tolerance)
63  {
64  expectEquals ((int) v1.size(), (int) v2.size());
65 
66  for (size_t i = 0; i < v1.size(); ++i)
67  expectWithinAbsoluteError (v1[i], v2[i], tolerance);
68  };
69 
70  InterpolatorType interpolator;
71 
72  constexpr size_t inputSize = 1001;
73  static_assert (inputSize > 800 + InterpolatorType::getBaseLatency(),
74  "The test InterpolatorTests input buffer is too small");
75 
76  std::vector<float> input (inputSize);
77  constexpr auto inputGaussianMidpoint = (float) (inputSize - 1) / 2.0f;
78  constexpr auto inputGaussianValueAtEnds = 0.000001f;
79  const auto inputGaussianWidth = std::sqrt (-std::log (inputGaussianValueAtEnds)) / inputGaussianMidpoint;
80 
81  createGaussian (input, 1.0f, inputGaussianMidpoint, inputGaussianWidth);
82 
83  for (auto speedRatio : { 0.4, 0.8263, 1.0, 1.05, 1.2384, 1.6 })
84  {
85  const auto expectedGaussianMidpoint = (inputGaussianMidpoint + InterpolatorType::getBaseLatency()) / (float) speedRatio;
86  const auto expectedGaussianWidth = inputGaussianWidth * (float) speedRatio;
87 
88  const auto outputBufferSize = (size_t) std::floor ((float) input.size() / speedRatio);
89 
90  for (int numBlocks : { 1, 5 })
91  {
92  const auto inputBlockSize = (float) input.size() / (float) numBlocks;
93  const auto outputBlockSize = (int) std::floor (inputBlockSize / speedRatio);
94 
95  std::vector<float> output (outputBufferSize, std::numeric_limits<float>::min());
96 
97  beginTest (interpolatorName + " process " + String (numBlocks) + " blocks ratio " + String (speedRatio));
98 
99  interpolator.reset();
100 
101  {
102  auto* inputPtr = input.data();
103  auto* outputPtr = output.data();
104 
105  for (int i = 0; i < numBlocks; ++i)
106  {
107  auto numInputSamplesRead = interpolator.process (speedRatio, inputPtr, outputPtr, outputBlockSize);
108  inputPtr += numInputSamplesRead;
109  outputPtr += outputBlockSize;
110  }
111  }
112 
113  expectWithinAbsoluteError (findGaussianPeak (output), expectedGaussianMidpoint, 0.1f);
114 
115  std::vector<float> expectedOutput (output.size());
116  createGaussian (expectedOutput, 1.0f, expectedGaussianMidpoint, expectedGaussianWidth);
117 
118  expectAllElementsWithin (output, expectedOutput, 0.02f);
119 
120  beginTest (interpolatorName + " process adding " + String (numBlocks) + " blocks ratio " + String (speedRatio));
121 
122  interpolator.reset();
123 
124  constexpr float addingGain = 0.7384f;
125 
126  {
127  auto* inputPtr = input.data();
128  auto* outputPtr = output.data();
129 
130  for (int i = 0; i < numBlocks; ++i)
131  {
132  auto numInputSamplesRead = interpolator.processAdding (speedRatio, inputPtr, outputPtr, outputBlockSize, addingGain);
133  inputPtr += numInputSamplesRead;
134  outputPtr += outputBlockSize;
135  }
136  }
137 
138  expectWithinAbsoluteError (findGaussianPeak (output), expectedGaussianMidpoint, 0.1f);
139 
140  std::vector<float> additionalOutput (output.size());
141  createGaussian (additionalOutput, addingGain, expectedGaussianMidpoint, expectedGaussianWidth);
142  FloatVectorOperations::add (expectedOutput.data(), additionalOutput.data(), (int) additionalOutput.size());
143 
144  expectAllElementsWithin (output, expectedOutput, 0.02f);
145  }
146 
147  beginTest (interpolatorName + " process wrap 0 ratio " + String (speedRatio));
148 
149  std::vector<float> doubleLengthOutput (2 * outputBufferSize, std::numeric_limits<float>::min());
150 
151  interpolator.reset();
152  interpolator.process (speedRatio, input.data(), doubleLengthOutput.data(), (int) doubleLengthOutput.size(),
153  (int) input.size(), 0);
154 
155  std::vector<float> expectedDoubleLengthOutput (doubleLengthOutput.size());
156  createGaussian (expectedDoubleLengthOutput, 1.0f, expectedGaussianMidpoint, expectedGaussianWidth);
157 
158  expectAllElementsWithin (doubleLengthOutput, expectedDoubleLengthOutput, 0.02f);
159 
160  beginTest (interpolatorName + " process wrap double ratio " + String (speedRatio));
161 
162  interpolator.reset();
163  interpolator.process (speedRatio, input.data(), doubleLengthOutput.data(), (int) doubleLengthOutput.size(),
164  (int) input.size(), (int) input.size());
165 
166  std::vector<float> secondGaussian (doubleLengthOutput.size());
167  createGaussian (secondGaussian, 1.0f, expectedGaussianMidpoint + (float) outputBufferSize, expectedGaussianWidth);
168  FloatVectorOperations::add (expectedDoubleLengthOutput.data(), secondGaussian.data(), (int) expectedDoubleLengthOutput.size());
169 
170  expectAllElementsWithin (doubleLengthOutput, expectedDoubleLengthOutput, 0.02f);
171  }
172  }
173 
174 public:
175  void runTest() override
176  {
177  runInterplatorTests<WindowedSincInterpolator> ("WindowedSincInterpolator");
178  runInterplatorTests<LagrangeInterpolator> ("LagrangeInterpolator");
179  runInterplatorTests<CatmullRomInterpolator> ("CatmullRomInterpolator");
180  runInterplatorTests<LinearInterpolator> ("LinearInterpolator");
181  }
182 };
183 
184 static InterpolatorTests interpolatorTests;
185 
186 #endif
187 
188 } // namespace juce