Add pipeline code
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
"""Measure audio with ffmpeg: RMS and peak per 50 ms window. The checker and the assembler both use this."""
|
||||
import math
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import tempfile
|
||||
|
||||
|
||||
def ffmpeg(args):
|
||||
r = subprocess.run(["ffmpeg", "-nostdin", "-hide_banner", "-loglevel", "error", "-y"] + args, capture_output=True, text=True)
|
||||
if r.returncode != 0:
|
||||
raise RuntimeError("ffmpeg failed: " + r.stderr.strip()[-500:])
|
||||
|
||||
|
||||
def probe(path):
|
||||
out = subprocess.run(["ffprobe", "-v", "error", "-select_streams", "a:0", "-show_entries",
|
||||
"stream=codec_name,sample_rate,channels,bit_rate:format=duration", "-of", "default=nw=1", path],
|
||||
capture_output=True, text=True, check=True).stdout
|
||||
return dict(line.split("=", 1) for line in out.strip().splitlines())
|
||||
|
||||
|
||||
def windows(path):
|
||||
"""[(rms_db, peak_db)] for consecutive 50 ms windows of the decoded file."""
|
||||
sr = int(probe(path)["sample_rate"])
|
||||
n = sr // 20
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
rms_file, peak_file = os.path.join(tmp, "rms.txt"), os.path.join(tmp, "peak.txt")
|
||||
af = ("asetnsamples=n=%d:p=0,astats=metadata=1:reset=1,"
|
||||
"ametadata=print:key=lavfi.astats.Overall.RMS_level:file=%s,"
|
||||
"ametadata=print:key=lavfi.astats.Overall.Peak_level:file=%s" % (n, rms_file, peak_file))
|
||||
r = subprocess.run(["ffmpeg", "-nostdin", "-hide_banner", "-loglevel", "error", "-i", path, "-af", af, "-f", "null", "-"],
|
||||
capture_output=True, text=True)
|
||||
if r.returncode != 0:
|
||||
raise RuntimeError("ffmpeg failed: " + r.stderr.strip()[-500:])
|
||||
read = lambda p, key: [float(v) for v in re.findall(key + r"=(-?[\d.]+|-inf|inf)", open(p).read())]
|
||||
rms, peak = read(rms_file, "RMS_level"), read(peak_file, "Peak_level")
|
||||
return list(zip(rms, peak))
|
||||
|
||||
|
||||
def db_mean_power(values):
|
||||
"""Overall level in dB of equal-length windows given as dB RMS."""
|
||||
if not values:
|
||||
return float("-inf")
|
||||
p = sum(10 ** (v / 10) for v in values) / len(values)
|
||||
return 10 * math.log10(p) if p > 0 else float("-inf")
|
||||
|
||||
|
||||
def overall(path):
|
||||
"""(rms_db, peak_db) of the whole file."""
|
||||
w = windows(path)
|
||||
return db_mean_power([r for r, _ in w]), max(p for _, p in w)
|
||||
Reference in New Issue
Block a user