1/* 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> 26#include <atomic> 27#include "AAudioArgsParser.h" 28#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 34// 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 39#define MAX_TIMESTAMPS 16 40 41typedef struct Timestamp { 42 int64_t position; 43 int64_t nanoseconds; 44} Timestamp; 45 46/** 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 72 // TODO Extract a common base class for record and playback. 73 /** 74 * Also known as "sample rate" 75 * Only call this after open() has been called. 76 */ 77 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 { 85 if (mStream == nullptr) { 86 return AAUDIO_ERROR_INVALID_STATE; 87 } 88 return AAudioStream_getSampleRate(mStream); 89 } 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 } 98 return AAudioStream_getChannelCount(mStream); 99 } 100 101 /** 102 * Open a stream 103 */ 104 aaudio_result_t open(const AAudioParameters ¶meters, 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 143 aaudio_result_t open(int channelCount, int sampSampleRate, aaudio_format_t format, 144 AAudioStream_dataCallback dataProc, 145 AAudioStream_errorCallback errorProc, 146 void *userContext) { 147 aaudio_result_t result = AAUDIO_OK; 148 149 // Use an AAudioStreamBuilder to contain requested parameters. 150 AAudioStreamBuilder *builder = nullptr; 151 result = AAudio_createStreamBuilder(&builder); 152 if (result != AAUDIO_OK) return result; 153 154 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 162 if (dataProc != nullptr) { 163 AAudioStreamBuilder_setDataCallback(builder, dataProc, userContext); 164 } 165 if (errorProc != nullptr) { 166 AAudioStreamBuilder_setErrorCallback(builder, errorProc, userContext); 167 } 168 //AAudioStreamBuilder_setFramesPerDataCallback(builder, CALLBACK_SIZE_FRAMES); 169 //AAudioStreamBuilder_setBufferCapacityInFrames(builder, 48 * 8); 170 171 // Open an AAudioStream using the Builder. 172 result = AAudioStreamBuilder_openStream(builder, &mStream); 173 174 AAudioStreamBuilder_delete(builder); 175 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; 183 } 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: 227 AAudioStream *mStream = nullptr; 228 aaudio_sharing_mode_t mRequestedSharingMode = SHARING_MODE; 229 aaudio_performance_mode_t mRequestedPerformanceMode = PERFORMANCE_MODE; 230 231}; 232 233typedef struct SineThreadedData_s { 234 235 SineGenerator sineOsc1; 236 SineGenerator sineOsc2; 237 Timestamp timestamps[MAX_TIMESTAMPS]; 238 int64_t framesTotal = 0; 239 int64_t nextFrameToGlitch = FORCED_UNDERRUN_PERIOD_FRAMES; 240 int32_t minNumFrames = INT32_MAX; 241 int32_t maxNumFrames = 0; 242 int32_t timestampCount = 0; // in timestamps 243 244 int scheduler = 0; 245 bool schedulerChecked = false; 246 bool forceUnderruns = false; 247 248 AAudioSimplePlayer simplePlayer; 249 int32_t callbackCount = 0; 250 WakeUp waker{AAUDIO_OK}; 251 252} 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) { 264 printf("ERROR - SimplePlayerDataCallbackProc needs userData\n"); 265 return AAUDIO_CALLBACK_RESULT_STOP; 266 } 267 SineThreadedData_t *sineData = (SineThreadedData_t *) userData; 268 sineData->callbackCount++; 269 270 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 280 if (!sineData->schedulerChecked) { 281 sineData->scheduler = sched_getscheduler(gettid()); 282 sineData->schedulerChecked = true; 283 } 284 285 if (sineData->timestampCount < MAX_TIMESTAMPS) { 286 Timestamp *timestamp = &sineData->timestamps[sineData->timestampCount]; 287 aaudio_result_t result = AAudioStream_getTimestamp(stream, 288 CLOCK_MONOTONIC, ×tamp->position, ×tamp->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 296 if (numFrames > sineData->maxNumFrames) { 297 sineData->maxNumFrames = numFrames; 298 } 299 if (numFrames < sineData->minNumFrames) { 300 sineData->minNumFrames = numFrames; 301 } 302 303 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, 337 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); 346} 347 348 349#endif //AAUDIO_SIMPLE_PLAYER_H 350