+#define BIQUAD_PROCESS(name, type) \
+static void biquad_process_## name(const type *const c, \
+ type *b, \
+ type *dst, const type *src, \
+ int nb_samples) \
+{ \
+ const type b0 = c[B0]; \
+ const type b1 = c[B1]; \
+ const type b2 = c[B2]; \
+ const type a1 = c[A1]; \
+ const type a2 = c[A2]; \
+ type z1 = b[0]; \
+ type z2 = b[1]; \
+ \
+ for (int n = 0; n + 1 < nb_samples; n++) { \
+ type in = src[n]; \
+ type out; \
+ \
+ out = in * b0 + z1; \
+ z1 = b1 * in + z2 + a1 * out; \
+ z2 = b2 * in + a2 * out; \
+ dst[n] = out; \
+ \
+ n++; \
+ in = src[n]; \
+ out = in * b0 + z1; \
+ z1 = b1 * in + z2 + a1 * out; \
+ z2 = b2 * in + a2 * out; \
+ dst[n] = out; \
+ } \
+ \
+ if (nb_samples & 1) { \
+ const int n = nb_samples - 1; \
+ const type in = src[n]; \
+ type out; \
+ \
+ out = in * b0 + z1; \
+ z1 = b1 * in + z2 + a1 * out; \
+ z2 = b2 * in + a2 * out; \
+ dst[n] = out; \
+ } \
+ \
+ b[0] = z1; \
+ b[1] = z2; \
+}
+
+BIQUAD_PROCESS(fltp, float)
+BIQUAD_PROCESS(dblp, double)
+
+#define XOVER_PROCESS(name, type, one, ff) \
+static int filter_channels_## name(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) \
+{ \
+ AudioCrossoverContext *s = ctx->priv; \
+ AVFrame *in = s->input_frame; \
+ AVFrame **frames = s->frames; \
+ const int start = (in->channels * jobnr) / nb_jobs; \
+ const int end = (in->channels * (jobnr+1)) / nb_jobs; \
+ const int nb_samples = in->nb_samples; \
+ const int nb_outs = ctx->nb_outputs; \
+ const int first_order = s->first_order; \
+ \
+ for (int ch = start; ch < end; ch++) { \
+ const type *src = (const type *)in->extended_data[ch]; \
+ type *xover = (type *)s->xover->extended_data[ch]; \
+ \
+ s->fdsp->vector_## ff ##mul_scalar((type *)frames[0]->extended_data[ch], src, \
+ s->level_in, FFALIGN(nb_samples, sizeof(type))); \
+ \
+ for (int band = 0; band < nb_outs; band++) { \
+ for (int f = 0; band + 1 < nb_outs && f < s->filter_count; f++) { \
+ const type *prv = (const type *)frames[band]->extended_data[ch]; \
+ type *dst = (type *)frames[band + 1]->extended_data[ch]; \
+ const type *hsrc = f == 0 ? prv : dst; \
+ type *hp = xover + nb_outs * 20 + band * 20 + f * 2; \
+ const type *const hpc = (type *)&s->hp[band][f].c ## ff; \
+ \
+ biquad_process_## name(hpc, hp, dst, hsrc, nb_samples); \
+ } \
+ \
+ for (int f = 0; band + 1 < nb_outs && f < s->filter_count; f++) { \
+ type *dst = (type *)frames[band]->extended_data[ch]; \
+ const type *lsrc = dst; \
+ type *lp = xover + band * 20 + f * 2; \
+ const type *const lpc = (type *)&s->lp[band][f].c ## ff; \
+ \
+ biquad_process_## name(lpc, lp, dst, lsrc, nb_samples); \
+ } \
+ \
+ for (int aband = band + 1; aband + 1 < nb_outs; aband++) { \
+ if (first_order) { \
+ const type *asrc = (const type *)frames[band]->extended_data[ch]; \
+ type *dst = (type *)frames[band]->extended_data[ch]; \
+ type *ap = xover + nb_outs * 40 + (aband * nb_outs + band) * 20; \
+ const type *const apc = (type *)&s->ap[aband][0].c ## ff; \
+ \
+ biquad_process_## name(apc, ap, dst, asrc, nb_samples); \
+ } \
+ \
+ for (int f = first_order; f < s->ap_filter_count; f++) { \
+ const type *asrc = (const type *)frames[band]->extended_data[ch]; \
+ type *dst = (type *)frames[band]->extended_data[ch]; \
+ type *ap = xover + nb_outs * 40 + (aband * nb_outs + band) * 20 + f * 2;\
+ const type *const apc = (type *)&s->ap[aband][f].c ## ff; \
+ \
+ biquad_process_## name(apc, ap, dst, asrc, nb_samples); \
+ } \
+ } \
+ } \
+ \
+ for (int band = 0; band < nb_outs; band++) { \
+ const type gain = s->gains[band] * ((band & 1 && first_order) ? -one : one); \
+ type *dst = (type *)frames[band]->extended_data[ch]; \
+ \
+ s->fdsp->vector_## ff ##mul_scalar(dst, dst, gain, \
+ FFALIGN(nb_samples, sizeof(type))); \
+ } \
+ } \
+ \
+ return 0; \
+}
+
+XOVER_PROCESS(fltp, float, 1.f, f)
+XOVER_PROCESS(dblp, double, 1.0, d)
+
+static int config_input(AVFilterLink *inlink)