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