 165b814f5d
			
		
	
	
		165b814f5d
		
	
	
	
	
		
			
			this is necessary in order to get Furnace to build using CMake 4.0. you should do: git submodule deinit extern/portaudio
		
			
				
	
	
		
			844 lines
		
	
	
		
			30 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			844 lines
		
	
	
		
			30 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @file paqa_devs.c
 | |
|     @ingroup qa_src
 | |
|     @brief Self Testing Quality Assurance app for PortAudio
 | |
|     Try to open devices and run through all possible configurations.
 | |
|     By default, open only the default devices. Command line options support
 | |
|     opening every device, or all input devices, or all output devices.
 | |
|     This test does not verify that the configuration works well.
 | |
|     It just verifies that it does not crash. It requires a human to
 | |
|     listen to the sine wave outputs.
 | |
| 
 | |
|     @author Phil Burk  http://www.softsynth.com
 | |
| 
 | |
|     Pieter adapted to V19 API. Test now relies heavily on
 | |
|     Pa_IsFormatSupported(). Uses same 'standard' sample rates
 | |
|     as in test pa_devs.c.
 | |
| */
 | |
| /*
 | |
|  * $Id$
 | |
|  *
 | |
|  * This program uses the PortAudio Portable Audio Library.
 | |
|  * For more information see: http://www.portaudio.com
 | |
|  * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
 | |
|  *
 | |
|  * Permission is hereby granted, free of charge, to any person obtaining
 | |
|  * a copy of this software and associated documentation files
 | |
|  * (the "Software"), to deal in the Software without restriction,
 | |
|  * including without limitation the rights to use, copy, modify, merge,
 | |
|  * publish, distribute, sublicense, and/or sell copies of the Software,
 | |
|  * and to permit persons to whom the Software is furnished to do so,
 | |
|  * subject to the following conditions:
 | |
|  *
 | |
|  * The above copyright notice and this permission notice shall be
 | |
|  * included in all copies or substantial portions of the Software.
 | |
|  *
 | |
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 | |
|  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 | |
|  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 | |
|  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
 | |
|  * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
 | |
|  * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 | |
|  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 | |
|  */
 | |
| 
 | |
| /*
 | |
|  * The text above constitutes the entire PortAudio license; however,
 | |
|  * the PortAudio community also makes the following non-binding requests:
 | |
|  *
 | |
|  * Any person wishing to distribute modifications to the Software is
 | |
|  * requested to send the modifications to the original developer so that
 | |
|  * they can be incorporated into the canonical version. It is also
 | |
|  * requested that these non-binding requests be included along with the
 | |
|  * license above.
 | |
|  */
 | |
| 
 | |
| #include <stdint.h>
 | |
| #include <stdio.h>
 | |
| #include <stdlib.h> /* for EXIT_SUCCESS and EXIT_FAILURE */
 | |
| #include <string.h>
 | |
| #define _USE_MATH_DEFINES
 | |
| #include <math.h>
 | |
| #include "portaudio.h"
 | |
| #include "pa_trace.h"
 | |
| #include "paqa_macros.h"
 | |
| 
 | |
| /****************************************** Definitions ***********/
 | |
| #define RUN_TIME_SECONDS   (1.2)
 | |
| #define BYPASS_TESTS       (0) /* If 1 then skip actual tests and just iterate. */
 | |
| 
 | |
| #define MODE_INPUT         (0)
 | |
| #define MODE_OUTPUT        (1)
 | |
| #define MAX_TEST_CHANNELS  (4)
 | |
| #define LOWEST_FREQUENCY   (300.0)
 | |
| #define SINE_AMPLITUDE     (0.2)
 | |
| #define MILLIS_PER_SECOND  (1000.0)
 | |
| #define DEFAULT_FRAMES_PER_BUFFER  (128)
 | |
| 
 | |
| #define TEST_LEVEL_QUICK   (0)
 | |
| #define TEST_LEVEL_NORMAL  (1)
 | |
| #define TEST_LEVEL_EXHAUSTIVE  (2)
 | |
| 
 | |
| PAQA_INSTANTIATE_GLOBALS
 | |
| 
 | |
| typedef struct PaSineOscillator
 | |
| {
 | |
|     float          phase;
 | |
|     float          phaseIncrement;
 | |
| } PaSineOscillator;
 | |
| 
 | |
| /* Parameters that cover all options for a test.
 | |
|  */
 | |
| typedef struct PaQaTestParameters
 | |
| {
 | |
|     PaDeviceIndex    deviceID;
 | |
|     PaSampleFormat   format;
 | |
|     double           sampleRate;
 | |
|     double           durationSeconds;
 | |
|     double           suggestedLatency;
 | |
|     int              framesPerBuffer;
 | |
|     int              numInputChannels;
 | |
|     int              numOutputChannels;
 | |
|     int              mode;
 | |
|     int              useCallback;
 | |
|     int              useNonInterleaved; /* Test paNonInterleaved flag */
 | |
| } PaQaTestParameters;
 | |
| 
 | |
| PaQaTestParameters kDefaultTestParameters = {
 | |
|     0, /* deviceId */
 | |
|     paFloat32,
 | |
|     44100,
 | |
|     RUN_TIME_SECONDS,
 | |
|     0.020,
 | |
|     DEFAULT_FRAMES_PER_BUFFER,
 | |
|     0, /* numInputChannels */
 | |
|     1, /* numOutputChannels */
 | |
|     MODE_OUTPUT,
 | |
|     1, /* useCallback */
 | |
|     0, /* useNonInterleaved */
 | |
| };
 | |
| 
 | |
| /* Runtime data used during the test. */
 | |
| typedef struct PaQaData
 | |
| {
 | |
|     const PaQaTestParameters *parameters;
 | |
|     // Dynamic state.
 | |
|     int              bytesPerSample;
 | |
|     volatile unsigned long    frameCounter;
 | |
|     volatile unsigned long    framesLeft;
 | |
|     unsigned long    framesPerBurst;
 | |
|     unsigned long    minFramesPerBuffer;
 | |
|     unsigned long    maxFramesPerBuffer;
 | |
|     unsigned long    framesDuration;
 | |
|     PaSineOscillator sineOscillators[MAX_TEST_CHANNELS];
 | |
|     void            *audioBuffer;
 | |
| } PaQaData;
 | |
| 
 | |
| /****************************************** Prototypes ***********/
 | |
| static int TestSingleStreamParameters(PaQaTestParameters parameters);
 | |
| static int QaCallback( const void *inputBuffer, void *outputBuffer,
 | |
|                        unsigned long framesPerBuffer,
 | |
|                        const PaStreamCallbackTimeInfo* timeInfo,
 | |
|                        PaStreamCallbackFlags statusFlags,
 | |
|                        void *userData );
 | |
| 
 | |
| static void PaQaSetupData(PaQaData *myData,
 | |
|                           const PaQaTestParameters *parameters)
 | |
| {
 | |
|     memset(myData, 0, sizeof(PaQaData));
 | |
| 
 | |
|     myData->parameters = parameters;
 | |
|     myData->frameCounter = 0;
 | |
|     myData->framesLeft = (unsigned long) (parameters->sampleRate * parameters->durationSeconds);
 | |
| 
 | |
|     myData->minFramesPerBuffer = UINT32_MAX;
 | |
|     myData->maxFramesPerBuffer = 0;
 | |
| 
 | |
|     for (int channelIndex = 0; channelIndex < MAX_TEST_CHANNELS; channelIndex++)
 | |
|     {
 | |
|         myData->sineOscillators[channelIndex].phase = 0.0f;
 | |
|         myData->sineOscillators[channelIndex].phaseIncrement =
 | |
|                 (2.0 * M_PI * LOWEST_FREQUENCY / parameters->sampleRate);
 | |
|     }
 | |
| 
 | |
|     switch( parameters->format )
 | |
|     {
 | |
|         case paFloat32:
 | |
|         case paInt32:
 | |
|         case paInt24:
 | |
|             myData->bytesPerSample = 4;
 | |
|             break;
 | |
|             /*  case paPackedInt24:
 | |
|              myData->bytesPerSample = 3;
 | |
|              break; */
 | |
|         default:
 | |
|             myData->bytesPerSample = 2;
 | |
|             break;
 | |
|     }
 | |
|     myData->framesPerBurst = (parameters->framesPerBuffer == 0) ? 128 : parameters->framesPerBuffer;
 | |
|     if (parameters->useCallback == 0) {
 | |
|         /* We need our own buffer for blocking IO. */
 | |
|         int numChannels = (parameters->mode == MODE_OUTPUT)
 | |
|                 ? parameters->numOutputChannels
 | |
|                 : parameters->numInputChannels;
 | |
|         myData->audioBuffer = malloc(myData->bytesPerSample * numChannels * myData->framesPerBurst);
 | |
|     }
 | |
| }
 | |
| 
 | |
| static void PaQaTeardownData(PaQaData *myData,
 | |
|                           const PaQaTestParameters *parameters)
 | |
| {
 | |
|     (void) parameters;
 | |
|     free(myData->audioBuffer);
 | |
| }
 | |
| 
 | |
| static float NextSineSample( PaSineOscillator *sineOscillator )
 | |
| {
 | |
|     float phase = sineOscillator->phase + sineOscillator->phaseIncrement;
 | |
|     if( phase > (float)M_PI ) phase -= (float)(2.0 * M_PI);
 | |
|     sineOscillator->phase = phase;
 | |
|     return sinf(phase) * SINE_AMPLITUDE;
 | |
| }
 | |
| 
 | |
| #define SETUP_BUFFERS(_data_type) \
 | |
|     _data_type *out; \
 | |
|     int stride; \
 | |
|     if (parameters->useNonInterleaved) { \
 | |
|         /* outputData points to an array of pointers to the buffers. */ \
 | |
|         void **buffers = (void **)outputData; \
 | |
|         out = (_data_type *)buffers[channelIndex]; \
 | |
|         stride = 1; \
 | |
|     } else { \
 | |
|         out =  &((_data_type *) outputData)[channelIndex]; \
 | |
|         stride = parameters->numOutputChannels; \
 | |
|     }
 | |
| 
 | |
| /*******************************************************************/
 | |
| /* This routine will be called by the PortAudio engine when audio is needed.
 | |
| ** It may be called at interrupt level on some machines so don't do anything
 | |
| ** that could mess up the system like calling malloc() or free().
 | |
| */
 | |
| static int QaCallback( const void *inputData,
 | |
|                        void *outputData,
 | |
|                        unsigned long framesPerBuffer,
 | |
|                        const PaStreamCallbackTimeInfo* timeInfo,
 | |
|                        PaStreamCallbackFlags statusFlags,
 | |
|                        void *userData )
 | |
| {
 | |
|     unsigned long frameIndex;
 | |
|     int channelIndex;
 | |
|     float sample;
 | |
|     PaQaData *data = (PaQaData *) userData;
 | |
|     const PaQaTestParameters *parameters = data->parameters;
 | |
|     (void) inputData;
 | |
| 
 | |
|     data->minFramesPerBuffer = (framesPerBuffer < data->minFramesPerBuffer)
 | |
|             ? framesPerBuffer : data->minFramesPerBuffer;
 | |
|     data->maxFramesPerBuffer = (framesPerBuffer > data->maxFramesPerBuffer)
 | |
|             ? framesPerBuffer : data->maxFramesPerBuffer;
 | |
| 
 | |
|     /* Play simple sine wave. */
 | |
|     if( parameters->mode == MODE_OUTPUT )
 | |
|     {
 | |
|         switch( parameters->format )
 | |
|         {
 | |
|         case paFloat32:
 | |
|             {
 | |
|                 for( channelIndex = 0; channelIndex < parameters->numOutputChannels; channelIndex++ )
 | |
|                 {
 | |
|                     SETUP_BUFFERS(float);
 | |
|                     for( frameIndex = 0; frameIndex < framesPerBuffer; frameIndex++ )
 | |
|                     {
 | |
|                         sample = NextSineSample( &data->sineOscillators[channelIndex] );
 | |
|                         *out = sample;
 | |
|                         out += stride;
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|             break;
 | |
| 
 | |
|         case paInt32:
 | |
|             {
 | |
|                 for( channelIndex = 0; channelIndex < parameters->numOutputChannels; channelIndex++ )
 | |
|                 {
 | |
|                     SETUP_BUFFERS(int32_t);
 | |
|                     for( frameIndex = 0; frameIndex < framesPerBuffer; frameIndex++ )
 | |
|                     {
 | |
|                         sample = NextSineSample( &data->sineOscillators[channelIndex] );
 | |
|                         *out = ((int32_t)(sample * 8388607)) << 8;
 | |
|                         out += stride;
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|             break;
 | |
| 
 | |
|         case paInt16:
 | |
|             {
 | |
|                 for( channelIndex = 0; channelIndex < parameters->numOutputChannels; channelIndex++ )
 | |
|                 {
 | |
|                     SETUP_BUFFERS(int16_t);
 | |
|                     for( frameIndex = 0; frameIndex < framesPerBuffer; frameIndex++ )
 | |
|                     {
 | |
|                         sample = NextSineSample( &data->sineOscillators[channelIndex] );
 | |
|                         *out = (int16_t)(sample * 32767);
 | |
|                         out += stride;
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|             break;
 | |
| 
 | |
|         default:
 | |
|             {
 | |
|                 unsigned char *out =  (unsigned char *) outputData;
 | |
|                 unsigned long numBytes = framesPerBuffer * parameters->numOutputChannels * data->bytesPerSample;
 | |
|                 memset(out, 0, numBytes);
 | |
|             }
 | |
|             break;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     data->frameCounter += framesPerBuffer;
 | |
| 
 | |
|     /* Are we through yet? */
 | |
|     if( data->framesLeft > framesPerBuffer )
 | |
|     {
 | |
|         PaUtil_AddTraceMessage("QaCallback: running. framesLeft", data->framesLeft );
 | |
|         data->framesLeft -= framesPerBuffer;
 | |
|         return paContinue;
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|         PaUtil_AddTraceMessage("QaCallback: DONE! framesLeft", data->framesLeft );
 | |
|         data->framesLeft = 0;
 | |
|         return paComplete;
 | |
|     }
 | |
| }
 | |
| 
 | |
| static PaError CheckBlockingIO(PaStream *stream,
 | |
|                                PaQaData *data,
 | |
|                                int millis) {
 | |
|     PaError result = paNoError;
 | |
|     double elapsedTime = 0.0;
 | |
|     double millisPerBurst = MILLIS_PER_SECOND * data->framesPerBurst / data->parameters->sampleRate;
 | |
|     while (elapsedTime < millis) {
 | |
|         int callbackResult;
 | |
|         if (data->parameters->mode == MODE_OUTPUT) {
 | |
|             callbackResult = QaCallback(NULL /*inputBuffer */,
 | |
|                                         data->audioBuffer,
 | |
|                                         data->framesPerBurst,
 | |
|                                         NULL /* timeInfo */, // TODO
 | |
|                                         0, // stream flags
 | |
|                                         data);
 | |
|             if (callbackResult == 0) {
 | |
|                 result = Pa_WriteStream(stream, data->audioBuffer, data->framesPerBurst);
 | |
|                 ASSERT_EQ(paNoError, result);
 | |
|             }
 | |
|         } else if (data->parameters->mode == MODE_INPUT) {
 | |
|             result = Pa_ReadStream(stream, data->audioBuffer, data->framesPerBurst);
 | |
|             ASSERT_EQ(paNoError, result);
 | |
|             callbackResult = QaCallback(data->audioBuffer,
 | |
|                                         NULL /*outputBuffer */,
 | |
|                                         data->framesPerBurst,
 | |
|                                         NULL /* timeInfo */, // TODO
 | |
|                                         0, // stream flags
 | |
|                                         data);
 | |
|         }
 | |
|         elapsedTime += millisPerBurst;
 | |
|     }
 | |
| error:
 | |
|     return result;
 | |
| }
 | |
| 
 | |
| static void CheckDefaultCallbackRun(PaStream *stream,
 | |
|                         PaQaData *data) {
 | |
|     PaError result = paNoError;
 | |
|     PaTime oldStreamTimeMillis = 0.0;
 | |
|     PaTime startStreamTimeMillis = 0.0;
 | |
|     unsigned long oldFramesLeft = INT32_MAX;
 | |
| 
 | |
|     oldStreamTimeMillis = Pa_GetStreamTime(stream) * MILLIS_PER_SECOND;
 | |
| 
 | |
|     ASSERT_EQ(0, Pa_IsStreamActive(stream));
 | |
|     ASSERT_EQ(1, Pa_IsStreamStopped(stream));
 | |
| 
 | |
|     ASSERT_EQ(paNoError, result = Pa_StartStream( stream ));
 | |
|     startStreamTimeMillis = Pa_GetStreamTime(stream) * MILLIS_PER_SECOND;
 | |
| 
 | |
|     ASSERT_EQ(1, Pa_IsStreamActive(stream));
 | |
|     ASSERT_EQ(0, Pa_IsStreamStopped(stream));
 | |
| 
 | |
|     /* Sleep long enough for the stream callback to have stopped itself. */
 | |
|     while ((oldStreamTimeMillis - startStreamTimeMillis) < ((RUN_TIME_SECONDS + 0.5) * MILLIS_PER_SECOND)
 | |
|            && (data->framesLeft > 0))
 | |
|     {
 | |
|         if (data->parameters->useCallback) {
 | |
|             Pa_Sleep(200);
 | |
|         } else {
 | |
|             result = CheckBlockingIO(stream,
 | |
|                                      data,
 | |
|                                      200);
 | |
|             ASSERT_EQ(paNoError, result);
 | |
|         }
 | |
| 
 | |
|         PaTime newStreamTime = Pa_GetStreamTime(stream) * MILLIS_PER_SECOND;
 | |
|         //printf("oldStreamTime  = %9.6f, newStreamTime = %9.6f\n", oldStreamTime, newStreamTime ); /**/
 | |
|         ASSERT_LE(oldStreamTimeMillis, newStreamTime);
 | |
| 
 | |
|         /* Check to make sure callback is decrementing framesLeft. */
 | |
|         unsigned long newFramesLeft = data->framesLeft;
 | |
|         //printf("oldFrames = %lu, newFrames = %lu\n", oldFramesLeft, newFramesLeft );
 | |
|         ASSERT_GE(oldFramesLeft, newFramesLeft);
 | |
| 
 | |
|         oldStreamTimeMillis = newStreamTime;
 | |
|         oldFramesLeft = newFramesLeft;
 | |
|     }
 | |
| 
 | |
|     ASSERT_EQ(0, data->framesLeft);
 | |
|     ASSERT_LE((1 * data->parameters->sampleRate), data->frameCounter);
 | |
| 
 | |
|     if (data->parameters->framesPerBuffer > 0) {
 | |
|         ASSERT_EQ(data->parameters->framesPerBuffer, data->minFramesPerBuffer);
 | |
|         ASSERT_EQ(data->parameters->framesPerBuffer, data->maxFramesPerBuffer);
 | |
|     } else {
 | |
|         ASSERT_GT(data->minFramesPerBuffer, 0);
 | |
|         ASSERT_LT(data->maxFramesPerBuffer, data->parameters->sampleRate);
 | |
|     }
 | |
| 
 | |
|     ASSERT_EQ(data->parameters->useCallback ? 0 : 1, Pa_IsStreamActive(stream));
 | |
|     ASSERT_EQ(0, Pa_IsStreamStopped(stream));
 | |
| 
 | |
|     ASSERT_EQ(paNoError, result = Pa_StopStream( stream ));
 | |
| 
 | |
|     ASSERT_EQ(0, Pa_IsStreamActive(stream));
 | |
|     ASSERT_EQ(1, Pa_IsStreamStopped(stream));
 | |
| 
 | |
|     ASSERT_EQ(paNoError, result = Pa_CloseStream( stream ));
 | |
|     return;
 | |
| 
 | |
| error:
 | |
|     printf("result = %d = for %s\n", result, Pa_GetErrorText(result));
 | |
|     Pa_CloseStream(stream);
 | |
|     return;
 | |
| }
 | |
| 
 | |
| /*******************************************************************/
 | |
| static int TestSingleStreamParameters(PaQaTestParameters testParameters)
 | |
| {
 | |
|     PaStreamParameters inputParameters, outputParameters, *ipp, *opp;
 | |
|     PaStream *stream = NULL;
 | |
|     PaQaData myData;
 | |
|     int numChannels = 0;
 | |
| 
 | |
|     if( testParameters.mode == MODE_INPUT )
 | |
|     {
 | |
|         opp = NULL;
 | |
|         numChannels = testParameters.numInputChannels;
 | |
|         inputParameters.device       = testParameters.deviceID;
 | |
|         inputParameters.channelCount = testParameters.numInputChannels;
 | |
|         inputParameters.sampleFormat = testParameters.format
 | |
|                 | (testParameters.useNonInterleaved ? paNonInterleaved : 0);
 | |
|         inputParameters.suggestedLatency = testParameters.suggestedLatency;
 | |
|         inputParameters.hostApiSpecificStreamInfo = NULL;
 | |
|         ipp = &inputParameters;
 | |
|     }
 | |
|     else if( testParameters.mode == MODE_OUTPUT )
 | |
|     {
 | |
|         ipp = NULL;
 | |
|         numChannels = testParameters.numOutputChannels;
 | |
|         outputParameters.device       = testParameters.deviceID;
 | |
|         outputParameters.channelCount = testParameters.numOutputChannels;
 | |
|         outputParameters.sampleFormat = testParameters.format
 | |
|                 | (testParameters.useNonInterleaved ? paNonInterleaved : 0);
 | |
|         outputParameters.suggestedLatency = testParameters.suggestedLatency;
 | |
|         outputParameters.hostApiSpecificStreamInfo = NULL;
 | |
|         opp = &outputParameters;
 | |
|     }
 | |
| 
 | |
|     printf("------ Test: %s, device = %d"
 | |
|            ", #ch = %d"
 | |
|            ", rate = %5g"
 | |
|            ", format = %lu"
 | |
|            ", %s, %s\n",
 | |
|             ( testParameters.mode == MODE_INPUT ) ? "INPUT" : "OUTPUT",
 | |
|            testParameters.deviceID,
 | |
|            numChannels,
 | |
|            testParameters.sampleRate,
 | |
|            (unsigned long)testParameters.format,
 | |
|            testParameters.useCallback ? "CALLBACK" : "BLOCKING",
 | |
|            testParameters.useNonInterleaved ? "NON-INT" : "INTER"
 | |
|            );
 | |
| 
 | |
|     if (BYPASS_TESTS) return 0;
 | |
| 
 | |
|     /* Setup data for callback thread. */
 | |
|     PaQaSetupData(&myData, &testParameters);
 | |
| 
 | |
|     if(paFormatIsSupported == Pa_IsFormatSupported( ipp, opp, testParameters.sampleRate ))
 | |
|     {
 | |
|         PaError resultOpen = Pa_OpenStream( &stream,
 | |
|                                 ipp,
 | |
|                                 opp,
 | |
|                                 testParameters.sampleRate,
 | |
|                                 testParameters.framesPerBuffer,
 | |
|                                 paClipOff,  /* we won't output out of range samples so don't bother clipping them */
 | |
|                                 testParameters.useCallback ? QaCallback : NULL,
 | |
|                                 &myData
 | |
|                                );
 | |
| 
 | |
|         if (resultOpen != paNoError) {
 | |
|             printf("Pa_OpenStream() returned = %d = for %s\n",
 | |
|                    resultOpen, Pa_GetErrorText(resultOpen));
 | |
|         }
 | |
|         ASSERT_EQ(paNoError, resultOpen);
 | |
|         ASSERT_TRUE(stream != NULL);
 | |
| 
 | |
|         {
 | |
|             const PaStreamInfo *streamInfo = Pa_GetStreamInfo(stream);
 | |
|             ASSERT_EQ((int)(testParameters.sampleRate), (int)(streamInfo->sampleRate));
 | |
|             if (testParameters.mode == MODE_INPUT) {
 | |
|                 ASSERT_EQ(0, (int)(streamInfo->outputLatency * 1000));
 | |
|             } else {
 | |
|                 ASSERT_EQ(0, (int)(streamInfo->inputLatency * 1000));
 | |
|             }
 | |
|         }
 | |
|         CheckDefaultCallbackRun(stream, &myData);
 | |
| 
 | |
|     } else {
 | |
|         printf("    Parameters NOT supported.\n");
 | |
|     }
 | |
|     PaQaTeardownData(&myData, &testParameters);
 | |
|     return 0;
 | |
| 
 | |
| error:
 | |
|     if( stream != NULL ) Pa_CloseStream( stream );
 | |
|     PaQaTeardownData(&myData, &testParameters);
 | |
|     return -1;
 | |
| }
 | |
| 
 | |
| 
 | |
| static void RunQuickTest()
 | |
| {
 | |
|     PaQaTestParameters parameters = kDefaultTestParameters;
 | |
| 
 | |
| #if 1
 | |
|     printf("\n=========== INPUT ==============\n");
 | |
|     parameters.mode = MODE_INPUT;
 | |
|     parameters.deviceID = Pa_GetDefaultInputDevice();
 | |
|     parameters.format = paFloat32;
 | |
| 
 | |
|     parameters.sampleRate = 44100;
 | |
|     parameters.numInputChannels = 1;
 | |
|     TestSingleStreamParameters(parameters);
 | |
|     parameters.sampleRate = 22050;
 | |
|     TestSingleStreamParameters(parameters);
 | |
| 
 | |
|     parameters.sampleRate = 44100;
 | |
|     parameters.numInputChannels = 2;
 | |
|     TestSingleStreamParameters(parameters);
 | |
| 
 | |
|     parameters.useCallback = 0;
 | |
|     TestSingleStreamParameters(parameters); /* Blocking */
 | |
|     parameters.useNonInterleaved = 1;
 | |
|     TestSingleStreamParameters(parameters); /* Blocking, NonInterleaved */
 | |
|     parameters.useCallback = 1;
 | |
|     TestSingleStreamParameters(parameters); /* NonInterleaved */
 | |
|     parameters.useCallback = 1;
 | |
| #endif
 | |
| 
 | |
|     printf("\n=========== OUTPUT =============\n");
 | |
|     parameters = kDefaultTestParameters;
 | |
|     parameters.mode = MODE_OUTPUT;
 | |
|     parameters.deviceID = Pa_GetDefaultOutputDevice();
 | |
|     parameters.sampleRate = 48000;
 | |
|     parameters.numOutputChannels = 1;
 | |
|     parameters.format = paFloat32;
 | |
|     parameters.useCallback = 0;
 | |
|     TestSingleStreamParameters(parameters);
 | |
| 
 | |
|     /* Interleaved */
 | |
|     parameters = kDefaultTestParameters;
 | |
|     parameters.deviceID = Pa_GetDefaultOutputDevice();
 | |
|     parameters.useNonInterleaved = 0;
 | |
|     parameters.numOutputChannels = 1;
 | |
|     parameters.useCallback = 1;
 | |
|     parameters.format = paFloat32;
 | |
|     TestSingleStreamParameters(parameters);
 | |
|     parameters.useCallback = 0;
 | |
|     parameters.format = paFloat32;
 | |
|     TestSingleStreamParameters(parameters); /* Blocking */
 | |
|     parameters.useCallback = 1;
 | |
| 
 | |
|     parameters.sampleRate = 44100;
 | |
|     parameters.numOutputChannels = 2;
 | |
|     parameters.format = paFloat32;
 | |
|     TestSingleStreamParameters(parameters);
 | |
| 
 | |
|     parameters.sampleRate = 22050;
 | |
|     parameters.numOutputChannels = 2;
 | |
|     parameters.format = paInt16;
 | |
|     TestSingleStreamParameters(parameters);
 | |
| 
 | |
|     /* Non-Interleaved */
 | |
|     parameters = kDefaultTestParameters;
 | |
|     parameters.deviceID = Pa_GetDefaultOutputDevice();
 | |
|     parameters.useNonInterleaved = 1;
 | |
|     parameters.numOutputChannels = 2;
 | |
|     parameters.format = paFloat32;
 | |
|     parameters.useCallback = 0;
 | |
|     TestSingleStreamParameters(parameters); /* Blocking */
 | |
|     parameters.useCallback = 1;
 | |
|     TestSingleStreamParameters(parameters);  /* Blocking */
 | |
|     parameters.format = paInt16;
 | |
|     TestSingleStreamParameters(parameters);
 | |
|     parameters.format = paInt32;
 | |
|     TestSingleStreamParameters(parameters);
 | |
| }
 | |
| 
 | |
| static const double constStandardSampleRates_[] = {
 | |
|         8000.0,  9600.0, 11025.0, 12000.0, 16000.0, 22050.0,
 | |
|         24000.0, 32000.0, 44100.0, 48000.0, 88200.0, 96000.0,
 | |
|         -1.0 }; /* Negative terminated list. */
 | |
| 
 | |
| static const PaSampleFormat constFormatsToTest_[] = {
 | |
|     paFloat32, paInt32, paInt16, 0
 | |
| }; /* Zero terminated list. */
 | |
| 
 | |
| /**
 | |
|  * Iterate through each option with other options set to default.
 | |
|  */
 | |
| static void TestNormal( int mode, int allDevices )
 | |
| {
 | |
|     PaQaTestParameters parameters = kDefaultTestParameters;
 | |
|     int id, jc, i;
 | |
|     int maxChannels;
 | |
|     int isDefault;
 | |
|     const PaDeviceInfo *pdi;
 | |
|     int numDevices = Pa_GetDeviceCount();
 | |
|     parameters.mode = mode;
 | |
| 
 | |
|     for( id=0; id<numDevices; id++ )            /* Iterate through all devices. */
 | |
|     {
 | |
|         parameters.deviceID = id;
 | |
| 
 | |
|         pdi = Pa_GetDeviceInfo( id );
 | |
| 
 | |
|         if( mode == MODE_INPUT ) {
 | |
|             maxChannels = pdi->maxInputChannels;
 | |
|             isDefault = ( id == Pa_GetDefaultInputDevice());
 | |
|         } else {
 | |
|             maxChannels = pdi->maxOutputChannels;
 | |
|             isDefault = ( id == Pa_GetDefaultOutputDevice());
 | |
|         }
 | |
|         if( maxChannels > MAX_TEST_CHANNELS )
 | |
|             maxChannels = MAX_TEST_CHANNELS;
 | |
|         if (maxChannels == 0) continue;  // skip this device, wrong direction
 | |
| 
 | |
|         if (!allDevices && !isDefault) continue; // skip this device
 | |
| 
 | |
|         printf("\n===========================================================\n");
 | |
|         printf("            Device = %s\n", pdi->name );
 | |
|         printf("===========================================================\n");
 | |
|         for( jc=1; jc<=maxChannels; jc++ )
 | |
|         {
 | |
|             if (mode == MODE_INPUT) {
 | |
|                 parameters.numInputChannels = jc;
 | |
|             } else {
 | |
|                 parameters.numOutputChannels = jc;
 | |
|             }
 | |
|             TestSingleStreamParameters(parameters);
 | |
|         }
 | |
| 
 | |
|         /* Try each standard sample rate. */
 | |
|         for( i=0; constStandardSampleRates_[i] > 0; i++ )
 | |
|         {
 | |
|             parameters.sampleRate = constStandardSampleRates_[i];
 | |
|             TestSingleStreamParameters(parameters);
 | |
|         }
 | |
|         parameters.sampleRate = pdi->defaultSampleRate;
 | |
| 
 | |
|         if (mode == MODE_INPUT) {
 | |
|             parameters.suggestedLatency = pdi->defaultHighInputLatency;
 | |
|             TestSingleStreamParameters(parameters);
 | |
|             parameters.suggestedLatency = pdi->defaultLowInputLatency;
 | |
|             TestSingleStreamParameters(parameters);
 | |
|         } else {
 | |
|             parameters.suggestedLatency = pdi->defaultHighOutputLatency;
 | |
|             TestSingleStreamParameters(parameters);
 | |
|             parameters.suggestedLatency = pdi->defaultLowOutputLatency;
 | |
|             TestSingleStreamParameters(parameters);
 | |
|         }
 | |
| 
 | |
|         for (int callback = 0; callback < 2; callback++) {
 | |
|             parameters.useCallback = callback;
 | |
|             for (int nonInterleaved = 0; nonInterleaved < 2; nonInterleaved++) {
 | |
|                 parameters.useNonInterleaved = nonInterleaved;
 | |
|                 TestSingleStreamParameters(parameters);
 | |
|             }
 | |
|         }
 | |
|         parameters.useCallback = 1;
 | |
|         parameters.useNonInterleaved = 0;
 | |
| 
 | |
|         for (int jf = 0; constFormatsToTest_[jf] > 0; jf++) {
 | |
|             parameters.format = constFormatsToTest_[jf];
 | |
|             TestSingleStreamParameters(parameters);
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| /*******************************************************************
 | |
| * Test each output device, through its full range of capabilities. */
 | |
| static void TestExhaustive( int mode, int allDevices )
 | |
| {
 | |
|     PaQaTestParameters parameters = kDefaultTestParameters;
 | |
|     int id, jc, i;
 | |
|     int maxChannels;
 | |
|     int isDefault;
 | |
|     const PaDeviceInfo *pdi;
 | |
|     int numDevices = Pa_GetDeviceCount();
 | |
|     parameters.mode = mode;
 | |
| 
 | |
|     for( id=0; id<numDevices; id++ )            /* Iterate through all devices. */
 | |
|     {
 | |
|         parameters.deviceID = id;
 | |
| 
 | |
|         pdi = Pa_GetDeviceInfo( id );
 | |
| 
 | |
|         if( mode == MODE_INPUT ) {
 | |
|             maxChannels = pdi->maxInputChannels;
 | |
|             isDefault = ( id == Pa_GetDefaultInputDevice());
 | |
|         } else {
 | |
|             maxChannels = pdi->maxOutputChannels;
 | |
|             isDefault = ( id == Pa_GetDefaultOutputDevice());
 | |
|         }
 | |
|         if( maxChannels > MAX_TEST_CHANNELS )
 | |
|             maxChannels = MAX_TEST_CHANNELS;
 | |
|         if (maxChannels == 0) continue;  // skip this device, wrong direction
 | |
| 
 | |
|         if (!allDevices && !isDefault) continue; // skip this device
 | |
| 
 | |
|         printf("\n===========================================================\n");
 | |
|         printf("            Device = %s\n", pdi->name );
 | |
|         printf("===========================================================\n");
 | |
|         for( jc=1; jc<=maxChannels; jc++ )
 | |
|         {
 | |
|             printf("\n---------------------- NumChannels = %d ------------\n", jc );
 | |
|             if (mode == MODE_INPUT) {
 | |
|                 parameters.numInputChannels = jc;
 | |
|             } else {
 | |
|                 parameters.numOutputChannels = jc;
 | |
|             }
 | |
|             /* Try each standard sample rate. */
 | |
|             for( i=0; constStandardSampleRates_[i] > 0; i++ )
 | |
|             {
 | |
|                 parameters.sampleRate = constStandardSampleRates_[i];
 | |
|                 for (int callback = 0; callback < 2; callback++) {
 | |
|                     parameters.useCallback = callback;
 | |
|                     for (int nonInterleaved = 0; nonInterleaved < 2; nonInterleaved++) {
 | |
|                         parameters.useNonInterleaved = nonInterleaved;
 | |
|                         for (int jf = 0; constFormatsToTest_[jf] > 0; jf++) {
 | |
|                             parameters.format = constFormatsToTest_[jf];
 | |
|                             TestSingleStreamParameters(parameters);
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| /*******************************************************************/
 | |
| static void usage( const char *name )
 | |
| {
 | |
|     printf("%s [-a] {-tN}\n", name);
 | |
|     printf("  -a - Test ALL devices, otherwise just the default devices.\n");
 | |
|     printf("  -i - test INPUT only.\n");
 | |
|     printf("  -o - test OUTPUT only.\n");
 | |
|     printf("  -t - Test level, 0=Quick, 1=Normal, 2=Exhaustive\n");
 | |
|     printf("  -? - Help\n");
 | |
| }
 | |
| 
 | |
| /*******************************************************************/
 | |
| int main( int argc, char **argv );
 | |
| int main( int argc, char **argv )
 | |
| {
 | |
|     int     i;
 | |
|     PaError result;
 | |
|     int     allDevices = 0;
 | |
|     int     testOutput = 1;
 | |
|     int     testInput = 1;
 | |
|     int     testLevel = TEST_LEVEL_NORMAL;
 | |
|     char   *executableName = argv[0];
 | |
| 
 | |
|     /* Parse command line parameters. */
 | |
|     i = 1;
 | |
|     while( i<argc )
 | |
|     {
 | |
|         char *arg = argv[i];
 | |
|         if( arg[0] == '-' )
 | |
|         {
 | |
|             switch(arg[1])
 | |
|             {
 | |
|                 case 'a':
 | |
|                     allDevices = 1;
 | |
|                     break;
 | |
|                 case 'i':
 | |
|                     testOutput = 0;
 | |
|                     break;
 | |
|                 case 'o':
 | |
|                     testInput = 0;
 | |
|                     break;
 | |
|                 case 't':
 | |
|                     testLevel = atoi(&arg[2]);
 | |
|                     break;
 | |
| 
 | |
|                 default:
 | |
|                     printf("Illegal option: %s\n", arg);
 | |
|                 case '?':
 | |
|                     usage( executableName );
 | |
|                     exit(1);
 | |
|                     break;
 | |
|             }
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             printf("Illegal argument: %s\n", arg);
 | |
|             usage( executableName );
 | |
|             return 1;
 | |
|         }
 | |
|         i += 1;
 | |
|     }
 | |
| 
 | |
|     ASSERT_EQ(2, sizeof(short)); /* The callback assumes we have 16-bit shorts. */
 | |
|     ASSERT_EQ(4, sizeof(int)); /* The callback assumes we have 32-bit ints. */
 | |
|     ASSERT_EQ(paNoError, (result=Pa_Initialize()));
 | |
| 
 | |
|     if (testLevel == TEST_LEVEL_QUICK) {
 | |
|         printf("\n---- Quick Test ---------------\n");
 | |
|         RunQuickTest();
 | |
|     } else {
 | |
|         if( testInput )
 | |
|         {
 | |
|             printf("\n---- Test INPUT ---------------\n");
 | |
|             if (testLevel == TEST_LEVEL_NORMAL) {
 | |
|                 TestNormal( MODE_INPUT, allDevices );
 | |
|             } else {
 | |
|                 TestExhaustive( MODE_INPUT, allDevices );
 | |
|             }
 | |
|         }
 | |
|         if( testOutput )
 | |
|         {
 | |
|             printf("\n---- Test OUTPUT ---------------\n");
 | |
|             if (testLevel == TEST_LEVEL_NORMAL) {
 | |
|                 TestNormal( MODE_OUTPUT, allDevices );
 | |
|             } else {
 | |
|                 TestExhaustive( MODE_OUTPUT, allDevices );
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
| error:
 | |
|     ASSERT_EQ(paNoError, Pa_Terminate());
 | |
| 
 | |
|     PAQA_PRINT_RESULT;
 | |
|     return PAQA_EXIT_RESULT;
 | |
| }
 |