File size: 2,735 Bytes
d801da2 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | """Explicit real-model unknown-symbol pause + short-first-batch acceptance."""
import argparse
from datetime import datetime, timezone
import json
from pathlib import Path
import sys
import threading
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT))
from tts.engine import SpeechEngine
from tts.text import SpeechError
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--assets", type=Path, required=True)
parser.add_argument("--output", type=Path, required=True)
args = parser.parse_args()
args.output.mkdir(parents=True, exist_ok=True)
engine = SpeechEngine(args.assets, ROOT / "assets.lock.json")
from mutagen.mp3 import MP3
report = {"at": datetime.now(timezone.utc).isoformat(), "engine": engine.info, "cases": [], "scope": "Real local CPU encoded MP3 and complete source ranges; not manual audible/production latency acceptance"}
cases = (("mixed", "校长读长文。𠮷※★😀αЖ,CPU和API继续。"), ("pause-only", "𠮷※★😀αЖ。"),
("control", "甲\f乙,后文继续。"), ("short-first", "这是一篇长文,校长文章写得很好,我得亲自处理。"))
for name, text in cases:
file = args.output / (name + ".mp3")
published = []
result = engine.synthesize(text, "zf_001", 1, file, threading.Event(), progress=published.append, unsupported_policy="pause")
assert result["segments"][0]["start"] == 0
assert result["segments"][-1]["end"] == len(text.encode("utf-16-le")) // 2
assert all(a["end"] == b["start"] for a, b in zip(result["segments"], result["segments"][1:]))
assert len(published) == len(result["segments"])
assert abs(MP3(file).info.length - result["audio_seconds"]) < .25
for part in published:
clip = file.with_name(f"{file.stem}.{part['index']}.mp3")
assert abs(MP3(clip).info.length - part["audio_seconds"]) < .25
if name == "mixed": assert result["unsupported_phoneme_count"] >= 6 and result["batches"] > 0
if name == "pause-only": assert result["batches"] == 0
report["cases"].append({"name": name, "result": result})
print(name, "PASS", "duration", result["audio_seconds"], "first-part", result["first_audio_seconds"], flush=True)
try:
engine.synthesize(cases[0][1], "zf_001", 1, args.output / "strict.mp3", threading.Event())
except SpeechError as error:
assert error.code == "unsupported_text"
else:
raise AssertionError("Default strict API must remain strict")
(args.output / "acceptance.json").write_text(json.dumps(report, ensure_ascii=False, indent=2))
if __name__ == "__main__": main()
|