History

avfilter/af_crossfeed: add option for linear phase filtering

But at expense of additional delay.

Paul B Mahol committed 4 years ago

Commit 5ccd8f41 parent 13098672 files · +167 −14Browse files

Showing the changes to libavfilter (1 of 2 files). Show all

Unified · Split

libavfilter/af_crossfeed.c+160−14
@@ -21,6 +21,7 @@ · 20 unmodified lines
21#include "libavutil/opt.h"21#include "libavutil/opt.h"
22#include "avfilter.h"22#include "avfilter.h"
23#include "audio.h"23#include "audio.h"
24#include "filters.h"
24#include "formats.h"25#include "formats.h"
25 26
26typedef struct CrossfeedContext {27typedef struct CrossfeedContext {
@@ -31,11 +32,16 @@ typedef struct CrossfeedContext { · 4 unmodified lines
31 double slope;32 double slope;
32 double level_in;33 double level_in;
33 double level_out;34 double level_out;
35 int block_samples;
36 int block_size;
34 37
35 double a0, a1, a2;38 double a0, a1, a2;
36 double b0, b1, b2;39 double b0, b1, b2;
37 40
38 double w1, w2;41 double w1, w2;
42
43 double *mid;
44 double *side[3];
39} CrossfeedContext;45} CrossfeedContext;
40 46
41static int query_formats(AVFilterContext *ctx)47static int query_formats(AVFilterContext *ctx)
@@ -77,9 +83,49 @@ static int config_input(AVFilterLink *inlink) · 35 unmodified lines
77 s->b1 /= s->a0;83 s->b1 /= s->a0;
78 s->b2 /= s->a0;84 s->b2 /= s->a0;
79 85
86 if (s->block_samples == 0 && s->block_size > 0) {
87 s->block_samples = s->block_size;
88 s->mid = av_calloc(s->block_samples * 2, sizeof(*s->mid));
89 for (int i = 0; i < 3; i++) {
90 s->side[i] = av_calloc(s->block_samples * 2, sizeof(*s->side[0]));
91 if (!s->side[i])
92 return AVERROR(ENOMEM);
93 }
94 }
95
80 return 0;96 return 0;
81}97}
82 98
99static void reverse_samples(double *dst, const double *src,
100 int nb_samples)
101{
102 for (int i = 0, j = nb_samples - 1; i < nb_samples; i++, j--)
103 dst[i] = src[j];
104}
105
106static void filter_samples(double *dst, const double *src,
107 int nb_samples,
108 double b0, double b1, double b2,
109 double a1, double a2,
110 double *sw1, double *sw2)
111{
112 double w1 = *sw1;
113 double w2 = *sw2;
114
115 for (int n = 0; n < nb_samples; n++) {
116 double side = src[n];
117 double oside = side * b0 + w1;
118
119 w1 = b1 * side + w2 + a1 * oside;
120 w2 = b2 * side + a2 * oside;
121
122 dst[n] = oside;
123 }
124
125 *sw1 = w1;
126 *sw2 = w2;
127}
128
83static int filter_frame(AVFilterLink *inlink, AVFrame *in)129static int filter_frame(AVFilterLink *inlink, AVFrame *in)
84{130{
85 AVFilterContext *ctx = inlink->dst;131 AVFilterContext *ctx = inlink->dst;
@@ -95,7 +141,6 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) · 9 unmodified lines
95 const double a2 = -s->a2;141 const double a2 = -s->a2;
96 AVFrame *out;142 AVFrame *out;
97 double *dst;143 double *dst;
98 int n;
99 144
100 if (av_frame_is_writable(in)) {145 if (av_frame_is_writable(in)) {
101 out = in;146 out = in;
@@ -109,21 +154,79 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) · 7 unmodified lines
109 }154 }
110 dst = (double *)out->data[0];155 dst = (double *)out->data[0];
111 156
112 for (n = 0; n < out->nb_samples; n++, src += 2, dst += 2) {157 if (s->block_samples == 0) {
113 double mid = (src[0] + src[1]) * level_in * .5;158 double w1 = s->w1;
114 double side = (src[0] - src[1]) * level_in * .5;159 double w2 = s->w2;
115 double oside = side * b0 + s->w1;160
161 for (int n = 0; n < out->nb_samples; n++, src += 2, dst += 2) {
162 double mid = (src[0] + src[1]) * level_in * .5;
163 double side = (src[0] - src[1]) * level_in * .5;
164 double oside = side * b0 + w1;
165
166 w1 = b1 * side + w2 + a1 * oside;
167 w2 = b2 * side + a2 * oside;
168
169 if (ctx->is_disabled) {
170 dst[0] = src[0];
171 dst[1] = src[1];
172 } else {
173 dst[0] = (mid + oside) * level_out;
174 dst[1] = (mid - oside) * level_out;
175 }
176 }
177
178 s->w1 = w1;
179 s->w2 = w2;
180 } else {
181 double *mdst = s->mid + s->block_samples;
182 double *sdst = s->side[0] + s->block_samples;
183 double *ssrc = s->side[0];
184 double *msrc = s->mid;
185 double w1 = s->w1;
186 double w2 = s->w2;
187
188 for (int n = 0; n < out->nb_samples; n++, src += 2) {
189 mdst[n] = (src[0] + src[1]) * level_in * .5;
190 sdst[n] = (src[0] - src[1]) * level_in * .5;
191 }
192
193 sdst = s->side[1];
194 filter_samples(sdst, ssrc, s->block_samples,
195 b0, b1, b2, a1, a2,
196 &w1, &w2);
197 s->w1 = w1;
198 s->w2 = w2;
116 199
117 s->w1 = b1 * side + s->w2 + a1 * oside;200 ssrc = s->side[0] + s->block_samples;
118 s->w2 = b2 * side + a2 * oside;201 sdst = s->side[1] + s->block_samples;
202 filter_samples(sdst, ssrc, s->block_samples,
203 b0, b1, b2, a1, a2,
204 &w1, &w2);
119 205
120 if (ctx->is_disabled) {206 reverse_samples(s->side[2], s->side[1], s->block_samples * 2);
121 dst[0] = src[0];207 w1 = w2 = 0.;
122 dst[1] = src[1];208 filter_samples(s->side[2], s->side[2], s->block_samples * 2,
123 } else {209 b0, b1, b2, a1, a2,
124 dst[0] = (mid + oside) * level_out;210 &w1, &w2);
125 dst[1] = (mid - oside) * level_out;211
212 reverse_samples(s->side[1], s->side[2] + s->block_samples, s->block_samples);
213
214 src = (const double *)in->data[0];
215 ssrc = s->side[1];
216 for (int n = 0; n < out->nb_samples; n++, src += 2, dst += 2) {
217 if (ctx->is_disabled) {
218 dst[0] = src[0];
219 dst[1] = src[1];
220 } else {
221 dst[0] = (msrc[n] + ssrc[n]) * level_out;
222 dst[1] = (msrc[n] - ssrc[n]) * level_out;
223 }
126 }224 }
225
226 memmove(s->mid, s->mid + s->block_samples,
227 s->block_samples * sizeof(*s->mid));
228 memmove(s->side[0], s->side[0] + s->block_samples,
229 s->block_samples * sizeof(*s->side[0]));
127 }230 }
128 231
129 if (out != in)232 if (out != in)
@@ -131,6 +234,37 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) · 1 unmodified line
131 return ff_filter_frame(outlink, out);234 return ff_filter_frame(outlink, out);
132}235}
133 236
237static int activate(AVFilterContext *ctx)
238{
239 AVFilterLink *inlink = ctx->inputs[0];
240 AVFilterLink *outlink = ctx->outputs[0];
241 CrossfeedContext *s = ctx->priv;
242 AVFrame *in = NULL;
243 int ret;
244
245 FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink);
246
247 if (s->block_samples > 0) {
248 ret = ff_inlink_consume_samples(inlink, s->block_samples, s->block_samples, &in);
249 } else {
250 ret = ff_inlink_consume_frame(inlink, &in);
251 }
252 if (ret < 0)
253 return ret;
254 if (ret > 0)
255 return filter_frame(inlink, in);
256
257 if (s->block_samples > 0 && ff_inlink_queued_samples(inlink) >= s->block_samples) {
258 ff_filter_set_ready(ctx, 10);
259 return 0;
260 }
261
262 FF_FILTER_FORWARD_STATUS(inlink, outlink);
263 FF_FILTER_FORWARD_WANTED(outlink, inlink);
264
265 return FFERROR_NOT_READY;
266}
267
134static int process_command(AVFilterContext *ctx, const char *cmd, const char *args,268static int process_command(AVFilterContext *ctx, const char *cmd, const char *args,
135 char *res, int res_len, int flags)269 char *res, int res_len, int flags)
136{270{
@@ -143,8 +277,18 @@ static int process_command(AVFilterContext *ctx, const char *cmd, const char *ar · 6 unmodified lines
143 return config_input(ctx->inputs[0]);277 return config_input(ctx->inputs[0]);
144}278}
145 279
280static av_cold void uninit(AVFilterContext *ctx)
281{
282 CrossfeedContext *s = ctx->priv;
283
284 av_freep(&s->mid);
285 for (int i = 0; i < 3; i++)
286 av_freep(&s->side[i]);
287}
288
146#define OFFSET(x) offsetof(CrossfeedContext, x)289#define OFFSET(x) offsetof(CrossfeedContext, x)
147#define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM290#define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM
291#define AF AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
148 292
149static const AVOption crossfeed_options[] = {293static const AVOption crossfeed_options[] = {
150 { "strength", "set crossfeed strength", OFFSET(strength), AV_OPT_TYPE_DOUBLE, {.dbl=.2}, 0, 1, FLAGS },294 { "strength", "set crossfeed strength", OFFSET(strength), AV_OPT_TYPE_DOUBLE, {.dbl=.2}, 0, 1, FLAGS },
@@ -152,6 +296,7 @@ static const AVOption crossfeed_options[] = { · 1 unmodified line
152 { "slope", "set curve slope", OFFSET(slope), AV_OPT_TYPE_DOUBLE, {.dbl=.5}, .01, 1, FLAGS },296 { "slope", "set curve slope", OFFSET(slope), AV_OPT_TYPE_DOUBLE, {.dbl=.5}, .01, 1, FLAGS },
153 { "level_in", "set level in", OFFSET(level_in), AV_OPT_TYPE_DOUBLE, {.dbl=.9}, 0, 1, FLAGS },297 { "level_in", "set level in", OFFSET(level_in), AV_OPT_TYPE_DOUBLE, {.dbl=.9}, 0, 1, FLAGS },
154 { "level_out", "set level out", OFFSET(level_out), AV_OPT_TYPE_DOUBLE, {.dbl=1.}, 0, 1, FLAGS },298 { "level_out", "set level out", OFFSET(level_out), AV_OPT_TYPE_DOUBLE, {.dbl=1.}, 0, 1, FLAGS },
299 { "block_size", "set the block size", OFFSET(block_size),AV_OPT_TYPE_INT, {.i64=0}, 0, 32768, AF },
155 { NULL }300 { NULL }
156};301};
157 302
@@ -161,7 +306,6 @@ static const AVFilterPad inputs[] = { · 3 unmodified lines
161 {306 {
162 .name = "default",307 .name = "default",
163 .type = AVMEDIA_TYPE_AUDIO,308 .type = AVMEDIA_TYPE_AUDIO,
164 .filter_frame = filter_frame,
165 .config_props = config_input,309 .config_props = config_input,
166 },310 },
167};311};
@@ -178,6 +322,8 @@ const AVFilter ff_af_crossfeed = { · 10 unmodified lines
178 .description = NULL_IF_CONFIG_SMALL("Apply headphone crossfeed filter."),322 .description = NULL_IF_CONFIG_SMALL("Apply headphone crossfeed filter."),
179 .priv_size = sizeof(CrossfeedContext),323 .priv_size = sizeof(CrossfeedContext),
180 .priv_class = &crossfeed_class,324 .priv_class = &crossfeed_class,
325 .activate = activate,
326 .uninit = uninit,
181 FILTER_INPUTS(inputs),327 FILTER_INPUTS(inputs),
182 FILTER_OUTPUTS(outputs),328 FILTER_OUTPUTS(outputs),
183 FILTER_QUERY_FUNC(query_formats),329 FILTER_QUERY_FUNC(query_formats),

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t