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 /*==========================================================================================*/
PaQa_GetNthFrequency(double baseFrequency,int index)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 /*==========================================================================================*/
PaQa_EraseBuffer(float * buffer,int numFrames,int samplesPerFrame)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 /*==========================================================================================*/
PaQa_SetupSineGenerator(PaQaSineGenerator * generator,double frequency,double amplitude,double frameRate)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 /*==========================================================================================*/
PaQa_MixSine(PaQaSineGenerator * generator,float * buffer,int numSamples,int stride)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 /*==========================================================================================*/
PaQa_GenerateCrackDISABLED(float * buffer,int numSamples,int stride)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 /*==========================================================================================*/
PaQa_InitializeRecording(PaQaRecording * recording,int maxFrames,int frameRate)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 /*==========================================================================================*/
PaQa_TerminateRecording(PaQaRecording * recording)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 /*==========================================================================================*/
PaQa_WriteRecording(PaQaRecording * recording,float * buffer,int numFrames,int stride)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 /*==========================================================================================*/
PaQa_WriteSilence(PaQaRecording * recording,int numFrames)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 /*==========================================================================================*/
PaQa_RecordFreeze(PaQaRecording * recording,int numFrames)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  */
PaQa_SaveRecordingToWaveFile(PaQaRecording * recording,const char * filename)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 
PaQa_MeasureCrossingSlope(float * buffer,int numFrames)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  */
PaQa_MeasureSineAmplitudeBySlope(PaQaRecording * recording,double frequency,double frameRate,int startFrame,int numFrames)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 /*==========================================================================================*/
PaQa_CorrelateSine(PaQaRecording * recording,double frequency,double frameRate,int startFrame,int numFrames,double * phasePtr)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 /*==========================================================================================*/
PaQa_FilterRecording(PaQaRecording * input,PaQaRecording * output,BiquadFilter * filter)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  */
PaQa_FindFirstMatch(PaQaRecording * recording,float * buffer,int numFrames,double threshold)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.
PaQa_MeasureArea(float * buffer,int numFrames,int stride)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.
PaQa_MeasureRootMeanSquare(float * buffer,int numFrames)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 
PaQa_CompareAmplitudes(PaQaRecording * recording,int startAt,float * buffer,int numFrames)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 /*==========================================================================================*/
PaQa_ComputePhaseDifference(double phase1,double phase2)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 /*==========================================================================================*/
PaQa_MeasureLatency(PaQaRecording * recording,PaQaTestTone * testTone,PaQaAnalysisResult * analysisResult)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.
PaQa_FadeInRecording(PaQaRecording * recording,int startFrame,int count)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  */
PaQa_DetectPop(PaQaRecording * recording,PaQaTestTone * testTone,PaQaAnalysisResult * analysisResult)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 /*==========================================================================================*/
PaQa_DetectPhaseError(PaQaRecording * recording,PaQaTestTone * testTone,PaQaAnalysisResult * analysisResult)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 /*==========================================================================================*/
PaQa_AnalyseRecording(PaQaRecording * recording,PaQaTestTone * testTone,PaQaAnalysisResult * analysisResult)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 
708