• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
41 typedef 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  */
49 class AAudioSimplePlayer {
50 public:
AAudioSimplePlayer()51     AAudioSimplePlayer() {}
~AAudioSimplePlayer()52     ~AAudioSimplePlayer() {
53         close();
54     };
55 
56     /**
57      * Call this before calling open().
58      * @param requestedSharingMode
59      */
setSharingMode(aaudio_sharing_mode_t requestedSharingMode)60     void setSharingMode(aaudio_sharing_mode_t requestedSharingMode) {
61         mRequestedSharingMode = requestedSharingMode;
62     }
63 
64     /**
65      * Call this before calling open().
66      * @param requestedPerformanceMode
67      */
setPerformanceMode(aaudio_performance_mode_t requestedPerformanceMode)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      */
getFramesPerSecond()77     int32_t getFramesPerSecond() const {
78         return getSampleRate(); // alias
79     }
80 
81     /**
82      * Only call this after open() has been called.
83      */
getSampleRate()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      */
getChannelCount()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 
open(int channelCount,int sampSampleRate,aaudio_format_t format,AAudioStream_dataCallback dataProc,AAudioStream_errorCallback errorProc,void * userContext)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 
close()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.
prime()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.
start()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.
stop()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 
getStream()222     AAudioStream *getStream() const {
223         return mStream;
224     }
225 
226 private:
227     AAudioStream             *mStream = nullptr;
228     aaudio_sharing_mode_t     mRequestedSharingMode = SHARING_MODE;
229     aaudio_performance_mode_t mRequestedPerformanceMode = PERFORMANCE_MODE;
230 
231 };
232 
233 typedef 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.
SimplePlayerDataCallbackProc(AAudioStream * stream,void * userData,void * audioData,int32_t numFrames)255 aaudio_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 
SimplePlayerErrorCallbackProc(AAudioStream * stream __unused,void * userData __unused,aaudio_result_t error)334 void 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