From: philburk Date: Tue, 8 Mar 2011 00:37:50 +0000 (+0000) Subject: Improve latency measurement in loopback so it handles DC steps better. X-Git-Tag: pa_stable_v19_20110326_r1647~6 X-Git-Url: https://andrewgundersen.net/repos?a=commitdiff_plain;h=c0289d34d763b48d486524209ff967a521e056a1;p=portaudio Improve latency measurement in loopback so it handles DC steps better. --- diff --git a/qa/loopback/src/audio_analyzer.c b/qa/loopback/src/audio_analyzer.c index f027f63..6fa2f76 100644 --- a/qa/loopback/src/audio_analyzer.c +++ b/qa/loopback/src/audio_analyzer.c @@ -345,21 +345,28 @@ void PaQa_FilterRecording( PaQaRecording *input, PaQaRecording *output, BiquadFi } /*==========================================================================================*/ -// Scan until we get a correlation that goes over the tolerance level, -// peaks then drops to half the peak. -double PaQa_FindFirstMatch( PaQaRecording *recording, float *buffer, int numFrames, double tolerance ) +/** Scan until we get a correlation of a single that goes over the tolerance level, + * peaks then drops to half the peak. + * Look for inverse correlation as well. + */ +double PaQa_FindFirstMatch( PaQaRecording *recording, float *buffer, int numFrames, double threshold ) { int ic,is; // How many buffers will fit in the recording? int maxCorrelations = recording->numFrames - numFrames; double maxSum = 0.0; int peakIndex = -1; + double inverseMaxSum = 0.0; + int inversePeakIndex = -1; double location = -1.0; QA_ASSERT_TRUE( "numFrames out of bounds", (numFrames < recording->numFrames) ); for( ic=0; icbuffer[ ic ]; @@ -374,13 +381,29 @@ double PaQa_FindFirstMatch( PaQaRecording *recording, float *buffer, int numFram maxSum = sum; peakIndex = ic; } - if( (maxSum > tolerance) && (sum < 0.5*maxSum) ) + if( ((-sum) > inverseMaxSum) ) { - location = peakIndex; + inverseMaxSum = -sum; + inversePeakIndex = ic; + } + pastPeak = (maxSum > threshold) && (sum < 0.5*maxSum); + inversePastPeak = (inverseMaxSum > threshold) && ((-sum) < 0.5*inverseMaxSum); + //printf("PaQa_FindFirstMatch: ic = %4d, sum = %8f, maxSum = %8f, inverseMaxSum = %8f\n", ic, sum, maxSum, inverseMaxSum ); + if( pastPeak && inversePastPeak ) + { + if( maxSum > inverseMaxSum ) + { + location = peakIndex; + } + else + { + location = inversePeakIndex; + } break; } } + //printf("PaQa_FindFirstMatch: location = %4d\n", (int)location ); return location; error: return -1.0; @@ -454,6 +477,7 @@ double PaQa_ComputePhaseDifference( double phase1, double phase2 ) /*==========================================================================================*/ int PaQa_MeasureLatency( PaQaRecording *recording, PaQaTestTone *testTone, PaQaAnalysisResult *analysisResult ) { + double threshold; PaQaSineGenerator generator; #define MAX_BUFFER_SIZE 2048 float buffer[MAX_BUFFER_SIZE]; @@ -470,7 +494,8 @@ int PaQa_MeasureLatency( PaQaRecording *recording, PaQaTestTone *testTone, PaQaA PaQa_EraseBuffer( buffer, cycleSize, testTone->samplesPerFrame ); PaQa_MixSine( &generator, buffer, cycleSize, testTone->samplesPerFrame ); - analysisResult->latency = PaQa_FindFirstMatch( recording, buffer, cycleSize, /* tolerance= */ 0.1 ); + threshold = cycleSize * 0.01; + analysisResult->latency = PaQa_FindFirstMatch( recording, buffer, cycleSize, threshold ); QA_ASSERT_TRUE( "Could not find the start of the signal.", (analysisResult->latency >= 0) ); analysisResult->amplitudeRatio = PaQa_CompareAmplitudes( recording, analysisResult->latency, buffer, cycleSize ); return 0; @@ -478,8 +503,6 @@ error: return -1; } - - /*==========================================================================================*/ // Apply cosine squared window. void PaQa_FadeInRecording( PaQaRecording *recording, int startFrame, int count ) @@ -551,7 +574,7 @@ int PaQa_DetectPop( PaQaRecording *recording, PaQaTestTone *testTone, PaQaAnalys // Scan remaining signal looking for peak. maxAmplitude = 0.0; maxPosition = -1; - for( i=0; ilatency; isamplesPerFrame ); } +} + +/*==========================================================================================*/ +/** + * Generate a recording with pop. + */ + +static void MakeRecordingWithPop( PaQaRecording *recording, PaQaTestTone *testTone, int popPosition, int popWidth, double popAmplitude ) +{ + int i; + + MakeCleanRecording( recording, testTone ); // Apply glitch to good recording. if( (popPosition + popWidth) >= recording->numFrames ) @@ -324,6 +335,9 @@ static int TestDetectPhaseErrors( void ) { int result; + result = TestDetectSinglePhaseError( 44100, 200, 477, -1, 0 ); + if( result < 0 ) return result; +/* result = TestDetectSinglePhaseError( 44100, 200, 77, -1, 0 ); if( result < 0 ) return result; @@ -345,7 +359,7 @@ static int TestDetectPhaseErrors( void ) // Note that if the frequency is too high then it is hard to detect single dropped frames. result = TestDetectSinglePhaseError( 44100, 200, 500, 4251, -1 ); if( result < 0 ) return result; - +*/ return 0; } @@ -371,7 +385,7 @@ static int TestDetectSinglePop( double sampleRate, int cycleSize, int latencyFra QA_ASSERT_EQUALS( "PaQa_InitializeRecording failed", 0, result ); MakeRecordingWithPop( &recording, &testTone, popPosition, popWidth, popAmplitude ); - + PaQa_AnalyseRecording( &recording, &testTone, &analysisResult ); #if PRINT_REPORTS @@ -379,7 +393,7 @@ static int TestDetectSinglePop( double sampleRate, int cycleSize, int latencyFra printf(" expected actual\n"); printf(" latency: %10.3f %10.3f\n", (double)latencyFrames, analysisResult.latency ); printf(" popPosition: %10.3f %10.3f\n", (double)popPosition, analysisResult.popPosition ); - printf(" popAmplitude: %10.3f %10.3f\n", (double)popAmplitude, analysisResult.popAmplitude ); + printf(" popAmplitude: %10.3f %10.3f\n", popAmplitude, analysisResult.popAmplitude ); printf(" cycleSize: %6d\n", cycleSize ); printf(" num added frames: %10.3f\n", analysisResult.numAddedFrames ); printf(" added frames at: %10.3f\n", analysisResult.addedFramesPosition ); @@ -403,6 +417,80 @@ error: return 1; } +/*==========================================================================================*/ +/** + * Analyse recording with a DC offset. + */ +static int TestSingleInitialSpike( double sampleRate, int stepPosition, int cycleSize, int latencyFrames, double stepAmplitude ) +{ + int i; + int result = 0; + // Account for highpass filter offset. + int expectedLatency = latencyFrames + 1; + PaQaRecording recording; + + PaQaRecording hipassOutput = { 0 }; + BiquadFilter hipassFilter; + + PaQaTestTone testTone; + PaQaAnalysisResult analysisResult = { 0.0 }; + int maxFrames = ((int)sampleRate) * 2; + + testTone.samplesPerFrame = 1; + testTone.sampleRate = sampleRate; + testTone.frequency = sampleRate / cycleSize; + testTone.amplitude = -0.5; + testTone.startDelay = latencyFrames; + + result = PaQa_InitializeRecording( &recording, maxFrames, (int) sampleRate ); + QA_ASSERT_EQUALS( "PaQa_InitializeRecording failed", 0, result ); + + result = PaQa_InitializeRecording( &hipassOutput, maxFrames, (int) sampleRate ); + QA_ASSERT_EQUALS( "PaQa_InitializeRecording failed", 0, result ); + + MakeCleanRecording( &recording, &testTone ); + + // Apply DC step. + for( i=stepPosition; i