aaudio example: add dynamic workload test
Add options to write_sine_callback example.
-w set base workload
-W alternate between base and this workload
-Z set period in seconds for alternating workloads
-a set CPU affinity
Bug: 202441352
Bug: 202345376
Test: adb shell write_sine_callback -pl -n4 -w1.0 -W14.0 -a2 -Z3
Change-Id: Id25d651dc0ac9befff1fc1af1132753dd7482c7c
diff --git a/media/libaaudio/examples/utils/AAudioSimplePlayer.h b/media/libaaudio/examples/utils/AAudioSimplePlayer.h
index 7daac20..956b3cd 100644
--- a/media/libaaudio/examples/utils/AAudioSimplePlayer.h
+++ b/media/libaaudio/examples/utils/AAudioSimplePlayer.h
@@ -40,6 +40,31 @@
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 @@
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 @@
} 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 @@
}
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 @@
return AAUDIO_CALLBACK_RESULT_STOP;
}
+ s_burnCPU((int32_t)(sineData->workload * kWorkloadScaler * numFrames));
+
sineData->callbackCount++;
sineData->framesTotal += numFrames;
return AAUDIO_CALLBACK_RESULT_CONTINUE;
diff --git a/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp b/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp
index cdc987b..400fc7c 100644
--- a/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp
+++ b/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp
@@ -31,10 +31,10 @@
#include "AAudioSimplePlayer.h"
#include "AAudioArgsParser.h"
-#define APP_VERSION "0.1.8"
+#define APP_VERSION "0.2.1"
-constexpr int32_t kDefaultHangTimeMSec = 10;
-
+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 @@
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 @@
myData.schedulerChecked = false;
myData.callbackCount = 0;
myData.hangTimeMSec = hangTimeMSec; // test AAudioStream_getXRunCount()
+ myData.cpuAffinity = cpuAffinity;
result = player.open(argParser,
SimplePlayerDataCallbackProc,
@@ -111,8 +116,8 @@
}
// 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 @@
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 @@
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 @@
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 @@
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 @@
? 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 @@
}
}
+ 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;