OpenShot Audio Library | OpenShotAudio: juce_Interpolators.cpp Source File

OpenShot Audio Library | OpenShotAudio 0.4.0

Loading...
Searching...
No Matches
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
23namespace juce
24{
25
26#if JUCE_UNIT_TESTS
27
28class InterpolatorTests final : public UnitTest
29{
30public:
31 InterpolatorTests()
32 : UnitTest ("InterpolatorTests", UnitTestCategories::audio)
33 {
34 }
35
36private:
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
174public:
175 void runTest() override
176 {
177 runInterplatorTests<WindowedSincInterpolator> ("WindowedSincInterpolator");
178 runInterplatorTests<LagrangeInterpolator> ("LagrangeInterpolator");
179 runInterplatorTests<CatmullRomInterpolator> ("CatmullRomInterpolator");
180 runInterplatorTests<LinearInterpolator> ("LinearInterpolator");
181 }
182};
183
184static InterpolatorTests interpolatorTests;
185
186#endif
187
188} // namespace juce