summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGlenn Kasten <gkasten@google.com>2017-05-22 16:50:01 -0700
committerGlenn Kasten <gkasten@google.com>2017-05-22 16:52:14 -0700
commitadaa3c6c8bf4acfe1f547d15a18c03a823eebdea (patch)
treec86a1649cdf9ed2032a4912dd4a8f70ec120e3f4
parent9e72ade37314f00216313b2cdcbbf5c16c5252e8 (diff)
downloaddrrickorang-adaa3c6c8bf4acfe1f547d15a18c03a823eebdea.tar.gz
Version 19
Snap to commit b200b95a3aff6a2d5b9c59343c29a806bc00d0f1 Improve auto calibration Keep track of average and RMS energy levels Fix race condition during systrace collection Allow systrace during latency test Update to latest build tools Display simulated load threads Fix memory leak Add privacy policy
-rw-r--r--LoopbackApp/app/build.gradle2
-rw-r--r--LoopbackApp/app/src/main/AndroidManifest.xml4
-rw-r--r--LoopbackApp/app/src/main/java/org/drrickorang/loopback/CaptureHolder.java30
-rw-r--r--LoopbackApp/app/src/main/java/org/drrickorang/loopback/Correlation.java9
-rw-r--r--LoopbackApp/app/src/main/java/org/drrickorang/loopback/LoopbackActivity.java116
-rw-r--r--LoopbackApp/app/src/main/java/org/drrickorang/loopback/LoopbackAudioThread.java6
-rw-r--r--LoopbackApp/app/src/main/java/org/drrickorang/loopback/NativeAudioThread.java4
-rw-r--r--LoopbackApp/app/src/main/java/org/drrickorang/loopback/SoundLevelCalibration.java56
-rw-r--r--LoopbackApp/app/src/main/jni/sles.cpp40
-rw-r--r--LoopbackApp/app/src/main/res/raw/loopback_listener7
-rw-r--r--LoopbackApp/build.gradle2
-rw-r--r--LoopbackApp/gradle/wrapper/gradle-wrapper.properties4
12 files changed, 202 insertions, 78 deletions
diff --git a/LoopbackApp/app/build.gradle b/LoopbackApp/app/build.gradle
index b1e114a..ff69f81 100644
--- a/LoopbackApp/app/build.gradle
+++ b/LoopbackApp/app/build.gradle
@@ -3,7 +3,7 @@ apply plugin: 'com.android.model.application'
model {
android {
compileSdkVersion = 23
- buildToolsVersion = "23.0.1"
+ buildToolsVersion = "25.0"
defaultConfig.with {
applicationId = "org.drrickorang.loopback"
diff --git a/LoopbackApp/app/src/main/AndroidManifest.xml b/LoopbackApp/app/src/main/AndroidManifest.xml
index 508f37f..a057e02 100644
--- a/LoopbackApp/app/src/main/AndroidManifest.xml
+++ b/LoopbackApp/app/src/main/AndroidManifest.xml
@@ -23,8 +23,8 @@
xmlns:android="http://schemas.android.com/apk/res/android"
package="org.drrickorang.loopback"
- android:versionCode="17"
- android:versionName="0.9.74">
+ android:versionCode="19"
+ android:versionName="0.9.75">
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
diff --git a/LoopbackApp/app/src/main/java/org/drrickorang/loopback/CaptureHolder.java b/LoopbackApp/app/src/main/java/org/drrickorang/loopback/CaptureHolder.java
index 774310b..99143f2 100644
--- a/LoopbackApp/app/src/main/java/org/drrickorang/loopback/CaptureHolder.java
+++ b/LoopbackApp/app/src/main/java/org/drrickorang/loopback/CaptureHolder.java
@@ -140,7 +140,14 @@ public class CaptureHolder {
/**
* Send signal to listener script to terminate and stop atrace
**/
- public static void stopLoopbackListenerScript() {
+ public void stopLoopbackListenerScript() {
+ if (mCaptureThread == null || !mCaptureThread.stopLoopbackListenerScript()) {
+ // The capture thread is unable to execute this operation.
+ stopLoopbackListenerScriptImpl();
+ }
+ }
+
+ static void stopLoopbackListenerScriptImpl() {
try {
OutputStream outputStream = new FileOutputStream(SIGNAL_FILE);
outputStream.write(TERMINATE_SIGNAL.getBytes());
@@ -204,6 +211,8 @@ public class CaptureHolder {
private CapturedState mNewCapturedState;
private int mIndexToPlace;
+ private boolean mIsRunning;
+ private boolean mSignalScriptToQuit;
/**
* Create new thread with capture state struct for captured systrace, bugreport and wav
@@ -217,6 +226,9 @@ public class CaptureHolder {
@Override
public void run() {
+ synchronized (this) {
+ mIsRunning = true;
+ }
// Write names of desired captures to signal file, signalling
// the listener script to write systrace and/or bugreport to those files
@@ -287,6 +299,13 @@ public class CaptureHolder {
for (CapturedState cs:mCapturedStates) log += "\n...." + cs;
Log.d(TAG, log);
+ synchronized (this) {
+ if (mSignalScriptToQuit) {
+ CaptureHolder.stopLoopbackListenerScriptImpl();
+ mSignalScriptToQuit = false;
+ }
+ mIsRunning = false;
+ }
Log.d(TAG, "Completed capture thread terminating");
}
@@ -294,5 +313,14 @@ public class CaptureHolder {
public synchronized void updateRank(int rank) {
mNewCapturedState.rank = Math.max(mNewCapturedState.rank, rank);
}
+
+ public synchronized boolean stopLoopbackListenerScript() {
+ if (mIsRunning) {
+ mSignalScriptToQuit = true;
+ return true;
+ } else {
+ return false;
+ }
+ }
}
}
diff --git a/LoopbackApp/app/src/main/java/org/drrickorang/loopback/Correlation.java b/LoopbackApp/app/src/main/java/org/drrickorang/loopback/Correlation.java
index d12cd7b..6c59bd9 100644
--- a/LoopbackApp/app/src/main/java/org/drrickorang/loopback/Correlation.java
+++ b/LoopbackApp/app/src/main/java/org/drrickorang/loopback/Correlation.java
@@ -37,6 +37,8 @@ public class Correlation implements Parcelable {
public double mEstimatedLatencySamples = 0;
public double mEstimatedLatencyMs = 0;
public double mEstimatedLatencyConfidence = 0.0;
+ public double mAverage = 0.0;
+ public double mRms = 0.0;
private double mAmplitudeThreshold = 0.001; // 0.001 = -60 dB noise
@@ -90,6 +92,9 @@ public class Correlation implements Parcelable {
mDataAutocorrelated.length, data.length, minIndex));
log(String.format(" average : %.3f rms: %.3f", average, rms));
+ mAverage = average;
+ mRms = rms;
+
mEstimatedLatencyConfidence = 0.0;
if (average > 0) {
double factor = 3.0;
@@ -199,6 +204,8 @@ public class Correlation implements Parcelable {
bundle.putDouble("mEstimatedLatencySamples", mEstimatedLatencySamples);
bundle.putDouble("mEstimatedLatencyMs", mEstimatedLatencyMs);
bundle.putDouble("mEstimatedLatencyConfidence", mEstimatedLatencyConfidence);
+ bundle.putDouble("mAverage", mAverage);
+ bundle.putDouble("mRms", mRms);
}
dest.writeBundle(bundle);
}
@@ -211,6 +218,8 @@ public class Correlation implements Parcelable {
mEstimatedLatencySamples = bundle.getDouble("mEstimatedLatencySamples");
mEstimatedLatencyMs = bundle.getDouble("mEstimatedLatencyMs");
mEstimatedLatencyConfidence = bundle.getDouble("mEstimatedLatencyConfidence");
+ mAverage = bundle.getDouble("mAverage");
+ mRms = bundle.getDouble("mRms");
}
}
diff --git a/LoopbackApp/app/src/main/java/org/drrickorang/loopback/LoopbackActivity.java b/LoopbackApp/app/src/main/java/org/drrickorang/loopback/LoopbackActivity.java
index 9cfa768..d3acd03 100644
--- a/LoopbackApp/app/src/main/java/org/drrickorang/loopback/LoopbackActivity.java
+++ b/LoopbackApp/app/src/main/java/org/drrickorang/loopback/LoopbackActivity.java
@@ -93,6 +93,8 @@ public class LoopbackActivity extends Activity
private static final int LATENCY_TEST_ENDED = 301;
private static final int BUFFER_TEST_STARTED = 302;
private static final int BUFFER_TEST_ENDED = 303;
+ private static final int CALIBRATION_STARTED = 304;
+ private static final int CALIBRATION_ENDED = 305;
// 0-100 controls compression rate, currently ignore because PNG format is being used
private static final int EXPORTED_IMAGE_QUALITY = 100;
@@ -163,6 +165,7 @@ public class LoopbackActivity extends Activity
private int mPlayerBufferSizeInBytes;
private int mRecorderBufferSizeInBytes;
private int mIgnoreFirstFrames; // TODO: this only applies to native mode
+ private CaptureHolder mCaptureHolder;
// for buffer test
private int[] mGlitchesData;
@@ -280,7 +283,7 @@ public class LoopbackActivity extends Activity
break;
}
if (getApp().isCaptureEnabled()) {
- CaptureHolder.stopLoopbackListenerScript();
+ mCaptureHolder.stopLoopbackListenerScript();
}
stopAudioTestThreads();
if (mIntentRunning && mIntentFileName != null && mIntentFileName.length() > 0) {
@@ -379,11 +382,10 @@ public class LoopbackActivity extends Activity
}
mIntentRunning = false;
-
- }
- if (getApp().isCaptureEnabled()) {
- CaptureHolder.stopLoopbackListenerScript();
- }
+ if (getApp().isCaptureEnabled()) {
+ mCaptureHolder.stopLoopbackListenerScript();
+ }
+ } // mNativeAudioThread != null
refreshSoundLevelBar();
break;
default:
@@ -428,6 +430,16 @@ public class LoopbackActivity extends Activity
LOOPBACK_NATIVE_AUDIO_THREAD_MESSAGE_BUFFER_REC_COMPLETE_ERRORS:
setTransportButtonsState(BUFFER_TEST_ENDED);
break;
+
+ // Sound Calibration started
+ case CALIBRATION_STARTED:
+ setTransportButtonsState(CALIBRATION_STARTED);
+ break;
+
+ // Sound Calibration ended
+ case CALIBRATION_ENDED:
+ setTransportButtonsState(CALIBRATION_ENDED);
+ break;
}
}
};
@@ -803,7 +815,7 @@ public class LoopbackActivity extends Activity
mBufferTestDurationInSeconds = getApp().getBufferTestDuration();
mBufferTestWavePlotDurationInSeconds = getApp().getBufferTestWavePlotDuration();
- CaptureHolder captureHolder = new CaptureHolder(getApp().getNumStateCaptures(),
+ mCaptureHolder = new CaptureHolder(getApp().getNumStateCaptures(),
getFileNamePrefix(), getApp().isCaptureWavSnippetsEnabled(),
getApp().isCaptureSysTraceEnabled(), getApp().isCaptureBugreportEnabled(),
this, mSamplingRate);
@@ -822,20 +834,20 @@ public class LoopbackActivity extends Activity
/ (Constant.BYTES_PER_FRAME * mSamplingRate));
mRecorderBufferPeriod.prepareMemberObjects(
Constant.MAX_RECORDED_LATE_CALLBACKS_PER_SECOND * mBufferTestDurationInSeconds,
- expectedRecorderBufferPeriod, captureHolder);
+ expectedRecorderBufferPeriod, mCaptureHolder);
int expectedPlayerBufferPeriod = Math.round(
(float) (mPlayerBufferSizeInBytes * Constant.MILLIS_PER_SECOND)
/ (Constant.BYTES_PER_FRAME * mSamplingRate));
mPlayerBufferPeriod.prepareMemberObjects(
Constant.MAX_RECORDED_LATE_CALLBACKS_PER_SECOND * mBufferTestDurationInSeconds,
- expectedPlayerBufferPeriod, captureHolder);
+ expectedPlayerBufferPeriod, mCaptureHolder);
mAudioThread = new LoopbackAudioThread(mSamplingRate, mPlayerBufferSizeInBytes,
mRecorderBufferSizeInBytes, micSourceMapped, /* no performance mode */ mRecorderBufferPeriod,
mPlayerBufferPeriod, mTestType, mBufferTestDurationInSeconds,
mBufferTestWavePlotDurationInSeconds, getApplicationContext(),
- mChannelIndex, captureHolder);
+ mChannelIndex, mCaptureHolder);
mAudioThread.setMessageHandler(mMessageHandler);
mAudioThread.mSessionId = sessionId;
mAudioThread.start();
@@ -848,7 +860,7 @@ public class LoopbackActivity extends Activity
mNativeAudioThread = new NativeAudioThread(mSamplingRate, mPlayerBufferSizeInBytes,
mRecorderBufferSizeInBytes, micSourceMapped, performanceModeMapped, mTestType,
mBufferTestDurationInSeconds, mBufferTestWavePlotDurationInSeconds,
- mIgnoreFirstFrames, captureHolder);
+ mIgnoreFirstFrames, mCaptureHolder);
mNativeAudioThread.setMessageHandler(mMessageHandler);
mNativeAudioThread.mSessionId = sessionId;
mNativeAudioThread.start();
@@ -910,6 +922,7 @@ public class LoopbackActivity extends Activity
private void setTransportButtonsState(int state){
Button latencyStart = (Button) findViewById(R.id.buttonStartLatencyTest);
Button bufferStart = (Button) findViewById(R.id.buttonStartBufferTest);
+ Button calibrationStart = (Button) findViewById(R.id.buttonCalibrateSoundLevel);
switch (state) {
case LATENCY_TEST_STARTED:
@@ -919,6 +932,7 @@ public class LoopbackActivity extends Activity
latencyStart.setCompoundDrawablesWithIntrinsicBounds(
R.drawable.ic_stop, 0, 0, 0);
bufferStart.setEnabled(false);
+ calibrationStart.setEnabled(false);
break;
case LATENCY_TEST_ENDED:
@@ -926,6 +940,7 @@ public class LoopbackActivity extends Activity
latencyStart.setCompoundDrawablesWithIntrinsicBounds(
R.drawable.ic_play_arrow, 0, 0, 0);
bufferStart.setEnabled(true);
+ calibrationStart.setEnabled(true);
break;
case BUFFER_TEST_STARTED:
@@ -935,14 +950,39 @@ public class LoopbackActivity extends Activity
bufferStart.setCompoundDrawablesWithIntrinsicBounds(
R.drawable.ic_stop, 0, 0, 0);
latencyStart.setEnabled(false);
+ calibrationStart.setEnabled(false);
break;
case BUFFER_TEST_ENDED:
findViewById(R.id.zoomAndSaveControlPanel).setVisibility(View.VISIBLE);
+ findViewById(R.id.resultSummary).setVisibility(View.VISIBLE);
+ findViewById(R.id.glitchReportPanel).setVisibility(View.VISIBLE);
bufferStart.setCompoundDrawablesWithIntrinsicBounds(
R.drawable.ic_play_arrow, 0, 0, 0);
latencyStart.setEnabled(true);
+ calibrationStart.setEnabled(true);
+ break;
+
+ case CALIBRATION_STARTED:
+ findViewById(R.id.zoomAndSaveControlPanel).setVisibility(View.INVISIBLE);
+ findViewById(R.id.resultSummary).setVisibility(View.INVISIBLE);
+ findViewById(R.id.glitchReportPanel).setVisibility(View.INVISIBLE);
+ bufferStart.setCompoundDrawablesWithIntrinsicBounds(
+ R.drawable.ic_stop, 0, 0, 0);
+ latencyStart.setEnabled(false);
+ bufferStart.setEnabled(false);
+ calibrationStart.setEnabled(false);
+ break;
+
+ case CALIBRATION_ENDED:
+ findViewById(R.id.zoomAndSaveControlPanel).setVisibility(View.VISIBLE);
+ findViewById(R.id.resultSummary).setVisibility(View.VISIBLE);
findViewById(R.id.glitchReportPanel).setVisibility(View.VISIBLE);
+ bufferStart.setCompoundDrawablesWithIntrinsicBounds(
+ R.drawable.ic_play_arrow, 0, 0, 0);
+ latencyStart.setEnabled(true);
+ bufferStart.setEnabled(true);
+ calibrationStart.setEnabled(true);
break;
}
}
@@ -1134,16 +1174,15 @@ public class LoopbackActivity extends Activity
}
public void onButtonCalibrateSoundLevel(final View view) {
- view.setEnabled(false);
+ Message m = Message.obtain();
+ m.what = CALIBRATION_STARTED;
+ mMessageHandler.sendMessage(m);
Runnable onComplete = new Runnable() {
@Override
public void run() {
- view.post(new Runnable() {
- @Override
- public void run() {
- view.setEnabled(true);
- }
- });
+ Message m = Message.obtain();
+ m.what = CALIBRATION_ENDED;
+ mMessageHandler.sendMessage(m);
}
};
doCalibration(onComplete);
@@ -1649,16 +1688,16 @@ public class LoopbackActivity extends Activity
s.append(mTestStartTimeString);
s.append("):\n");
- s.append("SR: " + mSamplingRate + " Hz");
- s.append(" ChannelIndex: " + (mChannelIndex < 0 ? "MONO" : mChannelIndex));
+ s.append("SR: ").append(mSamplingRate).append(" Hz");
+ s.append(" ChannelIndex: ").append(mChannelIndex < 0 ? "MONO" : mChannelIndex);
switch (mAudioThreadType) {
case Constant.AUDIO_THREAD_TYPE_JAVA:
- s.append(" Play Frames: " + playerFrames);
- s.append(" Record Frames: " + recorderFrames);
+ s.append(" Play Frames: " ).append(playerFrames);
+ s.append(" Record Frames: ").append(recorderFrames);
s.append(" Audio: JAVA");
break;
case Constant.AUDIO_THREAD_TYPE_NATIVE:
- s.append(" Frames: " + playerFrames);
+ s.append(" Frames: ").append(playerFrames);
s.append(" Audio: NATIVE");
break;
}
@@ -1666,36 +1705,41 @@ public class LoopbackActivity extends Activity
// mic source
String micSourceName = getApp().getMicSourceString(mMicSource);
if (micSourceName != null) {
- s.append(String.format(" Mic: %s", micSourceName));
+ s.append(" Mic: ").append(micSourceName);
}
// performance mode
String performanceModeName = getApp().getPerformanceModeString(mPerformanceMode);
if (performanceModeName != null) {
- s.append(String.format(" Performance Mode: %s", performanceModeName));
+ s.append(" Performance Mode: ").append(performanceModeName);
}
// sound level at start of test
- s.append(String.format(" Sound Level: %d/%d", mSoundLevel,
- mBarMasterLevel.getMax()));
+ s.append(" Sound Level: ").append(mSoundLevel).append("/").append(mBarMasterLevel.getMax());
+
+ // load threads
+ s.append(" Simulated Load Threads: ").append(getApp().getNumberOfLoadThreads());
// Show short summary of results, round trip latency or number of glitches
if (mTestType == Constant.LOOPBACK_PLUG_AUDIO_THREAD_TEST_TYPE_LATENCY) {
if (mIgnoreFirstFrames > 0) {
- s.append(" First Frames Ignored: " + mIgnoreFirstFrames);
+ s.append(" First Frames Ignored: ").append(mIgnoreFirstFrames);
}
if (mCorrelation.isValid()) {
- mTextViewResultSummary.setText(String.format("Latency: %.2f ms Confidence: %.2f",
- mCorrelation.mEstimatedLatencyMs, mCorrelation.mEstimatedLatencyConfidence));
+ mTextViewResultSummary.setText(String.format("Latency: %.2f ms Confidence: %.2f" +
+ " Average = %.4f RMS = %.4f",
+ mCorrelation.mEstimatedLatencyMs, mCorrelation.mEstimatedLatencyConfidence,
+ mCorrelation.mAverage, mCorrelation.mRms));
}
} else if (mTestType == Constant.LOOPBACK_PLUG_AUDIO_THREAD_TEST_TYPE_BUFFER_PERIOD &&
mGlitchesData != null) {
// show buffer test duration
- s.append("\nBuffer Test Duration: " + mBufferTestDurationInSeconds + " s");
+ s.append("\nBuffer Test Duration: ").append(mBufferTestDurationInSeconds).append(" s");
// show buffer test wave plot duration
- s.append(" Buffer Test Wave Plot Duration: last " +
- mBufferTestWavePlotDurationInSeconds + " s");
+ s.append(" Buffer Test Wave Plot Duration: last ");
+ s.append(mBufferTestWavePlotDurationInSeconds);
+ s.append(" s");
mTextViewResultSummary.setText(getResources().getString(R.string.numGlitches) + " " +
estimateNumberOfGlitches(mGlitchesData));
@@ -1704,7 +1748,7 @@ public class LoopbackActivity extends Activity
}
String info = getApp().getSystemInfo();
- s.append(" " + info);
+ s.append(" ").append(info);
mTextInfo.setText(s.toString());
}
@@ -1922,7 +1966,6 @@ public class LoopbackActivity extends Activity
log("Error closing ParcelFile Descriptor");
}
}
-
}
private StringBuilder getReport() {
@@ -1966,6 +2009,9 @@ public class LoopbackActivity extends Activity
sb.append(String.format("LatencyConfidence = %.2f",
mCorrelation.mEstimatedLatencyConfidence) + endline);
+
+ sb.append(String.format("Average = %.4f", mCorrelation.mAverage) + endline);
+ sb.append(String.format("RMS = %.4f", mCorrelation.mRms) + endline);
break;
case Constant.LOOPBACK_PLUG_AUDIO_THREAD_TEST_TYPE_BUFFER_PERIOD:
sb.append("Buffer Test Duration (s) = " + mBufferTestDurationInSeconds + endline);
diff --git a/LoopbackApp/app/src/main/java/org/drrickorang/loopback/LoopbackAudioThread.java b/LoopbackApp/app/src/main/java/org/drrickorang/loopback/LoopbackAudioThread.java
index ba68780..b4c3b3a 100644
--- a/LoopbackApp/app/src/main/java/org/drrickorang/loopback/LoopbackAudioThread.java
+++ b/LoopbackApp/app/src/main/java/org/drrickorang/loopback/LoopbackAudioThread.java
@@ -27,7 +27,6 @@ import android.util.Log;
import android.os.Handler;
import android.os.Message;
-
/**
* A thread/audio track based audio synth.
*/
@@ -172,9 +171,8 @@ public class LoopbackAudioThread extends Thread {
// read from the pipe and plays it out
int samplesAvailable = mLatencyTestPipe.availableToRead();
if (samplesAvailable > 0) {
- int samplesOfInterest = samplesAvailable;
- if (mMinPlayerBufferSizeSamples < samplesOfInterest)
- samplesOfInterest = mMinPlayerBufferSizeSamples;
+ int samplesOfInterest = Math.min(samplesAvailable,
+ mMinPlayerBufferSizeSamples);
int samplesRead = mLatencyTestPipe.read(audioShortArrayOut, 0,
samplesOfInterest);
diff --git a/LoopbackApp/app/src/main/java/org/drrickorang/loopback/NativeAudioThread.java b/LoopbackApp/app/src/main/java/org/drrickorang/loopback/NativeAudioThread.java
index 5a6735f..95d5899 100644
--- a/LoopbackApp/app/src/main/java/org/drrickorang/loopback/NativeAudioThread.java
+++ b/LoopbackApp/app/src/main/java/org/drrickorang/loopback/NativeAudioThread.java
@@ -292,6 +292,10 @@ public class NativeAudioThread extends Thread {
endDetecting();
}
+ if (mTestType == Constant.LOOPBACK_PLUG_AUDIO_THREAD_TEST_TYPE_LATENCY) {
+ mCaptureHolder.captureState(0);
+ }
+
runDestroy(sles_data);
final int maxTry = 20;
diff --git a/LoopbackApp/app/src/main/java/org/drrickorang/loopback/SoundLevelCalibration.java b/LoopbackApp/app/src/main/java/org/drrickorang/loopback/SoundLevelCalibration.java
index eca9a82..ed70a09 100644
--- a/LoopbackApp/app/src/main/java/org/drrickorang/loopback/SoundLevelCalibration.java
+++ b/LoopbackApp/app/src/main/java/org/drrickorang/loopback/SoundLevelCalibration.java
@@ -25,7 +25,7 @@ import android.util.Log;
class SoundLevelCalibration {
private static final int SECONDS_PER_LEVEL = 1;
private static final int MAX_STEPS = 15; // The maximum number of levels that should be tried
- private static final double CRITICAL_RATIO = 0.4; // Ratio of input over output amplitude at
+ private static final double CRITICAL_RATIO = 0.41; // Ratio of input over output amplitude at
// which the feedback loop neither decays nor
// grows (determined experimentally)
private static final String TAG = "SoundLevelCalibration";
@@ -68,34 +68,39 @@ class SoundLevelCalibration {
// TODO: Allow stopping in the middle of calibration
int calibrate() {
final int maxLevel = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
- int delta = (maxLevel + MAX_STEPS - 1) / MAX_STEPS; // round up
- int level;
- // TODO: Use a better algorithm such as binary search.
- for(level = maxLevel; level >= 0; level -= delta) {
- mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, level, 0);
- if (mChangeListener != null) {
- mChangeListener.go(level);
- }
-
- mNativeAudioThread.start();
- try {
- mNativeAudioThread.join();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- double[] data = mNativeAudioThread.getWaveData();
+ int levelBottom = 0;
+ int levelTop = maxLevel + 1;
+ while(levelTop - levelBottom > 1) {
+ int level = (levelBottom + levelTop) / 2;
+ Log.d(TAG, "setting level to " + level);
+ setVolume(level);
+
+ double amplitude = runAudioThread(mNativeAudioThread);
mNativeAudioThread = new NativeAudioThread(mNativeAudioThread); // generate fresh thread
- double amplitude = averageAmplitude(data);
Log.d(TAG, "calibrate: at sound level " + level + " volume was " + amplitude);
if (amplitude < Constant.SINE_WAVE_AMPLITUDE * CRITICAL_RATIO) {
- Log.d(TAG, "calibrate: chose sound level " + level);
- break;
+ levelBottom = level;
+ } else {
+ levelTop = level;
}
}
+ // At this point, levelBottom has the highest proper value, if there is one (0 otherwise)
+ Log.d(TAG, "Final level: " + levelBottom);
+ setVolume(levelBottom);
+ return levelBottom;
+ }
- // Return the maximum level if we can't find a proper one
- return level != 0 ? level : maxLevel;
+ private double runAudioThread(NativeAudioThread thread) {
+ // runs the native audio thread and returns the average amplitude
+ thread.start();
+ try {
+ thread.join();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ double[] data = thread.getWaveData();
+ return averageAmplitude(data);
}
// TODO: Only gives accurate results for an undistorted sine wave. Check for distortion.
@@ -110,6 +115,13 @@ class SoundLevelCalibration {
return Math.sqrt(2.0 * sumSquare / data.length); // amplitude of the sine wave
}
+ private void setVolume(int level) {
+ mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, level, 0);
+ if (mChangeListener != null) {
+ mChangeListener.go(level);
+ }
+ }
+
void setChangeListener(SoundLevelChangeListener changeListener) {
mChangeListener = changeListener;
}
diff --git a/LoopbackApp/app/src/main/jni/sles.cpp b/LoopbackApp/app/src/main/jni/sles.cpp
index 36a096c..159269b 100644
--- a/LoopbackApp/app/src/main/jni/sles.cpp
+++ b/LoopbackApp/app/src/main/jni/sles.cpp
@@ -45,7 +45,7 @@ int slesInit(sles_data ** ppSles, int samplingRate, int frameCount, int micSourc
memset(pSles, 0, sizeof(sles_data));
- SLES_PRINTF("malloc %zu bytes at %p", sizeof(sles_data), pSles);
+ SLES_PRINTF("pSles malloc %zu bytes at %p", sizeof(sles_data), pSles);
//__android_log_print(ANDROID_LOG_INFO, "sles_jni",
//"malloc %d bytes at %p", sizeof(sles_data), pSles);//Or ANDROID_LOG_INFO, ...
*ppSles = pSles;
@@ -70,6 +70,7 @@ int slesDestroy(sles_data ** ppSles) {
if (*ppSles != NULL)
{
+ SLES_PRINTF("free memory at %p",*ppSles);
free(*ppSles);
*ppSles = 0;
}
@@ -533,9 +534,13 @@ int slesCreateServer(sles_data *pSles, int samplingRate, int frameCount, int mic
// Initialize free buffers
pSles->freeBuffers = (char **) calloc(pSles->freeBufCount + 1, sizeof(char *));
+ SLES_PRINTF(" calloc freeBuffers %zu bytes at %p",pSles->freeBufCount + 1,
+ pSles->freeBuffers);
unsigned j;
for (j = 0; j < pSles->freeBufCount; ++j) {
pSles->freeBuffers[j] = (char *) malloc(pSles->bufSizeInBytes);
+ SLES_PRINTF(" buff%d malloc %zu bytes at %p",j, pSles->bufSizeInBytes,
+ pSles->freeBuffers[j]);
}
pSles->freeFront = 0;
pSles->freeRear = pSles->freeBufCount;
@@ -543,11 +548,13 @@ int slesCreateServer(sles_data *pSles, int samplingRate, int frameCount, int mic
// Initialize record queue
pSles->rxBuffers = (char **) calloc(pSles->rxBufCount + 1, sizeof(char *));
+ SLES_PRINTF(" calloc rxBuffers %zu bytes at %p",pSles->rxBufCount + 1, pSles->rxBuffers);
pSles->rxFront = 0;
pSles->rxRear = 0;
// Initialize play queue
pSles->txBuffers = (char **) calloc(pSles->txBufCount + 1, sizeof(char *));
+ SLES_PRINTF(" calloc txBuffers %zu bytes at %p",pSles->txBufCount + 1, pSles->txBuffers);
pSles->txFront = 0;
pSles->txRear = 0;
@@ -970,8 +977,35 @@ int slesDestroyServer(sles_data *pSles) {
(*(pSles->engineObject))->Destroy(pSles->engineObject);
SLES_PRINTF("slesDestroyServer 7");
-// free(pSles);
-// pSles = NULL;
+ //free buffers
+ if (NULL != pSles->freeBuffers) {
+ for (unsigned j = 0; j < pSles->freeBufCount; ++j) {
+ if (NULL != pSles->freeBuffers[j]) {
+ SLES_PRINTF(" free buff%d at %p",j, pSles->freeBuffers[j]);
+ free (pSles->freeBuffers[j]);
+ }
+ }
+ SLES_PRINTF(" free freeBuffers at %p", pSles->freeBuffers);
+ free(pSles->freeBuffers);
+ } else {
+ SLES_PRINTF(" freeBuffers NULL, no need to free");
+ }
+
+
+ if (NULL != pSles->rxBuffers) {
+ SLES_PRINTF(" free rxBuffers at %p", pSles->rxBuffers);
+ free(pSles->rxBuffers);
+ } else {
+ SLES_PRINTF(" rxBuffers NULL, no need to free");
+ }
+
+ if (NULL != pSles->txBuffers) {
+ SLES_PRINTF(" free txBuffers at %p", pSles->txBuffers);
+ free(pSles->txBuffers);
+ } else {
+ SLES_PRINTF(" txBuffers NULL, no need to free");
+ }
+
status = SLES_SUCCESS;
}
diff --git a/LoopbackApp/app/src/main/res/raw/loopback_listener b/LoopbackApp/app/src/main/res/raw/loopback_listener
index bcbce2c..a29b0c9 100644
--- a/LoopbackApp/app/src/main/res/raw/loopback_listener
+++ b/LoopbackApp/app/src/main/res/raw/loopback_listener
@@ -63,13 +63,6 @@ do
echo "LOOPBACK LISTENER: Finished capture"
- # Check for case that test has ended while capturing state and exit
- if [ -e "$SIGNAL_FILE" ] && [ -s "$SIGNAL_FILE" ] \
- && [ $(cat $SIGNAL_FILE) == $TERMINATE_SIGNAL ]
- then
- exitListener
- fi
-
rm $SIGNAL_FILE
fi
fi
diff --git a/LoopbackApp/build.gradle b/LoopbackApp/build.gradle
index 9d5a7a8..f1cc249 100644
--- a/LoopbackApp/build.gradle
+++ b/LoopbackApp/build.gradle
@@ -4,7 +4,7 @@ buildscript {
jcenter()
}
dependencies {
- classpath 'com.android.tools.build:gradle-experimental:0.6.0'
+ classpath 'com.android.tools.build:gradle-experimental:0.9.0'
}
}
diff --git a/LoopbackApp/gradle/wrapper/gradle-wrapper.properties b/LoopbackApp/gradle/wrapper/gradle-wrapper.properties
index 23b4dd3..dde259e 100644
--- a/LoopbackApp/gradle/wrapper/gradle-wrapper.properties
+++ b/LoopbackApp/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Tue Apr 19 06:51:07 PDT 2016
+#Tue Mar 21 12:29:44 PDT 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip