Voice_Clone / app.py
talhawk's picture
Update app.py
7163481 verified
Raw
History Blame Contribute Delete
4.09 kB
import os
os.environ["COQUI_TOS_AGREED"] = "1" # Coqui TOS auto-accept
os.environ.setdefault("COQUI_CACHE_DIR", "/home/user/.coqui")
import glob
import gradio as gr
import numpy as np
import soundfile as sf
from huggingface_hub import snapshot_download
from TTS.api import TTS
HF_REPO = "suhaibrashid17/XTTS-v2-Urdu-FT"
SAMPLE_RATE = 24000
def _find_file(patterns, root):
for pat in patterns:
hits = glob.glob(os.path.join(root, pat), recursive=True)
if hits:
return hits[0]
return None
def load_finetuned_xtts():
# Download to local cache (no symlinks to avoid weird paths)
repo_dir = snapshot_download(
repo_id=HF_REPO,
local_dir_use_symlinks=False,
allow_patterns=["**/*"] # pull everything once; avoids missing tokenizer/vocab
)
# Locate config + weight file
cfg = _find_file(["config.json", "**/config.json"], repo_dir)
mdl = _find_file(
["model.pth", "pytorch_model.bin", "**/model.pth", "**/pytorch_model.bin"],
repo_dir,
)
if not (cfg and mdl):
raise RuntimeError(
"Could not find model/config. Need config.json and model.pth (or pytorch_model.bin)."
)
# >>> IMPORTANT FIX <<<
# Coqui XTTS expects model_path = DIRECTORY that contains model.pth
model_dir = os.path.dirname(mdl)
# Load by giving directory (not the file) to model_path
tts = TTS(model_path=model_dir, config_path=cfg, gpu=False, progress_bar=False)
return tts
# ---- load once on startup
TTS_ENGINE = load_finetuned_xtts()
# ---------- helpers ----------
def _ensure_wav_path(audio):
"""
Gradio v5: Audio can be filepath or dict/tuple/ndarray.
Always return a real wav path.
"""
if audio is None:
return None
if isinstance(audio, str):
return audio
if isinstance(audio, dict) and "data" in audio:
data = audio["data"]
sr = int(audio.get("sampling_rate", SAMPLE_RATE))
path = "ref.wav"
sf.write(path, data, sr)
return path
if isinstance(audio, (tuple, list)):
# (sr, data) or (data, sr)
if isinstance(audio[0], (int, float)):
sr, data = audio
else:
data, sr = audio
path = "ref.wav"
sf.write(path, data, int(sr))
return path
if isinstance(audio, np.ndarray):
path = "ref.wav"
sf.write(path, audio, SAMPLE_RATE)
return path
raise ValueError("Unsupported audio input format")
def clean_urdu_text(txt: str) -> str:
if not txt:
return ""
t = " ".join(txt.split())
if t and t[-1] not in "۔!?؟":
t += "۔"
return t
# ---------- inference ----------
def clone_urdu(reference_audio, text):
ref_path = _ensure_wav_path(reference_audio)
if not ref_path:
return gr.Error("Reference audio required (5–30 seconds).")
txt = clean_urdu_text(text)
try:
out_path = "output_xtts_ur.wav"
# XTTS fine-tuned for Urdu
TTS_ENGINE.tts_to_file(
text=txt,
file_path=out_path,
speaker_wav=ref_path,
language="ur",
)
return out_path
except Exception as e:
return gr.Error(f"XTTS-Urdu error: {e}")
# ---------- UI ----------
with gr.Blocks(title="XTTS-v2 Urdu — Voice Clone (CPU)") as demo:
gr.Markdown("## XTTS-v2 Urdu — Voice Clone")
ref = gr.Audio(
label="Reference voice (WAV/MP3, ~5–30 sec)",
sources=["upload", "microphone"],
type="filepath",
waveform_options={"show_controls": True},
)
txt = gr.Textbox(
label="Urdu text",
lines=4,
placeholder="یہاں اردو متن لکھیں… مختصر جملے بہتر نتیجہ دیتے ہیں۔"
)
btn = gr.Button("Generate", variant="primary")
out = gr.Audio(label="Output", type="filepath")
btn.click(clone_urdu, inputs=[ref, txt], outputs=out)
if __name__ == "__main__":
demo.queue(max_size=8).launch(server_name="0.0.0.0", server_port=7860)