ktvoice commited on
Commit
6b6469c
·
verified ·
1 Parent(s): e16bdd9

Upload 6 files

Browse files
Files changed (2) hide show
  1. app.py +51 -115
  2. config.yaml +28 -26
app.py CHANGED
@@ -12,17 +12,18 @@ import time
12
  # IMPORT TỪ FILE ENGINE CỦA BẠN
13
  from tts_engine import VoiceEngine
14
 
15
- # CẤU HÌNH REPO NHÂN CỦA KTVOICE
16
- MY_BACKBONE_REPO = "ktvoice/Backbone"
17
- MY_CODEC_REPO = "ktvoice/Codec"
18
-
19
  device = "cuda" if torch.cuda.is_available() else "cpu"
20
 
 
 
 
 
21
  try:
22
  tts = VoiceEngine(
23
- backbone_repo=MY_BACKBONE_REPO,
24
  backbone_device=device,
25
- codec_repo=MY_CODEC_REPO,
26
  codec_device=device
27
  )
28
  except Exception as e:
@@ -35,7 +36,7 @@ except Exception as e:
35
  return np.random.uniform(-0.1, 0.1, 24000*2)
36
  tts = MockTTS()
37
 
38
- # --- DATA GIỌNG MẪU ---
39
  VOICE_SAMPLES = {
40
  "Tuyên (nam miền Bắc)": {"audio": "./sample/Tuyên (nam miền Bắc).wav", "text": "./sample/Tuyên (nam miền Bắc).txt"},
41
  "Thiện Tâm": {"audio": "./sample/thientam.mp3", "text": "./sample/thientam.txt"},
@@ -62,141 +63,76 @@ def load_reference_info(voice_choice):
62
  @spaces.GPU(duration=120)
63
  def synthesize_speech(text, voice_choice, custom_audio, custom_text, mode_tab, pause_level, speed_value):
64
  try:
65
- if not text or text.strip() == "":
66
- return None, "⚠️ Vui lòng nhập nội dung!"
67
 
68
- # Xử lý độ ngắt nghỉ (Pause level)
69
- processed_text = text
70
- if pause_level == "Trung bình":
71
- processed_text = processed_text.replace(",", ", , ").replace(".", ". . ")
72
- elif pause_level == "Dài":
73
- processed_text = processed_text.replace(",", ", , , ").replace(".", ". . . . ")
74
 
75
- if len(processed_text) > 400:
76
- processed_text = processed_text[:400]
77
-
78
- # Lấy dữ liệu Reference
79
  if mode_tab == "custom_mode":
80
- if custom_audio is None or not custom_text:
81
- return None, "⚠️ Thiếu Audio mẫu hoặc Text mẫu."
82
- ref_audio_path = custom_audio
83
- ref_text_raw = custom_text
84
  else:
85
- ref_audio_path = VOICE_SAMPLES[voice_choice]["audio"]
86
  with open(VOICE_SAMPLES[voice_choice]["text"], "r", encoding="utf-8") as f:
87
  ref_text_raw = f.read()
88
 
89
- # Thực hiện Inference
90
  start_time = time.time()
91
- ref_codes = tts.encode_reference(ref_audio_path)
92
- wav = tts.infer(processed_text, ref_codes, ref_text_raw)
93
 
94
- # Điều chỉnh Tốc độ
95
  if speed_value != 1.0:
96
  wav = librosa.effects.time_stretch(wav, rate=float(speed_value))
97
 
98
- process_time = time.time() - start_time
99
-
100
- with tempfile.NamedTemporaryFile(delete=False, suffix=".wav") as tmp_file:
101
- sf.write(tmp_file.name, wav, 24000)
102
- output_path = tmp_file.name
103
 
104
- return output_path, f"⚡ Hoàn tất: {process_time:.2f}s | Tốc độ: {speed_value}x"
105
- except Exception as e:
106
- return None, f"❌ Lỗi: {str(e)}"
107
 
108
- # --- UI SETUP (Premium Dark Mode) ---
109
- theme = gr.themes.Default(
110
- primary_hue="indigo",
111
- secondary_hue="blue",
112
- neutral_hue="slate",
113
- font=[gr.themes.GoogleFont('Inter'), 'sans-serif'],
114
- ).set(
115
- body_background_fill="#020617",
116
- block_background_fill="#0f172a",
117
- block_border_width="1px",
118
- input_background_fill="#1e293b",
119
- input_border_color="#334155",
120
- button_primary_background_fill="linear-gradient(135deg, #4f46e5 0%, #7c3aed 100%)",
121
  )
122
 
123
- css = """
124
- .main-wrap { max-width: 1240px !important; margin: auto !important; padding: 30px 20px !important; }
125
- .st-card {
126
- border-radius: 16px !important;
127
- border: 1px solid rgba(255,255,255,0.1) !important;
128
- box-shadow: 0 4px 25px rgba(0,0,0,0.6) !important;
129
- padding: 15px;
130
- }
131
- .result-card {
132
- background: linear-gradient(180deg, rgba(15, 23, 42, 0.8) 0%, rgba(30, 41, 59, 0.8) 100%) !important;
133
- border: 1px solid rgba(99, 102, 241, 0.2) !important;
134
- margin-top: 15px;
135
- }
136
- audio { filter: invert(90%) hue-rotate(180deg) brightness(1.5); width: 100%; border-radius: 8px; }
137
- .footer { text-align: center; margin-top: 50px; color: #475569; font-size: 0.85rem; letter-spacing: 1px; }
138
- """
139
 
140
  with gr.Blocks(title="AI Voice Studio") as demo:
141
  with gr.Column(elem_classes="main-wrap"):
142
- # Đã xóa phần Header "VieNeu Studio" theo yêu cầu
143
-
144
  with gr.Row(equal_height=True):
145
- # CỘT TRÁI: NHẬP VĂN BẢN
146
  with gr.Column(scale=1):
147
  with gr.Group(elem_classes="st-card"):
148
- text_input = gr.Textbox(
149
- label="VĂN BẢN ĐẦU VÀO",
150
- placeholder="Nhập nội dung cần chuyển đổi giọng nói...",
151
- lines=24, # Tăng số dòng để cân bằng với cột phải
152
- show_label=True,
153
- )
154
- char_count = gr.HTML("<div style='text-align: right; color: #6366f1; font-weight: bold; padding: 5px;'>0 / 250</div>")
155
-
156
- # CỘT PHẢI: CẤU HÌNH
157
  with gr.Column(scale=1):
158
  with gr.Tabs() as tabs:
159
- with gr.TabItem("👤 Giọng Mẫu", id="preset_mode"):
160
- voice_select = gr.Dropdown(
161
- choices=list(VOICE_SAMPLES.keys()),
162
- value="Tuyên (nam miền Bắc)",
163
- label="Lựa chọn nghệ sĩ",
164
- )
165
- with gr.Accordion("Nghe thử giọng mẫu", open=False):
166
- ref_audio_preview = gr.Audio(interactive=False, show_label=False)
167
- ref_text_preview = gr.Markdown("...")
168
-
169
- with gr.TabItem("🎙️ Tự Clone", id="custom_mode"):
170
- gr.Markdown("<p style='color: #94a3b8; font-size: 0.85rem; margin-bottom: 5px;'>Tải lên audio nguồn để hệ thống mô phỏng giọng nói.</p>")
171
- custom_audio = gr.Audio(label="Audio mẫu (.wav/mp3)", type="filepath")
172
- # Ô nội dung mẫu được làm rộng hơn (lines=6)
173
- custom_text = gr.Textbox(
174
- label="NỘI DUNG AUDIO MẪU",
175
- placeholder="Nhập chính xác lời thoại của audio mẫu để AI học nhịp điệu...",
176
- lines=6,
177
- show_label=True
178
- )
179
-
180
  with gr.Row():
181
- pause_level = gr.Radio(choices=["Mặc định", "Trung bình", "Dài"], value="Mặc định", label="Độ ngắt nghỉ", scale=1)
182
- speed_select = gr.Dropdown(choices=[0.8, 0.9, 1.0, 1.1, 1.2, 1.5], value=1.0, label="Tốc độ đọc", scale=1)
183
-
184
- current_mode = gr.State(value="preset_mode")
185
  gr.Markdown("<br>")
186
- btn_generate = gr.Button("TỔNG HỢP GIỌNG NÓI", variant="primary", size="lg")
187
-
188
- with gr.Group(elem_classes="st-card result-card"):
189
- audio_output = gr.Audio(label="KẾT QUẢ ÂM THANH", interactive=False, autoplay=True)
190
- status_output = gr.Markdown("<p style='text-align: center; color: #818cf8; font-weight: 500;'>✨ Hệ thống sẵn sàng thực hiện</p>")
191
-
192
- gr.HTML("<div class='footer'>ENGINE BY VIENEU-TTS • PROFESSIONAL STUDIO EDITION 2025</div>")
193
 
194
  # LOGIC
195
- text_input.change(lambda t: f"<div style='text-align: right; color: {'#6366f1' if len(t)<=250 else '#f43f5e'}; font-weight: bold; padding: 5px;'>{len(t)} / 250</div>", text_input, char_count)
196
- voice_select.change(lambda v: load_reference_info(v), voice_select, [ref_audio_preview, ref_text_preview])
197
- tabs.children[0].select(fn=lambda: "preset_mode", outputs=current_mode)
198
- tabs.children[1].select(fn=lambda: "custom_mode", outputs=current_mode)
199
- btn_generate.click(fn=synthesize_speech, inputs=[text_input, voice_select, custom_audio, custom_text, current_mode, pause_level, speed_select], outputs=[audio_output, status_output])
200
 
201
  if __name__ == "__main__":
202
- demo.queue().launch(theme=theme, css=css, server_name="0.0.0.0", server_port=7860)
 
12
  # IMPORT TỪ FILE ENGINE CỦA BẠN
13
  from tts_engine import VoiceEngine
14
 
15
+ # --- 1. SETUP MODEL (SỬ DỤNG CHÍNH XÁC REPO CỦA BẠN) ---
 
 
 
16
  device = "cuda" if torch.cuda.is_available() else "cpu"
17
 
18
+ # Đã chuyển hướng về tài khoản ktvoice của bạn
19
+ MY_BACKBONE = "ktvoice/Backbone"
20
+ MY_CODEC = "ktvoice/Codec"
21
+
22
  try:
23
  tts = VoiceEngine(
24
+ backbone_repo=MY_BACKBONE,
25
  backbone_device=device,
26
+ codec_repo=MY_CODEC,
27
  codec_device=device
28
  )
29
  except Exception as e:
 
36
  return np.random.uniform(-0.1, 0.1, 24000*2)
37
  tts = MockTTS()
38
 
39
+ # --- 2. DATA (Giọng mẫu cục bộ) ---
40
  VOICE_SAMPLES = {
41
  "Tuyên (nam miền Bắc)": {"audio": "./sample/Tuyên (nam miền Bắc).wav", "text": "./sample/Tuyên (nam miền Bắc).txt"},
42
  "Thiện Tâm": {"audio": "./sample/thientam.mp3", "text": "./sample/thientam.txt"},
 
63
  @spaces.GPU(duration=120)
64
  def synthesize_speech(text, voice_choice, custom_audio, custom_text, mode_tab, pause_level, speed_value):
65
  try:
66
+ if not text or text.strip() == "": return None, "⚠️ Vui lòng nhập nội dung!"
 
67
 
68
+ # Tiền xử lý độ ngắt nghỉ
69
+ p_text = text
70
+ if pause_level == "Trung bình": p_text = p_text.replace(",", ", , ").replace(".", ". . ")
71
+ elif pause_level == "Dài": p_text = p_text.replace(",", ", , , ").replace(".", ". . . . ")
 
 
72
 
 
 
 
 
73
  if mode_tab == "custom_mode":
74
+ ref_path, ref_text_raw = custom_audio, custom_text
 
 
 
75
  else:
76
+ ref_path = VOICE_SAMPLES[voice_choice]["audio"]
77
  with open(VOICE_SAMPLES[voice_choice]["text"], "r", encoding="utf-8") as f:
78
  ref_text_raw = f.read()
79
 
 
80
  start_time = time.time()
81
+ ref_codes = tts.encode_reference(ref_path)
82
+ wav = tts.infer(p_text[:400], ref_codes, ref_text_raw)
83
 
84
+ # Điều chỉnh tốc độ
85
  if speed_value != 1.0:
86
  wav = librosa.effects.time_stretch(wav, rate=float(speed_value))
87
 
88
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".wav") as tmp:
89
+ sf.write(tmp.name, wav, 24000)
90
+ output_path = tmp.name
 
 
91
 
92
+ return output_path, f"⚡ Hoàn tất: {time.time() - start_time:.2f}s"
93
+ except Exception as e: return None, f"❌ Lỗi: {str(e)}"
 
94
 
95
+ # --- UI SETUP ---
96
+ theme = gr.themes.Default(primary_hue="indigo", neutral_hue="slate").set(
97
+ body_background_fill="#020617", block_background_fill="#0f172a",
98
+ input_background_fill="#1e293b", button_primary_background_fill="linear-gradient(135deg, #4f46e5 0%, #7c3aed 100%)",
 
 
 
 
 
 
 
 
 
99
  )
100
 
101
+ css = ".main-wrap { max-width: 1240px !important; margin: auto !important; padding: 20px !important; } .st-card { border-radius: 16px !important; border: 1px solid rgba(255,255,255,0.1) !important; padding: 15px; background: #0f172a !important; } audio { filter: invert(90%) hue-rotate(180deg) brightness(1.5); width: 100%; }"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
102
 
103
  with gr.Blocks(title="AI Voice Studio") as demo:
104
  with gr.Column(elem_classes="main-wrap"):
 
 
105
  with gr.Row(equal_height=True):
 
106
  with gr.Column(scale=1):
107
  with gr.Group(elem_classes="st-card"):
108
+ text_input = gr.Textbox(label="VĂN BẢN ĐẦU VÀO", lines=24, show_label=True)
109
+ char_count = gr.HTML("<div style='text-align: right; color: #6366f1; font-weight: bold;'>0 / 250</div>")
 
 
 
 
 
 
 
110
  with gr.Column(scale=1):
111
  with gr.Tabs() as tabs:
112
+ with gr.TabItem("👤 Giọng Mẫu", id="p_mode"):
113
+ voice_select = gr.Dropdown(choices=list(VOICE_SAMPLES.keys()), value="Tuyên (nam miền Bắc)", label="Nghệ sĩ đọc")
114
+ with gr.Accordion("Nghe thử", open=False):
115
+ ref_p = gr.Audio(interactive=False, show_label=False)
116
+ ref_t = gr.Markdown("...")
117
+ with gr.TabItem("🎙️ Tự Clone", id="c_mode"):
118
+ c_audio = gr.Audio(label="Audio gốc", type="filepath")
119
+ c_text = gr.Textbox(label="NỘI DUNG MẪU", lines=6)
 
 
 
 
 
 
 
 
 
 
 
 
 
120
  with gr.Row():
121
+ p_lvl = gr.Radio(choices=["Mặc định", "Trung bình", "Dài"], value="Mặc định", label="Độ ngắt nghỉ")
122
+ s_val = gr.Dropdown(choices=[0.8, 0.9, 1.0, 1.1, 1.2, 1.5], value=1.0, label="Tốc độ")
123
+ c_mode = gr.State(value="p_mode")
 
124
  gr.Markdown("<br>")
125
+ btn = gr.Button("TỔNG HỢP NGAY", variant="primary", size="lg")
126
+ with gr.Group(elem_classes="st-card"):
127
+ a_out = gr.Audio(label="KẾT QUẢ", interactive=False, autoplay=True)
128
+ gr.HTML("<div style='text-align: center; margin-top: 40px; color: #475569;'>AI VOICE STUDIO • 2025</div>")
 
 
 
129
 
130
  # LOGIC
131
+ text_input.change(lambda t: f"<div style='text-align: right; color: {'#6366f1' if len(t)<=250 else '#f43f5e'}; font-weight: bold;'>{len(t)} / 250</div>", text_input, char_count)
132
+ voice_select.change(load_reference_info, voice_select, [ref_p, ref_t])
133
+ tabs.children[0].select(fn=lambda: "p_mode", outputs=c_mode)
134
+ tabs.children[1].select(fn=lambda: "c_mode", outputs=c_mode)
135
+ btn.click(synthesize_speech, [text_input, voice_select, c_audio, c_text, c_mode, p_lvl, s_val], [a_out])
136
 
137
  if __name__ == "__main__":
138
+ demo.queue().launch(theme=theme, css=css)
config.yaml CHANGED
@@ -1,65 +1,67 @@
 
 
1
  text_settings:
2
- max_chars_per_chunk: 256
3
  max_total_chars_streaming: 3000
4
 
 
5
  backbone_configs:
6
- "VieNeu-TTS (GPU)":
7
- repo: pnnbao-ump/VieNeu-TTS
8
  supports_streaming: false
9
- description: Chất lượng cao nhất, yêu cầu GPU
10
- "VieNeu-TTS-q8-gguf":
11
- repo: pnnbao-ump/VieNeu-TTS-q8-gguf
 
12
  supports_streaming: true
13
- description: Cân bằng giữa chất lượngtốc độ
14
- "VieNeu-TTS-q4-gguf":
15
- repo: pnnbao-ump/VieNeu-TTS-q4-gguf
 
16
  supports_streaming: true
17
- description: Nhẹ nhất, phù hợp CPU
18
 
 
19
  codec_configs:
20
- "NeuCodec (Standard)":
21
- repo: neuphonic/neucodec
22
- description: Codec chuẩn, tốc độ trung bình
23
  use_preencoded: false
24
- "NeuCodec ONNX (Fast CPU)":
25
- repo: neuphonic/neucodec-onnx-decoder
26
- description: Tối ưu cho CPU, cần pre-encoded codes
 
27
  use_preencoded: true
28
 
 
29
  voice_samples:
30
  "Tuyên (nam miền Bắc)":
31
  audio: ./sample/Tuyên (nam miền Bắc).wav
32
  text: ./sample/Tuyên (nam miền Bắc).txt
33
- codes: ./sample/Tuyên (nam miền Bắc).pt
 
 
34
  "Vĩnh (nam miền Nam)":
35
  audio: ./sample/Vĩnh (nam miền Nam).wav
36
  text: ./sample/Vĩnh (nam miền Nam).txt
37
- codes: ./sample/Vĩnh (nam miền Nam).pt
38
  "Bình (nam miền Bắc)":
39
  audio: ./sample/Bình (nam miền Bắc).wav
40
  text: ./sample/Bình (nam miền Bắc).txt
41
- codes: ./sample/Bình (nam miền Bắc).pt
42
  "Nguyên (nam miền Nam)":
43
  audio: ./sample/Nguyên (nam miền Nam).wav
44
  text: ./sample/Nguyên (nam miền Nam).txt
45
- codes: ./sample/Nguyên (nam miền Nam).pt
46
  "Sơn (nam miền Nam)":
47
  audio: ./sample/Sơn (nam miền Nam).wav
48
  text: ./sample/Sơn (nam miền Nam).txt
49
- codes: ./sample/Sơn (nam miền Nam).pt
50
  "Đoan (nữ miền Nam)":
51
  audio: ./sample/Đoan (nữ miền Nam).wav
52
  text: ./sample/Đoan (nữ miền Nam).txt
53
- codes: ./sample/Đoan (nữ miền Nam).pt
54
  "Ngọc (nữ miền Bắc)":
55
  audio: ./sample/Ngọc (nữ miền Bắc).wav
56
  text: ./sample/Ngọc (nữ miền Bắc).txt
57
- codes: ./sample/Ngọc (nữ miền Bắc).pt
58
  "Ly (nữ miền Bắc)":
59
  audio: ./sample/Ly (nữ miền Bắc).wav
60
  text: ./sample/Ly (nữ miền Bắc).txt
61
- codes: ./sample/Ly (nữ miền Bắc).pt
62
  "Dung (nữ miền Nam)":
63
  audio: ./sample/Dung (nữ miền Nam).wav
64
- text: ./sample/Dung (nữ miền Nam).txt
65
- codes: ./sample/Dung (nữ miền Nam).pt
 
1
+ # Cấu hình AI Voice Studio - Professional System Configuration
2
+
3
  text_settings:
4
+ max_chars_per_chunk: 250
5
  max_total_chars_streaming: 3000
6
 
7
+ # Cấu hình các dòng mô hình trí tuệ nhân tạo (Backbone) - Trỏ về tài khoản ktvoice
8
  backbone_configs:
9
+ "AI Engine - Premium (Standard)":
10
+ repo: ktvoice/Backbone
11
  supports_streaming: false
12
+ description: Chất lượng âm thanh cao nhất, sử dụng model gốc từ tài khoản ktvoice.
13
+
14
+ "AI Engine - Balanced (q8)":
15
+ repo: ktvoice/Backbone-q8-gguf
16
  supports_streaming: true
17
+ description: Phiên bản nén q8, cân bằng giữa tốc độchất lượng.
18
+
19
+ "AI Engine - Lite (q4 CPU)":
20
+ repo: ktvoice/Backbone-q4-gguf
21
  supports_streaming: true
22
+ description: Tốc độ xử lý cực nhanh trên CPU, tối ưu cho thiết bị cấu hình thấp.
23
 
24
+ # Cấu hình các bộ giải mã âm thanh (Codec)
25
  codec_configs:
26
+ "Standard High-Fidelity":
27
+ repo: ktvoice/Codec
28
+ description: Bộ giải mã tiêu chuẩn cho độ chi tiết âm thanh cao nhất.
29
  use_preencoded: false
30
+
31
+ "Turbo Decoder (ONNX)":
32
+ repo: ktvoice/Codec-ONNX
33
+ description: Bộ giải mã siêu tốc tối ưu cho CPU.
34
  use_preencoded: true
35
 
36
+ # Danh sách đầy đủ 10 nghệ sĩ và giọng đọc mẫu (Khớp hoàn toàn với app.py)
37
  voice_samples:
38
  "Tuyên (nam miền Bắc)":
39
  audio: ./sample/Tuyên (nam miền Bắc).wav
40
  text: ./sample/Tuyên (nam miền Bắc).txt
41
+ "Thiện Tâm":
42
+ audio: ./sample/thientam.mp3
43
+ text: ./sample/thientam.txt
44
  "Vĩnh (nam miền Nam)":
45
  audio: ./sample/Vĩnh (nam miền Nam).wav
46
  text: ./sample/Vĩnh (nam miền Nam).txt
 
47
  "Bình (nam miền Bắc)":
48
  audio: ./sample/Bình (nam miền Bắc).wav
49
  text: ./sample/Bình (nam miền Bắc).txt
 
50
  "Nguyên (nam miền Nam)":
51
  audio: ./sample/Nguyên (nam miền Nam).wav
52
  text: ./sample/Nguyên (nam miền Nam).txt
 
53
  "Sơn (nam miền Nam)":
54
  audio: ./sample/Sơn (nam miền Nam).wav
55
  text: ./sample/Sơn (nam miền Nam).txt
 
56
  "Đoan (nữ miền Nam)":
57
  audio: ./sample/Đoan (nữ miền Nam).wav
58
  text: ./sample/Đoan (nữ miền Nam).txt
 
59
  "Ngọc (nữ miền Bắc)":
60
  audio: ./sample/Ngọc (nữ miền Bắc).wav
61
  text: ./sample/Ngọc (nữ miền Bắc).txt
 
62
  "Ly (nữ miền Bắc)":
63
  audio: ./sample/Ly (nữ miền Bắc).wav
64
  text: ./sample/Ly (nữ miền Bắc).txt
 
65
  "Dung (nữ miền Nam)":
66
  audio: ./sample/Dung (nữ miền Nam).wav
67
+ text: ./sample/Dung (nữ miền Nam).txt