blob: fd1fc45c83a2f6d6d3819cc05f20e46ec8bf392b [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
Phil Burk67ed9da2017-09-06 16:26:52 -070036#define MAX_TIMESTAMPS 16
Phil Burk187dcd42017-08-29 09:04:35 -070037
38typedef struct Timestamp {
39 int64_t position;
40 int64_t nanoseconds;
41} Timestamp;
42
Eric Laurent629afae2017-05-25 18:25:51 -070043/**
44 * Simple wrapper for AAudio that opens an output stream either in callback or blocking write mode.
45 */
46class AAudioSimplePlayer {
47public:
48 AAudioSimplePlayer() {}
49 ~AAudioSimplePlayer() {
50 close();
51 };
52
53 /**
54 * Call this before calling open().
55 * @param requestedSharingMode
56 */
57 void setSharingMode(aaudio_sharing_mode_t requestedSharingMode) {
58 mRequestedSharingMode = requestedSharingMode;
59 }
60
61 /**
62 * Call this before calling open().
63 * @param requestedPerformanceMode
64 */
65 void setPerformanceMode(aaudio_performance_mode_t requestedPerformanceMode) {
66 mRequestedPerformanceMode = requestedPerformanceMode;
67 }
68
Phil Burk44795232017-06-30 16:27:38 -070069 // TODO Extract a common base class for record and playback.
Phil Burk44795232017-06-30 16:27:38 -070070
71 /**
72 * Only call this after open() has been called.
73 */
74 int32_t getSampleRate() const {
Eric Laurent629afae2017-05-25 18:25:51 -070075 if (mStream == nullptr) {
76 return AAUDIO_ERROR_INVALID_STATE;
77 }
Phil Burk44795232017-06-30 16:27:38 -070078 return AAudioStream_getSampleRate(mStream);
Eric Laurent629afae2017-05-25 18:25:51 -070079 }
80
81 /**
82 * Only call this after open() has been called.
83 */
84 int32_t getChannelCount() {
85 if (mStream == nullptr) {
86 return AAUDIO_ERROR_INVALID_STATE;
87 }
Phil Burk44795232017-06-30 16:27:38 -070088 return AAudioStream_getChannelCount(mStream);
Eric Laurent629afae2017-05-25 18:25:51 -070089 }
90
91 /**
92 * Open a stream
93 */
Phil Burk44795232017-06-30 16:27:38 -070094 aaudio_result_t open(const AAudioParameters &parameters,
95 AAudioStream_dataCallback dataCallback = nullptr,
96 AAudioStream_errorCallback errorCallback = nullptr,
97 void *userContext = nullptr) {
98 aaudio_result_t result = AAUDIO_OK;
99
100 // Use an AAudioStreamBuilder to contain requested parameters.
101 AAudioStreamBuilder *builder = nullptr;
102 result = AAudio_createStreamBuilder(&builder);
103 if (result != AAUDIO_OK) return result;
104
105 parameters.applyParameters(builder); // apply args
106
107 AAudioStreamBuilder_setDirection(builder, AAUDIO_DIRECTION_OUTPUT);
108
109 if (dataCallback != nullptr) {
110 AAudioStreamBuilder_setDataCallback(builder, dataCallback, userContext);
111 }
112 if (errorCallback != nullptr) {
113 AAudioStreamBuilder_setErrorCallback(builder, errorCallback, userContext);
114 }
115 //AAudioStreamBuilder_setFramesPerDataCallback(builder, CALLBACK_SIZE_FRAMES);
116 //AAudioStreamBuilder_setBufferCapacityInFrames(builder, 48 * 8);
117
118 // Open an AAudioStream using the Builder.
119 result = AAudioStreamBuilder_openStream(builder, &mStream);
120
121 if (result == AAUDIO_OK) {
122 int32_t sizeInBursts = parameters.getNumberOfBursts();
Phil Burkbad0f572019-03-29 11:03:13 -0700123 int32_t framesPerBurst = AAudioStream_getFramesPerBurst(mStream);
124 int32_t bufferSizeFrames = sizeInBursts * framesPerBurst;
125 AAudioStream_setBufferSizeInFrames(mStream, bufferSizeFrames);
Phil Burk44795232017-06-30 16:27:38 -0700126 }
127
128 AAudioStreamBuilder_delete(builder);
129 return result;
130 }
131
Eric Laurent629afae2017-05-25 18:25:51 -0700132 aaudio_result_t open(int channelCount, int sampSampleRate, aaudio_format_t format,
Phil Burk44795232017-06-30 16:27:38 -0700133 AAudioStream_dataCallback dataProc,
134 AAudioStream_errorCallback errorProc,
Eric Laurent629afae2017-05-25 18:25:51 -0700135 void *userContext) {
136 aaudio_result_t result = AAUDIO_OK;
137
138 // Use an AAudioStreamBuilder to contain requested parameters.
Phil Burk44795232017-06-30 16:27:38 -0700139 AAudioStreamBuilder *builder = nullptr;
140 result = AAudio_createStreamBuilder(&builder);
Eric Laurent629afae2017-05-25 18:25:51 -0700141 if (result != AAUDIO_OK) return result;
142
Phil Burk44795232017-06-30 16:27:38 -0700143 AAudioStreamBuilder_setDirection(builder, AAUDIO_DIRECTION_OUTPUT);
144 AAudioStreamBuilder_setPerformanceMode(builder, mRequestedPerformanceMode);
145 AAudioStreamBuilder_setSharingMode(builder, mRequestedSharingMode);
146
147 AAudioStreamBuilder_setChannelCount(builder, channelCount);
148 AAudioStreamBuilder_setSampleRate(builder, sampSampleRate);
149 AAudioStreamBuilder_setFormat(builder, format);
150
Eric Laurent629afae2017-05-25 18:25:51 -0700151 if (dataProc != nullptr) {
Phil Burk44795232017-06-30 16:27:38 -0700152 AAudioStreamBuilder_setDataCallback(builder, dataProc, userContext);
Eric Laurent629afae2017-05-25 18:25:51 -0700153 }
154 if (errorProc != nullptr) {
Phil Burk44795232017-06-30 16:27:38 -0700155 AAudioStreamBuilder_setErrorCallback(builder, errorProc, userContext);
Eric Laurent629afae2017-05-25 18:25:51 -0700156 }
Phil Burk44795232017-06-30 16:27:38 -0700157 //AAudioStreamBuilder_setFramesPerDataCallback(builder, CALLBACK_SIZE_FRAMES);
158 //AAudioStreamBuilder_setBufferCapacityInFrames(builder, 48 * 8);
Eric Laurent629afae2017-05-25 18:25:51 -0700159
160 // Open an AAudioStream using the Builder.
Phil Burk44795232017-06-30 16:27:38 -0700161 result = AAudioStreamBuilder_openStream(builder, &mStream);
Eric Laurent629afae2017-05-25 18:25:51 -0700162
Phil Burk44795232017-06-30 16:27:38 -0700163 AAudioStreamBuilder_delete(builder);
Phil Burk67ed9da2017-09-06 16:26:52 -0700164
Eric Laurent629afae2017-05-25 18:25:51 -0700165 return result;
166 }
167
168 aaudio_result_t close() {
169 if (mStream != nullptr) {
Eric Laurent629afae2017-05-25 18:25:51 -0700170 AAudioStream_close(mStream);
171 mStream = nullptr;
Eric Laurent629afae2017-05-25 18:25:51 -0700172 }
173 return AAUDIO_OK;
174 }
175
176 // Write zero data to fill up the buffer and prevent underruns.
177 aaudio_result_t prime() {
178 int32_t samplesPerFrame = AAudioStream_getChannelCount(mStream);
179 const int numFrames = 32;
180 float zeros[numFrames * samplesPerFrame];
181 memset(zeros, 0, sizeof(zeros));
182 aaudio_result_t result = numFrames;
183 while (result == numFrames) {
184 result = AAudioStream_write(mStream, zeros, numFrames, 0);
185 }
186 return result;
187 }
188
189 // Start the stream. AAudio will start calling your callback function.
190 aaudio_result_t start() {
191 aaudio_result_t result = AAudioStream_requestStart(mStream);
192 if (result != AAUDIO_OK) {
Phil Burk5bce4c92018-10-18 09:29:14 -0700193 printf("ERROR - AAudioStream_requestStart(output) returned %d %s\n",
Eric Laurent629afae2017-05-25 18:25:51 -0700194 result, AAudio_convertResultToText(result));
195 }
196 return result;
197 }
198
199 // Stop the stream. AAudio will stop calling your callback function.
200 aaudio_result_t stop() {
201 aaudio_result_t result = AAudioStream_requestStop(mStream);
202 if (result != AAUDIO_OK) {
Phil Burk5bce4c92018-10-18 09:29:14 -0700203 printf("ERROR - AAudioStream_requestStop(output) returned %d %s\n",
Phil Burk67ed9da2017-09-06 16:26:52 -0700204 result, AAudio_convertResultToText(result));
Eric Laurent629afae2017-05-25 18:25:51 -0700205 }
206 int32_t xRunCount = AAudioStream_getXRunCount(mStream);
207 printf("AAudioStream_getXRunCount %d\n", xRunCount);
208 return result;
209 }
210
Phil Burk67ed9da2017-09-06 16:26:52 -0700211 // Pause the stream. AAudio will stop calling your callback function.
212 aaudio_result_t pause() {
213 aaudio_result_t result = AAudioStream_requestPause(mStream);
214 if (result != AAUDIO_OK) {
Phil Burk5bce4c92018-10-18 09:29:14 -0700215 printf("ERROR - AAudioStream_requestPause(output) returned %d %s\n",
Phil Burk67ed9da2017-09-06 16:26:52 -0700216 result, AAudio_convertResultToText(result));
217 }
218 int32_t xRunCount = AAudioStream_getXRunCount(mStream);
219 printf("AAudioStream_getXRunCount %d\n", xRunCount);
220 return result;
221 }
222
Phil Burk45ebf852018-10-11 12:28:54 -0700223 aaudio_result_t waitUntilPaused() {
224 aaudio_result_t result = AAUDIO_OK;
225 aaudio_stream_state_t currentState = AAudioStream_getState(mStream);
226 aaudio_stream_state_t inputState = AAUDIO_STREAM_STATE_PAUSING;
227 while (result == AAUDIO_OK && currentState == AAUDIO_STREAM_STATE_PAUSING) {
228 result = AAudioStream_waitForStateChange(mStream, inputState,
229 &currentState, NANOS_PER_SECOND);
230 inputState = currentState;
231 }
232 if (result != AAUDIO_OK) {
233 return result;
234 }
235 return (currentState == AAUDIO_STREAM_STATE_PAUSED)
236 ? AAUDIO_OK : AAUDIO_ERROR_INVALID_STATE;
237 }
238
Phil Burk67ed9da2017-09-06 16:26:52 -0700239 // Flush the stream. AAudio will stop calling your callback function.
240 aaudio_result_t flush() {
241 aaudio_result_t result = AAudioStream_requestFlush(mStream);
242 if (result != AAUDIO_OK) {
Phil Burk5bce4c92018-10-18 09:29:14 -0700243 printf("ERROR - AAudioStream_requestFlush(output) returned %d %s\n",
Phil Burk67ed9da2017-09-06 16:26:52 -0700244 result, AAudio_convertResultToText(result));
245 }
246 return result;
247 }
248
Eric Laurent629afae2017-05-25 18:25:51 -0700249 AAudioStream *getStream() const {
250 return mStream;
251 }
252
253private:
Eric Laurent629afae2017-05-25 18:25:51 -0700254 AAudioStream *mStream = nullptr;
255 aaudio_sharing_mode_t mRequestedSharingMode = SHARING_MODE;
256 aaudio_performance_mode_t mRequestedPerformanceMode = PERFORMANCE_MODE;
Phil Burk7a61a3a2017-07-10 11:53:09 -0700257
Eric Laurent629afae2017-05-25 18:25:51 -0700258};
259
260typedef struct SineThreadedData_s {
Phil Burk7a61a3a2017-07-10 11:53:09 -0700261
Phil Burk67ed9da2017-09-06 16:26:52 -0700262 SineGenerator sineOscillators[MAX_CHANNELS];
263 Timestamp timestamps[MAX_TIMESTAMPS];
264 int64_t framesTotal = 0;
265 int64_t nextFrameToGlitch = FORCED_UNDERRUN_PERIOD_FRAMES;
266 int32_t minNumFrames = INT32_MAX;
267 int32_t maxNumFrames = 0;
268 int32_t timestampCount = 0; // in timestamps
269 int32_t sampleRate = 48000;
270 int32_t prefixToneFrames = 0;
271 bool sweepSetup = false;
Phil Burk7a61a3a2017-07-10 11:53:09 -0700272
Phil Burk67ed9da2017-09-06 16:26:52 -0700273 int scheduler = 0;
274 bool schedulerChecked = false;
Phil Burkf1533002019-02-07 13:04:28 -0800275 int32_t hangTimeMSec = 0;
Phil Burk7a61a3a2017-07-10 11:53:09 -0700276
277 AAudioSimplePlayer simplePlayer;
278 int32_t callbackCount = 0;
279 WakeUp waker{AAUDIO_OK};
280
Phil Burk67ed9da2017-09-06 16:26:52 -0700281 /**
282 * Set sampleRate first.
283 */
284 void setupSineBlip() {
285 for (int i = 0; i < MAX_CHANNELS; ++i) {
286 double centerFrequency = 880.0 * (i + 2);
287 sineOscillators[i].setup(centerFrequency, sampleRate);
288 sineOscillators[i].setSweep(centerFrequency, centerFrequency, 0.0);
289 }
290 }
291
292 void setupSineSweeps() {
293 for (int i = 0; i < MAX_CHANNELS; ++i) {
294 double centerFrequency = 220.0 * (i + 2);
295 sineOscillators[i].setup(centerFrequency, sampleRate);
296 double minFrequency = centerFrequency * 2.0 / 3.0;
297 // Change range slightly so they will go out of phase.
298 double maxFrequency = centerFrequency * 3.0 / 2.0;
299 double sweepSeconds = 5.0 + i;
300 sineOscillators[i].setSweep(minFrequency, maxFrequency, sweepSeconds);
301 }
302 sweepSetup = true;
303 }
304
Eric Laurent629afae2017-05-25 18:25:51 -0700305} SineThreadedData_t;
306
307// Callback function that fills the audio output buffer.
308aaudio_data_callback_result_t SimplePlayerDataCallbackProc(
309 AAudioStream *stream,
310 void *userData,
311 void *audioData,
312 int32_t numFrames
313 ) {
314
315 // should not happen but just in case...
316 if (userData == nullptr) {
Phil Burkfaeb8b22017-07-25 15:15:07 -0700317 printf("ERROR - SimplePlayerDataCallbackProc needs userData\n");
Eric Laurent629afae2017-05-25 18:25:51 -0700318 return AAUDIO_CALLBACK_RESULT_STOP;
319 }
320 SineThreadedData_t *sineData = (SineThreadedData_t *) userData;
321
Phil Burk67ed9da2017-09-06 16:26:52 -0700322 // Play an initial high tone so we can tell whether the beginning was truncated.
323 if (!sineData->sweepSetup && sineData->framesTotal >= sineData->prefixToneFrames) {
324 sineData->setupSineSweeps();
325 }
Phil Burkfaeb8b22017-07-25 15:15:07 -0700326
Phil Burkf1533002019-02-07 13:04:28 -0800327 if (sineData->hangTimeMSec > 0) {
Phil Burkfaeb8b22017-07-25 15:15:07 -0700328 if (sineData->framesTotal > sineData->nextFrameToGlitch) {
Phil Burkf1533002019-02-07 13:04:28 -0800329 usleep(sineData->hangTimeMSec * 1000);
330 printf("Hang callback at %lld frames for %d msec\n",
331 (long long) sineData->framesTotal,
332 sineData->hangTimeMSec);
Phil Burkfaeb8b22017-07-25 15:15:07 -0700333 sineData->nextFrameToGlitch += FORCED_UNDERRUN_PERIOD_FRAMES;
334 }
335 }
336
Eric Laurent629afae2017-05-25 18:25:51 -0700337 if (!sineData->schedulerChecked) {
338 sineData->scheduler = sched_getscheduler(gettid());
339 sineData->schedulerChecked = true;
340 }
341
Phil Burk187dcd42017-08-29 09:04:35 -0700342 if (sineData->timestampCount < MAX_TIMESTAMPS) {
343 Timestamp *timestamp = &sineData->timestamps[sineData->timestampCount];
344 aaudio_result_t result = AAudioStream_getTimestamp(stream,
345 CLOCK_MONOTONIC, &timestamp->position, &timestamp->nanoseconds);
346 if (result == AAUDIO_OK && // valid?
347 (sineData->timestampCount == 0 || // first one?
348 (timestamp->position != (timestamp - 1)->position))) { // advanced position?
349 sineData->timestampCount++; // keep this one
350 }
351 }
352
Phil Burkfcf9efd2017-07-14 08:25:08 -0700353 if (numFrames > sineData->maxNumFrames) {
354 sineData->maxNumFrames = numFrames;
355 }
356 if (numFrames < sineData->minNumFrames) {
357 sineData->minNumFrames = numFrames;
358 }
359
Eric Laurent629afae2017-05-25 18:25:51 -0700360 int32_t samplesPerFrame = AAudioStream_getChannelCount(stream);
Phil Burk67ed9da2017-09-06 16:26:52 -0700361
362
363 int numActiveOscilators = (samplesPerFrame > MAX_CHANNELS) ? MAX_CHANNELS : samplesPerFrame;
Eric Laurent629afae2017-05-25 18:25:51 -0700364 switch (AAudioStream_getFormat(stream)) {
365 case AAUDIO_FORMAT_PCM_I16: {
366 int16_t *audioBuffer = (int16_t *) audioData;
Phil Burk67ed9da2017-09-06 16:26:52 -0700367 for (int i = 0; i < numActiveOscilators; ++i) {
368 sineData->sineOscillators[i].render(&audioBuffer[i], samplesPerFrame,
369 numFrames);
Eric Laurent629afae2017-05-25 18:25:51 -0700370 }
371 }
Phil Burk67ed9da2017-09-06 16:26:52 -0700372 break;
Eric Laurent629afae2017-05-25 18:25:51 -0700373 case AAUDIO_FORMAT_PCM_FLOAT: {
374 float *audioBuffer = (float *) audioData;
Phil Burk67ed9da2017-09-06 16:26:52 -0700375 for (int i = 0; i < numActiveOscilators; ++i) {
376 sineData->sineOscillators[i].render(&audioBuffer[i], samplesPerFrame,
377 numFrames);
Eric Laurent629afae2017-05-25 18:25:51 -0700378 }
379 }
Phil Burk67ed9da2017-09-06 16:26:52 -0700380 break;
Eric Laurent629afae2017-05-25 18:25:51 -0700381 default:
382 return AAUDIO_CALLBACK_RESULT_STOP;
383 }
384
Phil Burk67ed9da2017-09-06 16:26:52 -0700385 sineData->callbackCount++;
386 sineData->framesTotal += numFrames;
Eric Laurent629afae2017-05-25 18:25:51 -0700387 return AAUDIO_CALLBACK_RESULT_CONTINUE;
388}
389
390void SimplePlayerErrorCallbackProc(
391 AAudioStream *stream __unused,
392 void *userData __unused,
Phil Burk7a61a3a2017-07-10 11:53:09 -0700393 aaudio_result_t error) {
394 // should not happen but just in case...
395 if (userData == nullptr) {
396 printf("ERROR - MyPlayerErrorCallbackProc needs userData\n");
397 return;
398 }
399 SineThreadedData_t *sineData = (SineThreadedData_t *) userData;
400 android::status_t ret = sineData->waker.wake(error);
401 printf("Error Callback, error: %d, futex wake returns %d\n", error, ret);
Eric Laurent629afae2017-05-25 18:25:51 -0700402}
403
Phil Burk7a61a3a2017-07-10 11:53:09 -0700404
Eric Laurent629afae2017-05-25 18:25:51 -0700405#endif //AAUDIO_SIMPLE_PLAYER_H