]> Repos - portaudio/commitdiff
updated wmme manual latency test
authorrossb <rossb@0f58301d-fd10-0410-b4af-bbb618454e57>
Thu, 21 Jul 2011 14:34:15 +0000 (14:34 +0000)
committerrossb <rossb@0f58301d-fd10-0410-b4af-bbb618454e57>
Thu, 21 Jul 2011 14:34:15 +0000 (14:34 +0000)
test/patest_wmme_find_best_latency_params.c

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