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

Upload 25 files

Browse files
Files changed (2) hide show
  1. app.py +121 -153
  2. requirements.txt +12 -4
app.py CHANGED
@@ -1,159 +1,127 @@
1
- import re
 
 
 
2
  import numpy as np
3
  import soundfile as sf
4
- import gradio as gr
5
- import matplotlib.pyplot as plt
6
- from scipy.signal import fftconvolve
7
- import librosa
8
- from transformers import pipeline
9
-
10
- # ---------------------------
11
- # Load HF TTS model (ak36/styletts2)
12
- # ---------------------------
13
- SR_OUT = 24000
14
- tts_pipe = pipeline("text-to-speech", model="ak36/styletts2")
15
-
16
- # ---------------------------
17
- # Audio helpers
18
- # ---------------------------
19
- def load_wav(path, sr_target=SR_OUT):
20
- wav, sr = sf.read(path)
21
- if wav.ndim > 1:
22
- wav = wav.mean(axis=1)
23
- if sr != sr_target:
24
- wav = librosa.resample(wav.astype(np.float32), orig_sr=sr, target_sr=sr_target)
25
- sr = sr_target
26
- return wav.astype(np.float32), sr
27
-
28
- def apply_reverb(wav, ir_path):
29
- ir, _ = load_wav(ir_path, sr_target=SR_OUT)
30
- return fftconvolve(wav, ir, mode="full")
31
-
32
- def add_noise(wav, noise_path, snr_db=10):
33
- noise, _ = load_wav(noise_path, sr_target=SR_OUT)
34
- if len(noise) < len(wav):
35
- noise = np.tile(noise, int(len(wav)/len(noise)) + 1)
36
- noise = noise[:len(wav)]
37
- sig_power = np.mean(wav**2)
38
- noise_power = np.mean(noise**2)
39
- scale = np.sqrt(sig_power / (10**(snr_db/10) * noise_power))
40
- return wav + noise * scale
41
-
42
- def bandlimit_phone(wav, sr=SR_OUT):
43
- return librosa.effects.preemphasis(wav)
44
-
45
- def plot_waveforms(clean, processed, sr=SR_OUT):
46
- fig, axes = plt.subplots(2, 1, figsize=(10, 4), sharex=True)
47
- t_clean = np.arange(len(clean)) / sr
48
- t_proc = np.arange(len(processed)) / sr
49
- axes[0].plot(t_clean, clean, color="blue")
50
- axes[0].set_title("Waveform sạch (ak36/styletts2)")
51
- axes[1].plot(t_proc, processed, color="red")
52
- axes[1].set_title("Waveform sau khi áp môi trường/noise")
53
- axes[1].set_xlabel("Thời gian (s)")
54
- fig.tight_layout()
55
- return fig
56
-
57
- # ---------------------------
58
- # Tag list
59
- # ---------------------------
60
- TAG_LIST = {
61
- "laugh": "😆 Cười thoải mái",
62
- "whisper": "🤫 Thì thầm",
63
- "giggle": "😂 Cười rúc rích",
64
- "surprise": "😲 Ngạc nhiên",
65
- "sad": "😢 Buồn",
66
- "happy": "😊 Vui vẻ",
67
- "angry": "😡 Giận dữ",
68
- }
69
- TAG_PATTERN = r"(<\/?(?:" + "|".join(TAG_LIST.keys()) + ")>)"
70
-
71
- # ---------------------------
72
- # Core synthesis
73
- # ---------------------------
74
- def synthesize(text, env, snr_db=10):
75
- tokens = re.split(TAG_PATTERN, text)
76
- clean_segments = []
 
 
77
 
78
- for tok in tokens:
79
- if not tok or tok.isspace():
80
- continue
81
- if tok.startswith("<") and tok.endswith(">"):
82
- # Model ak36/styletts2 chưa hỗ trợ style embedding riêng,
83
- # nên tags chỉ chia text thành đoạn.
84
- continue
85
- else:
86
- result = tts_pipe(text=tok)
87
- wav = result["audio"]
88
- sr = result["sampling_rate"]
89
- if sr != SR_OUT:
90
- wav = librosa.resample(wav, orig_sr=sr, target_sr=SR_OUT)
91
- clean_segments.append(wav.astype(np.float32))
92
 
93
- if not clean_segments:
94
- return None, None, None
95
-
96
- clean_audio = np.concatenate(clean_segments, axis=0)
97
- processed = clean_audio.copy()
98
-
99
- # Apply environment
100
- if env == "Church":
101
- processed = apply_reverb(processed, "ir_church.wav")
102
- elif env == "Hall":
103
- processed = apply_reverb(processed, "ir_hall.wav")
104
- elif env == "Cafe":
105
- processed = add_noise(processed, "noise_cafe.wav", snr_db=snr_db)
106
- elif env == "Street":
107
- processed = add_noise(processed, "noise_street.wav", snr_db=snr_db)
108
- elif env == "Office":
109
- processed = add_noise(processed, "noise_office.wav", snr_db=snr_db)
110
- elif env == "Supermarket":
111
- processed = add_noise(processed, "noise_supermarket.wav", snr_db=snr_db)
112
- elif env == "Phone":
113
- processed = bandlimit_phone(processed, sr=SR_OUT)
114
-
115
- fig = plot_waveforms(clean_audio, processed, sr=SR_OUT)
116
- return (SR_OUT, processed), fig, (SR_OUT, clean_audio)
117
-
118
- # ---------------------------
119
- # Example texts
120
- # ---------------------------
121
- EXAMPLES = [
122
- "Xin chào <whisper> tôi nói nhỏ </whisper> rồi <laugh> bật cười </laugh>.",
123
- "Tôi cảm thấy <happy> vui </happy> nhưng cũng <sad> buồn </sad>.",
124
- "Khi <surprise> bất ngờ </surprise> tôi <angry> giận dữ </angry>.",
125
- ]
126
-
127
- # ---------------------------
128
- # Gradio UI
129
- # ---------------------------
130
  with gr.Blocks() as demo:
131
- gr.Markdown("# 🎙️ ak36/styletts2 + Tags + Environment + Waveform Preview")
132
- gr.Markdown("Dùng model `ak36/styletts2` (giọng LibriTTS mặc định). Tags chia text thành đoạn.")
133
-
134
- with gr.Accordion("📑 Danh sách Tags + Emoji", open=False):
135
- md = "| Tag | Ý nghĩa |\n|-----|----------|\n"
136
- for k, v in TAG_LIST.items():
137
- md += f"| `<{k}>...</{k}>` | {v} |\n"
138
- gr.Markdown(md)
139
-
140
  with gr.Row():
141
- with gr.Column():
142
- text_in = gr.Textbox(value=EXAMPLES[0], label="Text với tags", lines=4)
143
- env_in = gr.Dropdown(
144
- choices=["Neutral", "Church", "Hall", "Cafe", "Street", "Phone", "Office", "Supermarket"],
145
- value="Neutral", label="Environment"
146
- )
147
- snr_slider = gr.Slider(0, 30, value=10, step=1, label="Noise SNR (dB)")
148
- btn = gr.Button("Generate")
149
- gr.Examples(examples=[[ex] for ex in EXAMPLES], inputs=[text_in], label="Ví dụ nhanh")
150
- with gr.Column():
151
- audio_out = gr.Audio(label="Processed", type="numpy")
152
- clean_out = gr.Audio(label="Clean (TTS only)", type="numpy")
153
- wave_plot = gr.Plot(label="So sánh Waveform")
154
-
155
- btn.click(fn=synthesize,
156
- inputs=[text_in, env_in, snr_slider],
157
- outputs=[audio_out, wave_plot, clean_out])
158
-
159
- demo.launch()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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ử 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 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)
requirements.txt CHANGED
@@ -1,8 +1,16 @@
1
- transformers
2
  torch
 
 
 
 
3
  soundfile
4
  numpy
5
- scipy
6
- gradio
 
 
 
 
 
7
  librosa
8
- matplotlib
 
 
1
  torch
2
+ torchaudio
3
+ transformers
4
+ huggingface_hub
5
+ gradio
6
  soundfile
7
  numpy
8
+ pyyaml
9
+ pydantic
10
+ httpx
11
+ phonemizer
12
+ nltk
13
+ munch
14
+ noisereduce
15
  librosa
16
+ pyttsx3