"""Step 3: the text for the voice, cut into segments (work/segs.json). Every note: lexicon.pre -> lines (a blank line = paragraph 'p', a single newline = 'l') -> lines over MAXC characters cut at sentence ends into near-equal pieces ('s') -> RUAccent '+' -> lexicon.post -> U+0301 text for the ru-stress model. A segment's takes are named __s.wav, the hash of the reference and the spoken text: change the lexicon or the reference and only the segments it touches are rendered again. """ import hashlib import json import math import re from .accent import acute_marks, plus_marks from .config import load from .lexicon import post, pre MAXC = 380 def split_long(s): if len(s) <= MAXC: return [s] sents = [x.strip() for x in re.findall(r".+?(?:[.!?…]+[»\"')]*(?=\s)|$)\s*", s) if x.strip()] k = math.ceil(len(s) / 330) target, out, cur = len(s) / k, [], "" for x in sents: if cur and len(cur) + len(x) + 1 > target * 1.15 and len(out) < k - 1: out.append(cur); cur = x else: cur = (cur + " " + x).strip() out.append(cur) return out def main(): cfg = load() ref = cfg["voice"]["reference"] notes = json.load(open(cfg.w("notes.json"), encoding="utf-8")) if cfg.only: # ENSVOICE_NOTES=n016,n238: a quick experiment on a few notes notes = [n for n in notes if n["k"] in cfg.only] segs = [] for n in notes: units, join = [], "p" for line in pre(n["text"]).split("\n"): line = line.strip() if not line: join = "p"; continue for j, piece in enumerate(split_long(line)): units.append(("s" if j else join, piece)) join = "l" for i, (jn, u) in enumerate(units): plus = post(plus_marks(u)) acute = acute_marks(plus) sid = f"{n['k']}_{i:02d}" h = hashlib.sha1(f"{ref}|{acute}".encode()).hexdigest()[:10] segs.append(dict(id=sid, take=f"{sid}_{h}", k=n["k"], i=i, join=jn if i else "-", text=u, plus=plus, acute=acute)) json.dump(segs, open(cfg.w("segs.json"), "w", encoding="utf-8"), ensure_ascii=False, indent=0) L = [len(s["text"]) for s in segs] print("segments", len(segs), "notes", len(notes), "longest", max(L), "chars", sum(L)) if __name__ == "__main__": main()