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