diff --git a/qa/paqa_devs.c b/qa/paqa_devs.c index 81b7509c7..c90ff8c70 100644 --- a/qa/paqa_devs.c +++ b/qa/paqa_devs.c @@ -52,83 +52,173 @@ * license above. */ +#include #include -#include +#include /* for EXIT_SUCCESS and EXIT_FAILURE */ #include #define _USE_MATH_DEFINES #include #include "portaudio.h" #include "pa_trace.h" +#include "paqa_macros.h" /****************************************** Definitions ***********/ +#define RUN_TIME_SECONDS (1.2) +#define BYPASS_TESTS (0) /* If 1 then skip actual tests and just iterate. */ + #define MODE_INPUT (0) #define MODE_OUTPUT (1) #define MAX_TEST_CHANNELS (4) #define LOWEST_FREQUENCY (300.0) -#define LOWEST_SAMPLE_RATE (8000.0) -#define PHASE_INCREMENT (2.0 * M_PI * LOWEST_FREQUENCY / LOWEST_SAMPLE_RATE) #define SINE_AMPLITUDE (0.2) +#define MILLIS_PER_SECOND (1000.0) +#define DEFAULT_FRAMES_PER_BUFFER (128) -typedef struct PaQaData +#define TEST_LEVEL_QUICK (0) +#define TEST_LEVEL_NORMAL (1) +#define TEST_LEVEL_EXHAUSTIVE (2) + +PAQA_INSTANTIATE_GLOBALS + +typedef struct PaSineOscillator { - unsigned long framesLeft; - int numChannels; - int bytesPerSample; - int mode; float phase; - PaSampleFormat format; -} -PaQaData; + float phaseIncrement; +} PaSineOscillator; + +/* Parameters that cover all options for a test. + */ +typedef struct PaQaTestParameters +{ + PaDeviceIndex deviceID; + PaSampleFormat format; + double sampleRate; + double durationSeconds; + double suggestedLatency; + int framesPerBuffer; + int numInputChannels; + int numOutputChannels; + int mode; + int useCallback; + int useNonInterleaved; /* Test paNonInterleaved flag */ +} PaQaTestParameters; + +PaQaTestParameters kDefaultTestParameters = { + 0, /* deviceId */ + paFloat32, + 44100, + RUN_TIME_SECONDS, + 0.020, + DEFAULT_FRAMES_PER_BUFFER, + 0, /* numInputChannels */ + 1, /* numOutputChannels */ + MODE_OUTPUT, + 1, /* useCallback */ + 0, /* useNonInterleaved */ +}; + +/* Runtime data used during the test. */ +typedef struct PaQaData +{ + const PaQaTestParameters *parameters; + // Dynamic state. + int bytesPerSample; + volatile unsigned long frameCounter; + volatile unsigned long framesLeft; + unsigned long framesPerBurst; + unsigned long minFramesPerBuffer; + unsigned long maxFramesPerBuffer; + unsigned long framesDuration; + PaSineOscillator sineOscillators[MAX_TEST_CHANNELS]; + void *audioBuffer; +} PaQaData; /****************************************** Prototypes ***********/ -static void TestDevices( int mode, int allDevices ); -static void TestFormats( int mode, PaDeviceIndex deviceID, double sampleRate, - int numChannels ); -static int TestAdvance( int mode, PaDeviceIndex deviceID, double sampleRate, - int numChannels, PaSampleFormat format ); +static int TestSingleStreamParameters(PaQaTestParameters parameters); static int QaCallback( const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void *userData ); -/****************************************** Globals ***********/ -static int gNumPassed = 0; -static int gNumFailed = 0; - -/****************************************** Macros ***********/ -/* Print ERROR if it fails. Tally success or failure. */ -/* Odd do-while wrapper seems to be needed for some compilers. */ -#define EXPECT(_exp) \ - do \ - { \ - if ((_exp)) {\ - /* printf("SUCCESS for %s\n", #_exp ); */ \ - gNumPassed++; \ - } \ - else { \ - printf("ERROR - 0x%x - %s for %s\n", result, \ - ((result == 0) ? "-" : Pa_GetErrorText(result)), \ - #_exp ); \ - gNumFailed++; \ - goto error; \ - } \ - } while(0) - -static float NextSineSample( PaQaData *data ) +static void PaQaSetupData(PaQaData *myData, + const PaQaTestParameters *parameters) +{ + memset(myData, 0, sizeof(PaQaData)); + + myData->parameters = parameters; + myData->frameCounter = 0; + myData->framesLeft = (unsigned long) (parameters->sampleRate * parameters->durationSeconds); + + myData->minFramesPerBuffer = UINT32_MAX; + myData->maxFramesPerBuffer = 0; + + for (int channelIndex = 0; channelIndex < MAX_TEST_CHANNELS; channelIndex++) + { + myData->sineOscillators[channelIndex].phase = 0.0f; + myData->sineOscillators[channelIndex].phaseIncrement = + (2.0 * M_PI * LOWEST_FREQUENCY / parameters->sampleRate); + } + + switch( parameters->format ) + { + case paFloat32: + case paInt32: + case paInt24: + myData->bytesPerSample = 4; + break; + /* case paPackedInt24: + myData->bytesPerSample = 3; + break; */ + default: + myData->bytesPerSample = 2; + break; + } + myData->framesPerBurst = (parameters->framesPerBuffer == 0) ? 128 : parameters->framesPerBuffer; + if (parameters->useCallback == 0) { + /* We need our own buffer for blocking IO. */ + int numChannels = (parameters->mode == MODE_OUTPUT) + ? parameters->numOutputChannels + : parameters->numInputChannels; + myData->audioBuffer = malloc(myData->bytesPerSample * numChannels * myData->framesPerBurst); + } +} + +static void PaQaTeardownData(PaQaData *myData, + const PaQaTestParameters *parameters) +{ + (void) parameters; + free(myData->audioBuffer); +} + +static float NextSineSample( PaSineOscillator *sineOscillator ) { - float phase = data->phase + PHASE_INCREMENT; - if( phase > M_PI ) phase -= (float) (2.0 * M_PI); - data->phase = phase; + float phase = sineOscillator->phase + sineOscillator->phaseIncrement; + if( phase > (float)M_PI ) phase -= (float)(2.0 * M_PI); + sineOscillator->phase = phase; return sinf(phase) * SINE_AMPLITUDE; } +#define SETUP_BUFFERS(_data_type) \ + _data_type *out; \ + int stride; \ + if (parameters->useNonInterleaved) { \ + /* outputData points to an array of pointers to the buffers. */ \ + void **buffers = (void **)outputData; \ + out = (_data_type *)buffers[channelIndex]; \ + stride = 1; \ + } else { \ + out = &((_data_type *) outputData)[channelIndex]; \ + stride = parameters->numOutputChannels; \ + } + /*******************************************************************/ /* This routine will be called by the PortAudio engine when audio is needed. ** It may be called at interrupt level on some machines so don't do anything ** that could mess up the system like calling malloc() or free(). */ -static int QaCallback( const void *inputBuffer, void *outputBuffer, +static int QaCallback( const void *inputData, + void *outputData, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, @@ -138,22 +228,29 @@ static int QaCallback( const void *inputBuffer, void *outputBuffer, unsigned long channelIndex; float sample; PaQaData *data = (PaQaData *) userData; - (void) inputBuffer; + const PaQaTestParameters *parameters = data->parameters; + (void) inputData; + + data->minFramesPerBuffer = (framesPerBuffer < data->minFramesPerBuffer) + ? framesPerBuffer : data->minFramesPerBuffer; + data->maxFramesPerBuffer = (framesPerBuffer > data->maxFramesPerBuffer) + ? framesPerBuffer : data->maxFramesPerBuffer; /* Play simple sine wave. */ - if( data->mode == MODE_OUTPUT ) + if( parameters->mode == MODE_OUTPUT ) { - switch( data->format ) + switch( parameters->format ) { case paFloat32: { - float *out = (float *) outputBuffer; - for( frameIndex = 0; frameIndex < framesPerBuffer; frameIndex++ ) + for( channelIndex = 0; channelIndex < parameters->numOutputChannels; channelIndex++ ) { - sample = NextSineSample( data ); - for( channelIndex = 0; channelIndex < data->numChannels; channelIndex++ ) + SETUP_BUFFERS(float); + for( frameIndex = 0; frameIndex < framesPerBuffer; frameIndex++ ) { - *out++ = sample; + sample = NextSineSample( &data->sineOscillators[channelIndex] ); + *out = sample; + out += stride; } } } @@ -161,13 +258,14 @@ static int QaCallback( const void *inputBuffer, void *outputBuffer, case paInt32: { - int *out = (int *) outputBuffer; - for( frameIndex = 0; frameIndex < framesPerBuffer; frameIndex++ ) + for( channelIndex = 0; channelIndex < parameters->numOutputChannels; channelIndex++ ) { - sample = NextSineSample( data ); - for( channelIndex = 0; channelIndex < data->numChannels; channelIndex++ ) + SETUP_BUFFERS(int32_t); + for( frameIndex = 0; frameIndex < framesPerBuffer; frameIndex++ ) { - *out++ = ((int)(sample * 0x00800000)) << 8; + sample = NextSineSample( &data->sineOscillators[channelIndex] ); + *out = ((int32_t)(sample * 8388607)) << 8; + out += stride; } } } @@ -175,13 +273,14 @@ static int QaCallback( const void *inputBuffer, void *outputBuffer, case paInt16: { - short *out = (short *) outputBuffer; - for( frameIndex = 0; frameIndex < framesPerBuffer; frameIndex++ ) + for( channelIndex = 0; channelIndex < parameters->numOutputChannels; channelIndex++ ) { - sample = NextSineSample( data ); - for( channelIndex = 0; channelIndex < data->numChannels; channelIndex++ ) + SETUP_BUFFERS(int16_t); + for( frameIndex = 0; frameIndex < framesPerBuffer; frameIndex++ ) { - *out++ = (short)(sample * 32767); + sample = NextSineSample( &data->sineOscillators[channelIndex] ); + *out = (int16_t)(sample * 32767); + out += stride; } } } @@ -189,123 +288,418 @@ static int QaCallback( const void *inputBuffer, void *outputBuffer, default: { - unsigned char *out = (unsigned char *) outputBuffer; - unsigned long numBytes = framesPerBuffer * data->numChannels * data->bytesPerSample; + unsigned char *out = (unsigned char *) outputData; + unsigned long numBytes = framesPerBuffer * parameters->numOutputChannels * data->bytesPerSample; memset(out, 0, numBytes); } break; } } + + data->frameCounter += framesPerBuffer; + /* Are we through yet? */ if( data->framesLeft > framesPerBuffer ) { PaUtil_AddTraceMessage("QaCallback: running. framesLeft", data->framesLeft ); data->framesLeft -= framesPerBuffer; - return 0; + return paContinue; } else { PaUtil_AddTraceMessage("QaCallback: DONE! framesLeft", data->framesLeft ); data->framesLeft = 0; - return 1; + return paComplete; } } -/*******************************************************************/ -static void usage( const char *name ) -{ - printf("%s [-a]\n", name); - printf(" -a - Test ALL devices, otherwise just the default devices.\n"); - printf(" -i - Test INPUT only.\n"); - printf(" -o - Test OUTPUT only.\n"); - printf(" -? - Help\n"); +static PaError CheckBlockingIO(PaStream *stream, + PaQaData *data, + int millis) { + PaError result = paNoError; + double elapsedTime = 0.0; + double millisPerBurst = MILLIS_PER_SECOND * data->framesPerBurst / data->parameters->sampleRate; + while (elapsedTime < millis) { + int callbackResult; + if (data->parameters->mode == MODE_OUTPUT) { + callbackResult = QaCallback(NULL /*inputBuffer */, + data->audioBuffer, + data->framesPerBurst, + NULL /* timeInfo */, // TODO + 0, // stream flags + data); + if (callbackResult == 0) { + result = Pa_WriteStream(stream, data->audioBuffer, data->framesPerBurst); + ASSERT_EQ(paNoError, result); + } + } else if (data->parameters->mode == MODE_INPUT) { + result = Pa_ReadStream(stream, data->audioBuffer, data->framesPerBurst); + ASSERT_EQ(paNoError, result); + callbackResult = QaCallback(data->audioBuffer, + NULL /*outputBuffer */, + data->framesPerBurst, + NULL /* timeInfo */, // TODO + 0, // stream flags + data); + } + elapsedTime += millisPerBurst; + } +error: + return result; +} + +static void CheckDefaultCallbackRun(PaStream *stream, + PaQaData *data) { + PaError result = paNoError; + PaTime oldStreamTimeMillis = 0.0; + PaTime startStreamTimeMillis = 0.0; + unsigned long oldFramesLeft = INT32_MAX; + + oldStreamTimeMillis = Pa_GetStreamTime(stream) * MILLIS_PER_SECOND; + + ASSERT_EQ(0, Pa_IsStreamActive(stream)); + ASSERT_EQ(1, Pa_IsStreamStopped(stream)); + + ASSERT_EQ(paNoError, result = Pa_StartStream( stream )); + startStreamTimeMillis = Pa_GetStreamTime(stream) * MILLIS_PER_SECOND; + + ASSERT_EQ(1, Pa_IsStreamActive(stream)); + ASSERT_EQ(0, Pa_IsStreamStopped(stream)); + + /* Sleep long enough for the stream callback to have stopped itself. */ + while ((oldStreamTimeMillis - startStreamTimeMillis) < ((RUN_TIME_SECONDS + 0.5) * MILLIS_PER_SECOND) + && (data->framesLeft > 0)) + { + if (data->parameters->useCallback) { + Pa_Sleep(200); + } else { + result = CheckBlockingIO(stream, + data, + 200); + ASSERT_EQ(paNoError, result); + } + + PaTime newStreamTime = Pa_GetStreamTime(stream) * MILLIS_PER_SECOND; + //printf("oldStreamTime = %9.6f, newStreamTime = %9.6f\n", oldStreamTime, newStreamTime ); /**/ + ASSERT_LE(oldStreamTimeMillis, newStreamTime); + + /* Check to make sure callback is decrementing framesLeft. */ + unsigned long newFramesLeft = data->framesLeft; + //printf("oldFrames = %lu, newFrames = %lu\n", oldFramesLeft, newFramesLeft ); + ASSERT_GE(oldFramesLeft, newFramesLeft); + + oldStreamTimeMillis = newStreamTime; + oldFramesLeft = newFramesLeft; + } + + ASSERT_EQ(0, data->framesLeft); + ASSERT_LE((1 * data->parameters->sampleRate), data->frameCounter); + + if (data->parameters->framesPerBuffer > 0) { + ASSERT_EQ(data->parameters->framesPerBuffer, data->minFramesPerBuffer); + ASSERT_EQ(data->parameters->framesPerBuffer, data->maxFramesPerBuffer); + } else { + ASSERT_GT(data->minFramesPerBuffer, 0); + ASSERT_LT(data->maxFramesPerBuffer, data->parameters->sampleRate); + } + + ASSERT_EQ(data->parameters->useCallback ? 0 : 1, Pa_IsStreamActive(stream)); + ASSERT_EQ(0, Pa_IsStreamStopped(stream)); + + ASSERT_EQ(paNoError, result = Pa_StopStream( stream )); + + ASSERT_EQ(0, Pa_IsStreamActive(stream)); + ASSERT_EQ(1, Pa_IsStreamStopped(stream)); + + ASSERT_EQ(paNoError, result = Pa_CloseStream( stream )); + return; + +error: + printf("result = %d = for %s\n", result, Pa_GetErrorText(result)); + Pa_CloseStream(stream); + return; } /*******************************************************************/ -int main( int argc, char **argv ); -int main( int argc, char **argv ) +static int TestSingleStreamParameters(PaQaTestParameters testParameters) { - int i; - PaError result; - int allDevices = 0; - int testOutput = 1; - int testInput = 1; - char *executableName = argv[0]; + PaStreamParameters inputParameters, outputParameters, *ipp, *opp; + PaStream *stream = NULL; + PaQaData myData; + int numChannels = 0; - /* Parse command line parameters. */ - i = 1; - while( isampleRate)); + if (testParameters.mode == MODE_INPUT) { + ASSERT_EQ(0, (int)(streamInfo->outputLatency * 1000)); + } else { + ASSERT_EQ(0, (int)(streamInfo->inputLatency * 1000)); + } + } + CheckDefaultCallbackRun(stream, &myData); - default: - printf("Illegal option: %s\n", arg); - case '?': - usage( executableName ); - exit(1); - break; + } else { + printf(" Parameters NOT supported.\n"); + } + PaQaTeardownData(&myData, &testParameters); + return 0; + +error: + if( stream != NULL ) Pa_CloseStream( stream ); + PaQaTeardownData(&myData, &testParameters); + return -1; +} + + +static void RunQuickTest() +{ + PaQaTestParameters parameters = kDefaultTestParameters; + +#if 1 + printf("\n=========== INPUT ==============\n"); + parameters.mode = MODE_INPUT; + parameters.deviceID = Pa_GetDefaultInputDevice(); + parameters.format = paFloat32; + + parameters.sampleRate = 44100; + parameters.numInputChannels = 1; + TestSingleStreamParameters(parameters); + parameters.sampleRate = 22050; + TestSingleStreamParameters(parameters); + + parameters.sampleRate = 44100; + parameters.numInputChannels = 2; + TestSingleStreamParameters(parameters); + + parameters.useCallback = 0; + TestSingleStreamParameters(parameters); /* Blocking */ + parameters.useNonInterleaved = 1; + TestSingleStreamParameters(parameters); /* Blocking, NonInterleaved */ + parameters.useCallback = 1; + TestSingleStreamParameters(parameters); /* NonInterleaved */ + parameters.useCallback = 1; +#endif + + printf("\n=========== OUTPUT =============\n"); + parameters = kDefaultTestParameters; + parameters.mode = MODE_OUTPUT; + parameters.deviceID = Pa_GetDefaultOutputDevice(); + parameters.sampleRate = 48000; + parameters.numOutputChannels = 1; + parameters.format = paFloat32; + parameters.useCallback = 0; + TestSingleStreamParameters(parameters); + + /* Interleaved */ + parameters = kDefaultTestParameters; + parameters.deviceID = Pa_GetDefaultOutputDevice(); + parameters.useNonInterleaved = 0; + parameters.numOutputChannels = 1; + parameters.useCallback = 1; + parameters.format = paFloat32; + TestSingleStreamParameters(parameters); + parameters.useCallback = 0; + parameters.format = paFloat32; + TestSingleStreamParameters(parameters); /* Blocking */ + parameters.useCallback = 1; + + parameters.sampleRate = 44100; + parameters.numOutputChannels = 2; + parameters.format = paFloat32; + TestSingleStreamParameters(parameters); + + parameters.sampleRate = 22050; + parameters.numOutputChannels = 2; + parameters.format = paInt16; + TestSingleStreamParameters(parameters); + + /* Non-Interleaved */ + parameters = kDefaultTestParameters; + parameters.deviceID = Pa_GetDefaultOutputDevice(); + parameters.useNonInterleaved = 1; + parameters.numOutputChannels = 2; + parameters.format = paFloat32; + parameters.useCallback = 0; + TestSingleStreamParameters(parameters); /* Blocking */ + parameters.useCallback = 1; + TestSingleStreamParameters(parameters); /* Blocking */ + parameters.format = paInt16; + TestSingleStreamParameters(parameters); + parameters.format = paInt32; + TestSingleStreamParameters(parameters); +} + +static const double constStandardSampleRates_[] = { + 8000.0, 9600.0, 11025.0, 12000.0, 16000.0, 22050.0, + 24000.0, 32000.0, 44100.0, 48000.0, 88200.0, 96000.0, + -1.0 }; /* Negative terminated list. */ + +static const PaSampleFormat constFormatsToTest_[] = { + paFloat32, paInt32, paInt16, 0 +}; /* Zero terminated list. */ + +/** + * Iterate through each option with other options set to default. + */ +static void TestNormal( int mode, int allDevices ) +{ + PaQaTestParameters parameters = kDefaultTestParameters; + int id, jc, i; + int maxChannels; + int isDefault; + const PaDeviceInfo *pdi; + int numDevices = Pa_GetDeviceCount(); + parameters.mode = mode; + + for( id=0; idmaxInputChannels; + isDefault = ( id == Pa_GetDefaultInputDevice()); + } else { + maxChannels = pdi->maxOutputChannels; + isDefault = ( id == Pa_GetDefaultOutputDevice()); + } + if( maxChannels > MAX_TEST_CHANNELS ) + maxChannels = MAX_TEST_CHANNELS; + if (maxChannels == 0) continue; // skip this device, wrong direction + + if (!allDevices && !isDefault) continue; // skip this device + + printf("\n===========================================================\n"); + printf(" Device = %s\n", pdi->name ); + printf("===========================================================\n"); + for( jc=1; jc<=maxChannels; jc++ ) + { + if (mode == MODE_INPUT) { + parameters.numInputChannels = jc; + } else { + parameters.numOutputChannels = jc; } + TestSingleStreamParameters(parameters); } - else + + /* Try each standard sample rate. */ + for( i=0; constStandardSampleRates_[i] > 0; i++ ) { - printf("Illegal argument: %s\n", arg); - usage( executableName ); - return 1; + parameters.sampleRate = constStandardSampleRates_[i]; + TestSingleStreamParameters(parameters); + } + parameters.sampleRate = pdi->defaultSampleRate; + if (mode == MODE_INPUT) { + parameters.suggestedLatency = pdi->defaultHighInputLatency; + TestSingleStreamParameters(parameters); + parameters.suggestedLatency = pdi->defaultLowInputLatency; + TestSingleStreamParameters(parameters); + } else { + parameters.suggestedLatency = pdi->defaultHighOutputLatency; + TestSingleStreamParameters(parameters); + parameters.suggestedLatency = pdi->defaultLowOutputLatency; + TestSingleStreamParameters(parameters); } - i += 1; - } - EXPECT(sizeof(short) == 2); /* The callback assumes we have 16-bit shorts. */ - EXPECT(sizeof(int) == 4); /* The callback assumes we have 32-bit ints. */ - EXPECT( ((result=Pa_Initialize()) == 0) ); + for (int callback = 0; callback < 2; callback++) { + parameters.useCallback = callback; + for (int nonInterleaved = 0; nonInterleaved < 2; nonInterleaved++) { + parameters.useNonInterleaved = nonInterleaved; + TestSingleStreamParameters(parameters); + } + } + parameters.useCallback = 1; + parameters.useNonInterleaved = 0; - if( testOutput ) - { - printf("\n---- Test OUTPUT ---------------\n"); - TestDevices( MODE_OUTPUT, allDevices ); - } - if( testInput ) - { - printf("\n---- Test INPUT ---------------\n"); - TestDevices( MODE_INPUT, allDevices ); + for (int jf = 0; constFormatsToTest_[jf] > 0; jf++) { + parameters.format = constFormatsToTest_[jf]; + TestSingleStreamParameters(parameters); + } } - -error: - Pa_Terminate(); - printf("QA Report: %d passed, %d failed.\n", gNumPassed, gNumFailed ); - return (gNumFailed > 0) ? 1 : 0; } /******************************************************************* -* Try each output device, through its full range of capabilities. */ -static void TestDevices( int mode, int allDevices ) +* Test each output device, through its full range of capabilities. */ +static void TestExhaustive( int mode, int allDevices ) { + PaQaTestParameters parameters = kDefaultTestParameters; int id, jc, i; int maxChannels; int isDefault; const PaDeviceInfo *pdi; - static double standardSampleRates[] = { 8000.0, 9600.0, 11025.0, 12000.0, - 16000.0, 22050.0, 24000.0, - 32000.0, 44100.0, 48000.0, - 88200.0, 96000.0, - -1.0 }; /* Negative terminated list. */ int numDevices = Pa_GetDeviceCount(); + parameters.mode = mode; + for( id=0; id MAX_TEST_CHANNELS ) maxChannels = MAX_TEST_CHANNELS; + if (maxChannels == 0) continue; // skip this device, wrong direction if (!allDevices && !isDefault) continue; // skip this device + printf("\n===========================================================\n"); + printf(" Device = %s\n", pdi->name ); + printf("===========================================================\n"); for( jc=1; jc<=maxChannels; jc++ ) { - printf("\n===========================================================\n"); - printf(" Device = %s\n", pdi->name ); - printf("===========================================================\n"); + printf("\n---------------------- NumChannels = %d ------------\n", jc ); + if (mode == MODE_INPUT) { + parameters.numInputChannels = jc; + } else { + parameters.numOutputChannels = jc; + } /* Try each standard sample rate. */ - for( i=0; standardSampleRates[i] > 0; i++ ) + for( i=0; constStandardSampleRates_[i] > 0; i++ ) { - TestFormats( mode, (PaDeviceIndex)id, standardSampleRates[i], jc ); + parameters.sampleRate = constStandardSampleRates_[i]; + for (int callback = 0; callback < 2; callback++) { + parameters.useCallback = callback; + for (int nonInterleaved = 0; nonInterleaved < 2; nonInterleaved++) { + parameters.useNonInterleaved = nonInterleaved; + for (int jf = 0; constFormatsToTest_[jf] > 0; jf++) { + parameters.format = constFormatsToTest_[jf]; + TestSingleStreamParameters(parameters); + } + } + } } } } } /*******************************************************************/ -static void TestFormats( int mode, PaDeviceIndex deviceID, double sampleRate, - int numChannels ) +static void usage( const char *name ) { - TestAdvance( mode, deviceID, sampleRate, numChannels, paFloat32 ); - TestAdvance( mode, deviceID, sampleRate, numChannels, paInt16 ); - TestAdvance( mode, deviceID, sampleRate, numChannels, paInt32 ); - /* TestAdvance( mode, deviceID, sampleRate, numChannels, paInt24 ); */ + printf("%s [-a] {-tN}\n", name); + printf(" -a - Test ALL devices, otherwise just the default devices.\n"); + printf(" -i - test INPUT only.\n"); + printf(" -o - test OUTPUT only.\n"); + printf(" -t - Test level, 0=Quick, 1=Normal, 2=Exhaustive\n"); + printf(" -? - Help\n"); } /*******************************************************************/ -static int TestAdvance( int mode, PaDeviceIndex deviceID, double sampleRate, - int numChannels, PaSampleFormat format ) +int main( int argc, char **argv ); +int main( int argc, char **argv ) { - PaStreamParameters inputParameters, outputParameters, *ipp, *opp; - PaStream *stream = NULL; - PaError result = paNoError; - PaQaData myData; - #define FRAMES_PER_BUFFER (64) - const int kNumSeconds = 100; - - /* Setup data for synthesis thread. */ - myData.framesLeft = (unsigned long) (sampleRate * kNumSeconds); - myData.numChannels = numChannels; - myData.mode = mode; - myData.format = format; - switch( format ) - { - case paFloat32: - case paInt32: - case paInt24: - myData.bytesPerSample = 4; - break; -/* case paPackedInt24: - myData.bytesPerSample = 3; - break; */ - default: - myData.bytesPerSample = 2; - break; - } + int i; + PaError result; + int allDevices = 0; + int testOutput = 1; + int testInput = 1; + int testLevel = TEST_LEVEL_NORMAL; + char *executableName = argv[0]; - if( mode == MODE_INPUT ) - { - inputParameters.device = deviceID; - inputParameters.channelCount = numChannels; - inputParameters.sampleFormat = format; - inputParameters.suggestedLatency = - Pa_GetDeviceInfo( inputParameters.device )->defaultLowInputLatency; - inputParameters.hostApiSpecificStreamInfo = NULL; - ipp = &inputParameters; - } - else + /* Parse command line parameters. */ + i = 1; + while( idefaultLowOutputLatency; - outputParameters.hostApiSpecificStreamInfo = NULL; - opp = &outputParameters; - } - else - { - opp = NULL; + default: + printf("Illegal option: %s\n", arg); + case '?': + usage( executableName ); + exit(1); + break; + } + } + else + { + printf("Illegal argument: %s\n", arg); + usage( executableName ); + return 1; + } + i += 1; } - if(paFormatIsSupported == Pa_IsFormatSupported( ipp, opp, sampleRate )) - { - printf("------ TestAdvance: %s, device = %d, rate = %g" - ", numChannels = %d, format = %lu -------\n", - ( mode == MODE_INPUT ) ? "INPUT" : "OUTPUT", - deviceID, sampleRate, numChannels, (unsigned long)format); - EXPECT( ((result = Pa_OpenStream( &stream, - ipp, - opp, - sampleRate, - FRAMES_PER_BUFFER, - paClipOff, /* we won't output out of range samples so don't bother clipping them */ - QaCallback, - &myData ) ) == 0) ); - if( stream ) + ASSERT_EQ(2, sizeof(short)); /* The callback assumes we have 16-bit shorts. */ + ASSERT_EQ(4, sizeof(int)); /* The callback assumes we have 32-bit ints. */ + ASSERT_EQ(paNoError, (result=Pa_Initialize())); + + if (testLevel == TEST_LEVEL_QUICK) { + printf("\n---- Quick Test ---------------\n"); + RunQuickTest(); + } else { + if( testInput ) + { + printf("\n---- Test INPUT ---------------\n"); + if (testLevel == TEST_LEVEL_NORMAL) { + TestNormal( MODE_INPUT, allDevices ); + } else { + TestExhaustive( MODE_INPUT, allDevices ); + } + } + if( testOutput ) { - PaTime oldStamp, newStamp; - unsigned long oldFrames; - int minDelay = ( mode == MODE_INPUT ) ? 1000 : 400; - /* Was: - int minNumBuffers = Pa_GetMinNumBuffers( FRAMES_PER_BUFFER, sampleRate ); - int msec = (int) ((minNumBuffers * 3 * 1000.0 * FRAMES_PER_BUFFER) / sampleRate); - */ - int msec = (int)( 3.0 * - (( mode == MODE_INPUT ) ? inputParameters.suggestedLatency : outputParameters.suggestedLatency )); - if( msec < minDelay ) msec = minDelay; - printf("msec = %d\n", msec); /**/ - EXPECT( ((result=Pa_StartStream( stream )) == 0) ); - /* Check to make sure PortAudio is advancing timeStamp. */ - oldStamp = Pa_GetStreamTime(stream); - Pa_Sleep(msec); - newStamp = Pa_GetStreamTime(stream); - printf("oldStamp = %9.6f, newStamp = %9.6f\n", oldStamp, newStamp ); /**/ - EXPECT( (oldStamp < newStamp) ); - /* Check to make sure callback is decrementing framesLeft. */ - oldFrames = myData.framesLeft; - Pa_Sleep(msec); - printf("oldFrames = %lu, myData.framesLeft = %lu\n", oldFrames, myData.framesLeft ); /**/ - EXPECT( (oldFrames > myData.framesLeft) ); - EXPECT( ((result=Pa_CloseStream( stream )) == 0) ); - stream = NULL; + printf("\n---- Test OUTPUT ---------------\n"); + if (testLevel == TEST_LEVEL_NORMAL) { + TestNormal( MODE_OUTPUT, allDevices ); + } else { + TestExhaustive( MODE_OUTPUT, allDevices ); + } } } - return 0; + error: - if( stream != NULL ) Pa_CloseStream( stream ); - return -1; + ASSERT_EQ(paNoError, Pa_Terminate()); + + PAQA_PRINT_RESULT; + return PAQA_EXIT_RESULT; } diff --git a/qa/paqa_errs.c b/qa/paqa_errs.c index 8d4094f94..3c66f2c58 100644 --- a/qa/paqa_errs.c +++ b/qa/paqa_errs.c @@ -43,10 +43,13 @@ * license above. */ +#include #include +#include /* for EXIT_SUCCESS and EXIT_FAILURE */ #include #include "portaudio.h" +#include "paqa_macros.h" /*--------- Definitions ---------*/ #define MODE_INPUT (0) @@ -54,6 +57,8 @@ #define FRAMES_PER_BUFFER (64) #define SAMPLE_RATE (44100.0) +PAQA_INSTANTIATE_GLOBALS + typedef struct PaQaData { unsigned long framesLeft; @@ -63,38 +68,6 @@ typedef struct PaQaData } PaQaData; -static int gNumPassed = 0; /* Two globals */ -static int gNumFailed = 0; - -/*------------------- Macros ------------------------------*/ -/* Print ERROR if it fails. Tally success or failure. Odd */ -/* do-while wrapper seems to be needed for some compilers. */ - -#define EXPECT(_exp) \ - do \ - { \ - if ((_exp)) {\ - gNumPassed++; \ - } \ - else { \ - printf("\nERROR - 0x%x - %s for %s\n", result, Pa_GetErrorText(result), #_exp ); \ - gNumFailed++; \ - goto error; \ - } \ - } while(0) - -#define HOPEFOR(_exp) \ - do \ - { \ - if ((_exp)) {\ - gNumPassed++; \ - } \ - else { \ - printf("\nERROR - 0x%x - %s for %s\n", result, Pa_GetErrorText(result), #_exp ); \ - gNumFailed++; \ - } \ - } while(0) - /*-------------------------------------------------------------------------*/ /* This routine will be called by the PortAudio engine when audio is needed. It may be called at interrupt level on some machines so don't do anything @@ -393,11 +366,14 @@ int main(void) { PaError result; - EXPECT(((result = Pa_Initialize()) == paNoError)); + printf("-----------------------------\n"); + printf("paqa_errs - PortAudio QA test\n"); + ASSERT_EQ(paNoError, (result = Pa_Initialize())); TestBadOpens(); TestBadActions(); error: Pa_Terminate(); - printf("QA Report: %d passed, %d failed.\n", gNumPassed, gNumFailed); - return 0; + + PAQA_PRINT_RESULT; + return PAQA_EXIT_RESULT; } diff --git a/qa/paqa_macros.h b/qa/paqa_macros.h new file mode 100644 index 000000000..5de07585b --- /dev/null +++ b/qa/paqa_macros.h @@ -0,0 +1,71 @@ + +#ifndef PORTAUDIO_QA_PAQA_MACROS_H +#define PORTAUDIO_QA_PAQA_MACROS_H + +extern int paQaNumPassed; +extern int paQaNumFailed; + +/* You must use this macro exactly once in each test program. */ +#define PAQA_INSTANTIATE_GLOBALS\ + int paQaNumPassed = 0;\ + int paQaNumFailed = 0; + +/*------------------- Macros ------------------------------*/ +/* Print ERROR if it fails. Tally success or failure. Odd */ +/* do-while wrapper seems to be needed for some compilers. */ +#define ASSERT_TRUE(_exp) \ + do \ + { \ + if (_exp) {\ + paQaNumPassed++; \ + } \ + else { \ + printf("ERROR at %s:%d, (%s) not true\n", \ + __FILE__, __LINE__, #_exp ); \ + paQaNumFailed++; \ + goto error; \ + } \ + } while(0) + +#define ASSERT_AB(_a, _b, _op, _opn) \ + do \ + { \ + int mA = (int)(_a); \ + int mB = (int)(_b); \ + if (mA _op mB) {\ + paQaNumPassed++; \ + } \ + else { \ + printf("ERROR at %s:%d, (%s) %s (%s), %d %s %d\n", \ + __FILE__, __LINE__, #_a, #_opn, #_b, mA, #_opn, mB ); \ + paQaNumFailed++; \ + goto error; \ + } \ + } while(0) + +#define ASSERT_EQ(_a, _b) ASSERT_AB(_a, _b, ==, !=) +#define ASSERT_NE(_a, _b) ASSERT_AB(_a, _b, !=, ==) +#define ASSERT_GT(_a, _b) ASSERT_AB(_a, _b, >, <=) +#define ASSERT_GE(_a, _b) ASSERT_AB(_a, _b, >=, <) +#define ASSERT_LT(_a, _b) ASSERT_AB(_a, _b, <, >=) +#define ASSERT_LE(_a, _b) ASSERT_AB(_a, _b, <=, >) + +#define HOPEFOR(_exp) \ + do \ + { \ + if ((_exp)) {\ + paQaNumPassed++; \ + } \ + else { \ + printf("\nERROR - 0x%x - %s for %s\n", result, Pa_GetErrorText(result), #_exp ); \ + paQaNumFailed++; \ + } \ + } while(0) + +#define PAQA_PRINT_RESULT \ + printf("QA Report: %d passed, %d failed.\n", paQaNumPassed, paQaNumFailed ) + +#define PAQA_EXIT_RESULT \ + (((paQaNumFailed > 0) || (paQaNumPassed == 0)) ? EXIT_FAILURE : EXIT_SUCCESS) + +#endif /* PORTAUDIO_QA_PAQA_MACROS_H */