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