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