paqa_devs.c (13780B)
1 /** @file paqa_devs.c 2 @ingroup qa_src 3 @brief Self Testing Quality Assurance app for PortAudio 4 Try to open each device and run through all the 5 possible configurations. This test does not verify 6 that the configuration works well. It just verifies 7 that it does not crash. It requires a human to listen to 8 the outputs. 9 10 @author Phil Burk http://www.softsynth.com 11 12 Pieter adapted to V19 API. Test now relies heavily on 13 Pa_IsFormatSupported(). Uses same 'standard' sample rates 14 as in test pa_devs.c. 15 */ 16 /* 17 * $Id$ 18 * 19 * This program uses the PortAudio Portable Audio Library. 20 * For more information see: http://www.portaudio.com 21 * Copyright (c) 1999-2000 Ross Bencina and Phil Burk 22 * 23 * Permission is hereby granted, free of charge, to any person obtaining 24 * a copy of this software and associated documentation files 25 * (the "Software"), to deal in the Software without restriction, 26 * including without limitation the rights to use, copy, modify, merge, 27 * publish, distribute, sublicense, and/or sell copies of the Software, 28 * and to permit persons to whom the Software is furnished to do so, 29 * subject to the following conditions: 30 * 31 * The above copyright notice and this permission notice shall be 32 * included in all copies or substantial portions of the Software. 33 * 34 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 35 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 36 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 37 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 38 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 39 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 40 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 41 */ 42 43 /* 44 * The text above constitutes the entire PortAudio license; however, 45 * the PortAudio community also makes the following non-binding requests: 46 * 47 * Any person wishing to distribute modifications to the Software is 48 * requested to send the modifications to the original developer so that 49 * they can be incorporated into the canonical version. It is also 50 * requested that these non-binding requests be included along with the 51 * license above. 52 */ 53 54 #include <stdio.h> 55 #include <math.h> 56 #include "portaudio.h" 57 #include "pa_trace.h" 58 59 /****************************************** Definitions ***********/ 60 #define MODE_INPUT (0) 61 #define MODE_OUTPUT (1) 62 #define MAX_TEST_CHANNELS 4 63 64 typedef struct PaQaData 65 { 66 unsigned long framesLeft; 67 int numChannels; 68 int bytesPerSample; 69 int mode; 70 short sawPhase; 71 PaSampleFormat format; 72 } 73 PaQaData; 74 75 /****************************************** Prototypes ***********/ 76 static void TestDevices( int mode ); 77 static void TestFormats( int mode, PaDeviceIndex deviceID, double sampleRate, 78 int numChannels ); 79 static int TestAdvance( int mode, PaDeviceIndex deviceID, double sampleRate, 80 int numChannels, PaSampleFormat format ); 81 static int QaCallback( const void *inputBuffer, void *outputBuffer, 82 unsigned long framesPerBuffer, 83 const PaStreamCallbackTimeInfo* timeInfo, 84 PaStreamCallbackFlags statusFlags, 85 void *userData ); 86 87 /****************************************** Globals ***********/ 88 static int gNumPassed = 0; 89 static int gNumFailed = 0; 90 91 /****************************************** Macros ***********/ 92 /* Print ERROR if it fails. Tally success or failure. */ 93 /* Odd do-while wrapper seems to be needed for some compilers. */ 94 #define EXPECT(_exp) \ 95 do \ 96 { \ 97 if ((_exp)) {\ 98 /* printf("SUCCESS for %s\n", #_exp ); */ \ 99 gNumPassed++; \ 100 } \ 101 else { \ 102 printf("ERROR - 0x%x - %s for %s\n", result, \ 103 ((result == 0) ? "-" : Pa_GetErrorText(result)), \ 104 #_exp ); \ 105 gNumFailed++; \ 106 goto error; \ 107 } \ 108 } while(0) 109 110 /*******************************************************************/ 111 /* This routine will be called by the PortAudio engine when audio is needed. 112 ** It may be called at interrupt level on some machines so don't do anything 113 ** that could mess up the system like calling malloc() or free(). 114 */ 115 static int QaCallback( const void *inputBuffer, void *outputBuffer, 116 unsigned long framesPerBuffer, 117 const PaStreamCallbackTimeInfo* timeInfo, 118 PaStreamCallbackFlags statusFlags, 119 void *userData ) 120 { 121 unsigned long i; 122 short phase; 123 PaQaData *data = (PaQaData *) userData; 124 (void) inputBuffer; 125 126 /* Play simple sawtooth wave. */ 127 if( data->mode == MODE_OUTPUT ) 128 { 129 phase = data->sawPhase; 130 switch( data->format ) 131 { 132 case paFloat32: 133 { 134 float *out = (float *) outputBuffer; 135 for( i=0; i<framesPerBuffer; i++ ) 136 { 137 phase += 0x123; 138 *out++ = (float) (phase * (1.0 / 32768.0)); 139 if( data->numChannels == 2 ) 140 { 141 *out++ = (float) (phase * (1.0 / 32768.0)); 142 } 143 } 144 } 145 break; 146 147 case paInt32: 148 { 149 int *out = (int *) outputBuffer; 150 for( i=0; i<framesPerBuffer; i++ ) 151 { 152 phase += 0x123; 153 *out++ = ((int) phase ) << 16; 154 if( data->numChannels == 2 ) 155 { 156 *out++ = ((int) phase ) << 16; 157 } 158 } 159 } 160 break; 161 162 case paInt16: 163 { 164 short *out = (short *) outputBuffer; 165 for( i=0; i<framesPerBuffer; i++ ) 166 { 167 phase += 0x123; 168 *out++ = phase; 169 if( data->numChannels == 2 ) 170 { 171 *out++ = phase; 172 } 173 } 174 } 175 break; 176 177 default: 178 { 179 unsigned char *out = (unsigned char *) outputBuffer; 180 unsigned long numBytes = framesPerBuffer * data->numChannels * data->bytesPerSample; 181 for( i=0; i<numBytes; i++ ) 182 { 183 *out++ = 0; 184 } 185 } 186 break; 187 } 188 data->sawPhase = phase; 189 } 190 /* Are we through yet? */ 191 if( data->framesLeft > framesPerBuffer ) 192 { 193 PaUtil_AddTraceMessage("QaCallback: running. framesLeft", data->framesLeft ); 194 data->framesLeft -= framesPerBuffer; 195 return 0; 196 } 197 else 198 { 199 PaUtil_AddTraceMessage("QaCallback: DONE! framesLeft", data->framesLeft ); 200 data->framesLeft = 0; 201 return 1; 202 } 203 } 204 205 /*******************************************************************/ 206 int main(void); 207 int main(void) 208 { 209 PaError result; 210 EXPECT( ((result=Pa_Initialize()) == 0) ); 211 printf("Test OUTPUT ---------------\n"); 212 TestDevices( MODE_OUTPUT ); 213 printf("Test INPUT ---------------\n"); 214 TestDevices( MODE_INPUT ); 215 error: 216 Pa_Terminate(); 217 printf("QA Report: %d passed, %d failed.\n", gNumPassed, gNumFailed ); 218 return (gNumFailed > 0) ? 1 : 0; 219 } 220 221 /******************************************************************* 222 * Try each output device, through its full range of capabilities. */ 223 static void TestDevices( int mode ) 224 { 225 int id, jc, i; 226 int maxChannels; 227 const PaDeviceInfo *pdi; 228 static double standardSampleRates[] = { 8000.0, 9600.0, 11025.0, 12000.0, 229 16000.0, 22050.0, 24000.0, 230 32000.0, 44100.0, 48000.0, 231 88200.0, 96000.0, 232 -1.0 }; /* Negative terminated list. */ 233 int numDevices = Pa_GetDeviceCount(); 234 for( id=0; id<numDevices; id++ ) /* Iterate through all devices. */ 235 { 236 pdi = Pa_GetDeviceInfo( id ); 237 /* Try 1 to maxChannels on each device. */ 238 maxChannels = (( mode == MODE_INPUT ) ? pdi->maxInputChannels : pdi->maxOutputChannels); 239 if( maxChannels > MAX_TEST_CHANNELS ) 240 maxChannels = MAX_TEST_CHANNELS; 241 242 for( jc=1; jc<=maxChannels; jc++ ) 243 { 244 printf("\n========================================================================\n"); 245 printf(" Device = %s\n", pdi->name ); 246 printf("========================================================================\n"); 247 /* Try each standard sample rate. */ 248 for( i=0; standardSampleRates[i] > 0; i++ ) 249 { 250 TestFormats( mode, (PaDeviceIndex)id, standardSampleRates[i], jc ); 251 } 252 } 253 } 254 } 255 256 /*******************************************************************/ 257 static void TestFormats( int mode, PaDeviceIndex deviceID, double sampleRate, 258 int numChannels ) 259 { 260 TestAdvance( mode, deviceID, sampleRate, numChannels, paFloat32 ); 261 TestAdvance( mode, deviceID, sampleRate, numChannels, paInt16 ); 262 TestAdvance( mode, deviceID, sampleRate, numChannels, paInt32 ); 263 /* TestAdvance( mode, deviceID, sampleRate, numChannels, paInt24 ); */ 264 } 265 266 /*******************************************************************/ 267 static int TestAdvance( int mode, PaDeviceIndex deviceID, double sampleRate, 268 int numChannels, PaSampleFormat format ) 269 { 270 PaStreamParameters inputParameters, outputParameters, *ipp, *opp; 271 PaStream *stream = NULL; 272 PaError result = paNoError; 273 PaQaData myData; 274 #define FRAMES_PER_BUFFER (64) 275 const int kNumSeconds = 100; 276 277 /* Setup data for synthesis thread. */ 278 myData.framesLeft = (unsigned long) (sampleRate * kNumSeconds); 279 myData.numChannels = numChannels; 280 myData.mode = mode; 281 myData.format = format; 282 switch( format ) 283 { 284 case paFloat32: 285 case paInt32: 286 case paInt24: 287 myData.bytesPerSample = 4; 288 break; 289 /* case paPackedInt24: 290 myData.bytesPerSample = 3; 291 break; */ 292 default: 293 myData.bytesPerSample = 2; 294 break; 295 } 296 297 if( mode == MODE_INPUT ) 298 { 299 inputParameters.device = deviceID; 300 inputParameters.channelCount = numChannels; 301 inputParameters.sampleFormat = format; 302 inputParameters.suggestedLatency = Pa_GetDeviceInfo( inputParameters.device )->defaultLowInputLatency; 303 inputParameters.hostApiSpecificStreamInfo = NULL; 304 ipp = &inputParameters; 305 } 306 else 307 { 308 ipp = NULL; 309 } 310 311 if( mode == MODE_OUTPUT ) 312 { 313 outputParameters.device = deviceID; 314 outputParameters.channelCount = numChannels; 315 outputParameters.sampleFormat = format; 316 outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency; 317 outputParameters.hostApiSpecificStreamInfo = NULL; 318 opp = &outputParameters; 319 } 320 else 321 { 322 opp = NULL; 323 } 324 325 if(paFormatIsSupported == Pa_IsFormatSupported( ipp, opp, sampleRate )) 326 { 327 printf("------ TestAdvance: %s, device = %d, rate = %g, numChannels = %d, format = %lu -------\n", 328 ( mode == MODE_INPUT ) ? "INPUT" : "OUTPUT", 329 deviceID, sampleRate, numChannels, (unsigned long)format); 330 EXPECT( ((result = Pa_OpenStream( &stream, 331 ipp, 332 opp, 333 sampleRate, 334 FRAMES_PER_BUFFER, 335 paClipOff, /* we won't output out of range samples so don't bother clipping them */ 336 QaCallback, 337 &myData ) ) == 0) ); 338 if( stream ) 339 { 340 PaTime oldStamp, newStamp; 341 unsigned long oldFrames; 342 int minDelay = ( mode == MODE_INPUT ) ? 1000 : 400; 343 /* Was: 344 int minNumBuffers = Pa_GetMinNumBuffers( FRAMES_PER_BUFFER, sampleRate ); 345 int msec = (int) ((minNumBuffers * 3 * 1000.0 * FRAMES_PER_BUFFER) / sampleRate); 346 */ 347 int msec = (int)( 3.0 * 348 (( mode == MODE_INPUT ) ? inputParameters.suggestedLatency : outputParameters.suggestedLatency )); 349 if( msec < minDelay ) msec = minDelay; 350 printf("msec = %d\n", msec); /**/ 351 EXPECT( ((result=Pa_StartStream( stream )) == 0) ); 352 /* Check to make sure PortAudio is advancing timeStamp. */ 353 oldStamp = Pa_GetStreamTime(stream); 354 Pa_Sleep(msec); 355 newStamp = Pa_GetStreamTime(stream); 356 printf("oldStamp = %9.6f, newStamp = %9.6f\n", oldStamp, newStamp ); /**/ 357 EXPECT( (oldStamp < newStamp) ); 358 /* Check to make sure callback is decrementing framesLeft. */ 359 oldFrames = myData.framesLeft; 360 Pa_Sleep(msec); 361 printf("oldFrames = %lu, myData.framesLeft = %lu\n", oldFrames, myData.framesLeft ); /**/ 362 EXPECT( (oldFrames > myData.framesLeft) ); 363 EXPECT( ((result=Pa_CloseStream( stream )) == 0) ); 364 stream = NULL; 365 } 366 } 367 return 0; 368 error: 369 if( stream != NULL ) Pa_CloseStream( stream ); 370 return -1; 371 }