Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -78,7 +78,7 @@ XTTS_MODEL.tokenizer = tokenizer
|
|
| 78 |
# =========================================================
|
| 79 |
# 4) Streaming-міксін у стылі transformers-stream-generator
|
| 80 |
# =========================================================
|
| 81 |
-
MIN_BUFFER_S = 0.06 # 60 мс
|
| 82 |
FADE_S = 0.008
|
| 83 |
TOKENS_PER_STEP = 4
|
| 84 |
|
|
@@ -271,12 +271,10 @@ def text_to_speech(belarusian_story, speaker_audio_file=None):
|
|
| 271 |
|
| 272 |
full_audio_chunks: List[np.ndarray] = []
|
| 273 |
|
| 274 |
-
# 1) струменім невялікімі буферамі — толькі ў stream_pipe
|
| 275 |
for buf in _chunker(gen, sampling_rate, MIN_BUFFER_S):
|
| 276 |
full_audio_chunks.append(buf)
|
| 277 |
yield (_pcm_f32_to_b64(buf), None)
|
| 278 |
|
| 279 |
-
# 2) фінал: запіс у WAV і STOP-сігнал для кліента
|
| 280 |
if not full_audio_chunks:
|
| 281 |
yield ("__STOP__", None)
|
| 282 |
return
|
|
@@ -290,7 +288,7 @@ def text_to_speech(belarusian_story, speaker_audio_file=None):
|
|
| 290 |
raise gr.Error(f"Памылка пры запісе фінальнага WAV: {e}")
|
| 291 |
|
| 292 |
# ---------------------------------------------------------
|
| 293 |
-
# 7) UI:
|
| 294 |
# ---------------------------------------------------------
|
| 295 |
examples = [
|
| 296 |
["Прывітанне! Гэта праверка жывога струменя беларускага TTS.", "Nestarka.wav"],
|
|
@@ -303,28 +301,27 @@ with gr.Blocks() as demo:
|
|
| 303 |
inp_text = gr.Textbox(lines=5, label="Тэкст на беларускай мове")
|
| 304 |
inp_voice = gr.Audio(type="filepath", label="Прыклад голасу (7+ сек)", interactive=True)
|
| 305 |
|
|
|
|
| 306 |
with gr.Row():
|
| 307 |
play_btn = gr.Button("▶️ Play")
|
| 308 |
stop_btn = gr.Button("⏹ Stop")
|
| 309 |
-
gr.Markdown(
|
| 310 |
-
f"**Sample rate:** {sampling_rate} Hz • Націсні *Play* перад генерацыяй або падчас — для запуску аўдыя."
|
| 311 |
-
)
|
| 312 |
|
| 313 |
-
# Схаваны канал для стриму base64-чанкаў
|
| 314 |
stream_pipe = gr.Textbox(value="", visible=False, label="stream_pipe")
|
| 315 |
final_file = gr.Audio(type="filepath", label="Згенераванае аўдыя (фінальны файл)", autoplay=False)
|
| 316 |
|
| 317 |
run_btn = gr.Button("Згенераваць")
|
| 318 |
|
| 319 |
-
# --- JS
|
| 320 |
-
|
| 321 |
() => {{
|
| 322 |
const sampleRate = {sampling_rate};
|
| 323 |
const AC = window.AudioContext || window.webkitAudioContext;
|
| 324 |
if (!AC) return;
|
| 325 |
if (!window.__wa) {{
|
| 326 |
const ctx = new AC({{ sampleRate }});
|
| 327 |
-
const bufferSize = 2048;
|
| 328 |
const node = ctx.createScriptProcessor(bufferSize, 0, 1);
|
| 329 |
let queue = [];
|
| 330 |
let playing = false;
|
|
@@ -351,21 +348,21 @@ with gr.Blocks() as demo:
|
|
| 351 |
stop: () => {{ playing = false; }},
|
| 352 |
reset: () => {{ playing = false; queue = []; }},
|
| 353 |
}};
|
|
|
|
|
|
|
| 354 |
}}
|
| 355 |
-
window.__wa.start();
|
| 356 |
}}
|
| 357 |
"""
|
| 358 |
|
| 359 |
STOP_JS = "() => { if (window.__wa) window.__wa.stop(); }"
|
|
|
|
| 360 |
|
| 361 |
-
|
| 362 |
-
|
| 363 |
-
# Base64 -> Float32 + push/stop
|
| 364 |
PUSH_JS = """
|
| 365 |
(b64) => {
|
| 366 |
if (!window.__wa || !b64) return;
|
| 367 |
if (b64 === "__STOP__") { window.__wa.stop(); return; }
|
| 368 |
-
// b64 PCM Float32 -> Float32Array
|
| 369 |
const bin = atob(b64);
|
| 370 |
const len = bin.length;
|
| 371 |
const buf = new ArrayBuffer(len);
|
|
@@ -376,28 +373,28 @@ with gr.Blocks() as demo:
|
|
| 376 |
}
|
| 377 |
"""
|
| 378 |
|
| 379 |
-
#
|
| 380 |
play_btn.click(fn=None, inputs=[], outputs=[], js=PLAY_JS)
|
| 381 |
stop_btn.click(fn=None, inputs=[], outputs=[], js=STOP_JS)
|
| 382 |
|
| 383 |
-
#
|
| 384 |
-
run_btn.click(fn=None, inputs=[], outputs=[], js=
|
| 385 |
|
| 386 |
-
# С
|
| 387 |
run_btn.click(
|
| 388 |
fn=text_to_speech,
|
| 389 |
inputs=[inp_text, inp_voice],
|
| 390 |
outputs=[stream_pipe, final_file]
|
| 391 |
)
|
| 392 |
|
| 393 |
-
#
|
| 394 |
stream_pipe.change(fn=None, inputs=[stream_pipe], outputs=[], js=PUSH_JS)
|
| 395 |
|
|
|
|
| 396 |
gr.Examples(
|
| 397 |
examples=examples,
|
| 398 |
inputs=[inp_text, inp_voice],
|
| 399 |
-
|
| 400 |
-
fn=text_to_speech,
|
| 401 |
cache_examples=False,
|
| 402 |
)
|
| 403 |
|
|
|
|
| 78 |
# =========================================================
|
| 79 |
# 4) Streaming-міксін у стылі transformers-stream-generator
|
| 80 |
# =========================================================
|
| 81 |
+
MIN_BUFFER_S = 0.06 # ~60 мс для гладкага плыннага прайгравання
|
| 82 |
FADE_S = 0.008
|
| 83 |
TOKENS_PER_STEP = 4
|
| 84 |
|
|
|
|
| 271 |
|
| 272 |
full_audio_chunks: List[np.ndarray] = []
|
| 273 |
|
|
|
|
| 274 |
for buf in _chunker(gen, sampling_rate, MIN_BUFFER_S):
|
| 275 |
full_audio_chunks.append(buf)
|
| 276 |
yield (_pcm_f32_to_b64(buf), None)
|
| 277 |
|
|
|
|
| 278 |
if not full_audio_chunks:
|
| 279 |
yield ("__STOP__", None)
|
| 280 |
return
|
|
|
|
| 288 |
raise gr.Error(f"Памылка пры запісе фінальнага WAV: {e}")
|
| 289 |
|
| 290 |
# ---------------------------------------------------------
|
| 291 |
+
# 7) UI: аўта-Play пры "Згенераваць" (ініт + reset + start у адным JS)
|
| 292 |
# ---------------------------------------------------------
|
| 293 |
examples = [
|
| 294 |
["Прывітанне! Гэта праверка жывога струменя беларускага TTS.", "Nestarka.wav"],
|
|
|
|
| 301 |
inp_text = gr.Textbox(lines=5, label="Тэкст на беларускай мове")
|
| 302 |
inp_voice = gr.Audio(type="filepath", label="Прыклад голасу (7+ сек)", interactive=True)
|
| 303 |
|
| 304 |
+
# Кастомныя кнопкі (Play/Stop пакідаю на ўсялякі выпадак)
|
| 305 |
with gr.Row():
|
| 306 |
play_btn = gr.Button("▶️ Play")
|
| 307 |
stop_btn = gr.Button("⏹ Stop")
|
| 308 |
+
gr.Markdown(f"**Sample rate:** {sampling_rate} Hz")
|
|
|
|
|
|
|
| 309 |
|
| 310 |
+
# Схаваны канал для стриму base64-чанкаў і фінальны файл
|
| 311 |
stream_pipe = gr.Textbox(value="", visible=False, label="stream_pipe")
|
| 312 |
final_file = gr.Audio(type="filepath", label="Згенераванае аўдыя (фінальны файл)", autoplay=False)
|
| 313 |
|
| 314 |
run_btn = gr.Button("Згенераваць")
|
| 315 |
|
| 316 |
+
# --- JS: ініт + reset + start (аўтаматычны Play на кнопку Згенераваць) ---
|
| 317 |
+
INIT_RESET_AND_PLAY_JS = f"""
|
| 318 |
() => {{
|
| 319 |
const sampleRate = {sampling_rate};
|
| 320 |
const AC = window.AudioContext || window.webkitAudioContext;
|
| 321 |
if (!AC) return;
|
| 322 |
if (!window.__wa) {{
|
| 323 |
const ctx = new AC({{ sampleRate }});
|
| 324 |
+
const bufferSize = 2048;
|
| 325 |
const node = ctx.createScriptProcessor(bufferSize, 0, 1);
|
| 326 |
let queue = [];
|
| 327 |
let playing = false;
|
|
|
|
| 348 |
stop: () => {{ playing = false; }},
|
| 349 |
reset: () => {{ playing = false; queue = []; }},
|
| 350 |
}};
|
| 351 |
+
}} else {{
|
| 352 |
+
window.__wa.reset();
|
| 353 |
}}
|
| 354 |
+
window.__wa.start(); // Аўта-Play
|
| 355 |
}}
|
| 356 |
"""
|
| 357 |
|
| 358 |
STOP_JS = "() => { if (window.__wa) window.__wa.stop(); }"
|
| 359 |
+
PLAY_JS = "() => { if (window.__wa) window.__wa.start(); }"
|
| 360 |
|
| 361 |
+
# Base64 -> Float32 + push/stop у канцы
|
|
|
|
|
|
|
| 362 |
PUSH_JS = """
|
| 363 |
(b64) => {
|
| 364 |
if (!window.__wa || !b64) return;
|
| 365 |
if (b64 === "__STOP__") { window.__wa.stop(); return; }
|
|
|
|
| 366 |
const bin = atob(b64);
|
| 367 |
const len = bin.length;
|
| 368 |
const buf = new ArrayBuffer(len);
|
|
|
|
| 373 |
}
|
| 374 |
"""
|
| 375 |
|
| 376 |
+
# Ручныя кнопкі
|
| 377 |
play_btn.click(fn=None, inputs=[], outputs=[], js=PLAY_JS)
|
| 378 |
stop_btn.click(fn=None, inputs=[], outputs=[], js=STOP_JS)
|
| 379 |
|
| 380 |
+
# Аўта-ініт+reset+play ПЕРАД стартам сервэрнай генерацыі
|
| 381 |
+
run_btn.click(fn=None, inputs=[], outputs=[], js=INIT_RESET_AND_PLAY_JS)
|
| 382 |
|
| 383 |
+
# Стрымінг (сервер)
|
| 384 |
run_btn.click(
|
| 385 |
fn=text_to_speech,
|
| 386 |
inputs=[inp_text, inp_voice],
|
| 387 |
outputs=[stream_pipe, final_file]
|
| 388 |
)
|
| 389 |
|
| 390 |
+
# Пуш чанкаў у WebAudio пры кожным абнаўленні схаванага канала
|
| 391 |
stream_pipe.change(fn=None, inputs=[stream_pipe], outputs=[], js=PUSH_JS)
|
| 392 |
|
| 393 |
+
# Прыклады: толькі запаўняем палі (не запускаем), каб аўта-Play быў праз «Згенераваць»
|
| 394 |
gr.Examples(
|
| 395 |
examples=examples,
|
| 396 |
inputs=[inp_text, inp_voice],
|
| 397 |
+
fn=None,
|
|
|
|
| 398 |
cache_examples=False,
|
| 399 |
)
|
| 400 |
|