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 <errno.h> 21#include <fcntl.h> 22#include <math.h> 23#include <stdio.h> 24#include <stdlib.h> 25#include <string.h> 26#include <sys/mman.h> 27#include <sys/stat.h> 28#include <time.h> 29#include <unistd.h> 30 31#include <iostream> 32#include <memory> 33#include <utility> 34#include <vector> 35 36#include <gtest/gtest.h> 37#include <log/log.h> 38#include <media/AudioBufferProvider.h> 39 40#include <media/AudioResampler.h> 41#include "../AudioResamplerDyn.h" 42#include "../AudioResamplerFirGen.h" 43#include "test_utils.h" 44 45template <typename T> 46static void printData(T *data, size_t size) { 47 const size_t stride = 8; 48 for (size_t i = 0; i < size; ) { 49 for (size_t j = 0; j < stride && i < size; ++j) { 50 std::cout << data[i++] << ' '; // extra space before newline 51 } 52 std::cout << '\n'; // or endl 53 } 54} 55 56void resample(int channels, void *output, 57 size_t outputFrames, const std::vector<size_t> &outputIncr, 58 android::AudioBufferProvider *provider, android::AudioResampler *resampler) 59{ 60 for (size_t i = 0, j = 0; i < outputFrames; ) { 61 size_t thisFrames = outputIncr[j++]; 62 if (j >= outputIncr.size()) { 63 j = 0; 64 } 65 if (thisFrames == 0 || thisFrames > outputFrames - i) { 66 thisFrames = outputFrames - i; 67 } 68 size_t framesResampled = resampler->resample( 69 (int32_t*) output + channels*i, thisFrames, provider); 70 // we should have enough buffer space, so there is no short count. 71 ASSERT_EQ(thisFrames, framesResampled); 72 i += thisFrames; 73 } 74} 75 76void buffercmp(const void *reference, const void *test, 77 size_t outputFrameSize, size_t outputFrames) 78{ 79 for (size_t i = 0; i < outputFrames; ++i) { 80 int check = memcmp((const char*)reference + i * outputFrameSize, 81 (const char*)test + i * outputFrameSize, outputFrameSize); 82 if (check) { 83 ALOGE("Failure at frame %zu", i); 84 ASSERT_EQ(check, 0); /* fails */ 85 } 86 } 87} 88 89void testBufferIncrement(size_t channels, bool useFloat, 90 unsigned inputFreq, unsigned outputFreq, 91 enum android::AudioResampler::src_quality quality) 92{ 93 const audio_format_t format = useFloat ? AUDIO_FORMAT_PCM_FLOAT : AUDIO_FORMAT_PCM_16_BIT; 94 // create the provider 95 std::vector<int> inputIncr; 96 SignalProvider provider; 97 if (useFloat) { 98 provider.setChirp<float>(channels, 99 0., outputFreq/2., outputFreq, outputFreq/2000.); 100 } else { 101 provider.setChirp<int16_t>(channels, 102 0., outputFreq/2., outputFreq, outputFreq/2000.); 103 } 104 provider.setIncr(inputIncr); 105 106 // calculate the output size 107 size_t outputFrames = ((int64_t) provider.getNumFrames() * outputFreq) / inputFreq; 108 size_t outputFrameSize = (channels == 1 ? 2 : channels) * (useFloat ? sizeof(float) : sizeof(int32_t)); 109 size_t outputSize = outputFrameSize * outputFrames; 110 outputSize &= ~7; 111 112 // create the resampler 113 android::AudioResampler* resampler; 114 115 resampler = android::AudioResampler::create(format, channels, outputFreq, quality); 116 resampler->setSampleRate(inputFreq); 117 resampler->setVolume(android::AudioResampler::UNITY_GAIN_FLOAT, 118 android::AudioResampler::UNITY_GAIN_FLOAT); 119 120 // set up the reference run 121 std::vector<size_t> refIncr; 122 refIncr.push_back(outputFrames); 123 void* reference = calloc(outputFrames, outputFrameSize); 124 resample(channels, reference, outputFrames, refIncr, &provider, resampler); 125 126 provider.reset(); 127 128#if 0 129 /* this test will fail - API interface issue: reset() does not clear internal buffers */ 130 resampler->reset(); 131#else 132 delete resampler; 133 resampler = android::AudioResampler::create(format, channels, outputFreq, quality); 134 resampler->setSampleRate(inputFreq); 135 resampler->setVolume(android::AudioResampler::UNITY_GAIN_FLOAT, 136 android::AudioResampler::UNITY_GAIN_FLOAT); 137#endif 138 139 // set up the test run 140 std::vector<size_t> outIncr; 141 outIncr.push_back(1); 142 outIncr.push_back(2); 143 outIncr.push_back(3); 144 void* test = calloc(outputFrames, outputFrameSize); 145 inputIncr.push_back(1); 146 inputIncr.push_back(3); 147 provider.setIncr(inputIncr); 148 resample(channels, test, outputFrames, outIncr, &provider, resampler); 149 150 // check 151 buffercmp(reference, test, outputFrameSize, outputFrames); 152 153 free(reference); 154 free(test); 155 delete resampler; 156} 157 158template <typename T> 159inline double sqr(T v) 160{ 161 double dv = static_cast<double>(v); 162 return dv * dv; 163} 164 165template <typename T> 166double signalEnergy(T *start, T *end, unsigned stride) 167{ 168 double accum = 0; 169 170 for (T *p = start; p < end; p += stride) { 171 accum += sqr(*p); 172 } 173 unsigned count = (end - start + stride - 1) / stride; 174 return accum / count; 175} 176 177// TI = resampler input type, int16_t or float 178// TO = resampler output type, int32_t or float 179template <typename TI, typename TO> 180void testStopbandDownconversion(size_t channels, 181 unsigned inputFreq, unsigned outputFreq, 182 unsigned passband, unsigned stopband, 183 enum android::AudioResampler::src_quality quality) 184{ 185 // create the provider 186 std::vector<int> inputIncr; 187 SignalProvider provider; 188 provider.setChirp<TI>(channels, 189 0., inputFreq/2., inputFreq, inputFreq/2000.); 190 provider.setIncr(inputIncr); 191 192 // calculate the output size 193 size_t outputFrames = ((int64_t) provider.getNumFrames() * outputFreq) / inputFreq; 194 size_t outputFrameSize = (channels == 1 ? 2 : channels) * sizeof(TO); 195 size_t outputSize = outputFrameSize * outputFrames; 196 outputSize &= ~7; 197 198 // create the resampler 199 android::AudioResampler* resampler; 200 201 resampler = android::AudioResampler::create( 202 is_same<TI, int16_t>::value ? AUDIO_FORMAT_PCM_16_BIT : AUDIO_FORMAT_PCM_FLOAT, 203 channels, outputFreq, quality); 204 resampler->setSampleRate(inputFreq); 205 resampler->setVolume(android::AudioResampler::UNITY_GAIN_FLOAT, 206 android::AudioResampler::UNITY_GAIN_FLOAT); 207 208 // set up the reference run 209 std::vector<size_t> refIncr; 210 refIncr.push_back(outputFrames); 211 void* reference = calloc(outputFrames, outputFrameSize); 212 resample(channels, reference, outputFrames, refIncr, &provider, resampler); 213 214 TO *out = reinterpret_cast<TO *>(reference); 215 216 // check signal energy in passband 217 const unsigned passbandFrame = passband * outputFreq / 1000.; 218 const unsigned stopbandFrame = stopband * outputFreq / 1000.; 219 220 // check each channel separately 221 if (channels == 1) channels = 2; // workaround (mono duplicates output channel) 222 223 for (size_t i = 0; i < channels; ++i) { 224 double passbandEnergy = signalEnergy(out, out + passbandFrame * channels, channels); 225 double stopbandEnergy = signalEnergy(out + stopbandFrame * channels, 226 out + outputFrames * channels, channels); 227 double dbAtten = -10. * log10(stopbandEnergy / passbandEnergy); 228 ASSERT_GT(dbAtten, 60.); 229 230#if 0 231 // internal verification 232 printf("if:%d of:%d pbf:%d sbf:%d sbe: %f pbe: %f db: %.2f\n", 233 provider.getNumFrames(), outputFrames, 234 passbandFrame, stopbandFrame, stopbandEnergy, passbandEnergy, dbAtten); 235 for (size_t i = 0; i < 10; ++i) { 236 std::cout << out[i+passbandFrame*channels] << std::endl; 237 } 238 for (size_t i = 0; i < 10; ++i) { 239 std::cout << out[i+stopbandFrame*channels] << std::endl; 240 } 241#endif 242 } 243 244 free(reference); 245 delete resampler; 246} 247 248void testFilterResponse( 249 size_t channels, unsigned inputFreq, unsigned outputFreq) 250{ 251 // create resampler 252 using ResamplerType = android::AudioResamplerDyn<float, float, float>; 253 std::unique_ptr<ResamplerType> rdyn( 254 static_cast<ResamplerType *>( 255 android::AudioResampler::create( 256 AUDIO_FORMAT_PCM_FLOAT, 257 channels, 258 outputFreq, 259 android::AudioResampler::DYN_HIGH_QUALITY))); 260 rdyn->setSampleRate(inputFreq); 261 262 // get design parameters 263 const int phases = rdyn->getPhases(); 264 const int halfLength = rdyn->getHalfLength(); 265 const float *coefs = rdyn->getFilterCoefs(); 266 const double fcr = rdyn->getNormalizedCutoffFrequency(); 267 const double tbw = rdyn->getNormalizedTransitionBandwidth(); 268 const double attenuation = rdyn->getFilterAttenuation(); 269 const double stopbandDb = rdyn->getStopbandAttenuationDb(); 270 const double passbandDb = rdyn->getPassbandRippleDb(); 271 const double fp = fcr - tbw / 2; 272 const double fs = fcr + tbw / 2; 273 274 printf("inputFreq:%d outputFreq:%d design" 275 " phases:%d halfLength:%d" 276 " fcr:%lf fp:%lf fs:%lf tbw:%lf" 277 " attenuation:%lf stopRipple:%.lf passRipple:%lf" 278 "\n", 279 inputFreq, outputFreq, 280 phases, halfLength, 281 fcr, fp, fs, tbw, 282 attenuation, stopbandDb, passbandDb); 283 284 // verify design parameters 285 constexpr int32_t passSteps = 1000; 286 double passMin, passMax, passRipple, stopMax, stopRipple; 287 android::testFir(coefs, phases, halfLength, fp / phases, fs / phases, 288 passSteps, phases * passSteps /* stopSteps */, 289 passMin, passMax, passRipple, 290 stopMax, stopRipple); 291 printf("inputFreq:%d outputFreq:%d verify" 292 " passMin:%lf passMax:%lf passRipple:%lf stopMax:%lf stopRipple:%lf" 293 "\n", 294 inputFreq, outputFreq, 295 passMin, passMax, passRipple, stopMax, stopRipple); 296 297 ASSERT_GT(stopRipple, 60.); // enough stopband attenuation 298 ASSERT_LT(passRipple, 0.2); // small passband ripple 299 ASSERT_GT(passMin, 0.99); // we do not attenuate the signal (ideally 1.) 300} 301 302/* Buffer increment test 303 * 304 * We compare a reference output, where we consume and process the entire 305 * buffer at a time, and a test output, where we provide small chunks of input 306 * data and process small chunks of output (which may not be equivalent in size). 307 * 308 * Two subtests - fixed phase (3:2 down) and interpolated phase (147:320 up) 309 */ 310TEST(audioflinger_resampler, bufferincrement_fixedphase) { 311 // all of these work 312 static const enum android::AudioResampler::src_quality kQualityArray[] = { 313 android::AudioResampler::LOW_QUALITY, 314 android::AudioResampler::MED_QUALITY, 315 android::AudioResampler::HIGH_QUALITY, 316 android::AudioResampler::VERY_HIGH_QUALITY, 317 android::AudioResampler::DYN_LOW_QUALITY, 318 android::AudioResampler::DYN_MED_QUALITY, 319 android::AudioResampler::DYN_HIGH_QUALITY, 320 }; 321 322 for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) { 323 testBufferIncrement(2, false, 48000, 32000, kQualityArray[i]); 324 } 325} 326 327TEST(audioflinger_resampler, bufferincrement_interpolatedphase) { 328 // all of these work except low quality 329 static const enum android::AudioResampler::src_quality kQualityArray[] = { 330// android::AudioResampler::LOW_QUALITY, 331 android::AudioResampler::MED_QUALITY, 332 android::AudioResampler::HIGH_QUALITY, 333 android::AudioResampler::VERY_HIGH_QUALITY, 334 android::AudioResampler::DYN_LOW_QUALITY, 335 android::AudioResampler::DYN_MED_QUALITY, 336 android::AudioResampler::DYN_HIGH_QUALITY, 337 }; 338 339 for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) { 340 testBufferIncrement(2, false, 22050, 48000, kQualityArray[i]); 341 } 342} 343 344TEST(audioflinger_resampler, bufferincrement_fixedphase_multi) { 345 // only dynamic quality 346 static const enum android::AudioResampler::src_quality kQualityArray[] = { 347 android::AudioResampler::DYN_LOW_QUALITY, 348 android::AudioResampler::DYN_MED_QUALITY, 349 android::AudioResampler::DYN_HIGH_QUALITY, 350 }; 351 352 for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) { 353 testBufferIncrement(4, false, 48000, 32000, kQualityArray[i]); 354 } 355} 356 357TEST(audioflinger_resampler, bufferincrement_interpolatedphase_multi_float) { 358 // only dynamic quality 359 static const enum android::AudioResampler::src_quality kQualityArray[] = { 360 android::AudioResampler::DYN_LOW_QUALITY, 361 android::AudioResampler::DYN_MED_QUALITY, 362 android::AudioResampler::DYN_HIGH_QUALITY, 363 }; 364 365 for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) { 366 testBufferIncrement(8, true, 22050, 48000, kQualityArray[i]); 367 } 368} 369 370/* Simple aliasing test 371 * 372 * This checks stopband response of the chirp signal to make sure frequencies 373 * are properly suppressed. It uses downsampling because the stopband can be 374 * clearly isolated by input frequencies exceeding the output sample rate (nyquist). 375 */ 376TEST(audioflinger_resampler, stopbandresponse_integer) { 377 // not all of these may work (old resamplers fail on downsampling) 378 static const enum android::AudioResampler::src_quality kQualityArray[] = { 379 //android::AudioResampler::LOW_QUALITY, 380 //android::AudioResampler::MED_QUALITY, 381 //android::AudioResampler::HIGH_QUALITY, 382 //android::AudioResampler::VERY_HIGH_QUALITY, 383 android::AudioResampler::DYN_LOW_QUALITY, 384 android::AudioResampler::DYN_MED_QUALITY, 385 android::AudioResampler::DYN_HIGH_QUALITY, 386 }; 387 388 // in this test we assume a maximum transition band between 12kHz and 20kHz. 389 // there must be at least 60dB relative attenuation between stopband and passband. 390 for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) { 391 testStopbandDownconversion<int16_t, int32_t>( 392 2, 48000, 32000, 12000, 20000, kQualityArray[i]); 393 } 394 395 // in this test we assume a maximum transition band between 7kHz and 15kHz. 396 // there must be at least 60dB relative attenuation between stopband and passband. 397 // (the weird ratio triggers interpolative resampling) 398 for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) { 399 testStopbandDownconversion<int16_t, int32_t>( 400 2, 48000, 22101, 7000, 15000, kQualityArray[i]); 401 } 402} 403 404TEST(audioflinger_resampler, stopbandresponse_integer_mono) { 405 // not all of these may work (old resamplers fail on downsampling) 406 static const enum android::AudioResampler::src_quality kQualityArray[] = { 407 //android::AudioResampler::LOW_QUALITY, 408 //android::AudioResampler::MED_QUALITY, 409 //android::AudioResampler::HIGH_QUALITY, 410 //android::AudioResampler::VERY_HIGH_QUALITY, 411 android::AudioResampler::DYN_LOW_QUALITY, 412 android::AudioResampler::DYN_MED_QUALITY, 413 android::AudioResampler::DYN_HIGH_QUALITY, 414 }; 415 416 // in this test we assume a maximum transition band between 12kHz and 20kHz. 417 // there must be at least 60dB relative attenuation between stopband and passband. 418 for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) { 419 testStopbandDownconversion<int16_t, int32_t>( 420 1, 48000, 32000, 12000, 20000, kQualityArray[i]); 421 } 422 423 // in this test we assume a maximum transition band between 7kHz and 15kHz. 424 // there must be at least 60dB relative attenuation between stopband and passband. 425 // (the weird ratio triggers interpolative resampling) 426 for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) { 427 testStopbandDownconversion<int16_t, int32_t>( 428 1, 48000, 22101, 7000, 15000, kQualityArray[i]); 429 } 430} 431 432TEST(audioflinger_resampler, stopbandresponse_integer_multichannel) { 433 // not all of these may work (old resamplers fail on downsampling) 434 static const enum android::AudioResampler::src_quality kQualityArray[] = { 435 //android::AudioResampler::LOW_QUALITY, 436 //android::AudioResampler::MED_QUALITY, 437 //android::AudioResampler::HIGH_QUALITY, 438 //android::AudioResampler::VERY_HIGH_QUALITY, 439 android::AudioResampler::DYN_LOW_QUALITY, 440 android::AudioResampler::DYN_MED_QUALITY, 441 android::AudioResampler::DYN_HIGH_QUALITY, 442 }; 443 444 // in this test we assume a maximum transition band between 12kHz and 20kHz. 445 // there must be at least 60dB relative attenuation between stopband and passband. 446 for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) { 447 testStopbandDownconversion<int16_t, int32_t>( 448 8, 48000, 32000, 12000, 20000, kQualityArray[i]); 449 } 450 451 // in this test we assume a maximum transition band between 7kHz and 15kHz. 452 // there must be at least 60dB relative attenuation between stopband and passband. 453 // (the weird ratio triggers interpolative resampling) 454 for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) { 455 testStopbandDownconversion<int16_t, int32_t>( 456 8, 48000, 22101, 7000, 15000, kQualityArray[i]); 457 } 458} 459 460TEST(audioflinger_resampler, stopbandresponse_float) { 461 // not all of these may work (old resamplers fail on downsampling) 462 static const enum android::AudioResampler::src_quality kQualityArray[] = { 463 //android::AudioResampler::LOW_QUALITY, 464 //android::AudioResampler::MED_QUALITY, 465 //android::AudioResampler::HIGH_QUALITY, 466 //android::AudioResampler::VERY_HIGH_QUALITY, 467 android::AudioResampler::DYN_LOW_QUALITY, 468 android::AudioResampler::DYN_MED_QUALITY, 469 android::AudioResampler::DYN_HIGH_QUALITY, 470 }; 471 472 // in this test we assume a maximum transition band between 12kHz and 20kHz. 473 // there must be at least 60dB relative attenuation between stopband and passband. 474 for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) { 475 testStopbandDownconversion<float, float>( 476 2, 48000, 32000, 12000, 20000, kQualityArray[i]); 477 } 478 479 // in this test we assume a maximum transition band between 7kHz and 15kHz. 480 // there must be at least 60dB relative attenuation between stopband and passband. 481 // (the weird ratio triggers interpolative resampling) 482 for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) { 483 testStopbandDownconversion<float, float>( 484 2, 48000, 22101, 7000, 15000, kQualityArray[i]); 485 } 486} 487 488TEST(audioflinger_resampler, stopbandresponse_float_mono) { 489 // not all of these may work (old resamplers fail on downsampling) 490 static const enum android::AudioResampler::src_quality kQualityArray[] = { 491 //android::AudioResampler::LOW_QUALITY, 492 //android::AudioResampler::MED_QUALITY, 493 //android::AudioResampler::HIGH_QUALITY, 494 //android::AudioResampler::VERY_HIGH_QUALITY, 495 android::AudioResampler::DYN_LOW_QUALITY, 496 android::AudioResampler::DYN_MED_QUALITY, 497 android::AudioResampler::DYN_HIGH_QUALITY, 498 }; 499 500 // in this test we assume a maximum transition band between 12kHz and 20kHz. 501 // there must be at least 60dB relative attenuation between stopband and passband. 502 for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) { 503 testStopbandDownconversion<float, float>( 504 1, 48000, 32000, 12000, 20000, kQualityArray[i]); 505 } 506 507 // in this test we assume a maximum transition band between 7kHz and 15kHz. 508 // there must be at least 60dB relative attenuation between stopband and passband. 509 // (the weird ratio triggers interpolative resampling) 510 for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) { 511 testStopbandDownconversion<float, float>( 512 1, 48000, 22101, 7000, 15000, kQualityArray[i]); 513 } 514} 515 516TEST(audioflinger_resampler, stopbandresponse_float_multichannel) { 517 // not all of these may work (old resamplers fail on downsampling) 518 static const enum android::AudioResampler::src_quality kQualityArray[] = { 519 //android::AudioResampler::LOW_QUALITY, 520 //android::AudioResampler::MED_QUALITY, 521 //android::AudioResampler::HIGH_QUALITY, 522 //android::AudioResampler::VERY_HIGH_QUALITY, 523 android::AudioResampler::DYN_LOW_QUALITY, 524 android::AudioResampler::DYN_MED_QUALITY, 525 android::AudioResampler::DYN_HIGH_QUALITY, 526 }; 527 528 // in this test we assume a maximum transition band between 12kHz and 20kHz. 529 // there must be at least 60dB relative attenuation between stopband and passband. 530 for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) { 531 testStopbandDownconversion<float, float>( 532 8, 48000, 32000, 12000, 20000, kQualityArray[i]); 533 } 534 535 // in this test we assume a maximum transition band between 7kHz and 15kHz. 536 // there must be at least 60dB relative attenuation between stopband and passband. 537 // (the weird ratio triggers interpolative resampling) 538 for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) { 539 testStopbandDownconversion<float, float>( 540 8, 48000, 22101, 7000, 15000, kQualityArray[i]); 541 } 542} 543 544TEST(audioflinger_resampler, filterresponse) { 545 std::vector<int> inSampleRates{ 546 8000, 547 11025, 548 12000, 549 16000, 550 22050, 551 24000, 552 32000, 553 44100, 554 48000, 555 88200, 556 96000, 557 176400, 558 192000, 559 }; 560 std::vector<int> outSampleRates{ 561 48000, 562 96000, 563 }; 564 565 for (int outSampleRate : outSampleRates) { 566 for (int inSampleRate : inSampleRates) { 567 testFilterResponse(2 /* channels */, inSampleRate, outSampleRate); 568 } 569 } 570} 571