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

Commit d7c0f9f5 authored by Phil Burk's avatar Phil Burk Committed by android-build-merger
Browse files

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

am: e185203e

Change-Id: I09ee898282a8dc415aa487a22335c98310176f0d
parents ea9037e6 e185203e
Loading
Loading
Loading
Loading
+85 −1
Original line number Diff line number Diff line
@@ -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
+21 −4
Original line number Diff line number Diff line
@@ -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
+7 −0
Original line number Diff line number Diff line
@@ -58,6 +58,13 @@ public:
        }
    }

    void setAmplitude(double amplitude) {
        mAmplitude = amplitude;
    }
    double getAmplitude() const {
        return mAmplitude;
    }

private:
    void advancePhase() {
        mPhase += mPhaseIncrement;
+64 −42
Original line number Diff line number Diff line
@@ -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();
    // 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 (result == AAUDIO_ERROR_DISCONNECTED) {
                disconnected = true;
            }
        if (state != AAUDIO_STREAM_STATE_STARTING && state != AAUDIO_STREAM_STATE_STARTED) {
            printf("Stream state is %d %s!\n", state, AAudio_convertStreamStateToText(state));
            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 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;
}