resampler_tests.cpp revision c0e5ec8e2d8db15b97094374d0a248e041304b62
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 <cutils/log.h>
33#include <gtest/gtest.h>
34#include <media/AudioBufferProvider.h>
35#include "AudioResampler.h"
36#include "test_utils.h"
37
38void resample(void *output, size_t outputFrames, const std::vector<size_t> &outputIncr,
39        android::AudioBufferProvider *provider, android::AudioResampler *resampler)
40{
41    for (size_t i = 0, j = 0; i < outputFrames; ) {
42        size_t thisFrames = outputIncr[j++];
43        if (j >= outputIncr.size()) {
44            j = 0;
45        }
46        if (thisFrames == 0 || thisFrames > outputFrames - i) {
47            thisFrames = outputFrames - i;
48        }
49        resampler->resample((int32_t*) output + 2*i, thisFrames, provider);
50        i += thisFrames;
51    }
52}
53
54void buffercmp(const void *reference, const void *test,
55        size_t outputFrameSize, size_t outputFrames)
56{
57    for (size_t i = 0; i < outputFrames; ++i) {
58        int check = memcmp((const char*)reference + i * outputFrameSize,
59                (const char*)test + i * outputFrameSize, outputFrameSize);
60        if (check) {
61            ALOGE("Failure at frame %d", i);
62            ASSERT_EQ(check, 0); /* fails */
63        }
64    }
65}
66
67void testBufferIncrement(size_t channels, unsigned inputFreq, unsigned outputFreq,
68        enum android::AudioResampler::src_quality quality)
69{
70    // create the provider
71    std::vector<int> inputIncr;
72    SignalProvider provider;
73    provider.setChirp<int16_t>(channels,
74            0., outputFreq/2., outputFreq, outputFreq/2000.);
75    provider.setIncr(inputIncr);
76
77    // calculate the output size
78    size_t outputFrames = ((int64_t) provider.getNumFrames() * outputFreq) / inputFreq;
79    size_t outputFrameSize = 2 * sizeof(int32_t);
80    size_t outputSize = outputFrameSize * outputFrames;
81    outputSize &= ~7;
82
83    // create the resampler
84    const int volumePrecision = 12; /* typical unity gain */
85    android::AudioResampler* resampler;
86
87    resampler = android::AudioResampler::create(16, channels, outputFreq, quality);
88    resampler->setSampleRate(inputFreq);
89    resampler->setVolume(1 << volumePrecision, 1 << volumePrecision);
90
91    // set up the reference run
92    std::vector<size_t> refIncr;
93    refIncr.push_back(outputFrames);
94    void* reference = malloc(outputSize);
95    resample(reference, outputFrames, refIncr, &provider, resampler);
96
97    provider.reset();
98
99#if 0
100    /* this test will fail - API interface issue: reset() does not clear internal buffers */
101    resampler->reset();
102#else
103    delete resampler;
104    resampler = android::AudioResampler::create(16, channels, outputFreq, quality);
105    resampler->setSampleRate(inputFreq);
106    resampler->setVolume(1 << volumePrecision, 1 << volumePrecision);
107#endif
108
109    // set up the test run
110    std::vector<size_t> outIncr;
111    outIncr.push_back(1);
112    outIncr.push_back(2);
113    outIncr.push_back(3);
114    void* test = malloc(outputSize);
115    resample(test, outputFrames, outIncr, &provider, resampler);
116
117    // check
118    buffercmp(reference, test, outputFrameSize, outputFrames);
119
120    free(reference);
121    free(test);
122    delete resampler;
123}
124
125template <typename T>
126inline double sqr(T v)
127{
128    double dv = static_cast<double>(v);
129    return dv * dv;
130}
131
132template <typename T>
133double signalEnergy(T *start, T *end, unsigned stride)
134{
135    double accum = 0;
136
137    for (T *p = start; p < end; p += stride) {
138        accum += sqr(*p);
139    }
140    unsigned count = (end - start + stride - 1) / stride;
141    return accum / count;
142}
143
144void testStopbandDownconversion(size_t channels,
145        unsigned inputFreq, unsigned outputFreq,
146        unsigned passband, unsigned stopband,
147        enum android::AudioResampler::src_quality quality)
148{
149    // create the provider
150    std::vector<int> inputIncr;
151    SignalProvider provider;
152    provider.setChirp<int16_t>(channels,
153            0., inputFreq/2., inputFreq, inputFreq/2000.);
154    provider.setIncr(inputIncr);
155
156    // calculate the output size
157    size_t outputFrames = ((int64_t) provider.getNumFrames() * outputFreq) / inputFreq;
158    size_t outputFrameSize = 2 * sizeof(int32_t);
159    size_t outputSize = outputFrameSize * outputFrames;
160    outputSize &= ~7;
161
162    // create the resampler
163    const int volumePrecision = 12; /* typical unity gain */
164    android::AudioResampler* resampler;
165
166    resampler = android::AudioResampler::create(16, channels, outputFreq, quality);
167    resampler->setSampleRate(inputFreq);
168    resampler->setVolume(1 << volumePrecision, 1 << volumePrecision);
169
170    // set up the reference run
171    std::vector<size_t> refIncr;
172    refIncr.push_back(outputFrames);
173    void* reference = malloc(outputSize);
174    resample(reference, outputFrames, refIncr, &provider, resampler);
175
176    int32_t *out = reinterpret_cast<int32_t *>(reference);
177
178    // check signal energy in passband
179    const unsigned passbandFrame = passband * outputFreq / 1000.;
180    const unsigned stopbandFrame = stopband * outputFreq / 1000.;
181
182    // check each channel separately
183    for (size_t i = 0; i < channels; ++i) {
184        double passbandEnergy = signalEnergy(out, out + passbandFrame * channels, channels);
185        double stopbandEnergy = signalEnergy(out + stopbandFrame * channels,
186                out + outputFrames * channels, channels);
187        double dbAtten = -10. * log10(stopbandEnergy / passbandEnergy);
188        ASSERT_GT(dbAtten, 60.);
189
190#if 0
191        // internal verification
192        printf("if:%d  of:%d  pbf:%d  sbf:%d  sbe: %f  pbe: %f  db: %.2f\n",
193                provider.getNumFrames(), outputFrames,
194                passbandFrame, stopbandFrame, stopbandEnergy, passbandEnergy, dbAtten);
195        for (size_t i = 0; i < 10; ++i) {
196            printf("%d\n", out[i+passbandFrame*channels]);
197        }
198        for (size_t i = 0; i < 10; ++i) {
199            printf("%d\n", out[i+stopbandFrame*channels]);
200        }
201#endif
202    }
203
204    free(reference);
205    delete resampler;
206}
207
208/* Buffer increment test
209 *
210 * We compare a reference output, where we consume and process the entire
211 * buffer at a time, and a test output, where we provide small chunks of input
212 * data and process small chunks of output (which may not be equivalent in size).
213 *
214 * Two subtests - fixed phase (3:2 down) and interpolated phase (147:320 up)
215 */
216TEST(audioflinger_resampler, bufferincrement_fixedphase) {
217    // all of these work
218    static const enum android::AudioResampler::src_quality kQualityArray[] = {
219            android::AudioResampler::LOW_QUALITY,
220            android::AudioResampler::MED_QUALITY,
221            android::AudioResampler::HIGH_QUALITY,
222            android::AudioResampler::VERY_HIGH_QUALITY,
223            android::AudioResampler::DYN_LOW_QUALITY,
224            android::AudioResampler::DYN_MED_QUALITY,
225            android::AudioResampler::DYN_HIGH_QUALITY,
226    };
227
228    for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
229        testBufferIncrement(2, 48000, 32000, kQualityArray[i]);
230    }
231}
232
233TEST(audioflinger_resampler, bufferincrement_interpolatedphase) {
234    // all of these work except low quality
235    static const enum android::AudioResampler::src_quality kQualityArray[] = {
236//           android::AudioResampler::LOW_QUALITY,
237            android::AudioResampler::MED_QUALITY,
238            android::AudioResampler::HIGH_QUALITY,
239            android::AudioResampler::VERY_HIGH_QUALITY,
240            android::AudioResampler::DYN_LOW_QUALITY,
241            android::AudioResampler::DYN_MED_QUALITY,
242            android::AudioResampler::DYN_HIGH_QUALITY,
243    };
244
245    for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
246        testBufferIncrement(2, 22050, 48000, kQualityArray[i]);
247    }
248}
249
250/* Simple aliasing test
251 *
252 * This checks stopband response of the chirp signal to make sure frequencies
253 * are properly suppressed.  It uses downsampling because the stopband can be
254 * clearly isolated by input frequencies exceeding the output sample rate (nyquist).
255 */
256TEST(audioflinger_resampler, stopbandresponse) {
257    // not all of these may work (old resamplers fail on downsampling)
258    static const enum android::AudioResampler::src_quality kQualityArray[] = {
259            //android::AudioResampler::LOW_QUALITY,
260            //android::AudioResampler::MED_QUALITY,
261            //android::AudioResampler::HIGH_QUALITY,
262            //android::AudioResampler::VERY_HIGH_QUALITY,
263            android::AudioResampler::DYN_LOW_QUALITY,
264            android::AudioResampler::DYN_MED_QUALITY,
265            android::AudioResampler::DYN_HIGH_QUALITY,
266    };
267
268    // in this test we assume a maximum transition band between 12kHz and 20kHz.
269    // there must be at least 60dB relative attenuation between stopband and passband.
270    for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
271        testStopbandDownconversion(2, 48000, 32000, 12000, 20000, kQualityArray[i]);
272    }
273
274    // in this test we assume a maximum transition band between 7kHz and 15kHz.
275    // there must be at least 60dB relative attenuation between stopband and passband.
276    // (the weird ratio triggers interpolative resampling)
277    for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
278        testStopbandDownconversion(2, 48000, 22101, 7000, 15000, kQualityArray[i]);
279    }
280}
281