Merged revisions 141806 via svnmerge from
[asterisk/asterisk.git] / utils / frame.c
1 /****************************************************************************
2  *
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.
6  *
7  * Name:    frame.c
8  * Version: see static char *standardversion, below.
9  * Author:  Mark Roberts <mark@manumark.de>
10  *          Michael Labuschke <michael@labuschke.de> sys_errlist fixes 
11  *              
12  ****************************************************************************/
13 /****************************************************************************
14  *  These are useful functions that all DSP programs might find handy
15  ****************************************************************************/
16
17 #include <stdio.h>
18 #include <math.h>
19 #include <stdlib.h> /* for exit and malloc */
20 #include <string.h>
21 #include <time.h>
22 #include <stdarg.h>
23 #include <errno.h>
24 #include <assert.h>
25 #include "frame.h"
26
27 time_t stopwatch;       /* will hold time at start of calculation */
28 int samplefrequency;
29 unsigned short samplewidth;
30 unsigned short channels;
31 int wavout;            /* TRUE iff out file should be a .WAV file */
32 int iswav;             /* TRUE iff in file was found to be a .WAV file */
33 FILE *in, *out;
34 char *infilename, *outfilename;
35 int verboselevel;
36 char *version = "";
37 char *usage = "";
38 static int test_usage;
39
40 static char *standardversion = "frame version 1.3, June 13th 2001";
41 static char *standardusage =
42 "\nOptions common to all mark-dsp programs:\n"
43
44 "-h \t\t create a WAV-header on output files.\n"
45 "-c#\t\t set number of channels to # (1 or 2). Default: like input.\n"
46 "-w#\t\t set number of bits per sample (width) to # (only 16)\n"
47 "-f#\t\t set sample frequency to #. Default: like input.\n"
48 "-V \t\t verbose: talk a lot.\n"
49 "-Q \t\t quiet: talk as little as possible.\n\n"
50 "In most cases, a filename of '-' means stdin or stdout.\n\n"
51 "Bug-reports: mark@manumark.de\n"
52 ;
53
54 /* -----------------------------------------------------------------------
55    Writes the number of samples to result that are yet to be read from anyin.
56    Return values are TRUE on success, FALSE on failure.
57    -----------------------------------------------------------------------*/
58 int getremainingfilelength( FILE *anyin, long *result)
59 {
60     long i;
61
62     i = ftell (anyin);
63     if (i == -1) return FALSE;
64     if (fseek (anyin, 0, SEEK_END) == -1) return FALSE;
65     *result = ftell (anyin);
66     if (*result == -1) return FALSE;
67     (*result) -= i;
68     (*result) /= samplewidth;
69     if (fseek (anyin, i, SEEK_SET) == -1) return FALSE;
70     return TRUE;
71 }
72
73 /* -----------------------------------------------------------------------
74    Read a .pk-header from 'anyin'.
75    -----------------------------------------------------------------------*/
76 void readpkheader( FILE *anyin)
77 {
78    unsigned short tempushort;
79    int tempint, i, x;
80    unsigned char blood[8];
81
82    for (i = 0; i < 11; i++)
83    {
84       fread( &tempint, 4, 1, anyin);
85       printf( "%d: %d, ", i, tempint);
86    }
87    printf( "\n");
88    fread( blood, 1, 8, anyin);
89    for (i = 0; i < 8; i++)
90       printf( "%d ", blood[i]);
91    printf( "\n");
92    for (i = 0; i < 8; i++)
93       {
94       for (x = 128; x > 0; x /= 2)
95          printf((blood[i] & x) == 0? "0 ":"1 ");
96       printf(i%4==3? "\n":"| ");
97       }
98    printf( "\n");
99    for (i = 0; i < 2; i++)
100    {
101       fread( &tempint, 4, 1, anyin);
102       printf( "%d: %d, ", i, tempint);
103    }
104    printf( "\n");
105    for (i = 0; i < 2; i++)
106    {
107       fread( &tempushort, 2, 1, anyin);
108       printf( "%d: %d, ", i, tempushort);
109    }
110    printf( "\n");
111 }
112
113
114
115 /* -----------------------------------------------------------------------
116    Read a .WAV header from 'anyin'. See header for details.
117    -----------------------------------------------------------------------*/
118 void readwavheader( FILE *anyin)
119 {
120    unsigned int tempuint, sf;
121    unsigned short tempushort, cn;
122    char str[9];
123    int nowav = FALSE;
124
125    iswav = FALSE;
126
127    if (ftell(anyin) == -1) /* If we cannot seek this file */
128      {
129        nowav = TRUE;   /* -> Pretend this is no wav-file */
130        chat("File not seekable: not checking for WAV-header.\n");
131      }
132    else
133      {
134        /* Expect four bytes "RIFF" and four bytes filelength */
135        fread (str, 1, 8, anyin);           /* 0 */
136        str[4] = '\0';
137        if (strcmp(str, "RIFF") != 0) nowav = TRUE;
138        /* Expect eight bytes "WAVEfmt " */
139        fread (str, 1, 8, anyin);           /* 8 */
140        str[8] = '\0';
141        if (strcmp(str, "WAVEfmt ") != 0) nowav = TRUE;
142        /* Expect length of fmt data, which should be 16 */
143        fread (&tempuint, 4, 1, anyin);   /* 16 */
144        if (tempuint != 16) nowav = TRUE;
145        /* Expect format tag, which should be 1 for pcm */
146        fread (&tempushort, 2, 1, anyin); /* 20 */
147        if (tempushort != 1)
148          nowav = TRUE;
149        /* Expect number of channels */
150        fread (&cn, 2, 1, anyin); /* 20 */
151        if (cn != 1 && cn != 2) nowav = TRUE;
152        /* Read samplefrequency */
153        fread (&sf, 4, 1, anyin);  /* 24 */
154        /* Read bytes per second: Should be samplefreq * channels * 2 */
155        fread (&tempuint, 4, 1, anyin);         /* 28 */
156        if (tempuint != sf * cn * 2) nowav = TRUE;
157        /* read bytes per frame: Should be channels * 2 */
158        fread (&tempushort, 2, 1, anyin);       /* 32 */
159        if (tempushort != cn * 2) nowav = TRUE;
160        /* Read bits per sample: Should be 16 */
161        fread (&tempushort, 2, 1, anyin);       /* 34 */
162        if (tempushort != 16) nowav = TRUE;
163        fread (str, 4, 1, anyin);            /* 36 */
164        str[4] = '\0';
165        if (strcmp(str, "data") != 0) nowav = TRUE;
166        fread (&tempuint, 4, 1, anyin);   /* 40 */
167        if (nowav)
168          {
169            fseek (anyin, 0, SEEK_SET);   /* Back to beginning of file */
170            chat("File has no WAV header.\n");
171          }
172        else
173          {
174            samplefrequency = sf;
175            channels = cn;
176            chat ("Read WAV header: %d channels, samplefrequency %d.\n",
177                  channels, samplefrequency);
178            iswav = TRUE;
179          }
180      }
181    return;
182 }
183
184
185
186 /* -----------------------------------------------------------------------
187    Write a .WAV header to 'out'. See header for details.
188    -----------------------------------------------------------------------*/
189 void makewavheader( void)
190 {
191    unsigned int tempuint, filelength;
192    unsigned short tempushort;
193
194    /* If fseek fails, don't create the header. */
195    if (fseek (out, 0, SEEK_END) != -1)
196      {
197        filelength = ftell (out);
198        chat ("filelength %d, ", filelength);
199        fseek (out, 0, SEEK_SET);
200        fwrite ("RIFF", 1, 4, out);   /* 0 */
201        tempuint = filelength - 8; fwrite (&tempuint, 4, 1, out);   /* 4 */
202        fwrite ("WAVEfmt ", 1, 8, out);   /* 8 */
203        /* length of fmt data 16 bytes */
204        tempuint = 16;
205        fwrite (&tempuint, 4, 1, out);   /* 16 */
206        /* Format tag: 1 for pcm */
207        tempushort = 1;
208        fwrite (&tempushort, 2, 1, out); /* 20 */
209        chat ("%d channels\n", channels);
210        fwrite (&channels, 2, 1, out);
211        chat ("samplefrequency %d\n", samplefrequency);
212        fwrite (&samplefrequency, 4, 1, out);  /* 24 */
213        /* Bytes per second */
214        tempuint = channels * samplefrequency * 2;
215        fwrite (&tempuint, 4, 1, out);         /* 28 */
216        /* Block align */
217        tempushort = 2 * channels;
218        fwrite (&tempushort, 2, 1, out);       /* 32 */
219        /* Bits per sample */
220        tempushort = 16;
221        fwrite (&tempushort, 2, 1, out);       /* 34 */
222        fwrite ("data", 4, 1, out);            /* 36 */
223        tempuint = filelength - 44; fwrite (&tempuint, 4, 1, out);   /* 40 */
224      }
225    return;
226 }
227
228 /* -----------------------------------------------------------------------
229    After all is read and done, inform the inclined user of the elapsed time
230    -----------------------------------------------------------------------*/
231 static void statistics( void)
232 {
233    int temp;
234
235    temp = time(NULL) - stopwatch;
236    if (temp != 1)
237    {
238       inform ("\nTime: %d seconds\n", temp);
239    }
240    else
241    {
242       inform ("\nTime: 1 second\n");
243    }
244    return;
245 }
246
247
248 /* -----------------------------------------------------------------------
249    Start the stopwatch and make sure the user is informed at end of program.
250    -----------------------------------------------------------------------*/
251 void startstopwatch(void)
252 {
253    stopwatch = time(NULL);       /* Remember time 'now' */
254    atexit(statistics);           /* Call function statistics() at exit. */
255
256    return;
257 }
258
259 /* --------------------------------------------------------------------
260    Tests the character 'coal' for being a command line option character,
261    momentarrily '-'.
262    -------------------------------------------------------------------- */
263 int isoptionchar (char coal)
264 {
265    return (coal =='-');
266 }
267
268 /* -----------------------------------------------------------------------
269    Reads through the arguments on the lookout for an option starting
270    with 'string'. The rest of the option is read as a time and passed
271    to *result, where the result is meant to mean 'number of samples' in
272    that time.
273    On failure, *result is unchanged.
274    return value is TRUE on success, FALSE otherwise.
275    -----------------------------------------------------------------------*/
276 int parsetimearg( int argcount, char *args[], char *string, int *result)
277 {
278     int i;
279
280     if ((i = findoption( argcount, args, string)) > 0)
281     {
282         if (parsetime(args[i] + 1 + strlen( string), result))
283             return TRUE;
284         argerrornum(args[i]+1, ME_NOTIME);
285     }
286     return FALSE;
287 }
288
289 /* -----------------------------------------------------------------------
290    The string argument is read as a time and passed
291    to *result, where the result is meant to mean 'number of samples' in
292    that time.
293    On failure, *result is unchanged.
294    return value is TRUE on success, FALSE otherwise.
295    -----------------------------------------------------------------------*/
296 int parsetime(char *string, int *result)
297 {
298     int k;
299     double temp;
300     char m, s, end;
301
302     k = sscanf(string, "%lf%c%c%c", &temp, &m, &s, &end);
303     switch (k)
304       {
305       case 0: case EOF: case 4:
306         return FALSE;
307       case 1:
308         *result = temp;
309         break;
310       case 2:
311         if (m == 's')
312           *result = temp * samplefrequency;
313         else
314           return FALSE;
315         break;
316       case 3:
317         if (m == 'm' && s == 's')
318           *result = temp * samplefrequency / 1000;
319         else if (m == 'H' && s == 'z')
320           *result = samplefrequency / temp;
321         else
322           return FALSE;
323         break;
324       default:
325         argerrornum(NULL, ME_THISCANTHAPPEN);
326       }
327     return TRUE;
328 }
329
330 /* -----------------------------------------------------------------------
331    The string argument is read as a frequency and passed
332    to *result, where the result is meant to mean 'number of samples' in
333    one cycle of that frequency.
334    On failure, *result is unchanged.
335    return value is TRUE on success, FALSE otherwise.
336    -----------------------------------------------------------------------*/
337 int parsefreq(char *string, double *result)
338 {
339     int k;
340     double temp;
341     char m, s, end;
342
343     k = sscanf(string, "%lf%c%c%c", &temp, &m, &s, &end);
344     switch (k)
345       {
346       case 0: case EOF: case 2: case 4:
347         return FALSE;
348       case 1:
349         *result = temp;
350         break;
351       case 3:
352         if (m == 'H' && s == 'z')
353           *result = samplefrequency / temp;
354         else
355           return FALSE;
356         break;
357       default:
358         argerrornum(NULL, ME_THISCANTHAPPEN);
359       }
360     return TRUE;
361 }
362
363 char *parsefilearg( int argcount, char *args[])
364 {
365   int i;
366   char *result = NULL;
367
368    for (i = 1; i < argcount; i++)
369    {
370       if (args[i][0] != '\0' &&
371           (!isoptionchar (args[i][0]) || args[i][1] == '\0' ))
372       {
373         /*---------------------------------------------*
374          * The argument is a filename:                 *
375          * it is either no dash followed by something, *
376          * or it is a dash following by nothing.       *
377          *---------------------------------------------*/
378         result = malloc( strlen( args[i]) + 1);
379         if (result == NULL)
380             fatalperror( "Couldn't allocate memory for filename\n");
381         strcpy( result, args[i]);
382         args[i][0] = '\0';                    /* Mark as used up */
383         break;
384       }
385    }
386    return result;
387 }
388
389 int parseswitch( char *found, char *wanted)
390 {
391   if (strncmp( found, wanted, strlen( wanted)) == 0)
392     {
393       if (found[strlen( wanted)] == '\0')
394         return TRUE;
395       else
396         argerrornum( found, ME_NOSWITCH);
397     }
398   return FALSE;
399 }
400
401 int parseswitcharg( int argcount, char *args[], char *string)
402 {
403   int i;
404
405   if ((i = findoption( argcount, args, string)) > 0)
406     {
407       if (args[i][strlen( string) + 1] == '\0')
408         return TRUE;
409       else
410         argerrornum( args[i] + 1, ME_NOSWITCH);
411     }
412   return FALSE;
413 }
414
415 int parseintarg( int argcount, char *args[], char *string, int *result)
416 {
417   int i, temp;
418   char c;
419
420   if ((i = findoption( argcount, args, string)) > 0)
421    {
422       switch (sscanf(args[i] + 1 + strlen( string),
423                      "%d%c", &temp, &c))
424       {
425         case 0: case EOF: case 2:
426             argerrornum(args[i]+1, ME_NOINT);
427             return FALSE;
428          case 1:
429            *result = temp;
430             break;
431          default:
432             say("frame.c: This can't happen\n");
433       }
434       return TRUE;
435    }
436   else
437     {
438       return FALSE;
439     }
440 }
441
442 /* --------------------------------------------------------------------
443    Reads through the arguments on the lookout for an option starting
444    with 'string'. The rest of the option is read as a double and
445    passed to *result.
446    On failure, *result is unchanged.
447    return value is TRUE on success, FALSE otherwise.
448    -------------------------------------------------------------------- */
449 int parsedoublearg( int argcount, char *args[], char *string, double *result)
450 {
451   int i;
452   double temp;
453   char end;
454
455   if ((i = findoption( argcount, args, string)) > 0)
456     {
457       switch (sscanf(args[i] + 1 + strlen( string), "%lf%c", &temp, &end))
458         {
459         case 0: case EOF: case 2:
460           argerrornum(args[i]+1, ME_NODOUBLE);
461           return FALSE;
462         case 1:
463           *result = temp;
464           break;
465         default:
466           say("frame.c: This can't happen\n");
467         }
468       return TRUE;
469     }
470   else
471     {
472       return FALSE;
473     }
474 }
475
476 /* --------------------------------------------------------------------
477    Reads through the arguments on the lookout for an option starting
478    with 'string'. The rest of the option is read as a volume, i.e.
479    absolute, percent or db. The result is passed to *result.
480    On failure, *result is unchanged.
481    return value is TRUE on success, FALSE otherwise.
482    -------------------------------------------------------------------- */
483 int parsevolarg( int argcount, char *args[], char *string, double *result)
484 {
485   double vol = 1.0;
486   char sbd, sbb, end;
487   int i, weird = FALSE;
488
489   if ((i = findoption( argcount, args, string)) > 0)
490     {
491       switch (sscanf(args[i] + 1 + strlen( string),
492                      "%lf%c%c%c", &vol, &sbd, &sbb, &end))
493         {
494           case 0: case EOF: case 4:
495           weird = TRUE;
496           break;    /* No number: error */
497         case 1:
498           *result = vol;
499           break;
500         case 2:
501           if (sbd == '%')
502             *result = vol / 100;
503           else
504             weird = TRUE;    /* One char but no percent: error */
505           break;
506         case 3:
507           if (sbd =='d' && sbb == 'b')
508             *result = pow(2, vol / 6.02);
509           else
510             weird = TRUE;    /* Two chars but not db: error */
511           break;
512         default:
513           say("frame.c: This can't happen.\n");
514         }
515       if (weird)
516         argerrornum( args[i] + 1, ME_NOVOL);
517           /* ("Weird option: couldn't parse volume '%s'\n", args[i]+2); */
518       return !weird;
519     }
520   else
521     {
522       return FALSE;
523     }
524 }
525
526
527 /* --------------------------------------------------------------------
528    Reads the specified string 's' and interprets it as a volume. The string
529    would be of the form 1.8 or 180% or 5db.
530    On success, the return value TRUE and *result is given result
531    (i.e. the relative volume, i.e. 1.8). On failure, FALSE is returned and
532    result is given value 1.0.
533    -------------------------------------------------------------------- */
534 int parsevolume(char *s, double *result)
535 {
536     int k;
537     char sbd, sbb, end;
538
539     *result = 1.0;
540     k = sscanf(s, "%lf%c%c%c", result, &sbd, &sbb, &end);
541     switch (k)
542     {
543       case 0:
544       case EOF:
545       case 4:
546        return FALSE;
547       case 1:
548        break;
549       case 2:
550        if (sbd != '%')
551            return FALSE;
552        (*result) /=100;
553        break;
554       case 3:
555        if (sbd !='d' || sbb != 'b')
556            return FALSE;
557        (*result) = pow(2, (*result) / 6.02);
558        break;
559       default:
560        say("parsevolume: This can't happen (%d).\n", k);
561     }
562     return TRUE;
563 }
564
565 /* --------------------------------------------------------------------
566    Reports an error due to parsing the string 's' encountered on the
567    command line.
568    -------------------------------------------------------------------- */
569 void argerror(char *s)
570 {
571   error ("Error parsing command line. Unrecognized option:\n\t-%s\n", s);
572   fatalerror("\nTry --help for help.\n");
573 }
574
575 /* --------------------------------------------------------------------
576    Reports an error due to parsing the string 's' encountered on the
577    command line. 'code' indicates the type of error.
578    -------------------------------------------------------------------- */
579 void argerrornum(char *s, Errornum code)
580 {
581   char *message;
582
583   if (code == ME_TOOMANYFILES)
584     {
585       error("Too many files on command line: '%s'.\n", s);
586     }
587   else
588     {
589       if (s != NULL)
590         error ("Error parsing option -%s:\n\t", s);
591       switch( code)
592         {
593         case ME_NOINT:
594           message = "Integer expected";
595           break;
596         case ME_NODOUBLE:
597           message = "Floating point number expected";
598           break;
599         case ME_NOTIME:
600           message = "Time argument expected";
601           break;
602         case ME_NOVOL:
603           message = "Volume argument expected";
604           break;
605         case ME_NOSWITCH:
606           message = "Garbage after switch-type option";
607           break;
608         case ME_HEADERONTEXTFILE:
609           message = "Option -h is not useful for text-output";
610           break;
611         case ME_NOINFILE:
612           message = "No input file specified";
613           break;
614         case ME_NOOUTFILE:
615           message = "No output file specified";
616           break;
617         case ME_NOIOFILE:
618           message = "No input/output file specified";
619           break;
620         case ME_NOSTDIN:
621           message = "Standard in not supported here";
622           break;
623         case ME_NOSTDOUT:
624           message = "Standard out not supported here";
625           break;
626         case ME_NOSTDIO:
627           message = "Standard in/out not supported here";
628           break;
629         case ME_NOTENOUGHFILES:
630           message = "Not enough files specified";
631           break;
632         case ME_THISCANTHAPPEN:
633           fatalerror("\nThis can't happen. Report this as a bug\n");
634           /* fatalerror does not return */
635         default:
636           error("Error code %d not implemented. Fix me!\n", code);
637           message = "Error message not implemented. Fix me!";
638         }
639       error("%s\n", message);
640     }
641   fatalerror("\nTry --help for help.\n");
642 }
643
644 /* --------------------------------------------------------------------
645    Reports an error due to parsing the string 's' encountered on the
646    command line. 'message' explains the type of error.
647    -------------------------------------------------------------------- */
648 void argerrortxt(char *s, char *message)
649 {
650   if (s != NULL)
651     error ("Error parsing option -%s:\n\t", s);
652   else
653     error ("Error parsing command line:\n\t");
654   error ("%s\n", message);
655   fatalerror("\nTry --help for help.\n");
656 }
657
658 /* --------------------------------------------------------------------
659    Check for any remaining arguments and complain about their existence
660    -------------------------------------------------------------------- */
661 void checknoargs( int argcount, char *args[])
662 {
663   int i, errorcount = 0;
664
665   for (i = 1; i < argcount; i++)
666     {
667       if (args[i][0] != '\0')   /* An unused argument! */
668         {
669           errorcount++;
670           if (errorcount == 1)
671             error("The following arguments were not recognized:\n");
672           error("\t%s\n", args[i]);
673         }
674     }
675   if (errorcount > 0)           /* Errors are fatal */
676     fatalerror("\nTry --help for help.\n");
677
678   return;                       /* No errors? Return. */
679 }
680
681 /* --------------------------------------------------------------------
682    Parses the command line arguments as represented by the function
683    arguments. Sets the global variables 'in', 'out', 'samplefrequency'
684    and 'samplewidth' accordingly. Also verboselevel.
685    The files 'in' and 'out' are even opened according to 'fileswitch'.
686    See headerfile for details
687    -------------------------------------------------------------------- */
688 void parseargs( int argcount, char *args[], int fileswitch)
689 {
690    char *filename;
691    int tempint;
692
693    if ((fileswitch & 1) != 0)     /* If getting infile  */
694      in = NULL;
695    if ((fileswitch & 4) != 0)     /* If getting outfile */
696      out = NULL;
697    wavout = FALSE;
698    verboselevel = 5;
699    samplefrequency = DEFAULTFREQ;
700    samplewidth = 2;
701    channels = 1;
702
703    /*-----------------------------------------------*
704     * First first check testcase, usage and version *
705     *-----------------------------------------------*/
706    test_usage = parseswitcharg( argcount, args, "-test-usage");
707    if (parseswitcharg( argcount, args, "-help"))
708        {
709          printf("%s%s", usage, standardusage);
710          exit(0);
711        }
712    if (parseswitcharg( argcount, args, "-version"))
713        {
714          printf("%s\n(%s)\n", version, standardversion);
715          exit(0);
716        }
717    /*--------------------------------------*
718     * Set verboselevel                     *
719     *--------------------------------------*/
720    while (parseswitcharg( argcount, args, "V"))
721                verboselevel = 10;
722    while (parseswitcharg( argcount, args, "Q"))
723                verboselevel = 1;
724    /*-------------------------------------------------*
725     * Get filenames and open files *
726     *-------------------------------------------------*/
727    if ((fileswitch & 1) != 0)        /* Infile wanted */
728      {
729        infilename = parsefilearg( argcount, args);
730        if (infilename == NULL)
731          argerrornum( NULL, ME_NOINFILE);
732        if (strcmp( infilename, "-") == 0)
733          {
734            infilename = "<stdin>";
735            in = stdin;
736            if ((fileswitch & 2) != 0)   /* Binfile wanted */
737              readwavheader( in);
738          }
739        else
740          {
741            if ((fileswitch & 2) == 0)   /* Textfile wanted */
742              in = fopen(infilename, "rt");
743            else                         /* Binfile wanted */
744              if ((in = fopen(infilename, "rb")) != NULL)
745                readwavheader( in);
746          }
747        if (in == NULL)
748          fatalerror("Error opening input file '%s': %s\n", infilename,strerror(errno));
749        else
750          inform("Using file '%s' as input\n", infilename);
751      }
752    if ((fileswitch & 4) != 0)        /* Outfile wanted */
753      {
754        outfilename = parsefilearg( argcount, args);
755        if (outfilename == NULL)
756          argerrornum( NULL, ME_NOOUTFILE);
757        if (strcmp( outfilename, "-") == 0)
758          {
759            outfilename = "<stdout>";
760            out = stdout;
761          }
762        else
763          {
764
765            if ((fileswitch & 8) == 0)   /* Textfile wanted */
766              out = fopen(outfilename, "wt");
767            else                         /* Binfile wanted */
768              out = fopen(outfilename, "wb");
769          }
770        if (out == NULL)
771          fatalerror("Error opening output file '%s': %s\n", outfilename,strerror(errno));
772        else
773          inform("Using file '%s' as output\n", outfilename);
774      }
775    if ((fileswitch & 32) != 0)      /* In-/Outfile wanted */
776      {
777        assert (in == NULL && out == NULL);
778        infilename = outfilename = parsefilearg( argcount, args);
779        if (outfilename == NULL)
780          argerrornum( NULL, ME_NOIOFILE);
781        if (strcmp( infilename, "-") == 0)
782          argerrornum( infilename, ME_NOSTDIN);
783        inform("Using file '%s' as input/output\n", outfilename);
784        in = out = fopen(outfilename, "r+");
785        if (out == NULL)
786          fatalerror("Error opening input/output file '%s': %s\n", outfilename,strerror(errno));
787
788        readwavheader( in);
789      }
790    if ((fileswitch & 16) == 0)  /* No additional files wanted */
791      {
792        if ((filename = parsefilearg( argcount, args)) != NULL)
793          argerrornum( filename, ME_TOOMANYFILES);
794      }
795
796    /*-------------------------------------------------*
797     * Set samplefrequency, width, wavout, 
798     *-------------------------------------------------*/
799    parseintarg( argcount, args, "f", &samplefrequency);
800    wavout = parseswitcharg( argcount, args, "h");
801    if (parseintarg( argcount, args, "w", &tempint))
802      {
803        if (tempint != 16)
804          argerrortxt(NULL, "Option -w is only valid "
805                      "with value 16. Sorry.");
806        else
807          samplewidth = tempint;
808      }
809    if (parseintarg( argcount, args, "c", &tempint))
810      {
811        if (tempint != 1 && tempint != 2)
812          argerrortxt(NULL, "Option -c is only valid "
813                      "with values 1 or 2. Sorry.");
814        else
815          channels = tempint;
816      }
817    /*-------------------------------------------------*
818     * Create WAV-header on output if wanted.          *
819     *-------------------------------------------------*/
820    if (wavout)
821      switch (fileswitch & (12))
822        {
823        case 4:   /* User wants header on textfile */
824          argerrornum( NULL, ME_HEADERONTEXTFILE);
825        case 12:  /* User wants header on binfile  */
826          makewavheader();
827          break;
828        case 0:   /* User wants header, but there is no outfile */
829          /* Problem: what about i/o-file, 32? You might want a header
830             on that? Better ignore this case. */
831          break;
832        case 8:    /* An application musn't ask for this */
833        default:   /* This can't happen */
834          assert( FALSE);
835        }
836    return;
837 }
838
839 /* --------------------------------------------------------------------
840    Returns the index 'i' of the first argument that IS an option, and
841    which begins with the label 's'. If there is none, -1.
842    We also mark that option as done with, i.e. we cross it out.
843    -------------------------------------------------------------------- */
844 int findoption( int argcount, char *args[], char *s)
845 {
846    int i;
847
848    if (test_usage)
849      printf("Checking for option -%s\n", s);
850
851    for (i=1; i<argcount; i++)
852    {
853      if (isoptionchar (args[i][0]) &&
854          strncmp( args[i] + 1, s, strlen( s)) == 0)
855        {
856          args[i][0] = '\0';
857          return i;
858        }
859    }
860    return -1;
861 }
862
863 /* --------------------------------------------------------------------
864    Finishes off the .WAV header (if any) and exits correctly and formerly.
865    -------------------------------------------------------------------- */
866 int myexit (int value)
867 {
868   switch (value)
869     {
870     case 0:
871       if (wavout)
872         makewavheader();  /* Writes a fully informed .WAV header */
873       chat ("Success!\n");
874       break;
875     default:
876       chat ("Failure.\n");
877       break;
878     }
879   exit (value);
880 }
881
882 /* --------------------------------------------------------------------
883    Reads the stated input file bufferwise, calls the function 'work'
884    with the proper values, and writes the result to the stated output file.
885    Return value: TRUE on success, FALSE otherwise.
886    -------------------------------------------------------------------- */
887 int workloop( FILE *theinfile, FILE *theoutfile,
888               int (*work)( short *buffer, int length) )
889 {
890   short *buffer;
891   int length, nowlength;
892
893   length = BUFFSIZE;
894   if ((buffer = malloc( sizeof(short) * length)) == NULL)
895     fatalperror ("");
896   while (TRUE)
897     {
898       nowlength = fread(buffer, sizeof(short), length, theinfile);
899       if (ferror( theinfile) != 0)
900         fatalperror("Error reading input file");
901       if (nowlength == 0)   /* Reached end of input file */
902         break;
903       /* Call the routine that does the work */
904       if (!work (buffer, nowlength))         /* On error, stop. */
905         return FALSE;
906       fwrite(buffer, sizeof(short), nowlength, theoutfile);
907       if (ferror( theoutfile) != 0)
908         fatalperror("Error writing to output file");
909     }
910   return TRUE;      /* Input file done with, no errors. */
911 }
912
913 int __attribute__((format (printf,1,2))) chat( const char *format, ...)
914 {
915     va_list ap;
916     int result = 0;
917
918     if (verboselevel > 5)
919     {
920         va_start( ap, format);
921         result = vfprintf( stderr, format, ap);
922         va_end( ap);
923     }
924     return result;
925 }
926
927 int __attribute__((format (printf,1,2))) inform( const char *format, ...)
928 {
929     va_list ap;
930     int result = 0;
931
932     if (verboselevel > 1)
933     {
934         va_start( ap, format);
935         result = vfprintf( stderr, format, ap);
936         va_end( ap);
937     }
938     return result;
939 }
940
941 int __attribute__((format (printf,1,2))) error( const char *format, ...)
942 {
943     va_list ap;
944     int result;
945
946     va_start( ap, format);
947     result = vfprintf( stderr, format, ap);
948     va_end( ap);
949     return result;
950 }
951
952 void __attribute__((format (printf,1,2))) fatalerror( const char *format, ...)
953 {
954     va_list ap;
955
956     va_start( ap, format);
957     vfprintf( stderr, format, ap);
958     va_end( ap);
959     myexit(1);
960 }
961
962 void fatalperror( const char *string)
963 {
964   perror( string);
965   myexit( 1);
966 }
967
968 int __attribute__((format (printf,1,2))) say( const char *format, ...)
969 {
970     va_list ap;
971     int result;
972
973     va_start( ap, format);
974     result = vfprintf( stdout, format, ap);
975     va_end( ap);
976     return result;
977 }
978
979
980 char *malloccopy( char *string)
981 {
982     char *result;
983
984     result = malloc( strlen( string) + 1);
985     if (result != NULL)
986         strcpy( result, string);
987     return result;
988 }
989
990
991 char *mallocconcat( char *one, char *two)
992 {
993     char *result;
994
995     result = malloc( strlen( one) + strlen( two) + 1);
996     if (result != NULL)
997       {
998         strcpy( result, one);
999         strcat( result, two);
1000       }
1001     return result;
1002 }
1003
1004 double double2db( double value)
1005 {
1006   if (value < 0)
1007     value = -value;
1008   return 6.0 * log( value / 32767) / log( 2);
1009 }
1010
1011 void readawaysamples( FILE *input, size_t size)
1012 {
1013   short *buffer;
1014   int samplesread, count;
1015
1016   buffer = malloc( sizeof( *buffer) * BUFFSIZE);
1017   if (buffer == NULL) fatalperror("Couldn't allocate buffer");
1018
1019   while (size > 0)
1020     {
1021       if (size > BUFFSIZE)
1022         count = BUFFSIZE;
1023       else
1024         count = size;
1025
1026       samplesread = fread( buffer, sizeof(*buffer), count, input);
1027       if (ferror( input) != 0)
1028         fatalperror("Error reading input file");
1029       size -= samplesread;
1030     }
1031   free( buffer);
1032 }
1033