The loopback test now passes cleanly for the first time on my PreSonus FireStudio.
Added tests for clip and dither flag combinations to loopback.
int maxFrames;
double baseFrequency;
double amplitude;
- int flags;
+ PaStreamFlags streamFlags; // paClipOff, etc
+ int flags; // PAQA_FLAG_TWO_STREAMS, PAQA_FLAG_USE_BLOCKING_IO
} TestParameters;
typedef struct LoopbackContext_s
PaTime streamInfoOutputLatency;
// Measured at runtime.
- int callbackCount; // incremented for each callback
- int inputBufferCount; // incremented if input buffer not NULL
+ volatile int callbackCount; // incremented for each callback
+ volatile int inputBufferCount; // incremented if input buffer not NULL
int inputUnderflowCount;
int inputOverflowCount;
- int outputBufferCount; // incremented if output buffer not NULL
+ 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;
}
}
-
+
+ // 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;
}
NULL,
test->sampleRate,
test->framesPerBuffer,
- paClipOff, /* we won't output out of range samples so don't bother clipping them */
+ test->streamFlags,
RecordAndPlaySinesCallback,
loopbackContext );
if( err != paNoError ) goto error;
&test->outputParameters,
test->sampleRate,
test->framesPerBuffer,
- paClipOff, /* we won't output out of range samples so don't bother clipping them */
+ test->streamFlags,
RecordAndPlaySinesCallback,
loopbackContext );
if( err != paNoError ) goto error;
{
printf( "OK" );
}
-
+
printf( "\n" );
+
PaQa_TeardownLoopbackContext( &loopbackContext );
if( numBadChannels > 0 )
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;
}
printf("\nTest Sample Formats using Half Duplex IO -----\n" );
- testParams.flags = PAQA_FLAG_TWO_STREAMS;
-
- for( iFormat=0; iFormat<numSampleFormats; iFormat++ )
- {
- int numBadChannels;
- PaSampleFormat format = sampleFormats[ iFormat ];
- testParams.inputParameters.sampleFormat = format;
- testParams.outputParameters.sampleFormat = format;
- printf("Sample format = %d = %s\n", (int) format, sampleFormatNames[iFormat] );
- numBadChannels = PaQa_SingleLoopBackTest( userOptions, &testParams );
- totalBadChannels += numBadChannels;
- }
+
+ PaQa_SetDefaultTestParameters( &testParams, inputDevice, outputDevice );
+ testParams.flags = PAQA_FLAG_TWO_STREAMS;
+ for( int iFlags= 0; iFlags<4; iFlags++ )
+ {
+ // Cycle through combinations of flags.
+ testParams.streamFlags = 0;
+ if( iFlags & 1 ) testParams.streamFlags |= paClipOff;
+ if( iFlags & 2 ) testParams.streamFlags |= paDitherOff;
+
+ for( iFormat=0; iFormat<numSampleFormats; iFormat++ )
+ {
+ int numBadChannels;
+ PaSampleFormat format = sampleFormats[ iFormat ];
+ testParams.inputParameters.sampleFormat = format;
+ testParams.outputParameters.sampleFormat = format;
+ printf("Sample format = %d = %s, PaStreamFlags = 0x%02X\n", (int) format, sampleFormatNames[iFormat], (unsigned int) testParams.streamFlags );
+ numBadChannels = PaQa_SingleLoopBackTest( userOptions, &testParams );
+ totalBadChannels += numBadChannels;
+ }
+ }
printf( "\n" );
printf( "****************************************\n");
PaQa_OverrideTestParameters( &testParams, userOptions );
testParams.maxFrames = (int) (LOOPBACK_DETECTION_DURATION_SECONDS * testParams.sampleRate);
- minAmplitude = testParams.amplitude / 2.0;
+ minAmplitude = testParams.amplitude / 4.0;
// Check to see if the selected formats are supported.
if( Pa_IsFormatSupported( &testParams.inputParameters, NULL, testParams.sampleRate ) != paFormatIsSupported )
see: "require clipping for dithering sample conversion functions?"
http://www.portaudio.com/trac/ticket/112
- @todo implement the converters marked IMPLEMENT ME: Float32_To_UInt8_Dither,
- Float32_To_UInt8_Clip, Float32_To_UInt8_DitherClip, Int32_To_Int24_Dither,
+ @todo implement the converters marked IMPLEMENT ME: Int32_To_Int24_Dither,
Int32_To_UInt8_Dither, Int24_To_Int16_Dither, Int24_To_Int8_Dither,
Int24_To_UInt8_Dither, Int16_To_Int8_Dither, Int16_To_UInt8_Dither
see: "some conversion functions are not implemented in pa_converters.c"
{
float *src = (float*)sourceBuffer;
signed char *dest = (signed char*)destinationBuffer;
- (void)ditherGenerator; /* unused parameter */
-
+
while( count-- )
{
float dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
{
float *src = (float*)sourceBuffer;
unsigned char *dest = (unsigned char*)destinationBuffer;
- (void)ditherGenerator; /* unused parameter */
-
+
while( count-- )
{
- /* IMPLEMENT ME */
-
+ float dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
+ /* use smaller scaler to prevent overflow when we add the dither */
+ float dithered = (*src * (126.0f)) + dither;
+ PaInt32 samp = (PaInt32) dithered;
+ *dest = (unsigned char) (128 + samp);
+
src += sourceStride;
dest += destinationStride;
}
while( count-- )
{
- /* IMPLEMENT ME */
+ PaInt32 samp = 128 + (PaInt32)(*src * (127.0f));
+ PA_CLIP_( samp, 0x0000, 0x00FF );
+ *dest = (unsigned char) samp;
src += sourceStride;
dest += destinationStride;
while( count-- )
{
- /* IMPLEMENT ME */
+ float dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
+ /* use smaller scaler to prevent overflow when we add the dither */
+ float dithered = (*src * (126.0f)) + dither;
+ PaInt32 samp = 128 + (PaInt32) dithered;
+ PA_CLIP_( samp, 0x0000, 0x00FF );
+ *dest = (unsigned char) samp;
src += sourceStride;
dest += destinationStride;
static OSStatus AudioDevicePropertyActualSampleRateListenerProc( AudioDeviceID inDevice, UInt32 inChannel, Boolean isInput, AudioDevicePropertyID inPropertyID, void *inClientData )
{
PaMacCoreStream *stream = (PaMacCoreStream*)inClientData;
+
+ // Make sure the callback is operating on a stream that is still valid!
+ assert( stream->streamRepresentation.magic == PA_STREAM_MAGIC );
+
OSStatus osErr = UpdateSampleRateFromDeviceProperty( stream, inDevice, isInput );
if( osErr == noErr )
{
{
OSStatus osErr = noErr;
PaMacCoreStream *stream = (PaMacCoreStream*)inClientData;
+
+ // Make sure the callback is operating on a stream that is still valid!
+ assert( stream->streamRepresentation.magic == PA_STREAM_MAGIC );
+
PaMacCoreDeviceProperties *deviceProperties = isInput ? &stream->inputProperties : &stream->outputProperties;
UInt32 *valuePtr = NULL;
switch( inPropertyID )
PaMacCoreStream *stream = (PaMacCoreStream*)inRefCon;
const bool isRender = inBusNumber == OUTPUT_ELEMENT;
int callbackResult = paContinue ;
- double hostTimeStampInPaTime = HOST_TIME_TO_PA_TIME(inTimeStamp->mHostTime);
+ double hostTimeStampInPaTime = HOST_TIME_TO_PA_TIME(inTimeStamp->mHostTime);
VVDBUG(("AudioIOProc()\n"));
#if TEST_UNSIGNED
#define TEST_FORMAT paUInt8
+typedef unsigned char sample_t;
+#define SILENCE ((sample_t)0x80)
#else
#define TEST_FORMAT paInt8
+typedef char sample_t;
+#define SILENCE ((sample_t)0x00)
#endif
#ifndef M_PI
typedef struct
{
-#if TEST_UNSIGNED
- unsigned char sine[TABLE_SIZE];
-#else
- char sine[TABLE_SIZE];
-#endif
+ sample_t sine[TABLE_SIZE];
int left_phase;
int right_phase;
unsigned int framesToGo;
void *userData )
{
paTestData *data = (paTestData*)userData;
- char *out = (char*)outputBuffer;
+ sample_t *out = (sample_t*)outputBuffer;
int i;
int framesToCalc;
int finished = 0;
/* zero remainder of final buffer */
for( ; i<(int)framesPerBuffer; i++ )
{
-#if TEST_UNSIGNED
- *out++ = (unsigned char) 0x80; /* left */
- *out++ = (unsigned char) 0x80; /* right */
-#else
- *out++ = 0; /* left */
- *out++ = 0; /* right */
-#endif
-
+ *out++ = SILENCE; /* left */
+ *out++ = SILENCE; /* right */
}
return finished;
}
/* initialise sinusoidal wavetable */
for( i=0; i<TABLE_SIZE; i++ )
{
- data.sine[i] = (char) (127.0 * sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. ));
-#if TEST_UNSIGNED
- data.sine[i] += (unsigned char) 0x80;
-#endif
+ data.sine[i] = SILENCE + (char) (127.0 * sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. ));
}
data.left_phase = data.right_phase = 0;
data.framesToGo = totalSamps = NUM_SECONDS * SAMPLE_RATE; /* Play for a few seconds. */