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