2 * Copyright (c) 2001-2010 Krzysztof Foltman, Markus Schmidt, Thor Harald Johansen, Vladimir Sadovnikov and others
3 * Copyright (c) 2015 Paul B Mahol
5 * This file is part of FFmpeg.
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.
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.
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
22 #include "libavutil/opt.h"
23 #include "libavutil/samplefmt.h"
28 typedef struct CompensationDelayContext
{
40 } CompensationDelayContext
;
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)
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
},
55 AVFILTER_DEFINE_CLASS(compensationdelay
);
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))
66 static int config_input(AVFilterLink
*inlink
)
68 AVFilterContext
*ctx
= inlink
->dst
;
69 CompensationDelayContext
*s
= ctx
->priv
;
70 unsigned min_size
, new_size
= 1;
72 s
->delay
= (s
->distance_m
* 100. + s
->distance_cm
* 1. + s
->distance_mm
* .1) *
73 COMP_DELAY_SOUND_FRONT_DELAY(s
->temp
) * inlink
->sample_rate
;
74 min_size
= inlink
->sample_rate
* COMP_DELAY_MAX_DELAY
;
76 while (new_size
< min_size
)
79 s
->buf_size
= new_size
;
80 s
->delay_frame
= ff_get_audio_buffer(inlink
, s
->buf_size
);
82 return AVERROR(ENOMEM
);
87 static int filter_frame(AVFilterLink
*inlink
, AVFrame
*in
)
89 AVFilterContext
*ctx
= inlink
->dst
;
90 AVFilterLink
*outlink
= ctx
->outputs
[0];
91 CompensationDelayContext
*s
= ctx
->priv
;
92 const unsigned b_mask
= s
->buf_size
- 1;
93 const unsigned buf_size
= s
->buf_size
;
94 const unsigned delay
= s
->delay
;
95 const double dry
= s
->dry
;
96 const double wet
= s
->wet
;
97 unsigned r_ptr
, w_ptr
= 0;
101 out
= ff_get_audio_buffer(outlink
, in
->nb_samples
);
104 return AVERROR(ENOMEM
);
106 av_frame_copy_props(out
, in
);
108 for (ch
= 0; ch
< inlink
->ch_layout
.nb_channels
; ch
++) {
109 const double *src
= (const double *)in
->extended_data
[ch
];
110 double *dst
= (double *)out
->extended_data
[ch
];
111 double *buffer
= (double *)s
->delay_frame
->extended_data
[ch
];
114 r_ptr
= (w_ptr
+ buf_size
- delay
) & b_mask
;
116 for (n
= 0; n
< in
->nb_samples
; n
++) {
117 const double sample
= src
[n
];
119 buffer
[w_ptr
] = sample
;
120 dst
[n
] = dry
* sample
+ wet
* buffer
[r_ptr
];
121 w_ptr
= (w_ptr
+ 1) & b_mask
;
122 r_ptr
= (r_ptr
+ 1) & b_mask
;
127 if (ctx
->is_disabled
) {
129 return ff_filter_frame(outlink
, in
);
133 return ff_filter_frame(outlink
, out
);
136 static int process_command(AVFilterContext
*ctx
, const char *cmd
, const char *args
,
137 char *res
, int res_len
, int flags
)
139 CompensationDelayContext
*s
= ctx
->priv
;
140 AVFilterLink
*outlink
= ctx
->outputs
[0];
143 ret
= ff_filter_process_command(ctx
, cmd
, args
, res
, res_len
, flags
);
147 s
->delay
= (s
->distance_m
* 100. + s
->distance_cm
* 1. + s
->distance_mm
* .1) *
148 COMP_DELAY_SOUND_FRONT_DELAY(s
->temp
) * outlink
->sample_rate
;
153 static av_cold
void uninit(AVFilterContext
*ctx
)
155 CompensationDelayContext
*s
= ctx
->priv
;
157 av_frame_free(&s
->delay_frame
);
160 static const AVFilterPad compensationdelay_inputs
[] = {
163 .type
= AVMEDIA_TYPE_AUDIO
,
164 .config_props
= config_input
,
165 .filter_frame
= filter_frame
,
169 const FFFilter ff_af_compensationdelay
= {
170 .p
.name
= "compensationdelay",
171 .p
.description
= NULL_IF_CONFIG_SMALL("Audio Compensation Delay Line."),
172 .p
.priv_class
= &compensationdelay_class
,
173 .p
.flags
= AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL
,
174 .priv_size
= sizeof(CompensationDelayContext
),
176 FILTER_INPUTS(compensationdelay_inputs
),
177 FILTER_OUTPUTS(ff_audio_default_filterpad
),
178 FILTER_SINGLE_SAMPLEFMT(AV_SAMPLE_FMT_DBLP
),
179 .process_command
= process_command
,