[FFmpeg-devel] [PATCH] avfilter: add asubcut filter

Paul B Mahol onemda at gmail.com
Mon Nov 30 16:25:04 EET 2020


On Mon, Nov 30, 2020 at 2:58 PM Marton Balint <cus at passwd.hu> wrote:

>
>
> On Mon, 30 Nov 2020, Paul B Mahol wrote:
>
> > Signed-off-by: Paul B Mahol <onemda at gmail.com>
> > ---
> > doc/filters.texi           | 25 +++++++++++
> > libavfilter/Makefile       |  1 +
> > libavfilter/af_asupercut.c | 89 ++++++++++++++++++++++++++++++--------
> > libavfilter/allfilters.c   |  1 +
> > 4 files changed, 97 insertions(+), 19 deletions(-)
> >
> > diff --git a/doc/filters.texi b/doc/filters.texi
> > index 06ed28f3f9..348cdae4e9 100644
> > --- a/doc/filters.texi
> > +++ b/doc/filters.texi
> > @@ -2636,6 +2636,28 @@ Default value is 20.
> >
> > This filter supports the all above options as @ref{commands}.
> >
> > + at section asubcut
> > +Cut subwoofer frequencies.
>
> Please explain in the documentation how is this different from a simple
> highpass filter, and when it is preferable to use this and when to use
> highpass.
>

This runs internally several cascaded filters to give a butterworth response
of given order. Also odd orders are butterworth as they use single pole
highpass
which is not available at all in a high pass filter (its single pole has
different characteristics).
This makes sense to use to cut-off unwanted low frequencies more
efficiently than single high pass filter.
Thus it has an order option to control curve steepness.
Simple highpass is just meant for custom usage when one needs more fine
grained control at expense of a huge filter graph for doing anything not
trivial.
Note the extra level option which is needed if one wants to avoid clipping
and do not want to use other filters for that.
In a perfect world, clipping would never happen as phase response would
remain zero, but in practice that is impossible to do with IIR filters.
There is little hack for that scenario, but it is not always 100% effective
and also it is not realtime, as it needs two areverse filters to do the job
and full input signal.


>
> Thanks,
> Marton
>
> > +
> > +The filter accepts the following options:
> > +
> > + at table @option
> > + at item cutoff
> > +Set cutoff frequency in Hertz. Allowed range is 2 to 200.
> > +Default value is 20.
> > +
> > + at item order
> > +Set filter order. Available values are from 3 to 20.
> > +Default value is 10.
> > +
> > + at item level
> > +Set input gain level. Allowed range is from 0 to 1. Default value is 1.
> > + at end table
> > +
> > + at subsection Commands
> > +
> > +This filter supports the all above options as @ref{commands}.
> > +
> > @section asupercut
> > Cut super frequencies.
> >
> > @@ -2649,6 +2671,9 @@ Default value is 20000.
> > @item order
> > Set filter order. Available values are from 3 to 20.
> > Default value is 10.
> > +
> > + at item level
> > +Set input gain level. Allowed range is from 0 to 1. Default value is 1.
> > @end table
> >
> > @subsection Commands
> > diff --git a/libavfilter/Makefile b/libavfilter/Makefile
> > index cff9402989..03998dc064 100644
> > --- a/libavfilter/Makefile
> > +++ b/libavfilter/Makefile
> > @@ -90,6 +90,7 @@ OBJS-$(CONFIG_ASR_FILTER)                    +=
> af_asr.o
> > OBJS-$(CONFIG_ASTATS_FILTER)                 += af_astats.o
> > OBJS-$(CONFIG_ASTREAMSELECT_FILTER)          += f_streamselect.o
> framesync.o
> > OBJS-$(CONFIG_ASUBBOOST_FILTER)              += af_asubboost.o
> > +OBJS-$(CONFIG_ASUBCUT_FILTER)                += af_asupercut.o
> > OBJS-$(CONFIG_ASUPERCUT_FILTER)              += af_asupercut.o
> > OBJS-$(CONFIG_ATEMPO_FILTER)                 += af_atempo.o
> > OBJS-$(CONFIG_ATRIM_FILTER)                  += trim.o
> > diff --git a/libavfilter/af_asupercut.c b/libavfilter/af_asupercut.c
> > index ef241405d8..6d0a2b79a9 100644
> > --- a/libavfilter/af_asupercut.c
> > +++ b/libavfilter/af_asupercut.c
> > @@ -32,6 +32,7 @@ typedef struct ASuperCutContext {
> >     const AVClass *class;
> >
> >     double cutoff;
> > +    double level;
> >     int order;
> >
> >     int filter_count;
> > @@ -95,27 +96,52 @@ static int get_coeffs(AVFilterContext *ctx)
> >     s->filter_count = s->order / 2 + (s->order & 1);
> >     calc_q_factors(s->order, q);
> >
> > -    if (s->order & 1) {
> > -        BiquadCoeffs *coeffs = &s->coeffs[0];
> > -        double omega = 2. * tan(M_PI * w0);
> > +    if (!strcmp(ctx->filter->name, "asubcut")) {
> > +        if (s->order & 1) {
> > +            BiquadCoeffs *coeffs = &s->coeffs[0];
> > +            double omega = 2. * tan(M_PI * w0);
> >
> > -        coeffs->b0 = omega / (2. + omega);
> > -        coeffs->b1 = coeffs->b0;
> > -        coeffs->b2 = 0.;
> > -        coeffs->a1 = -(omega - 2.) / (2. + omega);
> > -        coeffs->a2 = 0.;
> > -    }
> > +            coeffs->b0 = 2. / (2. + omega);
> > +            coeffs->b1 = -coeffs->b0;
> > +            coeffs->b2 = 0.;
> > +            coeffs->a1 = -(omega - 2.) / (2. + omega);
> > +            coeffs->a2 = 0.;
> > +        }
> > +
> > +        for (int b = (s->order & 1); b < s->filter_count; b++) {
> > +            BiquadCoeffs *coeffs = &s->coeffs[b];
> > +            const int idx = b - (s->order & 1);
> > +            double norm = 1.0 / (1.0 + K / q[idx] + K * K);
> >
> > -    for (int b = (s->order & 1); b < s->filter_count; b++) {
> > -        BiquadCoeffs *coeffs = &s->coeffs[b];
> > -        const int idx = b - (s->order & 1);
> > -        double norm = 1.0 / (1.0 + K / q[idx] + K * K);
> > +            coeffs->b0 = norm;
> > +            coeffs->b1 = -2.0 * coeffs->b0;
> > +            coeffs->b2 = coeffs->b0;
> > +            coeffs->a1 = -2.0 * (K * K - 1.0) * norm;
> > +            coeffs->a2 = -(1.0 - K / q[idx] + K * K) * norm;
> > +        }
> > +    } else {
> > +        if (s->order & 1) {
> > +            BiquadCoeffs *coeffs = &s->coeffs[0];
> > +            double omega = 2. * tan(M_PI * w0);
> > +
> > +            coeffs->b0 = omega / (2. + omega);
> > +            coeffs->b1 = coeffs->b0;
> > +            coeffs->b2 = 0.;
> > +            coeffs->a1 = -(omega - 2.) / (2. + omega);
> > +            coeffs->a2 = 0.;
> > +        }
> >
> > -        coeffs->b0 = K * K * norm;
> > -        coeffs->b1 = 2.0 * coeffs->b0;
> > -        coeffs->b2 = coeffs->b0;
> > -        coeffs->a1 = -2.0 * (K * K - 1.0) * norm;
> > -        coeffs->a2 = -(1.0 - K / q[idx] + K * K) * norm;
> > +        for (int b = (s->order & 1); b < s->filter_count; b++) {
> > +            BiquadCoeffs *coeffs = &s->coeffs[b];
> > +            const int idx = b - (s->order & 1);
> > +            double norm = 1.0 / (1.0 + K / q[idx] + K * K);
> > +
> > +            coeffs->b0 = K * K * norm;
> > +            coeffs->b1 = 2.0 * coeffs->b0;
> > +            coeffs->b2 = coeffs->b0;
> > +            coeffs->a1 = -2.0 * (K * K - 1.0) * norm;
> > +            coeffs->a2 = -(1.0 - K / q[idx] + K * K) * norm;
> > +        }
> >     }
> >
> >     return 0;
> > @@ -135,6 +161,7 @@ static int filter_channels_## name(AVFilterContext
> *ctx, void *arg, \
> >     AVFrame *in = td->in;                                           \
> >     const int start = (in->channels * jobnr) / nb_jobs;             \
> >     const int end = (in->channels * (jobnr+1)) / nb_jobs;           \
> > +    const double level = s->level;                                  \
> >                                                                     \
> >     for (int ch = start; ch < end; ch++) {                          \
> >         const type *src = (const type *)in->extended_data[ch];      \
> > @@ -150,7 +177,7 @@ static int filter_channels_## name(AVFilterContext
> *ctx, void *arg, \
> >             type *w = ((type *)s->w->extended_data[ch]) + b * 2;    \
> >                                                                     \
> >             for (int n = 0; n < in->nb_samples; n++) {              \
> > -                type sin = b ? dst[n] : src[n];                     \
> > +                type sin = b ? dst[n] : src[n] * level;             \
> >                 type sout = sin * b0 + w[0];                        \
> >                                                                     \
> >                 w[0] = b1 * sin + w[1] + a1 * sout;                 \
> > @@ -240,6 +267,7 @@ static av_cold void uninit(AVFilterContext *ctx)
> > static const AVOption asupercut_options[] = {
> >     { "cutoff", "set cutoff frequency", OFFSET(cutoff),
> AV_OPT_TYPE_DOUBLE, {.dbl=20000}, 20000, 192000, FLAGS },
> >     { "order",  "set filter order",     OFFSET(order),
> AV_OPT_TYPE_INT,    {.i64=10},        3,     20, FLAGS },
> > +    { "level",  "set input level",      OFFSET(level),
> AV_OPT_TYPE_DOUBLE, {.dbl=1.},        0.,    1., FLAGS },
> >     { NULL }
> > };
> >
> > @@ -276,3 +304,26 @@ AVFilter ff_af_asupercut = {
> >     .flags           = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC |
> >                        AVFILTER_FLAG_SLICE_THREADS,
> > };
> > +
> > +static const AVOption asubcut_options[] = {
> > +    { "cutoff", "set cutoff frequency", OFFSET(cutoff),
> AV_OPT_TYPE_DOUBLE, {.dbl=20},  2, 200, FLAGS },
> > +    { "order",  "set filter order",     OFFSET(order),
> AV_OPT_TYPE_INT,    {.i64=10},  3,  20, FLAGS },
> > +    { "level",  "set input level",      OFFSET(level),
> AV_OPT_TYPE_DOUBLE, {.dbl=1.}, 0.,  1., FLAGS },
> > +    { NULL }
> > +};
> > +
> > +AVFILTER_DEFINE_CLASS(asubcut);
> > +
> > +AVFilter ff_af_asubcut = {
> > +    .name            = "asubcut",
> > +    .description     = NULL_IF_CONFIG_SMALL("Cut subwoofer
> frequencies."),
> > +    .query_formats   = query_formats,
> > +    .priv_size       = sizeof(ASuperCutContext),
> > +    .priv_class      = &asubcut_class,
> > +    .uninit          = uninit,
> > +    .inputs          = inputs,
> > +    .outputs         = outputs,
> > +    .process_command = process_command,
> > +    .flags           = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC |
> > +                       AVFILTER_FLAG_SLICE_THREADS,
> > +};
> > diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
> > index 83f434bc27..d0fb7bfe9d 100644
> > --- a/libavfilter/allfilters.c
> > +++ b/libavfilter/allfilters.c
> > @@ -83,6 +83,7 @@ extern AVFilter ff_af_asr;
> > extern AVFilter ff_af_astats;
> > extern AVFilter ff_af_astreamselect;
> > extern AVFilter ff_af_asubboost;
> > +extern AVFilter ff_af_asubcut;
> > extern AVFilter ff_af_asupercut;
> > extern AVFilter ff_af_atempo;
> > extern AVFilter ff_af_atrim;
> > --
> > 2.17.1
> >
> > _______________________________________________
> > ffmpeg-devel mailing list
> > ffmpeg-devel at ffmpeg.org
> > https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> >
> > To unsubscribe, visit link above, or email
> > ffmpeg-devel-request at ffmpeg.org with subject "unsubscribe".
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel at ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request at ffmpeg.org with subject "unsubscribe".


More information about the ffmpeg-devel mailing list