EmadAgha commited on
Commit
d173109
·
verified ·
1 Parent(s): 4ebd109

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +71 -50
app.py CHANGED
@@ -4,65 +4,86 @@ import asyncio
4
  import tempfile
5
  import os
6
 
7
- # الدالة الرئيسية التي تستقبل المعاملات الجديدة
 
 
 
 
 
 
 
 
 
8
  async def text_to_speech_edge(text, voice, emotion, is_symbol, rate, pitch):
9
- if not text.strip():
10
- return None
 
 
 
 
 
 
 
 
11
 
12
- # التعامل مع القيم الافتراضية إذا كانت فارغة
13
- if not rate: rate = "+0%"
14
- if not pitch: pitch = "+0Hz"
15
-
16
- # تحديد اسم الصوت بناءً على المدخل
17
- voice_map = {
18
- "رجل (مصري)": "ar-EG-ShakirNeural",
19
- "سيدة (مصرية)": "ar-EG-SalmaNeural",
20
- "رجل (سعودي)": "ar-SA-HamedNeural",
21
- "سيدة (سعودية)": "ar-SA-ZariyahNeural",
22
- "English (US) M": "en-US-EricNeural",
23
- "English (US) F": "en-US-AriaNeural"
24
- }
25
 
26
- # استخدام المفتاح المباشر إذا لم يكن في القاموس
27
- selected_voice = voice_map.get(voice, voice)
 
 
 
28
 
 
 
 
 
29
  output_file = tempfile.NamedTemporaryFile(suffix=".mp3", delete=False)
30
  output_path = output_file.name
31
  output_file.close()
32
-
33
- # توليد الصوت مع المعاملات الإضافية
34
- communicate = edge_tts.Communicate(text, selected_voice, rate=rate, pitch=pitch)
35
- await communicate.save(output_path)
36
-
37
- return output_path
38
 
39
- # بناء الواجهة
40
- with gr.Blocks() as demo:
41
- with gr.Row():
42
- text_input = gr.Textbox(label="Text")
43
- voice_input = gr.Dropdown(
44
- ["رجل (مصري)", "سيدة (مصرية)", "رجل (سعودي)", "سيدة (سعودية)", "English (US) M", "English (US) F"],
45
- label="Voice"
46
- )
47
 
48
- # مدخلات إضافية ليتوافق مع طلب التطبيق
49
- emotion_input = gr.Textbox(label="Emotion", value="محايد", visible=False)
50
- symbol_input = gr.Checkbox(label="Is Symbol", value=True, visible=False)
51
-
52
- # المعاملات الجديدة المهمة
53
- rate_input = gr.Textbox(label="Rate", value="+0%")
54
- pitch_input = gr.Textbox(label="Pitch", value="+0Hz")
55
-
56
- output_audio = gr.Audio(label="Output", type="filepath")
57
-
58
- submit_btn = gr.Button("Generate")
59
-
60
- # ربط الدالة بالواجهة - تأكد من الترتيب الصحيح للمدخلات
61
- submit_btn.click(
62
- text_to_speech_edge,
63
- inputs=[text_input, voice_input, emotion_input, symbol_input, rate_input, pitch_input],
64
- outputs=output_audio
65
- )
 
 
 
 
 
 
 
 
66
 
67
  if __name__ == "__main__":
68
  demo.queue().launch()
 
4
  import tempfile
5
  import os
6
 
7
+ # Map readable labels to Edge TTS internal IDs
8
+ VOICE_MAP = {
9
+ "رجل (مصري)": "ar-EG-ShakirNeural",
10
+ "سيدة (مصرية)": "ar-EG-SalmaNeural",
11
+ "رجل (سعودي)": "ar-SA-HamedNeural",
12
+ "سيدة (سعودية)": "ar-SA-ZariyahNeural",
13
+ "English (US) M": "en-US-EricNeural",
14
+ "English (US) F": "en-US-AriaNeural"
15
+ }
16
+
17
  async def text_to_speech_edge(text, voice, emotion, is_symbol, rate, pitch):
18
+ """
19
+ Generates speech using Edge TTS.
20
+ Args:
21
+ text (str): The text to speak.
22
+ voice (str): Voice ID or Label.
23
+ emotion (str): Ignored (kept for compatibility).
24
+ is_symbol (bool): Ignored (kept for compatibility).
25
+ rate (str): Speed change (e.g., "+0%", "+20%").
26
+ pitch (str): Pitch change (e.g., "+0Hz", "+2st").
27
+ """
28
 
29
+ # 1. Validation
30
+ if not text or not text.strip():
31
+ return None
32
+
33
+ # 2. Voice Resolution
34
+ # Check if the input is a known Key (Label) or Value (ID)
35
+ selected_voice = "ar-SA-HamedNeural" # Default
36
+ if voice in VOICE_MAP:
37
+ selected_voice = VOICE_MAP[voice]
38
+ elif voice in VOICE_MAP.values():
39
+ selected_voice = voice
 
 
40
 
41
+ # 3. Parameter Sanitization
42
+ # EdgeTTS crashes if rate/pitch are None or empty strings.
43
+ # We ensure they always have a valid default value.
44
+ final_rate = rate if rate and isinstance(rate, str) and len(rate.strip()) > 0 else "+0%"
45
+ final_pitch = pitch if pitch and isinstance(pitch, str) and len(pitch.strip()) > 0 else "+0Hz"
46
 
47
+ print(f"Generating: TextLen={len(text)}, Voice={selected_voice}, Rate={final_rate}, Pitch={final_pitch}")
48
+
49
+ # 4. Temp File Creation
50
+ # delete=False is required for Gradio to serve the file after the function returns
51
  output_file = tempfile.NamedTemporaryFile(suffix=".mp3", delete=False)
52
  output_path = output_file.name
53
  output_file.close()
 
 
 
 
 
 
54
 
55
+ try:
56
+ # 5. Generation
57
+ communicate = edge_tts.Communicate(text, selected_voice, rate=final_rate, pitch=final_pitch)
58
+ await communicate.save(output_path)
59
+ return output_path
 
 
 
60
 
61
+ except Exception as e:
62
+ print(f"CRITICAL ERROR: {str(e)}")
63
+ # Return None or raise a Gradio Error to notify the frontend
64
+ raise gr.Error(f"Generation Failed: {str(e)}")
65
+
66
+ # Define the Interface
67
+ # The order of inputs MUST match the array sent from React:
68
+ # [text, voice, emotion, is_symbol, rate, pitch]
69
+ inputs = [
70
+ gr.Textbox(label="Text"),
71
+ gr.Dropdown(label="Voice", choices=list(VOICE_MAP.keys()) + list(VOICE_MAP.values())),
72
+ gr.Textbox(label="Emotion", value="neutral"),
73
+ gr.Checkbox(label="Is Symbol", value=True),
74
+ gr.Textbox(label="Rate", value="+0%"),
75
+ gr.Textbox(label="Pitch", value="+0Hz")
76
+ ]
77
+
78
+ outputs = gr.Audio(label="Generated Audio", type="filepath")
79
+
80
+ demo = gr.Interface(
81
+ fn=text_to_speech_edge,
82
+ inputs=inputs,
83
+ outputs=outputs,
84
+ title="Natiq Pro API",
85
+ allow_flagging="never"
86
+ )
87
 
88
  if __name__ == "__main__":
89
  demo.queue().launch()