resampler_tests.cpp revision c0e5ec8e2d8db15b97094374d0a248e041304b62
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 <cutils/log.h> 33#include <gtest/gtest.h> 34#include <media/AudioBufferProvider.h> 35#include "AudioResampler.h" 36#include "test_utils.h" 37 38void resample(void *output, size_t outputFrames, const std::vector<size_t> &outputIncr, 39 android::AudioBufferProvider *provider, android::AudioResampler *resampler) 40{ 41 for (size_t i = 0, j = 0; i < outputFrames; ) { 42 size_t thisFrames = outputIncr[j++]; 43 if (j >= outputIncr.size()) { 44 j = 0; 45 } 46 if (thisFrames == 0 || thisFrames > outputFrames - i) { 47 thisFrames = outputFrames - i; 48 } 49 resampler->resample((int32_t*) output + 2*i, thisFrames, provider); 50 i += thisFrames; 51 } 52} 53 54void buffercmp(const void *reference, const void *test, 55 size_t outputFrameSize, size_t outputFrames) 56{ 57 for (size_t i = 0; i < outputFrames; ++i) { 58 int check = memcmp((const char*)reference + i * outputFrameSize, 59 (const char*)test + i * outputFrameSize, outputFrameSize); 60 if (check) { 61 ALOGE("Failure at frame %d", i); 62 ASSERT_EQ(check, 0); /* fails */ 63 } 64 } 65} 66 67void testBufferIncrement(size_t channels, unsigned inputFreq, unsigned outputFreq, 68 enum android::AudioResampler::src_quality quality) 69{ 70 // create the provider 71 std::vector<int> inputIncr; 72 SignalProvider provider; 73 provider.setChirp<int16_t>(channels, 74 0., outputFreq/2., outputFreq, outputFreq/2000.); 75 provider.setIncr(inputIncr); 76 77 // calculate the output size 78 size_t outputFrames = ((int64_t) provider.getNumFrames() * outputFreq) / inputFreq; 79 size_t outputFrameSize = 2 * sizeof(int32_t); 80 size_t outputSize = outputFrameSize * outputFrames; 81 outputSize &= ~7; 82 83 // create the resampler 84 const int volumePrecision = 12; /* typical unity gain */ 85 android::AudioResampler* resampler; 86 87 resampler = android::AudioResampler::create(16, channels, outputFreq, quality); 88 resampler->setSampleRate(inputFreq); 89 resampler->setVolume(1 << volumePrecision, 1 << volumePrecision); 90 91 // set up the reference run 92 std::vector<size_t> refIncr; 93 refIncr.push_back(outputFrames); 94 void* reference = malloc(outputSize); 95 resample(reference, outputFrames, refIncr, &provider, resampler); 96 97 provider.reset(); 98 99#if 0 100 /* this test will fail - API interface issue: reset() does not clear internal buffers */ 101 resampler->reset(); 102#else 103 delete resampler; 104 resampler = android::AudioResampler::create(16, channels, outputFreq, quality); 105 resampler->setSampleRate(inputFreq); 106 resampler->setVolume(1 << volumePrecision, 1 << volumePrecision); 107#endif 108 109 // set up the test run 110 std::vector<size_t> outIncr; 111 outIncr.push_back(1); 112 outIncr.push_back(2); 113 outIncr.push_back(3); 114 void* test = malloc(outputSize); 115 resample(test, outputFrames, outIncr, &provider, resampler); 116 117 // check 118 buffercmp(reference, test, outputFrameSize, outputFrames); 119 120 free(reference); 121 free(test); 122 delete resampler; 123} 124 125template <typename T> 126inline double sqr(T v) 127{ 128 double dv = static_cast<double>(v); 129 return dv * dv; 130} 131 132template <typename T> 133double signalEnergy(T *start, T *end, unsigned stride) 134{ 135 double accum = 0; 136 137 for (T *p = start; p < end; p += stride) { 138 accum += sqr(*p); 139 } 140 unsigned count = (end - start + stride - 1) / stride; 141 return accum / count; 142} 143 144void testStopbandDownconversion(size_t channels, 145 unsigned inputFreq, unsigned outputFreq, 146 unsigned passband, unsigned stopband, 147 enum android::AudioResampler::src_quality quality) 148{ 149 // create the provider 150 std::vector<int> inputIncr; 151 SignalProvider provider; 152 provider.setChirp<int16_t>(channels, 153 0., inputFreq/2., inputFreq, inputFreq/2000.); 154 provider.setIncr(inputIncr); 155 156 // calculate the output size 157 size_t outputFrames = ((int64_t) provider.getNumFrames() * outputFreq) / inputFreq; 158 size_t outputFrameSize = 2 * sizeof(int32_t); 159 size_t outputSize = outputFrameSize * outputFrames; 160 outputSize &= ~7; 161 162 // create the resampler 163 const int volumePrecision = 12; /* typical unity gain */ 164 android::AudioResampler* resampler; 165 166 resampler = android::AudioResampler::create(16, channels, outputFreq, quality); 167 resampler->setSampleRate(inputFreq); 168 resampler->setVolume(1 << volumePrecision, 1 << volumePrecision); 169 170 // set up the reference run 171 std::vector<size_t> refIncr; 172 refIncr.push_back(outputFrames); 173 void* reference = malloc(outputSize); 174 resample(reference, outputFrames, refIncr, &provider, resampler); 175 176 int32_t *out = reinterpret_cast<int32_t *>(reference); 177 178 // check signal energy in passband 179 const unsigned passbandFrame = passband * outputFreq / 1000.; 180 const unsigned stopbandFrame = stopband * outputFreq / 1000.; 181 182 // check each channel separately 183 for (size_t i = 0; i < channels; ++i) { 184 double passbandEnergy = signalEnergy(out, out + passbandFrame * channels, channels); 185 double stopbandEnergy = signalEnergy(out + stopbandFrame * channels, 186 out + outputFrames * channels, channels); 187 double dbAtten = -10. * log10(stopbandEnergy / passbandEnergy); 188 ASSERT_GT(dbAtten, 60.); 189 190#if 0 191 // internal verification 192 printf("if:%d of:%d pbf:%d sbf:%d sbe: %f pbe: %f db: %.2f\n", 193 provider.getNumFrames(), outputFrames, 194 passbandFrame, stopbandFrame, stopbandEnergy, passbandEnergy, dbAtten); 195 for (size_t i = 0; i < 10; ++i) { 196 printf("%d\n", out[i+passbandFrame*channels]); 197 } 198 for (size_t i = 0; i < 10; ++i) { 199 printf("%d\n", out[i+stopbandFrame*channels]); 200 } 201#endif 202 } 203 204 free(reference); 205 delete resampler; 206} 207 208/* Buffer increment test 209 * 210 * We compare a reference output, where we consume and process the entire 211 * buffer at a time, and a test output, where we provide small chunks of input 212 * data and process small chunks of output (which may not be equivalent in size). 213 * 214 * Two subtests - fixed phase (3:2 down) and interpolated phase (147:320 up) 215 */ 216TEST(audioflinger_resampler, bufferincrement_fixedphase) { 217 // all of these work 218 static const enum android::AudioResampler::src_quality kQualityArray[] = { 219 android::AudioResampler::LOW_QUALITY, 220 android::AudioResampler::MED_QUALITY, 221 android::AudioResampler::HIGH_QUALITY, 222 android::AudioResampler::VERY_HIGH_QUALITY, 223 android::AudioResampler::DYN_LOW_QUALITY, 224 android::AudioResampler::DYN_MED_QUALITY, 225 android::AudioResampler::DYN_HIGH_QUALITY, 226 }; 227 228 for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) { 229 testBufferIncrement(2, 48000, 32000, kQualityArray[i]); 230 } 231} 232 233TEST(audioflinger_resampler, bufferincrement_interpolatedphase) { 234 // all of these work except low quality 235 static const enum android::AudioResampler::src_quality kQualityArray[] = { 236// android::AudioResampler::LOW_QUALITY, 237 android::AudioResampler::MED_QUALITY, 238 android::AudioResampler::HIGH_QUALITY, 239 android::AudioResampler::VERY_HIGH_QUALITY, 240 android::AudioResampler::DYN_LOW_QUALITY, 241 android::AudioResampler::DYN_MED_QUALITY, 242 android::AudioResampler::DYN_HIGH_QUALITY, 243 }; 244 245 for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) { 246 testBufferIncrement(2, 22050, 48000, kQualityArray[i]); 247 } 248} 249 250/* Simple aliasing test 251 * 252 * This checks stopband response of the chirp signal to make sure frequencies 253 * are properly suppressed. It uses downsampling because the stopband can be 254 * clearly isolated by input frequencies exceeding the output sample rate (nyquist). 255 */ 256TEST(audioflinger_resampler, stopbandresponse) { 257 // not all of these may work (old resamplers fail on downsampling) 258 static const enum android::AudioResampler::src_quality kQualityArray[] = { 259 //android::AudioResampler::LOW_QUALITY, 260 //android::AudioResampler::MED_QUALITY, 261 //android::AudioResampler::HIGH_QUALITY, 262 //android::AudioResampler::VERY_HIGH_QUALITY, 263 android::AudioResampler::DYN_LOW_QUALITY, 264 android::AudioResampler::DYN_MED_QUALITY, 265 android::AudioResampler::DYN_HIGH_QUALITY, 266 }; 267 268 // in this test we assume a maximum transition band between 12kHz and 20kHz. 269 // there must be at least 60dB relative attenuation between stopband and passband. 270 for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) { 271 testStopbandDownconversion(2, 48000, 32000, 12000, 20000, kQualityArray[i]); 272 } 273 274 // in this test we assume a maximum transition band between 7kHz and 15kHz. 275 // there must be at least 60dB relative attenuation between stopband and passband. 276 // (the weird ratio triggers interpolative resampling) 277 for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) { 278 testStopbandDownconversion(2, 48000, 22101, 7000, 15000, kQualityArray[i]); 279 } 280} 281