feat: add independent volume controls and refine audio filters
This commit is contained in:
1 parent
514158c29b
commit
e61c4a449d
2 files changed
+32
-18
No files matched your search
+4
-4
@@ -323,7 +323,7 @@ def extract_audio_loudnorm(input_file: str, duration: int, start_sec: float,
|
|||||||
map_args = ["-map", f"0:a:{stream_idx}"] if stream_idx is not None else []
|
map_args = ["-map", f"0:a:{stream_idx}"] if stream_idx is not None else []
|
||||||
base = ["ffmpeg", "-y", *ss_args, "-i", input_file, "-t", str(duration), "-vn", *map_args]
|
base = ["ffmpeg", "-y", *ss_args, "-i", input_file, "-t", str(duration), "-vn", *map_args]
|
||||||
measure_af = f"loudnorm=I={TARGET_LUFS}:TP={TARGET_TP}:LRA={TARGET_LRA}:print_format=json"
|
measure_af = f"loudnorm=I={TARGET_LUFS}:TP={TARGET_TP}:LRA={TARGET_LRA}:print_format=json"
|
||||||
gate_filter = ",lowpass=f=3000,agate=threshold=0.02:ratio=20:attack=5:release=50" if loud_gate else ""
|
gate_filter = ",lowpass=f=2000,highpass=f=100,agate=threshold=0.02:ratio=20:attack=5:release=50" if loud_gate else ""
|
||||||
|
|
||||||
# ─── Pass 1: measure ───
|
# ─── Pass 1: measure ───
|
||||||
print(f"[loudness] {stem}: pass 1/2 measuring…")
|
print(f"[loudness] {stem}: pass 1/2 measuring…")
|
||||||
@@ -332,7 +332,7 @@ def extract_audio_loudnorm(input_file: str, duration: int, start_sec: float,
|
|||||||
if r.returncode != 0:
|
if r.returncode != 0:
|
||||||
print(f"[loudness] {stem}: pass 1 failed ({r.stderr.strip()[-80:]}), falling back to simple extract", file=sys.stderr)
|
print(f"[loudness] {stem}: pass 1 failed ({r.stderr.strip()[-80:]}), falling back to simple extract", file=sys.stderr)
|
||||||
if loud_gate:
|
if loud_gate:
|
||||||
cmd_fallback = base + ["-af", "lowpass=f=3000,agate=threshold=0.02:ratio=20:attack=5:release=50", *AUDIO_CODEC, out_path]
|
cmd_fallback = base + ["-af", "lowpass=f=2000,highpass=f=100,agate=threshold=0.02:ratio=20:attack=5:release=50", *AUDIO_CODEC, out_path]
|
||||||
else:
|
else:
|
||||||
cmd_fallback = base + [*AUDIO_CODEC, out_path]
|
cmd_fallback = base + [*AUDIO_CODEC, out_path]
|
||||||
r2 = subprocess.run(cmd_fallback, capture_output=True, text=True)
|
r2 = subprocess.run(cmd_fallback, capture_output=True, text=True)
|
||||||
@@ -344,7 +344,7 @@ def extract_audio_loudnorm(input_file: str, duration: int, start_sec: float,
|
|||||||
if measured is None:
|
if measured is None:
|
||||||
print(f"[loudness] {stem}: could not parse measurement, falling back to simple extract", file=sys.stderr)
|
print(f"[loudness] {stem}: could not parse measurement, falling back to simple extract", file=sys.stderr)
|
||||||
if loud_gate:
|
if loud_gate:
|
||||||
cmd_fallback = base + ["-af", "lowpass=f=3000,agate=threshold=0.02:ratio=20:attack=5:release=50", *AUDIO_CODEC, out_path]
|
cmd_fallback = base + ["-af", "lowpass=f=2000,highpass=f=100,agate=threshold=0.02:ratio=20:attack=5:release=50", *AUDIO_CODEC, out_path]
|
||||||
else:
|
else:
|
||||||
cmd_fallback = base + [*AUDIO_CODEC, out_path]
|
cmd_fallback = base + [*AUDIO_CODEC, out_path]
|
||||||
r2 = subprocess.run(cmd_fallback, capture_output=True, text=True)
|
r2 = subprocess.run(cmd_fallback, capture_output=True, text=True)
|
||||||
@@ -495,7 +495,7 @@ for _file_idx, INPUT_FILE in enumerate(INPUT_FILES):
|
|||||||
cmd_audio = [
|
cmd_audio = [
|
||||||
"ffmpeg", "-y", *ss_args, "-i", INPUT_FILE, "-t", str(DURATION),
|
"ffmpeg", "-y", *ss_args, "-i", INPUT_FILE, "-t", str(DURATION),
|
||||||
"-vn", *map_args, "-af",
|
"-vn", *map_args, "-af",
|
||||||
"lowpass=f=3000,agate=threshold=0.02:ratio=20:attack=5:release=50",
|
"lowpass=f=2000,highpass=f=100,agate=threshold=0.02:ratio=20:attack=5:release=50",
|
||||||
*AUDIO_CODEC, audio_out
|
*AUDIO_CODEC, audio_out
|
||||||
]
|
]
|
||||||
else:
|
else:
|
||||||
|
|||||||
+28
-14
@@ -428,16 +428,29 @@ class VerifyWindow(QtWidgets.QMainWindow):
|
|||||||
self.play_mode.setFixedWidth(90)
|
self.play_mode.setFixedWidth(90)
|
||||||
ctrl.addWidget(self.play_mode)
|
ctrl.addWidget(self.play_mode)
|
||||||
|
|
||||||
ctrl.addWidget(QtWidgets.QLabel("Vol:"))
|
ctrl.addSpacing(10)
|
||||||
self.vol_slider = QtWidgets.QSlider(QtCore.Qt.Orientation.Horizontal)
|
|
||||||
self.vol_slider.setRange(0, 100)
|
ctrl.addWidget(QtWidgets.QLabel("Vol A:"))
|
||||||
self.vol_slider.setValue(self.default_volume)
|
self.vol_a_slider = QtWidgets.QSlider(QtCore.Qt.Orientation.Horizontal)
|
||||||
self.vol_slider.setMaximumWidth(100)
|
self.vol_a_slider.setRange(0, 200)
|
||||||
ctrl.addWidget(self.vol_slider)
|
self.vol_a_slider.setValue(self.default_volume)
|
||||||
self.vol_label = QtWidgets.QLabel(f"{self.default_volume}%")
|
self.vol_a_slider.setMaximumWidth(100)
|
||||||
self.vol_label.setMinimumWidth(35)
|
ctrl.addWidget(self.vol_a_slider)
|
||||||
ctrl.addWidget(self.vol_label)
|
self.vol_a_label = QtWidgets.QLabel(f"{self.default_volume}%")
|
||||||
self.vol_slider.valueChanged.connect(lambda v: self.vol_label.setText(f"{v}%"))
|
self.vol_a_label.setMinimumWidth(35)
|
||||||
|
ctrl.addWidget(self.vol_a_label)
|
||||||
|
self.vol_a_slider.valueChanged.connect(lambda v: self.vol_a_label.setText(f"{v}%"))
|
||||||
|
|
||||||
|
ctrl.addWidget(QtWidgets.QLabel("Vol B:"))
|
||||||
|
self.vol_b_slider = QtWidgets.QSlider(QtCore.Qt.Orientation.Horizontal)
|
||||||
|
self.vol_b_slider.setRange(0, 200)
|
||||||
|
self.vol_b_slider.setValue(self.default_volume)
|
||||||
|
self.vol_b_slider.setMaximumWidth(100)
|
||||||
|
ctrl.addWidget(self.vol_b_slider)
|
||||||
|
self.vol_b_label = QtWidgets.QLabel(f"{self.default_volume}%")
|
||||||
|
self.vol_b_label.setMinimumWidth(35)
|
||||||
|
ctrl.addWidget(self.vol_b_label)
|
||||||
|
self.vol_b_slider.valueChanged.connect(lambda v: self.vol_b_label.setText(f"{v}%"))
|
||||||
|
|
||||||
ctrl.addSpacing(10)
|
ctrl.addSpacing(10)
|
||||||
|
|
||||||
@@ -578,16 +591,17 @@ class VerifyWindow(QtWidgets.QMainWindow):
|
|||||||
|
|
||||||
a_seg = self.a_aligned[start_samp:]
|
a_seg = self.a_aligned[start_samp:]
|
||||||
b_seg = self.b_aligned[start_samp:]
|
b_seg = self.b_aligned[start_samp:]
|
||||||
vol = self.vol_slider.value() / 100.0
|
vol_a = self.vol_a_slider.value() / 100.0
|
||||||
|
vol_b = self.vol_b_slider.value() / 100.0
|
||||||
|
|
||||||
mode = self.play_mode.currentIndex()
|
mode = self.play_mode.currentIndex()
|
||||||
if mode == 0: # A+B mix
|
if mode == 0: # A+B mix
|
||||||
ml = min(len(a_seg), len(b_seg))
|
ml = min(len(a_seg), len(b_seg))
|
||||||
mix = ((a_seg[:ml] + b_seg[:ml]) / 2.0) * vol
|
mix = (a_seg[:ml] * vol_a + b_seg[:ml] * vol_b) / 2.0
|
||||||
elif mode == 1: # A only
|
elif mode == 1: # A only
|
||||||
mix = a_seg * vol
|
mix = a_seg * vol_a
|
||||||
else: # B only
|
else: # B only
|
||||||
mix = b_seg * vol
|
mix = b_seg * vol_b
|
||||||
|
|
||||||
self.audio_player.play(mix, self.sr)
|
self.audio_player.play(mix, self.sr)
|
||||||
self.playing_a_b = True
|
self.playing_a_b = True
|
||||||
|
|||||||
Reference in new issue
Block a user