Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@
public class TapLatencyAnalyser {
public static final int TYPE_TAP = 0;
float[] mHighPassBuffer;
float[] mFastBuffer;
float[] mSlowBuffer;
float[] mLowThresholdBuffer;
float [] mArmedIndexes;

private float mDroop = 0.995f;
private static final float EDGE_THRESHOLD = 0.01f;
Expand Down Expand Up @@ -56,6 +60,10 @@ public TapLatencyEvent[] analyze(float[] buffer, int offset, int numSamples) {

// Apply envelope follower.
float[] peakBuffer = new float[numSamples];
mFastBuffer = new float[numSamples];
mSlowBuffer = new float[numSamples];
mLowThresholdBuffer = new float[numSamples];
mArmedIndexes = new float[numSamples];
fillPeakBuffer(mHighPassBuffer, 0, numSamples, peakBuffer);
// Look for two attacks.
return scanForEdges(peakBuffer, numSamples);
Expand All @@ -69,6 +77,22 @@ public float[] getFilteredBuffer() {
return mHighPassBuffer;
}


public float[] getFastBuffer() {
return mFastBuffer;
}

public float[] getSlowBuffer() {
return mSlowBuffer;
}

public float[] getLowThresholdBuffer() {
return mLowThresholdBuffer;
}

public float[] getArmedIndexes() {
return mArmedIndexes;
}
// Based on https://en.wikipedia.org/wiki/High-pass_filter
private void highPassFilter(
float[] buffer, int offset, int numSamples, float[] highPassBuffer) {
Expand Down Expand Up @@ -124,6 +148,8 @@ private TapLatencyEvent[] scanForEdges(float[] peakBuffer, int numSamples) {
for (float level : peakBuffer) {
slow = slow + (level - slow) * slowCoefficient; // low pass filter
fast = fast + (level - fast) * fastCoefficient; // low pass filter
mSlowBuffer[sampleIndex] = slow;
mFastBuffer[sampleIndex] = fast;
if (armed && (fast > EDGE_THRESHOLD) && (fast > (2.0 * slow))) {
events.add(new TapLatencyEvent(TYPE_TAP, sampleIndex));
armed = false;
Expand All @@ -132,10 +158,16 @@ private TapLatencyEvent[] scanForEdges(float[] peakBuffer, int numSamples) {
// on the smaller variations that occur after the initial peak.
lowThreshold = fast * LOW_FRACTION;
}
mLowThresholdBuffer[sampleIndex] = lowThreshold;
// Use hysteresis when rearming.
if (fast < lowThreshold) {
armed = true;
}
if (armed) {
mArmedIndexes[sampleIndex] = 1.0f;
} else {
mArmedIndexes[sampleIndex] = 0.0f;
}
sampleIndex++;
}
return events.toArray(new TapLatencyEvent[0]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import static com.mobileer.oboetester.MidiTapTester.NoteListener;

import android.content.pm.PackageManager;
import android.graphics.Color;
import android.media.AudioDeviceInfo;
import android.media.AudioManager;
import android.media.midi.MidiDevice;
Expand Down Expand Up @@ -89,7 +90,18 @@ protected void onCreate(Bundle savedInstanceState) {


// Start a blip test when the waveform view is tapped.
WaveformView mWaveformView = (WaveformView) findViewById(R.id.waveview_audio);
WaveformView mWaveformView = (WaveformView) findViewById(R.id.waveview_audio_original);
WaveformView mFastWaveformView = (WaveformView) findViewById(R.id.waveview_audio_fast_avg);
WaveformView mSlowWaveformView = (WaveformView) findViewById(R.id.waveview_audio_slow_avg);
WaveformView mLowThresholdWaveformView = (WaveformView) findViewById(R.id.waveview_audio_lowThreshold);
WaveformView mArmedWaveformView = (WaveformView) findViewById(R.id.waveview_audio_armed_waveform);

update(R.id.waveview_audio_original, Color.BLUE, Color.argb(128,0, 120, 0), Color.TRANSPARENT);
update(R.id.waveview_audio_fast_avg, Color.argb(255,0, 247, 255), Color.TRANSPARENT, Color.argb(70, 255, 238, 0));
update(R.id.waveview_audio_slow_avg, Color.argb(255, 174, 0, 255), Color.TRANSPARENT, Color.argb(70, 255, 238, 0));
update(R.id.waveview_audio_lowThreshold, Color.argb(255, 255, 132, 0), Color.TRANSPARENT, Color.argb(70, 255, 238, 0));
update(R.id.waveview_audio_armed_waveform, Color.argb(50, 255, 238, 0), Color.TRANSPARENT, Color.RED);

mWaveformView.setOnTouchListener((view, event) -> {
// Do not call view.performClick() because it may trigger a touch sound!
int action = event.getActionMasked();
Expand Down Expand Up @@ -124,6 +136,11 @@ protected void onCreate(Bundle savedInstanceState) {
useNoisePulse(mUseNoisePulseCheckBox.isChecked());
}

private void update(int waveformViewId, int waveColor, int backgroundColor, int cursorColor) {
WaveformView waveformView = (WaveformView) findViewById(waveformViewId);
waveformView.updateTheme(waveColor, backgroundColor, cursorColor);
}

private void updateButtons(boolean running) {
mStartButton.setEnabled(!running);
mStopButton.setEnabled(running);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ public class TapToToneTester {

private final Activity mActivity;
private final WaveformView mWaveformView;
WaveformView mFastWaveformView;
WaveformView mSlowWaveformView;
WaveformView mLowThresholdWaveformView;
WaveformView mArmedWaveformView;
private final TextView mResultView;
private final Spinner mAudioSourceSpinner;

Expand All @@ -55,8 +59,12 @@ public TapToToneTester(Activity activity, String tapInstructions) {
mActivity = activity;
mTapInstructions = tapInstructions;
mResultView = (TextView) activity.findViewById(R.id.resultView);
mWaveformView = (WaveformView) activity.findViewById(R.id.waveview_audio);
mWaveformView = (WaveformView) activity.findViewById(R.id.waveview_audio_original);
mWaveformView.setEnabled(false);
mFastWaveformView = (WaveformView) activity.findViewById(R.id.waveview_audio_fast_avg);
mSlowWaveformView = (WaveformView) activity.findViewById(R.id.waveview_audio_slow_avg);
mLowThresholdWaveformView = (WaveformView) activity.findViewById(R.id.waveview_audio_lowThreshold);
mArmedWaveformView = (WaveformView) activity.findViewById(R.id.waveview_audio_armed_waveform);

float analysisTimeMax = ANALYSIS_TIME_TOTAL + mAnalysisTimeMargin;
mRecorder = new AudioRecordThread(ANALYSIS_SAMPLE_RATE,
Expand Down Expand Up @@ -180,7 +188,7 @@ public void showTestResults(TestResult result) {
for (int i = 0; i < numEdges; i++) {
cursors[i] = result.events[i].sampleIndex;
}
mWaveformView.setCursorData(cursors);
mArmedWaveformView.setCursorData(cursors);
}
// Did we get a good measurement?
if (result.events.length < 2) {
Expand All @@ -203,6 +211,10 @@ public void showTestResults(TestResult result) {
text = String.format(Locale.getDefault(), "tap-to-tone latency = %3d msec\n", latencyMillis);
}
mWaveformView.setSampleData(result.filtered);
mFastWaveformView.setSampleData(mTapLatencyAnalyser.getFastBuffer());
mSlowWaveformView.setSampleData(mTapLatencyAnalyser.getSlowBuffer());
mLowThresholdWaveformView.setSampleData(mTapLatencyAnalyser.getLowThresholdBuffer());
mArmedWaveformView.setSampleData(mTapLatencyAnalyser.getArmedIndexes());
}

if (mMeasurementCount > 0) {
Expand All @@ -217,6 +229,10 @@ public void showTestResults(TestResult result) {
public void run() {
mResultView.setText(postText);
mWaveformView.postInvalidate();
mFastWaveformView.postInvalidate();
mSlowWaveformView.postInvalidate();
mLowThresholdWaveformView.postInvalidate();
mArmedWaveformView.postInvalidate();
}
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,12 @@ private void init() {
mMessagePaint.setTextSize(MESSAGE_TEXT_SIZE);
}

public void updateTheme(int waveColor, int backgroundColor, int cursorColor) {
mWavePaint.setColor(waveColor);
mBackgroundPaint.setColor(backgroundColor);
mCursorPaint.setColor(cursorColor);
}

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
mCurrentWidth = w;
Expand Down
40 changes: 34 additions & 6 deletions apps/OboeTester/app/src/main/res/layout/activity_tap_to_tone.xml
Original file line number Diff line number Diff line change
Expand Up @@ -128,12 +128,40 @@
android:textStyle="bold"
android:text="@string/tap_help" />

<com.mobileer.oboetester.WaveformView
android:id="@+id/waveview_audio"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:minHeight="100dp"
/>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="fill_parent">
<com.mobileer.oboetester.WaveformView
android:id="@+id/waveview_audio_original"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:minHeight="100dp"
/>
<com.mobileer.oboetester.WaveformView
android:id="@+id/waveview_audio_fast_avg"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:minHeight="100dp"
/>
<com.mobileer.oboetester.WaveformView
android:id="@+id/waveview_audio_slow_avg"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:minHeight="100dp"
/>
<com.mobileer.oboetester.WaveformView
android:id="@+id/waveview_audio_lowThreshold"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:minHeight="100dp"
/>
<com.mobileer.oboetester.WaveformView
android:id="@+id/waveview_audio_armed_waveform"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:minHeight="100dp"
/>
</FrameLayout>
</LinearLayout>
</ScrollView>
</LinearLayout>
Loading