blob: 1e4f59cf1ce413b9a8b9320af13758a53c6edbee [file] [log] [blame]
Robert Wu9757d3c2022-12-14 21:35:20 +00001/*
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 * This file also tests a few different conversion techniques because
21 * sometimes that have caused compiler bugs.
22 */
23
24#include <iostream>
25
26#include <gtest/gtest.h>
27
28#include "flowgraph/resampler/MultiChannelResampler.h"
29
30using namespace RESAMPLER_OUTER_NAMESPACE::resampler;
31
32// Measure zero crossings.
33static int32_t countZeroCrossingsWithHysteresis(float *input, int32_t numSamples) {
34 const float kHysteresisLevel = 0.25f;
35 int zeroCrossingCount = 0;
36 int state = 0; // can be -1, 0, +1
37 for (int i = 0; i < numSamples; i++) {
38 if (input[i] >= kHysteresisLevel) {
39 if (state < 0) {
40 zeroCrossingCount++;
41 }
42 state = 1;
43 } else if (input[i] <= -kHysteresisLevel) {
44 if (state > 0) {
45 zeroCrossingCount++;
46 }
47 state = -1;
48 }
49 }
50 return zeroCrossingCount;
51}
52
53static constexpr int kChannelCount = 1;
54
55/**
56 * Convert a sine wave and then look for glitches.
57 * Glitches have a high value in the second derivative.
58 */
59static void checkResampler(int32_t sourceRate, int32_t sinkRate,
60 MultiChannelResampler::Quality quality) {
61 const int kNumOutputSamples = 10000;
62 const double framesPerCycle = 81.379; // target output period
63
64 int numInputSamples = kNumOutputSamples * sourceRate / sinkRate;
65
66 std::unique_ptr<float[]> inputBuffer = std::make_unique<float[]>(numInputSamples);
67 std::unique_ptr<float[]> outputBuffer = std::make_unique<float[]>(kNumOutputSamples);
68
69 // Generate a sine wave for input.
70 const double kPhaseIncrement = 2.0 * sinkRate / (framesPerCycle * sourceRate);
71 double phase = 0.0;
72 for (int i = 0; i < numInputSamples; i++) {
73 inputBuffer[i] = sin(phase * M_PI);
74 phase += kPhaseIncrement;
75 while (phase > 1.0) {
76 phase -= 2.0;
77 }
78 }
79 int sourceZeroCrossingCount = countZeroCrossingsWithHysteresis(
80 inputBuffer.get(), numInputSamples);
81
82 // Use a MultiChannelResampler to convert from the sourceRate to the sinkRate.
83 std::unique_ptr<MultiChannelResampler> mcResampler;
84 mcResampler.reset(MultiChannelResampler::make(kChannelCount,
85 sourceRate,
86 sinkRate,
87 quality));
88 int inputFramesLeft = numInputSamples;
89 int numRead = 0;
90 float *input = inputBuffer.get(); // for iteration
91 float *output = outputBuffer.get();
92 while (inputFramesLeft > 0) {
93 if (mcResampler->isWriteNeeded()) {
94 mcResampler->writeNextFrame(input);
95 input++;
96 inputFramesLeft--;
97 } else {
98 mcResampler->readNextFrame(output);
99 output++;
100 numRead++;
101 }
102 }
103
104 ASSERT_LE(numRead, kNumOutputSamples);
105 // Some frames are lost priming the FIR filter.
106 const int kMaxAlgorithmicFrameLoss = 16;
107 EXPECT_GT(numRead, kNumOutputSamples - kMaxAlgorithmicFrameLoss);
108
109 int sinkZeroCrossingCount = countZeroCrossingsWithHysteresis(outputBuffer.get(), numRead);
110 // Some cycles may get chopped off at the end.
111 const int kMaxZeroCrossingDelta = 3;
112 EXPECT_LE(abs(sourceZeroCrossingCount - sinkZeroCrossingCount), kMaxZeroCrossingDelta);
113
114 // Detect glitches by looking for spikes in the second derivative.
115 output = outputBuffer.get();
116 float previousValue = output[0];
117 float previousSlope = output[1] - output[0];
118 for (int i = 0; i < numRead; i++) {
119 float slope = output[i] - previousValue;
120 float slopeDelta = fabs(slope - previousSlope);
121 // Skip a few samples because there are often some steep slope changes at the beginning.
122 if (i > 10) {
123 EXPECT_LT(slopeDelta, 0.1);
124 }
125 previousValue = output[i];
126 previousSlope = slope;
127 }
128
129#if 0
130 // Save to disk for inspection.
131 FILE *fp = fopen( "/sdcard/Download/src_float_out.raw" , "wb" );
132 fwrite(outputBuffer.get(), sizeof(float), numRead, fp );
133 fclose(fp);
134#endif
135}
136
137
138TEST(test_resampler, resampler_scan_all) {
139 // TODO Add 64000, 88200, 96000 when they work. Failing now.
140 const int rates[] = {8000, 11025, 22050, 32000, 44100, 48000};
141 const MultiChannelResampler::Quality qualities[] =
142 {
143 MultiChannelResampler::Quality::Fastest,
144 MultiChannelResampler::Quality::Low,
145 MultiChannelResampler::Quality::Medium,
146 MultiChannelResampler::Quality::High,
147 MultiChannelResampler::Quality::Best
148 };
149 for (int srcRate : rates) {
150 for (int destRate : rates) {
151 for (auto quality : qualities) {
152 if (srcRate != destRate) {
153 checkResampler(srcRate, destRate, quality);
154 }
155 }
156 }
157 }
158}
159
160TEST(test_resampler, resampler_8000_11025_best) {
161 checkResampler(8000, 11025, MultiChannelResampler::Quality::Best);
162}
163TEST(test_resampler, resampler_8000_48000_best) {
164 checkResampler(8000, 48000, MultiChannelResampler::Quality::Best);
165}
166
167TEST(test_resampler, resampler_8000_44100_best) {
168 checkResampler(8000, 44100, MultiChannelResampler::Quality::Best);
169}
170
171TEST(test_resampler, resampler_11025_24000_best) {
172 checkResampler(11025, 24000, MultiChannelResampler::Quality::Best);
173}
174
175TEST(test_resampler, resampler_11025_48000_fastest) {
176 checkResampler(11025, 48000, MultiChannelResampler::Quality::Fastest);
177}
178TEST(test_resampler, resampler_11025_48000_low) {
179 checkResampler(11025, 48000, MultiChannelResampler::Quality::Low);
180}
181TEST(test_resampler, resampler_11025_48000_medium) {
182 checkResampler(11025, 48000, MultiChannelResampler::Quality::Medium);
183}
184TEST(test_resampler, resampler_11025_48000_high) {
185 checkResampler(11025, 48000, MultiChannelResampler::Quality::High);
186}
187
188TEST(test_resampler, resampler_11025_48000_best) {
189 checkResampler(11025, 48000, MultiChannelResampler::Quality::Best);
190}
191
192TEST(test_resampler, resampler_11025_44100_best) {
193 checkResampler(11025, 44100, MultiChannelResampler::Quality::Best);
194}
195
196// TODO This fails because the output is very low.
197//TEST(test_resampler, resampler_11025_88200_best) {
198// checkResampler(11025, 88200, MultiChannelResampler::Quality::Best);
199//}
200
201TEST(test_resampler, resampler_16000_48000_best) {
202 checkResampler(16000, 48000, MultiChannelResampler::Quality::Best);
203}
204
205TEST(test_resampler, resampler_44100_48000_low) {
206 checkResampler(44100, 48000, MultiChannelResampler::Quality::Low);
207}
208TEST(test_resampler, resampler_44100_48000_best) {
209 checkResampler(44100, 48000, MultiChannelResampler::Quality::Best);
210}
211
212// Look for glitches when downsampling.
213TEST(test_resampler, resampler_48000_11025_best) {
214 checkResampler(48000, 11025, MultiChannelResampler::Quality::Best);
215}
216TEST(test_resampler, resampler_48000_44100_best) {
217 checkResampler(48000, 44100, MultiChannelResampler::Quality::Best);
218}
219TEST(test_resampler, resampler_44100_11025_best) {
220 checkResampler(44100, 11025, MultiChannelResampler::Quality::Best);
221}