resampler_tests.cpp revision 42b011166ece30969667e0ff9dcf4832568c9c1a
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        resampler->resample((int32_t*) output + channels*i, thisFrames, provider);
52        i += thisFrames;
53    }
54}
55
56void 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 %d", i);
64            ASSERT_EQ(check, 0); /* fails */
65        }
66    }
67}
68
69void 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
138template <typename T>
139inline double sqr(T v)
140{
141    double dv = static_cast<double>(v);
142    return dv * dv;
143}
144
145template <typename T>
146double 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
159template <typename TI, typename TO>
160void 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 */
234TEST(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
251TEST(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
268TEST(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
281TEST(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 */
300TEST(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
328TEST(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
356TEST(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
384TEST(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