+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; \