1/*
2 * Copyright (C) 2017 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// Record input using AAudio and display the peak amplitudes.
18
19#ifndef AAUDIO_SIMPLE_RECORDER_H
20#define AAUDIO_SIMPLE_RECORDER_H
21
22#include <aaudio/AAudio.h>
23#include "AAudioArgsParser.h"
24
25//#define SHARING_MODE  AAUDIO_SHARING_MODE_EXCLUSIVE
26#define SHARING_MODE  AAUDIO_SHARING_MODE_SHARED
27#define PERFORMANCE_MODE AAUDIO_PERFORMANCE_MODE_NONE
28
29/**
30 * Simple wrapper for AAudio that opens an input stream either in callback or blocking read mode.
31 */
32class AAudioSimpleRecorder {
33public:
34    AAudioSimpleRecorder() {}
35    ~AAudioSimpleRecorder() {
36        close();
37    };
38
39    /**
40     * Call this before calling open().
41     * @param requestedSharingMode
42     */
43    void setSharingMode(aaudio_sharing_mode_t requestedSharingMode) {
44        mRequestedSharingMode = requestedSharingMode;
45    }
46
47    /**
48     * Call this before calling open().
49     * @param requestedPerformanceMode
50     */
51    void setPerformanceMode(aaudio_performance_mode_t requestedPerformanceMode) {
52        mRequestedPerformanceMode = requestedPerformanceMode;
53    }
54
55    /**
56     * Also known as "sample rate"
57     * Only call this after open() has been called.
58     */
59    int32_t getFramesPerSecond() const {
60        return getSampleRate(); // alias
61    }
62
63    /**
64     * Only call this after open() has been called.
65     */
66    int32_t getSampleRate() const {
67        if (mStream == nullptr) {
68            return AAUDIO_ERROR_INVALID_STATE;
69        }
70        return AAudioStream_getSampleRate(mStream);
71    }
72
73    /**
74     * Only call this after open() has been called.
75     */
76    int32_t getChannelCount() {
77        if (mStream == nullptr) {
78            return AAUDIO_ERROR_INVALID_STATE;
79        }
80        return AAudioStream_getChannelCount(mStream);;
81    }
82
83    /**
84     * @deprecated use getChannelCount()
85     */
86    int32_t getSamplesPerFrame() {
87        return getChannelCount();
88    }
89
90    /**
91     * Only call this after open() has been called.
92     */
93    int64_t getFramesRead() {
94        if (mStream == nullptr) {
95            return AAUDIO_ERROR_INVALID_STATE;
96        }
97        return AAudioStream_getFramesRead(mStream);
98    }
99
100    aaudio_result_t open(const AAudioParameters &parameters,
101                         AAudioStream_dataCallback dataCallback = nullptr,
102                         AAudioStream_errorCallback errorCallback = nullptr,
103                         void *userContext = nullptr) {
104        aaudio_result_t result = AAUDIO_OK;
105
106        // Use an AAudioStreamBuilder to contain requested parameters.
107        AAudioStreamBuilder *builder = nullptr;
108        result = AAudio_createStreamBuilder(&builder);
109        if (result != AAUDIO_OK) return result;
110
111        parameters.applyParameters(builder); // apply args
112
113        AAudioStreamBuilder_setDirection(builder, AAUDIO_DIRECTION_INPUT);
114
115        if (dataCallback != nullptr) {
116            AAudioStreamBuilder_setDataCallback(builder, dataCallback, userContext);
117        }
118        if (errorCallback != nullptr) {
119            AAudioStreamBuilder_setErrorCallback(builder, errorCallback, userContext);
120        }
121
122        // Open an AAudioStream using the Builder.
123        result = AAudioStreamBuilder_openStream(builder, &mStream);
124        if (result != AAUDIO_OK) {
125            fprintf(stderr, "ERROR - AAudioStreamBuilder_openStream() returned %d %s\n",
126                    result, AAudio_convertResultToText(result));
127        }
128
129        if (result == AAUDIO_OK) {
130            int32_t sizeInBursts = parameters.getNumberOfBursts();
131            if (sizeInBursts > 0) {
132                int32_t framesPerBurst = AAudioStream_getFramesPerBurst(mStream);
133                AAudioStream_setBufferSizeInFrames(mStream, sizeInBursts * framesPerBurst);
134            }
135        }
136
137        AAudioStreamBuilder_delete(builder);
138        return result;
139    }
140
141    /**
142     * Open a stream
143     */
144    aaudio_result_t open(int channelCount, int sampSampleRate, aaudio_format_t format,
145                         AAudioStream_dataCallback dataProc,
146                         AAudioStream_errorCallback errorProc,
147                         void *userContext) {
148        aaudio_result_t result = AAUDIO_OK;
149
150        // Use an AAudioStreamBuilder to contain requested parameters.
151        AAudioStreamBuilder *builder = nullptr;
152        result = AAudio_createStreamBuilder(&builder);
153        if (result != AAUDIO_OK) return result;
154
155        AAudioStreamBuilder_setDirection(builder, AAUDIO_DIRECTION_INPUT);
156        AAudioStreamBuilder_setPerformanceMode(builder, mRequestedPerformanceMode);
157        AAudioStreamBuilder_setSharingMode(builder, mRequestedSharingMode);
158        if (dataProc != nullptr) {
159            AAudioStreamBuilder_setDataCallback(builder, dataProc, userContext);
160        }
161        if (errorProc != nullptr) {
162            AAudioStreamBuilder_setErrorCallback(builder, errorProc, userContext);
163        }
164        AAudioStreamBuilder_setChannelCount(builder, channelCount);
165        AAudioStreamBuilder_setSampleRate(builder, sampSampleRate);
166        AAudioStreamBuilder_setFormat(builder, format);
167
168        // Open an AAudioStream using the Builder.
169        result = AAudioStreamBuilder_openStream(builder, &mStream);
170        if (result != AAUDIO_OK) {
171            fprintf(stderr, "ERROR - AAudioStreamBuilder_openStream() returned %d %s\n",
172                    result, AAudio_convertResultToText(result));
173        }
174
175        AAudioStreamBuilder_delete(builder);
176        return result;
177    }
178
179    aaudio_result_t close() {
180        if (mStream != nullptr) {
181            printf("call AAudioStream_close(%p)\n", mStream);  fflush(stdout);
182            AAudioStream_close(mStream);
183            mStream = nullptr;
184        }
185        return AAUDIO_OK;
186    }
187
188    // Write zero data to fill up the buffer and prevent underruns.
189    aaudio_result_t prime() {
190        int32_t samplesPerFrame = AAudioStream_getChannelCount(mStream);
191        const int numFrames = 32; // arbitrary
192        float zeros[numFrames * samplesPerFrame];
193        memset(zeros, 0, sizeof(zeros));
194        aaudio_result_t result = numFrames;
195        while (result == numFrames) {
196            result = AAudioStream_write(mStream, zeros, numFrames, 0);
197        }
198        return result;
199    }
200
201    // Start the stream. AAudio will start calling your callback function.
202     aaudio_result_t start() {
203        aaudio_result_t result = AAudioStream_requestStart(mStream);
204        if (result != AAUDIO_OK) {
205            fprintf(stderr, "ERROR - AAudioStream_requestStart() returned %d %s\n",
206                    result, AAudio_convertResultToText(result));
207        }
208        return result;
209    }
210
211    // Stop the stream. AAudio will stop calling your callback function.
212    aaudio_result_t stop() {
213        aaudio_result_t result = AAudioStream_requestStop(mStream);
214        if (result != AAUDIO_OK) {
215            fprintf(stderr, "ERROR - AAudioStream_requestStop() returned %d %s\n",
216                    result, AAudio_convertResultToText(result));
217        }
218        return result;
219    }
220
221    // Pause the stream. AAudio will stop calling your callback function.
222    aaudio_result_t pause() {
223        aaudio_result_t result = AAudioStream_requestPause(mStream);
224        if (result != AAUDIO_OK) {
225            fprintf(stderr, "ERROR - AAudioStream_requestPause() returned %d %s\n",
226                    result, AAudio_convertResultToText(result));
227        }
228        return result;
229    }
230
231    AAudioStream *getStream() const {
232        return mStream;
233    }
234
235private:
236    AAudioStream             *mStream = nullptr;
237    aaudio_sharing_mode_t     mRequestedSharingMode = SHARING_MODE;
238    aaudio_performance_mode_t mRequestedPerformanceMode = PERFORMANCE_MODE;
239};
240
241// Application data that gets passed to the callback.
242typedef struct PeakTrackerData {
243    float peakLevel;
244} PeakTrackerData_t;
245
246#define DECAY_FACTOR   0.999
247
248// Callback function that fills the audio output buffer.
249aaudio_data_callback_result_t SimpleRecorderDataCallbackProc(
250        AAudioStream *stream,
251        void *userData,
252        void *audioData,
253        int32_t numFrames
254        ) {
255
256    // should not happen but just in case...
257    if (userData == nullptr) {
258        fprintf(stderr, "ERROR - SimpleRecorderDataCallbackProc needs userData\n");
259        return AAUDIO_CALLBACK_RESULT_STOP;
260    }
261    PeakTrackerData_t *data = (PeakTrackerData_t *) userData;
262    // printf("MyCallbackProc(): frameCount = %d\n", numFrames);
263    int32_t samplesPerFrame = AAudioStream_getChannelCount(stream);
264    float sample;
265    // This code assume mono or stereo.
266    switch (AAudioStream_getFormat(stream)) {
267        case AAUDIO_FORMAT_PCM_I16: {
268            int16_t *audioBuffer = (int16_t *) audioData;
269            // Peak follower
270            for (int frameIndex = 0; frameIndex < numFrames; frameIndex++) {
271                sample = audioBuffer[frameIndex * samplesPerFrame] * (1.0/32768);
272                data->peakLevel *= DECAY_FACTOR;
273                if (sample > data->peakLevel) {
274                    data->peakLevel = sample;
275                }
276            }
277        }
278        break;
279        case AAUDIO_FORMAT_PCM_FLOAT: {
280            float *audioBuffer = (float *) audioData;
281            // Peak follower
282            for (int frameIndex = 0; frameIndex < numFrames; frameIndex++) {
283                sample = audioBuffer[frameIndex * samplesPerFrame];
284                data->peakLevel *= DECAY_FACTOR;
285                if (sample > data->peakLevel) {
286                    data->peakLevel = sample;
287                }
288            }
289        }
290        break;
291        default:
292            return AAUDIO_CALLBACK_RESULT_STOP;
293    }
294
295    return AAUDIO_CALLBACK_RESULT_CONTINUE;
296}
297
298void SimpleRecorderErrorCallbackProc(
299        AAudioStream *stream __unused,
300        void *userData __unused,
301        aaudio_result_t error)
302{
303    printf("Error Callback, error: %d\n",(int)error);
304}
305
306#endif //AAUDIO_SIMPLE_RECORDER_H
307