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.
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
20 #define FADE_TRI 't' /* Linear slope. */
21 #define FADE_PAR 'p' /* Inverted parabola. */
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
;
35 static double fade_gain(uint64_t index
, uint64_t range
, int fadetype
);
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
;
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
;
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');
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. */
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, '=');
95 return lsx_usage(effp
);
96 fade
->out_stop
= samples
;
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');
105 return lsx_usage(effp
);
106 fade
->out_start
= samples
;
108 } /* End for(t_argno) */
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
;
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 */
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
;
133 /* See if user specified a stop time */
134 if (fade
->out_stop_str
)
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");
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");
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
;
165 /* If user doesn't specify fade out length then
166 * use same length as input side. This is stored
169 fade
->out_start
= fade
->out_stop
- fade
->in_stop
;
172 /* If not specified then user wants to process all
173 * of file. Use a value of zero to indicate this.
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");
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
)
197 effp
->out_signal
.length
= truncate
?
198 fade
->out_stop
* effp
->in_signal
.channels
: effp
->in_signal
.length
;
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;
216 len
= ((*isamp
> *osamp
) ? *osamp
: *isamp
);
221 for(; len
&& more_output
; len
--)
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 */
232 fade_gain(fade
->samplesdone
- fade
->in_start
,
233 fade
->in_stop
- fade
->in_start
,
235 } /* endif fade-in */
236 else if (!fade
->do_out
|| fade
->samplesdone
< fade
->out_start
)
237 { /* steady gain phase */
239 } /* endif steady phase */
241 { /* fade-out phase, decrease gain */
243 fade_gain(fade
->out_stop
- fade
->samplesdone
,
244 fade
->out_stop
- fade
->out_start
,
246 } /* endif fade-out */
248 if (!(!fade
->do_out
|| fade
->samplesdone
< fade
->out_stop
))
254 { /* No output generated */
256 } /* endif something to output */
262 { /* Output generated, update pointers and counters */
265 } /* endif t_output */
267 /* Process next channel */
269 if (chcnt
>= effp
->in_signal
.channels
)
270 { /* all channels of this sample processed */
272 fade
->samplesdone
+= 1;
273 } /* endif all channels */
276 /* If not more samples will be returned, let application know
279 if (fade
->do_out
&& fade
->samplesdone
>= fade
->out_stop
)
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
;
295 len
-= len
% effp
->in_signal
.channels
;
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
--)
313 if (t_chan
>= effp
->in_signal
.channels
)
315 fade
->samplesdone
+= 1;
317 } /* endif channels */
320 if (fade
->do_out
&& fade
->samplesdone
>= fade
->out_stop
)
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
));
351 case FADE_TRI
: /* triangle */
355 case FADE_QUARTER
: /* quarter of sinewave */
356 retval
= sin(findex
* M_PI
/ 2);
359 case FADE_HALF
: /* half of sinewave... eh cosine wave */
360 retval
= (1 - cos(findex
* M_PI
)) / 2 ;
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);
369 case FADE_PAR
: /* inverted parabola */
370 retval
= (1 - (1 - findex
) * (1 - findex
));
373 /* TODO: more fade curves? */
374 default : /* Error indicating wrong fade curve */
382 static sox_effect_handler_t sox_fade_effect
= {
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
,
393 lsx_kill
, sizeof(priv_t
)
396 const sox_effect_handler_t
*lsx_fade_effect_fn(void)
398 return &sox_fade_effect
;