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