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))