1 /* libSoX compander effect
3 * Written by Nick Bailey (nick@bailey-family.org.uk or
4 * n.bailey@elec.gla.ac.uk)
6 * Copyright 1999 Chris Bagwell And Nick Bailey
7 * This source code is freely redistributable and may be used for
8 * any purpose. This copyright notice must be maintained.
9 * Chris Bagwell And Nick Bailey are not responsible for
10 * the consequences of using this software.
20 * Compressor/expander effect for libSoX.
22 * Flow diagram for one channel:
24 * ------------ ---------------
26 * ibuff ---+---| integrator |--->| transfer func |--->| |
28 * | ------------ --------------- | | * gain
29 * | | * |----------->obuff
32 * +----->| delay |-------------------------->| |
36 #define compand_usage \
37 "attack1,decay1{,attack2,decay2} [soft-knee-dB:]in-dB1[,out-dB1]{,in-dB2,out-dB2} [gain [initial-volume-dB [delay]]]\n" \
38 "\twhere {} means optional and repeatable and [] means optional.\n" \
39 "\tdB values are floating point or -inf'; times are in seconds."
41 * Note: clipping can occur if the transfer function pushes things too
42 * close to 0 dB. In that case, use a negative gain, or reduce the
43 * output level of the transfer function.
47 sox_compandt_t transfer_fn
;
50 double attack_times
[2]; /* 0:attack_time, 1:decay_time */
51 double volume
; /* Current "volume" of each channel */
53 unsigned expectedChannels
;/* Also flags that channels aren't to be treated
54 individually when = 1 and input not mono */
55 double delay
; /* Delay to apply before companding */
56 sox_sample_t
*delay_buf
; /* Old samples, used for delay processing */
57 ptrdiff_t delay_buf_size
;/* Size of delay_buf in samples */
58 ptrdiff_t delay_buf_index
; /* Index into delay_buf */
59 ptrdiff_t delay_buf_cnt
; /* No. of active entries in delay_buf */
60 int delay_buf_full
; /* Shows buffer situation (important for drain) */
62 char *arg0
; /* copies of arguments, so that they may be modified */
67 static int getopts(sox_effect_t
* effp
, int argc
, char * * argv
)
69 priv_t
* l
= (priv_t
*) effp
->priv
;
71 char dummy
; /* To check for extraneous chars. */
72 unsigned pairs
, i
, j
, commas
;
75 if (argc
< 2 || argc
> 5)
76 return lsx_usage(effp
);
78 l
->arg0
= lsx_strdup(argv
[0]);
79 l
->arg1
= lsx_strdup(argv
[1]);
80 l
->arg2
= argc
> 2 ? lsx_strdup(argv
[2]) : NULL
;
82 /* Start by checking the attack and decay rates */
83 for (s
= l
->arg0
, commas
= 0; *s
; ++s
) if (*s
== ',') ++commas
;
84 if ((commas
% 2) == 0) {
85 lsx_fail("there must be an even number of attack/decay parameters");
89 l
->channels
= lsx_calloc(pairs
, sizeof(*l
->channels
));
90 l
->expectedChannels
= pairs
;
92 /* Now tokenise the rates string and set up these arrays. Keep
93 them in seconds at the moment: we don't know the sample rate yet. */
94 for (i
= 0, s
= strtok(l
->arg0
, ","); s
!= NULL
; ++i
) {
95 for (j
= 0; j
< 2; ++j
) {
96 if (sscanf(s
, "%lf %c", &l
->channels
[i
].attack_times
[j
], &dummy
) != 1) {
97 lsx_fail("syntax error trying to read attack/decay time");
99 } else if (l
->channels
[i
].attack_times
[j
] < 0) {
100 lsx_fail("attack & decay times can't be less than 0 seconds");
103 s
= strtok(NULL
, ",");
107 if (!lsx_compandt_parse(&l
->transfer_fn
, l
->arg1
, l
->arg2
))
110 /* Set the initial "volume" to be attibuted to the input channels.
111 Unless specified, choose 0dB otherwise clipping will
112 result if the user has seleced a long attack time */
113 for (i
= 0; i
< l
->expectedChannels
; ++i
) {
114 double init_vol_dB
= 0;
115 if (argc
> 3 && sscanf(argv
[3], "%lf %c", &init_vol_dB
, &dummy
) != 1) {
116 lsx_fail("syntax error trying to read initial volume");
118 } else if (init_vol_dB
> 0) {
119 lsx_fail("initial volume is relative to maximum volume so can't exceed 0dB");
122 l
->channels
[i
].volume
= pow(10., init_vol_dB
/ 20);
125 /* If there is a delay, store it. */
126 if (argc
> 4 && sscanf(argv
[4], "%lf %c", &l
->delay
, &dummy
) != 1) {
127 lsx_fail("syntax error trying to read delay value");
129 } else if (l
->delay
< 0) {
130 lsx_fail("delay can't be less than 0 seconds");
137 static int start(sox_effect_t
* effp
)
139 priv_t
* l
= (priv_t
*) effp
->priv
;
142 lsx_debug("%i input channel(s) expected: actually %i",
143 l
->expectedChannels
, effp
->out_signal
.channels
);
144 for (i
= 0; i
< l
->expectedChannels
; ++i
)
145 lsx_debug("Channel %i: attack = %g decay = %g", i
,
146 l
->channels
[i
].attack_times
[0], l
->channels
[i
].attack_times
[1]);
147 if (!lsx_compandt_show(&l
->transfer_fn
, effp
->global_info
->plot
))
150 /* Convert attack and decay rates using number of samples */
151 for (i
= 0; i
< l
->expectedChannels
; ++i
)
152 for (j
= 0; j
< 2; ++j
)
153 if (l
->channels
[i
].attack_times
[j
] > 1.0/effp
->out_signal
.rate
)
154 l
->channels
[i
].attack_times
[j
] = 1.0 -
155 exp(-1.0/(effp
->out_signal
.rate
* l
->channels
[i
].attack_times
[j
]));
157 l
->channels
[i
].attack_times
[j
] = 1.0;
159 /* Allocate the delay buffer */
160 l
->delay_buf_size
= l
->delay
* effp
->out_signal
.rate
* effp
->out_signal
.channels
;
161 if (l
->delay_buf_size
> 0)
162 l
->delay_buf
= lsx_calloc((size_t)l
->delay_buf_size
, sizeof(*l
->delay_buf
));
163 l
->delay_buf_index
= 0;
164 l
->delay_buf_cnt
= 0;
165 l
->delay_buf_full
= 0;
171 * Update a volume value using the given sample
172 * value, the attack rate and decay rate
174 static void doVolume(double *v
, double samp
, priv_t
* l
, int chan
)
176 double s
= -samp
/ SOX_SAMPLE_MIN
;
177 double delta
= s
- *v
;
179 if (delta
> 0.0) /* increase volume according to attack rate */
180 *v
+= delta
* l
->channels
[chan
].attack_times
[0];
181 else /* reduce volume according to decay rate */
182 *v
+= delta
* l
->channels
[chan
].attack_times
[1];
185 static int flow(sox_effect_t
* effp
, const sox_sample_t
*ibuf
, sox_sample_t
*obuf
,
186 size_t *isamp
, size_t *osamp
)
188 priv_t
* l
= (priv_t
*) effp
->priv
;
189 int len
= (*isamp
> *osamp
) ? *osamp
: *isamp
;
190 int filechans
= effp
->out_signal
.channels
;
193 for (idone
= 0,odone
= 0; idone
< len
; ibuf
+= filechans
) {
196 /* Maintain the volume fields by simulating a leaky pump circuit */
197 for (chan
= 0; chan
< filechans
; ++chan
) {
198 if (l
->expectedChannels
== 1 && filechans
> 1) {
199 /* User is expecting same compander for all channels */
201 double maxsamp
= 0.0;
202 for (i
= 0; i
< filechans
; ++i
) {
203 double rect
= fabs((double)ibuf
[i
]);
204 if (rect
> maxsamp
) maxsamp
= rect
;
206 doVolume(&l
->channels
[0].volume
, maxsamp
, l
, 0);
209 doVolume(&l
->channels
[chan
].volume
, fabs((double)ibuf
[chan
]), l
, chan
);
212 /* Volume memory is updated: perform compand */
213 for (chan
= 0; chan
< filechans
; ++chan
) {
214 int ch
= l
->expectedChannels
> 1 ? chan
: 0;
215 double level_in_lin
= l
->channels
[ch
].volume
;
216 double level_out_lin
= lsx_compandt(&l
->transfer_fn
, level_in_lin
);
219 if (l
->delay_buf_size
<= 0) {
220 checkbuf
= ibuf
[chan
] * level_out_lin
;
221 SOX_SAMPLE_CLIP_COUNT(checkbuf
, effp
->clips
);
222 obuf
[odone
++] = checkbuf
;
225 if (l
->delay_buf_cnt
>= l
->delay_buf_size
) {
226 l
->delay_buf_full
=1; /* delay buffer is now definitely full */
227 checkbuf
= l
->delay_buf
[l
->delay_buf_index
] * level_out_lin
;
228 SOX_SAMPLE_CLIP_COUNT(checkbuf
, effp
->clips
);
229 obuf
[odone
] = checkbuf
;
234 idone
++; /* no "odone++" because we did not fill obuf[...] */
236 l
->delay_buf
[l
->delay_buf_index
++] = ibuf
[chan
];
237 l
->delay_buf_index
%= l
->delay_buf_size
;
242 *isamp
= idone
; *osamp
= odone
;
243 return (SOX_SUCCESS
);
246 static int drain(sox_effect_t
* effp
, sox_sample_t
*obuf
, size_t *osamp
)
248 priv_t
* l
= (priv_t
*) effp
->priv
;
249 size_t chan
, done
= 0;
251 if (l
->delay_buf_full
== 0)
252 l
->delay_buf_index
= 0;
253 while (done
+effp
->out_signal
.channels
<= *osamp
&& l
->delay_buf_cnt
> 0)
254 for (chan
= 0; chan
< effp
->out_signal
.channels
; ++chan
) {
255 int c
= l
->expectedChannels
> 1 ? chan
: 0;
256 double level_in_lin
= l
->channels
[c
].volume
;
257 double level_out_lin
= lsx_compandt(&l
->transfer_fn
, level_in_lin
);
258 obuf
[done
++] = l
->delay_buf
[l
->delay_buf_index
++] * level_out_lin
;
259 l
->delay_buf_index
%= l
->delay_buf_size
;
263 return l
->delay_buf_cnt
> 0 ? SOX_SUCCESS
: SOX_EOF
;
266 static int stop(sox_effect_t
* effp
)
268 priv_t
* l
= (priv_t
*) effp
->priv
;
274 static int lsx_kill(sox_effect_t
* effp
)
276 priv_t
* l
= (priv_t
*) effp
->priv
;
278 lsx_compandt_kill(&l
->transfer_fn
);
286 sox_effect_handler_t
const * lsx_compand_effect_fn(void)
288 static sox_effect_handler_t handler
= {
289 "compand", compand_usage
, SOX_EFF_MCHAN
| SOX_EFF_GAIN
,
290 getopts
, start
, flow
, drain
, stop
, lsx_kill
, sizeof(priv_t
)