59 lines
1.7 KiB
Python
59 lines
1.7 KiB
Python
"""
|
||
phoneme stress sanity check — run BEFORE training (Phase 6).
|
||
|
||
espeak-ng is piper's phonemizer. for each sampled transcript, print its IPA and
|
||
flag any multisyllable word whose IPA carries no primary-stress mark 'ˈ'. wrong
|
||
stress is the #1 cause of "sounds bad" — catch it here, not after GPU-days.
|
||
|
||
ponytail: thin wrapper over the espeak-ng CLI. ceiling = flags MISSING stress
|
||
only, not WRONG-POSITION stress (that needs an ear on the Phase 4 audio).
|
||
|
||
run: python piper/check_phonemes.py [--n 30] [--meta piper/dataset/metadata.csv]
|
||
requires: sudo apt install espeak-ng
|
||
"""
|
||
|
||
import argparse
|
||
import random
|
||
import shutil
|
||
import subprocess
|
||
import sys
|
||
from pathlib import Path
|
||
|
||
STRESS = "ˈ"
|
||
|
||
|
||
def ipa(text: str) -> str:
|
||
out = subprocess.run(
|
||
["espeak-ng", "-v", "ru", "--ipa", "-q", text],
|
||
capture_output=True, text=True,
|
||
)
|
||
return out.stdout.strip()
|
||
|
||
|
||
def main():
|
||
if not shutil.which("espeak-ng"):
|
||
sys.exit("espeak-ng not found — sudo apt install espeak-ng")
|
||
|
||
ap = argparse.ArgumentParser()
|
||
ap.add_argument("--meta", default="tts/piper/dataset/metadata.csv")
|
||
ap.add_argument("--n", type=int, default=30)
|
||
args = ap.parse_args()
|
||
|
||
lines = [l for l in Path(args.meta).read_text(encoding="utf-8").splitlines() if "|" in l]
|
||
sample = random.sample(lines, min(args.n, len(lines)))
|
||
|
||
flagged = 0
|
||
for line in sample:
|
||
_id, text = line.split("|", 1)
|
||
phon = ipa(text)
|
||
bad = STRESS not in phon and len(text.split()) >= 1
|
||
mark = " ⚠ NO STRESS" if bad else ""
|
||
flagged += bad
|
||
print(f"{text}\n → {phon}{mark}\n")
|
||
|
||
print(f"[{'!' if flagged else '✓'}] {flagged}/{len(sample)} samples missing stress marks")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|