1 /*
2 * Copyright 2022 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 /*
18 * Test FlowGraph
19 */
20
21 #include "math.h"
22 #include "stdio.h"
23
24 #include <gtest/gtest.h>
25 #include <oboe/Oboe.h>
26
27 #include "flowgraph/resampler/MultiChannelResampler.h"
28
29 using namespace oboe::resampler;
30
31 // Measure zero crossings.
countZeroCrossingsWithHysteresis(float * input,int32_t numSamples)32 static int32_t countZeroCrossingsWithHysteresis(float *input, int32_t numSamples) {
33 const float kHysteresisLevel = 0.25f;
34 int zeroCrossingCount = 0;
35 int state = 0; // can be -1, 0, +1
36 for (int i = 0; i < numSamples; i++) {
37 if (input[i] >= kHysteresisLevel) {
38 if (state < 0) {
39 zeroCrossingCount++;
40 }
41 state = 1;
42 } else if (input[i] <= -kHysteresisLevel) {
43 if (state > 0) {
44 zeroCrossingCount++;
45 }
46 state = -1;
47 }
48 }
49 return zeroCrossingCount;
50 }
51
52 static constexpr int kChannelCount = 1;
53
54 /**
55 * Convert a sine wave and then look for glitches.
56 * Glitches have a high value in the second derivative.
57 */
checkResampler(int32_t sourceRate,int32_t sinkRate,MultiChannelResampler::Quality quality)58 static void checkResampler(int32_t sourceRate, int32_t sinkRate,
59 MultiChannelResampler::Quality quality) {
60 const int kNumOutputSamples = 10000;
61 const double framesPerCycle = 81.379; // target output period
62
63 int numInputSamples = kNumOutputSamples * sourceRate / sinkRate;
64
65 std::unique_ptr<float[]> inputBuffer = std::make_unique<float[]>(numInputSamples);
66 std::unique_ptr<float[]> outputBuffer = std::make_unique<float[]>(kNumOutputSamples);
67
68 // Generate a sine wave for input.
69 const double kPhaseIncrement = 2.0 * sinkRate / (framesPerCycle * sourceRate);
70 double phase = 0.0;
71 for (int i = 0; i < numInputSamples; i++) {
72 inputBuffer[i] = sin(phase * M_PI);
73 phase += kPhaseIncrement;
74 while (phase > 1.0) {
75 phase -= 2.0;
76 }
77 }
78 int sourceZeroCrossingCount = countZeroCrossingsWithHysteresis(inputBuffer.get(), numInputSamples);
79
80 // Use a MultiChannelResampler to convert from the sourceRate to the sinkRate.
81 std::unique_ptr<MultiChannelResampler> mcResampler;
82 mcResampler.reset(MultiChannelResampler::make(kChannelCount,
83 sourceRate,
84 sinkRate,
85 quality));
86 int inputFramesLeft = numInputSamples;
87 int numRead = 0;
88 float *input = inputBuffer.get(); // for iteration
89 float *output = outputBuffer.get();
90 while (inputFramesLeft > 0) {
91 if (mcResampler->isWriteNeeded()) {
92 mcResampler->writeNextFrame(input);
93 input++;
94 inputFramesLeft--;
95 } else {
96 mcResampler->readNextFrame(output);
97 output++;
98 numRead++;
99 }
100 }
101
102 // Flush out remaining frames from the flowgraph
103 while (!mcResampler->isWriteNeeded()) {
104 mcResampler->readNextFrame(output);
105 output++;
106 numRead++;
107 }
108
109 ASSERT_LE(numRead, kNumOutputSamples);
110 // Some frames are lost priming the FIR filter.
111 const int kMaxAlgorithmicFrameLoss = 5;
112 EXPECT_GT(numRead, kNumOutputSamples - kMaxAlgorithmicFrameLoss);
113
114 int sinkZeroCrossingCount = countZeroCrossingsWithHysteresis(outputBuffer.get(), numRead);
115 // The sine wave may be cut off partially. This may cause multiple crossing
116 // differences when upsampling.
117 const int kMaxZeroCrossingDelta = std::max(sinkRate / sourceRate / 2, 1);
118 EXPECT_LE(abs(sourceZeroCrossingCount - sinkZeroCrossingCount), kMaxZeroCrossingDelta);
119
120 // Detect glitches by looking for spikes in the second derivative.
121 output = outputBuffer.get();
122 float previousValue = output[0];
123 float previousSlope = output[1] - output[0];
124 for (int i = 0; i < numRead; i++) {
125 float slope = output[i] - previousValue;
126 float slopeDelta = fabs(slope - previousSlope);
127 // Skip a few samples because there are often some steep slope changes at the beginning.
128 if (i > 10) {
129 EXPECT_LT(slopeDelta, 0.1);
130 }
131 previousValue = output[i];
132 previousSlope = slope;
133 }
134
135 #if 0
136 // Save to disk for inspection.
137 FILE *fp = fopen( "/sdcard/Download/src_float_out.raw" , "wb" );
138 fwrite(outputBuffer.get(), sizeof(float), numRead, fp );
139 fclose(fp);
140 #endif
141 }
142
143
TEST(test_resampler,resampler_scan_all)144 TEST(test_resampler, resampler_scan_all) {
145 const int rates[] = {8000, 11025, 22050, 32000, 44100, 48000, 64000, 88200, 96000};
146 const MultiChannelResampler::Quality qualities[] =
147 {
148 MultiChannelResampler::Quality::Fastest,
149 MultiChannelResampler::Quality::Low,
150 MultiChannelResampler::Quality::Medium,
151 MultiChannelResampler::Quality::High,
152 MultiChannelResampler::Quality::Best
153 };
154 for (int srcRate : rates) {
155 for (int destRate : rates) {
156 for (auto quality : qualities) {
157 if (srcRate != destRate) {
158 checkResampler(srcRate, destRate, quality);
159 }
160 }
161 }
162 }
163 }
164
TEST(test_resampler,resampler_8000_11025_best)165 TEST(test_resampler, resampler_8000_11025_best) {
166 checkResampler(8000, 11025, MultiChannelResampler::Quality::Best);
167 }
TEST(test_resampler,resampler_8000_48000_best)168 TEST(test_resampler, resampler_8000_48000_best) {
169 checkResampler(8000, 48000, MultiChannelResampler::Quality::Best);
170 }
171
TEST(test_resampler,resampler_8000_44100_best)172 TEST(test_resampler, resampler_8000_44100_best) {
173 checkResampler(8000, 44100, MultiChannelResampler::Quality::Best);
174 }
175
TEST(test_resampler,resampler_11025_24000_best)176 TEST(test_resampler, resampler_11025_24000_best) {
177 checkResampler(11025, 24000, MultiChannelResampler::Quality::Best);
178 }
179
TEST(test_resampler,resampler_11025_48000_fastest)180 TEST(test_resampler, resampler_11025_48000_fastest) {
181 checkResampler(11025, 48000, MultiChannelResampler::Quality::Fastest);
182 }
TEST(test_resampler,resampler_11025_48000_low)183 TEST(test_resampler, resampler_11025_48000_low) {
184 checkResampler(11025, 48000, MultiChannelResampler::Quality::Low);
185 }
TEST(test_resampler,resampler_11025_48000_medium)186 TEST(test_resampler, resampler_11025_48000_medium) {
187 checkResampler(11025, 48000, MultiChannelResampler::Quality::Medium);
188 }
TEST(test_resampler,resampler_11025_48000_high)189 TEST(test_resampler, resampler_11025_48000_high) {
190 checkResampler(11025, 48000, MultiChannelResampler::Quality::High);
191 }
192
TEST(test_resampler,resampler_11025_48000_best)193 TEST(test_resampler, resampler_11025_48000_best) {
194 checkResampler(11025, 48000, MultiChannelResampler::Quality::Best);
195 }
196
TEST(test_resampler,resampler_11025_44100_best)197 TEST(test_resampler, resampler_11025_44100_best) {
198 checkResampler(11025, 44100, MultiChannelResampler::Quality::Best);
199 }
200
TEST(test_resampler,resampler_11025_88200_best)201 TEST(test_resampler, resampler_11025_88200_best) {
202 checkResampler(11025, 88200, MultiChannelResampler::Quality::Best);
203 }
204
TEST(test_resampler,resampler_16000_48000_best)205 TEST(test_resampler, resampler_16000_48000_best) {
206 checkResampler(16000, 48000, MultiChannelResampler::Quality::Best);
207 }
208
TEST(test_resampler,resampler_44100_48000_low)209 TEST(test_resampler, resampler_44100_48000_low) {
210 checkResampler(44100, 48000, MultiChannelResampler::Quality::Low);
211 }
TEST(test_resampler,resampler_44100_48000_best)212 TEST(test_resampler, resampler_44100_48000_best) {
213 checkResampler(44100, 48000, MultiChannelResampler::Quality::Best);
214 }
215
216 // Look for glitches when downsampling.
TEST(test_resampler,resampler_48000_11025_best)217 TEST(test_resampler, resampler_48000_11025_best) {
218 checkResampler(48000, 11025, MultiChannelResampler::Quality::Best);
219 }
TEST(test_resampler,resampler_48000_44100_best)220 TEST(test_resampler, resampler_48000_44100_best) {
221 checkResampler(48000, 44100, MultiChannelResampler::Quality::Best);
222 }
TEST(test_resampler,resampler_44100_11025_best)223 TEST(test_resampler, resampler_44100_11025_best) {
224 checkResampler(44100, 11025, MultiChannelResampler::Quality::Best);
225 }
226