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.

This commit is contained in:
rossb 2011-08-11 12:18:02 +00:00
commit 17882c465f
2 changed files with 95 additions and 56 deletions

View file

@ -2,6 +2,12 @@
@ingroup test_src
@brief Print suggested vs. PaStreamInfo reported actual latency
@author Ross Bencina <rossb@audiomulch.com>
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 <math.h>
#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]);
}
printf("# sample rate=%f, frames per buffer=%d\n", (float)SAMPLE_RATE, framesPerBuffer );
if( argc > 4 ){
framesPerBuffer = atoi(argv[4]);
}
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,

View file

@ -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 )
# 10ms buffer sizes
compositeTestFramesPerBufferValues.append( 441 )
compositeTestFramesPerBufferValues.append( 882 )
individualPlotFramesPerBufferValues = [0,64,128,256,512] #output separate plots for these
isFirst = True
for framesPerBuffer in framesPerBufferValues:
os.system(testExeName + " -1 -1 " + str(framesPerBuffer) + ' > ' + dataFileName)
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()