test_utils.h 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#ifndef ANDROID_AUDIO_TEST_UTILS_H
18#define ANDROID_AUDIO_TEST_UTILS_H
19
20#include <audio_utils/sndfile.h>
21
22#ifndef ARRAY_SIZE
23#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
24#endif
25
26template<typename T, typename U>
27struct is_same
28{
29    static const bool value = false;
30};
31
32template<typename T>
33struct is_same<T, T>  // partial specialization
34{
35    static const bool value = true;
36};
37
38template<typename T>
39static inline T convertValue(double val)
40{
41    if (is_same<T, int16_t>::value) {
42        return floor(val * 32767.0 + 0.5);
43    } else if (is_same<T, int32_t>::value) {
44        return floor(val * (1UL<<31) + 0.5);
45    }
46    return val; // assume float or double
47}
48
49// Convert a list of integers in CSV format to a Vector of those values.
50// Returns the number of elements in the list, or -1 on error.
51static inline int parseCSV(const char *string, std::vector<int>& values)
52{
53    // pass 1: count the number of values and do syntax check
54    size_t numValues = 0;
55    bool hadDigit = false;
56    for (const char *p = string; ; ) {
57        switch (*p++) {
58        case '0': case '1': case '2': case '3': case '4':
59        case '5': case '6': case '7': case '8': case '9':
60            hadDigit = true;
61            break;
62        case '\0':
63            if (hadDigit) {
64                // pass 2: allocate and initialize vector of values
65                values.resize(++numValues);
66                values[0] = atoi(p = string);
67                for (size_t i = 1; i < numValues; ) {
68                    if (*p++ == ',') {
69                        values[i++] = atoi(p);
70                    }
71                }
72                return numValues;
73            }
74            // fall through
75        case ',':
76            if (hadDigit) {
77                hadDigit = false;
78                numValues++;
79                break;
80            }
81            // fall through
82        default:
83            return -1;
84        }
85    }
86}
87
88/* Creates a type-independent audio buffer provider from
89 * a buffer base address, size, framesize, and input increment array.
90 *
91 * No allocation or deallocation of the provided buffer is done.
92 */
93class TestProvider : public android::AudioBufferProvider {
94public:
95    TestProvider(void* addr, size_t frames, size_t frameSize,
96            const std::vector<int>& inputIncr)
97    : mAddr(addr),
98      mNumFrames(frames),
99      mFrameSize(frameSize),
100      mNextFrame(0), mUnrel(0), mInputIncr(inputIncr), mNextIdx(0)
101    {
102    }
103
104    TestProvider()
105    : mAddr(NULL), mNumFrames(0), mFrameSize(0),
106      mNextFrame(0), mUnrel(0), mNextIdx(0)
107    {
108    }
109
110    void setIncr(const std::vector<int>& inputIncr) {
111        mInputIncr = inputIncr;
112        mNextIdx = 0;
113    }
114
115    virtual android::status_t getNextBuffer(Buffer* buffer, int64_t pts __unused = kInvalidPTS)
116    {
117        size_t requestedFrames = buffer->frameCount;
118        if (requestedFrames > mNumFrames - mNextFrame) {
119            buffer->frameCount = mNumFrames - mNextFrame;
120        }
121        if (!mInputIncr.empty()) {
122            size_t provided = mInputIncr[mNextIdx++];
123            ALOGV("getNextBuffer() mValue[%d]=%u not %u",
124                    mNextIdx-1, provided, buffer->frameCount);
125            if (provided < buffer->frameCount) {
126                buffer->frameCount = provided;
127            }
128            if (mNextIdx >= mInputIncr.size()) {
129                mNextIdx = 0;
130            }
131        }
132        ALOGV("getNextBuffer() requested %u frames out of %u frames available"
133                " and returned %u frames\n",
134                requestedFrames, mNumFrames - mNextFrame, buffer->frameCount);
135        mUnrel = buffer->frameCount;
136        if (buffer->frameCount > 0) {
137            buffer->raw = (char *)mAddr + mFrameSize * mNextFrame;
138            return android::NO_ERROR;
139        } else {
140            buffer->raw = NULL;
141            return android::NOT_ENOUGH_DATA;
142        }
143    }
144
145    virtual void releaseBuffer(Buffer* buffer)
146    {
147        if (buffer->frameCount > mUnrel) {
148            ALOGE("releaseBuffer() released %u frames but only %u available "
149                    "to release\n", buffer->frameCount, mUnrel);
150            mNextFrame += mUnrel;
151            mUnrel = 0;
152        } else {
153
154            ALOGV("releaseBuffer() released %u frames out of %u frames available "
155                    "to release\n", buffer->frameCount, mUnrel);
156            mNextFrame += buffer->frameCount;
157            mUnrel -= buffer->frameCount;
158        }
159        buffer->frameCount = 0;
160        buffer->raw = NULL;
161    }
162
163    void reset()
164    {
165        mNextFrame = 0;
166    }
167
168    size_t getNumFrames()
169    {
170        return mNumFrames;
171    }
172
173
174protected:
175    void* mAddr;   // base address
176    size_t mNumFrames;   // total frames
177    int mFrameSize;      // frame size (# channels * bytes per sample)
178    size_t mNextFrame;   // index of next frame to provide
179    size_t mUnrel;       // number of frames not yet released
180    std::vector<int> mInputIncr; // number of frames provided per call
181    size_t mNextIdx;     // index of next entry in mInputIncr to use
182};
183
184/* Creates a buffer filled with a sine wave.
185 */
186template<typename T>
187static void createSine(void *vbuffer, size_t frames,
188        size_t channels, double sampleRate, double freq)
189{
190    double tscale = 1. / sampleRate;
191    T* buffer = reinterpret_cast<T*>(vbuffer);
192    for (size_t i = 0; i < frames; ++i) {
193        double t = i * tscale;
194        double y = sin(2. * M_PI * freq * t);
195        T yt = convertValue<T>(y);
196
197        for (size_t j = 0; j < channels; ++j) {
198            buffer[i*channels + j] = yt / (j + 1);
199        }
200    }
201}
202
203/* Creates a buffer filled with a chirp signal (a sine wave sweep).
204 *
205 * When creating the Chirp, note that the frequency is the true sinusoidal
206 * frequency not the sampling rate.
207 *
208 * http://en.wikipedia.org/wiki/Chirp
209 */
210template<typename T>
211static void createChirp(void *vbuffer, size_t frames,
212        size_t channels, double sampleRate,  double minfreq, double maxfreq)
213{
214    double tscale = 1. / sampleRate;
215    T *buffer = reinterpret_cast<T*>(vbuffer);
216    // note the chirp constant k has a divide-by-two.
217    double k = (maxfreq - minfreq) / (2. * tscale * frames);
218    for (size_t i = 0; i < frames; ++i) {
219        double t = i * tscale;
220        double y = sin(2. * M_PI * (k * t + minfreq) * t);
221        T yt = convertValue<T>(y);
222
223        for (size_t j = 0; j < channels; ++j) {
224            buffer[i*channels + j] = yt / (j + 1);
225        }
226    }
227}
228
229/* This derived class creates a buffer provider of datatype T,
230 * consisting of an input signal, e.g. from createChirp().
231 * The number of frames can be obtained from the base class
232 * TestProvider::getNumFrames().
233 */
234
235class SignalProvider : public TestProvider {
236public:
237    SignalProvider()
238    : mSampleRate(0),
239      mChannels(0)
240    {
241    }
242
243    virtual ~SignalProvider()
244    {
245        free(mAddr);
246        mAddr = NULL;
247    }
248
249    template <typename T>
250    void setChirp(size_t channels, double minfreq, double maxfreq, double sampleRate, double time)
251    {
252        createBufferByFrames<T>(channels, sampleRate, sampleRate*time);
253        createChirp<T>(mAddr, mNumFrames, mChannels, mSampleRate, minfreq, maxfreq);
254    }
255
256    template <typename T>
257    void setSine(size_t channels,
258            double freq, double sampleRate, double time)
259    {
260        createBufferByFrames<T>(channels, sampleRate, sampleRate*time);
261        createSine<T>(mAddr, mNumFrames,  mChannels, mSampleRate, freq);
262    }
263
264    template <typename T>
265    void setFile(const char *file_in)
266    {
267        SF_INFO info;
268        info.format = 0;
269        SNDFILE *sf = sf_open(file_in, SFM_READ, &info);
270        if (sf == NULL) {
271            perror(file_in);
272            return;
273        }
274        createBufferByFrames<T>(info.channels, info.samplerate, info.frames);
275        if (is_same<T, float>::value) {
276            (void) sf_readf_float(sf, (float *) mAddr, mNumFrames);
277        } else if (is_same<T, short>::value) {
278            (void) sf_readf_short(sf, (short *) mAddr, mNumFrames);
279        }
280        sf_close(sf);
281    }
282
283    template <typename T>
284    void createBufferByFrames(size_t channels, uint32_t sampleRate, size_t frames)
285    {
286        mNumFrames = frames;
287        mChannels = channels;
288        mFrameSize = mChannels * sizeof(T);
289        free(mAddr);
290        mAddr = malloc(mFrameSize * mNumFrames);
291        mSampleRate = sampleRate;
292    }
293
294    uint32_t getSampleRate() const {
295        return mSampleRate;
296    }
297
298    uint32_t getNumChannels() const {
299        return mChannels;
300    }
301
302protected:
303    uint32_t mSampleRate;
304    uint32_t mChannels;
305};
306
307#endif // ANDROID_AUDIO_TEST_UTILS_H
308