blob: fbc8cc5f4d2b2d669d72fb9799b5f183406b7ee7 [file] [log] [blame]
Andy Hung546734b2014-04-01 18:31:42 -07001/*
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
Mark Salyzyn60d02072016-09-29 08:48:48 -070020#include <errno.h>
21#include <fcntl.h>
22#include <math.h>
Andy Hung546734b2014-04-01 18:31:42 -070023#include <stdio.h>
24#include <stdlib.h>
Andy Hung546734b2014-04-01 18:31:42 -070025#include <string.h>
26#include <sys/mman.h>
27#include <sys/stat.h>
Andy Hung546734b2014-04-01 18:31:42 -070028#include <time.h>
Mark Salyzyn60d02072016-09-29 08:48:48 -070029#include <unistd.h>
30
Andy Hung42b01112014-07-20 14:04:19 -070031#include <iostream>
Mark Salyzyn60d02072016-09-29 08:48:48 -070032#include <utility>
33#include <vector>
34
35#include <android/log.h>
Andy Hung546734b2014-04-01 18:31:42 -070036#include <gtest/gtest.h>
37#include <media/AudioBufferProvider.h>
Mark Salyzyn60d02072016-09-29 08:48:48 -070038
Andy Hung546734b2014-04-01 18:31:42 -070039#include "AudioResampler.h"
Andy Hungc0e5ec82014-06-17 14:33:39 -070040#include "test_utils.h"
Andy Hung546734b2014-04-01 18:31:42 -070041
Andy Hung075abae2014-04-09 19:36:43 -070042void resample(int channels, void *output,
43 size_t outputFrames, const std::vector<size_t> &outputIncr,
Andy Hung546734b2014-04-01 18:31:42 -070044 android::AudioBufferProvider *provider, android::AudioResampler *resampler)
45{
46 for (size_t i = 0, j = 0; i < outputFrames; ) {
47 size_t thisFrames = outputIncr[j++];
48 if (j >= outputIncr.size()) {
49 j = 0;
50 }
51 if (thisFrames == 0 || thisFrames > outputFrames - i) {
52 thisFrames = outputFrames - i;
53 }
Andy Hung6b3b7e32015-03-29 00:49:22 -070054 size_t framesResampled = resampler->resample(
55 (int32_t*) output + channels*i, thisFrames, provider);
56 // we should have enough buffer space, so there is no short count.
57 ASSERT_EQ(thisFrames, framesResampled);
Andy Hung546734b2014-04-01 18:31:42 -070058 i += thisFrames;
59 }
60}
61
62void buffercmp(const void *reference, const void *test,
63 size_t outputFrameSize, size_t outputFrames)
64{
65 for (size_t i = 0; i < outputFrames; ++i) {
66 int check = memcmp((const char*)reference + i * outputFrameSize,
67 (const char*)test + i * outputFrameSize, outputFrameSize);
68 if (check) {
Glenn Kastena4daf0b2014-07-28 16:34:45 -070069 ALOGE("Failure at frame %zu", i);
Andy Hung546734b2014-04-01 18:31:42 -070070 ASSERT_EQ(check, 0); /* fails */
71 }
72 }
73}
74
Andy Hung075abae2014-04-09 19:36:43 -070075void testBufferIncrement(size_t channels, bool useFloat,
76 unsigned inputFreq, unsigned outputFreq,
Andy Hung546734b2014-04-01 18:31:42 -070077 enum android::AudioResampler::src_quality quality)
78{
Andy Hung3348e362014-07-07 10:21:44 -070079 const audio_format_t format = useFloat ? AUDIO_FORMAT_PCM_FLOAT : AUDIO_FORMAT_PCM_16_BIT;
Andy Hung546734b2014-04-01 18:31:42 -070080 // create the provider
Andy Hungc0e5ec82014-06-17 14:33:39 -070081 std::vector<int> inputIncr;
82 SignalProvider provider;
Andy Hung075abae2014-04-09 19:36:43 -070083 if (useFloat) {
84 provider.setChirp<float>(channels,
85 0., outputFreq/2., outputFreq, outputFreq/2000.);
86 } else {
87 provider.setChirp<int16_t>(channels,
88 0., outputFreq/2., outputFreq, outputFreq/2000.);
89 }
Andy Hungc0e5ec82014-06-17 14:33:39 -070090 provider.setIncr(inputIncr);
Andy Hung546734b2014-04-01 18:31:42 -070091
92 // calculate the output size
93 size_t outputFrames = ((int64_t) provider.getNumFrames() * outputFreq) / inputFreq;
Andy Hung075abae2014-04-09 19:36:43 -070094 size_t outputFrameSize = channels * (useFloat ? sizeof(float) : sizeof(int32_t));
Andy Hung546734b2014-04-01 18:31:42 -070095 size_t outputSize = outputFrameSize * outputFrames;
96 outputSize &= ~7;
97
98 // create the resampler
Andy Hung546734b2014-04-01 18:31:42 -070099 android::AudioResampler* resampler;
100
Andy Hung3348e362014-07-07 10:21:44 -0700101 resampler = android::AudioResampler::create(format, channels, outputFreq, quality);
Andy Hung546734b2014-04-01 18:31:42 -0700102 resampler->setSampleRate(inputFreq);
Andy Hung5e58b0a2014-06-23 19:07:29 -0700103 resampler->setVolume(android::AudioResampler::UNITY_GAIN_FLOAT,
104 android::AudioResampler::UNITY_GAIN_FLOAT);
Andy Hung546734b2014-04-01 18:31:42 -0700105
106 // set up the reference run
107 std::vector<size_t> refIncr;
108 refIncr.push_back(outputFrames);
Andy Hungccbba6e2017-01-05 16:43:35 -0800109 void* reference = calloc(outputFrames, outputFrameSize);
Andy Hung075abae2014-04-09 19:36:43 -0700110 resample(channels, reference, outputFrames, refIncr, &provider, resampler);
Andy Hung546734b2014-04-01 18:31:42 -0700111
112 provider.reset();
113
114#if 0
115 /* this test will fail - API interface issue: reset() does not clear internal buffers */
116 resampler->reset();
117#else
118 delete resampler;
Andy Hung3348e362014-07-07 10:21:44 -0700119 resampler = android::AudioResampler::create(format, channels, outputFreq, quality);
Andy Hung546734b2014-04-01 18:31:42 -0700120 resampler->setSampleRate(inputFreq);
Andy Hung5e58b0a2014-06-23 19:07:29 -0700121 resampler->setVolume(android::AudioResampler::UNITY_GAIN_FLOAT,
122 android::AudioResampler::UNITY_GAIN_FLOAT);
Andy Hung546734b2014-04-01 18:31:42 -0700123#endif
124
125 // set up the test run
126 std::vector<size_t> outIncr;
127 outIncr.push_back(1);
128 outIncr.push_back(2);
129 outIncr.push_back(3);
Andy Hungccbba6e2017-01-05 16:43:35 -0800130 void* test = calloc(outputFrames, outputFrameSize);
Andy Hung075abae2014-04-09 19:36:43 -0700131 inputIncr.push_back(1);
132 inputIncr.push_back(3);
133 provider.setIncr(inputIncr);
134 resample(channels, test, outputFrames, outIncr, &provider, resampler);
Andy Hung546734b2014-04-01 18:31:42 -0700135
136 // check
137 buffercmp(reference, test, outputFrameSize, outputFrames);
138
139 free(reference);
140 free(test);
141 delete resampler;
142}
143
144template <typename T>
145inline double sqr(T v)
146{
147 double dv = static_cast<double>(v);
148 return dv * dv;
149}
150
151template <typename T>
152double signalEnergy(T *start, T *end, unsigned stride)
153{
154 double accum = 0;
155
156 for (T *p = start; p < end; p += stride) {
157 accum += sqr(*p);
158 }
159 unsigned count = (end - start + stride - 1) / stride;
160 return accum / count;
161}
162
Andy Hung42b01112014-07-20 14:04:19 -0700163// TI = resampler input type, int16_t or float
164// TO = resampler output type, int32_t or float
165template <typename TI, typename TO>
Andy Hung546734b2014-04-01 18:31:42 -0700166void testStopbandDownconversion(size_t channels,
167 unsigned inputFreq, unsigned outputFreq,
168 unsigned passband, unsigned stopband,
169 enum android::AudioResampler::src_quality quality)
170{
171 // create the provider
Andy Hungc0e5ec82014-06-17 14:33:39 -0700172 std::vector<int> inputIncr;
173 SignalProvider provider;
Andy Hung42b01112014-07-20 14:04:19 -0700174 provider.setChirp<TI>(channels,
Andy Hungc0e5ec82014-06-17 14:33:39 -0700175 0., inputFreq/2., inputFreq, inputFreq/2000.);
176 provider.setIncr(inputIncr);
Andy Hung546734b2014-04-01 18:31:42 -0700177
178 // calculate the output size
179 size_t outputFrames = ((int64_t) provider.getNumFrames() * outputFreq) / inputFreq;
Andy Hung42b01112014-07-20 14:04:19 -0700180 size_t outputFrameSize = channels * sizeof(TO);
Andy Hung546734b2014-04-01 18:31:42 -0700181 size_t outputSize = outputFrameSize * outputFrames;
182 outputSize &= ~7;
183
184 // create the resampler
Andy Hung546734b2014-04-01 18:31:42 -0700185 android::AudioResampler* resampler;
186
Andy Hung42b01112014-07-20 14:04:19 -0700187 resampler = android::AudioResampler::create(
188 is_same<TI, int16_t>::value ? AUDIO_FORMAT_PCM_16_BIT : AUDIO_FORMAT_PCM_FLOAT,
Andy Hung3348e362014-07-07 10:21:44 -0700189 channels, outputFreq, quality);
Andy Hung546734b2014-04-01 18:31:42 -0700190 resampler->setSampleRate(inputFreq);
Andy Hung5e58b0a2014-06-23 19:07:29 -0700191 resampler->setVolume(android::AudioResampler::UNITY_GAIN_FLOAT,
192 android::AudioResampler::UNITY_GAIN_FLOAT);
Andy Hung546734b2014-04-01 18:31:42 -0700193
194 // set up the reference run
195 std::vector<size_t> refIncr;
196 refIncr.push_back(outputFrames);
Andy Hungccbba6e2017-01-05 16:43:35 -0800197 void* reference = calloc(outputFrames, outputFrameSize);
Andy Hung075abae2014-04-09 19:36:43 -0700198 resample(channels, reference, outputFrames, refIncr, &provider, resampler);
Andy Hung546734b2014-04-01 18:31:42 -0700199
Andy Hung42b01112014-07-20 14:04:19 -0700200 TO *out = reinterpret_cast<TO *>(reference);
Andy Hung546734b2014-04-01 18:31:42 -0700201
202 // check signal energy in passband
203 const unsigned passbandFrame = passband * outputFreq / 1000.;
204 const unsigned stopbandFrame = stopband * outputFreq / 1000.;
205
206 // check each channel separately
207 for (size_t i = 0; i < channels; ++i) {
208 double passbandEnergy = signalEnergy(out, out + passbandFrame * channels, channels);
209 double stopbandEnergy = signalEnergy(out + stopbandFrame * channels,
210 out + outputFrames * channels, channels);
211 double dbAtten = -10. * log10(stopbandEnergy / passbandEnergy);
212 ASSERT_GT(dbAtten, 60.);
213
214#if 0
215 // internal verification
216 printf("if:%d of:%d pbf:%d sbf:%d sbe: %f pbe: %f db: %.2f\n",
217 provider.getNumFrames(), outputFrames,
218 passbandFrame, stopbandFrame, stopbandEnergy, passbandEnergy, dbAtten);
219 for (size_t i = 0; i < 10; ++i) {
Andy Hung42b01112014-07-20 14:04:19 -0700220 std::cout << out[i+passbandFrame*channels] << std::endl;
Andy Hung546734b2014-04-01 18:31:42 -0700221 }
222 for (size_t i = 0; i < 10; ++i) {
Andy Hung42b01112014-07-20 14:04:19 -0700223 std::cout << out[i+stopbandFrame*channels] << std::endl;
Andy Hung546734b2014-04-01 18:31:42 -0700224 }
225#endif
226 }
227
228 free(reference);
229 delete resampler;
230}
231
232/* Buffer increment test
233 *
234 * We compare a reference output, where we consume and process the entire
235 * buffer at a time, and a test output, where we provide small chunks of input
236 * data and process small chunks of output (which may not be equivalent in size).
237 *
238 * Two subtests - fixed phase (3:2 down) and interpolated phase (147:320 up)
239 */
240TEST(audioflinger_resampler, bufferincrement_fixedphase) {
241 // all of these work
242 static const enum android::AudioResampler::src_quality kQualityArray[] = {
243 android::AudioResampler::LOW_QUALITY,
244 android::AudioResampler::MED_QUALITY,
245 android::AudioResampler::HIGH_QUALITY,
246 android::AudioResampler::VERY_HIGH_QUALITY,
247 android::AudioResampler::DYN_LOW_QUALITY,
248 android::AudioResampler::DYN_MED_QUALITY,
249 android::AudioResampler::DYN_HIGH_QUALITY,
250 };
251
252 for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
Andy Hung075abae2014-04-09 19:36:43 -0700253 testBufferIncrement(2, false, 48000, 32000, kQualityArray[i]);
Andy Hung546734b2014-04-01 18:31:42 -0700254 }
255}
256
257TEST(audioflinger_resampler, bufferincrement_interpolatedphase) {
258 // all of these work except low quality
259 static const enum android::AudioResampler::src_quality kQualityArray[] = {
260// android::AudioResampler::LOW_QUALITY,
261 android::AudioResampler::MED_QUALITY,
262 android::AudioResampler::HIGH_QUALITY,
263 android::AudioResampler::VERY_HIGH_QUALITY,
264 android::AudioResampler::DYN_LOW_QUALITY,
265 android::AudioResampler::DYN_MED_QUALITY,
266 android::AudioResampler::DYN_HIGH_QUALITY,
267 };
268
269 for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
Andy Hung075abae2014-04-09 19:36:43 -0700270 testBufferIncrement(2, false, 22050, 48000, kQualityArray[i]);
271 }
272}
273
274TEST(audioflinger_resampler, bufferincrement_fixedphase_multi) {
275 // only dynamic quality
276 static const enum android::AudioResampler::src_quality kQualityArray[] = {
277 android::AudioResampler::DYN_LOW_QUALITY,
278 android::AudioResampler::DYN_MED_QUALITY,
279 android::AudioResampler::DYN_HIGH_QUALITY,
280 };
281
282 for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
283 testBufferIncrement(4, false, 48000, 32000, kQualityArray[i]);
284 }
285}
286
287TEST(audioflinger_resampler, bufferincrement_interpolatedphase_multi_float) {
288 // only dynamic quality
289 static const enum android::AudioResampler::src_quality kQualityArray[] = {
290 android::AudioResampler::DYN_LOW_QUALITY,
291 android::AudioResampler::DYN_MED_QUALITY,
292 android::AudioResampler::DYN_HIGH_QUALITY,
293 };
294
295 for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
296 testBufferIncrement(8, true, 22050, 48000, kQualityArray[i]);
Andy Hung546734b2014-04-01 18:31:42 -0700297 }
298}
299
300/* Simple aliasing test
301 *
302 * This checks stopband response of the chirp signal to make sure frequencies
303 * are properly suppressed. It uses downsampling because the stopband can be
304 * clearly isolated by input frequencies exceeding the output sample rate (nyquist).
305 */
Andy Hung42b01112014-07-20 14:04:19 -0700306TEST(audioflinger_resampler, stopbandresponse_integer) {
Andy Hung546734b2014-04-01 18:31:42 -0700307 // not all of these may work (old resamplers fail on downsampling)
308 static const enum android::AudioResampler::src_quality kQualityArray[] = {
309 //android::AudioResampler::LOW_QUALITY,
310 //android::AudioResampler::MED_QUALITY,
311 //android::AudioResampler::HIGH_QUALITY,
312 //android::AudioResampler::VERY_HIGH_QUALITY,
313 android::AudioResampler::DYN_LOW_QUALITY,
314 android::AudioResampler::DYN_MED_QUALITY,
315 android::AudioResampler::DYN_HIGH_QUALITY,
316 };
317
318 // in this test we assume a maximum transition band between 12kHz and 20kHz.
319 // there must be at least 60dB relative attenuation between stopband and passband.
320 for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
Andy Hung42b01112014-07-20 14:04:19 -0700321 testStopbandDownconversion<int16_t, int32_t>(
322 2, 48000, 32000, 12000, 20000, kQualityArray[i]);
Andy Hung546734b2014-04-01 18:31:42 -0700323 }
324
325 // in this test we assume a maximum transition band between 7kHz and 15kHz.
326 // there must be at least 60dB relative attenuation between stopband and passband.
327 // (the weird ratio triggers interpolative resampling)
328 for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
Andy Hung42b01112014-07-20 14:04:19 -0700329 testStopbandDownconversion<int16_t, int32_t>(
330 2, 48000, 22101, 7000, 15000, kQualityArray[i]);
Andy Hung546734b2014-04-01 18:31:42 -0700331 }
332}
Andy Hung42b01112014-07-20 14:04:19 -0700333
334TEST(audioflinger_resampler, stopbandresponse_integer_multichannel) {
335 // not all of these may work (old resamplers fail on downsampling)
336 static const enum android::AudioResampler::src_quality kQualityArray[] = {
337 //android::AudioResampler::LOW_QUALITY,
338 //android::AudioResampler::MED_QUALITY,
339 //android::AudioResampler::HIGH_QUALITY,
340 //android::AudioResampler::VERY_HIGH_QUALITY,
341 android::AudioResampler::DYN_LOW_QUALITY,
342 android::AudioResampler::DYN_MED_QUALITY,
343 android::AudioResampler::DYN_HIGH_QUALITY,
344 };
345
346 // in this test we assume a maximum transition band between 12kHz and 20kHz.
347 // there must be at least 60dB relative attenuation between stopband and passband.
348 for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
349 testStopbandDownconversion<int16_t, int32_t>(
350 8, 48000, 32000, 12000, 20000, kQualityArray[i]);
351 }
352
353 // in this test we assume a maximum transition band between 7kHz and 15kHz.
354 // there must be at least 60dB relative attenuation between stopband and passband.
355 // (the weird ratio triggers interpolative resampling)
356 for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
357 testStopbandDownconversion<int16_t, int32_t>(
358 8, 48000, 22101, 7000, 15000, kQualityArray[i]);
359 }
360}
361
362TEST(audioflinger_resampler, stopbandresponse_float) {
363 // not all of these may work (old resamplers fail on downsampling)
364 static const enum android::AudioResampler::src_quality kQualityArray[] = {
365 //android::AudioResampler::LOW_QUALITY,
366 //android::AudioResampler::MED_QUALITY,
367 //android::AudioResampler::HIGH_QUALITY,
368 //android::AudioResampler::VERY_HIGH_QUALITY,
369 android::AudioResampler::DYN_LOW_QUALITY,
370 android::AudioResampler::DYN_MED_QUALITY,
371 android::AudioResampler::DYN_HIGH_QUALITY,
372 };
373
374 // in this test we assume a maximum transition band between 12kHz and 20kHz.
375 // there must be at least 60dB relative attenuation between stopband and passband.
376 for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
377 testStopbandDownconversion<float, float>(
378 2, 48000, 32000, 12000, 20000, kQualityArray[i]);
379 }
380
381 // in this test we assume a maximum transition band between 7kHz and 15kHz.
382 // there must be at least 60dB relative attenuation between stopband and passband.
383 // (the weird ratio triggers interpolative resampling)
384 for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
385 testStopbandDownconversion<float, float>(
386 2, 48000, 22101, 7000, 15000, kQualityArray[i]);
387 }
388}
389
390TEST(audioflinger_resampler, stopbandresponse_float_multichannel) {
391 // not all of these may work (old resamplers fail on downsampling)
392 static const enum android::AudioResampler::src_quality kQualityArray[] = {
393 //android::AudioResampler::LOW_QUALITY,
394 //android::AudioResampler::MED_QUALITY,
395 //android::AudioResampler::HIGH_QUALITY,
396 //android::AudioResampler::VERY_HIGH_QUALITY,
397 android::AudioResampler::DYN_LOW_QUALITY,
398 android::AudioResampler::DYN_MED_QUALITY,
399 android::AudioResampler::DYN_HIGH_QUALITY,
400 };
401
402 // in this test we assume a maximum transition band between 12kHz and 20kHz.
403 // there must be at least 60dB relative attenuation between stopband and passband.
404 for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
405 testStopbandDownconversion<float, float>(
406 8, 48000, 32000, 12000, 20000, kQualityArray[i]);
407 }
408
409 // in this test we assume a maximum transition band between 7kHz and 15kHz.
410 // there must be at least 60dB relative attenuation between stopband and passband.
411 // (the weird ratio triggers interpolative resampling)
412 for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
413 testStopbandDownconversion<float, float>(
414 8, 48000, 22101, 7000, 15000, kQualityArray[i]);
415 }
416}
417