+ } else {
+ memmove(taps, taps + in->nb_samples, NUMTAPS * sizeof(*taps));
+ }
+}
+
+static void mix(AVFilterContext *ctx, AVFrame *out,
+ int output_ch, int f0, int f1, int i0, int i1)
+{
+ EarwaxContext *s = ctx->priv;
+ const int16_t *srcl = (const int16_t *)s->frame[f0]->data[i0];
+ const int16_t *srcr = (const int16_t *)s->frame[f1]->data[i1];
+ int16_t *dst = (int16_t *)out->data[output_ch];
+
+ for (int n = 0; n < out->nb_samples; n++)
+ dst[n] = av_clip_int16(srcl[n] + srcr[n]);
+}
+
+static int filter_frame(AVFilterLink *inlink, AVFrame *in)
+{
+ AVFilterContext *ctx = inlink->dst;
+ EarwaxContext *s = ctx->priv;
+ AVFilterLink *outlink = ctx->outputs[0];
+ AVFrame *out = ff_get_audio_buffer(outlink, in->nb_samples);
+
+ for (int ch = 0; ch < 2; ch++) {
+ if (!s->frame[ch] || s->frame[ch]->nb_samples < in->nb_samples) {
+ av_frame_free(&s->frame[ch]);
+ s->frame[ch] = ff_get_audio_buffer(outlink, in->nb_samples);
+ if (!s->frame[ch]) {
+ av_frame_free(&in);
+ av_frame_free(&out);
+ return AVERROR(ENOMEM);
+ }
+ }
+ }
+
+ if (!out) {
+ av_frame_free(&in);
+ return AVERROR(ENOMEM);
+ }
+ av_frame_copy_props(out, in);
+
+ convolve(ctx, in, 0, 0, 0, 0);
+ convolve(ctx, in, 0, 1, 1, 1);
+ convolve(ctx, in, 1, 0, 0, 2);
+ convolve(ctx, in, 1, 1, 1, 3);
+
+ mix(ctx, out, 0, 0, 1, 1, 0);
+ mix(ctx, out, 1, 0, 1, 0, 1);
+
+ av_frame_free(&in);
+ return ff_filter_frame(outlink, out);
+}
+
+static av_cold void uninit(AVFilterContext *ctx)
+{
+ EarwaxContext *s = ctx->priv;