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