2 * Copyright (c) 2019 Paul B Mahol
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
23 #include "libavutil/avassert.h"
24 #include "libavutil/audio_fifo.h"
25 #include "libavutil/opt.h"
30 #define SQR(x) ((x) * (x))
32 typedef struct AudioNLMeansContext {
52 float (*compute_distance)(const float *f1, const float *f2, int K);
53 } AudioNLMeansContext;
55 #define OFFSET(x) offsetof(AudioNLMeansContext, x)
56 #define AF AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
58 static const AVOption anlmdn_options[] = {
59 { "s", "set denoising strength", OFFSET(a), AV_OPT_TYPE_FLOAT, {.dbl=1}, 1, 9999, AF },
60 { "p", "set patch duration", OFFSET(pd), AV_OPT_TYPE_DURATION, {.i64=2000}, 1000, 100000, AF },
61 { "r", "set research duration", OFFSET(rd), AV_OPT_TYPE_DURATION, {.i64=6000}, 2000, 300000, AF },
65 AVFILTER_DEFINE_CLASS(anlmdn);
67 static int query_formats(AVFilterContext *ctx)
69 AVFilterFormats *formats = NULL;
70 AVFilterChannelLayouts *layouts = NULL;
71 static const enum AVSampleFormat sample_fmts[] = {
77 formats = ff_make_format_list(sample_fmts);
79 return AVERROR(ENOMEM);
80 ret = ff_set_common_formats(ctx, formats);
84 layouts = ff_all_channel_counts();
86 return AVERROR(ENOMEM);
88 ret = ff_set_common_channel_layouts(ctx, layouts);
92 formats = ff_all_samplerates();
93 return ff_set_common_samplerates(ctx, formats);
96 static float compute_distance_ssd(const float *f1, const float *f2, int K)
100 for (int k = -K; k <= K; k++)
101 distance += SQR(f1[k] - f2[k]);
106 static int config_output(AVFilterLink *outlink)
108 AVFilterContext *ctx = outlink->src;
109 AudioNLMeansContext *s = ctx->priv;
111 s->K = av_rescale(s->pd, outlink->sample_rate, AV_TIME_BASE);
112 s->S = av_rescale(s->rd, outlink->sample_rate, AV_TIME_BASE);
114 s->pts = AV_NOPTS_VALUE;
116 s->N = s->H + (s->K + s->S) * 2;
118 av_frame_free(&s->in);
119 av_frame_free(&s->cache);
120 s->in = ff_get_audio_buffer(outlink, s->N);
122 return AVERROR(ENOMEM);
124 s->cache = ff_get_audio_buffer(outlink, s->S * 2);
126 return AVERROR(ENOMEM);
128 s->fifo = av_audio_fifo_alloc(outlink->format, outlink->channels, s->N);
130 return AVERROR(ENOMEM);
132 s->compute_distance = compute_distance_ssd;
137 static int filter_channel(AVFilterContext *ctx, void *arg, int ch, int nb_jobs)
139 AudioNLMeansContext *s = ctx->priv;
143 const float *f = (const float *)(s->in->extended_data[ch]) + K;
144 float *cache = (float *)s->cache->extended_data[ch];
145 const float sw = 32768.f / s->a;
146 float *dst = (float *)out->extended_data[ch] + s->offset;
148 for (int i = S; i < s->H + S; i++) {
149 float P = 0.f, Q = 0.f;
153 for (int j = i - S; j <= i + S; j++) {
156 cache[v++] = s->compute_distance(f + i, f + j, K);
159 for (int j = i - S; j < i; j++, v++)
160 cache[v] = cache[v] - SQR(f[i - K - 1] - f[j - K - 1]) + SQR(f[i + K] - f[j + K]);
162 for (int j = i + 1; j <= i + S; j++, v++)
163 cache[v] = cache[v] - SQR(f[i - K - 1] - f[j - K - 1]) + SQR(f[i + K] - f[j + K]);
166 for (int j = 0; j < v; j++) {
167 const float distance = cache[j];
170 av_assert0(distance >= 0.f);
175 P += w * f[i - S + j + (j >= S)];
188 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
190 AVFilterContext *ctx = inlink->dst;
191 AVFilterLink *outlink = ctx->outputs[0];
192 AudioNLMeansContext *s = ctx->priv;
194 int available, wanted, ret;
196 if (s->pts == AV_NOPTS_VALUE)
199 ret = av_audio_fifo_write(s->fifo, (void **)in->extended_data,
204 available = av_audio_fifo_size(s->fifo);
205 wanted = (available / s->H) * s->H;
207 if (wanted >= s->H && available >= s->N) {
208 out = ff_get_audio_buffer(outlink, wanted);
210 return AVERROR(ENOMEM);
213 while (available >= s->N) {
214 ret = av_audio_fifo_peek(s->fifo, (void **)s->in->extended_data, s->N);
218 ctx->internal->execute(ctx, filter_channel, out, NULL, inlink->channels);
220 av_audio_fifo_drain(s->fifo, s->H);
228 out->nb_samples = s->offset;
231 return ff_filter_frame(outlink, out);
237 static av_cold void uninit(AVFilterContext *ctx)
239 AudioNLMeansContext *s = ctx->priv;
241 av_audio_fifo_free(s->fifo);
242 av_frame_free(&s->in);
243 av_frame_free(&s->cache);
246 static const AVFilterPad inputs[] = {
249 .type = AVMEDIA_TYPE_AUDIO,
250 .filter_frame = filter_frame,
255 static const AVFilterPad outputs[] = {
258 .type = AVMEDIA_TYPE_AUDIO,
259 .config_props = config_output,
264 AVFilter ff_af_anlmdn = {
266 .description = NULL_IF_CONFIG_SMALL("Reduce broadband noise from stream using Non-Local Means."),
267 .query_formats = query_formats,
268 .priv_size = sizeof(AudioNLMeansContext),
269 .priv_class = &anlmdn_class,
273 .flags = AVFILTER_FLAG_SLICE_THREADS,