1 /****************************************************************************
3 * Programs for processing sound files in raw- or WAV-format.
4 * -- Useful functions for parsing command line options and
5 * issuing errors, warnings, and chit chat.
8 * Version: see static char *standardversion, below.
9 * Author: Mark Roberts <mark@manumark.de>
10 * Michael Labuschke <michael@labuschke.de> sys_errlist fixes
12 ****************************************************************************/
13 /****************************************************************************
14 * These are useful functions that all DSP programs might find handy
15 ****************************************************************************/
19 #include <stdlib.h> /* for exit and malloc */
25 /****************************************************************************
27 * Programs for processing sound files in raw- or WAV-format.
28 * -- Useful functions for parsing command line options and
29 * issuing errors, warnings, and chit chat.
32 * Version: see frame.c
33 * Author: Mark Roberts <mark@manumark.de>
35 ****************************************************************************/
36 /****************************************************************************
37 * These are useful functions that all DSP programs might find handy
38 ****************************************************************************/
43 /* fileswitch for parseargs:
45 The following are masks for several different ways of opening files.
46 --------------------------------------------------------------------
48 Bit 1: Open infile as binary (as opposed to text)
50 Bit 3: Open outfile as binary (as opposed to text)
51 Bit 4: Do not complain about too many file arguments
52 Bit 5: Open one file for input AND output, binary.
59 #define NOCOMPLAIN (16)
67 extern int samplefrequency;
68 extern unsigned short samplewidth;
69 extern unsigned short channels;
70 extern int wavout; /* TRUE iff out file is .WAV file */
71 extern int iswav; /* TRUE iff in file was found to be a .WAV file */
72 extern FILE *in, *out;
73 extern char *infilename, *outfilename;
74 extern int verboselevel;
75 extern char *version; /* String to be issued as version string. Should
76 be set by application. */
77 extern char *usage; /* String to be issued as usage string. Should be
78 set by application. */
80 #define DEFAULTFREQ 44100
81 #define BUFFSIZE 50000 /* How many samples to read in one go (preferred) */
82 #define MINBUFFSIZE 5000 /* How many samples to read in one go (minimum) */
84 /*************************************************
85 * Types of errors handled by argerrornum() *
86 *************************************************/
107 /* -----------------------------------------------------------------------
108 Create memory and copy 'string', returning a pointer to the copy.
109 NULL is returned if malloc fails.
110 -----------------------------------------------------------------------*/
111 extern char *malloccopy( char *string);
113 /* -----------------------------------------------------------------------
114 Start the stopwatch and make sure the user is informed at end of program.
115 -----------------------------------------------------------------------*/
116 extern void startstopwatch(void);
118 /* -----------------------------------------------------------------------
119 Writes the number of samples to result that are yet to be read from anyin.
120 I.e. the number of remaining bytes is divided by the number of bytes per
121 sample value, but not by the number of channels.
122 Return values are TRUE on success, FALSE on failure.
123 -----------------------------------------------------------------------*/
124 extern int getremainingfilelength( FILE *anyin, long *result);
126 /* -----------------------------------------------------------------------
127 Read a .pk-header from 'anyin' and printf the entries.
128 -----------------------------------------------------------------------*/
129 void readpkheader( FILE *anyin);
131 /* -----------------------------------------------------------------------
132 Read a .WAV header from 'anyin'.
133 If it is recognised, the data is used.
134 Otherwise, we assume it's PCM-data and ignore the header.
135 The global variable 'iswav' is set on success, otherwise cleared.
136 -----------------------------------------------------------------------*/
137 extern void readwavheader( FILE *anyin);
139 /* -----------------------------------------------------------------------
140 Write a .WAV header to 'out'.
141 The filepointer is placed at the end of 'out' before operation.
142 This should be called before any data is
143 written, and again, when ALL the data has been written.
144 First time, this positions the file pointer correctly; second time, the
145 missing data can be inserted that wasn't known the first time round.
146 -----------------------------------------------------------------------*/
147 extern void makewavheader( void);
149 /* --------------------------------------------------------------------
150 Tests the character 'coal' for being a command line option character,
151 momentarrily '/' or '-'.
152 -------------------------------------------------------------------- */
153 extern int isoptionchar (char coal);
155 /* -----------------------------------------------------------------------
156 Reads through the arguments on the lookout for an option starting
157 with 'string'. The rest of the option is read as a time and passed
158 to *result, where the result is meant to mean 'number of samples' in
160 On failure, *result is unchanged.
161 return value is TRUE on success, FALSE otherwise.
162 -----------------------------------------------------------------------*/
163 extern int parsetimearg( int argcount, char *args[], char *string,
166 /* -----------------------------------------------------------------------
167 The string argument is read as a time and passed to *result, where
168 the result is meant to mean 'number of samples' in that time. On
169 failure, *result is unchanged.
170 return value is TRUE on success, FALSE otherwise.
171 -----------------------------------------------------------------------*/
172 int parsetime(char *string, int *result);
174 /* -----------------------------------------------------------------------
175 The string argument is read as a frequency and passed
176 to *result, where the result is meant to mean 'number of samples' in
177 one cycle of that frequency.
178 On failure, *result is unchanged.
179 return value is TRUE on success, FALSE otherwise.
180 -----------------------------------------------------------------------*/
181 int parsefreq(char *string, double *result);
183 /* --------------------------------------------------------------------
184 Reads through the arguments on the lookout for a switch -'string'.
185 return value is TRUE if one exists, FALSE otherwise.
186 If characters remain after the switch, a fatal error is issued.
187 -------------------------------------------------------------------- */
188 extern int parseswitcharg( int argcount, char *args[], char *string);
190 /* --------------------------------------------------------------------
191 Reads through the arguments on the lookout for an option starting
192 with 'string'. The rest of the option is read as an integer and
194 On failure, &result is unchanged.
195 return value is TRUE on success, FALSE otherwise.
196 -------------------------------------------------------------------- */
197 extern int parseintarg( int argcount, char *args[], char *string,
200 /* --------------------------------------------------------------------
201 Reads through the arguments on the lookout for a filename, i.e. anything
202 that does not start with the optionchar. The filename is copied to
203 newly allocated memory, a pointer to which is returned.
204 The argument is marked as used. Therefore repeated use of this function
205 will yield a complete list of filenames on the commandline.
206 If malloc() fails, the function does not return.
207 -------------------------------------------------------------------- */
208 extern char *parsefilearg( int argcount, char *args[]);
210 /* --------------------------------------------------------------------
211 Reads through the arguments on the lookout for an option starting
212 with 'string'. The rest of the option is read as a double and
214 On failure, *result is unchanged.
215 return value is TRUE on success, FALSE otherwise.
216 -------------------------------------------------------------------- */
217 extern int parsedoublearg( int argcount, char *args[], char *string,
220 /* --------------------------------------------------------------------
221 Reads through the arguments on the lookout for an option starting
222 with 'string'. The rest of the option is read as a volume, i.e.
223 absolute, percent or db. The result is passed to *result.
224 On failure, *result is unchanged.
225 -------------------------------------------------------------------- */
226 extern int parsevolarg( int argcount, char *args[], char *string,
229 /* --------------------------------------------------------------------
230 Reads the specified string and interprets it as a volume. The string
231 would be of the form 1.8 or 180% or 5db.
232 On success, the return value is the relative volume, i.e. 1.8
233 On failure, -1 is returned.
234 -------------------------------------------------------------------- */
235 extern int parsevolume(char *s, double *result);
237 /* --------------------------------------------------------------------
238 Reads through the arguments on the lookout for a switch -'string'.
239 return value is TRUE if one exists, FALSE otherwise.
240 If characters remain after the switch, a fatal error is issued.
241 -------------------------------------------------------------------- */
242 extern int parseswitch( char *found, char *wanted);
244 /* --------------------------------------------------------------------
245 Reports an error due to parsing the string 's' encountered on the
247 -------------------------------------------------------------------- */
248 extern void argerror(char *s);
250 /* --------------------------------------------------------------------
251 Reports an error due to parsing the string 's' encountered on the
252 command line. 'code' indicates the type of error.
253 -------------------------------------------------------------------- */
254 extern void argerrornum(char *s, Errornum code);
256 /* --------------------------------------------------------------------
257 Reports an error due to parsing the string 's' encountered on the
258 command line. 'message' explains the type of error.
259 -------------------------------------------------------------------- */
260 extern void argerrortxt(char *s, char *message);
262 /* --------------------------------------------------------------------
263 Check for any remaining arguments and complain about their existence.
264 If arguments are found, this function does not return.
265 -------------------------------------------------------------------- */
266 extern void checknoargs( int argcount, char *args[]);
268 /* --------------------------------------------------------------------
269 Parses the command line arguments as represented by the function
270 arguments. Sets the global variables 'in', 'out', 'samplefrequency'
271 and 'samplewidth' accordingly.
272 According to 'fileswitch', in and out files are opened or not. See
273 above for an explanation of 'fileswitch'.
274 -------------------------------------------------------------------- */
275 extern void parseargs( int argcount, char *args[], int fileswitch);
277 /* --------------------------------------------------------------------
278 Returns the index 'i' of the first argument that IS an option, and
279 which begins with the label 's'. If there is none, -1.
280 We also mark that option as done with, i.e. we cross it out.
281 -------------------------------------------------------------------- */
282 extern int findoption( int argcount, char *args[], char *s);
284 /* --------------------------------------------------------------------
285 Finishes off the .WAV header (if any) and exits correctly and formerly.
286 -------------------------------------------------------------------- */
287 extern int myexit (int value);
289 /* --------------------------------------------------------------------
290 Reads the stated input file bufferwise, calls the function 'work'
291 with the proper values, and writes the result to the stated output file.
292 Return value: TRUE on success, FALSE otherwise.
293 -------------------------------------------------------------------- */
294 extern int workloop( FILE *theinfile, FILE *theoutfile,
295 int (*work)( short *buffer, int length) );
297 /* --------------------------------------------------------------------
298 Five functions for printing to stderr. Depending on the level of verbose,
299 output may be supressed. fatalerror() is like error() but does not return.
300 fatalperror() is like the standard function perror() but does not return.
301 -------------------------------------------------------------------- */
302 extern int chat( const char *format, ...);
303 extern int inform( const char *format, ...);
304 extern int error( const char *format, ...);
305 extern void fatalerror( const char *format, ...);
306 extern void fatalperror( const char *string);
308 /* --------------------------------------------------------------------
309 And one functions for printing to stdout.
310 -------------------------------------------------------------------- */
311 extern int say( const char *format, ...);
313 /* --------------------------------------------------------------------
314 Allocate memory for it and return a pointer to a string made up of
315 the two argument strings.
316 -------------------------------------------------------------------- */
317 extern char *mallocconcat( char *one, char *two);
319 /* --------------------------------------------------------------------
320 Convert a sample value to decibel.
321 -------------------------------------------------------------------- */
322 extern double double2db( double value);
324 /* --------------------------------------------------------------------
325 Read 'size' samples from file 'in' and lose them.
326 -------------------------------------------------------------------- */
327 extern void readawaysamples( FILE *in, size_t size);
331 time_t stopwatch; /* will hold time at start of calculation */
333 unsigned short samplewidth;
334 unsigned short channels;
335 int wavout; /* TRUE iff out file should be a .WAV file */
336 int iswav; /* TRUE iff in file was found to be a .WAV file */
338 char *infilename, *outfilename;
342 static int test_usage;
344 static char *standardversion = "frame version 1.3, June 13th 2001";
345 static char *standardusage =
346 "\nOptions common to all mark-dsp programs:\n"
348 "-h \t\t create a WAV-header on output files.\n"
349 "-c#\t\t set number of channels to # (1 or 2). Default: like input.\n"
350 "-w#\t\t set number of bits per sample (width) to # (only 16)\n"
351 "-f#\t\t set sample frequency to #. Default: like input.\n"
352 "-V \t\t verbose: talk a lot.\n"
353 "-Q \t\t quiet: talk as little as possible.\n\n"
354 "In most cases, a filename of '-' means stdin or stdout.\n\n"
355 "Bug-reports: mark@manumark.de\n"
358 /* -----------------------------------------------------------------------
359 Writes the number of samples to result that are yet to be read from anyin.
360 Return values are TRUE on success, FALSE on failure.
361 -----------------------------------------------------------------------*/
362 int getremainingfilelength( FILE *anyin, long *result)
367 if (i == -1) return FALSE;
368 if (fseek (anyin, 0, SEEK_END) == -1) return FALSE;
369 *result = ftell (anyin);
370 if (*result == -1) return FALSE;
372 (*result) /= samplewidth;
373 if (fseek (anyin, i, SEEK_SET) == -1) return FALSE;
377 /* -----------------------------------------------------------------------
378 Read a .pk-header from 'anyin'.
379 -----------------------------------------------------------------------*/
380 void readpkheader( FILE *anyin)
382 unsigned short tempushort;
384 unsigned char blood[8];
386 for (i = 0; i < 11; i++)
388 fread( &tempint, 4, 1, anyin);
389 printf( "%d: %d, ", i, tempint);
392 fread( blood, 1, 8, anyin);
393 for (i = 0; i < 8; i++)
394 printf( "%d ", blood[i]);
396 for (i = 0; i < 8; i++)
398 for (x = 128; x > 0; x /= 2)
399 printf((blood[i] & x) == 0? "0 ":"1 ");
400 printf(i%4==3? "\n":"| ");
403 for (i = 0; i < 2; i++)
405 fread( &tempint, 4, 1, anyin);
406 printf( "%d: %d, ", i, tempint);
409 for (i = 0; i < 2; i++)
411 fread( &tempushort, 2, 1, anyin);
412 printf( "%d: %d, ", i, tempushort);
419 /* -----------------------------------------------------------------------
420 Read a .WAV header from 'anyin'. See header for details.
421 -----------------------------------------------------------------------*/
422 void readwavheader( FILE *anyin)
424 unsigned int tempuint, sf;
425 unsigned short tempushort, cn;
431 if (ftell(anyin) == -1) /* If we cannot seek this file */
433 nowav = TRUE; /* -> Pretend this is no wav-file */
434 chat("File not seekable: not checking for WAV-header.\n");
438 /* Expect four bytes "RIFF" and four bytes filelength */
439 fread (str, 1, 8, anyin); /* 0 */
441 if (strcmp(str, "RIFF") != 0) nowav = TRUE;
442 /* Expect eight bytes "WAVEfmt " */
443 fread (str, 1, 8, anyin); /* 8 */
445 if (strcmp(str, "WAVEfmt ") != 0) nowav = TRUE;
446 /* Expect length of fmt data, which should be 16 */
447 fread (&tempuint, 4, 1, anyin); /* 16 */
448 if (tempuint != 16) nowav = TRUE;
449 /* Expect format tag, which should be 1 for pcm */
450 fread (&tempushort, 2, 1, anyin); /* 20 */
453 /* Expect number of channels */
454 fread (&cn, 2, 1, anyin); /* 20 */
455 if (cn != 1 && cn != 2) nowav = TRUE;
456 /* Read samplefrequency */
457 fread (&sf, 4, 1, anyin); /* 24 */
458 /* Read bytes per second: Should be samplefreq * channels * 2 */
459 fread (&tempuint, 4, 1, anyin); /* 28 */
460 if (tempuint != sf * cn * 2) nowav = TRUE;
461 /* read bytes per frame: Should be channels * 2 */
462 fread (&tempushort, 2, 1, anyin); /* 32 */
463 if (tempushort != cn * 2) nowav = TRUE;
464 /* Read bits per sample: Should be 16 */
465 fread (&tempushort, 2, 1, anyin); /* 34 */
466 if (tempushort != 16) nowav = TRUE;
467 fread (str, 4, 1, anyin); /* 36 */
469 if (strcmp(str, "data") != 0) nowav = TRUE;
470 fread (&tempuint, 4, 1, anyin); /* 40 */
473 fseek (anyin, 0, SEEK_SET); /* Back to beginning of file */
474 chat("File has no WAV header.\n");
478 samplefrequency = sf;
480 chat ("Read WAV header: %d channels, samplefrequency %d.\n",
481 channels, samplefrequency);
490 /* -----------------------------------------------------------------------
491 Write a .WAV header to 'out'. See header for details.
492 -----------------------------------------------------------------------*/
493 void makewavheader( void)
495 unsigned int tempuint, filelength;
496 unsigned short tempushort;
498 /* If fseek fails, don't create the header. */
499 if (fseek (out, 0, SEEK_END) != -1)
501 filelength = ftell (out);
502 chat ("filelength %d, ", filelength);
503 fseek (out, 0, SEEK_SET);
504 fwrite ("RIFF", 1, 4, out); /* 0 */
505 tempuint = filelength - 8; fwrite (&tempuint, 4, 1, out); /* 4 */
506 fwrite ("WAVEfmt ", 1, 8, out); /* 8 */
507 /* length of fmt data 16 bytes */
509 fwrite (&tempuint, 4, 1, out); /* 16 */
510 /* Format tag: 1 for pcm */
512 fwrite (&tempushort, 2, 1, out); /* 20 */
513 chat ("%d channels\n", channels);
514 fwrite (&channels, 2, 1, out);
515 chat ("samplefrequency %d\n", samplefrequency);
516 fwrite (&samplefrequency, 4, 1, out); /* 24 */
517 /* Bytes per second */
518 tempuint = channels * samplefrequency * 2;
519 fwrite (&tempuint, 4, 1, out); /* 28 */
521 tempushort = 2 * channels;
522 fwrite (&tempushort, 2, 1, out); /* 32 */
523 /* Bits per sample */
525 fwrite (&tempushort, 2, 1, out); /* 34 */
526 fwrite ("data", 4, 1, out); /* 36 */
527 tempuint = filelength - 44; fwrite (&tempuint, 4, 1, out); /* 40 */
532 /* -----------------------------------------------------------------------
533 After all is read and done, inform the inclined user of the elapsed time
534 -----------------------------------------------------------------------*/
535 static void statistics( void)
539 temp = time(NULL) - stopwatch;
542 inform ("\nTime: %d seconds\n", temp);
546 inform ("\nTime: 1 second\n");
552 /* -----------------------------------------------------------------------
553 Start the stopwatch and make sure the user is informed at end of program.
554 -----------------------------------------------------------------------*/
555 void startstopwatch(void)
557 stopwatch = time(NULL); /* Remember time 'now' */
558 atexit(statistics); /* Call function statistics() at exit. */
563 /* --------------------------------------------------------------------
564 Tests the character 'coal' for being a command line option character,
566 -------------------------------------------------------------------- */
567 int isoptionchar (char coal)
572 /* -----------------------------------------------------------------------
573 Reads through the arguments on the lookout for an option starting
574 with 'string'. The rest of the option is read as a time and passed
575 to *result, where the result is meant to mean 'number of samples' in
577 On failure, *result is unchanged.
578 return value is TRUE on success, FALSE otherwise.
579 -----------------------------------------------------------------------*/
580 int parsetimearg( int argcount, char *args[], char *string, int *result)
584 if ((i = findoption( argcount, args, string)) > 0)
586 if (parsetime(args[i] + 1 + strlen( string), result))
588 argerrornum(args[i]+1, ME_NOTIME);
593 /* -----------------------------------------------------------------------
594 The string argument is read as a time and passed
595 to *result, where the result is meant to mean 'number of samples' in
597 On failure, *result is unchanged.
598 return value is TRUE on success, FALSE otherwise.
599 -----------------------------------------------------------------------*/
600 int parsetime(char *string, int *result)
606 k = sscanf(string, "%lf%c%c%c", &temp, &m, &s, &end);
609 case 0: case EOF: case 4:
616 *result = temp * samplefrequency;
621 if (m == 'm' && s == 's')
622 *result = temp * samplefrequency / 1000;
623 else if (m == 'H' && s == 'z')
624 *result = samplefrequency / temp;
629 argerrornum(NULL, ME_THISCANTHAPPEN);
634 /* -----------------------------------------------------------------------
635 The string argument is read as a frequency and passed
636 to *result, where the result is meant to mean 'number of samples' in
637 one cycle of that frequency.
638 On failure, *result is unchanged.
639 return value is TRUE on success, FALSE otherwise.
640 -----------------------------------------------------------------------*/
641 int parsefreq(char *string, double *result)
647 k = sscanf(string, "%lf%c%c%c", &temp, &m, &s, &end);
650 case 0: case EOF: case 2: case 4:
656 if (m == 'H' && s == 'z')
657 *result = samplefrequency / temp;
662 argerrornum(NULL, ME_THISCANTHAPPEN);
667 char *parsefilearg( int argcount, char *args[])
672 for (i = 1; i < argcount; i++)
674 if (args[i][0] != '\0' &&
675 (!isoptionchar (args[i][0]) || args[i][1] == '\0' ))
677 /*---------------------------------------------*
678 * The argument is a filename: *
679 * it is either no dash followed by something, *
680 * or it is a dash following by nothing. *
681 *---------------------------------------------*/
682 result = malloc( strlen( args[i]) + 1);
684 fatalperror( "Couldn't allocate memory for filename\n");
685 strcpy( result, args[i]);
686 args[i][0] = '\0'; /* Mark as used up */
693 int parseswitch( char *found, char *wanted)
695 if (strncmp( found, wanted, strlen( wanted)) == 0)
697 if (found[strlen( wanted)] == '\0')
700 argerrornum( found, ME_NOSWITCH);
705 int parseswitcharg( int argcount, char *args[], char *string)
709 if ((i = findoption( argcount, args, string)) > 0)
711 if (args[i][strlen( string) + 1] == '\0')
714 argerrornum( args[i] + 1, ME_NOSWITCH);
719 int parseintarg( int argcount, char *args[], char *string, int *result)
724 if ((i = findoption( argcount, args, string)) > 0)
726 switch (sscanf(args[i] + 1 + strlen( string),
729 case 0: case EOF: case 2:
730 argerrornum(args[i]+1, ME_NOINT);
736 say("frame.c: This can't happen\n");
746 /* --------------------------------------------------------------------
747 Reads through the arguments on the lookout for an option starting
748 with 'string'. The rest of the option is read as a double and
750 On failure, *result is unchanged.
751 return value is TRUE on success, FALSE otherwise.
752 -------------------------------------------------------------------- */
753 int parsedoublearg( int argcount, char *args[], char *string, double *result)
759 if ((i = findoption( argcount, args, string)) > 0)
761 switch (sscanf(args[i] + 1 + strlen( string), "%lf%c", &temp, &end))
763 case 0: case EOF: case 2:
764 argerrornum(args[i]+1, ME_NODOUBLE);
770 say("frame.c: This can't happen\n");
780 /* --------------------------------------------------------------------
781 Reads through the arguments on the lookout for an option starting
782 with 'string'. The rest of the option is read as a volume, i.e.
783 absolute, percent or db. The result is passed to *result.
784 On failure, *result is unchanged.
785 return value is TRUE on success, FALSE otherwise.
786 -------------------------------------------------------------------- */
787 int parsevolarg( int argcount, char *args[], char *string, double *result)
791 int i, weird = FALSE;
793 if ((i = findoption( argcount, args, string)) > 0)
795 switch (sscanf(args[i] + 1 + strlen( string),
796 "%lf%c%c%c", &vol, &sbd, &sbb, &end))
798 case 0: case EOF: case 4:
800 break; /* No number: error */
808 weird = TRUE; /* One char but no percent: error */
811 if (sbd =='d' && sbb == 'b')
812 *result = pow(2, vol / 6.02);
814 weird = TRUE; /* Two chars but not db: error */
817 say("frame.c: This can't happen.\n");
820 argerrornum( args[i] + 1, ME_NOVOL);
821 /* ("Weird option: couldn't parse volume '%s'\n", args[i]+2); */
831 /* --------------------------------------------------------------------
832 Reads the specified string 's' and interprets it as a volume. The string
833 would be of the form 1.8 or 180% or 5db.
834 On success, the return value TRUE and *result is given result
835 (i.e. the relative volume, i.e. 1.8). On failure, FALSE is returned and
836 result is given value 1.0.
837 -------------------------------------------------------------------- */
838 int parsevolume(char *s, double *result)
844 k = sscanf(s, "%lf%c%c%c", result, &sbd, &sbb, &end);
859 if (sbd !='d' || sbb != 'b')
861 (*result) = pow(2, (*result) / 6.02);
864 say("parsevolume: This can't happen (%d).\n", k);
869 /* --------------------------------------------------------------------
870 Reports an error due to parsing the string 's' encountered on the
872 -------------------------------------------------------------------- */
873 void argerror(char *s)
875 error ("Error parsing command line. Unrecognized option:\n\t-%s\n", s);
876 fatalerror("\nTry --help for help.\n");
879 /* --------------------------------------------------------------------
880 Reports an error due to parsing the string 's' encountered on the
881 command line. 'code' indicates the type of error.
882 -------------------------------------------------------------------- */
883 void argerrornum(char *s, Errornum code)
887 if (code == ME_TOOMANYFILES)
889 error("Too many files on command line: '%s'.\n", s);
894 error ("Error parsing option -%s:\n\t", s);
898 message = "Integer expected";
901 message = "Floating point number expected";
904 message = "Time argument expected";
907 message = "Volume argument expected";
910 message = "Garbage after switch-type option";
912 case ME_HEADERONTEXTFILE:
913 message = "Option -h is not useful for text-output";
916 message = "No input file specified";
919 message = "No output file specified";
922 message = "No input/output file specified";
925 message = "Standard in not supported here";
928 message = "Standard out not supported here";
931 message = "Standard in/out not supported here";
933 case ME_NOTENOUGHFILES:
934 message = "Not enough files specified";
936 case ME_THISCANTHAPPEN:
937 fatalerror("\nThis can't happen. Report this as a bug\n");
938 /* fatalerror does not return */
940 error("Error code %d not implemented. Fix me!\n", code);
941 message = "Error message not implemented. Fix me!";
943 error("%s\n", message);
945 fatalerror("\nTry --help for help.\n");
948 /* --------------------------------------------------------------------
949 Reports an error due to parsing the string 's' encountered on the
950 command line. 'message' explains the type of error.
951 -------------------------------------------------------------------- */
952 void argerrortxt(char *s, char *message)
955 error ("Error parsing option -%s:\n\t", s);
957 error ("Error parsing command line:\n\t");
958 error ("%s\n", message);
959 fatalerror("\nTry --help for help.\n");
962 /* --------------------------------------------------------------------
963 Check for any remaining arguments and complain about their existence
964 -------------------------------------------------------------------- */
965 void checknoargs( int argcount, char *args[])
967 int i, errorcount = 0;
969 for (i = 1; i < argcount; i++)
971 if (args[i][0] != '\0') /* An unused argument! */
975 error("The following arguments were not recognized:\n");
976 error("\t%s\n", args[i]);
979 if (errorcount > 0) /* Errors are fatal */
980 fatalerror("\nTry --help for help.\n");
982 return; /* No errors? Return. */
985 /* --------------------------------------------------------------------
986 Parses the command line arguments as represented by the function
987 arguments. Sets the global variables 'in', 'out', 'samplefrequency'
988 and 'samplewidth' accordingly. Also verboselevel.
989 The files 'in' and 'out' are even opened according to 'fileswitch'.
990 See headerfile for details
991 -------------------------------------------------------------------- */
992 void parseargs( int argcount, char *args[], int fileswitch)
997 if ((fileswitch & 1) != 0) /* If getting infile */
999 if ((fileswitch & 4) != 0) /* If getting outfile */
1003 samplefrequency = DEFAULTFREQ;
1007 /*-----------------------------------------------*
1008 * First first check testcase, usage and version *
1009 *-----------------------------------------------*/
1010 test_usage = parseswitcharg( argcount, args, "-test-usage");
1011 if (parseswitcharg( argcount, args, "-help"))
1013 printf("%s%s", usage, standardusage);
1016 if (parseswitcharg( argcount, args, "-version"))
1018 printf("%s\n(%s)\n", version, standardversion);
1021 /*--------------------------------------*
1022 * Set verboselevel *
1023 *--------------------------------------*/
1024 while (parseswitcharg( argcount, args, "V"))
1026 while (parseswitcharg( argcount, args, "Q"))
1028 /*-------------------------------------------------*
1029 * Get filenames and open files *
1030 *-------------------------------------------------*/
1031 if ((fileswitch & 1) != 0) /* Infile wanted */
1033 infilename = parsefilearg( argcount, args);
1034 if (infilename == NULL)
1035 argerrornum( NULL, ME_NOINFILE);
1036 if (strcmp( infilename, "-") == 0)
1038 infilename = "<stdin>";
1040 if ((fileswitch & 2) != 0) /* Binfile wanted */
1045 if ((fileswitch & 2) == 0) /* Textfile wanted */
1046 in = fopen(infilename, "rt");
1047 else /* Binfile wanted */
1048 if ((in = fopen(infilename, "rb")) != NULL)
1052 fatalerror("Error opening input file '%s': %s\n", infilename,strerror(errno));
1054 inform("Using file '%s' as input\n", infilename);
1056 if ((fileswitch & 4) != 0) /* Outfile wanted */
1058 outfilename = parsefilearg( argcount, args);
1059 if (outfilename == NULL)
1060 argerrornum( NULL, ME_NOOUTFILE);
1061 if (strcmp( outfilename, "-") == 0)
1063 outfilename = "<stdout>";
1069 if ((fileswitch & 8) == 0) /* Textfile wanted */
1070 out = fopen(outfilename, "wt");
1071 else /* Binfile wanted */
1072 out = fopen(outfilename, "wb");
1075 fatalerror("Error opening output file '%s': %s\n", outfilename,strerror(errno));
1077 inform("Using file '%s' as output\n", outfilename);
1079 if ((fileswitch & 32) != 0) /* In-/Outfile wanted */
1081 assert (in == NULL && out == NULL);
1082 infilename = outfilename = parsefilearg( argcount, args);
1083 if (outfilename == NULL)
1084 argerrornum( NULL, ME_NOIOFILE);
1085 if (strcmp( infilename, "-") == 0)
1086 argerrornum( infilename, ME_NOSTDIN);
1087 inform("Using file '%s' as input/output\n", outfilename);
1088 in = out = fopen(outfilename, "r+");
1090 fatalerror("Error opening input/output file '%s': %s\n", outfilename,strerror(errno));
1094 if ((fileswitch & 16) == 0) /* No additional files wanted */
1096 if ((filename = parsefilearg( argcount, args)) != NULL)
1097 argerrornum( filename, ME_TOOMANYFILES);
1100 /*-------------------------------------------------*
1101 * Set samplefrequency, width, wavout,
1102 *-------------------------------------------------*/
1103 parseintarg( argcount, args, "f", &samplefrequency);
1104 wavout = parseswitcharg( argcount, args, "h");
1105 if (parseintarg( argcount, args, "w", &tempint))
1108 argerrortxt(NULL, "Option -w is only valid "
1109 "with value 16. Sorry.");
1111 samplewidth = tempint;
1113 if (parseintarg( argcount, args, "c", &tempint))
1115 if (tempint != 1 && tempint != 2)
1116 argerrortxt(NULL, "Option -c is only valid "
1117 "with values 1 or 2. Sorry.");
1121 /*-------------------------------------------------*
1122 * Create WAV-header on output if wanted. *
1123 *-------------------------------------------------*/
1125 switch (fileswitch & (12))
1127 case 4: /* User wants header on textfile */
1128 argerrornum( NULL, ME_HEADERONTEXTFILE);
1129 case 12: /* User wants header on binfile */
1132 case 0: /* User wants header, but there is no outfile */
1133 /* Problem: what about i/o-file, 32? You might want a header
1134 on that? Better ignore this case. */
1136 case 8: /* An application musn't ask for this */
1137 default: /* This can't happen */
1143 /* --------------------------------------------------------------------
1144 Returns the index 'i' of the first argument that IS an option, and
1145 which begins with the label 's'. If there is none, -1.
1146 We also mark that option as done with, i.e. we cross it out.
1147 -------------------------------------------------------------------- */
1148 int findoption( int argcount, char *args[], char *s)
1153 printf("Checking for option -%s\n", s);
1155 for (i=1; i<argcount; i++)
1157 if (isoptionchar (args[i][0]) &&
1158 strncmp( args[i] + 1, s, strlen( s)) == 0)
1167 /* --------------------------------------------------------------------
1168 Finishes off the .WAV header (if any) and exits correctly and formerly.
1169 -------------------------------------------------------------------- */
1170 int myexit (int value)
1176 makewavheader(); /* Writes a fully informed .WAV header */
1177 chat ("Success!\n");
1180 chat ("Failure.\n");
1186 /* --------------------------------------------------------------------
1187 Reads the stated input file bufferwise, calls the function 'work'
1188 with the proper values, and writes the result to the stated output file.
1189 Return value: TRUE on success, FALSE otherwise.
1190 -------------------------------------------------------------------- */
1191 int workloop( FILE *theinfile, FILE *theoutfile,
1192 int (*work)( short *buffer, int length) )
1195 int length, nowlength;
1198 if ((buffer = malloc( sizeof(short) * length)) == NULL)
1202 nowlength = fread(buffer, sizeof(short), length, theinfile);
1203 if (ferror( theinfile) != 0)
1204 fatalperror("Error reading input file");
1205 if (nowlength == 0) /* Reached end of input file */
1207 /* Call the routine that does the work */
1208 if (!work (buffer, nowlength)) /* On error, stop. */
1210 fwrite(buffer, sizeof(short), nowlength, theoutfile);
1211 if (ferror( theoutfile) != 0)
1212 fatalperror("Error writing to output file");
1214 return TRUE; /* Input file done with, no errors. */
1217 int chat( const char *format, ...)
1222 if (verboselevel > 5)
1224 va_start( ap, format);
1225 result = vfprintf( stderr, format, ap);
1232 int inform( const char *format, ...)
1237 if (verboselevel > 1)
1239 va_start( ap, format);
1240 result = vfprintf( stderr, format, ap);
1246 int error( const char *format, ...)
1251 va_start( ap, format);
1252 result = vfprintf( stderr, format, ap);
1257 void fatalerror( const char *format, ...)
1261 va_start( ap, format);
1262 vfprintf( stderr, format, ap);
1267 void fatalperror( const char *string)
1273 int say( const char *format, ...)
1278 va_start( ap, format);
1279 result = vfprintf( stdout, format, ap);
1285 char *malloccopy( char *string)
1289 result = malloc( strlen( string) + 1);
1291 strcpy( result, string);
1296 char *mallocconcat( char *one, char *two)
1300 result = malloc( strlen( one) + strlen( two) + 1);
1303 strcpy( result, one);
1304 strcat( result, two);
1309 double double2db( double value)
1313 return 6.0 * log( value / 32767) / log( 2);
1316 void readawaysamples( FILE *in, size_t size)
1319 int samplesread, count;
1321 buffer = malloc( sizeof( *buffer) * BUFFSIZE);
1322 if (buffer == NULL) fatalperror("Couldn't allocate buffer");
1326 if (size > BUFFSIZE)
1331 samplesread = fread( buffer, sizeof(*buffer), count, in);
1332 if (ferror( in) != 0)
1333 fatalperror("Error reading input file");
1334 size -= samplesread;
1339 /****************************************************************************
1341 * Programs for processing sound files in raw- or WAV-format.
1342 * -- Merge two mono WAV-files to one stereo WAV-file.
1344 * Name: stereorize.c
1346 * Author: Mark Roberts <mark@manumark.de>
1347 * Michael Labuschke <michael@labuschke.de>
1349 ****************************************************************************/
1358 static char *Version = "stereorize 1.1, November 5th 2000";
1359 static char *Usage =
1360 "Usage: stereorize [options] infile-left infile-right outfile\n\n"
1363 " stereorize left.wav right.wav stereo.wav -h\n\n"
1365 "Creates stereo.wav (with WAV-header, option -h) from data in mono files\n"
1366 "left.wav and right.wav.\n"
1369 int main( int argcount, char *args[])
1371 int i, k[2], maxk, stdin_in_use=FALSE;
1372 short *leftsample, *rightsample, *stereosample;
1374 char *filename[2], *tempname;
1382 parseargs( argcount, args, NOFILES | NOCOMPLAIN);
1384 for (i = 0; i < 2; i++)
1386 filename[i] = parsefilearg( argcount, args);
1387 if (filename[i] == NULL)
1388 argerrornum( NULL, ME_NOTENOUGHFILES);
1389 if (strcmp (filename[i], "-") == 0)
1392 argerrortxt( filename[i] + 1,
1393 "Cannot use <stdin> for both input files");
1394 filename[i] = "<stdin>";
1396 stdin_in_use = TRUE;
1400 channel[i] = fopen(filename[i], "rb");
1402 if (channel[i] == NULL)
1403 fatalerror( "Error opening input file '%s': %s\n", filename[i],strerror(errno));
1405 inform("Using file '%s' as input\n", filename[i]);
1407 for (i = 0; i < 2; i++)
1409 assert ( channel[i] != NULL);
1410 readwavheader( channel[i]);
1411 if (iswav && channels != 1)
1412 inform("Warning: '%s' is no mono file\n", filename[i]);
1415 outfilename = parsefilearg( argcount, args);
1416 if (outfilename == NULL) argerrornum( NULL, ME_NOOUTFILE);
1417 if (strcmp (outfilename, "-") == 0)
1419 outfilename = "<stdout>";
1424 out = fopen(outfilename, "wb");
1427 fatalerror( "Error opening output file '%s': %s\n", outfilename,strerror(errno));
1429 inform("Using file '%s' as output\n", outfilename);
1431 if ((tempname = parsefilearg( argcount, args)) != NULL)
1432 argerrornum( tempname, ME_TOOMANYFILES);
1434 checknoargs(argcount, args); /* Check that no arguments are left */
1436 leftsample = malloc( sizeof(*leftsample) * BUFFSIZE);
1437 rightsample = malloc( sizeof(*leftsample) * BUFFSIZE);
1438 stereosample = malloc( sizeof(*leftsample) * 2 * BUFFSIZE);
1439 if (leftsample == NULL || rightsample == NULL || stereosample == NULL)
1442 channels = 2; /* Output files are stereo */
1445 if ((strcmp(outfilename,"<stdout>")!=0) && (fseek( out, 0, SEEK_SET) != 0))
1446 fatalerror("Couldn't navigate output file '%s': %s\n",outfilename, strerror(errno));
1454 for (i = 0; i < 2; i++)
1456 k[i] = fread(i==0? leftsample : rightsample,
1457 sizeof(*leftsample),
1461 fatalerror("Error reading file '%s': %s\n", filename[i],strerror(errno));
1468 /*-------------------------------------------------*
1469 * First the left channel as far as it goes ... *
1470 *-------------------------------------------------*/
1471 for (i = 0; i < k[0]; i++)
1472 stereosample[2 * i] = leftsample[i];
1473 /*-------------------------------------------------*
1474 * ... and fill up till the end of this buffer. *
1475 *-------------------------------------------------*/
1476 for (; i < maxk; i++)
1477 stereosample[2 * i] = 0;
1479 /*-------------------------------------------------*
1480 * Next the right channel as far as it goes ... *
1481 *-------------------------------------------------*/
1482 for (i = 0; i < k[1]; i++)
1483 stereosample[2 * i + 1] = rightsample[i];
1484 /*-------------------------------------------------*
1485 * ... and fill up till the end of this buffer. *
1486 *-------------------------------------------------*/
1487 for (; i < maxk; i++)
1488 stereosample[2 * i + 1] = 0;
1490 fwrite(stereosample, sizeof(*leftsample), 2 * maxk, out);
1491 if (ferror( out) != 0)
1492 fatalerror("Error writing to file '%s': %s\n",
1493 outfilename, strerror(errno));
1495 /* That was an endless loop. This point is never reached. */