import gradio as gr import asyncio import edge_tts import re import os import uuid try: from pydub import AudioSegment from pydub.silence import detect_nonsilent except ImportError: raise ImportError("ကျေးဇူးပြု၍ requirements.txt တွင် pydub ထည့်ပေးပါ။") def format_srt_time(seconds): if seconds < 0: seconds = 0 millis = int(seconds * 1000) hours = millis // 3600000 millis %= 3600000 minutes = millis // 60000 millis %= 60000 secs = millis // 1000 millis %= 1000 return f"{hours:02d}:{minutes:02d}:{secs:02d},{millis:03d}" def segment_myanmar_text(raw_text, max_chars=40): for char in ["“", "”", '"', "‘", "’", "'", "--", "—", "…"]: raw_text = raw_text.replace(char, " ") raw_text = re.sub(r"[ \t]+", " ", raw_text).strip() split_pattern = re.split(r'([။၊!?\n]+)', raw_text) temp_chunks = [] current_chunk = "" for item in split_pattern: current_chunk += item if any(p in item for p in ['။', '၊', '?', '!', '\n']): clean_chunk = current_chunk.strip() if len(re.sub(r'[\s၊။!?\-\"\']', '', clean_chunk)) > 0: temp_chunks.append(clean_chunk) current_chunk = "" if current_chunk.strip(): clean_chunk = current_chunk.strip() if len(re.sub(r'[\s၊။!?\-\"\']', '', clean_chunk)) > 0: temp_chunks.append(clean_chunk) final_segments = [] for chunk in temp_chunks: if len(chunk) <= max_chars: final_segments.append(chunk) else: sub_words = chunk.split(' ') line_buffer = "" for word in sub_words: if len(line_buffer) + len(word) + 1 <= max_chars: line_buffer += (" " if line_buffer else "") + word else: if line_buffer: final_segments.append(line_buffer.strip()) if len(word) > max_chars: for i in range(0, len(word), max_chars): final_segments.append(word[i:i+max_chars]) line_buffer = "" else: line_buffer = word if line_buffer: final_segments.append(line_buffer.strip()) return final_segments async def process_voice_generation( text, surveyed_text, filename, s1_voice, s2_voice, s3_voice, style, srt_type, tone, speed, volume, progress=gr.Progress() ): if not text.strip(): return None, None, None processed_text = text.replace("--", " ").replace("—", " ").replace("…", " ") if surveyed_text.strip(): for line in surveyed_text.strip().split("\n"): if "=" in line: key, val = line.split("=", 1); processed_text = processed_text.replace(key.strip(), val.strip()) # အသံမြန်နှုန်း၊ အသံအတိုးအကျယ် ချိန်ညှိချက် base_speed = speed + 20 speed_rate = f"{'+' if base_speed >= 0 else ''}{base_speed}%" volume_rate = f"+{volume}%" # ဖိုင်အမည် သတ်မှတ်ခြင်း output_name = filename.strip() if filename.strip() else "Myanmar_TTS" output_mp3 = f"{output_name}.mp3" output_srt = f"{output_name}.srt" max_srt_chars = 40 if srt_type == "TikTok" else 70 sentences = segment_myanmar_text(processed_text, max_chars=max_srt_chars) total_sentences = len(sentences) if total_sentences == 0: return None, None, None progress(0.2, desc=f"⏳ စာသားများကို အသံအဖြစ် ပြောင်းလဲနေပါသည် ({total_sentences} ကြောင်း)...") sem = asyncio.Semaphore(15) # Multi-voice selection logic voice_map = { "သီဟ (🇲🇲 - ကျား)": "my-MM-ThihaNeural", "နီလာ (🇲🇲 - မ)": "my-MM-NilarNeural", "စမူပိုင်ကြီး (🇲🇲 - ကျား)": "my-MM-ThihaNeural" } async def fetch_audio(idx, sentence): async with sem: try: # စာကြောင်းအလိုက် Voice ခွဲပေးခြင်း (ဥပမာ - စာကြောင်းအလိုက် Voice 1, Voice 2 လှည့်သုံးချင်ရင်) if idx % 3 == 0: selected_voice = voice_map.get(s1_voice, "my-MM-ThihaNeural") elif idx % 3 == 1: selected_voice = voice_map.get(s2_voice, "my-MM-NilarNeural") else: selected_voice = voice_map.get(s3_voice, "my-MM-ThihaNeural") communicate = edge_tts.Communicate( text=sentence, voice=selected_voice, rate=speed_rate, volume=volume_rate ) chunk_audio = bytearray() async for msg in communicate.stream(): if msg["type"] == "audio": chunk_audio.extend(msg["data"]) return idx, chunk_audio except Exception as e: print(f"Skipping server error on sentence {idx}: {e}") return idx, b"" tasks = [fetch_audio(idx, s) for idx, s in enumerate(sentences)] audio_results = await asyncio.gather(*tasks) audio_results.sort(key=lambda x: x[0]) progress(0.7, desc="⚙️ အသံနှင့် စာတန်းထိုးကို အချောသတ် ချိန်ညှိနေပါသည်...") combined_audio = AudioSegment.empty() subtitles = [] current_time_sec = 0.0 natural_pause = AudioSegment.silent(duration=150) # အချင်းချင်း ဖိုင်နာမည် မထပ်အောင် User session အလိုက် random id သုံးပေးထားပါတယ် session_id = str(uuid.uuid4())[:8] for idx, chunk_audio in audio_results: if not chunk_audio: continue temp_file = f"temp_{session_id}_{idx}.mp3" with open(temp_file, "wb") as f: f.write(chunk_audio) try: segment = AudioSegment.from_mp3(temp_file) nonsilent_ranges = detect_nonsilent(segment, min_silence_len=50, silence_thresh=-50) if nonsilent_ranges: start_trim = nonsilent_ranges[0][0] end_trim = nonsilent_ranges[-1][1] segment = segment[start_trim:end_trim] segment = segment + natural_pause duration_sec = len(segment) / 1000.0 if duration_sec > 0: end_time = current_time_sec + duration_sec subtitles.append({ "start": current_time_sec, "end": end_time, "text": sentences[idx] }) current_time_sec = end_time combined_audio += segment except Exception as e: print(f"Error processing line {idx}: {e}") finally: if os.path.exists(temp_file): os.remove(temp_file) progress(0.9, desc="💾 ဖိုင်များကို သိမ်းဆည်းနေပါသည်...") if len(combined_audio) == 0: return None, None, None combined_audio.export(output_mp3, format="mp3") with open(output_srt, "w", encoding="utf-8-sig") as f: for i, sub in enumerate(subtitles, start=1): f.write(f"{i}\n") f.write(f"{format_srt_time(sub['start'])} --> {format_srt_time(sub['end'])}\n") f.write(sub["text"].strip()) f.write("\n\n") progress(1.0, desc="✅ အားလုံး ပြီးစီးပါပြီ!") return output_mp3, output_mp3, output_srt def tts_wrapper(text, rules_text, filename, s1_voice, s2_voice, s3_voice, style, srt_type, tone, speed, volume, progress=gr.Progress()): return asyncio.run(tts_wrapper_async(text, rules_text, filename, s1_voice, s2_voice, s3_voice, style, srt_type, tone, speed, volume, progress)) async def tts_wrapper_async(text, rules_text, filename, s1_voice, s2_voice, s3_voice, style, srt_type, tone, speed, volume, progress=gr.Progress()): return await process_voice_generation(text, surveyed_text=rules_text, filename=filename, s1_voice=s1_voice, s2_voice=s2_voice, s3_voice=s3_voice, style=style, srt_type=srt_type, tone=tone, speed=speed, volume=volume, progress=progress) custom_css = """ .large-btn { font-size: 24px !important; font-weight: bold !important; padding: 15px !important; margin-bottom: 15px !important; } .text-link-box { text-align: center; margin-bottom: 20px; padding: 10px; background-color: #1E293B; border-radius: 8px; } .text-link-box p { font-size: 20px !important; margin: 5px 0 !important; font-weight: 500; } .text-link-box a { color: #38BDF8 !important; text-decoration: underline !important; font-weight: bold !important; font-size: 24px !important; } """ with gr.Blocks(title="Myanmar Audio Studio", css=custom_css) as demo: gr.Markdown("

🎙️ Myanmar Audio & SRT Studio

") with gr.Column(): free_status_btn = gr.Button("🆓 TTS and SRT Free Version", variant="primary", interactive=False, elem_classes=["large-btn"]) gr.HTML( "" ) gr.Markdown("
") with gr.Group() as tts_content: with gr.Accordion("🔧 အသံထွက် ပြင်ဆင်ရန် (Pronunciation Rules)", open=False): rules = gr.TextArea(value="", placeholder="ဥပမာ - စာသား = အသံထွက်", lines=4, show_label=False) file_name = gr.Textbox(label="💾 သိမ်းဆည်းမည့် ဖိုင်အမည်", value="Myanmar_TTS") voice_choices = ["သီဟ (🇲🇲 - ကျား)", "နီလာ (🇲🇲 - မ)", "စမူပိုင်ကြီး (🇲🇲 - ကျား)"] with gr.Row(): s1_voice = gr.Dropdown(voice_choices, label="🎙️ [S1] အသံ", value="သီဟ (🇲🇲 - ကျား)") s2_voice = gr.Dropdown(voice_choices, label="🎙️ [S2] အသံ", value="နီလာ (🇲🇲 - မ)") s3_voice = gr.Dropdown(voice_choices, label="🎙️ [S3] အသံ", value="စမူပိုင်ကြီး (🇲🇲 - ကျား)") with gr.Row(): style = gr.Dropdown(["Normal (ပုံမှန်)"], label="🎭 အသံစတိုင်လ်", value="Normal (ပုံမှန်)") srt_type = gr.Radio(["TikTok", "YouTube"], label="စာတန်းထိုး အမျိုးအစား", value="TikTok") with gr.Accordion("⚙️ အဆင့်မြင့် ဆက်တင်များ", open=False): tone = gr.Slider(minimum=-50, maximum=50, value=0, step=1, label="Tone") speed = gr.Slider(minimum=-50, maximum=50, value=0, step=1, label="Speed") volume = gr.Slider(minimum=0, maximum=100, value=50, step=1, label="Volume (+%)") input_text = gr.Textbox(label="စာသား", placeholder="မင်္ဂလာပါ။", lines=4) generate_btn = gr.Button("🚀 အသံဖိုင် ဖန်တီးမည် (Generate)", variant="primary") output_audio = gr.Audio(label="🎧 ထွက်လာသော အသံဖိုင်", type="filepath") output_mp3_file = gr.File(label="💾 MP3 ဒေါင်းလုဒ်") output_srt_file = gr.File(label="📝 SRT ဒေါင်းလုဒ်") generate_btn.click( fn=tts_wrapper, inputs=[input_text, rules, file_name, s1_voice, s2_voice, s3_voice, style, srt_type, tone, speed, volume], outputs=[output_audio, output_mp3_file, output_srt_file] ) demo.launch()