All Products
Search
Document Center

Alibaba Cloud Model Studio:SSML and LaTeX

Last Updated:Oct 21, 2026

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_ssml parameter to true, 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: " → &quot;, ' → &apos;, & → &amp;, < → &lt;, and > → &gt;.

Quick start

The example below uses SSML to control speech rate during synthesis. Before running the code, complete these prerequisites:

  1. Obtain an API key
  2. 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-WebSocket
  • jackson-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

voice

String

No

Specifies the voice.

This attribute takes precedence over the voice parameter in the API request.

rate

String

No

Specifies the speech rate. This attribute takes precedence over the speech_rate parameter in the API request.

  • Valid values: a decimal number between 0.5 and 2 (inclusive)

  • Default value: 1

    • Values greater than 1 increase the speech rate
    • Values less than 1 decrease the speech rate

pitch

String

No

Specifies the pitch. This attribute takes precedence over the pitch_rate parameter in the API request.

  • Valid values: a decimal number between 0.5 and 2 (inclusive)

  • Default value: 1

    • Values greater than 1 raise the pitch
    • Values less than 1 lower the pitch

volume

String

No

Specifies the volume. This attribute takes precedence over the volume parameter in the API request.

  • Valid values: an integer between 0 and 100 (inclusive)

  • Default value: 50

    • Values greater than 50 increase the volume
    • Values less than 50 decrease the volume

effect

String

No

Specifies the audio effect.

  • Valid values:

    • robot: robot voice effect
    • lolita: lolita voice effect
    • lowpass: low-pass filter effect
    • echo: echo effect
    • eq: equalizer (advanced)
    • lpfilter: low-pass filter (advanced)
    • hpfilter: high-pass filter (advanced)

    Note

    • eq, lpfilter, and hpfilter are advanced effect types. Use the effectValue parameter to customize specific effects.
    • Each SSML tag supports only one effect. Setting multiple effect attributes simultaneously is not allowed.
    • Enabling audio effects increases synthesis latency.

effectValue

String

No

Configures the specific behavior of the audio effect (the effect parameter). Applies to three advanced effect types: eq, lpfilter, and hpfilter.

  • Valid values:

    • eq (equalizer): the system supports 8 frequency bands by default, corresponding to the following frequencies:

      ["40 Hz","100 Hz", "200 Hz", "400 Hz", "800 Hz", "1600 Hz", "4000 Hz", "12000 Hz"].

      Each band has a bandwidth of 1.0q.

      Use the effectValue parameter to specify the gain for each band. The parameter is a string of 8 integers ranging from -20 to 20, separated by spaces. A value of 0 means no gain adjustment for that frequency.

      Example: effectValue="1 1 1 1 1 1 1 1"

    • lpfilter (low-pass filter): specifies the cutoff frequency. Valid values: an integer in the range (0, target_sample_rate/2]. Example: effectValue="800".

    • hpfilter (high-pass filter): specifies the cutoff frequency. Valid values: an integer in the range (0, target_sample_rate/2]. Example: effectValue="1200".

bgm

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 & with &amp;.

  • Audio requirements:

    There is no upper limit on the background music file size, but larger files take longer to download. If the synthesized speech is longer than the background music, the music loops automatically.

    • Sample rate: 16 kHz

    • Channels: mono

    • Format: WAV

      To convert a non-WAV file, use ffmpeg:

      ffmpeg -i input_audio -acodec pcm_s16le -ac 1 -ar 16000 output.wav
      
    • Bit depth: 16-bit

ImportantThe uploader bears legal responsibility for the copyright of uploaded audio.

backgroundMusicVolume

String

No

Specifies the background music volume. Use this attribute together with the bgm attribute.

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

time

String

No

Specifies the pause duration, in seconds or milliseconds (for example, "2s" or "50ms").

  • Valid values:

    • In seconds (s): an integer between 1 and 10 (inclusive)
    • In milliseconds (ms): an integer between 50 and 10000 (inclusive)

When multiple <break> tags are used consecutively, the total pause duration is the sum of all individual durations. If the total exceeds 10 seconds, only the first 10 seconds are applied.

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

alias

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

alphabet

String

Yes

Specifies the pronunciation type: pinyin (for Chinese) or phonetic symbols (for English).

Valid values:

ph

String

Yes

Specifies the exact pinyin or phonetic notation. Usage rules:

  • Separate pinyin for multiple characters with spaces. The number of pinyin entries must match the number of characters.
  • Each pinyin entry consists of the pronunciation and a tone number. Tone numbers range from 1 to 5, where 5 represents the neutral tone.

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

src

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 & with &amp;.

  • Audio requirements:

    • Sample rate: 16 kHz

    • Channels: mono

    • Format: WAV

      To convert a non-WAV file, use ffmpeg:

      ffmpeg -i input_audio -acodec pcm_s16le -ac 1 -ar 16000 output.wav
      
    • File size: 2 MB maximum

    • Bit depth: 16-bit

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

interpret-as

String

Yes

Specifies the content type of the text within the tag.

Valid values:

  • cardinal: reads as a standard integer or decimal number
  • digits: reads each digit individually (for example, 123 is read as "one two three")
  • telephone: reads digit by digit in the standard phone number format
  • name: reads using standard name pronunciation rules
  • address: reads using standard address pronunciation rules
  • id: reads using standard identifier (account name, nickname) pronunciation rules
  • characters: reads each character in the text individually
  • punctuation: reads the name of each punctuation mark
  • date: reads using standard date pronunciation rules
  • time: reads using standard time pronunciation rules
  • currency: reads using standard monetary amount pronunciation rules
  • measure: reads using standard unit of measurement pronunciation rules

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

2 + 3 = 5

$2 + 3 = 5$

二加三等于五

-

Subtraction

3 - 2 = 1

$3 - 2 = 1$

三减二等于一

\pm

Plus or minus

\pm 1 \pm 2

$\pm 1\pm 2$

正负一加减二

Positive or negative

\times

Multiplication

2 \times 3 = 6

$2 \times 3 = 6$

二乘三等于六

×

2 × 3 = 6

$$2 × 3 = 6$$

*

2 * 3 = 6

\(2 * 3 = 6\)

\div

Division

6\div2=3

\[6\div2=3\]

六除二等于三

÷

6÷2=3

$6÷2=3$

/

6/2=3

$6/2=3$

=

Equals

3+5=8

$3+5=8$

三加五等于八

<

Less than

1< 2

$1< 2$

一小于二

≤

Less than or equal to

3≤5

$3≤5$

三小于等于五

<=

3<=5

$3<=5$

\leq

3\leq5

$3\leq 5$

\le

3\le5

$3\le 5$

\leqq

3\leqq5

$3\leqq 5$

\leqslant

3\leqslant5

$3\leqslant 5$

>

Greater than

2>1

$2>1$

二大于一

≥

Greater than or equal to

5≥3

$5≥3$

五大于等于三

>=

5>=3

$5>=3$

\geq

5\geq3

$5\geq 3$

\ge

5\ge3

$5\ge 3$

\geqq

5\geqq3

$5\geqq 3$

\geqslant

5\geqslant3

$5\geqslant 3$

\frac

Fraction

2\frac3

$\frac {2}{3}$

三分之二

^

Power

2^1

$2^{1}$

二的一次方

\sqrt

Root

\sqrt{9} = 3

$\sqrt {9} = 3$

根号下九等于三

\sqrt[3]{8} = 2

$\sqrt[3]{8} = 2$

三次根号下八等于二

\%

Percentage

5\%

$5\%$

百分之五

|

Absolute value

∣3∣=3

$|3| =3$

绝对值三的绝对值等于三

\vert

3\vert=3

$\vert 3\vert =3$

绝对值三绝对值等于三

\lg

Logarithm

lg {10}

$\lg {10}$

log十

\log

Logarithm

\log{5}

$\log{5}$

log五

\ln

Natural logarithm

\lnX

$ln {10}$

LN十

!

Factorial

5!

$5!$

五的阶乘

()

Brackets

(2+1)

$(2+1)$

括号内二加一

\{ \}

$\{2+1\}$

大括号二加一反大括号

Special mathematical symbols

Tag or symbol

Meaning

Formula source example

Input text example

Example reading

\alpha

alpha

\alpha

$\alpha$

α

\Alpha

\Alpha

$\Alpha$

\beta

beta

\beta

$\beta$

β

\Beta

\Beta

$\Beta$

\gamma

gamma

\gamma

$\gamma$

γ

\Gamma

\Gamma

$\Gamma$

\delta

delta

\delta

$\delta$

Δ

\Delta

\Delta

$\Delta$

\infty

Infinity (Chinese)

\infty

$\infty$

无穷大

∞

infty (English)

∞

$∞$

Geometry

Tag or symbol

Meaning

Formula source example

Input text example

Example reading

\pi

Pi

\pi=3.14159

$\pi =3.14159$

派等于三点一四一五九

\sin (sine)

Trigonometric function

\sin 30^\circ =\frac {1}{2}

$\sin 30^\circ =\frac {1}{2}$

sine三十度等于二分之一

\cos (cosine)

\cos 45^\circ =\frac {\sqrt {2}}{2}

$\cos 45^\circ =\frac {\sqrt {2}}{2}$

cosine四十五度等于二分之根号下二

\tan (tangent)

\tan 30^\circ =\frac {\sin 30^\circ}{\cos 30^\circ}

$\tan 30^\circ =\frac {\sin 30^\circ}{\cos 30^\circ}$

tangent三十度等于cosine三十度分之sine三十度

\csc (cosecant)

\csc A

$\csc A$

cosecant A

\sec (secant)

\sec A

$\sec A$

secant A

\cot (cotangent)

\cot A

$\cot A$

cotangent A

\angle

Angle

\angle AB

$\angle AB$

角A B

∠

∠AB

$∠AB$

^\circ

Degree

∠AB = 30^\circ

$∠AB = 30^\circ$

角A B 等于三十度

\odot

Circle

\odot

$\odot$

圆

\overset\frown

Arc

\overset\frown {BC}

$\overset\frown {BC}$

弧BC

\rm{Rt}

Right angle

\because \rm{Rt}\triangle ABC

$\because \rm{Rt}\triangle ABC$

因为直角三角形ABC

\mathrm{Rt}

\therefore AB \perp BC

$\therefore AB \perp BC$

所以AB垂直于BC

\triangle

Triangle

\triangleABC

$\triangle ABC$

三角形ABC

△

△ABC

$△ABC$

\parallelogram

Parallelogram

\parallelogramABCD

$\parallelogram ABCD$

平行四边形ABCD

\perp

Perpendicular

AB \perp BC

$AB \perp BC$

A B 垂直于 B C

\bot

AB \bot BC

$AB \bot BC$

⊥

AB ⊥ BC

$AB ⊥ BC$

\parallel

Parallel

A\parallel B

$A\parallel B$

A平行于B

\equalparallel

Parallel and equal

A\equalparallel B

$A\equalparallel B$

A平行且相等于B

\cong

Congruent

△ABC\cong△DEF

$△ABC\cong△DEF$

三角形ABC全等于三角形DEF

Conditions

Tag or symbol

Meaning

Formula source example

Input text example

Example reading

\implies

Implies

\implies 1+1=2

$\implies 1+1=2$

推出一加一等于二

\iff

Equivalent

p\iffq

$p\iffq$

p等价q

\because

Because

\because a = b \therefore b=a

$\because a = b \therefore b=a$

因为a等于b所以b等于a

\therefore

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

mm

Millimetre

5\quantity{mm}

$5\quantity{mm}$

五毫米

cm

Centimetre

5\quantity{cm}

$5\quantity{cm}$

五厘米

dm

Decimetre

5\quantity{dm}

$5\quantity{dm}$

五分米

m

Metre

5\quantity{m}

$5\quantity{m}$

五米

km

Kilometre

5\quantity{km}

$5\quantity{km}$

五千米

g

Gram

5\quantity{g}

$5\quantity{g}$

五克

kg

Kilogram

5\quantity{kg}

$5\quantity{kg}$

五千克

t

Tonne

5\quantity{t}

$5\quantity{t}$

五吨

mm^2

Square millimetre

5\quantity{mm^2}

$5\quantity{mm^2}$

五平方毫米

cm^2

Square centimetre

5\quantity{cm^2}

$5\quantity{cm^2}$

五平方厘米

dm^2

Square decimetre

5\quantity{dm^2}

$5\quantity{dm^2}$

五平方分米

m^2

Square metre

5\quantity{m^2}

$5\quantity{m^2}$

五平方米

km^2

Square kilometre

5\quantity{km^2}

$5\quantity{km^2}$

五平方千米

mm^3

Cubic millimetre

5\quantity{mm^3}

$5\quantity{mm^3}$

五立方毫米

cm^3

Cubic centimetre

5\quantity{cm^3}

$5\quantity{cm^3}$

五立方厘米

dm^3

Cubic decimetre

5\quantity{dm^3}

$5\quantity{dm^3}$

五立方分米

m^3

Cubic metre

5\quantity{m^3}

$5\quantity{m^3}$

五立方米

km^3

Cubic kilometre

5\quantity{km^3}

$5\quantity{km^3}$

五立方千米

ml

Millilitre

5\quantity{ml}

$5\quantity{ml}$

五毫升

s

Second

5\quantity{s}

$5\quantity{s}$

五秒

min

Minute

5\quantity{min}

$5\quantity{min}$

五分

h

Hour

5\quantity{h}

$5\quantity{h}$

五小时

km/h

Kilometres per hour

5\quantity{km/h}

$5\quantity{km/h}$

五千米每小时

g/l

Grams per litre

5\quantity{g/l}

$5\quantity{g/l}$

五克每升

FAQ

Why is a formula not read correctly?

  1. Check that the formula is enclosed in $...$, $$...$$, \(...\), or \[...\].
  2. Check for unsupported tags or symbols.
  3. 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.