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