cannam@124: /** @file paqa_errs.c cannam@124: @ingroup qa_src cannam@124: @brief Self Testing Quality Assurance app for PortAudio cannam@124: Do lots of bad things to test error reporting. cannam@124: @author Phil Burk http://www.softsynth.com cannam@124: Pieter Suurmond adapted to V19 API. cannam@124: */ cannam@124: /* cannam@124: * $Id: paqa_errs.c 1756 2011-09-08 06:09:29Z philburk $ cannam@124: * cannam@124: * This program uses the PortAudio Portable Audio Library. cannam@124: * For more information see: http://www.portaudio.com cannam@124: * Copyright (c) 1999-2000 Ross Bencina and Phil Burk cannam@124: * cannam@124: * Permission is hereby granted, free of charge, to any person obtaining cannam@124: * a copy of this software and associated documentation files cannam@124: * (the "Software"), to deal in the Software without restriction, cannam@124: * including without limitation the rights to use, copy, modify, merge, cannam@124: * publish, distribute, sublicense, and/or sell copies of the Software, cannam@124: * and to permit persons to whom the Software is furnished to do so, cannam@124: * subject to the following conditions: cannam@124: * cannam@124: * The above copyright notice and this permission notice shall be cannam@124: * included in all copies or substantial portions of the Software. cannam@124: * cannam@124: * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, cannam@124: * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF cannam@124: * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. cannam@124: * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR cannam@124: * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF cannam@124: * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION cannam@124: * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. cannam@124: */ cannam@124: cannam@124: /* cannam@124: * The text above constitutes the entire PortAudio license; however, cannam@124: * the PortAudio community also makes the following non-binding requests: cannam@124: * cannam@124: * Any person wishing to distribute modifications to the Software is cannam@124: * requested to send the modifications to the original developer so that cannam@124: * they can be incorporated into the canonical version. It is also cannam@124: * requested that these non-binding requests be included along with the cannam@124: * license above. cannam@124: */ cannam@124: cannam@124: #include cannam@124: #include cannam@124: cannam@124: #include "portaudio.h" cannam@124: cannam@124: /*--------- Definitions ---------*/ cannam@124: #define MODE_INPUT (0) cannam@124: #define MODE_OUTPUT (1) cannam@124: #define FRAMES_PER_BUFFER (64) cannam@124: #define SAMPLE_RATE (44100.0) cannam@124: cannam@124: typedef struct PaQaData cannam@124: { cannam@124: unsigned long framesLeft; cannam@124: int numChannels; cannam@124: int bytesPerSample; cannam@124: int mode; cannam@124: } cannam@124: PaQaData; cannam@124: cannam@124: static int gNumPassed = 0; /* Two globals */ cannam@124: static int gNumFailed = 0; cannam@124: cannam@124: /*------------------- Macros ------------------------------*/ cannam@124: /* Print ERROR if it fails. Tally success or failure. Odd */ cannam@124: /* do-while wrapper seems to be needed for some compilers. */ cannam@124: cannam@124: #define EXPECT(_exp) \ cannam@124: do \ cannam@124: { \ cannam@124: if ((_exp)) {\ cannam@124: gNumPassed++; \ cannam@124: } \ cannam@124: else { \ cannam@124: printf("\nERROR - 0x%x - %s for %s\n", result, Pa_GetErrorText(result), #_exp ); \ cannam@124: gNumFailed++; \ cannam@124: goto error; \ cannam@124: } \ cannam@124: } while(0) cannam@124: cannam@124: #define HOPEFOR(_exp) \ cannam@124: do \ cannam@124: { \ cannam@124: if ((_exp)) {\ cannam@124: gNumPassed++; \ cannam@124: } \ cannam@124: else { \ cannam@124: printf("\nERROR - 0x%x - %s for %s\n", result, Pa_GetErrorText(result), #_exp ); \ cannam@124: gNumFailed++; \ cannam@124: } \ cannam@124: } while(0) cannam@124: cannam@124: /*-------------------------------------------------------------------------*/ cannam@124: /* This routine will be called by the PortAudio engine when audio is needed. cannam@124: It may be called at interrupt level on some machines so don't do anything cannam@124: that could mess up the system like calling malloc() or free(). cannam@124: */ cannam@124: static int QaCallback( const void* inputBuffer, cannam@124: void* outputBuffer, cannam@124: unsigned long framesPerBuffer, cannam@124: const PaStreamCallbackTimeInfo* timeInfo, cannam@124: PaStreamCallbackFlags statusFlags, cannam@124: void* userData ) cannam@124: { cannam@124: unsigned long i; cannam@124: unsigned char* out = (unsigned char *) outputBuffer; cannam@124: PaQaData* data = (PaQaData *) userData; cannam@124: cannam@124: (void)inputBuffer; /* Prevent "unused variable" warnings. */ cannam@124: cannam@124: /* Zero out buffer so we don't hear terrible noise. */ cannam@124: if( data->mode == MODE_OUTPUT ) cannam@124: { cannam@124: unsigned long numBytes = framesPerBuffer * data->numChannels * data->bytesPerSample; cannam@124: for( i=0; iframesLeft > framesPerBuffer ) cannam@124: { cannam@124: data->framesLeft -= framesPerBuffer; cannam@124: return 0; cannam@124: } cannam@124: else cannam@124: { cannam@124: data->framesLeft = 0; cannam@124: return 1; cannam@124: } cannam@124: } cannam@124: cannam@124: static PaDeviceIndex FindInputOnlyDevice(void) cannam@124: { cannam@124: PaDeviceIndex result = Pa_GetDefaultInputDevice(); cannam@124: if( result != paNoDevice && Pa_GetDeviceInfo(result)->maxOutputChannels == 0 ) cannam@124: return result; cannam@124: cannam@124: for( result = 0; result < Pa_GetDeviceCount(); ++result ) cannam@124: { cannam@124: if( Pa_GetDeviceInfo(result)->maxOutputChannels == 0 ) cannam@124: return result; cannam@124: } cannam@124: cannam@124: return paNoDevice; cannam@124: } cannam@124: cannam@124: static PaDeviceIndex FindOutputOnlyDevice(void) cannam@124: { cannam@124: PaDeviceIndex result = Pa_GetDefaultOutputDevice(); cannam@124: if( result != paNoDevice && Pa_GetDeviceInfo(result)->maxInputChannels == 0 ) cannam@124: return result; cannam@124: cannam@124: for( result = 0; result < Pa_GetDeviceCount(); ++result ) cannam@124: { cannam@124: if( Pa_GetDeviceInfo(result)->maxInputChannels == 0 ) cannam@124: return result; cannam@124: } cannam@124: cannam@124: return paNoDevice; cannam@124: } cannam@124: cannam@124: /*-------------------------------------------------------------------------------------------------*/ cannam@124: static int TestBadOpens( void ) cannam@124: { cannam@124: PaStream* stream = NULL; cannam@124: PaError result; cannam@124: PaQaData myData; cannam@124: PaStreamParameters ipp, opp; cannam@124: const PaDeviceInfo* info = NULL; cannam@124: cannam@124: cannam@124: /* Setup data for synthesis thread. */ cannam@124: myData.framesLeft = (unsigned long) (SAMPLE_RATE * 100); /* 100 seconds */ cannam@124: myData.numChannels = 1; cannam@124: myData.mode = MODE_OUTPUT; cannam@124: cannam@124: /*----------------------------- No devices specified: */ cannam@124: ipp.device = opp.device = paNoDevice; cannam@124: ipp.channelCount = opp.channelCount = 0; /* Also no channels. */ cannam@124: ipp.hostApiSpecificStreamInfo = opp.hostApiSpecificStreamInfo = NULL; cannam@124: ipp.sampleFormat = opp.sampleFormat = paFloat32; cannam@124: /* Take the low latency of the default device for all subsequent tests. */ cannam@124: info = Pa_GetDeviceInfo(Pa_GetDefaultInputDevice()); cannam@124: ipp.suggestedLatency = info ? info->defaultLowInputLatency : 0.100; cannam@124: info = Pa_GetDeviceInfo(Pa_GetDefaultOutputDevice()); cannam@124: opp.suggestedLatency = info ? info->defaultLowOutputLatency : 0.100; cannam@124: HOPEFOR(((result = Pa_OpenStream(&stream, &ipp, &opp, cannam@124: SAMPLE_RATE, FRAMES_PER_BUFFER, cannam@124: paClipOff, QaCallback, &myData )) == paInvalidDevice)); cannam@124: cannam@124: /*----------------------------- No devices specified #2: */ cannam@124: HOPEFOR(((result = Pa_OpenStream(&stream, NULL, NULL, cannam@124: SAMPLE_RATE, FRAMES_PER_BUFFER, cannam@124: paClipOff, QaCallback, &myData )) == paInvalidDevice)); cannam@124: cannam@124: /*----------------------------- Out of range input device specified: */ cannam@124: ipp.hostApiSpecificStreamInfo = opp.hostApiSpecificStreamInfo = NULL; cannam@124: ipp.sampleFormat = opp.sampleFormat = paFloat32; cannam@124: ipp.channelCount = 0; ipp.device = Pa_GetDeviceCount(); /* And no output device, and no channels. */ cannam@124: opp.channelCount = 0; opp.device = paNoDevice; cannam@124: HOPEFOR(((result = Pa_OpenStream(&stream, &ipp, NULL, cannam@124: SAMPLE_RATE, FRAMES_PER_BUFFER, cannam@124: paClipOff, QaCallback, &myData )) == paInvalidDevice)); cannam@124: cannam@124: /*----------------------------- Out of range output device specified: */ cannam@124: ipp.hostApiSpecificStreamInfo = opp.hostApiSpecificStreamInfo = NULL; cannam@124: ipp.sampleFormat = opp.sampleFormat = paFloat32; cannam@124: ipp.channelCount = 0; ipp.device = paNoDevice; /* And no input device, and no channels. */ cannam@124: opp.channelCount = 0; opp.device = Pa_GetDeviceCount(); cannam@124: HOPEFOR(((result = Pa_OpenStream(&stream, NULL, &opp, cannam@124: SAMPLE_RATE, FRAMES_PER_BUFFER, cannam@124: paClipOff, QaCallback, &myData )) == paInvalidDevice)); cannam@124: cannam@124: if (Pa_GetDefaultInputDevice() != paNoDevice) { cannam@124: /*----------------------------- Zero input channels: */ cannam@124: ipp.hostApiSpecificStreamInfo = opp.hostApiSpecificStreamInfo = NULL; cannam@124: ipp.sampleFormat = opp.sampleFormat = paFloat32; cannam@124: ipp.channelCount = 0; ipp.device = Pa_GetDefaultInputDevice(); cannam@124: opp.channelCount = 0; opp.device = paNoDevice; /* And no output device, and no output channels. */ cannam@124: HOPEFOR(((result = Pa_OpenStream(&stream, &ipp, NULL, cannam@124: SAMPLE_RATE, FRAMES_PER_BUFFER, cannam@124: paClipOff, QaCallback, &myData )) == paInvalidChannelCount)); cannam@124: } cannam@124: cannam@124: if (Pa_GetDefaultOutputDevice() != paNoDevice) { cannam@124: /*----------------------------- Zero output channels: */ cannam@124: ipp.hostApiSpecificStreamInfo = opp.hostApiSpecificStreamInfo = NULL; cannam@124: ipp.sampleFormat = opp.sampleFormat = paFloat32; cannam@124: ipp.channelCount = 0; ipp.device = paNoDevice; /* And no input device, and no input channels. */ cannam@124: opp.channelCount = 0; opp.device = Pa_GetDefaultOutputDevice(); cannam@124: HOPEFOR(((result = Pa_OpenStream(&stream, NULL, &opp, cannam@124: SAMPLE_RATE, FRAMES_PER_BUFFER, cannam@124: paClipOff, QaCallback, &myData )) == paInvalidChannelCount)); cannam@124: } cannam@124: /*----------------------------- Nonzero input and output channels but no output device: */ cannam@124: ipp.hostApiSpecificStreamInfo = opp.hostApiSpecificStreamInfo = NULL; cannam@124: ipp.sampleFormat = opp.sampleFormat = paFloat32; cannam@124: ipp.channelCount = 2; ipp.device = Pa_GetDefaultInputDevice(); /* Both stereo. */ cannam@124: opp.channelCount = 2; opp.device = paNoDevice; cannam@124: HOPEFOR(((result = Pa_OpenStream(&stream, &ipp, &opp, cannam@124: SAMPLE_RATE, FRAMES_PER_BUFFER, cannam@124: paClipOff, QaCallback, &myData )) == paInvalidDevice)); cannam@124: cannam@124: /*----------------------------- Nonzero input and output channels but no input device: */ cannam@124: ipp.hostApiSpecificStreamInfo = opp.hostApiSpecificStreamInfo = NULL; cannam@124: ipp.sampleFormat = opp.sampleFormat = paFloat32; cannam@124: ipp.channelCount = 2; ipp.device = paNoDevice; cannam@124: opp.channelCount = 2; opp.device = Pa_GetDefaultOutputDevice(); cannam@124: HOPEFOR(((result = Pa_OpenStream(&stream, &ipp, &opp, cannam@124: SAMPLE_RATE, FRAMES_PER_BUFFER, cannam@124: paClipOff, QaCallback, &myData )) == paInvalidDevice)); cannam@124: cannam@124: if (Pa_GetDefaultOutputDevice() != paNoDevice) { cannam@124: /*----------------------------- NULL stream pointer: */ cannam@124: ipp.hostApiSpecificStreamInfo = opp.hostApiSpecificStreamInfo = NULL; cannam@124: ipp.sampleFormat = opp.sampleFormat = paFloat32; cannam@124: ipp.channelCount = 0; ipp.device = paNoDevice; /* Output is more likely than input. */ cannam@124: opp.channelCount = 2; opp.device = Pa_GetDefaultOutputDevice(); /* Only 2 output channels. */ cannam@124: HOPEFOR(((result = Pa_OpenStream(NULL, &ipp, &opp, cannam@124: SAMPLE_RATE, FRAMES_PER_BUFFER, cannam@124: paClipOff, QaCallback, &myData )) == paBadStreamPtr)); cannam@124: cannam@124: /*----------------------------- Low sample rate: */ cannam@124: ipp.hostApiSpecificStreamInfo = opp.hostApiSpecificStreamInfo = NULL; cannam@124: ipp.sampleFormat = opp.sampleFormat = paFloat32; cannam@124: ipp.channelCount = 0; ipp.device = paNoDevice; cannam@124: opp.channelCount = 2; opp.device = Pa_GetDefaultOutputDevice(); cannam@124: HOPEFOR(((result = Pa_OpenStream(&stream, NULL, &opp, cannam@124: 1.0, FRAMES_PER_BUFFER, /* 1 cycle per second (1 Hz) is too low. */ cannam@124: paClipOff, QaCallback, &myData )) == paInvalidSampleRate)); cannam@124: cannam@124: /*----------------------------- High sample rate: */ cannam@124: ipp.hostApiSpecificStreamInfo = opp.hostApiSpecificStreamInfo = NULL; cannam@124: ipp.sampleFormat = opp.sampleFormat = paFloat32; cannam@124: ipp.channelCount = 0; ipp.device = paNoDevice; cannam@124: opp.channelCount = 2; opp.device = Pa_GetDefaultOutputDevice(); cannam@124: HOPEFOR(((result = Pa_OpenStream(&stream, NULL, &opp, cannam@124: 10000000.0, FRAMES_PER_BUFFER, /* 10^6 cycles per second (10 MHz) is too high. */ cannam@124: paClipOff, QaCallback, &myData )) == paInvalidSampleRate)); cannam@124: cannam@124: /*----------------------------- NULL callback: */ cannam@124: /* NULL callback is valid in V19 -- it means use blocking read/write stream cannam@124: cannam@124: ipp.hostApiSpecificStreamInfo = opp.hostApiSpecificStreamInfo = NULL; cannam@124: ipp.sampleFormat = opp.sampleFormat = paFloat32; cannam@124: ipp.channelCount = 0; ipp.device = paNoDevice; cannam@124: opp.channelCount = 2; opp.device = Pa_GetDefaultOutputDevice(); cannam@124: HOPEFOR(((result = Pa_OpenStream(&stream, NULL, &opp, cannam@124: SAMPLE_RATE, FRAMES_PER_BUFFER, cannam@124: paClipOff, cannam@124: NULL, cannam@124: &myData )) == paNullCallback)); cannam@124: */ cannam@124: cannam@124: /*----------------------------- Bad flag: */ cannam@124: ipp.hostApiSpecificStreamInfo = opp.hostApiSpecificStreamInfo = NULL; cannam@124: ipp.sampleFormat = opp.sampleFormat = paFloat32; cannam@124: ipp.channelCount = 0; ipp.device = paNoDevice; cannam@124: opp.channelCount = 2; opp.device = Pa_GetDefaultOutputDevice(); cannam@124: HOPEFOR(((result = Pa_OpenStream(&stream, NULL, &opp, cannam@124: SAMPLE_RATE, FRAMES_PER_BUFFER, cannam@124: 255, /* Is 8 maybe legal V19 API? */ cannam@124: QaCallback, &myData )) == paInvalidFlag)); cannam@124: } cannam@124: cannam@124: /*----------------------------- using input device as output device: */ cannam@124: if( FindInputOnlyDevice() != paNoDevice ) cannam@124: { cannam@124: ipp.hostApiSpecificStreamInfo = opp.hostApiSpecificStreamInfo = NULL; cannam@124: ipp.sampleFormat = opp.sampleFormat = paFloat32; cannam@124: ipp.channelCount = 0; ipp.device = paNoDevice; /* And no input device, and no channels. */ cannam@124: opp.channelCount = 2; opp.device = FindInputOnlyDevice(); cannam@124: HOPEFOR(((result = Pa_OpenStream(&stream, NULL, &opp, cannam@124: SAMPLE_RATE, FRAMES_PER_BUFFER, cannam@124: paClipOff, QaCallback, &myData )) == paInvalidChannelCount)); cannam@124: } cannam@124: cannam@124: /*----------------------------- using output device as input device: */ cannam@124: if( FindOutputOnlyDevice() != paNoDevice ) cannam@124: { cannam@124: ipp.hostApiSpecificStreamInfo = opp.hostApiSpecificStreamInfo = NULL; cannam@124: ipp.sampleFormat = opp.sampleFormat = paFloat32; cannam@124: ipp.channelCount = 2; ipp.device = FindOutputOnlyDevice(); cannam@124: opp.channelCount = 0; opp.device = paNoDevice; /* And no output device, and no channels. */ cannam@124: HOPEFOR(((result = Pa_OpenStream(&stream, &ipp, NULL, cannam@124: SAMPLE_RATE, FRAMES_PER_BUFFER, cannam@124: paClipOff, QaCallback, &myData )) == paInvalidChannelCount)); cannam@124: cannam@124: } cannam@124: cannam@124: if( stream != NULL ) Pa_CloseStream( stream ); cannam@124: return result; cannam@124: } cannam@124: cannam@124: /*-----------------------------------------------------------------------------------------*/ cannam@124: static int TestBadActions( void ) cannam@124: { cannam@124: PaStream* stream = NULL; cannam@124: const PaDeviceInfo* deviceInfo = NULL; cannam@124: PaError result = 0; cannam@124: PaQaData myData; cannam@124: PaStreamParameters opp; cannam@124: const PaDeviceInfo* info = NULL; cannam@124: cannam@124: /* Setup data for synthesis thread. */ cannam@124: myData.framesLeft = (unsigned long)(SAMPLE_RATE * 100); /* 100 seconds */ cannam@124: myData.numChannels = 1; cannam@124: myData.mode = MODE_OUTPUT; cannam@124: cannam@124: opp.device = Pa_GetDefaultOutputDevice(); /* Default output. */ cannam@124: opp.channelCount = 2; /* Stereo output. */ cannam@124: opp.hostApiSpecificStreamInfo = NULL; cannam@124: opp.sampleFormat = paFloat32; cannam@124: info = Pa_GetDeviceInfo(opp.device); cannam@124: opp.suggestedLatency = info ? info->defaultLowOutputLatency : 0.100; cannam@124: cannam@124: if (opp.device != paNoDevice) { cannam@124: HOPEFOR(((result = Pa_OpenStream(&stream, NULL, /* Take NULL as input parame- */ cannam@124: &opp, /* ters, meaning try only output. */ cannam@124: SAMPLE_RATE, FRAMES_PER_BUFFER, cannam@124: paClipOff, QaCallback, &myData )) == paNoError)); cannam@124: } cannam@124: cannam@124: HOPEFOR(((deviceInfo = Pa_GetDeviceInfo(paNoDevice)) == NULL)); cannam@124: HOPEFOR(((deviceInfo = Pa_GetDeviceInfo(87654)) == NULL)); cannam@124: HOPEFOR(((result = Pa_StartStream(NULL)) == paBadStreamPtr)); cannam@124: HOPEFOR(((result = Pa_StopStream(NULL)) == paBadStreamPtr)); cannam@124: HOPEFOR(((result = Pa_IsStreamStopped(NULL)) == paBadStreamPtr)); cannam@124: HOPEFOR(((result = Pa_IsStreamActive(NULL)) == paBadStreamPtr)); cannam@124: HOPEFOR(((result = Pa_CloseStream(NULL)) == paBadStreamPtr)); cannam@124: HOPEFOR(((result = Pa_SetStreamFinishedCallback(NULL, NULL)) == paBadStreamPtr)); cannam@124: HOPEFOR(((result = !Pa_GetStreamInfo(NULL)))); cannam@124: HOPEFOR(((result = Pa_GetStreamTime(NULL)) == 0.0)); cannam@124: HOPEFOR(((result = Pa_GetStreamCpuLoad(NULL)) == 0.0)); cannam@124: HOPEFOR(((result = Pa_ReadStream(NULL, NULL, 0)) == paBadStreamPtr)); cannam@124: HOPEFOR(((result = Pa_WriteStream(NULL, NULL, 0)) == paBadStreamPtr)); cannam@124: cannam@124: /** @todo test Pa_GetStreamReadAvailable and Pa_GetStreamWriteAvailable */ cannam@124: cannam@124: if (stream != NULL) Pa_CloseStream(stream); cannam@124: return result; cannam@124: } cannam@124: cannam@124: /*---------------------------------------------------------------------*/ cannam@124: int main(void); cannam@124: int main(void) cannam@124: { cannam@124: PaError result; cannam@124: cannam@124: EXPECT(((result = Pa_Initialize()) == paNoError)); cannam@124: TestBadOpens(); cannam@124: TestBadActions(); cannam@124: error: cannam@124: Pa_Terminate(); cannam@124: printf("QA Report: %d passed, %d failed.\n", gNumPassed, gNumFailed); cannam@124: return 0; cannam@124: }