• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 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 //#define LOG_NDEBUG 0
18 #define LOG_TAG "audioflinger_resampler_tests"
19 
20 #include <unistd.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <fcntl.h>
24 #include <string.h>
25 #include <sys/mman.h>
26 #include <sys/stat.h>
27 #include <errno.h>
28 #include <time.h>
29 #include <math.h>
30 #include <vector>
31 #include <utility>
32 #include <iostream>
33 #include <cutils/log.h>
34 #include <gtest/gtest.h>
35 #include <media/AudioBufferProvider.h>
36 #include "AudioResampler.h"
37 #include "test_utils.h"
38 
resample(int channels,void * output,size_t outputFrames,const std::vector<size_t> & outputIncr,android::AudioBufferProvider * provider,android::AudioResampler * resampler)39 void resample(int channels, void *output,
40         size_t outputFrames, const std::vector<size_t> &outputIncr,
41         android::AudioBufferProvider *provider, android::AudioResampler *resampler)
42 {
43     for (size_t i = 0, j = 0; i < outputFrames; ) {
44         size_t thisFrames = outputIncr[j++];
45         if (j >= outputIncr.size()) {
46             j = 0;
47         }
48         if (thisFrames == 0 || thisFrames > outputFrames - i) {
49             thisFrames = outputFrames - i;
50         }
51         resampler->resample((int32_t*) output + channels*i, thisFrames, provider);
52         i += thisFrames;
53     }
54 }
55 
buffercmp(const void * reference,const void * test,size_t outputFrameSize,size_t outputFrames)56 void buffercmp(const void *reference, const void *test,
57         size_t outputFrameSize, size_t outputFrames)
58 {
59     for (size_t i = 0; i < outputFrames; ++i) {
60         int check = memcmp((const char*)reference + i * outputFrameSize,
61                 (const char*)test + i * outputFrameSize, outputFrameSize);
62         if (check) {
63             ALOGE("Failure at frame %zu", i);
64             ASSERT_EQ(check, 0); /* fails */
65         }
66     }
67 }
68 
testBufferIncrement(size_t channels,bool useFloat,unsigned inputFreq,unsigned outputFreq,enum android::AudioResampler::src_quality quality)69 void testBufferIncrement(size_t channels, bool useFloat,
70         unsigned inputFreq, unsigned outputFreq,
71         enum android::AudioResampler::src_quality quality)
72 {
73     const audio_format_t format = useFloat ? AUDIO_FORMAT_PCM_FLOAT : AUDIO_FORMAT_PCM_16_BIT;
74     // create the provider
75     std::vector<int> inputIncr;
76     SignalProvider provider;
77     if (useFloat) {
78         provider.setChirp<float>(channels,
79                 0., outputFreq/2., outputFreq, outputFreq/2000.);
80     } else {
81         provider.setChirp<int16_t>(channels,
82                 0., outputFreq/2., outputFreq, outputFreq/2000.);
83     }
84     provider.setIncr(inputIncr);
85 
86     // calculate the output size
87     size_t outputFrames = ((int64_t) provider.getNumFrames() * outputFreq) / inputFreq;
88     size_t outputFrameSize = channels * (useFloat ? sizeof(float) : sizeof(int32_t));
89     size_t outputSize = outputFrameSize * outputFrames;
90     outputSize &= ~7;
91 
92     // create the resampler
93     android::AudioResampler* resampler;
94 
95     resampler = android::AudioResampler::create(format, channels, outputFreq, quality);
96     resampler->setSampleRate(inputFreq);
97     resampler->setVolume(android::AudioResampler::UNITY_GAIN_FLOAT,
98             android::AudioResampler::UNITY_GAIN_FLOAT);
99 
100     // set up the reference run
101     std::vector<size_t> refIncr;
102     refIncr.push_back(outputFrames);
103     void* reference = malloc(outputSize);
104     resample(channels, reference, outputFrames, refIncr, &provider, resampler);
105 
106     provider.reset();
107 
108 #if 0
109     /* this test will fail - API interface issue: reset() does not clear internal buffers */
110     resampler->reset();
111 #else
112     delete resampler;
113     resampler = android::AudioResampler::create(format, channels, outputFreq, quality);
114     resampler->setSampleRate(inputFreq);
115     resampler->setVolume(android::AudioResampler::UNITY_GAIN_FLOAT,
116             android::AudioResampler::UNITY_GAIN_FLOAT);
117 #endif
118 
119     // set up the test run
120     std::vector<size_t> outIncr;
121     outIncr.push_back(1);
122     outIncr.push_back(2);
123     outIncr.push_back(3);
124     void* test = malloc(outputSize);
125     inputIncr.push_back(1);
126     inputIncr.push_back(3);
127     provider.setIncr(inputIncr);
128     resample(channels, test, outputFrames, outIncr, &provider, resampler);
129 
130     // check
131     buffercmp(reference, test, outputFrameSize, outputFrames);
132 
133     free(reference);
134     free(test);
135     delete resampler;
136 }
137 
138 template <typename T>
sqr(T v)139 inline double sqr(T v)
140 {
141     double dv = static_cast<double>(v);
142     return dv * dv;
143 }
144 
145 template <typename T>
signalEnergy(T * start,T * end,unsigned stride)146 double signalEnergy(T *start, T *end, unsigned stride)
147 {
148     double accum = 0;
149 
150     for (T *p = start; p < end; p += stride) {
151         accum += sqr(*p);
152     }
153     unsigned count = (end - start + stride - 1) / stride;
154     return accum / count;
155 }
156 
157 // TI = resampler input type, int16_t or float
158 // TO = resampler output type, int32_t or float
159 template <typename TI, typename TO>
testStopbandDownconversion(size_t channels,unsigned inputFreq,unsigned outputFreq,unsigned passband,unsigned stopband,enum android::AudioResampler::src_quality quality)160 void testStopbandDownconversion(size_t channels,
161         unsigned inputFreq, unsigned outputFreq,
162         unsigned passband, unsigned stopband,
163         enum android::AudioResampler::src_quality quality)
164 {
165     // create the provider
166     std::vector<int> inputIncr;
167     SignalProvider provider;
168     provider.setChirp<TI>(channels,
169             0., inputFreq/2., inputFreq, inputFreq/2000.);
170     provider.setIncr(inputIncr);
171 
172     // calculate the output size
173     size_t outputFrames = ((int64_t) provider.getNumFrames() * outputFreq) / inputFreq;
174     size_t outputFrameSize = channels * sizeof(TO);
175     size_t outputSize = outputFrameSize * outputFrames;
176     outputSize &= ~7;
177 
178     // create the resampler
179     android::AudioResampler* resampler;
180 
181     resampler = android::AudioResampler::create(
182             is_same<TI, int16_t>::value ? AUDIO_FORMAT_PCM_16_BIT : AUDIO_FORMAT_PCM_FLOAT,
183             channels, outputFreq, quality);
184     resampler->setSampleRate(inputFreq);
185     resampler->setVolume(android::AudioResampler::UNITY_GAIN_FLOAT,
186             android::AudioResampler::UNITY_GAIN_FLOAT);
187 
188     // set up the reference run
189     std::vector<size_t> refIncr;
190     refIncr.push_back(outputFrames);
191     void* reference = malloc(outputSize);
192     resample(channels, reference, outputFrames, refIncr, &provider, resampler);
193 
194     TO *out = reinterpret_cast<TO *>(reference);
195 
196     // check signal energy in passband
197     const unsigned passbandFrame = passband * outputFreq / 1000.;
198     const unsigned stopbandFrame = stopband * outputFreq / 1000.;
199 
200     // check each channel separately
201     for (size_t i = 0; i < channels; ++i) {
202         double passbandEnergy = signalEnergy(out, out + passbandFrame * channels, channels);
203         double stopbandEnergy = signalEnergy(out + stopbandFrame * channels,
204                 out + outputFrames * channels, channels);
205         double dbAtten = -10. * log10(stopbandEnergy / passbandEnergy);
206         ASSERT_GT(dbAtten, 60.);
207 
208 #if 0
209         // internal verification
210         printf("if:%d  of:%d  pbf:%d  sbf:%d  sbe: %f  pbe: %f  db: %.2f\n",
211                 provider.getNumFrames(), outputFrames,
212                 passbandFrame, stopbandFrame, stopbandEnergy, passbandEnergy, dbAtten);
213         for (size_t i = 0; i < 10; ++i) {
214             std::cout << out[i+passbandFrame*channels] << std::endl;
215         }
216         for (size_t i = 0; i < 10; ++i) {
217             std::cout << out[i+stopbandFrame*channels] << std::endl;
218         }
219 #endif
220     }
221 
222     free(reference);
223     delete resampler;
224 }
225 
226 /* Buffer increment test
227  *
228  * We compare a reference output, where we consume and process the entire
229  * buffer at a time, and a test output, where we provide small chunks of input
230  * data and process small chunks of output (which may not be equivalent in size).
231  *
232  * Two subtests - fixed phase (3:2 down) and interpolated phase (147:320 up)
233  */
TEST(audioflinger_resampler,bufferincrement_fixedphase)234 TEST(audioflinger_resampler, bufferincrement_fixedphase) {
235     // all of these work
236     static const enum android::AudioResampler::src_quality kQualityArray[] = {
237             android::AudioResampler::LOW_QUALITY,
238             android::AudioResampler::MED_QUALITY,
239             android::AudioResampler::HIGH_QUALITY,
240             android::AudioResampler::VERY_HIGH_QUALITY,
241             android::AudioResampler::DYN_LOW_QUALITY,
242             android::AudioResampler::DYN_MED_QUALITY,
243             android::AudioResampler::DYN_HIGH_QUALITY,
244     };
245 
246     for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
247         testBufferIncrement(2, false, 48000, 32000, kQualityArray[i]);
248     }
249 }
250 
TEST(audioflinger_resampler,bufferincrement_interpolatedphase)251 TEST(audioflinger_resampler, bufferincrement_interpolatedphase) {
252     // all of these work except low quality
253     static const enum android::AudioResampler::src_quality kQualityArray[] = {
254 //           android::AudioResampler::LOW_QUALITY,
255             android::AudioResampler::MED_QUALITY,
256             android::AudioResampler::HIGH_QUALITY,
257             android::AudioResampler::VERY_HIGH_QUALITY,
258             android::AudioResampler::DYN_LOW_QUALITY,
259             android::AudioResampler::DYN_MED_QUALITY,
260             android::AudioResampler::DYN_HIGH_QUALITY,
261     };
262 
263     for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
264         testBufferIncrement(2, false, 22050, 48000, kQualityArray[i]);
265     }
266 }
267 
TEST(audioflinger_resampler,bufferincrement_fixedphase_multi)268 TEST(audioflinger_resampler, bufferincrement_fixedphase_multi) {
269     // only dynamic quality
270     static const enum android::AudioResampler::src_quality kQualityArray[] = {
271             android::AudioResampler::DYN_LOW_QUALITY,
272             android::AudioResampler::DYN_MED_QUALITY,
273             android::AudioResampler::DYN_HIGH_QUALITY,
274     };
275 
276     for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
277         testBufferIncrement(4, false, 48000, 32000, kQualityArray[i]);
278     }
279 }
280 
TEST(audioflinger_resampler,bufferincrement_interpolatedphase_multi_float)281 TEST(audioflinger_resampler, bufferincrement_interpolatedphase_multi_float) {
282     // only dynamic quality
283     static const enum android::AudioResampler::src_quality kQualityArray[] = {
284             android::AudioResampler::DYN_LOW_QUALITY,
285             android::AudioResampler::DYN_MED_QUALITY,
286             android::AudioResampler::DYN_HIGH_QUALITY,
287     };
288 
289     for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
290         testBufferIncrement(8, true, 22050, 48000, kQualityArray[i]);
291     }
292 }
293 
294 /* Simple aliasing test
295  *
296  * This checks stopband response of the chirp signal to make sure frequencies
297  * are properly suppressed.  It uses downsampling because the stopband can be
298  * clearly isolated by input frequencies exceeding the output sample rate (nyquist).
299  */
TEST(audioflinger_resampler,stopbandresponse_integer)300 TEST(audioflinger_resampler, stopbandresponse_integer) {
301     // not all of these may work (old resamplers fail on downsampling)
302     static const enum android::AudioResampler::src_quality kQualityArray[] = {
303             //android::AudioResampler::LOW_QUALITY,
304             //android::AudioResampler::MED_QUALITY,
305             //android::AudioResampler::HIGH_QUALITY,
306             //android::AudioResampler::VERY_HIGH_QUALITY,
307             android::AudioResampler::DYN_LOW_QUALITY,
308             android::AudioResampler::DYN_MED_QUALITY,
309             android::AudioResampler::DYN_HIGH_QUALITY,
310     };
311 
312     // in this test we assume a maximum transition band between 12kHz and 20kHz.
313     // there must be at least 60dB relative attenuation between stopband and passband.
314     for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
315         testStopbandDownconversion<int16_t, int32_t>(
316                 2, 48000, 32000, 12000, 20000, kQualityArray[i]);
317     }
318 
319     // in this test we assume a maximum transition band between 7kHz and 15kHz.
320     // there must be at least 60dB relative attenuation between stopband and passband.
321     // (the weird ratio triggers interpolative resampling)
322     for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
323         testStopbandDownconversion<int16_t, int32_t>(
324                 2, 48000, 22101, 7000, 15000, kQualityArray[i]);
325     }
326 }
327 
TEST(audioflinger_resampler,stopbandresponse_integer_multichannel)328 TEST(audioflinger_resampler, stopbandresponse_integer_multichannel) {
329     // not all of these may work (old resamplers fail on downsampling)
330     static const enum android::AudioResampler::src_quality kQualityArray[] = {
331             //android::AudioResampler::LOW_QUALITY,
332             //android::AudioResampler::MED_QUALITY,
333             //android::AudioResampler::HIGH_QUALITY,
334             //android::AudioResampler::VERY_HIGH_QUALITY,
335             android::AudioResampler::DYN_LOW_QUALITY,
336             android::AudioResampler::DYN_MED_QUALITY,
337             android::AudioResampler::DYN_HIGH_QUALITY,
338     };
339 
340     // in this test we assume a maximum transition band between 12kHz and 20kHz.
341     // there must be at least 60dB relative attenuation between stopband and passband.
342     for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
343         testStopbandDownconversion<int16_t, int32_t>(
344                 8, 48000, 32000, 12000, 20000, kQualityArray[i]);
345     }
346 
347     // in this test we assume a maximum transition band between 7kHz and 15kHz.
348     // there must be at least 60dB relative attenuation between stopband and passband.
349     // (the weird ratio triggers interpolative resampling)
350     for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
351         testStopbandDownconversion<int16_t, int32_t>(
352                 8, 48000, 22101, 7000, 15000, kQualityArray[i]);
353     }
354 }
355 
TEST(audioflinger_resampler,stopbandresponse_float)356 TEST(audioflinger_resampler, stopbandresponse_float) {
357     // not all of these may work (old resamplers fail on downsampling)
358     static const enum android::AudioResampler::src_quality kQualityArray[] = {
359             //android::AudioResampler::LOW_QUALITY,
360             //android::AudioResampler::MED_QUALITY,
361             //android::AudioResampler::HIGH_QUALITY,
362             //android::AudioResampler::VERY_HIGH_QUALITY,
363             android::AudioResampler::DYN_LOW_QUALITY,
364             android::AudioResampler::DYN_MED_QUALITY,
365             android::AudioResampler::DYN_HIGH_QUALITY,
366     };
367 
368     // in this test we assume a maximum transition band between 12kHz and 20kHz.
369     // there must be at least 60dB relative attenuation between stopband and passband.
370     for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
371         testStopbandDownconversion<float, float>(
372                 2, 48000, 32000, 12000, 20000, kQualityArray[i]);
373     }
374 
375     // in this test we assume a maximum transition band between 7kHz and 15kHz.
376     // there must be at least 60dB relative attenuation between stopband and passband.
377     // (the weird ratio triggers interpolative resampling)
378     for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
379         testStopbandDownconversion<float, float>(
380                 2, 48000, 22101, 7000, 15000, kQualityArray[i]);
381     }
382 }
383 
TEST(audioflinger_resampler,stopbandresponse_float_multichannel)384 TEST(audioflinger_resampler, stopbandresponse_float_multichannel) {
385     // not all of these may work (old resamplers fail on downsampling)
386     static const enum android::AudioResampler::src_quality kQualityArray[] = {
387             //android::AudioResampler::LOW_QUALITY,
388             //android::AudioResampler::MED_QUALITY,
389             //android::AudioResampler::HIGH_QUALITY,
390             //android::AudioResampler::VERY_HIGH_QUALITY,
391             android::AudioResampler::DYN_LOW_QUALITY,
392             android::AudioResampler::DYN_MED_QUALITY,
393             android::AudioResampler::DYN_HIGH_QUALITY,
394     };
395 
396     // in this test we assume a maximum transition band between 12kHz and 20kHz.
397     // there must be at least 60dB relative attenuation between stopband and passband.
398     for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
399         testStopbandDownconversion<float, float>(
400                 8, 48000, 32000, 12000, 20000, kQualityArray[i]);
401     }
402 
403     // in this test we assume a maximum transition band between 7kHz and 15kHz.
404     // there must be at least 60dB relative attenuation between stopband and passband.
405     // (the weird ratio triggers interpolative resampling)
406     for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
407         testStopbandDownconversion<float, float>(
408                 8, 48000, 22101, 7000, 15000, kQualityArray[i]);
409     }
410 }
411 
412