/*
multimux  copyright 2003-always Jan Panteltje.

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/


#include "multimux.h"


char *strsave(char *s) /* save string s somewhere */
{
char *p;

p = (char *) malloc(strlen(s) + 1);
if(!p) return 0;

strcpy(p, s);
return p;
} /* end function strsave */


int print_usage()
{
fprintf(stderr, "\
Usage:                multimux [-a AC3bitrate] [-d delay2,...,delayn] [-f] [-h] [-v] infile1 infile2 ... infile7\n\
                       output is to stdout, or in case the -f flag is used, to ffmpeg.\n\n\
-a                    AC3 bitrate, passed to ffmpeg in case the -f flag is used, in kilobits per second.\n\
                      Normally multimux wil calculate 64kb/s per channel x number of channels, so 320kb/s for 5 channels,\n\
                      This option allows you to force a different total bitrate.\n\
-d delay1,...,delayn  delays in milliseconds for audio channels.\n\
                         (no negative delays allowed, silence is added in front.)\n\
-f                    encode to AC3 via ffmpeg, without intermediate wave file, all automatic,\n\
                         calls multimux with ffmpeg recursively, and writes to ac3 file.\n\
                         example command:\n\
                         multimux -f file1.wav file2.wav file3.wav file4.wav file5.wav\n\
                         this will create file 5ch.ac3.\n\
-h                    help (this help).\n\
-i                    ignore data_length field in input wave headers.\n\
-r                    raw output format, for piping when output file > 4GB (MS wave format is limited to 4GB).\n\
                         example: use for multi channel ac3 without intermediate wave file:\n\
                         multimux -r q1.wav q2.wav q3.wav q4.wav q5.wav |\n\
                         ffmpeg -f s16le -ac 5 -ar 48000 -i /dev/stdin -ab 320k -acodec ac3 5ch.ac3\n\
                         This option does _not_ automatically execute ffmpeg.\n\
-s                    silent mode, no expected output file size and no bytes written display.\n\
-v                    verbose, prints functions and arguments.\n\
                      Examples:\n\
                       Make multi channel wave file < 4GB:\n\
                        multimux file1.wav file2.wav file3.wav file4.wav > 4channel.wav\n\
                       Same with delays:\n\
                        multimux -d 0,1000,2000,3000 file1.wav file2.wav file3.wav file4.wav > 4channel.wav\n\
                       Encode to 6ch.ac3:\n\
                        multimux -f file1.wav file2.wav file3.wav file4.wav file4.wav file5.wav file6.wav\n\
                       Make a raw output file:\n\
                        multimux -r file1.wav file2.wav file3.wav file4.wav > 4ch.raw\n\
");

return 1;
} /* end function print_usage */


int main(int argc, char **argv)
{
int a, i, j;
double da, db;
wave_header *out_header;
wave_header *in_header[MAX_FILES];
FILE *ifptr[MAX_FILES];
char *input_filename[MAX_FILES];
int input_files;
struct stat *statptr[MAX_FILES];
int skip_argument_flag;
unsigned char uc[10]; /* max number of bytes per sample, using only 2 here */
double delay[MAX_FILES];
char *delay_string;
char *token;
const char delimiters[] = ",";
size_t bytes_to_add[MAX_FILES];
size_t bytes_added[MAX_FILES];
size_t longest_input_file_length;
size_t longest_input_data_length;
double expected_output_filesize;
double bytes_written;
int display_counter;
size_t sta;
int raw_output_flag;
uint8_t buffer[8192]; // wave header is always 44 bytes, but struct wave header could be different.
char command[100][1024];
char tcommand[MAX_FILES * 1024];
FILE *tfptr;
FILE *pptr;
int ffmpeg_flag;
int ac3_bitrate;
int ignore_data_length_field_flag;
uint32_t offset_to_data;
uint32_t data_start;
uint16_t silent_flag;
int header_size = 44; // wave header is always 44 bytes

fprintf(stderr, "Panteltje multimux-%s \n", VERSION);

/* defaults */
delay_string = 0;
longest_input_data_length = 0;
raw_output_flag = 0;
ffmpeg_flag = 0;
ac3_bitrate = 0;

for(i = 0; i < MAX_FILES; i++)
	{
	delay[i] = 0.0;
	}

ignore_data_length_field_flag = 0;
silent_flag = 0;

/* end defaults */

while(1)
	{
	a = getopt(argc, argv, "a:d:fhirsv");
	if(a == -1) break;

	switch(a)
		{
		case 'a': // pass a different bitrate then calculated to ffmpeg
			ac3_bitrate = atoi(optarg);
			break;
		case 'd': // delays
			delay_string = strsave(optarg);
			if(! delay_string)
				{
				fprintf(stderr, "multimux: could not allocate space for delay_string, abnorting.\n");

				exit(1);
				}

			break;
		case 'f': // use ffmpeg to make AC3
			ffmpeg_flag = 1;
			raw_output_flag = 1;
			break;
		case 'h': // help
			print_usage();
			exit(1);
			break;
		case 'i':
			ignore_data_length_field_flag = 1;
			break;
		case 'r': // raw output
			raw_output_flag = 1;
			break;
		case 's':
			silent_flag = 1;
			break;
		case 'v': // verbose 
			debug_flag = 1;
			break;
        case -1:
        	break;
		case '?':
			if (isprint(optopt) )
 				{
 				fprintf(stderr, "Unknown option `-%c'.\n", optopt);
 				}
			else
				{
				fprintf(stderr, "Unknown option character `\\x%x'.\n",\
				optopt);
				}
			print_usage();

			exit(1);
			break;			
		default:
			print_usage();

			exit(1);
			break;
		}/* end switch a */
	}/* end while getopt() */

if(debug_flag)
	{
	// are we on a 16, 32, or 64 bit system?
	fprintf(stderr, "unsigned int max=%u unsigned long max=%lu\n", UINT_MAX, ULONG_MAX);
	}

skip_argument_flag = 0;
input_files = 0;
for(i = 1; i < argc; i++)
	{
	if( strcmp(argv[i], "-a") == 0)
		{
		skip_argument_flag = 1;
		continue;
		}

	if( strcmp(argv[i], "-d") == 0)
		{
		skip_argument_flag = 1;
		continue;
		}

	if( argv[i][0] == '-')
		{	
		continue;
		}
		
	if(! skip_argument_flag)
		{
		input_filename[input_files] = strsave(argv[i]);
		if(! input_filename[input_files])
			{
			fprintf(stderr, "multimux: could not allocate space for input_filename[%d], aborting.\n", input_files);

			exit(1);
			}
 
		if(debug_flag)
			{
			fprintf(stderr, "found input file %s\n", input_filename[input_files]);
			}

		input_files++;

		if(input_files > MAX_FILES)
			{
			fprintf(stderr, "multimux: too many (%d) input files specified, maximum is %d, aborting.\n", input_files, MAX_FILES);
			
			exit(1);
			}
		} /* end if ! command line flag */

	skip_argument_flag = 0;
	} /* end while al input files */

if(input_files == 0)
	{
	fprintf(stderr, "No input file(s) specified, aborting (multimux -h for help).\n");

	exit(1);
	}

if(input_files == 1)
	{
	fprintf(stderr, "multimux: WARNING only 1 input file specified!\n");
	}

/* allocate space for output wave header */
out_header = (wave_header *) malloc( sizeof(wave_header) );
if(! out_header)
	{
	fprintf(stderr,\
	"multimux(): could not allocate space for out_header, aborting.\n");

	exit(1);
	}

/* try to open the input file(s) */
for(i = 0; i < input_files; i++)
	{
	ifptr[i] = fopen(input_filename[i], "r");

	if(! ifptr[i])
		{
		fprintf(stderr, "multimux: could not open file %s for read, aborting.\n", input_filename[i]);

 		exit(1);
		}

	} /* end for all possible input files */

if(debug_flag)
	{
	fprintf(stderr, "input_files=%d\n", input_files);
	}

/* if bitrate not user specified calculate it */
if(! ac3_bitrate) ac3_bitrate = input_files * 64;

/* parse delay string */
for(i = 0; i < input_files; i++)
	{
	if(i == 0) token = strtok (delay_string, delimiters);
	else token = strtok (NULL, delimiters);
	if(! token) break;

	sscanf(token, "%lf", &delay[i]);

	if(debug_flag)
		{
		fprintf(stderr, "delay[%d]=%.2f\n", i, delay[i]);
		}

	} /* end for all delays (as many delays as input files) */

longest_input_file_length = 0;
for(i = 0; i < input_files; i++)
	{
	/* allocate space for structure stat so we can get length input file */
	statptr[i] = (struct stat*) malloc( sizeof(struct stat) );
    if(! statptr[i])
		{
		fprintf(stderr, "multimux: could not allocate space for structure stat[%d], aborting.\n", i);

		exit(1);
		}

	/* fill in structure stat */
	a = stat(input_filename[i], statptr[i]);
	if(a == -1)
		{
		fprintf(stderr, "stat filename[%d] failed, aborting.\n", i);
	
		exit(1);
		}

	if(debug_flag)
		{
		fprintf(stderr, "statptr[%d]=%p i=%d statptr[%d] -> st_size=%lu\n",\
		i, statptr[i], i, i, (unsigned long)statptr[i] -> st_size);
		}

	fprintf(stderr, "input file %s size is %lu delay is %.2f milli seconds\n",\
	input_filename[i], (unsigned long)statptr[i] -> st_size, delay[i]);

	/* get longest input file length */
	if( statptr[i] -> st_size > longest_input_file_length)
		{
		longest_input_file_length = statptr[i] -> st_size;
		}

	/* free structure stat */
	free(statptr[i]);

	} /* end for all input files */

if(debug_flag)
	{
	fprintf(stderr, "longest_input_file_length=%lu\n", (unsigned long)longest_input_file_length);
	}

/* create wave header for output file */
//char main_chunk[4];		/* +0	'RIFF' */
//uint32_t length;			/* +4	length of file */
//uint8_t chunk_type[4];	/* +8	'WAVE' */
//uint8_t sub_chunk[4];		/* +12	'fmt ' */
//uint32_t length_chunk;	/* +16	length sub_chunk, always 16 bytes */
//uint16_t format;			/* +20	always 1 = PCM-Code */
//uint16_t modus;			/* +22	1 = Mono, 2 = Stereo */
//uint32_t sample_fq;		/* +24	Sample Freq */
//uint32_t byte_p_sec;		/* +28	Data per sec */
//uint16_t byte_p_spl;		/* +32	bytes per sample, 1=8 bit, 2=16 bit (mono) 2=8 bit, 4=16 bit (stereo) */
//uint16_t bit_p_spl;		/* +34	bits per sample, 8, 12, 16 */
//uint8_t data_chunk[4];	/* +36	'data' */
//uint32_t data_length;		/* +40	length of data */
// total length = 44 bytes


for(i = 0; i < input_files; i++)
	{
	in_header[i] = (wave_header *) malloc( sizeof(wave_header) );
	if(! in_header[i])
		{
		fprintf(stderr, "multimux: could not allocate space for in_header[%d], aborting.\n", i);

		exit(1);
		}

	a = fread(buffer, sizeof(char), header_size, ifptr[i]);
	if(a < header_size)
		{
		fprintf(stderr, "multimux: could not read wave header for file %s, aborting.\n", input_filename[i]);

		exit(1);
		}

	if( (buffer[0] != 'R') || (buffer[1] != 'I') || (buffer[2] != 'F') || (buffer[3] != 'F') )
		{
		fprintf(stderr, "multimux: input file %s is not a RIFF file, aborting.\n", input_filename[i]);
	
		exit(1);			
		}

	if( (buffer[8] != 'W') || (buffer[9] != 'A') || (buffer[10] != 'V') || (buffer[11] != 'E') )
		{
		fprintf(stderr, "multimux: input file %s is not a WAVE file, aborting.\n", input_filename[i]);
	
		exit(1);			
		}

	if( (buffer[12] != 'f') || (buffer[13] != 'm') || (buffer[14] != 't') || (buffer[15] != ' ') )
		{
		fprintf(stderr, "multimux: WARNING input file %s header does not contain 'fmt '.\n", input_filename[i]);
	
//		exit(1);			
		}

	in_header[i] -> length =		buffer[ 4] | (buffer[ 5] << 8) | (buffer[6] << 16) | (buffer[7] << 24); 
	in_header[i] -> length_chunk =	buffer[16] | (buffer[17] << 8) | (buffer[18] << 16) | (buffer[19] << 24);
	in_header[i] -> format =		buffer[20] | (buffer[21] << 8);
	in_header[i] -> modus =			buffer[22] | (buffer[23] << 8);
	in_header[i] -> sample_fq =		buffer[24] | (buffer[25] << 8) | (buffer[26] << 16) | (buffer[27] << 24);
	in_header[i] -> byte_p_sec =	buffer[28] | (buffer[29] << 8) | (buffer[30] << 16) | (buffer[31] << 24);
	in_header[i] -> byte_p_spl =	buffer[32] | (buffer[33] << 8);
	in_header[i] -> bit_p_spl =		buffer[34] | (buffer[35] << 8);
	in_header[i] -> data_length =	buffer[40] | (buffer[41] << 8) | (buffer[42] << 16) | (buffer[43] << 24);

	if(debug_flag)
		{
		fprintf(stderr, "in_header[%d] -> length =%d\n", i, in_header[i] -> length);
		fprintf(stderr, "in_header[%d] -> length_chunk =%d\n", i, in_header[i] -> length_chunk);
		fprintf(stderr, "in_header[%d] -> format =%d\n", i, in_header[i] -> format);
		fprintf(stderr, "in_header[%d] -> modus =%d\n", i, in_header[i] -> modus);
		fprintf(stderr, "in_header[%d] -> sample_fq =%d\n", i, in_header[i] -> sample_fq);
		fprintf(stderr, "in_header[%d] -> byte_p_sec =%d\n", i, in_header[i] -> byte_p_sec);
		fprintf(stderr, "in_header[%d] -> byte_p_spl =%d\n", i, in_header[i] -> byte_p_spl);
		fprintf(stderr, "in_header[%d] -> bit_p_spl =%d\n", i, in_header[i] -> bit_p_spl);
		fprintf(stderr, "in_header[%d] -> data_length =%d\n", i, in_header[i] -> data_length);
		fprintf(stderr, "\n");
		}

	if(i == 0)
		{
		fprintf(stderr,\
		"channels=%d  bits per sample=%d  sample frequency=%d\n", input_files, in_header[0] -> bit_p_spl, in_header[0] -> sample_fq);

		if( (in_header[i] -> bit_p_spl != 8) && (in_header[i] -> bit_p_spl != 16) && (in_header[i] -> bit_p_spl != 24) )
			{
			fprintf(stderr, "multimux: unsupported bits per sample %d, supported is 8, 16, or 24, aborting.\n", in_header[i] -> bit_p_spl); 
			
			exit(1);
			}
		}


	/* some tests for correct and same format input files */

	if(in_header[i] -> modus != 1)
		{
		fprintf(stderr, "multimux: input file %s is not a mono file, aborting.\n", input_filename[i]);
	
		exit(1);			
		}

	if(in_header[i] -> length_chunk != 16)
		{
		fprintf(stderr, "multimux: input file %s incorrect header, length_chunk != 16, aborting.\n", input_filename[i]);
	
		exit(1);			
		}

	if(in_header[i] -> format != 1)
		{
		fprintf(stderr, "multimux: input file %s is not a PCM file, aborting.\n", input_filename[i]);
	
		exit(1);			
		}
	
	if(in_header[i] -> sample_fq != in_header[0] -> sample_fq)
		{
		fprintf(stderr, "multimux: input file %s has a different sample frequency then input file %s, aborting.\n",\
		input_filename[i], input_filename[0]);
		
		exit(1);		
		}

	if(in_header[i] -> byte_p_spl != in_header[0] -> byte_p_spl)
		{
		fprintf(stderr, "multimux: input file %s has a different number of bytes per sample then input file %s, aborting.\n",\
		input_filename[i], input_filename[0]);
		
		exit(1);		
		}

	if(in_header[i] -> bit_p_spl != in_header[0] -> bit_p_spl)
		{
		fprintf(stderr, "multimux: input file %s has a different number of bits per sample then input file %s, aborting.\n",\
		input_filename[i], input_filename[0]);
		
		exit(1);		
		}

	if( (buffer[36] != 'd') || (buffer[37] != 'a') || (buffer[38] != 't') || (buffer[39] != 'a') )
		{
		fprintf(stderr, "multimux: input file %s has no field 'data' in expected position, testing for offset.\n",\
		input_filename[i]);		
		
		/* it seems that if 'data' is not present, then a 4 byte offset is present to the word 'data' */
		
		if( (buffer[36] == 'F') && (buffer[37] == 'L') &&  (buffer[38] == 'L') && (buffer[39] == 'R') )
			{
			fprintf(stderr, "FLLR extention found.\n");

			offset_to_data = buffer[40] | (buffer[41] << 8) | (buffer[42] << 16) | (buffer[43] << 24);
			fprintf(stderr, "multimux: inputfile %s offset_to_data=%d (0x%02x)\n", input_filename[i], offset_to_data, offset_to_data);

			data_start = offset_to_data + header_size + 8; /* offset + already read + 'data' + length */
			fprintf(stderr, "multimux: inputfile %s data_start=%d (0x%02x)\n", input_filename[i], data_start, data_start);
			
			a = fread(buffer + header_size, sizeof(char), data_start - header_size, ifptr[i]);
			if(a < (data_start - header_size) )
				{
				fprintf(stderr, "multimux: could not read wave header for file %s, aborting.\n", input_filename[i]);
			                    
				exit(1);
			    }                                      

//for(a = data_start - 8; a < data_start + 20; a++) fprintf(stderr, "a=%d %02x\n", a, buffer[a]);
//fprintf(stderr, "\n");


			/* test for 'data' in extended position */

			if( (buffer[data_start - 8] != 'd') || (buffer[data_start - 7] != 'a') || \
			(buffer[data_start - 6] != 't') || (buffer[data_start - 5] != 'a') )
				{
				fprintf(stderr, "multimux: input file %s has no field 'data' in extended position, aborting.\n", input_filename[i]);
				
				exit(1);
				}

			/* overrule original data length */

			in_header[i] -> data_length = \
				buffer[data_start - 4] | (buffer[data_start - 3] << 8) | (buffer[data_start - 2] << 16) | (buffer[data_start - 1] << 24);

			fprintf(stderr, "multimux: input file %s data_length=%d\n", input_filename[i], in_header[i] -> data_length);
	
			}
		else
			{
			fprintf(stderr, "multimux: input file %s has no field 'data' in expected position, aborting.\n", input_filename[i]);
			
			exit(1);
			}
		}
	}

out_header -> main_chunk[0] = 'R';
out_header -> main_chunk[1] = 'I';
out_header -> main_chunk[2] = 'F';
out_header -> main_chunk[3] = 'F';

out_header -> chunk_type[0] = 'W';
out_header -> chunk_type[1] = 'A';
out_header -> chunk_type[2] = 'V';
out_header -> chunk_type[3] = 'E';
 
out_header -> sub_chunk[0] = 'f';
out_header -> sub_chunk[1] = 'm';
out_header -> sub_chunk[2] = 't';
out_header -> sub_chunk[3] = ' ';

out_header -> data_chunk[0] = 'd';
out_header -> data_chunk[1] = 'a';
out_header -> data_chunk[2] = 't';
out_header -> data_chunk[3] = 'a';

out_header -> length_chunk = 16; //always 16
out_header -> format = 1; //PCM
out_header -> modus = input_files;  // 2; // stereo


/* calculate bytes corresponding to each file delay */
/* bytes per millisecond */
da = (double)in_header[0] -> byte_p_sec / 1000.0;

for(i = 0; i < input_files; i++)
	{
	/* bytes to shift in audio */
	db = da * (double)delay[i];

	bytes_to_add[i] = (size_t)db;
	a = bytes_to_add[i] % in_header[0] -> byte_p_spl;

	/* align on sample */
	if(a) bytes_to_add[i] += in_header[0] -> byte_p_spl - a;  

	if(debug_flag)
		{
		fprintf(stderr, "bytes_to_add[%d]=%lu\n", i, (unsigned long)bytes_to_add[i]);
		}

	/* so far nothing written */
	bytes_added[i] = 0;
	}

/* get longest input data length */
longest_input_data_length = 0;
for(i = 0; i < input_files; i++)
	{
	if(in_header[i] -> data_length > longest_input_data_length)
		{
		longest_input_data_length = in_header[i] -> data_length;
		}
	}

sta = 0;
for(i = 0; i < input_files; i++)
	{
	if(bytes_to_add[i] > sta)
		{
		sta = bytes_to_add[i];
		}
	}


if(ignore_data_length_field_flag)
	{
	expected_output_filesize = (double)input_files * ( (double)longest_input_file_length - (double)header_size + (double)sta );
	}
else
	{
	expected_output_filesize = (double)input_files * ( (double)longest_input_data_length + (double)sta );
	}

if(! raw_output_flag)
	{
	expected_output_filesize += (double)header_size;
	}


if(debug_flag)
	{
	fprintf(stderr, "longest_input_data_length=%lu\n", (unsigned long)longest_input_data_length);
	}

if( ( (! raw_output_flag) && (expected_output_filesize > (double)0xffffffff) ) || ffmpeg_flag)
	{
	if(! ffmpeg_flag)
		{
		fprintf(stderr, "multimux: output filesize is %.0f this exceeds 2^32 bytes (4GB),\n\
		MS wave format does not support this, use the -r flag to directly encode to AC3!, aborting.\n", expected_output_filesize);
		
		fprintf(stderr,\
		"Try the following command line to encode to AC3 (increase bitrate value after -ab if you need better quality) use cut and paste:\n");
		}

	/* Auto execute? */

	/*
	assemble command to pipe via ffmpeg, in the report for cut and paste use no '-y' overwrite flag, but only in the final,
	after asking if output file must be overwritten.
	*/
	a = 0;	
	if(! ffmpeg_flag) fprintf(stderr, "multimux -r -d ");
	snprintf(command[0], 1023, "multimux -s -r -d ");
	a++;

	for(i = 0; i < input_files; i++)
		{	
		if(! ffmpeg_flag) fprintf(stderr, "%.2f", delay[i]);		
		snprintf(command[a], 1023, "%.2f", delay[i]);
		a++;

		if(i < (input_files - 1) )
			{
			if(! ffmpeg_flag) fprintf(stderr, ",");
			snprintf(command[a], 1023, ",");
			a++;
			}
		}
	
	if(! ffmpeg_flag) fprintf(stderr, " ");
	snprintf(command[a], 1023, " ");
	a++;

	for(i = 0; i < input_files; i++)
		{
		if(! ffmpeg_flag) fprintf(stderr, "%s ", input_filename[i]);
		snprintf(command[a], 1023, "%s ", input_filename[i]);
		a++;
		}

	if(in_header[0] -> bit_p_spl == 8) // 8 bits
		{
		if(! ffmpeg_flag) fprintf(stderr, "| ffmpeg -f u8 ");
		snprintf(command[a], 1023, "| ffmpeg -y -f u8 ");
		a++;
		}
	else if(in_header[0] -> bit_p_spl == 16) // s16le (little endian)
		{
		if(! ffmpeg_flag) fprintf(stderr, "| ffmpeg -f s16le ");
		snprintf(command[a], 1023, "| ffmpeg -y -f s16le ");
		a++;
		}
	/* input check only allows 8 and 16 bit per sample */

	if(! ffmpeg_flag) fprintf(stderr, "-ac %d -ar %d -i /dev/stdin -ab %dk -acodec ac3 %dch.ac3\n",\
	input_files, in_header[0] -> sample_fq, ac3_bitrate, input_files);

	snprintf(command[a], 1023, "-ac %d -ar %d -i /dev/stdin -ab %dk -acodec ac3 %dch.ac3\n",\
	input_files, in_header[0] -> sample_fq, ac3_bitrate, input_files);
	a++;

	/* put all pieces together */
	tcommand[0] = 0;
	for(i = 0; i < a; i++)
		{
		strcat(tcommand, command[i]);	
		}
	if(! ffmpeg_flag) fprintf(stderr, "\n");

	/* | ffmpeg -y -f s16le -ac 5 -ar 48000 -i /dev/stdin -ab 320k -acodec ac3 5ch.ac3 */

	if(! ffmpeg_flag)
		{
		fprintf(stderr, "Execute this command (y/n)ENTER?\n");

		fscanf(stdin, "%10s", command[0]);

		if( toupper( command[0][0]) != 'Y')
			{
			fprintf(stderr, "multimux: user abort.\n");

			exit(1);
			}

		} /* end if ! ffmpeg_flag */

	/* ffmpeg does not ask for overwrite with -y flag in pipe mode, just aborts. */

	/* test if output file already exists */

	sprintf(command[1], "%dch.ac3", input_files);

	tfptr = fopen(command[1], "r");
	if(tfptr)
		{
		fclose(tfptr);

		fprintf(stderr, "Output file %s aready exists, overwrite (y/n)ENTER?\n", command[1]);
			
		fscanf(stdin, "%10s", command[0]);
		if( toupper( command[0][0]) != 'Y')
			{
			fprintf(stderr, "multimux: user abort.\n");

			exit(1);
			}
		}
		
	fprintf(stderr, "multimux: executing\n%s\n", tcommand);

	pptr = popen(tcommand, "w");
	pclose(pptr);

	exit(0);
	} /* end if not raw and too long or ffmpeg pipe */

out_header -> byte_p_sec = in_header[0] -> byte_p_sec * input_files;
out_header -> byte_p_spl = in_header[0] -> byte_p_spl; // 2 mono
out_header -> sample_fq = in_header[0] -> sample_fq;
out_header -> bit_p_spl = in_header[0] -> bit_p_spl; // 16

if(debug_flag)
	{
	fprintf(stderr, "longest_input_file_length=%lu\n", (unsigned long)longest_input_file_length);
	}

if(ignore_data_length_field_flag)
	{
	out_header -> data_length = (longest_input_file_length + sta - header_size) * input_files;	
	}
else
	{	
	out_header -> data_length = (longest_input_data_length + sta) * input_files;
	}
	
out_header -> length = out_header -> data_length + header_size - 8;

if(debug_flag)
	{
	fprintf(stderr, "out_header -> data_length=%lu\n", (unsigned long)out_header -> data_length);
	fprintf(stderr, "out_header -> length=%lu\n", (unsigned long)out_header -> length);
	}

if(! silent_flag)
	{
	fprintf(stderr, "expected output filesize is %.0f\n", expected_output_filesize);
	
	if(raw_output_flag)
		{
		fprintf(stderr, "you can convert this to ac3 with ffmpeg:\n");
		
		if(in_header[0] -> bit_p_spl == 16)
			{
			fprintf(stderr,\
			"ffmpeg -f s16le -ac %d -ar %d -i file.raw -ab %dk -acodec ac3 %dch.ac3\n",\
			input_files, in_header[0] -> sample_fq, ac3_bitrate, input_files);				
			}
		else if(in_header[0] -> bit_p_spl == 8)
			{
			fprintf(stderr,\
			"ffmpeg -f u8 -ac %d -ar %d -i file.raw -ab %dk -acodec ac3 %dch.ac3\n",\
			input_files, in_header[0] -> sample_fq, ac3_bitrate, input_files);				
			}	

		fprintf(stderr, "writing raw output format\n");
		}
	else
		{
		fprintf(stderr, "writing wave format file\n");
		}
	} /* end if ! silent_flag */

bytes_written = 0.0;

if(! raw_output_flag)
	{
	/* write out wave header */

//char main_chunk[4];		/* +0	'RIFF' */
//uint32_t length;			/* +4	length of file */
//uint8_t chunk_type[4];	/* +8	'WAVE' */
//uint8_t sub_chunk[4];		/* +12	'fmt ' */
//uint32_t length_chunk;	/* +16	length sub_chunk, always 16 bytes */
//uint16_t format;			/* +20	always 1 = PCM-Code */
//uint16_t modus;			/* +22	1 = Mono, 2 = Stereo */
//uint32_t sample_fq;		/* +24	Sample Freq */
//uint32_t byte_p_sec;		/* +28	Data per sec */
//uint16_t byte_p_spl;		/* +32	bytes per sample, 1=8 bit, 2=16 bit (mono) 2=8 bit, 4=16 bit (stereo) */
//uint16_t bit_p_spl;		/* +34	bits per sample, 8, 12, 16 */
//uint8_t data_chunk[4];	/* +36	'data' */
//uint32_t data_length;		/* +40	length of data */
// total length = 44 bytes
	
	buffer[ 0] = 'R';
	buffer[ 1] = 'I';
	buffer[ 2] = 'F';
	buffer[ 3] = 'F';

	buffer[ 4] = out_header -> length & 0xff;
	buffer[ 5] = ( (out_header -> length) & 0xff00) >> 8;
	buffer[ 6] = ( (out_header -> length) & 0xff0000) >> 16;
	buffer[ 7] = ( (out_header -> length) & 0xff000000) >> 24;

	buffer[ 8] = 'W';
	buffer[ 9] = 'A';
	buffer[10] = 'V';
	buffer[11] = 'E';

	buffer[12] = 'f';
	buffer[13] = 'm';
	buffer[14] = 't';
	buffer[15] = ' ';

	buffer[16] = out_header -> length_chunk & 0xff;
	buffer[17] = ( (out_header -> length_chunk) & 0xff00) >> 8;
	buffer[18] = ( (out_header -> length_chunk) & 0xff0000) >> 16;
	buffer[19] = ( (out_header -> length_chunk) & 0xff000000) >> 24;

	buffer[20] = 1;
	buffer[21] = 0;

	buffer[22] = out_header -> modus & 0xff;
	buffer[23] = ( (out_header -> modus) & 0xff00) >> 8;

	buffer[24] = out_header -> sample_fq & 0xff;
	buffer[25] = ( (out_header -> sample_fq) & 0xff00) >> 8;
	buffer[26] = ( (out_header -> sample_fq) & 0xff0000) >> 16;
	buffer[27] = ( (out_header -> sample_fq) & 0xff000000) >> 24;

	buffer[28] = out_header -> byte_p_sec  & 0xff;
	buffer[29] = ( (out_header -> byte_p_sec) & 0xff00) >> 8;
	buffer[30] = ( (out_header -> byte_p_sec) & 0xff0000) >> 16;
	buffer[31] = ( (out_header -> byte_p_sec) & 0xff000000) >> 24;

	buffer[32] = out_header -> byte_p_spl & 0xff;
	buffer[33] = ( (out_header -> byte_p_spl) & 0xff00) >> 8;

	buffer[34] = out_header -> bit_p_spl & 0xff;
	buffer[35] = ( (out_header -> bit_p_spl) & 0xff00) >> 8;

	buffer[36] = 'd';
	buffer[37] = 'a';
	buffer[38] = 't';
	buffer[39] = 'a';

	buffer[40] = out_header -> data_length & 0xff;
	buffer[41] = ( (out_header -> data_length) & 0xff00) >> 8;
	buffer[42] = ( (out_header -> data_length) & 0xff0000) >> 16;
	buffer[43] = ( (out_header -> data_length) & 0xff000000) >> 24;

	a = fwrite(buffer, sizeof(char), header_size, stdout);
	if(a != header_size)
		{
		fprintf(stderr, "multimux(): could only write %d of %d bytes of out header, aborting.\n", a, header_size);   

		exit(1); 
		}

	} /* end if not raw (write header) */
	 
bytes_written += (double)a;

display_counter = 0;
while(1)
	{
	for(i = 0; i < input_files; i++)
		{
		/* write lead silence samples if delay specified for this channel */
		if(bytes_to_add[i] > bytes_added[i])
			{
			for(j = 0; j < out_header -> byte_p_spl; j++)
				{
				if(out_header -> bit_p_spl == 8) uc[j] = 128;
				if(out_header -> bit_p_spl == 16) uc[j] = 0;
				if(out_header -> bit_p_spl == 24) uc[j] = 0;
				}								

			bytes_added[i] += out_header -> byte_p_spl;
			}
		else
			{
			a = fread(uc, sizeof(char), out_header -> byte_p_spl, ifptr[i]);
			}

		/* shorter files are padded with silence */
		if(a < 1) /* EOF or error */
			{
			if(out_header -> byte_p_spl == 1)
				{
				uc[0] = 128;
				}
			else
				{
				uc[0] = uc[1] = uc[2] = uc[3] = uc[4] = uc[5] = 0;
				}
			}

		if(bytes_written >= expected_output_filesize) break;

		/* write the data */
		a = fwrite(uc, sizeof(char),  out_header -> byte_p_spl, stdout);
		bytes_written += (double)a;		
		display_counter += a;

		} /* end for all possible input files */

	/* report progress every now end then... */
	if( ( display_counter > 1024) || (bytes_written >= expected_output_filesize) )
		{
		da = 100.0 * (bytes_written / expected_output_filesize);
   
		if(! silent_flag)
			{
			fprintf(stderr, "writing output file %.0f (%.2f%%)\r", bytes_written, da);
			}
		else
			{
			fprintf(stderr, "(%.2f%%)\r", da);
			}	

		display_counter = 0;
		}

	if(bytes_written >= expected_output_filesize) break;

	} /* end while */

/* close input files */
for(i = 0; i < input_files; i++)
	{
	fclose(ifptr[i]);
	}

fprintf(stderr, "multimux ready\n");

exit(0);
} /* end function main */


