Spaces:
Sleeping
Sleeping
| import tempfile | |
| import edge_tts | |
| import gradio as gr | |
| import asyncio | |
| # --- Final, VERIFIED Language & Voice Configuration --- | |
| language_dict = { | |
| "English": { | |
| "Jenny (Female, US)": "en-US-JennyNeural", | |
| "Andrew (Male, US)": "en-US-AndrewNeural", | |
| "Sonia (Female, UK)": "en-GB-SoniaNeural", | |
| "Ryan (Male, UK)": "en-GB-RyanNeural" | |
| }, | |
| "Amharic": { | |
| "Mekdes (Female)": "am-ET-MekdesNeural", | |
| "Ameha (Male)": "am-ET-AmehaNeural" | |
| }, | |
| "Tigrinya": { | |
| # WORKAROUND: Using Amharic voices as a fallback for Tigrinya. | |
| "Lulia (Female)": "am-ET-MekdesNeural", | |
| "Birhane (Male)": "am-ET-AmehaNeural" | |
| }, | |
| "Oromo": { | |
| # This is a mock-up. It uses Swahili voices as a fallback. | |
| "Zuri (Female)": "sw-KE-ZuriNeural", | |
| "Rafiki (Male)": "sw-KE-RafikiNeural" | |
| }, | |
| "Somali": { | |
| "Ubax (Female)": "so-SO-UbaxNeural", | |
| "Muuse (Male)": "so-SO-MuuseNeural" | |
| }, | |
| "Arabic": { | |
| "Zariyah (Female, KSA)": "ar-SA-ZariyahNeural", | |
| "Hamed (Male, KSA)": "ar-SA-HamedNeural" | |
| }, | |
| "French": { | |
| "Denise (Female)": "fr-FR-DeniseNeural", | |
| "Henri (Male)": "fr-FR-HenriNeural" | |
| }, | |
| "German": { | |
| "Katja (Female)": "de-DE-KatjaNeural", | |
| "Conrad (Male)": "de-DE-ConradNeural" | |
| }, | |
| "Italian": { | |
| "Elsa (Female)": "it-IT-ElsaNeural", | |
| "Diego (Male)": "it-IT-DiegoNeural" | |
| }, | |
| "Japanese": { | |
| "Nanami (Female)": "ja-JP-NanamiNeural", | |
| "Keita (Male)": "ja-JP-KeitaNeural" | |
| }, | |
| "Korean": { | |
| "Sun-Hi (Female)": "ko-KR-SunHiNeural", | |
| "InJoon (Male)": "ko-KR-InJoonNeural" | |
| }, | |
| "Chinese (Simplified)": { | |
| "Xiaoxiao (Female)": "zh-CN-XiaoxiaoNeural", | |
| "Yunxi (Male)": "zh-CN-YunxiNeural" | |
| }, | |
| "Chinese (Traditional)": { | |
| "HsiaoChen (Female)": "zh-TW-HsiaoChenNeural", | |
| "YunJhe (Male)": "zh-TW-YunJheNeural" | |
| } | |
| } | |
| async def text_to_speech_edge(text, language, speaker): | |
| try: | |
| voice = language_dict[language][speaker] | |
| except KeyError: | |
| raise gr.Error(f"Error: Voice '{speaker}' not found for {language}.") | |
| try: | |
| communicate = edge_tts.Communicate(text, voice) | |
| with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as tmp_file: | |
| tmp_path = tmp_file.name | |
| await asyncio.wait_for(communicate.save(tmp_path), timeout=60) | |
| return tmp_path | |
| except asyncio.TimeoutError: | |
| raise gr.Error("Error: Request timed out. Please try again.") | |
| except Exception as e: | |
| raise gr.Error(f"An unexpected error occurred: {str(e)}") | |
| def update_speakers(language): | |
| speakers = list(language_dict.get(language, [])) | |
| return gr.Dropdown(choices=speakers, value=speakers[0] if speakers else None, interactive=True) | |
| # --- Gradio Interface --- | |
| with gr.Blocks(title="SelamGPT TTS", theme=gr.themes.Soft()) as demo: | |
| gr.Markdown("# SelamGPT Text-to-Speech") | |
| with gr.Row(): | |
| language = gr.Dropdown( | |
| choices=list(language_dict.keys()), | |
| value="Amharic", | |
| label="Language" | |
| ) | |
| speaker = gr.Dropdown( | |
| label="Speaker", | |
| choices=list(language_dict["Amharic"].keys()), | |
| value="Mekdes (Female)" | |
| ) | |
| with gr.Column(): | |
| input_text = gr.Textbox(label="Input Text", placeholder="Enter text here...") | |
| generate_btn = gr.Button("Generate Audio", variant="primary") | |
| output_audio = gr.Audio(label="Output Audio", autoplay=True) | |
| language.change(fn=update_speakers, inputs=language, outputs=speaker) | |
| generate_btn.click(fn=text_to_speech_edge, inputs=[input_text, language, speaker], outputs=output_audio) | |
| if __name__ == "__main__": | |
| demo.launch() |