1 
2 /*
3  * PortAudio Portable Real-Time Audio Library
4  * Latest Version at: http://www.portaudio.com
5  *
6  * Copyright (c) 1999-2010 Phil Burk and Ross Bencina
7  *
8  * Permission is hereby granted, free of charge, to any person obtaining
9  * a copy of this software and associated documentation files
10  * (the "Software"), to deal in the Software without restriction,
11  * including without limitation the rights to use, copy, modify, merge,
12  * publish, distribute, sublicense, and/or sell copies of the Software,
13  * and to permit persons to whom the Software is furnished to do so,
14  * subject to the following conditions:
15  *
16  * The above copyright notice and this permission notice shall be
17  * included in all copies or substantial portions of the Software.
18  *
19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
23  * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
24  * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
PeCoffLoaderRelocateImageEx(IN UINT16 * Reloc,IN OUT CHAR8 * Fixup,IN OUT CHAR8 ** FixupData,IN UINT64 Adjust)25  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26  */
27 
28 /*
29  * The text above constitutes the entire PortAudio license; however,
30  * the PortAudio community also makes the following non-binding requests:
31  *
32  * Any person wishing to distribute modifications to the Software is
33  * requested to send the modifications to the original developer so that
34  * they can be incorporated into the canonical version. It is also
35  * requested that these non-binding requests be included along with the
36  * license above.
37  */
38 
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <memory.h>
42 #include <math.h>
43 #include <string.h>
44 
45 #include "portaudio.h"
46 
47 #include "qa_tools.h"
48 
49 #include "paqa_tools.h"
50 #include "audio_analyzer.h"
51 #include "test_audio_analyzer.h"
52 
53 /** Accumulate counts for how many tests pass or fail. */
54 int g_testsPassed = 0;
55 int g_testsFailed = 0;
56 
57 #define MAX_NUM_GENERATORS                   (8)
58 #define MAX_NUM_RECORDINGS                   (8)
59 #define MAX_BACKGROUND_NOISE_RMS             (0.0004)
60 #define LOOPBACK_DETECTION_DURATION_SECONDS  (0.8)
61 #define DEFAULT_FRAMES_PER_BUFFER            (0)
62 #define PAQA_WAIT_STREAM_MSEC                (100)
63 #define PAQA_TEST_DURATION                   (1.2)
64 
65 // Use two separate streams instead of one full duplex stream.
66 #define PAQA_FLAG_TWO_STREAMS       (1<<0)
67 // Use bloching read/write for loopback.
68 #define PAQA_FLAG_USE_BLOCKING_IO   (1<<1)
69 
70 const char * s_FlagOnNames[] =
71 {
72 	"Two Streams (Half Duplex)",
73 	"Blocking Read/Write"
74 };
PeHotRelocateImageEx(IN UINT16 * Reloc,IN OUT CHAR8 * Fixup,IN OUT CHAR8 ** FixupData,IN UINT64 Adjust)75 
76 const char * s_FlagOffNames[] =
77 {
78 	"One Stream (Full Duplex)",
79 	"Callback"
80 };
81 
82 
83 /** Parameters that describe a single test run. */
84 typedef struct TestParameters_s
85 {
86 	PaStreamParameters inputParameters;
87 	PaStreamParameters outputParameters;
88 	double             sampleRate;
89 	int                samplesPerFrame;
90 	int                framesPerBuffer;
91 	int                maxFrames;
92 	double             baseFrequency;
93 	double             amplitude;
94     PaStreamFlags      streamFlags;  // paClipOff, etc
95 	int                flags;        // PAQA_FLAG_TWO_STREAMS, PAQA_FLAG_USE_BLOCKING_IO
96 } TestParameters;
97 
98 typedef struct LoopbackContext_s
99 {
100 	// Generate a unique signal on each channel.
101 	PaQaSineGenerator  generators[MAX_NUM_GENERATORS];
102 	// Record each channel individually.
103 	PaQaRecording      recordings[MAX_NUM_RECORDINGS];
104 
105 	// Reported by the stream after it's opened
106 	PaTime             streamInfoInputLatency;
107 	PaTime             streamInfoOutputLatency;
108 
109 	// Measured at runtime.
110 	volatile int       callbackCount; // incremented for each callback
111 	volatile int       inputBufferCount; // incremented if input buffer not NULL
112 	int                inputUnderflowCount;
113 	int                inputOverflowCount;
114 
115 	volatile int       outputBufferCount; // incremented if output buffer not NULL
116 	int                outputOverflowCount;
117 	int                outputUnderflowCount;
118 
119     // Measure whether input or output is lagging behind.
120     volatile int       minInputOutputDelta;
121     volatile int       maxInputOutputDelta;
122 
123 	int                minFramesPerBuffer;
124 	int                maxFramesPerBuffer;
125 	int                primingCount;
126 	TestParameters    *test;
127 	volatile int       done;
128 } LoopbackContext;
129 
130 typedef struct UserOptions_s
131 {
132 	int           sampleRate;
133 	int           framesPerBuffer;
134 	int           inputLatency;
135 	int           outputLatency;
136 	int           saveBadWaves;
137 	int           verbose;
138 	int           waveFileCount;
139 	const char   *waveFilePath;
140 	PaDeviceIndex inputDevice;
141 	PaDeviceIndex outputDevice;
142 } UserOptions;
143 
144 #define BIG_BUFFER_SIZE  (sizeof(float) * 2 * 2 * 1024)
145 static unsigned char g_ReadWriteBuffer[BIG_BUFFER_SIZE];
146 
147 #define MAX_CONVERSION_SAMPLES   (2 * 32 * 1024)
148 #define CONVERSION_BUFFER_SIZE  (sizeof(float) * 2 * MAX_CONVERSION_SAMPLES)
149 static unsigned char g_ConversionBuffer[CONVERSION_BUFFER_SIZE];
150 
151 /*******************************************************************/
152 static int RecordAndPlaySinesCallback( const void *inputBuffer, void *outputBuffer,
153 						unsigned long framesPerBuffer,
154 						const PaStreamCallbackTimeInfo* timeInfo,
155 						PaStreamCallbackFlags statusFlags,
156 						void *userData )
157 {
158 	int i;
159 	LoopbackContext *loopbackContext = (LoopbackContext *) userData;
160 
161 
162 	loopbackContext->callbackCount += 1;
163 	if( statusFlags & paInputUnderflow ) loopbackContext->inputUnderflowCount += 1;
164 	if( statusFlags & paInputOverflow ) loopbackContext->inputOverflowCount += 1;
165 	if( statusFlags & paOutputUnderflow ) loopbackContext->outputUnderflowCount += 1;
166 	if( statusFlags & paOutputOverflow ) loopbackContext->outputOverflowCount += 1;
167 	if( statusFlags & paPrimingOutput ) loopbackContext->primingCount += 1;
168 	if( framesPerBuffer > loopbackContext->maxFramesPerBuffer )
169 	{
170 		loopbackContext->maxFramesPerBuffer = framesPerBuffer;
171 	}
172 	if( framesPerBuffer < loopbackContext->minFramesPerBuffer )
173 	{
174 		loopbackContext->minFramesPerBuffer = framesPerBuffer;
175 	}
176 
177     /* This may get called with NULL inputBuffer during initial setup.
178 	 * We may also use the same callback with output only streams.
179 	 */
180 	if( inputBuffer != NULL)
181 	{
182 		int channelsPerFrame = loopbackContext->test->inputParameters.channelCount;
183 		float *in = (float *)inputBuffer;
184 		PaSampleFormat inFormat = loopbackContext->test->inputParameters.sampleFormat;
185 
186 		loopbackContext->inputBufferCount += 1;
187 
188 		if( inFormat != paFloat32 )
189 		{
190 			int samplesToConvert = framesPerBuffer * channelsPerFrame;
191 			in = (float *) g_ConversionBuffer;
192 			if( samplesToConvert > MAX_CONVERSION_SAMPLES )
193 			{
194 				// Hack to prevent buffer overflow.
195 				// @todo Loop with small buffer instead of failing.
196 				printf("Format conversion buffer too small!\n");
197 				return paComplete;
198 			}
199 			PaQa_ConvertToFloat( inputBuffer, samplesToConvert, inFormat, (float *) g_ConversionBuffer );
200 		}
201 
202 		// Read each channel from the buffer.
203 		for( i=0; i<channelsPerFrame; i++ )
204 		{
205 			loopbackContext->done |= PaQa_WriteRecording( &loopbackContext->recordings[i],
206 										in + i,
207 										framesPerBuffer,
208 										channelsPerFrame );
209 		}
210 	}
211 
212 	if( outputBuffer != NULL )
213 	{
214 		int channelsPerFrame = loopbackContext->test->outputParameters.channelCount;
215 		float *out = (float *)outputBuffer;
216 		PaSampleFormat outFormat = loopbackContext->test->outputParameters.sampleFormat;
217 
218 		loopbackContext->outputBufferCount += 1;
219 
220 		if( outFormat != paFloat32 )
221 		{
222 			// If we need to convert then mix to the g_ConversionBuffer and then convert into the PA outputBuffer.
223 			out = (float *) g_ConversionBuffer;
224 		}
225 
226 		PaQa_EraseBuffer( out, framesPerBuffer, channelsPerFrame );
227 		for( i=0; i<channelsPerFrame; i++ )
228 		{
229 			PaQa_MixSine( &loopbackContext->generators[i],
230 						 out + i,
231 						 framesPerBuffer,
232 						 channelsPerFrame );
233 		}
234 
235 		if( outFormat != paFloat32 )
236 		{
237 			int samplesToConvert = framesPerBuffer * channelsPerFrame;
238 			if( samplesToConvert > MAX_CONVERSION_SAMPLES )
239 			{
240 				printf("Format conversion buffer too small!\n");
241 				return paComplete;
242 			}
243 			PaQa_ConvertFromFloat( out, framesPerBuffer * channelsPerFrame, outFormat, outputBuffer );
244 		}
245 
246 	}
247 
248     // Measure whether the input or output are lagging behind.
249     // Don't measure lag at end.
250     if( !loopbackContext->done )
251     {
252         int inputOutputDelta = loopbackContext->inputBufferCount - loopbackContext->outputBufferCount;
253         if( loopbackContext->maxInputOutputDelta < inputOutputDelta )
254         {
255             loopbackContext->maxInputOutputDelta = inputOutputDelta;
256         }
257         if( loopbackContext->minInputOutputDelta > inputOutputDelta )
258         {
259             loopbackContext->minInputOutputDelta = inputOutputDelta;
260         }
261     }
262 
263 	return loopbackContext->done ? paComplete : paContinue;
264 }
265 
266 static void CopyStreamInfoToLoopbackContext( LoopbackContext *loopbackContext, PaStream *inputStream, PaStream *outputStream )
267 {
268 	const PaStreamInfo *inputStreamInfo = Pa_GetStreamInfo( inputStream );
269 	const PaStreamInfo *outputStreamInfo = Pa_GetStreamInfo( outputStream );
270 
271 	loopbackContext->streamInfoInputLatency = inputStreamInfo ? inputStreamInfo->inputLatency : -1;
272 	loopbackContext->streamInfoOutputLatency = outputStreamInfo ? outputStreamInfo->outputLatency : -1;
273 }
274 
275 /*******************************************************************/
276 /**
277  * Open a full duplex audio stream.
278  * Generate sine waves on the output channels and record the input channels.
279  * Then close the stream.
280  * @return 0 if OK or negative error.
281  */
282 int PaQa_RunLoopbackFullDuplex( LoopbackContext *loopbackContext )
283 {
284 	PaStream *stream = NULL;
285 	PaError err = 0;
286 	TestParameters *test = loopbackContext->test;
287 	loopbackContext->done = 0;
288 	// Use one full duplex stream.
289 	err = Pa_OpenStream(
290 					&stream,
291 					&test->inputParameters,
292 					&test->outputParameters,
293 					test->sampleRate,
294 					test->framesPerBuffer,
295 					paClipOff, /* we won't output out of range samples so don't bother clipping them */
296 					RecordAndPlaySinesCallback,
297 					loopbackContext );
298 	if( err != paNoError ) goto error;
299 
300 	CopyStreamInfoToLoopbackContext( loopbackContext, stream, stream );
301 
302 	err = Pa_StartStream( stream );
303 	if( err != paNoError ) goto error;
304 
305 	// Wait for stream to finish.
306 	while( loopbackContext->done == 0 )
307 	{
308 		Pa_Sleep(PAQA_WAIT_STREAM_MSEC);
309 	}
310 
311 	err = Pa_StopStream( stream );
312 	if( err != paNoError ) goto error;
313 
314 	err = Pa_CloseStream( stream );
315 	if( err != paNoError ) goto error;
316 
317 	return 0;
318 
319 error:
320 	return err;
321 }
322 
323 /*******************************************************************/
324 /**
325  * Open two audio streams, one for input and one for output.
326  * Generate sine waves on the output channels and record the input channels.
327  * Then close the stream.
328  * @return 0 if OK or paTimedOut.
329  */
330 
331 int PaQa_WaitForStream( LoopbackContext *loopbackContext )
332 {
333 	int timeoutMSec = 1000 * PAQA_TEST_DURATION * 2;
334 
335 	// Wait for stream to finish or timeout.
336 	while( (loopbackContext->done == 0) && (timeoutMSec > 0) )
337 	{
338 		Pa_Sleep(PAQA_WAIT_STREAM_MSEC);
339 		timeoutMSec -= PAQA_WAIT_STREAM_MSEC;
340 	}
341 
342 	if( loopbackContext->done == 0 )
343 	{
344 		printf("ERROR - stream completion timed out!");
345 		return paTimedOut;
346 	}
347 	return 0;
348 }
349 
350 /*******************************************************************/
351 /**
352  * Open two audio streams, one for input and one for output.
353  * Generate sine waves on the output channels and record the input channels.
354  * Then close the stream.
355  * @return 0 if OK or negative error.
356  */
357 int PaQa_RunLoopbackHalfDuplex( LoopbackContext *loopbackContext )
358 {
359 	PaStream *inStream = NULL;
360 	PaStream *outStream = NULL;
361 	PaError err = 0;
362 	int timedOut = 0;
363 	TestParameters *test = loopbackContext->test;
364 	loopbackContext->done = 0;
365 
366 	// Use two half duplex streams.
367 	err = Pa_OpenStream(
368 						&inStream,
369 						&test->inputParameters,
370 						NULL,
371 						test->sampleRate,
372 						test->framesPerBuffer,
373 						test->streamFlags,
374 						RecordAndPlaySinesCallback,
375 						loopbackContext );
376 	if( err != paNoError ) goto error;
377 	err = Pa_OpenStream(
378 						&outStream,
379 						NULL,
380 						&test->outputParameters,
381 						test->sampleRate,
382 						test->framesPerBuffer,
383 						test->streamFlags,
384 						RecordAndPlaySinesCallback,
385 						loopbackContext );
386 	if( err != paNoError ) goto error;
387 
388 	CopyStreamInfoToLoopbackContext( loopbackContext, inStream, outStream );
389 
390 	err = Pa_StartStream( inStream );
391 	if( err != paNoError ) goto error;
392 
393 	// Start output later so we catch the beginning of the waveform.
394 	err = Pa_StartStream( outStream );
395 	if( err != paNoError ) goto error;
396 
397 	timedOut = PaQa_WaitForStream( loopbackContext );
398 
399 	err = Pa_StopStream( inStream );
400 	if( err != paNoError ) goto error;
401 
402 	err = Pa_StopStream( outStream );
403 	if( err != paNoError ) goto error;
404 
405 	err = Pa_CloseStream( inStream );
406 	if( err != paNoError ) goto error;
407 
408 	err = Pa_CloseStream( outStream );
409 	if( err != paNoError ) goto error;
410 
411 	return timedOut;
412 
413 error:
414 	return err;
415 }
416 
417 
418 /*******************************************************************/
419 /**
420  * Open one audio streams, just for input.
421  * Record background level.
422  * Then close the stream.
423  * @return 0 if OK or negative error.
424  */
425 int PaQa_RunInputOnly( LoopbackContext *loopbackContext )
426 {
427 	PaStream *inStream = NULL;
428 	PaError err = 0;
429 	int timedOut = 0;
430 	TestParameters *test = loopbackContext->test;
431 	loopbackContext->done = 0;
432 
433 	// Just open an input stream.
434 	err = Pa_OpenStream(
435 						&inStream,
436 						&test->inputParameters,
437 						NULL,
438 						test->sampleRate,
439 						test->framesPerBuffer,
440 						paClipOff, /* We won't output out of range samples so don't bother clipping them. */
441 						RecordAndPlaySinesCallback,
442 						loopbackContext );
443 	if( err != paNoError ) goto error;
444 
445 	err = Pa_StartStream( inStream );
446 	if( err != paNoError ) goto error;
447 
448 	timedOut = PaQa_WaitForStream( loopbackContext );
449 
450 	err = Pa_StopStream( inStream );
451 	if( err != paNoError ) goto error;
452 
453 	err = Pa_CloseStream( inStream );
454 	if( err != paNoError ) goto error;
455 
456 	return timedOut;
457 
458 error:
459 	return err;
460 }
461 
462 /*******************************************************************/
463 static int RecordAndPlayBlockingIO( PaStream *inStream,
464 									  PaStream *outStream,
465 									  LoopbackContext *loopbackContext
466 									  )
467 {
468 	int i;
469 	float *in = (float *)g_ReadWriteBuffer;
470 	float *out = (float *)g_ReadWriteBuffer;
471 	PaError err;
472 	int done = 0;
473 	long available;
474 	const long maxPerBuffer = 64;
475 	TestParameters *test = loopbackContext->test;
476 	long framesPerBuffer = test->framesPerBuffer;
477 	if( framesPerBuffer <= 0 )
478 	{
479 		framesPerBuffer = maxPerBuffer; // bigger values might run past end of recording
480 	}
481 
482 	// Read in audio.
483 	err = Pa_ReadStream( inStream, in, framesPerBuffer );
484 	// Ignore an overflow on the first read.
485 	//if( !((loopbackContext->callbackCount == 0) && (err == paInputOverflowed)) )
486 	if( err != paInputOverflowed )
487 	{
488 		QA_ASSERT_EQUALS( "Pa_ReadStream failed", paNoError, err );
489 	}
490 	else
491 	{
492 		loopbackContext->inputOverflowCount += 1;
493 	}
494 
495 
496 	// Save in a recording.
497 	for( i=0; i<loopbackContext->test->inputParameters.channelCount; i++ )
498 	{
499 		done |= PaQa_WriteRecording( &loopbackContext->recordings[i],
500 		         in + i,
501 		         framesPerBuffer,
502 		         loopbackContext->test->inputParameters.channelCount );
503 	}
504 
505 	// Synthesize audio.
506 	available = Pa_GetStreamWriteAvailable( outStream );
507 	if( available > (2*framesPerBuffer) ) available = (2*framesPerBuffer);
508 	PaQa_EraseBuffer( out, available, loopbackContext->test->outputParameters.channelCount );
509 	for( i=0; i<loopbackContext->test->outputParameters.channelCount; i++ )
510 	{
511 		PaQa_MixSine( &loopbackContext->generators[i],
512 		          out + i,
513 		          available,
514 		          loopbackContext->test->outputParameters.channelCount );
515 	}
516 
517 	// Write out audio.
518 	err = Pa_WriteStream( outStream, out, available );
519 	// Ignore an underflow on the first write.
520 	//if( !((loopbackContext->callbackCount == 0) && (err == paOutputUnderflowed)) )
521 	if( err != paOutputUnderflowed )
522 	{
523 		QA_ASSERT_EQUALS( "Pa_WriteStream failed", paNoError, err );
524 	}
525 	else
526 	{
527 		loopbackContext->outputUnderflowCount += 1;
528 	}
529 
530 
531 	loopbackContext->callbackCount += 1;
532 
533 	return done;
534 error:
535 	return err;
536 }
537 
538 
539 /*******************************************************************/
540 /**
541  * Open two audio streams with non-blocking IO.
542  * Generate sine waves on the output channels and record the input channels.
543  * Then close the stream.
544  * @return 0 if OK or negative error.
545  */
546 int PaQa_RunLoopbackHalfDuplexBlockingIO( LoopbackContext *loopbackContext )
547 {
548 	PaStream *inStream = NULL;
549 	PaStream *outStream = NULL;
550 	PaError err = 0;
551 	TestParameters *test = loopbackContext->test;
552 
553 	// Use two half duplex streams.
554 	err = Pa_OpenStream(
555 						&inStream,
556 						&test->inputParameters,
557 						NULL,
558 						test->sampleRate,
559 						test->framesPerBuffer,
560 						paClipOff, /* we won't output out of range samples so don't bother clipping them */
561 						NULL, // causes non-blocking IO
562 						NULL );
563 	if( err != paNoError ) goto error1;
564 	err = Pa_OpenStream(
565 						&outStream,
566 						NULL,
567 						&test->outputParameters,
568 						test->sampleRate,
569 						test->framesPerBuffer,
570 						paClipOff, /* we won't output out of range samples so don't bother clipping them */
571 						NULL, // causes non-blocking IO
572 						NULL );
573 	if( err != paNoError ) goto error2;
574 
575 	CopyStreamInfoToLoopbackContext( loopbackContext, inStream, outStream );
576 
577 	err = Pa_StartStream( outStream );
578 	if( err != paNoError ) goto error3;
579 
580 	err = Pa_StartStream( inStream );
581 	if( err != paNoError ) goto error3;
582 
583 	while( err == 0 )
584 	{
585 		err = RecordAndPlayBlockingIO( inStream, outStream, loopbackContext );
586 		if( err < 0 ) goto error3;
587 	}
588 
589 	err = Pa_StopStream( inStream );
590 	if( err != paNoError ) goto error3;
591 
592 	err = Pa_StopStream( outStream );
593 	if( err != paNoError ) goto error3;
594 
595 	err = Pa_CloseStream( outStream );
596 	if( err != paNoError ) goto error2;
597 
598 	err = Pa_CloseStream( inStream );
599 	if( err != paNoError ) goto error1;
600 
601 
602 	return 0;
603 
604 error3:
605 	Pa_CloseStream( outStream );
606 error2:
607 	Pa_CloseStream( inStream );
608 error1:
609 	return err;
610 }
611 
612 
613 /*******************************************************************/
614 /**
615  * Open one audio stream with non-blocking IO.
616  * Generate sine waves on the output channels and record the input channels.
617  * Then close the stream.
618  * @return 0 if OK or negative error.
619  */
620 int PaQa_RunLoopbackFullDuplexBlockingIO( LoopbackContext *loopbackContext )
621 {
622 	PaStream *stream = NULL;
623 	PaError err = 0;
624 	TestParameters *test = loopbackContext->test;
625 
626 	// Use one full duplex stream.
627 	err = Pa_OpenStream(
628 						&stream,
629 						&test->inputParameters,
630 						&test->outputParameters,
631 						test->sampleRate,
632 						test->framesPerBuffer,
633 						paClipOff, /* we won't output out of range samples so don't bother clipping them */
634 						NULL, // causes non-blocking IO
635 						NULL );
636 	if( err != paNoError ) goto error1;
637 
638 	CopyStreamInfoToLoopbackContext( loopbackContext, stream, stream );
639 
640 	err = Pa_StartStream( stream );
641 	if( err != paNoError ) goto error2;
642 
643 	while( err == 0 )
644 	{
645 		err = RecordAndPlayBlockingIO( stream, stream, loopbackContext );
646 		if( err < 0 ) goto error2;
647 	}
648 
649 	err = Pa_StopStream( stream );
650 	if( err != paNoError ) goto error2;
651 
652 
653 	err = Pa_CloseStream( stream );
654 	if( err != paNoError ) goto error1;
655 
656 
657 	return 0;
658 
659 error2:
660 	Pa_CloseStream( stream );
661 error1:
662 	return err;
663 }
664 
665 
666 /*******************************************************************/
667 /**
668  * Run some kind of loopback test.
669  * @return 0 if OK or negative error.
670  */
671 int PaQa_RunLoopback( LoopbackContext *loopbackContext )
672 {
673 	PaError err = 0;
674 	TestParameters *test = loopbackContext->test;
675 
676 
677 	if( test->flags & PAQA_FLAG_TWO_STREAMS )
678 	{
679 		if( test->flags & PAQA_FLAG_USE_BLOCKING_IO )
680 		{
681 			err = PaQa_RunLoopbackHalfDuplexBlockingIO( loopbackContext );
682 		}
683 		else
684 		{
685 			err = PaQa_RunLoopbackHalfDuplex( loopbackContext );
686 		}
687 	}
688 	else
689 	{
690 		if( test->flags & PAQA_FLAG_USE_BLOCKING_IO )
691 		{
692 			err = PaQa_RunLoopbackFullDuplexBlockingIO( loopbackContext );
693 		}
694 		else
695 		{
696 			err = PaQa_RunLoopbackFullDuplex( loopbackContext );
697 		}
698 	}
699 
700 	if( err != paNoError )
701 	{
702 		printf("PortAudio error = %s\n", Pa_GetErrorText( err ) );
703 	}
704 	return err;
705 }
706 
707 /*******************************************************************/
708 static int PaQa_SaveTestResultToWaveFile( UserOptions *userOptions, PaQaRecording *recording )
709 {
710 	if( userOptions->saveBadWaves )
711 	{
712 		char filename[256];
713 #ifdef WIN32
714         _snprintf( filename, sizeof(filename), "%s\\paloopback_%d.wav", userOptions->waveFilePath, userOptions->waveFileCount++ );
715 #else
716 		snprintf( filename, sizeof(filename), "%s/paloopback_%d.wav", userOptions->waveFilePath, userOptions->waveFileCount++ );
717 #endif
718 		printf( "\"%s\", ", filename );
719 		return PaQa_SaveRecordingToWaveFile( recording, filename );
720 	}
721 	return 0;
722 }
723 
724 /*******************************************************************/
725 static int PaQa_SetupLoopbackContext( LoopbackContext *loopbackContextPtr, TestParameters *testParams )
726 {
727 	int i;
728 	// Setup loopback context.
729 	memset( loopbackContextPtr, 0, sizeof(LoopbackContext) );
730 	loopbackContextPtr->test = testParams;
731 	for( i=0; i<testParams->samplesPerFrame; i++ )
732 	{
733 		int err = PaQa_InitializeRecording( &loopbackContextPtr->recordings[i], testParams->maxFrames, testParams->sampleRate );
734 		QA_ASSERT_EQUALS( "PaQa_InitializeRecording failed", paNoError, err );
735 	}
736 	for( i=0; i<testParams->samplesPerFrame; i++ )
737 	{
738 		PaQa_SetupSineGenerator( &loopbackContextPtr->generators[i], PaQa_GetNthFrequency( testParams->baseFrequency, i ),
739 								testParams->amplitude, testParams->sampleRate );
740 	}
741 	loopbackContextPtr->minFramesPerBuffer = 0x0FFFFFFF;
742 	return 0;
743 error:
744 	return -1;
745 }
746 
747 /*******************************************************************/
748 static void PaQa_TeardownLoopbackContext( LoopbackContext *loopbackContextPtr )
749 {
750 	int i;
751 	if( loopbackContextPtr->test != NULL )
752 	{
753 		for( i=0; i<loopbackContextPtr->test->samplesPerFrame; i++ )
754 		{
755 			PaQa_TerminateRecording( &loopbackContextPtr->recordings[i] );
756 		}
757 	}
758 }
759 
760 /*******************************************************************/
761 static void PaQa_PrintShortErrorReport( PaQaAnalysisResult *analysisResultPtr, int channel )
762 {
763 	printf("channel %d ", channel);
764 	if( analysisResultPtr->popPosition > 0 )
765 	{
766 		printf("POP %0.3f at %d, ", (double)analysisResultPtr->popAmplitude, (int)analysisResultPtr->popPosition );
767 	}
768 	else
769 	{
770 		if( analysisResultPtr->addedFramesPosition > 0 )
771 		{
772 			printf("ADD %d at %d ", (int)analysisResultPtr->numAddedFrames, (int)analysisResultPtr->addedFramesPosition );
773 		}
774 
775 		if( analysisResultPtr->droppedFramesPosition > 0 )
776 		{
777 			printf("DROP %d at %d ", (int)analysisResultPtr->numDroppedFrames, (int)analysisResultPtr->droppedFramesPosition );
778 		}
779 	}
780 }
781 
782 /*******************************************************************/
783 static void PaQa_PrintFullErrorReport( PaQaAnalysisResult *analysisResultPtr, int channel )
784 {
785 	printf("\n=== Loopback Analysis ===================\n");
786 	printf("             channel: %d\n", channel );
787 	printf("             latency: %10.3f\n", analysisResultPtr->latency );
788 	printf("      amplitudeRatio: %10.3f\n", (double)analysisResultPtr->amplitudeRatio );
789 	printf("         popPosition: %10.3f\n", (double)analysisResultPtr->popPosition );
790 	printf("        popAmplitude: %10.3f\n", (double)analysisResultPtr->popAmplitude );
791 	printf("    num added frames: %10.3f\n", analysisResultPtr->numAddedFrames );
792 	printf("     added frames at: %10.3f\n", analysisResultPtr->addedFramesPosition );
793 	printf("  num dropped frames: %10.3f\n", analysisResultPtr->numDroppedFrames );
794 	printf("   dropped frames at: %10.3f\n", analysisResultPtr->droppedFramesPosition );
795 }
796 
797 /*******************************************************************/
798 /**
799  * Test loopback connection using the given parameters.
800  * @return number of channels with glitches, or negative error.
801  */
802 static int PaQa_SingleLoopBackTest( UserOptions *userOptions, TestParameters *testParams )
803 {
804 	int i;
805 	LoopbackContext loopbackContext;
806 	PaError err = paNoError;
807 	PaQaTestTone testTone;
808 	PaQaAnalysisResult analysisResult;
809 	int numBadChannels = 0;
810 
811 	printf("| %5d | %6d | ", ((int)(testParams->sampleRate+0.5)), testParams->framesPerBuffer );
812 	fflush(stdout);
813 
814 	testTone.samplesPerFrame = testParams->samplesPerFrame;
815 	testTone.sampleRate = testParams->sampleRate;
816 	testTone.amplitude = testParams->amplitude;
817 	testTone.startDelay = 0;
818 
819 	err = PaQa_SetupLoopbackContext( &loopbackContext, testParams );
820 	if( err ) return err;
821 
822 	err = PaQa_RunLoopback( &loopbackContext );
823 	QA_ASSERT_TRUE("loopback did not run", (loopbackContext.callbackCount > 1) );
824 
825 	printf( "%7.2f %7.2f %7.2f | ",
826 		   loopbackContext.streamInfoInputLatency * 1000.0,
827 		   loopbackContext.streamInfoOutputLatency * 1000.0,
828 		   (loopbackContext.streamInfoInputLatency + loopbackContext.streamInfoOutputLatency) * 1000.0
829 		   );
830 
831 	printf( "%4d/%4d/%4d, %4d/%4d/%4d | ",
832 		   loopbackContext.inputOverflowCount,
833 		   loopbackContext.inputUnderflowCount,
834 		   loopbackContext.inputBufferCount,
835 		   loopbackContext.outputOverflowCount,
836 		   loopbackContext.outputUnderflowCount,
837 		   loopbackContext.outputBufferCount
838 		   );
839 
840 	// Analyse recording to detect glitches.
841 	for( i=0; i<testParams->samplesPerFrame; i++ )
842 	{
843 		double freq = PaQa_GetNthFrequency( testParams->baseFrequency, i );
844 		testTone.frequency = freq;
845 
846 		PaQa_AnalyseRecording(  &loopbackContext.recordings[i], &testTone, &analysisResult );
847 
848 		if( i==0 )
849 		{
850             double latencyMSec;
851 
852 			printf( "%4d-%4d | ",
853 				   loopbackContext.minFramesPerBuffer,
854 				   loopbackContext.maxFramesPerBuffer
855 				   );
856 
857 			latencyMSec = 1000.0 * analysisResult.latency / testParams->sampleRate;
858 			printf("%7.2f | ", latencyMSec );
859 
860 		}
861 
862 		if( analysisResult.valid )
863 		{
864 			int badChannel = ( (analysisResult.popPosition > 0)
865 					   || (analysisResult.addedFramesPosition > 0)
866 					   || (analysisResult.droppedFramesPosition > 0) );
867 
868 			if( badChannel )
869 			{
870 				if( userOptions->verbose )
871 				{
872 					PaQa_PrintFullErrorReport( &analysisResult, i );
873 				}
874 				else
875 				{
876 					PaQa_PrintShortErrorReport( &analysisResult, i );
877 				}
878 				PaQa_SaveTestResultToWaveFile( userOptions, &loopbackContext.recordings[i] );
879 			}
880 			numBadChannels += badChannel;
881 		}
882 		else
883 		{
884 			printf( "[%d] No or low signal, ampRatio = %f", i, analysisResult.amplitudeRatio );
885 			numBadChannels += 1;
886 		}
887 
888 	}
889 	if( numBadChannels == 0 )
890 	{
891 		printf( "OK" );
892 	}
893 
894     // Print the # errors so far to make it easier to see where the error occured.
895 	printf( " - #errs = %d\n", g_testsFailed );
896 
897 	PaQa_TeardownLoopbackContext( &loopbackContext );
898 	if( numBadChannels > 0 )
899 	{
900 		g_testsFailed += 1;
901 	}
902 	return numBadChannels;
903 
904 error:
905 	PaQa_TeardownLoopbackContext( &loopbackContext );
906 	printf( "\n" );
907 	g_testsFailed += 1;
908 	return err;
909 }
910 
911 /*******************************************************************/
912 static void PaQa_SetDefaultTestParameters( TestParameters *testParamsPtr, PaDeviceIndex inputDevice, PaDeviceIndex outputDevice )
913 {
914 	memset( testParamsPtr, 0, sizeof(TestParameters) );
915 
916 	testParamsPtr->samplesPerFrame = 2;
917 	testParamsPtr->amplitude = 0.5;
918 	testParamsPtr->sampleRate = 44100;
919 	testParamsPtr->maxFrames = (int) (PAQA_TEST_DURATION * testParamsPtr->sampleRate);
920 	testParamsPtr->framesPerBuffer = DEFAULT_FRAMES_PER_BUFFER;
921 	testParamsPtr->baseFrequency = 200.0;
922 	testParamsPtr->flags = PAQA_FLAG_TWO_STREAMS;
923     testParamsPtr->streamFlags = paClipOff; /* we won't output out of range samples so don't bother clipping them */
924 
925 	testParamsPtr->inputParameters.device = inputDevice;
926 	testParamsPtr->inputParameters.sampleFormat = paFloat32;
927 	testParamsPtr->inputParameters.channelCount = testParamsPtr->samplesPerFrame;
928 	testParamsPtr->inputParameters.suggestedLatency = Pa_GetDeviceInfo( inputDevice )->defaultLowInputLatency;
929 	//testParamsPtr->inputParameters.suggestedLatency = Pa_GetDeviceInfo( inputDevice )->defaultHighInputLatency;
930 
931 	testParamsPtr->outputParameters.device = outputDevice;
932 	testParamsPtr->outputParameters.sampleFormat = paFloat32;
933 	testParamsPtr->outputParameters.channelCount = testParamsPtr->samplesPerFrame;
934 	testParamsPtr->outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputDevice )->defaultLowOutputLatency;
935 	//testParamsPtr->outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputDevice )->defaultHighOutputLatency;
936 }
937 
938 /*******************************************************************/
939 static void PaQa_OverrideTestParameters( TestParameters *testParamsPtr,  UserOptions *userOptions )
940 {
941 	// Check to see if a specific value was requested.
942 	if( userOptions->sampleRate >= 0 )
943 	{
944 		testParamsPtr->sampleRate = userOptions->sampleRate;
945 		testParamsPtr->maxFrames = (int) (PAQA_TEST_DURATION * testParamsPtr->sampleRate);
946 	}
947 	if( userOptions->framesPerBuffer >= 0 )
948 	{
949 		testParamsPtr->framesPerBuffer = userOptions->framesPerBuffer;
950 	}
951 	if( userOptions->inputLatency >= 0 )
952 	{
953 		testParamsPtr->inputParameters.suggestedLatency = userOptions->inputLatency * 0.001;
954 	}
955 	if( userOptions->outputLatency >= 0 )
956 	{
957 		testParamsPtr->outputParameters.suggestedLatency = userOptions->outputLatency * 0.001;
958 	}
959 	printf( "   Running with suggested latency (msec): input = %5.2f, out = %5.2f\n",
960 		(testParamsPtr->inputParameters.suggestedLatency * 1000.0),
961 		(testParamsPtr->outputParameters.suggestedLatency * 1000.0) );
962 }
963 
964 /*******************************************************************/
965 /**
966  * Run a series of tests on this loopback connection.
967  * @return number of bad channel results
968  */
969 static int PaQa_AnalyzeLoopbackConnection( UserOptions *userOptions, PaDeviceIndex inputDevice, PaDeviceIndex outputDevice )
970 {
971 	int iFlags;
972 	int iRate;
973 	int iSize;
974 	int iFormat;
975 	int savedValue;
976 	TestParameters testParams;
977 	const PaDeviceInfo *inputDeviceInfo = Pa_GetDeviceInfo( inputDevice );
978 	const PaDeviceInfo *outputDeviceInfo = Pa_GetDeviceInfo( outputDevice );
979     int totalBadChannels = 0;
980 
981 	// test half duplex first because it is more likely to work.
982 	int flagSettings[] = { PAQA_FLAG_TWO_STREAMS, 0 };
983 	int numFlagSettings = (sizeof(flagSettings)/sizeof(int));
984 
985 	double sampleRates[] = { 8000.0, 11025.0, 16000.0, 22050.0, 32000.0, 44100.0, 48000.0, 96000.0 };
986 	int numRates = (sizeof(sampleRates)/sizeof(double));
987 
988 	// framesPerBuffer==0 means PA decides on the buffer size.
989 	int framesPerBuffers[] = { 0, 16, 32, 40, 64, 100, 128, 256, 512, 1024 };
990 	int numBufferSizes = (sizeof(framesPerBuffers)/sizeof(int));
991 
992 	PaSampleFormat sampleFormats[] = { paFloat32, paUInt8, paInt8, paInt16, paInt32 };
993 	const char *sampleFormatNames[] = { "paFloat32", "paUInt8", "paInt8", "paInt16", "paInt32" };
994 	int numSampleFormats = (sizeof(sampleFormats)/sizeof(PaSampleFormat));
995 
996     printf( "=============== Analysing Loopback %d to %d =====================\n", outputDevice, inputDevice  );
997 	printf( "   Devices: %s => %s\n", outputDeviceInfo->name, inputDeviceInfo->name);
998 
999 	PaQa_SetDefaultTestParameters( &testParams, inputDevice, outputDevice );
1000 
1001 	PaQa_OverrideTestParameters( &testParams, userOptions );
1002 
1003 	// Loop though combinations of audio parameters.
1004 	for( iFlags=0; iFlags<numFlagSettings; iFlags++ )
1005 	{
1006 		int numRuns = 0;
1007 
1008 		testParams.flags = flagSettings[iFlags];
1009 		printf( "\n************ Mode = %s ************\n",
1010 			   (( testParams.flags & 1 ) ? s_FlagOnNames[0] : s_FlagOffNames[0]) );
1011 
1012 		printf("|-   requested  -|-  stream info latency  -|- measured ------------------------------\n");
1013 		printf("|-sRate-|-fr/buf-|- in    - out   - total -|- over/under/calls for in, out -|- frm/buf -|-latency-|- channel results -\n");
1014 
1015 		// Loop though various sample rates.
1016 		if( userOptions->sampleRate < 0 )
1017 		{
1018 			savedValue = testParams.sampleRate;
1019 			for( iRate=0; iRate<numRates; iRate++ )
1020 			{
1021                 int numBadChannels;
1022 
1023 				// SAMPLE RATE
1024 				testParams.sampleRate = sampleRates[iRate];
1025 				testParams.maxFrames = (int) (PAQA_TEST_DURATION * testParams.sampleRate);
1026 
1027 				numBadChannels = PaQa_SingleLoopBackTest( userOptions, &testParams );
1028 				totalBadChannels += numBadChannels;
1029 			}
1030 			testParams.sampleRate = savedValue;
1031 			testParams.maxFrames = (int) (PAQA_TEST_DURATION * testParams.sampleRate);
1032 			printf( "\n" );
1033 			numRuns += 1;
1034 		}
1035 
1036 		// Loop through various buffer sizes.
1037 		if( userOptions->framesPerBuffer < 0 )
1038 		{
1039 			savedValue = testParams.framesPerBuffer;
1040 			for( iSize=0; iSize<numBufferSizes; iSize++ )
1041 			{
1042                 int numBadChannels;
1043 
1044 				// BUFFER SIZE
1045 				testParams.framesPerBuffer = framesPerBuffers[iSize];
1046 
1047 				numBadChannels = PaQa_SingleLoopBackTest( userOptions, &testParams );
1048 				totalBadChannels += numBadChannels;
1049 			}
1050 			testParams.framesPerBuffer = savedValue;
1051 			printf( "\n" );
1052 			numRuns += 1;
1053 		}
1054 		// Run one with single parameters in case we did not do a series.
1055 		if( numRuns == 0 )
1056 		{
1057 			int numBadChannels = PaQa_SingleLoopBackTest( userOptions, &testParams );
1058 			totalBadChannels += numBadChannels;
1059 		}
1060 	}
1061 
1062 	printf("\nTest Sample Formats using Half Duplex IO -----\n" );
1063 
1064 	PaQa_SetDefaultTestParameters( &testParams, inputDevice, outputDevice );
1065 	testParams.flags = PAQA_FLAG_TWO_STREAMS;
1066     for( iFlags= 0; iFlags<4; iFlags++ )
1067     {
1068         // Cycle through combinations of flags.
1069         testParams.streamFlags = 0;
1070         if( iFlags & 1 ) testParams.streamFlags |= paClipOff;
1071         if( iFlags & 2 ) testParams.streamFlags |= paDitherOff;
1072 
1073         for( iFormat=0; iFormat<numSampleFormats; iFormat++ )
1074         {
1075             int numBadChannels;
1076             PaSampleFormat format = sampleFormats[ iFormat ];
1077             testParams.inputParameters.sampleFormat = format;
1078             testParams.outputParameters.sampleFormat = format;
1079             printf("Sample format = %d = %s, PaStreamFlags = 0x%02X\n", (int) format, sampleFormatNames[iFormat], (unsigned int) testParams.streamFlags );
1080             numBadChannels = PaQa_SingleLoopBackTest( userOptions, &testParams );
1081             totalBadChannels += numBadChannels;
1082         }
1083     }
1084 	printf( "\n" );
1085 	printf( "****************************************\n");
1086 
1087 	return totalBadChannels;
1088 }
1089 
1090 /*******************************************************************/
1091 int PaQa_CheckForClippedLoopback( LoopbackContext *loopbackContextPtr )
1092 {
1093 	int clipped = 0;
1094 	TestParameters *testParamsPtr = loopbackContextPtr->test;
1095 
1096 	// Start in the middle assuming past latency.
1097 	int startFrame = testParamsPtr->maxFrames/2;
1098 	int numFrames = testParamsPtr->maxFrames/2;
1099 
1100 	// Check to see if the signal is clipped.
1101 	double amplitudeLeft = PaQa_MeasureSineAmplitudeBySlope( &loopbackContextPtr->recordings[0],
1102 															testParamsPtr->baseFrequency, testParamsPtr->sampleRate,
1103 															startFrame, numFrames );
1104 	double gainLeft = amplitudeLeft / testParamsPtr->amplitude;
1105 	double amplitudeRight = PaQa_MeasureSineAmplitudeBySlope( &loopbackContextPtr->recordings[1],
1106 															 testParamsPtr->baseFrequency, testParamsPtr->sampleRate,
1107 															 startFrame, numFrames );
1108 	double gainRight = amplitudeLeft / testParamsPtr->amplitude;
1109 	printf("   Loop gain: left = %f, right = %f\n", gainLeft, gainRight );
1110 
1111 	if( (amplitudeLeft > 1.0 ) || (amplitudeRight > 1.0) )
1112 	{
1113 		printf("ERROR - loop gain is too high. Should be around than 1.0. Please lower output level and/or input gain.\n" );
1114 		clipped = 1;
1115 	}
1116 	return clipped;
1117 }
1118 
1119 /*******************************************************************/
1120 int PaQa_MeasureBackgroundNoise( LoopbackContext *loopbackContextPtr, double *rmsPtr )
1121 {
1122 	int result = 0;
1123 	*rmsPtr = 0.0;
1124 	// Rewind so we can record some input.
1125 	loopbackContextPtr->recordings[0].numFrames = 0;
1126 	loopbackContextPtr->recordings[1].numFrames = 0;
1127 	result = PaQa_RunInputOnly( loopbackContextPtr );
1128 	if( result == 0 )
1129 	{
1130 		double leftRMS = PaQa_MeasureRootMeanSquare( loopbackContextPtr->recordings[0].buffer,
1131 													loopbackContextPtr->recordings[0].numFrames );
1132 		double rightRMS = PaQa_MeasureRootMeanSquare( loopbackContextPtr->recordings[1].buffer,
1133 													 loopbackContextPtr->recordings[1].numFrames );
1134 		*rmsPtr = (leftRMS + rightRMS) / 2.0;
1135 	}
1136 	return result;
1137 }
1138 
1139 /*******************************************************************/
1140 /**
1141  * Output a sine wave then try to detect it on input.
1142  *
1143  * @return 1 if loopback connected, 0 if not, or negative error.
1144  */
1145 int PaQa_CheckForLoopBack( UserOptions *userOptions, PaDeviceIndex inputDevice, PaDeviceIndex outputDevice )
1146 {
1147 	TestParameters testParams;
1148 	LoopbackContext loopbackContext;
1149     const PaDeviceInfo *inputDeviceInfo;
1150     const PaDeviceInfo *outputDeviceInfo;
1151 	PaError err = paNoError;
1152 	double minAmplitude;
1153 	int loopbackIsConnected;
1154     int startFrame, numFrames;
1155     double magLeft, magRight;
1156 
1157 	inputDeviceInfo = Pa_GetDeviceInfo( inputDevice );
1158 	if( inputDeviceInfo == NULL )
1159 	{
1160 		printf("ERROR - Pa_GetDeviceInfo for input returned NULL.\n");
1161 		return paInvalidDevice;
1162 	}
1163 	if( inputDeviceInfo->maxInputChannels < 2 )
1164 	{
1165 		return 0;
1166 	}
1167 
1168 	outputDeviceInfo = Pa_GetDeviceInfo( outputDevice );
1169 	if( outputDeviceInfo == NULL )
1170 	{
1171 		printf("ERROR - Pa_GetDeviceInfo for output returned NULL.\n");
1172 		return paInvalidDevice;
1173 	}
1174 	if( outputDeviceInfo->maxOutputChannels < 2 )
1175 	{
1176 		return 0;
1177 	}
1178 
1179 	printf( "Look for loopback cable between \"%s\" => \"%s\"\n", outputDeviceInfo->name, inputDeviceInfo->name);
1180 
1181 	printf( "   Default suggested input latency (msec): low = %5.2f, high = %5.2f\n",
1182 		(inputDeviceInfo->defaultLowInputLatency * 1000.0),
1183 		(inputDeviceInfo->defaultHighInputLatency * 1000.0) );
1184 	printf( "   Default suggested output latency (msec): low = %5.2f, high = %5.2f\n",
1185 		(outputDeviceInfo->defaultLowOutputLatency * 1000.0),
1186 		(outputDeviceInfo->defaultHighOutputLatency * 1000.0) );
1187 
1188 	PaQa_SetDefaultTestParameters( &testParams, inputDevice, outputDevice );
1189 
1190 	PaQa_OverrideTestParameters( &testParams, userOptions );
1191 
1192 	testParams.maxFrames = (int) (LOOPBACK_DETECTION_DURATION_SECONDS * testParams.sampleRate);
1193 	minAmplitude = testParams.amplitude / 4.0;
1194 
1195 	// Check to see if the selected formats are supported.
1196 	if( Pa_IsFormatSupported( &testParams.inputParameters, NULL, testParams.sampleRate ) != paFormatIsSupported )
1197 	{
1198 		printf( "Input not supported for this format!\n" );
1199 		return 0;
1200 	}
1201 	if( Pa_IsFormatSupported( NULL, &testParams.outputParameters, testParams.sampleRate ) != paFormatIsSupported )
1202 	{
1203 		printf( "Output not supported for this format!\n" );
1204 		return 0;
1205 	}
1206 
1207 	PaQa_SetupLoopbackContext( &loopbackContext, &testParams );
1208 
1209 	if( inputDevice == outputDevice )
1210 	{
1211 		// Use full duplex if checking for loopback on one device.
1212 		testParams.flags &= ~PAQA_FLAG_TWO_STREAMS;
1213 	}
1214 	else
1215 	{
1216 		// Use half duplex if checking for loopback on two different device.
1217 		testParams.flags = PAQA_FLAG_TWO_STREAMS;
1218 	}
1219 	err = PaQa_RunLoopback( &loopbackContext );
1220 	QA_ASSERT_TRUE("loopback detection callback did not run", (loopbackContext.callbackCount > 1) );
1221 
1222 	// Analyse recording to see if we captured the output.
1223 	// Start in the middle assuming past latency.
1224 	startFrame = testParams.maxFrames/2;
1225 	numFrames = testParams.maxFrames/2;
1226 	magLeft = PaQa_CorrelateSine( &loopbackContext.recordings[0],
1227 									loopbackContext.generators[0].frequency,
1228 									testParams.sampleRate,
1229 									startFrame, numFrames, NULL );
1230 	magRight = PaQa_CorrelateSine( &loopbackContext.recordings[1],
1231 									loopbackContext.generators[1].frequency,
1232 									testParams.sampleRate,
1233 									startFrame, numFrames, NULL );
1234 	printf("   Amplitudes: left = %f, right = %f\n", magLeft, magRight );
1235 
1236 	// Check for backwards cable.
1237     loopbackIsConnected = ((magLeft > minAmplitude) && (magRight > minAmplitude));
1238 
1239 	if( !loopbackIsConnected )
1240 	{
1241 		double magLeftReverse = PaQa_CorrelateSine( &loopbackContext.recordings[0],
1242 												   loopbackContext.generators[1].frequency,
1243 												   testParams.sampleRate,
1244 												   startFrame, numFrames, NULL );
1245 
1246 		double magRightReverse = PaQa_CorrelateSine( &loopbackContext.recordings[1],
1247 													loopbackContext.generators[0].frequency,
1248 													testParams.sampleRate,
1249 													startFrame, numFrames, NULL );
1250 
1251 		if ((magLeftReverse > minAmplitude) && (magRightReverse>minAmplitude))
1252 		{
1253 			printf("ERROR - You seem to have the left and right channels swapped on the loopback cable!\n");
1254 		}
1255 	}
1256 	else
1257 	{
1258 		double rms = 0.0;
1259 		if( PaQa_CheckForClippedLoopback( &loopbackContext ) )
1260 		{
1261 			// Clipped so don't use this loopback.
1262 			loopbackIsConnected = 0;
1263 		}
1264 
1265 		err = PaQa_MeasureBackgroundNoise( &loopbackContext, &rms );
1266 		printf("   Background noise = %f\n", rms );
1267 		if( err )
1268 		{
1269 			printf("ERROR - Could not measure background noise on this input!\n");
1270 			loopbackIsConnected = 0;
1271 		}
1272 		else if( rms > MAX_BACKGROUND_NOISE_RMS )
1273 		{
1274 			printf("ERROR - There is too much background noise on this input!\n");
1275 			loopbackIsConnected = 0;
1276 		}
1277 	}
1278 
1279 	PaQa_TeardownLoopbackContext( &loopbackContext );
1280 	return loopbackIsConnected;
1281 
1282 error:
1283 	PaQa_TeardownLoopbackContext( &loopbackContext );
1284 	return err;
1285 }
1286 
1287 /*******************************************************************/
1288 /**
1289  * If there is a loopback connection then run the analysis.
1290  */
1291 static int CheckLoopbackAndScan( UserOptions *userOptions,
1292 								PaDeviceIndex iIn, PaDeviceIndex iOut )
1293 {
1294 	int loopbackConnected = PaQa_CheckForLoopBack( userOptions, iIn, iOut );
1295 	if( loopbackConnected > 0 )
1296 	{
1297 		PaQa_AnalyzeLoopbackConnection( userOptions, iIn, iOut );
1298 		return 1;
1299 	}
1300 	return 0;
1301 }
1302 
1303 /*******************************************************************/
1304 /**
1305  * Scan every combination of output to input device.
1306  * If a loopback is found the analyse the combination.
1307  * The scan can be overriden using the -i and -o command line options.
1308  */
1309 static int ScanForLoopback(UserOptions *userOptions)
1310 {
1311 	PaDeviceIndex iIn,iOut;
1312 	int  numLoopbacks = 0;
1313     int  numDevices;
1314     numDevices = Pa_GetDeviceCount();
1315 
1316 	// If both devices are specified then just use that combination.
1317 	if ((userOptions->inputDevice >= 0) && (userOptions->outputDevice >= 0))
1318 	{
1319 		numLoopbacks += CheckLoopbackAndScan( userOptions, userOptions->inputDevice, userOptions->outputDevice );
1320 	}
1321 	else if (userOptions->inputDevice >= 0)
1322 	{
1323 		// Just scan for output.
1324 		for( iOut=0; iOut<numDevices; iOut++ )
1325 		{
1326 			numLoopbacks += CheckLoopbackAndScan( userOptions, userOptions->inputDevice, iOut );
1327 		}
1328 	}
1329 	else if (userOptions->outputDevice >= 0)
1330 	{
1331 		// Just scan for input.
1332 		for( iIn=0; iIn<numDevices; iIn++ )
1333 		{
1334 			numLoopbacks += CheckLoopbackAndScan( userOptions, iIn, userOptions->outputDevice );
1335 		}
1336 	}
1337 	else
1338 	{
1339 		// Scan both.
1340 		for( iOut=0; iOut<numDevices; iOut++ )
1341 		{
1342 			for( iIn=0; iIn<numDevices; iIn++ )
1343 			{
1344 				numLoopbacks += CheckLoopbackAndScan( userOptions, iIn, iOut );
1345 			}
1346 		}
1347 	}
1348 	QA_ASSERT_TRUE( "No good loopback cable found.", (numLoopbacks > 0) );
1349 	return numLoopbacks;
1350 
1351 error:
1352 	return -1;
1353 }
1354 
1355 /*==========================================================================================*/
1356 int TestSampleFormatConversion( void )
1357 {
1358 	int i;
1359 	const float floatInput[] = { 1.0, 0.5, -0.5, -1.0 };
1360 
1361 	const char charInput[] = { 127, 64, -64, -128 };
1362 	const unsigned char ucharInput[] = { 255, 128+64, 64, 0 };
1363 	const short shortInput[] = { 32767, 32768/2, -32768/2, -32768 };
1364 	const int intInput[] = { 2147483647, 2147483647/2, -1073741824 /*-2147483648/2 doesn't work in msvc*/, -2147483648 };
1365 
1366 	float floatOutput[4];
1367 	short shortOutput[4];
1368 	int intOutput[4];
1369 	unsigned char ucharOutput[4];
1370 	char charOutput[4];
1371 
1372 	QA_ASSERT_EQUALS("int must be 32-bit", 4, (int) sizeof(int) );
1373 	QA_ASSERT_EQUALS("short must be 16-bit", 2, (int) sizeof(short) );
1374 
1375 	// from Float ======
1376 	PaQa_ConvertFromFloat( floatInput, 4, paUInt8, ucharOutput );
1377 	for( i=0; i<4; i++ )
1378 	{
1379 		QA_ASSERT_CLOSE_INT( "paFloat32 -> paUInt8 -> error", ucharInput[i], ucharOutput[i], 1 );
1380 	}
1381 
1382 	PaQa_ConvertFromFloat( floatInput, 4, paInt8, charOutput );
1383 	for( i=0; i<4; i++ )
1384 	{
1385 		QA_ASSERT_CLOSE_INT( "paFloat32 -> paInt8 -> error", charInput[i], charOutput[i], 1 );
1386 	}
1387 
1388 	PaQa_ConvertFromFloat( floatInput, 4, paInt16, shortOutput );
1389 	for( i=0; i<4; i++ )
1390 	{
1391 		QA_ASSERT_CLOSE_INT( "paFloat32 -> paInt16 error", shortInput[i], shortOutput[i], 1 );
1392 	}
1393 
1394 	PaQa_ConvertFromFloat( floatInput, 4, paInt32, intOutput );
1395 	for( i=0; i<4; i++ )
1396 	{
1397 		QA_ASSERT_CLOSE_INT( "paFloat32 -> paInt32 error", intInput[i], intOutput[i], 0x00010000 );
1398 	}
1399 
1400 
1401 	// to Float ======
1402 	memset( floatOutput, 0, sizeof(floatOutput) );
1403 	PaQa_ConvertToFloat( ucharInput, 4, paUInt8, floatOutput );
1404 	for( i=0; i<4; i++ )
1405 	{
1406 		QA_ASSERT_CLOSE( "paUInt8 -> paFloat32 error", floatInput[i], floatOutput[i], 0.01 );
1407 	}
1408 
1409 	memset( floatOutput, 0, sizeof(floatOutput) );
1410 	PaQa_ConvertToFloat( charInput, 4, paInt8, floatOutput );
1411 	for( i=0; i<4; i++ )
1412 	{
1413 		QA_ASSERT_CLOSE( "paInt8 -> paFloat32 error", floatInput[i], floatOutput[i], 0.01 );
1414 	}
1415 
1416 	memset( floatOutput, 0, sizeof(floatOutput) );
1417 	PaQa_ConvertToFloat( shortInput, 4, paInt16, floatOutput );
1418 	for( i=0; i<4; i++ )
1419 	{
1420 		QA_ASSERT_CLOSE( "paInt16 -> paFloat32 error", floatInput[i], floatOutput[i], 0.001 );
1421 	}
1422 
1423 	memset( floatOutput, 0, sizeof(floatOutput) );
1424 	PaQa_ConvertToFloat( intInput, 4, paInt32, floatOutput );
1425 	for( i=0; i<4; i++ )
1426 	{
1427 		QA_ASSERT_CLOSE( "paInt32 -> paFloat32 error", floatInput[i], floatOutput[i], 0.00001 );
1428 	}
1429 
1430 	return 0;
1431 
1432 error:
1433 	return -1;
1434 }
1435 
1436 
1437 /*******************************************************************/
1438 void usage( const char *name )
1439 {
1440 	printf("%s [-i# -o# -l# -r# -s# -m -w -dDir]\n", name);
1441 	printf("  -i# - Input device ID. Will scan for loopback cable if not specified.\n");
1442 	printf("  -o# - Output device ID. Will scan for loopback if not specified.\n");
1443 	printf("  -l# - Latency for both input and output in milliseconds.\n");
1444 	printf("  --inputLatency # Input latency in milliseconds.\n");
1445 	printf("  --outputLatency # Output latency in milliseconds.\n");
1446 	printf("  -r# - Sample Rate in Hz.  Will use multiple common rates if not specified.\n");
1447 	printf("  -s# - Size of callback buffer in frames, framesPerBuffer. Will use common values if not specified.\n");
1448 	printf("  -w  - Save bad recordings in a WAV file.\n");
1449 	printf("  -dDir - Path for Directory for WAV files. Default is current directory.\n");
1450 	printf("  -m  - Just test the DSP Math code and not the audio devices.\n");
1451 	printf("  -v  - Verbose reports.\n");
1452 }
1453 
1454 /*******************************************************************/
1455 int main( int argc, char **argv )
1456 {
1457     int i;
1458 	UserOptions userOptions;
1459 	int result = 0;
1460 	int justMath = 0;
1461     char *executableName = argv[0];
1462 
1463 	printf("PortAudio LoopBack Test built " __DATE__ " at " __TIME__ "\n");
1464 
1465 	if( argc > 1 ){
1466 		printf("running with arguments:");
1467 		for(i=1; i < argc; ++i )
1468 			printf(" %s", argv[i] );
1469 		printf("\n");
1470 	}else{
1471 		printf("running with no arguments\n");
1472 	}
1473 
1474 	memset(&userOptions, 0, sizeof(userOptions));
1475 	userOptions.inputDevice = paNoDevice;
1476 	userOptions.outputDevice = paNoDevice;
1477 	userOptions.sampleRate = -1;
1478 	userOptions.framesPerBuffer = -1;
1479 	userOptions.inputLatency = -1;
1480 	userOptions.outputLatency = -1;
1481 	userOptions.waveFilePath = ".";
1482 
1483 	// Process arguments. Skip name of executable.
1484 	i = 1;
1485 	while( i<argc )
1486 	{
1487 		char *arg = argv[i];
1488 		if( arg[0] == '-' )
1489 		{
1490 			switch(arg[1])
1491 			{
1492 				case 'i':
1493 					userOptions.inputDevice = atoi(&arg[2]);
1494 					break;
1495 				case 'o':
1496 					userOptions.outputDevice = atoi(&arg[2]);
1497 					break;
1498 				case 'l':
1499 					userOptions.inputLatency = userOptions.outputLatency = atoi(&arg[2]);
1500 					break;
1501 				case 'r':
1502 					userOptions.sampleRate = atoi(&arg[2]);
1503 					break;
1504 				case 's':
1505 					userOptions.framesPerBuffer = atoi(&arg[2]);
1506 					break;
1507 
1508 				case 'm':
1509 					printf("Option -m set so just testing math and not the audio devices.\n");
1510 					justMath = 1;
1511 					break;
1512 
1513 				case 'w':
1514 					userOptions.saveBadWaves = 1;
1515 					break;
1516 				case 'd':
1517 					userOptions.waveFilePath = &arg[2];
1518 					break;
1519 
1520 				case 'v':
1521 					userOptions.verbose = 1;
1522 					break;
1523 
1524 				case 'h':
1525 					usage( executableName );
1526 					exit(0);
1527 					break;
1528 
1529 				case '-':
1530 				{
1531 					if( strcmp( &arg[2], "inputLatency" ) == 0 )
1532 					{
1533 						i += 1;
1534 						userOptions.inputLatency = atoi(argv[i]);
1535 					}
1536 					else if( strcmp( &arg[2], "outputLatency" ) == 0 )
1537 					{
1538 						i += 1;
1539 						userOptions.outputLatency = atoi(argv[i]);
1540 					}
1541 					else
1542 					{
1543 						printf("Illegal option: %s\n", arg);
1544 						usage( executableName );
1545 						exit(1);
1546 					}
1547 
1548 				}
1549 					break;
1550 
1551 
1552 				default:
1553 					printf("Illegal option: %s\n", arg);
1554 					usage( executableName );
1555 					exit(1);
1556 					break;
1557 			}
1558 		}
1559 		else
1560 		{
1561 			printf("Illegal argument: %s\n", arg);
1562 			usage( executableName );
1563 			exit(1);
1564 
1565 		}
1566 		i += 1;
1567 	}
1568 
1569 	result = PaQa_TestAnalyzer();
1570 
1571 	// Test sample format conversion tool.
1572 	result = TestSampleFormatConversion();
1573 
1574 	if( (result == 0) && (justMath == 0) )
1575 	{
1576 		Pa_Initialize();
1577 		printf( "PortAudio version number = %d\nPortAudio version text = '%s'\n",
1578 			   Pa_GetVersion(), Pa_GetVersionText() );
1579 		printf( "=============== PortAudio Devices ========================\n" );
1580 		PaQa_ListAudioDevices();
1581         if( Pa_GetDeviceCount() == 0 )
1582             printf( "no devices found.\n" );
1583 
1584 		printf( "=============== Detect Loopback ==========================\n" );
1585 		ScanForLoopback(&userOptions);
1586 
1587 		Pa_Terminate();
1588 	}
1589 
1590 	if (g_testsFailed == 0)
1591 	{
1592 		printf("PortAudio QA SUCCEEDED! %d tests passed, %d tests failed\n", g_testsPassed, g_testsFailed );
1593 		return 0;
1594 
1595 	}
1596 	else
1597 	{
1598 		printf("PortAudio QA FAILED! %d tests passed, %d tests failed\n", g_testsPassed, g_testsFailed );
1599 		return 1;
1600 	}
1601 }
1602