# -*- 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 |