blob: 8f9c2701794e4241e0b58a78981ec6df2bb61e90 [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"
36
37#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
38
39template<typename T, typename U>
40struct is_same
41{
42 static const bool value = false;
43};
44
45template<typename T>
46struct is_same<T, T> // partial specialization
47{
48 static const bool value = true;
49};
50
51template<typename T>
52static inline T convertValue(double val)
53{
54 if (is_same<T, int16_t>::value) {
55 return floor(val * 32767.0 + 0.5);
56 } else if (is_same<T, int32_t>::value) {
57 return floor(val * (1UL<<31) + 0.5);
58 }
59 return val; // assume float or double
60}
61
62/* Creates a type-independent audio buffer provider from
63 * a buffer base address, size, framesize, and input increment array.
64 *
65 * No allocation or deallocation of the provided buffer is done.
66 */
67class TestProvider : public android::AudioBufferProvider {
68public:
69 TestProvider(const void* addr, size_t frames, size_t frameSize,
70 const std::vector<size_t>& inputIncr)
71 : mAddr(addr),
72 mNumFrames(frames),
73 mFrameSize(frameSize),
74 mNextFrame(0), mUnrel(0), mInputIncr(inputIncr), mNextIdx(0)
75 {
76 }
77
78 virtual android::status_t getNextBuffer(Buffer* buffer, int64_t pts __unused = kInvalidPTS )
79 {
80 size_t requestedFrames = buffer->frameCount;
81 if (requestedFrames > mNumFrames - mNextFrame) {
82 buffer->frameCount = mNumFrames - mNextFrame;
83 }
84 if (!mInputIncr.empty()) {
85 size_t provided = mInputIncr[mNextIdx++];
86 ALOGV("getNextBuffer() mValue[%d]=%u not %u",
87 mNextIdx-1, provided, buffer->frameCount);
88 if (provided < buffer->frameCount) {
89 buffer->frameCount = provided;
90 }
91 if (mNextIdx >= mInputIncr.size()) {
92 mNextIdx = 0;
93 }
94 }
95 ALOGV("getNextBuffer() requested %u frames out of %u frames available"
96 " and returned %u frames\n",
97 requestedFrames, mNumFrames - mNextFrame, buffer->frameCount);
98 mUnrel = buffer->frameCount;
99 if (buffer->frameCount > 0) {
100 buffer->raw = (char *)mAddr + mFrameSize * mNextFrame;
101 return android::NO_ERROR;
102 } else {
103 buffer->raw = NULL;
104 return android::NOT_ENOUGH_DATA;
105 }
106 }
107
108 virtual void releaseBuffer(Buffer* buffer)
109 {
110 if (buffer->frameCount > mUnrel) {
111 ALOGE("releaseBuffer() released %u frames but only %u available "
112 "to release\n", buffer->frameCount, mUnrel);
113 mNextFrame += mUnrel;
114 mUnrel = 0;
115 } else {
116
117 ALOGV("releaseBuffer() released %u frames out of %u frames available "
118 "to release\n", buffer->frameCount, mUnrel);
119 mNextFrame += buffer->frameCount;
120 mUnrel -= buffer->frameCount;
121 }
122 buffer->frameCount = 0;
123 buffer->raw = NULL;
124 }
125
126 void reset()
127 {
128 mNextFrame = 0;
129 }
130
131 size_t getNumFrames()
132 {
133 return mNumFrames;
134 }
135
136 void setIncr(const std::vector<size_t> inputIncr)
137 {
138 mNextIdx = 0;
139 mInputIncr = inputIncr;
140 }
141
142protected:
143 const void* mAddr; // base address
144 size_t mNumFrames; // total frames
145 int mFrameSize; // frame size (# channels * bytes per sample)
146 size_t mNextFrame; // index of next frame to provide
147 size_t mUnrel; // number of frames not yet released
148 std::vector<size_t> mInputIncr; // number of frames provided per call
149 size_t mNextIdx; // index of next entry in mInputIncr to use
150};
151
152/* Creates a buffer filled with a sine wave.
153 *
154 * Returns a pair consisting of the sine signal buffer and the number of frames.
155 * The caller must delete[] the buffer when no longer needed (no shared_ptr<>).
156 */
157template<typename T>
158static std::pair<T*, size_t> createSine(size_t channels,
159 double freq, double samplingRate, double time)
160{
161 double tscale = 1. / samplingRate;
162 size_t frames = static_cast<size_t>(samplingRate * time);
163 T* buffer = new T[frames * channels];
164 for (size_t i = 0; i < frames; ++i) {
165 double t = i * tscale;
166 double y = sin(2. * M_PI * freq * t);
167 T yt = convertValue<T>(y);
168
169 for (size_t j = 0; j < channels; ++j) {
170 buffer[i*channels + j] = yt / (j + 1);
171 }
172 }
173 return std::make_pair(buffer, frames);
174}
175
176/* Creates a buffer filled with a chirp signal (a sine wave sweep).
177 *
178 * Returns a pair consisting of the chirp signal buffer and the number of frames.
179 * The caller must delete[] the buffer when no longer needed (no shared_ptr<>).
180 *
181 * When creating the Chirp, note that the frequency is the true sinusoidal
182 * frequency not the sampling rate.
183 *
184 * http://en.wikipedia.org/wiki/Chirp
185 */
186template<typename T>
187static std::pair<T*, size_t> createChirp(size_t channels,
188 double minfreq, double maxfreq, double samplingRate, double time)
189{
190 double tscale = 1. / samplingRate;
191 size_t frames = static_cast<size_t>(samplingRate * time);
192 T *buffer = new T[frames * channels];
193 // note the chirp constant k has a divide-by-two.
194 double k = (maxfreq - minfreq) / (2. * time);
195 for (size_t i = 0; i < frames; ++i) {
196 double t = i * tscale;
197 double y = sin(2. * M_PI * (k * t + minfreq) * t);
198 T yt = convertValue<T>(y);
199
200 for (size_t j = 0; j < channels; ++j) {
201 buffer[i*channels + j] = yt / (j + 1);
202 }
203 }
204 return std::make_pair(buffer, frames);
205}
206
207/* This derived class creates a buffer provider of datatype T,
208 * consisting of an input signal, e.g. from createChirp().
209 * The number of frames can be obtained from the base class
210 * TestProvider::getNumFrames().
211 */
212template <typename T>
213class SignalProvider : public TestProvider {
214public:
215 SignalProvider(const std::pair<T*, size_t>& bufferInfo, size_t channels,
216 const std::vector<size_t>& values)
217 : TestProvider(bufferInfo.first, bufferInfo.second, channels * sizeof(T), values),
218 mManagedPtr(bufferInfo.first)
219 {
220 }
221
222 virtual ~SignalProvider()
223 {
224 delete[] mManagedPtr;
225 }
226
227protected:
228 T* mManagedPtr;
229};
230
231void resample(void *output, size_t outputFrames, const std::vector<size_t> &outputIncr,
232 android::AudioBufferProvider *provider, android::AudioResampler *resampler)
233{
234 for (size_t i = 0, j = 0; i < outputFrames; ) {
235 size_t thisFrames = outputIncr[j++];
236 if (j >= outputIncr.size()) {
237 j = 0;
238 }
239 if (thisFrames == 0 || thisFrames > outputFrames - i) {
240 thisFrames = outputFrames - i;
241 }
242 resampler->resample((int32_t*) output + 2*i, thisFrames, provider);
243 i += thisFrames;
244 }
245}
246
247void buffercmp(const void *reference, const void *test,
248 size_t outputFrameSize, size_t outputFrames)
249{
250 for (size_t i = 0; i < outputFrames; ++i) {
251 int check = memcmp((const char*)reference + i * outputFrameSize,
252 (const char*)test + i * outputFrameSize, outputFrameSize);
253 if (check) {
254 ALOGE("Failure at frame %d", i);
255 ASSERT_EQ(check, 0); /* fails */
256 }
257 }
258}
259
260void testBufferIncrement(size_t channels, unsigned inputFreq, unsigned outputFreq,
261 enum android::AudioResampler::src_quality quality)
262{
263 // create the provider
264 std::vector<size_t> inputIncr;
265 SignalProvider<int16_t> provider(createChirp<int16_t>(channels,
266 0., outputFreq/2., outputFreq, outputFreq/2000.),
267 channels, inputIncr);
268
269 // calculate the output size
270 size_t outputFrames = ((int64_t) provider.getNumFrames() * outputFreq) / inputFreq;
271 size_t outputFrameSize = 2 * sizeof(int32_t);
272 size_t outputSize = outputFrameSize * outputFrames;
273 outputSize &= ~7;
274
275 // create the resampler
276 const int volumePrecision = 12; /* typical unity gain */
277 android::AudioResampler* resampler;
278
279 resampler = android::AudioResampler::create(16, channels, outputFreq, quality);
280 resampler->setSampleRate(inputFreq);
281 resampler->setVolume(1 << volumePrecision, 1 << volumePrecision);
282
283 // set up the reference run
284 std::vector<size_t> refIncr;
285 refIncr.push_back(outputFrames);
286 void* reference = malloc(outputSize);
287 resample(reference, outputFrames, refIncr, &provider, resampler);
288
289 provider.reset();
290
291#if 0
292 /* this test will fail - API interface issue: reset() does not clear internal buffers */
293 resampler->reset();
294#else
295 delete resampler;
296 resampler = android::AudioResampler::create(16, channels, outputFreq, quality);
297 resampler->setSampleRate(inputFreq);
298 resampler->setVolume(1 << volumePrecision, 1 << volumePrecision);
299#endif
300
301 // set up the test run
302 std::vector<size_t> outIncr;
303 outIncr.push_back(1);
304 outIncr.push_back(2);
305 outIncr.push_back(3);
306 void* test = malloc(outputSize);
307 resample(test, outputFrames, outIncr, &provider, resampler);
308
309 // check
310 buffercmp(reference, test, outputFrameSize, outputFrames);
311
312 free(reference);
313 free(test);
314 delete resampler;
315}
316
317template <typename T>
318inline double sqr(T v)
319{
320 double dv = static_cast<double>(v);
321 return dv * dv;
322}
323
324template <typename T>
325double signalEnergy(T *start, T *end, unsigned stride)
326{
327 double accum = 0;
328
329 for (T *p = start; p < end; p += stride) {
330 accum += sqr(*p);
331 }
332 unsigned count = (end - start + stride - 1) / stride;
333 return accum / count;
334}
335
336void testStopbandDownconversion(size_t channels,
337 unsigned inputFreq, unsigned outputFreq,
338 unsigned passband, unsigned stopband,
339 enum android::AudioResampler::src_quality quality)
340{
341 // create the provider
342 std::vector<size_t> inputIncr;
343 SignalProvider<int16_t> provider(createChirp<int16_t>(channels,
344 0., inputFreq/2., inputFreq, inputFreq/2000.),
345 channels, inputIncr);
346
347 // calculate the output size
348 size_t outputFrames = ((int64_t) provider.getNumFrames() * outputFreq) / inputFreq;
349 size_t outputFrameSize = 2 * sizeof(int32_t);
350 size_t outputSize = outputFrameSize * outputFrames;
351 outputSize &= ~7;
352
353 // create the resampler
354 const int volumePrecision = 12; /* typical unity gain */
355 android::AudioResampler* resampler;
356
357 resampler = android::AudioResampler::create(16, channels, outputFreq, quality);
358 resampler->setSampleRate(inputFreq);
359 resampler->setVolume(1 << volumePrecision, 1 << volumePrecision);
360
361 // set up the reference run
362 std::vector<size_t> refIncr;
363 refIncr.push_back(outputFrames);
364 void* reference = malloc(outputSize);
365 resample(reference, outputFrames, refIncr, &provider, resampler);
366
367 int32_t *out = reinterpret_cast<int32_t *>(reference);
368
369 // check signal energy in passband
370 const unsigned passbandFrame = passband * outputFreq / 1000.;
371 const unsigned stopbandFrame = stopband * outputFreq / 1000.;
372
373 // check each channel separately
374 for (size_t i = 0; i < channels; ++i) {
375 double passbandEnergy = signalEnergy(out, out + passbandFrame * channels, channels);
376 double stopbandEnergy = signalEnergy(out + stopbandFrame * channels,
377 out + outputFrames * channels, channels);
378 double dbAtten = -10. * log10(stopbandEnergy / passbandEnergy);
379 ASSERT_GT(dbAtten, 60.);
380
381#if 0
382 // internal verification
383 printf("if:%d of:%d pbf:%d sbf:%d sbe: %f pbe: %f db: %.2f\n",
384 provider.getNumFrames(), outputFrames,
385 passbandFrame, stopbandFrame, stopbandEnergy, passbandEnergy, dbAtten);
386 for (size_t i = 0; i < 10; ++i) {
387 printf("%d\n", out[i+passbandFrame*channels]);
388 }
389 for (size_t i = 0; i < 10; ++i) {
390 printf("%d\n", out[i+stopbandFrame*channels]);
391 }
392#endif
393 }
394
395 free(reference);
396 delete resampler;
397}
398
399/* Buffer increment test
400 *
401 * We compare a reference output, where we consume and process the entire
402 * buffer at a time, and a test output, where we provide small chunks of input
403 * data and process small chunks of output (which may not be equivalent in size).
404 *
405 * Two subtests - fixed phase (3:2 down) and interpolated phase (147:320 up)
406 */
407TEST(audioflinger_resampler, bufferincrement_fixedphase) {
408 // all of these work
409 static const enum android::AudioResampler::src_quality kQualityArray[] = {
410 android::AudioResampler::LOW_QUALITY,
411 android::AudioResampler::MED_QUALITY,
412 android::AudioResampler::HIGH_QUALITY,
413 android::AudioResampler::VERY_HIGH_QUALITY,
414 android::AudioResampler::DYN_LOW_QUALITY,
415 android::AudioResampler::DYN_MED_QUALITY,
416 android::AudioResampler::DYN_HIGH_QUALITY,
417 };
418
419 for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
420 testBufferIncrement(2, 48000, 32000, kQualityArray[i]);
421 }
422}
423
424TEST(audioflinger_resampler, bufferincrement_interpolatedphase) {
425 // all of these work except low quality
426 static const enum android::AudioResampler::src_quality kQualityArray[] = {
427// android::AudioResampler::LOW_QUALITY,
428 android::AudioResampler::MED_QUALITY,
429 android::AudioResampler::HIGH_QUALITY,
430 android::AudioResampler::VERY_HIGH_QUALITY,
431 android::AudioResampler::DYN_LOW_QUALITY,
432 android::AudioResampler::DYN_MED_QUALITY,
433 android::AudioResampler::DYN_HIGH_QUALITY,
434 };
435
436 for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
437 testBufferIncrement(2, 22050, 48000, kQualityArray[i]);
438 }
439}
440
441/* Simple aliasing test
442 *
443 * This checks stopband response of the chirp signal to make sure frequencies
444 * are properly suppressed. It uses downsampling because the stopband can be
445 * clearly isolated by input frequencies exceeding the output sample rate (nyquist).
446 */
447TEST(audioflinger_resampler, stopbandresponse) {
448 // not all of these may work (old resamplers fail on downsampling)
449 static const enum android::AudioResampler::src_quality kQualityArray[] = {
450 //android::AudioResampler::LOW_QUALITY,
451 //android::AudioResampler::MED_QUALITY,
452 //android::AudioResampler::HIGH_QUALITY,
453 //android::AudioResampler::VERY_HIGH_QUALITY,
454 android::AudioResampler::DYN_LOW_QUALITY,
455 android::AudioResampler::DYN_MED_QUALITY,
456 android::AudioResampler::DYN_HIGH_QUALITY,
457 };
458
459 // in this test we assume a maximum transition band between 12kHz and 20kHz.
460 // there must be at least 60dB relative attenuation between stopband and passband.
461 for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
462 testStopbandDownconversion(2, 48000, 32000, 12000, 20000, kQualityArray[i]);
463 }
464
465 // in this test we assume a maximum transition band between 7kHz and 15kHz.
466 // there must be at least 60dB relative attenuation between stopband and passband.
467 // (the weird ratio triggers interpolative resampling)
468 for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
469 testStopbandDownconversion(2, 48000, 22101, 7000, 15000, kQualityArray[i]);
470 }
471}