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

History | View | Annotate | Download (18.3 KB)

1
/*
2
 * $Id: $
3
 * Portable Audio I/O Library
4
 * Windows MME low level buffer user guided parameters search
5
 *
6
 * Copyright (c) 2010 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 <time.h>
41
#include <math.h>
42

    
43
#define  _WIN32_WINNT 0x0501 /* for GetNativeSystemInfo */ 
44
#include <windows.h>    /* required when using pa_win_wmme.h */
45
#include <mmsystem.h>   /* required when using pa_win_wmme.h */
46

    
47
#include <conio.h>      /* for _getch */
48

    
49

    
50
#include "portaudio.h"
51
#include "pa_win_wmme.h"
52

    
53

    
54
#define DEFAULT_SAMPLE_RATE             (44100.)
55

    
56
#ifndef M_PI
57
#define M_PI  (3.14159265)
58
#endif
59

    
60
#define TABLE_SIZE              (2048)
61

    
62
#define CHANNEL_COUNT           (2)
63

    
64

    
65
/* seach parameters. we test all buffer counts in this range */
66
#define MIN_WMME_BUFFER_COUNT        (2)
67
#define MAX_WMME_BUFFER_COUNT        (12)
68

    
69

    
70
/*******************************************************************/
71
/* functions to query and print Windows version information */
72

    
73
typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);
74

    
75
LPFN_ISWOW64PROCESS fnIsWow64Process;
76

    
77
static BOOL IsWow64()
78
{
79
    BOOL bIsWow64 = FALSE;
80

    
81
    //IsWow64Process is not available on all supported versions of Windows.
82
    //Use GetModuleHandle to get a handle to the DLL that contains the function
83
    //and GetProcAddress to get a pointer to the function if available.
84

    
85
    fnIsWow64Process = (LPFN_ISWOW64PROCESS) GetProcAddress(
86
        GetModuleHandle(TEXT("kernel32")),"IsWow64Process" );
87

    
88
    if(NULL != fnIsWow64Process)
89
    {
90
        if (!fnIsWow64Process(GetCurrentProcess(),&bIsWow64))
91
        {
92
            //handle error
93
        }
94
    }
95
    return bIsWow64;
96
}
97

    
98
static void printWindowsVersionInfo( FILE *fp )
99
{
100
    OSVERSIONINFOEX osVersionInfoEx;
101
    SYSTEM_INFO systemInfo;
102
    const char *osName = "Unknown";
103
    const char *osProductType = "";
104
    const char *processorArchitecture = "Unknown";
105

    
106
    memset( &osVersionInfoEx, 0, sizeof(OSVERSIONINFOEX) );
107
    osVersionInfoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
108
    GetVersionEx( &osVersionInfoEx );
109

    
110
    
111
    if( osVersionInfoEx.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS ){
112
        switch( osVersionInfoEx.dwMinorVersion ){
113
            case 0: osName = "Windows 95"; break;
114
            case 10: osName = "Windows 98"; break;  // could also be 98SE (I've seen code discriminate based 
115
                                                    // on osInfo.Version.Revision.ToString() == "2222A")
116
            case 90: osName = "Windows Me"; break;
117
        }
118
    }else if( osVersionInfoEx.dwPlatformId == VER_PLATFORM_WIN32_NT ){
119
        switch( osVersionInfoEx.dwMajorVersion ){
120
            case 3: osName = "Windows NT 3.51"; break;
121
            case 4: osName = "Windows NT 4.0"; break;
122
            case 5: switch( osVersionInfoEx.dwMinorVersion ){
123
                        case 0: osName = "Windows 2000"; break;
124
                        case 1: osName = "Windows XP"; break;
125
                        case 2:
126
                            if( osVersionInfoEx.wSuiteMask & 0x00008000 /*VER_SUITE_WH_SERVER*/ ){
127
                                osName = "Windows Home Server";
128
                            }else{
129
                                if( osVersionInfoEx.wProductType == VER_NT_WORKSTATION ){
130
                                    osName = "Windows XP Professional x64 Edition (?)";
131
                                }else{
132
                                    if( GetSystemMetrics(/*SM_SERVERR2*/89) == 0 )
133
                                        osName = "Windows Server 2003";
134
                                    else
135
                                        osName = "Windows Server 2003 R2";
136
                                }
137
                            }break;
138
                    }break;
139
            case 6:switch( osVersionInfoEx.dwMinorVersion ){
140
                        case 0: 
141
                            if( osVersionInfoEx.wProductType == VER_NT_WORKSTATION )
142
                                osName = "Windows Vista";
143
                            else
144
                                osName = "Windows Server 2008";
145
                            break;
146
                        case 1: 
147
                            if( osVersionInfoEx.wProductType == VER_NT_WORKSTATION )
148
                                osName = "Windows 7";
149
                            else
150
                                osName = "Windows Server 2008 R2";
151
                            break;
152
                    }break;
153
        }
154
    }
155

    
156
    if(osVersionInfoEx.dwMajorVersion == 4)
157
    {
158
        if(osVersionInfoEx.wProductType == VER_NT_WORKSTATION)
159
            osProductType = "Workstation";
160
        else if(osVersionInfoEx.wProductType == VER_NT_SERVER)
161
            osProductType = "Server";
162
    }
163
    else if(osVersionInfoEx.dwMajorVersion == 5)
164
    {
165
        if(osVersionInfoEx.wProductType == VER_NT_WORKSTATION)
166
        {
167
            if((osVersionInfoEx.wSuiteMask & VER_SUITE_PERSONAL) == VER_SUITE_PERSONAL)
168
                osProductType = "Home Edition"; // Windows XP Home Edition
169
            else
170
                osProductType = "Professional"; // Windows XP / Windows 2000 Professional
171
        }
172
        else if(osVersionInfoEx.wProductType == VER_NT_SERVER)
173
        {
174
            if(osVersionInfoEx.dwMinorVersion == 0) 
175
            {
176
                if((osVersionInfoEx.wSuiteMask & VER_SUITE_DATACENTER) == VER_SUITE_DATACENTER)
177
                    osProductType = "Datacenter Server"; // Windows 2000 Datacenter Server
178
                else if((osVersionInfoEx.wSuiteMask & VER_SUITE_ENTERPRISE) == VER_SUITE_ENTERPRISE)
179
                    osProductType = "Advanced Server"; // Windows 2000 Advanced Server
180
                else
181
                    osProductType = "Server"; // Windows 2000 Server
182
            }
183
        }
184
        else
185
        {
186
            if((osVersionInfoEx.wSuiteMask & VER_SUITE_DATACENTER) == VER_SUITE_DATACENTER)
187
                osProductType = "Datacenter Edition"; // Windows Server 2003 Datacenter Edition
188
            else if((osVersionInfoEx.wSuiteMask & VER_SUITE_ENTERPRISE) == VER_SUITE_ENTERPRISE)
189
                osProductType = "Enterprise Edition"; // Windows Server 2003 Enterprise Edition
190
            else if((osVersionInfoEx.wSuiteMask & VER_SUITE_BLADE) == VER_SUITE_BLADE)
191
                osProductType = "Web Edition"; // Windows Server 2003 Web Edition
192
            else
193
                osProductType = "Standard Edition"; // Windows Server 2003 Standard Edition
194
        }
195
    }
196

    
197
    memset( &systemInfo, 0, sizeof(SYSTEM_INFO) );
198
    GetNativeSystemInfo( &systemInfo );
199

    
200
    if( systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL )
201
        processorArchitecture = "x86";
202
    else if( systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64 )
203
        processorArchitecture = "x64";
204
    else if( systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64 )
205
        processorArchitecture = "Itanium";
206

    
207

    
208
    fprintf( fp, "OS name and edition: %s %s\n", osName, osProductType );
209
    fprintf( fp, "OS version: %d.%d.%d %S\n", 
210
                osVersionInfoEx.dwMajorVersion, osVersionInfoEx.dwMinorVersion, 
211
                osVersionInfoEx.dwBuildNumber, osVersionInfoEx.szCSDVersion );
212
    fprintf( fp, "Processor architecture: %s\n", processorArchitecture );
213
    fprintf( fp, "WoW64 process: %s\n", IsWow64() ? "Yes" : "No" );
214
}
215

    
216
static void printTimeAndDate( FILE *fp )
217
{
218
    struct tm *local;
219
    time_t t;
220

    
221
    t = time(NULL);
222
    local = localtime(&t);
223
    fprintf(fp, "Local time and date: %s", asctime(local));
224
    local = gmtime(&t);
225
    fprintf(fp, "UTC time and date: %s", asctime(local));
226
}
227

    
228
/*******************************************************************/
229

    
230
typedef struct
231
{
232
    float sine[TABLE_SIZE];
233
        double phase;
234
    double phaseIncrement;
235
    volatile int fadeIn;
236
    volatile int fadeOut;
237
    double amp;
238
}
239
paTestData;
240

    
241
static paTestData data;
242

    
243
/* This routine will be called by the PortAudio engine when audio is needed.
244
** It may called at interrupt level on some machines so don't do anything
245
** that could mess up the system like calling malloc() or free().
246
*/
247
static int patestCallback( const void *inputBuffer, void *outputBuffer,
248
                            unsigned long framesPerBuffer,
249
                            const PaStreamCallbackTimeInfo* timeInfo,
250
                            PaStreamCallbackFlags statusFlags,
251
                            void *userData )
252
{
253
    paTestData *data = (paTestData*)userData;
254
    float *out = (float*)outputBuffer;
255
    unsigned long i,j;
256

    
257
    (void) timeInfo; /* Prevent unused variable warnings. */
258
    (void) statusFlags;
259
    (void) inputBuffer;
260
    
261
    for( i=0; i<framesPerBuffer; i++ )
262
    {
263
        float x = data->sine[(int)data->phase];
264
        data->phase += data->phaseIncrement;
265
        if( data->phase >= TABLE_SIZE ){
266
                        data->phase -= TABLE_SIZE;
267
                }
268

    
269
        x *= data->amp;
270
        if( data->fadeIn ){
271
            data->amp += .001;
272
            if( data->amp >= 1. )
273
                data->fadeIn = 0;
274
        }else if( data->fadeOut ){
275
            if( data->amp > 0 )
276
                data->amp -= .001;
277
        }
278

    
279
                for( j = 0; j < CHANNEL_COUNT; ++j ){
280
            *out++ = x;
281
                }
282
        }
283
    
284
    if( data->amp > 0 )
285
        return paContinue;
286
    else
287
        return paComplete;
288
}
289

    
290

    
291
#define YES     1
292
#define NO      0
293

    
294

    
295
static int playUntilKeyPress( int deviceIndex, float sampleRate, 
296
                             int framesPerUserBuffer, int framesPerWmmeBuffer, int wmmeBufferCount )
297
{
298
    PaStreamParameters outputParameters;
299
    PaWinMmeStreamInfo wmmeStreamInfo;
300
    PaStream *stream;
301
    PaError err;
302
    int c;
303

    
304
    outputParameters.device = deviceIndex;
305
    outputParameters.channelCount = CHANNEL_COUNT;
306
    outputParameters.sampleFormat = paFloat32; /* 32 bit floating point processing */
307
    outputParameters.suggestedLatency = 0; /*Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;*/
308
    outputParameters.hostApiSpecificStreamInfo = NULL;
309

    
310
    wmmeStreamInfo.size = sizeof(PaWinMmeStreamInfo);
311
    wmmeStreamInfo.hostApiType = paMME; 
312
    wmmeStreamInfo.version = 1;
313
    wmmeStreamInfo.flags = paWinMmeUseLowLevelLatencyParameters | paWinMmeDontThrottleOverloadedProcessingThread;
314
    wmmeStreamInfo.framesPerBuffer = framesPerWmmeBuffer;
315
    wmmeStreamInfo.bufferCount = wmmeBufferCount;
316
    outputParameters.hostApiSpecificStreamInfo = &wmmeStreamInfo;
317

    
318
    err = Pa_OpenStream(
319
              &stream,
320
              NULL, /* no input */
321
              &outputParameters,
322
              sampleRate,
323
              framesPerUserBuffer,
324
              paClipOff | paPrimeOutputBuffersUsingStreamCallback,      /* we won't output out of range samples so don't bother clipping them */
325
              patestCallback,
326
              &data );
327
    if( err != paNoError ) goto error;
328

    
329
    data.amp = 0;
330
    data.fadeIn = 1;
331
    data.fadeOut = 0;
332
    data.phase = 0;
333
    data.phaseIncrement = 15 + ((rand()%100) / 10); // randomise pitch
334

    
335
    err = Pa_StartStream( stream );
336
    if( err != paNoError ) goto error;
337

    
338

    
339
    do{
340
        printf( "Trying buffer size %d.\nIf it sounds smooth (without clicks or glitches) press 'y', if it sounds bad press 'n' ('q' to quit)\n", framesPerWmmeBuffer );
341
        c = tolower(_getch());
342
        if( c == 'q' ){
343
            Pa_Terminate();
344
            exit(0);
345
        }
346
    }while( c != 'y' && c != 'n' );
347

    
348
    data.fadeOut = 1;
349
    while( Pa_IsStreamActive(stream) == 1 )
350
        Pa_Sleep( 100 );
351

    
352
    err = Pa_StopStream( stream );
353
    if( err != paNoError ) goto error;
354

    
355
    err = Pa_CloseStream( stream );
356
    if( err != paNoError ) goto error;
357

    
358
    return (c == 'y') ? YES : NO;
359

    
360
error:
361
    return err;
362
}
363

    
364
/*******************************************************************/
365
static void usage( int wmmeHostApiIndex )
366
{
367
    int i;
368

    
369
    fprintf( stderr, "PortAudio WMME output latency user guided test\n" );
370
    fprintf( stderr, "Usage: x.exe mme-device-index [sampleRate [min-buffer-count max-buffer-count]]\n" );
371
    fprintf( stderr, "Invalid device index. Use one of these:\n" );
372
    for( i=0; i < Pa_GetDeviceCount(); ++i ){
373

    
374
        if( Pa_GetDeviceInfo(i)->hostApi == wmmeHostApiIndex && Pa_GetDeviceInfo(i)->maxOutputChannels > 0  )
375
            fprintf( stderr, "%d (%s)\n", i, Pa_GetDeviceInfo(i)->name );
376
    }
377
    Pa_Terminate();
378
    exit(-1);
379
}
380

    
381
/*
382
    ideas: 
383
        o- could be testing with 80% CPU load
384
        o- could test with different channel counts
385
*/
386

    
387
int main(int argc, char* argv[])
388
{
389
    PaError err;
390
    int i;
391
    int deviceIndex;
392
    int wmmeBufferCount, wmmeBufferSize, smallestWorkingBufferSize;
393
    int smallestWorkingBufferingLatencyFrames;
394
    int min, max, mid;
395
    int testResult;
396
    FILE *resultsFp;
397
    int wmmeHostApiIndex;
398
    const PaHostApiInfo *wmmeHostApiInfo;
399
    double sampleRate = DEFAULT_SAMPLE_RATE;
400
    int wmmeMinBufferCount = MIN_WMME_BUFFER_COUNT;
401
    int wmmeMaxBufferCount = MAX_WMME_BUFFER_COUNT;
402

    
403
    err = Pa_Initialize();
404
    if( err != paNoError ) goto error;
405

    
406
    wmmeHostApiIndex = Pa_HostApiTypeIdToHostApiIndex( paMME );
407
    wmmeHostApiInfo = Pa_GetHostApiInfo( wmmeHostApiIndex );
408

    
409
    if( argc > 5 )
410
        usage(wmmeHostApiIndex);
411

    
412
        deviceIndex = wmmeHostApiInfo->defaultOutputDevice;
413
        if( argc >= 2 ){
414
        deviceIndex = -1;
415
                if( sscanf( argv[1], "%d", &deviceIndex ) != 1 )
416
            usage(wmmeHostApiIndex);
417
        if( deviceIndex < 0 || deviceIndex >= Pa_GetDeviceCount() || Pa_GetDeviceInfo(deviceIndex)->hostApi != wmmeHostApiIndex ){
418
            usage(wmmeHostApiIndex);
419
        }
420
        }
421

    
422
    printf( "Using device id %d (%s)\n", deviceIndex, Pa_GetDeviceInfo(deviceIndex)->name );
423

    
424
    if( argc >= 3 ){
425
        if( sscanf( argv[2], "%lf", &sampleRate ) != 1 )
426
            usage(wmmeHostApiIndex);
427
    }
428

    
429
    printf( "Testing with sample rate %f.\n", (float)sampleRate );
430

    
431
    if( argc == 4 ){
432
        if( sscanf( argv[3], "%d", &wmmeMinBufferCount ) != 1 )
433
            usage(wmmeHostApiIndex);
434
        wmmeMaxBufferCount = wmmeMinBufferCount;
435
    }
436

    
437
    if( argc == 5 ){
438
        if( sscanf( argv[3], "%d", &wmmeMinBufferCount ) != 1 )
439
            usage(wmmeHostApiIndex);
440
        if( sscanf( argv[4], "%d", &wmmeMaxBufferCount ) != 1 )
441
            usage(wmmeHostApiIndex);
442
    }
443

    
444
    printf( "Testing buffer counts from %d to %d\n", wmmeMinBufferCount, wmmeMaxBufferCount );
445

    
446

    
447
    /* initialise sinusoidal wavetable */
448
    for( i=0; i<TABLE_SIZE; i++ )
449
    {
450
        data.sine[i] = (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. );
451
    }
452

    
453
        data.phase = 0;
454

    
455
    resultsFp = fopen( "results.txt", "at" );
456
    fprintf( resultsFp, "*** WMME smallest working output buffer sizes\n" );
457

    
458
    printTimeAndDate( resultsFp );
459
    printWindowsVersionInfo( resultsFp );
460
    
461
    fprintf( resultsFp, "audio device: %s\n", Pa_GetDeviceInfo( deviceIndex )->name );
462
    fflush( resultsFp );
463

    
464
    fprintf( resultsFp, "Sample rate: %f\n", (float)sampleRate );
465
    fprintf( resultsFp, "Buffer count, Smallest working buffer size (frames), Smallest working buffering latency (frames), Smallest working buffering latency (Seconds)\n" );
466

    
467
    for( wmmeBufferCount = wmmeMinBufferCount; wmmeBufferCount <= wmmeMaxBufferCount; ++wmmeBufferCount ){
468
 
469
        printf( "Test %d of %d\n", (wmmeBufferCount - wmmeMinBufferCount) + 1, (wmmeMaxBufferCount-wmmeMinBufferCount) + 1 );
470
        printf( "Testing with %d buffers...\n", wmmeBufferCount );
471

    
472
        /*
473
            Binary search after Niklaus Wirth
474
            from http://en.wikipedia.org/wiki/Binary_search_algorithm#The_algorithm
475
         */
476
        min = 1;
477
        max = (int)((sampleRate * .3) / (wmmeBufferCount-1)); //8192;    /* we assume that this size works 300ms */
478
        smallestWorkingBufferSize = 0;
479

    
480
        do{
481
            mid = min + ((max - min) / 2);
482

    
483
            wmmeBufferSize = mid;
484
            testResult = playUntilKeyPress( deviceIndex, sampleRate, wmmeBufferSize, wmmeBufferSize, wmmeBufferCount );
485

    
486
            if( testResult == YES ){
487
                max = mid - 1;
488
                smallestWorkingBufferSize = wmmeBufferSize;
489
            }else{
490
                min = mid + 1;
491
            }
492
             
493
        }while( (min <= max) && (testResult == YES || testResult == NO) );
494

    
495
        smallestWorkingBufferingLatencyFrames = smallestWorkingBufferSize * (wmmeBufferCount - 1);
496

    
497
        printf( "Smallest working buffer size for %d buffers is: %d\n", wmmeBufferCount, smallestWorkingBufferSize );
498
        printf( "Corresponding to buffering latency of %d frames, or %f seconds.\n", smallestWorkingBufferingLatencyFrames, smallestWorkingBufferingLatencyFrames / sampleRate );
499

    
500
        fprintf( resultsFp, "%d, %d, %d, %f\n", wmmeBufferCount, smallestWorkingBufferSize, smallestWorkingBufferingLatencyFrames, smallestWorkingBufferingLatencyFrames / sampleRate );
501
        fflush( resultsFp );
502
    }
503

    
504
    fprintf( resultsFp, "###\n" );
505
    fclose( resultsFp );
506
    
507
    Pa_Terminate();
508
    printf("Test finished.\n");
509
    
510
    return err;
511
error:
512
    Pa_Terminate();
513
    fprintf( stderr, "An error occured while using the portaudio stream\n" );
514
    fprintf( stderr, "Error number: %d\n", err );
515
    fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
516
    return err;
517
}
518