F5-TTS文本语音合成模型的使用和接口封装,tts合成音频,http流式输出,音频采样率转换

F5-TTS文本语音生成模型

1. F5-TTS的简介

2024年10月8日,上海交通大学团队发布,F5-TTS (A Fairytaler that Fakes Fluent and Faithful Speech with Flow Matching) 是一款基于扩散Transformer和ConvNeXt V2的文本转语音 (TTS) 模型。F5-TTS旨在生成流畅且忠实的语音,其训练速度和推理速度都得到了提升。 项目还提供了一个名为E2 TTS的模型,它是论文中模型的更接近的复现版本,基于Flat-UNet Transformer。 预训练模型已发布在Hugging Face和Model Scope上。

总而言之,F5-TTS是一个功能强大且易于使用的TTS模型,它结合了扩散模型和流匹配技术,实现了快速训练、快速推理和高质量的语音生成。 其提供的Gradio应用和CLI工具也方便了用户的使用。 项目文档较为完善,方便用户快速上手。

GitHub地址:https://github.com/SWivid/F5-TTS

论文地址:https://arxiv.org/abs/2410.06885

2.模型特点:

快速训练和推理: 相比于其他模型,F5-TTS的训练和推理速度更快。

流畅逼真的语音: 采用流匹配技术,生成更流畅、更自然、更忠实的语音。

基于扩散Transformer和ConvNeXt V2: 利用先进的架构,提升模型性能。

多风格/多说话人生成: 支持多风格和多说话人的语音生成。

提供Gradio应用: 提供友好的图形用户界面,方便用户进行推理和微调。

支持语音聊天: 通过集成Qwen2.5-3B-Instruct模型,支持语音聊天功能。

提供了E2 TTS模型: 作为论文中模型的更接近的复现版本,方便研究者复现论文结果。

Sway Sampling: 一种推理时间的流步骤采样策略,极大地提高了性能。

3.F5-TTS的安装和使用方法

环境配置
  • 使用conda创建虚拟环境
创建一个Python 3.10的conda环境 (也可以使用virtualenv):
conda create -n f5-tts python=3.10
conda activate f5-tts
  • 安装PyTorch和Torchaudio依赖

安装PyTorch和Torchaudio,CUDA版本根据你的显卡选择:
pip install torch==2.3.0+cu118 torchaudio==2.3.0+cu118 --extra-index-url https://download.pytorch.org/whl/cu118
克隆项目,安装环境依赖
git clone https://github.com/SWivid/F5-TTS.git
cd F5-TTS
pip install -e .

4.推理

  • 提供了三种推理方式:

1、Gradio应用 (Web界面)
运行 f5-tts_infer-gradio 命令启动Gradio应用,支持基本TTS、多风格/多说话人生成和基于Qwen2.5-3B-Instruct的语音聊天。可以使用 --port 和 --host 参数指定端口和主机,使用 --share 参数生成共享链接。

2、CLI推理
使用 f5-tts_infer-cli 命令进行命令行推理。 需要指定模型名称 (–model)、参考音频路径 (–ref_audio)、参考文本 (–ref_text) 和要生成的文本 (–gen_text)。 可以使用配置文件 (-c) 指定参数。 支持多语音生成。

# Launch a Gradio app (web interface)
f5-tts_infer-gradio

# Specify the port/host
f5-tts_infer-gradio --port 7860 --host 0.0.0.0

# Launch a share link
f5-tts_infer-gradio --share
模型文件下载可能出现连接超时等网络问题
  • 访问模型文件镜像站
   https://huggingface.co/SWivid/F5-TTS
  • 访问国内镜像站
  https://hf-mirror.com/

方法:huggingface-cli

huggingface-cli 是 Hugging Face 官方提供的命令行工具,自带完善的下载功能。

  1. 安装依赖
      pip install -U huggingface_hub
  1. 设置环境变量
  • Linux
 export HF_ENDPOINT=https://hf-mirror.com
  • 使用文本编辑器以管理员权限打开/etc/environment文件。你可以使用nano或者vim。例如,使用nano的命令如下:
    • sudo nano /etc/environment
  • 在文件中添加你的环境变量。在你的情况下,添加这行:
HF_ENDPOINT="https://hf-mirror.com"

  • 保存并关闭文件。如果你使用的是nano,可以通过按Ctrl+X,然后按Y确认保存,最后按Enter键来保存文件。
  • 为了使变更立即生效,你可以注销并重新登录,或者在终端中运行以下命令来重载环境变量:
source /etc/environment

这样,HF_ENDPOINT环境变量就被设置为永久的了,并且每次启动时都会自动加载。

3.1 下载模型示例

 huggingface-cli download --resume-download SWivid/F5-TTS --local-dir /home/x1/F5-TTS/ckpts/
 

3.2 下载数据集示例

huggingface-cli download --repo-type dataset --resume-download wikitext --local-dir wikitext

可以添加 --local-dir-use-symlinks False 参数禁用文件软链接,这样下载路径下所见即所得,详细解释请见上面提到的教程。

5.启动Gradio应用 (Web界面)

  • 命令
f5-tts_infer-gradio --port 13066 --host 0.0.0.0

在这里插入图片描述

6.编写一个Python推理的Flask接口程序

import re
from flask import Flask, request, jsonify, send_file
import io
import tempfile
import soundfile as sf
import os
from f5_tts.infer.utils_infer import (
    preprocess_ref_audio_text,
    infer_process,
    remove_silence_for_generated_wav
)
from f5_tts.model import DiT
from f5_tts.infer.utils_infer import load_vocoder, load_model

app = Flask(__name__)

# Paths to model and vocab files
MODEL_PATH = "/home/x1/F5-TTS/ckpts/F5TTS_Base/model_1200000.safetensors"
VOCAB_PATH = "/home/x1/F5-TTS/ckpts/F5TTS_Base/vocab.txt"

# Initialize TTS model and vocoder
F5TTS_ema_model = None
vocoder = load_vocoder()

def load_f5tts_model():
    global F5TTS_ema_model
    if F5TTS_ema_model is None:
        F5TTS_model_cfg = dict(dim=1024, depth=22, heads=16, ff_mult=2, text_dim=512, conv_layers=4)
        F5TTS_ema_model = load_model(DiT, F5TTS_model_cfg, MODEL_PATH, vocab_file=VOCAB_PATH)

load_f5tts_model()

def convert_to_chinese_date(text):
    """Convert dates and numbers in the text to Chinese format."""
    num_map = {
   "0": "零", "1": "一", "2": "二", "3": "三", "4": "四",
               "5": "五", "6": "六", "7": "七", "8": "八", "9": "九"}
    
    def number_to_chinese(match):
        number = match.group()
        if len(number) == 1:  # 单个数字
            return num_map[number]
        elif len(number) == 2:  # 两位数
            if number.startswith("1"):  # 特殊处理10-19
                return "十" + (num_map[number[1]] if number[1] != "0" else "")
            else:
                return num_map[number[0]] + "十" + (num_map[number[1]] if number[1] != "0" else "")
        else:
            return "".join(num_map[digit] for digit in number)  # 处理三位及以上的数字

    # 将日期格式(如12月、10日)处理为中文读法
    text = re.sub(r'\d+', number_to_chinese, text)
    return text

@app.route('/generateAudio', methods=['POST'])
def synthesize():
    # Validate and parse input
    if 'gen_text' not in request.form:
        return jsonify({
   "error": "Missing required parameter: 'gen_text'"}), 400

    gen_text = request.form['gen_text']
    ref_text = request.form.get('ref_text', '')
    ref_audio_path = None

    if 'ref_audio' in request.files:
        # Save uploaded reference audio file to a temporary location
        ref_audio = request.files['ref_audio']
        with tempfile.NamedTemporaryFile(delete=False, suffix=".wav") as temp_audio_file:
            ref_audio.save(temp_audio_file.name)
            ref_audio_path = temp_audio_file.name
    elif 'ref_audio_path' in request.form:
        # Use reference audio path provided in the form
        ref_audio_path = request.form['ref_audio_path']
        if not os.path.exists(ref_audio_path):
            return jsonify({
   "error": f"File not found: {
     ref_audio_path}"}), 400

    if not ref_audio_path:
        return jsonify({
   "error": "Missing required parameter: 'ref_audio' or 'ref_audio_path'"}), 400

    try:
        # Convert dates in gen_text to Chinese format
        gen_text = convert_to_chinese_date(gen_text)

        # Preprocess reference audio and text
        ref_audio_data, ref_text = preprocess_ref_audio_text(ref_audio_path, ref_text)

        # Synthesize speech
        final_wave, final_sample_rate, _ = infer_process(
            ref_audio_data,
            ref_text,
            gen_text,
            F5TTS_ema_model,
            vocoder,
            cross_fade_duration=0.15,
            speed=1.0,
        )

        # Remove silences from generated audio
        with tempfile.NamedTemporaryFile(delete=False, suffix=".wav") as temp_generated_audio:
            sf.write(temp_generated_audio.name, final_wave, final_sample_rate)
            remove_silence_for_generated_wav(temp_generated_audio.name)
            final_wave, _ = sf.read(temp_generated_audio.name)

        # Convert synthesized audio to bytes
        audio_buffer = io.BytesIO()
        sf.write(audio_buffer, final_wave, final_sample_rate, format='WAV')
        audio_buffer.seek(0)

        return send_file(
            audio_buffer,
            as_attachment=True,
            download_name="synthesized_audio.wav",
            mimetype="audio/wav"
        )

    except Exception as e:
        return jsonify({
   "error": str(e)}), 500


if __name__ == '__main__':
    app.run(host='0.0.0.0', port=13666, debug=True)


  • 注意在读取日期时候会出错,这个地方可以编写一个日期处理函数来解决这个问题。
6.1 启动语音合成的推理程序
  • 进入Python虚拟环境
source /home/x1/anaconda3/bin/activate
conda activate f5-tts
  • 进入程序所在的目录,运行并挂起程序,输出日志到日志文件
# 进入程序所在的目录
cd /home/x1/F5-TTS/src/f5_tts/infer/
# 运行并挂起程序,输出日志到日志文件
nohup python   tts_api.py >> ./tts_api.log &

7.使用go语言编写一个合成声音的接口

  • 创建F5_TTSGenerateAudio.go文件
func GenerateTTSAudio(name, generateText string) ([]byte, error) {
   
	url := config.Conf.TTSVoiceModelUrl + "/generateAudio"

	// 从配置中获取参数
	refAudioPath := config.Conf.TTSVoiceModelSets[name].ReferAudioPath
	refText := config.Conf.TTSVoiceModelSets[name].ReferText

	// 创建一个缓冲区,用于存储多部分表单数据
	var requestBody bytes.Buffer
	writer := multipart.NewWriter(&requestBody)

	// 检查是否需要上传参考音频文件
	if refAudioPath != "" {
   
		file, err := os.Open(refAudioPath)
		if err != nil {
   
			return nil, fmt.Errorf("无法打开参考音频文件: %v", err)
		}
		defer file.Close()

		// 将参考音频文件添加到表单中
		fileWriter, err := writer.CreateFormFile("ref_audio", "ref_audio.wav")
		if err != nil {
   
			return nil, fmt.Errorf("无法创建表单文件: %v", err)
		}
		if _, err = io.Copy(fileWriter, file); err != nil {
   
			return nil, fmt.Errorf("无法将文件复制到表单文件中: %v", err)
		}
	} else {
   
		return nil, fmt.Errorf("参考音频路径未提供")
	}
	fmt.Println("refAudioPath: ", refAudioPath)

	// 添加参考音频路径到表单(如果接口需要)
	/*	if err := writer.WriteField("ref_audio_path", refAudioPath); err != nil {
			return nil, fmt.Errorf("无法添加参考音频路径到表单: %v", err)
		}
	*/
	// 添加参考文本到表单
	if err := writer.WriteField("ref_text", refText); err != nil {
   
		return nil, fmt.Errorf("无法添加参考文本到表单: %v", err)
	}
	fmt.Println("ref_text: ", refText)
	// 添加需要生成音频的文本到表单
	if err := writer.WriteField("gen_text", generateText); err != nil {
   
		return nil, fmt.Errorf("无法添加生成文本到表单: %v", err)
	}

	// 关闭 writer,以完成表单
	if err := writer.Close(); err != nil {
   
		return nil, fmt.Errorf("无法关闭 writer: %v", err)
	}

	// 创建 HTTP POST 请求
	request, err := http.NewRequest("POST", url, &requestBody
### 二维前缀算法在瓦片图案生成或处理中的应用 #### 定义与基本原理 二维前缀是一种用于快速求解矩形区域内元素总的技术。对于给定的一个矩阵 `A`,可以预先计算一个新的矩阵 `prefixSum`,其中每个元素 `(i,j)` 表示从原点 `(0,0)` 到当前坐标的子矩阵内所有数值之。 通过这种方式,在后续查询任意指定区域内的元素累积值时只需常数时间复杂度 O(1),因为只需要访问四个预处理过的节点即可完成加减运算得出结果[^1]。 #### 应用场景分析 当涉及到像地图服务这样的应用场景时——特别是采用分层切片机制的地图系统(如微软 Bing 地图),这种技术能够显著提升性能效率: - **加速渲染过程**:利用二维前缀可以在瞬间获取特定范围内的数据汇总信息,从而加快图像合成速度; - **简化碰撞检测逻辑**:游戏开发等领域经常需要用到对象间相互作用判断,借助此方法可迅速定位目标区间并作出响应; - **优化路径规划算法**:无论是最短路还是其他形式的空间搜索问题,都能受益于高效的数据检索能力所带来的优势[^2]。 #### 实现案例展示 下面给出一段 Python 代码片段作为例子说明如何基于上述理论框架构建实际解决方案: ```python def build_prefix_sum(matrix): rows = len(matrix) cols = len(matrix[0]) if matrix else 0 prefix_sum = [[0]*(cols+1) for _ in range(rows+1)] for i in range(1,rows+1): for j in range(1,cols+1): prefix_sum[i][j]=matrix[i-1][j-1]+\ prefix_sum[i-1][j]+ \ prefix_sum[i][j-1]- \ prefix_sum[i-1][j-1] return prefix_sum def query_submatrix_sum(prefix_sum,x1,y1,x2,y2): """Query sum of elements within sub-matrix defined by top-left (x1,y1), bottom-right(x2,y2).""" return prefix_sum[x2+1][y2+1]-prefix_sum[x1][y2+1]-prefix_sum[x2+1][y1]+prefix_sum[x1][y1] # Example usage: input_matrix=[[3,0,1,4],[2,8,7,5],[4,6,9,1]] ps=build_prefix_sum(input_matrix) print(query_submatrix_sum(ps,1,1,2,2)) # Output should be 30 which is the sum inside this area. ```
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值