resampler_tests.cpp revision 546734b3ab577d46afe863515104a062e88a109b
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
37#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
38
39template<typename T, typename U>
40struct is_same
41{
42    static const bool value = false;
43};
44
45template<typename T>
46struct is_same<T, T>  // partial specialization
47{
48    static const bool value = true;
49};
50
51template<typename T>
52static inline T convertValue(double val)
53{
54    if (is_same<T, int16_t>::value) {
55        return floor(val * 32767.0 + 0.5);
56    } else if (is_same<T, int32_t>::value) {
57        return floor(val * (1UL<<31) + 0.5);
58    }
59    return val; // assume float or double
60}
61
62/* Creates a type-independent audio buffer provider from
63 * a buffer base address, size, framesize, and input increment array.
64 *
65 * No allocation or deallocation of the provided buffer is done.
66 */
67class TestProvider : public android::AudioBufferProvider {
68public:
69    TestProvider(const void* addr, size_t frames, size_t frameSize,
70            const std::vector<size_t>& inputIncr)
71    : mAddr(addr),
72      mNumFrames(frames),
73      mFrameSize(frameSize),
74      mNextFrame(0), mUnrel(0), mInputIncr(inputIncr), mNextIdx(0)
75    {
76    }
77
78    virtual android::status_t getNextBuffer(Buffer* buffer, int64_t pts __unused = kInvalidPTS )
79    {
80        size_t requestedFrames = buffer->frameCount;
81        if (requestedFrames > mNumFrames - mNextFrame) {
82            buffer->frameCount = mNumFrames - mNextFrame;
83        }
84        if (!mInputIncr.empty()) {
85            size_t provided = mInputIncr[mNextIdx++];
86            ALOGV("getNextBuffer() mValue[%d]=%u not %u",
87                    mNextIdx-1, provided, buffer->frameCount);
88            if (provided < buffer->frameCount) {
89                buffer->frameCount = provided;
90            }
91            if (mNextIdx >= mInputIncr.size()) {
92                mNextIdx = 0;
93            }
94        }
95        ALOGV("getNextBuffer() requested %u frames out of %u frames available"
96                " and returned %u frames\n",
97                requestedFrames, mNumFrames - mNextFrame, buffer->frameCount);
98        mUnrel = buffer->frameCount;
99        if (buffer->frameCount > 0) {
100            buffer->raw = (char *)mAddr + mFrameSize * mNextFrame;
101            return android::NO_ERROR;
102        } else {
103            buffer->raw = NULL;
104            return android::NOT_ENOUGH_DATA;
105        }
106    }
107
108    virtual void releaseBuffer(Buffer* buffer)
109    {
110        if (buffer->frameCount > mUnrel) {
111            ALOGE("releaseBuffer() released %u frames but only %u available "
112                    "to release\n", buffer->frameCount, mUnrel);
113            mNextFrame += mUnrel;
114            mUnrel = 0;
115        } else {
116
117            ALOGV("releaseBuffer() released %u frames out of %u frames available "
118                    "to release\n", buffer->frameCount, mUnrel);
119            mNextFrame += buffer->frameCount;
120            mUnrel -= buffer->frameCount;
121        }
122        buffer->frameCount = 0;
123        buffer->raw = NULL;
124    }
125
126    void reset()
127    {
128        mNextFrame = 0;
129    }
130
131    size_t getNumFrames()
132    {
133        return mNumFrames;
134    }
135
136    void setIncr(const std::vector<size_t> inputIncr)
137    {
138        mNextIdx = 0;
139        mInputIncr = inputIncr;
140    }
141
142protected:
143    const void* mAddr;   // base address
144    size_t mNumFrames;   // total frames
145    int mFrameSize;      // frame size (# channels * bytes per sample)
146    size_t mNextFrame;   // index of next frame to provide
147    size_t mUnrel;       // number of frames not yet released
148    std::vector<size_t> mInputIncr; // number of frames provided per call
149    size_t mNextIdx;     // index of next entry in mInputIncr to use
150};
151
152/* Creates a buffer filled with a sine wave.
153 *
154 * Returns a pair consisting of the sine signal buffer and the number of frames.
155 * The caller must delete[] the buffer when no longer needed (no shared_ptr<>).
156 */
157template<typename T>
158static std::pair<T*, size_t> createSine(size_t channels,
159        double freq, double samplingRate, double time)
160{
161    double tscale = 1. / samplingRate;
162    size_t frames = static_cast<size_t>(samplingRate * time);
163    T* buffer = new T[frames * channels];
164    for (size_t i = 0; i < frames; ++i) {
165        double t = i * tscale;
166        double y = sin(2. * M_PI * freq * t);
167        T yt = convertValue<T>(y);
168
169        for (size_t j = 0; j < channels; ++j) {
170            buffer[i*channels + j] = yt / (j + 1);
171        }
172    }
173    return std::make_pair(buffer, frames);
174}
175
176/* Creates a buffer filled with a chirp signal (a sine wave sweep).
177 *
178 * Returns a pair consisting of the chirp signal buffer and the number of frames.
179 * The caller must delete[] the buffer when no longer needed (no shared_ptr<>).
180 *
181 * When creating the Chirp, note that the frequency is the true sinusoidal
182 * frequency not the sampling rate.
183 *
184 * http://en.wikipedia.org/wiki/Chirp
185 */
186template<typename T>
187static std::pair<T*, size_t> createChirp(size_t channels,
188        double minfreq, double maxfreq, double samplingRate, double time)
189{
190    double tscale = 1. / samplingRate;
191    size_t frames = static_cast<size_t>(samplingRate * time);
192    T *buffer = new T[frames * channels];
193    // note the chirp constant k has a divide-by-two.
194    double k = (maxfreq - minfreq) / (2. * time);
195    for (size_t i = 0; i < frames; ++i) {
196        double t = i * tscale;
197        double y = sin(2. * M_PI * (k * t + minfreq) * t);
198        T yt = convertValue<T>(y);
199
200        for (size_t j = 0; j < channels; ++j) {
201            buffer[i*channels + j] = yt / (j + 1);
202        }
203    }
204    return std::make_pair(buffer, frames);
205}
206
207/* This derived class creates a buffer provider of datatype T,
208 * consisting of an input signal, e.g. from createChirp().
209 * The number of frames can be obtained from the base class
210 * TestProvider::getNumFrames().
211 */
212template <typename T>
213class SignalProvider : public TestProvider {
214public:
215    SignalProvider(const std::pair<T*, size_t>& bufferInfo, size_t channels,
216            const std::vector<size_t>& values)
217    : TestProvider(bufferInfo.first, bufferInfo.second, channels * sizeof(T), values),
218      mManagedPtr(bufferInfo.first)
219    {
220    }
221
222    virtual ~SignalProvider()
223    {
224        delete[] mManagedPtr;
225    }
226
227protected:
228    T* mManagedPtr;
229};
230
231void resample(void *output, size_t outputFrames, const std::vector<size_t> &outputIncr,
232        android::AudioBufferProvider *provider, android::AudioResampler *resampler)
233{
234    for (size_t i = 0, j = 0; i < outputFrames; ) {
235        size_t thisFrames = outputIncr[j++];
236        if (j >= outputIncr.size()) {
237            j = 0;
238        }
239        if (thisFrames == 0 || thisFrames > outputFrames - i) {
240            thisFrames = outputFrames - i;
241        }
242        resampler->resample((int32_t*) output + 2*i, thisFrames, provider);
243        i += thisFrames;
244    }
245}
246
247void buffercmp(const void *reference, const void *test,
248        size_t outputFrameSize, size_t outputFrames)
249{
250    for (size_t i = 0; i < outputFrames; ++i) {
251        int check = memcmp((const char*)reference + i * outputFrameSize,
252                (const char*)test + i * outputFrameSize, outputFrameSize);
253        if (check) {
254            ALOGE("Failure at frame %d", i);
255            ASSERT_EQ(check, 0); /* fails */
256        }
257    }
258}
259
260void testBufferIncrement(size_t channels, unsigned inputFreq, unsigned outputFreq,
261        enum android::AudioResampler::src_quality quality)
262{
263    // create the provider
264    std::vector<size_t> inputIncr;
265    SignalProvider<int16_t> provider(createChirp<int16_t>(channels,
266            0., outputFreq/2., outputFreq, outputFreq/2000.),
267            channels, inputIncr);
268
269    // calculate the output size
270    size_t outputFrames = ((int64_t) provider.getNumFrames() * outputFreq) / inputFreq;
271    size_t outputFrameSize = 2 * sizeof(int32_t);
272    size_t outputSize = outputFrameSize * outputFrames;
273    outputSize &= ~7;
274
275    // create the resampler
276    const int volumePrecision = 12; /* typical unity gain */
277    android::AudioResampler* resampler;
278
279    resampler = android::AudioResampler::create(16, channels, outputFreq, quality);
280    resampler->setSampleRate(inputFreq);
281    resampler->setVolume(1 << volumePrecision, 1 << volumePrecision);
282
283    // set up the reference run
284    std::vector<size_t> refIncr;
285    refIncr.push_back(outputFrames);
286    void* reference = malloc(outputSize);
287    resample(reference, outputFrames, refIncr, &provider, resampler);
288
289    provider.reset();
290
291#if 0
292    /* this test will fail - API interface issue: reset() does not clear internal buffers */
293    resampler->reset();
294#else
295    delete resampler;
296    resampler = android::AudioResampler::create(16, channels, outputFreq, quality);
297    resampler->setSampleRate(inputFreq);
298    resampler->setVolume(1 << volumePrecision, 1 << volumePrecision);
299#endif
300
301    // set up the test run
302    std::vector<size_t> outIncr;
303    outIncr.push_back(1);
304    outIncr.push_back(2);
305    outIncr.push_back(3);
306    void* test = malloc(outputSize);
307    resample(test, outputFrames, outIncr, &provider, resampler);
308
309    // check
310    buffercmp(reference, test, outputFrameSize, outputFrames);
311
312    free(reference);
313    free(test);
314    delete resampler;
315}
316
317template <typename T>
318inline double sqr(T v)
319{
320    double dv = static_cast<double>(v);
321    return dv * dv;
322}
323
324template <typename T>
325double signalEnergy(T *start, T *end, unsigned stride)
326{
327    double accum = 0;
328
329    for (T *p = start; p < end; p += stride) {
330        accum += sqr(*p);
331    }
332    unsigned count = (end - start + stride - 1) / stride;
333    return accum / count;
334}
335
336void testStopbandDownconversion(size_t channels,
337        unsigned inputFreq, unsigned outputFreq,
338        unsigned passband, unsigned stopband,
339        enum android::AudioResampler::src_quality quality)
340{
341    // create the provider
342    std::vector<size_t> inputIncr;
343    SignalProvider<int16_t> provider(createChirp<int16_t>(channels,
344            0., inputFreq/2., inputFreq, inputFreq/2000.),
345            channels, inputIncr);
346
347    // calculate the output size
348    size_t outputFrames = ((int64_t) provider.getNumFrames() * outputFreq) / inputFreq;
349    size_t outputFrameSize = 2 * sizeof(int32_t);
350    size_t outputSize = outputFrameSize * outputFrames;
351    outputSize &= ~7;
352
353    // create the resampler
354    const int volumePrecision = 12; /* typical unity gain */
355    android::AudioResampler* resampler;
356
357    resampler = android::AudioResampler::create(16, channels, outputFreq, quality);
358    resampler->setSampleRate(inputFreq);
359    resampler->setVolume(1 << volumePrecision, 1 << volumePrecision);
360
361    // set up the reference run
362    std::vector<size_t> refIncr;
363    refIncr.push_back(outputFrames);
364    void* reference = malloc(outputSize);
365    resample(reference, outputFrames, refIncr, &provider, resampler);
366
367    int32_t *out = reinterpret_cast<int32_t *>(reference);
368
369    // check signal energy in passband
370    const unsigned passbandFrame = passband * outputFreq / 1000.;
371    const unsigned stopbandFrame = stopband * outputFreq / 1000.;
372
373    // check each channel separately
374    for (size_t i = 0; i < channels; ++i) {
375        double passbandEnergy = signalEnergy(out, out + passbandFrame * channels, channels);
376        double stopbandEnergy = signalEnergy(out + stopbandFrame * channels,
377                out + outputFrames * channels, channels);
378        double dbAtten = -10. * log10(stopbandEnergy / passbandEnergy);
379        ASSERT_GT(dbAtten, 60.);
380
381#if 0
382        // internal verification
383        printf("if:%d  of:%d  pbf:%d  sbf:%d  sbe: %f  pbe: %f  db: %.2f\n",
384                provider.getNumFrames(), outputFrames,
385                passbandFrame, stopbandFrame, stopbandEnergy, passbandEnergy, dbAtten);
386        for (size_t i = 0; i < 10; ++i) {
387            printf("%d\n", out[i+passbandFrame*channels]);
388        }
389        for (size_t i = 0; i < 10; ++i) {
390            printf("%d\n", out[i+stopbandFrame*channels]);
391        }
392#endif
393    }
394
395    free(reference);
396    delete resampler;
397}
398
399/* Buffer increment test
400 *
401 * We compare a reference output, where we consume and process the entire
402 * buffer at a time, and a test output, where we provide small chunks of input
403 * data and process small chunks of output (which may not be equivalent in size).
404 *
405 * Two subtests - fixed phase (3:2 down) and interpolated phase (147:320 up)
406 */
407TEST(audioflinger_resampler, bufferincrement_fixedphase) {
408    // all of these work
409    static const enum android::AudioResampler::src_quality kQualityArray[] = {
410            android::AudioResampler::LOW_QUALITY,
411            android::AudioResampler::MED_QUALITY,
412            android::AudioResampler::HIGH_QUALITY,
413            android::AudioResampler::VERY_HIGH_QUALITY,
414            android::AudioResampler::DYN_LOW_QUALITY,
415            android::AudioResampler::DYN_MED_QUALITY,
416            android::AudioResampler::DYN_HIGH_QUALITY,
417    };
418
419    for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
420        testBufferIncrement(2, 48000, 32000, kQualityArray[i]);
421    }
422}
423
424TEST(audioflinger_resampler, bufferincrement_interpolatedphase) {
425    // all of these work except low quality
426    static const enum android::AudioResampler::src_quality kQualityArray[] = {
427//           android::AudioResampler::LOW_QUALITY,
428            android::AudioResampler::MED_QUALITY,
429            android::AudioResampler::HIGH_QUALITY,
430            android::AudioResampler::VERY_HIGH_QUALITY,
431            android::AudioResampler::DYN_LOW_QUALITY,
432            android::AudioResampler::DYN_MED_QUALITY,
433            android::AudioResampler::DYN_HIGH_QUALITY,
434    };
435
436    for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
437        testBufferIncrement(2, 22050, 48000, kQualityArray[i]);
438    }
439}
440
441/* Simple aliasing test
442 *
443 * This checks stopband response of the chirp signal to make sure frequencies
444 * are properly suppressed.  It uses downsampling because the stopband can be
445 * clearly isolated by input frequencies exceeding the output sample rate (nyquist).
446 */
447TEST(audioflinger_resampler, stopbandresponse) {
448    // not all of these may work (old resamplers fail on downsampling)
449    static const enum android::AudioResampler::src_quality kQualityArray[] = {
450            //android::AudioResampler::LOW_QUALITY,
451            //android::AudioResampler::MED_QUALITY,
452            //android::AudioResampler::HIGH_QUALITY,
453            //android::AudioResampler::VERY_HIGH_QUALITY,
454            android::AudioResampler::DYN_LOW_QUALITY,
455            android::AudioResampler::DYN_MED_QUALITY,
456            android::AudioResampler::DYN_HIGH_QUALITY,
457    };
458
459    // in this test we assume a maximum transition band between 12kHz and 20kHz.
460    // there must be at least 60dB relative attenuation between stopband and passband.
461    for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
462        testStopbandDownconversion(2, 48000, 32000, 12000, 20000, kQualityArray[i]);
463    }
464
465    // in this test we assume a maximum transition band between 7kHz and 15kHz.
466    // there must be at least 60dB relative attenuation between stopband and passband.
467    // (the weird ratio triggers interpolative resampling)
468    for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
469        testStopbandDownconversion(2, 48000, 22101, 7000, 15000, kQualityArray[i]);
470    }
471}
472