#include #include enum { BYTES_PER_SAMPLE = 2 }; enum { MAX_SAMPLE = (~(~0 << (8*BYTES_PER_SAMPLE-1))) }; typedef struct Command Command; struct Command { FILE* fp; double leftProportion; Command* next; }; static void emitIntegerSample( double samp ) { int val = (int)( MAX_SAMPLE * samp ); if ( val < -MAX_SAMPLE ) { val = -MAX_SAMPLE; } else if ( val > MAX_SAMPLE ) { val = MAX_SAMPLE; } fputc( ( val >> 8 ) & 0xFF, stdout ); fputc( ( val >> 0 ) & 0xFF, stdout ); } static void emitFloatingSample( double samp ) { fwrite( (const char*)&samp, sizeof(samp), 1, stdout ); } static void cleanUpList( Command* list ) { while ( list != 0 ) { Command* next = list->next; pclose( list->fp ); free( list ); list = next; } } static unsigned int addCommand( Command** commands, const char* str, double leftProportion ) { FILE* fp = popen( str, "r" ); if ( fp == 0 ) { cleanUpList( *commands ); fprintf( stderr, "Couldn't run <%s>\n", str ); perror( "popen" ); return __LINE__; } else { Command* nn = (Command*)malloc( sizeof(Command) ); if ( nn == 0 ) { pclose( fp ); cleanUpList( *commands ); fprintf( stderr, "Couldn't get memory\n" ); return __LINE__; } nn->fp = fp; nn->leftProportion = leftProportion; nn->next = *commands; *commands = nn; } return 0; } int main( int argc, const char* argv[] ) { unsigned int help = 0; double gain = 1.00; void (*emitSample)( double ) = emitIntegerSample; unsigned int done = 0; Command* commands = 0; unsigned int commandCount = 0; double leftProportion = 0.5; unsigned int frequency = 8192; double length = -1.0; double fadeIn = 0.0; double fadeOut = 5.0; unsigned int stereo = 0; unsigned int ii; unsigned int lastSample; unsigned int fadeInEnd; unsigned int fadeOutSamples; unsigned int fadeOutStart; for ( ii=1; ii < argc; ++ii ) { if ( !strcmp( argv[ ii ], "-command" ) ) { if ( addCommand( &commands, argv[ ++ii ], leftProportion ) != 0 ) { return __LINE__; } else { ++commandCount; } } else if ( !strcmp( argv[ ii ], "-file" ) ) { FILE* fp = fopen( argv[ ++ii ], "r" ); if ( fp == 0 ) { fprintf( stderr, "Unable to read file <%s>\n", argv[ii] ); return __LINE__; } else { char buf[ 8192 ]; while ( fgets( buf, sizeof(buf), fp ) != 0 ) { if ( buf[0] == '@' ) { leftProportion = strtod( &buf[1], 0 ); } else { if ( addCommand( &commands, buf, leftProportion ) != 0 ) { fclose( fp ); return __LINE__; } else { ++commandCount; } } } fclose( fp ); } } else if ( !strcmp( argv[ ii ], "-prop" ) ) { leftProportion = strtod( argv[ ++ii ], 0 ); } else if ( !strcmp( argv[ ii ], "-gain" ) ) { gain = strtod( argv[ ++ii ], 0 ); } else if ( !strcmp( argv[ ii ], "-freq" ) ) { frequency = strtol( argv[ ++ii ], 0, 0 ); } else if ( !strcmp( argv[ ii ], "-length" ) ) { length = strtod( argv[ ++ii ], 0 ); } else if ( !strcmp( argv[ ii ], "-fadeIn" ) ) { fadeIn = strtod( argv[ ++ii ], 0 ); } else if ( !strcmp( argv[ ii ], "-fadeOut" ) ) { fadeOut = strtod( argv[ ++ii ], 0 ); } else if ( !strcmp( argv[ ii ], "-float" ) ) { emitSample = emitFloatingSample; } else if ( !strcmp( argv[ ii ], "-stereo" ) ) { stereo = 1; } else { fprintf( stderr, "Unknown argument <%s>\n", argv[ii] ); help = 1; } } if ( gain < 0.0000001 || commands == 0 ) { fprintf( stderr, "Bad parameters\n" ); help = 1; } if ( help ) { fprintf( stderr, "Usage: %s\n", argv[0] ); fprintf( stderr, "\t[\n" ); fprintf( stderr, "\t\t[-prop proportionForLeftChannel]\n" ); fprintf( stderr, "\t\t[-command commandToFork]\n" ); fprintf( stderr, "\t\t...\n" ); fprintf( stderr, "\t]\n" ); fprintf( stderr, "\t[-gain gain]\n" ); fprintf( stderr, "\t[-freq frequency]\n" ); fprintf( stderr, "\t[-length lengthOfSong]\n" ); fprintf( stderr, "\t[-fadeIn lengthOfFadeIn]\n" ); fprintf( stderr, "\t[-fadeOut lengthOfFadeOut]\n" ); fprintf( stderr, "\t[-float]\n" ); fprintf( stderr, "\t[-stereo]\n" ); fprintf( stderr, "\t[-help]\n" ); return __LINE__; } /* adjust gain to avoid clipping */ gain /= (double) commandCount; if ( length < 0.0 ) { lastSample = 0; fadeInEnd = 0; fadeOutSamples = 0; fadeOutStart = ~0; } else { lastSample = (unsigned int)( length * (double)frequency ); fadeInEnd = (unsigned int)( fadeIn * (double)frequency ); fadeOutSamples = (unsigned int)( fadeOut * (double)frequency ); if ( lastSample >= fadeOutSamples ) { fadeOutStart = lastSample - fadeOutSamples; } else { fadeOutStart = 0; } if ( fadeOutStart < fadeInEnd ) { fadeInEnd = fadeOutStart = ( fadeInEnd + fadeOutStart ) / 2; fadeOutSamples = lastSample - fadeOutSamples; } } for ( ii=0; !done && ( ( lastSample == 0 ) || ii < lastSample ); ++ii ) { Command* ptr = commands; double leftSamp = 0.0; double rightSamp = 0.0; double effectiveGain = gain; while ( ptr != 0 ) { double thisGuy; if ( fread( (char*)&thisGuy, sizeof(double), 1, ptr->fp ) != 1 ) { done = 1; break; } leftSamp += ptr->leftProportion * thisGuy; rightSamp += ( 1.0 - ptr->leftProportion ) * thisGuy; ptr = ptr->next; } if ( ii < fadeInEnd ) { effectiveGain = gain * ( (double)ii ) / ( (double) fadeInEnd ); } else if ( ii >= fadeOutStart ) { effectiveGain = gain - gain * ( (double)( ii - fadeOutStart ) ) / ( (double) fadeOutSamples ); } if ( stereo ) { (*emitSample)( leftSamp * effectiveGain ); (*emitSample)( rightSamp * effectiveGain ); } else { (*emitSample)( ( leftSamp + rightSamp ) * effectiveGain ); } } cleanUpList( commands ); return 0; }