2 * Copyright (c) 2016 The FFmpeg Project
4 * This file is part of FFmpeg.
6 * FFmpeg is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * FFmpeg is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with FFmpeg; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 #include "libavutil/channel_layout.h"
22 #include "libavutil/opt.h"
27 typedef struct CrystalizerContext {
32 int (*filter)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs);
35 #define OFFSET(x) offsetof(CrystalizerContext, x)
36 #define A AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM
38 static const AVOption crystalizer_options[] = {
39 { "i", "set intensity", OFFSET(mult), AV_OPT_TYPE_FLOAT, {.dbl=2.0}, 0, 10, A },
40 { "c", "enable clipping", OFFSET(clip), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1, A },
44 AVFILTER_DEFINE_CLASS(crystalizer);
46 static int query_formats(AVFilterContext *ctx)
48 AVFilterFormats *formats = NULL;
49 AVFilterChannelLayouts *layouts = NULL;
50 static const enum AVSampleFormat sample_fmts[] = {
51 AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_FLTP,
52 AV_SAMPLE_FMT_DBL, AV_SAMPLE_FMT_DBLP,
57 formats = ff_make_format_list(sample_fmts);
59 return AVERROR(ENOMEM);
60 ret = ff_set_common_formats(ctx, formats);
64 layouts = ff_all_channel_counts();
66 return AVERROR(ENOMEM);
68 ret = ff_set_common_channel_layouts(ctx, layouts);
72 formats = ff_all_samplerates();
73 return ff_set_common_samplerates(ctx, formats);
76 typedef struct ThreadData {
86 static int filter_flt(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
91 const void **s = td->s;
92 const int nb_samples = td->nb_samples;
93 const int channels = td->channels;
94 float mult = td->mult;
95 const int clip = td->clip;
96 const int start = (channels * jobnr) / nb_jobs;
97 const int end = (channels * (jobnr+1)) / nb_jobs;
101 for (c = start; c < end; c++) {
102 const float *src = s[0];
105 for (n = 0; n < nb_samples; n++) {
106 float current = src[c];
107 dst[c] = current + (current - prv[c]) * mult;
110 dst[c] = av_clipf(dst[c], -1, 1);
121 static int filter_dbl(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
123 ThreadData *td = arg;
126 const void **s = td->s;
127 const int nb_samples = td->nb_samples;
128 const int channels = td->channels;
129 float mult = td->mult;
130 const int clip = td->clip;
131 const int start = (channels * jobnr) / nb_jobs;
132 const int end = (channels * (jobnr+1)) / nb_jobs;
136 for (c = start; c < end; c++) {
137 const double *src = s[0];
140 for (n = 0; n < nb_samples; n++) {
141 double current = src[c];
143 dst[c] = current + (current - prv[c]) * mult;
146 dst[c] = av_clipd(dst[c], -1, 1);
157 static int filter_fltp(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
159 ThreadData *td = arg;
162 const void **s = td->s;
163 const int nb_samples = td->nb_samples;
164 const int channels = td->channels;
165 float mult = td->mult;
166 const int clip = td->clip;
167 const int start = (channels * jobnr) / nb_jobs;
168 const int end = (channels * (jobnr+1)) / nb_jobs;
171 for (c = start; c < end; c++) {
172 const float *src = s[c];
176 for (n = 0; n < nb_samples; n++) {
177 float current = src[n];
179 dst[n] = current + (current - prv[0]) * mult;
182 dst[n] = av_clipf(dst[n], -1, 1);
190 static int filter_dblp(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
192 ThreadData *td = arg;
195 const void **s = td->s;
196 const int nb_samples = td->nb_samples;
197 const int channels = td->channels;
198 float mult = td->mult;
199 const int clip = td->clip;
200 const int start = (channels * jobnr) / nb_jobs;
201 const int end = (channels * (jobnr+1)) / nb_jobs;
204 for (c = start; c < end; c++) {
205 const double *src = s[c];
209 for (n = 0; n < nb_samples; n++) {
210 double current = src[n];
212 dst[n] = current + (current - prv[0]) * mult;
215 dst[n] = av_clipd(dst[n], -1, 1);
223 static int config_input(AVFilterLink *inlink)
225 AVFilterContext *ctx = inlink->dst;
226 CrystalizerContext *s = ctx->priv;
228 switch (inlink->format) {
229 case AV_SAMPLE_FMT_FLT: s->filter = filter_flt; break;
230 case AV_SAMPLE_FMT_DBL: s->filter = filter_dbl; break;
231 case AV_SAMPLE_FMT_FLTP: s->filter = filter_fltp; break;
232 case AV_SAMPLE_FMT_DBLP: s->filter = filter_dblp; break;
238 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
240 AVFilterContext *ctx = inlink->dst;
241 AVFilterLink *outlink = ctx->outputs[0];
242 CrystalizerContext *s = ctx->priv;
247 s->prev = ff_get_audio_buffer(inlink, 1);
250 return AVERROR(ENOMEM);
254 if (av_frame_is_writable(in)) {
257 out = ff_get_audio_buffer(outlink, in->nb_samples);
260 return AVERROR(ENOMEM);
262 av_frame_copy_props(out, in);
265 td.d = (void **)out->extended_data;
266 td.s = (const void **)in->extended_data;
267 td.p = (void **)s->prev->extended_data;
268 td.nb_samples = in->nb_samples;
269 td.channels = in->channels;
270 td.mult = ctx->is_disabled ? 0.f : s->mult;
272 ctx->internal->execute(ctx, s->filter, &td, NULL, FFMIN(inlink->channels,
273 ff_filter_get_nb_threads(ctx)));
278 return ff_filter_frame(outlink, out);
281 static av_cold void uninit(AVFilterContext *ctx)
283 CrystalizerContext *s = ctx->priv;
285 av_frame_free(&s->prev);
288 static const AVFilterPad inputs[] = {
291 .type = AVMEDIA_TYPE_AUDIO,
292 .filter_frame = filter_frame,
293 .config_props = config_input,
298 static const AVFilterPad outputs[] = {
301 .type = AVMEDIA_TYPE_AUDIO,
306 AVFilter ff_af_crystalizer = {
307 .name = "crystalizer",
308 .description = NULL_IF_CONFIG_SMALL("Simple audio noise sharpening filter."),
309 .query_formats = query_formats,
310 .priv_size = sizeof(CrystalizerContext),
311 .priv_class = &crystalizer_class,
315 .process_command = ff_filter_process_command,
316 .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL |
317 AVFILTER_FLAG_SLICE_THREADS,