做语音助手、给文章配有声版、批量生成短视频配音时,第一步往往都是“把文字变成声音”。过去要拿到自然的语音,要么用系统自带、机械感很强的离线引擎,要么接入付费的云 TTS 服务。Edge-TTS 的出现改变了这一点:它是微软开源的 文本转语音(TTS) 库,直接调用与 Edge 浏览器同源的在线神经语音,音质接近真人,关键是完全免费、无需申请 API Key。本文从原理到实战,带你用 Python 把任意文本合成为自然流畅的语音文件,并封装成可调用的服务。
一、TTS 是什么:从拼接合成到神经网络语音
文本转语音(Text-To-Speech,简称 TTS)的目标,是让机器把一段文字“读”出来。早期方案是“拼接合成”:把录好的音素、音节裁剪拼接,声音生硬、断句怪异;后来演进到“参数合成”,用声码器建模声学特征,流畅度提升但仍显机械。如今主流是神经网络语音(Neural TTS):模型直接端到端学习“文字→波形”,能自然处理语气、停顿和重音,听感已非常接近真人。微软的在线神经语音就属于这一代,而 Edge-TTS 让我们无需付费就能免费使用它。
二、为什么选 Edge-TTS
横向对比几个常见方案,Edge-TTS 的优势很明显:它基于微软云端神经语音,音质高、音色多、支持中文在内的数十种语言,而且对个人开发者零门槛——不用注册、不用 Key、没有免费额度焦虑。相比完全离线但机械的 pyttsx3,它胜在“像人”;相比同样高质量的 Azure TTS,它胜在“免费”。代价是合成时必须联网调用微软服务(并非离线引擎),且不适合对延迟和并发有极端要求的生产核心链路。对大多数个人项目、内部工具、内容创作来说,这个取舍非常划算。
2.1 几类方案的定位
| 方案 | 音质 | 是否免费 | 是否需联网 | 适用场景 |
|---|---|---|---|---|
| Edge-TTS | 神经语音·高 | 是(免 Key) | 是 | 个人项目、内容配音、原型 |
| pyttsx3 | 离线·机械 | 是 | 否 | 无网络环境、简单提示音 |
| Azure TTS | 神经语音·高 | 付费 | 是 | 企业级、需 SLA 保障 |
| CosyVoice / ChatTTS | 神经语音·高 | 开源自部署 | 自部署 | 需要私有化、定制音色 |
三、安装与环境准备
Edge-TTS 是一个纯 Python 包,底层走 WebSocket 与微软服务通信。安装只需一行,Python 3.8+ 均可。
# 安装
pip install edge-tts
# 命令行直接试一句(无需写代码)
edge-tts --text "你好,这是一段测试语音" --voice zh-CN-XiaoxiaoNeural --write-media hello.mp3
如果命令行能正常生成 hello.mp3,说明网络与运行环境都没问题,接下来就可以用 Python API 精细控制了。需要编写脚本时,用 VS Code 这类编辑器配合终端运行最顺手。
四、第一行:把文本合成成语音文件
Edge-TTS 是异步库(基于 asyncio),核心对象是 Communicate。传入文本和音色名,调用 save() 即可落盘为 mp3。
import asyncio, edge_tts
TEXT = "欢迎使用 Edge-TTS,这是一段由神经网络生成的语音。"
VOICE = "zh-CN-XiaoxiaoNeural" # 微软晓晓,中文女声
async def main():
communicate = edge_tts.Communicate(text=TEXT, voice=VOICE)
await communicate.save("output.mp3")
print("已生成 output.mp3")
asyncio.run(main())
就这么几行,你就拥有了一段接近真人的中文朗读。注意所有调用都写在 async 函数里,最后用 asyncio.run() 启动事件循环——这是 Edge-TTS 的硬性约定,忘记 await 或没跑事件循环是新手最常见的报错来源。
五、选音色与语言:先列一遍可用声音
Edge-TTS 内置上百种音色,覆盖中、英、日、韩等多语言,每种语言还有男女声与不同风格(如“晓晓”“云希”“云扬”)。先列出全部可用音色,再挑合适的:
import asyncio, edge_tts
async def list_voices():
voices = await edge_tts.list_voices()
for v in voices:
# ShortName 形如 zh-CN-XiaoxiaoNeural
if v["Locale"].startswith("zh") or v["Locale"].startswith("en"):
print(v["ShortName"], v["Locale"], v["Gender"])
asyncio.run(list_voices())
常用的中文音色有 zh-CN-XiaoxiaoNeural(女声·温柔)、zh-CN-YunxiNeural(男声·活泼)、zh-CN-YunyangNeural(男声·新闻播报风);英文可选 en-US-AriaNeural、en-US-GuyNeural 等。音色名统一以“语言-地区-名字”命名,切换多语种朗读时改一个 VOICE 变量即可。
六、用 SSML 调节语速、音调和停顿
单纯传文本时,语速、停顿都由模型默认处理。若要更精细控制——比如给儿童内容放慢语速、在标题后加长停顿——可以用 SSML(语音合成标记语言)。Edge-TTS 接受 SSML 字符串作为输入,通过 <prosody> 调速率/音高,用 <break> 插入停顿。
import asyncio, edge_tts
SSML = '''<?xml version="1.0" encoding="utf-8"?>
<speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis" xml:lang="zh-CN">
<voice name="zh-CN-XiaoxiaoNeural">
大家好,欢迎收听本期技术播客。
<break time="600ms"/>
<prosody rate="0.9" pitch="+3%">今天我们用更慢的语速,讲清楚 Edge-TTS 的玩法。</prosody>
</voice>
</speak>'''
async def main():
# 传入 SSML 时,voice 参数可省略(由 <voice> 标签决定)
communicate = edge_tts.Communicate(text=SSML, voice=None)
await communicate.save("ssml_output.mp3")
asyncio.run(main())
rate 取值如 0.9(慢 10%)、1.2(快 20%);pitch 用百分比或半音;break time 支持 ms 或 s。合理加停顿和语速变化,能显著提升听感,尤其适合做有声书、课程录音。
七、长文本分片与批量合成
微软单次合成有长度和时长的上限(通常数千字符、音频约 10 分钟封顶)。给长文配音时,必须先把文本按句号、段落切分成片段,逐段合成,再拼接。下面给出一个“按标点断句 + 顺序合成”的稳健写法:
import asyncio, re, edge_tts
def split_text(text, max_len=200):
# 优先按标点断句,避免把一句话拦腰截断
parts, buf = [], ""
for seg in re.split(r"(?<=[。!?.!?])", text):
if len(buf) + len(seg) <= max_len:
buf += seg
else:
if buf: parts.append(buf)
buf = seg
if buf: parts.append(buf)
return parts
async def synthesize(long_text, voice, out_prefix):
for i, chunk in enumerate(split_text(long_text)):
c = edge_tts.Communicate(text=chunk, voice=voice)
await c.save(f"{out_prefix}_{i:03d}.mp3")
print(f"片段 {i} 完成,{len(chunk)} 字")
# 之后用 ffmpeg 把 001/002... 按顺序拼接成完整音频即可
# ffmpeg -f concat -safe 0 -i list.txt -c copy final.mp3
切分时按中文句号、问号、感叹号断句,既保证单段不超限,又不会在句子中间切断导致朗读生硬。多段合成完,用一行 ffmpeg 命令即可串成完整音频文件。
八、封装一个命令行 TTS 工具
把上面的能力收拢成一个可复用的小工具,支持“指定文本/文件、选音色、输出路径”,日常用起来更顺。
import asyncio, argparse, edge_tts
async def run(text, voice, out):
await edge_tts.Communicate(text=text, voice=voice).save(out)
if __name__ == "__main__":
p = argparse.ArgumentParser()
p.add_argument("-t", "--text", help="要合成的文本")
p.add_argument("-f", "--file", help="读取文本文件")
p.add_argument("-v", "--voice", default="zh-CN-XiaoxiaoNeural")
p.add_argument("-o", "--out", default="out.mp3")
a = p.parse_args()
src = open(a.file, encoding="utf-8").read() if a.file else a.text
asyncio.run(run(src, a.voice, a.out))
print("done ->", a.out)
# 用法:
# python tts.py -t "今天天气不错" -v zh-CN-YunxiNeural -o a.mp3
# python tts.py -f article.txt -o book.mp3
九、把它做成 API:配合 FastAPI 在线合成
若要集成进网页或 App,把 TTS 包成一个 HTTP 接口最方便。结合本站讲过的 FastAPI 流式 API 思路,下面用 StreamingResponse 把合成结果以音频流返回,前端拿到即可直接播放:
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
import edge_tts, io, asyncio
app = FastAPI()
@app.get("/tts")
async def tts(text: str, voice: str = "zh-CN-XiaoxiaoNeural"):
communicate = edge_tts.Communicate(text=text, voice=voice)
buf = io.BytesIO()
async for chunk in communicate.stream():
if chunk["type"] == "audio":
buf.write(chunk["data"])
buf.seek(0)
return StreamingResponse(buf, media_type="audio/mpeg",
headers={"Content-Disposition": "attachment; filename=tts.mp3"})
# 启动:uvicorn main:app --port 8000
# 调用:http://127.0.0.1:8000/tts?text=你好世界
这里的 stream() 会边合成边返回音频分片,配合流式响应能做到“边生成边播放”,体验比先生成完整文件再返回更好。把它用 Docker 打包后,就能稳定部署在内网或云服务器上,供多个业务共用。
| 环节 | 要点 |
|---|---|
| 事件循环 | 全程 await,入口用 asyncio.run 或 FastAPI 的 async 视图 |
| 音色选择 | 中文首推 Xiaoxiao / Yunxi,多语切换改 voice 即可 |
| 长文本 | 先按标点切分,再逐段合成、ffmpeg 拼接 |
| 返回方式 | 小文件 save 落盘;在线服务用 StreamingResponse 流式返回 |
十、与语音识别组成闭环:STT + TTS
文本转语音只是语音交互的一半。把本文的 TTS 与语音识别(STT)结合,就能搭出一个最小可用的“语音对话”闭环:麦克风采集声音 → STT 转成文字 → 大模型生成回复 → TTS 读出来。这种组合特别适合做本地知识库的语音问答、无障碍朗读、自动播客生成等场景;若要更进一步,还能接上 Dify + Ollama 这类编排平台,把“听懂—思考—回答—朗读”串成一条流水线。
十一、常见坑与排障
| 现象 | 原因与对策 |
|---|---|
| RuntimeError: asyncio 事件循环相关 | 确认逻辑写在 async 函数内,并用 asyncio.run 启动 |
| 返回空文件 / 无音频 | 检查文本是否为空、网络是否可达微软服务(需联网) |
| 长文本被截断 | 单段超字符/时长上限,按第七节切分后再合成 |
| 中文乱码 | 读取文本文件时指定 encoding=’utf-8′ |
| 并发过多被限流 | 控制并发数,逐段顺序合成,必要时加重试与退避 |
| 音色名无效 | 先用 list_voices() 列出真实 ShortName,避免手敲拼写错误 |
另外要记住:Edge-TTS 调用的是微软在线服务,必须能访问外网;在隔离内网里它会直接失败。对内网强隔离且要求离线的场景,应改用 pyttsx3 或自部署 CosyVoice 等方案。
十二、小结
Edge-TTS 用极低的门槛,把高质量神经 文本转语音 能力交到了每个 Python 开发者手里:免 Key、免费、音质自然、支持 SSML 精细调控。从一行 save() 到封装命令行工具,再到用 FastAPI 暴露成在线接口,它都能平滑胜任。把它和语音识别、大模型编排组合起来,你就能快速搭出语音助手、有声内容生产线这类过去“想做但嫌贵”的应用。下一步,不妨挑一篇你自己的文章,用文中的脚本生成一段朗读音频,亲耳感受一下神经网络语音的听感差异。




