FFmpeg
normalize.py
Go to the documentation of this file.
1 #!/usr/bin/env python3
2 
3 import argparse
4 import logging
5 import shlex
6 import subprocess
7 
8 HELP = '''
9 Normalize audio input.
10 
11 The command uses ffprobe to analyze an input file with the ebur128
12 filter, and finally run ffmpeg to normalize the input depending on the
13 computed adjustment.
14 
15 ffmpeg encoding arguments can be passed through the extra arguments
16 after options, for example as in:
17 normalize.py --input input.mp3 --output output.mp3 -- -loglevel debug -y
18 '''
19 
20 logging.basicConfig(format='normalize|%(levelname)s> %(message)s', level=logging.INFO)
21 log = logging.getLogger()
22 
23 
24 class Formatter(
25  argparse.ArgumentDefaultsHelpFormatter, argparse.RawDescriptionHelpFormatter
26 ):
27  pass
28 
29 
30 def normalize():
31  parser = argparse.ArgumentParser(description=HELP, formatter_class=Formatter)
32  parser.add_argument('--input', '-i', required=True, help='specify input file')
33  parser.add_argument('--output', '-o', required=True, help='specify output file')
34  parser.add_argument('--dry-run', '-n', help='simulate commands', action='store_true')
35  parser.add_argument('encode_arguments', nargs='*', help='specify encode options used for the actual encoding')
36 
37  args = parser.parse_args()
38 
39  analysis_cmd = [
40  'ffprobe', '-v', 'error', '-of', 'compact=p=0:nk=1',
41  '-show_entries', 'frame_tags=lavfi.r128.I', '-f', 'lavfi',
42  f"amovie='{args.input}',ebur128=metadata=1"
43  ]
44 
45  def _run_command(cmd, dry_run=False):
46  log.info(f"Running command:\n$ {shlex.join(cmd)}")
47  if not dry_run:
48  result = subprocess.run(cmd, check=True, stdout=subprocess.PIPE)
49  return result
50 
51  result = _run_command(analysis_cmd)
52 
53  loudness = ref = -23
54  for line in result.stdout.splitlines():
55  sline = line.rstrip()
56  if sline:
57  loudness = sline
58 
59  adjust = ref - float(loudness)
60  if abs(adjust) < 0.0001:
61  logging.info(f"No normalization needed for '{args.input}'")
62  return
63 
64  logging.info(f"Adjusting '{args.input}' by {adjust:.2f}dB...")
65  normalize_cmd = [
66  'ffmpeg', '-i', args.input, '-af', f'volume={adjust:.2f}dB'
67  ] + args.encode_arguments + [args.output]
68 
69  _run_command(normalize_cmd, args.dry_run)
70 
71 
72 if __name__ == '__main__':
73  normalize()
normalize.Formatter
Definition: normalize.py:26
normalize.normalize
def normalize()
Definition: normalize.py:30
float
float
Definition: af_crystalizer.c:121
abs
#define abs(x)
Definition: cuda_runtime.h:35
normalize
Definition: normalize.py:1