audio_analyzer.c (22540B)
1 2 /* 3 * PortAudio Portable Real-Time Audio Library 4 * Latest Version at: http://www.portaudio.com 5 * 6 * Copyright (c) 1999-2010 Phil Burk and Ross Bencina 7 * 8 * Permission is hereby granted, free of charge, to any person obtaining 9 * a copy of this software and associated documentation files 10 * (the "Software"), to deal in the Software without restriction, 11 * including without limitation the rights to use, copy, modify, merge, 12 * publish, distribute, sublicense, and/or sell copies of the Software, 13 * and to permit persons to whom the Software is furnished to do so, 14 * subject to the following conditions: 15 * 16 * The above copyright notice and this permission notice shall be 17 * included in all copies or substantial portions of the Software. 18 * 19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 22 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 23 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 24 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 25 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 */ 27 28 /* 29 * The text above constitutes the entire PortAudio license; however, 30 * the PortAudio community also makes the following non-binding requests: 31 * 32 * Any person wishing to distribute modifications to the Software is 33 * requested to send the modifications to the original developer so that 34 * they can be incorporated into the canonical version. It is also 35 * requested that these non-binding requests be included along with the 36 * license above. 37 */ 38 39 #include <stdio.h> 40 #include <stdlib.h> 41 #include <string.h> 42 #include <assert.h> 43 #include <math.h> 44 #include "qa_tools.h" 45 #include "audio_analyzer.h" 46 #include "write_wav.h" 47 48 #define PAQA_POP_THRESHOLD (0.04) 49 50 /*==========================================================================================*/ 51 double PaQa_GetNthFrequency( double baseFrequency, int index ) 52 { 53 // Use 13 tone equal tempered scale because it does not generate harmonic ratios. 54 return baseFrequency * pow( 2.0, index / 13.0 ); 55 } 56 57 /*==========================================================================================*/ 58 void PaQa_EraseBuffer( float *buffer, int numFrames, int samplesPerFrame ) 59 { 60 int i; 61 int numSamples = numFrames * samplesPerFrame; 62 for( i=0; i<numSamples; i++ ) 63 { 64 *buffer++ = 0.0; 65 } 66 } 67 68 /*==========================================================================================*/ 69 void PaQa_SetupSineGenerator( PaQaSineGenerator *generator, double frequency, double amplitude, double frameRate ) 70 { 71 generator->phase = 0.0; 72 generator->amplitude = amplitude; 73 generator->frequency = frequency; 74 generator->phaseIncrement = 2.0 * frequency * MATH_PI / frameRate; 75 } 76 77 /*==========================================================================================*/ 78 void PaQa_MixSine( PaQaSineGenerator *generator, float *buffer, int numSamples, int stride ) 79 { 80 int i; 81 for( i=0; i<numSamples; i++ ) 82 { 83 float value = sinf( (float) generator->phase ) * generator->amplitude; 84 *buffer += value; // Mix with existing value. 85 buffer += stride; 86 // Advance phase and wrap around. 87 generator->phase += generator->phaseIncrement; 88 if (generator->phase > MATH_TWO_PI) 89 { 90 generator->phase -= MATH_TWO_PI; 91 } 92 } 93 } 94 95 /*==========================================================================================*/ 96 void PaQa_GenerateCrackDISABLED( float *buffer, int numSamples, int stride ) 97 { 98 int i; 99 int offset = numSamples/2; 100 for( i=0; i<numSamples; i++ ) 101 { 102 float phase = (MATH_TWO_PI * 0.5 * (i - offset)) / numSamples; 103 float cosp = cosf( phase ); 104 float cos2 = cosp * cosp; 105 // invert second half of signal 106 float value = (i < offset) ? cos2 : (0-cos2); 107 *buffer = value; 108 buffer += stride; 109 } 110 } 111 112 113 /*==========================================================================================*/ 114 int PaQa_InitializeRecording( PaQaRecording *recording, int maxFrames, int frameRate ) 115 { 116 int numBytes = maxFrames * sizeof(float); 117 recording->buffer = (float*)malloc(numBytes); 118 QA_ASSERT_TRUE( "Allocate recording buffer.", (recording->buffer != NULL) ); 119 recording->maxFrames = maxFrames; recording->sampleRate = frameRate; 120 recording->numFrames = 0; 121 return 0; 122 error: 123 return 1; 124 } 125 126 /*==========================================================================================*/ 127 void PaQa_TerminateRecording( PaQaRecording *recording ) 128 { 129 if (recording->buffer != NULL) 130 { 131 free( recording->buffer ); 132 recording->buffer = NULL; 133 } 134 recording->maxFrames = 0; 135 } 136 137 /*==========================================================================================*/ 138 int PaQa_WriteRecording( PaQaRecording *recording, float *buffer, int numFrames, int stride ) 139 { 140 int i; 141 int framesToWrite; 142 float *data = &recording->buffer[recording->numFrames]; 143 144 framesToWrite = numFrames; 145 if ((framesToWrite + recording->numFrames) > recording->maxFrames) 146 { 147 framesToWrite = recording->maxFrames - recording->numFrames; 148 } 149 150 for( i=0; i<framesToWrite; i++ ) 151 { 152 *data++ = *buffer; 153 buffer += stride; 154 } 155 recording->numFrames += framesToWrite; 156 return (recording->numFrames >= recording->maxFrames); 157 } 158 159 /*==========================================================================================*/ 160 int PaQa_WriteSilence( PaQaRecording *recording, int numFrames ) 161 { 162 int i; 163 int framesToRecord; 164 float *data = &recording->buffer[recording->numFrames]; 165 166 framesToRecord = numFrames; 167 if ((framesToRecord + recording->numFrames) > recording->maxFrames) 168 { 169 framesToRecord = recording->maxFrames - recording->numFrames; 170 } 171 172 for( i=0; i<framesToRecord; i++ ) 173 { 174 *data++ = 0.0f; 175 } 176 recording->numFrames += framesToRecord; 177 return (recording->numFrames >= recording->maxFrames); 178 } 179 180 /*==========================================================================================*/ 181 int PaQa_RecordFreeze( PaQaRecording *recording, int numFrames ) 182 { 183 int i; 184 int framesToRecord; 185 float *data = &recording->buffer[recording->numFrames]; 186 187 framesToRecord = numFrames; 188 if ((framesToRecord + recording->numFrames) > recording->maxFrames) 189 { 190 framesToRecord = recording->maxFrames - recording->numFrames; 191 } 192 193 for( i=0; i<framesToRecord; i++ ) 194 { 195 // Copy old value forward as if the signal had frozen. 196 data[i] = data[i-1]; 197 } 198 recording->numFrames += framesToRecord; 199 return (recording->numFrames >= recording->maxFrames); 200 } 201 202 /*==========================================================================================*/ 203 /** 204 * Write recording to WAV file. 205 */ 206 int PaQa_SaveRecordingToWaveFile( PaQaRecording *recording, const char *filename ) 207 { 208 WAV_Writer writer; 209 int result = 0; 210 #define NUM_SAMPLES (200) 211 short data[NUM_SAMPLES]; 212 const int samplesPerFrame = 1; 213 int numLeft = recording->numFrames; 214 float *buffer = &recording->buffer[0]; 215 216 result = Audio_WAV_OpenWriter( &writer, filename, recording->sampleRate, samplesPerFrame ); 217 if( result < 0 ) goto error; 218 219 while( numLeft > 0 ) 220 { 221 int i; 222 int numToSave = (numLeft > NUM_SAMPLES) ? NUM_SAMPLES : numLeft; 223 // Convert double samples to shorts. 224 for( i=0; i<numToSave; i++ ) 225 { 226 double fval = *buffer++; 227 // Convert float to int and clip to short range. 228 int ival = fval * 32768.0; 229 if( ival > 32767 ) ival = 32767; 230 else if( ival < -32768 ) ival = -32768; 231 data[i] = ival; 232 } 233 result = Audio_WAV_WriteShorts( &writer, data, numToSave ); 234 if( result < 0 ) goto error; 235 numLeft -= numToSave; 236 } 237 238 result = Audio_WAV_CloseWriter( &writer ); 239 if( result < 0 ) goto error; 240 241 return 0; 242 243 error: 244 printf("ERROR: result = %d\n", result ); 245 return result; 246 #undef NUM_SAMPLES 247 } 248 249 /*==========================================================================================*/ 250 251 double PaQa_MeasureCrossingSlope( float *buffer, int numFrames ) 252 { 253 int i; 254 double slopeTotal = 0.0; 255 int slopeCount = 0; 256 float previous; 257 double averageSlope = 0.0; 258 259 previous = buffer[0]; 260 for( i=1; i<numFrames; i++ ) 261 { 262 float current = buffer[i]; 263 if( (current > 0.0) && (previous < 0.0) ) 264 { 265 double delta = current - previous; 266 slopeTotal += delta; 267 slopeCount += 1; 268 } 269 previous = current; 270 } 271 if( slopeCount > 0 ) 272 { 273 averageSlope = slopeTotal / slopeCount; 274 } 275 return averageSlope; 276 } 277 278 /*==========================================================================================*/ 279 /* 280 * We can't just measure the peaks cuz they may be clipped. 281 * But the zero crossing should be intact. 282 * The measured slope of a sine wave at zero should be: 283 * 284 * slope = sin( 2PI * frequency / sampleRate ) 285 * 286 */ 287 double PaQa_MeasureSineAmplitudeBySlope( PaQaRecording *recording, 288 double frequency, double frameRate, 289 int startFrame, int numFrames ) 290 { 291 float *buffer = &recording->buffer[startFrame]; 292 double measuredSlope = PaQa_MeasureCrossingSlope( buffer, numFrames ); 293 double unitySlope = sin( MATH_TWO_PI * frequency / frameRate ); 294 double estimatedAmplitude = measuredSlope / unitySlope; 295 return estimatedAmplitude; 296 } 297 298 /*==========================================================================================*/ 299 double PaQa_CorrelateSine( PaQaRecording *recording, double frequency, double frameRate, 300 int startFrame, int numFrames, double *phasePtr ) 301 { 302 double magnitude = 0.0; 303 int numLeft = numFrames; 304 double phase = 0.0; 305 double phaseIncrement = 2.0 * MATH_PI * frequency / frameRate; 306 double sinAccumulator = 0.0; 307 double cosAccumulator = 0.0; 308 float *data = &recording->buffer[startFrame]; 309 310 QA_ASSERT_TRUE( "startFrame out of bounds", (startFrame < recording->numFrames) ); 311 QA_ASSERT_TRUE( "numFrames out of bounds", ((startFrame+numFrames) <= recording->numFrames) ); 312 313 while( numLeft > 0 ) 314 { 315 double sample = (double) *data++; 316 sinAccumulator += sample * sin( phase ); 317 cosAccumulator += sample * cos( phase ); 318 phase += phaseIncrement; 319 if (phase > MATH_TWO_PI) 320 { 321 phase -= MATH_TWO_PI; 322 } 323 numLeft -= 1; 324 } 325 sinAccumulator = sinAccumulator / numFrames; 326 cosAccumulator = cosAccumulator / numFrames; 327 // TODO Why do I have to multiply by 2.0? Need it to make result come out right. 328 magnitude = 2.0 * sqrt( (sinAccumulator * sinAccumulator) + (cosAccumulator * cosAccumulator )); 329 if( phasePtr != NULL ) 330 { 331 double phase = atan2( cosAccumulator, sinAccumulator ); 332 *phasePtr = phase; 333 } 334 return magnitude; 335 error: 336 return -1.0; 337 } 338 339 /*==========================================================================================*/ 340 void PaQa_FilterRecording( PaQaRecording *input, PaQaRecording *output, BiquadFilter *filter ) 341 { 342 int numToFilter = (input->numFrames > output->maxFrames) ? output->maxFrames : input->numFrames; 343 BiquadFilter_Filter( filter, &input->buffer[0], &output->buffer[0], numToFilter ); 344 output->numFrames = numToFilter; 345 } 346 347 /*==========================================================================================*/ 348 /** Scan until we get a correlation of a single that goes over the tolerance level, 349 * peaks then drops to half the peak. 350 * Look for inverse correlation as well. 351 */ 352 double PaQa_FindFirstMatch( PaQaRecording *recording, float *buffer, int numFrames, double threshold ) 353 { 354 int ic,is; 355 // How many buffers will fit in the recording? 356 int maxCorrelations = recording->numFrames - numFrames; 357 double maxSum = 0.0; 358 int peakIndex = -1; 359 double inverseMaxSum = 0.0; 360 int inversePeakIndex = -1; 361 double location = -1.0; 362 363 QA_ASSERT_TRUE( "numFrames out of bounds", (numFrames < recording->numFrames) ); 364 365 for( ic=0; ic<maxCorrelations; ic++ ) 366 { 367 int pastPeak; 368 int inversePastPeak; 369 370 double sum = 0.0; 371 // Correlate buffer against the recording. 372 float *recorded = &recording->buffer[ ic ]; 373 for( is=0; is<numFrames; is++ ) 374 { 375 float s1 = buffer[is]; 376 float s2 = *recorded++; 377 sum += s1 * s2; 378 } 379 if( (sum > maxSum) ) 380 { 381 maxSum = sum; 382 peakIndex = ic; 383 } 384 if( ((-sum) > inverseMaxSum) ) 385 { 386 inverseMaxSum = -sum; 387 inversePeakIndex = ic; 388 } 389 pastPeak = (maxSum > threshold) && (sum < 0.5*maxSum); 390 inversePastPeak = (inverseMaxSum > threshold) && ((-sum) < 0.5*inverseMaxSum); 391 //printf("PaQa_FindFirstMatch: ic = %4d, sum = %8f, maxSum = %8f, inverseMaxSum = %8f\n", ic, sum, maxSum, inverseMaxSum ); 392 if( pastPeak && inversePastPeak ) 393 { 394 if( maxSum > inverseMaxSum ) 395 { 396 location = peakIndex; 397 } 398 else 399 { 400 location = inversePeakIndex; 401 } 402 break; 403 } 404 405 } 406 //printf("PaQa_FindFirstMatch: location = %4d\n", (int)location ); 407 return location; 408 error: 409 return -1.0; 410 } 411 412 /*==========================================================================================*/ 413 // Measure the area under the curve by summing absolute value of each value. 414 double PaQa_MeasureArea( float *buffer, int numFrames, int stride ) 415 { 416 int is; 417 double area = 0.0; 418 for( is=0; is<numFrames; is++ ) 419 { 420 area += fabs( *buffer ); 421 buffer += stride; 422 } 423 return area; 424 } 425 426 /*==========================================================================================*/ 427 // Measure the area under the curve by summing absolute value of each value. 428 double PaQa_MeasureRootMeanSquare( float *buffer, int numFrames ) 429 { 430 int is; 431 double area = 0.0; 432 double root; 433 for( is=0; is<numFrames; is++ ) 434 { 435 float value = *buffer++; 436 area += value * value; 437 } 438 root = sqrt( area ); 439 return root / numFrames; 440 } 441 442 443 /*==========================================================================================*/ 444 // Compare the amplitudes of these two signals. 445 // Return ratio of recorded signal over buffer signal. 446 447 double PaQa_CompareAmplitudes( PaQaRecording *recording, int startAt, float *buffer, int numFrames ) 448 { 449 QA_ASSERT_TRUE( "startAt+numFrames out of bounds", ((startAt+numFrames) < recording->numFrames) ); 450 451 { 452 double recordedArea = PaQa_MeasureArea( &recording->buffer[startAt], numFrames, 1 ); 453 double bufferArea = PaQa_MeasureArea( buffer, numFrames, 1 ); 454 if( bufferArea == 0.0 ) return 100000000.0; 455 return recordedArea / bufferArea; 456 } 457 error: 458 return -1.0; 459 } 460 461 462 /*==========================================================================================*/ 463 double PaQa_ComputePhaseDifference( double phase1, double phase2 ) 464 { 465 double delta = phase1 - phase2; 466 while( delta > MATH_PI ) 467 { 468 delta -= MATH_TWO_PI; 469 } 470 while( delta < -MATH_PI ) 471 { 472 delta += MATH_TWO_PI; 473 } 474 return delta; 475 } 476 477 /*==========================================================================================*/ 478 int PaQa_MeasureLatency( PaQaRecording *recording, PaQaTestTone *testTone, PaQaAnalysisResult *analysisResult ) 479 { 480 double threshold; 481 PaQaSineGenerator generator; 482 #define MAX_BUFFER_SIZE 2048 483 float buffer[MAX_BUFFER_SIZE]; 484 double period = testTone->sampleRate / testTone->frequency; 485 int cycleSize = (int) (period + 0.5); 486 //printf("PaQa_AnalyseRecording: frequency = %8f, frameRate = %8f, period = %8f, cycleSize = %8d\n", 487 // testTone->frequency, testTone->sampleRate, period, cycleSize ); 488 analysisResult->latency = -1; 489 analysisResult->valid = (0); 490 491 // Set up generator to find matching first cycle. 492 QA_ASSERT_TRUE( "cycleSize out of bounds", (cycleSize < MAX_BUFFER_SIZE) ); 493 PaQa_SetupSineGenerator( &generator, testTone->frequency, testTone->amplitude, testTone->sampleRate ); 494 PaQa_EraseBuffer( buffer, cycleSize, testTone->samplesPerFrame ); 495 PaQa_MixSine( &generator, buffer, cycleSize, testTone->samplesPerFrame ); 496 497 threshold = cycleSize * 0.02; 498 analysisResult->latency = PaQa_FindFirstMatch( recording, buffer, cycleSize, threshold ); 499 QA_ASSERT_TRUE( "Could not find the start of the signal.", (analysisResult->latency >= 0) ); 500 analysisResult->amplitudeRatio = PaQa_CompareAmplitudes( recording, analysisResult->latency, buffer, cycleSize ); 501 return 0; 502 error: 503 return -1; 504 } 505 506 /*==========================================================================================*/ 507 // Apply cosine squared window. 508 void PaQa_FadeInRecording( PaQaRecording *recording, int startFrame, int count ) 509 { 510 int is; 511 double phase = 0.5 * MATH_PI; 512 // Advance a quarter wave 513 double phaseIncrement = 0.25 * 2.0 * MATH_PI / count; 514 515 assert( startFrame >= 0 ); 516 assert( count > 0 ); 517 518 /* Zero out initial part of the recording. */ 519 for( is=0; is<startFrame; is++ ) 520 { 521 recording->buffer[ is ] = 0.0f; 522 } 523 /* Fade in where signal begins. */ 524 for( is=0; is<count; is++ ) 525 { 526 double c = cos( phase ); 527 double w = c * c; 528 float x = recording->buffer[ is + startFrame ]; 529 float y = x * w; 530 //printf("FADE %d : w=%f, x=%f, y=%f\n", is, w, x, y ); 531 recording->buffer[ is + startFrame ] = y; 532 533 phase += phaseIncrement; 534 } 535 } 536 537 538 /*==========================================================================================*/ 539 /** Apply notch filter and high pass filter then detect remaining energy. 540 */ 541 int PaQa_DetectPop( PaQaRecording *recording, PaQaTestTone *testTone, PaQaAnalysisResult *analysisResult ) 542 { 543 int result = 0; 544 int i; 545 double maxAmplitude; 546 int maxPosition; 547 548 PaQaRecording notchOutput = { 0 }; 549 BiquadFilter notchFilter; 550 551 PaQaRecording hipassOutput = { 0 }; 552 BiquadFilter hipassFilter; 553 554 int frameRate = (int) recording->sampleRate; 555 556 analysisResult->popPosition = -1; 557 analysisResult->popAmplitude = 0.0; 558 559 result = PaQa_InitializeRecording( ¬chOutput, recording->numFrames, frameRate ); 560 QA_ASSERT_EQUALS( "PaQa_InitializeRecording failed", 0, result ); 561 562 result = PaQa_InitializeRecording( &hipassOutput, recording->numFrames, frameRate ); 563 QA_ASSERT_EQUALS( "PaQa_InitializeRecording failed", 0, result ); 564 565 // Use notch filter to remove test tone. 566 BiquadFilter_SetupNotch( ¬chFilter, testTone->frequency / frameRate, 0.5 ); 567 PaQa_FilterRecording( recording, ¬chOutput, ¬chFilter ); 568 //result = PaQa_SaveRecordingToWaveFile( ¬chOutput, "notch_output.wav" ); 569 //QA_ASSERT_EQUALS( "PaQa_SaveRecordingToWaveFile failed", 0, result ); 570 571 // Apply fade-in window. 572 PaQa_FadeInRecording( ¬chOutput, (int) analysisResult->latency, 500 ); 573 574 // Use high pass to accentuate the edges of a pop. At higher frequency! 575 BiquadFilter_SetupHighPass( &hipassFilter, 2.0 * testTone->frequency / frameRate, 0.5 ); 576 PaQa_FilterRecording( ¬chOutput, &hipassOutput, &hipassFilter ); 577 //result = PaQa_SaveRecordingToWaveFile( &hipassOutput, "hipass_output.wav" ); 578 //QA_ASSERT_EQUALS( "PaQa_SaveRecordingToWaveFile failed", 0, result ); 579 580 // Scan remaining signal looking for peak. 581 maxAmplitude = 0.0; 582 maxPosition = -1; 583 for( i=(int) analysisResult->latency; i<hipassOutput.numFrames; i++ ) 584 { 585 float x = hipassOutput.buffer[i]; 586 float mag = fabs( x ); 587 if( mag > maxAmplitude ) 588 { 589 maxAmplitude = mag; 590 maxPosition = i; 591 } 592 } 593 594 if( maxAmplitude > PAQA_POP_THRESHOLD ) 595 { 596 analysisResult->popPosition = maxPosition; 597 analysisResult->popAmplitude = maxAmplitude; 598 } 599 600 PaQa_TerminateRecording( ¬chOutput ); 601 PaQa_TerminateRecording( &hipassOutput ); 602 return 0; 603 604 error: 605 PaQa_TerminateRecording( ¬chOutput ); 606 PaQa_TerminateRecording( &hipassOutput ); 607 return -1; 608 } 609 610 /*==========================================================================================*/ 611 int PaQa_DetectPhaseError( PaQaRecording *recording, PaQaTestTone *testTone, PaQaAnalysisResult *analysisResult ) 612 { 613 int i; 614 double period = testTone->sampleRate / testTone->frequency; 615 int cycleSize = (int) (period + 0.5); 616 617 double maxAddedFrames = 0.0; 618 double maxDroppedFrames = 0.0; 619 620 double previousPhase = 0.0; 621 double previousFrameError = 0; 622 int loopCount = 0; 623 int skip = cycleSize; 624 int windowSize = cycleSize; 625 626 // Scan recording starting with first cycle, looking for phase errors. 627 analysisResult->numDroppedFrames = 0.0; 628 analysisResult->numAddedFrames = 0.0; 629 analysisResult->droppedFramesPosition = -1.0; 630 analysisResult->addedFramesPosition = -1.0; 631 632 for( i=analysisResult->latency; i<(recording->numFrames - windowSize); i += skip ) 633 { 634 double expectedPhase = previousPhase + (skip * MATH_TWO_PI / period); 635 double expectedPhaseIncrement = PaQa_ComputePhaseDifference( expectedPhase, previousPhase ); 636 637 double phase = 666.0; 638 double mag = PaQa_CorrelateSine( recording, testTone->frequency, testTone->sampleRate, i, windowSize, &phase ); 639 if( (loopCount > 1) && (mag > 0.0) ) 640 { 641 double phaseDelta = PaQa_ComputePhaseDifference( phase, previousPhase ); 642 double phaseError = PaQa_ComputePhaseDifference( phaseDelta, expectedPhaseIncrement ); 643 // Convert phaseError to equivalent number of frames. 644 double frameError = period * phaseError / MATH_TWO_PI; 645 double consecutiveFrameError = frameError + previousFrameError; 646 // if( fabs(frameError) > 0.01 ) 647 // { 648 // printf("FFFFFFFFFFFFF frameError = %f, at %d\n", frameError, i ); 649 // } 650 if( consecutiveFrameError > 0.8 ) 651 { 652 double droppedFrames = consecutiveFrameError; 653 if (droppedFrames > (maxDroppedFrames * 1.001)) 654 { 655 analysisResult->numDroppedFrames = droppedFrames; 656 analysisResult->droppedFramesPosition = i + (windowSize/2); 657 maxDroppedFrames = droppedFrames; 658 } 659 } 660 else if( consecutiveFrameError < -0.8 ) 661 { 662 double addedFrames = 0 - consecutiveFrameError; 663 if (addedFrames > (maxAddedFrames * 1.001)) 664 { 665 analysisResult->numAddedFrames = addedFrames; 666 analysisResult->addedFramesPosition = i + (windowSize/2); 667 maxAddedFrames = addedFrames; 668 } 669 } 670 previousFrameError = frameError; 671 672 673 //if( i<8000 ) 674 //{ 675 // printf("%d: phase = %8f, expected = %8f, delta = %8f, frameError = %8f\n", i, phase, expectedPhaseIncrement, phaseDelta, frameError ); 676 //} 677 } 678 previousPhase = phase; 679 loopCount += 1; 680 } 681 return 0; 682 } 683 684 /*==========================================================================================*/ 685 int PaQa_AnalyseRecording( PaQaRecording *recording, PaQaTestTone *testTone, PaQaAnalysisResult *analysisResult ) 686 { 687 int result = 0; 688 689 memset( analysisResult, 0, sizeof(PaQaAnalysisResult) ); 690 result = PaQa_MeasureLatency( recording, testTone, analysisResult ); 691 QA_ASSERT_EQUALS( "latency measurement", 0, result ); 692 693 if( (analysisResult->latency >= 0) && (analysisResult->amplitudeRatio > 0.1) ) 694 { 695 analysisResult->valid = (1); 696 697 result = PaQa_DetectPop( recording, testTone, analysisResult ); 698 QA_ASSERT_EQUALS( "detect pop", 0, result ); 699 700 result = PaQa_DetectPhaseError( recording, testTone, analysisResult ); 701 QA_ASSERT_EQUALS( "detect phase error", 0, result ); 702 } 703 return 0; 704 error: 705 return -1; 706 } 707