| @@ -28,6 +28,7 @@ · 27 unmodified lines |
| 28 | | 28 | |
| 29 | #include "libavutil/avassert.h" | 29 | #include "libavutil/avassert.h" |
| 30 | #include "libavutil/channel_layout.h" | 30 | #include "libavutil/channel_layout.h" |
| | 31 | #include "libavutil/eval.h" |
| 31 | #include "libavutil/opt.h" | 32 | #include "libavutil/opt.h" |
| 32 | | 33 | |
| 33 | #define MIN_FILTER_SIZE 3 | 34 | #define MIN_FILTER_SIZE 3 |
| @@ -41,6 +42,26 @@ · 7 unmodified lines |
| 41 | #include "filters.h" | 42 | #include "filters.h" |
| 42 | #include "internal.h" | 43 | #include "internal.h" |
| 43 | | 44 | |
| | 45 | static const char * const var_names[] = { |
| | 46 | "ch", ///< the value of the current channel |
| | 47 | "sn", ///< number of samples |
| | 48 | "nb_channels", |
| | 49 | "t", ///< timestamp expressed in seconds |
| | 50 | "sr", ///< sample rate |
| | 51 | "p", ///< peak value |
| | 52 | NULL |
| | 53 | }; |
| | 54 | |
| | 55 | enum var_name { |
| | 56 | VAR_CH, |
| | 57 | VAR_SN, |
| | 58 | VAR_NB_CHANNELS, |
| | 59 | VAR_T, |
| | 60 | VAR_SR, |
| | 61 | VAR_P, |
| | 62 | VAR_VARS_NB |
| | 63 | }; |
| | 64 | |
| 44 | typedef struct local_gain { | 65 | typedef struct local_gain { |
| 45 | double max_gain; | 66 | double max_gain; |
| 46 | double threshold; | 67 | double threshold; |
| @@ -65,6 +86,7 @@ typedef struct DynamicAudioNormalizerContext { · 18 unmodified lines |
| 65 | int channels_coupled; | 86 | int channels_coupled; |
| 66 | int alt_boundary_mode; | 87 | int alt_boundary_mode; |
| 67 | double overlap; | 88 | double overlap; |
| | 89 | char *expr_str; |
| 68 | | 90 | |
| 69 | double peak_value; | 91 | double peak_value; |
| 70 | double max_amplification; | 92 | double max_amplification; |
| @@ -91,6 +113,9 @@ typedef struct DynamicAudioNormalizerContext { · 20 unmodified lines |
| 91 | cqueue *is_enabled; | 113 | cqueue *is_enabled; |
| 92 | | 114 | |
| 93 | AVFrame *window; | 115 | AVFrame *window; |
| | 116 | |
| | 117 | AVExpr *expr; |
| | 118 | double var_values[VAR_VARS_NB]; |
| 94 | } DynamicAudioNormalizerContext; | 119 | } DynamicAudioNormalizerContext; |
| 95 | | 120 | |
| 96 | typedef struct ThreadData { | 121 | typedef struct ThreadData { |
| @@ -122,10 +147,12 @@ static const AVOption dynaudnorm_options[] = { · 25 unmodified lines |
| 122 | { "s", "set the compress factor", OFFSET(compress_factor), AV_OPT_TYPE_DOUBLE, {.dbl = 0.0}, 0.0, 30.0, FLAGS }, | 147 | { "s", "set the compress factor", OFFSET(compress_factor), AV_OPT_TYPE_DOUBLE, {.dbl = 0.0}, 0.0, 30.0, FLAGS }, |
| 123 | { "threshold", "set the threshold value", OFFSET(threshold), AV_OPT_TYPE_DOUBLE, {.dbl = 0.0}, 0.0, 1.0, FLAGS }, | 148 | { "threshold", "set the threshold value", OFFSET(threshold), AV_OPT_TYPE_DOUBLE, {.dbl = 0.0}, 0.0, 1.0, FLAGS }, |
| 124 | { "t", "set the threshold value", OFFSET(threshold), AV_OPT_TYPE_DOUBLE, {.dbl = 0.0}, 0.0, 1.0, FLAGS }, | 149 | { "t", "set the threshold value", OFFSET(threshold), AV_OPT_TYPE_DOUBLE, {.dbl = 0.0}, 0.0, 1.0, FLAGS }, |
| 125 | { "channels", "set channels to filter", OFFSET(channels_to_filter),AV_OPT_TYPE_STRING, {.str="all"}, 0, 0, FLAGS }, | 150 | { "channels", "set channels to filter", OFFSET(channels_to_filter),AV_OPT_TYPE_STRING, {.str="all"}, 0, 0, FLAGS }, |
| 126 | { "h", "set channels to filter", OFFSET(channels_to_filter),AV_OPT_TYPE_STRING, {.str="all"}, 0, 0, FLAGS }, | 151 | { "h", "set channels to filter", OFFSET(channels_to_filter),AV_OPT_TYPE_STRING, {.str="all"}, 0, 0, FLAGS }, |
| 127 | { "overlap", "set the frame overlap", OFFSET(overlap), AV_OPT_TYPE_DOUBLE, {.dbl=.0}, 0.0, 1.0, FLAGS }, | 152 | { "overlap", "set the frame overlap", OFFSET(overlap), AV_OPT_TYPE_DOUBLE, {.dbl=.0}, 0.0, 1.0, FLAGS }, |
| 128 | { "o", "set the frame overlap", OFFSET(overlap), AV_OPT_TYPE_DOUBLE, {.dbl=.0}, 0.0, 1.0, FLAGS }, | 153 | { "o", "set the frame overlap", OFFSET(overlap), AV_OPT_TYPE_DOUBLE, {.dbl=.0}, 0.0, 1.0, FLAGS }, |
| | 154 | { "curve", "set the custom peak mapping curve",OFFSET(expr_str), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = FLAGS }, |
| | 155 | { "v", "set the custom peak mapping curve",OFFSET(expr_str), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = FLAGS }, |
| 129 | { NULL } | 156 | { NULL } |
| 130 | }; | 157 | }; |
| 131 | | 158 | |
| @@ -309,12 +336,15 @@ static av_cold void uninit(AVFilterContext *ctx) · 177 unmodified lines |
| 309 | ff_bufqueue_discard_all(&s->queue); | 336 | ff_bufqueue_discard_all(&s->queue); |
| 310 | | 337 | |
| 311 | av_frame_free(&s->window); | 338 | av_frame_free(&s->window); |
| | 339 | av_expr_free(s->expr); |
| | 340 | s->expr = NULL; |
| 312 | } | 341 | } |
| 313 | | 342 | |
| 314 | static int config_input(AVFilterLink *inlink) | 343 | static int config_input(AVFilterLink *inlink) |
| 315 | { | 344 | { |
| 316 | AVFilterContext *ctx = inlink->dst; | 345 | AVFilterContext *ctx = inlink->dst; |
| 317 | DynamicAudioNormalizerContext *s = ctx->priv; | 346 | DynamicAudioNormalizerContext *s = ctx->priv; |
| | 347 | int ret = 0; |
| 318 | | 348 | |
| 319 | uninit(ctx); | 349 | uninit(ctx); |
| 320 | | 350 | |
| @@ -358,7 +388,13 @@ static int config_input(AVFilterLink *inlink) · 37 unmodified lines |
| 358 | return AVERROR(ENOMEM); | 388 | return AVERROR(ENOMEM); |
| 359 | s->sample_advance = FFMAX(1, lrint(s->frame_len * (1. - s->overlap))); | 389 | s->sample_advance = FFMAX(1, lrint(s->frame_len * (1. - s->overlap))); |
| 360 | | 390 | |
| 361 | return 0; | 391 | s->var_values[VAR_SR] = inlink->sample_rate; |
| | 392 | s->var_values[VAR_NB_CHANNELS] = s->channels; |
| | 393 | |
| | 394 | if (s->expr_str) |
| | 395 | ret = av_expr_parse(&s->expr, s->expr_str, var_names, NULL, NULL, |
| | 396 | NULL, NULL, 0, ctx); |
| | 397 | return ret; |
| 362 | } | 398 | } |
| 363 | | 399 | |
| 364 | static inline double fade(double prev, double next, int pos, int length) | 400 | 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 * · 68 unmodified lines |
| 433 | const double peak_magnitude = find_peak_magnitude(frame, channel); | 469 | const double peak_magnitude = find_peak_magnitude(frame, channel); |
| 434 | const double maximum_gain = s->peak_value / peak_magnitude; | 470 | const double maximum_gain = s->peak_value / peak_magnitude; |
| 435 | const double rms_gain = s->target_rms > DBL_EPSILON ? (s->target_rms / compute_frame_rms(frame, channel)) : DBL_MAX; | 471 | const double rms_gain = s->target_rms > DBL_EPSILON ? (s->target_rms / compute_frame_rms(frame, channel)) : DBL_MAX; |
| | 472 | double target_gain = DBL_MAX; |
| 436 | local_gain gain; | 473 | local_gain gain; |
| 437 | | 474 | |
| | 475 | if (s->expr_str) { |
| | 476 | double var_values[VAR_VARS_NB]; |
| | 477 | |
| | 478 | memcpy(var_values, s->var_values, sizeof(var_values)); |
| | 479 | |
| | 480 | var_values[VAR_CH] = channel; |
| | 481 | var_values[VAR_P] = peak_magnitude; |
| | 482 | |
| | 483 | target_gain = av_expr_eval(s->expr, var_values, s) / peak_magnitude; |
| | 484 | } |
| | 485 | |
| 438 | gain.threshold = peak_magnitude > s->threshold; | 486 | gain.threshold = peak_magnitude > s->threshold; |
| 439 | gain.max_gain = bound(s->max_amplification, fmin(maximum_gain, rms_gain)); | 487 | gain.max_gain = bound(s->max_amplification, fmin(target_gain, fmin(maximum_gain, rms_gain))); |
| 440 | | 488 | |
| 441 | return gain; | 489 | return gain; |
| 442 | } | 490 | } |
| @@ -731,6 +779,9 @@ static int analyze_frame(AVFilterContext *ctx, AVFilterLink *outlink, AVFrame ** · 288 unmodified lines |
| 731 | analyze_frame = *frame; | 779 | analyze_frame = *frame; |
| 732 | } | 780 | } |
| 733 | | 781 | |
| | 782 | s->var_values[VAR_SN] = outlink->sample_count_in; |
| | 783 | s->var_values[VAR_T] = s->var_values[VAR_SN] * (double)1/outlink->sample_rate; |
| | 784 | |
| 734 | if (s->channels_coupled) { | 785 | if (s->channels_coupled) { |
| 735 | const local_gain gain = get_max_local_gain(s, analyze_frame, -1); | 786 | const local_gain gain = get_max_local_gain(s, analyze_frame, -1); |
| 736 | for (int c = 0; c < s->channels; c++) | 787 | for (int c = 0; c < s->channels; c++) |
| @@ -951,7 +1002,12 @@ static int process_command(AVFilterContext *ctx, const char *cmd, const char *ar · 214 unmodified lines |
| 951 | | 1002 | |
| 952 | s->frame_len = frame_size(inlink->sample_rate, s->frame_len_msec); | 1003 | s->frame_len = frame_size(inlink->sample_rate, s->frame_len_msec); |
| 953 | s->sample_advance = FFMAX(1, lrint(s->frame_len * (1. - s->overlap))); | 1004 | s->sample_advance = FFMAX(1, lrint(s->frame_len * (1. - s->overlap))); |
| 954 | | 1005 | if (s->expr_str) { |
| | 1006 | ret = av_expr_parse(&s->expr, s->expr_str, var_names, NULL, NULL, |
| | 1007 | NULL, NULL, 0, ctx); |
| | 1008 | if (ret < 0) |
| | 1009 | return ret; |
| | 1010 | } |
| 955 | return 0; | 1011 | return 0; |
| 956 | } | 1012 | } |
| 957 | | 1013 | |