blob: d5a348431716324675961ae71a2bf0d411263d4b
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 |
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 | |
66 | static int query_formats(AVFilterContext *ctx) |
67 | { |
68 | AVFilterChannelLayouts *layouts; |
69 | AVFilterFormats *formats; |
70 | static const enum AVSampleFormat sample_fmts[] = { |
71 | AV_SAMPLE_FMT_DBLP, |
72 | AV_SAMPLE_FMT_NONE |
73 | }; |
74 | int ret; |
75 | |
76 | layouts = ff_all_channel_counts(); |
77 | if (!layouts) |
78 | return AVERROR(ENOMEM); |
79 | ret = ff_set_common_channel_layouts(ctx, layouts); |
80 | if (ret < 0) |
81 | return ret; |
82 | |
83 | formats = ff_make_format_list(sample_fmts); |
84 | if (!formats) |
85 | return AVERROR(ENOMEM); |
86 | ret = ff_set_common_formats(ctx, formats); |
87 | if (ret < 0) |
88 | return ret; |
89 | |
90 | formats = ff_all_samplerates(); |
91 | if (!formats) |
92 | return AVERROR(ENOMEM); |
93 | return ff_set_common_samplerates(ctx, formats); |
94 | } |
95 | |
96 | static int config_input(AVFilterLink *inlink) |
97 | { |
98 | AVFilterContext *ctx = inlink->dst; |
99 | CompensationDelayContext *s = ctx->priv; |
100 | unsigned min_size, new_size = 1; |
101 | |
102 | s->delay = (s->distance_m * 100. + s->distance_cm * 1. + s->distance_mm * .1) * |
103 | COMP_DELAY_SOUND_FRONT_DELAY(s->temp) * inlink->sample_rate; |
104 | min_size = inlink->sample_rate * COMP_DELAY_MAX_DELAY; |
105 | |
106 | while (new_size < min_size) |
107 | new_size <<= 1; |
108 | |
109 | s->delay_frame = av_frame_alloc(); |
110 | if (!s->delay_frame) |
111 | return AVERROR(ENOMEM); |
112 | |
113 | s->buf_size = new_size; |
114 | s->delay_frame->format = inlink->format; |
115 | s->delay_frame->nb_samples = new_size; |
116 | s->delay_frame->channel_layout = inlink->channel_layout; |
117 | |
118 | return av_frame_get_buffer(s->delay_frame, 32); |
119 | } |
120 | |
121 | static int filter_frame(AVFilterLink *inlink, AVFrame *in) |
122 | { |
123 | AVFilterContext *ctx = inlink->dst; |
124 | CompensationDelayContext *s = ctx->priv; |
125 | const unsigned b_mask = s->buf_size - 1; |
126 | const unsigned buf_size = s->buf_size; |
127 | const unsigned delay = s->delay; |
128 | const double dry = s->dry; |
129 | const double wet = s->wet; |
130 | unsigned r_ptr, w_ptr; |
131 | AVFrame *out; |
132 | int n, ch; |
133 | |
134 | out = ff_get_audio_buffer(inlink, in->nb_samples); |
135 | if (!out) { |
136 | av_frame_free(&in); |
137 | return AVERROR(ENOMEM); |
138 | } |
139 | av_frame_copy_props(out, in); |
140 | |
141 | for (ch = 0; ch < inlink->channels; ch++) { |
142 | const double *src = (const double *)in->extended_data[ch]; |
143 | double *dst = (double *)out->extended_data[ch]; |
144 | double *buffer = (double *)s->delay_frame->extended_data[ch]; |
145 | |
146 | w_ptr = s->w_ptr; |
147 | r_ptr = (w_ptr + buf_size - delay) & b_mask; |
148 | |
149 | for (n = 0; n < in->nb_samples; n++) { |
150 | const double sample = src[n]; |
151 | |
152 | buffer[w_ptr] = sample; |
153 | dst[n] = dry * sample + wet * buffer[r_ptr]; |
154 | w_ptr = (w_ptr + 1) & b_mask; |
155 | r_ptr = (r_ptr + 1) & b_mask; |
156 | } |
157 | } |
158 | s->w_ptr = w_ptr; |
159 | |
160 | av_frame_free(&in); |
161 | return ff_filter_frame(ctx->outputs[0], out); |
162 | } |
163 | |
164 | static av_cold void uninit(AVFilterContext *ctx) |
165 | { |
166 | CompensationDelayContext *s = ctx->priv; |
167 | |
168 | av_frame_free(&s->delay_frame); |
169 | } |
170 | |
171 | static const AVFilterPad compensationdelay_inputs[] = { |
172 | { |
173 | .name = "default", |
174 | .type = AVMEDIA_TYPE_AUDIO, |
175 | .config_props = config_input, |
176 | .filter_frame = filter_frame, |
177 | }, |
178 | { NULL } |
179 | }; |
180 | |
181 | static const AVFilterPad compensationdelay_outputs[] = { |
182 | { |
183 | .name = "default", |
184 | .type = AVMEDIA_TYPE_AUDIO, |
185 | }, |
186 | { NULL } |
187 | }; |
188 | |
189 | AVFilter ff_af_compensationdelay = { |
190 | .name = "compensationdelay", |
191 | .description = NULL_IF_CONFIG_SMALL("Audio Compensation Delay Line."), |
192 | .query_formats = query_formats, |
193 | .priv_size = sizeof(CompensationDelayContext), |
194 | .priv_class = &compensationdelay_class, |
195 | .uninit = uninit, |
196 | .inputs = compensationdelay_inputs, |
197 | .outputs = compensationdelay_outputs, |
198 | }; |
199 |