diff options
author | ragomusic <ragomusic@users.noreply.github.com> | 2015-05-11 12:16:46 -0700 |
---|---|---|
committer | ragomusic <ragomusic@users.noreply.github.com> | 2015-05-11 12:16:46 -0700 |
commit | 17b7d005bf1f1fe864ce44576e212d3837eb46b8 (patch) | |
tree | 43849d279586746c7801a92f55febf7c38d5504d | |
parent | 92e3320b85d6771e9cb064e287bbcf71f24f7656 (diff) | |
parent | 17d7623d4de4402052e4e94a73343deb1b060adc (diff) | |
download | drrickorang-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.xml | 3 | ||||
-rw-r--r-- | LoopbackApp/.idea/encodings.xml | 5 | ||||
-rw-r--r-- | LoopbackApp/.idea/gradle.xml | 4 | ||||
-rw-r--r-- | LoopbackApp/.idea/misc.xml | 56 | ||||
-rw-r--r-- | LoopbackApp/.idea/modules.xml | 3 | ||||
-rw-r--r-- | LoopbackApp/.idea/scopes/scope_settings.xml | 5 | ||||
-rw-r--r-- | LoopbackApp/.idea/vcs.xml | 3 | ||||
-rw-r--r-- | LoopbackApp/LoopbackApp.iml | 6 | ||||
-rw-r--r-- | LoopbackApp/app/app.iml | 8 | ||||
-rw-r--r-- | LoopbackApp/app/src/main/AndroidManifest.xml | 8 | ||||
-rw-r--r-- | LoopbackApp/app/src/main/java/org/drrickorang/loopback/Correlation.java | 130 | ||||
-rw-r--r-- | LoopbackApp/app/src/main/java/org/drrickorang/loopback/LoopbackActivity.java | 47 | ||||
-rw-r--r-- | LoopbackApp/app/src/main/res/layout/main_activity.xml | 19 | ||||
-rw-r--r-- | LoopbackApp/app/src/main/res/layout/settings_activity.xml | 2 |
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" |