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