1 /*
2  * This file is part of FFmpeg.
3  *
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.
8  *
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.
13  *
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
17  */
18 
19 #include "libavutil/ffmath.h"
20 #include "libavutil/opt.h"
21 #include "avfilter.h"
22 #include "audio.h"
23 #include "formats.h"
24 
25 typedef struct AudioDynamicSmoothContext {
26     const AVClass *class;
27 
28     double sensitivity;
29     double basefreq;
30 
31     AVFrame *coeffs;
32 } AudioDynamicSmoothContext;
33 
config_input(AVFilterLink *inlink)34 static int config_input(AVFilterLink *inlink)
35 {
36     AVFilterContext *ctx = inlink->dst;
37     AudioDynamicSmoothContext *s = ctx->priv;
38 
39     s->coeffs = ff_get_audio_buffer(inlink, 3);
40     if (!s->coeffs)
41         return AVERROR(ENOMEM);
42 
43     return 0;
44 }
45 
filter_frame(AVFilterLink *inlink, AVFrame *in)46 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
47 {
48     AVFilterContext *ctx = inlink->dst;
49     AVFilterLink *outlink = ctx->outputs[0];
50     AudioDynamicSmoothContext *s = ctx->priv;
51     const double sensitivity = s->sensitivity;
52     const double wc = s->basefreq / in->sample_rate;
53     AVFrame *out;
54 
55     if (av_frame_is_writable(in)) {
56         out = in;
57     } else {
58         out = ff_get_audio_buffer(outlink, in->nb_samples);
59         if (!out) {
60             av_frame_free(&in);
61             return AVERROR(ENOMEM);
62         }
63         av_frame_copy_props(out, in);
64     }
65 
66     for (int ch = 0; ch < out->ch_layout.nb_channels; ch++) {
67         const double *src = (const double *)in->extended_data[ch];
68         double *dst = (double *)out->extended_data[ch];
69         double *coeffs = (double *)s->coeffs->extended_data[ch];
70         double low1 = coeffs[0];
71         double low2 = coeffs[1];
72         double inz  = coeffs[2];
73 
74         for (int n = 0; n < out->nb_samples; n++) {
75             double low1z = low1;
76             double low2z = low2;
77             double bandz = low2z - low1z;
78             double wd = wc + sensitivity * fabs(bandz);
79             double g = fmin(1., wd * (5.9948827 + wd * (-11.969296 + wd * 15.959062)));
80 
81             low1 = low1z + g * (0.5 * (src[n] + inz)   - low1z);
82             low2 = low2z + g * (0.5 * (low1   + low1z) - low2z);
83             inz = src[n];
84             dst[n] = ctx->is_disabled ? src[n] : low2;
85         }
86 
87         coeffs[0] = low1;
88         coeffs[1] = low2;
89         coeffs[2] = inz;
90     }
91 
92     if (out != in)
93         av_frame_free(&in);
94     return ff_filter_frame(outlink, out);
95 }
96 
uninit(AVFilterContext *ctx)97 static av_cold void uninit(AVFilterContext *ctx)
98 {
99     AudioDynamicSmoothContext *s = ctx->priv;
100 
101     av_frame_free(&s->coeffs);
102 }
103 
104 #define OFFSET(x) offsetof(AudioDynamicSmoothContext, x)
105 #define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM
106 
107 static const AVOption adynamicsmooth_options[] = {
108     { "sensitivity",  "set smooth sensitivity",  OFFSET(sensitivity),  AV_OPT_TYPE_DOUBLE, {.dbl=2},     0, 1000000, FLAGS },
109     { "basefreq",     "set base frequency",      OFFSET(basefreq),     AV_OPT_TYPE_DOUBLE, {.dbl=22050}, 2, 1000000, FLAGS },
110     { NULL }
111 };
112 
113 AVFILTER_DEFINE_CLASS(adynamicsmooth);
114 
115 static const AVFilterPad inputs[] = {
116     {
117         .name         = "default",
118         .type         = AVMEDIA_TYPE_AUDIO,
119         .filter_frame = filter_frame,
120         .config_props = config_input,
121     },
122 };
123 
124 static const AVFilterPad outputs[] = {
125     {
126         .name = "default",
127         .type = AVMEDIA_TYPE_AUDIO,
128     },
129 };
130 
131 const AVFilter ff_af_adynamicsmooth = {
132     .name            = "adynamicsmooth",
133     .description     = NULL_IF_CONFIG_SMALL("Apply Dynamic Smoothing of input audio."),
134     .priv_size       = sizeof(AudioDynamicSmoothContext),
135     .priv_class      = &adynamicsmooth_class,
136     .uninit          = uninit,
137     FILTER_INPUTS(inputs),
138     FILTER_OUTPUTS(outputs),
139     FILTER_SINGLE_SAMPLEFMT(AV_SAMPLE_FMT_DBLP),
140     .flags           = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL,
141     .process_command = ff_filter_process_command,
142 };
143