]> git.sesse.net Git - ffmpeg/blobdiff - libavfilter/af_biquads.c
avfilter/af_acrossover: reduce memory usage
[ffmpeg] / libavfilter / af_biquads.c
index 247a47256fc77a6134a996804e859037b3afc4fa..3ec26d2a9d7e52ab9032b22be3f92f49c339ecf0 100644 (file)
@@ -93,6 +93,14 @@ enum WidthType {
     NB_WTYPE,
 };
 
+enum TransformType {
+    DI,
+    DII,
+    TDII,
+    LATT,
+    NB_TTYPE,
+};
+
 typedef struct ChanCache {
     double i1, i2;
     double o1, o2;
@@ -106,16 +114,24 @@ typedef struct BiquadsContext {
     int width_type;
     int poles;
     int csg;
+    int transform_type;
+
+    int bypass;
 
     double gain;
     double frequency;
     double width;
     double mix;
     uint64_t channels;
+    int normalize;
+    int order;
 
     double a0, a1, a2;
     double b0, b1, b2;
 
+    double oa0, oa1, oa2;
+    double ob0, ob1, ob2;
+
     ChanCache *cache;
     int block_align;
 
@@ -125,21 +141,6 @@ typedef struct BiquadsContext {
                    int disabled);
 } BiquadsContext;
 
-static av_cold int init(AVFilterContext *ctx)
-{
-    BiquadsContext *s = ctx->priv;
-
-    if (s->filter_type != biquad) {
-        if (s->frequency <= 0 || s->width <= 0) {
-            av_log(ctx, AV_LOG_ERROR, "Invalid frequency %f and/or width %f <= 0\n",
-                   s->frequency, s->width);
-            return AVERROR(EINVAL);
-        }
-    }
-
-    return 0;
-}
-
 static int query_formats(AVFilterContext *ctx)
 {
     AVFilterFormats *formats;
@@ -256,6 +257,173 @@ BIQUAD_FILTER(s32, int32_t, INT32_MIN, INT32_MAX, 1)
 BIQUAD_FILTER(flt, float,   -1., 1., 0)
 BIQUAD_FILTER(dbl, double,  -1., 1., 0)
 
+#define BIQUAD_DII_FILTER(name, type, min, max, need_clipping)                \
+static void biquad_dii_## name (BiquadsContext *s,                            \
+                            const void *input, void *output, int len,         \
+                            double *z1, double *z2,                           \
+                            double *unused1, double *unused2,                 \
+                            double b0, double b1, double b2,                  \
+                            double a1, double a2, int *clippings,             \
+                            int disabled)                                     \
+{                                                                             \
+    const type *ibuf = input;                                                 \
+    type *obuf = output;                                                      \
+    double w1 = *z1;                                                          \
+    double w2 = *z2;                                                          \
+    double wet = s->mix;                                                      \
+    double dry = 1. - wet;                                                    \
+    double in, out, w0;                                                       \
+                                                                              \
+    a1 = -a1;                                                                 \
+    a2 = -a2;                                                                 \
+                                                                              \
+    for (int i = 0; i < len; i++) {                                           \
+        in = ibuf[i];                                                         \
+        w0 = in + a1 * w1 + a2 * w2;                                          \
+        out = b0 * w0 + b1 * w1 + b2 * w2;                                    \
+        w2 = w1;                                                              \
+        w1 = w0;                                                              \
+        out = out * wet + in * dry;                                           \
+        if (disabled) {                                                       \
+            obuf[i] = in;                                                     \
+        } else if (need_clipping && out < min) {                              \
+            (*clippings)++;                                                   \
+            obuf[i] = min;                                                    \
+        } else if (need_clipping && out > max) {                              \
+            (*clippings)++;                                                   \
+            obuf[i] = max;                                                    \
+        } else {                                                              \
+            obuf[i] = out;                                                    \
+        }                                                                     \
+    }                                                                         \
+    *z1 = w1;                                                                 \
+    *z2 = w2;                                                                 \
+}
+
+BIQUAD_DII_FILTER(s16, int16_t, INT16_MIN, INT16_MAX, 1)
+BIQUAD_DII_FILTER(s32, int32_t, INT32_MIN, INT32_MAX, 1)
+BIQUAD_DII_FILTER(flt, float,   -1., 1., 0)
+BIQUAD_DII_FILTER(dbl, double,  -1., 1., 0)
+
+#define BIQUAD_TDII_FILTER(name, type, min, max, need_clipping)               \
+static void biquad_tdii_## name (BiquadsContext *s,                           \
+                            const void *input, void *output, int len,         \
+                            double *z1, double *z2,                           \
+                            double *unused1, double *unused2,                 \
+                            double b0, double b1, double b2,                  \
+                            double a1, double a2, int *clippings,             \
+                            int disabled)                                     \
+{                                                                             \
+    const type *ibuf = input;                                                 \
+    type *obuf = output;                                                      \
+    double w1 = *z1;                                                          \
+    double w2 = *z2;                                                          \
+    double wet = s->mix;                                                      \
+    double dry = 1. - wet;                                                    \
+    double in, out;                                                           \
+                                                                              \
+    a1 = -a1;                                                                 \
+    a2 = -a2;                                                                 \
+                                                                              \
+    for (int i = 0; i < len; i++) {                                           \
+        in = ibuf[i];                                                         \
+        out = b0 * in + w1;                                                   \
+        w1 = b1 * in + w2 + a1 * out;                                         \
+        w2 = b2 * in + a2 * out;                                              \
+        out = out * wet + in * dry;                                           \
+        if (disabled) {                                                       \
+            obuf[i] = in;                                                     \
+        } else if (need_clipping && out < min) {                              \
+            (*clippings)++;                                                   \
+            obuf[i] = min;                                                    \
+        } else if (need_clipping && out > max) {                              \
+            (*clippings)++;                                                   \
+            obuf[i] = max;                                                    \
+        } else {                                                              \
+            obuf[i] = out;                                                    \
+        }                                                                     \
+    }                                                                         \
+    *z1 = w1;                                                                 \
+    *z2 = w2;                                                                 \
+}
+
+BIQUAD_TDII_FILTER(s16, int16_t, INT16_MIN, INT16_MAX, 1)
+BIQUAD_TDII_FILTER(s32, int32_t, INT32_MIN, INT32_MAX, 1)
+BIQUAD_TDII_FILTER(flt, float,   -1., 1., 0)
+BIQUAD_TDII_FILTER(dbl, double,  -1., 1., 0)
+
+#define BIQUAD_LATT_FILTER(name, type, min, max, need_clipping)               \
+static void biquad_latt_## name (BiquadsContext *s,                           \
+                           const void *input, void *output, int len,          \
+                           double *z1, double *z2,                            \
+                           double *unused1, double *unused2,                  \
+                           double v0, double v1, double v2,                   \
+                           double k0, double k1, int *clippings,              \
+                           int disabled)                                      \
+{                                                                             \
+    const type *ibuf = input;                                                 \
+    type *obuf = output;                                                      \
+    double s0 = *z1;                                                          \
+    double s1 = *z2;                                                          \
+    double wet = s->mix;                                                      \
+    double dry = 1. - wet;                                                    \
+    double in, out;                                                           \
+    double t0, t1;                                                            \
+                                                                              \
+    for (int i = 0; i < len; i++) {                                           \
+        out  = 0.;                                                            \
+        in   = ibuf[i];                                                       \
+        t0   = in - k1 * s0;                                                  \
+        t1   = t0 * k1 + s0;                                                  \
+        out += t1 * v2;                                                       \
+                                                                              \
+        t0    = t0 - k0 * s1;                                                 \
+        t1    = t0 * k0 + s1;                                                 \
+        out  += t1 * v1;                                                      \
+                                                                              \
+        out  += t0 * v0;                                                      \
+        s0    = t1;                                                           \
+        s1    = t0;                                                           \
+                                                                              \
+        out = out * wet + in * dry;                                           \
+        if (disabled) {                                                       \
+            obuf[i] = in;                                                     \
+        } else if (need_clipping && out < min) {                              \
+            (*clippings)++;                                                   \
+            obuf[i] = min;                                                    \
+        } else if (need_clipping && out > max) {                              \
+            (*clippings)++;                                                   \
+            obuf[i] = max;                                                    \
+        } else {                                                              \
+            obuf[i] = out;                                                    \
+        }                                                                     \
+    }                                                                         \
+    *z1 = s0;                                                                 \
+    *z2 = s1;                                                                 \
+}
+
+BIQUAD_LATT_FILTER(s16, int16_t, INT16_MIN, INT16_MAX, 1)
+BIQUAD_LATT_FILTER(s32, int32_t, INT32_MIN, INT32_MAX, 1)
+BIQUAD_LATT_FILTER(flt, float,   -1., 1., 0)
+BIQUAD_LATT_FILTER(dbl, double,  -1., 1., 0)
+
+static void convert_dir2latt(BiquadsContext *s)
+{
+    double k0, k1, v0, v1, v2;
+
+    k1 = s->a2;
+    k0 = s->a1 / (1. + k1);
+    v2 = s->b2;
+    v1 = s->b1 - v2 * s->a1;
+    v0 = s->b0 - v1 * k0 - v2 * k1;
+
+    s->a1 = k0;
+    s->a2 = k1;
+    s->b0 = v0;
+    s->b1 = v1;
+    s->b2 = v2;
+}
+
 static int config_filter(AVFilterLink *outlink, int reset)
 {
     AVFilterContext *ctx    = outlink->src;
@@ -263,15 +431,18 @@ static int config_filter(AVFilterLink *outlink, int reset)
     AVFilterLink *inlink    = ctx->inputs[0];
     double A = ff_exp10(s->gain / 40);
     double w0 = 2 * M_PI * s->frequency / inlink->sample_rate;
+    double K = tan(w0 / 2.);
     double alpha, beta;
 
-    if (w0 > M_PI) {
-        av_log(ctx, AV_LOG_ERROR,
-               "Invalid frequency %f. Frequency must be less than half the sample-rate %d.\n",
-               s->frequency, inlink->sample_rate);
-        return AVERROR(EINVAL);
+    s->bypass = (((w0 > M_PI || w0 <= 0.) && reset) || (s->width <= 0.)) && (s->filter_type != biquad);
+    if (s->bypass) {
+        av_log(ctx, AV_LOG_WARNING, "Invalid frequency and/or width!\n");
+        return 0;
     }
 
+    if ((w0 > M_PI || w0 <= 0.) && (s->filter_type != biquad))
+        return AVERROR(EINVAL);
+
     switch (s->width_type) {
     case NONE:
         alpha = 0.0;
@@ -299,6 +470,12 @@ static int config_filter(AVFilterLink *outlink, int reset)
 
     switch (s->filter_type) {
     case biquad:
+        s->a0 = s->oa0;
+        s->a1 = s->oa1;
+        s->a2 = s->oa2;
+        s->b0 = s->ob0;
+        s->b1 = s->ob1;
+        s->b2 = s->ob2;
         break;
     case equalizer:
         s->a0 =   1 + alpha / A;
@@ -388,12 +565,24 @@ static int config_filter(AVFilterLink *outlink, int reset)
         }
         break;
     case allpass:
-        s->a0 =  1 + alpha;
-        s->a1 = -2 * cos(w0);
-        s->a2 =  1 - alpha;
-        s->b0 =  1 - alpha;
-        s->b1 = -2 * cos(w0);
-        s->b2 =  1 + alpha;
+        switch (s->order) {
+        case 1:
+            s->a0 = 1.;
+            s->a1 = -(1. - K) / (1. + K);
+            s->a2 = 0.;
+            s->b0 = s->a1;
+            s->b1 = s->a0;
+            s->b2 = 0.;
+            break;
+        case 2:
+            s->a0 =  1 + alpha;
+            s->a1 = -2 * cos(w0);
+            s->a2 =  1 - alpha;
+            s->b0 =  1 - alpha;
+            s->b1 = -2 * cos(w0);
+            s->b2 =  1 + alpha;
+        break;
+        }
         break;
     default:
         av_assert0(0);
@@ -408,21 +597,97 @@ static int config_filter(AVFilterLink *outlink, int reset)
     s->b2 /= s->a0;
     s->a0 /= s->a0;
 
+    if (s->normalize && fabs(s->b0 + s->b1 + s->b2) > 1e-6) {
+        double factor = (s->a0 + s->a1 + s->a2) / (s->b0 + s->b1 + s->b2);
+
+        s->b0 *= factor;
+        s->b1 *= factor;
+        s->b2 *= factor;
+    }
+
     s->cache = av_realloc_f(s->cache, sizeof(ChanCache), inlink->channels);
     if (!s->cache)
         return AVERROR(ENOMEM);
     if (reset)
         memset(s->cache, 0, sizeof(ChanCache) * inlink->channels);
 
-    switch (inlink->format) {
-    case AV_SAMPLE_FMT_S16P: s->filter = biquad_s16; break;
-    case AV_SAMPLE_FMT_S32P: s->filter = biquad_s32; break;
-    case AV_SAMPLE_FMT_FLTP: s->filter = biquad_flt; break;
-    case AV_SAMPLE_FMT_DBLP: s->filter = biquad_dbl; break;
-    default: av_assert0(0);
-    }
+    switch (s->transform_type) {
+    case DI:
+        switch (inlink->format) {
+        case AV_SAMPLE_FMT_S16P:
+            s->filter = biquad_s16;
+            break;
+        case AV_SAMPLE_FMT_S32P:
+            s->filter = biquad_s32;
+            break;
+        case AV_SAMPLE_FMT_FLTP:
+            s->filter = biquad_flt;
+            break;
+        case AV_SAMPLE_FMT_DBLP:
+            s->filter = biquad_dbl;
+            break;
+        default: av_assert0(0);
+        }
+        break;
+    case DII:
+        switch (inlink->format) {
+        case AV_SAMPLE_FMT_S16P:
+            s->filter = biquad_dii_s16;
+            break;
+        case AV_SAMPLE_FMT_S32P:
+            s->filter = biquad_dii_s32;
+            break;
+        case AV_SAMPLE_FMT_FLTP:
+            s->filter = biquad_dii_flt;
+            break;
+        case AV_SAMPLE_FMT_DBLP:
+            s->filter = biquad_dii_dbl;
+            break;
+        default: av_assert0(0);
+        }
+        break;
+    case TDII:
+        switch (inlink->format) {
+        case AV_SAMPLE_FMT_S16P:
+            s->filter = biquad_tdii_s16;
+            break;
+        case AV_SAMPLE_FMT_S32P:
+            s->filter = biquad_tdii_s32;
+            break;
+        case AV_SAMPLE_FMT_FLTP:
+            s->filter = biquad_tdii_flt;
+            break;
+        case AV_SAMPLE_FMT_DBLP:
+            s->filter = biquad_tdii_dbl;
+            break;
+        default: av_assert0(0);
+        }
+        break;
+    case LATT:
+        switch (inlink->format) {
+        case AV_SAMPLE_FMT_S16P:
+            s->filter = biquad_latt_s16;
+            break;
+        case AV_SAMPLE_FMT_S32P:
+            s->filter = biquad_latt_s32;
+            break;
+        case AV_SAMPLE_FMT_FLTP:
+            s->filter = biquad_latt_flt;
+            break;
+        case AV_SAMPLE_FMT_DBLP:
+            s->filter = biquad_latt_dbl;
+            break;
+        default: av_assert0(0);
+        }
+        break;
+    default:
+        av_assert0(0);
+     }
+
+     s->block_align = av_get_bytes_per_sample(inlink->format);
 
-    s->block_align = av_get_bytes_per_sample(inlink->format);
+     if (s->transform_type == LATT)
+         convert_dir2latt(s);
 
     return 0;
 }
@@ -472,6 +737,9 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *buf)
     ThreadData td;
     int ch;
 
+    if (s->bypass)
+        return ff_filter_frame(outlink, buf);
+
     if (av_frame_is_writable(buf)) {
         out_buf = buf;
     } else {
@@ -503,127 +771,12 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *buf)
 static int process_command(AVFilterContext *ctx, const char *cmd, const char *args,
                            char *res, int res_len, int flags)
 {
-    BiquadsContext *s = ctx->priv;
     AVFilterLink *outlink = ctx->outputs[0];
+    int ret;
 
-    if ((!strcmp(cmd, "frequency") || !strcmp(cmd, "f")) &&
-        (s->filter_type == equalizer ||
-         s->filter_type == lowshelf  ||
-         s->filter_type == highshelf ||
-         s->filter_type == bass      ||
-         s->filter_type == treble    ||
-         s->filter_type == bandpass  ||
-         s->filter_type == bandreject||
-         s->filter_type == lowpass   ||
-         s->filter_type == highpass  ||
-         s->filter_type == allpass)) {
-        double freq;
-
-        if (sscanf(args, "%lf", &freq) != 1) {
-            av_log(ctx, AV_LOG_ERROR, "Invalid frequency value.\n");
-            return AVERROR(EINVAL);
-        }
-
-        s->frequency = freq;
-    } else if ((!strcmp(cmd, "gain") || !strcmp(cmd, "g")) &&
-        (s->filter_type == equalizer ||
-         s->filter_type == lowshelf  ||
-         s->filter_type == highshelf ||
-         s->filter_type == bass      ||
-         s->filter_type == treble)) {
-        double gain;
-
-        if (sscanf(args, "%lf", &gain) != 1) {
-            av_log(ctx, AV_LOG_ERROR, "Invalid gain value.\n");
-            return AVERROR(EINVAL);
-        }
-
-        s->gain = av_clipd(gain, -900, 900);
-    } else if (!strcmp(cmd, "mix") || !strcmp(cmd, "m")) {
-        double mix;
-
-        if (sscanf(args, "%lf", &mix) != 1) {
-            av_log(ctx, AV_LOG_ERROR, "Invalid mix value.\n");
-            return AVERROR(EINVAL);
-        }
-
-        s->mix = av_clipd(mix, 0, 1);
-    } else if ((!strcmp(cmd, "width") || !strcmp(cmd, "w")) &&
-        (s->filter_type == equalizer ||
-         s->filter_type == lowshelf  ||
-         s->filter_type == highshelf ||
-         s->filter_type == bass      ||
-         s->filter_type == treble    ||
-         s->filter_type == bandpass  ||
-         s->filter_type == bandreject||
-         s->filter_type == lowpass   ||
-         s->filter_type == highpass  ||
-         s->filter_type == allpass)) {
-        double width;
-
-        if (sscanf(args, "%lf", &width) != 1) {
-            av_log(ctx, AV_LOG_ERROR, "Invalid width value.\n");
-            return AVERROR(EINVAL);
-        }
-
-        s->width = width;
-    } else if ((!strcmp(cmd, "width_type") || !strcmp(cmd, "t")) &&
-        (s->filter_type == equalizer ||
-         s->filter_type == lowshelf  ||
-         s->filter_type == highshelf ||
-         s->filter_type == bass      ||
-         s->filter_type == treble    ||
-         s->filter_type == bandpass  ||
-         s->filter_type == bandreject||
-         s->filter_type == lowpass   ||
-         s->filter_type == highpass  ||
-         s->filter_type == allpass)) {
-        char width_type;
-
-        if (sscanf(args, "%c", &width_type) != 1) {
-            av_log(ctx, AV_LOG_ERROR, "Invalid width_type value.\n");
-            return AVERROR(EINVAL);
-        }
-
-        switch (width_type) {
-        case 'h': width_type = HERTZ;   break;
-        case 'q': width_type = QFACTOR; break;
-        case 'o': width_type = OCTAVE;  break;
-        case 's': width_type = SLOPE;   break;
-        case 'k': width_type = KHERTZ;  break;
-        default:
-            av_log(ctx, AV_LOG_ERROR, "Invalid width_type value: %c\n", width_type);
-            return AVERROR(EINVAL);
-        }
-
-        s->width_type = width_type;
-    } else if ((!strcmp(cmd, "a0") ||
-                !strcmp(cmd, "a1") ||
-                !strcmp(cmd, "a2") ||
-                !strcmp(cmd, "b0") ||
-                !strcmp(cmd, "b1") ||
-                !strcmp(cmd, "b2")) &&
-               s->filter_type == biquad) {
-        double value;
-
-        if (sscanf(args, "%lf", &value) != 1) {
-            av_log(ctx, AV_LOG_ERROR, "Invalid biquad value.\n");
-            return AVERROR(EINVAL);
-        }
-
-        if (!strcmp(cmd, "a0"))
-            s->a0 = value;
-        else if (!strcmp(cmd, "a1"))
-            s->a1 = value;
-        else if (!strcmp(cmd, "a2"))
-            s->a2 = value;
-        else if (!strcmp(cmd, "b0"))
-            s->b0 = value;
-        else if (!strcmp(cmd, "b1"))
-            s->b1 = value;
-        else if (!strcmp(cmd, "b2"))
-            s->b2 = value;
-    }
+    ret = ff_filter_process_command(ctx, cmd, args, res, res_len, flags);
+    if (ret < 0)
+        return ret;
 
     return config_filter(outlink, 0);
 }
@@ -654,19 +807,20 @@ static const AVFilterPad outputs[] = {
 };
 
 #define OFFSET(x) offsetof(BiquadsContext, x)
-#define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
+#define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM
+#define AF AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
 
 #define DEFINE_BIQUAD_FILTER(name_, description_)                       \
 AVFILTER_DEFINE_CLASS(name_);                                           \
-static av_cold int name_##_init(AVFilterContext *ctx) \
+static av_cold int name_##_init(AVFilterContext *ctx)                   \
 {                                                                       \
     BiquadsContext *s = ctx->priv;                                      \
     s->class = &name_##_class;                                          \
     s->filter_type = name_;                                             \
-    return init(ctx);                                             \
+    return 0;                                                           \
 }                                                                       \
                                                          \
-AVFilter ff_af_##name_ = {                         \
+AVFilter ff_af_##name_ = {                               \
     .name          = #name_,                             \
     .description   = NULL_IF_CONFIG_SMALL(description_), \
     .priv_size     = sizeof(BiquadsContext),             \
@@ -699,6 +853,14 @@ static const AVOption equalizer_options[] = {
     {"m",   "set mix", OFFSET(mix), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, FLAGS},
     {"channels", "set channels to filter", OFFSET(channels), AV_OPT_TYPE_CHANNEL_LAYOUT, {.i64=-1}, INT64_MIN, INT64_MAX, FLAGS},
     {"c",        "set channels to filter", OFFSET(channels), AV_OPT_TYPE_CHANNEL_LAYOUT, {.i64=-1}, INT64_MIN, INT64_MAX, FLAGS},
+    {"normalize", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS},
+    {"n",         "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS},
+    {"transform", "set transform type", OFFSET(transform_type), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_TTYPE-1, AF, "transform_type"},
+    {"a",         "set transform type", OFFSET(transform_type), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_TTYPE-1, AF, "transform_type"},
+    {"di",   "direct form I",  0, AV_OPT_TYPE_CONST, {.i64=DI}, 0, 0, AF, "transform_type"},
+    {"dii",  "direct form II", 0, AV_OPT_TYPE_CONST, {.i64=DII}, 0, 0, AF, "transform_type"},
+    {"tdii", "transposed direct form II", 0, AV_OPT_TYPE_CONST, {.i64=TDII}, 0, 0, AF, "transform_type"},
+    {"latt", "lattice-ladder form", 0, AV_OPT_TYPE_CONST, {.i64=LATT}, 0, 0, AF, "transform_type"},
     {NULL}
 };
 
@@ -723,6 +885,14 @@ static const AVOption bass_options[] = {
     {"m",   "set mix", OFFSET(mix), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, FLAGS},
     {"channels", "set channels to filter", OFFSET(channels), AV_OPT_TYPE_CHANNEL_LAYOUT, {.i64=-1}, INT64_MIN, INT64_MAX, FLAGS},
     {"c",        "set channels to filter", OFFSET(channels), AV_OPT_TYPE_CHANNEL_LAYOUT, {.i64=-1}, INT64_MIN, INT64_MAX, FLAGS},
+    {"normalize", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS},
+    {"n",         "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS},
+    {"transform", "set transform type", OFFSET(transform_type), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_TTYPE-1, AF, "transform_type"},
+    {"a",         "set transform type", OFFSET(transform_type), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_TTYPE-1, AF, "transform_type"},
+    {"di",   "direct form I",  0, AV_OPT_TYPE_CONST, {.i64=DI}, 0, 0, AF, "transform_type"},
+    {"dii",  "direct form II", 0, AV_OPT_TYPE_CONST, {.i64=DII}, 0, 0, AF, "transform_type"},
+    {"tdii", "transposed direct form II", 0, AV_OPT_TYPE_CONST, {.i64=TDII}, 0, 0, AF, "transform_type"},
+    {"latt", "lattice-ladder form", 0, AV_OPT_TYPE_CONST, {.i64=LATT}, 0, 0, AF, "transform_type"},
     {NULL}
 };
 
@@ -747,6 +917,14 @@ static const AVOption treble_options[] = {
     {"m",   "set mix", OFFSET(mix), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, FLAGS},
     {"channels", "set channels to filter", OFFSET(channels), AV_OPT_TYPE_CHANNEL_LAYOUT, {.i64=-1}, INT64_MIN, INT64_MAX, FLAGS},
     {"c",        "set channels to filter", OFFSET(channels), AV_OPT_TYPE_CHANNEL_LAYOUT, {.i64=-1}, INT64_MIN, INT64_MAX, FLAGS},
+    {"normalize", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS},
+    {"n",         "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS},
+    {"transform", "set transform type", OFFSET(transform_type), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_TTYPE-1, AF, "transform_type"},
+    {"a",         "set transform type", OFFSET(transform_type), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_TTYPE-1, AF, "transform_type"},
+    {"di",   "direct form I",  0, AV_OPT_TYPE_CONST, {.i64=DI}, 0, 0, AF, "transform_type"},
+    {"dii",  "direct form II", 0, AV_OPT_TYPE_CONST, {.i64=DII}, 0, 0, AF, "transform_type"},
+    {"tdii", "transposed direct form II", 0, AV_OPT_TYPE_CONST, {.i64=TDII}, 0, 0, AF, "transform_type"},
+    {"latt", "lattice-ladder form", 0, AV_OPT_TYPE_CONST, {.i64=LATT}, 0, 0, AF, "transform_type"},
     {NULL}
 };
 
@@ -770,6 +948,14 @@ static const AVOption bandpass_options[] = {
     {"m",   "set mix", OFFSET(mix), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, FLAGS},
     {"channels", "set channels to filter", OFFSET(channels), AV_OPT_TYPE_CHANNEL_LAYOUT, {.i64=-1}, INT64_MIN, INT64_MAX, FLAGS},
     {"c",        "set channels to filter", OFFSET(channels), AV_OPT_TYPE_CHANNEL_LAYOUT, {.i64=-1}, INT64_MIN, INT64_MAX, FLAGS},
+    {"normalize", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS},
+    {"n",         "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS},
+    {"transform", "set transform type", OFFSET(transform_type), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_TTYPE-1, AF, "transform_type"},
+    {"a",         "set transform type", OFFSET(transform_type), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_TTYPE-1, AF, "transform_type"},
+    {"di",   "direct form I",  0, AV_OPT_TYPE_CONST, {.i64=DI}, 0, 0, AF, "transform_type"},
+    {"dii",  "direct form II", 0, AV_OPT_TYPE_CONST, {.i64=DII}, 0, 0, AF, "transform_type"},
+    {"tdii", "transposed direct form II", 0, AV_OPT_TYPE_CONST, {.i64=TDII}, 0, 0, AF, "transform_type"},
+    {"latt", "lattice-ladder form", 0, AV_OPT_TYPE_CONST, {.i64=LATT}, 0, 0, AF, "transform_type"},
     {NULL}
 };
 
@@ -792,6 +978,14 @@ static const AVOption bandreject_options[] = {
     {"m",   "set mix", OFFSET(mix), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, FLAGS},
     {"channels", "set channels to filter", OFFSET(channels), AV_OPT_TYPE_CHANNEL_LAYOUT, {.i64=-1}, INT64_MIN, INT64_MAX, FLAGS},
     {"c",        "set channels to filter", OFFSET(channels), AV_OPT_TYPE_CHANNEL_LAYOUT, {.i64=-1}, INT64_MIN, INT64_MAX, FLAGS},
+    {"normalize", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS},
+    {"n",         "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS},
+    {"transform", "set transform type", OFFSET(transform_type), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_TTYPE-1, AF, "transform_type"},
+    {"a",         "set transform type", OFFSET(transform_type), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_TTYPE-1, AF, "transform_type"},
+    {"di",   "direct form I",  0, AV_OPT_TYPE_CONST, {.i64=DI}, 0, 0, AF, "transform_type"},
+    {"dii",  "direct form II", 0, AV_OPT_TYPE_CONST, {.i64=DII}, 0, 0, AF, "transform_type"},
+    {"tdii", "transposed direct form II", 0, AV_OPT_TYPE_CONST, {.i64=TDII}, 0, 0, AF, "transform_type"},
+    {"latt", "lattice-ladder form", 0, AV_OPT_TYPE_CONST, {.i64=LATT}, 0, 0, AF, "transform_type"},
     {NULL}
 };
 
@@ -810,12 +1004,20 @@ static const AVOption lowpass_options[] = {
     {"k", "kHz", 0, AV_OPT_TYPE_CONST, {.i64=KHERTZ}, 0, 0, FLAGS, "width_type"},
     {"width", "set width", OFFSET(width), AV_OPT_TYPE_DOUBLE, {.dbl=0.707}, 0, 99999, FLAGS},
     {"w",     "set width", OFFSET(width), AV_OPT_TYPE_DOUBLE, {.dbl=0.707}, 0, 99999, FLAGS},
-    {"poles", "set number of poles", OFFSET(poles), AV_OPT_TYPE_INT, {.i64=2}, 1, 2, FLAGS},
-    {"p",     "set number of poles", OFFSET(poles), AV_OPT_TYPE_INT, {.i64=2}, 1, 2, FLAGS},
+    {"poles", "set number of poles", OFFSET(poles), AV_OPT_TYPE_INT, {.i64=2}, 1, 2, AF},
+    {"p",     "set number of poles", OFFSET(poles), AV_OPT_TYPE_INT, {.i64=2}, 1, 2, AF},
     {"mix", "set mix", OFFSET(mix), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, FLAGS},
     {"m",   "set mix", OFFSET(mix), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, FLAGS},
     {"channels", "set channels to filter", OFFSET(channels), AV_OPT_TYPE_CHANNEL_LAYOUT, {.i64=-1}, INT64_MIN, INT64_MAX, FLAGS},
     {"c",        "set channels to filter", OFFSET(channels), AV_OPT_TYPE_CHANNEL_LAYOUT, {.i64=-1}, INT64_MIN, INT64_MAX, FLAGS},
+    {"normalize", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS},
+    {"n",         "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS},
+    {"transform", "set transform type", OFFSET(transform_type), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_TTYPE-1, AF, "transform_type"},
+    {"a",         "set transform type", OFFSET(transform_type), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_TTYPE-1, AF, "transform_type"},
+    {"di",   "direct form I",  0, AV_OPT_TYPE_CONST, {.i64=DI}, 0, 0, AF, "transform_type"},
+    {"dii",  "direct form II", 0, AV_OPT_TYPE_CONST, {.i64=DII}, 0, 0, AF, "transform_type"},
+    {"tdii", "transposed direct form II", 0, AV_OPT_TYPE_CONST, {.i64=TDII}, 0, 0, AF, "transform_type"},
+    {"latt", "lattice-ladder form", 0, AV_OPT_TYPE_CONST, {.i64=LATT}, 0, 0, AF, "transform_type"},
     {NULL}
 };
 
@@ -834,12 +1036,20 @@ static const AVOption highpass_options[] = {
     {"k", "kHz", 0, AV_OPT_TYPE_CONST, {.i64=KHERTZ}, 0, 0, FLAGS, "width_type"},
     {"width", "set width", OFFSET(width), AV_OPT_TYPE_DOUBLE, {.dbl=0.707}, 0, 99999, FLAGS},
     {"w",     "set width", OFFSET(width), AV_OPT_TYPE_DOUBLE, {.dbl=0.707}, 0, 99999, FLAGS},
-    {"poles", "set number of poles", OFFSET(poles), AV_OPT_TYPE_INT, {.i64=2}, 1, 2, FLAGS},
-    {"p",     "set number of poles", OFFSET(poles), AV_OPT_TYPE_INT, {.i64=2}, 1, 2, FLAGS},
+    {"poles", "set number of poles", OFFSET(poles), AV_OPT_TYPE_INT, {.i64=2}, 1, 2, AF},
+    {"p",     "set number of poles", OFFSET(poles), AV_OPT_TYPE_INT, {.i64=2}, 1, 2, AF},
     {"mix", "set mix", OFFSET(mix), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, FLAGS},
     {"m",   "set mix", OFFSET(mix), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, FLAGS},
     {"channels", "set channels to filter", OFFSET(channels), AV_OPT_TYPE_CHANNEL_LAYOUT, {.i64=-1}, INT64_MIN, INT64_MAX, FLAGS},
     {"c",        "set channels to filter", OFFSET(channels), AV_OPT_TYPE_CHANNEL_LAYOUT, {.i64=-1}, INT64_MIN, INT64_MAX, FLAGS},
+    {"normalize", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS},
+    {"n",         "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS},
+    {"transform", "set transform type", OFFSET(transform_type), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_TTYPE-1, AF, "transform_type"},
+    {"a",         "set transform type", OFFSET(transform_type), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_TTYPE-1, AF, "transform_type"},
+    {"di",   "direct form I",  0, AV_OPT_TYPE_CONST, {.i64=DI}, 0, 0, AF, "transform_type"},
+    {"dii",  "direct form II", 0, AV_OPT_TYPE_CONST, {.i64=DII}, 0, 0, AF, "transform_type"},
+    {"tdii", "transposed direct form II", 0, AV_OPT_TYPE_CONST, {.i64=TDII}, 0, 0, AF, "transform_type"},
+    {"latt", "lattice-ladder form", 0, AV_OPT_TYPE_CONST, {.i64=LATT}, 0, 0, AF, "transform_type"},
     {NULL}
 };
 
@@ -862,6 +1072,16 @@ static const AVOption allpass_options[] = {
     {"m",   "set mix", OFFSET(mix), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, FLAGS},
     {"channels", "set channels to filter", OFFSET(channels), AV_OPT_TYPE_CHANNEL_LAYOUT, {.i64=-1}, INT64_MIN, INT64_MAX, FLAGS},
     {"c",        "set channels to filter", OFFSET(channels), AV_OPT_TYPE_CHANNEL_LAYOUT, {.i64=-1}, INT64_MIN, INT64_MAX, FLAGS},
+    {"normalize", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS},
+    {"n",         "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS},
+    {"order", "set filter order", OFFSET(order), AV_OPT_TYPE_INT, {.i64=2}, 1, 2, FLAGS},
+    {"o",     "set filter order", OFFSET(order), AV_OPT_TYPE_INT, {.i64=2}, 1, 2, FLAGS},
+    {"transform", "set transform type", OFFSET(transform_type), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_TTYPE-1, AF, "transform_type"},
+    {"a",         "set transform type", OFFSET(transform_type), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_TTYPE-1, AF, "transform_type"},
+    {"di",   "direct form I",  0, AV_OPT_TYPE_CONST, {.i64=DI}, 0, 0, AF, "transform_type"},
+    {"dii",  "direct form II", 0, AV_OPT_TYPE_CONST, {.i64=DII}, 0, 0, AF, "transform_type"},
+    {"tdii", "transposed direct form II", 0, AV_OPT_TYPE_CONST, {.i64=TDII}, 0, 0, AF, "transform_type"},
+    {"latt", "lattice-ladder form", 0, AV_OPT_TYPE_CONST, {.i64=LATT}, 0, 0, AF, "transform_type"},
     {NULL}
 };
 
@@ -886,6 +1106,14 @@ static const AVOption lowshelf_options[] = {
     {"m",   "set mix", OFFSET(mix), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, FLAGS},
     {"channels", "set channels to filter", OFFSET(channels), AV_OPT_TYPE_CHANNEL_LAYOUT, {.i64=-1}, INT64_MIN, INT64_MAX, FLAGS},
     {"c",        "set channels to filter", OFFSET(channels), AV_OPT_TYPE_CHANNEL_LAYOUT, {.i64=-1}, INT64_MIN, INT64_MAX, FLAGS},
+    {"normalize", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS},
+    {"n",         "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS},
+    {"transform", "set transform type", OFFSET(transform_type), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_TTYPE-1, AF, "transform_type"},
+    {"a",         "set transform type", OFFSET(transform_type), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_TTYPE-1, AF, "transform_type"},
+    {"di",   "direct form I",  0, AV_OPT_TYPE_CONST, {.i64=DI}, 0, 0, AF, "transform_type"},
+    {"dii",  "direct form II", 0, AV_OPT_TYPE_CONST, {.i64=DII}, 0, 0, AF, "transform_type"},
+    {"tdii", "transposed direct form II", 0, AV_OPT_TYPE_CONST, {.i64=TDII}, 0, 0, AF, "transform_type"},
+    {"latt", "lattice-ladder form", 0, AV_OPT_TYPE_CONST, {.i64=LATT}, 0, 0, AF, "transform_type"},
     {NULL}
 };
 
@@ -910,6 +1138,14 @@ static const AVOption highshelf_options[] = {
     {"m",   "set mix", OFFSET(mix), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, FLAGS},
     {"channels", "set channels to filter", OFFSET(channels), AV_OPT_TYPE_CHANNEL_LAYOUT, {.i64=-1}, INT64_MIN, INT64_MAX, FLAGS},
     {"c",        "set channels to filter", OFFSET(channels), AV_OPT_TYPE_CHANNEL_LAYOUT, {.i64=-1}, INT64_MIN, INT64_MAX, FLAGS},
+    {"normalize", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS},
+    {"n",         "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS},
+    {"transform", "set transform type", OFFSET(transform_type), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_TTYPE-1, AF, "transform_type"},
+    {"a",         "set transform type", OFFSET(transform_type), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_TTYPE-1, AF, "transform_type"},
+    {"di",   "direct form I",  0, AV_OPT_TYPE_CONST, {.i64=DI}, 0, 0, AF, "transform_type"},
+    {"dii",  "direct form II", 0, AV_OPT_TYPE_CONST, {.i64=DII}, 0, 0, AF, "transform_type"},
+    {"tdii", "transposed direct form II", 0, AV_OPT_TYPE_CONST, {.i64=TDII}, 0, 0, AF, "transform_type"},
+    {"latt", "lattice-ladder form", 0, AV_OPT_TYPE_CONST, {.i64=LATT}, 0, 0, AF, "transform_type"},
     {NULL}
 };
 
@@ -917,16 +1153,24 @@ DEFINE_BIQUAD_FILTER(highshelf, "Apply a high shelf filter.");
 #endif  /* CONFIG_HIGHSHELF_FILTER */
 #if CONFIG_BIQUAD_FILTER
 static const AVOption biquad_options[] = {
-    {"a0", NULL, OFFSET(a0), AV_OPT_TYPE_DOUBLE, {.dbl=1}, INT32_MIN, INT32_MAX, FLAGS},
-    {"a1", NULL, OFFSET(a1), AV_OPT_TYPE_DOUBLE, {.dbl=0}, INT32_MIN, INT32_MAX, FLAGS},
-    {"a2", NULL, OFFSET(a2), AV_OPT_TYPE_DOUBLE, {.dbl=0}, INT32_MIN, INT32_MAX, FLAGS},
-    {"b0", NULL, OFFSET(b0), AV_OPT_TYPE_DOUBLE, {.dbl=0}, INT32_MIN, INT32_MAX, FLAGS},
-    {"b1", NULL, OFFSET(b1), AV_OPT_TYPE_DOUBLE, {.dbl=0}, INT32_MIN, INT32_MAX, FLAGS},
-    {"b2", NULL, OFFSET(b2), AV_OPT_TYPE_DOUBLE, {.dbl=0}, INT32_MIN, INT32_MAX, FLAGS},
+    {"a0", NULL, OFFSET(oa0), AV_OPT_TYPE_DOUBLE, {.dbl=1}, INT32_MIN, INT32_MAX, FLAGS},
+    {"a1", NULL, OFFSET(oa1), AV_OPT_TYPE_DOUBLE, {.dbl=0}, INT32_MIN, INT32_MAX, FLAGS},
+    {"a2", NULL, OFFSET(oa2), AV_OPT_TYPE_DOUBLE, {.dbl=0}, INT32_MIN, INT32_MAX, FLAGS},
+    {"b0", NULL, OFFSET(ob0), AV_OPT_TYPE_DOUBLE, {.dbl=0}, INT32_MIN, INT32_MAX, FLAGS},
+    {"b1", NULL, OFFSET(ob1), AV_OPT_TYPE_DOUBLE, {.dbl=0}, INT32_MIN, INT32_MAX, FLAGS},
+    {"b2", NULL, OFFSET(ob2), AV_OPT_TYPE_DOUBLE, {.dbl=0}, INT32_MIN, INT32_MAX, FLAGS},
     {"mix", "set mix", OFFSET(mix), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, FLAGS},
     {"m",   "set mix", OFFSET(mix), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, FLAGS},
     {"channels", "set channels to filter", OFFSET(channels), AV_OPT_TYPE_CHANNEL_LAYOUT, {.i64=-1}, INT64_MIN, INT64_MAX, FLAGS},
     {"c",        "set channels to filter", OFFSET(channels), AV_OPT_TYPE_CHANNEL_LAYOUT, {.i64=-1}, INT64_MIN, INT64_MAX, FLAGS},
+    {"normalize", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS},
+    {"n",         "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS},
+    {"transform", "set transform type", OFFSET(transform_type), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_TTYPE-1, AF, "transform_type"},
+    {"a",         "set transform type", OFFSET(transform_type), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_TTYPE-1, AF, "transform_type"},
+    {"di",   "direct form I",  0, AV_OPT_TYPE_CONST, {.i64=DI}, 0, 0, AF, "transform_type"},
+    {"dii",  "direct form II", 0, AV_OPT_TYPE_CONST, {.i64=DII}, 0, 0, AF, "transform_type"},
+    {"tdii", "transposed direct form II", 0, AV_OPT_TYPE_CONST, {.i64=TDII}, 0, 0, AF, "transform_type"},
+    {"latt", "lattice-ladder form", 0, AV_OPT_TYPE_CONST, {.i64=LATT}, 0, 0, AF, "transform_type"},
     {NULL}
 };