blob: 0bb719fae5a38cefda20d5e027bda90a40319408
1 | /* |
2 | * Copyright (c) 1999 Chris Bagwell |
3 | * Copyright (c) 1999 Nick Bailey |
4 | * Copyright (c) 2007 Rob Sykes <robs@users.sourceforge.net> |
5 | * Copyright (c) 2013 Paul B Mahol |
6 | * Copyright (c) 2014 Andrew Kelley |
7 | * |
8 | * This file is part of FFmpeg. |
9 | * |
10 | * FFmpeg is free software; you can redistribute it and/or |
11 | * modify it under the terms of the GNU Lesser General Public |
12 | * License as published by the Free Software Foundation; either |
13 | * version 2.1 of the License, or (at your option) any later version. |
14 | * |
15 | * FFmpeg is distributed in the hope that it will be useful, |
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
18 | * Lesser General Public License for more details. |
19 | * |
20 | * You should have received a copy of the GNU Lesser General Public |
21 | * License along with FFmpeg; if not, write to the Free Software |
22 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
23 | */ |
24 | |
25 | /** |
26 | * @file |
27 | * audio compand filter |
28 | */ |
29 | |
30 | #include "libavutil/avassert.h" |
31 | #include "libavutil/avstring.h" |
32 | #include "libavutil/ffmath.h" |
33 | #include "libavutil/opt.h" |
34 | #include "libavutil/samplefmt.h" |
35 | #include "audio.h" |
36 | #include "avfilter.h" |
37 | #include "internal.h" |
38 | |
39 | typedef struct ChanParam { |
40 | double attack; |
41 | double decay; |
42 | double volume; |
43 | } ChanParam; |
44 | |
45 | typedef struct CompandSegment { |
46 | double x, y; |
47 | double a, b; |
48 | } CompandSegment; |
49 | |
50 | typedef struct CompandContext { |
51 | const AVClass *class; |
52 | int nb_segments; |
53 | char *attacks, *decays, *points; |
54 | CompandSegment *segments; |
55 | ChanParam *channels; |
56 | double in_min_lin; |
57 | double out_min_lin; |
58 | double curve_dB; |
59 | double gain_dB; |
60 | double initial_volume; |
61 | double delay; |
62 | AVFrame *delay_frame; |
63 | int delay_samples; |
64 | int delay_count; |
65 | int delay_index; |
66 | int64_t pts; |
67 | |
68 | int (*compand)(AVFilterContext *ctx, AVFrame *frame); |
69 | } CompandContext; |
70 | |
71 | #define OFFSET(x) offsetof(CompandContext, x) |
72 | #define A AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM |
73 | |
74 | static const AVOption compand_options[] = { |
75 | { "attacks", "set time over which increase of volume is determined", OFFSET(attacks), AV_OPT_TYPE_STRING, { .str = "0.3" }, 0, 0, A }, |
76 | { "decays", "set time over which decrease of volume is determined", OFFSET(decays), AV_OPT_TYPE_STRING, { .str = "0.8" }, 0, 0, A }, |
77 | { "points", "set points of transfer function", OFFSET(points), AV_OPT_TYPE_STRING, { .str = "-70/-70|-60/-20" }, 0, 0, A }, |
78 | { "soft-knee", "set soft-knee", OFFSET(curve_dB), AV_OPT_TYPE_DOUBLE, { .dbl = 0.01 }, 0.01, 900, A }, |
79 | { "gain", "set output gain", OFFSET(gain_dB), AV_OPT_TYPE_DOUBLE, { .dbl = 0 }, -900, 900, A }, |
80 | { "volume", "set initial volume", OFFSET(initial_volume), AV_OPT_TYPE_DOUBLE, { .dbl = 0 }, -900, 0, A }, |
81 | { "delay", "set delay for samples before sending them to volume adjuster", OFFSET(delay), AV_OPT_TYPE_DOUBLE, { .dbl = 0 }, 0, 20, A }, |
82 | { NULL } |
83 | }; |
84 | |
85 | AVFILTER_DEFINE_CLASS(compand); |
86 | |
87 | static av_cold int init(AVFilterContext *ctx) |
88 | { |
89 | CompandContext *s = ctx->priv; |
90 | s->pts = AV_NOPTS_VALUE; |
91 | return 0; |
92 | } |
93 | |
94 | static av_cold void uninit(AVFilterContext *ctx) |
95 | { |
96 | CompandContext *s = ctx->priv; |
97 | |
98 | av_freep(&s->channels); |
99 | av_freep(&s->segments); |
100 | av_frame_free(&s->delay_frame); |
101 | } |
102 | |
103 | static int query_formats(AVFilterContext *ctx) |
104 | { |
105 | AVFilterChannelLayouts *layouts; |
106 | AVFilterFormats *formats; |
107 | static const enum AVSampleFormat sample_fmts[] = { |
108 | AV_SAMPLE_FMT_DBLP, |
109 | AV_SAMPLE_FMT_NONE |
110 | }; |
111 | int ret; |
112 | |
113 | layouts = ff_all_channel_counts(); |
114 | if (!layouts) |
115 | return AVERROR(ENOMEM); |
116 | ret = ff_set_common_channel_layouts(ctx, layouts); |
117 | if (ret < 0) |
118 | return ret; |
119 | |
120 | formats = ff_make_format_list(sample_fmts); |
121 | if (!formats) |
122 | return AVERROR(ENOMEM); |
123 | ret = ff_set_common_formats(ctx, formats); |
124 | if (ret < 0) |
125 | return ret; |
126 | |
127 | formats = ff_all_samplerates(); |
128 | if (!formats) |
129 | return AVERROR(ENOMEM); |
130 | return ff_set_common_samplerates(ctx, formats); |
131 | } |
132 | |
133 | static void count_items(char *item_str, int *nb_items) |
134 | { |
135 | char *p; |
136 | |
137 | *nb_items = 1; |
138 | for (p = item_str; *p; p++) { |
139 | if (*p == ' ' || *p == '|') |
140 | (*nb_items)++; |
141 | } |
142 | } |
143 | |
144 | static void update_volume(ChanParam *cp, double in) |
145 | { |
146 | double delta = in - cp->volume; |
147 | |
148 | if (delta > 0.0) |
149 | cp->volume += delta * cp->attack; |
150 | else |
151 | cp->volume += delta * cp->decay; |
152 | } |
153 | |
154 | static double get_volume(CompandContext *s, double in_lin) |
155 | { |
156 | CompandSegment *cs; |
157 | double in_log, out_log; |
158 | int i; |
159 | |
160 | if (in_lin < s->in_min_lin) |
161 | return s->out_min_lin; |
162 | |
163 | in_log = log(in_lin); |
164 | |
165 | for (i = 1; i < s->nb_segments; i++) |
166 | if (in_log <= s->segments[i].x) |
167 | break; |
168 | cs = &s->segments[i - 1]; |
169 | in_log -= cs->x; |
170 | out_log = cs->y + in_log * (cs->a * in_log + cs->b); |
171 | |
172 | return exp(out_log); |
173 | } |
174 | |
175 | static int compand_nodelay(AVFilterContext *ctx, AVFrame *frame) |
176 | { |
177 | CompandContext *s = ctx->priv; |
178 | AVFilterLink *inlink = ctx->inputs[0]; |
179 | const int channels = inlink->channels; |
180 | const int nb_samples = frame->nb_samples; |
181 | AVFrame *out_frame; |
182 | int chan, i; |
183 | int err; |
184 | |
185 | if (av_frame_is_writable(frame)) { |
186 | out_frame = frame; |
187 | } else { |
188 | out_frame = ff_get_audio_buffer(inlink, nb_samples); |
189 | if (!out_frame) { |
190 | av_frame_free(&frame); |
191 | return AVERROR(ENOMEM); |
192 | } |
193 | err = av_frame_copy_props(out_frame, frame); |
194 | if (err < 0) { |
195 | av_frame_free(&out_frame); |
196 | av_frame_free(&frame); |
197 | return err; |
198 | } |
199 | } |
200 | |
201 | for (chan = 0; chan < channels; chan++) { |
202 | const double *src = (double *)frame->extended_data[chan]; |
203 | double *dst = (double *)out_frame->extended_data[chan]; |
204 | ChanParam *cp = &s->channels[chan]; |
205 | |
206 | for (i = 0; i < nb_samples; i++) { |
207 | update_volume(cp, fabs(src[i])); |
208 | |
209 | dst[i] = src[i] * get_volume(s, cp->volume); |
210 | } |
211 | } |
212 | |
213 | if (frame != out_frame) |
214 | av_frame_free(&frame); |
215 | |
216 | return ff_filter_frame(ctx->outputs[0], out_frame); |
217 | } |
218 | |
219 | #define MOD(a, b) (((a) >= (b)) ? (a) - (b) : (a)) |
220 | |
221 | static int compand_delay(AVFilterContext *ctx, AVFrame *frame) |
222 | { |
223 | CompandContext *s = ctx->priv; |
224 | AVFilterLink *inlink = ctx->inputs[0]; |
225 | const int channels = inlink->channels; |
226 | const int nb_samples = frame->nb_samples; |
227 | int chan, i, av_uninit(dindex), oindex, av_uninit(count); |
228 | AVFrame *out_frame = NULL; |
229 | int err; |
230 | |
231 | if (s->pts == AV_NOPTS_VALUE) { |
232 | s->pts = (frame->pts == AV_NOPTS_VALUE) ? 0 : frame->pts; |
233 | } |
234 | |
235 | av_assert1(channels > 0); /* would corrupt delay_count and delay_index */ |
236 | |
237 | for (chan = 0; chan < channels; chan++) { |
238 | AVFrame *delay_frame = s->delay_frame; |
239 | const double *src = (double *)frame->extended_data[chan]; |
240 | double *dbuf = (double *)delay_frame->extended_data[chan]; |
241 | ChanParam *cp = &s->channels[chan]; |
242 | double *dst; |
243 | |
244 | count = s->delay_count; |
245 | dindex = s->delay_index; |
246 | for (i = 0, oindex = 0; i < nb_samples; i++) { |
247 | const double in = src[i]; |
248 | update_volume(cp, fabs(in)); |
249 | |
250 | if (count >= s->delay_samples) { |
251 | if (!out_frame) { |
252 | out_frame = ff_get_audio_buffer(inlink, nb_samples - i); |
253 | if (!out_frame) { |
254 | av_frame_free(&frame); |
255 | return AVERROR(ENOMEM); |
256 | } |
257 | err = av_frame_copy_props(out_frame, frame); |
258 | if (err < 0) { |
259 | av_frame_free(&out_frame); |
260 | av_frame_free(&frame); |
261 | return err; |
262 | } |
263 | out_frame->pts = s->pts; |
264 | s->pts += av_rescale_q(nb_samples - i, |
265 | (AVRational){ 1, inlink->sample_rate }, |
266 | inlink->time_base); |
267 | } |
268 | |
269 | dst = (double *)out_frame->extended_data[chan]; |
270 | dst[oindex++] = dbuf[dindex] * get_volume(s, cp->volume); |
271 | } else { |
272 | count++; |
273 | } |
274 | |
275 | dbuf[dindex] = in; |
276 | dindex = MOD(dindex + 1, s->delay_samples); |
277 | } |
278 | } |
279 | |
280 | s->delay_count = count; |
281 | s->delay_index = dindex; |
282 | |
283 | av_frame_free(&frame); |
284 | |
285 | if (out_frame) { |
286 | err = ff_filter_frame(ctx->outputs[0], out_frame); |
287 | return err; |
288 | } |
289 | |
290 | return 0; |
291 | } |
292 | |
293 | static int compand_drain(AVFilterLink *outlink) |
294 | { |
295 | AVFilterContext *ctx = outlink->src; |
296 | CompandContext *s = ctx->priv; |
297 | const int channels = outlink->channels; |
298 | AVFrame *frame = NULL; |
299 | int chan, i, dindex; |
300 | |
301 | /* 2048 is to limit output frame size during drain */ |
302 | frame = ff_get_audio_buffer(outlink, FFMIN(2048, s->delay_count)); |
303 | if (!frame) |
304 | return AVERROR(ENOMEM); |
305 | frame->pts = s->pts; |
306 | s->pts += av_rescale_q(frame->nb_samples, |
307 | (AVRational){ 1, outlink->sample_rate }, outlink->time_base); |
308 | |
309 | av_assert0(channels > 0); |
310 | for (chan = 0; chan < channels; chan++) { |
311 | AVFrame *delay_frame = s->delay_frame; |
312 | double *dbuf = (double *)delay_frame->extended_data[chan]; |
313 | double *dst = (double *)frame->extended_data[chan]; |
314 | ChanParam *cp = &s->channels[chan]; |
315 | |
316 | dindex = s->delay_index; |
317 | for (i = 0; i < frame->nb_samples; i++) { |
318 | dst[i] = dbuf[dindex] * get_volume(s, cp->volume); |
319 | dindex = MOD(dindex + 1, s->delay_samples); |
320 | } |
321 | } |
322 | s->delay_count -= frame->nb_samples; |
323 | s->delay_index = dindex; |
324 | |
325 | return ff_filter_frame(outlink, frame); |
326 | } |
327 | |
328 | static int config_output(AVFilterLink *outlink) |
329 | { |
330 | AVFilterContext *ctx = outlink->src; |
331 | CompandContext *s = ctx->priv; |
332 | const int sample_rate = outlink->sample_rate; |
333 | double radius = s->curve_dB * M_LN10 / 20.0; |
334 | char *p, *saveptr = NULL; |
335 | const int channels = outlink->channels; |
336 | int nb_attacks, nb_decays, nb_points; |
337 | int new_nb_items, num; |
338 | int i; |
339 | int err; |
340 | |
341 | |
342 | count_items(s->attacks, &nb_attacks); |
343 | count_items(s->decays, &nb_decays); |
344 | count_items(s->points, &nb_points); |
345 | |
346 | if (channels <= 0) { |
347 | av_log(ctx, AV_LOG_ERROR, "Invalid number of channels: %d\n", channels); |
348 | return AVERROR(EINVAL); |
349 | } |
350 | |
351 | if (nb_attacks > channels || nb_decays > channels) { |
352 | av_log(ctx, AV_LOG_ERROR, |
353 | "Number of attacks/decays bigger than number of channels.\n"); |
354 | return AVERROR(EINVAL); |
355 | } |
356 | |
357 | uninit(ctx); |
358 | |
359 | s->channels = av_mallocz_array(channels, sizeof(*s->channels)); |
360 | s->nb_segments = (nb_points + 4) * 2; |
361 | s->segments = av_mallocz_array(s->nb_segments, sizeof(*s->segments)); |
362 | |
363 | if (!s->channels || !s->segments) { |
364 | uninit(ctx); |
365 | return AVERROR(ENOMEM); |
366 | } |
367 | |
368 | p = s->attacks; |
369 | for (i = 0, new_nb_items = 0; i < nb_attacks; i++) { |
370 | char *tstr = av_strtok(p, " |", &saveptr); |
371 | p = NULL; |
372 | new_nb_items += sscanf(tstr, "%lf", &s->channels[i].attack) == 1; |
373 | if (s->channels[i].attack < 0) { |
374 | uninit(ctx); |
375 | return AVERROR(EINVAL); |
376 | } |
377 | } |
378 | nb_attacks = new_nb_items; |
379 | |
380 | p = s->decays; |
381 | for (i = 0, new_nb_items = 0; i < nb_decays; i++) { |
382 | char *tstr = av_strtok(p, " |", &saveptr); |
383 | p = NULL; |
384 | new_nb_items += sscanf(tstr, "%lf", &s->channels[i].decay) == 1; |
385 | if (s->channels[i].decay < 0) { |
386 | uninit(ctx); |
387 | return AVERROR(EINVAL); |
388 | } |
389 | } |
390 | nb_decays = new_nb_items; |
391 | |
392 | if (nb_attacks != nb_decays) { |
393 | av_log(ctx, AV_LOG_ERROR, |
394 | "Number of attacks %d differs from number of decays %d.\n", |
395 | nb_attacks, nb_decays); |
396 | uninit(ctx); |
397 | return AVERROR(EINVAL); |
398 | } |
399 | |
400 | for (i = nb_decays; i < channels; i++) { |
401 | s->channels[i].attack = s->channels[nb_decays - 1].attack; |
402 | s->channels[i].decay = s->channels[nb_decays - 1].decay; |
403 | } |
404 | |
405 | #define S(x) s->segments[2 * ((x) + 1)] |
406 | p = s->points; |
407 | for (i = 0, new_nb_items = 0; i < nb_points; i++) { |
408 | char *tstr = av_strtok(p, " |", &saveptr); |
409 | p = NULL; |
410 | if (sscanf(tstr, "%lf/%lf", &S(i).x, &S(i).y) != 2) { |
411 | av_log(ctx, AV_LOG_ERROR, |
412 | "Invalid and/or missing input/output value.\n"); |
413 | uninit(ctx); |
414 | return AVERROR(EINVAL); |
415 | } |
416 | if (i && S(i - 1).x > S(i).x) { |
417 | av_log(ctx, AV_LOG_ERROR, |
418 | "Transfer function input values must be increasing.\n"); |
419 | uninit(ctx); |
420 | return AVERROR(EINVAL); |
421 | } |
422 | S(i).y -= S(i).x; |
423 | av_log(ctx, AV_LOG_DEBUG, "%d: x=%f y=%f\n", i, S(i).x, S(i).y); |
424 | new_nb_items++; |
425 | } |
426 | num = new_nb_items; |
427 | |
428 | /* Add 0,0 if necessary */ |
429 | if (num == 0 || S(num - 1).x) |
430 | num++; |
431 | |
432 | #undef S |
433 | #define S(x) s->segments[2 * (x)] |
434 | /* Add a tail off segment at the start */ |
435 | S(0).x = S(1).x - 2 * s->curve_dB; |
436 | S(0).y = S(1).y; |
437 | num++; |
438 | |
439 | /* Join adjacent colinear segments */ |
440 | for (i = 2; i < num; i++) { |
441 | double g1 = (S(i - 1).y - S(i - 2).y) * (S(i - 0).x - S(i - 1).x); |
442 | double g2 = (S(i - 0).y - S(i - 1).y) * (S(i - 1).x - S(i - 2).x); |
443 | int j; |
444 | |
445 | if (fabs(g1 - g2)) |
446 | continue; |
447 | num--; |
448 | for (j = --i; j < num; j++) |
449 | S(j) = S(j + 1); |
450 | } |
451 | |
452 | for (i = 0; i < s->nb_segments; i += 2) { |
453 | s->segments[i].y += s->gain_dB; |
454 | s->segments[i].x *= M_LN10 / 20; |
455 | s->segments[i].y *= M_LN10 / 20; |
456 | } |
457 | |
458 | #define L(x) s->segments[i - (x)] |
459 | for (i = 4; i < s->nb_segments; i += 2) { |
460 | double x, y, cx, cy, in1, in2, out1, out2, theta, len, r; |
461 | |
462 | L(4).a = 0; |
463 | L(4).b = (L(2).y - L(4).y) / (L(2).x - L(4).x); |
464 | |
465 | L(2).a = 0; |
466 | L(2).b = (L(0).y - L(2).y) / (L(0).x - L(2).x); |
467 | |
468 | theta = atan2(L(2).y - L(4).y, L(2).x - L(4).x); |
469 | len = hypot(L(2).x - L(4).x, L(2).y - L(4).y); |
470 | r = FFMIN(radius, len); |
471 | L(3).x = L(2).x - r * cos(theta); |
472 | L(3).y = L(2).y - r * sin(theta); |
473 | |
474 | theta = atan2(L(0).y - L(2).y, L(0).x - L(2).x); |
475 | len = hypot(L(0).x - L(2).x, L(0).y - L(2).y); |
476 | r = FFMIN(radius, len / 2); |
477 | x = L(2).x + r * cos(theta); |
478 | y = L(2).y + r * sin(theta); |
479 | |
480 | cx = (L(3).x + L(2).x + x) / 3; |
481 | cy = (L(3).y + L(2).y + y) / 3; |
482 | |
483 | L(2).x = x; |
484 | L(2).y = y; |
485 | |
486 | in1 = cx - L(3).x; |
487 | out1 = cy - L(3).y; |
488 | in2 = L(2).x - L(3).x; |
489 | out2 = L(2).y - L(3).y; |
490 | L(3).a = (out2 / in2 - out1 / in1) / (in2 - in1); |
491 | L(3).b = out1 / in1 - L(3).a * in1; |
492 | } |
493 | L(3).x = 0; |
494 | L(3).y = L(2).y; |
495 | |
496 | s->in_min_lin = exp(s->segments[1].x); |
497 | s->out_min_lin = exp(s->segments[1].y); |
498 | |
499 | for (i = 0; i < channels; i++) { |
500 | ChanParam *cp = &s->channels[i]; |
501 | |
502 | if (cp->attack > 1.0 / sample_rate) |
503 | cp->attack = 1.0 - exp(-1.0 / (sample_rate * cp->attack)); |
504 | else |
505 | cp->attack = 1.0; |
506 | if (cp->decay > 1.0 / sample_rate) |
507 | cp->decay = 1.0 - exp(-1.0 / (sample_rate * cp->decay)); |
508 | else |
509 | cp->decay = 1.0; |
510 | cp->volume = ff_exp10(s->initial_volume / 20); |
511 | } |
512 | |
513 | s->delay_samples = s->delay * sample_rate; |
514 | if (s->delay_samples <= 0) { |
515 | s->compand = compand_nodelay; |
516 | return 0; |
517 | } |
518 | |
519 | s->delay_frame = av_frame_alloc(); |
520 | if (!s->delay_frame) { |
521 | uninit(ctx); |
522 | return AVERROR(ENOMEM); |
523 | } |
524 | |
525 | s->delay_frame->format = outlink->format; |
526 | s->delay_frame->nb_samples = s->delay_samples; |
527 | s->delay_frame->channel_layout = outlink->channel_layout; |
528 | |
529 | err = av_frame_get_buffer(s->delay_frame, 32); |
530 | if (err) |
531 | return err; |
532 | |
533 | s->compand = compand_delay; |
534 | return 0; |
535 | } |
536 | |
537 | static int filter_frame(AVFilterLink *inlink, AVFrame *frame) |
538 | { |
539 | AVFilterContext *ctx = inlink->dst; |
540 | CompandContext *s = ctx->priv; |
541 | |
542 | return s->compand(ctx, frame); |
543 | } |
544 | |
545 | static int request_frame(AVFilterLink *outlink) |
546 | { |
547 | AVFilterContext *ctx = outlink->src; |
548 | CompandContext *s = ctx->priv; |
549 | int ret = 0; |
550 | |
551 | ret = ff_request_frame(ctx->inputs[0]); |
552 | |
553 | if (ret == AVERROR_EOF && !ctx->is_disabled && s->delay_count) |
554 | ret = compand_drain(outlink); |
555 | |
556 | return ret; |
557 | } |
558 | |
559 | static const AVFilterPad compand_inputs[] = { |
560 | { |
561 | .name = "default", |
562 | .type = AVMEDIA_TYPE_AUDIO, |
563 | .filter_frame = filter_frame, |
564 | }, |
565 | { NULL } |
566 | }; |
567 | |
568 | static const AVFilterPad compand_outputs[] = { |
569 | { |
570 | .name = "default", |
571 | .request_frame = request_frame, |
572 | .config_props = config_output, |
573 | .type = AVMEDIA_TYPE_AUDIO, |
574 | }, |
575 | { NULL } |
576 | }; |
577 | |
578 | |
579 | AVFilter ff_af_compand = { |
580 | .name = "compand", |
581 | .description = NULL_IF_CONFIG_SMALL( |
582 | "Compress or expand audio dynamic range."), |
583 | .query_formats = query_formats, |
584 | .priv_size = sizeof(CompandContext), |
585 | .priv_class = &compand_class, |
586 | .init = init, |
587 | .uninit = uninit, |
588 | .inputs = compand_inputs, |
589 | .outputs = compand_outputs, |
590 | }; |
591 |