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