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 <unistd.h>
23 #include <sched.h>
24
25 #include <aaudio/AAudio.h>
26 #include <atomic>
27 #include "AAudioArgsParser.h"
28 #include "SineGenerator.h"
29
30 //#define SHARING_MODE AAUDIO_SHARING_MODE_EXCLUSIVE
31 #define SHARING_MODE AAUDIO_SHARING_MODE_SHARED
32 #define PERFORMANCE_MODE AAUDIO_PERFORMANCE_MODE_NONE
33
34 // Arbitrary period for glitches, once per second at 48000 Hz.
35 #define FORCED_UNDERRUN_PERIOD_FRAMES 48000
36 // How long to sleep in a callback to cause an intentional glitch. For testing.
37 #define FORCED_UNDERRUN_SLEEP_MICROS (10 * 1000)
38
39 #define MAX_TIMESTAMPS 16
40
41 typedef struct Timestamp {
42 int64_t position;
43 int64_t nanoseconds;
44 } Timestamp;
45
46 /**
47 * Simple wrapper for AAudio that opens an output stream either in callback or blocking write mode.
48 */
49 class AAudioSimplePlayer {
50 public:
AAudioSimplePlayer()51 AAudioSimplePlayer() {}
~AAudioSimplePlayer()52 ~AAudioSimplePlayer() {
53 close();
54 };
55
56 /**
57 * Call this before calling open().
58 * @param requestedSharingMode
59 */
setSharingMode(aaudio_sharing_mode_t requestedSharingMode)60 void setSharingMode(aaudio_sharing_mode_t requestedSharingMode) {
61 mRequestedSharingMode = requestedSharingMode;
62 }
63
64 /**
65 * Call this before calling open().
66 * @param requestedPerformanceMode
67 */
setPerformanceMode(aaudio_performance_mode_t requestedPerformanceMode)68 void setPerformanceMode(aaudio_performance_mode_t requestedPerformanceMode) {
69 mRequestedPerformanceMode = requestedPerformanceMode;
70 }
71
72 // TODO Extract a common base class for record and playback.
73 /**
74 * Also known as "sample rate"
75 * Only call this after open() has been called.
76 */
getFramesPerSecond()77 int32_t getFramesPerSecond() const {
78 return getSampleRate(); // alias
79 }
80
81 /**
82 * Only call this after open() has been called.
83 */
getSampleRate()84 int32_t getSampleRate() const {
85 if (mStream == nullptr) {
86 return AAUDIO_ERROR_INVALID_STATE;
87 }
88 return AAudioStream_getSampleRate(mStream);
89 }
90
91 /**
92 * Only call this after open() has been called.
93 */
getChannelCount()94 int32_t getChannelCount() {
95 if (mStream == nullptr) {
96 return AAUDIO_ERROR_INVALID_STATE;
97 }
98 return AAudioStream_getChannelCount(mStream);
99 }
100
101 /**
102 * Open a stream
103 */
104 aaudio_result_t open(const AAudioParameters ¶meters,
105 AAudioStream_dataCallback dataCallback = nullptr,
106 AAudioStream_errorCallback errorCallback = nullptr,
107 void *userContext = nullptr) {
108 aaudio_result_t result = AAUDIO_OK;
109
110 // Use an AAudioStreamBuilder to contain requested parameters.
111 AAudioStreamBuilder *builder = nullptr;
112 result = AAudio_createStreamBuilder(&builder);
113 if (result != AAUDIO_OK) return result;
114
115 parameters.applyParameters(builder); // apply args
116
117 AAudioStreamBuilder_setDirection(builder, AAUDIO_DIRECTION_OUTPUT);
118
119 if (dataCallback != nullptr) {
120 AAudioStreamBuilder_setDataCallback(builder, dataCallback, userContext);
121 }
122 if (errorCallback != nullptr) {
123 AAudioStreamBuilder_setErrorCallback(builder, errorCallback, userContext);
124 }
125 //AAudioStreamBuilder_setFramesPerDataCallback(builder, CALLBACK_SIZE_FRAMES);
126 //AAudioStreamBuilder_setBufferCapacityInFrames(builder, 48 * 8);
127
128 // Open an AAudioStream using the Builder.
129 result = AAudioStreamBuilder_openStream(builder, &mStream);
130
131 if (result == AAUDIO_OK) {
132 int32_t sizeInBursts = parameters.getNumberOfBursts();
133 if (sizeInBursts > 0) {
134 int32_t framesPerBurst = AAudioStream_getFramesPerBurst(mStream);
135 AAudioStream_setBufferSizeInFrames(mStream, sizeInBursts * framesPerBurst);
136 }
137 }
138
139 AAudioStreamBuilder_delete(builder);
140 return result;
141 }
142
open(int channelCount,int sampSampleRate,aaudio_format_t format,AAudioStream_dataCallback dataProc,AAudioStream_errorCallback errorProc,void * userContext)143 aaudio_result_t open(int channelCount, int sampSampleRate, aaudio_format_t format,
144 AAudioStream_dataCallback dataProc,
145 AAudioStream_errorCallback errorProc,
146 void *userContext) {
147 aaudio_result_t result = AAUDIO_OK;
148
149 // Use an AAudioStreamBuilder to contain requested parameters.
150 AAudioStreamBuilder *builder = nullptr;
151 result = AAudio_createStreamBuilder(&builder);
152 if (result != AAUDIO_OK) return result;
153
154 AAudioStreamBuilder_setDirection(builder, AAUDIO_DIRECTION_OUTPUT);
155 AAudioStreamBuilder_setPerformanceMode(builder, mRequestedPerformanceMode);
156 AAudioStreamBuilder_setSharingMode(builder, mRequestedSharingMode);
157
158 AAudioStreamBuilder_setChannelCount(builder, channelCount);
159 AAudioStreamBuilder_setSampleRate(builder, sampSampleRate);
160 AAudioStreamBuilder_setFormat(builder, format);
161
162 if (dataProc != nullptr) {
163 AAudioStreamBuilder_setDataCallback(builder, dataProc, userContext);
164 }
165 if (errorProc != nullptr) {
166 AAudioStreamBuilder_setErrorCallback(builder, errorProc, userContext);
167 }
168 //AAudioStreamBuilder_setFramesPerDataCallback(builder, CALLBACK_SIZE_FRAMES);
169 //AAudioStreamBuilder_setBufferCapacityInFrames(builder, 48 * 8);
170
171 // Open an AAudioStream using the Builder.
172 result = AAudioStreamBuilder_openStream(builder, &mStream);
173
174 AAudioStreamBuilder_delete(builder);
175 return result;
176 }
177
close()178 aaudio_result_t close() {
179 if (mStream != nullptr) {
180 printf("call AAudioStream_close(%p)\n", mStream); fflush(stdout);
181 AAudioStream_close(mStream);
182 mStream = nullptr;
183 }
184 return AAUDIO_OK;
185 }
186
187 // Write zero data to fill up the buffer and prevent underruns.
prime()188 aaudio_result_t prime() {
189 int32_t samplesPerFrame = AAudioStream_getChannelCount(mStream);
190 const int numFrames = 32;
191 float zeros[numFrames * samplesPerFrame];
192 memset(zeros, 0, sizeof(zeros));
193 aaudio_result_t result = numFrames;
194 while (result == numFrames) {
195 result = AAudioStream_write(mStream, zeros, numFrames, 0);
196 }
197 return result;
198 }
199
200 // Start the stream. AAudio will start calling your callback function.
start()201 aaudio_result_t start() {
202 aaudio_result_t result = AAudioStream_requestStart(mStream);
203 if (result != AAUDIO_OK) {
204 printf("ERROR - AAudioStream_requestStart() returned %d %s\n",
205 result, AAudio_convertResultToText(result));
206 }
207 return result;
208 }
209
210 // Stop the stream. AAudio will stop calling your callback function.
stop()211 aaudio_result_t stop() {
212 aaudio_result_t result = AAudioStream_requestStop(mStream);
213 if (result != AAUDIO_OK) {
214 printf("ERROR - AAudioStream_requestStop() returned %d %s\n",
215 result, AAudio_convertResultToText(result));
216 }
217 int32_t xRunCount = AAudioStream_getXRunCount(mStream);
218 printf("AAudioStream_getXRunCount %d\n", xRunCount);
219 return result;
220 }
221
getStream()222 AAudioStream *getStream() const {
223 return mStream;
224 }
225
226 private:
227 AAudioStream *mStream = nullptr;
228 aaudio_sharing_mode_t mRequestedSharingMode = SHARING_MODE;
229 aaudio_performance_mode_t mRequestedPerformanceMode = PERFORMANCE_MODE;
230
231 };
232
233 typedef struct SineThreadedData_s {
234
235 SineGenerator sineOsc1;
236 SineGenerator sineOsc2;
237 Timestamp timestamps[MAX_TIMESTAMPS];
238 int64_t framesTotal = 0;
239 int64_t nextFrameToGlitch = FORCED_UNDERRUN_PERIOD_FRAMES;
240 int32_t minNumFrames = INT32_MAX;
241 int32_t maxNumFrames = 0;
242 int32_t timestampCount = 0; // in timestamps
243
244 int scheduler = 0;
245 bool schedulerChecked = false;
246 bool forceUnderruns = false;
247
248 AAudioSimplePlayer simplePlayer;
249 int32_t callbackCount = 0;
250 WakeUp waker{AAUDIO_OK};
251
252 } SineThreadedData_t;
253
254 // Callback function that fills the audio output buffer.
SimplePlayerDataCallbackProc(AAudioStream * stream,void * userData,void * audioData,int32_t numFrames)255 aaudio_data_callback_result_t SimplePlayerDataCallbackProc(
256 AAudioStream *stream,
257 void *userData,
258 void *audioData,
259 int32_t numFrames
260 ) {
261
262 // should not happen but just in case...
263 if (userData == nullptr) {
264 printf("ERROR - SimplePlayerDataCallbackProc needs userData\n");
265 return AAUDIO_CALLBACK_RESULT_STOP;
266 }
267 SineThreadedData_t *sineData = (SineThreadedData_t *) userData;
268 sineData->callbackCount++;
269
270 sineData->framesTotal += numFrames;
271
272 if (sineData->forceUnderruns) {
273 if (sineData->framesTotal > sineData->nextFrameToGlitch) {
274 usleep(FORCED_UNDERRUN_SLEEP_MICROS);
275 printf("Simulate glitch at %lld\n", (long long) sineData->framesTotal);
276 sineData->nextFrameToGlitch += FORCED_UNDERRUN_PERIOD_FRAMES;
277 }
278 }
279
280 if (!sineData->schedulerChecked) {
281 sineData->scheduler = sched_getscheduler(gettid());
282 sineData->schedulerChecked = true;
283 }
284
285 if (sineData->timestampCount < MAX_TIMESTAMPS) {
286 Timestamp *timestamp = &sineData->timestamps[sineData->timestampCount];
287 aaudio_result_t result = AAudioStream_getTimestamp(stream,
288 CLOCK_MONOTONIC, ×tamp->position, ×tamp->nanoseconds);
289 if (result == AAUDIO_OK && // valid?
290 (sineData->timestampCount == 0 || // first one?
291 (timestamp->position != (timestamp - 1)->position))) { // advanced position?
292 sineData->timestampCount++; // keep this one
293 }
294 }
295
296 if (numFrames > sineData->maxNumFrames) {
297 sineData->maxNumFrames = numFrames;
298 }
299 if (numFrames < sineData->minNumFrames) {
300 sineData->minNumFrames = numFrames;
301 }
302
303 int32_t samplesPerFrame = AAudioStream_getChannelCount(stream);
304 // This code only plays on the first one or two channels.
305 // TODO Support arbitrary number of channels.
306 switch (AAudioStream_getFormat(stream)) {
307 case AAUDIO_FORMAT_PCM_I16: {
308 int16_t *audioBuffer = (int16_t *) audioData;
309 // Render sine waves as shorts to first channel.
310 sineData->sineOsc1.render(&audioBuffer[0], samplesPerFrame, numFrames);
311 // Render sine waves to second channel if there is one.
312 if (samplesPerFrame > 1) {
313 sineData->sineOsc2.render(&audioBuffer[1], samplesPerFrame, numFrames);
314 }
315 }
316 break;
317 case AAUDIO_FORMAT_PCM_FLOAT: {
318 float *audioBuffer = (float *) audioData;
319 // Render sine waves as floats to first channel.
320 sineData->sineOsc1.render(&audioBuffer[0], samplesPerFrame, numFrames);
321 // Render sine waves to second channel if there is one.
322 if (samplesPerFrame > 1) {
323 sineData->sineOsc2.render(&audioBuffer[1], samplesPerFrame, numFrames);
324 }
325 }
326 break;
327 default:
328 return AAUDIO_CALLBACK_RESULT_STOP;
329 }
330
331 return AAUDIO_CALLBACK_RESULT_CONTINUE;
332 }
333
SimplePlayerErrorCallbackProc(AAudioStream * stream __unused,void * userData __unused,aaudio_result_t error)334 void SimplePlayerErrorCallbackProc(
335 AAudioStream *stream __unused,
336 void *userData __unused,
337 aaudio_result_t error) {
338 // should not happen but just in case...
339 if (userData == nullptr) {
340 printf("ERROR - MyPlayerErrorCallbackProc needs userData\n");
341 return;
342 }
343 SineThreadedData_t *sineData = (SineThreadedData_t *) userData;
344 android::status_t ret = sineData->waker.wake(error);
345 printf("Error Callback, error: %d, futex wake returns %d\n", error, ret);
346 }
347
348
349 #endif //AAUDIO_SIMPLE_PLAYER_H
350