From 82be1e5c0d828eef0b69307a61bc14f5b23ed595 Mon Sep 17 00:00:00 2001 From: Paul B Mahol Date: Mon, 6 Nov 2023 00:36:17 +0100 Subject: [PATCH] avfilter/af_adynamicequalizer: do gain calculations in log domain --- libavfilter/adynamicequalizer_template.c | 132 ++++++++++++++--------- libavfilter/af_adynamicequalizer.c | 61 ++++++----- 2 files changed, 120 insertions(+), 73 deletions(-) diff --git a/libavfilter/adynamicequalizer_template.c b/libavfilter/adynamicequalizer_template.c index c5830db215..653d51c3a9 100644 --- a/libavfilter/adynamicequalizer_template.c +++ b/libavfilter/adynamicequalizer_template.c @@ -26,8 +26,10 @@ #undef FMIN #undef CLIP #undef SAMPLE_FORMAT -#undef EPSILON #undef FABS +#undef FLOG10 +#undef FEXP10 +#undef EPSILON #if DEPTH == 32 #define SAMPLE_FORMAT float #define SQRT sqrtf @@ -39,8 +41,10 @@ #define FMAX fmaxf #define CLIP av_clipf #define FABS fabsf +#define FLOG10 log10f +#define FEXP10 ff_exp10f +#define EPSILON (1.f / (1 << 23)) #define ftype float -#define EPSILON (1.f / (1 << 22)) #else #define SAMPLE_FORMAT double #define SQRT sqrt @@ -52,10 +56,15 @@ #define FMAX fmax #define CLIP av_clipd #define FABS fabs +#define FLOG10 log10 +#define FEXP10 ff_exp10 +#define EPSILON (1.0 / (1LL << 53)) #define ftype double -#define EPSILON (1.0 / (1LL << 51)) #endif +#define LIN2LOG(x) (20.0 * FLOG10(x)) +#define LOG2LIN(x) (FEXP10(x / 20.0)) + #define fn3(a,b) a##_##b #define fn2(a,b) fn3(a,b) #define fn(a) fn2(a, SAMPLE_FORMAT) @@ -85,8 +94,11 @@ static int fn(filter_prepare)(AVFilterContext *ctx) ftype *dm = fn(s->dm); ftype k; - s->attack_coef = get_coef(s->attack, sample_rate); - s->release_coef = get_coef(s->release, sample_rate); + s->threshold_log = LIN2LOG(s->threshold); + s->dattack_coef = get_coef(s->dattack, sample_rate); + s->drelease_coef = get_coef(s->drelease, sample_rate); + s->gattack_coef = s->dattack_coef * 0.25; + s->grelease_coef = s->drelease_coef * 0.25; switch (dftype) { case 0: @@ -150,9 +162,11 @@ static int fn(filter_channels)(AVFilterContext *ctx, void *arg, int jobnr, int n const ftype range = s->range; const ftype tfrequency = FMIN(s->tfrequency, sample_rate * 0.5); const int mode = s->mode; - const int power = (mode == CUT_BELOW || mode == CUT_ABOVE) ? -1 : 1; - const ftype release = s->release_coef; - const ftype attack = s->attack_coef; + const ftype power = (mode == CUT_BELOW || mode == CUT_ABOVE) ? -ONE : ONE; + const ftype grelease = s->grelease_coef; + const ftype gattack = s->gattack_coef; + const ftype drelease = s->drelease_coef; + const ftype dattack = s->dattack_coef; const ftype tqfactor = s->tqfactor; const ftype itqfactor = ONE / tqfactor; const ftype fg = TAN(M_PI * tfrequency / sample_rate); @@ -164,21 +178,39 @@ static int fn(filter_channels)(AVFilterContext *ctx, void *arg, int jobnr, int n const ftype *da = fn(s->da); const ftype *dm = fn(s->dm); - if (detection > 0) { + if (detection == DET_ON) { for (int ch = start; ch < end; ch++) { const ftype *src = (const ftype *)in->extended_data[ch]; ChannelContext *cc = &s->cc[ch]; ftype *tstate = fn(cc->tstate); + ftype new_threshold = ZERO; + + if (cc->detection != detection) { + cc->detection = detection; + fn(cc->new_threshold_log) = LIN2LOG(EPSILON); + } for (int n = 0; n < in->nb_samples; n++) { - ftype detect = fn(get_svf)(src[n], dm, da, tstate); - fn(cc->threshold) = FMAX(fn(cc->threshold), detect); + ftype detect = FABS(fn(get_svf)(src[n], dm, da, tstate)); + new_threshold = FMAX(new_threshold, detect); } + + fn(cc->new_threshold_log) = FMAX(fn(cc->new_threshold_log), LIN2LOG(new_threshold)); } - } else if (detection < 0) { + } else if (detection == DET_DISABLED) { for (int ch = start; ch < end; ch++) { ChannelContext *cc = &s->cc[ch]; - fn(cc->threshold) = s->threshold; + fn(cc->threshold_log) = s->threshold_log; + cc->detection = detection; + } + } else if (detection == DET_OFF) { + for (int ch = start; ch < end; ch++) { + ChannelContext *cc = &s->cc[ch]; + if (cc->detection == DET_ON) + fn(cc->threshold_log) = fn(cc->new_threshold_log); + else if (cc->detection == DET_UNSET) + fn(cc->threshold_log) = s->threshold_log; + cc->detection = detection; } } @@ -186,95 +218,99 @@ static int fn(filter_channels)(AVFilterContext *ctx, void *arg, int jobnr, int n const ftype *src = (const ftype *)in->extended_data[ch]; ftype *dst = (ftype *)out->extended_data[ch]; ChannelContext *cc = &s->cc[ch]; - const ftype threshold = fn(cc->threshold); + const ftype threshold_log = fn(cc->threshold_log); ftype *fa = fn(cc->fa), *fm = fn(cc->fm); ftype *fstate = fn(cc->fstate); ftype *dstate = fn(cc->dstate); - ftype gain = fn(cc->gain); - const int init = cc->init; + ftype detect = fn(cc->detect); + ftype lin_gain = fn(cc->lin_gain); + int init = cc->init; for (int n = 0; n < out->nb_samples; n++) { - ftype detect, v, listen, new_gain = ONE; - ftype k, g; + ftype new_detect, new_lin_gain = ONE; + ftype f, v, listen, k, g, ld; - detect = listen = fn(get_svf)(src[n], dm, da, dstate); - detect = FABS(detect); + listen = fn(get_svf)(src[n], dm, da, dstate); + if (mode > LISTEN) { + new_detect = FABS(listen); + f = (new_detect > detect) * dattack + (new_detect <= detect) * drelease; + detect = f * new_detect + (ONE - f) * detect; + } switch (mode) { case LISTEN: break; case CUT_BELOW: case BOOST_BELOW: - if (detect < threshold) - new_gain = CLIP(ONE + makeup + (threshold - detect) * ratio, ONE, range); + ld = LIN2LOG(detect); + if (ld < threshold_log) { + ftype new_log_gain = CLIP(makeup + (threshold_log - ld) * ratio, ZERO, range) * power; + new_lin_gain = LOG2LIN(new_log_gain); + } break; case CUT_ABOVE: case BOOST_ABOVE: - if (detect > threshold) - new_gain = CLIP(ONE + makeup + (detect - threshold) * ratio, ONE, range); + ld = LIN2LOG(detect); + if (ld > threshold_log) { + ftype new_log_gain = CLIP(makeup + (ld - threshold_log) * ratio, ZERO, range) * power; + new_lin_gain = LOG2LIN(new_log_gain); + } break; } - if (power < 0) - new_gain = ONE / new_gain; + f = (new_lin_gain > lin_gain) * gattack + (new_lin_gain <= lin_gain) * grelease; + new_lin_gain = f * new_lin_gain + (ONE - f) * lin_gain; - if (mode > LISTEN) { - ftype delta = new_gain - gain; - - if (delta > EPSILON) - new_gain = gain + attack * delta; - else if (delta < -EPSILON) - new_gain = gain + release * delta; - } - - if (gain != new_gain || !init) { - gain = new_gain; + if (lin_gain != new_lin_gain || !init) { + init = 1; + lin_gain = new_lin_gain; switch (tftype) { case 0: - k = itqfactor / gain; + k = itqfactor / lin_gain; fa[0] = ONE / (ONE + fg * (fg + k)); fa[1] = fg * fa[0]; fa[2] = fg * fa[1]; fm[0] = ONE; - fm[1] = k * (gain * gain - ONE); + fm[1] = k * (lin_gain * lin_gain - ONE); fm[2] = ZERO; break; case 1: k = itqfactor; - g = fg / SQRT(gain); + g = fg / SQRT(lin_gain); fa[0] = ONE / (ONE + g * (g + k)); fa[1] = g * fa[0]; fa[2] = g * fa[1]; fm[0] = ONE; - fm[1] = k * (gain - ONE); - fm[2] = gain * gain - ONE; + fm[1] = k * (lin_gain - ONE); + fm[2] = lin_gain * lin_gain - ONE; break; case 2: k = itqfactor; - g = fg * SQRT(gain); + g = fg * SQRT(lin_gain); fa[0] = ONE / (ONE + g * (g + k)); fa[1] = g * fa[0]; fa[2] = g * fa[1]; - fm[0] = gain * gain; - fm[1] = k * (ONE - gain) * gain; - fm[2] = ONE - gain * gain; + fm[0] = lin_gain * lin_gain; + fm[1] = k * (ONE - lin_gain) * lin_gain; + fm[2] = ONE - lin_gain * lin_gain; break; } } v = fn(get_svf)(src[n], fm, fa, fstate); - v = mode == -1 ? listen : v; + v = mode == LISTEN ? listen : v; dst[n] = is_disabled ? src[n] : v; } - fn(cc->gain) = gain; + fn(cc->detect) = detect; + fn(cc->lin_gain) = lin_gain; cc->init = 1; } diff --git a/libavfilter/af_adynamicequalizer.c b/libavfilter/af_adynamicequalizer.c index ae51bdd075..1926ae8ec1 100644 --- a/libavfilter/af_adynamicequalizer.c +++ b/libavfilter/af_adynamicequalizer.c @@ -18,18 +18,27 @@ #include +#include "libavutil/ffmath.h" #include "libavutil/opt.h" #include "avfilter.h" #include "audio.h" #include "formats.h" +enum DetectionModes { + DET_UNSET = 0, + DET_DISABLED, + DET_OFF, + DET_ON, + NB_DMODES, +}; + enum FilterModes { LISTEN = -1, CUT_BELOW, CUT_ABOVE, BOOST_BELOW, BOOST_ABOVE, - NB_MODES, + NB_FMODES, }; typedef struct ChannelContext { @@ -37,14 +46,19 @@ typedef struct ChannelContext { double dstate_double[2]; double fstate_double[2]; double tstate_double[2]; - double gain_double; - double threshold_double; + double lin_gain_double; + double detect_double; + double threshold_log_double; + double new_threshold_log_double; float fa_float[3], fm_float[3]; float dstate_float[2]; float fstate_float[2]; float tstate_float[2]; - float gain_float; - float threshold_float; + float lin_gain_float; + float detect_float; + float threshold_log_float; + float new_threshold_log_float; + int detection; int init; } ChannelContext; @@ -52,6 +66,7 @@ typedef struct AudioDynamicEqualizerContext { const AVClass *class; double threshold; + double threshold_log; double dfrequency; double dqfactor; double tfrequency; @@ -59,10 +74,12 @@ typedef struct AudioDynamicEqualizerContext { double ratio; double range; double makeup; - double attack; - double release; - double attack_coef; - double release_coef; + double dattack; + double drelease; + double dattack_coef; + double drelease_coef; + double gattack_coef; + double grelease_coef; int mode; int detection; int tftype; @@ -100,7 +117,7 @@ static int query_formats(AVFilterContext *ctx) static double get_coef(double x, double sr) { - return 1.0 - exp(-1000. / (x * sr)); + return 1.0 - exp(-1.0 / (0.001 * x * sr)); } typedef struct ThreadData { @@ -135,12 +152,6 @@ static int config_input(AVFilterLink *inlink) break; } - for (int ch = 0; ch < inlink->ch_layout.nb_channels; ch++) { - ChannelContext *cc = &s->cc[ch]; - cc->gain_float = 1.f; - cc->gain_double = 1.0; - } - return 0; } @@ -191,12 +202,12 @@ static const AVOption adynamicequalizer_options[] = { { "dqfactor", "set detection Q factor", OFFSET(dqfactor), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0.001, 1000, FLAGS }, { "tfrequency", "set target frequency", OFFSET(tfrequency), AV_OPT_TYPE_DOUBLE, {.dbl=1000}, 2, 1000000, FLAGS }, { "tqfactor", "set target Q factor", OFFSET(tqfactor), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0.001, 1000, FLAGS }, - { "attack", "set attack duration", OFFSET(attack), AV_OPT_TYPE_DOUBLE, {.dbl=20}, 1, 2000, FLAGS }, - { "release", "set release duration", OFFSET(release), AV_OPT_TYPE_DOUBLE, {.dbl=200}, 1, 2000, FLAGS }, + { "attack", "set detection attack duration", OFFSET(dattack), AV_OPT_TYPE_DOUBLE, {.dbl=20}, 0.01, 2000, FLAGS }, + { "release","set detection release duration",OFFSET(drelease), AV_OPT_TYPE_DOUBLE, {.dbl=200}, 0.01, 2000, FLAGS }, { "ratio", "set ratio factor", OFFSET(ratio), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 30, FLAGS }, - { "makeup", "set makeup gain", OFFSET(makeup), AV_OPT_TYPE_DOUBLE, {.dbl=0}, 0, 100, FLAGS }, - { "range", "set max gain", OFFSET(range), AV_OPT_TYPE_DOUBLE, {.dbl=50}, 1, 200, FLAGS }, - { "mode", "set mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, LISTEN,NB_MODES-1,FLAGS, "mode" }, + { "makeup", "set makeup gain", OFFSET(makeup), AV_OPT_TYPE_DOUBLE, {.dbl=0}, 0, 1000, FLAGS }, + { "range", "set max gain", OFFSET(range), AV_OPT_TYPE_DOUBLE, {.dbl=50}, 1, 2000, FLAGS }, + { "mode", "set mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, LISTEN,NB_FMODES-1,FLAGS, "mode" }, { "listen", 0, 0, AV_OPT_TYPE_CONST, {.i64=LISTEN}, 0, 0, FLAGS, "mode" }, { "cutbelow", 0, 0, AV_OPT_TYPE_CONST, {.i64=CUT_BELOW},0, 0, FLAGS, "mode" }, { "cutabove", 0, 0, AV_OPT_TYPE_CONST, {.i64=CUT_ABOVE},0, 0, FLAGS, "mode" }, @@ -211,10 +222,10 @@ static const AVOption adynamicequalizer_options[] = { { "bell", 0, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "tftype" }, { "lowshelf", 0, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "tftype" }, { "highshelf",0, 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, FLAGS, "tftype" }, - { "auto", "set auto threshold", OFFSET(detection), AV_OPT_TYPE_INT, {.i64=-1}, -1, 1, FLAGS, "auto" }, - { "disabled", 0, 0, AV_OPT_TYPE_CONST, {.i64=-1}, 0, 0, FLAGS, "auto" }, - { "off", 0, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "auto" }, - { "on", 0, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "auto" }, + { "auto", "set auto threshold", OFFSET(detection), AV_OPT_TYPE_INT, {.i64=DET_OFF},DET_DISABLED,NB_DMODES-1,FLAGS, "auto" }, + { "disabled", 0, 0, AV_OPT_TYPE_CONST, {.i64=DET_DISABLED}, 0, 0, FLAGS, "auto" }, + { "off", 0, 0, AV_OPT_TYPE_CONST, {.i64=DET_OFF}, 0, 0, FLAGS, "auto" }, + { "on", 0, 0, AV_OPT_TYPE_CONST, {.i64=DET_ON}, 0, 0, FLAGS, "auto" }, { "precision", "set processing precision", OFFSET(precision), AV_OPT_TYPE_INT, {.i64=0}, 0, 2, AF, "precision" }, { "auto", "set auto processing precision", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, AF, "precision" }, { "float", "set single-floating point processing precision", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, AF, "precision" },