Edge-TTS 安装使用完整教程
1. Edge-TTS 简介
Edge-TTS 是一个 Python 库,它允许你直接调用微软 Edge 浏览器的在线文本转语音服务,无需安装 Edge 浏览器,无需 Windows 系统,也无需 API 密钥,即可免费使用微软的神经网络语音合成技术。它本质上是调用微软 Azure 认知服务的神经语音合成接口,但通过开源封装规避了付费限制,支持 60 余种语言,可生成接近真人发音的高质量语音。
主要优势:
- 完全免费,无需注册或申请 API Key
- 支持多种语言和语音角色(包括普通话、粤语、英语等)
- 生成的语音自然度高,基于深度学习模型
- 支持语速、音量、音调等参数调节
- 支持流式输出,适合实时交互场景
使用限制:
- 不建议将输出语音用于商业售卖(如制作有声书出售)
- 单日调用次数建议控制在 5 万次以内,避免触发风控
- 需要网络连接(调用微软在线服务)
2. 环境准备与安装
2.1 前置条件
确保系统已安装 Python 3.8 或更高版本,可通过以下命令检查:
bash
python --version
2.2 安装 Edge-TTS
使用 pip 安装:
bash
pip install edge-tts
如果只打算在命令行中使用(不嵌入 Python 项目),推荐使用 pipx 安装,避免污染全局环境:
bash
pipx install edge-tts
2.3 验证安装
安装完成后,验证是否成功:
bash
edge-tts --list-voices | grep "zh-CN"
如果能看到中文语音列表,说明安装成功。
3. 命令行基础使用
3.1 最简单的语音合成
bash
edge-tts --text "你好,这是Edge TTS语音合成测试。" --write-media output.mp3
3.2 指定语音角色
bash
edge-tts --voice zh-CN-YunxiNeural --text "大家好,欢迎使用Edge TTS" --write-media hello_cn.mp3
3.3 查看所有可用语音
bash
edge-tts --list-voices > voices.json
常用中文语音角色:
| 语音名 | 说明 |
|---|---|
| zh-CN-XiaoxiaoNeural | 女声(自然流畅) |
| zh-CN-YunxiNeural | 男声(中气十足) |
| zh-CN-YunjianNeural | 男声(成熟稳重) |
| zh-CN-XiaoyiNeural | 女声(温柔型) |
| zh-HK-HiuGaaiNeural | 粤语(香港)女声 |
| zh-TW-HsiaoChenNeural | 台湾普通话女声 |
3.4 调整语速、音量和音调
bash
# 语速减慢50%
edge-tts --rate=-50% --text "Hello, world!" --write-media slow.mp3
# 音量降低50%
edge-tts --volume=-50% --text "Hello, world!" --write-media quiet.mp3
# 音调降低50Hz
edge-tts --pitch=-50Hz --text "Hello, world!" --write-media low_pitch.mp3
参数格式说明:
- rate :语速调整,如
+20%表示加快 20%,-10%表示减慢 10% - volume :音量调整,如
+50%表示增大 50% - pitch :音调调整,如
+5Hz表示升高 5Hz,-10Hz表示降低 10Hz
3.5 从文件读取文本
bash
edge-tts --text-file input.txt --write-media output.mp3
3.6 生成字幕文件
bash
edge-tts --text "Hello world" --write-media output.mp3 --write-subtitles output.srt
4. Python 项目中的详细使用
4.1 同步调用(快速上手)
对于简单的脚本,可以使用 save_sync 方法进行同步调用:
python
import edge_tts
text = "大家好,欢迎关注语音之家。"
voice = "zh-CN-YunxiNeural"
communicate = edge_tts.Communicate(text=text, voice=voice)
communicate.save_sync("output.mp3")
这是最简洁的调用方式,适合快速验证和简单场景。
4.2 异步调用(推荐)
Edge-TTS 原生是异步库,在异步项目中使用 await communicate.save() 是更高效的方式:
python
import asyncio
import edge_tts
async def main():
text = "大家好,这是异步方式生成的语音。"
voice = "zh-CN-XiaoxiaoNeural"
communicate = edge_tts.Communicate(text, voice)
await communicate.save("output.mp3")
asyncio.run(main())
如果你已经在异步框架(如 FastAPI、aiohttp)中,直接在协程内 await 即可:
python
async def generate_speech(text: str, output_path: str):
communicate = edge_tts.Communicate(text, "zh-CN-YunxiNeural")
await communicate.save(output_path)
注意 :Edge-TTS 是异步库,无法直接在非异步上下文中使用 await。如果必须在同步代码中调用,可以通过事件循环包装:
python
import asyncio
def sync_generate(text, voice, output):
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
communicate = edge_tts.Communicate(text, voice)
loop.run_until_complete(communicate.save(output))
loop.close()
4.3 流式获取音频数据
communicate.stream() 方法以流式方式返回音频数据块,适合需要实时处理音频的场景(如边生成边播放、实时对话系统):
python
import asyncio
import edge_tts
async def stream_audio(text, voice="zh-CN-XiaoxiaoNeural"):
communicate = edge_tts.Communicate(text, voice)
audio_bytes = b""
async for chunk in communicate.stream():
if chunk["type"] == "audio":
audio_bytes += chunk["data"]
return audio_bytes
每个 chunk 的结构包含 type 和 data 两个字段。当 type == "audio" 时,data 是音频二进制数据;还可能出现 type == "WordBoundary" 的 chunk,包含单词边界信息,可用于生成字幕。
4.4 获取单词边界时间戳
流式处理时,WordBoundary 类型的 chunk 提供了每个词的精确时间信息:
python
async def get_word_timings(text, voice="zh-CN-XiaoxiaoNeural"):
communicate = edge_tts.Communicate(text, voice)
timings = []
async for chunk in communicate.stream():
if chunk["type"] == "WordBoundary":
timings.append({
"text": chunk["text"],
"offset": chunk["offset"], # 100纳秒单位
"duration": chunk["duration"] # 100纳秒单位
})
return timings
这些时间戳可以用于生成 SRT 字幕或实现卡拉 OK 效果。
5. 参数详解与高级调优
5.1 Communicate 构造参数
python
communicate = edge_tts.Communicate(
text="要合成的文本",
voice="zh-CN-XiaoxiaoNeural",
rate="+0%", # 语速,默认 +0%
volume="+0%", # 音量,默认 +0%
pitch="+0Hz", # 音调,默认 +0Hz
)
5.2 不同场景的参数推荐
| 场景 | rate | pitch | 效果 |
|---|---|---|---|
| 正常播报 | +0% | +0Hz | 标准语速和音调 |
| 快速播报(新闻) | +15%~+25% | +0Hz | 节奏紧凑 |
| 温柔叙述(有声书) | -5%~-10% | -3Hz~+3Hz | 舒缓自然 |
| 活泼角色 | +10%~+20% | +5Hz~+15Hz | 年轻有活力 |
| 低沉角色 | -5%~-10% | -10Hz~-20Hz | 成熟稳重 |
| 紧张/惊恐 | +20%~+30% | +8Hz~+15Hz | 急促高亢 |
5.3 使用 SSML 精细控制
Edge-TTS 支持 SSML(语音合成标记语言),可以通过文本内容传入 SSML 来实现更精细的控制:
python
import asyncio
import edge_tts
async def speak_with_ssml():
ssml = """<speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis" xml:lang="zh-CN">
<voice name="zh-CN-YunxiNeural">
<prosody rate="+15%" pitch="+5Hz">
这是通过SSML控制的语音合成示例。
</prosody>
</voice>
</speak>"""
communicate = edge_tts.Communicate(ssml, "zh-CN-YunxiNeural")
await communicate.save("ssml_output.mp3")
asyncio.run(speak_with_ssml())
当文本以 <speak> 开头时,Edge-TTS 会自动识别为 SSML 格式。
6. 多角色批量合成实战
在实际项目中,经常需要为对话文本生成多角色语音。以下是一个完整的批量合成示例:
python
import asyncio
import edge_tts
# 角色-语音映射表
VOICE_MAP = {
"旁白": "zh-CN-YunjianNeural",
"女主": "zh-CN-XiaoxiaoNeural",
"男主": "zh-CN-YunxiNeural",
"老人": "zh-CN-YunjianNeural",
}
# 角色-参数映射(语速、音调)
PARAM_MAP = {
"旁白": {"rate": "-5%", "pitch": "+0Hz"},
"女主": {"rate": "+5%", "pitch": "+5Hz"},
"男主": {"rate": "+0%", "pitch": "-3Hz"},
"老人": {"rate": "-15%", "pitch": "-10Hz"},
}
async def synthesize_dialogue(dialogue_list: list, output_dir: str = "./output"):
"""
dialogue_list: [(角色, 文本), ...]
"""
import os
os.makedirs(output_dir, exist_ok=True)
for i, (speaker, text) in enumerate(dialogue_list):
voice = VOICE_MAP.get(speaker, "zh-CN-XiaoxiaoNeural")
params = PARAM_MAP.get(speaker, {})
rate = params.get("rate", "+0%")
pitch = params.get("pitch", "+0Hz")
communicate = edge_tts.Communicate(
text, voice, rate=rate, pitch=pitch
)
filename = f"{output_dir}/{i+1:03d}_{speaker}.mp3"
await communicate.save(filename)
print(f"已生成: {filename}")
async def main():
dialogue = [
("旁白", "在一个风和日丽的下午,故事开始了。"),
("女主", "你今天怎么来得这么晚?"),
("男主", "路上堵车了,抱歉让你久等。"),
("老人", "年轻人啊,时间不等人,要珍惜每一天。"),
]
await synthesize_dialogue(dialogue)
asyncio.run(main())
如果需要将多段音频合并为一段完整的语音,可以使用 pydub 进行拼接:
python
from pydub import AudioSegment
import os
def merge_audio_files(file_list, output_path):
combined = AudioSegment.empty()
for f in file_list:
audio = AudioSegment.from_mp3(f)
combined += audio
combined += AudioSegment.silent(duration=300) # 每段后加300ms停顿
combined.export(output_path, format="mp3")
7. 在 Web 框架中集成
7.1 FastAPI 示例
python
from fastapi import FastAPI, HTTPException
from fastapi.responses import FileResponse
import edge_tts
import tempfile
import os
app = FastAPI()
@app.post("/tts")
async def text_to_speech(text: str, voice: str = "zh-CN-XiaoxiaoNeural"):
try:
communicate = edge_tts.Communicate(text, voice)
with tempfile.NamedTemporaryFile(
delete=False, suffix=".mp3"
) as tmp:
tmp_path = tmp.name
await communicate.save(tmp_path)
return FileResponse(
tmp_path,
media_type="audio/mpeg",
filename="speech.mp3"
)
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
7.2 带重试机制的健壮版本
网络请求可能偶发失败,建议加入重试逻辑:
python
async def synthesize_with_retry(
text, voice, output_path, max_retries=5, delay=1
):
for attempt in range(max_retries):
try:
communicate = edge_tts.Communicate(text, voice)
await communicate.save(output_path)
return True
except Exception as e:
print(f"第 {attempt+1} 次尝试失败: {e}")
if attempt < max_retries - 1:
await asyncio.sleep(delay * (attempt + 1))
return False
8. 常见问题与注意事项
Q:报错 RuntimeError: asyncio.run() cannot be called from a running event loop?
说明你已经在异步环境中调用了 asyncio.run()。在异步框架中直接 await communicate.save() 即可,不要嵌套 asyncio.run()。
Q:生成的语音不完整或中断?
通常是网络波动导致。建议加入重试机制,或使用流式方式逐块获取后自行拼接。
Q:命令行参数报错 --rate -50%?
命令行中使用带负号的参数值时,需要写成 --rate=-50% 的形式,等号不能省略。
Q:如何选择合适的语音角色?
先运行 edge-tts --list-voices 查看全部可用语音,中文语音可以按 zh-CN 过滤。建议通过试听小段文本来确定最合适的音色。
Q:能否完全离线使用?
不能。Edge-TTS 调用的是微软在线服务,需要网络连接。如需完全离线,可以考虑 Coqui TTS 或 pyttsx3 等方案。