[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