lazymaster.py (1886B)
1 #!/usr/bin/env python3 2 import subprocess 3 import sys 4 import json 5 from typing import cast 6 7 8 def main(): 9 if len(sys.argv) < 2: 10 print("lazymaster input.wav [output.wav]") 11 sys.exit(1) 12 13 input_file = sys.argv[1] 14 15 # Measure loudness 16 cmd_measure = [ 17 "ffmpeg", 18 "-i", 19 input_file, 20 "-af", 21 "loudnorm=I=-13:TP=-1:LRA=8:print_format=json", 22 "-f", 23 "null", 24 "-", 25 ] 26 27 result = subprocess.run(cmd_measure, capture_output=True, text=True) 28 29 # Parse output values 30 try: 31 stderr_output = result.stderr 32 json_start = stderr_output.find("{") 33 json_end = stderr_output.rfind("}") + 1 34 if json_start == -1 or json_end == -1: 35 raise ValueError("json output is missing") 36 37 stats = cast(dict[str, str], json.loads(stderr_output[json_start:json_end])) 38 39 measured_i = stats["input_i"] 40 measured_tp = stats["input_tp"] 41 measured_lra = stats["input_lra"] 42 measured_thresh = stats["input_thresh"] 43 offset = stats["target_offset"] 44 except (ValueError, json.JSONDecodeError, KeyError) as e: 45 print(f"error parsing loudness stats from ffmpeg output {e}") 46 sys.exit(1) 47 48 print( 49 f"I={measured_i}, TP={measured_tp}, LRA={measured_lra}, Thresh={measured_thresh}, Offset={offset}" 50 ) 51 52 # Normalize loudness 53 if len(sys.argv) > 2: 54 output_file = sys.argv[2] 55 cmd_normalize = [ 56 "ffmpeg", 57 "-i", 58 input_file, 59 "-af", 60 f"loudnorm=I=-13:TP=-1:LRA=8:measured_I={measured_i}:measured_LRA={measured_lra}:measured_TP={measured_tp}:measured_thresh={measured_thresh}:offset={offset}:linear=true", 61 "-y", 62 output_file, 63 ] 64 65 _ = subprocess.run(cmd_normalize, check=True) 66 67 68 if __name__ == "__main__": 69 main()