Skip to content

Commit 79a44b2

Browse files
authored
OboeTester: Fix problem with phaseJitter=0 in DataPaths (#1978)
Set phase calculation as invalid when magnitude is low. Some tests has phaseJitter=0. Also some tests had very high phaseJitter and should have failed. So I lower the max allowable jitter.
1 parent c1d4eaf commit 79a44b2

7 files changed

Lines changed: 83 additions & 52 deletions

File tree

apps/OboeTester/app/src/main/cpp/analyzer/BaseSineAnalyzer.h

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ class BaseSineAnalyzer : public LoopbackProcessor {
4545
mScaledTolerance = mMagnitude * getTolerance();
4646
}
4747

48+
/**
49+
*
50+
* @return valid phase or kPhaseInvalid=-999
51+
*/
4852
double getPhaseOffset() {
4953
ALOGD("%s(), mPhaseOffset = %f\n", __func__, mPhaseOffset);
5054
return mPhaseOffset;
@@ -129,7 +133,18 @@ class BaseSineAnalyzer : public LoopbackProcessor {
129133
double cosMean = mCosAccumulator / mFramesAccumulated;
130134
double magnitude = 2.0 * sqrt((sinMean * sinMean) + (cosMean * cosMean));
131135
if (phasePtr != nullptr) {
132-
double phase = atan2(cosMean, sinMean);
136+
double phase;
137+
if (magnitude < kMinValidMagnitude) {
138+
phase = kPhaseInvalid;
139+
ALOGD("%s() mag very low! sinMean = %7.5f, cosMean = %7.5f",
140+
__func__, sinMean, cosMean);
141+
} else {
142+
phase = atan2(cosMean, sinMean);
143+
if (phase == 0.0) {
144+
ALOGD("%s() phase zero! sinMean = %7.5f, cosMean = %7.5f",
145+
__func__, sinMean, cosMean);
146+
}
147+
}
133148
*phasePtr = phase;
134149
}
135150
return magnitude;
@@ -153,9 +168,12 @@ class BaseSineAnalyzer : public LoopbackProcessor {
153168
if (mFramesAccumulated == mSinePeriod) {
154169
const double coefficient = 0.1;
155170
double magnitude = calculateMagnitudePhase(&mPhaseOffset);
156-
ALOGD("%s(), mPhaseOffset = %f\n", __func__, mPhaseOffset);
157-
// One pole averaging filter.
158-
setMagnitude((mMagnitude * (1.0 - coefficient)) + (magnitude * coefficient));
171+
172+
ALOGD("%s(), phaseOffset = %f\n", __func__, mPhaseOffset);
173+
if (mPhaseOffset != kPhaseInvalid) {
174+
// One pole averaging filter.
175+
setMagnitude((mMagnitude * (1.0 - coefficient)) + (magnitude * coefficient));
176+
}
159177
resetAccumulator();
160178
return true;
161179
} else {
@@ -193,9 +211,19 @@ class BaseSineAnalyzer : public LoopbackProcessor {
193211
double mPhaseIncrement = 0.0;
194212
double mOutputPhase = 0.0;
195213
double mOutputAmplitude = 0.75;
214+
// This is the phase offset between the output sine wave and the recorded
215+
// signal at the tuned frequency.
196216
// If this jumps around then we are probably just hearing noise.
217+
// Noise can cause the magnitude to be high but mPhaseOffset will be pretty random.
218+
// If we are tracking a sine wave then mPhaseOffset should be consistent.
197219
double mPhaseOffset = 0.0;
220+
// kPhaseInvalid indicates that the phase measurement cannot be used.
221+
// We were seeing times when a magnitude of zero was causing atan2(s,c) to
222+
// return a phase of zero, which looked valid to Java. This is a way of passing
223+
// an error code back to Java as a single value to avoid race conditions.
224+
static constexpr double kPhaseInvalid = -999.0;
198225
double mMagnitude = 0.0;
226+
static constexpr double kMinValidMagnitude = 2.0 / (1 << 16);
199227
int32_t mFramesAccumulated = 0;
200228
double mSinAccumulator = 0.0;
201229
double mCosAccumulator = 0.0;

apps/OboeTester/app/src/main/cpp/analyzer/DataPathAnalyzer.h

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,13 @@ class DataPathAnalyzer : public BaseSineAnalyzer {
6565

6666
if (transformSample(sample, mOutputPhase)) {
6767
// Analyze magnitude and phase on every period.
68-
double diff = fabs(calculatePhaseError(mPhaseOffset, mPreviousPhaseOffset));
69-
if (diff < mPhaseTolerance) {
70-
mMaxMagnitude = std::max(mMagnitude, mMaxMagnitude);
68+
if (mPhaseOffset != kPhaseInvalid) {
69+
double diff = fabs(calculatePhaseError(mPhaseOffset, mPreviousPhaseOffset));
70+
if (diff < mPhaseTolerance) {
71+
mMaxMagnitude = std::max(mMagnitude, mMaxMagnitude);
72+
}
73+
mPreviousPhaseOffset = mPhaseOffset;
7174
}
72-
mPreviousPhaseOffset = mPhaseOffset;
7375
}
7476
return result;
7577
}

apps/OboeTester/app/src/main/cpp/analyzer/GlitchAnalyzer.h

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -183,17 +183,20 @@ class GlitchAnalyzer : public BaseSineAnalyzer {
183183
mFramesAccumulated++;
184184
// Must be a multiple of the period or the calculation will not be accurate.
185185
if (mFramesAccumulated == mSinePeriod * PERIODS_NEEDED_FOR_LOCK) {
186-
setMagnitude(calculateMagnitudePhase(&mPhaseOffset));
187-
ALOGD("%s() mag = %f, mPhaseOffset = %f",
188-
__func__, mMagnitude, mPhaseOffset);
189-
if (mMagnitude > mThreshold) {
190-
if (fabs(mPhaseOffset) < kMaxPhaseError) {
191-
mState = STATE_LOCKED;
192-
mConsecutiveBadFrames = 0;
186+
double magnitude = calculateMagnitudePhase(&mPhaseOffset);
187+
if (mPhaseOffset != kPhaseInvalid) {
188+
setMagnitude(magnitude);
189+
ALOGD("%s() mag = %f, mPhaseOffset = %f",
190+
__func__, magnitude, mPhaseOffset);
191+
if (mMagnitude > mThreshold) {
192+
if (fabs(mPhaseOffset) < kMaxPhaseError) {
193+
mState = STATE_LOCKED;
194+
mConsecutiveBadFrames = 0;
193195
// ALOGD("%5d: switch to STATE_LOCKED", mFrameCounter);
196+
}
197+
// Adjust mInputPhase to match measured phase
198+
mInputPhase += mPhaseOffset;
194199
}
195-
// Adjust mInputPhase to match measured phase
196-
mInputPhase += mPhaseOffset;
197200
}
198201
resetAccumulator();
199202
}

apps/OboeTester/app/src/main/java/com/mobileer/oboetester/BaseAutoGlitchActivity.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,6 @@ protected void log(String text) {
214214
protected void appendFailedSummary(String text) {
215215
mAutomatedTestRunner.appendFailedSummary(text);
216216
}
217-
218217
protected void appendSummary(String text) {
219218
mAutomatedTestRunner.appendSummary(text);
220219
}

apps/OboeTester/app/src/main/java/com/mobileer/oboetester/TestDataPathsActivity.java

Lines changed: 24 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,13 @@
1919
import static com.mobileer.oboetester.IntentBasedTestSupport.configureStreamsFromBundle;
2020
import static com.mobileer.oboetester.StreamConfiguration.convertChannelMaskToText;
2121

22+
import android.app.Instrumentation;
2223
import android.media.AudioDeviceInfo;
2324
import android.media.AudioManager;
2425
import android.os.Build;
2526
import android.os.Bundle;
2627
import android.util.Log;
28+
import android.view.KeyEvent;
2729
import android.widget.CheckBox;
2830
import android.widget.RadioButton;
2931
import android.widget.RadioGroup;
@@ -88,11 +90,13 @@ public class TestDataPathsActivity extends BaseAutoGlitchActivity {
8890

8991
public static final int DURATION_SECONDS = 3;
9092
private final static double MIN_REQUIRED_MAGNITUDE = 0.001;
91-
private final static double MAX_SINE_FREQUENCY = 1000.0;
93+
private final static int MAX_SINE_FREQUENCY = 1000;
9294
private final static int TYPICAL_SAMPLE_RATE = 48000;
9395
private final static double FRAMES_PER_CYCLE = TYPICAL_SAMPLE_RATE / MAX_SINE_FREQUENCY;
9496
private final static double PHASE_PER_BIN = 2.0 * Math.PI / FRAMES_PER_CYCLE;
95-
private final static double MAX_ALLOWED_JITTER = 2.0 * PHASE_PER_BIN;
97+
private final static double MAX_ALLOWED_JITTER = 0.5 * PHASE_PER_BIN;
98+
// This must match the value of kPhaseInvalid in BaseSineAnalyzer.h
99+
private final static double PHASE_INVALID = -999.0;
96100
private final static String MAGNITUDE_FORMAT = "%7.5f";
97101

98102
// These define the values returned by the Java API deviceInfo.getChannelMasks().
@@ -266,18 +270,20 @@ private void gatherData() {
266270
// Only look at the phase if we have a signal.
267271
if (mMagnitude >= MIN_REQUIRED_MAGNITUDE) {
268272
double phase = getPhaseDataPaths();
269-
// Wait for the analyzer to get a lock on the signal.
270-
// Arbitrary number of phase measurements before we start measuring jitter.
271-
final int kMinPhaseMeasurementsRequired = 4;
272-
if (mPhaseCount >= kMinPhaseMeasurementsRequired) {
273-
double phaseError = Math.abs(calculatePhaseError(phase, mPhase));
274-
// collect average error
275-
mPhaseErrorSum += phaseError;
276-
mPhaseErrorCount++;
277-
Log.d(TAG, String.format(Locale.getDefault(), "phase = %7.4f, mPhase = %7.4f, phaseError = %7.4f, jitter = %7.4f",
278-
phase, mPhase, phaseError, getAveragePhaseError()));
273+
if (phase != PHASE_INVALID) {
274+
// Wait for the analyzer to get a lock on the signal.
275+
// Arbitrary number of phase measurements before we start measuring jitter.
276+
final int kMinPhaseMeasurementsRequired = 4;
277+
if (mPhaseCount >= kMinPhaseMeasurementsRequired) {
278+
double phaseError = Math.abs(calculatePhaseError(phase, mPhase));
279+
// collect average error
280+
mPhaseErrorSum += phaseError;
281+
mPhaseErrorCount++;
282+
Log.d(TAG, String.format(Locale.getDefault(), "phase = %7.4f, mPhase = %7.4f, phaseError = %7.4f, jitter = %7.4f",
283+
phase, mPhase, phaseError, getAveragePhaseError()));
284+
}
285+
mPhase = phase;
279286
}
280-
mPhase = phase;
281287
mPhaseCount++;
282288
}
283289
}
@@ -454,7 +460,8 @@ String getOneLineSummary() {
454460
+ " D=" + actualOutConfig.getDeviceId()
455461
+ ", ch=" + channelText(getOutputChannel(), actualOutConfig.getChannelCount())
456462
+ ", SR=" + actualOutConfig.getSampleRate()
457-
+ ", mag = " + getMagnitudeText(mMaxMagnitude);
463+
+ ", mag = " + getMagnitudeText(mMaxMagnitude)
464+
+ ", jitter = " + getJitterText();
458465
}
459466

460467
@Override
@@ -533,18 +540,14 @@ int convertJavaInChannelMaskToNativeChannelMask(int javaChannelMask) {
533540
}
534541

535542
void logOneLineSummary(TestResult testResult) {
536-
logOneLineSummary(testResult, "");
537-
}
538-
539-
void logOneLineSummary(TestResult testResult, String extra) {
540543
int result = testResult.result;
541544
String oneLineSummary;
542545
if (result == TEST_RESULT_SKIPPED) {
543-
oneLineSummary = "#" + mAutomatedTestRunner.getTestCount() + extra + ", SKIP";
546+
oneLineSummary = "#" + mAutomatedTestRunner.getTestCount() + ", SKIP";
544547
} else if (result == TEST_RESULT_FAILED) {
545-
oneLineSummary = getOneLineSummary() + extra + ", FAIL";
548+
oneLineSummary = getOneLineSummary() + ", FAIL";
546549
} else {
547-
oneLineSummary = getOneLineSummary() + extra;
550+
oneLineSummary = getOneLineSummary();
548551
}
549552
appendSummary(oneLineSummary + "\n");
550553
}
@@ -554,11 +557,6 @@ void logBoth(String text) {
554557
appendSummary(text + "\n");
555558
}
556559

557-
void logFailed(String text) {
558-
log(text);
559-
logAnalysis(text + "\n");
560-
}
561-
562560
private void testDeviceOutputInfo(AudioDeviceInfo outputDeviceInfo) throws InterruptedException {
563561
AudioDeviceInfo inputDeviceInfo = findCompatibleInputDevice(outputDeviceInfo.getType());
564562
showDeviceInfo(outputDeviceInfo, inputDeviceInfo);

apps/OboeTester/app/src/main/java/com/mobileer/oboetester/WaveformView.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,9 @@ private void init() {
7777
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
7878
mCurrentWidth = w;
7979
mCurrentHeight = h;
80-
mOffsetY = 0.5f * h;
81-
mScaleY = 0.0f - mOffsetY;
80+
mOffsetY = 0.5f * h; // Center waveform vertically in the viewport.
81+
// Scale down so that we can see the top of the waveforms if they are clipped.
82+
mScaleY = -0.95f * mOffsetY; // Negate so positive values are on top.
8283
}
8384

8485
public String getMessage() {
@@ -121,8 +122,8 @@ protected void onDraw(Canvas canvas) {
121122
float x0 = 0.0f;
122123
if (xScale < 1.0) {
123124
// Draw a vertical bar for multiple samples.
124-
float ymin = mOffsetY;
125-
float ymax = mOffsetY;
125+
float ymin = mOffsetY; // vertical center
126+
float ymax = mOffsetY; // vertical center
126127
for (int i = 0; i < mSampleCount; i++) {
127128
float x1 = i * xScale;
128129
if ((int) x0 != (int) x1) {

apps/OboeTester/app/src/main/res/layout/activity_data_paths.xml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,18 @@
1616
android:orientation="horizontal">
1717

1818
<CheckBox
19-
android:id="@+id/checkbox_paths_input_presets"
19+
android:id="@+id/checkbox_paths_all_channels"
2020
android:layout_width="wrap_content"
2121
android:layout_height="wrap_content"
2222
android:checked="true"
23-
android:text="InPre" />
23+
android:text="AllCh" />
2424

2525
<CheckBox
26-
android:id="@+id/checkbox_paths_all_channels"
26+
android:id="@+id/checkbox_paths_input_presets"
2727
android:layout_width="wrap_content"
2828
android:layout_height="wrap_content"
2929
android:checked="true"
30-
android:text="AllCh" />
30+
android:text="InPre" />
3131

3232
<CheckBox
3333
android:id="@+id/checkbox_paths_all_sample_rates"

0 commit comments

Comments
 (0)