Loading media/libaaudio/examples/utils/AAudioArgsParser.h +6 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,8 @@ #ifndef AAUDIO_EXAMPLE_ARGS_PARSER_H #define AAUDIO_EXAMPLE_ARGS_PARSER_H #define MAX_CHANNELS 8 #include <cctype> #include <unistd.h> #include <stdio.h> Loading @@ -39,6 +41,10 @@ public: } void setChannelCount(int32_t channelCount) { if (channelCount > MAX_CHANNELS) { printf("Sorry, MAX of %d channels!\n", MAX_CHANNELS); channelCount = MAX_CHANNELS; } mChannelCount = channelCount; } Loading media/libaaudio/examples/utils/AAudioSimplePlayer.h +81 −39 Original line number Diff line number Diff line Loading @@ -19,11 +19,10 @@ #ifndef AAUDIO_SIMPLE_PLAYER_H #define AAUDIO_SIMPLE_PLAYER_H #include <unistd.h> #include <sched.h> #include <unistd.h> #include <aaudio/AAudio.h> #include <atomic> #include "AAudioArgsParser.h" #include "SineGenerator.h" Loading Loading @@ -70,13 +69,6 @@ public: } // TODO Extract a common base class for record and playback. /** * Also known as "sample rate" * Only call this after open() has been called. */ int32_t getFramesPerSecond() const { return getSampleRate(); // alias } /** * Only call this after open() has been called. Loading Loading @@ -172,6 +164,7 @@ public: result = AAudioStreamBuilder_openStream(builder, &mStream); AAudioStreamBuilder_delete(builder); return result; } Loading Loading @@ -219,6 +212,28 @@ public: return result; } // Pause the stream. AAudio will stop calling your callback function. aaudio_result_t pause() { aaudio_result_t result = AAudioStream_requestPause(mStream); if (result != AAUDIO_OK) { printf("ERROR - AAudioStream_requestPause() returned %d %s\n", result, AAudio_convertResultToText(result)); } int32_t xRunCount = AAudioStream_getXRunCount(mStream); printf("AAudioStream_getXRunCount %d\n", xRunCount); return result; } // Flush the stream. AAudio will stop calling your callback function. aaudio_result_t flush() { aaudio_result_t result = AAudioStream_requestFlush(mStream); if (result != AAUDIO_OK) { printf("ERROR - AAudioStream_requestFlush() returned %d %s\n", result, AAudio_convertResultToText(result)); } return result; } AAudioStream *getStream() const { return mStream; } Loading @@ -232,14 +247,16 @@ private: typedef struct SineThreadedData_s { SineGenerator sineOsc1; SineGenerator sineOsc2; SineGenerator sineOscillators[MAX_CHANNELS]; Timestamp timestamps[MAX_TIMESTAMPS]; int64_t framesTotal = 0; int64_t nextFrameToGlitch = FORCED_UNDERRUN_PERIOD_FRAMES; int32_t minNumFrames = INT32_MAX; int32_t maxNumFrames = 0; int32_t timestampCount = 0; // in timestamps int32_t sampleRate = 48000; int32_t prefixToneFrames = 0; bool sweepSetup = false; int scheduler = 0; bool schedulerChecked = false; Loading @@ -249,6 +266,30 @@ typedef struct SineThreadedData_s { int32_t callbackCount = 0; WakeUp waker{AAUDIO_OK}; /** * Set sampleRate first. */ void setupSineBlip() { for (int i = 0; i < MAX_CHANNELS; ++i) { double centerFrequency = 880.0 * (i + 2); sineOscillators[i].setup(centerFrequency, sampleRate); sineOscillators[i].setSweep(centerFrequency, centerFrequency, 0.0); } } void setupSineSweeps() { for (int i = 0; i < MAX_CHANNELS; ++i) { double centerFrequency = 220.0 * (i + 2); sineOscillators[i].setup(centerFrequency, sampleRate); double minFrequency = centerFrequency * 2.0 / 3.0; // Change range slightly so they will go out of phase. double maxFrequency = centerFrequency * 3.0 / 2.0; double sweepSeconds = 5.0 + i; sineOscillators[i].setSweep(minFrequency, maxFrequency, sweepSeconds); } sweepSetup = true; } } SineThreadedData_t; // Callback function that fills the audio output buffer. Loading @@ -265,9 +306,11 @@ aaudio_data_callback_result_t SimplePlayerDataCallbackProc( return AAUDIO_CALLBACK_RESULT_STOP; } SineThreadedData_t *sineData = (SineThreadedData_t *) userData; sineData->callbackCount++; sineData->framesTotal += numFrames; // Play an initial high tone so we can tell whether the beginning was truncated. if (!sineData->sweepSetup && sineData->framesTotal >= sineData->prefixToneFrames) { sineData->setupSineSweeps(); } if (sineData->forceUnderruns) { if (sineData->framesTotal > sineData->nextFrameToGlitch) { Loading Loading @@ -301,26 +344,23 @@ aaudio_data_callback_result_t SimplePlayerDataCallbackProc( } int32_t samplesPerFrame = AAudioStream_getChannelCount(stream); // This code only plays on the first one or two channels. // TODO Support arbitrary number of channels. int numActiveOscilators = (samplesPerFrame > MAX_CHANNELS) ? MAX_CHANNELS : samplesPerFrame; switch (AAudioStream_getFormat(stream)) { case AAUDIO_FORMAT_PCM_I16: { int16_t *audioBuffer = (int16_t *) audioData; // Render sine waves as shorts to first channel. sineData->sineOsc1.render(&audioBuffer[0], samplesPerFrame, numFrames); // Render sine waves to second channel if there is one. if (samplesPerFrame > 1) { sineData->sineOsc2.render(&audioBuffer[1], samplesPerFrame, numFrames); for (int i = 0; i < numActiveOscilators; ++i) { sineData->sineOscillators[i].render(&audioBuffer[i], samplesPerFrame, numFrames); } } break; case AAUDIO_FORMAT_PCM_FLOAT: { float *audioBuffer = (float *) audioData; // Render sine waves as floats to first channel. sineData->sineOsc1.render(&audioBuffer[0], samplesPerFrame, numFrames); // Render sine waves to second channel if there is one. if (samplesPerFrame > 1) { sineData->sineOsc2.render(&audioBuffer[1], samplesPerFrame, numFrames); for (int i = 0; i < numActiveOscilators; ++i) { sineData->sineOscillators[i].render(&audioBuffer[i], samplesPerFrame, numFrames); } } break; Loading @@ -328,6 +368,8 @@ aaudio_data_callback_result_t SimplePlayerDataCallbackProc( return AAUDIO_CALLBACK_RESULT_STOP; } sineData->callbackCount++; sineData->framesTotal += numFrames; return AAUDIO_CALLBACK_RESULT_CONTINUE; } Loading media/libaaudio/examples/utils/SineGenerator.h +10 −9 Original line number Diff line number Diff line Loading @@ -31,20 +31,20 @@ public: } void setSweep(double frequencyLow, double frequencyHigh, double seconds) { mSweeping = seconds > 0.0; if (mSweeping) { mPhaseIncrementLow = frequencyLow * M_PI * 2 / mFrameRate; mPhaseIncrementHigh = frequencyHigh * M_PI * 2 / mFrameRate; double numFrames = seconds * mFrameRate; mUpScaler = pow((frequencyHigh / frequencyLow), (1.0 / numFrames)); mDownScaler = 1.0 / mUpScaler; mGoingUp = true; mSweeping = true; } } void render(int16_t *buffer, int32_t channelStride, int32_t numFrames) { int sampleIndex = 0; for (int i = 0; i < numFrames; i++) { buffer[sampleIndex] = (int16_t) (32767 * sin(mPhase) * mAmplitude); buffer[sampleIndex] = (int16_t) (INT16_MAX * sin(mPhase) * mAmplitude); sampleIndex += channelStride; advancePhase(); } Loading @@ -61,6 +61,7 @@ public: void setAmplitude(double amplitude) { mAmplitude = amplitude; } double getAmplitude() const { return mAmplitude; } Loading media/libaaudio/examples/write_sine/src/write_sine.cpp +11 −11 Original line number Diff line number Diff line Loading @@ -48,6 +48,7 @@ int main(int argc, const char **argv) int32_t framesToPlay = 0; int32_t framesLeft = 0; int32_t xRunCount = 0; int numActiveOscilators = 0; float *floatData = nullptr; int16_t *shortData = nullptr; Loading Loading @@ -77,8 +78,8 @@ int main(int argc, const char **argv) actualSampleRate = AAudioStream_getSampleRate(aaudioStream); actualDataFormat = AAudioStream_getFormat(aaudioStream); myData.sineOsc1.setup(440.0, actualSampleRate); myData.sineOsc2.setup(660.0, actualSampleRate); myData.sampleRate = actualSampleRate; myData.setupSineSweeps(); // Some DMA might use very short bursts of 16 frames. We don't need to write such small // buffers. But it helps to use a multiple of the burst size for predictable scheduling. Loading Loading @@ -117,19 +118,18 @@ int main(int argc, const char **argv) // Play for a while. framesToPlay = actualSampleRate * argParser.getDurationSeconds(); framesLeft = framesToPlay; numActiveOscilators = (actualChannelCount > MAX_CHANNELS) ? MAX_CHANNELS : actualChannelCount; while (framesLeft > 0) { // Render as FLOAT or PCM if (actualDataFormat == AAUDIO_FORMAT_PCM_FLOAT) { // Render sine waves to left and right channels. myData.sineOsc1.render(&floatData[0], actualChannelCount, framesPerWrite); if (actualChannelCount > 1) { myData.sineOsc2.render(&floatData[1], actualChannelCount, framesPerWrite); for (int i = 0; i < numActiveOscilators; ++i) { myData.sineOscillators[i].render(&floatData[i], actualChannelCount, framesPerWrite); } } else if (actualDataFormat == AAUDIO_FORMAT_PCM_I16) { // Render sine waves to left and right channels. myData.sineOsc1.render(&shortData[0], actualChannelCount, framesPerWrite); if (actualChannelCount > 1) { myData.sineOsc2.render(&shortData[1], actualChannelCount, framesPerWrite); for (int i = 0; i < numActiveOscilators; ++i) { myData.sineOscillators[i].render(&shortData[i], actualChannelCount, framesPerWrite); } } Loading media/libaaudio/examples/write_sine/src/write_sine_callback.cpp +129 −46 Original line number Diff line number Diff line Loading @@ -28,7 +28,6 @@ #include <aaudio/AAudio.h> #include "AAudioExampleUtils.h" #include "AAudioSimplePlayer.h" #include "../../utils/AAudioSimplePlayer.h" /** * Open stream, play some sine waves, then close the stream. Loading @@ -36,37 +35,39 @@ * @param argParser * @return AAUDIO_OK or negative error code */ static aaudio_result_t testOpenPlayClose(AAudioArgsParser &argParser) static aaudio_result_t testOpenPlayClose(AAudioArgsParser &argParser, int32_t loopCount, int32_t prefixToneMsec) { SineThreadedData_t myData; AAudioSimplePlayer &player = myData.simplePlayer; aaudio_result_t result = AAUDIO_OK; bool disconnected = false; bool bailOut = false; int64_t startedAtNanos; printf("----------------------- run complete test --------------------------\n"); myData.schedulerChecked = false; myData.callbackCount = 0; // TODO add a command line option for the forceUnderruns myData.forceUnderruns = false; // set true to test AAudioStream_getXRunCount() result = player.open(argParser, SimplePlayerDataCallbackProc, SimplePlayerErrorCallbackProc, &myData); if (result != AAUDIO_OK) { fprintf(stderr, "ERROR - player.open() returned %d\n", result); fprintf(stderr, "ERROR - player.open() returned %s\n", AAudio_convertResultToText(result)); goto error; } argParser.compareWithStream(player.getStream()); // 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); myData.sampleRate = player.getSampleRate(); myData.prefixToneFrames = prefixToneMsec * myData.sampleRate / 1000; if (myData.prefixToneFrames > 0) { myData.setupSineBlip(); } else { myData.setupSineSweeps(); } #if 0 Loading @@ -78,6 +79,23 @@ static aaudio_result_t testOpenPlayClose(AAudioArgsParser &argParser) } #endif for (int loopIndex = 0; loopIndex < loopCount; loopIndex++) { // Only play data on every other loop so we can hear if there is stale data. double amplitude; int32_t durationSeconds; if ((loopIndex & 1) == 0) { printf("--------------- SINE ------\n"); amplitude = 0.2; durationSeconds = argParser.getDurationSeconds(); } else { printf("--------------- QUIET -----\n"); amplitude = 0.0; durationSeconds = 2; // just wait briefly when quiet } for (int i = 0; i < MAX_CHANNELS; ++i) { myData.sineOscillators[i].setAmplitude(amplitude); } result = player.start(); if (result != AAUDIO_OK) { fprintf(stderr, "ERROR - player.start() returned %d\n", result); Loading @@ -85,35 +103,69 @@ static aaudio_result_t testOpenPlayClose(AAudioArgsParser &argParser) } // Play a sine wave in the background. printf("Sleep for %d seconds while audio plays in a callback thread.\n", argParser.getDurationSeconds()); printf("Sleep for %d seconds while audio plays in a callback thread. %d of %d\n", argParser.getDurationSeconds(), (loopIndex + 1), loopCount); startedAtNanos = getNanoseconds(CLOCK_MONOTONIC); for (int second = 0; second < argParser.getDurationSeconds(); second++) { for (int second = 0; second < durationSeconds; second++) { // 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; 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", ", second = %3d, framesWritten = %8d, underruns = %d\n", ret, result, (int) millis, second, (int) AAudioStream_getFramesWritten(player.getStream()), (int) AAudioStream_getXRunCount(player.getStream())); if (result != AAUDIO_OK) { if (result == AAUDIO_ERROR_DISCONNECTED) { disconnected = true; } disconnected = (result == AAUDIO_ERROR_DISCONNECTED); bailOut = true; break; } } printf("AAudio result = %d = %s\n", result, AAudio_convertResultToText(result)); printf("call stop() callback # = %d\n", myData.callbackCount); // Alternate between using stop or pause for each sine/quiet pair. // Repeat this pattern: {sine-stop-quiet-stop-sine-pause-quiet-pause} if ((loopIndex & 2) == 0) { printf("STOP, callback # = %d\n", myData.callbackCount); result = player.stop(); } else { printf("PAUSE/FLUSH, callback # = %d\n", myData.callbackCount); result = player.pause(); if (result != AAUDIO_OK) { goto error; } result = player.flush(); } if (result != AAUDIO_OK) { goto error; } if (bailOut) { break; } { aaudio_stream_state_t state = AAudioStream_getState(player.getStream()); aaudio_stream_state_t finalState = AAUDIO_STREAM_STATE_UNINITIALIZED; int64_t timeoutNanos = 2000 * NANOS_PER_MILLISECOND; result = AAudioStream_waitForStateChange(player.getStream(), state, &finalState, timeoutNanos); printf("waitForStateChange returns %s, state = %s\n", AAudio_convertResultToText(result), AAudio_convertStreamStateToText(finalState)); int64_t written = AAudioStream_getFramesWritten(player.getStream()); int64_t read = AAudioStream_getFramesRead(player.getStream()); printf(" framesWritten = %lld, framesRead = %lld, diff = %d\n", (long long) written, (long long) read, (int) (written - read)); } } printf("call close()\n"); result = player.close(); if (result != AAUDIO_OK) { Loading Loading @@ -147,23 +199,54 @@ error: return disconnected ? AAUDIO_ERROR_DISCONNECTED : result; } static void usage() { 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"); } int main(int argc, const char **argv) { AAudioArgsParser argParser; aaudio_result_t result; int32_t loopCount = 1; int32_t prefixToneMsec = 0; // 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; printf("%s - Play a sine sweep using an AAudio callback V0.1.3\n", argv[0]); for (int i = 1; i < argc; i++) { const char *arg = argv[i]; if (argParser.parseArg(arg)) { // Handle options that are not handled by the ArgParser if (arg[0] == '-') { char option = arg[1]; switch (option) { case 'l': loopCount = atoi(&arg[2]); break; case 't': prefixToneMsec = atoi(&arg[2]); break; default: usage(); exit(EXIT_FAILURE); break; } } else { usage(); exit(EXIT_FAILURE); break; } } } // Keep looping until we can complete the test without disconnecting. while((result = testOpenPlayClose(argParser)) == AAUDIO_ERROR_DISCONNECTED); while((result = testOpenPlayClose(argParser, loopCount, prefixToneMsec)) == AAUDIO_ERROR_DISCONNECTED); return (result) ? EXIT_FAILURE : EXIT_SUCCESS; } Loading
media/libaaudio/examples/utils/AAudioArgsParser.h +6 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,8 @@ #ifndef AAUDIO_EXAMPLE_ARGS_PARSER_H #define AAUDIO_EXAMPLE_ARGS_PARSER_H #define MAX_CHANNELS 8 #include <cctype> #include <unistd.h> #include <stdio.h> Loading @@ -39,6 +41,10 @@ public: } void setChannelCount(int32_t channelCount) { if (channelCount > MAX_CHANNELS) { printf("Sorry, MAX of %d channels!\n", MAX_CHANNELS); channelCount = MAX_CHANNELS; } mChannelCount = channelCount; } Loading
media/libaaudio/examples/utils/AAudioSimplePlayer.h +81 −39 Original line number Diff line number Diff line Loading @@ -19,11 +19,10 @@ #ifndef AAUDIO_SIMPLE_PLAYER_H #define AAUDIO_SIMPLE_PLAYER_H #include <unistd.h> #include <sched.h> #include <unistd.h> #include <aaudio/AAudio.h> #include <atomic> #include "AAudioArgsParser.h" #include "SineGenerator.h" Loading Loading @@ -70,13 +69,6 @@ public: } // TODO Extract a common base class for record and playback. /** * Also known as "sample rate" * Only call this after open() has been called. */ int32_t getFramesPerSecond() const { return getSampleRate(); // alias } /** * Only call this after open() has been called. Loading Loading @@ -172,6 +164,7 @@ public: result = AAudioStreamBuilder_openStream(builder, &mStream); AAudioStreamBuilder_delete(builder); return result; } Loading Loading @@ -219,6 +212,28 @@ public: return result; } // Pause the stream. AAudio will stop calling your callback function. aaudio_result_t pause() { aaudio_result_t result = AAudioStream_requestPause(mStream); if (result != AAUDIO_OK) { printf("ERROR - AAudioStream_requestPause() returned %d %s\n", result, AAudio_convertResultToText(result)); } int32_t xRunCount = AAudioStream_getXRunCount(mStream); printf("AAudioStream_getXRunCount %d\n", xRunCount); return result; } // Flush the stream. AAudio will stop calling your callback function. aaudio_result_t flush() { aaudio_result_t result = AAudioStream_requestFlush(mStream); if (result != AAUDIO_OK) { printf("ERROR - AAudioStream_requestFlush() returned %d %s\n", result, AAudio_convertResultToText(result)); } return result; } AAudioStream *getStream() const { return mStream; } Loading @@ -232,14 +247,16 @@ private: typedef struct SineThreadedData_s { SineGenerator sineOsc1; SineGenerator sineOsc2; SineGenerator sineOscillators[MAX_CHANNELS]; Timestamp timestamps[MAX_TIMESTAMPS]; int64_t framesTotal = 0; int64_t nextFrameToGlitch = FORCED_UNDERRUN_PERIOD_FRAMES; int32_t minNumFrames = INT32_MAX; int32_t maxNumFrames = 0; int32_t timestampCount = 0; // in timestamps int32_t sampleRate = 48000; int32_t prefixToneFrames = 0; bool sweepSetup = false; int scheduler = 0; bool schedulerChecked = false; Loading @@ -249,6 +266,30 @@ typedef struct SineThreadedData_s { int32_t callbackCount = 0; WakeUp waker{AAUDIO_OK}; /** * Set sampleRate first. */ void setupSineBlip() { for (int i = 0; i < MAX_CHANNELS; ++i) { double centerFrequency = 880.0 * (i + 2); sineOscillators[i].setup(centerFrequency, sampleRate); sineOscillators[i].setSweep(centerFrequency, centerFrequency, 0.0); } } void setupSineSweeps() { for (int i = 0; i < MAX_CHANNELS; ++i) { double centerFrequency = 220.0 * (i + 2); sineOscillators[i].setup(centerFrequency, sampleRate); double minFrequency = centerFrequency * 2.0 / 3.0; // Change range slightly so they will go out of phase. double maxFrequency = centerFrequency * 3.0 / 2.0; double sweepSeconds = 5.0 + i; sineOscillators[i].setSweep(minFrequency, maxFrequency, sweepSeconds); } sweepSetup = true; } } SineThreadedData_t; // Callback function that fills the audio output buffer. Loading @@ -265,9 +306,11 @@ aaudio_data_callback_result_t SimplePlayerDataCallbackProc( return AAUDIO_CALLBACK_RESULT_STOP; } SineThreadedData_t *sineData = (SineThreadedData_t *) userData; sineData->callbackCount++; sineData->framesTotal += numFrames; // Play an initial high tone so we can tell whether the beginning was truncated. if (!sineData->sweepSetup && sineData->framesTotal >= sineData->prefixToneFrames) { sineData->setupSineSweeps(); } if (sineData->forceUnderruns) { if (sineData->framesTotal > sineData->nextFrameToGlitch) { Loading Loading @@ -301,26 +344,23 @@ aaudio_data_callback_result_t SimplePlayerDataCallbackProc( } int32_t samplesPerFrame = AAudioStream_getChannelCount(stream); // This code only plays on the first one or two channels. // TODO Support arbitrary number of channels. int numActiveOscilators = (samplesPerFrame > MAX_CHANNELS) ? MAX_CHANNELS : samplesPerFrame; switch (AAudioStream_getFormat(stream)) { case AAUDIO_FORMAT_PCM_I16: { int16_t *audioBuffer = (int16_t *) audioData; // Render sine waves as shorts to first channel. sineData->sineOsc1.render(&audioBuffer[0], samplesPerFrame, numFrames); // Render sine waves to second channel if there is one. if (samplesPerFrame > 1) { sineData->sineOsc2.render(&audioBuffer[1], samplesPerFrame, numFrames); for (int i = 0; i < numActiveOscilators; ++i) { sineData->sineOscillators[i].render(&audioBuffer[i], samplesPerFrame, numFrames); } } break; case AAUDIO_FORMAT_PCM_FLOAT: { float *audioBuffer = (float *) audioData; // Render sine waves as floats to first channel. sineData->sineOsc1.render(&audioBuffer[0], samplesPerFrame, numFrames); // Render sine waves to second channel if there is one. if (samplesPerFrame > 1) { sineData->sineOsc2.render(&audioBuffer[1], samplesPerFrame, numFrames); for (int i = 0; i < numActiveOscilators; ++i) { sineData->sineOscillators[i].render(&audioBuffer[i], samplesPerFrame, numFrames); } } break; Loading @@ -328,6 +368,8 @@ aaudio_data_callback_result_t SimplePlayerDataCallbackProc( return AAUDIO_CALLBACK_RESULT_STOP; } sineData->callbackCount++; sineData->framesTotal += numFrames; return AAUDIO_CALLBACK_RESULT_CONTINUE; } Loading
media/libaaudio/examples/utils/SineGenerator.h +10 −9 Original line number Diff line number Diff line Loading @@ -31,20 +31,20 @@ public: } void setSweep(double frequencyLow, double frequencyHigh, double seconds) { mSweeping = seconds > 0.0; if (mSweeping) { mPhaseIncrementLow = frequencyLow * M_PI * 2 / mFrameRate; mPhaseIncrementHigh = frequencyHigh * M_PI * 2 / mFrameRate; double numFrames = seconds * mFrameRate; mUpScaler = pow((frequencyHigh / frequencyLow), (1.0 / numFrames)); mDownScaler = 1.0 / mUpScaler; mGoingUp = true; mSweeping = true; } } void render(int16_t *buffer, int32_t channelStride, int32_t numFrames) { int sampleIndex = 0; for (int i = 0; i < numFrames; i++) { buffer[sampleIndex] = (int16_t) (32767 * sin(mPhase) * mAmplitude); buffer[sampleIndex] = (int16_t) (INT16_MAX * sin(mPhase) * mAmplitude); sampleIndex += channelStride; advancePhase(); } Loading @@ -61,6 +61,7 @@ public: void setAmplitude(double amplitude) { mAmplitude = amplitude; } double getAmplitude() const { return mAmplitude; } Loading
media/libaaudio/examples/write_sine/src/write_sine.cpp +11 −11 Original line number Diff line number Diff line Loading @@ -48,6 +48,7 @@ int main(int argc, const char **argv) int32_t framesToPlay = 0; int32_t framesLeft = 0; int32_t xRunCount = 0; int numActiveOscilators = 0; float *floatData = nullptr; int16_t *shortData = nullptr; Loading Loading @@ -77,8 +78,8 @@ int main(int argc, const char **argv) actualSampleRate = AAudioStream_getSampleRate(aaudioStream); actualDataFormat = AAudioStream_getFormat(aaudioStream); myData.sineOsc1.setup(440.0, actualSampleRate); myData.sineOsc2.setup(660.0, actualSampleRate); myData.sampleRate = actualSampleRate; myData.setupSineSweeps(); // Some DMA might use very short bursts of 16 frames. We don't need to write such small // buffers. But it helps to use a multiple of the burst size for predictable scheduling. Loading Loading @@ -117,19 +118,18 @@ int main(int argc, const char **argv) // Play for a while. framesToPlay = actualSampleRate * argParser.getDurationSeconds(); framesLeft = framesToPlay; numActiveOscilators = (actualChannelCount > MAX_CHANNELS) ? MAX_CHANNELS : actualChannelCount; while (framesLeft > 0) { // Render as FLOAT or PCM if (actualDataFormat == AAUDIO_FORMAT_PCM_FLOAT) { // Render sine waves to left and right channels. myData.sineOsc1.render(&floatData[0], actualChannelCount, framesPerWrite); if (actualChannelCount > 1) { myData.sineOsc2.render(&floatData[1], actualChannelCount, framesPerWrite); for (int i = 0; i < numActiveOscilators; ++i) { myData.sineOscillators[i].render(&floatData[i], actualChannelCount, framesPerWrite); } } else if (actualDataFormat == AAUDIO_FORMAT_PCM_I16) { // Render sine waves to left and right channels. myData.sineOsc1.render(&shortData[0], actualChannelCount, framesPerWrite); if (actualChannelCount > 1) { myData.sineOsc2.render(&shortData[1], actualChannelCount, framesPerWrite); for (int i = 0; i < numActiveOscilators; ++i) { myData.sineOscillators[i].render(&shortData[i], actualChannelCount, framesPerWrite); } } Loading
media/libaaudio/examples/write_sine/src/write_sine_callback.cpp +129 −46 Original line number Diff line number Diff line Loading @@ -28,7 +28,6 @@ #include <aaudio/AAudio.h> #include "AAudioExampleUtils.h" #include "AAudioSimplePlayer.h" #include "../../utils/AAudioSimplePlayer.h" /** * Open stream, play some sine waves, then close the stream. Loading @@ -36,37 +35,39 @@ * @param argParser * @return AAUDIO_OK or negative error code */ static aaudio_result_t testOpenPlayClose(AAudioArgsParser &argParser) static aaudio_result_t testOpenPlayClose(AAudioArgsParser &argParser, int32_t loopCount, int32_t prefixToneMsec) { SineThreadedData_t myData; AAudioSimplePlayer &player = myData.simplePlayer; aaudio_result_t result = AAUDIO_OK; bool disconnected = false; bool bailOut = false; int64_t startedAtNanos; printf("----------------------- run complete test --------------------------\n"); myData.schedulerChecked = false; myData.callbackCount = 0; // TODO add a command line option for the forceUnderruns myData.forceUnderruns = false; // set true to test AAudioStream_getXRunCount() result = player.open(argParser, SimplePlayerDataCallbackProc, SimplePlayerErrorCallbackProc, &myData); if (result != AAUDIO_OK) { fprintf(stderr, "ERROR - player.open() returned %d\n", result); fprintf(stderr, "ERROR - player.open() returned %s\n", AAudio_convertResultToText(result)); goto error; } argParser.compareWithStream(player.getStream()); // 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); myData.sampleRate = player.getSampleRate(); myData.prefixToneFrames = prefixToneMsec * myData.sampleRate / 1000; if (myData.prefixToneFrames > 0) { myData.setupSineBlip(); } else { myData.setupSineSweeps(); } #if 0 Loading @@ -78,6 +79,23 @@ static aaudio_result_t testOpenPlayClose(AAudioArgsParser &argParser) } #endif for (int loopIndex = 0; loopIndex < loopCount; loopIndex++) { // Only play data on every other loop so we can hear if there is stale data. double amplitude; int32_t durationSeconds; if ((loopIndex & 1) == 0) { printf("--------------- SINE ------\n"); amplitude = 0.2; durationSeconds = argParser.getDurationSeconds(); } else { printf("--------------- QUIET -----\n"); amplitude = 0.0; durationSeconds = 2; // just wait briefly when quiet } for (int i = 0; i < MAX_CHANNELS; ++i) { myData.sineOscillators[i].setAmplitude(amplitude); } result = player.start(); if (result != AAUDIO_OK) { fprintf(stderr, "ERROR - player.start() returned %d\n", result); Loading @@ -85,35 +103,69 @@ static aaudio_result_t testOpenPlayClose(AAudioArgsParser &argParser) } // Play a sine wave in the background. printf("Sleep for %d seconds while audio plays in a callback thread.\n", argParser.getDurationSeconds()); printf("Sleep for %d seconds while audio plays in a callback thread. %d of %d\n", argParser.getDurationSeconds(), (loopIndex + 1), loopCount); startedAtNanos = getNanoseconds(CLOCK_MONOTONIC); for (int second = 0; second < argParser.getDurationSeconds(); second++) { for (int second = 0; second < durationSeconds; second++) { // 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; 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", ", second = %3d, framesWritten = %8d, underruns = %d\n", ret, result, (int) millis, second, (int) AAudioStream_getFramesWritten(player.getStream()), (int) AAudioStream_getXRunCount(player.getStream())); if (result != AAUDIO_OK) { if (result == AAUDIO_ERROR_DISCONNECTED) { disconnected = true; } disconnected = (result == AAUDIO_ERROR_DISCONNECTED); bailOut = true; break; } } printf("AAudio result = %d = %s\n", result, AAudio_convertResultToText(result)); printf("call stop() callback # = %d\n", myData.callbackCount); // Alternate between using stop or pause for each sine/quiet pair. // Repeat this pattern: {sine-stop-quiet-stop-sine-pause-quiet-pause} if ((loopIndex & 2) == 0) { printf("STOP, callback # = %d\n", myData.callbackCount); result = player.stop(); } else { printf("PAUSE/FLUSH, callback # = %d\n", myData.callbackCount); result = player.pause(); if (result != AAUDIO_OK) { goto error; } result = player.flush(); } if (result != AAUDIO_OK) { goto error; } if (bailOut) { break; } { aaudio_stream_state_t state = AAudioStream_getState(player.getStream()); aaudio_stream_state_t finalState = AAUDIO_STREAM_STATE_UNINITIALIZED; int64_t timeoutNanos = 2000 * NANOS_PER_MILLISECOND; result = AAudioStream_waitForStateChange(player.getStream(), state, &finalState, timeoutNanos); printf("waitForStateChange returns %s, state = %s\n", AAudio_convertResultToText(result), AAudio_convertStreamStateToText(finalState)); int64_t written = AAudioStream_getFramesWritten(player.getStream()); int64_t read = AAudioStream_getFramesRead(player.getStream()); printf(" framesWritten = %lld, framesRead = %lld, diff = %d\n", (long long) written, (long long) read, (int) (written - read)); } } printf("call close()\n"); result = player.close(); if (result != AAUDIO_OK) { Loading Loading @@ -147,23 +199,54 @@ error: return disconnected ? AAUDIO_ERROR_DISCONNECTED : result; } static void usage() { 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"); } int main(int argc, const char **argv) { AAudioArgsParser argParser; aaudio_result_t result; int32_t loopCount = 1; int32_t prefixToneMsec = 0; // 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; printf("%s - Play a sine sweep using an AAudio callback V0.1.3\n", argv[0]); for (int i = 1; i < argc; i++) { const char *arg = argv[i]; if (argParser.parseArg(arg)) { // Handle options that are not handled by the ArgParser if (arg[0] == '-') { char option = arg[1]; switch (option) { case 'l': loopCount = atoi(&arg[2]); break; case 't': prefixToneMsec = atoi(&arg[2]); break; default: usage(); exit(EXIT_FAILURE); break; } } else { usage(); exit(EXIT_FAILURE); break; } } } // Keep looping until we can complete the test without disconnecting. while((result = testOpenPlayClose(argParser)) == AAUDIO_ERROR_DISCONNECTED); while((result = testOpenPlayClose(argParser, loopCount, prefixToneMsec)) == AAUDIO_ERROR_DISCONNECTED); return (result) ? EXIT_FAILURE : EXIT_SUCCESS; }