1 /* Ari Moisio <armoi@sci.fi> Aug 29 2000, based on skeleton effect
2  * Written by Chris Bagwell (cbagwell@sprynet.com) - March 16, 1999
3  *
4  * Copyright 1999 Chris Bagwell And Sundry Contributors
5  * This source code is freely redistributable and may be used for
6  * any purpose.  This copyright notice must be maintained.
7  * Chris Bagwell And Sundry Contributors are not responsible for
8  * the consequences of using this software.
9  */
10 
11 #include "sox_i.h"
12 
13 /* Fade curves */
14 #define FADE_QUARTER    'q'     /* Quarter of sine wave, 0 to pi/2 */
15 #define FADE_HALF       'h'     /* Half of sine wave, pi/2 to 1.5 * pi
16                                 * scaled so that -1 means no output
17                                 * and 1 means 0 db attenuation. */
18 #define FADE_LOG        'l'     /* Logarithmic curve. Fades -100 db
19                                 * in given time. */
20 #define FADE_TRI        't'     /* Linear slope. */
21 #define FADE_PAR        'p'     /* Inverted parabola. */
22 
23 #include <string.h>
24 
25 /* Private data for fade file */
26 typedef struct { /* These are measured as samples */
27     uint64_t in_start, in_stop, out_start, out_stop, samplesdone;
28     char *in_stop_str, *out_start_str, *out_stop_str;
29     char in_fadetype, out_fadetype;
30     char do_out;
31     int endpadwarned;
32 } priv_t;
33 
34 /* prototypes */
35 static double fade_gain(uint64_t index, uint64_t range, int fadetype);
36 
37 /*
38  * Process options
39  *
40  * Don't do initialization now.
41  * The 'info' fields are not yet filled in.
42  */
43 
sox_fade_getopts(sox_effect_t * effp,int argc,char ** argv)44 static int sox_fade_getopts(sox_effect_t * effp, int argc, char **argv)
45 {
46 
47     priv_t * fade = (priv_t *) effp->priv;
48     char t_char[2];
49     int t_argno;
50     uint64_t samples;
51     const char *n;
52   --argc, ++argv;
53 
54     if (argc < 1 || argc > 4)
55          return lsx_usage(effp);
56 
57     /* because sample rate is unavailable at this point we store the
58      * string off for later computations.
59      */
60 
61     if (sscanf(argv[0], "%1[qhltp]", t_char))
62     {
63         fade->in_fadetype = *t_char;
64         fade->out_fadetype = *t_char;
65 
66         argv++;
67         argc--;
68     }
69     else
70     {
71         /* No type given. */
72         fade->in_fadetype = 'l';
73         fade->out_fadetype = 'l';
74     }
75 
76     fade->in_stop_str = lsx_strdup(argv[0]);
77     /* Do a dummy parse to see if it will fail */
78     n = lsx_parsesamples(0., fade->in_stop_str, &samples, 't');
79     if (!n || *n)
80       return lsx_usage(effp);
81 
82     fade->in_stop = samples;
83     fade->out_start_str = fade->out_stop_str = 0;
84 
85     for (t_argno = 1; t_argno < argc && t_argno < 3; t_argno++)
86     {
87         /* See if there is fade-in/fade-out times/curves specified. */
88         if(t_argno == 1)
89         {
90             fade->out_stop_str = lsx_strdup(argv[t_argno]);
91 
92             /* Do a dummy parse to see if it will fail */
93             n = lsx_parseposition(0., fade->out_stop_str, NULL, (uint64_t)0, (uint64_t)0, '=');
94             if (!n || *n)
95               return lsx_usage(effp);
96             fade->out_stop = samples;
97         }
98         else
99         {
100             fade->out_start_str = lsx_strdup(argv[t_argno]);
101 
102             /* Do a dummy parse to see if it will fail */
103             n = lsx_parsesamples(0., fade->out_start_str, &samples, 't');
104             if (!n || *n)
105               return lsx_usage(effp);
106             fade->out_start = samples;
107         }
108     } /* End for(t_argno) */
109 
110     return(SOX_SUCCESS);
111 }
112 
113 /*
114  * Prepare processing.
115  * Do all initializations.
116  */
sox_fade_start(sox_effect_t * effp)117 static int sox_fade_start(sox_effect_t * effp)
118 {
119     priv_t * fade = (priv_t *) effp->priv;
120     sox_bool truncate = sox_false;
121     uint64_t samples;
122     uint64_t in_length = effp->in_signal.length != SOX_UNKNOWN_LEN ?
123       effp->in_signal.length / effp->in_signal.channels : SOX_UNKNOWN_LEN;
124 
125     /* converting time values to samples */
126     fade->in_start = 0;
127     if (lsx_parsesamples(effp->in_signal.rate, fade->in_stop_str,
128                         &samples, 't') == NULL)
129       return lsx_usage(effp);
130 
131     fade->in_stop = samples;
132     fade->do_out = 0;
133     /* See if user specified a stop time */
134     if (fade->out_stop_str)
135     {
136         fade->do_out = 1;
137         if (!lsx_parseposition(effp->in_signal.rate, fade->out_stop_str,
138                             &samples, (uint64_t)0, in_length, '=') ||
139             samples == SOX_UNKNOWN_LEN) {
140           lsx_fail("audio length is unknown");
141           return SOX_EOF;
142         }
143         fade->out_stop = samples;
144 
145         if (!(truncate = !!fade->out_stop)) {
146           fade->out_stop = effp->in_signal.length != SOX_UNKNOWN_LEN ?
147               effp->in_signal.length / effp->in_signal.channels :
148               0;
149           if (!fade->out_stop) {
150             lsx_fail("cannot fade out: audio length is neither known nor given");
151             return SOX_EOF;
152           }
153         }
154 
155         /* See if user wants to fade out. */
156         if (fade->out_start_str)
157         {
158             if (lsx_parsesamples(effp->in_signal.rate, fade->out_start_str,
159                         &samples, 't') == NULL)
160               return lsx_usage(effp);
161             /* Fade time is relative to stop time. */
162             fade->out_start = fade->out_stop - samples;
163         }
164         else
165             /* If user doesn't specify fade out length then
166              * use same length as input side.  This is stored
167              * in in_stop.
168              */
169             fade->out_start = fade->out_stop - fade->in_stop;
170     }
171     else
172         /* If not specified then user wants to process all
173          * of file.  Use a value of zero to indicate this.
174          */
175         fade->out_stop = 0;
176 
177     if (fade->out_start) {              /* Sanity check */
178       if (fade->in_stop > fade->out_start)
179         --fade->in_stop;                /* 1 sample grace for rounding error. */
180       if (fade->in_stop > fade->out_start) {
181         lsx_fail("fade-out overlaps fade-in");
182         return SOX_EOF;
183       }
184     }
185 
186     fade->samplesdone = fade->in_start;
187     fade->endpadwarned = 0;
188 
189     lsx_debug("in_start = %" PRIu64 " in_stop = %" PRIu64 " "
190       "out_start = %" PRIu64 " out_stop = %" PRIu64,
191       fade->in_start, fade->in_stop, fade->out_start, fade->out_stop);
192 
193     if (fade->in_start == fade->in_stop && !truncate &&
194         fade->out_start == fade->out_stop)
195       return SOX_EFF_NULL;
196 
197     effp->out_signal.length = truncate ?
198         fade->out_stop * effp->in_signal.channels : effp->in_signal.length;
199 
200     return SOX_SUCCESS;
201 }
202 
203 /*
204  * Processed signed long samples from ibuf to obuf.
205  * Return number of samples processed.
206  */
sox_fade_flow(sox_effect_t * effp,const sox_sample_t * ibuf,sox_sample_t * obuf,size_t * isamp,size_t * osamp)207 static int sox_fade_flow(sox_effect_t * effp, const sox_sample_t *ibuf, sox_sample_t *obuf,
208                  size_t *isamp, size_t *osamp)
209 {
210     priv_t * fade = (priv_t *) effp->priv;
211     /* len is total samples, chcnt counts channels */
212     int len = 0, t_output = 1, more_output = 1;
213     sox_sample_t t_ibuf;
214     size_t chcnt = 0;
215 
216     len = ((*isamp > *osamp) ? *osamp : *isamp);
217 
218     *osamp = 0;
219     *isamp = 0;
220 
221     for(; len && more_output; len--)
222     {
223         t_ibuf = *ibuf;
224 
225         if ((fade->samplesdone >= fade->in_start) &&
226             (!fade->do_out || fade->samplesdone < fade->out_stop))
227         { /* something to generate output */
228 
229             if (fade->samplesdone < fade->in_stop)
230             { /* fade-in phase, increase gain */
231                 *obuf = t_ibuf *
232                     fade_gain(fade->samplesdone - fade->in_start,
233                               fade->in_stop - fade->in_start,
234                               fade->in_fadetype);
235             } /* endif fade-in */
236             else if (!fade->do_out || fade->samplesdone < fade->out_start)
237             { /* steady gain phase */
238                 *obuf = t_ibuf;
239             } /* endif  steady phase */
240             else
241             { /* fade-out phase, decrease gain */
242                 *obuf = t_ibuf *
243                     fade_gain(fade->out_stop - fade->samplesdone,
244                               fade->out_stop - fade->out_start,
245                               fade->out_fadetype);
246             } /* endif fade-out */
247 
248             if (!(!fade->do_out || fade->samplesdone < fade->out_stop))
249                 more_output = 0;
250 
251             t_output = 1;
252         }
253         else
254         { /* No output generated */
255             t_output = 0;
256         } /* endif something to output */
257 
258         *isamp += 1;
259         ibuf++;
260 
261         if (t_output)
262         { /* Output generated, update pointers and counters */
263             obuf++;
264             *osamp += 1;
265         } /* endif t_output */
266 
267         /* Process next channel */
268         chcnt++;
269         if (chcnt >= effp->in_signal.channels)
270         { /* all channels of this sample processed */
271             chcnt = 0;
272             fade->samplesdone += 1;
273         } /* endif all channels */
274     } /* endfor */
275 
276     /* If not more samples will be returned, let application know
277      * this.
278      */
279     if (fade->do_out && fade->samplesdone >= fade->out_stop)
280         return SOX_EOF;
281     else
282         return SOX_SUCCESS;
283 }
284 
285 /*
286  * Drain out remaining samples if the effect generates any.
287  */
sox_fade_drain(sox_effect_t * effp,sox_sample_t * obuf,size_t * osamp)288 static int sox_fade_drain(sox_effect_t * effp, sox_sample_t *obuf, size_t *osamp)
289 {
290     priv_t * fade = (priv_t *) effp->priv;
291     int len;
292     size_t t_chan = 0;
293 
294     len = *osamp;
295     len -= len % effp->in_signal.channels;
296     *osamp = 0;
297 
298     if (fade->do_out && fade->samplesdone < fade->out_stop &&
299         !(fade->endpadwarned))
300     { /* Warning about padding silence into end of sample */
301         lsx_warn("End time past end of audio. Padding with silence");
302         fade->endpadwarned = 1;
303     } /* endif endpadwarned */
304 
305     for (;len && (fade->do_out &&
306                   fade->samplesdone < fade->out_stop); len--)
307     {
308         *obuf = 0;
309         obuf++;
310         *osamp += 1;
311 
312         t_chan++;
313         if (t_chan >= effp->in_signal.channels)
314         {
315             fade->samplesdone += 1;
316             t_chan = 0;
317         } /* endif channels */
318     } /* endfor */
319 
320     if (fade->do_out && fade->samplesdone >= fade->out_stop)
321         return SOX_EOF;
322     else
323         return SOX_SUCCESS;
324 }
325 
326 /*
327  * Do anything required when you stop reading samples.
328  *      (free allocated memory, etc.)
329  */
lsx_kill(sox_effect_t * effp)330 static int lsx_kill(sox_effect_t * effp)
331 {
332     priv_t * fade = (priv_t *) effp->priv;
333 
334     free(fade->in_stop_str);
335     free(fade->out_start_str);
336     free(fade->out_stop_str);
337     return (SOX_SUCCESS);
338 }
339 
340 /* Function returns gain value 0.0 - 1.0 according index / range ratio
341 * and -1.0 if  type is invalid
342 * todo: to optimize performance calculate gain every now and then and interpolate */
fade_gain(uint64_t index,uint64_t range,int type)343 static double fade_gain(uint64_t index, uint64_t range, int type)
344 {
345     double retval = 0.0, findex = 0.0;
346 
347     /* TODO: does it really have to be contrained to [0.0, 1.0]? */
348     findex = max(0.0, min(1.0, 1.0 * index / range));
349 
350     switch (type) {
351     case FADE_TRI :             /* triangle */
352       retval = findex;
353       break;
354 
355     case FADE_QUARTER :         /* quarter of sinewave */
356       retval = sin(findex * M_PI / 2);
357       break;
358 
359     case FADE_HALF :          /* half of sinewave... eh cosine wave */
360       retval = (1 - cos(findex * M_PI )) / 2 ;
361       break;
362 
363     case FADE_LOG :             /* logarithmic */
364       /* 5 means 100 db attenuation. */
365       /* TODO: should this be adopted with bit depth */
366       retval =  pow(0.1, (1 - findex) * 5);
367       break;
368 
369     case FADE_PAR :             /* inverted parabola */
370       retval = (1 - (1 - findex)  * (1 - findex));
371       break;
372 
373     /* TODO: more fade curves? */
374     default :                  /* Error indicating wrong fade curve */
375       retval = -1.0;
376       break;
377     }
378 
379     return retval;
380 }
381 
382 static sox_effect_handler_t sox_fade_effect = {
383   "fade",
384   "[ type ] fade-in-length [ stop-position [ fade-out-length ] ]\n"
385   "       Time is in hh:mm:ss.frac format.\n"
386   "       Fade type one of q, h, t, l or p.",
387   SOX_EFF_MCHAN | SOX_EFF_LENGTH,
388   sox_fade_getopts,
389   sox_fade_start,
390   sox_fade_flow,
391   sox_fade_drain,
392   NULL,
393   lsx_kill, sizeof(priv_t)
394 };
395 
lsx_fade_effect_fn(void)396 const sox_effect_handler_t *lsx_fade_effect_fn(void)
397 {
398     return &sox_fade_effect;
399 }
400