You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Coqui-AI TTS Server的JavaScript API调用中实现speaker_wav参数(XTTS语音克隆场景)

如何在Coqui-AI TTS Server的JavaScript API调用中实现speaker_wav参数(XTTS语音克隆场景)

看起来你在Coqui TTS Server上用XTTS做语音克隆时,卡在了API调用传参这块——终端命令行能用,但JS里试了各种方式都没搞定,还怕每次调用重新加载模型耗时间对吧?我来帮你理清楚问题出在哪,一步步解决:

一、先修正服务器端的代码问题

你自己修改了Flask路由,但有几个关键小错误,导致参数传了也没生效:

  1. 拼写错误:langauge_name 应该是 language_name
  2. XTTS的语音克隆参数是speaker_wav,不是原代码里的style_wav(style_wav是给其他模型用的)
  3. 要确保服务器能正确读取你传的音频文件路径

给你修正后的完整路由代码:

@app.route("/api/tts", methods=["GET", "POST"])
def tts():
    with lock:
        # 从请求的查询参数、表单或请求头里捞参数,兼容多种传参方式
        text = request.headers.get("text") or request.values.get("text", "")
        speaker_wav = request.headers.get("speaker_wav") or request.values.get("speaker_wav", "")
        language_idx = request.headers.get("language_idx") or request.values.get("language_idx", "en")

        print(f" > 待合成文本: {text}")
        print(f" > 参考语音路径: {speaker_wav}")
        print(f" > 语言标识: {language_idx}")

        # 注意XTTS的synthesizer.tts参数对应:用speaker_wav而不是style_wav
        wavs = synthesizer.tts(
            text=text,
            language_name=language_idx,
            speaker_wav=speaker_wav
        )
        out = io.BytesIO()
        synthesizer.save_wav(wavs, out)
        return send_file(out, mimetype="audio/wav")

二、正确的JavaScript API调用方式

你之前试了各种传参方式没成,核心原因是要匹配服务器端的参数读取逻辑(request.values能读取FormData、URL编码表单和query string的参数),下面给你两种最靠谱的JS调用方案:

方案1:用FormData传参(推荐,后续要传文件内容也兼容)

async function synthesizeVoice() {
    const formData = new FormData();
    formData.append('text', 'Testing, 1 2 3...');
    // 重点:这里的路径必须是**服务器操作系统能直接访问的绝对路径**,不是客户端本地路径!
    // Windows路径记得用双反斜杠转义,或者换成正斜杠
    formData.append('speaker_wav', 'C:\\Users\\perso\\Downloads\\Phonetic Sentence\\Converted\\speaker.wav');
    formData.append('language_idx', 'en');

    try {
        const response = await fetch('http://localhost:5002/api/tts', {
            method: 'POST',
            body: formData
        });

        if (!response.ok) throw new Error(`API请求失败: ${response.status}`);
        
        // 处理返回的音频文件
        const audioBlob = await response.blob();
        const audioUrl = URL.createObjectURL(audioBlob);
        const audio = new Audio(audioUrl);
        audio.play();
    } catch (error) {
        console.error('调用出错:', error);
    }
}

方案2:用URL编码的表单数据

如果不想用FormData,也可以用传统的表单编码方式:

async function synthesizeVoice() {
    const params = new URLSearchParams();
    params.append('text', 'Testing, 1 2 3...');
    params.append('speaker_wav', 'C:\\Users\\perso\\Downloads\\Phonetic Sentence\\Converted\\speaker.wav');
    params.append('language_idx', 'en');

    try {
        const response = await fetch('http://localhost:5002/api/tts', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded'
            },
            body: params
        });

        if (!response.ok) throw new Error(`API请求失败: ${response.status}`);
        
        const audioBlob = await response.blob();
        const audioUrl = URL.createObjectURL(audioBlob);
        const audio = new Audio(audioUrl);
        audio.play();
    } catch (error) {
        console.error('调用出错:', error);
    }
}

方案3:用GET请求(参数放URL查询串)

如果要兼容GET方式,记得对特殊路径字符做编码:

async function synthesizeVoice() {
    const customText = 'Testing, 1 2 3...';
    // 对Windows路径的特殊字符做URL编码
    const speakerWavPath = encodeURIComponent('C:\\Users\\perso\\Downloads\\Phonetic Sentence\\Converted\\speaker.wav');
    const languageIdx = 'en';

    const apiUrl = `http://localhost:5002/api/tts?text=${encodeURIComponent(customText)}&speaker_wav=${speakerWavPath}&language_idx=${languageIdx}`;

    try {
        const response = await fetch(apiUrl);

        if (!response.ok) throw new Error(`API请求失败: ${response.status}`);
        
        const audioBlob = await response.blob();
        const audioUrl = URL.createObjectURL(audioBlob);
        const audio = new Audio(audioUrl);
        audio.play();
    } catch (error) {
        console.error('调用出错:', error);
    }
}

三、解决模型重复加载的问题

你担心的“每次调用重新加载模型”的问题,其实用TTS Server已经完美解决了!你的bat文件启动服务器时,已经一次性加载了XTTS v2模型,后续所有API调用都是复用这个已经加载好的模型实例,完全没有终端命令那种每次启动都要加载模型的等待时间——只要你的bat启动的服务器一直运行,就不用再担心这个问题。

确认你的bat文件没问题(你现在的写法是对的,再贴一遍方便你核对):

set TORCH_FORCE_NO_WEIGHTS_ONLY_LOAD=1
echo TORCH_FORCE_NO_WEIGHTS_ONLY_LOAD=%TORCH_FORCE_NO_WEIGHTS_ONLY_LOAD%
"C:\Users\perso\miniconda3\envs\TTS\Scripts\tts-server.exe" --model_path "C:\Users\perso\AppData\Local\tts\tts_models--multilingual--multi-dataset--xtts_v2" --config_path "C:\Users\perso\AppData\Local\tts\tts_models--multilingual--multi-dataset--xtts_v2\config.json"

四、常见问题排查

  1. 服务器找不到音频文件:speaker_wav必须是服务器能直接访问的绝对路径,不能是客户端本地路径!Windows路径记得用双反斜杠或者正斜杠,比如把C:\xxx改成C:/xxx或者C:\\xxx
  2. 参数没传进去:看TTS Server的终端日志,确认打印出来的speaker_wav、text、language_idx是你传的正确值
  3. 跨域问题:如果你的前端和TTS Server不在同一端口,要给Flask加跨域支持:安装pip install flask-cors,然后在服务器代码开头加:
    from flask_cors import CORS
    app = Flask(__name__)
    CORS(app)  # 生产环境可以限制允许的域名,比如CORS(app, resources={r"/api/*": {"origins": "http://your-frontend-domain.com"}})
    
  4. 模型不匹配:确保启动服务器时指定的是XTTS v2的模型路径,不是其他TTS模型

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.08 11:03:00