Files

132 lines
5.1 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""界面回归检查之二:对话框的后台任务链路(QThread + 信号)真跑一遍。
自检覆盖了 process_file / export_audio 这些纯函数,但没有覆盖
"Qt 工作线程 → 信号 → 界面回填"这段管线。这里直接构造对话框、
触发 _run()、泵事件循环直到完成,再检查产物是否真的生成。
"""
import os
import sys
import time
os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, ROOT)
import numpy as np
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QApplication
QApplication.setAttribute(Qt.AA_EnableHighDpiScaling, True)
app = QApplication([])
from recorder.gui import QSS # noqa: E402
app.setStyleSheet(QSS)
from recorder import dsp # noqa: E402
from recorder.wavfile import WavReader, write_wav # noqa: E402
from recorder.window import ExportDialog, ProcessDialog # noqa: E402
fails = []
def check(name, ok, detail=""):
print((" ✓ " if ok else " ✗ ") + name + ((" " + detail) if detail else ""))
if not ok:
fails.append(name)
def pump(seconds=1.0, until=None):
"""泵事件循环,直到 until() 为真或超时。"""
t0 = time.time()
while time.time() - t0 < seconds:
app.processEvents()
time.sleep(0.02)
if until is not None and until():
return True
return until() if until else True
work = os.path.join(ROOT, "dev", "shots", "dlg")
os.makedirs(work, exist_ok=True)
# 造一段"两头静音 + 中间正弦 + 轻微直流偏置"的测试素材
sr = 48000
rng = np.random.default_rng(3)
sil = rng.standard_normal((sr // 2, 2)) * dsp.db_to_lin(-85) + 0.001
tone = np.stack([np.sin(2 * np.pi * 440 * np.arange(sr) / sr) * 0.25] * 2, axis=1)
src = os.path.join(work, "dialog_src.wav")
write_wav(src, np.concatenate([sil, tone, sil]).astype(np.float32), sr, bit_depth="24")
print("── 后期处理对话框 " + "─" * 48)
pd = ProcessDialog(src, None)
pd.show()
app.processEvents()
pd.trim.setChecked(True)
pd.norm.setCurrentIndex(1) # 峰值归一化
pd.depth.setCurrentIndex(pd.depth.findData("16")) # 16 位输出
pd._run()
ok = pump(60, until=lambda: pd.progress.value() == 100 and "完成" in pd.log.toPlainText())
out = os.path.splitext(src)[0] + "_processed.wav"
check("后期处理任务完成并回填界面", ok and pd.run_btn.isEnabled(),
pd.log.toPlainText().strip().splitlines()[-1] if pd.log.toPlainText() else "")
check("生成了处理后的文件", os.path.exists(out),
f"{os.path.getsize(out) if os.path.exists(out) else 0} 字节")
check("处理完成后「打开输出目录」按钮可用", pd.open_btn.isEnabled())
if os.path.exists(out):
with WavReader(out) as r:
info = r.info()
data = r.read()
check("输出为 16 位且用时变短(首尾静音已裁)",
info["bits"] == 16 and 1.1 < info["duration"] < 1.5,
f"{info['format']},{info['duration']:.3f}s(原始 2.000s)")
check("峰值被归一化到 -1 dBFS 附近",
abs(float(dsp.dbfs(dsp.peak(data, axis=0))[0]) + 1.0) < 0.3,
f"{float(dsp.dbfs(dsp.peak(data, axis=0))[0]):+.2f} dBFS")
check("直流偏移已去除", abs(float(np.mean(data))) < 1e-3,
f"{float(np.mean(data)):.2e}")
pd.close()
print("\n── 导出对话框 " + "─" * 50)
ed = ExportDialog(out, None)
ed.show()
app.processEvents()
check("导出预设列表完整", ed.preset.count() >= 8,
"、".join(ed.preset.itemData(i) for i in range(ed.preset.count())))
ed.preset.setCurrentIndex(1) # WAV 24 位
ed._run()
done = pump(60, until=lambda: "成功" in ed.log.toPlainText()
or "失败" in ed.log.toPlainText())
check("导出任务完成并回填界面", done and "成功" in ed.log.toPlainText(),
ed.log.toPlainText().strip())
exported = None
for i in range(ed.preset.count()):
from recorder import post
p = post.default_export_path(out, ed.preset.itemData(i))
if os.path.exists(p):
exported = p
check("导出文件已生成且不与源文件同名",
exported is not None and os.path.abspath(exported) != os.path.abspath(out),
os.path.basename(exported) if exported else "无")
# 找不到 ffmpeg 的场景:必须有清晰提示而不是崩溃
from recorder import post as _post
if _post.find_ffmpeg() is None:
ed.preset.setCurrentIndex(ed.preset.findData("flac"))
ed._run()
pump(20, until=lambda: "失败" in ed.log.toPlainText())
check("缺少 ffmpeg 时导出失败并给出提示",
"ffmpeg" in ed.log.toPlainText(), ed.log.toPlainText().strip()[-60:])
else:
ed.preset.setCurrentIndex(ed.preset.findData("flac"))
ed._run()
flac_ok = pump(120, until=lambda: "成功" in ed.log.toPlainText()
or "失败" in ed.log.toPlainText())
check("导出 FLAC 成功", flac_ok and "成功" in ed.log.toPlainText(),
ed.log.toPlainText().strip()[-60:])
ed.close()
print("\n" + "=" * 62)
if fails:
print(f"失败 {len(fails)} 项:" + "、".join(fails))
sys.exit(1)
print("对话框后台任务链路校验全部通过")