| 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 | 172032 | static void update_volume(ChanParam *cp, double in) | |
| 116 | { | ||
| 117 | 172032 | double delta = in - cp->volume; | |
| 118 | |||
| 119 |
2/2✓ Branch 0 taken 678 times.
✓ Branch 1 taken 171354 times.
|
172032 | if (delta > 0.0) |
| 120 | 678 | cp->volume += delta * cp->attack; | |
| 121 | else | ||
| 122 | 171354 | cp->volume += delta * cp->decay; | |
| 123 | 172032 | } | |
| 124 | |||
| 125 | 172032 | 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 58024 times.
✓ Branch 1 taken 114008 times.
|
172032 | if (in_lin < s->in_min_lin) |
| 132 | 58024 | 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 | 21 | static int compand_nodelay(AVFilterContext *ctx, AVFrame *frame) | |
| 147 | { | ||
| 148 | 21 | CompandContext *s = ctx->priv; | |
| 149 | 21 | AVFilterLink *inlink = ctx->inputs[0]; | |
| 150 | 21 | const int channels = inlink->ch_layout.nb_channels; | |
| 151 | 21 | 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 21 times.
✗ Branch 2 not taken.
|
21 | if (av_frame_is_writable(frame)) { |
| 157 | 21 | 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 42 times.
✓ Branch 1 taken 21 times.
|
63 | for (chan = 0; chan < channels; chan++) { |
| 173 | 42 | const double *src = (double *)frame->extended_data[chan]; | |
| 174 | 42 | double *dst = (double *)out_frame->extended_data[chan]; | |
| 175 | 42 | ChanParam *cp = &s->channels[chan]; | |
| 176 | |||
| 177 |
2/2✓ Branch 0 taken 172032 times.
✓ Branch 1 taken 42 times.
|
172074 | for (i = 0; i < nb_samples; i++) { |
| 178 | 172032 | update_volume(cp, fabs(src[i])); | |
| 179 | |||
| 180 | 172032 | dst[i] = src[i] * get_volume(s, cp->volume); | |
| 181 | } | ||
| 182 | } | ||
| 183 | |||
| 184 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 21 times.
|
21 | if (frame != out_frame) |
| 185 | ✗ | av_frame_free(&frame); | |
| 186 | |||
| 187 | 21 | 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 | 21 | static int filter_frame(AVFilterLink *inlink, AVFrame *frame) | |
| 506 | { | ||
| 507 | 21 | AVFilterContext *ctx = inlink->dst; | |
| 508 | 21 | CompandContext *s = ctx->priv; | |
| 509 | |||
| 510 | 21 | return s->compand(ctx, frame); | |
| 511 | } | ||
| 512 | |||
| 513 | 20 | static int request_frame(AVFilterLink *outlink) | |
| 514 | { | ||
| 515 | 20 | AVFilterContext *ctx = outlink->src; | |
| 516 | 20 | CompandContext *s = ctx->priv; | |
| 517 | 20 | int ret = 0; | |
| 518 | |||
| 519 | 20 | ret = ff_request_frame(ctx->inputs[0]); | |
| 520 | |||
| 521 |
1/6✗ Branch 0 not taken.
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
|
20 | if (ret == AVERROR_EOF && !ctx->is_disabled && s->delay_count) |
| 522 | ✗ | ret = compand_drain(outlink); | |
| 523 | |||
| 524 | 20 | 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 FFFilter ff_af_compand = { | ||
| 546 | .p.name = "compand", | ||
| 547 | .p.description = NULL_IF_CONFIG_SMALL( | ||
| 548 | "Compress or expand audio dynamic range."), | ||
| 549 | .p.priv_class = &compand_class, | ||
| 550 | .priv_size = sizeof(CompandContext), | ||
| 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 |