annotate src/portaudio_20161030_catalina_patch/test/patest_converters.c @ 83:ae30d91d2ffe

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