Spaces:
Paused
Paused
Update app.py
Browse files
app.py
CHANGED
|
@@ -2,11 +2,9 @@ import gradio as gr
|
|
| 2 |
import os
|
| 3 |
import torch
|
| 4 |
from TTS.api import TTS # مكتبة Coqui TTS
|
| 5 |
-
|
| 6 |
-
import soundfile as sf
|
| 7 |
-
import numpy as np
|
| 8 |
|
| 9 |
-
# --- 1. تحميل النم
|
| 10 |
# هذا الجزء يستغرق وقتاً طويلاً ويستهلك ذاكرة كبيرة.
|
| 11 |
# يفضل وضع النماذج في Cache Hugging Face إذا أمكن.
|
| 12 |
|
|
@@ -16,118 +14,68 @@ print(f"Using device: {DEVICE}")
|
|
| 16 |
|
| 17 |
# تحميل نموذج Coqui TTS (XTTS-v2)
|
| 18 |
# هذا النموذج هو الذي يقوم باستنساخ الصوت وتحويل النص إلى كلام
|
|
|
|
| 19 |
try:
|
| 20 |
tts = TTS("tts_models/multilingual/multi-dataset/xtts_v2", gpu=torch.cuda.is_available())
|
| 21 |
print("Coqui TTS XTTS-v2 model loaded successfully.")
|
| 22 |
except Exception as e:
|
| 23 |
print(f"Error loading Coqui TTS model: {e}")
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
# تحميل نموذج Whisper للتعرف على الكلام (ASR)
|
| 27 |
-
# whisper-tiny هو نموذج صغير نسبياً ولكنه جيد للتعربية
|
| 28 |
-
try:
|
| 29 |
-
asr_processor = AutoProcessor.from_pretrained("openai/whisper-tiny")
|
| 30 |
-
asr_model = AutoModelForSpeechSeq2Seq.from_pretrained("openai/whisper-tiny").to(DEVICE)
|
| 31 |
-
print("Whisper Tiny ASR model loaded successfully.")
|
| 32 |
-
except Exception as e:
|
| 33 |
-
print(f"Error loading Whisper ASR model: {e}")
|
| 34 |
-
asr_processor = None
|
| 35 |
-
asr_model = None
|
| 36 |
|
| 37 |
# --- 2. تعريف وظائف التطبيق ---
|
| 38 |
|
| 39 |
# دالة لتخزين مسار الصوت المرجعي المستنسخ
|
| 40 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 41 |
if not audio_path:
|
| 42 |
-
return None, "خطأ: لم يتم تسجيل أو رفع صوت لنسخه."
|
| 43 |
-
|
| 44 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 45 |
print(f"Reference audio set to: {audio_path}")
|
| 46 |
-
return audio_path, "تم تحميل الصوت المرجعي بنجاح! يمكنك الآن ت
|
| 47 |
|
| 48 |
# دالة لتحويل النص إلى كلام بالصوت المستنسخ (Text-to-Speech)
|
| 49 |
def synthesize_text(reference_audio_path, text_input):
|
| 50 |
if not tts:
|
| 51 |
-
return None, "خطأ: نموذج استنساخ الصوت لم يتم تحميله بشكل صحيح."
|
| 52 |
if not reference_audio_path:
|
| 53 |
-
return None, "خطأ: يرجى أولاً تسجيل أو رفع صوت لنسخه."
|
| 54 |
if not text_input:
|
| 55 |
return None, "خطأ: يرجى إدخال نص لتحويله."
|
| 56 |
|
| 57 |
try:
|
| 58 |
# XTTS-v2 يدعم لغات متعددة، بما في ذلك العربية (ar)
|
| 59 |
-
# سيقوم النموذج باستخدام speaker_wav (الصوت المرجعي) لاستنساخ الصوت
|
| 60 |
-
# ويقوم بتحويل النص text_input باللغة "ar".
|
| 61 |
output_file = "output_tts.wav"
|
| 62 |
tts.tts_to_file(
|
| 63 |
text=text_input,
|
| 64 |
speaker_wav=reference_audio_path,
|
| 65 |
-
language="ar",
|
| 66 |
file_path=output_file
|
| 67 |
)
|
| 68 |
print(f"Text-to-Speech generated: {output_file}")
|
| 69 |
return output_file, "تم تحويل النص إلى صوت بنجاح!"
|
| 70 |
except Exception as e:
|
| 71 |
print(f"Error during TTS synthesis: {e}")
|
| 72 |
-
return None, f"خطأ أثناء تحويل النص إلى صوت: {e}"
|
| 73 |
-
|
| 74 |
-
# دالة لتحويل الكلام إلى كلام بالصوت المستنسخ (Speech-to-Speech)
|
| 75 |
-
# هذا يتضمن ASR (تحويل الكلام إلى نص) ثم TTS (تحويل النص إلى كلام مستنسخ)
|
| 76 |
-
def process_speech(reference_audio_path, input_speech_path):
|
| 77 |
-
if not tts:
|
| 78 |
-
return None, "خطأ: نموذج استنساخ الصوت لم يتم تحميله بشكل صحيح."
|
| 79 |
-
if not asr_model or not asr_processor:
|
| 80 |
-
return None, "خطأ: نموذج التعرف على الكلام (ASR) لم يتم تحميله بشكل صحيح."
|
| 81 |
-
if not reference_audio_path:
|
| 82 |
-
return None, "خطأ: يرجى أولاً تسجيل أو رفع صوت لنسخه."
|
| 83 |
-
if not input_speech_path:
|
| 84 |
-
return None, "خطأ: يرجى تسجيل أو رفع صوت لكي يتم نطقه."
|
| 85 |
-
|
| 86 |
-
try:
|
| 87 |
-
# 1. تحويل الكلام المدخل إلى نص (ASR)
|
| 88 |
-
# قراءة الملف الصوتي المدخل
|
| 89 |
-
audio_data, sample_rate = sf.read(input_speech_path)
|
| 90 |
-
# قد يحتاج Whisper إلى 16kHz
|
| 91 |
-
if sample_rate != 16000:
|
| 92 |
-
# يجب إعادة أخذ العينات إذا لم يكن 16 كيلو هرتز
|
| 93 |
-
# هذا يتطلب مكتبات مثل librosa أو torchaudio،
|
| 94 |
-
# لتبسيط الأمر، سنفترض أن Gradio يضمن 16kHz أو سيهمل.
|
| 95 |
-
# الأفضل استخدام torchaudio.transforms.Resample
|
| 96 |
-
pass # في بيئات Gradio، غالباً ما يتم إعادة أخذ العينات تلقائياً
|
| 97 |
-
|
| 98 |
-
# معالجة الصوت لـ Whisper
|
| 99 |
-
input_features = asr_processor(audio_data, sampling_rate=sample_rate, return_tensors="pt").input_features
|
| 100 |
-
input_features = input_features.to(DEVICE) # نقل إلى GPU إن وجد
|
| 101 |
-
|
| 102 |
-
# توليد الرموز المميزة (tokens)
|
| 103 |
-
predicted_ids = asr_model.generate(input_features)
|
| 104 |
-
|
| 105 |
-
# تحويل الرموز المميزة إلى نص
|
| 106 |
-
transcribed_text = asr_processor.batch_decode(predicted_ids, skip_special_tokens=True)[0]
|
| 107 |
-
print(f"Transcribed text: {transcribed_text}")
|
| 108 |
-
|
| 109 |
-
if not transcribed_text.strip():
|
| 110 |
-
return None, "خطأ: لم يتم التعرف على أي كلام واضح في التسجيل الصوتي المدخل."
|
| 111 |
-
|
| 112 |
-
# 2. تحويل النص المعرف إلى كلام بالصوت المستنسخ (TTS)
|
| 113 |
-
output_file = "output_sts.wav"
|
| 114 |
-
tts.tts_to_file(
|
| 115 |
-
text=transcribed_text,
|
| 116 |
-
speaker_wav=reference_audio_path,
|
| 117 |
-
language="ar", # تحديد اللغة العربية
|
| 118 |
-
file_path=output_file
|
| 119 |
-
)
|
| 120 |
-
print(f"Speech-to-Speech generated: {output_file}")
|
| 121 |
-
return output_file, f"تم التعرف على النص: '{transcribed_text}' وتم نطقه بالصوت المستنسخ بنجاح!"
|
| 122 |
-
|
| 123 |
-
except Exception as e:
|
| 124 |
-
print(f"Error during STS processing: {e}")
|
| 125 |
-
return None, f"خطأ أثناء معالجة الكلام: {e}"
|
| 126 |
|
| 127 |
# --- 3. بناء واجهة Gradio ---
|
| 128 |
-
with gr.Blocks(theme="soft", title="تطبيق استنساخ الصوت") as demo:
|
| 129 |
-
gr.Markdown("# تطبيق استنساخ الصوت")
|
| 130 |
-
gr.Markdown("قم بتسجيل أو رفع صوت مرجعي (5-10 ثواني على الأقل) لاستنساخه، ثم استخدمه لتحويل النص
|
| 131 |
|
| 132 |
# متغير الحالة لتخزين مسار الصوت المرجعي
|
| 133 |
reference_audio_path_state = gr.State(None)
|
|
@@ -153,30 +101,15 @@ with gr.Blocks(theme="soft", title="تطبيق استنساخ الصوت") as de
|
|
| 153 |
synthesize_btn = gr.Button("تحويل النص إلى صوت")
|
| 154 |
tts_status_msg = gr.Markdown("")
|
| 155 |
|
| 156 |
-
#
|
| 157 |
-
with gr.Column(elem_id="sts_section"):
|
| 158 |
-
gr.Markdown("## 3. تحويل الكلام إلى كلام (STS)")
|
| 159 |
-
gr.Markdown("**(يتطلب التعرف على الكلام أولاً)**")
|
| 160 |
-
with gr.Row():
|
| 161 |
-
record_input_speech = gr.Audio(
|
| 162 |
-
sources=["microphone"], type="filepath", label="سجل الصوت الذي تريد أن ينطق بصوتك المستنسخ"
|
| 163 |
-
)
|
| 164 |
-
upload_input_speech = gr.Audio(
|
| 165 |
-
sources=["upload"], type="filepath", label="أو ارفع ملف صوتي لينطق بصوتك المستنسخ"
|
| 166 |
-
)
|
| 167 |
-
sts_output = gr.Audio(label="الصوت المستنسخ (من كلامك)", autoplay=True)
|
| 168 |
-
process_speech_btn = gr.Button("نطق الكلام بالصوت المستنسخ")
|
| 169 |
-
sts_status_msg = gr.Markdown("")
|
| 170 |
-
|
| 171 |
-
# رسالة تحذير حول الموارد والأداء
|
| 172 |
gr.Markdown("""
|
| 173 |
<p style="font-size: 0.9em; color: #a00; margin-top: 30px; padding-top: 15px; border-top: 1px dashed #f00; text-align: center; font-weight: bold;">
|
| 174 |
-
⚠️ ملاحظة هامة: هذا التطبيق يستخدم نم
|
| 175 |
<br>
|
| 176 |
-
* **سيستغرق التحميل الأولي وقتاً طويلاً جداً (عدة دقائق).**
|
| 177 |
* **يوصى بشدة باستخدام موارد GPU (مثل T4 Small) للحصول على أداء مقبول.**
|
| 178 |
-
* على موارد CPU (Basic)، قد تكون العمليات بطيئة للغاية أو تفشل بسبب تجاوز المهلة.
|
| 179 |
-
* جودة الاستنساخ تعتمد بشكل كبير على
|
| 180 |
</p>
|
| 181 |
""", elem_id="disclaimer")
|
| 182 |
|
|
@@ -185,10 +118,9 @@ with gr.Blocks(theme="soft", title="تطبيق استنساخ الصوت") as de
|
|
| 185 |
# عند النقر على "تحديد الصوت المرجعي"
|
| 186 |
set_ref_audio_btn.click(
|
| 187 |
fn=set_reference_audio,
|
| 188 |
-
inputs=[record_ref_audio_input, upload_ref_audio_input],
|
| 189 |
outputs=[reference_audio_path_state, ref_status_msg]
|
| 190 |
)
|
| 191 |
-
# ملاحظة: Gradio يمكنه تمرير قيمة أحد المدخلين إذا كان الآخر فارغاً.
|
| 192 |
|
| 193 |
# عند النقر على "تحويل النص إلى صوت"
|
| 194 |
synthesize_btn.click(
|
|
@@ -197,15 +129,6 @@ with gr.Blocks(theme="soft", title="تطبيق استنساخ الصوت") as de
|
|
| 197 |
outputs=[tts_output, tts_status_msg]
|
| 198 |
)
|
| 199 |
|
| 200 |
-
# عند النقر على "نطق الكلام بالصوت المستنسخ"
|
| 201 |
-
process_speech_btn.click(
|
| 202 |
-
fn=process_speech,
|
| 203 |
-
inputs=[reference_audio_path_state, record_input_speech, upload_input_speech],
|
| 204 |
-
outputs=[sts_output, sts_status_msg]
|
| 205 |
-
)
|
| 206 |
-
# ملاحظة: سنحتاج لدمج مدخلات record_input_speech و upload_input_speech في دالة process_speech
|
| 207 |
-
# Gradio سيمرر القيمة الموجودة (المسجلة أو المرفوعة).
|
| 208 |
-
|
| 209 |
# تشغيل التطبيق
|
| 210 |
if __name__ == "__main__":
|
| 211 |
demo.launch()
|
|
|
|
| 2 |
import os
|
| 3 |
import torch
|
| 4 |
from TTS.api import TTS # مكتبة Coqui TTS
|
| 5 |
+
import soundfile as sf # للتعامل مع الملفات الصوتية (Coqui TTS قد تستخدمها داخلياً)
|
|
|
|
|
|
|
| 6 |
|
| 7 |
+
# --- 1. تحميل النموذج (يتم مرة واحدة عند بدء التطبيق) ---
|
| 8 |
# هذا الجزء يستغرق وقتاً طويلاً ويستهلك ذاكرة كبيرة.
|
| 9 |
# يفضل وضع النماذج في Cache Hugging Face إذا أمكن.
|
| 10 |
|
|
|
|
| 14 |
|
| 15 |
# تحميل نموذج Coqui TTS (XTTS-v2)
|
| 16 |
# هذا النموذج هو الذي يقوم باستنساخ الصوت وتحويل النص إلى كلام
|
| 17 |
+
tts = None # تهيئة المتغير
|
| 18 |
try:
|
| 19 |
tts = TTS("tts_models/multilingual/multi-dataset/xtts_v2", gpu=torch.cuda.is_available())
|
| 20 |
print("Coqui TTS XTTS-v2 model loaded successfully.")
|
| 21 |
except Exception as e:
|
| 22 |
print(f"Error loading Coqui TTS model: {e}")
|
| 23 |
+
# إذا فشل التحميل، لن يكون tts متاحاً وستظهر رسالة خطأ للمستخدم.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 24 |
|
| 25 |
# --- 2. تعريف وظائف التطبيق ---
|
| 26 |
|
| 27 |
# دالة لتخزين مسار الصوت المرجعي المستنسخ
|
| 28 |
+
# تتلقى مسار الصوت من مدخل الميكروفون أو من مدخل رفع الملفات
|
| 29 |
+
def set_reference_audio(mic_path, upload_path):
|
| 30 |
+
audio_path = None
|
| 31 |
+
if mic_path:
|
| 32 |
+
audio_path = mic_path
|
| 33 |
+
elif upload_path:
|
| 34 |
+
audio_path = upload_path
|
| 35 |
+
|
| 36 |
if not audio_path:
|
| 37 |
+
return None, "خطأ: لم يتم تسجيل أو رفع صوت لنسخه. يرجى المحاولة مرة أخرى."
|
| 38 |
+
|
| 39 |
+
# التأكد من أن الملف الصوتي صالح للقراءة
|
| 40 |
+
try:
|
| 41 |
+
# اختبار قراءة الملف للتأكد من سلامته وقابليته للوصول
|
| 42 |
+
# بعض الأحيان Gradio يمرر مساراً غير صالح أو فارغاً.
|
| 43 |
+
# sf.read سيقوم بفحص الملف
|
| 44 |
+
sf.read(audio_path)
|
| 45 |
+
except Exception as e:
|
| 46 |
+
return None, f"خطأ في قراءة الملف الصوتي: {e}. تأكد من أنه ملف صوتي صالح."
|
| 47 |
+
|
| 48 |
print(f"Reference audio set to: {audio_path}")
|
| 49 |
+
return audio_path, "تم تحميل الصوت المرجعي بنجاح! يمكنك الآن تحويل النص إلى صوتك المستنسخ."
|
| 50 |
|
| 51 |
# دالة لتحويل النص إلى كلام بالصوت المستنسخ (Text-to-Speech)
|
| 52 |
def synthesize_text(reference_audio_path, text_input):
|
| 53 |
if not tts:
|
| 54 |
+
return None, "خطأ: نموذج استنساخ الصوت لم يتم تحميله بشكل صحيح عند بدء التطبيق. الرجاء التحقق من سجلات Space."
|
| 55 |
if not reference_audio_path:
|
| 56 |
+
return None, "خطأ: يرجى أولاً تسجيل أو رفع صوت لنسخه وتحديده."
|
| 57 |
if not text_input:
|
| 58 |
return None, "خطأ: يرجى إدخال نص لتحويله."
|
| 59 |
|
| 60 |
try:
|
| 61 |
# XTTS-v2 يدعم لغات متعددة، بما في ذلك العربية (ar)
|
|
|
|
|
|
|
| 62 |
output_file = "output_tts.wav"
|
| 63 |
tts.tts_to_file(
|
| 64 |
text=text_input,
|
| 65 |
speaker_wav=reference_audio_path,
|
| 66 |
+
language="ar", # تحديد اللغة العربية
|
| 67 |
file_path=output_file
|
| 68 |
)
|
| 69 |
print(f"Text-to-Speech generated: {output_file}")
|
| 70 |
return output_file, "تم تحويل النص إلى صوت بنجاح!"
|
| 71 |
except Exception as e:
|
| 72 |
print(f"Error during TTS synthesis: {e}")
|
| 73 |
+
return None, f"خطأ أثناء تحويل النص إلى صوت: {e}. تأكد من جودة الصوت المرجعي."
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 74 |
|
| 75 |
# --- 3. بناء واجهة Gradio ---
|
| 76 |
+
with gr.Blocks(theme="soft", title="تطبيق استنساخ الصوت (TTS)") as demo:
|
| 77 |
+
gr.Markdown("# تطبيق استنساخ الصوت (تحويل النص إلى كلام)")
|
| 78 |
+
gr.Markdown("قم بتسجيل أو رفع **صوت مرجعي واضح (5-10 ثواني على الأقل)** لاستنساخه، ثم استخدمه لتحويل النص إلى صوتك المستنسخ.")
|
| 79 |
|
| 80 |
# متغير الحالة لتخزين مسار الصوت المرجعي
|
| 81 |
reference_audio_path_state = gr.State(None)
|
|
|
|
| 101 |
synthesize_btn = gr.Button("تحويل النص إلى صوت")
|
| 102 |
tts_status_msg = gr.Markdown("")
|
| 103 |
|
| 104 |
+
# رسالة تحذير حول الموارد والأداء (مهمة جداً)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 105 |
gr.Markdown("""
|
| 106 |
<p style="font-size: 0.9em; color: #a00; margin-top: 30px; padding-top: 15px; border-top: 1px dashed #f00; text-align: center; font-weight: bold;">
|
| 107 |
+
⚠️ ملاحظة هامة: هذا التطبيق يستخدم نموذج ذكاء اصطناعي كبير (XTTS-v2).
|
| 108 |
<br>
|
| 109 |
+
* **سيستغرق التحميل الأولي (عند بدء Space) وقتاً طويلاً جداً (عدة دقائق).**
|
| 110 |
* **يوصى بشدة باستخدام موارد GPU (مثل T4 Small) للحصول على أداء مقبول.**
|
| 111 |
+
* على موارد CPU (Basic)، قد تكون العمليات بطيئة للغاية أو تفشل بسبب تجاوز المهلة أو نفاد الذاكرة.
|
| 112 |
+
* جودة الاستنساخ تعتمد بشكل كبير على وضوح ونقاء الصوت المرجعي الذي تقدمه.
|
| 113 |
</p>
|
| 114 |
""", elem_id="disclaimer")
|
| 115 |
|
|
|
|
| 118 |
# عند النقر على "تحديد الصوت المرجعي"
|
| 119 |
set_ref_audio_btn.click(
|
| 120 |
fn=set_reference_audio,
|
| 121 |
+
inputs=[record_ref_audio_input, upload_ref_audio_input],
|
| 122 |
outputs=[reference_audio_path_state, ref_status_msg]
|
| 123 |
)
|
|
|
|
| 124 |
|
| 125 |
# عند النقر على "تحويل النص إلى صوت"
|
| 126 |
synthesize_btn.click(
|
|
|
|
| 129 |
outputs=[tts_output, tts_status_msg]
|
| 130 |
)
|
| 131 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 132 |
# تشغيل التطبيق
|
| 133 |
if __name__ == "__main__":
|
| 134 |
demo.launch()
|