Loading media/libaaudio/examples/utils/AAudioExampleUtils.h +85 −1 Original line number Diff line number Diff line Loading @@ -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) Loading @@ -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); Loading Loading @@ -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 media/libaaudio/examples/utils/AAudioSimplePlayer.h +21 −4 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ #include <sched.h> #include <aaudio/AAudio.h> #include <atomic> #include "AAudioArgsParser.h" #include "SineGenerator.h" Loading Loading @@ -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. Loading @@ -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; Loading Loading @@ -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 media/libaaudio/examples/utils/SineGenerator.h +7 −0 Original line number Diff line number Diff line Loading @@ -58,6 +58,13 @@ public: } } void setAmplitude(double amplitude) { mAmplitude = amplitude; } double getAmplitude() const { return mAmplitude; } private: void advancePhase() { mPhase += mPhaseIncrement; Loading media/libaaudio/examples/write_sine/src/write_sine_callback.cpp +64 −42 Original line number Diff line number Diff line Loading @@ -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> Loading @@ -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) { Loading @@ -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); Loading @@ -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; Loading @@ -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; } Loading
media/libaaudio/examples/utils/AAudioExampleUtils.h +85 −1 Original line number Diff line number Diff line Loading @@ -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) Loading @@ -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); Loading Loading @@ -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
media/libaaudio/examples/utils/AAudioSimplePlayer.h +21 −4 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ #include <sched.h> #include <aaudio/AAudio.h> #include <atomic> #include "AAudioArgsParser.h" #include "SineGenerator.h" Loading Loading @@ -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. Loading @@ -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; Loading Loading @@ -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
media/libaaudio/examples/utils/SineGenerator.h +7 −0 Original line number Diff line number Diff line Loading @@ -58,6 +58,13 @@ public: } } void setAmplitude(double amplitude) { mAmplitude = amplitude; } double getAmplitude() const { return mAmplitude; } private: void advancePhase() { mPhase += mPhaseIncrement; Loading
media/libaaudio/examples/write_sine/src/write_sine_callback.cpp +64 −42 Original line number Diff line number Diff line Loading @@ -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> Loading @@ -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) { Loading @@ -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); Loading @@ -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; Loading @@ -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; }