]> git.sesse.net Git - ffmpeg/blob - libavfilter/af_firequalizer.c
78d776713cb561878e0d981322b87cacaa482250
[ffmpeg] / libavfilter / af_firequalizer.c
1 /*
2  * Copyright (c) 2016 Muhammad Faiz <mfcc64@gmail.com>
3  *
4  * This file is part of FFmpeg.
5  *
6  * FFmpeg is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * FFmpeg is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with FFmpeg; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19  */
20
21 #include "libavutil/opt.h"
22 #include "libavutil/eval.h"
23 #include "libavutil/avassert.h"
24 #include "libavcodec/avfft.h"
25 #include "avfilter.h"
26 #include "internal.h"
27 #include "audio.h"
28
29 #define RDFT_BITS_MIN 4
30 #define RDFT_BITS_MAX 16
31
32 enum WindowFunc {
33     WFUNC_RECTANGULAR,
34     WFUNC_HANN,
35     WFUNC_HAMMING,
36     WFUNC_BLACKMAN,
37     WFUNC_NUTTALL3,
38     WFUNC_MNUTTALL3,
39     WFUNC_NUTTALL,
40     WFUNC_BNUTTALL,
41     WFUNC_BHARRIS,
42     WFUNC_TUKEY,
43     NB_WFUNC
44 };
45
46 enum Scale {
47     SCALE_LINLIN,
48     SCALE_LINLOG,
49     SCALE_LOGLIN,
50     SCALE_LOGLOG,
51     NB_SCALE
52 };
53
54 #define NB_GAIN_ENTRY_MAX 4096
55 typedef struct {
56     double  freq;
57     double  gain;
58 } GainEntry;
59
60 typedef struct {
61     int buf_idx;
62     int overlap_idx;
63 } OverlapIndex;
64
65 typedef struct {
66     const AVClass *class;
67
68     RDFTContext   *analysis_irdft;
69     RDFTContext   *rdft;
70     RDFTContext   *irdft;
71     int           analysis_rdft_len;
72     int           rdft_len;
73
74     float         *analysis_buf;
75     float         *kernel_tmp_buf;
76     float         *kernel_buf;
77     float         *conv_buf;
78     OverlapIndex  *conv_idx;
79     int           fir_len;
80     int           nsamples_max;
81     int64_t       next_pts;
82     int           frame_nsamples_max;
83     int           remaining;
84
85     char          *gain_cmd;
86     char          *gain_entry_cmd;
87     const char    *gain;
88     const char    *gain_entry;
89     double        delay;
90     double        accuracy;
91     int           wfunc;
92     int           fixed;
93     int           multi;
94     int           zero_phase;
95     int           scale;
96
97     int           nb_gain_entry;
98     int           gain_entry_err;
99     GainEntry     gain_entry_tbl[NB_GAIN_ENTRY_MAX];
100 } FIREqualizerContext;
101
102 #define OFFSET(x) offsetof(FIREqualizerContext, x)
103 #define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
104
105 static const AVOption firequalizer_options[] = {
106     { "gain", "set gain curve", OFFSET(gain), AV_OPT_TYPE_STRING, { .str = "gain_interpolate(f)" }, 0, 0, FLAGS },
107     { "gain_entry", "set gain entry", OFFSET(gain_entry), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, FLAGS },
108     { "delay", "set delay", OFFSET(delay), AV_OPT_TYPE_DOUBLE, { .dbl = 0.01 }, 0.0, 1e10, FLAGS },
109     { "accuracy", "set accuracy", OFFSET(accuracy), AV_OPT_TYPE_DOUBLE, { .dbl = 5.0 }, 0.0, 1e10, FLAGS },
110     { "wfunc", "set window function", OFFSET(wfunc), AV_OPT_TYPE_INT, { .i64 = WFUNC_HANN }, 0, NB_WFUNC-1, FLAGS, "wfunc" },
111         { "rectangular", "rectangular window", 0, AV_OPT_TYPE_CONST, { .i64 = WFUNC_RECTANGULAR }, 0, 0, FLAGS, "wfunc" },
112         { "hann", "hann window", 0, AV_OPT_TYPE_CONST, { .i64 = WFUNC_HANN }, 0, 0, FLAGS, "wfunc" },
113         { "hamming", "hamming window", 0, AV_OPT_TYPE_CONST, { .i64 = WFUNC_HAMMING }, 0, 0, FLAGS, "wfunc" },
114         { "blackman", "blackman window", 0, AV_OPT_TYPE_CONST, { .i64 = WFUNC_BLACKMAN }, 0, 0, FLAGS, "wfunc" },
115         { "nuttall3", "3-term nuttall window", 0, AV_OPT_TYPE_CONST, { .i64 = WFUNC_NUTTALL3 }, 0, 0, FLAGS, "wfunc" },
116         { "mnuttall3", "minimum 3-term nuttall window", 0, AV_OPT_TYPE_CONST, { .i64 = WFUNC_MNUTTALL3 }, 0, 0, FLAGS, "wfunc" },
117         { "nuttall", "nuttall window", 0, AV_OPT_TYPE_CONST, { .i64 = WFUNC_NUTTALL }, 0, 0, FLAGS, "wfunc" },
118         { "bnuttall", "blackman-nuttall window", 0, AV_OPT_TYPE_CONST, { .i64 = WFUNC_BNUTTALL }, 0, 0, FLAGS, "wfunc" },
119         { "bharris", "blackman-harris window", 0, AV_OPT_TYPE_CONST, { .i64 = WFUNC_BHARRIS }, 0, 0, FLAGS, "wfunc" },
120         { "tukey", "tukey window", 0, AV_OPT_TYPE_CONST, { .i64 = WFUNC_TUKEY }, 0, 0, FLAGS, "wfunc" },
121     { "fixed", "set fixed frame samples", OFFSET(fixed), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS },
122     { "multi", "set multi channels mode", OFFSET(multi), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS },
123     { "zero_phase", "set zero phase mode", OFFSET(zero_phase), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS },
124     { "scale", "set gain scale", OFFSET(scale), AV_OPT_TYPE_INT, { .i64 = SCALE_LINLOG }, 0, NB_SCALE-1, FLAGS, "scale" },
125         { "linlin", "linear-freq linear-gain", 0, AV_OPT_TYPE_CONST, { .i64 = SCALE_LINLIN }, 0, 0, FLAGS, "scale" },
126         { "linlog", "linear-freq logarithmic-gain", 0, AV_OPT_TYPE_CONST, { .i64 = SCALE_LINLOG }, 0, 0, FLAGS, "scale" },
127         { "loglin", "logarithmic-freq linear-gain", 0, AV_OPT_TYPE_CONST, { .i64 = SCALE_LOGLIN }, 0, 0, FLAGS, "scale" },
128         { "loglog", "logarithmic-freq logarithmic-gain", 0, AV_OPT_TYPE_CONST, { .i64 = SCALE_LOGLOG }, 0, 0, FLAGS, "scale" },
129     { NULL }
130 };
131
132 AVFILTER_DEFINE_CLASS(firequalizer);
133
134 static void common_uninit(FIREqualizerContext *s)
135 {
136     av_rdft_end(s->analysis_irdft);
137     av_rdft_end(s->rdft);
138     av_rdft_end(s->irdft);
139     s->analysis_irdft = s->rdft = s->irdft = NULL;
140
141     av_freep(&s->analysis_buf);
142     av_freep(&s->kernel_tmp_buf);
143     av_freep(&s->kernel_buf);
144     av_freep(&s->conv_buf);
145     av_freep(&s->conv_idx);
146 }
147
148 static av_cold void uninit(AVFilterContext *ctx)
149 {
150     FIREqualizerContext *s = ctx->priv;
151
152     common_uninit(s);
153     av_freep(&s->gain_cmd);
154     av_freep(&s->gain_entry_cmd);
155 }
156
157 static int query_formats(AVFilterContext *ctx)
158 {
159     AVFilterChannelLayouts *layouts;
160     AVFilterFormats *formats;
161     static const enum AVSampleFormat sample_fmts[] = {
162         AV_SAMPLE_FMT_FLTP,
163         AV_SAMPLE_FMT_NONE
164     };
165     int ret;
166
167     layouts = ff_all_channel_counts();
168     if (!layouts)
169         return AVERROR(ENOMEM);
170     ret = ff_set_common_channel_layouts(ctx, layouts);
171     if (ret < 0)
172         return ret;
173
174     formats = ff_make_format_list(sample_fmts);
175     if (!formats)
176         return AVERROR(ENOMEM);
177     ret = ff_set_common_formats(ctx, formats);
178     if (ret < 0)
179         return ret;
180
181     formats = ff_all_samplerates();
182     if (!formats)
183         return AVERROR(ENOMEM);
184     return ff_set_common_samplerates(ctx, formats);
185 }
186
187 static void fast_convolute(FIREqualizerContext *s, const float *kernel_buf, float *conv_buf,
188                            OverlapIndex *idx, float *data, int nsamples)
189 {
190     if (nsamples <= s->nsamples_max) {
191         float *buf = conv_buf + idx->buf_idx * s->rdft_len;
192         float *obuf = conv_buf + !idx->buf_idx * s->rdft_len + idx->overlap_idx;
193         int k;
194
195         memcpy(buf, data, nsamples * sizeof(*data));
196         memset(buf + nsamples, 0, (s->rdft_len - nsamples) * sizeof(*data));
197         av_rdft_calc(s->rdft, buf);
198
199         buf[0] *= kernel_buf[0];
200         buf[1] *= kernel_buf[1];
201         for (k = 2; k < s->rdft_len; k += 2) {
202             float re, im;
203             re = buf[k] * kernel_buf[k] - buf[k+1] * kernel_buf[k+1];
204             im = buf[k] * kernel_buf[k+1] + buf[k+1] * kernel_buf[k];
205             buf[k] = re;
206             buf[k+1] = im;
207         }
208
209         av_rdft_calc(s->irdft, buf);
210         for (k = 0; k < s->rdft_len - idx->overlap_idx; k++)
211             buf[k] += obuf[k];
212         memcpy(data, buf, nsamples * sizeof(*data));
213         idx->buf_idx = !idx->buf_idx;
214         idx->overlap_idx = nsamples;
215     } else {
216         while (nsamples > s->nsamples_max * 2) {
217             fast_convolute(s, kernel_buf, conv_buf, idx, data, s->nsamples_max);
218             data += s->nsamples_max;
219             nsamples -= s->nsamples_max;
220         }
221         fast_convolute(s, kernel_buf, conv_buf, idx, data, nsamples/2);
222         fast_convolute(s, kernel_buf, conv_buf, idx, data + nsamples/2, nsamples - nsamples/2);
223     }
224 }
225
226 static double entry_func(void *p, double freq, double gain)
227 {
228     AVFilterContext *ctx = p;
229     FIREqualizerContext *s = ctx->priv;
230
231     if (s->nb_gain_entry >= NB_GAIN_ENTRY_MAX) {
232         av_log(ctx, AV_LOG_ERROR, "entry table overflow.\n");
233         s->gain_entry_err = AVERROR(EINVAL);
234         return 0;
235     }
236
237     if (isnan(freq)) {
238         av_log(ctx, AV_LOG_ERROR, "nan frequency (%g, %g).\n", freq, gain);
239         s->gain_entry_err = AVERROR(EINVAL);
240         return 0;
241     }
242
243     if (s->nb_gain_entry > 0 && freq <= s->gain_entry_tbl[s->nb_gain_entry - 1].freq) {
244         av_log(ctx, AV_LOG_ERROR, "unsorted frequency (%g, %g).\n", freq, gain);
245         s->gain_entry_err = AVERROR(EINVAL);
246         return 0;
247     }
248
249     s->gain_entry_tbl[s->nb_gain_entry].freq = freq;
250     s->gain_entry_tbl[s->nb_gain_entry].gain = gain;
251     s->nb_gain_entry++;
252     return 0;
253 }
254
255 static int gain_entry_compare(const void *key, const void *memb)
256 {
257     const double *freq = key;
258     const GainEntry *entry = memb;
259
260     if (*freq < entry[0].freq)
261         return -1;
262     if (*freq > entry[1].freq)
263         return 1;
264     return 0;
265 }
266
267 static double gain_interpolate_func(void *p, double freq)
268 {
269     AVFilterContext *ctx = p;
270     FIREqualizerContext *s = ctx->priv;
271     GainEntry *res;
272     double d0, d1, d;
273
274     if (isnan(freq))
275         return freq;
276
277     if (!s->nb_gain_entry)
278         return 0;
279
280     if (freq <= s->gain_entry_tbl[0].freq)
281         return s->gain_entry_tbl[0].gain;
282
283     if (freq >= s->gain_entry_tbl[s->nb_gain_entry-1].freq)
284         return s->gain_entry_tbl[s->nb_gain_entry-1].gain;
285
286     res = bsearch(&freq, &s->gain_entry_tbl, s->nb_gain_entry - 1, sizeof(*res), gain_entry_compare);
287     av_assert0(res);
288
289     d  = res[1].freq - res[0].freq;
290     d0 = freq - res[0].freq;
291     d1 = res[1].freq - freq;
292
293     if (d0 && d1)
294         return (d0 * res[1].gain + d1 * res[0].gain) / d;
295
296     if (d0)
297         return res[1].gain;
298
299     return res[0].gain;
300 }
301
302 static const char *const var_names[] = {
303     "f",
304     "sr",
305     "ch",
306     "chid",
307     "chs",
308     "chlayout",
309     NULL
310 };
311
312 enum VarOffset {
313     VAR_F,
314     VAR_SR,
315     VAR_CH,
316     VAR_CHID,
317     VAR_CHS,
318     VAR_CHLAYOUT,
319     VAR_NB
320 };
321
322 static int generate_kernel(AVFilterContext *ctx, const char *gain, const char *gain_entry)
323 {
324     FIREqualizerContext *s = ctx->priv;
325     AVFilterLink *inlink = ctx->inputs[0];
326     const char *gain_entry_func_names[] = { "entry", NULL };
327     const char *gain_func_names[] = { "gain_interpolate", NULL };
328     double (*gain_entry_funcs[])(void *, double, double) = { entry_func, NULL };
329     double (*gain_funcs[])(void *, double) = { gain_interpolate_func, NULL };
330     double vars[VAR_NB];
331     AVExpr *gain_expr;
332     int ret, k, center, ch;
333     int xlog = s->scale == SCALE_LOGLIN || s->scale == SCALE_LOGLOG;
334     int ylog = s->scale == SCALE_LINLOG || s->scale == SCALE_LOGLOG;
335
336     s->nb_gain_entry = 0;
337     s->gain_entry_err = 0;
338     if (gain_entry) {
339         double result = 0.0;
340         ret = av_expr_parse_and_eval(&result, gain_entry, NULL, NULL, NULL, NULL,
341                                      gain_entry_func_names, gain_entry_funcs, ctx, 0, ctx);
342         if (ret < 0)
343             return ret;
344         if (s->gain_entry_err < 0)
345             return s->gain_entry_err;
346     }
347
348     av_log(ctx, AV_LOG_DEBUG, "nb_gain_entry = %d.\n", s->nb_gain_entry);
349
350     ret = av_expr_parse(&gain_expr, gain, var_names,
351                         gain_func_names, gain_funcs, NULL, NULL, 0, ctx);
352     if (ret < 0)
353         return ret;
354
355     vars[VAR_CHS] = inlink->channels;
356     vars[VAR_CHLAYOUT] = inlink->channel_layout;
357     vars[VAR_SR] = inlink->sample_rate;
358     for (ch = 0; ch < inlink->channels; ch++) {
359         double result;
360         vars[VAR_CH] = ch;
361         vars[VAR_CHID] = av_channel_layout_extract_channel(inlink->channel_layout, ch);
362         vars[VAR_F] = 0.0;
363         if (xlog)
364             vars[VAR_F] = log2(0.05 * vars[VAR_F]);
365         result = av_expr_eval(gain_expr, vars, ctx);
366         s->analysis_buf[0] = ylog ? pow(10.0, 0.05 * result) : result;
367
368         vars[VAR_F] = 0.5 * inlink->sample_rate;
369         if (xlog)
370             vars[VAR_F] = log2(0.05 * vars[VAR_F]);
371         result = av_expr_eval(gain_expr, vars, ctx);
372         s->analysis_buf[1] = ylog ? pow(10.0, 0.05 * result) : result;
373
374         for (k = 1; k < s->analysis_rdft_len/2; k++) {
375             vars[VAR_F] = k * ((double)inlink->sample_rate /(double)s->analysis_rdft_len);
376             if (xlog)
377                 vars[VAR_F] = log2(0.05 * vars[VAR_F]);
378             result = av_expr_eval(gain_expr, vars, ctx);
379             s->analysis_buf[2*k] = ylog ? pow(10.0, 0.05 * result) : result;
380             s->analysis_buf[2*k+1] = 0.0;
381         }
382
383         av_rdft_calc(s->analysis_irdft, s->analysis_buf);
384         center = s->fir_len / 2;
385
386         for (k = 0; k <= center; k++) {
387             double u = k * (M_PI/center);
388             double win;
389             switch (s->wfunc) {
390             case WFUNC_RECTANGULAR:
391                 win = 1.0;
392                 break;
393             case WFUNC_HANN:
394                 win = 0.5 + 0.5 * cos(u);
395                 break;
396             case WFUNC_HAMMING:
397                 win = 0.53836 + 0.46164 * cos(u);
398                 break;
399             case WFUNC_BLACKMAN:
400                 win = 0.42 + 0.5 * cos(u) + 0.08 * cos(2*u);
401                 break;
402             case WFUNC_NUTTALL3:
403                 win = 0.40897 + 0.5 * cos(u) + 0.09103 * cos(2*u);
404                 break;
405             case WFUNC_MNUTTALL3:
406                 win = 0.4243801 + 0.4973406 * cos(u) + 0.0782793 * cos(2*u);
407                 break;
408             case WFUNC_NUTTALL:
409                 win = 0.355768 + 0.487396 * cos(u) + 0.144232 * cos(2*u) + 0.012604 * cos(3*u);
410                 break;
411             case WFUNC_BNUTTALL:
412                 win = 0.3635819 + 0.4891775 * cos(u) + 0.1365995 * cos(2*u) + 0.0106411 * cos(3*u);
413                 break;
414             case WFUNC_BHARRIS:
415                 win = 0.35875 + 0.48829 * cos(u) + 0.14128 * cos(2*u) + 0.01168 * cos(3*u);
416                 break;
417             case WFUNC_TUKEY:
418                 win = (u <= 0.5 * M_PI) ? 1.0 : (0.5 + 0.5 * cos(2*u - M_PI));
419                 break;
420             default:
421                 av_assert0(0);
422             }
423             s->analysis_buf[k] *= (2.0/s->analysis_rdft_len) * (2.0/s->rdft_len) * win;
424         }
425
426         for (k = 0; k < center - k; k++) {
427             float tmp = s->analysis_buf[k];
428             s->analysis_buf[k] = s->analysis_buf[center - k];
429             s->analysis_buf[center - k] = tmp;
430         }
431
432         for (k = 1; k <= center; k++)
433             s->analysis_buf[center + k] = s->analysis_buf[center - k];
434
435         memset(s->analysis_buf + s->fir_len, 0, (s->rdft_len - s->fir_len) * sizeof(*s->analysis_buf));
436         av_rdft_calc(s->rdft, s->analysis_buf);
437
438         for (k = 0; k < s->rdft_len; k++) {
439             if (isnan(s->analysis_buf[k]) || isinf(s->analysis_buf[k])) {
440                 av_log(ctx, AV_LOG_ERROR, "filter kernel contains nan or infinity.\n");
441                 av_expr_free(gain_expr);
442                 return AVERROR(EINVAL);
443             }
444         }
445
446         memcpy(s->kernel_tmp_buf + ch * s->rdft_len, s->analysis_buf, s->rdft_len * sizeof(*s->analysis_buf));
447         if (!s->multi)
448             break;
449     }
450
451     memcpy(s->kernel_buf, s->kernel_tmp_buf, (s->multi ? inlink->channels : 1) * s->rdft_len * sizeof(*s->kernel_buf));
452     av_expr_free(gain_expr);
453     return 0;
454 }
455
456 static int config_input(AVFilterLink *inlink)
457 {
458     AVFilterContext *ctx = inlink->dst;
459     FIREqualizerContext *s = ctx->priv;
460     int rdft_bits;
461
462     common_uninit(s);
463
464     s->next_pts = 0;
465     s->frame_nsamples_max = 0;
466
467     s->fir_len = FFMAX(2 * (int)(inlink->sample_rate * s->delay) + 1, 3);
468     s->remaining = s->fir_len - 1;
469
470     for (rdft_bits = RDFT_BITS_MIN; rdft_bits <= RDFT_BITS_MAX; rdft_bits++) {
471         s->rdft_len = 1 << rdft_bits;
472         s->nsamples_max = s->rdft_len - s->fir_len + 1;
473         if (s->nsamples_max * 2 >= s->fir_len)
474             break;
475     }
476
477     if (rdft_bits > RDFT_BITS_MAX) {
478         av_log(ctx, AV_LOG_ERROR, "too large delay, please decrease it.\n");
479         return AVERROR(EINVAL);
480     }
481
482     if (!(s->rdft = av_rdft_init(rdft_bits, DFT_R2C)) || !(s->irdft = av_rdft_init(rdft_bits, IDFT_C2R)))
483         return AVERROR(ENOMEM);
484
485     for ( ; rdft_bits <= RDFT_BITS_MAX; rdft_bits++) {
486         s->analysis_rdft_len = 1 << rdft_bits;
487         if (inlink->sample_rate <= s->accuracy * s->analysis_rdft_len)
488             break;
489     }
490
491     if (rdft_bits > RDFT_BITS_MAX) {
492         av_log(ctx, AV_LOG_ERROR, "too small accuracy, please increase it.\n");
493         return AVERROR(EINVAL);
494     }
495
496     if (!(s->analysis_irdft = av_rdft_init(rdft_bits, IDFT_C2R)))
497         return AVERROR(ENOMEM);
498
499     s->analysis_buf = av_malloc_array(s->analysis_rdft_len, sizeof(*s->analysis_buf));
500     s->kernel_tmp_buf = av_malloc_array(s->rdft_len * (s->multi ? inlink->channels : 1), sizeof(*s->kernel_tmp_buf));
501     s->kernel_buf = av_malloc_array(s->rdft_len * (s->multi ? inlink->channels : 1), sizeof(*s->kernel_buf));
502     s->conv_buf   = av_calloc(2 * s->rdft_len * inlink->channels, sizeof(*s->conv_buf));
503     s->conv_idx   = av_calloc(inlink->channels, sizeof(*s->conv_idx));
504     if (!s->analysis_buf || !s->kernel_tmp_buf || !s->kernel_buf || !s->conv_buf || !s->conv_idx)
505         return AVERROR(ENOMEM);
506
507     av_log(ctx, AV_LOG_DEBUG, "sample_rate = %d, channels = %d, analysis_rdft_len = %d, rdft_len = %d, fir_len = %d, nsamples_max = %d.\n",
508            inlink->sample_rate, inlink->channels, s->analysis_rdft_len, s->rdft_len, s->fir_len, s->nsamples_max);
509
510     if (s->fixed)
511         inlink->min_samples = inlink->max_samples = inlink->partial_buf_size = s->nsamples_max;
512
513     return generate_kernel(ctx, s->gain_cmd ? s->gain_cmd : s->gain,
514                            s->gain_entry_cmd ? s->gain_entry_cmd : s->gain_entry);
515 }
516
517 static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
518 {
519     AVFilterContext *ctx = inlink->dst;
520     FIREqualizerContext *s = ctx->priv;
521     int ch;
522
523     for (ch = 0; ch < inlink->channels; ch++) {
524         fast_convolute(s, s->kernel_buf + (s->multi ? ch * s->rdft_len : 0),
525                        s->conv_buf + 2 * ch * s->rdft_len, s->conv_idx + ch,
526                        (float *) frame->extended_data[ch], frame->nb_samples);
527     }
528
529     s->next_pts = AV_NOPTS_VALUE;
530     if (frame->pts != AV_NOPTS_VALUE) {
531         s->next_pts = frame->pts + av_rescale_q(frame->nb_samples, av_make_q(1, inlink->sample_rate), inlink->time_base);
532         if (s->zero_phase)
533             frame->pts -= av_rescale_q(s->fir_len/2, av_make_q(1, inlink->sample_rate), inlink->time_base);
534     }
535     s->frame_nsamples_max = FFMAX(s->frame_nsamples_max, frame->nb_samples);
536     return ff_filter_frame(ctx->outputs[0], frame);
537 }
538
539 static int request_frame(AVFilterLink *outlink)
540 {
541     AVFilterContext *ctx = outlink->src;
542     FIREqualizerContext *s= ctx->priv;
543     int ret;
544
545     ret = ff_request_frame(ctx->inputs[0]);
546     if (ret == AVERROR_EOF && s->remaining > 0 && s->frame_nsamples_max > 0) {
547         AVFrame *frame = ff_get_audio_buffer(outlink, FFMIN(s->remaining, s->frame_nsamples_max));
548
549         if (!frame)
550             return AVERROR(ENOMEM);
551
552         av_samples_set_silence(frame->extended_data, 0, frame->nb_samples, outlink->channels, frame->format);
553         frame->pts = s->next_pts;
554         s->remaining -= frame->nb_samples;
555         ret = filter_frame(ctx->inputs[0], frame);
556     }
557
558     return ret;
559 }
560
561 static int process_command(AVFilterContext *ctx, const char *cmd, const char *args,
562                            char *res, int res_len, int flags)
563 {
564     FIREqualizerContext *s = ctx->priv;
565     int ret = AVERROR(ENOSYS);
566
567     if (!strcmp(cmd, "gain")) {
568         char *gain_cmd;
569
570         gain_cmd = av_strdup(args);
571         if (!gain_cmd)
572             return AVERROR(ENOMEM);
573
574         ret = generate_kernel(ctx, gain_cmd, s->gain_entry_cmd ? s->gain_entry_cmd : s->gain_entry);
575         if (ret >= 0) {
576             av_freep(&s->gain_cmd);
577             s->gain_cmd = gain_cmd;
578         } else {
579             av_freep(&gain_cmd);
580         }
581     } else if (!strcmp(cmd, "gain_entry")) {
582         char *gain_entry_cmd;
583
584         gain_entry_cmd = av_strdup(args);
585         if (!gain_entry_cmd)
586             return AVERROR(ENOMEM);
587
588         ret = generate_kernel(ctx, s->gain_cmd ? s->gain_cmd : s->gain, gain_entry_cmd);
589         if (ret >= 0) {
590             av_freep(&s->gain_entry_cmd);
591             s->gain_entry_cmd = gain_entry_cmd;
592         } else {
593             av_freep(&gain_entry_cmd);
594         }
595     }
596
597     return ret;
598 }
599
600 static const AVFilterPad firequalizer_inputs[] = {
601     {
602         .name           = "default",
603         .config_props   = config_input,
604         .filter_frame   = filter_frame,
605         .type           = AVMEDIA_TYPE_AUDIO,
606         .needs_writable = 1,
607     },
608     { NULL }
609 };
610
611 static const AVFilterPad firequalizer_outputs[] = {
612     {
613         .name           = "default",
614         .request_frame  = request_frame,
615         .type           = AVMEDIA_TYPE_AUDIO,
616     },
617     { NULL }
618 };
619
620 AVFilter ff_af_firequalizer = {
621     .name               = "firequalizer",
622     .description        = NULL_IF_CONFIG_SMALL("Finite Impulse Response Equalizer."),
623     .uninit             = uninit,
624     .query_formats      = query_formats,
625     .process_command    = process_command,
626     .priv_size          = sizeof(FIREqualizerContext),
627     .inputs             = firequalizer_inputs,
628     .outputs            = firequalizer_outputs,
629     .priv_class         = &firequalizer_class,
630 };