【Python】edge-tts :便捷语音合成_edge tts
第一章:初识 edge-tts
—— 开启语音合成之旅
1.1 文本转语音 (TTS) 技术概述
文本转语音(Text-to-Speech, TTS),顾名思义,是一种将输入的文本信息转换成可听的语音波形的技术。它是人机语音交互的关键组成部分,使得计算机能够像人一样“说话”。
-
1.1.1 TTS 的发展简史与重要性
TTS 技术的研究可以追溯到上世纪中叶,早期的 TTS 系统通常基于参数合成或拼接合成的方法,声音机械、不自然。- 参数合成 (Parametric Synthesis): 通过建立声学模型来模拟人的发声过程,生成语音参数(如基频、共振峰等),再由这些参数合成语音。这种方法灵活性较高,但自然度往往不足。
- 拼接合成 (Concatenative Synthesis): 将预先录制好的大量语音单元(如音素、音节、词语甚至短语)存储在数据库中,合成时根据输入文本选取合适的单元进行拼接。如果语音库足够大且拼接算法优秀,可以获得较高的自然度,但对于未登录词或复杂韵律的表现可能不佳。常见的有单元选择合成 (Unit Selection Synthesis)。
- 统计参数语音合成 (Statistical Parametric Speech Synthesis, SPSS): 例如基于隐马尔可夫模型 (HMM) 的 TTS。它通过统计模型来学习文本特征到声学特征的映射关系,然后根据模型生成声学参数,再通过声码器合成语音。相比传统参数合成,自然度有所提升。
- 深度学习驱动的 TTS (Deep Learning based TTS): 近年来,随着深度学习技术的突破,端到端 (End-to-End) 的 TTS 模型,如 Google 的 Tacotron、WaveNet,百度的 Deep Voice,以及 Facebook 的 VoiceLoop 等,极大地提升了合成语音的自然度和表现力。这些模型可以直接从文本学习到语音波形,或者学习到高质量的声谱图再通过神经声码器(如 WaveNet, WaveGlow, HiFi-GAN)转换成波形。这类方法生成的语音在自然度、情感表达等方面已经非常接近真人水平。
TTS 技术的重要性体现在:
- 信息获取的多样化: 为视觉障碍人士提供了重要的信息获取渠道,也方便了驾驶、运动等不便阅读的场景。
- 提升人机交互体验: 使交互更加自然、人性化,例如智能音箱、语音助手。
- 自动化内容生产: 自动生成有声读物、新闻播报、视频配音等,提高效率。
- 教育与娱乐: 语言学习、故事讲述、游戏角色配音等。
- 公共服务与通知: 公共交通报站、紧急警报播报等。
-
1.1.2 现代 TTS 系统的关键组成部分
一个现代的 TTS 系统通常包含以下几个核心模块:-
文本预处理 (Text Preprocessing / Normalization):
- 分句 (Sentence Segmentation): 将大段文本切分成句子。
- 文本规范化 (Text Normalization, TN): 将文本中的非标准词语(如数字、日期、时间、缩写、符号等)转换为标准发音的词语形式。例如,将 “Dr. Smith lives at 123 Main St.” 转换为 “Doctor Smith lives at one twenty-three Main Street”。
- 多音字消歧 (Polyphone Disambiguation): 确定多音字在当前上下文中的正确读音,例如“银行 (háng)”和“行 (xíng)人”。
- 韵律预测的初步标记 (Prosodic Feature Prediction - Initial): 可能包括对词性、短语边界等的初步分析,为后续的韵律建模做准备。
-
语言学分析 (Linguistic Analysis):
- 词法分析 (Lexical Analysis): 识别单词、词性等。
- 句法分析 (Syntactic Analysis): 分析句子结构,如短语、从句等。
- 语义分析 (Semantic Analysis - 较高级): 理解词语和句子的含义,有助于更自然的情感和重音表达。
-
声学模型/韵律模型 (Acoustic Model / Prosody Model):
- 目标声学特征预测: 根据文本和语言学分析的结果,预测目标语音的声学特征,如梅尔频谱图 (Mel-spectrogram)、基频 (F0)、时长 (duration) 等。深度学习模型如 Tacotron2, FastSpeech 等在此阶段发挥核心作用。
- 韵律建模 (Prosody Modeling): 控制合成语音的语速、停顿、语调、重音等,使其听起来更自然、更富有表现力。SSML(语音合成标记语言)通常在此阶段介入,允许用户显式控制这些韵律特征。
-
声码器 (Vocoder) / 波形生成 (Waveform Generation):
- 将声学模型预测出的声学特征(通常是某种频谱表示)转换成最终的原始音频波形数据。
- 传统的声码器如 Griffin-Lim 算法,或者基于信号处理的方法。
- 神经声码器 (Neural Vocoders) 如 WaveNet, WaveGlow, Parallel WaveGAN, HiFi-GAN 等,能够生成非常高质量和高自然度的语音波形,是现代 TTS 系统音质提升的关键。
edge-tts
本身并不直接实现这些复杂的模块,而是作为客户端,将文本(或 SSML)发送给微软的云端 TTS 服务,由微软的后端服务完成上述所有处理流程,并返回合成的音频流。 -
1.2 微软 Edge 浏览器的 TTS 能力与 edge-tts
的渊源
微软在其 Edge 浏览器中内置了强大的“大声朗读”(Read Aloud) 功能,允许用户方便地听取网页内容。这项功能背后依赖的是微软认知服务 (Microsoft Cognitive Services) 中的语音服务 (Speech Service) 或类似的技术栈。
-
1.2.1 Edge 浏览器“大声朗读”功能的特点
- 高自然度语音: Edge 提供的语音,特别是其“神经元语音”(Neural Voices),通常非常自然流畅,接近真人发音。
- 多语言支持: 支持全球多种主要语言和地区的口音。
- 多种语音选择: 同一种语言下通常提供多种不同性别、不同风格的语音供用户选择。
- 可控的语速: 用户可以在浏览器中调整朗读的语速。
- 无需额外安装: 作为浏览器内置功能,用户无需安装任何插件即可使用。
-
1.2.2
edge-tts
的诞生:解锁云端能力
edge-tts
库的作者发现 Edge 浏览器进行语音合成时,会通过 WebSocket 与微软的某个语音合成服务API进行通信。这个API虽然没有被微软作为公开的开发者API直接推广给所有Python开发者(微软有其官方的 Azure Speech SDK),但其存在为edge-tts
提供了可能性。edge-tts
通过模拟 Edge 浏览器与此服务之间的通信协议(主要是 WebSocket 连接和特定格式的消息传递,通常涉及 SSML),使得 Python 程序也能够利用这个高质量的语音合成引擎。关键点:
edge-tts
本质上是一个非官方的客户端,它利用了 Edge 浏览器使用的后端 TTS 服务。- 这意味着
edge-tts
的功能和稳定性在一定程度上会受到微软对其内部 API 策略调整的影响。如果微软更改 API 端点、通信协议或认证方式,edge-tts
可能需要更新才能继续工作。 - 它提供了一个比使用完整 Azure Speech SDK 更轻量级、更便捷(在某些简单场景下)的选择,特别是对于那些希望快速集成高质量免费(或低成本,依赖于微软对此服务的使用策略)TTS 功能的开发者。
-
1.2.3
edge-tts
的核心优势- 卓越的语音质量: 直接受益于微软先进的神经元语音技术,合成的语音非常自然。
- 丰富的语音选择: 提供了大量的语言和语音选项。
- 免费使用 (目前): 截至目前,通过
edge-tts
使用该服务通常是免费的,这对于个人项目和小规模应用非常有吸引力。(但用户应意识到这可能随时改变,且应遵守合理使用原则)。 - 跨平台: 作为 Python 库,可以在 Windows, macOS, Linux 等多种操作系统上运行。
- 支持 SSML: 允许通过语音合成标记语言对语音输出进行精细控制。
- 异步支持: 基于
asyncio
,可以方便地进行异步操作,适合构建高并发应用。 - 易于上手: 提供了简洁的命令行工具和 Python API。
1.3 edge-tts
与微软官方 Azure Speech SDK 的关系
理解 edge-tts
和 Azure Speech SDK 之间的区别非常重要:
-
Azure Speech SDK:
- 官方支持: 是微软官方提供和支持的 SDK,用于访问 Azure Cognitive Services 中的语音服务(包括语音转文本 STT、文本转语音 TTS、语音翻译等)。
- 功能全面: 提供非常丰富的功能选项,包括标准语音、神经元语音、自定义语音 (Custom Voice)、说话风格、情感调整、更详细的音频输出格式控制、词法发音调整、详细的错误报告和诊断等。
- 面向商业应用: 设计上更偏向于企业级和商业级应用,提供 SLA (服务等级协议) 保证(对于付费层级)。
- 需要 Azure 账户和订阅: 使用 Azure Speech SDK 通常需要一个 Azure 账户,并创建一个语音服务资源,获取订阅密钥 (Subscription Key) 和区域 (Region) 信息。部分服务有免费额度,超出则需付费。
- 文档完善: 有详尽的官方文档、示例代码和技术支持。
-
edge-tts
:- 非官方客户端: 是社区驱动的项目,利用了 Edge 浏览器使用的未公开或半公开的 API 端点。
- 功能相对精简: 主要聚焦于利用 Edge 的神经元语音进行 TTS,功能上可能不如 Azure Speech SDK 全面(例如,自定义语音、非常高级的 SSML 特性支持程度可能不同)。
- 通常免费: 目前使用是免费的,但没有官方的 SLA 保证,且服务策略可能变更。
- 无需 Azure 订阅密钥: 直接使用,不需要显式的 Azure 账户配置(因为它模拟的是浏览器行为)。
- 依赖于API的稳定性: 如果微软更改其内部API,
edge-tts
可能会失效,需要库作者更新。
选择考量:
- 对于个人项目、快速原型、学习或对成本敏感的小型应用,且可接受其非官方性质带来的潜在不确定性:
edge-tts
是一个极具吸引力的选择,因为它提供了极高质量的语音而无需复杂的账户设置和费用。 - 对于需要官方支持、SLA保证、最全面功能集、可预测的定价模型以及计划进行大规模商业部署的企业级应用: 强烈推荐使用官方的 Azure Speech SDK。
- 对于希望深入研究特定语音特性或需要自定义语音模型的场景: Azure Speech SDK 提供的能力更为强大。
可以认为 edge-tts
是一个“取巧”的、轻量级的解决方案,它让更广泛的开发者能够便捷地接触到微软高质量的神经元语音。
1.4 安装 edge-tts
安装 edge-tts
非常简单,主要通过 Python 的包管理器 pip
。
-
1.4.1 环境准备
-
Python 版本:
edge-tts
通常需要 Python 3.7 或更高版本,因为它依赖于asyncio
以及一些现代的 Python 特性。建议使用较新的 Python 3 版本(例如 3.8+)。
你可以通过以下命令检查你的 Python 版本:python --version# 或者,如果你的系统上有多个Python版本python3 --version
如果未安装 Python 或版本过低,请先从 Python 官方网站 (python.org) 下载并安装合适的版本。
-
pip
包管理器:pip
通常随 Python 一起安装。你可以检查其版本:pip --version# 或者pip3 --version
如果需要,可以升级
pip
:python -m pip install --upgrade pip# 或者python3 -m pip install --upgrade pip
-
虚拟环境 (推荐): 为了避免不同项目之间的库版本冲突,强烈建议在 Python 项目中使用虚拟环境。
- 创建虚拟环境 (以
venv
为例):# 在你的项目目录下python -m venv .venv # python -m venv: 使用 venv 模块创建虚拟环境# .venv: 虚拟环境的名称 (一个常用的名称,可以自定义)
- 激活虚拟环境:
- Windows (CMD):
.venv\\Scripts\\activate
- Windows (PowerShell):
.venv\\Scripts\\Activate.ps1# 如果遇到执行策略问题,可能需要先运行: Set-ExecutionPolicy RemoteSigned -Scope CurrentUser
- macOS / Linux (bash/zsh):
source .venv/bin/activate
激活虚拟环境后,你的命令行提示符通常会显示虚拟环境的名称,例如
(.venv) user@hostname:...$
。之后通过pip install
安装的库将仅位于此虚拟环境中。 - Windows (CMD):
- 退出虚拟环境:
deactivate
- 创建虚拟环境 (以
-
-
1.4.2 使用
pip
安装edge-tts
在激活了虚拟环境(如果使用的话)的命令行或终端中,运行以下命令:pip install edge-tts# pip install: pip命令,用于安装Python包# edge-tts: 要安装的包的名称
pip
会自动从 Python Package Index (PyPI) 下载edge-tts
及其所有依赖项(例如aiohttp
,websockets
等)并进行安装。 -
1.4.3 安装特定版本或升级
- 安装特定版本: 如果你需要安装特定版本的
edge-tts
(例如,为了兼容性或避免某个新版本的bug),可以指定版本号:pip install edge-tts==1.2.3 # 替换为实际的版本号# edge-tts==1.2.3: 指定安装版本号为 1.2.3 的 edge-tts
- 升级到最新版本: 如果你已经安装了
edge-tts
,想升级到最新的稳定版本:pip install --upgrade edge-tts# --upgrade: pip 的选项,用于升级已安装的包
- 安装特定版本: 如果你需要安装特定版本的
-
1.4.4 验证安装
安装完成后,你可以通过几种方式验证edge-tts
是否成功安装:-
命令行工具检查:
edge-tts
提供了一个命令行工具。你可以尝试列出可用的语音:edge-tts --list-voices# edge-tts: 调用 edge-tts 命令行工具# --list-voices: 该工具的选项,用于列出所有可用的语音
如果命令成功执行并输出了一个语音列表,则表明命令行工具部分安装正确。
-
Python 解释器中导入: 打开 Python 解释器 (直接输入
python
或python3
命令),然后尝试导入edge_tts
模块:import edge_ttsprint(edge_tts.VoicesManager) # 尝试访问库中的一个对象# import edge_tts: 导入 edge_tts 库# print(edge_tts.VoicesManager): 打印库中的 VoicesManager 类,检查是否能成功访问
如果没有出现
ModuleNotFoundError
或其他导入错误,则表明库本身已成功安装并可以被 Python 代码使用。
-
-
1.4.5 潜在的安装问题与解决
- 网络问题:
pip install
需要从互联网下载包。如果你的网络连接有问题,或者需要通过代理访问互联网,可能会导致安装失败。- 代理配置: 如果需要代理,可以配置
pip
使用代理:pip install --proxy <PROXY_ADDRESS>:<PROXY_PORT> edge-tts# --proxy: pip 的选项,用于指定代理服务器地址和端口# :: 替换为你的代理服务器实际地址和端口
或者设置环境变量
HTTP_PROXY
和HTTPS_PROXY
。
- 代理配置: 如果需要代理,可以配置
- 权限问题: 在某些系统上,如果你没有使用虚拟环境,并且尝试向系统级的 Python 环境安装包,可能会遇到权限不足的错误。
- 解决方法1 (推荐): 使用虚拟环境。
- 解决方法2 (不推荐,需谨慎): 在 Linux 或 macOS 上使用
sudo pip install edge-tts
(这会将包安装到系统Python,可能引发问题)。在 Windows 上,以管理员身份运行命令提示符。
- 依赖冲突: 极少数情况下,
edge-tts
的依赖项可能与你环境中已有的其他库版本冲突。虚拟环境可以最大限度地减少这类问题。如果发生冲突,错误消息通常会给出提示,你可能需要手动调整相关库的版本或寻求社区帮助。 - 编译器缺失 (针对某些依赖):
edge-tts
的某些依赖项(特别是底层的网络库,如aiohttp
可能依赖yarl
和multidict
,它们可能有C扩展)在某些平台上安装时可能需要C编译器。大多数情况下,PyPI 会提供预编译的二进制轮子 (wheels),用户无需编译。但如果你的平台或Python版本没有对应的轮子,pip
会尝试从源码编译,这时如果缺少编译器(如 GCC for Linux, MSVC for Windows),安装会失败。- 解决方法: 安装相应的构建工具。例如,在 Debian/Ubuntu 上可能是
sudo apt install build-essential python3-dev
;在 Windows 上可能需要安装 Microsoft C++ Build Tools。不过,对于edge-tts
及其核心依赖,这种情况相对少见,因为它们通常有良好的轮子支持。
- 解决方法: 安装相应的构建工具。例如,在 Debian/Ubuntu 上可能是
- 网络问题:
1.5 edge-tts
的基本命令行用法
edge-tts
不仅是一个 Python 库,还附带了一个非常方便的命令行工具 (CLI),也叫做 edge-tts
。这使得你可以不编写任何 Python 代码就能快速地将文本转换为语音文件。
-
1.5.1 查看帮助信息
要了解命令行工具的所有可用选项和功能,可以使用-h
或--help
参数:edge-tts --help# --help: 显示命令行工具的帮助信息和可用选项
这将输出详细的用法说明,包括所有支持的参数及其解释。
-
1.5.2 基本的文本到语音文件转换
最基本的功能是将一段文本转换成一个音频文件。edge-tts --text \"你好,世界!这是一个来自 edge-tts 的测试。\" --write-media hello_world_cli.mp3# --text \"内容\": 指定要转换为语音的文本内容# --write-media hello_world_cli.mp3: 指定输出的音频文件名,这里是 hello_world_cli.mp3
执行完毕后,当前目录下会生成一个名为
hello_world_cli.mp3
的音频文件,内容就是 “你好,世界!这是一个来自 edge-tts 的测试。” 的语音。 -
1.5.3 选择语音 (Voice Selection)
edge-tts
支持多种语言和多种发音人。你可以使用--list-voices
来查看所有可用的语音:edge-tts --list-voices# --list-voices: 列出所有可用的语音及其属性,如 Name, ShortName, Gender, Locale
输出会是类似这样的列表(部分示例):
Name: Microsoft Server Speech Text to Speech Voice (en-US, AriaNeural)ShortName: en-US-AriaNeuralGender: FemaleLocale: en-USName: Microsoft Server Speech Text to Speech Voice (en-US, GuyNeural)ShortName: en-US-GuyNeuralGender: MaleLocale: en-USName: Microsoft Server Speech Text to Speech Voice (zh-CN, XiaoxiaoNeural)ShortName: zh-CN-XiaoxiaoNeuralGender: FemaleLocale: zh-CNName: Microsoft Server Speech Text to Speech Voice (zh-CN, YunxiNeural)ShortName: zh-CN-YunxiNeuralGender: MaleLocale: zh-CN... (更多语音)
每一条记录都包含了语音的
Name
(完整名称)、ShortName
(短名称,用于在命令行中指定语音)、Gender
(性别) 和Locale
(语言地区代码)。要使用特定的语音,可以使用
--voice
或-v
参数,后跟语音的ShortName
:edge-tts --text \"Hello world, this is a test from edge-tts.\" --voice \"en-US-AriaNeural\" --write-media hello_aria.mp3# --voice \"en-US-AriaNeural\": 指定使用 ShortName 为 en-US-AriaNeural 的语音 (美国英语,Aria,女性)edge-tts --text \"早上好,今天天气真不错。\" --voice \"zh-CN-XiaoxiaoNeural\" --write-media morning_xiaoxiao.mp3# --voice \"zh-CN-XiaoxiaoNeural\": 指定使用 ShortName 为 zh-CN-XiaoxiaoNeural 的语音 (简体中文,晓晓,女性)
如果你不指定
--voice
,edge-tts
通常会使用一个默认的英语语音(具体哪个可能会随版本变化)。 -
1.5.4 控制语速 (Rate) 和音量 (Volume)
你可以调整合成语音的语速和音量。-
语速 (
--rate
):
语速可以指定为百分比(相对于默认语速)或预定义的词语(如x-slow
,slow
,medium
,fast
,x-fast
)。
通常格式是+n%
(提高n%) 或-n%
(降低n%)。0%
代表默认语速。# 较慢的语速edge-tts --text \"This is spoken slowly.\" --voice \"en-US-GuyNeural\" --rate=\"-20%\" --write-media slow_speech.mp3# --rate=\"-20%\": 将语速降低20%# 较快的语速edge-tts --text \"This is spoken quickly.\" --voice \"en-US-GuyNeural\" --rate=\"+30%\" --write-media fast_speech.mp3# --rate=\"+30%\": 将语速提高30%
注意:百分比前的
+
号有时可以省略,但为了清晰,建议写上。语速调整的确切效果和支持范围可能因语音和服务端实现而异。 -
音量 (
--volume
):
音量也可以指定为百分比(相对于默认音量)。
格式与语速类似:+n%
(提高n%) 或-n%
(降低n%)。0%
代表默认音量。# 较大的音量edge-tts --text \"This is spoken loudly.\" --voice \"en-US-AriaNeural\" --volume=\"+20%\" --write-media loud_speech.mp3# --volume=\"+20%\": 将音量提高20%# 较小的音量edge-tts --text \"This is spoken softly.\" --voice \"en-US-AriaNeural\" --volume=\"-50%\" --write-media soft_speech.mp3# --volume=\"-50%\": 将音量降低50%
音量的调整范围通常在
0%
(静音) 到100%
(最大音量) 之间,但这里的百分比是相对调整。
-
-
1.5.5 控制音高 (Pitch)
你还可以调整语音的音高。- 音高 (
--pitch
):
音高也可以用百分比或预定义词语(如x-low
,low
,medium
,high
,x-high
)表示。
格式也类似:+n%
(提高n%) 或-n%
(降低n%)。0%
代表默认音高。# 较低的音高edge-tts --text \"My voice is low.\" --voice \"en-US-GuyNeural\" --pitch=\"-20%\" --write-media low_pitch.mp3# --pitch=\"-20%\": 将音高降低20%# 较高的音高edge-tts --text \"My voice is high.\" --voice \"en-US-GuyNeural\" --pitch=\"+30%\" --write-media high_pitch.mp3# --pitch=\"+30%\": 将音高提高30%
过度调整音高可能会导致声音失真或听起来不自然。
- 音高 (
-
1.5.6 从文件读取文本 (
--text-file
)
如果你的文本内容很长,或者已经保存在一个文件中,可以使用--text-file
参数来指定输入的文本文件。文件应为 UTF-8 编码。# 首先创建一个文本文件,例如 story.txt# 内容:# Once upon a time, in a land far, far away, there lived a brave knight.# He embarked on a quest to find a legendary dragon.echo \"Once upon a time, in a land far, far away, there lived a brave knight. He embarked on a quest to find a legendary dragon.\" > story.txt# echo ... > story.txt: 创建一个名为 story.txt 的文件并写入内容edge-tts --text-file story.txt --voice \"en-US-AriaNeural\" --write-media story_audio.mp3# --text-file story.txt: 从名为 story.txt 的文件中读取文本内容
如果同时指定了
--text
和--text-file
,通常--text
参数会优先。 -
1.5.7 使用 SSML (
--ssml
)
对于更高级的语音控制,例如插入停顿、改变特定词语的发音方式、混合不同语音等,可以使用 SSML (语音合成标记语言)。你需要将 SSML 内容保存在一个.xml
或.ssml
文件中,然后通过--ssml-file
(或者如果库支持直接命令行传递SSML,可能是--ssml
参数,但通常是通过文件) 来指定。
注意:较新版本的edge-tts
CLI 可能直接支持--ssml \"...\"
参数来传递SSML字符串,或者依然主要通过文件。请用edge-tts --help
确认。假设这里我们使用文件方式。创建一个 SSML 文件,例如
speech_control.ssml
:<speak version=\"1.0\" xmlns=\"http://www.w3.org/2001/10/synthesis\" xml:lang=\"en-US\"> <voice name=\"en-US-AriaNeural\"> <prosody rate=\"slow\">Hello world!</prosody> <break time=\"500ms\"/> I will now speak faster. <prosody rate=\"fast\">This part is quick.</prosody> <break strength=\"medium\"/> And now, let\'s try a different pitch. <prosody pitch=\"high\">This is high pitched.</prosody> </voice></speak>
解释这个 SSML 文件:
: SSML 文档的根元素。
xml:lang=\"en-US\"
: 指定文档的主要语言。: 选择一个特定的语音。注意,这里用的
name
可能需要是ShortName
,具体取决于edge-tts
如何解析。在 Python 库中,通常用ShortName
。...
: 将 “Hello world!” 以较慢的语速说出。: 插入一个 500 毫秒的停顿。
...
: 将 “This part is quick.” 以较快的语速说出。: 插入一个中等强度的停顿。
...
: 将 “This is high pitched.” 以较高的音调说出。
然后使用命令行:
# 假设 speech_control.ssml 在当前目录edge-tts --ssml-file speech_control.ssml --write-media ssml_output.mp3# --ssml-file speech_control.ssml: 从名为 speech_control.ssml 的文件中读取 SSML 内容# 注意:这里没有指定 --voice,因为 SSML 文件内部已经通过 指定了。# 如果 SSML 内部没有指定 voice,或者你想覆盖它,可能需要命令行配合 --voice。# 行为细节需查阅最新文档或通过实验确认。
SSML 提供了非常强大的控制能力,我们将在后续章节中详细探讨。
-
1.5.8 输出到标准输出 (Piping)
有时你可能不想将音频保存到文件,而是希望将其通过管道传递给其他命令进行处理(例如,用ffplay
或mpv
直接播放)。
如果edge-tts
支持将原始音频数据输出到 stdout,通常是将--write-media
的文件名指定为-
(一个横杠,常用于表示标准输出)。# 尝试输出到标准输出并用 ffplay 播放 (需要安装 ffmpeg)# 这个命令的可用性取决于 edge-tts 是否支持输出原始音频到 stdout 以及其格式# edge-tts --text \"Playing directly.\" --voice \"en-US-AriaNeural\" --write-media - | ffplay -nodisp -autoexit -# --write-media - : 尝试将输出写入标准输出# | ffplay ... : 将标准输出通过管道传递给 ffplay 命令进行播放# -nodisp: ffplay 选项,不显示视频窗口# -autoexit: ffplay 选项,播放完毕后自动退出# 最后的 - : ffplay 从标准输入读取数据# 如果直接输出到 stdout 不支持或格式不兼容,另一种方法是先生成临时文件# edge-tts --text \"Playing via temp file.\" --voice \"en-US-AriaNeural\" --write-media temp_audio.mp3 && ffplay -nodisp -autoexit temp_audio.mp3 && rm temp_audio.mp3# &&: Shell操作符,前一个命令成功执行后才执行后一个# rm temp_audio.mp3: 删除临时文件
edge-tts
的 CLI 是否直接支持将编码后的音频(如MP3)流式输出到 stdout 以便管道传输,需要查阅其最新文档或通过edge-tts --help
中关于--write-media
的说明确认。如果它主要设计为写入完整文件,那么直接流式播放可能需要通过 Python 库结合其他工具实现。 -
1.5.9 指定代理 (
--proxy
)
如果你的网络环境需要通过代理服务器访问互联网,可以使用--proxy
参数。edge-tts --text \"Testing with proxy.\" --voice \"en-US-AriaNeural\" --proxy \"http://your_proxy_server:port\" --write-media proxy_test.mp3# --proxy \"http://your_proxy_server:port\": 指定使用的HTTP代理服务器地址和端口# 例如: --proxy \"http://127.0.0.1:8080\"# 支持的代理格式可能包括 http, https, socks5 等,具体需看 `aiohttp` 的支持。
-
1.5.10 其他可能的选项
edge-tts
的命令行工具可能还包含其他选项,例如:--locale LOCALE
: 仅列出特定语言环境的语音,配合--list-voices
使用。edge-tts --list-voices --locale zh-CN# --locale zh-CN: 仅列出中文(中国)的语音
- 控制输出音频的格式 (虽然通常默认是 MP3,但未来或特定版本可能支持配置)。
- 详细模式 (
-V
或--verbose
) 用于输出更详细的日志信息,方便调试。
始终建议通过
edge-tts --help
来获取最准确和最新的命令行选项信息。
通过这些命令,你可以方便地在不写代码的情况下使用 edge-tts
的核心功能。这对于快速测试语音、生成少量音频文件或在 shell 脚本中集成 TTS 非常有用。对于更复杂的逻辑和应用程序集成,我们接下来将转向 Python 库的使用。
1.6 使用 edge-tts
Python 库
虽然命令行工具非常便捷,但要在应用程序中集成 TTS 功能、处理复杂逻辑、进行流式传输或自定义更多行为,就需要使用 edge-tts
提供的 Python API。edge-tts
库是基于 asyncio
构建的,这意味着它的核心操作是异步的,这对于网络I/O密集型的 TTS 任务来说非常高效。
-
1.6.1 核心类与异步概念
在使用edge-tts
库之前,需要对 Python 的asyncio
有基本的了解。asyncio
是 Python 用于编写并发代码的库,使用async/await
语法。async def
: 用于定义一个协程 (coroutine),这是一种可以暂停和恢复执行的特殊函数。await
: 用于暂停协程的执行,等待一个awaitable
对象(通常是另一个协程或返回Future
的操作)完成。在等待期间,事件循环可以运行其他任务。- 事件循环 (Event Loop):
asyncio
的核心,负责调度和执行协程。
edge-tts
的主要交互通常围绕以下几个核心类或概念:Communicate
类: 这是进行实际文本到语音转换的核心类。你需要实例化它,并调用其方法来生成语音。VoicesManager
类: 用于获取和管理可用的语音列表。- SSML (Speech Synthesis Markup Language): 虽然不是一个类,但它是一种重要的输入格式,允许对语音输出进行精细控制。
-
1.6.2 基本的文本到语音文件 (异步)
让我们看一个最基本的例子:将文本转换为 MP3 文件并保存。import asyncio # 导入 asyncio 库,用于异步编程import edge_tts # 导入 edge_tts 库TEXT = \"Hello World! This is a test from the edge-tts Python library.\" # 要转换的文本# VOICE = \"en-US-AriaNeural\" # 指定要使用的语音,这里是美国英语的Aria Neural语音VOICE = \"zh-CN-XiaoxiaoNeural\" # 更换为中文语音,晓晓 Neural# OUTPUT_FILE = \"hello_world_python.mp3\" # 指定输出的MP3文件名OUTPUT_FILE = \"你好世界_python.mp3\" # 指定输出的MP3文件名async def amain() -> None: # 定义一个异步主函数 amain \"\"\"Main function\"\"\" communicate = edge_tts.Communicate(TEXT, VOICE) # 创建 Communicate 类的实例,传入文本和语音名称 # Communicate(text, voice, rate, volume, pitch, proxy) # text: 要合成的文本 # voice: 语音的 ShortName # rate: 语速 (可选, 例如 \"+10%\") # volume: 音量 (可选, 例如 \"-5%\") # pitch: 音高 (可选, 例如 \"high\") # proxy: 代理服务器地址 (可选, 例如 \"http://localhost:8080\") # 调用 save 方法异步保存音频到文件 # 这个方法内部会处理与服务器的通信、接收音频数据并写入文件 await communicate.save(OUTPUT_FILE) # await: 等待 communicate.save 操作完成 # OUTPUT_FILE: 保存音频的文件路径 print(f\"音频文件已保存到: { OUTPUT_FILE}\") # 打印保存成功的消息if __name__ == \"__main__\": # 当脚本作为主程序运行时 # Python 3.7+ 可以使用 asyncio.run() asyncio.run(amain()) # 运行异步主函数 amain # 对于 Python 3.6 及更早版本,需要手动获取和运行事件循环: # loop = asyncio.get_event_loop_policy().new_event_loop() # asyncio.set_event_loop(loop) # try: # loop.run_until_complete(amain()) # finally: # loop.close()
代码解释:
import asyncio
和import edge_tts
: 导入必要的库。TEXT
,VOICE
,OUTPUT_FILE
: 定义了输入文本、选择的语音(这里使用了微软的en-US-AriaNeural
,一个高质量的女性英文语音,以及zh-CN-XiaoxiaoNeural
,一个中文女性语音)和输出文件名。你可以从edge-tts --list-voices
的输出中选择你喜欢的ShortName
。async def amain():
: 定义了一个异步函数amain
。所有edge-tts
的核心操作都应该是异步的,所以它们需要在一个异步函数内部被await
。communicate = edge_tts.Communicate(TEXT, VOICE)
: 创建edge_tts.Communicate
类的一个实例。构造函数接收文本和语音名称作为基本参数。它还有可选参数如rate
,volume
,pitch
和proxy
,我们稍后会看到。await communicate.save(OUTPUT_FILE)
: 这是执行 TTS 并保存到文件的关键步骤。save()
是一个协程方法,所以我们使用await
来等待它完成。它会连接到微软的 TTS 服务,发送文本,接收音频数据,然后将其写入到指定的OUTPUT_FILE
。asyncio.run(amain())
: 这是在 Python 3.7+ 中运行顶层异步函数的标准方式。它负责创建事件循环,运行amain()
协程直到完成,然后关闭事件循环。
运行此脚本:
保存代码为例如tts_basic.py
,然后在命令行中运行:python tts_basic.py
执行成功后,你会在脚本所在的目录下找到
你好世界_python.mp3
(或hello_world_python.mp3
) 文件,播放它就能听到合成的语音。 -
1.6.3 获取和选择语音 (
VoicesManager
)
如果你想在代码中动态地获取可用的语音列表,而不是硬编码ShortName
,可以使用VoicesManager
类。import asyncioimport edge_ttsasync def list_available_voices(): # 定义一个异步函数来列出可用语音 \"\"\"Lists available voices.\"\"\" voices_manager = await edge_tts.VoicesManager.create() # 异步创建 VoicesManager 实例 # VoicesManager.create() 是一个异步类方法,用于初始化并获取语音列表 all_voices = voices_manager.voices # 获取所有语音的列表,每个元素是一个包含语音信息的字典 print(f\"找到 { len(all_voices)} 种可用语音:\\n\") # 打印找到的语音数量 # 遍历并打印每种语音的详细信息 for i, voice_info in enumerate(all_voices): print(f\"语音 { i+1}:\") # 打印语音序号 print(f\" Name: { voice_info[\'Name\']}\") # 打印语音的完整名称 print(f\" ShortName: { voice_info[\'ShortName\']}\") # 打印语音的短名称 (用于API调用) print(f\" Gender: { voice_info[\'Gender\']}\") # 打印语音的性别 print(f\" Locale: { voice_info[\'Locale\']}\") # 打印语音的语言地区代码 # voice_info 中可能还有其他字段,如 SuggestedCodec, FriendlyName, Status 等 print(\"-\" * 20) # 打印分隔线 # 按条件查找语音,例如:查找所有中文普通话女声 print(\"\\n查找所有中文(中国)女声 (zh-CN, Female):\") chinese_female_voices = voices_manager.find(Gender=\"Female\", Locale=\"zh-CN\") # voices_manager.find(): 根据提供的条件筛选语音 # Gender=\"Female\": 筛选条件,性别为女性 # Locale=\"zh-CN\": 筛选条件,语言地区为中文(中国) if chinese_female_voices: # 如果找到了匹配的语音 for voice_info in chinese_female_voices: print(f\" ShortName: { voice_info[\'ShortName\']}, Name: { voice_info[\'Name\']}\") else: print(\" 未找到匹配的中文女声。\") # 如果没有找到,打印提示 # 查找特定 ShortName 的语音 (虽然通常直接用 ShortName,但也可以这样验证) target_short_name = \"en-US-AriaNeural\" found_specific = voices_manager.find(ShortName=target_short_name) if found_specific: print(f\"\\n找到了 ShortName 为 \'{ target_short_name}\' 的语音: { found_specific[0][\'Name\']}\") else: print(f\"\\n未找到 ShortName 为 \'{ target_short_name}\' 的语音。\")async def generate_speech_with_selected_voice(): # 定义一个异步函数,使用筛选出的语音生成音频 \"\"\"Generates speech using a selected voice after filtering.\"\"\" voices_manager = await edge_tts.VoicesManager.create() # 创建 VoicesManager # 示例:选择第一个可用的简体中文女声 target_locale = \"zh-CN\" target_gender = \"Female\" selected_voices = voices_manager.find(Locale=target_locale, Gender=target_gender) if not selected_voices: # 如果没有找到匹配的语音 print(f\"错误:未找到语言为 \'{ target_locale}\' 且性别为 \'{ target_gender}\' 的语音。\") # 尝试备选:选择任意一个简体中文语音 print(\"尝试选择任意一个简体中文语音...\") selected_voices = voices_manager.find(Locale=target_locale) if not selected_voices: print(f\"错误:也未找到任何语言为 \'{ target_locale}\' 的语音。将使用默认语音(如果有)。\") # 在这种情况下,如果不指定 voice 给 Communicate,它可能会使用一个内置的默认值(通常是英文) # 或者你可以选择抛出异常或使用一个已知的安全默认值 selected_voice_shortname = \"en-US-AriaNeural\" # 使用一个已知的英文语音作为后备 else: selected_voice_shortname = selected_voices[0][\"ShortName\"] # 选择列表中的第一个 print(f\"已选择备用语音: { selected_voice_shortname}\") else: selected_voice_shortname = selected_voices[0][\"ShortName\"] # 选择列表中的第一个 print(f\"已选择语音: { selected_voice_shortname} ({ selected_voices[0][\'Name\']})\") text_to_speak = \"这是通过Python代码动态选择语音后生成的音频。\" output_filename = \"dynamic_voice_selection_example.mp3\" communicate = edge_tts.Communicate(text_to_speak, selected_voice_shortname) # 使用上面选择的 selected_voice_shortname await communicate.save(output_filename) print(f\"音频文件已保存到: { output_filename}\")if __name__ == \"__main__\": print(\"--- 列出所有可用语音 ---\") asyncio.run(list_available_voices()) # 运行列出语音的函数 print(\"\\n--- 使用动态选择的语音生成音频 ---\") asyncio.run(generate_speech_with_selected_voice()) # 运行使用动态选择语音生成音频的函数
代码解释 (
list_available_voices
):voices_manager = await edge_tts.VoicesManager.create()
:VoicesManager.create()
是一个异步类方法,用于初始化管理器并从微软服务器获取最新的语音列表。必须await
它。all_voices = voices_manager.voices
: 获取一个包含所有语音信息的列表。列表中的每个元素都是一个字典,包含了语音的Name
,ShortName
,Gender
,Locale
等属性。voices_manager.find(Gender=\"Female\", Locale=\"zh-CN\")
:find()
方法允许你根据一个或多个条件来筛选语音。它返回一个匹配条件的语音信息字典的列表。
代码解释 (
generate_speech_with_selected_voice
):- 演示了如何结合
VoicesManager
来动态选择一个语音。 - 首先尝试查找 “zh-CN” 的 “Female” 语音。
- 如果找不到,它会尝试查找任何 “zh-CN” 语音作为备选。
- 如果还是找不到,它会硬编码一个已知的英文语音作为最终的后备,并给出提示。在实际应用中,你可能需要更完善的错误处理或默认逻辑。
- 获取到
selected_voice_shortname
后,将其传递给Communicate
实例。
这种动态查找和选择语音的能力对于构建需要支持多语言或允许用户选择语音的应用非常有用。
-
1.6.4 控制语速、音量和音高 (Python API)
Communicate
类的构造函数接受rate
,volume
, 和pitch
参数来控制这些语音属性。这些参数的格式与命令行工具中的类似(例如\"+10%\"
,\"-20%\"
)。import asyncioimport edge_ttsTEXT = \"Let\'s try adjusting rate, volume, and pitch using the Python API.\"VOICE = \"en-US-GuyNeural\" # 使用 GuyNeural 语音,一个男性英文语音async def generate_with_prosody_controls(): # 定义异步函数来演示韵律控制 \"\"\"Generates speech with different prosody controls.\"\"\" # 示例1: 较慢的语速,较大的音量,较高的音高 output_file_1 = \"prosody_control_1.mp3\" communicate_1 = edge_tts.Communicate( TEXT, VOICE, rate=\"-25%\", # 语速降低 25% volume=\"+10%\", # 音量增加 10% pitch=\"+15%\" # 音高增加 15% ) await communicate_1.save(output_file_1) print(f\"已生成音频 (慢速、大声、高音): { output_file_1}\") # 示例2: 较快的语速,默认音量,较低的音高 output_file_2 = \"prosody_control_2.mp3\" communicate_2 = edge_tts.Communicate( TEXT, VOICE, rate=\"+40%\", # 语速增加 40% # volume 使用默认值 pitch=\"-20%\" # 音高降低 20% ) await communicate_2.save(output_file_2) print(f\"已生成音频 (快速、默认音量、低音): { output_file_2}\") # 示例3: 使用预定义的词语 (如果支持,具体支持的词语需查文档或SSML规范) # 注意:Communicate 的构造函数参数通常期望百分比字符串。 # 要使用 \"x-slow\", \"loud\" 等词,通常需要通过 SSML 实现。 # 这里我们还是用百分比,但可以模拟这些效果。 output_file_3 = \"prosody_control_3_simulated_keywords.mp3\" text_for_keywords = \"This simulates x-slow rate, and medium pitch.\" communicate_3 = edge_tts.Communicate( text_for_keywords, VOICE, rate=\"-50%\", # 模拟 \"x-slow\" pitch=\"0%\" # 模拟 \"medium\" (默认音高) ) await communicate_3.save(output_file_3) print(f\"已生成音频 (模拟 x-slow, medium pitch): { output_file_3}\")if __name__ == \"__main__\": asyncio.run(generate_with_prosody_controls())
代码解释:
- 在创建
Communicate
实例时,直接通过rate
,volume
,pitch
关键字参数传递期望的调整值。 - 值的格式是字符串,例如
\"-25%\"
表示降低25%,\"+40%\"
表示增加40%。\"0%\"
通常表示默认值。 - 这些参数会被
edge-tts
内部转换为相应的 SSML标签属性,然后发送给微软的 TTS 服务。
- 对于更精细或复杂的韵律控制(例如,一句话中不同部分使用不同设置),SSML 是更强大的工具,我们稍后会详细讨论。
- 在创建
-
1.6.5 流式传输音频 (
stream()
方法)
Communicate.save()
方法是一次性将所有音频数据写入文件。但在某些场景下,你可能希望以数据块 (chunks) 的形式逐步获取音频数据,例如:- 直接播放音频流而无需先保存到完整文件。
- 将音频流式传输到另一个进程或网络连接。
- 处理非常长的文本,避免一次性在内存中加载整个音频。
edge-tts
提供了Communicate.stream()
方法来实现这一点。stream()
是一个异步生成器 (async generator),它会yield
音频数据块。import asyncioimport edge_tts# 你可能需要一个播放库,例如 playsound, simpleaudio, pydub, หรือ pygame# 这里我们只是将数据块写入一个文件,模拟流式处理# 在实际播放场景中,你会将这些数据块送入播放器的缓冲区TEXT_STREAM = \"This audio is being streamed chunk by chunk. You could play it as it arrives.\"VOICE_STREAM = \"en-GB-SoniaNeural\" # 使用英国英语女声 SoniaOUTPUT_STREAM_FILE = \"streamed_audio_python.mp3\"async def stream_audio_to_file(): # 定义异步函数来演示流式音频处理 \"\"\"Streams audio data and writes it to a file chunk by chunk.\"\"\" communicate = edge_tts.Communicate(TEXT_STREAM, VOICE_STREAM) print(f\"开始流式传输音频到 { OUTPUT_STREAM_FILE}...\") # \'wb\' 表示以二进制写模式打开文件 with open(OUTPUT_STREAM_FILE, \"wb\") as audio_file: # 打开输出文件 # communicate.stream() 是一个异步生成器 async for chunk in communicate.stream(): # 异步迭代 communicate.stream() 返回的音频块 if chunk[\"type\"] == \"audio\": # 检查块的类型是否为 \"audio\" print(f\"接收到音频数据块,大小: { len(chunk[\'data\'])} bytes\") # 打印接收到的数据块大小 audio_file.write(chunk[\"data\"]) # 将音频数据写入文件 elif chunk[\"type\"] == \"WordBoundary\": # 检查块的类型是否为 \"WordBoundary\" (词边界信息) print(f\"词边界: Text=\'{ chunk[\'text\']}\', Offset={ chunk[\'offset\']}, Duration={ chunk[\'duration\']}\") # chunk[\'text\']: 当前词语的文本 # chunk[\'offset\']: 当前词语在整个音频中的开始时间 (通常是相对于音频流开始的字节偏移或时间偏移,具体看库的实现) # chunk[\'duration\']: 当前词语的持续时间 elif chunk[\"type\"] == \"SentenceBoundary\": # 检查块的类型是否为 \"SentenceBoundary\" (句子边界信息) print(f\"句子边界: Text=\'{ chunk[\'text\']}\', Offset={ chunk[\'offset\']}, Duration={ chunk[\'duration\']}\") print(f\"音频流传输完成。文件已保存: { OUTPUT_STREAM_FILE}\")if __name__ == \"__main__\": asyncio.run(stream_audio_to_file())
代码解释:
async for chunk in communicate.stream():
:communicate.stream()
返回一个异步生成器。我们使用async for
来迭代它。在每次迭代中,chunk
是一个字典,包含了该数据块的信息。chunk[\"type\"]
: 这个键表示数据块的类型。\"audio\"
: 表示chunk[\"data\"]
包含的是实际的音频字节数据 (例如 MP3 数据的一部分)。\"WordBoundary\"
: 表示这是一个词边界事件。chunk
中会包含关于当前识别到的词的信息,如text
(词文本),offset
(在音频流中的偏移量,单位可能是字节或毫秒,取决于edge-tts
的实现和服务器返回的信息), 和duration
(词的持续时间,单位可能是毫秒)。这对于实现字幕同步或语音高亮非常有用。\"SentenceBoundary\"
: 类似于词边界,但表示句子结束的事件。
audio_file.write(chunk[\"data\"])
: 如果块是音频数据,我们将其写入文件。在实际播放应用中,你会将此数据送入音频播放库的缓冲区。- 处理边界信息: 我们可以选择性地处理词边界和句子边界信息。
流式播放示例 (概念性,需要音频播放库):
假设你有一个名为player
的音频播放对象,它有一个feed(data)
方法来接收音频数据块并播放。# 这是一个概念性的例子,实际代码需要具体的播放库import asyncioimport edge_tts# from some_audio_player_library import Player # 假设有一个播放库# async def play_audio_stream_concept():# player = Player() # 初始化播放器# await player.start() # 启动播放器# communicate = edge_tts.Communicate(\"Streaming directly to player.\", \"en-US-JennyNeural\")# async for chunk in communicate.stream():# if chunk[\"type\"] == \"audio\":# await player.feed(chunk[\"data\"]) # 将音频数据块送入播放器# elif chunk[\"type\"] == \"WordBoundary\":# print(f\"Playing word: {chunk[\'text\']}\") # 可以在这里做一些同步UI更新# await player.stop() # 播放完毕后停止播放器
我们将在后续章节中更详细地探讨如何结合具体音频库实现流式播放。
-
1.6.6 使用代理 (
proxy
参数)
与命令行工具类似,Communicate
类的构造函数也接受一个proxy
参数,用于在需要通过代理服务器访问微软 TTS 服务时进行配置。import asyncioimport edge_ttsTEXT_PROXY = \"Testing TTS via a proxy server using Python.\"VOICE_PROXY = \"en-AU-NatashaNeural\" # 澳大利亚英语女声OUTPUT_PROXY_FILE = \"proxy_test_python.mp3\"# 替换为你的实际代理服务器地址和端口# 支持的格式通常是 \"http://user:pass@host:port\" 或 \"socks5://user:pass@host:port\"# 具体支持的格式取决于底层的 aiohttp 库PROXY_SERVER = \"http://localhost:8080\" # 假设本地有一个HTTP代理在8080端口async def generate_with_proxy(): # 定义异步函数来演示使用代理 \"\"\"Generates speech using a proxy server.\"\"\" try: communicate = edge_tts.Communicate( TEXT_PROXY, VOICE_PROXY, proxy=PROXY_SERVER # 传入代理服务器信息 ) await communicate.save(OUTPUT_PROXY_FILE) print(f\"通过代理 \'{ PROXY_SERVER}\' 生成音频成功: { OUTPUT_PROXY_FILE}\") except Exception as e: # 捕获可能发生的异常 # 例如,代理服务器不可用,或者代理配置错误 print(f\"通过代理 \'{ PROXY_SERVER}\' 生成音频失败: { e}\") print(\"请确保代理服务器正在运行并且配置正确。\") print(\"如果不需要代理,请将 PROXY_SERVER 设置为 None 或移除 proxy 参数。\")if __name__ == \"__main__\": # 重要提示:要测试此代码,你需要一个实际在 PROXY_SERVER 地址上运行的代理服务器。 # 如果你没有运行代理,可以将 PROXY_SERVER 设置为 None,或者直接调用不带 proxy 参数的 Communicate。 # 例如,如果无代理: # communicate = edge_tts.Communicate(TEXT_PROXY, VOICE_PROXY) # 为了演示,我们假设代理可能不存在,所以用 try-except 包裹 asyncio.run(generate_with_proxy())
代码解释:
- 在创建
Communicate
实例时,将代理服务器的 URL 字符串传递给proxy
参数。 edge-tts
底层使用aiohttp
进行网络请求,aiohttp
会处理代理连接。- 代码中加入了
try-except
块来捕获可能因代理问题(如代理不可达、认证失败等)引发的异常。 - 注意: 测试此代码需要一个实际可用的代理服务器。如果你的网络环境不需要代理,可以将
PROXY_SERVER
设置为None
或直接省略proxy
参数。
- 在创建
-
1.6.7 错误处理和异常
网络请求和外部服务交互总是有可能出现问题。edge-tts
在遇到问题时会抛出异常。你的代码应该准备好处理这些异常,以确保健壮性。常见的异常可能包括:
edge_tts.exceptions.NoAudioReceivedError
: 未能从服务器接收到任何音频数据。可能是因为文本过短、包含不支持的字符、或者服务器临时问题。edge_tts.exceptions.UnknownResponseError
: 服务器返回了未知的或错误格式的响应。edge_tts.exceptions.VoicesManagerError
: 在获取语音列表时发生错误。- 底层的
aiohttp
异常,例如aiohttp.ClientConnectorError
(无法连接到服务器)、aiohttp.ClientProxyConnectionError
(代理连接问题)、aiohttp.ClientTimeout
(请求超时) 等。
import asyncioimport edge_ttsfrom edge_tts.exceptions import NoAudioReceivedError, VoicesManagerError # 导入特定的异常类import aiohttp # 导入 aiohttp 以便捕获其特定异常TEXT_ERROR_HANDLING = \"Testing error handling capabilities.\"VOICE_ERROR_HANDLING = \"en-IN-PrabhatNeural\" # 印度英语男声OUTPUT_ERROR_HANDLING_FILE = \"error_handling_test.mp3\"async def generate_with_error_handling(): # 定义异步函数来演示错误处理 \"\"\"Generates speech with error handling.\"\"\" try: print(f\"尝试获取语音列表...\") voices_manager = await edge_tts.VoicesManager.create(proxy=\"http://nonexistent.proxy:1234\") # 故意使用无效代理 # voices_manager = await edge_tts.VoicesManager.create() # 正常情况 available_voices = voices_manager.find(Locale=\"en-IN\") if not available_voices: print(\"警告: 未找到指定的 en-IN 语音,将尝试使用默认语音或可能失败。\") selected_voice = VOICE_ERROR_HANDLING # 即使找不到也继续尝试,看Communicate的行为 else: selected_voice = available_voices[0][\"ShortName\"] print(f\"选择的语音: { selected_voice}\") print(f\"\\n尝试生成语音: \'{ TEXT_ERROR_HANDLING}\'\") # 故意制造一个可能导致 NoAudioReceivedError 的情况 (例如,非常规字符或空文本) # text_to_send = \"@@@###$$$\" text_to_send = TEXT_ERROR_HANDLING # 正常文本 # 模拟网络不通的情况,可以临时修改 Communicate 内部连接的 URL,但这不推荐直接做 # 更实际的测试是断开网络或使用无效代理 communicate = edge_tts.Communicate( text_to_send, selected_voice, # proxy=\"http://nonexistent.proxy:1234\" # 也可以在这里设置无效代理 ) await communicate.save(OUTPUT_ERROR_HANDLING_FILE) print(f\"音频生成成功: { OUTPUT_ERROR_HANDLING_FILE}\") except VoicesManagerError as vme: # 捕获 VoicesManager 相关的错误 print(f\"获取语音列表时发生错误: { vme}\") print(\"请检查网络连接和代理设置(如果使用)。\") except NoAudioReceivedError as nare: # 捕获未收到音频的错误 print(f\"错误: 未能从服务器接收到音频数据。 { nare}\") print(\"这可能是因为文本内容不被支持、文本过短、或者服务器端问题。\") except aiohttp.ClientConnectorError as cce: # 捕获 aiohttp 连接错误 print(f\"网络连接错误: 无法连接到语音服务。 { cce}\") print(\"请检查你的网络连接。\") except aiohttp.ClientProxyConnectionError as cpce: # 捕获 aiohttp 代理连接错误 print(f\"代理连接错误: 无法通过代理连接到语音服务。 { cpce}\") print(\"请检查代理服务器的地址、端口、用户名和密码是否正确,以及代理服务器是否正常运行。\") except aiohttp.ClientTimeout as cto: # 捕获 aiohttp 超时错误 print(f\"请求超时: 连接到语音服务或接收数据超时。 { cto}\") print(\"网络可能不稳定,或者服务器响应缓慢。可以尝试增加超时设置(如果库支持)。\") except edge_tts.exceptions.UnknownResponseError as ure: # 捕获未知响应错误 print(f\"服务器返回未知响应: { ure}\") except Exception as e: # 捕获所有其他未预料到的异常 print(f\"发生未知错误: { type(e).__name__} - { e}\") # 在实际应用中,你可能还想记录堆栈跟踪 # import traceback # print(traceback.format_exc()) finally: print(\"\\n错误处理演示结束。\")if __name__ == \"__main__\": # 要触发 VoicesManagerError 或 ClientProxyConnectionError, # 你可以在 VoicesManager.create() 或 Communicate() 中提供一个无效的代理地址。 # 要触发 NoAudioReceivedError,可以尝试传递非常短的或包含大量非语音字符的文本。 # 要触发 ClientConnectorError,可以尝试在没有网络连接的情况下运行。 asyncio.run(generate_with_error_handling())
代码解释:
- 我们使用
try...except
块来包裹可能抛出异常的edge-tts
操作。 - 我们捕获了
edge_tts
自定义的几个主要异常,如VoicesManagerError
,NoAudioReceivedError
,UnknownResponseError
。 - 我们还捕获了更底层的
aiohttp
客户端异常,如ClientConnectorError
(网络连接问题),ClientProxyConnectionError
(代理问题),ClientTimeout
(超时)。 - 最后有一个通用的
except Exception as e:
来捕获任何其他未预料到的错误。 - 在
except
块中,我们打印错误信息,并可以根据错误类型给用户提供有用的反馈或执行回退逻辑。 - 测试方法:
VoicesManagerError
/ClientProxyConnectionError
: 在VoicesManager.create()
或Communicate()
中传入一个无效的代理地址,例如proxy=\"http://nonexistent.proxy:1234\"
。NoAudioReceivedError
: 尝试给Communicate
传递非常短的文本(如单个特殊字符)或完全空的字符串。ClientConnectorError
: 在运行脚本前断开你的网络连接。ClientTimeout
: 如果网络非常慢,或者服务器响应慢,可能会触发。aiohttp
有默认超时,edge-tts
可能也允许配置。
良好的错误处理对于构建可靠的应用程序至关重要。
第二章:精通 SSML —— 驾驭语音合成的艺术
SSML (Speech Synthesis Markup Language) 是一种基于 XML 的标记语言,旨在为语音合成应用提供一种标准化的方式来控制语音输出的各个方面,如发音、音量、音高、语速、停顿、强调等等。通过使用 SSML,开发者可以超越简单的纯文本输入,创造出更自然、更富有表现力、更符合特定场景需求的合成语音。
edge-tts
库(以及其背后的微软语音服务)对 SSML 提供了良好的支持。当你向 Communicate
类提供 SSML 格式的输入时,你可以解锁许多通过纯文本参数难以实现或无法实现的控制能力。
2.1 SSML 基础回顾
在深入 edge-tts
的具体应用之前,我们先快速回顾一下 SSML 的核心元素和结构。一个基本的 SSML 文档结构如下:
<speak version=\"1.0\" xmlns=\"http://www.w3.org/2001/10/synthesis\" xmlns:mstts=\"http://www.w3.org/2001/mstts\" xml:lang=\"en-US\"> Your text content and SSML tags go here.</speak>
元素:
- 是 SSML 文档的根元素。所有其他 SSML 标签都必须包含在
标签内。
version=\"1.0\"
: 指定使用的 SSML 版本。xmlns=\"http://www.w3.org/2001/10/synthesis\"
: 标准的 SSML 命名空间。xmlns:mstts=\"http://www.w3.org/2001/mstts\"
(可选,但对于微软特定功能常用): 微软 TTS 特有的命名空间,用于支持一些微软扩展的 SSML 功能,如情感表达、特定说话风格等。xml:lang=\"en-US\"
: 指定文档内容的主要语言和地区。这个属性非常重要,它会影响发音、词汇选择和默认语音(如果在内部没有用标签明确指定)。例如,
zh-CN
代表中国大陆的普通话,ja-JP
代表日语。
- 是 SSML 文档的根元素。所有其他 SSML 标签都必须包含在
2.2 在 edge-tts
Python 库中使用 SSML
要在 edge-tts
的 Python API 中使用 SSML,你需要将完整的 SSML 字符串作为 text
参数传递给 Communicate
类的构造函数。此时,voice
参数(以及 rate
, volume
, pitch
等基本参数)的行为会有些不同:
- 如果 SSML 内部通过
标签指定了语音,那么
Communicate
构造函数中的voice
参数通常会被忽略,或者其作用可能取决于edge-tts
内部处理 SSML 的逻辑(例如,是否作为未指定语音时的后备)。通常建议在 SSML 中明确指定语音。 - 如果 SSML 内部没有指定语音,那么
Communicate
构造函数中的voice
参数(如果提供)将被用作整个 SSML 文档的默认语音。 - 对于
rate
,volume
,pitch
等参数,如果它们在Communicate
构造函数中设置,并且 SSML 文本本身(不在任何标签内)也包含纯文本,这些参数可能会应用于那些纯文本部分。但是,SSML 内部的
标签会覆盖这些全局设置。
最佳实践是:当使用 SSML 时,尽可能在 SSML 内部通过相应的标签来控制语音、语速、音量等属性,以获得最精确和可预测的结果。
让我们看一个使用 SSML 的基本 Python 示例:
import asyncioimport edge_tts# VOICE_FOR_SSML = \"en-US-AriaNeural\"VOICE_FOR_SSML = \"zh-CN-XiaoxiaoNeural\" # 使用晓晓 Neural 语音# SSML 字符串# 注意:xml:lang 应该与你选择的语音的语言匹配SSML_TEXT = f\"\"\" <voice name=\"{ VOICE_FOR_SSML}\"> 你好,世界! 这是一个使用 SSML 的测试。 这句话的语速会比较慢,音调也会稍低一些。 而这句话会说得很快,音调也比较高! 再会! \"\"\"# f-string 用于方便地将 VOICE_FOR_SSML 变量插入到 SSML 字符串中# OUTPUT_SSML_FILE = \"ssml_example_python.mp3\"OUTPUT_SSML_FILE = \"ssml_示例_python.mp3\"async def generate_from_ssml(): # 定义异步函数从 SSML 生成音频 \"\"\"Generates audio from an SSML string.\"\"\" # 当text参数是SSML时,Communicate 会自动识别 # voice 参数在这里是可选的,因为SSML内部已经指定了voice # 如果SSML内部没有标签,这里的voice参数会作为默认 communicate = edge_tts.Communicate(SSML_TEXT, VOICE_FOR_SSML) # 即使SSML中指定了voice,Communicate 的 voice 参数也最好提供一个匹配的, # 以免 edge-tts 内部对语言或元数据处理产生困惑。 # 或者,如果 SSML 内部已完整定义,可以将 Communicate 的 voice 参数设为 None 或省略, # 但需要测试具体版本的行为。通常提供一个与 xml:lang 匹配的 voice 是安全的。 await communicate.save(OUTPUT_SSML_FILE) print(f\"SSML 音频文件已保存到: { OUTPUT_SSML_FILE}\") print(\"\\n使用的 SSML 内容:\") print(SSML_TEXT)if __name__ == \"__main__\": asyncio.run(generate_from_ssml())
代码解释:
VOICE_FOR_SSML
: 定义了我们想在 SSML 中使用的语音的ShortName
。SSML_TEXT
: 这是一个多行 f-string,包含了完整的 SSML 文档。xml:lang=\"zh-CN\"
: 表明 SSML 内容是中文。: 明确指定了要使用的语音。
: 插入一个持续 500 毫秒的停顿。
...
: 将内部文本的语速减慢,音高降低10%。: 插入一个中等时长的停顿(具体时长由 TTS 引擎根据上下文决定)。
: 这是一个微软特定的标签 (
mstts
命名空间),在它后面的 “再会!” 文本之前插入 300ms 的静音。: 在 “再会!” 文本之后插入 1 秒的静音。
communicate = edge_tts.Communicate(SSML_TEXT, VOICE_FOR_SSML)
: 我们将整个SSML_TEXT
字符串作为第一个参数(通常是纯文本的text
参数)传递给Communicate
。edge-tts
能够识别输入是否为有效的 XML/SSML 字符串。我们仍然传递了VOICE_FOR_SSML
作为第二个参数,虽然 SSML 内部已经指定了语音,但这通常是一个好的做法,可以帮助edge-tts
正确设置与该语音相关的元数据或进行兼容性检查。如果 SSML 中没有标签,这里的
voice
参数将成为必需。
运行此脚本后,生成的 ssml_示例_python.mp3
文件将包含根据 SSML 指令精确控制的语音输出。
2.3 常用 SSML 标签详解及其在 edge-tts
中的应用
下面我们将详细介绍一些常用的 SSML 标签,并提供如何在 edge-tts
中使用它们的示例。
2.3.1 - 选择语音、语言和性别
voice
标签用于在 SSML 文档的不同部分使用不同的语音,或者明确指定要使用的语音。
- 属性:
name
(必需): 指定要使用的语音的ShortName
(例如,\"en-US-AriaNeural\"
,\"zh-CN-XiaoxiaoNeural\"
). 你可以从edge-tts --list-voices
或VoicesManager
获取这些名称。gender
(可选): 指定期望的性别 (\"male\"
,\"female\"
,\"neutral\"
). 如果同时提供了name
,则name
优先。如果只提供gender
和xml:lang
(或外部voice
参数的语言),TTS 服务会尝试选择一个匹配的语音。xml:lang
(可选,但强烈推荐在级别或更高级别
明确指定): 指定该
标签内文本的语言和地区代码。如果
标签的
xml:lang
与其父元素(如)的
xml:lang
不同,则会发生语言切换。
示例:在同一段语音中切换中英文,并使用不同语音
import asyncioimport edge_tts# 语音选择CHINESE_VOICE = \"zh-CN-YunxiNeural\" # 云希 (男声)ENGLISH_VOICE = \"en-US-GuyNeural\" # Guy (男声)SSML_MULTI_VOICE = f\"\"\" <voice name=\"{ CHINESE_VOICE}\"> 你好,我将说一段中文。 <voice name=\"{ ENGLISH_VOICE}\" xml:lang=\"en-US\"> Now, I will speak some English. This voice is different. <voice name=\"{ CHINESE_VOICE}\"> 现在我又说回中文了。 如果您有任何问题,请随时提出。 \"\"\"# mstts:express-as styledegree=\"0.8\" : 尝试使用微软特定的客服说话风格,强度为0.8# 并非所有语音都支持所有风格,支持程度也可能变化OUTPUT_MULTI_VOICE_FILE = \"ssml_multi_voice_python.mp3\"async def generate_multi_voice_ssml(): # 定义异步函数来生成多语音SSML音频 # 当 SSML 中包含 时,Communicate 的第二个 voice 参数可以省略或设为 None, # 或者提供一个与 标签的 xml:lang 匹配的默认 voice。 # 这里我们提供 CHINESE_VOICE 作为 Communicate 的 voice 参数, # 它将作为 SSML 中没有明确 标签部分的默认(尽管本例中所有部分都有)。 communicate = edge_tts.Communicate(SSML_MULTI_VOICE, CHINESE_VOICE) await communicate.save(OUTPUT_MULTI_VOICE_FILE) print(f\"多语音 SSML 文件已保存到: { OUTPUT_MULTI_VOICE_FILE}\") print(\"\\n使用的 SSML:\")