sox.1: fix example for mcompand
[sox.git] / src / fade.c
blob3cf4876e2ffeead8f26b3d94566b4bfe3850b9c1
1 /* Ari Moisio <armoi@sci.fi> Aug 29 2000, based on skeleton effect
2 * Written by Chris Bagwell (cbagwell@sprynet.com) - March 16, 1999
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 */
11 #include "sox_i.h"
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. */
23 #include <string.h>
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;
34 /* prototypes */
35 static double fade_gain(uint64_t index, uint64_t range, int fadetype);
38 * Process options
40 * Don't do initialization now.
41 * The 'info' fields are not yet filled in.
44 static int sox_fade_getopts(sox_effect_t * effp, int argc, char **argv)
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;
54 if (argc < 1 || argc > 4)
55 return lsx_usage(effp);
57 /* because sample rate is unavailable at this point we store the
58 * string off for later computations.
61 if (sscanf(argv[0], "%1[qhltp]", t_char))
63 fade->in_fadetype = *t_char;
64 fade->out_fadetype = *t_char;
66 argv++;
67 argc--;
69 else
71 /* No type given. */
72 fade->in_fadetype = 'l';
73 fade->out_fadetype = 'l';
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);
82 fade->in_stop = samples;
83 fade->out_start_str = fade->out_stop_str = 0;
85 for (t_argno = 1; t_argno < argc && t_argno < 3; t_argno++)
87 /* See if there is fade-in/fade-out times/curves specified. */
88 if(t_argno == 1)
90 fade->out_stop_str = lsx_strdup(argv[t_argno]);
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;
98 else
100 fade->out_start_str = lsx_strdup(argv[t_argno]);
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;
108 } /* End for(t_argno) */
110 return(SOX_SUCCESS);
114 * Prepare processing.
115 * Do all initializations.
117 static int sox_fade_start(sox_effect_t * effp)
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;
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);
131 fade->in_stop = samples;
132 fade->do_out = 0;
133 /* See if user specified a stop time */
134 if (fade->out_stop_str)
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;
143 fade->out_stop = samples;
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 :
149 if (!fade->out_stop) {
150 lsx_fail("cannot fade out: audio length is neither known nor given");
151 return SOX_EOF;
155 /* See if user wants to fade out. */
156 if (fade->out_start_str)
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;
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.
169 fade->out_start = fade->out_stop - fade->in_stop;
171 else
172 /* If not specified then user wants to process all
173 * of file. Use a value of zero to indicate this.
175 fade->out_stop = 0;
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;
186 fade->samplesdone = fade->in_start;
187 fade->endpadwarned = 0;
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);
193 if (fade->in_start == fade->in_stop && !truncate &&
194 fade->out_start == fade->out_stop)
195 return SOX_EFF_NULL;
197 effp->out_signal.length = truncate ?
198 fade->out_stop * effp->in_signal.channels : effp->in_signal.length;
200 return SOX_SUCCESS;
204 * Processed signed long samples from ibuf to obuf.
205 * Return number of samples processed.
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)
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;
216 len = ((*isamp > *osamp) ? *osamp : *isamp);
218 *osamp = 0;
219 *isamp = 0;
221 for(; len && more_output; len--)
223 t_ibuf = *ibuf;
225 if ((fade->samplesdone >= fade->in_start) &&
226 (!fade->do_out || fade->samplesdone < fade->out_stop))
227 { /* something to generate output */
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 */
248 if (!(!fade->do_out || fade->samplesdone < fade->out_stop))
249 more_output = 0;
251 t_output = 1;
253 else
254 { /* No output generated */
255 t_output = 0;
256 } /* endif something to output */
258 *isamp += 1;
259 ibuf++;
261 if (t_output)
262 { /* Output generated, update pointers and counters */
263 obuf++;
264 *osamp += 1;
265 } /* endif t_output */
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 */
276 /* If not more samples will be returned, let application know
277 * this.
279 if (fade->do_out && fade->samplesdone >= fade->out_stop)
280 return SOX_EOF;
281 else
282 return SOX_SUCCESS;
286 * Drain out remaining samples if the effect generates any.
288 static int sox_fade_drain(sox_effect_t * effp, sox_sample_t *obuf, size_t *osamp)
290 priv_t * fade = (priv_t *) effp->priv;
291 int len;
292 size_t t_chan = 0;
294 len = *osamp;
295 len -= len % effp->in_signal.channels;
296 *osamp = 0;
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 */
305 for (;len && (fade->do_out &&
306 fade->samplesdone < fade->out_stop); len--)
308 *obuf = 0;
309 obuf++;
310 *osamp += 1;
312 t_chan++;
313 if (t_chan >= effp->in_signal.channels)
315 fade->samplesdone += 1;
316 t_chan = 0;
317 } /* endif channels */
318 } /* endfor */
320 if (fade->do_out && fade->samplesdone >= fade->out_stop)
321 return SOX_EOF;
322 else
323 return SOX_SUCCESS;
327 * Do anything required when you stop reading samples.
328 * (free allocated memory, etc.)
330 static int lsx_kill(sox_effect_t * effp)
332 priv_t * fade = (priv_t *) effp->priv;
334 free(fade->in_stop_str);
335 free(fade->out_start_str);
336 free(fade->out_stop_str);
337 return (SOX_SUCCESS);
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 */
343 static double fade_gain(uint64_t index, uint64_t range, int type)
345 double retval = 0.0, findex = 0.0;
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));
350 switch (type) {
351 case FADE_TRI : /* triangle */
352 retval = findex;
353 break;
355 case FADE_QUARTER : /* quarter of sinewave */
356 retval = sin(findex * M_PI / 2);
357 break;
359 case FADE_HALF : /* half of sinewave... eh cosine wave */
360 retval = (1 - cos(findex * M_PI )) / 2 ;
361 break;
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;
369 case FADE_PAR : /* inverted parabola */
370 retval = (1 - (1 - findex) * (1 - findex));
371 break;
373 /* TODO: more fade curves? */
374 default : /* Error indicating wrong fade curve */
375 retval = -1.0;
376 break;
379 return retval;
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)
396 const sox_effect_handler_t *lsx_fade_effect_fn(void)
398 return &sox_fade_effect;