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 ¶meters,
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 ¤tState, 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, ×tamp->position, ×tamp->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
363 int numActiveOscilators = (samplesPerFrame > MAX_CHANNELS) ? MAX_CHANNELS : samplesPerFrame;
364 switch (AAudioStream_getFormat(stream)) {
365 case AAUDIO_FORMAT_PCM_I16: {
366 int16_t *audioBuffer = (int16_t *) audioData;
367 for (int i = 0; i < numActiveOscilators; ++i) {
368 sineData->sineOscillators[i].render(&audioBuffer[i], samplesPerFrame,
369 numFrames);
370 }
371 }
372 break;
373 case AAUDIO_FORMAT_PCM_FLOAT: {
374 float *audioBuffer = (float *) audioData;
375 for (int i = 0; i < numActiveOscilators; ++i) {
376 sineData->sineOscillators[i].render(&audioBuffer[i], samplesPerFrame,
377 numFrames);
378 }
379 }
380 break;
381 default:
382 return AAUDIO_CALLBACK_RESULT_STOP;
383 }
384
385 sineData->callbackCount++;
386 sineData->framesTotal += numFrames;
387 return AAUDIO_CALLBACK_RESULT_CONTINUE;
388 }
389
SimplePlayerErrorCallbackProc(AAudioStream * stream __unused,void * userData __unused,aaudio_result_t error)390 void SimplePlayerErrorCallbackProc(
391 AAudioStream *stream __unused,
392 void *userData __unused,
393 aaudio_result_t error) {
394 // should not happen but just in case...
395 if (userData == nullptr) {
396 printf("ERROR - MyPlayerErrorCallbackProc needs userData\n");
397 return;
398 }
399 SineThreadedData_t *sineData = (SineThreadedData_t *) userData;
400 android::status_t ret = sineData->waker.wake(error);
401 printf("Error Callback, error: %d, futex wake returns %d\n", error, ret);
402 }
403
404
405 #endif //AAUDIO_SIMPLE_PLAYER_H
406