avfilter/af_dynaudnorm: allow to filter subset of channels

This commit is contained in:
Paul B Mahol 2022-02-27 19:37:10 +01:00
parent b9f91a7cbc
commit a9124a75b0
2 changed files with 24 additions and 4 deletions

View File

@ -4389,6 +4389,9 @@ If input frame volume is above this value frame will be normalized.
Otherwise frame may not be normalized at all. The default value is set Otherwise frame may not be normalized at all. The default value is set
to 0, which means all input frames will be normalized. to 0, which means all input frames will be normalized.
This option is mostly useful if digital noise is not wanted to be amplified. This option is mostly useful if digital noise is not wanted to be amplified.
@item channels, h
Specify which channels to filter, by default all available channels are filtered.
@end table @end table
@subsection Commands @subsection Commands

View File

@ -27,6 +27,7 @@
#include <float.h> #include <float.h>
#include "libavutil/avassert.h" #include "libavutil/avassert.h"
#include "libavutil/channel_layout.h"
#include "libavutil/opt.h" #include "libavutil/opt.h"
#define MIN_FILTER_SIZE 3 #define MIN_FILTER_SIZE 3
@ -76,6 +77,7 @@ typedef struct DynamicAudioNormalizerContext {
int channels; int channels;
int eof; int eof;
uint64_t channels_to_filter;
int64_t pts; int64_t pts;
cqueue **gain_history_original; cqueue **gain_history_original;
@ -110,6 +112,8 @@ 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_CHANNEL_LAYOUT, {.i64=-1}, INT64_MIN, INT64_MAX, FLAGS },
{ "h", "set channels to filter", OFFSET(channels_to_filter),AV_OPT_TYPE_CHANNEL_LAYOUT, {.i64=-1}, INT64_MIN, INT64_MAX, FLAGS },
{ NULL } { NULL }
}; };
@ -514,6 +518,11 @@ static inline double update_value(double new, double old, double aggressiveness)
return aggressiveness * new + (1.0 - aggressiveness) * old; return aggressiveness * new + (1.0 - aggressiveness) * old;
} }
static inline int bypass_channel(DynamicAudioNormalizerContext *s, AVFrame *frame, int ch)
{
return !(av_channel_layout_extract_channel(frame->channel_layout, ch) & s->channels_to_filter);
}
static void perform_dc_correction(DynamicAudioNormalizerContext *s, AVFrame *frame) static void perform_dc_correction(DynamicAudioNormalizerContext *s, AVFrame *frame)
{ {
const double diff = 1.0 / frame->nb_samples; const double diff = 1.0 / frame->nb_samples;
@ -521,6 +530,7 @@ static void perform_dc_correction(DynamicAudioNormalizerContext *s, AVFrame *fra
int c, i; int c, i;
for (c = 0; c < s->channels; c++) { for (c = 0; c < s->channels; c++) {
const int bypass = bypass_channel(s, frame, c);
double *dst_ptr = (double *)frame->extended_data[c]; double *dst_ptr = (double *)frame->extended_data[c];
double current_average_value = 0.0; double current_average_value = 0.0;
double prev_value; double prev_value;
@ -531,7 +541,7 @@ static void perform_dc_correction(DynamicAudioNormalizerContext *s, AVFrame *fra
prev_value = is_first_frame ? current_average_value : s->dc_correction_value[c]; prev_value = is_first_frame ? current_average_value : s->dc_correction_value[c];
s->dc_correction_value[c] = is_first_frame ? current_average_value : update_value(current_average_value, s->dc_correction_value[c], 0.1); s->dc_correction_value[c] = is_first_frame ? current_average_value : update_value(current_average_value, s->dc_correction_value[c], 0.1);
for (i = 0; i < frame->nb_samples; i++) { for (i = 0; i < frame->nb_samples && !bypass; i++) {
dst_ptr[i] -= fade(prev_value, s->dc_correction_value[c], i, frame->nb_samples); dst_ptr[i] -= fade(prev_value, s->dc_correction_value[c], i, frame->nb_samples);
} }
} }
@ -604,6 +614,11 @@ static void perform_compression(DynamicAudioNormalizerContext *s, AVFrame *frame
for (c = 0; c < s->channels; c++) { for (c = 0; c < s->channels; c++) {
double *const dst_ptr = (double *)frame->extended_data[c]; double *const dst_ptr = (double *)frame->extended_data[c];
const int bypass = bypass_channel(s, frame, c);
if (bypass)
continue;
for (i = 0; i < frame->nb_samples; i++) { for (i = 0; i < frame->nb_samples; i++) {
const double localThresh = fade(prev_actual_thresh, curr_actual_thresh, i, frame->nb_samples); const double localThresh = fade(prev_actual_thresh, curr_actual_thresh, i, frame->nb_samples);
dst_ptr[i] = copysign(bound(localThresh, fabs(dst_ptr[i])), dst_ptr[i]); dst_ptr[i] = copysign(bound(localThresh, fabs(dst_ptr[i])), dst_ptr[i]);
@ -611,19 +626,20 @@ static void perform_compression(DynamicAudioNormalizerContext *s, AVFrame *frame
} }
} else { } else {
for (c = 0; c < s->channels; c++) { for (c = 0; c < s->channels; c++) {
const int bypass = bypass_channel(s, frame, c);
const double standard_deviation = compute_frame_std_dev(s, frame, c); const double standard_deviation = compute_frame_std_dev(s, frame, c);
const double current_threshold = setup_compress_thresh(FFMIN(1.0, s->compress_factor * standard_deviation)); const double current_threshold = setup_compress_thresh(FFMIN(1.0, s->compress_factor * standard_deviation));
const double prev_value = is_first_frame ? current_threshold : s->compress_threshold[c]; const double prev_value = is_first_frame ? current_threshold : s->compress_threshold[c];
double prev_actual_thresh, curr_actual_thresh; double prev_actual_thresh, curr_actual_thresh;
double *dst_ptr; double *dst_ptr;
s->compress_threshold[c] = is_first_frame ? current_threshold : update_value(current_threshold, s->compress_threshold[c], 1.0/3.0); s->compress_threshold[c] = is_first_frame ? current_threshold : update_value(current_threshold, s->compress_threshold[c], 1.0/3.0);
prev_actual_thresh = setup_compress_thresh(prev_value); prev_actual_thresh = setup_compress_thresh(prev_value);
curr_actual_thresh = setup_compress_thresh(s->compress_threshold[c]); curr_actual_thresh = setup_compress_thresh(s->compress_threshold[c]);
dst_ptr = (double *)frame->extended_data[c]; dst_ptr = (double *)frame->extended_data[c];
for (i = 0; i < frame->nb_samples; i++) { for (i = 0; i < frame->nb_samples && !bypass; i++) {
const double localThresh = fade(prev_actual_thresh, curr_actual_thresh, i, frame->nb_samples); const double localThresh = fade(prev_actual_thresh, curr_actual_thresh, i, frame->nb_samples);
dst_ptr[i] = copysign(bound(localThresh, fabs(dst_ptr[i])), dst_ptr[i]); dst_ptr[i] = copysign(bound(localThresh, fabs(dst_ptr[i])), dst_ptr[i]);
} }
@ -668,13 +684,14 @@ static void amplify_frame(DynamicAudioNormalizerContext *s, AVFrame *in,
int c, i; int c, i;
for (c = 0; c < s->channels; c++) { for (c = 0; c < s->channels; c++) {
const int bypass = bypass_channel(s, frame, c);
const double *src_ptr = (const double *)in->extended_data[c]; const double *src_ptr = (const double *)in->extended_data[c];
double *dst_ptr = (double *)frame->extended_data[c]; double *dst_ptr = (double *)frame->extended_data[c];
double current_amplification_factor; double current_amplification_factor;
cqueue_dequeue(s->gain_history_smoothed[c], &current_amplification_factor); cqueue_dequeue(s->gain_history_smoothed[c], &current_amplification_factor);
for (i = 0; i < frame->nb_samples && enabled; i++) { for (i = 0; i < frame->nb_samples && enabled && !bypass; i++) {
const double amplification_factor = fade(s->prev_amplification_factor[c], const double amplification_factor = fade(s->prev_amplification_factor[c],
current_amplification_factor, i, current_amplification_factor, i,
frame->nb_samples); frame->nb_samples);