ref: f1ebb3d4d6f46a896ac91a2aef166ac1f84b86ee
parent: 926fc36598461ed3dc432a709047a741b4436a3a
author: robs <robs>
date: Sat Feb 9 05:31:38 EST 2008
Added remix effect
--- a/AUTHORS
+++ b/AUTHORS
@@ -8,6 +8,8 @@
Mantainers:
Chris Bagwell cbagwell@users.sourceforge.net
+ OSS and Sun players, bugfixes, ADPCM support,
+ patch collection and maintance.
Reuben Thomas rrt@sc3d.org
Build system makeover, libsndfile and ffmpeg sound
format support, libao playback, Secret Rabbit Code
@@ -17,8 +19,8 @@
formats.
Effects: key, tempo, pad, bass, treble, new reverb, new
flanger, soft-knee companding, speed via resampling, filters
- makeover inc. gnuplot & octave plotting; new effects chain
- with buffering and any # channels.
+ makeover inc. gnuplot & octave plotting, splice, remix;
+ new effects chain with buffering and any # channels.
Others: open input files via URL, file merging, play
multiple files with mixed format types, play with replay-gain,
building with cmake, much manual improvement and expansion,
@@ -94,8 +96,5 @@
Stuart Daines <sjd.u-net.com>
Patches for r/w support of gsm-encoded wav files,
cleanup of wav.c.
- Chris Bagwell cbagwell@sprynet.com
- OSS and Sun players, bugfixes, ADPCM support,
- patch collection and maintance.
Matthias Nutt
Multiple effects from command line.
--- a/ChangeLog
+++ b/ChangeLog
@@ -12,6 +12,7 @@
Effects:
o Added effect to splice together audio sections. (robs)
+ o Added remix effect (complements the mixer effect). (robs)
Other new features:
@@ -92,7 +93,7 @@
o Show (with --plot) compand transfer function. (robs)
o Allow e.g. "vol 6dB" (as well as "vol 6 dB"). (robs)
o Changed deemph filter from 1st order to 2nd order for
- better accuracy. (robs)
+ slightly better accuracy. (robs)
o Add option to silence effect to leave periods of silence
in and only strip out extra silence. (Mark Schreiber)
o synth can now generate any number of channels. (robs)
--- a/soxeffect.7
+++ b/soxeffect.7
@@ -512,6 +512,9 @@
.TE
.DT
.SP
+See also
+.B remix
+for a similar effect.
.TP
\fBnoiseprof\fR [\fIprofile-file\fR]
Calculate a profile of the audio for use in noise reduction. See the
@@ -651,6 +654,146 @@
.B resample
for other sample-rate changing effects, and see
\fBresample\fR for more discussion of resampling.
+.TP
+\fBremix\fR [\fB\-a\fR\^|\^\fB\-m\fR] <\fIout-spec\fR>
+\fIout-spec\fR = \fIin-spec\fR{\fB,\fIin-spec\fR} | \fB0\fR
+.br
+\fIin-spec\fR = [\fIin-chan\fR]\^[\fB\-\fR[\fIin-chan2\fR]]\^[\fIvol-spec\fR]
+.br
+\fIvol-spec\fR = \fBp\fR\^|\^\fBi\fR\^|\^\fBv\^\fR[\fIvolume\fR]
+.br
+.SP
+Select and mix input audio channels into output audio channels. Each output
+channel is specified, in turn, by a given \fIout-spec\fR: a list of
+contributing input channels and volume specifications.
+.SP
+Note that this effect operates on the audio
+.I channels
+within the SoX effects processing chain; it should not be confused with the
+.B \-m
+global option (where multiple
+.I files
+are mix-combined before entering the effects chain).
+.SP
+An
+.I out-spec
+contains comma-separated input channel-numbers and hyphen-delimited
+channel-number ranges; alternatively,
+.B 0
+may be given to create a silent output channel. For example,
+.EX
+ sox input.au output.au remix 6 7 8 0
+.EE
+creates an output file with four channels, where channels 1, 2, and 3 are
+copies of channels 6, 7, and 8 in the input file, and channel 4 is silent.
+Whereas
+.EX
+ sox input.au output.au remix 1-3,7 3
+.EE
+creates a stereo output file where the left channel is a mix-down of input
+channels 1, 2, 3, and 7, and the right channel is a copy of input channel 3.
+.SP
+Where a range of channels is specified, the channel numbers to the left and
+right of the hyphen are optional and default to 1 and to the number of input
+channels respectively. Thus
+.EX
+ sox input.au output.au remix -
+.EE
+performs a mix-down of all input channels to mono.
+.SP
+By default, where an output channel is mixed from multiple (n) input
+channels, each input channel will be scaled by a factor of \(S1/\s-2n\s+2.
+Custom mixing volumes can be set by following a given input channel or range
+of input channels with a \fIvol-spec\fR (volume specification).
+This is one of the letters \fBp\fR, \fBi\fR, or \fBv\fR,
+followed by a volume number, the meaning of which depends on the given
+letter and is defined as follows:
+.TS
+center;
+lI lI lI
+c l l.
+Letter Volume number Notes
+p power adjust in dB 0 = no change
+i power adjust in dB T{
+.na
+As `p', but invert the audio
+T}
+v voltage multiplier T{
+.na
+1 = no change, 0\*d5 \(~= 6dB attenuation, 2 \(~= 6dB gain, \-1 = invert
+T}
+.TE
+
+If an
+.I out-spec
+includes at least one
+.I vol-spec
+then, by default, \(S1/\s-2n\s+2 scaling is not applied to any other channels in the
+same out-spec (though may be in other out-specs).
+The \-a (automatic)
+option however, can be given to retain the automatic scaling in this
+case. For example,
+.EX
+ sox input.au output.au remix 1,2 3,4v0.8
+.EE
+results in channel level multipliers of 0\*d5,0\*d5 1,0\*d8, whereas
+.EX
+ sox input.au output.au remix -a 1,2 3,4v0.8
+.EE
+results in channel level multipliers of 0\*d5,0\*d5 0\*d5,0\*d8.
+.SP
+The \-m (manual) option disables all automatic volume adjustments, so
+.EX
+ sox input.au output.au remix -m 1,2 3,4v0.8
+.EE
+results in channel level multipliers of 1,1 1,0\*d8.
+.SP
+The volume number is optional and omitting it corresponds to no volume
+change; however, the only case in which this is useful is in conjunction
+with
+.BR i .
+For example, if
+.I input.au
+is stereo, then
+.EX
+ sox input.au output.au remix 1,2i
+.EE
+is a mono equivalent of the
+.B oops
+effect.
+.TS
+center;
+c8 c8 c.
+* * *
+.TE
+.SP
+One typical use of the
+.B remix
+effect is to split an audio file into a set of files, each containing
+one of the constituent channels (in order to perform subsequent
+processing on individual audio channels). Where more than a few
+channels are involved, a script such as the following is useful:
+.EX
+#!/bin/sh # This is a Bourne shell script
+chans=$(sox -V "$1" -n 2>&1|grep \^Chan|head -1|sed "s/.*: //")
+while [ $chans -ge 1 ]; do
+ chans0=$(printf %02i $chans) # 2 digits hence up to 99 chans
+ out=$(echo "$1"|sed "s/\\(.*\\)\\.\\(.*\\)/\\1-$chans0.\\2/")
+ sox "$1" "$out" remix $chans
+ chans=$(expr $chans - 1)
+done
+.EE
+If a file
+.I input.au
+containing six audio channels were given, the script would produce six
+output files:
+.IR input-01.au ,
+\fIinput-02.au\fR, ...,
+.IR input-06.au .
+.SP
+See also
+.B mixer
+for a similar effect.
.TP
\fBrepeat \fIcount\fR
Repeat the entire audio \fIcount\fR times.
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -18,15 +18,15 @@
# Format with: !xargs echo|tr ' ' '\n'|sort|column|expand|sed 's/^/ /'
set(effects_srcs
- biquad echos noiseprof resample swap
- biquads effects noisered reverb synth
- chorus fade pad reverse tempo
- compand FFT pan silence tremolo
- compandt filter phaser skeleff trim
- dcshift flanger pitch speed vibro
- dither key polyphas splice vol
- earwax mcompand rate stat
- echo mixer repeat stretch
+ biquad echos noiseprof repeat stretch
+ biquads effects noisered resample swap
+ chorus fade pad reverb synth
+ compand FFT pan reverse tempo
+ compandt filter phaser silence tremolo
+ dcshift flanger pitch skeleff trim
+ dither key polyphas speed vibro
+ earwax mcompand rate splice vol
+ echo mixer remix stat
)
set(formats_srcs
8svx cvsd hcom s1-fmt u2-fmt
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -196,7 +196,7 @@
compandt.c compandt.h dcshift.c dither.c earwax.c echo.c echos.c \
effects.c effects.h fade.c FFT.c FFT.h fifo.h filter.c flanger.c key.c \
ladspa.c mcompand.c mixer.c noiseprof.c noisered.c noisered.h pad.c \
- pan.c phaser.c pitch.c polyphas.c rabbit.c rate.c repeat.c \
+ pan.c phaser.c pitch.c polyphas.c rabbit.c rate.c remix.c repeat.c \
resample.c reverb.c reverse.c silence.c skeleff.c speed.c \
splice.c stat.c stretch.c swap.c synth.c tempo.c tremolo.c trim.c \
vibro.c vol.c
--- a/src/effects.h
+++ b/src/effects.h
@@ -43,6 +43,7 @@
#endif
EFFECT(rate)
EFFECT(repeat)
+ EFFECT(remix)
EFFECT(resample)
EFFECT(reverb)
EFFECT(reverse)
--- /dev/null
+++ b/src/remix.c
@@ -1,0 +1,163 @@
+/*
+ * Effect: remix
+ * Copyright (c) 2008 robs@users.sourceforge.net
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, write to the Free Software Foundation,
+ * Fifth Floor, 51 Franklin Street, Boston, MA 02111-1301, USA.
+ */
+
+#include "sox_i.h"
+#include <math.h>
+#include <string.h>
+
+typedef struct remix
+{
+ enum {semi, automatic, manual} mode;
+ unsigned num_out_channels, min_in_channels;
+ struct {
+ char * str; /* Command-line argument to parse for this out_spec */
+ unsigned num_in_channels;
+ struct in_spec {
+ unsigned channel_num;
+ double multiplier;
+ } * in_specs;
+ } * out_specs;
+} * remix_t;
+
+assert_static(sizeof(struct remix) <= SOX_MAX_EFFECT_PRIVSIZE,
+ /* else */ remix_PRIVSIZE_too_big);
+
+#define PARSE(SEP, SCAN, VAR, MIN, SEPARATORS) do {\
+ end = strpbrk(text, SEPARATORS); \
+ if (end == text) \
+ SEP = *text++; \
+ else { \
+ SEP = (SEPARATORS)[strlen(SEPARATORS) - 1]; \
+ n = sscanf(text, SCAN"%c", &VAR, &SEP); \
+ if (VAR < MIN || (n == 2 && !strchr(SEPARATORS, SEP))) \
+ return sox_usage(effp); \
+ text = end? end + 1 : text + strlen(text); \
+ } \
+} while (0)
+
+static int parse(sox_effect_t * effp, char * * argv, unsigned channels)
+{
+ remix_t p = (remix_t) effp->priv;
+ unsigned i, j;
+
+ p->min_in_channels = 0;
+ for (i = 0; i < p->num_out_channels; ++i) {
+ sox_bool mul_spec = sox_false;
+ char * text, * end;
+ if (argv) /* 1st parse only */
+ p->out_specs[i].str = xstrdup(argv[i]);
+ for (j = 0, text = p->out_specs[i].str; *text;) {
+ static char const separators[] = "-vpi,";
+ char sep1, sep2;
+ int chan1 = 1, chan2 = channels, n;
+ double multiplier = HUGE_VAL;
+
+ PARSE(sep1, "%i", chan1, 0, separators);
+ if (!chan1) {
+ if (j || *text)
+ return sox_usage(effp);
+ continue;
+ }
+ if (sep1 == '-')
+ PARSE(sep1, "%i", chan2, 0, separators + 1);
+ else chan2 = chan1;
+ if (sep1 != ',') {
+ multiplier = sep1 == 'v' ? 1 : 0;
+ PARSE(sep2, "%lf", multiplier, -HUGE_VAL, separators + 4);
+ if (sep1 != 'v')
+ multiplier = (sep1 == 'p'? 1 : -1) * exp(multiplier / 40 * log(10.));
+ mul_spec = sox_true;
+ }
+ if (chan2 < chan1) {int t = chan1; chan1 = chan2; chan2 = t;}
+ p->out_specs[i].in_specs = xrealloc(p->out_specs[i].in_specs,
+ (j + chan2 - chan1 + 1) * sizeof(*p->out_specs[i].in_specs));
+ while (chan1 <= chan2) {
+ p->out_specs[i].in_specs[j].channel_num = chan1++ - 1;
+ p->out_specs[i].in_specs[j++].multiplier = multiplier;
+ }
+ p->min_in_channels = max(p->min_in_channels, (unsigned)chan2);
+ }
+ p->out_specs[i].num_in_channels = j;
+ for (j = 0; j < p->out_specs[i].num_in_channels; ++j)
+ if (p->out_specs[i].in_specs[j].multiplier == HUGE_VAL)
+ p->out_specs[i].in_specs[j].multiplier = (p->mode == automatic || (p->mode == semi && !mul_spec)) ? 1. / p->out_specs[i].num_in_channels : 1;
+ }
+ effp->outinfo.channels = p->num_out_channels;
+ return SOX_SUCCESS;
+}
+
+static int create(sox_effect_t * effp, int argc, char * * argv)
+{
+ remix_t p = (remix_t) effp->priv;
+ if (argc && !strcmp(*argv, "-m")) p->mode = manual , ++argv, --argc;
+ if (argc && !strcmp(*argv, "-a")) p->mode = automatic, ++argv, --argc;
+ p->out_specs = xcalloc(p->num_out_channels = argc, sizeof(*p->out_specs));
+ return parse(effp, argv, 1); /* No channels yet; parse with dummy */
+}
+
+static int start(sox_effect_t * effp)
+{
+ remix_t p = (remix_t) effp->priv;
+ parse(effp, NULL, effp->ininfo.channels);
+ if (effp->ininfo.channels < p->min_in_channels) {
+ sox_fail("too few input channels");
+ return SOX_EOF;
+ }
+ return SOX_SUCCESS;
+}
+
+static int flow(sox_effect_t * effp, const sox_sample_t * ibuf,
+ sox_sample_t * obuf, sox_size_t * isamp, sox_size_t * osamp)
+{
+ remix_t p = (remix_t) effp->priv;
+ unsigned i, j, len;
+ len = min(*isamp / effp->ininfo.channels, *osamp / effp->outinfo.channels);
+ *isamp = len * effp->ininfo.channels;
+ *osamp = len * effp->outinfo.channels;
+
+ for (; len--; ibuf += effp->ininfo.channels) for (j = 0; j < effp->outinfo.channels; j++) {
+ double out = 0;
+ for (i = 0; i < p->out_specs[j].num_in_channels; i++)
+ out += ibuf[p->out_specs[j].in_specs[i].channel_num] * p->out_specs[j].in_specs[i].multiplier;
+ *obuf++ = SOX_ROUND_CLIP_COUNT(out, effp->clips);
+ }
+ return SOX_SUCCESS;
+}
+
+static int kill(sox_effect_t * effp)
+{
+ remix_t p = (remix_t) effp->priv;
+ unsigned i;
+ for (i = 0; i < p->num_out_channels; ++i) {
+ free(p->out_specs[i].str);
+ free(p->out_specs[i].in_specs);
+ }
+ free(p->out_specs);
+ return SOX_SUCCESS;
+}
+
+sox_effect_handler_t const * sox_remix_effect_fn(void)
+{
+ static sox_effect_handler_t handler = {
+ "remix", "<0|in-chan[v|d|i volume]{,in-chan[v|d|i volume]}>",
+ SOX_EFF_MCHAN | SOX_EFF_CHAN,
+ create, start, flow, NULL, NULL, kill
+ };
+ return &handler;
+}
--- a/src/sox.c
+++ b/src/sox.c
@@ -1149,8 +1149,8 @@
static void sigint(int s)
{
static struct timeval then;
- if (input_count > 1 && show_progress && s == SIGINT && combine_method <= sox_concatenate &&
- since(&then, 1.0, sox_true))
+ if (input_count > 1 && show_progress && s == SIGINT &&
+ combine_method <= sox_concatenate && since(&then, 1.0, sox_true))
user_skip = sox_true;
else user_abort = sox_true;
}
@@ -1536,8 +1536,13 @@
ofile->signal.size = combiner.size;
if (ofile->signal.encoding == SOX_ENCODING_UNKNOWN)
ofile->signal.encoding = combiner.encoding;
- if (ofile->signal.channels == 0)
- ofile->signal.channels = combiner.channels;
+ if (ofile->signal.channels == 0) {
+ unsigned j;
+ for (j = 0; j < nuser_effects && !ofile->signal.channels; ++j)
+ ofile->signal.channels = user_efftab[nuser_effects - 1 - j].outinfo.channels;
+ if (ofile->signal.channels == 0)
+ ofile->signal.channels = combiner.channels;
+ }
combiner.rate *= sox_effects_globals.speed;