/* mixer.c by Michael Thorpe 2018-06-23 */ #include #include #include #define SHORT_USAGE "mixer -h | { -[emv] | -[blor] <#> | }* " struct channel { struct channel *next; unsigned int sample_rate; /* Not yet supported */ unsigned int bits_per_sample; signed long left_gain_times_256; signed long right_gain_times_256; signed long current_sample; int little_endian; unsigned long saturations; char *name; FILE *f; }; int main(int argc,char **argv) { int i,output_bits_per_sample=16,output_in_stereo=1,verbose=0,retval=0; struct channel *c; char buf[3],*s; signed long current_left_sample,current_right_sample,summand; unsigned long summing_saturations=0; unsigned long output_skip=0,output_length=0; struct channel default_channel={0,44100,16,256,256,0,0,0,0,0}; for(i=1;i set input bits/sample for following file(s) (default: 16)\n" " -e toggle endianness for following files/output (default: big-endian)\n" " -h show this help message\n" " -l <#> set left channel volume for following file(s) (default: 256)\n" " -m toggle stereo/mono output (default: stereo)\n" " -o <#> set output bits/sample (default: 16)\n" " -r <#> set right channel volume for following file(s) (default: 256)\n" " -v toggle verbosity\n" " name of a file, or - for stdin\n" ,stdout); return(0); } else if(!strcmp(argv[i],"-e")) { default_channel.little_endian=!default_channel.little_endian; } else if(!strcmp(argv[i],"-m")) { output_in_stereo=!output_in_stereo; } else if(!strcmp(argv[i],"-v")) { verbose=!verbose; } else if(argv[i][0]=='-' && argv[i][1] && iname=argv[i]; if(argv[i][0]=='-' && !argv[i][1]) c->f=stdin; else c->f=fopen(argv[i],"rb"); if(!c->f) { perror("open"); return(1); } default_channel.next=c; } } if(!default_channel.next) { fputs("usage: " SHORT_USAGE "\n",stderr); return(1); } while(1) { current_left_sample=0; current_right_sample=0; for(c=default_channel.next;c;c=c->next) { if(c->little_endian) { buf[2]=buf[1]=buf[0]=0; if(1 != fread(buf+3-(c->bits_per_sample>>3),c->bits_per_sample>>3,1,c->f)) goto no_more_samples; c->current_sample=(((signed int)(signed char)buf[2])<<16)+(((unsigned int)(unsigned char)buf[1])<<8)+((unsigned int)(unsigned char)buf[0]); } else { buf[2]=buf[1]=0; if(1 != fread(buf,c->bits_per_sample>>3,1,c->f)) goto no_more_samples; c->current_sample=(((signed int)(signed char)buf[0])<<16)+(((unsigned int)(unsigned char)buf[1])<<8)+((unsigned int)(unsigned char)buf[2]); } if(c->current_sample&0x800000) c->current_sample|=~0xFFFFFF; summand=(c->current_sample*c->left_gain_times_256)/256L; if(c->left_gain_times_256) { if((summand<0)^(c->current_sample<0)^(c->left_gain_times_256<0)) { if(verbose) fputc('.',stderr); c->saturations++; } current_left_sample+=summand; } if(c->right_gain_times_256) { summand=(c->current_sample*c->right_gain_times_256)/256L; if((summand<0)^(c->current_sample<0)^(c->right_gain_times_256<0)) { if(verbose) fputc('.',stderr); c->saturations++; } current_right_sample+=summand; } } if(current_left_sample<-0x800000 || 0x800000<=current_left_sample) { if(verbose) fprintf(stderr,"Summing saturation: %8.8lX\n",current_left_sample&0xFFFFFFFF); current_left_sample=current_left_sample<0?-0x800000:0x7FFFFF; summing_saturations++; } if(output_skip) { output_skip--; continue; } if(default_channel.little_endian) { if(output_bits_per_sample>16) putchar(current_left_sample); if(output_bits_per_sample>8) putchar(current_left_sample>>8); putchar(current_left_sample>>16); } else { putchar(current_left_sample>>16); if(output_bits_per_sample>8) putchar(current_left_sample>>8); if(output_bits_per_sample>16) putchar(current_left_sample); } if(output_in_stereo) { if(current_right_sample<-0x800000 || 0x800000<=current_right_sample) { if(verbose) fprintf(stderr,"Summing saturation: %6.6lX\n",(current_right_sample>>8)&0xFFFFFFL); current_right_sample=current_right_sample<0?-0x800000:0x7FFFFF; summing_saturations++; } if(default_channel.little_endian) { if(output_bits_per_sample>16) putchar(current_right_sample); if(output_bits_per_sample>8) putchar(current_right_sample>>8); putchar(current_right_sample>>16); } else { putchar(current_right_sample>>16); if(output_bits_per_sample>8) putchar(current_right_sample>>8); if(output_bits_per_sample>16) putchar(current_right_sample); } } if(output_length && !--output_length) goto no_more_samples; } no_more_samples: if(summing_saturations) { if(verbose) fputc('\n',stderr); fprintf(stderr,"Saturated %lu times when summing\n",summing_saturations); retval=1; } for(c=default_channel.next;c;c=c->next) { if(c->saturations) { fprintf(stderr,"Channel %s saturated %lu times\n",c->name,c->saturations); retval=1; } } return(retval); }