# -*- coding: utf-8 -*- """网络热门音效 —— 生成 / 列表 / 播放 / 自己放素材。 用户要求:「再加上近期网络热门音效,什么冰冰冰,啊哈哈哈哈,各种 老牧师音效,可以直接远程播放」。 ⚠ 版权说明(这个测试也在钉这件事): 「冰冰冰」「啊哈哈哈」「老牧师」是**网上流行的真实录音**,有版权。 安装包里内置的是**程序合成**的版本(正弦/方波现场算), 名字对着感觉来,但不是那些录音本身、也不冒充。 用户想用一模一样的一手素材,丢进 data/sfx/ 就行 —— 这一条也要测。 """ from __future__ import annotations import struct import sys import tempfile import time import wave from pathlib import Path ROOT = Path(__file__).resolve().parent.parent sys.path.insert(0, str(ROOT / "windows")) passed = 0 failed = 0 def check(name, ok, detail=""): global passed, failed if ok: passed += 1 print(" [PASS] " + name) else: failed += 1 print(" [FAIL] " + name + ((" —— " + str(detail)) if detail else "")) def main() -> int: print("=" * 62) print(" 网络热门音效(可远程播放)") print("=" * 62) from lanremote.sfx import Sfx, BUILTIN_SPECS, SfxError, USER_EXTS work = Path(tempfile.mkdtemp(prefix="sfx_")) # ---------------------------------------------------------------- 一 print(" ---- 一、用户点名的三个必须在 ----") s = Sfx(work / "data") t0 = time.time() made = s.ensure_builtin() took = time.time() - t0 check("首次运行能生成内置音效", made >= 3, "只生成了 %d 个" % made) check("生成够快(< 3 秒)", took < 3.0, "用了 %.2f 秒" % took) names = [it["name"] for it in s.list_sfx()] print(" 生成的:%s" % "、".join(names)) for wanted in ("冰冰冰", "啊哈哈哈", "老牧师"): check("有「%s」" % wanted, wanted in names, "列表里没有") # ---------------------------------------------------------------- 二 print(" ---- 二、生成的是合法 WAV(真的能放)----") for it in s.list_sfx(): p = s.dir / it["file"] try: with wave.open(str(p)) as w: ch = w.getnchannels() sr = w.getframerate() n = w.getnframes() frames = w.readframes(min(n, 2000)) ok = (ch == 1 and sr == 16000 and n > 1000 and len(frames) > 0) check("%s 是合法 WAV(%d Hz, %.2f 秒)" % (it["name"], sr, n / sr), ok, "ch=%s sr=%s n=%s" % (ch, sr, n)) except Exception as exc: # noqa: BLE001 check("%s 是合法 WAV" % it["name"], False, "%s: %s" % (type(exc).__name__, exc)) # ---------------------------------------------------------------- 三 print(" ---- 三、有声音(不是一片静音)----") p = s.dir / "冰冰冰.wav" with wave.open(str(p)) as w: raw = w.readframes(w.getnframes()) vals = struct.unpack("<%dh" % (len(raw) // 2), raw[: (len(raw) // 2) * 2]) peak = max(abs(v) for v in vals) nonzero = sum(1 for v in vals if abs(v) > 50) check("峰值够大(有声)", peak > 8000, "峰值只有 %d" % peak) check("不是全静音", nonzero > len(vals) * 0.2, "只有 %.0f%% 的采样有声音" % (100.0 * nonzero / max(1, len(vals)))) check("没有削顶爆音(峰值不超过 int16 上限)", peak <= 32767) # ---------------------------------------------------------------- 四 print(" ---- 四、用户自己放素材 ----") # 造一个假的 mp3(内容不重要,只看"能不能被列出来/能不能被解析到") fake = s.dir / "我自己的素材.mp3" fake.write_bytes(b"\xff\xfb\x90\x00" + b"\x00" * 500) names2 = [it["name"] for it in s.list_sfx()] check("用户放的素材出现在列表里", "我自己的素材" in names2, "列表:%s" % names2) check("用户素材标记为非内置", any(it["name"] == "我自己的素材" and not it["builtin"] for it in s.list_sfx())) check("内置的仍在", "冰冰冰" in names2) check("能解析到用户素材的路径", s.resolve("我自己的素材").name == "我自己的素材.mp3") check("能按带后缀的名字解析", s.resolve("我自己的素材.mp3").name == "我自己的素材.mp3") # ---------------------------------------------------------------- 五 print(" ---- 五、播放(用假播放器,别真出声)----") played = [] s2 = Sfx(work / "data", player=lambda path: (played.append(path), "正在播放")[1]) msg = s2.play("冰冰冰") check("play() 调用了播放器", len(played) == 1, "调用 %d 次" % len(played)) check("传给播放器的是生成的 WAV", played and played[0].endswith("冰冰冰.wav"), played[0] if played else "没调用") check("返回了提示文字", "播放" in msg, repr(msg)) # ---------------------------------------------------------------- 六 print(" ---- 六、出错路径 ----") for bad in ("", "根本没有这个音效", "../../etc/passwd", None): try: s2.resolve(bad or "") check("拒绝 %r" % (bad,), False, "居然解析成功了") except SfxError: check("拒绝 %r" % (bad,), True) except Exception as exc: # noqa: BLE001 check("拒绝 %r" % (bad,), False, "抛了别的异常 %s" % type(exc).__name__) # ---------------------------------------------------------------- 七 print(" ---- 七、目录里有说明文件(用户能找到去哪放素材)----") # ⚠ 别用 glob("*.txt") 取第一个 —— 目录里还有 .builtin.txt # (生成清单),它的排序可能排在说明文件前面, # 于是读到的是清单内容、断言全红。按名字精确取。 readme_path = s.dir / "放这里.txt" check("sfx 目录里有说明文件", readme_path.is_file(), "没有说明文件 -> 用户不知道往哪放素材") if readme_path.is_file(): body = readme_path.read_text(encoding="utf-8", errors="replace") check("说明里写了「放进来就能用」", "放" in body and ("mp3" in body or "wav" in body)) check("说明里讲清了内置的是合成的(版权说明)", "合成" in body or "版权" in body) # ---------------------------------------------------------------- 八 print(" ---- 八、删掉内置音效后不会又被生成回来 ----") target = s.dir / "叮.wav" if target.is_file(): target.unlink() s.ensure_builtin() check("删掉的内置音效不会自动重生", not target.is_file(), "又被生成回来了 -> 用户删不掉") print() print("=" * 62) print(" 通过 %d 项,失败 %d 项" % (passed, failed)) return 0 if failed == 0 else 1 if __name__ == "__main__": sys.exit(main())