summaryrefslogtreecommitdiff
path: root/LoopbackApp/app/src/main/java
diff options
context:
space:
mode:
Diffstat (limited to 'LoopbackApp/app/src/main/java')
-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
2 files changed, 157 insertions, 20 deletions
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) {