diff options
author | markster <markster@f38db490-d61c-443f-a65b-d21fe96a405b> | 2005-01-21 03:56:22 +0000 |
---|---|---|
committer | markster <markster@f38db490-d61c-443f-a65b-d21fe96a405b> | 2005-01-21 03:56:22 +0000 |
commit | 799f7460d92342318c5a8e617e73cad96a6c081e (patch) | |
tree | bd3a5e293acff59ef674f147c1b5e6cd74a576ad /utils | |
parent | ad916c0c92be96c62e6ea33ca4c8e767307ef5a5 (diff) |
Add stereoize (bug #3142), faster than soxmix
git-svn-id: http://svn.digium.com/svn/asterisk/trunk@4859 f38db490-d61c-443f-a65b-d21fe96a405b
Diffstat (limited to 'utils')
-rwxr-xr-x | utils/.cvsignore | 1 | ||||
-rwxr-xr-x | utils/Makefile | 7 | ||||
-rwxr-xr-x | utils/frame.c | 1034 | ||||
-rwxr-xr-x | utils/frame.h | 300 | ||||
-rwxr-xr-x | utils/stereorize.c | 159 |
5 files changed, 1498 insertions, 3 deletions
diff --git a/utils/.cvsignore b/utils/.cvsignore index 19e0c18c1..b050dc58c 100755 --- a/utils/.cvsignore +++ b/utils/.cvsignore @@ -1,3 +1,4 @@ .depend astman smsq +stereoize diff --git a/utils/Makefile b/utils/Makefile index 352752eac..dcacce210 100755 --- a/utils/Makefile +++ b/utils/Makefile @@ -8,7 +8,7 @@ ifeq ($(findstring BSD,${OSARCH}),BSD) CFLAGS+=-I/usr/local/include -L/usr/local/lib endif -TARGET=none +TARGET=stereorize TARGET+=$(shell if [ -f /usr/include/popt.h ]; then echo "smsq"; else if [ -f /usr/local/include/popt.h ]; then echo "smsq"; fi ; fi) TARGET+=$(shell if [ -f /usr/include/newt.h ]; then echo "astman"; else if [ -f /usr/local/include/newt.h ]; then echo "astman"; fi ; fi) @@ -22,14 +22,15 @@ install: fi; \ done -none: - clean: rm -f *.o astman smsq .depend astman: astman.o ../md5.o $(CC) $(CFLAGS) -o astman astman.o ../md5.o -lnewt +stereorize: stereorize.o frame.o + $(CC) $(CFLAGS) -o stereorize stereorize.o frame.o -lm + smsq: smsq.o $(CC) $(CFLAGS) -o smsq smsq.o -lpopt diff --git a/utils/frame.c b/utils/frame.c new file mode 100755 index 000000000..bd83bc57e --- /dev/null +++ b/utils/frame.c @@ -0,0 +1,1034 @@ +/**************************************************************************** + * + * 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 <mark@manumark.de> + * Michael Labuschke <michael@labuschke.de> sys_errlist fixes + * + ****************************************************************************/ +/**************************************************************************** + * These are useful functions that all DSP programs might find handy + ****************************************************************************/ + +#include <stdio.h> +#include <math.h> +#include <stdlib.h> /* for exit and malloc */ +#include <string.h> +#include <time.h> +#include <stdarg.h> +#include <errno.h> +#include <assert.h> +#include "frame.h" + +time_t stopwatch; /* will hold time at start of calculation */ +unsigned 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 = "<stdin>"; + 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 = "<stdout>"; + 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<argcount; i++) + { + if (isoptionchar (args[i][0]) && + strncmp( args[i] + 1, s, strlen( s)) == 0) + { + args[i][0] = '\0'; + return i; + } + } + return -1; +} + +/* -------------------------------------------------------------------- + Finishes off the .WAV header (if any) and exits correctly and formerly. + -------------------------------------------------------------------- */ +int myexit (int value) +{ + switch (value) + { + case 0: + if (wavout) + makewavheader(); /* Writes a fully informed .WAV header */ + chat ("Success!\n"); + break; + default: + chat ("Failure.\n"); + break; + } + exit (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. + -------------------------------------------------------------------- */ +int workloop( FILE *theinfile, FILE *theoutfile, + int (*work)( short *buffer, int length) ) +{ + short *buffer; + int length, nowlength; + + length = BUFFSIZE; + if ((buffer = malloc( sizeof(short) * length)) == NULL) + fatalperror (""); + while (TRUE) + { + nowlength = fread(buffer, sizeof(short), length, theinfile); + if (ferror( theinfile) != 0) + fatalperror("Error reading input file"); + if (nowlength == 0) /* Reached end of input file */ + break; + /* Call the routine that does the work */ + if (!work (buffer, nowlength)) /* On error, stop. */ + return FALSE; + fwrite(buffer, sizeof(short), nowlength, theoutfile); + if (ferror( theoutfile) != 0) + fatalperror("Error writing to output file"); + } + return TRUE; /* Input file done with, no errors. */ +} + +int chat( const char *format, ...) +{ + va_list ap; + int result = 0; + + if (verboselevel > 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); +} + diff --git a/utils/frame.h b/utils/frame.h new file mode 100755 index 000000000..6f5dd3043 --- /dev/null +++ b/utils/frame.h @@ -0,0 +1,300 @@ +/**************************************************************************** + * + * 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 <mark@manumark.de> + * + ****************************************************************************/ +/**************************************************************************** + * These are useful functions that all DSP programs might find handy + ****************************************************************************/ + +/* 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 unsigned 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); diff --git a/utils/stereorize.c b/utils/stereorize.c new file mode 100755 index 000000000..7d72cbdbf --- /dev/null +++ b/utils/stereorize.c @@ -0,0 +1,159 @@ +/**************************************************************************** + * + * 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 <mark@manumark.de> + * Michael Labuschke <michael@labuschke.de> + * + ****************************************************************************/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <time.h> +#include <assert.h> +#include "frame.h" + +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 <stdin> for both input files"); + filename[i] = "<stdin>"; + 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 = "<stdout>"; + 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,"<stdout>")!=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. */ +} |