/**************************************************************************** * * Programs for processing sound files in raw- or WAV-format. * -- Useful functions for parsing command line options and * issuing errors, warnings, and chit chat. * * Name: frame.c * Version: see static char *standardversion, below. * Author: Mark Roberts * Michael Labuschke sys_errlist fixes * ****************************************************************************/ /**************************************************************************** * These are useful functions that all DSP programs might find handy ****************************************************************************/ #include #include #include /* for exit and malloc */ #include #include #include #include #include /**************************************************************************** * * Programs for processing sound files in raw- or WAV-format. * -- Useful functions for parsing command line options and * issuing errors, warnings, and chit chat. * * Name: frame.h * Version: see frame.c * Author: Mark Roberts * ****************************************************************************/ /**************************************************************************** * These are useful functions that all DSP programs might find handy ****************************************************************************/ #ifndef ASDF #define ASDF /* fileswitch for parseargs: The following are masks for several different ways of opening files. -------------------------------------------------------------------- Bit 0: Open infile? Bit 1: Open infile as binary (as opposed to text) Bit 2: Open outfile? Bit 3: Open outfile as binary (as opposed to text) Bit 4: Do not complain about too many file arguments Bit 5: Open one file for input AND output, binary. */ #define INTEXT (1+0) #define INBIN (1+2) #define OUTTEXT (4) #define OUTBIN (4+8) #define NOFILES (0) #define NOCOMPLAIN (16) #define IOBIN (32) #ifndef FALSE #define FALSE (0==1) #define TRUE (0==0) #endif extern int samplefrequency; extern unsigned short samplewidth; extern unsigned short channels; extern int wavout; /* TRUE iff out file is .WAV file */ extern int iswav; /* TRUE iff in file was found to be a .WAV file */ extern FILE *in, *out; extern char *infilename, *outfilename; extern int verboselevel; extern char *version; /* String to be issued as version string. Should be set by application. */ extern char *usage; /* String to be issued as usage string. Should be set by application. */ #define DEFAULTFREQ 44100 #define BUFFSIZE 50000 /* How many samples to read in one go (preferred) */ #define MINBUFFSIZE 5000 /* How many samples to read in one go (minimum) */ /************************************************* * Types of errors handled by argerrornum() * *************************************************/ typedef enum { ME_NOINT, ME_NODOUBLE, ME_NOTIME, ME_NOVOL, ME_NOSWITCH, ME_TOOMANYFILES, ME_HEADERONTEXTFILE, ME_NOINFILE, ME_NOOUTFILE, ME_NOIOFILE, ME_NOSTDIN, ME_NOSTDOUT, ME_NOSTDIO, ME_NOTENOUGHFILES, ME_THISCANTHAPPEN } Errornum; /* ----------------------------------------------------------------------- Create memory and copy 'string', returning a pointer to the copy. NULL is returned if malloc fails. -----------------------------------------------------------------------*/ extern char *malloccopy( char *string); /* ----------------------------------------------------------------------- Start the stopwatch and make sure the user is informed at end of program. -----------------------------------------------------------------------*/ extern void startstopwatch(void); /* ----------------------------------------------------------------------- Writes the number of samples to result that are yet to be read from anyin. I.e. the number of remaining bytes is divided by the number of bytes per sample value, but not by the number of channels. Return values are TRUE on success, FALSE on failure. -----------------------------------------------------------------------*/ extern int getremainingfilelength( FILE *anyin, long *result); /* ----------------------------------------------------------------------- Read a .pk-header from 'anyin' and printf the entries. -----------------------------------------------------------------------*/ void readpkheader( FILE *anyin); /* ----------------------------------------------------------------------- Read a .WAV header from 'anyin'. If it is recognised, the data is used. Otherwise, we assume it's PCM-data and ignore the header. The global variable 'iswav' is set on success, otherwise cleared. -----------------------------------------------------------------------*/ extern void readwavheader( FILE *anyin); /* ----------------------------------------------------------------------- Write a .WAV header to 'out'. The filepointer is placed at the end of 'out' before operation. This should be called before any data is written, and again, when ALL the data has been written. First time, this positions the file pointer correctly; second time, the missing data can be inserted that wasn't known the first time round. -----------------------------------------------------------------------*/ extern void makewavheader( void); /* -------------------------------------------------------------------- Tests the character 'coal' for being a command line option character, momentarrily '/' or '-'. -------------------------------------------------------------------- */ extern int isoptionchar (char coal); /* ----------------------------------------------------------------------- Reads through the arguments on the lookout for an option starting with 'string'. The rest of the option is read as a time and passed to *result, where the result is meant to mean 'number of samples' in that time. On failure, *result is unchanged. return value is TRUE on success, FALSE otherwise. -----------------------------------------------------------------------*/ extern int parsetimearg( int argcount, char *args[], char *string, int *result); /* ----------------------------------------------------------------------- The string argument is read as a time and passed to *result, where the result is meant to mean 'number of samples' in that time. On failure, *result is unchanged. return value is TRUE on success, FALSE otherwise. -----------------------------------------------------------------------*/ int parsetime(char *string, int *result); /* ----------------------------------------------------------------------- The string argument is read as a frequency and passed to *result, where the result is meant to mean 'number of samples' in one cycle of that frequency. On failure, *result is unchanged. return value is TRUE on success, FALSE otherwise. -----------------------------------------------------------------------*/ int parsefreq(char *string, double *result); /* -------------------------------------------------------------------- Reads through the arguments on the lookout for a switch -'string'. return value is TRUE if one exists, FALSE otherwise. If characters remain after the switch, a fatal error is issued. -------------------------------------------------------------------- */ extern int parseswitcharg( int argcount, char *args[], char *string); /* -------------------------------------------------------------------- Reads through the arguments on the lookout for an option starting with 'string'. The rest of the option is read as an integer and passed to &result. On failure, &result is unchanged. return value is TRUE on success, FALSE otherwise. -------------------------------------------------------------------- */ extern int parseintarg( int argcount, char *args[], char *string, int *result); /* -------------------------------------------------------------------- Reads through the arguments on the lookout for a filename, i.e. anything that does not start with the optionchar. The filename is copied to newly allocated memory, a pointer to which is returned. The argument is marked as used. Therefore repeated use of this function will yield a complete list of filenames on the commandline. If malloc() fails, the function does not return. -------------------------------------------------------------------- */ extern char *parsefilearg( int argcount, char *args[]); /* -------------------------------------------------------------------- Reads through the arguments on the lookout for an option starting with 'string'. The rest of the option is read as a double and passed to *result. On failure, *result is unchanged. return value is TRUE on success, FALSE otherwise. -------------------------------------------------------------------- */ extern int parsedoublearg( int argcount, char *args[], char *string, double *result); /* -------------------------------------------------------------------- Reads through the arguments on the lookout for an option starting with 'string'. The rest of the option is read as a volume, i.e. absolute, percent or db. The result is passed to *result. On failure, *result is unchanged. -------------------------------------------------------------------- */ extern int parsevolarg( int argcount, char *args[], char *string, double *result); /* -------------------------------------------------------------------- Reads the specified string and interprets it as a volume. The string would be of the form 1.8 or 180% or 5db. On success, the return value is the relative volume, i.e. 1.8 On failure, -1 is returned. -------------------------------------------------------------------- */ extern int parsevolume(char *s, double *result); /* -------------------------------------------------------------------- Reads through the arguments on the lookout for a switch -'string'. return value is TRUE if one exists, FALSE otherwise. If characters remain after the switch, a fatal error is issued. -------------------------------------------------------------------- */ extern int parseswitch( char *found, char *wanted); /* -------------------------------------------------------------------- Reports an error due to parsing the string 's' encountered on the command line. -------------------------------------------------------------------- */ extern void argerror(char *s); /* -------------------------------------------------------------------- Reports an error due to parsing the string 's' encountered on the command line. 'code' indicates the type of error. -------------------------------------------------------------------- */ extern void argerrornum(char *s, Errornum code); /* -------------------------------------------------------------------- Reports an error due to parsing the string 's' encountered on the command line. 'message' explains the type of error. -------------------------------------------------------------------- */ extern void argerrortxt(char *s, char *message); /* -------------------------------------------------------------------- Check for any remaining arguments and complain about their existence. If arguments are found, this function does not return. -------------------------------------------------------------------- */ extern void checknoargs( int argcount, char *args[]); /* -------------------------------------------------------------------- Parses the command line arguments as represented by the function arguments. Sets the global variables 'in', 'out', 'samplefrequency' and 'samplewidth' accordingly. According to 'fileswitch', in and out files are opened or not. See above for an explanation of 'fileswitch'. -------------------------------------------------------------------- */ extern void parseargs( int argcount, char *args[], int fileswitch); /* -------------------------------------------------------------------- Returns the index 'i' of the first argument that IS an option, and which begins with the label 's'. If there is none, -1. We also mark that option as done with, i.e. we cross it out. -------------------------------------------------------------------- */ extern int findoption( int argcount, char *args[], char *s); /* -------------------------------------------------------------------- Finishes off the .WAV header (if any) and exits correctly and formerly. -------------------------------------------------------------------- */ extern int myexit (int value); /* -------------------------------------------------------------------- Reads the stated input file bufferwise, calls the function 'work' with the proper values, and writes the result to the stated output file. Return value: TRUE on success, FALSE otherwise. -------------------------------------------------------------------- */ extern int workloop( FILE *theinfile, FILE *theoutfile, int (*work)( short *buffer, int length) ); /* -------------------------------------------------------------------- Five functions for printing to stderr. Depending on the level of verbose, output may be supressed. fatalerror() is like error() but does not return. fatalperror() is like the standard function perror() but does not return. -------------------------------------------------------------------- */ extern int chat( const char *format, ...); extern int inform( const char *format, ...); extern int error( const char *format, ...); extern void fatalerror( const char *format, ...); extern void fatalperror( const char *string); /* -------------------------------------------------------------------- And one functions for printing to stdout. -------------------------------------------------------------------- */ extern int say( const char *format, ...); /* -------------------------------------------------------------------- Allocate memory for it and return a pointer to a string made up of the two argument strings. -------------------------------------------------------------------- */ extern char *mallocconcat( char *one, char *two); /* -------------------------------------------------------------------- Convert a sample value to decibel. -------------------------------------------------------------------- */ extern double double2db( double value); /* -------------------------------------------------------------------- Read 'size' samples from file 'in' and lose them. -------------------------------------------------------------------- */ extern void readawaysamples( FILE *in, size_t size); #endif time_t stopwatch; /* will hold time at start of calculation */ int samplefrequency; unsigned short samplewidth; unsigned short channels; int wavout; /* TRUE iff out file should be a .WAV file */ int iswav; /* TRUE iff in file was found to be a .WAV file */ FILE *in, *out; char *infilename, *outfilename; int verboselevel; char *version = ""; char *usage = ""; static int test_usage; static char *standardversion = "frame version 1.3, June 13th 2001"; static char *standardusage = "\nOptions common to all mark-dsp programs:\n" "-h \t\t create a WAV-header on output files.\n" "-c#\t\t set number of channels to # (1 or 2). Default: like input.\n" "-w#\t\t set number of bits per sample (width) to # (only 16)\n" "-f#\t\t set sample frequency to #. Default: like input.\n" "-V \t\t verbose: talk a lot.\n" "-Q \t\t quiet: talk as little as possible.\n\n" "In most cases, a filename of '-' means stdin or stdout.\n\n" "Bug-reports: mark@manumark.de\n" ; /* ----------------------------------------------------------------------- Writes the number of samples to result that are yet to be read from anyin. Return values are TRUE on success, FALSE on failure. -----------------------------------------------------------------------*/ int getremainingfilelength( FILE *anyin, long *result) { long i; i = ftell (anyin); if (i == -1) return FALSE; if (fseek (anyin, 0, SEEK_END) == -1) return FALSE; *result = ftell (anyin); if (*result == -1) return FALSE; (*result) -= i; (*result) /= samplewidth; if (fseek (anyin, i, SEEK_SET) == -1) return FALSE; return TRUE; } /* ----------------------------------------------------------------------- Read a .pk-header from 'anyin'. -----------------------------------------------------------------------*/ void readpkheader( FILE *anyin) { unsigned short tempushort; int tempint, i, x; unsigned char blood[8]; for (i = 0; i < 11; i++) { fread( &tempint, 4, 1, anyin); printf( "%d: %d, ", i, tempint); } printf( "\n"); fread( blood, 1, 8, anyin); for (i = 0; i < 8; i++) printf( "%d ", blood[i]); printf( "\n"); for (i = 0; i < 8; i++) { for (x = 128; x > 0; x /= 2) printf((blood[i] & x) == 0? "0 ":"1 "); printf(i%4==3? "\n":"| "); } printf( "\n"); for (i = 0; i < 2; i++) { fread( &tempint, 4, 1, anyin); printf( "%d: %d, ", i, tempint); } printf( "\n"); for (i = 0; i < 2; i++) { fread( &tempushort, 2, 1, anyin); printf( "%d: %d, ", i, tempushort); } printf( "\n"); } /* ----------------------------------------------------------------------- Read a .WAV header from 'anyin'. See header for details. -----------------------------------------------------------------------*/ void readwavheader( FILE *anyin) { unsigned int tempuint, sf; unsigned short tempushort, cn; char str[9]; int nowav = FALSE; iswav = FALSE; if (ftell(anyin) == -1) /* If we cannot seek this file */ { nowav = TRUE; /* -> Pretend this is no wav-file */ chat("File not seekable: not checking for WAV-header.\n"); } else { /* Expect four bytes "RIFF" and four bytes filelength */ fread (str, 1, 8, anyin); /* 0 */ str[4] = '\0'; if (strcmp(str, "RIFF") != 0) nowav = TRUE; /* Expect eight bytes "WAVEfmt " */ fread (str, 1, 8, anyin); /* 8 */ str[8] = '\0'; if (strcmp(str, "WAVEfmt ") != 0) nowav = TRUE; /* Expect length of fmt data, which should be 16 */ fread (&tempuint, 4, 1, anyin); /* 16 */ if (tempuint != 16) nowav = TRUE; /* Expect format tag, which should be 1 for pcm */ fread (&tempushort, 2, 1, anyin); /* 20 */ if (tempushort != 1) nowav = TRUE; /* Expect number of channels */ fread (&cn, 2, 1, anyin); /* 20 */ if (cn != 1 && cn != 2) nowav = TRUE; /* Read samplefrequency */ fread (&sf, 4, 1, anyin); /* 24 */ /* Read bytes per second: Should be samplefreq * channels * 2 */ fread (&tempuint, 4, 1, anyin); /* 28 */ if (tempuint != sf * cn * 2) nowav = TRUE; /* read bytes per frame: Should be channels * 2 */ fread (&tempushort, 2, 1, anyin); /* 32 */ if (tempushort != cn * 2) nowav = TRUE; /* Read bits per sample: Should be 16 */ fread (&tempushort, 2, 1, anyin); /* 34 */ if (tempushort != 16) nowav = TRUE; fread (str, 4, 1, anyin); /* 36 */ str[4] = '\0'; if (strcmp(str, "data") != 0) nowav = TRUE; fread (&tempuint, 4, 1, anyin); /* 40 */ if (nowav) { fseek (anyin, 0, SEEK_SET); /* Back to beginning of file */ chat("File has no WAV header.\n"); } else { samplefrequency = sf; channels = cn; chat ("Read WAV header: %d channels, samplefrequency %d.\n", channels, samplefrequency); iswav = TRUE; } } return; } /* ----------------------------------------------------------------------- Write a .WAV header to 'out'. See header for details. -----------------------------------------------------------------------*/ void makewavheader( void) { unsigned int tempuint, filelength; unsigned short tempushort; /* If fseek fails, don't create the header. */ if (fseek (out, 0, SEEK_END) != -1) { filelength = ftell (out); chat ("filelength %d, ", filelength); fseek (out, 0, SEEK_SET); fwrite ("RIFF", 1, 4, out); /* 0 */ tempuint = filelength - 8; fwrite (&tempuint, 4, 1, out); /* 4 */ fwrite ("WAVEfmt ", 1, 8, out); /* 8 */ /* length of fmt data 16 bytes */ tempuint = 16; fwrite (&tempuint, 4, 1, out); /* 16 */ /* Format tag: 1 for pcm */ tempushort = 1; fwrite (&tempushort, 2, 1, out); /* 20 */ chat ("%d channels\n", channels); fwrite (&channels, 2, 1, out); chat ("samplefrequency %d\n", samplefrequency); fwrite (&samplefrequency, 4, 1, out); /* 24 */ /* Bytes per second */ tempuint = channels * samplefrequency * 2; fwrite (&tempuint, 4, 1, out); /* 28 */ /* Block align */ tempushort = 2 * channels; fwrite (&tempushort, 2, 1, out); /* 32 */ /* Bits per sample */ tempushort = 16; fwrite (&tempushort, 2, 1, out); /* 34 */ fwrite ("data", 4, 1, out); /* 36 */ tempuint = filelength - 44; fwrite (&tempuint, 4, 1, out); /* 40 */ } return; } /* ----------------------------------------------------------------------- After all is read and done, inform the inclined user of the elapsed time -----------------------------------------------------------------------*/ static void statistics( void) { int temp; temp = time(NULL) - stopwatch; if (temp != 1) { inform ("\nTime: %d seconds\n", temp); } else { inform ("\nTime: 1 second\n"); } return; } /* ----------------------------------------------------------------------- Start the stopwatch and make sure the user is informed at end of program. -----------------------------------------------------------------------*/ void startstopwatch(void) { stopwatch = time(NULL); /* Remember time 'now' */ atexit(statistics); /* Call function statistics() at exit. */ return; } /* -------------------------------------------------------------------- Tests the character 'coal' for being a command line option character, momentarrily '-'. -------------------------------------------------------------------- */ int isoptionchar (char coal) { return (coal =='-'); } /* ----------------------------------------------------------------------- Reads through the arguments on the lookout for an option starting with 'string'. The rest of the option is read as a time and passed to *result, where the result is meant to mean 'number of samples' in that time. On failure, *result is unchanged. return value is TRUE on success, FALSE otherwise. -----------------------------------------------------------------------*/ int parsetimearg( int argcount, char *args[], char *string, int *result) { int i; if ((i = findoption( argcount, args, string)) > 0) { if (parsetime(args[i] + 1 + strlen( string), result)) return TRUE; argerrornum(args[i]+1, ME_NOTIME); } return FALSE; } /* ----------------------------------------------------------------------- The string argument is read as a time and passed to *result, where the result is meant to mean 'number of samples' in that time. On failure, *result is unchanged. return value is TRUE on success, FALSE otherwise. -----------------------------------------------------------------------*/ int parsetime(char *string, int *result) { int k; double temp; char m, s, end; k = sscanf(string, "%lf%c%c%c", &temp, &m, &s, &end); switch (k) { case 0: case EOF: case 4: return FALSE; case 1: *result = temp; break; case 2: if (m == 's') *result = temp * samplefrequency; else return FALSE; break; case 3: if (m == 'm' && s == 's') *result = temp * samplefrequency / 1000; else if (m == 'H' && s == 'z') *result = samplefrequency / temp; else return FALSE; break; default: argerrornum(NULL, ME_THISCANTHAPPEN); } return TRUE; } /* ----------------------------------------------------------------------- The string argument is read as a frequency and passed to *result, where the result is meant to mean 'number of samples' in one cycle of that frequency. On failure, *result is unchanged. return value is TRUE on success, FALSE otherwise. -----------------------------------------------------------------------*/ int parsefreq(char *string, double *result) { int k; double temp; char m, s, end; k = sscanf(string, "%lf%c%c%c", &temp, &m, &s, &end); switch (k) { case 0: case EOF: case 2: case 4: return FALSE; case 1: *result = temp; break; case 3: if (m == 'H' && s == 'z') *result = samplefrequency / temp; else return FALSE; break; default: argerrornum(NULL, ME_THISCANTHAPPEN); } return TRUE; } char *parsefilearg( int argcount, char *args[]) { int i; char *result = NULL; for (i = 1; i < argcount; i++) { if (args[i][0] != '\0' && (!isoptionchar (args[i][0]) || args[i][1] == '\0' )) { /*---------------------------------------------* * The argument is a filename: * * it is either no dash followed by something, * * or it is a dash following by nothing. * *---------------------------------------------*/ result = malloc( strlen( args[i]) + 1); if (result == NULL) fatalperror( "Couldn't allocate memory for filename\n"); strcpy( result, args[i]); args[i][0] = '\0'; /* Mark as used up */ break; } } return result; } int parseswitch( char *found, char *wanted) { if (strncmp( found, wanted, strlen( wanted)) == 0) { if (found[strlen( wanted)] == '\0') return TRUE; else argerrornum( found, ME_NOSWITCH); } return FALSE; } int parseswitcharg( int argcount, char *args[], char *string) { int i; if ((i = findoption( argcount, args, string)) > 0) { if (args[i][strlen( string) + 1] == '\0') return TRUE; else argerrornum( args[i] + 1, ME_NOSWITCH); } return FALSE; } int parseintarg( int argcount, char *args[], char *string, int *result) { int i, temp; char c; if ((i = findoption( argcount, args, string)) > 0) { switch (sscanf(args[i] + 1 + strlen( string), "%d%c", &temp, &c)) { case 0: case EOF: case 2: argerrornum(args[i]+1, ME_NOINT); return FALSE; case 1: *result = temp; break; default: say("frame.c: This can't happen\n"); } return TRUE; } else { return FALSE; } } /* -------------------------------------------------------------------- Reads through the arguments on the lookout for an option starting with 'string'. The rest of the option is read as a double and passed to *result. On failure, *result is unchanged. return value is TRUE on success, FALSE otherwise. -------------------------------------------------------------------- */ int parsedoublearg( int argcount, char *args[], char *string, double *result) { int i; double temp; char end; if ((i = findoption( argcount, args, string)) > 0) { switch (sscanf(args[i] + 1 + strlen( string), "%lf%c", &temp, &end)) { case 0: case EOF: case 2: argerrornum(args[i]+1, ME_NODOUBLE); return FALSE; case 1: *result = temp; break; default: say("frame.c: This can't happen\n"); } return TRUE; } else { return FALSE; } } /* -------------------------------------------------------------------- Reads through the arguments on the lookout for an option starting with 'string'. The rest of the option is read as a volume, i.e. absolute, percent or db. The result is passed to *result. On failure, *result is unchanged. return value is TRUE on success, FALSE otherwise. -------------------------------------------------------------------- */ int parsevolarg( int argcount, char *args[], char *string, double *result) { double vol = 1.0; char sbd, sbb, end; int i, weird = FALSE; if ((i = findoption( argcount, args, string)) > 0) { switch (sscanf(args[i] + 1 + strlen( string), "%lf%c%c%c", &vol, &sbd, &sbb, &end)) { case 0: case EOF: case 4: weird = TRUE; break; /* No number: error */ case 1: *result = vol; break; case 2: if (sbd == '%') *result = vol / 100; else weird = TRUE; /* One char but no percent: error */ break; case 3: if (sbd =='d' && sbb == 'b') *result = pow(2, vol / 6.02); else weird = TRUE; /* Two chars but not db: error */ break; default: say("frame.c: This can't happen.\n"); } if (weird) argerrornum( args[i] + 1, ME_NOVOL); /* ("Weird option: couldn't parse volume '%s'\n", args[i]+2); */ return !weird; } else { return FALSE; } } /* -------------------------------------------------------------------- Reads the specified string 's' and interprets it as a volume. The string would be of the form 1.8 or 180% or 5db. On success, the return value TRUE and *result is given result (i.e. the relative volume, i.e. 1.8). On failure, FALSE is returned and result is given value 1.0. -------------------------------------------------------------------- */ int parsevolume(char *s, double *result) { int k; char sbd, sbb, end; *result = 1.0; k = sscanf(s, "%lf%c%c%c", result, &sbd, &sbb, &end); switch (k) { case 0: case EOF: case 4: return FALSE; case 1: break; case 2: if (sbd != '%') return FALSE; (*result) /=100; break; case 3: if (sbd !='d' || sbb != 'b') return FALSE; (*result) = pow(2, (*result) / 6.02); break; default: say("parsevolume: This can't happen (%d).\n", k); } return TRUE; } /* -------------------------------------------------------------------- Reports an error due to parsing the string 's' encountered on the command line. -------------------------------------------------------------------- */ void argerror(char *s) { error ("Error parsing command line. Unrecognized option:\n\t-%s\n", s); fatalerror("\nTry --help for help.\n"); } /* -------------------------------------------------------------------- Reports an error due to parsing the string 's' encountered on the command line. 'code' indicates the type of error. -------------------------------------------------------------------- */ void argerrornum(char *s, Errornum code) { char *message; if (code == ME_TOOMANYFILES) { error("Too many files on command line: '%s'.\n", s); } else { if (s != NULL) error ("Error parsing option -%s:\n\t", s); switch( code) { case ME_NOINT: message = "Integer expected"; break; case ME_NODOUBLE: message = "Floating point number expected"; break; case ME_NOTIME: message = "Time argument expected"; break; case ME_NOVOL: message = "Volume argument expected"; break; case ME_NOSWITCH: message = "Garbage after switch-type option"; break; case ME_HEADERONTEXTFILE: message = "Option -h is not useful for text-output"; break; case ME_NOINFILE: message = "No input file specified"; break; case ME_NOOUTFILE: message = "No output file specified"; break; case ME_NOIOFILE: message = "No input/output file specified"; break; case ME_NOSTDIN: message = "Standard in not supported here"; break; case ME_NOSTDOUT: message = "Standard out not supported here"; break; case ME_NOSTDIO: message = "Standard in/out not supported here"; break; case ME_NOTENOUGHFILES: message = "Not enough files specified"; break; case ME_THISCANTHAPPEN: fatalerror("\nThis can't happen. Report this as a bug\n"); /* fatalerror does not return */ default: error("Error code %d not implemented. Fix me!\n", code); message = "Error message not implemented. Fix me!"; } error("%s\n", message); } fatalerror("\nTry --help for help.\n"); } /* -------------------------------------------------------------------- Reports an error due to parsing the string 's' encountered on the command line. 'message' explains the type of error. -------------------------------------------------------------------- */ void argerrortxt(char *s, char *message) { if (s != NULL) error ("Error parsing option -%s:\n\t", s); else error ("Error parsing command line:\n\t"); error ("%s\n", message); fatalerror("\nTry --help for help.\n"); } /* -------------------------------------------------------------------- Check for any remaining arguments and complain about their existence -------------------------------------------------------------------- */ void checknoargs( int argcount, char *args[]) { int i, errorcount = 0; for (i = 1; i < argcount; i++) { if (args[i][0] != '\0') /* An unused argument! */ { errorcount++; if (errorcount == 1) error("The following arguments were not recognized:\n"); error("\t%s\n", args[i]); } } if (errorcount > 0) /* Errors are fatal */ fatalerror("\nTry --help for help.\n"); return; /* No errors? Return. */ } /* -------------------------------------------------------------------- Parses the command line arguments as represented by the function arguments. Sets the global variables 'in', 'out', 'samplefrequency' and 'samplewidth' accordingly. Also verboselevel. The files 'in' and 'out' are even opened according to 'fileswitch'. See headerfile for details -------------------------------------------------------------------- */ void parseargs( int argcount, char *args[], int fileswitch) { char *filename; int tempint; if ((fileswitch & 1) != 0) /* If getting infile */ in = NULL; if ((fileswitch & 4) != 0) /* If getting outfile */ out = NULL; wavout = FALSE; verboselevel = 5; samplefrequency = DEFAULTFREQ; samplewidth = 2; channels = 1; /*-----------------------------------------------* * First first check testcase, usage and version * *-----------------------------------------------*/ test_usage = parseswitcharg( argcount, args, "-test-usage"); if (parseswitcharg( argcount, args, "-help")) { printf("%s%s", usage, standardusage); exit(0); } if (parseswitcharg( argcount, args, "-version")) { printf("%s\n(%s)\n", version, standardversion); exit(0); } /*--------------------------------------* * Set verboselevel * *--------------------------------------*/ while (parseswitcharg( argcount, args, "V")) verboselevel = 10; while (parseswitcharg( argcount, args, "Q")) verboselevel = 1; /*-------------------------------------------------* * Get filenames and open files * *-------------------------------------------------*/ if ((fileswitch & 1) != 0) /* Infile wanted */ { infilename = parsefilearg( argcount, args); if (infilename == NULL) argerrornum( NULL, ME_NOINFILE); if (strcmp( infilename, "-") == 0) { infilename = ""; in = stdin; if ((fileswitch & 2) != 0) /* Binfile wanted */ readwavheader( in); } else { if ((fileswitch & 2) == 0) /* Textfile wanted */ in = fopen(infilename, "rt"); else /* Binfile wanted */ if ((in = fopen(infilename, "rb")) != NULL) readwavheader( in); } if (in == NULL) fatalerror("Error opening input file '%s': %s\n", infilename,strerror(errno)); else inform("Using file '%s' as input\n", infilename); } if ((fileswitch & 4) != 0) /* Outfile wanted */ { outfilename = parsefilearg( argcount, args); if (outfilename == NULL) argerrornum( NULL, ME_NOOUTFILE); if (strcmp( outfilename, "-") == 0) { outfilename = ""; out = stdout; } else { if ((fileswitch & 8) == 0) /* Textfile wanted */ out = fopen(outfilename, "wt"); else /* Binfile wanted */ out = fopen(outfilename, "wb"); } if (out == NULL) fatalerror("Error opening output file '%s': %s\n", outfilename,strerror(errno)); else inform("Using file '%s' as output\n", outfilename); } if ((fileswitch & 32) != 0) /* In-/Outfile wanted */ { assert (in == NULL && out == NULL); infilename = outfilename = parsefilearg( argcount, args); if (outfilename == NULL) argerrornum( NULL, ME_NOIOFILE); if (strcmp( infilename, "-") == 0) argerrornum( infilename, ME_NOSTDIN); inform("Using file '%s' as input/output\n", outfilename); in = out = fopen(outfilename, "r+"); if (out == NULL) fatalerror("Error opening input/output file '%s': %s\n", outfilename,strerror(errno)); readwavheader( in); } if ((fileswitch & 16) == 0) /* No additional files wanted */ { if ((filename = parsefilearg( argcount, args)) != NULL) argerrornum( filename, ME_TOOMANYFILES); } /*-------------------------------------------------* * Set samplefrequency, width, wavout, *-------------------------------------------------*/ parseintarg( argcount, args, "f", &samplefrequency); wavout = parseswitcharg( argcount, args, "h"); if (parseintarg( argcount, args, "w", &tempint)) { if (tempint != 16) argerrortxt(NULL, "Option -w is only valid " "with value 16. Sorry."); else samplewidth = tempint; } if (parseintarg( argcount, args, "c", &tempint)) { if (tempint != 1 && tempint != 2) argerrortxt(NULL, "Option -c is only valid " "with values 1 or 2. Sorry."); else channels = tempint; } /*-------------------------------------------------* * Create WAV-header on output if wanted. * *-------------------------------------------------*/ if (wavout) switch (fileswitch & (12)) { case 4: /* User wants header on textfile */ argerrornum( NULL, ME_HEADERONTEXTFILE); case 12: /* User wants header on binfile */ makewavheader(); break; case 0: /* User wants header, but there is no outfile */ /* Problem: what about i/o-file, 32? You might want a header on that? Better ignore this case. */ break; case 8: /* An application musn't ask for this */ default: /* This can't happen */ assert( FALSE); } return; } /* -------------------------------------------------------------------- Returns the index 'i' of the first argument that IS an option, and which begins with the label 's'. If there is none, -1. We also mark that option as done with, i.e. we cross it out. -------------------------------------------------------------------- */ int findoption( int argcount, char *args[], char *s) { int i; if (test_usage) printf("Checking for option -%s\n", s); for (i=1; i 5) { va_start( ap, format); result = vfprintf( stderr, format, ap); va_end( ap); } return result; } int inform( const char *format, ...) { va_list ap; int result = 0; if (verboselevel > 1) { va_start( ap, format); result = vfprintf( stderr, format, ap); va_end( ap); } return result; } int error( const char *format, ...) { va_list ap; int result; va_start( ap, format); result = vfprintf( stderr, format, ap); va_end( ap); return result; } void fatalerror( const char *format, ...) { va_list ap; va_start( ap, format); vfprintf( stderr, format, ap); va_end( ap); myexit(1); } void fatalperror( const char *string) { perror( string); myexit( 1); } int say( const char *format, ...) { va_list ap; int result; va_start( ap, format); result = vfprintf( stdout, format, ap); va_end( ap); return result; } char *malloccopy( char *string) { char *result; result = malloc( strlen( string) + 1); if (result != NULL) strcpy( result, string); return result; } char *mallocconcat( char *one, char *two) { char *result; result = malloc( strlen( one) + strlen( two) + 1); if (result != NULL) { strcpy( result, one); strcat( result, two); } return result; } double double2db( double value) { if (value < 0) value = -value; return 6.0 * log( value / 32767) / log( 2); } void readawaysamples( FILE *in, size_t size) { short *buffer; int samplesread, count; buffer = malloc( sizeof( *buffer) * BUFFSIZE); if (buffer == NULL) fatalperror("Couldn't allocate buffer"); while (size > 0) { if (size > BUFFSIZE) count = BUFFSIZE; else count = size; samplesread = fread( buffer, sizeof(*buffer), count, in); if (ferror( in) != 0) fatalperror("Error reading input file"); size -= samplesread; } free( buffer); } /**************************************************************************** * * Programs for processing sound files in raw- or WAV-format. * -- Merge two mono WAV-files to one stereo WAV-file. * * Name: stereorize.c * Version: 1.1 * Author: Mark Roberts * Michael Labuschke * ****************************************************************************/ #include #include #include #include #include #include static char *Version = "stereorize 1.1, November 5th 2000"; static char *Usage = "Usage: stereorize [options] infile-left infile-right outfile\n\n" "Example:\n" " stereorize left.wav right.wav stereo.wav -h\n\n" "Creates stereo.wav (with WAV-header, option -h) from data in mono files\n" "left.wav and right.wav.\n" ; int main( int argcount, char *args[]) { int i, k[2], maxk, stdin_in_use=FALSE; short *leftsample, *rightsample, *stereosample; FILE *channel[2]; char *filename[2], *tempname; version = Version; usage = Usage; channel[0] = NULL; channel[1] = NULL; parseargs( argcount, args, NOFILES | NOCOMPLAIN); for (i = 0; i < 2; i++) { filename[i] = parsefilearg( argcount, args); if (filename[i] == NULL) argerrornum( NULL, ME_NOTENOUGHFILES); if (strcmp (filename[i], "-") == 0) { if (stdin_in_use) argerrortxt( filename[i] + 1, "Cannot use for both input files"); filename[i] = ""; channel[i] = stdin; stdin_in_use = TRUE; } else { channel[i] = fopen(filename[i], "rb"); } if (channel[i] == NULL) fatalerror( "Error opening input file '%s': %s\n", filename[i],strerror(errno)); else inform("Using file '%s' as input\n", filename[i]); } for (i = 0; i < 2; i++) { assert ( channel[i] != NULL); readwavheader( channel[i]); if (iswav && channels != 1) inform("Warning: '%s' is no mono file\n", filename[i]); } outfilename = parsefilearg( argcount, args); if (outfilename == NULL) argerrornum( NULL, ME_NOOUTFILE); if (strcmp (outfilename, "-") == 0) { outfilename = ""; out = stdout; } else { out = fopen(outfilename, "wb"); } if (out == NULL) fatalerror( "Error opening output file '%s': %s\n", outfilename,strerror(errno)); else inform("Using file '%s' as output\n", outfilename); if ((tempname = parsefilearg( argcount, args)) != NULL) argerrornum( tempname, ME_TOOMANYFILES); checknoargs(argcount, args); /* Check that no arguments are left */ leftsample = malloc( sizeof(*leftsample) * BUFFSIZE); rightsample = malloc( sizeof(*leftsample) * BUFFSIZE); stereosample = malloc( sizeof(*leftsample) * 2 * BUFFSIZE); if (leftsample == NULL || rightsample == NULL || stereosample == NULL) fatalperror (""); channels = 2; /* Output files are stereo */ if (wavout) { if ((strcmp(outfilename,"")!=0) && (fseek( out, 0, SEEK_SET) != 0)) fatalerror("Couldn't navigate output file '%s': %s\n",outfilename, strerror(errno)); makewavheader(); } startstopwatch(); while (TRUE) { maxk = 0; for (i = 0; i < 2; i++) { k[i] = fread(i==0? leftsample : rightsample, sizeof(*leftsample), BUFFSIZE, channel[i]); if (k[i] == -1) fatalerror("Error reading file '%s': %s\n", filename[i],strerror(errno)); if (k[i] > maxk) maxk = k[i]; } if (maxk == 0) myexit (0); /*-------------------------------------------------* * First the left channel as far as it goes ... * *-------------------------------------------------*/ for (i = 0; i < k[0]; i++) stereosample[2 * i] = leftsample[i]; /*-------------------------------------------------* * ... and fill up till the end of this buffer. * *-------------------------------------------------*/ for (; i < maxk; i++) stereosample[2 * i] = 0; /*-------------------------------------------------* * Next the right channel as far as it goes ... * *-------------------------------------------------*/ for (i = 0; i < k[1]; i++) stereosample[2 * i + 1] = rightsample[i]; /*-------------------------------------------------* * ... and fill up till the end of this buffer. * *-------------------------------------------------*/ for (; i < maxk; i++) stereosample[2 * i + 1] = 0; fwrite(stereosample, sizeof(*leftsample), 2 * maxk, out); if (ferror( out) != 0) fatalerror("Error writing to file '%s': %s\n", outfilename, strerror(errno)); } /* That was an endless loop. This point is never reached. */ }