avfilter/af_dynaudnorm: add curve option

This commit is contained in:
Paul B Mahol 2022-11-08 15:17:50 +01:00
parent a0c7c9d636
commit 8e96319674
2 changed files with 88 additions and 5 deletions

View File

@ -4587,6 +4587,33 @@ Using >0 and <1 values will make less conservative gain adjustments, like
when framelen option is set to smaller value, if framelen option value is
compensated for non-zero overlap then gain adjustments will be smoother across time
compared to zero overlap case.
@item curve, v
Specify the peak mapping curve expression which is going to be used when calculating
gain applied to frames. The max output frame gain will still be limited by other
options mentioned previously for this filter.
The expression can contain the following constants:
@table @option
@item ch
current channel number
@item sn
current sample number
@item nb_channels
number of channels
@item t
timestamp expressed in seconds
@item sr
sample rate
@item p
current frame peak value
@end table
@end table
@subsection Commands

View File

@ -28,6 +28,7 @@
#include "libavutil/avassert.h"
#include "libavutil/channel_layout.h"
#include "libavutil/eval.h"
#include "libavutil/opt.h"
#define MIN_FILTER_SIZE 3
@ -41,6 +42,26 @@
#include "filters.h"
#include "internal.h"
static const char * const var_names[] = {
"ch", ///< the value of the current channel
"sn", ///< number of samples
"nb_channels",
"t", ///< timestamp expressed in seconds
"sr", ///< sample rate
"p", ///< peak value
NULL
};
enum var_name {
VAR_CH,
VAR_SN,
VAR_NB_CHANNELS,
VAR_T,
VAR_SR,
VAR_P,
VAR_VARS_NB
};
typedef struct local_gain {
double max_gain;
double threshold;
@ -65,6 +86,7 @@ typedef struct DynamicAudioNormalizerContext {
int channels_coupled;
int alt_boundary_mode;
double overlap;
char *expr_str;
double peak_value;
double max_amplification;
@ -91,6 +113,9 @@ typedef struct DynamicAudioNormalizerContext {
cqueue *is_enabled;
AVFrame *window;
AVExpr *expr;
double var_values[VAR_VARS_NB];
} DynamicAudioNormalizerContext;
typedef struct ThreadData {
@ -122,10 +147,12 @@ static const AVOption dynaudnorm_options[] = {
{ "s", "set the compress factor", OFFSET(compress_factor), AV_OPT_TYPE_DOUBLE, {.dbl = 0.0}, 0.0, 30.0, FLAGS },
{ "threshold", "set the threshold value", OFFSET(threshold), AV_OPT_TYPE_DOUBLE, {.dbl = 0.0}, 0.0, 1.0, FLAGS },
{ "t", "set the threshold value", OFFSET(threshold), AV_OPT_TYPE_DOUBLE, {.dbl = 0.0}, 0.0, 1.0, FLAGS },
{ "channels", "set channels to filter", OFFSET(channels_to_filter),AV_OPT_TYPE_STRING, {.str="all"}, 0, 0, FLAGS },
{ "h", "set channels to filter", OFFSET(channels_to_filter),AV_OPT_TYPE_STRING, {.str="all"}, 0, 0, FLAGS },
{ "channels", "set channels to filter", OFFSET(channels_to_filter),AV_OPT_TYPE_STRING, {.str="all"}, 0, 0, FLAGS },
{ "h", "set channels to filter", OFFSET(channels_to_filter),AV_OPT_TYPE_STRING, {.str="all"}, 0, 0, FLAGS },
{ "overlap", "set the frame overlap", OFFSET(overlap), AV_OPT_TYPE_DOUBLE, {.dbl=.0}, 0.0, 1.0, FLAGS },
{ "o", "set the frame overlap", OFFSET(overlap), AV_OPT_TYPE_DOUBLE, {.dbl=.0}, 0.0, 1.0, FLAGS },
{ "curve", "set the custom peak mapping curve",OFFSET(expr_str), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = FLAGS },
{ "v", "set the custom peak mapping curve",OFFSET(expr_str), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = FLAGS },
{ NULL }
};
@ -309,12 +336,15 @@ static av_cold void uninit(AVFilterContext *ctx)
ff_bufqueue_discard_all(&s->queue);
av_frame_free(&s->window);
av_expr_free(s->expr);
s->expr = NULL;
}
static int config_input(AVFilterLink *inlink)
{
AVFilterContext *ctx = inlink->dst;
DynamicAudioNormalizerContext *s = ctx->priv;
int ret = 0;
uninit(ctx);
@ -358,7 +388,13 @@ static int config_input(AVFilterLink *inlink)
return AVERROR(ENOMEM);
s->sample_advance = FFMAX(1, lrint(s->frame_len * (1. - s->overlap)));
return 0;
s->var_values[VAR_SR] = inlink->sample_rate;
s->var_values[VAR_NB_CHANNELS] = s->channels;
if (s->expr_str)
ret = av_expr_parse(&s->expr, s->expr_str, var_names, NULL, NULL,
NULL, NULL, 0, ctx);
return ret;
}
static inline double fade(double prev, double next, int pos, int length)
@ -433,10 +469,22 @@ static local_gain get_max_local_gain(DynamicAudioNormalizerContext *s, AVFrame *
const double peak_magnitude = find_peak_magnitude(frame, channel);
const double maximum_gain = s->peak_value / peak_magnitude;
const double rms_gain = s->target_rms > DBL_EPSILON ? (s->target_rms / compute_frame_rms(frame, channel)) : DBL_MAX;
double target_gain = DBL_MAX;
local_gain gain;
if (s->expr_str) {
double var_values[VAR_VARS_NB];
memcpy(var_values, s->var_values, sizeof(var_values));
var_values[VAR_CH] = channel;
var_values[VAR_P] = peak_magnitude;
target_gain = av_expr_eval(s->expr, var_values, s) / peak_magnitude;
}
gain.threshold = peak_magnitude > s->threshold;
gain.max_gain = bound(s->max_amplification, fmin(maximum_gain, rms_gain));
gain.max_gain = bound(s->max_amplification, fmin(target_gain, fmin(maximum_gain, rms_gain)));
return gain;
}
@ -731,6 +779,9 @@ static int analyze_frame(AVFilterContext *ctx, AVFilterLink *outlink, AVFrame **
analyze_frame = *frame;
}
s->var_values[VAR_SN] = outlink->sample_count_in;
s->var_values[VAR_T] = s->var_values[VAR_SN] * (double)1/outlink->sample_rate;
if (s->channels_coupled) {
const local_gain gain = get_max_local_gain(s, analyze_frame, -1);
for (int c = 0; c < s->channels; c++)
@ -951,7 +1002,12 @@ static int process_command(AVFilterContext *ctx, const char *cmd, const char *ar
s->frame_len = frame_size(inlink->sample_rate, s->frame_len_msec);
s->sample_advance = FFMAX(1, lrint(s->frame_len * (1. - s->overlap)));
if (s->expr_str) {
ret = av_expr_parse(&s->expr, s->expr_str, var_names, NULL, NULL,
NULL, NULL, 0, ctx);
if (ret < 0)
return ret;
}
return 0;
}