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