Merge "aaudio examples: handle disconnect in write_sine_callback" into oc-mr1-dev

gugelfrei
TreeHugger Robot 7 years ago committed by Android (Google) Code Review
commit e185203e55

@ -17,9 +17,14 @@
#ifndef AAUDIO_EXAMPLE_UTILS_H
#define AAUDIO_EXAMPLE_UTILS_H
#include <unistd.h>
#include <atomic>
#include <linux/futex.h>
#include <sched.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <aaudio/AAudio.h>
#include <utils/Errors.h>
#define NANOS_PER_MICROSECOND ((int64_t)1000)
#define NANOS_PER_MILLISECOND (NANOS_PER_MICROSECOND * 1000)
@ -40,6 +45,12 @@ const char *getSharingModeText(aaudio_sharing_mode_t mode) {
return modeText;
}
static void convertNanosecondsToTimespec(int64_t nanoseconds, struct timespec *time) {
time->tv_sec = nanoseconds / NANOS_PER_SECOND;
// Calculate the fractional nanoseconds. Avoids expensive % operation.
time->tv_nsec = nanoseconds - (time->tv_sec * NANOS_PER_SECOND);
}
static int64_t getNanoseconds(clockid_t clockId = CLOCK_MONOTONIC) {
struct timespec time;
int result = clock_gettime(clockId, &time);
@ -79,4 +90,77 @@ static double calculateLatencyMillis(int64_t position1, int64_t nanoseconds1,
return latencyMillis;
}
// ================================================================================
// These Futex calls are common online examples.
static android::status_t sys_futex(void *addr1, int op, int val1,
struct timespec *timeout, void *addr2, int val3) {
android::status_t result = (android::status_t) syscall(SYS_futex, addr1,
op, val1, timeout,
addr2, val3);
return (result == 0) ? 0 : -errno;
}
static android::status_t futex_wake(void *addr, int numWake) {
// Use _PRIVATE because we are just using the futex in one process.
return sys_futex(addr, FUTEX_WAKE_PRIVATE, numWake, NULL, NULL, 0);
}
static android::status_t futex_wait(void *addr, int current, struct timespec *time) {
// Use _PRIVATE because we are just using the futex in one process.
return sys_futex(addr, FUTEX_WAIT_PRIVATE, current, time, NULL, 0);
}
// TODO better name?
/**
* The WakeUp class is used to send a wakeup signal to one or more sleeping threads.
*/
class WakeUp {
public:
WakeUp() : mValue(0) {}
explicit WakeUp(int32_t value) : mValue(value) {}
/**
* Wait until the internal value no longer matches the given value.
* Note that this code uses a futex, which is subject to spurious wake-ups.
* So check to make sure that the desired condition has been met.
*
* @return zero if the value changes or various negative errors including
* -ETIMEDOUT if a timeout occurs,
* or -EINTR if interrupted by a signal,
* or -EAGAIN or -EWOULDBLOCK if the internal value does not match the specified value
*/
android::status_t wait(int32_t value, int64_t timeoutNanoseconds) {
struct timespec time;
convertNanosecondsToTimespec(timeoutNanoseconds, &time);
return futex_wait(&mValue, value, &time);
}
/**
* Increment value and wake up any threads that need to be woken.
*
* @return number of waiters woken up
*/
android::status_t wake() {
++mValue;
return futex_wake(&mValue, INT_MAX);
}
/**
* Set value and wake up any threads that need to be woken.
*
* @return number of waiters woken up
*/
android::status_t wake(int32_t value) {
mValue.store(value);
return futex_wake(&mValue, INT_MAX);
}
int32_t get() {
return mValue.load();
}
private:
std::atomic<int32_t> mValue;
};
#endif // AAUDIO_EXAMPLE_UTILS_H

@ -23,6 +23,7 @@
#include <sched.h>
#include <aaudio/AAudio.h>
#include <atomic>
#include "AAudioArgsParser.h"
#include "SineGenerator.h"
@ -219,18 +220,26 @@ private:
AAudioStream *mStream = nullptr;
aaudio_sharing_mode_t mRequestedSharingMode = SHARING_MODE;
aaudio_performance_mode_t mRequestedPerformanceMode = PERFORMANCE_MODE;
};
typedef struct SineThreadedData_s {
SineGenerator sineOsc1;
SineGenerator sineOsc2;
int64_t framesTotal = 0;
int64_t nextFrameToGlitch = FORCED_UNDERRUN_PERIOD_FRAMES;
int32_t minNumFrames = INT32_MAX;
int32_t maxNumFrames = 0;
int scheduler;
int scheduler = 0;
bool schedulerChecked = false;
bool forceUnderruns = false;
AAudioSimplePlayer simplePlayer;
int32_t callbackCount = 0;
WakeUp waker{AAUDIO_OK};
} SineThreadedData_t;
// Callback function that fills the audio output buffer.
@ -247,6 +256,7 @@ aaudio_data_callback_result_t SimplePlayerDataCallbackProc(
return AAUDIO_CALLBACK_RESULT_STOP;
}
SineThreadedData_t *sineData = (SineThreadedData_t *) userData;
sineData->callbackCount++;
sineData->framesTotal += numFrames;
@ -304,9 +314,16 @@ aaudio_data_callback_result_t SimplePlayerDataCallbackProc(
void SimplePlayerErrorCallbackProc(
AAudioStream *stream __unused,
void *userData __unused,
aaudio_result_t error)
{
printf("Error Callback, error: %d\n",(int)error);
aaudio_result_t error) {
// should not happen but just in case...
if (userData == nullptr) {
printf("ERROR - MyPlayerErrorCallbackProc needs userData\n");
return;
}
SineThreadedData_t *sineData = (SineThreadedData_t *) userData;
android::status_t ret = sineData->waker.wake(error);
printf("Error Callback, error: %d, futex wake returns %d\n", error, ret);
}
#endif //AAUDIO_SIMPLE_PLAYER_H

@ -58,6 +58,13 @@ public:
}
}
void setAmplitude(double amplitude) {
mAmplitude = amplitude;
}
double getAmplitude() const {
return mAmplitude;
}
private:
void advancePhase() {
mPhase += mPhaseIncrement;

@ -15,6 +15,7 @@
*/
// Play sine waves using an AAudio callback.
// If a disconnection occurs then reopen the stream on the new device.
#include <assert.h>
#include <unistd.h>
@ -22,33 +23,32 @@
#include <sched.h>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <time.h>
#include <aaudio/AAudio.h>
#include "AAudioExampleUtils.h"
#include "AAudioSimplePlayer.h"
#include "../../utils/AAudioSimplePlayer.h"
int main(int argc, const char **argv)
/**
* Open stream, play some sine waves, then close the stream.
*
* @param argParser
* @return AAUDIO_OK or negative error code
*/
static aaudio_result_t testOpenPlayClose(AAudioArgsParser &argParser)
{
AAudioArgsParser argParser;
AAudioSimplePlayer player;
SineThreadedData_t myData;
aaudio_result_t result;
int32_t actualSampleRate;
// Make printf print immediately so that debug info is not stuck
// in a buffer if we hang or crash.
setvbuf(stdout, nullptr, _IONBF, (size_t) 0);
printf("%s - Play a sine sweep using an AAudio callback V0.1.3\n", argv[0]);
AAudioSimplePlayer &player = myData.simplePlayer;
aaudio_result_t result = AAUDIO_OK;
bool disconnected = false;
int64_t startedAtNanos;
printf("----------------------- run complete test --------------------------\n");
myData.schedulerChecked = false;
myData.callbackCount = 0;
myData.forceUnderruns = false; // set true to test AAudioStream_getXRunCount()
if (argParser.parseArgs(argc, argv)) {
return EXIT_FAILURE;
}
result = player.open(argParser,
SimplePlayerDataCallbackProc, SimplePlayerErrorCallbackProc, &myData);
if (result != AAUDIO_OK) {
@ -58,13 +58,19 @@ int main(int argc, const char **argv)
argParser.compareWithStream(player.getStream());
actualSampleRate = player.getSampleRate();
myData.sineOsc1.setup(440.0, actualSampleRate);
myData.sineOsc1.setSweep(300.0, 600.0, 5.0);
myData.sineOsc2.setup(660.0, actualSampleRate);
myData.sineOsc2.setSweep(350.0, 900.0, 7.0);
// Setup sine wave generators.
{
int32_t actualSampleRate = player.getSampleRate();
myData.sineOsc1.setup(440.0, actualSampleRate);
myData.sineOsc1.setSweep(300.0, 600.0, 5.0);
myData.sineOsc1.setAmplitude(0.2);
myData.sineOsc2.setup(660.0, actualSampleRate);
myData.sineOsc2.setSweep(350.0, 900.0, 7.0);
myData.sineOsc2.setAmplitude(0.2);
}
#if 0
// writes not allowed for callback streams
result = player.prime(); // FIXME crashes AudioTrack.cpp
if (result != AAUDIO_OK) {
fprintf(stderr, "ERROR - player.prime() returned %d\n", result);
@ -78,34 +84,32 @@ int main(int argc, const char **argv)
goto error;
}
// Play a sine wave in the background.
printf("Sleep for %d seconds while audio plays in a callback thread.\n",
argParser.getDurationSeconds());
startedAtNanos = getNanoseconds(CLOCK_MONOTONIC);
for (int second = 0; second < argParser.getDurationSeconds(); second++)
{
const struct timespec request = { .tv_sec = 1, .tv_nsec = 0 };
(void) clock_nanosleep(CLOCK_MONOTONIC, 0 /*flags*/, &request, NULL /*remain*/);
aaudio_stream_state_t state;
result = AAudioStream_waitForStateChange(player.getStream(),
AAUDIO_STREAM_STATE_CLOSED,
&state,
0);
// Sleep a while. Wake up early if there is an error, for example a DISCONNECT.
long ret = myData.waker.wait(AAUDIO_OK, NANOS_PER_SECOND);
int64_t millis = (getNanoseconds(CLOCK_MONOTONIC) - startedAtNanos) / NANOS_PER_MILLISECOND;
result = myData.waker.get();
printf("wait() returns %ld, aaudio_result = %d, at %6d millis"
", second = %d, framesWritten = %8d, underruns = %d\n",
ret, result, (int) millis,
second,
(int) AAudioStream_getFramesWritten(player.getStream()),
(int) AAudioStream_getXRunCount(player.getStream()));
if (result != AAUDIO_OK) {
fprintf(stderr, "ERROR - AAudioStream_waitForStateChange() returned %d\n", result);
goto error;
}
if (state != AAUDIO_STREAM_STATE_STARTING && state != AAUDIO_STREAM_STATE_STARTED) {
printf("Stream state is %d %s!\n", state, AAudio_convertStreamStateToText(state));
if (result == AAUDIO_ERROR_DISCONNECTED) {
disconnected = true;
}
break;
}
printf("framesWritten = %d, underruns = %d\n",
(int) AAudioStream_getFramesWritten(player.getStream()),
(int) AAudioStream_getXRunCount(player.getStream())
);
}
printf("Woke up now.\n");
printf("AAudio result = %d = %s\n", result, AAudio_convertResultToText(result));
printf("call stop()\n");
printf("call stop() callback # = %d\n", myData.callbackCount);
result = player.stop();
if (result != AAUDIO_OK) {
goto error;
@ -126,10 +130,28 @@ int main(int argc, const char **argv)
printf("max numFrames = %8d\n", (int) myData.maxNumFrames);
printf("SUCCESS\n");
return EXIT_SUCCESS;
error:
player.close();
printf("exiting - AAudio result = %d = %s\n", result, AAudio_convertResultToText(result));
return EXIT_FAILURE;
return disconnected ? AAUDIO_ERROR_DISCONNECTED : result;
}
int main(int argc, const char **argv)
{
AAudioArgsParser argParser;
aaudio_result_t result;
// Make printf print immediately so that debug info is not stuck
// in a buffer if we hang or crash.
setvbuf(stdout, nullptr, _IONBF, (size_t) 0);
printf("%s - Play a sine sweep using an AAudio callback V0.1.2\n", argv[0]);
if (argParser.parseArgs(argc, argv)) {
return EXIT_FAILURE;
}
// Keep looping until we can complete the test without disconnecting.
while((result = testOpenPlayClose(argParser)) == AAUDIO_ERROR_DISCONNECTED);
return (result) ? EXIT_FAILURE : EXIT_SUCCESS;
}

Loading…
Cancel
Save