Skip to main content
语音合成

实时语音合成

实时语音合成将文本实时转换为自然语音,支持流式输入与输出,具备声音复刻、声音设计及精细化音频控制能力,适用于语音助手、有声读物、智能客服等场景。

概述

实现低延迟文本到语音转换。
  • 支持流式输入与输出,首包延迟低
  • 可调节语速、语调、音量与码率,实现精细的语音效果控制
  • 兼容主流音频格式(PCM、WAV、MP3、Opus),最高支持 48kHz 采样率输出
  • 支持指令控制,可通过自然语言指令控制语音表现力
  • 支持声音复刻声音设计音色定制
  • 支持情感与富语言标签,可在文本中嵌入标签控制情感表达或插入拟声效果
批量场景(有声读物、课件配音等)可使用非实时语音合成。各模型选型建议请参见语音合成
Sambert 为早期语音合成模型,新项目建议优先使用 CosyVoice 或 Qwen-TTS,可获得更好的合成效果和更丰富的功能支持。

前提条件

快速开始

以下是各模型的语音合成示例。更多示例和参数说明请参见各模型的API参考
  • Qwen-Audio-TTS
  • CosyVoice
  • Qwen-TTS
以下示例演示如何使用系统音色进行语音合成。如需使用指令控制功能,请通过 instruction 参数设置指令。
Python
# coding=utf-8

import os
import dashscope
from dashscope.audio.tts_v2 import *

# 新加坡和北京地域的API Key不同。获取API Key:https://help.aliyun.com/zh/model-studio/get-api-key
# 若没有配置环境变量,请用阿里云百炼API Key将下行替换为:dashscope.api_key = "sk-xxx"
dashscope.api_key = os.environ.get('DASHSCOPE_API_KEY')

# 以下为华北2(北京)地域的配置,调用时请将"{WorkspaceId}"替换为真实的业务空间ID,各地域的配置不同。
dashscope.base_websocket_api_url='wss://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/api-ws/v1/inference'

# 模型
# qwen-audio-3.0-tts-flash/qwen-audio-3.0-tts-plus:使用longanhuan_v3.6等音色。
# 不同语言选择对应音色
model = "qwen-audio-3.0-tts-flash"
# 音色
voice = "longanhuan_v3.6"

# 实例化SpeechSynthesizer,并在构造方法中传入模型(model)、音色(voice)等请求参数
synthesizer = SpeechSynthesizer(model=model, voice=voice)
# 发送待合成文本,获取二进制音频
audio = synthesizer.call("今天天气怎么样?")
# 首次发送文本时需建立 WebSocket 连接,因此首包延迟会包含连接建立的耗时
print('[Metric] requestId为:{},首包延迟为:{}毫秒'.format(
    synthesizer.get_last_request_id(),
    synthesizer.get_first_package_delay()))

# 将音频保存至本地
with open('output.mp3', 'wb') as f:
    f.write(audio)

会话配置

Qwen-TTS 交互模式

Qwen-TTS Realtime API 提供两种交互模式:
  • server_commit 模式:由服务端智能处理文本分段与合成时机,适合大段文本的连续合成场景。客户端只需持续追加文本,无需关注分段和提交。
  • commit 模式:由客户端主动提交文本缓冲区以触发合成,适合需要精确控制合成时机的场景(如对话式 AI 逐轮合成)。
切换交互模式
  • WebSocket:通过 session.update 事件中的 mode 字段设置。
{
    "type": "session.update",
    "session": {
        "mode": "server_commit"
    }
}
  • Python SDK:在 update_session 方法中通过 mode 参数设置。
qwen_tts_realtime.update_session(
    voice='Cherry',
    response_format=AudioFormat.PCM_24000HZ_MONO_16BIT,
    mode='server_commit'
)
  • Java SDK:通过 QwenTtsRealtimeConfig.builder() 设置 mode 参数。
QwenTtsRealtimeConfig config = QwenTtsRealtimeConfig.builder()
        .voice("Cherry")
        .responseFormat(ttsFormat)
        .mode("server_commit")
        .build();
qwenTtsRealtime.updateSession(config);
完整的 SDK 代码示例请参见Python SDKJava SDK。WebSocket 事件生命周期和连接复用说明请参见WebSocket API参考

进阶功能

指令控制

指令控制通过自然语言描述控制语音的音调、语速、情感和音色特点,无需调整复杂的音频参数。 各模型指令规格
  • Qwen-Audio-TTS
  • CosyVoice
  • Qwen-TTS
支持的模型qwen-audio-3.0-tts-plusqwen-audio-3.0-tts-flash系统音色和声音复刻音色:均可输入任意指令。
适用场景
  • 有声书和广播剧配音
  • 广告和宣传片配音
  • 游戏角色和动画配音
  • 情感化的智能语音助手
  • 纪录片和新闻播报
如何编写高质量的声音描述
  • 核心原则
    1. 具体而非模糊:使用描绘声音特质的词语,如“低沉”、“清脆”、“语速偏快”,避免“好听”、“普通”等主观或模糊的表述。
    2. 多维而非单一:好的描述通常涵盖多个维度(如性别、年龄、情感等)。仅写“女声”过于宽泛,难以生成有特色的音色。
    3. 客观而非主观:聚焦声音的物理和感知特征。例如,用”音调偏高,带有活力“代替”我最喜欢的声音”。
    4. 原创而非模仿:描述声音的特质,而非要求模仿特定人物(如名人、演员)。模型不支持模仿,且可能涉及版权风险。
    5. 简洁而非冗余:确保每个词都有明确作用,避免重复的同义词或无意义的修饰。
  • 描述维度参考 建议组合以下维度描述声音,维度越丰富,生成效果越精准。

    维度

    描述示例

    性别

    男性、女性、中性

    年龄

    儿童(5-12 岁)、青少年(13-18 岁)、青年(19-35 岁)、中年(36-55 岁)、老年(55 岁以上)

    音调

    高音、中音、低音、偏高、偏低

    语速

    快速、中速、缓慢、偏快、偏慢

    情感

    开朗、沉稳、温柔、严肃、活泼、冷静、治愈

    特点

    有磁性、清脆、沙哑、圆润、甜美、浑厚、有力

    用途

    新闻播报、广告配音、有声书、动画角色、语音助手、纪录片解说

  • 示例
    • 标准播音风格:吐字清晰精准,字正腔圆
    • 年轻活泼的女性声音,语速较快,带有明显的上扬语调,适合介绍时尚产品
    • 沉稳的中年男性,语速缓慢,音色低沉有磁性,适合朗读新闻或纪录片解说
    • 温柔知性的女性,30 岁左右,语调平和,适合有声书朗读
    • 可爱的儿童声音,大约 8 岁女孩,说话略带稚气,适合动画角色配音

方言

本节介绍如何让模型用中文方言(如河南话、四川话、粤语等)输出语音。不同模型和音色类型的设置方式不同。 各模型方言设置方式
  • Qwen-Audio-TTS
  • CosyVoice
  • Qwen-TTS
  • 系统音色:在Qwen-Audio-TTS音色列表中选择以下任一种音色:
    • 支持方言的系统音色,无需额外设置即可输出对应方言。
    • 支持指令控制且可指定方言的音色,通过指令文本指定方言。
  • 声音复刻音色:通过指令控制功能设置,例如指令文本写 请用河南话表达
具体支持哪些方言:参见Qwen-Audio-TTS中各模型“支持的语言”。

情感与富语言标签

Qwen-Audio-TTS 系列模型支持在待合成文本(text 参数)中直接嵌入情感与富语言标签,用于控制语音的情感表达或在指定位置插入拟声效果(如笑声、叹息等),无需调整复杂的音频参数即可生成更具表现力的语音。
支持的模型:仅 qwen-audio-3.0-tts-plusqwen-audio-3.0-tts-flash限制:仅支持单向流式模式。
控制类标签 控制类标签用于设定语音的情感或风格。将标签写在文本中,标签会作用于其后的所有文本,直到遇到下一个控制类标签,或因句子较长被自动切分为止。

标签

说明

[sad]

悲伤

[amazed]

惊叹

[deep and loud shouting]

深沉大声呐喊

[trembling]

颤抖

[angry]

愤怒

[excited]

兴奋

[sarcastic]

讽刺

[curious]

好奇

[like dracula]

德古拉风格(低沉、阴森)

[bored]

无聊

[tired]

疲惫

[scornful]

轻蔑

[shouting]

大喊

[asmr]

ASMR 轻柔耳语

[panicked]

恐慌

[mischievously]

调皮

[empathetic]

共情

[whispers]

耳语

[reluctantly]

不情愿

[crying]

哭泣

[serious]

严肃

[very slowly]

非常缓慢地说话

[very fast]

非常快速地说话

富语言类标签 富语言类标签用于在文本的当前位置插入一段拟声效果,不影响前后文本的情感风格。

标签

说明

[gasp]

倒吸一口气

[sighing]

叹息

[clears throat]

清嗓

[giggles]

咯咯笑

[laughing]

大笑

[cough]

咳嗽

[snorts]

哼声、嗤笑

使用示例 以下示例展示如何在 text 参数中组合使用控制类标签和富语言类标签: [excited]今天的天气真不错![laughing]我们一起出去玩吧! 上述文本中,[excited] 是控制类标签,作用于其后的所有文本,使语音带有兴奋的情感;[laughing] 是富语言类标签,在该位置插入一段笑声效果后继续合成后续文本。 您也可以在同一段文本中切换不同情感: [serious]请注意安全事项。[excited]好了,现在让我们开始吧! 其中 [serious] 控制第一句为严肃语气,[excited] 从第二句起切换为兴奋语气。

取消任务

在实时语音合成过程中,如果需要中断当前轮次合成,可以发送取消指令。取消后服务端会立即结束当前任务并返回结束事件,您可在当前 WebSocket 连接上继续发起新的合成任务,无需重新建立连接。 使用方式
  • Python SDK:1.26.4 及以上版本,调用 SpeechSynthesizer.streaming_cancel()
  • Java SDK:2.22.26 及以上版本,调用 SpeechSynthesizer.streamingCancel()
  • WebSocket 原始协议:发送 finish-task 事件,并在 input 中设置 directive=cancel
模型限制
  • 华北2(北京)地域:Qwen-Audio-TTS 系列模型的所有模型都支持该功能;CosyVoice 系列模型仅 v2 及以上版本支持该功能。
  • 新加坡地域:Qwen-Audio-TTS 系列模型的所有模型都支持该功能;CosyVoice 系列模型不支持该功能。

WebSocket 原始协议调用

以下示例展示如何通过 WebSocket 原始协议直连服务端,适用于不使用 DashScope SDK 的场景。此为最小可运行实现,WebSocket 协议请参见各模型的 API 参考。
  • Qwen-Audio-TTS/CosyVoice
  • Qwen-TTS
  • Sambert
Qwen-Audio-TTS 和 CosyVoice 使用相同的 WebSocket 协议,只需替换 modelvoice 参数。以下示例以 qwen-audio-3.0-tts-flash 为例,使用 CosyVoice 时将 model 替换为 cosyvoice-v3-flash 等,voice 替换为对应音色即可。
  • Go
  • C#
  • PHP
  • Node.js
  • Java
  • Python
package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"os"
	"strings"
	"time"

	"github.com/google/uuid"
	"github.com/gorilla/websocket"
)

const (
	// 以下为华北2(北京)地域的配置,调用时请将"{WorkspaceId}"替换为真实的业务空间ID,各地域的配置不同。
	wsURL      = "wss://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/api-ws/v1/inference"
	outputFile = "output.mp3"
)

func main() {
	// 新加坡和北京地域的API Key不同。获取API Key:https://help.aliyun.com/zh/model-studio/get-api-key
	// 若没有配置环境变量,请用阿里云百炼API Key将下行替换为:apiKey := "sk-xxx"
	apiKey := os.Getenv("DASHSCOPE_API_KEY")

	// 清空输出文件
	os.Remove(outputFile)
	os.Create(outputFile)

	// 连接WebSocket
	header := make(http.Header)
	header.Add("X-DashScope-DataInspection", "enable")
	header.Add("Authorization", fmt.Sprintf("bearer %s", apiKey))

	conn, resp, err := websocket.DefaultDialer.Dial(wsURL, header)
	if err != nil {
		if resp != nil {
			fmt.Printf("连接失败 HTTP状态码: %d\n", resp.StatusCode)
		}
		fmt.Println("连接失败:", err)
		return
	}
	defer conn.Close()

	// 生成任务ID
	taskID := uuid.New().String()
	fmt.Printf("生成任务ID: %s\n", taskID)

	// 发送run-task事件
	runTaskCmd := map[string]interface{}{
		"header": map[string]interface{}{
			"action":    "run-task",
			"task_id":   taskID,
			"streaming": "duplex",
		},
		"payload": map[string]interface{}{
			"task_group": "audio",
			"task":       "tts",
			"function":   "SpeechSynthesizer",
			"model":      "qwen-audio-3.0-tts-flash",
			"parameters": map[string]interface{}{
				"text_type":   "PlainText",
				"voice":       "longanhuan_v3.6",
				"format":      "mp3",
				"sample_rate": 22050,
				"volume":      50,
				"rate":        1,
				"pitch":       1,
				// 如果enable_ssml设为true,只允许发送一次continue-task事件,否则会报错“Text request limit violated, expected 1.”
				"enable_ssml": false,
			},
			"input": map[string]interface{}{},
		},
	}

	runTaskJSON, _ := json.Marshal(runTaskCmd)
	fmt.Printf("发送run-task事件: %s\n", string(runTaskJSON))

	err = conn.WriteMessage(websocket.TextMessage, runTaskJSON)
	if err != nil {
		fmt.Println("发送run-task失败:", err)
		return
	}

	textSent := false

	// 处理消息
	for {
		messageType, message, err := conn.ReadMessage()
		if err != nil {
			fmt.Println("读取消息失败:", err)
			break
		}

		// 处理二进制消息
		if messageType == websocket.BinaryMessage {
			fmt.Printf("收到二进制消息,长度: %d\n", len(message))
			file, _ := os.OpenFile(outputFile, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
			file.Write(message)
			file.Close()
			continue
		}

		// 处理文本消息
		messageStr := string(message)
		fmt.Printf("收到文本消息: %s\n", strings.ReplaceAll(messageStr, "\n", ""))

		// 简单解析JSON获取event类型
		var msgMap map[string]interface{}
		if json.Unmarshal(message, &msgMap) == nil {
			if header, ok := msgMap["header"].(map[string]interface{}); ok {
				if event, ok := header["event"].(string); ok {
					fmt.Printf("事件类型: %s\n", event)

					switch event {
					case "task-started":
						fmt.Println("=== 收到task-started事件 ===")

						if !textSent {
							// 发送continue-task事件

							texts := []string{"床前明月光,疑是地上霜。", "举头望明月,低头思故乡。"}

							for _, text := range texts {
								continueTaskCmd := map[string]interface{}{
									"header": map[string]interface{}{
										"action":    "continue-task",
										"task_id":   taskID,
										"streaming": "duplex",
									},
									"payload": map[string]interface{}{
										"input": map[string]interface{}{
											"text": text,
										},
									},
								}

								continueTaskJSON, _ := json.Marshal(continueTaskCmd)
								fmt.Printf("发送continue-task事件: %s\n", string(continueTaskJSON))

								err = conn.WriteMessage(websocket.TextMessage, continueTaskJSON)
								if err != nil {
									fmt.Println("发送continue-task失败:", err)
									return
								}
							}

							textSent = true

							// 延迟发送finish-task
							time.Sleep(500 * time.Millisecond)

							// 发送finish-task事件
							finishTaskCmd := map[string]interface{}{
								"header": map[string]interface{}{
									"action":    "finish-task",
									"task_id":   taskID,
									"streaming": "duplex",
								},
								"payload": map[string]interface{}{
									"input": map[string]interface{}{},
								},
							}

							finishTaskJSON, _ := json.Marshal(finishTaskCmd)
							fmt.Printf("发送finish-task事件: %s\n", string(finishTaskJSON))

							err = conn.WriteMessage(websocket.TextMessage, finishTaskJSON)
							if err != nil {
								fmt.Println("发送finish-task失败:", err)
								return
							}
						}

					case "task-finished":
						fmt.Println("=== 任务完成 ===")
						return

					case "task-failed":
						fmt.Println("=== 任务失败 ===")
						if header["error_message"] != nil {
							fmt.Printf("错误信息: %s\n", header["error_message"])
						}
						return

					case "result-generated":
						fmt.Println("收到result-generated事件")
					}
				}
			}
		}
	}
}

应用于生产环境

连接复用(WebSocket)

WebSocket 连接支持复用:一个合成任务结束后,无需重新建立连接即可开启下一个任务。 复用流程
  • Qwen-Audio-TTS / CosyVoice/ Sambert:客户端发送 finish-task,服务端返回 task-finished 后,可重新发送 run-task 开启新任务。
  • Qwen-TTS:客户端发送 session.finish,服务端返回 session.finished 后,可建立新会话开启下一个任务。
取消任务后复用:对于 Qwen-Audio-TTS / CosyVoice,如果使用 cancel 指令取消当前任务,服务端返回 task-finished 后,同样可以在当前连接上重新发送 run-task 开启新任务。详情请参见取消任务
  1. 必须等服务端返回结束事件(task-finishedsession.finished)后才可发起新任务。
  2. Qwen-Audio-TTS、CosyVoice 和 Sambert 在复用连接中的不同任务需要使用不同的 task_id
  3. 任务失败时服务端返回错误事件并关闭连接,该连接不可复用。
  4. 任务结束后 60 秒无新任务,连接自动断开。
各模型事件说明请参见对应的API参考

模型限流

模型调用受限流规则约束,超出限制时服务端返回 Requests rate limit exceeded, please try again later. 报错,需降低调用频率或并发数后重试。 各模型的限流条件请参见限流

高并发最佳实践

DashScope SDK 内置池化机制,可复用 WebSocket 连接和合成对象,避免频繁创建销毁带来的开销。
  • Qwen-Audio-TTS/CosyVoice
  • Sambert
Qwen-Audio-TTS 和 CosyVoice 使用相同的 SDK 接口,以下示例同样适用于 Qwen-Audio-TTS 系列模型,只需替换 modelvoice 参数。

前提条件

  • Python SDK
  • Java SDK
Python SDK 通过 SpeechSynthesizerObjectPool 管理和复用 SpeechSynthesizer 对象。对象池在初始化时即创建指定数量的 SpeechSynthesizer 实例并建立 WebSocket 连接,获取对象时可直接发起请求,降低首包延迟。归还后连接保持活跃,等待下次复用。

实现步骤

  1. 安装依赖:安装DashScope依赖(pip install -U dashscope
  2. 创建并配置对象池 对象池大小推荐设为峰值并发数的 1.5~2 倍,且不应超过账户的 QPS 限制。 创建全局单例对象池(初始化时建立连接,有一定耗时):
from dashscope.audio.tts_v2 import SpeechSynthesizerObjectPool

connectionPool = SpeechSynthesizerObjectPool(max_size=20)
import dashscope
# 以下为华北2(北京)地域的配置,调用时请将"{WorkspaceId}"替换为真实的业务空间ID,各地域的配置不同。
dashscope.base_http_api_url = "https://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/api/v1"
  • 在对象池场景中,SpeechSynthesizerObjectPool在初始化时即按当前全局dashscope.api_key与服务端建立 WebSocket 连接。apiKey 仅在 WebSocket 建连握手时写入Authorization请求头用于鉴权,后续任务消息(如run-task)本身不携带 apiKey。池创建后修改dashscope.api_key不会影响池内已建连接——borrow_synthesizer取出的对象(包括归还后再次复用的对象)仍使用握手时的 apiKey,新值会被静默忽略,可能导致身份、配额或计费归属与预期不一致。注意:borrow_synthesizer也不支持通过参数指定 apiKey。
  • 如确需使用多个不同的 API Key,请为每个 API Key 维护独立的SpeechSynthesizerObjectPool实例
  1. 从对象池中获取SpeechSynthesizer对象 如果当前未归还的对象数已超过池容量,系统会额外创建新对象。 此类对象需重新建立连接,不具备复用效果。
speech_synthesizer = connectionPool.borrow_synthesizer(
    model='cosyvoice-v3-flash',
    voice='longanyang',
    seed=12382,
    callback=synthesizer_callback
)
  1. 进行语音合成 调用SpeechSynthesizer对象的call或streaming_call方法进行语音合成。
  2. 归还SpeechSynthesizer对象 任务结束后归还对象以供复用。 不要归还未完成任务或任务失败的对象。
connectionPool.return_synthesizer(speech_synthesizer)
完整代码
复制使用前请注意:SpeechSynthesizerObjectPool在初始化时即按当前全局dashscope.api_key与服务端建立 WebSocket 连接并完成鉴权;池创建后再修改dashscope.api_key不会影响池内已建连接,新值会被静默忽略。多 API Key 场景请为每个 API Key 维护独立的池实例。详见上文重要说明。
# !/usr/bin/env python3
# Copyright (C) Alibaba Group. All Rights Reserved.
# MIT License (https://opensource.org/licenses/MIT)

import os
import time
import threading

import dashscope
from dashscope.audio.tts_v2 import *

USE_CONNECTION_POOL = True
text_to_synthesize = [
    '第一句、欢迎使用阿里巴巴语音合成服务。',
    '第二句、欢迎使用阿里巴巴语音合成服务。',
    '第三句、欢迎使用阿里巴巴语音合成服务。',
]
connectionPool = None

def init_dashscope_api_key():
    '''
    Set your DashScope API-key. More information:
    https://github.com/aliyun/alibabacloud-bailian-speech-demo/blob/master/PREREQUISITES.md
    '''
    # 新加坡和北京地域的API Key不同。获取API Key:https://help.aliyun.com/zh/model-studio/get-api-key
    if 'DASHSCOPE_API_KEY' in os.environ:
        dashscope.api_key = os.environ[
            'DASHSCOPE_API_KEY']  # load API-key from environment variable DASHSCOPE_API_KEY
    else:
        dashscope.api_key = '<your-dashscope-api-key>'  # set API-key manually

def synthesis_text_to_speech_and_play_by_streaming_mode(text, task_id):
    global USE_CONNECTION_POOL, connectionPool
    '''
    Synthesize speech with given text by streaming mode, async call and play the synthesized audio in real-time.
    for more information, please refer to https://help.aliyun.com/document_detail/2712523.html
    '''

    complete_event = threading.Event()

    # Define a callback to handle the result

    class Callback(ResultCallback):
        def on_open(self):
            # when using object pool, on_open will be called after task start
            self.file = open(f'result_{task_id}.mp3', 'wb')
            print(f'[task_{task_id}] start')

        def on_complete(self):
            print(f'[task_{task_id}] speech synthesis task complete successfully.')
            complete_event.set()

        def on_error(self, message: str):
            print(f'[task_{task_id}] speech synthesis task failed, {message}')

        def on_close(self):
            # when using object pool, on_close will be called after task finished
            print(f'[task_{task_id}] finished')

        def on_event(self, message):
            # print(f'recv speech synthsis message {message}')
            pass

        def on_data(self, data: bytes) -> None:
            # send to player
            # save audio to file
            self.file.write(data)

    # Call the speech synthesizer callback
    synthesizer_callback = Callback()

    # Initialize the speech synthesizer
    # you can customize the synthesis parameters, like voice, format, sample_rate or other parameters
    if USE_CONNECTION_POOL:
        speech_synthesizer = connectionPool.borrow_synthesizer(
            model='cosyvoice-v3-flash',
            voice='longanyang',
            seed=12382,
            callback=synthesizer_callback
        )
    else:
        speech_synthesizer = SpeechSynthesizer(model='cosyvoice-v3-flash',
                                               voice='longanyang',
                                               seed=12382,
                                               callback=synthesizer_callback)
    try:
        speech_synthesizer.call(text)
    except Exception as e:
        print(f'[task_{task_id}] speech synthesis task failed, {e}')
        if USE_CONNECTION_POOL:
            # close the synthesizer connection manually if task failed when using connection pool.
            speech_synthesizer.close()
        return

    print('[task_{}] Synthesized text: {}'.format(task_id, text))
    complete_event.wait()
    print('[task_{}][Metric] requestId: {}, first package delay ms: {}'.format(
        task_id,
        speech_synthesizer.get_last_request_id(),
        speech_synthesizer.get_first_package_delay()))
    if USE_CONNECTION_POOL:
        connectionPool.return_synthesizer(speech_synthesizer)

# main function
if __name__ == '__main__':
    # 必须先设置 dashscope.api_key 和 base_websocket_api_url,再创建 SpeechSynthesizerObjectPool。
    # 池在初始化时即按当前全局 dashscope.api_key 建立 WebSocket 连接,
    # 池创建后再修改 dashscope.api_key 不会影响池内已建连接。
    # 以下为华北2(北京)地域的配置,调用时请将"{WorkspaceId}"替换为真实的业务空间ID,各地域的配置不同。
    dashscope.base_websocket_api_url='wss://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/api-ws/v1/inference'
    init_dashscope_api_key()

    if USE_CONNECTION_POOL:
        print('creating connection pool')
        start_time = time.time() * 1000
        connectionPool = SpeechSynthesizerObjectPool(max_size=3)
        end_time = time.time() * 1000
        print('connection pool created, cost: {} ms'.format(end_time - start_time))

    task_thread_list = []
    for task_id in range(3):
        thread = threading.Thread(
            target=synthesis_text_to_speech_and_play_by_streaming_mode,
            args=(text_to_synthesize[task_id], task_id))
        task_thread_list.append(thread)

    for task_thread in task_thread_list:
        task_thread.start()

    for task_thread in task_thread_list:
        task_thread.join()

    if USE_CONNECTION_POOL:
        connectionPool.shutdown()

资源管理与异常处理

  • 任务成功:当语音合成任务正常完成时,必须调用 connectionPool.return_synthesizer(speech_synthesizer)SpeechSynthesizer 对象归还到池中,以便复用。
    不要归还未完成任务或任务失败的SpeechSynthesizer对象。
  • 任务失败:当 SDK 内部或业务逻辑抛出异常导致任务中断时,主动关闭底层的 WebSocket 连接:speech_synthesizer.close()
  • 在所有语音合成任务完成后,要通过如下方式关闭对象池:connectionPool.shutdown()
  • 在服务出现TaskFailed报错时,不需要额外处理。

支持的模型与地域

  • 华北2(北京)
  • 新加坡
调用以下模型时,请选择北京地域的API Key
  • Qwen-Audio-TTS:qwen-audio-3.0-tts-plus、qwen-audio-3.0-tts-flash
  • CosyVoice:cosyvoice-v3.5-plus、cosyvoice-v3.5-flash、cosyvoice-v3-plus、cosyvoice-v3-flash、cosyvoice-v2、cosyvoice-v1
  • Qwen-TTS
    • Qwen3-TTS-Instruct-Flash-Realtime:qwen3-tts-instruct-flash-realtime(稳定版,当前等同qwen3-tts-instruct-flash-realtime-2026-01-22)、qwen3-tts-instruct-flash-realtime-2026-01-22(最新快照版)
    • Qwen3-TTS-VD-Realtime:qwen3-tts-vd-realtime-2026-01-15(最新快照版)、qwen3-tts-vd-realtime-2025-12-16(快照版)
    • Qwen3-TTS-VC-Realtime:qwen3-tts-vc-realtime-2026-01-15(最新快照版)、qwen3-tts-vc-realtime-2025-11-27(快照版)
    • Qwen3-TTS-Flash-Realtime:qwen3-tts-flash-realtime(稳定版,当前等同qwen3-tts-flash-realtime-2025-11-27)、qwen3-tts-flash-realtime-2025-11-27(最新快照版)、qwen3-tts-flash-realtime-2025-09-18(快照版)
    • Qwen-TTS-Realtime:qwen-tts-realtime(稳定版,当前等同qwen-tts-realtime-2025-07-15)、qwen-tts-realtime-latest(最新版,当前等同qwen-tts-realtime-2025-07-15)、qwen-tts-realtime-2025-07-15(快照版)
  • Sambert:sambert-zhinan-v1、sambert-zhiqi-v1、sambert-zhichu-v1、sambert-zhide-v1、sambert-zhijia-v1、sambert-zhiru-v1、sambert-zhiqian-v1、sambert-zhixiang-v1、sambert-zhiwei-v1、sambert-zhihao-v1、sambert-zhijing-v1、sambert-zhiming-v1、sambert-zhimo-v1、sambert-zhina-v1、sambert-zhishu-v1、sambert-zhistella-v1、sambert-zhiting-v1、sambert-zhixiao-v1、sambert-zhiya-v1、sambert-zhiye-v1、sambert-zhiying-v1、sambert-zhiyuan-v1、sambert-zhiyue-v1、sambert-zhigui-v1、sambert-zhishuo-v1、sambert-zhimiao-emo-v1、sambert-zhimao-v1、sambert-zhilun-v1、sambert-zhifei-v1、sambert-zhida-v1、sambert-camila-v1、sambert-perla-v1、sambert-indah-v1、sambert-clara-v1、sambert-hanna-v1、sambert-beth-v1、sambert-betty-v1、sambert-cally-v1、sambert-cindy-v1、sambert-eva-v1、sambert-donna-v1、sambert-brian-v1、sambert-waan-v1,请参见Sambert模型列表

支持的音色

不同模型支持的音色不同。将请求参数 voice 设为音色列表中 voice参数 列的值即可。

API参考

常见问题

Q:语音合成发音错误怎么办?多音字如何控制发音?

  • 将多音字替换为同音的其他汉字,快速解决发音问题。
  • 使用 SSML 标记语言控制发音:Sambert 和 CosyVoice 均支持 SSML。

Q:使用复刻音色生成的音频无声音如何排查?

  1. 确认音色状态 调用CosyVoice声音复刻/设计API接口,确认音色的 status 是否为 OK
  2. 检查模型版本一致性 确保复刻音色时使用的 target_model 参数与语音合成时的 model 参数完全一致。例如:
    • 复刻时使用 cosyvoice-v3-plus
    • 合成时也必须使用 cosyvoice-v3-plus
  3. 验证源音频质量 检查复刻音色时使用的源音频是否符合CosyVoice声音复刻/设计API
    • 音频时长:10-20秒
    • 音质清晰
    • 无背景噪音
  4. 检查请求参数 确认语音合成请求中的 voice 参数已设置为复刻音色的 ID。

Q:声音复刻后合成效果不稳定或语音不完整怎么办?

如果复刻音色后合成的语音出现以下问题:
  • 语音播放不完整,只读出部分文字
  • 合成效果不稳定,时好时坏
  • 语音中包含异常停顿或静音段
可能原因:源音频质量不符合要求。 解决方案:请检查源音频是否符合录音操作指南中的音频要求,建议按照录音指南重新录制。

Q:为什么语音合成的实际时长与 WAV 文件显示的时长不一致?

语音合成采用流式机制,边合成边返回数据,因此保存的 WAV 文件头中的时长是预估值,存在一定误差。如需精确时长,可将 format 设置为 pcm,待获取完整合成结果后自行添加 WAV 文件头信息。

Q:为什么音频无法播放?

请按以下场景逐一排查:
  1. 音频保存为完整文件(如 xx.mp3)的情况
    1. 音频格式一致性:请求参数中的音频格式须与文件后缀一致(如参数为 wav 则文件须为 .wav)。
    2. 播放器兼容性:确认播放器支持该音频的格式和采样率。
  2. 流式播放音频的情况
    1. 将音频流保存为完整文件,尝试用播放器播放。如果文件无法播放,请参考场景 1 的排查方法。
    2. 如果文件可正常播放,则问题在流式播放实现。请确认播放器支持流式播放(如 ffmpeg、pyaudio、AudioFormat、MediaSource 等)。

Q:为什么音频播放卡顿?

请按以下步骤逐一排查:
  1. 检查文本发送速度:确保发送间隔合理,避免上段音频播完后下段文本尚未到达。
  2. 检查回调函数性能:
    • 确认回调函数中无阻塞性业务逻辑。
    • 回调运行在 WebSocket 线程,阻塞会影响数据接收。建议将音频数据写入独立缓冲区,在其他线程中处理。
  3. 检查网络稳定性:网络波动可能导致音频传输中断或延迟。

Q:语音合成耗时较长是什么原因?

请按以下步骤排查:
  1. 检查输入间隔 如果是流式合成,确认文本发送间隔是否过长,过长会导致合成总时长增加。
  2. 分析性能指标
    • 首包延迟:正常约 500ms。
    • RTF(实时率 = 合成总耗时 / 音频时长):正常应小于 1.0。

Q:合成的音频中读出了文本里的特殊符号怎么办?

Qwen-TTS 系列模型可能将文本中的部分特殊符号(如 Markdown 加粗标记 **)合成为语音。可通过以下方式处理:
  1. 调用前对文本进行预处理,去除特殊符号。
  2. 改用 CosyVoice 模型。

Q:如何限制 API Key 仅用于语音合成服务(权限隔离)?

通过新建业务空间并仅授权特定模型,可限制 API Key 的使用范围。请参见业务空间管理

Q:子业务空间的 API Key 能否调用 Qwen-Audio-TTS/CosyVoice 模型?

默认业务空间下,所有模型均可调用。 子业务空间下,需要为 API Key 对应的子业务空间进行模型授权。请参见子业务空间的模型调用
Token Plan
模型体验
模型调优
模型压缩目录节点
用量统计与性能监控
资产中心
服务支持