ffmpeg-normalize 1.37.5__tar.gz → 1.37.6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (17) hide show
  1. {ffmpeg_normalize-1.37.5 → ffmpeg_normalize-1.37.6}/PKG-INFO +1 -1
  2. {ffmpeg_normalize-1.37.5 → ffmpeg_normalize-1.37.6}/pyproject.toml +1 -1
  3. {ffmpeg_normalize-1.37.5 → ffmpeg_normalize-1.37.6}/src/ffmpeg_normalize/_media_file.py +37 -4
  4. {ffmpeg_normalize-1.37.5 → ffmpeg_normalize-1.37.6}/src/ffmpeg_normalize/_streams.py +24 -0
  5. {ffmpeg_normalize-1.37.5 → ffmpeg_normalize-1.37.6}/LICENSE.md +0 -0
  6. {ffmpeg_normalize-1.37.5 → ffmpeg_normalize-1.37.6}/README.md +0 -0
  7. {ffmpeg_normalize-1.37.5 → ffmpeg_normalize-1.37.6}/src/ffmpeg_normalize/__init__.py +0 -0
  8. {ffmpeg_normalize-1.37.5 → ffmpeg_normalize-1.37.6}/src/ffmpeg_normalize/__main__.py +0 -0
  9. {ffmpeg_normalize-1.37.5 → ffmpeg_normalize-1.37.6}/src/ffmpeg_normalize/_cmd_utils.py +0 -0
  10. {ffmpeg_normalize-1.37.5 → ffmpeg_normalize-1.37.6}/src/ffmpeg_normalize/_errors.py +0 -0
  11. {ffmpeg_normalize-1.37.5 → ffmpeg_normalize-1.37.6}/src/ffmpeg_normalize/_ffmpeg_normalize.py +0 -0
  12. {ffmpeg_normalize-1.37.5 → ffmpeg_normalize-1.37.6}/src/ffmpeg_normalize/_logger.py +0 -0
  13. {ffmpeg_normalize-1.37.5 → ffmpeg_normalize-1.37.6}/src/ffmpeg_normalize/_presets.py +0 -0
  14. {ffmpeg_normalize-1.37.5 → ffmpeg_normalize-1.37.6}/src/ffmpeg_normalize/data/presets/music.json +0 -0
  15. {ffmpeg_normalize-1.37.5 → ffmpeg_normalize-1.37.6}/src/ffmpeg_normalize/data/presets/podcast.json +0 -0
  16. {ffmpeg_normalize-1.37.5 → ffmpeg_normalize-1.37.6}/src/ffmpeg_normalize/data/presets/streaming-video.json +0 -0
  17. {ffmpeg_normalize-1.37.5 → ffmpeg_normalize-1.37.6}/src/ffmpeg_normalize/py.typed +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ffmpeg-normalize
3
- Version: 1.37.5
3
+ Version: 1.37.6
4
4
  Summary: Normalize audio via ffmpeg
5
5
  Keywords: ffmpeg,normalize,audio
6
6
  Author: Werner Robitza
@@ -4,7 +4,7 @@ build-backend = "uv_build"
4
4
 
5
5
  [project]
6
6
  name = "ffmpeg-normalize"
7
- version = "1.37.5"
7
+ version = "1.37.6"
8
8
  description = "Normalize audio via ffmpeg"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -880,17 +880,50 @@ class MediaFile:
880
880
  "This can happen when normalization is skipped (e.g., with --lower-only)."
881
881
  )
882
882
 
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"
884
884
  if self.ffmpeg_normalize.dynamic is False:
885
885
  for audio_stream in self.streams["audio"].values():
886
886
  pass2_stats = audio_stream.get_stats()["ebu_pass2"]
887
887
  if pass2_stats is None:
888
888
  continue
889
889
  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
+
890
924
  _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}"
894
927
  )
895
928
 
896
929
  _logger.debug("Normalization finished")
@@ -557,6 +557,30 @@ class AudioStream(MediaStream):
557
557
 
558
558
  stats = self.loudness_statistics["ebu_pass1"]
559
559
 
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
+
560
584
  # Adjust target level for batch mode to preserve relative loudness
561
585
  if batch_reference is not None:
562
586
  input_i = float(stats["input_i"])