AI文字语音项目:搭建一个支持情感控制、可二次封装的TTS服务

文章目录
📦 第一阶段:环境准备与模型部署
1. 创建项目并安装核心依赖
打开你的终端,执行以下命令:
# 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 2. 下载并初始化ChatTTS模型
创建一个名为 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")classEmotionalTTS:""" 情感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}")defsynthesize(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)-> np.ndarray:""" 核心合成函数。 Args: text: 要合成的文本。 emotion: 情感标签,从 `emotion_params_map` 中选择。 speaker_embedding: 可选,特定说话人音色嵌入。 speed: 语速因子 ( >1 加速, <1 减速)。 sample_rate: 输出音频采样率。 save_path: 如需直接保存,提供.wav文件路径。 Returns: audio_data: 合成的音频波形数据 (numpy数组)。 """# 1. 文本预处理 (ChatTTS要求特殊处理) texts =[text]# 2. 情感参数注入 (通过`infer_seed`控制) params = self.emotion_params_map.get(emotion, self.emotion_params_map['neutral'])# 3. 生成随机种子以实现不同的情感/音色 (可控的随机性) rand_spk = np.random.randint(0,100000)if speaker_embedding isNoneelseNone# 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:ifnot 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 defbatch_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 isNone: emotions =['neutral']*len(texts) file_paths =[]for i,(text, emotion)inenumerate(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 defget_available_emotions(self)-> List[str]:"""返回预定义的情感标签列表。"""returnlist(self.emotion_params_map.keys())# 示例:如何创建音色嵌入(高级功能,用于克隆特定音色)defcreate_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 simpleaudiodefmain():# 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.0if emo !='sad'else0.9,# 悲伤时语速放慢 save_path=f"./output/demo_{emo}.wav"# 保存文件)# 尝试播放(如果环境支持)try: play_obj = sa.play_buffer(audio_data,1,2, sr) play_obj.wait_done()except:print(f"音频已保存,如需播放请查看文件: demo_{emo}.wav")# 4. 批量合成示例print("\n"+"="*50)print("开始批量合成示例...") batch_texts =["早上好,今天天气真不错。","我对此感到非常失望。","太棒了!我们终于成功了!","请立即离开这个地方。"] batch_emotions =['friendly','sad','happy','angry'] saved_files = tts_engine.batch_synthesize( texts=batch_texts, emotions=batch_emotions, save_dir="./output/batch")print(f"批量合成完成,共生成 {len(saved_files)} 个文件。")# 5. 高级:尝试自定义情感参数(直接修改映射)print("\n"+"="*50)print("高级:自定义情感参数...") tts_engine.emotion_params_map['whisper']={'temperature':0.2,'spk_emb':None}# 耳语 audio_custom, _ = tts_engine.synthesize("这是一个秘密,我只告诉你一个人。", emotion='whisper', save_path="./output/whisper_secret.wav")print("自定义情感「whisper」合成完成。")if __name__ =="__main__":# 确保有输出目录import os os.makedirs("./output", exist_ok=True) os.makedirs("./output/batch", exist_ok=True) main()print("\n所有测试完成!请检查 './output' 目录下的音频文件。")🔧 第四阶段:部署为API服务(Flask示例)
将你的封装部署为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 =Nonedefinit_engine():global tts_engine print("正在加载TTS模型...") tts_engine = EmotionalTTS(device='cpu')# API服务通常用CPUprint("模型加载完毕,API服务就绪。") init_engine()@app.route('/synthesize', methods=['POST'])defsynthesize():"""API端点:文本转语音""" data = request.json # 解析请求参数 text = data.get('text','') emotion = data.get('emotion','neutral') speed =float(data.get('speed',1.0))ifnot 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='WAV') audio_bytes.seek(0)# 也可以选择保存到文件后返回URL(生产环境建议)# filename = f"{uuid.uuid4()}.wav"# filepath = os.path.join('./audio_cache', filename)# sf.write(filepath, audio_data, sr)# return jsonify({'url': f'/audio/{filename}'})return send_file( audio_bytes, mimetype='audio/wav', as_attachment=True, download_name=f'speech_{emotion}.wav')except Exception as e:return jsonify({'error':f'合成失败: {str(e)}'}),[email protected]('/emotions', methods=['GET'])deflist_emotions():"""返回支持的情感列表"""return jsonify({'emotions': tts_engine.get_available_emotions()})if __name__ =='__main__': os.makedirs('./audio_cache', exist_ok=True)# 生产环境请使用 waitress 或 gunicorn,不要用debug模式 app.run(host='0.0.0.0', port=5000, debug=True, use_reloader=False)启动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 📝 重要补充与高级扩展
1. 如何提升效果与定制化
- 优化情感:调整
emotion_params_map中的temperature值(0.1-1.5),值越高声音越有表现力(可能不稳定),值越低保真稳定。 - 细粒度控制:在文本中插入 ChatTTS特定的控制符,例如
[uv_break](短停顿)、[laugh](笑声),能让效果更生动。 - 微调模型:如果想针对特定场景(如广播剧)优化,需准备高质量的
(文本, 音频, 情感标签)配对数据,使用ChatTTS训练脚本进行微调。
2. 项目结构建议
MyEmotionalTTS/ ├── EmotionalTTS.py # 核心封装类 ├── init_model.py # 初始化脚本 ├── test_tts.py # 测试脚本 ├── api_service.py # Flask API服务 ├── requirements.txt # 依赖列表 ├── output/ # 生成音频目录 └── README.md # 项目说明 3. 生产环境注意事项
- 性能:首次推理较慢,后续会缓存。如需高并发,考虑模型预热和队列系统。
- 稳定性:API服务中务必添加异常处理和输入验证。
- 内存:加载模型约占用2-3GB GPU内存(或更多CPU内存)。可尝试使用
torch.compile或模型量化(如torch.quantization)进行优化。
这个方案提供了从安装、封装、测试到部署的完整代码链路。可以直接复制代码运行,并根据注释进行修改和扩展。
