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// Play sine waves using an AAudio callback.
18
19#ifndef AAUDIO_SIMPLE_PLAYER_H
20#define AAUDIO_SIMPLE_PLAYER_H
21
22#include <unistd.h>
23#include <sched.h>
24
25#include <aaudio/AAudio.h>
26#include <atomic>
27#include "AAudioArgsParser.h"
28#include "SineGenerator.h"
29
30//#define SHARING_MODE  AAUDIO_SHARING_MODE_EXCLUSIVE
31#define SHARING_MODE  AAUDIO_SHARING_MODE_SHARED
32#define PERFORMANCE_MODE AAUDIO_PERFORMANCE_MODE_NONE
33
34// Arbitrary period for glitches, once per second at 48000 Hz.
35#define FORCED_UNDERRUN_PERIOD_FRAMES    48000
36// How long to sleep in a callback to cause an intentional glitch. For testing.
37#define FORCED_UNDERRUN_SLEEP_MICROS     (10 * 1000)
38
39#define MAX_TIMESTAMPS   16
40
41typedef struct Timestamp {
42    int64_t position;
43    int64_t nanoseconds;
44} Timestamp;
45
46/**
47 * Simple wrapper for AAudio that opens an output stream either in callback or blocking write mode.
48 */
49class AAudioSimplePlayer {
50public:
51    AAudioSimplePlayer() {}
52    ~AAudioSimplePlayer() {
53        close();
54    };
55
56    /**
57     * Call this before calling open().
58     * @param requestedSharingMode
59     */
60    void setSharingMode(aaudio_sharing_mode_t requestedSharingMode) {
61        mRequestedSharingMode = requestedSharingMode;
62    }
63
64    /**
65     * Call this before calling open().
66     * @param requestedPerformanceMode
67     */
68    void setPerformanceMode(aaudio_performance_mode_t requestedPerformanceMode) {
69        mRequestedPerformanceMode = requestedPerformanceMode;
70    }
71
72    // TODO Extract a common base class for record and playback.
73    /**
74     * Also known as "sample rate"
75     * Only call this after open() has been called.
76     */
77    int32_t getFramesPerSecond() const {
78        return getSampleRate(); // alias
79    }
80
81    /**
82     * Only call this after open() has been called.
83     */
84    int32_t getSampleRate() const {
85        if (mStream == nullptr) {
86            return AAUDIO_ERROR_INVALID_STATE;
87        }
88        return AAudioStream_getSampleRate(mStream);
89    }
90
91    /**
92     * Only call this after open() has been called.
93     */
94    int32_t getChannelCount() {
95        if (mStream == nullptr) {
96            return AAUDIO_ERROR_INVALID_STATE;
97        }
98        return AAudioStream_getChannelCount(mStream);
99    }
100
101    /**
102     * Open a stream
103     */
104    aaudio_result_t open(const AAudioParameters &parameters,
105                         AAudioStream_dataCallback dataCallback = nullptr,
106                         AAudioStream_errorCallback errorCallback = nullptr,
107                         void *userContext = nullptr) {
108        aaudio_result_t result = AAUDIO_OK;
109
110        // Use an AAudioStreamBuilder to contain requested parameters.
111        AAudioStreamBuilder *builder = nullptr;
112        result = AAudio_createStreamBuilder(&builder);
113        if (result != AAUDIO_OK) return result;
114
115        parameters.applyParameters(builder); // apply args
116
117        AAudioStreamBuilder_setDirection(builder, AAUDIO_DIRECTION_OUTPUT);
118
119        if (dataCallback != nullptr) {
120            AAudioStreamBuilder_setDataCallback(builder, dataCallback, userContext);
121        }
122        if (errorCallback != nullptr) {
123            AAudioStreamBuilder_setErrorCallback(builder, errorCallback, userContext);
124        }
125        //AAudioStreamBuilder_setFramesPerDataCallback(builder, CALLBACK_SIZE_FRAMES);
126        //AAudioStreamBuilder_setBufferCapacityInFrames(builder, 48 * 8);
127
128        // Open an AAudioStream using the Builder.
129        result = AAudioStreamBuilder_openStream(builder, &mStream);
130
131        if (result == AAUDIO_OK) {
132            int32_t sizeInBursts = parameters.getNumberOfBursts();
133            if (sizeInBursts > 0) {
134                int32_t framesPerBurst = AAudioStream_getFramesPerBurst(mStream);
135                AAudioStream_setBufferSizeInFrames(mStream, sizeInBursts * framesPerBurst);
136            }
137        }
138
139        AAudioStreamBuilder_delete(builder);
140        return result;
141    }
142
143    aaudio_result_t open(int channelCount, int sampSampleRate, aaudio_format_t format,
144                         AAudioStream_dataCallback dataProc,
145                         AAudioStream_errorCallback errorProc,
146                         void *userContext) {
147        aaudio_result_t result = AAUDIO_OK;
148
149        // Use an AAudioStreamBuilder to contain requested parameters.
150        AAudioStreamBuilder *builder = nullptr;
151        result = AAudio_createStreamBuilder(&builder);
152        if (result != AAUDIO_OK) return result;
153
154        AAudioStreamBuilder_setDirection(builder, AAUDIO_DIRECTION_OUTPUT);
155        AAudioStreamBuilder_setPerformanceMode(builder, mRequestedPerformanceMode);
156        AAudioStreamBuilder_setSharingMode(builder, mRequestedSharingMode);
157
158        AAudioStreamBuilder_setChannelCount(builder, channelCount);
159        AAudioStreamBuilder_setSampleRate(builder, sampSampleRate);
160        AAudioStreamBuilder_setFormat(builder, format);
161
162        if (dataProc != nullptr) {
163            AAudioStreamBuilder_setDataCallback(builder, dataProc, userContext);
164        }
165        if (errorProc != nullptr) {
166            AAudioStreamBuilder_setErrorCallback(builder, errorProc, userContext);
167        }
168        //AAudioStreamBuilder_setFramesPerDataCallback(builder, CALLBACK_SIZE_FRAMES);
169        //AAudioStreamBuilder_setBufferCapacityInFrames(builder, 48 * 8);
170
171        // Open an AAudioStream using the Builder.
172        result = AAudioStreamBuilder_openStream(builder, &mStream);
173
174        AAudioStreamBuilder_delete(builder);
175        return result;
176    }
177
178    aaudio_result_t close() {
179        if (mStream != nullptr) {
180            printf("call AAudioStream_close(%p)\n", mStream);  fflush(stdout);
181            AAudioStream_close(mStream);
182            mStream = nullptr;
183        }
184        return AAUDIO_OK;
185    }
186
187    // Write zero data to fill up the buffer and prevent underruns.
188    aaudio_result_t prime() {
189        int32_t samplesPerFrame = AAudioStream_getChannelCount(mStream);
190        const int numFrames = 32;
191        float zeros[numFrames * samplesPerFrame];
192        memset(zeros, 0, sizeof(zeros));
193        aaudio_result_t result = numFrames;
194        while (result == numFrames) {
195            result = AAudioStream_write(mStream, zeros, numFrames, 0);
196        }
197        return result;
198    }
199
200    // Start the stream. AAudio will start calling your callback function.
201     aaudio_result_t start() {
202        aaudio_result_t result = AAudioStream_requestStart(mStream);
203        if (result != AAUDIO_OK) {
204            printf("ERROR - AAudioStream_requestStart() returned %d %s\n",
205                    result, AAudio_convertResultToText(result));
206        }
207        return result;
208    }
209
210    // Stop the stream. AAudio will stop calling your callback function.
211    aaudio_result_t stop() {
212        aaudio_result_t result = AAudioStream_requestStop(mStream);
213        if (result != AAUDIO_OK) {
214            printf("ERROR - AAudioStream_requestStop() returned %d %s\n",
215                    result, AAudio_convertResultToText(result));
216        }
217        int32_t xRunCount = AAudioStream_getXRunCount(mStream);
218        printf("AAudioStream_getXRunCount %d\n", xRunCount);
219        return result;
220    }
221
222    AAudioStream *getStream() const {
223        return mStream;
224    }
225
226private:
227    AAudioStream             *mStream = nullptr;
228    aaudio_sharing_mode_t     mRequestedSharingMode = SHARING_MODE;
229    aaudio_performance_mode_t mRequestedPerformanceMode = PERFORMANCE_MODE;
230
231};
232
233typedef struct SineThreadedData_s {
234
235    SineGenerator  sineOsc1;
236    SineGenerator  sineOsc2;
237    Timestamp      timestamps[MAX_TIMESTAMPS];
238    int64_t        framesTotal = 0;
239    int64_t        nextFrameToGlitch = FORCED_UNDERRUN_PERIOD_FRAMES;
240    int32_t        minNumFrames = INT32_MAX;
241    int32_t        maxNumFrames = 0;
242    int32_t        timestampCount = 0; // in timestamps
243
244    int            scheduler = 0;
245    bool           schedulerChecked = false;
246    bool           forceUnderruns = false;
247
248    AAudioSimplePlayer simplePlayer;
249    int32_t            callbackCount = 0;
250    WakeUp             waker{AAUDIO_OK};
251
252} SineThreadedData_t;
253
254// Callback function that fills the audio output buffer.
255aaudio_data_callback_result_t SimplePlayerDataCallbackProc(
256        AAudioStream *stream,
257        void *userData,
258        void *audioData,
259        int32_t numFrames
260        ) {
261
262    // should not happen but just in case...
263    if (userData == nullptr) {
264        printf("ERROR - SimplePlayerDataCallbackProc needs userData\n");
265        return AAUDIO_CALLBACK_RESULT_STOP;
266    }
267    SineThreadedData_t *sineData = (SineThreadedData_t *) userData;
268    sineData->callbackCount++;
269
270    sineData->framesTotal += numFrames;
271
272    if (sineData->forceUnderruns) {
273        if (sineData->framesTotal > sineData->nextFrameToGlitch) {
274            usleep(FORCED_UNDERRUN_SLEEP_MICROS);
275            printf("Simulate glitch at %lld\n", (long long) sineData->framesTotal);
276            sineData->nextFrameToGlitch += FORCED_UNDERRUN_PERIOD_FRAMES;
277        }
278    }
279
280    if (!sineData->schedulerChecked) {
281        sineData->scheduler = sched_getscheduler(gettid());
282        sineData->schedulerChecked = true;
283    }
284
285    if (sineData->timestampCount < MAX_TIMESTAMPS) {
286        Timestamp *timestamp = &sineData->timestamps[sineData->timestampCount];
287        aaudio_result_t result = AAudioStream_getTimestamp(stream,
288            CLOCK_MONOTONIC, &timestamp->position, &timestamp->nanoseconds);
289        if (result == AAUDIO_OK && // valid?
290                (sineData->timestampCount == 0 || // first one?
291                (timestamp->position != (timestamp - 1)->position))) { // advanced position?
292            sineData->timestampCount++; // keep this one
293        }
294    }
295
296    if (numFrames > sineData->maxNumFrames) {
297        sineData->maxNumFrames = numFrames;
298    }
299    if (numFrames < sineData->minNumFrames) {
300        sineData->minNumFrames = numFrames;
301    }
302
303    int32_t samplesPerFrame = AAudioStream_getChannelCount(stream);
304    // This code only plays on the first one or two channels.
305    // TODO Support arbitrary number of channels.
306    switch (AAudioStream_getFormat(stream)) {
307        case AAUDIO_FORMAT_PCM_I16: {
308            int16_t *audioBuffer = (int16_t *) audioData;
309            // Render sine waves as shorts to first channel.
310            sineData->sineOsc1.render(&audioBuffer[0], samplesPerFrame, numFrames);
311            // Render sine waves to second channel if there is one.
312            if (samplesPerFrame > 1) {
313                sineData->sineOsc2.render(&audioBuffer[1], samplesPerFrame, numFrames);
314            }
315        }
316        break;
317        case AAUDIO_FORMAT_PCM_FLOAT: {
318            float *audioBuffer = (float *) audioData;
319            // Render sine waves as floats to first channel.
320            sineData->sineOsc1.render(&audioBuffer[0], samplesPerFrame, numFrames);
321            // Render sine waves to second channel if there is one.
322            if (samplesPerFrame > 1) {
323                sineData->sineOsc2.render(&audioBuffer[1], samplesPerFrame, numFrames);
324            }
325        }
326        break;
327        default:
328            return AAUDIO_CALLBACK_RESULT_STOP;
329    }
330
331    return AAUDIO_CALLBACK_RESULT_CONTINUE;
332}
333
334void SimplePlayerErrorCallbackProc(
335        AAudioStream *stream __unused,
336        void *userData __unused,
337        aaudio_result_t error) {
338    // should not happen but just in case...
339    if (userData == nullptr) {
340        printf("ERROR - MyPlayerErrorCallbackProc needs userData\n");
341        return;
342    }
343    SineThreadedData_t *sineData = (SineThreadedData_t *) userData;
344    android::status_t ret = sineData->waker.wake(error);
345    printf("Error Callback, error: %d, futex wake returns %d\n", error, ret);
346}
347
348
349#endif //AAUDIO_SIMPLE_PLAYER_H
350