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);