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

Michael Niedermayer michael at niedermayer.cc
Thu Aug 11 14:32:13 EEST 2016


On Wed, Aug 10, 2016 at 06:23:32PM +0200, Paul B Mahol wrote:
> Hi,
> 
> patch attached.

>  doc/filters.texi          |   49 +++++++
>  libavfilter/Makefile      |    1 
>  libavfilter/af_acrusher.c |  291 ++++++++++++++++++++++++++++++++++++++++++++++
>  libavfilter/allfilters.c  |    1 
>  4 files changed, 342 insertions(+)
> edb554dcdc55f1658d3570acf9eb1ab8b7699a88  0001-avfilter-add-acrusher-filter.patch
> From 0d50ac2d704ebc719ab2557c1024ed4b58505a28 Mon Sep 17 00:00:00 2001
> From: Paul B Mahol <onemda at gmail.com>
> Date: Wed, 10 Aug 2016 16:11:37 +0200
> Subject: [PATCH] avfilter: add acrusher filter
> 
> ---
>  doc/filters.texi          |  49 ++++++++
>  libavfilter/Makefile      |   1 +
>  libavfilter/af_acrusher.c | 291 ++++++++++++++++++++++++++++++++++++++++++++++
>  libavfilter/allfilters.c  |   1 +
>  4 files changed, 342 insertions(+)
>  create mode 100644 libavfilter/af_acrusher.c
> 
> diff --git a/doc/filters.texi b/doc/filters.texi
> index 9dab959..562fff5 100644
> --- a/doc/filters.texi
> +++ b/doc/filters.texi
> @@ -441,6 +441,55 @@ ffmpeg -i first.flac -i second.flac -filter_complex acrossfade=d=10:o=0:c1=exp:c
>  @end example
>  @end itemize
>  
> + at section acrusher
> +
> +Reduce audio bit resolution.
> +
> +This filter is bit crusher with enhanced funcionality. A bit crusher
> +is used to audibly reduce number of bits an audio signal is sampled
> +with. This doesn't change the bit depth at all, it just produces the
> +effect. Material reduced in bit depth sounds more harsh and "digital".
> +This filter is able to even round to continous values instead of discrete
> +bit depths.
> +Additionally it has a D/C offset which results in different crushing of
> +the lower and the upper half of the signal.
> +An Anti-Aliasing setting is able to produce "softer" crushing sounds.
> +
> +Another feature of this filter is the logarithmic mode.
> +This setting switches from linear distances between bits to logarithmic ones.
> +The result is a much more "natural" sounding crusher which doesn't gate low
> +signals for example. The human ear has a logarithmic perception, too
> +so this kind of crushing is much more pleasant.
> +Logarithmic crushing is also able to get anti-aliased.
> +
> +The filter accepts the following options:
> +
> + at table @option
> + at item level_in
> +Set level in.
> +
> + at item level_out
> +Set level out.
> +
> + at item bits
> +Set bit reduction.
> +
> + at item mix
> +Set mixing ammount.
> +
> + at item mode
> +Can be linear: @code{lin} or logarithmic: @code{log}.
> +
> + at item dc
> +Set DC.
> +
> + at item aa
> +Set anti-aliasing.
> +
> + at item samples
> +Set sample reduction.
> + at end table
> +
>  @section adelay
>  
>  Delay one or more audio channels.
> diff --git a/libavfilter/Makefile b/libavfilter/Makefile
> index cd62fd5..0d94f84 100644
> --- a/libavfilter/Makefile
> +++ b/libavfilter/Makefile
> @@ -30,6 +30,7 @@ OBJS-$(HAVE_THREADS)                         += pthread.o
>  OBJS-$(CONFIG_ABENCH_FILTER)                 += f_bench.o
>  OBJS-$(CONFIG_ACOMPRESSOR_FILTER)            += af_sidechaincompress.o
>  OBJS-$(CONFIG_ACROSSFADE_FILTER)             += af_afade.o
> +OBJS-$(CONFIG_ACRUSHER_FILTER)               += af_acrusher.o
>  OBJS-$(CONFIG_ADELAY_FILTER)                 += af_adelay.o
>  OBJS-$(CONFIG_AECHO_FILTER)                  += af_aecho.o
>  OBJS-$(CONFIG_AEMPHASIS_FILTER)              += af_aemphasis.o
> diff --git a/libavfilter/af_acrusher.c b/libavfilter/af_acrusher.c
> new file mode 100644
> index 0000000..339cbf2
> --- /dev/null
> +++ b/libavfilter/af_acrusher.c
> @@ -0,0 +1,291 @@
> +/*
> + * Copyright (c) Markus Schmidt and Christian Holschuh
> + *
> + * This file is part of FFmpeg.
> + *
> + * FFmpeg is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * FFmpeg is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with FFmpeg; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
> + */
> +
> +#include "libavutil/opt.h"
> +#include "avfilter.h"
> +#include "internal.h"
> +#include "audio.h"
> +
> +typedef struct SRContext {
> +    double target;
> +    double real;
> +    double samples;
> +    double last;
> +} SRContext;
> +
> +typedef struct ACrusherContext {
> +    const AVClass *class;
> +
> +    double level_in;
> +    double level_out;
> +    double bits;
> +    double mix;
> +    int mode;
> +    double dc;
> +    double aa;
> +    double samples;
> +
> +    double sqr;
> +    double aa1;
> +    double coeff;
> +    int    round;
> +
> +    SRContext *sr;
> +} ACrusherContext;
> +
> +#define OFFSET(x) offsetof(ACrusherContext, x)
> +#define A AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
> +
> +static const AVOption acrusher_options[] = {
> +    { "level_in", "set level in",         OFFSET(level_in),  AV_OPT_TYPE_DOUBLE, {.dbl=1},  0.015625, 64, A },
> +    { "level_out","set level out",        OFFSET(level_out), AV_OPT_TYPE_DOUBLE, {.dbl=1},  0.015625, 64, A },
> +    { "bits",     "set bit reduction",    OFFSET(bits),      AV_OPT_TYPE_DOUBLE, {.dbl=8},  1,        64, A },
> +    { "mix",      "set mix",              OFFSET(mix),       AV_OPT_TYPE_DOUBLE, {.dbl=.5}, 0,         1, A },
> +    { "mode",     "set mode",             OFFSET(mode),      AV_OPT_TYPE_INT,    {.i64=0},  0,         1, A, "mode" },
> +    {   "lin",    "linear",               0,                 AV_OPT_TYPE_CONST,  {.i64=0},  0,         0, A, "mode" },
> +    {   "log",    "logarithmic",          0,                 AV_OPT_TYPE_CONST,  {.i64=1},  0,         0, A, "mode" },
> +    { "dc",       "set DC",               OFFSET(dc),        AV_OPT_TYPE_DOUBLE, {.dbl=1},.25,         4, A },
> +    { "aa",       "set anti-aliasing",    OFFSET(aa),        AV_OPT_TYPE_DOUBLE, {.dbl=.5}, 0,         1, A },
> +    { "samples",  "set sample reduction", OFFSET(samples),   AV_OPT_TYPE_DOUBLE, {.dbl=1},  1,       250, A },
> +    { NULL }
> +};
> +
> +AVFILTER_DEFINE_CLASS(acrusher);
> +
> +static double samplereduction(ACrusherContext *s, SRContext *sr, double in)
> +{
> +    sr->samples++;
> +    if (sr->samples >= s->round) {
> +        sr->target += s->samples;
> +        sr->real += s->round;
> +        if (sr->target + s->samples >= sr->real + 1) {
> +            sr->last = in;
> +            sr->target = 0;
> +            sr->real   = 0;
> +        }
> +        sr->samples = 0;
> +    }
> +    return sr->last;
> +}
> +

> +static double add_dc(double s, double dc)
> +{
> +    return s > 0 ? s * dc : s / dc;
> +}
> +
> +static double remove_dc(double s, double dc)
> +{
> +    return s > 0 ? s / dc : s * dc;
> +}

the division can be avoided as dc is a constant, multiplication by
the inverse can be used ...


> +
> +static double bitreduction(ACrusherContext *s, double in)
> +{
> +    const double sqr = s->sqr;
> +    const double coeff = s->coeff;
> +    const double aa = s->aa;
> +    const double aa1 = s->aa1;
> +    double y, k;
> +
> +    // add dc
> +    in = add_dc(in, s->dc);
> +
> +    // main rounding calculation depending on mode
> +
> +    // the idea for anti-aliasing:
> +    // you need a function f which brings you to the scale, where you want to round
> +    // and the function f_b (with f(f_b)=id) which brings you back to your original scale.
> +    //
> +    // then you can use the logic below in the following way:
> +    // y = f(in) and k = roundf(y)
> +    // if (y > k + aa1)
> +    //      k = f_b(k) + ( f_b(k+1) - f_b(k) ) *0.5 * (sin(x - PI/2) + 1)
> +    // if (y < k + aa1)
> +    //      k = f_b(k) - ( f_b(k+1) - f_b(k) ) *0.5 * (sin(x - PI/2) + 1)
> +    //
> +    // whereas x = (fabs(f(in) - k) - aa1) * PI / aa
> +    // for both cases.
> +
> +    switch (s->mode) {
> +    case 0:
> +    default:
> +        // linear
> +        y = in * coeff;
> +        k = roundf(y);
> +        if (k - aa1 <= y && y <= k + aa1) {
> +            k /= coeff;
> +        } else if (y > k + aa1) {
> +            k = k / coeff + ((k + 1) / coeff - k / coeff) * 0.5 * (sin(M_PI * (fabs(y - k) - aa1) / aa - M_PI_2) + 1);
> +        } else {
> +            k = k / coeff - (k / coeff - (k - 1) / coeff) * 0.5 * (sin(M_PI * (fabs(y - k) - aa1) / aa - M_PI_2) + 1);
> +        }
> +        break;
> +    case 1:
> +        // logarithmic
> +        y = sqr * log(fabs(in)) + sqr * sqr;
> +        k = roundf(y);
> +        if(!in) {
> +            k = 0;
> +        } else if (k - aa1 <= y && y <= k + aa1) {
> +            k = in / fabs(in) * exp(k / sqr - sqr);
> +        } else if (y > k + aa1) {
> +            k = in / fabs(in) * (exp(k / sqr - sqr) + (exp((k + 1) / sqr - sqr) -
> +                exp(k / sqr - sqr)) * 0.5 * (sin(M_PI * (fabs(y - k) - aa1) / aa - M_PI_2) + 1));
> +        } else {
> +            k = in / fabs(in) * (exp(k / sqr - sqr) - (exp(k / sqr - sqr) -
> +                exp((k - 1) / sqr - sqr)) * 0.5 * (sin(M_PI * (fabs(y - k) - aa1) / aa - M_PI_2) + 1));

0.5 * (sin(M_PI * (fabs(y - k) - aa1) / aa - M_PI_2) + 1)
occurs 4 times, maybe it could be factored into its own inline function

in / fabs(in) should be some sign() macro or function

exp(k / sqr - sqr) is used multiple times

k = Y * (exp(k / S - S) - (exp(k / S - S) - exp((k - 1) / S - S)) * X);
can be simplified to
k = Y * exp(k / S - S)(1 - (1 - exp( - 1 / S)) * X);

an S being constant IIUC so some of this can be calculated outside
the loop


[...]
> +static int config_input(AVFilterLink *inlink)
> +{
> +    AVFilterContext *ctx = inlink->dst;
> +    ACrusherContext *s = ctx->priv;
> +

> +    s->coeff = pow(2., s->bits) - 1;

exp2()


[...]
-- 
Michael     GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB

Complexity theory is the science of finding the exact solution to an
approximation. Benchmarking OTOH is finding an approximation of the exact
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 181 bytes
Desc: Digital signature
URL: <http://ffmpeg.org/pipermail/ffmpeg-devel/attachments/20160811/dd2786c0/attachment.sig>


More information about the ffmpeg-devel mailing list