blob: 4557f0234b373eed0c19d92b1bc00aac8b371d99 [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
33/**
34 * Simple wrapper for AAudio that opens an output stream either in callback or blocking write mode.
35 */
36class AAudioSimplePlayer {
37public:
38 AAudioSimplePlayer() {}
39 ~AAudioSimplePlayer() {
40 close();
41 };
42
43 /**
44 * Call this before calling open().
45 * @param requestedSharingMode
46 */
47 void setSharingMode(aaudio_sharing_mode_t requestedSharingMode) {
48 mRequestedSharingMode = requestedSharingMode;
49 }
50
51 /**
52 * Call this before calling open().
53 * @param requestedPerformanceMode
54 */
55 void setPerformanceMode(aaudio_performance_mode_t requestedPerformanceMode) {
56 mRequestedPerformanceMode = requestedPerformanceMode;
57 }
58
Phil Burk44795232017-06-30 16:27:38 -070059 // TODO Extract a common base class for record and playback.
Eric Laurent629afae2017-05-25 18:25:51 -070060 /**
61 * Also known as "sample rate"
62 * Only call this after open() has been called.
63 */
Phil Burk44795232017-06-30 16:27:38 -070064 int32_t getFramesPerSecond() const {
65 return getSampleRate(); // alias
66 }
67
68 /**
69 * Only call this after open() has been called.
70 */
71 int32_t getSampleRate() const {
Eric Laurent629afae2017-05-25 18:25:51 -070072 if (mStream == nullptr) {
73 return AAUDIO_ERROR_INVALID_STATE;
74 }
Phil Burk44795232017-06-30 16:27:38 -070075 return AAudioStream_getSampleRate(mStream);
Eric Laurent629afae2017-05-25 18:25:51 -070076 }
77
78 /**
79 * Only call this after open() has been called.
80 */
81 int32_t getChannelCount() {
82 if (mStream == nullptr) {
83 return AAUDIO_ERROR_INVALID_STATE;
84 }
Phil Burk44795232017-06-30 16:27:38 -070085 return AAudioStream_getChannelCount(mStream);
Eric Laurent629afae2017-05-25 18:25:51 -070086 }
87
88 /**
89 * Open a stream
90 */
Phil Burk44795232017-06-30 16:27:38 -070091 aaudio_result_t open(const AAudioParameters &parameters,
92 AAudioStream_dataCallback dataCallback = nullptr,
93 AAudioStream_errorCallback errorCallback = nullptr,
94 void *userContext = nullptr) {
95 aaudio_result_t result = AAUDIO_OK;
96
97 // Use an AAudioStreamBuilder to contain requested parameters.
98 AAudioStreamBuilder *builder = nullptr;
99 result = AAudio_createStreamBuilder(&builder);
100 if (result != AAUDIO_OK) return result;
101
102 parameters.applyParameters(builder); // apply args
103
104 AAudioStreamBuilder_setDirection(builder, AAUDIO_DIRECTION_OUTPUT);
105
106 if (dataCallback != nullptr) {
107 AAudioStreamBuilder_setDataCallback(builder, dataCallback, userContext);
108 }
109 if (errorCallback != nullptr) {
110 AAudioStreamBuilder_setErrorCallback(builder, errorCallback, userContext);
111 }
112 //AAudioStreamBuilder_setFramesPerDataCallback(builder, CALLBACK_SIZE_FRAMES);
113 //AAudioStreamBuilder_setBufferCapacityInFrames(builder, 48 * 8);
114
115 // Open an AAudioStream using the Builder.
116 result = AAudioStreamBuilder_openStream(builder, &mStream);
117
118 if (result == AAUDIO_OK) {
119 int32_t sizeInBursts = parameters.getNumberOfBursts();
120 if (sizeInBursts > 0) {
121 int32_t framesPerBurst = AAudioStream_getFramesPerBurst(mStream);
122 AAudioStream_setBufferSizeInFrames(mStream, sizeInBursts * framesPerBurst);
123 }
124 }
125
126 AAudioStreamBuilder_delete(builder);
127 return result;
128 }
129
Eric Laurent629afae2017-05-25 18:25:51 -0700130 aaudio_result_t open(int channelCount, int sampSampleRate, aaudio_format_t format,
Phil Burk44795232017-06-30 16:27:38 -0700131 AAudioStream_dataCallback dataProc,
132 AAudioStream_errorCallback errorProc,
Eric Laurent629afae2017-05-25 18:25:51 -0700133 void *userContext) {
134 aaudio_result_t result = AAUDIO_OK;
135
136 // Use an AAudioStreamBuilder to contain requested parameters.
Phil Burk44795232017-06-30 16:27:38 -0700137 AAudioStreamBuilder *builder = nullptr;
138 result = AAudio_createStreamBuilder(&builder);
Eric Laurent629afae2017-05-25 18:25:51 -0700139 if (result != AAUDIO_OK) return result;
140
Phil Burk44795232017-06-30 16:27:38 -0700141 AAudioStreamBuilder_setDirection(builder, AAUDIO_DIRECTION_OUTPUT);
142 AAudioStreamBuilder_setPerformanceMode(builder, mRequestedPerformanceMode);
143 AAudioStreamBuilder_setSharingMode(builder, mRequestedSharingMode);
144
145 AAudioStreamBuilder_setChannelCount(builder, channelCount);
146 AAudioStreamBuilder_setSampleRate(builder, sampSampleRate);
147 AAudioStreamBuilder_setFormat(builder, format);
148
Eric Laurent629afae2017-05-25 18:25:51 -0700149 if (dataProc != nullptr) {
Phil Burk44795232017-06-30 16:27:38 -0700150 AAudioStreamBuilder_setDataCallback(builder, dataProc, userContext);
Eric Laurent629afae2017-05-25 18:25:51 -0700151 }
152 if (errorProc != nullptr) {
Phil Burk44795232017-06-30 16:27:38 -0700153 AAudioStreamBuilder_setErrorCallback(builder, errorProc, userContext);
Eric Laurent629afae2017-05-25 18:25:51 -0700154 }
Phil Burk44795232017-06-30 16:27:38 -0700155 //AAudioStreamBuilder_setFramesPerDataCallback(builder, CALLBACK_SIZE_FRAMES);
156 //AAudioStreamBuilder_setBufferCapacityInFrames(builder, 48 * 8);
Eric Laurent629afae2017-05-25 18:25:51 -0700157
158 // Open an AAudioStream using the Builder.
Phil Burk44795232017-06-30 16:27:38 -0700159 result = AAudioStreamBuilder_openStream(builder, &mStream);
Eric Laurent629afae2017-05-25 18:25:51 -0700160
Phil Burk44795232017-06-30 16:27:38 -0700161 AAudioStreamBuilder_delete(builder);
Eric Laurent629afae2017-05-25 18:25:51 -0700162 return result;
163 }
164
165 aaudio_result_t close() {
166 if (mStream != nullptr) {
167 printf("call AAudioStream_close(%p)\n", mStream); fflush(stdout);
168 AAudioStream_close(mStream);
169 mStream = nullptr;
Eric Laurent629afae2017-05-25 18:25:51 -0700170 }
171 return AAUDIO_OK;
172 }
173
174 // Write zero data to fill up the buffer and prevent underruns.
175 aaudio_result_t prime() {
176 int32_t samplesPerFrame = AAudioStream_getChannelCount(mStream);
177 const int numFrames = 32;
178 float zeros[numFrames * samplesPerFrame];
179 memset(zeros, 0, sizeof(zeros));
180 aaudio_result_t result = numFrames;
181 while (result == numFrames) {
182 result = AAudioStream_write(mStream, zeros, numFrames, 0);
183 }
184 return result;
185 }
186
187 // Start the stream. AAudio will start calling your callback function.
188 aaudio_result_t start() {
189 aaudio_result_t result = AAudioStream_requestStart(mStream);
190 if (result != AAUDIO_OK) {
191 printf("ERROR - AAudioStream_requestStart() returned %d %s\n",
192 result, AAudio_convertResultToText(result));
193 }
194 return result;
195 }
196
197 // Stop the stream. AAudio will stop calling your callback function.
198 aaudio_result_t stop() {
199 aaudio_result_t result = AAudioStream_requestStop(mStream);
200 if (result != AAUDIO_OK) {
201 printf("ERROR - AAudioStream_requestStop() returned %d %s\n",
202 result, AAudio_convertResultToText(result));
203 }
204 int32_t xRunCount = AAudioStream_getXRunCount(mStream);
205 printf("AAudioStream_getXRunCount %d\n", xRunCount);
206 return result;
207 }
208
209 AAudioStream *getStream() const {
210 return mStream;
211 }
212
213private:
Eric Laurent629afae2017-05-25 18:25:51 -0700214 AAudioStream *mStream = nullptr;
215 aaudio_sharing_mode_t mRequestedSharingMode = SHARING_MODE;
216 aaudio_performance_mode_t mRequestedPerformanceMode = PERFORMANCE_MODE;
217};
218
219typedef struct SineThreadedData_s {
220 SineGenerator sineOsc1;
221 SineGenerator sineOsc2;
Phil Burkfcf9efd2017-07-14 08:25:08 -0700222 int32_t minNumFrames = INT32_MAX;
223 int32_t maxNumFrames = 0;
Eric Laurent629afae2017-05-25 18:25:51 -0700224 int scheduler;
225 bool schedulerChecked;
226} SineThreadedData_t;
227
228// Callback function that fills the audio output buffer.
229aaudio_data_callback_result_t SimplePlayerDataCallbackProc(
230 AAudioStream *stream,
231 void *userData,
232 void *audioData,
233 int32_t numFrames
234 ) {
235
236 // should not happen but just in case...
237 if (userData == nullptr) {
238 fprintf(stderr, "ERROR - SimplePlayerDataCallbackProc needs userData\n");
239 return AAUDIO_CALLBACK_RESULT_STOP;
240 }
241 SineThreadedData_t *sineData = (SineThreadedData_t *) userData;
242
243 if (!sineData->schedulerChecked) {
244 sineData->scheduler = sched_getscheduler(gettid());
245 sineData->schedulerChecked = true;
246 }
247
Phil Burkfcf9efd2017-07-14 08:25:08 -0700248 if (numFrames > sineData->maxNumFrames) {
249 sineData->maxNumFrames = numFrames;
250 }
251 if (numFrames < sineData->minNumFrames) {
252 sineData->minNumFrames = numFrames;
253 }
254
Eric Laurent629afae2017-05-25 18:25:51 -0700255 int32_t samplesPerFrame = AAudioStream_getChannelCount(stream);
256 // This code only plays on the first one or two channels.
257 // TODO Support arbitrary number of channels.
258 switch (AAudioStream_getFormat(stream)) {
259 case AAUDIO_FORMAT_PCM_I16: {
260 int16_t *audioBuffer = (int16_t *) audioData;
261 // Render sine waves as shorts to first channel.
262 sineData->sineOsc1.render(&audioBuffer[0], samplesPerFrame, numFrames);
263 // Render sine waves to second channel if there is one.
264 if (samplesPerFrame > 1) {
265 sineData->sineOsc2.render(&audioBuffer[1], samplesPerFrame, numFrames);
266 }
267 }
268 break;
269 case AAUDIO_FORMAT_PCM_FLOAT: {
270 float *audioBuffer = (float *) audioData;
271 // Render sine waves as floats to first channel.
272 sineData->sineOsc1.render(&audioBuffer[0], samplesPerFrame, numFrames);
273 // Render sine waves to second channel if there is one.
274 if (samplesPerFrame > 1) {
275 sineData->sineOsc2.render(&audioBuffer[1], samplesPerFrame, numFrames);
276 }
277 }
278 break;
279 default:
280 return AAUDIO_CALLBACK_RESULT_STOP;
281 }
282
283 return AAUDIO_CALLBACK_RESULT_CONTINUE;
284}
285
286void SimplePlayerErrorCallbackProc(
287 AAudioStream *stream __unused,
288 void *userData __unused,
289 aaudio_result_t error)
290{
291 printf("Error Callback, error: %d\n",(int)error);
292}
293
294#endif //AAUDIO_SIMPLE_PLAYER_H