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:
parent
903263d777
commit
17882c465f
2 changed files with 95 additions and 56 deletions
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
Loading…
Reference in a new issue