From: Ross Bencina Date: Thu, 21 Jan 2021 13:00:40 +0000 (+1100) Subject: Clean up whitespace in qa/ in preparation for .editorconfig. (#418) X-Git-Tag: v19.7.0~40 X-Git-Url: https://andrewgundersen.net/repos?a=commitdiff_plain;h=0b832f5ff155e3b9f505d458aaf3483729d3f97b;p=portaudio Clean up whitespace in qa/ in preparation for .editorconfig. (#418) * Clean up whitespace in qa/ in preparation for .editorconfig. Convert tabs to 4 spaces. Indent by 4 spaces. Strip trailing whitespace. Ensure EOL at EOF. --- diff --git a/qa/loopback/src/audio_analyzer.c b/qa/loopback/src/audio_analyzer.c index 0a3dbdf..ddbd32c 100644 --- a/qa/loopback/src/audio_analyzer.c +++ b/qa/loopback/src/audio_analyzer.c @@ -26,13 +26,13 @@ */ /* - * The text above constitutes the entire PortAudio license; however, + * 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 + * they can be incorporated into the canonical version. It is also + * requested that these non-binding requests be included along with the * license above. */ @@ -50,153 +50,153 @@ /*==========================================================================================*/ double PaQa_GetNthFrequency( double baseFrequency, int index ) { - // Use 13 tone equal tempered scale because it does not generate harmonic ratios. - return baseFrequency * pow( 2.0, index / 13.0 ); + // Use 13 tone equal tempered scale because it does not generate harmonic ratios. + return baseFrequency * pow( 2.0, index / 13.0 ); } /*==========================================================================================*/ void PaQa_EraseBuffer( float *buffer, int numFrames, int samplesPerFrame ) { - int i; - int numSamples = numFrames * samplesPerFrame; - for( i=0; iphase = 0.0; - generator->amplitude = amplitude; - generator->frequency = frequency; - generator->phaseIncrement = 2.0 * frequency * MATH_PI / frameRate; + generator->phase = 0.0; + generator->amplitude = amplitude; + generator->frequency = frequency; + generator->phaseIncrement = 2.0 * frequency * MATH_PI / frameRate; } /*==========================================================================================*/ void PaQa_MixSine( PaQaSineGenerator *generator, float *buffer, int numSamples, int stride ) { - int i; - for( i=0; iphase ) * generator->amplitude; - *buffer += value; // Mix with existing value. - buffer += stride; - // Advance phase and wrap around. - generator->phase += generator->phaseIncrement; - if (generator->phase > MATH_TWO_PI) - { - generator->phase -= MATH_TWO_PI; - } - } + int i; + for( i=0; iphase ) * generator->amplitude; + *buffer += value; // Mix with existing value. + buffer += stride; + // Advance phase and wrap around. + generator->phase += generator->phaseIncrement; + if (generator->phase > MATH_TWO_PI) + { + generator->phase -= MATH_TWO_PI; + } + } } /*==========================================================================================*/ void PaQa_GenerateCrackDISABLED( float *buffer, int numSamples, int stride ) { - int i; - int offset = numSamples/2; - for( i=0; ibuffer = (float*)malloc(numBytes); - QA_ASSERT_TRUE( "Allocate recording buffer.", (recording->buffer != NULL) ); - recording->maxFrames = maxFrames; recording->sampleRate = frameRate; - recording->numFrames = 0; - return 0; + int numBytes = maxFrames * sizeof(float); + recording->buffer = (float*)malloc(numBytes); + QA_ASSERT_TRUE( "Allocate recording buffer.", (recording->buffer != NULL) ); + recording->maxFrames = maxFrames; recording->sampleRate = frameRate; + recording->numFrames = 0; + return 0; error: - return 1; + return 1; } /*==========================================================================================*/ void PaQa_TerminateRecording( PaQaRecording *recording ) -{ - if (recording->buffer != NULL) - { - free( recording->buffer ); - recording->buffer = NULL; - } - recording->maxFrames = 0; +{ + if (recording->buffer != NULL) + { + free( recording->buffer ); + recording->buffer = NULL; + } + recording->maxFrames = 0; } /*==========================================================================================*/ int PaQa_WriteRecording( PaQaRecording *recording, float *buffer, int numFrames, int stride ) { - int i; - int framesToWrite; + int i; + int framesToWrite; float *data = &recording->buffer[recording->numFrames]; framesToWrite = numFrames; - if ((framesToWrite + recording->numFrames) > recording->maxFrames) - { - framesToWrite = recording->maxFrames - recording->numFrames; - } - - for( i=0; inumFrames += framesToWrite; - return (recording->numFrames >= recording->maxFrames); + if ((framesToWrite + recording->numFrames) > recording->maxFrames) + { + framesToWrite = recording->maxFrames - recording->numFrames; + } + + for( i=0; inumFrames += framesToWrite; + return (recording->numFrames >= recording->maxFrames); } /*==========================================================================================*/ int PaQa_WriteSilence( PaQaRecording *recording, int numFrames ) { - int i; - int framesToRecord; + int i; + int framesToRecord; float *data = &recording->buffer[recording->numFrames]; framesToRecord = numFrames; - if ((framesToRecord + recording->numFrames) > recording->maxFrames) - { - framesToRecord = recording->maxFrames - recording->numFrames; - } - - for( i=0; inumFrames += framesToRecord; - return (recording->numFrames >= recording->maxFrames); + if ((framesToRecord + recording->numFrames) > recording->maxFrames) + { + framesToRecord = recording->maxFrames - recording->numFrames; + } + + for( i=0; inumFrames += framesToRecord; + return (recording->numFrames >= recording->maxFrames); } /*==========================================================================================*/ int PaQa_RecordFreeze( PaQaRecording *recording, int numFrames ) { - int i; - int framesToRecord; + int i; + int framesToRecord; float *data = &recording->buffer[recording->numFrames]; framesToRecord = numFrames; - if ((framesToRecord + recording->numFrames) > recording->maxFrames) - { - framesToRecord = recording->maxFrames - recording->numFrames; - } - - for( i=0; inumFrames += framesToRecord; - return (recording->numFrames >= recording->maxFrames); + if ((framesToRecord + recording->numFrames) > recording->maxFrames) + { + framesToRecord = recording->maxFrames - recording->numFrames; + } + + for( i=0; inumFrames += framesToRecord; + return (recording->numFrames >= recording->maxFrames); } /*==========================================================================================*/ @@ -209,37 +209,37 @@ int PaQa_SaveRecordingToWaveFile( PaQaRecording *recording, const char *filename int result = 0; #define NUM_SAMPLES (200) short data[NUM_SAMPLES]; - const int samplesPerFrame = 1; + const int samplesPerFrame = 1; int numLeft = recording->numFrames; - float *buffer = &recording->buffer[0]; + float *buffer = &recording->buffer[0]; result = Audio_WAV_OpenWriter( &writer, filename, recording->sampleRate, samplesPerFrame ); if( result < 0 ) goto error; - - while( numLeft > 0 ) + + while( numLeft > 0 ) { - int i; + int i; int numToSave = (numLeft > NUM_SAMPLES) ? NUM_SAMPLES : numLeft; - // Convert double samples to shorts. - for( i=0; i 32767 ) ival = 32767; - else if( ival < -32768 ) ival = -32768; - data[i] = ival; - } - result = Audio_WAV_WriteShorts( &writer, data, numToSave ); + // Convert double samples to shorts. + for( i=0; i 32767 ) ival = 32767; + else if( ival < -32768 ) ival = -32768; + data[i] = ival; + } + result = Audio_WAV_WriteShorts( &writer, data, numToSave ); if( result < 0 ) goto error; - numLeft -= numToSave; + numLeft -= numToSave; } - + result = Audio_WAV_CloseWriter( &writer ); if( result < 0 ) goto error; - + return 0; - + error: printf("ERROR: result = %d\n", result ); return result; @@ -250,29 +250,29 @@ error: double PaQa_MeasureCrossingSlope( float *buffer, int numFrames ) { - int i; - double slopeTotal = 0.0; - int slopeCount = 0; - float previous; - double averageSlope = 0.0; - - previous = buffer[0]; - for( i=1; i 0.0) && (previous < 0.0) ) - { - double delta = current - previous; - slopeTotal += delta; - slopeCount += 1; - } - previous = current; - } - if( slopeCount > 0 ) - { - averageSlope = slopeTotal / slopeCount; - } - return averageSlope; + int i; + double slopeTotal = 0.0; + int slopeCount = 0; + float previous; + double averageSlope = 0.0; + + previous = buffer[0]; + for( i=1; i 0.0) && (previous < 0.0) ) + { + double delta = current - previous; + slopeTotal += delta; + slopeCount += 1; + } + previous = current; + } + if( slopeCount > 0 ) + { + averageSlope = slopeTotal / slopeCount; + } + return averageSlope; } /*==========================================================================================*/ @@ -285,63 +285,63 @@ double PaQa_MeasureCrossingSlope( float *buffer, int numFrames ) * */ double PaQa_MeasureSineAmplitudeBySlope( PaQaRecording *recording, - double frequency, double frameRate, - int startFrame, int numFrames ) + double frequency, double frameRate, + int startFrame, int numFrames ) { - float *buffer = &recording->buffer[startFrame]; - double measuredSlope = PaQa_MeasureCrossingSlope( buffer, numFrames ); - double unitySlope = sin( MATH_TWO_PI * frequency / frameRate ); - double estimatedAmplitude = measuredSlope / unitySlope; - return estimatedAmplitude; + float *buffer = &recording->buffer[startFrame]; + double measuredSlope = PaQa_MeasureCrossingSlope( buffer, numFrames ); + double unitySlope = sin( MATH_TWO_PI * frequency / frameRate ); + double estimatedAmplitude = measuredSlope / unitySlope; + return estimatedAmplitude; } /*==========================================================================================*/ double PaQa_CorrelateSine( PaQaRecording *recording, double frequency, double frameRate, - int startFrame, int numFrames, double *phasePtr ) + int startFrame, int numFrames, double *phasePtr ) { - double magnitude = 0.0; + double magnitude = 0.0; int numLeft = numFrames; - double phase = 0.0; - double phaseIncrement = 2.0 * MATH_PI * frequency / frameRate; - double sinAccumulator = 0.0; - double cosAccumulator = 0.0; - float *data = &recording->buffer[startFrame]; + double phase = 0.0; + double phaseIncrement = 2.0 * MATH_PI * frequency / frameRate; + double sinAccumulator = 0.0; + double cosAccumulator = 0.0; + float *data = &recording->buffer[startFrame]; QA_ASSERT_TRUE( "startFrame out of bounds", (startFrame < recording->numFrames) ); - QA_ASSERT_TRUE( "numFrames out of bounds", ((startFrame+numFrames) <= recording->numFrames) ); - - while( numLeft > 0 ) - { - double sample = (double) *data++; - sinAccumulator += sample * sin( phase ); - cosAccumulator += sample * cos( phase ); - phase += phaseIncrement; - if (phase > MATH_TWO_PI) - { - phase -= MATH_TWO_PI; - } - numLeft -= 1; - } - sinAccumulator = sinAccumulator / numFrames; - cosAccumulator = cosAccumulator / numFrames; - // TODO Why do I have to multiply by 2.0? Need it to make result come out right. - magnitude = 2.0 * sqrt( (sinAccumulator * sinAccumulator) + (cosAccumulator * cosAccumulator )); - if( phasePtr != NULL ) - { - double phase = atan2( cosAccumulator, sinAccumulator ); - *phasePtr = phase; - } - return magnitude; + QA_ASSERT_TRUE( "numFrames out of bounds", ((startFrame+numFrames) <= recording->numFrames) ); + + while( numLeft > 0 ) + { + double sample = (double) *data++; + sinAccumulator += sample * sin( phase ); + cosAccumulator += sample * cos( phase ); + phase += phaseIncrement; + if (phase > MATH_TWO_PI) + { + phase -= MATH_TWO_PI; + } + numLeft -= 1; + } + sinAccumulator = sinAccumulator / numFrames; + cosAccumulator = cosAccumulator / numFrames; + // TODO Why do I have to multiply by 2.0? Need it to make result come out right. + magnitude = 2.0 * sqrt( (sinAccumulator * sinAccumulator) + (cosAccumulator * cosAccumulator )); + if( phasePtr != NULL ) + { + double phase = atan2( cosAccumulator, sinAccumulator ); + *phasePtr = phase; + } + return magnitude; error: - return -1.0; + return -1.0; } /*==========================================================================================*/ void PaQa_FilterRecording( PaQaRecording *input, PaQaRecording *output, BiquadFilter *filter ) { - int numToFilter = (input->numFrames > output->maxFrames) ? output->maxFrames : input->numFrames; - BiquadFilter_Filter( filter, &input->buffer[0], &output->buffer[0], numToFilter ); - output->numFrames = numToFilter; + int numToFilter = (input->numFrames > output->maxFrames) ? output->maxFrames : input->numFrames; + BiquadFilter_Filter( filter, &input->buffer[0], &output->buffer[0], numToFilter ); + output->numFrames = numToFilter; } /*==========================================================================================*/ @@ -351,92 +351,92 @@ void PaQa_FilterRecording( PaQaRecording *input, PaQaRecording *output, BiquadFi */ double PaQa_FindFirstMatch( PaQaRecording *recording, float *buffer, int numFrames, double threshold ) { - int ic,is; - // How many buffers will fit in the recording? - int maxCorrelations = recording->numFrames - numFrames; - double maxSum = 0.0; - int peakIndex = -1; - double inverseMaxSum = 0.0; - int inversePeakIndex = -1; - double location = -1.0; + int ic,is; + // How many buffers will fit in the recording? + int maxCorrelations = recording->numFrames - numFrames; + double maxSum = 0.0; + int peakIndex = -1; + double inverseMaxSum = 0.0; + int inversePeakIndex = -1; + double location = -1.0; QA_ASSERT_TRUE( "numFrames out of bounds", (numFrames < recording->numFrames) ); - for( ic=0; icbuffer[ ic ]; - for( is=0; is maxSum) ) - { - maxSum = sum; - peakIndex = ic; - } - if( ((-sum) > inverseMaxSum) ) - { - inverseMaxSum = -sum; - inversePeakIndex = ic; - } - pastPeak = (maxSum > threshold) && (sum < 0.5*maxSum); - inversePastPeak = (inverseMaxSum > threshold) && ((-sum) < 0.5*inverseMaxSum); - //printf("PaQa_FindFirstMatch: ic = %4d, sum = %8f, maxSum = %8f, inverseMaxSum = %8f\n", ic, sum, maxSum, inverseMaxSum ); - if( pastPeak && inversePastPeak ) - { - if( maxSum > inverseMaxSum ) - { - location = peakIndex; - } - else - { - location = inversePeakIndex; - } - break; - } - - } - //printf("PaQa_FindFirstMatch: location = %4d\n", (int)location ); - return location; + for( ic=0; icbuffer[ ic ]; + for( is=0; is maxSum) ) + { + maxSum = sum; + peakIndex = ic; + } + if( ((-sum) > inverseMaxSum) ) + { + inverseMaxSum = -sum; + inversePeakIndex = ic; + } + pastPeak = (maxSum > threshold) && (sum < 0.5*maxSum); + inversePastPeak = (inverseMaxSum > threshold) && ((-sum) < 0.5*inverseMaxSum); + //printf("PaQa_FindFirstMatch: ic = %4d, sum = %8f, maxSum = %8f, inverseMaxSum = %8f\n", ic, sum, maxSum, inverseMaxSum ); + if( pastPeak && inversePastPeak ) + { + if( maxSum > inverseMaxSum ) + { + location = peakIndex; + } + else + { + location = inversePeakIndex; + } + break; + } + + } + //printf("PaQa_FindFirstMatch: location = %4d\n", (int)location ); + return location; error: - return -1.0; + return -1.0; } /*==========================================================================================*/ // Measure the area under the curve by summing absolute value of each value. double PaQa_MeasureArea( float *buffer, int numFrames, int stride ) { - int is; - double area = 0.0; - for( is=0; isnumFrames) ); + QA_ASSERT_TRUE( "startAt+numFrames out of bounds", ((startAt+numFrames) < recording->numFrames) ); { - double recordedArea = PaQa_MeasureArea( &recording->buffer[startAt], numFrames, 1 ); - double bufferArea = PaQa_MeasureArea( buffer, numFrames, 1 ); - if( bufferArea == 0.0 ) return 100000000.0; - return recordedArea / bufferArea; + double recordedArea = PaQa_MeasureArea( &recording->buffer[startAt], numFrames, 1 ); + double bufferArea = PaQa_MeasureArea( buffer, numFrames, 1 ); + if( bufferArea == 0.0 ) return 100000000.0; + return recordedArea / bufferArea; } error: - return -1.0; + return -1.0; } /*==========================================================================================*/ double PaQa_ComputePhaseDifference( double phase1, double phase2 ) { - double delta = phase1 - phase2; - while( delta > MATH_PI ) - { - delta -= MATH_TWO_PI; - } - while( delta < -MATH_PI ) - { - delta += MATH_TWO_PI; - } - return delta; + double delta = phase1 - phase2; + while( delta > MATH_PI ) + { + delta -= MATH_TWO_PI; + } + while( delta < -MATH_PI ) + { + delta += MATH_TWO_PI; + } + return delta; } /*==========================================================================================*/ int PaQa_MeasureLatency( PaQaRecording *recording, PaQaTestTone *testTone, PaQaAnalysisResult *analysisResult ) { - double threshold; - PaQaSineGenerator generator; + double threshold; + PaQaSineGenerator generator; #define MAX_BUFFER_SIZE 2048 - float buffer[MAX_BUFFER_SIZE]; - double period = testTone->sampleRate / testTone->frequency; - int cycleSize = (int) (period + 0.5); - //printf("PaQa_AnalyseRecording: frequency = %8f, frameRate = %8f, period = %8f, cycleSize = %8d\n", - // testTone->frequency, testTone->sampleRate, period, cycleSize ); - analysisResult->latency = -1; - analysisResult->valid = (0); - - // Set up generator to find matching first cycle. - QA_ASSERT_TRUE( "cycleSize out of bounds", (cycleSize < MAX_BUFFER_SIZE) ); - PaQa_SetupSineGenerator( &generator, testTone->frequency, testTone->amplitude, testTone->sampleRate ); - PaQa_EraseBuffer( buffer, cycleSize, testTone->samplesPerFrame ); - PaQa_MixSine( &generator, buffer, cycleSize, testTone->samplesPerFrame ); - - threshold = cycleSize * 0.02; - analysisResult->latency = PaQa_FindFirstMatch( recording, buffer, cycleSize, threshold ); - QA_ASSERT_TRUE( "Could not find the start of the signal.", (analysisResult->latency >= 0) ); - analysisResult->amplitudeRatio = PaQa_CompareAmplitudes( recording, analysisResult->latency, buffer, cycleSize ); - return 0; + float buffer[MAX_BUFFER_SIZE]; + double period = testTone->sampleRate / testTone->frequency; + int cycleSize = (int) (period + 0.5); + //printf("PaQa_AnalyseRecording: frequency = %8f, frameRate = %8f, period = %8f, cycleSize = %8d\n", + // testTone->frequency, testTone->sampleRate, period, cycleSize ); + analysisResult->latency = -1; + analysisResult->valid = (0); + + // Set up generator to find matching first cycle. + QA_ASSERT_TRUE( "cycleSize out of bounds", (cycleSize < MAX_BUFFER_SIZE) ); + PaQa_SetupSineGenerator( &generator, testTone->frequency, testTone->amplitude, testTone->sampleRate ); + PaQa_EraseBuffer( buffer, cycleSize, testTone->samplesPerFrame ); + PaQa_MixSine( &generator, buffer, cycleSize, testTone->samplesPerFrame ); + + threshold = cycleSize * 0.02; + analysisResult->latency = PaQa_FindFirstMatch( recording, buffer, cycleSize, threshold ); + QA_ASSERT_TRUE( "Could not find the start of the signal.", (analysisResult->latency >= 0) ); + analysisResult->amplitudeRatio = PaQa_CompareAmplitudes( recording, analysisResult->latency, buffer, cycleSize ); + return 0; error: - return -1; + return -1; } /*==========================================================================================*/ // Apply cosine squared window. void PaQa_FadeInRecording( PaQaRecording *recording, int startFrame, int count ) { - int is; - double phase = 0.5 * MATH_PI; - // Advance a quarter wave - double phaseIncrement = 0.25 * 2.0 * MATH_PI / count; - + int is; + double phase = 0.5 * MATH_PI; + // Advance a quarter wave + double phaseIncrement = 0.25 * 2.0 * MATH_PI / count; + assert( startFrame >= 0 ); - assert( count > 0 ); - + assert( count > 0 ); + /* Zero out initial part of the recording. */ - for( is=0; isbuffer[ is ] = 0.0f; } /* Fade in where signal begins. */ for( is=0; isbuffer[ is + startFrame ]; - float y = x * w; - //printf("FADE %d : w=%f, x=%f, y=%f\n", is, w, x, y ); - recording->buffer[ is + startFrame ] = y; + double c = cos( phase ); + double w = c * c; + float x = recording->buffer[ is + startFrame ]; + float y = x * w; + //printf("FADE %d : w=%f, x=%f, y=%f\n", is, w, x, y ); + recording->buffer[ is + startFrame ] = y; phase += phaseIncrement; - } + } } @@ -539,146 +539,146 @@ void PaQa_FadeInRecording( PaQaRecording *recording, int startFrame, int count ) /** Apply notch filter and high pass filter then detect remaining energy. */ int PaQa_DetectPop( PaQaRecording *recording, PaQaTestTone *testTone, PaQaAnalysisResult *analysisResult ) -{ +{ int result = 0; - int i; + int i; double maxAmplitude; - int maxPosition; - - PaQaRecording notchOutput = { 0 }; - BiquadFilter notchFilter; - - PaQaRecording hipassOutput = { 0 }; - BiquadFilter hipassFilter; - - int frameRate = (int) recording->sampleRate; - - analysisResult->popPosition = -1; - analysisResult->popAmplitude = 0.0; - - result = PaQa_InitializeRecording( ¬chOutput, recording->numFrames, frameRate ); - QA_ASSERT_EQUALS( "PaQa_InitializeRecording failed", 0, result ); - - result = PaQa_InitializeRecording( &hipassOutput, recording->numFrames, frameRate ); - QA_ASSERT_EQUALS( "PaQa_InitializeRecording failed", 0, result ); - - // Use notch filter to remove test tone. - BiquadFilter_SetupNotch( ¬chFilter, testTone->frequency / frameRate, 0.5 ); - PaQa_FilterRecording( recording, ¬chOutput, ¬chFilter ); - //result = PaQa_SaveRecordingToWaveFile( ¬chOutput, "notch_output.wav" ); - //QA_ASSERT_EQUALS( "PaQa_SaveRecordingToWaveFile failed", 0, result ); - - // Apply fade-in window. - PaQa_FadeInRecording( ¬chOutput, (int) analysisResult->latency, 500 ); - - // Use high pass to accentuate the edges of a pop. At higher frequency! - BiquadFilter_SetupHighPass( &hipassFilter, 2.0 * testTone->frequency / frameRate, 0.5 ); - PaQa_FilterRecording( ¬chOutput, &hipassOutput, &hipassFilter ); - //result = PaQa_SaveRecordingToWaveFile( &hipassOutput, "hipass_output.wav" ); - //QA_ASSERT_EQUALS( "PaQa_SaveRecordingToWaveFile failed", 0, result ); - - // Scan remaining signal looking for peak. - maxAmplitude = 0.0; - maxPosition = -1; - for( i=(int) analysisResult->latency; i maxAmplitude ) - { - maxAmplitude = mag; - maxPosition = i; - } - } - - if( maxAmplitude > PAQA_POP_THRESHOLD ) - { - analysisResult->popPosition = maxPosition; - analysisResult->popAmplitude = maxAmplitude; - } - - PaQa_TerminateRecording( ¬chOutput ); - PaQa_TerminateRecording( &hipassOutput ); - return 0; - + int maxPosition; + + PaQaRecording notchOutput = { 0 }; + BiquadFilter notchFilter; + + PaQaRecording hipassOutput = { 0 }; + BiquadFilter hipassFilter; + + int frameRate = (int) recording->sampleRate; + + analysisResult->popPosition = -1; + analysisResult->popAmplitude = 0.0; + + result = PaQa_InitializeRecording( ¬chOutput, recording->numFrames, frameRate ); + QA_ASSERT_EQUALS( "PaQa_InitializeRecording failed", 0, result ); + + result = PaQa_InitializeRecording( &hipassOutput, recording->numFrames, frameRate ); + QA_ASSERT_EQUALS( "PaQa_InitializeRecording failed", 0, result ); + + // Use notch filter to remove test tone. + BiquadFilter_SetupNotch( ¬chFilter, testTone->frequency / frameRate, 0.5 ); + PaQa_FilterRecording( recording, ¬chOutput, ¬chFilter ); + //result = PaQa_SaveRecordingToWaveFile( ¬chOutput, "notch_output.wav" ); + //QA_ASSERT_EQUALS( "PaQa_SaveRecordingToWaveFile failed", 0, result ); + + // Apply fade-in window. + PaQa_FadeInRecording( ¬chOutput, (int) analysisResult->latency, 500 ); + + // Use high pass to accentuate the edges of a pop. At higher frequency! + BiquadFilter_SetupHighPass( &hipassFilter, 2.0 * testTone->frequency / frameRate, 0.5 ); + PaQa_FilterRecording( ¬chOutput, &hipassOutput, &hipassFilter ); + //result = PaQa_SaveRecordingToWaveFile( &hipassOutput, "hipass_output.wav" ); + //QA_ASSERT_EQUALS( "PaQa_SaveRecordingToWaveFile failed", 0, result ); + + // Scan remaining signal looking for peak. + maxAmplitude = 0.0; + maxPosition = -1; + for( i=(int) analysisResult->latency; i maxAmplitude ) + { + maxAmplitude = mag; + maxPosition = i; + } + } + + if( maxAmplitude > PAQA_POP_THRESHOLD ) + { + analysisResult->popPosition = maxPosition; + analysisResult->popAmplitude = maxAmplitude; + } + + PaQa_TerminateRecording( ¬chOutput ); + PaQa_TerminateRecording( &hipassOutput ); + return 0; + error: - PaQa_TerminateRecording( ¬chOutput ); - PaQa_TerminateRecording( &hipassOutput ); - return -1; + PaQa_TerminateRecording( ¬chOutput ); + PaQa_TerminateRecording( &hipassOutput ); + return -1; } /*==========================================================================================*/ int PaQa_DetectPhaseError( PaQaRecording *recording, PaQaTestTone *testTone, PaQaAnalysisResult *analysisResult ) -{ - int i; - double period = testTone->sampleRate / testTone->frequency; - int cycleSize = (int) (period + 0.5); - - double maxAddedFrames = 0.0; - double maxDroppedFrames = 0.0; - - double previousPhase = 0.0; - double previousFrameError = 0; - int loopCount = 0; - int skip = cycleSize; - int windowSize = cycleSize; +{ + int i; + double period = testTone->sampleRate / testTone->frequency; + int cycleSize = (int) (period + 0.5); + + double maxAddedFrames = 0.0; + double maxDroppedFrames = 0.0; + + double previousPhase = 0.0; + double previousFrameError = 0; + int loopCount = 0; + int skip = cycleSize; + int windowSize = cycleSize; // Scan recording starting with first cycle, looking for phase errors. - analysisResult->numDroppedFrames = 0.0; - analysisResult->numAddedFrames = 0.0; - analysisResult->droppedFramesPosition = -1.0; - analysisResult->addedFramesPosition = -1.0; - - for( i=analysisResult->latency; i<(recording->numFrames - windowSize); i += skip ) - { - double expectedPhase = previousPhase + (skip * MATH_TWO_PI / period); - double expectedPhaseIncrement = PaQa_ComputePhaseDifference( expectedPhase, previousPhase ); - - double phase = 666.0; - double mag = PaQa_CorrelateSine( recording, testTone->frequency, testTone->sampleRate, i, windowSize, &phase ); - if( (loopCount > 1) && (mag > 0.0) ) - { - double phaseDelta = PaQa_ComputePhaseDifference( phase, previousPhase ); - double phaseError = PaQa_ComputePhaseDifference( phaseDelta, expectedPhaseIncrement ); - // Convert phaseError to equivalent number of frames. - double frameError = period * phaseError / MATH_TWO_PI; - double consecutiveFrameError = frameError + previousFrameError; -// if( fabs(frameError) > 0.01 ) -// { -// printf("FFFFFFFFFFFFF frameError = %f, at %d\n", frameError, i ); -// } - if( consecutiveFrameError > 0.8 ) - { - double droppedFrames = consecutiveFrameError; - if (droppedFrames > (maxDroppedFrames * 1.001)) - { - analysisResult->numDroppedFrames = droppedFrames; - analysisResult->droppedFramesPosition = i + (windowSize/2); - maxDroppedFrames = droppedFrames; - } - } - else if( consecutiveFrameError < -0.8 ) - { - double addedFrames = 0 - consecutiveFrameError; - if (addedFrames > (maxAddedFrames * 1.001)) - { - analysisResult->numAddedFrames = addedFrames; - analysisResult->addedFramesPosition = i + (windowSize/2); - maxAddedFrames = addedFrames; - } - } - previousFrameError = frameError; - - - //if( i<8000 ) - //{ - // printf("%d: phase = %8f, expected = %8f, delta = %8f, frameError = %8f\n", i, phase, expectedPhaseIncrement, phaseDelta, frameError ); - //} - } - previousPhase = phase; - loopCount += 1; - } - return 0; + analysisResult->numDroppedFrames = 0.0; + analysisResult->numAddedFrames = 0.0; + analysisResult->droppedFramesPosition = -1.0; + analysisResult->addedFramesPosition = -1.0; + + for( i=analysisResult->latency; i<(recording->numFrames - windowSize); i += skip ) + { + double expectedPhase = previousPhase + (skip * MATH_TWO_PI / period); + double expectedPhaseIncrement = PaQa_ComputePhaseDifference( expectedPhase, previousPhase ); + + double phase = 666.0; + double mag = PaQa_CorrelateSine( recording, testTone->frequency, testTone->sampleRate, i, windowSize, &phase ); + if( (loopCount > 1) && (mag > 0.0) ) + { + double phaseDelta = PaQa_ComputePhaseDifference( phase, previousPhase ); + double phaseError = PaQa_ComputePhaseDifference( phaseDelta, expectedPhaseIncrement ); + // Convert phaseError to equivalent number of frames. + double frameError = period * phaseError / MATH_TWO_PI; + double consecutiveFrameError = frameError + previousFrameError; +// if( fabs(frameError) > 0.01 ) +// { +// printf("FFFFFFFFFFFFF frameError = %f, at %d\n", frameError, i ); +// } + if( consecutiveFrameError > 0.8 ) + { + double droppedFrames = consecutiveFrameError; + if (droppedFrames > (maxDroppedFrames * 1.001)) + { + analysisResult->numDroppedFrames = droppedFrames; + analysisResult->droppedFramesPosition = i + (windowSize/2); + maxDroppedFrames = droppedFrames; + } + } + else if( consecutiveFrameError < -0.8 ) + { + double addedFrames = 0 - consecutiveFrameError; + if (addedFrames > (maxAddedFrames * 1.001)) + { + analysisResult->numAddedFrames = addedFrames; + analysisResult->addedFramesPosition = i + (windowSize/2); + maxAddedFrames = addedFrames; + } + } + previousFrameError = frameError; + + + //if( i<8000 ) + //{ + // printf("%d: phase = %8f, expected = %8f, delta = %8f, frameError = %8f\n", i, phase, expectedPhaseIncrement, phaseDelta, frameError ); + //} + } + previousPhase = phase; + loopCount += 1; + } + return 0; } /*==========================================================================================*/ @@ -686,22 +686,22 @@ int PaQa_AnalyseRecording( PaQaRecording *recording, PaQaTestTone *testTone, PaQ { int result = 0; - memset( analysisResult, 0, sizeof(PaQaAnalysisResult) ); - result = PaQa_MeasureLatency( recording, testTone, analysisResult ); + memset( analysisResult, 0, sizeof(PaQaAnalysisResult) ); + result = PaQa_MeasureLatency( recording, testTone, analysisResult ); QA_ASSERT_EQUALS( "latency measurement", 0, result ); - - if( (analysisResult->latency >= 0) && (analysisResult->amplitudeRatio > 0.1) ) - { - analysisResult->valid = (1); - - result = PaQa_DetectPop( recording, testTone, analysisResult ); - QA_ASSERT_EQUALS( "detect pop", 0, result ); - - result = PaQa_DetectPhaseError( recording, testTone, analysisResult ); - QA_ASSERT_EQUALS( "detect phase error", 0, result ); - } - return 0; + + if( (analysisResult->latency >= 0) && (analysisResult->amplitudeRatio > 0.1) ) + { + analysisResult->valid = (1); + + result = PaQa_DetectPop( recording, testTone, analysisResult ); + QA_ASSERT_EQUALS( "detect pop", 0, result ); + + result = PaQa_DetectPhaseError( recording, testTone, analysisResult ); + QA_ASSERT_EQUALS( "detect phase error", 0, result ); + } + return 0; error: - return -1; + return -1; } diff --git a/qa/loopback/src/audio_analyzer.h b/qa/loopback/src/audio_analyzer.h index c98acbb..8d9f1ee 100644 --- a/qa/loopback/src/audio_analyzer.h +++ b/qa/loopback/src/audio_analyzer.h @@ -26,13 +26,13 @@ */ /* - * The text above constitutes the entire PortAudio license; however, + * 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 + * they can be incorporated into the canonical version. It is also + * requested that these non-binding requests be included along with the * license above. */ @@ -46,44 +46,44 @@ typedef struct PaQaSineGenerator_s { - double phase; - double phaseIncrement; - double frequency; - double amplitude; + double phase; + double phaseIncrement; + double frequency; + double amplitude; } PaQaSineGenerator; -/** Container for a monophonic audio sample in memory. */ +/** Container for a monophonic audio sample in memory. */ typedef struct PaQaRecording_s { - /** Maximum number of frames that can fit in the allocated buffer. */ - int maxFrames; - float *buffer; - /** Actual number of valid frames in the buffer. */ - int numFrames; - int sampleRate; + /** Maximum number of frames that can fit in the allocated buffer. */ + int maxFrames; + float *buffer; + /** Actual number of valid frames in the buffer. */ + int numFrames; + int sampleRate; } PaQaRecording; typedef struct PaQaTestTone_s { - int samplesPerFrame; - int startDelay; - double sampleRate; - double frequency; - double amplitude; + int samplesPerFrame; + int startDelay; + double sampleRate; + double frequency; + double amplitude; } PaQaTestTone; typedef struct PaQaAnalysisResult_s { - int valid; - /** Latency in samples from output to input. */ - double latency; - double amplitudeRatio; - double popAmplitude; - double popPosition; - double numDroppedFrames; - double droppedFramesPosition; - double numAddedFrames; - double addedFramesPosition; + int valid; + /** Latency in samples from output to input. */ + double latency; + double amplitudeRatio; + double popAmplitude; + double popPosition; + double numDroppedFrames; + double droppedFramesPosition; + double numAddedFrames; + double addedFramesPosition; } PaQaAnalysisResult; @@ -101,7 +101,7 @@ void PaQa_EraseBuffer( float *buffer, int numFrames, int samplesPerFrame ); void PaQa_MixSine( PaQaSineGenerator *generator, float *buffer, int numSamples, int stride ); void PaQa_WriteSine( float *buffer, int numSamples, int stride, - double frequency, double amplitude ); + double frequency, double amplitude ); /** * Generate a signal with a sharp edge in the middle that can be recognized despite some phase shift. @@ -133,12 +133,12 @@ void PaQa_SetupSineGenerator( PaQaSineGenerator *generator, double frequency, do * Allocate memory for containing a mono audio signal. Set up recording for writing. */ int PaQa_InitializeRecording( PaQaRecording *recording, int maxSamples, int sampleRate ); - + /** * Free memory allocated by PaQa_InitializeRecording. */ void PaQa_TerminateRecording( PaQaRecording *recording ); - + /** * Apply a biquad filter to the audio from the input recording and write it to the output recording. */ @@ -146,29 +146,29 @@ void PaQa_FilterRecording( PaQaRecording *input, PaQaRecording *output, BiquadFi int PaQa_SaveRecordingToWaveFile( PaQaRecording *recording, const char *filename ); - + /** * @param stride is the spacing of samples to skip in the input buffer. To use every samples pass 1. To use every other sample pass 2. */ int PaQa_WriteRecording( PaQaRecording *recording, float *buffer, int numSamples, int stride ); - + /** Write zeros into a recording. */ int PaQa_WriteSilence( PaQaRecording *recording, int numSamples ); - + int PaQa_RecordFreeze( PaQaRecording *recording, int numSamples ); double PaQa_CorrelateSine( PaQaRecording *recording, double frequency, double frameRate, - int startFrame, int numSamples, double *phasePtr ); + int startFrame, int numSamples, double *phasePtr ); double PaQa_FindFirstMatch( PaQaRecording *recording, float *buffer, int numSamples, double tolerance ); -/** +/** * Estimate the original amplitude of a clipped sine wave by measuring * its average slope at the zero crossings. */ double PaQa_MeasureSineAmplitudeBySlope( PaQaRecording *recording, - double frequency, double frameRate, - int startFrame, int numFrames ); + double frequency, double frameRate, + int startFrame, int numFrames ); double PaQa_MeasureRootMeanSquare( float *buffer, int numFrames ); diff --git a/qa/loopback/src/biquad_filter.c b/qa/loopback/src/biquad_filter.c index 1bc4a23..1715fa3 100755 --- a/qa/loopback/src/biquad_filter.c +++ b/qa/loopback/src/biquad_filter.c @@ -4,9 +4,10 @@ #include "biquad_filter.h" /** - * Unit_BiquadFilter implements a second order IIR filter. - Here is the equation that we use for this filter: - y(n) = a0*x(n) + a1*x(n-1) + a2*x(n-2) - b1*y(n-1) - b2*y(n-2) + * Unit_BiquadFilter implements a second order IIR filter. + * + * Here is the equation that we use for this filter: + * y(n) = a0*x(n) + a1*x(n-1) + a2*x(n-2) - b1*y(n-1) - b2*y(n-2) * * @author (C) 2002 Phil Burk, SoftSynth.com, All Rights Reserved */ @@ -17,17 +18,17 @@ */ static void BiquadFilter_CalculateCommon( BiquadFilter *filter, double ratio, double Q ) { - double omega; - - memset( filter, 0, sizeof(BiquadFilter) ); + double omega; + + memset( filter, 0, sizeof(BiquadFilter) ); /* Don't let frequency get too close to Nyquist or filter will blow up. */ - if( ratio >= 0.499 ) ratio = 0.499; - omega = 2.0 * (double)FILTER_PI * ratio; + if( ratio >= 0.499 ) ratio = 0.499; + omega = 2.0 * (double)FILTER_PI * ratio; - filter->cos_omega = (double) cos( omega ); - filter->sin_omega = (double) sin( omega ); - filter->alpha = filter->sin_omega / (2.0 * Q); + filter->cos_omega = (double) cos( omega ); + filter->sin_omega = (double) sin( omega ); + filter->alpha = filter->sin_omega / (2.0 * Q); } /********************************************************************************* @@ -35,21 +36,21 @@ static void BiquadFilter_CalculateCommon( BiquadFilter *filter, double ratio, do */ void BiquadFilter_SetupHighPass( BiquadFilter *filter, double ratio, double Q ) { - double scalar, opc; - - if( ratio < BIQUAD_MIN_RATIO ) ratio = BIQUAD_MIN_RATIO; - if( Q < BIQUAD_MIN_Q ) Q = BIQUAD_MIN_Q; - - BiquadFilter_CalculateCommon( filter, ratio, Q ); - - scalar = 1.0 / (1.0 + filter->alpha); - opc = (1.0 + filter->cos_omega); - - filter->a0 = opc * 0.5 * scalar; - filter->a1 = - opc * scalar; + double scalar, opc; + + if( ratio < BIQUAD_MIN_RATIO ) ratio = BIQUAD_MIN_RATIO; + if( Q < BIQUAD_MIN_Q ) Q = BIQUAD_MIN_Q; + + BiquadFilter_CalculateCommon( filter, ratio, Q ); + + scalar = 1.0 / (1.0 + filter->alpha); + opc = (1.0 + filter->cos_omega); + + filter->a0 = opc * 0.5 * scalar; + filter->a1 = - opc * scalar; filter->a2 = filter->a0; - filter->b1 = -2.0 * filter->cos_omega * scalar; - filter->b2 = (1.0 - filter->alpha) * scalar; + filter->b1 = -2.0 * filter->cos_omega * scalar; + filter->b2 = (1.0 - filter->alpha) * scalar; } @@ -58,21 +59,21 @@ void BiquadFilter_SetupHighPass( BiquadFilter *filter, double ratio, double Q ) */ void BiquadFilter_SetupNotch( BiquadFilter *filter, double ratio, double Q ) { - double scalar, opc; - - if( ratio < BIQUAD_MIN_RATIO ) ratio = BIQUAD_MIN_RATIO; - if( Q < BIQUAD_MIN_Q ) Q = BIQUAD_MIN_Q; - - BiquadFilter_CalculateCommon( filter, ratio, Q ); - - scalar = 1.0 / (1.0 + filter->alpha); - opc = (1.0 + filter->cos_omega); - - filter->a0 = scalar; - filter->a1 = -2.0 * filter->cos_omega * scalar; + double scalar, opc; + + if( ratio < BIQUAD_MIN_RATIO ) ratio = BIQUAD_MIN_RATIO; + if( Q < BIQUAD_MIN_Q ) Q = BIQUAD_MIN_Q; + + BiquadFilter_CalculateCommon( filter, ratio, Q ); + + scalar = 1.0 / (1.0 + filter->alpha); + opc = (1.0 + filter->cos_omega); + + filter->a0 = scalar; + filter->a1 = -2.0 * filter->cos_omega * scalar; filter->a2 = filter->a0; - filter->b1 = filter->a1; - filter->b2 = (1.0 - filter->alpha) * scalar; + filter->b1 = filter->a1; + filter->b2 = (1.0 - filter->alpha) * scalar; } /***************************************************************** @@ -80,43 +81,43 @@ void BiquadFilter_SetupNotch( BiquadFilter *filter, double ratio, double Q ) */ void BiquadFilter_Filter( BiquadFilter *filter, float *inputs, float *outputs, int numSamples ) { - int i; + int i; double xn, yn; - // Pull values from structure to speed up the calculation. - double a0 = filter->a0; - double a1 = filter->a1; - double a2 = filter->a2; - double b1 = filter->b1; - double b2 = filter->b2; - double xn1 = filter->xn1; - double xn2 = filter->xn2; - double yn1 = filter->yn1; - double yn2 = filter->yn2; - - for( i=0; ia0; + double a1 = filter->a1; + double a2 = filter->a2; + double b1 = filter->b1; + double b2 = filter->b2; + double xn1 = filter->xn1; + double xn2 = filter->xn2; + double yn1 = filter->yn1; + double yn2 = filter->yn2; + + for( i=0; ixn1 = xn1; - filter->xn2 = xn2; - filter->yn1 = yn1; - filter->yn2 = yn2; + + if( (i & 7) == 0 ) + { + // Apply a small bipolar impulse to filter to prevent arithmetic underflow. + // Underflows can cause the FPU to interrupt the CPU. + yn1 += (double) 1.0E-26; + yn2 -= (double) 1.0E-26; + } + } + + filter->xn1 = xn1; + filter->xn2 = xn2; + filter->yn1 = yn1; + filter->yn2 = yn2; } diff --git a/qa/loopback/src/biquad_filter.h b/qa/loopback/src/biquad_filter.h index cbac688..0895aba 100755 --- a/qa/loopback/src/biquad_filter.h +++ b/qa/loopback/src/biquad_filter.h @@ -3,7 +3,7 @@ /** - * Unit_BiquadFilter implements a second order IIR filter. + * Unit_BiquadFilter implements a second order IIR filter. * * @author (C) 2002 Phil Burk, SoftSynth.com, All Rights Reserved */ @@ -14,20 +14,20 @@ typedef struct BiquadFilter_s { double xn1; // storage for delayed signals - double xn2; - double yn1; - double yn2; + double xn2; + double yn1; + double yn2; - double a0; // coefficients - double a1; - double a2; + double a0; // coefficients + double a1; + double a2; - double b1; - double b2; + double b1; + double b2; - double cos_omega; - double sin_omega; - double alpha; + double cos_omega; + double sin_omega; + double alpha; } BiquadFilter; void BiquadFilter_SetupHighPass( BiquadFilter *filter, double ratio, double Q ); diff --git a/qa/loopback/src/paqa.c b/qa/loopback/src/paqa.c index ac8e841..5eb6283 100644 --- a/qa/loopback/src/paqa.c +++ b/qa/loopback/src/paqa.c @@ -26,13 +26,13 @@ */ /* - * The text above constitutes the entire PortAudio license; however, + * 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 + * they can be incorporated into the canonical version. It is also + * requested that these non-binding requests be included along with the * license above. */ @@ -69,76 +69,76 @@ int g_testsFailed = 0; const char * s_FlagOnNames[] = { - "Two Streams (Half Duplex)", - "Blocking Read/Write" + "Two Streams (Half Duplex)", + "Blocking Read/Write" }; const char * s_FlagOffNames[] = { - "One Stream (Full Duplex)", - "Callback" + "One Stream (Full Duplex)", + "Callback" }; /** Parameters that describe a single test run. */ typedef struct TestParameters_s { - PaStreamParameters inputParameters; - PaStreamParameters outputParameters; - double sampleRate; - int samplesPerFrame; - int framesPerBuffer; - int maxFrames; - double baseFrequency; - double amplitude; + PaStreamParameters inputParameters; + PaStreamParameters outputParameters; + double sampleRate; + int samplesPerFrame; + int framesPerBuffer; + int maxFrames; + double baseFrequency; + double amplitude; PaStreamFlags streamFlags; // paClipOff, etc - int flags; // PAQA_FLAG_TWO_STREAMS, PAQA_FLAG_USE_BLOCKING_IO + int flags; // PAQA_FLAG_TWO_STREAMS, PAQA_FLAG_USE_BLOCKING_IO } TestParameters; typedef struct LoopbackContext_s { - // Generate a unique signal on each channel. - PaQaSineGenerator generators[MAX_NUM_GENERATORS]; - // Record each channel individually. - PaQaRecording recordings[MAX_NUM_RECORDINGS]; - - // Reported by the stream after it's opened - PaTime streamInfoInputLatency; - PaTime streamInfoOutputLatency; - - // Measured at runtime. - volatile int callbackCount; // incremented for each callback - volatile int inputBufferCount; // incremented if input buffer not NULL - int inputUnderflowCount; - int inputOverflowCount; - - volatile int outputBufferCount; // incremented if output buffer not NULL - int outputOverflowCount; - int outputUnderflowCount; - + // Generate a unique signal on each channel. + PaQaSineGenerator generators[MAX_NUM_GENERATORS]; + // Record each channel individually. + PaQaRecording recordings[MAX_NUM_RECORDINGS]; + + // Reported by the stream after it's opened + PaTime streamInfoInputLatency; + PaTime streamInfoOutputLatency; + + // Measured at runtime. + volatile int callbackCount; // incremented for each callback + volatile int inputBufferCount; // incremented if input buffer not NULL + int inputUnderflowCount; + int inputOverflowCount; + + volatile int outputBufferCount; // incremented if output buffer not NULL + int outputOverflowCount; + int outputUnderflowCount; + // Measure whether input or output is lagging behind. volatile int minInputOutputDelta; volatile int maxInputOutputDelta; - - int minFramesPerBuffer; - int maxFramesPerBuffer; - int primingCount; - TestParameters *test; - volatile int done; + + int minFramesPerBuffer; + int maxFramesPerBuffer; + int primingCount; + TestParameters *test; + volatile int done; } LoopbackContext; typedef struct UserOptions_s { - int sampleRate; - int framesPerBuffer; - int inputLatency; - int outputLatency; - int saveBadWaves; - int verbose; - int waveFileCount; - const char *waveFilePath; - PaDeviceIndex inputDevice; - PaDeviceIndex outputDevice; + int sampleRate; + int framesPerBuffer; + int inputLatency; + int outputLatency; + int saveBadWaves; + int verbose; + int waveFileCount; + const char *waveFilePath; + PaDeviceIndex inputDevice; + PaDeviceIndex outputDevice; } UserOptions; #define BIG_BUFFER_SIZE (sizeof(float) * 2 * 2 * 1024) @@ -150,105 +150,105 @@ static unsigned char g_ConversionBuffer[CONVERSION_BUFFER_SIZE]; /*******************************************************************/ static int RecordAndPlaySinesCallback( const void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo* timeInfo, - PaStreamCallbackFlags statusFlags, - void *userData ) + unsigned long framesPerBuffer, + const PaStreamCallbackTimeInfo* timeInfo, + PaStreamCallbackFlags statusFlags, + void *userData ) { - int i; - LoopbackContext *loopbackContext = (LoopbackContext *) userData; - - - loopbackContext->callbackCount += 1; - if( statusFlags & paInputUnderflow ) loopbackContext->inputUnderflowCount += 1; - if( statusFlags & paInputOverflow ) loopbackContext->inputOverflowCount += 1; - if( statusFlags & paOutputUnderflow ) loopbackContext->outputUnderflowCount += 1; - if( statusFlags & paOutputOverflow ) loopbackContext->outputOverflowCount += 1; - if( statusFlags & paPrimingOutput ) loopbackContext->primingCount += 1; - if( framesPerBuffer > loopbackContext->maxFramesPerBuffer ) - { - loopbackContext->maxFramesPerBuffer = framesPerBuffer; - } - if( framesPerBuffer < loopbackContext->minFramesPerBuffer ) - { - loopbackContext->minFramesPerBuffer = framesPerBuffer; - } - + int i; + LoopbackContext *loopbackContext = (LoopbackContext *) userData; + + + loopbackContext->callbackCount += 1; + if( statusFlags & paInputUnderflow ) loopbackContext->inputUnderflowCount += 1; + if( statusFlags & paInputOverflow ) loopbackContext->inputOverflowCount += 1; + if( statusFlags & paOutputUnderflow ) loopbackContext->outputUnderflowCount += 1; + if( statusFlags & paOutputOverflow ) loopbackContext->outputOverflowCount += 1; + if( statusFlags & paPrimingOutput ) loopbackContext->primingCount += 1; + if( framesPerBuffer > loopbackContext->maxFramesPerBuffer ) + { + loopbackContext->maxFramesPerBuffer = framesPerBuffer; + } + if( framesPerBuffer < loopbackContext->minFramesPerBuffer ) + { + loopbackContext->minFramesPerBuffer = framesPerBuffer; + } + /* This may get called with NULL inputBuffer during initial setup. - * We may also use the same callback with output only streams. - */ - if( inputBuffer != NULL) - { - int channelsPerFrame = loopbackContext->test->inputParameters.channelCount; - float *in = (float *)inputBuffer; - PaSampleFormat inFormat = loopbackContext->test->inputParameters.sampleFormat; - - loopbackContext->inputBufferCount += 1; - - if( inFormat != paFloat32 ) - { - int samplesToConvert = framesPerBuffer * channelsPerFrame; - in = (float *) g_ConversionBuffer; - if( samplesToConvert > MAX_CONVERSION_SAMPLES ) - { - // Hack to prevent buffer overflow. - // @todo Loop with small buffer instead of failing. - printf("Format conversion buffer too small!\n"); - return paComplete; - } - PaQa_ConvertToFloat( inputBuffer, samplesToConvert, inFormat, (float *) g_ConversionBuffer ); - } - - // Read each channel from the buffer. - for( i=0; idone |= PaQa_WriteRecording( &loopbackContext->recordings[i], - in + i, - framesPerBuffer, - channelsPerFrame ); - } - } - - if( outputBuffer != NULL ) - { - int channelsPerFrame = loopbackContext->test->outputParameters.channelCount; - float *out = (float *)outputBuffer; - PaSampleFormat outFormat = loopbackContext->test->outputParameters.sampleFormat; - - loopbackContext->outputBufferCount += 1; - - if( outFormat != paFloat32 ) - { - // If we need to convert then mix to the g_ConversionBuffer and then convert into the PA outputBuffer. - out = (float *) g_ConversionBuffer; - } - - PaQa_EraseBuffer( out, framesPerBuffer, channelsPerFrame ); - for( i=0; igenerators[i], - out + i, - framesPerBuffer, - channelsPerFrame ); - } - - if( outFormat != paFloat32 ) - { - int samplesToConvert = framesPerBuffer * channelsPerFrame; - if( samplesToConvert > MAX_CONVERSION_SAMPLES ) - { - printf("Format conversion buffer too small!\n"); - return paComplete; - } - PaQa_ConvertFromFloat( out, framesPerBuffer * channelsPerFrame, outFormat, outputBuffer ); - } - - } - + * We may also use the same callback with output only streams. + */ + if( inputBuffer != NULL) + { + int channelsPerFrame = loopbackContext->test->inputParameters.channelCount; + float *in = (float *)inputBuffer; + PaSampleFormat inFormat = loopbackContext->test->inputParameters.sampleFormat; + + loopbackContext->inputBufferCount += 1; + + if( inFormat != paFloat32 ) + { + int samplesToConvert = framesPerBuffer * channelsPerFrame; + in = (float *) g_ConversionBuffer; + if( samplesToConvert > MAX_CONVERSION_SAMPLES ) + { + // Hack to prevent buffer overflow. + // @todo Loop with small buffer instead of failing. + printf("Format conversion buffer too small!\n"); + return paComplete; + } + PaQa_ConvertToFloat( inputBuffer, samplesToConvert, inFormat, (float *) g_ConversionBuffer ); + } + + // Read each channel from the buffer. + for( i=0; idone |= PaQa_WriteRecording( &loopbackContext->recordings[i], + in + i, + framesPerBuffer, + channelsPerFrame ); + } + } + + if( outputBuffer != NULL ) + { + int channelsPerFrame = loopbackContext->test->outputParameters.channelCount; + float *out = (float *)outputBuffer; + PaSampleFormat outFormat = loopbackContext->test->outputParameters.sampleFormat; + + loopbackContext->outputBufferCount += 1; + + if( outFormat != paFloat32 ) + { + // If we need to convert then mix to the g_ConversionBuffer and then convert into the PA outputBuffer. + out = (float *) g_ConversionBuffer; + } + + PaQa_EraseBuffer( out, framesPerBuffer, channelsPerFrame ); + for( i=0; igenerators[i], + out + i, + framesPerBuffer, + channelsPerFrame ); + } + + if( outFormat != paFloat32 ) + { + int samplesToConvert = framesPerBuffer * channelsPerFrame; + if( samplesToConvert > MAX_CONVERSION_SAMPLES ) + { + printf("Format conversion buffer too small!\n"); + return paComplete; + } + PaQa_ConvertFromFloat( out, framesPerBuffer * channelsPerFrame, outFormat, outputBuffer ); + } + + } + // Measure whether the input or output are lagging behind. // Don't measure lag at end. if( !loopbackContext->done ) - { + { int inputOutputDelta = loopbackContext->inputBufferCount - loopbackContext->outputBufferCount; if( loopbackContext->maxInputOutputDelta < inputOutputDelta ) { @@ -259,21 +259,21 @@ static int RecordAndPlaySinesCallback( const void *inputBuffer, void *outputBuff loopbackContext->minInputOutputDelta = inputOutputDelta; } } - - return loopbackContext->done ? paComplete : paContinue; + + return loopbackContext->done ? paComplete : paContinue; } static void CopyStreamInfoToLoopbackContext( LoopbackContext *loopbackContext, PaStream *inputStream, PaStream *outputStream ) { - const PaStreamInfo *inputStreamInfo = Pa_GetStreamInfo( inputStream ); - const PaStreamInfo *outputStreamInfo = Pa_GetStreamInfo( outputStream ); + const PaStreamInfo *inputStreamInfo = Pa_GetStreamInfo( inputStream ); + const PaStreamInfo *outputStreamInfo = Pa_GetStreamInfo( outputStream ); - loopbackContext->streamInfoInputLatency = inputStreamInfo ? inputStreamInfo->inputLatency : -1; - loopbackContext->streamInfoOutputLatency = outputStreamInfo ? outputStreamInfo->outputLatency : -1; + loopbackContext->streamInfoInputLatency = inputStreamInfo ? inputStreamInfo->inputLatency : -1; + loopbackContext->streamInfoOutputLatency = outputStreamInfo ? outputStreamInfo->outputLatency : -1; } /*******************************************************************/ -/** +/** * Open a full duplex audio stream. * Generate sine waves on the output channels and record the input channels. * Then close the stream. @@ -281,47 +281,47 @@ static void CopyStreamInfoToLoopbackContext( LoopbackContext *loopbackContext, P */ int PaQa_RunLoopbackFullDuplex( LoopbackContext *loopbackContext ) { - PaStream *stream = NULL; - PaError err = 0; - TestParameters *test = loopbackContext->test; - loopbackContext->done = 0; - // Use one full duplex stream. - err = Pa_OpenStream( - &stream, - &test->inputParameters, - &test->outputParameters, - test->sampleRate, - test->framesPerBuffer, - paClipOff, /* we won't output out of range samples so don't bother clipping them */ - RecordAndPlaySinesCallback, - loopbackContext ); - if( err != paNoError ) goto error; - - CopyStreamInfoToLoopbackContext( loopbackContext, stream, stream ); - - err = Pa_StartStream( stream ); - if( err != paNoError ) goto error; - - // Wait for stream to finish. - while( loopbackContext->done == 0 ) - { - Pa_Sleep(PAQA_WAIT_STREAM_MSEC); - } - - err = Pa_StopStream( stream ); - if( err != paNoError ) goto error; - - err = Pa_CloseStream( stream ); - if( err != paNoError ) goto error; - - return 0; - + PaStream *stream = NULL; + PaError err = 0; + TestParameters *test = loopbackContext->test; + loopbackContext->done = 0; + // Use one full duplex stream. + err = Pa_OpenStream( + &stream, + &test->inputParameters, + &test->outputParameters, + test->sampleRate, + test->framesPerBuffer, + paClipOff, /* we won't output out of range samples so don't bother clipping them */ + RecordAndPlaySinesCallback, + loopbackContext ); + if( err != paNoError ) goto error; + + CopyStreamInfoToLoopbackContext( loopbackContext, stream, stream ); + + err = Pa_StartStream( stream ); + if( err != paNoError ) goto error; + + // Wait for stream to finish. + while( loopbackContext->done == 0 ) + { + Pa_Sleep(PAQA_WAIT_STREAM_MSEC); + } + + err = Pa_StopStream( stream ); + if( err != paNoError ) goto error; + + err = Pa_CloseStream( stream ); + if( err != paNoError ) goto error; + + return 0; + error: - return err; + return err; } /*******************************************************************/ -/** +/** * Open two audio streams, one for input and one for output. * Generate sine waves on the output channels and record the input channels. * Then close the stream. @@ -330,25 +330,25 @@ error: int PaQa_WaitForStream( LoopbackContext *loopbackContext ) { - int timeoutMSec = 1000 * PAQA_TEST_DURATION * 2; - - // Wait for stream to finish or timeout. - while( (loopbackContext->done == 0) && (timeoutMSec > 0) ) - { - Pa_Sleep(PAQA_WAIT_STREAM_MSEC); - timeoutMSec -= PAQA_WAIT_STREAM_MSEC; - } - - if( loopbackContext->done == 0 ) - { - printf("ERROR - stream completion timed out!"); - return paTimedOut; - } - return 0; + int timeoutMSec = 1000 * PAQA_TEST_DURATION * 2; + + // Wait for stream to finish or timeout. + while( (loopbackContext->done == 0) && (timeoutMSec > 0) ) + { + Pa_Sleep(PAQA_WAIT_STREAM_MSEC); + timeoutMSec -= PAQA_WAIT_STREAM_MSEC; + } + + if( loopbackContext->done == 0 ) + { + printf("ERROR - stream completion timed out!"); + return paTimedOut; + } + return 0; } /*******************************************************************/ -/** +/** * Open two audio streams, one for input and one for output. * Generate sine waves on the output channels and record the input channels. * Then close the stream. @@ -356,67 +356,67 @@ int PaQa_WaitForStream( LoopbackContext *loopbackContext ) */ int PaQa_RunLoopbackHalfDuplex( LoopbackContext *loopbackContext ) { - PaStream *inStream = NULL; - PaStream *outStream = NULL; - PaError err = 0; - int timedOut = 0; - TestParameters *test = loopbackContext->test; - loopbackContext->done = 0; - - // Use two half duplex streams. - err = Pa_OpenStream( - &inStream, - &test->inputParameters, - NULL, - test->sampleRate, - test->framesPerBuffer, - test->streamFlags, - RecordAndPlaySinesCallback, - loopbackContext ); - if( err != paNoError ) goto error; - err = Pa_OpenStream( - &outStream, - NULL, - &test->outputParameters, - test->sampleRate, - test->framesPerBuffer, - test->streamFlags, - RecordAndPlaySinesCallback, - loopbackContext ); - if( err != paNoError ) goto error; - - CopyStreamInfoToLoopbackContext( loopbackContext, inStream, outStream ); - - err = Pa_StartStream( inStream ); - if( err != paNoError ) goto error; - - // Start output later so we catch the beginning of the waveform. - err = Pa_StartStream( outStream ); - if( err != paNoError ) goto error; - - timedOut = PaQa_WaitForStream( loopbackContext ); - - err = Pa_StopStream( inStream ); - if( err != paNoError ) goto error; - - err = Pa_StopStream( outStream ); - if( err != paNoError ) goto error; - - err = Pa_CloseStream( inStream ); - if( err != paNoError ) goto error; - - err = Pa_CloseStream( outStream ); - if( err != paNoError ) goto error; - - return timedOut; - + PaStream *inStream = NULL; + PaStream *outStream = NULL; + PaError err = 0; + int timedOut = 0; + TestParameters *test = loopbackContext->test; + loopbackContext->done = 0; + + // Use two half duplex streams. + err = Pa_OpenStream( + &inStream, + &test->inputParameters, + NULL, + test->sampleRate, + test->framesPerBuffer, + test->streamFlags, + RecordAndPlaySinesCallback, + loopbackContext ); + if( err != paNoError ) goto error; + err = Pa_OpenStream( + &outStream, + NULL, + &test->outputParameters, + test->sampleRate, + test->framesPerBuffer, + test->streamFlags, + RecordAndPlaySinesCallback, + loopbackContext ); + if( err != paNoError ) goto error; + + CopyStreamInfoToLoopbackContext( loopbackContext, inStream, outStream ); + + err = Pa_StartStream( inStream ); + if( err != paNoError ) goto error; + + // Start output later so we catch the beginning of the waveform. + err = Pa_StartStream( outStream ); + if( err != paNoError ) goto error; + + timedOut = PaQa_WaitForStream( loopbackContext ); + + err = Pa_StopStream( inStream ); + if( err != paNoError ) goto error; + + err = Pa_StopStream( outStream ); + if( err != paNoError ) goto error; + + err = Pa_CloseStream( inStream ); + if( err != paNoError ) goto error; + + err = Pa_CloseStream( outStream ); + if( err != paNoError ) goto error; + + return timedOut; + error: - return err; + return err; } /*******************************************************************/ -/** +/** * Open one audio streams, just for input. * Record background level. * Then close the stream. @@ -424,120 +424,120 @@ error: */ int PaQa_RunInputOnly( LoopbackContext *loopbackContext ) { - PaStream *inStream = NULL; - PaError err = 0; - int timedOut = 0; - TestParameters *test = loopbackContext->test; - loopbackContext->done = 0; - - // Just open an input stream. - err = Pa_OpenStream( - &inStream, - &test->inputParameters, - NULL, - test->sampleRate, - test->framesPerBuffer, - paClipOff, /* We won't output out of range samples so don't bother clipping them. */ - RecordAndPlaySinesCallback, - loopbackContext ); - if( err != paNoError ) goto error; - - err = Pa_StartStream( inStream ); - if( err != paNoError ) goto error; - - timedOut = PaQa_WaitForStream( loopbackContext ); - - err = Pa_StopStream( inStream ); - if( err != paNoError ) goto error; - - err = Pa_CloseStream( inStream ); - if( err != paNoError ) goto error; - - return timedOut; - + PaStream *inStream = NULL; + PaError err = 0; + int timedOut = 0; + TestParameters *test = loopbackContext->test; + loopbackContext->done = 0; + + // Just open an input stream. + err = Pa_OpenStream( + &inStream, + &test->inputParameters, + NULL, + test->sampleRate, + test->framesPerBuffer, + paClipOff, /* We won't output out of range samples so don't bother clipping them. */ + RecordAndPlaySinesCallback, + loopbackContext ); + if( err != paNoError ) goto error; + + err = Pa_StartStream( inStream ); + if( err != paNoError ) goto error; + + timedOut = PaQa_WaitForStream( loopbackContext ); + + err = Pa_StopStream( inStream ); + if( err != paNoError ) goto error; + + err = Pa_CloseStream( inStream ); + if( err != paNoError ) goto error; + + return timedOut; + error: - return err; + return err; } /*******************************************************************/ static int RecordAndPlayBlockingIO( PaStream *inStream, - PaStream *outStream, - LoopbackContext *loopbackContext - ) -{ - int i; - float *in = (float *)g_ReadWriteBuffer; - float *out = (float *)g_ReadWriteBuffer; - PaError err; - int done = 0; - long available; - const long maxPerBuffer = 64; - TestParameters *test = loopbackContext->test; - long framesPerBuffer = test->framesPerBuffer; - if( framesPerBuffer <= 0 ) - { - framesPerBuffer = maxPerBuffer; // bigger values might run past end of recording - } - - // Read in audio. - err = Pa_ReadStream( inStream, in, framesPerBuffer ); - // Ignore an overflow on the first read. - //if( !((loopbackContext->callbackCount == 0) && (err == paInputOverflowed)) ) - if( err != paInputOverflowed ) - { - QA_ASSERT_EQUALS( "Pa_ReadStream failed", paNoError, err ); - } - else - { - loopbackContext->inputOverflowCount += 1; - } - - - // Save in a recording. - for( i=0; itest->inputParameters.channelCount; i++ ) - { - done |= PaQa_WriteRecording( &loopbackContext->recordings[i], - in + i, - framesPerBuffer, - loopbackContext->test->inputParameters.channelCount ); - } - - // Synthesize audio. - available = Pa_GetStreamWriteAvailable( outStream ); - if( available > (2*framesPerBuffer) ) available = (2*framesPerBuffer); - PaQa_EraseBuffer( out, available, loopbackContext->test->outputParameters.channelCount ); - for( i=0; itest->outputParameters.channelCount; i++ ) - { - PaQa_MixSine( &loopbackContext->generators[i], - out + i, - available, - loopbackContext->test->outputParameters.channelCount ); - } - - // Write out audio. - err = Pa_WriteStream( outStream, out, available ); - // Ignore an underflow on the first write. - //if( !((loopbackContext->callbackCount == 0) && (err == paOutputUnderflowed)) ) - if( err != paOutputUnderflowed ) - { - QA_ASSERT_EQUALS( "Pa_WriteStream failed", paNoError, err ); - } - else - { - loopbackContext->outputUnderflowCount += 1; - } - - - loopbackContext->callbackCount += 1; - - return done; + PaStream *outStream, + LoopbackContext *loopbackContext + ) +{ + int i; + float *in = (float *)g_ReadWriteBuffer; + float *out = (float *)g_ReadWriteBuffer; + PaError err; + int done = 0; + long available; + const long maxPerBuffer = 64; + TestParameters *test = loopbackContext->test; + long framesPerBuffer = test->framesPerBuffer; + if( framesPerBuffer <= 0 ) + { + framesPerBuffer = maxPerBuffer; // bigger values might run past end of recording + } + + // Read in audio. + err = Pa_ReadStream( inStream, in, framesPerBuffer ); + // Ignore an overflow on the first read. + //if( !((loopbackContext->callbackCount == 0) && (err == paInputOverflowed)) ) + if( err != paInputOverflowed ) + { + QA_ASSERT_EQUALS( "Pa_ReadStream failed", paNoError, err ); + } + else + { + loopbackContext->inputOverflowCount += 1; + } + + + // Save in a recording. + for( i=0; itest->inputParameters.channelCount; i++ ) + { + done |= PaQa_WriteRecording( &loopbackContext->recordings[i], + in + i, + framesPerBuffer, + loopbackContext->test->inputParameters.channelCount ); + } + + // Synthesize audio. + available = Pa_GetStreamWriteAvailable( outStream ); + if( available > (2*framesPerBuffer) ) available = (2*framesPerBuffer); + PaQa_EraseBuffer( out, available, loopbackContext->test->outputParameters.channelCount ); + for( i=0; itest->outputParameters.channelCount; i++ ) + { + PaQa_MixSine( &loopbackContext->generators[i], + out + i, + available, + loopbackContext->test->outputParameters.channelCount ); + } + + // Write out audio. + err = Pa_WriteStream( outStream, out, available ); + // Ignore an underflow on the first write. + //if( !((loopbackContext->callbackCount == 0) && (err == paOutputUnderflowed)) ) + if( err != paOutputUnderflowed ) + { + QA_ASSERT_EQUALS( "Pa_WriteStream failed", paNoError, err ); + } + else + { + loopbackContext->outputUnderflowCount += 1; + } + + + loopbackContext->callbackCount += 1; + + return done; error: - return err; + return err; } /*******************************************************************/ -/** +/** * Open two audio streams with non-blocking IO. * Generate sine waves on the output channels and record the input channels. * Then close the stream. @@ -545,73 +545,73 @@ error: */ int PaQa_RunLoopbackHalfDuplexBlockingIO( LoopbackContext *loopbackContext ) { - PaStream *inStream = NULL; - PaStream *outStream = NULL; - PaError err = 0; - TestParameters *test = loopbackContext->test; - - // Use two half duplex streams. - err = Pa_OpenStream( - &inStream, - &test->inputParameters, - NULL, - test->sampleRate, - test->framesPerBuffer, - paClipOff, /* we won't output out of range samples so don't bother clipping them */ - NULL, // causes non-blocking IO - NULL ); - if( err != paNoError ) goto error1; - err = Pa_OpenStream( - &outStream, - NULL, - &test->outputParameters, - test->sampleRate, - test->framesPerBuffer, - paClipOff, /* we won't output out of range samples so don't bother clipping them */ - NULL, // causes non-blocking IO - NULL ); - if( err != paNoError ) goto error2; - - CopyStreamInfoToLoopbackContext( loopbackContext, inStream, outStream ); - - err = Pa_StartStream( outStream ); - if( err != paNoError ) goto error3; - - err = Pa_StartStream( inStream ); - if( err != paNoError ) goto error3; - - while( err == 0 ) - { - err = RecordAndPlayBlockingIO( inStream, outStream, loopbackContext ); - if( err < 0 ) goto error3; - } - - err = Pa_StopStream( inStream ); - if( err != paNoError ) goto error3; - - err = Pa_StopStream( outStream ); - if( err != paNoError ) goto error3; - - err = Pa_CloseStream( outStream ); - if( err != paNoError ) goto error2; - - err = Pa_CloseStream( inStream ); - if( err != paNoError ) goto error1; - - - return 0; - + PaStream *inStream = NULL; + PaStream *outStream = NULL; + PaError err = 0; + TestParameters *test = loopbackContext->test; + + // Use two half duplex streams. + err = Pa_OpenStream( + &inStream, + &test->inputParameters, + NULL, + test->sampleRate, + test->framesPerBuffer, + paClipOff, /* we won't output out of range samples so don't bother clipping them */ + NULL, // causes non-blocking IO + NULL ); + if( err != paNoError ) goto error1; + err = Pa_OpenStream( + &outStream, + NULL, + &test->outputParameters, + test->sampleRate, + test->framesPerBuffer, + paClipOff, /* we won't output out of range samples so don't bother clipping them */ + NULL, // causes non-blocking IO + NULL ); + if( err != paNoError ) goto error2; + + CopyStreamInfoToLoopbackContext( loopbackContext, inStream, outStream ); + + err = Pa_StartStream( outStream ); + if( err != paNoError ) goto error3; + + err = Pa_StartStream( inStream ); + if( err != paNoError ) goto error3; + + while( err == 0 ) + { + err = RecordAndPlayBlockingIO( inStream, outStream, loopbackContext ); + if( err < 0 ) goto error3; + } + + err = Pa_StopStream( inStream ); + if( err != paNoError ) goto error3; + + err = Pa_StopStream( outStream ); + if( err != paNoError ) goto error3; + + err = Pa_CloseStream( outStream ); + if( err != paNoError ) goto error2; + + err = Pa_CloseStream( inStream ); + if( err != paNoError ) goto error1; + + + return 0; + error3: - Pa_CloseStream( outStream ); + Pa_CloseStream( outStream ); error2: - Pa_CloseStream( inStream ); + Pa_CloseStream( inStream ); error1: - return err; + return err; } /*******************************************************************/ -/** +/** * Open one audio stream with non-blocking IO. * Generate sine waves on the output channels and record the input channels. * Then close the stream. @@ -619,669 +619,669 @@ error1: */ int PaQa_RunLoopbackFullDuplexBlockingIO( LoopbackContext *loopbackContext ) { - PaStream *stream = NULL; - PaError err = 0; - TestParameters *test = loopbackContext->test; - - // Use one full duplex stream. - err = Pa_OpenStream( - &stream, - &test->inputParameters, - &test->outputParameters, - test->sampleRate, - test->framesPerBuffer, - paClipOff, /* we won't output out of range samples so don't bother clipping them */ - NULL, // causes non-blocking IO - NULL ); - if( err != paNoError ) goto error1; - - CopyStreamInfoToLoopbackContext( loopbackContext, stream, stream ); - - err = Pa_StartStream( stream ); - if( err != paNoError ) goto error2; - - while( err == 0 ) - { - err = RecordAndPlayBlockingIO( stream, stream, loopbackContext ); - if( err < 0 ) goto error2; - } - - err = Pa_StopStream( stream ); - if( err != paNoError ) goto error2; - - - err = Pa_CloseStream( stream ); - if( err != paNoError ) goto error1; - - - return 0; - + PaStream *stream = NULL; + PaError err = 0; + TestParameters *test = loopbackContext->test; + + // Use one full duplex stream. + err = Pa_OpenStream( + &stream, + &test->inputParameters, + &test->outputParameters, + test->sampleRate, + test->framesPerBuffer, + paClipOff, /* we won't output out of range samples so don't bother clipping them */ + NULL, // causes non-blocking IO + NULL ); + if( err != paNoError ) goto error1; + + CopyStreamInfoToLoopbackContext( loopbackContext, stream, stream ); + + err = Pa_StartStream( stream ); + if( err != paNoError ) goto error2; + + while( err == 0 ) + { + err = RecordAndPlayBlockingIO( stream, stream, loopbackContext ); + if( err < 0 ) goto error2; + } + + err = Pa_StopStream( stream ); + if( err != paNoError ) goto error2; + + + err = Pa_CloseStream( stream ); + if( err != paNoError ) goto error1; + + + return 0; + error2: - Pa_CloseStream( stream ); + Pa_CloseStream( stream ); error1: - return err; + return err; } /*******************************************************************/ -/** +/** * Run some kind of loopback test. * @return 0 if OK or negative error. */ int PaQa_RunLoopback( LoopbackContext *loopbackContext ) { - PaError err = 0; - TestParameters *test = loopbackContext->test; - - - if( test->flags & PAQA_FLAG_TWO_STREAMS ) - { - if( test->flags & PAQA_FLAG_USE_BLOCKING_IO ) - { - err = PaQa_RunLoopbackHalfDuplexBlockingIO( loopbackContext ); - } - else - { - err = PaQa_RunLoopbackHalfDuplex( loopbackContext ); - } - } - else - { - if( test->flags & PAQA_FLAG_USE_BLOCKING_IO ) - { - err = PaQa_RunLoopbackFullDuplexBlockingIO( loopbackContext ); - } - else - { - err = PaQa_RunLoopbackFullDuplex( loopbackContext ); - } - } - - if( err != paNoError ) - { - printf("PortAudio error = %s\n", Pa_GetErrorText( err ) ); - } - return err; + PaError err = 0; + TestParameters *test = loopbackContext->test; + + + if( test->flags & PAQA_FLAG_TWO_STREAMS ) + { + if( test->flags & PAQA_FLAG_USE_BLOCKING_IO ) + { + err = PaQa_RunLoopbackHalfDuplexBlockingIO( loopbackContext ); + } + else + { + err = PaQa_RunLoopbackHalfDuplex( loopbackContext ); + } + } + else + { + if( test->flags & PAQA_FLAG_USE_BLOCKING_IO ) + { + err = PaQa_RunLoopbackFullDuplexBlockingIO( loopbackContext ); + } + else + { + err = PaQa_RunLoopbackFullDuplex( loopbackContext ); + } + } + + if( err != paNoError ) + { + printf("PortAudio error = %s\n", Pa_GetErrorText( err ) ); + } + return err; } /*******************************************************************/ static int PaQa_SaveTestResultToWaveFile( UserOptions *userOptions, PaQaRecording *recording ) { - if( userOptions->saveBadWaves ) - { - char filename[256]; + if( userOptions->saveBadWaves ) + { + char filename[256]; #ifdef WIN32 _snprintf( filename, sizeof(filename), "%s\\paloopback_%d.wav", userOptions->waveFilePath, userOptions->waveFileCount++ ); #else - snprintf( filename, sizeof(filename), "%s/paloopback_%d.wav", userOptions->waveFilePath, userOptions->waveFileCount++ ); -#endif - printf( "\"%s\", ", filename ); - return PaQa_SaveRecordingToWaveFile( recording, filename ); - } - return 0; + snprintf( filename, sizeof(filename), "%s/paloopback_%d.wav", userOptions->waveFilePath, userOptions->waveFileCount++ ); +#endif + printf( "\"%s\", ", filename ); + return PaQa_SaveRecordingToWaveFile( recording, filename ); + } + return 0; } /*******************************************************************/ static int PaQa_SetupLoopbackContext( LoopbackContext *loopbackContextPtr, TestParameters *testParams ) { - int i; - // Setup loopback context. - memset( loopbackContextPtr, 0, sizeof(LoopbackContext) ); - loopbackContextPtr->test = testParams; - for( i=0; isamplesPerFrame; i++ ) - { - int err = PaQa_InitializeRecording( &loopbackContextPtr->recordings[i], testParams->maxFrames, testParams->sampleRate ); - QA_ASSERT_EQUALS( "PaQa_InitializeRecording failed", paNoError, err ); - } - for( i=0; isamplesPerFrame; i++ ) - { - PaQa_SetupSineGenerator( &loopbackContextPtr->generators[i], PaQa_GetNthFrequency( testParams->baseFrequency, i ), - testParams->amplitude, testParams->sampleRate ); - } - loopbackContextPtr->minFramesPerBuffer = 0x0FFFFFFF; - return 0; + int i; + // Setup loopback context. + memset( loopbackContextPtr, 0, sizeof(LoopbackContext) ); + loopbackContextPtr->test = testParams; + for( i=0; isamplesPerFrame; i++ ) + { + int err = PaQa_InitializeRecording( &loopbackContextPtr->recordings[i], testParams->maxFrames, testParams->sampleRate ); + QA_ASSERT_EQUALS( "PaQa_InitializeRecording failed", paNoError, err ); + } + for( i=0; isamplesPerFrame; i++ ) + { + PaQa_SetupSineGenerator( &loopbackContextPtr->generators[i], PaQa_GetNthFrequency( testParams->baseFrequency, i ), + testParams->amplitude, testParams->sampleRate ); + } + loopbackContextPtr->minFramesPerBuffer = 0x0FFFFFFF; + return 0; error: - return -1; + return -1; } /*******************************************************************/ static void PaQa_TeardownLoopbackContext( LoopbackContext *loopbackContextPtr ) { - int i; - if( loopbackContextPtr->test != NULL ) - { - for( i=0; itest->samplesPerFrame; i++ ) - { - PaQa_TerminateRecording( &loopbackContextPtr->recordings[i] ); - } - } + int i; + if( loopbackContextPtr->test != NULL ) + { + for( i=0; itest->samplesPerFrame; i++ ) + { + PaQa_TerminateRecording( &loopbackContextPtr->recordings[i] ); + } + } } /*******************************************************************/ static void PaQa_PrintShortErrorReport( PaQaAnalysisResult *analysisResultPtr, int channel ) { - printf("channel %d ", channel); - if( analysisResultPtr->popPosition > 0 ) - { - printf("POP %0.3f at %d, ", (double)analysisResultPtr->popAmplitude, (int)analysisResultPtr->popPosition ); - } - else - { - if( analysisResultPtr->addedFramesPosition > 0 ) - { - printf("ADD %d at %d ", (int)analysisResultPtr->numAddedFrames, (int)analysisResultPtr->addedFramesPosition ); - } - - if( analysisResultPtr->droppedFramesPosition > 0 ) - { - printf("DROP %d at %d ", (int)analysisResultPtr->numDroppedFrames, (int)analysisResultPtr->droppedFramesPosition ); - } - } + printf("channel %d ", channel); + if( analysisResultPtr->popPosition > 0 ) + { + printf("POP %0.3f at %d, ", (double)analysisResultPtr->popAmplitude, (int)analysisResultPtr->popPosition ); + } + else + { + if( analysisResultPtr->addedFramesPosition > 0 ) + { + printf("ADD %d at %d ", (int)analysisResultPtr->numAddedFrames, (int)analysisResultPtr->addedFramesPosition ); + } + + if( analysisResultPtr->droppedFramesPosition > 0 ) + { + printf("DROP %d at %d ", (int)analysisResultPtr->numDroppedFrames, (int)analysisResultPtr->droppedFramesPosition ); + } + } } /*******************************************************************/ static void PaQa_PrintFullErrorReport( PaQaAnalysisResult *analysisResultPtr, int channel ) { - printf("\n=== Loopback Analysis ===================\n"); - printf(" channel: %d\n", channel ); - printf(" latency: %10.3f\n", analysisResultPtr->latency ); - printf(" amplitudeRatio: %10.3f\n", (double)analysisResultPtr->amplitudeRatio ); - printf(" popPosition: %10.3f\n", (double)analysisResultPtr->popPosition ); - printf(" popAmplitude: %10.3f\n", (double)analysisResultPtr->popAmplitude ); - printf(" num added frames: %10.3f\n", analysisResultPtr->numAddedFrames ); - printf(" added frames at: %10.3f\n", analysisResultPtr->addedFramesPosition ); - printf(" num dropped frames: %10.3f\n", analysisResultPtr->numDroppedFrames ); - printf(" dropped frames at: %10.3f\n", analysisResultPtr->droppedFramesPosition ); + printf("\n=== Loopback Analysis ===================\n"); + printf(" channel: %d\n", channel ); + printf(" latency: %10.3f\n", analysisResultPtr->latency ); + printf(" amplitudeRatio: %10.3f\n", (double)analysisResultPtr->amplitudeRatio ); + printf(" popPosition: %10.3f\n", (double)analysisResultPtr->popPosition ); + printf(" popAmplitude: %10.3f\n", (double)analysisResultPtr->popAmplitude ); + printf(" num added frames: %10.3f\n", analysisResultPtr->numAddedFrames ); + printf(" added frames at: %10.3f\n", analysisResultPtr->addedFramesPosition ); + printf(" num dropped frames: %10.3f\n", analysisResultPtr->numDroppedFrames ); + printf(" dropped frames at: %10.3f\n", analysisResultPtr->droppedFramesPosition ); } /*******************************************************************/ -/** +/** * Test loopback connection using the given parameters. * @return number of channels with glitches, or negative error. */ static int PaQa_SingleLoopBackTest( UserOptions *userOptions, TestParameters *testParams ) { - int i; - LoopbackContext loopbackContext; - PaError err = paNoError; - PaQaTestTone testTone; - PaQaAnalysisResult analysisResult; - int numBadChannels = 0; - - printf("| %5d | %6d | ", ((int)(testParams->sampleRate+0.5)), testParams->framesPerBuffer ); - fflush(stdout); - - testTone.samplesPerFrame = testParams->samplesPerFrame; - testTone.sampleRate = testParams->sampleRate; - testTone.amplitude = testParams->amplitude; - testTone.startDelay = 0; - - err = PaQa_SetupLoopbackContext( &loopbackContext, testParams ); - if( err ) return err; - - err = PaQa_RunLoopback( &loopbackContext ); - QA_ASSERT_TRUE("loopback did not run", (loopbackContext.callbackCount > 1) ); - - printf( "%7.2f %7.2f %7.2f | ", - loopbackContext.streamInfoInputLatency * 1000.0, - loopbackContext.streamInfoOutputLatency * 1000.0, - (loopbackContext.streamInfoInputLatency + loopbackContext.streamInfoOutputLatency) * 1000.0 - ); - - printf( "%4d/%4d/%4d, %4d/%4d/%4d | ", - loopbackContext.inputOverflowCount, - loopbackContext.inputUnderflowCount, - loopbackContext.inputBufferCount, - loopbackContext.outputOverflowCount, - loopbackContext.outputUnderflowCount, - loopbackContext.outputBufferCount - ); - - // Analyse recording to detect glitches. - for( i=0; isamplesPerFrame; i++ ) - { - double freq = PaQa_GetNthFrequency( testParams->baseFrequency, i ); - testTone.frequency = freq; - - PaQa_AnalyseRecording( &loopbackContext.recordings[i], &testTone, &analysisResult ); - - if( i==0 ) - { + int i; + LoopbackContext loopbackContext; + PaError err = paNoError; + PaQaTestTone testTone; + PaQaAnalysisResult analysisResult; + int numBadChannels = 0; + + printf("| %5d | %6d | ", ((int)(testParams->sampleRate+0.5)), testParams->framesPerBuffer ); + fflush(stdout); + + testTone.samplesPerFrame = testParams->samplesPerFrame; + testTone.sampleRate = testParams->sampleRate; + testTone.amplitude = testParams->amplitude; + testTone.startDelay = 0; + + err = PaQa_SetupLoopbackContext( &loopbackContext, testParams ); + if( err ) return err; + + err = PaQa_RunLoopback( &loopbackContext ); + QA_ASSERT_TRUE("loopback did not run", (loopbackContext.callbackCount > 1) ); + + printf( "%7.2f %7.2f %7.2f | ", + loopbackContext.streamInfoInputLatency * 1000.0, + loopbackContext.streamInfoOutputLatency * 1000.0, + (loopbackContext.streamInfoInputLatency + loopbackContext.streamInfoOutputLatency) * 1000.0 + ); + + printf( "%4d/%4d/%4d, %4d/%4d/%4d | ", + loopbackContext.inputOverflowCount, + loopbackContext.inputUnderflowCount, + loopbackContext.inputBufferCount, + loopbackContext.outputOverflowCount, + loopbackContext.outputUnderflowCount, + loopbackContext.outputBufferCount + ); + + // Analyse recording to detect glitches. + for( i=0; isamplesPerFrame; i++ ) + { + double freq = PaQa_GetNthFrequency( testParams->baseFrequency, i ); + testTone.frequency = freq; + + PaQa_AnalyseRecording( &loopbackContext.recordings[i], &testTone, &analysisResult ); + + if( i==0 ) + { double latencyMSec; - printf( "%4d-%4d | ", - loopbackContext.minFramesPerBuffer, - loopbackContext.maxFramesPerBuffer - ); - - latencyMSec = 1000.0 * analysisResult.latency / testParams->sampleRate; - printf("%7.2f | ", latencyMSec ); - - } - - if( analysisResult.valid ) - { - int badChannel = ( (analysisResult.popPosition > 0) - || (analysisResult.addedFramesPosition > 0) - || (analysisResult.droppedFramesPosition > 0) ); - - if( badChannel ) - { - if( userOptions->verbose ) - { - PaQa_PrintFullErrorReport( &analysisResult, i ); - } - else - { - PaQa_PrintShortErrorReport( &analysisResult, i ); - } - PaQa_SaveTestResultToWaveFile( userOptions, &loopbackContext.recordings[i] ); - } - numBadChannels += badChannel; - } - else - { - printf( "[%d] No or low signal, ampRatio = %f", i, analysisResult.amplitudeRatio ); - numBadChannels += 1; - } - - } - if( numBadChannels == 0 ) - { - printf( "OK" ); - } + printf( "%4d-%4d | ", + loopbackContext.minFramesPerBuffer, + loopbackContext.maxFramesPerBuffer + ); + + latencyMSec = 1000.0 * analysisResult.latency / testParams->sampleRate; + printf("%7.2f | ", latencyMSec ); + + } + + if( analysisResult.valid ) + { + int badChannel = ( (analysisResult.popPosition > 0) + || (analysisResult.addedFramesPosition > 0) + || (analysisResult.droppedFramesPosition > 0) ); + + if( badChannel ) + { + if( userOptions->verbose ) + { + PaQa_PrintFullErrorReport( &analysisResult, i ); + } + else + { + PaQa_PrintShortErrorReport( &analysisResult, i ); + } + PaQa_SaveTestResultToWaveFile( userOptions, &loopbackContext.recordings[i] ); + } + numBadChannels += badChannel; + } + else + { + printf( "[%d] No or low signal, ampRatio = %f", i, analysisResult.amplitudeRatio ); + numBadChannels += 1; + } + + } + if( numBadChannels == 0 ) + { + printf( "OK" ); + } // Print the # errors so far to make it easier to see where the error occurred. - printf( " - #errs = %d\n", g_testsFailed ); - - PaQa_TeardownLoopbackContext( &loopbackContext ); - if( numBadChannels > 0 ) - { - g_testsFailed += 1; - } - return numBadChannels; - + printf( " - #errs = %d\n", g_testsFailed ); + + PaQa_TeardownLoopbackContext( &loopbackContext ); + if( numBadChannels > 0 ) + { + g_testsFailed += 1; + } + return numBadChannels; + error: - PaQa_TeardownLoopbackContext( &loopbackContext ); - printf( "\n" ); - g_testsFailed += 1; - return err; + PaQa_TeardownLoopbackContext( &loopbackContext ); + printf( "\n" ); + g_testsFailed += 1; + return err; } /*******************************************************************/ static void PaQa_SetDefaultTestParameters( TestParameters *testParamsPtr, PaDeviceIndex inputDevice, PaDeviceIndex outputDevice ) { - memset( testParamsPtr, 0, sizeof(TestParameters) ); - - testParamsPtr->samplesPerFrame = 2; - testParamsPtr->amplitude = 0.5; - testParamsPtr->sampleRate = 44100; - testParamsPtr->maxFrames = (int) (PAQA_TEST_DURATION * testParamsPtr->sampleRate); - testParamsPtr->framesPerBuffer = DEFAULT_FRAMES_PER_BUFFER; - testParamsPtr->baseFrequency = 200.0; - testParamsPtr->flags = PAQA_FLAG_TWO_STREAMS; + memset( testParamsPtr, 0, sizeof(TestParameters) ); + + testParamsPtr->samplesPerFrame = 2; + testParamsPtr->amplitude = 0.5; + testParamsPtr->sampleRate = 44100; + testParamsPtr->maxFrames = (int) (PAQA_TEST_DURATION * testParamsPtr->sampleRate); + testParamsPtr->framesPerBuffer = DEFAULT_FRAMES_PER_BUFFER; + testParamsPtr->baseFrequency = 200.0; + testParamsPtr->flags = PAQA_FLAG_TWO_STREAMS; testParamsPtr->streamFlags = paClipOff; /* we won't output out of range samples so don't bother clipping them */ - - testParamsPtr->inputParameters.device = inputDevice; - testParamsPtr->inputParameters.sampleFormat = paFloat32; - testParamsPtr->inputParameters.channelCount = testParamsPtr->samplesPerFrame; - testParamsPtr->inputParameters.suggestedLatency = Pa_GetDeviceInfo( inputDevice )->defaultLowInputLatency; - //testParamsPtr->inputParameters.suggestedLatency = Pa_GetDeviceInfo( inputDevice )->defaultHighInputLatency; - - testParamsPtr->outputParameters.device = outputDevice; - testParamsPtr->outputParameters.sampleFormat = paFloat32; - testParamsPtr->outputParameters.channelCount = testParamsPtr->samplesPerFrame; - testParamsPtr->outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputDevice )->defaultLowOutputLatency; - //testParamsPtr->outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputDevice )->defaultHighOutputLatency; + + testParamsPtr->inputParameters.device = inputDevice; + testParamsPtr->inputParameters.sampleFormat = paFloat32; + testParamsPtr->inputParameters.channelCount = testParamsPtr->samplesPerFrame; + testParamsPtr->inputParameters.suggestedLatency = Pa_GetDeviceInfo( inputDevice )->defaultLowInputLatency; + //testParamsPtr->inputParameters.suggestedLatency = Pa_GetDeviceInfo( inputDevice )->defaultHighInputLatency; + + testParamsPtr->outputParameters.device = outputDevice; + testParamsPtr->outputParameters.sampleFormat = paFloat32; + testParamsPtr->outputParameters.channelCount = testParamsPtr->samplesPerFrame; + testParamsPtr->outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputDevice )->defaultLowOutputLatency; + //testParamsPtr->outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputDevice )->defaultHighOutputLatency; } /*******************************************************************/ static void PaQa_OverrideTestParameters( TestParameters *testParamsPtr, UserOptions *userOptions ) { - // Check to see if a specific value was requested. - if( userOptions->sampleRate >= 0 ) - { - testParamsPtr->sampleRate = userOptions->sampleRate; - testParamsPtr->maxFrames = (int) (PAQA_TEST_DURATION * testParamsPtr->sampleRate); - } - if( userOptions->framesPerBuffer >= 0 ) - { - testParamsPtr->framesPerBuffer = userOptions->framesPerBuffer; - } - if( userOptions->inputLatency >= 0 ) - { - testParamsPtr->inputParameters.suggestedLatency = userOptions->inputLatency * 0.001; - } - if( userOptions->outputLatency >= 0 ) - { - testParamsPtr->outputParameters.suggestedLatency = userOptions->outputLatency * 0.001; - } - printf( " Running with suggested latency (msec): input = %5.2f, out = %5.2f\n", - (testParamsPtr->inputParameters.suggestedLatency * 1000.0), - (testParamsPtr->outputParameters.suggestedLatency * 1000.0) ); + // Check to see if a specific value was requested. + if( userOptions->sampleRate >= 0 ) + { + testParamsPtr->sampleRate = userOptions->sampleRate; + testParamsPtr->maxFrames = (int) (PAQA_TEST_DURATION * testParamsPtr->sampleRate); + } + if( userOptions->framesPerBuffer >= 0 ) + { + testParamsPtr->framesPerBuffer = userOptions->framesPerBuffer; + } + if( userOptions->inputLatency >= 0 ) + { + testParamsPtr->inputParameters.suggestedLatency = userOptions->inputLatency * 0.001; + } + if( userOptions->outputLatency >= 0 ) + { + testParamsPtr->outputParameters.suggestedLatency = userOptions->outputLatency * 0.001; + } + printf( " Running with suggested latency (msec): input = %5.2f, out = %5.2f\n", + (testParamsPtr->inputParameters.suggestedLatency * 1000.0), + (testParamsPtr->outputParameters.suggestedLatency * 1000.0) ); } /*******************************************************************/ -/** +/** * Run a series of tests on this loopback connection. * @return number of bad channel results */ static int PaQa_AnalyzeLoopbackConnection( UserOptions *userOptions, PaDeviceIndex inputDevice, PaDeviceIndex outputDevice ) { - int iFlags; - int iRate; - int iSize; - int iFormat; - int savedValue; - TestParameters testParams; - const PaDeviceInfo *inputDeviceInfo = Pa_GetDeviceInfo( inputDevice ); - const PaDeviceInfo *outputDeviceInfo = Pa_GetDeviceInfo( outputDevice ); + int iFlags; + int iRate; + int iSize; + int iFormat; + int savedValue; + TestParameters testParams; + const PaDeviceInfo *inputDeviceInfo = Pa_GetDeviceInfo( inputDevice ); + const PaDeviceInfo *outputDeviceInfo = Pa_GetDeviceInfo( outputDevice ); int totalBadChannels = 0; - // test half duplex first because it is more likely to work. - int flagSettings[] = { PAQA_FLAG_TWO_STREAMS, 0 }; - int numFlagSettings = (sizeof(flagSettings)/sizeof(int)); - - double sampleRates[] = { 8000.0, 11025.0, 16000.0, 22050.0, 32000.0, 44100.0, 48000.0, 96000.0 }; - int numRates = (sizeof(sampleRates)/sizeof(double)); - - // framesPerBuffer==0 means PA decides on the buffer size. - int framesPerBuffers[] = { 0, 16, 32, 40, 64, 100, 128, 256, 512, 1024 }; - int numBufferSizes = (sizeof(framesPerBuffers)/sizeof(int)); - - PaSampleFormat sampleFormats[] = { paFloat32, paUInt8, paInt8, paInt16, paInt32 }; - const char *sampleFormatNames[] = { "paFloat32", "paUInt8", "paInt8", "paInt16", "paInt32" }; - int numSampleFormats = (sizeof(sampleFormats)/sizeof(PaSampleFormat)); - + // test half duplex first because it is more likely to work. + int flagSettings[] = { PAQA_FLAG_TWO_STREAMS, 0 }; + int numFlagSettings = (sizeof(flagSettings)/sizeof(int)); + + double sampleRates[] = { 8000.0, 11025.0, 16000.0, 22050.0, 32000.0, 44100.0, 48000.0, 96000.0 }; + int numRates = (sizeof(sampleRates)/sizeof(double)); + + // framesPerBuffer==0 means PA decides on the buffer size. + int framesPerBuffers[] = { 0, 16, 32, 40, 64, 100, 128, 256, 512, 1024 }; + int numBufferSizes = (sizeof(framesPerBuffers)/sizeof(int)); + + PaSampleFormat sampleFormats[] = { paFloat32, paUInt8, paInt8, paInt16, paInt32 }; + const char *sampleFormatNames[] = { "paFloat32", "paUInt8", "paInt8", "paInt16", "paInt32" }; + int numSampleFormats = (sizeof(sampleFormats)/sizeof(PaSampleFormat)); + printf( "=============== Analysing Loopback %d to %d =====================\n", outputDevice, inputDevice ); - printf( " Devices: %s => %s\n", outputDeviceInfo->name, inputDeviceInfo->name); - - PaQa_SetDefaultTestParameters( &testParams, inputDevice, outputDevice ); - - PaQa_OverrideTestParameters( &testParams, userOptions ); - - // Loop though combinations of audio parameters. - for( iFlags=0; iFlagssampleRate < 0 ) - { - savedValue = testParams.sampleRate; - for( iRate=0; iRate %s\n", outputDeviceInfo->name, inputDeviceInfo->name); + + PaQa_SetDefaultTestParameters( &testParams, inputDevice, outputDevice ); + + PaQa_OverrideTestParameters( &testParams, userOptions ); + + // Loop though combinations of audio parameters. + for( iFlags=0; iFlagssampleRate < 0 ) + { + savedValue = testParams.sampleRate; + for( iRate=0; iRateframesPerBuffer < 0 ) - { - savedValue = testParams.framesPerBuffer; - for( iSize=0; iSizeframesPerBuffer < 0 ) + { + savedValue = testParams.framesPerBuffer; + for( iSize=0; iSizetest; - - // Start in the middle assuming past latency. - int startFrame = testParamsPtr->maxFrames/2; - int numFrames = testParamsPtr->maxFrames/2; - - // Check to see if the signal is clipped. - double amplitudeLeft = PaQa_MeasureSineAmplitudeBySlope( &loopbackContextPtr->recordings[0], - testParamsPtr->baseFrequency, testParamsPtr->sampleRate, - startFrame, numFrames ); - double gainLeft = amplitudeLeft / testParamsPtr->amplitude; - double amplitudeRight = PaQa_MeasureSineAmplitudeBySlope( &loopbackContextPtr->recordings[1], - testParamsPtr->baseFrequency, testParamsPtr->sampleRate, - startFrame, numFrames ); - double gainRight = amplitudeLeft / testParamsPtr->amplitude; - printf(" Loop gain: left = %f, right = %f\n", gainLeft, gainRight ); - - if( (amplitudeLeft > 1.0 ) || (amplitudeRight > 1.0) ) - { - printf("ERROR - loop gain is too high. Should be around than 1.0. Please lower output level and/or input gain.\n" ); - clipped = 1; - } - return clipped; + int clipped = 0; + TestParameters *testParamsPtr = loopbackContextPtr->test; + + // Start in the middle assuming past latency. + int startFrame = testParamsPtr->maxFrames/2; + int numFrames = testParamsPtr->maxFrames/2; + + // Check to see if the signal is clipped. + double amplitudeLeft = PaQa_MeasureSineAmplitudeBySlope( &loopbackContextPtr->recordings[0], + testParamsPtr->baseFrequency, testParamsPtr->sampleRate, + startFrame, numFrames ); + double gainLeft = amplitudeLeft / testParamsPtr->amplitude; + double amplitudeRight = PaQa_MeasureSineAmplitudeBySlope( &loopbackContextPtr->recordings[1], + testParamsPtr->baseFrequency, testParamsPtr->sampleRate, + startFrame, numFrames ); + double gainRight = amplitudeLeft / testParamsPtr->amplitude; + printf(" Loop gain: left = %f, right = %f\n", gainLeft, gainRight ); + + if( (amplitudeLeft > 1.0 ) || (amplitudeRight > 1.0) ) + { + printf("ERROR - loop gain is too high. Should be around than 1.0. Please lower output level and/or input gain.\n" ); + clipped = 1; + } + return clipped; } /*******************************************************************/ int PaQa_MeasureBackgroundNoise( LoopbackContext *loopbackContextPtr, double *rmsPtr ) { - int result = 0; - *rmsPtr = 0.0; - // Rewind so we can record some input. - loopbackContextPtr->recordings[0].numFrames = 0; - loopbackContextPtr->recordings[1].numFrames = 0; - result = PaQa_RunInputOnly( loopbackContextPtr ); - if( result == 0 ) - { - double leftRMS = PaQa_MeasureRootMeanSquare( loopbackContextPtr->recordings[0].buffer, - loopbackContextPtr->recordings[0].numFrames ); - double rightRMS = PaQa_MeasureRootMeanSquare( loopbackContextPtr->recordings[1].buffer, - loopbackContextPtr->recordings[1].numFrames ); - *rmsPtr = (leftRMS + rightRMS) / 2.0; - } - return result; + int result = 0; + *rmsPtr = 0.0; + // Rewind so we can record some input. + loopbackContextPtr->recordings[0].numFrames = 0; + loopbackContextPtr->recordings[1].numFrames = 0; + result = PaQa_RunInputOnly( loopbackContextPtr ); + if( result == 0 ) + { + double leftRMS = PaQa_MeasureRootMeanSquare( loopbackContextPtr->recordings[0].buffer, + loopbackContextPtr->recordings[0].numFrames ); + double rightRMS = PaQa_MeasureRootMeanSquare( loopbackContextPtr->recordings[1].buffer, + loopbackContextPtr->recordings[1].numFrames ); + *rmsPtr = (leftRMS + rightRMS) / 2.0; + } + return result; } /*******************************************************************/ -/** +/** * Output a sine wave then try to detect it on input. * * @return 1 if loopback connected, 0 if not, or negative error. */ int PaQa_CheckForLoopBack( UserOptions *userOptions, PaDeviceIndex inputDevice, PaDeviceIndex outputDevice ) { - TestParameters testParams; - LoopbackContext loopbackContext; - const PaDeviceInfo *inputDeviceInfo; - const PaDeviceInfo *outputDeviceInfo; - PaError err = paNoError; - double minAmplitude; - int loopbackIsConnected; + TestParameters testParams; + LoopbackContext loopbackContext; + const PaDeviceInfo *inputDeviceInfo; + const PaDeviceInfo *outputDeviceInfo; + PaError err = paNoError; + double minAmplitude; + int loopbackIsConnected; int startFrame, numFrames; double magLeft, magRight; - inputDeviceInfo = Pa_GetDeviceInfo( inputDevice ); - if( inputDeviceInfo == NULL ) - { - printf("ERROR - Pa_GetDeviceInfo for input returned NULL.\n"); - return paInvalidDevice; - } - if( inputDeviceInfo->maxInputChannels < 2 ) - { - return 0; - } - - outputDeviceInfo = Pa_GetDeviceInfo( outputDevice ); - if( outputDeviceInfo == NULL ) - { - printf("ERROR - Pa_GetDeviceInfo for output returned NULL.\n"); - return paInvalidDevice; - } - if( outputDeviceInfo->maxOutputChannels < 2 ) - { - return 0; - } - - printf( "Look for loopback cable between \"%s\" => \"%s\"\n", outputDeviceInfo->name, inputDeviceInfo->name); - - printf( " Default suggested input latency (msec): low = %5.2f, high = %5.2f\n", - (inputDeviceInfo->defaultLowInputLatency * 1000.0), - (inputDeviceInfo->defaultHighInputLatency * 1000.0) ); - printf( " Default suggested output latency (msec): low = %5.2f, high = %5.2f\n", - (outputDeviceInfo->defaultLowOutputLatency * 1000.0), - (outputDeviceInfo->defaultHighOutputLatency * 1000.0) ); - - PaQa_SetDefaultTestParameters( &testParams, inputDevice, outputDevice ); - - PaQa_OverrideTestParameters( &testParams, userOptions ); - - testParams.maxFrames = (int) (LOOPBACK_DETECTION_DURATION_SECONDS * testParams.sampleRate); - minAmplitude = testParams.amplitude / 4.0; - - // Check to see if the selected formats are supported. - if( Pa_IsFormatSupported( &testParams.inputParameters, NULL, testParams.sampleRate ) != paFormatIsSupported ) - { - printf( "Input not supported for this format!\n" ); - return 0; - } - if( Pa_IsFormatSupported( NULL, &testParams.outputParameters, testParams.sampleRate ) != paFormatIsSupported ) - { - printf( "Output not supported for this format!\n" ); - return 0; - } - - PaQa_SetupLoopbackContext( &loopbackContext, &testParams ); - - if( inputDevice == outputDevice ) - { - // Use full duplex if checking for loopback on one device. - testParams.flags &= ~PAQA_FLAG_TWO_STREAMS; - } - else - { - // Use half duplex if checking for loopback on two different device. - testParams.flags = PAQA_FLAG_TWO_STREAMS; - } - err = PaQa_RunLoopback( &loopbackContext ); - QA_ASSERT_TRUE("loopback detection callback did not run", (loopbackContext.callbackCount > 1) ); - - // Analyse recording to see if we captured the output. - // Start in the middle assuming past latency. - startFrame = testParams.maxFrames/2; - numFrames = testParams.maxFrames/2; - magLeft = PaQa_CorrelateSine( &loopbackContext.recordings[0], - loopbackContext.generators[0].frequency, - testParams.sampleRate, - startFrame, numFrames, NULL ); - magRight = PaQa_CorrelateSine( &loopbackContext.recordings[1], - loopbackContext.generators[1].frequency, - testParams.sampleRate, - startFrame, numFrames, NULL ); - printf(" Amplitudes: left = %f, right = %f\n", magLeft, magRight ); - - // Check for backwards cable. + inputDeviceInfo = Pa_GetDeviceInfo( inputDevice ); + if( inputDeviceInfo == NULL ) + { + printf("ERROR - Pa_GetDeviceInfo for input returned NULL.\n"); + return paInvalidDevice; + } + if( inputDeviceInfo->maxInputChannels < 2 ) + { + return 0; + } + + outputDeviceInfo = Pa_GetDeviceInfo( outputDevice ); + if( outputDeviceInfo == NULL ) + { + printf("ERROR - Pa_GetDeviceInfo for output returned NULL.\n"); + return paInvalidDevice; + } + if( outputDeviceInfo->maxOutputChannels < 2 ) + { + return 0; + } + + printf( "Look for loopback cable between \"%s\" => \"%s\"\n", outputDeviceInfo->name, inputDeviceInfo->name); + + printf( " Default suggested input latency (msec): low = %5.2f, high = %5.2f\n", + (inputDeviceInfo->defaultLowInputLatency * 1000.0), + (inputDeviceInfo->defaultHighInputLatency * 1000.0) ); + printf( " Default suggested output latency (msec): low = %5.2f, high = %5.2f\n", + (outputDeviceInfo->defaultLowOutputLatency * 1000.0), + (outputDeviceInfo->defaultHighOutputLatency * 1000.0) ); + + PaQa_SetDefaultTestParameters( &testParams, inputDevice, outputDevice ); + + PaQa_OverrideTestParameters( &testParams, userOptions ); + + testParams.maxFrames = (int) (LOOPBACK_DETECTION_DURATION_SECONDS * testParams.sampleRate); + minAmplitude = testParams.amplitude / 4.0; + + // Check to see if the selected formats are supported. + if( Pa_IsFormatSupported( &testParams.inputParameters, NULL, testParams.sampleRate ) != paFormatIsSupported ) + { + printf( "Input not supported for this format!\n" ); + return 0; + } + if( Pa_IsFormatSupported( NULL, &testParams.outputParameters, testParams.sampleRate ) != paFormatIsSupported ) + { + printf( "Output not supported for this format!\n" ); + return 0; + } + + PaQa_SetupLoopbackContext( &loopbackContext, &testParams ); + + if( inputDevice == outputDevice ) + { + // Use full duplex if checking for loopback on one device. + testParams.flags &= ~PAQA_FLAG_TWO_STREAMS; + } + else + { + // Use half duplex if checking for loopback on two different device. + testParams.flags = PAQA_FLAG_TWO_STREAMS; + } + err = PaQa_RunLoopback( &loopbackContext ); + QA_ASSERT_TRUE("loopback detection callback did not run", (loopbackContext.callbackCount > 1) ); + + // Analyse recording to see if we captured the output. + // Start in the middle assuming past latency. + startFrame = testParams.maxFrames/2; + numFrames = testParams.maxFrames/2; + magLeft = PaQa_CorrelateSine( &loopbackContext.recordings[0], + loopbackContext.generators[0].frequency, + testParams.sampleRate, + startFrame, numFrames, NULL ); + magRight = PaQa_CorrelateSine( &loopbackContext.recordings[1], + loopbackContext.generators[1].frequency, + testParams.sampleRate, + startFrame, numFrames, NULL ); + printf(" Amplitudes: left = %f, right = %f\n", magLeft, magRight ); + + // Check for backwards cable. loopbackIsConnected = ((magLeft > minAmplitude) && (magRight > minAmplitude)); - if( !loopbackIsConnected ) - { - double magLeftReverse = PaQa_CorrelateSine( &loopbackContext.recordings[0], - loopbackContext.generators[1].frequency, - testParams.sampleRate, - startFrame, numFrames, NULL ); - - double magRightReverse = PaQa_CorrelateSine( &loopbackContext.recordings[1], - loopbackContext.generators[0].frequency, - testParams.sampleRate, - startFrame, numFrames, NULL ); - - if ((magLeftReverse > minAmplitude) && (magRightReverse>minAmplitude)) - { - printf("ERROR - You seem to have the left and right channels swapped on the loopback cable!\n"); - } - } - else - { - double rms = 0.0; - if( PaQa_CheckForClippedLoopback( &loopbackContext ) ) - { - // Clipped so don't use this loopback. - loopbackIsConnected = 0; - } - - err = PaQa_MeasureBackgroundNoise( &loopbackContext, &rms ); - printf(" Background noise = %f\n", rms ); - if( err ) - { - printf("ERROR - Could not measure background noise on this input!\n"); - loopbackIsConnected = 0; - } - else if( rms > MAX_BACKGROUND_NOISE_RMS ) - { - printf("ERROR - There is too much background noise on this input!\n"); - loopbackIsConnected = 0; - } - } - - PaQa_TeardownLoopbackContext( &loopbackContext ); - return loopbackIsConnected; - + if( !loopbackIsConnected ) + { + double magLeftReverse = PaQa_CorrelateSine( &loopbackContext.recordings[0], + loopbackContext.generators[1].frequency, + testParams.sampleRate, + startFrame, numFrames, NULL ); + + double magRightReverse = PaQa_CorrelateSine( &loopbackContext.recordings[1], + loopbackContext.generators[0].frequency, + testParams.sampleRate, + startFrame, numFrames, NULL ); + + if ((magLeftReverse > minAmplitude) && (magRightReverse>minAmplitude)) + { + printf("ERROR - You seem to have the left and right channels swapped on the loopback cable!\n"); + } + } + else + { + double rms = 0.0; + if( PaQa_CheckForClippedLoopback( &loopbackContext ) ) + { + // Clipped so don't use this loopback. + loopbackIsConnected = 0; + } + + err = PaQa_MeasureBackgroundNoise( &loopbackContext, &rms ); + printf(" Background noise = %f\n", rms ); + if( err ) + { + printf("ERROR - Could not measure background noise on this input!\n"); + loopbackIsConnected = 0; + } + else if( rms > MAX_BACKGROUND_NOISE_RMS ) + { + printf("ERROR - There is too much background noise on this input!\n"); + loopbackIsConnected = 0; + } + } + + PaQa_TeardownLoopbackContext( &loopbackContext ); + return loopbackIsConnected; + error: - PaQa_TeardownLoopbackContext( &loopbackContext ); - return err; + PaQa_TeardownLoopbackContext( &loopbackContext ); + return err; } /*******************************************************************/ @@ -1289,17 +1289,17 @@ error: * If there is a loopback connection then run the analysis. */ static int CheckLoopbackAndScan( UserOptions *userOptions, - PaDeviceIndex iIn, PaDeviceIndex iOut ) + PaDeviceIndex iIn, PaDeviceIndex iOut ) { - int loopbackConnected = PaQa_CheckForLoopBack( userOptions, iIn, iOut ); - if( loopbackConnected > 0 ) - { - PaQa_AnalyzeLoopbackConnection( userOptions, iIn, iOut ); - return 1; - } - return 0; + int loopbackConnected = PaQa_CheckForLoopBack( userOptions, iIn, iOut ); + if( loopbackConnected > 0 ) + { + PaQa_AnalyzeLoopbackConnection( userOptions, iIn, iOut ); + return 1; + } + return 0; } - + /*******************************************************************/ /** * Scan every combination of output to input device. @@ -1308,294 +1308,294 @@ static int CheckLoopbackAndScan( UserOptions *userOptions, */ static int ScanForLoopback(UserOptions *userOptions) { - PaDeviceIndex iIn,iOut; - int numLoopbacks = 0; + PaDeviceIndex iIn,iOut; + int numLoopbacks = 0; int numDevices; - numDevices = Pa_GetDeviceCount(); - - // If both devices are specified then just use that combination. - if ((userOptions->inputDevice >= 0) && (userOptions->outputDevice >= 0)) - { - numLoopbacks += CheckLoopbackAndScan( userOptions, userOptions->inputDevice, userOptions->outputDevice ); - } - else if (userOptions->inputDevice >= 0) - { - // Just scan for output. - for( iOut=0; iOutinputDevice, iOut ); - } - } - else if (userOptions->outputDevice >= 0) - { - // Just scan for input. - for( iIn=0; iInoutputDevice ); - } - } - else - { - // Scan both. - for( iOut=0; iOut 0) ); - return numLoopbacks; - + numDevices = Pa_GetDeviceCount(); + + // If both devices are specified then just use that combination. + if ((userOptions->inputDevice >= 0) && (userOptions->outputDevice >= 0)) + { + numLoopbacks += CheckLoopbackAndScan( userOptions, userOptions->inputDevice, userOptions->outputDevice ); + } + else if (userOptions->inputDevice >= 0) + { + // Just scan for output. + for( iOut=0; iOutinputDevice, iOut ); + } + } + else if (userOptions->outputDevice >= 0) + { + // Just scan for input. + for( iIn=0; iInoutputDevice ); + } + } + else + { + // Scan both. + for( iOut=0; iOut 0) ); + return numLoopbacks; + error: - return -1; + return -1; } /*==========================================================================================*/ int TestSampleFormatConversion( void ) { - int i; - const float floatInput[] = { 1.0, 0.5, -0.5, -1.0 }; - - const char charInput[] = { 127, 64, -64, -128 }; - const unsigned char ucharInput[] = { 255, 128+64, 64, 0 }; - const short shortInput[] = { 32767, 32768/2, -32768/2, -32768 }; - const int intInput[] = { 2147483647, 2147483647/2, -1073741824 /*-2147483648/2 doesn't work in msvc*/, -2147483648 }; - - float floatOutput[4]; - short shortOutput[4]; - int intOutput[4]; - unsigned char ucharOutput[4]; - char charOutput[4]; - - QA_ASSERT_EQUALS("int must be 32-bit", 4, (int) sizeof(int) ); - QA_ASSERT_EQUALS("short must be 16-bit", 2, (int) sizeof(short) ); - - // from Float ====== - PaQa_ConvertFromFloat( floatInput, 4, paUInt8, ucharOutput ); - for( i=0; i<4; i++ ) - { - QA_ASSERT_CLOSE_INT( "paFloat32 -> paUInt8 -> error", ucharInput[i], ucharOutput[i], 1 ); - } - - PaQa_ConvertFromFloat( floatInput, 4, paInt8, charOutput ); - for( i=0; i<4; i++ ) - { - QA_ASSERT_CLOSE_INT( "paFloat32 -> paInt8 -> error", charInput[i], charOutput[i], 1 ); - } - - PaQa_ConvertFromFloat( floatInput, 4, paInt16, shortOutput ); - for( i=0; i<4; i++ ) - { - QA_ASSERT_CLOSE_INT( "paFloat32 -> paInt16 error", shortInput[i], shortOutput[i], 1 ); - } - - PaQa_ConvertFromFloat( floatInput, 4, paInt32, intOutput ); - for( i=0; i<4; i++ ) - { - QA_ASSERT_CLOSE_INT( "paFloat32 -> paInt32 error", intInput[i], intOutput[i], 0x00010000 ); - } - - - // to Float ====== - memset( floatOutput, 0, sizeof(floatOutput) ); - PaQa_ConvertToFloat( ucharInput, 4, paUInt8, floatOutput ); - for( i=0; i<4; i++ ) - { - QA_ASSERT_CLOSE( "paUInt8 -> paFloat32 error", floatInput[i], floatOutput[i], 0.01 ); - } - - memset( floatOutput, 0, sizeof(floatOutput) ); - PaQa_ConvertToFloat( charInput, 4, paInt8, floatOutput ); - for( i=0; i<4; i++ ) - { - QA_ASSERT_CLOSE( "paInt8 -> paFloat32 error", floatInput[i], floatOutput[i], 0.01 ); - } - - memset( floatOutput, 0, sizeof(floatOutput) ); - PaQa_ConvertToFloat( shortInput, 4, paInt16, floatOutput ); - for( i=0; i<4; i++ ) - { - QA_ASSERT_CLOSE( "paInt16 -> paFloat32 error", floatInput[i], floatOutput[i], 0.001 ); - } - - memset( floatOutput, 0, sizeof(floatOutput) ); - PaQa_ConvertToFloat( intInput, 4, paInt32, floatOutput ); - for( i=0; i<4; i++ ) - { - QA_ASSERT_CLOSE( "paInt32 -> paFloat32 error", floatInput[i], floatOutput[i], 0.00001 ); - } - - return 0; - + int i; + const float floatInput[] = { 1.0, 0.5, -0.5, -1.0 }; + + const char charInput[] = { 127, 64, -64, -128 }; + const unsigned char ucharInput[] = { 255, 128+64, 64, 0 }; + const short shortInput[] = { 32767, 32768/2, -32768/2, -32768 }; + const int intInput[] = { 2147483647, 2147483647/2, -1073741824 /*-2147483648/2 doesn't work in msvc*/, -2147483648 }; + + float floatOutput[4]; + short shortOutput[4]; + int intOutput[4]; + unsigned char ucharOutput[4]; + char charOutput[4]; + + QA_ASSERT_EQUALS("int must be 32-bit", 4, (int) sizeof(int) ); + QA_ASSERT_EQUALS("short must be 16-bit", 2, (int) sizeof(short) ); + + // from Float ====== + PaQa_ConvertFromFloat( floatInput, 4, paUInt8, ucharOutput ); + for( i=0; i<4; i++ ) + { + QA_ASSERT_CLOSE_INT( "paFloat32 -> paUInt8 -> error", ucharInput[i], ucharOutput[i], 1 ); + } + + PaQa_ConvertFromFloat( floatInput, 4, paInt8, charOutput ); + for( i=0; i<4; i++ ) + { + QA_ASSERT_CLOSE_INT( "paFloat32 -> paInt8 -> error", charInput[i], charOutput[i], 1 ); + } + + PaQa_ConvertFromFloat( floatInput, 4, paInt16, shortOutput ); + for( i=0; i<4; i++ ) + { + QA_ASSERT_CLOSE_INT( "paFloat32 -> paInt16 error", shortInput[i], shortOutput[i], 1 ); + } + + PaQa_ConvertFromFloat( floatInput, 4, paInt32, intOutput ); + for( i=0; i<4; i++ ) + { + QA_ASSERT_CLOSE_INT( "paFloat32 -> paInt32 error", intInput[i], intOutput[i], 0x00010000 ); + } + + + // to Float ====== + memset( floatOutput, 0, sizeof(floatOutput) ); + PaQa_ConvertToFloat( ucharInput, 4, paUInt8, floatOutput ); + for( i=0; i<4; i++ ) + { + QA_ASSERT_CLOSE( "paUInt8 -> paFloat32 error", floatInput[i], floatOutput[i], 0.01 ); + } + + memset( floatOutput, 0, sizeof(floatOutput) ); + PaQa_ConvertToFloat( charInput, 4, paInt8, floatOutput ); + for( i=0; i<4; i++ ) + { + QA_ASSERT_CLOSE( "paInt8 -> paFloat32 error", floatInput[i], floatOutput[i], 0.01 ); + } + + memset( floatOutput, 0, sizeof(floatOutput) ); + PaQa_ConvertToFloat( shortInput, 4, paInt16, floatOutput ); + for( i=0; i<4; i++ ) + { + QA_ASSERT_CLOSE( "paInt16 -> paFloat32 error", floatInput[i], floatOutput[i], 0.001 ); + } + + memset( floatOutput, 0, sizeof(floatOutput) ); + PaQa_ConvertToFloat( intInput, 4, paInt32, floatOutput ); + for( i=0; i<4; i++ ) + { + QA_ASSERT_CLOSE( "paInt32 -> paFloat32 error", floatInput[i], floatOutput[i], 0.00001 ); + } + + return 0; + error: - return -1; + return -1; } /*******************************************************************/ void usage( const char *name ) { - printf("%s [-i# -o# -l# -r# -s# -m -w -dDir]\n", name); - printf(" -i# - Input device ID. Will scan for loopback cable if not specified.\n"); - printf(" -o# - Output device ID. Will scan for loopback if not specified.\n"); - printf(" -l# - Latency for both input and output in milliseconds.\n"); - printf(" --inputLatency # Input latency in milliseconds.\n"); - printf(" --outputLatency # Output latency in milliseconds.\n"); - printf(" -r# - Sample Rate in Hz. Will use multiple common rates if not specified.\n"); - printf(" -s# - Size of callback buffer in frames, framesPerBuffer. Will use common values if not specified.\n"); - printf(" -w - Save bad recordings in a WAV file.\n"); - printf(" -dDir - Path for Directory for WAV files. Default is current directory.\n"); - printf(" -m - Just test the DSP Math code and not the audio devices.\n"); - printf(" -v - Verbose reports.\n"); + printf("%s [-i# -o# -l# -r# -s# -m -w -dDir]\n", name); + printf(" -i# - Input device ID. Will scan for loopback cable if not specified.\n"); + printf(" -o# - Output device ID. Will scan for loopback if not specified.\n"); + printf(" -l# - Latency for both input and output in milliseconds.\n"); + printf(" --inputLatency # Input latency in milliseconds.\n"); + printf(" --outputLatency # Output latency in milliseconds.\n"); + printf(" -r# - Sample Rate in Hz. Will use multiple common rates if not specified.\n"); + printf(" -s# - Size of callback buffer in frames, framesPerBuffer. Will use common values if not specified.\n"); + printf(" -w - Save bad recordings in a WAV file.\n"); + printf(" -dDir - Path for Directory for WAV files. Default is current directory.\n"); + printf(" -m - Just test the DSP Math code and not the audio devices.\n"); + printf(" -v - Verbose reports.\n"); } /*******************************************************************/ int main( int argc, char **argv ) { int i; - UserOptions userOptions; - int result = 0; - int justMath = 0; + UserOptions userOptions; + int result = 0; + int justMath = 0; char *executableName = argv[0]; - printf("PortAudio LoopBack Test built " __DATE__ " at " __TIME__ "\n"); - - if( argc > 1 ){ - printf("running with arguments:"); - for(i=1; i < argc; ++i ) - printf(" %s", argv[i] ); - printf("\n"); - }else{ - printf("running with no arguments\n"); - } - - memset(&userOptions, 0, sizeof(userOptions)); - userOptions.inputDevice = paNoDevice; - userOptions.outputDevice = paNoDevice; - userOptions.sampleRate = -1; - userOptions.framesPerBuffer = -1; - userOptions.inputLatency = -1; - userOptions.outputLatency = -1; - userOptions.waveFilePath = "."; - - // Process arguments. Skip name of executable. - i = 1; - while( i 1 ){ + printf("running with arguments:"); + for(i=1; i < argc; ++i ) + printf(" %s", argv[i] ); + printf("\n"); + }else{ + printf("running with no arguments\n"); + } + + memset(&userOptions, 0, sizeof(userOptions)); + userOptions.inputDevice = paNoDevice; + userOptions.outputDevice = paNoDevice; + userOptions.sampleRate = -1; + userOptions.framesPerBuffer = -1; + userOptions.inputLatency = -1; + userOptions.outputLatency = -1; + userOptions.waveFilePath = "."; + + // Process arguments. Skip name of executable. + i = 1; + while( imaxInputChannels ); printf( ", %2d out", deviceInfo->maxOutputChannels ); - printf( ", %s", deviceInfo->name ); + printf( ", %s", deviceInfo->name ); printf( ", on %s\n", Pa_GetHostApiInfo( deviceInfo->hostApi )->name ); - } + } } /*******************************************************************/ void PaQa_ConvertToFloat( const void *input, int numSamples, PaSampleFormat inFormat, float *output ) { - int i; - switch( inFormat ) - { - case paUInt8: - { - unsigned char *data = (unsigned char *)input; - for( i=0; i> 8; - float fval = (float) (value / ((double) 0x00800000)); - *output++ = fval; - } - } - break; - } - + int i; + switch( inFormat ) + { + case paUInt8: + { + unsigned char *data = (unsigned char *)input; + for( i=0; i> 8; + float fval = (float) (value / ((double) 0x00800000)); + *output++ = fval; + } + } + break; + } + } /*******************************************************************/ void PaQa_ConvertFromFloat( const float *input, int numSamples, PaSampleFormat outFormat, void *output ) { - int i; - switch( outFormat ) - { - case paUInt8: - { - unsigned char *data = (unsigned char *)output; - for( i=0; i(tolerance)) \ - { \ - printf( "%s:%d - ERROR - %s, expected %f, got %f, tol=%f\n", __FILE__, __LINE__, message, ((double)(expected)), ((double)(actual)), ((double)(tolerance)) ); \ - g_testsFailed++; \ - goto error; \ - } \ - else g_testsPassed++; + if (fabs((expected)-(actual))>(tolerance)) \ + { \ + printf( "%s:%d - ERROR - %s, expected %f, got %f, tol=%f\n", __FILE__, __LINE__, message, ((double)(expected)), ((double)(actual)), ((double)(tolerance)) ); \ + g_testsFailed++; \ + goto error; \ + } \ + else g_testsPassed++; #define QA_ASSERT_CLOSE_INT( message, expected, actual, tolerance ) \ if (abs((expected)-(actual))>(tolerance)) \ diff --git a/qa/loopback/src/test_audio_analyzer.c b/qa/loopback/src/test_audio_analyzer.c index add57d4..82fa859 100644 --- a/qa/loopback/src/test_audio_analyzer.c +++ b/qa/loopback/src/test_audio_analyzer.c @@ -26,13 +26,13 @@ */ /* - * The text above constitutes the entire PortAudio license; however, + * 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 + * they can be incorporated into the canonical version. It is also + * requested that these non-binding requests be included along with the * license above. */ @@ -56,48 +56,48 @@ */ static int TestSingleMonoTone( void ) { - int result = 0; - PaQaSineGenerator generator; - PaQaRecording recording; - float buffer[FRAMES_PER_BLOCK]; - double sampleRate = 44100.0; - int maxFrames = ((int)sampleRate) * 1; - int samplesPerFrame = 1; - int stride = 1; + int result = 0; + PaQaSineGenerator generator; + PaQaRecording recording; + float buffer[FRAMES_PER_BLOCK]; + double sampleRate = 44100.0; + int maxFrames = ((int)sampleRate) * 1; + int samplesPerFrame = 1; + int stride = 1; int done = 0; - double freq = 234.5; - double amp = 0.5; - + double freq = 234.5; + double amp = 0.5; + double mag1, mag2; - // Setup a sine oscillator. - PaQa_SetupSineGenerator( &generator, freq, amp, sampleRate ); - - result = PaQa_InitializeRecording( &recording, maxFrames, (int) sampleRate ); - QA_ASSERT_EQUALS( "PaQa_InitializeRecording failed", 0, result ); - - done = 0; - while (!done) - { - PaQa_EraseBuffer( buffer, FRAMES_PER_BLOCK, samplesPerFrame ); - PaQa_MixSine( &generator, buffer, FRAMES_PER_BLOCK, stride ); - done = PaQa_WriteRecording( &recording, buffer, FRAMES_PER_BLOCK, samplesPerFrame ); - } - - mag1 = PaQa_CorrelateSine( &recording, freq, sampleRate, 0, recording.numFrames, NULL ); - QA_ASSERT_CLOSE( "exact frequency match", amp, mag1, 0.01 ); - - mag2 = PaQa_CorrelateSine( &recording, freq * 1.23, sampleRate, 0, recording.numFrames, NULL ); - QA_ASSERT_CLOSE( "wrong frequency", 0.0, mag2, 0.01 ); - - PaQa_TerminateRecording( &recording ); - return 0; - + // Setup a sine oscillator. + PaQa_SetupSineGenerator( &generator, freq, amp, sampleRate ); + + result = PaQa_InitializeRecording( &recording, maxFrames, (int) sampleRate ); + QA_ASSERT_EQUALS( "PaQa_InitializeRecording failed", 0, result ); + + done = 0; + while (!done) + { + PaQa_EraseBuffer( buffer, FRAMES_PER_BLOCK, samplesPerFrame ); + PaQa_MixSine( &generator, buffer, FRAMES_PER_BLOCK, stride ); + done = PaQa_WriteRecording( &recording, buffer, FRAMES_PER_BLOCK, samplesPerFrame ); + } + + mag1 = PaQa_CorrelateSine( &recording, freq, sampleRate, 0, recording.numFrames, NULL ); + QA_ASSERT_CLOSE( "exact frequency match", amp, mag1, 0.01 ); + + mag2 = PaQa_CorrelateSine( &recording, freq * 1.23, sampleRate, 0, recording.numFrames, NULL ); + QA_ASSERT_CLOSE( "wrong frequency", 0.0, mag2, 0.01 ); + + PaQa_TerminateRecording( &recording ); + return 0; + error: - PaQa_TerminateRecording( &recording); - return 1; - + PaQa_TerminateRecording( &recording); + return 1; + } /*==========================================================================================*/ @@ -107,60 +107,60 @@ error: static int TestMixedMonoTones( void ) { - int i; - int result = 0; + int i; + int result = 0; #define NUM_TONES (5) - PaQaSineGenerator generators[NUM_TONES]; - PaQaRecording recording; - float buffer[FRAMES_PER_BLOCK]; - double sampleRate = 44100.0; - int maxFrames = ((int)sampleRate) * 1; - int samplesPerFrame = 1; - - double baseFreq = 234.5; - double amp = 0.1; - + PaQaSineGenerator generators[NUM_TONES]; + PaQaRecording recording; + float buffer[FRAMES_PER_BLOCK]; + double sampleRate = 44100.0; + int maxFrames = ((int)sampleRate) * 1; + int samplesPerFrame = 1; + + double baseFreq = 234.5; + double amp = 0.1; + double mag2; int stride = samplesPerFrame; - int done = 0; - - // Setup a sine oscillator. - for( i=0; istartDelay; - - int stride = 1; - // Record some initial silence. - int done = PaQa_WriteSilence( recording, testTone->startDelay ); - + + int stride = 1; + // Record some initial silence. + int done = PaQa_WriteSilence( recording, testTone->startDelay ); + // Setup a sine oscillator. - PaQa_SetupSineGenerator( &generator, testTone->frequency, testTone->amplitude, testTone->sampleRate ); - - while (!done) - { - int framesThisLoop = BUFFER_SIZE; - - if( frameCounter == glitchPosition ) - { - if( framesToAdd > 0 ) - { - // Record some frozen data without advancing the sine generator. - done = PaQa_RecordFreeze( recording, framesToAdd ); - frameCounter += framesToAdd; - } - else if( framesToAdd < 0 ) - { - // Advance sine generator a few frames. - PaQa_MixSine( &generator, buffer, 0 - framesToAdd, stride ); - } - - } - else if( (frameCounter < glitchPosition) && ((frameCounter + framesThisLoop) > glitchPosition) ) - { - // Go right up to the glitchPosition. - framesThisLoop = glitchPosition - frameCounter; - } - - if( framesThisLoop > 0 ) - { - PaQa_EraseBuffer( buffer, framesThisLoop, testTone->samplesPerFrame ); - PaQa_MixSine( &generator, buffer, framesThisLoop, stride ); - done = PaQa_WriteRecording( recording, buffer, framesThisLoop, testTone->samplesPerFrame ); - } - frameCounter += framesThisLoop; - } + PaQa_SetupSineGenerator( &generator, testTone->frequency, testTone->amplitude, testTone->sampleRate ); + + while (!done) + { + int framesThisLoop = BUFFER_SIZE; + + if( frameCounter == glitchPosition ) + { + if( framesToAdd > 0 ) + { + // Record some frozen data without advancing the sine generator. + done = PaQa_RecordFreeze( recording, framesToAdd ); + frameCounter += framesToAdd; + } + else if( framesToAdd < 0 ) + { + // Advance sine generator a few frames. + PaQa_MixSine( &generator, buffer, 0 - framesToAdd, stride ); + } + + } + else if( (frameCounter < glitchPosition) && ((frameCounter + framesThisLoop) > glitchPosition) ) + { + // Go right up to the glitchPosition. + framesThisLoop = glitchPosition - frameCounter; + } + + if( framesThisLoop > 0 ) + { + PaQa_EraseBuffer( buffer, framesThisLoop, testTone->samplesPerFrame ); + PaQa_MixSine( &generator, buffer, framesThisLoop, stride ); + done = PaQa_WriteRecording( recording, buffer, framesThisLoop, testTone->samplesPerFrame ); + } + frameCounter += framesThisLoop; + } } @@ -227,24 +227,24 @@ static void MakeRecordingWithAddedFrames( PaQaRecording *recording, PaQaTestTone static void MakeCleanRecording( PaQaRecording *recording, PaQaTestTone *testTone ) { - PaQaSineGenerator generator; + PaQaSineGenerator generator; #define BUFFER_SIZE 512 - float buffer[BUFFER_SIZE]; - - int stride = 1; - // Record some initial silence. - int done = PaQa_WriteSilence( recording, testTone->startDelay ); - - // Setup a sine oscillator. - PaQa_SetupSineGenerator( &generator, testTone->frequency, testTone->amplitude, testTone->sampleRate ); - - // Generate recording with good phase. - while (!done) - { - PaQa_EraseBuffer( buffer, BUFFER_SIZE, testTone->samplesPerFrame ); - PaQa_MixSine( &generator, buffer, BUFFER_SIZE, stride ); - done = PaQa_WriteRecording( recording, buffer, BUFFER_SIZE, testTone->samplesPerFrame ); - } + float buffer[BUFFER_SIZE]; + + int stride = 1; + // Record some initial silence. + int done = PaQa_WriteSilence( recording, testTone->startDelay ); + + // Setup a sine oscillator. + PaQa_SetupSineGenerator( &generator, testTone->frequency, testTone->amplitude, testTone->sampleRate ); + + // Generate recording with good phase. + while (!done) + { + PaQa_EraseBuffer( buffer, BUFFER_SIZE, testTone->samplesPerFrame ); + PaQa_MixSine( &generator, buffer, BUFFER_SIZE, stride ); + done = PaQa_WriteRecording( recording, buffer, BUFFER_SIZE, testTone->samplesPerFrame ); + } } /*==========================================================================================*/ @@ -254,22 +254,22 @@ static void MakeCleanRecording( PaQaRecording *recording, PaQaTestTone *testTone static void MakeRecordingWithPop( PaQaRecording *recording, PaQaTestTone *testTone, int popPosition, int popWidth, double popAmplitude ) { - int i; - - MakeCleanRecording( recording, testTone ); - - // Apply glitch to good recording. - if( (popPosition + popWidth) >= recording->numFrames ) - { - popWidth = (recording->numFrames - popPosition) - 1; - } - - for( i=0; ibuffer[i+popPosition]; - float bad = (good > 0.0) ? (good - popAmplitude) : (good + popAmplitude); - recording->buffer[i+popPosition] = bad; - } + int i; + + MakeCleanRecording( recording, testTone ); + + // Apply glitch to good recording. + if( (popPosition + popWidth) >= recording->numFrames ) + { + popWidth = (recording->numFrames - popPosition) - 1; + } + + for( i=0; ibuffer[i+popPosition]; + float bad = (good > 0.0) ? (good - popAmplitude) : (good + popAmplitude); + recording->buffer[i+popPosition] = bad; + } } /*==========================================================================================*/ @@ -278,54 +278,54 @@ static void MakeRecordingWithPop( PaQaRecording *recording, PaQaTestTone *testTo */ static int TestDetectSinglePhaseError( double sampleRate, int cycleSize, int latencyFrames, int glitchPosition, int framesAdded ) { - int result = 0; - PaQaRecording recording; - PaQaTestTone testTone; - PaQaAnalysisResult analysisResult = { 0.0 }; - int framesDropped = 0; + int result = 0; + PaQaRecording recording; + PaQaTestTone testTone; + PaQaAnalysisResult analysisResult = { 0.0 }; + int framesDropped = 0; int maxFrames = ((int)sampleRate) * 2; - testTone.samplesPerFrame = 1; - testTone.sampleRate = sampleRate; - testTone.frequency = sampleRate / cycleSize; - testTone.amplitude = 0.5; - testTone.startDelay = latencyFrames; - - result = PaQa_InitializeRecording( &recording, maxFrames, (int) sampleRate ); - QA_ASSERT_EQUALS( "PaQa_InitializeRecording failed", 0, result ); - - MakeRecordingWithAddedFrames( &recording, &testTone, glitchPosition, framesAdded ); - - PaQa_AnalyseRecording( &recording, &testTone, &analysisResult ); - - if( framesAdded < 0 ) - { - framesDropped = -framesAdded; - framesAdded = 0; - } - + testTone.samplesPerFrame = 1; + testTone.sampleRate = sampleRate; + testTone.frequency = sampleRate / cycleSize; + testTone.amplitude = 0.5; + testTone.startDelay = latencyFrames; + + result = PaQa_InitializeRecording( &recording, maxFrames, (int) sampleRate ); + QA_ASSERT_EQUALS( "PaQa_InitializeRecording failed", 0, result ); + + MakeRecordingWithAddedFrames( &recording, &testTone, glitchPosition, framesAdded ); + + PaQa_AnalyseRecording( &recording, &testTone, &analysisResult ); + + if( framesAdded < 0 ) + { + framesDropped = -framesAdded; + framesAdded = 0; + } + #if PRINT_REPORTS - printf("\n=== Dropped Frame Analysis ===================\n"); - printf(" expected actual\n"); - printf(" latency: %10.3f %10.3f\n", (double)latencyFrames, analysisResult.latency ); - printf(" num added frames: %10.3f %10.3f\n", (double)framesAdded, analysisResult.numAddedFrames ); - printf(" added frames at: %10.3f %10.3f\n", (double)glitchPosition, analysisResult.addedFramesPosition ); - printf(" num dropped frames: %10.3f %10.3f\n", (double)framesDropped, analysisResult.numDroppedFrames ); - printf(" dropped frames at: %10.3f %10.3f\n", (double)glitchPosition, analysisResult.droppedFramesPosition ); + printf("\n=== Dropped Frame Analysis ===================\n"); + printf(" expected actual\n"); + printf(" latency: %10.3f %10.3f\n", (double)latencyFrames, analysisResult.latency ); + printf(" num added frames: %10.3f %10.3f\n", (double)framesAdded, analysisResult.numAddedFrames ); + printf(" added frames at: %10.3f %10.3f\n", (double)glitchPosition, analysisResult.addedFramesPosition ); + printf(" num dropped frames: %10.3f %10.3f\n", (double)framesDropped, analysisResult.numDroppedFrames ); + printf(" dropped frames at: %10.3f %10.3f\n", (double)glitchPosition, analysisResult.droppedFramesPosition ); #endif - - QA_ASSERT_CLOSE( "PaQa_AnalyseRecording latency", latencyFrames, analysisResult.latency, 0.5 ); - QA_ASSERT_CLOSE( "PaQa_AnalyseRecording framesAdded", framesAdded, analysisResult.numAddedFrames, 1.0 ); - QA_ASSERT_CLOSE( "PaQa_AnalyseRecording framesDropped", framesDropped, analysisResult.numDroppedFrames, 1.0 ); -// QA_ASSERT_CLOSE( "PaQa_AnalyseRecording glitchPosition", glitchPosition, analysisResult.glitchPosition, cycleSize ); - - PaQa_TerminateRecording( &recording ); - return 0; - + + QA_ASSERT_CLOSE( "PaQa_AnalyseRecording latency", latencyFrames, analysisResult.latency, 0.5 ); + QA_ASSERT_CLOSE( "PaQa_AnalyseRecording framesAdded", framesAdded, analysisResult.numAddedFrames, 1.0 ); + QA_ASSERT_CLOSE( "PaQa_AnalyseRecording framesDropped", framesDropped, analysisResult.numDroppedFrames, 1.0 ); +// QA_ASSERT_CLOSE( "PaQa_AnalyseRecording glitchPosition", glitchPosition, analysisResult.glitchPosition, cycleSize ); + + PaQa_TerminateRecording( &recording ); + return 0; + error: - PaQa_TerminateRecording( &recording); - return 1; -} + PaQa_TerminateRecording( &recording); + return 1; +} /*==========================================================================================*/ /** @@ -333,34 +333,34 @@ error: */ static int TestDetectPhaseErrors( void ) { - int result; - - result = TestDetectSinglePhaseError( 44100, 200, 477, -1, 0 ); - if( result < 0 ) return result; + int result; + + result = TestDetectSinglePhaseError( 44100, 200, 477, -1, 0 ); + if( result < 0 ) return result; /* - result = TestDetectSinglePhaseError( 44100, 200, 77, -1, 0 ); - if( result < 0 ) return result; - - result = TestDetectSinglePhaseError( 44100, 200, 83, 3712, 9 ); - if( result < 0 ) return result; - - result = TestDetectSinglePhaseError( 44100, 280, 83, 3712, 27 ); - if( result < 0 ) return result; - - result = TestDetectSinglePhaseError( 44100, 200, 234, 3712, -9 ); - if( result < 0 ) return result; - - result = TestDetectSinglePhaseError( 44100, 200, 2091, 8923, -2 ); - if( result < 0 ) return result; - - result = TestDetectSinglePhaseError( 44100, 120, 1782, 5772, -18 ); - if( result < 0 ) return result; - - // Note that if the frequency is too high then it is hard to detect single dropped frames. - result = TestDetectSinglePhaseError( 44100, 200, 500, 4251, -1 ); - if( result < 0 ) return result; + result = TestDetectSinglePhaseError( 44100, 200, 77, -1, 0 ); + if( result < 0 ) return result; + + result = TestDetectSinglePhaseError( 44100, 200, 83, 3712, 9 ); + if( result < 0 ) return result; + + result = TestDetectSinglePhaseError( 44100, 280, 83, 3712, 27 ); + if( result < 0 ) return result; + + result = TestDetectSinglePhaseError( 44100, 200, 234, 3712, -9 ); + if( result < 0 ) return result; + + result = TestDetectSinglePhaseError( 44100, 200, 2091, 8923, -2 ); + if( result < 0 ) return result; + + result = TestDetectSinglePhaseError( 44100, 120, 1782, 5772, -18 ); + if( result < 0 ) return result; + + // Note that if the frequency is too high then it is hard to detect single dropped frames. + result = TestDetectSinglePhaseError( 44100, 200, 500, 4251, -1 ); + if( result < 0 ) return result; */ - return 0; + return 0; } /*==========================================================================================*/ @@ -369,53 +369,53 @@ static int TestDetectPhaseErrors( void ) */ static int TestDetectSinglePop( double sampleRate, int cycleSize, int latencyFrames, int popPosition, int popWidth, double popAmplitude ) { - int result = 0; - PaQaRecording recording; - PaQaTestTone testTone; - PaQaAnalysisResult analysisResult = { 0.0 }; - int maxFrames = ((int)sampleRate) * 2; - - testTone.samplesPerFrame = 1; - testTone.sampleRate = sampleRate; - testTone.frequency = sampleRate / cycleSize; - testTone.amplitude = 0.5; - testTone.startDelay = latencyFrames; - - result = PaQa_InitializeRecording( &recording, maxFrames, (int) sampleRate ); - QA_ASSERT_EQUALS( "PaQa_InitializeRecording failed", 0, result ); - - MakeRecordingWithPop( &recording, &testTone, popPosition, popWidth, popAmplitude ); - - PaQa_AnalyseRecording( &recording, &testTone, &analysisResult ); - + int result = 0; + PaQaRecording recording; + PaQaTestTone testTone; + PaQaAnalysisResult analysisResult = { 0.0 }; + int maxFrames = ((int)sampleRate) * 2; + + testTone.samplesPerFrame = 1; + testTone.sampleRate = sampleRate; + testTone.frequency = sampleRate / cycleSize; + testTone.amplitude = 0.5; + testTone.startDelay = latencyFrames; + + result = PaQa_InitializeRecording( &recording, maxFrames, (int) sampleRate ); + QA_ASSERT_EQUALS( "PaQa_InitializeRecording failed", 0, result ); + + MakeRecordingWithPop( &recording, &testTone, popPosition, popWidth, popAmplitude ); + + PaQa_AnalyseRecording( &recording, &testTone, &analysisResult ); + #if PRINT_REPORTS - printf("\n=== Pop Analysis ===================\n"); - printf(" expected actual\n"); - printf(" latency: %10.3f %10.3f\n", (double)latencyFrames, analysisResult.latency ); - printf(" popPosition: %10.3f %10.3f\n", (double)popPosition, analysisResult.popPosition ); - printf(" popAmplitude: %10.3f %10.3f\n", popAmplitude, analysisResult.popAmplitude ); - printf(" cycleSize: %6d\n", cycleSize ); - printf(" num added frames: %10.3f\n", analysisResult.numAddedFrames ); - printf(" added frames at: %10.3f\n", analysisResult.addedFramesPosition ); - printf(" num dropped frames: %10.3f\n", analysisResult.numDroppedFrames ); - printf(" dropped frames at: %10.3f\n", analysisResult.droppedFramesPosition ); + printf("\n=== Pop Analysis ===================\n"); + printf(" expected actual\n"); + printf(" latency: %10.3f %10.3f\n", (double)latencyFrames, analysisResult.latency ); + printf(" popPosition: %10.3f %10.3f\n", (double)popPosition, analysisResult.popPosition ); + printf(" popAmplitude: %10.3f %10.3f\n", popAmplitude, analysisResult.popAmplitude ); + printf(" cycleSize: %6d\n", cycleSize ); + printf(" num added frames: %10.3f\n", analysisResult.numAddedFrames ); + printf(" added frames at: %10.3f\n", analysisResult.addedFramesPosition ); + printf(" num dropped frames: %10.3f\n", analysisResult.numDroppedFrames ); + printf(" dropped frames at: %10.3f\n", analysisResult.droppedFramesPosition ); #endif - - QA_ASSERT_CLOSE( "PaQa_AnalyseRecording latency", latencyFrames, analysisResult.latency, 0.5 ); - QA_ASSERT_CLOSE( "PaQa_AnalyseRecording popPosition", popPosition, analysisResult.popPosition, 10 ); - if( popWidth > 0 ) - { - QA_ASSERT_CLOSE( "PaQa_AnalyseRecording popAmplitude", popAmplitude, analysisResult.popAmplitude, 0.1 * popAmplitude ); - } - - PaQa_TerminateRecording( &recording ); - return 0; - + + QA_ASSERT_CLOSE( "PaQa_AnalyseRecording latency", latencyFrames, analysisResult.latency, 0.5 ); + QA_ASSERT_CLOSE( "PaQa_AnalyseRecording popPosition", popPosition, analysisResult.popPosition, 10 ); + if( popWidth > 0 ) + { + QA_ASSERT_CLOSE( "PaQa_AnalyseRecording popAmplitude", popAmplitude, analysisResult.popAmplitude, 0.1 * popAmplitude ); + } + + PaQa_TerminateRecording( &recording ); + return 0; + error: - PaQa_SaveRecordingToWaveFile( &recording, "bad_recording.wav" ); - PaQa_TerminateRecording( &recording); - return 1; -} + PaQa_SaveRecordingToWaveFile( &recording, "bad_recording.wav" ); + PaQa_TerminateRecording( &recording); + return 1; +} /*==========================================================================================*/ /** @@ -423,73 +423,73 @@ error: */ static int TestSingleInitialSpike( double sampleRate, int stepPosition, int cycleSize, int latencyFrames, double stepAmplitude ) { - int i; - int result = 0; - // Account for highpass filter offset. - int expectedLatency = latencyFrames + 1; - PaQaRecording recording; - - PaQaRecording hipassOutput = { 0 }; - BiquadFilter hipassFilter; - - PaQaTestTone testTone; - PaQaAnalysisResult analysisResult = { 0.0 }; - int maxFrames = ((int)sampleRate) * 2; - - testTone.samplesPerFrame = 1; - testTone.sampleRate = sampleRate; - testTone.frequency = sampleRate / cycleSize; - testTone.amplitude = -0.5; - testTone.startDelay = latencyFrames; - - result = PaQa_InitializeRecording( &recording, maxFrames, (int) sampleRate ); - QA_ASSERT_EQUALS( "PaQa_InitializeRecording failed", 0, result ); - - result = PaQa_InitializeRecording( &hipassOutput, maxFrames, (int) sampleRate ); - QA_ASSERT_EQUALS( "PaQa_InitializeRecording failed", 0, result ); - - MakeCleanRecording( &recording, &testTone ); - - // Apply DC step. - for( i=stepPosition; i>8); - *addr++ = (unsigned char) (data>>16); - *addr++ = (unsigned char) (data>>24); - *addrPtr = addr; + unsigned char *addr = *addrPtr; + *addr++ = (unsigned char) data; + *addr++ = (unsigned char) (data>>8); + *addr++ = (unsigned char) (data>>16); + *addr++ = (unsigned char) (data>>24); + *addrPtr = addr; } /* Write short word data to a little endian format byte array. */ static void WriteShortLE( unsigned char **addrPtr, unsigned short data ) { - unsigned char *addr = *addrPtr; - *addr++ = (unsigned char) data; - *addr++ = (unsigned char) (data>>8); - *addrPtr = addr; + unsigned char *addr = *addrPtr; + *addr++ = (unsigned char) data; + *addr++ = (unsigned char) (data>>8); + *addrPtr = addr; } /* Write IFF ChunkType data to a byte array. */ static void WriteChunkType( unsigned char **addrPtr, unsigned long cktyp ) { - unsigned char *addr = *addrPtr; - *addr++ = (unsigned char) (cktyp>>24); - *addr++ = (unsigned char) (cktyp>>16); - *addr++ = (unsigned char) (cktyp>>8); - *addr++ = (unsigned char) cktyp; - *addrPtr = addr; + unsigned char *addr = *addrPtr; + *addr++ = (unsigned char) (cktyp>>24); + *addr++ = (unsigned char) (cktyp>>16); + *addr++ = (unsigned char) (cktyp>>8); + *addr++ = (unsigned char) cktyp; + *addrPtr = addr; } #define WAV_HEADER_SIZE (4 + 4 + 4 + /* RIFF+size+WAVE */ \ @@ -87,14 +87,14 @@ static void WriteChunkType( unsigned char **addrPtr, unsigned long cktyp ) */ long Audio_WAV_OpenWriter( WAV_Writer *writer, const char *fileName, int frameRate, int samplesPerFrame ) { - unsigned int bytesPerSecond; + unsigned int bytesPerSecond; unsigned char header[ WAV_HEADER_SIZE ]; - unsigned char *addr = header; + unsigned char *addr = header; int numWritten; - + writer->dataSize = 0; writer->dataSizeOffset = 0; - + writer->fid = fopen( fileName, "wb" ); if( writer->fid == NULL ) { @@ -102,35 +102,35 @@ long Audio_WAV_OpenWriter( WAV_Writer *writer, const char *fileName, int frameRa } /* Write RIFF header. */ - WriteChunkType( &addr, RIFF_ID ); + WriteChunkType( &addr, RIFF_ID ); /* Write RIFF size as zero for now. Will patch later. */ - WriteLongLE( &addr, 0 ); + WriteLongLE( &addr, 0 ); /* Write WAVE form ID. */ - WriteChunkType( &addr, WAVE_ID ); + WriteChunkType( &addr, WAVE_ID ); /* Write format chunk based on AudioSample structure. */ - WriteChunkType( &addr, FMT_ID ); + WriteChunkType( &addr, FMT_ID ); WriteLongLE( &addr, 16 ); WriteShortLE( &addr, WAVE_FORMAT_PCM ); - bytesPerSecond = frameRate * samplesPerFrame * sizeof( short); - WriteShortLE( &addr, (short) samplesPerFrame ); - WriteLongLE( &addr, frameRate ); - WriteLongLE( &addr, bytesPerSecond ); - WriteShortLE( &addr, (short) (samplesPerFrame * sizeof( short)) ); /* bytesPerBlock */ - WriteShortLE( &addr, (short) 16 ); /* bits per sample */ + bytesPerSecond = frameRate * samplesPerFrame * sizeof( short); + WriteShortLE( &addr, (short) samplesPerFrame ); + WriteLongLE( &addr, frameRate ); + WriteLongLE( &addr, bytesPerSecond ); + WriteShortLE( &addr, (short) (samplesPerFrame * sizeof( short)) ); /* bytesPerBlock */ + WriteShortLE( &addr, (short) 16 ); /* bits per sample */ /* Write ID and size for 'data' chunk. */ - WriteChunkType( &addr, DATA_ID ); + WriteChunkType( &addr, DATA_ID ); /* Save offset so we can patch it later. */ writer->dataSizeOffset = (int) (addr - header); - WriteLongLE( &addr, 0 ); + WriteLongLE( &addr, 0 ); numWritten = fwrite( header, 1, sizeof(header), writer->fid ); if( numWritten != sizeof(header) ) return -1; - return (int) numWritten; + return (int) numWritten; } /********************************************************************************* @@ -138,31 +138,31 @@ long Audio_WAV_OpenWriter( WAV_Writer *writer, const char *fileName, int frameRa * Returns bytes written or negative error code. */ long Audio_WAV_WriteShorts( WAV_Writer *writer, - short *samples, - int numSamples - ) + short *samples, + int numSamples + ) { - unsigned char buffer[2]; + unsigned char buffer[2]; unsigned char *bufferPtr; - int i; - short *p = samples; + int i; + short *p = samples; int numWritten; int bytesWritten; - if( numSamples <= 0 ) - { - return -1; - } + if( numSamples <= 0 ) + { + return -1; + } for( i=0; ifid ); if( numWritten != sizeof(buffer) ) return -1; - } + } bytesWritten = numSamples * sizeof(short); writer->dataSize += bytesWritten; - return (int) bytesWritten; + return (int) bytesWritten; } /********************************************************************************* @@ -171,7 +171,7 @@ long Audio_WAV_WriteShorts( WAV_Writer *writer, */ long Audio_WAV_CloseWriter( WAV_Writer *writer ) { - unsigned char buffer[4]; + unsigned char buffer[4]; unsigned char *bufferPtr; int numWritten; int riffSize; @@ -212,7 +212,7 @@ int main( void ) #define NUM_SAMPLES (200) short data[NUM_SAMPLES]; short saw = 0; - + for( i=0; i #include @@ -103,14 +103,14 @@ static int gNumFailed = 0; static int QaCallback( const void* inputBuffer, void* outputBuffer, unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo* timeInfo, - PaStreamCallbackFlags statusFlags, + const PaStreamCallbackTimeInfo* timeInfo, + PaStreamCallbackFlags statusFlags, void* userData ) { unsigned long i; unsigned char* out = (unsigned char *) outputBuffer; PaQaData* data = (PaQaData *) userData; - + (void)inputBuffer; /* Prevent "unused variable" warnings. */ /* Zero out buffer so we don't hear terrible noise. */ @@ -174,7 +174,7 @@ static int TestBadOpens( void ) PaStreamParameters ipp, opp; const PaDeviceInfo* info = NULL; - + /* Setup data for synthesis thread. */ myData.framesLeft = (unsigned long) (SAMPLE_RATE * 100); /* 100 seconds */ myData.numChannels = 1; @@ -222,7 +222,7 @@ static int TestBadOpens( void ) ipp.hostApiSpecificStreamInfo = opp.hostApiSpecificStreamInfo = NULL; ipp.sampleFormat = opp.sampleFormat = paFloat32; ipp.channelCount = 0; ipp.device = Pa_GetDefaultInputDevice(); - opp.channelCount = 0; opp.device = paNoDevice; /* And no output device, and no output channels. */ + opp.channelCount = 0; opp.device = paNoDevice; /* And no output device, and no output channels. */ HOPEFOR(((result = Pa_OpenStream(&stream, &ipp, NULL, SAMPLE_RATE, FRAMES_PER_BUFFER, paClipOff, QaCallback, &myData )) == paInvalidChannelCount)); @@ -274,7 +274,7 @@ static int TestBadOpens( void ) HOPEFOR(((result = Pa_OpenStream(&stream, NULL, &opp, 1.0, FRAMES_PER_BUFFER, /* 1 cycle per second (1 Hz) is too low. */ paClipOff, QaCallback, &myData )) == paInvalidSampleRate)); - + /*----------------------------- High sample rate: */ ipp.hostApiSpecificStreamInfo = opp.hostApiSpecificStreamInfo = NULL; ipp.sampleFormat = opp.sampleFormat = paFloat32; @@ -286,7 +286,7 @@ static int TestBadOpens( void ) /*----------------------------- NULL callback: */ /* NULL callback is valid in V19 -- it means use blocking read/write stream - + ipp.hostApiSpecificStreamInfo = opp.hostApiSpecificStreamInfo = NULL; ipp.sampleFormat = opp.sampleFormat = paFloat32; ipp.channelCount = 0; ipp.device = paNoDevice; @@ -366,7 +366,7 @@ static int TestBadActions( void ) SAMPLE_RATE, FRAMES_PER_BUFFER, paClipOff, QaCallback, &myData )) == paNoError)); } - + HOPEFOR(((deviceInfo = Pa_GetDeviceInfo(paNoDevice)) == NULL)); HOPEFOR(((deviceInfo = Pa_GetDeviceInfo(87654)) == NULL)); HOPEFOR(((result = Pa_StartStream(NULL)) == paBadStreamPtr)); @@ -392,7 +392,7 @@ int main(void); int main(void) { PaError result; - + EXPECT(((result = Pa_Initialize()) == paNoError)); TestBadOpens(); TestBadActions(); diff --git a/qa/paqa_latency.c b/qa/paqa_latency.c index 389c065..a70807b 100644 --- a/qa/paqa_latency.c +++ b/qa/paqa_latency.c @@ -1,7 +1,7 @@ /** @file paqa_latency.c - @ingroup qa_src - @brief Test latency estimates. - @author Ross Bencina + @ingroup qa_src + @brief Test latency estimates. + @author Ross Bencina @author Phil Burk */ /* @@ -32,13 +32,13 @@ */ /* - * The text above constitutes the entire PortAudio license; however, + * 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 + * 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 @@ -91,7 +91,7 @@ static int patestCallback( const void *inputBuffer, void *outputBuffer, (void) timeInfo; /* Prevent unused variable warnings. */ (void) statusFlags; (void) inputBuffer; - + if( data->minFramesPerBuffer > framesPerBuffer ) { data->minFramesPerBuffer = framesPerBuffer; @@ -100,7 +100,7 @@ static int patestCallback( const void *inputBuffer, void *outputBuffer, { data->maxFramesPerBuffer = framesPerBuffer; } - + /* Measure min and max output time stamp delta. */ if( data->callbackCount > 0 ) { @@ -115,7 +115,7 @@ static int patestCallback( const void *inputBuffer, void *outputBuffer, } } data->previousTimeInfo = *timeInfo; - + for( i=0; isine[data->left_phase]; /* left */ @@ -125,12 +125,12 @@ static int patestCallback( const void *inputBuffer, void *outputBuffer, data->right_phase += 3; /* higher pitch so we can distinguish left and right. */ if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE; } - + data->callbackCount += 1; return paContinue; } -PaError paqaCheckLatency( PaStreamParameters *outputParamsPtr, +PaError paqaCheckLatency( PaStreamParameters *outputParamsPtr, paTestData *dataPtr, double sampleRate, unsigned long framesPerBuffer ) { PaError err; @@ -142,7 +142,7 @@ PaError paqaCheckLatency( PaStreamParameters *outputParamsPtr, dataPtr->minDeltaDacTime = 9999999.0; dataPtr->maxDeltaDacTime = 0.0; dataPtr->callbackCount = 0; - + printf("Stream parameter: suggestedOutputLatency = %g\n", outputParamsPtr->suggestedLatency ); if( framesPerBuffer == paFramesPerBufferUnspecified ){ printf("Stream parameter: user framesPerBuffer = paFramesPerBufferUnspecified\n" ); @@ -159,7 +159,7 @@ PaError paqaCheckLatency( PaStreamParameters *outputParamsPtr, patestCallback, dataPtr ); if( err != paNoError ) goto error1; - + streamInfo = Pa_GetStreamInfo( stream ); printf("Stream info: inputLatency = %g\n", streamInfo->inputLatency ); printf("Stream info: outputLatency = %g\n", streamInfo->outputLatency ); @@ -169,7 +169,7 @@ PaError paqaCheckLatency( PaStreamParameters *outputParamsPtr, printf("Play for %d seconds.\n", NUM_SECONDS ); Pa_Sleep( NUM_SECONDS * 1000 ); - + printf(" minFramesPerBuffer = %4d\n", dataPtr->minFramesPerBuffer ); printf(" maxFramesPerBuffer = %4d\n", dataPtr->maxFramesPerBuffer ); printf(" minDeltaDacTime = %f\n", dataPtr->minDeltaDacTime ); @@ -181,7 +181,7 @@ PaError paqaCheckLatency( PaStreamParameters *outputParamsPtr, err = Pa_CloseStream( stream ); Pa_Sleep( 1 * 1000 ); - + printf("-------------------------------------\n"); return err; error2: @@ -245,26 +245,26 @@ static int paqaCheckMultipleSuggested( PaDeviceIndex deviceIndex, int isInput ) streamParameters.hostApiSpecificStreamInfo = NULL; streamParameters.sampleFormat = paFloat32; sampleRate = pdi->defaultSampleRate; - + printf(" lowLatency = %g\n", lowLatency ); printf(" highLatency = %g\n", highLatency ); printf(" numChannels = %d\n", numChannels ); printf(" sampleRate = %g\n", sampleRate ); - + if( (highLatency - lowLatency) < 0.001 ) { numLoops = 1; } for( i=0; iinputLatency; @@ -317,7 +317,7 @@ static int paqaVerifySuggestedLatency( void ) int result = 0; const PaDeviceInfo *pdi; int numDevices = Pa_GetDeviceCount(); - + printf("\n ------------------------ paqaVerifySuggestedLatency\n"); for( id=0; iddefaultLowOutputLatency; err = paqaCheckLatency( &outputParameters, &data, sampleRate, framesPerBuffer ); if( err != paNoError ) goto error; - + printf("------------- 64 frame buffer with 1.1 * defaultLow latency.\n"); framesPerBuffer = 64; outputParameters.suggestedLatency = deviceInfo->defaultLowOutputLatency * 1.1; err = paqaCheckLatency( &outputParameters, &data, sampleRate, framesPerBuffer ); if( err != paNoError ) goto error; - + // Try to create a huge buffer that is bigger than the allowed device maximum. printf("------------- Try a huge buffer.\n"); framesPerBuffer = 16*1024; outputParameters.suggestedLatency = ((double)framesPerBuffer) / sampleRate; // approximate err = paqaCheckLatency( &outputParameters, &data, sampleRate, framesPerBuffer ); if( err != paNoError ) goto error; - + printf("------------- Try suggestedLatency = 0.0\n"); outputParameters.suggestedLatency = 0.0; err = paqaCheckLatency( &outputParameters, &data, sampleRate, paFramesPerBufferUnspecified ); if( err != paNoError ) goto error; - + printf("------------- Try suggestedLatency = defaultLowOutputLatency\n"); outputParameters.suggestedLatency = deviceInfo->defaultLowOutputLatency; err = paqaCheckLatency( &outputParameters, &data, sampleRate, paFramesPerBufferUnspecified ); if( err != paNoError ) goto error; - + printf("------------- Try suggestedLatency = defaultHighOutputLatency\n"); outputParameters.suggestedLatency = deviceInfo->defaultHighOutputLatency; err = paqaCheckLatency( &outputParameters, &data, sampleRate, paFramesPerBufferUnspecified ); if( err != paNoError ) goto error; - + printf("------------- Try suggestedLatency = defaultHighOutputLatency * 4\n"); outputParameters.suggestedLatency = deviceInfo->defaultHighOutputLatency * 4; err = paqaCheckLatency( &outputParameters, &data, sampleRate, paFramesPerBufferUnspecified ); if( err != paNoError ) goto error; - + Pa_Terminate(); printf("SUCCESS - test finished.\n"); return err; - + error: Pa_Terminate(); fprintf( stderr, "ERROR - test failed.\n" );