From 136d03347fcc5d90bf68a52452b510fe61b524dc Mon Sep 17 00:00:00 2001 From: Phil Burk Date: Wed, 10 Jul 2024 18:02:26 -0700 Subject: [PATCH] OboeTester: add sine wave oscillator as a reference for input This will prevent glitches caused by the number of input and output frames being different in the full duplex callback. Fixes #2068 --- .../app/src/main/cpp/FullDuplexAnalyzer.h | 4 --- .../src/main/cpp/analyzer/BaseSineAnalyzer.h | 27 ++++++++++++++----- .../src/main/cpp/analyzer/DataPathAnalyzer.h | 2 +- .../src/main/cpp/analyzer/GlitchAnalyzer.h | 12 +-------- 4 files changed, 23 insertions(+), 22 deletions(-) diff --git a/apps/OboeTester/app/src/main/cpp/FullDuplexAnalyzer.h b/apps/OboeTester/app/src/main/cpp/FullDuplexAnalyzer.h index 653d806bf..82b1abedc 100644 --- a/apps/OboeTester/app/src/main/cpp/FullDuplexAnalyzer.h +++ b/apps/OboeTester/app/src/main/cpp/FullDuplexAnalyzer.h @@ -29,10 +29,6 @@ class FullDuplexAnalyzer : public FullDuplexStreamWithConversion { public: FullDuplexAnalyzer(LoopbackProcessor *processor) : mLoopbackProcessor(processor) { - // If we are measuring glitches then we should set this >1 to avoid input underruns. - // Underruns are more common when doing sample rate conversion because of the variable - // callback sizes. - setNumInputBurstsCushion(3); } /** diff --git a/apps/OboeTester/app/src/main/cpp/analyzer/BaseSineAnalyzer.h b/apps/OboeTester/app/src/main/cpp/analyzer/BaseSineAnalyzer.h index 6adb115be..1ecece528 100644 --- a/apps/OboeTester/app/src/main/cpp/analyzer/BaseSineAnalyzer.h +++ b/apps/OboeTester/app/src/main/cpp/analyzer/BaseSineAnalyzer.h @@ -74,6 +74,14 @@ class BaseSineAnalyzer : public LoopbackProcessor { mTolerance = tolerance; } + // advance and wrap phase + void incrementInputPhase() { + mInputPhase += mPhaseIncrement; + if (mInputPhase > M_PI) { + mInputPhase -= (2.0 * M_PI); + } + } + // advance and wrap phase void incrementOutputPhase() { mOutputPhase += mPhaseIncrement; @@ -82,6 +90,7 @@ class BaseSineAnalyzer : public LoopbackProcessor { } } + /** * @param frameData upon return, contains the reference sine wave * @param channelCount @@ -142,11 +151,12 @@ class BaseSineAnalyzer : public LoopbackProcessor { * @param referencePhase * @return true if magnitude and phase updated */ - bool transformSample(float sample, float referencePhase) { - // Track incoming signal and slowly adjust magnitude to account - // for drift in the DRC or AGC. - mSinAccumulator += static_cast(sample) * sinf(referencePhase); - mCosAccumulator += static_cast(sample) * cosf(referencePhase); + bool transformSample(float sample) { + // Compare incoming signal with the reference input sine wave. + mSinAccumulator += static_cast(sample) * sinf(mInputPhase); + mCosAccumulator += static_cast(sample) * cosf(mInputPhase); + incrementInputPhase(); + mFramesAccumulated++; // Must be a multiple of the period or the calculation will not be accurate. if (mFramesAccumulated == mSinePeriod) { @@ -181,6 +191,7 @@ class BaseSineAnalyzer : public LoopbackProcessor { void prepareToTest() override { LoopbackProcessor::prepareToTest(); mSinePeriod = getSampleRate() / kTargetGlitchFrequency; + mInputPhase = 0.0f; mOutputPhase = 0.0f; mInverseSinePeriod = 1.0 / mSinePeriod; mPhaseIncrement = 2.0 * M_PI * mInverseSinePeriod; @@ -193,9 +204,13 @@ class BaseSineAnalyzer : public LoopbackProcessor { int32_t mSinePeriod = 1; // this will be set before use double mInverseSinePeriod = 1.0; double mPhaseIncrement = 0.0; + // Use two sine wave phases, input and output. + // This is because the number of input and output samples may differ + // in a callback and the output frame count may advance ahead of the input, or visa versa. + double mInputPhase = 0.0; double mOutputPhase = 0.0; double mOutputAmplitude = 0.75; - // This is the phase offset between the output sine wave and the recorded + // This is the phase offset between the mInputPhase sine wave and the recorded // signal at the tuned frequency. // If this jumps around then we are probably just hearing noise. // Noise can cause the magnitude to be high but mPhaseOffset will be pretty random. diff --git a/apps/OboeTester/app/src/main/cpp/analyzer/DataPathAnalyzer.h b/apps/OboeTester/app/src/main/cpp/analyzer/DataPathAnalyzer.h index f13996b2b..deabaa96a 100644 --- a/apps/OboeTester/app/src/main/cpp/analyzer/DataPathAnalyzer.h +++ b/apps/OboeTester/app/src/main/cpp/analyzer/DataPathAnalyzer.h @@ -63,7 +63,7 @@ class DataPathAnalyzer : public BaseSineAnalyzer { float sample = frameData[getInputChannel()]; mInfiniteRecording.write(sample); - if (transformSample(sample, mOutputPhase)) { + if (transformSample(sample)) { // Analyze magnitude and phase on every period. if (mPhaseOffset != kPhaseInvalid) { double diff = fabs(calculatePhaseError(mPhaseOffset, mPreviousPhaseOffset)); diff --git a/apps/OboeTester/app/src/main/cpp/analyzer/GlitchAnalyzer.h b/apps/OboeTester/app/src/main/cpp/analyzer/GlitchAnalyzer.h index fff87902d..426474404 100644 --- a/apps/OboeTester/app/src/main/cpp/analyzer/GlitchAnalyzer.h +++ b/apps/OboeTester/app/src/main/cpp/analyzer/GlitchAnalyzer.h @@ -229,7 +229,7 @@ class GlitchAnalyzer : public BaseSineAnalyzer { // Track incoming signal and slowly adjust magnitude to account // for drift in the DRC or AGC. // Must be a multiple of the period or the calculation will not be accurate. - if (transformSample(sample, mInputPhase)) { + if (transformSample(sample)) { // Adjust phase to account for sample rate drift. mInputPhase += mPhaseOffset; @@ -249,7 +249,6 @@ class GlitchAnalyzer : public BaseSineAnalyzer { } } } - incrementInputPhase(); } break; case STATE_GLITCHING: { @@ -288,14 +287,6 @@ class GlitchAnalyzer : public BaseSineAnalyzer { int maxMeasurableGlitchLength() const { return 2 * mSinePeriod; } - // advance and wrap phase - void incrementInputPhase() { - mInputPhase += mPhaseIncrement; - if (mInputPhase > M_PI) { - mInputPhase -= (2.0 * M_PI); - } - } - bool isOutputEnabled() override { return mState != STATE_IDLE; } void onGlitchStart() { @@ -399,7 +390,6 @@ class GlitchAnalyzer : public BaseSineAnalyzer { sine_state_t mState = STATE_IDLE; int64_t mLastGlitchPosition; - double mInputPhase = 0.0; double mMaxGlitchDelta = 0.0; int32_t mGlitchCount = 0; int32_t mConsecutiveBadFrames = 0;