From 2184f91230d92f73daef07a3f96dc1d029af3ba0 Mon Sep 17 00:00:00 2001
From: Mark Spencer <markster@digium.com>
Date: Fri, 21 Jan 2005 03:56:22 +0000
Subject: [PATCH] Add stereoize (bug #3142), faster than soxmix

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@4859 65c4cc65-6c06-0410-ace0-fbb531ad65f3
---
 res/res_monitor.c  |    6 +-
 utils/.cvsignore   |    1 +
 utils/Makefile     |    7 +-
 utils/frame.c      | 1034 ++++++++++++++++++++++++++++++++++++++++++++
 utils/frame.h      |  300 +++++++++++++
 utils/stereorize.c |  159 +++++++
 6 files changed, 1501 insertions(+), 6 deletions(-)
 create mode 100755 utils/frame.c
 create mode 100755 utils/frame.h
 create mode 100755 utils/stereorize.c

diff --git a/res/res_monitor.c b/res/res_monitor.c
index 4c93517db2..d3d85f6ad8 100755
--- a/res/res_monitor.c
+++ b/res/res_monitor.c
@@ -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);
 		}
diff --git a/utils/.cvsignore b/utils/.cvsignore
index 19e0c18c19..b050dc58cf 100755
--- a/utils/.cvsignore
+++ b/utils/.cvsignore
@@ -1,3 +1,4 @@
 .depend
 astman
 smsq
+stereoize
diff --git a/utils/Makefile b/utils/Makefile
index 352752eace..dcacce210f 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 0000000000..bd83bc57ed
--- /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 0000000000..6f5dd30434
--- /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 0000000000..7d72cbdbfa
--- /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. */
+}
-- 
GitLab