Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import onnxruntime as ort | |
| import numpy as np | |
| import json | |
| import pysrt | |
| import re | |
| import os | |
| from vinorm import TTSnorm | |
| from phonemizer import phonemize | |
| from scipy.io import wavfile | |
| # --- 1. DANH SÁCH GIỌNG NÓI TỪ ẢNH GOOGLE DRIVE --- | |
| # Bạn có thể thêm hoặc sửa tên hiển thị ở đây | |
| VOICE_MAP = { | |
| "Phương Mai 1": "phuongmai1.onnx", | |
| "Ngọc Ngân 3701": "ngocngan3701.onnx", | |
| "Lạc Phi": "lacphi.onnx", | |
| "Ngọc Huyền": "ngochuyen.onnx", | |
| "Duy Oryx 3175": "duyoryx3175.onnx", | |
| # Thêm các giọng khác nếu bạn đã upload tệp lên | |
| } | |
| # Biến toàn cục lưu trữ session để tiết kiệm RAM | |
| current_sess = None | |
| current_phoneme_map = None | |
| current_sample_rate = 22050 | |
| def load_voice_model(name): | |
| global current_sess, current_phoneme_map, current_sample_rate | |
| onnx_file = VOICE_MAP[name] | |
| json_file = onnx_file + ".json" | |
| if not os.path.exists(onnx_file) or not os.path.exists(json_file): | |
| raise FileNotFoundError(f"Thiếu tệp {onnx_file} hoặc {json_file} trên Space!") | |
| with open(json_file, "r", encoding="utf-8") as f: | |
| cfg = json.load(f) | |
| current_phoneme_map = cfg["phoneme_id_map"] | |
| current_sample_rate = cfg.get("audio", {}).get("sample_rate", 22050) | |
| current_sess = ort.InferenceSession(onnx_file, providers=['CPUExecutionProvider']) | |
| # --- 2. XỬ LÝ ÂM VỊ & ĐỒNG BỘ SRT --- | |
| def process_text(text): | |
| text = re.sub(r'<[^>]*>', '', text) # Làm sạch HTML | |
| text = TTSnorm(text.replace('\n', ' ').strip()) # Chuẩn hóa tiếng Việt | |
| phonemes = phonemize(text, language='vi', backend='espeak', strip=True) | |
| ids = [current_phoneme_map.get("^", [1])[0]] | |
| for p in phonemes: | |
| if p in current_phoneme_map: | |
| ids.extend(current_phoneme_map[p]) | |
| ids.append(current_phoneme_map.get("_", [0])[0]) | |
| ids.append(current_phoneme_map.get("$", [2])[0]) | |
| return ids | |
| def srt_to_audio_sync(voice_name, srt_file, speed=1.30): | |
| if srt_file is None: return "Chưa chọn file SRT", None | |
| try: | |
| load_voice_model(voice_name) # Nạp model được chọn | |
| subs = pysrt.open(srt_file.name, encoding='utf-8') | |
| final_audio_stream = [] | |
| current_sample_idx = 0 | |
| for sub in subs: | |
| # Đồng bộ mốc thời gian SRT | |
| start_sample = int((sub.start.ordinal / 1000.0) * current_sample_rate) | |
| if start_sample > current_sample_idx: | |
| final_audio_stream.append(np.zeros(start_sample - current_sample_idx, dtype=np.float32)) | |
| current_sample_idx = start_sample | |
| # Inference ONNX | |
| ids = process_text(sub.text) | |
| scales = np.array([0.667, 0.8, 1.0/speed], dtype=np.float32) | |
| outputs = current_sess.run(None, { | |
| "input": np.array([ids], dtype=np.int64), | |
| "input_lengths": np.array([len(ids)], dtype=np.int64), | |
| "scales": scales | |
| }) | |
| # Làm phẳng mảng 4D | |
| audio_segment = outputs[0].flatten().astype(np.float32) | |
| final_audio_stream.append(audio_segment) | |
| current_sample_idx += len(audio_segment) | |
| output_path = "synced_voice.wav" | |
| wavfile.write(output_path, current_sample_rate, np.concatenate(final_audio_stream)) | |
| return f"Hoàn thành lồng tiếng với giọng: {voice_name}", output_path | |
| except Exception as e: | |
| return f"Lỗi: {str(e)}", None | |
| # --- 3. GIAO DIỆN CHỌN TÊN GIỌNG --- | |
| with gr.Blocks(title="Multi-Voice SRT Dubbing") as demo: | |
| gr.Markdown("# 🗣️ Hệ thống lồng tiếng 7 giọng Tiếng Việt") | |
| with gr.Row(): | |
| with gr.Column(): | |
| # Menu chọn tên giọng như trong ảnh Drive của bạn | |
| voice_select = gr.Dropdown(choices=list(VOICE_MAP.keys()), value="Phương Mai 1", label="Chọn giọng đọc") | |
| file_input = gr.File(label="Tải file SRT từ SD Card", file_types=[".srt"]) | |
| speed_input = gr.Slider(0.5, 2.0, value=1.30, label="Tốc độ (Mặc định 1.30)") | |
| btn = gr.Button("🚀 Chạy lồng tiếng", variant="primary") | |
| with gr.Column(): | |
| status = gr.Textbox(label="Trạng thái") | |
| audio_out = gr.Audio(label="Audio hoàn chỉnh (Khớp thời gian)") | |
| btn.click(srt_to_audio_sync, [voice_select, file_input, speed_input], [status, audio_out]) | |
| demo.launch(server_name="0.0.0.0") |