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)
+
static void count_coefficients(char *item_str, int *nb_items)
{
char *p;
return 0;
}
-static const char *format[] = { "%lf", "%lf %lfi", "%lf %lfr", "%lf %lfd", "%lf %lfi" };
+static const char *const format[] = { "%lf", "%lf %lfi", "%lf %lfr", "%lf %lfd", "%lf %lfi" };
static int read_channels(AVFilterContext *ctx, int channels, uint8_t *item_str, int ab)
{
return i * fact(i - 1.);
}
-static double coef_sf2zf(double *a, int N, int n, double fs)
+static double coef_sf2zf(double *a, int N, int n)
{
double z = 0.;
for (int k = FFMAX(n - N + i, 0); k <= FFMIN(i, n); k++) {
acc += ((fact(i) * fact(N - i)) /
(fact(k) * fact(i - k) * fact(n - k) * fact(N - i - n + k))) *
- ((k & 1) ? -1. : 1.);;
+ ((k & 1) ? -1. : 1.);
}
z += a[i] * pow(2., i) * acc;
return z;
}
-static void convert_sf2tf(AVFilterContext *ctx, int channels, int sample_rate)
+static void convert_sf2tf(AVFilterContext *ctx, int channels)
{
AudioIIRContext *s = ctx->priv;
int ch;
memcpy(temp1, iir->ab[1], iir->nb_ab[1] * sizeof(*temp1));
for (int n = 0; n < iir->nb_ab[0]; n++)
- iir->ab[0][n] = coef_sf2zf(temp0, iir->nb_ab[0] - 1, n, sample_rate);
+ iir->ab[0][n] = coef_sf2zf(temp0, iir->nb_ab[0] - 1, n);
for (int n = 0; n < iir->nb_ab[1]; n++)
- iir->ab[1][n] = coef_sf2zf(temp1, iir->nb_ab[1] - 1, n, sample_rate);
+ iir->ab[1][n] = coef_sf2zf(temp1, iir->nb_ab[1] - 1, n);
next:
av_free(temp0);
return ret;
if (s->format == -1) {
- convert_sf2tf(ctx, inlink->channels, inlink->sample_rate);
+ convert_sf2tf(ctx, inlink->channels);
s->format = 0;
} else if (s->format == 2) {
convert_pr2zp(ctx, inlink->channels);
ret = convert_zp2tf(ctx, inlink->channels);
if (ret < 0)
return ret;
+ } else if (s->format == -2 && s->process > 0) {
+ av_log(ctx, AV_LOG_ERROR, "Only direct processing is implemented for lattice-ladder function.\n");
+ return AVERROR_PATCHWELCOME;
} else if (s->format <= 0 && s->process == 1) {
av_log(ctx, AV_LOG_ERROR, "Serial processing is not implemented for transfer function.\n");
return AVERROR_PATCHWELCOME;
return ret;
}
+ for (ch = 0; s->format == -2 && ch < inlink->channels; ch++) {
+ IIRChannel *iir = &s->iir[ch];
+
+ if (iir->nb_ab[0] != iir->nb_ab[1] + 1) {
+ av_log(ctx, AV_LOG_ERROR, "Number of ladder coefficients must be one more than number of reflection coefficients.\n");
+ return AVERROR(EINVAL);
+ }
+ }
+
for (ch = 0; s->format == 0 && ch < inlink->channels; ch++) {
IIRChannel *iir = &s->iir[ch];
case AV_SAMPLE_FMT_S16P: s->iir_channel = s->process == 2 ? iir_ch_parallel_s16p : s->process == 1 ? iir_ch_serial_s16p : iir_ch_s16p; break;
}
+ if (s->format == -2) {
+ switch (inlink->format) {
+ case AV_SAMPLE_FMT_DBLP: s->iir_channel = iir_ch_lattice_dblp; break;
+ case AV_SAMPLE_FMT_FLTP: s->iir_channel = iir_ch_lattice_fltp; break;
+ case AV_SAMPLE_FMT_S32P: s->iir_channel = iir_ch_lattice_s32p; break;
+ case AV_SAMPLE_FMT_S16P: s->iir_channel = iir_ch_lattice_s16p; break;
+ }
+ }
+
return 0;
}
#define VF AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
static const AVOption aiir_options[] = {
- { "zeros", "set B/numerator/zeros coefficients", OFFSET(b_str), AV_OPT_TYPE_STRING, {.str="1+0i 1-0i"}, 0, 0, AF },
- { "z", "set B/numerator/zeros coefficients", OFFSET(b_str), AV_OPT_TYPE_STRING, {.str="1+0i 1-0i"}, 0, 0, AF },
- { "poles", "set A/denominator/poles coefficients", OFFSET(a_str),AV_OPT_TYPE_STRING, {.str="1+0i 1-0i"}, 0, 0, AF },
- { "p", "set A/denominator/poles coefficients", OFFSET(a_str), AV_OPT_TYPE_STRING, {.str="1+0i 1-0i"}, 0, 0, AF },
+ { "zeros", "set B/numerator/zeros/reflection coefficients", OFFSET(b_str), AV_OPT_TYPE_STRING, {.str="1+0i 1-0i"}, 0, 0, AF },
+ { "z", "set B/numerator/zeros/reflection coefficients", OFFSET(b_str), AV_OPT_TYPE_STRING, {.str="1+0i 1-0i"}, 0, 0, AF },
+ { "poles", "set A/denominator/poles/ladder coefficients", OFFSET(a_str), AV_OPT_TYPE_STRING, {.str="1+0i 1-0i"}, 0, 0, AF },
+ { "p", "set A/denominator/poles/ladder coefficients", OFFSET(a_str), AV_OPT_TYPE_STRING, {.str="1+0i 1-0i"}, 0, 0, AF },
{ "gains", "set channels gains", OFFSET(g_str), AV_OPT_TYPE_STRING, {.str="1|1"}, 0, 0, AF },
{ "k", "set channels gains", OFFSET(g_str), AV_OPT_TYPE_STRING, {.str="1|1"}, 0, 0, AF },
{ "dry", "set dry gain", OFFSET(dry_gain), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, AF },
{ "wet", "set wet gain", OFFSET(wet_gain), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, AF },
- { "format", "set coefficients format", OFFSET(format), AV_OPT_TYPE_INT, {.i64=1}, -1, 4, AF, "format" },
- { "f", "set coefficients format", OFFSET(format), AV_OPT_TYPE_INT, {.i64=1}, -1, 4, AF, "format" },
+ { "format", "set coefficients format", OFFSET(format), AV_OPT_TYPE_INT, {.i64=1}, -2, 4, AF, "format" },
+ { "f", "set coefficients format", OFFSET(format), AV_OPT_TYPE_INT, {.i64=1}, -2, 4, AF, "format" },
+ { "ll", "lattice-ladder function", 0, AV_OPT_TYPE_CONST, {.i64=-2}, 0, 0, AF, "format" },
{ "sf", "analog transfer function", 0, AV_OPT_TYPE_CONST, {.i64=-1}, 0, 0, AF, "format" },
{ "tf", "digital transfer function", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, AF, "format" },
{ "zp", "Z-plane zeros/poles", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, AF, "format" },
AVFILTER_DEFINE_CLASS(aiir);
-AVFilter ff_af_aiir = {
+const AVFilter ff_af_aiir = {
.name = "aiir",
.description = NULL_IF_CONFIG_SMALL("Apply Infinite Impulse Response filter with supplied coefficients."),
.priv_size = sizeof(AudioIIRContext),