summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorragomusic <ragomusic@users.noreply.github.com>2015-05-11 12:16:46 -0700
committerragomusic <ragomusic@users.noreply.github.com>2015-05-11 12:16:46 -0700
commit17b7d005bf1f1fe864ce44576e212d3837eb46b8 (patch)
tree43849d279586746c7801a92f55febf7c38d5504d
parent92e3320b85d6771e9cb064e287bbcf71f24f7656 (diff)
parent17d7623d4de4402052e4e94a73343deb1b060adc (diff)
downloaddrrickorang-17b7d005bf1f1fe864ce44576e212d3837eb46b8.tar.gz
Merge pull request #9 from ragomusic/master
Simple Latency Estimation via graphic correlation and UI polishing
-rw-r--r--LoopbackApp/.idea/compiler.xml3
-rw-r--r--LoopbackApp/.idea/encodings.xml5
-rw-r--r--LoopbackApp/.idea/gradle.xml4
-rw-r--r--LoopbackApp/.idea/misc.xml56
-rw-r--r--LoopbackApp/.idea/modules.xml3
-rw-r--r--LoopbackApp/.idea/scopes/scope_settings.xml5
-rw-r--r--LoopbackApp/.idea/vcs.xml3
-rw-r--r--LoopbackApp/LoopbackApp.iml6
-rw-r--r--LoopbackApp/app/app.iml8
-rw-r--r--LoopbackApp/app/src/main/AndroidManifest.xml8
-rw-r--r--LoopbackApp/app/src/main/java/org/drrickorang/loopback/Correlation.java130
-rw-r--r--LoopbackApp/app/src/main/java/org/drrickorang/loopback/LoopbackActivity.java47
-rw-r--r--LoopbackApp/app/src/main/res/layout/main_activity.xml19
-rw-r--r--LoopbackApp/app/src/main/res/layout/settings_activity.xml2
14 files changed, 249 insertions, 50 deletions
diff --git a/LoopbackApp/.idea/compiler.xml b/LoopbackApp/.idea/compiler.xml
index 217af47..9a8b7e5 100644
--- a/LoopbackApp/.idea/compiler.xml
+++ b/LoopbackApp/.idea/compiler.xml
@@ -19,5 +19,4 @@
</profile>
</annotationProcessing>
</component>
-</project>
-
+</project> \ No newline at end of file
diff --git a/LoopbackApp/.idea/encodings.xml b/LoopbackApp/.idea/encodings.xml
deleted file mode 100644
index e206d70..0000000
--- a/LoopbackApp/.idea/encodings.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project version="4">
- <component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false" />
-</project>
-
diff --git a/LoopbackApp/.idea/gradle.xml b/LoopbackApp/.idea/gradle.xml
index 736c7b5..1bbc21d 100644
--- a/LoopbackApp/.idea/gradle.xml
+++ b/LoopbackApp/.idea/gradle.xml
@@ -5,6 +5,7 @@
<GradleProjectSettings>
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
+ <option name="gradleJvm" value="1.7" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
@@ -14,5 +15,4 @@
</GradleProjectSettings>
</option>
</component>
-</project>
-
+</project> \ No newline at end of file
diff --git a/LoopbackApp/.idea/misc.xml b/LoopbackApp/.idea/misc.xml
index 58ff01f..6dcbe11 100644
--- a/LoopbackApp/.idea/misc.xml
+++ b/LoopbackApp/.idea/misc.xml
@@ -3,9 +3,62 @@
<component name="EntryPointsManager">
<entry_points version="2.0" />
</component>
+ <component name="ProjectInspectionProfilesVisibleTreeState">
+ <entry key="Project Default">
+ <profile-state>
+ <expanded-state>
+ <State>
+ <id />
+ </State>
+ <State>
+ <id>Android Lint</id>
+ </State>
+ <State>
+ <id>Finalization issuesJava</id>
+ </State>
+ <State>
+ <id>Groovy</id>
+ </State>
+ <State>
+ <id>ImportsJava</id>
+ </State>
+ <State>
+ <id>JUnit issuesJava</id>
+ </State>
+ <State>
+ <id>Java</id>
+ </State>
+ <State>
+ <id>Memory issuesJava</id>
+ </State>
+ <State>
+ <id>Probable bugsGroovy</id>
+ </State>
+ </expanded-state>
+ <selected-state>
+ <State>
+ <id>Android</id>
+ </State>
+ </selected-state>
+ </profile-state>
+ </entry>
+ </component>
+ <component name="ProjectLevelVcsManager" settingsEditedManually="false">
+ <OptionsSetting value="true" id="Add" />
+ <OptionsSetting value="true" id="Remove" />
+ <OptionsSetting value="true" id="Checkout" />
+ <OptionsSetting value="true" id="Update" />
+ <OptionsSetting value="true" id="Status" />
+ <OptionsSetting value="true" id="Edit" />
+ <ConfirmationsSetting value="0" id="Add" />
+ <ConfirmationsSetting value="0" id="Remove" />
+ </component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" assert-keyword="true" jdk-15="true" project-jdk-name="1.7" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
+ <component name="ProjectType">
+ <option name="id" value="Android" />
+ </component>
<component name="masterDetails">
<states>
<state key="ProjectJDKs.UI">
@@ -22,5 +75,4 @@
</state>
</states>
</component>
-</project>
-
+</project> \ No newline at end of file
diff --git a/LoopbackApp/.idea/modules.xml b/LoopbackApp/.idea/modules.xml
index 0b31235..8f01d90 100644
--- a/LoopbackApp/.idea/modules.xml
+++ b/LoopbackApp/.idea/modules.xml
@@ -6,5 +6,4 @@
<module fileurl="file://$PROJECT_DIR$/app/app.iml" filepath="$PROJECT_DIR$/app/app.iml" />
</modules>
</component>
-</project>
-
+</project> \ No newline at end of file
diff --git a/LoopbackApp/.idea/scopes/scope_settings.xml b/LoopbackApp/.idea/scopes/scope_settings.xml
deleted file mode 100644
index 922003b..0000000
--- a/LoopbackApp/.idea/scopes/scope_settings.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<component name="DependencyValidationManager">
- <state>
- <option name="SKIP_IMPORT_STATEMENTS" value="false" />
- </state>
-</component> \ No newline at end of file
diff --git a/LoopbackApp/.idea/vcs.xml b/LoopbackApp/.idea/vcs.xml
index def6a6a..6564d52 100644
--- a/LoopbackApp/.idea/vcs.xml
+++ b/LoopbackApp/.idea/vcs.xml
@@ -3,5 +3,4 @@
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="" />
</component>
-</project>
-
+</project> \ No newline at end of file
diff --git a/LoopbackApp/LoopbackApp.iml b/LoopbackApp/LoopbackApp.iml
index 0bb6048..e257fad 100644
--- a/LoopbackApp/LoopbackApp.iml
+++ b/LoopbackApp/LoopbackApp.iml
@@ -1,9 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
-<module external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="unspecified" type="JAVA_MODULE" version="4">
+<module external.linked.project.id="LoopbackApp" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="unspecified" type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="java-gradle" name="Java-Gradle">
<configuration>
<option name="BUILD_FOLDER_PATH" value="$MODULE_DIR$/build" />
+ <option name="BUILDABLE" value="false" />
</configuration>
</facet>
</component>
@@ -15,5 +16,4 @@
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
-</module>
-
+</module> \ No newline at end of file
diff --git a/LoopbackApp/app/app.iml b/LoopbackApp/app/app.iml
index 5590b6b..76a0905 100644
--- a/LoopbackApp/app/app.iml
+++ b/LoopbackApp/app/app.iml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<module external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/.." external.system.id="GRADLE" external.system.module.group="LoopbackApp" external.system.module.version="unspecified" type="JAVA_MODULE" version="4">
+<module external.linked.project.id=":app" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/.." external.system.id="GRADLE" external.system.module.group="LoopbackApp" external.system.module.version="unspecified" type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="android-gradle" name="Android-Gradle">
<configuration>
@@ -12,8 +12,9 @@
<option name="SELECTED_TEST_ARTIFACT" value="_android_test_" />
<option name="ASSEMBLE_TASK_NAME" value="assembleDebug" />
<option name="COMPILE_JAVA_TASK_NAME" value="compileDebugSources" />
- <option name="ASSEMBLE_TEST_TASK_NAME" value="assembleDebugAndroidTest" />
<option name="SOURCE_GEN_TASK_NAME" value="generateDebugSources" />
+ <option name="ASSEMBLE_TEST_TASK_NAME" value="assembleDebugAndroidTest" />
+ <option name="COMPILE_JAVA_TEST_TASK_NAME" value="compileDebugAndroidTestSources" />
<option name="TEST_SOURCE_GEN_TASK_NAME" value="generateDebugAndroidTestSources" />
<option name="ALLOW_USER_CONFIGURATION" value="false" />
<option name="MANIFEST_FILE_RELATIVE_PATH" value="/src/main/AndroidManifest.xml" />
@@ -86,5 +87,4 @@
<orderEntry type="jdk" jdkName="Android API 21 Platform" jdkType="Android SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
-</module>
-
+</module> \ No newline at end of file
diff --git a/LoopbackApp/app/src/main/AndroidManifest.xml b/LoopbackApp/app/src/main/AndroidManifest.xml
index a50ca2c..9f6aa7c 100644
--- a/LoopbackApp/app/src/main/AndroidManifest.xml
+++ b/LoopbackApp/app/src/main/AndroidManifest.xml
@@ -22,11 +22,12 @@
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="org.drrickorang.loopback"
+
android:versionCode="5"
android:versionName="0.3">
<uses-sdk
- android:minSdkVersion="8"
+ android:minSdkVersion="11"
android:targetSdkVersion="21"/>
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
@@ -35,10 +36,12 @@
<application
android:label="@string/app_name"
android:icon="@drawable/ic_launcher"
- android:name="LoopbackApplication">
+ android:name="LoopbackApplication"
+ >
<activity
android:name="org.drrickorang.loopback.LoopbackActivity"
android:screenOrientation="sensorPortrait"
+ android:theme="@android:style/Theme.Holo.Light"
android:configChanges="orientation|keyboardHidden|screenLayout">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
@@ -50,6 +53,7 @@
android:name="org.drrickorang.loopback.SettingsActivity"
android:parentActivityName="org.drrickorang.loopback.LoopbackActivity"
android:screenOrientation="sensorPortrait"
+ android:theme="@android:style/Theme.Holo.Light"
android:configChanges="orientation|keyboardHidden|screenLayout">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
diff --git a/LoopbackApp/app/src/main/java/org/drrickorang/loopback/Correlation.java b/LoopbackApp/app/src/main/java/org/drrickorang/loopback/Correlation.java
new file mode 100644
index 0000000..aac3522
--- /dev/null
+++ b/LoopbackApp/app/src/main/java/org/drrickorang/loopback/Correlation.java
@@ -0,0 +1,130 @@
+package org.drrickorang.loopback;
+
+import android.util.Log;
+
+/**
+ * Created by rago on 5/8/15.
+ */
+public class Correlation {
+
+ private int mBlockSize = 4096;
+ private int mSamplingRate = 44100;
+ private double [] mDataDownsampled = new double [mBlockSize];
+ private double [] mDataAutocorrelated = new double[mBlockSize];
+
+ public double mEstimatedLatencySamples = 0;
+ public double mEstimatedLatencyMs = 0;
+
+
+
+ public void init(int blockSize, int samplingRate) {
+ mBlockSize = blockSize;
+ mSamplingRate = samplingRate;
+ }
+
+ public boolean computeCorrelation(double [] data, int samplingRate) {
+ boolean status = false;
+ log("Started Auto Correlation for data with " + data.length + " points");
+ mSamplingRate = samplingRate;
+
+ downsampleData(data, mDataDownsampled);
+
+ //correlation vector
+ autocorrelation(mDataDownsampled, mDataAutocorrelated);
+
+
+ int N = data.length; //all samples available
+ double groupSize = (double) N / mBlockSize; //samples per downsample point.
+
+ double maxValue = 0;
+ int maxIndex = -1;
+
+ double minLatencyMs = 8; //min latency expected. This algorithm should be improved.
+ int minIndex = (int)(0.5 + minLatencyMs * mSamplingRate / (groupSize*1000));
+
+ //find max
+ for(int i=minIndex; i<mDataAutocorrelated.length; i++) {
+ if(mDataAutocorrelated[i] > maxValue) {
+ maxValue = mDataAutocorrelated[i];
+ maxIndex = i;
+ }
+ }
+
+ log(String.format(" Maxvalue %f, max Index : %d/%d (%d) minIndex=%d",maxValue, maxIndex, mDataAutocorrelated.length, data.length, minIndex));
+
+
+
+ mEstimatedLatencySamples = maxIndex*groupSize;
+
+ mEstimatedLatencyMs = mEstimatedLatencySamples *1000/mSamplingRate;
+
+ log(String.format(" latencySamples: %.2f %.2f ms", mEstimatedLatencySamples, mEstimatedLatencyMs));
+
+ status = true;
+
+ return status;
+ }
+
+ private boolean downsampleData(double [] data, double [] dataDownsampled) {
+
+ boolean status = false;
+ // mDataDownsampled = new double[mBlockSize];
+ for (int i=0; i<mBlockSize; i++) {
+ dataDownsampled[i] = 0;
+ }
+
+ int N = data.length; //all samples available
+ double groupSize = (double) N / mBlockSize;
+
+ int currentIndex = 0;
+ double nextGroup = groupSize;
+ for (int i = 0; i<N && currentIndex<mBlockSize; i++) {
+
+ if(i> nextGroup) { //advanced to next group.
+ currentIndex++;
+ nextGroup += groupSize;
+ }
+
+ if (currentIndex>=mBlockSize) {
+ break;
+ }
+ dataDownsampled[currentIndex] += Math.abs(data[i]);
+ }
+
+ status = true;
+
+ return status;
+ }
+
+ private boolean autocorrelation(double [] data, double [] dataOut) {
+ boolean status = false;
+
+ double sumsquared = 0;
+ int N = data.length;
+ for(int i=0; i<N; i++) {
+ double value = data[i];
+ sumsquared += value*value;
+ }
+
+ //dataOut = new double[N];
+
+ if(sumsquared>0) {
+ //correlate (not circular correlation)
+ for (int i = 0; i < N; i++) {
+ dataOut[i] = 0;
+ for (int j = 0; j < N - i; j++) {
+
+ dataOut[i] += data[j] * data[i + j];
+ }
+ dataOut[i] = dataOut[i] / sumsquared;
+ }
+ status = true;
+ }
+
+ return status;
+ }
+
+ private static void log(String msg) {
+ Log.v("Recorder", msg);
+ }
+}
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 698db9d..87dfd21 100644
--- a/LoopbackApp/app/src/main/java/org/drrickorang/loopback/LoopbackActivity.java
+++ b/LoopbackApp/app/src/main/java/org/drrickorang/loopback/LoopbackActivity.java
@@ -72,7 +72,10 @@ public class LoopbackActivity extends Activity {
SeekBar mBarMasterLevel;
TextView mTextInfo;
+ TextView mTextViewCurrentLevel;
+ TextView mTextViewEstimatedLatency;
private double [] mWaveData;
+ private Correlation mCorrelation = new Correlation();
int mSamplingRate;
Toast toast;
@@ -96,6 +99,7 @@ public class LoopbackActivity extends Activity {
if(audioThread != null) {
mWaveData = audioThread.getWaveData();
mSamplingRate = audioThread.mSamplingRate;
+ mCorrelation.computeCorrelation(mWaveData,mSamplingRate);
log("got message java rec complete!!");
refreshPlots();
refreshState();
@@ -118,6 +122,7 @@ public class LoopbackActivity extends Activity {
if(nativeAudioThread != null) {
mWaveData = nativeAudioThread.getWaveData();
mSamplingRate = nativeAudioThread.mSamplingRate;
+ mCorrelation.computeCorrelation(mWaveData, mSamplingRate);
log("got message native rec complete!!");
refreshPlots();
refreshState();
@@ -166,16 +171,20 @@ public class LoopbackActivity extends Activity {
}
@Override
- public void onProgressChanged(SeekBar seekBar, int progress,boolean fromUser) {
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
am.setStreamVolume(AudioManager.STREAM_MUSIC,
progress, 0);
refreshState();
- log("Changed stream volume to: "+progress);
+ log("Changed stream volume to: " + progress);
}
});
mWavePlotView = (WavePlotView) findViewById(R.id.viewWavePlot);
+
+ mTextViewCurrentLevel = (TextView) findViewById(R.id.textViewCurrentLevel);
+
+ mTextViewEstimatedLatency = (TextView) findViewById(R.id.textViewEstimatedLatency);
refreshState();
}
@@ -316,15 +325,10 @@ public class LoopbackActivity extends Activity {
public void onButtonSave(View view) {
//create filename with date
- String date = (String) DateFormat.format("MMddkkmm", System.currentTimeMillis());
+ String date = (String) DateFormat.format("MMddkkmmss", System.currentTimeMillis());
String micSource = getApp().getMicSourceString( getApp().getMicSource());
String fileName = micSource+"_"+date;
- //MIC
- //VERSION?
- //hardware?
-
-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
@@ -332,7 +336,7 @@ public class LoopbackActivity extends Activity {
intent2.addCategory(Intent.CATEGORY_OPENABLE);
intent2.setType("image/png");
- intent2.putExtra(Intent.EXTRA_TITLE,fileName+".png"); //suggested filename
+ intent2.putExtra(Intent.EXTRA_TITLE, fileName + ".png"); //suggested filename
startActivityForResult(intent2, SAVE_TO_PNG_REQUEST);
// browser.
@@ -340,17 +344,11 @@ public class LoopbackActivity extends Activity {
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("audio/wav");
- intent.putExtra(Intent.EXTRA_TITLE,fileName+".wav"); //suggested filename
+ intent.putExtra(Intent.EXTRA_TITLE, fileName + ".wav"); //suggested filename
startActivityForResult(intent, SAVE_TO_WAVE_REQUEST);
-
-
- }
- else {
-
+ } else {
showToast("Saving Wave to: "+fileName+".wav");
-// Toast.makeText(getApplicationContext(), "Saving Wave to: "+fileName,
-// Toast.LENGTH_SHORT).show();
//save to a given uri... local file?
Uri uri = Uri.parse("file://mnt/sdcard/"+fileName+".wav");
@@ -364,7 +362,7 @@ public class LoopbackActivity extends Activity {
@Override
public void onActivityResult(int requestCode, int resultCode,
Intent resultData) {
- log("ActivityResult request: "+requestCode + " result:" + resultCode);
+ log("ActivityResult request: " + requestCode + " result:" + resultCode);
if (requestCode == SAVE_TO_WAVE_REQUEST && resultCode == Activity.RESULT_OK) {
log("got SAVE TO WAV intent back!");
Uri uri = null;
@@ -458,7 +456,10 @@ public class LoopbackActivity extends Activity {
int currentVolume = am.getStreamVolume(AudioManager.STREAM_MUSIC);
mBarMasterLevel.setProgress(currentVolume);
- log("refreshState 2");
+ mTextViewCurrentLevel.setText(String.format("Level: %d/%d",currentVolume,
+ mBarMasterLevel.getMax()));
+
+ log("refreshState 2b");
//get info
int samplingRate = getApp().getSamplingRate();
@@ -487,10 +488,16 @@ public class LoopbackActivity extends Activity {
}
String info = getApp().getSystemInfo();
- s.append(" "+info);
+ s.append(" " + info);
mTextInfo.setText(s.toString());
+ if(mCorrelation.mEstimatedLatencyMs>0.0001) {
+ mTextViewEstimatedLatency.setText(String.format("Latency: %.2f ms", mCorrelation.mEstimatedLatencyMs));
+ } else {
+ mTextViewEstimatedLatency.setText(String.format("Latency: ----"));
+ }
+
}
private static void log(String msg) {
diff --git a/LoopbackApp/app/src/main/res/layout/main_activity.xml b/LoopbackApp/app/src/main/res/layout/main_activity.xml
index 16e058c..70a7093 100644
--- a/LoopbackApp/app/src/main/res/layout/main_activity.xml
+++ b/LoopbackApp/app/src/main/res/layout/main_activity.xml
@@ -111,6 +111,25 @@
</LinearLayout>
<LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <TextView
+ android:layout_width="100dp"
+ android:layout_height="wrap_content"
+ android:text="Current Level"
+ android:id="@+id/textViewCurrentLevel"/>
+
+ <TextView
+ android:layout_width="180dp"
+ android:layout_height="wrap_content"
+ android:text="latency"
+ android:id="@+id/textViewEstimatedLatency"
+ android:textStyle="bold"/>
+ </LinearLayout>
+
+ <LinearLayout
android:layout_marginTop="0mm"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
diff --git a/LoopbackApp/app/src/main/res/layout/settings_activity.xml b/LoopbackApp/app/src/main/res/layout/settings_activity.xml
index b0e5a89..8aa61ca 100644
--- a/LoopbackApp/app/src/main/res/layout/settings_activity.xml
+++ b/LoopbackApp/app/src/main/res/layout/settings_activity.xml
@@ -20,7 +20,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
- android:background="#000000">
+ android:background="#FFFFFF">
<ScrollView
android:layout_width="fill_parent"