Home | History | Annotate | Download | only in tests
      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