blob: 169ce02043338d5ba545fa778b4644a6b664a04e [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
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>
Andy Hung42b01112014-07-20 14:04:19 -070032#include <iostream>
Andy Hung546734b2014-04-01 18:31:42 -070033#include <cutils/log.h>
34#include <gtest/gtest.h>
35#include <media/AudioBufferProvider.h>
36#include "AudioResampler.h"
Andy Hungc0e5ec82014-06-17 14:33:39 -070037#include "test_utils.h"
Andy Hung546734b2014-04-01 18:31:42 -070038
Andy Hung075abae2014-04-09 19:36:43 -070039void resample(int channels, void *output,
40 size_t outputFrames, const std::vector<size_t> &outputIncr,
Andy Hung546734b2014-04-01 18:31:42 -070041 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 }
Andy Hung075abae2014-04-09 19:36:43 -070051 resampler->resample((int32_t*) output + channels*i, thisFrames, provider);
Andy Hung546734b2014-04-01 18:31:42 -070052 i += thisFrames;
53 }
54}
55
56void 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 %d", i);
64 ASSERT_EQ(check, 0); /* fails */
65 }
66 }
67}
68
Andy Hung075abae2014-04-09 19:36:43 -070069void testBufferIncrement(size_t channels, bool useFloat,
70 unsigned inputFreq, unsigned outputFreq,
Andy Hung546734b2014-04-01 18:31:42 -070071 enum android::AudioResampler::src_quality quality)
72{
Andy Hung3348e362014-07-07 10:21:44 -070073 const audio_format_t format = useFloat ? AUDIO_FORMAT_PCM_FLOAT : AUDIO_FORMAT_PCM_16_BIT;
Andy Hung546734b2014-04-01 18:31:42 -070074 // create the provider
Andy Hungc0e5ec82014-06-17 14:33:39 -070075 std::vector<int> inputIncr;
76 SignalProvider provider;
Andy Hung075abae2014-04-09 19:36:43 -070077 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 }
Andy Hungc0e5ec82014-06-17 14:33:39 -070084 provider.setIncr(inputIncr);
Andy Hung546734b2014-04-01 18:31:42 -070085
86 // calculate the output size
87 size_t outputFrames = ((int64_t) provider.getNumFrames() * outputFreq) / inputFreq;
Andy Hung075abae2014-04-09 19:36:43 -070088 size_t outputFrameSize = channels * (useFloat ? sizeof(float) : sizeof(int32_t));
Andy Hung546734b2014-04-01 18:31:42 -070089 size_t outputSize = outputFrameSize * outputFrames;
90 outputSize &= ~7;
91
92 // create the resampler
Andy Hung546734b2014-04-01 18:31:42 -070093 android::AudioResampler* resampler;
94
Andy Hung3348e362014-07-07 10:21:44 -070095 resampler = android::AudioResampler::create(format, channels, outputFreq, quality);
Andy Hung546734b2014-04-01 18:31:42 -070096 resampler->setSampleRate(inputFreq);
Andy Hung5e58b0a2014-06-23 19:07:29 -070097 resampler->setVolume(android::AudioResampler::UNITY_GAIN_FLOAT,
98 android::AudioResampler::UNITY_GAIN_FLOAT);
Andy Hung546734b2014-04-01 18:31:42 -070099
100 // set up the reference run
101 std::vector<size_t> refIncr;
102 refIncr.push_back(outputFrames);
103 void* reference = malloc(outputSize);
Andy Hung075abae2014-04-09 19:36:43 -0700104 resample(channels, reference, outputFrames, refIncr, &provider, resampler);
Andy Hung546734b2014-04-01 18:31:42 -0700105
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;
Andy Hung3348e362014-07-07 10:21:44 -0700113 resampler = android::AudioResampler::create(format, channels, outputFreq, quality);
Andy Hung546734b2014-04-01 18:31:42 -0700114 resampler->setSampleRate(inputFreq);
Andy Hung5e58b0a2014-06-23 19:07:29 -0700115 resampler->setVolume(android::AudioResampler::UNITY_GAIN_FLOAT,
116 android::AudioResampler::UNITY_GAIN_FLOAT);
Andy Hung546734b2014-04-01 18:31:42 -0700117#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);
Andy Hung075abae2014-04-09 19:36:43 -0700125 inputIncr.push_back(1);
126 inputIncr.push_back(3);
127 provider.setIncr(inputIncr);
128 resample(channels, test, outputFrames, outIncr, &provider, resampler);
Andy Hung546734b2014-04-01 18:31:42 -0700129
130 // check
131 buffercmp(reference, test, outputFrameSize, outputFrames);
132
133 free(reference);
134 free(test);
135 delete resampler;
136}
137
138template <typename T>
139inline double sqr(T v)
140{
141 double dv = static_cast<double>(v);
142 return dv * dv;
143}
144
145template <typename T>
146double 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
Andy Hung42b01112014-07-20 14:04:19 -0700157// TI = resampler input type, int16_t or float
158// TO = resampler output type, int32_t or float
159template <typename TI, typename TO>
Andy Hung546734b2014-04-01 18:31:42 -0700160void 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
Andy Hungc0e5ec82014-06-17 14:33:39 -0700166 std::vector<int> inputIncr;
167 SignalProvider provider;
Andy Hung42b01112014-07-20 14:04:19 -0700168 provider.setChirp<TI>(channels,
Andy Hungc0e5ec82014-06-17 14:33:39 -0700169 0., inputFreq/2., inputFreq, inputFreq/2000.);
170 provider.setIncr(inputIncr);
Andy Hung546734b2014-04-01 18:31:42 -0700171
172 // calculate the output size
173 size_t outputFrames = ((int64_t) provider.getNumFrames() * outputFreq) / inputFreq;
Andy Hung42b01112014-07-20 14:04:19 -0700174 size_t outputFrameSize = channels * sizeof(TO);
Andy Hung546734b2014-04-01 18:31:42 -0700175 size_t outputSize = outputFrameSize * outputFrames;
176 outputSize &= ~7;
177
178 // create the resampler
Andy Hung546734b2014-04-01 18:31:42 -0700179 android::AudioResampler* resampler;
180
Andy Hung42b01112014-07-20 14:04:19 -0700181 resampler = android::AudioResampler::create(
182 is_same<TI, int16_t>::value ? AUDIO_FORMAT_PCM_16_BIT : AUDIO_FORMAT_PCM_FLOAT,
Andy Hung3348e362014-07-07 10:21:44 -0700183 channels, outputFreq, quality);
Andy Hung546734b2014-04-01 18:31:42 -0700184 resampler->setSampleRate(inputFreq);
Andy Hung5e58b0a2014-06-23 19:07:29 -0700185 resampler->setVolume(android::AudioResampler::UNITY_GAIN_FLOAT,
186 android::AudioResampler::UNITY_GAIN_FLOAT);
Andy Hung546734b2014-04-01 18:31:42 -0700187
188 // set up the reference run
189 std::vector<size_t> refIncr;
190 refIncr.push_back(outputFrames);
191 void* reference = malloc(outputSize);
Andy Hung075abae2014-04-09 19:36:43 -0700192 resample(channels, reference, outputFrames, refIncr, &provider, resampler);
Andy Hung546734b2014-04-01 18:31:42 -0700193
Andy Hung42b01112014-07-20 14:04:19 -0700194 TO *out = reinterpret_cast<TO *>(reference);
Andy Hung546734b2014-04-01 18:31:42 -0700195
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) {
Andy Hung42b01112014-07-20 14:04:19 -0700214 std::cout << out[i+passbandFrame*channels] << std::endl;
Andy Hung546734b2014-04-01 18:31:42 -0700215 }
216 for (size_t i = 0; i < 10; ++i) {
Andy Hung42b01112014-07-20 14:04:19 -0700217 std::cout << out[i+stopbandFrame*channels] << std::endl;
Andy Hung546734b2014-04-01 18:31:42 -0700218 }
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 */
234TEST(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) {
Andy Hung075abae2014-04-09 19:36:43 -0700247 testBufferIncrement(2, false, 48000, 32000, kQualityArray[i]);
Andy Hung546734b2014-04-01 18:31:42 -0700248 }
249}
250
251TEST(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) {
Andy Hung075abae2014-04-09 19:36:43 -0700264 testBufferIncrement(2, false, 22050, 48000, kQualityArray[i]);
265 }
266}
267
268TEST(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
281TEST(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]);
Andy Hung546734b2014-04-01 18:31:42 -0700291 }
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 */
Andy Hung42b01112014-07-20 14:04:19 -0700300TEST(audioflinger_resampler, stopbandresponse_integer) {
Andy Hung546734b2014-04-01 18:31:42 -0700301 // 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) {
Andy Hung42b01112014-07-20 14:04:19 -0700315 testStopbandDownconversion<int16_t, int32_t>(
316 2, 48000, 32000, 12000, 20000, kQualityArray[i]);
Andy Hung546734b2014-04-01 18:31:42 -0700317 }
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) {
Andy Hung42b01112014-07-20 14:04:19 -0700323 testStopbandDownconversion<int16_t, int32_t>(
324 2, 48000, 22101, 7000, 15000, kQualityArray[i]);
Andy Hung546734b2014-04-01 18:31:42 -0700325 }
326}
Andy Hung42b01112014-07-20 14:04:19 -0700327
328TEST(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
356TEST(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
384TEST(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