]> Repos - portaudio/commitdiff
initial implementation of patest_wmme_find_best_latency_params.c. just a draft. needs...
authorrossb <rossb@0f58301d-fd10-0410-b4af-bbb618454e57>
Thu, 14 Jul 2011 09:02:08 +0000 (09:02 +0000)
committerrossb <rossb@0f58301d-fd10-0410-b4af-bbb618454e57>
Thu, 14 Jul 2011 09:02:08 +0000 (09:02 +0000)
test/patest_wmme_find_best_latency_params.c [new file with mode: 0644]

diff --git a/test/patest_wmme_find_best_latency_params.c b/test/patest_wmme_find_best_latency_params.c
new file mode 100644 (file)
index 0000000..4434a09
--- /dev/null
@@ -0,0 +1,275 @@
+/*\r
+ * $Id: $\r
+ * Portable Audio I/O Library\r
+ * Windows MME low level buffer parameters search\r
+ *\r
+ * Copyright (c) 2010 Ross Bencina\r
+ *\r
+ * Permission is hereby granted, free of charge, to any person obtaining\r
+ * a copy of this software and associated documentation files\r
+ * (the "Software"), to deal in the Software without restriction,\r
+ * including without limitation the rights to use, copy, modify, merge,\r
+ * publish, distribute, sublicense, and/or sell copies of the Software,\r
+ * and to permit persons to whom the Software is furnished to do so,\r
+ * subject to the following conditions:\r
+ *\r
+ * The above copyright notice and this permission notice shall be\r
+ * included in all copies or substantial portions of the Software.\r
+ *\r
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\r
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR\r
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\r
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\r
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
+ */\r
+\r
+/*\r
+ * The text above constitutes the entire PortAudio license; however, \r
+ * the PortAudio community also makes the following non-binding requests:\r
+ *\r
+ * Any person wishing to distribute modifications to the Software is\r
+ * requested to send the modifications to the original developer so that\r
+ * they can be incorporated into the canonical version. It is also \r
+ * requested that these non-binding requests be included along with the \r
+ * license above.\r
+ */\r
+\r
+#include <stdio.h>\r
+#include <math.h>\r
+\r
+#include <windows.h>    /* required when using pa_win_wmme.h */\r
+#include <mmsystem.h>   /* required when using pa_win_wmme.h */\r
+\r
+#include <conio.h>      /* for _getch */\r
+\r
+\r
+#include "portaudio.h"\r
+#include "pa_win_wmme.h"\r
+\r
+\r
+#define SAMPLE_RATE             (22050)\r
+\r
+#ifndef M_PI\r
+#define M_PI  (3.14159265)\r
+#endif\r
+\r
+#define TABLE_SIZE              (2048)\r
+\r
+#define CHANNEL_COUNT           (2)\r
+\r
+\r
+typedef struct\r
+{\r
+    float sine[TABLE_SIZE];\r
+       double phase;\r
+}\r
+paTestData;\r
+\r
+static paTestData data;\r
+\r
+/* This routine will be called by the PortAudio engine when audio is needed.\r
+** It may called at interrupt level on some machines so don't do anything\r
+** that could mess up the system like calling malloc() or free().\r
+*/\r
+static int patestCallback( const void *inputBuffer, void *outputBuffer,\r
+                            unsigned long framesPerBuffer,\r
+                            const PaStreamCallbackTimeInfo* timeInfo,\r
+                            PaStreamCallbackFlags statusFlags,\r
+                            void *userData )\r
+{\r
+    paTestData *data = (paTestData*)userData;\r
+    float *out = (float*)outputBuffer;\r
+    unsigned long i,j;\r
+\r
+    (void) timeInfo; /* Prevent unused variable warnings. */\r
+    (void) statusFlags;\r
+    (void) inputBuffer;\r
+    \r
+    for( i=0; i<framesPerBuffer; i++ )\r
+    {\r
+        float x = data->sine[(int)data->phase];\r
+        data->phase += 20;\r
+        if( data->phase >= TABLE_SIZE ){\r
+                       data->phase -= TABLE_SIZE;\r
+               }\r
+\r
+               for( j = 0; j < CHANNEL_COUNT; ++j ){\r
+            *out++ = x;\r
+               }\r
+       }\r
+    \r
+    return paContinue;\r
+}\r
+\r
+\r
+#define YES     1\r
+#define NO      0\r
+\r
+\r
+static int playUntilKeyPress( int deviceIndex, int framesPerUserBuffer, int framesPerWmmeBuffer, int wmmeBufferCount )\r
+{\r
+    PaStreamParameters outputParameters;\r
+    PaWinMmeStreamInfo wmmeStreamInfo;\r
+    PaStream *stream;\r
+    PaError err;\r
+    int c;\r
+\r
+    outputParameters.device = deviceIndex;\r
+    outputParameters.channelCount = CHANNEL_COUNT;\r
+    outputParameters.sampleFormat = paFloat32; /* 32 bit floating point processing */\r
+    outputParameters.suggestedLatency = 0; /*Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;*/\r
+    outputParameters.hostApiSpecificStreamInfo = NULL;\r
+\r
+    wmmeStreamInfo.size = sizeof(PaWinMmeStreamInfo);\r
+    wmmeStreamInfo.hostApiType = paMME; \r
+    wmmeStreamInfo.version = 1;\r
+    wmmeStreamInfo.flags = paWinMmeUseLowLevelLatencyParameters | paWinMmeDontThrottleOverloadedProcessingThread;\r
+    wmmeStreamInfo.framesPerBuffer = framesPerWmmeBuffer;\r
+    wmmeStreamInfo.bufferCount = wmmeBufferCount;\r
+    outputParameters.hostApiSpecificStreamInfo = &wmmeStreamInfo;\r
+\r
+    err = Pa_OpenStream(\r
+              &stream,\r
+              NULL, /* no input */\r
+              &outputParameters,\r
+              SAMPLE_RATE,\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
+    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
+        c = tolower(_getch());\r
+    }while( c != 'y' && c != 'n' );\r
+\r
+    err = Pa_AbortStream( stream );\r
+    if( err != paNoError ) goto error;\r
+\r
+    err = Pa_CloseStream( stream );\r
+    if( err != paNoError ) goto error;\r
+\r
+    return (c == 'y') ? YES : NO;\r
+\r
+error:\r
+    return err;\r
+}\r
+\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 min, max, mid;\r
+    int testResult;\r
+    FILE *resultsFp;\r
+    OSVERSIONINFO windowsVersion;\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
+\r
+    */\r
+\r
+       deviceIndex = Pa_GetHostApiInfo( Pa_HostApiTypeIdToHostApiIndex( paMME ) )->defaultOutputDevice;\r
+       if( argc == 2 ){\r
+               sscanf( argv[1], "%d", &deviceIndex );\r
+       }\r
+\r
+       printf( "using device id %d (%s)\n", deviceIndex, Pa_GetDeviceInfo(deviceIndex)->name );\r
+\r
+    /* initialise sinusoidal wavetable */\r
+    for( i=0; i<TABLE_SIZE; i++ )\r
+    {\r
+        data.sine[i] = (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. );\r
+    }\r
+\r
+       data.phase = 0;\r
+\r
+\r
+    resultsFp = fopen( "results.txt", "at" );\r
+    fprintf( resultsFp, "*** WMME smallest working 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
+    \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
+\r
+    for( wmmeBufferCount = 2; wmmeBufferCount < 13; ++wmmeBufferCount ){\r
\r
+     \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
+        smallestWorkingBufferSize = 0;\r
+\r
+        do{\r
+            mid = min + ((max - min) / 2);\r
+\r
+            wmmeBufferSize = mid;\r
+            testResult = playUntilKeyPress( deviceIndex, wmmeBufferSize, wmmeBufferSize, wmmeBufferCount );\r
+\r
+            if( testResult == YES ){\r
+                max = mid - 1;\r
+                smallestWorkingBufferSize = wmmeBufferSize;\r
+            }else{\r
+                min = mid + 1;\r
+            }\r
+             \r
+        }while( (min <= max) && (testResult == YES || testResult == NO) );\r
+\r
+        printf( "smallest working buffer size for %d buffers is: %d\n", wmmeBufferCount, smallestWorkingBufferSize );\r
+\r
+        fprintf( resultsFp, "%d, %d\n", wmmeBufferCount, smallestWorkingBufferSize );\r
+        fflush( resultsFp );\r
+    }\r
+\r
+    fprintf( resultsFp, "###\n" );\r
+\r
+    fclose( resultsFp );\r
+    \r
+    Pa_Terminate();\r
+    printf("Test finished.\n");\r
+    \r
+    return err;\r
+error:\r
+    Pa_Terminate();\r
+    fprintf( stderr, "An error occured while using the portaudio stream\n" );\r
+    fprintf( stderr, "Error number: %d\n", err );\r
+    fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );\r
+    return err;\r
+}\r
+\r