import gradio as gr import edge_tts import asyncio import tempfile import os from pydub import AudioSegment # ================== 1. Emotion Presets ================== EMOTION_PRESETS = { "๐Ÿ˜Š แ€•แ€ปแ€ฑแ€ฌแ€บแ€›แ€ฝแ€พแ€„แ€บ (Cheerful)": {"pitch": 5, "rate": 7, "volume": "medium"}, "๐Ÿ˜ข แ€แ€™แ€บแ€ธแ€”แ€Šแ€บแ€ธ (Sad)": {"pitch": -5, "rate": -10, "volume": "soft"}, "๐Ÿ˜  แ€’แ€ฑแ€ซแ€ž (Angry)": {"pitch": 5, "rate": 12, "volume": "loud"}, "๐Ÿ˜Œ แ€กแ€ฑแ€ธแ€†แ€ฑแ€ธ (Calm)": {"pitch": -3, "rate": -5, "volume": "medium"}, "๐ŸŽ‰ แ€…แ€ญแ€แ€บแ€œแ€พแ€ฏแ€•แ€บแ€›แ€พแ€ฌแ€ธ (Excited)": {"pitch": 7, "rate": 10, "volume": "loud"}, "๐Ÿ”ง Custom (แ€€แ€ญแ€ฏแ€šแ€บแ€แ€ญแ€ฏแ€„แ€บแ€•แ€ผแ€„แ€บแ€†แ€„แ€บ)": {"pitch": 0, "rate": 0, "volume": "medium"} } def update_emotion(emotion): preset = EMOTION_PRESETS[emotion] return gr.update(value=preset["pitch"]), gr.update(value=preset["rate"]), gr.update(value=preset["volume"]) # ================== 2. Audio Effects ================== def apply_eq(audio, bass_gain_db, treble_gain_db): if bass_gain_db != 0: bass_part = audio.low_pass_filter(250).apply_gain(bass_gain_db) audio = audio.overlay(bass_part) if treble_gain_db != 0: treble_part = audio.high_pass_filter(4000).apply_gain(treble_gain_db) audio = audio.overlay(treble_part) return audio def apply_reverb(audio, amount=50, delay_ms=80, echoes=5): if amount <= 0: return audio decay_db_per_echo = -amount / 15.0 wet = AudioSegment.silent(duration=len(audio) + echoes * delay_ms) for i in range(1, echoes+1): gain = decay_db_per_echo * i echo = audio.apply_gain(gain) silence_before = AudioSegment.silent(duration=i*delay_ms) echo_with_delay = silence_before + echo wet = wet.overlay(echo_with_delay) combined = audio.overlay(wet[:len(audio)]) return combined # ================== 3. Main TTS Pipeline (No auto breaks) ================== async def tts_pipeline(text, emotion, pitch, rate, volume_ssml, bass_gain, treble_gain, reverb_amount, output_gain, voice): pitch_str = f"+{pitch}%" if pitch >= 0 else f"{pitch}%" rate_str = f"+{rate}%" if rate >= 0 else f"{rate}%" ssml = f""" {text} """ communicate = edge_tts.Communicate(ssml, voice=voice) tmp_mp3 = tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") await communicate.save(tmp_mp3.name) audio = AudioSegment.from_mp3(tmp_mp3.name) os.unlink(tmp_mp3.name) audio = apply_eq(audio, bass_gain, treble_gain) audio = apply_reverb(audio, reverb_amount) audio = audio.apply_gain(output_gain) out_mp3 = tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") audio.export(out_mp3.name, format="mp3") return out_mp3.name # ================== 4. Gradio Interface ================== with gr.Blocks(title="Myanmar Emotional TTS") as demo: gr.Markdown("## ๐ŸŽ™๏ธ แ€™แ€ผแ€”แ€บแ€™แ€ฌ Emotional TTS (Edge TTS + Effects)") gr.Markdown("แ€…แ€ฌแ€›แ€ญแ€ฏแ€€แ€บแ€‘แ€Šแ€ทแ€บแ€•แ€ซแŠ Emotion แ€›แ€ฝแ€ฑแ€ธแ€•แ€ซแŠ Pitch/Rate แ€€แ€ญแ€ฏ แ€€แ€ญแ€ฏแ€šแ€บแ€แ€ญแ€ฏแ€„แ€บแ€Šแ€พแ€ญแ€•แ€ผแ€ฎแ€ธ แ€กแ€žแ€ถแ€กแ€€แ€ปแ€ญแ€ฏแ€ธแ€žแ€€แ€บแ€›แ€ฑแ€ฌแ€€แ€บแ€™แ€พแ€ฏแ€™แ€ปแ€ฌแ€ธ (EQ, Reverb, Volume) แ€‘แ€Šแ€ทแ€บแ€”แ€ญแ€ฏแ€„แ€บแ€•แ€ซแ€แ€šแ€บแ‹") with gr.Row(): with gr.Column(scale=2): text_input = gr.Textbox(label="๐Ÿ“ แ€™แ€ผแ€”แ€บแ€™แ€ฌแ€…แ€ฌแ€žแ€ฌแ€ธ", lines=5, placeholder="แ€’แ€ฎแ€™แ€พแ€ฌ แ€…แ€ฌแ€›แ€ญแ€ฏแ€€แ€บแ€‘แ€Šแ€ทแ€บแ€•แ€ซ...") with gr.Column(scale=1): voice = gr.Dropdown(label="๐ŸŽค แ€กแ€žแ€ถ", choices=["my-MM-NilarNeural", "my-MM-ThihaNeural"], value="my-MM-NilarNeural") emotion = gr.Dropdown(label="๐ŸŽญ Emotion", choices=list(EMOTION_PRESETS.keys()), value="๐Ÿ˜Š แ€•แ€ปแ€ฑแ€ฌแ€บแ€›แ€ฝแ€พแ€„แ€บ (Cheerful)") with gr.Row(): pitch = gr.Slider(label="๐ŸŽต Pitch (%)", minimum=-20, maximum=20, value=5, step=1) rate = gr.Slider(label="โฉ Rate (%)", minimum=-20, maximum=20, value=7, step=1) volume_ssml = gr.Dropdown(label="๐Ÿ”Š Volume (SSML)", choices=["x-soft","soft","medium","loud","x-loud"], value="medium") gr.Markdown("### ๐ŸŽš๏ธ Audio Effects") with gr.Row(): bass_gain = gr.Slider(label="๐ŸŽง Bass Gain (dB)", minimum=-10, maximum=10, value=0, step=1) treble_gain = gr.Slider(label="๐ŸŽง Treble Gain (dB)", minimum=-10, maximum=10, value=0, step=1) with gr.Row(): reverb_amount = gr.Slider(label="๐ŸŒŠ Reverb Amount", minimum=0, maximum=100, value=0, step=1) output_gain = gr.Slider(label="๐Ÿ”ˆ Output Volume (dB)", minimum=-10, maximum=20, value=0, step=1) generate_btn = gr.Button("โ–ถ๏ธ แ€กแ€žแ€ถแ€‘แ€ฏแ€แ€บแ€™แ€šแ€บ", variant="primary") audio_output = gr.Audio(label="๐Ÿ”Š แ€›แ€œแ€ฌแ€แ€ฒแ€ท แ€กแ€žแ€ถ") emotion.change( fn=update_emotion, inputs=emotion, outputs=[pitch, rate, volume_ssml] ) generate_btn.click( fn=tts_pipeline, inputs=[text_input, emotion, pitch, rate, volume_ssml, bass_gain, treble_gain, reverb_amount, output_gain, voice], outputs=audio_output ) if __name__ == "__main__": demo.launch()