File size: 2,958 Bytes
4b22121
 
 
 
253b483
4b22121
253b483
 
4b22121
253b483
 
 
4b22121
7f9ef3b
 
 
 
 
9ae478f
 
4b22121
 
 
 
253b483
 
 
 
4b22121
33650f8
 
4b22121
 
 
253b483
 
 
dcedafb
 
 
7f9ef3b
 
 
 
63b0c66
579e57f
63b0c66
 
 
 
 
7f9ef3b
63b0c66
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7f9ef3b
 
63b0c66
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
import asyncio
import edge_tts
import gradio as gr
import os
from datetime import datetime

AUDIO_DIR = "saved_audios"
os.makedirs(AUDIO_DIR, exist_ok=True)

def generate_unique_filename(folder, prefix="audio", ext="mp3"):
    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S_%f")
    return os.path.join(folder, f"{prefix}_{timestamp}.{ext}")

async def get_voices():
    # 取得 Edge TTS 可用語音清單
    voices = await edge_tts.list_voices()
    return [voice["ShortName"] for voice in voices]

async def generate_speech(text, voice, rate, pitch, folder=AUDIO_DIR):
    output_file = generate_unique_filename(folder)
    communicate = edge_tts.Communicate(text, voice, rate=rate, pitch=pitch)
    await communicate.save(output_file)
    return output_file

def list_saved_audios():
    files = sorted(os.listdir(AUDIO_DIR), reverse=True)
    return [os.path.join(AUDIO_DIR, f) for f in files if f.endswith(".mp3")]

async def tts_interface(text, voice, rate_percentage, pitch_hz):
    rate = f"{'+' if rate_percentage >= 0 else ''}{rate_percentage}%"
    pitch = f"{'+' if pitch_hz >= 0 else ''}{pitch_hz}Hz"
    audio_path = await generate_speech(text, voice, rate, pitch)
    return audio_path

def play_saved_audio(audio_file):
    return audio_file

def clear_textbox():
    return ""

# 先同步取得語音清單,避免 async 問題
voices = asyncio.run(get_voices())
default_voice = "zh-CN-XiaoxiaoNeural" if "zh-CN-XiaoxiaoNeural" in voices else voices[0]

with gr.Blocks(theme=gr.themes.Soft()) as demo:
    gr.Markdown("## 🎙️ Edge TTS 語音合成工具\n\n- 支援語音合成、語音檔自動儲存與播放\n- 介面簡潔、操作直覺")

    with gr.Tab("語音合成"):
        with gr.Row():
            text_input = gr.Textbox(lines=5, label="輸入文本")
            clear_btn = gr.Button("清空")
        voice_input = gr.Dropdown(voices, value=default_voice, label="選擇語音")
        rate_input = gr.Slider(-50, 50, value=0, step=1, label="語速調整 (%)")
        pitch_input = gr.Slider(-50, 50, value=0, step=1, label="音高調整 (Hz)")
        tts_btn = gr.Button("生成語音")
        audio_output = gr.Audio(type="filepath", label="生成的語音")
        tts_btn.click(
            fn=tts_interface,
            inputs=[text_input, voice_input, rate_input, pitch_input],
            outputs=audio_output
        )
        clear_btn.click(fn=clear_textbox, outputs=text_input)

    with gr.Tab("檢視已儲存語音"):
        audio_files = gr.Dropdown(list_saved_audios(), label="選擇已儲存語音檔案", interactive=True)
        saved_audio_output = gr.Audio(type="filepath", label="播放已儲存語音")
        audio_files.change(fn=play_saved_audio, inputs=audio_files, outputs=saved_audio_output)

    # 若要每次開啟都刷新語音檔案清單,可加下列一行
    # demo.load(lambda: gr.Dropdown.update(choices=list_saved_audios()), None, audio_files)

demo.launch()