Spaces:
Runtime error
Runtime error
Update app.py
Browse files
app.py
CHANGED
|
@@ -6,62 +6,54 @@ import traceback
|
|
| 6 |
import threading
|
| 7 |
import os
|
| 8 |
|
| 9 |
-
# --- دیکشنری زبانها و صداها با
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
'
|
| 15 |
-
'
|
| 16 |
-
'
|
| 17 |
-
'
|
| 18 |
-
'
|
| 19 |
-
|
| 20 |
-
'
|
| 21 |
-
'
|
| 22 |
-
|
| 23 |
-
'
|
| 24 |
-
'
|
| 25 |
-
|
| 26 |
-
'
|
| 27 |
-
'
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 31 |
}
|
| 32 |
|
| 33 |
-
#
|
| 34 |
-
|
| 35 |
-
language_display_dict = {}
|
| 36 |
-
for display_key_english, (voice_id, gender_tag) in original_language_dict.items():
|
| 37 |
-
# استخراج زبان و نام از کلید انگلیسی
|
| 38 |
-
lang_name_part = display_key_english.split('-')[0]
|
| 39 |
-
model_name_part = display_key_english.split('-', 1)[1] if '-' in display_key_english else display_key_english
|
| 40 |
-
|
| 41 |
-
persian_lang_name = lang_name_part # به طور پیش فرض، اگر ترجمه نداشتیم
|
| 42 |
-
if "English" in lang_name_part:
|
| 43 |
-
persian_lang_name = "انگلیسی"
|
| 44 |
-
elif "Spanish (Mexican)" in lang_name_part:
|
| 45 |
-
persian_lang_name = "اسپانیایی (مکزیک)"
|
| 46 |
-
elif "Korean" in lang_name_part:
|
| 47 |
-
persian_lang_name = "کرهای"
|
| 48 |
-
elif "Persian (Iran)" in lang_name_part:
|
| 49 |
-
persian_lang_name = "فارسی (ایران)"
|
| 50 |
-
# ... اضافه کردن ترجمه برای سایر زبان ها ...
|
| 51 |
-
|
| 52 |
-
new_display_key = f"{persian_lang_name} - {model_name_part}"
|
| 53 |
-
language_display_dict[new_display_key] = {'id': voice_id, 'gender': gender_tag.lower()}
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
# --- توابع تبدیل متن به گفتار و wrapper (همانند قبل، اما از language_display_dict استفاده می کند) ---
|
| 57 |
-
async def text_to_speech_edge_async(text, language_display_key, rate, volume, pitch): # ورودی به language_display_key تغییر کرد
|
| 58 |
try:
|
| 59 |
if not text: return "خطا: لطفاً متنی را برای تبدیل وارد کنید.", None
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
if
|
| 63 |
-
voice_id = voice_info['id']
|
| 64 |
-
|
| 65 |
rate_str, volume_str, pitch_str = f"{int(rate):+g}%", f"{int(volume):+g}%", f"{int(pitch):+g}Hz"
|
| 66 |
communicate = edge_tts.Communicate(text, voice_id, rate=rate_str, volume=volume_str, pitch=pitch_str)
|
| 67 |
with tempfile.NamedTemporaryFile(delete=False, suffix=".wav") as tmp_file: tmp_path = tmp_file.name
|
|
@@ -83,20 +75,20 @@ def _get_or_create_event_loop():
|
|
| 83 |
_event_loops_by_thread[thread_id] = asyncio.new_event_loop()
|
| 84 |
return _event_loops_by_thread[thread_id]
|
| 85 |
|
| 86 |
-
def text_to_speech_edge_sync_wrapper(text,
|
| 87 |
try:
|
| 88 |
loop = _get_or_create_event_loop(); asyncio.set_event_loop(loop)
|
| 89 |
-
result = loop.run_until_complete(text_to_speech_edge_async(text,
|
| 90 |
except RuntimeError as e:
|
| 91 |
if "no current event loop" in str(e).lower() or "cannot be called from a running event loop" in str(e).lower():
|
| 92 |
new_loop = asyncio.new_event_loop(); asyncio.set_event_loop(new_loop)
|
| 93 |
-
try: result = new_loop.run_until_complete(text_to_speech_edge_async(text,
|
| 94 |
finally: new_loop.close()
|
| 95 |
else: return f"خطای اجرایی: {e}", None
|
| 96 |
except Exception as e: return f"خطای غیرمنتظره: {type(e).__name__}", None
|
| 97 |
return result
|
| 98 |
|
| 99 |
-
# --- تعریف تم و CSS (
|
| 100 |
app_theme = gr.themes.Soft(
|
| 101 |
primary_hue=gr.themes.colors.blue,
|
| 102 |
secondary_hue=gr.themes.colors.sky,
|
|
@@ -138,13 +130,10 @@ body { font-family: 'Vazirmatn', 'Arial', sans-serif; direction: rtl; }
|
|
| 138 |
background: #2980b9 !important; transform: translateY(-2px) !important;
|
| 139 |
box-shadow: 0 5px 10px rgba(52, 152, 219, 0.35) !important;
|
| 140 |
}
|
| 141 |
-
.gr-input, .gr-dropdown, .gr-textbox, .gr-slider
|
| 142 |
border-radius: 8px !important; border: 1px solid #ced4da !important;
|
| 143 |
font-size: 0.95em !important;
|
| 144 |
}
|
| 145 |
-
.gr-radio label input { margin-left: 5px; margin-right: 2px; } /* تنظیم فاصله دکمه های رادیو */
|
| 146 |
-
.gr-radio { padding: 5px; border:none !important; } /* حذف حاشیه اضافی از رادیو */
|
| 147 |
-
|
| 148 |
.gr-input:focus, .gr-dropdown:focus, .gr-textbox:focus {
|
| 149 |
box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.15) !important;
|
| 150 |
border-color: #5dade2 !important;
|
|
@@ -153,7 +142,7 @@ body { font-family: 'Vazirmatn', 'Arial', sans-serif; direction: rtl; }
|
|
| 153 |
border-radius: 10px !important; border: 1px solid #e9ecef !important;
|
| 154 |
background-color: #f8f9fa !important; padding: 0.75rem !important;
|
| 155 |
}
|
| 156 |
-
label > span {
|
| 157 |
font-weight: 500 !important; color: #495057 !important; font-size: 0.9em !important;
|
| 158 |
margin-bottom: 3px !important; display: block;
|
| 159 |
}
|
|
@@ -182,38 +171,17 @@ button[title="Flag"], button[aria-label="Flag"] {display: none !important; }
|
|
| 182 |
.main-content-row > .gr-column:nth-child(1) { flex-basis: 60%; }
|
| 183 |
.main-content-row > .gr-column:nth-child(2) { flex-basis: 40%; }
|
| 184 |
.gr-button.lg.primary { width: auto !important; }
|
| 185 |
-
.gr-radio { display: flex; justify-content: center; gap: 15px; margin-bottom: 10px;} /* چیدمان بهتر رادیو در دسکتاپ */
|
| 186 |
-
.gr-radio label { margin-bottom: 0 !important; } /* جلوگیری از پرش برچسب رادیو */
|
| 187 |
-
|
| 188 |
}
|
| 189 |
"""
|
| 190 |
|
| 191 |
-
# ---
|
| 192 |
-
|
| 193 |
-
|
| 194 |
-
|
| 195 |
-
|
| 196 |
-
|
| 197 |
-
else: # "مرد" یا "زن"
|
| 198 |
-
for display_key, voice_info in language_display_dict.items():
|
| 199 |
-
if voice_info['gender'] == gender_filter:
|
| 200 |
-
filtered_choices.append(display_key)
|
| 201 |
|
| 202 |
-
|
| 203 |
-
first_choice = filtered_choices[0]
|
| 204 |
-
# برگرداندن آپدیت برای Dropdown
|
| 205 |
-
return gr.Dropdown(choices=filtered_choices, value=first_choice, label="🗣️ زبان و مدل صدا (فیلتر شده)")
|
| 206 |
-
|
| 207 |
-
|
| 208 |
-
default_voice_key_display = "فارسی (ایران) - Farid- (Male)" # نام نمایشی جدید برای صدای پیش فرض
|
| 209 |
-
if default_voice_key_display not in language_display_dict:
|
| 210 |
-
# اگر صدای پیش فرض فارسی در لیست نبود (مثلا به خاطر فیلتر اولیه)، اولین مورد موجود را انتخاب کن
|
| 211 |
-
if list(language_display_dict.keys()):
|
| 212 |
-
default_voice_key_display = list(language_display_dict.keys())[0]
|
| 213 |
-
else:
|
| 214 |
-
default_voice_key_display = None # اگر لیست کلا خالی است
|
| 215 |
-
|
| 216 |
-
LOGO_URL = "https://raw.githubusercontent.com/gradio-app/gradio/main/guides/assets/gradio-logo.svg"
|
| 217 |
|
| 218 |
with gr.Blocks(theme=app_theme, css=custom_css) as demo:
|
| 219 |
with gr.Row():
|
|
@@ -225,14 +193,6 @@ with gr.Blocks(theme=app_theme, css=custom_css) as demo:
|
|
| 225 |
</div>
|
| 226 |
""")
|
| 227 |
|
| 228 |
-
# --- دکمه های رادیویی برای انتخاب جنسیت ---
|
| 229 |
-
gender_radio = gr.Radio(
|
| 230 |
-
choices=["همه", "مرد", "زن"],
|
| 231 |
-
value="همه",
|
| 232 |
-
label="انتخاب جنسیت صدا",
|
| 233 |
-
elem_classes="gr-radio" # برای استایل دهی بهتر
|
| 234 |
-
)
|
| 235 |
-
|
| 236 |
with gr.Row(elem_classes="main-content-row"):
|
| 237 |
with gr.Column(scale=3):
|
| 238 |
input_text = gr.Textbox(
|
|
@@ -241,10 +201,9 @@ with gr.Blocks(theme=app_theme, css=custom_css) as demo:
|
|
| 241 |
placeholder="متن برای تبدیل...",
|
| 242 |
value=""
|
| 243 |
)
|
| 244 |
-
|
| 245 |
-
|
| 246 |
-
|
| 247 |
-
value=default_voice_key_display,
|
| 248 |
label="🗣️ زبان و مدل صدا"
|
| 249 |
)
|
| 250 |
with gr.Accordion("⚙️ تنظیمات بیشتر (اختیاری)", open=False):
|
|
@@ -261,29 +220,23 @@ with gr.Blocks(theme=app_theme, css=custom_css) as demo:
|
|
| 261 |
|
| 262 |
gr.HTML("<hr style='margin-top: 1rem; margin-bottom: 1rem; border: 0; border-top: 1px solid #dee2e6;'>")
|
| 263 |
|
|
|
|
| 264 |
gr.Examples(
|
| 265 |
examples=[
|
| 266 |
-
["سلام بر شما، این یک آزمایش تبدیل متن به گفتار با صدای فارسی است.",
|
| 267 |
-
["Gradio is a fantastic library for building ML demos quickly and easily.",
|
| 268 |
-
["株式会社グラディオへようこそ。",
|
| 269 |
],
|
| 270 |
-
inputs=[input_text,
|
| 271 |
outputs=[output_text_status, output_audio],
|
| 272 |
fn=text_to_speech_edge_sync_wrapper,
|
| 273 |
cache_examples=False,
|
| 274 |
label="💡 نمونههای آماده برای تست"
|
| 275 |
)
|
| 276 |
|
| 277 |
-
# --- اتصال دکمه رادیو به تابع آپدیت Dropdown ---
|
| 278 |
-
gender_radio.change(
|
| 279 |
-
fn=update_language_dropdown,
|
| 280 |
-
inputs=gender_radio,
|
| 281 |
-
outputs=language_dropdown
|
| 282 |
-
)
|
| 283 |
-
|
| 284 |
submit_button.click(
|
| 285 |
fn=text_to_speech_edge_sync_wrapper,
|
| 286 |
-
inputs=[input_text,
|
| 287 |
outputs=[output_text_status, output_audio],
|
| 288 |
)
|
| 289 |
|
|
|
|
| 6 |
import threading
|
| 7 |
import os
|
| 8 |
|
| 9 |
+
# --- دیکشنری زبانها و صداها با کلیدهای فارسی ---
|
| 10 |
+
language_dict = {
|
| 11 |
+
'انگلیسی - جنی (زن)': 'en-US-JennyNeural',
|
| 12 |
+
'انگلیسی - گای (مرد)': 'en-US-GuyNeural',
|
| 13 |
+
'انگلیسی - آنا (زن)': 'en-US-AnaNeural',
|
| 14 |
+
'انگلیسی - آریا (زن)': 'en-US-AriaNeural',
|
| 15 |
+
'انگلیسی - کریستوفر (مرد)': 'en-US-ChristopherNeural',
|
| 16 |
+
'انگلیسی - اریک (مرد)': 'en-US-EricNeural',
|
| 17 |
+
'انگلیسی - میشل (زن)': 'en-US-MichelleNeural',
|
| 18 |
+
'انگلیسی - راجر (مرد)': 'en-US-RogerNeural',
|
| 19 |
+
|
| 20 |
+
'فارسی (ایران) - دلارا (زن)': 'fa-IR-DilaraNeural',
|
| 21 |
+
'فارسی (ایران) - فرید (مرد)': 'fa-IR-FaridNeural',
|
| 22 |
+
|
| 23 |
+
'اسپانیایی (مکزیک) - دالیا (زن)': 'es-MX-DaliaNeural',
|
| 24 |
+
'اسپانیایی (مکزیک) - خورخه (مرد)': 'es-MX-JorgeNeural', # "Jorge-" به "خورخه"
|
| 25 |
+
|
| 26 |
+
'کرهای - سان-هی (زن)': 'ko-KR-SunHiNeural', # "Sun-Hi-" به "سان-هی"
|
| 27 |
+
'کرهای - اینجون (مرد)': 'ko-KR-InJoonNeural', # "InJoon-" به "اینجون"
|
| 28 |
+
|
| 29 |
+
'ژاپنی - نانامی (زن)': 'ja-JP-NanamiNeural', # "Nanami-" به "نانامی"
|
| 30 |
+
'ژاپنی - کیتا (مرد)': 'ja-JP-KeitaNeural', # "Keita-" به "کیتا"
|
| 31 |
+
|
| 32 |
+
'فرانسوی - دنیز (زن)': 'fr-FR-DeniseNeural', # "Denise-" به "دنیز"
|
| 33 |
+
'فرانسوی - الوئیز (زن)': 'fr-FR-EloiseNeural',# "Eloise-" به "الوئیز"
|
| 34 |
+
'فرانسوی - هانری (مرد)': 'fr-FR-HenriNeural', # "Henri-" به "هانری"
|
| 35 |
+
|
| 36 |
+
'آلمانی - کاتیا (زن)': 'de-DE-KatjaNeural', # "Katja-" به "کاتیا"
|
| 37 |
+
'آلمانی - آملا (زن)': 'de-DE-AmalaNeural', # "Amala-" به "آملا"
|
| 38 |
+
'آلمانی - کنراد (مرد)': 'de-DE-ConradNeural', # "Conrad-" به "کنراد"
|
| 39 |
+
'آلمانی - کیلیان (مرد)': 'de-DE-KillianNeural', # "Killian-" به "کیلیان"
|
| 40 |
+
|
| 41 |
+
# ... شما باید بقیه زبانها را به همین ترتیب فارسیسازی کنید ...
|
| 42 |
+
# مثال های بیشتر:
|
| 43 |
+
'عربی (عربستان) - حامد (مرد)': 'ar-SA-HamedNeural',
|
| 44 |
+
'عربی (عربستان) - زاریا (زن)': 'ar-SA-ZariyahNeural',
|
| 45 |
+
|
| 46 |
+
'چینی (ماندارین ساده) - شیائوشیاو (زن)': 'zh-CN-XiaoxiaoNeural',
|
| 47 |
+
'چینی (ماندارین ساده) - یونیانگ (مرد)': 'zh-CN-YunyangNeural',
|
| 48 |
}
|
| 49 |
|
| 50 |
+
# --- توابع تبدیل متن به گفتار و wrapper (بدون تغییر باقی میمانند) ---
|
| 51 |
+
async def text_to_speech_edge_async(text, language_code, rate, volume, pitch):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 52 |
try:
|
| 53 |
if not text: return "خطا: لطفاً متنی را برای تبدیل وارد کنید.", None
|
| 54 |
+
# language_code در اینجا کلید فارسی از dropdown است
|
| 55 |
+
voice_id = language_dict.get(language_code) # مقدار voice_id همان کد انگلیسی مثل 'en-US-JennyNeural' خواهد بود
|
| 56 |
+
if voice_id is None: return f"خطا: مدل صدای انتخاب شده ('{language_code}') یافت نشد.", None
|
|
|
|
|
|
|
| 57 |
rate_str, volume_str, pitch_str = f"{int(rate):+g}%", f"{int(volume):+g}%", f"{int(pitch):+g}Hz"
|
| 58 |
communicate = edge_tts.Communicate(text, voice_id, rate=rate_str, volume=volume_str, pitch=pitch_str)
|
| 59 |
with tempfile.NamedTemporaryFile(delete=False, suffix=".wav") as tmp_file: tmp_path = tmp_file.name
|
|
|
|
| 75 |
_event_loops_by_thread[thread_id] = asyncio.new_event_loop()
|
| 76 |
return _event_loops_by_thread[thread_id]
|
| 77 |
|
| 78 |
+
def text_to_speech_edge_sync_wrapper(text, language_code, rate, volume, pitch):
|
| 79 |
try:
|
| 80 |
loop = _get_or_create_event_loop(); asyncio.set_event_loop(loop)
|
| 81 |
+
result = loop.run_until_complete(text_to_speech_edge_async(text, language_code, rate, volume, pitch))
|
| 82 |
except RuntimeError as e:
|
| 83 |
if "no current event loop" in str(e).lower() or "cannot be called from a running event loop" in str(e).lower():
|
| 84 |
new_loop = asyncio.new_event_loop(); asyncio.set_event_loop(new_loop)
|
| 85 |
+
try: result = new_loop.run_until_complete(text_to_speech_edge_async(text, language_code, rate, volume, pitch))
|
| 86 |
finally: new_loop.close()
|
| 87 |
else: return f"خطای اجرایی: {e}", None
|
| 88 |
except Exception as e: return f"خطای غیرمنتظره: {type(e).__name__}", None
|
| 89 |
return result
|
| 90 |
|
| 91 |
+
# --- تعریف تم و CSS (بدون تغییر باقی میمانند) ---
|
| 92 |
app_theme = gr.themes.Soft(
|
| 93 |
primary_hue=gr.themes.colors.blue,
|
| 94 |
secondary_hue=gr.themes.colors.sky,
|
|
|
|
| 130 |
background: #2980b9 !important; transform: translateY(-2px) !important;
|
| 131 |
box-shadow: 0 5px 10px rgba(52, 152, 219, 0.35) !important;
|
| 132 |
}
|
| 133 |
+
.gr-input, .gr-dropdown, .gr-textbox, .gr-slider {
|
| 134 |
border-radius: 8px !important; border: 1px solid #ced4da !important;
|
| 135 |
font-size: 0.95em !important;
|
| 136 |
}
|
|
|
|
|
|
|
|
|
|
| 137 |
.gr-input:focus, .gr-dropdown:focus, .gr-textbox:focus {
|
| 138 |
box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.15) !important;
|
| 139 |
border-color: #5dade2 !important;
|
|
|
|
| 142 |
border-radius: 10px !important; border: 1px solid #e9ecef !important;
|
| 143 |
background-color: #f8f9fa !important; padding: 0.75rem !important;
|
| 144 |
}
|
| 145 |
+
label > span {
|
| 146 |
font-weight: 500 !important; color: #495057 !important; font-size: 0.9em !important;
|
| 147 |
margin-bottom: 3px !important; display: block;
|
| 148 |
}
|
|
|
|
| 171 |
.main-content-row > .gr-column:nth-child(1) { flex-basis: 60%; }
|
| 172 |
.main-content-row > .gr-column:nth-child(2) { flex-basis: 40%; }
|
| 173 |
.gr-button.lg.primary { width: auto !important; }
|
|
|
|
|
|
|
|
|
|
| 174 |
}
|
| 175 |
"""
|
| 176 |
|
| 177 |
+
# --- تعریف رابط کاربری با Blocks ---
|
| 178 |
+
# مقدار پیش فرض برای زبان، حالا باید کلید فارسی باشد
|
| 179 |
+
default_voice_key_fa = 'فارسی (ایران) - فرید (مرد)'
|
| 180 |
+
if default_voice_key_fa not in language_dict:
|
| 181 |
+
# اگر به هر دلیلی کلید فارسی بالا در لیست نبود، اولین مورد لیست را انتخاب کن
|
| 182 |
+
default_voice_key_fa = list(language_dict.keys())[0] if language_dict else None
|
|
|
|
|
|
|
|
|
|
|
|
|
| 183 |
|
| 184 |
+
LOGO_URL = "https://raw.githubusercontent.com/gradio-app/gradio/main/guides/assets/gradio-logo.svg" # یا URL لوگوی خودتان
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 185 |
|
| 186 |
with gr.Blocks(theme=app_theme, css=custom_css) as demo:
|
| 187 |
with gr.Row():
|
|
|
|
| 193 |
</div>
|
| 194 |
""")
|
| 195 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 196 |
with gr.Row(elem_classes="main-content-row"):
|
| 197 |
with gr.Column(scale=3):
|
| 198 |
input_text = gr.Textbox(
|
|
|
|
| 201 |
placeholder="متن برای تبدیل...",
|
| 202 |
value=""
|
| 203 |
)
|
| 204 |
+
language = gr.Dropdown(
|
| 205 |
+
choices=list(language_dict.keys()), # کلیدهای فارسی برای نمایش به کاربر
|
| 206 |
+
value=default_voice_key_fa, # استفاده از کلید فارسی برای مقدار پیش فرض
|
|
|
|
| 207 |
label="🗣️ زبان و مدل صدا"
|
| 208 |
)
|
| 209 |
with gr.Accordion("⚙️ تنظیمات بیشتر (اختیاری)", open=False):
|
|
|
|
| 220 |
|
| 221 |
gr.HTML("<hr style='margin-top: 1rem; margin-bottom: 1rem; border: 0; border-top: 1px solid #dee2e6;'>")
|
| 222 |
|
| 223 |
+
# مثال ها هم باید از کلیدهای فارسی استفاده کنند
|
| 224 |
gr.Examples(
|
| 225 |
examples=[
|
| 226 |
+
["سلام بر شما، این یک آزمایش تبدیل متن به گفتار با صدای فارسی است.", 'فارسی (ایران) - دلارا (زن)', 0, 0, 0],
|
| 227 |
+
["Gradio is a fantastic library for building ML demos quickly and easily.", 'انگلیسی - جنی (زن)', +10, -5, 0],
|
| 228 |
+
["株式会社グラディオへようこそ。", 'ژاپنی - کیتا (مرد)', -5, 0, +5],
|
| 229 |
],
|
| 230 |
+
inputs=[input_text, language, rate, volume, pitch],
|
| 231 |
outputs=[output_text_status, output_audio],
|
| 232 |
fn=text_to_speech_edge_sync_wrapper,
|
| 233 |
cache_examples=False,
|
| 234 |
label="💡 نمونههای آماده برای تست"
|
| 235 |
)
|
| 236 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 237 |
submit_button.click(
|
| 238 |
fn=text_to_speech_edge_sync_wrapper,
|
| 239 |
+
inputs=[input_text, language, rate, volume, pitch],
|
| 240 |
outputs=[output_text_status, output_audio],
|
| 241 |
)
|
| 242 |
|