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

History | View | Annotate | Download (17.8 KB)

1
/*
2
 * $Id: $
3
 * Portable Audio I/O Library
4
 * Windows DirectSound low level buffer user guided parameters search
5
 *
6
 * Copyright (c) 2010-2011 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>
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_ds.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
/*******************************************************************/
66
/* functions to query and print Windows version information */
67

    
68
typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);
69

    
70
LPFN_ISWOW64PROCESS fnIsWow64Process;
71

    
72
static BOOL IsWow64()
73
{
74
    BOOL bIsWow64 = FALSE;
75

    
76
    //IsWow64Process is not available on all supported versions of Windows.
77
    //Use GetModuleHandle to get a handle to the DLL that contains the function
78
    //and GetProcAddress to get a pointer to the function if available.
79

    
80
    fnIsWow64Process = (LPFN_ISWOW64PROCESS) GetProcAddress(
81
        GetModuleHandle(TEXT("kernel32")),"IsWow64Process" );
82

    
83
    if(NULL != fnIsWow64Process)
84
    {
85
        if (!fnIsWow64Process(GetCurrentProcess(),&bIsWow64))
86
        {
87
            //handle error
88
        }
89
    }
90
    return bIsWow64;
91
}
92

    
93
static void printWindowsVersionInfo( FILE *fp )
94
{
95
    OSVERSIONINFOEX osVersionInfoEx;
96
    SYSTEM_INFO systemInfo;
97
    const char *osName = "Unknown";
98
    const char *osProductType = "";
99
    const char *processorArchitecture = "Unknown";
100

    
101
    memset( &osVersionInfoEx, 0, sizeof(OSVERSIONINFOEX) );
102
    osVersionInfoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
103
    GetVersionEx( &osVersionInfoEx );
104

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

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

    
192
    memset( &systemInfo, 0, sizeof(SYSTEM_INFO) );
193
    GetNativeSystemInfo( &systemInfo );
194

    
195
    if( systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL )
196
        processorArchitecture = "x86";
197
    else if( systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64 )
198
        processorArchitecture = "x64";
199
    else if( systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64 )
200
        processorArchitecture = "Itanium";
201

    
202

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

    
211
static void printTimeAndDate( FILE *fp )
212
{
213
    struct tm *local;
214
    time_t t;
215

    
216
    t = time(NULL);
217
    local = localtime(&t);
218
    fprintf(fp, "Local time and date: %s", asctime(local));
219
    local = gmtime(&t);
220
    fprintf(fp, "UTC time and date: %s", asctime(local));
221
}
222

    
223
/*******************************************************************/
224

    
225
typedef struct
226
{
227
    float sine[TABLE_SIZE];
228
        double phase;
229
    double phaseIncrement;
230
    volatile int fadeIn;
231
    volatile int fadeOut;
232
    double amp;
233
}
234
paTestData;
235

    
236
static paTestData data;
237

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

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

    
264
        x *= data->amp;
265
        if( data->fadeIn ){
266
            data->amp += .001;
267
            if( data->amp >= 1. )
268
                data->fadeIn = 0;
269
        }else if( data->fadeOut ){
270
            if( data->amp > 0 )
271
                data->amp -= .001;
272
        }
273

    
274
                for( j = 0; j < CHANNEL_COUNT; ++j ){
275
            *out++ = x;
276
                }
277
        }
278
    
279
    if( data->amp > 0 )
280
        return paContinue;
281
    else
282
        return paComplete;
283
}
284

    
285

    
286
#define YES     1
287
#define NO      0
288

    
289

    
290
static int playUntilKeyPress( int deviceIndex, float sampleRate, 
291
                             int framesPerUserBuffer, int framesPerDSoundBuffer )
292
{
293
    PaStreamParameters outputParameters;
294
    PaWinDirectSoundStreamInfo directSoundStreamInfo;
295
    PaStream *stream;
296
    PaError err;
297
    int c;
298

    
299
    outputParameters.device = deviceIndex;
300
    outputParameters.channelCount = CHANNEL_COUNT;
301
    outputParameters.sampleFormat = paFloat32; /* 32 bit floating point processing */
302
    outputParameters.suggestedLatency = 0; /*Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;*/
303
    outputParameters.hostApiSpecificStreamInfo = NULL;
304

    
305
    directSoundStreamInfo.size = sizeof(PaWinDirectSoundStreamInfo);
306
    directSoundStreamInfo.hostApiType = paDirectSound; 
307
    directSoundStreamInfo.version = 2;
308
    directSoundStreamInfo.flags = paWinDirectSoundUseLowLevelLatencyParameters;
309
    directSoundStreamInfo.framesPerBuffer = framesPerDSoundBuffer;
310
    outputParameters.hostApiSpecificStreamInfo = &directSoundStreamInfo;
311

    
312
    err = Pa_OpenStream(
313
              &stream,
314
              NULL, /* no input */
315
              &outputParameters,
316
              sampleRate,
317
              framesPerUserBuffer,
318
              paClipOff | paPrimeOutputBuffersUsingStreamCallback,      /* we won't output out of range samples so don't bother clipping them */
319
              patestCallback,
320
              &data );
321
    if( err != paNoError ) goto error;
322

    
323
    data.amp = 0;
324
    data.fadeIn = 1;
325
    data.fadeOut = 0;
326
    data.phase = 0;
327
    data.phaseIncrement = 15 + ((rand()%100) / 10); // randomise pitch
328

    
329
    err = Pa_StartStream( stream );
330
    if( err != paNoError ) goto error;
331

    
332

    
333
    do{
334
        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", framesPerDSoundBuffer );
335
        c = tolower(_getch());
336
        if( c == 'q' ){
337
            Pa_Terminate();
338
            exit(0);
339
        }
340
    }while( c != 'y' && c != 'n' );
341

    
342
    data.fadeOut = 1;
343
    while( Pa_IsStreamActive(stream) == 1 )
344
        Pa_Sleep( 100 );
345

    
346
    err = Pa_StopStream( stream );
347
    if( err != paNoError ) goto error;
348

    
349
    err = Pa_CloseStream( stream );
350
    if( err != paNoError ) goto error;
351

    
352
    return (c == 'y') ? YES : NO;
353

    
354
error:
355
    return err;
356
}
357

    
358
/*******************************************************************/
359
static void usage( int dsoundHostApiIndex )
360
{
361
    int i;
362

    
363
    fprintf( stderr, "PortAudio DirectSound output latency user guided test\n" );
364
    fprintf( stderr, "Usage: x.exe dsound-device-index [sampleRate]\n" );
365
    fprintf( stderr, "Invalid device index. Use one of these:\n" );
366
    for( i=0; i < Pa_GetDeviceCount(); ++i ){
367

    
368
        if( Pa_GetDeviceInfo(i)->hostApi == dsoundHostApiIndex && Pa_GetDeviceInfo(i)->maxOutputChannels > 0  )
369
            fprintf( stderr, "%d (%s)\n", i, Pa_GetDeviceInfo(i)->name );
370
    }
371
    Pa_Terminate();
372
    exit(-1);
373
}
374

    
375
/*
376
    ideas: 
377
        o- could be testing with 80% CPU load
378
        o- could test with different channel counts
379
*/
380

    
381
int main(int argc, char* argv[])
382
{
383
    PaError err;
384
    int i;
385
    int deviceIndex;
386
    int dsoundBufferSize, smallestWorkingBufferSize;
387
    int smallestWorkingBufferingLatencyFrames;
388
    int min, max, mid;
389
    int testResult;
390
    FILE *resultsFp;
391
    int dsoundHostApiIndex;
392
    const PaHostApiInfo *dsoundHostApiInfo;
393
    double sampleRate = DEFAULT_SAMPLE_RATE;
394

    
395
    err = Pa_Initialize();
396
    if( err != paNoError ) goto error;
397

    
398
    dsoundHostApiIndex = Pa_HostApiTypeIdToHostApiIndex( paDirectSound );
399
    dsoundHostApiInfo = Pa_GetHostApiInfo( dsoundHostApiIndex );
400

    
401
    if( argc > 3 )
402
        usage(dsoundHostApiIndex);
403

    
404
        deviceIndex = dsoundHostApiInfo->defaultOutputDevice;
405
        if( argc >= 2 ){
406
        deviceIndex = -1;
407
                if( sscanf( argv[1], "%d", &deviceIndex ) != 1 )
408
            usage(dsoundHostApiInfo);
409
        if( deviceIndex < 0 || deviceIndex >= Pa_GetDeviceCount() || Pa_GetDeviceInfo(deviceIndex)->hostApi != dsoundHostApiIndex ){
410
            usage(dsoundHostApiInfo);
411
        }
412
        }
413

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

    
416
    if( argc >= 3 ){
417
        if( sscanf( argv[2], "%lf", &sampleRate ) != 1 )
418
            usage(dsoundHostApiIndex);
419
    }
420

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

    
423
 
424

    
425
    /* initialise sinusoidal wavetable */
426
    for( i=0; i<TABLE_SIZE; i++ )
427
    {
428
        data.sine[i] = (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. );
429
    }
430

    
431
        data.phase = 0;
432

    
433
    resultsFp = fopen( "results.txt", "at" );
434
    fprintf( resultsFp, "*** DirectSound smallest working output buffer sizes\n" );
435

    
436
    printTimeAndDate( resultsFp );
437
    printWindowsVersionInfo( resultsFp );
438
    
439
    fprintf( resultsFp, "audio device: %s\n", Pa_GetDeviceInfo( deviceIndex )->name );
440
    fflush( resultsFp );
441

    
442
    fprintf( resultsFp, "Sample rate: %f\n", (float)sampleRate );
443
    fprintf( resultsFp, "Smallest working buffer size (frames), Smallest working buffering latency (frames), Smallest working buffering latency (Seconds)\n" );
444

    
445

    
446
    /*
447
        Binary search after Niklaus Wirth
448
        from http://en.wikipedia.org/wiki/Binary_search_algorithm#The_algorithm
449
     */
450
    min = 1;
451
    max = (int)(sampleRate * .3);   /* we assume that this size works 300ms */
452
    smallestWorkingBufferSize = 0;
453

    
454
    do{
455
        mid = min + ((max - min) / 2);
456

    
457
        dsoundBufferSize = mid;
458
        testResult = playUntilKeyPress( deviceIndex, sampleRate, 0, dsoundBufferSize );
459

    
460
        if( testResult == YES ){
461
            max = mid - 1;
462
            smallestWorkingBufferSize = dsoundBufferSize;
463
        }else{
464
            min = mid + 1;
465
        }
466
         
467
    }while( (min <= max) && (testResult == YES || testResult == NO) );
468

    
469
    smallestWorkingBufferingLatencyFrames = smallestWorkingBufferSize; /* not strictly true, but we're using an unspecified callback size, so kind of */
470

    
471
    printf( "Smallest working buffer size is: %d\n", smallestWorkingBufferSize );
472
    printf( "Corresponding to buffering latency of %d frames, or %f seconds.\n", smallestWorkingBufferingLatencyFrames, smallestWorkingBufferingLatencyFrames / sampleRate );
473

    
474
    fprintf( resultsFp, "%d, %d, %f\n", smallestWorkingBufferSize, smallestWorkingBufferingLatencyFrames, smallestWorkingBufferingLatencyFrames / sampleRate );
475
    fflush( resultsFp );
476

    
477

    
478
    /* power of 2 test. iterate to the smallest power of two that works */
479

    
480
    smallestWorkingBufferSize = 0;
481
    dsoundBufferSize = 64;
482

    
483
    do{
484
        testResult = playUntilKeyPress( deviceIndex, sampleRate, 0, dsoundBufferSize );
485

    
486
        if( testResult == YES ){
487
            smallestWorkingBufferSize = dsoundBufferSize;
488
        }else{
489
            dsoundBufferSize *= 2;
490
        }
491

    
492
    }while( (dsoundBufferSize <= (int)(sampleRate * .3)) && testResult == NO );
493
    
494
    smallestWorkingBufferingLatencyFrames = smallestWorkingBufferSize; /* not strictly true, but we're using an unspecified callback size, so kind of */
495

    
496
    fprintf( resultsFp, "%d, %d, %f\n", smallestWorkingBufferSize, smallestWorkingBufferingLatencyFrames, smallestWorkingBufferingLatencyFrames / sampleRate );
497
    fflush( resultsFp );
498

    
499

    
500
    fprintf( resultsFp, "###\n" );
501
    fclose( resultsFp );
502
    
503
    Pa_Terminate();
504
    printf("Test finished.\n");
505
    
506
    return err;
507
error:
508
    Pa_Terminate();
509
    fprintf( stderr, "An error occurred while using the PortAudio stream\n" );
510
    fprintf( stderr, "Error number: %d\n", err );
511
    fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
512
    return err;
513
}
514