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