1 /* SoX Effects chain (c) 2007 robs@users.sourceforge.net
3 * This library is free software; you can redistribute it and/or modify it
4 * under the terms of the GNU Lesser General Public License as published by
5 * the Free Software Foundation; either version 2.1 of the License, or (at
6 * your option) any later version.
8 * This library is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
11 * General Public License for more details.
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this library; if not, write to the Free Software Foundation,
15 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
26 #define DEBUG_EFFECTS_CHAIN 0
28 /* Default effect handler functions for do-nothing situations: */
30 static int default_function(sox_effect_t
* effp UNUSED
)
35 /* Pass through samples verbatim */
36 int lsx_flow_copy(sox_effect_t
* effp UNUSED
, const sox_sample_t
* ibuf
,
37 sox_sample_t
* obuf
, size_t * isamp
, size_t * osamp
)
39 *isamp
= *osamp
= min(*isamp
, *osamp
);
40 memcpy(obuf
, ibuf
, *isamp
* sizeof(*obuf
));
44 /* Inform no more samples to drain */
45 static int default_drain(sox_effect_t
* effp UNUSED
, sox_sample_t
*obuf UNUSED
, size_t *osamp
)
51 /* Check that no parameters have been given */
52 static int default_getopts(sox_effect_t
* effp
, int argc
, char **argv UNUSED
)
54 return --argc
? lsx_usage(effp
) : SOX_SUCCESS
;
57 /* Partially initialise the effect structure; signal info will come later */
58 sox_effect_t
* sox_create_effect(sox_effect_handler_t
const * eh
)
60 sox_effect_t
* effp
= lsx_calloc(1, sizeof(*effp
));
63 effp
->global_info
= sox_get_effects_globals();
65 if (!effp
->handler
.getopts
) effp
->handler
.getopts
= default_getopts
;
66 if (!effp
->handler
.start
) effp
->handler
.start
= default_function
;
67 if (!effp
->handler
.flow
) effp
->handler
.flow
= lsx_flow_copy
;
68 if (!effp
->handler
.drain
) effp
->handler
.drain
= default_drain
;
69 if (!effp
->handler
.stop
) effp
->handler
.stop
= default_function
;
70 if (!effp
->handler
.kill
) effp
->handler
.kill
= default_function
;
72 effp
->priv
= lsx_calloc(1, effp
->handler
.priv_size
);
75 } /* sox_create_effect */
77 int sox_effect_options(sox_effect_t
*effp
, int argc
, char * const argv
[])
81 char * * argv2
= lsx_malloc((argc
+ 1) * sizeof(*argv2
));
82 argv2
[0] = (char *)effp
->handler
.name
;
83 memcpy(argv2
+ 1, argv
, argc
* sizeof(*argv2
));
84 result
= effp
->handler
.getopts(effp
, argc
+ 1, argv2
);
87 } /* sox_effect_options */
91 sox_effects_chain_t
* sox_create_effects_chain(
92 sox_encodinginfo_t
const * in_enc
, sox_encodinginfo_t
const * out_enc
)
94 sox_effects_chain_t
* result
= lsx_calloc(1, sizeof(sox_effects_chain_t
));
95 result
->global_info
= *sox_get_effects_globals();
96 result
->in_enc
= in_enc
;
97 result
->out_enc
= out_enc
;
99 } /* sox_create_effects_chain */
101 void sox_delete_effects_chain(sox_effects_chain_t
*ecp
)
103 if (ecp
&& ecp
->length
)
104 sox_delete_effects(ecp
);
107 } /* sox_delete_effects_chain */
109 /* Effect can call in start() or flow() to set minimum input size to flow() */
110 int lsx_effect_set_imin(sox_effect_t
* effp
, size_t imin
)
112 if (imin
> sox_globals
.bufsiz
/ effp
->flows
) {
113 lsx_fail("sox_bufsiz not big enough");
121 /* Effects table to be extended in steps of EFF_TABLE_STEP */
122 #define EFF_TABLE_STEP 8
124 /* Add an effect to the chain. *in is the input signal for this effect. *out is
125 * a suggestion as to what the output signal should be, but depending on its
126 * given options and *in, the effect can choose to do differently. Whatever
127 * output rate and channels the effect does produce are written back to *in,
128 * ready for the next effect in the chain.
130 int sox_add_effect(sox_effects_chain_t
* chain
, sox_effect_t
* effp
, sox_signalinfo_t
* in
, sox_signalinfo_t
const * out
)
132 int ret
, (*start
)(sox_effect_t
* effp
) = effp
->handler
.start
;
134 sox_effect_t eff0
; /* Copy of effect for flow 0 before calling start */
136 effp
->global_info
= &chain
->global_info
;
137 effp
->in_signal
= *in
;
138 effp
->out_signal
= *out
;
139 effp
->in_encoding
= chain
->in_enc
;
140 effp
->out_encoding
= chain
->out_enc
;
141 if (!(effp
->handler
.flags
& SOX_EFF_CHAN
))
142 effp
->out_signal
.channels
= in
->channels
;
143 if (!(effp
->handler
.flags
& SOX_EFF_RATE
))
144 effp
->out_signal
.rate
= in
->rate
;
145 if (!(effp
->handler
.flags
& SOX_EFF_PREC
))
146 effp
->out_signal
.precision
= (effp
->handler
.flags
& SOX_EFF_MODIFY
)?
147 in
->precision
: SOX_SAMPLE_PRECISION
;
148 if (!(effp
->handler
.flags
& SOX_EFF_GAIN
))
149 effp
->out_signal
.mult
= in
->mult
;
152 (effp
->handler
.flags
& SOX_EFF_MCHAN
)? 1 : effp
->in_signal
.channels
;
155 eff0
= *effp
, eff0
.priv
= lsx_memdup(eff0
.priv
, eff0
.handler
.priv_size
);
156 eff0
.in_signal
.mult
= NULL
; /* Only used in channel 0 */
158 if (ret
== SOX_EFF_NULL
) {
159 lsx_report("has no effect in this configuration");
165 if (ret
!= SOX_SUCCESS
) {
170 lsx_debug("mult=%g", *in
->mult
);
172 if (!(effp
->handler
.flags
& SOX_EFF_LENGTH
)) {
173 effp
->out_signal
.length
= in
->length
;
174 if (effp
->out_signal
.length
!= SOX_UNKNOWN_LEN
) {
175 if (effp
->handler
.flags
& SOX_EFF_CHAN
)
176 effp
->out_signal
.length
=
177 effp
->out_signal
.length
/ in
->channels
* effp
->out_signal
.channels
;
178 if (effp
->handler
.flags
& SOX_EFF_RATE
)
179 effp
->out_signal
.length
=
180 effp
->out_signal
.length
/ in
->rate
* effp
->out_signal
.rate
+ .5;
184 *in
= effp
->out_signal
;
186 if (chain
->length
== chain
->table_size
) {
187 chain
->table_size
+= EFF_TABLE_STEP
;
188 lsx_debug_more("sox_add_effect: extending effects table, "
189 "new size = %lu", (unsigned long)chain
->table_size
);
190 lsx_revalloc(chain
->effects
, chain
->table_size
);
193 chain
->effects
[chain
->length
] =
194 lsx_calloc(effp
->flows
, sizeof(chain
->effects
[chain
->length
][0]));
195 chain
->effects
[chain
->length
][0] = *effp
;
197 for (f
= 1; f
< effp
->flows
; ++f
) {
198 chain
->effects
[chain
->length
][f
] = eff0
;
199 chain
->effects
[chain
->length
][f
].flow
= f
;
200 chain
->effects
[chain
->length
][f
].priv
= lsx_memdup(eff0
.priv
, eff0
.handler
.priv_size
);
201 if (start(&chain
->effects
[chain
->length
][f
]) != SOX_SUCCESS
) {
212 static int flow_effect(sox_effects_chain_t
* chain
, size_t n
)
214 sox_effect_t
* effp1
= &chain
->effects
[n
- 1][0];
215 sox_effect_t
* effp
= &chain
->effects
[n
][0];
216 int effstatus
= SOX_SUCCESS
, f
= 0;
218 const sox_sample_t
*ibuf
;
219 size_t idone
= effp1
->oend
- effp1
->obeg
;
220 size_t obeg
= sox_globals
.bufsiz
- effp
->oend
;
221 #if DEBUG_EFFECTS_CHAIN
222 size_t pre_idone
= idone
;
223 size_t pre_odone
= obeg
;
226 if (effp
->flows
== 1) { /* Run effect on all channels at once */
227 idone
-= idone
% effp
->in_signal
.channels
;
228 effstatus
= effp
->handler
.flow(effp
, &effp1
->obuf
[effp1
->obeg
],
229 &effp
->obuf
[effp
->oend
], &idone
, &obeg
);
230 if (obeg
% effp
->out_signal
.channels
!= 0) {
231 lsx_fail("multi-channel effect flowed asymmetrically!");
234 } else { /* Run effect on each channel individually */
235 sox_sample_t
*obuf
= &effp
->obuf
[effp
->oend
];
236 size_t idone_last
= 0, odone_last
= 0; /* Initialised to prevent warning */
238 ibuf
= &effp1
->obuf
[effp1
->obeg
];
239 for (i
= 0; i
< idone
; i
+= effp
->flows
)
240 for (f
= 0; f
< (int)effp
->flows
; ++f
)
241 chain
->ibufc
[f
][i
/ effp
->flows
] = *ibuf
++;
244 if (sox_globals
.use_threads
&& effp
->flows
> 1)
246 #pragma omp parallel for
247 for (f
= 0; f
< (int)effp
->flows
; ++f
) {
248 size_t idonec
= idone
/ effp
->flows
;
249 size_t odonec
= obeg
/ effp
->flows
;
250 int eff_status_c
= effp
->handler
.flow(&chain
->effects
[n
][f
],
251 chain
->ibufc
[f
], chain
->obufc
[f
], &idonec
, &odonec
);
257 if (eff_status_c
!= SOX_SUCCESS
)
261 else /* sox_globals.use_threads */
264 for (f
= 0; f
< (int)effp
->flows
; ++f
) {
265 size_t idonec
= idone
/ effp
->flows
;
266 size_t odonec
= obeg
/ effp
->flows
;
267 int eff_status_c
= effp
->handler
.flow(&chain
->effects
[n
][f
],
268 chain
->ibufc
[f
], chain
->obufc
[f
], &idonec
, &odonec
);
269 if (f
&& (idonec
!= idone_last
|| odonec
!= odone_last
)) {
270 lsx_fail("flowed asymmetrically!");
276 if (eff_status_c
!= SOX_SUCCESS
)
281 for (i
= 0; i
< odone_last
; ++i
)
282 for (f
= 0; f
< (int)effp
->flows
; ++f
)
283 *obuf
++ = chain
->obufc
[f
][i
];
285 idone
= effp
->flows
* idone_last
;
286 obeg
= effp
->flows
* odone_last
;
288 #if DEBUG_EFFECTS_CHAIN
289 lsx_report("flow: %5" PRIuPTR
" %5" PRIuPTR
" %5" PRIuPTR
" %5" PRIuPTR
,
290 pre_idone
, pre_odone
, idone
, obeg
);
292 effp1
->obeg
+= idone
;
293 if (effp1
->obeg
== effp1
->oend
)
294 effp1
->obeg
= effp1
->oend
= 0;
295 else if (effp1
->oend
- effp1
->obeg
< effp
->imin
) { /* Need to refill? */
296 memmove(effp1
->obuf
, &effp1
->obuf
[effp1
->obeg
], (effp1
->oend
- effp1
->obeg
) * sizeof(*effp1
->obuf
));
297 effp1
->oend
-= effp1
->obeg
;
303 return effstatus
== SOX_SUCCESS
? SOX_SUCCESS
: SOX_EOF
;
306 /* The same as flow_effect but with no input */
307 static int drain_effect(sox_effects_chain_t
* chain
, size_t n
)
309 sox_effect_t
* effp
= &chain
->effects
[n
][0];
310 int effstatus
= SOX_SUCCESS
;
312 size_t obeg
= sox_globals
.bufsiz
- effp
->oend
;
313 #if DEBUG_EFFECTS_CHAIN
314 size_t pre_odone
= obeg
;
317 if (effp
->flows
== 1) { /* Run effect on all channels at once */
318 effstatus
= effp
->handler
.drain(effp
, &effp
->obuf
[effp
->oend
], &obeg
);
319 if (obeg
% effp
->out_signal
.channels
!= 0) {
320 lsx_fail("multi-channel effect drained asymmetrically!");
323 } else { /* Run effect on each channel individually */
324 sox_sample_t
*obuf
= &effp
->obuf
[effp
->oend
];
325 size_t odone_last
= 0; /* Initialised to prevent warning */
327 for (f
= 0; f
< effp
->flows
; ++f
) {
328 size_t odonec
= obeg
/ effp
->flows
;
329 int eff_status_c
= effp
->handler
.drain(&chain
->effects
[n
][f
], chain
->obufc
[f
], &odonec
);
330 if (f
&& (odonec
!= odone_last
)) {
331 lsx_fail("drained asymmetrically!");
336 if (eff_status_c
!= SOX_SUCCESS
)
340 for (i
= 0; i
< odone_last
; ++i
)
341 for (f
= 0; f
< effp
->flows
; ++f
)
342 *obuf
++ = chain
->obufc
[f
][i
];
343 obeg
= f
* odone_last
;
345 #if DEBUG_EFFECTS_CHAIN
346 lsx_report("drain: %5" PRIuPTR
" %5" PRIuPTR
" %5" PRIuPTR
" %5" PRIuPTR
,
347 (size_t)0, pre_odone
, (size_t)0, obeg
);
349 if (!obeg
) /* This is the only thing that drain has and flow hasn't */
354 return effstatus
== SOX_SUCCESS
? SOX_SUCCESS
: SOX_EOF
;
357 /* Flow data through the effects chain until an effect or callback gives EOF */
358 int sox_flow_effects(sox_effects_chain_t
* chain
, int (* callback
)(sox_bool all_done
, void * client_data
), void * client_data
)
360 int flow_status
= SOX_SUCCESS
;
361 size_t e
, source_e
= 0; /* effect indices */
362 size_t f
, max_flows
= 0;
363 sox_bool draining
= sox_true
;
365 for (e
= 0; e
< chain
->length
; ++e
) {
366 chain
->effects
[e
][0].obuf
= lsx_realloc(chain
->effects
[e
][0].obuf
,
367 sox_globals
.bufsiz
* sizeof(chain
->effects
[e
][0].obuf
[0]));
368 /* Possibly there is already a buffer, if this is a used effect;
369 it may still contain samples in that case. */
370 /* Memory will be freed by sox_delete_effect() later. */
371 max_flows
= max(max_flows
, chain
->effects
[e
][0].flows
);
373 if (max_flows
== 1) /* don't need interleave buffers */
375 chain
->ibufc
= lsx_calloc(max_flows
, sizeof(*chain
->ibufc
));
376 chain
->obufc
= lsx_calloc(max_flows
, sizeof(*chain
->obufc
));
377 for (f
= 0; f
< max_flows
; ++f
) {
378 chain
->ibufc
[f
] = lsx_calloc(sox_globals
.bufsiz
/ 2, sizeof(chain
->ibufc
[f
][0]));
379 chain
->obufc
[f
] = lsx_calloc(sox_globals
.bufsiz
/ 2, sizeof(chain
->obufc
[f
][0]));
382 e
= chain
->length
- 1;
383 while (source_e
< chain
->length
) {
384 #define have_imin (e > 0 && e < chain->length && chain->effects[e - 1][0].oend - chain->effects[e - 1][0].obeg >= chain->effects[e][0].imin)
385 size_t osize
= chain
->effects
[e
][0].oend
- chain
->effects
[e
][0].obeg
;
386 if (e
== source_e
&& (draining
|| !have_imin
)) {
387 if (drain_effect(chain
, e
) == SOX_EOF
) {
389 draining
= sox_false
;
391 } else if (have_imin
&& flow_effect(chain
, e
) == SOX_EOF
) {
392 flow_status
= SOX_EOF
;
393 if (e
== chain
->length
- 1)
398 if (e
< chain
->length
&& chain
->effects
[e
][0].oend
- chain
->effects
[e
][0].obeg
> osize
) /* False for output */
400 else if (e
== source_e
)
402 else if ((int)--e
< (int)source_e
)
405 if (callback
&& callback(source_e
== chain
->length
, client_data
) != SOX_SUCCESS
) {
406 flow_status
= SOX_EOF
; /* Client has requested to stop the flow. */
411 for (f
= 0; f
< max_flows
; ++f
) {
412 free(chain
->ibufc
[f
]);
413 free(chain
->obufc
[f
]);
421 sox_uint64_t
sox_effects_clips(sox_effects_chain_t
* chain
)
425 for (i
= 1; i
< chain
->length
- 1; ++i
)
426 for (f
= 0; f
< chain
->effects
[i
][0].flows
; ++f
)
427 clips
+= chain
->effects
[i
][f
].clips
;
431 sox_uint64_t
sox_stop_effect(sox_effect_t
*effp
)
436 for (f
= 0; f
< effp
->flows
; ++f
) {
437 effp
[f
].handler
.stop(&effp
[f
]);
438 clips
+= effp
[f
].clips
;
443 void sox_push_effect_last(sox_effects_chain_t
*chain
, sox_effect_t
*effp
)
445 if (chain
->length
== chain
->table_size
) {
446 chain
->table_size
+= EFF_TABLE_STEP
;
447 lsx_debug_more("sox_push_effect_last: extending effects table, "
448 "new size = %lu", (unsigned long)chain
->table_size
);
449 lsx_revalloc(chain
->effects
, chain
->table_size
);
452 chain
->effects
[chain
->length
++] = effp
;
453 } /* sox_push_effect_last */
455 sox_effect_t
*sox_pop_effect_last(sox_effects_chain_t
*chain
)
457 if (chain
->length
> 0)
461 effp
= chain
->effects
[chain
->length
];
462 chain
->effects
[chain
->length
] = NULL
;
467 } /* sox_pop_effect_last */
469 /* Free resources related to effect.
470 * Note: This currently closes down the effect which might
471 * not be obvious from name.
473 void sox_delete_effect(sox_effect_t
*effp
)
478 if ((clips
= sox_stop_effect(effp
)) != 0)
479 lsx_warn("%s clipped %" PRIu64
" samples; decrease volume?",
480 effp
->handler
.name
, clips
);
481 if (effp
->obeg
!= effp
->oend
)
482 lsx_debug("output buffer still held %" PRIuPTR
" samples; dropped.",
483 (effp
->oend
- effp
->obeg
)/effp
->out_signal
.channels
);
484 /* May or may not indicate a problem; it is normal if the user aborted
485 processing, or if an effect like "trim" stopped early. */
486 effp
->handler
.kill(effp
); /* N.B. only one kill; not one per flow */
487 for (f
= 0; f
< effp
->flows
; ++f
)
493 void sox_delete_effect_last(sox_effects_chain_t
*chain
)
495 if (chain
->length
> 0)
498 sox_delete_effect(chain
->effects
[chain
->length
]);
499 chain
->effects
[chain
->length
] = NULL
;
501 } /* sox_delete_effect_last */
503 /* Remove all effects from the chain.
504 * Note: This currently closes down the effect which might
505 * not be obvious from name.
507 void sox_delete_effects(sox_effects_chain_t
* chain
)
511 for (e
= 0; e
< chain
->length
; ++e
) {
512 sox_delete_effect(chain
->effects
[e
]);
513 chain
->effects
[e
] = NULL
;
518 /*----------------------------- Effects library ------------------------------*/
520 static sox_effect_fn_t s_sox_effect_fns
[] = {
521 #define EFFECT(f) lsx_##f##_effect_fn,
527 const sox_effect_fn_t
*
528 sox_get_effect_fns(void)
530 return s_sox_effect_fns
;
533 /* Find a named effect in the effects library */
534 sox_effect_handler_t
const * sox_find_effect(char const * name
)
537 sox_effect_fn_t
const * fns
= sox_get_effect_fns();
538 for (e
= 0; fns
[e
]; ++e
) {
539 const sox_effect_handler_t
*eh
= fns
[e
] ();
540 if (eh
&& eh
->name
&& strcasecmp(eh
->name
, name
) == 0)
541 return eh
; /* Found it. */