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
21 #include "libavutil/ffmath.h"
22 #include "libavutil/mem.h"
23 #include "libavutil/opt.h"
47 typedef struct ChannelContext
{
48 double fa_double
[3], fm_double
[3];
49 double dstate_double
[2];
50 double fstate_double
[2];
51 double tstate_double
[2];
52 double lin_gain_double
;
54 double threshold_log_double
;
55 double new_threshold_log_double
;
56 double log_sum_double
;
58 float fa_float
[3], fm_float
[3];
59 float dstate_float
[2];
60 float fstate_float
[2];
61 float tstate_float
[2];
64 float threshold_log_float
;
65 float new_threshold_log_float
;
78 typedef struct AudioDynamicEqualizerContext
{
104 int (*filter_prepare
)(AVFilterContext
*ctx
);
105 int (*filter_channels
)(AVFilterContext
*ctx
, void *arg
, int jobnr
, int nb_jobs
);
107 double da_double
[3], dm_double
[3];
108 float da_float
[3], dm_float
[3];
111 } AudioDynamicEqualizerContext
;
113 static int query_formats(const AVFilterContext
*ctx
,
114 AVFilterFormatsConfig
**cfg_in
,
115 AVFilterFormatsConfig
**cfg_out
)
117 const AudioDynamicEqualizerContext
*s
= ctx
->priv
;
118 static const enum AVSampleFormat sample_fmts
[3][3] = {
119 { AV_SAMPLE_FMT_FLTP
, AV_SAMPLE_FMT_DBLP
, AV_SAMPLE_FMT_NONE
},
120 { AV_SAMPLE_FMT_FLTP
, AV_SAMPLE_FMT_NONE
},
121 { AV_SAMPLE_FMT_DBLP
, AV_SAMPLE_FMT_NONE
},
125 if ((ret
= ff_set_common_formats_from_list2(ctx
, cfg_in
, cfg_out
,
126 sample_fmts
[s
->precision
])) < 0)
132 static double get_coef(double x
, double sr
)
134 return 1.0 - exp(-1.0 / (0.001 * x
* sr
));
137 typedef struct ThreadData
{
142 #include "adynamicequalizer_template.c"
146 #include "adynamicequalizer_template.c"
148 static int config_input(AVFilterLink
*inlink
)
150 AVFilterContext
*ctx
= inlink
->dst
;
151 AudioDynamicEqualizerContext
*s
= ctx
->priv
;
153 s
->format
= inlink
->format
;
154 s
->cc
= av_calloc(inlink
->ch_layout
.nb_channels
, sizeof(*s
->cc
));
156 return AVERROR(ENOMEM
);
157 s
->nb_channels
= inlink
->ch_layout
.nb_channels
;
160 case AV_SAMPLE_FMT_DBLP
:
161 s
->filter_prepare
= filter_prepare_double
;
162 s
->filter_channels
= filter_channels_double
;
164 case AV_SAMPLE_FMT_FLTP
:
165 s
->filter_prepare
= filter_prepare_float
;
166 s
->filter_channels
= filter_channels_float
;
170 for (int ch
= 0; ch
< s
->nb_channels
; ch
++) {
171 ChannelContext
*cc
= &s
->cc
[ch
];
172 cc
->queue
= av_calloc(inlink
->sample_rate
, sizeof(double));
173 cc
->dqueue
= av_calloc(inlink
->sample_rate
, sizeof(double));
174 if (!cc
->queue
|| !cc
->dqueue
)
175 return AVERROR(ENOMEM
);
181 static int filter_frame(AVFilterLink
*inlink
, AVFrame
*in
)
183 AVFilterContext
*ctx
= inlink
->dst
;
184 AVFilterLink
*outlink
= ctx
->outputs
[0];
185 AudioDynamicEqualizerContext
*s
= ctx
->priv
;
189 if (av_frame_is_writable(in
)) {
192 out
= ff_get_audio_buffer(outlink
, in
->nb_samples
);
195 return AVERROR(ENOMEM
);
197 av_frame_copy_props(out
, in
);
202 s
->filter_prepare(ctx
);
203 ff_filter_execute(ctx
, s
->filter_channels
, &td
, NULL
,
204 FFMIN(outlink
->ch_layout
.nb_channels
, ff_filter_get_nb_threads(ctx
)));
208 return ff_filter_frame(outlink
, out
);
211 static av_cold
void uninit(AVFilterContext
*ctx
)
213 AudioDynamicEqualizerContext
*s
= ctx
->priv
;
215 for (int ch
= 0; ch
< s
->nb_channels
; ch
++) {
216 ChannelContext
*cc
= &s
->cc
[ch
];
217 av_freep(&cc
->queue
);
218 av_freep(&cc
->dqueue
);
223 #define OFFSET(x) offsetof(AudioDynamicEqualizerContext, x)
224 #define AF AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
225 #define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM
227 static const AVOption adynamicequalizer_options
[] = {
228 { "threshold", "set detection threshold", OFFSET(threshold
), AV_OPT_TYPE_DOUBLE
, {.dbl
=0}, 0, 100, FLAGS
},
229 { "dfrequency", "set detection frequency", OFFSET(dfrequency
), AV_OPT_TYPE_DOUBLE
, {.dbl
=1000}, 2, 1000000, FLAGS
},
230 { "dqfactor", "set detection Q factor", OFFSET(dqfactor
), AV_OPT_TYPE_DOUBLE
, {.dbl
=1}, 0.001, 1000, FLAGS
},
231 { "tfrequency", "set target frequency", OFFSET(tfrequency
), AV_OPT_TYPE_DOUBLE
, {.dbl
=1000}, 2, 1000000, FLAGS
},
232 { "tqfactor", "set target Q factor", OFFSET(tqfactor
), AV_OPT_TYPE_DOUBLE
, {.dbl
=1}, 0.001, 1000, FLAGS
},
233 { "attack", "set detection attack duration", OFFSET(dattack
), AV_OPT_TYPE_DOUBLE
, {.dbl
=20}, 0.01, 2000, FLAGS
},
234 { "release","set detection release duration",OFFSET(drelease
), AV_OPT_TYPE_DOUBLE
, {.dbl
=200}, 0.01, 2000, FLAGS
},
235 { "ratio", "set ratio factor", OFFSET(ratio
), AV_OPT_TYPE_DOUBLE
, {.dbl
=1}, 0, 30, FLAGS
},
236 { "makeup", "set makeup gain", OFFSET(makeup
), AV_OPT_TYPE_DOUBLE
, {.dbl
=0}, 0, 1000, FLAGS
},
237 { "range", "set max gain", OFFSET(range
), AV_OPT_TYPE_DOUBLE
, {.dbl
=50}, 1, 2000, FLAGS
},
238 { "mode", "set mode", OFFSET(mode
), AV_OPT_TYPE_INT
, {.i64
=0}, LISTEN
,NB_FMODES
-1,FLAGS
, .unit
= "mode" },
239 { "listen", 0, 0, AV_OPT_TYPE_CONST
, {.i64
=LISTEN
}, 0, 0, FLAGS
, .unit
= "mode" },
240 { "cutbelow", 0, 0, AV_OPT_TYPE_CONST
, {.i64
=CUT_BELOW
},0, 0, FLAGS
, .unit
= "mode" },
241 { "cutabove", 0, 0, AV_OPT_TYPE_CONST
, {.i64
=CUT_ABOVE
},0, 0, FLAGS
, .unit
= "mode" },
242 { "boostbelow", 0, 0, AV_OPT_TYPE_CONST
, {.i64
=BOOST_BELOW
},0, 0, FLAGS
, .unit
= "mode" },
243 { "boostabove", 0, 0, AV_OPT_TYPE_CONST
, {.i64
=BOOST_ABOVE
},0, 0, FLAGS
, .unit
= "mode" },
244 { "dftype", "set detection filter type",OFFSET(dftype
), AV_OPT_TYPE_INT
, {.i64
=0}, 0, 3, FLAGS
, .unit
= "dftype" },
245 { "bandpass", 0, 0, AV_OPT_TYPE_CONST
, {.i64
=0}, 0, 0, FLAGS
, .unit
= "dftype" },
246 { "lowpass", 0, 0, AV_OPT_TYPE_CONST
, {.i64
=1}, 0, 0, FLAGS
, .unit
= "dftype" },
247 { "highpass", 0, 0, AV_OPT_TYPE_CONST
, {.i64
=2}, 0, 0, FLAGS
, .unit
= "dftype" },
248 { "peak", 0, 0, AV_OPT_TYPE_CONST
, {.i64
=3}, 0, 0, FLAGS
, .unit
= "dftype" },
249 { "tftype", "set target filter type", OFFSET(tftype
), AV_OPT_TYPE_INT
, {.i64
=0}, 0, 2, FLAGS
, .unit
= "tftype" },
250 { "bell", 0, 0, AV_OPT_TYPE_CONST
, {.i64
=0}, 0, 0, FLAGS
, .unit
= "tftype" },
251 { "lowshelf", 0, 0, AV_OPT_TYPE_CONST
, {.i64
=1}, 0, 0, FLAGS
, .unit
= "tftype" },
252 { "highshelf",0, 0, AV_OPT_TYPE_CONST
, {.i64
=2}, 0, 0, FLAGS
, .unit
= "tftype" },
253 { "auto", "set auto threshold", OFFSET(detection
), AV_OPT_TYPE_INT
, {.i64
=DET_OFF
},DET_DISABLED
,NB_DMODES
-1,FLAGS
, .unit
= "auto" },
254 { "disabled", 0, 0, AV_OPT_TYPE_CONST
, {.i64
=DET_DISABLED
}, 0, 0, FLAGS
, .unit
= "auto" },
255 { "off", 0, 0, AV_OPT_TYPE_CONST
, {.i64
=DET_OFF
}, 0, 0, FLAGS
, .unit
= "auto" },
256 { "on", 0, 0, AV_OPT_TYPE_CONST
, {.i64
=DET_ON
}, 0, 0, FLAGS
, .unit
= "auto" },
257 { "adaptive", 0, 0, AV_OPT_TYPE_CONST
, {.i64
=DET_ADAPTIVE
}, 0, 0, FLAGS
, .unit
= "auto" },
258 { "precision", "set processing precision", OFFSET(precision
), AV_OPT_TYPE_INT
, {.i64
=0}, 0, 2, AF
, .unit
= "precision" },
259 { "auto", "set auto processing precision", 0, AV_OPT_TYPE_CONST
, {.i64
=0}, 0, 0, AF
, .unit
= "precision" },
260 { "float", "set single-floating point processing precision", 0, AV_OPT_TYPE_CONST
, {.i64
=1}, 0, 0, AF
, .unit
= "precision" },
261 { "double","set double-floating point processing precision", 0, AV_OPT_TYPE_CONST
, {.i64
=2}, 0, 0, AF
, .unit
= "precision" },
265 AVFILTER_DEFINE_CLASS(adynamicequalizer
);
267 static const AVFilterPad inputs
[] = {
270 .type
= AVMEDIA_TYPE_AUDIO
,
271 .filter_frame
= filter_frame
,
272 .config_props
= config_input
,
276 const FFFilter ff_af_adynamicequalizer
= {
277 .p
.name
= "adynamicequalizer",
278 .p
.description
= NULL_IF_CONFIG_SMALL("Apply Dynamic Equalization of input audio."),
279 .p
.priv_class
= &adynamicequalizer_class
,
280 .p
.flags
= AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL
|
281 AVFILTER_FLAG_SLICE_THREADS
,
282 .priv_size
= sizeof(AudioDynamicEqualizerContext
),
284 FILTER_INPUTS(inputs
),
285 FILTER_OUTPUTS(ff_audio_default_filterpad
),
286 FILTER_QUERY_FUNC2(query_formats
),
287 .process_command
= ff_filter_process_command
,