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
39void 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
59void 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
72void 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
141template <typename T>
142inline double sqr(T v)
143{
144    double dv = static_cast<double>(v);
145    return dv * dv;
146}
147
148template <typename T>
149double 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
162template <typename TI, typename TO>
163void 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 */
237TEST(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
254TEST(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
271TEST(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
284TEST(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 */
303TEST(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
331TEST(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
359TEST(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
387TEST(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