• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2001-2010 Krzysztof Foltman, Markus Schmidt, Thor Harald Johansen, Vladimir Sadovnikov and others
3  * Copyright (c) 2015 Paul B Mahol
4  *
5  * This file is part of FFmpeg.
6  *
7  * FFmpeg is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * FFmpeg is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with FFmpeg; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20  */
21 
22 #include "libavutil/opt.h"
23 #include "libavutil/samplefmt.h"
24 #include "avfilter.h"
25 #include "audio.h"
26 #include "internal.h"
27 
28 typedef struct CompensationDelayContext {
29     const AVClass *class;
30     int distance_mm;
31     int distance_cm;
32     int distance_m;
33     double dry, wet;
34     int temp;
35 
36     unsigned delay;
37     unsigned w_ptr;
38     unsigned buf_size;
39     AVFrame *delay_frame;
40 } CompensationDelayContext;
41 
42 #define OFFSET(x) offsetof(CompensationDelayContext, x)
43 #define A (AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM)
44 
45 static const AVOption compensationdelay_options[] = {
46     { "mm",   "set mm distance",    OFFSET(distance_mm), AV_OPT_TYPE_INT,    {.i64=0},    0,  10, A },
47     { "cm",   "set cm distance",    OFFSET(distance_cm), AV_OPT_TYPE_INT,    {.i64=0},    0, 100, A },
48     { "m",    "set meter distance", OFFSET(distance_m),  AV_OPT_TYPE_INT,    {.i64=0},    0, 100, A },
49     { "dry",  "set dry amount",     OFFSET(dry),         AV_OPT_TYPE_DOUBLE, {.dbl=0},    0,   1, A },
50     { "wet",  "set wet amount",     OFFSET(wet),         AV_OPT_TYPE_DOUBLE, {.dbl=1},    0,   1, A },
51     { "temp", "set temperature °C", OFFSET(temp),        AV_OPT_TYPE_INT,    {.i64=20}, -50,  50, A },
52     { NULL }
53 };
54 
55 AVFILTER_DEFINE_CLASS(compensationdelay);
56 
57 // The maximum distance for options
58 #define COMP_DELAY_MAX_DISTANCE            (100.0 * 100.0 + 100.0 * 1.0 + 1.0)
59 // The actual speed of sound in normal conditions
60 #define COMP_DELAY_SOUND_SPEED_KM_H(temp)  1.85325 * (643.95 * sqrt(((temp + 273.15) / 273.15)))
61 #define COMP_DELAY_SOUND_SPEED_CM_S(temp)  (COMP_DELAY_SOUND_SPEED_KM_H(temp) * (1000.0 * 100.0) /* cm/km */ / (60.0 * 60.0) /* s/h */)
62 #define COMP_DELAY_SOUND_FRONT_DELAY(temp) (1.0 / COMP_DELAY_SOUND_SPEED_CM_S(temp))
63 // The maximum delay may be reached by this filter
64 #define COMP_DELAY_MAX_DELAY               (COMP_DELAY_MAX_DISTANCE * COMP_DELAY_SOUND_FRONT_DELAY(50))
65 
config_input(AVFilterLink * inlink)66 static int config_input(AVFilterLink *inlink)
67 {
68     AVFilterContext *ctx = inlink->dst;
69     CompensationDelayContext *s = ctx->priv;
70     unsigned min_size, new_size = 1;
71     int ret;
72 
73     s->delay = (s->distance_m * 100. + s->distance_cm * 1. + s->distance_mm * .1) *
74                COMP_DELAY_SOUND_FRONT_DELAY(s->temp) * inlink->sample_rate;
75     min_size = inlink->sample_rate * COMP_DELAY_MAX_DELAY;
76 
77     while (new_size < min_size)
78         new_size <<= 1;
79 
80     s->delay_frame = av_frame_alloc();
81     if (!s->delay_frame)
82         return AVERROR(ENOMEM);
83 
84     s->buf_size                    = new_size;
85     s->delay_frame->format         = inlink->format;
86     s->delay_frame->nb_samples     = new_size;
87 #if FF_API_OLD_CHANNEL_LAYOUT
88 FF_DISABLE_DEPRECATION_WARNINGS
89     s->delay_frame->channel_layout = inlink->channel_layout;
90     s->delay_frame->channels       = inlink->ch_layout.nb_channels;
91 FF_ENABLE_DEPRECATION_WARNINGS
92 #endif
93     if ((ret = av_channel_layout_copy(&s->delay_frame->ch_layout, &inlink->ch_layout)) < 0)
94         return ret;
95 
96     return av_frame_get_buffer(s->delay_frame, 0);
97 }
98 
filter_frame(AVFilterLink * inlink,AVFrame * in)99 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
100 {
101     AVFilterContext *ctx = inlink->dst;
102     AVFilterLink *outlink = ctx->outputs[0];
103     CompensationDelayContext *s = ctx->priv;
104     const unsigned b_mask = s->buf_size - 1;
105     const unsigned buf_size = s->buf_size;
106     const unsigned delay = s->delay;
107     const double dry = s->dry;
108     const double wet = s->wet;
109     unsigned r_ptr, w_ptr = 0;
110     AVFrame *out;
111     int n, ch;
112 
113     out = ff_get_audio_buffer(outlink, in->nb_samples);
114     if (!out) {
115         av_frame_free(&in);
116         return AVERROR(ENOMEM);
117     }
118     av_frame_copy_props(out, in);
119 
120     for (ch = 0; ch < inlink->ch_layout.nb_channels; ch++) {
121         const double *src = (const double *)in->extended_data[ch];
122         double *dst = (double *)out->extended_data[ch];
123         double *buffer = (double *)s->delay_frame->extended_data[ch];
124 
125         w_ptr =  s->w_ptr;
126         r_ptr = (w_ptr + buf_size - delay) & b_mask;
127 
128         for (n = 0; n < in->nb_samples; n++) {
129             const double sample = src[n];
130 
131             buffer[w_ptr] = sample;
132             dst[n] = dry * sample + wet * buffer[r_ptr];
133             w_ptr = (w_ptr + 1) & b_mask;
134             r_ptr = (r_ptr + 1) & b_mask;
135         }
136     }
137     s->w_ptr = w_ptr;
138 
139     if (ctx->is_disabled) {
140         av_frame_free(&out);
141         return ff_filter_frame(outlink, in);
142     }
143 
144     av_frame_free(&in);
145     return ff_filter_frame(outlink, out);
146 }
147 
process_command(AVFilterContext * ctx,const char * cmd,const char * args,char * res,int res_len,int flags)148 static int process_command(AVFilterContext *ctx, const char *cmd, const char *args,
149                            char *res, int res_len, int flags)
150 {
151     CompensationDelayContext *s = ctx->priv;
152     AVFilterLink *outlink = ctx->outputs[0];
153     int ret;
154 
155     ret = ff_filter_process_command(ctx, cmd, args, res, res_len, flags);
156     if (ret < 0)
157         return ret;
158 
159     s->delay = (s->distance_m * 100. + s->distance_cm * 1. + s->distance_mm * .1) *
160                COMP_DELAY_SOUND_FRONT_DELAY(s->temp) * outlink->sample_rate;
161 
162     return 0;
163 }
164 
uninit(AVFilterContext * ctx)165 static av_cold void uninit(AVFilterContext *ctx)
166 {
167     CompensationDelayContext *s = ctx->priv;
168 
169     av_frame_free(&s->delay_frame);
170 }
171 
172 static const AVFilterPad compensationdelay_inputs[] = {
173     {
174         .name         = "default",
175         .type         = AVMEDIA_TYPE_AUDIO,
176         .config_props = config_input,
177         .filter_frame = filter_frame,
178     },
179 };
180 
181 static const AVFilterPad compensationdelay_outputs[] = {
182     {
183         .name = "default",
184         .type = AVMEDIA_TYPE_AUDIO,
185     },
186 };
187 
188 const AVFilter ff_af_compensationdelay = {
189     .name          = "compensationdelay",
190     .description   = NULL_IF_CONFIG_SMALL("Audio Compensation Delay Line."),
191     .priv_size     = sizeof(CompensationDelayContext),
192     .priv_class    = &compensationdelay_class,
193     .uninit        = uninit,
194     FILTER_INPUTS(compensationdelay_inputs),
195     FILTER_OUTPUTS(compensationdelay_outputs),
196     FILTER_SINGLE_SAMPLEFMT(AV_SAMPLE_FMT_DBLP),
197     .process_command = process_command,
198     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL,
199 };
200