Merge "Minor improvements to test app"
diff --git a/wilhelm/tests/sandbox/playbq.c b/wilhelm/tests/sandbox/playbq.c
index aaa72f1..c0f71a7 100644
--- a/wilhelm/tests/sandbox/playbq.c
+++ b/wilhelm/tests/sandbox/playbq.c
@@ -21,6 +21,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <time.h>
#include <unistd.h>
#include <SLES/OpenSLES.h>
@@ -30,6 +31,9 @@
#include <sndfile.h>
#endif
+#define max(a, b) ((a) > (b) ? (a) : (b))
+#define min(a, b) ((a) < (b) ? (a) : (b))
+
unsigned numBuffers = 2;
int framesPerBuffer = 512;
SNDFILE *sndfile;
@@ -114,14 +118,18 @@
byteOrder = nativeByteOrder;
SLboolean enableReverb = SL_BOOLEAN_FALSE;
- SLpermille rate = 1000;
+ SLpermille initialRate = 0;
+ SLpermille finalRate = 0;
+ SLpermille deltaRate = 1;
+ SLmillisecond deltaRateMs = 0;
// process command-line options
int i;
for (i = 1; i < argc; ++i) {
char *arg = argv[i];
- if (arg[0] != '-')
+ if (arg[0] != '-') {
break;
+ }
if (!strcmp(arg, "-b")) {
byteOrder = SL_BYTEORDER_BIGENDIAN;
} else if (!strcmp(arg, "-l")) {
@@ -133,7 +141,17 @@
} else if (!strncmp(arg, "-n", 2)) {
numBuffers = atoi(&arg[2]);
} else if (!strncmp(arg, "-p", 2)) {
- rate = atoi(&arg[2]);
+ initialRate = atoi(&arg[2]);
+ } else if (!strncmp(arg, "-P", 2)) {
+ finalRate = atoi(&arg[2]);
+ } else if (!strncmp(arg, "-q", 2)) {
+ deltaRate = atoi(&arg[2]);
+ // deltaRate is a magnitude, so take absolute value
+ if (deltaRate < 0) {
+ deltaRate = -deltaRate;
+ }
+ } else if (!strncmp(arg, "-Q", 2)) {
+ deltaRateMs = atoi(&arg[2]);
} else if (!strcmp(arg, "-r")) {
enableReverb = SL_BOOLEAN_TRUE;
} else {
@@ -143,6 +161,16 @@
if (argc - i != 1) {
fprintf(stderr, "usage: [-b/l] [-8] [-f#] [-n#] [-p#] [-r] %s filename\n", argv[0]);
+ fprintf(stderr, " -b force big-endian byte order (default is native byte order)\n");
+ fprintf(stderr, " -l force little-endian byte order (default is native byte order)\n");
+ fprintf(stderr, " -8 output 8-bits per sample (default is 16-bits per sample)\n");
+ fprintf(stderr, " -f# frames per buffer (default 512)\n");
+ fprintf(stderr, " -n# number of buffers (default 2)\n");
+ fprintf(stderr, " -p# initial playback rate in per mille (default 1000)\n");
+ fprintf(stderr, " -P# final playback rate in per mille (default same as -p#)\n");
+ fprintf(stderr, " -q# magnitude of playback rate changes in per mille (default 1)\n");
+ fprintf(stderr, " -Q# period between playback rate changes in ms (default 50)\n");
+ fprintf(stderr, " -r enable reverb (default disabled)\n");
return EXIT_FAILURE;
}
@@ -162,8 +190,9 @@
break;
default:
fprintf(stderr, "unsupported channel count %d\n", sfinfo.channels);
- break;
+ goto close_sndfile;
}
+
switch (sfinfo.samplerate) {
case 8000:
case 11025:
@@ -177,15 +206,17 @@
break;
default:
fprintf(stderr, "unsupported sample rate %d\n", sfinfo.samplerate);
- break;
+ goto close_sndfile;
}
+
switch (sfinfo.format & SF_FORMAT_TYPEMASK) {
case SF_FORMAT_WAV:
break;
default:
fprintf(stderr, "unsupported format type 0x%x\n", sfinfo.format & SF_FORMAT_TYPEMASK);
- break;
+ goto close_sndfile;
}
+
switch (sfinfo.format & SF_FORMAT_SUBMASK) {
case SF_FORMAT_PCM_16:
case SF_FORMAT_PCM_U8:
@@ -195,7 +226,7 @@
break;
default:
fprintf(stderr, "unsupported sub-format 0x%x\n", sfinfo.format & SF_FORMAT_SUBMASK);
- break;
+ goto close_sndfile;
}
buffers = (short *) malloc(framesPerBuffer * sfinfo.channels * sizeof(short) * numBuffers);
@@ -264,7 +295,10 @@
SLObjectItf playerObject;
result = (*engineEngine)->CreateAudioPlayer(engineEngine, &playerObject, &audioSrc,
&audioSnk, enableReverb ? 3 : 2, ids2, req2);
- assert(SL_RESULT_SUCCESS == result);
+ if (SL_RESULT_SUCCESS != result) {
+ fprintf(stderr, "can't create audio player\n");
+ goto no_player;
+ }
// realize the player
result = (*playerObject)->Realize(playerObject, SL_BOOLEAN_FALSE);
@@ -291,14 +325,29 @@
result = (*playerPlaybackRate)->GetProperties(playerPlaybackRate, &defaultProperties);
assert(SL_RESULT_SUCCESS == result);
printf("default playback rate %d per mille, properties 0x%x\n", defaultRate, defaultProperties);
- if (rate != defaultRate) {
- result = (*playerPlaybackRate)->SetRate(playerPlaybackRate, rate);
+ if (initialRate <= 0) {
+ initialRate = defaultRate;
+ }
+ if (finalRate <= 0) {
+ finalRate = initialRate;
+ }
+ SLpermille currentRate = defaultRate;
+ if (finalRate == initialRate) {
+ deltaRate = 0;
+ } else if (finalRate < initialRate) {
+ deltaRate = -deltaRate;
+ }
+ if (initialRate != defaultRate) {
+ result = (*playerPlaybackRate)->SetRate(playerPlaybackRate, initialRate);
if (SL_RESULT_FEATURE_UNSUPPORTED == result) {
- fprintf(stderr, "playback rate %d is unsupported\n", rate);
+ fprintf(stderr, "initial playback rate %d is unsupported\n", initialRate);
+ deltaRate = 0;
} else if (SL_RESULT_PARAMETER_INVALID == result) {
- fprintf(stderr, "playback rate %d is invalid", rate);
+ fprintf(stderr, "initial playback rate %d is invalid\n", initialRate);
+ deltaRate = 0;
} else {
assert(SL_RESULT_SUCCESS == result);
+ currentRate = initialRate;
}
}
@@ -336,8 +385,9 @@
result = (*playerBufferQueue)->Enqueue(playerBufferQueue, buffer, nbytes);
assert(SL_RESULT_SUCCESS == result);
}
- if (which >= numBuffers)
+ if (which >= numBuffers) {
which = 0;
+ }
// register a callback on the buffer queue
result = (*playerBufferQueue)->RegisterCallback(playerBufferQueue, callback, NULL);
@@ -347,6 +397,12 @@
result = (*playerPlay)->SetPlayState(playerPlay, SL_PLAYSTATE_PLAYING);
assert(SL_RESULT_SUCCESS == result);
+ // get the initial time
+ struct timespec prevTs;
+ clock_gettime(CLOCK_MONOTONIC, &prevTs);
+ long elapsedNs = 0;
+ long deltaRateNs = deltaRateMs * 1000000;
+
// wait until the buffer queue is empty
SLBufferQueueState bufqstate;
for (;;) {
@@ -355,17 +411,54 @@
if (0 >= bufqstate.count) {
break;
}
- sleep(1);
+ if (deltaRate == 0) {
+ sleep(1);
+ } else {
+ struct timespec curTs;
+ clock_gettime(CLOCK_MONOTONIC, &curTs);
+ elapsedNs += (curTs.tv_sec - prevTs.tv_sec) * 1000000000 +
+ // this term can be negative
+ (curTs.tv_nsec - prevTs.tv_nsec);
+ prevTs = curTs;
+ if (elapsedNs < deltaRateNs) {
+ usleep((deltaRateNs - elapsedNs) / 1000);
+ continue;
+ }
+ elapsedNs -= deltaRateNs;
+ SLpermille nextRate = currentRate + deltaRate;
+ result = (*playerPlaybackRate)->SetRate(playerPlaybackRate, nextRate);
+ if (SL_RESULT_SUCCESS != result) {
+ fprintf(stderr, "next playback rate %d is unsupported\n", nextRate);
+ } else if (SL_RESULT_PARAMETER_INVALID == result) {
+ fprintf(stderr, "next playback rate %d is invalid\n", nextRate);
+ } else {
+ assert(SL_RESULT_SUCCESS == result);
+ }
+ currentRate = nextRate;
+ if (currentRate >= max(initialRate, finalRate)) {
+ currentRate = max(initialRate, finalRate);
+ deltaRate = -abs(deltaRate);
+ } else if (currentRate <= min(initialRate, finalRate)) {
+ currentRate = min(initialRate, finalRate);
+ deltaRate = abs(deltaRate);
+ }
+ }
}
// destroy audio player
(*playerObject)->Destroy(playerObject);
+no_player:
+
// destroy output mix
(*outputMixObject)->Destroy(outputMixObject);
// destroy engine
(*engineObject)->Destroy(engineObject);
+close_sndfile:
+
+ (void) sf_close(sndfile);
+
return EXIT_SUCCESS;
}