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 }