Translsis commited on
Commit
d88d287
·
verified ·
1 Parent(s): bcd292a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +106 -90
app.py CHANGED
@@ -1,6 +1,6 @@
1
- import spaces # PHẢI import TRƯỚC mọi thứ trên HF Spaces ZeroGPU
2
  import os
3
- os.environ['SPACES_ZERO_GPU'] = '1' # Set environment variable explicitly
4
 
5
  import gradio as gr
6
  import soundfile as sf
@@ -64,56 +64,81 @@ def load_reference_info(voice_choice):
64
  return None, ""
65
 
66
  @spaces.GPU(duration=120)
67
- def synthesize_speech(text, voice_choice, custom_audio, custom_text, voice_mode):
 
68
  try:
69
  if not text or text.strip() == "":
70
  return None, "⚠️ Vui lòng nhập văn bản cần tổng hợp!"
71
 
72
  if len(text) > 250:
73
- return None, f"❌ Văn bản quá dài ({len(text)}/250 ký tự)! Vui lòng cắt ngắn lại để đảm bảo chất lượng."
74
 
75
- # Xác định mode dựa vào voice_mode string
76
- use_custom = (voice_mode == "🎙️ Giọng tùy chỉnh (Custom)")
77
 
78
- if use_custom:
79
- if custom_audio is None or not custom_text:
80
- return None, "⚠️ Vui lòng tải lên Audio và nhập nội dung Audio đó."
81
- ref_audio_path = custom_audio
82
- ref_text_raw = custom_text
83
- print("🎨 Mode: Custom Voice")
84
- else:
85
- if voice_choice not in VOICE_SAMPLES:
86
- return None, "⚠️ Vui lòng chọn một giọng mẫu."
87
- ref_audio_path = VOICE_SAMPLES[voice_choice]["audio"]
88
- ref_text_path = VOICE_SAMPLES[voice_choice]["text"]
89
-
90
- if not os.path.exists(ref_audio_path):
91
- return None, f"❌ Không tìm thấy file audio: {ref_audio_path}"
92
-
93
- with open(ref_text_path, "r", encoding="utf-8") as f:
94
- ref_text_raw = f.read()
95
- print(f"🎤 Mode: Preset Voice ({voice_choice})")
96
-
97
  print(f"📝 Text: {text[:50]}...")
98
 
99
  start_time = time.time()
100
-
101
  ref_codes = tts.encode_reference(ref_audio_path)
102
  wav = tts.infer(text, ref_codes, ref_text_raw)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
103
 
 
 
 
 
 
 
104
  end_time = time.time()
 
105
  process_time = end_time - start_time
106
 
107
  with tempfile.NamedTemporaryFile(delete=False, suffix=".wav") as tmp_file:
108
  sf.write(tmp_file.name, wav, 24000)
109
  output_path = tmp_file.name
110
 
111
- return output_path, f"✅ Thành công! (Mất {process_time:.2f} giây để tạo)"
112
 
113
  except Exception as e:
114
  import traceback
115
  traceback.print_exc()
116
- return None, f"❌ Lỗi hệ thống: {str(e)}"
117
 
118
  # --- 4. UI SETUP ---
119
  theme = gr.themes.Soft(
@@ -167,11 +192,6 @@ EXAMPLES_LIST = [
167
  ["Về miền Tây không chỉ để ngắm nhìn sông nước hữu tình, mà còn để cảm nhận tấm chân tình của người dân nơi đây. Cùng ngồi xuồng ba lá len lỏi qua rặng dừa nước, nghe câu vọng cổ ngọt ngào thì còn gì b��ng.", "Vĩnh (nam miền Nam)"],
168
  ["Hà Nội những ngày vào thu mang một vẻ đẹp trầm mặc và cổ kính đến lạ thường. Đi dạo quanh Hồ Gươm vào sáng sớm, hít hà mùi hoa sữa nồng nàn và thưởng thức chút cốm làng Vòng là trải nghiệm khó quên.", "Bình (nam miền Bắc)"],
169
  ["Sự bùng nổ của trí tuệ nhân tạo đang định hình lại cách chúng ta làm việc và sinh sống. Từ xe tự lái đến trợ lý ảo thông minh, công nghệ đang dần xóa nhòa ranh giới giữa thực tại và những bộ phim viễn tưởng.", "Tuyên (nam miền Bắc)"],
170
- ["Sài Gòn hối hả là thế, nhưng chỉ cần tấp vào một quán cà phê ven đường, gọi ly bạc xỉu đá và ngắm nhìn dòng người qua lại, bạn sẽ thấy thành phố này cũng có những khoảng lặng thật bình yên và đáng yêu.", "Nguyên (nam miền Nam)"],
171
- ["Để đảm bảo tiến độ dự án quan trọng này, chúng ta cần tập trung tối đa nguồn lực và phối hợp chặt chẽ giữa các phòng ban. Mọi khó khăn phát sinh cần được báo cáo ngay lập tức để ban lãnh đạo xử lý kịp thời.", "Sơn (nam miền Nam)"],
172
- ["Ngày xửa ngày xưa, ở một ngôi làng nọ có cô Tấm xinh đẹp, nết na nhưng sớm mồ côi mẹ. Dù bị mẹ kế và Cám hãm hại đủ đường, Tấm vẫn giữ được tấm lòng lương thiện và cuối cùng tìm được hạnh phúc xứng đáng.", "Đoan (nữ miền Nam)"],
173
- ["Dạ em chào anh chị, hiện tại bên em đang có chương trình ưu đãi đặc biệt cho căn hộ hướng sông này. Với thiết kế hiện đại và không gian xanh mát, đây chắc chắn là tổ ấm lý tưởng mà gia đình mình đang tìm kiếm.", "Ly (nữ miền Bắc)"],
174
- ["Dưới cơn mưa phùn lất phất của những ngày cuối đông, em khẽ nép vào vai anh, cảm nhận hơi ấm lan tỏa. Những khoảnh khắc bình dị như thế này khiến em nhận ra rằng, hạnh phúc đôi khi chỉ đơn giản là được ở bên nhau.", "Ngọc (nữ miền Bắc)"],
175
  ]
176
 
177
  with gr.Blocks(theme=theme, css=css, title="VieNeu-TTS Studio") as demo:
@@ -191,62 +211,63 @@ with gr.Blocks(theme=theme, css=css, title="VieNeu-TTS Studio") as demo:
191
  </div>
192
  """)
193
 
194
- with gr.Row(elem_classes="container", equal_height=False):
195
-
196
- # --- LEFT: INPUT ---
197
- with gr.Column(scale=3, variant="panel"):
198
  gr.Markdown("### 📝 Văn bản đầu vào")
199
  text_input = gr.Textbox(
200
  label="Nhập văn bản",
201
  placeholder="Nhập nội dung tiếng Việt cần chuyển thành giọng nói...",
202
  lines=4,
203
- value="Sự bùng nổ của trí tuệ nhân tạo đang định hình lại cách chúng ta làm việc và sinh sống. Từ xe tự lái đến trợ lý ảo thông minh, công nghệ đang dần xóa nhòa ranh giới giữa thực tại và những bộ phim viễn tưởng.",
204
  show_label=False
205
  )
206
-
207
  with gr.Row():
208
  char_count = gr.HTML("<div style='text-align: right; color: #64748B; font-size: 0.8rem;'>0 / 250 ký tự</div>")
209
-
210
- gr.Markdown("### 🗣️ Chọn giọng đọc")
211
-
212
- voice_mode = gr.Radio(
213
- choices=["👤 Giọng có sẵn (Preset)", "🎙️ Giọng tùy chỉnh (Custom)"],
214
- value="👤 Giọng có sẵn (Preset)",
215
- label="Chế độ"
216
- )
217
-
218
- with gr.Group(visible=True) as preset_group:
219
- voice_select = gr.Dropdown(
220
- choices=list(VOICE_SAMPLES.keys()),
221
- value="Tuyên (nam miền Bắc)",
222
- label="Danh sách giọng",
223
- interactive=True
224
- )
225
- with gr.Accordion("Thông tin giọng mẫu", open=False):
226
- ref_audio_preview = gr.Audio(label="Audio mẫu", interactive=False, type="filepath")
227
- ref_text_preview = gr.Markdown("...")
228
-
229
- with gr.Group(visible=False) as custom_group:
230
- gr.Markdown("Tải lên giọng của bạn (Zero-shot Cloning)")
231
- custom_audio = gr.Audio(label="File ghi âm (.wav)", type="filepath")
232
- custom_text = gr.Textbox(label="Nội dung ghi âm", placeholder="Nhập chính xác lời thoại...")
233
-
234
- btn_generate = gr.Button("🎵 Tổng hợp giọng nói", variant="primary", size="lg")
235
-
236
- # --- RIGHT: OUTPUT ---
237
- with gr.Column(scale=2):
238
- gr.Markdown("### 🎧 Kết quả")
239
- with gr.Group():
240
- audio_output = gr.Audio(label="Audio đầu ra", type="filepath", show_download_button=True, autoplay=True)
241
- status_output = gr.Textbox(label="Trạng thái", show_label=False, elem_classes="status-box", placeholder="Sẵn sàng...")
 
 
 
242
 
243
- # --- EXAMPLES ---
244
  with gr.Row(elem_classes="container"):
245
  with gr.Column():
246
  gr.Markdown("### 📚 Ví dụ mẫu")
247
  gr.Examples(examples=EXAMPLES_LIST, inputs=[text_input, voice_select], label="Thử nghiệm nhanh")
248
 
249
- # --- LOGIC ---
250
  def update_count(text):
251
  l = len(text)
252
  if l > 250:
@@ -269,24 +290,19 @@ with gr.Blocks(theme=theme, css=css, title="VieNeu-TTS Studio") as demo:
269
  voice_select.change(update_ref_preview, voice_select, [ref_audio_preview, ref_text_preview])
270
  demo.load(update_ref_preview, voice_select, [ref_audio_preview, ref_text_preview])
271
 
272
- def toggle_voice_mode(mode):
273
- is_custom = (mode == "🎙️ Giọng tùy chỉnh (Custom)")
274
- return gr.update(visible=not is_custom), gr.update(visible=is_custom)
275
-
276
- voice_mode.change(
277
- fn=toggle_voice_mode,
278
- inputs=[voice_mode],
279
- outputs=[preset_group, custom_group]
280
  )
281
 
282
- btn_generate.click(
283
- fn=synthesize_speech,
284
- inputs=[text_input, voice_select, custom_audio, custom_text, voice_mode],
285
- outputs=[audio_output, status_output]
 
286
  )
287
 
288
  if __name__ == "__main__":
289
- demo.queue().launch(
290
- server_name="0.0.0.0",
291
- server_port=7860
292
- )
 
1
+ import spaces
2
  import os
3
+ os.environ['SPACES_ZERO_GPU'] = '1'
4
 
5
  import gradio as gr
6
  import soundfile as sf
 
64
  return None, ""
65
 
66
  @spaces.GPU(duration=120)
67
+ def synthesize_preset(text, voice_choice):
68
+ """Tổng hợp giọng từ preset"""
69
  try:
70
  if not text or text.strip() == "":
71
  return None, "⚠️ Vui lòng nhập văn bản cần tổng hợp!"
72
 
73
  if len(text) > 250:
74
+ return None, f"❌ Văn bản quá dài ({len(text)}/250 ký tự)!"
75
 
76
+ if voice_choice not in VOICE_SAMPLES:
77
+ return None, "⚠️ Vui lòng chọn một giọng mẫu."
78
 
79
+ ref_audio_path = VOICE_SAMPLES[voice_choice]["audio"]
80
+ ref_text_path = VOICE_SAMPLES[voice_choice]["text"]
81
+
82
+ if not os.path.exists(ref_audio_path):
83
+ return None, f"❌ Không tìm thấy file audio: {ref_audio_path}"
84
+
85
+ with open(ref_text_path, "r", encoding="utf-8") as f:
86
+ ref_text_raw = f.read()
87
+
88
+ print(f"🎤 Preset Voice: {voice_choice}")
 
 
 
 
 
 
 
 
 
89
  print(f"📝 Text: {text[:50]}...")
90
 
91
  start_time = time.time()
 
92
  ref_codes = tts.encode_reference(ref_audio_path)
93
  wav = tts.infer(text, ref_codes, ref_text_raw)
94
+ end_time = time.time()
95
+
96
+ process_time = end_time - start_time
97
+
98
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".wav") as tmp_file:
99
+ sf.write(tmp_file.name, wav, 24000)
100
+ output_path = tmp_file.name
101
+
102
+ return output_path, f"✅ Thành công! (Mất {process_time:.2f} giây)"
103
+
104
+ except Exception as e:
105
+ import traceback
106
+ traceback.print_exc()
107
+ return None, f"❌ Lỗi: {str(e)}"
108
+
109
+ @spaces.GPU(duration=120)
110
+ def synthesize_custom(text, custom_audio, custom_text):
111
+ """Tổng hợp giọng custom"""
112
+ try:
113
+ if not text or text.strip() == "":
114
+ return None, "⚠️ Vui lòng nhập văn bản cần tổng hợp!"
115
+
116
+ if len(text) > 250:
117
+ return None, f"❌ Văn bản quá dài ({len(text)}/250 ký tự)!"
118
+
119
+ if custom_audio is None or not custom_text:
120
+ return None, "⚠️ Vui lòng tải lên Audio và nhập nội dung Audio đó."
121
 
122
+ print("🎨 Custom Voice")
123
+ print(f"📝 Text: {text[:50]}...")
124
+
125
+ start_time = time.time()
126
+ ref_codes = tts.encode_reference(custom_audio)
127
+ wav = tts.infer(text, ref_codes, custom_text)
128
  end_time = time.time()
129
+
130
  process_time = end_time - start_time
131
 
132
  with tempfile.NamedTemporaryFile(delete=False, suffix=".wav") as tmp_file:
133
  sf.write(tmp_file.name, wav, 24000)
134
  output_path = tmp_file.name
135
 
136
+ return output_path, f"✅ Thành công! (Mất {process_time:.2f} giây)"
137
 
138
  except Exception as e:
139
  import traceback
140
  traceback.print_exc()
141
+ return None, f"❌ Lỗi: {str(e)}"
142
 
143
  # --- 4. UI SETUP ---
144
  theme = gr.themes.Soft(
 
192
  ["Về miền Tây không chỉ để ngắm nhìn sông nước hữu tình, mà còn để cảm nhận tấm chân tình của người dân nơi đây. Cùng ngồi xuồng ba lá len lỏi qua rặng dừa nước, nghe câu vọng cổ ngọt ngào thì còn gì b��ng.", "Vĩnh (nam miền Nam)"],
193
  ["Hà Nội những ngày vào thu mang một vẻ đẹp trầm mặc và cổ kính đến lạ thường. Đi dạo quanh Hồ Gươm vào sáng sớm, hít hà mùi hoa sữa nồng nàn và thưởng thức chút cốm làng Vòng là trải nghiệm khó quên.", "Bình (nam miền Bắc)"],
194
  ["Sự bùng nổ của trí tuệ nhân tạo đang định hình lại cách chúng ta làm việc và sinh sống. Từ xe tự lái đến trợ lý ảo thông minh, công nghệ đang dần xóa nhòa ranh giới giữa thực tại và những bộ phim viễn tưởng.", "Tuyên (nam miền Bắc)"],
 
 
 
 
 
195
  ]
196
 
197
  with gr.Blocks(theme=theme, css=css, title="VieNeu-TTS Studio") as demo:
 
211
  </div>
212
  """)
213
 
214
+ # Shared text input
215
+ with gr.Row(elem_classes="container"):
216
+ with gr.Column():
 
217
  gr.Markdown("### 📝 Văn bản đầu vào")
218
  text_input = gr.Textbox(
219
  label="Nhập văn bản",
220
  placeholder="Nhập nội dung tiếng Việt cần chuyển thành giọng nói...",
221
  lines=4,
222
+ value="Sự bùng nổ của trí tuệ nhân tạo đang định hình lại cách chúng ta làm việc và sinh sống.",
223
  show_label=False
224
  )
 
225
  with gr.Row():
226
  char_count = gr.HTML("<div style='text-align: right; color: #64748B; font-size: 0.8rem;'>0 / 250 ký tự</div>")
227
+
228
+ with gr.Tabs() as tabs:
229
+ # TAB 1: Preset Voices
230
+ with gr.Tab("👤 Giọng có sẵn"):
231
+ with gr.Row(elem_classes="container"):
232
+ with gr.Column(scale=3):
233
+ voice_select = gr.Dropdown(
234
+ choices=list(VOICE_SAMPLES.keys()),
235
+ value="Tuyên (nam miền Bắc)",
236
+ label="Chọn giọng",
237
+ interactive=True
238
+ )
239
+ with gr.Accordion("Thông tin giọng mẫu", open=False):
240
+ ref_audio_preview = gr.Audio(label="Audio mẫu", interactive=False)
241
+ ref_text_preview = gr.Markdown("...")
242
+
243
+ btn_preset = gr.Button("🎵 Tổng hợp giọng nói", variant="primary", size="lg")
244
+
245
+ with gr.Column(scale=2):
246
+ gr.Markdown("### 🎧 Kết quả")
247
+ audio_preset = gr.Audio(label="Audio đầu ra", type="filepath")
248
+ status_preset = gr.Textbox(label="Trạng thái", show_label=False, elem_classes="status-box", placeholder="Sẵn sàng...")
249
+
250
+ # TAB 2: Custom Voice
251
+ with gr.Tab("🎙️ Giọng tùy chỉnh"):
252
+ with gr.Row(elem_classes="container"):
253
+ with gr.Column(scale=3):
254
+ gr.Markdown("Tải lên giọng của bạn (Zero-shot Cloning)")
255
+ custom_audio = gr.File(label="File ghi âm (.wav)", file_types=[".wav"])
256
+ custom_text = gr.Textbox(label="Nội dung ghi âm", placeholder="Nhập chính xác lời thoại...", lines=3)
257
+ btn_custom = gr.Button("🎵 Tổng hợp giọng nói", variant="primary", size="lg")
258
+
259
+ with gr.Column(scale=2):
260
+ gr.Markdown("### 🎧 Kết quả")
261
+ audio_custom = gr.Audio(label="Audio đầu ra", type="filepath")
262
+ status_custom = gr.Textbox(label="Trạng thái", show_label=False, elem_classes="status-box", placeholder="Sẵn sàng...")
263
 
264
+ # Examples
265
  with gr.Row(elem_classes="container"):
266
  with gr.Column():
267
  gr.Markdown("### 📚 Ví dụ mẫu")
268
  gr.Examples(examples=EXAMPLES_LIST, inputs=[text_input, voice_select], label="Thử nghiệm nhanh")
269
 
270
+ # --- EVENT HANDLERS ---
271
  def update_count(text):
272
  l = len(text)
273
  if l > 250:
 
290
  voice_select.change(update_ref_preview, voice_select, [ref_audio_preview, ref_text_preview])
291
  demo.load(update_ref_preview, voice_select, [ref_audio_preview, ref_text_preview])
292
 
293
+ btn_preset.click(
294
+ fn=synthesize_preset,
295
+ inputs=[text_input, voice_select],
296
+ outputs=[audio_preset, status_preset],
297
+ api_name="preset"
 
 
 
298
  )
299
 
300
+ btn_custom.click(
301
+ fn=synthesize_custom,
302
+ inputs=[text_input, custom_audio, custom_text],
303
+ outputs=[audio_custom, status_custom],
304
+ api_name="custom"
305
  )
306
 
307
  if __name__ == "__main__":
308
+ demo.queue().launch()