• 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 
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