文章目录
- 📦 第一阶段:环境准备与模型部署
- 🧱 第二阶段:核心封装与情感控制接口
- 🚀 第三阶段:使用与测试
- 🔧 第四阶段:部署为API服务(Flask示例)
- 📝 重要补充与高级扩展
📦 第一阶段:环境准备与模型部署
1. 创建项目并安装核心依赖
打开你的终端,执行以下命令:
# 1. 创建项目目录mkdirMyEmotionalTTS&&cdMyEmotionalTTS# 2. 创建Python虚拟环境(推荐)python -m venv venv# 在Linux/Mac上激活:sourcevenv/bin/activate# 在Windows上激活:# venv\Scripts\activate# 3. 安装PyTorch (根据你的CUDA版本选择,以CUDA 12.1为例)pipinstalltorch torchaudio --index-url https://download.pytorch.org/whl/cu121# 4. 安装ChatTTS及其他依赖pipinstallChatTTS transformers soundfile ipython2. 下载并初始化ChatTTS模型
创建一个名为init_model.py的脚本,写入以下代码:
importChatTTSimporttorchimportwarnings warnings.filterwarnings("ignore")# 初始化ChatTTSchat=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,这是二次封装的精髓。
importChatTTSimporttorchimportnumpyasnpimportsoundfileassffromtypingimportList,Optional,Dictimportwarnings 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()# 加载模型ifdevice: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)ifspeaker_embeddingisNoneelseNone# 4. 模型推理withtorch.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. 语速调整 (简单的重采样,生产环境可用更优算法)ifspeed!=1.0:fromscipyimportsignal new_length=int(len(audio_data)/speed)audio_data=signal.resample(audio_data,new_length)# 6. 保存文件(如果提供了路径)ifsave_path:ifnotsave_path.endswith('.wav'):save_path+='.wav'sf.write(save_path,audio_data,samplerate=sample_rate)print(f"[音频已保存] ->{save_path}")returnaudio_data,sample_ratedefbatch_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: 保存的音频文件路径列表。 """importos os.makedirs(save_dir,exist_ok=True)ifemotionsisNone:emotions=['neutral']*len(texts)file_paths=[]fori,(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)returnfile_pathsdefget_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"[提示] 音色克隆功能需参考官方最新实现。")returnnp.random.randn(1,1024).astype(np.float32)# 占位符🚀 第三阶段:使用与测试
创建一个测试脚本test_tts.py来使用我们的封装类。
fromEmotionalTTSimportEmotionalTTSimportsoundfileassfimportsimpleaudioassa# 用于直接播放,安装: 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="你好,世界!这是一个测试,看看情感语音合成效果怎么样。"# 用不同的情感合成同一句话foremoin['neutral','happy','sad','angry']:print(f"\n>>> 正在用「{emo}」情感合成...")audio_data,sr=tts_engine.synthesize(text=test_text,emotion=emo,speed=1.0ifemo!='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__":# 确保有输出目录importos os.makedirs("./output",exist_ok=True)os.makedirs("./output/batch",exist_ok=True)main()print("\n所有测试完成!请检查 './output' 目录下的音频文件。")🔧 第四阶段:部署为API服务(Flask示例)
将你的封装部署为Web服务,以便其他程序调用。创建api_service.py:
fromflaskimportFlask,request,jsonify,send_filefromEmotionalTTSimportEmotionalTTSimportioimportsoundfileassfimportnumpyasnpimportuuidimportos app=Flask(__name__)tts_engine=Nonedefinit_engine():globaltts_engineprint("正在加载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))ifnottext:returnjsonify({'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}'})returnsend_file(audio_bytes,mimetype='audio/wav',as_attachment=True,download_name=f'speech_{emotion}.wav')exceptExceptionase:returnjsonify({'error':f'合成失败:{str(e)}'}),500@app.route('/emotions',methods=['GET'])deflist_emotions():"""返回支持的情感列表"""returnjsonify({'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)进行优化。
这个方案提供了从安装、封装、测试到部署的完整代码链路。可以直接复制代码运行,并根据注释进行修改和扩展。