resampler_tests.cpp revision 546734b3ab577d46afe863515104a062e88a109b
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 <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} 472