Russian narration for the Enshrouded notes: mod 0.4.0, installer, generation pipeline
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
"""Choose the reference clip by numbers: 16 note paragraphs x 2 seeds in every candidate voice.
|
||||
|
||||
usage: refbench [ref_id ...] render (tts env); no ids: the configured reference + the 8 best by sim
|
||||
refbench --report table after `qc --jobs work/refbench/jobs.json --out work/refbench/qc.json`
|
||||
Pick the reference with low WER and few bad takes (a leaking reference shows as many takes with WER > 0.1:
|
||||
English at the start), then the highest MOS; put its id into voice.reference. Local backend only.
|
||||
"""
|
||||
import collections
|
||||
import json
|
||||
import os
|
||||
import random
|
||||
import re
|
||||
import statistics as st
|
||||
import sys
|
||||
|
||||
from .config import load
|
||||
|
||||
|
||||
def report(cfg):
|
||||
q = json.load(open(cfg.w("refbench", "qc.json"), encoding="utf-8"))
|
||||
by = collections.defaultdict(list)
|
||||
for k, r in q.items():
|
||||
if "error" not in r:
|
||||
by[k.split("|")[0]].append(r)
|
||||
rows = []
|
||||
for v, rs in by.items():
|
||||
so = sum(r["stress_ok"] for r in rs) / max(1, sum(r["stress_n"] for r in rs))
|
||||
rows.append((st.mean(r.get("mos", 0) for r in rs), v, len(rs), st.mean(r["wer"] for r in rs),
|
||||
sum(r["wer"] > .1 for r in rs), so, st.mean(r["sim"] for r in rs)))
|
||||
print(f"{'MOS':>5} {'WER':>6} {'bad':>4} {'stress':>7} {'SIM':>6} reference")
|
||||
for mos, v, n, wer, bad, so, sim in sorted(rows, reverse=True):
|
||||
print(f"{mos:5.2f} {wer:6.3f} {bad:4d} {so:7.3f} {sim:6.3f} {v}")
|
||||
|
||||
|
||||
def main():
|
||||
cfg = load()
|
||||
if "--report" in sys.argv:
|
||||
return report(cfg)
|
||||
from .accent import acute_marks, plus_marks
|
||||
from .lexicon import post, pre
|
||||
from .render import reference
|
||||
from .tts.local import LocalTTS
|
||||
refs = json.load(open(cfg.w("refs.json"), encoding="utf-8"))
|
||||
ids = [a for a in sys.argv[1:] if not a.startswith("-")] or \
|
||||
list(dict.fromkeys([cfg["voice"]["reference"]] + [c["id"] for c in refs[:8]]))
|
||||
notes = json.load(open(cfg.w("notes.json"), encoding="utf-8"))
|
||||
tp = cfg.w("refbench", "texts.json")
|
||||
if tp.exists():
|
||||
sel = json.load(open(tp, encoding="utf-8"))
|
||||
else:
|
||||
random.seed(7)
|
||||
paras = [(n["k"], p.strip()) for n in notes for p in re.split(r"\n\s*\n", pre(n["text"])) if 150 <= len(p.strip()) <= 380]
|
||||
random.shuffle(paras)
|
||||
seen, sel = set(), []
|
||||
for k, p in paras:
|
||||
if k not in seen and len(sel) < 16:
|
||||
seen.add(k); sel.append(dict(id=k, text=p, plus=post(plus_marks(p))))
|
||||
json.dump(sel, open(tp, "w", encoding="utf-8"), ensure_ascii=False, indent=1)
|
||||
tts, jobs = None, []
|
||||
for rid in ids:
|
||||
wav, text = reference(cfg, rid)
|
||||
tts = tts or LocalTTS(cfg, wav, text)
|
||||
tts.set_voice(wav, text)
|
||||
for seed in (1000, 1001):
|
||||
items = [(acute_marks(s["plus"]), str(cfg.w("refbench", "takes", f"{rid}~{s['id']}~{seed}.wav")))
|
||||
for s in sel]
|
||||
todo = [it for it in items if not os.path.exists(it[1])]
|
||||
if todo:
|
||||
tts.speak(todo, seed)
|
||||
jobs += [dict(id=f"{rid}|{s['id']}|{seed}", text=s["text"], plus=s["plus"], wav=it[1]) for s, it in zip(sel, items)]
|
||||
json.dump(jobs, open(cfg.w("refbench", "jobs.json"), "w", encoding="utf-8"), ensure_ascii=False)
|
||||
print("REFBENCH_RENDERED", len(jobs), "takes; next: python run.py qc --jobs", cfg.w("refbench", "jobs.json"),
|
||||
"--out", cfg.w("refbench", "qc.json"))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user