Spaces:
Running on Zero
Running on Zero
Update app.py
Browse files
app.py
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
| 1 |
import os
|
| 2 |
import sys
|
| 3 |
import tempfile
|
|
@@ -18,7 +21,6 @@ REPO_URL = "https://github.com/tuteishygpt/coqui-ai-TTS.git"
|
|
| 18 |
REPO_DIR = "coqui-ai-TTS"
|
| 19 |
|
| 20 |
if not os.path.exists(REPO_DIR):
|
| 21 |
-
# Клануем fork з беларускай падтрымкай
|
| 22 |
subprocess.run(
|
| 23 |
["git", "clone", REPO_URL, REPO_DIR],
|
| 24 |
check=True,
|
|
@@ -80,11 +82,19 @@ tokenizer = VoiceBpeTokenizer(vocab_file=vocab_file)
|
|
| 80 |
XTTS_MODEL.tokenizer = tokenizer
|
| 81 |
|
| 82 |
# ---------------------------------------------------------
|
| 83 |
-
# 4.
|
| 84 |
# ---------------------------------------------------------
|
| 85 |
@spaces.GPU(duration=60)
|
| 86 |
def text_to_speech(belarusian_story, speaker_audio_file=None):
|
| 87 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 88 |
raise gr.Error("Увядзі хоць нейкі тэкст 🙂")
|
| 89 |
|
| 90 |
# калі аўдыё не перададзена — бярэм голас па змаўчанні
|
|
@@ -104,12 +114,12 @@ def text_to_speech(belarusian_story, speaker_audio_file=None):
|
|
| 104 |
except Exception as e:
|
| 105 |
raise gr.Error(f"Памылка пры атрыманні латэнтаў голасу: {e}")
|
| 106 |
|
| 107 |
-
# ✅
|
| 108 |
try:
|
| 109 |
lang = "be"
|
| 110 |
chunk_limit = tokenizer.char_limits.get(lang, 250)
|
| 111 |
tts_texts = split_sentence(
|
| 112 |
-
belarusian_story.strip(),
|
| 113 |
lang=lang,
|
| 114 |
text_split_length=chunk_limit,
|
| 115 |
)
|
|
@@ -119,7 +129,10 @@ def text_to_speech(belarusian_story, speaker_audio_file=None):
|
|
| 119 |
except Exception as e:
|
| 120 |
raise gr.Error(f"Памылка пры падзеле тэксту на сказы: {e}")
|
| 121 |
|
| 122 |
-
|
|
|
|
|
|
|
|
|
|
| 123 |
for text in tqdm(tts_texts):
|
| 124 |
try:
|
| 125 |
with torch.no_grad():
|
|
@@ -134,58 +147,63 @@ def text_to_speech(belarusian_story, speaker_audio_file=None):
|
|
| 134 |
top_k=10,
|
| 135 |
top_p=0.3,
|
| 136 |
)
|
| 137 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 138 |
except Exception as e:
|
| 139 |
raise gr.Error(f"Памылка пры генерырацыі аўдыя: {e}")
|
| 140 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 141 |
try:
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
)
|
| 147 |
except Exception as e:
|
| 148 |
-
raise gr.Error(f"Памылка пры а
|
| 149 |
-
|
| 150 |
-
temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=".wav")
|
| 151 |
-
write(temp_file.name, sampling_rate, out_wav)
|
| 152 |
-
|
| 153 |
-
return temp_file.name
|
| 154 |
|
| 155 |
|
| 156 |
# ---------------------------------------------------------
|
| 157 |
-
# 5. Прыклады
|
| 158 |
# ---------------------------------------------------------
|
| 159 |
examples = [
|
| 160 |
[
|
| 161 |
"Такім чынам, клуб стаў уладальнікам усіх існых на сёння міжнародных трафеяў паўднёваамерыканскага футболу.",
|
| 162 |
"Nestarka.wav",
|
| 163 |
-
"krai.wav",
|
| 164 |
],
|
| 165 |
[
|
| 166 |
"Яму не ўдалося палепшыць фінансавае становішча каралеўства, а, наадварот, прыйшлося распрадаваць каштоўнасці чэшскай кароны.",
|
| 167 |
"muzh.wav",
|
| 168 |
-
"examples/цуды.wav",
|
| 169 |
],
|
| 170 |
[
|
| 171 |
"Кампілятарамі называюць праграмы, якія пераўтвараюць код вышэйшага ўзроўню ў код ніжэйшага ўзроўню.",
|
| 172 |
"chunk_100.wav",
|
| 173 |
-
"examples/надВозерам.wav",
|
| 174 |
],
|
| 175 |
[
|
| 176 |
"Акрамя таго, ліхачы аддаюць перавагу рэгі, хіп-хопу і класічнай музыцы.",
|
| 177 |
"d1015.mp3",
|
| 178 |
-
"examples/Беларусь.wav",
|
| 179 |
],
|
| 180 |
[
|
| 181 |
"Позірк можа быць уважлівым, зацікаўленым, захопленым, але бывае і нахабным, задзірлівым, пагардлівым, напышлівым.",
|
| 182 |
"donarka_ench.wav",
|
| 183 |
-
"examples/цуды.wav",
|
| 184 |
],
|
| 185 |
[
|
| 186 |
"Такі нават шчыры, ці што: родная мова народу – трасянка, а беларуская яму чужая!",
|
| 187 |
"muzhcynski.wav",
|
| 188 |
-
"examples/цуды.wav",
|
| 189 |
],
|
| 190 |
]
|
| 191 |
|
|
@@ -195,16 +213,17 @@ analytics_script = """
|
|
| 195 |
window.dataLayer = window.dataLayer || [];
|
| 196 |
function gtag(){dataLayer.push(arguments);}
|
| 197 |
gtag('js', new Date());
|
| 198 |
-
|
| 199 |
gtag('config', 'G-TKDCRCQ7FK');
|
| 200 |
</script>
|
| 201 |
"""
|
| 202 |
|
| 203 |
-
|
| 204 |
-
|
|
|
|
|
|
|
| 205 |
gr.HTML(analytics_script)
|
| 206 |
gr.Interface(
|
| 207 |
-
fn=text_to_speech,
|
| 208 |
inputs=[
|
| 209 |
gr.Textbox(lines=5, label="Тэкст на беларускай мове"),
|
| 210 |
gr.Audio(
|
|
@@ -214,33 +233,18 @@ with demo:
|
|
| 214 |
),
|
| 215 |
],
|
| 216 |
outputs=gr.Audio(
|
| 217 |
-
type="filepath",
|
| 218 |
-
label="Згенераванае аўдыя",
|
| 219 |
),
|
| 220 |
-
title="Belarusian TTS Demo",
|
| 221 |
description="""
|
| 222 |
-
<p>Увядзіце тэкст
|
| 223 |
-
голас па змаўчанн
|
| 224 |
-
а
|
| 225 |
-
|
| 226 |
-
<p><strong>Карысныя парады:</strong></p>
|
| 227 |
-
<ul>
|
| 228 |
-
<li>Выкарыстоўвайце прыклады з добрай якасцю, без іншых гукаў і разнастайнай інтанацыяй,
|
| 229 |
-
ад яе моцна залежыць вынік.</li>
|
| 230 |
-
<li>Інтанацыя таксама ўплывае на націскі.</li>
|
| 231 |
-
<li>Прыклады галасоў могуць быць на любой мове.</li>
|
| 232 |
-
</ul>
|
| 233 |
-
|
| 234 |
-
<p>Каб палепшыць якасць мадэлі (націскі і дакладнасць кланавання галасоў), патрэбны дадатковыя датасэты.
|
| 235 |
-
Ахвяруйце свой голас праз <a href="https://Donar.by" target="_blank">Donar.by</a></p>
|
| 236 |
-
|
| 237 |
-
<p>Далучайцеся да нашай беларускай суполкі ў ТГ, каб дапамагчы ці даведацца пра навіны ШІ:
|
| 238 |
-
<a href="https://t.me/SHibelChat" target="_blank">https://t.me/SHibelChat</a>.</p>
|
| 239 |
-
|
| 240 |
-
<p><strong>Падтрымаць праект:</strong> <a href="https://buymeacoffee.com/tuteishygpt" target="_blank">Buy Me a Coffee</a></p>
|
| 241 |
""",
|
| 242 |
examples=examples,
|
| 243 |
cache_examples=False,
|
|
|
|
| 244 |
)
|
| 245 |
|
| 246 |
if __name__ == "__main__":
|
|
|
|
| 1 |
+
# Калі запускаеце ў чыстым асяроддзі:
|
| 2 |
+
# !pip install -q gradio spaces huggingface_hub torch scipy tqdm gitpython
|
| 3 |
+
|
| 4 |
import os
|
| 5 |
import sys
|
| 6 |
import tempfile
|
|
|
|
| 21 |
REPO_DIR = "coqui-ai-TTS"
|
| 22 |
|
| 23 |
if not os.path.exists(REPO_DIR):
|
|
|
|
| 24 |
subprocess.run(
|
| 25 |
["git", "clone", REPO_URL, REPO_DIR],
|
| 26 |
check=True,
|
|
|
|
| 82 |
XTTS_MODEL.tokenizer = tokenizer
|
| 83 |
|
| 84 |
# ---------------------------------------------------------
|
| 85 |
+
# 4. Патокавая TTS-функцыя (генератар)
|
| 86 |
# ---------------------------------------------------------
|
| 87 |
@spaces.GPU(duration=60)
|
| 88 |
def text_to_speech(belarusian_story, speaker_audio_file=None):
|
| 89 |
+
"""
|
| 90 |
+
Streaming-выхад для gr.Audio:
|
| 91 |
+
- на кожным кроку вяртае (sample_rate, np.ndarray[float32]),
|
| 92 |
+
дзе масіў — гэта КАНКАТЭНАВАНЫ дагэтуль гукавы сігнал.
|
| 93 |
+
- у самым канцы дадаткова вяртаецца шлях да temp-файла з фінальным WAV,
|
| 94 |
+
каб карыстальнік мог яго спампаваць.
|
| 95 |
+
Gradio сам зразумее генератар і будзе абнаўляць аудыё па меры паступлення.
|
| 96 |
+
"""
|
| 97 |
+
if not belarusian_story or str(belarusian_story).strip() == "":
|
| 98 |
raise gr.Error("Увядзі хоць нейкі тэкст 🙂")
|
| 99 |
|
| 100 |
# калі аўдыё не перададзена — бярэм голас па змаўчанні
|
|
|
|
| 114 |
except Exception as e:
|
| 115 |
raise gr.Error(f"Памылка пры атрыманні латэнтаў голасу: {e}")
|
| 116 |
|
| 117 |
+
# ✅ Нарэзка тэксту на сказы/чанкі праз split_sentence
|
| 118 |
try:
|
| 119 |
lang = "be"
|
| 120 |
chunk_limit = tokenizer.char_limits.get(lang, 250)
|
| 121 |
tts_texts = split_sentence(
|
| 122 |
+
str(belarusian_story).strip(),
|
| 123 |
lang=lang,
|
| 124 |
text_split_length=chunk_limit,
|
| 125 |
)
|
|
|
|
| 129 |
except Exception as e:
|
| 130 |
raise gr.Error(f"Памылка пры падзеле тэксту на сказы: {e}")
|
| 131 |
|
| 132 |
+
# Будзем назапашваць гукавыя кавалкі
|
| 133 |
+
chunks = []
|
| 134 |
+
running_total = None # для хутчэйшага concat (лацінка: cumulative)
|
| 135 |
+
|
| 136 |
for text in tqdm(tts_texts):
|
| 137 |
try:
|
| 138 |
with torch.no_grad():
|
|
|
|
| 147 |
top_k=10,
|
| 148 |
top_p=0.3,
|
| 149 |
)
|
| 150 |
+
cur = wav_chunk["wav"].astype(np.float32)
|
| 151 |
+
chunks.append(cur)
|
| 152 |
+
|
| 153 |
+
# 🔴 Паступова абнаўляем агульны буфер.
|
| 154 |
+
# Каб не рабіць O(n^2) канкатэнацыю кожны раз, можна час ад часу rebalance,
|
| 155 |
+
# але для тыповых даўжынь будзе дастаткова:
|
| 156 |
+
if running_total is None:
|
| 157 |
+
running_total = cur
|
| 158 |
+
else:
|
| 159 |
+
running_total = np.concatenate([running_total, cur], axis=0)
|
| 160 |
+
|
| 161 |
+
# STREAM: вяртаем «тое, што маем ужо цяпер»
|
| 162 |
+
yield (sampling_rate, running_total)
|
| 163 |
+
|
| 164 |
except Exception as e:
|
| 165 |
raise gr.Error(f"Памылка пры генерырацыі аўдыя: {e}")
|
| 166 |
|
| 167 |
+
# Фінал: запішам у temp-файл і таксама вернем яго шлях (Gradio падхопіць апошняе значэнне)
|
| 168 |
+
if running_total is None:
|
| 169 |
+
raise gr.Error("Нічога не згенеравана. Праверце ўваходныя даныя.")
|
| 170 |
+
|
| 171 |
try:
|
| 172 |
+
temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=".wav")
|
| 173 |
+
write(temp_file.name, sampling_rate, running_total)
|
| 174 |
+
# Канчатковае вяртанне: можна вярнуць шлях — Gradio пакажа кнопку загрузкі.
|
| 175 |
+
yield temp_file.name
|
|
|
|
| 176 |
except Exception as e:
|
| 177 |
+
raise gr.Error(f"Памылка пры запісе фінальнага WAV: {e}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 178 |
|
| 179 |
|
| 180 |
# ---------------------------------------------------------
|
| 181 |
+
# 5. Прыклады (2 калоны: тэкст + файл голасу)
|
| 182 |
# ---------------------------------------------------------
|
| 183 |
examples = [
|
| 184 |
[
|
| 185 |
"Такім чынам, клуб стаў уладальнікам усіх існых на сёння міжнародных трафеяў паўднёваамерыканскага футболу.",
|
| 186 |
"Nestarka.wav",
|
|
|
|
| 187 |
],
|
| 188 |
[
|
| 189 |
"Яму не ўдалося палепшыць фінансавае становішча каралеўства, а, наадварот, прыйшлося распрадаваць каштоўнасці чэшскай кароны.",
|
| 190 |
"muzh.wav",
|
|
|
|
| 191 |
],
|
| 192 |
[
|
| 193 |
"Кампілятарамі называюць праграмы, якія пераўтвараюць код вышэйшага ўзроўню ў код ніжэйшага ўзроўню.",
|
| 194 |
"chunk_100.wav",
|
|
|
|
| 195 |
],
|
| 196 |
[
|
| 197 |
"Акрамя таго, ліхачы аддаюць перавагу рэгі, хіп-хопу і класічнай музыцы.",
|
| 198 |
"d1015.mp3",
|
|
|
|
| 199 |
],
|
| 200 |
[
|
| 201 |
"Позірк можа быць уважлівым, зацікаўленым, захопленым, але бывае і нахабным, задзірлівым, пагардлівым, напышлівым.",
|
| 202 |
"donarka_ench.wav",
|
|
|
|
| 203 |
],
|
| 204 |
[
|
| 205 |
"Такі нават шчыры, ці што: родная мова народу – трасянка, а беларуская яму чужая!",
|
| 206 |
"muzhcynski.wav",
|
|
|
|
| 207 |
],
|
| 208 |
]
|
| 209 |
|
|
|
|
| 213 |
window.dataLayer = window.dataLayer || [];
|
| 214 |
function gtag(){dataLayer.push(arguments);}
|
| 215 |
gtag('js', new Date());
|
|
|
|
| 216 |
gtag('config', 'G-TKDCRCQ7FK');
|
| 217 |
</script>
|
| 218 |
"""
|
| 219 |
|
| 220 |
+
# ---------------------------------------------------------
|
| 221 |
+
# 6. Gradio UI
|
| 222 |
+
# ---------------------------------------------------------
|
| 223 |
+
with gr.Blocks() as demo:
|
| 224 |
gr.HTML(analytics_script)
|
| 225 |
gr.Interface(
|
| 226 |
+
fn=text_to_speech, # генератар
|
| 227 |
inputs=[
|
| 228 |
gr.Textbox(lines=5, label="Тэкст на беларускай мове"),
|
| 229 |
gr.Audio(
|
|
|
|
| 233 |
),
|
| 234 |
],
|
| 235 |
outputs=gr.Audio(
|
| 236 |
+
type="filepath", # дазваляем і (sr, np.array), і канчатковы шлях
|
| 237 |
+
label="Згенераванае аўдыя (патокава)",
|
| 238 |
),
|
| 239 |
+
title="Belarusian TTS Demo — Streaming",
|
| 240 |
description="""
|
| 241 |
+
<p>Увядзіце тэкст — і аўдыя будзе адлюстроўвацца <b>па меры сінтэзу</b> (патокава).</p>
|
| 242 |
+
<p>Вы можаце выкарыстаць голас па змаўчанн��, абраць адзін з прыкладаў ці загрузіць уласны файл.</p>
|
| 243 |
+
<p><strong>Парады:</strong> выкарыстоўвайце чыстыя, разнастайныя па інтанацыі прыклады; інтанацыя ўплывае на націскі.</p>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 244 |
""",
|
| 245 |
examples=examples,
|
| 246 |
cache_examples=False,
|
| 247 |
+
allow_flagging="never",
|
| 248 |
)
|
| 249 |
|
| 250 |
if __name__ == "__main__":
|