Use SSML to control voice characteristics such as speech rate, pitch, and volume, and LaTeX to read mathematical formulas in Chinese. Learn about supported models, usage limits, API examples, tags, and symbols.
Feature overview
SSML is an XML-based speech synthesis markup language. It sets voice characteristics such as voice, speech rate, pitch, and volume, controls pauses, specifies pronunciation and reading styles, and adds background music and sound effects. The CosyVoice LaTeX feature converts mathematical formulas in text into Chinese speech.
Use these features for audiobooks, customer service announcements, multilingual pronunciation, and online education. The following sections list the models, voices, and APIs for each feature. For model selection, see Speech synthesis.
Usage limits
SSML
-
Supported models:
qwen-audio-3.1-tts-plus,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 -
Voices: Only cloned voices and system voices marked as supporting SSML in the Qwen-Audio-TTS voice list or CosyVoice voice list are supported.
-
APIs:
- Java SDK (version 2.20.3 or later): non-streaming and unidirectional streaming calls
- Python SDK (version 1.23.4 or later): non-streaming and unidirectional streaming calls
- WebSocket API: set the
enable_ssmlparameter totrue, and send a single continue-task event
LaTeX
-
Supported models:
cosyvoice-v3.5-plus,cosyvoice-v3.5-flash,cosyvoice-v3-plus,cosyvoice-v3-flash,cosyvoice-v2 -
Language: Chinese only.
-
Content: Use supported tags and symbols. Markdown math code blocks, such as blocks marked as
math, are not supported. Put only the formula inside the delimiters. Other text inside them may be read incorrectly.
Usage
SSML
When using SSML, follow these rules:
- Enclose all text in
<speak></speak>. Multiple<speak>elements can appear in sequence, but cannot be nested. - Escape XML special characters inside tags:
"→",'→',&→&,<→<, and>→>.
Quick start
The example below uses SSML to control speech rate during synthesis. Before running the code, complete these prerequisites:
- Obtain an API key
- Install the DashScope SDK (Python 1.23.4 or later, Java 2.20.3 or later). For details, see Install the SDK.
ImportantThe cosyvoice-v3.5-plus and cosyvoice-v3.5-flash models are currently available only in the Beijing region and are designed exclusively for voice cloning scenarios (no preset voices are provided). Before using these models, create a target voice by following the instructions in Voice cloning.
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 feature description:
* 1. Only non-streaming calls and unidirectional streaming calls support the SSML feature
* Use an SSML-compatible cloned or preset voice (such as longanyang for cosyvoice-v3-flash). See this page for supported models.
*/
public class Main {
private static String model = "cosyvoice-v3-flash";
private static String voice = "longanyang";
public static void main(String[] args) {
// Singapore region URL. Replace WorkspaceId with your actual workspace ID. The URL varies by region.
Constants.baseWebsocketApiUrl = "wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference";
streamAudioDataToSpeaker();
System.exit(0);
}
public static void streamAudioDataToSpeaker() {
SpeechSynthesisParam param =
SpeechSynthesisParam.builder()
// The API Keys for the Singapore and Beijing regions are different. Get an API Key: https://www.alibabacloud.com/help/model-studio/get-api-key
// If you have not configured an environment variable, replace the following line with your Model Studio 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 {
// Non-streaming call, blocks until audio is returned
// Special characters need to be escaped
audio = synthesizer.call("<speak rate=\"2\">My speaking rate is faster than a normal person's.</speak>");
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
// Close the WebSocket connection when the task ends
synthesizer.getDuplexApi().close(1000, "bye");
}
if (audio != null) {
// Save audio data to local file "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);
}
}
// The first text transmission requires establishing a WebSocket connection, so the first packet latency includes the connection setup time
System.out.println(
"[Metric] requestId: "
+ synthesizer.getLastRequestId()
+ ", first packet latency (ms): "
+ 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 feature description:
* 1. Only non-streaming calls and unidirectional streaming calls support the SSML feature
* Use an SSML-compatible cloned or preset voice (such as longanyang for cosyvoice-v3-flash). See this page for supported models.
*/
public class Main {
private static String model = "cosyvoice-v3-flash";
private static String voice = "longanyang";
public static void main(String[] args) {
// Singapore region URL. Replace WorkspaceId with your actual workspace ID. The URL varies by region.
Constants.baseWebsocketApiUrl = "wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference";
streamAudioDataToSpeaker();
System.out.println("Audio has been saved to the output.mp3 file");
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("Failed to create output file: " + e.getMessage());
return;
}
// Implement the ResultCallback interface
ResultCallback<SpeechSynthesisResult> callback = new ResultCallback<SpeechSynthesisResult>() {
@Override
public void onEvent(SpeechSynthesisResult result) {
if (result.getAudioFrame() != null) {
// Write audio data to local file
try {
byte[] audioData = result.getAudioFrame().array();
fileOutputStream[0].write(audioData);
fileOutputStream[0].flush();
} catch (IOException e) {
System.err.println("Failed to write audio data: " + e.getMessage());
}
}
}
@Override
public void onComplete() {
System.out.println("Received Complete, speech synthesis finished");
closeFileOutputStream(fileOutputStream[0]);
latch.countDown();
}
@Override
public void onError(Exception e) {
System.out.println("An error occurred: " + e.toString());
closeFileOutputStream(fileOutputStream[0]);
latch.countDown();
}
};
SpeechSynthesisParam param =
SpeechSynthesisParam.builder()
// The API Keys for the Singapore and Beijing regions are different. Get an API Key: https://www.alibabacloud.com/help/model-studio/get-api-key
// If you have not configured an environment variable, replace the following line with your Model Studio 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 {
// Unidirectional streaming call, returns null immediately (actual results are delivered asynchronously via the callback interface), retrieve binary audio in real time from the onEvent method of the callback interface
// Special characters need to be escaped
synthesizer.call("<speak rate=\"2\">My speaking rate is faster than a normal person's.</speak>");
// Wait for synthesis to complete
latch.await();
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
// Close the WebSocket connection after the task ends
try {
synthesizer.getDuplexApi().close(1000, "bye");
} catch (Exception e) {
System.err.println("Failed to close WebSocket connection: " + e.getMessage());
}
// Ensure the file stream is closed
closeFileOutputStream(fileOutputStream[0]);
}
// The first text transmission requires establishing a WebSocket connection, so the first packet latency includes the connection setup time
System.out.println(
"[Metric] requestId: "
+ synthesizer.getLastRequestId()
+ ", first packet latency (ms): "
+ synthesizer.getFirstPackageDelay());
}
private static void closeFileOutputStream(FileOutputStream fileOutputStream) {
try {
if (fileOutputStream != null) {
fileOutputStream.close();
}
} catch (IOException e) {
System.err.println("Failed to close file stream: " + e.getMessage());
}
}
}
Python SDK
# coding=utf-8
# SSML feature description:
# 1. Only non-streaming calls and unidirectional streaming calls support the SSML feature
# Use an SSML-compatible cloned or preset voice (such as longanyang for cosyvoice-v3-flash). See this page for supported models.
import dashscope
from dashscope.audio.tts_v2 import *
import os
# The API Keys for the Singapore and Beijing regions are different. Get an API Key: https://www.alibabacloud.com/help/model-studio/get-api-key
# If you have not configured an environment variable, replace the following line with your Model Studio API Key: dashscope.api_key = "sk-xxx"
dashscope.api_key = os.environ.get('DASHSCOPE_API_KEY')
# Singapore region URL. Replace WorkspaceId with your actual workspace ID. The URL varies by region.
dashscope.base_websocket_api_url='wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference'
# Model
model = "cosyvoice-v3-flash"
# Voice
voice = "longanyang"
# Instantiate SpeechSynthesizer and pass request parameters such as model and voice in the constructor
synthesizer = SpeechSynthesizer(model=model, voice=voice)
# Non-streaming call, blocks until audio is returned
# Special characters need to be escaped
audio = synthesizer.call("<speak rate=\"2\">My speaking rate is faster than a normal person's.</speak>")
# Save the audio to a local file
with open('output.mp3', 'wb') as f:
f.write(audio)
# The first text transmission requires establishing a WebSocket connection, so the first packet latency includes the connection setup time
print('[Metric] requestId: {}, first packet latency: {} ms'.format(
synthesizer.get_last_request_id(),
synthesizer.get_first_package_delay()))
# coding=utf-8
# SSML feature description:
# 1. Only non-streaming calls and unidirectional streaming calls support the SSML feature
# Use an SSML-compatible cloned or preset voice (such as longanyang for cosyvoice-v3-flash). See this page for supported models.
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
# The API Keys for the Singapore and Beijing regions are different. Get an API Key: https://www.alibabacloud.com/help/model-studio/get-api-key
# If you have not configured an environment variable, replace the following line with your Model Studio API Key: dashscope.api_key = "sk-xxx"
dashscope.api_key = os.environ.get('DASHSCOPE_API_KEY')
# Singapore region URL. Replace WorkspaceId with your actual workspace ID. The URL varies by region.
dashscope.base_websocket_api_url='wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference'
# Model
model = "cosyvoice-v3-flash"
# Voice
voice = "longanyang"
# Define the callback interface
class Callback(ResultCallback):
_player = None
_stream = None
def on_open(self):
# Open the output file for writing audio data
self.file = open("output.mp3", "wb")
print("Connection established: " + get_timestamp())
def on_complete(self):
print("Speech synthesis completed, all results have been received: " + get_timestamp())
if hasattr(self, 'file') and self.file:
self.file.close()
self
# The first text transmission requires establishing a WebSocket connection, so the first packet latency includes the connection setup time
print('[Metric] requestId: {}, first packet latency: {} ms'.format(
self.synthesizer.get_last_request_id(),
self.synthesizer.get_first_package_delay()))
def on_error(self, message: str):
print(f"Speech synthesis error occurred: {message}")
if hasattr(self, 'file') and self.file:
self.file.close()
def on_close(self):
print("Connection closed: " + 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() + " Binary audio length: " + str(len(data)))
# Write audio data to file
self.file.write(data)
callback = Callback()
# Instantiate SpeechSynthesizer and pass request parameters such as model and voice in the constructor
synthesizer = SpeechSynthesizer(
model=model,
voice=voice,
callback=callback,
)
# Assign the synthesizer instance to callback for use in on_complete
callback.synthesizer = synthesizer
# Unidirectional streaming call, send text for synthesis, retrieve binary audio in real time from the on_data method of the callback interface
# Special characters need to be escaped
synthesizer.call("<speak rate=\"2\">My speaking rate is faster than a normal person's.</speak>")
WebSocket API
Java
For Java development, use the Java DashScope SDK. For details, see Java SDK.
This Java WebSocket example requires the following dependencies:
Java-WebSocketjackson-databind
Manage these dependencies with Maven or 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 code:
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 feature description:
* 1. When sending the run-task command, set the enable_ssml parameter to true to enable SSML support
* 2. Send text containing SSML via the continue-task command, and only one continue-task command is allowed
* Use an SSML-compatible cloned or preset voice (such as longanyang for cosyvoice-v3-flash). See this page for supported models.
*/
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("Connection established");
// Send run-task command
// If enable_ssml is set to true, only one continue-task command is allowed; otherwise the error "Text request limit violated, expected 1." will be returned
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("Received message from server: " + 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("Received task-started event from server");
// Send continue-task command. When using SSML, this command can only be sent once
// Special characters need to be escaped
sendContinueTask("<speak rate=\\\"2\\\">My speaking rate is faster than a normal person's.</speak>");
// Send finish-task command
sendFinishTask();
} else if ("task-finished".equals(event)) {
System.out.println("Received task-finished event from server");
taskFinished = true;
closeConnection();
} else if ("task-failed".equals(event)) {
System.out.println("Task failed: " + message);
closeConnection();
}
}
}
} catch (Exception e) {
System.err.println("An error occurred: " + e.getMessage());
}
}
@Override
public void onMessage(ByteBuffer message) {
System.out.println("Received binary audio data, size: " + message.remaining());
try (FileOutputStream fos = new FileOutputStream(outputFile, true)) {
byte[] buffer = new byte[message.remaining()];
message.get(buffer);
fos.write(buffer);
System.out.println("Audio data written to local file " + outputFile);
} catch (IOException e) {
System.err.println("Failed to write audio data to local file: " + e.getMessage());
}
}
@Override
public void onClose(int code, String reason, boolean remote) {
System.out.println("Connection closed: " + reason + " (" + code + ")");
}
@Override
public void onError(Exception ex) {
System.err.println("Error: " + 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 {
// The API Keys for the Singapore and Beijing regions are different. Get an API Key: https://www.alibabacloud.com/help/model-studio/get-api-key
// If you have not configured an environment variable, replace the following line with your Model Studio API Key: String apiKey = "sk-xxx"
String apiKey = System.getenv("DASHSCOPE_API_KEY");
if (apiKey == null || apiKey.isEmpty()) {
System.err.println("Please set the DASHSCOPE_API_KEY environment variable");
return;
}
Map<String, String> headers = new HashMap<>();
headers.put("Authorization", "bearer " + apiKey);
// Singapore region URL. Replace WorkspaceId with your actual workspace ID. The URL varies by region.
TTSWebSocketClient client = new TTSWebSocketClient(new URI("wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference/"), headers);
client.connect();
while (!client.isClosed() && !client.taskFinished) {
Thread.sleep(1000);
}
} catch (Exception e) {
System.err.println("Failed to connect to WebSocket service: " + e.getMessage());
e.printStackTrace();
}
}
}
Python
For Python development, use the Python DashScope SDK. For details, see Python SDK.
The following is a Python WebSocket example. Before running it, install the required dependency:
pip uninstall websocket-client
pip uninstall websocket
pip install websocket-client
ImportantThe Python example file must not be named websocket.py, because this name causes a naming conflict and the following error (AttributeError: module 'websocket' has no attribute 'WebSocketApp'. Did you mean: 'WebSocket'?).
# SSML feature description:
# 1. When sending the run-task command, set the enable_ssml parameter to true to enable SSML support
# 2. Send text containing SSML via the continue-task command, and only one continue-task command is allowed
# Use an SSML-compatible cloned or preset voice (such as longanyang for cosyvoice-v3-flash). See this page for supported models.
import websocket
import json
import uuid
import os
import time
class TTSClient:
def __init__(self, api_key, uri):
"""
Initialize a TTSClient instance
Parameters:
api_key (str): API Key for authentication
uri (str): WebSocket service address
"""
self.api_key = api_key # Replace with your API Key
self.uri = uri # Replace with your WebSocket address
self.task_id = str(uuid.uuid4()) # Generate a unique task ID
self.output_file = f"output_{int(time.time())}.mp3" # Output audio file path
self.ws = None # WebSocketApp instance
self.task_started = False # Whether task-started has been received
self.task_finished = False # Whether task-finished / task-failed has been received
def on_open(self, ws):
"""
Callback when WebSocket connection is established
Sends run-task command to start the speech synthesis task
"""
print("WebSocket connected")
# Construct run-task command
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,
# If enable_ssml is set to True, only one continue-task command is allowed; otherwise the error "Text request limit violated, expected 1." will be returned
"enable_ssml": True
},
"input": {}
}
}
# Send run-task command
ws.send(json.dumps(run_task_cmd))
print("run-task command sent")
def on_message(self, ws, message):
"""
Callback when a message is received
Handles text and binary messages separately
"""
if isinstance(message, str):
# Process JSON text messages
try:
msg_json = json.loads(message)
print(f"Received JSON message: {msg_json}")
if "header" in msg_json:
header = msg_json["header"]
if "event" in header:
event = header["event"]
if event == "task-started":
print("Task started")
self.task_started = True
# Send continue-task command. When using SSML, this command can only be sent once
# Special characters need to be escaped
self.send_continue_task("<speak rate=\"2\">My speaking rate is faster than a normal person's.</speak>")
# Send finish-task after continue-task is sent
self.send_finish_task()
elif event == "task-finished":
print("Task completed")
self.task_finished = True
self.close(ws)
elif event == "task-failed":
error_msg = msg_json.get("error_message", "Unknown error")
print(f"Task failed: {error_msg}")
self.task_finished = True
self.close(ws)
except json.JSONDecodeError as e:
print(f"JSON parsing failed: {e}")
else:
# Process binary messages (audio data)
print(f"Received binary message, size: {len(message)} bytes")
with open(self.output_file, "ab") as f:
f.write(message)
print(f"Audio data written to local file {self.output_file}")
def on_error(self, ws, error):
"""Callback when an error occurs"""
print(f"WebSocket error: {error}")
def on_close(self, ws, close_status_code, close_msg):
"""Callback when connection is closed"""
print(f"WebSocket closed: {close_msg} ({close_status_code})")
def send_continue_task(self, text):
"""Send continue-task command with the text content to synthesize"""
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 command sent, text content: {text}")
def send_finish_task(self):
"""Send finish-task command to end the speech synthesis task"""
cmd = {
"header": {
"action": "finish-task",
"task_id": self.task_id,
"streaming": "duplex"
},
"payload": {
"input": {}
}
}
self.ws.send(json.dumps(cmd))
print("finish-task command sent")
def close(self, ws):
"""Actively close the connection"""
if ws and ws.sock and ws.sock.connected:
ws.close()
print("Connection closed actively")
def run(self):
"""Start the WebSocket client"""
# Set request headers (authentication)
header = {
"Authorization": f"bearer {self.api_key}",
"X-DashScope-DataInspection": "enable"
}
# Create WebSocketApp instance
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("Listening for WebSocket messages...")
self.ws.run_forever() # Start persistent connection listener
# Example usage
if __name__ == "__main__":
# The API Keys for the Singapore and Beijing regions are different. Get an API Key: https://www.alibabacloud.com/help/model-studio/get-api-key
# If you have not configured an environment variable, replace the following line with your Model Studio API Key: API_KEY = "sk-xxx"
API_KEY = os.environ.get("DASHSCOPE_API_KEY")
# Singapore region URL. Replace WorkspaceId with your actual workspace ID. The URL varies by region.
SERVER_URI = "wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference/"
client = TTSClient(API_KEY, SERVER_URI)
client.run()
Go
// SSML feature description:
// 1. When sending the run-task command, set the enable_ssml parameter to true to enable SSML support
// 2. Send text containing SSML via the continue-task command, and only one continue-task command is allowed
// Use an SSML-compatible cloned or preset voice (such as longanyang for cosyvoice-v3-flash). See this page for supported models.
package main
import (
"encoding/json"
"fmt"
"net/http"
"os"
"strings"
"time"
"github.com/google/uuid"
"github.com/gorilla/websocket"
)
const (
// Singapore region URL. Replace WorkspaceId with your actual workspace ID. The URL varies by region.
wsURL = "wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference/"
outputFile = "output.mp3"
)
func main() {
// The API Keys for the Singapore and Beijing regions are different. Get an API Key: https://www.alibabacloud.com/help/model-studio/get-api-key
// If you have not configured an environment variable, replace the following line with your Model Studio API Key: apiKey := "sk-xxx"
apiKey := os.Getenv("DASHSCOPE_API_KEY")
// Clear the output file
os.Remove(outputFile)
os.Create(outputFile)
// Connect to 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("Connection failed, HTTP status code: %d\n", resp.StatusCode)
}
fmt.Println("Connection failed:", err)
return
}
defer conn.Close()
// Generate task ID
taskID := uuid.New().String()
fmt.Printf("Generated task ID: %s\n", taskID)
// Send run-task command
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,
// If enable_ssml is set to true, only one continue-task command is allowed; otherwise the error "Text request limit violated, expected 1." will be returned
"enable_ssml": true,
},
"input": map[string]interface{}{},
},
}
runTaskJSON, _ := json.Marshal(runTaskCmd)
fmt.Printf("Sending run-task command: %s\n", string(runTaskJSON))
err = conn.WriteMessage(websocket.TextMessage, runTaskJSON)
if err != nil {
fmt.Println("Failed to send run-task:", err)
return
}
textSent := false
// Process messages
for {
messageType, message, err := conn.ReadMessage()
if err != nil {
fmt.Println("Failed to read message:", err)
break
}
// Process binary messages
if messageType == websocket.BinaryMessage {
fmt.Printf("Received binary message, length: %d\n", len(message))
file, _ := os.OpenFile(outputFile, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
file.Write(message)
file.Close()
continue
}
// Process text messages
messageStr := string(message)
fmt.Printf("Received text message: %s\n", strings.ReplaceAll(messageStr, "\n", ""))
// Simple JSON parsing to get event type
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("Event type: %s\n", event)
switch event {
case "task-started":
fmt.Println("=== Received task-started event ===")
if !textSent {
// Send continue-task command. When using SSML, this command can only be sent once
continueTaskCmd := map[string]interface{}{
"header": map[string]interface{}{
"action": "continue-task",
"task_id": taskID,
"streaming": "duplex",
},
"payload": map[string]interface{}{
"input": map[string]interface{}{
// Special characters need to be escaped
"text": "<speak rate=\"2\">My speaking rate is faster than a normal person's.</speak>",
},
},
}
continueTaskJSON, _ := json.Marshal(continueTaskCmd)
fmt.Printf("Sending continue-task command: %s\n", string(continueTaskJSON))
err = conn.WriteMessage(websocket.TextMessage, continueTaskJSON)
if err != nil {
fmt.Println("Failed to send continue-task:", err)
return
}
textSent = true
// Delay before sending finish-task
time.Sleep(500 * time.Millisecond)
// Send finish-task command
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("Sending finish-task command: %s\n", string(finishTaskJSON))
err = conn.WriteMessage(websocket.TextMessage, finishTaskJSON)
if err != nil {
fmt.Println("Failed to send finish-task:", err)
return
}
}
case "task-finished":
fmt.Println("=== Task completed ===")
return
case "task-failed":
fmt.Println("=== Task failed ===")
if header["error_message"] != nil {
fmt.Printf("Error message: %s\n", header["error_message"])
}
return
case "result-generated":
fmt.Println("Received result-generated event")
}
}
}
}
}
}
C#
using System.Net.WebSockets;
using System.Text;
using System.Text.Json;
// SSML feature description:
// 1. When sending the run-task command, set the enable_ssml parameter to true to enable SSML support
// 2. Send text containing SSML via the continue-task command, and only one continue-task command is allowed
// Use an SSML-compatible cloned or preset voice (such as longanyang for cosyvoice-v3-flash). See this page for supported models.
class Program {
// The API Keys for the Singapore and Beijing regions are different. Get an API Key: https://www.alibabacloud.com/help/model-studio/get-api-key
// If you have not configured an environment variable, replace the following line with your Model Studio 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.");
// Singapore region URL. Replace WorkspaceId with your actual workspace ID. The URL varies by region.
private const string WebSocketUrl = "wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference/";
// Output file path
private const string OutputFilePath = "output.mp3";
// WebSocket client
private static ClientWebSocket _webSocket = new ClientWebSocket();
// Cancellation token source
private static CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
// Task ID
private static string? _taskId;
// Whether the task has started
private static TaskCompletionSource<bool> _taskStartedTcs = new TaskCompletionSource<bool>();
static async Task Main(string[] args) {
try {
// Clear the output file
ClearOutputFile(OutputFilePath);
// Connect to the WebSocket service
await ConnectToWebSocketAsync(WebSocketUrl);
// Start the message receiving task
Task receiveTask = ReceiveMessagesAsync();
// Send run-task command
_taskId = GenerateTaskId();
await SendRunTaskCommandAsync(_taskId);
// Wait for the task-started event
await _taskStartedTcs.Task;
// Send continue-task command. When using SSML, this command can only be sent once
// Special characters need to be escaped
await SendContinueTaskCommandAsync("<speak rate=\"2\">My speaking rate is faster than a normal person's.</speak>");
// Send finish-task command
await SendFinishTaskCommandAsync(_taskId);
// Wait for the receiving task to complete
await receiveTask;
Console.WriteLine("Task completed, connection closed.");
} catch (OperationCanceledException) {
Console.WriteLine("Task was cancelled.");
} catch (Exception ex) {
Console.WriteLine($"An error occurred: {ex.Message}");
} finally {
_cancellationTokenSource.Cancel();
_webSocket.Dispose();
}
}
private static void ClearOutputFile(string filePath) {
if (File.Exists(filePath)) {
File.WriteAllText(filePath, string.Empty);
Console.WriteLine("Output file cleared.");
} else {
Console.WriteLine("Output file does not exist, no need to clear.");
}
}
private static async Task ConnectToWebSocketAsync(string url) {
var uri = new Uri(url);
if (_webSocket.State == WebSocketState.Connecting || _webSocket.State == WebSocketState.Open) {
return;
}
// Set WebSocket connection headers
_webSocket.Options.SetRequestHeader("Authorization", $"bearer {ApiKey}");
_webSocket.Options.SetRequestHeader("X-DashScope-DataInspection", "enable");
try {
await _webSocket.ConnectAsync(uri, _cancellationTokenSource.Token);
Console.WriteLine("Successfully connected to the WebSocket service.");
} catch (OperationCanceledException) {
Console.WriteLine("WebSocket connection was cancelled.");
} catch (Exception ex) {
Console.WriteLine($"WebSocket connection failed: {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,
// If enable_ssml is set to true, only one continue-task command is allowed; otherwise the error "Text request limit violated, expected 1." will be returned
enable_ssml = true
},
input = new { }
});
await SendJsonMessageAsync(command);
Console.WriteLine("run-task command sent.");
}
private static async Task SendContinueTaskCommandAsync(string text) {
if (_taskId == null) {
throw new InvalidOperationException("Task ID is not initialized.");
}
var command = CreateCommand("continue-task", _taskId, "duplex", new {
input = new {
text
}
});
await SendJsonMessageAsync(command);
Console.WriteLine("continue-task command sent.");
}
private static async Task SendFinishTaskCommandAsync(string taskId) {
var command = CreateCommand("finish-task", taskId, "duplex", new {
input = new { }
});
await SendJsonMessageAsync(command);
Console.WriteLine("finish-task command sent.");
}
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("Message sending was cancelled.");
}
}
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("Task started.");
_taskStartedTcs.TrySetResult(true);
break;
case "task-finished":
Console.WriteLine("Task finished.");
_cancellationTokenSource.Cancel();
break;
case "task-failed":
Console.WriteLine("Task failed: " + response.RootElement.GetProperty("header").GetProperty("error_message").GetString());
_cancellationTokenSource.Cancel();
break;
default:
// result-generated can be handled here
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) {
// Process binary data
Console.WriteLine("Received binary data...");
// Save binary data to file
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("Message receiving was cancelled.");
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
The sample code directory structure is:
my-php-project/
├── composer.json
├── vendor/
└── index.php
The composer.json file contains the following dependencies. Adjust the version numbers as needed:
{
"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/"
}
}
}
The index.php file contains the following code:
<!-- SSML feature description: -->
<!-- 1. When sending the run-task command, set the enable_ssml parameter to true to enable SSML support -->
<!-- 2. Send text containing SSML via the continue-task command, and only one continue-task command is allowed -->
<!-- 3. Only cloned voices of the qwen-audio-3.0-tts-flash, qwen-audio-3.0-tts-plus, cosyvoice-v3-flash, cosyvoice-v3-plus, and cosyvoice-v2 models, as well as system voices marked as SSML-supported in the voice list, support the SSML feature (for example, the longanyang voice of the cosyvoice-v3-flash model) -->
<?php
require __DIR__ . '/vendor/autoload.php';
use Ratchet\Client\Connector;
use React\EventLoop\Loop;
use React\Socket\Connector as SocketConnector;
// The API Keys for the Singapore and Beijing regions are different. Get an API Key: https://www.alibabacloud.com/help/model-studio/get-api-key
// If you have not configured an environment variable, replace the following line with your Model Studio API Key: $api_key = "sk-xxx"
$api_key = getenv("DASHSCOPE_API_KEY");
// Singapore region URL. Replace WorkspaceId with your actual workspace ID. The URL varies by region.
$websocket_url = 'wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference/'; // WebSocket server address
$output_file = 'output.mp3'; // Output file path
$loop = Loop::get();
if (file_exists($output_file)) {
// Clear file contents
file_put_contents($output_file, '');
}
// Create a custom connector
$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 "Connected to WebSocket server\n";
// Generate task ID
$taskId = generateTaskId();
// Send run-task command
sendRunTaskMessage($conn, $taskId);
// Define the function to send continue-task command
$sendContinueTask = function() use ($conn, $loop, $taskId) {
// Send continue-task command. When using SSML, this command can only be sent once
$continueTaskMessage = json_encode([
"header" => [
"action" => "continue-task",
"task_id" => $taskId,
"streaming" => "duplex"
],
"payload" => [
"input" => [
// Special characters need to be escaped
"text" => "<speak rate=\"2\">My speaking rate is faster than a normal person's.</speak>"
]
]
]);
$conn->send($continueTaskMessage);
// Send finish-task command
sendFinishTaskMessage($conn, $taskId);
};
// Flag for whether task-started event has been received
$taskStarted = false;
// Listen for messages
$conn->on('message', function($msg) use ($conn, $sendContinueTask, $loop, &$taskStarted, $taskId, $output_file) {
if ($msg->isBinary()) {
// Write binary data to local file
file_put_contents($output_file, $msg->getPayload(), FILE_APPEND);
} else {
// Process non-binary messages
$response = json_decode($msg, true);
if (isset($response['header']['event'])) {
handleEvent($conn, $response, $sendContinueTask, $loop, $taskId, $taskStarted);
} else {
echo "Unknown message format\n";
}
}
});
// Listen for connection close
$conn->on('close', function($code = null, $reason = null) {
echo "Connection closed\n";
if ($code !== null) {
echo "Close code: " . $code . "\n";
}
if ($reason !== null) {
echo "Close reason: " . $reason . "\n";
}
});
}, function ($e) {
echo "Unable to connect: {$e->getMessage()}\n";
});
$loop->run();
/**
* Generate task ID
* @return string
*/
function generateTaskId(): string {
return bin2hex(random_bytes(16));
}
/**
* Send run-task command
* @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,
// If enable_ssml is set to true, only one continue-task command is allowed; otherwise the error "Text request limit violated, expected 1." will be returned
"enable_ssml" => true
],
"input" => (object) []
]
]);
echo "Preparing to send run-task command: " . $runTaskMessage . "\n";
$conn->send($runTaskMessage);
echo "run-task command sent\n";
}
/**
* Read audio file
* @param string $filePath
* @return bool|string
*/
function readAudioFile(string $filePath) {
$voiceData = file_get_contents($filePath);
if ($voiceData === false) {
echo "Unable to read audio file\n";
}
return $voiceData;
}
/**
* Split audio data
* @param string $data
* @param int $chunkSize
* @return array
*/
function splitAudioData(string $data, int $chunkSize): array {
return str_split($data, $chunkSize);
}
/**
* Send finish-task command
* @param $conn
* @param $taskId
*/
function sendFinishTaskMessage($conn, $taskId) {
$finishTaskMessage = json_encode([
"header" => [
"action" => "finish-task",
"task_id" => $taskId,
"streaming" => "duplex"
],
"payload" => [
"input" => (object) []
]
]);
echo "Preparing to send finish-task command: " . $finishTaskMessage . "\n";
$conn->send($finishTaskMessage);
echo "finish-task command sent\n";
}
/**
* Handle events
* @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 "Task started, sending continue-task command...\n";
$taskStarted = true;
// Send continue-task command
$sendContinueTask();
break;
case 'result-generated':
// Ignore result-generated events
break;
case 'task-finished':
echo "Task completed\n";
$conn->close();
break;
case 'task-failed':
echo "Task failed\n";
echo "Error code: " . $response['header']['error_code'] . "\n";
echo "Error message: " . $response['header']['error_message'] . "\n";
$conn->close();
break;
case 'error':
echo "Error: " . $response['payload']['message'] . "\n";
break;
default:
echo "Unknown event: " . $response['header']['event'] . "\n";
break;
}
// If the task is completed, close the connection
if ($response['header']['event'] == 'task-finished') {
// Wait 1 second to ensure all data has been transmitted
$loop->addTimer(1, function() use ($conn) {
$conn->close();
echo "Client closed connection\n";
});
}
// If task-started event has not been received, close the connection
if (!$taskStarted && in_array($response['header']['event'], ['task-failed', 'error'])) {
$conn->close();
}
}
Node.js
Install the required dependencies:
npm install ws
npm install uuid
Sample code:
// SSML feature description:
// 1. When sending the run-task command, set the enable_ssml parameter to true to enable SSML support
// 2. Send text containing SSML via the continue-task command, and only one continue-task command is allowed
// Use an SSML-compatible cloned or preset voice (such as longanyang for cosyvoice-v3-flash). See this page for supported models.
import fs from 'fs';
import WebSocket from 'ws';
import { v4 as uuid } from 'uuid'; // For generating UUIDs
// The API Keys for the Singapore and Beijing regions are different. Get an API Key: https://www.alibabacloud.com/help/model-studio/get-api-key
// If you have not configured an environment variable, replace the following line with your Model Studio API Key: const apiKey = "sk-xxx"
const apiKey = process.env.DASHSCOPE_API_KEY;
// Singapore region URL. Replace WorkspaceId with your actual workspace ID. The URL varies by region.
const url = 'wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference/';
// Output file path
const outputFilePath = 'output.mp3';
// Clear the output file
fs.writeFileSync(outputFilePath, '');
// Create WebSocket client
const ws = new WebSocket(url, {
headers: {
Authorization: `bearer ${apiKey}`,
'X-DashScope-DataInspection': 'enable'
}
});
let taskStarted = false;
let taskId = uuid();
ws.on('open', () => {
console.log('Connected to WebSocket server');
// Send run-task command
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', // Voice
format: 'mp3', // Audio format
sample_rate: 22050, // Sample rate
volume: 50, // Volume
rate: 1, // Speech rate
pitch: 1, // Pitch
enable_ssml: true // Whether to enable SSML. If enable_ssml is set to true, only one continue-task command is allowed; otherwise the error "Text request limit violated, expected 1." will be returned
},
input: {}
}
});
ws.send(runTaskMessage);
console.log('run-task message sent');
});
const fileStream = fs.createWriteStream(outputFilePath, { flags: 'a' });
ws.on('message', (data, isBinary) => {
if (isBinary) {
// Write binary data to file
fileStream.write(data);
} else {
const message = JSON.parse(data);
switch (message.header.event) {
case 'task-started':
taskStarted = true;
console.log('Task started');
// Send continue-task command
sendContinueTasks(ws);
break;
case 'task-finished':
console.log('Task completed');
ws.close();
fileStream.end(() => {
console.log('File stream closed');
});
break;
case 'task-failed':
console.error('Task failed:', message.header.error_message);
ws.close();
fileStream.end(() => {
console.log('File stream closed');
});
break;
default:
// result-generated can be handled here
break;
}
}
});
function sendContinueTasks(ws) {
if (taskStarted) {
// Send continue-task command. When using SSML, this command can only be sent once
const continueTaskMessage = JSON.stringify({
header: {
action: 'continue-task',
task_id: taskId,
streaming: 'duplex'
},
payload: {
input: {
// Special characters need to be escaped
text: '<speak rate="2">My speaking rate is faster than a normal person's.</speak>'
}
}
});
ws.send(continueTaskMessage);
// Send finish-task command
const finishTaskMessage = JSON.stringify({
header: {
action: 'finish-task',
task_id: taskId,
streaming: 'duplex'
},
payload: {
input: {}
}
});
ws.send(finishTaskMessage);
}
}
ws.on('close', () => {
console.log('Disconnected from WebSocket server');
});
LaTeX
Usage
Enclose mathematical formulas in delimiters, then call the speech synthesis API.
Supported delimiters (choose any one; the effect is the same):
$...$$$...$$\(...\)\[...\]
Example input:
这是一道分数:$\frac{1}{2}$。
The fraction is read in Chinese as “二分之一” (one half).
Use the same delimiters for formulas with multiple operators, for example:
这是一道一元二次方程的求根公式:$x = \frac{-b \pm \sqrt{b^2-4ac}}{2a}$,请仔细计算。
In JSON and regular strings in languages such as Python, Java, and JavaScript, write a backslash \ as \\. For example, write the LaTeX command \frac as \\frac in a regular string.
Quick start
This Python example synthesizes Chinese text containing a fraction and saves it as output.mp3. See the SSML quick start for API key and SDK setup.
# coding=utf-8
import os
import dashscope
from dashscope.audio.tts_v2 import *
# If the environment variable is not set, replace the next line with dashscope.api_key = "sk-xxx".
dashscope.api_key = os.environ.get('DASHSCOPE_API_KEY')
# Replace WorkspaceId with your workspace ID. Use the endpoint for your region.
dashscope.base_websocket_api_url='wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference'
model = "cosyvoice-v3-flash"
voice = "longanyang"
synthesizer = SpeechSynthesizer(model=model, voice=voice)
# Escape each backslash as \\ in a regular string.
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 tags
NoteThe implementation is based on the W3C SSML 1.0 specification. Only the tags listed below are supported.
<speak>: Root element
<speak> is the root element for all SSML content. All text must be enclosed within <speak></speak> tags.
<speak>Text that requires SSML processing</speak>
Attributes
Attribute | Type | Required | Description |
|---|---|---|---|
| String | No | Specifies the voice. This attribute takes precedence over the
|
| String | No | Specifies the speech rate. This attribute takes precedence over the
|
| String | No | Specifies the pitch. This attribute takes precedence over the
|
| String | No | Specifies the volume. This attribute takes precedence over the
|
| String | No | Specifies the audio effect.
|
| String | No | Configures the specific behavior of the audio effect (the
|
| String | No | Adds background music to synthesized speech. Store the audio file in Alibaba Cloud OSS and make it accessible through an Internet URL. See Upload objects. For a private object, use a valid signed URL. Escape XML special characters in the URL, such as replacing
ImportantThe uploader bears legal responsibility for the copyright of uploaded audio. |
| String | No | Specifies the background music volume. Use this attribute together with the |
Examples
No attributes
<speak>
Text that requires SSML tags
</speak>
Combined attributes (space-separated)
<speak rate="1.5" pitch="0.8" volume="80">
So when put together, my voice sounds like this.
</speak>
voice
<speak voice="longcheng_v2">
I am a male voice.
</speak>
rate
<speak rate="2">
My speech rate is faster than normal.
</speak>
pitch
<speak pitch="0.5">
However, my pitch is lower than others.
</speak>
volume
<speak volume="80">
My volume is also very high.
</speak>
effect
<speak effect="robot">
Do you like the robot WALL-E?
</speak>
effectValue
<speak effect="eq" effectValue="1 -20 1 1 1 1 20 1">
Do you like the robot WALL-E?
</speak>
<speak effect="lpfilter" effectValue="1200">
Do you like the robot WALL-E?
</speak>
<speak effect="hpfilter" effectValue="1200">
Do you like the robot WALL-E?
</speak>
bgm
<speak bgm="https://nls.alicdn.com/bgm/2.wav" backgroundMusicVolume="30" rate="0.8" volume="40">
<break time="2s"/>
The old trees on the shady cliff are shrouded in mist
<break time="700ms"/>
The sound of rain is still in the bamboo forest
<break time="700ms"/>
I know that cotton contributes to the country's plan
<break time="700ms"/>
The scenery of Mianzhou is always pitiable
<break time="2s"/>
</speak>
Tag relationships
The <speak> tag can contain text and the following child tags:
<break>: Control pause duration<sub>: Substitute text<phoneme>: Specify pronunciation (pinyin/phonetic)<soundEvent>: Insert an external sound (ringtone, cat meow, etc.)<say-as>: Set text interpretation (numbers, dates, phone numbers, etc.)
<break>: Control pauses
Inserts a silent pause during speech synthesis to simulate natural pauses in conversation. Supports seconds (s) and milliseconds (ms) as time units.
<!-- No attributes -->
<break/>
<!-- With time attribute -->
<break time="string"/>
Attributes
Attribute | Type | Required | Description |
|---|---|---|---|
| String | No | Specifies the pause duration, in seconds or milliseconds (for example, "2s" or "50ms").
When multiple |
Without attributes, <break> inserts a 1-second pause.
Examples
time
<speak>
Please close your eyes and take a rest.<break time="500ms"/>Okay, please open your eyes.
</speak>
The three consecutive <break> tags below specify a total pause of 15 seconds. The actual pause is capped at 10 seconds.
<speak>
Please close your eyes and take a rest.<break time="5s"/><break time="5s"/><break time="5s"/>Okay, please open your eyes.
</speak>
Tag relationships
<break> is a self-closing element and cannot contain child elements.
<sub>: Substitute text
Replaces specified text with content that's more suitable for speech. For example, reads "W3C" as "World Wide Web Consortium."
<sub alias="string"></sub>
Attributes
Attribute | Type | Required | Description |
|---|---|---|---|
| String | Yes | Specifies the replacement text to be read aloud. |
Examples
alias
<speak>
<sub alias="network protocol">W3C</sub>
</speak>
Tag relationships
The <sub> tag can contain only plain text.
<phoneme>: Specify pronunciation
Provides precise control over how text is pronounced. Chinese text supports pinyin notation, and English text supports CMU phonetic notation. This is useful for disambiguating polyphonic characters and handling foreign language pronunciation.
<phoneme alphabet="string" ph="string">Text</phoneme>
Attributes
Attribute | Type | Required | Description |
|---|---|---|---|
| String | Yes | Specifies the pronunciation type: pinyin (for Chinese) or phonetic symbols (for English). Valid values:
|
| String | Yes | Specifies the exact pinyin or phonetic notation. Usage rules:
|
Examples
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>
Tag relationships
The <phoneme> tag can contain only plain text.
<soundEvent>: Insert a sound effect
Inserts a sound effect file (such as an alert tone or ambient sound) at a specific point in the speech to enrich the audio output.
<soundEvent src="URL"/>
Attributes
Attribute | Type | Required | Description |
|---|---|---|---|
| String | Yes | Specifies the URL of an external audio file. Store the audio file in Alibaba Cloud OSS and make it accessible through an Internet URL. See Upload objects. For a private object, use a valid signed URL. Escape XML special characters in the URL, such as replacing
ImportantThe uploader bears legal responsibility for the copyright of uploaded audio. |
Examples
src
<speak>
A horse was frightened<soundEvent src="https://nls.alicdn.com/sound-event/horse-neigh.wav"/>and people scattered to avoid it.
</speak>
Tag relationships
<soundEvent> is a self-closing element and cannot contain child elements.
<say-as>: Set text interpretation
Specifies the content type of text (such as numbers, dates, or phone numbers) so the system reads it according to the appropriate rules for that type.
<say-as interpret-as="string">Text</say-as>
Attributes
Attribute | Type | Required | Description |
|---|---|---|---|
| String | Yes | Specifies the content type of the text within the tag. Valid values:
|
View the supported say-as formats
cardinal
Chinese
Format | Example | Chinese output | Notes |
|---|---|---|---|
Digit string | 145 | 一百四十五 | Positive and negative integers up to 20 digits: [-99999999999999999999,99999999999999999999]. Decimal places have no specific limit; 10 or fewer are recommended. |
Minus sign+Digit string | -145 | 负一百四十五 | |
Comma-separated groups of 3 digits | 10,000 | 一万 | |
Minus sign+Comma-separated groups of 3 digits | -10,124 | 负一万一百二十四 | |
Digit string+Decimal point+Two zeros | 10.00 | 十 | |
Minus sign+Digit string+Decimal point+Two zeros | -110.00 | 负一百一十 | |
Digit string+Decimal point+Digit string | 79.090 | 七十九点零九零 | |
Minus sign+Digit string+Decimal point+Digit string | -79.001 | 负七十九点零零一 |
English
Format | Example | English output | Notes |
|---|---|---|---|
Digit string | 145 | one hundred forty five | Integer range: positive and negative integers up to 13 digits, [-999999999999,999999999999]. Decimal range: no specific limit on decimal places, but 10 or fewer is recommended. |
Digit string starting with zero | 0145 | one hundred forty five | |
Minus sign + digit string | -145 | minus hundred forty five | |
Digit string with comma separating every 3 digits | 60,000 | sixty thousand | |
Minus sign + comma-separated digit string | -208,000 | minus two hundred eight thousand | |
Digit string + decimal point + zero | 12.00 | twelve | |
Digit string + decimal point + digit string | 12.34 | twelve point three four | |
Comma-separated digit string + decimal point + digit string | 1,000.1 | one thousand point one | |
Minus sign + digit string + decimal point + digit string | -12.34 | minus twelve point three four | |
Minus sign + comma-separated digit string + decimal point + digit string | -1,000.1 | minus one thousand point one | |
(Comma-separated) digit string + hyphen + (comma-separated) digit string | 1-1,000 | one to one thousand | |
Other default readings | 012.34 | twelve point three four | None |
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
Chinese
Format | Example | Chinese output | Notes |
|---|---|---|---|
Digit string | 129090909 | 一二九零九零九零九 | Digit string length has no specific limit; 20 digits or fewer are recommended. A pause follows each digit when the string has more than 10 digits. |
English
Format | Example | English output | Notes |
|---|---|---|---|
Digit string | 12034 | one two zero three four | No specific limit on digit string length, but 20 or fewer digits is recommended. When digit strings are grouped by spaces or hyphens, a comma pause is inserted between groups. Up to 5 groups are supported. |
Digit string + space/hyphen + digit string + space/hyphen + digit string + space/hyphen + digit string | 1-23-456 7890 | one, two three, four five six, seven eight nine zero |
telephone
Chinese
Format | Example | Chinese output | Notes |
|---|---|---|---|
Landline number | 4930286 | 四九三 零二八六 | Supports 7- or 8-digit landline numbers separated by spaces or hyphens (-). Group 7 digits as 3-4 and 8 digits as 4-4. |
493 0286 | 四九三 零二八六 | ||
493-0286 | 四九三 零二八六 | ||
62552560 | 六二五五 二五六零 | ||
6255 2560 | 六二五五 二五六零 | ||
6255-2560 | 六二五五 二五六零 | ||
Landline number+Extension | 4930286-109 | 四九三 零二八六 转幺零九 | Supports 1- to 4-digit extensions. |
4930286转109 | 四九三 零二八六 转幺零九 | ||
4930286分机109 | 四九三 零二八六 分机幺零九 | ||
4930286分机号109 | 四九三 零二八六 分机号幺零九 | ||
Area code+Landline number | 01062552560 | 零幺零 六二五五 二五六零 | Supported area codes: 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 | 三幺九 八九零 七零九八 | ||
Area code+Landline number+Extension | 010 62552560-109 | 零幺零 六二五五 二五六零 转幺零九 | None |
010-62552560-109 | 零幺零 六二五五 二五六零 转幺零九 | ||
(010)62552560-109 | 零幺零 六二五五 二五六零 转幺零九 | ||
(010)62552560转109 | 零幺零 六二五五 二五六零 转幺零九 | ||
(010)62552560分机109 | 零幺零 六二五五 二五六零 分机幺零九 | ||
(010)62552560分机号109 | 零幺零 六二五五 二五六零 分机号幺零九 | ||
Country code+Area code+Landline number | 86-010-62791627 | 八六 零幺零 六二七九 幺六二七 | Country codes 86, (86), +86, (+86), and 0086 are all read as “八六” (eight six). |
(86)10-62791627 | 八六 幺零 六二七九 幺六二七 | ||
+86-010-62791627 | 八六 零幺零 六二七九 幺六二七 | ||
0086-10-62791627 | 八六 幺零 六二七九 幺六二七 | ||
(+86)-10-6279 1627 | 八六 幺零 六二七九 幺六二七 | ||
Country code+Area code+Landline number+Extension | (86)21-58118818-207 | 八六 二幺 五八幺幺 八八幺八 转二零七 | None |
(86)021-5811-8818-207 | 八六 零二幺 五八幺幺 八八幺八 转二零七 | ||
(86)021-58118818转207 | 八六 零二幺 五八幺幺 八八幺八 转二零七 | ||
(86)21-5811-8818分机207 | 八六 二幺 五八幺幺 八八幺八 分机二零七 | ||
+86-021-58118818分机号207 | 八六 零二幺 五八幺幺 八八幺八分机号二零七 | ||
Mobile number | 139 0000 5678 | 幺三九 零零零零 五六七八 | Supports 11-digit mobile numbers grouped as 3-3-5 or 3-4-4. |
139-000-05678 | 幺三九 零零零 零五六七八 | ||
139 000 05678 | 幺三九 零零零 零五六七八 | ||
Country code+Mobile number | +86-13900005678 | 八六 幺三九 零零零零 五六七八 | None |
(+86)-139-0000-5678 | 八六 幺三九 零零零零 五六七八 | ||
+8613900005678 | 八六 幺三九 零零零零 五六七八 | ||
0086-139 000 05678 | 八六 幺三九 零零零 零五六七八 | ||
Service number | 123 | 幺二三 | Supports common service numbers, 10-digit numbers starting with 400/800 grouped as 3-3-4, and 16-digit numbers starting with 12530/17951/12593. |
95678 | 九五六七八 | ||
4008110510 | 四零零 八幺幺 零五幺零 | ||
800-810-8888 | 八零零 八幺零 八八八八 | ||
1253013520638377 | 幺二五三零 幺三五 二零六三 八三七七 | ||
Other | (86)(21)9899-80800-0909 | 八六 二幺 九八九九 八零八零零 零九零九 | Supports digit strings with separators (parentheses or hyphens). |
English
Format | Example | English output | Notes |
|---|---|---|---|
Digit string | 12034 | one two oh three four | No specific limit on digit string length, but 20 or fewer digits is recommended.When digit strings are grouped by spaces or hyphens, a comma pause is inserted between groups. Up to 5 groups are supported. |
Digit string + space/hyphen + digit string + space/hyphen + digit string | 1-23-456 7890 | one, two three, four five six, seven eight nine oh | |
Plus sign + digit string + space/hyphen + digit string | +43-211-0567 | plus four three, two one, oh five six seven | |
Left paren + digit string + right paren + space + digit string + space/hyphen + digit string | (21) 654-3210 | (two one) six five four, three two one oh |
address
Chinese
Format | Example | Chinese output | Notes |
|---|---|---|---|
Common address format | 元和镇嘉元30-9 | 元和镇嘉元三十杠九 | Supports common address formats. An address here means a standard postal address. |
市台路388弄1107-1108号 | 市台路三八八弄幺幺零七杠幺幺零八号 | ||
华润二十四城六期锦云府3-1-3205 | 华润二十四城六期锦云府三杠一杠三二零五 | ||
圣华名都大厦2幢2006室 | 圣华名都大厦二幢二零零六室 | ||
五常街道庭院5幢4单元201 | 五常街道庭院五幢四单元二零幺 | ||
芙蓉江路150弄19号 | 芙蓉江路幺五零弄十九号 |
English
This type is not supported for English text.
id
Chinese
Format | Example | Chinese output | Notes |
|---|---|---|---|
String | dell0101 | D E L L 零 一 零 一 | Supports uppercase and lowercase English letters, digits 0-9, and underscores. Spaces in the output indicate pauses between individually read characters. |
myid_1998 | M Y I D 下划线 一 九 九 八 | ||
AiTest | A I T E S T |
English
For English text, this type works the same as characters.
characters
Chinese
Format | Example | Chinese output | Notes |
|---|---|---|---|
String | ISBN 1-001-099098-1 | I S B N 一 杠 零 零 一 杠 零 九 九 零 九 八 杠 一 | Supports Chinese characters, uppercase and lowercase English letters, digits 0-9, and some full-width and half-width characters. Spaces indicate pauses between individually read characters. Escape XML special characters in the text. |
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 三 三 零 | ||
αβγ | 阿尔法 贝塔 伽玛 |
English
Format | Example | English output | Notes |
|---|---|---|---|
String | *b+3$.c-0'=α | asterisk B plus three dollar dot C dash zero apostrophe equals alpha | Supports Chinese characters, uppercase and lowercase letters, digits 0-9, and some full-width and half-width characters. Spaces in the output indicate pauses between characters, meaning each character is read individually. If the text inside the tag contains XML special characters, escape them. |
punctuation
Chinese
Format | Example | Chinese output | Notes |
|---|---|---|---|
Punctuation | … | 省略号 | Supports common Chinese and English punctuation. Spaces indicate pauses between individually read characters. Escape XML special characters in the text. |
…… | 省略号 | ||
!"#$%& | 叹号 双引号 井号 dollar 百分号 and | ||
‘()*+ | 单引号 左括号 右括号 星号 加号 | ||
,-./:; | 逗号 杠 点 斜杠 冒号 分号 | ||
<=>?@ | 小于 等号 大于 问号 at | ||
[\]^_ | 左方括号 反斜线 右方括号 脱字符 下划线 |
English
For English text, this type works the same as characters.
date
Chinese
Format | Example | Chinese output | Notes |
|---|---|---|---|
xx年 | 71年 | 七一年 | Supports 2-digit years in 60-99, 00-09, and 10-19, and 4-digit years in 1000-1999 and 2000-2099. |
04年 | 零四年 | ||
19年 | 一九年 | ||
1011年 | 一零一一年 | ||
1998年 | 一九九八年 | ||
2008年 | 二零零八年 | ||
xx年xx月 | 98年4月 | 九八年四月 | Months 1-9 can have a leading zero or omit it, for example “1908年4月” and “1908年04月”. |
1998年04月 | 一九九八年四月 | ||
08年8月 | 零八年八月 | ||
2008年8月 | 二零零八年八月 | ||
xx年xx月xx日xx年xx月xx号 | 98年4月23日 | 九八年四月二十三日 | Days 1-9 can have a leading zero or omit it, for example “1908年4月8日” and “1908年04月08日”. |
1998年04月23日 | 一九九八年四月二十三日 | ||
08年8月8号 | 零八年八月八号 | ||
2008年08月08号 | 二零零八年八月八号 | ||
xx年xx月xx日xx年xx月xx号 | 98年4月23日 | 九八年四月二十三日 | Days 1-9 can have a leading zero or omit it, for example “1908年4月8日” and “1908年04月08日”. |
1998年04月23日 | 一九九八年四月二十三日 | ||
08年8月8号 | 零八年八月八号 | ||
2008年08月08号 | 二零零八年八月八号 | ||
xx月xx号 | 3月20日 | 三月二十日 | None |
08月07号 | 八月七号 | ||
Year/month abbreviation | 2018/08 | 二零一八年八月 | Supports /, -, and . as separators. |
2018-08 | 二零一八年八月 | ||
2018.08 | 二零一八年八月 | ||
Year/month/day abbreviation | 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日 | 零四年九月一日至三十日 | Supports ~ and - to indicate “to”. |
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号 | 十月一号至七号 | ||
Year/month/day abbreviation~Year/month/day abbreviation | 2018/03/03~2019/01/01 | 二零一八年三月三日至二零一九年一月一日 | Supports / and . as separators, and ~ and - to indicate “to”. |
1997.9.9~1998.9.9 | 一九九七年九月九日至一九九八年九月九日 | ||
Month/day abbreviation~Month/day abbreviation | 10/20~10/31 | 十月二十日至十月三十一日 | |
xx~xx月xx月~xx月 | 1~10月 | 一至十月 | |
1月~10月 | 一月至十月 | ||
Month/day/year abbreviation | 10/20/2018 | 二零一八年十月二十日 | Only 4-digit years, / separators, and month/day/year order are supported. |
English
Format | Example | English output | Notes |
|---|---|---|---|
Four digits/two digits or four digits-two digits | 2000/01 | two thousand, oh one | Spans across years. |
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 | ||
Four-digit number starting with 1 or 2 | 2000 | two thousand | 4-digit year. |
1900 | nineteen hundred | ||
1905 | nineteen oh five | ||
2021 | twenty one | ||
Day - day of week (hyphen) or Day - day of week or Day & day of week | mon-wed | monday to wednesday | If the text in the day-of-the-week range tag contains special XML characters, escape the characters. |
tue~fri | tuesday to friday | ||
sat&sun | saturday and sunday | ||
DD-DD MMM, YYYY or DD~DD MMM, YYYY or DD&DD MMM, YYYY | 19-20 Jan, 2000 | the nineteen to the twentieth of january two thousand | DD: 2-digit day. MMM: 3-letter month abbreviation or full word. YYYY: 4-digit year starting with 1 or 2. |
01 ~ 10 Jul, 2020 | the first to the tenth of july twenty | ||
05&06 Apr, 2009 | the fifth and the sixth of april two thousand nine | ||
MMM DD-DD or MMM DD~DD or MMM DD&DD | Feb 01 - 03 | feburary the first to the third | MMM: 3-letter month abbreviation or full word. DD: 2-digit day. |
Aug 10~20 | august the tenth to the twentieth | ||
Dec 11&12 | december the eleventh and the twelfth | ||
MMM-MMM or MMM~MMM or MMM&MMM | Jan-Jun | january to june | MMM: 3-letter month abbreviation or full word. |
jul ~ dec | july to december | ||
sep&oct | september and october | ||
YYYY-YYYY or YYYY~YYYY | 1990 - 2000 | nineteen ninety to two thousand | YYYY: 4-digit year starting with 1 or 2. |
2001~2021 | two thousand one to twenty one | ||
WWW DD MMM YYYY | Sun 20 Nov 2011 | sunday the twentieth of november twenty eleven | WWW is the three-letter abbreviation or full name for a day of the week. DD is a two-digit day. MMM is the three-letter abbreviation or full name for a month. MM is a two-digit month (or the three-letter abbreviation or full name for a month). YYYY is a four-digit year starting with 1 or 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 | |
Other default readings | 10 Mar, 2001 | the tenth of march two thousand one | None |
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
Chinese
Format | Example | Chinese output | Notes |
|---|---|---|---|
Time | 12:00 | 十二点 | Supports common time and time range formats. |
12:00:00点 | 十二点 | ||
10:20分 | 十点二十分 | ||
10:20:30 | 十点二十分三十秒 | ||
09:18:14 | 九点十八分十四秒 | ||
Time~Time | 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 | 上午十点二十分到下午十三点三十分 | ||
Time abbreviation | 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 | 中午十二点二十分十二秒 |
English
Format | Example | English output | Notes |
|---|---|---|---|
HH:MM AM or PM | 09:00 AM | nine A M | HH: 1 or 2-digit hour. MM: 2-digit minute. AM/PM: morning/afternoon. |
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 | |
Time point-Time point | 8:00 am - 05:30 pm | eight a m to five p m | Supports common time formats and ranges. |
7:05~10:15 AM | seven oh five to ten fifteen A M | ||
09:00-13:00 | nine oclock to thirteen hundred |
currency
Chinese
Format | Example | Chinese output | Notes |
|---|---|---|---|
Number+Currency identifier | 12.00 RMB | 十二人民币 | Supports AUD (Australian dollar), CAD (Canadian dollar), HKD (Hong Kong dollar), JPY (Japanese yen), USD (US dollar), CHF (Swiss franc), NOK (Norwegian krone), SEK (Swedish krona), GBP (British pound), RMB (renminbi), CNY (yuan), and EUR (euro). Numbers can be integers, decimals, or comma-separated international notation. |
12.50 RMB | 十二点五零人民币 | ||
12,000,000 RMB | 一千二百万人民币 | ||
12,000,000.00 RMB | 一千二百万人民币 | ||
12,000.35 RMB | 一万两千点三五人民币 | ||
Currency identifier+Number | $12 | 十二美元 | Supports CAD (Canadian dollar), $ (US dollar), Fr (franc), kr (Danish krone), £ (British pound), ¥ (yuan), and € (euro). Numbers can be integers, decimals, or comma-separated international notation. |
$12.00 | 十二美元 | ||
$12.12 | 二点一二美元 | ||
$12,000 | 一万两千美元 | ||
$12,000.00 | 一万两千美元 | ||
$12,000.99 | 一万两千点九九美元 | ||
Other default readings | 1213 | 一千二百一十三 | None |
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 | 一万两千 |
English
Format | Example | English output | Notes |
|---|---|---|---|
Number + currency identifier | 1.00 RMB | one yuan | Supported number formats: integers, decimals, and comma-separated international notation. Supported currency identifiers: 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 | ||
Currency identifier + number | US$ 1.00 | one US dollar | Supported number formats: integers, decimals, and comma-separated international notation. Supported currency identifiers: 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 | ||
Number + classifier + currency identifier or Currency identifier + number+Quantifier | 1.23 Tn RMB | one point two three trillion yuan | Supported classifier formats: 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
Chinese
Format | Example | Chinese output | Notes |
|---|---|---|---|
Number+Chinese unit | 2片 | 两片 | Supports common Chinese units and unit abbreviations. |
120公顷 | 一百二十公顷 | ||
100多毫克 | 一百多毫克 | ||
100来米 | 一百来米 | ||
100余人 | 一百余人 | ||
1厘米20毫米 | 一厘米二十毫米 | ||
120.00平方公里 | 一百二十平方公里 | ||
Number+Unit abbreviation | 120.56 cm² | 一百二十点五六平方厘米 | |
120 ㎡ 56 cm² | 一百二十平方米五十六平方厘米 | ||
100 m 12 cm 6 mm | 一百米十二厘米六毫米 | ||
Range | 10~15 kg | 十至十五千克 | |
10.24~789.82亩 | 十点二四至七百八十九点八二亩 | ||
10米~15米 | 十米至十五米 | ||
10.24 cm~19.08 cm | 十点二四厘米至十九点零八厘米 | ||
Number+Unit+"/"+Unit | 10元/斤 | 十元每斤 | |
199~299元/件 | 一百九十九至二百九十九元每件 | ||
299.99元/g~399.99元/g | 二百九十九点九九元每克至三百九十九点九九元每克 | ||
Other default readings | 12扎 | 十二扎 | |
30 rm | 三十r m | ||
4万万同胞 | 四万万同胞 | ||
12.897微克 | 十二点八九七微克 |
English
Format | Example | English output | Notes |
|---|---|---|---|
Number + unit of measurement | 1.0 kg | one kilogram | Supported number formats: integers, decimals, and comma-separated international notation. Supports common unit abbreviations. |
1,234.01 km | one thousand two hundred thirty four point zero one kilometres. | ||
Unit of measurement | mm2 | square millimetre |
Common symbols
Symbol | Chinese reading | English reading |
|---|---|---|
! | 叹号 | 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 |
Common units
Format | Category | Chinese examples | English examples |
|---|---|---|---|
Abbreviation | Length | nm(纳米)、μm(微米)、 mm(毫米)、cm(厘米)、m(米)、km(千米)、ft(英尺)、in(英寸) | nm (nanometre), μm (micrometre), mm (millimetre), cm (centimetre), m (metre), km (kilometre), ft (foot), in (inch) |
Area | cm²(平方厘米)、㎡(平方米)、km²(平方千米)、SqFt(平方英尺) | cm² (square centimetre), ㎡ (square metre), km2 (square kilometre), SqFt (square foot) | |
Volume | cm³(立方厘米)、m³(立方米)、km³(立方千米)、mL(毫升)、L(升)、gallon(加仑) | cm³ (cubic centimetre), m³ (cubic metre), km3 (cubic kilometre), mL (millilitre), L (millilitre), gal (gallon) | |
Weight | μg(微克)、mg(毫克)、g(克)、kg(千克) | μg (microgram), mg (microgram), g (gram), kg (kilogram) | |
Time | min(分)、sec(秒)、ms(毫秒) | min (minute), sec (second), ms (millisecond) | |
Electromagnetism | μ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) | |
Sound | dB(分贝) | dB (decibel) | |
Pressure | Pa(帕)、kPa(千帕)、Mpa(兆帕) | Pa (pascal), kPa (kilopascal), MPa (megapascal) | |
Other common units | Also supports Chinese units beyond these categories, such as “米”, “秒”, “美元”, and “毫升每瓶”, and Chinese measure words such as “架”, “场”, “头”, “部”, and “盆”. | Also supports other units, such as tsp (teaspoon), rpm (round per minute), KB (kilobyte), and mmHg (millimetre of mercury). | |
Examples
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>
Tag relationships
The <say-as> tag can contain text.
LaTeX tags and symbols
Select a category to view supported tags and symbols. Input text examples include delimiters. Example readings remain in Chinese because this feature produces Chinese speech.
Basic mathematics
Tag or symbol | Meaning | Formula source example | Input text example | Example reading |
|---|---|---|---|---|
| Addition |
|
| 二加三等于五 |
| Subtraction |
|
| 三减二等于一 |
| Plus or minus |
|
| 正负一加减二 |
Positive or negative | ||||
| Multiplication |
|
| 二乘三等于六 |
|
|
| ||
|
|
| ||
| Division |
|
| 六除二等于三 |
|
|
| ||
|
|
| ||
| Equals |
|
| 三加五等于八 |
| Less than |
|
| 一小于二 |
| Less than or equal to |
|
| 三小于等于五 |
|
|
| ||
|
|
| ||
|
|
| ||
|
|
| ||
|
|
| ||
| Greater than |
|
| 二大于一 |
| Greater than or equal to |
|
| 五大于等于三 |
|
|
| ||
|
|
| ||
|
|
| ||
|
|
| ||
|
|
| ||
| Fraction |
|
| 三分之二 |
| Power |
|
| 二的一次方 |
| Root |
|
| 根号下九等于三 |
|
| 三次根号下八等于二 | ||
| Percentage |
|
| 百分之五 |
| Absolute value |
|
| 绝对值三的绝对值等于三 |
|
|
| 绝对值三绝对值等于三 | |
| Logarithm |
|
| log十 |
| Logarithm |
|
| log五 |
| Natural logarithm |
|
| LN十 |
| Factorial |
|
| 五的阶乘 |
| Brackets |
|
| 括号内二加一 |
|
| 大括号二加一反大括号 |
Special mathematical symbols
Tag or symbol | Meaning | Formula source example | Input text example | Example reading |
|---|---|---|---|---|
| alpha |
|
| α |
|
|
| ||
| beta |
|
| β |
|
|
| ||
| gamma |
|
| γ |
|
|
| ||
| delta |
|
| Δ |
|
|
| ||
| Infinity (Chinese) |
|
| 无穷大 |
| infty (English) |
|
|
Geometry
Tag or symbol | Meaning | Formula source example | Input text example | Example reading |
|---|---|---|---|---|
| Pi |
|
| 派等于三点一四一五九 |
| Trigonometric function |
|
| sine三十度等于二分之一 |
|
|
| cosine四十五度等于二分之根号下二 | |
|
|
| tangent三十度等于cosine三十度分之sine三十度 | |
|
|
| cosecant A | |
|
|
| secant A | |
|
|
| cotangent A | |
| Angle |
|
| 角A B |
|
|
| ||
| Degree |
|
| 角A B 等于三十度 |
| Circle |
|
| 圆 |
| Arc |
|
| 弧BC |
| Right angle |
|
| 因为直角三角形ABC |
|
|
| 所以AB垂直于BC | |
| Triangle |
|
| 三角形ABC |
|
|
| ||
| Parallelogram |
|
| 平行四边形ABCD |
| Perpendicular |
|
| A B 垂直于 B C |
|
|
| ||
|
|
| ||
| Parallel |
|
| A平行于B |
| Parallel and equal |
|
| A平行且相等于B |
| Congruent |
|
| 三角形ABC全等于三角形DEF |
Conditions
Tag or symbol | Meaning | Formula source example | Input text example | Example reading |
|---|---|---|---|---|
| Implies |
|
| 推出一加一等于二 |
| Equivalent |
|
| p等价q |
| Because |
|
| 因为a等于b所以b等于a |
| Therefore |
Units
Wrap units in \unit, \quantity, \mathit, \mathrm, or \rm, for example \unit{cm}.
Tag or symbol | Meaning | Formula source example | Input text example | Example reading |
|---|---|---|---|---|
| Millimetre |
|
| 五毫米 |
| Centimetre |
|
| 五厘米 |
| Decimetre |
|
| 五分米 |
| Metre |
|
| 五米 |
| Kilometre |
|
| 五千米 |
| Gram |
|
| 五克 |
| Kilogram |
|
| 五千克 |
| Tonne |
|
| 五吨 |
| Square millimetre |
|
| 五平方毫米 |
| Square centimetre |
|
| 五平方厘米 |
| Square decimetre |
|
| 五平方分米 |
| Square metre |
|
| 五平方米 |
| Square kilometre |
|
| 五平方千米 |
| Cubic millimetre |
|
| 五立方毫米 |
| Cubic centimetre |
|
| 五立方厘米 |
| Cubic decimetre |
|
| 五立方分米 |
| Cubic metre |
|
| 五立方米 |
| Cubic kilometre |
|
| 五立方千米 |
| Millilitre |
|
| 五毫升 |
| Second |
|
| 五秒 |
| Minute |
|
| 五分 |
| Hour |
|
| 五小时 |
| Kilometres per hour |
|
| 五千米每小时 |
| Grams per litre |
|
| 五克每升 |
FAQ
Why is a formula not read correctly?
- Check that the formula is enclosed in
$...$,$$...$$,\(...\), or\[...\]. - Check for unsupported tags or symbols.
- Check that each
\is escaped as\\in the request string.
How do I handle backslashes in code?
See the escaping instructions under Usage. Formula source and code strings use different notation: write \frac as \\frac in a regular string.