Implement the Float32 to UInt8 converters.
The loopback test now passes cleanly for the first time on my PreSonus FireStudio. Added tests for clip and dither flag combinations to loopback.
This commit is contained in:
parent
a1dabb01fe
commit
9cdd56f5b4
4 changed files with 88 additions and 50 deletions
|
|
@ -91,7 +91,8 @@ typedef struct TestParameters_s
|
|||
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
|
||||
|
|
@ -106,15 +107,19 @@ 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;
|
||||
|
|
@ -239,7 +244,22 @@ static int RecordAndPlaySinesCallback( const void *inputBuffer, void *outputBuff
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
|
@ -350,7 +370,7 @@ int PaQa_RunLoopbackHalfDuplex( LoopbackContext *loopbackContext )
|
|||
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;
|
||||
|
|
@ -360,7 +380,7 @@ int PaQa_RunLoopbackHalfDuplex( LoopbackContext *loopbackContext )
|
|||
&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;
|
||||
|
|
@ -870,8 +890,9 @@ static int PaQa_SingleLoopBackTest( UserOptions *userOptions, TestParameters *te
|
|||
{
|
||||
printf( "OK" );
|
||||
}
|
||||
|
||||
|
||||
printf( "\n" );
|
||||
|
||||
|
||||
PaQa_TeardownLoopbackContext( &loopbackContext );
|
||||
if( numBadChannels > 0 )
|
||||
|
|
@ -899,6 +920,7 @@ static void PaQa_SetDefaultTestParameters( TestParameters *testParamsPtr, PaDevi
|
|||
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;
|
||||
|
|
@ -1038,18 +1060,27 @@ static int PaQa_AnalyzeLoopbackConnection( UserOptions *userOptions, PaDeviceInd
|
|||
}
|
||||
|
||||
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");
|
||||
|
||||
|
|
@ -1159,7 +1190,7 @@ int PaQa_CheckForLoopBack( UserOptions *userOptions, PaDeviceIndex inputDevice,
|
|||
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 )
|
||||
|
|
|
|||
|
|
@ -49,8 +49,7 @@
|
|||
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"
|
||||
|
|
@ -729,8 +728,7 @@ static void Float32_To_Int8_Dither(
|
|||
{
|
||||
float *src = (float*)sourceBuffer;
|
||||
signed char *dest = (signed char*)destinationBuffer;
|
||||
(void)ditherGenerator; /* unused parameter */
|
||||
|
||||
|
||||
while( count-- )
|
||||
{
|
||||
float dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
|
||||
|
|
@ -821,12 +819,15 @@ static void Float32_To_UInt8_Dither(
|
|||
{
|
||||
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;
|
||||
}
|
||||
|
|
@ -845,7 +846,9 @@ static void Float32_To_UInt8_Clip(
|
|||
|
||||
while( count-- )
|
||||
{
|
||||
/* IMPLEMENT ME */
|
||||
PaInt32 samp = 128 + (PaInt32)(*src * (127.0f));
|
||||
PA_CLIP_( samp, 0x0000, 0x00FF );
|
||||
*dest = (unsigned char) samp;
|
||||
|
||||
src += sourceStride;
|
||||
dest += destinationStride;
|
||||
|
|
@ -865,7 +868,12 @@ static void Float32_To_UInt8_DitherClip(
|
|||
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -1009,6 +1009,10 @@ static OSStatus UpdateSampleRateFromDeviceProperty( PaMacCoreStream *stream, Aud
|
|||
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 )
|
||||
{
|
||||
|
|
@ -1034,6 +1038,10 @@ static OSStatus AudioDevicePropertyGenericListenerProc( AudioDeviceID inDevice,
|
|||
{
|
||||
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 )
|
||||
|
|
@ -2107,7 +2115,7 @@ static OSStatus AudioIOProc( void *inRefCon,
|
|||
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"));
|
||||
|
||||
|
|
|
|||
|
|
@ -52,8 +52,12 @@
|
|||
|
||||
#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
|
||||
|
|
@ -62,11 +66,7 @@
|
|||
|
||||
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;
|
||||
|
|
@ -84,7 +84,7 @@ static int patestCallback( const void *inputBuffer, void *outputBuffer,
|
|||
void *userData )
|
||||
{
|
||||
paTestData *data = (paTestData*)userData;
|
||||
char *out = (char*)outputBuffer;
|
||||
sample_t *out = (sample_t*)outputBuffer;
|
||||
int i;
|
||||
int framesToCalc;
|
||||
int finished = 0;
|
||||
|
|
@ -114,14 +114,8 @@ static int patestCallback( const void *inputBuffer, void *outputBuffer,
|
|||
/* 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;
|
||||
}
|
||||
|
|
@ -145,10 +139,7 @@ int main(void)
|
|||
/* 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. */
|
||||
|
|
|
|||
Loading…
Reference in a new issue