FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavfilter/af_mcompand.c
Date: 2024-11-20 23:03:26
Exec Total Coverage
Lines: 0 307 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/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 AVFilter ff_af_mcompand = {
638 .name = "mcompand",
639 .description = NULL_IF_CONFIG_SMALL(
640 "Multiband Compress or expand audio dynamic range."),
641 .priv_size = sizeof(MCompandContext),
642 .priv_class = &mcompand_class,
643 .uninit = uninit,
644 FILTER_INPUTS(mcompand_inputs),
645 FILTER_OUTPUTS(mcompand_outputs),
646 FILTER_SINGLE_SAMPLEFMT(AV_SAMPLE_FMT_DBLP),
647 };
648