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 // Try to trigger bugs by playing randomly on multiple streams. 18 19 #include <stdio.h> 20 #include <stdlib.h> 21 #include <vector> 22 23 #include <aaudio/AAudio.h> 24 #include "AAudioArgsParser.h" 25 #include "AAudioExampleUtils.h" 26 #include "AAudioSimplePlayer.h" 27 #include "SineGenerator.h" 28 29 #define DEFAULT_TIMEOUT_NANOS (1 * NANOS_PER_SECOND) 30 31 #define NUM_LOOPS 1000 32 #define MAX_MICROS_DELAY (2 * 1000 * 1000) 33 34 // TODO Consider adding an input stream. 35 #define PROB_START (0.20) 36 #define PROB_PAUSE (PROB_START + 0.10) 37 #define PROB_FLUSH (PROB_PAUSE + 0.10) 38 #define PROB_STOP (PROB_FLUSH + 0.10) 39 #define PROB_CLOSE (PROB_STOP + 0.10) 40 static_assert(PROB_CLOSE < 0.9, "Probability sum too high."); 41 42 aaudio_data_callback_result_t AAudioMonkeyDataCallback( 43 AAudioStream *stream, 44 void *userData, 45 void *audioData, 46 int32_t numFrames); 47 48 void AAudioMonkeyErrorCallbackProc( 49 AAudioStream *stream __unused, 50 void *userData __unused, 51 aaudio_result_t error) { 52 printf("Error Callback, error: %d\n",(int)error); 53 } 54 55 // This function is not thread safe. Only use this from a single thread. 56 double nextRandomDouble() { 57 return drand48(); 58 } 59 60 class AAudioMonkey : public AAudioSimplePlayer { 61 public: 62 63 AAudioMonkey(int index, AAudioArgsParser *argParser) 64 : mArgParser(argParser) 65 , mIndex(index) {} 66 67 aaudio_result_t open() { 68 printf("Monkey # %d ---------------------------------------------- OPEN\n", mIndex); 69 double offset = mIndex * 50; 70 mSine1.setup(440.0, 48000); 71 mSine1.setSweep(300.0 + offset, 600.0 + offset, 5.0); 72 mSine2.setup(660.0, 48000); 73 mSine2.setSweep(350.0 + offset, 900.0 + offset, 7.0); 74 75 aaudio_result_t result = AAudioSimplePlayer::open(*mArgParser, 76 AAudioMonkeyDataCallback, 77 AAudioMonkeyErrorCallbackProc, 78 this); 79 if (result != AAUDIO_OK) { 80 printf("ERROR - player.open() returned %d\n", result); 81 } 82 83 mArgParser->compareWithStream(getStream()); 84 return result; 85 } 86 87 bool isOpen() { 88 return (getStream() != nullptr); 89 90 } 91 /** 92 * 93 * @return true if stream passes tests 94 */ 95 bool validate() { 96 if (!isOpen()) return true; // closed is OK 97 98 // update and query stream state 99 aaudio_stream_state_t state = AAUDIO_STREAM_STATE_UNKNOWN; 100 aaudio_result_t result = AAudioStream_waitForStateChange(getStream(), 101 AAUDIO_STREAM_STATE_UNKNOWN, &state, 0); 102 if (result != AAUDIO_OK) { 103 printf("ERROR - AAudioStream_waitForStateChange returned %d\n", result); 104 return false; 105 } 106 107 int64_t framesRead = AAudioStream_getFramesRead(getStream()); 108 int64_t framesWritten = AAudioStream_getFramesWritten(getStream()); 109 int32_t xRuns = AAudioStream_getXRunCount(getStream()); 110 // Print status 111 printf("%30s, framesWritten = %8lld, framesRead = %8lld, xRuns = %d\n", 112 AAudio_convertStreamStateToText(state), 113 (unsigned long long) framesWritten, 114 (unsigned long long) framesRead, 115 xRuns); 116 117 if (framesWritten < framesRead) { 118 printf("WARNING - UNDERFLOW - diff = %d !!!!!!!!!!!!\n", 119 (int) (framesWritten - framesRead)); 120 } 121 return true; 122 } 123 124 aaudio_result_t invoke() { 125 aaudio_result_t result = AAUDIO_OK; 126 if (!isOpen()) { 127 result = open(); 128 if (result != AAUDIO_OK) return result; 129 } 130 131 if (!validate()) { 132 return -1; 133 } 134 135 double dice = nextRandomDouble(); 136 // Select an action based on a weighted probability. 137 if (dice < PROB_START) { 138 printf("start\n"); 139 result = AAudioStream_requestStart(getStream()); 140 } else if (dice < PROB_PAUSE) { 141 printf("pause\n"); 142 result = AAudioStream_requestPause(getStream()); 143 } else if (dice < PROB_FLUSH) { 144 printf("flush\n"); 145 result = AAudioStream_requestFlush(getStream()); 146 } else if (dice < PROB_STOP) { 147 printf("stop\n"); 148 result = AAudioStream_requestStop(getStream()); 149 } else if (dice < PROB_CLOSE) { 150 printf("close\n"); 151 result = close(); 152 } else { 153 printf("do nothing\n"); 154 } 155 156 if (result == AAUDIO_ERROR_INVALID_STATE) { 157 printf(" got AAUDIO_ERROR_INVALID_STATE - expected from a monkey\n"); 158 result = AAUDIO_OK; 159 } 160 if (result == AAUDIO_OK && isOpen()) { 161 if (!validate()) { 162 result = -1; 163 } 164 } 165 return result; 166 } 167 168 aaudio_data_callback_result_t renderAudio( 169 AAudioStream *stream, 170 void *audioData, 171 int32_t numFrames) { 172 173 int32_t samplesPerFrame = AAudioStream_getChannelCount(stream); 174 // This code only plays on the first one or two channels. 175 // TODO Support arbitrary number of channels. 176 switch (AAudioStream_getFormat(stream)) { 177 case AAUDIO_FORMAT_PCM_I16: { 178 int16_t *audioBuffer = (int16_t *) audioData; 179 // Render sine waves as shorts to first channel. 180 mSine1.render(&audioBuffer[0], samplesPerFrame, numFrames); 181 // Render sine waves to second channel if there is one. 182 if (samplesPerFrame > 1) { 183 mSine2.render(&audioBuffer[1], samplesPerFrame, numFrames); 184 } 185 } 186 break; 187 case AAUDIO_FORMAT_PCM_FLOAT: { 188 float *audioBuffer = (float *) audioData; 189 // Render sine waves as floats to first channel. 190 mSine1.render(&audioBuffer[0], samplesPerFrame, numFrames); 191 // Render sine waves to second channel if there is one. 192 if (samplesPerFrame > 1) { 193 mSine2.render(&audioBuffer[1], samplesPerFrame, numFrames); 194 } 195 } 196 break; 197 default: 198 return AAUDIO_CALLBACK_RESULT_STOP; 199 } 200 return AAUDIO_CALLBACK_RESULT_CONTINUE; 201 } 202 203 private: 204 const AAudioArgsParser *mArgParser; 205 const int mIndex; 206 SineGenerator mSine1; 207 SineGenerator mSine2; 208 }; 209 210 // Callback function that fills the audio output buffer. 211 aaudio_data_callback_result_t AAudioMonkeyDataCallback( 212 AAudioStream *stream, 213 void *userData, 214 void *audioData, 215 int32_t numFrames 216 ) { 217 // should not happen but just in case... 218 if (userData == nullptr) { 219 printf("ERROR - AAudioMonkeyDataCallback needs userData\n"); 220 return AAUDIO_CALLBACK_RESULT_STOP; 221 } 222 AAudioMonkey *monkey = (AAudioMonkey *) userData; 223 return monkey->renderAudio(stream, audioData, numFrames); 224 } 225 226 227 static void usage() { 228 AAudioArgsParser::usage(); 229 printf(" -i{seed} Initial random seed\n"); 230 printf(" -t{count} number of monkeys in the Troop\n"); 231 } 232 233 int main(int argc, const char **argv) { 234 AAudioArgsParser argParser; 235 std::vector<AAudioMonkey> monkeys; 236 aaudio_result_t result; 237 int numMonkeys = 1; 238 239 // Make printf print immediately so that debug info is not stuck 240 // in a buffer if we hang or crash. 241 setvbuf(stdout, nullptr, _IONBF, (size_t) 0); 242 243 printf("%s - Monkeys\n", argv[0]); 244 245 long int seed = (long int)getNanoseconds(); // different every time by default 246 247 for (int i = 1; i < argc; i++) { 248 const char *arg = argv[i]; 249 if (argParser.parseArg(arg)) { 250 // Handle options that are not handled by the ArgParser 251 if (arg[0] == '-') { 252 char option = arg[1]; 253 switch (option) { 254 case 'i': 255 seed = atol(&arg[2]); 256 break; 257 case 't': 258 numMonkeys = atoi(&arg[2]); 259 break; 260 default: 261 usage(); 262 exit(EXIT_FAILURE); 263 break; 264 } 265 } else { 266 usage(); 267 exit(EXIT_FAILURE); 268 break; 269 } 270 } 271 } 272 273 srand48(seed); 274 printf("seed = %ld, nextRandomDouble() = %f\n", seed, nextRandomDouble()); 275 276 for (int m = 0; m < numMonkeys; m++) { 277 monkeys.emplace_back(m, &argParser); 278 } 279 280 for (int i = 0; i < NUM_LOOPS; i++) { 281 // pick a random monkey and invoke it 282 double dice = nextRandomDouble(); 283 int monkeyIndex = floor(dice * numMonkeys); 284 printf("----------- Monkey #%d\n", monkeyIndex); 285 result = monkeys[monkeyIndex].invoke(); 286 if (result != AAUDIO_OK) { 287 goto error; 288 } 289 290 // sleep some random time 291 dice = nextRandomDouble(); 292 dice = dice * dice * dice; // skew towards smaller delays 293 int micros = (int) (dice * MAX_MICROS_DELAY); 294 usleep(micros); 295 296 // TODO consider making this multi-threaded, one thread per monkey, to catch more bugs 297 } 298 299 printf("PASS\n"); 300 return EXIT_SUCCESS; 301 302 error: 303 printf("FAIL - AAudio result = %d = %s\n", result, AAudio_convertResultToText(result)); 304 usleep(1000 * 1000); // give me time to stop the logcat 305 return EXIT_FAILURE; 306 } 307 308