pnnbao-ump commited on
Commit
8dc4072
·
1 Parent(s): a7ef740

deploy VieNeu-TTS-1000h

Browse files
Files changed (1) hide show
  1. app.py +122 -126
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
- # 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(
17
- backbone_repo="pnnbao-ump/VieNeu-TTS",
18
- backbone_device="cpu", # Load trên CPU trước
19
  codec_repo="neuphonic/neucodec",
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": {
27
  "audio": "./sample/id_0001.wav",
28
  "text": "./sample/id_0001.txt"
29
  },
30
- "Nữ miền Nam": {
31
  "audio": "./sample/id_0002.wav",
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):
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
- # 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:
52
  ref_audio_path = custom_audio
53
- ref_text = custom_text
 
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
- 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"
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, ref_text)
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 tạm
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 trên {device.upper()}!"
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
- # Các dụ mẫu
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
- #warning {
121
- background-color: #fff3cd;
122
- border: 1px solid #ffc107;
123
- border-radius: 5px;
124
- padding: 10px;
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 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
- Tác giả: [Phạm Nguyễn Ngọc Bảo](https://github.com/pnnbao97)
144
- Model: [VieNeu-TTS](https://huggingface.co/pnnbao-ump/VieNeu-TTS)
145
- Code: [GitHub](https://github.com/pnnbao97/VieNeu-TTS)
146
- Demo: [Hugging Face](https://huggingface.co/spaces/pnnbao-ump/VieNeu-TTS)
147
- """)
 
 
148
 
 
 
149
  with gr.Row():
150
- with gr.Column():
151
- # Input text
 
 
152
  text_input = gr.Textbox(
153
- label="📝 Văn bản đầu vào (tối đa 500 ký tự)",
154
- placeholder="Nhập văn bản tiếng Việt...",
155
- lines=4,
156
- max_lines=6,
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
- # Character counter
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="🎤 Chọn giọng",
167
- value="Nam miền Nam"
168
  )
169
 
170
- # Custom voice option
171
- with gr.Accordion("🎨 Hoặc sử dụng giọng tùy chỉnh", open=False):
172
- gr.Markdown("*Upload file audio (.wav) và nội dung text tương ứng*")
173
- custom_audio = gr.Audio(
174
- label="File audio mẫu",
175
- type="filepath"
176
- )
 
 
 
 
177
  custom_text = gr.Textbox(
178
- label="Nội dung của audio mẫu",
179
  placeholder="Nhập chính xác nội dung...",
180
  lines=2
181
  )
182
-
183
- # Submit button
184
- submit_btn = gr.Button("🎵 Tổng hợp giọng nói", variant="primary", size="lg")
185
-
186
- with gr.Column():
187
- # Output
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
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
200
 
201
  # Update character count
202
  def update_char_count(text):
203
  count = len(text) if text else 0
204
- color = "red" if count > 500 else "green"
205
- return f"<span style='color: {color}'>{count} / 500 ký tự</span>"
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 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__":