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