阿里云 Qwen TTS (CosyVoice) 使用指南

2 阅读9分钟

手把手教你接入阿里云 Qwen TTS,让 AI 帮你「开口说话」

🎙️ 从 API 申请到代码落地,一篇搞定 AI 语音合成

📖 前言

最近在搞一个需要语音合成的小项目,调研了一圈市面上的 TTS 服务:

服务优点缺点
OpenAI TTS效果顶级贵、中文一般、需要梯子
Azure Speech稳定、音色多配置复杂、计费不透明
讯飞语音老牌、稳定中规中矩、克隆能力弱
百度语音便宜效果一般
Qwen TTS中文自然、支持克隆、SSML 完善文档稍乱(所以写了这篇)

最后选了 阿里云 Qwen TTS(CosyVoice),跑了一段时间感觉还不错,这里把完整的接入流程和踩坑经验分享出来。

本文你将学到:

  • ✅ 如何申请 API Key
  • ✅ TTS 接口怎么调
  • ✅ 预置音色 & 克隆自己的声音
  • ✅ 用 SSML 让语音有情感变化
  • ✅ cURL / Node.js 代码模板(直接复制能跑)

废话不多说,直接开整 👇


目录


什么是 Qwen TTS

Qwen TTS 是阿里云通义千问(Qwen)推出的语音合成服务,基于 CosyVoice 模型,能够将文本转换为自然流畅的语音。

核心特点

特性说明
多音色支持提供丰富的预置音色,涵盖男声、女声、不同语言和风格
音色克隆支持上传音频样本,克隆自定义音色
SSML 支持支持语音合成标记语言,精细控制语速、停顿、情感等
流式输出支持 WebSocket 流式返回音频,适合实时场景
高质量音频支持多种采样率(8k-48k)和格式(mp3/wav/pcm)

可用模型

模型名称说明
cosyvoice-v3-plus推荐使用,支持音色克隆,效果最佳
cosyvoice-v3-flash快速版本,响应更快,适合对延迟敏感的场景

获取 API Key

步骤 1:注册/登录阿里云

访问 阿里云官网,注册或登录你的账号。

步骤 2:开通百炼服务

  1. 进入 阿里云百炼控制台
  2. 首次使用需要开通服务(免费开通)
  3. 新用户通常有免费额度可用

步骤 3:创建 API Key

  1. 在百炼控制台左侧菜单找到 API-KEY 管理
  2. 点击 创建新的 API-KEY
  3. 复制并妥善保存你的 API Key

⚠️ 安全提示:API Key 是敏感信息,请勿在客户端代码或公开仓库中暴露。


API 接口说明

Qwen TTS 提供两种调用方式:

1. HTTP API(简单场景)

适合一次性合成短文本。

Endpoint:

POST https://dashscope.aliyuncs.com/api/v1/services/aigc/text2audio/text-to-audio

Headers:

Authorization: Bearer YOUR_API_KEY
Content-Type: application/json

2. WebSocket API(流式/长文本)

适合实时流式输出或长文本合成。

Endpoint:

wss://dashscope.aliyuncs.com/api-ws/v1/inference/

核心参数说明

参数类型必填说明
modelstring模型名称,如 cosyvoice-v3-plus
textstring要合成的文本内容
voicestring音色 ID(预置或克隆)
formatstring音频格式:mp3(默认)、wavpcm
sample_rateint采样率:80001600022050(默认)、240004410048000
volumeint音量:0-100,默认 50
ratefloat语速:0.5-2.0,默认 1.0
pitchfloat音调:0.5-2.0,默认 1.0
enable_ssmlbool是否启用 SSML 解析,默认 false

获取 Voice ID

方式一:使用预置音色

阿里云提供了丰富的预置音色,无需额外配置即可使用。

常用预置音色示例:

Voice ID描述
longxiaochun龙小淳,女声,温柔知性
longxiaoxia龙小夏,女声,活泼可爱
longyuan龙媛,女声,亲切自然
longwan龙湾,男声,沉稳大气
longjielidou龙杰力豆,男声,阳光活力

完整音色列表请参考:CosyVoice 音色列表

方式二:克隆自定义音色

如果预置音色不满足需求,可以上传音频样本克隆专属音色。

克隆步骤:

  1. 准备音频样本

    • 格式:MP3 或 WAV
    • 时长:建议 10-30 秒
    • 质量:清晰的人声,避免背景噪音
  2. 调用克隆 API

# 1. 上传音频获取 URL(或使用已有的公网音频 URL)

# 2. 创建克隆音色
curl -X POST "https://dashscope.aliyuncs.com/api/v1/services/audio/tts/customization" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "cosyvoice-v3-plus",
    "action": "create",
    "prefix": "myvoice",
    "url": "https://your-audio-url.com/sample.mp3"
  }'
  1. 获取 Voice ID

    • 克隆成功后会返回 voice_id
    • 格式类似:cosyvoice-v3-plus-voice-xxxxxxxxxxxx
    • 该 ID 可永久使用
  2. 查询克隆状态

curl -X POST "https://dashscope.aliyuncs.com/api/v1/services/audio/tts/customization" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "cosyvoice-v3-plus",
    "action": "query",
    "voice_id": "YOUR_VOICE_ID"
  }'

状态说明:

  • DEPLOYING:处理中,请等待
  • OK:已就绪,可以使用
  • UNDEPLOYED:处理失败

代码示例

cURL 示例

HTTP API(同步)
curl -X POST "https://dashscope.aliyuncs.com/api/v1/services/aigc/text2audio/text-to-audio" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "cosyvoice-v3-plus",
    "input": {
      "text": "你好,欢迎使用阿里云语音合成服务。"
    },
    "parameters": {
      "voice": "longxiaochun",
      "format": "mp3",
      "sample_rate": 22050,
      "volume": 50,
      "rate": 1.0,
      "pitch": 1.0
    }
  }' \
  --output output.mp3

Node.js 示例

WebSocket 流式合成
const WebSocket = require('ws');
const fs = require('fs');
const { v4: uuidv4 } = require('uuid');

const API_KEY = 'YOUR_API_KEY';

/**
 * 使用 WebSocket 流式合成语音
 * @param {string} text - 要合成的文本
 * @param {string} voiceId - 音色 ID
 * @param {string} outputPath - 输出文件路径
 * @param {Object} options - 可选参数
 */
async function synthesize(text, voiceId, outputPath, options = {}) {
    const {
        model = 'cosyvoice-v3-plus',
        format = 'mp3',
        sampleRate = 22050,
        volume = 50,
        rate = 1.0,
        pitch = 1.0
    } = options;

    const taskId = uuidv4();

    return new Promise((resolve, reject) => {
        const ws = new WebSocket('wss://dashscope.aliyuncs.com/api-ws/v1/inference/', {
            headers: {
                'Authorization': `bearer ${API_KEY}`
            }
        });

        const fileStream = fs.createWriteStream(outputPath);

        ws.on('open', () => {
            console.log('WebSocket 连接成功');

            // 发送任务请求
            const runTaskMessage = {
                header: {
                    action: 'run-task',
                    task_id: taskId,
                    streaming: 'duplex'
                },
                payload: {
                    task_group: 'audio',
                    task: 'tts',
                    function: 'SpeechSynthesizer',
                    model: model,
                    parameters: {
                        text_type: 'PlainText',
                        voice: voiceId,
                        format: format,
                        sample_rate: sampleRate,
                        volume: volume,
                        rate: rate,
                        pitch: pitch
                    },
                    input: {}
                }
            };

            ws.send(JSON.stringify(runTaskMessage));
        });

        ws.on('message', (data, isBinary) => {
            if (isBinary) {
                // 二进制数据是音频流
                fileStream.write(data);
            } else {
                const message = JSON.parse(data.toString());
                const event = message.header?.event;

                switch (event) {
                    case 'task-started':
                        console.log('任务已启动,发送文本...');

                        // 发送文本内容
                        ws.send(JSON.stringify({
                            header: {
                                action: 'continue-task',
                                task_id: taskId,
                                streaming: 'duplex'
                            },
                            payload: {
                                input: { text: text }
                            }
                        }));

                        // 发送结束信号
                        setTimeout(() => {
                            ws.send(JSON.stringify({
                                header: {
                                    action: 'finish-task',
                                    task_id: taskId,
                                    streaming: 'duplex'
                                },
                                payload: { input: {} }
                            }));
                        }, 100);
                        break;

                    case 'task-finished':
                        console.log('语音合成完成');
                        fileStream.end(() => {
                            ws.close();
                            resolve(outputPath);
                        });
                        break;

                    case 'task-failed':
                        const error = message.header?.error_message || '未知错误';
                        console.error('合成失败:', error);
                        fileStream.end();
                        ws.close();
                        reject(new Error(error));
                        break;
                }
            }
        });

        ws.on('error', (error) => {
            console.error('WebSocket 错误:', error.message);
            fileStream.end();
            reject(error);
        });
    });
}

// 使用示例
synthesize(
    '你好,这是一段测试语音。',
    'longxiaochun',  // 或使用克隆的 voice_id
    './output.mp3'
).then(path => {
    console.log('输出文件:', path);
}).catch(err => {
    console.error('错误:', err.message);
});
HTTP API 简单调用
const https = require('https');
const fs = require('fs');

const API_KEY = 'YOUR_API_KEY';

async function textToSpeech(text, voiceId, outputPath) {
    const requestBody = JSON.stringify({
        model: 'cosyvoice-v3-plus',
        input: { text },
        parameters: {
            voice: voiceId,
            format: 'mp3',
            sample_rate: 22050
        }
    });

    const options = {
        hostname: 'dashscope.aliyuncs.com',
        path: '/api/v1/services/aigc/text2audio/text-to-audio',
        method: 'POST',
        headers: {
            'Authorization': `Bearer ${API_KEY}`,
            'Content-Type': 'application/json',
            'Content-Length': Buffer.byteLength(requestBody)
        }
    };

    return new Promise((resolve, reject) => {
        const req = https.request(options, (res) => {
            const fileStream = fs.createWriteStream(outputPath);
            res.pipe(fileStream);
            fileStream.on('finish', () => {
                fileStream.close();
                resolve(outputPath);
            });
        });

        req.on('error', reject);
        req.write(requestBody);
        req.end();
    });
}

// 使用示例
textToSpeech('你好世界', 'longxiaochun', './hello.mp3');

SSML 情感表达

想让合成的语音更有感染力?SSML(语音合成标记语言)可以帮你实现丰富的情感变化。

什么是 SSML

SSML(Speech Synthesis Markup Language)是一种 XML 格式的标记语言,用于精细控制语音合成的各种细节:

  • 语速 (rate):控制说话快慢
  • 音调 (pitch):控制声音高低
  • 音量 (volume):控制声音大小
  • 停顿 (<break>):在特定位置添加停顿
  • 音效 (effect):添加机器人、萝莉、回声等特效

基本语法

所有 SSML 内容必须包裹在 <speak> 标签中:

<speak rate="1.0" pitch="1.0" volume="50">
  你的文本内容
</speak>

情绪参数预设

通过调整 ratepitchvolume 三个参数的组合,可以表达不同的情绪:

情绪ratepitchvolume特点描述
😊 开心/兴奋1.151.165语速快、音调高、音量大
😢 悲伤/失落0.850.940语速慢、音调低、音量小
😠 生气/愤怒1.21.0575语速快、音调略高、音量大
🥰 温柔/安慰0.91.0545语速慢、音调略高、音量轻柔
😲 惊讶/震惊1.11.1560语速略快、音调明显升高
😟 担忧/紧张0.950.9550语速略慢、音调略低
🤔 疑惑/困惑0.91.050语速慢、正常音调
💪 自信/坚定1.01.065正常语速、音量大

停顿控制

使用 <break> 标签添加停顿,让语音更自然:

场景推荐时长
逗号/顿号后50-100ms
句号后300-500ms
思考/犹豫200-400ms
戏剧性停顿500-1000ms
段落间1-2s

实用示例

示例 1:开心地打招呼
<speak rate="1.15" pitch="1.1" volume="65">
  哇!<break time="200ms"/>好久不见!<break time="300ms"/>
  最近过得怎么样?
</speak>
示例 2:悲伤的告别
<speak rate="0.85" pitch="0.9" volume="40">
  好吧...<break time="500ms"/>
  我知道了。<break time="400ms"/>
  那就...<break time="300ms"/>再见吧。
</speak>
示例 3:愤怒的质问
<speak rate="1.2" pitch="1.05" volume="75">
  不行!<break time="200ms"/>
  绝对不行!<break time="300ms"/>
  你到底在想什么?
</speak>
示例 4:温柔的安慰
<speak rate="0.9" pitch="1.05" volume="45">
  别担心,<break time="400ms"/>
  一切都会好起来的。<break time="500ms"/>
  我会一直陪着你。
</speak>
示例 5:情绪转换(从疑惑到惊喜)
<speak>
  <speak rate="0.9" pitch="1.0" volume="50">
    这是什么?<break time="400ms"/>
  </speak>
  <speak rate="1.15" pitch="1.15" volume="65">
    哇!<break time="200ms"/>是我一直想要的礼物!
  </speak>
</speak>
示例 6:讲故事(多情绪切换)
<speak rate="1.0" pitch="1.0" volume="50">
  从前有一座山,<break time="300ms"/>山里有一座庙。
  <break time="500ms"/>
  
  <speak rate="0.9" pitch="0.95" volume="45">
    老和尚对小和尚说...<break time="600ms"/>
  </speak>
  
  <speak rate="1.1" pitch="1.1" volume="60">
    "小和尚!<break time="200ms"/>去打水!"
  </speak>
</speak>

用 AI 生成 SSML

手动编写 SSML 比较繁琐,推荐使用 AI 工具(如 ChatGPT、Claude、通义千问等)自动转换。

Prompt 模板

将以下 Prompt 发送给 AI,即可将普通文本转换为带情感的 SSML:

请将以下文本转换为 Qwen TTS 的 SSML 格式。要求:
1. 根据语境添加适当的情感参数(rate/pitch/volume)
2. 在合适的位置添加停顿(<break>)
3. 情绪变化处使用嵌套的 <speak> 标签
4. 参考情绪参数:
   - 开心:rate=1.15, pitch=1.1, volume=65
   - 悲伤:rate=0.85, pitch=0.9, volume=40
   - 生气:rate=1.2, pitch=1.05, volume=75
   - 温柔:rate=0.9, pitch=1.05, volume=45

需要转换的文本:
"""
[在这里粘贴你的文本]
"""
转换示例

原始文本:

你怎么才来?我等了你好久!算了算了,看你这么辛苦,我就不怪你了。走吧,我们去吃饭。

AI 转换后的 SSML:

<speak>
  <speak rate="1.15" pitch="1.05" volume="70">
    你怎么才来?<break time="200ms"/>
    我等了你好久!
  </speak>
  <break time="500ms"/>
  <speak rate="0.95" pitch="1.0" volume="50">
    算了算了,<break time="300ms"/>
    看你这么辛苦,<break time="200ms"/>我就不怪你了。
  </speak>
  <break time="400ms"/>
  <speak rate="1.1" pitch="1.1" volume="60">
    走吧,<break time="200ms"/>我们去吃饭!
  </speak>
</speak>

API 调用注意事项

使用 SSML 时,需要在 API 参数中启用:

parameters: {
    voice: 'your_voice_id',
    format: 'mp3',
    enable_ssml: true  // 必须设为 true
}

特殊字符转义:

字符转义写法
"&quot;
'&apos;
&&amp;
<&lt;
>&gt;

常见问题

Q: 如何选择合适的模型?

场景推荐模型
音色克隆cosyvoice-v3-plus
追求低延迟cosyvoice-v3-flash
高质量要求cosyvoice-v3-plus

Q: 支持哪些音频格式?

格式说明
mp3通用格式,体积小,推荐使用
wav无损格式,体积较大
pcm原始音频数据,需自行处理

Q: 如何调整语音效果?

  • 语速:调整 rate 参数(0.5-2.0),值越大越快
  • 音调:调整 pitch 参数(0.5-2.0),值越大越高
  • 音量:调整 volume 参数(0-100)

Q: 长文本如何处理?

  • 建议使用 WebSocket 流式 API
  • 可将长文本分段处理,然后拼接音频
  • 注意单次请求的文本长度限制(通常 5000 字符)

Q: 克隆音色的音频要求?

  • 时长:10-30 秒效果最佳
  • 格式:MP3 或 WAV
  • 质量:清晰人声,无背景音乐/噪音
  • 内容:自然说话,语速适中

参考链接


最后更新:2026 年 2 月