| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /* | ||
| 2 | * COpyright (c) 2002 Daniel Pouzzner | ||
| 3 | * Copyright (c) 1999 Chris Bagwell | ||
| 4 | * Copyright (c) 1999 Nick Bailey | ||
| 5 | * Copyright (c) 2007 Rob Sykes <robs@users.sourceforge.net> | ||
| 6 | * Copyright (c) 2013 Paul B Mahol | ||
| 7 | * Copyright (c) 2014 Andrew Kelley | ||
| 8 | * | ||
| 9 | * This file is part of FFmpeg. | ||
| 10 | * | ||
| 11 | * FFmpeg is free software; you can redistribute it and/or | ||
| 12 | * modify it under the terms of the GNU Lesser General Public | ||
| 13 | * License as published by the Free Software Foundation; either | ||
| 14 | * version 2.1 of the License, or (at your option) any later version. | ||
| 15 | * | ||
| 16 | * FFmpeg is distributed in the hope that it will be useful, | ||
| 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 19 | * Lesser General Public License for more details. | ||
| 20 | * | ||
| 21 | * You should have received a copy of the GNU Lesser General Public | ||
| 22 | * License along with FFmpeg; if not, write to the Free Software | ||
| 23 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 24 | */ | ||
| 25 | |||
| 26 | /** | ||
| 27 | * @file | ||
| 28 | * audio multiband compand filter | ||
| 29 | */ | ||
| 30 | |||
| 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 CompandSegment { | ||
| 41 | double x, y; | ||
| 42 | double a, b; | ||
| 43 | } CompandSegment; | ||
| 44 | |||
| 45 | typedef struct CompandT { | ||
| 46 | CompandSegment *segments; | ||
| 47 | int nb_segments; | ||
| 48 | double in_min_lin; | ||
| 49 | double out_min_lin; | ||
| 50 | double curve_dB; | ||
| 51 | double gain_dB; | ||
| 52 | } CompandT; | ||
| 53 | |||
| 54 | #define N 4 | ||
| 55 | |||
| 56 | typedef struct PrevCrossover { | ||
| 57 | double in; | ||
| 58 | double out_low; | ||
| 59 | double out_high; | ||
| 60 | } PrevCrossover[N * 2]; | ||
| 61 | |||
| 62 | typedef struct Crossover { | ||
| 63 | PrevCrossover *previous; | ||
| 64 | size_t pos; | ||
| 65 | double coefs[3 *(N+1)]; | ||
| 66 | } Crossover; | ||
| 67 | |||
| 68 | typedef struct CompBand { | ||
| 69 | CompandT transfer_fn; | ||
| 70 | double *attack_rate; | ||
| 71 | double *decay_rate; | ||
| 72 | double *volume; | ||
| 73 | double delay; | ||
| 74 | double topfreq; | ||
| 75 | Crossover filter; | ||
| 76 | AVFrame *delay_buf; | ||
| 77 | size_t delay_size; | ||
| 78 | ptrdiff_t delay_buf_ptr; | ||
| 79 | size_t delay_buf_cnt; | ||
| 80 | } CompBand; | ||
| 81 | |||
| 82 | typedef struct MCompandContext { | ||
| 83 | const AVClass *class; | ||
| 84 | |||
| 85 | char *args; | ||
| 86 | |||
| 87 | int nb_bands; | ||
| 88 | CompBand *bands; | ||
| 89 | AVFrame *band_buf1, *band_buf2, *band_buf3; | ||
| 90 | int band_samples; | ||
| 91 | size_t delay_buf_size; | ||
| 92 | } MCompandContext; | ||
| 93 | |||
| 94 | #define OFFSET(x) offsetof(MCompandContext, x) | ||
| 95 | #define A AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM | ||
| 96 | |||
| 97 | static const AVOption mcompand_options[] = { | ||
| 98 | { "args", "set parameters for each band", OFFSET(args), AV_OPT_TYPE_STRING, { .str = "0.005,0.1 6 -47/-40,-34/-34,-17/-33 100 | 0.003,0.05 6 -47/-40,-34/-34,-17/-33 400 | 0.000625,0.0125 6 -47/-40,-34/-34,-15/-33 1600 | 0.0001,0.025 6 -47/-40,-34/-34,-31/-31,-0/-30 6400 | 0,0.025 6 -38/-31,-28/-28,-0/-25 22000" }, 0, 0, A }, | ||
| 99 | { NULL } | ||
| 100 | }; | ||
| 101 | |||
| 102 | AVFILTER_DEFINE_CLASS(mcompand); | ||
| 103 | |||
| 104 | ✗ | static av_cold void uninit(AVFilterContext *ctx) | |
| 105 | { | ||
| 106 | ✗ | MCompandContext *s = ctx->priv; | |
| 107 | int i; | ||
| 108 | |||
| 109 | ✗ | av_frame_free(&s->band_buf1); | |
| 110 | ✗ | av_frame_free(&s->band_buf2); | |
| 111 | ✗ | av_frame_free(&s->band_buf3); | |
| 112 | |||
| 113 | ✗ | if (s->bands) { | |
| 114 | ✗ | for (i = 0; i < s->nb_bands; i++) { | |
| 115 | ✗ | av_freep(&s->bands[i].attack_rate); | |
| 116 | ✗ | av_freep(&s->bands[i].decay_rate); | |
| 117 | ✗ | av_freep(&s->bands[i].volume); | |
| 118 | ✗ | av_freep(&s->bands[i].transfer_fn.segments); | |
| 119 | ✗ | av_freep(&s->bands[i].filter.previous); | |
| 120 | ✗ | av_frame_free(&s->bands[i].delay_buf); | |
| 121 | } | ||
| 122 | } | ||
| 123 | ✗ | av_freep(&s->bands); | |
| 124 | ✗ | } | |
| 125 | |||
| 126 | ✗ | static void count_items(char *item_str, int *nb_items, char delimiter) | |
| 127 | { | ||
| 128 | char *p; | ||
| 129 | |||
| 130 | ✗ | *nb_items = 1; | |
| 131 | ✗ | for (p = item_str; *p; p++) { | |
| 132 | ✗ | if (*p == delimiter) | |
| 133 | ✗ | (*nb_items)++; | |
| 134 | } | ||
| 135 | ✗ | } | |
| 136 | |||
| 137 | ✗ | static void update_volume(CompBand *cb, double in, int ch) | |
| 138 | { | ||
| 139 | ✗ | double delta = in - cb->volume[ch]; | |
| 140 | |||
| 141 | ✗ | if (delta > 0.0) | |
| 142 | ✗ | cb->volume[ch] += delta * cb->attack_rate[ch]; | |
| 143 | else | ||
| 144 | ✗ | cb->volume[ch] += delta * cb->decay_rate[ch]; | |
| 145 | ✗ | } | |
| 146 | |||
| 147 | ✗ | static double get_volume(CompandT *s, double in_lin) | |
| 148 | { | ||
| 149 | CompandSegment *cs; | ||
| 150 | double in_log, out_log; | ||
| 151 | int i; | ||
| 152 | |||
| 153 | ✗ | if (in_lin <= s->in_min_lin) | |
| 154 | ✗ | return s->out_min_lin; | |
| 155 | |||
| 156 | ✗ | in_log = log(in_lin); | |
| 157 | |||
| 158 | ✗ | for (i = 1; i < s->nb_segments; i++) | |
| 159 | ✗ | if (in_log <= s->segments[i].x) | |
| 160 | ✗ | break; | |
| 161 | ✗ | cs = &s->segments[i - 1]; | |
| 162 | ✗ | in_log -= cs->x; | |
| 163 | ✗ | out_log = cs->y + in_log * (cs->a * in_log + cs->b); | |
| 164 | |||
| 165 | ✗ | return exp(out_log); | |
| 166 | } | ||
| 167 | |||
| 168 | ✗ | static int parse_points(char *points, int nb_points, double radius, | |
| 169 | CompandT *s, AVFilterContext *ctx) | ||
| 170 | { | ||
| 171 | int new_nb_items, num; | ||
| 172 | ✗ | char *saveptr = NULL; | |
| 173 | ✗ | char *p = points; | |
| 174 | int i; | ||
| 175 | |||
| 176 | #define S(x) s->segments[2 * ((x) + 1)] | ||
| 177 | ✗ | for (i = 0, new_nb_items = 0; i < nb_points; i++) { | |
| 178 | ✗ | char *tstr = av_strtok(p, ",", &saveptr); | |
| 179 | ✗ | p = NULL; | |
| 180 | ✗ | if (!tstr || sscanf(tstr, "%lf/%lf", &S(i).x, &S(i).y) != 2) { | |
| 181 | ✗ | av_log(ctx, AV_LOG_ERROR, | |
| 182 | "Invalid and/or missing input/output value.\n"); | ||
| 183 | ✗ | return AVERROR(EINVAL); | |
| 184 | } | ||
| 185 | ✗ | if (i && S(i - 1).x > S(i).x) { | |
| 186 | ✗ | av_log(ctx, AV_LOG_ERROR, | |
| 187 | "Transfer function input values must be increasing.\n"); | ||
| 188 | ✗ | return AVERROR(EINVAL); | |
| 189 | } | ||
| 190 | ✗ | S(i).y -= S(i).x; | |
| 191 | ✗ | av_log(ctx, AV_LOG_DEBUG, "%d: x=%f y=%f\n", i, S(i).x, S(i).y); | |
| 192 | ✗ | new_nb_items++; | |
| 193 | } | ||
| 194 | ✗ | num = new_nb_items; | |
| 195 | |||
| 196 | /* Add 0,0 if necessary */ | ||
| 197 | ✗ | if (num == 0 || S(num - 1).x) | |
| 198 | ✗ | num++; | |
| 199 | |||
| 200 | #undef S | ||
| 201 | #define S(x) s->segments[2 * (x)] | ||
| 202 | /* Add a tail off segment at the start */ | ||
| 203 | ✗ | S(0).x = S(1).x - 2 * s->curve_dB; | |
| 204 | ✗ | S(0).y = S(1).y; | |
| 205 | ✗ | num++; | |
| 206 | |||
| 207 | /* Join adjacent colinear segments */ | ||
| 208 | ✗ | for (i = 2; i < num; i++) { | |
| 209 | ✗ | double g1 = (S(i - 1).y - S(i - 2).y) * (S(i - 0).x - S(i - 1).x); | |
| 210 | ✗ | double g2 = (S(i - 0).y - S(i - 1).y) * (S(i - 1).x - S(i - 2).x); | |
| 211 | int j; | ||
| 212 | |||
| 213 | ✗ | if (fabs(g1 - g2)) | |
| 214 | ✗ | continue; | |
| 215 | ✗ | num--; | |
| 216 | ✗ | for (j = --i; j < num; j++) | |
| 217 | ✗ | S(j) = S(j + 1); | |
| 218 | } | ||
| 219 | |||
| 220 | ✗ | for (i = 0; i < s->nb_segments; i += 2) { | |
| 221 | ✗ | s->segments[i].y += s->gain_dB; | |
| 222 | ✗ | s->segments[i].x *= M_LN10 / 20; | |
| 223 | ✗ | s->segments[i].y *= M_LN10 / 20; | |
| 224 | } | ||
| 225 | |||
| 226 | #define L(x) s->segments[i - (x)] | ||
| 227 | ✗ | for (i = 4; i < s->nb_segments; i += 2) { | |
| 228 | double x, y, cx, cy, in1, in2, out1, out2, theta, len, r; | ||
| 229 | |||
| 230 | ✗ | L(4).a = 0; | |
| 231 | ✗ | L(4).b = (L(2).y - L(4).y) / (L(2).x - L(4).x); | |
| 232 | |||
| 233 | ✗ | L(2).a = 0; | |
| 234 | ✗ | L(2).b = (L(0).y - L(2).y) / (L(0).x - L(2).x); | |
| 235 | |||
| 236 | ✗ | theta = atan2(L(2).y - L(4).y, L(2).x - L(4).x); | |
| 237 | ✗ | len = hypot(L(2).x - L(4).x, L(2).y - L(4).y); | |
| 238 | ✗ | r = FFMIN(radius, len); | |
| 239 | ✗ | L(3).x = L(2).x - r * cos(theta); | |
| 240 | ✗ | L(3).y = L(2).y - r * sin(theta); | |
| 241 | |||
| 242 | ✗ | theta = atan2(L(0).y - L(2).y, L(0).x - L(2).x); | |
| 243 | ✗ | len = hypot(L(0).x - L(2).x, L(0).y - L(2).y); | |
| 244 | ✗ | r = FFMIN(radius, len / 2); | |
| 245 | ✗ | x = L(2).x + r * cos(theta); | |
| 246 | ✗ | y = L(2).y + r * sin(theta); | |
| 247 | |||
| 248 | ✗ | cx = (L(3).x + L(2).x + x) / 3; | |
| 249 | ✗ | cy = (L(3).y + L(2).y + y) / 3; | |
| 250 | |||
| 251 | ✗ | L(2).x = x; | |
| 252 | ✗ | L(2).y = y; | |
| 253 | |||
| 254 | ✗ | in1 = cx - L(3).x; | |
| 255 | ✗ | out1 = cy - L(3).y; | |
| 256 | ✗ | in2 = L(2).x - L(3).x; | |
| 257 | ✗ | out2 = L(2).y - L(3).y; | |
| 258 | ✗ | L(3).a = (out2 / in2 - out1 / in1) / (in2 - in1); | |
| 259 | ✗ | L(3).b = out1 / in1 - L(3).a * in1; | |
| 260 | } | ||
| 261 | ✗ | L(3).x = 0; | |
| 262 | ✗ | L(3).y = L(2).y; | |
| 263 | |||
| 264 | ✗ | s->in_min_lin = exp(s->segments[1].x); | |
| 265 | ✗ | s->out_min_lin = exp(s->segments[1].y); | |
| 266 | |||
| 267 | ✗ | return 0; | |
| 268 | } | ||
| 269 | |||
| 270 | ✗ | static void square_quadratic(double const *x, double *y) | |
| 271 | { | ||
| 272 | ✗ | y[0] = x[0] * x[0]; | |
| 273 | ✗ | y[1] = 2 * x[0] * x[1]; | |
| 274 | ✗ | y[2] = 2 * x[0] * x[2] + x[1] * x[1]; | |
| 275 | ✗ | y[3] = 2 * x[1] * x[2]; | |
| 276 | ✗ | y[4] = x[2] * x[2]; | |
| 277 | ✗ | } | |
| 278 | |||
| 279 | ✗ | static int crossover_setup(AVFilterLink *outlink, Crossover *p, double frequency) | |
| 280 | { | ||
| 281 | ✗ | double w0 = 2 * M_PI * frequency / outlink->sample_rate; | |
| 282 | ✗ | double Q = sqrt(.5), alpha = sin(w0) / (2*Q); | |
| 283 | double x[9], norm; | ||
| 284 | int i; | ||
| 285 | |||
| 286 | ✗ | if (w0 > M_PI) | |
| 287 | ✗ | return AVERROR(EINVAL); | |
| 288 | |||
| 289 | ✗ | x[0] = (1 - cos(w0))/2; /* Cf. filter_LPF in biquads.c */ | |
| 290 | ✗ | x[1] = 1 - cos(w0); | |
| 291 | ✗ | x[2] = (1 - cos(w0))/2; | |
| 292 | ✗ | x[3] = (1 + cos(w0))/2; /* Cf. filter_HPF in biquads.c */ | |
| 293 | ✗ | x[4] = -(1 + cos(w0)); | |
| 294 | ✗ | x[5] = (1 + cos(w0))/2; | |
| 295 | ✗ | x[6] = 1 + alpha; | |
| 296 | ✗ | x[7] = -2*cos(w0); | |
| 297 | ✗ | x[8] = 1 - alpha; | |
| 298 | |||
| 299 | ✗ | for (norm = x[6], i = 0; i < 9; ++i) | |
| 300 | ✗ | x[i] /= norm; | |
| 301 | |||
| 302 | ✗ | square_quadratic(x , p->coefs); | |
| 303 | ✗ | square_quadratic(x + 3, p->coefs + 5); | |
| 304 | ✗ | square_quadratic(x + 6, p->coefs + 10); | |
| 305 | |||
| 306 | ✗ | p->previous = av_calloc(outlink->ch_layout.nb_channels, sizeof(*p->previous)); | |
| 307 | ✗ | if (!p->previous) | |
| 308 | ✗ | return AVERROR(ENOMEM); | |
| 309 | |||
| 310 | ✗ | return 0; | |
| 311 | } | ||
| 312 | |||
| 313 | ✗ | static int config_output(AVFilterLink *outlink) | |
| 314 | { | ||
| 315 | ✗ | AVFilterContext *ctx = outlink->src; | |
| 316 | ✗ | MCompandContext *s = ctx->priv; | |
| 317 | int ret, ch, i, k, new_nb_items, nb_bands; | ||
| 318 | ✗ | char *p = s->args, *saveptr = NULL; | |
| 319 | ✗ | int max_delay_size = 0; | |
| 320 | |||
| 321 | ✗ | count_items(s->args, &nb_bands, '|'); | |
| 322 | ✗ | s->nb_bands = FFMAX(1, nb_bands); | |
| 323 | |||
| 324 | ✗ | s->bands = av_calloc(nb_bands, sizeof(*s->bands)); | |
| 325 | ✗ | if (!s->bands) | |
| 326 | ✗ | return AVERROR(ENOMEM); | |
| 327 | |||
| 328 | ✗ | for (i = 0, new_nb_items = 0; i < nb_bands; i++) { | |
| 329 | ✗ | int nb_points, nb_attacks, nb_items = 0; | |
| 330 | ✗ | char *tstr2, *tstr = av_strtok(p, "|", &saveptr); | |
| 331 | ✗ | char *p2, *p3, *saveptr2 = NULL, *saveptr3 = NULL; | |
| 332 | double radius; | ||
| 333 | |||
| 334 | ✗ | if (!tstr) | |
| 335 | ✗ | return AVERROR(EINVAL); | |
| 336 | ✗ | p = NULL; | |
| 337 | |||
| 338 | ✗ | p2 = tstr; | |
| 339 | ✗ | count_items(tstr, &nb_items, ' '); | |
| 340 | ✗ | tstr2 = av_strtok(p2, " ", &saveptr2); | |
| 341 | ✗ | if (!tstr2) { | |
| 342 | ✗ | av_log(ctx, AV_LOG_ERROR, "at least one attacks/decays rate is mandatory\n"); | |
| 343 | ✗ | return AVERROR(EINVAL); | |
| 344 | } | ||
| 345 | ✗ | p2 = NULL; | |
| 346 | ✗ | p3 = tstr2; | |
| 347 | |||
| 348 | ✗ | count_items(tstr2, &nb_attacks, ','); | |
| 349 | ✗ | if (!nb_attacks || nb_attacks & 1) { | |
| 350 | ✗ | av_log(ctx, AV_LOG_ERROR, "number of attacks rate plus decays rate must be even\n"); | |
| 351 | ✗ | return AVERROR(EINVAL); | |
| 352 | } | ||
| 353 | |||
| 354 | ✗ | s->bands[i].attack_rate = av_calloc(outlink->ch_layout.nb_channels, sizeof(double)); | |
| 355 | ✗ | s->bands[i].decay_rate = av_calloc(outlink->ch_layout.nb_channels, sizeof(double)); | |
| 356 | ✗ | s->bands[i].volume = av_calloc(outlink->ch_layout.nb_channels, sizeof(double)); | |
| 357 | ✗ | if (!s->bands[i].attack_rate || !s->bands[i].decay_rate || !s->bands[i].volume) | |
| 358 | ✗ | return AVERROR(ENOMEM); | |
| 359 | |||
| 360 | ✗ | for (k = 0; k < FFMIN(nb_attacks / 2, outlink->ch_layout.nb_channels); k++) { | |
| 361 | ✗ | char *tstr3 = av_strtok(p3, ",", &saveptr3); | |
| 362 | |||
| 363 | ✗ | p3 = NULL; | |
| 364 | ✗ | sscanf(tstr3, "%lf", &s->bands[i].attack_rate[k]); | |
| 365 | ✗ | tstr3 = av_strtok(p3, ",", &saveptr3); | |
| 366 | ✗ | sscanf(tstr3, "%lf", &s->bands[i].decay_rate[k]); | |
| 367 | |||
| 368 | ✗ | if (s->bands[i].attack_rate[k] > 1.0 / outlink->sample_rate) { | |
| 369 | ✗ | s->bands[i].attack_rate[k] = 1.0 - exp(-1.0 / (outlink->sample_rate * s->bands[i].attack_rate[k])); | |
| 370 | } else { | ||
| 371 | ✗ | s->bands[i].attack_rate[k] = 1.0; | |
| 372 | } | ||
| 373 | |||
| 374 | ✗ | if (s->bands[i].decay_rate[k] > 1.0 / outlink->sample_rate) { | |
| 375 | ✗ | s->bands[i].decay_rate[k] = 1.0 - exp(-1.0 / (outlink->sample_rate * s->bands[i].decay_rate[k])); | |
| 376 | } else { | ||
| 377 | ✗ | s->bands[i].decay_rate[k] = 1.0; | |
| 378 | } | ||
| 379 | } | ||
| 380 | |||
| 381 | ✗ | for (ch = k; ch < outlink->ch_layout.nb_channels; ch++) { | |
| 382 | ✗ | s->bands[i].attack_rate[ch] = s->bands[i].attack_rate[k - 1]; | |
| 383 | ✗ | s->bands[i].decay_rate[ch] = s->bands[i].decay_rate[k - 1]; | |
| 384 | } | ||
| 385 | |||
| 386 | ✗ | tstr2 = av_strtok(p2, " ", &saveptr2); | |
| 387 | ✗ | if (!tstr2) { | |
| 388 | ✗ | av_log(ctx, AV_LOG_ERROR, "transfer function curve in dB must be set\n"); | |
| 389 | ✗ | return AVERROR(EINVAL); | |
| 390 | } | ||
| 391 | ✗ | sscanf(tstr2, "%lf", &s->bands[i].transfer_fn.curve_dB); | |
| 392 | |||
| 393 | ✗ | radius = s->bands[i].transfer_fn.curve_dB * M_LN10 / 20.0; | |
| 394 | |||
| 395 | ✗ | tstr2 = av_strtok(p2, " ", &saveptr2); | |
| 396 | ✗ | if (!tstr2) { | |
| 397 | ✗ | av_log(ctx, AV_LOG_ERROR, "transfer points missing\n"); | |
| 398 | ✗ | return AVERROR(EINVAL); | |
| 399 | } | ||
| 400 | |||
| 401 | ✗ | count_items(tstr2, &nb_points, ','); | |
| 402 | ✗ | s->bands[i].transfer_fn.nb_segments = (nb_points + 4) * 2; | |
| 403 | ✗ | s->bands[i].transfer_fn.segments = av_calloc(s->bands[i].transfer_fn.nb_segments, | |
| 404 | sizeof(CompandSegment)); | ||
| 405 | ✗ | if (!s->bands[i].transfer_fn.segments) | |
| 406 | ✗ | return AVERROR(ENOMEM); | |
| 407 | |||
| 408 | ✗ | ret = parse_points(tstr2, nb_points, radius, &s->bands[i].transfer_fn, ctx); | |
| 409 | ✗ | if (ret < 0) { | |
| 410 | ✗ | av_log(ctx, AV_LOG_ERROR, "transfer points parsing failed\n"); | |
| 411 | ✗ | return ret; | |
| 412 | } | ||
| 413 | |||
| 414 | ✗ | tstr2 = av_strtok(p2, " ", &saveptr2); | |
| 415 | ✗ | if (!tstr2) { | |
| 416 | ✗ | av_log(ctx, AV_LOG_ERROR, "crossover_frequency is missing\n"); | |
| 417 | ✗ | return AVERROR(EINVAL); | |
| 418 | } | ||
| 419 | |||
| 420 | ✗ | new_nb_items += sscanf(tstr2, "%lf", &s->bands[i].topfreq) == 1; | |
| 421 | ✗ | if (s->bands[i].topfreq < 0 || s->bands[i].topfreq >= outlink->sample_rate / 2.0) { | |
| 422 | ✗ | av_log(ctx, AV_LOG_ERROR, "crossover_frequency: %f, should be >=0 and lower than half of sample rate: %f.\n", s->bands[i].topfreq, outlink->sample_rate / 2.0); | |
| 423 | ✗ | return AVERROR(EINVAL); | |
| 424 | } | ||
| 425 | |||
| 426 | ✗ | if (s->bands[i].topfreq != 0) { | |
| 427 | ✗ | ret = crossover_setup(outlink, &s->bands[i].filter, s->bands[i].topfreq); | |
| 428 | ✗ | if (ret < 0) | |
| 429 | ✗ | return ret; | |
| 430 | } | ||
| 431 | |||
| 432 | ✗ | tstr2 = av_strtok(p2, " ", &saveptr2); | |
| 433 | ✗ | if (tstr2) { | |
| 434 | ✗ | sscanf(tstr2, "%lf", &s->bands[i].delay); | |
| 435 | ✗ | max_delay_size = FFMAX(max_delay_size, s->bands[i].delay * outlink->sample_rate); | |
| 436 | |||
| 437 | ✗ | tstr2 = av_strtok(p2, " ", &saveptr2); | |
| 438 | ✗ | if (tstr2) { | |
| 439 | double initial_volume; | ||
| 440 | |||
| 441 | ✗ | sscanf(tstr2, "%lf", &initial_volume); | |
| 442 | ✗ | initial_volume = pow(10.0, initial_volume / 20); | |
| 443 | |||
| 444 | ✗ | for (k = 0; k < outlink->ch_layout.nb_channels; k++) { | |
| 445 | ✗ | s->bands[i].volume[k] = initial_volume; | |
| 446 | } | ||
| 447 | |||
| 448 | ✗ | tstr2 = av_strtok(p2, " ", &saveptr2); | |
| 449 | ✗ | if (tstr2) { | |
| 450 | ✗ | sscanf(tstr2, "%lf", &s->bands[i].transfer_fn.gain_dB); | |
| 451 | } | ||
| 452 | } | ||
| 453 | } | ||
| 454 | } | ||
| 455 | ✗ | s->nb_bands = new_nb_items; | |
| 456 | |||
| 457 | ✗ | for (i = 0; max_delay_size > 0 && i < s->nb_bands; i++) { | |
| 458 | ✗ | s->bands[i].delay_buf = ff_get_audio_buffer(outlink, max_delay_size); | |
| 459 | ✗ | if (!s->bands[i].delay_buf) | |
| 460 | ✗ | return AVERROR(ENOMEM); | |
| 461 | } | ||
| 462 | ✗ | s->delay_buf_size = max_delay_size; | |
| 463 | |||
| 464 | ✗ | return 0; | |
| 465 | } | ||
| 466 | |||
| 467 | #define CONVOLVE _ _ _ _ | ||
| 468 | |||
| 469 | ✗ | static void crossover(int ch, Crossover *p, | |
| 470 | double *ibuf, double *obuf_low, | ||
| 471 | double *obuf_high, size_t len) | ||
| 472 | { | ||
| 473 | double out_low, out_high; | ||
| 474 | |||
| 475 | ✗ | while (len--) { | |
| 476 | ✗ | p->pos = p->pos ? p->pos - 1 : N - 1; | |
| 477 | #define _ out_low += p->coefs[j] * p->previous[ch][p->pos + j].in \ | ||
| 478 | - p->coefs[2*N+2 + j] * p->previous[ch][p->pos + j].out_low, j++; | ||
| 479 | { | ||
| 480 | ✗ | int j = 1; | |
| 481 | ✗ | out_low = p->coefs[0] * *ibuf; | |
| 482 | ✗ | CONVOLVE | |
| 483 | ✗ | *obuf_low++ = out_low; | |
| 484 | } | ||
| 485 | #undef _ | ||
| 486 | #define _ out_high += p->coefs[j+N+1] * p->previous[ch][p->pos + j].in \ | ||
| 487 | - p->coefs[2*N+2 + j] * p->previous[ch][p->pos + j].out_high, j++; | ||
| 488 | { | ||
| 489 | ✗ | int j = 1; | |
| 490 | ✗ | out_high = p->coefs[N+1] * *ibuf; | |
| 491 | ✗ | CONVOLVE | |
| 492 | ✗ | *obuf_high++ = out_high; | |
| 493 | } | ||
| 494 | ✗ | p->previous[ch][p->pos + N].in = p->previous[ch][p->pos].in = *ibuf++; | |
| 495 | ✗ | p->previous[ch][p->pos + N].out_low = p->previous[ch][p->pos].out_low = out_low; | |
| 496 | ✗ | p->previous[ch][p->pos + N].out_high = p->previous[ch][p->pos].out_high = out_high; | |
| 497 | } | ||
| 498 | ✗ | } | |
| 499 | |||
| 500 | ✗ | static int mcompand_channel(MCompandContext *c, CompBand *l, double *ibuf, double *obuf, int len, int ch) | |
| 501 | { | ||
| 502 | int i; | ||
| 503 | |||
| 504 | ✗ | for (i = 0; i < len; i++) { | |
| 505 | double level_in_lin, level_out_lin, checkbuf; | ||
| 506 | /* Maintain the volume fields by simulating a leaky pump circuit */ | ||
| 507 | ✗ | update_volume(l, fabs(ibuf[i]), ch); | |
| 508 | |||
| 509 | /* Volume memory is updated: perform compand */ | ||
| 510 | ✗ | level_in_lin = l->volume[ch]; | |
| 511 | ✗ | level_out_lin = get_volume(&l->transfer_fn, level_in_lin); | |
| 512 | |||
| 513 | ✗ | if (c->delay_buf_size <= 0) { | |
| 514 | ✗ | checkbuf = ibuf[i] * level_out_lin; | |
| 515 | ✗ | obuf[i] = checkbuf; | |
| 516 | } else { | ||
| 517 | ✗ | double *delay_buf = (double *)l->delay_buf->extended_data[ch]; | |
| 518 | |||
| 519 | /* FIXME: note that this lookahead algorithm is really lame: | ||
| 520 | the response to a peak is released before the peak | ||
| 521 | arrives. */ | ||
| 522 | |||
| 523 | /* because volume application delays differ band to band, but | ||
| 524 | total delay doesn't, the volume is applied in an iteration | ||
| 525 | preceding that in which the sample goes to obuf, except in | ||
| 526 | the band(s) with the longest vol app delay. | ||
| 527 | |||
| 528 | the offset between delay_buf_ptr and the sample to apply | ||
| 529 | vol to, is a constant equal to the difference between this | ||
| 530 | band's delay and the longest delay of all the bands. */ | ||
| 531 | |||
| 532 | ✗ | if (l->delay_buf_cnt >= l->delay_size) { | |
| 533 | ✗ | checkbuf = | |
| 534 | ✗ | delay_buf[(l->delay_buf_ptr + | |
| 535 | ✗ | c->delay_buf_size - | |
| 536 | ✗ | l->delay_size) % c->delay_buf_size] * level_out_lin; | |
| 537 | ✗ | delay_buf[(l->delay_buf_ptr + c->delay_buf_size - | |
| 538 | ✗ | l->delay_size) % c->delay_buf_size] = checkbuf; | |
| 539 | } | ||
| 540 | ✗ | if (l->delay_buf_cnt >= c->delay_buf_size) { | |
| 541 | ✗ | obuf[i] = delay_buf[l->delay_buf_ptr]; | |
| 542 | } else { | ||
| 543 | ✗ | l->delay_buf_cnt++; | |
| 544 | } | ||
| 545 | ✗ | delay_buf[l->delay_buf_ptr++] = ibuf[i]; | |
| 546 | ✗ | l->delay_buf_ptr %= c->delay_buf_size; | |
| 547 | } | ||
| 548 | } | ||
| 549 | |||
| 550 | ✗ | return 0; | |
| 551 | } | ||
| 552 | |||
| 553 | ✗ | static int filter_frame(AVFilterLink *inlink, AVFrame *in) | |
| 554 | { | ||
| 555 | ✗ | AVFilterContext *ctx = inlink->dst; | |
| 556 | ✗ | AVFilterLink *outlink = ctx->outputs[0]; | |
| 557 | ✗ | MCompandContext *s = ctx->priv; | |
| 558 | AVFrame *out, *abuf, *bbuf, *cbuf; | ||
| 559 | int ch, band, i; | ||
| 560 | |||
| 561 | ✗ | out = ff_get_audio_buffer(outlink, in->nb_samples); | |
| 562 | ✗ | if (!out) { | |
| 563 | ✗ | av_frame_free(&in); | |
| 564 | ✗ | return AVERROR(ENOMEM); | |
| 565 | } | ||
| 566 | |||
| 567 | ✗ | if (s->band_samples < in->nb_samples) { | |
| 568 | ✗ | av_frame_free(&s->band_buf1); | |
| 569 | ✗ | av_frame_free(&s->band_buf2); | |
| 570 | ✗ | av_frame_free(&s->band_buf3); | |
| 571 | |||
| 572 | ✗ | s->band_buf1 = ff_get_audio_buffer(outlink, in->nb_samples); | |
| 573 | ✗ | s->band_buf2 = ff_get_audio_buffer(outlink, in->nb_samples); | |
| 574 | ✗ | s->band_buf3 = ff_get_audio_buffer(outlink, in->nb_samples); | |
| 575 | ✗ | s->band_samples = in->nb_samples; | |
| 576 | } | ||
| 577 | |||
| 578 | ✗ | for (ch = 0; ch < outlink->ch_layout.nb_channels; ch++) { | |
| 579 | ✗ | double *a, *dst = (double *)out->extended_data[ch]; | |
| 580 | |||
| 581 | ✗ | for (band = 0, abuf = in, bbuf = s->band_buf2, cbuf = s->band_buf1; band < s->nb_bands; band++) { | |
| 582 | ✗ | CompBand *b = &s->bands[band]; | |
| 583 | |||
| 584 | ✗ | if (b->topfreq) { | |
| 585 | ✗ | crossover(ch, &b->filter, (double *)abuf->extended_data[ch], | |
| 586 | ✗ | (double *)bbuf->extended_data[ch], (double *)cbuf->extended_data[ch], in->nb_samples); | |
| 587 | } else { | ||
| 588 | ✗ | bbuf = abuf; | |
| 589 | ✗ | abuf = cbuf; | |
| 590 | } | ||
| 591 | |||
| 592 | ✗ | if (abuf == in) | |
| 593 | ✗ | abuf = s->band_buf3; | |
| 594 | ✗ | mcompand_channel(s, b, (double *)bbuf->extended_data[ch], (double *)abuf->extended_data[ch], out->nb_samples, ch); | |
| 595 | ✗ | a = (double *)abuf->extended_data[ch]; | |
| 596 | ✗ | for (i = 0; i < out->nb_samples; i++) { | |
| 597 | ✗ | dst[i] += a[i]; | |
| 598 | } | ||
| 599 | |||
| 600 | ✗ | FFSWAP(AVFrame *, abuf, cbuf); | |
| 601 | } | ||
| 602 | } | ||
| 603 | |||
| 604 | ✗ | out->pts = in->pts; | |
| 605 | ✗ | av_frame_free(&in); | |
| 606 | ✗ | return ff_filter_frame(outlink, out); | |
| 607 | } | ||
| 608 | |||
| 609 | ✗ | static int request_frame(AVFilterLink *outlink) | |
| 610 | { | ||
| 611 | ✗ | AVFilterContext *ctx = outlink->src; | |
| 612 | int ret; | ||
| 613 | |||
| 614 | ✗ | ret = ff_request_frame(ctx->inputs[0]); | |
| 615 | |||
| 616 | ✗ | return ret; | |
| 617 | } | ||
| 618 | |||
| 619 | static const AVFilterPad mcompand_inputs[] = { | ||
| 620 | { | ||
| 621 | .name = "default", | ||
| 622 | .type = AVMEDIA_TYPE_AUDIO, | ||
| 623 | .filter_frame = filter_frame, | ||
| 624 | }, | ||
| 625 | }; | ||
| 626 | |||
| 627 | static const AVFilterPad mcompand_outputs[] = { | ||
| 628 | { | ||
| 629 | .name = "default", | ||
| 630 | .type = AVMEDIA_TYPE_AUDIO, | ||
| 631 | .request_frame = request_frame, | ||
| 632 | .config_props = config_output, | ||
| 633 | }, | ||
| 634 | }; | ||
| 635 | |||
| 636 | |||
| 637 | const FFFilter ff_af_mcompand = { | ||
| 638 | .p.name = "mcompand", | ||
| 639 | .p.description = NULL_IF_CONFIG_SMALL( | ||
| 640 | "Multiband Compress or expand audio dynamic range."), | ||
| 641 | .p.priv_class = &mcompand_class, | ||
| 642 | .priv_size = sizeof(MCompandContext), | ||
| 643 | .uninit = uninit, | ||
| 644 | FILTER_INPUTS(mcompand_inputs), | ||
| 645 | FILTER_OUTPUTS(mcompand_outputs), | ||
| 646 | FILTER_SINGLE_SAMPLEFMT(AV_SAMPLE_FMT_DBLP), | ||
| 647 | }; | ||
| 648 |