blob: 3fafecf9b0e09e7077d54e045e8d96468bba938b [file] [log] [blame]
Eric Laurent629afae2017-05-25 18:25:51 -07001/*
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
Eric Laurent629afae2017-05-25 18:25:51 -070022#include <sched.h>
Phil Burk67ed9da2017-09-06 16:26:52 -070023#include <unistd.h>
Eric Laurent629afae2017-05-25 18:25:51 -070024
25#include <aaudio/AAudio.h>
Phil Burk44795232017-06-30 16:27:38 -070026#include "AAudioArgsParser.h"
Eric Laurent629afae2017-05-25 18:25:51 -070027#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
Phil Burk967abf82017-11-21 10:02:34 -080033// Arbitrary period for glitches
34#define FORCED_UNDERRUN_PERIOD_FRAMES (2 * 48000)
Phil Burkfaeb8b22017-07-25 15:15:07 -070035// How long to sleep in a callback to cause an intentional glitch. For testing.
36#define FORCED_UNDERRUN_SLEEP_MICROS (10 * 1000)
37
Phil Burk67ed9da2017-09-06 16:26:52 -070038#define MAX_TIMESTAMPS 16
Phil Burk187dcd42017-08-29 09:04:35 -070039
40typedef struct Timestamp {
41 int64_t position;
42 int64_t nanoseconds;
43} Timestamp;
44
Eric Laurent629afae2017-05-25 18:25:51 -070045/**
46 * Simple wrapper for AAudio that opens an output stream either in callback or blocking write mode.
47 */
48class AAudioSimplePlayer {
49public:
50 AAudioSimplePlayer() {}
51 ~AAudioSimplePlayer() {
52 close();
53 };
54
55 /**
56 * Call this before calling open().
57 * @param requestedSharingMode
58 */
59 void setSharingMode(aaudio_sharing_mode_t requestedSharingMode) {
60 mRequestedSharingMode = requestedSharingMode;
61 }
62
63 /**
64 * Call this before calling open().
65 * @param requestedPerformanceMode
66 */
67 void setPerformanceMode(aaudio_performance_mode_t requestedPerformanceMode) {
68 mRequestedPerformanceMode = requestedPerformanceMode;
69 }
70
Phil Burk44795232017-06-30 16:27:38 -070071 // TODO Extract a common base class for record and playback.
Phil Burk44795232017-06-30 16:27:38 -070072
73 /**
74 * Only call this after open() has been called.
75 */
76 int32_t getSampleRate() const {
Eric Laurent629afae2017-05-25 18:25:51 -070077 if (mStream == nullptr) {
78 return AAUDIO_ERROR_INVALID_STATE;
79 }
Phil Burk44795232017-06-30 16:27:38 -070080 return AAudioStream_getSampleRate(mStream);
Eric Laurent629afae2017-05-25 18:25:51 -070081 }
82
83 /**
84 * Only call this after open() has been called.
85 */
86 int32_t getChannelCount() {
87 if (mStream == nullptr) {
88 return AAUDIO_ERROR_INVALID_STATE;
89 }
Phil Burk44795232017-06-30 16:27:38 -070090 return AAudioStream_getChannelCount(mStream);
Eric Laurent629afae2017-05-25 18:25:51 -070091 }
92
93 /**
94 * Open a stream
95 */
Phil Burk44795232017-06-30 16:27:38 -070096 aaudio_result_t open(const AAudioParameters &parameters,
97 AAudioStream_dataCallback dataCallback = nullptr,
98 AAudioStream_errorCallback errorCallback = nullptr,
99 void *userContext = nullptr) {
100 aaudio_result_t result = AAUDIO_OK;
101
102 // Use an AAudioStreamBuilder to contain requested parameters.
103 AAudioStreamBuilder *builder = nullptr;
104 result = AAudio_createStreamBuilder(&builder);
105 if (result != AAUDIO_OK) return result;
106
107 parameters.applyParameters(builder); // apply args
108
109 AAudioStreamBuilder_setDirection(builder, AAUDIO_DIRECTION_OUTPUT);
110
111 if (dataCallback != nullptr) {
112 AAudioStreamBuilder_setDataCallback(builder, dataCallback, userContext);
113 }
114 if (errorCallback != nullptr) {
115 AAudioStreamBuilder_setErrorCallback(builder, errorCallback, userContext);
116 }
117 //AAudioStreamBuilder_setFramesPerDataCallback(builder, CALLBACK_SIZE_FRAMES);
118 //AAudioStreamBuilder_setBufferCapacityInFrames(builder, 48 * 8);
119
120 // Open an AAudioStream using the Builder.
121 result = AAudioStreamBuilder_openStream(builder, &mStream);
122
123 if (result == AAUDIO_OK) {
124 int32_t sizeInBursts = parameters.getNumberOfBursts();
125 if (sizeInBursts > 0) {
126 int32_t framesPerBurst = AAudioStream_getFramesPerBurst(mStream);
127 AAudioStream_setBufferSizeInFrames(mStream, sizeInBursts * framesPerBurst);
128 }
129 }
130
131 AAudioStreamBuilder_delete(builder);
132 return result;
133 }
134
Eric Laurent629afae2017-05-25 18:25:51 -0700135 aaudio_result_t open(int channelCount, int sampSampleRate, aaudio_format_t format,
Phil Burk44795232017-06-30 16:27:38 -0700136 AAudioStream_dataCallback dataProc,
137 AAudioStream_errorCallback errorProc,
Eric Laurent629afae2017-05-25 18:25:51 -0700138 void *userContext) {
139 aaudio_result_t result = AAUDIO_OK;
140
141 // Use an AAudioStreamBuilder to contain requested parameters.
Phil Burk44795232017-06-30 16:27:38 -0700142 AAudioStreamBuilder *builder = nullptr;
143 result = AAudio_createStreamBuilder(&builder);
Eric Laurent629afae2017-05-25 18:25:51 -0700144 if (result != AAUDIO_OK) return result;
145
Phil Burk44795232017-06-30 16:27:38 -0700146 AAudioStreamBuilder_setDirection(builder, AAUDIO_DIRECTION_OUTPUT);
147 AAudioStreamBuilder_setPerformanceMode(builder, mRequestedPerformanceMode);
148 AAudioStreamBuilder_setSharingMode(builder, mRequestedSharingMode);
149
150 AAudioStreamBuilder_setChannelCount(builder, channelCount);
151 AAudioStreamBuilder_setSampleRate(builder, sampSampleRate);
152 AAudioStreamBuilder_setFormat(builder, format);
153
Eric Laurent629afae2017-05-25 18:25:51 -0700154 if (dataProc != nullptr) {
Phil Burk44795232017-06-30 16:27:38 -0700155 AAudioStreamBuilder_setDataCallback(builder, dataProc, userContext);
Eric Laurent629afae2017-05-25 18:25:51 -0700156 }
157 if (errorProc != nullptr) {
Phil Burk44795232017-06-30 16:27:38 -0700158 AAudioStreamBuilder_setErrorCallback(builder, errorProc, userContext);
Eric Laurent629afae2017-05-25 18:25:51 -0700159 }
Phil Burk44795232017-06-30 16:27:38 -0700160 //AAudioStreamBuilder_setFramesPerDataCallback(builder, CALLBACK_SIZE_FRAMES);
161 //AAudioStreamBuilder_setBufferCapacityInFrames(builder, 48 * 8);
Eric Laurent629afae2017-05-25 18:25:51 -0700162
163 // Open an AAudioStream using the Builder.
Phil Burk44795232017-06-30 16:27:38 -0700164 result = AAudioStreamBuilder_openStream(builder, &mStream);
Eric Laurent629afae2017-05-25 18:25:51 -0700165
Phil Burk44795232017-06-30 16:27:38 -0700166 AAudioStreamBuilder_delete(builder);
Phil Burk67ed9da2017-09-06 16:26:52 -0700167
Eric Laurent629afae2017-05-25 18:25:51 -0700168 return result;
169 }
170
171 aaudio_result_t close() {
172 if (mStream != nullptr) {
173 printf("call AAudioStream_close(%p)\n", mStream); fflush(stdout);
174 AAudioStream_close(mStream);
175 mStream = nullptr;
Eric Laurent629afae2017-05-25 18:25:51 -0700176 }
177 return AAUDIO_OK;
178 }
179
180 // Write zero data to fill up the buffer and prevent underruns.
181 aaudio_result_t prime() {
182 int32_t samplesPerFrame = AAudioStream_getChannelCount(mStream);
183 const int numFrames = 32;
184 float zeros[numFrames * samplesPerFrame];
185 memset(zeros, 0, sizeof(zeros));
186 aaudio_result_t result = numFrames;
187 while (result == numFrames) {
188 result = AAudioStream_write(mStream, zeros, numFrames, 0);
189 }
190 return result;
191 }
192
193 // Start the stream. AAudio will start calling your callback function.
194 aaudio_result_t start() {
195 aaudio_result_t result = AAudioStream_requestStart(mStream);
196 if (result != AAUDIO_OK) {
197 printf("ERROR - AAudioStream_requestStart() returned %d %s\n",
198 result, AAudio_convertResultToText(result));
199 }
200 return result;
201 }
202
203 // Stop the stream. AAudio will stop calling your callback function.
204 aaudio_result_t stop() {
205 aaudio_result_t result = AAudioStream_requestStop(mStream);
206 if (result != AAUDIO_OK) {
207 printf("ERROR - AAudioStream_requestStop() returned %d %s\n",
Phil Burk67ed9da2017-09-06 16:26:52 -0700208 result, AAudio_convertResultToText(result));
Eric Laurent629afae2017-05-25 18:25:51 -0700209 }
210 int32_t xRunCount = AAudioStream_getXRunCount(mStream);
211 printf("AAudioStream_getXRunCount %d\n", xRunCount);
212 return result;
213 }
214
Phil Burk67ed9da2017-09-06 16:26:52 -0700215 // Pause the stream. AAudio will stop calling your callback function.
216 aaudio_result_t pause() {
217 aaudio_result_t result = AAudioStream_requestPause(mStream);
218 if (result != AAUDIO_OK) {
219 printf("ERROR - AAudioStream_requestPause() returned %d %s\n",
220 result, AAudio_convertResultToText(result));
221 }
222 int32_t xRunCount = AAudioStream_getXRunCount(mStream);
223 printf("AAudioStream_getXRunCount %d\n", xRunCount);
224 return result;
225 }
226
227 // Flush the stream. AAudio will stop calling your callback function.
228 aaudio_result_t flush() {
229 aaudio_result_t result = AAudioStream_requestFlush(mStream);
230 if (result != AAUDIO_OK) {
231 printf("ERROR - AAudioStream_requestFlush() returned %d %s\n",
232 result, AAudio_convertResultToText(result));
233 }
234 return result;
235 }
236
Eric Laurent629afae2017-05-25 18:25:51 -0700237 AAudioStream *getStream() const {
238 return mStream;
239 }
240
241private:
Eric Laurent629afae2017-05-25 18:25:51 -0700242 AAudioStream *mStream = nullptr;
243 aaudio_sharing_mode_t mRequestedSharingMode = SHARING_MODE;
244 aaudio_performance_mode_t mRequestedPerformanceMode = PERFORMANCE_MODE;
Phil Burk7a61a3a2017-07-10 11:53:09 -0700245
Eric Laurent629afae2017-05-25 18:25:51 -0700246};
247
248typedef struct SineThreadedData_s {
Phil Burk7a61a3a2017-07-10 11:53:09 -0700249
Phil Burk67ed9da2017-09-06 16:26:52 -0700250 SineGenerator sineOscillators[MAX_CHANNELS];
251 Timestamp timestamps[MAX_TIMESTAMPS];
252 int64_t framesTotal = 0;
253 int64_t nextFrameToGlitch = FORCED_UNDERRUN_PERIOD_FRAMES;
254 int32_t minNumFrames = INT32_MAX;
255 int32_t maxNumFrames = 0;
256 int32_t timestampCount = 0; // in timestamps
257 int32_t sampleRate = 48000;
258 int32_t prefixToneFrames = 0;
259 bool sweepSetup = false;
Phil Burk7a61a3a2017-07-10 11:53:09 -0700260
Phil Burk67ed9da2017-09-06 16:26:52 -0700261 int scheduler = 0;
262 bool schedulerChecked = false;
263 bool forceUnderruns = false;
Phil Burk7a61a3a2017-07-10 11:53:09 -0700264
265 AAudioSimplePlayer simplePlayer;
266 int32_t callbackCount = 0;
267 WakeUp waker{AAUDIO_OK};
268
Phil Burk67ed9da2017-09-06 16:26:52 -0700269 /**
270 * Set sampleRate first.
271 */
272 void setupSineBlip() {
273 for (int i = 0; i < MAX_CHANNELS; ++i) {
274 double centerFrequency = 880.0 * (i + 2);
275 sineOscillators[i].setup(centerFrequency, sampleRate);
276 sineOscillators[i].setSweep(centerFrequency, centerFrequency, 0.0);
277 }
278 }
279
280 void setupSineSweeps() {
281 for (int i = 0; i < MAX_CHANNELS; ++i) {
282 double centerFrequency = 220.0 * (i + 2);
283 sineOscillators[i].setup(centerFrequency, sampleRate);
284 double minFrequency = centerFrequency * 2.0 / 3.0;
285 // Change range slightly so they will go out of phase.
286 double maxFrequency = centerFrequency * 3.0 / 2.0;
287 double sweepSeconds = 5.0 + i;
288 sineOscillators[i].setSweep(minFrequency, maxFrequency, sweepSeconds);
289 }
290 sweepSetup = true;
291 }
292
Eric Laurent629afae2017-05-25 18:25:51 -0700293} SineThreadedData_t;
294
295// Callback function that fills the audio output buffer.
296aaudio_data_callback_result_t SimplePlayerDataCallbackProc(
297 AAudioStream *stream,
298 void *userData,
299 void *audioData,
300 int32_t numFrames
301 ) {
302
303 // should not happen but just in case...
304 if (userData == nullptr) {
Phil Burkfaeb8b22017-07-25 15:15:07 -0700305 printf("ERROR - SimplePlayerDataCallbackProc needs userData\n");
Eric Laurent629afae2017-05-25 18:25:51 -0700306 return AAUDIO_CALLBACK_RESULT_STOP;
307 }
308 SineThreadedData_t *sineData = (SineThreadedData_t *) userData;
309
Phil Burk67ed9da2017-09-06 16:26:52 -0700310 // Play an initial high tone so we can tell whether the beginning was truncated.
311 if (!sineData->sweepSetup && sineData->framesTotal >= sineData->prefixToneFrames) {
312 sineData->setupSineSweeps();
313 }
Phil Burkfaeb8b22017-07-25 15:15:07 -0700314
315 if (sineData->forceUnderruns) {
316 if (sineData->framesTotal > sineData->nextFrameToGlitch) {
317 usleep(FORCED_UNDERRUN_SLEEP_MICROS);
318 printf("Simulate glitch at %lld\n", (long long) sineData->framesTotal);
319 sineData->nextFrameToGlitch += FORCED_UNDERRUN_PERIOD_FRAMES;
320 }
321 }
322
Eric Laurent629afae2017-05-25 18:25:51 -0700323 if (!sineData->schedulerChecked) {
324 sineData->scheduler = sched_getscheduler(gettid());
325 sineData->schedulerChecked = true;
326 }
327
Phil Burk187dcd42017-08-29 09:04:35 -0700328 if (sineData->timestampCount < MAX_TIMESTAMPS) {
329 Timestamp *timestamp = &sineData->timestamps[sineData->timestampCount];
330 aaudio_result_t result = AAudioStream_getTimestamp(stream,
331 CLOCK_MONOTONIC, &timestamp->position, &timestamp->nanoseconds);
332 if (result == AAUDIO_OK && // valid?
333 (sineData->timestampCount == 0 || // first one?
334 (timestamp->position != (timestamp - 1)->position))) { // advanced position?
335 sineData->timestampCount++; // keep this one
336 }
337 }
338
Phil Burkfcf9efd2017-07-14 08:25:08 -0700339 if (numFrames > sineData->maxNumFrames) {
340 sineData->maxNumFrames = numFrames;
341 }
342 if (numFrames < sineData->minNumFrames) {
343 sineData->minNumFrames = numFrames;
344 }
345
Eric Laurent629afae2017-05-25 18:25:51 -0700346 int32_t samplesPerFrame = AAudioStream_getChannelCount(stream);
Phil Burk67ed9da2017-09-06 16:26:52 -0700347
348
349 int numActiveOscilators = (samplesPerFrame > MAX_CHANNELS) ? MAX_CHANNELS : samplesPerFrame;
Eric Laurent629afae2017-05-25 18:25:51 -0700350 switch (AAudioStream_getFormat(stream)) {
351 case AAUDIO_FORMAT_PCM_I16: {
352 int16_t *audioBuffer = (int16_t *) audioData;
Phil Burk67ed9da2017-09-06 16:26:52 -0700353 for (int i = 0; i < numActiveOscilators; ++i) {
354 sineData->sineOscillators[i].render(&audioBuffer[i], samplesPerFrame,
355 numFrames);
Eric Laurent629afae2017-05-25 18:25:51 -0700356 }
357 }
Phil Burk67ed9da2017-09-06 16:26:52 -0700358 break;
Eric Laurent629afae2017-05-25 18:25:51 -0700359 case AAUDIO_FORMAT_PCM_FLOAT: {
360 float *audioBuffer = (float *) audioData;
Phil Burk67ed9da2017-09-06 16:26:52 -0700361 for (int i = 0; i < numActiveOscilators; ++i) {
362 sineData->sineOscillators[i].render(&audioBuffer[i], samplesPerFrame,
363 numFrames);
Eric Laurent629afae2017-05-25 18:25:51 -0700364 }
365 }
Phil Burk67ed9da2017-09-06 16:26:52 -0700366 break;
Eric Laurent629afae2017-05-25 18:25:51 -0700367 default:
368 return AAUDIO_CALLBACK_RESULT_STOP;
369 }
370
Phil Burk67ed9da2017-09-06 16:26:52 -0700371 sineData->callbackCount++;
372 sineData->framesTotal += numFrames;
Eric Laurent629afae2017-05-25 18:25:51 -0700373 return AAUDIO_CALLBACK_RESULT_CONTINUE;
374}
375
376void SimplePlayerErrorCallbackProc(
377 AAudioStream *stream __unused,
378 void *userData __unused,
Phil Burk7a61a3a2017-07-10 11:53:09 -0700379 aaudio_result_t error) {
380 // should not happen but just in case...
381 if (userData == nullptr) {
382 printf("ERROR - MyPlayerErrorCallbackProc needs userData\n");
383 return;
384 }
385 SineThreadedData_t *sineData = (SineThreadedData_t *) userData;
386 android::status_t ret = sineData->waker.wake(error);
387 printf("Error Callback, error: %d, futex wake returns %d\n", error, ret);
Eric Laurent629afae2017-05-25 18:25:51 -0700388}
389
Phil Burk7a61a3a2017-07-10 11:53:09 -0700390
Eric Laurent629afae2017-05-25 18:25:51 -0700391#endif //AAUDIO_SIMPLE_PLAYER_H