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( ¬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 /*==========================================================================================*/
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