1 /* SoX Effects chain (c) 2007 robs@users.sourceforge.net
2 *
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.
7 *
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.
12 *
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
16 */
17
18 #define LSX_EFF_ALIAS
19 #include "sox_i.h"
20 #include <assert.h>
21 #include <string.h>
22 #ifdef HAVE_STRINGS_H
23 #include <strings.h>
24 #endif
25
26 #define DEBUG_EFFECTS_CHAIN 0
27
28 /* Default effect handler functions for do-nothing situations: */
29
default_function(sox_effect_t * effp UNUSED)30 static int default_function(sox_effect_t * effp UNUSED)
31 {
32 return SOX_SUCCESS;
33 }
34
35 /* Pass through samples verbatim */
lsx_flow_copy(sox_effect_t * effp UNUSED,const sox_sample_t * ibuf,sox_sample_t * obuf,size_t * isamp,size_t * osamp)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)
38 {
39 *isamp = *osamp = min(*isamp, *osamp);
40 memcpy(obuf, ibuf, *isamp * sizeof(*obuf));
41 return SOX_SUCCESS;
42 }
43
44 /* Inform no more samples to drain */
default_drain(sox_effect_t * effp UNUSED,sox_sample_t * obuf UNUSED,size_t * osamp)45 static int default_drain(sox_effect_t * effp UNUSED, sox_sample_t *obuf UNUSED, size_t *osamp)
46 {
47 *osamp = 0;
48 return SOX_EOF;
49 }
50
51 /* Check that no parameters have been given */
default_getopts(sox_effect_t * effp,int argc,char ** argv UNUSED)52 static int default_getopts(sox_effect_t * effp, int argc, char **argv UNUSED)
53 {
54 return --argc? lsx_usage(effp) : SOX_SUCCESS;
55 }
56
57 /* Partially initialise the effect structure; signal info will come later */
sox_create_effect(sox_effect_handler_t const * eh)58 sox_effect_t * sox_create_effect(sox_effect_handler_t const * eh)
59 {
60 sox_effect_t * effp = lsx_calloc(1, sizeof(*effp));
61 effp->obuf = NULL;
62
63 effp->global_info = sox_get_effects_globals();
64 effp->handler = *eh;
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;
71
72 effp->priv = lsx_calloc(1, effp->handler.priv_size);
73
74 return effp;
75 } /* sox_create_effect */
76
sox_effect_options(sox_effect_t * effp,int argc,char * const argv[])77 int sox_effect_options(sox_effect_t *effp, int argc, char * const argv[])
78 {
79 int result;
80
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);
85 free(argv2);
86 return result;
87 } /* sox_effect_options */
88
89 /* Effects chain: */
90
sox_create_effects_chain(sox_encodinginfo_t const * in_enc,sox_encodinginfo_t const * out_enc)91 sox_effects_chain_t * sox_create_effects_chain(
92 sox_encodinginfo_t const * in_enc, sox_encodinginfo_t const * out_enc)
93 {
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;
98 return result;
99 } /* sox_create_effects_chain */
100
sox_delete_effects_chain(sox_effects_chain_t * ecp)101 void sox_delete_effects_chain(sox_effects_chain_t *ecp)
102 {
103 if (ecp && ecp->length)
104 sox_delete_effects(ecp);
105 free(ecp->effects);
106 free(ecp);
107 } /* sox_delete_effects_chain */
108
109 /* Effect can call in start() or flow() to set minimum input size to flow() */
lsx_effect_set_imin(sox_effect_t * effp,size_t imin)110 int lsx_effect_set_imin(sox_effect_t * effp, size_t imin)
111 {
112 if (imin > sox_globals.bufsiz / effp->flows) {
113 lsx_fail("sox_bufsiz not big enough");
114 return SOX_EOF;
115 }
116
117 effp->imin = imin;
118 return SOX_SUCCESS;
119 }
120
121 /* Effects table to be extended in steps of EFF_TABLE_STEP */
122 #define EFF_TABLE_STEP 8
123
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.
129 */
sox_add_effect(sox_effects_chain_t * chain,sox_effect_t * effp,sox_signalinfo_t * in,sox_signalinfo_t const * out)130 int sox_add_effect(sox_effects_chain_t * chain, sox_effect_t * effp, sox_signalinfo_t * in, sox_signalinfo_t const * out)
131 {
132 int ret, (*start)(sox_effect_t * effp) = effp->handler.start;
133 size_t f;
134 sox_effect_t eff0; /* Copy of effect for flow 0 before calling start */
135
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;
150
151 effp->flows =
152 (effp->handler.flags & SOX_EFF_MCHAN)? 1 : effp->in_signal.channels;
153 effp->clips = 0;
154 effp->imin = 0;
155 eff0 = *effp, eff0.priv = lsx_memdup(eff0.priv, eff0.handler.priv_size);
156 eff0.in_signal.mult = NULL; /* Only used in channel 0 */
157 ret = start(effp);
158 if (ret == SOX_EFF_NULL) {
159 lsx_report("has no effect in this configuration");
160 free(eff0.priv);
161 effp->handler.kill(effp);
162 free(effp->priv);
163 effp->priv = NULL;
164 return SOX_SUCCESS;
165 }
166 if (ret != SOX_SUCCESS) {
167 free(eff0.priv);
168 return SOX_EOF;
169 }
170 if (in->mult)
171 lsx_debug("mult=%g", *in->mult);
172
173 if (!(effp->handler.flags & SOX_EFF_LENGTH)) {
174 effp->out_signal.length = in->length;
175 if (effp->out_signal.length != SOX_UNKNOWN_LEN) {
176 if (effp->handler.flags & SOX_EFF_CHAN)
177 effp->out_signal.length =
178 effp->out_signal.length / in->channels * effp->out_signal.channels;
179 if (effp->handler.flags & SOX_EFF_RATE)
180 effp->out_signal.length =
181 effp->out_signal.length / in->rate * effp->out_signal.rate + .5;
182 }
183 }
184
185 *in = effp->out_signal;
186
187 if (chain->length == chain->table_size) {
188 chain->table_size += EFF_TABLE_STEP;
189 lsx_debug_more("sox_add_effect: extending effects table, "
190 "new size = %" PRIuPTR, chain->table_size);
191 lsx_revalloc(chain->effects, chain->table_size);
192 }
193
194 chain->effects[chain->length] =
195 lsx_calloc(effp->flows, sizeof(chain->effects[chain->length][0]));
196 chain->effects[chain->length][0] = *effp;
197
198 for (f = 1; f < effp->flows; ++f) {
199 chain->effects[chain->length][f] = eff0;
200 chain->effects[chain->length][f].flow = f;
201 chain->effects[chain->length][f].priv = lsx_memdup(eff0.priv, eff0.handler.priv_size);
202 if (start(&chain->effects[chain->length][f]) != SOX_SUCCESS) {
203 free(eff0.priv);
204 return SOX_EOF;
205 }
206 }
207
208 ++chain->length;
209 free(eff0.priv);
210 return SOX_SUCCESS;
211 }
212
213 /* An effect's output buffer (effp->obuf) generally has this layout:
214 * |. . . A1A2A3B1B2B3C1C2C3. . . . . . . . . . . . . . . . . . |
215 * ^0 ^obeg ^oend ^bufsiz
216 * (where A1 is the first sample of channel 1, A2 the first sample of
217 * channel 2, etc.), i.e. the channels are interleaved.
218 * However, while sox_flow_effects() is running, output buffers are
219 * adapted to how the following effect expects its input, to avoid
220 * back-and-forth conversions. If the following effect operates on
221 * each of several channels separately (flows > 1), the layout is
222 * changed to this uninterleaved form:
223 * |. A1B1C1. . . . . . . A2B2C2. . . . . . . A3B3C3. . . . . . |
224 * ^0 ^obeg ^oend ^bufsiz
225 * <--- channel 1 ----><--- channel 2 ----><--- channel 3 ---->
226 * The buffer is logically subdivided into channel buffers of size
227 * bufsiz/flows each, starting at offsets 0, bufsiz/flows,
228 * 2*(bufsiz/flows) etc. Within the channel buffers, the data starts
229 * at position obeg/flows and ends before oend/flows. In case bufsiz
230 * is not evenly divisible by flows, there will be an unused area at
231 * the very end of the output buffer.
232 * The interleave() and deinterleave() functions convert between these
233 * two representations.
234 */
235 static void interleave(size_t flows, size_t length, sox_sample_t *from,
236 size_t bufsiz, size_t offset, sox_sample_t *to);
237 static void deinterleave(size_t flows, size_t length, sox_sample_t *from,
238 sox_sample_t *to, size_t bufsiz, size_t offset);
239
flow_effect(sox_effects_chain_t * chain,size_t n)240 static int flow_effect(sox_effects_chain_t * chain, size_t n)
241 {
242 sox_effect_t *effp1 = chain->effects[n - 1];
243 sox_effect_t *effp = chain->effects[n];
244 int effstatus = SOX_SUCCESS;
245 size_t f = 0;
246 size_t idone = effp1->oend - effp1->obeg;
247 size_t obeg = sox_globals.bufsiz - effp->oend;
248 sox_bool il_change = (effp->flows == 1) !=
249 (chain->length == n + 1 || chain->effects[n+1]->flows == 1);
250 #if DEBUG_EFFECTS_CHAIN
251 size_t pre_idone = idone;
252 size_t pre_odone = obeg;
253 #endif
254
255 if (effp->flows == 1) { /* Run effect on all channels at once */
256 idone -= idone % effp->in_signal.channels;
257 effstatus = effp->handler.flow(effp, effp1->obuf + effp1->obeg,
258 il_change ? chain->il_buf : effp->obuf + effp->oend,
259 &idone, &obeg);
260 if (obeg % effp->out_signal.channels != 0) {
261 lsx_fail("multi-channel effect flowed asymmetrically!");
262 effstatus = SOX_EOF;
263 }
264 if (il_change)
265 deinterleave(chain->effects[n+1]->flows, obeg, chain->il_buf,
266 effp->obuf, sox_globals.bufsiz, effp->oend);
267 } else { /* Run effect on each channel individually */
268 sox_sample_t *obuf = il_change ? chain->il_buf : effp->obuf;
269 size_t flow_offs = sox_globals.bufsiz/effp->flows;
270 size_t idone_min = SOX_SIZE_MAX, idone_max = 0;
271 size_t odone_min = SOX_SIZE_MAX, odone_max = 0;
272
273 #ifdef HAVE_OPENMP_3_1
274 #pragma omp parallel for \
275 if(sox_globals.use_threads) \
276 schedule(static) default(none) \
277 shared(effp,effp1,idone,obeg,obuf,flow_offs,chain,n,effstatus) \
278 reduction(min:idone_min,odone_min) reduction(max:idone_max,odone_max)
279 #elif defined HAVE_OPENMP
280 #pragma omp parallel for \
281 if(sox_globals.use_threads) \
282 schedule(static) default(none) \
283 shared(effp,effp1,idone,obeg,obuf,flow_offs,chain,n,effstatus) \
284 firstprivate(idone_min,odone_min,idone_max,odone_max) \
285 lastprivate(idone_min,odone_min,idone_max,odone_max)
286 #endif
287 for (f = 0; f < effp->flows; ++f) {
288 size_t idonec = idone / effp->flows;
289 size_t odonec = obeg / effp->flows;
290 int eff_status_c = effp->handler.flow(&chain->effects[n][f],
291 effp1->obuf + f*flow_offs + effp1->obeg/effp->flows,
292 obuf + f*flow_offs + effp->oend/effp->flows,
293 &idonec, &odonec);
294 idone_min = min(idonec, idone_min); idone_max = max(idonec, idone_max);
295 odone_min = min(odonec, odone_min); odone_max = max(odonec, odone_max);
296
297 if (eff_status_c != SOX_SUCCESS)
298 effstatus = SOX_EOF;
299 }
300
301 if (idone_min != idone_max || odone_min != odone_max) {
302 lsx_fail("flowed asymmetrically!");
303 effstatus = SOX_EOF;
304 }
305 idone = effp->flows * idone_max;
306 obeg = effp->flows * odone_max;
307
308 if (il_change)
309 interleave(effp->flows, obeg, chain->il_buf, sox_globals.bufsiz,
310 effp->oend, effp->obuf + effp->oend);
311 }
312 effp1->obeg += idone;
313 if (effp1->obeg == effp1->oend)
314 effp1->obeg = effp1->oend = 0;
315 else if (effp1->oend - effp1->obeg < effp->imin) { /* Need to refill? */
316 size_t flow_offs = sox_globals.bufsiz/effp->flows;
317 for (f = 0; f < effp->flows; ++f)
318 memcpy(effp1->obuf + f * flow_offs,
319 effp1->obuf + f * flow_offs + effp1->obeg/effp->flows,
320 (effp1->oend - effp1->obeg)/effp->flows * sizeof(*effp1->obuf));
321 effp1->oend -= effp1->obeg;
322 effp1->obeg = 0;
323 }
324
325 effp->oend += obeg;
326
327 #if DEBUG_EFFECTS_CHAIN
328 lsx_report("\t" "flow: %2" PRIuPTR " (%1" PRIuPTR ") "
329 "%5" PRIuPTR " %5" PRIuPTR " %5" PRIuPTR " %5" PRIuPTR " "
330 "%5" PRIuPTR " [%" PRIuPTR "-%" PRIuPTR "]",
331 n, effp->flows, pre_idone, pre_odone, idone, obeg,
332 effp1->oend - effp1->obeg, effp1->obeg, effp1->oend);
333 #endif
334
335 return effstatus == SOX_SUCCESS? SOX_SUCCESS : SOX_EOF;
336 }
337
338 /* The same as flow_effect but with no input */
drain_effect(sox_effects_chain_t * chain,size_t n)339 static int drain_effect(sox_effects_chain_t * chain, size_t n)
340 {
341 sox_effect_t *effp = chain->effects[n];
342 int effstatus = SOX_SUCCESS;
343 size_t f = 0;
344 size_t obeg = sox_globals.bufsiz - effp->oend;
345 sox_bool il_change = (effp->flows == 1) !=
346 (chain->length == n + 1 || chain->effects[n+1]->flows == 1);
347 #if DEBUG_EFFECTS_CHAIN
348 size_t pre_odone = obeg;
349 #endif
350
351 if (effp->flows == 1) { /* Run effect on all channels at once */
352 effstatus = effp->handler.drain(effp,
353 il_change ? chain->il_buf : effp->obuf + effp->oend,
354 &obeg);
355 if (obeg % effp->out_signal.channels != 0) {
356 lsx_fail("multi-channel effect drained asymmetrically!");
357 effstatus = SOX_EOF;
358 }
359 if (il_change)
360 deinterleave(chain->effects[n+1]->flows, obeg, chain->il_buf,
361 effp->obuf, sox_globals.bufsiz, effp->oend);
362 } else { /* Run effect on each channel individually */
363 sox_sample_t *obuf = il_change ? chain->il_buf : effp->obuf;
364 size_t flow_offs = sox_globals.bufsiz/effp->flows;
365 size_t odone_last = 0; /* Initialised to prevent warning */
366
367 for (f = 0; f < effp->flows; ++f) {
368 size_t odonec = obeg / effp->flows;
369 int eff_status_c = effp->handler.drain(&chain->effects[n][f],
370 obuf + f*flow_offs + effp->oend/effp->flows,
371 &odonec);
372 if (f && (odonec != odone_last)) {
373 lsx_fail("drained asymmetrically!");
374 effstatus = SOX_EOF;
375 }
376 odone_last = odonec;
377
378 if (eff_status_c != SOX_SUCCESS)
379 effstatus = SOX_EOF;
380 }
381
382 obeg = effp->flows * odone_last;
383
384 if (il_change)
385 interleave(effp->flows, obeg, chain->il_buf, sox_globals.bufsiz,
386 effp->oend, effp->obuf + effp->oend);
387 }
388 if (!obeg) /* This is the only thing that drain has and flow hasn't */
389 effstatus = SOX_EOF;
390
391 effp->oend += obeg;
392
393 #if DEBUG_EFFECTS_CHAIN
394 lsx_report("\t" "drain: %2" PRIuPTR " (%1" PRIuPTR ") "
395 "%5" PRIuPTR " %5" PRIuPTR " %5" PRIuPTR " %5" PRIuPTR,
396 n, effp->flows, (size_t)0, pre_odone, (size_t)0, obeg);
397 #endif
398
399 return effstatus == SOX_SUCCESS? SOX_SUCCESS : SOX_EOF;
400 }
401
402 /* Flow data through the effects chain until an effect or callback gives EOF */
sox_flow_effects(sox_effects_chain_t * chain,int (* callback)(sox_bool all_done,void * client_data),void * client_data)403 int sox_flow_effects(sox_effects_chain_t * chain, int (* callback)(sox_bool all_done, void * client_data), void * client_data)
404 {
405 int flow_status = SOX_SUCCESS;
406 size_t e, source_e = 0; /* effect indices */
407 size_t max_flows = 0;
408 sox_bool draining = sox_true;
409
410 for (e = 0; e < chain->length; ++e) {
411 sox_effect_t *effp = chain->effects[e];
412 effp->obuf =
413 lsx_realloc(effp->obuf, sox_globals.bufsiz * sizeof(*effp->obuf));
414 /* Memory will be freed by sox_delete_effect() later. */
415 /* Possibly there was already a buffer, if this is a used effect;
416 it may still contain samples in that case. */
417 if (effp->oend > sox_globals.bufsiz) {
418 lsx_warn("buffer size insufficient; buffered samples were dropped");
419 /* can only happen if bufsize has been reduced since the last run */
420 effp->obeg = effp->oend = 0;
421 }
422 max_flows = max(max_flows, effp->flows);
423 }
424 if (max_flows > 1) /* might need interleave buffer */
425 chain->il_buf = lsx_malloc(sox_globals.bufsiz * sizeof(sox_sample_t));
426 else
427 chain->il_buf = NULL;
428
429 /* Go through the effects, and if there are samples in one of the
430 buffers, deinterleave it (if necessary). */
431 for (e = 0; e + 1 < chain->length; e++) {
432 sox_effect_t *effp = chain->effects[e];
433 if (effp->oend > effp->obeg && chain->effects[e+1]->flows > 1) {
434 sox_sample_t *sw = chain->il_buf; chain->il_buf = effp->obuf; effp->obuf = sw;
435 deinterleave(chain->effects[e+1]->flows, effp->oend - effp->obeg,
436 chain->il_buf, effp->obuf, sox_globals.bufsiz, effp->obeg);
437 }
438 }
439
440 e = chain->length - 1;
441 while (source_e < chain->length) {
442 #define have_imin (e > 0 && e < chain->length && chain->effects[e - 1]->oend - chain->effects[e - 1]->obeg >= chain->effects[e]->imin)
443 size_t osize = chain->effects[e]->oend - chain->effects[e]->obeg;
444 if (e == source_e && (draining || !have_imin)) {
445 if (drain_effect(chain, e) == SOX_EOF) {
446 ++source_e;
447 draining = sox_false;
448 }
449 } else if (have_imin && flow_effect(chain, e) == SOX_EOF) {
450 flow_status = SOX_EOF;
451 if (e == chain->length - 1)
452 break;
453 source_e = e;
454 draining = sox_true;
455 }
456 if (e < chain->length && chain->effects[e]->oend - chain->effects[e]->obeg > osize) /* False for output */
457 ++e;
458 else if (e == source_e)
459 draining = sox_true;
460 else if (e < source_e)
461 e = source_e;
462 else
463 --e;
464
465 if (callback && callback(source_e == chain->length, client_data) != SOX_SUCCESS) {
466 flow_status = SOX_EOF; /* Client has requested to stop the flow. */
467 break;
468 }
469 }
470
471 /* If an effect's output buffer still has samples, and if it is
472 uninterleaved, then re-interleave it. Necessary since it might
473 be reused, and at that time possibly followed by an MCHAN effect. */
474 for (e = 0; e + 1 < chain->length; e++) {
475 sox_effect_t *effp = chain->effects[e];
476 if (effp->oend > effp->obeg && chain->effects[e+1]->flows > 1) {
477 sox_sample_t *sw = chain->il_buf; chain->il_buf = effp->obuf; effp->obuf = sw;
478 interleave(chain->effects[e+1]->flows, effp->oend - effp->obeg,
479 chain->il_buf, sox_globals.bufsiz, effp->obeg, effp->obuf);
480 }
481 }
482
483 free(chain->il_buf);
484 return flow_status;
485 }
486
sox_effects_clips(sox_effects_chain_t * chain)487 sox_uint64_t sox_effects_clips(sox_effects_chain_t * chain)
488 {
489 size_t i, f;
490 uint64_t clips = 0;
491 for (i = 1; i < chain->length - 1; ++i)
492 for (f = 0; f < chain->effects[i][0].flows; ++f)
493 clips += chain->effects[i][f].clips;
494 return clips;
495 }
496
sox_stop_effect(sox_effect_t * effp)497 sox_uint64_t sox_stop_effect(sox_effect_t *effp)
498 {
499 size_t f;
500 uint64_t clips = 0;
501
502 for (f = 0; f < effp->flows; ++f) {
503 effp[f].handler.stop(&effp[f]);
504 clips += effp[f].clips;
505 }
506 return clips;
507 }
508
sox_push_effect_last(sox_effects_chain_t * chain,sox_effect_t * effp)509 void sox_push_effect_last(sox_effects_chain_t *chain, sox_effect_t *effp)
510 {
511 if (chain->length == chain->table_size) {
512 chain->table_size += EFF_TABLE_STEP;
513 lsx_debug_more("sox_push_effect_last: extending effects table, "
514 "new size = %" PRIuPTR, chain->table_size);
515 lsx_revalloc(chain->effects, chain->table_size);
516 }
517
518 chain->effects[chain->length++] = effp;
519 } /* sox_push_effect_last */
520
sox_pop_effect_last(sox_effects_chain_t * chain)521 sox_effect_t *sox_pop_effect_last(sox_effects_chain_t *chain)
522 {
523 if (chain->length > 0)
524 {
525 sox_effect_t *effp;
526 chain->length--;
527 effp = chain->effects[chain->length];
528 chain->effects[chain->length] = NULL;
529 return effp;
530 }
531 else
532 return NULL;
533 } /* sox_pop_effect_last */
534
535 /* Free resources related to effect.
536 * Note: This currently closes down the effect which might
537 * not be obvious from name.
538 */
sox_delete_effect(sox_effect_t * effp)539 void sox_delete_effect(sox_effect_t *effp)
540 {
541 uint64_t clips;
542 size_t f;
543
544 if ((clips = sox_stop_effect(effp)) != 0)
545 lsx_warn("%s clipped %" PRIu64 " samples; decrease volume?",
546 effp->handler.name, clips);
547 if (effp->obeg != effp->oend)
548 lsx_debug("output buffer still held %" PRIuPTR " samples; dropped.",
549 (effp->oend - effp->obeg)/effp->out_signal.channels);
550 /* May or may not indicate a problem; it is normal if the user aborted
551 processing, or if an effect like "trim" stopped early. */
552 effp->handler.kill(effp); /* N.B. only one kill; not one per flow */
553 for (f = 0; f < effp->flows; ++f)
554 free(effp[f].priv);
555 free(effp->obuf);
556 free(effp);
557 }
558
sox_delete_effect_last(sox_effects_chain_t * chain)559 void sox_delete_effect_last(sox_effects_chain_t *chain)
560 {
561 if (chain->length > 0)
562 {
563 chain->length--;
564 sox_delete_effect(chain->effects[chain->length]);
565 chain->effects[chain->length] = NULL;
566 }
567 } /* sox_delete_effect_last */
568
569 /* Remove all effects from the chain.
570 * Note: This currently closes down the effect which might
571 * not be obvious from name.
572 */
sox_delete_effects(sox_effects_chain_t * chain)573 void sox_delete_effects(sox_effects_chain_t * chain)
574 {
575 size_t e;
576
577 for (e = 0; e < chain->length; ++e) {
578 sox_delete_effect(chain->effects[e]);
579 chain->effects[e] = NULL;
580 }
581 chain->length = 0;
582 }
583
584 /*----------------------------- Effects library ------------------------------*/
585
586 static sox_effect_fn_t s_sox_effect_fns[] = {
587 #define EFFECT(f) lsx_##f##_effect_fn,
588 #include "effects.h"
589 #undef EFFECT
590 NULL
591 };
592
593 const sox_effect_fn_t*
sox_get_effect_fns(void)594 sox_get_effect_fns(void)
595 {
596 return s_sox_effect_fns;
597 }
598
599 /* Find a named effect in the effects library */
sox_find_effect(char const * name)600 sox_effect_handler_t const * sox_find_effect(char const * name)
601 {
602 int e;
603 sox_effect_fn_t const * fns = sox_get_effect_fns();
604 for (e = 0; fns[e]; ++e) {
605 const sox_effect_handler_t *eh = fns[e] ();
606 if (eh && eh->name && strcasecmp(eh->name, name) == 0)
607 return eh; /* Found it. */
608 }
609 return NULL;
610 }
611
612
613 /*----------------------------- Helper functions -----------------------------*/
614
615 /* interleave() parameters:
616 * flows: number of samples per wide sample
617 * length: number of samples to copy
618 * [pertaining to the (non-interleaved) source buffer:]
619 * from: start address
620 * bufsiz: total size
621 * offset: position at which to start reading
622 * [pertaining to the (interleaved) destination buffer:]
623 * to: start address
624 */
interleave(size_t flows,size_t length,sox_sample_t * from,size_t bufsiz,size_t offset,sox_sample_t * to)625 static void interleave(size_t flows, size_t length, sox_sample_t *from,
626 size_t bufsiz, size_t offset, sox_sample_t *to)
627 {
628 size_t i;
629 const size_t wide_samples = length/flows;
630 const size_t flow_offs = bufsiz/flows;
631 from += offset/flows;
632 for (i = 0; i < wide_samples; i++) {
633 sox_sample_t *inner_from = from + i;
634 sox_sample_t *inner_to = to + i * flows;
635 size_t f;
636 for (f = 0; f < flows; f++) {
637 *inner_to++ = *inner_from;
638 inner_from += flow_offs;
639 }
640 }
641 }
642
643 /* deinterleave() parameters:
644 * flows: number of samples per wide sample
645 * length: number of samples to copy
646 * [pertaining to the (interleaved) source buffer:]
647 * from: start address
648 * [pertaining to the (non-interleaved) destination buffer:]
649 * to: start address
650 * bufsiz: total size
651 * offset: position at which to start writing
652 */
deinterleave(size_t flows,size_t length,sox_sample_t * from,sox_sample_t * to,size_t bufsiz,size_t offset)653 static void deinterleave(size_t flows, size_t length, sox_sample_t *from,
654 sox_sample_t *to, size_t bufsiz, size_t offset)
655 {
656 const size_t wide_samples = length/flows;
657 const size_t flow_offs = bufsiz/flows;
658 size_t f;
659 to += offset/flows;
660 for (f = 0; f < flows; f++) {
661 sox_sample_t *inner_to = to + f*flow_offs;
662 sox_sample_t *inner_from = from + f;
663 size_t i = wide_samples;
664 while (i--) {
665 *inner_to++ = *inner_from;
666 inner_from += flows;
667 }
668 }
669 }
670