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 "SineGenerator.h"
27
28//#define SHARING_MODE  AAUDIO_SHARING_MODE_EXCLUSIVE
29#define SHARING_MODE  AAUDIO_SHARING_MODE_SHARED
30#define PERFORMANCE_MODE AAUDIO_PERFORMANCE_MODE_NONE
31
32/**
33 * Simple wrapper for AAudio that opens an output stream either in callback or blocking write mode.
34 */
35class AAudioSimplePlayer {
36public:
37    AAudioSimplePlayer() {}
38    ~AAudioSimplePlayer() {
39        close();
40    };
41
42    /**
43     * Call this before calling open().
44     * @param requestedSharingMode
45     */
46    void setSharingMode(aaudio_sharing_mode_t requestedSharingMode) {
47        mRequestedSharingMode = requestedSharingMode;
48    }
49
50    /**
51     * Call this before calling open().
52     * @param requestedPerformanceMode
53     */
54    void setPerformanceMode(aaudio_performance_mode_t requestedPerformanceMode) {
55        mRequestedPerformanceMode = requestedPerformanceMode;
56    }
57
58    /**
59     * Also known as "sample rate"
60     * Only call this after open() has been called.
61     */
62    int32_t getFramesPerSecond() {
63        if (mStream == nullptr) {
64            return AAUDIO_ERROR_INVALID_STATE;
65        }
66        return AAudioStream_getSampleRate(mStream);;
67    }
68
69    /**
70     * Only call this after open() has been called.
71     */
72    int32_t getChannelCount() {
73        if (mStream == nullptr) {
74            return AAUDIO_ERROR_INVALID_STATE;
75        }
76        return AAudioStream_getChannelCount(mStream);;
77    }
78
79    /**
80     * Open a stream
81     */
82    aaudio_result_t open(int channelCount, int sampSampleRate, aaudio_format_t format,
83                         AAudioStream_dataCallback dataProc, AAudioStream_errorCallback errorProc,
84                         void *userContext) {
85        aaudio_result_t result = AAUDIO_OK;
86
87        // Use an AAudioStreamBuilder to contain requested parameters.
88        result = AAudio_createStreamBuilder(&mBuilder);
89        if (result != AAUDIO_OK) return result;
90
91        //AAudioStreamBuilder_setSampleRate(mBuilder, 44100);
92        AAudioStreamBuilder_setPerformanceMode(mBuilder, mRequestedPerformanceMode);
93        AAudioStreamBuilder_setSharingMode(mBuilder, mRequestedSharingMode);
94        if (dataProc != nullptr) {
95            AAudioStreamBuilder_setDataCallback(mBuilder, dataProc, userContext);
96        }
97        if (errorProc != nullptr) {
98            AAudioStreamBuilder_setErrorCallback(mBuilder, errorProc, userContext);
99        }
100        AAudioStreamBuilder_setChannelCount(mBuilder, channelCount);
101        AAudioStreamBuilder_setSampleRate(mBuilder, sampSampleRate);
102        AAudioStreamBuilder_setFormat(mBuilder, format);
103        //AAudioStreamBuilder_setFramesPerDataCallback(mBuilder, CALLBACK_SIZE_FRAMES);
104        AAudioStreamBuilder_setBufferCapacityInFrames(mBuilder, 48 * 8);
105
106        //aaudio_performance_mode_t perfMode = AAUDIO_PERFORMANCE_MODE_NONE;
107        aaudio_performance_mode_t perfMode = AAUDIO_PERFORMANCE_MODE_LOW_LATENCY;
108        //aaudio_performance_mode_t perfMode = AAUDIO_PERFORMANCE_MODE_POWER_SAVING;
109        AAudioStreamBuilder_setPerformanceMode(mBuilder, perfMode);
110
111        // Open an AAudioStream using the Builder.
112        result = AAudioStreamBuilder_openStream(mBuilder, &mStream);
113        if (result != AAUDIO_OK) goto finish1;
114
115        printf("AAudioStream_getFramesPerBurst() = %d\n",
116               AAudioStream_getFramesPerBurst(mStream));
117        printf("AAudioStream_getBufferSizeInFrames() = %d\n",
118               AAudioStream_getBufferSizeInFrames(mStream));
119        printf("AAudioStream_getBufferCapacityInFrames() = %d\n",
120               AAudioStream_getBufferCapacityInFrames(mStream));
121        printf("AAudioStream_getPerformanceMode() = %d, requested %d\n",
122               AAudioStream_getPerformanceMode(mStream), perfMode);
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_getChannelCount(mStream);
144        const int numFrames = 32;
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            printf("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            printf("ERROR - AAudioStream_requestStop() returned %d %s\n",
169                    result, AAudio_convertResultToText(result));
170        }
171        int32_t xRunCount = AAudioStream_getXRunCount(mStream);
172        printf("AAudioStream_getXRunCount %d\n", xRunCount);
173        return result;
174    }
175
176    AAudioStream *getStream() const {
177        return mStream;
178    }
179
180private:
181    AAudioStreamBuilder      *mBuilder = nullptr;
182    AAudioStream             *mStream = nullptr;
183    aaudio_sharing_mode_t     mRequestedSharingMode = SHARING_MODE;
184    aaudio_performance_mode_t mRequestedPerformanceMode = PERFORMANCE_MODE;
185};
186
187typedef struct SineThreadedData_s {
188    SineGenerator  sineOsc1;
189    SineGenerator  sineOsc2;
190    int            scheduler;
191    bool           schedulerChecked;
192} SineThreadedData_t;
193
194// Callback function that fills the audio output buffer.
195aaudio_data_callback_result_t SimplePlayerDataCallbackProc(
196        AAudioStream *stream,
197        void *userData,
198        void *audioData,
199        int32_t numFrames
200        ) {
201
202    // should not happen but just in case...
203    if (userData == nullptr) {
204        fprintf(stderr, "ERROR - SimplePlayerDataCallbackProc needs userData\n");
205        return AAUDIO_CALLBACK_RESULT_STOP;
206    }
207    SineThreadedData_t *sineData = (SineThreadedData_t *) userData;
208
209    if (!sineData->schedulerChecked) {
210        sineData->scheduler = sched_getscheduler(gettid());
211        sineData->schedulerChecked = true;
212    }
213
214    int32_t samplesPerFrame = AAudioStream_getChannelCount(stream);
215    // This code only plays on the first one or two channels.
216    // TODO Support arbitrary number of channels.
217    switch (AAudioStream_getFormat(stream)) {
218        case AAUDIO_FORMAT_PCM_I16: {
219            int16_t *audioBuffer = (int16_t *) audioData;
220            // Render sine waves as shorts to first channel.
221            sineData->sineOsc1.render(&audioBuffer[0], samplesPerFrame, numFrames);
222            // Render sine waves to second channel if there is one.
223            if (samplesPerFrame > 1) {
224                sineData->sineOsc2.render(&audioBuffer[1], samplesPerFrame, numFrames);
225            }
226        }
227        break;
228        case AAUDIO_FORMAT_PCM_FLOAT: {
229            float *audioBuffer = (float *) audioData;
230            // Render sine waves as floats to first channel.
231            sineData->sineOsc1.render(&audioBuffer[0], samplesPerFrame, numFrames);
232            // Render sine waves to second channel if there is one.
233            if (samplesPerFrame > 1) {
234                sineData->sineOsc2.render(&audioBuffer[1], samplesPerFrame, numFrames);
235            }
236        }
237        break;
238        default:
239            return AAUDIO_CALLBACK_RESULT_STOP;
240    }
241
242    return AAUDIO_CALLBACK_RESULT_CONTINUE;
243}
244
245void SimplePlayerErrorCallbackProc(
246        AAudioStream *stream __unused,
247        void *userData __unused,
248        aaudio_result_t error)
249{
250    printf("Error Callback, error: %d\n",(int)error);
251}
252
253#endif //AAUDIO_SIMPLE_PLAYER_H
254