Upload comfy/custom_nodes/ComfyUI-IndexTTS2/audition_klaus.py with huggingface_hub
Browse files
comfy/custom_nodes/ComfyUI-IndexTTS2/audition_klaus.py
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""audition_klaus.py — single-fragment IndexTTS-2 audition for the Web UI's
|
| 3 |
+
歪果仁 mode试听 button.
|
| 4 |
+
|
| 5 |
+
Loads IndexTTS-2 once, runs ONE infer with klaus_lexicon mangling on the
|
| 6 |
+
provided text, writes a single FLAC. Sibling to render_klaus_episode.py but
|
| 7 |
+
single-fragment + no concat / atempo / sidecar — the Web UI just needs a
|
| 8 |
+
~6-second self-introduction sample to preview the voice.
|
| 9 |
+
|
| 10 |
+
Cold-start ~4 min (model load); warm ~3-5 s. The Web UI cache-by-name keeps
|
| 11 |
+
this cost paid only once per voice — see voice_workshop.audition_indextts.
|
| 12 |
+
|
| 13 |
+
Usage (server-side):
|
| 14 |
+
cd /root/IndexTTS2
|
| 15 |
+
.venv/bin/python audition_klaus.py \
|
| 16 |
+
--ref-wav refs/web/<voice>.flac \
|
| 17 |
+
--text "嗨,你好呀!我是 Klaus..." \
|
| 18 |
+
--output /root/ComfyUI/output/voice_audition/klaus_audition_klaus_NNN.flac
|
| 19 |
+
"""
|
| 20 |
+
|
| 21 |
+
from __future__ import annotations
|
| 22 |
+
|
| 23 |
+
import argparse
|
| 24 |
+
import sys
|
| 25 |
+
import time
|
| 26 |
+
from pathlib import Path
|
| 27 |
+
|
| 28 |
+
sys.path.insert(0, str(Path(__file__).parent))
|
| 29 |
+
import klaus_lexicon # noqa: E402
|
| 30 |
+
|
| 31 |
+
from indextts.infer_v2 import IndexTTS2 # noqa: E402
|
| 32 |
+
|
| 33 |
+
# Same locked-in profile as render_klaus_episode.py — keeps the audition
|
| 34 |
+
# faithful to what the actual render will sound like.
|
| 35 |
+
LEXICON_PROFILE = dict(
|
| 36 |
+
tone_4to1_prob=0.95,
|
| 37 |
+
tone_3to2_prob=0.95,
|
| 38 |
+
retroflex_loss_prob=0.0,
|
| 39 |
+
u_prob=0.0,
|
| 40 |
+
)
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
def main() -> int:
|
| 44 |
+
ap = argparse.ArgumentParser(description=__doc__.split("\n")[0])
|
| 45 |
+
ap.add_argument("--ref-wav", required=True, help="path to spk_audio_prompt (relative to cwd or absolute)")
|
| 46 |
+
ap.add_argument("--text", required=True, help="self-intro text to speak")
|
| 47 |
+
ap.add_argument("--output", required=True, help="final .flac path")
|
| 48 |
+
ap.add_argument("--seed", type=int, default=42)
|
| 49 |
+
args = ap.parse_args()
|
| 50 |
+
|
| 51 |
+
if not Path(args.ref_wav).exists():
|
| 52 |
+
sys.exit(f"REF missing: {args.ref_wav} (cwd={Path.cwd()})")
|
| 53 |
+
|
| 54 |
+
print(">> loading IndexTTS2…", flush=True)
|
| 55 |
+
t0 = time.perf_counter()
|
| 56 |
+
tts = IndexTTS2(cfg_path="checkpoints/config.yaml", model_dir="checkpoints", use_fp16=True)
|
| 57 |
+
print(f">> loaded in {time.perf_counter()-t0:.1f}s on {tts.device}", flush=True)
|
| 58 |
+
|
| 59 |
+
mangled, stats = klaus_lexicon.transform(args.text, seed=args.seed, **LEXICON_PROFILE)
|
| 60 |
+
rate = stats["overrides_emitted"] / max(stats["total_han"], 1)
|
| 61 |
+
print(f">> mangled ({rate:.0%} override): {mangled}", flush=True)
|
| 62 |
+
|
| 63 |
+
Path(args.output).parent.mkdir(parents=True, exist_ok=True)
|
| 64 |
+
t0 = time.perf_counter()
|
| 65 |
+
tts.infer(spk_audio_prompt=args.ref_wav, text=mangled, output_path=args.output, verbose=False)
|
| 66 |
+
print(f">> done in {time.perf_counter()-t0:.1f}s -> {args.output}", flush=True)
|
| 67 |
+
return 0
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
if __name__ == "__main__":
|
| 71 |
+
sys.exit(main())
|