Spaces:
Running
Running
File size: 2,986 Bytes
3ec6b9e | 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 53 54 55 56 57 58 59 60 61 62 63 | """Run the PiVo Python reference and dump intermediates for JS parity tests."""
import json, sys
from pathlib import Path
import numpy as np
ROOT = Path(__file__).resolve().parents[3] # the workspace that holds model/ and ref/
sys.path.insert(0, str(ROOT / "model"))
import pivo.text as T
import pivo.runtime as R
from pivo.audio import write_wav
# The app uses the frontend's opt-in letter-spelling fallback for unknown words.
R.phonemize = lambda text, overrides=None: T.phonemize(text, strict=False, overrides=overrides)
out = ROOT / "ref"
out.mkdir(exist_ok=True)
sentences = json.loads((Path(__file__).resolve().parents[1] / "sentences.json").read_text())
rt = R.Runtime(ROOT / "model/onnx", threads=1)
# Capture the decoder features fed to the vocoder.
orig_run = {}
captured = {}
for name, sess in rt.sessions.items():
class Wrap:
def __init__(self, s, n): self.s, self.n = s, n
def run(self, outs, feeds):
r = self.s.run(outs, feeds)
captured.setdefault(self.n, []).append((feeds, r))
return r
rt.sessions[name] = Wrap(sess, name)
frontend = []
for i, s in enumerate(sentences):
fr = T.phonemize(s, strict=False)
frontend.append({"text": s, "normalized": fr.normalized_text, "symbols": list(fr.symbols),
"ids": list(fr.token_ids), "unknown": list(fr.unknown_words)})
for voice in ["Pi", "Vo"]:
captured.clear()
audio = rt.synthesize(s, voice=voice)
enc_feeds, enc_out = captured["encoder"][0]
feats = np.concatenate([r[0][0] for _, r in captured["decoder"]], 0)
stem = f"s{i}_{voice}"
np.save(out / f"{stem}_encoded.npy", enc_out[0][0])
np.save(out / f"{stem}_logdur.npy", enc_out[1][0])
np.save(out / f"{stem}_features.npy", feats)
np.save(out / f"{stem}_audio.npy", audio.astype(np.float32))
write_wav(out / f"{stem}.wav", audio)
print(stem, len(fr.token_ids), "tokens", feats.shape[0], "frames", f"{len(audio)/16000:.2f}s")
# Extra frontend-only cases.
extra = ["", "a", "Mr. and Mrs. Jones vs. the St. Louis Prof.", "I'll be there at 9:30, ok?", "x-ray, re-enter, e-mail",
"THE END", "USA FBI ABCDEF", "Pi's voice isn't Vo's.", "5th 1st 2nd 3rd 100th 11th 12th 13th 20th 21st 101st",
"2.5 0.75 -3.25 1,000 99,999,999 1000000000000000", "$0.01 $1 $1.00 £1.01 €100.5", "rock 'n' roll", "Café naïve résumé",
"ellipsis… and “smart quotes” and ‘single’ — dash – en", "hello;world:foo!bar?baz.", "x"]
for s in extra:
try:
fr = T.phonemize(s, strict=False)
frontend.append({"text": s, "normalized": fr.normalized_text, "symbols": list(fr.symbols),
"ids": list(fr.token_ids), "unknown": list(fr.unknown_words)})
except Exception as e:
frontend.append({"text": s, "error": f"{type(e).__name__}: {e}"})
(out / "frontend.json").write_text(json.dumps(frontend, indent=1, ensure_ascii=False))
|