rate: add some sanity checking
[sox.git] / src / mcompand.c
blob5682c5e2b0deea8436e96a349d905dd0f73f2ff2
1 /* multiband compander effect for SoX
2 * by Daniel Pouzzner <douzzer@mega.nu> 2002-Oct-8
4 * Compander code adapted from the SoX compand effect, by Nick Bailey
6 * SoX is Copyright 1999 Chris Bagwell And Nick Bailey This source code is
7 * freely redistributable and may be used for any purpose. This copyright
8 * notice must be maintained. Chris Bagwell And Nick Bailey are not
9 * responsible for the consequences of using this software.
12 * Usage:
13 * mcompand quoted_compand_args [crossover_frequency
14 * quoted_compand_args [...]]
16 * quoted_compand_args are as for the compand effect:
18 * attack1,decay1[,attack2,decay2...]
19 * in-dB1,out-dB1[,in-dB2,out-dB2...]
20 * [ gain [ initial-volume [ delay ] ] ]
22 * Beware a variety of headroom (clipping) bugaboos.
24 * Implementation details:
25 * The input is divided into bands using 4th order Linkwitz-Riley IIRs.
26 * This is akin to the crossover of a loudspeaker, and results in flat
27 * frequency response absent compander action.
29 * The outputs of the array of companders is summed, and sample truncation
30 * is done on the final sum.
32 * Modifications to the predictive compression code properly maintain
33 * alignment of the outputs of the array of companders when the companders
34 * have different prediction intervals (volume application delays). Note
35 * that the predictive mode of the limiter needs some TLC - in fact, a
36 * rewrite - since what's really useful is to assure that a waveform won't
37 * be clipped, by slewing the volume in advance so that the peak is at
38 * limit (or below, if there's a higher subsequent peak visible in the
39 * lookahead window) once it's reached. */
41 #ifdef NDEBUG /* Enable assert always. */
42 #undef NDEBUG /* Must undef above assert.h or other that might include it. */
43 #endif
45 #include "sox_i.h"
46 #include <assert.h>
47 #include <string.h>
48 #include <stdlib.h>
49 #include "compandt.h"
50 #include "mcompand_xover.h"
52 typedef struct {
53 sox_compandt_t transfer_fn;
55 size_t expectedChannels; /* Also flags that channels aren't to be treated
56 individually when = 1 and input not mono */
57 double *attackRate; /* An array of attack rates */
58 double *decayRate; /* ... and of decay rates */
59 double *volume; /* Current "volume" of each channel */
60 double delay; /* Delay to apply before companding */
61 double topfreq; /* upper bound crossover frequency */
62 crossover_t filter;
63 sox_sample_t *delay_buf; /* Old samples, used for delay processing */
64 size_t delay_size; /* lookahead for this band (in samples) - function of delay, above */
65 ptrdiff_t delay_buf_ptr; /* Index into delay_buf */
66 size_t delay_buf_cnt; /* No. of active entries in delay_buf */
67 } comp_band_t;
69 typedef struct {
70 size_t nBands;
71 sox_sample_t *band_buf1, *band_buf2, *band_buf3;
72 size_t band_buf_len;
73 size_t delay_buf_size;/* Size of delay_buf in samples */
74 comp_band_t *bands;
76 char *arg; /* copy of current argument */
77 } priv_t;
80 * Process options
82 * Don't do initialization now.
83 * The 'info' fields are not yet filled in.
85 static int sox_mcompand_getopts_1(comp_band_t * l, size_t n, char **argv)
87 char *s;
88 size_t rates, i, commas;
90 /* Start by checking the attack and decay rates */
92 for (s = argv[0], commas = 0; *s; ++s)
93 if (*s == ',') ++commas;
95 if (commas % 2 == 0) /* There must be an even number of
96 attack/decay parameters */
98 lsx_fail("compander: Odd number of attack & decay rate parameters");
99 return (SOX_EOF);
102 rates = 1 + commas/2;
103 l->attackRate = lsx_malloc(sizeof(double) * rates);
104 l->decayRate = lsx_malloc(sizeof(double) * rates);
105 l->volume = lsx_malloc(sizeof(double) * rates);
106 l->expectedChannels = rates;
107 l->delay_buf = NULL;
109 /* Now tokenise the rates string and set up these arrays. Keep
110 them in seconds at the moment: we don't know the sample rate yet. */
112 s = strtok(argv[0], ","); i = 0;
113 do {
114 l->attackRate[i] = atof(s); s = strtok(NULL, ",");
115 l->decayRate[i] = atof(s); s = strtok(NULL, ",");
116 ++i;
117 } while (s != NULL);
119 if (!lsx_compandt_parse(&l->transfer_fn, argv[1], n>2 ? argv[2] : 0))
120 return SOX_EOF;
122 /* Set the initial "volume" to be attibuted to the input channels.
123 Unless specified, choose 1.0 (maximum) otherwise clipping will
124 result if the user has seleced a long attack time */
125 for (i = 0; i < l->expectedChannels; ++i) {
126 double v = n>=4 ? pow(10.0, atof(argv[3])/20) : 1.0;
127 l->volume[i] = v;
129 /* If there is a delay, store it. */
130 if (n >= 5) l->delay = atof(argv[4]);
131 else l->delay = 0.0;
133 return (SOX_SUCCESS);
136 static int parse_subarg(char *s, char **subargv, size_t *subargc) {
137 char **ap;
138 char *s_p;
140 s_p = s;
141 *subargc = 0;
142 for (ap = subargv; (*ap = strtok(s_p, " \t")) != NULL;) {
143 s_p = NULL;
144 if (*subargc == 5) {
145 ++*subargc;
146 break;
148 if (**ap != '\0') {
149 ++ap;
150 ++*subargc;
154 if (*subargc < 2 || *subargc > 5)
156 lsx_fail("Wrong number of parameters for the compander effect within mcompand; usage:\n"
157 "\tattack1,decay1{,attack2,decay2} [soft-knee-dB:]in-dB1[,out-dB1]{,in-dB2,out-dB2} [gain [initial-volume-dB [delay]]]\n"
158 "\twhere {} means optional and repeatable and [] means optional.\n"
159 "\tdB values are floating point or -inf'; times are in seconds.");
160 return (SOX_EOF);
161 } else
162 return SOX_SUCCESS;
165 static int getopts(sox_effect_t * effp, int argc, char **argv)
167 char *subargv[6], *cp;
168 size_t subargc, i;
170 priv_t * c = (priv_t *) effp->priv;
171 --argc, ++argv;
173 c->band_buf1 = c->band_buf2 = c->band_buf3 = 0;
174 c->band_buf_len = 0;
176 /* how many bands? */
177 if (! (argc&1)) {
178 lsx_fail("mcompand accepts only an odd number of arguments:\argc"
179 " mcompand quoted_compand_args [crossover_freq quoted_compand_args [...]]");
180 return SOX_EOF;
182 c->nBands = (argc+1)>>1;
184 c->bands = lsx_calloc(c->nBands, sizeof(comp_band_t));
186 for (i=0;i<c->nBands;++i) {
187 c->arg = lsx_strdup(argv[i<<1]);
188 if (parse_subarg(c->arg,subargv,&subargc) != SOX_SUCCESS)
189 return SOX_EOF;
190 if (sox_mcompand_getopts_1(&c->bands[i], subargc, &subargv[0]) != SOX_SUCCESS)
191 return SOX_EOF;
192 free(c->arg);
193 c->arg = NULL;
194 if (i == (c->nBands-1))
195 c->bands[i].topfreq = 0;
196 else {
197 c->bands[i].topfreq = lsx_parse_frequency(argv[(i<<1)+1],&cp);
198 if (*cp) {
199 lsx_fail("bad frequency in args to mcompand");
200 return SOX_EOF;
202 if ((i>0) && (c->bands[i].topfreq < c->bands[i-1].topfreq)) {
203 lsx_fail("mcompand crossover frequencies must be in ascending order.");
204 return SOX_EOF;
209 return SOX_SUCCESS;
213 * Prepare processing.
214 * Do all initializations.
216 static int start(sox_effect_t * effp)
218 priv_t * c = (priv_t *) effp->priv;
219 comp_band_t * l;
220 size_t i;
221 size_t band;
223 for (band=0;band<c->nBands;++band) {
224 l = &c->bands[band];
225 l->delay_size = c->bands[band].delay * effp->out_signal.rate * effp->out_signal.channels;
226 if (l->delay_size > c->delay_buf_size)
227 c->delay_buf_size = l->delay_size;
230 for (band=0;band<c->nBands;++band) {
231 l = &c->bands[band];
232 /* Convert attack and decay rates using number of samples */
234 for (i = 0; i < l->expectedChannels; ++i) {
235 if (l->attackRate[i] > 1.0/effp->out_signal.rate)
236 l->attackRate[i] = 1.0 -
237 exp(-1.0/(effp->out_signal.rate * l->attackRate[i]));
238 else
239 l->attackRate[i] = 1.0;
240 if (l->decayRate[i] > 1.0/effp->out_signal.rate)
241 l->decayRate[i] = 1.0 -
242 exp(-1.0/(effp->out_signal.rate * l->decayRate[i]));
243 else
244 l->decayRate[i] = 1.0;
247 /* Allocate the delay buffer */
248 if (c->delay_buf_size > 0)
249 l->delay_buf = lsx_calloc(sizeof(long), c->delay_buf_size);
250 l->delay_buf_ptr = 0;
251 l->delay_buf_cnt = 0;
253 if (l->topfreq != 0)
254 crossover_setup(effp, &l->filter, l->topfreq);
256 return (SOX_SUCCESS);
260 * Update a volume value using the given sample
261 * value, the attack rate and decay rate
264 static void doVolume(double *v, double samp, comp_band_t * l, size_t chan)
266 double s = samp/(~((sox_sample_t)1<<31));
267 double delta = s - *v;
269 if (delta > 0.0) /* increase volume according to attack rate */
270 *v += delta * l->attackRate[chan];
271 else /* reduce volume according to decay rate */
272 *v += delta * l->decayRate[chan];
275 static int sox_mcompand_flow_1(sox_effect_t * effp, priv_t * c, comp_band_t * l, const sox_sample_t *ibuf, sox_sample_t *obuf, size_t len, size_t filechans)
277 size_t idone, odone;
279 for (idone = 0, odone = 0; idone < len; ibuf += filechans) {
280 size_t chan;
282 /* Maintain the volume fields by simulating a leaky pump circuit */
284 if (l->expectedChannels == 1 && filechans > 1) {
285 /* User is expecting same compander for all channels */
286 double maxsamp = 0.0;
287 for (chan = 0; chan < filechans; ++chan) {
288 double rect = fabs((double)ibuf[chan]);
289 if (rect > maxsamp)
290 maxsamp = rect;
292 doVolume(&l->volume[0], maxsamp, l, (size_t) 0);
293 } else {
294 for (chan = 0; chan < filechans; ++chan)
295 doVolume(&l->volume[chan], fabs((double)ibuf[chan]), l, chan);
298 /* Volume memory is updated: perform compand */
299 for (chan = 0; chan < filechans; ++chan) {
300 int ch = l->expectedChannels > 1 ? chan : 0;
301 double level_in_lin = l->volume[ch];
302 double level_out_lin = lsx_compandt(&l->transfer_fn, level_in_lin);
303 double checkbuf;
305 if (c->delay_buf_size <= 0) {
306 checkbuf = ibuf[chan] * level_out_lin;
307 SOX_SAMPLE_CLIP_COUNT(checkbuf, effp->clips);
308 obuf[odone++] = checkbuf;
309 idone++;
310 } else {
311 /* FIXME: note that this lookahead algorithm is really lame:
312 the response to a peak is released before the peak
313 arrives. */
315 /* because volume application delays differ band to band, but
316 total delay doesn't, the volume is applied in an iteration
317 preceding that in which the sample goes to obuf, except in
318 the band(s) with the longest vol app delay.
320 the offset between delay_buf_ptr and the sample to apply
321 vol to, is a constant equal to the difference between this
322 band's delay and the longest delay of all the bands. */
324 if (l->delay_buf_cnt >= l->delay_size) {
325 checkbuf = l->delay_buf[(l->delay_buf_ptr + c->delay_buf_size - l->delay_size)%c->delay_buf_size] * level_out_lin;
326 SOX_SAMPLE_CLIP_COUNT(checkbuf, effp->clips);
327 l->delay_buf[(l->delay_buf_ptr + c->delay_buf_size - l->delay_size)%c->delay_buf_size] = checkbuf;
329 if (l->delay_buf_cnt >= c->delay_buf_size) {
330 obuf[odone] = l->delay_buf[l->delay_buf_ptr];
331 odone++;
332 idone++;
333 } else {
334 l->delay_buf_cnt++;
335 idone++; /* no "odone++" because we did not fill obuf[...] */
337 l->delay_buf[l->delay_buf_ptr++] = ibuf[chan];
338 l->delay_buf_ptr %= c->delay_buf_size;
343 if (idone != odone || idone != len) {
344 /* Emergency brake - will lead to memory corruption otherwise since we
345 cannot report back to flow() how many samples were consumed/emitted.
346 Additionally, flow() doesn't know how to handle diverging
347 sub-compander delays. */
348 lsx_fail("Using a compander delay within mcompand is currently not supported");
349 exit(1);
350 /* FIXME */
353 return (SOX_SUCCESS);
357 * Processed signed long samples from ibuf to obuf.
358 * Return number of samples processed.
360 static int flow(sox_effect_t * effp, const sox_sample_t *ibuf, sox_sample_t *obuf,
361 size_t *isamp, size_t *osamp) {
362 priv_t * c = (priv_t *) effp->priv;
363 comp_band_t * l;
364 size_t len = min(*isamp, *osamp);
365 size_t band, i;
366 sox_sample_t *abuf, *bbuf, *cbuf, *oldabuf, *ibuf_copy;
367 double out;
369 if (c->band_buf_len < len) {
370 c->band_buf1 = lsx_realloc(c->band_buf1,len*sizeof(sox_sample_t));
371 c->band_buf2 = lsx_realloc(c->band_buf2,len*sizeof(sox_sample_t));
372 c->band_buf3 = lsx_realloc(c->band_buf3,len*sizeof(sox_sample_t));
373 c->band_buf_len = len;
376 len -= len % effp->out_signal.channels;
378 ibuf_copy = lsx_malloc(*isamp * sizeof(sox_sample_t));
379 memcpy(ibuf_copy, ibuf, *isamp * sizeof(sox_sample_t));
381 /* split ibuf into bands using filters, pipe each band through sox_mcompand_flow_1, then add back together and write to obuf */
383 memset(obuf,0,len * sizeof *obuf);
384 for (band=0,abuf=ibuf_copy,bbuf=c->band_buf2,cbuf=c->band_buf1;band<c->nBands;++band) {
385 l = &c->bands[band];
387 if (l->topfreq)
388 crossover_flow(effp, &l->filter, abuf, bbuf, cbuf, len);
389 else {
390 bbuf = abuf;
391 abuf = cbuf;
393 if (abuf == ibuf_copy)
394 abuf = c->band_buf3;
395 (void)sox_mcompand_flow_1(effp, c,l,bbuf,abuf,len, (size_t)effp->out_signal.channels);
396 for (i=0;i<len;++i)
398 out = (double)obuf[i] + (double)abuf[i];
399 SOX_SAMPLE_CLIP_COUNT(out, effp->clips);
400 obuf[i] = out;
402 oldabuf = abuf;
403 abuf = cbuf;
404 cbuf = oldabuf;
407 *isamp = *osamp = len;
409 free(ibuf_copy);
411 return SOX_SUCCESS;
414 static int sox_mcompand_drain_1(sox_effect_t * effp, priv_t * c, comp_band_t * l, sox_sample_t *obuf, size_t maxdrain)
416 size_t done;
417 double out;
420 * Drain out delay samples. Note that this loop does all channels.
422 for (done = 0; done < maxdrain && l->delay_buf_cnt > 0; done++) {
423 out = obuf[done] + l->delay_buf[l->delay_buf_ptr++];
424 SOX_SAMPLE_CLIP_COUNT(out, effp->clips);
425 obuf[done] = out;
426 l->delay_buf_ptr %= c->delay_buf_size;
427 l->delay_buf_cnt--;
430 /* tell caller number of samples played */
431 return done;
436 * Drain out compander delay lines.
438 static int drain(sox_effect_t * effp, sox_sample_t *obuf, size_t *osamp)
440 size_t band, drained, mostdrained = 0;
441 priv_t * c = (priv_t *)effp->priv;
442 comp_band_t * l;
444 *osamp -= *osamp % effp->out_signal.channels;
446 memset(obuf,0,*osamp * sizeof *obuf);
447 for (band=0;band<c->nBands;++band) {
448 l = &c->bands[band];
449 drained = sox_mcompand_drain_1(effp, c,l,obuf,*osamp);
450 if (drained > mostdrained)
451 mostdrained = drained;
454 *osamp = mostdrained;
456 if (mostdrained)
457 return SOX_SUCCESS;
458 else
459 return SOX_EOF;
463 * Clean up compander effect.
465 static int stop(sox_effect_t * effp)
467 priv_t * c = (priv_t *) effp->priv;
468 comp_band_t * l;
469 size_t band;
471 free(c->band_buf1);
472 c->band_buf1 = NULL;
473 free(c->band_buf2);
474 c->band_buf2 = NULL;
475 free(c->band_buf3);
476 c->band_buf3 = NULL;
478 for (band = 0; band < c->nBands; band++) {
479 l = &c->bands[band];
480 free(l->delay_buf);
481 if (l->topfreq != 0)
482 free(l->filter.previous);
485 return SOX_SUCCESS;
488 static int lsx_kill(sox_effect_t * effp)
490 priv_t * c = (priv_t *) effp->priv;
491 comp_band_t * l;
492 size_t band;
494 for (band = 0; band < c->nBands; band++) {
495 l = &c->bands[band];
496 lsx_compandt_kill(&l->transfer_fn);
497 free(l->decayRate);
498 free(l->attackRate);
499 free(l->volume);
501 free(c->arg);
502 free(c->bands);
503 c->bands = NULL;
505 return SOX_SUCCESS;
508 const sox_effect_handler_t *lsx_mcompand_effect_fn(void)
510 static sox_effect_handler_t handler = {
511 "mcompand",
512 "quoted_compand_args [crossover_frequency[k] quoted_compand_args [...]]\n"
513 "\n"
514 "quoted_compand_args are as for the compand effect:\n"
515 "\n"
516 " attack1,decay1[,attack2,decay2...]\n"
517 " in-dB1,out-dB1[,in-dB2,out-dB2...]\n"
518 " [ gain [ initial-volume [ delay ] ] ]",
519 SOX_EFF_MCHAN | SOX_EFF_GAIN,
520 getopts, start, flow, drain, stop, lsx_kill, sizeof(priv_t)
523 return &handler;