[FFmpeg-devel] [PATCH 2/2] avfilter: add declip audio filter
Paul B Mahol
onemda at gmail.com
Sun Mar 25 19:41:25 EEST 2018
Signed-off-by: Paul B Mahol <onemda at 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
+ at section declip
+Remove clipped samples from input audio.
+
+Samples detected as clipped are replaced by interpolated samples using
+autoregressive modeling.
+
+ at table @option
+ at 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.
+
+ at 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.
+
+ at item a
+Set autoregression order, in percentage of window size. Allowed range is from
+ at code{1} to @code{50}. Default value is @code{2} percent. This option also controls
+quality of interpolated samples using neighbour good samples.
+
+ at 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.
+ at 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
More information about the ffmpeg-devel
mailing list