shithub: sox

Download patch

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},