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