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