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 <one...@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 > > +@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: > + > +@table @option > +@item level_in > +Set level in. > + > +@item level_out > +Set level out. > + > +@item bits > +Set bit reduction. > + > +@item mix > +Set mixing ammount. > + > +@item mode > +Can be linear: @code{lin} or logarithmic: @code{log}. > + > +@item dc > +Set DC. > + > +@item aa > +Set anti-aliasing. > + > +@item samples > +Set sample reduction. > +@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
signature.asc
Description: Digital signature
_______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel