主要使用python tkinter HTTP方式调用百度智能云长文本在线合成API的方式实现文字合成语音的小工具非专业的Python开发写的不规范的地方请多多包涵。文章目录使用百度智能云长文本在线合成API文本转语音窗口形式将文本转换语音Python 代码打包成exe文件在百度智能云平台创建应用界面截图项目结构截图读取配置文件代码import os import json import getpass def load_key(keyname: str) - object: dir_path os.path.dirname(os.path.realpath(__file__)) file_name dir_path /config.json if os.path.exists(file_name): with open(file_name, r) as file: Key json.load(file) if keyname in Key and Key[keyname]: return Key[keyname] else: keyval getpass.getpass(配置文件中没有相应的key请输入对应配置信).strip() Key[keyname] keyval with open(file_name, w) as file: json.dump(Key, file, indent4) return keyval else: keyval getpass.getpass(配置文件中没有相应的key请输入对应配置信).strip() Key { keyname: keyval } with open(file_name, w) as file: json.dump(Key, file, indent4) return keyval主程序代码import datetime import io import json import threading import time import tkinter as tk import urllib from tkinter import ttk, filedialog import messagebox import pygame import requests from config.load_key import load_key is_playing False # 记录语音文件的路径 volume_map3url # 合成音频的任务ID volume_task_id # 获取百度语音合成的access_token def get_access_token(): url load_key(BAIDU_OAUTH_TOKEN_URL) params { grant_type: client_credentials, client_id: load_key(BAIDU_API_KEY), client_secret: load_key(BAIDU_SECRET_KEY) } response requests.post(url, paramsparams) if response.status_code ! 200: messagebox.showerror(错误, 网络繁忙请稍后重试) return None return str(response.json().get(access_token)) # 获取文本内容 def get_text_as_list(text_widget): # 获取文本内容 text_content text_widget.get(1.0, tk.END) # 移除末尾的换行符 text_content text_content.strip() # 分割为列表 text_list text_content.splitlines() return text_list class Voice: def __init__(self, code, name): self.code code self.name name def get_voice_file(): url load_key(BAIDU_TTS_QUERY_API_URL) get_access_token() task_ids [volume_task_id] payload json.dumps({ task_ids: task_ids }, ensure_asciiFalse) headers { Accept: application/json } response requests.request(POST, url, headersheaders, datapayload.encode(utf-8)) if response.status_code ! 200: messagebox.showerror(错误, 网络繁忙请稍后重试) return False res_json response.json() print(res_json) if res_json.get(tasks_info): task_info res_json.get(tasks_info)[0] if task_info.get(task_status) Success: task_result task_info.get(task_result) speech_url task_result.get(speech_url) global volume_map3url volume_map3url speech_url return True else: print(当前语音合成任务状态 task_info.get(task_status)) return False else: messagebox.showerror(错误, 获取音频文件失败) return False class SpeechSynthesis: def __init__(self, master): self.master master master.title(语音合成) master.geometry(400x750) # 禁止调整窗口大小 master.resizable(False, False) # 创建文本域 self.text_area tk.Text(master, width50, height30) self.text_area.grid(row0, column0, pady5, padx5, rowspan2, columnspan4, stickytk.EW) # 语速 self.speed_label tk.Label(master,text语速0-15) self.speed_label.grid(row3, column0, padx10, pady5, stickytk.W) self.speed_entry tk.Entry(master, width34) self.speed_entry.insert(0, 5) self.speed_entry.grid(row3, column1, padx5, pady5, stickytk.EW) # 音量 self.volume_label tk.Label(master, text音量0-15) self.volume_label.grid(row4, column0, padx10, pady5, stickytk.W) self.volume_entry tk.Entry(master, width34) self.volume_entry.insert(0, 5) self.volume_entry.grid(row4, column1, padx5, pady5, stickytk.EW) # 音调 self.pitch_label tk.Label(master, text音调0-15) self.pitch_label.grid(row5, column0, padx10, pady5, stickytk.W) self.pitch_entry tk.Entry(master, width34) self.pitch_entry.insert(0, 5) self.pitch_entry.grid(row5, column1, padx5, pady5, stickytk.EW) # 选择语音 self.voice_list [ Voice(0, 度小美), Voice(5003, 度逍遥精品), Voice(106, 度博文), Voice(5118, 度小鹿), Voice(5, 度小娇), Voice(103, 度米朵), Voice(110, 度小童), Voice(111, 度小萌), Voice(4003, 度逍遥臻品), Voice(4259, 度小新-播音女声), ] self.choice_voice_label tk.Label(master, text选择语音) self.choice_voice_label.grid(row6, column0, padx10, pady5, stickytk.W) self.voices_combo ttk.Combobox(master, values[str(voice.name) for voice in self.voice_list], width34) # 默认选择第一个选项 self.voices_combo.current(0) self.voices_combo.grid(row6, column1, padx5, pady5, stickytk.EW) # 文件保存路径 self.voice_file_label tk.Label(master, text文件名可选) self.voice_file_label.grid(row7, column0, padx10, pady5, stickytk.W) self.filename_entry tk.Entry(master, width34) self.filename_entry.grid(row7, column1, padx5, pady5, stickytk.EW) # 创建按钮 self.generate_button tk.Button(master, text开始合成, commandself.generate_voice) self.generate_button.grid(row8, column0, padx120, pady5, columnspan 2, stickytk.NSEW) self.preview_button tk.Button(master, text试听, commandself.preview_speech) self.preview_button.grid(row9, column0, padx120, pady5, columnspan2, stickytk.EW) self.save_button tk.Button(master, text保存到本地, commandself.save_speech) self.save_button.grid(row10, column0, padx120, pady5, columnspan2, stickytk.EW) # 创建一个标签用于显示状态信息设置凹陷的边框样式边框宽度为1水平和垂直方向的间距为5 self.status_label tk.Label(master, text, bd1, padx5, pady5, reliefsunken) self.status_label.grid(row11, column0, padx5, pady10, columnspan 2, stickytk.NSEW) def save_speech(self): global volume_map3url if not volume_map3url: messagebox.showerror(错误, 请先合成语音文件) return global is_playing if is_playing: # 如果正在播放语音更新状态标签提示用户 self.status_label.config(text正在试听中请等待停止后再保存) # 直接返回不执行后续保存操作 return path filedialog.askdirectory() # 获取用户输入的文件名 filename self.filename_entry.get() if not filename: # 如果用户没有输入文件名使用当前时间生成一个文件名 filename datetime.datetime.now().strftime(%Y%m%d%H%M%S) # 给文件名添加.mp3后缀 filename .mp3 # 替换为你的本地保存路径和文件名 local_filename path / filename urllib.request.urlretrieve(volume_map3url, local_filename) self.status_label.config(text音频文件保存成功) volume_map3url global volume_task_id volume_task_id def preview_speech(self): global volume_map3url if not volume_map3url: messagebox.showerror(错误, 请先合成语音文件) return global is_playing if is_playing: # 如果正在播放语音更新状态标签提示用户 self.status_label.config(text正在试听中请等待停止后再点击) # 直接返回不执行后续试听操作 return # 将is_playing设置为True表示开始试听 is_playing True # 更新状态标签提示用户正在试听 self.status_label.config(text正在试听中...) def play(): try: # 发起HTTP请求获取MP3数据 response requests.get(volume_map3url) response.raise_for_status() # 确保请求成功 pygame.mixer.init() # 使用pygame播放MP3数据直接播放bytes sound pygame.mixer.Sound(io.BytesIO(response.content)) sound.play() # 等待声音播放完毕可选 while pygame.mixer.get_busy(): pygame.time.Clock().tick(10) finally: global is_playing # 无论语音播放是否正常完成都将is_playing设置为False表示试听结束 is_playing False # 更新状态标签提示用户试听已结束 self.status_label.config(text试听已结束) # 在后台线程中启动播放语音的任务daemonTrue表示该线程会在主线程结束时自动结束 threading.Thread(targetplay, daemonTrue).start() # 合成语音 def generate_voice(self): input_text_area self.text_area.get(1.0, end-1c) if not input_text_area: messagebox.showerror(错误, 请先输入文本信息) return False global is_playing if is_playing: # 如果正在播放语音更新状态标签提示用户 self.status_label.config(text正在试听中请等待停止后再点击) # 直接返回不执行后续试听操作 return False url load_key(BAIDU_TTS_CREATE_API_URL) get_access_token() selected_voice_val self.voices_combo.get() selected_voice_code [voice.code for voice in self.voice_list if voice.name selected_voice_val][0] print(selected_voice_code) payload json.dumps({ text: get_text_as_list(self.text_area), format: mp3-16k, voice: int(selected_voice_code), lang: zh, speed: int(self.speed_entry.get()), pitch: int(self.pitch_entry.get()), volume: int(self.volume_entry.get()), enable_subtitle: 0 }, ensure_asciiFalse) headers { Accept: application/json } response requests.request(POST, url, headersheaders, datapayload.encode(utf-8)) if response.status_code ! 200: self.status_label.config(text语音合成失败请稍后重试) return False res_json response.json() print(res_json) if res_json.get(task_id): task_id res_json.get(task_id) global volume_task_id volume_task_id task_id self.status_label.config(text语音合成完成正在获取语音文件...) # 获取语音文件 while True: time.sleep(15) voice_flag get_voice_file() if voice_flag: self.status_label.config(text语音文件获取成功可试听或保存到本地) break return True else: self.status_label.config(text语音生成失败) return False if __name__ __main__: root tk.Tk() app SpeechSynthesis(root) root.mainloop()