• 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 <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 
40 typedef 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  */
48 class AAudioSimplePlayer {
49 public:
AAudioSimplePlayer()50     AAudioSimplePlayer() {}
~AAudioSimplePlayer()51     ~AAudioSimplePlayer() {
52         close();
53     };
54 
55     /**
56      * Call this before calling open().
57      * @param requestedSharingMode
58      */
setSharingMode(aaudio_sharing_mode_t requestedSharingMode)59     void setSharingMode(aaudio_sharing_mode_t requestedSharingMode) {
60         mRequestedSharingMode = requestedSharingMode;
61     }
62 
63     /**
64      * Call this before calling open().
65      * @param requestedPerformanceMode
66      */
setPerformanceMode(aaudio_performance_mode_t requestedPerformanceMode)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      */
getSampleRate()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      */
getChannelCount()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 
open(int channelCount,int sampSampleRate,aaudio_format_t format,AAudioStream_dataCallback dataProc,AAudioStream_errorCallback errorProc,void * userContext)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 
close()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.
prime()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.
start()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.
stop()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.
pause()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.
flush()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 
getStream()236     AAudioStream *getStream() const {
237         return mStream;
238     }
239 
240 private:
241     AAudioStream             *mStream = nullptr;
242     aaudio_sharing_mode_t     mRequestedSharingMode = SHARING_MODE;
243     aaudio_performance_mode_t mRequestedPerformanceMode = PERFORMANCE_MODE;
244 
245 };
246 
247 typedef 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      */
setupSineBlipSineThreadedData_s271     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 
setupSineSweepsSineThreadedData_s279     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.
SimplePlayerDataCallbackProc(AAudioStream * stream,void * userData,void * audioData,int32_t numFrames)295 aaudio_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 
SimplePlayerErrorCallbackProc(AAudioStream * stream __unused,void * userData __unused,aaudio_result_t error)375 void 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