FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavfilter/af_mcompand.c
Date: 2022-12-09 07:38:14
Exec Total Coverage
Lines: 0 302 0.0%
Functions: 0 12 0.0%
Branches: 0 136 0.0%

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