# app.py | |
import gradio as gr | |
import google.generativeai as genai | |
import os | |
import io | |
from scipy.io.wavfile import write as write_wav # برای ذخیره فایل صوتی | |
# دریافت API Key از Secrets هاگینگ فیس | |
# مطمئن شوید که یک Secret به نام GOOGLE_API_KEY در اسپیس خود تعریف کردهاید | |
GOOGLE_API_KEY = os.getenv("GOOGLE_API_KEY") | |
if not GOOGLE_API_KEY: | |
raise ValueError("GOOGLE_API_KEY not found in environment variables. Please set it in Hugging Face Secrets.") | |
genai.configure(api_key=GOOGLE_API_KEY) | |
# انتخاب مدل TTS | |
# گزینه ها: "gemini-2.5-flash-preview-tts" یا "gemini-2.5-pro-preview-tts" | |
# برای شروع، از flash استفاده میکنیم | |
TTS_MODEL_NAME = "gemini-2.5-flash-preview-tts" # یا "tts-1" اگر نامهای سادهتر هم کار میکنند (باید مستندات دقیق را چک کرد) | |
# بر اساس مستندات جدید، نام دقیق مدلها به این شکل است. | |
# لیستی از صداهای موجود (این لیست ممکن است نیاز به بروزرسانی بر اساس مستندات دقیق مدل TTS داشته باشد) | |
# این فقط یک مثال است، باید نامهای دقیق voice ها را از مستندات پیدا کنید. | |
# مستندات قبلی برای Live API صداهایی مانند Puck, Charon, Kore, Fenrir, Aoede, Leda, Orus, and Zephyr را ذکر کرده بود. | |
# اما برای مدلهای TTS اختصاصی، ممکن است لیست متفاوت باشد یا اصلاً نیازی به انتخاب voice نباشد و مدل خودش بهینه عمل کند. | |
# فعلاً این بخش را ساده نگه میداریم و به مدل اجازه میدهیم voice را انتخاب کند. | |
AVAILABLE_VOICES = ["Default"] # یا لیستی از نامهای واقعی voice اگر دارید | |
def generate_audio(text_to_speak, voice_selection="Default"): | |
""" | |
متن را به صدا تبدیل میکند با استفاده از Gemini API. | |
""" | |
if not text_to_speak: | |
raise gr.Error("لطفاً متنی را برای تبدیل به صدا وارد کنید.") | |
print(f"درخواست TTS برای متن: '{text_to_speak[:50]}...' با voice: {voice_selection}") | |
try: | |
# ایجاد مدل | |
# توجه: نحوه فراخوانی مدل TTS ممکن است با generate_content متفاوت باشد. | |
# باید مستندات دقیق را برای "Text-to-speech (TTS)" با Gemini 2.5 Flash/Pro بررسی کنیم. | |
# فرض میکنیم که میتوانیم با generate_content و ارسال متن، خروجی صوتی بگیریم. | |
# این بخش احتمالاً نیاز به اصلاح بر اساس API دقیق TTS دارد. | |
# بر اساس مستندات قیمتگذاری، مدلهای TTS ورودی متن و خروجی صدا دارند. | |
# نحوه دقیق فراخوانی برای دریافت بایتهای صوتی ممکن است به این شکل باشد: | |
model = genai.GenerativeModel(TTS_MODEL_NAME) | |
# برای مدلهای TTS، "prompt" همان متنی است که میخواهیم به صدا تبدیل شود. | |
# ممکن است نیاز به پارامترهای خاصی در generation_config برای صدا باشد. | |
response = model.generate_content( | |
text_to_speak, | |
# generation_config=genai.types.GenerationConfig( | |
# # پارامترهای خاص TTS در اینجا قرار میگیرند، اگر وجود داشته باشد | |
# # مثلاً voice، سرعت، لحن و ... | |
# # response_mime_type="audio/wav" or "audio/mp3" ??? | |
# ) | |
) | |
# پاسخ مدلهای TTS معمولاً شامل بایتهای صوتی است. | |
# باید بررسی کنیم که پاسخ در چه فرمتی است. | |
# فرض میکنیم پاسخ دارای یک پراپرتی audio_content یا مشابه است که بایتها را دارد. | |
# این بخش کاملاً به خروجی واقعی API بستگی دارد. | |
# --- این بخش حدسی است و باید با مستندات API تطبیق داده شود --- | |
if hasattr(response, 'audio_content') and response.audio_content: | |
audio_bytes = response.audio_content | |
elif hasattr(response, 'candidates') and response.candidates[0].content.parts[0].inline_data: | |
# این ساختار برای inline_data در پاسخهای چندوجهی است | |
audio_part = response.candidates[0].content.parts[0] | |
if audio_part.inline_data.mime_type.startswith("audio/"): | |
audio_bytes = audio_part.inline_data.data | |
else: | |
raise gr.Error(f"فرمت پاسخ صوتی نامعتبر: {audio_part.inline_data.mime_type}") | |
elif hasattr(response, 'text'): # اگر به اشتباه پاسخ متنی گرفتیم | |
raise gr.Error(f"مدل پاسخ متنی برگرداند به جای صدا: {response.text}") | |
else: | |
print("پاسخ کامل مدل:", response) # برای دیباگ | |
raise gr.Error("پاسخ صوتی از مدل دریافت نشد یا فرمت آن ناشناخته است.") | |
# --- پایان بخش حدسی --- | |
# ذخیره بایتهای صوتی در یک فایل WAV موقت | |
# ما به نرخ نمونهبرداری (sample rate) صدای خروجی نیاز داریم. | |
# مدلهای TTS معمولاً با نرخ نمونهبرداری مشخصی خروجی میدهند (مثلاً 24000 Hz). | |
# این مقدار باید از مستندات API گرفته شود. فرض میکنیم 24000 Hz است. | |
sample_rate = 24000 # هرتز - این را از مستندات API برای مدل TTS خود چک کنید! | |
# تبدیل بایتها به فرمتی که Gradio بتواند پخش کند (فایل WAV) | |
# کتابخانه google-generativeai ممکن است مستقیماً فایل صوتی برنگرداند، بلکه بایتهای خام PCM. | |
# یا ممکن است یک آبجکت خاص Audio برگرداند. | |
# سادهترین حالت این است که API مستقیماً بایتهای یک فایل WAV را برگرداند. | |
# اگر بایتهای خام PCM برمیگرداند، باید آنها را به WAV تبدیل کنیم. | |
# فرض میکنیم audio_bytes حاوی دادههای یک فایل WAV کامل است | |
# یا باید با استفاده از scipy.io.wavfile یا wave آن را بسازیم. | |
# اگر audio_bytes داده خام PCM16 است: | |
# import numpy as np | |
# audio_np = np.frombuffer(audio_bytes, dtype=np.int16) | |
# wav_io = io.BytesIO() | |
# write_wav(wav_io, sample_rate, audio_np) | |
# output_audio_path = wav_io # Gradio میتواند BytesIO را به عنوان فایل صوتی بپذیرد | |
# برای سادگی، فرض میکنیم audio_bytes بایتهای یک فایل صوتی قابل پخش است (مثلاً WAV) | |
# و Gradio میتواند آن را مستقیماً به عنوان (sample_rate, np_array) یا مسیر فایل یا BytesIO بپذیرد. | |
# اگر API یک آبجکت خاص برمیگرداند، باید آن را مطابق مستندات پردازش کنید. | |
# برای اینکه Gradio بتواند پخش کند، ما به (sample_rate, numpy_array) نیاز داریم | |
# یا مسیر یک فایل. اگر بایتهای خام PCM داریم: | |
# این بخش نیاز به کار بیشتری دارد اگر API بایتهای خام PCM برمیگرداند. | |
# فعلاً فرض میکنیم API یک فرمت قابل قبول برای Gradio برمیگرداند یا ما آن را تبدیل میکنیم. | |
# سادهترین راه برای تست اولیه: ذخیره بایتها در فایل و برگرداندن مسیر فایل | |
output_filename = "output_audio.wav" | |
with open(output_filename, "wb") as f: | |
f.write(audio_bytes) | |
print(f"فایل صوتی در {output_filename} ذخیره شد.") | |
return output_filename # Gradio میتواند مسیر فایل را برای Audio output بگیرد | |
except Exception as e: | |
print(f"خطا در تولید صدا: {e}") | |
# نمایش جزئیات بیشتر خطا برای دیباگ | |
import traceback | |
traceback.print_exc() | |
raise gr.Error(f"خطا در ارتباط با Gemini API یا پردازش صدا: {str(e)}") | |
# ایجاد رابط کاربری Gradio | |
with gr.Blocks(theme=gr.themes.Soft()) as demo: | |
gr.Markdown("# تبدیل متن به صدا با Gemini ♊") | |
gr.Markdown("متن خود را وارد کنید تا با استفاده از مدلهای جدید Gemini به صدا تبدیل شود.") | |
with gr.Row(): | |
with gr.Column(scale=2): | |
text_input = gr.Textbox(lines=5, label="متن ورودی", placeholder="متن خود را اینجا بنویسید...") | |
# voice_dropdown = gr.Dropdown(choices=AVAILABLE_VOICES, value="Default", label="انتخاب صدا (اختیاری)") # فعلاً ساده | |
submit_button = gr.Button("🔊 تبدیل به صدا", variant="primary") | |
with gr.Column(scale=1): | |
audio_output = gr.Audio(label="خروجی صدا", type="filepath") # یا type="numpy" اگر آرایه برمیگردانید | |
gr.Examples( | |
examples=[ | |
["سلام، حال شما چطور است؟"], | |
["به دنیای هوش مصنوعی خوش آمدید."], | |
["این یک تست برای تبدیل متن به صدا با استفاده از جیمینای است."] | |
], | |
inputs=[text_input] | |
) | |
submit_button.click( | |
fn=generate_audio, | |
inputs=[text_input], # voice_dropdown اگر فعال بود | |
outputs=[audio_output], | |
api_name="text_to_speech" | |
) | |
gr.Markdown("---") | |
gr.Markdown(f"مدل مورد استفاده: `{TTS_MODEL_NAME}`") | |
if __name__ == "__main__": | |
demo.launch(debug=True) # debug=True برای دیدن لاگهای بیشتر در کنسول |