sofelia-tts-checkpoints / training /test_inference_arabic.py
hamdallah's picture
Sofelia TTS training checkpoints (Eliaa final + multispeaker + stage1)
c3f9ad5 verified
Raw History Blame Contribute Delete
4.11 kB
#!/usr/bin/env python3
"""
Kokoro Arabic: Test Inference
==============================
Synthesizes Palestinian Arabic test sentences from a StyleTTS2 checkpoint.
Unlike test_inference.py (German), this bypasses KPipeline: Arabic has no
Kokoro lang_code, so we G2P with misaki espeak-ng 'ar' and apply the same
phoneme fixups as scripts/prepare_arabic.py, then call KModel directly.
Usage:
CUDA_VISIBLE_DEVICES= python3 scripts/test_inference_arabic.py \
--checkpoint StyleTTS2/logs/kokoro-arabic/epoch_1st_00000.pth \
--voicepack voices/ar_kore_e1.pt \
--output-dir test_output/epoch1_ar --device cpu
"""
import argparse
import sys
from pathlib import Path
_repo_root = Path(__file__).resolve().parents[1]
_kokoro_submodule = _repo_root / "kokoro"
if _kokoro_submodule.exists() and str(_kokoro_submodule) not in sys.path:
sys.path.insert(0, str(_kokoro_submodule))
sys.path.insert(0, str(_repo_root / "scripts"))
from prepare_arabic import PHONEME_FIXUPS, normalize_arabic # noqa: E402
from test_inference import convert_checkpoint # noqa: E402
TEST_SENTENCES = [
# Daily-life Palestinian dialect
"مرحبا، كيف حالك اليوم؟ إن شاء الله تكون مبسوط.",
"بدي أروح ع السوق أشتري خضرة وفواكه للبيت.",
# Pharyngeals and emphatics (ع، ح، ص، ض، ط)
"الصبح الساعة سبعة طلعت من البيت على الشغل.",
"حكيت مع عمي عن العرس اللي صار الأسبوع الماضي.",
# Numbers and prices
"هاد حقه خمسين شيكل بس، مش غالي.",
# Question prosody
"شو رأيك نطلع نتمشى شوي بعد العصر؟",
# Qaf-heavy (ق realization check)
"قلت لقاسم القهوة بالسوق قريبة من البنك القديم.",
]
def main():
parser = argparse.ArgumentParser(description="Test Arabic Kokoro inference")
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument("--checkpoint", help="StyleTTS2 checkpoint (.pth), converted automatically")
group.add_argument("--model", help="Already-converted Kokoro-format weights (.pth)")
parser.add_argument("--voicepack", required=True)
parser.add_argument("--config", default="training/config.json")
parser.add_argument("--output-dir", default="test_output/arabic")
parser.add_argument("--device", default="cpu", choices=["cpu", "cuda"])
parser.add_argument("--speed", type=float, default=1.0)
args = parser.parse_args()
import numpy as np
import soundfile as sf
import torch
from kokoro import KModel
from misaki import espeak
out = Path(args.output_dir)
out.mkdir(parents=True, exist_ok=True)
if args.checkpoint:
model_path = convert_checkpoint(
args.checkpoint, str(out / "kokoro_arabic_converted.pth")
)
else:
model_path = args.model
print(f"Loading KModel on {args.device}...")
kmodel = KModel(
repo_id="hexgrad/Kokoro-82M", config=args.config, model=model_path
)
kmodel = kmodel.to(args.device).eval()
voice = torch.load(args.voicepack, map_location="cpu", weights_only=True)
print(f"Voicepack: {args.voicepack} {tuple(voice.shape)}")
g2p = espeak.EspeakG2P(language="ar")
for i, text in enumerate(TEST_SENTENCES):
print(f"\n[{i + 1}/{len(TEST_SENTENCES)}] {text}")
ps, _ = g2p(normalize_arabic(text, g2p))
for old, new in PHONEME_FIXUPS.items():
ps = ps.replace(old, new)
ps = ps.strip()
print(f" phonemes: {ps}")
if len(ps) > 510:
ps = ps[:510]
ref_s = voice[len(ps) - 1]
with torch.no_grad():
audio = kmodel(ps, ref_s, args.speed, return_output=False)
audio = audio.cpu().numpy().squeeze()
wav_path = out / f"ar_test_{i + 1:02d}.wav"
sf.write(str(wav_path), audio, 24000)
print(f" saved: {wav_path} ({len(audio) / 24000:.1f}s)")
print(f"\nDone! Output in {out}/")
if __name__ == "__main__":
main()