avfilter: add atilt filter

This commit is contained in:
Paul B Mahol 2020-12-06 12:45:34 +01:00
parent b53a7d2d4d
commit 1da2dd5c77
6 changed files with 320 additions and 1 deletions

View File

@ -13,6 +13,7 @@ version <next>:
- Apple Graphics (SMC) encoder - Apple Graphics (SMC) encoder
- hsvkey and hsvhold video filters - hsvkey and hsvhold video filters
- adecorrelate audio filter - adecorrelate audio filter
- atilt audio filter
version 4.4: version 4.4:

View File

@ -2989,6 +2989,35 @@ Change filter tempo scale factor.
Syntax for the command is : "@var{tempo}" Syntax for the command is : "@var{tempo}"
@end table @end table
@section atilt
Apply spectral tilt filter to audio stream.
This filter apply any spectral roll-off slope over any specified frequency band.
The filter accepts the following options:
@table @option
@item freq
Set central frequency of tilt in Hz. Default is 10000 Hz.
@item slope
Set slope direction of tilt. Default is 0. Allowed range is from -1 to 1.
@item width
Set width of tilt. Default is 1000. Allowed range is from 100 to 10000.
@item order
Set order of tilt filter.
@item level
Set input volume level. Allowed range is from 0 to 4.
Defalt is 1.
@end table
@subsection Commands
This filter supports the all above options as @ref{commands}.
@section atrim @section atrim
Trim the input so that the output contains one continuous subpart of the input. Trim the input so that the output contains one continuous subpart of the input.

View File

@ -99,6 +99,7 @@ OBJS-$(CONFIG_ASUPERCUT_FILTER) += af_asupercut.o
OBJS-$(CONFIG_ASUPERPASS_FILTER) += af_asupercut.o OBJS-$(CONFIG_ASUPERPASS_FILTER) += af_asupercut.o
OBJS-$(CONFIG_ASUPERSTOP_FILTER) += af_asupercut.o OBJS-$(CONFIG_ASUPERSTOP_FILTER) += af_asupercut.o
OBJS-$(CONFIG_ATEMPO_FILTER) += af_atempo.o OBJS-$(CONFIG_ATEMPO_FILTER) += af_atempo.o
OBJS-$(CONFIG_ATILT_FILTER) += af_atilt.o
OBJS-$(CONFIG_ATRIM_FILTER) += trim.o OBJS-$(CONFIG_ATRIM_FILTER) += trim.o
OBJS-$(CONFIG_AXCORRELATE_FILTER) += af_axcorrelate.o OBJS-$(CONFIG_AXCORRELATE_FILTER) += af_axcorrelate.o
OBJS-$(CONFIG_AZMQ_FILTER) += f_zmq.o OBJS-$(CONFIG_AZMQ_FILTER) += f_zmq.o

287
libavfilter/af_atilt.c Normal file
View File

@ -0,0 +1,287 @@
/*
* 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/channel_layout.h"
#include "libavutil/ffmath.h"
#include "libavutil/opt.h"
#include "avfilter.h"
#include "audio.h"
#include "formats.h"
#define MAX_ORDER 30
typedef struct Coeffs {
double g;
double a1;
double b0, b1;
} Coeffs;
typedef struct ATiltContext {
const AVClass *class;
double freq;
double level;
double slope;
double width;
int order;
Coeffs coeffs[MAX_ORDER];
AVFrame *w;
int (*filter_channels)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs);
} ATiltContext;
static int query_formats(AVFilterContext *ctx)
{
static const enum AVSampleFormat sample_fmts[] = {
AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_DBLP,
AV_SAMPLE_FMT_NONE
};
int ret;
ret = ff_set_common_formats_from_list(ctx, sample_fmts);
if (ret < 0)
return ret;
ret = ff_set_common_all_channel_counts(ctx);
if (ret < 0)
return ret;
return ff_set_common_all_samplerates(ctx);
}
static double prewarp(double w, double T, double wp)
{
return wp * tan(w * T * 0.5) / tan(wp * T * 0.5);
}
static double mz(int i, double w0, double r, double alpha)
{
return w0 * pow(r, -alpha + i);
}
static double mp(int i, double w0, double r)
{
return w0 * pow(r, i);
}
static double mzh(int i, double T, double w0, double r, double alpha)
{
return prewarp(mz(i, w0, r, alpha), T, w0);
}
static double mph(int i, double T, double w0, double r)
{
return prewarp(mp(i, w0, r), T, w0);
}
static void set_tf1s(Coeffs *coeffs, double b1, double b0, double a0,
double w1, double sr, double alpha)
{
double c = 1.0 / tan(w1 * 0.5 / sr);
double d = a0 + c;
coeffs->b1 = (b0 - b1 * c) / d;
coeffs->b0 = (b0 + b1 * c) / d;
coeffs->a1 = (a0 - c) / d;
coeffs->g = a0 / b0;
}
static void set_filter(AVFilterContext *ctx,
int order, double sr, double f0,
double bw, double alpha)
{
ATiltContext *s = ctx->priv;
const double w0 = 2. * M_PI * f0;
const double f1 = f0 + bw;
const double w1 = 1.;
const double r = pow(f1 / f0, 1.0 / (order - 1.0));
const double T = 1. / sr;
for (int i = 0; i < order; i++) {
Coeffs *coeffs = &s->coeffs[i];
set_tf1s(coeffs, 1.0, mzh(i, T, w0, r, alpha), mph(i, T, w0, r),
w1, sr, alpha);
}
}
static int get_coeffs(AVFilterContext *ctx)
{
ATiltContext *s = ctx->priv;
AVFilterLink *inlink = ctx->inputs[0];
set_filter(ctx, s->order, inlink->sample_rate, s->freq, s->width, s->slope);
return 0;
}
typedef struct ThreadData {
AVFrame *in, *out;
} ThreadData;
#define FILTER(name, type) \
static int filter_channels_## name(AVFilterContext *ctx, void *arg, \
int jobnr, int nb_jobs) \
{ \
ATiltContext *s = ctx->priv; \
ThreadData *td = arg; \
AVFrame *out = td->out; \
AVFrame *in = td->in; \
const int start = (in->channels * jobnr) / nb_jobs; \
const int end = (in->channels * (jobnr+1)) / nb_jobs; \
const type level = s->level; \
\
for (int ch = start; ch < end; ch++) { \
const type *src = (const type *)in->extended_data[ch]; \
type *dst = (type *)out->extended_data[ch]; \
\
for (int b = 0; b < s->order; b++) { \
Coeffs *coeffs = &s->coeffs[b]; \
const type g = coeffs->g; \
const type a1 = coeffs->a1; \
const type b0 = coeffs->b0; \
const type b1 = coeffs->b1; \
type *w = ((type *)s->w->extended_data[ch]) + b * 2; \
\
for (int n = 0; n < in->nb_samples; n++) { \
type sain = b ? dst[n] : src[n] * level; \
type saout = sain * b0 + w[0] * b1 - w[1] * a1; \
\
w[0] = sain; \
w[1] = saout; \
\
dst[n] = saout * g; \
} \
} \
} \
\
return 0; \
}
FILTER(fltp, float)
FILTER(dblp, double)
static int config_input(AVFilterLink *inlink)
{
AVFilterContext *ctx = inlink->dst;
ATiltContext *s = ctx->priv;
switch (inlink->format) {
case AV_SAMPLE_FMT_FLTP: s->filter_channels = filter_channels_fltp; break;
case AV_SAMPLE_FMT_DBLP: s->filter_channels = filter_channels_dblp; break;
}
s->w = ff_get_audio_buffer(inlink, 2 * MAX_ORDER);
if (!s->w)
return AVERROR(ENOMEM);
return get_coeffs(ctx);
}
static int filter_frame(AVFilterLink *inlink, AVFrame *in)
{
AVFilterContext *ctx = inlink->dst;
ATiltContext *s = ctx->priv;
AVFilterLink *outlink = ctx->outputs[0];
ThreadData td;
AVFrame *out;
if (av_frame_is_writable(in)) {
out = in;
} else {
out = ff_get_audio_buffer(outlink, in->nb_samples);
if (!out) {
av_frame_free(&in);
return AVERROR(ENOMEM);
}
av_frame_copy_props(out, in);
}
td.in = in; td.out = out;
ctx->internal->execute(ctx, s->filter_channels, &td, NULL, FFMIN(inlink->channels,
ff_filter_get_nb_threads(ctx)));
if (out != in)
av_frame_free(&in);
return ff_filter_frame(outlink, out);
}
static int process_command(AVFilterContext *ctx, const char *cmd, const char *args,
char *res, int res_len, int flags)
{
int ret;
ret = ff_filter_process_command(ctx, cmd, args, res, res_len, flags);
if (ret < 0)
return ret;
return get_coeffs(ctx);
}
static av_cold void uninit(AVFilterContext *ctx)
{
ATiltContext *s = ctx->priv;
av_frame_free(&s->w);
}
#define OFFSET(x) offsetof(ATiltContext, x)
#define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM
static const AVOption atilt_options[] = {
{ "freq", "set central frequency",OFFSET(freq), AV_OPT_TYPE_DOUBLE, {.dbl=10000}, 20, 192000, FLAGS },
{ "slope", "set filter slope", OFFSET(slope), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -1, 1, FLAGS },
{ "width", "set filter width", OFFSET(width), AV_OPT_TYPE_DOUBLE, {.dbl=1000}, 100, 10000, FLAGS },
{ "order", "set filter order", OFFSET(order), AV_OPT_TYPE_INT, {.i64=5}, 2,MAX_ORDER, FLAGS },
{ "level", "set input level", OFFSET(level), AV_OPT_TYPE_DOUBLE, {.dbl=1.}, 0., 4., FLAGS },
{ NULL }
};
AVFILTER_DEFINE_CLASS(atilt);
static const AVFilterPad inputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.filter_frame = filter_frame,
.config_props = config_input,
},
};
static const AVFilterPad outputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
},
};
AVFilter ff_af_atilt = {
.name = "atilt",
.description = NULL_IF_CONFIG_SMALL("Apply spectral tilt to audio."),
.query_formats = query_formats,
.priv_size = sizeof(ATiltContext),
.priv_class = &atilt_class,
.uninit = uninit,
FILTER_INPUTS(inputs),
FILTER_OUTPUTS(outputs),
.process_command = process_command,
.flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC |
AVFILTER_FLAG_SLICE_THREADS,
};

View File

@ -92,6 +92,7 @@ extern const AVFilter ff_af_asupercut;
extern const AVFilter ff_af_asuperpass; extern const AVFilter ff_af_asuperpass;
extern const AVFilter ff_af_asuperstop; extern const AVFilter ff_af_asuperstop;
extern const AVFilter ff_af_atempo; extern const AVFilter ff_af_atempo;
extern const AVFilter ff_af_atilt;
extern const AVFilter ff_af_atrim; extern const AVFilter ff_af_atrim;
extern const AVFilter ff_af_axcorrelate; extern const AVFilter ff_af_axcorrelate;
extern const AVFilter ff_af_azmq; extern const AVFilter ff_af_azmq;

View File

@ -30,7 +30,7 @@
#include "libavutil/version.h" #include "libavutil/version.h"
#define LIBAVFILTER_VERSION_MAJOR 8 #define LIBAVFILTER_VERSION_MAJOR 8
#define LIBAVFILTER_VERSION_MINOR 5 #define LIBAVFILTER_VERSION_MINOR 6
#define LIBAVFILTER_VERSION_MICRO 100 #define LIBAVFILTER_VERSION_MICRO 100