From a4dd0ad63c00f4dee3b86dfd3075d1d61b2b3180 Mon Sep 17 00:00:00 2001 From: sanine Date: Sat, 27 Aug 2022 23:52:56 -0500 Subject: add plibsys --- portaudio/qa/loopback/src/audio_analyzer.c | 706 ---------- portaudio/qa/loopback/src/audio_analyzer.h | 187 --- portaudio/qa/loopback/src/biquad_filter.c | 123 -- portaudio/qa/loopback/src/biquad_filter.h | 38 - portaudio/qa/loopback/src/paqa.c | 1601 ----------------------- portaudio/qa/loopback/src/paqa_tools.c | 171 --- portaudio/qa/loopback/src/paqa_tools.h | 52 - portaudio/qa/loopback/src/qa_tools.h | 83 -- portaudio/qa/loopback/src/test_audio_analyzer.c | 718 ---------- portaudio/qa/loopback/src/test_audio_analyzer.h | 46 - portaudio/qa/loopback/src/write_wav.c | 242 ---- portaudio/qa/loopback/src/write_wav.h | 103 -- 12 files changed, 4070 deletions(-) delete mode 100644 portaudio/qa/loopback/src/audio_analyzer.c delete mode 100644 portaudio/qa/loopback/src/audio_analyzer.h delete mode 100755 portaudio/qa/loopback/src/biquad_filter.c delete mode 100755 portaudio/qa/loopback/src/biquad_filter.h delete mode 100644 portaudio/qa/loopback/src/paqa.c delete mode 100644 portaudio/qa/loopback/src/paqa_tools.c delete mode 100644 portaudio/qa/loopback/src/paqa_tools.h delete mode 100755 portaudio/qa/loopback/src/qa_tools.h delete mode 100644 portaudio/qa/loopback/src/test_audio_analyzer.c delete mode 100644 portaudio/qa/loopback/src/test_audio_analyzer.h delete mode 100755 portaudio/qa/loopback/src/write_wav.c delete mode 100755 portaudio/qa/loopback/src/write_wav.h (limited to 'portaudio/qa/loopback/src') diff --git a/portaudio/qa/loopback/src/audio_analyzer.c b/portaudio/qa/loopback/src/audio_analyzer.c deleted file mode 100644 index fbdd631..0000000 --- a/portaudio/qa/loopback/src/audio_analyzer.c +++ /dev/null @@ -1,706 +0,0 @@ - -/* - * PortAudio Portable Real-Time Audio Library - * Latest Version at: http://www.portaudio.com - * - * Copyright (c) 1999-2010 Phil Burk and Ross Bencina - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/* - * The text above constitutes the entire PortAudio license; however, - * the PortAudio community also makes the following non-binding requests: - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. It is also - * requested that these non-binding requests be included along with the - * license above. - */ - -#include -#include -#include -#include -#include -#include "qa_tools.h" -#include "audio_analyzer.h" -#include "write_wav.h" - -#define PAQA_POP_THRESHOLD (0.04) - -/*==========================================================================================*/ -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 ); -} - -/*==========================================================================================*/ -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; -} - -/*==========================================================================================*/ -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; - } - } -} - -/*==========================================================================================*/ -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; -error: - return 1; -} - -/*==========================================================================================*/ -void PaQa_TerminateRecording( PaQaRecording *recording ) -{ - 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; - 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); -} - -/*==========================================================================================*/ -int PaQa_WriteSilence( PaQaRecording *recording, int numFrames ) -{ - 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); -} - -/*==========================================================================================*/ -int PaQa_RecordFreeze( PaQaRecording *recording, int numFrames ) -{ - 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); -} - -/*==========================================================================================*/ -/** - * Write recording to WAV file. - */ -int PaQa_SaveRecordingToWaveFile( PaQaRecording *recording, const char *filename ) -{ - WAV_Writer writer; - int result = 0; -#define NUM_SAMPLES (200) - short data[NUM_SAMPLES]; - const int samplesPerFrame = 1; - int numLeft = recording->numFrames; - float *buffer = &recording->buffer[0]; - - result = Audio_WAV_OpenWriter( &writer, filename, recording->sampleRate, samplesPerFrame ); - if( result < 0 ) goto error; - - while( numLeft > 0 ) - { - 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 ); - if( result < 0 ) goto error; - numLeft -= numToSave; - } - - result = Audio_WAV_CloseWriter( &writer ); - if( result < 0 ) goto error; - - return 0; - -error: - printf("ERROR: result = %d\n", result ); - return result; -#undef NUM_SAMPLES -} - -/*==========================================================================================*/ - -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; -} - -/*==========================================================================================*/ -/* - * We can't just measure the peaks cuz they may be clipped. - * But the zero crossing should be intact. - * The measured slope of a sine wave at zero should be: - * - * slope = sin( 2PI * frequency / sampleRate ) - * - */ -double PaQa_MeasureSineAmplitudeBySlope( PaQaRecording *recording, - 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; -} - -/*==========================================================================================*/ -double PaQa_CorrelateSine( PaQaRecording *recording, double frequency, double frameRate, - int startFrame, int numFrames, double *phasePtr ) -{ - 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]; - - 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; -error: - 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; -} - -/*==========================================================================================*/ -/** Scan until we get a correlation of a single that goes over the tolerance level, - * peaks then drops to half the peak. - * Look for inverse correlation as well. - */ -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; - - 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; -error: - 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) ); - - { - 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; -} - - -/*==========================================================================================*/ -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; -} - -/*==========================================================================================*/ -int PaQa_MeasureLatency( PaQaRecording *recording, PaQaTestTone *testTone, PaQaAnalysisResult *analysisResult ) -{ - 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; -error: - 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; - - assert( startFrame >= 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; - - phase += phaseIncrement; - } -} - - -/*==========================================================================================*/ -/** 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; - 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; - -error: - 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; - - // 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; -} - -/*==========================================================================================*/ -int PaQa_AnalyseRecording( PaQaRecording *recording, PaQaTestTone *testTone, PaQaAnalysisResult *analysisResult ) -{ - int result = 0; - - 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; -error: - return -1; -} diff --git a/portaudio/qa/loopback/src/audio_analyzer.h b/portaudio/qa/loopback/src/audio_analyzer.h deleted file mode 100644 index 8d9f1ee..0000000 --- a/portaudio/qa/loopback/src/audio_analyzer.h +++ /dev/null @@ -1,187 +0,0 @@ - -/* - * PortAudio Portable Real-Time Audio Library - * Latest Version at: http://www.portaudio.com - * - * Copyright (c) 1999-2010 Phil Burk and Ross Bencina - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/* - * The text above constitutes the entire PortAudio license; however, - * the PortAudio community also makes the following non-binding requests: - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. It is also - * requested that these non-binding requests be included along with the - * license above. - */ - -#ifndef _AUDIO_ANALYZER_H -#define _AUDIO_ANALYZER_H - -#include "biquad_filter.h" - -#define MATH_PI (3.141592653589793238462643) -#define MATH_TWO_PI (2.0 * MATH_PI) - -typedef struct PaQaSineGenerator_s -{ - double phase; - double phaseIncrement; - double frequency; - double amplitude; -} PaQaSineGenerator; - -/** 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; -} PaQaRecording; - -typedef struct PaQaTestTone_s -{ - 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; -} PaQaAnalysisResult; - - -/*================================================================*/ -/*================= General DSP Tools ============================*/ -/*================================================================*/ -/** - * Calculate Nth frequency of a series for use in testing multiple channels. - * Series should avoid harmonic overlap between channels. - */ -double PaQa_GetNthFrequency( double baseFrequency, int index ); - -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 ); - -/** - * Generate a signal with a sharp edge in the middle that can be recognized despite some phase shift. - */ -void PaQa_GenerateCrack( float *buffer, int numSamples, int stride ); - -double PaQa_ComputePhaseDifference( double phase1, double phase2 ); - -/** - * Measure the area under the curve by summing absolute value of each value. - */ -double PaQa_MeasureArea( float *buffer, int numFrames, int stride ); - -/** - * Measure slope of the positive zero crossings. - */ -double PaQa_MeasureCrossingSlope( float *buffer, int numFrames ); - - -/** - * Prepare an oscillator that can generate a sine tone for testing. - */ -void PaQa_SetupSineGenerator( PaQaSineGenerator *generator, double frequency, double amplitude, double frameRate ); - -/*================================================================*/ -/*================= Recordings ===================================*/ -/*================================================================*/ -/** - * 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. - */ -void PaQa_FilterRecording( PaQaRecording *input, PaQaRecording *output, BiquadFilter *filter ); - - -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 ); - -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 PaQa_MeasureRootMeanSquare( float *buffer, int numFrames ); - -/** - * Compare the amplitudes of these two signals. - * Return ratio of recorded signal over buffer signal. - */ -double PaQa_CompareAmplitudes( PaQaRecording *recording, int startAt, float *buffer, int numSamples ); - -/** - * Analyse a recording of a sine wave. - * Measure latency and look for dropped frames, etc. - */ -int PaQa_AnalyseRecording( PaQaRecording *recording, PaQaTestTone *testTone, PaQaAnalysisResult *analysisResult ); - -#endif /* _AUDIO_ANALYZER_H */ diff --git a/portaudio/qa/loopback/src/biquad_filter.c b/portaudio/qa/loopback/src/biquad_filter.c deleted file mode 100755 index 1715fa3..0000000 --- a/portaudio/qa/loopback/src/biquad_filter.c +++ /dev/null @@ -1,123 +0,0 @@ -#include -#include - -#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) - * - * @author (C) 2002 Phil Burk, SoftSynth.com, All Rights Reserved - */ - -#define FILTER_PI (3.141592653589793238462643) -/*********************************************************** -** Calculate coefficients common to many parametric biquad filters. -*/ -static void BiquadFilter_CalculateCommon( BiquadFilter *filter, double ratio, double Q ) -{ - 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; - - filter->cos_omega = (double) cos( omega ); - filter->sin_omega = (double) sin( omega ); - filter->alpha = filter->sin_omega / (2.0 * Q); -} - -/********************************************************************************* - ** Calculate coefficients for Highpass filter. - */ -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; - filter->a2 = filter->a0; - filter->b1 = -2.0 * filter->cos_omega * scalar; - filter->b2 = (1.0 - filter->alpha) * scalar; -} - - -/********************************************************************************* - ** Calculate coefficients for Notch filter. - */ -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; - filter->a2 = filter->a0; - filter->b1 = filter->a1; - filter->b2 = (1.0 - filter->alpha) * scalar; -} - -/***************************************************************** -** Perform core IIR filter calculation without permutation. -*/ -void BiquadFilter_Filter( BiquadFilter *filter, float *inputs, float *outputs, int numSamples ) -{ - 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; ixn1 = xn1; - filter->xn2 = xn2; - filter->yn1 = yn1; - filter->yn2 = yn2; -} diff --git a/portaudio/qa/loopback/src/biquad_filter.h b/portaudio/qa/loopback/src/biquad_filter.h deleted file mode 100755 index 0895aba..0000000 --- a/portaudio/qa/loopback/src/biquad_filter.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef _BIQUADFILTER_H -#define _BIQUADFILTER_H - - -/** - * Unit_BiquadFilter implements a second order IIR filter. - * - * @author (C) 2002 Phil Burk, SoftSynth.com, All Rights Reserved - */ - -#define BIQUAD_MIN_RATIO (0.000001) -#define BIQUAD_MIN_Q (0.00001) - -typedef struct BiquadFilter_s -{ - double xn1; // storage for delayed signals - double xn2; - double yn1; - double yn2; - - double a0; // coefficients - double a1; - double a2; - - double b1; - double b2; - - double cos_omega; - double sin_omega; - double alpha; -} BiquadFilter; - -void BiquadFilter_SetupHighPass( BiquadFilter *filter, double ratio, double Q ); -void BiquadFilter_SetupNotch( BiquadFilter *filter, double ratio, double Q ); - -void BiquadFilter_Filter( BiquadFilter *filter, float *inputs, float *outputs, int numSamples ); - -#endif diff --git a/portaudio/qa/loopback/src/paqa.c b/portaudio/qa/loopback/src/paqa.c deleted file mode 100644 index 5eb6283..0000000 --- a/portaudio/qa/loopback/src/paqa.c +++ /dev/null @@ -1,1601 +0,0 @@ - -/* - * PortAudio Portable Real-Time Audio Library - * Latest Version at: http://www.portaudio.com - * - * Copyright (c) 1999-2010 Phil Burk and Ross Bencina - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/* - * The text above constitutes the entire PortAudio license; however, - * the PortAudio community also makes the following non-binding requests: - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. It is also - * requested that these non-binding requests be included along with the - * license above. - */ - -#include -#include -#include -#include -#include - -#include "portaudio.h" - -#include "qa_tools.h" - -#include "paqa_tools.h" -#include "audio_analyzer.h" -#include "test_audio_analyzer.h" - -/** Accumulate counts for how many tests pass or fail. */ -int g_testsPassed = 0; -int g_testsFailed = 0; - -#define MAX_NUM_GENERATORS (8) -#define MAX_NUM_RECORDINGS (8) -#define MAX_BACKGROUND_NOISE_RMS (0.0004) -#define LOOPBACK_DETECTION_DURATION_SECONDS (0.8) -#define DEFAULT_FRAMES_PER_BUFFER (0) -#define PAQA_WAIT_STREAM_MSEC (100) -#define PAQA_TEST_DURATION (1.2) - -// Use two separate streams instead of one full duplex stream. -#define PAQA_FLAG_TWO_STREAMS (1<<0) -// Use bloching read/write for loopback. -#define PAQA_FLAG_USE_BLOCKING_IO (1<<1) - -const char * s_FlagOnNames[] = -{ - "Two Streams (Half Duplex)", - "Blocking Read/Write" -}; - -const char * s_FlagOffNames[] = -{ - "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; - PaStreamFlags streamFlags; // paClipOff, etc - 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; - - // 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; -} 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; -} UserOptions; - -#define BIG_BUFFER_SIZE (sizeof(float) * 2 * 2 * 1024) -static unsigned char g_ReadWriteBuffer[BIG_BUFFER_SIZE]; - -#define MAX_CONVERSION_SAMPLES (2 * 32 * 1024) -#define CONVERSION_BUFFER_SIZE (sizeof(float) * 2 * MAX_CONVERSION_SAMPLES) -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 ) -{ - 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 ); - } - - } - - // 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 ) - { - loopbackContext->maxInputOutputDelta = inputOutputDelta; - } - if( loopbackContext->minInputOutputDelta > inputOutputDelta ) - { - loopbackContext->minInputOutputDelta = inputOutputDelta; - } - } - - 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 ); - - 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. - * @return 0 if OK or negative error. - */ -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; - -error: - 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. - * @return 0 if OK or paTimedOut. - */ - -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; -} - -/*******************************************************************/ -/** - * 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. - * @return 0 if OK or negative error. - */ -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; - -error: - return err; -} - - -/*******************************************************************/ -/** - * Open one audio streams, just for input. - * Record background level. - * Then close the stream. - * @return 0 if OK or negative 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; - -error: - 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; -error: - 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. - * @return 0 if OK or negative 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; - -error3: - Pa_CloseStream( outStream ); -error2: - Pa_CloseStream( inStream ); -error1: - 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. - * @return 0 if OK or negative error. - */ -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; - -error2: - Pa_CloseStream( stream ); -error1: - 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; -} - -/*******************************************************************/ -static int PaQa_SaveTestResultToWaveFile( UserOptions *userOptions, PaQaRecording *recording ) -{ - 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; -} - -/*******************************************************************/ -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; -error: - 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] ); - } - } -} - -/*******************************************************************/ -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 ); - } - } -} - -/*******************************************************************/ -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 ); -} - -/*******************************************************************/ -/** - * 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 ) - { - 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" ); - } - - // 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; - -error: - 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; - 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; -} - -/*******************************************************************/ -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) ); -} - -/*******************************************************************/ -/** - * 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 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)); - - 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; iRateframesPerBuffer < 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 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; -} - -/*******************************************************************/ -/** - * 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; - 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. - 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; - -error: - PaQa_TeardownLoopbackContext( &loopbackContext ); - return err; -} - -/*******************************************************************/ -/** - * If there is a loopback connection then run the analysis. - */ -static int CheckLoopbackAndScan( UserOptions *userOptions, - PaDeviceIndex iIn, PaDeviceIndex iOut ) -{ - 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. - * If a loopback is found the analyse the combination. - * The scan can be overridden using the -i and -o command line options. - */ -static int ScanForLoopback(UserOptions *userOptions) -{ - 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; - -error: - 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; - -error: - 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"); -} - -/*******************************************************************/ -int main( int argc, char **argv ) -{ - int i; - 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( imaxInputChannels ); - printf( ", %2d out", deviceInfo->maxOutputChannels ); - 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; - } - -} - -/*******************************************************************/ -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 -#include "portaudio.h" - -void PaQa_ListAudioDevices(void); - -void PaQa_ConvertToFloat( const void *input, int numSamples, PaSampleFormat inFormat, float *output ); - -void PaQa_ConvertFromFloat( const float *input, int numSamples, PaSampleFormat outFormat, void *output ); - -#endif /* _PAQA_TOOLS_H */ diff --git a/portaudio/qa/loopback/src/qa_tools.h b/portaudio/qa/loopback/src/qa_tools.h deleted file mode 100755 index 9b2debd..0000000 --- a/portaudio/qa/loopback/src/qa_tools.h +++ /dev/null @@ -1,83 +0,0 @@ - -/* - * PortAudio Portable Real-Time Audio Library - * Latest Version at: http://www.portaudio.com - * - * Copyright (c) 1999-2010 Phil Burk and Ross Bencina - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/* - * The text above constitutes the entire PortAudio license; however, - * the PortAudio community also makes the following non-binding requests: - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. It is also - * requested that these non-binding requests be included along with the - * license above. - */ - -#ifndef _QA_TOOLS_H -#define _QA_TOOLS_H - -extern int g_testsPassed; -extern int g_testsFailed; - -#define QA_ASSERT_TRUE( message, flag ) \ - if( !(flag) ) \ - { \ - printf( "%s:%d - ERROR - %s\n", __FILE__, __LINE__, message ); \ - g_testsFailed++; \ - goto error; \ - } \ - else g_testsPassed++; - - -#define QA_ASSERT_EQUALS( message, expected, actual ) \ - if( ((expected) != (actual)) ) \ - { \ - printf( "%s:%d - ERROR - %s, expected %d, got %d\n", __FILE__, __LINE__, message, expected, actual ); \ - g_testsFailed++; \ - goto error; \ - } \ - else g_testsPassed++; - -#define QA_ASSERT_CLOSE( message, expected, actual, tolerance ) \ - 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)) \ - { \ - printf( "%s:%d - ERROR - %s, expected %d, got %d, tol=%d\n", __FILE__, __LINE__, message, ((int)(expected)), ((int)(actual)), ((int)(tolerance)) ); \ - g_testsFailed++; \ - goto error; \ - } \ - else g_testsPassed++; - - -#endif diff --git a/portaudio/qa/loopback/src/test_audio_analyzer.c b/portaudio/qa/loopback/src/test_audio_analyzer.c deleted file mode 100644 index 82fa859..0000000 --- a/portaudio/qa/loopback/src/test_audio_analyzer.c +++ /dev/null @@ -1,718 +0,0 @@ - -/* - * PortAudio Portable Real-Time Audio Library - * Latest Version at: http://www.portaudio.com - * - * Copyright (c) 1999-2010 Phil Burk and Ross Bencina - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/* - * The text above constitutes the entire PortAudio license; however, - * the PortAudio community also makes the following non-binding requests: - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. It is also - * requested that these non-binding requests be included along with the - * license above. - */ - -#include -#include -#include -#include "qa_tools.h" -#include "audio_analyzer.h" -#include "test_audio_analyzer.h" -#include "write_wav.h" -#include "biquad_filter.h" - -#define FRAMES_PER_BLOCK (64) -#define PRINT_REPORTS 0 - -#define TEST_SAVED_WAVE (0) - -/*==========================================================================================*/ -/** - * Detect a single tone. - */ -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 done = 0; - - 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; - -error: - PaQa_TerminateRecording( &recording); - return 1; - -} - -/*==========================================================================================*/ -/** - * Mix multiple tones and then detect them. - */ - -static int TestMixedMonoTones( void ) -{ - 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; - - 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 ); - - // 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; - } -} - - -/*==========================================================================================*/ -/** - * Generate a clean recording. - */ - -static void MakeCleanRecording( PaQaRecording *recording, PaQaTestTone *testTone ) -{ - 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 ); - } -} - -/*==========================================================================================*/ -/** - * Generate a recording with pop. - */ - -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; - } -} - -/*==========================================================================================*/ -/** - * Detect one phase error in a recording. - */ -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 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; - } - -#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 ); -#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; - -error: - PaQa_TerminateRecording( &recording); - return 1; -} - -/*==========================================================================================*/ -/** - * Test various dropped sample scenarios. - */ -static int TestDetectPhaseErrors( void ) -{ - 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; -*/ - return 0; -} - -/*==========================================================================================*/ -/** - * Detect one pop in a recording. - */ -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 ); - -#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 ); -#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; - -error: - PaQa_SaveRecordingToWaveFile( &recording, "bad_recording.wav" ); - PaQa_TerminateRecording( &recording); - return 1; -} - -/*==========================================================================================*/ -/** - * Analyse recording with a DC offset. - */ -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 -#include -#include "write_wav.h" - - -/* Write long word data to a little endian format byte array. */ -static void WriteLongLE( unsigned char **addrPtr, unsigned long data ) -{ - 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; -} - -/* 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; -} - -#define WAV_HEADER_SIZE (4 + 4 + 4 + /* RIFF+size+WAVE */ \ - 4 + 4 + 16 + /* fmt chunk */ \ - 4 + 4 ) /* data chunk */ - - -/********************************************************************************* - * Open named file and write WAV header to the file. - * The header includes the DATA chunk type and size. - * Returns number of bytes written to file or negative error code. - */ -long Audio_WAV_OpenWriter( WAV_Writer *writer, const char *fileName, int frameRate, int samplesPerFrame ) -{ - unsigned int bytesPerSecond; - unsigned char header[ WAV_HEADER_SIZE ]; - unsigned char *addr = header; - int numWritten; - - writer->dataSize = 0; - writer->dataSizeOffset = 0; - - writer->fid = fopen( fileName, "wb" ); - if( writer->fid == NULL ) - { - return -1; - } - -/* Write RIFF header. */ - WriteChunkType( &addr, RIFF_ID ); - -/* Write RIFF size as zero for now. Will patch later. */ - WriteLongLE( &addr, 0 ); - -/* Write WAVE form ID. */ - WriteChunkType( &addr, WAVE_ID ); - -/* Write format chunk based on AudioSample structure. */ - 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 */ - -/* Write ID and size for 'data' chunk. */ - WriteChunkType( &addr, DATA_ID ); -/* Save offset so we can patch it later. */ - writer->dataSizeOffset = (int) (addr - header); - WriteLongLE( &addr, 0 ); - - numWritten = fwrite( header, 1, sizeof(header), writer->fid ); - if( numWritten != sizeof(header) ) return -1; - - return (int) numWritten; -} - -/********************************************************************************* - * Write to the data chunk portion of a WAV file. - * Returns bytes written or negative error code. - */ -long Audio_WAV_WriteShorts( WAV_Writer *writer, - short *samples, - int numSamples - ) -{ - unsigned char buffer[2]; - unsigned char *bufferPtr; - int i; - short *p = samples; - int numWritten; - int bytesWritten; - 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; -} - -/********************************************************************************* - * Close WAV file. - * Update chunk sizes so it can be read by audio applications. - */ -long Audio_WAV_CloseWriter( WAV_Writer *writer ) -{ - unsigned char buffer[4]; - unsigned char *bufferPtr; - int numWritten; - int riffSize; - - /* Go back to beginning of file and update DATA size */ - int result = fseek( writer->fid, writer->dataSizeOffset, SEEK_SET ); - if( result < 0 ) return result; - - bufferPtr = buffer; - WriteLongLE( &bufferPtr, writer->dataSize ); - numWritten = fwrite( buffer, 1, sizeof( buffer), writer->fid ); - if( numWritten != sizeof(buffer) ) return -1; - - /* Update RIFF size */ - result = fseek( writer->fid, 4, SEEK_SET ); - if( result < 0 ) return result; - - riffSize = writer->dataSize + (WAV_HEADER_SIZE - 8); - bufferPtr = buffer; - WriteLongLE( &bufferPtr, riffSize ); - numWritten = fwrite( buffer, 1, sizeof( buffer), writer->fid ); - if( numWritten != sizeof(buffer) ) return -1; - - fclose( writer->fid ); - writer->fid = NULL; - return writer->dataSize; -} - -/********************************************************************************* - * Simple test that write a sawtooth waveform to a file. - */ -#if 0 -int main( void ) -{ - int i; - WAV_Writer writer; - int result; -#define NUM_SAMPLES (200) - short data[NUM_SAMPLES]; - short saw = 0; - - for( i=0; i