Skip to content

Commit

Permalink
OboeTester: add sine wave oscillator as a reference for input
Browse files Browse the repository at this point in the history
This will prevent glitches caused by the number of input and
output frames being different in the full duplex callback.

Fixes #2068
  • Loading branch information
philburk committed Jul 11, 2024
1 parent b63b536 commit 136d033
Show file tree
Hide file tree
Showing 4 changed files with 23 additions and 22 deletions.
4 changes: 0 additions & 4 deletions apps/OboeTester/app/src/main/cpp/FullDuplexAnalyzer.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

/**
Expand Down
27 changes: 21 additions & 6 deletions apps/OboeTester/app/src/main/cpp/analyzer/BaseSineAnalyzer.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -82,6 +90,7 @@ class BaseSineAnalyzer : public LoopbackProcessor {
}
}


/**
* @param frameData upon return, contains the reference sine wave
* @param channelCount
Expand Down Expand Up @@ -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<double>(sample) * sinf(referencePhase);
mCosAccumulator += static_cast<double>(sample) * cosf(referencePhase);
bool transformSample(float sample) {
// Compare incoming signal with the reference input sine wave.
mSinAccumulator += static_cast<double>(sample) * sinf(mInputPhase);
mCosAccumulator += static_cast<double>(sample) * cosf(mInputPhase);
incrementInputPhase();

mFramesAccumulated++;
// Must be a multiple of the period or the calculation will not be accurate.
if (mFramesAccumulated == mSinePeriod) {
Expand Down Expand Up @@ -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;
Expand All @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down
12 changes: 1 addition & 11 deletions apps/OboeTester/app/src/main/cpp/analyzer/GlitchAnalyzer.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -249,7 +249,6 @@ class GlitchAnalyzer : public BaseSineAnalyzer {
}
}
}
incrementInputPhase();
} break;

case STATE_GLITCHING: {
Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit 136d033

Please sign in to comment.