File size: 8,780 Bytes
2fa8637
 
 
dec8bb3
2fa8637
 
3090f63
59d09f0
dec8bb3
88afdfa
2fa8637
 
 
3090f63
2fa8637
 
88afdfa
 
 
 
59d09f0
 
 
2fa8637
 
88afdfa
2fa8637
 
59d09f0
3090f63
88afdfa
59d09f0
 
dec8bb3
 
 
 
 
 
 
 
59d09f0
 
 
88afdfa
59d09f0
 
3090f63
 
dec8bb3
3090f63
 
59d09f0
3090f63
 
169ec87
3090f63
 
59d09f0
 
 
 
 
 
3090f63
59d09f0
3090f63
169ec87
59d09f0
 
169ec87
59d09f0
 
 
3090f63
 
 
59d09f0
3090f63
59d09f0
169ec87
3090f63
 
 
 
 
169ec87
59d09f0
 
3090f63
59d09f0
 
 
2fa8637
3090f63
2fa8637
169ec87
 
 
dec8bb3
169ec87
dec8bb3
169ec87
88afdfa
169ec87
88afdfa
 
169ec87
dec8bb3
169ec87
dec8bb3
 
169ec87
 
 
88afdfa
dec8bb3
169ec87
dec8bb3
169ec87
 
88afdfa
dec8bb3
 
169ec87
dec8bb3
169ec87
 
3090f63
2fa8637
dec8bb3
2fa8637
 
 
 
 
 
 
 
3090f63
169ec87
 
 
 
 
 
 
 
2fa8637
 
3090f63
2fa8637
 
 
 
88afdfa
59d09f0
3090f63
2fa8637
dec8bb3
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
# from google.generativeai import types # دیگر نیازی به types برای GenerationConfig با response_modalities نیست
import os
import io
from scipy.io.wavfile import write as write_wav
import numpy as np
import traceback
import json # برای parse کردن خطای JSON احتمالی

GOOGLE_API_KEY = os.getenv("GOOGLE_API_KEY")
if not GOOGLE_API_KEY:
    raise ValueError("GOOGLE_API_KEY not found in environment variables.")
genai.configure(api_key=GOOGLE_API_KEY)

# --- تغییر نام مدل به نسخه Pro TTS ---
TTS_MODEL_NAME = "gemini-2.5-pro-preview-tts"
# --- پایان تغییر ---

AVAILABLE_VOICES = ["پیش‌فرض (مدل انتخاب کند)"]

def generate_audio(text_to_speak, selected_voice_name="پیش‌فرض (مدل انتخاب کند)"):
    if not text_to_speak:
        raise gr.Error("لطفاً متنی را برای تبدیل به صدا وارد کنید.")
    print(f"درخواست TTS برای متن: '{text_to_speak[:50]}...' با گوینده: {selected_voice_name} و مدل: models/{TTS_MODEL_NAME}")

    try:
        model = genai.GenerativeModel(f"models/{TTS_MODEL_NAME}")

        generation_config_params = {} # فعلاً بدون پارامتر خاص برای generation_config

        if selected_voice_name != "پیش‌فرض (مدل انتخاب کند)":
            print(f"توجه: انتخاب گوینده ('{selected_voice_name}') هنوز به طور کامل پیاده‌سازی نشده است.")

        generation_config_to_pass = None
        if generation_config_params:
            generation_config_to_pass = genai.types.GenerationConfig(**generation_config_params)
            print(f"ارسال درخواست به Gemini با generation_config: {generation_config_params}")
        else:
            print("ارسال درخواست به Gemini بدون generation_config خاص (با تنظیمات پیش‌فرض مدل).")

        response = model.generate_content(
            text_to_speak,
            generation_config=generation_config_to_pass
        )

        audio_bytes = None
        generated_mime_type = None
        sample_rate = 24000

        if hasattr(response, 'candidates') and response.candidates and \
           response.candidates[0].content and response.candidates[0].content.parts:
            for part in response.candidates[0].content.parts:
                if hasattr(part, 'inline_data') and part.inline_data and \
                   hasattr(part.inline_data, 'mime_type') and part.inline_data.mime_type.startswith("audio/"):
                    audio_bytes = part.inline_data.data
                    generated_mime_type = part.inline_data.mime_type
                    if ";rate=" in generated_mime_type:
                        try:
                            sample_rate = int(generated_mime_type.split(";rate=")[1])
                            print(f"نرخ نمونه‌برداری از MIME type استخراج شد: {sample_rate} Hz")
                        except:
                            print(f"خطا در استخراج نرخ نمونه‌برداری از MIME type: {generated_mime_type}. از پیش‌فرض {sample_rate} Hz استفاده می‌شود.")
                    print(f"داده صوتی با MIME type: {generated_mime_type} دریافت شد.")
                    break
        
        if audio_bytes is None:
            if hasattr(response, 'audio_content'):
                audio_bytes = response.audio_content
                generated_mime_type = "audio/wav"
                print("داده صوتی از فیلد audio_content دریافت شد.")
            else:
                print("پاسخ کامل مدل (برای دیباگ):", response)
                error_text = response.prompt_feedback if hasattr(response, 'prompt_feedback') else str(response)
                raise gr.Error(f"پاسخ صوتی از مدل دریافت نشد. پاسخ مدل: {error_text}")

        output_filename = "output.wav"
        if "pcm" in (generated_mime_type or "").lower():
            print(f"داده PCM خام ({len(audio_bytes)} بایت) با نرخ نمونه‌برداری {sample_rate} Hz دریافت شد، در حال تبدیل به WAV...")
            audio_np = np.frombuffer(audio_bytes, dtype=np.int16)
            wav_io = io.BytesIO()
            write_wav(wav_io, sample_rate, audio_np)
            wav_io.seek(0)
            with open(output_filename, "wb") as f:
                f.write(wav_io.read())
        elif audio_bytes:
             print(f"داده صوتی با فرمت {generated_mime_type} ({len(audio_bytes)} بایت) دریافت شد، مستقیم ذخیره می‌شود.")
             with open(output_filename, "wb") as f:
                f.write(audio_bytes)
        else:
            raise gr.Error("هیچ داده صوتی برای ذخیره وجود ندارد.")

        print(f"فایل صوتی در {output_filename} ذخیره شد.")
        return output_filename

    except genai.types.BlockedPromptException as bpe:
        print(f"درخواست توسط مدل بلاک شد: {bpe}")
        raise gr.Error(f"محتوای شما توسط مدل پذیرفته نشد. لطفاً متن دیگری را امتحان کنید. دلیل: {bpe}")
    except Exception as e:
        print(f"خطای کلی در تولید صدا: {e}")
        traceback.print_exc()
        error_message_from_api = ""
        # ... (بقیه کد مدیریت خطا که قبلاً داشتیم) ...
        if hasattr(e, 'args') and e.args:
            if isinstance(e.args[0], str) and "HttpError" in e.args[0]:
                error_message_from_api = str(e.args[0])
                try:
                    details_start = error_message_from_api.find('{')
                    if details_start != -1:
                        json_str_candidate = error_message_from_api[details_start:]
                        cleaned_json_str = ''.join(c for c in json_str_candidate if ord(c) >= 32 or c in ('\t','\r','\n')).strip()
                        error_obj = json.loads(cleaned_json_str)
                        if 'error' in error_obj and 'message' in error_obj['error']:
                            error_message_from_api = error_obj['error']['message']
                        elif 'message' in error_obj :
                             error_message_from_api = error_obj['message']
                except Exception as json_e:
                    print(f"خطا در parse کردن جزئیات JSON از پیام خطای API: {json_e}")
            else:
                 error_message_from_api = str(e.args[0])
        elif hasattr(e, 'message') and isinstance(e.message, str):
            error_message_from_api = e.message

        final_error_message = f"خطا در ارتباط با Gemini API یا پردازش صدا: {str(e)}"
        if error_message_from_api and error_message_from_api not in final_error_message :
            final_error_message += f" | پیام دقیق‌تر API: {error_message_from_api}"
        raise gr.Error(final_error_message)


# --- رابط کاربری 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="متن خود را اینجا بنویسید...")
            submit_button = gr.Button("🔊 تبدیل به صدا", variant="primary")
        with gr.Column(scale=1):
            audio_output = gr.Audio(label="خروجی صدا", type="filepath")
    gr.Examples(
        examples=[
            ["سلام، حال شما چطور است؟"],
            ["به دنیای هوش مصنوعی خوش آمدید."],
            ["این یک تست برای تبدیل متن به صدا با استفاده از جیمینای است."]
        ],
        inputs=[text_input]
    )
    submit_button.click(
        fn=generate_audio,
        inputs=[text_input],
        outputs=[audio_output],
        api_name="text_to_speech"
    )
    gr.Markdown("---")
    gr.Markdown(f"مدل مورد استفاده: `models/{TTS_MODEL_NAME}`") # نام مدل به روز شده را نمایش می‌دهد
    gr.Markdown("توجه: برای انتخاب گوینده‌های مختلف، نیاز به بررسی مستندات دقیق مدل TTS و بروزرسانی کد است.")

if __name__ == "__main__":
    demo.launch(debug=True)