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 برای دیدن لاگ‌های بیشتر در کنسول