blob: d2e7f23277d173675fd41a47911e29fe59ce1b03 [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
Eric Laurent629afae2017-05-25 18:25:51 -070039/**
40 * Simple wrapper for AAudio that opens an output stream either in callback or blocking write mode.
41 */
42class AAudioSimplePlayer {
43public:
44 AAudioSimplePlayer() {}
45 ~AAudioSimplePlayer() {
46 close();
47 };
48
49 /**
50 * Call this before calling open().
51 * @param requestedSharingMode
52 */
53 void setSharingMode(aaudio_sharing_mode_t requestedSharingMode) {
54 mRequestedSharingMode = requestedSharingMode;
55 }
56
57 /**
58 * Call this before calling open().
59 * @param requestedPerformanceMode
60 */
61 void setPerformanceMode(aaudio_performance_mode_t requestedPerformanceMode) {
62 mRequestedPerformanceMode = requestedPerformanceMode;
63 }
64
Phil Burk44795232017-06-30 16:27:38 -070065 // TODO Extract a common base class for record and playback.
Eric Laurent629afae2017-05-25 18:25:51 -070066 /**
67 * Also known as "sample rate"
68 * Only call this after open() has been called.
69 */
Phil Burk44795232017-06-30 16:27:38 -070070 int32_t getFramesPerSecond() const {
71 return getSampleRate(); // alias
72 }
73
74 /**
75 * Only call this after open() has been called.
76 */
77 int32_t getSampleRate() const {
Eric Laurent629afae2017-05-25 18:25:51 -070078 if (mStream == nullptr) {
79 return AAUDIO_ERROR_INVALID_STATE;
80 }
Phil Burk44795232017-06-30 16:27:38 -070081 return AAudioStream_getSampleRate(mStream);
Eric Laurent629afae2017-05-25 18:25:51 -070082 }
83
84 /**
85 * Only call this after open() has been called.
86 */
87 int32_t getChannelCount() {
88 if (mStream == nullptr) {
89 return AAUDIO_ERROR_INVALID_STATE;
90 }
Phil Burk44795232017-06-30 16:27:38 -070091 return AAudioStream_getChannelCount(mStream);
Eric Laurent629afae2017-05-25 18:25:51 -070092 }
93
94 /**
95 * Open a stream
96 */
Phil Burk44795232017-06-30 16:27:38 -070097 aaudio_result_t open(const AAudioParameters &parameters,
98 AAudioStream_dataCallback dataCallback = nullptr,
99 AAudioStream_errorCallback errorCallback = nullptr,
100 void *userContext = nullptr) {
101 aaudio_result_t result = AAUDIO_OK;
102
103 // Use an AAudioStreamBuilder to contain requested parameters.
104 AAudioStreamBuilder *builder = nullptr;
105 result = AAudio_createStreamBuilder(&builder);
106 if (result != AAUDIO_OK) return result;
107
108 parameters.applyParameters(builder); // apply args
109
110 AAudioStreamBuilder_setDirection(builder, AAUDIO_DIRECTION_OUTPUT);
111
112 if (dataCallback != nullptr) {
113 AAudioStreamBuilder_setDataCallback(builder, dataCallback, userContext);
114 }
115 if (errorCallback != nullptr) {
116 AAudioStreamBuilder_setErrorCallback(builder, errorCallback, userContext);
117 }
118 //AAudioStreamBuilder_setFramesPerDataCallback(builder, CALLBACK_SIZE_FRAMES);
119 //AAudioStreamBuilder_setBufferCapacityInFrames(builder, 48 * 8);
120
121 // Open an AAudioStream using the Builder.
122 result = AAudioStreamBuilder_openStream(builder, &mStream);
123
124 if (result == AAUDIO_OK) {
125 int32_t sizeInBursts = parameters.getNumberOfBursts();
126 if (sizeInBursts > 0) {
127 int32_t framesPerBurst = AAudioStream_getFramesPerBurst(mStream);
128 AAudioStream_setBufferSizeInFrames(mStream, sizeInBursts * framesPerBurst);
129 }
130 }
131
132 AAudioStreamBuilder_delete(builder);
133 return result;
134 }
135
Eric Laurent629afae2017-05-25 18:25:51 -0700136 aaudio_result_t open(int channelCount, int sampSampleRate, aaudio_format_t format,
Phil Burk44795232017-06-30 16:27:38 -0700137 AAudioStream_dataCallback dataProc,
138 AAudioStream_errorCallback errorProc,
Eric Laurent629afae2017-05-25 18:25:51 -0700139 void *userContext) {
140 aaudio_result_t result = AAUDIO_OK;
141
142 // Use an AAudioStreamBuilder to contain requested parameters.
Phil Burk44795232017-06-30 16:27:38 -0700143 AAudioStreamBuilder *builder = nullptr;
144 result = AAudio_createStreamBuilder(&builder);
Eric Laurent629afae2017-05-25 18:25:51 -0700145 if (result != AAUDIO_OK) return result;
146
Phil Burk44795232017-06-30 16:27:38 -0700147 AAudioStreamBuilder_setDirection(builder, AAUDIO_DIRECTION_OUTPUT);
148 AAudioStreamBuilder_setPerformanceMode(builder, mRequestedPerformanceMode);
149 AAudioStreamBuilder_setSharingMode(builder, mRequestedSharingMode);
150
151 AAudioStreamBuilder_setChannelCount(builder, channelCount);
152 AAudioStreamBuilder_setSampleRate(builder, sampSampleRate);
153 AAudioStreamBuilder_setFormat(builder, format);
154
Eric Laurent629afae2017-05-25 18:25:51 -0700155 if (dataProc != nullptr) {
Phil Burk44795232017-06-30 16:27:38 -0700156 AAudioStreamBuilder_setDataCallback(builder, dataProc, userContext);
Eric Laurent629afae2017-05-25 18:25:51 -0700157 }
158 if (errorProc != nullptr) {
Phil Burk44795232017-06-30 16:27:38 -0700159 AAudioStreamBuilder_setErrorCallback(builder, errorProc, userContext);
Eric Laurent629afae2017-05-25 18:25:51 -0700160 }
Phil Burk44795232017-06-30 16:27:38 -0700161 //AAudioStreamBuilder_setFramesPerDataCallback(builder, CALLBACK_SIZE_FRAMES);
162 //AAudioStreamBuilder_setBufferCapacityInFrames(builder, 48 * 8);
Eric Laurent629afae2017-05-25 18:25:51 -0700163
164 // Open an AAudioStream using the Builder.
Phil Burk44795232017-06-30 16:27:38 -0700165 result = AAudioStreamBuilder_openStream(builder, &mStream);
Eric Laurent629afae2017-05-25 18:25:51 -0700166
Phil Burk44795232017-06-30 16:27:38 -0700167 AAudioStreamBuilder_delete(builder);
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",
208 result, AAudio_convertResultToText(result));
209 }
210 int32_t xRunCount = AAudioStream_getXRunCount(mStream);
211 printf("AAudioStream_getXRunCount %d\n", xRunCount);
212 return result;
213 }
214
215 AAudioStream *getStream() const {
216 return mStream;
217 }
218
219private:
Eric Laurent629afae2017-05-25 18:25:51 -0700220 AAudioStream *mStream = nullptr;
221 aaudio_sharing_mode_t mRequestedSharingMode = SHARING_MODE;
222 aaudio_performance_mode_t mRequestedPerformanceMode = PERFORMANCE_MODE;
Phil Burk7a61a3a2017-07-10 11:53:09 -0700223
Eric Laurent629afae2017-05-25 18:25:51 -0700224};
225
226typedef struct SineThreadedData_s {
Phil Burk7a61a3a2017-07-10 11:53:09 -0700227
Eric Laurent629afae2017-05-25 18:25:51 -0700228 SineGenerator sineOsc1;
229 SineGenerator sineOsc2;
Phil Burkfaeb8b22017-07-25 15:15:07 -0700230 int64_t framesTotal = 0;
231 int64_t nextFrameToGlitch = FORCED_UNDERRUN_PERIOD_FRAMES;
Phil Burkfcf9efd2017-07-14 08:25:08 -0700232 int32_t minNumFrames = INT32_MAX;
233 int32_t maxNumFrames = 0;
Phil Burk7a61a3a2017-07-10 11:53:09 -0700234
235 int scheduler = 0;
Phil Burkfaeb8b22017-07-25 15:15:07 -0700236 bool schedulerChecked = false;
237 bool forceUnderruns = false;
Phil Burk7a61a3a2017-07-10 11:53:09 -0700238
239 AAudioSimplePlayer simplePlayer;
240 int32_t callbackCount = 0;
241 WakeUp waker{AAUDIO_OK};
242
Eric Laurent629afae2017-05-25 18:25:51 -0700243} SineThreadedData_t;
244
245// Callback function that fills the audio output buffer.
246aaudio_data_callback_result_t SimplePlayerDataCallbackProc(
247 AAudioStream *stream,
248 void *userData,
249 void *audioData,
250 int32_t numFrames
251 ) {
252
253 // should not happen but just in case...
254 if (userData == nullptr) {
Phil Burkfaeb8b22017-07-25 15:15:07 -0700255 printf("ERROR - SimplePlayerDataCallbackProc needs userData\n");
Eric Laurent629afae2017-05-25 18:25:51 -0700256 return AAUDIO_CALLBACK_RESULT_STOP;
257 }
258 SineThreadedData_t *sineData = (SineThreadedData_t *) userData;
Phil Burk7a61a3a2017-07-10 11:53:09 -0700259 sineData->callbackCount++;
Eric Laurent629afae2017-05-25 18:25:51 -0700260
Phil Burkfaeb8b22017-07-25 15:15:07 -0700261 sineData->framesTotal += numFrames;
262
263 if (sineData->forceUnderruns) {
264 if (sineData->framesTotal > sineData->nextFrameToGlitch) {
265 usleep(FORCED_UNDERRUN_SLEEP_MICROS);
266 printf("Simulate glitch at %lld\n", (long long) sineData->framesTotal);
267 sineData->nextFrameToGlitch += FORCED_UNDERRUN_PERIOD_FRAMES;
268 }
269 }
270
Eric Laurent629afae2017-05-25 18:25:51 -0700271 if (!sineData->schedulerChecked) {
272 sineData->scheduler = sched_getscheduler(gettid());
273 sineData->schedulerChecked = true;
274 }
275
Phil Burkfcf9efd2017-07-14 08:25:08 -0700276 if (numFrames > sineData->maxNumFrames) {
277 sineData->maxNumFrames = numFrames;
278 }
279 if (numFrames < sineData->minNumFrames) {
280 sineData->minNumFrames = numFrames;
281 }
282
Eric Laurent629afae2017-05-25 18:25:51 -0700283 int32_t samplesPerFrame = AAudioStream_getChannelCount(stream);
284 // This code only plays on the first one or two channels.
285 // TODO Support arbitrary number of channels.
286 switch (AAudioStream_getFormat(stream)) {
287 case AAUDIO_FORMAT_PCM_I16: {
288 int16_t *audioBuffer = (int16_t *) audioData;
289 // Render sine waves as shorts to first channel.
290 sineData->sineOsc1.render(&audioBuffer[0], samplesPerFrame, numFrames);
291 // Render sine waves to second channel if there is one.
292 if (samplesPerFrame > 1) {
293 sineData->sineOsc2.render(&audioBuffer[1], samplesPerFrame, numFrames);
294 }
295 }
296 break;
297 case AAUDIO_FORMAT_PCM_FLOAT: {
298 float *audioBuffer = (float *) audioData;
299 // Render sine waves as floats to first channel.
300 sineData->sineOsc1.render(&audioBuffer[0], samplesPerFrame, numFrames);
301 // Render sine waves to second channel if there is one.
302 if (samplesPerFrame > 1) {
303 sineData->sineOsc2.render(&audioBuffer[1], samplesPerFrame, numFrames);
304 }
305 }
306 break;
307 default:
308 return AAUDIO_CALLBACK_RESULT_STOP;
309 }
310
311 return AAUDIO_CALLBACK_RESULT_CONTINUE;
312}
313
314void SimplePlayerErrorCallbackProc(
315 AAudioStream *stream __unused,
316 void *userData __unused,
Phil Burk7a61a3a2017-07-10 11:53:09 -0700317 aaudio_result_t error) {
318 // should not happen but just in case...
319 if (userData == nullptr) {
320 printf("ERROR - MyPlayerErrorCallbackProc needs userData\n");
321 return;
322 }
323 SineThreadedData_t *sineData = (SineThreadedData_t *) userData;
324 android::status_t ret = sineData->waker.wake(error);
325 printf("Error Callback, error: %d, futex wake returns %d\n", error, ret);
Eric Laurent629afae2017-05-25 18:25:51 -0700326}
327
Phil Burk7a61a3a2017-07-10 11:53:09 -0700328
Eric Laurent629afae2017-05-25 18:25:51 -0700329#endif //AAUDIO_SIMPLE_PLAYER_H