gearmulator

Emulation of classic VA synths of the late 90s/2000s that are based on Motorola 56300 family DSPs
Log | Files | Refs | Submodules | README | LICENSE

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( &notchOutput, 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( &notchFilter, testTone->frequency / frameRate, 0.5 );
    567 	PaQa_FilterRecording( recording, &notchOutput, &notchFilter );
    568 	//result = PaQa_SaveRecordingToWaveFile( &notchOutput, "notch_output.wav" );
    569 	//QA_ASSERT_EQUALS( "PaQa_SaveRecordingToWaveFile failed", 0, result );
    570 	
    571 	// Apply fade-in window.
    572 	PaQa_FadeInRecording( &notchOutput, (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( &notchOutput, &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( &notchOutput );
    601 	PaQa_TerminateRecording( &hipassOutput );
    602 	return 0;
    603 	
    604 error:
    605 	PaQa_TerminateRecording( &notchOutput );
    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