xref: /aosp_15_r20/frameworks/av/media/libaaudio/tests/test_resampler.cpp (revision ec779b8e0859a360c3d303172224686826e6e0e1)
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  * 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 
30 using namespace RESAMPLER_OUTER_NAMESPACE::resampler;
31 
32 // Measure zero crossings.
countZeroCrossingsWithHysteresis(float * input,int32_t numSamples)33 static 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 
53 static 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  */
checkResampler(int32_t sourceRate,int32_t sinkRate,MultiChannelResampler::Quality quality)59 static 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     // Flush out remaining frames from the flowgraph
105     while (!mcResampler->isWriteNeeded()) {
106         mcResampler->readNextFrame(output);
107         output++;
108         numRead++;
109     }
110 
111     ASSERT_LE(numRead, kNumOutputSamples);
112     // Some frames are lost priming the FIR filter.
113     const int kMaxAlgorithmicFrameLoss = 5;
114     EXPECT_GT(numRead, kNumOutputSamples - kMaxAlgorithmicFrameLoss);
115 
116     int sinkZeroCrossingCount = countZeroCrossingsWithHysteresis(outputBuffer.get(), numRead);
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