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 &parameters,
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                                                      &currentState, 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, &timestamp->position, &timestamp->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