shithub: sox

Download patch

ref: 22be79e45c4ddf331c574f53bca3259998ce4f61
parent: bed68352b97e75821d31862d0ff282c0950aed94
author: robs <robs>
date: Sat Dec 16 14:43:49 EST 2006

sox file merging

--- a/AUTHORS
+++ b/AUTHORS
@@ -90,4 +90,5 @@
 	Rob Sykes		robs@users.sourceforge.net
 		Bass & treble effects, FLAC support, initial 24bit
 		support, Octave plotting for filters, new flanger,
-		various small fixes, enchancements and clean-ups.
+                file merging, various small fixes, enchancements
+                and clean-ups.
--- a/ChangeLog
+++ b/ChangeLog
@@ -63,6 +63,7 @@
     conversions.  (robs)
   o Added large file support.  (Reuben Thomas)
   o Higher quality audio speed adjustment.  (robs)
+  o Added ability to merge e.g. 2 mono files to 1 stereo file.  (robs)
 
 sox-12.18.2
 -----------
--- a/TODO
+++ b/TODO
@@ -25,8 +25,6 @@
     really is.  For 2->2 mixes, make a 2 option L->L and R->R
     shortcut.  Similar 4 option for 4->4.
 
-  o Merging, e.g. two mono files to one stereo file.
-
   o In avg effect, going from 1->2 should have a 2 number shortcut
     so that you don't have to put 100% in each channel.  Similar
     for 2->4.
--- a/sox.1
+++ b/sox.1
@@ -202,6 +202,17 @@
 Print usage information on the specified effect.  The name
 \fBall\fR can be used to disable usage on all effects.
 .TP 10
+\fB\-m\fR, \fB\-\-mix\fR
+Behave as \fBsoxmix\fR, i.e. mix multiple input files instead of
+concatenating them.
+.TP 10
+\fB\-M\fR, \fB\-\-merge\fR
+Merge multiple input files instead of concatenating them.
+Input files must have the same data format characteristics.
+This can be used for example to merge two mono files into one
+stereo file; the first and second mono files become
+the left and right channels of the stereo file.
+.TP 10
 \fB-o\fR
 Run in a mode that can be used, in conjunction with the GNU
 Octave program, to assist with the selection and configuration
@@ -215,7 +226,7 @@
 	octave plot.m
 .TP 10
 \fB-q\fR
-Run in quiet mode when SoX wouldn't otherwise do that.  Inverse of \fB-S\fR
+Run in quiet mode when SoX wouldn't otherwise do so.  Inverse of \fB-S\fR
 option.
 .TP
 \fB-S\fR
--- a/src/sox.c
+++ b/src/sox.c
@@ -71,8 +71,7 @@
  * Rewrite for multiple effects: Aug 24, 1994.
  */
 
-static int soxmix = 0;          /* non-zero if running as soxmix */
- 
+static enum {SOX_CONCAT, SOX_MIX, SOX_MERGE} mode = SOX_CONCAT;
 static int clipped = 0;         /* Volume change clipping errors */
 static int writing = 1;         /* are we writing to a file? assume yes. */
 static st_globalinfo_t globalinfo = {false, 1};
@@ -184,8 +183,9 @@
     myname = argv[0];
 
     i = strlen(myname);
-    if (i >= sizeof("soxmix") - 1)
-      soxmix = strcmp(myname + i - (sizeof("soxmix") - 1), "soxmix") == 0;
+    if (i >= sizeof("soxmix") - 1 &&
+        strcmp(myname + i - (sizeof("soxmix") - 1), "soxmix") == 0)
+      mode = SOX_MIX;
 
     st_output_message_handler = sox_output_message;
 
@@ -225,12 +225,12 @@
 
     /* Make sure we got at least the required # of input filenames */
     input_count = file_count ? file_count - 1 : 0;
-    if (input_count < (soxmix ? 2 : 1))
+    if (input_count < (mode == SOX_CONCAT ? 1 : 2))
         usage("Not enough input filenames specified");
 
     for (i = 0; i < input_count; i++)
     {
-      if (soxmix) {
+      if (mode == SOX_MIX) {
         /* When mixing audio, default to input side volume
          * adjustments that will make sure no clipping will
          * occur.  Users most likely won't be happy with
@@ -333,7 +333,7 @@
   return result;
 }
 
-static char *getoptstr = "+r:v:t:c:C:phsuUAaig1b2w34lf8dxV::Sqoen";
+static char *getoptstr = "+r:v:t:c:C:phsuUAaig1b2w34lf8dxV::SqoenmM";
 
 static struct option long_options[] =
 {
@@ -342,7 +342,9 @@
     {"comment", required_argument, NULL, 0},
     {"comment-file", required_argument, NULL, 0},
 
-    {"help", 0, NULL, 'h'},
+    {"help", no_argument, NULL, 'h'},
+    {"mix", no_argument, NULL, 'm'},
+    {"merge", no_argument, NULL, 'M'},
     {NULL, 0, NULL, 0}
 };
 
@@ -379,6 +381,14 @@
         }
         break;
 
+      case 'm':
+         mode = SOX_MIX;
+         break;
+
+      case 'M':
+         mode = SOX_MERGE;
+         break;
+
       case 'e': case 'n':
         return true;            /* Is null file. */
 
@@ -526,9 +536,11 @@
                       file_desc[f]->filename, file_desc[f]->comment);
     }
 
-    for (f = 1; f < input_count; f++)
+    for (f = 0; f < input_count; f++)
     {
-        if (compare_input(file_desc[0], file_desc[f]) != ST_SUCCESS)
+        if (mode == SOX_MERGE)
+          file_desc[f]->info.channels *= input_count;
+        if (f && compare_input(file_desc[0], file_desc[f]) != ST_SUCCESS)
         {
             st_fail("Input files must have the same rate, channels, data size, and encoding");
             exit(1);
@@ -618,7 +630,7 @@
                       file_desc[file_count-1]->comment);
     }
 
-    /* Set the input rate for the speed effect */
+    /* Adjust the input rate for the speed effect */
     for (f = 0; f < input_count; ++f)
       file_desc[f]->info.rate = file_desc[f]->info.rate * globalinfo.speed + .5;
 
@@ -631,14 +643,19 @@
     /* Reserve an output buffer for all effects */
     reserve_effect_buf();
 
-    if (soxmix) {
+    if (mode != SOX_CONCAT) {
       for (f = 0; f < input_count; f++)
         {
+          st_size_t alloc_size = ST_BUFSIZ * sizeof(st_sample_t);
           /* Treat overall length the same as longest input file. */
           if (file_desc[f]->length > input_samples)
             input_samples = file_desc[f]->length;
           
-          ibuf[f] = (st_sample_t *)xmalloc(ST_BUFSIZ * sizeof(st_sample_t));
+          if (mode == SOX_MERGE) {
+            alloc_size /= input_count;
+            file_desc[f]->info.channels /= input_count;
+          }
+          ibuf[f] = (st_sample_t *)xmalloc(alloc_size);
           
           if (status)
             print_input_status(f);
@@ -675,7 +692,7 @@
        * (or ST_EOF).
        */
       do {
-        if (!soxmix) {
+        if (mode == SOX_CONCAT) {
           ilen[0] = st_read(file_desc[current_input], efftab[0].obuf, 
                          (st_ssize_t)ST_BUFSIZ);
           if (ilen[0] > ST_BUFSIZ)
@@ -723,7 +740,7 @@
             clipped += volumechange(efftab[0].obuf, 
                                     efftab[0].olen,
                                     file_opts[current_input]->volume);
-        } else /* soxmix */ {
+        } else if (mode == SOX_MIX) {
           for (f = 0; f < input_count; f++)
             {
               ilen[f] = st_read(file_desc[f], ibuf[f], (st_ssize_t)ST_BUFSIZ);
@@ -783,7 +800,28 @@
                       }
                 }
             }
+        } else {                 /* mode == SOX_MERGE */
+          efftab[0].olen = 0;
+          for (f = 0; f < input_count; ++f) {
+            ilen[f] = st_read(file_desc[f], ibuf[f], ST_BUFSIZ / input_count);
+            if (ilen[f] == ST_EOF) {
+              ilen[f] = 0;
+              if (file_desc[f]->st_errno)
+                fprintf(stderr, file_desc[f]->st_errstr);
+            }
+            if ((st_size_t)ilen[f] > efftab[0].olen)
+              efftab[0].olen = ilen[f];
+          }
+          
+          for (s = 0; s < efftab[0].olen; s++)
+            for (f = 0; f < input_count; f++)
+              efftab[0].obuf[s * input_count + f] =
+                (s < (st_size_t)ilen[f]) * ibuf[f][s];
+
+          read_samples += efftab[0].olen;
+          efftab[0].olen *= input_count;
         }
+
         efftab[0].odone = 0;
 
         /* If not writing and no effects are occuring then not much
@@ -823,7 +861,7 @@
       fputs("\n\n", stderr);
     }
 
-    if (soxmix)
+    if (mode != SOX_CONCAT)
       /* Free input buffers now that they are not used */
       for (f = 0; f < input_count; f++)
         {
@@ -1608,7 +1646,7 @@
     double out_size;
     char unit;
 
-    /* Currently, for both sox and soxmix, all input files must have
+    /* Currently, for all sox modes, all input files must have
      * the same sample rate.  So we can always just use the rate
      * of the first input file to compute time.
      */
@@ -1694,7 +1732,7 @@
     const st_effect_t *e;
     static char *usagestr;
 
-    if (soxmix)
+    if (mode != SOX_CONCAT)
       usagestr = "[ gopts ] [ fopts ] ifile1 [fopts] ifile2 [ fopts ] ofile [ effect [ effopts ] ]";
     else
       usagestr = "[ gopts ] [ fopts ] ifile [ fopts ] ofile [ effect [ effopts ] ]";
@@ -1708,7 +1746,7 @@
 "Special filenames (ifile, ofile):\n"
 "\n"
 "-               use stdin or stdout\n"
-"-n/-e           use the null file handler; for use with e.g. synth & stat.\n"
+"-n, -e          use the null file handler; for use with e.g. synth & stat.\n"
 "\n"
 "Global options (gopts):\n"
 "\n"
@@ -1718,6 +1756,9 @@
 "--help          same as -h\n"
 "--help-effect=name\n"
 "                print usage of specified effect.  use 'all' to print all\n"
+"-m, --mix       mix multiple input files (instead of concatenating)\n"
+"-M, --merge     merge multiple input files (instead of concatenating)\n"
+"-o              generate Octave commands to plot response of filter effect\n"
 "-q              run in quiet mode.  Inverse of -S option\n"
 "-S              print status while processing audio data.\n"
 "--version       print version number of SoX and exit\n"