+#define PARALLEL_IIR_CH(name, type, min, max, need_clipping) \
+static int iir_ch_parallel_## name(AVFilterContext *ctx, void *arg, \
+ int ch, int nb_jobs) \
+{ \
+ AudioIIRContext *s = ctx->priv; \
+ const double ig = s->dry_gain; \
+ const double og = s->wet_gain; \
+ const double mix = s->mix; \
+ const double imix = 1. - mix; \
+ ThreadData *td = arg; \
+ AVFrame *in = td->in, *out = td->out; \
+ const type *src = (const type *)in->extended_data[ch]; \
+ type *dst = (type *)out->extended_data[ch]; \
+ IIRChannel *iir = &s->iir[ch]; \
+ const double g = iir->g; \
+ const double fir = iir->fir; \
+ int *clippings = &iir->clippings; \
+ int nb_biquads = (FFMAX(iir->nb_ab[0], iir->nb_ab[1]) + 1) / 2; \
+ int n, i; \
+ \
+ for (i = 0; i < nb_biquads; i++) { \
+ const double a1 = -iir->biquads[i].a[1]; \
+ const double a2 = -iir->biquads[i].a[2]; \
+ const double b1 = iir->biquads[i].b[1]; \
+ const double b2 = iir->biquads[i].b[2]; \
+ double w1 = iir->biquads[i].w1; \
+ double w2 = iir->biquads[i].w2; \
+ \
+ for (n = 0; n < in->nb_samples; n++) { \
+ double i0 = ig * src[n]; \
+ double o0 = w1; \
+ \
+ w1 = b1 * i0 + w2 + a1 * o0; \
+ w2 = b2 * i0 + a2 * o0; \
+ o0 *= og * g; \
+ o0 += dst[n]; \
+ \
+ if (need_clipping && o0 < min) { \
+ (*clippings)++; \
+ dst[n] = min; \
+ } else if (need_clipping && o0 > max) { \
+ (*clippings)++; \
+ dst[n] = max; \
+ } else { \
+ dst[n] = o0; \
+ } \
+ } \
+ iir->biquads[i].w1 = w1; \
+ iir->biquads[i].w2 = w2; \
+ } \
+ \
+ for (n = 0; n < in->nb_samples; n++) { \
+ dst[n] += fir * src[n]; \
+ dst[n] = dst[n] * mix + imix * src[n]; \
+ } \
+ \
+ return 0; \
+}
+
+PARALLEL_IIR_CH(s16p, int16_t, INT16_MIN, INT16_MAX, 1)
+PARALLEL_IIR_CH(s32p, int32_t, INT32_MIN, INT32_MAX, 1)
+PARALLEL_IIR_CH(fltp, float, -1., 1., 0)
+PARALLEL_IIR_CH(dblp, double, -1., 1., 0)
+
+#define LATTICE_IIR_CH(name, type, min, max, need_clipping) \
+static int iir_ch_lattice_## name(AVFilterContext *ctx, void *arg, \
+ int ch, int nb_jobs) \
+{ \
+ AudioIIRContext *s = ctx->priv; \
+ const double ig = s->dry_gain; \
+ const double og = s->wet_gain; \
+ const double mix = s->mix; \
+ ThreadData *td = arg; \
+ AVFrame *in = td->in, *out = td->out; \
+ const type *src = (const type *)in->extended_data[ch]; \
+ double n0, n1, p0, *x = (double *)s->iir[ch].cache[0]; \
+ const int nb_stages = s->iir[ch].nb_ab[1]; \
+ const double *v = s->iir[ch].ab[0]; \
+ const double *k = s->iir[ch].ab[1]; \
+ const double g = s->iir[ch].g; \
+ int *clippings = &s->iir[ch].clippings; \
+ type *dst = (type *)out->extended_data[ch]; \
+ int n; \
+ \
+ for (n = 0; n < in->nb_samples; n++) { \
+ const double in = src[n] * ig; \
+ double out = 0.; \
+ \
+ n1 = in; \
+ for (int i = nb_stages - 1; i >= 0; i--) { \
+ n0 = n1 - k[i] * x[i]; \
+ p0 = n0 * k[i] + x[i]; \
+ out += p0 * v[i+1]; \
+ x[i] = p0; \
+ n1 = n0; \
+ } \
+ \
+ out += n1 * v[0]; \
+ memmove(&x[1], &x[0], nb_stages * sizeof(*x)); \
+ x[0] = n1; \
+ out *= og * g; \
+ out = out * mix + in * (1. - mix); \
+ if (need_clipping && out < min) { \
+ (*clippings)++; \
+ dst[n] = min; \
+ } else if (need_clipping && out > max) { \
+ (*clippings)++; \
+ dst[n] = max; \
+ } else { \
+ dst[n] = out; \
+ } \
+ } \
+ \
+ return 0; \
+}
+
+LATTICE_IIR_CH(s16p, int16_t, INT16_MIN, INT16_MAX, 1)
+LATTICE_IIR_CH(s32p, int32_t, INT32_MIN, INT32_MAX, 1)
+LATTICE_IIR_CH(fltp, float, -1., 1., 0)
+LATTICE_IIR_CH(dblp, double, -1., 1., 0)
+