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 / paqa_latency.c @ 164:9fa11135915a

History | View | Annotate | Download (17.8 KB)

1
/** @file paqa_latency.c
2
        @ingroup qa_src
3
        @brief Test latency estimates.
4
        @author Ross Bencina <rossb@audiomulch.com>
5
    @author Phil Burk <philburk@softsynth.com>
6
*/
7
/*
8
 * $Id: patest_sine.c 1368 2008-03-01 00:38:27Z rossb $
9
 *
10
 * This program uses the PortAudio Portable Audio Library.
11
 * For more information see: http://www.portaudio.com/
12
 * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
13
 *
14
 * Permission is hereby granted, free of charge, to any person obtaining
15
 * a copy of this software and associated documentation files
16
 * (the "Software"), to deal in the Software without restriction,
17
 * including without limitation the rights to use, copy, modify, merge,
18
 * publish, distribute, sublicense, and/or sell copies of the Software,
19
 * and to permit persons to whom the Software is furnished to do so,
20
 * subject to the following conditions:
21
 *
22
 * The above copyright notice and this permission notice shall be
23
 * included in all copies or substantial portions of the Software.
24
 *
25
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
28
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
29
 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
30
 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32
 */
33

    
34
/*
35
 * The text above constitutes the entire PortAudio license; however, 
36
 * the PortAudio community also makes the following non-binding requests:
37
 *
38
 * Any person wishing to distribute modifications to the Software is
39
 * requested to send the modifications to the original developer so that
40
 * they can be incorporated into the canonical version. It is also 
41
 * requested that these non-binding requests be included along with the 
42
 * license above.
43
 */
44
#include <stdio.h>
45
#include <math.h>
46
#include "portaudio.h"
47
#include "loopback/src/qa_tools.h"
48

    
49
#define NUM_SECONDS   (5)
50
#define SAMPLE_RATE   (44100)
51
#define FRAMES_PER_BUFFER  (64)
52

    
53
#ifndef M_PI
54
#define M_PI  (3.14159265)
55
#endif
56

    
57
#define TABLE_SIZE   (200)
58
typedef struct
59
{
60
    float sine[TABLE_SIZE];
61
    int left_phase;
62
    int right_phase;
63
    char message[20];
64
    int minFramesPerBuffer;
65
    int maxFramesPerBuffer;
66
    int callbackCount;
67
    PaTime minDeltaDacTime;
68
    PaTime maxDeltaDacTime;
69
    PaStreamCallbackTimeInfo previousTimeInfo;
70
}
71
paTestData;
72

    
73
/* Used to tally the results of the QA tests. */
74
int g_testsPassed = 0;
75
int g_testsFailed = 0;
76

    
77
/* This routine will be called by the PortAudio engine when audio is needed.
78
** It may called at interrupt level on some machines so don't do anything
79
** that could mess up the system like calling malloc() or free().
80
*/
81
static int patestCallback( const void *inputBuffer, void *outputBuffer,
82
                            unsigned long framesPerBuffer,
83
                            const PaStreamCallbackTimeInfo* timeInfo,
84
                            PaStreamCallbackFlags statusFlags,
85
                            void *userData )
86
{
87
    paTestData *data = (paTestData*)userData;
88
    float *out = (float*)outputBuffer;
89
    unsigned long i;
90

    
91
    (void) timeInfo; /* Prevent unused variable warnings. */
92
    (void) statusFlags;
93
    (void) inputBuffer;
94
    
95
    if( data->minFramesPerBuffer > framesPerBuffer )
96
    {
97
        data->minFramesPerBuffer = framesPerBuffer;
98
    }
99
    if( data->maxFramesPerBuffer < framesPerBuffer )
100
    {
101
        data->maxFramesPerBuffer = framesPerBuffer;
102
    }
103
    
104
    /* Measure min and max output time stamp delta. */
105
    if( data->callbackCount > 0 )
106
    {
107
        PaTime delta = timeInfo->outputBufferDacTime - data->previousTimeInfo.outputBufferDacTime;
108
        if( data->minDeltaDacTime > delta )
109
        {
110
            data->minDeltaDacTime = delta;
111
        }
112
        if( data->maxDeltaDacTime < delta )
113
        {
114
            data->maxDeltaDacTime = delta;
115
        }
116
    }
117
    data->previousTimeInfo = *timeInfo;
118
    
119
    for( i=0; i<framesPerBuffer; i++ )
120
    {
121
        *out++ = data->sine[data->left_phase];  /* left */
122
        *out++ = data->sine[data->right_phase];  /* right */
123
        data->left_phase += 1;
124
        if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE;
125
        data->right_phase += 3; /* higher pitch so we can distinguish left and right. */
126
        if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE;
127
    }
128
    
129
    data->callbackCount += 1;
130
    return paContinue;
131
}
132

    
133
PaError paqaCheckLatency( PaStreamParameters *outputParamsPtr, 
134
                         paTestData *dataPtr, double sampleRate, unsigned long framesPerBuffer )
135
{
136
    PaError err;
137
    PaStream *stream;
138
    const PaStreamInfo* streamInfo;
139

    
140
    dataPtr->minFramesPerBuffer = 9999999;
141
    dataPtr->maxFramesPerBuffer = 0;
142
    dataPtr->minDeltaDacTime = 9999999.0;
143
    dataPtr->maxDeltaDacTime = 0.0;
144
    dataPtr->callbackCount = 0;
145
    
146
    printf("Stream parameter: suggestedOutputLatency = %g\n", outputParamsPtr->suggestedLatency );
147
    if( framesPerBuffer == paFramesPerBufferUnspecified ){
148
        printf("Stream parameter: user framesPerBuffer = paFramesPerBufferUnspecified\n" );
149
    }else{
150
        printf("Stream parameter: user framesPerBuffer = %lu\n", framesPerBuffer );
151
    }
152
    err = Pa_OpenStream(
153
                        &stream,
154
                        NULL, /* no input */
155
                        outputParamsPtr,
156
                        sampleRate,
157
                        framesPerBuffer,
158
                        paClipOff,      /* we won't output out of range samples so don't bother clipping them */
159
                        patestCallback,
160
                        dataPtr );
161
    if( err != paNoError ) goto error1;
162
    
163
    streamInfo = Pa_GetStreamInfo( stream );
164
    printf("Stream info: inputLatency  = %g\n", streamInfo->inputLatency );
165
    printf("Stream info: outputLatency = %g\n", streamInfo->outputLatency );
166

    
167
    err = Pa_StartStream( stream );
168
    if( err != paNoError ) goto error2;
169

    
170
    printf("Play for %d seconds.\n", NUM_SECONDS );
171
    Pa_Sleep( NUM_SECONDS * 1000 );
172
    
173
    printf("  minFramesPerBuffer = %4d\n", dataPtr->minFramesPerBuffer );
174
    printf("  maxFramesPerBuffer = %4d\n", dataPtr->maxFramesPerBuffer );
175
    printf("  minDeltaDacTime = %f\n", dataPtr->minDeltaDacTime );
176
    printf("  maxDeltaDacTime = %f\n", dataPtr->maxDeltaDacTime );
177

    
178
    err = Pa_StopStream( stream );
179
    if( err != paNoError ) goto error2;
180

    
181
    err = Pa_CloseStream( stream );
182
    Pa_Sleep( 1 * 1000 );
183

    
184
    
185
    printf("-------------------------------------\n");
186
    return err;
187
error2:
188
    Pa_CloseStream( stream );
189
error1:
190
    printf("-------------------------------------\n");
191
    return err;
192
}
193

    
194

    
195
/*******************************************************************/
196
static int paqaNoopCallback( const void *inputBuffer, void *outputBuffer,
197
                          unsigned long framesPerBuffer,
198
                          const PaStreamCallbackTimeInfo* timeInfo,
199
                          PaStreamCallbackFlags statusFlags,
200
                          void *userData )
201
{
202
    (void)inputBuffer;
203
    (void)outputBuffer;
204
    (void)framesPerBuffer;
205
    (void)timeInfo;
206
    (void)statusFlags;
207
    (void)userData;
208
    return paContinue;
209
}
210

    
211
/*******************************************************************/
212
static int paqaCheckMultipleSuggested( PaDeviceIndex deviceIndex, int isInput )
213
{
214
    int i;
215
    int numLoops = 10;
216
    PaError err;
217
    PaStream *stream;
218
    PaStreamParameters streamParameters;
219
    const PaStreamInfo* streamInfo;
220
    double lowLatency;
221
    double highLatency;
222
    double finalLatency;
223
    double sampleRate = SAMPLE_RATE;
224
    const PaDeviceInfo *pdi = Pa_GetDeviceInfo( deviceIndex );
225
    double previousLatency = 0.0;
226
    int numChannels = 1;
227
    float toleranceRatio = 1.0;
228

    
229
    printf("------------------------ paqaCheckMultipleSuggested - %s\n",
230
           (isInput ? "INPUT" : "OUTPUT") );
231
    if( isInput )
232
    {
233
        lowLatency = pdi->defaultLowInputLatency;
234
        highLatency = pdi->defaultHighInputLatency;
235
        numChannels = (pdi->maxInputChannels < 2) ? 1 : 2;
236
    }
237
    else
238
    {
239
        lowLatency = pdi->defaultLowOutputLatency;
240
        highLatency = pdi->defaultHighOutputLatency;
241
        numChannels = (pdi->maxOutputChannels < 2) ? 1 : 2;
242
    }
243
    streamParameters.channelCount = numChannels;
244
    streamParameters.device = deviceIndex;
245
    streamParameters.hostApiSpecificStreamInfo = NULL;
246
    streamParameters.sampleFormat = paFloat32;
247
    sampleRate = pdi->defaultSampleRate;
248
    
249
    printf(" lowLatency  = %g\n", lowLatency );
250
    printf(" highLatency = %g\n", highLatency );
251
    printf(" numChannels = %d\n", numChannels );
252
    printf(" sampleRate  = %g\n", sampleRate );
253
    
254
    if( (highLatency - lowLatency) < 0.001 )
255
    {
256
        numLoops = 1;
257
    }
258

    
259
    for( i=0; i<numLoops; i++ )
260
    {   
261
        if( numLoops == 1 )
262
            streamParameters.suggestedLatency = lowLatency;
263
        else
264
            streamParameters.suggestedLatency = lowLatency + ((highLatency - lowLatency) * i /(numLoops - 1));
265
        
266
        printf("   suggestedLatency[%d] = %6.4f\n", i, streamParameters.suggestedLatency );
267
    
268
        err = Pa_OpenStream(
269
                            &stream,
270
                            (isInput ? &streamParameters : NULL),
271
                            (isInput ? NULL : &streamParameters),
272
                            sampleRate,
273
                            paFramesPerBufferUnspecified,
274
                            paClipOff,      /* we won't output out of range samples so don't bother clipping them */
275
                            paqaNoopCallback,
276
                            NULL );
277
        if( err != paNoError ) goto error;
278
        
279
        streamInfo = Pa_GetStreamInfo( stream );
280
        
281
        err = Pa_CloseStream( stream );
282
        
283
        if( isInput )
284
        {
285
            finalLatency = streamInfo->inputLatency;
286
        }
287
        else
288
        {
289
            finalLatency = streamInfo->outputLatency;
290
        }
291
        printf("          finalLatency = %6.4f\n", finalLatency );
292
        /* For the default low & high latency values, expect quite close; for other requested
293
         * values, at worst the next power-of-2 may result (eg 513 -> 1024) */
294
        toleranceRatio = ( (i == 0) || (i == ( numLoops - 1 )) ) ? 0.1 : 1.0;
295
        QA_ASSERT_CLOSE( "final latency should be close to suggested latency",
296
                        streamParameters.suggestedLatency, finalLatency, (streamParameters.suggestedLatency * toleranceRatio) );
297
        if( i == 0 )
298
        {
299
            previousLatency = finalLatency;
300
        }
301
    }
302

    
303
    if( numLoops > 1 )
304
    {
305
        QA_ASSERT_TRUE( " final latency should increase with suggested latency", (finalLatency > previousLatency) );
306
    }
307

    
308
    return 0;
309
error:
310
    return -1;
311
}
312

    
313
/*******************************************************************/
314
static int paqaVerifySuggestedLatency( void )
315
{
316
    PaDeviceIndex id;
317
    int result = 0;
318
    const PaDeviceInfo *pdi;
319
    int numDevices = Pa_GetDeviceCount();
320
    
321
    printf("\n ------------------------ paqaVerifySuggestedLatency\n");
322
    for( id=0; id<numDevices; id++ )            /* Iterate through all devices. */
323
    {
324
        pdi = Pa_GetDeviceInfo( id );
325
        printf("\nUsing device #%d: '%s' (%s)\n", id, pdi->name, Pa_GetHostApiInfo(pdi->hostApi)->name);
326
        if( pdi->maxOutputChannels > 0 )
327
        {
328
            if( paqaCheckMultipleSuggested( id, 0 ) < 0 )
329
            {
330
                printf("OUTPUT CHECK FAILED !!! #%d: '%s'\n", id, pdi->name);
331
                result -= 1;
332
            }
333
        }
334
        if( pdi->maxInputChannels > 0 )
335
        {
336
            if( paqaCheckMultipleSuggested( id, 1 ) < 0 )
337
            {
338
                printf("INPUT CHECK FAILED !!! #%d: '%s'\n", id, pdi->name);
339
                result -= 1;
340
            }
341
        }
342
    }
343
    return result;
344
}
345

    
346
/*******************************************************************/
347
static int paqaVerifyDeviceInfoLatency( void )
348
{
349
    PaDeviceIndex id;
350
    const PaDeviceInfo *pdi;
351
    int numDevices = Pa_GetDeviceCount();
352
    
353
    printf("\n ------------------------ paqaVerifyDeviceInfoLatency\n");
354
    for( id=0; id<numDevices; id++ ) /* Iterate through all devices. */
355
    {
356
        pdi = Pa_GetDeviceInfo( id );
357
        printf("Using device #%d: '%s' (%s)\n", id, pdi->name, Pa_GetHostApiInfo(pdi->hostApi)->name);
358
        if( pdi->maxOutputChannels > 0 )
359
        {
360
            printf("  Output defaultLowOutputLatency  = %f seconds\n", pdi->defaultLowOutputLatency);
361
            printf("  Output defaultHighOutputLatency = %f seconds\n", pdi->defaultHighOutputLatency);
362
            QA_ASSERT_TRUE( "defaultLowOutputLatency should be > 0", (pdi->defaultLowOutputLatency > 0.0) );
363
            QA_ASSERT_TRUE( "defaultHighOutputLatency should be > 0", (pdi->defaultHighOutputLatency > 0.0) );
364
            QA_ASSERT_TRUE( "defaultHighOutputLatency should be >= Low", (pdi->defaultHighOutputLatency >= pdi->defaultLowOutputLatency) );
365
        }
366
        if( pdi->maxInputChannels > 0 )
367
        {
368
            printf("  Input defaultLowInputLatency  = %f seconds\n", pdi->defaultLowInputLatency);
369
            printf("  Input defaultHighInputLatency = %f seconds\n", pdi->defaultHighInputLatency);
370
            QA_ASSERT_TRUE( "defaultLowInputLatency should be > 0", (pdi->defaultLowInputLatency > 0.0) );
371
            QA_ASSERT_TRUE( "defaultHighInputLatency should be > 0", (pdi->defaultHighInputLatency > 0.0) );
372
            QA_ASSERT_TRUE( "defaultHighInputLatency should be >= Low", (pdi->defaultHighInputLatency >= pdi->defaultLowInputLatency) );
373
        }
374
    }
375
    return 0;
376
error:
377
    return -1;
378
}
379

    
380

    
381

    
382
/*******************************************************************/
383
int main(void);
384
int main(void)
385
{
386
    PaStreamParameters outputParameters;
387
    PaError err;
388
    paTestData data;
389
    const PaDeviceInfo *deviceInfo;
390
    int i;
391
    int framesPerBuffer;
392
    double sampleRate = SAMPLE_RATE;
393
    
394
    printf("\nPortAudio QA: investigate output latency.\n");
395

    
396
    /* initialise sinusoidal wavetable */
397
    for( i=0; i<TABLE_SIZE; i++ )
398
    {
399
        data.sine[i] = (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. );
400
    }
401
    data.left_phase = data.right_phase = 0;
402
    
403
    err = Pa_Initialize();
404
    if( err != paNoError ) goto error;
405
    
406
    /* Run self tests. */
407
    if( paqaVerifyDeviceInfoLatency() < 0 ) goto error;
408
    
409
    if( paqaVerifySuggestedLatency() < 0 ) goto error;
410
    
411
    outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
412
    if (outputParameters.device == paNoDevice) {
413
      fprintf(stderr,"Error: No default output device.\n");
414
      goto error;
415
    }
416

    
417
    printf("\n\nNow running Audio Output Tests...\n");
418
    printf("-------------------------------------\n");
419

    
420
    outputParameters.channelCount = 2;       /* stereo output */
421
    outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output */
422
    deviceInfo = Pa_GetDeviceInfo( outputParameters.device );
423
    printf("Using device #%d: '%s' (%s)\n", outputParameters.device, deviceInfo->name, Pa_GetHostApiInfo(deviceInfo->hostApi)->name);
424
    printf("Device info: defaultLowOutputLatency  = %f seconds\n", deviceInfo->defaultLowOutputLatency);
425
    printf("Device info: defaultHighOutputLatency = %f seconds\n", deviceInfo->defaultHighOutputLatency);
426
    sampleRate = deviceInfo->defaultSampleRate;
427
    printf("Sample Rate for following tests: %g\n", sampleRate);
428
    outputParameters.hostApiSpecificStreamInfo = NULL;
429
    printf("-------------------------------------\n");
430

    
431
    // Try to use a small buffer that is smaller than we think the device can handle.
432
    // Try to force combining multiple user buffers into a host buffer.
433
    printf("------------- Try a very small buffer.\n");
434
    framesPerBuffer = 9;
435
    outputParameters.suggestedLatency = deviceInfo->defaultLowOutputLatency;
436
    err = paqaCheckLatency( &outputParameters, &data, sampleRate, framesPerBuffer );
437
    if( err != paNoError ) goto error;
438
    
439
    printf("------------- 64 frame buffer with 1.1 * defaultLow latency.\n");
440
    framesPerBuffer = 64;
441
    outputParameters.suggestedLatency = deviceInfo->defaultLowOutputLatency * 1.1;
442
    err = paqaCheckLatency( &outputParameters, &data, sampleRate, framesPerBuffer );
443
    if( err != paNoError ) goto error;
444
    
445
    // Try to create a huge buffer that is bigger than the allowed device maximum.
446
    printf("------------- Try a huge buffer.\n");
447
    framesPerBuffer = 16*1024;
448
    outputParameters.suggestedLatency = ((double)framesPerBuffer) / sampleRate; // approximate
449
    err = paqaCheckLatency( &outputParameters, &data, sampleRate, framesPerBuffer );
450
    if( err != paNoError ) goto error;
451
    
452
    printf("------------- Try suggestedLatency = 0.0\n");
453
    outputParameters.suggestedLatency = 0.0;
454
    err = paqaCheckLatency( &outputParameters, &data, sampleRate, paFramesPerBufferUnspecified );
455
    if( err != paNoError ) goto error;
456
    
457
    printf("------------- Try suggestedLatency = defaultLowOutputLatency\n");
458
    outputParameters.suggestedLatency = deviceInfo->defaultLowOutputLatency;
459
    err = paqaCheckLatency( &outputParameters, &data, sampleRate, paFramesPerBufferUnspecified );
460
    if( err != paNoError ) goto error;
461
    
462
    printf("------------- Try suggestedLatency = defaultHighOutputLatency\n");
463
    outputParameters.suggestedLatency = deviceInfo->defaultHighOutputLatency;
464
    err = paqaCheckLatency( &outputParameters, &data, sampleRate, paFramesPerBufferUnspecified );
465
    if( err != paNoError ) goto error;
466
    
467
    printf("------------- Try suggestedLatency = defaultHighOutputLatency * 4\n");
468
    outputParameters.suggestedLatency = deviceInfo->defaultHighOutputLatency * 4;
469
    err = paqaCheckLatency( &outputParameters, &data, sampleRate, paFramesPerBufferUnspecified );
470
    if( err != paNoError ) goto error;
471
    
472
    Pa_Terminate();
473
    printf("SUCCESS - test finished.\n");
474
    return err;
475
    
476
error:
477
    Pa_Terminate();
478
    fprintf( stderr, "ERROR - test failed.\n" );
479
    fprintf( stderr, "Error number: %d\n", err );
480
    fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
481
    return err;
482
}