File size: 3,614 Bytes
9b20cba
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
import os
import torch
import gradio as gr
from datetime import datetime
from vinorm import TTSnorm
from underthesea import sent_tokenize
from unidecode import unidecode
import soundfile as sf
from TTS.tts.configs.xtts_config import XttsConfig
from TTS.tts.models.xtts import Xtts
from huggingface_hub import snapshot_download
import os

# Tải model nếu chưa có
if not os.path.exists("model/model.pth"):
    snapshot_download(repo_id="epchannel/EpXTTS", repo_type="model", local_dir="model")
    
# Load XTTS model
def load_model():
    config = XttsConfig()
    config.load_json("model/config.json")
    model = Xtts.init_from_config(config)
    model.load_checkpoint(config, checkpoint_path="model/model.pth", vocab_path="model/vocab.json")
    if torch.cuda.is_available():
        model.cuda()
    return model

# Chuẩn hóa văn bản tiếng Việt
def normalize_vietnamese_text(text):
    return (
        TTSnorm(text, unknown=False, lower=False, rule=True)
        .replace("..", ".").replace("!.", "!").replace("?.", "?")
        .replace(" .", ".").replace(" ,", ",").replace('"', "")
        .replace("'", "").replace("AI", "Ây Ai").replace("A.I", "Ây Ai")
        .replace("anh/chị", "anh chị")
    )

# Tạo tên file
def get_file_name(text, max_char=50):
    filename = unidecode(text[:max_char].lower().replace(" ", "_"))
    timestamp = datetime.now().strftime("%m%d%H%M%S")
    return f"{timestamp}_{filename}"

# Sinh tiếng nói
def synthesize(text, voice_choice):
    model = load_model()
    ref_audio = f"model/samples/{voice_choice}.wav"

    # Prepare speaker embedding
    gpt_latent, speaker_embed = model.get_conditioning_latents(
        audio_path=ref_audio,
        gpt_cond_len=model.config.gpt_cond_len,
        max_ref_length=model.config.max_ref_len,
        sound_norm_refs=model.config.sound_norm_refs,
    )

    try:
        text = normalize_vietnamese_text(text)
    except:
        pass

    sentences = sent_tokenize(text)
    wav_chunks = []
    for sent in sentences:
        if sent.strip() == "":
            continue
        wav = model.inference(
            text=sent,
            language="vi",
            gpt_cond_latent=gpt_latent,
            speaker_embedding=speaker_embed,
            temperature=0.5,
            top_k=20,
            top_p=0.85,
            repetition_penalty=5.0,
        )
        wav_chunks.append(torch.tensor(wav["wav"]))

    final_wav = torch.cat(wav_chunks, dim=0).unsqueeze(0)
    filename = f"./output/{get_file_name(text)}.mp3"
    os.makedirs("output", exist_ok=True)
    sf.write(filename, final_wav.squeeze(0).numpy(), 24000, format='MP3')
    return filename

# Giao diện Gradio
voices = {
    "Bống Xinh": "bongxinh",
    "Nam Calm": "nam-calm",
    "Nam Cham": "nam-cham",
    "Nam Truyền cảm": "nam-truyen-cam",
    "Nữ Lưu Loát": "nu-luu-loat",
    "Nữ Nhẹ Nhàng": "nu-nhe-nhang",
    # Thêm các giọng bạn có...
}


with gr.Blocks() as demo:
    gr.Markdown("## 🇻🇳 Text to Speech tiếng Việt (XTTS)")
    with gr.Row():
        text_input = gr.Textbox(label="Nhập văn bản", lines=5, placeholder="Nhập văn bản tiếng Việt...")
    voice_choice = gr.Radio(choices=list(voices.keys()), label="Chọn giọng đọc", value="Bông Xinh")
    btn = gr.Button("🎙️ Chuyển thành giọng nói")
    audio_output = gr.Audio(label="🔊 Kết quả")

    def process(text, voice_label):
        file = synthesize(text, voices[voice_label])
        return file

    btn.click(fn=process, inputs=[text_input, voice_choice], outputs=audio_output)

demo.launch()