2 * This file is part of FFmpeg.
4 * FFmpeg is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * FFmpeg is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with FFmpeg; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 #include "libavutil/channel_layout.h"
20 #include "libavutil/ffmath.h"
21 #include "libavutil/opt.h"
28 typedef struct Coeffs
{
34 typedef struct ATiltContext
{
43 Coeffs coeffs
[MAX_ORDER
];
47 int (*filter_channels
)(AVFilterContext
*ctx
, void *arg
, int jobnr
, int nb_jobs
);
50 static double prewarp(double w
, double T
, double wp
)
52 return wp
* tan(w
* T
* 0.5) / tan(wp
* T
* 0.5);
55 static double mz(int i
, double w0
, double r
, double alpha
)
57 return w0
* pow(r
, -alpha
+ i
);
60 static double mp(int i
, double w0
, double r
)
62 return w0
* pow(r
, i
);
65 static double mzh(int i
, double T
, double w0
, double r
, double alpha
)
67 return prewarp(mz(i
, w0
, r
, alpha
), T
, w0
);
70 static double mph(int i
, double T
, double w0
, double r
)
72 return prewarp(mp(i
, w0
, r
), T
, w0
);
75 static void set_tf1s(Coeffs
*coeffs
, double b1
, double b0
, double a0
,
76 double w1
, double sr
, double alpha
)
78 double c
= 1.0 / tan(w1
* 0.5 / sr
);
81 coeffs
->b1
= (b0
- b1
* c
) / d
;
82 coeffs
->b0
= (b0
+ b1
* c
) / d
;
83 coeffs
->a1
= (a0
- c
) / d
;
87 static void set_filter(AVFilterContext
*ctx
,
88 int order
, double sr
, double f0
,
89 double bw
, double alpha
)
91 ATiltContext
*s
= ctx
->priv
;
92 const double w0
= 2. * M_PI
* f0
;
93 const double f1
= f0
+ bw
;
95 const double r
= pow(f1
/ f0
, 1.0 / (order
- 1.0));
96 const double T
= 1. / sr
;
98 for (int i
= 0; i
< order
; i
++) {
99 Coeffs
*coeffs
= &s
->coeffs
[i
];
101 set_tf1s(coeffs
, 1.0, mzh(i
, T
, w0
, r
, alpha
), mph(i
, T
, w0
, r
),
106 static int get_coeffs(AVFilterContext
*ctx
)
108 ATiltContext
*s
= ctx
->priv
;
109 AVFilterLink
*inlink
= ctx
->inputs
[0];
111 set_filter(ctx
, s
->order
, inlink
->sample_rate
, s
->freq
, s
->width
, s
->slope
);
116 typedef struct ThreadData
{
120 #define FILTER(name, type) \
121 static int filter_channels_## name(AVFilterContext *ctx, void *arg, \
122 int jobnr, int nb_jobs) \
124 ATiltContext *s = ctx->priv; \
125 ThreadData *td = arg; \
126 AVFrame *out = td->out; \
127 AVFrame *in = td->in; \
128 const int start = (in->ch_layout.nb_channels * jobnr) / nb_jobs; \
129 const int end = (in->ch_layout.nb_channels * (jobnr+1)) / nb_jobs; \
130 const type level = s->level; \
132 for (int ch = start; ch < end; ch++) { \
133 const type *src = (const type *)in->extended_data[ch]; \
134 type *dst = (type *)out->extended_data[ch]; \
136 for (int b = 0; b < s->order; b++) { \
137 Coeffs *coeffs = &s->coeffs[b]; \
138 const type g = coeffs->g; \
139 const type a1 = coeffs->a1; \
140 const type b0 = coeffs->b0; \
141 const type b1 = coeffs->b1; \
142 type *w = ((type *)s->w->extended_data[ch]) + b * 2; \
144 for (int n = 0; n < in->nb_samples; n++) { \
145 type sain = b ? dst[n] : src[n] * level; \
146 type saout = sain * b0 + w[0] * b1 - w[1] * a1; \
151 dst[n] = saout * g; \
162 static int config_input(AVFilterLink
*inlink
)
164 AVFilterContext
*ctx
= inlink
->dst
;
165 ATiltContext
*s
= ctx
->priv
;
167 switch (inlink
->format
) {
168 case AV_SAMPLE_FMT_FLTP
: s
->filter_channels
= filter_channels_fltp
; break;
169 case AV_SAMPLE_FMT_DBLP
: s
->filter_channels
= filter_channels_dblp
; break;
172 s
->w
= ff_get_audio_buffer(inlink
, 2 * MAX_ORDER
);
174 return AVERROR(ENOMEM
);
176 return get_coeffs(ctx
);
179 static int filter_frame(AVFilterLink
*inlink
, AVFrame
*in
)
181 AVFilterContext
*ctx
= inlink
->dst
;
182 ATiltContext
*s
= ctx
->priv
;
183 AVFilterLink
*outlink
= ctx
->outputs
[0];
187 if (av_frame_is_writable(in
)) {
190 out
= ff_get_audio_buffer(outlink
, in
->nb_samples
);
193 return AVERROR(ENOMEM
);
195 av_frame_copy_props(out
, in
);
198 td
.in
= in
; td
.out
= out
;
199 ff_filter_execute(ctx
, s
->filter_channels
, &td
, NULL
, FFMIN(inlink
->ch_layout
.nb_channels
,
200 ff_filter_get_nb_threads(ctx
)));
204 return ff_filter_frame(outlink
, out
);
207 static int process_command(AVFilterContext
*ctx
, const char *cmd
, const char *args
,
208 char *res
, int res_len
, int flags
)
212 ret
= ff_filter_process_command(ctx
, cmd
, args
, res
, res_len
, flags
);
216 return get_coeffs(ctx
);
219 static av_cold
void uninit(AVFilterContext
*ctx
)
221 ATiltContext
*s
= ctx
->priv
;
223 av_frame_free(&s
->w
);
226 #define OFFSET(x) offsetof(ATiltContext, x)
227 #define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM
229 static const AVOption atilt_options
[] = {
230 { "freq", "set central frequency",OFFSET(freq
), AV_OPT_TYPE_DOUBLE
, {.dbl
=10000}, 20, 192000, FLAGS
},
231 { "slope", "set filter slope", OFFSET(slope
), AV_OPT_TYPE_DOUBLE
, {.dbl
=0}, -1, 1, FLAGS
},
232 { "width", "set filter width", OFFSET(width
), AV_OPT_TYPE_DOUBLE
, {.dbl
=1000}, 100, 10000, FLAGS
},
233 { "order", "set filter order", OFFSET(order
), AV_OPT_TYPE_INT
, {.i64
=5}, 2,MAX_ORDER
, FLAGS
},
234 { "level", "set input level", OFFSET(level
), AV_OPT_TYPE_DOUBLE
, {.dbl
=1.}, 0., 4., FLAGS
},
238 AVFILTER_DEFINE_CLASS(atilt
);
240 static const AVFilterPad inputs
[] = {
243 .type
= AVMEDIA_TYPE_AUDIO
,
244 .filter_frame
= filter_frame
,
245 .config_props
= config_input
,
249 const FFFilter ff_af_atilt
= {
251 .p
.description
= NULL_IF_CONFIG_SMALL("Apply spectral tilt to audio."),
252 .p
.priv_class
= &atilt_class
,
253 .p
.flags
= AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC
|
254 AVFILTER_FLAG_SLICE_THREADS
,
255 .priv_size
= sizeof(ATiltContext
),
257 FILTER_INPUTS(inputs
),
258 FILTER_OUTPUTS(ff_audio_default_filterpad
),
259 FILTER_SAMPLEFMTS(AV_SAMPLE_FMT_FLTP
, AV_SAMPLE_FMT_DBLP
),
260 .process_command
= process_command
,