From: philburk Date: Wed, 2 Mar 2011 21:03:45 +0000 (+0000) Subject: Add latency control in loopback to fix Linux latency problems. X-Git-Tag: pa_stable_v19_20110326_r1647~19 X-Git-Url: https://andrewgundersen.net/repos?a=commitdiff_plain;h=0c00ae88452341598988e97645d50ac516509f11;p=portaudio Add latency control in loopback to fix Linux latency problems. --- diff --git a/qa/loopback/src/paqa.c b/qa/loopback/src/paqa.c index f95e7de..1fcc48b 100644 --- a/qa/loopback/src/paqa.c +++ b/qa/loopback/src/paqa.c @@ -52,12 +52,13 @@ int g_testsPassed = 0; int g_testsFailed = 0; -#define MAX_NUM_GENERATORS (8) -#define MAX_NUM_RECORDINGS (8) -#define MAX_BACKGROUND_NOISE_RMS (0.0004) +#define MAX_NUM_GENERATORS (8) +#define MAX_NUM_RECORDINGS (8) +#define MAX_BACKGROUND_NOISE_RMS (0.0004) #define LOOPBACK_DETECTION_DURATION_SECONDS (0.8) -#define DEFAULT_FRAMES_PER_BUFFER (64) -#define PAQA_WAIT_STREAM_MSEC (100) +#define DEFAULT_FRAMES_PER_BUFFER (128) +#define DEFAULT_HIGH_LATENCY_MSEC (100) +#define PAQA_WAIT_STREAM_MSEC (100) // Use two separate streams instead of one full duplex stream. #define PAQA_FLAG_TWO_STREAMS (1<<0) @@ -103,6 +104,8 @@ typedef struct LoopbackContext_s int outputUnderflowCount; int inputOverflowCount; int outputOverflowCount; + int minFramesPerBuffer; + int maxFramesPerBuffer; int primingCount; TestParameters *test; volatile int done; @@ -142,7 +145,15 @@ static int RecordAndPlaySinesCallback( const void *inputBuffer, void *outputBuff if( statusFlags & paOutputUnderflow ) loopbackContext->outputUnderflowCount += 1; if( statusFlags & paOutputOverflow ) loopbackContext->outputOverflowCount += 1; if( statusFlags & paPrimingOutput ) loopbackContext->primingCount += 1; - + if( framesPerBuffer > loopbackContext->maxFramesPerBuffer ) + { + loopbackContext->maxFramesPerBuffer = framesPerBuffer; + } + if( framesPerBuffer < loopbackContext->minFramesPerBuffer ) + { + loopbackContext->minFramesPerBuffer = framesPerBuffer; + } + /* This may get called with NULL inputBuffer during initial setup. * We may also use the same callback with output only streams. */ @@ -292,7 +303,7 @@ int PaQa_RunLoopbackHalfDuplex( LoopbackContext *loopbackContext ) // Wait for stream to finish. while( loopbackContext->done == 0 ) { - Pa_Sleep(50); + Pa_Sleep(PAQA_WAIT_STREAM_MSEC); } err = Pa_StopStream( inStream ); @@ -346,7 +357,7 @@ int PaQa_RunInputOnly( LoopbackContext *loopbackContext ) // Wait for stream to finish. while( loopbackContext->done == 0 ) { - Pa_Sleep(50); + Pa_Sleep(PAQA_WAIT_STREAM_MSEC); } err = Pa_StopStream( inStream ); @@ -632,6 +643,7 @@ static int PaQa_SetupLoopbackContext( LoopbackContext *loopbackContextPtr, TestP PaQa_SetupSineGenerator( &loopbackContextPtr->generators[i], PaQa_GetNthFrequency( testParams->baseFrequency, i ), testParams->amplitude, testParams->sampleRate ); } + loopbackContextPtr->minFramesPerBuffer = 0x0FFFFFFF; return 0; error: return -1; @@ -641,10 +653,13 @@ error: static void PaQa_TeardownLoopbackContext( LoopbackContext *loopbackContextPtr ) { int i; - for( i=0; itest->samplesPerFrame; i++ ) + if( loopbackContextPtr->test != NULL ) { - PaQa_TerminateRecording( &loopbackContextPtr->recordings[i] ); - } + for( i=0; itest->samplesPerFrame; i++ ) + { + PaQa_TerminateRecording( &loopbackContextPtr->recordings[i] ); + } + } } /*******************************************************************/ @@ -766,6 +781,7 @@ static int PaQa_SingleLoopBackTest( UserOptions *userOptions, TestParameters *te printf( "\n" ); + // Sleep to see if this lets the audio drivers stabilize. Pa_Sleep( 500 ); PaQa_TeardownLoopbackContext( &loopbackContext ); @@ -778,6 +794,7 @@ static int PaQa_SingleLoopBackTest( UserOptions *userOptions, TestParameters *te error: PaQa_TeardownLoopbackContext( &loopbackContext ); printf( "\n" ); + g_testsFailed += 1; return err; } @@ -786,7 +803,6 @@ static void PaQa_SetDefaultTestParameters( TestParameters *testParamsPtr, PaDevi { memset( testParamsPtr, 0, sizeof(TestParameters) ); - testParamsPtr->samplesPerFrame = 2; testParamsPtr->amplitude = 0.5; testParamsPtr->sampleRate = 44100; @@ -798,14 +814,34 @@ static void PaQa_SetDefaultTestParameters( TestParameters *testParamsPtr, PaDevi testParamsPtr->inputParameters.device = inputDevice; testParamsPtr->inputParameters.sampleFormat = paFloat32; testParamsPtr->inputParameters.channelCount = testParamsPtr->samplesPerFrame; - testParamsPtr->inputParameters.suggestedLatency = - Pa_GetDeviceInfo( inputDevice )->defaultLowInputLatency; + testParamsPtr->inputParameters.suggestedLatency = DEFAULT_HIGH_LATENCY_MSEC * 0.001; testParamsPtr->outputParameters.device = outputDevice; testParamsPtr->outputParameters.sampleFormat = paFloat32; testParamsPtr->outputParameters.channelCount = testParamsPtr->samplesPerFrame; - testParamsPtr->outputParameters.suggestedLatency = - Pa_GetDeviceInfo( outputDevice )->defaultLowOutputLatency; + testParamsPtr->outputParameters.suggestedLatency = DEFAULT_HIGH_LATENCY_MSEC * 0.001; +} + +/*******************************************************************/ +static void PaQa_OverrideTestParameters( TestParameters *testParamsPtr, UserOptions *userOptions ) +{ + // Check to see if a specific value was requested. + if( userOptions->sampleRate >= 0 ) + { + testParamsPtr->sampleRate = userOptions->sampleRate; + } + if( userOptions->framesPerBuffer >= 0 ) + { + testParamsPtr->framesPerBuffer = userOptions->framesPerBuffer; + } + if( userOptions->latency >= 0 ) + { + testParamsPtr->inputParameters.suggestedLatency = userOptions->latency * 0.001; + testParamsPtr->outputParameters.suggestedLatency = userOptions->latency * 0.001; + } + printf( " running with suggested latency (msec): input = %5.2f, out = %5.2f\n", + (testParamsPtr->inputParameters.suggestedLatency * 1000.0), + (testParamsPtr->outputParameters.suggestedLatency * 1000.0) ); } /*******************************************************************/ @@ -820,22 +856,20 @@ static int PaQa_AnalyzeLoopbackConnection( UserOptions *userOptions, PaDeviceInd int iSize; int iFormat; int totalBadChannels = 0; + int savedValue; TestParameters testParams; const PaDeviceInfo *inputDeviceInfo; const PaDeviceInfo *outputDeviceInfo; inputDeviceInfo = Pa_GetDeviceInfo( inputDevice ); outputDeviceInfo = Pa_GetDeviceInfo( outputDevice ); - double bestSampleRate = 44100.0; - int bestBufferSize = DEFAULT_FRAMES_PER_BUFFER; - printf( "=============== Analysing Loopback %d to %d ====================\n", outputDevice, inputDevice ); printf( " Devices: %s => %s\n", outputDeviceInfo->name, inputDeviceInfo->name); // test half duplex first because it is more likely to work. int flagSettings[] = { PAQA_FLAG_TWO_STREAMS, 0 }; int numFlagSettings = (sizeof(flagSettings)/sizeof(int)); - + double sampleRates[] = { 8000.0, 11025.0, 16000.0, 22050.0, 32000.0, 44100.0, 48000.0, 96000.0 }; int numRates = (sizeof(sampleRates)/sizeof(double)); @@ -846,22 +880,9 @@ static int PaQa_AnalyzeLoopbackConnection( UserOptions *userOptions, PaDeviceInd const char *sampleFormatNames[] = { "paUInt8", "paInt16", "paInt32" }; int numSampleFormats = (sizeof(sampleFormats)/sizeof(PaSampleFormat)); - // Check to see if a specific value was requested. - if( userOptions->sampleRate > 0 ) - { - sampleRates[0] = userOptions->sampleRate; - bestSampleRate = userOptions->sampleRate; - numRates = 1; - } - if( userOptions->framesPerBuffer > 0 ) - { - framesPerBuffers[0] = userOptions->framesPerBuffer; - bestBufferSize = userOptions->framesPerBuffer; - numBufferSizes = 1; - } - PaQa_SetDefaultTestParameters( &testParams, inputDevice, outputDevice ); - testParams.maxFrames = (int) (0.5 * testParams.sampleRate); + + PaQa_OverrideTestParameters( &testParams, userOptions ); // Loop though combinations of audio parameters. for( iFlags=0; iFlagsmaxInputChannels < 2 ) { return 0; } outputDeviceInfo = Pa_GetDeviceInfo( outputDevice ); + QA_ASSERT_TRUE( "Pa_GetDeviceInfo for output returned NULL.", (outputDeviceInfo != NULL) ); if( outputDeviceInfo->maxOutputChannels < 2 ) { return 0; @@ -993,7 +1017,17 @@ int PaQa_CheckForLoopBack( PaDeviceIndex inputDevice, PaDeviceIndex outputDevice printf( "Look for loopback cable between \"%s\" => \"%s\"\n", outputDeviceInfo->name, inputDeviceInfo->name); + printf( " default suggested input latency (msec): low = %5.2f, high = %5.2f\n", + (inputDeviceInfo->defaultLowInputLatency * 1000.0), + (inputDeviceInfo->defaultHighInputLatency * 1000.0) ); + printf( " default suggested output latency (msec): low = %5.2f, high = %5.2f\n", + (outputDeviceInfo->defaultLowInputLatency * 1000.0), + (outputDeviceInfo->defaultHighInputLatency * 1000.0) ); + PaQa_SetDefaultTestParameters( &testParams, inputDevice, outputDevice ); + + PaQa_OverrideTestParameters( &testParams, userOptions ); + testParams.maxFrames = (int) (LOOPBACK_DETECTION_DURATION_SECONDS * testParams.sampleRate); minAmplitude = testParams.amplitude / 2.0; @@ -1007,7 +1041,7 @@ int PaQa_CheckForLoopBack( PaDeviceIndex inputDevice, PaDeviceIndex outputDevice { printf( "Output not supported for this format!\n" ); return 0; - } + }! PaQa_SetupLoopbackContext( &loopbackContext, &testParams ); @@ -1065,11 +1099,10 @@ int PaQa_CheckForLoopBack( PaDeviceIndex inputDevice, PaDeviceIndex outputDevice loopbackConnected = 0; } else if( rms > MAX_BACKGROUND_NOISE_RMS ) - { printf("ERROR - There is too much background noise on this input!\n"); + { + printf("ERROR - There is too much background noise on this input!\n"); loopbackConnected = 0; } - - } PaQa_TeardownLoopbackContext( &loopbackContext ); @@ -1087,7 +1120,7 @@ error: static int CheckLoopbackAndScan( UserOptions *userOptions, PaDeviceIndex iIn, PaDeviceIndex iOut ) { - int loopbackConnected = PaQa_CheckForLoopBack( iIn, iOut ); + int loopbackConnected = PaQa_CheckForLoopBack( userOptions, iIn, iOut ); if( loopbackConnected > 0 ) { PaQa_AnalyzeLoopbackConnection( userOptions, iIn, iOut ); @@ -1125,6 +1158,7 @@ static int ScanForLoopback(UserOptions *userOptions) else if (userOptions->outputDevice >= 0) { // Just scan for input. + QA_ASSERT_TRUE( "No good loopback cable found.", (numLoopbacks > 0) ); for( iIn=0; iInoutputDevice ); @@ -1136,6 +1170,7 @@ static int ScanForLoopback(UserOptions *userOptions) for( iOut=0; iOut 0) ); for( iIn=0; iIn