import gradio as gr import numpy as np import tempfile import os from gtts import gTTS import librosa def text_to_speech(text, language="ko"): """Convert text to speech using Google TTS""" if not text.strip(): return None, None with tempfile.NamedTemporaryFile(suffix=".mp3", delete=False) as fp: temp_filename = fp.name tts = gTTS(text=text, lang=language, slow=False) tts.save(temp_filename) # Load the audio for visualization and playback y, sr = librosa.load(temp_filename, sr=None) return temp_filename, (sr, y) def clear_outputs(): return None, None css = """ @keyframes pulse { 0% { box-shadow: 0 0 0 0 rgba(255, 215, 0, 0.7); } 70% { box-shadow: 0 0 0 10px rgba(255, 215, 0, 0); } 100% { box-shadow: 0 0 0 0 rgba(255, 215, 0, 0); } } .playing-audio { animation: pulse 1.5s infinite; border: 2px solid #FFD700 !important; background-color: rgba(255, 215, 0, 0.1) !important; } /* 신세대 스타일 음성 박스 */ .gradio-audio { background: linear-gradient(135deg, #50C878, #4CBB17) !important; border-radius: 15px !important; padding: 15px !important; box-shadow: 0 8px 20px rgba(0, 0, 0, 0.2) !important; transition: all 0.3s ease !important; } .gradio-audio:hover { transform: translateY(-5px) !important; box-shadow: 0 12px 25px rgba(0, 0, 0, 0.25) !important; } /* 오디오 플레이어 스타일링 */ audio { border-radius: 30px !important; background-color: rgba(255, 255, 255, 0.2) !important; width: 100% !important; } /* 레이블 스타일 */ .gradio-audio label { color: white !important; font-weight: bold !important; font-size: 1.1em !important; text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.3) !important; margin-bottom: 10px !important; } /* 음성 파형 컨테이너 */ .gradio-audio .waveform-container { background-color: rgba(255, 255, 255, 0.1) !important; border-radius: 10px !important; padding: 5px !important; } /* 빨간색 버튼 스타일 */ .red-button { background-color: #FF0000 !important; color: white !important; border: none !important; font-weight: bold !important; } .red-button:hover { background-color: #CC0000 !important; } """ with gr.Blocks(theme=gr.themes.Soft(primary_hue="green"), css=css) as demo: gr.Markdown("# 한국어 텍스트 음성 변환 (TTS)") with gr.Row(): with gr.Column(scale=3): text_input = gr.Textbox( placeholder="변환할 텍스트를 입력하세요...", label="텍스트 입력", lines=5 ) with gr.Row(): lang_selector = gr.Dropdown( choices=["ko", "en", "ja", "zh-CN", "fr", "es", "de"], value="ko", label="언어 선택", info="ko: 한국어, en: 영어, ja: 일본어, zh-CN: 중국어, fr: 프랑스어, es: 스페인어, de: 독일어" ) btn = gr.Button("음성 창조", elem_classes=["red-button"]) clear_btn = gr.Button("초기화") with gr.Column(scale=3): audio_output = gr.Audio(label="생성된 음성", type="filepath") waveform = gr.Audio(label="파형", type="numpy", visible=True) # Add JavaScript for audio playback animation audio_js = """ function setupAudioAnimation() { const audioElements = document.querySelectorAll('audio'); audioElements.forEach(audio => { audio.addEventListener('play', () => { const audioContainer = audio.closest('.gradio-audio'); if (audioContainer) { audioContainer.classList.add('playing-audio'); } }); audio.addEventListener('pause', () => { const audioContainer = audio.closest('.gradio-audio'); if (audioContainer) { audioContainer.classList.remove('playing-audio'); } }); audio.addEventListener('ended', () => { const audioContainer = audio.closest('.gradio-audio'); if (audioContainer) { audioContainer.classList.remove('playing-audio'); } }); }); } // Setup initial elements setupAudioAnimation(); // Setup observer for dynamically added elements const observer = new MutationObserver((mutations) => { for (const mutation of mutations) { if (mutation.addedNodes.length) { setupAudioAnimation(); } } }); observer.observe(document.body, { childList: true, subtree: true }); """ demo.load(None, js=audio_js) btn.click( fn=text_to_speech, inputs=[text_input, lang_selector], outputs=[audio_output, waveform] ) clear_btn.click( fn=clear_outputs, inputs=[], outputs=[audio_output, waveform] ) gr.Markdown(""" ### 사용 방법 1. 변환하고 싶은 텍스트를 입력하세요 2. 언어를 선택하세요 (기본: 한국어) 3. '음성 창조' 버튼을 클릭하세요 4. 생성된 음성을 들어보세요 """) if __name__ == '__main__': demo.launch()