blob: 987162cf0fc5626a3ef54d658c57186f82c59a3a [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>
32#include <cutils/log.h>
33#include <gtest/gtest.h>
34#include <media/AudioBufferProvider.h>
35#include "AudioResampler.h"
Andy Hungc0e5ec82014-06-17 14:33:39 -070036#include "test_utils.h"
Andy Hung546734b2014-04-01 18:31:42 -070037
Andy Hung075abae2014-04-09 19:36:43 -070038void resample(int channels, void *output,
39 size_t outputFrames, const std::vector<size_t> &outputIncr,
Andy Hung546734b2014-04-01 18:31:42 -070040 android::AudioBufferProvider *provider, android::AudioResampler *resampler)
41{
42 for (size_t i = 0, j = 0; i < outputFrames; ) {
43 size_t thisFrames = outputIncr[j++];
44 if (j >= outputIncr.size()) {
45 j = 0;
46 }
47 if (thisFrames == 0 || thisFrames > outputFrames - i) {
48 thisFrames = outputFrames - i;
49 }
Andy Hung075abae2014-04-09 19:36:43 -070050 resampler->resample((int32_t*) output + channels*i, thisFrames, provider);
Andy Hung546734b2014-04-01 18:31:42 -070051 i += thisFrames;
52 }
53}
54
55void buffercmp(const void *reference, const void *test,
56 size_t outputFrameSize, size_t outputFrames)
57{
58 for (size_t i = 0; i < outputFrames; ++i) {
59 int check = memcmp((const char*)reference + i * outputFrameSize,
60 (const char*)test + i * outputFrameSize, outputFrameSize);
61 if (check) {
62 ALOGE("Failure at frame %d", i);
63 ASSERT_EQ(check, 0); /* fails */
64 }
65 }
66}
67
Andy Hung075abae2014-04-09 19:36:43 -070068void testBufferIncrement(size_t channels, bool useFloat,
69 unsigned inputFreq, unsigned outputFreq,
Andy Hung546734b2014-04-01 18:31:42 -070070 enum android::AudioResampler::src_quality quality)
71{
Andy Hung3348e362014-07-07 10:21:44 -070072 const audio_format_t format = useFloat ? AUDIO_FORMAT_PCM_FLOAT : AUDIO_FORMAT_PCM_16_BIT;
Andy Hung546734b2014-04-01 18:31:42 -070073 // create the provider
Andy Hungc0e5ec82014-06-17 14:33:39 -070074 std::vector<int> inputIncr;
75 SignalProvider provider;
Andy Hung075abae2014-04-09 19:36:43 -070076 if (useFloat) {
77 provider.setChirp<float>(channels,
78 0., outputFreq/2., outputFreq, outputFreq/2000.);
79 } else {
80 provider.setChirp<int16_t>(channels,
81 0., outputFreq/2., outputFreq, outputFreq/2000.);
82 }
Andy Hungc0e5ec82014-06-17 14:33:39 -070083 provider.setIncr(inputIncr);
Andy Hung546734b2014-04-01 18:31:42 -070084
85 // calculate the output size
86 size_t outputFrames = ((int64_t) provider.getNumFrames() * outputFreq) / inputFreq;
Andy Hung075abae2014-04-09 19:36:43 -070087 size_t outputFrameSize = channels * (useFloat ? sizeof(float) : sizeof(int32_t));
Andy Hung546734b2014-04-01 18:31:42 -070088 size_t outputSize = outputFrameSize * outputFrames;
89 outputSize &= ~7;
90
91 // create the resampler
92 const int volumePrecision = 12; /* typical unity gain */
93 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);
97 resampler->setVolume(1 << volumePrecision, 1 << volumePrecision);
98
99 // set up the reference run
100 std::vector<size_t> refIncr;
101 refIncr.push_back(outputFrames);
102 void* reference = malloc(outputSize);
Andy Hung075abae2014-04-09 19:36:43 -0700103 resample(channels, reference, outputFrames, refIncr, &provider, resampler);
Andy Hung546734b2014-04-01 18:31:42 -0700104
105 provider.reset();
106
107#if 0
108 /* this test will fail - API interface issue: reset() does not clear internal buffers */
109 resampler->reset();
110#else
111 delete resampler;
Andy Hung3348e362014-07-07 10:21:44 -0700112 resampler = android::AudioResampler::create(format, channels, outputFreq, quality);
Andy Hung546734b2014-04-01 18:31:42 -0700113 resampler->setSampleRate(inputFreq);
114 resampler->setVolume(1 << volumePrecision, 1 << volumePrecision);
115#endif
116
117 // set up the test run
118 std::vector<size_t> outIncr;
119 outIncr.push_back(1);
120 outIncr.push_back(2);
121 outIncr.push_back(3);
122 void* test = malloc(outputSize);
Andy Hung075abae2014-04-09 19:36:43 -0700123 inputIncr.push_back(1);
124 inputIncr.push_back(3);
125 provider.setIncr(inputIncr);
126 resample(channels, test, outputFrames, outIncr, &provider, resampler);
Andy Hung546734b2014-04-01 18:31:42 -0700127
128 // check
129 buffercmp(reference, test, outputFrameSize, outputFrames);
130
131 free(reference);
132 free(test);
133 delete resampler;
134}
135
136template <typename T>
137inline double sqr(T v)
138{
139 double dv = static_cast<double>(v);
140 return dv * dv;
141}
142
143template <typename T>
144double signalEnergy(T *start, T *end, unsigned stride)
145{
146 double accum = 0;
147
148 for (T *p = start; p < end; p += stride) {
149 accum += sqr(*p);
150 }
151 unsigned count = (end - start + stride - 1) / stride;
152 return accum / count;
153}
154
155void testStopbandDownconversion(size_t channels,
156 unsigned inputFreq, unsigned outputFreq,
157 unsigned passband, unsigned stopband,
158 enum android::AudioResampler::src_quality quality)
159{
160 // create the provider
Andy Hungc0e5ec82014-06-17 14:33:39 -0700161 std::vector<int> inputIncr;
162 SignalProvider provider;
163 provider.setChirp<int16_t>(channels,
164 0., inputFreq/2., inputFreq, inputFreq/2000.);
165 provider.setIncr(inputIncr);
Andy Hung546734b2014-04-01 18:31:42 -0700166
167 // calculate the output size
168 size_t outputFrames = ((int64_t) provider.getNumFrames() * outputFreq) / inputFreq;
Andy Hung075abae2014-04-09 19:36:43 -0700169 size_t outputFrameSize = channels * sizeof(int32_t);
Andy Hung546734b2014-04-01 18:31:42 -0700170 size_t outputSize = outputFrameSize * outputFrames;
171 outputSize &= ~7;
172
173 // create the resampler
174 const int volumePrecision = 12; /* typical unity gain */
175 android::AudioResampler* resampler;
176
Andy Hung3348e362014-07-07 10:21:44 -0700177 resampler = android::AudioResampler::create(AUDIO_FORMAT_PCM_16_BIT,
178 channels, outputFreq, quality);
Andy Hung546734b2014-04-01 18:31:42 -0700179 resampler->setSampleRate(inputFreq);
180 resampler->setVolume(1 << volumePrecision, 1 << volumePrecision);
181
182 // set up the reference run
183 std::vector<size_t> refIncr;
184 refIncr.push_back(outputFrames);
185 void* reference = malloc(outputSize);
Andy Hung075abae2014-04-09 19:36:43 -0700186 resample(channels, reference, outputFrames, refIncr, &provider, resampler);
Andy Hung546734b2014-04-01 18:31:42 -0700187
188 int32_t *out = reinterpret_cast<int32_t *>(reference);
189
190 // check signal energy in passband
191 const unsigned passbandFrame = passband * outputFreq / 1000.;
192 const unsigned stopbandFrame = stopband * outputFreq / 1000.;
193
194 // check each channel separately
195 for (size_t i = 0; i < channels; ++i) {
196 double passbandEnergy = signalEnergy(out, out + passbandFrame * channels, channels);
197 double stopbandEnergy = signalEnergy(out + stopbandFrame * channels,
198 out + outputFrames * channels, channels);
199 double dbAtten = -10. * log10(stopbandEnergy / passbandEnergy);
200 ASSERT_GT(dbAtten, 60.);
201
202#if 0
203 // internal verification
204 printf("if:%d of:%d pbf:%d sbf:%d sbe: %f pbe: %f db: %.2f\n",
205 provider.getNumFrames(), outputFrames,
206 passbandFrame, stopbandFrame, stopbandEnergy, passbandEnergy, dbAtten);
207 for (size_t i = 0; i < 10; ++i) {
208 printf("%d\n", out[i+passbandFrame*channels]);
209 }
210 for (size_t i = 0; i < 10; ++i) {
211 printf("%d\n", out[i+stopbandFrame*channels]);
212 }
213#endif
214 }
215
216 free(reference);
217 delete resampler;
218}
219
220/* Buffer increment test
221 *
222 * We compare a reference output, where we consume and process the entire
223 * buffer at a time, and a test output, where we provide small chunks of input
224 * data and process small chunks of output (which may not be equivalent in size).
225 *
226 * Two subtests - fixed phase (3:2 down) and interpolated phase (147:320 up)
227 */
228TEST(audioflinger_resampler, bufferincrement_fixedphase) {
229 // all of these work
230 static const enum android::AudioResampler::src_quality kQualityArray[] = {
231 android::AudioResampler::LOW_QUALITY,
232 android::AudioResampler::MED_QUALITY,
233 android::AudioResampler::HIGH_QUALITY,
234 android::AudioResampler::VERY_HIGH_QUALITY,
235 android::AudioResampler::DYN_LOW_QUALITY,
236 android::AudioResampler::DYN_MED_QUALITY,
237 android::AudioResampler::DYN_HIGH_QUALITY,
238 };
239
240 for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
Andy Hung075abae2014-04-09 19:36:43 -0700241 testBufferIncrement(2, false, 48000, 32000, kQualityArray[i]);
Andy Hung546734b2014-04-01 18:31:42 -0700242 }
243}
244
245TEST(audioflinger_resampler, bufferincrement_interpolatedphase) {
246 // all of these work except low quality
247 static const enum android::AudioResampler::src_quality kQualityArray[] = {
248// android::AudioResampler::LOW_QUALITY,
249 android::AudioResampler::MED_QUALITY,
250 android::AudioResampler::HIGH_QUALITY,
251 android::AudioResampler::VERY_HIGH_QUALITY,
252 android::AudioResampler::DYN_LOW_QUALITY,
253 android::AudioResampler::DYN_MED_QUALITY,
254 android::AudioResampler::DYN_HIGH_QUALITY,
255 };
256
257 for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
Andy Hung075abae2014-04-09 19:36:43 -0700258 testBufferIncrement(2, false, 22050, 48000, kQualityArray[i]);
259 }
260}
261
262TEST(audioflinger_resampler, bufferincrement_fixedphase_multi) {
263 // only dynamic quality
264 static const enum android::AudioResampler::src_quality kQualityArray[] = {
265 android::AudioResampler::DYN_LOW_QUALITY,
266 android::AudioResampler::DYN_MED_QUALITY,
267 android::AudioResampler::DYN_HIGH_QUALITY,
268 };
269
270 for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
271 testBufferIncrement(4, false, 48000, 32000, kQualityArray[i]);
272 }
273}
274
275TEST(audioflinger_resampler, bufferincrement_interpolatedphase_multi_float) {
276 // only dynamic quality
277 static const enum android::AudioResampler::src_quality kQualityArray[] = {
278 android::AudioResampler::DYN_LOW_QUALITY,
279 android::AudioResampler::DYN_MED_QUALITY,
280 android::AudioResampler::DYN_HIGH_QUALITY,
281 };
282
283 for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
284 testBufferIncrement(8, true, 22050, 48000, kQualityArray[i]);
Andy Hung546734b2014-04-01 18:31:42 -0700285 }
286}
287
288/* Simple aliasing test
289 *
290 * This checks stopband response of the chirp signal to make sure frequencies
291 * are properly suppressed. It uses downsampling because the stopband can be
292 * clearly isolated by input frequencies exceeding the output sample rate (nyquist).
293 */
294TEST(audioflinger_resampler, stopbandresponse) {
295 // not all of these may work (old resamplers fail on downsampling)
296 static const enum android::AudioResampler::src_quality kQualityArray[] = {
297 //android::AudioResampler::LOW_QUALITY,
298 //android::AudioResampler::MED_QUALITY,
299 //android::AudioResampler::HIGH_QUALITY,
300 //android::AudioResampler::VERY_HIGH_QUALITY,
301 android::AudioResampler::DYN_LOW_QUALITY,
302 android::AudioResampler::DYN_MED_QUALITY,
303 android::AudioResampler::DYN_HIGH_QUALITY,
304 };
305
306 // in this test we assume a maximum transition band between 12kHz and 20kHz.
307 // there must be at least 60dB relative attenuation between stopband and passband.
308 for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
309 testStopbandDownconversion(2, 48000, 32000, 12000, 20000, kQualityArray[i]);
310 }
311
312 // in this test we assume a maximum transition band between 7kHz and 15kHz.
313 // there must be at least 60dB relative attenuation between stopband and passband.
314 // (the weird ratio triggers interpolative resampling)
315 for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
316 testStopbandDownconversion(2, 48000, 22101, 7000, 15000, kQualityArray[i]);
317 }
318}