From 17882c465f06872a0aceb91ee42eaf09ddda8c82 Mon Sep 17 00:00:00 2001 From: rossb Date: Thu, 11 Aug 2011 12:18:02 +0000 Subject: [PATCH] Updated patest_suggested_vs_streaminfo_latency test. Python script now creates plots for a number of different buffer sizes. Supports testing at different sample rates. --- test/patest_suggested_vs_streaminfo_latency.c | 58 +++++------- .../patest_suggested_vs_streaminfo_latency.py | 93 ++++++++++++++----- 2 files changed, 95 insertions(+), 56 deletions(-) diff --git a/test/patest_suggested_vs_streaminfo_latency.c b/test/patest_suggested_vs_streaminfo_latency.c index 473f9a1..a6584a5 100644 --- a/test/patest_suggested_vs_streaminfo_latency.c +++ b/test/patest_suggested_vs_streaminfo_latency.c @@ -2,6 +2,12 @@ @ingroup test_src @brief Print suggested vs. PaStreamInfo reported actual latency @author Ross Bencina + + Opens streams with a sequence of suggested latency values + from 0 to 2 seconds in .5ms intervals and gathers the resulting actual + latency values. Output a csv file and graph suggested vs. actual. Run + with framesPerBuffer unspecified, powers of 2 and multiples of 50 and + prime number buffer sizes. */ /* * $Id: patest_sine.c 1368 2008-03-01 00:38:27Z rossb $ @@ -44,18 +50,16 @@ #include #include "portaudio.h" -#define NUM_SECONDS (5) -#define SAMPLE_RATE (44100) -#define FRAMES_PER_BUFFER (64) - -#define NUM_CHANNELS (2) +#define SAMPLE_RATE (44100) +#define FRAMES_PER_BUFFER (128) +#define NUM_CHANNELS (2) #define SUGGESTED_LATENCY_START_SECONDS (0.0) -#define SUGGESTED_LATENCY_END_SECONDS (.5) -#define SUGGESTED_LATENCY_INCREMENT_SECONDS (0.0005) // half a millisecond increments +#define SUGGESTED_LATENCY_END_SECONDS (2.0) +#define SUGGESTED_LATENCY_INCREMENT_SECONDS (0.0005) /* half a millisecond increments */ -// dummy callback. does nothing +/* dummy callback. does nothing. never gets called */ static int patestCallback( const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo* timeInfo, @@ -64,27 +68,6 @@ static int patestCallback( const void *inputBuffer, void *outputBuffer, { } - -/* - TODO: - - - A test that opens streams with a sequence of suggested latency values - from 0 to 2 seconds in .5ms intervals and gathers the resulting actual - latency values. Output a csv file and graph suggested vs. actual. Run - with framesPerBuffer unspecified, powers of 2 and multiples of 50 and - prime number buffer sizes. - - o- add command line parameters to specify frames per buffer, sample rate, devices - - o- test at standard sample rates - - - - - -*/ - /*******************************************************************/ static void usage() { @@ -93,7 +76,7 @@ static void usage() const char *channelString; fprintf( stderr, "PortAudio suggested (requested) vs. resulting (reported) stream latency test\n" ); - fprintf( stderr, "Usage: x.exe input-device-index output-device-index frames-per-buffer\n" ); + fprintf( stderr, "Usage: x.exe input-device-index output-device-index sample-rate frames-per-buffer\n" ); fprintf( stderr, "Use -1 for default device index, or use one of these:\n" ); for( i=0; i < Pa_GetDeviceCount(); ++i ){ deviceInfo = Pa_GetDeviceInfo(i); @@ -121,6 +104,7 @@ int main( int argc, const char* argv[] ) PaStreamInfo *streamInfo; PaDeviceInfo *deviceInfo; int deviceCount; + float sampleRate = SAMPLE_RATE; int framesPerBuffer = FRAMES_PER_BUFFER; err = Pa_Initialize(); if( err != paNoError ) goto error; @@ -129,10 +113,14 @@ int main( int argc, const char* argv[] ) usage(); if( argc > 3 ){ - framesPerBuffer = atoi(argv[3]); + sampleRate = atoi(argv[3]); + } + + if( argc > 4 ){ + framesPerBuffer = atoi(argv[4]); } - printf("# sample rate=%f, frames per buffer=%d\n", (float)SAMPLE_RATE, framesPerBuffer ); + printf("# sample rate=%f, frames per buffer=%d\n", (float)sampleRate, framesPerBuffer ); outputParameters.device = -1; if( argc > 1 ) @@ -208,7 +196,7 @@ int main( int argc, const char* argv[] ) &stream, NULL, /* no input */ &outputParameters, - SAMPLE_RATE, + sampleRate, framesPerBuffer, paClipOff, /* we won't output out of range samples so don't bother clipping them */ patestCallback, @@ -228,7 +216,7 @@ int main( int argc, const char* argv[] ) &stream, &inputParameters, NULL, /* no output */ - SAMPLE_RATE, + sampleRate, framesPerBuffer, paClipOff, /* we won't output out of range samples so don't bother clipping them */ patestCallback, @@ -248,7 +236,7 @@ int main( int argc, const char* argv[] ) &stream, &inputParameters, &outputParameters, - SAMPLE_RATE, + sampleRate, framesPerBuffer, paClipOff, /* we won't output out of range samples so don't bother clipping them */ patestCallback, diff --git a/test/patest_suggested_vs_streaminfo_latency.py b/test/patest_suggested_vs_streaminfo_latency.py index 9995949..ad9591a 100644 --- a/test/patest_suggested_vs_streaminfo_latency.py +++ b/test/patest_suggested_vs_streaminfo_latency.py @@ -10,13 +10,16 @@ import os from pylab import * import numpy from matplotlib.backends.backend_pdf import PdfPages -pdfFile = PdfPages('patest_suggested_vs_streaminfo_latency.pdf') testExeName = "PATest.exe" # rename to whatever the compiled patest_suggested_vs_streaminfo_latency.c binary is -dataFileName = 'patest_suggested_vs_streaminfo_latency.csv' # code below calls the exe to generate this file +dataFileName = "patest_suggested_vs_streaminfo_latency.csv" # code below calls the exe to generate this file inputDeviceIndex = -1 # -1 means default -inputDeviceIndex = -1 # -1 means default +outputDeviceIndex = -1 # -1 means default +sampleRate = 44100 +pdfFilenameSuffix = "_wmme" + +pdfFile = PdfPages("patest_suggested_vs_streaminfo_latency_" + str(sampleRate) + pdfFilenameSuffix +".pdf") #output this pdf file def loadCsvData( dataFileName ): @@ -50,45 +53,93 @@ def loadCsvData( dataFileName ): result.fullDuplexInputLatency = data[4] return result; + +def setFigureTitleAndAxisLabels( framesPerBufferString ): + title("PortAudio suggested (requested) vs. resulting (reported) stream latency\n" + framesPerBufferString) + ylabel("PaStreamInfo::{input,output}Latency (s)") + xlabel("Pa_OpenStream suggestedLatency (s)") + grid(True) + legend(loc="upper left") + +def setDisplayRangeSeconds( maxSeconds ): + xlim(0, maxSeconds) + ylim(0, maxSeconds) + + # run the test with different frames per buffer values: -framesPerBufferValues = [0] +compositeTestFramesPerBufferValues = [0] # powers of two -#for i in range (1,11): -# framesPerBufferValues.append( 2 ^ i ) +for i in range (1,11): + compositeTestFramesPerBufferValues.append( pow(2,i) ) -# could also test: multiples of 10, random numbers, powers of primes, etc +# multiples of 50 +for i in range (1,20): + compositeTestFramesPerBufferValues.append( i * 50 ) -isFirst = True +# 10ms buffer sizes +compositeTestFramesPerBufferValues.append( 441 ) +compositeTestFramesPerBufferValues.append( 882 ) -for framesPerBuffer in framesPerBufferValues: +individualPlotFramesPerBufferValues = [0,64,128,256,512] #output separate plots for these - os.system(testExeName + " -1 -1 " + str(framesPerBuffer) + ' > ' + dataFileName) +isFirst = True + +for framesPerBuffer in compositeTestFramesPerBufferValues: + commandString = testExeName + " " + str(inputDeviceIndex) + " " + str(outputDeviceIndex) + " " + str(sampleRate) + " " + str(framesPerBuffer) + ' > ' + dataFileName + print commandString + os.system(commandString) d = loadCsvData(dataFileName) if isFirst: - figure(1) + figure(1) # title sheet gcf().text(0.1, 0.0, - 'patest_suggested_vs_streaminfo_latency\n%s\n%s\n%s\n'%(d.inputDevice,d.outputDevice,d.sampleRate)) + "patest_suggested_vs_streaminfo_latency\n%s\n%s\n%s\n"%(d.inputDevice,d.outputDevice,d.sampleRate)) pdfFile.savefig() - isFirst = False - - figure(2) + + + figure(2) # composite plot, includes all compositeTestFramesPerBufferValues - plot( d.suggestedLatency, d.suggestedLatency ) + if isFirst: + plot( d.suggestedLatency, d.suggestedLatency, label="Suggested latency" ) + plot( d.suggestedLatency, d.halfDuplexOutputLatency ) plot( d.suggestedLatency, d.halfDuplexInputLatency ) plot( d.suggestedLatency, d.fullDuplexOutputLatency ) plot( d.suggestedLatency, d.fullDuplexInputLatency ) -title('PortAudio suggested (requested) vs. resulting (reported) stream latency\n%s'%str(framesPerBufferValues)) -ylabel('PaStreamInfo::{input,output}Latency (s)') -xlabel('Pa_OpenStream suggestedLatency (s)') -grid(True) + if framesPerBuffer in individualPlotFramesPerBufferValues: # individual plots + figure( 3 + individualPlotFramesPerBufferValues.index(framesPerBuffer) ) + + plot( d.suggestedLatency, d.suggestedLatency, label="Suggested latency" ) + plot( d.suggestedLatency, d.halfDuplexOutputLatency, label="Half-duplex output latency" ) + plot( d.suggestedLatency, d.halfDuplexInputLatency, label="Half-duplex input latency" ) + plot( d.suggestedLatency, d.fullDuplexOutputLatency, label="Full-duplex output latency" ) + plot( d.suggestedLatency, d.fullDuplexInputLatency, label="Full-duplex input latency" ) + + if framesPerBuffer == 0: + framesPerBufferText = "paFramesPerBufferUnspecified" + else: + framesPerBufferText = str(framesPerBuffer) + setFigureTitleAndAxisLabels( "user frames per buffer: "+str(framesPerBufferText) ) + setDisplayRangeSeconds(2.2) + pdfFile.savefig() + setDisplayRangeSeconds(0.1) + setFigureTitleAndAxisLabels( "user frames per buffer: "+str(framesPerBufferText)+" (detail)" ) + pdfFile.savefig() + + isFirst = False +figure(2) +setFigureTitleAndAxisLabels( "composite of frames per buffer values:\n"+str(compositeTestFramesPerBufferValues) ) +setDisplayRangeSeconds(2.2) +pdfFile.savefig() +setDisplayRangeSeconds(0.1) +setFigureTitleAndAxisLabels( "composite of frames per buffer values:\n"+str(compositeTestFramesPerBufferValues)+" (detail)" ) pdfFile.savefig() pdfFile.close() -show() +#uncomment this to display interactively, otherwise we just output a pdf +#show() -- 2.43.0