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 ~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 aaudio_result_t result = AAUDIO_OK;
124
125 // Use an AAudioStreamBuilder to contain requested parameters.
126 AAudioStreamBuilder *builder = nullptr;
127 result = AAudio_createStreamBuilder(&builder);
128 if (result != AAUDIO_OK) return result;
129
130 parameters.applyParameters(builder); // apply args
131
132 AAudioStreamBuilder_setDirection(builder, AAUDIO_DIRECTION_OUTPUT);
133
134 if (dataCallback != nullptr) {
135 AAudioStreamBuilder_setDataCallback(builder, dataCallback, userContext);
136 }
137 if (errorCallback != nullptr) {
138 AAudioStreamBuilder_setErrorCallback(builder, errorCallback, userContext);
139 }
140 //AAudioStreamBuilder_setFramesPerDataCallback(builder, CALLBACK_SIZE_FRAMES);
141 //AAudioStreamBuilder_setBufferCapacityInFrames(builder, 48 * 8);
142
143 // Open an AAudioStream using the Builder.
144 result = AAudioStreamBuilder_openStream(builder, &mStream);
145
146 if (result == AAUDIO_OK) {
147 int32_t sizeInBursts = parameters.getNumberOfBursts();
148 int32_t framesPerBurst = AAudioStream_getFramesPerBurst(mStream);
149 int32_t bufferSizeFrames = sizeInBursts * framesPerBurst;
150 AAudioStream_setBufferSizeInFrames(mStream, bufferSizeFrames);
151 }
152
153 AAudioStreamBuilder_delete(builder);
154 return result;
155 }
156
open(int channelCount,int sampSampleRate,aaudio_format_t format,AAudioStream_dataCallback dataProc,AAudioStream_errorCallback errorProc,void * userContext)157 aaudio_result_t open(int channelCount, int sampSampleRate, aaudio_format_t format,
158 AAudioStream_dataCallback dataProc,
159 AAudioStream_errorCallback errorProc,
160 void *userContext) {
161 aaudio_result_t result = AAUDIO_OK;
162
163 // Use an AAudioStreamBuilder to contain requested parameters.
164 AAudioStreamBuilder *builder = nullptr;
165 result = AAudio_createStreamBuilder(&builder);
166 if (result != AAUDIO_OK) return result;
167
168 AAudioStreamBuilder_setDirection(builder, AAUDIO_DIRECTION_OUTPUT);
169 AAudioStreamBuilder_setPerformanceMode(builder, mRequestedPerformanceMode);
170 AAudioStreamBuilder_setSharingMode(builder, mRequestedSharingMode);
171
172 AAudioStreamBuilder_setChannelCount(builder, channelCount);
173 AAudioStreamBuilder_setSampleRate(builder, sampSampleRate);
174 AAudioStreamBuilder_setFormat(builder, format);
175
176 if (dataProc != nullptr) {
177 AAudioStreamBuilder_setDataCallback(builder, dataProc, userContext);
178 }
179 if (errorProc != nullptr) {
180 AAudioStreamBuilder_setErrorCallback(builder, errorProc, userContext);
181 }
182 //AAudioStreamBuilder_setFramesPerDataCallback(builder, CALLBACK_SIZE_FRAMES);
183 //AAudioStreamBuilder_setBufferCapacityInFrames(builder, 48 * 8);
184
185 // Open an AAudioStream using the Builder.
186 result = AAudioStreamBuilder_openStream(builder, &mStream);
187
188 AAudioStreamBuilder_delete(builder);
189
190 return result;
191 }
192
close()193 aaudio_result_t close() {
194 if (mStream != nullptr) {
195 AAudioStream_close(mStream);
196 mStream = nullptr;
197 }
198 return AAUDIO_OK;
199 }
200
201 // Write zero data to fill up the buffer and prevent underruns.
prime()202 aaudio_result_t prime() {
203 int32_t samplesPerFrame = AAudioStream_getChannelCount(mStream);
204 const int numFrames = 32;
205 float zeros[numFrames * samplesPerFrame];
206 memset(zeros, 0, sizeof(zeros));
207 aaudio_result_t result = numFrames;
208 while (result == numFrames) {
209 result = AAudioStream_write(mStream, zeros, numFrames, 0);
210 }
211 return result;
212 }
213
214 // Start the stream. AAudio will start calling your callback function.
start()215 aaudio_result_t start() {
216 aaudio_result_t result = AAudioStream_requestStart(mStream);
217 if (result != AAUDIO_OK) {
218 printf("ERROR - AAudioStream_requestStart(output) returned %d %s\n",
219 result, AAudio_convertResultToText(result));
220 }
221 return result;
222 }
223
224 // Stop the stream. AAudio will stop calling your callback function.
stop()225 aaudio_result_t stop() {
226 aaudio_result_t result = AAudioStream_requestStop(mStream);
227 if (result != AAUDIO_OK) {
228 printf("ERROR - AAudioStream_requestStop(output) returned %d %s\n",
229 result, AAudio_convertResultToText(result));
230 }
231 int32_t xRunCount = AAudioStream_getXRunCount(mStream);
232 printf("AAudioStream_getXRunCount %d\n", xRunCount);
233 return result;
234 }
235
236 // Pause the stream. AAudio will stop calling your callback function.
pause()237 aaudio_result_t pause() {
238 aaudio_result_t result = AAudioStream_requestPause(mStream);
239 if (result != AAUDIO_OK) {
240 printf("ERROR - AAudioStream_requestPause(output) returned %d %s\n",
241 result, AAudio_convertResultToText(result));
242 }
243 int32_t xRunCount = AAudioStream_getXRunCount(mStream);
244 printf("AAudioStream_getXRunCount %d\n", xRunCount);
245 return result;
246 }
247
waitUntilPaused()248 aaudio_result_t waitUntilPaused() {
249 aaudio_result_t result = AAUDIO_OK;
250 aaudio_stream_state_t currentState = AAudioStream_getState(mStream);
251 aaudio_stream_state_t inputState = AAUDIO_STREAM_STATE_PAUSING;
252 while (result == AAUDIO_OK && currentState == AAUDIO_STREAM_STATE_PAUSING) {
253 result = AAudioStream_waitForStateChange(mStream, inputState,
254 ¤tState, NANOS_PER_SECOND);
255 inputState = currentState;
256 }
257 if (result != AAUDIO_OK) {
258 return result;
259 }
260 return (currentState == AAUDIO_STREAM_STATE_PAUSED)
261 ? AAUDIO_OK : AAUDIO_ERROR_INVALID_STATE;
262 }
263
264 // Flush the stream. AAudio will stop calling your callback function.
flush()265 aaudio_result_t flush() {
266 aaudio_result_t result = AAudioStream_requestFlush(mStream);
267 if (result != AAUDIO_OK) {
268 printf("ERROR - AAudioStream_requestFlush(output) returned %d %s\n",
269 result, AAudio_convertResultToText(result));
270 }
271 return result;
272 }
273
getStream()274 AAudioStream *getStream() const {
275 return mStream;
276 }
277
278 private:
279 AAudioStream *mStream = nullptr;
280 aaudio_sharing_mode_t mRequestedSharingMode = SHARING_MODE;
281 aaudio_performance_mode_t mRequestedPerformanceMode = PERFORMANCE_MODE;
282
283 };
284
285 typedef struct SineThreadedData_s {
286
287 SineGenerator sineOscillators[MAX_CHANNELS];
288 Timestamp timestamps[MAX_TIMESTAMPS];
289 int64_t framesTotal = 0;
290 int64_t nextFrameToGlitch = FORCED_UNDERRUN_PERIOD_FRAMES;
291 int32_t minNumFrames = INT32_MAX;
292 int32_t maxNumFrames = 0;
293 int32_t timestampCount = 0; // in timestamps
294 int32_t sampleRate = 48000;
295 int32_t prefixToneFrames = 0;
296 double workload = 0.0;
297 bool sweepSetup = false;
298
299 int scheduler = 0;
300 bool schedulerChecked = false;
301 int32_t hangTimeMSec = 0;
302 int cpuAffinity = -1;
303
304 AAudioSimplePlayer simplePlayer;
305 int32_t callbackCount = 0;
306 WakeUp waker{AAUDIO_OK};
307
308 /**
309 * Set sampleRate first.
310 */
setupSineBlipSineThreadedData_s311 void setupSineBlip() {
312 for (int i = 0; i < MAX_CHANNELS; ++i) {
313 double centerFrequency = 880.0 * (i + 2);
314 sineOscillators[i].setup(centerFrequency, sampleRate);
315 sineOscillators[i].setSweep(centerFrequency, centerFrequency, 0.0);
316 }
317 }
318
setupSineSweepsSineThreadedData_s319 void setupSineSweeps() {
320 for (int i = 0; i < MAX_CHANNELS; ++i) {
321 double centerFrequency = 220.0 * (i + 2);
322 sineOscillators[i].setup(centerFrequency, sampleRate);
323 double minFrequency = centerFrequency * 2.0 / 3.0;
324 // Change range slightly so they will go out of phase.
325 double maxFrequency = centerFrequency * 3.0 / 2.0;
326 double sweepSeconds = 5.0 + i;
327 sineOscillators[i].setSweep(minFrequency, maxFrequency, sweepSeconds);
328 }
329 sweepSetup = true;
330 }
331
332 } SineThreadedData_t;
333
setCpuAffinity(int cpuIndex)334 int setCpuAffinity(int cpuIndex) {
335 cpu_set_t cpu_set;
336 CPU_ZERO(&cpu_set);
337 CPU_SET(cpuIndex, &cpu_set);
338 int err = sched_setaffinity((pid_t) 0, sizeof(cpu_set_t), &cpu_set);
339 return err == 0 ? 0 : -errno;
340 }
341
342 // Callback function that fills the audio output buffer.
SimplePlayerDataCallbackProc(AAudioStream * stream,void * userData,void * audioData,int32_t numFrames)343 aaudio_data_callback_result_t SimplePlayerDataCallbackProc(
344 AAudioStream *stream,
345 void *userData,
346 void *audioData,
347 int32_t numFrames
348 ) {
349
350 // should not happen but just in case...
351 if (userData == nullptr) {
352 printf("ERROR - SimplePlayerDataCallbackProc needs userData\n");
353 return AAUDIO_CALLBACK_RESULT_STOP;
354 }
355 SineThreadedData_t *sineData = (SineThreadedData_t *) userData;
356
357 if (sineData->cpuAffinity >= 0) {
358 setCpuAffinity(sineData->cpuAffinity);
359 sineData->cpuAffinity = -1;
360 }
361 // Play an initial high tone so we can tell whether the beginning was truncated.
362 if (!sineData->sweepSetup && sineData->framesTotal >= sineData->prefixToneFrames) {
363 sineData->setupSineSweeps();
364 }
365
366 if (sineData->hangTimeMSec > 0) {
367 if (sineData->framesTotal > sineData->nextFrameToGlitch) {
368 usleep(sineData->hangTimeMSec * 1000);
369 printf("Hang callback at %lld frames for %d msec\n",
370 (long long) sineData->framesTotal,
371 sineData->hangTimeMSec);
372 sineData->nextFrameToGlitch += FORCED_UNDERRUN_PERIOD_FRAMES;
373 }
374 }
375
376 if (!sineData->schedulerChecked) {
377 sineData->scheduler = sched_getscheduler(gettid());
378 sineData->schedulerChecked = true;
379 }
380
381 if (sineData->timestampCount < MAX_TIMESTAMPS) {
382 Timestamp *timestamp = &sineData->timestamps[sineData->timestampCount];
383 aaudio_result_t result = AAudioStream_getTimestamp(stream,
384 CLOCK_MONOTONIC, ×tamp->position, ×tamp->nanoseconds);
385 if (result == AAUDIO_OK && // valid?
386 (sineData->timestampCount == 0 || // first one?
387 (timestamp->position != (timestamp - 1)->position))) { // advanced position?
388 sineData->timestampCount++; // keep this one
389 }
390 }
391
392 if (numFrames > sineData->maxNumFrames) {
393 sineData->maxNumFrames = numFrames;
394 }
395 if (numFrames < sineData->minNumFrames) {
396 sineData->minNumFrames = numFrames;
397 }
398
399 int32_t samplesPerFrame = AAudioStream_getChannelCount(stream);
400
401 int numActiveOscillators = std::min(samplesPerFrame, MAX_CHANNELS);
402 switch (AAudioStream_getFormat(stream)) {
403 case AAUDIO_FORMAT_PCM_I16: {
404 int16_t *audioBuffer = (int16_t *) audioData;
405 for (int i = 0; i < numActiveOscillators; ++i) {
406 sineData->sineOscillators[i].render(&audioBuffer[i],
407 samplesPerFrame, numFrames);
408 }
409 }
410 break;
411 case AAUDIO_FORMAT_PCM_FLOAT: {
412 float *audioBuffer = (float *) audioData;
413 for (int i = 0; i < numActiveOscillators; ++i) {
414 sineData->sineOscillators[i].render(&audioBuffer[i],
415 samplesPerFrame, numFrames);
416 }
417 }
418 break;
419 case AAUDIO_FORMAT_PCM_I24_PACKED: {
420 uint8_t *audioBuffer = (uint8_t *) audioData;
421 for (int i = 0; i < numActiveOscillators; ++i) {
422 static const int bytesPerSample = getBytesPerSample(AAUDIO_FORMAT_PCM_I24_PACKED);
423 sineData->sineOscillators[i].render24(&audioBuffer[i * bytesPerSample],
424 samplesPerFrame, numFrames);
425 }
426 }
427 break;
428 case AAUDIO_FORMAT_PCM_I32: {
429 int32_t *audioBuffer = (int32_t *) audioData;
430 for (int i = 0; i < numActiveOscillators; ++i) {
431 sineData->sineOscillators[i].render(&audioBuffer[i],
432 samplesPerFrame, numFrames);
433 }
434 }
435 break;
436 default:
437 return AAUDIO_CALLBACK_RESULT_STOP;
438 }
439
440 s_burnCPU((int32_t)(sineData->workload * kWorkloadScaler * numFrames));
441
442 sineData->callbackCount++;
443 sineData->framesTotal += numFrames;
444 return AAUDIO_CALLBACK_RESULT_CONTINUE;
445 }
446
SimplePlayerErrorCallbackProc(AAudioStream * stream __unused,void * userData __unused,aaudio_result_t error)447 void SimplePlayerErrorCallbackProc(
448 AAudioStream *stream __unused,
449 void *userData __unused,
450 aaudio_result_t error) {
451 // should not happen but just in case...
452 if (userData == nullptr) {
453 printf("ERROR - MyPlayerErrorCallbackProc needs userData\n");
454 return;
455 }
456 SineThreadedData_t *sineData = (SineThreadedData_t *) userData;
457 android::status_t ret = sineData->waker.wake(error);
458 printf("Error Callback, error: %d, futex wake returns %d\n", error, ret);
459 }
460
461
462 #endif //AAUDIO_SIMPLE_PLAYER_H
463