To check out this repository please hg clone the following URL, or open the URL using EasyMercurial or your preferred Mercurial client.

The primary repository for this project is hosted at https://github.com/sonic-visualiser/sv-dependency-builds .
This repository is a read-only copy which is updated automatically every hour.

Statistics Download as Zip
| Branch: | Tag: | Revision:

root / src / portaudio_20161030_catalina_patch / qa / loopback / src / audio_analyzer.c @ 164:9fa11135915a

History | View | Annotate | Download (22 KB)

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