Spaces:
Runtime error
Runtime error
Commit
·
8dc4072
1
Parent(s):
a7ef740
deploy VieNeu-TTS-1000h
Browse files
app.py
CHANGED
|
@@ -1,208 +1,215 @@
|
|
| 1 |
import spaces # PHẢI import TRƯỚC mọi thứ!
|
| 2 |
import os
|
| 3 |
os.environ['SPACES_ZERO_GPU'] = '1' # Set environment variable
|
| 4 |
-
|
| 5 |
import gradio as gr
|
| 6 |
-
import numpy as np
|
| 7 |
import soundfile as sf
|
| 8 |
import tempfile
|
| 9 |
import torch
|
|
|
|
| 10 |
|
| 11 |
-
|
| 12 |
-
from vieneutts import VieNeuTTS
|
| 13 |
|
| 14 |
-
# Khởi tạo model
|
| 15 |
print("📦 Đang tải model...")
|
|
|
|
|
|
|
|
|
|
| 16 |
tts = VieNeuTTS(
|
| 17 |
-
backbone_repo="pnnbao-ump/VieNeu-TTS",
|
| 18 |
-
backbone_device=
|
| 19 |
codec_repo="neuphonic/neucodec",
|
| 20 |
-
codec_device=
|
| 21 |
)
|
| 22 |
print("✅ Model đã tải xong!")
|
| 23 |
|
| 24 |
# Danh sách giọng mẫu
|
| 25 |
VOICE_SAMPLES = {
|
| 26 |
-
"Nam
|
| 27 |
"audio": "./sample/id_0001.wav",
|
| 28 |
"text": "./sample/id_0001.txt"
|
| 29 |
},
|
| 30 |
-
"Nữ
|
| 31 |
"audio": "./sample/id_0002.wav",
|
| 32 |
"text": "./sample/id_0002.txt"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
}
|
| 34 |
}
|
| 35 |
|
| 36 |
-
@spaces.GPU(duration=120)
|
| 37 |
def synthesize_speech(text, voice_choice, custom_audio=None, custom_text=None):
|
| 38 |
-
"""
|
| 39 |
-
Tổng hợp giọng nói từ văn bản - Chạy trên GPU
|
| 40 |
-
"""
|
| 41 |
try:
|
| 42 |
-
# Kiểm tra text input
|
| 43 |
if not text or text.strip() == "":
|
| 44 |
return None, "❌ Vui lòng nhập văn bản cần tổng hợp"
|
| 45 |
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
return None, "❌ Văn bản quá dài! Vui lòng nhập tối đa 500 ký tự"
|
| 49 |
|
| 50 |
# Xác định reference audio và text
|
| 51 |
if custom_audio is not None and custom_text:
|
| 52 |
ref_audio_path = custom_audio
|
| 53 |
-
|
|
|
|
| 54 |
elif voice_choice in VOICE_SAMPLES:
|
| 55 |
ref_audio_path = VOICE_SAMPLES[voice_choice]["audio"]
|
| 56 |
ref_text_path = VOICE_SAMPLES[voice_choice]["text"]
|
| 57 |
with open(ref_text_path, "r", encoding="utf-8") as f:
|
| 58 |
-
|
|
|
|
| 59 |
else:
|
| 60 |
return None, "❌ Vui lòng chọn giọng hoặc tải lên audio tùy chỉnh"
|
| 61 |
|
| 62 |
-
#
|
| 63 |
-
device = "cuda" if torch.cuda.is_available() else "cpu"
|
| 64 |
-
if device == "cuda":
|
| 65 |
-
print("🚀 Đang chuyển model lên GPU...")
|
| 66 |
-
tts.backbone = tts.backbone.to("cuda")
|
| 67 |
-
tts.codec = tts.codec.to("cuda")
|
| 68 |
-
|
| 69 |
-
# Encode reference audio
|
| 70 |
print(f"📝 Đang xử lý: {text[:50]}...")
|
| 71 |
ref_codes = tts.encode_reference(ref_audio_path)
|
| 72 |
|
| 73 |
-
# Tổng hợp giọng nói
|
| 74 |
print(f"🎵 Đang tổng hợp giọng nói trên {device.upper()}...")
|
| 75 |
-
wav = tts.infer(text, ref_codes,
|
| 76 |
-
|
| 77 |
-
# Di chuyển model về CPU
|
| 78 |
-
if device == "cuda":
|
| 79 |
-
print("💾 Đang giải phóng GPU...")
|
| 80 |
-
tts.backbone = tts.backbone.to("cpu")
|
| 81 |
-
tts.codec = tts.codec.to("cpu")
|
| 82 |
-
torch.cuda.empty_cache()
|
| 83 |
|
| 84 |
-
# Lưu file
|
| 85 |
with tempfile.NamedTemporaryFile(delete=False, suffix=".wav") as tmp_file:
|
| 86 |
sf.write(tmp_file.name, wav, 24000)
|
| 87 |
output_path = tmp_file.name
|
| 88 |
|
| 89 |
print("✅ Hoàn thành!")
|
| 90 |
-
return output_path, f"✅ Tổng hợp thành công
|
| 91 |
|
| 92 |
except Exception as e:
|
| 93 |
print(f"❌ Lỗi: {str(e)}")
|
| 94 |
import traceback
|
| 95 |
traceback.print_exc()
|
| 96 |
-
|
| 97 |
-
# Giải phóng GPU khi có lỗi
|
| 98 |
-
try:
|
| 99 |
-
if torch.cuda.is_available():
|
| 100 |
-
tts.backbone = tts.backbone.to("cpu")
|
| 101 |
-
tts.codec = tts.codec.to("cpu")
|
| 102 |
-
torch.cuda.empty_cache()
|
| 103 |
-
except:
|
| 104 |
-
pass
|
| 105 |
-
|
| 106 |
return None, f"❌ Lỗi: {str(e)}"
|
| 107 |
|
| 108 |
-
#
|
| 109 |
-
examples = [
|
| 110 |
-
["Legacy là một bộ phim đột phá về mặt âm nhạc, quay phim, hiệu ứng đặc biệt, và tôi rất mừng vì cuối cùng nó cũng được cả giới phê bình lẫn người hâm mộ đánh giá lại. Chúng ta đã quá bất công với bộ phim này vào năm 2010.", "Nam miền Nam"],
|
| 111 |
-
["Từ nhiều nguồn tài liệu lịch sử, có thể thấy nuôi con theo phong cách Do Thái không chỉ tốt cho đứa trẻ mà còn tốt cho cả các bậc cha mẹ.", "Nữ miền Nam"],
|
| 112 |
-
["Các bác sĩ đang nghiên cứu một loại vaccine mới chống lại virus cúm mùa. Thí nghiệm lâm sàng cho thấy phản ứng miễn dịch mạnh mẽ và ít tác dụng phụ, mở ra hy vọng phòng chống dịch bệnh hiệu quả hơn trong tương lai.", "Nam miền Nam"],
|
| 113 |
-
]
|
| 114 |
-
|
| 115 |
-
# Custom CSS
|
| 116 |
custom_css = """
|
| 117 |
.gradio-container {
|
| 118 |
max-width: 900px !important;
|
|
|
|
| 119 |
}
|
| 120 |
-
|
| 121 |
-
background-color: #
|
| 122 |
-
border:
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
margin: 10px 0;
|
| 126 |
-
}
|
| 127 |
-
#info {
|
| 128 |
-
background-color: #d1ecf1;
|
| 129 |
-
border: 1px solid #17a2b8;
|
| 130 |
-
border-radius: 5px;
|
| 131 |
-
padding: 10px;
|
| 132 |
margin: 10px 0;
|
|
|
|
| 133 |
}
|
| 134 |
"""
|
| 135 |
|
| 136 |
-
# Tạo giao diện
|
| 137 |
with gr.Blocks(title="VieNeu-TTS", css=custom_css, theme=gr.themes.Soft()) as demo:
|
| 138 |
gr.Markdown("""
|
| 139 |
-
# 🎙️ VieNeu-TTS: Vietnamese Text-to-Speech
|
| 140 |
|
| 141 |
-
Hệ thống tổng hợp tiếng nói tiếng Việt được **finetune từ NeuTTS-Air** - một mô hình TTS tiên tiến sử dụng Large Language Model và Neural Codec.
|
| 142 |
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
| 147 |
-
|
|
|
|
|
|
|
| 148 |
|
|
|
|
|
|
|
| 149 |
with gr.Row():
|
| 150 |
-
|
| 151 |
-
|
|
|
|
|
|
|
| 152 |
text_input = gr.Textbox(
|
| 153 |
-
label="
|
| 154 |
-
placeholder="Nhập văn bản tiếng Việt...",
|
| 155 |
-
lines=
|
| 156 |
-
|
| 157 |
-
value="Legacy là một bộ phim đột phá về mặt âm nhạc, quay phim, hiệu ứng đặc biệt, và tôi rất mừng vì cuối cùng nó cũng được cả giới phê bình lẫn người hâm mộ đánh giá lại. Chúng ta đã quá bất công với bộ phim này vào năm 2010." # Example 1 làm mặc định
|
| 158 |
)
|
| 159 |
|
| 160 |
-
|
| 161 |
-
char_count = gr.Markdown("209 / 500 ký tự") # Update số ký tự của example 1
|
| 162 |
|
| 163 |
-
# Voice selection
|
| 164 |
voice_select = gr.Radio(
|
| 165 |
choices=list(VOICE_SAMPLES.keys()),
|
| 166 |
-
label="
|
| 167 |
-
value="Nam
|
| 168 |
)
|
| 169 |
|
| 170 |
-
|
| 171 |
-
|
| 172 |
-
|
| 173 |
-
|
| 174 |
-
|
| 175 |
-
|
| 176 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 177 |
custom_text = gr.Textbox(
|
| 178 |
-
label="Nội dung
|
| 179 |
placeholder="Nhập chính xác nội dung...",
|
| 180 |
lines=2
|
| 181 |
)
|
| 182 |
-
|
| 183 |
-
|
| 184 |
-
|
| 185 |
-
|
| 186 |
-
|
| 187 |
-
|
| 188 |
-
audio_output = gr.Audio(label="🔊 Kết quả")
|
| 189 |
-
status_output = gr.Textbox(label="📊 Trạng thái", interactive=False)
|
| 190 |
|
| 191 |
# Examples
|
| 192 |
-
gr.
|
| 193 |
-
|
| 194 |
-
|
| 195 |
-
|
| 196 |
-
|
| 197 |
-
|
| 198 |
-
|
| 199 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 200 |
|
| 201 |
# Update character count
|
| 202 |
def update_char_count(text):
|
| 203 |
count = len(text) if text else 0
|
| 204 |
-
color = "
|
| 205 |
-
return f"<span style='color: {color}'>{count} /
|
| 206 |
|
| 207 |
text_input.change(
|
| 208 |
fn=update_char_count,
|
|
@@ -216,17 +223,6 @@ with gr.Blocks(title="VieNeu-TTS", css=custom_css, theme=gr.themes.Soft()) as de
|
|
| 216 |
inputs=[text_input, voice_select, custom_audio, custom_text],
|
| 217 |
outputs=[audio_output, status_output]
|
| 218 |
)
|
| 219 |
-
|
| 220 |
-
gr.Markdown("""
|
| 221 |
-
---
|
| 222 |
-
### 📌 Thông tin
|
| 223 |
-
|
| 224 |
-
**Liên kết:**
|
| 225 |
-
- [GitHub Repository](https://github.com/pnnbao97/VieNeu-TTS)
|
| 226 |
-
- [Model Card](https://huggingface.co/pnnbao-ump/VieNeu-TTS)
|
| 227 |
-
|
| 228 |
-
<sub>Powered by VieNeu-TTS | Built with ❤️ for Vietnamese TTS</sub>
|
| 229 |
-
""")
|
| 230 |
|
| 231 |
# Launch
|
| 232 |
if __name__ == "__main__":
|
|
|
|
| 1 |
import spaces # PHẢI import TRƯỚC mọi thứ!
|
| 2 |
import os
|
| 3 |
os.environ['SPACES_ZERO_GPU'] = '1' # Set environment variable
|
|
|
|
| 4 |
import gradio as gr
|
|
|
|
| 5 |
import soundfile as sf
|
| 6 |
import tempfile
|
| 7 |
import torch
|
| 8 |
+
from vieneu_tts import VieNeuTTS
|
| 9 |
|
| 10 |
+
print("⏳ Đang khởi động VieNeu-TTS...")
|
|
|
|
| 11 |
|
| 12 |
+
# Khởi tạo model
|
| 13 |
print("📦 Đang tải model...")
|
| 14 |
+
device = "cuda" if torch.cuda.is_available() else "cpu"
|
| 15 |
+
print(f"🖥️ Sử dụng thiết bị: {device.upper()}")
|
| 16 |
+
|
| 17 |
tts = VieNeuTTS(
|
| 18 |
+
backbone_repo="pnnbao-ump/VieNeu-TTS-1000h",
|
| 19 |
+
backbone_device=device,
|
| 20 |
codec_repo="neuphonic/neucodec",
|
| 21 |
+
codec_device=device
|
| 22 |
)
|
| 23 |
print("✅ Model đã tải xong!")
|
| 24 |
|
| 25 |
# Danh sách giọng mẫu
|
| 26 |
VOICE_SAMPLES = {
|
| 27 |
+
"Nam 1": {
|
| 28 |
"audio": "./sample/id_0001.wav",
|
| 29 |
"text": "./sample/id_0001.txt"
|
| 30 |
},
|
| 31 |
+
"Nữ 1": {
|
| 32 |
"audio": "./sample/id_0002.wav",
|
| 33 |
"text": "./sample/id_0002.txt"
|
| 34 |
+
},
|
| 35 |
+
"Nam 2": {
|
| 36 |
+
"audio": "./sample/id_0003.wav",
|
| 37 |
+
"text": "./sample/id_0003.txt"
|
| 38 |
+
},
|
| 39 |
+
"Nữ 2": {
|
| 40 |
+
"audio": "./sample/id_0004.wav",
|
| 41 |
+
"text": "./sample/id_0004.txt"
|
| 42 |
+
},
|
| 43 |
+
"Nam 3": {
|
| 44 |
+
"audio": "./sample/id_0005.wav",
|
| 45 |
+
"text": "./sample/id_0005.txt"
|
| 46 |
+
},
|
| 47 |
+
"Nam 4": {
|
| 48 |
+
"audio": "./sample/id_0007.wav",
|
| 49 |
+
"text": "./sample/id_0007.txt"
|
| 50 |
}
|
| 51 |
}
|
| 52 |
|
| 53 |
+
@spaces.GPU(duration=120)
|
| 54 |
def synthesize_speech(text, voice_choice, custom_audio=None, custom_text=None):
|
| 55 |
+
"""Tổng hợp giọng nói từ văn bản"""
|
|
|
|
|
|
|
| 56 |
try:
|
|
|
|
| 57 |
if not text or text.strip() == "":
|
| 58 |
return None, "❌ Vui lòng nhập văn bản cần tổng hợp"
|
| 59 |
|
| 60 |
+
if len(text) > 250:
|
| 61 |
+
return None, "❌ Văn bản quá dài! Vui lòng nhập tối đa 250 ký tự. Để tổng hợp văn bản dài hơn, vui lòng tham khảo examples/infer_long_text.py"
|
|
|
|
| 62 |
|
| 63 |
# Xác định reference audio và text
|
| 64 |
if custom_audio is not None and custom_text:
|
| 65 |
ref_audio_path = custom_audio
|
| 66 |
+
ref_text_raw = custom_text
|
| 67 |
+
print("🎨 Sử dụng giọng tùy chỉnh")
|
| 68 |
elif voice_choice in VOICE_SAMPLES:
|
| 69 |
ref_audio_path = VOICE_SAMPLES[voice_choice]["audio"]
|
| 70 |
ref_text_path = VOICE_SAMPLES[voice_choice]["text"]
|
| 71 |
with open(ref_text_path, "r", encoding="utf-8") as f:
|
| 72 |
+
ref_text_raw = f.read()
|
| 73 |
+
print(f"🎤 Sử dụng giọng: {voice_choice}")
|
| 74 |
else:
|
| 75 |
return None, "❌ Vui lòng chọn giọng hoặc tải lên audio tùy chỉnh"
|
| 76 |
|
| 77 |
+
# Encode và tổng hợp
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 78 |
print(f"📝 Đang xử lý: {text[:50]}...")
|
| 79 |
ref_codes = tts.encode_reference(ref_audio_path)
|
| 80 |
|
|
|
|
| 81 |
print(f"🎵 Đang tổng hợp giọng nói trên {device.upper()}...")
|
| 82 |
+
wav = tts.infer(text, ref_codes, ref_text_raw)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 83 |
|
| 84 |
+
# Lưu file
|
| 85 |
with tempfile.NamedTemporaryFile(delete=False, suffix=".wav") as tmp_file:
|
| 86 |
sf.write(tmp_file.name, wav, 24000)
|
| 87 |
output_path = tmp_file.name
|
| 88 |
|
| 89 |
print("✅ Hoàn thành!")
|
| 90 |
+
return output_path, f"✅ Tổng hợp thành công"
|
| 91 |
|
| 92 |
except Exception as e:
|
| 93 |
print(f"❌ Lỗi: {str(e)}")
|
| 94 |
import traceback
|
| 95 |
traceback.print_exc()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 96 |
return None, f"❌ Lỗi: {str(e)}"
|
| 97 |
|
| 98 |
+
# Custom CSS - tối giản
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 99 |
custom_css = """
|
| 100 |
.gradio-container {
|
| 101 |
max-width: 900px !important;
|
| 102 |
+
margin: 0 auto !important;
|
| 103 |
}
|
| 104 |
+
.warning-box {
|
| 105 |
+
background-color: #fef3c7;
|
| 106 |
+
border-left: 4px solid #f59e0b;
|
| 107 |
+
padding: 12px 16px;
|
| 108 |
+
border-radius: 6px;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 109 |
margin: 10px 0;
|
| 110 |
+
color: #000000;
|
| 111 |
}
|
| 112 |
"""
|
| 113 |
|
| 114 |
+
# Tạo giao diện
|
| 115 |
with gr.Blocks(title="VieNeu-TTS", css=custom_css, theme=gr.themes.Soft()) as demo:
|
| 116 |
gr.Markdown("""
|
|
|
|
| 117 |
|
|
|
|
| 118 |
|
| 119 |
+
# VieNeu-TTS
|
| 120 |
+
|
| 121 |
+
Hệ thống tổng hợp tiếng nói tiếng Việt sử dụng Large Language Model
|
| 122 |
+
|
| 123 |
+
**Phiên bản:** VieNeu-TTS-1000h (model mới nhất, train trên 1000 giờ dữ liệu)
|
| 124 |
+
|
| 125 |
+
[GitHub](https://github.com/pnnbao97/VieNeu-TTS) • [Model Card](https://huggingface.co/pnnbao-ump/VieNeu-TTS) • [Finetune Guide](https://github.com/pnnbao-ump/VieNeuTTS/blob/main/finetune.ipynb)
|
| 126 |
|
| 127 |
+
""")
|
| 128 |
+
# Main interface
|
| 129 |
with gr.Row():
|
| 130 |
+
|
| 131 |
+
|
| 132 |
+
with gr.Column(scale=1):
|
| 133 |
+
|
| 134 |
text_input = gr.Textbox(
|
| 135 |
+
label="Văn bản",
|
| 136 |
+
placeholder="Nhập văn bản tiếng Việt (khuyến cáo dưới 250 ký tự)...",
|
| 137 |
+
lines=5,
|
| 138 |
+
value="Trí tuệ nhân tạo đang cách mạng hóa nhiều lĩnh vực, từ y tế, giáo dục đến giao thông vận tải, mang lại những giải pháp thông minh và hiệu quả."
|
|
|
|
| 139 |
)
|
| 140 |
|
| 141 |
+
char_count = gr.Markdown("**142 / 250 ký tự**")
|
|
|
|
| 142 |
|
|
|
|
| 143 |
voice_select = gr.Radio(
|
| 144 |
choices=list(VOICE_SAMPLES.keys()),
|
| 145 |
+
label="Chọn giọng",
|
| 146 |
+
value="Nam 1"
|
| 147 |
)
|
| 148 |
|
| 149 |
+
submit_btn = gr.Button("Tổng hợp", variant="primary", size="lg")
|
| 150 |
+
|
| 151 |
+
with gr.Column(scale=1):
|
| 152 |
+
audio_output = gr.Audio(label="Kết quả", type="filepath")
|
| 153 |
+
status_output = gr.Textbox(label="Trạng thái", interactive=False, show_label=False)
|
| 154 |
+
|
| 155 |
+
with gr.Accordion("Giọng tùy chỉnh", open=False):
|
| 156 |
+
gr.Markdown("""
|
| 157 |
+
Tải lên file audio và nhập nội dung tương ứng. Để có kết quả tốt nhất, nên finetune model trên giọng của bạn.
|
| 158 |
+
""")
|
| 159 |
+
custom_audio = gr.Audio(label="File audio (.wav)", type="filepath")
|
| 160 |
custom_text = gr.Textbox(
|
| 161 |
+
label="Nội dung audio",
|
| 162 |
placeholder="Nhập chính xác nội dung...",
|
| 163 |
lines=2
|
| 164 |
)
|
| 165 |
+
gr.HTML("""
|
| 166 |
+
<div class="warning-box" style="color: #000000;">
|
| 167 |
+
⚠️ Chúng tôi khuyến cáo sử dụng đoạn văn bản <250 ký tự để đảm bảo chất lượng tốt nhất.
|
| 168 |
+
Nếu muốn tổng hợp văn bản dài hơn, vui lòng tham khảo code trong examples/infer_long_text.py
|
| 169 |
+
</div>
|
| 170 |
+
""")
|
|
|
|
|
|
|
| 171 |
|
| 172 |
# Examples
|
| 173 |
+
with gr.Row():
|
| 174 |
+
gr.Examples(
|
| 175 |
+
examples=[
|
| 176 |
+
["Trí tuệ nhân tạo đang cách mạng hóa nhiều lĩnh vực, từ y tế, giáo dục đến giao thông vận tải, mang lại những giải pháp thông minh và hiệu quả.", "Nam 1"],
|
| 177 |
+
["Trên bầu trời xanh thẳm, những đám mây trắng lửng lờ trôi như những chiếc thuyền nhỏ đang lướt nhẹ theo dòng gió. Dưới mặt đất, cánh đồng lúa vàng rực trải dài tới tận chân trời, những bông lúa nghiêng mình theo từng làn gió.", "Nữ 2"],
|
| 178 |
+
["Legacy là một bộ phim đột phá về mặt âm nhạc, quay phim, hiệu ứng đặc biệt, và tôi rất mừng vì cuối cùng nó cũng được cả giới phê bình lẫn người hâm mộ đánh giá lại. Chúng ta đã quá bất công với bộ phim này vào năm 2010.", "Nam 4"],
|
| 179 |
+
["Thật đáng ngạc nhiên! Mặc dù con đường này rất xa và khó đi, nhưng với sự kiên trì và sự đồng lòng của tất cả mọi người, chúng ta đã hoàn thành được công việc sửa chữa trước 3 ngày so với kế hoạch ban đầu, bạn có tin không?", "Nữ 1"],
|
| 180 |
+
["Các bác sĩ đang nghiên cứu một loại vaccine mới chống lại virus cúm mùa. Thí nghiệm lâm sàng cho thấy phản ứng miễn dịch mạnh mẽ và ít tác dụng phụ.", "Nam 2"],
|
| 181 |
+
],
|
| 182 |
+
inputs=[text_input, voice_select],
|
| 183 |
+
outputs=[audio_output, status_output],
|
| 184 |
+
fn=synthesize_speech,
|
| 185 |
+
cache_examples=False
|
| 186 |
+
)
|
| 187 |
+
|
| 188 |
+
# Footer info
|
| 189 |
+
gr.Markdown("""
|
| 190 |
+
---
|
| 191 |
+
|
| 192 |
+
**Tác giả:** Phạm Nguyễn Ngọc Bảo • **Model:** VieNeu-TTS-1000h
|
| 193 |
+
|
| 194 |
+
**Lưu ý:** Nếu muốn sử dụng model cũ VieNeu-TTS-140h, hãy thay đổi `backbone_repo` trong mã nguồn
|
| 195 |
+
|
| 196 |
+
---
|
| 197 |
+
|
| 198 |
+
### Ủng hộ dự án
|
| 199 |
+
|
| 200 |
+
VieNeu-TTS là dự án miễn phí và mã nguồn mở. Tuy nhiên, việc train model TTS chất lượng cao trên 1000+ giờ dữ liệu đòi hỏi nguồn lực tính toán đáng kể.
|
| 201 |
+
|
| 202 |
+
Nếu bạn thấy dự án này hữu ích, hãy cân nhắc ủng hộ:
|
| 203 |
+
|
| 204 |
+
☕ [Buy Me a Coffee](https://buymeacoffee.com/pnnbao)
|
| 205 |
+
|
| 206 |
+
""")
|
| 207 |
|
| 208 |
# Update character count
|
| 209 |
def update_char_count(text):
|
| 210 |
count = len(text) if text else 0
|
| 211 |
+
color = "#dc2626" if count > 250 else "#374151"
|
| 212 |
+
return f"<span style='color: {color}; font-weight: 500'>{count} / 250 ký tự</span>"
|
| 213 |
|
| 214 |
text_input.change(
|
| 215 |
fn=update_char_count,
|
|
|
|
| 223 |
inputs=[text_input, voice_select, custom_audio, custom_text],
|
| 224 |
outputs=[audio_output, status_output]
|
| 225 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 226 |
|
| 227 |
# Launch
|
| 228 |
if __name__ == "__main__":
|