blob: cc0cb34195396f4ec62d6ee6145280049060d470 [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 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 Burkfaeb8b22017-07-25 15:15:07 -070033// Arbitrary period for glitches, once per second at 48000 Hz.
34#define FORCED_UNDERRUN_PERIOD_FRAMES 48000
35// How long to sleep in a callback to cause an intentional glitch. For testing.
36#define FORCED_UNDERRUN_SLEEP_MICROS (10 * 1000)
37
Eric Laurent629afae2017-05-25 18:25:51 -070038/**
39 * Simple wrapper for AAudio that opens an output stream either in callback or blocking write mode.
40 */
41class AAudioSimplePlayer {
42public:
43 AAudioSimplePlayer() {}
44 ~AAudioSimplePlayer() {
45 close();
46 };
47
48 /**
49 * Call this before calling open().
50 * @param requestedSharingMode
51 */
52 void setSharingMode(aaudio_sharing_mode_t requestedSharingMode) {
53 mRequestedSharingMode = requestedSharingMode;
54 }
55
56 /**
57 * Call this before calling open().
58 * @param requestedPerformanceMode
59 */
60 void setPerformanceMode(aaudio_performance_mode_t requestedPerformanceMode) {
61 mRequestedPerformanceMode = requestedPerformanceMode;
62 }
63
Phil Burk44795232017-06-30 16:27:38 -070064 // TODO Extract a common base class for record and playback.
Eric Laurent629afae2017-05-25 18:25:51 -070065 /**
66 * Also known as "sample rate"
67 * Only call this after open() has been called.
68 */
Phil Burk44795232017-06-30 16:27:38 -070069 int32_t getFramesPerSecond() const {
70 return getSampleRate(); // alias
71 }
72
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);
Eric Laurent629afae2017-05-25 18:25:51 -0700167 return result;
168 }
169
170 aaudio_result_t close() {
171 if (mStream != nullptr) {
172 printf("call AAudioStream_close(%p)\n", mStream); fflush(stdout);
173 AAudioStream_close(mStream);
174 mStream = nullptr;
Eric Laurent629afae2017-05-25 18:25:51 -0700175 }
176 return AAUDIO_OK;
177 }
178
179 // Write zero data to fill up the buffer and prevent underruns.
180 aaudio_result_t prime() {
181 int32_t samplesPerFrame = AAudioStream_getChannelCount(mStream);
182 const int numFrames = 32;
183 float zeros[numFrames * samplesPerFrame];
184 memset(zeros, 0, sizeof(zeros));
185 aaudio_result_t result = numFrames;
186 while (result == numFrames) {
187 result = AAudioStream_write(mStream, zeros, numFrames, 0);
188 }
189 return result;
190 }
191
192 // Start the stream. AAudio will start calling your callback function.
193 aaudio_result_t start() {
194 aaudio_result_t result = AAudioStream_requestStart(mStream);
195 if (result != AAUDIO_OK) {
196 printf("ERROR - AAudioStream_requestStart() returned %d %s\n",
197 result, AAudio_convertResultToText(result));
198 }
199 return result;
200 }
201
202 // Stop the stream. AAudio will stop calling your callback function.
203 aaudio_result_t stop() {
204 aaudio_result_t result = AAudioStream_requestStop(mStream);
205 if (result != AAUDIO_OK) {
206 printf("ERROR - AAudioStream_requestStop() returned %d %s\n",
207 result, AAudio_convertResultToText(result));
208 }
209 int32_t xRunCount = AAudioStream_getXRunCount(mStream);
210 printf("AAudioStream_getXRunCount %d\n", xRunCount);
211 return result;
212 }
213
214 AAudioStream *getStream() const {
215 return mStream;
216 }
217
218private:
Eric Laurent629afae2017-05-25 18:25:51 -0700219 AAudioStream *mStream = nullptr;
220 aaudio_sharing_mode_t mRequestedSharingMode = SHARING_MODE;
221 aaudio_performance_mode_t mRequestedPerformanceMode = PERFORMANCE_MODE;
222};
223
224typedef struct SineThreadedData_s {
225 SineGenerator sineOsc1;
226 SineGenerator sineOsc2;
Phil Burkfaeb8b22017-07-25 15:15:07 -0700227 int64_t framesTotal = 0;
228 int64_t nextFrameToGlitch = FORCED_UNDERRUN_PERIOD_FRAMES;
Phil Burkfcf9efd2017-07-14 08:25:08 -0700229 int32_t minNumFrames = INT32_MAX;
230 int32_t maxNumFrames = 0;
Eric Laurent629afae2017-05-25 18:25:51 -0700231 int scheduler;
Phil Burkfaeb8b22017-07-25 15:15:07 -0700232 bool schedulerChecked = false;
233 bool forceUnderruns = false;
Eric Laurent629afae2017-05-25 18:25:51 -0700234} SineThreadedData_t;
235
236// Callback function that fills the audio output buffer.
237aaudio_data_callback_result_t SimplePlayerDataCallbackProc(
238 AAudioStream *stream,
239 void *userData,
240 void *audioData,
241 int32_t numFrames
242 ) {
243
244 // should not happen but just in case...
245 if (userData == nullptr) {
Phil Burkfaeb8b22017-07-25 15:15:07 -0700246 printf("ERROR - SimplePlayerDataCallbackProc needs userData\n");
Eric Laurent629afae2017-05-25 18:25:51 -0700247 return AAUDIO_CALLBACK_RESULT_STOP;
248 }
249 SineThreadedData_t *sineData = (SineThreadedData_t *) userData;
250
Phil Burkfaeb8b22017-07-25 15:15:07 -0700251 sineData->framesTotal += numFrames;
252
253 if (sineData->forceUnderruns) {
254 if (sineData->framesTotal > sineData->nextFrameToGlitch) {
255 usleep(FORCED_UNDERRUN_SLEEP_MICROS);
256 printf("Simulate glitch at %lld\n", (long long) sineData->framesTotal);
257 sineData->nextFrameToGlitch += FORCED_UNDERRUN_PERIOD_FRAMES;
258 }
259 }
260
Eric Laurent629afae2017-05-25 18:25:51 -0700261 if (!sineData->schedulerChecked) {
262 sineData->scheduler = sched_getscheduler(gettid());
263 sineData->schedulerChecked = true;
264 }
265
Phil Burkfcf9efd2017-07-14 08:25:08 -0700266 if (numFrames > sineData->maxNumFrames) {
267 sineData->maxNumFrames = numFrames;
268 }
269 if (numFrames < sineData->minNumFrames) {
270 sineData->minNumFrames = numFrames;
271 }
272
Eric Laurent629afae2017-05-25 18:25:51 -0700273 int32_t samplesPerFrame = AAudioStream_getChannelCount(stream);
274 // This code only plays on the first one or two channels.
275 // TODO Support arbitrary number of channels.
276 switch (AAudioStream_getFormat(stream)) {
277 case AAUDIO_FORMAT_PCM_I16: {
278 int16_t *audioBuffer = (int16_t *) audioData;
279 // Render sine waves as shorts to first channel.
280 sineData->sineOsc1.render(&audioBuffer[0], samplesPerFrame, numFrames);
281 // Render sine waves to second channel if there is one.
282 if (samplesPerFrame > 1) {
283 sineData->sineOsc2.render(&audioBuffer[1], samplesPerFrame, numFrames);
284 }
285 }
286 break;
287 case AAUDIO_FORMAT_PCM_FLOAT: {
288 float *audioBuffer = (float *) audioData;
289 // Render sine waves as floats 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 default:
298 return AAUDIO_CALLBACK_RESULT_STOP;
299 }
300
301 return AAUDIO_CALLBACK_RESULT_CONTINUE;
302}
303
304void SimplePlayerErrorCallbackProc(
305 AAudioStream *stream __unused,
306 void *userData __unused,
307 aaudio_result_t error)
308{
309 printf("Error Callback, error: %d\n",(int)error);
310}
311
312#endif //AAUDIO_SIMPLE_PLAYER_H