1 /* libSoX effect: gain/norm/etc. (c) 2008-9 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
24 sox_bool do_equalise
, do_balance
, do_balance_no_clip
, do_limiter
;
25 sox_bool do_restore
, make_headroom
, do_normalise
, do_scan
;
26 double fixed_gain
; /* Valid only in channel 0 */
28 double mult
, reclaim
, rms
, limiter
;
30 sox_sample_t min
, max
;
34 static int create(sox_effect_t
* effp
, int argc
, char * * argv
)
36 priv_t
* p
= (priv_t
*)effp
->priv
;
38 for (--argc
, ++argv
; argc
&& **argv
== '-' && argv
[0][1] &&
39 !isdigit((unsigned char)argv
[0][1]) && argv
[0][1] != '.'; --argc
, ++argv
)
40 for (q
= &argv
[0][1]; *q
; ++q
) switch (*q
) {
41 case 'n': p
->do_scan
= p
->do_normalise
= sox_true
; break;
42 case 'e': p
->do_scan
= p
->do_equalise
= sox_true
; break;
43 case 'B': p
->do_scan
= p
->do_balance
= sox_true
; break;
44 case 'b': p
->do_scan
= p
->do_balance_no_clip
= sox_true
; break;
45 case 'r': p
->do_scan
= p
->do_restore
= sox_true
; break;
46 case 'h': p
->make_headroom
= sox_true
; break;
47 case 'l': p
->do_limiter
= sox_true
; break;
48 default: lsx_fail("invalid option `-%c'", *q
); return lsx_usage(effp
);
50 if ((p
->do_equalise
+ p
->do_balance
+ p
->do_balance_no_clip
+ p
->do_restore
)/ sox_true
> 1) {
51 lsx_fail("only one of -e, -B, -b, -r may be given");
54 if (p
->do_normalise
&& p
->do_restore
) {
55 lsx_fail("only one of -n, -r may be given");
58 if (p
->do_limiter
&& p
->make_headroom
) {
59 lsx_fail("only one of -l, -h may be given");
62 do {NUMERIC_PARAMETER(fixed_gain
, -HUGE_VAL
, HUGE_VAL
)} while (0);
63 p
->fixed_gain
= dB_to_linear(p
->fixed_gain
);
64 return argc
? lsx_usage(effp
) : SOX_SUCCESS
;
67 static int start(sox_effect_t
* effp
)
69 priv_t
* p
= (priv_t
*)effp
->priv
;
71 if (effp
->flow
== 0) {
73 if (!effp
->in_signal
.mult
|| *effp
->in_signal
.mult
>= 1) {
74 lsx_fail("can't reclaim headroom");
77 p
->reclaim
= 1 / *effp
->in_signal
.mult
;
79 effp
->out_signal
.mult
= p
->make_headroom
? &p
->fixed_gain
: NULL
;
80 if (!p
->do_equalise
&& !p
->do_balance
&& !p
->do_balance_no_clip
)
81 effp
->flows
= 1; /* essentially a conditional SOX_EFF_MCHAN */
87 p
->tmp_file
= lsx_tmpfile();
88 if (p
->tmp_file
== NULL
) {
89 lsx_fail("can't create temporary file: %s", strerror(errno
));
94 p
->limiter
= (1 - 1 / p
->fixed_gain
) * (1. / SOX_SAMPLE_MAX
);
95 else if (p
->fixed_gain
== floor(p
->fixed_gain
) && !p
->do_scan
)
96 effp
->out_signal
.precision
= effp
->in_signal
.precision
;
100 static int flow(sox_effect_t
* effp
, const sox_sample_t
* ibuf
,
101 sox_sample_t
* obuf
, size_t * isamp
, size_t * osamp
)
103 priv_t
* p
= (priv_t
*)effp
->priv
;
107 if (fwrite(ibuf
, sizeof(*ibuf
), *isamp
, p
->tmp_file
) != *isamp
) {
108 lsx_fail("error writing temporary file: %s", strerror(errno
));
111 if (p
->do_balance
&& !p
->do_normalise
)
112 for (len
= *isamp
; len
; --len
, ++ibuf
) {
113 double d
= SOX_SAMPLE_TO_FLOAT_64BIT(*ibuf
, effp
->clips
);
117 else if (p
->do_balance
|| p
->do_balance_no_clip
)
118 for (len
= *isamp
; len
; --len
, ++ibuf
) {
119 double d
= SOX_SAMPLE_TO_FLOAT_64BIT(*ibuf
, effp
->clips
);
122 p
->max
= max(p
->max
, *ibuf
);
123 p
->min
= min(p
->min
, *ibuf
);
125 else for (len
= *isamp
; len
; --len
, ++ibuf
) {
126 p
->max
= max(p
->max
, *ibuf
);
127 p
->min
= min(p
->min
, *ibuf
);
129 *osamp
= 0; /* samples not output until drain */
132 double mult
= ((priv_t
*)(effp
- effp
->flow
)->priv
)->fixed_gain
;
133 len
= *isamp
= *osamp
= min(*isamp
, *osamp
);
134 if (!p
->do_limiter
) for (; len
; --len
, ++ibuf
)
135 *obuf
++ = SOX_ROUND_CLIP_COUNT(*ibuf
* mult
, effp
->clips
);
136 else for (; len
; --len
, ++ibuf
) {
137 double d
= *ibuf
* mult
;
138 *obuf
++ = d
< 0 ? 1 / (1 / d
- p
->limiter
) - .5 :
139 d
> 0 ? 1 / (1 / d
+ p
->limiter
) + .5 : 0;
145 static void start_drain(sox_effect_t
* effp
)
147 priv_t
* p
= (priv_t
*)effp
->priv
;
148 double max
= SOX_SAMPLE_MAX
, max_peak
= 0, max_rms
= 0;
151 if (p
->do_balance
|| p
->do_balance_no_clip
) {
152 for (i
= 0; i
< effp
->flows
; ++i
) {
153 priv_t
* q
= (priv_t
*)(effp
- effp
->flow
+ i
)->priv
;
154 max_rms
= max(max_rms
, sqrt(q
->rms
/ q
->num_samples
));
157 for (i
= 0; i
< effp
->flows
; ++i
) {
158 priv_t
* q
= (priv_t
*)(effp
- effp
->flow
+ i
)->priv
;
159 double this_rms
= sqrt(q
->rms
/ q
->num_samples
);
160 double this_peak
= max(q
->max
/ max
, q
->min
/ (double)SOX_SAMPLE_MIN
);
161 q
->mult
= this_rms
!= 0? max_rms
/ this_rms
: 1;
162 max_peak
= max(max_peak
, q
->mult
* this_peak
);
163 q
->mult
*= p
->fixed_gain
;
165 if (p
->do_normalise
|| (p
->do_balance_no_clip
&& max_peak
> 1))
166 for (i
= 0; i
< effp
->flows
; ++i
) {
167 priv_t
* q
= (priv_t
*)(effp
- effp
->flow
+ i
)->priv
;
170 } else if (p
->do_equalise
&& !p
->do_normalise
) {
171 for (i
= 0; i
< effp
->flows
; ++i
) {
172 priv_t
* q
= (priv_t
*)(effp
- effp
->flow
+ i
)->priv
;
173 double this_peak
= max(q
->max
/ max
, q
->min
/ (double)SOX_SAMPLE_MIN
);
174 max_peak
= max(max_peak
, this_peak
);
175 q
->mult
= p
->fixed_gain
/ this_peak
;
178 for (i
= 0; i
< effp
->flows
; ++i
) {
179 priv_t
* q
= (priv_t
*)(effp
- effp
->flow
+ i
)->priv
;
183 p
->mult
= min(max
/ p
->max
, (double)SOX_SAMPLE_MIN
/ p
->min
);
185 if (p
->reclaim
> p
->mult
)
186 lsx_report("%.3gdB not reclaimed", linear_to_dB(p
->reclaim
/ p
->mult
));
187 else p
->mult
= p
->reclaim
;
189 p
->mult
*= p
->fixed_gain
;
194 static int drain(sox_effect_t
* effp
, sox_sample_t
* obuf
, size_t * osamp
)
196 priv_t
* p
= (priv_t
*)effp
->priv
;
198 int result
= SOX_SUCCESS
;
200 *osamp
-= *osamp
% effp
->in_signal
.channels
;
205 len
= fread(obuf
, sizeof(*obuf
), *osamp
, p
->tmp_file
);
206 if (len
!= *osamp
&& !feof(p
->tmp_file
)) {
207 lsx_fail("error reading temporary file: %s", strerror(errno
));
210 if (!p
->do_limiter
) for (*osamp
= len
; len
; --len
, ++obuf
)
211 *obuf
= SOX_ROUND_CLIP_COUNT(*obuf
* p
->mult
, effp
->clips
);
212 else for (*osamp
= len
; len
; --len
) {
213 double d
= *obuf
* p
->mult
;
214 *obuf
++ = d
< 0 ? 1 / (1 / d
- p
->limiter
) - .5 :
215 d
> 0 ? 1 / (1 / d
+ p
->limiter
) + .5 : 0;
222 static int stop(sox_effect_t
* effp
)
224 priv_t
* p
= (priv_t
*)effp
->priv
;
226 fclose(p
->tmp_file
); /* auto-deleted by lsx_tmpfile */
230 sox_effect_handler_t
const * lsx_gain_effect_fn(void)
232 static sox_effect_handler_t handler
= {
233 "gain", NULL
, SOX_EFF_GAIN
,
234 create
, start
, flow
, drain
, stop
, NULL
, sizeof(priv_t
)};
235 static char const * lines
[] = {
236 "[-e|-b|-B|-r] [-n] [-l|-h] [gain-dB]",
237 "-e\t Equalise channels: peak to that with max peak;",
238 "-B\t Balance channels: rms to that with max rms; no clip protection",
239 "-b\t Balance channels: rms to that with max rms; clip protection",
241 "-r\t Reclaim headroom (as much as possible without clipping); see -h",
242 "-n\t Norm file to 0dBfs(output precision); gain-dB, if present, usually <0",
243 "-l\t Use simple limiter",
244 "-h\t Apply attenuation for headroom for subsequent effects; gain-dB, if",
245 "\t present, is subject to reclaim by a subsequent gain -r",
246 "gain-dB\t Apply gain in dB",
249 handler
.usage
= lsx_usage_lines(&usage
, lines
, array_length(lines
));
253 /*------------------ emulation of the old `normalise' effect -----------------*/
255 static int norm_getopts(sox_effect_t
* effp
, int argc
, char * * argv
)
260 argv2
[0] = argv
[0], --argc
, ++argv
;
263 argv2
[argc2
++] = *argv
, --argc
, ++argv
;
264 return argc
? lsx_usage(effp
) :
265 lsx_gain_effect_fn()->getopts(effp
, argc2
, argv2
);
268 sox_effect_handler_t
const * lsx_norm_effect_fn(void)
270 static sox_effect_handler_t handler
;
271 handler
= *lsx_gain_effect_fn();
272 handler
.name
= "norm";
273 handler
.usage
= "[level]";
274 handler
.getopts
= norm_getopts
;