• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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     ASSERT_LE(numRead, kNumOutputSamples);
103     // Some frames are lost priming the FIR filter.
104     const int kMaxAlgorithmicFrameLoss = 16;
105     EXPECT_GT(numRead, kNumOutputSamples - kMaxAlgorithmicFrameLoss);
106 
107     int sinkZeroCrossingCount = countZeroCrossingsWithHysteresis(outputBuffer.get(), numRead);
108     // Some cycles may get chopped off at the end.
109     const int kMaxZeroCrossingDelta = 3;
110     EXPECT_LE(abs(sourceZeroCrossingCount - sinkZeroCrossingCount), kMaxZeroCrossingDelta);
111 
112     // Detect glitches by looking for spikes in the second derivative.
113     output = outputBuffer.get();
114     float previousValue = output[0];
115     float previousSlope = output[1] - output[0];
116     for (int i = 0; i < numRead; i++) {
117         float slope = output[i] - previousValue;
118         float slopeDelta = fabs(slope - previousSlope);
119         // Skip a few samples because there are often some steep slope changes at the beginning.
120         if (i > 10) {
121             EXPECT_LT(slopeDelta, 0.1);
122         }
123         previousValue = output[i];
124         previousSlope = slope;
125     }
126 
127 #if 0
128     // Save to disk for inspection.
129     FILE *fp = fopen( "/sdcard/Download/src_float_out.raw" , "wb" );
130     fwrite(outputBuffer.get(), sizeof(float), numRead, fp );
131     fclose(fp);
132 #endif
133 }
134 
135 
TEST(test_resampler,resampler_scan_all)136 TEST(test_resampler, resampler_scan_all) {
137     // TODO Add 64000, 88200, 96000 when they work. Failing now.
138     const int rates[] = {8000, 11025, 22050, 32000, 44100, 48000};
139     const MultiChannelResampler::Quality qualities[] =
140     {
141         MultiChannelResampler::Quality::Fastest,
142         MultiChannelResampler::Quality::Low,
143         MultiChannelResampler::Quality::Medium,
144         MultiChannelResampler::Quality::High,
145         MultiChannelResampler::Quality::Best
146     };
147     for (int srcRate : rates) {
148         for (int destRate : rates) {
149             for (auto quality : qualities) {
150                 if (srcRate != destRate) {
151                     checkResampler(srcRate, destRate, quality);
152                 }
153             }
154         }
155     }
156 }
157 
TEST(test_resampler,resampler_8000_11025_best)158 TEST(test_resampler, resampler_8000_11025_best) {
159     checkResampler(8000, 11025, MultiChannelResampler::Quality::Best);
160 }
TEST(test_resampler,resampler_8000_48000_best)161 TEST(test_resampler, resampler_8000_48000_best) {
162     checkResampler(8000, 48000, MultiChannelResampler::Quality::Best);
163 }
164 
TEST(test_resampler,resampler_8000_44100_best)165 TEST(test_resampler, resampler_8000_44100_best) {
166     checkResampler(8000, 44100, MultiChannelResampler::Quality::Best);
167 }
168 
TEST(test_resampler,resampler_11025_24000_best)169 TEST(test_resampler, resampler_11025_24000_best) {
170     checkResampler(11025, 24000, MultiChannelResampler::Quality::Best);
171 }
172 
TEST(test_resampler,resampler_11025_48000_fastest)173 TEST(test_resampler, resampler_11025_48000_fastest) {
174     checkResampler(11025, 48000, MultiChannelResampler::Quality::Fastest);
175 }
TEST(test_resampler,resampler_11025_48000_low)176 TEST(test_resampler, resampler_11025_48000_low) {
177     checkResampler(11025, 48000, MultiChannelResampler::Quality::Low);
178 }
TEST(test_resampler,resampler_11025_48000_medium)179 TEST(test_resampler, resampler_11025_48000_medium) {
180     checkResampler(11025, 48000, MultiChannelResampler::Quality::Medium);
181 }
TEST(test_resampler,resampler_11025_48000_high)182 TEST(test_resampler, resampler_11025_48000_high) {
183     checkResampler(11025, 48000, MultiChannelResampler::Quality::High);
184 }
185 
TEST(test_resampler,resampler_11025_48000_best)186 TEST(test_resampler, resampler_11025_48000_best) {
187     checkResampler(11025, 48000, MultiChannelResampler::Quality::Best);
188 }
189 
TEST(test_resampler,resampler_11025_44100_best)190 TEST(test_resampler, resampler_11025_44100_best) {
191     checkResampler(11025, 44100, MultiChannelResampler::Quality::Best);
192 }
193 
194 // TODO This fails because the output is very low.
195 //TEST(test_resampler, resampler_11025_88200_best) {
196 //    checkResampler(11025, 88200, MultiChannelResampler::Quality::Best);
197 //}
198 
TEST(test_resampler,resampler_16000_48000_best)199 TEST(test_resampler, resampler_16000_48000_best) {
200     checkResampler(16000, 48000, MultiChannelResampler::Quality::Best);
201 }
202 
TEST(test_resampler,resampler_44100_48000_low)203 TEST(test_resampler, resampler_44100_48000_low) {
204     checkResampler(44100, 48000, MultiChannelResampler::Quality::Low);
205 }
TEST(test_resampler,resampler_44100_48000_best)206 TEST(test_resampler, resampler_44100_48000_best) {
207     checkResampler(44100, 48000, MultiChannelResampler::Quality::Best);
208 }
209 
210 // Look for glitches when downsampling.
TEST(test_resampler,resampler_48000_11025_best)211 TEST(test_resampler, resampler_48000_11025_best) {
212     checkResampler(48000, 11025, MultiChannelResampler::Quality::Best);
213 }
TEST(test_resampler,resampler_48000_44100_best)214 TEST(test_resampler, resampler_48000_44100_best) {
215     checkResampler(48000, 44100, MultiChannelResampler::Quality::Best);
216 }
TEST(test_resampler,resampler_44100_11025_best)217 TEST(test_resampler, resampler_44100_11025_best) {
218     checkResampler(44100, 11025, MultiChannelResampler::Quality::Best);
219 }
220