On 8/11/16, Michael Niedermayer <mich...@niedermayer.cc> wrote: > 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() >
Changed what I could and applied. _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel