Spaces:
Running
Running
| import gradio as gr | |
| import uuid | |
| import os | |
| import gzip | |
| import subprocess | |
| import datetime | |
| import time | |
| import soundfile as sf | |
| from app_utils import tts_interface, models | |
| # === Only keep the 'برنا' model === | |
| borna_model = next(model for model in models if "برنا" in model[0] or "برنا" in model[2]) | |
| voice_id = borna_model[2] | |
| voice_url = borna_model[3] | |
| # === Paths === | |
| STATIC_DIR = "static" | |
| os.makedirs(STATIC_DIR, exist_ok=True) | |
| PHONETIC_FILE = os.path.join(STATIC_DIR, "fa_extra.txt.gz") | |
| # === TTS Function === | |
| def tts(text): | |
| # Save input text for phonemizer | |
| input_file = os.path.join(STATIC_DIR, "input.txt") | |
| with open(input_file, "w", encoding="utf-8") as f: | |
| f.write(text.strip()) | |
| # Run espeak-ng to get phonemes | |
| phonemes_result = subprocess.run( | |
| ["espeak-ng", "-v", "fa", "-x", "-q", "-f", input_file], | |
| capture_output=True, | |
| text=True | |
| ) | |
| phonemes = phonemes_result.stdout.strip() | |
| # Run TTS | |
| (sample_rate, audio_data), _ = tts_interface(voice_id, text.strip(), '') | |
| out_path = os.path.join(STATIC_DIR, f"{uuid.uuid4().hex}.wav") | |
| sf.write(out_path, audio_data, samplerate=sample_rate, subtype="PCM_16") | |
| status = f"مدل: {voice_url}\nآوانگاشت: {phonemes}" | |
| return out_path, status | |
| # === Phonemizer + Save === | |
| def phonemize(word, phonetic, task): | |
| if task == "phonemize" or not phonetic.strip(): | |
| phoneme_target = word | |
| else: | |
| phoneme_target = f"[[{phonetic.strip()}]]" | |
| input_file = os.path.join(STATIC_DIR, "input.txt") | |
| with open(input_file, "w", encoding="utf-8") as f: | |
| f.write(phoneme_target) | |
| result = subprocess.run( | |
| ["espeak-ng", "-v", "fa", "-x", "-q", "-f", input_file], | |
| capture_output=True, | |
| text=True | |
| ) | |
| phonemes = result.stdout.strip() | |
| out_path = os.path.join(STATIC_DIR, f"{uuid.uuid4().hex}.wav") | |
| subprocess.run(f'espeak-ng -v fa -w "{out_path}" -f "{input_file}"', shell=True) | |
| if task == "send" and phonetic.strip(): | |
| with gzip.open(PHONETIC_FILE, "at", encoding="utf-8") as f: | |
| f.write(f"{word.strip()}\t{phonetic.strip()}\n") | |
| status = f"متن: {word}\nآوانگاشت: {phonemes}" | |
| return out_path, phonemes, status | |
| # === List saved words === | |
| def list_words(): | |
| entries = [] | |
| with gzip.open(PHONETIC_FILE, "rt", encoding="utf-8") as f: | |
| for line in f: | |
| if not line.startswith("//"): | |
| parts = line.strip().split("\t") | |
| if len(parts) == 2: | |
| entries.append((parts[0], parts[1])) | |
| return entries | |
| # === Iran time without pytz === | |
| def get_iran_time(): | |
| offset = 3.5 * 3600 | |
| utc = time.gmtime() | |
| iran_time = time.localtime(time.mktime(utc) + offset) | |
| return datetime.datetime(*iran_time[:6]).strftime("%Y-%m-%d %H:%M:%S") | |
| # Log start | |
| with gzip.open(PHONETIC_FILE, "at", encoding="utf-8") as f: | |
| f.write(f"// started at : {get_iran_time()}\n") | |
| # === UI === | |
| with gr.Blocks(title="TTS for Persian with Bornaa") as demo: | |
| gr.Markdown("## متن به گفتار 🗣️") | |
| with gr.Tab("تبدیل متن به گفتار"): | |
| text_input = gr.Textbox(label="متن فارسی", lines=2) | |
| out_audio = gr.Audio(label="خروجی صوتی") | |
| out_status = gr.Textbox(label="وضعیت مدل", interactive=False) | |
| btn_tts = gr.Button("تبدیل کن") | |
| btn_tts.click(tts, inputs=text_input, outputs=[out_audio, out_status]) | |
| with gr.Tab("ویرایش تلفظ"): | |
| word_input = gr.Textbox(label="کلمه", placeholder="مثلاً: سلام") | |
| phonetic_input = gr.Textbox(label="آوانویسی دلخواه (اختیاری)") | |
| task_choice = gr.Radio(label="عملیات", choices=["phonemize", "send"], value="phonemize") | |
| ph_audio = gr.Audio(label="صدای تولیدشده") | |
| ph_output = gr.Textbox(label="آوانگاشت") | |
| ph_status = gr.Textbox(label="وضعیت", interactive=False) | |
| btn_phonemize = gr.Button("انجام") | |
| btn_phonemize.click(phonemize, | |
| inputs=[word_input, phonetic_input, task_choice], | |
| outputs=[ph_audio, ph_output, ph_status]) | |
| with gr.Tab("واژههای اخیر"): | |
| gr.Markdown("### واژههای ذخیرهشده") | |
| word_table = gr.Dataframe(headers=["کلمه", "آوانویسی"], datatype=["str", "str"]) | |
| btn_load_words = gr.Button("بارگذاری") | |
| btn_load_words.click(fn=list_words, inputs=[], outputs=[word_table]) | |
| demo.launch() |