blob: 606c4baaf277983adf94aa735cfd8dd7cfc5b761 [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
22#include <unistd.h>
23#include <sched.h>
24
25#include <aaudio/AAudio.h>
Phil Burk7a61a3a2017-07-10 11:53:09 -070026#include <atomic>
Phil Burk44795232017-06-30 16:27:38 -070027#include "AAudioArgsParser.h"
Eric Laurent629afae2017-05-25 18:25:51 -070028#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
Phil Burkfaeb8b22017-07-25 15:15:07 -070034// 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
Phil Burk187dcd42017-08-29 09:04:35 -070039#define MAX_TIMESTAMPS 16
40
41typedef struct Timestamp {
42 int64_t position;
43 int64_t nanoseconds;
44} Timestamp;
45
Eric Laurent629afae2017-05-25 18:25:51 -070046/**
47 * Simple wrapper for AAudio that opens an output stream either in callback or blocking write mode.
48 */
49class AAudioSimplePlayer {
50public:
51 AAudioSimplePlayer() {}
52 ~AAudioSimplePlayer() {
53 close();
54 };
55
56 /**
57 * Call this before calling open().
58 * @param requestedSharingMode
59 */
60 void setSharingMode(aaudio_sharing_mode_t requestedSharingMode) {
61 mRequestedSharingMode = requestedSharingMode;
62 }
63
64 /**
65 * Call this before calling open().
66 * @param requestedPerformanceMode
67 */
68 void setPerformanceMode(aaudio_performance_mode_t requestedPerformanceMode) {
69 mRequestedPerformanceMode = requestedPerformanceMode;
70 }
71
Phil Burk44795232017-06-30 16:27:38 -070072 // TODO Extract a common base class for record and playback.
Eric Laurent629afae2017-05-25 18:25:51 -070073 /**
74 * Also known as "sample rate"
75 * Only call this after open() has been called.
76 */
Phil Burk44795232017-06-30 16:27:38 -070077 int32_t getFramesPerSecond() const {
78 return getSampleRate(); // alias
79 }
80
81 /**
82 * Only call this after open() has been called.
83 */
84 int32_t getSampleRate() const {
Eric Laurent629afae2017-05-25 18:25:51 -070085 if (mStream == nullptr) {
86 return AAUDIO_ERROR_INVALID_STATE;
87 }
Phil Burk44795232017-06-30 16:27:38 -070088 return AAudioStream_getSampleRate(mStream);
Eric Laurent629afae2017-05-25 18:25:51 -070089 }
90
91 /**
92 * Only call this after open() has been called.
93 */
94 int32_t getChannelCount() {
95 if (mStream == nullptr) {
96 return AAUDIO_ERROR_INVALID_STATE;
97 }
Phil Burk44795232017-06-30 16:27:38 -070098 return AAudioStream_getChannelCount(mStream);
Eric Laurent629afae2017-05-25 18:25:51 -070099 }
100
101 /**
102 * Open a stream
103 */
Phil Burk44795232017-06-30 16:27:38 -0700104 aaudio_result_t open(const AAudioParameters &parameters,
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
Eric Laurent629afae2017-05-25 18:25:51 -0700143 aaudio_result_t open(int channelCount, int sampSampleRate, aaudio_format_t format,
Phil Burk44795232017-06-30 16:27:38 -0700144 AAudioStream_dataCallback dataProc,
145 AAudioStream_errorCallback errorProc,
Eric Laurent629afae2017-05-25 18:25:51 -0700146 void *userContext) {
147 aaudio_result_t result = AAUDIO_OK;
148
149 // Use an AAudioStreamBuilder to contain requested parameters.
Phil Burk44795232017-06-30 16:27:38 -0700150 AAudioStreamBuilder *builder = nullptr;
151 result = AAudio_createStreamBuilder(&builder);
Eric Laurent629afae2017-05-25 18:25:51 -0700152 if (result != AAUDIO_OK) return result;
153
Phil Burk44795232017-06-30 16:27:38 -0700154 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
Eric Laurent629afae2017-05-25 18:25:51 -0700162 if (dataProc != nullptr) {
Phil Burk44795232017-06-30 16:27:38 -0700163 AAudioStreamBuilder_setDataCallback(builder, dataProc, userContext);
Eric Laurent629afae2017-05-25 18:25:51 -0700164 }
165 if (errorProc != nullptr) {
Phil Burk44795232017-06-30 16:27:38 -0700166 AAudioStreamBuilder_setErrorCallback(builder, errorProc, userContext);
Eric Laurent629afae2017-05-25 18:25:51 -0700167 }
Phil Burk44795232017-06-30 16:27:38 -0700168 //AAudioStreamBuilder_setFramesPerDataCallback(builder, CALLBACK_SIZE_FRAMES);
169 //AAudioStreamBuilder_setBufferCapacityInFrames(builder, 48 * 8);
Eric Laurent629afae2017-05-25 18:25:51 -0700170
171 // Open an AAudioStream using the Builder.
Phil Burk44795232017-06-30 16:27:38 -0700172 result = AAudioStreamBuilder_openStream(builder, &mStream);
Eric Laurent629afae2017-05-25 18:25:51 -0700173
Phil Burk44795232017-06-30 16:27:38 -0700174 AAudioStreamBuilder_delete(builder);
Eric Laurent629afae2017-05-25 18:25:51 -0700175 return result;
176 }
177
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;
Eric Laurent629afae2017-05-25 18:25:51 -0700183 }
184 return AAUDIO_OK;
185 }
186
187 // Write zero data to fill up the buffer and prevent underruns.
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.
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.
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
222 AAudioStream *getStream() const {
223 return mStream;
224 }
225
226private:
Eric Laurent629afae2017-05-25 18:25:51 -0700227 AAudioStream *mStream = nullptr;
228 aaudio_sharing_mode_t mRequestedSharingMode = SHARING_MODE;
229 aaudio_performance_mode_t mRequestedPerformanceMode = PERFORMANCE_MODE;
Phil Burk7a61a3a2017-07-10 11:53:09 -0700230
Eric Laurent629afae2017-05-25 18:25:51 -0700231};
232
233typedef struct SineThreadedData_s {
Phil Burk7a61a3a2017-07-10 11:53:09 -0700234
Eric Laurent629afae2017-05-25 18:25:51 -0700235 SineGenerator sineOsc1;
236 SineGenerator sineOsc2;
Phil Burk187dcd42017-08-29 09:04:35 -0700237 Timestamp timestamps[MAX_TIMESTAMPS];
Phil Burkfaeb8b22017-07-25 15:15:07 -0700238 int64_t framesTotal = 0;
239 int64_t nextFrameToGlitch = FORCED_UNDERRUN_PERIOD_FRAMES;
Phil Burkfcf9efd2017-07-14 08:25:08 -0700240 int32_t minNumFrames = INT32_MAX;
241 int32_t maxNumFrames = 0;
Phil Burk187dcd42017-08-29 09:04:35 -0700242 int32_t timestampCount = 0; // in timestamps
Phil Burk7a61a3a2017-07-10 11:53:09 -0700243
244 int scheduler = 0;
Phil Burkfaeb8b22017-07-25 15:15:07 -0700245 bool schedulerChecked = false;
246 bool forceUnderruns = false;
Phil Burk7a61a3a2017-07-10 11:53:09 -0700247
248 AAudioSimplePlayer simplePlayer;
249 int32_t callbackCount = 0;
250 WakeUp waker{AAUDIO_OK};
251
Eric Laurent629afae2017-05-25 18:25:51 -0700252} SineThreadedData_t;
253
254// Callback function that fills the audio output buffer.
255aaudio_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) {
Phil Burkfaeb8b22017-07-25 15:15:07 -0700264 printf("ERROR - SimplePlayerDataCallbackProc needs userData\n");
Eric Laurent629afae2017-05-25 18:25:51 -0700265 return AAUDIO_CALLBACK_RESULT_STOP;
266 }
267 SineThreadedData_t *sineData = (SineThreadedData_t *) userData;
Phil Burk7a61a3a2017-07-10 11:53:09 -0700268 sineData->callbackCount++;
Eric Laurent629afae2017-05-25 18:25:51 -0700269
Phil Burkfaeb8b22017-07-25 15:15:07 -0700270 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
Eric Laurent629afae2017-05-25 18:25:51 -0700280 if (!sineData->schedulerChecked) {
281 sineData->scheduler = sched_getscheduler(gettid());
282 sineData->schedulerChecked = true;
283 }
284
Phil Burk187dcd42017-08-29 09:04:35 -0700285 if (sineData->timestampCount < MAX_TIMESTAMPS) {
286 Timestamp *timestamp = &sineData->timestamps[sineData->timestampCount];
287 aaudio_result_t result = AAudioStream_getTimestamp(stream,
288 CLOCK_MONOTONIC, &timestamp->position, &timestamp->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
Phil Burkfcf9efd2017-07-14 08:25:08 -0700296 if (numFrames > sineData->maxNumFrames) {
297 sineData->maxNumFrames = numFrames;
298 }
299 if (numFrames < sineData->minNumFrames) {
300 sineData->minNumFrames = numFrames;
301 }
302
Eric Laurent629afae2017-05-25 18:25:51 -0700303 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
334void SimplePlayerErrorCallbackProc(
335 AAudioStream *stream __unused,
336 void *userData __unused,
Phil Burk7a61a3a2017-07-10 11:53:09 -0700337 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);
Eric Laurent629afae2017-05-25 18:25:51 -0700346}
347
Phil Burk7a61a3a2017-07-10 11:53:09 -0700348
Eric Laurent629afae2017-05-25 18:25:51 -0700349#endif //AAUDIO_SIMPLE_PLAYER_H