resampler_tests.cpp revision 3348e36c51e91e78020bcc6578eda83d97c31bec
1326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams/* 2326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams * Copyright (C) 2014 The Android Open Source Project 3326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams * 4326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams * Licensed under the Apache License, Version 2.0 (the "License"); 5326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams * you may not use this file except in compliance with the License. 6326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams * You may obtain a copy of the License at 7326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams * 8326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams * http://www.apache.org/licenses/LICENSE-2.0 9326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams * 10326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams * Unless required by applicable law or agreed to in writing, software 11326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams * distributed under the License is distributed on an "AS IS" BASIS, 12326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams * See the License for the specific language governing permissions and 14326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams * limitations under the License. 15326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams */ 16326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams 17326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams//#define LOG_NDEBUG 0 18326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams#define LOG_TAG "audioflinger_resampler_tests" 19326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams 20326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams#include <unistd.h> 21326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams#include <stdio.h> 22326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams#include <stdlib.h> 23326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams#include <fcntl.h> 24326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams#include <string.h> 25326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams#include <sys/mman.h> 265c3e3bc8af6de6be5e6bd68e1d5168496f99e6cfJason Sams#include <sys/stat.h> 27326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams#include <errno.h> 28326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams#include <time.h> 29326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams#include <math.h> 30326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams#include <vector> 31326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams#include <utility> 32326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams#include <cutils/log.h> 33326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams#include <gtest/gtest.h> 34326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams#include <media/AudioBufferProvider.h> 35e514b45de8561fbc6ef6770845102ca10b0a69d7Jason Sams#include "AudioResampler.h" 36fa84da2cbc271f855b3b1ec75bb688abdf1d1d01Jason Sams#include "test_utils.h" 37fa84da2cbc271f855b3b1ec75bb688abdf1d1d01Jason Sams 38326e0ddf89e8df2837752fbfd7a014814b32082cJason Samsvoid resample(int channels, void *output, 39326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams size_t outputFrames, const std::vector<size_t> &outputIncr, 40326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams android::AudioBufferProvider *provider, android::AudioResampler *resampler) 41326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams{ 42326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams for (size_t i = 0, j = 0; i < outputFrames; ) { 43326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams size_t thisFrames = outputIncr[j++]; 44326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams if (j >= outputIncr.size()) { 45326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams j = 0; 46326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams } 47326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams if (thisFrames == 0 || thisFrames > outputFrames - i) { 48326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams thisFrames = outputFrames - i; 49326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams } 507fabe1a3bf8de37d86021bb7f744c791db81aed3Jason Sams resampler->resample((int32_t*) output + channels*i, thisFrames, provider); 51cf4c7c9b2f513be77a5b9853319ca82ac2b128edJason Sams i += thisFrames; 52326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams } 53326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams} 54cf4c7c9b2f513be77a5b9853319ca82ac2b128edJason Sams 55cf4c7c9b2f513be77a5b9853319ca82ac2b128edJason Samsvoid buffercmp(const void *reference, const void *test, 56326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams size_t outputFrameSize, size_t outputFrames) 57326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams{ 58326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams for (size_t i = 0; i < outputFrames; ++i) { 599397e30ce5fe3f6af9212a93b490836b04fdfffaJason Sams int check = memcmp((const char*)reference + i * outputFrameSize, 609397e30ce5fe3f6af9212a93b490836b04fdfffaJason Sams (const char*)test + i * outputFrameSize, outputFrameSize); 61e5ffb879ae535a899a486285a23bea05e912480fJason Sams if (check) { 629397e30ce5fe3f6af9212a93b490836b04fdfffaJason Sams ALOGE("Failure at frame %d", i); 63326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams ASSERT_EQ(check, 0); /* fails */ 649397e30ce5fe3f6af9212a93b490836b04fdfffaJason Sams } 65326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams } 66e579df42e85d9e00f53c42ef1b78dbd209dba989Jason Sams} 67e579df42e85d9e00f53c42ef1b78dbd209dba989Jason Sams 68e5ffb879ae535a899a486285a23bea05e912480fJason Samsvoid testBufferIncrement(size_t channels, bool useFloat, 69e5ffb879ae535a899a486285a23bea05e912480fJason Sams unsigned inputFreq, unsigned outputFreq, 70e5ffb879ae535a899a486285a23bea05e912480fJason Sams enum android::AudioResampler::src_quality quality) 715c3e3bc8af6de6be5e6bd68e1d5168496f99e6cfJason Sams{ 725c3e3bc8af6de6be5e6bd68e1d5168496f99e6cfJason Sams const audio_format_t format = useFloat ? AUDIO_FORMAT_PCM_FLOAT : AUDIO_FORMAT_PCM_16_BIT; 73e5ffb879ae535a899a486285a23bea05e912480fJason Sams // create the provider 743dd429cc32388ca0c3d7a9368ed2e348b8fdaab1Jason Sams std::vector<int> inputIncr; 75e5ffb879ae535a899a486285a23bea05e912480fJason Sams SignalProvider provider; 76cf4c7c9b2f513be77a5b9853319ca82ac2b128edJason Sams if (useFloat) { 77cf4c7c9b2f513be77a5b9853319ca82ac2b128edJason Sams provider.setChirp<float>(channels, 78326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams 0., outputFreq/2., outputFreq, outputFreq/2000.); 795c3e3bc8af6de6be5e6bd68e1d5168496f99e6cfJason Sams } else { 805c3e3bc8af6de6be5e6bd68e1d5168496f99e6cfJason Sams provider.setChirp<int16_t>(channels, 81326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams 0., outputFreq/2., outputFreq, outputFreq/2000.); 82326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams } 83326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams provider.setIncr(inputIncr); 845c3e3bc8af6de6be5e6bd68e1d5168496f99e6cfJason Sams 855c3e3bc8af6de6be5e6bd68e1d5168496f99e6cfJason Sams // calculate the output size 86fa84da2cbc271f855b3b1ec75bb688abdf1d1d01Jason Sams size_t outputFrames = ((int64_t) provider.getNumFrames() * outputFreq) / inputFreq; 87fa84da2cbc271f855b3b1ec75bb688abdf1d1d01Jason Sams size_t outputFrameSize = channels * (useFloat ? sizeof(float) : sizeof(int32_t)); 88fa84da2cbc271f855b3b1ec75bb688abdf1d1d01Jason Sams size_t outputSize = outputFrameSize * outputFrames; 89fa84da2cbc271f855b3b1ec75bb688abdf1d1d01Jason Sams outputSize &= ~7; 90fa84da2cbc271f855b3b1ec75bb688abdf1d1d01Jason Sams 91326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams // create the resampler 92326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams const int volumePrecision = 12; /* typical unity gain */ 93326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams android::AudioResampler* resampler; 94326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams 95326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams resampler = android::AudioResampler::create(format, channels, outputFreq, quality); 96326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams resampler->setSampleRate(inputFreq); 97326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams resampler->setVolume(1 << volumePrecision, 1 << volumePrecision); 98326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams 99326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams // set up the reference run 100326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams std::vector<size_t> refIncr; 101326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams refIncr.push_back(outputFrames); 102326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams void* reference = malloc(outputSize); 103326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams resample(channels, reference, outputFrames, refIncr, &provider, resampler); 104326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams 105326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams provider.reset(); 106326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams 1077fabe1a3bf8de37d86021bb7f744c791db81aed3Jason Sams#if 0 108cf4c7c9b2f513be77a5b9853319ca82ac2b128edJason Sams /* this test will fail - API interface issue: reset() does not clear internal buffers */ 109326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams resampler->reset(); 110326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams#else 111326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams delete resampler; 112326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams resampler = android::AudioResampler::create(format, channels, outputFreq, quality); 113326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams resampler->setSampleRate(inputFreq); 114326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams resampler->setVolume(1 << volumePrecision, 1 << volumePrecision); 115326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams#endif 116cf4c7c9b2f513be77a5b9853319ca82ac2b128edJason Sams 117cf4c7c9b2f513be77a5b9853319ca82ac2b128edJason Sams // set up the test run 118fa84da2cbc271f855b3b1ec75bb688abdf1d1d01Jason Sams std::vector<size_t> outIncr; 119fa84da2cbc271f855b3b1ec75bb688abdf1d1d01Jason Sams outIncr.push_back(1); 120fa84da2cbc271f855b3b1ec75bb688abdf1d1d01Jason Sams outIncr.push_back(2); 121fa84da2cbc271f855b3b1ec75bb688abdf1d1d01Jason Sams outIncr.push_back(3); 122326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams void* test = malloc(outputSize); 123326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams inputIncr.push_back(1); 124326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams inputIncr.push_back(3); 125326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams provider.setIncr(inputIncr); 126326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams resample(channels, test, outputFrames, outIncr, &provider, resampler); 127326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams 128 // check 129 buffercmp(reference, test, outputFrameSize, outputFrames); 130 131 free(reference); 132 free(test); 133 delete resampler; 134} 135 136template <typename T> 137inline double sqr(T v) 138{ 139 double dv = static_cast<double>(v); 140 return dv * dv; 141} 142 143template <typename T> 144double signalEnergy(T *start, T *end, unsigned stride) 145{ 146 double accum = 0; 147 148 for (T *p = start; p < end; p += stride) { 149 accum += sqr(*p); 150 } 151 unsigned count = (end - start + stride - 1) / stride; 152 return accum / count; 153} 154 155void testStopbandDownconversion(size_t channels, 156 unsigned inputFreq, unsigned outputFreq, 157 unsigned passband, unsigned stopband, 158 enum android::AudioResampler::src_quality quality) 159{ 160 // create the provider 161 std::vector<int> inputIncr; 162 SignalProvider provider; 163 provider.setChirp<int16_t>(channels, 164 0., inputFreq/2., inputFreq, inputFreq/2000.); 165 provider.setIncr(inputIncr); 166 167 // calculate the output size 168 size_t outputFrames = ((int64_t) provider.getNumFrames() * outputFreq) / inputFreq; 169 size_t outputFrameSize = channels * sizeof(int32_t); 170 size_t outputSize = outputFrameSize * outputFrames; 171 outputSize &= ~7; 172 173 // create the resampler 174 const int volumePrecision = 12; /* typical unity gain */ 175 android::AudioResampler* resampler; 176 177 resampler = android::AudioResampler::create(AUDIO_FORMAT_PCM_16_BIT, 178 channels, outputFreq, quality); 179 resampler->setSampleRate(inputFreq); 180 resampler->setVolume(1 << volumePrecision, 1 << volumePrecision); 181 182 // set up the reference run 183 std::vector<size_t> refIncr; 184 refIncr.push_back(outputFrames); 185 void* reference = malloc(outputSize); 186 resample(channels, reference, outputFrames, refIncr, &provider, resampler); 187 188 int32_t *out = reinterpret_cast<int32_t *>(reference); 189 190 // check signal energy in passband 191 const unsigned passbandFrame = passband * outputFreq / 1000.; 192 const unsigned stopbandFrame = stopband * outputFreq / 1000.; 193 194 // check each channel separately 195 for (size_t i = 0; i < channels; ++i) { 196 double passbandEnergy = signalEnergy(out, out + passbandFrame * channels, channels); 197 double stopbandEnergy = signalEnergy(out + stopbandFrame * channels, 198 out + outputFrames * channels, channels); 199 double dbAtten = -10. * log10(stopbandEnergy / passbandEnergy); 200 ASSERT_GT(dbAtten, 60.); 201 202#if 0 203 // internal verification 204 printf("if:%d of:%d pbf:%d sbf:%d sbe: %f pbe: %f db: %.2f\n", 205 provider.getNumFrames(), outputFrames, 206 passbandFrame, stopbandFrame, stopbandEnergy, passbandEnergy, dbAtten); 207 for (size_t i = 0; i < 10; ++i) { 208 printf("%d\n", out[i+passbandFrame*channels]); 209 } 210 for (size_t i = 0; i < 10; ++i) { 211 printf("%d\n", out[i+stopbandFrame*channels]); 212 } 213#endif 214 } 215 216 free(reference); 217 delete resampler; 218} 219 220/* Buffer increment test 221 * 222 * We compare a reference output, where we consume and process the entire 223 * buffer at a time, and a test output, where we provide small chunks of input 224 * data and process small chunks of output (which may not be equivalent in size). 225 * 226 * Two subtests - fixed phase (3:2 down) and interpolated phase (147:320 up) 227 */ 228TEST(audioflinger_resampler, bufferincrement_fixedphase) { 229 // all of these work 230 static const enum android::AudioResampler::src_quality kQualityArray[] = { 231 android::AudioResampler::LOW_QUALITY, 232 android::AudioResampler::MED_QUALITY, 233 android::AudioResampler::HIGH_QUALITY, 234 android::AudioResampler::VERY_HIGH_QUALITY, 235 android::AudioResampler::DYN_LOW_QUALITY, 236 android::AudioResampler::DYN_MED_QUALITY, 237 android::AudioResampler::DYN_HIGH_QUALITY, 238 }; 239 240 for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) { 241 testBufferIncrement(2, false, 48000, 32000, kQualityArray[i]); 242 } 243} 244 245TEST(audioflinger_resampler, bufferincrement_interpolatedphase) { 246 // all of these work except low quality 247 static const enum android::AudioResampler::src_quality kQualityArray[] = { 248// android::AudioResampler::LOW_QUALITY, 249 android::AudioResampler::MED_QUALITY, 250 android::AudioResampler::HIGH_QUALITY, 251 android::AudioResampler::VERY_HIGH_QUALITY, 252 android::AudioResampler::DYN_LOW_QUALITY, 253 android::AudioResampler::DYN_MED_QUALITY, 254 android::AudioResampler::DYN_HIGH_QUALITY, 255 }; 256 257 for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) { 258 testBufferIncrement(2, false, 22050, 48000, kQualityArray[i]); 259 } 260} 261 262TEST(audioflinger_resampler, bufferincrement_fixedphase_multi) { 263 // only dynamic quality 264 static const enum android::AudioResampler::src_quality kQualityArray[] = { 265 android::AudioResampler::DYN_LOW_QUALITY, 266 android::AudioResampler::DYN_MED_QUALITY, 267 android::AudioResampler::DYN_HIGH_QUALITY, 268 }; 269 270 for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) { 271 testBufferIncrement(4, false, 48000, 32000, kQualityArray[i]); 272 } 273} 274 275TEST(audioflinger_resampler, bufferincrement_interpolatedphase_multi_float) { 276 // only dynamic quality 277 static const enum android::AudioResampler::src_quality kQualityArray[] = { 278 android::AudioResampler::DYN_LOW_QUALITY, 279 android::AudioResampler::DYN_MED_QUALITY, 280 android::AudioResampler::DYN_HIGH_QUALITY, 281 }; 282 283 for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) { 284 testBufferIncrement(8, true, 22050, 48000, kQualityArray[i]); 285 } 286} 287 288/* Simple aliasing test 289 * 290 * This checks stopband response of the chirp signal to make sure frequencies 291 * are properly suppressed. It uses downsampling because the stopband can be 292 * clearly isolated by input frequencies exceeding the output sample rate (nyquist). 293 */ 294TEST(audioflinger_resampler, stopbandresponse) { 295 // not all of these may work (old resamplers fail on downsampling) 296 static const enum android::AudioResampler::src_quality kQualityArray[] = { 297 //android::AudioResampler::LOW_QUALITY, 298 //android::AudioResampler::MED_QUALITY, 299 //android::AudioResampler::HIGH_QUALITY, 300 //android::AudioResampler::VERY_HIGH_QUALITY, 301 android::AudioResampler::DYN_LOW_QUALITY, 302 android::AudioResampler::DYN_MED_QUALITY, 303 android::AudioResampler::DYN_HIGH_QUALITY, 304 }; 305 306 // in this test we assume a maximum transition band between 12kHz and 20kHz. 307 // there must be at least 60dB relative attenuation between stopband and passband. 308 for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) { 309 testStopbandDownconversion(2, 48000, 32000, 12000, 20000, kQualityArray[i]); 310 } 311 312 // in this test we assume a maximum transition band between 7kHz and 15kHz. 313 // there must be at least 60dB relative attenuation between stopband and passband. 314 // (the weird ratio triggers interpolative resampling) 315 for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) { 316 testStopbandDownconversion(2, 48000, 22101, 7000, 15000, kQualityArray[i]); 317 } 318} 319