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 <errno.h>
21#include <fcntl.h>
22#include <math.h>
23#include <stdio.h>
24#include <stdlib.h>
25#include <string.h>
26#include <sys/mman.h>
27#include <sys/stat.h>
28#include <time.h>
29#include <unistd.h>
30
31#include <iostream>
32#include <memory>
33#include <utility>
34#include <vector>
35
36#include <gtest/gtest.h>
37#include <log/log.h>
38#include <media/AudioBufferProvider.h>
39
40#include <media/AudioResampler.h>
41#include "../AudioResamplerDyn.h"
42#include "../AudioResamplerFirGen.h"
43#include "test_utils.h"
44
45template <typename T>
46static void printData(T *data, size_t size) {
47    const size_t stride = 8;
48    for (size_t i = 0; i < size; ) {
49        for (size_t j = 0; j < stride && i < size; ++j) {
50            std::cout << data[i++] << ' ';  // extra space before newline
51        }
52        std::cout << '\n'; // or endl
53    }
54}
55
56void resample(int channels, void *output,
57        size_t outputFrames, const std::vector<size_t> &outputIncr,
58        android::AudioBufferProvider *provider, android::AudioResampler *resampler)
59{
60    for (size_t i = 0, j = 0; i < outputFrames; ) {
61        size_t thisFrames = outputIncr[j++];
62        if (j >= outputIncr.size()) {
63            j = 0;
64        }
65        if (thisFrames == 0 || thisFrames > outputFrames - i) {
66            thisFrames = outputFrames - i;
67        }
68        size_t framesResampled = resampler->resample(
69                (int32_t*) output + channels*i, thisFrames, provider);
70        // we should have enough buffer space, so there is no short count.
71        ASSERT_EQ(thisFrames, framesResampled);
72        i += thisFrames;
73    }
74}
75
76void buffercmp(const void *reference, const void *test,
77        size_t outputFrameSize, size_t outputFrames)
78{
79    for (size_t i = 0; i < outputFrames; ++i) {
80        int check = memcmp((const char*)reference + i * outputFrameSize,
81                (const char*)test + i * outputFrameSize, outputFrameSize);
82        if (check) {
83            ALOGE("Failure at frame %zu", i);
84            ASSERT_EQ(check, 0); /* fails */
85        }
86    }
87}
88
89void testBufferIncrement(size_t channels, bool useFloat,
90        unsigned inputFreq, unsigned outputFreq,
91        enum android::AudioResampler::src_quality quality)
92{
93    const audio_format_t format = useFloat ? AUDIO_FORMAT_PCM_FLOAT : AUDIO_FORMAT_PCM_16_BIT;
94    // create the provider
95    std::vector<int> inputIncr;
96    SignalProvider provider;
97    if (useFloat) {
98        provider.setChirp<float>(channels,
99                0., outputFreq/2., outputFreq, outputFreq/2000.);
100    } else {
101        provider.setChirp<int16_t>(channels,
102                0., outputFreq/2., outputFreq, outputFreq/2000.);
103    }
104    provider.setIncr(inputIncr);
105
106    // calculate the output size
107    size_t outputFrames = ((int64_t) provider.getNumFrames() * outputFreq) / inputFreq;
108    size_t outputFrameSize = (channels == 1 ? 2 : channels) * (useFloat ? sizeof(float) : sizeof(int32_t));
109    size_t outputSize = outputFrameSize * outputFrames;
110    outputSize &= ~7;
111
112    // create the resampler
113    android::AudioResampler* resampler;
114
115    resampler = android::AudioResampler::create(format, channels, outputFreq, quality);
116    resampler->setSampleRate(inputFreq);
117    resampler->setVolume(android::AudioResampler::UNITY_GAIN_FLOAT,
118            android::AudioResampler::UNITY_GAIN_FLOAT);
119
120    // set up the reference run
121    std::vector<size_t> refIncr;
122    refIncr.push_back(outputFrames);
123    void* reference = calloc(outputFrames, outputFrameSize);
124    resample(channels, reference, outputFrames, refIncr, &provider, resampler);
125
126    provider.reset();
127
128#if 0
129    /* this test will fail - API interface issue: reset() does not clear internal buffers */
130    resampler->reset();
131#else
132    delete resampler;
133    resampler = android::AudioResampler::create(format, channels, outputFreq, quality);
134    resampler->setSampleRate(inputFreq);
135    resampler->setVolume(android::AudioResampler::UNITY_GAIN_FLOAT,
136            android::AudioResampler::UNITY_GAIN_FLOAT);
137#endif
138
139    // set up the test run
140    std::vector<size_t> outIncr;
141    outIncr.push_back(1);
142    outIncr.push_back(2);
143    outIncr.push_back(3);
144    void* test = calloc(outputFrames, outputFrameSize);
145    inputIncr.push_back(1);
146    inputIncr.push_back(3);
147    provider.setIncr(inputIncr);
148    resample(channels, test, outputFrames, outIncr, &provider, resampler);
149
150    // check
151    buffercmp(reference, test, outputFrameSize, outputFrames);
152
153    free(reference);
154    free(test);
155    delete resampler;
156}
157
158template <typename T>
159inline double sqr(T v)
160{
161    double dv = static_cast<double>(v);
162    return dv * dv;
163}
164
165template <typename T>
166double signalEnergy(T *start, T *end, unsigned stride)
167{
168    double accum = 0;
169
170    for (T *p = start; p < end; p += stride) {
171        accum += sqr(*p);
172    }
173    unsigned count = (end - start + stride - 1) / stride;
174    return accum / count;
175}
176
177// TI = resampler input type, int16_t or float
178// TO = resampler output type, int32_t or float
179template <typename TI, typename TO>
180void testStopbandDownconversion(size_t channels,
181        unsigned inputFreq, unsigned outputFreq,
182        unsigned passband, unsigned stopband,
183        enum android::AudioResampler::src_quality quality)
184{
185    // create the provider
186    std::vector<int> inputIncr;
187    SignalProvider provider;
188    provider.setChirp<TI>(channels,
189            0., inputFreq/2., inputFreq, inputFreq/2000.);
190    provider.setIncr(inputIncr);
191
192    // calculate the output size
193    size_t outputFrames = ((int64_t) provider.getNumFrames() * outputFreq) / inputFreq;
194    size_t outputFrameSize = (channels == 1 ? 2 : channels) * sizeof(TO);
195    size_t outputSize = outputFrameSize * outputFrames;
196    outputSize &= ~7;
197
198    // create the resampler
199    android::AudioResampler* resampler;
200
201    resampler = android::AudioResampler::create(
202            is_same<TI, int16_t>::value ? AUDIO_FORMAT_PCM_16_BIT : AUDIO_FORMAT_PCM_FLOAT,
203            channels, outputFreq, quality);
204    resampler->setSampleRate(inputFreq);
205    resampler->setVolume(android::AudioResampler::UNITY_GAIN_FLOAT,
206            android::AudioResampler::UNITY_GAIN_FLOAT);
207
208    // set up the reference run
209    std::vector<size_t> refIncr;
210    refIncr.push_back(outputFrames);
211    void* reference = calloc(outputFrames, outputFrameSize);
212    resample(channels, reference, outputFrames, refIncr, &provider, resampler);
213
214    TO *out = reinterpret_cast<TO *>(reference);
215
216    // check signal energy in passband
217    const unsigned passbandFrame = passband * outputFreq / 1000.;
218    const unsigned stopbandFrame = stopband * outputFreq / 1000.;
219
220    // check each channel separately
221    if (channels == 1) channels = 2; // workaround (mono duplicates output channel)
222
223    for (size_t i = 0; i < channels; ++i) {
224        double passbandEnergy = signalEnergy(out, out + passbandFrame * channels, channels);
225        double stopbandEnergy = signalEnergy(out + stopbandFrame * channels,
226                out + outputFrames * channels, channels);
227        double dbAtten = -10. * log10(stopbandEnergy / passbandEnergy);
228        ASSERT_GT(dbAtten, 60.);
229
230#if 0
231        // internal verification
232        printf("if:%d  of:%d  pbf:%d  sbf:%d  sbe: %f  pbe: %f  db: %.2f\n",
233                provider.getNumFrames(), outputFrames,
234                passbandFrame, stopbandFrame, stopbandEnergy, passbandEnergy, dbAtten);
235        for (size_t i = 0; i < 10; ++i) {
236            std::cout << out[i+passbandFrame*channels] << std::endl;
237        }
238        for (size_t i = 0; i < 10; ++i) {
239            std::cout << out[i+stopbandFrame*channels] << std::endl;
240        }
241#endif
242    }
243
244    free(reference);
245    delete resampler;
246}
247
248void testFilterResponse(
249        size_t channels, unsigned inputFreq, unsigned outputFreq)
250{
251    // create resampler
252    using ResamplerType = android::AudioResamplerDyn<float, float, float>;
253    std::unique_ptr<ResamplerType> rdyn(
254            static_cast<ResamplerType *>(
255                    android::AudioResampler::create(
256                            AUDIO_FORMAT_PCM_FLOAT,
257                            channels,
258                            outputFreq,
259                            android::AudioResampler::DYN_HIGH_QUALITY)));
260    rdyn->setSampleRate(inputFreq);
261
262    // get design parameters
263    const int phases = rdyn->getPhases();
264    const int halfLength = rdyn->getHalfLength();
265    const float *coefs = rdyn->getFilterCoefs();
266    const double fcr = rdyn->getNormalizedCutoffFrequency();
267    const double tbw = rdyn->getNormalizedTransitionBandwidth();
268    const double attenuation = rdyn->getFilterAttenuation();
269    const double stopbandDb = rdyn->getStopbandAttenuationDb();
270    const double passbandDb = rdyn->getPassbandRippleDb();
271    const double fp = fcr - tbw / 2;
272    const double fs = fcr + tbw / 2;
273
274    printf("inputFreq:%d outputFreq:%d design"
275            " phases:%d halfLength:%d"
276            " fcr:%lf fp:%lf fs:%lf tbw:%lf"
277            " attenuation:%lf stopRipple:%.lf passRipple:%lf"
278            "\n",
279            inputFreq, outputFreq,
280            phases, halfLength,
281            fcr, fp, fs, tbw,
282            attenuation, stopbandDb, passbandDb);
283
284    // verify design parameters
285    constexpr int32_t passSteps = 1000;
286    double passMin, passMax, passRipple, stopMax, stopRipple;
287    android::testFir(coefs, phases, halfLength, fp / phases, fs / phases,
288            passSteps, phases * passSteps /* stopSteps */,
289            passMin, passMax, passRipple,
290            stopMax, stopRipple);
291    printf("inputFreq:%d outputFreq:%d verify"
292            " passMin:%lf passMax:%lf passRipple:%lf stopMax:%lf stopRipple:%lf"
293            "\n",
294            inputFreq, outputFreq,
295            passMin, passMax, passRipple, stopMax, stopRipple);
296
297    ASSERT_GT(stopRipple, 60.);  // enough stopband attenuation
298    ASSERT_LT(passRipple, 0.2);  // small passband ripple
299    ASSERT_GT(passMin, 0.99);    // we do not attenuate the signal (ideally 1.)
300}
301
302/* Buffer increment test
303 *
304 * We compare a reference output, where we consume and process the entire
305 * buffer at a time, and a test output, where we provide small chunks of input
306 * data and process small chunks of output (which may not be equivalent in size).
307 *
308 * Two subtests - fixed phase (3:2 down) and interpolated phase (147:320 up)
309 */
310TEST(audioflinger_resampler, bufferincrement_fixedphase) {
311    // all of these work
312    static const enum android::AudioResampler::src_quality kQualityArray[] = {
313            android::AudioResampler::LOW_QUALITY,
314            android::AudioResampler::MED_QUALITY,
315            android::AudioResampler::HIGH_QUALITY,
316            android::AudioResampler::VERY_HIGH_QUALITY,
317            android::AudioResampler::DYN_LOW_QUALITY,
318            android::AudioResampler::DYN_MED_QUALITY,
319            android::AudioResampler::DYN_HIGH_QUALITY,
320    };
321
322    for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
323        testBufferIncrement(2, false, 48000, 32000, kQualityArray[i]);
324    }
325}
326
327TEST(audioflinger_resampler, bufferincrement_interpolatedphase) {
328    // all of these work except low quality
329    static const enum android::AudioResampler::src_quality kQualityArray[] = {
330//           android::AudioResampler::LOW_QUALITY,
331            android::AudioResampler::MED_QUALITY,
332            android::AudioResampler::HIGH_QUALITY,
333            android::AudioResampler::VERY_HIGH_QUALITY,
334            android::AudioResampler::DYN_LOW_QUALITY,
335            android::AudioResampler::DYN_MED_QUALITY,
336            android::AudioResampler::DYN_HIGH_QUALITY,
337    };
338
339    for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
340        testBufferIncrement(2, false, 22050, 48000, kQualityArray[i]);
341    }
342}
343
344TEST(audioflinger_resampler, bufferincrement_fixedphase_multi) {
345    // only dynamic quality
346    static const enum android::AudioResampler::src_quality kQualityArray[] = {
347            android::AudioResampler::DYN_LOW_QUALITY,
348            android::AudioResampler::DYN_MED_QUALITY,
349            android::AudioResampler::DYN_HIGH_QUALITY,
350    };
351
352    for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
353        testBufferIncrement(4, false, 48000, 32000, kQualityArray[i]);
354    }
355}
356
357TEST(audioflinger_resampler, bufferincrement_interpolatedphase_multi_float) {
358    // only dynamic quality
359    static const enum android::AudioResampler::src_quality kQualityArray[] = {
360            android::AudioResampler::DYN_LOW_QUALITY,
361            android::AudioResampler::DYN_MED_QUALITY,
362            android::AudioResampler::DYN_HIGH_QUALITY,
363    };
364
365    for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
366        testBufferIncrement(8, true, 22050, 48000, kQualityArray[i]);
367    }
368}
369
370/* Simple aliasing test
371 *
372 * This checks stopband response of the chirp signal to make sure frequencies
373 * are properly suppressed.  It uses downsampling because the stopband can be
374 * clearly isolated by input frequencies exceeding the output sample rate (nyquist).
375 */
376TEST(audioflinger_resampler, stopbandresponse_integer) {
377    // not all of these may work (old resamplers fail on downsampling)
378    static const enum android::AudioResampler::src_quality kQualityArray[] = {
379            //android::AudioResampler::LOW_QUALITY,
380            //android::AudioResampler::MED_QUALITY,
381            //android::AudioResampler::HIGH_QUALITY,
382            //android::AudioResampler::VERY_HIGH_QUALITY,
383            android::AudioResampler::DYN_LOW_QUALITY,
384            android::AudioResampler::DYN_MED_QUALITY,
385            android::AudioResampler::DYN_HIGH_QUALITY,
386    };
387
388    // in this test we assume a maximum transition band between 12kHz and 20kHz.
389    // there must be at least 60dB relative attenuation between stopband and passband.
390    for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
391        testStopbandDownconversion<int16_t, int32_t>(
392                2, 48000, 32000, 12000, 20000, kQualityArray[i]);
393    }
394
395    // in this test we assume a maximum transition band between 7kHz and 15kHz.
396    // there must be at least 60dB relative attenuation between stopband and passband.
397    // (the weird ratio triggers interpolative resampling)
398    for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
399        testStopbandDownconversion<int16_t, int32_t>(
400                2, 48000, 22101, 7000, 15000, kQualityArray[i]);
401    }
402}
403
404TEST(audioflinger_resampler, stopbandresponse_integer_mono) {
405    // not all of these may work (old resamplers fail on downsampling)
406    static const enum android::AudioResampler::src_quality kQualityArray[] = {
407            //android::AudioResampler::LOW_QUALITY,
408            //android::AudioResampler::MED_QUALITY,
409            //android::AudioResampler::HIGH_QUALITY,
410            //android::AudioResampler::VERY_HIGH_QUALITY,
411            android::AudioResampler::DYN_LOW_QUALITY,
412            android::AudioResampler::DYN_MED_QUALITY,
413            android::AudioResampler::DYN_HIGH_QUALITY,
414    };
415
416    // in this test we assume a maximum transition band between 12kHz and 20kHz.
417    // there must be at least 60dB relative attenuation between stopband and passband.
418    for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
419        testStopbandDownconversion<int16_t, int32_t>(
420                1, 48000, 32000, 12000, 20000, kQualityArray[i]);
421    }
422
423    // in this test we assume a maximum transition band between 7kHz and 15kHz.
424    // there must be at least 60dB relative attenuation between stopband and passband.
425    // (the weird ratio triggers interpolative resampling)
426    for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
427        testStopbandDownconversion<int16_t, int32_t>(
428                1, 48000, 22101, 7000, 15000, kQualityArray[i]);
429    }
430}
431
432TEST(audioflinger_resampler, stopbandresponse_integer_multichannel) {
433    // not all of these may work (old resamplers fail on downsampling)
434    static const enum android::AudioResampler::src_quality kQualityArray[] = {
435            //android::AudioResampler::LOW_QUALITY,
436            //android::AudioResampler::MED_QUALITY,
437            //android::AudioResampler::HIGH_QUALITY,
438            //android::AudioResampler::VERY_HIGH_QUALITY,
439            android::AudioResampler::DYN_LOW_QUALITY,
440            android::AudioResampler::DYN_MED_QUALITY,
441            android::AudioResampler::DYN_HIGH_QUALITY,
442    };
443
444    // in this test we assume a maximum transition band between 12kHz and 20kHz.
445    // there must be at least 60dB relative attenuation between stopband and passband.
446    for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
447        testStopbandDownconversion<int16_t, int32_t>(
448                8, 48000, 32000, 12000, 20000, kQualityArray[i]);
449    }
450
451    // in this test we assume a maximum transition band between 7kHz and 15kHz.
452    // there must be at least 60dB relative attenuation between stopband and passband.
453    // (the weird ratio triggers interpolative resampling)
454    for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
455        testStopbandDownconversion<int16_t, int32_t>(
456                8, 48000, 22101, 7000, 15000, kQualityArray[i]);
457    }
458}
459
460TEST(audioflinger_resampler, stopbandresponse_float) {
461    // not all of these may work (old resamplers fail on downsampling)
462    static const enum android::AudioResampler::src_quality kQualityArray[] = {
463            //android::AudioResampler::LOW_QUALITY,
464            //android::AudioResampler::MED_QUALITY,
465            //android::AudioResampler::HIGH_QUALITY,
466            //android::AudioResampler::VERY_HIGH_QUALITY,
467            android::AudioResampler::DYN_LOW_QUALITY,
468            android::AudioResampler::DYN_MED_QUALITY,
469            android::AudioResampler::DYN_HIGH_QUALITY,
470    };
471
472    // in this test we assume a maximum transition band between 12kHz and 20kHz.
473    // there must be at least 60dB relative attenuation between stopband and passband.
474    for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
475        testStopbandDownconversion<float, float>(
476                2, 48000, 32000, 12000, 20000, kQualityArray[i]);
477    }
478
479    // in this test we assume a maximum transition band between 7kHz and 15kHz.
480    // there must be at least 60dB relative attenuation between stopband and passband.
481    // (the weird ratio triggers interpolative resampling)
482    for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
483        testStopbandDownconversion<float, float>(
484                2, 48000, 22101, 7000, 15000, kQualityArray[i]);
485    }
486}
487
488TEST(audioflinger_resampler, stopbandresponse_float_mono) {
489    // not all of these may work (old resamplers fail on downsampling)
490    static const enum android::AudioResampler::src_quality kQualityArray[] = {
491            //android::AudioResampler::LOW_QUALITY,
492            //android::AudioResampler::MED_QUALITY,
493            //android::AudioResampler::HIGH_QUALITY,
494            //android::AudioResampler::VERY_HIGH_QUALITY,
495            android::AudioResampler::DYN_LOW_QUALITY,
496            android::AudioResampler::DYN_MED_QUALITY,
497            android::AudioResampler::DYN_HIGH_QUALITY,
498    };
499
500    // in this test we assume a maximum transition band between 12kHz and 20kHz.
501    // there must be at least 60dB relative attenuation between stopband and passband.
502    for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
503        testStopbandDownconversion<float, float>(
504                1, 48000, 32000, 12000, 20000, kQualityArray[i]);
505    }
506
507    // in this test we assume a maximum transition band between 7kHz and 15kHz.
508    // there must be at least 60dB relative attenuation between stopband and passband.
509    // (the weird ratio triggers interpolative resampling)
510    for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
511        testStopbandDownconversion<float, float>(
512                1, 48000, 22101, 7000, 15000, kQualityArray[i]);
513    }
514}
515
516TEST(audioflinger_resampler, stopbandresponse_float_multichannel) {
517    // not all of these may work (old resamplers fail on downsampling)
518    static const enum android::AudioResampler::src_quality kQualityArray[] = {
519            //android::AudioResampler::LOW_QUALITY,
520            //android::AudioResampler::MED_QUALITY,
521            //android::AudioResampler::HIGH_QUALITY,
522            //android::AudioResampler::VERY_HIGH_QUALITY,
523            android::AudioResampler::DYN_LOW_QUALITY,
524            android::AudioResampler::DYN_MED_QUALITY,
525            android::AudioResampler::DYN_HIGH_QUALITY,
526    };
527
528    // in this test we assume a maximum transition band between 12kHz and 20kHz.
529    // there must be at least 60dB relative attenuation between stopband and passband.
530    for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
531        testStopbandDownconversion<float, float>(
532                8, 48000, 32000, 12000, 20000, kQualityArray[i]);
533    }
534
535    // in this test we assume a maximum transition band between 7kHz and 15kHz.
536    // there must be at least 60dB relative attenuation between stopband and passband.
537    // (the weird ratio triggers interpolative resampling)
538    for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
539        testStopbandDownconversion<float, float>(
540                8, 48000, 22101, 7000, 15000, kQualityArray[i]);
541    }
542}
543
544TEST(audioflinger_resampler, filterresponse) {
545    std::vector<int> inSampleRates{
546        8000,
547        11025,
548        12000,
549        16000,
550        22050,
551        24000,
552        32000,
553        44100,
554        48000,
555        88200,
556        96000,
557        176400,
558        192000,
559    };
560    std::vector<int> outSampleRates{
561        48000,
562        96000,
563    };
564
565    for (int outSampleRate : outSampleRates) {
566        for (int inSampleRate : inSampleRates) {
567            testFilterResponse(2 /* channels */, inSampleRate, outSampleRate);
568        }
569    }
570}
571