Spaces:
Runtime error
Runtime error
Update app.py
Browse files
app.py
CHANGED
|
@@ -1,16 +1,15 @@
|
|
| 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 |
# Import vieneutts SAU khi đã setup spaces
|
| 12 |
from vieneutts import VieNeuTTS
|
| 13 |
-
|
| 14 |
# Khởi tạo model trên CPU trước
|
| 15 |
print("📦 Đang tải model...")
|
| 16 |
tts = VieNeuTTS(
|
|
@@ -20,7 +19,6 @@ tts = VieNeuTTS(
|
|
| 20 |
codec_device="cpu"
|
| 21 |
)
|
| 22 |
print("✅ Model đã tải xong!")
|
| 23 |
-
|
| 24 |
# Danh sách giọng mẫu
|
| 25 |
VOICE_SAMPLES = {
|
| 26 |
"Nam miền Nam": {
|
|
@@ -32,6 +30,8 @@ VOICE_SAMPLES = {
|
|
| 32 |
"text": "./sample/id_0002.txt"
|
| 33 |
}
|
| 34 |
}
|
|
|
|
|
|
|
| 35 |
|
| 36 |
@spaces.GPU(duration=120) # Giữ GPU trong 120 giây
|
| 37 |
def synthesize_speech(text, voice_choice, custom_audio=None, custom_text=None):
|
|
@@ -41,11 +41,11 @@ def synthesize_speech(text, voice_choice, custom_audio=None, custom_text=None):
|
|
| 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 |
# Giới hạn độ dài text
|
| 47 |
if len(text) > 500:
|
| 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:
|
|
@@ -57,7 +57,7 @@ def synthesize_speech(text, voice_choice, custom_audio=None, custom_text=None):
|
|
| 57 |
with open(ref_text_path, "r", encoding="utf-8") as f:
|
| 58 |
ref_text = f.read()
|
| 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 |
# Di chuyển model lên GPU
|
| 63 |
device = "cuda" if torch.cuda.is_available() else "cpu"
|
|
@@ -86,8 +86,17 @@ def synthesize_speech(text, voice_choice, custom_audio=None, custom_text=None):
|
|
| 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 trên {device.upper()}!"
|
| 91 |
|
| 92 |
except Exception as e:
|
| 93 |
print(f"❌ Lỗi: {str(e)}")
|
|
@@ -103,7 +112,24 @@ def synthesize_speech(text, voice_choice, custom_audio=None, custom_text=None):
|
|
| 103 |
except:
|
| 104 |
pass
|
| 105 |
|
| 106 |
-
return None, f"❌ Lỗi: {str(e)}"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 107 |
|
| 108 |
# Các ví dụ mẫu
|
| 109 |
examples = [
|
|
@@ -111,7 +137,6 @@ examples = [
|
|
| 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 {
|
|
@@ -132,17 +157,14 @@ custom_css = """
|
|
| 132 |
margin: 10px 0;
|
| 133 |
}
|
| 134 |
"""
|
| 135 |
-
|
| 136 |
# Tạo giao diện Gradio
|
| 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 |
-
Code: [GitHub](https://github.com/pnnbao97/VieNeu-TTS)
|
| 146 |
Demo: [Hugging Face](https://huggingface.co/spaces/pnnbao-ump/VieNeu-TTS)
|
| 147 |
""")
|
| 148 |
|
|
@@ -188,12 +210,22 @@ with gr.Blocks(title="VieNeu-TTS", css=custom_css, theme=gr.themes.Soft()) as de
|
|
| 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.Markdown("### 💡 Ví dụ nhanh")
|
| 193 |
gr.Examples(
|
| 194 |
examples=examples,
|
| 195 |
inputs=[text_input, voice_select],
|
| 196 |
-
outputs=[audio_output, status_output],
|
| 197 |
fn=synthesize_speech,
|
| 198 |
cache_examples=False
|
| 199 |
)
|
|
@@ -210,11 +242,18 @@ with gr.Blocks(title="VieNeu-TTS", css=custom_css, theme=gr.themes.Soft()) as de
|
|
| 210 |
outputs=[char_count]
|
| 211 |
)
|
| 212 |
|
| 213 |
-
# Event handler
|
| 214 |
submit_btn.click(
|
| 215 |
fn=synthesize_speech,
|
| 216 |
inputs=[text_input, voice_select, custom_audio, custom_text],
|
| 217 |
-
outputs=[audio_output, status_output]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 218 |
)
|
| 219 |
|
| 220 |
gr.Markdown("""
|
|
|
|
| 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 numpy as np
|
| 6 |
import soundfile as sf
|
| 7 |
import tempfile
|
| 8 |
import torch
|
| 9 |
+
import time
|
| 10 |
+
import pandas as pd
|
| 11 |
# Import vieneutts SAU khi đã setup spaces
|
| 12 |
from vieneutts import VieNeuTTS
|
|
|
|
| 13 |
# Khởi tạo model trên CPU trước
|
| 14 |
print("📦 Đang tải model...")
|
| 15 |
tts = VieNeuTTS(
|
|
|
|
| 19 |
codec_device="cpu"
|
| 20 |
)
|
| 21 |
print("✅ Model đã tải xong!")
|
|
|
|
| 22 |
# Danh sách giọng mẫu
|
| 23 |
VOICE_SAMPLES = {
|
| 24 |
"Nam miền Nam": {
|
|
|
|
| 30 |
"text": "./sample/id_0002.txt"
|
| 31 |
}
|
| 32 |
}
|
| 33 |
+
# Lưu lịch sử (global list)
|
| 34 |
+
history = []
|
| 35 |
|
| 36 |
@spaces.GPU(duration=120) # Giữ GPU trong 120 giây
|
| 37 |
def synthesize_speech(text, voice_choice, custom_audio=None, custom_text=None):
|
|
|
|
| 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", None
|
| 45 |
|
| 46 |
# Giới hạn độ dài text
|
| 47 |
if len(text) > 500:
|
| 48 |
+
return None, "❌ Văn bản quá dài! Vui lòng nhập tối đa 500 ký tự", None
|
| 49 |
|
| 50 |
# Xác định reference audio và text
|
| 51 |
if custom_audio is not None and custom_text:
|
|
|
|
| 57 |
with open(ref_text_path, "r", encoding="utf-8") as f:
|
| 58 |
ref_text = f.read()
|
| 59 |
else:
|
| 60 |
+
return None, "❌ Vui lòng chọn giọng hoặc tải lên audio tùy chỉnh", None
|
| 61 |
|
| 62 |
# Di chuyển model lên GPU
|
| 63 |
device = "cuda" if torch.cuda.is_available() else "cpu"
|
|
|
|
| 86 |
sf.write(tmp_file.name, wav, 24000)
|
| 87 |
output_path = tmp_file.name
|
| 88 |
|
| 89 |
+
# Lưu vào lịch sử
|
| 90 |
+
timestamp = time.strftime("%Y-%m-%d %H:%M:%S")
|
| 91 |
+
history.append({
|
| 92 |
+
"Thời gian": timestamp,
|
| 93 |
+
"Văn bản": text,
|
| 94 |
+
"Giọng": voice_choice if voice_choice else "Tùy chỉnh",
|
| 95 |
+
"Audio": output_path
|
| 96 |
+
})
|
| 97 |
+
|
| 98 |
print("✅ Hoàn thành!")
|
| 99 |
+
return output_path, f"✅ Tổng hợp thành công trên {device.upper()}!", update_history()
|
| 100 |
|
| 101 |
except Exception as e:
|
| 102 |
print(f"❌ Lỗi: {str(e)}")
|
|
|
|
| 112 |
except:
|
| 113 |
pass
|
| 114 |
|
| 115 |
+
return None, f"❌ Lỗi: {str(e)}", None
|
| 116 |
+
|
| 117 |
+
# Hàm cập nhật lịch sử cho Dataframe
|
| 118 |
+
def update_history():
|
| 119 |
+
if not history:
|
| 120 |
+
return pd.DataFrame(columns=["Thời gian", "Văn bản", "Giọng", "Audio"])
|
| 121 |
+
df = pd.DataFrame(history)
|
| 122 |
+
df = df[["Thời gian", "Văn bản", "Giọng"]] # Không hiển thị path audio đầy đủ trong df
|
| 123 |
+
return df
|
| 124 |
+
|
| 125 |
+
# Hàm tải về lịch sử dưới dạng CSV
|
| 126 |
+
def download_history():
|
| 127 |
+
if not history:
|
| 128 |
+
return None
|
| 129 |
+
df = pd.DataFrame(history)
|
| 130 |
+
with tempfile.NamedTemporaryFile(delete=False, suffix=".csv") as tmp_file:
|
| 131 |
+
df.to_csv(tmp_file.name, index=False, encoding="utf-8-sig")
|
| 132 |
+
return tmp_file.name
|
| 133 |
|
| 134 |
# Các ví dụ mẫu
|
| 135 |
examples = [
|
|
|
|
| 137 |
["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"],
|
| 138 |
["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"],
|
| 139 |
]
|
|
|
|
| 140 |
# Custom CSS
|
| 141 |
custom_css = """
|
| 142 |
.gradio-container {
|
|
|
|
| 157 |
margin: 10px 0;
|
| 158 |
}
|
| 159 |
"""
|
|
|
|
| 160 |
# Tạo giao diện Gradio
|
| 161 |
with gr.Blocks(title="VieNeu-TTS", css=custom_css, theme=gr.themes.Soft()) as demo:
|
| 162 |
gr.Markdown("""
|
| 163 |
# 🎙️ VieNeu-TTS: Vietnamese Text-to-Speech
|
|
|
|
| 164 |
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.
|
| 165 |
+
Tác giả: [Phạm Nguyễn Ngọc Bảo](https://github.com/pnnbao97)
|
| 166 |
+
Model: [VieNeu-TTS](https://huggingface.co/pnnbao-ump/VieNeu-TTS)
|
| 167 |
+
Code: [GitHub](https://github.com/pnnbao97/VieNeu-TTS)
|
|
|
|
| 168 |
Demo: [Hugging Face](https://huggingface.co/spaces/pnnbao-ump/VieNeu-TTS)
|
| 169 |
""")
|
| 170 |
|
|
|
|
| 210 |
audio_output = gr.Audio(label="🔊 Kết quả")
|
| 211 |
status_output = gr.Textbox(label="📊 Trạng thái", interactive=False)
|
| 212 |
|
| 213 |
+
# Lịch sử
|
| 214 |
+
gr.Markdown("### 📜 Lịch sử tổng hợp")
|
| 215 |
+
history_df = gr.Dataframe(
|
| 216 |
+
value=update_history(),
|
| 217 |
+
headers=["Thời gian", "Văn bản", "Giọng"],
|
| 218 |
+
interactive=False
|
| 219 |
+
)
|
| 220 |
+
download_btn = gr.Button("📥 Tải về lịch sử (CSV)")
|
| 221 |
+
download_file = gr.File(label="Tệp tải về", interactive=False)
|
| 222 |
+
|
| 223 |
# Examples
|
| 224 |
gr.Markdown("### 💡 Ví dụ nhanh")
|
| 225 |
gr.Examples(
|
| 226 |
examples=examples,
|
| 227 |
inputs=[text_input, voice_select],
|
| 228 |
+
outputs=[audio_output, status_output, history_df],
|
| 229 |
fn=synthesize_speech,
|
| 230 |
cache_examples=False
|
| 231 |
)
|
|
|
|
| 242 |
outputs=[char_count]
|
| 243 |
)
|
| 244 |
|
| 245 |
+
# Event handler for synthesize
|
| 246 |
submit_btn.click(
|
| 247 |
fn=synthesize_speech,
|
| 248 |
inputs=[text_input, voice_select, custom_audio, custom_text],
|
| 249 |
+
outputs=[audio_output, status_output, history_df]
|
| 250 |
+
)
|
| 251 |
+
|
| 252 |
+
# Event handler for download
|
| 253 |
+
download_btn.click(
|
| 254 |
+
fn=download_history,
|
| 255 |
+
inputs=[],
|
| 256 |
+
outputs=[download_file]
|
| 257 |
)
|
| 258 |
|
| 259 |
gr.Markdown("""
|