You've already forked FFmpeg
mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2025-08-10 06:10:52 +02:00
avfilter/af_dynaudnorm: add curve option
This commit is contained in:
@@ -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
|
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
|
compensated for non-zero overlap then gain adjustments will be smoother across time
|
||||||
compared to zero overlap case.
|
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
|
@end table
|
||||||
|
|
||||||
@subsection Commands
|
@subsection Commands
|
||||||
|
@@ -28,6 +28,7 @@
|
|||||||
|
|
||||||
#include "libavutil/avassert.h"
|
#include "libavutil/avassert.h"
|
||||||
#include "libavutil/channel_layout.h"
|
#include "libavutil/channel_layout.h"
|
||||||
|
#include "libavutil/eval.h"
|
||||||
#include "libavutil/opt.h"
|
#include "libavutil/opt.h"
|
||||||
|
|
||||||
#define MIN_FILTER_SIZE 3
|
#define MIN_FILTER_SIZE 3
|
||||||
@@ -41,6 +42,26 @@
|
|||||||
#include "filters.h"
|
#include "filters.h"
|
||||||
#include "internal.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 {
|
typedef struct local_gain {
|
||||||
double max_gain;
|
double max_gain;
|
||||||
double threshold;
|
double threshold;
|
||||||
@@ -65,6 +86,7 @@ typedef struct DynamicAudioNormalizerContext {
|
|||||||
int channels_coupled;
|
int channels_coupled;
|
||||||
int alt_boundary_mode;
|
int alt_boundary_mode;
|
||||||
double overlap;
|
double overlap;
|
||||||
|
char *expr_str;
|
||||||
|
|
||||||
double peak_value;
|
double peak_value;
|
||||||
double max_amplification;
|
double max_amplification;
|
||||||
@@ -91,6 +113,9 @@ typedef struct DynamicAudioNormalizerContext {
|
|||||||
cqueue *is_enabled;
|
cqueue *is_enabled;
|
||||||
|
|
||||||
AVFrame *window;
|
AVFrame *window;
|
||||||
|
|
||||||
|
AVExpr *expr;
|
||||||
|
double var_values[VAR_VARS_NB];
|
||||||
} DynamicAudioNormalizerContext;
|
} DynamicAudioNormalizerContext;
|
||||||
|
|
||||||
typedef struct ThreadData {
|
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 },
|
{ "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 },
|
{ "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 },
|
{ "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 },
|
{ "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 },
|
{ "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 },
|
{ "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 },
|
{ "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 }
|
{ NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -309,12 +336,15 @@ static av_cold void uninit(AVFilterContext *ctx)
|
|||||||
ff_bufqueue_discard_all(&s->queue);
|
ff_bufqueue_discard_all(&s->queue);
|
||||||
|
|
||||||
av_frame_free(&s->window);
|
av_frame_free(&s->window);
|
||||||
|
av_expr_free(s->expr);
|
||||||
|
s->expr = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int config_input(AVFilterLink *inlink)
|
static int config_input(AVFilterLink *inlink)
|
||||||
{
|
{
|
||||||
AVFilterContext *ctx = inlink->dst;
|
AVFilterContext *ctx = inlink->dst;
|
||||||
DynamicAudioNormalizerContext *s = ctx->priv;
|
DynamicAudioNormalizerContext *s = ctx->priv;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
uninit(ctx);
|
uninit(ctx);
|
||||||
|
|
||||||
@@ -358,7 +388,13 @@ static int config_input(AVFilterLink *inlink)
|
|||||||
return AVERROR(ENOMEM);
|
return AVERROR(ENOMEM);
|
||||||
s->sample_advance = FFMAX(1, lrint(s->frame_len * (1. - s->overlap)));
|
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)
|
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 peak_magnitude = find_peak_magnitude(frame, channel);
|
||||||
const double maximum_gain = s->peak_value / peak_magnitude;
|
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;
|
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;
|
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.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;
|
return gain;
|
||||||
}
|
}
|
||||||
@@ -731,6 +779,9 @@ static int analyze_frame(AVFilterContext *ctx, AVFilterLink *outlink, AVFrame **
|
|||||||
analyze_frame = *frame;
|
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) {
|
if (s->channels_coupled) {
|
||||||
const local_gain gain = get_max_local_gain(s, analyze_frame, -1);
|
const local_gain gain = get_max_local_gain(s, analyze_frame, -1);
|
||||||
for (int c = 0; c < s->channels; c++)
|
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->frame_len = frame_size(inlink->sample_rate, s->frame_len_msec);
|
||||||
s->sample_advance = FFMAX(1, lrint(s->frame_len * (1. - s->overlap)));
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user