File size: 10,715 Bytes
2fa8637 |
1 2 3 4 5 6 7 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 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 |
# 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 برای دیدن لاگهای بیشتر در کنسول |