From c0e5ec8e2d8db15b97094374d0a248e041304b62 Mon Sep 17 00:00:00 2001 From: Andy Hung Date: Tue, 17 Jun 2014 14:33:39 -0700 Subject: Extract out test utility functions from resampler_tests These will be used for mixer tests later. Change-Id: I22b1c89857058cfb2450afe15e80d6c9306a31f1 --- services/audioflinger/tests/Android.mk | 4 + services/audioflinger/tests/resampler_tests.cpp | 213 +--------------- services/audioflinger/tests/test_utils.h | 307 ++++++++++++++++++++++++ 3 files changed, 322 insertions(+), 202 deletions(-) create mode 100644 services/audioflinger/tests/test_utils.h (limited to 'services/audioflinger/tests') diff --git a/services/audioflinger/tests/Android.mk b/services/audioflinger/tests/Android.mk index 874f18f..f365637 100644 --- a/services/audioflinger/tests/Android.mk +++ b/services/audioflinger/tests/Android.mk @@ -1,5 +1,8 @@ # 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 := \ diff --git a/services/audioflinger/tests/resampler_tests.cpp b/services/audioflinger/tests/resampler_tests.cpp index 8f9c270..4a67d0b 100644 --- a/services/audioflinger/tests/resampler_tests.cpp +++ b/services/audioflinger/tests/resampler_tests.cpp @@ -33,200 +33,7 @@ #include #include #include "AudioResampler.h" - -#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) - -template -struct is_same -{ - static const bool value = false; -}; - -template -struct is_same // partial specialization -{ - static const bool value = true; -}; - -template -static inline T convertValue(double val) -{ - if (is_same::value) { - return floor(val * 32767.0 + 0.5); - } else if (is_same::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& 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 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 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 -static std::pair createSine(size_t channels, - double freq, double samplingRate, double time) -{ - double tscale = 1. / samplingRate; - size_t frames = static_cast(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(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 -static std::pair createChirp(size_t channels, - double minfreq, double maxfreq, double samplingRate, double time) -{ - double tscale = 1. / samplingRate; - size_t frames = static_cast(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(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 -class SignalProvider : public TestProvider { -public: - SignalProvider(const std::pair& bufferInfo, size_t channels, - const std::vector& 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 &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 inputIncr; - SignalProvider provider(createChirp(channels, - 0., outputFreq/2., outputFreq, outputFreq/2000.), - channels, inputIncr); + std::vector inputIncr; + SignalProvider provider; + provider.setChirp(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 inputIncr; - SignalProvider provider(createChirp(channels, - 0., inputFreq/2., inputFreq, inputFreq/2000.), - channels, inputIncr); + std::vector inputIncr; + SignalProvider provider; + provider.setChirp(channels, + 0., inputFreq/2., inputFreq, inputFreq/2000.); + provider.setIncr(inputIncr); // calculate the output size size_t outputFrames = ((int64_t) provider.getNumFrames() * outputFreq) / inputFreq; diff --git a/services/audioflinger/tests/test_utils.h b/services/audioflinger/tests/test_utils.h new file mode 100644 index 0000000..f954292 --- /dev/null +++ b/services/audioflinger/tests/test_utils.h @@ -0,0 +1,307 @@ +/* + * 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 + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) +#endif + +template +struct is_same +{ + static const bool value = false; +}; + +template +struct is_same // partial specialization +{ + static const bool value = true; +}; + +template +static inline T convertValue(double val) +{ + if (is_same::value) { + return floor(val * 32767.0 + 0.5); + } else if (is_same::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& 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& 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& 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 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 +static void createSine(void *vbuffer, size_t frames, + size_t channels, double sampleRate, double freq) +{ + double tscale = 1. / sampleRate; + T* buffer = reinterpret_cast(vbuffer); + for (size_t i = 0; i < frames; ++i) { + double t = i * tscale; + double y = sin(2. * M_PI * freq * t); + T yt = convertValue(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 +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(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(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 + void setChirp(size_t channels, double minfreq, double maxfreq, double sampleRate, double time) + { + createBufferByFrames(channels, sampleRate, sampleRate*time); + createChirp(mAddr, mNumFrames, mChannels, mSampleRate, minfreq, maxfreq); + } + + template + void setSine(size_t channels, + double freq, double sampleRate, double time) + { + createBufferByFrames(channels, sampleRate, sampleRate*time); + createSine(mAddr, mNumFrames, mChannels, mSampleRate, freq); + } + + template + 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(info.channels, info.samplerate, info.frames); + if (is_same::value) { + (void) sf_readf_float(sf, (float *) mAddr, mNumFrames); + } else if (is_same::value) { + (void) sf_readf_short(sf, (short *) mAddr, mNumFrames); + } + sf_close(sf); + } + + template + 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 -- cgit v1.1