Spaces:
Runtime error
Runtime error
Update app.py
Browse files
app.py
CHANGED
@@ -2,10 +2,12 @@ import gradio as gr
|
|
2 |
import edge_tts
|
3 |
import tempfile
|
4 |
import asyncio
|
5 |
-
import traceback
|
|
|
|
|
6 |
|
7 |
-
# ---
|
8 |
-
#
|
9 |
language_dict = {
|
10 |
'English-Jenny (Female)': 'en-US-JennyNeural',
|
11 |
'English-Guy (Male)': 'en-US-GuyNeural',
|
@@ -297,7 +299,7 @@ language_dict = {
|
|
297 |
'Zulu (South Africa)-Themba- (Male)': 'zu-ZA-ThembaNeural',
|
298 |
}
|
299 |
|
300 |
-
# --- تابع اصلی
|
301 |
async def text_to_speech_edge_async(text, language_code, rate, volume, pitch):
|
302 |
try:
|
303 |
if not text:
|
@@ -305,17 +307,13 @@ async def text_to_speech_edge_async(text, language_code, rate, volume, pitch):
|
|
305 |
|
306 |
voice_id = language_dict.get(language_code)
|
307 |
if voice_id is None:
|
308 |
-
return f"خطا: مدل صدای انتخاب شده ('{language_code}')
|
309 |
|
310 |
-
|
311 |
-
# اگر مقدار 0 باشد، باید از فرمت "+0%" یا "-0%" استفاده شود.
|
312 |
-
# یا اینکه اگر کتابخانه اجازه می دهد، None یا رشته خالی ارسال کنیم.
|
313 |
-
# با توجه به مستندات edge-tts، باید همیشه با % یا Hz باشند.
|
314 |
-
rate_str = f"{int(rate):+g}%" # :+g علامت را برای صفر هم می گذارد
|
315 |
volume_str = f"{int(volume):+g}%"
|
316 |
pitch_str = f"{int(pitch):+g}Hz"
|
317 |
|
318 |
-
print(f"
|
319 |
|
320 |
communicate = edge_tts.Communicate(
|
321 |
text, voice_id, rate=rate_str, volume=volume_str, pitch=pitch_str
|
@@ -325,122 +323,250 @@ async def text_to_speech_edge_async(text, language_code, rate, volume, pitch):
|
|
325 |
tmp_path = tmp_file.name
|
326 |
|
327 |
await communicate.save(tmp_path)
|
328 |
-
print(f"TTS
|
329 |
-
return "تبدیل
|
330 |
|
331 |
except edge_tts.exceptions.NoAudioReceived:
|
332 |
-
error_msg = f"خطا:
|
333 |
-
print(f"NoAudioReceived: {error_msg}")
|
334 |
return error_msg, None
|
335 |
-
except ValueError as ve:
|
336 |
-
error_msg = f"خطا در پارامترهای ورودی
|
337 |
print(f"ValueError in edge-tts: {error_msg}")
|
338 |
traceback.print_exc()
|
339 |
return error_msg, None
|
340 |
except Exception as e:
|
341 |
-
print(f"
|
342 |
traceback.print_exc()
|
343 |
-
user_error_msg = f"
|
344 |
return user_error_msg, None
|
345 |
|
346 |
-
# --- Wrapper برای اجرای تابع
|
347 |
-
|
348 |
-
import threading
|
349 |
-
import os
|
350 |
-
|
351 |
-
_event_loops = {} # برای ذخیره event loop ها به ازای هر thread
|
352 |
|
353 |
-
def
|
354 |
thread_id = threading.get_ident()
|
355 |
-
if thread_id not in
|
356 |
-
loop
|
357 |
-
asyncio.
|
358 |
-
_event_loops[thread_id] = loop
|
359 |
-
# print(f"Created new event loop for thread {thread_id} (PID: {os.getpid()})")
|
360 |
# else:
|
361 |
-
# print(f"Reusing event loop for thread {thread_id}
|
362 |
-
return
|
363 |
-
|
364 |
|
365 |
def text_to_speech_edge_sync_wrapper(text, language_code, rate, volume, pitch):
|
366 |
-
# print(f"Sync wrapper called
|
367 |
try:
|
368 |
-
|
369 |
-
loop = get_or_create_event_loop_for_thread()
|
370 |
-
# اطمینان از اینکه این لوپ برای این thread به عنوان لوپ فعلی تنظیم شده
|
371 |
asyncio.set_event_loop(loop)
|
372 |
-
# اجرای کوروتین در لوپ
|
373 |
result = loop.run_until_complete(
|
374 |
text_to_speech_edge_async(text, language_code, rate, volume, pitch)
|
375 |
)
|
376 |
except RuntimeError as e:
|
377 |
-
print(f"RuntimeError in sync_wrapper (PID: {os.getpid()}, Thread: {threading.get_ident()}): {e}")
|
378 |
-
traceback.print_exc()
|
379 |
-
#
|
380 |
-
# این بخش شاید دیگر لازم نباشد با get_or_create_event_loop_for_thread
|
381 |
if "no current event loop" in str(e).lower() or "cannot be called from a running event loop" in str(e).lower():
|
382 |
-
print("Attempting with a completely new loop
|
383 |
new_loop = asyncio.new_event_loop()
|
384 |
-
asyncio.set_event_loop(new_loop)
|
385 |
try:
|
386 |
result = new_loop.run_until_complete(
|
387 |
text_to_speech_edge_async(text, language_code, rate, volume, pitch)
|
388 |
)
|
389 |
finally:
|
390 |
-
new_loop.close()
|
391 |
else:
|
392 |
-
return f"خطای
|
393 |
except Exception as e:
|
394 |
-
print(f"Unexpected Exception in sync_wrapper (PID: {os.getpid()}, Thread: {threading.get_ident()}):")
|
395 |
-
traceback.print_exc()
|
396 |
-
return f"خطای غیرمنتظره در
|
397 |
return result
|
398 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
399 |
|
400 |
-
#
|
401 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
402 |
default_voice_key = 'Persian (Iran)-Farid- (Male)'
|
403 |
if default_voice_key not in language_dict:
|
404 |
default_voice_key = list(language_dict.keys())[0] if language_dict else None
|
405 |
|
406 |
-
|
407 |
-
|
408 |
-
|
409 |
-
|
410 |
-
|
411 |
-
|
412 |
-
|
413 |
-
|
414 |
-
|
415 |
-
|
416 |
-
)
|
417 |
-
pitch = gr.Slider(
|
418 |
-
minimum=-50, maximum=50, step=1, value=0, label="زیر و بمی (Pitch)", info="مقدار بر حسب هرتز (Hz)"
|
419 |
-
)
|
420 |
|
421 |
-
|
422 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
423 |
|
|
|
424 |
|
425 |
-
|
426 |
-
|
427 |
-
|
428 |
-
|
429 |
-
|
430 |
-
|
431 |
-
|
432 |
-
|
433 |
-
|
434 |
-
|
435 |
-
|
436 |
-
|
437 |
-
|
438 |
-
|
439 |
-
|
440 |
-
|
441 |
-
|
442 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
443 |
|
444 |
-
#
|
445 |
-
|
446 |
-
iface.launch() # در HF Spaces فقط launch() کافی است
|
|
|
2 |
import edge_tts
|
3 |
import tempfile
|
4 |
import asyncio
|
5 |
+
import traceback
|
6 |
+
import threading
|
7 |
+
import os
|
8 |
|
9 |
+
# --- دیکشنری زبانها و صداها ---
|
10 |
+
# لطفاً مطمئن شوید این لیست کامل و صحیح است.
|
11 |
language_dict = {
|
12 |
'English-Jenny (Female)': 'en-US-JennyNeural',
|
13 |
'English-Guy (Male)': 'en-US-GuyNeural',
|
|
|
299 |
'Zulu (South Africa)-Themba- (Male)': 'zu-ZA-ThembaNeural',
|
300 |
}
|
301 |
|
302 |
+
# --- تابع اصلی تبدیل متن به گفتار (ناهمزمان) ---
|
303 |
async def text_to_speech_edge_async(text, language_code, rate, volume, pitch):
|
304 |
try:
|
305 |
if not text:
|
|
|
307 |
|
308 |
voice_id = language_dict.get(language_code)
|
309 |
if voice_id is None:
|
310 |
+
return f"خطا: مدل صدای انتخاب شده ('{language_code}') یافت نشد.", None
|
311 |
|
312 |
+
rate_str = f"{int(rate):+g}%"
|
|
|
|
|
|
|
|
|
313 |
volume_str = f"{int(volume):+g}%"
|
314 |
pitch_str = f"{int(pitch):+g}Hz"
|
315 |
|
316 |
+
# print(f"TTS Request: Voice='{voice_id}', Rate='{rate_str}', Volume='{volume_str}', Pitch='{pitch_str}'")
|
317 |
|
318 |
communicate = edge_tts.Communicate(
|
319 |
text, voice_id, rate=rate_str, volume=volume_str, pitch=pitch_str
|
|
|
323 |
tmp_path = tmp_file.name
|
324 |
|
325 |
await communicate.save(tmp_path)
|
326 |
+
# print(f"TTS Success. Audio: {tmp_path}")
|
327 |
+
return "تبدیل با موفقیت انجام شد.", tmp_path
|
328 |
|
329 |
except edge_tts.exceptions.NoAudioReceived:
|
330 |
+
error_msg = f"خطا: صدایی برای متن و صدای انتخاب شده دریافت نشد (صدا: {voice_id})."
|
331 |
+
print(f"NoAudioReceived Error: {error_msg}")
|
332 |
return error_msg, None
|
333 |
+
except ValueError as ve:
|
334 |
+
error_msg = f"خطا در پارامترهای ورودی edge-tts: {ve} (صدا: {voice_id}, نرخ: {rate_str}, حجم: {volume_str}, زیروبمی: {pitch_str})"
|
335 |
print(f"ValueError in edge-tts: {error_msg}")
|
336 |
traceback.print_exc()
|
337 |
return error_msg, None
|
338 |
except Exception as e:
|
339 |
+
print(f"Unexpected error in text_to_speech_edge_async (PID: {os.getpid()}, Thread: {threading.get_ident()}):")
|
340 |
traceback.print_exc()
|
341 |
+
user_error_msg = f"خطای غیرمنتظره در سرور: {type(e).__name__}"
|
342 |
return user_error_msg, None
|
343 |
|
344 |
+
# --- Wrapper برای اجرای تابع ناهمزمان در محیط همزمان Gradio ---
|
345 |
+
_event_loops_by_thread = {}
|
|
|
|
|
|
|
|
|
346 |
|
347 |
+
def _get_or_create_event_loop():
|
348 |
thread_id = threading.get_ident()
|
349 |
+
if thread_id not in _event_loops_by_thread or _event_loops_by_thread[thread_id].is_closed():
|
350 |
+
# print(f"Creating new event loop for thread {thread_id}")
|
351 |
+
_event_loops_by_thread[thread_id] = asyncio.new_event_loop()
|
|
|
|
|
352 |
# else:
|
353 |
+
# print(f"Reusing event loop for thread {thread_id}")
|
354 |
+
return _event_loops_by_thread[thread_id]
|
|
|
355 |
|
356 |
def text_to_speech_edge_sync_wrapper(text, language_code, rate, volume, pitch):
|
357 |
+
# print(f"Sync wrapper called by PID: {os.getpid()}, Thread: {threading.get_ident()}")
|
358 |
try:
|
359 |
+
loop = _get_or_create_event_loop()
|
|
|
|
|
360 |
asyncio.set_event_loop(loop)
|
|
|
361 |
result = loop.run_until_complete(
|
362 |
text_to_speech_edge_async(text, language_code, rate, volume, pitch)
|
363 |
)
|
364 |
except RuntimeError as e:
|
365 |
+
# print(f"RuntimeError in sync_wrapper (PID: {os.getpid()}, Thread: {threading.get_ident()}): {e}")
|
366 |
+
# traceback.print_exc()
|
367 |
+
# Fallback for "cannot be called from a running event loop" or similar issues
|
|
|
368 |
if "no current event loop" in str(e).lower() or "cannot be called from a running event loop" in str(e).lower():
|
369 |
+
# print("Fallback: Attempting with a completely new, temporary loop...")
|
370 |
new_loop = asyncio.new_event_loop()
|
371 |
+
asyncio.set_event_loop(new_loop)
|
372 |
try:
|
373 |
result = new_loop.run_until_complete(
|
374 |
text_to_speech_edge_async(text, language_code, rate, volume, pitch)
|
375 |
)
|
376 |
finally:
|
377 |
+
new_loop.close()
|
378 |
else:
|
379 |
+
return f"خطای اجرایی (RuntimeError): {e}", None
|
380 |
except Exception as e:
|
381 |
+
# print(f"Unexpected Exception in sync_wrapper (PID: {os.getpid()}, Thread: {threading.get_ident()}):")
|
382 |
+
# traceback.print_exc()
|
383 |
+
return f"خطای غیرمنتظره در Wrapper: {type(e).__name__}", None
|
384 |
return result
|
385 |
|
386 |
+
# --- تعریف تم و رابط کاربری با Gradio Blocks ---
|
387 |
+
# انتخاب یک تم زیبا
|
388 |
+
app_theme = gr.themes.Soft(
|
389 |
+
primary_hue=gr.themes.colors.blue,
|
390 |
+
secondary_hue=gr.themes.colors.sky,
|
391 |
+
neutral_hue=gr.themes.colors.slate,
|
392 |
+
font=[gr.themes.GoogleFont("Vazirmatn"), "Arial", "sans-serif"], # اضافه کردن فونت فارسی وزیرمتن
|
393 |
+
).set(
|
394 |
+
# اینجا میتوانید مقادیر بیشتری از تم را سفارشی کنید
|
395 |
+
# مثال: button_primary_background_fill="linear-gradient(to right, #0072ff, #00c6ff)",
|
396 |
+
# body_background_fill="#f0f9ff",
|
397 |
+
)
|
398 |
|
399 |
+
# CSS سفارشی برای انیمیشن ها و ظاهر بهتر
|
400 |
+
custom_css = """
|
401 |
+
body {
|
402 |
+
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); /* پس زمینه گرادیانت نرم */
|
403 |
+
overflow-x: hidden; /* جلوگیری از اسکرول افقی ناخواسته */
|
404 |
+
}
|
405 |
+
.gradio-container {
|
406 |
+
max-width: 900px !important; /* کمی عریض تر */
|
407 |
+
margin: 2rem auto !important;
|
408 |
+
border-radius: 20px !important;
|
409 |
+
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1) !important;
|
410 |
+
overflow: hidden; /* برای اینکه border-radius روی همه چیز اعمال شود */
|
411 |
+
background-color: white !important;
|
412 |
+
}
|
413 |
+
.app-title-container {
|
414 |
+
text-align: center;
|
415 |
+
padding: 20px 0;
|
416 |
+
background: linear-gradient(to right, #6a11cb 0%, #2575fc 100%); /* یک گرادیانت جذاب برای هدر */
|
417 |
+
border-top-left-radius: 20px;
|
418 |
+
border-top-right-radius: 20px;
|
419 |
+
margin-bottom: 15px; /* کمی فاصله از پایین هدر */
|
420 |
+
}
|
421 |
+
.app-title-container img {
|
422 |
+
width: 70px;
|
423 |
+
height: auto;
|
424 |
+
margin-bottom: 0px;
|
425 |
+
animation: float 3s ease-in-out infinite; /* انیمیشن شناور برای لوگو */
|
426 |
+
}
|
427 |
+
.app-title-container h1 {
|
428 |
+
color: white !important; /* رنگ متن عنوان */
|
429 |
+
font-size: 2.2em !important;
|
430 |
+
font-weight: 700 !important;
|
431 |
+
margin-top: 5px;
|
432 |
+
text-shadow: 1px 1px 3px rgba(0,0,0,0.2);
|
433 |
+
}
|
434 |
+
.app-title-container p {
|
435 |
+
color: #e0e0e0 !important; /* رنگ متن توضیحات زیر عنوان */
|
436 |
+
font-size: 1.1em !important;
|
437 |
+
margin-top: 5px;
|
438 |
+
}
|
439 |
+
|
440 |
+
.gr-button.lg.primary { /* استایل برای دکمه اصلی */
|
441 |
+
background: linear-gradient(to right, #0072ff, #00c6ff) !important;
|
442 |
+
color: white !important;
|
443 |
+
font-weight: bold !important;
|
444 |
+
border-radius: 10px !important;
|
445 |
+
padding: 12px 20px !important;
|
446 |
+
transition: transform 0.2s ease, box-shadow 0.2s ease !important;
|
447 |
+
box-shadow: 0 4px 6px rgba(0, 114, 255, 0.2) !important;
|
448 |
+
}
|
449 |
+
.gr-button.lg.primary:hover {
|
450 |
+
transform: translateY(-2px) !important;
|
451 |
+
box-shadow: 0 6px 12px rgba(0, 114, 255, 0.3) !important;
|
452 |
+
}
|
453 |
+
.gr-input, .gr-dropdown, .gr-textbox, .gr-slider { /* استایل برای اجزای ورودی */
|
454 |
+
border-radius: 8px !important;
|
455 |
+
border: 1px solid #dcdcdc !important;
|
456 |
+
transition: box-shadow 0.2s ease;
|
457 |
+
}
|
458 |
+
.gr-input:focus, .gr-dropdown:focus, .gr-textbox:focus {
|
459 |
+
box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25) !important;
|
460 |
+
border-color: #80bdff !important;
|
461 |
+
}
|
462 |
+
.gr-panel { /* برای بخش Accordion */
|
463 |
+
border-radius: 10px !important;
|
464 |
+
border: 1px solid #e0e0e0 !important;
|
465 |
+
background-color: #fafafa !important;
|
466 |
+
}
|
467 |
+
label span { /* برای برچسب ها */
|
468 |
+
font-weight: 500 !important;
|
469 |
+
color: #333 !important;
|
470 |
+
}
|
471 |
+
.footer-markdown a {
|
472 |
+
color: #007bff;
|
473 |
+
text-decoration: none;
|
474 |
+
}
|
475 |
+
.footer-markdown a:hover {
|
476 |
+
text-decoration: underline;
|
477 |
+
}
|
478 |
+
|
479 |
+
/* انیمیشن برای لوگو */
|
480 |
+
@keyframes float {
|
481 |
+
0% { transform: translatey(0px); }
|
482 |
+
50% { transform: translatey(-8px); }
|
483 |
+
100% { transform: translatey(0px); }
|
484 |
+
}
|
485 |
+
/* انیمیشن برای بارگذاری (می توان برای بخش وضعیت استفاده کرد) */
|
486 |
+
.status-processing {
|
487 |
+
animation: pulse 1.5s infinite;
|
488 |
+
}
|
489 |
+
@keyframes pulse {
|
490 |
+
0% { opacity: 1; }
|
491 |
+
50% { opacity: 0.5; }
|
492 |
+
100% { opacity: 1; }
|
493 |
+
}
|
494 |
+
"""
|
495 |
+
|
496 |
+
# انتخاب صدای پیش فرض (مطمئن شوید در language_dict وجود دارد)
|
497 |
default_voice_key = 'Persian (Iran)-Farid- (Male)'
|
498 |
if default_voice_key not in language_dict:
|
499 |
default_voice_key = list(language_dict.keys())[0] if language_dict else None
|
500 |
|
501 |
+
# ساخت رابط کاربری با Blocks
|
502 |
+
with gr.Blocks(theme=app_theme, css=custom_css) as demo:
|
503 |
+
# بخش عنوان با HTML و CSS سفارشی
|
504 |
+
with gr.Row():
|
505 |
+
gr.HTML("""
|
506 |
+
<div class="app-title-container">
|
507 |
+
<img src="https://raw.githubusercontent.com/gradio-app/gradio/main/guides/assets/gradio-logo.svg" alt="Edge TTS Logo">
|
508 |
+
<h1>مبدل متن به گفتار Edge-TTS</h1>
|
509 |
+
<p>کیفیت صدای طبیعی با قدرت مایکروسافت Edge</p>
|
510 |
+
</div>
|
511 |
+
""")
|
|
|
|
|
|
|
512 |
|
513 |
+
with gr.Row(equal_height=False): # equal_height=False برای کنترل بهتر ارتفاع ستون ها
|
514 |
+
with gr.Column(scale=3): # ستون ورودی ها
|
515 |
+
input_text = gr.Textbox(
|
516 |
+
lines=7,
|
517 |
+
label="📝 متن ورودی خود را اینجا بنویسید",
|
518 |
+
placeholder="مثال: سلام، به دنیای تبدیل متن به گفتار خوش آمدید...",
|
519 |
+
value="سلام، این یک نمونه متن برای آزمایش است." # مقدار پیش فرض برای تست
|
520 |
+
)
|
521 |
+
language = gr.Dropdown(
|
522 |
+
choices=list(language_dict.keys()),
|
523 |
+
value=default_voice_key,
|
524 |
+
label="🗣️ مدل و زبان صدا را انتخاب کنید"
|
525 |
+
)
|
526 |
+
with gr.Accordion("⚙️ تنظیمات پیشرفته صدا (اختیاری)", open=False, elem_classes="gr-panel"):
|
527 |
+
with gr.Row():
|
528 |
+
rate = gr.Slider(minimum=-100, maximum=100, step=1, value=0, label="💨 سرعت گفتار (%)", scale=1)
|
529 |
+
volume = gr.Slider(minimum=-100, maximum=100, step=1, value=0, label="🔊 حجم صدا (%)", scale=1)
|
530 |
+
pitch = gr.Slider(minimum=-50, maximum=50, step=1, value=0, label="🎶 زیر و بمی صدا (Hz)", scale=2) # scale=2 برای پهن تر شدن
|
531 |
|
532 |
+
submit_button = gr.Button("🎤 تبدیل به گفتار و پخش", variant="primary", icon="▶️")
|
533 |
|
534 |
+
with gr.Column(scale=2): # ستون خروجی ها
|
535 |
+
output_text_status = gr.Textbox(label="📊 وضعیت عملیات", interactive=False, lines=1)
|
536 |
+
output_audio = gr.Audio(type="filepath", label="🎧 فایل صوتی تولید شده", interactive=False)
|
537 |
+
gr.Markdown("---", css="margin-top: 20px; margin-bottom: 20px;")
|
538 |
+
gr.Markdown(
|
539 |
+
"""
|
540 |
+
<div class='footer-markdown' style='text-align: center; font-size: 0.9em; color: #555;'>
|
541 |
+
✨ توسعه داده شده با Gradio ✨
|
542 |
+
<br>
|
543 |
+
<a href='https://github.com/rany2/edge-tts' target='_blank' rel='noopener noreferrer'>مشاهده کتابخانه edge-tts در گیتهاب</a>
|
544 |
+
</div>
|
545 |
+
""",
|
546 |
+
elem_classes="footer-markdown"
|
547 |
+
)
|
548 |
+
|
549 |
+
# تعریف عملکرد دکمه
|
550 |
+
submit_button.click(
|
551 |
+
fn=text_to_speech_edge_sync_wrapper,
|
552 |
+
inputs=[input_text, language, rate, volume, pitch],
|
553 |
+
outputs=[output_text_status, output_audio],
|
554 |
+
# api_name="text_to_speech" # برای فراخوانی از طریق API (اختیاری)
|
555 |
+
)
|
556 |
+
|
557 |
+
# بخش مثال ها
|
558 |
+
gr.Examples(
|
559 |
+
examples=[
|
560 |
+
["به سرویس تبدیل متن به گفتار خوش آمدید. امیدوارم از کیفیت صدا لذت ببرید.", 'Persian (Iran)-Dilara- (Female)', +5, 0, -5],
|
561 |
+
["Gradio makes it easy to build and share machine learning apps.", 'English-Jenny (Female)', 0, +10, 0],
|
562 |
+
["こんにちは、これは音声合成のテストです。", 'Japanese-Nanami- (Female)', -10, 0, +5],
|
563 |
+
],
|
564 |
+
inputs=[input_text, language, rate, volume, pitch],
|
565 |
+
outputs=[output_text_status, output_audio], # خروجی ها باید با تابع اصلی مطابقت داشته باشند
|
566 |
+
fn=text_to_speech_edge_sync_wrapper, # تابع باید برای examples هم تعریف شود
|
567 |
+
cache_examples=True, # اگر می خواهید مثال ها کش شوند ( مطمئن شوید با مقادیر صفر مشکلی نیست)
|
568 |
+
label="💡 چند نمونه برای شروع"
|
569 |
+
)
|
570 |
|
571 |
+
# اجرای برنامه
|
572 |
+
demo.launch()
|
|