Signed-off-by: Paul B Mahol <onemda@gmail.com>
Paul B Mahol authored on 2017/05/15 01:30:12... | ... |
@@ -2251,6 +2251,35 @@ Set temperature degree in Celsius. This is the temperature of the environment. |
2251 | 2251 |
Default is 20. |
2252 | 2252 |
@end table |
2253 | 2253 |
|
2254 |
+@section crossfeed |
|
2255 |
+Apply headphone crossfeed filter. |
|
2256 |
+ |
|
2257 |
+Crossfeed is the process of blending the left and right channels of stereo |
|
2258 |
+audio recording. |
|
2259 |
+It is mainly used to reduce extreme stereo separation of low frequencies. |
|
2260 |
+ |
|
2261 |
+The intent is to produce more speaker like sound to the listener. |
|
2262 |
+ |
|
2263 |
+The filter accepts the following options: |
|
2264 |
+ |
|
2265 |
+@table @option |
|
2266 |
+@item strength |
|
2267 |
+Set strength of crossfeed. Default is 0.2. Allowed range is from 0 to 1. |
|
2268 |
+This sets gain of low shelf filter for side part of stereo image. |
|
2269 |
+Default is -6dB. Max allowed is -30db when strength is set to 1. |
|
2270 |
+ |
|
2271 |
+@item range |
|
2272 |
+Set soundstage wideness. Default is 0.5. Allowed range is from 0 to 1. |
|
2273 |
+This sets cut off frequency of low shelf filter. Default is cut off near |
|
2274 |
+1550 Hz. With range set to 1 cut off frequency is set to 2100 Hz. |
|
2275 |
+ |
|
2276 |
+@item level_in |
|
2277 |
+Set input gain. Default is 0.9. |
|
2278 |
+ |
|
2279 |
+@item level_out |
|
2280 |
+Set output gain. Default is 1. |
|
2281 |
+@end table |
|
2282 |
+ |
|
2254 | 2283 |
@section crystalizer |
2255 | 2284 |
Simple algorithm to expand audio dynamic range. |
2256 | 2285 |
|
... | ... |
@@ -81,6 +81,7 @@ OBJS-$(CONFIG_CHANNELSPLIT_FILTER) += af_channelsplit.o |
81 | 81 |
OBJS-$(CONFIG_CHORUS_FILTER) += af_chorus.o generate_wave_table.o |
82 | 82 |
OBJS-$(CONFIG_COMPAND_FILTER) += af_compand.o |
83 | 83 |
OBJS-$(CONFIG_COMPENSATIONDELAY_FILTER) += af_compensationdelay.o |
84 |
+OBJS-$(CONFIG_CROSSFEED_FILTER) += af_crossfeed.o |
|
84 | 85 |
OBJS-$(CONFIG_CRYSTALIZER_FILTER) += af_crystalizer.o |
85 | 86 |
OBJS-$(CONFIG_DCSHIFT_FILTER) += af_dcshift.o |
86 | 87 |
OBJS-$(CONFIG_DYNAUDNORM_FILTER) += af_dynaudnorm.o |
87 | 88 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,169 @@ |
0 |
+/* |
|
1 |
+ * This file is part of FFmpeg. |
|
2 |
+ * |
|
3 |
+ * FFmpeg is free software; you can redistribute it and/or |
|
4 |
+ * modify it under the terms of the GNU Lesser General Public |
|
5 |
+ * License as published by the Free Software Foundation; either |
|
6 |
+ * version 2.1 of the License, or (at your option) any later version. |
|
7 |
+ * |
|
8 |
+ * FFmpeg is distributed in the hope that it will be useful, |
|
9 |
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
10 |
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
11 |
+ * Lesser General Public License for more details. |
|
12 |
+ * |
|
13 |
+ * You should have received a copy of the GNU Lesser General Public |
|
14 |
+ * License along with FFmpeg; if not, write to the Free Software |
|
15 |
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
|
16 |
+ */ |
|
17 |
+ |
|
18 |
+#include "libavutil/channel_layout.h" |
|
19 |
+#include "libavutil/opt.h" |
|
20 |
+#include "avfilter.h" |
|
21 |
+#include "audio.h" |
|
22 |
+#include "formats.h" |
|
23 |
+ |
|
24 |
+typedef struct CrossfeedContext { |
|
25 |
+ const AVClass *class; |
|
26 |
+ |
|
27 |
+ double range; |
|
28 |
+ double strength; |
|
29 |
+ double level_in; |
|
30 |
+ double level_out; |
|
31 |
+ |
|
32 |
+ double a0, a1, a2; |
|
33 |
+ double b0, b1, b2; |
|
34 |
+ |
|
35 |
+ double i1, i2; |
|
36 |
+ double o1, o2; |
|
37 |
+} CrossfeedContext; |
|
38 |
+ |
|
39 |
+static int query_formats(AVFilterContext *ctx) |
|
40 |
+{ |
|
41 |
+ AVFilterFormats *formats = NULL; |
|
42 |
+ AVFilterChannelLayouts *layout = NULL; |
|
43 |
+ int ret; |
|
44 |
+ |
|
45 |
+ if ((ret = ff_add_format (&formats, AV_SAMPLE_FMT_DBL )) < 0 || |
|
46 |
+ (ret = ff_set_common_formats (ctx , formats )) < 0 || |
|
47 |
+ (ret = ff_add_channel_layout (&layout , AV_CH_LAYOUT_STEREO)) < 0 || |
|
48 |
+ (ret = ff_set_common_channel_layouts (ctx , layout )) < 0 || |
|
49 |
+ (ret = ff_set_common_samplerates (ctx , ff_all_samplerates())) < 0) |
|
50 |
+ return ret; |
|
51 |
+ |
|
52 |
+ return 0; |
|
53 |
+} |
|
54 |
+ |
|
55 |
+static int config_input(AVFilterLink *inlink) |
|
56 |
+{ |
|
57 |
+ AVFilterContext *ctx = inlink->dst; |
|
58 |
+ CrossfeedContext *s = ctx->priv; |
|
59 |
+ double A = exp(s->strength * -30 / 40 * log(10.)); |
|
60 |
+ double w0 = 2 * M_PI * (1. - s->range) * 2100 / inlink->sample_rate; |
|
61 |
+ double alpha; |
|
62 |
+ |
|
63 |
+ alpha = sin(w0) / 2 * sqrt(2 * (1 / 0.5 - 1) + 2); |
|
64 |
+ |
|
65 |
+ s->a0 = (A + 1) + (A - 1) * cos(w0) + 2 * sqrt(A) * alpha; |
|
66 |
+ s->a1 = -2 * ((A - 1) + (A + 1) * cos(w0)); |
|
67 |
+ s->a2 = (A + 1) + (A - 1) * cos(w0) - 2 * sqrt(A) * alpha; |
|
68 |
+ s->b0 = A * ((A + 1) - (A - 1) * cos(w0) + 2 * sqrt(A) * alpha); |
|
69 |
+ s->b1 = 2 * A * ((A - 1) - (A + 1) * cos(w0)); |
|
70 |
+ s->b2 = A * ((A + 1) - (A - 1) * cos(w0) - 2 * sqrt(A) * alpha); |
|
71 |
+ |
|
72 |
+ s->a1 /= s->a0; |
|
73 |
+ s->a2 /= s->a0; |
|
74 |
+ s->b0 /= s->a0; |
|
75 |
+ s->b1 /= s->a0; |
|
76 |
+ s->b2 /= s->a0; |
|
77 |
+ |
|
78 |
+ return 0; |
|
79 |
+} |
|
80 |
+ |
|
81 |
+static int filter_frame(AVFilterLink *inlink, AVFrame *in) |
|
82 |
+{ |
|
83 |
+ AVFilterContext *ctx = inlink->dst; |
|
84 |
+ AVFilterLink *outlink = ctx->outputs[0]; |
|
85 |
+ CrossfeedContext *s = ctx->priv; |
|
86 |
+ const double *src = (const double *)in->data[0]; |
|
87 |
+ const double level_in = s->level_in; |
|
88 |
+ const double level_out = s->level_out; |
|
89 |
+ const double b0 = s->b0; |
|
90 |
+ const double b1 = s->b1; |
|
91 |
+ const double b2 = s->b2; |
|
92 |
+ const double a1 = s->a1; |
|
93 |
+ const double a2 = s->a2; |
|
94 |
+ AVFrame *out; |
|
95 |
+ double *dst; |
|
96 |
+ int n; |
|
97 |
+ |
|
98 |
+ if (av_frame_is_writable(in)) { |
|
99 |
+ out = in; |
|
100 |
+ } else { |
|
101 |
+ out = ff_get_audio_buffer(inlink, in->nb_samples); |
|
102 |
+ if (!out) { |
|
103 |
+ av_frame_free(&in); |
|
104 |
+ return AVERROR(ENOMEM); |
|
105 |
+ } |
|
106 |
+ av_frame_copy_props(out, in); |
|
107 |
+ } |
|
108 |
+ dst = (double *)out->data[0]; |
|
109 |
+ |
|
110 |
+ for (n = 0; n < out->nb_samples; n++, src += 2, dst += 2) { |
|
111 |
+ double mid = (src[0] + src[1]) * level_in * .5; |
|
112 |
+ double side = (src[0] - src[1]) * level_in * .5; |
|
113 |
+ double oside = side * b0 + s->i1 * b1 + s->i2 * b2 - s->o1 * a1 - s->o2 * a2; |
|
114 |
+ |
|
115 |
+ s->i2 = s->i1; |
|
116 |
+ s->i1 = side; |
|
117 |
+ s->o2 = s->o1; |
|
118 |
+ s->o1 = oside; |
|
119 |
+ |
|
120 |
+ dst[0] = (mid + oside) * level_out; |
|
121 |
+ dst[1] = (mid - oside) * level_out; |
|
122 |
+ } |
|
123 |
+ |
|
124 |
+ if (out != in) |
|
125 |
+ av_frame_free(&in); |
|
126 |
+ return ff_filter_frame(outlink, out); |
|
127 |
+} |
|
128 |
+ |
|
129 |
+#define OFFSET(x) offsetof(CrossfeedContext, x) |
|
130 |
+#define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM |
|
131 |
+ |
|
132 |
+static const AVOption crossfeed_options[] = { |
|
133 |
+ { "strength", "set crossfeed strength", OFFSET(strength), AV_OPT_TYPE_DOUBLE, {.dbl=.2}, 0, 1, FLAGS }, |
|
134 |
+ { "range", "set soundstage wideness", OFFSET(range), AV_OPT_TYPE_DOUBLE, {.dbl=.5}, 0, 1, FLAGS }, |
|
135 |
+ { "level_in", "set level in", OFFSET(level_in), AV_OPT_TYPE_DOUBLE, {.dbl=.9}, 0, 1, FLAGS }, |
|
136 |
+ { "level_out", "set level out", OFFSET(level_out), AV_OPT_TYPE_DOUBLE, {.dbl=1.}, 0, 1, FLAGS }, |
|
137 |
+ { NULL } |
|
138 |
+}; |
|
139 |
+ |
|
140 |
+AVFILTER_DEFINE_CLASS(crossfeed); |
|
141 |
+ |
|
142 |
+static const AVFilterPad inputs[] = { |
|
143 |
+ { |
|
144 |
+ .name = "default", |
|
145 |
+ .type = AVMEDIA_TYPE_AUDIO, |
|
146 |
+ .filter_frame = filter_frame, |
|
147 |
+ .config_props = config_input, |
|
148 |
+ }, |
|
149 |
+ { NULL } |
|
150 |
+}; |
|
151 |
+ |
|
152 |
+static const AVFilterPad outputs[] = { |
|
153 |
+ { |
|
154 |
+ .name = "default", |
|
155 |
+ .type = AVMEDIA_TYPE_AUDIO, |
|
156 |
+ }, |
|
157 |
+ { NULL } |
|
158 |
+}; |
|
159 |
+ |
|
160 |
+AVFilter ff_af_crossfeed = { |
|
161 |
+ .name = "crossfeed", |
|
162 |
+ .description = NULL_IF_CONFIG_SMALL("Apply headphone crossfeed filter."), |
|
163 |
+ .query_formats = query_formats, |
|
164 |
+ .priv_size = sizeof(CrossfeedContext), |
|
165 |
+ .priv_class = &crossfeed_class, |
|
166 |
+ .inputs = inputs, |
|
167 |
+ .outputs = outputs, |
|
168 |
+}; |
... | ... |
@@ -94,6 +94,7 @@ static void register_all(void) |
94 | 94 |
REGISTER_FILTER(CHORUS, chorus, af); |
95 | 95 |
REGISTER_FILTER(COMPAND, compand, af); |
96 | 96 |
REGISTER_FILTER(COMPENSATIONDELAY, compensationdelay, af); |
97 |
+ REGISTER_FILTER(CROSSFEED, crossfeed, af); |
|
97 | 98 |
REGISTER_FILTER(CRYSTALIZER, crystalizer, af); |
98 | 99 |
REGISTER_FILTER(DCSHIFT, dcshift, af); |
99 | 100 |
REGISTER_FILTER(DYNAUDNORM, dynaudnorm, af); |
... | ... |
@@ -30,8 +30,8 @@ |
30 | 30 |
#include "libavutil/version.h" |
31 | 31 |
|
32 | 32 |
#define LIBAVFILTER_VERSION_MAJOR 6 |
33 |
-#define LIBAVFILTER_VERSION_MINOR 89 |
|
34 |
-#define LIBAVFILTER_VERSION_MICRO 101 |
|
33 |
+#define LIBAVFILTER_VERSION_MINOR 90 |
|
34 |
+#define LIBAVFILTER_VERSION_MICRO 100 |
|
35 | 35 |
|
36 | 36 |
#define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \ |
37 | 37 |
LIBAVFILTER_VERSION_MINOR, \ |