diff options
Diffstat (limited to 'LoopbackApp/app/src/main/java')
-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 |
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) { |