Views
No views yet
1# System
2apt-get install espeak-ng
3
4# Python
5pip install torch torchaudio soundfile pyyaml munch huggingface_hub
6
7# Patched StyleTTS2 fork (required for model loading)
8git clone https://github.com/semidark/StyleTTS2.git1import subprocess, torch, soundfile as sf, torchaudio, random, sys
2from pathlib import Path
3from huggingface_hub import hf_hub_download
4
5# 1. Download checkpoint
6ckpt_path = hf_hub_download("NMikka/kokoro-georgian", "kokoro_georgian_e9.pth")
7
8# 2. Add StyleTTS2 to path (cloned above)
9sys.path.insert(0, "StyleTTS2")
10from models import build_model, load_ASR_models, load_F0_models
11from Utils.PLBERT.util import load_plbert
12from kokoro_symbols import TextCleaner
13from kokoro_tb_utils import run_kokoro_inference
14import yaml
15from munch import Munch
16
17# 3. Load model
18if not hasattr(torch, "_original_load"):
19 torch._original_load = torch.load
20 torch.load = lambda *a, **kw: torch._original_load(*a, **{**kw, "weights_only": False})
21
22config_path = hf_hub_download("NMikka/kokoro-georgian", "config_georgian.yml")
23cfg = yaml.safe_load(open(config_path))
24
25def munchify(d):
26 return Munch({k: munchify(v) for k, v in d.items()}) if isinstance(d, dict) else d
27
28device = "cuda" if torch.cuda.is_available() else "cpu"
29model_params = munchify(cfg["model_params"])
30text_aligner = load_ASR_models("StyleTTS2/Utils/ASR/epoch_00080.pth", "StyleTTS2/Utils/ASR/config.yml")
31pitch_extractor = load_F0_models("StyleTTS2/Utils/JDC/bst.t7")
32plbert = load_plbert("StyleTTS2/Utils/PLBERT/")
33
34model = build_model(model_params, text_aligner, pitch_extractor, plbert)
35ckpt = torch.load(ckpt_path, map_location="cpu")
36state = ckpt.get("net", ckpt)
37for name, module in model.items():
38 if name not in state:
39 continue
40 sd = state[name]
41 if any(k.startswith("module.") for k in sd.keys()):
42 sd = {k.removeprefix("module."): v for k, v in sd.items()}
43 module.load_state_dict(sd, strict=False)
44model = Munch({k: v.to(device).eval() for k, v in model.items()})
45
46# 4. Extract voicepack from reference audio (a directory of 24kHz mono WAVs)
47mel_transform = torchaudio.transforms.MelSpectrogram(
48 sample_rate=24000, n_fft=2048, win_length=1200, hop_length=300, n_mels=80
49).to(device)
50
51def extract_voicepack(audio_dir, n=50):
52 wavs = random.sample(list(Path(audio_dir).rglob("*.wav")), min(n, len(list(Path(audio_dir).rglob("*.wav")))))
53 acoustic, prosodic = [], []
54 with torch.no_grad():
55 for w in wavs:
56 import soundfile as sf
57 data, sr = sf.read(str(w), dtype="float32")
58 if data.ndim > 1: data = data.mean(axis=1)
59 wav = torch.from_numpy(data).unsqueeze(0)
60 if sr != 24000: wav = torchaudio.functional.resample(wav, sr, 24000)
61 wav = wav.to(device)
62 mel = ((mel_transform(wav) + 1e-5).log2() - (-4)) / 4
63 if mel.shape[-1] < 10: continue
64 acoustic.append(model.style_encoder(mel.unsqueeze(1)))
65 prosodic.append(model.predictor_encoder(mel.unsqueeze(1)))
66 return torch.cat([torch.stack(acoustic).mean(0), torch.stack(prosodic).mean(0)], dim=-1)
67
68voicepack = extract_voicepack("/path/to/reference/audio/")
69
70# 5. Georgian G2P via espeak-ng
71def ka_g2p(text):
72 r = subprocess.run(["espeak-ng", "-v", "ka", "--ipa=3", "-q"],
73 input=text, capture_output=True, text=True)
74 ipa = " ".join(l.strip() for l in r.stdout.splitlines() if l.strip())
75 for tied, lig in {"dʒ":"ʥ","tʃ":"ʨ","ts":"ʦ","dz":"ʣ"}.items():
76 ipa = ipa.replace(tied, lig)
77 return ipa.replace("", "")
78
79# 6. Synthesise
80text = "გამარჯობა, სამყარო!"
81ipa = ka_g2p(text)
82tc = TextCleaner()
83audios = run_kokoro_inference(model, [(text, tc(ipa))], voicepack, device, tc)
84wav = audios[0][1].cpu().numpy().squeeze()
85sf.write("output.wav", wav, 24000)1python infer.py \
2 --text "გამარჯობა, სამყარო!" \
3 --checkpoint kokoro_georgian_e9.pth \
4 --audio-ref /path/to/reference/speaker/wavs/ \
5 --styletts2-dir ./StyleTTS2 \
6 --output output.wavespeak-ng -v ka with ZWJ-stripping and affricate ligature remapping