]> Repos - portaudio/commitdiff
Implement the Float32 to UInt8 converters.
authorphilburk <philburk@0f58301d-fd10-0410-b4af-bbb618454e57>
Thu, 1 Sep 2011 22:08:32 +0000 (22:08 +0000)
committerphilburk <philburk@0f58301d-fd10-0410-b4af-bbb618454e57>
Thu, 1 Sep 2011 22:08:32 +0000 (22:08 +0000)
The loopback test now passes cleanly for the first time on my PreSonus FireStudio.
Added tests for clip and dither flag combinations to loopback.

qa/loopback/src/paqa.c
src/common/pa_converters.c
src/hostapi/coreaudio/pa_mac_core.c
test/patest_sine8.c

index 3ac6599f35c0c3f7095bdfb86542c8cd40945bbe..ad993a935b2b5e1f40465b8cce203c2ec71578ca 100644 (file)
@@ -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 )
index 92daf8f952acd855a266553dedd95705b7dfb2c6..2107f5e28913f00ad1ae6c0fceeda4c7a320f41f 100644 (file)
@@ -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;
index 0ebf20d092af75fb3decf4c64c919758f4a30bd1..bab668ce605fb0590497ca902795ddaa93c0d0a5 100644 (file)
@@ -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"));
 
index beeae9e3eda084022a73e172c6b9ad03b5ed9ee2..c00319a003b2a1f0ceea449b2ffd4b08addb2877 100644 (file)
 
 #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;
@@ -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. */