Home | History | Annotate | Download | only in tests
      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