Add stereoize (bug #3142), faster than soxmix
authorMark Spencer <markster@digium.com>
Fri, 21 Jan 2005 03:56:22 +0000 (03:56 +0000)
committerMark Spencer <markster@digium.com>
Fri, 21 Jan 2005 03:56:22 +0000 (03:56 +0000)
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@4859 65c4cc65-6c06-0410-ace0-fbb531ad65f3

res/res_monitor.c
utils/.cvsignore
utils/Makefile
utils/frame.c [new file with mode: 0755]
utils/frame.h [new file with mode: 0755]
utils/stereorize.c [new file with mode: 0755]

index 4c93517..d3d85f6 100755 (executable)
@@ -241,7 +241,7 @@ int ast_monitor_stop(struct ast_channel *chan, int need_lock)
                        /* Set the execute application */
                        execute = pbx_builtin_getvar_helper(chan, "MONITOR_EXEC");
                        if (!execute || ast_strlen_zero(execute)) { 
-                               execute = "nice -n 19 soxmix"; 
+                               execute = "nice -n 19 soxmix";
                                delfiles = 1;
                        } 
                        execute_args = pbx_builtin_getvar_helper(chan, "MONITOR_EXEC_ARGS");
@@ -251,10 +251,10 @@ int ast_monitor_stop(struct ast_channel *chan, int need_lock)
                        
                        snprintf(tmp, sizeof(tmp), "%s \"%s/%s-in.%s\" \"%s/%s-out.%s\" \"%s/%s.%s\" %s &", execute, dir, name, format, dir, name, format, dir, name, format,execute_args);
                        if (delfiles) {
-                               snprintf(tmp2,sizeof(tmp2), "( %s& rm -f \"%s\"/%s-* ) &",tmp, dir ,name); /* remove legs when done mixing */
+                               snprintf(tmp2,sizeof(tmp2), "( %s& rm -f \"%s/%s-*\" ) &",tmp, dir ,name); /* remove legs when done mixing */
                                strncpy(tmp, tmp2, sizeof(tmp) - 1);
                        }
-                       ast_verbose("monitor executing %s\n",tmp);
+                       ast_log(LOG_DEBUG,"monitor executing %s\n",tmp);
                        if (ast_safe_system(tmp) == -1)
                                ast_log(LOG_WARNING, "Execute of %s failed.\n",tmp);
                }
index 19e0c18..b050dc5 100755 (executable)
@@ -1,3 +1,4 @@
 .depend
 astman
 smsq
+stereoize
index 352752e..dcacce2 100755 (executable)
@@ -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 (executable)
index 0000000..bd83bc5
--- /dev/null
@@ -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 (executable)
index 0000000..6f5dd30
--- /dev/null
@@ -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 (executable)
index 0000000..7d72cbd
--- /dev/null
@@ -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. */
+}