TTS语音合成:让数字人开口说话
这篇文章帮你理解语音合成
数字人除了要”长得好看”,还得”会说话”。TTS(Text-to-Speech)就是让电脑把文字变成语音的技术。读完这篇,你会了解各种TTS方案的优缺点,知道怎么给自己的数字人配上好听的声音,以及怎么做声音克隆。
先搞清楚:什么是TTS?
TTS(Text-to-Speech)就是把文字转成语音的技术。比如你输入一段文字”今天天气很好”,TTS系统就会生成对应的语音音频文件。
TTS的应用场景
| 场景 | 说明 |
|---|---|
| 数字人配音 | 让数字人说话 |
| 语音导航 | GPS导航的语音播报 |
| 智能客服 | AI接听电话 |
| 有声书 | 自动生成书籍朗读 |
| 无障碍辅助 | 屏幕阅读器 |
TTS技术的发展历程
| 时代 | 技术 | 效果 | 代表 |
|---|---|---|---|
| 早期 | 波形拼接 | 机械感强 | PSOLA |
| 中期 | 参数合成 | 稍有改善 | HTS |
| 深度学习 | 神经网络 | 自然流畅 | WaveNet |
| 当前 | 端到端 | 非常自然 | GPT-SoVITS, Fish-Speech |
1. 主流TTS方案对比
1.1 在线服务 vs 开源方案
| 类型 | 优点 | 缺点 | 适合场景 |
|---|---|---|---|
| 在线服务 | 效果好、使用简单 | 需要付费、数据上传 | 快速上线、生产环境 |
| 开源方案 | 免费、可本地部署 | 需要配置、效果参差 | 学习研究、自建系统 |
1.2 在线TTS服务推荐
| 服务商 | 中文效果 | 价格 | 推荐指数 |
|---|---|---|---|
| Azure TTS | ⭐⭐⭐⭐ | 中等 | ⭐⭐⭐⭐⭐ |
| 火山引擎 | ⭐⭐⭐⭐⭐ | 便宜 | ⭐⭐⭐⭐⭐ |
| 阿里云 | ⭐⭐⭐⭐⭐ | 便宜 | ⭐⭐⭐⭐ |
| 腾讯云 | ⭐⭐⭐⭐ | 便宜 | ⭐⭐⭐⭐ |
1.3 开源TTS方案推荐
| 项目 | 中文支持 | 声音克隆 | 推荐指数 |
|---|---|---|---|
| Fish-Speech | ⭐⭐⭐⭐⭐ | ✅ | ⭐⭐⭐⭐⭐ |
| Coqui TTS | ⭐⭐⭐⭐ | ✅ | ⭐⭐⭐⭐ |
| VITS | ⭐⭐⭐⭐ | ✅ | ⭐⭐⭐⭐ |
| GPT-SoVITS | ⭐⭐⭐⭐⭐ | ✅ | ⭐⭐⭐⭐⭐ |
2. Fish-Speech:最好的中文开源TTS
2.1 Fish-Speech是什么?
Fish-Speech是国产开源的TTS项目,专为中文优化,效果非常好,而且完全免费可商用。
主要特点:
- 对中文支持极佳
- 支持声音克隆(只需10秒音频)
- 支持多说话人
- 完全开源免费
- 训练推理效率高
2.2 安装Fish-Speech
# 克隆仓库
git clone https://github.com/fishaudio/fish-speech.git
cd fish-speech
# 方法1:使用Docker(推荐)
docker compose up -d
# 方法2:手动安装
# 创建虚拟环境
python -m venv fish_env
source fish_env/bin/activate # Windows: fish_env\Scripts\activate
# 安装依赖
pip install -r requirements.txt
# 下载模型
# 模型会自动下载,首次使用时会提示2.3 使用Fish-Speech进行语音合成
方式一:命令行使用
# 基础使用
python tools/tts_service.py --text "你好,这是一段测试语音"
# 指定声音
python tools/tts_service.py \
--text "你好,欢迎使用Fish-Speech" \
--speaker "default"
# 指定语速和音调
python tools/tts_service.py \
--text "今天天气真不错" \
--speed 1.2 \
--pitch 1.0
# 输出到文件
python tools/tts_service.py \
--text "你好,世界" \
--output "output.wav"方式二:网页界面
# 启动网页界面
python tools/webui.py
# 然后打开浏览器访问 http://127.0.0.1:78602.4 Fish-Speech Python API
"""
Fish-Speech Python API使用示例
"""
import requests
import base64
import json
class FishSpeechTTS:
"""Fish-Speech TTS封装"""
def __init__(self, api_url="http://localhost:8080"):
self.api_url = api_url
def synthesize(self, text, speaker="default", speed=1.0):
"""
文字转语音
Args:
text: 要转换的文字
speaker: 说话人ID
speed: 语速 (0.5-2.0)
Returns:
bytes: WAV格式音频数据
"""
response = requests.post(
f"{self.api_url}/v1/tts",
json={
"text": text,
"speaker": speaker,
"speed": speed
}
)
if response.status_code == 200:
return response.content
else:
raise Exception(f"TTS failed: {response.text}")
def synthesize_to_file(self, text, output_path, **kwargs):
"""
文字转语音并保存文件
Args:
text: 要转换的文字
output_path: 输出文件路径
**kwargs: 其他参数
"""
audio_data = self.synthesize(text, **kwargs)
with open(output_path, 'wb') as f:
f.write(audio_data)
print(f"音频已保存到: {output_path}")
# 使用示例
if __name__ == "__main__":
tts = FishSpeechTTS()
# 生成语音
audio = tts.synthesize("你好,这是一个测试。")
# 保存到文件
tts.synthesize_to_file(
"今天天气真不错,适合出门散步。",
"output.wav"
)2.5 声音克隆教程
Fish-Speech支持声音克隆,只需要10-30秒的音频样本。
准备工作:
- 准备一段干净的语音(最好是无背景音乐的人声)
- 时长10秒-30秒最佳
- 音频格式最好是WAV或MP3
训练自己的声音模型:
# 1. 准备训练数据
# 将音频文件放到 data/your_voice/ 目录下
# 命名格式: audio_001.wav, audio_002.wav, ...
# 2. 准备训练文本
# 创建一个文本文件 audio_transcripts.txt:
# audio_001.wav|今天天气很好
# audio_002.wav|我们一起去公园吧
# 3. 开始训练
python tools/train.py \
--data_dir ./data/your_voice \
--output_dir ./checkpoints/your_voice \
--max_epochs 100 \
--batch_size 16
# 4. 训练完成后,使用你的声音
python tools/tts_service.py \
--text "你好,这是我的声音" \
--speaker your_voice3. GPT-SoVITS:新一代声音克隆
3.1 GPT-SoVITS是什么?
GPT-SoVITS是2024年新出的声音克隆技术,只需要1分钟的训练数据就能达到很好的效果。
优势:
- 训练数据需求少(1分钟即可)
- 克隆效果好
- 支持多语言
- 推理速度快
3.2 安装GPT-SoVITS
# 克隆仓库
git clone https://github.com/RVC-Boss/GPT-SoVITS.git
cd GPT-SoVITS
# 安装依赖
pip install -r requirements.txt
# 下载预训练模型
# 需要从HuggingFace下载:
# - GPT-SoVITS-base.pth
# - v2权重3.3 GPT-SoVITS使用教程
"""
GPT-SoVITS使用示例
"""
import torch
from GPT_SoVITS.download import get_hf_download
from GPT_SoVITS.inference import GPT_SoVITS
# 加载模型
gpt_sovits = GPT_SoVITS.from_pretrained(
"GPT_SoVITS/SoVITS.pth",
"GPT_SoVITS/GPT.pth"
)
def clone_and_speak(audio_sample, text):
"""
克隆声音并合成语音
Args:
audio_sample: 参考音频路径
text: 要说的话
"""
# 克隆声音特征
voice_embedding = gpt_sovits.extract_voice(audio_sample)
# 生成语音
audio = gpt_sovits.generate(
text=text,
ref_audio=voice_embedding,
language="auto" # 自动检测语言
)
return audio
# 使用示例
if __name__ == "__main__":
# 用3秒音频克隆声音
audio = clone_and_speak(
audio_sample="my_voice.wav",
text="你好,这是用我自己的声音合成的。"
)
# 保存
import soundfile as sf
sf.write("cloned_voice.wav", audio, 24000)4. Coqui TTS:老牌开源TTS
4.1 Coqui TTS介绍
Coqui TTS是发展比较成熟的开源TTS项目,支持多种语言和声音克隆。
4.2 安装Coqui TTS
# 安装
pip install TTS
# 验证安装
tts --version4.3 Coqui TTS基础使用
"""
Coqui TTS使用示例
"""
from TTS.api import TTS
# 初始化(使用默认模型)
tts = TTS(model_name="tts_models/zh-CN/baker/tacotron2-DDC-GST")
# 基础合成
tts.tts_to_file(
text="你好,这是一个测试。",
file_path="output.wav"
)
# 指定说话人
tts.tts_to_file(
text="你好,这是一个测试。",
speaker="female_1", # 指定说话人
file_path="output2.wav"
)
# 使用参考音频(声音克隆)
tts.tts_with_vc_to_file(
speaker_wav="reference_voice.wav",
text="用这个声音说话。",
file_path="output3.wav"
)4.4 Coqui TTS进阶:训练自定义声音
"""
Coqui TTS训练自定义声音
"""
from TTS.trainer import Trainer
from TTS.config import load_config
from TTS.tts.datasets import load_tts_samples
# 1. 准备数据
# 创建dataset_config.json:
# {
# "datasets": [
# {
# "name": "my_voice",
# "path": "./data/my_voice",
# "meta_file_train": "metadata.csv",
# "audio_config": {
# "sample_rate": 22050
# }
# }
# ]
# }
# 2. 创建metadata.csv:
# wav_file,duration,text
# audio_001.wav,3.5,你好世界
# audio_002.wav,4.2,今天天气很好
# 3. 开始训练
# 加载配置
config = load_config("train_config.json")
# 创建数据集
train_samples, eval_samples = load_tts_samples(
config.datasets,
eval_split=True
)
# 训练
trainer = Trainer(
config=config,
output_path="./output",
train_samples=train_samples,
eval_samples=eval_samples
)
trainer.fit()5. Azure TTS:企业级云服务
5.1 Azure TTS优势
Azure TTS是微软的云端TTS服务,优点是:
- 效果非常好,接近真人
- 神经网络语音非常自然
- 支持情感控制
- 全球70+语言
- 稳定可靠
5.2 Azure TTS安装和使用
# 安装SDK
pip install azure-cognitiveservices-speech"""
Azure TTS使用示例
"""
import azure.cognitiveservices.speech as speechsdk
import os
class AzureTTS:
"""Azure TTS封装"""
def __init__(self, subscription_key, region="eastus"):
self.speech_config = speechsdk.SpeechConfig(
subscription=subscription_key,
region=region
)
def synthesize(self, text, voice="zh-CN-XiaoxiaoNeural",
output_file="output.wav"):
"""
文字转语音
Args:
text: 要转换的文字
voice: 语音名称
output_file: 输出文件路径
"""
self.speech_config.speech_synthesis_voice_name = voice
audio_config = speechsdk.audio.AudioOutputConfig(
filename=output_file
)
synthesizer = speechsdk.SpeechSynthesizer(
speech_config=self.speech_config,
audio_config=audio_config
)
result = synthesizer.speak_text_async(text).get()
if result.reason == speechsdk.ResultReason.SynthesizingAudioCompleted:
print(f"音频已保存到: {output_file}")
else:
print(f"合成失败: {result.error_details}")
def synthesize_with_emotion(self, text, emotion,
output_file="output.wav"):
"""
带情感的语音合成(使用SSML)
Args:
text: 要转换的文字
emotion: 情感类型 (cheerful/sad/angry/etc.)
output_file: 输出文件路径
"""
# 构建SSML
ssml = f"""
<speak version='1.0' xmlns='https://www.w3.org/2001/10/synthesis'
xml:lang='zh-CN'>
<voice name='zh-CN-XiaoxiaoNeural'>
<mstts:express-as style='{emotion}'>
{text}
</mstts:express-as>
</voice>
</speak>
"""
audio_config = speechsdk.audio.AudioOutputConfig(
filename=output_file
)
synthesizer = speechsdk.SpeechSynthesizer(
speech_config=self.speech_config,
audio_config=audio_config
)
result = synthesizer.speak_ssml_async(ssml).get()
return result.reason == speechsdk.ResultReason.SynthesizingAudioCompleted
# 使用示例
if __name__ == "__main__":
# 初始化(需要设置环境变量或直接传入key)
subscription_key = os.getenv("AZURE_SPEECH_KEY")
region = "eastus"
tts = AzureTTS(subscription_key, region)
# 基础合成
tts.synthesize(
text="你好,欢迎使用Azure语音合成服务。",
voice="zh-CN-XiaoxiaoNeural",
output_file="azure_basic.wav"
)
# 带情感合成
tts.synthesize_with_emotion(
text="太棒了!我们成功了!",
emotion="cheerful",
output_file="azure_happy.wav"
)
tts.synthesize_with_emotion(
text="我感到有点难过。",
emotion="sad",
output_file="azure_sad.wav"
)5.3 Azure TTS情感控制
Azure支持多种情感样式:
| 情感 | 使用场景 | 效果 |
|---|---|---|
| cheerful | 开心、兴奋 | 语气上扬 |
| sad | 悲伤、沮丧 | 语气下沉 |
| angry | 生气、愤怒 | 语气强硬 |
| afraid | 害怕、紧张 | 语速加快 |
| excited | 兴奋、激动 | 语气夸张 |
| friendly | 友好、亲切 | 语气温和 |
| hopeful | 希望、期待 | 语气上扬 |
| terrified | 恐惧、害怕 | 语速极快 |
6. XTTS:高质量声音克隆
6.1 XTTS是什么?
XTTS是Coqui推出的新一代声音克隆模型,可以只用短音频就能克隆声音。
6.2 XTTS使用
"""
XTTS声音克隆示例
"""
from TTS.api import TTS
# 初始化
tts = TTS(model_name="tts_models/multilingual/multi-dataset/xtts")
# 使用参考音频克隆声音
tts.tts_to_file(
text="这是用你的声音合成的语音。",
speaker_wav="my_voice.wav", # 参考音频
language="zh",
file_path="cloned_output.wav"
)
# 多语言支持
tts.tts_to_file(
text="Hello, this is a test.",
speaker_wav="english_voice.wav",
language="en",
file_path="english_output.wav"
)7. 火山引擎TTS:国产好选择
7.1 火山引擎TTS
火山引擎是字节跳动旗下的AI服务平台,TTS效果很好,价格便宜。
"""
火山引擎TTS示例
"""
import requests
import hashlib
import time
import base64
class VolcanoTTS:
"""火山引擎TTS封装"""
def __init__(self, app_id, access_token, cluster="volcengine_streaming_common"):
self.app_id = app_id
self.access_token = access_token
self.cluster = cluster
self.base_url = "https://openspeech.bytedance.com/api/v1/tts"
def synthesize(self, text, voice="zh_female_qingxin",
speed=1.0, pitch=1.0, volume=1.0):
"""
文字转语音
Args:
text: 要转换的文字
voice: 语音名称
speed: 语速 (0.5-2.0)
pitch: 音调 (0.5-2.0)
volume: 音量 (0.5-2.0)
Returns:
bytes: WAV格式音频数据
"""
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer;{self.access_token}",
"Aid": self.app_id
}
payload = {
"app": {
"appid": self.app_id,
"token": self.access_token,
"cluster": self.cluster
},
"user": {
"uid": "12345"
},
"audio": {
"voice": voice,
"encoding": "wav",
"speed_ratio": speed,
"volume_ratio": volume,
"pitch_ratio": pitch,
"sample_rate": 16000
},
"request": {
"reqid": str(int(time.time() * 1000)),
"text": text,
"text_type": "plain",
"operation": "submit"
}
}
response = requests.post(
self.base_url,
json=payload,
headers=headers
)
if response.status_code == 200:
return response.content
else:
raise Exception(f"TTS failed: {response.text}")
def synthesize_to_file(self, text, output_path, **kwargs):
"""保存到文件"""
audio_data = self.synthesize(text, **kwargs)
with open(output_path, 'wb') as f:
f.write(audio_data)
print(f"音频已保存到: {output_path}")
# 使用示例
if __name__ == "__main__":
tts = VolcanoTTS(
app_id="your_app_id",
access_token="your_access_token"
)
tts.synthesize_to_file(
text="你好,欢迎使用火山引擎语音合成服务。",
voice="zh_female_qingxin",
output_path="volcano_output.wav"
)8. 实时TTS流式输出
8.1 什么是流式TTS?
流式TTS就是”边生成边播放”,不需要等整段语音生成完毕才能听。这对数字人实时对话非常重要。
8.2 流式TTS实现
"""
流式TTS实现
"""
import asyncio
import websockets
import base64
import numpy as np
class StreamingTTS:
"""流式TTS客户端"""
def __init__(self, tts_service):
self.tts = tts_service
self.sample_rate = 24000
async def stream_synthesize(self, text, websocket_url):
"""
流式合成并发送到WebSocket
Args:
text: 要转换的文字
websocket_url: WebSocket服务器地址
"""
async with websockets.connect(websocket_url) as ws:
# 发送文本
await ws.send(json.dumps({
"type": "text",
"text": text
}))
# 接收流式音频
buffer = bytearray()
while True:
message = await ws.recv()
if isinstance(message, bytes):
# 音频数据
buffer.extend(message)
# 可以在这里播放音频
# play_audio_chunk(message)
else:
# JSON消息
data = json.loads(message)
if data.get("type") == "done":
break
return bytes(buffer)
async def synthesize_and_play(self, text):
"""
合成并直接播放
"""
# 这里需要结合音频播放库
# 比如使用pyaudio或sounddevice
pass
# WebSocket服务端示例
"""
from fastapi import WebSocket
from fastapi import FastAPI
import asyncio
app = FastAPI()
@app.websocket("/ws/tts")
async def tts_websocket(ws: WebSocket):
await ws.accept()
try:
while True:
# 接收文本
data = await ws.receive_json()
text = data.get("text", "")
# 流式合成并发送
tts = TTSEngine()
async for chunk in tts.stream_synthesize(text):
await ws.send_bytes(chunk)
# 发送完成信号
await ws.send_json({"type": "done"})
except Exception as e:
print(f"Error: {e}")
finally:
await ws.close()
"""9. 情感语音合成
9.1 什么是情感TTS?
情感TTS可以合成带有不同情感的语音,比如开心的、悲伤的、愤怒的等。
9.2 实现情感控制
"""
情感TTS控制示例
"""
from enum import Enum
from typing import Dict
class Emotion(Enum):
"""情感枚举"""
NEUTRAL = "neutral"
HAPPY = "happy"
SAD = "sad"
ANGRY = "angry"
EXCITED = "excited"
SCARED = "scared"
class EmotionalTTS:
"""情感TTS"""
def __init__(self, tts_engine):
self.tts = tts_engine
# 情感对应的语音参数调整
self.emotion_params: Dict[Emotion, Dict] = {
Emotion.NEUTRAL: {"pitch": 1.0, "speed": 1.0, "volume": 1.0},
Emotion.HAPPY: {"pitch": 1.1, "speed": 1.1, "volume": 1.0},
Emotion.SAD: {"pitch": 0.9, "speed": 0.9, "volume": 0.9},
Emotion.ANGRY: {"pitch": 1.0, "speed": 1.2, "volume": 1.2},
Emotion.EXCITED: {"pitch": 1.2, "speed": 1.15, "volume": 1.1},
Emotion.SCARED: {"pitch": 1.15, "speed": 1.1, "volume": 1.0}
}
def synthesize_emotional(self, text, emotion: Emotion):
"""情感合成"""
params = self.emotion_params.get(emotion, self.emotion_params[Emotion.NEUTRAL])
return self.tts.synthesize(
text=text,
pitch=params["pitch"],
speed=params["speed"],
volume=params["volume"]
)
# 使用示例
if __name__ == "__main__":
tts = TTSEngine() # 初始化你的TTS引擎
emotional_tts = EmotionalTTS(tts)
# 根据对话内容选择情感
dialogues = [
("我们成功了!太棒了!", Emotion.HAPPY),
("对不起,让你失望了。", Emotion.SAD),
("你说什么?!", Emotion.ANGRY),
("哇,太不可思议了!", Emotion.EXCITED),
]
for text, emotion in dialogues:
audio = emotional_tts.synthesize_emotional(text, emotion)
# 保存或播放
print(f"合成情感语音: {emotion.value}")10. 常见问题与解决方案
Q1: 合成的语音听起来机械?
原因:模型质量问题
解决:
- 使用更高级的模型(如GPT-SoVITS、Fish-Speech)
- 调整语速参数,不要太快
- 使用带情感控制的语音
Q2: 中文发音不准确?
原因:发音人问题
解决:
- 选择中文优化的模型(如Fish-Speech)
- 使用SSML标记特殊发音
- 检查输入文字是否有错别字
Q3: 声音克隆效果差?
原因:训练数据问题
解决:
- 使用更清晰的音频
- 增加训练数据量
- 确保音频无背景噪音
- 增加训练轮数
Q4: 实时合成延迟太高?
解决:
- 使用流式输出
- 预加载模型到内存
- 使用GPU加速
- 选择更轻量的模型
11. 工具链推荐
11.1 快速上线方案
| 场景 | 推荐 | 说明 |
|---|---|---|
| 快速体验 | Azure TTS | 效果好,5分钟接入 |
| 预算有限 | 火山引擎 | 便宜,效果不错 |
| 中文为主 | Fish-Speech | 免费,中文最好 |
11.2 声音克隆方案
| 场景 | 推荐 | 说明 |
|---|---|---|
| 快速克隆 | GPT-SoVITS | 1分钟数据,效果好 |
| 高质量克隆 | Fish-Speech | 需要更多数据,效果顶级 |
| 精确控制 | Coqui + 训练 | 工作量大,但最可控 |
11.3 企业级方案
| 场景 | 推荐 | 说明 |
|---|---|---|
| 稳定可靠 | Azure + 自建 | 核心用Azure,边缘自建 |
| 大规模 | 自建GPT-SoVITS集群 | 成本最低,弹性扩展 |
相关文档
更新日志
| 日期 | 版本 | 修改内容 |
|---|---|---|
| 2026-04-18 | v1.0 | 初版完成 |
| 2026-04-24 | v1.1 | 深度改写,增加开源方案实操 |
版权声明
本文档为归愚知识库原创内容,采用CC BY-NC-SA 4.0协议授权。