Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1305af78 authored by Phil Burk's avatar Phil Burk Committed by Android (Google) Code Review
Browse files

Merge "aaudio example: add dynamic workload test"

parents 3e34526e c7acc137
Loading
Loading
Loading
Loading
+41 −0
Original line number Diff line number Diff line
@@ -40,6 +40,31 @@ typedef struct Timestamp {
    int64_t nanoseconds;
} Timestamp;

static constexpr int32_t   kWorkloadScaler = 500;

// Linear congruential random number generator.
static uint32_t s_random16() {
    static uint32_t seed = 1234;
    seed = ((seed * 31421) + 6927) & 0x0FFFF;
    return seed;
}

/**
 * The random number generator is good for burning CPU because the compiler cannot
 * easily optimize away the computation.
 * @param workload number of times to execute the loop
 * @return a white noise value between -1.0 and +1.0
 */
static float s_burnCPU(int32_t workload) {
    uint32_t random = 0;
    for (int32_t i = 0; i < workload; i++) {
        for (int32_t j = 0; j < 10; j++) {
            random = random ^ s_random16();
        }
    }
    return (random - 32768) * (1.0 / 32768);
}

/**
 * Simple wrapper for AAudio that opens an output stream either in callback or blocking write mode.
 */
@@ -268,11 +293,13 @@ typedef struct SineThreadedData_s {
    int32_t            timestampCount = 0; // in timestamps
    int32_t            sampleRate = 48000;
    int32_t            prefixToneFrames = 0;
    double             workload = 0.0;
    bool               sweepSetup = false;

    int                scheduler = 0;
    bool               schedulerChecked = false;
    int32_t            hangTimeMSec = 0;
    int                cpuAffinity = -1;

    AAudioSimplePlayer simplePlayer;
    int32_t            callbackCount = 0;
@@ -304,6 +331,14 @@ typedef struct SineThreadedData_s {

} SineThreadedData_t;

int setCpuAffinity(int cpuIndex) {
cpu_set_t cpu_set;
    CPU_ZERO(&cpu_set);
    CPU_SET(cpuIndex, &cpu_set);
    int err = sched_setaffinity((pid_t) 0, sizeof(cpu_set_t), &cpu_set);
    return err == 0 ? 0 : -errno;
}

// Callback function that fills the audio output buffer.
aaudio_data_callback_result_t SimplePlayerDataCallbackProc(
        AAudioStream *stream,
@@ -319,6 +354,10 @@ aaudio_data_callback_result_t SimplePlayerDataCallbackProc(
    }
    SineThreadedData_t *sineData = (SineThreadedData_t *) userData;

    if (sineData->cpuAffinity >= 0) {
        setCpuAffinity(sineData->cpuAffinity);
        sineData->cpuAffinity = -1;
    }
    // Play an initial high tone so we can tell whether the beginning was truncated.
    if (!sineData->sweepSetup && sineData->framesTotal >= sineData->prefixToneFrames) {
        sineData->setupSineSweeps();
@@ -398,6 +437,8 @@ aaudio_data_callback_result_t SimplePlayerDataCallbackProc(
            return AAUDIO_CALLBACK_RESULT_STOP;
    }

    s_burnCPU((int32_t)(sineData->workload * kWorkloadScaler * numFrames));

    sineData->callbackCount++;
    sineData->framesTotal += numFrames;
    return AAUDIO_CALLBACK_RESULT_CONTINUE;
+50 −8
Original line number Diff line number Diff line
@@ -31,10 +31,10 @@
#include "AAudioSimplePlayer.h"
#include "AAudioArgsParser.h"

#define APP_VERSION  "0.1.8"

constexpr int32_t kDefaultHangTimeMSec = 10;
#define APP_VERSION  "0.2.1"

static constexpr int32_t kDefaultHangTimeMSec = 10;
static constexpr int32_t kWorkPeriodSeconds = 6;
/**
 * Open stream, play some sine waves, then close the stream.
 *
@@ -44,7 +44,11 @@ constexpr int32_t kDefaultHangTimeMSec = 10;
static aaudio_result_t testOpenPlayClose(AAudioArgsParser &argParser,
                                         int32_t loopCount,
                                         int32_t prefixToneMsec,
                                         int32_t hangTimeMSec)
                                         int32_t hangTimeMSec,
                                         int     cpuAffinity,
                                         double  lowWorkLoad,
                                         double  highWorkLoad,
                                         int32_t workPeriodSeconds)
{
    SineThreadedData_t myData;
    AAudioSimplePlayer &player = myData.simplePlayer;
@@ -57,6 +61,7 @@ static aaudio_result_t testOpenPlayClose(AAudioArgsParser &argParser,
    myData.schedulerChecked = false;
    myData.callbackCount = 0;
    myData.hangTimeMSec = hangTimeMSec; // test AAudioStream_getXRunCount()
    myData.cpuAffinity = cpuAffinity;

    result = player.open(argParser,
                         SimplePlayerDataCallbackProc,
@@ -111,8 +116,8 @@ static aaudio_result_t testOpenPlayClose(AAudioArgsParser &argParser,
        }

        // Play a sine wave in the background.
        printf("Sleep for %d seconds while audio plays in a callback thread. %d of %d\n",
               argParser.getDurationSeconds(), (loopIndex + 1), loopCount);
        printf("Monitor for %d seconds while audio plays in a callback thread. %d of %d, %d\n",
               argParser.getDurationSeconds(), (loopIndex + 1), loopCount, workPeriodSeconds);
        startedAtNanos = getNanoseconds(CLOCK_MONOTONIC);
        for (int second = 0; second < durationSeconds; second++) {
            // Sleep a while. Wake up early if there is an error, for example a DISCONNECT.
@@ -123,13 +128,17 @@ static aaudio_result_t testOpenPlayClose(AAudioArgsParser &argParser,
            const int32_t framesWritten = (int32_t) AAudioStream_getFramesWritten(player.getStream());
            const int32_t framesRead = (int32_t) AAudioStream_getFramesRead(player.getStream());
            const int32_t xruns = AAudioStream_getXRunCount(player.getStream());
            myData.workload = ((second % (2 * workPeriodSeconds)) < workPeriodSeconds)
                    ? lowWorkLoad : highWorkLoad;
            printf(" waker result = %d, at %6d millis"
                           ", second = %3d, frames written %8d - read %8d = %8d, underruns = %d\n",
                   ", second = %3d, frames written %8d - read %8d = %8d"
                   ", work = %5.1f, underruns = %d\n",
                   result, (int) millis,
                   second,
                   framesWritten,
                   framesRead,
                   framesWritten - framesRead,
                   myData.workload,
                   xruns);
            if (result != AAUDIO_OK) {
                disconnected = (result == AAUDIO_ERROR_DISCONNECTED);
@@ -220,6 +229,11 @@ static void usage() {
    AAudioArgsParser::usage();
    printf("      -l{count} loopCount start/stop, every other one is silent\n");
    printf("      -t{msec}  play a high pitched tone at the beginning\n");
    printf("      -w{workload}  set base workload, default 0.0\n");
    printf("      -W{workload}  alternate between this higher workload and base workload\n");
    printf("      -Z{duration}  number of seconds to spend at each workload, default = %d\n",
           kWorkPeriodSeconds);
    printf("      -a{cpu}   set CPU affinity, default none\n");
    printf("      -h{msec}  force periodic underruns by hanging in callback\n");
    printf("                If no value specified then %d used.\n",
            kDefaultHangTimeMSec);
@@ -232,6 +246,10 @@ int main(int argc, const char **argv)
    int32_t            loopCount = 1;
    int32_t            prefixToneMsec = 0;
    int32_t            hangTimeMSec = 0;
    int                cpuAffinity = -1;
    double             lowWorkLoad = 0.0;
    double             highWorkLoad = -1.0;
    int32_t            workPeriodSeconds = kWorkPeriodSeconds;

    // Make printf print immediately so that debug info is not stuck
    // in a buffer if we hang or crash.
@@ -247,6 +265,9 @@ int main(int argc, const char **argv)
            if (arg[0] == '-') {
                char option = arg[1];
                switch (option) {
                    case 'a':
                        cpuAffinity = atoi(&arg[2]);
                        break;
                    case 'l':
                        loopCount = atoi(&arg[2]);
                        break;
@@ -258,6 +279,15 @@ int main(int argc, const char **argv)
                                ? atoi(&arg[2])
                                : kDefaultHangTimeMSec;
                        break;
                    case 'w':
                        lowWorkLoad = atof(&arg[2]);
                        break;
                    case 'W':
                        highWorkLoad = atof(&arg[2]);
                        break;
                    case 'Z':
                        workPeriodSeconds = atoi(&arg[2]);
                        break;
                    default:
                        usage();
                        exit(EXIT_FAILURE);
@@ -271,9 +301,21 @@ int main(int argc, const char **argv)
        }
    }

    if (highWorkLoad > 0) {
        if (highWorkLoad < lowWorkLoad) {
            printf("ERROR - -W%f workload lower than -w%f workload", highWorkLoad, lowWorkLoad);
            return EXIT_FAILURE;
        }
    } else {
        highWorkLoad = lowWorkLoad; // high not specified so use low
    }

    // Keep looping until we can complete the test without disconnecting.
    while((result = testOpenPlayClose(argParser, loopCount,
            prefixToneMsec, hangTimeMSec))
            prefixToneMsec, hangTimeMSec,
            cpuAffinity,
            lowWorkLoad, highWorkLoad,
            workPeriodSeconds))
            == AAUDIO_ERROR_DISCONNECTED);

    return (result) ? EXIT_FAILURE : EXIT_SUCCESS;