• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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