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
AAudioMonkeyErrorCallbackProc(AAudioStream * stream __unused,void * userData __unused,aaudio_result_t error)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.
nextRandomDouble()56 double nextRandomDouble() {
57 return drand48();
58 }
59
60 class AAudioMonkey : public AAudioSimplePlayer {
61 public:
62
AAudioMonkey(int index,AAudioArgsParser * argParser)63 AAudioMonkey(int index, AAudioArgsParser *argParser)
64 : mArgParser(argParser)
65 , mIndex(index) {}
66
open()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
isOpen()87 bool isOpen() {
88 return (getStream() != nullptr);
89
90 }
91 /**
92 *
93 * @return true if stream passes tests
94 */
validate()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
invoke()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
renderAudio(AAudioStream * stream,void * audioData,int32_t numFrames)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.
AAudioMonkeyDataCallback(AAudioStream * stream,void * userData,void * audioData,int32_t numFrames)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
usage()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
main(int argc,const char ** argv)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