diff --git a/README.md b/README.md index ca0a38b..d39bdc0 100644 --- a/README.md +++ b/README.md @@ -224,6 +224,13 @@ Some containers (like MP4) also cannot handle PCM audio. If you want to use such - `--keep-loudness-range-target`: Keep the input loudness range target to allow for linear normalization. +- `--keep-lra-above-loudness-range-target`: Keep input loudness range above loudness range target. + + - `LOUDNESS_RANGE_TARGET` for input loudness range `<= LOUDNESS_RANGE_TARGET` or + - keep input loudness range target above `LOUDNESS_RANGE_TARGET`. + + as alternative to `--keep-loudness-range-target` to allow for linear normalization. + - `-tp TRUE_PEAK, --true-peak TRUE_PEAK`: EBU Maximum True Peak in dBTP (default: -2.0). Range is -9.0 - +0.0. diff --git a/ffmpeg_normalize/__main__.py b/ffmpeg_normalize/__main__.py index 7194007..14a37dc 100644 --- a/ffmpeg_normalize/__main__.py +++ b/ffmpeg_normalize/__main__.py @@ -194,6 +194,19 @@ def create_parser() -> argparse.ArgumentParser: ), ) + group_ebu.add_argument( + "--keep-lra-above-loudness-range-target", + action="store_true", + help=textwrap.dedent( + """\ + Keep input loudness range above loudness range target. + - `LOUDNESS_RANGE_TARGET` for input loudness range `<= LOUDNESS_RANGE_TARGET` or + - keep input loudness range target above `LOUDNESS_RANGE_TARGET`. + as alternative to `--keep-loudness-range-target` to allow for linear normalization. + """ + ), + ) + group_ebu.add_argument( "-tp", "--true-peak", @@ -486,6 +499,7 @@ def _split_options(opts: str) -> list[str]: loudness_range_target=cli_args.loudness_range_target, # threshold=cli_args.threshold, keep_loudness_range_target=cli_args.keep_loudness_range_target, + keep_lra_above_loudness_range_target=cli_args.keep_lra_above_loudness_range_target, true_peak=cli_args.true_peak, offset=cli_args.offset, dual_mono=cli_args.dual_mono, diff --git a/ffmpeg_normalize/_ffmpeg_normalize.py b/ffmpeg_normalize/_ffmpeg_normalize.py index af636ea..2b56b32 100644 --- a/ffmpeg_normalize/_ffmpeg_normalize.py +++ b/ffmpeg_normalize/_ffmpeg_normalize.py @@ -55,6 +55,7 @@ class FFmpegNormalize: print_stats (bool, optional): Print loudnorm stats. Defaults to False. loudness_range_target (float, optional): Loudness range target. Defaults to 7.0. keep_loudness_range_target (bool, optional): Keep loudness range target. Defaults to False. + keep_lra_above_loudness_range_target (bool, optional): Keep input loudness range above loudness range target. Defaults to False. true_peak (float, optional): True peak. Defaults to -2.0. offset (float, optional): Offset. Defaults to 0.0. dual_mono (bool, optional): Dual mono. Defaults to False. @@ -89,6 +90,7 @@ def __init__( # threshold=0.5, loudness_range_target: float = 7.0, keep_loudness_range_target: bool = False, + keep_lra_above_loudness_range_target: bool = False, true_peak: float = -2.0, offset: float = 0.0, dual_mono: bool = False, @@ -147,6 +149,14 @@ def __init__( "Remove --keep-loudness-range-target or remove the --lrt/--loudness-range-target option." ) + self.keep_lra_above_loudness_range_target = keep_lra_above_loudness_range_target + + if self.keep_loudness_range_target and self.keep_lra_above_loudness_range_target: + raise FFmpegNormalizeError( + "Options --keep-loudness-range-target and --keep-lra-above-loudness-range-target are partially contradictory! " + "Please choose just one of the two options" + ) + self.true_peak = check_range(true_peak, -9, 0, name="true_peak") self.offset = check_range(offset, -99, 99, name="offset") diff --git a/ffmpeg_normalize/_streams.py b/ffmpeg_normalize/_streams.py index 4d71a34..ee47064 100644 --- a/ffmpeg_normalize/_streams.py +++ b/ffmpeg_normalize/_streams.py @@ -411,6 +411,19 @@ def get_second_pass_opts_ebu(self) -> str: self.loudness_statistics["ebu"]["input_lra"] ) + if self.media_file.ffmpeg_normalize.keep_lra_above_loudness_range_target: + if self.loudness_statistics["ebu"]["input_lra"] <= self.media_file.ffmpeg_normalize.loudness_range_target: + _logger.debug( + "Setting loudness range target in second pass loudnorm filter" + ) + else: + self.media_file.ffmpeg_normalize.loudness_range_target = ( + self.loudness_statistics["ebu"]["input_lra"] + ) + _logger.debug( + "Keeping target loudness range in second pass loudnorm filter" + ) + if ( self.media_file.ffmpeg_normalize.loudness_range_target < self.loudness_statistics["ebu"]["input_lra"] @@ -420,7 +433,8 @@ def get_second_pass_opts_ebu(self) -> str: f"Input file had loudness range of {self.loudness_statistics['ebu']['input_lra']}. " f"This is larger than the loudness range target ({self.media_file.ffmpeg_normalize.loudness_range_target}). " "Normalization will revert to dynamic mode. Choose a higher target loudness range if you want linear normalization. " - "Alternatively, use the --keep-loudness-range-target option to keep the target loudness range from the input." + "Alternatively, use the --keep-loudness-range-target or --keep-lra-above-loudness-range-target option to keep the target loudness range from " + "the input." ) will_use_dynamic_mode = True