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