NeuralFalcon commited on
Commit
2103a11
·
verified ·
1 Parent(s): c107abd

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +505 -0
app.py ADDED
@@ -0,0 +1,505 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # %cd /content/omnivoice-colab
2
+ import subprocess
3
+ # Clone the repository
4
+ subprocess.run(["git", "clone", "https://github.com/k2-fsa/OmniVoice.git"])
5
+
6
+
7
+
8
+
9
+ import os
10
+ import sys
11
+ import logging
12
+ import tempfile
13
+ from typing import Any, Dict
14
+
15
+ import gradio as gr
16
+ import numpy as np
17
+ import torch
18
+ import scipy.io.wavfile as wavfile
19
+ import re
20
+ import os
21
+ import uuid
22
+ temp_audio_dir="./Omni_Audio"
23
+ os.makedirs(temp_audio_dir, exist_ok=True)
24
+
25
+
26
+ # ---------------------------------------------------------------------------
27
+ # Setup path to import subtitle_maker from /content/omnivoice-colab/OmniVoice/
28
+ OmniVoice_path = f"{os.getcwd()}/OmniVoice/"
29
+ sys.path.append(OmniVoice_path)
30
+ from subtitle import subtitle_maker
31
+
32
+ # Attempt to import Whisper's supported language dict to filter unsupported languages
33
+ try:
34
+ from subtitle import LANGUAGE_CODE as WHISPER_LANGUAGE_CODE
35
+ except ImportError:
36
+ WHISPER_LANGUAGE_CODE = None
37
+
38
+ from omnivoice import OmniVoice, OmniVoiceGenerationConfig
39
+ from omnivoice.utils.lang_map import LANG_NAMES, lang_display_name
40
+
41
+ # ---------------------------------------------------------------------------
42
+ # Logging Setup
43
+ # ---------------------------------------------------------------------------
44
+ logging.basicConfig(
45
+ level=logging.WARNING,
46
+ format="%(asctime)s %(name)s %(levelname)s: %(message)s",
47
+ )
48
+ logging.getLogger("omnivoice").setLevel(logging.DEBUG)
49
+
50
+ # ---------------------------------------------------------------------------
51
+ # Model Loading (Global Scope)
52
+ # ---------------------------------------------------------------------------
53
+ print("Loading model from k2-fsa/OmniVoice to cuda ...")
54
+ model = OmniVoice.from_pretrained(
55
+ "k2-fsa/OmniVoice",
56
+ device_map="cuda",
57
+ dtype=torch.float16,
58
+ load_asr=False,
59
+ )
60
+
61
+ # from hf_mirror import download_model
62
+ # try:
63
+ # model = OmniVoice.from_pretrained(
64
+ # "k2-fsa/OmniVoice",
65
+ # device_map="cuda",
66
+ # dtype=torch.float16,
67
+ # load_asr=False,
68
+ # )
69
+ # except Exception as e:
70
+ # omnivoice_model_path=download_model(
71
+ # "k2-fsa/OmniVoice",
72
+ # download_folder="./OmniVoice_Model",
73
+ # redownload=False,
74
+ # workers=6,
75
+ # use_snapshot=False,
76
+ # )
77
+
78
+ # model = OmniVoice.from_pretrained(
79
+ # omnivoice_model_path,
80
+ # device_map="cuda",
81
+ # dtype=torch.float16,
82
+ # load_asr=False,
83
+ # )
84
+
85
+ sampling_rate = model.sampling_rate
86
+ print("Model loaded successfully!")
87
+
88
+ # ---------------------------------------------------------------------------
89
+ # Event Tags & JS Functions
90
+ # ---------------------------------------------------------------------------
91
+ EVENT_TAGS = [
92
+ "[laughter]", "[sigh]", "[confirmation-en]", "[question-en]",
93
+ "[question-ah]", "[question-oh]", "[question-ei]", "[question-yi]",
94
+ "[surprise-ah]", "[surprise-oh]", "[surprise-wa]", "[surprise-yo]",
95
+ "[dissatisfaction-hnn]"
96
+ ]
97
+
98
+ # JS for Voice Clone Tab Textbox
99
+ INSERT_TAG_JS_VC = """
100
+ (tag_val, current_text) => {
101
+ const textarea = document.querySelector('#vc_textbox textarea');
102
+ if (!textarea) return current_text + " " + tag_val;
103
+ const start = textarea.selectionStart;
104
+ const end = textarea.selectionEnd;
105
+ let prefix = " ";
106
+ let suffix = " ";
107
+ if (!current_text) return tag_val;
108
+ if (start === 0) prefix = "";
109
+ else if (current_text[start - 1] === ' ') prefix = "";
110
+ if (end < current_text.length && current_text[end] === ' ') suffix = "";
111
+ return current_text.slice(0, start) + prefix + tag_val + suffix + current_text.slice(end);
112
+ }
113
+ """
114
+
115
+ # JS for Voice Design Tab Textbox
116
+ INSERT_TAG_JS_VD = """
117
+ (tag_val, current_text) => {
118
+ const textarea = document.querySelector('#vd_textbox textarea');
119
+ if (!textarea) return current_text + " " + tag_val;
120
+ const start = textarea.selectionStart;
121
+ const end = textarea.selectionEnd;
122
+ let prefix = " ";
123
+ let suffix = " ";
124
+ if (!current_text) return tag_val;
125
+ if (start === 0) prefix = "";
126
+ else if (current_text[start - 1] === ' ') prefix = "";
127
+ if (end < current_text.length && current_text[end] === ' ') suffix = "";
128
+ return current_text.slice(0, start) + prefix + tag_val + suffix + current_text.slice(end);
129
+ }
130
+ """
131
+
132
+ # ---------------------------------------------------------------------------
133
+ # UI Configurations & Language Mappings
134
+ # ---------------------------------------------------------------------------
135
+ _ALL_LANGUAGES = ["Auto"] + sorted(lang_display_name(n) for n in LANG_NAMES)
136
+
137
+ _CATEGORIES = {
138
+ "Gender": ["Male", "Female"],
139
+ "Age": ["Child", "Teenager", "Young Adult", "Middle-aged", "Elderly"],
140
+ "Pitch": ["Very Low Pitch", "Low Pitch", "Moderate Pitch", "High Pitch", "Very High Pitch"],
141
+ "Style": ["Whisper"],
142
+ "English Accent": [
143
+ "American Accent", "Australian Accent", "British Accent", "Chinese Accent",
144
+ "Canadian Accent", "Indian Accent", "Korean Accent", "Portuguese Accent",
145
+ "Russian Accent", "Japanese Accent"
146
+ ],
147
+ "Chinese Dialect": [
148
+ "Henan Dialect", "Shaanxi Dialect", "Sichuan Dialect", "Guizhou Dialect",
149
+ "Yunnan Dialect", "Guilin Dialect", "Jinan Dialect", "Shijiazhuang Dialect",
150
+ "Gansu Dialect", "Ningxia Dialect", "Qingdao Dialect", "Northeast Dialect"
151
+ ],
152
+ }
153
+
154
+ DIALECT_MAP = {
155
+ "Henan Dialect": "河南话", "Shaanxi Dialect": "陕西话", "Sichuan Dialect": "四川话",
156
+ "Guizhou Dialect": "贵州话", "Yunnan Dialect": "云南话", "Guilin Dialect": "桂林话",
157
+ "Jinan Dialect": "济南话", "Shijiazhuang Dialect": "石家庄话", "Gansu Dialect": "甘肃话",
158
+ "Ningxia Dialect": "宁夏话", "Qingdao Dialect": "青岛话", "Northeast Dialect": "东北话",
159
+ }
160
+
161
+ _ATTR_INFO = {
162
+ "English Accent": "Only effective for English speech.",
163
+ "Chinese Dialect": "Only effective for Chinese speech.",
164
+ }
165
+
166
+ # ---------------------------------------------------------------------------
167
+ # Core Logic & Helpers
168
+ # ---------------------------------------------------------------------------
169
+ def _is_whisper_supported(lang):
170
+ """Check if the selected language is supported by Whisper to save processing time."""
171
+ if not lang or lang == "Auto":
172
+ return True
173
+
174
+ if WHISPER_LANGUAGE_CODE is None:
175
+ return True
176
+
177
+ supported_langs = [str(k).lower() for k in WHISPER_LANGUAGE_CODE.keys()] + \
178
+ [str(v).lower() for v in WHISPER_LANGUAGE_CODE.values()]
179
+
180
+ lang_lower = lang.lower()
181
+ for w_lang in supported_langs:
182
+ if w_lang in lang_lower or lang_lower in w_lang:
183
+ return True
184
+
185
+ return False
186
+
187
+ def generate_subtitles_if_needed(wav_path, lang, want_subs):
188
+ """Generates Subtitles only if user requested them and language is supported."""
189
+ if not want_subs:
190
+ return None, None, None
191
+
192
+ if not _is_whisper_supported(lang):
193
+ logging.warning(f"Language '{lang}' is likely unsupported by Whisper. Skipping subtitle generation.")
194
+ return None, None, None
195
+
196
+ try:
197
+ whisper_lang = lang if (lang and lang != "Auto") else None
198
+ whisper_results = subtitle_maker(wav_path, whisper_lang)
199
+ if whisper_results and len(whisper_results) > 3:
200
+ return whisper_results[1], whisper_results[2], whisper_results[3]
201
+ except Exception as e:
202
+ logging.warning(f"Subtitle generation failed: {e}")
203
+
204
+ return None, None, None
205
+
206
+
207
+ def tts_file_name(text, language="en"):
208
+ global temp_audio_dir
209
+
210
+ # --- Clean text ---
211
+ clean_text = re.sub(r'[^a-zA-Z\s]', '', text) # keep only letters + spaces
212
+ clean_text = clean_text.lower().strip().replace(" ", "_")
213
+
214
+ if not clean_text:
215
+ clean_text = "audio"
216
+
217
+ # --- Truncate ---
218
+ truncated = clean_text[:20]
219
+
220
+ # --- Clean language ---
221
+ lang = re.sub(r'\s+', '_', language.strip().lower()) if language else "unknown"
222
+
223
+ # --- Random suffix ---
224
+ rand = uuid.uuid4().hex[:8].upper()
225
+
226
+ # --- Final filename ---
227
+ return f"{temp_audio_dir}/{truncated}_{lang}_{rand}.wav"
228
+
229
+
230
+ def _gen_core(
231
+ text, language, ref_audio, instruct, num_step, guidance_scale,
232
+ denoise, speed, duration, preprocess_prompt, postprocess_output, mode, ref_text=None
233
+ ):
234
+ """Core Text-to-Speech Generation Logic"""
235
+ if not text or not text.strip():
236
+ return None, "Please enter the text to synthesize."
237
+
238
+ if mode == "clone" and ref_audio and not ref_text:
239
+ try:
240
+ whisper_lang = language if (language and language != "Auto") else None
241
+ whisper_results = subtitle_maker(ref_audio, whisper_lang)
242
+ if whisper_results and len(whisper_results) > 7:
243
+ ref_text = whisper_results[7]
244
+ except Exception as e:
245
+ logging.warning(f"Fallback transcription failed: {e}")
246
+
247
+ gen_config = OmniVoiceGenerationConfig(
248
+ num_step=int(num_step or 32),
249
+ guidance_scale=float(guidance_scale) if guidance_scale is not None else 2.0,
250
+ denoise=bool(denoise) if denoise is not None else True,
251
+ preprocess_prompt=bool(preprocess_prompt),
252
+ postprocess_output=bool(postprocess_output),
253
+ )
254
+
255
+ lang = language if (language and language != "Auto") else None
256
+ kw: Dict[str, Any] = dict(text=text.strip(), language=lang, generation_config=gen_config)
257
+
258
+ if speed is not None and float(speed) != 1.0:
259
+ kw["speed"] = float(speed)
260
+ if duration is not None and float(duration) > 0:
261
+ kw["duration"] = float(duration)
262
+
263
+ if mode == "clone":
264
+ if not ref_audio:
265
+ return None, "Please upload a reference audio."
266
+ kw["voice_clone_prompt"] = model.create_voice_clone_prompt(ref_audio=ref_audio, ref_text=ref_text)
267
+ if mode == "design":
268
+ if instruct and instruct.strip():
269
+ kw["instruct"] = instruct.strip()
270
+
271
+ try:
272
+ audio = model.generate(**kw)
273
+ except Exception as e:
274
+ return None, f"Error: {type(e).__name__}: {e}"
275
+
276
+ waveform = audio[0].squeeze(0).numpy()
277
+ waveform = (waveform * 32767).astype(np.int16)
278
+ return (sampling_rate, waveform), "Done."
279
+
280
+ # ---------------------------------------------------------------------------
281
+ # Gradio UI Construction
282
+ # ---------------------------------------------------------------------------
283
+ theme = gr.themes.Soft(font=["Inter", "Arial", "sans-serif"])
284
+ css = """
285
+ .gradio-container {max-width: 100% !important; font-size: 16px !important;}
286
+ .gradio-container h1 {font-size: 1.5em !important;}
287
+ .gradio-container .prose {font-size: 1.1em !important;}
288
+ .compact-audio audio {height: 60px !important;}
289
+ .compact-audio .waveform {min-height: 80px !important;}
290
+
291
+ /* CSS for Event Tags */
292
+ .tag-container {
293
+ display: flex !important;
294
+ flex-wrap: wrap !important;
295
+ gap: 8px !important;
296
+ margin-top: 5px !important;
297
+ margin-bottom: 10px !important;
298
+ border: none !important;
299
+ background: transparent !important;
300
+ }
301
+ .tag-btn {
302
+ min-width: fit-content !important;
303
+ width: auto !important;
304
+ height: 32px !important;
305
+ font-size: 13px !important;
306
+ background: #eef2ff !important;
307
+ border: 1px solid #c7d2fe !important;
308
+ color: #3730a3 !important;
309
+ border-radius: 6px !important;
310
+ padding: 0 10px !important;
311
+ margin: 0 !important;
312
+ box-shadow: none !important;
313
+ }
314
+ .tag-btn:hover {
315
+ background: #c7d2fe !important;
316
+ transform: translateY(-1px);
317
+ }
318
+ """
319
+
320
+ def _lang_dropdown(label="Language (optional)", value="Auto"):
321
+ return gr.Dropdown(
322
+ label=label, choices=_ALL_LANGUAGES, value=value,
323
+ allow_custom_value=False, interactive=True,
324
+ )
325
+
326
+ def _gen_settings():
327
+ with gr.Accordion("Generation Settings (optional)", open=False):
328
+ sp = gr.Slider(0.5, 1.5, value=1.0, step=0.05, label="Speed", info="1.0 = normal. >1 faster, <1 slower.")
329
+ du = gr.Number(value=None, label="Duration (seconds)", info="Set a fixed duration to override speed.")
330
+ ns = gr.Slider(4, 64, value=32, step=1, label="Inference Steps", info="Lower = faster, higher = better quality.")
331
+ dn = gr.Checkbox(label="Denoise", value=True)
332
+ gs = gr.Slider(0.0, 4.0, value=2.0, step=0.1, label="Guidance Scale (CFG)")
333
+ pp = gr.Checkbox(label="Preprocess Prompt", value=True, info="Applies silence removal and trims reference audio.")
334
+ po = gr.Checkbox(label="Postprocess Output", value=True, info="Removes long silences from generated audio.")
335
+ return ns, gs, dn, sp, du, pp, po
336
+
337
+ with gr.Blocks(theme=theme, css=css, title="OmniVoice Demo") as demo:
338
+ gr.HTML("""
339
+ <div style="text-align: center; margin: 20px auto; max-width: 800px;">
340
+ <h1 style="font-size: 2.5em; margin-bottom: 5px;">🎙️ OmniVoice Multilingual </h1>
341
+ <p>State-of-the-art text-to-speech model for 600+ languages, supporting Voice Clone and Voice Design.</p>
342
+ </div>
343
+ """)
344
+
345
+ with gr.Tabs():
346
+ # ==============================================================
347
+ # Voice Clone Tab
348
+ # ==============================================================
349
+ with gr.TabItem("Voice Clone"):
350
+ with gr.Row():
351
+ with gr.Column(scale=1):
352
+ # Added elem_id for JS hook
353
+ vc_text = gr.Textbox(label="Text to Synthesize", lines=4, placeholder="Enter the text to synthesize...", elem_id="vc_textbox")
354
+
355
+ # Tag Buttons for Voice Clone
356
+ with gr.Row(elem_classes=["tag-container"]):
357
+ for tag in EVENT_TAGS:
358
+ btn = gr.Button(tag, elem_classes=["tag-btn"])
359
+ btn.click(
360
+ fn=None,
361
+ inputs=[btn, vc_text],
362
+ outputs=vc_text,
363
+ js=INSERT_TAG_JS_VC
364
+ )
365
+
366
+ with gr.Row():
367
+ vc_lang = _lang_dropdown("Language (optional)")
368
+ vc_want_subs = gr.Checkbox(label="Want Subtitles ?", value=False)
369
+ vc_ref_audio = gr.Audio(label="Reference Audio (3–10 seconds audio)", type="filepath", elem_classes="compact-audio")
370
+
371
+ vc_ref_text = gr.Textbox(
372
+ label="Reference Text", lines=2,
373
+ placeholder="Auto-transcribed upon audio upload. You can manually edit it if Whisper gets it wrong."
374
+ )
375
+
376
+ vc_btn = gr.Button("Generate", variant="primary")
377
+ vc_ns, vc_gs, vc_dn, vc_sp, vc_du, vc_pp, vc_po = _gen_settings()
378
+
379
+ with gr.Column(scale=1):
380
+ vc_audio = gr.Audio(label="Output Audio", type="numpy")
381
+ vc_status = gr.Textbox(label="Status", lines=1)
382
+
383
+ with gr.Accordion("Download files", open=False):
384
+ vc_out_wav = gr.File(label="Generated Audio (WAV)")
385
+ vc_out_custom_srt = gr.File(label="Sentence Level SRT")
386
+ vc_out_word_srt = gr.File(label="Word Level SRT")
387
+ vc_out_shorts_srt = gr.File(label="Shorts SRT")
388
+
389
+ def _auto_transcribe(audio_path, lang):
390
+ if not audio_path:
391
+ return gr.update(value="")
392
+ try:
393
+ whisper_lang = lang if lang != "Auto" else None
394
+ whisper_results = subtitle_maker(audio_path, whisper_lang)
395
+ if whisper_results and len(whisper_results) > 7:
396
+ return gr.update(value=whisper_results[7])
397
+ except Exception as e:
398
+ logging.warning(f"Auto-transcription failed: {e}")
399
+ return gr.update(value="")
400
+
401
+ vc_ref_audio.change(
402
+ fn=_auto_transcribe,
403
+ inputs=[vc_ref_audio, vc_lang],
404
+ outputs=[vc_ref_text]
405
+ )
406
+
407
+ def _clone_fn(text, lang, ref_aud, ref_text, want_subs, ns, gs, dn, sp, du, pp, po):
408
+ res = _gen_core(text, lang, ref_aud, None, ns, gs, dn, sp, du, pp, po, mode="clone", ref_text=ref_text)
409
+ if res[0] is None:
410
+ return None, res[1], None, None, None, None
411
+
412
+ audio_tuple, status = res
413
+ sr, waveform = audio_tuple
414
+ # tmp_wav = tempfile.NamedTemporaryFile(suffix=".wav", delete=False).name
415
+ tmp_wav=tts_file_name(text, language=lang)
416
+ wavfile.write(tmp_wav, sr, waveform)
417
+
418
+ c_srt, w_srt, s_srt = generate_subtitles_if_needed(tmp_wav, lang, want_subs)
419
+
420
+ return audio_tuple, status, tmp_wav, c_srt, w_srt, s_srt
421
+
422
+ vc_btn.click(
423
+ _clone_fn,
424
+ inputs=[vc_text, vc_lang, vc_ref_audio, vc_ref_text, vc_want_subs, vc_ns, vc_gs, vc_dn, vc_sp, vc_du, vc_pp, vc_po],
425
+ outputs=[vc_audio, vc_status, vc_out_wav, vc_out_custom_srt, vc_out_word_srt, vc_out_shorts_srt],
426
+ )
427
+
428
+ # ==============================================================
429
+ # Voice Design Tab
430
+ # ==============================================================
431
+ with gr.TabItem("Voice Design"):
432
+ with gr.Row():
433
+ with gr.Column(scale=1):
434
+ # Added elem_id for JS hook
435
+ vd_text = gr.Textbox(label="Text to Synthesize", lines=4, placeholder="Enter the text to synthesize...", elem_id="vd_textbox")
436
+
437
+ # Tag Buttons for Voice Design
438
+ with gr.Row(elem_classes=["tag-container"]):
439
+ for tag in EVENT_TAGS:
440
+ btn = gr.Button(tag, elem_classes=["tag-btn"])
441
+ btn.click(
442
+ fn=None,
443
+ inputs=[btn, vd_text],
444
+ outputs=vd_text,
445
+ js=INSERT_TAG_JS_VD
446
+ )
447
+
448
+ with gr.Row():
449
+ vd_lang = _lang_dropdown(value='Auto')
450
+ vd_want_subs = gr.Checkbox(label="Want Subtitles ?", value=False)
451
+ vd_btn = gr.Button("Generate", variant="primary")
452
+ with gr.Accordion("Character Voice Design", open=False):
453
+ vd_groups = []
454
+ for _cat, _choices in _CATEGORIES.items():
455
+ default_val = "Auto"
456
+ if _cat == "Gender":
457
+ default_val = "Female"
458
+ elif _cat == "Age":
459
+ default_val = "Young Adult"
460
+
461
+ vd_groups.append(
462
+ gr.Dropdown(label=_cat, choices=["Auto"] + _choices, value=default_val, info=_ATTR_INFO.get(_cat))
463
+ )
464
+
465
+ vd_ns, vd_gs, vd_dn, vd_sp, vd_du, vd_pp, vd_po = _gen_settings()
466
+
467
+ with gr.Column(scale=1):
468
+ vd_audio = gr.Audio(label="Output Audio", type="numpy")
469
+ vd_status = gr.Textbox(label="Status", lines=1)
470
+
471
+ with gr.Accordion("Download files", open=False):
472
+ vd_out_wav = gr.File(label="Generated Audio (WAV)")
473
+ vd_out_custom_srt = gr.File(label="Sentence Level SRT")
474
+ vd_out_word_srt = gr.File(label="Word Level SRT")
475
+ vd_out_shorts_srt = gr.File(label="Shorts SRT")
476
+
477
+ def _build_instruct(groups):
478
+ selected = [g for g in groups if g and g != "Auto"]
479
+ if not selected: return None
480
+ return ", ".join([DIALECT_MAP.get(v, v) for v in selected])
481
+
482
+ def _design_fn(text, lang, want_subs, ns, gs, dn, sp, du, pp, po, *groups):
483
+ instruct = _build_instruct(groups)
484
+ res = _gen_core(text, lang, None, instruct, ns, gs, dn, sp, du, pp, po, mode="design")
485
+ if res[0] is None:
486
+ return None, res[1], None, None, None, None
487
+
488
+ audio_tuple, status = res
489
+ sr, waveform = audio_tuple
490
+ tmp_wav=tts_file_name(text, language=lang)
491
+ # tmp_wav = tempfile.NamedTemporaryFile(suffix=".wav", delete=False).name
492
+ wavfile.write(tmp_wav, sr, waveform)
493
+
494
+ c_srt, w_srt, s_srt = generate_subtitles_if_needed(tmp_wav, lang, want_subs)
495
+
496
+ return audio_tuple, status, tmp_wav, c_srt, w_srt, s_srt
497
+
498
+ vd_btn.click(
499
+ _design_fn,
500
+ inputs=[vd_text, vd_lang, vd_want_subs, vd_ns, vd_gs, vd_dn, vd_sp, vd_du, vd_pp, vd_po] + vd_groups,
501
+ outputs=[vd_audio, vd_status, vd_out_wav, vd_out_custom_srt, vd_out_word_srt, vd_out_shorts_srt],
502
+ )
503
+
504
+ if __name__ == "__main__":
505
+ demo.queue().launch(share=True, debug=True)