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