Loading media/libaaudio/examples/loopback/Android.bp +5 −1 Original line number Diff line number Diff line Loading @@ -3,6 +3,10 @@ cc_test { gtest: false, srcs: ["src/loopback.cpp"], cflags: ["-Wall", "-Werror"], shared_libs: ["libaaudio"], static_libs: ["libsndfile"], shared_libs: [ "libaaudio", "libaudioutils", ], header_libs: ["libaaudio_example_utils"], } media/libaaudio/examples/loopback/jni/Android.mk +2 −1 Original line number Diff line number Diff line Loading @@ -10,6 +10,7 @@ LOCAL_C_INCLUDES := \ # NDK recommends using this kind of relative path instead of an absolute path. LOCAL_SRC_FILES:= ../src/loopback.cpp LOCAL_CFLAGS := -Wall -Werror LOCAL_SHARED_LIBRARIES := libaaudio LOCAL_STATIC_LIBRARIES := libsndfile LOCAL_SHARED_LIBRARIES := libaaudio libaudioutils LOCAL_MODULE := aaudio_loopback include $(BUILD_EXECUTABLE) media/libaaudio/examples/loopback/src/LoopbackAnalyzer.h +117 −55 Original line number Diff line number Diff line Loading @@ -30,6 +30,8 @@ #include <stdlib.h> #include <unistd.h> #include <audio_utils/sndfile.h> // Tag for machine readable results as property = value pairs #define LOOPBACK_RESULT_TAG "RESULT: " #define LOOPBACK_SAMPLE_RATE 48000 Loading @@ -37,6 +39,7 @@ #define MILLIS_PER_SECOND 1000 #define MAX_ZEROTH_PARTIAL_BINS 40 constexpr double MAX_ECHO_GAIN = 10.0; // based on experiments, otherwise autocorrelation too noisy static const float s_Impulse[] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.2f, // silence on each side of the impulse Loading Loading @@ -156,6 +159,8 @@ static int measureLatencyFromEchos(const float *haystack, int haystackSize, const float *needle, int needleSize, LatencyReport *report) { const double threshold = 0.1; printf("measureLatencyFromEchos: haystackSize = %d, needleSize = %d\n", haystackSize, needleSize); // Find first peak int first = (int) (findFirstMatch(haystack, Loading @@ -173,7 +178,7 @@ static int measureLatencyFromEchos(const float *haystack, int haystackSize, needleSize, threshold) + 0.5); printf("first = %d, again at %d\n", first, again); printf("measureLatencyFromEchos: first = %d, again at %d\n", first, again); first = again; // Allocate results array Loading Loading @@ -270,37 +275,60 @@ public: return mData; } void setSampleRate(int32_t sampleRate) { mSampleRate = sampleRate; } int32_t getSampleRate() { return mSampleRate; } int save(const char *fileName, bool writeShorts = true) { SNDFILE *sndFile = nullptr; int written = 0; const int chunkSize = 64; FILE *fid = fopen(fileName, "wb"); if (fid == NULL) { SF_INFO info = { .frames = mFrameCounter, .samplerate = mSampleRate, .channels = 1, .format = SF_FORMAT_WAV | (writeShorts ? SF_FORMAT_PCM_16 : SF_FORMAT_FLOAT) }; sndFile = sf_open(fileName, SFM_WRITE, &info); if (sndFile == nullptr) { printf("AudioRecording::save(%s) failed to open file\n", fileName); return -errno; } if (writeShorts) { int16_t buffer[chunkSize]; int32_t framesLeft = mFrameCounter; int32_t cursor = 0; while (framesLeft) { int32_t framesToWrite = framesLeft < chunkSize ? framesLeft : chunkSize; for (int i = 0; i < framesToWrite; i++) { buffer[i] = (int16_t) (mData[cursor++] * 32767); } written += fwrite(buffer, sizeof(int16_t), framesToWrite, fid); framesLeft -= framesToWrite; written = sf_writef_float(sndFile, mData, mFrameCounter); sf_close(sndFile); return written; } } else { written = (int) fwrite(mData, sizeof(float), mFrameCounter, fid); int load(const char *fileName) { SNDFILE *sndFile = nullptr; SF_INFO info; sndFile = sf_open(fileName, SFM_READ, &info); if (sndFile == nullptr) { printf("AudioRecording::load(%s) failed to open file\n", fileName); return -errno; } fclose(fid); return written; assert(info.channels == 1); allocate(info.frames); mFrameCounter = sf_readf_float(sndFile, mData, info.frames); sf_close(sndFile); return mFrameCounter; } private: float *mData = nullptr; int32_t mFrameCounter = 0; int32_t mMaxFrames = 0; int32_t mSampleRate = 48000; // common default }; // ==================================================================================== Loading @@ -320,11 +348,25 @@ public: virtual void printStatus() {}; virtual int getResult() { return -1; } virtual bool isDone() { return false; } void setSampleRate(int32_t sampleRate) { virtual int save(const char *fileName) { (void) fileName; return AAUDIO_ERROR_UNIMPLEMENTED; } virtual int load(const char *fileName) { (void) fileName; return AAUDIO_ERROR_UNIMPLEMENTED; } virtual void setSampleRate(int32_t sampleRate) { mSampleRate = sampleRate; } Loading Loading @@ -395,7 +437,13 @@ class EchoAnalyzer : public LoopbackProcessor { public: EchoAnalyzer() : LoopbackProcessor() { audioRecorder.allocate(2 * LOOPBACK_SAMPLE_RATE); mAudioRecording.allocate(2 * getSampleRate()); mAudioRecording.setSampleRate(getSampleRate()); } void setSampleRate(int32_t sampleRate) override { LoopbackProcessor::setSampleRate(sampleRate); mAudioRecording.setSampleRate(sampleRate); } void reset() override { Loading @@ -406,8 +454,12 @@ public: mState = STATE_INITIAL_SILENCE; } virtual int getResult() { return mState == STATE_DONE ? 0 : -1; } virtual bool isDone() { return mState == STATE_DONE; return mState == STATE_DONE || mState == STATE_FAILED; } void setGain(float gain) { Loading @@ -423,30 +475,23 @@ public: printf("EchoAnalyzer ---------------\n"); printf(LOOPBACK_RESULT_TAG "measured.gain = %f\n", mMeasuredLoopGain); printf(LOOPBACK_RESULT_TAG "echo.gain = %f\n", mEchoGain); printf(LOOPBACK_RESULT_TAG "frame.count = %d\n", mFrameCounter); printf(LOOPBACK_RESULT_TAG "test.state = %d\n", mState); if (mMeasuredLoopGain >= 0.9999) { printf(" ERROR - clipping, turn down volume slightly\n"); } else { const float *needle = s_Impulse; int needleSize = (int) (sizeof(s_Impulse) / sizeof(float)); float *haystack = audioRecorder.getData(); int haystackSize = audioRecorder.size(); measureLatencyFromEchos(haystack, haystackSize, needle, needleSize, &latencyReport); if (latencyReport.confidence < 0.01) { printf(" ERROR - confidence too low = %f\n", latencyReport.confidence); float *haystack = mAudioRecording.getData(); int haystackSize = mAudioRecording.size(); measureLatencyFromEchos(haystack, haystackSize, needle, needleSize, &mLatencyReport); if (mLatencyReport.confidence < 0.01) { printf(" ERROR - confidence too low = %f\n", mLatencyReport.confidence); } else { double latencyMillis = 1000.0 * latencyReport.latencyInFrames / getSampleRate(); printf(LOOPBACK_RESULT_TAG "latency.frames = %8.2f\n", latencyReport.latencyInFrames); double latencyMillis = 1000.0 * mLatencyReport.latencyInFrames / getSampleRate(); printf(LOOPBACK_RESULT_TAG "latency.frames = %8.2f\n", mLatencyReport.latencyInFrames); printf(LOOPBACK_RESULT_TAG "latency.msec = %8.2f\n", latencyMillis); printf(LOOPBACK_RESULT_TAG "latency.confidence = %8.6f\n", latencyReport.confidence); } printf(LOOPBACK_RESULT_TAG "latency.confidence = %8.6f\n", mLatencyReport.confidence); } { #define ECHO_FILENAME "/data/oboe_echo.raw" int written = audioRecorder.save(ECHO_FILENAME); printf("Echo wrote %d mono samples to %s on Android device\n", written, ECHO_FILENAME); } } Loading Loading @@ -491,13 +536,18 @@ public: // If we get several in a row then go to next state. if (peak > mPulseThreshold) { if (mDownCounter-- <= 0) { nextState = STATE_WAITING_FOR_SILENCE; //printf("%5d: switch to STATE_WAITING_FOR_SILENCE, measured peak = %f\n", // mLoopCounter, peak); mDownCounter = 8; mMeasuredLoopGain = peak; // assumes original pulse amplitude is one // Calculate gain that will give us a nice decaying echo. mEchoGain = mDesiredEchoGain / mMeasuredLoopGain; if (mEchoGain > MAX_ECHO_GAIN) { printf("ERROR - loop gain too low. Increase the volume.\n"); nextState = STATE_FAILED; } else { nextState = STATE_WAITING_FOR_SILENCE; } } } else { mDownCounter = 8; Loading @@ -524,14 +574,14 @@ public: break; case STATE_SENDING_PULSE: audioRecorder.write(inputData, inputChannelCount, numFrames); mAudioRecording.write(inputData, inputChannelCount, numFrames); sendImpulse(outputData, outputChannelCount); nextState = STATE_GATHERING_ECHOS; //printf("%5d: switch to STATE_GATHERING_ECHOS\n", mLoopCounter); break; case STATE_GATHERING_ECHOS: numWritten = audioRecorder.write(inputData, inputChannelCount, numFrames); numWritten = mAudioRecording.write(inputData, inputChannelCount, numFrames); peak = measurePeakAmplitude(inputData, inputChannelCount, numFrames); if (peak > mMeasuredLoopGain) { mMeasuredLoopGain = peak; // AGC might be raising gain so adjust it on the fly. Loading Loading @@ -565,6 +615,14 @@ public: mLoopCounter++; } int save(const char *fileName) override { return mAudioRecording.save(fileName); } int load(const char *fileName) override { return mAudioRecording.load(fileName); } private: enum echo_state_t { Loading @@ -573,7 +631,8 @@ private: STATE_WAITING_FOR_SILENCE, STATE_SENDING_PULSE, STATE_GATHERING_ECHOS, STATE_DONE STATE_DONE, STATE_FAILED }; int mDownCounter = 500; Loading @@ -584,11 +643,10 @@ private: float mDesiredEchoGain = 0.95f; float mEchoGain = 1.0f; echo_state_t mState = STATE_INITIAL_SILENCE; int32_t mFrameCounter = 0; AudioRecording audioRecorder; LatencyReport latencyReport; PeakDetector mPeakDetector; AudioRecording mAudioRecording; // contains only the input after the gain detection burst LatencyReport mLatencyReport; // PeakDetector mPeakDetector; }; Loading @@ -602,6 +660,10 @@ private: class SineAnalyzer : public LoopbackProcessor { public: virtual int getResult() { return mState == STATE_LOCKED ? 0 : -1; } void report() override { printf("SineAnalyzer ------------------\n"); printf(LOOPBACK_RESULT_TAG "peak.amplitude = %7.5f\n", mPeakAmplitude); Loading media/libaaudio/examples/loopback/src/loopback.cpp +56 −31 Original line number Diff line number Diff line Loading @@ -37,10 +37,10 @@ // Tag for machine readable results as property = value pairs #define RESULT_TAG "RESULT: " #define SAMPLE_RATE 48000 #define NUM_SECONDS 5 #define NUM_INPUT_CHANNELS 1 #define FILENAME "/data/oboe_input.raw" #define FILENAME_ALL "/data/loopback_all.wav" #define FILENAME_ECHOS "/data/loopback_echos.wav" #define APP_VERSION "0.1.22" struct LoopbackData { Loading @@ -61,7 +61,7 @@ struct LoopbackData { SineAnalyzer sineAnalyzer; EchoAnalyzer echoAnalyzer; AudioRecording audioRecorder; AudioRecording audioRecording; LoopbackProcessor *loopbackProcessor; }; Loading Loading @@ -126,7 +126,7 @@ static aaudio_data_callback_result_t MyDataCallbackProc( result = AAUDIO_CALLBACK_RESULT_STOP; } else if (framesRead > 0) { myData->audioRecorder.write(myData->inputData, myData->audioRecording.write(myData->inputData, myData->actualInputChannelCount, numFrames); Loading Loading @@ -176,7 +176,8 @@ static void usage() { printf(" p for _POWER_SAVING\n"); printf(" -t{test} select test mode\n"); printf(" m for sine magnitude\n"); printf(" e for echo latency (default)\n\n"); printf(" e for echo latency (default)\n"); printf(" f for file latency, analyzes %s\n\n", FILENAME_ECHOS); printf(" -x use EXCLUSIVE mode for output\n"); printf(" -X use EXCLUSIVE mode for input\n"); printf("Example: aaudio_loopback -n2 -pl -Pl -x\n"); Loading Loading @@ -205,6 +206,7 @@ static aaudio_performance_mode_t parsePerformanceMode(char c) { enum { TEST_SINE_MAGNITUDE = 0, TEST_ECHO_LATENCY, TEST_FILE_LATENCY, }; static int parseTestMode(char c) { Loading @@ -217,6 +219,9 @@ static int parseTestMode(char c) { case 'e': testMode = TEST_ECHO_LATENCY; break; case 'f': testMode = TEST_FILE_LATENCY; break; default: printf("ERROR in value test mode %c\n", c); break; Loading Loading @@ -268,6 +273,7 @@ int main(int argc, const char **argv) aaudio_format_t actualInputFormat; aaudio_format_t actualOutputFormat; aaudio_performance_mode_t inputPerformanceLevel = AAUDIO_PERFORMANCE_MODE_LOW_LATENCY; int32_t actualSampleRate = 0; int testMode = TEST_ECHO_LATENCY; double gain = 1.0; Loading Loading @@ -324,7 +330,6 @@ int main(int argc, const char **argv) int32_t requestedDuration = argParser.getDurationSeconds(); int32_t recordingDuration = std::min(60, requestedDuration); loopbackData.audioRecorder.allocate(recordingDuration * SAMPLE_RATE); switch(testMode) { case TEST_SINE_MAGNITUDE: Loading @@ -334,6 +339,16 @@ int main(int argc, const char **argv) loopbackData.echoAnalyzer.setGain(gain); loopbackData.loopbackProcessor = &loopbackData.echoAnalyzer; break; case TEST_FILE_LATENCY: { loopbackData.echoAnalyzer.setGain(gain); loopbackData.loopbackProcessor = &loopbackData.echoAnalyzer; int read = loopbackData.loopbackProcessor->load(FILENAME_ECHOS); printf("main() read %d mono samples from %s on Android device\n", read, FILENAME_ECHOS); loopbackData.loopbackProcessor->report(); return 0; } break; default: exit(1); break; Loading @@ -344,7 +359,7 @@ int main(int argc, const char **argv) result = player.open(argParser, MyDataCallbackProc, MyErrorCallbackProc, &loopbackData); if (result != AAUDIO_OK) { fprintf(stderr, "ERROR - player.open() returned %d\n", result); goto finish; exit(1); } outputStream = player.getStream(); argParser.compareWithStream(outputStream); Loading @@ -352,6 +367,10 @@ int main(int argc, const char **argv) actualOutputFormat = AAudioStream_getFormat(outputStream); assert(actualOutputFormat == AAUDIO_FORMAT_PCM_FLOAT); actualSampleRate = AAudioStream_getSampleRate(outputStream); loopbackData.audioRecording.allocate(recordingDuration * actualSampleRate); loopbackData.audioRecording.setSampleRate(actualSampleRate); printf("INPUT stream ----------------------------------------\n"); // Use different parameters for the input. argParser.setNumberOfBursts(AAUDIO_UNSPECIFIED); Loading Loading @@ -380,7 +399,7 @@ int main(int argc, const char **argv) // Allocate a buffer for the audio data. loopbackData.inputFramesMaximum = 32 * framesPerBurst; loopbackData.inputBuffersToDiscard = 100; loopbackData.inputBuffersToDiscard = 200; loopbackData.inputData = new int16_t[loopbackData.inputFramesMaximum * loopbackData.actualInputChannelCount]; Loading Loading @@ -436,6 +455,9 @@ int main(int argc, const char **argv) } } if (loopbackData.loopbackProcessor->getResult() < 0) { printf("Test failed!\n"); } else { printf("input error = %d = %s\n", loopbackData.inputError, AAudio_convertResultToText(loopbackData.inputError)); Loading @@ -447,14 +469,17 @@ int main(int argc, const char **argv) if (loopbackData.inputError == AAUDIO_OK) { if (testMode == TEST_SINE_MAGNITUDE) { printAudioGraph(loopbackData.audioRecorder, 200); printAudioGraph(loopbackData.audioRecording, 200); } loopbackData.loopbackProcessor->report(); } { int written = loopbackData.audioRecorder.save(FILENAME); printf("main() wrote %d mono samples to %s on Android device\n", written, FILENAME); int written = loopbackData.loopbackProcessor->save(FILENAME_ECHOS); printf("main() wrote %d mono samples to %s on Android device\n", written, FILENAME_ECHOS); printf("main() loopbackData.audioRecording.getSampleRate() = %d\n", loopbackData.audioRecording.getSampleRate()); written = loopbackData.audioRecording.save(FILENAME_ALL); printf("main() wrote %d mono samples to %s on Android device\n", written, FILENAME_ALL); } finish: Loading media/libaaudio/examples/utils/AAudioSimplePlayer.h +0 −1 Original line number Diff line number Diff line Loading @@ -170,7 +170,6 @@ public: aaudio_result_t close() { if (mStream != nullptr) { printf("call AAudioStream_close(%p)\n", mStream); fflush(stdout); AAudioStream_close(mStream); mStream = nullptr; } Loading Loading
media/libaaudio/examples/loopback/Android.bp +5 −1 Original line number Diff line number Diff line Loading @@ -3,6 +3,10 @@ cc_test { gtest: false, srcs: ["src/loopback.cpp"], cflags: ["-Wall", "-Werror"], shared_libs: ["libaaudio"], static_libs: ["libsndfile"], shared_libs: [ "libaaudio", "libaudioutils", ], header_libs: ["libaaudio_example_utils"], }
media/libaaudio/examples/loopback/jni/Android.mk +2 −1 Original line number Diff line number Diff line Loading @@ -10,6 +10,7 @@ LOCAL_C_INCLUDES := \ # NDK recommends using this kind of relative path instead of an absolute path. LOCAL_SRC_FILES:= ../src/loopback.cpp LOCAL_CFLAGS := -Wall -Werror LOCAL_SHARED_LIBRARIES := libaaudio LOCAL_STATIC_LIBRARIES := libsndfile LOCAL_SHARED_LIBRARIES := libaaudio libaudioutils LOCAL_MODULE := aaudio_loopback include $(BUILD_EXECUTABLE)
media/libaaudio/examples/loopback/src/LoopbackAnalyzer.h +117 −55 Original line number Diff line number Diff line Loading @@ -30,6 +30,8 @@ #include <stdlib.h> #include <unistd.h> #include <audio_utils/sndfile.h> // Tag for machine readable results as property = value pairs #define LOOPBACK_RESULT_TAG "RESULT: " #define LOOPBACK_SAMPLE_RATE 48000 Loading @@ -37,6 +39,7 @@ #define MILLIS_PER_SECOND 1000 #define MAX_ZEROTH_PARTIAL_BINS 40 constexpr double MAX_ECHO_GAIN = 10.0; // based on experiments, otherwise autocorrelation too noisy static const float s_Impulse[] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.2f, // silence on each side of the impulse Loading Loading @@ -156,6 +159,8 @@ static int measureLatencyFromEchos(const float *haystack, int haystackSize, const float *needle, int needleSize, LatencyReport *report) { const double threshold = 0.1; printf("measureLatencyFromEchos: haystackSize = %d, needleSize = %d\n", haystackSize, needleSize); // Find first peak int first = (int) (findFirstMatch(haystack, Loading @@ -173,7 +178,7 @@ static int measureLatencyFromEchos(const float *haystack, int haystackSize, needleSize, threshold) + 0.5); printf("first = %d, again at %d\n", first, again); printf("measureLatencyFromEchos: first = %d, again at %d\n", first, again); first = again; // Allocate results array Loading Loading @@ -270,37 +275,60 @@ public: return mData; } void setSampleRate(int32_t sampleRate) { mSampleRate = sampleRate; } int32_t getSampleRate() { return mSampleRate; } int save(const char *fileName, bool writeShorts = true) { SNDFILE *sndFile = nullptr; int written = 0; const int chunkSize = 64; FILE *fid = fopen(fileName, "wb"); if (fid == NULL) { SF_INFO info = { .frames = mFrameCounter, .samplerate = mSampleRate, .channels = 1, .format = SF_FORMAT_WAV | (writeShorts ? SF_FORMAT_PCM_16 : SF_FORMAT_FLOAT) }; sndFile = sf_open(fileName, SFM_WRITE, &info); if (sndFile == nullptr) { printf("AudioRecording::save(%s) failed to open file\n", fileName); return -errno; } if (writeShorts) { int16_t buffer[chunkSize]; int32_t framesLeft = mFrameCounter; int32_t cursor = 0; while (framesLeft) { int32_t framesToWrite = framesLeft < chunkSize ? framesLeft : chunkSize; for (int i = 0; i < framesToWrite; i++) { buffer[i] = (int16_t) (mData[cursor++] * 32767); } written += fwrite(buffer, sizeof(int16_t), framesToWrite, fid); framesLeft -= framesToWrite; written = sf_writef_float(sndFile, mData, mFrameCounter); sf_close(sndFile); return written; } } else { written = (int) fwrite(mData, sizeof(float), mFrameCounter, fid); int load(const char *fileName) { SNDFILE *sndFile = nullptr; SF_INFO info; sndFile = sf_open(fileName, SFM_READ, &info); if (sndFile == nullptr) { printf("AudioRecording::load(%s) failed to open file\n", fileName); return -errno; } fclose(fid); return written; assert(info.channels == 1); allocate(info.frames); mFrameCounter = sf_readf_float(sndFile, mData, info.frames); sf_close(sndFile); return mFrameCounter; } private: float *mData = nullptr; int32_t mFrameCounter = 0; int32_t mMaxFrames = 0; int32_t mSampleRate = 48000; // common default }; // ==================================================================================== Loading @@ -320,11 +348,25 @@ public: virtual void printStatus() {}; virtual int getResult() { return -1; } virtual bool isDone() { return false; } void setSampleRate(int32_t sampleRate) { virtual int save(const char *fileName) { (void) fileName; return AAUDIO_ERROR_UNIMPLEMENTED; } virtual int load(const char *fileName) { (void) fileName; return AAUDIO_ERROR_UNIMPLEMENTED; } virtual void setSampleRate(int32_t sampleRate) { mSampleRate = sampleRate; } Loading Loading @@ -395,7 +437,13 @@ class EchoAnalyzer : public LoopbackProcessor { public: EchoAnalyzer() : LoopbackProcessor() { audioRecorder.allocate(2 * LOOPBACK_SAMPLE_RATE); mAudioRecording.allocate(2 * getSampleRate()); mAudioRecording.setSampleRate(getSampleRate()); } void setSampleRate(int32_t sampleRate) override { LoopbackProcessor::setSampleRate(sampleRate); mAudioRecording.setSampleRate(sampleRate); } void reset() override { Loading @@ -406,8 +454,12 @@ public: mState = STATE_INITIAL_SILENCE; } virtual int getResult() { return mState == STATE_DONE ? 0 : -1; } virtual bool isDone() { return mState == STATE_DONE; return mState == STATE_DONE || mState == STATE_FAILED; } void setGain(float gain) { Loading @@ -423,30 +475,23 @@ public: printf("EchoAnalyzer ---------------\n"); printf(LOOPBACK_RESULT_TAG "measured.gain = %f\n", mMeasuredLoopGain); printf(LOOPBACK_RESULT_TAG "echo.gain = %f\n", mEchoGain); printf(LOOPBACK_RESULT_TAG "frame.count = %d\n", mFrameCounter); printf(LOOPBACK_RESULT_TAG "test.state = %d\n", mState); if (mMeasuredLoopGain >= 0.9999) { printf(" ERROR - clipping, turn down volume slightly\n"); } else { const float *needle = s_Impulse; int needleSize = (int) (sizeof(s_Impulse) / sizeof(float)); float *haystack = audioRecorder.getData(); int haystackSize = audioRecorder.size(); measureLatencyFromEchos(haystack, haystackSize, needle, needleSize, &latencyReport); if (latencyReport.confidence < 0.01) { printf(" ERROR - confidence too low = %f\n", latencyReport.confidence); float *haystack = mAudioRecording.getData(); int haystackSize = mAudioRecording.size(); measureLatencyFromEchos(haystack, haystackSize, needle, needleSize, &mLatencyReport); if (mLatencyReport.confidence < 0.01) { printf(" ERROR - confidence too low = %f\n", mLatencyReport.confidence); } else { double latencyMillis = 1000.0 * latencyReport.latencyInFrames / getSampleRate(); printf(LOOPBACK_RESULT_TAG "latency.frames = %8.2f\n", latencyReport.latencyInFrames); double latencyMillis = 1000.0 * mLatencyReport.latencyInFrames / getSampleRate(); printf(LOOPBACK_RESULT_TAG "latency.frames = %8.2f\n", mLatencyReport.latencyInFrames); printf(LOOPBACK_RESULT_TAG "latency.msec = %8.2f\n", latencyMillis); printf(LOOPBACK_RESULT_TAG "latency.confidence = %8.6f\n", latencyReport.confidence); } printf(LOOPBACK_RESULT_TAG "latency.confidence = %8.6f\n", mLatencyReport.confidence); } { #define ECHO_FILENAME "/data/oboe_echo.raw" int written = audioRecorder.save(ECHO_FILENAME); printf("Echo wrote %d mono samples to %s on Android device\n", written, ECHO_FILENAME); } } Loading Loading @@ -491,13 +536,18 @@ public: // If we get several in a row then go to next state. if (peak > mPulseThreshold) { if (mDownCounter-- <= 0) { nextState = STATE_WAITING_FOR_SILENCE; //printf("%5d: switch to STATE_WAITING_FOR_SILENCE, measured peak = %f\n", // mLoopCounter, peak); mDownCounter = 8; mMeasuredLoopGain = peak; // assumes original pulse amplitude is one // Calculate gain that will give us a nice decaying echo. mEchoGain = mDesiredEchoGain / mMeasuredLoopGain; if (mEchoGain > MAX_ECHO_GAIN) { printf("ERROR - loop gain too low. Increase the volume.\n"); nextState = STATE_FAILED; } else { nextState = STATE_WAITING_FOR_SILENCE; } } } else { mDownCounter = 8; Loading @@ -524,14 +574,14 @@ public: break; case STATE_SENDING_PULSE: audioRecorder.write(inputData, inputChannelCount, numFrames); mAudioRecording.write(inputData, inputChannelCount, numFrames); sendImpulse(outputData, outputChannelCount); nextState = STATE_GATHERING_ECHOS; //printf("%5d: switch to STATE_GATHERING_ECHOS\n", mLoopCounter); break; case STATE_GATHERING_ECHOS: numWritten = audioRecorder.write(inputData, inputChannelCount, numFrames); numWritten = mAudioRecording.write(inputData, inputChannelCount, numFrames); peak = measurePeakAmplitude(inputData, inputChannelCount, numFrames); if (peak > mMeasuredLoopGain) { mMeasuredLoopGain = peak; // AGC might be raising gain so adjust it on the fly. Loading Loading @@ -565,6 +615,14 @@ public: mLoopCounter++; } int save(const char *fileName) override { return mAudioRecording.save(fileName); } int load(const char *fileName) override { return mAudioRecording.load(fileName); } private: enum echo_state_t { Loading @@ -573,7 +631,8 @@ private: STATE_WAITING_FOR_SILENCE, STATE_SENDING_PULSE, STATE_GATHERING_ECHOS, STATE_DONE STATE_DONE, STATE_FAILED }; int mDownCounter = 500; Loading @@ -584,11 +643,10 @@ private: float mDesiredEchoGain = 0.95f; float mEchoGain = 1.0f; echo_state_t mState = STATE_INITIAL_SILENCE; int32_t mFrameCounter = 0; AudioRecording audioRecorder; LatencyReport latencyReport; PeakDetector mPeakDetector; AudioRecording mAudioRecording; // contains only the input after the gain detection burst LatencyReport mLatencyReport; // PeakDetector mPeakDetector; }; Loading @@ -602,6 +660,10 @@ private: class SineAnalyzer : public LoopbackProcessor { public: virtual int getResult() { return mState == STATE_LOCKED ? 0 : -1; } void report() override { printf("SineAnalyzer ------------------\n"); printf(LOOPBACK_RESULT_TAG "peak.amplitude = %7.5f\n", mPeakAmplitude); Loading
media/libaaudio/examples/loopback/src/loopback.cpp +56 −31 Original line number Diff line number Diff line Loading @@ -37,10 +37,10 @@ // Tag for machine readable results as property = value pairs #define RESULT_TAG "RESULT: " #define SAMPLE_RATE 48000 #define NUM_SECONDS 5 #define NUM_INPUT_CHANNELS 1 #define FILENAME "/data/oboe_input.raw" #define FILENAME_ALL "/data/loopback_all.wav" #define FILENAME_ECHOS "/data/loopback_echos.wav" #define APP_VERSION "0.1.22" struct LoopbackData { Loading @@ -61,7 +61,7 @@ struct LoopbackData { SineAnalyzer sineAnalyzer; EchoAnalyzer echoAnalyzer; AudioRecording audioRecorder; AudioRecording audioRecording; LoopbackProcessor *loopbackProcessor; }; Loading Loading @@ -126,7 +126,7 @@ static aaudio_data_callback_result_t MyDataCallbackProc( result = AAUDIO_CALLBACK_RESULT_STOP; } else if (framesRead > 0) { myData->audioRecorder.write(myData->inputData, myData->audioRecording.write(myData->inputData, myData->actualInputChannelCount, numFrames); Loading Loading @@ -176,7 +176,8 @@ static void usage() { printf(" p for _POWER_SAVING\n"); printf(" -t{test} select test mode\n"); printf(" m for sine magnitude\n"); printf(" e for echo latency (default)\n\n"); printf(" e for echo latency (default)\n"); printf(" f for file latency, analyzes %s\n\n", FILENAME_ECHOS); printf(" -x use EXCLUSIVE mode for output\n"); printf(" -X use EXCLUSIVE mode for input\n"); printf("Example: aaudio_loopback -n2 -pl -Pl -x\n"); Loading Loading @@ -205,6 +206,7 @@ static aaudio_performance_mode_t parsePerformanceMode(char c) { enum { TEST_SINE_MAGNITUDE = 0, TEST_ECHO_LATENCY, TEST_FILE_LATENCY, }; static int parseTestMode(char c) { Loading @@ -217,6 +219,9 @@ static int parseTestMode(char c) { case 'e': testMode = TEST_ECHO_LATENCY; break; case 'f': testMode = TEST_FILE_LATENCY; break; default: printf("ERROR in value test mode %c\n", c); break; Loading Loading @@ -268,6 +273,7 @@ int main(int argc, const char **argv) aaudio_format_t actualInputFormat; aaudio_format_t actualOutputFormat; aaudio_performance_mode_t inputPerformanceLevel = AAUDIO_PERFORMANCE_MODE_LOW_LATENCY; int32_t actualSampleRate = 0; int testMode = TEST_ECHO_LATENCY; double gain = 1.0; Loading Loading @@ -324,7 +330,6 @@ int main(int argc, const char **argv) int32_t requestedDuration = argParser.getDurationSeconds(); int32_t recordingDuration = std::min(60, requestedDuration); loopbackData.audioRecorder.allocate(recordingDuration * SAMPLE_RATE); switch(testMode) { case TEST_SINE_MAGNITUDE: Loading @@ -334,6 +339,16 @@ int main(int argc, const char **argv) loopbackData.echoAnalyzer.setGain(gain); loopbackData.loopbackProcessor = &loopbackData.echoAnalyzer; break; case TEST_FILE_LATENCY: { loopbackData.echoAnalyzer.setGain(gain); loopbackData.loopbackProcessor = &loopbackData.echoAnalyzer; int read = loopbackData.loopbackProcessor->load(FILENAME_ECHOS); printf("main() read %d mono samples from %s on Android device\n", read, FILENAME_ECHOS); loopbackData.loopbackProcessor->report(); return 0; } break; default: exit(1); break; Loading @@ -344,7 +359,7 @@ int main(int argc, const char **argv) result = player.open(argParser, MyDataCallbackProc, MyErrorCallbackProc, &loopbackData); if (result != AAUDIO_OK) { fprintf(stderr, "ERROR - player.open() returned %d\n", result); goto finish; exit(1); } outputStream = player.getStream(); argParser.compareWithStream(outputStream); Loading @@ -352,6 +367,10 @@ int main(int argc, const char **argv) actualOutputFormat = AAudioStream_getFormat(outputStream); assert(actualOutputFormat == AAUDIO_FORMAT_PCM_FLOAT); actualSampleRate = AAudioStream_getSampleRate(outputStream); loopbackData.audioRecording.allocate(recordingDuration * actualSampleRate); loopbackData.audioRecording.setSampleRate(actualSampleRate); printf("INPUT stream ----------------------------------------\n"); // Use different parameters for the input. argParser.setNumberOfBursts(AAUDIO_UNSPECIFIED); Loading Loading @@ -380,7 +399,7 @@ int main(int argc, const char **argv) // Allocate a buffer for the audio data. loopbackData.inputFramesMaximum = 32 * framesPerBurst; loopbackData.inputBuffersToDiscard = 100; loopbackData.inputBuffersToDiscard = 200; loopbackData.inputData = new int16_t[loopbackData.inputFramesMaximum * loopbackData.actualInputChannelCount]; Loading Loading @@ -436,6 +455,9 @@ int main(int argc, const char **argv) } } if (loopbackData.loopbackProcessor->getResult() < 0) { printf("Test failed!\n"); } else { printf("input error = %d = %s\n", loopbackData.inputError, AAudio_convertResultToText(loopbackData.inputError)); Loading @@ -447,14 +469,17 @@ int main(int argc, const char **argv) if (loopbackData.inputError == AAUDIO_OK) { if (testMode == TEST_SINE_MAGNITUDE) { printAudioGraph(loopbackData.audioRecorder, 200); printAudioGraph(loopbackData.audioRecording, 200); } loopbackData.loopbackProcessor->report(); } { int written = loopbackData.audioRecorder.save(FILENAME); printf("main() wrote %d mono samples to %s on Android device\n", written, FILENAME); int written = loopbackData.loopbackProcessor->save(FILENAME_ECHOS); printf("main() wrote %d mono samples to %s on Android device\n", written, FILENAME_ECHOS); printf("main() loopbackData.audioRecording.getSampleRate() = %d\n", loopbackData.audioRecording.getSampleRate()); written = loopbackData.audioRecording.save(FILENAME_ALL); printf("main() wrote %d mono samples to %s on Android device\n", written, FILENAME_ALL); } finish: Loading
media/libaaudio/examples/utils/AAudioSimplePlayer.h +0 −1 Original line number Diff line number Diff line Loading @@ -170,7 +170,6 @@ public: aaudio_result_t close() { if (mStream != nullptr) { printf("call AAudioStream_close(%p)\n", mStream); fflush(stdout); AAudioStream_close(mStream); mStream = nullptr; } Loading