Skip to content

Commit 9833bc6

Browse files
committed
feat: improved warning messages for dynamic reversion, fixes #314
1 parent fb184d9 commit 9833bc6

2 files changed

Lines changed: 61 additions & 4 deletions

File tree

src/ffmpeg_normalize/_media_file.py

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -880,17 +880,50 @@ def _second_pass(self) -> Iterator[float]:
880880
"This can happen when normalization is skipped (e.g., with --lower-only)."
881881
)
882882

883-
# warn if self.media_file.ffmpeg_normalize.dynamic == False and any of the second pass stats contain "normalization_type" == "dynamic"
883+
# warn if dynamic == False and any of the second pass stats contain "normalization_type" == "dynamic"
884884
if self.ffmpeg_normalize.dynamic is False:
885885
for audio_stream in self.streams["audio"].values():
886886
pass2_stats = audio_stream.get_stats()["ebu_pass2"]
887887
if pass2_stats is None:
888888
continue
889889
if pass2_stats["normalization_type"] == "dynamic":
890+
pass1_stats = audio_stream.get_stats()["ebu_pass1"]
891+
892+
reason = ""
893+
if pass1_stats is not None:
894+
linear_gain = (
895+
self.ffmpeg_normalize.target_level - pass1_stats["input_i"]
896+
)
897+
estimated_tp = pass1_stats["input_tp"] + linear_gain
898+
if estimated_tp > self.ffmpeg_normalize.true_peak:
899+
min_tp = estimated_tp
900+
max_target = (
901+
self.ffmpeg_normalize.true_peak
902+
- pass1_stats["input_tp"]
903+
+ pass1_stats["input_i"]
904+
)
905+
reason = (
906+
f" Reason: the input true peak ({pass1_stats['input_tp']:.2f} dBTP) is too high — "
907+
f"after linear gain of {linear_gain:.2f} dB, "
908+
f"the estimated true peak would be {estimated_tp:.2f} dBTP, "
909+
f"exceeding the target of {self.ffmpeg_normalize.true_peak} dBTP. "
910+
f"To avoid this, raise --true-peak (-tp) to at least {min_tp:.1f}, "
911+
f"or lower the target level (-t) to at most {max_target:.1f}."
912+
)
913+
elif (
914+
pass1_stats["input_lra"]
915+
> self.ffmpeg_normalize.loudness_range_target
916+
):
917+
reason = (
918+
f" Reason: the input loudness range ({pass1_stats['input_lra']:.2f} LU) "
919+
f"exceeds the target ({self.ffmpeg_normalize.loudness_range_target:.2f} LU). "
920+
"Consider raising the target loudness range or using "
921+
"--keep-loudness-range-target / --keep-lra-above-loudness-range-target."
922+
)
923+
890924
_logger.warning(
891-
f"{self.input_file}: You specified linear normalization, but the loudnorm filter reverted to dynamic normalization. "
892-
"This may lead to unexpected results. "
893-
"Consider your input settings, e.g. choose a lower target level or higher target loudness range."
925+
f"{self.input_file}: You specified linear normalization, but the loudnorm filter "
926+
f"reverted to dynamic normalization.{reason}"
894927
)
895928

896929
_logger.debug("Normalization finished")

src/ffmpeg_normalize/_streams.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -557,6 +557,30 @@ def get_second_pass_opts_ebu(self, batch_reference: float | None = None) -> str:
557557

558558
stats = self.loudness_statistics["ebu_pass1"]
559559

560+
# Check if the true peak constraint will force dynamic mode.
561+
# In linear mode, a uniform gain of (target - input_i) is applied.
562+
# If that would leave the true peak above the TP limit, loudnorm
563+
# cannot satisfy both constraints linearly and will fall back to
564+
# dynamic processing.
565+
if not will_use_dynamic_mode:
566+
linear_gain = target_level - stats["input_i"]
567+
estimated_tp = stats["input_tp"] + linear_gain
568+
if estimated_tp > self.media_file.ffmpeg_normalize.true_peak:
569+
min_tp = estimated_tp
570+
max_target = (
571+
self.media_file.ffmpeg_normalize.true_peak
572+
- stats["input_tp"]
573+
+ stats["input_i"]
574+
)
575+
_logger.warning(
576+
f"{self.media_file.input_file}: Linear normalization would result in an estimated true peak of "
577+
f"{estimated_tp:.2f} dBTP (input true peak {stats['input_tp']:.2f} dBTP + gain of {linear_gain:.2f} dB), "
578+
f"which exceeds the target true peak of {self.media_file.ffmpeg_normalize.true_peak} dBTP. "
579+
"The loudnorm filter will likely use dynamic mode instead. "
580+
f"To avoid this, raise --true-peak (-tp) to at least {min_tp:.1f}, "
581+
f"or lower the target level (-t) to at most {max_target:.1f}."
582+
)
583+
560584
# Adjust target level for batch mode to preserve relative loudness
561585
if batch_reference is not None:
562586
input_i = float(stats["input_i"])

0 commit comments

Comments
 (0)