shithub: sox

Download patch

ref: 77c687fbcaf09a7f434bf7de7b617ff7fa25f4eb
parent: f8cde2c8f5430871ab298912da46e5da14215f4c
author: robs <robs>
date: Sat Apr 11 16:36:11 EDT 2009

make changing channels consistent with changing rate

--- a/FEATURES.in
+++ b/FEATURES.in
@@ -92,6 +92,7 @@
 ** trim: Trim the ends of the audio
 
 * Mixing effects
+** channels: Auto mix or duplicate to change number of channels
 ** mixer: Mix up to 4 channels in certain ways
 ** remix: Produce arbitrarily mixed output channels
 ** swap: Swap stereo channels
--- a/sox.1
+++ b/sox.1
@@ -958,23 +958,26 @@
 .TP
 \fB\-c\fR \fICHANNELS\fR, \fB\-\-channels\fR \fICHANNELS\fR
 The number of audio channels in the audio file; this can be any number
-greater than zero.  To cause the output file to have a different number of
-channels than the input file, include this option with the output file
-options.  If the input and output file have a different number of channels
-then the
-.B mixer
-effect must be used.  If the
-.B mixer
-effect is not specified on the
-command line it will be invoked internally with default parameters.
+greater than zero.
 .SP
-Alternatively, some effects (e.g.
-.BR synth ,
-.BR remix )
-determine what will be the number of output channels; in this case, neither
-this option nor the
-.B mixer
-effect is necessary.
+For an input file, the most likely need for this option is with a
+`raw' audio file.  For file types that store the number of
+channels in the file header, this option is only needed in order to
+override the (presumably incorrect) value in the file header.
+.SP
+For an output file, this option provides a shorthand for specifying
+that the
+.B channels
+effect should be invoked in order to change (if necessary) the number
+of channels in the audio signal to the given number of channels.  For
+example, the following two commands are equivalent:
+.EX
+.ne 2
+	sox input.au -c 1 output.au bass -3
+	sox input.au      output.au bass -3 channels 1
+.EE
+though the second command is more flexible as it allows the effects to
+be ordered arbitrarily.
 .TP
 \fB\-e \fIENCODING\fR, \fB\-\-encoding\fR \fIENCODING\fR
 The audio encoding type.
@@ -1049,23 +1052,24 @@
 .TP
 \fB\-r, \fB\-\-rate\fR \fIRATE\fR[\fBk\fR]
 Gives the sample rate in Hz (or kHz if appended with `k') of the file.
-For an input file, the most likely need for this option is for a `raw'
-audio file; for audio file types that store the sample rate value in
-the file header, this option is only needed in order to override
-the (presumably incorrect) value in the file header.
+.SP
+For an input file, the most likely need for this option is with a
+`raw' audio file.  For file types that store the sample rate
+value in the file header, this option is only needed in order to
+override the (presumably incorrect) value in the file header.
+.SP
 For an output file, this option provides a shorthand for specifying
 that the
 .B rate
-effect should be invoked in order to change the sample rate of the
-audio signal before it is stored in the output file.
-For example, the following two commands are
-equivalent:
+effect should be invoked in order to change (if necessary) the sample
+rate of the audio signal to the given value.  For example, the
+following two commands are equivalent:
 .EX
 .ne 2
 	sox input.au -r 48k output.au bass -3
 	sox input.au        output.au bass -3 rate 48k
 .EE
-though the second command is more flexible as it allows
+though the second form is more flexible as it allows
 .B rate
 options to be given, and allows the effects to be ordered arbitrarily.
 .TP
@@ -1404,6 +1408,33 @@
 in place of
 .BR gain\ 1 .
 .TP
+\fBchannels \fICHANNELS\fR
+Invoke a simple algorithm to change the number of channels in
+the audio signal to the given number
+.IR CHANNELS :
+mixing if decreasing the number of channels or duplicating if
+increasing the number of channels.
+.SP
+The
+.B channels
+effect is invoked automatically if SoX's \fB\-c\fR option specifies a
+number of channels that is different to that of the input file(s).
+Alternatively, if this effect is given explicitly, then SoX's
+.B \-c
+option need not be given.  For example, the following two commands are
+equivalent:
+.EX
+.ne 2
+	sox input.au -c 1 output.au bass -3
+	sox input.au      output.au bass -3 channels 1
+.EE
+though the second form is more flexible as it allows the effects to
+be ordered arbitrarily.
+.SP
+See also
+.B remix
+for an effect that allows channels to be mixed arbitrarily.
+.TP
 \fBchorus \fIgain-in gain-out\fR <\fIdelay decay speed depth \fB\-s\fR\^|\^\fB\-t\fR>
 Add a chorus effect to the audio.  This can make a single vocal sound
 like a chorus, but can also be applied to instrumentation.
@@ -2111,11 +2142,8 @@
 .I files
 are mix-combined before entering the effects chain).
 .SP
-This effect is automatically used when the number of input
-channels differ from the number of output channels.  When reducing
-the number of channels it is possible to manually specify the
-.B mixer
-effect and use the \fB\-l\fR, \fB\-r\fR, \fB\-f\fR, \fB\-b\fR,
+When reducing the number of channels it is possible to 
+use the \fB\-l\fR, \fB\-r\fR, \fB\-f\fR, \fB\-b\fR,
 \fB\-1\fR, \fB\-2\fR, \fB\-3\fR, \fB\-4\fR, options to select only
 the left, right, front, back channel(s) or specific channel
 for the output instead of averaging the channels.
--- a/src/effects.h
+++ b/src/effects.h
@@ -22,6 +22,7 @@
   EFFECT(bass)
   EFFECT(bend)
   EFFECT(chorus)
+  EFFECT(channels)
   EFFECT(compand)
   EFFECT(contrast)
   EFFECT(crop)
--- a/src/remix.c
+++ b/src/remix.c
@@ -98,6 +98,18 @@
   return SOX_SUCCESS;
 }
 
+static int show(priv_t *p)
+{
+  unsigned i, j;
+
+  for (j = 0; j < p->num_out_channels; j++) {
+    lsx_debug("%i: ", j);
+    for (i = 0; i < p->out_specs[j].num_in_channels; i++)
+      lsx_debug("\t%i %g", p->out_specs[j].in_specs[i].channel_num, p->out_specs[j].in_specs[i].multiplier);
+  }
+  return SOX_SUCCESS;
+}
+
 static int create(sox_effect_t * effp, int argc, char * * argv)
 {
   priv_t * p = (priv_t *)effp->priv;
@@ -135,6 +147,7 @@
     *effp->in_signal.mult /= max_sum;
   if (!non_integer)
     effp->out_signal.precision = effp->in_signal.precision;
+  show(p);
   return SOX_SUCCESS;
 }
 
@@ -172,7 +185,72 @@
 {
   static sox_effect_handler_t handler = {
     "remix", "[-m|-a] [-p] <0|in-chan[v|d|i volume]{,in-chan[v|d|i volume]}>",
-    SOX_EFF_MCHAN | SOX_EFF_CHAN | SOX_EFF_GAIN, create, start, flow, NULL, NULL, kill, sizeof(priv_t)
+    SOX_EFF_MCHAN | SOX_EFF_CHAN | SOX_EFF_GAIN,
+    create, start, flow, NULL, NULL, kill, sizeof(priv_t)
   };
+  return &handler;
+}
+
+/*----------------------- The `channels' effect alias ------------------------*/
+
+static int channels_getopts(sox_effect_t * effp, int argc, char * * argv)
+{
+  priv_t * p = (priv_t *)effp->priv;
+  char dummy;     /* To check for extraneous chars. */
+
+  if (argc == 2) {
+    if (sscanf(argv[1], "%d %c", (int *)&p->num_out_channels,
+          &dummy) != 1 || (int)p->num_out_channels <= 0)
+      return lsx_usage(effp);
+    effp->out_signal.channels = p->num_out_channels;
+  }
+  else if (argc != 1)
+    return lsx_usage(effp);
+  return SOX_SUCCESS;
+}
+
+static int channels_start(sox_effect_t * effp)
+{
+  priv_t * p = (priv_t *)effp->priv;
+  unsigned num_out_channels = p->num_out_channels != 0 ? p->num_out_channels : effp->out_signal.channels;
+  unsigned i, j;
+
+  p->out_specs = lsx_calloc(num_out_channels, sizeof(*p->out_specs));
+  if (effp->in_signal.channels == num_out_channels)
+    return SOX_EFF_NULL;
+
+  if (effp->in_signal.channels > num_out_channels) {
+    for (j = 0; j < num_out_channels; j++) {
+      unsigned in_per_out = (effp->in_signal.channels + num_out_channels - 1 - j) / num_out_channels;
+      lsx_valloc(p->out_specs[j].in_specs, in_per_out);
+      p->out_specs[j].num_in_channels = in_per_out;
+      for (i = 0; i < in_per_out; ++i) {
+        p->out_specs[j].in_specs[i].channel_num = i * num_out_channels + j;
+        p->out_specs[j].in_specs[i].multiplier = 1. / in_per_out;
+      }
+    }
+    effp->out_signal.precision = SOX_SAMPLE_PRECISION;
+  }
+  else for (j = 0; j < num_out_channels; j++) {
+    lsx_valloc(p->out_specs[j].in_specs, 1);
+    p->out_specs[j].num_in_channels = 1;
+    p->out_specs[j].in_specs[0].channel_num = j % effp->in_signal.channels;
+    p->out_specs[j].in_specs[0].multiplier = 1;
+  }
+  effp->out_signal.channels = p->num_out_channels = num_out_channels;
+  show(p);
+  return SOX_SUCCESS;
+}
+
+sox_effect_handler_t const * lsx_channels_effect_fn(void)
+{
+  static sox_effect_handler_t handler;
+  handler = *lsx_remix_effect_fn();
+  handler.name = "channels";
+  handler.usage = "number";
+  handler.getopts = channels_getopts;
+  handler.start = channels_start;
+  handler.flags |= ~SOX_EFF_MODIFY;
+  handler.flags &= ~SOX_EFF_GAIN;
   return &handler;
 }
--- a/src/sox.c
+++ b/src/sox.c
@@ -970,7 +970,7 @@
       signal.rate != ofile->ft->signal.rate)
     auto_effect(chain, "rate", rate_arg != NULL, &rate_arg, &signal, &guard);
   if (signal.channels != ofile->ft->signal.channels)
-    auto_effect(chain, "mixer", 0, NULL, &signal, &guard);
+    auto_effect(chain, "channels", 0, NULL, &signal, &guard);
   if (signal.rate != ofile->ft->signal.rate)
     auto_effect(chain, "rate", rate_arg != NULL, &rate_arg, &signal, &guard);
 
@@ -2224,7 +2224,7 @@
       break;
 
     case 'c':
-      if (sscanf(optarg, "%i %c", &i, &dummy) != 1 || i <= 0) {
+      if (sscanf(optarg, "%d %c", &i, &dummy) != 1 || i <= 0) {
         lsx_fail("Channels value `%s' is not a positive integer", optarg);
         exit(1);
       }
@@ -2239,7 +2239,7 @@
       break;
 
     case 'b':
-      if (sscanf(optarg, "%i %c", &i, &dummy) != 1 || i <= 0) {
+      if (sscanf(optarg, "%d %c", &i, &dummy) != 1 || i <= 0) {
         lsx_fail("Bits value `%s' is not a positive integer", optarg);
         exit(1);
       }
@@ -2309,7 +2309,7 @@
       if (optarg == NULL)
         ++sox_globals.verbosity;
       else {
-        if (sscanf(optarg, "%i %c", &i, &dummy) != 1 || i < 0) {
+        if (sscanf(optarg, "%d %c", &i, &dummy) != 1 || i < 0) {
           sox_globals.verbosity = 2;
           lsx_fail("Verbosity value `%s' is not a non-negative integer", optarg);
           exit(1);
@@ -2518,7 +2518,7 @@
       if (optarg == NULL)
         ++sox_globals.verbosity;
       else {
-        if (sscanf(optarg, "%i %c", &i, &dummy) != 1 || i < 0) {
+        if (sscanf(optarg, "%d %c", &i, &dummy) != 1 || i < 0) {
           sox_globals.verbosity = 2;
           lsx_fail("Verbosity value `%s' is not a non-negative integer", optarg);
           exit(1);