ref: 02fb037bc06ea15f3a5d5ce38f2d9af42533ed6f
parent: 2c65075425ac1ba118a3b520554dc165b28afe90
author: robs <robs>
date: Sun Jan 21 17:35:11 EST 2007
Display the (auto-)selected effects chain with -V. Don't list deprecated effects in sox usage; move them to separate section of man page. mixer is the new name for avg. Null pointer idiom for effect driver functions.
--- a/sox.1
+++ b/sox.1
@@ -287,7 +287,7 @@
plays an audio file (through the default sound device) whilst applying a
bass boosting effect,
.SP
- play \-c 4 \-n \-c 1 synth sin %\-12 sin %\-9 sin %\-5 sin %\-2 vol 0\*d7 avg fade q 0\*d1 1 0\*d1
+ play \-c 4 \-n \-c 1 synth sin %\-12 sin %\-9 sin %\-5 sin %\-2 vol 0\*d7 mixer fade q 0\*d1 1 0\*d1
.SP
plays a synthesised `A minor seventh' chord with a pipe-organ sound,
.SP
@@ -388,8 +388,9 @@
and will be merged together (instead of concatenated)
to form the output file.
A merged audio file comprises all of the channels from all of the input
-files; a merged file could be un-merged using the
-.B pick
+files; a merged file could be un-merged by using multiple invocations of
+SoX with the
+.B mixer
effect.
.SP
For example, two mono files could be merged to form one
@@ -484,9 +485,9 @@
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 avg
+.B mixer
effect must be used. If the
-.B avg
+.B mixer
effect is not specified on the
command line it will be invoked internally with default parameters.
.TP
@@ -1029,52 +1030,6 @@
.SP
This effect supports the \fB\-\-octave\fR global option.
.TP
-\fBavg\fR [ \fB\-l\fR\^|\^\fB\-r\fR\^|\^\fB\-f\fR\^|\^\fB\-b\fR\^|\^\fB\-1\fR\^|\^\fB\-2\fR\^|\^\fB\-3\fR\^|\^\fB\-4\fR\^|\^\fIn\fR{\fB,\fIn\fR} ]
-Reduce the number of audio channels by mixing or selecting channels,
-or duplicate channels to increase the number of channels.
-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 avg
-effect and 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.
-The \fB\-l\fR, and \fB\-r\fR options will do averaging
-in quad-channel files so select the exact channel to prevent this.
-.SP
-The
-.B avg
-effect can also be invoked with up to 16
-numbers, separated by commas, which specify the proportion (0 = 0% and 1 = 100%)
-of each input channel that is to be mixed into each output channel.
-In two-channel mode, 4 numbers are given: l \*(RA l, l \*(RA r, r \*(RA l, and r \*(RA r,
-respectively.
-In four-channel mode, the first 4 numbers give the proportions for the
-left-front output channel, as follows: lf \*(RA lf, rf \*(RA lf, lb \*(RA lf, and
-rb \*(RA rf.
-The next 4 give the right-front output in the same order, then
-left-back and right-back.
-.SP
-It is also possible to use the 16 numbers to expand or reduce the
-channel count; just specify 0 for unused channels.
-.SP
-Finally, certain reduced combination of numbers can be specified
-for certain input/output channel combinations.
-.TS
-center box ;
-cB cB cB lB
-c c c l .
-In Ch Out Ch Num Mappings
-2 1 2 l \*(RA l, r \*(RA l
-2 2 1 adjust balance
-4 1 4 lf \*(RA l, rf \*(RA l, lb \*(RA l, rb \*(RA l
-4 2 2 lf \*(RA l&rf \*(RA r, lb \*(RA l&rb \*(RA r
-4 4 1 adjust balance
-4 4 2 front balance, back balance
-.TE
-.SP
-.TP
\fBband\fR [\fB\-n\fR] \fIcenter\fR [width\fR[\fBh\fR\^|\^\fBo\fR\^|\^\fBq\fR]]
Apply a band-pass filter.
The frequency response drops logarithmically
@@ -1386,14 +1341,6 @@
.SP
See [3] for a detailed description of flanging.
.TP
-\fBhighp\fR \fIfrequency\fR
-Apply a high-pass filter.
-This effect is just an alias for the
-.B highpass
-effect used with its
-.B -1
-option; it is retained for backwards compatibilty only.
-.TP
\fBhighpass\fR\^|\^\fBlowpass\fR [\fB-1\fR|\fB-2\fR] \fIfrequency\fR [\fRwidth\fR[\fBq\fR\^|\^\fBo\fR\^|\^\fBh\fR]]
Apply a high-pass or low-pass filter with 3dB point \fIfrequency\fR.
The filter can be either single-pole (with
@@ -1412,16 +1359,8 @@
.SP
See also \fBfilter\fR for filters with a steeper roll-off.
.TP
-\fBlowp \fIfrequency\fR
+\fBlowpass\fR [\fB-1\fR|\fB-2\fR] \fIfrequency\fR [\fRwidth\fR[\fBq\fR\^|\^\fBo\fR\^|\^\fBh\fR]]
Apply a low-pass filter.
-This effect is just an alias for the
-.B lowpass
-effect used with its
-.B -1
-option; it is retained for backwards compatibilty only.
-.TP
-\fBlowpass\fR [\fB-1\fR|\fB-2\fR] \fIfrequency\fR [\fIQ\fR]
-Apply a low-pass filter.
See the description of the \fBhighpass\fR effect for details.
.TP
\fBlua\fR \fIlua-script\fR [\fIoption...\fR]
@@ -1429,9 +1368,6 @@
.BR soxlua (7)
for details.
.TP
-\fBmask\fR [\fIdepth\fR]
-This effect is just a deprecated alias for the \fBdither\fR effect, left for historical reasons.
-.TP
\fBmcompand "\fIattack1\fB,\fIdecay1\fR{\fB,\fIattack2\fB,\fIdecay2\fR}
\fIin-dB1\fB,\fIout-dB1\fR{\fB,\fIin-dB2\fB,\fIout-dB2\fR}
.br
@@ -1445,6 +1381,52 @@
\fIxover-freq\fR. This can be repeated multiple times to create
multiple bands.
.TP
+\fBmixer\fR [ \fB\-l\fR\^|\^\fB\-r\fR\^|\^\fB\-f\fR\^|\^\fB\-b\fR\^|\^\fB\-1\fR\^|\^\fB\-2\fR\^|\^\fB\-3\fR\^|\^\fB\-4\fR\^|\^\fIn\fR{\fB,\fIn\fR} ]
+Reduce the number of audio channels by mixing or selecting channels,
+or duplicate channels to increase the number of channels.
+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,
+\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.
+The \fB\-l\fR, and \fB\-r\fR options will do averaging
+in quad-channel files so select the exact channel to prevent this.
+.SP
+The
+.B mixer
+effect can also be invoked with up to 16
+numbers, separated by commas, which specify the proportion (0 = 0% and 1 = 100%)
+of each input channel that is to be mixed into each output channel.
+In two-channel mode, 4 numbers are given: l \*(RA l, l \*(RA r, r \*(RA l, and r \*(RA r,
+respectively.
+In four-channel mode, the first 4 numbers give the proportions for the
+left-front output channel, as follows: lf \*(RA lf, rf \*(RA lf, lb \*(RA lf, and
+rb \*(RA rf.
+The next 4 give the right-front output in the same order, then
+left-back and right-back.
+.SP
+It is also possible to use the 16 numbers to expand or reduce the
+channel count; just specify 0 for unused channels.
+.SP
+Finally, certain reduced combination of numbers can be specified
+for certain input/output channel combinations.
+.TS
+center box ;
+cB cB cB lB
+c c c l .
+In Ch Out Ch Num Mappings
+2 1 2 l \*(RA l, r \*(RA l
+2 2 1 adjust balance
+4 1 4 lf \*(RA l, rf \*(RA l, lb \*(RA l, rb \*(RA l
+4 2 2 lf \*(RA l&rf \*(RA r, lb \*(RA l&rb \*(RA r
+4 4 1 adjust balance
+4 4 2 front balance, back balance
+.TE
+.SP
+.TP
\fBnoiseprof\fR [\fIprofile-file\fR]
Calculate a profile of the audio for use in noise reduction.
See the description of the \fBnoisered\fR effect for details.
@@ -1515,11 +1497,6 @@
(\fB\-t\fR). The decay should be less than 0\*d5 to avoid
feedback. Gain-out is the volume of the output.
.TP
-\fBpick\fR [ \fB\-l\fR\^|\^\fB\-r\fR\^|\^\fB\-f\fR\^|\^\fB\-b\fR\^|\^\fB\-1\fR\^|\^\fB\-2\fR\^|\^\fB\-3\fR\^|\^\fB\-4\fR\^|\^\fIn\fR{\fB,\fIn\fR} ]
-Pick a subset of channels to be copied into the output file. This effect is just an alias of the
-.B avg
-effect and is retained for backwards compatibility only.
-.TP
\fBpitch \fIshift\fR [\fIwidth interpolate fade\fR]
Change the pitch of file without affecting its duration by cross-fading
shifted samples.
@@ -1576,10 +1553,6 @@
for other sample-rate changing effects, and see
\fBresample\fR for more discussion of resampling.
.TP
-\fBrate\fR
-Does the same as \fBresample\fR with no parameters; it exists for
-backwards compatibility.
-.TP
\fBrepeat \fIcount\fR
Repeat the entire audio \fIcount\fR times.
Requires disk space to store the data to be repeated.
@@ -2032,6 +2005,46 @@
Not specifying this parameter will cause no limiter to be used. In verbose
mode, this effect will display the percentage of the audio that needed to be
limited.
+.SS Deprecated Effects
+The following effects have ben renamed or have their functionality
+included in another effect. They continue to work in this version of
+SoX but may be removed in future.
+.TP
+\fBavg\fR [ \fB\-l\fR\^|\^\fB\-r\fR\^|\^\fB\-f\fR\^|\^\fB\-b\fR\^|\^\fB\-1\fR\^|\^\fB\-2\fR\^|\^\fB\-3\fR\^|\^\fB\-4\fR\^|\^\fIn\fR{\fB,\fIn\fR} ]
+Reduce the number of audio channels by mixing or selecting channels,
+or duplicate channels to increase the number of channels.
+This effect is just an alias of the
+.B mixer
+effect and is retained for backwards compatibility only.
+.TP
+\fBhighp\fR \fIfrequency\fR
+Apply a high-pass filter.
+This effect is just an alias for the
+.B highpass
+effect used with its
+.B -1
+option; it is retained for backwards compatibilty only.
+.TP
+\fBlowp \fIfrequency\fR
+Apply a low-pass filter.
+This effect is just an alias for the
+.B lowpass
+effect used with its
+.B -1
+option; it is retained for backwards compatibilty only.
+.TP
+\fBmask\fR [\fIdepth\fR]
+This effect is just a deprecated alias for the \fBdither\fR effect, left for historical reasons.
+.TP
+\fBpick\fR [ \fB\-l\fR\^|\^\fB\-r\fR\^|\^\fB\-f\fR\^|\^\fB\-b\fR\^|\^\fB\-1\fR\^|\^\fB\-2\fR\^|\^\fB\-3\fR\^|\^\fB\-4\fR\^|\^\fIn\fR{\fB,\fIn\fR} ]
+Pick a subset of channels to be copied into the output file.
+This effect is just an alias of the
+.B mixer
+effect and is retained for backwards compatibility only.
+.TP
+\fBrate\fR
+Does the same as \fBresample\fR with no parameters; it exists for
+backwards compatibility.
.SH DIAGNOSTICS
Exit status is 0 for no error, 1 if there is a problem with the
command-line parameters, or 2 if an error occurs during file processing.
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -22,9 +22,9 @@
sfircam.h skelform.c smp.c sndfile.c sndrtool.c sphere.c tx16w.c \
voc.c vorbis.c vox.c wav.c wav.h wve.c xa.c
-effects = avg.c band.h biquad.c biquad.h biquads.c chorus.c compand.c dcshift.c\
- deemph.h earwax.c echo.c echos.c fade.c FFT.c FFT.h filter.c \
- flanger.c luaeff.c luaform.c lintlib.c mask.c mcompand.c noiseprof.c \
+effects = band.h biquad.c biquad.h biquads.c chorus.c compand.c dcshift.c\
+ deemph.h dither.c earwax.c echo.c echos.c fade.c FFT.c FFT.h filter.c\
+ flanger.c luaeff.c luaform.c lintlib.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 resample.c reverb.c reverse.c silence.c \
skeleff.c speed.c stat.c stretch.c swap.c synth.c trim.c vibro.c vol.c
--- a/src/avg.c
+++ /dev/null
@@ -1,582 +1,0 @@
-
-/*
- * July 5, 1991
- * Copyright 1991 Lance Norskog And Sundry Contributors
- * This source code is freely redistributable and may be used for
- * any purpose. This copyright notice must be maintained.
- * Lance Norskog And Sundry Contributors are not responsible for
- * the consequences of using this software.
- *
- * Channel duplication code by Graeme W. Gill - 93/5/18
- * General-purpose panning by Geoffrey H. Kuenning -- 2000/11/28
- */
-
-/*
- * Sound Tools stereo/quad -> mono mixdown effect file.
- * and mono/stereo -> stereo/quad channel duplication.
- */
-
-#include "st_i.h"
-#include <ctype.h>
-#include <string.h>
-#include <stdlib.h>
-
-static st_effect_t st_avg_effect;
-
-typedef struct avgstuff {
- /* How to generate each output channel. sources[i][j] */
- /* represents the fraction of channel i that should be passed */
- /* through to channel j on output, and so forth. Channel 0 is */
- /* left front, channel 1 is right front, and 2 and 3 are left */
- /* and right rear, respectively. (GHK) */
- double sources[4][4];
- int num_pans;
- int mix; /* How are we mixing it? */
-} *avg_t;
-
-/* MIX_CENTER is shorthand to mix channels together at 50% each */
-#define MIX_CENTER 0
-#define MIX_LEFT 1
-#define MIX_RIGHT 2
-#define MIX_FRONT 3
-#define MIX_BACK 4
-#define MIX_SPECIFIED 5
-#define MIX_LEFT_FRONT 6
-#define MIX_RIGHT_FRONT 7
-#define MIX_LEFT_BACK 8
-#define MIX_RIGHT_BACK 9
-
-
-/*
- * Process options
- */
-static int st_avg_getopts(eff_t effp, int n, char **argv)
-{
- avg_t avg = (avg_t) effp->priv;
- double* pans = &avg->sources[0][0];
- int i;
-
- for (i = 0; i < 16; i++)
- pans[i] = 0.0;
- avg->mix = MIX_CENTER;
- avg->num_pans = 0;
-
- /* Parse parameters. Since we don't yet know the number of */
- /* input and output channels, we'll record the information for */
- /* later. */
- if (n == 1) {
- if(!strcmp(argv[0], "-l"))
- avg->mix = MIX_LEFT;
- else if (!strcmp(argv[0], "-r"))
- avg->mix = MIX_RIGHT;
- else if (!strcmp(argv[0], "-f"))
- avg->mix = MIX_FRONT;
- else if (!strcmp(argv[0], "-b"))
- avg->mix = MIX_BACK;
- else if (!strcmp(argv[0], "-1"))
- avg->mix = MIX_LEFT_FRONT;
- else if (!strcmp(argv[0], "-2"))
- avg->mix = MIX_RIGHT_FRONT;
- else if (!strcmp(argv[0], "-3"))
- avg->mix = MIX_LEFT_BACK;
- else if (!strcmp(argv[0], "-4"))
- avg->mix = MIX_RIGHT_BACK;
- else if (argv[0][0] == '-' && !isdigit((int)argv[0][1])
- && argv[0][1] != '.') {
- st_fail(st_avg_effect.usage);
- return (ST_EOF);
- }
- else {
- int commas;
- char *s;
- avg->mix = MIX_SPECIFIED;
- pans[0] = atof(argv[0]);
- for (s = argv[0], commas = 0; *s; ++s) {
- if (*s == ',') {
- ++commas;
- if (commas >= 16) {
- st_fail("avg can only take up to 16 pan values");
- return (ST_EOF);
- }
- pans[commas] = atof(s+1);
- }
- }
- avg->num_pans = commas + 1;
- }
- }
- else if (n == 0) {
- avg->mix = MIX_CENTER;
- }
- else {
- st_fail(st_avg_effect.usage);
- return ST_EOF;
- }
-
- return (ST_SUCCESS);
-}
-
-/*
- * Start processing
- */
-static int st_avg_start(eff_t effp)
-{
- /*
- Hmmm, this is tricky. Lemme think:
- channel orders are [0][0],[0][1], etc.
- i.e., 0->0, 0->1, 0->2, 0->3, 1->0, 1->1, ...
- trailing zeros are omitted
- L/R balance is x= -1 for left only, 1 for right only
- 1->1 channel effects:
- changing volume by x is x,0,0,0
- 1->2 channel effects:
- duplicating everywhere is 1,1,0,0
- 1->4 channel effects:
- duplicating everywhere is 1,1,1,1
- 2->1 channel effects:
- left only is 1,0,0,0 0,0,0,0
- right only is 0,0,0,0 1,0,0,0
- left+right is 0.5,0,0,0 0.5,0,0,0
- left-right is 1,0,0,0 -1,0,0,0
- 2->2 channel effects:
- L/R balance can be done several ways. The standard stereo
- way is both the easiest and the most sensible:
- min(1-x,1),0,0,0 0,min(1+x,1),0,0
- left to both is 1,1,0,0
- right to both is 0,0,0,0 1,1,0,0
- left+right to both is 0.5,0.5,0,0 0.5,0.5,0,0
- left-right to both is 1,1,0,0 -1,-1,0,0
- left-right to left, right-left to right is 1,-1,0,0 -1,1,0,0
- 2->4 channel effects:
- front duplicated into rear is 1,0,1,0 0,1,0,1
- front swapped into rear (why?) is 1,0,0,1 0,1,1,0
- front put into rear as mono (why?) is 1,0,0.5,0.5 0,1,0.5,0.5
- 4->1 channel effects:
- left front only is 1,0,0,0
- left only is 0.5,0,0,0 0,0,0,0 0.5,0,0,0
- etc.
- 4->2 channel effects:
- merge front/back is 0.5,0,0,0 0,0.5,0,0 0.5,0,0,0 0,0.5,0,0
- selections similar to above
- 4->4 channel effects:
- left front to all is 1,1,1,1 0,0,0,0
- right front to all is 0,0,0,0 1,1,1,1
- left f/r to all f/r is 1,1,0,0 0,0,0,0 0,0,1,1 0,0,0,0
- etc.
-
- The interesting cases from above (deserving of abbreviations of
- less than 16 numbers) are:
-
- 0) n->n volume change (1 number)
- 1) 1->n duplication (0 numbers)
- 2) 2->1 mixdown (0 or 2 numbers)
- 3) 2->2 balance (1 number)
- 4) 2->2 fully general mix (4 numbers)
- 5) 2->4 duplication (0 numbers)
- 6) 4->1 mixdown (0 or 4 numbers)
- 7) 4->2 mixdown (0, or 2 numbers)
- 8) 4->4 balance (1 or 2 numbers)
-
- The above has one ambiguity: n->n volume change conflicts with
- n->n balance for n != 1. In such a case, we'll prefer
- balance, since there is already a volume effect in vol.c.
-
- GHK 2000/11/28
- */
- avg_t avg = (avg_t) effp->priv;
- double pans[16];
- int i, j;
- int ichan, ochan;
-
- for (i = 0; i < 16; i++)
- pans[i] = ((double*)&avg->sources[0][0])[i];
-
- ichan = effp->ininfo.channels;
- ochan = effp->outinfo.channels;
- if (ochan == -1) {
- st_fail("Output must have known number of channels to use avg effect");
- return(ST_EOF);
- }
-
- if ((ichan != 1 && ichan != 2 && ichan != 4)
- || (ochan != 1 && ochan != 2 && ochan != 4)) {
- st_fail("Can't average %d channels into %d channels",
- ichan, ochan);
- return (ST_EOF);
- }
-
- /* Handle the special-case flags */
- switch (avg->mix) {
- case MIX_CENTER:
- if (ichan == ochan) {
- st_fail("Output must have different number of channels to use avg effect");
- return(ST_EOF);
- }
- break; /* Code below will handle this case */
- case MIX_LEFT:
- if (ichan == 2 && ochan == 1)
- {
- pans[0] = 1.0;
- pans[1] = 0.0;
- avg->num_pans = 2;
- }
- else if (ichan == 4 && ochan == 1)
- {
- pans[0] = 0.5;
- pans[1] = 0.0;
- pans[2] = 0.5;
- pans[3] = 0.0;
- avg->num_pans = 4;
- }
- else
- {
- st_fail("Can't average %d channels into %d channels",
- ichan, ochan);
- return ST_EOF;
- }
- break;
- case MIX_RIGHT:
- if (ichan == 2 && ochan == 1)
- {
- pans[0] = 0.0;
- pans[1] = 1.0;
- avg->num_pans = 2;
- }
- else if (ichan == 4 && ochan == 1)
- {
- pans[0] = 0.0;
- pans[1] = 0.5;
- pans[2] = 0.0;
- pans[3] = 0.5;
- avg->num_pans = 4;
- }
- else
- {
- st_fail("Can't average %d channels into %d channels",
- ichan, ochan);
- return ST_EOF;
- }
- break;
- case MIX_FRONT:
- if (ichan == 4 && ochan == 2)
- {
- pans[0] = 1.0;
- pans[1] = 0.0;
- avg->num_pans = 2;
- }
- else
- {
- st_fail("avg: -f option requires 4 channels input and 2 channel output");
- return ST_EOF;
- }
- break;
- case MIX_BACK:
- if (ichan == 4 && ochan == 2)
- {
- pans[0] = 0.0;
- pans[1] = 1.0;
- avg->num_pans = 2;
- }
- else
- {
- st_fail("avg: -b option requires 4 channels input and 2 channel output");
- return ST_EOF;
- }
- break;
- case MIX_LEFT_FRONT:
- if (ichan == 2 && ochan == 1)
- {
- pans[0] = 1.0;
- pans[1] = 0.0;
- avg->num_pans = 2;
- }
- else if (ichan == 4 && ochan == 1)
- {
- pans[0] = 1.0;
- pans[1] = 0.0;
- pans[2] = 0.0;
- pans[3] = 0.0;
- avg->num_pans = 4;
- }
- else
- {
- st_fail("avg: -1 option requires 4 channels input and 1 channel output");
- return ST_EOF;
- }
- break;
- case MIX_RIGHT_FRONT:
- if (ichan == 2 && ochan == 1)
- {
- pans[0] = 0.0;
- pans[1] = 1.0;
- avg->num_pans = 2;
- }
- else if (ichan == 4 && ochan == 1)
- {
- pans[0] = 0.0;
- pans[1] = 1.0;
- pans[2] = 0.0;
- pans[3] = 0.0;
- avg->num_pans = 4;
- }
- else
- {
- st_fail("avg: -2 option requires 4 channels input and 1 channel output");
- return ST_EOF;
- }
- break;
- case MIX_LEFT_BACK:
- if (ichan == 4 && ochan == 1)
- {
- pans[0] = 0.0;
- pans[1] = 0.0;
- pans[2] = 1.0;
- pans[3] = 0.0;
- avg->num_pans = 4;
- }
- else
- {
- st_fail("avg: -3 option requires 4 channels input and 1 channel output");
- return ST_EOF;
- }
- case MIX_RIGHT_BACK:
- if (ichan == 4 && ochan == 1)
- {
- pans[0] = 0.0;
- pans[1] = 0.0;
- pans[2] = 0.0;
- pans[3] = 1.0;
- avg->num_pans = 4;
- }
- else
- {
- st_fail("avg: -4 option requires 4 channels input and 1 channel output");
- return ST_EOF;
- }
-
- case MIX_SPECIFIED:
- break;
- default:
- st_fail("Unknown mix option in average effect");
- return ST_EOF;
- }
-
- /* If number of pans is 4 or less then its a shorthand
- * representation. If user specified it, then we have
- * garbage in our sources[][] array. Need to clear that
- * now that all data is stored in pans[] array.
- */
- if (avg->num_pans <= 4)
- {
- for (i = 0; i < ichan; i++)
- {
- for (j = 0; j < ochan; j++)
- {
- avg->sources[i][j] = 0;
- }
- }
- }
-
- /* If the number of pans given is 4 or fewer, handle the special */
- /* cases listed in the comments above. The code is lengthy but */
- /* straightforward. */
- if (avg->num_pans == 0) {
- /* CASE 1 */
- if (ichan == 1 && ochan > ichan) {
- avg->sources[0][0] = 1.0;
- avg->sources[0][1] = 1.0;
- avg->sources[0][2] = 1.0;
- avg->sources[0][3] = 1.0;
- }
- /* CASE 2 */
- else if (ichan == 2 && ochan == 1) {
- avg->sources[0][0] = 0.5;
- avg->sources[1][0] = 0.5;
- }
- /* CASE 5 */
- else if (ichan == 2 && ochan == 4) {
- avg->sources[0][0] = 1.0;
- avg->sources[0][2] = 1.0;
- avg->sources[1][1] = 1.0;
- avg->sources[1][3] = 1.0;
- }
- /* CASE 6 */
- else if (ichan == 4 && ochan == 1) {
- avg->sources[0][0] = 0.25;
- avg->sources[1][0] = 0.25;
- avg->sources[2][0] = 0.25;
- avg->sources[3][0] = 0.25;
- }
- /* CASE 7 */
- else if (ichan == 4 && ochan == 2) {
- avg->sources[0][0] = 0.5;
- avg->sources[1][1] = 0.5;
- avg->sources[2][0] = 0.5;
- avg->sources[3][1] = 0.5;
- }
- else {
- st_fail("You must specify at least one mix level when using avg with an unusual number of channels.");
- return(ST_EOF);
- }
- }
- else if (avg->num_pans == 1) {
- /* Might be volume change or balance change */
- /* CASE 3 and CASE 8 */
- if ((ichan == 2 || ichan == 4) && ichan == ochan) {
- /* -1 is left only, 1 is right only */
- if (pans[0] <= 0.0) {
- avg->sources[1][1] = pans[0] + 1.0;
- if (avg->sources[1][1] < 0.0)
- avg->sources[1][1] = 0.0;
- avg->sources[0][0] = 1.0;
- }
- else {
- avg->sources[0][0] = 1.0 - pans[0];
- if (avg->sources[0][0] < 0.0)
- avg->sources[0][0] = 0.0;
- avg->sources[1][1] = 1.0;
- }
- if (ichan == 4) {
- avg->sources[2][2] = avg->sources[0][0];
- avg->sources[3][3] = avg->sources[1][1];
- }
- }
- else
- {
- st_fail("Invalid options specified to avg while not mixing");
- return ST_EOF;
- }
- }
- else if (avg->num_pans == 2) {
- /* CASE 2 */
- if (ichan == 2 && ochan == 1) {
- avg->sources[0][0] = pans[0];
- avg->sources[1][0] = pans[1];
- }
- /* CASE 7 */
- else if (ichan == 4 && ochan == 2) {
- avg->sources[0][0] = pans[0];
- avg->sources[1][1] = pans[0];
- avg->sources[2][0] = pans[1];
- avg->sources[3][1] = pans[1];
- }
- /* CASE 8 */
- else if (ichan == 4 && ochan == 4) {
- /* pans[0] is front -> front, pans[1] is for back */
- avg->sources[0][0] = pans[0];
- avg->sources[1][1] = pans[0];
- avg->sources[2][2] = pans[1];
- avg->sources[3][3] = pans[1];
- }
- else
- {
- st_fail("Invalid options specified to avg for this channel combination");
- return ST_EOF;
- }
- }
- else if (avg->num_pans == 4) {
- /* CASE 4 */
- if (ichan == 2 && ochan == 2) {
- /* Shorthand for 2-channel case */
- avg->sources[0][0] = pans[0];
- avg->sources[0][1] = pans[1];
- avg->sources[1][0] = pans[2];
- avg->sources[1][1] = pans[3];
- }
- /* CASE 6 */
- else if (ichan == 4 && ochan == 1) {
- avg->sources[0][0] = pans[0];
- avg->sources[1][0] = pans[1];
- avg->sources[2][0] = pans[2];
- avg->sources[3][0] = pans[3];
- }
- else
- {
- st_fail("Invalid options specified to avg for this channel combination");
- return ST_EOF;
- }
- }
- else
- {
- st_fail("Invalid options specified to avg while not mixing");
- return ST_EOF;
- }
-
- return ST_SUCCESS;
-}
-
-/*
- * Process either isamp or osamp samples, whichever is smaller.
- */
-
-static int st_avg_flow(eff_t effp, const st_sample_t *ibuf, st_sample_t *obuf,
- st_size_t *isamp, st_size_t *osamp)
-{
- avg_t avg = (avg_t) effp->priv;
- st_size_t len, done;
- int ichan, ochan;
- int i, j;
- double samp;
-
- ichan = effp->ininfo.channels;
- ochan = effp->outinfo.channels;
- len = *isamp / ichan;
- if (len > *osamp / ochan)
- len = *osamp / ochan;
- for (done = 0; done < len; done++, ibuf += ichan, obuf += ochan) {
- for (j = 0; j < ochan; j++) {
- samp = 0.0;
- for (i = 0; i < ichan; i++)
- samp += ibuf[i] * avg->sources[i][j];
- ST_SAMPLE_CLIP_COUNT(samp, effp->clips);
- obuf[j] = samp;
- }
- }
- *isamp = len * ichan;
- *osamp = len * ochan;
- return (ST_SUCCESS);
-}
-
-/*
- * Do anything required when you stop reading samples.
- * Don't close input file!
- *
- * Should have statistics on right, left, and output amplitudes.
- */
-static int st_avg_stop(eff_t effp UNUSED)
-{
- return (ST_SUCCESS); /* nothing to do */
-}
-
-static st_effect_t st_avg_effect = {
- "avg",
- "Usage: avg [ -l | -r | -f | -b | -1 | -2 | -3 | -4 | n,n,n...,n ]",
- ST_EFF_MCHAN | ST_EFF_CHAN,
- st_avg_getopts,
- st_avg_start,
- st_avg_flow,
- st_effect_nothing_drain,
- st_avg_stop,
- st_effect_nothing
-};
-
-static st_effect_t st_pick_effect = {
- "pick",
- "Usage: pick [ -l | -r | -f | -b | -1 | -2 | -3 | -4 | n,n,n...,n ]",
- ST_EFF_MCHAN | ST_EFF_CHAN,
- st_avg_getopts,
- st_avg_start,
- st_avg_flow,
- st_effect_nothing_drain,
- st_avg_stop,
- st_effect_nothing
-};
-
-const st_effect_t *st_avg_effect_fn(void)
-{
- return &st_avg_effect;
-}
-
-const st_effect_t *st_pick_effect_fn(void)
-{
- return &st_pick_effect;
-}
--- a/src/biquad.h
+++ b/src/biquad.h
@@ -81,16 +81,6 @@
int st_biquad_flow(eff_t effp, const st_sample_t *ibuf, st_sample_t *obuf,
st_size_t *isamp, st_size_t *osamp);
-#define BIQUAD_EFFECT(name,group,usage) \
-st_effect_t const * st_##name##_effect_fn(void) { \
- static st_effect_t driver = { \
- #name, "Usage: " #name " " usage, 0, \
- group##_getopts, start, st_biquad_flow, \
- st_effect_nothing_drain, st_effect_nothing, st_effect_nothing \
- }; \
- return &driver; \
-}
-
#undef st_fail
#define st_fail st_message_filename=effp->name,st_fail
--- a/src/biquads.c
+++ b/src/biquads.c
@@ -299,15 +299,24 @@
}
-BIQUAD_EFFECT(highp, hilo1, "cutoff-frequency")
-BIQUAD_EFFECT(lowp, hilo1, "cutoff-frequency")
-BIQUAD_EFFECT(highpass, hilo2, "[-1|-2] frequency [width[q|o|h]]")
-BIQUAD_EFFECT(lowpass, hilo2, "[-1|-2] frequency [width[q|o|h]]")
-BIQUAD_EFFECT(bandpass, bandpass, "[-c] frequency width[h|q|o]")
-BIQUAD_EFFECT(bandreject,bandrej, "frequency width[h|q|o]")
-BIQUAD_EFFECT(allpass, allpass, "frequency width[h|q|o]")
-BIQUAD_EFFECT(bass, tone, "gain [frequency [width[s|h|q|o]]]")
-BIQUAD_EFFECT(treble, tone, "gain [frequency [width[s|h|q|o]]]")
-BIQUAD_EFFECT(equalizer, equalizer,"frequency width[q|o|h] gain")
-BIQUAD_EFFECT(band, band, "[-n] center [width[h|q|o]]")
-BIQUAD_EFFECT(deemph, deemph, "takes no options")
+#define BIQUAD_EFFECT(name,group,usage,flags) \
+st_effect_t const * st_##name##_effect_fn(void) { \
+ static st_effect_t driver = { \
+ #name, "Usage: " #name " " usage, flags, \
+ group##_getopts, start, st_biquad_flow, 0, 0, 0, \
+ }; \
+ return &driver; \
+}
+
+BIQUAD_EFFECT(highp, hilo1, "cutoff-frequency", ST_EFF_DEPRECATED)
+BIQUAD_EFFECT(lowp, hilo1, "cutoff-frequency", ST_EFF_DEPRECATED)
+BIQUAD_EFFECT(highpass, hilo2, "[-1|-2] frequency [width[q|o|h]]", 0)
+BIQUAD_EFFECT(lowpass, hilo2, "[-1|-2] frequency [width[q|o|h]]", 0)
+BIQUAD_EFFECT(bandpass, bandpass, "[-c] frequency width[h|q|o]", 0)
+BIQUAD_EFFECT(bandreject,bandrej, "frequency width[h|q|o]", 0)
+BIQUAD_EFFECT(allpass, allpass, "frequency width[h|q|o]", 0)
+BIQUAD_EFFECT(bass, tone, "gain [frequency [width[s|h|q|o]]]", 0)
+BIQUAD_EFFECT(treble, tone, "gain [frequency [width[s|h|q|o]]]", 0)
+BIQUAD_EFFECT(equalizer, equalizer,"frequency width[q|o|h] gain", 0)
+BIQUAD_EFFECT(band, band, "[-n] center [width[h|q|o]]", 0)
+BIQUAD_EFFECT(deemph, deemph, "takes no options", 0)
--- /dev/null
+++ b/src/dither.c
@@ -1,0 +1,108 @@
+/*
+ * Sound Tools dithering noise effect file.
+ *
+ * July 5, 1991
+ * Copyright 1991 Lance Norskog And Sundry Contributors
+ * This source code is freely redistributable and may be used for
+ * any purpose. This copyright notice must be maintained.
+ * Lance Norskog And Sundry Contributors are not responsible for
+ * the consequences of using this software.
+ *
+ * TODO: does triangular noise, could do local shaping
+ *
+ */
+
+#include <stdlib.h>
+#include <math.h>
+#include "st_i.h"
+
+typedef struct dither {
+ double amount;
+} * dither_t;
+
+assert_static(sizeof(struct dither) <= ST_MAX_EFFECT_PRIVSIZE,
+ /* else */ dither_PRIVSIZE_too_big);
+
+static int getopts(eff_t effp, int n, char * * argv)
+{
+ dither_t dither = (dither_t) effp->priv;
+
+ if (n > 1) {
+ st_fail(effp->h->usage);
+ return ST_EOF;
+ }
+
+ dither->amount = sqrt(2); /* M_SQRT2 missing in some places */ /* Default to half a bit. */
+ if (n == 1) {
+ double amount;
+ char dummy;
+ int scanned = sscanf(*argv, "%lf %c", &amount, &dummy);
+ if (scanned == 1 && amount > 0)
+ dither->amount *= amount;
+ else {
+ st_fail(effp->h->usage);
+ return ST_EOF;
+ }
+ }
+
+ return ST_SUCCESS;
+}
+
+static int start(eff_t effp)
+{
+ dither_t dither = (dither_t) effp->priv;
+
+ if (effp->outinfo.encoding == ST_ENCODING_ULAW ||
+ effp->outinfo.encoding == ST_ENCODING_ALAW) {
+ dither->amount *= 16;
+ return ST_SUCCESS;
+ } else if (effp->outinfo.size == ST_SIZE_BYTE) {
+ dither->amount *= 256;
+ return ST_SUCCESS;
+ } else if (effp->outinfo.size == ST_SIZE_16BIT)
+ return ST_SUCCESS;
+ else if (effp->outinfo.size == ST_SIZE_24BIT) {
+ dither->amount /= 256;
+ return ST_SUCCESS;
+ } else if (effp->outinfo.size == ST_SIZE_64BIT) {
+ dither->amount /= 16384;
+ return ST_SUCCESS;
+ }
+
+ st_fail("Invalid size %d", effp->outinfo.size);
+ return ST_EOF;
+}
+
+/* FIXME: Scale noise more sensibly for sizes >= 24 bits */
+static int flow(eff_t effp, const st_sample_t * ibuf,
+ st_sample_t * obuf, st_size_t * isamp, st_size_t * osamp)
+{
+ dither_t dither = (dither_t)effp->priv;
+ st_size_t len = min(*isamp, *osamp);
+
+ *isamp = *osamp = len;
+ while (len--) { /* 16 signed bits of triangular noise */
+ int tri16 = ((rand() % 32768L) + (rand() % 32768L)) - 32767;
+ double l = *ibuf++ + tri16 * dither->amount;
+ *obuf++ = ST_ROUND_CLIP_COUNT(l, effp->clips);
+ }
+ return ST_SUCCESS;
+}
+
+st_effect_t const * st_dither_effect_fn(void)
+{
+ static st_effect_t driver = {
+ "dither", "Usage: dither [amount]", ST_EFF_MCHAN,
+ getopts, start, flow, 0, 0, 0
+ };
+ return &driver;
+}
+
+st_effect_t const * st_mask_effect_fn(void)
+{
+ static st_effect_t driver = {
+ "mask", "Usage: mask [amount]", ST_EFF_MCHAN | ST_EFF_DEPRECATED,
+ getopts, start, flow, 0, 0, 0
+ };
+ return &driver;
+}
--- a/src/handlers.c
+++ b/src/handlers.c
@@ -114,6 +114,7 @@
st_lua_effect_fn,
st_mask_effect_fn,
st_mcompand_effect_fn,
+ st_mixer_effect_fn,
st_noiseprof_effect_fn,
st_noisered_effect_fn,
st_pad_effect_fn,
--- a/src/mask.c
+++ /dev/null
@@ -1,124 +1,0 @@
-/*
- * Sound Tools masking noise effect file.
- *
- * July 5, 1991
- * Copyright 1991 Lance Norskog And Sundry Contributors
- * This source code is freely redistributable and may be used for
- * any purpose. This copyright notice must be maintained.
- * Lance Norskog And Sundry Contributors are not responsible for
- * the consequences of using this software.
- *
- * TODO: does triangular noise, could do local shaping
- *
- */
-
-#include <stdlib.h>
-#include <math.h>
-#include "st_i.h"
-
-typedef struct mask {
- double amount;
-} * mask_t;
-
-assert_static(sizeof(struct mask) <= ST_MAX_EFFECT_PRIVSIZE,
- /* else */ mask_PRIVSIZE_too_big);
-
-static int st_mask_getopts(eff_t effp, int n, char * * argv)
-{
- mask_t mask = (mask_t) effp->priv;
-
- if (n > 1) {
- st_fail(effp->h->usage);
- return ST_EOF;
- }
-
- mask->amount = sqrt(2); /* M_SQRT2 missing in some places */ /* Default to half a bit. */
- if (n == 1) {
- double amount;
- char dummy;
- int scanned = sscanf(*argv, "%lf %c", &amount, &dummy);
- if (scanned == 1 && amount > 0)
- mask->amount *= amount;
- else {
- st_fail(effp->h->usage);
- return ST_EOF;
- }
- }
-
- return ST_SUCCESS;
-}
-
-static int st_mask_start(eff_t effp)
-{
- mask_t mask = (mask_t) effp->priv;
-
- if (effp->outinfo.encoding == ST_ENCODING_ULAW ||
- effp->outinfo.encoding == ST_ENCODING_ALAW) {
- mask->amount *= 16;
- return ST_SUCCESS;
- } else if (effp->outinfo.size == ST_SIZE_BYTE) {
- mask->amount *= 256;
- return ST_SUCCESS;
- } else if (effp->outinfo.size == ST_SIZE_16BIT)
- return ST_SUCCESS;
- else if (effp->outinfo.size == ST_SIZE_24BIT) {
- mask->amount /= 256;
- return ST_SUCCESS;
- } else if (effp->outinfo.size == ST_SIZE_64BIT) {
- mask->amount /= 16384;
- return ST_SUCCESS;
- }
-
- st_fail("Invalid size %d", effp->outinfo.size);
- return ST_EOF;
-}
-
-/* FIXME: Scale noise more sensibly for sizes >= 24 bits */
-static int st_mask_flow(eff_t effp, const st_sample_t * ibuf,
- st_sample_t * obuf, st_size_t * isamp, st_size_t * osamp)
-{
- mask_t mask = (mask_t)effp->priv;
- st_size_t len = min(*isamp, *osamp);
-
- *isamp = *osamp = len;
- while (len--) { /* 16 signed bits of triangular noise */
- int tri16 = ((rand() % 32768L) + (rand() % 32768L)) - 32767;
- double l = *ibuf++ + tri16 * mask->amount;
- *obuf++ = ST_ROUND_CLIP_COUNT(l, effp->clips);
- }
- return ST_SUCCESS;
-}
-
-static st_effect_t st_mask_effect = {
- "mask",
- "Usage: mask [amount]",
- ST_EFF_MCHAN,
- st_mask_getopts,
- st_effect_nothing,
- st_mask_flow,
- st_effect_nothing_drain,
- st_effect_nothing,
- st_effect_nothing
-};
-
-st_effect_t const * st_mask_effect_fn(void)
-{
- return &st_mask_effect;
-}
-
-static st_effect_t st_dither_effect = {
- "dither",
- "Usage: dither [amount]",
- ST_EFF_MCHAN,
- st_mask_getopts,
- st_mask_start,
- st_mask_flow,
- st_effect_nothing_drain,
- st_effect_nothing,
- st_effect_nothing
-};
-
-st_effect_t const * st_dither_effect_fn(void)
-{
- return &st_dither_effect;
-}
--- /dev/null
+++ b/src/mixer.c
@@ -1,0 +1,569 @@
+/*
+ * July 5, 1991
+ * Copyright 1991 Lance Norskog And Sundry Contributors
+ * This source code is freely redistributable and may be used for
+ * any purpose. This copyright notice must be maintained.
+ * Lance Norskog And Sundry Contributors are not responsible for
+ * the consequences of using this software.
+ *
+ * Channel duplication code by Graeme W. Gill - 93/5/18
+ * General-purpose panning by Geoffrey H. Kuenning -- 2000/11/28
+ */
+
+/*
+ * Sound Tools stereo/quad -> mono mixdown effect file.
+ * and mono/stereo -> stereo/quad channel duplication.
+ */
+
+#include "st_i.h"
+#include <ctype.h>
+#include <string.h>
+#include <stdlib.h>
+
+static st_effect_t st_mixer_effect;
+
+typedef struct mixerstuff {
+ /* How to generate each output channel. sources[i][j] */
+ /* represents the fraction of channel i that should be passed */
+ /* through to channel j on output, and so forth. Channel 0 is */
+ /* left front, channel 1 is right front, and 2 and 3 are left */
+ /* and right rear, respectively. (GHK) */
+ double sources[4][4];
+ int num_pans;
+ int mix; /* How are we mixing it? */
+} *mixer_t;
+
+/* MIX_CENTER is shorthand to mix channels together at 50% each */
+#define MIX_CENTER 0
+#define MIX_LEFT 1
+#define MIX_RIGHT 2
+#define MIX_FRONT 3
+#define MIX_BACK 4
+#define MIX_SPECIFIED 5
+#define MIX_LEFT_FRONT 6
+#define MIX_RIGHT_FRONT 7
+#define MIX_LEFT_BACK 8
+#define MIX_RIGHT_BACK 9
+
+
+/*
+ * Process options
+ */
+static int getopts(eff_t effp, int n, char **argv)
+{
+ mixer_t mixer = (mixer_t) effp->priv;
+ double* pans = &mixer->sources[0][0];
+ int i;
+
+ for (i = 0; i < 16; i++)
+ pans[i] = 0.0;
+ mixer->mix = MIX_CENTER;
+ mixer->num_pans = 0;
+
+ /* Parse parameters. Since we don't yet know the number of */
+ /* input and output channels, we'll record the information for */
+ /* later. */
+ if (n == 1) {
+ if(!strcmp(argv[0], "-l"))
+ mixer->mix = MIX_LEFT;
+ else if (!strcmp(argv[0], "-r"))
+ mixer->mix = MIX_RIGHT;
+ else if (!strcmp(argv[0], "-f"))
+ mixer->mix = MIX_FRONT;
+ else if (!strcmp(argv[0], "-b"))
+ mixer->mix = MIX_BACK;
+ else if (!strcmp(argv[0], "-1"))
+ mixer->mix = MIX_LEFT_FRONT;
+ else if (!strcmp(argv[0], "-2"))
+ mixer->mix = MIX_RIGHT_FRONT;
+ else if (!strcmp(argv[0], "-3"))
+ mixer->mix = MIX_LEFT_BACK;
+ else if (!strcmp(argv[0], "-4"))
+ mixer->mix = MIX_RIGHT_BACK;
+ else if (argv[0][0] == '-' && !isdigit((int)argv[0][1])
+ && argv[0][1] != '.') {
+ st_fail(st_mixer_effect.usage);
+ return (ST_EOF);
+ }
+ else {
+ int commas;
+ char *s;
+ mixer->mix = MIX_SPECIFIED;
+ pans[0] = atof(argv[0]);
+ for (s = argv[0], commas = 0; *s; ++s) {
+ if (*s == ',') {
+ ++commas;
+ if (commas >= 16) {
+ st_fail("mixer can only take up to 16 pan values");
+ return (ST_EOF);
+ }
+ pans[commas] = atof(s+1);
+ }
+ }
+ mixer->num_pans = commas + 1;
+ }
+ }
+ else if (n == 0) {
+ mixer->mix = MIX_CENTER;
+ }
+ else {
+ st_fail(st_mixer_effect.usage);
+ return ST_EOF;
+ }
+
+ return (ST_SUCCESS);
+}
+
+/*
+ * Start processing
+ */
+static int start(eff_t effp)
+{
+ /*
+ Hmmm, this is tricky. Lemme think:
+ channel orders are [0][0],[0][1], etc.
+ i.e., 0->0, 0->1, 0->2, 0->3, 1->0, 1->1, ...
+ trailing zeros are omitted
+ L/R balance is x= -1 for left only, 1 for right only
+ 1->1 channel effects:
+ changing volume by x is x,0,0,0
+ 1->2 channel effects:
+ duplicating everywhere is 1,1,0,0
+ 1->4 channel effects:
+ duplicating everywhere is 1,1,1,1
+ 2->1 channel effects:
+ left only is 1,0,0,0 0,0,0,0
+ right only is 0,0,0,0 1,0,0,0
+ left+right is 0.5,0,0,0 0.5,0,0,0
+ left-right is 1,0,0,0 -1,0,0,0
+ 2->2 channel effects:
+ L/R balance can be done several ways. The standard stereo
+ way is both the easiest and the most sensible:
+ min(1-x,1),0,0,0 0,min(1+x,1),0,0
+ left to both is 1,1,0,0
+ right to both is 0,0,0,0 1,1,0,0
+ left+right to both is 0.5,0.5,0,0 0.5,0.5,0,0
+ left-right to both is 1,1,0,0 -1,-1,0,0
+ left-right to left, right-left to right is 1,-1,0,0 -1,1,0,0
+ 2->4 channel effects:
+ front duplicated into rear is 1,0,1,0 0,1,0,1
+ front swapped into rear (why?) is 1,0,0,1 0,1,1,0
+ front put into rear as mono (why?) is 1,0,0.5,0.5 0,1,0.5,0.5
+ 4->1 channel effects:
+ left front only is 1,0,0,0
+ left only is 0.5,0,0,0 0,0,0,0 0.5,0,0,0
+ etc.
+ 4->2 channel effects:
+ merge front/back is 0.5,0,0,0 0,0.5,0,0 0.5,0,0,0 0,0.5,0,0
+ selections similar to above
+ 4->4 channel effects:
+ left front to all is 1,1,1,1 0,0,0,0
+ right front to all is 0,0,0,0 1,1,1,1
+ left f/r to all f/r is 1,1,0,0 0,0,0,0 0,0,1,1 0,0,0,0
+ etc.
+
+ The interesting cases from above (deserving of abbreviations of
+ less than 16 numbers) are:
+
+ 0) n->n volume change (1 number)
+ 1) 1->n duplication (0 numbers)
+ 2) 2->1 mixdown (0 or 2 numbers)
+ 3) 2->2 balance (1 number)
+ 4) 2->2 fully general mix (4 numbers)
+ 5) 2->4 duplication (0 numbers)
+ 6) 4->1 mixdown (0 or 4 numbers)
+ 7) 4->2 mixdown (0, or 2 numbers)
+ 8) 4->4 balance (1 or 2 numbers)
+
+ The above has one ambiguity: n->n volume change conflicts with
+ n->n balance for n != 1. In such a case, we'll prefer
+ balance, since there is already a volume effect in vol.c.
+
+ GHK 2000/11/28
+ */
+ mixer_t mixer = (mixer_t) effp->priv;
+ double pans[16];
+ int i, j;
+ int ichan, ochan;
+
+ for (i = 0; i < 16; i++)
+ pans[i] = ((double*)&mixer->sources[0][0])[i];
+
+ ichan = effp->ininfo.channels;
+ ochan = effp->outinfo.channels;
+ if (ochan == -1) {
+ st_fail("Output must have known number of channels to use mixer effect");
+ return(ST_EOF);
+ }
+
+ if ((ichan != 1 && ichan != 2 && ichan != 4)
+ || (ochan != 1 && ochan != 2 && ochan != 4)) {
+ st_fail("Can't average %d channels into %d channels",
+ ichan, ochan);
+ return (ST_EOF);
+ }
+
+ /* Handle the special-case flags */
+ switch (mixer->mix) {
+ case MIX_CENTER:
+ if (ichan == ochan) {
+ st_fail("Output must have different number of channels to use mixer effect");
+ return(ST_EOF);
+ }
+ break; /* Code below will handle this case */
+ case MIX_LEFT:
+ if (ichan == 2 && ochan == 1)
+ {
+ pans[0] = 1.0;
+ pans[1] = 0.0;
+ mixer->num_pans = 2;
+ }
+ else if (ichan == 4 && ochan == 1)
+ {
+ pans[0] = 0.5;
+ pans[1] = 0.0;
+ pans[2] = 0.5;
+ pans[3] = 0.0;
+ mixer->num_pans = 4;
+ }
+ else
+ {
+ st_fail("Can't average %d channels into %d channels",
+ ichan, ochan);
+ return ST_EOF;
+ }
+ break;
+ case MIX_RIGHT:
+ if (ichan == 2 && ochan == 1)
+ {
+ pans[0] = 0.0;
+ pans[1] = 1.0;
+ mixer->num_pans = 2;
+ }
+ else if (ichan == 4 && ochan == 1)
+ {
+ pans[0] = 0.0;
+ pans[1] = 0.5;
+ pans[2] = 0.0;
+ pans[3] = 0.5;
+ mixer->num_pans = 4;
+ }
+ else
+ {
+ st_fail("Can't average %d channels into %d channels",
+ ichan, ochan);
+ return ST_EOF;
+ }
+ break;
+ case MIX_FRONT:
+ if (ichan == 4 && ochan == 2)
+ {
+ pans[0] = 1.0;
+ pans[1] = 0.0;
+ mixer->num_pans = 2;
+ }
+ else
+ {
+ st_fail("-f option requires 4 channels input and 2 channel output");
+ return ST_EOF;
+ }
+ break;
+ case MIX_BACK:
+ if (ichan == 4 && ochan == 2)
+ {
+ pans[0] = 0.0;
+ pans[1] = 1.0;
+ mixer->num_pans = 2;
+ }
+ else
+ {
+ st_fail("-b option requires 4 channels input and 2 channel output");
+ return ST_EOF;
+ }
+ break;
+ case MIX_LEFT_FRONT:
+ if (ichan == 2 && ochan == 1)
+ {
+ pans[0] = 1.0;
+ pans[1] = 0.0;
+ mixer->num_pans = 2;
+ }
+ else if (ichan == 4 && ochan == 1)
+ {
+ pans[0] = 1.0;
+ pans[1] = 0.0;
+ pans[2] = 0.0;
+ pans[3] = 0.0;
+ mixer->num_pans = 4;
+ }
+ else
+ {
+ st_fail("-1 option requires 4 channels input and 1 channel output");
+ return ST_EOF;
+ }
+ break;
+ case MIX_RIGHT_FRONT:
+ if (ichan == 2 && ochan == 1)
+ {
+ pans[0] = 0.0;
+ pans[1] = 1.0;
+ mixer->num_pans = 2;
+ }
+ else if (ichan == 4 && ochan == 1)
+ {
+ pans[0] = 0.0;
+ pans[1] = 1.0;
+ pans[2] = 0.0;
+ pans[3] = 0.0;
+ mixer->num_pans = 4;
+ }
+ else
+ {
+ st_fail("-2 option requires 4 channels input and 1 channel output");
+ return ST_EOF;
+ }
+ break;
+ case MIX_LEFT_BACK:
+ if (ichan == 4 && ochan == 1)
+ {
+ pans[0] = 0.0;
+ pans[1] = 0.0;
+ pans[2] = 1.0;
+ pans[3] = 0.0;
+ mixer->num_pans = 4;
+ }
+ else
+ {
+ st_fail("-3 option requires 4 channels input and 1 channel output");
+ return ST_EOF;
+ }
+ case MIX_RIGHT_BACK:
+ if (ichan == 4 && ochan == 1)
+ {
+ pans[0] = 0.0;
+ pans[1] = 0.0;
+ pans[2] = 0.0;
+ pans[3] = 1.0;
+ mixer->num_pans = 4;
+ }
+ else
+ {
+ st_fail("-4 option requires 4 channels input and 1 channel output");
+ return ST_EOF;
+ }
+
+ case MIX_SPECIFIED:
+ break;
+ default:
+ st_fail("Unknown mix option in average effect");
+ return ST_EOF;
+ }
+
+ /* If number of pans is 4 or less then its a shorthand
+ * representation. If user specified it, then we have
+ * garbage in our sources[][] array. Need to clear that
+ * now that all data is stored in pans[] array.
+ */
+ if (mixer->num_pans <= 4)
+ {
+ for (i = 0; i < ichan; i++)
+ {
+ for (j = 0; j < ochan; j++)
+ {
+ mixer->sources[i][j] = 0;
+ }
+ }
+ }
+
+ /* If the number of pans given is 4 or fewer, handle the special */
+ /* cases listed in the comments above. The code is lengthy but */
+ /* straightforward. */
+ if (mixer->num_pans == 0) {
+ /* CASE 1 */
+ if (ichan == 1 && ochan > ichan) {
+ mixer->sources[0][0] = 1.0;
+ mixer->sources[0][1] = 1.0;
+ mixer->sources[0][2] = 1.0;
+ mixer->sources[0][3] = 1.0;
+ }
+ /* CASE 2 */
+ else if (ichan == 2 && ochan == 1) {
+ mixer->sources[0][0] = 0.5;
+ mixer->sources[1][0] = 0.5;
+ }
+ /* CASE 5 */
+ else if (ichan == 2 && ochan == 4) {
+ mixer->sources[0][0] = 1.0;
+ mixer->sources[0][2] = 1.0;
+ mixer->sources[1][1] = 1.0;
+ mixer->sources[1][3] = 1.0;
+ }
+ /* CASE 6 */
+ else if (ichan == 4 && ochan == 1) {
+ mixer->sources[0][0] = 0.25;
+ mixer->sources[1][0] = 0.25;
+ mixer->sources[2][0] = 0.25;
+ mixer->sources[3][0] = 0.25;
+ }
+ /* CASE 7 */
+ else if (ichan == 4 && ochan == 2) {
+ mixer->sources[0][0] = 0.5;
+ mixer->sources[1][1] = 0.5;
+ mixer->sources[2][0] = 0.5;
+ mixer->sources[3][1] = 0.5;
+ }
+ else {
+ st_fail("You must specify at least one mix level when using mixer with an unusual number of channels.");
+ return(ST_EOF);
+ }
+ }
+ else if (mixer->num_pans == 1) {
+ /* Might be volume change or balance change */
+ /* CASE 3 and CASE 8 */
+ if ((ichan == 2 || ichan == 4) && ichan == ochan) {
+ /* -1 is left only, 1 is right only */
+ if (pans[0] <= 0.0) {
+ mixer->sources[1][1] = pans[0] + 1.0;
+ if (mixer->sources[1][1] < 0.0)
+ mixer->sources[1][1] = 0.0;
+ mixer->sources[0][0] = 1.0;
+ }
+ else {
+ mixer->sources[0][0] = 1.0 - pans[0];
+ if (mixer->sources[0][0] < 0.0)
+ mixer->sources[0][0] = 0.0;
+ mixer->sources[1][1] = 1.0;
+ }
+ if (ichan == 4) {
+ mixer->sources[2][2] = mixer->sources[0][0];
+ mixer->sources[3][3] = mixer->sources[1][1];
+ }
+ }
+ else
+ {
+ st_fail("Invalid options specified to mixer while not mixing");
+ return ST_EOF;
+ }
+ }
+ else if (mixer->num_pans == 2) {
+ /* CASE 2 */
+ if (ichan == 2 && ochan == 1) {
+ mixer->sources[0][0] = pans[0];
+ mixer->sources[1][0] = pans[1];
+ }
+ /* CASE 7 */
+ else if (ichan == 4 && ochan == 2) {
+ mixer->sources[0][0] = pans[0];
+ mixer->sources[1][1] = pans[0];
+ mixer->sources[2][0] = pans[1];
+ mixer->sources[3][1] = pans[1];
+ }
+ /* CASE 8 */
+ else if (ichan == 4 && ochan == 4) {
+ /* pans[0] is front -> front, pans[1] is for back */
+ mixer->sources[0][0] = pans[0];
+ mixer->sources[1][1] = pans[0];
+ mixer->sources[2][2] = pans[1];
+ mixer->sources[3][3] = pans[1];
+ }
+ else
+ {
+ st_fail("Invalid options specified to mixer for this channel combination");
+ return ST_EOF;
+ }
+ }
+ else if (mixer->num_pans == 4) {
+ /* CASE 4 */
+ if (ichan == 2 && ochan == 2) {
+ /* Shorthand for 2-channel case */
+ mixer->sources[0][0] = pans[0];
+ mixer->sources[0][1] = pans[1];
+ mixer->sources[1][0] = pans[2];
+ mixer->sources[1][1] = pans[3];
+ }
+ /* CASE 6 */
+ else if (ichan == 4 && ochan == 1) {
+ mixer->sources[0][0] = pans[0];
+ mixer->sources[1][0] = pans[1];
+ mixer->sources[2][0] = pans[2];
+ mixer->sources[3][0] = pans[3];
+ }
+ else
+ {
+ st_fail("Invalid options specified to mixer for this channel combination");
+ return ST_EOF;
+ }
+ }
+ else
+ {
+ st_fail("Invalid options specified to mixer while not mixing");
+ return ST_EOF;
+ }
+
+ return ST_SUCCESS;
+}
+
+/*
+ * Process either isamp or osamp samples, whichever is smaller.
+ */
+
+static int flow(eff_t effp, const st_sample_t *ibuf, st_sample_t *obuf,
+ st_size_t *isamp, st_size_t *osamp)
+{
+ mixer_t mixer = (mixer_t) effp->priv;
+ st_size_t len, done;
+ int ichan, ochan;
+ int i, j;
+ double samp;
+
+ ichan = effp->ininfo.channels;
+ ochan = effp->outinfo.channels;
+ len = *isamp / ichan;
+ if (len > *osamp / ochan)
+ len = *osamp / ochan;
+ for (done = 0; done < len; done++, ibuf += ichan, obuf += ochan) {
+ for (j = 0; j < ochan; j++) {
+ samp = 0.0;
+ for (i = 0; i < ichan; i++)
+ samp += ibuf[i] * mixer->sources[i][j];
+ ST_SAMPLE_CLIP_COUNT(samp, effp->clips);
+ obuf[j] = samp;
+ }
+ }
+ *isamp = len * ichan;
+ *osamp = len * ochan;
+ return (ST_SUCCESS);
+}
+
+st_effect_t const * st_mixer_effect_fn(void)
+{
+ static st_effect_t driver = {
+ "mixer",
+ "Usage: mixer [ -l | -r | -f | -b | -1 | -2 | -3 | -4 | n,n,n...,n ]",
+ ST_EFF_MCHAN | ST_EFF_CHAN,
+ getopts, start, flow, 0, 0, 0
+ };
+ return &driver;
+}
+
+st_effect_t const * st_avg_effect_fn(void)
+{
+ static st_effect_t driver = {
+ "avg",
+ "Usage: avg [ -l | -r | -f | -b | -1 | -2 | -3 | -4 | n,n,n...,n ]",
+ ST_EFF_MCHAN | ST_EFF_CHAN | ST_EFF_DEPRECATED,
+ getopts, start, flow, 0, 0, 0
+ };
+ return &driver;
+}
+
+st_effect_t const * st_pick_effect_fn(void)
+{
+ static st_effect_t driver = {
+ "pick",
+ "Usage: pick [ -l | -r | -f | -b | -1 | -2 | -3 | -4 | n,n,n...,n ]",
+ ST_EFF_MCHAN | ST_EFF_CHAN | ST_EFF_DEPRECATED,
+ getopts, start, flow, 0, 0, 0
+ };
+ return &driver;
+}
--- a/src/rate.c
+++ b/src/rate.c
@@ -17,7 +17,7 @@
static st_effect_t st_rate_effect = {
"rate",
"Usage: Rate effect takes no options",
- ST_EFF_RATE,
+ ST_EFF_RATE | ST_EFF_DEPRECATED,
st_resample_getopts,
st_resample_start,
st_resample_flow,
--- a/src/skeleff.c
+++ b/src/skeleff.c
@@ -49,7 +49,6 @@
/*
* Prepare processing.
* Do all initializations.
- * If there's nothing to do, use st_effect_nothing instead.
*/
static int start(eff_t effp)
{
@@ -97,7 +96,6 @@
/*
* Drain out remaining samples if the effect generates any.
- * If there's nothing to do, use st_effect_nothing_drain instead.
*/
static int drain(eff_t effp, st_sample_t *obuf, st_size_t *osamp)
{
@@ -111,7 +109,6 @@
/*
* Do anything required when you stop reading samples.
- * If there's nothing to do, use st_effect_nothing instead.
*/
static int stop(eff_t effp)
{
@@ -121,7 +118,6 @@
/*
* Do anything required when you delete an effect.
* (free allocated memory, etc.)
- * If there's nothing to do, use st_effect_nothing instead.
*/
static int delete(eff_t effp)
{
@@ -131,8 +127,9 @@
/*
* Effect descriptor.
- * If one of the methods does nothing, use the relevant
- * st_effect_nothing* method.
+ * If no specific processing is needed for any of
+ * the 6 functions, then the function can be deleted
+ * and 0 used in place of the its name below.
*/
static st_effect_t st_skel_effect = {
"skel",
--- a/src/sox.c
+++ b/src/sox.c
@@ -681,7 +681,7 @@
*/
static void process(void) {
- int e, flowstatus;
+ int e, flowstatus = 0;
size_t current_input = 0;
st_size_t s, f;
st_ssize_t ilen[MAX_INPUT_FILES];
@@ -963,200 +963,130 @@
static void parse_effects(int argc, char **argv)
{
int argc_effect;
- int effect_rc;
- nuser_effects = 0;
+ for (nuser_effects = 0; optind < argc; ++nuser_effects) {
+ struct st_effect * e = &user_efftab[nuser_effects];
+ int (*getopts)(eff_t effp, int argc, char *argv[]);
- while (optind < argc) {
if (nuser_effects >= MAX_USER_EFF) {
- st_fail("too many effects specified (at most %d allowed)", MAX_USER_EFF);
+ st_fail("too many effects specified (at most %i allowed)", MAX_USER_EFF);
exit(1);
}
- argc_effect = st_geteffect_opt(&user_efftab[nuser_effects],
- argc - optind, &argv[optind]);
-
+ argc_effect = st_geteffect_opt(e, argc - optind, &argv[optind]);
if (argc_effect == ST_EOF) {
st_fail("Effect `%s' does not exist!", argv[optind]);
exit(1);
}
- /* Skip past effect name */
- optind++;
-
- user_efftab[nuser_effects].globalinfo = &globalinfo;
- effect_rc = (*user_efftab[nuser_effects].h->getopts)
- (&user_efftab[nuser_effects], argc_effect, &argv[optind]);
-
- if (effect_rc == ST_EOF)
+ optind++; /* Skip past effect name */
+ e->globalinfo = &globalinfo;
+ getopts = e->h->getopts? e->h->getopts : st_effect_nothing_getopts;
+ if (getopts(e, argc_effect, &argv[optind]) == ST_EOF)
exit(2);
- /* Skip past the effect arguments */
- optind += argc_effect;
- nuser_effects++;
+ optind += argc_effect; /* Skip past the effect arguments */
}
}
-/*
- * If no effect given, decide what it should be.
- * Smart ruleset for multiple effects in sequence.
- * Puts user-specified effect in right place.
- */
-static void build_effects_table(void)
-{
- int i;
- int needchan = 0, needrate = 0, haschan = 0, hasrate = 0;
- int effects_mask = 0;
- int status;
+static void add_effect(int * effects_mask)
+{
+ struct st_effect * e = &efftab[neffects];
- needrate = (file_desc[0]->signal.rate != ofile->signal.rate);
- needchan = (file_desc[0]->signal.channels != ofile->signal.channels);
+ /* Copy format info to effect table */
+ *effects_mask =
+ st_updateeffect(e, &file_desc[0]->signal, &ofile->signal, *effects_mask);
- for (i = 0; i < nuser_effects; i++) {
- if (user_efftab[i].h->flags & ST_EFF_CHAN)
- haschan++;
- if (user_efftab[i].h->flags & ST_EFF_RATE)
- hasrate++;
- }
+ /* If this effect can't handle multiple channels then account for this. */
+ if (e->ininfo.channels > 1 && !(e->h->flags & ST_EFF_MCHAN))
+ memcpy(&efftabR[neffects], e, sizeof(*e));
- if (haschan > 1) {
- st_fail("Cannot specify multiple effects that modify number of channels");
- exit(2);
- }
- if (hasrate > 1)
- st_report("Cannot specify multiple effects that change sample rate");
+ st_report("Effects chain:%10s %-6s %uHz", e->name,
+ e->ininfo.channels < 2? "mono" :
+ (e->h->flags & ST_EFF_MCHAN)? "multi" : "stereo", e->ininfo.rate);
- /* --------- add the effects ------------------------ */
+ ++neffects;
+}
- /* efftab[0] is always the input stream and always exists */
- neffects = 1;
+static void add_default_effect(char const * name, int * effects_mask)
+{
+ struct st_effect * e = &efftab[neffects];
+ int (*getopts)(eff_t effp, int argc, char *argv[]);
- /* If reducing channels then its faster to run all effects
- * after the avg effect.
- */
- if (needchan && !(haschan) &&
- (file_desc[0]->signal.channels > ofile->signal.channels))
- {
- /* Find effect and update initial pointers */
- st_geteffect(&efftab[neffects], "avg");
+ /* Find effect and update initial pointers */
+ st_geteffect(e, name);
- /* give default opts for added effects */
- efftab[neffects].globalinfo = &globalinfo;
- status = (* efftab[neffects].h->getopts)(&efftab[neffects], (int)0,
- (char **)0);
+ /* Set up & give default opts for added effects */
+ e->globalinfo = &globalinfo;
+ getopts = e->h->getopts? e->h->getopts : st_effect_nothing_getopts;
+ if (getopts(e, 0, NULL) == ST_EOF)
+ exit(2);
- if (status == ST_EOF)
- exit(2);
+ add_effect(effects_mask);
+}
- /* Copy format info to effect table */
- effects_mask = st_updateeffect(&efftab[neffects],
- &file_desc[0]->signal,
- &ofile->signal,
- effects_mask);
+/* If needed effects are not given, auto-add at (performance) optimal point.
+ */
+static void build_effects_table(void)
+{
+ int i;
+ int effects_mask = 0;
+ st_bool need_rate = file_desc[0]->signal.rate != ofile->signal.rate;
+ st_bool need_chan = file_desc[0]->signal.channels != ofile->signal.channels;
- neffects++;
- }
+ { /* Check if we have to add effects to change rate/chans or if the
+ user has specified effects to do this, in which case, check if
+ too many rate/channel-changing effects have been specified: */
+ int user_chan_effects = 0, user_rate_effects = 0;
- /* If reducing the number of samples, it's faster to run all effects
- * after the resample effect.
- */
- if (needrate && !(hasrate) &&
- (file_desc[0]->signal.rate > ofile->signal.rate))
- {
- st_geteffect(&efftab[neffects], "resample");
-
- /* set up & give default opts for added effects */
- efftab[neffects].globalinfo = &globalinfo;
- status = (* efftab[neffects].h->getopts)(&efftab[neffects], (int)0,
- (char **)0);
-
- if (status == ST_EOF)
- exit(2);
-
- /* Copy format info to effect table */
- effects_mask = st_updateeffect(&efftab[neffects],
- &file_desc[0]->signal,
- &ofile->signal,
- effects_mask);
-
- /* Rate can't handle multiple channels so be sure and
- * account for that.
- */
- if (efftab[neffects].ininfo.channels > 1)
- memcpy(&efftabR[neffects], &efftab[neffects],
- sizeof(struct st_effect));
-
- neffects++;
- }
-
- /* Copy over user specified effects into real efftab */
- for (i = 0; i < nuser_effects; i++) {
- memcpy(&efftab[neffects], &user_efftab[i], sizeof(struct st_effect));
-
- /* Copy format info to effect table */
- effects_mask = st_updateeffect(&efftab[neffects],
- &file_desc[0]->signal,
- &ofile->signal,
- effects_mask);
-
- /* If this effect can't handle multiple channels then
- * account for this. */
- if ((efftab[neffects].ininfo.channels > 1) &&
- !(efftab[neffects].h->flags & ST_EFF_MCHAN))
- memcpy(&efftabR[neffects], &efftab[neffects],
- sizeof(struct st_effect));
-
- neffects++;
- }
-
- /* If rate effect hasn't been added by now then add it here.
- * Check adding rate before avg because it's faster to run
- * rate on less channels then more.
- */
- if (needrate && !(effects_mask & ST_EFF_RATE)) {
- st_geteffect(&efftab[neffects], "resample");
-
- /* Set up & give default opts for added effect */
- efftab[neffects].globalinfo = &globalinfo;
- status = (* efftab[neffects].h->getopts)(&efftab[neffects], 0, NULL);
-
- if (status == ST_EOF)
+ for (i = 0; i < nuser_effects; i++) {
+ if (user_efftab[i].h->flags & ST_EFF_CHAN) {
+ need_chan = st_false;
+ ++user_chan_effects;
+ }
+ if (user_efftab[i].h->flags & ST_EFF_RATE) {
+ need_rate = st_false;
+ ++user_rate_effects;
+ }
+ }
+ if (user_chan_effects > 1) {
+ st_fail("Cannot specify multiple effects that change number of channels");
exit(2);
-
- /* Copy format info to effect table */
- effects_mask = st_updateeffect(&efftab[neffects],
- &file_desc[0]->signal,
- &ofile->signal,
- effects_mask);
-
- /* Rate can't handle multiple channels so be sure and
- * account for that. */
- if (efftab[neffects].ininfo.channels > 1)
- memcpy(&efftabR[neffects], &efftab[neffects],
- sizeof(struct st_effect));
-
- neffects++;
+ }
+ if (user_rate_effects > 1)
+ st_report("Cannot specify multiple effects that change sample rate");
+ /* FIXME: exit here or add comment as to why not */
}
- /* If we haven't added avg effect yet then do it now.
- */
- if (needchan && !(effects_mask & ST_EFF_CHAN)) {
- st_geteffect(&efftab[neffects], "avg");
+ /* --------- add the effects ------------------------ */
- /* set up & give default opts for added effects */
- efftab[neffects].globalinfo = &globalinfo;
- status = (* efftab[neffects].h->getopts)(&efftab[neffects], 0, NULL);
- if (status == ST_EOF)
- exit(2);
+ /* efftab[0] is always the input stream and always exists */
+ neffects = 1;
- /* Copy format info to effect table */
- effects_mask = st_updateeffect(&efftab[neffects],
- &file_desc[0]->signal,
- &ofile->signal,
- effects_mask);
-
- neffects++;
+ /* If reducing channels, it's faster to do so before all other effects: */
+ if (need_chan && file_desc[0]->signal.channels > ofile->signal.channels) {
+ add_default_effect("mixer", &effects_mask);
+ need_chan = st_false;
}
+ /* If reducing rate, it's faster to do so before all other effects
+ * (except reducing channels): */
+ if (need_rate && file_desc[0]->signal.rate > ofile->signal.rate) {
+ add_default_effect("resample", &effects_mask);
+ need_rate = st_false;
+ }
+ /* Copy user specified effects into the real efftab */
+ for (i = 0; i < nuser_effects; i++) {
+ memcpy(&efftab[neffects], &user_efftab[i], sizeof(efftab[0]));
+ add_effect(&effects_mask);
+ }
+ /* If rate/channels-changing effects are needed but haven't yet been
+ * added, then do it here. Change rate before channels because it's
+ * faster to change rate on a smaller # of channels and # of channels
+ * can not be reduced, only increased, at this point. */
+ if (need_rate)
+ add_default_effect("resample", &effects_mask);
+ if (need_chan)
+ add_default_effect("mixer", &effects_mask);
}
static int start_all_effects(void)
@@ -1164,16 +1094,17 @@
int e, ret = ST_SUCCESS;
for (e = 1; e < neffects; e++) {
+ int (*start)(eff_t effp) =
+ efftab[e].h->start? efftab[e].h->start : st_effect_nothing;
efftab[e].clips = 0;
- if ((ret = (*efftab[e].h->start)(&efftab[e])) == ST_EOF)
+ if ((ret = start(&efftab[e])) == ST_EOF)
break;
if (efftabR[e].name) {
efftabR[e].clips = 0;
- if ((ret = (*efftabR[e].h->start)(&efftabR[e])) != ST_SUCCESS)
+ if ((ret = start(&efftabR[e])) != ST_SUCCESS)
break;
}
}
-
return ret;
}
@@ -1326,6 +1257,8 @@
const st_sample_t *ibuf;
st_sample_t *obuf;
int effstatus, effstatusl, effstatusr;
+ int (*flow)(eff_t, st_sample_t const*, st_sample_t*, st_size_t*, st_size_t*) =
+ efftab[e].h->flow? efftab[e].h->flow : st_effect_nothing_flow;
/* Do not attempt to do any more effect processing during
* user aborts as we may be stuck in an infinite flow loop.
@@ -1348,11 +1281,11 @@
st_debug("pre %s idone=%d, odone=%d", efftab[e].name, idone, odone);
st_debug("pre %s odone1=%d, olen1=%d odone=%d olen=%d", efftab[e].name, efftab[e-1].odone, efftab[e-1].olen, efftab[e].odone, efftab[e].olen);
- effstatus = (* efftab[e].h->flow)(&efftab[e],
- &efftab[e - 1].obuf[efftab[e - 1].odone],
- &efftab[e].obuf[efftab[e].olen],
- (st_size_t *)&idone,
- (st_size_t *)&odone);
+ effstatus = flow(&efftab[e],
+ &efftab[e - 1].obuf[efftab[e - 1].odone],
+ &efftab[e].obuf[efftab[e].olen],
+ (st_size_t *)&idone,
+ (st_size_t *)&odone);
efftab[e - 1].odone += idone;
/* Don't update efftab[e].odone as we didn't consume data */
@@ -1380,16 +1313,16 @@
st_debug("pre %s idone=%d, odone=%d", efftab[e].name, idone, odone);
st_debug("pre %s odone1=%d, olen1=%d odone=%d olen=%d", efftab[e].name, efftab[e - 1].odone, efftab[e - 1].olen, efftab[e].odone, efftab[e].olen);
- effstatusl = (* efftab[e].h->flow)(&efftab[e],
- ibufl, obufl, (st_size_t *)&idonel,
- (st_size_t *)&odonel);
+ effstatusl = flow(&efftab[e],
+ ibufl, obufl, (st_size_t *)&idonel,
+ (st_size_t *)&odonel);
/* right */
idoner = idone / 2; /* odd-length logic */
odoner = odone / 2;
- effstatusr = (* efftabR[e].h->flow)(&efftabR[e],
- ibufr, obufr, (st_size_t *)&idoner,
- (st_size_t *)&odoner);
+ effstatusr = flow(&efftabR[e],
+ ibufr, obufr, (st_size_t *)&idoner,
+ (st_size_t *)&odoner);
obuf = &efftab[e].obuf[efftab[e].olen];
/* This loop implies left and right effect will always output
@@ -1453,11 +1386,12 @@
st_ssize_t i, olen, olenl, olenr;
st_sample_t *obuf;
int rc;
+ int (*drain)(eff_t effp, st_sample_t *obuf, st_size_t *osamp) =
+ efftab[e].h->drain? efftab[e].h->drain : st_effect_nothing_drain;
if (! efftabR[e].name) {
efftab[e].olen = ST_BUFSIZ;
- rc = (* efftab[e].h->drain)(&efftab[e],efftab[e].obuf,
- &efftab[e].olen);
+ rc = drain(&efftab[e],efftab[e].obuf, &efftab[e].olen);
efftab[e].odone = 0;
} else {
int rc_l, rc_r;
@@ -1466,13 +1400,11 @@
/* left */
olenl = olen/2;
- rc_l = (* efftab[e].h->drain)(&efftab[e], obufl,
- (st_size_t *)&olenl);
+ rc_l = drain(&efftab[e], obufl, (st_size_t *)&olenl);
/* right */
olenr = olen/2;
- rc_r = (* efftab[e].h->drain)(&efftabR[e], obufr,
- (st_size_t *)&olenr);
+ rc_r = drain(&efftabR[e], obufr, (st_size_t *)&olenr);
if (rc_l == ST_EOF || rc_r == ST_EOF)
rc = ST_EOF;
@@ -1499,17 +1431,22 @@
for (e = 1; e < neffects; e++) {
st_size_t clips;
- (*efftab[e].h->stop)(&efftab[e]);
+ int (*stop)(eff_t effp) =
+ efftab[e].h->stop? efftab[e].h->stop : st_effect_nothing;
+ int (*delete)(eff_t effp) =
+ efftab[e].h->delete? efftab[e].h->delete : st_effect_nothing;
+
+ stop(&efftab[e]);
clips = efftab[e].clips;
- (*efftab[e].h->delete)(&efftab[e]);
+ delete(&efftab[e]);
+
if (efftabR[e].name) {
- (*efftabR[e].h->stop)(&efftabR[e]);
+ stop(&efftabR[e]);
clips += efftab[e].clips;
- (*efftabR[e].h->delete)(&efftabR[e]);
+ delete(&efftabR[e]);
}
if (clips != 0)
- st_warn("%s clipped %u samples; decrease volume?", efftab[e].name,
- clips);
+ st_warn("%s clipped %u samples; decrease volume?", efftab[e].name, clips);
}
}
@@ -1642,7 +1579,7 @@
"-v, --volume volume input file volume adjustment factor (real number)\n"
"\n");
- printf("Supported file formats:");
+ printf("SUPPORTED FILE FORMATS:");
for (i = 0, formats = 0; st_format_fns[i]; i++) {
char const * const *names = st_format_fns[i]()->names;
while (*names++)
@@ -1659,10 +1596,10 @@
printf(" %s", format_list[i]);
free(format_list);
- printf("\n\nSupported effects:");
+ printf("\n\nSUPPORTED EFFECTS:");
for (i = 0; st_effect_fns[i]; i++) {
e = st_effect_fns[i]();
- if (e && e->name)
+ if (e && e->name && !(e->flags & ST_EFF_DEPRECATED))
printf(" %s", e->name);
}
--- a/src/st.h
+++ b/src/st.h
@@ -324,10 +324,11 @@
extern const char * const st_size_bits_str[];
extern const char * const st_encodings_str[];
-#define ST_EFF_CHAN 1 /* Effect can mix channels up/down */
-#define ST_EFF_RATE 2 /* Effect can alter data rate */
-#define ST_EFF_MCHAN 4 /* Effect can handle multi-channel */
-#define ST_EFF_REPORT 8 /* Effect does not affect the audio */
+#define ST_EFF_CHAN 1 /* Effect can mix channels up/down */
+#define ST_EFF_RATE 2 /* Effect can alter data rate */
+#define ST_EFF_MCHAN 4 /* Effect can handle multi-channel */
+#define ST_EFF_REPORT 8 /* Effect does not affect the audio */
+#define ST_EFF_DEPRECATED 16 /* Effect is living on borrowed time */
/*
* Handler structure for each effect.
@@ -337,7 +338,7 @@
typedef struct
{
- char const *name; /* effect name */
+ char const *name; /* effect name */
char const *usage;
unsigned int flags;
--- a/src/st_i.h
+++ b/src/st_i.h
@@ -340,6 +340,7 @@
extern const st_effect_t *st_lua_effect_fn(void);
extern const st_effect_t *st_mask_effect_fn(void);
extern const st_effect_t *st_mcompand_effect_fn(void);
+extern const st_effect_t *st_mixer_effect_fn(void);
extern const st_effect_t *st_noiseprof_effect_fn(void);
extern const st_effect_t *st_noisered_effect_fn(void);
extern const st_effect_t *st_pad_effect_fn(void);