使用 SSML 控制语速、音高、音量等声音特征,使用 LaTeX 朗读中文数学公式。本文介绍支持范围、调用示例及标签与符号参考。
功能介绍
SSML 是基于 XML 的语音合成标记语言,可设置音色、语速、音高和音量等声音特征,控制停顿、指定发音与读法,并添加背景音乐和音效。CosyVoice 的 LaTeX 公式朗读功能可将文本中的数学公式转换为中文语音。
这两项功能可用于有声读物、客服信息播报、多语种发音和在线教育。各功能的适用模型、音色及接口见下文;模型选型请参见语音合成。
使用限制
SSML
-
支持模型:
qwen-audio-3.1-tts-flash、qwen-audio-3.0-tts-flash、qwen-audio-3.0-tts-plus、cosyvoice-v3.5-flash、cosyvoice-v3.5-plus、cosyvoice-v3-flash、cosyvoice-v3-plus、cosyvoice-v2 -
音色: 仅复刻音色,以及Qwen-Audio-TTS音色列表和CosyVoice音色列表中标注为支持 SSML 的系统音色可用。
-
接口:
- Java SDK(2.20.3 及以上版本):支持非流式调用和单向流式调用
- Python SDK(1.23.4 及以上版本):支持非流式调用和单向流式调用
- WebSocket API:需将参数
enable_ssml设置为true,且只允许发送一次 continue-task 事件 - HTTP API:需将参数
enable_ssml设置为true
LaTeX
-
支持模型:
cosyvoice-v3.5-plus、cosyvoice-v3.5-flash、cosyvoice-v3-plus、cosyvoice-v3-flash、cosyvoice-v2 -
语种:仅支持中文。
-
内容:公式必须在支持的标签与符号范围内。不支持 Markdown 数学公式代码块(例如标记为
math的代码块)。分隔符内仅放公式,混入非公式文本可能导致朗读不准确。
使用方式
SSML
使用 SSML 时,请遵循以下规则:
- 所有文本必须放在
<speak></speak>中。支持多个<speak>标签并列,不支持嵌套。 - 标签内的 XML 特殊字符需要转义:
"→"、'→'、&→&、<→<、>→>。
快速开始
以下示例展示如何使用 SSML 控制语速进行语音合成。运行前,请完成以下准备工作:
- 获取与配置 API Key
- 安装 DashScope SDK(Python 1.23.4 及以上版本,Java 2.20.3 及以上版本)。详情请参见安装SDK。
重要cosyvoice-v3.5-plus 和 cosyvoice-v3.5-flash 模型目前仅在北京地域可用,专用于声音复刻场景(不提供系统音色)。使用前,请先参见声音复刻创建目标音色。
Java SDK
import com.alibaba.dashscope.audio.ttsv2.SpeechSynthesisParam;
import com.alibaba.dashscope.audio.ttsv2.SpeechSynthesizer;
import com.alibaba.dashscope.utils.Constants;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
/**
* SSML功能说明:
* 1. 只有非流式调用和单向流式调用支持SSML功能
* 使用支持 SSML 的复刻音色或系统音色(如 cosyvoice-v3-flash 的 longanyang);适用模型见本页使用限制。
*/
public class Main {
private static String model = "cosyvoice-v3-flash";
private static String voice = "longanyang";
public static void main(String[] args) {
// 以下为华北2(北京)地域的URL,调用时请将WorkspaceId替换为真实的业务空间ID,各地域的URL不同。
Constants.baseWebsocketApiUrl = "wss://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/api-ws/v1/inference";
streamAudioDataToSpeaker();
System.exit(0);
}
public static void streamAudioDataToSpeaker() {
SpeechSynthesisParam param =
SpeechSynthesisParam.builder()
// 新加坡和北京地域的API Key不同。获取API Key:https://help.aliyun.com/zh/model-studio/get-api-key
// 若没有配置环境变量,请用百炼API Key将下行替换为:.apiKey("sk-xxx")
.apiKey(System.getenv("DASHSCOPE_API_KEY"))
.model(model)
.voice(voice)
.build();
SpeechSynthesizer synthesizer = new SpeechSynthesizer(param, null);
ByteBuffer audio = null;
try {
// 非流式调用,阻塞直至音频返回
// 特殊字符需要进行转义
audio = synthesizer.call("<speak rate=\"2\">我的语速比正常人快。</speak>");
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
// 任务结束关闭websocket连接
synthesizer.getDuplexApi().close(1000, "bye");
}
if (audio != null) {
// 将音频数据保存到本地文件“output.mp3”中
File file = new File("output.mp3");
try (FileOutputStream fos = new FileOutputStream(file)) {
fos.write(audio.array());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
// 首次发送文本时需建立 WebSocket 连接,因此首包延迟会包含连接建立的耗时
System.out.println(
"[Metric] requestId为:"
+ synthesizer.getLastRequestId()
+ "首包延迟(毫秒)为:"
+ synthesizer.getFirstPackageDelay());
}
}
import com.alibaba.dashscope.audio.tts.SpeechSynthesisResult;
import com.alibaba.dashscope.audio.ttsv2.SpeechSynthesisAudioFormat;
import com.alibaba.dashscope.audio.ttsv2.SpeechSynthesisParam;
import com.alibaba.dashscope.audio.ttsv2.SpeechSynthesizer;
import com.alibaba.dashscope.common.ResultCallback;
import com.alibaba.dashscope.utils.Constants;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
/**
* SSML功能说明:
* 1. 只有非流式调用和单向流式调用支持SSML功能
* 使用支持 SSML 的复刻音色或系统音色(如 cosyvoice-v3-flash 的 longanyang);适用模型见本页使用限制。
*/
public class Main {
private static String model = "cosyvoice-v3-flash";
private static String voice = "longanyang";
public static void main(String[] args) {
// 以下为华北2(北京)地域的URL,调用时请将WorkspaceId替换为真实的业务空间ID,各地域的URL不同。
Constants.baseWebsocketApiUrl = "wss://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/api-ws/v1/inference";
streamAudioDataToSpeaker();
System.out.println("音频已保存到 output.mp3 文件中");
System.exit(0);
}
public static void streamAudioDataToSpeaker() {
CountDownLatch latch = new CountDownLatch(1);
final FileOutputStream[] fileOutputStream = new FileOutputStream[1];
try {
fileOutputStream[0] = new FileOutputStream("output.mp3");
} catch (IOException e) {
System.err.println("无法创建输出文件: " + e.getMessage());
return;
}
// 实现回调接口ResultCallback
ResultCallback<SpeechSynthesisResult> callback = new ResultCallback<SpeechSynthesisResult>() {
@Override
public void onEvent(SpeechSynthesisResult result) {
if (result.getAudioFrame() != null) {
// 将音频数据写入本地文件
try {
byte[] audioData = result.getAudioFrame().array();
fileOutputStream[0].write(audioData);
fileOutputStream[0].flush();
} catch (IOException e) {
System.err.println("写入音频数据失败: " + e.getMessage());
}
}
}
@Override
public void onComplete() {
System.out.println("收到Complete,语音合成结束");
closeFileOutputStream(fileOutputStream[0]);
latch.countDown();
}
@Override
public void onError(Exception e) {
System.out.println("出现异常:" + e.toString());
closeFileOutputStream(fileOutputStream[0]);
latch.countDown();
}
};
SpeechSynthesisParam param =
SpeechSynthesisParam.builder()
// 新加坡和北京地域的API Key不同。获取API Key:https://help.aliyun.com/zh/model-studio/get-api-key
// 若没有配置环境变量,请用百炼API Key将下行替换为:.apiKey("sk-xxx")
.apiKey(System.getenv("DASHSCOPE_API_KEY"))
.model(model)
.voice(voice)
.format(SpeechSynthesisAudioFormat.MP3_22050HZ_MONO_256KBPS)
.build();
SpeechSynthesizer synthesizer = new SpeechSynthesizer(param, callback);
try {
// 单向流式调用,立即返回null(实际结果通过回调接口异步传递),在回调接口的onEvent方法中实时获取二进制音频
// 特殊字符需要进行转义
synthesizer.call("<speak rate=\"2\">我的语速比正常人快。</speak>");
// 等待合成完成
latch.await();
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
// 任务结束后关闭websocket连接
try {
synthesizer.getDuplexApi().close(1000, "bye");
} catch (Exception e) {
System.err.println("关闭WebSocket连接失败: " + e.getMessage());
}
// 确保文件流被关闭
closeFileOutputStream(fileOutputStream[0]);
}
// 首次发送文本时需建立 WebSocket 连接,因此首包延迟会包含连接建立的耗时
System.out.println(
"[Metric] requestId为:"
+ synthesizer.getLastRequestId()
+ ",首包延迟(毫秒)为:"
+ synthesizer.getFirstPackageDelay());
}
private static void closeFileOutputStream(FileOutputStream fileOutputStream) {
try {
if (fileOutputStream != null) {
fileOutputStream.close();
}
} catch (IOException e) {
System.err.println("关闭文件流失败: " + e.getMessage());
}
}
}
Python SDK
# coding=utf-8
# SSML功能说明:
# 1. 只有非流式调用和单向流式调用支持SSML功能
# 使用支持 SSML 的复刻音色或系统音色(如 cosyvoice-v3-flash 的 longanyang);适用模型见本页使用限制。
import dashscope
from dashscope.audio.tts_v2 import *
import os
# 新加坡和北京地域的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(北京)地域的URL,调用时请将WorkspaceId替换为真实的业务空间ID,各地域的URL不同。
dashscope.base_websocket_api_url='wss://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/api-ws/v1/inference'
# 模型
model = "cosyvoice-v3-flash"
# 音色
voice = "longanyang"
# 实例化SpeechSynthesizer,并在构造方法中传入模型(model)、音色(voice)等请求参数
synthesizer = SpeechSynthesizer(model=model, voice=voice)
# 非流式调用,阻塞直至音频返回
# 特殊字符需要进行转义
audio = synthesizer.call("<speak rate=\"2\">我的语速比正常人快。</speak>")
# 将音频保存至本地
with open('output.mp3', 'wb') as f:
f.write(audio)
# 首次发送文本时需建立 WebSocket 连接,因此首包延迟会包含连接建立的耗时
print('[Metric] requestId为:{},首包延迟为:{}毫秒'.format(
synthesizer.get_last_request_id(),
synthesizer.get_first_package_delay()))
# coding=utf-8
# SSML功能说明:
# 1. 只有非流式调用和单向流式调用支持SSML功能
# 使用支持 SSML 的复刻音色或系统音色(如 cosyvoice-v3-flash 的 longanyang);适用模型见本页使用限制。
import dashscope
from dashscope.audio.tts_v2 import *
import os
from datetime import datetime
def get_timestamp():
now = datetime.now()
formatted_timestamp = now.strftime("[%Y-%m-%d %H:%M:%S.%f]")
return formatted_timestamp
# 新加坡和北京地域的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(北京)地域的URL,调用时请将WorkspaceId替换为真实的业务空间ID,各地域的URL不同。
dashscope.base_websocket_api_url='wss://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/api-ws/v1/inference'
# 模型
model = "cosyvoice-v3-flash"
# 音色
voice = "longanyang"
# 定义回调接口
class Callback(ResultCallback):
_player = None
_stream = None
def on_open(self):
# 打开输出文件,准备写入音频数据
self.file = open("output.mp3", "wb")
print("连接建立:" + get_timestamp())
def on_complete(self):
print("语音合成完成,所有合成结果已被接收:" + get_timestamp())
if hasattr(self, 'file') and self.file:
self.file.close()
self
# 首次发送文本时需建立 WebSocket 连接,因此首包延迟会包含连接建立的耗时
print('[Metric] requestId为:{},首包延迟为:{}毫秒'.format(
self.synthesizer.get_last_request_id(),
self.synthesizer.get_first_package_delay()))
def on_error(self, message: str):
print(f"语音合成出现异常:{message}")
if hasattr(self, 'file') and self.file:
self.file.close()
def on_close(self):
print("连接关闭:" + get_timestamp())
if hasattr(self, 'file') and self.file:
self.file.close()
def on_event(self, message):
pass
def on_data(self, data: bytes) -> None:
print(get_timestamp() + " 二进制音频长度为:" + str(len(data)))
# 将音频数据写入文件
self.file.write(data)
callback = Callback()
# 实例化SpeechSynthesizer,并在构造方法中传入模型(model)、音色(voice)等请求参数
synthesizer = SpeechSynthesizer(
model=model,
voice=voice,
callback=callback,
)
# 将synthesizer实例赋值给callback,以便在on_complete中使用
callback.synthesizer = synthesizer
# 单向流式调用,发送待合成文本,在回调接口的on_data方法中实时获取二进制音频
# 特殊字符需要进行转义
synthesizer.call("<speak rate=\"2\">我的语速比正常人快。</speak>")
WebSocket API
Java
Java 开发建议使用 Java DashScope SDK,详情请参见Java SDK。
以下是Java WebSocket的调用示例。在运行示例前,请确保已导入以下依赖:
Java-WebSocketjackson-databind
建议使用 Maven 或 Gradle 管理依赖包,配置如下:
<dependencies>
<!-- WebSocket Client -->
<dependency>
<groupId>org.java-websocket</groupId>
<artifactId>Java-WebSocket</artifactId>
<version>1.5.3</version>
</dependency>
<!-- JSON Processing -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.0</version>
</dependency>
</dependencies>
// 省略其它代码
dependencies {
// WebSocket Client
implementation 'org.java-websocket:Java-WebSocket:1.5.3'
// JSON Processing
implementation 'com.fasterxml.jackson.core:jackson-databind:2.13.0'
}
// 省略其它代码
Java代码如下:
import com.fasterxml.jackson.databind.ObjectMapper;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URI;
import java.nio.ByteBuffer;
import java.util.*;
/**
* SSML功能说明:
* 1. 在发送run-task指令时,将参数enable_ssml设置为true,以开启SSML支持
* 2. 通过continue-task指令发送包含SSML的文本,且只允许发送一次continue-task指令
* 使用支持 SSML 的复刻音色或系统音色(如 cosyvoice-v3-flash 的 longanyang);适用模型见本页使用限制。
*/
public class TTSWebSocketClient extends WebSocketClient {
private final String taskId = UUID.randomUUID().toString();
private final String outputFile = "output_" + System.currentTimeMillis() + ".mp3";
private boolean taskFinished = false;
public TTSWebSocketClient(URI serverUri, Map<String, String> headers) {
super(serverUri, headers);
}
@Override
public void onOpen(ServerHandshake serverHandshake) {
System.out.println("连接成功");
// 发送run-task指令
// 如果enable_ssml设为true,只允许发送一次continue-task指令,否则会报错“Text request limit violated, expected 1.”
String runTaskCommand = "{ \"header\": { \"action\": \"run-task\", \"task_id\": \"" + taskId + "\", \"streaming\": \"duplex\" }, \"payload\": { \"task_group\": \"audio\", \"task\": \"tts\", \"function\": \"SpeechSynthesizer\", \"model\": \"cosyvoice-v3-flash\", \"parameters\": { \"text_type\": \"PlainText\", \"voice\": \"longanyang\", \"format\": \"mp3\", \"sample_rate\": 22050, \"volume\": 50, \"rate\": 1, \"pitch\": 1, \"enable_ssml\": true }, \"input\": {} }}";
send(runTaskCommand);
}
@Override
public void onMessage(String message) {
System.out.println("收到服务端返回的消息:" + message);
try {
// Parse JSON message
Map<String, Object> messageMap = new ObjectMapper().readValue(message, Map.class);
if (messageMap.containsKey("header")) {
Map<String, Object> header = (Map<String, Object>) messageMap.get("header");
if (header.containsKey("event")) {
String event = (String) header.get("event");
if ("task-started".equals(event)) {
System.out.println("收到服务端返回的task-started事件");
// 发送 continue-task 指令,使用SSML功能时,该指令只允许发送一次
// 特殊字符需要进行转义
sendContinueTask("<speak rate=\\\"2\\\">我的语速比正常人快。</speak>");
// 发送finish-task指令
sendFinishTask();
} else if ("task-finished".equals(event)) {
System.out.println("收到服务端返回的task-finished事件");
taskFinished = true;
closeConnection();
} else if ("task-failed".equals(event)) {
System.out.println("任务失败:" + message);
closeConnection();
}
}
}
} catch (Exception e) {
System.err.println("出现异常:" + e.getMessage());
}
}
@Override
public void onMessage(ByteBuffer message) {
System.out.println("收到的二进制音频数据大小为:" + message.remaining());
try (FileOutputStream fos = new FileOutputStream(outputFile, true)) {
byte[] buffer = new byte[message.remaining()];
message.get(buffer);
fos.write(buffer);
System.out.println("音频数据已写入本地文件" + outputFile + "中");
} catch (IOException e) {
System.err.println("音频数据写入本地文件失败:" + e.getMessage());
}
}
@Override
public void onClose(int code, String reason, boolean remote) {
System.out.println("连接关闭:" + reason + " (" + code + ")");
}
@Override
public void onError(Exception ex) {
System.err.println("报错:" + ex.getMessage());
ex.printStackTrace();
}
private void sendContinueTask(String text) {
String command = "{ \"header\": { \"action\": \"continue-task\", \"task_id\": \"" + taskId + "\", \"streaming\": \"duplex\" }, \"payload\": { \"input\": { \"text\": \"" + text + "\" } }}";
send(command);
}
private void sendFinishTask() {
String command = "{ \"header\": { \"action\": \"finish-task\", \"task_id\": \"" + taskId + "\", \"streaming\": \"duplex\" }, \"payload\": { \"input\": {} }}";
send(command);
}
private void closeConnection() {
if (!isClosed()) {
close();
}
}
public static void main(String[] args) {
try {
// 新加坡和北京地域的API Key不同。获取API Key:https://help.aliyun.com/zh/model-studio/get-api-key
// 若没有配置环境变量,请用百炼API Key将下行替换为:String apiKey = "sk-xxx"
String apiKey = System.getenv("DASHSCOPE_API_KEY");
if (apiKey == null || apiKey.isEmpty()) {
System.err.println("请设置 DASHSCOPE_API_KEY 环境变量");
return;
}
Map<String, String> headers = new HashMap<>();
headers.put("Authorization", "bearer " + apiKey);
// 以下为华北2(北京)地域的URL,调用时请将WorkspaceId替换为真实的业务空间ID,各地域的URL不同。
TTSWebSocketClient client = new TTSWebSocketClient(new URI("wss://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/api-ws/v1/inference/"), headers);
client.connect();
while (!client.isClosed() && !client.taskFinished) {
Thread.sleep(1000);
}
} catch (Exception e) {
System.err.println("连接WebSocket服务失败:" + e.getMessage());
e.printStackTrace();
}
}
}
Python
Python 开发建议使用 Python DashScope SDK,详情请参见Python SDK。
以下是Python WebSocket的调用示例。在运行示例前,请确保通过如下方式导入依赖:
pip uninstall websocket-client
pip uninstall websocket
pip install websocket-client
重要运行示例代码的 Python 文件不能命名为 websocket.py,否则会报错(AttributeError: module 'websocket' has no attribute 'WebSocketApp'. Did you mean: 'WebSocket'?)。
# SSML功能说明:
# 1. 在发送run-task指令时,将参数enable_ssml设置为true,以开启SSML支持
# 2. 通过continue-task指令发送包含SSML的文本,且只允许发送一次continue-task指令
# 使用支持 SSML 的复刻音色或系统音色(如 cosyvoice-v3-flash 的 longanyang);适用模型见本页使用限制。
import websocket
import json
import uuid
import os
import time
class TTSClient:
def __init__(self, api_key, uri):
"""
初始化 TTSClient 实例
参数:
api_key (str): 鉴权用的 API Key
uri (str): WebSocket 服务地址
"""
self.api_key = api_key # 替换为你的 API Key
self.uri = uri # 替换为你的 WebSocket 地址
self.task_id = str(uuid.uuid4()) # 生成唯一任务 ID
self.output_file = f"output_{int(time.time())}.mp3" # 输出音频文件路径
self.ws = None # WebSocketApp 实例
self.task_started = False # 是否收到 task-started
self.task_finished = False # 是否收到 task-finished / task-failed
def on_open(self, ws):
"""
WebSocket 连接建立时回调函数
发送 run-task 指令开启语音合成任务
"""
print("WebSocket 已连接")
# 构造 run-task 指令
run_task_cmd = {
"header": {
"action": "run-task",
"task_id": self.task_id,
"streaming": "duplex"
},
"payload": {
"task_group": "audio",
"task": "tts",
"function": "SpeechSynthesizer",
"model": "cosyvoice-v3-flash",
"parameters": {
"text_type": "PlainText",
"voice": "longanyang",
"format": "mp3",
"sample_rate": 22050,
"volume": 50,
"rate": 1,
"pitch": 1,
# 如果enable_ssml设为True,只允许发送一次continue-task指令,否则会报错“Text request limit violated, expected 1.”
"enable_ssml": True
},
"input": {}
}
}
# 发送 run-task 指令
ws.send(json.dumps(run_task_cmd))
print("已发送 run-task 指令")
def on_message(self, ws, message):
"""
接收到消息时的回调函数
区分文本和二进制消息处理
"""
if isinstance(message, str):
# 处理 JSON 文本消息
try:
msg_json = json.loads(message)
print(f"收到 JSON 消息: {msg_json}")
if "header" in msg_json:
header = msg_json["header"]
if "event" in header:
event = header["event"]
if event == "task-started":
print("任务已启动")
self.task_started = True
# 发送 continue-task 指令,使用SSML功能时,该指令只允许发送一次
# 特殊字符需要进行转义
self.send_continue_task("<speak rate=\"2\">我的语速比正常人快。</speak>")
# continue-task 发送完成后发送 finish-task
self.send_finish_task()
elif event == "task-finished":
print("任务已完成")
self.task_finished = True
self.close(ws)
elif event == "task-failed":
error_msg = msg_json.get("error_message", "未知错误")
print(f"任务失败: {error_msg}")
self.task_finished = True
self.close(ws)
except json.JSONDecodeError as e:
print(f"JSON 解析失败: {e}")
else:
# 处理二进制消息(音频数据)
print(f"收到二进制消息,大小: {len(message)} 字节")
with open(self.output_file, "ab") as f:
f.write(message)
print(f"已将音频数据写入本地文件{self.output_file}中")
def on_error(self, ws, error):
"""发生错误时的回调"""
print(f"WebSocket 出错: {error}")
def on_close(self, ws, close_status_code, close_msg):
"""连接关闭时的回调"""
print(f"WebSocket 已关闭: {close_msg} ({close_status_code})")
def send_continue_task(self, text):
"""发送 continue-task 指令,附带要合成的文本内容"""
cmd = {
"header": {
"action": "continue-task",
"task_id": self.task_id,
"streaming": "duplex"
},
"payload": {
"input": {
"text": text
}
}
}
self.ws.send(json.dumps(cmd))
print(f"已发送 continue-task 指令,文本内容: {text}")
def send_finish_task(self):
"""发送 finish-task 指令,结束语音合成任务"""
cmd = {
"header": {
"action": "finish-task",
"task_id": self.task_id,
"streaming": "duplex"
},
"payload": {
"input": {}
}
}
self.ws.send(json.dumps(cmd))
print("已发送 finish-task 指令")
def close(self, ws):
"""主动关闭连接"""
if ws and ws.sock and ws.sock.connected:
ws.close()
print("已主动关闭连接")
def run(self):
"""启动 WebSocket 客户端"""
# 设置请求头部(鉴权)
header = {
"Authorization": f"bearer {self.api_key}",
"X-DashScope-DataInspection": "enable"
}
# 创建 WebSocketApp 实例
self.ws = websocket.WebSocketApp(
self.uri,
header=header,
on_open=self.on_open,
on_message=self.on_message,
on_error=self.on_error,
on_close=self.on_close
)
print("正在监听 WebSocket 消息...")
self.ws.run_forever() # 启动长连接监听
# 示例使用方式
if __name__ == "__main__":
# 新加坡和北京地域的API Key不同。获取API Key:https://help.aliyun.com/zh/model-studio/get-api-key
# 若没有配置环境变量,请用百炼API Key将下行替换为:API_KEY = "sk-xxx"
API_KEY = os.environ.get("DASHSCOPE_API_KEY")
# 以下为华北2(北京)地域的URL,调用时请将WorkspaceId替换为真实的业务空间ID,各地域的URL不同。
SERVER_URI = "wss://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/api-ws/v1/inference/"
client = TTSClient(API_KEY, SERVER_URI)
client.run()
Go
// SSML功能说明:
// 1. 在发送run-task指令时,将参数enable_ssml设置为true,以开启SSML支持
// 2. 通过continue-task指令发送包含SSML的文本,且只允许发送一次continue-task指令
// 使用支持 SSML 的复刻音色或系统音色(如 cosyvoice-v3-flash 的 longanyang);适用模型见本页使用限制。
package main
import (
"encoding/json"
"fmt"
"net/http"
"os"
"strings"
"time"
"github.com/google/uuid"
"github.com/gorilla/websocket"
)
const (
// 以下为华北2(北京)地域的URL,调用时请将WorkspaceId替换为真实的业务空间ID,各地域的URL不同。
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": "cosyvoice-v3-flash",
"parameters": map[string]interface{}{
"text_type": "PlainText",
"voice": "longanyang",
"format": "mp3",
"sample_rate": 22050,
"volume": 50,
"rate": 1,
"pitch": 1,
// 如果enable_ssml设为true,只允许发送一次continue-task指令,否则会报错“Text request limit violated, expected 1.”
"enable_ssml": true,
},
"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 指令,使用SSML功能时,该指令只允许发送一次
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": "<speak rate=\"2\">我的语速比正常人快。</speak>",
},
},
}
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事件")
}
}
}
}
}
}
C#
using System.Net.WebSockets;
using System.Text;
using System.Text.Json;
// SSML功能说明:
// 1. 在发送run-task指令时,将参数enable_ssml设置为true,以开启SSML支持
// 2. 通过continue-task指令发送包含SSML的文本,且只允许发送一次continue-task指令
// 使用支持 SSML 的复刻音色或系统音色(如 cosyvoice-v3-flash 的 longanyang);适用模型见本页使用限制。
class Program {
// 新加坡和北京地域的API Key不同。获取API Key:https://help.aliyun.com/zh/model-studio/get-api-key
// 若没有配置环境变量,请用百炼API Key将下行替换为:private static readonly string ApiKey = "sk-xxx"
private static readonly string ApiKey = Environment.GetEnvironmentVariable("DASHSCOPE_API_KEY") ?? throw new InvalidOperationException("DASHSCOPE_API_KEY environment variable is not set.");
// 以下为华北2(北京)地域的URL,调用时请将WorkspaceId替换为真实的业务空间ID,各地域的URL不同。
private const string WebSocketUrl = "wss://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/api-ws/v1/inference/";
// 输出文件路径
private const string OutputFilePath = "output.mp3";
// WebSocket客户端
private static ClientWebSocket _webSocket = new ClientWebSocket();
// 取消令牌源
private static CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
// 任务ID
private static string? _taskId;
// 任务是否已启动
private static TaskCompletionSource<bool> _taskStartedTcs = new TaskCompletionSource<bool>();
static async Task Main(string[] args) {
try {
// 清空输出文件
ClearOutputFile(OutputFilePath);
// 连接WebSocket服务
await ConnectToWebSocketAsync(WebSocketUrl);
// 启动接收消息的任务
Task receiveTask = ReceiveMessagesAsync();
// 发送run-task指令
_taskId = GenerateTaskId();
await SendRunTaskCommandAsync(_taskId);
// 等待task-started事件
await _taskStartedTcs.Task;
// 发送 continue-task 指令,使用SSML功能时,该指令只允许发送一次
// 特殊字符需要进行转义
await SendContinueTaskCommandAsync("<speak rate=\"2\">我的语速比正常人快。</speak>");
// 发送finish-task指令
await SendFinishTaskCommandAsync(_taskId);
// 等待接收任务完成
await receiveTask;
Console.WriteLine("任务完成,连接已关闭。");
} catch (OperationCanceledException) {
Console.WriteLine("任务被取消。");
} catch (Exception ex) {
Console.WriteLine($"发生错误:{ex.Message}");
} finally {
_cancellationTokenSource.Cancel();
_webSocket.Dispose();
}
}
private static void ClearOutputFile(string filePath) {
if (File.Exists(filePath)) {
File.WriteAllText(filePath, string.Empty);
Console.WriteLine("输出文件已清空。");
} else {
Console.WriteLine("输出文件不存在,无需清空。");
}
}
private static async Task ConnectToWebSocketAsync(string url) {
var uri = new Uri(url);
if (_webSocket.State == WebSocketState.Connecting || _webSocket.State == WebSocketState.Open) {
return;
}
// 设置WebSocket连接的头部信息
_webSocket.Options.SetRequestHeader("Authorization", $"bearer {ApiKey}");
_webSocket.Options.SetRequestHeader("X-DashScope-DataInspection", "enable");
try {
await _webSocket.ConnectAsync(uri, _cancellationTokenSource.Token);
Console.WriteLine("已成功连接到WebSocket服务。");
} catch (OperationCanceledException) {
Console.WriteLine("WebSocket连接被取消。");
} catch (Exception ex) {
Console.WriteLine($"WebSocket连接失败: {ex.Message}");
throw;
}
}
private static async Task SendRunTaskCommandAsync(string taskId) {
var command = CreateCommand("run-task", taskId, "duplex", new {
task_group = "audio",
task = "tts",
function = "SpeechSynthesizer",
model = "cosyvoice-v3-flash",
parameters = new
{
text_type = "PlainText",
voice = "longanyang",
format = "mp3",
sample_rate = 22050,
volume = 50,
rate = 1,
pitch = 1,
// 如果enable_ssml设为true,只允许发送一次continue-task指令,否则会报错“Text request limit violated, expected 1.”
enable_ssml = true
},
input = new { }
});
await SendJsonMessageAsync(command);
Console.WriteLine("已发送run-task指令。");
}
private static async Task SendContinueTaskCommandAsync(string text) {
if (_taskId == null) {
throw new InvalidOperationException("任务ID未初始化。");
}
var command = CreateCommand("continue-task", _taskId, "duplex", new {
input = new {
text
}
});
await SendJsonMessageAsync(command);
Console.WriteLine("已发送continue-task指令。");
}
private static async Task SendFinishTaskCommandAsync(string taskId) {
var command = CreateCommand("finish-task", taskId, "duplex", new {
input = new { }
});
await SendJsonMessageAsync(command);
Console.WriteLine("已发送finish-task指令。");
}
private static async Task SendJsonMessageAsync(string message) {
var buffer = Encoding.UTF8.GetBytes(message);
try {
await _webSocket.SendAsync(new ArraySegment<byte>(buffer), WebSocketMessageType.Text, true, _cancellationTokenSource.Token);
} catch (OperationCanceledException) {
Console.WriteLine("消息发送被取消。");
}
}
private static async Task ReceiveMessagesAsync() {
while (_webSocket.State == WebSocketState.Open) {
var response = await ReceiveMessageAsync();
if (response != null) {
var eventStr = response.RootElement.GetProperty("header").GetProperty("event").GetString();
switch (eventStr) {
case "task-started":
Console.WriteLine("任务已启动。");
_taskStartedTcs.TrySetResult(true);
break;
case "task-finished":
Console.WriteLine("任务已完成。");
_cancellationTokenSource.Cancel();
break;
case "task-failed":
Console.WriteLine("任务失败:" + response.RootElement.GetProperty("header").GetProperty("error_message").GetString());
_cancellationTokenSource.Cancel();
break;
default:
// result-generated可在此处理
break;
}
}
}
}
private static async Task<JsonDocument?> ReceiveMessageAsync() {
var buffer = new byte[1024 * 4];
var segment = new ArraySegment<byte>(buffer);
try {
WebSocketReceiveResult result = await _webSocket.ReceiveAsync(segment, _cancellationTokenSource.Token);
if (result.MessageType == WebSocketMessageType.Close) {
await _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", _cancellationTokenSource.Token);
return null;
}
if (result.MessageType == WebSocketMessageType.Binary) {
// 处理二进制数据
Console.WriteLine("接收到二进制数据...");
// 将二进制数据保存到文件
using (var fileStream = new FileStream(OutputFilePath, FileMode.Append)) {
fileStream.Write(buffer, 0, result.Count);
}
return null;
}
string message = Encoding.UTF8.GetString(buffer, 0, result.Count);
return JsonDocument.Parse(message);
} catch (OperationCanceledException) {
Console.WriteLine("消息接收被取消。");
return null;
}
}
private static string GenerateTaskId() {
return Guid.NewGuid().ToString("N").Substring(0, 32);
}
private static string CreateCommand(string action, string taskId, string streaming, object payload) {
var command = new {
header = new {
action,
task_id = taskId,
streaming
},
payload
};
return JsonSerializer.Serialize(command);
}
}
PHP
示例代码目录结构为:
my-php-project/
├── composer.json
├── vendor/
└── index.php
composer.json内容如下,相关依赖的版本号请根据实际情况自行决定:
{
"require": {
"react/event-loop": "^1.3",
"react/socket": "^1.11",
"react/stream": "^1.2",
"react/http": "^1.1",
"ratchet/pawl": "^0.4"
},
"autoload": {
"psr-4": {
"App\\": "src/"
}
}
}
index.php内容如下:
<!-- SSML功能说明: -->
<!-- 1. 在发送run-task指令时,将参数enable_ssml设置为true,以开启SSML支持 -->
<!-- 2. 通过continue-task指令发送包含SSML的文本,且只允许发送一次continue-task指令 -->
<!-- 3. 只有qwen-audio-3.0-tts-flash、qwen-audio-3.0-tts-plus、cosyvoice-v3-flash、cosyvoice-v3-plus和cosyvoice-v2模型的复刻音色以及音色列表中标记为支持SSML的系统音色支持SSML功能(例如cosyvoice-v3-flash模型的longanyang音色) -->
<?php
require __DIR__ . '/vendor/autoload.php';
use Ratchet\Client\Connector;
use React\EventLoop\Loop;
use React\Socket\Connector as SocketConnector;
// 新加坡和北京地域的API Key不同。获取API Key:https://help.aliyun.com/zh/model-studio/get-api-key
// 若没有配置环境变量,请用百炼API Key将下行替换为:$api_key = "sk-xxx"
$api_key = getenv("DASHSCOPE_API_KEY");
// 以下为华北2(北京)地域的URL,调用时请将WorkspaceId替换为真实的业务空间ID,各地域的URL不同。
$websocket_url = 'wss://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/api-ws/v1/inference/'; // WebSocket服务器地址
$output_file = 'output.mp3'; // 输出文件路径
$loop = Loop::get();
if (file_exists($output_file)) {
// 清空文件内容
file_put_contents($output_file, '');
}
// 创建自定义的连接器
$socketConnector = new SocketConnector($loop, [
'tcp' => [
'bindto' => '0.0.0.0:0',
],
'tls' => [
'verify_peer' => false,
'verify_peer_name' => false,
],
]);
$connector = new Connector($loop, $socketConnector);
$headers = [
'Authorization' => 'bearer ' . $api_key,
'X-DashScope-DataInspection' => 'enable'
];
$connector($websocket_url, [], $headers)->then(function ($conn) use ($loop, $output_file) {
echo "连接到WebSocket服务器\n";
// 生成任务ID
$taskId = generateTaskId();
// 发送 run-task 指令
sendRunTaskMessage($conn, $taskId);
// 定义发送 continue-task 指令的函数
$sendContinueTask = function() use ($conn, $loop, $taskId) {
// 发送 continue-task 指令,使用SSML功能时,该指令只允许发送一次
$continueTaskMessage = json_encode([
"header" => [
"action" => "continue-task",
"task_id" => $taskId,
"streaming" => "duplex"
],
"payload" => [
"input" => [
// 特殊字符需要进行转义
"text" => "<speak rate=\"2\">我的语速比正常人快。</speak>"
]
]
]);
$conn->send($continueTaskMessage);
// 发送 finish-task 指令
sendFinishTaskMessage($conn, $taskId);
};
// 标记是否收到 task-started 事件
$taskStarted = false;
// 监听消息
$conn->on('message', function($msg) use ($conn, $sendContinueTask, $loop, &$taskStarted, $taskId, $output_file) {
if ($msg->isBinary()) {
// 写入二进制数据到本地文件
file_put_contents($output_file, $msg->getPayload(), FILE_APPEND);
} else {
// 处理非二进制消息
$response = json_decode($msg, true);
if (isset($response['header']['event'])) {
handleEvent($conn, $response, $sendContinueTask, $loop, $taskId, $taskStarted);
} else {
echo "未知的消息格式\n";
}
}
});
// 监听连接关闭
$conn->on('close', function($code = null, $reason = null) {
echo "连接已关闭\n";
if ($code !== null) {
echo "关闭代码: " . $code . "\n";
}
if ($reason !== null) {
echo "关闭原因:" . $reason . "\n";
}
});
}, function ($e) {
echo "无法连接:{$e->getMessage()}\n";
});
$loop->run();
/**
* 生成任务ID
* @return string
*/
function generateTaskId(): string {
return bin2hex(random_bytes(16));
}
/**
* 发送 run-task 指令
* @param $conn
* @param $taskId
*/
function sendRunTaskMessage($conn, $taskId) {
$runTaskMessage = json_encode([
"header" => [
"action" => "run-task",
"task_id" => $taskId,
"streaming" => "duplex"
],
"payload" => [
"task_group" => "audio",
"task" => "tts",
"function" => "SpeechSynthesizer",
"model" => "cosyvoice-v3-flash",
"parameters" => [
"text_type" => "PlainText",
"voice" => "longanyang",
"format" => "mp3",
"sample_rate" => 22050,
"volume" => 50,
"rate" => 1,
"pitch" => 1,
// 如果enable_ssml设为true,只允许发送一次continue-task指令,否则会报错“Text request limit violated, expected 1.”
"enable_ssml" => true
],
"input" => (object) []
]
]);
echo "准备发送run-task指令: " . $runTaskMessage . "\n";
$conn->send($runTaskMessage);
echo "run-task指令已发送\n";
}
/**
* 读取音频文件
* @param string $filePath
* @return bool|string
*/
function readAudioFile(string $filePath) {
$voiceData = file_get_contents($filePath);
if ($voiceData === false) {
echo "无法读取音频文件\n";
}
return $voiceData;
}
/**
* 分割音频数据
* @param string $data
* @param int $chunkSize
* @return array
*/
function splitAudioData(string $data, int $chunkSize): array {
return str_split($data, $chunkSize);
}
/**
* 发送 finish-task 指令
* @param $conn
* @param $taskId
*/
function sendFinishTaskMessage($conn, $taskId) {
$finishTaskMessage = json_encode([
"header" => [
"action" => "finish-task",
"task_id" => $taskId,
"streaming" => "duplex"
],
"payload" => [
"input" => (object) []
]
]);
echo "准备发送finish-task指令: " . $finishTaskMessage . "\n";
$conn->send($finishTaskMessage);
echo "finish-task指令已发送\n";
}
/**
* 处理事件
* @param $conn
* @param $response
* @param $sendContinueTask
* @param $loop
* @param $taskId
* @param $taskStarted
*/
function handleEvent($conn, $response, $sendContinueTask, $loop, $taskId, &$taskStarted) {
switch ($response['header']['event']) {
case 'task-started':
echo "任务开始,发送continue-task指令...\n";
$taskStarted = true;
// 发送 continue-task 指令
$sendContinueTask();
break;
case 'result-generated':
// 忽略result-generated事件
break;
case 'task-finished':
echo "任务完成\n";
$conn->close();
break;
case 'task-failed':
echo "任务失败\n";
echo "错误代码:" . $response['header']['error_code'] . "\n";
echo "错误信息:" . $response['header']['error_message'] . "\n";
$conn->close();
break;
case 'error':
echo "错误:" . $response['payload']['message'] . "\n";
break;
default:
echo "未知事件:" . $response['header']['event'] . "\n";
break;
}
// 如果任务已完成,关闭连接
if ($response['header']['event'] == 'task-finished') {
// 等待1秒以确保所有数据都已传输完毕
$loop->addTimer(1, function() use ($conn) {
$conn->close();
echo "客户端关闭连接\n";
});
}
// 如果没有收到 task-started 事件,关闭连接
if (!$taskStarted && in_array($response['header']['event'], ['task-failed', 'error'])) {
$conn->close();
}
}
Node.js
需安装相关依赖:
npm install ws
npm install uuid
示例代码如下:
// SSML功能说明:
// 1. 在发送run-task指令时,将参数enable_ssml设置为true,以开启SSML支持
// 2. 通过continue-task指令发送包含SSML的文本,且只允许发送一次continue-task指令
// 使用支持 SSML 的复刻音色或系统音色(如 cosyvoice-v3-flash 的 longanyang);适用模型见本页使用限制。
import fs from 'fs';
import WebSocket from 'ws';
import { v4 as uuid } from 'uuid'; // 用于生成UUID
// 新加坡和北京地域的API Key不同。获取API Key:https://help.aliyun.com/zh/model-studio/get-api-key
// 若没有配置环境变量,请用百炼API Key将下行替换为:const apiKey = "sk-xxx"
const apiKey = process.env.DASHSCOPE_API_KEY;
// 以下为华北2(北京)地域的URL,调用时请将WorkspaceId替换为真实的业务空间ID,各地域的URL不同。
const url = 'wss://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/api-ws/v1/inference/';
// 输出文件路径
const outputFilePath = 'output.mp3';
// 清空输出文件
fs.writeFileSync(outputFilePath, '');
// 创建WebSocket客户端
const ws = new WebSocket(url, {
headers: {
Authorization: `bearer ${apiKey}`,
'X-DashScope-DataInspection': 'enable'
}
});
let taskStarted = false;
let taskId = uuid();
ws.on('open', () => {
console.log('已连接到WebSocket服务器');
// 发送run-task指令
const runTaskMessage = JSON.stringify({
header: {
action: 'run-task',
task_id: taskId,
streaming: 'duplex'
},
payload: {
task_group: 'audio',
task: 'tts',
function: 'SpeechSynthesizer',
model: 'cosyvoice-v3-flash',
parameters: {
text_type: 'PlainText',
voice: 'longanyang', // 音色
format: 'mp3', // 音频格式
sample_rate: 22050, // 采样率
volume: 50, // 音量
rate: 1, // 语速
pitch: 1, // 音调
enable_ssml: true // 是否开启SSML功能。如果enable_ssml设为true,只允许发送一次continue-task指令,否则会报错“Text request limit violated, expected 1.”
},
input: {}
}
});
ws.send(runTaskMessage);
console.log('已发送run-task消息');
});
const fileStream = fs.createWriteStream(outputFilePath, { flags: 'a' });
ws.on('message', (data, isBinary) => {
if (isBinary) {
// 写入二进制数据到文件
fileStream.write(data);
} else {
const message = JSON.parse(data);
switch (message.header.event) {
case 'task-started':
taskStarted = true;
console.log('任务已开始');
// 发送continue-task指令
sendContinueTasks(ws);
break;
case 'task-finished':
console.log('任务已完成');
ws.close();
fileStream.end(() => {
console.log('文件流已关闭');
});
break;
case 'task-failed':
console.error('任务失败:', message.header.error_message);
ws.close();
fileStream.end(() => {
console.log('文件流已关闭');
});
break;
default:
// 可以在这里处理result-generated
break;
}
}
});
function sendContinueTasks(ws) {
if (taskStarted) {
// 发送 continue-task 指令,使用SSML功能时,该指令只允许发送一次
const continueTaskMessage = JSON.stringify({
header: {
action: 'continue-task',
task_id: taskId,
streaming: 'duplex'
},
payload: {
input: {
// 特殊字符需要进行转义
text: '<speak rate="2">我的语速比正常人快。</speak>'
}
}
});
ws.send(continueTaskMessage);
// 发送finish-task指令
const finishTaskMessage = JSON.stringify({
header: {
action: 'finish-task',
task_id: taskId,
streaming: 'duplex'
},
payload: {
input: {}
}
});
ws.send(finishTaskMessage);
}
}
ws.on('close', () => {
console.log('已断开与WebSocket服务器的连接');
});
HTTP API
curl -X POST https://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/api/v1/services/audio/tts/SpeechSynthesizer \
-H "Authorization: Bearer $DASHSCOPE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "cosyvoice-v3-flash",
"input": {
"text": "<speak rate=\"2\">我的语速比正常人快。</speak>",
"voice": "longanyang",
"format": "mp3",
"sample_rate": 24000,
"enable_ssml": true
}
}'
LaTeX
使用方法
将数学公式用分隔符包裹后,调用语音合成 API。
支持的分隔符(任选一种,效果相同):
$...$$$...$$\(...\)\[...\]
示例输入:
这是一道分数:$\frac{1}{2}$。
上述分数朗读为“二分之一”。
包含多个运算符的公式也使用相同的分隔符,例如:
这是一道一元二次方程的求根公式:$x = \frac{-b \pm \sqrt{b^2-4ac}}{2a}$,请仔细计算。
在 JSON 和 Python、Java、JavaScript 等语言的普通字符串中,反斜杠 \ 需要写为 \\。例如,公式中的 \frac 在普通字符串中写为 \\frac。
快速开始
以下 Python 示例合成包含分数的中文文本,保存为 output.mp3。API Key 和 SDK 的准备方法见 SSML 快速开始。
# coding=utf-8
import os
import dashscope
from dashscope.audio.tts_v2 import *
# 若没有配置环境变量,请用百炼API Key将下行替换为:dashscope.api_key = "sk-xxx"
dashscope.api_key = os.environ.get('DASHSCOPE_API_KEY')
# 以下为华北2(北京)地域的URL,调用时请将WorkspaceId替换为真实的业务空间ID,各地域的URL不同。
dashscope.base_websocket_api_url='wss://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/api-ws/v1/inference'
model = "cosyvoice-v3-flash"
voice = "longanyang"
synthesizer = SpeechSynthesizer(model=model, voice=voice)
# 公式中的反斜杠需要转义为 \\
audio = synthesizer.call("这是一道分数:$\\frac{1}{2}$。")
with open('output.mp3', 'wb') as f:
f.write(audio)
print('requestId: {}'.format(synthesizer.get_last_request_id()))
SSML 标签
说明功能实现参考 W3C SSML 1.0 规范,支持的标签以下文为准,并非全部标准标签。
<speak>:根节点
<speak> 是所有 SSML 标签的根节点。使用 SSML 功能时,文本内容必须包含在 <speak></speak> 标签之间。
<speak>需要使用SSML功能的文本</speak>
属性
属性名称 | 属性类型 | 是否必选 | 描述 |
|---|---|---|---|
| String | 否 | 指定发音人(音色)。 该属性的优先级高于接口请求参数
|
| String | 否 | 指定语速。该属性的优先级高于接口请求参数
|
| String | 否 | 指定音高(语调)。该属性的优先级高于接口请求参数
|
| String | 否 | 指定音量。该属性的优先级高于接口请求参数
|
| String | 否 | 指定音效。
|
| String | 否 | 用于进一步配置音效(
|
| String | 否 | 为合成语音添加背景音乐。 音频文件需存储在阿里云 OSS 上,并可通过外网 URL 访问,上传方法请参见上传文件。私有文件可使用有效的签名 URL。URL 中的 XML 特殊字符须转义,例如将
重要上传方应对上传音频的版权承担相应的法律责任。 |
| String | 否 | 指定背景音乐的音量。需与 |
示例
空属性
<speak>
需要调用SSML标签的文本
</speak>
属性组合(空格分隔)
<speak rate="1.5" pitch="0.8" volume="80">
所以放在一起,我的声音是这样的。
</speak>
voice
<speak voice="longcheng_v2">
我是男声。
</speak>
rate
<speak rate="2">
我的语速比正常人快。
</speak>
pitch
<speak pitch="0.5">
我的音高却比别人低。
</speak>
volume
<speak volume="80">
我的音量也很大。
</speak>
effect
<speak effect="robot">
你喜欢机器人瓦力吗?
</speak>
effectValue
<speak effect="eq" effectValue="1 -20 1 1 1 1 20 1">
你喜欢机器人瓦力吗?
</speak>
<speak effect="lpfilter" effectValue="1200">
你喜欢机器人瓦力吗?
</speak>
<speak effect="hpfilter" effectValue="1200">
你喜欢机器人瓦力吗?
</speak>
bgm
<speak bgm="https://nls.alicdn.com/bgm/2.wav" backgroundMusicVolume="30" rate="0.8" volume="40">
<break time="2s"/>
阴崖老木苍苍烟
<break time="700ms"/>
雨声犹在竹林间
<break time="700ms"/>
绵蕝固知裨国计
<break time="700ms"/>
绵州风物总堪怜
<break time="2s"/>
</speak>
标签关系
<speak> 标签可以包含文本和以下子标签:
<break>:控制停顿时间<sub>:替换文本<phoneme>:指定发音(拼音/音标)<soundEvent>:插入一段外部声音(铃声、猫叫等)<say-as>:设置文本的读法(数字、日期、电话号码等)
<break>:控制停顿
在语音合成过程中插入一段静默停顿,模拟自然说话中的间歇。支持秒(s)和毫秒(ms)两种单位。
<!-- 空属性 -->
<break/>
<!-- 带time属性 -->
<break time="string"/>
属性
属性名称 | 属性类型 | 是否必选 | 描述 |
|---|---|---|---|
| String | 否 | 设置停顿时长,支持秒和毫秒两种单位(如 “2s” 或 “50ms”)。
连续使用多个 |
不带属性的 <break> 默认停顿 1 秒。
示例
time
<speak>
请闭上眼睛休息一下<break time="500ms"/>好了,请睁开眼睛。
</speak>
连续使用以下三个 <break> 标签时,设置的总停顿时长为 15 秒,实际仅保留 10 秒。
<speak>
请闭上眼睛休息一下<break time="5s"/><break time="5s"/><break time="5s"/>好了,请睁开眼睛。
</speak>
标签关系
<break> 是自闭合标签,不能包含任何子标签。
<sub>:替换文本
将指定文本替换为更适合朗读的内容。例如,将 “W3C” 朗读为 “网络协议标准”。
<sub alias="string"></sub>
属性
属性名称 | 属性类型 | 是否必选 | 描述 |
|---|---|---|---|
| String | 是 | 指定用于替代原文本的朗读内容。 |
示例
alias
<speak>
<sub alias="网络协议标准">W3C</sub>
</speak>
标签关系
<sub> 标签内只能包含纯文本。
<phoneme>:指定发音
精确控制文本的发音方式。中文可通过拼音指定读音,英文可使用 CMU 音标。适用于多音字消歧和外语发音等场景。
<phoneme alphabet="string" ph="string">文本</phoneme>
属性
属性名称 | 属性类型 | 是否必选 | 描述 |
|---|---|---|---|
| String | 是 | 指定发音类型:拼音(对应中文)或音标(对应英文)。 取值范围:
|
| String | 是 | 指定具体的拼音或音标。使用规则如下:
|
示例
ph
<speak>
去<phoneme alphabet="py" ph="dian3 dang4 hang2">典当行</phoneme>把这个玩意<phoneme alphabet="py" ph="dang4 diao4">当掉</phoneme>
</speak>
<speak>
How to spell <phoneme alphabet="cmu" ph="S AY N">sin</phoneme>?
</speak>
标签关系
<phoneme> 标签内只能包含纯文本。
<soundEvent>:插入音效
在语音中的指定位置插入音效文件(如提示音、环境音等),增强语音表达的丰富性。
<soundEvent src="URL"/>
属性
属性名称 | 属性类型 | 是否必选 | 描述 |
|---|---|---|---|
| String | 是 | 指定外部音频文件的 URL。 音频文件需存储在阿里云 OSS 上,并可通过外网 URL 访问,上传方法请参见上传文件。私有文件可使用有效的签名 URL。URL 中的 XML 特殊字符须转义,例如将
重要上传方应对上传音频的版权承担相应的法律责任。 |
示例
src
<speak>
一匹马受了惊吓<soundEvent src="https://nls.alicdn.com/sound-event/horse-neigh.wav"/>人们四散躲避
</speak>
标签关系
<soundEvent> 是自闭合标签,不能包含任何子标签。
<say-as>:设置读法
指定文本的内容类型(如数字、日期、电话号码等),系统将按照对应类型的规范读法进行朗读。
<say-as interpret-as="string">文本</say-as>
属性
属性名称 | 属性类型 | 是否必选 | 描述 |
|---|---|---|---|
| String | 是 | 指定标签内文本的内容类型。 取值范围:
|
点击查看 say-as 支持范围
cardinal
中文
格式 | 示例 | 中文输出 | 说明 |
|---|---|---|---|
数字串 | 145 | 一百四十五 | 整数输入范围:20位以内的正负整数,[-99999999999999999999,99999999999999999999]。 小数输入范围:对小数点后小数的位数没有特殊限制,建议不超过10位。 |
负号+数字串 | -145 | 负一百四十五 | |
以逗号分隔3位数字串 | 10,000 | 一万 | |
负号+以逗号分隔3位数字串 | -10,124 | 负一万一百二十四 | |
数字串+小数点+2个零 | 10.00 | 十 | |
负号+数字串+小数点+2个零 | -110.00 | 负一百一十 | |
数字串+小数点+数字串 | 79.090 | 七十九点零九零 | |
负号+数字串+小数点+数字串 | -79.001 | 负七十九点零零一 |
英文
格式 | 示例 | 英文输出 | 说明 |
|---|---|---|---|
数字串 | 145 | one hundred forty five | 整数输入范围:13位以内的正负整数,[-999999999999,999999999999]。 小数输入范围:对小数点后小数的位数没有特殊限制,建议不超过10位。 |
以零开头的数字串 | 0145 | one hundred forty five | |
负号+数字串 | -145 | minus hundred forty five | |
以逗号分隔三位数字串 | 60,000 | sixty thousand | |
负号+以逗号分隔三位数字串 | -208,000 | minus two hundred eight thousand | |
数字串+小数点+零 | 12.00 | twelve | |
数字串+小数点+数字串 | 12.34 | twelve point three four | |
以逗号分隔三位数字串+小数点+数字串 | 1,000.1 | one thousand point one | |
负号+数字串+小数点+数字串 | -12.34 | minus twelve point three four | |
负号+以逗号分隔三位数字串+小数点+数字串 | -1,000.1 | minus one thousand point one | |
(以逗号分隔三位)数字串+连词符+(以逗号分隔三位)数字 | 1-1,000 | one to one thousand | |
其他默认读法 | 012.34 | twelve point three four | 无 |
1/2 | one half | ||
-3/4 | minus three quarters | ||
5.1/6 | five point one over six | ||
-3 1/2 | minus three and a half | ||
1,000.3^3 | one thousand point three to the power of three | ||
3e9.1 | three times ten to the power of nine point one | ||
23.10% | twenty three point one percent |
digits
中文
格式 | 示例 | 中文输出 | 说明 |
|---|---|---|---|
数字串 | 129090909 | 一二九零九零九零九 | 对数字串的长度没有特殊限制,建议不超过20位。 当数字串超过10位时,每个数字后插入停顿。 |
英文
格式 | 示例 | 英文输出 | 说明 |
|---|---|---|---|
数字串 | 12034 | one two zero three four | 对数字串的长度没有特殊限制,建议不超过20位。 当数字串以空格或连词符分组时,分组之间会插入逗号而产生适当停顿,支持最长5个分组。 |
数字串+空格或连词符+数字串+空格或连词符+数字串+空格或连词符+数字串 | 1-23-456 7890 | one, two three, four five six, seven eight nine zero |
telephone
中文
格式 | 示例 | 中文输出 | 说明 |
|---|---|---|---|
座机号 | 4930286 | 四九三 零二八六 | 支持7~8位座机号,支持空格和“-”作为分隔符。 其中,7位座机号支持“3-4”的数字分隔方式;8位座机号支持“4-4”的数字分隔方式。 |
493 0286 | 四九三 零二八六 | ||
493-0286 | 四九三 零二八六 | ||
62552560 | 六二五五 二五六零 | ||
6255 2560 | 六二五五 二五六零 | ||
6255-2560 | 六二五五 二五六零 | ||
座机号+分机号 | 4930286-109 | 四九三 零二八六 转幺零九 | 支持1~4位分机号。 |
4930286转109 | 四九三 零二八六 转幺零九 | ||
4930286分机109 | 四九三 零二八六 分机幺零九 | ||
4930286分机号109 | 四九三 零二八六 分机号幺零九 | ||
区号+座机号 | 01062552560 | 零幺零 六二五五 二五六零 | 支持区号:010、02x、03xx、04xx、05xx、07xx、08xx、09xx。 |
010 62552560 | 零幺零 六二五五 二五六零 | ||
010 6255 2560 | 零幺零 六二五五 二五六零 | ||
010 6255-2560 | 零幺零 六二五五 二五六零 | ||
010-62552560 | 零幺零 六二五五 二五六零 | ||
010-6255-2560 | 零幺零 六二五五 二五六零 | ||
(010)62552560 | 零幺零 六二五五 二五六零 | ||
03198907098 | 零三幺九 八九零 七零九八 | ||
0319-8907098 | 三幺九 八九零 七零九八 | ||
区号+座机号+分机号 | 010 62552560-109 | 零幺零 六二五五 二五六零 转幺零九 | 无 |
010-62552560-109 | 零幺零 六二五五 二五六零 转幺零九 | ||
(010)62552560-109 | 零幺零 六二五五 二五六零 转幺零九 | ||
(010)62552560转109 | 零幺零 六二五五 二五六零 转幺零九 | ||
(010)62552560分机109 | 零幺零 六二五五 二五六零 分机幺零九 | ||
(010)62552560分机号109 | 零幺零 六二五五 二五六零 分机号幺零九 | ||
国家代码+区号+座机号 | 86-010-62791627 | 八六 零幺零 六二七九 幺六二七 | 支持国家代码:86、 (86)、+86、(+86)、0086。并统一读为“八六”。 |
(86)10-62791627 | 八六 幺零 六二七九 幺六二七 | ||
+86-010-62791627 | 八六 零幺零 六二七九 幺六二七 | ||
0086-10-62791627 | 八六 幺零 六二七九 幺六二七 | ||
(+86)-10-6279 1627 | 八六 幺零 六二七九 幺六二七 | ||
国家代码+区号+座机号+分机号 | (86)21-58118818-207 | 八六 二幺 五八幺幺 八八幺八 转二零七 | 无 |
(86)021-5811-8818-207 | 八六 零二幺 五八幺幺 八八幺八 转二零七 | ||
(86)021-58118818转207 | 八六 零二幺 五八幺幺 八八幺八 转二零七 | ||
(86)21-5811-8818分机207 | 八六 二幺 五八幺幺 八八幺八 分机二零七 | ||
+86-021-58118818分机号207 | 八六 零二幺 五八幺幺 八八幺八分机号二零七 | ||
手机号 | 139 0000 5678 | 幺三九 零零零零 五六七八 | 支持11位手机号,支持3-3-5、3-4-4两种数字分隔方式 |
139-000-05678 | 幺三九 零零零 零五六七八 | ||
139 000 05678 | 幺三九 零零零 零五六七八 | ||
国家代码+手机号 | +86-13900005678 | 八六 幺三九 零零零零 五六七八 | 无 |
(+86)-139-0000-5678 | 八六 幺三九 零零零零 五六七八 | ||
+8613900005678 | 八六 幺三九 零零零零 五六七八 | ||
0086-139 000 05678 | 八六 幺三九 零零零 零五六七八 | ||
服务号 | 123 | 幺二三 | 支持常用的服务号。 支持以400/800开头的10位服务号,支持以“3-3-4”的数字分隔方式。 支持以12530/17951/12593开头的16位号码。 |
95678 | 九五六七八 | ||
4008110510 | 四零零 八幺幺 零五幺零 | ||
800-810-8888 | 八零零 八幺零 八八八八 | ||
1253013520638377 | 幺二五三零 幺三五 二零六三 八三七七 | ||
其他 | (86)(21)9899-80800-0909 | 八六 二幺 九八九九 八零八零零 零九零九 | 支持“数字串+分隔符(左右括号、-)”方式。 |
英文
格式 | 示例 | 英文输出 | 说明 |
|---|---|---|---|
数字串 | 12034 | one two oh three four | 对数字串的长度没有特殊限制,建议不超过20位。当数字串以空格或连词符分组时,分组之间会插入逗号而产生适当停顿,支持最长5个分组。 |
数字串+空格或连词符+数字串+空格或连词符+数字串 | 1-23-456 7890 | one, two three, four five six, seven eight nine oh | |
加号+数字串+空格或连词符+数字串 | +43-211-0567 | plus four three, two one one, oh five six seven | |
左括号+数字串+右括号+空格+数字串+空格或连词符+数字串 | (21) 654-3210 | (two one) six five four, three two one oh |
address
中文
格式 | 示例 | 中文输出 | 说明 |
|---|---|---|---|
常用地址格式 | 元和镇嘉元30-9 | 元和镇嘉元三十杠九 | 支持常用地址格式。此处地址指标准的邮寄地址。 |
市台路388弄1107-1108号 | 市台路三八八弄幺幺零七杠幺幺零八号 | ||
华润二十四城六期锦云府3-1-3205 | 华润二十四城六期锦云府三杠一杠三二零五 | ||
圣华名都大厦2幢2006室 | 圣华名都大厦二幢二零零六室 | ||
五常街道庭院5幢4单元201 | 五常街道庭院五幢四单元二零幺 | ||
芙蓉江路150弄19号 | 芙蓉江路幺五零弄十九号 |
英文
英文文本不支持此类型。
id
中文
格式 | 示例 | 中文输出 | 说明 |
|---|---|---|---|
字符串 | dell0101 | D E L L 零 一 零 一 | 大小写英文字符、阿拉伯数字0~9、下划线。 输出的空格表示每个字符之间插入停顿,即字符一个一个地读。 |
myid_1998 | M Y I D 下划线 一 九 九 八 | ||
AiTest | A I T E S T |
英文
英文文本中,此类型与 characters 的作用相同。
characters
中文
格式 | 示例 | 中文输出 | 说明 |
|---|---|---|---|
字符串 | ISBN 1-001-099098-1 | I S B N 一 杠 零 零 一 杠 零 九 九 零 九 八 杠 一 | 支持中文汉字、大小写英文字符、阿拉伯数字0~9以及部分全角和半角字符。 输出的空格表示每个字符之间插入停顿,即字符一个一个地读。标签内的文本如果包含XML的特殊字符,需要做字符转义。 |
x10b2345_u | x 一 零 b 二 三 四 五 下划线 u | ||
v1.0.1 | v 一 点 零 点 一 | ||
版本号2.0 | 版本号二 点 零 | ||
苏M MA000 | 苏M M A 零 零 零 | ||
空中客车A330 | 空中客车A 三 三 零 | ||
型号s01 s02和s03 | 型号s 零 一 s 零二 和s 零 三 | ||
空中客车A330 | 空中客车A 三 三 零 | ||
αβγ | 阿尔法 贝塔 伽玛 |
英文
格式 | 示例 | 英文输出 | 说明 |
|---|---|---|---|
字符串 | *b+3$.c-0'=α | asterisk B plus three dollar dot C dash zero apostrophe equals alpha | 支持中文汉字、大小写英文字符、阿拉伯数字0~9以及部分全角和半角字符。 输出的空格表示每个字符之间插入停顿,即字符一个一个地读。 标签内的文本如果包含XML的特殊字符,需要做字符转义。 |
punctuation
中文
格式 | 示例 | 中文输出 | 说明 |
|---|---|---|---|
标点符号 | … | 省略号 | 支持常见中英文标点。输出的空格表示每个字符之间插入停顿,即字符一个一个地读。 标签内的文本如果包含XML的特殊字符,需要做字符转义。 |
…… | 省略号 | ||
!"#$%& | 叹号 双引号 井号 dollar 百分号 and | ||
‘()*+ | 单引号 左括号 右括号 星号 加号 | ||
,-./:; | 逗号 杠 点 斜杠 冒号 分号 | ||
<=>?@ | 小于 等号 大于 问号 at | ||
[\]^_ | 左方括号 反斜线 右方括号 脱字符 下划线 |
英文
英文文本中,此类型与 characters 的作用相同。
date
中文
格式 | 示例 | 中文输出 | 说明 |
|---|---|---|---|
xx年 | 71年 | 七一年 | 支持2位和4位年份。其中: 2位年份支持60年~99年、00年~09年、10年~19年。 4位年份支持1000年~1999年、2000年~2099年。 |
04年 | 零四年 | ||
19年 | 一九年 | ||
1011年 | 一零一一年 | ||
1998年 | 一九九八年 | ||
2008年 | 二零零八年 | ||
xx年xx月 | 98年4月 | 九八年四月 | 当月份为1到9月时,支持开头带“0”和不带“0”两种写法。例如“1908年4月”和“1908年04月”。 |
1998年04月 | 一九九八年四月 | ||
08年8月 | 零八年八月 | ||
2008年8月 | 二零零八年八月 | ||
xx年xx月xx日xx年xx月xx号 | 98年4月23日 | 九八年四月二十三日 | 当日期为1到9日时,支持开头带“0”和不带“0”两种写法。例如“1908年4月8日”和“1908年04月08日”。 |
1998年04月23日 | 一九九八年四月二十三日 | ||
08年8月8号 | 零八年八月八号 | ||
2008年08月08号 | 二零零八年八月八号 | ||
xx年xx月xx日xx年xx月xx号 | 98年4月23日 | 九八年四月二十三日 | 当日期为1到9日时,支持开头带“0”和不“0”两种写法。例如“1908年4月8日”和“1908年04月08日”。 |
1998年04月23日 | 一九九八年四月二十三日 | ||
08年8月8号 | 零八年八月八号 | ||
2008年08月08号 | 二零零八年八月八号 | ||
xx月xx号 | 3月20日 | 三月二十日 | 无 |
08月07号 | 八月七号 | ||
年月缩写 | 2018/08 | 二零一八年八月 | 支持“/”、“-”、“.”作为缩写的分隔符。 |
2018-08 | 二零一八年八月 | ||
2018.08 | 二零一八年八月 | ||
年月日缩写 | 2018/08/08 | 二零一八年八月八日 | |
2018-8-8 | 二零一八年八月八日 | ||
2018.08.08 | 二零一八年八月八日 | ||
xx年xx月xx日~xx年xx月xx日xx年xx月xx号~xx年xx月xx号 | 04年9月1日~30日 | 零四年九月一日至三十日 | 支持“~”、“-”作为“至”的缩写标志。 |
2004年09月01号-2008年06月08号 | 二零零四年九月一号至二零零八年六月八号 | ||
xx年xx月xx日~xx日xx年xx月xx号~xx号 | 04年9月1日~30日 | 零四年九月一日至三十日 | |
2004年09月01号-2008年06月08号 | 二零零四年九月一号至二零零八年六月八号 | ||
xx年xx月~xx年xx月 | 01年04月~10年04月 | 零一年四月至一零年四月 | |
2001年04月~2010年04月 | 二零零一年四月至二零一零年四月 | ||
xx月xx日~xx月xx日xx月xx号~xx月xx号 | 10月1日~10月7日 | 十月一日至十月七日 | |
10月01号~10月07号 | 十月一号至十月七号 | ||
xx月xx日~xx日xx月xx号~xx号 | 10月1日~7日 | 十月一日至七日 | |
10月01号~07号 | 十月一号至七号 | ||
年月日缩写~年月日缩写 | 2018/03/03~2019/01/01 | 二零一八年三月三日至二零一九年一月一日 | 支持“/”、“.”作为缩写的分隔符,支持“~”、“-”作为“至”的缩写标志。 |
1997.9.9~1998.9.9 | 一九九七年九月九日至一九九八年九月九日 | ||
月日缩写~月日缩写 | 10/20~10/31 | 十月二十日至十月三十一日 | |
xx~xx月xx月~xx月 | 1~10月 | 一至十月 | |
1月~10月 | 一月至十月 | ||
月日年缩写 | 10/20/2018 | 二零一八年十月二十日 | 仅支持4位的年份,仅支持“/”作为日期的分隔符,仅支持“月/日/年”的书写方式。 |
英文
格式 | 示例 | 英文输出 | 说明 |
|---|---|---|---|
四位数字/两位数字或四位数字-两位数字 | 2000/01 | two thousand, oh one | 跨年度。 |
1900-01 | nineteen hundred, oh one | ||
2001-02 | twenty oh one, oh two | ||
2019-20 | twenty nineteen, twenty | ||
1998-99 | nineteen ninety eight, ninety nine | ||
1999-00 | nineteen ninety nine, oh oh | ||
以1或2开头的四位数字 | 2000 | two thousand | 四位数字年份。 |
1900 | nineteen hundred | ||
1905 | nineteen oh five | ||
2021 | twenty twenty one | ||
星期几-星期几 或 星期几~星期几 或 星期几&星期几 | mon-wed | monday to wednesday | 星期几的范围标签内的文本如果包含XML的特殊字符,需要做字符转义。 |
tue~fri | tuesday to friday | ||
sat&sun | saturday and sunday | ||
DD-DD MMM, YYYY 或 DD~DD MMM, YYYY 或 DD&DD MMM, YYYY | 19-20 Jan, 2000 | the nineteen to the twentieth of january two thousand | DD表示两位数字日期,MMM表示月份的三字母缩写或完整单词,YYYY表示以1或2开头的四位数字年份。 |
01 ~ 10 Jul, 2020 | the first to the tenth of july twenty twenty | ||
05&06 Apr, 2009 | the fifth and the sixth of april two thousand nine | ||
MMM DD-DD 或 MMM DD~DD 或 MMM DD&DD | Feb 01 - 03 | feburary the first to the third | MMM表示月份的三字母缩写或完整单词,DD表示两位数字日期。 |
Aug 10~20 | august the tenth to the twentieth | ||
Dec 11&12 | december the eleventh and the twelfth | ||
MMM-MMM 或 MMM~MMM 或 MMM&MMM | Jan-Jun | january to june | MMM表示月份的三字母缩写或完整单词。 |
jul ~ dec | july to december | ||
sep&oct | september and october | ||
YYYY-YYYY 或 YYYY~YYYY | 1990 - 2000 | nineteen ninety to two thousand | YYYY表示以1或2开头的四位数字年份。 |
2001~2021 | two thousand one to twenty twenty one | ||
WWW DD MMM YYYY | Sun 20 Nov 2011 | sunday the twentieth of november twenty eleven | WWW表示星期几的三字母缩写或完整单词,DD表示两位数字日期,MMM表示月份的三字母缩写或完整单词,MM表示两位数字月份(或三字母缩写或完整单词),YYYY表示以1或2开头的四位数字年份。 |
WWW DD MMM | Sun 20 Nov | sunday the twentieth of november | |
WWW MMM DD YYYY | Sun Nov 20 2011 | sunday november the twentieth twenty eleven | |
WWW MMM DD | Sun Nov 20 | sunday november the twentieth | |
WWW YYYY-MM-DD | Sat 2010-10-01 | aturday october the first twenty ten | |
WWW YYYY/MM/DD | Sat 2010/10/01 | saturday october the first twenty ten | |
WWW MM/DD/YYYY | Sun 11/20/2011 | sunday november the twentieth twenty eleven | |
MM/DD/YYYY | 11/20/2011 | november the twentieth twenty eleven | |
YYYY | 1998 | nineteen ninety eight | |
其他默认读法 | 10 Mar, 2001 | the tenth of march two thousand one | 无 |
10 Mar | the tenth of march | ||
Mar 2001 | march two thousand one | ||
Fri. 10/Mar/2001 | friday the tenth of march two thousand one | ||
Mar 10th, 2001 | march the tenth two thousand one | ||
Mar 10 | march the tenth | ||
2001/03/10 | march the tenth two thousand one | ||
2001-03-10 | march the tenth two thousand one | ||
2000s | two thousands | ||
2010's | twenty tens | ||
1900's | nineteen hundreds | ||
1990s | nineteen nineties |
time
中文
格式 | 示例 | 中文输出 | 说明 |
|---|---|---|---|
时刻 | 12:00 | 十二点 | 支持常用时间和时间范围格式。 |
12:00:00点 | 十二点 | ||
10:20分 | 十点二十分 | ||
10:20:30 | 十点二十分三十秒 | ||
09:18:14 | 九点十八分十四秒 | ||
时刻~时刻 | 11:00~12:00 | 十一点到十二点 | |
09:00-14:00 | 九点到十四点 | ||
11:00~11:30 | 十一点到十一点三十分 | ||
11:00-12:18 | 十一点到十二点十八分 | ||
10:30~11:00 | 十点三十分到十一点 | ||
09:28-10:00 | 九点二十八分到十点 | ||
10:20~11:20 | 十点二十分到十一点二十分 | ||
06:00~08:00 | 六点到八点 | ||
上午10:20~下午13:30 | 上午十点二十分到下午十三点三十分 | ||
时间缩写 | 5:00 am | 凌晨五点整 | |
5:30 am | 凌晨五点半 | ||
5:20:12 am | 凌晨五点二十分十二秒 | ||
7:00 am | 上午七点整 | ||
7:30 AM | 上午七点半 | ||
7:20:12 a.m. | 上午七点二十分十二秒 | ||
07:08:12 A.M. | 上午七点零八分十二秒 | ||
5:00 pm | 下午五点整 | ||
5:30 PM | 下午五点半 | ||
5:20:12 p.m. | 下午五点二十分十二秒 | ||
05:09:12 P.M. | 下午五点零九分十二秒 | ||
9:00 pm | 晚上九点整 | ||
9:30 pm | 晚上九点半 | ||
9:20:12 PM | 晚上九点二十分十二秒 | ||
9:02:12 P.M. | 晚上九点零二分十二秒 | ||
12:00 pm | 中午十二点整 | ||
12:30 p.m. | 中午十二点半 | ||
12:20:12 PM | 中午十二点二十分十二秒 |
英文
格式 | 示例 | 英文输出 | 说明 |
|---|---|---|---|
HH:MM AM或PM | 09:00 AM | nine A M | HH表示一或两位数字小时,MM表示两位数字分钟,AM/PM表示上/下午。 |
09:03 PM | nine oh three P M | ||
09:13 p.m. | nine thirteen p m | ||
HH:MM | 21:00 | twenty one hundred | |
HHMM | 100 | one oclock | |
时刻-时刻 | 8:00 am - 05:30 pm | eight a m to five p m | 支持常见时间格式和范围。 |
7:05~10:15 AM | seven oh five to ten fifteen A M | ||
09:00-13:00 | nine oclock to thirteen hundred |
currency
中文
格式 | 示例 | 中文输出 | 说明 |
|---|---|---|---|
数字+金额标识符 | 12.00 RMB | 十二人民币 | 支持AUD(澳元) 、CAD(加元)、 HKD(港币)、JPY(日元)、USD(美元)、CHF(瑞士法郎)、NOK(挪威克朗)、SEK(瑞典克朗)、GBP(英镑)、 RMB(人民币)、CNY(元)和EUR(欧元)。 支持的数字格式包括:整数、小数以及以逗号分隔的国际写法。 |
12.50 RMB | 十二点五零人民币 | ||
12,000,000 RMB | 一千二百万人民币 | ||
12,000,000.00 RMB | 一千二百万人民币 | ||
12,000.35 RMB | 一万两千点三五人民币 | ||
金额标识符+数字 | $12 | 十二美元 | 支持 CAD(加元)、 $(美元)、Fr(法郎)、kr(丹麦克朗)、 £(英镑)、¥(元)和 €(欧元)。 支持的数字格式包括:整数、小数以及以逗号分隔的国际写法。 |
$12.00 | 十二美元 | ||
$12.12 | 二点一二美元 | ||
$12,000 | 一万两千美元 | ||
$12,000.00 | 一万两千美元 | ||
$12,000.99 | 一万两千点九九美元 | ||
其他默认读法 | 1213 | 一千二百一十三 | 无 |
1213 KML | 一千二百一十三K M L | ||
1213.00 KML | 一千二百一十三K M L | ||
1213.9 KML | 一千二百一十三点九K M L | ||
1,000 KML | 一千K M L | ||
1,000.00 KML | 一千K M L | ||
1,000.98 KML | 一千点九八K M L | ||
12,000 | 一万两千 |
英文
格式 | 示例 | 英文输出 | 说明 |
|---|---|---|---|
数字+金额识别符 | 1.00 RMB | one yuan | 支持的数字格式:整数、小数以及以逗号分隔的国际写法。 支持的金额识别符: CN¥ (yuan) CNY (yuan) RMB (yuan) AUD (australian dollar) CAD (canadian dollar) CHF (swiss franc) DKK (danish krone) EUR (euro) GBP (british pound) HKD (Hong Kong(China) dollar) JPY (japanese yen) NOK (norwegian krone) SEK (swedish krona) SGD (singapore dollar) USD (united states dollar) |
2.02 CNY | two point zero two yuan | ||
1,000.23 CN¥ | one thousand point two three yuan | ||
1.01 SGD | one singapore dollar and one cent | ||
2.01 CAD | two canadian dollars and one cent | ||
3.1 HKD | three hong kong dollars and ten cents | ||
1,000.00 EUR | one thousand euros | ||
金额识别符+数字 | US$ 1.00 | one US dollar | 支持的数字格式:整数、小数以及以逗号分隔的国际写法。 支持的金额识别符: US$ (US dollar) CA$ (Canadian dollar) AU$ (Australian dollar) SG$ (Singapore dollar) HK$ (Hong Kong dollar) C$ (Canadian dollar) A$ (Australian dollar) $ (dollar) £ (pound) € (euro) CN¥ (yuan) CNY (yuan) RMB (yuan) AUD (australian dollar) CAD (canadian dollar) CHF (swiss franc) DKK (danish krone) EUR (euro) GBP (british pound) HKD (Hong Kong(China) dollar) JPY (japanese yen) NOK (norwegian krone) SEK (swedish krona) SGD (singapore dollar) USD (united states dollar) |
$0.01 | one cent | ||
JPY 1.01 | one japanese yen and one sen | ||
£1.1 | one pound and ten pence | ||
€2.01 | two euros and one cent | ||
USD 1,000 | one thousand united states dollars | ||
数字+量词+金额识别符 或 金额识别符+数字+量词 | 1.23 Tn RMB | one point two three trillion yuan | 支持的量词格式包括: thousand million billion trillion Mil (million) mil (million) Bil (billion) bil (billion) MM (million) Bn (billion) bn (billion) Tn (trillion) tn (trillion) K(thousand) k (thousand) M (million) m (million) |
$1.2 K | one point two thousand dollars |
measure
中文
格式 | 示例 | 中文输出 | 说明 |
|---|---|---|---|
数字+中文单位 | 2片 | 两片 | 支持常见中文单位及单位缩写。 |
120公顷 | 一百二十公顷 | ||
100多毫克 | 一百多毫克 | ||
100来米 | 一百来米 | ||
100余人 | 一百余人 | ||
1厘米20毫米 | 一厘米二十毫米 | ||
120.00平方公里 | 一百二十平方公里 | ||
数字+单位缩写 | 120.56 cm² | 一百二十点五六平方厘米 | |
120 ㎡ 56 cm² | 一百二十平方米五十六平方厘米 | ||
100 m 12 cm 6 mm | 一百米十二厘米六毫米 | ||
范围 | 10~15 kg | 十至十五千克 | |
10.24~789.82亩 | 十点二四至七百八十九点八二亩 | ||
10米~15米 | 十米至十五米 | ||
10.24 cm~19.08 cm | 十点二四厘米至十九点零八厘米 | ||
数字+单位+"/"+单位 | 10元/斤 | 十元每斤 | |
199~299元/件 | 一百九十九至二百九十九元每件 | ||
299.99元/g~399.99元/g | 二百九十九点九九元每克至三百九十九点九九元每克 | ||
其他默认读法 | 12扎 | 十二扎 | |
30 rm | 三十r m | ||
4万万同胞 | 四万万同胞 | ||
12.897微克 | 十二点八九七微克 |
英文
格式 | 示例 | 英文输出 | 说明 |
|---|---|---|---|
数字+计量单位 | 1.0 kg | one kilogram | 支持的数字格式:整数、小数以及以逗号分隔的国际写法。 支持常见单位缩写。 |
1,234.01 km | one thousand two hundred thirty four point zero one kilometres. | ||
计量单位 | mm2 | square millimetre |
常见符号
符号 | 中文读法 | 英文读法 |
|---|---|---|
! | 叹号 | exclamation mark |
“ | 双引号 | double quote |
# | 井号 | pound |
$ | dollar | dollar |
% | 百分号 | percent |
& | and | and |
‘ | 单引号 | left quote |
( | 左括号 | left parenthesis |
) | 右括号 | right parenthesis |
* | 星 | asterisk |
+ | 加 | plus |
, | 逗号 | comma |
- | 杠 | dash |
. | 点 | dot |
/ | 斜杠 | slash |
: | 零冒号 | solon |
; | 分号 | semicolon |
< | 小于 | less than |
= | 等号 | equals |
> | 大于 | greater than |
? | 问号 | question mark |
@ | at | at |
[ | 左方括号 | left bracket |
\ | 反斜线 | back slash |
] | 右方括号 | right bracket |
^ | 脱字符 | caret |
_ | 下划线 | underscore |
` | 反引号 | back quote |
{ | 左花括号 | left brace |
| | 竖线 | vertical bar |
} | 右花括号 | right brace |
~ | 波浪线 | tilde |
! | 叹号 | exclamation mark |
“ | 左双引号 | left double quote |
” | 右双引号 | right double qute |
‘ | 左单引号 | left quote |
’ | 右单引号 | right quote |
( | 左括号 | left parenthesis |
) | 右括号 | right parenthesis |
, | 逗号 | comma |
。 | 句号 | full stop |
— | 杠 | em dash |
: | 冒号 | colon |
; | 分号 | semicolon |
? | 问号 | question mark |
、 | 顿号 | enumeration comma |
… | 省略号 | ellipsis |
…… | 省略号 | ellipsis |
《 | 左书名号 | left guillemet |
》 | 右书名号 | right guillemet |
¥ | 人民币符号 | yuan |
≥ | 大于等于 | greater than or equal to |
≤ | 小于等于 | less than or equal to |
≠ | 不等于 | not equal |
≈ | 约等于 | approximately equal |
± | 加减 | plus or minus |
× | 乘 | times |
π | 派 | pi |
Α | 阿尔法 | alpha |
Β | 贝塔 | beta |
Γ | 伽玛 | gamma |
Δ | 德尔塔 | delta |
Ε | 艾普西龙 | epsilon |
Ζ | 捷塔 | zeta |
Θ | 西塔 | theta |
Ι | 艾欧塔 | iota |
Κ | 喀帕 | kappa |
∧ | 拉姆达 | lambda |
Μ | 缪 | mu |
Ν | 拗 | nu |
Ξ | 克西 | ksi |
Ο | 欧麦克轮 | omicron |
∏ | 派 | pi |
Ρ | 柔 | rho |
∑ | 西格玛 | sigma |
Τ | 套 | tau |
Υ | 宇普西龙 | upsilon |
Φ | fai | phi |
Χ | 器 | chi |
Ψ | 普赛 | psi |
Ω | 欧米伽 | omega |
α | 阿尔法 | alpha |
β | 贝塔 | beta |
γ | 伽玛 | gamma |
δ | 德尔塔 | delta |
ε | 艾普西龙 | epsilon |
ζ | 捷塔 | zeta |
η | 依塔 | eta |
θ | 西塔 | theta |
ι | 艾欧塔 | iota |
κ | 喀帕 | kappa |
λ | 拉姆达 | lambda |
μ | 缪 | mu |
ν | 拗 | nu |
ξ | 克西 | ksi |
ο | 欧麦克轮 | omicron |
π | 派 | pi |
ρ | 柔 | rho |
σ | 西格玛 | sigma |
τ | 套 | tau |
υ | 宇普西龙 | upsilon |
φ | fai | phi |
χ | 器 | chi |
ψ | 普赛 | psi |
ω | 欧米伽 | omega |
常见计量单位
格式 | 类别 | 中文示例 | 英文示例 |
|---|---|---|---|
缩写 | 长度 | nm(纳米)、μm(微米)、 mm(毫米)、cm(厘米)、m(米)、km(千米)、ft(英尺)、in(英寸) | nm (nanometre), μm (micrometre), mm (millimetre), cm (centimetre), m (metre), km (kilometre), ft (foot), in (inch) |
面积 | cm²(平方厘米)、㎡(平方米)、km²(平方千米)、SqFt(平方英尺) | cm² (square centimetre), ㎡ (square metre), km2 (square kilometre), SqFt (square foot) | |
体积 | cm³(立方厘米)、m³(立方米)、km³(立方千米)、mL(毫升)、L(升)、gallon(加仑) | cm³ (cubic centimetre), m³ (cubic metre), km3 (cubic kilometre), mL (millilitre), L (millilitre), gal (gallon) | |
重量 | μg(微克)、mg(毫克)、g(克)、kg(千克) | μg (microgram), mg (microgram), g (gram), kg (kilogram) | |
时间 | min(分)、sec(秒)、ms(毫秒) | min (minute), sec (second), ms (millisecond) | |
电磁 | μA(微安)、mA(毫安)、Ω(欧姆)、Hz(赫兹)、kHz(千赫兹)、MHz(兆赫兹)、GHz(吉赫兹)、V(伏)、kV(千伏)、kWh(千瓦时) | μA (microamp), mA (milliamp), Hz (hertz), kHz (kilohertz), MHz (megahertz), GHz (gigahertz), V (volt), kV (kilovolt), kWh (kilowatt hour) | |
声音 | dB(分贝) | dB (decibel) | |
气压 | Pa(帕)、kPa(千帕)、Mpa(兆帕) | Pa (pascal), kPa (kilopascal), MPa (megapascal) | |
其他常见单位 | 支持不限于上述类别的中文单位,例如“米”、“秒”、“美元”、“毫升每瓶”等。以及中文量词,例如“架”、“场”、“头”、“部”、“盆”等。 | 支持不限于上述类别的计量单位,例如 tsp (teaspoon), rpm (round per minute), KB (kilobyte), mmHg (milimetre of mercury) 等。 | |
示例
cardinal
<speak>
<say-as interpret-as="cardinal">12345</say-as>
</speak>
<speak>
<say-as interpret-as="cardinal">10234</say-as>
</speak>
digits
<speak>
<say-as interpret-as="digits">12345</say-as>
</speak>
<speak>
<say-as interpret-as="digits">10234</say-as>
</speak>
telephone
<speak>
<say-as interpret-as="telephone">12345</say-as>
</speak>
<speak>
<say-as interpret-as="telephone">10234</say-as>
</speak>
name
<speak>
她的曾用名是<say-as interpret-as="name">曾小凡</say-as>
</speak>
address
<speak>
<say-as interpret-as="address">富路国际1号楼3单元304</say-as>
</speak>
id
<speak>
<say-as interpret-as="id">myid_1998</say-as>
</speak>
characters
<speak>
<say-as interpret-as="characters">希腊字母αβ</say-as>
</speak>
<speak>
<say-as interpret-as="characters">*b+3.c$=α</say-as>
</speak>
punctuation
<speak>
<say-as interpret-as="punctuation"> -./:;</say-as>
</speak>
date
<speak>
<say-as interpret-as="date">1000-10-10</say-as>
</speak>
<speak>
<say-as interpret-as="date">10-01-2020</say-as>
</speak>
time
<speak>
<say-as interpret-as="time">5:00am</say-as>
</speak>
<speak>
<say-as interpret-as="time">0500</say-as>
</speak>
currency
<speak>
<say-as interpret-as="currency">13,000,000.00RMB</say-as>
</speak>
<speak>
<say-as interpret-as="currency">$1,000.01</say-as>
</speak>
measure
<speak>
<say-as interpret-as="measure">100m12cm6mm</say-as>
</speak>
<speak>
<say-as interpret-as="measure">1,000.01kg</say-as>
</speak>
标签关系
<say-as> 标签可以包含文本。
LaTeX 标签与符号
按类别查看支持的标签与符号。公式输入示例包含分隔符;读法列为中文示例。
基础数学
标签或符号 | 含义 | 公式内容示例 | 公式输入示例 | 示例读法 |
|---|---|---|---|---|
| 加 |
|
| 二加三等于五 |
| 减 |
|
| 三减二等于一 |
| 加减 |
|
| 正负一加减二 |
正负 | ||||
| 乘 |
|
| 二乘三等于六 |
|
|
| ||
|
|
| ||
| 除 |
|
| 六除二等于三 |
|
|
| ||
|
|
| ||
| 等于 |
|
| 三加五等于八 |
| 小于 |
|
| 一小于二 |
| 小于等于 |
|
| 三小于等于五 |
|
|
| ||
|
|
| ||
|
|
| ||
|
|
| ||
|
|
| ||
| 大于 |
|
| 二大于一 |
| 大于等于 |
|
| 五大于等于三 |
|
|
| ||
|
|
| ||
|
|
| ||
|
|
| ||
|
|
| ||
| 分数 |
|
| 三分之二 |
| 次方 |
|
| 二的一次方 |
| 开根 |
|
| 根号下九等于三 |
|
| 三次根号下八等于二 | ||
| 百分比 |
|
| 百分之五 |
| 绝对值 |
|
| 绝对值三的绝对值等于三 |
|
|
| 绝对值三绝对值等于三 | |
| 对数 |
|
| log十 |
| 对数 |
|
| log五 |
| 自然对数 |
|
| LN十 |
| 阶乘 |
|
| 五的阶乘 |
| 括号 |
|
| 括号内二加一 |
|
| 大括号二加一反大括号 |
特殊数学符号
标签或符号 | 含义 | 公式内容示例 | 公式输入示例 | 示例读法 |
|---|---|---|---|---|
| alpha |
|
| α |
|
|
| ||
| beta |
|
| β |
|
|
| ||
| gamma |
|
| γ |
|
|
| ||
| delta |
|
| Δ |
|
|
| ||
| 无穷大(中) |
|
| 无穷大 |
| infty (英) |
|
|
几何
标签或符号 | 含义 | 公式内容示例 | 公式输入示例 | 示例读法 |
|---|---|---|---|---|
| 派 |
|
| 派等于三点一四一五九 |
| 三角函数 |
|
| sine三十度等于二分之一 |
|
|
| cosine四十五度等于二分之根号下二 | |
|
|
| tangent三十度等于cosine三十度分之sine三十度 | |
|
|
| cosecant A | |
|
|
| secant A | |
|
|
| cotangent A | |
| 角 |
|
| 角A B |
|
|
| ||
| 度 |
|
| 角A B 等于三十度 |
| 圆 |
|
| 圆 |
| 弧 |
|
| 弧BC |
| 直角 |
|
| 因为直角三角形ABC |
|
|
| 所以AB垂直于BC | |
| 三角形 |
|
| 三角形ABC |
|
|
| ||
| 平行四边形 |
|
| 平行四边形ABCD |
| 垂直 |
|
| A B 垂直于 B C |
|
|
| ||
|
|
| ||
| 平行 |
|
| A平行于B |
| 平行且相等于 |
|
| A平行且相等于B |
| 全等 |
|
| 三角形ABC全等于三角形DEF |
条件
标签或符号 | 含义 | 公式内容示例 | 公式输入示例 | 示例读法 |
|---|---|---|---|---|
| 推出 |
|
| 推出一加一等于二 |
| 等价 |
|
| p等价q |
| 因为 |
|
| 因为a等于b所以b等于a |
| 所以 |
单位
单位须使用 \unit、\quantity、\mathit、\mathrm 或 \rm 包裹,例如 \unit{cm}。
标签或符号 | 含义 | 公式内容示例 | 公式输入示例 | 示例读法 |
|---|---|---|---|---|
| 毫米 |
|
| 五毫米 |
| 厘米 |
|
| 五厘米 |
| 分米 |
|
| 五分米 |
| 米 |
|
| 五米 |
| 千米 |
|
| 五千米 |
| 克 |
|
| 五克 |
| 千克 |
|
| 五千克 |
| 吨 |
|
| 五吨 |
| 平方毫米 |
|
| 五平方毫米 |
| 平方厘米 |
|
| 五平方厘米 |
| 平方分米 |
|
| 五平方分米 |
| 平方米 |
|
| 五平方米 |
| 平方千米 |
|
| 五平方千米 |
| 立方毫米 |
|
| 五立方毫米 |
| 立方厘米 |
|
| 五立方厘米 |
| 立方分米 |
|
| 五立方分米 |
| 立方米 |
|
| 五立方米 |
| 立方千米 |
|
| 五立方千米 |
| 毫升 |
|
| 五毫升 |
| 秒 |
|
| 五秒 |
| 分钟 |
|
| 五分 |
| 小时 |
|
| 五小时 |
| 千米每小时 |
|
| 五千米每小时 |
| 克每升 |
|
| 五克每升 |
常见问题
公式没有被正确朗读,如何排查?
- 检查公式是否已用
$...$、$$...$$、\(...\)或\[...\]包裹。 - 检查公式是否包含不支持的标签或符号。
- 检查请求字符串中的
\是否已正确转义为\\。
如何在代码中处理反斜杠?
请参见使用方法中的转义说明。公式内容和代码字符串的写法不同,例如 \frac 在普通字符串中应写为 \\frac。