Views
No views yet
pip install f5-tts gradio ruaccent transformers torch torchaudio huggingface_hub1#!/usr/bin/env python3
2import os
3import gc
4import tempfile
5import traceback
6from pathlib import Path
7
8import gradio as gr
9import numpy as np
10import soundfile as sf
11import torch
12import torchaudio
13from huggingface_hub import hf_hub_download, snapshot_download
14from ruaccent import RUAccent
15from f5_tts.infer.utils_infer import (
16 infer_process,
17 load_model,
18 load_vocoder,
19 preprocess_ref_audio_text,
20 remove_silence_for_generated_wav,
21 save_spectrogram,
22 tempfile_kwargs,
23)
24from f5_tts.model import DiT
25
26MODEL_CFG = dict(dim=1024, depth=22, heads=16, ff_mult=2, text_dim=512, conv_layers=4)
27MODEL_REPO = "ESpeech/ESpeech-TTS-1_RL-V2"
28MODEL_FILE = "espeech_tts_rlv2.pt"
29VOCAB_FILE = "vocab.txt"
30
31loaded_model = None
32
33def ensure_model():
34 global loaded_model
35 if loaded_model is not None:
36 return loaded_model
37 model_path = None
38 vocab_path = None
39 print(f"Trying to download model file '{MODEL_FILE}' and '{VOCAB_FILE}' from hub '{MODEL_REPO}'")
40 try:
41 model_path = hf_hub_download(repo_id=MODEL_REPO, filename=MODEL_FILE)
42 vocab_path = hf_hub_download(repo_id=MODEL_REPO, filename=VOCAB_FILE)
43 print(f"Downloaded model to {model_path}")
44 print(f"Downloaded vocab to {vocab_path}")
45 except Exception as e:
46 print("hf_hub_download failed:", e)
47 if model_path is None or vocab_path is None:
48 try:
49 local_dir = f"cache_{MODEL_REPO.replace('/', '_')}"
50 print(f"Attempting snapshot_download into {local_dir}...")
51 snapshot_dir = snapshot_download(repo_id=MODEL_REPO, cache_dir=None, local_dir=local_dir, token=hf_token)
52 possible_model = os.path.join(snapshot_dir, MODEL_FILE)
53 possible_vocab = os.path.join(snapshot_dir, VOCAB_FILE)
54 if os.path.exists(possible_model):
55 model_path = possible_model
56 if os.path.exists(possible_vocab):
57 vocab_path = possible_vocab
58 print(f"Snapshot downloaded to {snapshot_dir}")
59 except Exception as e:
60 print("snapshot_download failed:", e)
61 if not model_path or not os.path.exists(model_path):
62 raise FileNotFoundError(f"Model file not found after download attempts: {model_path}")
63 if not vocab_path or not os.path.exists(vocab_path):
64 raise FileNotFoundError(f"Vocab file not found after download attempts: {vocab_path}")
65 print(f"Loading model from: {model_path}")
66 loaded_model = load_model(DiT, MODEL_CFG, model_path, vocab_file=vocab_path)
67 return loaded_model
68
69print("Preloading model...")
70try:
71 ensure_model()
72 print("Model preloaded.")
73except Exception as e:
74 print(f"Model preload failed: {e}")
75
76print("Loading RUAccent...")
77accentizer = RUAccent()
78accentizer.load(omograph_model_size='turbo3.1', use_dictionary=True, tiny_mode=False)
79print("RUAccent loaded.")
80
81print("Loading vocoder...")
82vocoder = load_vocoder()
83print("Vocoder loaded.")
84
85def process_text_with_accent(text, accentizer):
86 if not text or not text.strip():
87 return text
88 if '+' in text:
89 return text
90 else:
91 return accentizer.process_all(text)
92
93def process_texts_only(ref_text, gen_text):
94 processed_ref_text = process_text_with_accent(ref_text, accentizer)
95 processed_gen_text = process_text_with_accent(gen_text, accentizer)
96 return processed_ref_text, processed_gen_text
97
98def synthesize(
99 ref_audio,
100 ref_text,
101 gen_text,
102 remove_silence,
103 seed,
104 cross_fade_duration=0.15,
105 nfe_step=32,
106 speed=1.0,
107):
108 if not ref_audio:
109 gr.Warning("Please provide reference audio.")
110 return None, None, ref_text, gen_text
111 if seed is None or seed < 0 or seed > 2**31 - 1:
112 seed = np.random.randint(0, 2**31 - 1)
113 torch.manual_seed(int(seed))
114 if not gen_text or not gen_text.strip():
115 gr.Warning("Please enter text to generate.")
116 return None, None, ref_text, gen_text
117 if not ref_text or not ref_text.strip():
118 gr.Warning("Please provide reference text.")
119 return None, None, ref_text, gen_text
120 processed_ref_text = process_text_with_accent(ref_text, accentizer)
121 processed_gen_text = process_text_with_accent(gen_text, accentizer)
122 try:
123 model = ensure_model()
124 except Exception as e:
125 gr.Warning(f"Failed to load model: {e}")
126 return None, None, processed_ref_text, processed_gen_text
127 device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
128 try:
129 if device.type == "cuda":
130 try:
131 model.to(device)
132 vocoder.to(device)
133 except Exception as e:
134 print("Warning: failed to move model/vocoder to cuda:", e)
135 try:
136 ref_audio_proc, processed_ref_text_final = preprocess_ref_audio_text(
137 ref_audio,
138 processed_ref_text,
139 show_info=gr.Info
140 )
141 except Exception as e:
142 gr.Warning(f"Preprocess failed: {e}")
143 traceback.print_exc()
144 return None, None, processed_ref_text, processed_gen_text
145 try:
146 final_wave, final_sample_rate, combined_spectrogram = infer_process(
147 ref_audio_proc,
148 processed_ref_text_final,
149 processed_gen_text,
150 model,
151 vocoder,
152 cross_fade_duration=cross_fade_duration,
153 nfe_step=nfe_step,
154 speed=speed,
155 show_info=gr.Info,
156 progress=gr.Progress(),
157 )
158 except Exception as e:
159 gr.Warning(f"Infer failed: {e}")
160 traceback.print_exc()
161 return None, None, processed_ref_text, processed_gen_text
162 if remove_silence:
163 try:
164 with tempfile.NamedTemporaryFile(suffix=".wav", **tempfile_kwargs) as f:
165 temp_path = f.name
166 sf.write(temp_path, final_wave, final_sample_rate)
167 remove_silence_for_generated_wav(temp_path)
168 final_wave_tensor, _ = torchaudio.load(temp_path)
169 final_wave = final_wave_tensor.squeeze().cpu().numpy()
170 except Exception as e:
171 print("Remove silence failed:", e)
172 try:
173 with tempfile.NamedTemporaryFile(suffix=".png", **tempfile_kwargs) as tmp_spectrogram:
174 spectrogram_path = tmp_spectrogram.name
175 save_spectrogram(combined_spectrogram, spectrogram_path)
176 except Exception as e:
177 print("Save spectrogram failed:", e)
178 spectrogram_path = None
179 return (final_sample_rate, final_wave), spectrogram_path, processed_ref_text_final, processed_gen_text
180 finally:
181 if device.type == "cuda":
182 try:
183 model.to("cpu")
184 vocoder.to("cpu")
185 torch.cuda.empty_cache()
186 gc.collect()
187 except Exception as e:
188 print("Warning during cuda cleanup:", e)
189
190with gr.Blocks(title="ESpeech-TTS") as app:
191 gr.Markdown("# ESpeech-TTS")
192 gr.Markdown("💡 **Совет:** Добавьте символ '+' для ударения (например, 'прив+ет')")
193 gr.Markdown("❌ **Совет:** Референс должен быть не более 12 секунд")
194 with gr.Row():
195 with gr.Column():
196 ref_audio_input = gr.Audio(label="Reference Audio", type="filepath")
197 ref_text_input = gr.Textbox(
198 label="Reference Text",
199 lines=2,
200 placeholder="Text corresponding to reference audio"
201 )
202 with gr.Column():
203 gen_text_input = gr.Textbox(
204 label="Text to Generate",
205 lines=5,
206 max_lines=20,
207 placeholder="Enter text to synthesize..."
208 )
209 process_text_btn = gr.Button("✏️ Process Text (Add Accents)", variant="secondary")
210 with gr.Accordion("Advanced Settings", open=False):
211 with gr.Row():
212 seed_input = gr.Number(label="Seed (-1 for random)", value=-1, precision=0)
213 remove_silence = gr.Checkbox(label="Remove Silences", value=False)
214 with gr.Row():
215 speed_slider = gr.Slider(label="Speed", minimum=0.3, maximum=2.0, value=1.0, step=0.1)
216 nfe_slider = gr.Slider(label="NFE Steps", minimum=4, maximum=64, value=48, step=2)
217 cross_fade_slider = gr.Slider(label="Cross-Fade Duration (s)", minimum=0.0, maximum=1.0, value=0.15, step=0.01)
218 generate_btn = gr.Button("🎤 Generate Speech", variant="primary", size="lg")
219 with gr.Row():
220 audio_output = gr.Audio(label="Generated Audio", type="numpy")
221 spectrogram_output = gr.Image(label="Spectrogram", type="filepath")
222 process_text_btn.click(
223 process_texts_only,
224 inputs=[ref_text_input, gen_text_input],
225 outputs=[ref_text_input, gen_text_input]
226 )
227 generate_btn.click(
228 synthesize,
229 inputs=[
230 ref_audio_input,
231 ref_text_input,
232 gen_text_input,
233 remove_silence,
234 seed_input,
235 cross_fade_slider,
236 nfe_slider,
237 speed_slider,
238 ],
239 outputs=[audio_output, spectrogram_output, ref_text_input, gen_text_input]
240 )
241
242if __name__ == "__main__":
243 app.launch()