archivartaunik commited on
Commit
6f6f86b
·
verified ·
1 Parent(s): 06ad7e9

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +523 -0
app.py ADDED
@@ -0,0 +1,523 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # app.py
2
+ import os
3
+ import sys
4
+ import subprocess
5
+ import logging
6
+
7
+ # каб лагі ішлі адразу ў stdout
8
+ os.environ.setdefault("PYTHONUNBUFFERED", "1")
9
+
10
+ import spaces
11
+ import gradio as gr
12
+ import torch
13
+ import numpy as np
14
+ from huggingface_hub import hf_hub_download
15
+
16
+ # -----------------------------
17
+ # Лагаванне толькі ў stdout
18
+ # -----------------------------
19
+ def _setup_logging():
20
+ handler = logging.StreamHandler(sys.stdout)
21
+ handler.setFormatter(logging.Formatter("%(asctime)s %(levelname)s %(message)s"))
22
+ logging.basicConfig(level=logging.INFO, handlers=[handler], force=True)
23
+ logging.captureWarnings(True)
24
+ logger = logging.getLogger("be-xtts-app")
25
+ logger.propagate = False
26
+ logger.setLevel(logging.INFO)
27
+ return logger
28
+
29
+ logger = _setup_logging()
30
+
31
+ # ---------------------------------------------------------
32
+ # 1) Клануем fork coqui-ai-TTS з падтрымкай беларускай
33
+ # ---------------------------------------------------------
34
+ REPO_URL = "https://github.com/tuteishygpt/coqui-ai-TTS.git"
35
+ REPO_DIR = "coqui-ai-TTS"
36
+
37
+ if not os.path.exists(REPO_DIR):
38
+ subprocess.run(["git", "clone", REPO_URL, REPO_DIR], check=True)
39
+
40
+ repo_root = os.path.abspath(REPO_DIR)
41
+ if repo_root not in sys.path:
42
+ sys.path.insert(0, repo_root)
43
+
44
+ # ---------------------------------------------------------
45
+ # 2) Імпарты з TTS
46
+ # ---------------------------------------------------------
47
+ from TTS.tts.configs.xtts_config import XttsConfig
48
+ from TTS.tts.models.xtts import Xtts
49
+ from TTS.tts.layers.xtts.tokenizer import (
50
+ split_sentence,
51
+ VoiceBpeTokenizer,
52
+ )
53
+
54
+ # ---------------------------------------------------------
55
+ # 3) Шляхі да файлаў мадэлі
56
+ # ---------------------------------------------------------
57
+ repo_id = "archivartaunik/BE_XTTS_V2_10ep250k"
58
+ model_dir = "./model"
59
+ os.makedirs(model_dir, exist_ok=True)
60
+
61
+ checkpoint_file = os.path.join(model_dir, "model.pth")
62
+ config_file = os.path.join(model_dir, "config.json")
63
+ vocab_file = os.path.join(model_dir, "vocab.json")
64
+ default_voice_file = os.path.join(model_dir, "voice.wav")
65
+ speakers_file = os.path.join(model_dir, "speakers_xtts.pth")
66
+
67
+ if not os.path.exists(checkpoint_file):
68
+ hf_hub_download(repo_id, filename="model.pth", local_dir=model_dir)
69
+ if not os.path.exists(config_file):
70
+ hf_hub_download(repo_id, filename="config.json", local_dir=model_dir)
71
+ if not os.path.exists(vocab_file):
72
+ hf_hub_download(repo_id, filename="vocab.json", local_dir=model_dir)
73
+ if not os.path.exists(default_voice_file):
74
+ hf_hub_download(repo_id, filename="voice.wav", local_dir=model_dir)
75
+ # новае: падцягваем speakers_xtts.pth
76
+ if not os.path.exists(speakers_file):
77
+ try:
78
+ hf_hub_download(repo_id, filename="speakers_xtts.pth", local_dir=model_dir)
79
+ except Exception as e:
80
+ logger.warning("Не атрымалася спампаваць speakers_xtts.pth: %s", e)
81
+
82
+ # ---------------------------------------------------------
83
+ # 4) Загрузка мадэлі і токенайзера
84
+ # ---------------------------------------------------------
85
+ config = XttsConfig()
86
+ config.load_json(config_file)
87
+
88
+ XTTS_MODEL = Xtts.init_from_config(config)
89
+ XTTS_MODEL.load_checkpoint(
90
+ config,
91
+ checkpoint_path=checkpoint_file,
92
+ vocab_path=vocab_file,
93
+ use_deepspeed=False,
94
+ )
95
+
96
+ device = "cuda:0" if torch.cuda.is_available() else "cpu"
97
+ XTTS_MODEL.to(device)
98
+ sampling_rate = int(XTTS_MODEL.config.audio["sample_rate"])
99
+
100
+ # Ініцыялізацыя VoiceBpeTokenizer і падкладанне ў мадэль
101
+ tokenizer = VoiceBpeTokenizer(vocab_file=vocab_file)
102
+ XTTS_MODEL.tokenizer = tokenizer
103
+
104
+ # Базавыя значэнні для кандыцыянавання
105
+ CFG_GPT_COND = int(getattr(XTTS_MODEL.config, "gpt_cond_len", 6))
106
+ CFG_MAX_REF = int(getattr(XTTS_MODEL.config, "max_ref_len", 20))
107
+ CFG_NORM = bool(getattr(XTTS_MODEL.config, "sound_norm_refs", True))
108
+
109
+ # ---------------------------------------------------------
110
+ # 4.1) Загрузка speakers_xtts.pth
111
+ # ---------------------------------------------------------
112
+ SPEAKERS_DB: dict[str, dict] = {}
113
+ SPEAKER_CHOICES: list[str] = ["— з аўдыё (reference) —"]
114
+
115
+ if os.path.exists(speakers_file):
116
+ try:
117
+ raw = torch.load(speakers_file, map_location="cpu")
118
+ # магчымыя фарматы:
119
+ # 1) {"speakers": {name: {...}}}
120
+ # 2) {name: {...}}
121
+ if isinstance(raw, dict) and "speakers" in raw and isinstance(raw["speakers"], dict):
122
+ speakers_dict = raw["speakers"]
123
+ else:
124
+ speakers_dict = raw
125
+
126
+ valid_count = 0
127
+ if isinstance(speakers_dict, dict):
128
+ for name, val in speakers_dict.items():
129
+ if (
130
+ isinstance(val, dict)
131
+ and "gpt_cond_latent" in val
132
+ and "speaker_embedding" in val
133
+ ):
134
+ SPEAKERS_DB[str(name)] = {
135
+ "gpt_cond_latent": val["gpt_cond_latent"],
136
+ "speaker_embedding": val["speaker_embedding"],
137
+ }
138
+ valid_count += 1
139
+
140
+ if valid_count > 0:
141
+ S_NAMES = sorted(SPEAKERS_DB.keys())
142
+ SPEAKER_CHOICES.extend(S_NAMES)
143
+ logger.info("Загружана %d галасоў з speakers_xtts.pth", valid_count)
144
+ else:
145
+ logger.warning(
146
+ "speakers_xtts.pth загружаны, але не знойдзена ніводнага "
147
+ "галасу з ключамі 'gpt_cond_latent' і 'speaker_embedding'."
148
+ )
149
+ except Exception as e:
150
+ logger.exception("Памылка пры загрузцы speakers_xtts.pth: %s", e)
151
+ else:
152
+ logger.warning("speakers_xtts.pth не знойдзены па шляху: %s", speakers_file)
153
+
154
+ # ---------------------------------------------------------
155
+ # Утыліты
156
+ # ---------------------------------------------------------
157
+ def clip_for_log(s: str, limit: int = 600):
158
+ s = (s or "").replace("\n", " ").strip()
159
+ return s if len(s) <= limit else s[:limit] + " ... [clipped]"
160
+
161
+ def log_after_chunk(idx: int, text: str, ui_logs: list):
162
+ line = f"[TEXT] chunk {idx}: AFTER :: {clip_for_log(text)}"
163
+ logger.info(line)
164
+ print(line, flush=True) # дублюем у stdout, гарантуем бачнасць
165
+ ui_logs.append(line)
166
+
167
+ # ---------------------------------------------------------
168
+ # 5) Функцыя TTS (лагі толькі AFTER)
169
+ # + падтрымка speakers_xtts.pth
170
+ # + асобнае аўдыё "Прыклад прасодыі" толькі для gpt_cond_latent
171
+ # ---------------------------------------------------------
172
+ @spaces.GPU(duration=60)
173
+ def text_to_speech(
174
+ belarusian_story: str,
175
+ speaker_audio_file: str | None,
176
+ prosody_audio_file: str | None, # НОВЫ ПАРАМЕТР
177
+ preset_speaker: str = "— з аўдыё (reference) —",
178
+ language: str = "be",
179
+ preprocess_text_flag: bool = True,
180
+ gpt_cond_len: int = CFG_GPT_COND,
181
+ max_ref_len: int = CFG_MAX_REF,
182
+ sound_norm_refs: bool = CFG_NORM,
183
+ temperature: float = 0.2,
184
+ length_penalty: float = 1.0,
185
+ repetition_penalty: float = 7.0,
186
+ top_k: int = 30,
187
+ top_p: float = 0.8,
188
+ ):
189
+ """
190
+ Вяртае: (sr, waveform), LOG_TEXT
191
+
192
+ Лагі ўключаюць ТОЛЬКІ радкі '[TEXT] chunk N: AFTER :: ...'
193
+
194
+ Параметр preset_speaker:
195
+ - калі значэнне з SPEAKERS_DB — бярэцца голас з speakers_xtts.pth
196
+ - калі '— з аўдыё (reference) —' — выкарыстоўваецца speaker_audio_file / voice.wav
197
+
198
+ Дадаткова:
199
+ - калі prosody_audio_file загружаны, ён выкарыстоўваецца ТОЛЬКІ для gpt_cond_latent
200
+ - speaker_embedding вызначаецца так, як рэалізавана раней:
201
+ * альбо з SPEAKERS_DB
202
+ * альбо з reference-аудыё (speaker_audio_file / voice.wav)
203
+ """
204
+ if not belarusian_story or belarusian_story.strip() == "":
205
+ raise gr.Error("Увядзі хоць нейкі тэкст 🙂")
206
+
207
+ lang_short = (language or "be").split("-")[0]
208
+ chunk_limit = tokenizer.char_limits.get(lang_short, 250)
209
+
210
+ # 1) падзел на чанкі
211
+ try:
212
+ tts_texts = split_sentence(
213
+ belarusian_story.strip(),
214
+ lang=lang_short,
215
+ text_split_length=chunk_limit,
216
+ )
217
+ tts_texts = [s.strip() for s in tts_texts if s and s.strip()]
218
+ if not tts_texts:
219
+ raise gr.Error("Не атрымалася падзяліць тэкст на сказы/чанкі.")
220
+ except Exception as e:
221
+ logger.exception("Памылка пры падзеле тэксту")
222
+ raise gr.Error(f"Памылка пры падзеле тэксту: {e}")
223
+
224
+ # 2) поўная апрацоўка (лагі толькі AFTER)
225
+ ui_logs = []
226
+ if preprocess_text_flag:
227
+ processed = []
228
+ for idx, s in enumerate(tts_texts, start=1):
229
+ tokenizer.check_input_length(s, lang_short) # можа вывесці WARN у stdout
230
+ s_proc = tokenizer.preprocess_text(s, lang_short)
231
+ log_after_chunk(idx, s_proc, ui_logs) # ЛОГ ТОЛЬКІ AFTER
232
+ processed.append(s_proc)
233
+ tts_texts = processed
234
+
235
+ # 3) атрыманне латэнтаў голасу:
236
+ # - speaker_embedding: "як зараз рэалізавана"
237
+ # - gpt_cond_latent: альбо з таго ж месца, альбо з prosody_audio_file
238
+ use_preset = (
239
+ isinstance(preset_speaker, str)
240
+ and preset_speaker in SPEAKERS_DB
241
+ )
242
+
243
+ gpt_cond_latent = None
244
+ speaker_embedding = None
245
+
246
+ # 3a) speaker_embedding (і, калі няма prosody_audio_file, gpt_cond_latent)
247
+ if use_preset:
248
+ # галасавы прэсэт з speakers_xtts.pth
249
+ try:
250
+ sp = SPEAKERS_DB[preset_speaker]
251
+ speaker_embedding = sp["speaker_embedding"].to(device)
252
+ if not prosody_audio_file:
253
+ # калі асобны прыклад прасодыі НЕ зададзены,
254
+ # то gpt_cond_latent таксама бярэм з прэсэта (як раней)
255
+ gpt_cond_latent = sp["gpt_cond_latent"].to(device)
256
+ except Exception as e:
257
+ logger.exception(
258
+ "Памылка пры выкарыстанні галасу '%s' з speakers_xtts.pth", preset_speaker
259
+ )
260
+ raise gr.Error(
261
+ f"Памылка пры выкарыстанні падрыхтаванага галасу '{preset_speaker}': {e}"
262
+ )
263
+ else:
264
+ # reference-аудыё (Прыклад голасу / voice.wav)
265
+ ref_path = speaker_audio_file
266
+ if not ref_path or (
267
+ not isinstance(ref_path, str)
268
+ and getattr(ref_path, "name", "") == ""
269
+ ):
270
+ ref_path = default_voice_file
271
+
272
+ try:
273
+ ref_gpt_cond_latent, ref_speaker_embedding = XTTS_MODEL.get_conditioning_latents(
274
+ audio_path=ref_path,
275
+ gpt_cond_len=int(gpt_cond_len),
276
+ max_ref_length=int(max_ref_len),
277
+ sound_norm_refs=bool(sound_norm_refs),
278
+ )
279
+ speaker_embedding = ref_speaker_embedding.to(device)
280
+ if not prosody_audio_file:
281
+ # калі асобны прыклад прасодыі НЕ зададзены,
282
+ # то gpt_cond_latent таксама бярэм з reference-аудыё (як раней)
283
+ gpt_cond_latent = ref_gpt_cond_latent.to(device)
284
+ except Exception as e:
285
+ logger.exception("Памылка пры атрыманні латэнтаў голасу з reference-аудыё")
286
+ raise gr.Error(f"Памылка пры атрыманні латэнтаў голасу: {e}")
287
+
288
+ # 3b) Калі загружаны "Прыклад прасодыі" — выкарыстоўваем яго ТОЛЬКІ для gpt_cond_latent
289
+ if prosody_audio_file:
290
+ prosody_path = prosody_audio_file
291
+ if not isinstance(prosody_path, str) and getattr(prosody_path, "name", ""):
292
+ prosody_path = prosody_path.name
293
+
294
+ try:
295
+ prosody_gpt_cond_latent, _ = XTTS_MODEL.get_conditioning_latents(
296
+ audio_path=prosody_path,
297
+ gpt_cond_len=int(gpt_cond_len),
298
+ max_ref_length=int(max_ref_len),
299
+ sound_norm_refs=bool(sound_norm_refs),
300
+ )
301
+ gpt_cond_latent = prosody_gpt_cond_latent.to(device)
302
+ except Exception as e:
303
+ logger.exception("Памылка пры атрыманні прасодыі з 'Прыклад прасодыі'")
304
+ raise gr.Error(f"Памылка пры атрыманні прасодыі з аўдыё: {e}")
305
+
306
+ # праверка, што абодва латэнты ёсць
307
+ if gpt_cond_latent is None or speaker_embedding is None:
308
+ raise gr.Error(
309
+ "Не атрымалася вызначыць gpt_cond_latent або speaker_embedding. "
310
+ "Праверце налады галасу і файлы аўдыё."
311
+ )
312
+
313
+ # 4) генерацыя
314
+ all_wavs = []
315
+ for text in tts_texts:
316
+ try:
317
+ with torch.no_grad():
318
+ wav_chunk = XTTS_MODEL.inference(
319
+ text=text,
320
+ language=lang_short,
321
+ gpt_cond_latent=gpt_cond_latent,
322
+ speaker_embedding=speaker_embedding,
323
+ temperature=float(temperature),
324
+ length_penalty=float(length_penalty),
325
+ repetition_penalty=float(repetition_penalty),
326
+ top_k=int(top_k),
327
+ top_p=float(top_p),
328
+ )
329
+ all_wavs.append(wav_chunk["wav"])
330
+ except Exception as e:
331
+ logger.exception("Памылка пры генерырацыі аўдыя")
332
+ raise gr.Error(f"Памылка пры генерырацыі аўдыя: {e}")
333
+
334
+ if not all_wavs:
335
+ raise gr.Error("Нічога не згенеравалася — праверце ўваходныя даныя.")
336
+
337
+ try:
338
+ out_wav = np.concatenate(all_wavs).astype(np.float32)
339
+ except Exception as e:
340
+ logger.exception("Памылка пры аб'яднанні аўдыя")
341
+ raise gr.Error(f"Памылка пры аб'яднанні аўдыя: {e}")
342
+
343
+ return (sampling_rate, out_wav), "\n".join(ui_logs)
344
+
345
+ # ---------------------------------------------------------
346
+ # 5.1) Прэв'ю выбранага галасу (кароткая фраза)
347
+ # ---------------------------------------------------------
348
+ @spaces.GPU(duration=30)
349
+ def preview_speaker(
350
+ preset_speaker: str = "— з аўдыё (reference) —",
351
+ language: str = "be",
352
+ ):
353
+ """
354
+ Генеруе кароткі прыклад для выбранага прэсэта/рефэрэнса.
355
+ Вяртае толькі (sr, waveform) без лагаў.
356
+ """
357
+ lang_short = (language or "be").split("-")[0]
358
+
359
+ sample_texts = {
360
+ "be": "Гэта прыклад беларускага голасу.",
361
+ "ru": "Это пример голоса.",
362
+ "uk": "Це приклад голосу.",
363
+ "pl": "To jest przykładowy głos.",
364
+ "en": "This is a sample voice.",
365
+ "de": "Dies ist eine Beispielstimme.",
366
+ "fr": "Ceci est une voix d'exemple.",
367
+ "es": "Esta es una voz de ejemplo.",
368
+ }
369
+ sample_text = sample_texts.get(lang_short, "This is a sample voice.")
370
+
371
+ # Выкарыстоўваем тыя ж налады, што і ў асноўнай функцыі
372
+ (audio, _logs) = text_to_speech(
373
+ belarusian_story=sample_text,
374
+ speaker_audio_file=None, # для прэсэта не трэба
375
+ prosody_audio_file=None, # асобны прыклад прасодыі для прэв'ю не патрабуецца
376
+ preset_speaker=preset_speaker,
377
+ language=language,
378
+ preprocess_text_flag=True,
379
+ gpt_cond_len=CFG_GPT_COND,
380
+ max_ref_len=CFG_MAX_REF,
381
+ sound_norm_refs=CFG_NORM,
382
+ temperature=0.2,
383
+ length_penalty=1.0,
384
+ repetition_penalty=7.0,
385
+ top_k=30,
386
+ top_p=0.8,
387
+ )
388
+ return audio
389
+
390
+ # ---------------------------------------------------------
391
+ # 6) UI (Gradio Blocks)
392
+ # ---------------------------------------------------------
393
+ with gr.Blocks() as demo:
394
+ gr.Markdown("# Belarusian TTS Demo (XTTSv2 + tokenizer.py) — лагі толькі AFTER")
395
+
396
+ # НОВЫ РАДОК: тэкст + прыклад голасу + прыклад прасодыі
397
+ with gr.Row():
398
+ txt = gr.Textbox(lines=8, label="Тэкст")
399
+ ref = gr.Audio(type="filepath", label="Прыклад голасу (≥7 с)")
400
+ prosody = gr.Audio(
401
+ type="filepath",
402
+ label="Прыклад прасодыі (≥7 с, неабавязкова)",
403
+ )
404
+
405
+ # выбар галасу з speakers_xtts.pth
406
+ speaker_dropdown = gr.Dropdown(
407
+ label="Падрыхтаваныя галасы (speakers_xtts.pth)",
408
+ choices=SPEAKER_CHOICES,
409
+ value=SPEAKER_CHOICES[0],
410
+ )
411
+
412
+ # Аўдыё для прэв'ю галасу
413
+ preview_audio = gr.Audio(type="numpy", label="Прэв'ю выбранага галасу")
414
+
415
+ # Кнопка для праслухоўвання выбранага прэсэта
416
+ preview_btn = gr.Button("▶️ Прайграць выбраны голас")
417
+
418
+ with gr.Row():
419
+ language = gr.Dropdown(
420
+ label="Мова (language)",
421
+ choices=[
422
+ "be","ru","uk","pl","cs","en","de","fr","es",
423
+ "it","pt","tr","vi","zh","ja","ko","nl","hu","ar","hi"
424
+ ],
425
+ value="be",
426
+ )
427
+ preprocess_text_flag = gr.Checkbox(
428
+ value=True,
429
+ label="Апрацоўваць тэкст праз tokenizer.py"
430
+ )
431
+
432
+ with gr.Accordion("Параметры кандыцыянавання (для reference-аудыё)", open=False):
433
+ with gr.Row():
434
+ gpt_cond_len = gr.Slider(
435
+ 1, max(1, CFG_GPT_COND*3),
436
+ step=1,
437
+ value=CFG_GPT_COND,
438
+ label="gpt_cond_len (сек.)"
439
+ )
440
+ max_ref_len = gr.Slider(
441
+ 1, max(1, CFG_MAX_REF*3),
442
+ step=1,
443
+ value=CFG_MAX_REF,
444
+ label="max_ref_len (сек.)"
445
+ )
446
+ sound_norm_refs = gr.Checkbox(
447
+ value=CFG_NORM,
448
+ label="sound_norm_refs"
449
+ )
450
+
451
+ with gr.Accordion("Параметры генерацыі", open=True):
452
+ with gr.Row():
453
+ temperature = gr.Slider(
454
+ 0.0, 2.0,
455
+ value=0.2,
456
+ step=0.01,
457
+ label="temperature"
458
+ )
459
+ top_k = gr.Slider(
460
+ 1, 100,
461
+ value=30,
462
+ step=1,
463
+ label="top_k"
464
+ )
465
+ with gr.Row():
466
+ top_p = gr.Slider(
467
+ 0.0, 1.0,
468
+ value=0.8,
469
+ step=0.01,
470
+ label="top_p"
471
+ )
472
+ length_penalty = gr.Slider(
473
+ 0.5, 3.5,
474
+ value=1.0,
475
+ step=0.05,
476
+ label="length_penalty"
477
+ )
478
+ repetition_penalty = gr.Slider(
479
+ 0.5, 20.0,
480
+ value=7.0,
481
+ step=0.1,
482
+ label="repetition_penalty"
483
+ )
484
+
485
+ out_audio = gr.Audio(type="numpy", label="Згенераванае аўдыя")
486
+ out_logs = gr.Textbox(lines=16, label="Лагі (толькі AFTER)")
487
+
488
+ btn = gr.Button("🔊 Генераваць")
489
+
490
+ # асноўная генерацыя
491
+ btn.click(
492
+ fn=text_to_speech,
493
+ inputs=[
494
+ txt,
495
+ ref,
496
+ prosody, # НОВЫ INPUT
497
+ speaker_dropdown,
498
+ language,
499
+ preprocess_text_flag,
500
+ gpt_cond_len,
501
+ max_ref_len,
502
+ sound_norm_refs,
503
+ temperature,
504
+ length_penalty,
505
+ repetition_penalty,
506
+ top_k,
507
+ top_p,
508
+ ],
509
+ outputs=[out_audio, out_logs],
510
+ )
511
+
512
+ # прэв'ю выбранага галасу
513
+ preview_btn.click(
514
+ fn=preview_speaker,
515
+ inputs=[speaker_dropdown, language],
516
+ outputs=preview_audio,
517
+ )
518
+
519
+ # ---------------------------------------------------------
520
+ # 7) Запуск
521
+ # ---------------------------------------------------------
522
+ if __name__ == "__main__":
523
+ demo.launch()