57 lines
2.3 KiB
Python
Executable File
57 lines
2.3 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# ffmpeg-album-normalise: Normalise music album-by-album using FFmpeg loudnorm
|
|
# Copyright © 2019 Lee Yingtong Li (RunasSudo)
|
|
#
|
|
# This program is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU Affero General Public License as published by
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU Affero General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU Affero General Public License
|
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
import argparse
|
|
import json
|
|
import math
|
|
import os.path
|
|
import subprocess
|
|
|
|
parser = argparse.ArgumentParser(description='Normalise audio files using FFmpeg loudnorm')
|
|
parser.add_argument('filename', nargs='+', help='Input files')
|
|
parser.add_argument('--loudness', '-i', default=-24, type=float, help='Target loudness')
|
|
parser.add_argument('--outdir', '-o', default='.', help='Output directory')
|
|
parser.add_argument('--outfmt', '-t', default='wav', help='Output file format suffix')
|
|
parser.add_argument('--levels', '-l', default='levels.json', help='levels.json output file')
|
|
parser.add_argument('--args', '-a', default='', help='Additional arguments to pass to FFmpeg')
|
|
args = parser.parse_args()
|
|
|
|
with open(args.levels, 'r') as f:
|
|
levels = json.load(f)
|
|
|
|
avg_i = 0
|
|
for _, level in levels.items():
|
|
avg_i += 10**(float(level['input_i'])*10)
|
|
avg_i /= len(levels)
|
|
avg_i = math.log10(avg_i)/10
|
|
|
|
for filename in args.filename:
|
|
print(filename)
|
|
|
|
target_loudness = args.loudness + (float(levels[filename]['input_i']) - avg_i)
|
|
|
|
cmd = ['ffmpeg', '-i', filename, '-filter:a']
|
|
cmd.append('loudnorm=i={}:measured_i={}:measured_lra={}:measured_tp={}:measured_thresh={}:linear=true'.format(target_loudness, levels[filename]['input_i'], levels[filename]['input_lra'], levels[filename]['input_tp'], levels[filename]['input_thresh']))
|
|
cmd.extend(args.args.split())
|
|
cmd.append(os.path.join(args.outdir, os.path.splitext(filename)[0] + '.' + args.outfmt))
|
|
|
|
proc = subprocess.run(cmd)
|
|
|
|
if proc.returncode != 0:
|
|
print('FFmpeg error')
|
|
sys.exit(1)
|