From: rossb Date: Thu, 21 Jul 2011 14:34:15 +0000 (+0000) Subject: updated wmme manual latency test X-Git-Tag: pa_stable_v19_20111121_r1788~66 X-Git-Url: https://andrewgundersen.net/repos?a=commitdiff_plain;h=3165f0f4730a6c02fe61e7437bdf549c91820bd7;p=portaudio updated wmme manual latency test --- diff --git a/test/patest_wmme_find_best_latency_params.c b/test/patest_wmme_find_best_latency_params.c index 4434a09..d78e4b2 100644 --- a/test/patest_wmme_find_best_latency_params.c +++ b/test/patest_wmme_find_best_latency_params.c @@ -1,7 +1,7 @@ /* * $Id: $ * Portable Audio I/O Library - * Windows MME low level buffer parameters search + * Windows MME low level buffer user guided parameters search * * Copyright (c) 2010 Ross Bencina * @@ -37,8 +37,10 @@ */ #include +#include #include +#define _WIN32_WINNT 0x0501 /* for GetNativeSystemInfo */ #include /* required when using pa_win_wmme.h */ #include /* required when using pa_win_wmme.h */ @@ -49,7 +51,7 @@ #include "pa_win_wmme.h" -#define SAMPLE_RATE (22050) +#define DEFAULT_SAMPLE_RATE (44100.) #ifndef M_PI #define M_PI (3.14159265) @@ -60,10 +62,173 @@ #define CHANNEL_COUNT (2) +/* seach parameters. we test all buffer counts in this range */ +#define MIN_WMME_BUFFER_COUNT (2) +#define MAX_WMME_BUFFER_COUNT (12) + + +/*******************************************************************/ +/* functions to query and print Windows version information */ + +typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL); + +LPFN_ISWOW64PROCESS fnIsWow64Process; + +static BOOL IsWow64() +{ + BOOL bIsWow64 = FALSE; + + //IsWow64Process is not available on all supported versions of Windows. + //Use GetModuleHandle to get a handle to the DLL that contains the function + //and GetProcAddress to get a pointer to the function if available. + + fnIsWow64Process = (LPFN_ISWOW64PROCESS) GetProcAddress( + GetModuleHandle(TEXT("kernel32")),"IsWow64Process" ); + + if(NULL != fnIsWow64Process) + { + if (!fnIsWow64Process(GetCurrentProcess(),&bIsWow64)) + { + //handle error + } + } + return bIsWow64; +} + +static void printWindowsVersionInfo( FILE *fp ) +{ + OSVERSIONINFOEX osVersionInfoEx; + SYSTEM_INFO systemInfo; + const char *osName = "Unknown"; + const char *osProductType = ""; + const char *processorArchitecture = "Unknown"; + + memset( &osVersionInfoEx, 0, sizeof(OSVERSIONINFOEX) ); + osVersionInfoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); + GetVersionEx( &osVersionInfoEx ); + + + if( osVersionInfoEx.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS ){ + switch( osVersionInfoEx.dwMinorVersion ){ + case 0: osName = "Windows 95"; break; + case 10: osName = "Windows 98"; break; // could also be SE (code I saw discriminated on osInfo.Version.Revision.ToString() == "2222A") + case 90: osName = "Windows Me"; break; + } + }else if( osVersionInfoEx.dwPlatformId == VER_PLATFORM_WIN32_NT ){ + switch( osVersionInfoEx.dwMajorVersion ){ + case 3: osName = "Windows NT 3.51"; break; + case 4: osName = "Windows NT 4.0"; break; + case 5: switch( osVersionInfoEx.dwMinorVersion ){ + case 0: osName = "Windows 2000"; break; + case 1: osName = "Windows XP"; break; + case 2: + if( osVersionInfoEx.wSuiteMask & 0x00008000 /*VER_SUITE_WH_SERVER*/ ){ + osName = "Windows Home Server"; + }else{ + if( osVersionInfoEx.wProductType == VER_NT_WORKSTATION ){ + osName = "Windows XP Professional x64 Edition (?)"; + }else{ + if( GetSystemMetrics(/*SM_SERVERR2*/89) == 0 ) + osName = "Windows Server 2003"; + else + osName = "Windows Server 2003 R2"; + } + }break; + }break; + case 6:switch( osVersionInfoEx.dwMinorVersion ){ + case 0: + if( osVersionInfoEx.wProductType == VER_NT_WORKSTATION ) + osName = "Windows Vista"; + else + osName = "Windows Server 2008"; + case 1: + if( osVersionInfoEx.wProductType == VER_NT_WORKSTATION ) + osName = "Windows 7"; + else + osName = "Windows Server 2008 R2"; + }break; + } + } + + if(osVersionInfoEx.dwMajorVersion == 4) + { + if(osVersionInfoEx.wProductType == VER_NT_WORKSTATION) + osProductType = "Workstation"; + else if(osVersionInfoEx.wProductType == VER_NT_SERVER) + osProductType = "Server"; + } + else if(osVersionInfoEx.dwMajorVersion == 5) + { + if(osVersionInfoEx.wProductType == VER_NT_WORKSTATION) + { + if((osVersionInfoEx.wSuiteMask & VER_SUITE_PERSONAL) == VER_SUITE_PERSONAL) + osProductType = "Home Edition"; // Windows XP Home Edition + else + osProductType = "Professional"; // Windows XP / Windows 2000 Professional + } + else if(osVersionInfoEx.wProductType == VER_NT_SERVER) + { + if(osVersionInfoEx.dwMinorVersion == 0) + { + if((osVersionInfoEx.wSuiteMask & VER_SUITE_DATACENTER) == VER_SUITE_DATACENTER) + osProductType = "Datacenter Server"; // Windows 2000 Datacenter Server + else if((osVersionInfoEx.wSuiteMask & VER_SUITE_ENTERPRISE) == VER_SUITE_ENTERPRISE) + osProductType = "Advanced Server"; // Windows 2000 Advanced Server + else + osProductType = "Server"; // Windows 2000 Server + } + } + else + { + if((osVersionInfoEx.wSuiteMask & VER_SUITE_DATACENTER) == VER_SUITE_DATACENTER) + osProductType = "Datacenter Edition"; // Windows Server 2003 Datacenter Edition + else if((osVersionInfoEx.wSuiteMask & VER_SUITE_ENTERPRISE) == VER_SUITE_ENTERPRISE) + osProductType = "Enterprise Edition"; // Windows Server 2003 Enterprise Edition + else if((osVersionInfoEx.wSuiteMask & VER_SUITE_BLADE) == VER_SUITE_BLADE) + osProductType = "Web Edition"; // Windows Server 2003 Web Edition + else + osProductType = "Standard Edition"; // Windows Server 2003 Standard Edition + } + } + + memset( &systemInfo, 0, sizeof(SYSTEM_INFO) ); + GetNativeSystemInfo( &systemInfo ); + + if( systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL ) + processorArchitecture = "x86"; + else if( systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64 ) + processorArchitecture = "x64"; + else if( systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64 ) + processorArchitecture = "Itanium"; + + + fprintf( fp, "OS name and edition: %s %s\n", osName, osProductType ); + fprintf( fp, "OS version: %d.%d.%d %S\n", osVersionInfoEx.dwMajorVersion, osVersionInfoEx.dwMinorVersion, osVersionInfoEx.dwBuildNumber, osVersionInfoEx.szCSDVersion ); + fprintf( fp, "Processor architecture: %s\n", processorArchitecture ); + fprintf( fp, "WoW64 process: %s\n", IsWow64() ? "Yes" : "No" ); +} + +static void printTimeAndDate( FILE *fp ) +{ + struct tm *local; + time_t t; + + t = time(NULL); + local = localtime(&t); + fprintf(fp, "Local time and date: %s", asctime(local)); + local = gmtime(&t); + fprintf(fp, "UTC time and date: %s", asctime(local)); +} + +/*******************************************************************/ + typedef struct { float sine[TABLE_SIZE]; double phase; + volatile int fadeIn; + volatile int fadeOut; + double amp; } paTestData; @@ -95,12 +260,25 @@ static int patestCallback( const void *inputBuffer, void *outputBuffer, data->phase -= TABLE_SIZE; } + x *= data->amp; + if( data->fadeIn ){ + data->amp += .001; + if( data->amp >= 1. ) + data->fadeIn = 0; + }else if( data->fadeOut ){ + if( data->amp > 0 ) + data->amp -= .001; + } + for( j = 0; j < CHANNEL_COUNT; ++j ){ *out++ = x; } } - return paContinue; + if( data->amp > 0 ) + return paContinue; + else + return paComplete; } @@ -108,7 +286,7 @@ static int patestCallback( const void *inputBuffer, void *outputBuffer, #define NO 0 -static int playUntilKeyPress( int deviceIndex, int framesPerUserBuffer, int framesPerWmmeBuffer, int wmmeBufferCount ) +static int playUntilKeyPress( int deviceIndex, float sampleRate, int framesPerUserBuffer, int framesPerWmmeBuffer, int wmmeBufferCount ) { PaStreamParameters outputParameters; PaWinMmeStreamInfo wmmeStreamInfo; @@ -134,23 +312,35 @@ static int playUntilKeyPress( int deviceIndex, int framesPerUserBuffer, int fram &stream, NULL, /* no input */ &outputParameters, - SAMPLE_RATE, + sampleRate, framesPerUserBuffer, paClipOff | paPrimeOutputBuffersUsingStreamCallback, /* we won't output out of range samples so don't bother clipping them */ patestCallback, &data ); if( err != paNoError ) goto error; + data.amp = 0; + data.fadeIn = 1; + data.fadeOut = 0; + err = Pa_StartStream( stream ); if( err != paNoError ) goto error; do{ - printf( "Trying buffer size %d.\nIf it sounds smooth press 'y', if it sounds bad press 'n'\n", framesPerWmmeBuffer ); + printf( "Trying buffer size %d.\nIf it sounds smooth press 'y', if it sounds bad press 'n' ('q' to quit)\n", framesPerWmmeBuffer ); c = tolower(_getch()); + if( c == 'q' ){ + Pa_Terminate(); + exit(0); + } }while( c != 'y' && c != 'n' ); - err = Pa_AbortStream( stream ); + data.fadeOut = 1; + while( Pa_IsStreamActive(stream) == 1 ) + Pa_Sleep( 100 ); + + err = Pa_StopStream( stream ); if( err != paNoError ) goto error; err = Pa_CloseStream( stream ); @@ -162,33 +352,90 @@ error: return err; } - /*******************************************************************/ +static void usage( int wmmeHostApiIndex ) +{ + int i; + + fprintf( stderr, "PortAudio WMME output latency user guided test\n" ); + fprintf( stderr, "Usage: x.exe mme-device-index [sampleRate [min-buffer-count max-buffer-count]]\n" ); + fprintf( stderr, "Invalid device index. Use one of these:\n" ); + for( i=0; i < Pa_GetDeviceCount(); ++i ){ + + if( Pa_GetDeviceInfo(i)->hostApi == wmmeHostApiIndex ) + fprintf( stderr, "%d (%s)\n", i, Pa_GetDeviceInfo(i)->name); + } + Pa_Terminate(); + exit(-1); +} + +/* + TODO: test at 22050 44100, 48000, 96000, mono and stereo and surround + + TODO: could be testing with 80% CPU load + + another thing to try would be setting the timeBeginPeriod granularity to 1ms and see if it changes the behavior +*/ + int main(int argc, char* argv[]) { PaError err; int i; int deviceIndex; int wmmeBufferCount, wmmeBufferSize, smallestWorkingBufferSize; + int smallestWorkingBufferingLatencyFrames; int min, max, mid; int testResult; FILE *resultsFp; - OSVERSIONINFO windowsVersion; + int wmmeHostApiIndex; + const PaHostApiInfo *wmmeHostApiInfo; + double sampleRate = DEFAULT_SAMPLE_RATE; + int wmmeMinBufferCount = MIN_WMME_BUFFER_COUNT; + int wmmeMaxBufferCount = MAX_WMME_BUFFER_COUNT; err = Pa_Initialize(); if( err != paNoError ) goto error; - /* - TODO: print an index of devices and ask the user to select one + wmmeHostApiIndex = Pa_HostApiTypeIdToHostApiIndex( paMME ); + wmmeHostApiInfo = Pa_GetHostApiInfo( wmmeHostApiIndex ); - */ + if( argc > 5 ) + usage(wmmeHostApiIndex); - deviceIndex = Pa_GetHostApiInfo( Pa_HostApiTypeIdToHostApiIndex( paMME ) )->defaultOutputDevice; - if( argc == 2 ){ - sscanf( argv[1], "%d", &deviceIndex ); + deviceIndex = wmmeHostApiInfo->defaultOutputDevice; + if( argc >= 2 ){ + deviceIndex = -1; + if( sscanf( argv[1], "%d", &deviceIndex ) != 1 ) + usage(wmmeHostApiIndex); + if( deviceIndex < 0 || deviceIndex >= Pa_GetDeviceCount() || Pa_GetDeviceInfo(deviceIndex)->hostApi != wmmeHostApiIndex ){ + usage(wmmeHostApiIndex); + } } - printf( "using device id %d (%s)\n", deviceIndex, Pa_GetDeviceInfo(deviceIndex)->name ); + printf( "Using device id %d (%s)\n", deviceIndex, Pa_GetDeviceInfo(deviceIndex)->name ); + + if( argc >= 3 ){ + if( sscanf( argv[2], "%lf", &sampleRate ) != 1 ) + usage(wmmeHostApiIndex); + } + + printf( "Testing with sample rate %f.\n", (float)sampleRate ); + + if( argc == 4 ){ + if( sscanf( argv[3], "%d", &wmmeMinBufferCount ) != 1 ) + usage(wmmeHostApiIndex); + wmmeMaxBufferCount = wmmeMinBufferCount; + } + + if( argc == 5 ){ + if( sscanf( argv[3], "%d", &wmmeMinBufferCount ) != 1 ) + usage(wmmeHostApiIndex); + if( sscanf( argv[4], "%d", &wmmeMaxBufferCount ) != 1 ) + usage(wmmeHostApiIndex); + } + + printf( "Testing buffer counts from %d to %d\n", wmmeMinBufferCount, wmmeMaxBufferCount ); + /* initialise sinusoidal wavetable */ for( i=0; iname ); + printTimeAndDate( resultsFp ); + printWindowsVersionInfo( resultsFp ); + fprintf( resultsFp, "audio device: %s\n", Pa_GetDeviceInfo( deviceIndex )->name ); + fflush( resultsFp ); - /* - TODO: test at 22050 44100, 48000, 96000, mono and stereo and surround - - TODO: should be testing with 80% CPU load - - - another thing to try would be setting the timeBeginPeriod granularity to 1ms and see if it changes the behavior - */ - - printf( "testing with sample rate %f.\n", (float)SAMPLE_RATE ); - fprintf( resultsFp, "sample rate: %f\n", (float)SAMPLE_RATE ); - fprintf( resultsFp, "buffer count, smallest working size (frames)\n" ); + fprintf( resultsFp, "Sample rate: %f\n", (float)sampleRate ); + fprintf( resultsFp, "Buffer count, Smallest working buffer size (frames), Smallest working buffering latency (frames), Smallest working buffering latency (Seconds)\n" ); - for( wmmeBufferCount = 2; wmmeBufferCount < 13; ++wmmeBufferCount ){ + for( wmmeBufferCount = wmmeMinBufferCount; wmmeBufferCount <= wmmeMaxBufferCount; ++wmmeBufferCount ){ // TODO print out range we're going to try before starting - - printf( "testing with %d buffers...\n", wmmeBufferCount ); + printf( "Test %d of %d\n", (wmmeBufferCount - wmmeMinBufferCount) + 1, (wmmeMaxBufferCount-wmmeMinBufferCount) + 1 ); + printf( "Testing with %d buffers...\n", wmmeBufferCount ); /* Binary search after Niklaus Wirth from http://en.wikipedia.org/wiki/Binary_search_algorithm#The_algorithm */ min = 1; - max = 8192; /* we assume that this size works */ + max = (int)((sampleRate * .3) / (wmmeBufferCount-1)); //8192; /* we assume that this size works 300ms */ smallestWorkingBufferSize = 0; do{ mid = min + ((max - min) / 2); wmmeBufferSize = mid; - testResult = playUntilKeyPress( deviceIndex, wmmeBufferSize, wmmeBufferSize, wmmeBufferCount ); + testResult = playUntilKeyPress( deviceIndex, sampleRate, wmmeBufferSize, wmmeBufferSize, wmmeBufferCount ); if( testResult == YES ){ max = mid - 1; @@ -251,14 +485,16 @@ int main(int argc, char* argv[]) }while( (min <= max) && (testResult == YES || testResult == NO) ); - printf( "smallest working buffer size for %d buffers is: %d\n", wmmeBufferCount, smallestWorkingBufferSize ); + smallestWorkingBufferingLatencyFrames = smallestWorkingBufferSize * (wmmeBufferCount - 1); - fprintf( resultsFp, "%d, %d\n", wmmeBufferCount, smallestWorkingBufferSize ); + printf( "Smallest working buffer size for %d buffers is: %d\n", wmmeBufferCount, smallestWorkingBufferSize ); + printf( "Corresponding to buffering latency of %d frames, or %f seconds.\n", smallestWorkingBufferingLatencyFrames, smallestWorkingBufferingLatencyFrames / sampleRate ); + + fprintf( resultsFp, "%d, %d, %d, %f\n", wmmeBufferCount, smallestWorkingBufferSize, smallestWorkingBufferingLatencyFrames, smallestWorkingBufferingLatencyFrames / sampleRate ); fflush( resultsFp ); } fprintf( resultsFp, "###\n" ); - fclose( resultsFp ); Pa_Terminate();