Signed-off-by: Paul B Mahol <one...@gmail.com> --- Depends on declick filter. --- doc/filters.texi | 28 +++++++++++++++++++ libavfilter/Makefile | 1 + libavfilter/af_declick.c | 73 +++++++++++++++++++++++++++++++++++++++++++++--- libavfilter/allfilters.c | 1 + 4 files changed, 99 insertions(+), 4 deletions(-)
diff --git a/doc/filters.texi b/doc/filters.texi index 9a067ba9ea..86cf570ab9 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -2597,6 +2597,34 @@ This controls between how much samples, which are detected as impulsive noise, any sample between 2 detected noise samples is considered also as noise sample. @end table +@section declip +Remove clipped samples from input audio. + +Samples detected as clipped are replaced by interpolated samples using +autoregressive modeling. + +@table @option +@item w +Set window size, in milliseconds. Allowed range is from @code{10} to @code{100}. +Default value is @code{50} milliseconds. +This sets size of window which will be processed at once. + +@item o +Set window overlap, in percentage of window size. Allowed range is from @code{50} +to @code{95}. Default value is @code{75} percent. + +@item a +Set autoregression order, in percentage of window size. Allowed range is from +@code{1} to @code{50}. Default value is @code{2} percent. This option also controls +quality of interpolated samples using neighbour good samples. + +@item t +Set threshold value. Allowed range is from @code{0.2} to @code{1.0}. +Default value is @code{0.98}. +Any sample which absolute value is equal or higher of this value will be +detected as clipped and be replaced with interpolated value. +@end table + @section drmeter Measure audio dynamic range. diff --git a/libavfilter/Makefile b/libavfilter/Makefile index 978751d2a0..505287b5b4 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -88,6 +88,7 @@ OBJS-$(CONFIG_CROSSFEED_FILTER) += af_crossfeed.o OBJS-$(CONFIG_CRYSTALIZER_FILTER) += af_crystalizer.o OBJS-$(CONFIG_DCSHIFT_FILTER) += af_dcshift.o OBJS-$(CONFIG_DECLICK_FILTER) += af_declick.o +OBJS-$(CONFIG_DECLIP_FILTER) += af_declick.o OBJS-$(CONFIG_DRMETER_FILTER) += af_drmeter.o OBJS-$(CONFIG_DYNAUDNORM_FILTER) += af_dynaudnorm.o OBJS-$(CONFIG_EARWAX_FILTER) += af_earwax.o diff --git a/libavfilter/af_declick.c b/libavfilter/af_declick.c index 0de4c35c95..658dae420b 100644 --- a/libavfilter/af_declick.c +++ b/libavfilter/af_declick.c @@ -29,9 +29,11 @@ typedef struct DeclickContext { double w; double overlap; double threshold; + double clip_threshold; double ar; double burst; + int is_declip; int ar_order; int nb_burst_samples; int window_size; @@ -68,6 +70,10 @@ typedef struct DeclickContext { AVAudioFifo *fifo; double *window_func_lut; + + int (*detector)(struct DeclickContext *s, double sigmae, double *detection, + double *acoefficients, uint8_t *click, int *index, + const double *src, double *dst); } DeclickContext; #define OFFSET(x) offsetof(DeclickContext, x) @@ -354,6 +360,28 @@ static int interpolation(DeclickContext *s, const double *src, int ar_order, return cholesky_decomposition(s, matrix, vector, nb_clicks, interpolated); } +static int detect_clips(DeclickContext *s, double unused0, double *unused1, double *unused2, + uint8_t *clips, int *index, + const double *src, double *dst) +{ + const double threshold = s->clip_threshold; + int i, nb_clips = 0; + + for (i = 0; i < s->window_size; i++) { + clips[i] = fabs(src[i]) >= threshold; + dst[i] = src[i]; + } + + memset(clips, 0, s->ar_order * sizeof(*clips)); + memset(clips + (s->window_size - s->ar_order), 0, s->ar_order * sizeof(*clips)); + + for (i = s->ar_order; i < s->window_size - s->ar_order; i++) + if (clips[i]) + index[nb_clips++] = i; + + return nb_clips; +} + static int detect_clicks(DeclickContext *s, double sigmae, double *detection, double *acoefficients, uint8_t *click, int *index, const double *src, double *dst) @@ -441,8 +469,8 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) int *index = s->index; int nb_clicks; - nb_clicks = detect_clicks(s, sigmae, s->detection, s->acoefficients, - s->click, index, src, dst); + nb_clicks = s->detector(s, sigmae, s->detection, s->acoefficients, + s->click, index, src, dst); if (nb_clicks > 0) { ret = interpolation(s, src, s->ar_order, s->acoefficients, index, nb_clicks, s->auxiliary, interpolated); @@ -521,13 +549,27 @@ static int request_frame(AVFilterLink *outlink) return ret; } +static av_cold int init(AVFilterContext *ctx) +{ + DeclickContext *s = ctx->priv; + + s->is_declip = !strcmp(ctx->filter->name, "declip"); + if (s->is_declip) { + s->detector = detect_clips; + } else { + s->detector = detect_clicks; + } + + return 0; +} static av_cold void uninit(AVFilterContext *ctx) { DeclickContext *s = ctx->priv; - av_log(ctx, AV_LOG_INFO, "Detected clicks in %"PRId64" of %"PRId64" samples (%g%%).\n", - s->detected_clicks, s->nb_samples, 100. * s->detected_clicks / s->nb_samples); + av_log(ctx, AV_LOG_INFO, "Detected %s in %"PRId64" of %"PRId64" samples (%g%%).\n", + s->is_declip ? "clips" : "clicks", s->detected_clicks, + s->nb_samples, 100. * s->detected_clicks / s->nb_samples); av_audio_fifo_free(s->fifo); av_freep(&s->window_func_lut); @@ -580,6 +622,29 @@ AVFilter ff_af_declick = { .query_formats = query_formats, .priv_size = sizeof(DeclickContext), .priv_class = &declick_class, + .init = init, + .uninit = uninit, + .inputs = inputs, + .outputs = outputs, +}; + +static const AVOption declip_options[] = { + { "w", "set window size", OFFSET(w), AV_OPT_TYPE_DOUBLE, {.dbl=50}, 10, 100, AF }, + { "o", "set window overlap", OFFSET(overlap), AV_OPT_TYPE_DOUBLE, {.dbl=75}, 50, 95, AF }, + { "a", "set autoregression order", OFFSET(ar), AV_OPT_TYPE_DOUBLE, {.dbl=8}, 1, 50, AF }, + { "t", "set threshold", OFFSET(clip_threshold), AV_OPT_TYPE_DOUBLE, {.dbl=0.98}, 0.2, 1., AF }, + { NULL } +}; + +AVFILTER_DEFINE_CLASS(declip); + +AVFilter ff_af_declip = { + .name = "declip", + .description = NULL_IF_CONFIG_SMALL("Remove clipping from input audio."), + .query_formats = query_formats, + .priv_size = sizeof(DeclickContext), + .priv_class = &declip_class, + .init = init, .uninit = uninit, .inputs = inputs, .outputs = outputs, diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index cf5016f2c1..4d18e243ab 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -99,6 +99,7 @@ static void register_all(void) REGISTER_FILTER(CRYSTALIZER, crystalizer, af); REGISTER_FILTER(DCSHIFT, dcshift, af); REGISTER_FILTER(DECLICK, declick, af); + REGISTER_FILTER(DECLIP, declip, af); REGISTER_FILTER(DRMETER, drmeter, af); REGISTER_FILTER(DYNAUDNORM, dynaudnorm, af); REGISTER_FILTER(EARWAX, earwax, af); -- 2.11.0 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel