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() |