ref: 50f5368a56be08480b207c77a093cdccf2a814ce
parent: d72859bd2a4f920574f0cd63fe6dacbc5c15db97
author: cbagwell <cbagwell>
date: Thu Oct 5 18:14:47 EDT 2000
Added new Fade effect from Ari Moisio. Added code to ignore MARK chunks in AIFF files if the loop type is set to NoLoop.
--- a/Changelog
+++ b/Changelog
@@ -11,6 +11,8 @@
o Fixed a bug in the mask effect introduced in 12.17. If the libc
version of rand() returned more then 15-bit values then it would
trash your data. Reported by Friedhel Mehnert.
+ o Added a new fade in/out effect from Ari Moisio.
+ o AIFF files now ignore a MARK chunk if the loop type is NoLoop (0).
sox-12.17
---------
--- a/Makefile.dos
+++ b/Makefile.dos
@@ -18,9 +18,9 @@
EOBJ = avg.obj band.obj bandpass.obj breject.obj btrworth.obj chorus.obj \
compand.obj copy.obj cut.obj deemphas.obj echo.obj \
- echos.obj filter.obj flanger.obj highp.obj highpass.obj lowp.obj \
- lowpass.obj map.obj mask.obj phaser.obj pick.obj pitch.obj pan.obj \
- polyphase.obj rate.obj resample.obj reverb.obj \
+ echos.obj fade.obj filter.obj flanger.obj highp.obj highpass.obj \
+ lowp.obj lowpass.obj map.obj mask.obj phaser.obj pick.obj \
+ pitch.obj pan.obj polyphase.obj rate.obj resample.obj reverb.obj \
reverse.obj speed.obj split.obj stat.obj stretch.obj \
swap.obj trim.obj vibro.obj vol.obj
--- a/Makefile.gcc
+++ b/Makefile.gcc
@@ -34,7 +34,7 @@
voc.o wav.o wve.o
EOBJ = avg.o band.o bandpass.o breject.o btrworth.o chorus.o compand.o \
- copy.o cut.o deemphas.o echo.o echos.o filter.o flanger.o \
+ copy.o cut.o deemphas.o echo.o echos.o fade.o filter.o flanger.o \
highp.o highpass.o lowp.o lowpass.o map.o mask.o pan.o \
phaser.o pick.o pitch.o polyphas.o rate.o resample.o reverb.o \
reverse.o speed.o split.o stat.o stretch.o swap.o \
--- a/README
+++ b/README
@@ -47,6 +47,7 @@
o Cut out loop samples
o Add an echo
o Add a sequence of echos
+ o Fade in and out
o Apply a flanger effect
o Apply a high-pass filter
o Apply a low-pass filter
--- a/sox.1
+++ b/sox.1
@@ -62,10 +62,13 @@
.br
\fBdeemph\fR
.br
- \fBecho\fR \fIgain-in gain-out delay decay\fR [ \fIdelay decay ...\fR]
+ \fBecho\fR \fIgain-in gain-out delay decay\fR [ \fIdelay decay ...\fR ]
.br
- \fBechos\fR \fIgain-in gain-out delay decay\fR [ \fIdelay decay ...\fR]
+ \fBechos\fR \fIgain-in gain-out delay decay\fR [ \fIdelay decay ...\fR ]
.br
+ \fBfade\fR [ \fItype\fR ] \fIfade-in-length\fR
+ [ \fIstop-time\fR [ \fIfade-out-length\fR ] ]
+.br
\fBfilter\fR [ \fIlow\fR ]-[ \fIhigh\fR ] [ \fIwindow-len\fR [ \fIbeta\fR ]]
.br
\fBflanger\fR \fIgain-in gain-out delay decay speed\fR < -s | -t >
@@ -650,6 +653,17 @@
Each delay/decay part gives the delay in milliseconds
and the decay (relative to gain-in) of that echo.
Gain-out is the volume of the output.
+.TP
+fade [ \fItype\fR ] \fIfade-in-length\fR
+.TP 10
+ [ \fIstop-time\fR [ \fIfade-out-length\fR ] ]
+Add a fade effect to the beginning, end, or both of the audio data.
+
+For fade-ins, this starts from the first sample and ramps the volume of the audio from 0 to full volume over \fIfade-in-length\fR seconds. Specify 0 seconds if no fade-in is wanted.
+
+For fade-outs, the audio data will be trucated at the stop-time and the volume will be ramped from full volume down to 0 starting at \fIfade-out-length\fR seconds before the \fIstop-time\fR. No fade-out is performed if these options are not specified.
+
+An optional \fItype\fR can be specified to change the type of envelope. Choices are q for quarter of a sinewave, h for half a sinewave, t for linear slope, l for logarithmic, and p for inverted parabola. The default is a linear slope.
.TP 10
filter [ \fIlow\fR ]-[ \fIhigh\fR ] [ \fIwindow-len\fR [ \fIbeta\fR ] ]
Apply a Sinc-windowed lowpass, highpass, or bandpass filter of given
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -46,7 +46,7 @@
sf.o smp.o sndrtool.o sphere.o tx16w.o voc.o wav.o wve.o
EOBJ = avg.o band.o bandpass.o breject.o btrworth.o chorus.o compand.o \
- copy.o cut.o deemphas.o echo.o echos.o filter.o flanger.o \
+ copy.o cut.o deemphas.o echo.o echos.o fade.o filter.o flanger.o \
highp.o highpass.o lowp.o lowpass.o map.o mask.o pan.o \
phaser.o pick.o pitch.o polyphas.o rate.o resample.o reverb.o \
reverse.o speed.o split.o stat.o stretch.o swap.o trim.o \
--- a/src/aiff.c
+++ b/src/aiff.c
@@ -225,11 +225,12 @@
st_readw(ft, &releaseLoopBegin); /* begin marker */
st_readw(ft, &releaseLoopEnd); /* end marker */
- /* At least one known program generates an INST */
- /* block with everything zeroed out (meaning */
- /* no Loops used). In this case it should just */
- /* be ignored. */
- if (sustainLoopBegin == 0 && releaseLoopBegin == 0)
+ /* Required to ignore loops on playback if type
+ * is 0 (NoLoop). The other check is done for
+ * historical reason and is probably not needed.
+ */
+ if ((ft->loops[0].type == 0) ||
+ (sustainLoopBegin == 0 && releaseLoopBegin == 0))
foundinstr = 0;
else
foundinstr = 1;
--- /dev/null
+++ b/src/fade.c
@@ -1,0 +1,321 @@
+/* This code is based in skel.c
+ * Written by Chris Bagwell (cbagwell@sprynet.com) - March 16, 1999
+ * Non-skel parts written by
+ * Ari Moisio <armoi@sci.fi> Aug 29 2000.
+ *
+ * Copyright 1999 Chris Bagwell And Sundry Contributors
+ * This source code is freely redistributable and may be used for
+ * any purpose. This copyright notice must be maintained.
+ * Chris Bagwell And Sundry Contributors are not responsible for
+ * the consequences of using this software.
+ */
+
+/* Time resolution one millisecond */
+#define TIMERES 1000
+/* Fade curves */
+#define FADE_QUARTER 'q' /* Quarter of sine wave, 0 to pi/2 */
+#define FADE_HALF 'h' /* Half of sine wave, pi/2 to 1.5 * pi
+ * scaled so that -1 means no output
+ * and 1 means 0 db attenuation. */
+#define FADE_LOG 'l' /* Logarithmic curve. Fades -100 db
+ * in given time. */
+#define FADE_TRI 't' /* Linear slope. */
+#define FADE_PAR 'p' /* Inverted parabola. */
+
+#include <math.h>
+#include "st.h"
+
+/* Private data for fade file */
+typedef struct fadestuff
+{ /* These are measured as samples */
+ ULONG in_start, in_stop, out_start, out_stop, samplesdone;
+ char in_fadetype, out_fadetype;
+ int endpadwarned;
+} *fade_t;
+
+#define FADE_USAGE "Usage: fade [ type ] fade-in-length [ stop-time [ fade-out-length ] ]\nTimes in seconds.\nFade type one of q, h, t, l or p.\n"
+
+/* prototypes */
+static double fade_gain(ULONG index, ULONG range, char fadetype);
+
+/*
+ * Process options
+ *
+ * Don't do initialization now.
+ * The 'info' fields are not yet filled in.
+ */
+
+int st_fade_getopts(effp, n, argv)
+eff_t effp;
+int n;
+char **argv;
+{
+
+ fade_t fade = (fade_t) effp->priv;
+ double t_double;
+ char t_char;
+ int t_argno;
+
+ if (n < 1 || n > 4)
+ { /* Wrong number of arguments. */
+ st_fail(FADE_USAGE);
+ return(ST_EOF);
+ }
+
+ /* because sample rate is unavailable at this point we convert time
+ * values to seconds * TIMERES and convert them to samples later.
+ */
+
+ if (sscanf(argv[0], "%1[qhltp]", &t_char))
+ {
+ fade->in_fadetype = t_char;
+ fade->out_fadetype = t_char;
+
+ argv++;
+ n--;
+ }
+ else
+ {
+ /* No type given. */
+ fade->in_fadetype = 'l';
+ fade->out_fadetype = 'l';
+ }
+
+ if (sscanf(argv[0], "%lf", &t_double) == 1)
+ { /* got something numerical */
+ /* Don't support in_start. Its job is done by trim effect */
+ fade->in_start = 0;
+ fade->in_stop = t_double * TIMERES;
+ }
+ else
+ { /* unnumeric value */
+ st_fail(FADE_USAGE);
+ return(ST_EOF);
+ } /* endif numeric fade-in */
+
+ fade->out_start = fade->out_stop = 0;
+
+ for (t_argno = 1; t_argno < n && t_argno <= 3; t_argno++)
+ { /* See if there is fade-in/fade-out times/curves specified. */
+ if (sscanf(argv[t_argno], "%lf", &t_double))
+ {
+ if (t_argno == 1)
+ {
+ fade->out_stop = t_double * TIMERES;
+ /* Zero fade-out too */
+ fade->out_start = fade->out_stop;
+ }
+ else
+ {
+ fade->out_start = fade->out_stop - t_double * TIMERES;
+ }
+ }
+ else
+ {
+ st_fail(FADE_USAGE);
+ return(ST_EOF);
+ }
+ } /* End for(t_argno) */
+
+ /* Sanity check for fade times vs total time */
+ if (fade->in_stop > fade->out_start && fade->out_start != 0)
+ { /* Fades too long */
+ st_fail("Fade: End of fade-in should not happen before beginning of fade-out");
+ return(ST_EOF);
+ } /* endif fade time sanity */
+
+ return(ST_SUCCESS);
+}
+
+/*
+ * Prepare processing.
+ * Do all initializations.
+ */
+void st_fade_start(effp)
+eff_t effp;
+{
+ fade_t fade = (fade_t) effp->priv;
+
+ /* converting time values to samples */
+ fade->in_start = fade->in_start * effp->ininfo.rate / TIMERES;
+ fade->in_stop = fade->in_stop * effp->ininfo.rate / TIMERES;
+ fade->out_start = fade->out_start * effp->ininfo.rate / TIMERES;
+ fade->out_stop = fade->out_stop * effp->ininfo.rate / TIMERES;
+
+ /* If lead-in is required it is handled as negative sample numbers */
+ fade->samplesdone = (fade->in_start < 0 ? fade->in_start :0);
+
+ fade->endpadwarned = 0;
+}
+
+/*
+ * Processed signed long samples from ibuf to obuf.
+ * Return number of samples processed.
+ */
+void st_fade_flow(effp, ibuf, obuf, isamp, osamp)
+eff_t effp;
+LONG *ibuf, *obuf;
+int *isamp, *osamp;
+{
+ fade_t fade = (fade_t) effp->priv;
+ /* len is total samples, chcnt counts channels */
+ int len = 0, chcnt = 0, t_output = 0;
+ LONG t_ibuf;
+
+ len = ((*isamp > *osamp) ? *osamp : *isamp);
+
+ *osamp = 0;
+ *isamp = 0;
+
+ for(; len; len--)
+ {
+ t_ibuf = (fade->samplesdone < 0 ? 0 : *ibuf);
+
+ if ((fade->samplesdone >= fade->in_start) &&
+ (fade->out_stop == 0 || fade->samplesdone < fade->out_stop))
+ { /* something to generate output */
+ if (fade->samplesdone < fade->in_stop)
+ { /* fade-in phase, increase gain */
+ *obuf = t_ibuf *
+ fade_gain(fade->samplesdone - fade->in_start,
+ fade->in_stop - fade->in_start,
+ fade->in_fadetype);
+ } /* endif fade-in */
+
+ if (fade->samplesdone >= fade->in_stop &&
+ (fade->out_start == 0 || fade->samplesdone < fade->out_start))
+ { /* steady gain phase */
+ *obuf = t_ibuf;
+ } /* endif steady phase */
+
+ if (fade->out_start != 0 && fade->samplesdone >= fade->out_start)
+ { /* fade-out phase, decrease gain */
+ *obuf = t_ibuf *
+ fade_gain(fade->out_stop - fade->samplesdone,
+ fade->out_stop - fade->out_start,
+ fade->out_fadetype);
+ } /* endif fade-out */
+
+ t_output = 1;
+ }
+ else
+ { /* No output generated */
+ t_output = 0;
+ } /* endif something to output */
+
+ if (fade->samplesdone >= 0 )
+ { /* Something to input */
+ *isamp += 1;
+ ibuf++;
+ } /* endif something accepted as input */
+
+ if (t_output)
+ { /* Output generated, update pointers and counters */
+ obuf++;
+ *osamp += 1;
+ } /* endif t_output */
+
+ /* Process next channel */
+ chcnt++;
+ if (chcnt >= effp->ininfo.channels)
+ { /* all channels of this sample processed */
+ chcnt = 0;
+ fade->samplesdone += 1;
+ } /* endif all channels */
+ } /* endfor */
+}
+
+/*
+ * Drain out remaining samples if the effect generates any.
+ */
+void st_fade_drain(effp, obuf, osamp)
+eff_t effp;
+LONG *obuf;
+int *osamp;
+{
+ fade_t fade = (fade_t) effp->priv;
+ int len, t_chan = 0;
+
+ len = *osamp;
+ *osamp = 0;
+
+ if (fade->out_stop != 0 && fade->samplesdone < fade->out_stop &&
+ !(fade->endpadwarned))
+ { /* Warning about padding silence into end of sample */
+ st_warn("Fade: warning: End time passed end-of-file. Padding with silence");
+ fade->endpadwarned = 1;
+ } /* endif endpadwarned */
+
+ for (;len && (fade->out_stop != 0 &&
+ fade->samplesdone < fade->out_stop); len--)
+ {
+ *obuf = 0;
+ obuf++;
+ *osamp += 1;
+
+ t_chan++;
+ if (t_chan >= effp->ininfo.channels)
+ {
+ fade->samplesdone += 1;
+ t_chan = 0;
+ } /* endif channels */
+ } /* endfor */
+}
+
+/*
+ * Do anything required when you stop reading samples.
+ * (free allocated memory, etc.)
+ */
+void st_fade_stop(effp)
+eff_t effp;
+{
+ /* nothing to do */
+}
+
+/* Function returns gain value 0.0 - 1.0 according index / range ratio
+* and -1.0 if type is invalid
+* todo: to optimize performance calculate gain every now and then and interpolate */
+static double fade_gain(index, range, type)
+ULONG index, range;
+char type;
+{
+ double retval = 0.0, findex = 0.0;
+
+ findex = 1.0 * index / range;
+
+ /* todo: are these really needed */
+ findex = (findex < 0 ? 0.0 : findex);
+ findex = (findex > 1.0 ? 1.0 : findex);
+
+ switch (type)
+ {
+ case FADE_TRI : /* triangle */
+ retval = findex;
+ break;
+
+ case FADE_QUARTER : /* quarter of sinewave */
+ retval = sin(findex * M_PI / 2);
+ break;
+
+ case FADE_HALF : /* half of sinewave... eh cosine wave */
+ retval = (1 - cos(findex * M_PI )) / 2 ;
+ break;
+
+ case FADE_LOG : /* logaritmic */
+ /* 5 means 100 db attenuation. */
+ /* todo: should this be adopted with bit depth */
+ retval = pow(0.1, (1 - findex) * 5);
+ break;
+
+ case FADE_PAR : /* inverted parabola */
+ retval = (1 - (1 - findex) * (1 - findex));
+ break;
+
+ /* todo: more fade curves? */
+ default : /* Error indicating wrong fade curve */
+ retval = -1.0;
+ break;
+ } /* endswitch */
+
+ return retval;
+}
--- a/src/handlers.c
+++ b/src/handlers.c
@@ -505,6 +505,12 @@
extern int st_echos_drain();
extern int st_echos_stop();
+extern int st_fade_getopts();
+extern int st_fade_start();
+extern int st_fade_flow();
+extern int st_fade_drain();
+extern int st_fade_stop();
+
extern int st_filter_getopts();
extern int st_filter_start();
extern int st_filter_flow();
@@ -679,6 +685,9 @@
{"echos", 0,
st_echos_getopts, st_echos_start, st_echos_flow,
st_echos_drain, st_echos_stop},
+ {"fade", ST_EFF_MCHAN,
+ st_fade_getopts, st_fade_start, st_fade_flow,
+ st_fade_drain, st_fade_stop},
{ "filter", 0,
st_filter_getopts, st_filter_start, st_filter_flow,
st_filter_drain, st_filter_stop},