resampler_tests.cpp revision 3348e36c51e91e78020bcc6578eda83d97c31bec
1326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams/*
2326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams * Copyright (C) 2014 The Android Open Source Project
3326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams *
4326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams * Licensed under the Apache License, Version 2.0 (the "License");
5326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams * you may not use this file except in compliance with the License.
6326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams * You may obtain a copy of the License at
7326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams *
8326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams *      http://www.apache.org/licenses/LICENSE-2.0
9326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams *
10326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams * Unless required by applicable law or agreed to in writing, software
11326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams * distributed under the License is distributed on an "AS IS" BASIS,
12326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams * See the License for the specific language governing permissions and
14326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams * limitations under the License.
15326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams */
16326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams
17326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams//#define LOG_NDEBUG 0
18326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams#define LOG_TAG "audioflinger_resampler_tests"
19326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams
20326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams#include <unistd.h>
21326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams#include <stdio.h>
22326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams#include <stdlib.h>
23326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams#include <fcntl.h>
24326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams#include <string.h>
25326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams#include <sys/mman.h>
265c3e3bc8af6de6be5e6bd68e1d5168496f99e6cfJason Sams#include <sys/stat.h>
27326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams#include <errno.h>
28326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams#include <time.h>
29326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams#include <math.h>
30326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams#include <vector>
31326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams#include <utility>
32326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams#include <cutils/log.h>
33326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams#include <gtest/gtest.h>
34326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams#include <media/AudioBufferProvider.h>
35e514b45de8561fbc6ef6770845102ca10b0a69d7Jason Sams#include "AudioResampler.h"
36fa84da2cbc271f855b3b1ec75bb688abdf1d1d01Jason Sams#include "test_utils.h"
37fa84da2cbc271f855b3b1ec75bb688abdf1d1d01Jason Sams
38326e0ddf89e8df2837752fbfd7a014814b32082cJason Samsvoid resample(int channels, void *output,
39326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams        size_t outputFrames, const std::vector<size_t> &outputIncr,
40326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams        android::AudioBufferProvider *provider, android::AudioResampler *resampler)
41326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams{
42326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    for (size_t i = 0, j = 0; i < outputFrames; ) {
43326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams        size_t thisFrames = outputIncr[j++];
44326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams        if (j >= outputIncr.size()) {
45326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams            j = 0;
46326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams        }
47326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams        if (thisFrames == 0 || thisFrames > outputFrames - i) {
48326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams            thisFrames = outputFrames - i;
49326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams        }
507fabe1a3bf8de37d86021bb7f744c791db81aed3Jason Sams        resampler->resample((int32_t*) output + channels*i, thisFrames, provider);
51cf4c7c9b2f513be77a5b9853319ca82ac2b128edJason Sams        i += thisFrames;
52326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    }
53326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams}
54cf4c7c9b2f513be77a5b9853319ca82ac2b128edJason Sams
55cf4c7c9b2f513be77a5b9853319ca82ac2b128edJason Samsvoid buffercmp(const void *reference, const void *test,
56326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams        size_t outputFrameSize, size_t outputFrames)
57326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams{
58326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    for (size_t i = 0; i < outputFrames; ++i) {
599397e30ce5fe3f6af9212a93b490836b04fdfffaJason Sams        int check = memcmp((const char*)reference + i * outputFrameSize,
609397e30ce5fe3f6af9212a93b490836b04fdfffaJason Sams                (const char*)test + i * outputFrameSize, outputFrameSize);
61e5ffb879ae535a899a486285a23bea05e912480fJason Sams        if (check) {
629397e30ce5fe3f6af9212a93b490836b04fdfffaJason Sams            ALOGE("Failure at frame %d", i);
63326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams            ASSERT_EQ(check, 0); /* fails */
649397e30ce5fe3f6af9212a93b490836b04fdfffaJason Sams        }
65326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    }
66e579df42e85d9e00f53c42ef1b78dbd209dba989Jason Sams}
67e579df42e85d9e00f53c42ef1b78dbd209dba989Jason Sams
68e5ffb879ae535a899a486285a23bea05e912480fJason Samsvoid testBufferIncrement(size_t channels, bool useFloat,
69e5ffb879ae535a899a486285a23bea05e912480fJason Sams        unsigned inputFreq, unsigned outputFreq,
70e5ffb879ae535a899a486285a23bea05e912480fJason Sams        enum android::AudioResampler::src_quality quality)
715c3e3bc8af6de6be5e6bd68e1d5168496f99e6cfJason Sams{
725c3e3bc8af6de6be5e6bd68e1d5168496f99e6cfJason Sams    const audio_format_t format = useFloat ? AUDIO_FORMAT_PCM_FLOAT : AUDIO_FORMAT_PCM_16_BIT;
73e5ffb879ae535a899a486285a23bea05e912480fJason Sams    // create the provider
743dd429cc32388ca0c3d7a9368ed2e348b8fdaab1Jason Sams    std::vector<int> inputIncr;
75e5ffb879ae535a899a486285a23bea05e912480fJason Sams    SignalProvider provider;
76cf4c7c9b2f513be77a5b9853319ca82ac2b128edJason Sams    if (useFloat) {
77cf4c7c9b2f513be77a5b9853319ca82ac2b128edJason Sams        provider.setChirp<float>(channels,
78326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams                0., outputFreq/2., outputFreq, outputFreq/2000.);
795c3e3bc8af6de6be5e6bd68e1d5168496f99e6cfJason Sams    } else {
805c3e3bc8af6de6be5e6bd68e1d5168496f99e6cfJason Sams        provider.setChirp<int16_t>(channels,
81326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams                0., outputFreq/2., outputFreq, outputFreq/2000.);
82326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    }
83326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    provider.setIncr(inputIncr);
845c3e3bc8af6de6be5e6bd68e1d5168496f99e6cfJason Sams
855c3e3bc8af6de6be5e6bd68e1d5168496f99e6cfJason Sams    // calculate the output size
86fa84da2cbc271f855b3b1ec75bb688abdf1d1d01Jason Sams    size_t outputFrames = ((int64_t) provider.getNumFrames() * outputFreq) / inputFreq;
87fa84da2cbc271f855b3b1ec75bb688abdf1d1d01Jason Sams    size_t outputFrameSize = channels * (useFloat ? sizeof(float) : sizeof(int32_t));
88fa84da2cbc271f855b3b1ec75bb688abdf1d1d01Jason Sams    size_t outputSize = outputFrameSize * outputFrames;
89fa84da2cbc271f855b3b1ec75bb688abdf1d1d01Jason Sams    outputSize &= ~7;
90fa84da2cbc271f855b3b1ec75bb688abdf1d1d01Jason Sams
91326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    // create the resampler
92326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    const int volumePrecision = 12; /* typical unity gain */
93326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    android::AudioResampler* resampler;
94326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams
95326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    resampler = android::AudioResampler::create(format, channels, outputFreq, quality);
96326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    resampler->setSampleRate(inputFreq);
97326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    resampler->setVolume(1 << volumePrecision, 1 << volumePrecision);
98326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams
99326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    // set up the reference run
100326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    std::vector<size_t> refIncr;
101326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    refIncr.push_back(outputFrames);
102326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    void* reference = malloc(outputSize);
103326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    resample(channels, reference, outputFrames, refIncr, &provider, resampler);
104326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams
105326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    provider.reset();
106326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams
1077fabe1a3bf8de37d86021bb7f744c791db81aed3Jason Sams#if 0
108cf4c7c9b2f513be77a5b9853319ca82ac2b128edJason Sams    /* this test will fail - API interface issue: reset() does not clear internal buffers */
109326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    resampler->reset();
110326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams#else
111326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    delete resampler;
112326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    resampler = android::AudioResampler::create(format, channels, outputFreq, quality);
113326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    resampler->setSampleRate(inputFreq);
114326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    resampler->setVolume(1 << volumePrecision, 1 << volumePrecision);
115326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams#endif
116cf4c7c9b2f513be77a5b9853319ca82ac2b128edJason Sams
117cf4c7c9b2f513be77a5b9853319ca82ac2b128edJason Sams    // set up the test run
118fa84da2cbc271f855b3b1ec75bb688abdf1d1d01Jason Sams    std::vector<size_t> outIncr;
119fa84da2cbc271f855b3b1ec75bb688abdf1d1d01Jason Sams    outIncr.push_back(1);
120fa84da2cbc271f855b3b1ec75bb688abdf1d1d01Jason Sams    outIncr.push_back(2);
121fa84da2cbc271f855b3b1ec75bb688abdf1d1d01Jason Sams    outIncr.push_back(3);
122326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    void* test = malloc(outputSize);
123326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    inputIncr.push_back(1);
124326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    inputIncr.push_back(3);
125326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    provider.setIncr(inputIncr);
126326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams    resample(channels, test, outputFrames, outIncr, &provider, resampler);
127326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams
128    // check
129    buffercmp(reference, test, outputFrameSize, outputFrames);
130
131    free(reference);
132    free(test);
133    delete resampler;
134}
135
136template <typename T>
137inline double sqr(T v)
138{
139    double dv = static_cast<double>(v);
140    return dv * dv;
141}
142
143template <typename T>
144double signalEnergy(T *start, T *end, unsigned stride)
145{
146    double accum = 0;
147
148    for (T *p = start; p < end; p += stride) {
149        accum += sqr(*p);
150    }
151    unsigned count = (end - start + stride - 1) / stride;
152    return accum / count;
153}
154
155void testStopbandDownconversion(size_t channels,
156        unsigned inputFreq, unsigned outputFreq,
157        unsigned passband, unsigned stopband,
158        enum android::AudioResampler::src_quality quality)
159{
160    // create the provider
161    std::vector<int> inputIncr;
162    SignalProvider provider;
163    provider.setChirp<int16_t>(channels,
164            0., inputFreq/2., inputFreq, inputFreq/2000.);
165    provider.setIncr(inputIncr);
166
167    // calculate the output size
168    size_t outputFrames = ((int64_t) provider.getNumFrames() * outputFreq) / inputFreq;
169    size_t outputFrameSize = channels * sizeof(int32_t);
170    size_t outputSize = outputFrameSize * outputFrames;
171    outputSize &= ~7;
172
173    // create the resampler
174    const int volumePrecision = 12; /* typical unity gain */
175    android::AudioResampler* resampler;
176
177    resampler = android::AudioResampler::create(AUDIO_FORMAT_PCM_16_BIT,
178            channels, outputFreq, quality);
179    resampler->setSampleRate(inputFreq);
180    resampler->setVolume(1 << volumePrecision, 1 << volumePrecision);
181
182    // set up the reference run
183    std::vector<size_t> refIncr;
184    refIncr.push_back(outputFrames);
185    void* reference = malloc(outputSize);
186    resample(channels, reference, outputFrames, refIncr, &provider, resampler);
187
188    int32_t *out = reinterpret_cast<int32_t *>(reference);
189
190    // check signal energy in passband
191    const unsigned passbandFrame = passband * outputFreq / 1000.;
192    const unsigned stopbandFrame = stopband * outputFreq / 1000.;
193
194    // check each channel separately
195    for (size_t i = 0; i < channels; ++i) {
196        double passbandEnergy = signalEnergy(out, out + passbandFrame * channels, channels);
197        double stopbandEnergy = signalEnergy(out + stopbandFrame * channels,
198                out + outputFrames * channels, channels);
199        double dbAtten = -10. * log10(stopbandEnergy / passbandEnergy);
200        ASSERT_GT(dbAtten, 60.);
201
202#if 0
203        // internal verification
204        printf("if:%d  of:%d  pbf:%d  sbf:%d  sbe: %f  pbe: %f  db: %.2f\n",
205                provider.getNumFrames(), outputFrames,
206                passbandFrame, stopbandFrame, stopbandEnergy, passbandEnergy, dbAtten);
207        for (size_t i = 0; i < 10; ++i) {
208            printf("%d\n", out[i+passbandFrame*channels]);
209        }
210        for (size_t i = 0; i < 10; ++i) {
211            printf("%d\n", out[i+stopbandFrame*channels]);
212        }
213#endif
214    }
215
216    free(reference);
217    delete resampler;
218}
219
220/* Buffer increment test
221 *
222 * We compare a reference output, where we consume and process the entire
223 * buffer at a time, and a test output, where we provide small chunks of input
224 * data and process small chunks of output (which may not be equivalent in size).
225 *
226 * Two subtests - fixed phase (3:2 down) and interpolated phase (147:320 up)
227 */
228TEST(audioflinger_resampler, bufferincrement_fixedphase) {
229    // all of these work
230    static const enum android::AudioResampler::src_quality kQualityArray[] = {
231            android::AudioResampler::LOW_QUALITY,
232            android::AudioResampler::MED_QUALITY,
233            android::AudioResampler::HIGH_QUALITY,
234            android::AudioResampler::VERY_HIGH_QUALITY,
235            android::AudioResampler::DYN_LOW_QUALITY,
236            android::AudioResampler::DYN_MED_QUALITY,
237            android::AudioResampler::DYN_HIGH_QUALITY,
238    };
239
240    for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
241        testBufferIncrement(2, false, 48000, 32000, kQualityArray[i]);
242    }
243}
244
245TEST(audioflinger_resampler, bufferincrement_interpolatedphase) {
246    // all of these work except low quality
247    static const enum android::AudioResampler::src_quality kQualityArray[] = {
248//           android::AudioResampler::LOW_QUALITY,
249            android::AudioResampler::MED_QUALITY,
250            android::AudioResampler::HIGH_QUALITY,
251            android::AudioResampler::VERY_HIGH_QUALITY,
252            android::AudioResampler::DYN_LOW_QUALITY,
253            android::AudioResampler::DYN_MED_QUALITY,
254            android::AudioResampler::DYN_HIGH_QUALITY,
255    };
256
257    for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
258        testBufferIncrement(2, false, 22050, 48000, kQualityArray[i]);
259    }
260}
261
262TEST(audioflinger_resampler, bufferincrement_fixedphase_multi) {
263    // only dynamic quality
264    static const enum android::AudioResampler::src_quality kQualityArray[] = {
265            android::AudioResampler::DYN_LOW_QUALITY,
266            android::AudioResampler::DYN_MED_QUALITY,
267            android::AudioResampler::DYN_HIGH_QUALITY,
268    };
269
270    for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
271        testBufferIncrement(4, false, 48000, 32000, kQualityArray[i]);
272    }
273}
274
275TEST(audioflinger_resampler, bufferincrement_interpolatedphase_multi_float) {
276    // only dynamic quality
277    static const enum android::AudioResampler::src_quality kQualityArray[] = {
278            android::AudioResampler::DYN_LOW_QUALITY,
279            android::AudioResampler::DYN_MED_QUALITY,
280            android::AudioResampler::DYN_HIGH_QUALITY,
281    };
282
283    for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
284        testBufferIncrement(8, true, 22050, 48000, kQualityArray[i]);
285    }
286}
287
288/* Simple aliasing test
289 *
290 * This checks stopband response of the chirp signal to make sure frequencies
291 * are properly suppressed.  It uses downsampling because the stopband can be
292 * clearly isolated by input frequencies exceeding the output sample rate (nyquist).
293 */
294TEST(audioflinger_resampler, stopbandresponse) {
295    // not all of these may work (old resamplers fail on downsampling)
296    static const enum android::AudioResampler::src_quality kQualityArray[] = {
297            //android::AudioResampler::LOW_QUALITY,
298            //android::AudioResampler::MED_QUALITY,
299            //android::AudioResampler::HIGH_QUALITY,
300            //android::AudioResampler::VERY_HIGH_QUALITY,
301            android::AudioResampler::DYN_LOW_QUALITY,
302            android::AudioResampler::DYN_MED_QUALITY,
303            android::AudioResampler::DYN_HIGH_QUALITY,
304    };
305
306    // in this test we assume a maximum transition band between 12kHz and 20kHz.
307    // there must be at least 60dB relative attenuation between stopband and passband.
308    for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
309        testStopbandDownconversion(2, 48000, 32000, 12000, 20000, kQualityArray[i]);
310    }
311
312    // in this test we assume a maximum transition band between 7kHz and 15kHz.
313    // there must be at least 60dB relative attenuation between stopband and passband.
314    // (the weird ratio triggers interpolative resampling)
315    for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
316        testStopbandDownconversion(2, 48000, 22101, 7000, 15000, kQualityArray[i]);
317    }
318}
319