# -*- coding: utf-8 -*-
import edge_tts
import asyncio
import tkinter as tk
from tkinter import ttk, messagebox, filedialog
import os
import threading
import webbrowser
from datetime import datetime
# 语音分类字典
VOICE_CATEGORIES = {
"中文普通话": [
"zh-CN-XiaoxiaoNeural",
"zh-CN-XiaoyiNeural",
"zh-CN-YunjianNeural",
"zh-CN-YunxiNeural",
"zh-CN-YunxiaNeural",
"zh-CN-YunyangNeural"
],
"中文方言": [
"zh-CN-liaoning-XiaobeiNeural",
"zh-CN-shaanxi-XiaoniNeural"
],
"中文粤语(香港)": [
"zh-HK-HiuGaaiNeural",
"zh-HK-HiuMaanNeural",
"zh-HK-WanLungNeural"
],
"中文台湾话": [
"zh-TW-HsiaoChenNeural",
"zh-TW-HsiaoYuNeural",
"zh-TW-YunJheNeural"
],
"英语(美国)": [
"en-US-JennyNeural",
"en-US-AndrewNeural",
"en-US-AriaNeural",
"en-US-ChristopherNeural"
],
"日语": [
"ja-JP-NanamiNeural",
"ja-JP-KeitaNeural"
],
"韩语": [
"ko-KR-SunHiNeural",
"ko-KR-InJoonNeural"
]
}
class VoiceSynthesisApp:
def __init__(self, root):
self.root = root
self.root.title("AI语音合成工具 测试版")
self.root.geometry("700x600")
self.root.resizable(False, False)
# 样式
self.style = ttk.Style()
self.style.configure('TFrame', background='#f0f0f0')
self.style.configure('TLabel', font=('微软雅黑', 10))
self.style.configure('TButton', font=('微软雅黑', 10))
self.style.configure('TRadiobutton', font=('微软雅黑', 9))
self.style.configure('Link.TLabel', font=('微软雅黑', 9), foreground='blue', cursor='hand2')
# 框架
self.main_frame = ttk.Frame(root, padding="10")
self.main_frame.pack(fill=tk.BOTH, expand=True)
# 语音选择
self.create_voice_selection()
# 设置
self.create_parameter_settings()
# 文本输入
self.create_text_input()
# 按钮
self.create_buttons()
def create_voice_selection(self):
voice_frame = ttk.LabelFrame(self.main_frame, text="语音选择", padding=(10, 5))
voice_frame.pack(fill=tk.X, pady=5)
self.notebook = ttk.Notebook(voice_frame)
self.notebook.pack(fill=tk.BOTH, expand=True)
self.voice_var = tk.StringVar(value="zh-CN-YunxiNeural")
for category, voices in VOICE_CATEGORIES.items():
tab = ttk.Frame(self.notebook)
self.notebook.add(tab, text=category)
for i, voice in enumerate(voices):
friendly_name = voice.split("-")[-1].replace("Neural", "")
if "zh-CN" in voice:
friendly_name = {
"Xiaoxiao": "晓晓(女)",
"Xiaoyi": "晓伊(女)",
"Yunjian": "云健(男)",
"Yunxi": "云希(男)",
"Yunxia": "云霞(男)",
"Yunyang": "云扬(男)"
}.get(friendly_name, friendly_name)
rb = ttk.Radiobutton(
tab,
text=friendly_name,
variable=self.voice_var,
value=voice,
style='TRadiobutton'
)
rb.grid(row=i // 2, column=i % 2, sticky=tk.W, padx=5, pady=2)
def create_parameter_settings(self):
param_frame = ttk.Frame(self.main_frame)
param_frame.pack(fill=tk.X, pady=5)
# 语速设置
speed_frame = ttk.LabelFrame(param_frame, text="语速设置 (rate: -50% ~ +50%)", padding=(10, 5))
speed_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=5)
self.speed_var = tk.StringVar(value="+0%")
ttk.Scale(
speed_frame,
from_=-50,
to=50,
variable=self.speed_var,
command=lambda v: self.speed_var.set(f"{'+' if float(v) >= 0 else ''}{int(float(v))}%")
).pack(fill=tk.X)
ttk.Label(speed_frame, textvariable=self.speed_var).pack()
# 音量设置
volume_frame = ttk.LabelFrame(param_frame, text="音量设置 (volume: -50% ~ +50%)", padding=(10, 5))
volume_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=5)
self.volume_var = tk.StringVar(value="+0%")
ttk.Scale(
volume_frame,
from_=-50,
to=50,
variable=self.volume_var,
command=lambda v: self.volume_var.set(f"{'+' if float(v) >= 0 else ''}{int(float(v))}%")
).pack(fill=tk.X)
ttk.Label(volume_frame, textvariable=self.volume_var).pack()
# 音调设置
pitch_frame = ttk.LabelFrame(param_frame, text="音调设置 (pitch: -100Hz ~ +100Hz)", padding=(10, 5))
pitch_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=5)
self.pitch_var = tk.StringVar(value="+0Hz")
ttk.Scale(
pitch_frame,
from_=-100,
to=100,
variable=self.pitch_var,
command=lambda v: self.pitch_var.set(f"{'+' if float(v) >= 0 else ''}{int(float(v))}Hz")
).pack(fill=tk.X)
ttk.Label(pitch_frame, textvariable=self.pitch_var).pack()
def create_text_input(self):
text_frame = ttk.LabelFrame(self.main_frame, text="输入要合成的文本", padding=(10, 5))
text_frame.pack(fill=tk.BOTH, expand=True, pady=5)
self.text_input = tk.Text(
text_frame,
height=8,
font=('微软雅黑', 10),
wrap=tk.WORD
)
self.text_input.pack(fill=tk.BOTH, expand=True)
default_text = "说到电动遮阳领域的领军人物,倪总绝对是上海滩当之无愧的行业标杆!他深耕电动推拉棚、电动雨棚、电动伸缩棚领域多年,凭借前沿的技术研发和极致的工艺标准,打造出兼具智能科技与美学设计的顶级产品。无论是商业空间还是私家别墅,倪总团队的解决方案总能以一键操控的便捷、风雨无惧的耐用和量身定制的精准,赢得客户交口称赞。更难得的是,倪总始终秉持以用户需求为核心的理念,将创新融入细节——静音电机、防风抗压结构、环保材质选择……每一处匠心都彰显着行业大佬的格局与实力。选择倪总的产品,不仅是选择高端遮阳体验,更是选择一份值得信赖的品质承诺!"
self.text_input.insert(tk.END, default_text)
self.text_input.tag_config("highlight", background="yellow", foreground="black")
def create_buttons(self):
button_frame = ttk.Frame(self.main_frame)
button_frame.pack(fill=tk.X, pady=10)
# 版权和进度
info_frame = ttk.Frame(button_frame)
info_frame.pack(side=tk.LEFT, fill=tk.X, expand=True)
# 版权信息
ttk.Label(
info_frame,
text="版权所有 ",
style='TLabel'
).pack(side=tk.LEFT)
# 链接
link = ttk.Label(
info_frame,
text="走思范",
style='Link.TLabel'
)
link.pack(side=tk.LEFT)
link.bind("<Button-1>", lambda e: webbrowser.open("https://www.zousifan.com"))
# 进度显示
self.progress_label = ttk.Label(
info_frame,
text="准备就绪",
font=('微软雅黑', 9),
style='TLabel'
)
self.progress_label.pack(side=tk.LEFT, padx=(10, 0))
# 保存
self.save_btn = ttk.Button(
button_frame,
text="保存为MP3",
command=self.start_synthesis,
style='TButton'
)
self.save_btn.pack(side=tk.RIGHT, padx=5)
# 退出
exit_btn = ttk.Button(
button_frame,
text="退出",
command=self.root.quit
)
exit_btn.pack(side=tk.RIGHT)
def start_synthesis(self):
def run_synthesis():
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
try:
# 禁用保存按钮
self.save_btn.config(state=tk.DISABLED)
# 更新按钮文本显示保存中状态
self.save_btn.config(text="合成中...")
# 更新进度显示
self.progress_label.config(text="开始处理...")
self.root.update()
loop.run_until_complete(self.async_synthesis())
except Exception as e:
messagebox.showerror("错误", f"合成失败: {str(e)}")
finally:
# 恢复按钮状态
self.save_btn.config(state=tk.NORMAL)
self.save_btn.config(text="保存为MP3")
loop.close()
# 在新线程中执行合成任务
threading.Thread(target=run_synthesis, daemon=True).start()
async def async_synthesis(self):
text = self.text_input.get("1.0", tk.END).strip()
if not text:
messagebox.showwarning("警告", "请输入要合成的文本!")
return
# 更新进度显示
self.progress_label.config(text="正在准备合成...")
self.root.update()
# 生成带时间戳的文件名
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
default_filename = f"语音合成_{timestamp}.mp3"
# 弹出文件保存对话框
filepath = filedialog.asksaveasfilename(
title="保存语音文件",
initialdir=os.path.expanduser("~"),
initialfile=default_filename,
defaultextension=".mp3",
filetypes=[("MP3音频文件", "*.mp3")]
)
if not filepath: # 取消了保存
self.progress_label.config(text="已取消") # 改为progress_label
return
try:
# 更新进度显示
self.progress_label.config(text="正在合成语音...") # 改为progress_label
self.root.update()
# 使用edge_tts进行语音合成
communicate = edge_tts.Communicate(
text=text,
voice=self.voice_var.get(),
rate=self.speed_var.get(),
volume=self.volume_var.get(),
pitch=self.pitch_var.get()
)
# 更新进度显示
self.progress_label.config(text="正在保存文件...")
self.root.update()
# 保存到文件
await communicate.save(filepath)
# 更新进度显示
self.progress_label.config(text="保存成功!")
messagebox.showinfo("保存成功", f"语音文件已保存到:\n{filepath}")
except Exception as e:
# 更新进度显示
self.progress_label.config(text=f"合成失败: {str(e)}") # 改为progress_label
# 删除可能已经创建的不完整文件
if os.path.exists(filepath):
try:
os.remove(filepath)
except:
pass
raise e
if __name__ == "__main__":
root = tk.Tk()
app = VoiceSynthesisApp(root)
root.mainloop()
文件下载 | 文件名称:AI转语音工具 |
| 下载声明:此附件只适用于研究学习,请勿用作商业用途本站对使用的后果不承担责任! | |
| 下载地址:/wp-content/uploads/2025/04/the_edge_tts.rar | |
