From bd5afecdcbb678aa24bc13a882cdae6e974e9645 Mon Sep 17 00:00:00 2001 From: Paul B Mahol Date: Sun, 29 Nov 2015 23:22:47 +0100 Subject: [PATCH] avfilter: add sidechaingate filter Signed-off-by: Paul B Mahol --- Changelog | 1 + doc/filters.texi | 61 ++++++++++++++ libavfilter/Makefile | 1 + libavfilter/af_agate.c | 170 ++++++++++++++++++++++++++++++++++++++- libavfilter/allfilters.c | 1 + libavfilter/version.h | 2 +- 6 files changed, 232 insertions(+), 4 deletions(-) diff --git a/Changelog b/Changelog index 69d5688d14..2d2a92b3d4 100644 --- a/Changelog +++ b/Changelog @@ -38,6 +38,7 @@ version : - acompressor filter - support encoding 16-bit RLE SGI images - apulsator filter +- sidechaingate audio filter version 2.8: diff --git a/doc/filters.texi b/doc/filters.texi index 9f722376b8..8b763e9d73 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -2597,6 +2597,67 @@ ffmpeg -i main.flac -i sidechain.flac -filter_complex "[1:a]asplit=2[sc][mix];[0 @end example @end itemize +@section sidechaingate + +A sidechain gate acts like a normal (wideband) gate but has the ability to +filter the detected signal before sending it to the gain reduction stage. +Normally a gate uses the full range signal to detect a level above the +threshold. +For example: If you cut all lower frequencies from your sidechain signal +the gate will decrease the volume of your track only if not enough highs +appear. With this technique you are able to reduce the resonation of a +natural drum or remove "rumbling" of muted strokes from a heavily distorted +guitar. +It needs two input streams and returns one output stream. +First input stream will be processed depending on second stream signal. + +The filter accepts the following options: + +@table @option +@item level_in +Set input level before filtering. +Default is 1. Allowed range is from 0.015625 to 64. + +@item range +Set the level of gain reduction when the signal is below the threshold. +Default is 0.06125. Allowed range is from 0 to 1. + +@item threshold +If a signal rises above this level the gain reduction is released. +Default is 0.125. Allowed range is from 0 to 1. + +@item ratio +Set a ratio about which the signal is reduced. +Default is 2. Allowed range is from 1 to 9000. + +@item attack +Amount of milliseconds the signal has to rise above the threshold before gain +reduction stops. +Default is 20 milliseconds. Allowed range is from 0.01 to 9000. + +@item release +Amount of milliseconds the signal has to fall below the threshold before the +reduction is increased again. Default is 250 milliseconds. +Allowed range is from 0.01 to 9000. + +@item makeup +Set amount of amplification of signal after processing. +Default is 1. Allowed range is from 1 to 64. + +@item knee +Curve the sharp knee around the threshold to enter gain reduction more softly. +Default is 2.828427125. Allowed range is from 1 to 8. + +@item detection +Choose if exact signal should be taken for detection or an RMS like one. +Default is peak. Can be peak or rms. + +@item link +Choose if the average level between all channels or the louder channel affects +the reduction. +Default is average. Can be average or maximum. +@end table + @section silencedetect Detect silence in an audio stream. diff --git a/libavfilter/Makefile b/libavfilter/Makefile index b6c0d7b21d..740a640789 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -83,6 +83,7 @@ OBJS-$(CONFIG_REPLAYGAIN_FILTER) += af_replaygain.o OBJS-$(CONFIG_RESAMPLE_FILTER) += af_resample.o OBJS-$(CONFIG_RUBBERBAND_FILTER) += af_rubberband.o OBJS-$(CONFIG_SIDECHAINCOMPRESS_FILTER) += af_sidechaincompress.o +OBJS-$(CONFIG_SIDECHAINGATE_FILTER) += af_agate.o OBJS-$(CONFIG_SILENCEDETECT_FILTER) += af_silencedetect.o OBJS-$(CONFIG_SILENCEREMOVE_FILTER) += af_silenceremove.o OBJS-$(CONFIG_STEREOTOOLS_FILTER) += af_stereotools.o diff --git a/libavfilter/af_agate.c b/libavfilter/af_agate.c index d3f74fb97f..23e45c6f77 100644 --- a/libavfilter/af_agate.c +++ b/libavfilter/af_agate.c @@ -18,6 +18,12 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +/** + * @file + * Audio (Sidechain) Gate filter + */ + +#include "libavutil/avassert.h" #include "libavutil/channel_layout.h" #include "libavutil/opt.h" #include "avfilter.h" @@ -46,12 +52,14 @@ typedef struct AudioGateContext { double lin_slope; double attack_coeff; double release_coeff; + + AVFrame *input_frame[2]; } AudioGateContext; #define OFFSET(x) offsetof(AudioGateContext, x) #define A AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM -static const AVOption agate_options[] = { +static const AVOption options[] = { { "level_in", "set input level", OFFSET(level_in), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0.015625, 64, A }, { "range", "set max gain reduction", OFFSET(range), AV_OPT_TYPE_DOUBLE, {.dbl=0.06125}, 0, 1, A }, { "threshold", "set threshold", OFFSET(threshold), AV_OPT_TYPE_DOUBLE, {.dbl=0.125}, 0, 1, A }, @@ -69,6 +77,7 @@ static const AVOption agate_options[] = { { NULL } }; +#define agate_options options AVFILTER_DEFINE_CLASS(agate); static int query_formats(AVFilterContext *ctx) @@ -97,7 +106,7 @@ static int query_formats(AVFilterContext *ctx) return ff_set_common_samplerates(ctx, formats); } -static int config_input(AVFilterLink *inlink) +static int agate_config_input(AVFilterLink *inlink) { AVFilterContext *ctx = inlink->dst; AudioGateContext *s = ctx->priv; @@ -221,7 +230,7 @@ static const AVFilterPad inputs[] = { .name = "default", .type = AVMEDIA_TYPE_AUDIO, .filter_frame = filter_frame, - .config_props = config_input, + .config_props = agate_config_input, }, { NULL } }; @@ -243,3 +252,158 @@ AVFilter ff_af_agate = { .inputs = inputs, .outputs = outputs, }; + +#if CONFIG_SIDECHAINGATE_FILTER + +#define sidechaingate_options options +AVFILTER_DEFINE_CLASS(sidechaingate); + +static int scfilter_frame(AVFilterLink *link, AVFrame *in) +{ + AVFilterContext *ctx = link->dst; + AudioGateContext *s = ctx->priv; + AVFilterLink *outlink = ctx->outputs[0]; + const double *scsrc; + double *sample; + int nb_samples; + int ret, i; + + for (i = 0; i < 2; i++) + if (link == ctx->inputs[i]) + break; + av_assert0(i < 2 && !s->input_frame[i]); + s->input_frame[i] = in; + + if (!s->input_frame[0] || !s->input_frame[1]) + return 0; + + nb_samples = FFMIN(s->input_frame[0]->nb_samples, + s->input_frame[1]->nb_samples); + + sample = (double *)s->input_frame[0]->data[0]; + scsrc = (const double *)s->input_frame[1]->data[0]; + + gate(s, sample, sample, scsrc, nb_samples, + ctx->inputs[0], ctx->inputs[1]); + ret = ff_filter_frame(outlink, s->input_frame[0]); + + s->input_frame[0] = NULL; + av_frame_free(&s->input_frame[1]); + + return ret; +} + +static int screquest_frame(AVFilterLink *outlink) +{ + AVFilterContext *ctx = outlink->src; + AudioGateContext *s = ctx->priv; + int i, ret; + + /* get a frame on each input */ + for (i = 0; i < 2; i++) { + AVFilterLink *inlink = ctx->inputs[i]; + if (!s->input_frame[i] && + (ret = ff_request_frame(inlink)) < 0) + return ret; + + /* request the same number of samples on all inputs */ + if (i == 0) + ctx->inputs[1]->request_samples = s->input_frame[0]->nb_samples; + } + + return 0; +} + +static int scquery_formats(AVFilterContext *ctx) +{ + AVFilterFormats *formats; + AVFilterChannelLayouts *layouts = NULL; + static const enum AVSampleFormat sample_fmts[] = { + AV_SAMPLE_FMT_DBL, + AV_SAMPLE_FMT_NONE + }; + int ret, i; + + if (!ctx->inputs[0]->in_channel_layouts || + !ctx->inputs[0]->in_channel_layouts->nb_channel_layouts) { + av_log(ctx, AV_LOG_WARNING, + "No channel layout for input 1\n"); + return AVERROR(EAGAIN); + } + + if ((ret = ff_add_channel_layout(&layouts, ctx->inputs[0]->in_channel_layouts->channel_layouts[0])) < 0 || + (ret = ff_channel_layouts_ref(layouts, &ctx->outputs[0]->in_channel_layouts)) < 0) + return ret; + + for (i = 0; i < 2; i++) { + layouts = ff_all_channel_counts(); + if ((ret = ff_channel_layouts_ref(layouts, &ctx->inputs[i]->out_channel_layouts)) < 0) + return ret; + } + + formats = ff_make_format_list(sample_fmts); + if ((ret = ff_set_common_formats(ctx, formats)) < 0) + return ret; + + formats = ff_all_samplerates(); + return ff_set_common_samplerates(ctx, formats); +} + +static int scconfig_output(AVFilterLink *outlink) +{ + AVFilterContext *ctx = outlink->src; + + if (ctx->inputs[0]->sample_rate != ctx->inputs[1]->sample_rate) { + av_log(ctx, AV_LOG_ERROR, + "Inputs must have the same sample rate " + "%d for in0 vs %d for in1\n", + ctx->inputs[0]->sample_rate, ctx->inputs[1]->sample_rate); + return AVERROR(EINVAL); + } + + outlink->sample_rate = ctx->inputs[0]->sample_rate; + outlink->time_base = ctx->inputs[0]->time_base; + outlink->channel_layout = ctx->inputs[0]->channel_layout; + outlink->channels = ctx->inputs[0]->channels; + + agate_config_input(ctx->inputs[0]); + + return 0; +} + +static const AVFilterPad sidechaingate_inputs[] = { + { + .name = "main", + .type = AVMEDIA_TYPE_AUDIO, + .filter_frame = scfilter_frame, + .needs_writable = 1, + .needs_fifo = 1, + },{ + .name = "sidechain", + .type = AVMEDIA_TYPE_AUDIO, + .filter_frame = scfilter_frame, + .needs_fifo = 1, + }, + { NULL } +}; + +static const AVFilterPad sidechaingate_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_AUDIO, + .config_props = scconfig_output, + .request_frame = screquest_frame, + }, + { NULL } +}; + +AVFilter ff_af_sidechaingate = { + .name = "sidechaingate", + .description = NULL_IF_CONFIG_SMALL("Audio sidechain gate."), + .priv_size = sizeof(AudioGateContext), + .priv_class = &sidechaingate_class, + .query_formats = scquery_formats, + .inputs = sidechaingate_inputs, + .outputs = sidechaingate_outputs, +}; +#endif /* CONFIG_SIDECHAINGATE_FILTER */ diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index 9502ebf48a..6557612066 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -105,6 +105,7 @@ void avfilter_register_all(void) REGISTER_FILTER(RESAMPLE, resample, af); REGISTER_FILTER(RUBBERBAND, rubberband, af); REGISTER_FILTER(SIDECHAINCOMPRESS, sidechaincompress, af); + REGISTER_FILTER(SIDECHAINGATE, sidechaingate, af); REGISTER_FILTER(SILENCEDETECT, silencedetect, af); REGISTER_FILTER(SILENCEREMOVE, silenceremove, af); REGISTER_FILTER(STEREOTOOLS, stereotools, af); diff --git a/libavfilter/version.h b/libavfilter/version.h index 449d9174dc..893ec52531 100644 --- a/libavfilter/version.h +++ b/libavfilter/version.h @@ -30,7 +30,7 @@ #include "libavutil/version.h" #define LIBAVFILTER_VERSION_MAJOR 6 -#define LIBAVFILTER_VERSION_MINOR 18 +#define LIBAVFILTER_VERSION_MINOR 19 #define LIBAVFILTER_VERSION_MICRO 100 #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \