Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c0e5ec8e authored by Andy Hung's avatar Andy Hung
Browse files

Extract out test utility functions from resampler_tests

These will be used for mixer tests later.

Change-Id: I22b1c89857058cfb2450afe15e80d6c9306a31f1
parent 3a227969
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
# Build the unit tests for audioflinger

#
# resampler unit test
#
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)

@@ -20,6 +23,7 @@ LOCAL_C_INCLUDES := \
	bionic/libstdc++/include \
	external/gtest/include \
	external/stlport/stlport \
	$(call include-path-for, audio-utils) \
	frameworks/av/services/audioflinger

LOCAL_SRC_FILES := \
+11 −202
Original line number Diff line number Diff line
@@ -33,200 +33,7 @@
#include <gtest/gtest.h>
#include <media/AudioBufferProvider.h>
#include "AudioResampler.h"

#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))

template<typename T, typename U>
struct is_same
{
    static const bool value = false;
};

template<typename T>
struct is_same<T, T>  // partial specialization
{
    static const bool value = true;
};

template<typename T>
static inline T convertValue(double val)
{
    if (is_same<T, int16_t>::value) {
        return floor(val * 32767.0 + 0.5);
    } else if (is_same<T, int32_t>::value) {
        return floor(val * (1UL<<31) + 0.5);
    }
    return val; // assume float or double
}

/* Creates a type-independent audio buffer provider from
 * a buffer base address, size, framesize, and input increment array.
 *
 * No allocation or deallocation of the provided buffer is done.
 */
class TestProvider : public android::AudioBufferProvider {
public:
    TestProvider(const void* addr, size_t frames, size_t frameSize,
            const std::vector<size_t>& inputIncr)
    : mAddr(addr),
      mNumFrames(frames),
      mFrameSize(frameSize),
      mNextFrame(0), mUnrel(0), mInputIncr(inputIncr), mNextIdx(0)
    {
    }

    virtual android::status_t getNextBuffer(Buffer* buffer, int64_t pts __unused = kInvalidPTS )
    {
        size_t requestedFrames = buffer->frameCount;
        if (requestedFrames > mNumFrames - mNextFrame) {
            buffer->frameCount = mNumFrames - mNextFrame;
        }
        if (!mInputIncr.empty()) {
            size_t provided = mInputIncr[mNextIdx++];
            ALOGV("getNextBuffer() mValue[%d]=%u not %u",
                    mNextIdx-1, provided, buffer->frameCount);
            if (provided < buffer->frameCount) {
                buffer->frameCount = provided;
            }
            if (mNextIdx >= mInputIncr.size()) {
                mNextIdx = 0;
            }
        }
        ALOGV("getNextBuffer() requested %u frames out of %u frames available"
                " and returned %u frames\n",
                requestedFrames, mNumFrames - mNextFrame, buffer->frameCount);
        mUnrel = buffer->frameCount;
        if (buffer->frameCount > 0) {
            buffer->raw = (char *)mAddr + mFrameSize * mNextFrame;
            return android::NO_ERROR;
        } else {
            buffer->raw = NULL;
            return android::NOT_ENOUGH_DATA;
        }
    }

    virtual void releaseBuffer(Buffer* buffer)
    {
        if (buffer->frameCount > mUnrel) {
            ALOGE("releaseBuffer() released %u frames but only %u available "
                    "to release\n", buffer->frameCount, mUnrel);
            mNextFrame += mUnrel;
            mUnrel = 0;
        } else {

            ALOGV("releaseBuffer() released %u frames out of %u frames available "
                    "to release\n", buffer->frameCount, mUnrel);
            mNextFrame += buffer->frameCount;
            mUnrel -= buffer->frameCount;
        }
        buffer->frameCount = 0;
        buffer->raw = NULL;
    }

    void reset()
    {
        mNextFrame = 0;
    }

    size_t getNumFrames()
    {
        return mNumFrames;
    }

    void setIncr(const std::vector<size_t> inputIncr)
    {
        mNextIdx = 0;
        mInputIncr = inputIncr;
    }

protected:
    const void* mAddr;   // base address
    size_t mNumFrames;   // total frames
    int mFrameSize;      // frame size (# channels * bytes per sample)
    size_t mNextFrame;   // index of next frame to provide
    size_t mUnrel;       // number of frames not yet released
    std::vector<size_t> mInputIncr; // number of frames provided per call
    size_t mNextIdx;     // index of next entry in mInputIncr to use
};

/* Creates a buffer filled with a sine wave.
 *
 * Returns a pair consisting of the sine signal buffer and the number of frames.
 * The caller must delete[] the buffer when no longer needed (no shared_ptr<>).
 */
template<typename T>
static std::pair<T*, size_t> createSine(size_t channels,
        double freq, double samplingRate, double time)
{
    double tscale = 1. / samplingRate;
    size_t frames = static_cast<size_t>(samplingRate * time);
    T* buffer = new T[frames * channels];
    for (size_t i = 0; i < frames; ++i) {
        double t = i * tscale;
        double y = sin(2. * M_PI * freq * t);
        T yt = convertValue<T>(y);

        for (size_t j = 0; j < channels; ++j) {
            buffer[i*channels + j] = yt / (j + 1);
        }
    }
    return std::make_pair(buffer, frames);
}

/* Creates a buffer filled with a chirp signal (a sine wave sweep).
 *
 * Returns a pair consisting of the chirp signal buffer and the number of frames.
 * The caller must delete[] the buffer when no longer needed (no shared_ptr<>).
 *
 * When creating the Chirp, note that the frequency is the true sinusoidal
 * frequency not the sampling rate.
 *
 * http://en.wikipedia.org/wiki/Chirp
 */
template<typename T>
static std::pair<T*, size_t> createChirp(size_t channels,
        double minfreq, double maxfreq, double samplingRate, double time)
{
    double tscale = 1. / samplingRate;
    size_t frames = static_cast<size_t>(samplingRate * time);
    T *buffer = new T[frames * channels];
    // note the chirp constant k has a divide-by-two.
    double k = (maxfreq - minfreq) / (2. * time);
    for (size_t i = 0; i < frames; ++i) {
        double t = i * tscale;
        double y = sin(2. * M_PI * (k * t + minfreq) * t);
        T yt = convertValue<T>(y);

        for (size_t j = 0; j < channels; ++j) {
            buffer[i*channels + j] = yt / (j + 1);
        }
    }
    return std::make_pair(buffer, frames);
}

/* This derived class creates a buffer provider of datatype T,
 * consisting of an input signal, e.g. from createChirp().
 * The number of frames can be obtained from the base class
 * TestProvider::getNumFrames().
 */
template <typename T>
class SignalProvider : public TestProvider {
public:
    SignalProvider(const std::pair<T*, size_t>& bufferInfo, size_t channels,
            const std::vector<size_t>& values)
    : TestProvider(bufferInfo.first, bufferInfo.second, channels * sizeof(T), values),
      mManagedPtr(bufferInfo.first)
    {
    }

    virtual ~SignalProvider()
    {
        delete[] mManagedPtr;
    }

protected:
    T* mManagedPtr;
};
#include "test_utils.h"

void resample(void *output, size_t outputFrames, const std::vector<size_t> &outputIncr,
        android::AudioBufferProvider *provider, android::AudioResampler *resampler)
@@ -261,10 +68,11 @@ void testBufferIncrement(size_t channels, unsigned inputFreq, unsigned outputFre
        enum android::AudioResampler::src_quality quality)
{
    // create the provider
    std::vector<size_t> inputIncr;
    SignalProvider<int16_t> provider(createChirp<int16_t>(channels,
            0., outputFreq/2., outputFreq, outputFreq/2000.),
            channels, inputIncr);
    std::vector<int> inputIncr;
    SignalProvider provider;
    provider.setChirp<int16_t>(channels,
            0., outputFreq/2., outputFreq, outputFreq/2000.);
    provider.setIncr(inputIncr);

    // calculate the output size
    size_t outputFrames = ((int64_t) provider.getNumFrames() * outputFreq) / inputFreq;
@@ -339,10 +147,11 @@ void testStopbandDownconversion(size_t channels,
        enum android::AudioResampler::src_quality quality)
{
    // create the provider
    std::vector<size_t> inputIncr;
    SignalProvider<int16_t> provider(createChirp<int16_t>(channels,
            0., inputFreq/2., inputFreq, inputFreq/2000.),
            channels, inputIncr);
    std::vector<int> inputIncr;
    SignalProvider provider;
    provider.setChirp<int16_t>(channels,
            0., inputFreq/2., inputFreq, inputFreq/2000.);
    provider.setIncr(inputIncr);

    // calculate the output size
    size_t outputFrames = ((int64_t) provider.getNumFrames() * outputFreq) / inputFreq;
+307 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef ANDROID_AUDIO_TEST_UTILS_H
#define ANDROID_AUDIO_TEST_UTILS_H

#include <audio_utils/sndfile.h>

#ifndef ARRAY_SIZE
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
#endif

template<typename T, typename U>
struct is_same
{
    static const bool value = false;
};

template<typename T>
struct is_same<T, T>  // partial specialization
{
    static const bool value = true;
};

template<typename T>
static inline T convertValue(double val)
{
    if (is_same<T, int16_t>::value) {
        return floor(val * 32767.0 + 0.5);
    } else if (is_same<T, int32_t>::value) {
        return floor(val * (1UL<<31) + 0.5);
    }
    return val; // assume float or double
}

// Convert a list of integers in CSV format to a Vector of those values.
// Returns the number of elements in the list, or -1 on error.
static inline int parseCSV(const char *string, std::vector<int>& values)
{
    // pass 1: count the number of values and do syntax check
    size_t numValues = 0;
    bool hadDigit = false;
    for (const char *p = string; ; ) {
        switch (*p++) {
        case '0': case '1': case '2': case '3': case '4':
        case '5': case '6': case '7': case '8': case '9':
            hadDigit = true;
            break;
        case '\0':
            if (hadDigit) {
                // pass 2: allocate and initialize vector of values
                values.resize(++numValues);
                values[0] = atoi(p = string);
                for (size_t i = 1; i < numValues; ) {
                    if (*p++ == ',') {
                        values[i++] = atoi(p);
                    }
                }
                return numValues;
            }
            // fall through
        case ',':
            if (hadDigit) {
                hadDigit = false;
                numValues++;
                break;
            }
            // fall through
        default:
            return -1;
        }
    }
}

/* Creates a type-independent audio buffer provider from
 * a buffer base address, size, framesize, and input increment array.
 *
 * No allocation or deallocation of the provided buffer is done.
 */
class TestProvider : public android::AudioBufferProvider {
public:
    TestProvider(void* addr, size_t frames, size_t frameSize,
            const std::vector<int>& inputIncr)
    : mAddr(addr),
      mNumFrames(frames),
      mFrameSize(frameSize),
      mNextFrame(0), mUnrel(0), mInputIncr(inputIncr), mNextIdx(0)
    {
    }

    TestProvider()
    : mAddr(NULL), mNumFrames(0), mFrameSize(0),
      mNextFrame(0), mUnrel(0), mNextIdx(0)
    {
    }

    void setIncr(const std::vector<int>& inputIncr) {
        mInputIncr = inputIncr;
        mNextIdx = 0;
    }

    virtual android::status_t getNextBuffer(Buffer* buffer, int64_t pts __unused = kInvalidPTS)
    {
        size_t requestedFrames = buffer->frameCount;
        if (requestedFrames > mNumFrames - mNextFrame) {
            buffer->frameCount = mNumFrames - mNextFrame;
        }
        if (!mInputIncr.empty()) {
            size_t provided = mInputIncr[mNextIdx++];
            ALOGV("getNextBuffer() mValue[%d]=%u not %u",
                    mNextIdx-1, provided, buffer->frameCount);
            if (provided < buffer->frameCount) {
                buffer->frameCount = provided;
            }
            if (mNextIdx >= mInputIncr.size()) {
                mNextIdx = 0;
            }
        }
        ALOGV("getNextBuffer() requested %u frames out of %u frames available"
                " and returned %u frames\n",
                requestedFrames, mNumFrames - mNextFrame, buffer->frameCount);
        mUnrel = buffer->frameCount;
        if (buffer->frameCount > 0) {
            buffer->raw = (char *)mAddr + mFrameSize * mNextFrame;
            return android::NO_ERROR;
        } else {
            buffer->raw = NULL;
            return android::NOT_ENOUGH_DATA;
        }
    }

    virtual void releaseBuffer(Buffer* buffer)
    {
        if (buffer->frameCount > mUnrel) {
            ALOGE("releaseBuffer() released %u frames but only %u available "
                    "to release\n", buffer->frameCount, mUnrel);
            mNextFrame += mUnrel;
            mUnrel = 0;
        } else {

            ALOGV("releaseBuffer() released %u frames out of %u frames available "
                    "to release\n", buffer->frameCount, mUnrel);
            mNextFrame += buffer->frameCount;
            mUnrel -= buffer->frameCount;
        }
        buffer->frameCount = 0;
        buffer->raw = NULL;
    }

    void reset()
    {
        mNextFrame = 0;
    }

    size_t getNumFrames()
    {
        return mNumFrames;
    }


protected:
    void* mAddr;   // base address
    size_t mNumFrames;   // total frames
    int mFrameSize;      // frame size (# channels * bytes per sample)
    size_t mNextFrame;   // index of next frame to provide
    size_t mUnrel;       // number of frames not yet released
    std::vector<int> mInputIncr; // number of frames provided per call
    size_t mNextIdx;     // index of next entry in mInputIncr to use
};

/* Creates a buffer filled with a sine wave.
 */
template<typename T>
static void createSine(void *vbuffer, size_t frames,
        size_t channels, double sampleRate, double freq)
{
    double tscale = 1. / sampleRate;
    T* buffer = reinterpret_cast<T*>(vbuffer);
    for (size_t i = 0; i < frames; ++i) {
        double t = i * tscale;
        double y = sin(2. * M_PI * freq * t);
        T yt = convertValue<T>(y);

        for (size_t j = 0; j < channels; ++j) {
            buffer[i*channels + j] = yt / (j + 1);
        }
    }
}

/* Creates a buffer filled with a chirp signal (a sine wave sweep).
 *
 * When creating the Chirp, note that the frequency is the true sinusoidal
 * frequency not the sampling rate.
 *
 * http://en.wikipedia.org/wiki/Chirp
 */
template<typename T>
static void createChirp(void *vbuffer, size_t frames,
        size_t channels, double sampleRate,  double minfreq, double maxfreq)
{
    double tscale = 1. / sampleRate;
    T *buffer = reinterpret_cast<T*>(vbuffer);
    // note the chirp constant k has a divide-by-two.
    double k = (maxfreq - minfreq) / (2. * tscale * frames);
    for (size_t i = 0; i < frames; ++i) {
        double t = i * tscale;
        double y = sin(2. * M_PI * (k * t + minfreq) * t);
        T yt = convertValue<T>(y);

        for (size_t j = 0; j < channels; ++j) {
            buffer[i*channels + j] = yt / (j + 1);
        }
    }
}

/* This derived class creates a buffer provider of datatype T,
 * consisting of an input signal, e.g. from createChirp().
 * The number of frames can be obtained from the base class
 * TestProvider::getNumFrames().
 */

class SignalProvider : public TestProvider {
public:
    SignalProvider()
    : mSampleRate(0),
      mChannels(0)
    {
    }

    virtual ~SignalProvider()
    {
        free(mAddr);
        mAddr = NULL;
    }

    template <typename T>
    void setChirp(size_t channels, double minfreq, double maxfreq, double sampleRate, double time)
    {
        createBufferByFrames<T>(channels, sampleRate, sampleRate*time);
        createChirp<T>(mAddr, mNumFrames, mChannels, mSampleRate, minfreq, maxfreq);
    }

    template <typename T>
    void setSine(size_t channels,
            double freq, double sampleRate, double time)
    {
        createBufferByFrames<T>(channels, sampleRate, sampleRate*time);
        createSine<T>(mAddr, mNumFrames,  mChannels, mSampleRate, freq);
    }

    template <typename T>
    void setFile(const char *file_in)
    {
        SF_INFO info;
        info.format = 0;
        SNDFILE *sf = sf_open(file_in, SFM_READ, &info);
        if (sf == NULL) {
            perror(file_in);
            return;
        }
        createBufferByFrames<T>(info.channels, info.samplerate, info.frames);
        if (is_same<T, float>::value) {
            (void) sf_readf_float(sf, (float *) mAddr, mNumFrames);
        } else if (is_same<T, short>::value) {
            (void) sf_readf_short(sf, (short *) mAddr, mNumFrames);
        }
        sf_close(sf);
    }

    template <typename T>
    void createBufferByFrames(size_t channels, uint32_t sampleRate, size_t frames)
    {
        mNumFrames = frames;
        mChannels = channels;
        mFrameSize = mChannels * sizeof(T);
        free(mAddr);
        mAddr = malloc(mFrameSize * mNumFrames);
        mSampleRate = sampleRate;
    }

    uint32_t getSampleRate() const {
        return mSampleRate;
    }

    uint32_t getNumChannels() const {
        return mChannels;
    }

protected:
    uint32_t mSampleRate;
    uint32_t mChannels;
};

#endif // ANDROID_AUDIO_TEST_UTILS_H