搭建支持情感控制的二次封装 TTS 服务
如何使用 ChatTTS 搭建支持情感控制的可二次封装 TTS 服务。内容涵盖环境准备、核心封装类实现、测试脚本编写以及基于 Flask 的 API 部署。通过调整温度参数和种子值控制情感表达,并提供批量合成与音色嵌入接口,适合集成到实际项目中。

如何使用 ChatTTS 搭建支持情感控制的可二次封装 TTS 服务。内容涵盖环境准备、核心封装类实现、测试脚本编写以及基于 Flask 的 API 部署。通过调整温度参数和种子值控制情感表达,并提供批量合成与音色嵌入接口,适合集成到实际项目中。

打开终端,执行以下命令:
# 1. 创建项目目录
mkdir MyEmotionalTTS && cd MyEmotionalTTS
# 2. 创建 Python 虚拟环境(推荐)
python -m venv venv
# 在 Linux/Mac 上激活:source venv/bin/activate
# 在 Windows 上激活:venv\Scripts\activate
# 3. 安装 PyTorch (根据你的 CUDA 版本选择,以 CUDA 12.1 为例)
pip install torch torchaudio --index-url https://download.pytorch.org/whl/cu121
# 4. 安装 ChatTTS 及其他依赖
pip install ChatTTS transformers soundfile ipython
创建一个名为 init_model.py 的脚本,写入以下代码:
import ChatTTS
import torch
import warnings
warnings.filterwarnings("ignore")
# 初始化 ChatTTS
chat = ChatTTS.Chat()
# 加载模型(自动下载权重,约 2GB)
chat.load_models(compile=False)
# `compile=False` 可避免特定环境下的错误
# 查看可用模型参数(可选)
print("模型加载成功!")
print(f"设备:{chat.device}")
# 将模型设为推理模式(重要)
chat.eval()
# 保存模型对象以供后续使用(示例,实际我们会在封装类中管理)
# import pickle
# with open('chat_model.pkl', 'wb') as f:
# pickle.dump(chat, f)
运行它来下载和验证模型:
python init_model.py
创建一个核心封装类 EmotionalTTS.py,这是二次封装的精髓。
import ChatTTS
import torch
import numpy as np
import soundfile as sf
from typing import List, Optional, Dict
import warnings
warnings.filterwarnings("ignore")
class EmotionalTTS:
"""
情感 TTS 二次封装类。
提供易于使用的接口,用于控制情感、音色和语速。
"""
def __init__(self, model_path: str = None, device: str = None):
"""
初始化 TTS 引擎。
Args:
model_path: 预加载的模型路径(暂无用处,ChatTTS 自动下载)。
device: 指定设备,如 'cuda', 'cpu'。为 None 则自动选择。
"""
self.chat = ChatTTS.Chat()
# 加载模型
if device:
self.chat.load_models(compile=False, device=device)
else:
self.chat.load_models(compile=False)
# 设置为评估模式
self.chat.eval()
# 情感 - 参数映射字典 (你可以根据效果扩展这个字典)
self.emotion_params_map = {
'happy': {'temperature': 0.7, 'spk_emb': None}, # 开心,语速稍快
'sad': {'temperature': 0.3, 'spk_emb': None}, # 悲伤,语速慢
'angry': {'temperature': 0.9, 'spk_emb': None}, # 生气,音调高
'neutral': {'temperature': 0.5, 'spk_emb': None}, # 中性
'friendly': {'temperature': 0.6, 'spk_emb': None} # 友好
}
print(f"[初始化完成] 模型运行在:{self.chat.device}")
def synthesize(self, text: str, emotion: str = 'neutral', speaker_embedding: Optional[np.ndarray] = None, speed: float = 1.0, sample_rate: int = 24000, save_path: Optional[str] = None) -> tuple:
"""
核心合成函数。
Args:
text: 要合成的文本。
emotion: 情感标签,从 `emotion_params_map` 中选择。
speaker_embedding: 可选,特定说话人音色嵌入。
speed: 语速因子 (>1 加速,<1 减速)。
sample_rate: 输出音频采样率。
save_path: 如需直接保存,提供.wav 文件路径。
Returns:
audio_data: 合成的音频波形数据 (numpy 数组)。
sr: 采样率
"""
# 1. 文本预处理 (ChatTTS 要求特殊处理)
texts = [text]
# 2. 情感参数注入 (通过 params_infer_code 控制)
params = self.emotion_params_map.get(emotion, self.emotion_params_map['neutral'])
# 3. 生成随机种子以实现不同的情感/音色 (可控的随机性)
rand_spk = np.random.randint(0, 100000) if speaker_embedding is None else None
# 4. 模型推理
with torch.no_grad():
wavs, _ = self.chat.infer(
texts,
params_refine_text={'prompt': f'[speaker_emo={emotion}]'}, # 提示词控制情感
params_infer_code={
'spk_emb': speaker_embedding,
'seed': rand_spk,
'temperature': params['temperature'],
},
do_text_normalization=True,
return_duration=True
)
audio_data = wavs.squeeze() # 从 [1, samples] 变为 [samples]
# 5. 语速调整 (简单的重采样,生产环境可用更优算法)
if speed != 1.0:
from scipy import signal
new_length = int(len(audio_data) / speed)
audio_data = signal.resample(audio_data, new_length)
# 6. 保存文件(如果提供了路径)
if save_path:
if not save_path.endswith('.wav'):
save_path += '.wav'
sf.write(save_path, audio_data, samplerate=sample_rate)
print(f"[音频已保存] -> {save_path}")
return audio_data, sample_rate
def batch_synthesize(self, texts: List[str], emotions: Optional[List[str]] = None, save_dir: str = "./output_batch") -> List[str]:
"""
批量合成文本。
Args:
texts: 文本列表。
emotions: 对应的情感列表,为 None 则全部使用中性。
save_dir: 输出目录。
Returns:
file_paths: 保存的音频文件路径列表。
"""
import os
os.makedirs(save_dir, exist_ok=True)
if emotions is None:
emotions = ['neutral'] * len(texts)
file_paths = []
for i, (text, emotion) in enumerate(zip(texts, emotions)):
print(f"处理中 ({i+1}/{len(texts)}): {text[:30]}... [{emotion}]")
save_path = os.path.join(save_dir, f"batch_{i:03d}_{emotion}.wav")
self.synthesize(text, emotion=emotion, save_path=save_path)
file_paths.append(save_path)
return file_paths
def get_available_emotions(self) -> List[str]:
"""返回预定义的情感标签列表。"""
return list(self.emotion_params_map.keys())
# 示例:如何创建音色嵌入(高级功能,用于克隆特定音色)
def create_speaker_embedding(self, reference_audio_path: str) -> np.ndarray:
"""
从参考音频中提取说话人嵌入。
Args:
reference_audio_path: 参考音频文件路径 (.wav)。
Returns:
spk_emb: 说话人嵌入向量。
"""
# 注意:ChatTTS 官方尚未直接提供此接口,此处为示意。
# 实际可参考其 `infer` 方法中 `spk_emb` 的用法。
# 这里返回一个随机向量作为占位符。
print(f"[提示] 音色克隆功能需参考官方最新实现。")
return np.random.randn(1, 1024).astype(np.float32) # 占位符
创建一个测试脚本 test_tts.py 来使用我们的封装类。
from EmotionalTTS import EmotionalTTS
import soundfile as sf
import simpleaudio as sa # 用于直接播放,安装: pip install simpleaudio
def main():
# 1. 初始化引擎
print("="*50)
print("初始化情感 TTS 引擎...")
tts_engine = EmotionalTTS(device='cuda') # 如果你有 GPU
# tts_engine = EmotionalTTS(device='cpu') # 使用 CPU
# 2. 查看支持的情感
print("支持的情感:", tts_engine.get_available_emotions())
print("="*50)
# 3. 单句合成示例
test_text = "你好,世界!这是一个测试,看看情感语音合成效果怎么样。"
# 用不同的情感合成同一句话
for emo in ['neutral', 'happy', 'sad', 'angry']:
print(f"\n>>> 正在用「{emo}」情感合成...")
audio_data, sr = tts_engine.synthesize(
text=test_text,
emotion=emo,
speed=1.0 if emo != 'sad' else 0.9, # 悲伤时语速放慢
save_path=f"./output/demo_{emo}.wav" # 保存文件
)
# 尝试播放(如果环境支持)
try:
play_obj = sa.play_buffer(audio_data, 1, , sr)
play_obj.wait_done()
:
()
( + *)
()
batch_texts = [
,
,
,
]
batch_emotions = [, , , ]
saved_files = tts_engine.batch_synthesize(
texts=batch_texts,
emotions=batch_emotions,
save_dir=
)
()
( + *)
()
tts_engine.emotion_params_map[] = {: , : }
audio_custom, _ = tts_engine.synthesize(
,
emotion=,
save_path=
)
()
__name__ == :
os
os.makedirs(, exist_ok=)
os.makedirs(, exist_ok=)
main()
()
将你的封装部署为 Web 服务,以便其他程序调用。创建 api_service.py:
from flask import Flask, request, jsonify, send_file
from EmotionalTTS import EmotionalTTS
import io
import soundfile as sf
import numpy as np
import uuid
import os
app = Flask(__name__)
tts_engine = None
def init_engine():
global tts_engine
print("正在加载 TTS 模型...")
tts_engine = EmotionalTTS(device='cpu') # API 服务通常用 CPU
print("模型加载完毕,API 服务就绪。")
init_engine()
@app.route('/synthesize', methods=['POST'])
def synthesize():
"""API 端点:文本转语音"""
data = request.json # 解析请求参数
text = data.get('text', '')
emotion = data.get('emotion', 'neutral')
speed = float(data.get('speed', 1.0))
if not text:
return jsonify({'error': '文本内容不能为空'}), 400
# 调用引擎合成
try:
audio_data, sr = tts_engine.synthesize(
text=text,
emotion=emotion,
speed=speed
)
# 将音频数据转为字节流返回
audio_bytes = io.BytesIO()
sf.write(audio_bytes, audio_data, samplerate=sr, format=)
audio_bytes.seek()
send_file(
audio_bytes,
mimetype=,
as_attachment=,
download_name=
)
Exception e:
jsonify({: }),
():
jsonify({: tts_engine.get_available_emotions()})
__name__ == :
os.makedirs(, exist_ok=)
app.run(host=, port=, debug=, use_reloader=)
启动 API 服务:
python api_service.py
使用 CURL 测试 API:
curl -X POST http://127.0.0.1:5000/synthesize \
-H "Content-Type: application/json" \
-d '{"text": "你好,欢迎使用情感 TTS API 服务", "emotion": "friendly", "speed": 1.1}' \
--output output_api.wav
emotion_params_map 中的 temperature 值(0.1-1.5),值越高声音越有表现力(可能不稳定),值越低越稳定。[uv_break](短停顿)、[laugh](笑声),能让效果更生动。(文本,音频,情感标签) 配对数据,使用 ChatTTS 训练脚本进行微调。MyEmotionalTTS/
├── EmotionalTTS.py # 核心封装类
├── init_model.py # 初始化脚本
├── test_tts.py # 测试脚本
├── api_service.py # Flask API 服务
├── requirements.txt # 依赖列表
├── output/ # 生成音频目录
└── README.md # 项目说明
torch.compile 或模型量化(如 torch.quantization)进行优化。该方案提供了从安装、封装、测试到部署的完整代码链路。可以直接复制代码运行,并根据注释进行修改和扩展。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online
基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online
解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online