You've already forked FFmpeg
							
							
				mirror of
				https://github.com/FFmpeg/FFmpeg.git
				synced 2025-10-30 23:18:11 +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 | ||||
| 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 | ||||
|   | ||||
| @@ -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; | ||||
| } | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user