gearmulator

Emulation of classic VA synths of the late 90s/2000s that are based on Motorola 56300 family DSPs
Log | Files | Refs | Submodules | README | LICENSE

patest_converters.c (14941B)


      1 /** @file patest_converters.c
      2 	@ingroup test_src
      3 	@brief Tests the converter functions in pa_converters.c
      4 	@author Ross Bencina <rossb@audiomulch.com>
      5 
      6     Link with pa_dither.c and pa_converters.c
      7 
      8     see http://www.portaudio.com/trac/wiki/V19ConvertersStatus for a discussion of this.
      9 */
     10 /*
     11  * $Id: $
     12  *
     13  * This program uses the PortAudio Portable Audio Library.
     14  * For more information see: http://www.portaudio.com/
     15  * Copyright (c) 1999-2008 Ross Bencina and Phil Burk
     16  *
     17  * Permission is hereby granted, free of charge, to any person obtaining
     18  * a copy of this software and associated documentation files
     19  * (the "Software"), to deal in the Software without restriction,
     20  * including without limitation the rights to use, copy, modify, merge,
     21  * publish, distribute, sublicense, and/or sell copies of the Software,
     22  * and to permit persons to whom the Software is furnished to do so,
     23  * subject to the following conditions:
     24  *
     25  * The above copyright notice and this permission notice shall be
     26  * included in all copies or substantial portions of the Software.
     27  *
     28  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     29  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     30  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
     31  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
     32  * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
     33  * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
     34  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     35  */
     36 
     37 /*
     38  * The text above constitutes the entire PortAudio license; however, 
     39  * the PortAudio community also makes the following non-binding requests:
     40  *
     41  * Any person wishing to distribute modifications to the Software is
     42  * requested to send the modifications to the original developer so that
     43  * they can be incorporated into the canonical version. It is also 
     44  * requested that these non-binding requests be included along with the 
     45  * license above.
     46  */
     47 #include <stdio.h>
     48 #include <stdlib.h>
     49 #include <string.h>
     50 #include <math.h>
     51 
     52 #include "portaudio.h"
     53 #include "pa_converters.h"
     54 #include "pa_dither.h"
     55 #include "pa_types.h"
     56 #include "pa_endianness.h"
     57 
     58 #ifndef M_PI
     59 #define M_PI  (3.14159265)
     60 #endif
     61 
     62 #define MAX_PER_CHANNEL_FRAME_COUNT     (2048)
     63 #define MAX_CHANNEL_COUNT               (8)
     64 
     65 
     66 #define SAMPLE_FORMAT_COUNT (6)
     67 
     68 static PaSampleFormat sampleFormats_[ SAMPLE_FORMAT_COUNT ] = 
     69     { paFloat32, paInt32, paInt24, paInt16, paInt8, paUInt8 }; /* all standard PA sample formats */
     70 
     71 static const char* sampleFormatNames_[SAMPLE_FORMAT_COUNT] = 
     72     { "paFloat32", "paInt32", "paInt24", "paInt16", "paInt8", "paUInt8" };
     73 
     74 
     75 static const char* abbreviatedSampleFormatNames_[SAMPLE_FORMAT_COUNT] = 
     76     { "f32", "i32", "i24", "i16", " i8", "ui8" };
     77 
     78 
     79 PaError My_Pa_GetSampleSize( PaSampleFormat format );
     80 
     81 /*
     82     available flags are paClipOff and paDitherOff
     83     clipping is usually applied for float -> int conversions
     84     dither is usually applied for all downconversions (ie anything but 8bit->8bit conversions
     85 */
     86 
     87 static int CanClip( PaSampleFormat sourceFormat, PaSampleFormat destinationFormat )
     88 {
     89     if( sourceFormat == paFloat32 && destinationFormat != sourceFormat )
     90         return 1;
     91     else
     92         return 0;
     93 }
     94 
     95 static int CanDither( PaSampleFormat sourceFormat, PaSampleFormat destinationFormat )
     96 {
     97     if( sourceFormat < destinationFormat && sourceFormat != paInt8 )
     98         return 1;
     99     else
    100         return 0;
    101 }
    102 
    103 static void GenerateOneCycleSineReference( double *out, int frameCount, int strideFrames )
    104 {
    105     int i;
    106     for( i=0; i < frameCount; ++i ){
    107         *out = sin( ((double)i/(double)frameCount) * 2. * M_PI );
    108         out += strideFrames;
    109     }
    110 }
    111 
    112 
    113 static void GenerateOneCycleSine( PaSampleFormat format, void *buffer, int frameCount, int strideFrames )
    114 {
    115     switch( format ){
    116 
    117         case paFloat32:
    118             {
    119                 int i;
    120                 float *out = (float*)buffer;
    121                 for( i=0; i < frameCount; ++i ){
    122                     *out = (float).9 * sin( ((double)i/(double)frameCount) * 2. * M_PI );
    123                     out += strideFrames;
    124                 }
    125             }
    126             break;
    127         case paInt32:
    128             {
    129                 int i;
    130                 PaInt32 *out = (PaInt32*)buffer;
    131                 for( i=0; i < frameCount; ++i ){
    132                     *out = (PaInt32)(.9 * sin( ((double)i/(double)frameCount) * 2. * M_PI ) * 0x7FFFFFFF);
    133                     out += strideFrames;
    134                 }
    135             }
    136             break;
    137         case paInt24:
    138             {
    139                 int i;
    140                 unsigned char *out = (unsigned char*)buffer;
    141                 for( i=0; i < frameCount; ++i ){
    142                     signed long temp = (PaInt32)(.9 * sin( ((double)i/(double)frameCount) * 2. * M_PI ) * 0x7FFFFFFF);
    143                     
    144                     #if defined(PA_LITTLE_ENDIAN)
    145                             out[0] = (unsigned char)(temp >> 8) & 0xFF;
    146                             out[1] = (unsigned char)(temp >> 16) & 0xFF;
    147                             out[2] = (unsigned char)(temp >> 24) & 0xFF;
    148                     #elif defined(PA_BIG_ENDIAN)
    149                             out[0] = (unsigned char)(temp >> 24) & 0xFF;
    150                             out[1] = (unsigned char)(temp >> 16) & 0xFF;
    151                             out[2] = (unsigned char)(temp >> 8) & 0xFF;
    152                     #endif
    153                     out += 3;
    154                 }
    155             }
    156             break;
    157         case paInt16:
    158             {
    159                 int i;
    160                 PaInt16 *out = (PaInt16*)buffer;
    161                 for( i=0; i < frameCount; ++i ){
    162                     *out = (PaInt16)(.9 * sin( ((double)i/(double)frameCount) * 2. * M_PI ) * 0x7FFF );
    163                     out += strideFrames;
    164                 }
    165             }
    166             break;
    167         case paInt8:
    168             {
    169                 int i;
    170                 signed char *out = (signed char*)buffer;
    171                 for( i=0; i < frameCount; ++i ){
    172                     *out = (signed char)(.9 * sin( ((double)i/(double)frameCount) * 2. * M_PI ) * 0x7F );
    173                     out += strideFrames;
    174                 }
    175             }
    176             break;
    177         case paUInt8:
    178             {
    179                 int i;
    180                 unsigned char *out = (unsigned char*)buffer;
    181                 for( i=0; i < frameCount; ++i ){
    182                     *out = (unsigned char)( .5 * (1. + (.9 * sin( ((double)i/(double)frameCount) * 2. * M_PI ))) * 0xFF  );
    183                     out += strideFrames;
    184                 }
    185             }
    186             break;
    187     }
    188 }
    189 
    190 int TestNonZeroPresent( void *buffer, int size )
    191 {
    192     char *p = (char*)buffer;
    193     int i;
    194 
    195     for( i=0; i < size; ++i ){
    196     
    197         if( *p != 0 )
    198             return 1;
    199         ++p;
    200     }   
    201 
    202     return 0;
    203 }
    204 
    205 float MaximumAbsDifference( float* sourceBuffer, float* referenceBuffer, int count )
    206 {
    207     float result = 0;
    208     float difference;
    209     while( count-- ){
    210         difference = fabs( *sourceBuffer++ - *referenceBuffer++ );
    211         if( difference > result )
    212             result = difference;
    213     }
    214 
    215     return result;
    216 }  
    217 
    218 int main( const char **argv, int argc )
    219 {
    220     PaUtilTriangularDitherGenerator ditherState;
    221     PaUtilConverter *converter;
    222     void *destinationBuffer, *sourceBuffer;
    223     double *referenceBuffer;
    224     int sourceFormatIndex, destinationFormatIndex;
    225     PaSampleFormat sourceFormat, destinationFormat;
    226     PaStreamFlags flags;
    227     int passFailMatrix[SAMPLE_FORMAT_COUNT][SAMPLE_FORMAT_COUNT]; // [source][destination]
    228     float noiseAmplitudeMatrix[SAMPLE_FORMAT_COUNT][SAMPLE_FORMAT_COUNT]; // [source][destination]
    229     float amp;
    230 
    231 #define FLAG_COMBINATION_COUNT (4)
    232     PaStreamFlags flagCombinations[FLAG_COMBINATION_COUNT] = { paNoFlag, paClipOff, paDitherOff, paClipOff | paDitherOff };
    233     const char *flagCombinationNames[FLAG_COMBINATION_COUNT] = { "paNoFlag", "paClipOff", "paDitherOff", "paClipOff | paDitherOff" };
    234     int flagCombinationIndex;
    235 
    236     PaUtil_InitializeTriangularDitherState( &ditherState );
    237 
    238     /* allocate more than enough space, we use sizeof(float) but we need to fit any 32 bit datum */
    239 
    240     destinationBuffer = (void*)malloc( MAX_PER_CHANNEL_FRAME_COUNT * MAX_CHANNEL_COUNT * sizeof(float) );
    241     sourceBuffer = (void*)malloc( MAX_PER_CHANNEL_FRAME_COUNT * MAX_CHANNEL_COUNT * sizeof(float) );
    242     referenceBuffer = (void*)malloc( MAX_PER_CHANNEL_FRAME_COUNT * MAX_CHANNEL_COUNT * sizeof(float) );
    243 
    244 
    245     /* the first round of tests simply iterates through the buffer combinations testing
    246         that putting something in gives something out */
    247 
    248     printf( "= Sine wave in, something out =\n" );
    249 
    250     printf( "\n" );
    251 
    252     GenerateOneCycleSine( paFloat32, referenceBuffer, MAX_PER_CHANNEL_FRAME_COUNT, 1 );
    253 
    254     for( flagCombinationIndex = 0; flagCombinationIndex < FLAG_COMBINATION_COUNT; ++flagCombinationIndex ){
    255         flags = flagCombinations[flagCombinationIndex];
    256 
    257         printf( "\n" );
    258         printf( "== flags = %s ==\n", flagCombinationNames[flagCombinationIndex] );
    259 
    260         for( sourceFormatIndex = 0; sourceFormatIndex < SAMPLE_FORMAT_COUNT; ++sourceFormatIndex ){
    261             for( destinationFormatIndex = 0; destinationFormatIndex < SAMPLE_FORMAT_COUNT; ++destinationFormatIndex ){
    262                 sourceFormat = sampleFormats_[sourceFormatIndex];
    263                 destinationFormat = sampleFormats_[destinationFormatIndex];
    264                 //printf( "%s -> %s ", sampleFormatNames_[ sourceFormatIndex ], sampleFormatNames_[ destinationFormatIndex ] );
    265 
    266                 converter = PaUtil_SelectConverter( sourceFormat, destinationFormat, flags );
    267 
    268                 /* source is a sinewave */
    269                 GenerateOneCycleSine( sourceFormat, sourceBuffer, MAX_PER_CHANNEL_FRAME_COUNT, 1 );
    270 
    271                 /* zero destination */
    272                 memset( destinationBuffer, 0, MAX_PER_CHANNEL_FRAME_COUNT * My_Pa_GetSampleSize( destinationFormat ) );
    273 
    274                 (*converter)( destinationBuffer, 1, sourceBuffer, 1, MAX_PER_CHANNEL_FRAME_COUNT, &ditherState );
    275 
    276     /*
    277     Other ways we could test this would be:
    278         - pass a constant, check for a constant (wouldn't work with dither)
    279         - pass alternating +/-, check for the same...
    280     */
    281                 if( TestNonZeroPresent( destinationBuffer, MAX_PER_CHANNEL_FRAME_COUNT * My_Pa_GetSampleSize( destinationFormat ) ) ){
    282                     //printf( "PASSED\n" );
    283                     passFailMatrix[sourceFormatIndex][destinationFormatIndex] = 1;
    284                 }else{
    285                     //printf( "FAILED\n" );
    286                     passFailMatrix[sourceFormatIndex][destinationFormatIndex] = 0;
    287                 }
    288 
    289                 
    290                 /* try to measure the noise floor (comparing output signal to a float32 sine wave) */
    291 
    292                 if( passFailMatrix[sourceFormatIndex][destinationFormatIndex] ){
    293 
    294                     /* convert destination back to paFloat32 into source */
    295                     converter = PaUtil_SelectConverter( destinationFormat, paFloat32, paNoFlag );
    296 
    297                     memset( sourceBuffer, 0, MAX_PER_CHANNEL_FRAME_COUNT * My_Pa_GetSampleSize( paFloat32 ) );
    298                     (*converter)( sourceBuffer, 1, destinationBuffer, 1, MAX_PER_CHANNEL_FRAME_COUNT, &ditherState );
    299 
    300                     if( TestNonZeroPresent( sourceBuffer, MAX_PER_CHANNEL_FRAME_COUNT * My_Pa_GetSampleSize( paFloat32 ) ) ){
    301     
    302                         noiseAmplitudeMatrix[sourceFormatIndex][destinationFormatIndex] = MaximumAbsDifference( (float*)sourceBuffer, (float*)referenceBuffer, MAX_PER_CHANNEL_FRAME_COUNT );
    303                         
    304                     }else{
    305                         /* can't test noise floor because there is no conversion from dest format to float available */
    306                         noiseAmplitudeMatrix[sourceFormatIndex][destinationFormatIndex] = -1; // mark as failed
    307                     }
    308                 }else{
    309                     noiseAmplitudeMatrix[sourceFormatIndex][destinationFormatIndex] = -1; // mark as failed
    310                 }
    311             }
    312         }
    313 
    314         printf( "\n" );
    315         printf( "=== Output contains non-zero data ===\n" );
    316         printf( "Key: . - pass, X - fail\n" );
    317         printf( "{{{\n" ); // trac preformated text tag
    318         printf( "in|  out:    " );
    319         for( destinationFormatIndex = 0; destinationFormatIndex < SAMPLE_FORMAT_COUNT; ++destinationFormatIndex ){
    320             printf( "  %s   ", abbreviatedSampleFormatNames_[destinationFormatIndex] );
    321         }
    322         printf( "\n" );
    323 
    324         for( sourceFormatIndex = 0; sourceFormatIndex < SAMPLE_FORMAT_COUNT; ++sourceFormatIndex ){
    325             printf( "%s         ", abbreviatedSampleFormatNames_[sourceFormatIndex] );
    326             for( destinationFormatIndex = 0; destinationFormatIndex < SAMPLE_FORMAT_COUNT; ++destinationFormatIndex ){
    327                 printf( "   %s   ", (passFailMatrix[sourceFormatIndex][destinationFormatIndex])? " ." : " X" );
    328             }
    329             printf( "\n" );
    330         }
    331         printf( "}}}\n" ); // trac preformated text tag
    332 
    333         printf( "\n" );
    334         printf( "=== Combined dynamic range (src->dest->float32) ===\n" );
    335         printf( "Key: Noise amplitude in dBfs, X - fail (either above failed or dest->float32 failed)\n" );
    336         printf( "{{{\n" ); // trac preformated text tag
    337         printf( "in|  out:    " );
    338         for( destinationFormatIndex = 0; destinationFormatIndex < SAMPLE_FORMAT_COUNT; ++destinationFormatIndex ){
    339             printf( "  %s   ", abbreviatedSampleFormatNames_[destinationFormatIndex] );
    340         }
    341         printf( "\n" );
    342 
    343         for( sourceFormatIndex = 0; sourceFormatIndex < SAMPLE_FORMAT_COUNT; ++sourceFormatIndex ){
    344             printf( " %s       ", abbreviatedSampleFormatNames_[sourceFormatIndex] );
    345             for( destinationFormatIndex = 0; destinationFormatIndex < SAMPLE_FORMAT_COUNT; ++destinationFormatIndex ){
    346                 amp = noiseAmplitudeMatrix[sourceFormatIndex][destinationFormatIndex];
    347                 if( amp < 0. )
    348                     printf( "    X   " );
    349                 else
    350                     printf( " % 6.1f ", 20.*log10(amp) );
    351             }
    352             printf( "\n" );
    353         }
    354         printf( "}}}\n" ); // trac preformated text tag
    355     }
    356 
    357 
    358     free( destinationBuffer );
    359     free( sourceBuffer );
    360     free( referenceBuffer );
    361 }
    362 
    363 // copied here for now otherwise we need to include the world just for this function.
    364 PaError My_Pa_GetSampleSize( PaSampleFormat format )
    365 {
    366     int result;
    367 
    368     switch( format & ~paNonInterleaved )
    369     {
    370 
    371     case paUInt8:
    372     case paInt8:
    373         result = 1;
    374         break;
    375 
    376     case paInt16:
    377         result = 2;
    378         break;
    379 
    380     case paInt24:
    381         result = 3;
    382         break;
    383 
    384     case paFloat32:
    385     case paInt32:
    386         result = 4;
    387         break;
    388 
    389     default:
    390         result = paSampleFormatNotSupported;
    391         break;
    392     }
    393 
    394     return (PaError) result;
    395 }