tonyshark commited on
Commit
64749f5
·
verified ·
1 Parent(s): 7f65a42

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +311 -105
app.py CHANGED
@@ -1,127 +1,333 @@
1
- # app.py
2
- import gradio as gr
3
- import torch
4
  import os
 
 
5
  import numpy as np
6
  import soundfile as sf
7
- import time
 
 
 
8
  from model import StyleTTModel
9
 
10
  SPEAKER_WAV_PATH = "speakers/example_female.wav"
11
  OUTPUT_FILENAME = "output.wav"
12
  SAMPLE_RATE = 24000
13
 
14
- # Kiểm tra xem file giọng nói tham chiếu có tồn tại không
15
- if not os.path.exists(SPEAKER_WAV_PATH):
16
- raise FileNotFoundError(f"Không tìm thấy file giọng nói tham chiếu tại: {SPEAKER_WAV_PATH}. "
17
- "Vui lòng tạo thư mục và đặt file .wav của bạn vào đó.")
18
-
19
- print("Bắt đầu khởi tạo StyleTTS2 Model...")
20
- model = StyleTTModel(speaker_wav=SPEAKER_WAV_PATH)
21
- print("Đang tải model StyleTTS2. Quá trình này có thể mất vài phút...")
22
- start_time = time.time()
23
- model.load()
24
- end_time = time.time()
25
- print(f"Model đã được tải thành công sau {end_time - start_time:.2f} giây.")
26
-
27
-
28
- def process_expressive_tags(text: str) -> str:
29
- """
30
- Chuyển đổi các tag biểu cảm như <laugh> thành các cụm từ mô tả
31
- StyleTTS2 có thể hiểu để thay đổi giọng điệu.
32
- """
33
- tag_map = {
34
- "<laugh>": "[laughing]",
35
- "<whisper>": "[whispering]",
36
- "<sigh>": "[sighs]",
37
- "<cry>": "[crying]",
38
- "<naughty>": "[mischievous tone]",
39
- "<giggle>": "[giggling]",
40
- "<tease>": "[teasingly]",
41
- "<smirk>": "[sly tone]",
42
- "<surprise>": "[surprised tone]",
43
- "<romantic>": "[romantic tone]",
44
- "<shy>": "[shyly]",
45
- "<excited>": "[excitedly]",
46
- "<shock>": "[shocked tone]",
47
- "<curious>": "[curious tone]",
48
- "<discover>": "[discovering tone]",
49
- "<blush>": "[blushing voice]"
50
- }
51
-
52
- processed_text = text
53
- for tag, style_prompt in tag_map.items():
54
- processed_text = processed_text.replace(tag, style_prompt)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
 
56
- print(f"Văn bản sau khi xử lý tag: '{processed_text}'")
57
- return processed_text
 
 
 
 
 
 
 
 
 
 
 
 
 
58
 
59
- # --- Phần 3: Hàm chính để tổng hợp giọng nói ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
 
61
- def generate_speech(text: str, speed: float):
62
- """
63
- Hàm chính được gọi bởi Gradio để tạo ra âm thanh từ văn bản.
64
- """
65
- if not text:
66
- return None
 
 
 
 
 
 
 
 
67
 
68
- print(f"Nhận được văn bản: '{text}' với tốc độ {speed}")
69
-
70
- # Bước 1: Xử lý các tag biểu cảm
71
- styled_text = process_expressive_tags(text)
72
-
73
- # Bước 2: Sử dụng plugin để tổng hợp âm thanh
74
- # Hàm synthesize trả về một mảng numpy
75
- audio_array = model.synthesize(styled_text, speed=speed)
76
-
77
- sf.write(OUTPUT_FILENAME, audio_array, SAMPLE_RATE, 'FLOAT')
78
-
79
- print(f"Đã tạo file âm thanh thành công tại: {OUTPUT_FILENAME}")
80
-
81
- return OUTPUT_FILENAME
82
 
 
 
83
 
84
- with gr.Blocks() as demo:
85
- gr.Markdown(
86
- """
87
- # 🎙️ Demo StyleTTS2 Lite với Tag biểu cảm
88
- Nhập văn bản vào ô bên dưới. Bạn có thể sử dụng các tag như `<laugh>`, `<whisper>`, `<sigh>`
89
- để thêm cảm xúc cho giọng nói. Điều chỉnh thanh trượt để thay đổi tốc độ nói.
90
- """
91
- )
92
-
93
- with gr.Row():
94
- text_input = gr.Textbox(
95
- label="Nhập văn bản ở đây",
96
- placeholder="Ví dụ: Chào bạn, tôi là một trợ lý ảo. <laugh> Thật vui được gặp bạn!",
97
- lines=4
98
- )
99
-
100
- speed_slider = gr.Slider(
101
- minimum=0.5,
102
- maximum=2.0,
103
- value=1.0,
104
- step=0.1,
105
- label="Tốc độ nói (Speed)"
106
- )
107
-
108
- generate_button = gr.Button("Tạo giọng nói 🎙️", variant="primary")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
109
 
110
- audio_output = gr.Audio(label="Kết quả", type="filepath")
 
 
 
 
111
 
112
- # Kết nối các thành phần
113
- generate_button.click(
114
- fn=generate_speech,
115
- inputs=[text_input, speed_slider],
116
- outputs=audio_output
117
- )
118
 
119
- gr.Markdown("### Các tag biểu cảm được hỗ trợ:")
120
- gr.Markdown("- `<laugh>`: Tiếng cười\n- `<whisper>`: Thì thầm\n- `<sigh>`: Thở dài\n- `<cry>`: Khóc\n- `<giggle>`: Cười khúc khích\n- `<tease>`: Trêu chọc\n- `<surprised>`: Ngạc nhiên\n- Và nhiều tag khác trong mã nguồn...")
 
 
 
121
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
122
 
123
- # --- Phần 5: Chạy ứng dụng ---
 
 
124
 
 
125
  if __name__ == "__main__":
126
- # Chia sẻ (share=True) sẽ tạo một liên kết công khai tạm thời
127
- demo.launch(share=False)
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import os
2
+ import re
3
+ import time
4
  import numpy as np
5
  import soundfile as sf
6
+ import matplotlib.pyplot as plt
7
+ import librosa
8
+ import gradio as gr
9
+ from scipy.signal import fftconvolve
10
  from model import StyleTTModel
11
 
12
  SPEAKER_WAV_PATH = "speakers/example_female.wav"
13
  OUTPUT_FILENAME = "output.wav"
14
  SAMPLE_RATE = 24000
15
 
16
+ # Global model variable
17
+ model = None
18
+
19
+ def initialize_model():
20
+ """Initialize the StyleTTS model with error handling"""
21
+ global model
22
+ try:
23
+ # Check if speaker reference file exists
24
+ if not os.path.exists(SPEAKER_WAV_PATH):
25
+ raise FileNotFoundError(f"Không tìm thấy file giọng nói tham chiếu tại: {SPEAKER_WAV_PATH}. "
26
+ "Vui lòng tạo thư mục và đặt file .wav của bạn vào đó.")
27
+
28
+ print("Bắt đầu khởi tạo StyleTTS2 Model...")
29
+ model = StyleTTModel(speaker_wav=SPEAKER_WAV_PATH)
30
+ print("Đang tải model StyleTTS2. Quá trình này có thể mất vài phút...")
31
+ start_time = time.time()
32
+ model.load()
33
+ end_time = time.time()
34
+ print(f"Model đã được tải thành công sau {end_time - start_time:.2f} giây.")
35
+ return True
36
+ except Exception as e:
37
+ print(f"Lỗi khi khởi tạo model: {e}")
38
+ model = None
39
+ return False
40
+
41
+ # Initialize model on startup
42
+ model_loaded = initialize_model()
43
+
44
+ # ---------------------------
45
+ # Load HF TTS model (hexgrad/styletts2)
46
+ # ---------------------------
47
+ SR_OUT = 24000
48
+ # tts_pipe = pipeline("text-to-speech", model="hexgrad/styletts2")
49
+
50
+ # ---------------------------
51
+ # Audio helpers
52
+ # ---------------------------
53
+ def load_wav(path, sr_target=SR_OUT):
54
+ wav, sr = sf.read(path)
55
+ if wav.ndim > 1:
56
+ wav = wav.mean(axis=1)
57
+ if sr != sr_target:
58
+ wav = librosa.resample(wav.astype(np.float32), orig_sr=sr, target_sr=sr_target)
59
+ sr = sr_target
60
+ return wav.astype(np.float32), sr
61
+
62
+ def apply_reverb(wav, ir_path):
63
+ """Apply reverb effect using impulse response"""
64
+ try:
65
+ if not os.path.exists(ir_path):
66
+ print(f"Cảnh báo: Không tìm thấy file impulse response: {ir_path}")
67
+ return wav
68
+ ir, _ = load_wav(ir_path, sr_target=SR_OUT)
69
+ return fftconvolve(wav, ir, mode="full")
70
+ except Exception as e:
71
+ print(f"Lỗi khi áp dụng reverb: {e}")
72
+ return wav
73
+
74
+ def add_noise(wav, noise_path, snr_db=10):
75
+ """Add background noise to audio"""
76
+ try:
77
+ if not os.path.exists(noise_path):
78
+ print(f"Cảnh báo: Không tìm thấy file noise: {noise_path}")
79
+ return wav
80
+ noise, _ = load_wav(noise_path, sr_target=SR_OUT)
81
+ if len(noise) < len(wav):
82
+ noise = np.tile(noise, int(len(wav)/len(noise)) + 1)
83
+ noise = noise[:len(wav)]
84
+ sig_power = np.mean(wav**2)
85
+ noise_power = np.mean(noise**2)
86
+ if noise_power == 0:
87
+ return wav
88
+ scale = np.sqrt(sig_power / (10**(snr_db/10) * noise_power))
89
+ return wav + noise * scale
90
+ except Exception as e:
91
+ print(f"Lỗi khi thêm noise: {e}")
92
+ return wav
93
+
94
+ def bandlimit_phone(wav, sr=SR_OUT):
95
+ """Apply phone-like band limiting"""
96
+ try:
97
+ return librosa.effects.preemphasis(wav)
98
+ except Exception as e:
99
+ print(f"Lỗi khi áp dụng band limiting: {e}")
100
+ return wav
101
+
102
+ def plot_waveforms(clean, processed, sr=SR_OUT):
103
+ """Create waveform comparison plot"""
104
+ try:
105
+ fig, axes = plt.subplots(2, 1, figsize=(10, 4), sharex=True)
106
+ t_clean = np.arange(len(clean)) / sr
107
+ t_proc = np.arange(len(processed)) / sr
108
+
109
+ axes[0].plot(t_clean, clean, color="blue", linewidth=0.8)
110
+ axes[0].set_title("🎤 Waveform gốc (StyleTTS2)")
111
+ axes[0].set_ylabel("Amplitude")
112
+ axes[0].grid(True, alpha=0.3)
113
 
114
+ axes[1].plot(t_proc, processed, color="red", linewidth=0.8)
115
+ axes[1].set_title("🎵 Waveform có hiệu ứng môi trường")
116
+ axes[1].set_xlabel("Thời gian (s)")
117
+ axes[1].set_ylabel("Amplitude")
118
+ axes[1].grid(True, alpha=0.3)
119
+
120
+ fig.tight_layout()
121
+ return fig
122
+ except Exception as e:
123
+ print(f"Lỗi khi tạo biểu đồ: {e}")
124
+ # Return a simple error plot
125
+ fig, ax = plt.subplots(1, 1, figsize=(10, 2))
126
+ ax.text(0.5, 0.5, "Không thể tạo biểu đồ", ha='center', va='center', transform=ax.transAxes)
127
+ ax.set_title("Lỗi tạo biểu đồ")
128
+ return fig
129
 
130
+ # ---------------------------
131
+ # Tag list
132
+ # ---------------------------
133
+ TAG_LIST = {
134
+ "laugh": "😆 Cười thoải mái",
135
+ "whisper": "🤫 Thì thầm",
136
+ "naughty": "😏 Tinh nghịch",
137
+ "giggle": "😂 Cười rúc rích",
138
+ "tease": "😉 Trêu chọc",
139
+ "smirk": "😼 Đắc ý",
140
+ "surprise": "😲 Ngạc nhiên",
141
+ "shock": "😱 Hoảng hốt",
142
+ "romantic": "❤️ Lãng mạn",
143
+ "shy": "🫣 Bẽn lẽn",
144
+ "excited": "🤩 Phấn khích",
145
+ "curious": "🧐 Tò mò",
146
+ "discover": "✨ Phát hiện",
147
+ "blush": "🌸 Ngượng ngùng",
148
+ "angry": "😡 Giận dữ",
149
+ "sad": "😢 Buồn",
150
+ "happy": "😊 Vui vẻ",
151
+ "fear": "😨 Sợ hãi",
152
+ "confident": "😎 Tự tin",
153
+ "serious": "😐 Nghiêm túc",
154
+ "tired": "🥱 Mệt mỏi",
155
+ "cry": "😭 Khóc",
156
+ "love": "😍 Yêu thương",
157
+ "disgust": "🤢 Ghê tởm",
158
+ }
159
+ TAG_PATTERN = r"(<\/?(?:" + "|".join(TAG_LIST.keys()) + ")>)"
160
 
161
+ # ---------------------------
162
+ # Core synthesis
163
+ # ---------------------------
164
+ def synthesize(text, env, snr_db=10, speed=1.0):
165
+ """Synthesize text to speech with environment effects"""
166
+ try:
167
+ # Check if model is loaded
168
+ if model is None:
169
+ print("Lỗi: Model chưa được tải. Vui lòng khởi động lại ứng dụng.")
170
+ return None, None, None
171
+
172
+ # Parse text and extract segments
173
+ tokens = re.split(TAG_PATTERN, text)
174
+ clean_segments = []
175
 
176
+ for tok in tokens:
177
+ if not tok or tok.isspace():
178
+ continue
179
+ if tok.startswith("<") and tok.endswith(">"):
180
+ # Skip tags for now - they're just for text segmentation
181
+ continue
182
+ else:
183
+ # Synthesize each text segment
184
+ try:
185
+ audio_array = model.synthesize(tok, speed=speed)
186
+ clean_segments.append(audio_array)
187
+ except Exception as e:
188
+ print(f"Lỗi khi tổng hợp đoạn '{tok}': {e}")
189
+ continue
190
 
191
+ if not clean_segments:
192
+ return None, None, None
193
 
194
+ # Concatenate all audio segments
195
+ clean_audio = np.concatenate(clean_segments, axis=0)
196
+ processed = clean_audio.copy()
197
+
198
+ # Apply environment effects
199
+ try:
200
+ if env == "Church":
201
+ processed = apply_reverb(processed, "ir_church.wav")
202
+ elif env == "Hall":
203
+ processed = apply_reverb(processed, "ir_hall.wav")
204
+ elif env == "Cafe":
205
+ processed = add_noise(processed, "noise_cafe.wav", snr_db=snr_db)
206
+ elif env == "Street":
207
+ processed = add_noise(processed, "noise_street.wav", snr_db=snr_db)
208
+ elif env == "Office":
209
+ processed = add_noise(processed, "noise_office.wav", snr_db=snr_db)
210
+ elif env == "Supermarket":
211
+ processed = add_noise(processed, "noise_supermarket.wav", snr_db=snr_db)
212
+ elif env == "Phone":
213
+ processed = bandlimit_phone(processed, sr=SR_OUT)
214
+ except Exception as e:
215
+ print(f"Cảnh báo: Không thể áp dụng hiệu ứng môi trường '{env}': {e}")
216
+ # Continue with clean audio if environment effects fail
217
+
218
+ # Create waveform comparison plot
219
+ fig = plot_waveforms(clean_audio, processed, sr=SR_OUT)
220
+
221
+ return (SR_OUT, processed), fig, (SR_OUT, clean_audio)
222
+
223
+ except Exception as e:
224
+ print(f"Lỗi trong quá trình tổng hợp: {e}")
225
+ return None, None, None
226
+
227
+ # ---------------------------
228
+ # Examples
229
+ # ---------------------------
230
+ EXAMPLES = [
231
+ "Xin chào <whisper> tôi nói nhỏ </whisper> rồi <laugh> bật cười </laugh>.",
232
+ "Tôi cảm thấy <happy> vui </happy> nhưng cũng <sad> buồn </sad>.",
233
+ "Khi <surprise> bất ngờ </surprise> tôi <shock> hoảng hốt </shock>.",
234
+ ]
235
+
236
+ # ---------------------------
237
+ # Gradio UI
238
+ # ---------------------------
239
+ with gr.Blocks(title="StyleTTS2 Text-to-Speech", theme=gr.themes.Soft()) as demo:
240
+ gr.Markdown("# 🎙️ StyleTTS2 Text-to-Speech với Hiệu ứng Môi trường")
241
 
242
+ # Model status indicator
243
+ if model_loaded:
244
+ gr.Markdown("✅ **Model đã sẵn sàng** - Bạn có thể bắt đầu tạo giọng nói!")
245
+ else:
246
+ gr.Markdown("❌ **Lỗi tải model** - Vui lòng kiểm tra file giọng nói tham chiếu và khởi động lại.")
247
 
248
+ gr.Markdown("Sử dụng StyleTTS2 với khả năng thêm hiệu ứng môi trường và điều chỉnh tốc độ nói.")
 
 
 
 
 
249
 
250
+ with gr.Accordion("📑 Danh sách Tags + Emoji", open=False):
251
+ md = "| Tag | Ý nghĩa |\n|-----|----------|\n"
252
+ for k, v in TAG_LIST.items():
253
+ md += f"| `<{k}>...</{k}>` | {v} |\n"
254
+ gr.Markdown(md)
255
 
256
+ with gr.Row():
257
+ with gr.Column(scale=1):
258
+ gr.Markdown("### ⚙️ Cài đặt")
259
+
260
+ text_in = gr.Textbox(
261
+ value=EXAMPLES[0],
262
+ label="📝 Văn bản cần chuyển đổi",
263
+ lines=4,
264
+ placeholder="Nhập văn bản của bạn ở đây. Sử dụng tags để tạo cảm xúc..."
265
+ )
266
+
267
+ with gr.Row():
268
+ env_in = gr.Dropdown(
269
+ choices=["Neutral", "Church", "Hall", "Cafe", "Street", "Phone", "Office", "Supermarket"],
270
+ value="Neutral",
271
+ label="🌍 Môi trường âm thanh",
272
+ info="Chọn môi trường để áp dụng hiệu ứng"
273
+ )
274
+ speed_slider = gr.Slider(
275
+ minimum=0.5,
276
+ maximum=2.0,
277
+ value=1.0,
278
+ step=0.1,
279
+ label="⚡ Tốc độ nói",
280
+ info="1.0 = bình thường, < 1.0 = chậm, > 1.0 = nhanh"
281
+ )
282
+
283
+ snr_slider = gr.Slider(
284
+ 0, 30,
285
+ value=10,
286
+ step=1,
287
+ label="🔊 Mức độ nhiễu (SNR dB)",
288
+ info="Chỉ áp dụng cho môi trường có tiếng ồn. Cao hơn = ít nhiễu hơn"
289
+ )
290
+
291
+ btn = gr.Button("🎵 Tạo giọng nói", variant="primary", size="lg")
292
+
293
+ gr.Examples(
294
+ examples=[[ex] for ex in EXAMPLES],
295
+ inputs=[text_in],
296
+ label="💡 Ví dụ nhanh"
297
+ )
298
+
299
+ with gr.Column(scale=1):
300
+ gr.Markdown("### 🎧 Kết quả")
301
+
302
+ audio_out = gr.Audio(
303
+ label="🎵 Âm thanh có hiệu ứng",
304
+ type="numpy",
305
+ info="Phiên bản có áp dụng hiệu ứng môi trường"
306
+ )
307
+ clean_out = gr.Audio(
308
+ label="🎤 Âm thanh gốc",
309
+ type="numpy",
310
+ info="Phiên bản gốc không có hiệu ứng"
311
+ )
312
+ wave_plot = gr.Plot(
313
+ label="📊 So sánh dạng sóng",
314
+ info="Biểu đồ so sánh âm thanh gốc và có hiệu ứng"
315
+ )
316
 
317
+ btn.click(fn=synthesize,
318
+ inputs=[text_in, env_in, snr_slider, speed_slider],
319
+ outputs=[audio_out, wave_plot, clean_out])
320
 
321
+ # Launch the application
322
  if __name__ == "__main__":
323
+ try:
324
+ print("🚀 Đang khởi động ứng dụng StyleTTS2...")
325
+ demo.launch(
326
+ server_name="0.0.0.0",
327
+ server_port=7860,
328
+ share=False,
329
+ show_error=True
330
+ )
331
+ except Exception as e:
332
+ print(f"❌ Lỗi khi khởi động ứng dụng: {e}")
333
+ print("Vui lòng kiểm tra lại cấu hình và thử lại.")