Merge changes I921c81db,If2a22882,Ide7e7642,I5593cd1a,I923e6043, ... am: 1869736253

am: 035dda9bd4

Change-Id: I812b746ae032bf91d50f4cc1f257c45efd050083
gugelfrei
Phil Burk 6 years ago committed by android-build-merger
commit 0cbe83b72c

@ -5,6 +5,7 @@ cc_test {
cflags: ["-Wall", "-Werror"],
shared_libs: ["libaaudio"],
header_libs: ["libaaudio_example_utils"],
pack_relocations: false,
}
cc_test {
@ -14,4 +15,5 @@ cc_test {
cflags: ["-Wall", "-Werror"],
shared_libs: ["libaaudio"],
header_libs: ["libaaudio_example_utils"],
pack_relocations: false,
}

@ -9,4 +9,5 @@ cc_test {
"libaudioutils",
],
header_libs: ["libaaudio_example_utils"],
pack_relocations: false,
}

@ -34,12 +34,123 @@
// Tag for machine readable results as property = value pairs
#define LOOPBACK_RESULT_TAG "RESULT: "
#define LOOPBACK_SAMPLE_RATE 48000
#define MILLIS_PER_SECOND 1000
constexpr int32_t kDefaultSampleRate = 48000;
constexpr int32_t kMillisPerSecond = 1000;
constexpr int32_t kMinLatencyMillis = 4; // arbitrary and very low
constexpr int32_t kMaxLatencyMillis = 400; // arbitrary and generous
constexpr double kMaxEchoGain = 10.0; // based on experiments, otherwise too noisy
constexpr double kMinimumConfidence = 0.5;
#define MAX_ZEROTH_PARTIAL_BINS 40
constexpr double MAX_ECHO_GAIN = 10.0; // based on experiments, otherwise autocorrelation too noisy
static void printAudioScope(float sample) {
const int maxStars = 80; // arbitrary, fits on one line
char c = '*';
if (sample < -1.0) {
sample = -1.0;
c = '$';
} else if (sample > 1.0) {
sample = 1.0;
c = '$';
}
int numSpaces = (int) (((sample + 1.0) * 0.5) * maxStars);
for (int i = 0; i < numSpaces; i++) {
putchar(' ');
}
printf("%c\n", c);
}
/*
FIR filter designed with
http://t-filter.appspot.com
sampling frequency: 48000 Hz
* 0 Hz - 8000 Hz
gain = 1.2
desired ripple = 5 dB
actual ripple = 5.595266169703693 dB
* 12000 Hz - 20000 Hz
gain = 0
desired attenuation = -40 dB
actual attenuation = -37.58691566571914 dB
*/
#define FILTER_TAP_NUM 11
static const float sFilterTaps8000[FILTER_TAP_NUM] = {
-0.05944219353343189f,
-0.07303434839503208f,
-0.037690487672689066f,
0.1870480506596512f,
0.3910337357836833f,
0.5333672385425637f,
0.3910337357836833f,
0.1870480506596512f,
-0.037690487672689066f,
-0.07303434839503208f,
-0.05944219353343189f
};
class LowPassFilter {
public:
/*
* Filter one input sample.
* @return filtered output
*/
float filter(float input) {
float output = 0.0f;
mX[mCursor] = input;
// Index backwards over x.
int xIndex = mCursor + FILTER_TAP_NUM;
// Write twice so we avoid having to wrap in the middle of the convolution.
mX[xIndex] = input;
for (int i = 0; i < FILTER_TAP_NUM; i++) {
output += sFilterTaps8000[i] * mX[xIndex--];
}
if (++mCursor >= FILTER_TAP_NUM) {
mCursor = 0;
}
return output;
}
/**
* @return true if PASSED
*/
bool test() {
// Measure the impulse of the filter at different phases so we exercise
// all the wraparound cases in the FIR.
for (int offset = 0; offset < (FILTER_TAP_NUM * 2); offset++ ) {
// printf("LowPassFilter: cursor = %d\n", mCursor);
// Offset by one each time.
if (filter(0.0f) != 0.0f) {
printf("ERROR: filter should return 0.0 before impulse response\n");
return false;
}
for (int i = 0; i < FILTER_TAP_NUM; i++) {
float output = filter((i == 0) ? 1.0f : 0.0f); // impulse
if (output != sFilterTaps8000[i]) {
printf("ERROR: filter should return impulse response\n");
return false;
}
}
for (int i = 0; i < FILTER_TAP_NUM; i++) {
if (filter(0.0f) != 0.0f) {
printf("ERROR: filter should return 0.0 after impulse response\n");
return false;
}
}
}
return true;
}
private:
float mX[FILTER_TAP_NUM * 2]{}; // twice as big as needed to avoid wrapping
int32_t mCursor = 0;
};
// A narrow impulse seems to have better immunity against over estimating the
// latency due to detecting subharmonics by the auto-correlator.
@ -78,6 +189,12 @@ private:
int64_t mSeed = 99887766;
};
typedef struct LatencyReport_s {
double latencyInFrames;
double confidence;
} LatencyReport;
static double calculateCorrelation(const float *a,
const float *b,
int windowSize)
@ -101,130 +218,75 @@ static double calculateCorrelation(const float *a,
return correlation;
}
static int calculateCorrelations(const float *haystack, int haystackSize,
const float *needle, int needleSize,
float *results, int resultSize)
{
int maxCorrelations = haystackSize - needleSize;
int numCorrelations = std::min(maxCorrelations, resultSize);
for (int ic = 0; ic < numCorrelations; ic++) {
double correlation = calculateCorrelation(&haystack[ic], needle, needleSize);
results[ic] = correlation;
}
return numCorrelations;
}
/*==========================================================================================*/
/**
* Scan until we get a correlation of a single scan that goes over the tolerance level,
* peaks then drops back down.
*/
static double findFirstMatch(const float *haystack, int haystackSize,
const float *needle, int needleSize, double threshold )
{
int ic;
// How many correlations can we calculate?
int numCorrelations = haystackSize - needleSize;
double maxCorrelation = 0.0;
int peakIndex = -1;
double location = -1.0;
const double backThresholdScaler = 0.5;
for (ic = 0; ic < numCorrelations; ic++) {
double correlation = calculateCorrelation(&haystack[ic], needle, needleSize);
if( (correlation > maxCorrelation) ) {
maxCorrelation = correlation;
peakIndex = ic;
}
//printf("PaQa_FindFirstMatch: ic = %4d, correlation = %8f, maxSum = %8f\n",
// ic, correlation, maxSum );
// Are we past what we were looking for?
if((maxCorrelation > threshold) && (correlation < backThresholdScaler * maxCorrelation)) {
location = peakIndex;
break;
}
static int measureLatencyFromEchos(const float *data,
int32_t numFloats,
int32_t sampleRate,
LatencyReport *report) {
// Allocate results array
const int minReasonableLatencyFrames = sampleRate * kMinLatencyMillis / kMillisPerSecond;
const int maxReasonableLatencyFrames = sampleRate * kMaxLatencyMillis / kMillisPerSecond;
int32_t maxCorrelationSize = maxReasonableLatencyFrames * 3;
int numCorrelations = std::min(numFloats, maxCorrelationSize);
float *correlations = new float[numCorrelations]{};
float *harmonicSums = new float[numCorrelations]{};
// Perform sliding auto-correlation.
// Skip first frames to avoid huge peak at zero offset.
for (int i = minReasonableLatencyFrames; i < numCorrelations; i++) {
int32_t remaining = numFloats - i;
float correlation = (float) calculateCorrelation(&data[i], data, remaining);
correlations[i] = correlation;
// printf("correlation[%d] = %f\n", ic, correlation);
}
return location;
}
typedef struct LatencyReport_s {
double latencyInFrames;
double confidence;
} LatencyReport;
// Apply a technique similar to Harmonic Product Spectrum Analysis to find echo fundamental.
// Using first echo instead of the original impulse for a better match.
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,
haystackSize,
needle,
needleSize,
threshold) + 0.5);
// Use first echo as the needle for the other echos because
// it will be more similar.
needle = &haystack[first];
int again = (int) (findFirstMatch(haystack,
haystackSize,
needle,
needleSize,
threshold) + 0.5);
printf("measureLatencyFromEchos: first = %d, again at %d\n", first, again);
first = again;
// Allocate results array
int remaining = haystackSize - first;
const int maxReasonableLatencyFrames = 48000 * 2; // arbitrary but generous value
int numCorrelations = std::min(remaining, maxReasonableLatencyFrames);
float *correlations = new float[numCorrelations];
float *harmonicSums = new float[numCorrelations](); // set to zero
// Generate correlation for every position.
numCorrelations = calculateCorrelations(&haystack[first], remaining,
needle, needleSize,
correlations, numCorrelations);
// Add higher harmonics mapped onto lower harmonics.
// This reinforces the "fundamental" echo.
const int numEchoes = 10;
// Apply a technique similar to Harmonic Product Spectrum Analysis to find echo fundamental.
// Add higher harmonics mapped onto lower harmonics. This reinforces the "fundamental" echo.
const int numEchoes = 8;
for (int partial = 1; partial < numEchoes; partial++) {
for (int i = 0; i < numCorrelations; i++) {
for (int i = minReasonableLatencyFrames; i < numCorrelations; i++) {
harmonicSums[i / partial] += correlations[i] / partial;
}
}
// Find highest peak in correlation array.
float maxCorrelation = 0.0;
float sumOfPeaks = 0.0;
int peakIndex = 0;
const int skip = MAX_ZEROTH_PARTIAL_BINS; // skip low bins
for (int i = skip; i < numCorrelations; i++) {
for (int i = 0; i < numCorrelations; i++) {
if (harmonicSums[i] > maxCorrelation) {
maxCorrelation = harmonicSums[i];
sumOfPeaks += maxCorrelation;
peakIndex = i;
printf("maxCorrelation = %f at %d\n", maxCorrelation, peakIndex);
// printf("maxCorrelation = %f at %d\n", maxCorrelation, peakIndex);
}
}
report->latencyInFrames = peakIndex;
if (sumOfPeaks < 0.0001) {
/*
{
int32_t topPeak = peakIndex * 7 / 2;
for (int i = 0; i < topPeak; i++) {
float sample = harmonicSums[i];
printf("%4d: %7.5f ", i, sample);
printAudioScope(sample);
}
}
*/
// Calculate confidence.
if (maxCorrelation < 0.001) {
report->confidence = 0.0;
} else {
report->confidence = maxCorrelation / sumOfPeaks;
// Compare peak to average value around peak.
int32_t numSamples = std::min(numCorrelations, peakIndex * 2);
if (numSamples <= 0) {
report->confidence = 0.0;
} else {
double sum = 0.0;
for (int i = 0; i < numSamples; i++) {
sum += harmonicSums[i];
}
const double average = sum / numSamples;
const double ratio = average / maxCorrelation; // will be < 1.0
report->confidence = 1.0 - sqrt(ratio);
}
}
delete[] correlations;
@ -320,7 +382,9 @@ public:
}
assert(info.channels == 1);
assert(info.format == SF_FORMAT_FLOAT);
setSampleRate(info.samplerate);
allocate(info.frames);
mFrameCounter = sf_readf_float(sndFile, mData, info.frames);
@ -328,11 +392,49 @@ public:
return mFrameCounter;
}
/**
* Square the samples so they are all positive and so the peaks are emphasized.
*/
void square() {
for (int i = 0; i < mFrameCounter; i++) {
const float sample = mData[i];
mData[i] = sample * sample;
}
}
/**
* Low pass filter the recording using a simple FIR filter.
* Note that the lowpass filter cutoff tracks the sample rate.
* That is OK because the impulse width is a fixed number of samples.
*/
void lowPassFilter() {
for (int i = 0; i < mFrameCounter; i++) {
mData[i] = mLowPassFilter.filter(mData[i]);
}
}
/**
* Remove DC offset using a one-pole one-zero IIR filter.
*/
void dcBlocker() {
const float R = 0.996; // narrow notch at zero Hz
float x1 = 0.0;
float y1 = 0.0;
for (int i = 0; i < mFrameCounter; i++) {
const float x = mData[i];
const float y = x - x1 + (R * y1);
mData[i] = y;
y1 = y;
x1 = x;
}
}
private:
float *mData = nullptr;
int32_t mFrameCounter = 0;
int32_t mMaxFrames = 0;
int32_t mSampleRate = 48000; // common default
float *mData = nullptr;
int32_t mFrameCounter = 0;
int32_t mMaxFrames = 0;
int32_t mSampleRate = kDefaultSampleRate; // common default
LowPassFilter mLowPassFilter;
};
// ====================================================================================
@ -352,8 +454,12 @@ public:
virtual void printStatus() {};
virtual int getResult() {
return -1;
int32_t getResult() {
return mResult;
}
void setResult(int32_t result) {
mResult = result;
}
virtual bool isDone() {
@ -382,7 +488,7 @@ public:
static float measurePeakAmplitude(float *inputData, int inputChannelCount, int numFrames) {
float peak = 0.0f;
for (int i = 0; i < numFrames; i++) {
float pos = fabs(*inputData);
const float pos = fabs(*inputData);
if (pos > peak) {
peak = pos;
}
@ -393,7 +499,8 @@ public:
private:
int32_t mSampleRate = LOOPBACK_SAMPLE_RATE;
int32_t mSampleRate = kDefaultSampleRate;
int32_t mResult = 0;
};
class PeakDetector {
@ -412,24 +519,6 @@ private:
float mPrevious = 0.0f;
};
static void printAudioScope(float sample) {
const int maxStars = 80; // arbitrary, fits on one line
char c = '*';
if (sample < -1.0) {
sample = -1.0;
c = '$';
} else if (sample > 1.0) {
sample = 1.0;
c = '$';
}
int numSpaces = (int) (((sample + 1.0) * 0.5) * maxStars);
for (int i = 0; i < numSpaces; i++) {
putchar(' ');
}
printf("%c\n", c);
}
// ====================================================================================
/**
* Measure latency given a loopback stream data.
@ -450,17 +539,13 @@ public:
}
void reset() override {
mDownCounter = 200;
mDownCounter = getSampleRate() / 2;
mLoopCounter = 0;
mMeasuredLoopGain = 0.0f;
mEchoGain = 1.0f;
mState = STATE_INITIAL_SILENCE;
}
virtual int getResult() {
return mState == STATE_DONE ? 0 : -1;
}
virtual bool isDone() {
return mState == STATE_DONE || mState == STATE_FAILED;
}
@ -473,27 +558,57 @@ public:
return mEchoGain;
}
void report() override {
bool testLowPassFilter() {
LowPassFilter filter;
return filter.test();
}
void report() override {
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 "test.state = %d\n", mState);
if (mMeasuredLoopGain >= 0.9999) {
if (getResult() != 0) {
printf(LOOPBACK_RESULT_TAG "result = %d\n", getResult());
return;
}
// printf("LowPassFilter test %s\n", testLowPassFilter() ? "PASSED" : "FAILED");
printf(LOOPBACK_RESULT_TAG "measured.gain = %8f\n", mMeasuredLoopGain);
printf(LOOPBACK_RESULT_TAG "echo.gain = %8f\n", mEchoGain);
printf(LOOPBACK_RESULT_TAG "test.state = %8d\n", mState);
printf(LOOPBACK_RESULT_TAG "test.state.name = %8s\n", convertStateToText(mState));
if (mState == STATE_WAITING_FOR_SILENCE) {
printf("WARNING - Stuck waiting for silence. Input may be too noisy!\n");
setResult(ERROR_NOISY);
} else if (mMeasuredLoopGain >= 0.9999) {
printf(" ERROR - clipping, turn down volume slightly\n");
setResult(ERROR_CLIPPING);
} else if (mState != STATE_DONE && mState != STATE_GATHERING_ECHOS) {
printf("WARNING - Bad state. Check volume on device.\n");
setResult(ERROR_INVALID_STATE);
} else {
const float *needle = s_Impulse;
int needleSize = (int) (sizeof(s_Impulse) / sizeof(float));
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 * 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", mLatencyReport.confidence);
// Cleanup the signal to improve the auto-correlation.
mAudioRecording.dcBlocker();
mAudioRecording.square();
mAudioRecording.lowPassFilter();
printf("Please wait several seconds for auto-correlation to complete.\n");
measureLatencyFromEchos(mAudioRecording.getData(),
mAudioRecording.size(),
getSampleRate(),
&mLatencyReport);
double latencyMillis = kMillisPerSecond * (double) 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",
mLatencyReport.confidence);
if (mLatencyReport.confidence < kMinimumConfidence) {
printf(" ERROR - confidence too low!\n");
setResult(ERROR_CONFIDENCE);
}
}
}
@ -519,6 +634,11 @@ public:
sendImpulses(outputData, outputChannelCount, kImpulseSizeInFrames);
}
// @return number of frames for a typical block of processing
int32_t getBlockFrames() {
return getSampleRate() / 8;
}
void process(float *inputData, int inputChannelCount,
float *outputData, int outputChannelCount,
int numFrames) override {
@ -527,7 +647,7 @@ public:
int numWritten;
int numSamples;
echo_state_t nextState = mState;
echo_state nextState = mState;
switch (mState) {
case STATE_INITIAL_SILENCE:
@ -536,10 +656,11 @@ public:
for (int i = 0; i < numSamples; i++) {
outputData[i] = 0;
}
if (mDownCounter-- <= 0) {
mDownCounter -= numFrames;
if (mDownCounter <= 0) {
nextState = STATE_MEASURING_GAIN;
//printf("%5d: switch to STATE_MEASURING_GAIN\n", mLoopCounter);
mDownCounter = 8;
mDownCounter = getBlockFrames() * 2;
}
break;
@ -548,14 +669,16 @@ public:
peak = measurePeakAmplitude(inputData, inputChannelCount, numFrames);
// If we get several in a row then go to next state.
if (peak > mPulseThreshold) {
if (mDownCounter-- <= 0) {
mDownCounter -= numFrames;
if (mDownCounter <= 0) {
//printf("%5d: switch to STATE_WAITING_FOR_SILENCE, measured peak = %f\n",
// mLoopCounter, peak);
mDownCounter = 8;
mDownCounter = getBlockFrames();
mMeasuredLoopGain = peak; // assumes original pulse amplitude is one
mSilenceThreshold = peak * 0.1; // scale silence to measured pulse
// Calculate gain that will give us a nice decaying echo.
mEchoGain = mDesiredEchoGain / mMeasuredLoopGain;
if (mEchoGain > MAX_ECHO_GAIN) {
if (mEchoGain > kMaxEchoGain) {
printf("ERROR - loop gain too low. Increase the volume.\n");
nextState = STATE_FAILED;
} else {
@ -563,7 +686,7 @@ public:
}
}
} else if (numFrames > kImpulseSizeInFrames){ // ignore short callbacks
mDownCounter = 8;
mDownCounter = getBlockFrames();
}
break;
@ -576,13 +699,14 @@ public:
peak = measurePeakAmplitude(inputData, inputChannelCount, numFrames);
// If we get several in a row then go to next state.
if (peak < mSilenceThreshold) {
if (mDownCounter-- <= 0) {
mDownCounter -= numFrames;
if (mDownCounter <= 0) {
nextState = STATE_SENDING_PULSE;
//printf("%5d: switch to STATE_SENDING_PULSE\n", mLoopCounter);
mDownCounter = 8;
mDownCounter = getBlockFrames();
}
} else {
mDownCounter = 8;
mDownCounter = getBlockFrames();
}
break;
@ -615,11 +739,11 @@ public:
}
if (numWritten < numFrames) {
nextState = STATE_DONE;
//printf("%5d: switch to STATE_DONE\n", mLoopCounter);
}
break;
case STATE_DONE:
case STATE_FAILED:
default:
break;
}
@ -633,12 +757,23 @@ public:
}
int load(const char *fileName) override {
return mAudioRecording.load(fileName);
int result = mAudioRecording.load(fileName);
setSampleRate(mAudioRecording.getSampleRate());
mState = STATE_DONE;
return result;
}
private:
enum echo_state_t {
enum error_code {
ERROR_OK = 0,
ERROR_NOISY = -99,
ERROR_CLIPPING,
ERROR_CONFIDENCE,
ERROR_INVALID_STATE
};
enum echo_state {
STATE_INITIAL_SILENCE,
STATE_MEASURING_GAIN,
STATE_WAITING_FOR_SILENCE,
@ -648,6 +783,35 @@ private:
STATE_FAILED
};
const char *convertStateToText(echo_state state) {
const char *result = "Unknown";
switch(state) {
case STATE_INITIAL_SILENCE:
result = "INIT";
break;
case STATE_MEASURING_GAIN:
result = "GAIN";
break;
case STATE_WAITING_FOR_SILENCE:
result = "SILENCE";
break;
case STATE_SENDING_PULSE:
result = "PULSE";
break;
case STATE_GATHERING_ECHOS:
result = "ECHOS";
break;
case STATE_DONE:
result = "DONE";
break;
case STATE_FAILED:
result = "FAILED";
break;
}
return result;
}
int32_t mDownCounter = 500;
int32_t mLoopCounter = 0;
int32_t mSampleIndex = 0;
@ -656,7 +820,7 @@ private:
float mMeasuredLoopGain = 0.0f;
float mDesiredEchoGain = 0.95f;
float mEchoGain = 1.0f;
echo_state_t mState = STATE_INITIAL_SILENCE;
echo_state mState = STATE_INITIAL_SILENCE;
AudioRecording mAudioRecording; // contains only the input after the gain detection burst
LatencyReport mLatencyReport;
@ -674,27 +838,38 @@ 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);
printf(LOOPBACK_RESULT_TAG "sine.magnitude = %7.5f\n", mMagnitude);
printf(LOOPBACK_RESULT_TAG "phase.offset = %7.5f\n", mPhaseOffset);
printf(LOOPBACK_RESULT_TAG "ref.phase = %7.5f\n", mPhase);
printf(LOOPBACK_RESULT_TAG "frames.accumulated = %6d\n", mFramesAccumulated);
printf(LOOPBACK_RESULT_TAG "sine.period = %6d\n", mSinePeriod);
printf(LOOPBACK_RESULT_TAG "test.state = %6d\n", mState);
printf(LOOPBACK_RESULT_TAG "frame.count = %6d\n", mFrameCounter);
printf(LOOPBACK_RESULT_TAG "peak.amplitude = %8f\n", mPeakAmplitude);
printf(LOOPBACK_RESULT_TAG "sine.magnitude = %8f\n", mMagnitude);
printf(LOOPBACK_RESULT_TAG "peak.noise = %8f\n", mPeakNoise);
printf(LOOPBACK_RESULT_TAG "rms.noise = %8f\n", mRootMeanSquareNoise);
float amplitudeRatio = mMagnitude / mPeakNoise;
float signalToNoise = amplitudeRatio * amplitudeRatio;
printf(LOOPBACK_RESULT_TAG "signal.to.noise = %8.2f\n", signalToNoise);
float signalToNoiseDB = 10.0 * log(signalToNoise);
printf(LOOPBACK_RESULT_TAG "signal.to.noise.db = %8.2f\n", signalToNoiseDB);
if (signalToNoiseDB < MIN_SNRATIO_DB) {
printf("ERROR - signal to noise ratio is too low! < %d dB. Adjust volume.\n", MIN_SNRATIO_DB);
setResult(ERROR_NOISY);
}
printf(LOOPBACK_RESULT_TAG "frames.accumulated = %8d\n", mFramesAccumulated);
printf(LOOPBACK_RESULT_TAG "sine.period = %8d\n", mSinePeriod);
printf(LOOPBACK_RESULT_TAG "test.state = %8d\n", mState);
printf(LOOPBACK_RESULT_TAG "frame.count = %8d\n", mFrameCounter);
// Did we ever get a lock?
bool gotLock = (mState == STATE_LOCKED) || (mGlitchCount > 0);
if (!gotLock) {
printf("ERROR - failed to lock on reference sine tone\n");
setResult(ERROR_NO_LOCK);
} else {
// Only print if meaningful.
printf(LOOPBACK_RESULT_TAG "glitch.count = %6d\n", mGlitchCount);
printf(LOOPBACK_RESULT_TAG "glitch.count = %8d\n", mGlitchCount);
printf(LOOPBACK_RESULT_TAG "max.glitch = %8f\n", mMaxGlitchDelta);
if (mGlitchCount > 0) {
printf("ERROR - number of glitches > 0\n");
setResult(ERROR_GLITCHES);
}
}
}
@ -732,15 +907,48 @@ public:
}
for (int i = 0; i < numFrames; i++) {
bool sineEnabled = true;
float sample = inputData[i * inputChannelCount];
float sinOut = sinf(mPhase);
switch (mState) {
case STATE_IDLE:
sineEnabled = false;
mDownCounter--;
if (mDownCounter <= 0) {
mState = STATE_MEASURE_NOISE;
mDownCounter = NOISE_FRAME_COUNT;
}
break;
case STATE_MEASURE_NOISE:
sineEnabled = false;
mPeakNoise = std::max(abs(sample), mPeakNoise);
mNoiseSumSquared += sample * sample;
mDownCounter--;
if (mDownCounter <= 0) {
mState = STATE_WAITING_FOR_SIGNAL;
mRootMeanSquareNoise = sqrt(mNoiseSumSquared / NOISE_FRAME_COUNT);
mTolerance = std::max(MIN_TOLERANCE, mPeakNoise * 2.0f);
mPhase = 0.0; // prevent spike at start
}
break;
case STATE_IMMUNE:
mDownCounter--;
if (mDownCounter <= 0) {
mState = STATE_WAITING_FOR_SIGNAL;
}
break;
case STATE_WAITING_FOR_SIGNAL:
if (peak > mThreshold) {
mState = STATE_WAITING_FOR_LOCK;
//printf("%5d: switch to STATE_WAITING_FOR_LOCK\n", mFrameCounter);
resetAccumulator();
}
break;
case STATE_WAITING_FOR_LOCK:
mSinAccumulator += sample * sinOut;
mCosAccumulator += sample * cosf(mPhase);
@ -766,13 +974,14 @@ public:
// printf(" predicted = %f, actual = %f\n", predicted, sample);
float diff = predicted - sample;
if (fabs(diff) > mTolerance) {
float absDiff = fabs(diff);
mMaxGlitchDelta = std::max(mMaxGlitchDelta, absDiff);
if (absDiff > mTolerance) {
mGlitchCount++;
//printf("%5d: Got a glitch # %d, predicted = %f, actual = %f\n",
// mFrameCounter, mGlitchCount, predicted, sample);
mState = STATE_IMMUNE;
//printf("%5d: switch to STATE_IMMUNE\n", mFrameCounter);
mDownCounter = mSinePeriod; // Set duration of IMMUNE state.
mDownCounter = mSinePeriod * PERIODS_IMMUNE;
}
// Track incoming signal and slowly adjust magnitude to account
@ -792,44 +1001,23 @@ public:
} break;
}
float output = 0.0f;
// Output sine wave so we can measure it.
outputData[i * outputChannelCount] = (sinOut * mOutputAmplitude)
+ (mWhiteNoise.nextRandomDouble() * mNoiseAmplitude);
// printf("%5d: sin(%f) = %f, %f\n", i, mPhase, sinOut, mPhaseIncrement);
// advance and wrap phase
mPhase += mPhaseIncrement;
if (mPhase > M_PI) {
mPhase -= (2.0 * M_PI);
if (sineEnabled) {
output = (sinOut * mOutputAmplitude)
+ (mWhiteNoise.nextRandomDouble() * mNoiseAmplitude);
// printf("%5d: sin(%f) = %f, %f\n", i, mPhase, sinOut, mPhaseIncrement);
// advance and wrap phase
mPhase += mPhaseIncrement;
if (mPhase > M_PI) {
mPhase -= (2.0 * M_PI);
}
}
outputData[i * outputChannelCount] = output;
mFrameCounter++;
}
// Do these once per buffer.
switch (mState) {
case STATE_IDLE:
mState = STATE_IMMUNE; // so we can tell when
break;
case STATE_IMMUNE:
mDownCounter -= numFrames;
if (mDownCounter <= 0) {
mState = STATE_WAITING_FOR_SIGNAL;
//printf("%5d: switch to STATE_WAITING_FOR_SIGNAL\n", mFrameCounter);
}
break;
case STATE_WAITING_FOR_SIGNAL:
if (peak > mThreshold) {
mState = STATE_WAITING_FOR_LOCK;
//printf("%5d: switch to STATE_WAITING_FOR_LOCK\n", mFrameCounter);
resetAccumulator();
}
break;
case STATE_WAITING_FOR_LOCK:
case STATE_LOCKED:
break;
mFrameCounter++;
}
}
void resetAccumulator() {
@ -840,18 +1028,31 @@ public:
void reset() override {
mGlitchCount = 0;
mState = STATE_IMMUNE;
mDownCounter = IMMUNE_FRAME_COUNT;
mState = STATE_IDLE;
mDownCounter = IDLE_FRAME_COUNT;
mPhaseIncrement = 2.0 * M_PI / mSinePeriod;
printf("phaseInc = %f for period %d\n", mPhaseIncrement, mSinePeriod);
resetAccumulator();
mProcessCount = 0;
mPeakNoise = 0.0f;
mNoiseSumSquared = 0.0;
mRootMeanSquareNoise = 0.0;
mPhase = 0.0f;
mMaxGlitchDelta = 0.0;
}
private:
enum error_code {
OK,
ERROR_NO_LOCK = -80,
ERROR_GLITCHES,
ERROR_NOISY
};
enum sine_state_t {
STATE_IDLE,
STATE_MEASURE_NOISE,
STATE_IMMUNE,
STATE_WAITING_FOR_SIGNAL,
STATE_WAITING_FOR_LOCK,
@ -859,10 +1060,16 @@ private:
};
enum constants {
IMMUNE_FRAME_COUNT = 48 * 500,
PERIODS_NEEDED_FOR_LOCK = 8
// Arbitrary durations, assuming 48000 Hz
IDLE_FRAME_COUNT = 48 * 100,
NOISE_FRAME_COUNT = 48 * 600,
PERIODS_NEEDED_FOR_LOCK = 8,
PERIODS_IMMUNE = 2,
MIN_SNRATIO_DB = 65
};
static constexpr float MIN_TOLERANCE = 0.01;
int mSinePeriod = 79;
double mPhaseIncrement = 0.0;
double mPhase = 0.0;
@ -870,25 +1077,29 @@ private:
double mPreviousPhaseOffset = 0.0;
double mMagnitude = 0.0;
double mThreshold = 0.005;
double mTolerance = 0.01;
double mTolerance = MIN_TOLERANCE;
int32_t mFramesAccumulated = 0;
int32_t mProcessCount = 0;
double mSinAccumulator = 0.0;
double mCosAccumulator = 0.0;
float mMaxGlitchDelta = 0.0f;
int32_t mGlitchCount = 0;
double mPeakAmplitude = 0.0;
int mDownCounter = IMMUNE_FRAME_COUNT;
int mDownCounter = IDLE_FRAME_COUNT;
int32_t mFrameCounter = 0;
float mOutputAmplitude = 0.75;
// measure background noise
float mPeakNoise = 0.0f;
double mNoiseSumSquared = 0.0;
double mRootMeanSquareNoise = 0.0;
PseudoRandom mWhiteNoise;
float mNoiseAmplitude = 0.00; // Used to experiment with warbling caused by DRC.
sine_state_t mState = STATE_IDLE;
};
#undef LOOPBACK_SAMPLE_RATE
#undef LOOPBACK_RESULT_TAG
#endif /* AAUDIO_EXAMPLES_LOOPBACK_ANALYSER_H */

@ -35,15 +35,17 @@
#include "AAudioExampleUtils.h"
#include "LoopbackAnalyzer.h"
// V0.4.00 = rectify and low-pass filter the echos, use auto-correlation on entire echo
#define APP_VERSION "0.4.00"
// Tag for machine readable results as property = value pairs
#define RESULT_TAG "RESULT: "
#define NUM_SECONDS 5
#define PERIOD_MILLIS 1000
#define NUM_INPUT_CHANNELS 1
#define FILENAME_ALL "/data/loopback_all.wav"
#define FILENAME_ECHOS "/data/loopback_echos.wav"
#define APP_VERSION "0.2.04"
#define FILENAME_PROCESSED "/data/loopback_processed.wav"
constexpr int kLogPeriodMillis = 1000;
constexpr int kNumInputChannels = 1;
constexpr int kNumCallbacksToDrain = 20;
constexpr int kNumCallbacksToDiscard = 20;
@ -174,7 +176,8 @@ static aaudio_data_callback_result_t MyDataCallbackProc(
int64_t inputFramesWritten = AAudioStream_getFramesWritten(myData->inputStream);
int64_t inputFramesRead = AAudioStream_getFramesRead(myData->inputStream);
int64_t framesAvailable = inputFramesWritten - inputFramesRead;
actualFramesRead = readFormattedData(myData, numFrames);
actualFramesRead = readFormattedData(myData, numFrames); // READ
if (actualFramesRead < 0) {
result = AAUDIO_CALLBACK_RESULT_STOP;
} else {
@ -194,6 +197,7 @@ static aaudio_data_callback_result_t MyDataCallbackProc(
}
myData->insufficientReadCount++;
myData->insufficientReadFrames += numFrames - actualFramesRead; // deficit
// printf("Error insufficientReadCount = %d\n",(int)myData->insufficientReadCount);
}
int32_t numSamples = actualFramesRead * myData->actualInputChannelCount;
@ -336,9 +340,9 @@ int main(int argc, const char **argv)
aaudio_result_t result = AAUDIO_OK;
aaudio_sharing_mode_t requestedInputSharingMode = AAUDIO_SHARING_MODE_SHARED;
int requestedInputChannelCount = NUM_INPUT_CHANNELS;
int requestedInputChannelCount = kNumInputChannels;
aaudio_format_t requestedInputFormat = AAUDIO_FORMAT_UNSPECIFIED;
int32_t requestedInputCapacity = -1;
int32_t requestedInputCapacity = AAUDIO_UNSPECIFIED;
aaudio_performance_mode_t inputPerformanceLevel = AAUDIO_PERFORMANCE_MODE_LOW_LATENCY;
int32_t outputFramesPerBurst = 0;
@ -356,6 +360,9 @@ int main(int argc, const char **argv)
printf("%s - Audio loopback using AAudio V" APP_VERSION "\n", argv[0]);
// Use LOW_LATENCY as the default to match input default.
argParser.setPerformanceMode(AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);
for (int i = 1; i < argc; i++) {
const char *arg = argv[i];
if (argParser.parseArg(arg)) {
@ -404,7 +411,7 @@ int main(int argc, const char **argv)
}
int32_t requestedDuration = argParser.getDurationSeconds();
int32_t requestedDurationMillis = requestedDuration * MILLIS_PER_SECOND;
int32_t requestedDurationMillis = requestedDuration * kMillisPerSecond;
int32_t timeMillis = 0;
int32_t recordingDuration = std::min(60 * 5, requestedDuration);
@ -421,9 +428,11 @@ int main(int argc, const char **argv)
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);
printf("main() read %d mono samples from %s on Android device, rate = %d\n",
read, FILENAME_ECHOS,
loopbackData.loopbackProcessor->getSampleRate());
loopbackData.loopbackProcessor->report();
return 0;
goto report_result;
}
break;
default:
@ -459,15 +468,10 @@ int main(int argc, const char **argv)
argParser.setPerformanceMode(inputPerformanceLevel);
argParser.setChannelCount(requestedInputChannelCount);
argParser.setSharingMode(requestedInputSharingMode);
// Make sure the input buffer has plenty of capacity.
// Extra capacity on input should not increase latency if we keep it drained.
int32_t inputBufferCapacity = requestedInputCapacity;
if (inputBufferCapacity < 0) {
int32_t outputBufferCapacity = AAudioStream_getBufferCapacityInFrames(outputStream);
inputBufferCapacity = 2 * outputBufferCapacity;
if (requestedInputCapacity != AAUDIO_UNSPECIFIED) {
printf("Warning! If you set input capacity then maybe no FAST track on Legacy path!\n");
}
argParser.setBufferCapacity(inputBufferCapacity);
argParser.setBufferCapacity(requestedInputCapacity);
result = recorder.open(argParser);
if (result != AAUDIO_OK) {
@ -517,15 +521,11 @@ int main(int argc, const char **argv)
// Start OUTPUT first so INPUT does not overflow.
result = player.start();
if (result != AAUDIO_OK) {
printf("ERROR - AAudioStream_requestStart(output) returned %d = %s\n",
result, AAudio_convertResultToText(result));
goto finish;
}
result = recorder.start();
if (result != AAUDIO_OK) {
printf("ERROR - AAudioStream_requestStart(input) returned %d = %s\n",
result, AAudio_convertResultToText(result));
goto finish;
}
@ -568,7 +568,7 @@ int main(int argc, const char **argv)
AAudioStream_getXRunCount(outputStream)
);
}
int32_t periodMillis = (timeMillis < 2000) ? PERIOD_MILLIS / 4 : PERIOD_MILLIS;
int32_t periodMillis = (timeMillis < 2000) ? kLogPeriodMillis / 4 : kLogPeriodMillis;
usleep(periodMillis * 1000);
timeMillis += periodMillis;
}
@ -590,15 +590,22 @@ int main(int argc, const char **argv)
printf("input error = %d = %s\n",
loopbackData.inputError, AAudio_convertResultToText(loopbackData.inputError));
written = loopbackData.loopbackProcessor->save(FILENAME_ECHOS);
if (written > 0) {
printf("main() wrote %8d mono samples to \"%s\" on Android device\n",
written, FILENAME_ECHOS);
}
written = loopbackData.audioRecording.save(FILENAME_ALL);
if (written > 0) {
printf("main() wrote %8d mono samples to \"%s\" on Android device\n",
written, FILENAME_ALL);
}
if (loopbackData.inputError == AAUDIO_OK) {
if (testMode == TEST_SINE_MAGNITUDE) {
printAudioGraph(loopbackData.audioRecording, 200);
}
// Print again so we don't have to scroll past waveform.
printf("OUTPUT Stream ----------------------------------------\n");
argParser.compareWithStream(outputStream);
printf("INPUT Stream ----------------------------------------\n");
argParser.compareWithStream(inputStream);
loopbackData.loopbackProcessor->report();
}
@ -606,22 +613,28 @@ int main(int argc, const char **argv)
{
int32_t framesRead = AAudioStream_getFramesRead(inputStream);
int32_t framesWritten = AAudioStream_getFramesWritten(inputStream);
const int64_t framesAvailable = framesWritten - framesRead;
printf("Callback Results ---------------------------------------- INPUT\n");
printf(" input overruns = %d\n", AAudioStream_getXRunCount(inputStream));
printf(" input overruns = %8d\n", AAudioStream_getXRunCount(inputStream));
printf(" framesWritten = %8d\n", framesWritten);
printf(" framesRead = %8d\n", framesRead);
printf(" myFramesRead = %8d\n", (int) loopbackData.framesReadTotal);
printf(" written - read = %8d\n", (int) (framesWritten - framesRead));
printf(" written - read = %8d\n", (int) framesAvailable);
printf(" insufficient # = %8d\n", (int) loopbackData.insufficientReadCount);
if (loopbackData.insufficientReadCount > 0) {
printf(" insufficient frames = %8d\n", (int) loopbackData.insufficientReadFrames);
printf(" insuffic. frames = %8d\n", (int) loopbackData.insufficientReadFrames);
}
int32_t actualInputCapacity = AAudioStream_getBufferCapacityInFrames(inputStream);
if (framesAvailable > 2 * actualInputCapacity) {
printf(" WARNING: written - read > 2*capacity !\n");
}
}
{
int32_t framesRead = AAudioStream_getFramesRead(outputStream);
int32_t framesWritten = AAudioStream_getFramesWritten(outputStream);
printf("Callback Results ---------------------------------------- OUTPUT\n");
printf(" output underruns = %d\n", AAudioStream_getXRunCount(outputStream));
printf(" output underruns = %8d\n", AAudioStream_getXRunCount(outputStream));
printf(" myFramesWritten = %8d\n", (int) loopbackData.framesWrittenTotal);
printf(" framesWritten = %8d\n", framesWritten);
printf(" framesRead = %8d\n", framesRead);
@ -629,22 +642,6 @@ int main(int argc, const char **argv)
printf(" max numFrames = %8d\n", (int) loopbackData.maxNumFrames);
}
written = loopbackData.loopbackProcessor->save(FILENAME_ECHOS);
if (written > 0) {
printf("main() wrote %8d mono samples to \"%s\" on Android device\n",
written, FILENAME_ECHOS);
}
written = loopbackData.audioRecording.save(FILENAME_ALL);
if (written > 0) {
printf("main() wrote %8d mono samples to \"%s\" on Android device\n",
written, FILENAME_ALL);
}
if (loopbackData.loopbackProcessor->getResult() < 0) {
printf("ERROR: LOOPBACK PROCESSING FAILED. Maybe because the volume was too low.\n");
result = loopbackData.loopbackProcessor->getResult();
}
if (loopbackData.insufficientReadCount > 3) {
printf("ERROR: LOOPBACK PROCESSING FAILED. insufficientReadCount too high\n");
result = AAUDIO_ERROR_UNAVAILABLE;
@ -656,14 +653,23 @@ finish:
delete[] loopbackData.inputFloatData;
delete[] loopbackData.inputShortData;
report_result:
written = loopbackData.loopbackProcessor->save(FILENAME_PROCESSED);
if (written > 0) {
printf("main() wrote %8d processed samples to \"%s\" on Android device\n",
written, FILENAME_PROCESSED);
}
if (loopbackData.loopbackProcessor->getResult() < 0) {
result = loopbackData.loopbackProcessor->getResult();
}
printf(RESULT_TAG "result = %d \n", result); // machine readable
printf("result is %s\n", AAudio_convertResultToText(result)); // human readable
if (result != AAUDIO_OK) {
printf("FAILURE\n");
printf("TEST FAILED\n");
return EXIT_FAILURE;
} else {
printf("SUCCESS\n");
printf("TEST PASSED\n");
return EXIT_SUCCESS;
}
}

@ -130,10 +130,12 @@ public:
}
int32_t getBufferCapacity() const {
printf("%s() returns %d\n", __func__, mBufferCapacity);
return mBufferCapacity;
}
void setBufferCapacity(int32_t frames) {
printf("%s(%d)\n", __func__, frames);
mBufferCapacity = frames;
}
@ -185,18 +187,26 @@ public:
mNumberOfBursts = numBursts;
}
int32_t getFramesPerCallback() const {
return mFramesPerCallback;
}
void setFramesPerCallback(int32_t size) {
mFramesPerCallback = size;
}
/**
* Apply these parameters to a stream builder.
* @param builder
*/
void applyParameters(AAudioStreamBuilder *builder) const {
AAudioStreamBuilder_setBufferCapacityInFrames(builder, getBufferCapacity());
AAudioStreamBuilder_setChannelCount(builder, mChannelCount);
AAudioStreamBuilder_setDeviceId(builder, mDeviceId);
AAudioStreamBuilder_setFormat(builder, mFormat);
AAudioStreamBuilder_setFramesPerDataCallback(builder, mFramesPerCallback);
AAudioStreamBuilder_setPerformanceMode(builder, mPerformanceMode);
AAudioStreamBuilder_setSampleRate(builder, mSampleRate);
AAudioStreamBuilder_setBufferCapacityInFrames(builder, mBufferCapacity);
AAudioStreamBuilder_setDeviceId(builder, mDeviceId);
AAudioStreamBuilder_setSharingMode(builder, mSharingMode);
AAudioStreamBuilder_setPerformanceMode(builder, mPerformanceMode);
// Call P functions if supported.
loadFutureFunctions();
@ -232,6 +242,7 @@ private:
aaudio_input_preset_t mInputPreset = AAUDIO_UNSPECIFIED;
int32_t mNumberOfBursts = AAUDIO_UNSPECIFIED;
int32_t mFramesPerCallback = AAUDIO_UNSPECIFIED;
};
class AAudioArgsParser : public AAudioParameters {
@ -297,6 +308,9 @@ public:
case 'y':
setContentType(atoi(&arg[2]));
break;
case 'z':
setFramesPerCallback(atoi(&arg[2]));
break;
default:
unrecognized = true;
break;
@ -350,6 +364,7 @@ public:
printf(" -u{usage} eg. 14 for AAUDIO_USAGE_GAME\n");
printf(" -x to use EXCLUSIVE mode\n");
printf(" -y{contentType} eg. 1 for AAUDIO_CONTENT_TYPE_SPEECH\n");
printf(" -z{callbackSize} or block size, in frames, default = 0\n");
}
static aaudio_performance_mode_t parsePerformanceMode(char c) {
@ -406,6 +421,9 @@ public:
printf(" Capacity: requested = %d, actual = %d\n", getBufferCapacity(),
AAudioStream_getBufferCapacityInFrames(stream));
printf(" CallbackSize: requested = %d, actual = %d\n", getFramesPerCallback(),
AAudioStream_getFramesPerDataCallback(stream));
printf(" SharingMode: requested = %s, actual = %s\n",
getSharingModeText(getSharingMode()),
getSharingModeText(AAudioStream_getSharingMode(stream)));

@ -193,7 +193,7 @@ public:
aaudio_result_t start() {
aaudio_result_t result = AAudioStream_requestStart(mStream);
if (result != AAUDIO_OK) {
printf("ERROR - AAudioStream_requestStart() returned %d %s\n",
printf("ERROR - AAudioStream_requestStart(output) returned %d %s\n",
result, AAudio_convertResultToText(result));
}
return result;
@ -203,7 +203,7 @@ public:
aaudio_result_t stop() {
aaudio_result_t result = AAudioStream_requestStop(mStream);
if (result != AAUDIO_OK) {
printf("ERROR - AAudioStream_requestStop() returned %d %s\n",
printf("ERROR - AAudioStream_requestStop(output) returned %d %s\n",
result, AAudio_convertResultToText(result));
}
int32_t xRunCount = AAudioStream_getXRunCount(mStream);
@ -215,7 +215,7 @@ public:
aaudio_result_t pause() {
aaudio_result_t result = AAudioStream_requestPause(mStream);
if (result != AAUDIO_OK) {
printf("ERROR - AAudioStream_requestPause() returned %d %s\n",
printf("ERROR - AAudioStream_requestPause(output) returned %d %s\n",
result, AAudio_convertResultToText(result));
}
int32_t xRunCount = AAudioStream_getXRunCount(mStream);
@ -223,11 +223,27 @@ public:
return result;
}
aaudio_result_t waitUntilPaused() {
aaudio_result_t result = AAUDIO_OK;
aaudio_stream_state_t currentState = AAudioStream_getState(mStream);
aaudio_stream_state_t inputState = AAUDIO_STREAM_STATE_PAUSING;
while (result == AAUDIO_OK && currentState == AAUDIO_STREAM_STATE_PAUSING) {
result = AAudioStream_waitForStateChange(mStream, inputState,
&currentState, NANOS_PER_SECOND);
inputState = currentState;
}
if (result != AAUDIO_OK) {
return result;
}
return (currentState == AAUDIO_STREAM_STATE_PAUSED)
? AAUDIO_OK : AAUDIO_ERROR_INVALID_STATE;
}
// 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",
printf("ERROR - AAudioStream_requestFlush(output) returned %d %s\n",
result, AAudio_convertResultToText(result));
}
return result;

@ -201,8 +201,10 @@ public:
aaudio_result_t start() {
aaudio_result_t result = AAudioStream_requestStart(mStream);
if (result != AAUDIO_OK) {
fprintf(stderr, "ERROR - AAudioStream_requestStart() returned %d %s\n",
fprintf(stderr, "ERROR - AAudioStream_requestStart(input) returned %d %s\n",
result, AAudio_convertResultToText(result));
fprintf(stderr, " Did you remember to enter: adb root ????\n");
}
return result;
}
@ -211,8 +213,9 @@ public:
aaudio_result_t stop() {
aaudio_result_t result = AAudioStream_requestStop(mStream);
if (result != AAUDIO_OK) {
fprintf(stderr, "ERROR - AAudioStream_requestStop() returned %d %s\n",
fprintf(stderr, "ERROR - AAudioStream_requestStop(input) returned %d %s\n",
result, AAudio_convertResultToText(result));
}
return result;
}
@ -221,7 +224,7 @@ public:
aaudio_result_t pause() {
aaudio_result_t result = AAudioStream_requestPause(mStream);
if (result != AAUDIO_OK) {
fprintf(stderr, "ERROR - AAudioStream_requestPause() returned %d %s\n",
fprintf(stderr, "ERROR - AAudioStream_requestPause(input) returned %d %s\n",
result, AAudio_convertResultToText(result));
}
return result;

@ -4,6 +4,7 @@ cc_test {
cflags: ["-Wall", "-Werror"],
shared_libs: ["libaaudio"],
header_libs: ["libaaudio_example_utils"],
pack_relocations: false,
}
cc_test {
@ -12,4 +13,5 @@ cc_test {
cflags: ["-Wall", "-Werror"],
shared_libs: ["libaaudio"],
header_libs: ["libaaudio_example_utils"],
pack_relocations: false,
}

@ -30,6 +30,8 @@
#include "AAudioSimplePlayer.h"
#include "AAudioArgsParser.h"
#define APP_VERSION "0.1.5"
/**
* Open stream, play some sine waves, then close the stream.
*
@ -109,13 +111,13 @@ static aaudio_result_t testOpenPlayClose(AAudioArgsParser &argParser,
startedAtNanos = getNanoseconds(CLOCK_MONOTONIC);
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);
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"
printf(" waker result = %d, at %6d millis"
", second = %3d, framesWritten = %8d, underruns = %d\n",
ret, result, (int) millis,
result, (int) millis,
second,
(int) AAudioStream_getFramesWritten(player.getStream()),
(int) AAudioStream_getXRunCount(player.getStream()));
@ -138,6 +140,10 @@ static aaudio_result_t testOpenPlayClose(AAudioArgsParser &argParser,
if (result != AAUDIO_OK) {
goto error;
}
result = player.waitUntilPaused();
if (result != AAUDIO_OK) {
goto error;
}
result = player.flush();
}
if (result != AAUDIO_OK) {
@ -219,7 +225,7 @@ int main(int argc, const char **argv)
// 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.4\n", argv[0]);
printf("%s - Play a sine sweep using an AAudio callback V%s\n", argv[0], APP_VERSION);
for (int i = 1; i < argc; i++) {
const char *arg = argv[i];

Loading…
Cancel
Save